Fossil SCM

Merge from trunk.

brickviking 2025-03-28 10:11 UTC bv-infotool merge
Commit f60a7ed29171903f0df24366c041997ae1475024632454b0fb83737a1234505f
--- .fossil-settings/ignore-glob
+++ .fossil-settings/ignore-glob
@@ -5,5 +5,6 @@
55
fossil
66
fossil.exe
77
win/fossil.exe
88
*shell-see.*
99
*sqlite3-see.*
10
+bld
1011
--- .fossil-settings/ignore-glob
+++ .fossil-settings/ignore-glob
@@ -5,5 +5,6 @@
5 fossil
6 fossil.exe
7 win/fossil.exe
8 *shell-see.*
9 *sqlite3-see.*
 
10
--- .fossil-settings/ignore-glob
+++ .fossil-settings/ignore-glob
@@ -5,5 +5,6 @@
5 fossil
6 fossil.exe
7 win/fossil.exe
8 *shell-see.*
9 *sqlite3-see.*
10 bld
11

No diff available

+86 -24
--- extsrc/shell.c
+++ extsrc/shell.c
@@ -1162,10 +1162,27 @@
11621162
}
11631163
}
11641164
return n;
11651165
}
11661166
#endif
1167
+
1168
+/*
1169
+** Check to see if z[] is a valid VT100 escape. If it is, then
1170
+** return the number of bytes in the escape sequence. Return 0 if
1171
+** z[] is not a VT100 escape.
1172
+**
1173
+** This routine assumes that z[0] is \033 (ESC).
1174
+*/
1175
+static int isVt100(const unsigned char *z){
1176
+ int i;
1177
+ if( z[1]!='[' ) return 0;
1178
+ i = 2;
1179
+ while( z[i]>=0x30 && z[i]<=0x3f ){ i++; }
1180
+ while( z[i]>=0x20 && z[i]<=0x2f ){ i++; }
1181
+ if( z[i]<0x40 || z[i]>0x7e ) return 0;
1182
+ return i+1;
1183
+}
11671184
11681185
/*
11691186
** Output string zUtf to stdout as w characters. If w is negative,
11701187
** then right-justify the text. W is the width in UTF-8 characters, not
11711188
** in bytes. This is different from the %*.*s specification in printf
@@ -1178,10 +1195,11 @@
11781195
static void utf8_width_print(FILE *out, int w, const char *zUtf){
11791196
const unsigned char *a = (const unsigned char*)zUtf;
11801197
unsigned char c;
11811198
int i = 0;
11821199
int n = 0;
1200
+ int k;
11831201
int aw = w<0 ? -w : w;
11841202
if( zUtf==0 ) zUtf = "";
11851203
while( (c = a[i])!=0 ){
11861204
if( (c&0xc0)==0xc0 ){
11871205
int u;
@@ -1190,10 +1208,12 @@
11901208
if( x+n>aw ){
11911209
break;
11921210
}
11931211
i += len;
11941212
n += x;
1213
+ }else if( c==0x1b && (k = isVt100(&a[i]))>0 ){
1214
+ i += k;
11951215
}else if( n>=aw ){
11961216
break;
11971217
}else{
11981218
n++;
11991219
i++;
@@ -6270,12 +6290,11 @@
62706290
** start HIDDEN,
62716291
** stop HIDDEN,
62726292
** step HIDDEN
62736293
** );
62746294
**
6275
-** The virtual table also has a rowid, logically equivalent to n+1 where
6276
-** "n" is the ascending integer in the aforesaid production definition.
6295
+** The virtual table also has a rowid which is an alias for the value.
62776296
**
62786297
** Function arguments in queries against this virtual table are translated
62796298
** into equality constraints against successive hidden columns. In other
62806299
** words, the following pairs of queries are equivalent to each other:
62816300
**
@@ -6326,10 +6345,11 @@
63266345
/* #include "sqlite3ext.h" */
63276346
SQLITE_EXTENSION_INIT1
63286347
#include <assert.h>
63296348
#include <string.h>
63306349
#include <limits.h>
6350
+#include <math.h>
63316351
63326352
#ifndef SQLITE_OMIT_VIRTUALTABLE
63336353
/*
63346354
** Return that member of a generate_series(...) sequence whose 0-based
63356355
** index is ix. The 0th member is given by smBase. The sequence members
@@ -6486,10 +6506,11 @@
64866506
){
64876507
sqlite3_vtab *pNew;
64886508
int rc;
64896509
64906510
/* Column numbers */
6511
+#define SERIES_COLUMN_ROWID (-1)
64916512
#define SERIES_COLUMN_VALUE 0
64926513
#define SERIES_COLUMN_START 1
64936514
#define SERIES_COLUMN_STOP 2
64946515
#define SERIES_COLUMN_STEP 3
64956516
@@ -6573,17 +6594,15 @@
65736594
#define LARGEST_UINT64 (0xffffffff|(((sqlite3_uint64)0xffffffff)<<32))
65746595
#define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
65756596
#endif
65766597
65776598
/*
6578
-** Return the rowid for the current row, logically equivalent to n+1 where
6579
-** "n" is the ascending integer in the aforesaid production definition.
6599
+** The rowid is the same as the value.
65806600
*/
65816601
static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
65826602
series_cursor *pCur = (series_cursor*)cur;
6583
- sqlite3_uint64 n = pCur->ss.uSeqIndexNow;
6584
- *pRowid = (sqlite3_int64)((n<LARGEST_UINT64)? n+1 : 0);
6603
+ *pRowid = pCur->ss.iValueNow;
65856604
return SQLITE_OK;
65866605
}
65876606
65886607
/*
65896608
** Return TRUE if the cursor has been moved off of the last
@@ -6692,29 +6711,56 @@
66926711
if( idxNum & 0x3380 ){
66936712
/* Extract the maximum range of output values determined by
66946713
** constraints on the "value" column.
66956714
*/
66966715
if( idxNum & 0x0080 ){
6697
- iMin = iMax = sqlite3_value_int64(argv[i++]);
6716
+ if( sqlite3_value_numeric_type(argv[i])==SQLITE_FLOAT ){
6717
+ double r = sqlite3_value_double(argv[i++]);
6718
+ if( r==ceil(r) ){
6719
+ iMin = iMax = (sqlite3_int64)r;
6720
+ }else{
6721
+ returnNoRows = 1;
6722
+ }
6723
+ }else{
6724
+ iMin = iMax = sqlite3_value_int64(argv[i++]);
6725
+ }
66986726
}else{
66996727
if( idxNum & 0x0300 ){
6700
- iMin = sqlite3_value_int64(argv[i++]);
6701
- if( idxNum & 0x0200 ){
6702
- if( iMin==LARGEST_INT64 ){
6703
- returnNoRows = 1;
6728
+ if( sqlite3_value_numeric_type(argv[i])==SQLITE_FLOAT ){
6729
+ double r = sqlite3_value_double(argv[i++]);
6730
+ if( idxNum & 0x0200 && r==ceil(r) ){
6731
+ iMin = (sqlite3_int64)ceil(r+1.0);
67046732
}else{
6705
- iMin++;
6733
+ iMin = (sqlite3_int64)ceil(r);
6734
+ }
6735
+ }else{
6736
+ iMin = sqlite3_value_int64(argv[i++]);
6737
+ if( idxNum & 0x0200 ){
6738
+ if( iMin==LARGEST_INT64 ){
6739
+ returnNoRows = 1;
6740
+ }else{
6741
+ iMin++;
6742
+ }
67066743
}
67076744
}
67086745
}
67096746
if( idxNum & 0x3000 ){
6710
- iMax = sqlite3_value_int64(argv[i++]);
6711
- if( idxNum & 0x2000 ){
6712
- if( iMax==SMALLEST_INT64 ){
6713
- returnNoRows = 1;
6747
+ if( sqlite3_value_numeric_type(argv[i])==SQLITE_FLOAT ){
6748
+ double r = sqlite3_value_double(argv[i++]);
6749
+ if( (idxNum & 0x2000)!=0 && r==floor(r) ){
6750
+ iMax = (sqlite3_int64)(r-1.0);
67146751
}else{
6715
- iMax--;
6752
+ iMax = (sqlite3_int64)floor(r);
6753
+ }
6754
+ }else{
6755
+ iMax = sqlite3_value_int64(argv[i++]);
6756
+ if( idxNum & 0x2000 ){
6757
+ if( iMax==SMALLEST_INT64 ){
6758
+ returnNoRows = 1;
6759
+ }else{
6760
+ iMax--;
6761
+ }
67166762
}
67176763
}
67186764
}
67196765
if( iMin>iMax ){
67206766
returnNoRows = 1;
@@ -6867,11 +6913,14 @@
68676913
idxNum |= 0x40;
68686914
}
68696915
continue;
68706916
}
68716917
if( pConstraint->iColumn<SERIES_COLUMN_START ){
6872
- if( pConstraint->iColumn==SERIES_COLUMN_VALUE && pConstraint->usable ){
6918
+ if( (pConstraint->iColumn==SERIES_COLUMN_VALUE ||
6919
+ pConstraint->iColumn==SERIES_COLUMN_ROWID)
6920
+ && pConstraint->usable
6921
+ ){
68736922
switch( op ){
68746923
case SQLITE_INDEX_CONSTRAINT_EQ:
68756924
case SQLITE_INDEX_CONSTRAINT_IS: {
68766925
idxNum |= 0x0080;
68776926
idxNum &= ~0x3300;
@@ -24197,13 +24246,18 @@
2419724246
j++;
2419824247
}while( (n&7)!=0 && n<mxWidth );
2419924248
i++;
2420024249
continue;
2420124250
}
24202
- n++;
24203
- j += 3;
24204
- i++;
24251
+ if( c==0x1b && p->eEscMode==SHELL_ESC_OFF && (k = isVt100(&z[i]))>0 ){
24252
+ i += k;
24253
+ j += k;
24254
+ }else{
24255
+ n++;
24256
+ j += 3;
24257
+ i++;
24258
+ }
2420524259
}
2420624260
if( n>=mxWidth && bWordWrap ){
2420724261
/* Perhaps try to back up to a better place to break the line */
2420824262
for(k=i; k>i/2; k--){
2420924263
if( IsSpace(z[k-1]) ) break;
@@ -24265,13 +24319,21 @@
2426524319
break;
2426624320
case SHELL_ESC_ASCII:
2426724321
zOut[j++] = '^';
2426824322
zOut[j++] = 0x40 + c;
2426924323
break;
24270
- case SHELL_ESC_OFF:
24271
- zOut[j++] = c;
24324
+ case SHELL_ESC_OFF: {
24325
+ int nn;
24326
+ if( c==0x1b && (nn = isVt100(&z[i]))>0 ){
24327
+ memcpy(&zOut[j], &z[i], nn);
24328
+ j += nn;
24329
+ i += nn - 1;
24330
+ }else{
24331
+ zOut[j++] = c;
24332
+ }
2427224333
break;
24334
+ }
2427324335
}
2427424336
i++;
2427524337
}
2427624338
zOut[j] = 0;
2427724339
return (char*)zOut;
@@ -27021,11 +27083,11 @@
2702127083
for(i=0; i<pgSz; i+=16){
2702227084
const u8 *aLine = aData+i;
2702327085
for(j=0; j<16 && aLine[j]==0; j++){}
2702427086
if( j==16 ) continue;
2702527087
if( !seenPageLabel ){
27026
- sqlite3_fprintf(p->out, "| page %lld offset %lld\n", pgno, pgno*pgSz);
27088
+ sqlite3_fprintf(p->out, "| page %lld offset %lld\n",pgno,(pgno-1)*pgSz);
2702727089
seenPageLabel = 1;
2702827090
}
2702927091
sqlite3_fprintf(p->out, "| %5d:", i);
2703027092
for(j=0; j<16; j++) sqlite3_fprintf(p->out, " %02x", aLine[j]);
2703127093
sqlite3_fprintf(p->out, " ");
2703227094
--- extsrc/shell.c
+++ extsrc/shell.c
@@ -1162,10 +1162,27 @@
1162 }
1163 }
1164 return n;
1165 }
1166 #endif
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1167
1168 /*
1169 ** Output string zUtf to stdout as w characters. If w is negative,
1170 ** then right-justify the text. W is the width in UTF-8 characters, not
1171 ** in bytes. This is different from the %*.*s specification in printf
@@ -1178,10 +1195,11 @@
1178 static void utf8_width_print(FILE *out, int w, const char *zUtf){
1179 const unsigned char *a = (const unsigned char*)zUtf;
1180 unsigned char c;
1181 int i = 0;
1182 int n = 0;
 
1183 int aw = w<0 ? -w : w;
1184 if( zUtf==0 ) zUtf = "";
1185 while( (c = a[i])!=0 ){
1186 if( (c&0xc0)==0xc0 ){
1187 int u;
@@ -1190,10 +1208,12 @@
1190 if( x+n>aw ){
1191 break;
1192 }
1193 i += len;
1194 n += x;
 
 
1195 }else if( n>=aw ){
1196 break;
1197 }else{
1198 n++;
1199 i++;
@@ -6270,12 +6290,11 @@
6270 ** start HIDDEN,
6271 ** stop HIDDEN,
6272 ** step HIDDEN
6273 ** );
6274 **
6275 ** The virtual table also has a rowid, logically equivalent to n+1 where
6276 ** "n" is the ascending integer in the aforesaid production definition.
6277 **
6278 ** Function arguments in queries against this virtual table are translated
6279 ** into equality constraints against successive hidden columns. In other
6280 ** words, the following pairs of queries are equivalent to each other:
6281 **
@@ -6326,10 +6345,11 @@
6326 /* #include "sqlite3ext.h" */
6327 SQLITE_EXTENSION_INIT1
6328 #include <assert.h>
6329 #include <string.h>
6330 #include <limits.h>
 
6331
6332 #ifndef SQLITE_OMIT_VIRTUALTABLE
6333 /*
6334 ** Return that member of a generate_series(...) sequence whose 0-based
6335 ** index is ix. The 0th member is given by smBase. The sequence members
@@ -6486,10 +6506,11 @@
6486 ){
6487 sqlite3_vtab *pNew;
6488 int rc;
6489
6490 /* Column numbers */
 
6491 #define SERIES_COLUMN_VALUE 0
6492 #define SERIES_COLUMN_START 1
6493 #define SERIES_COLUMN_STOP 2
6494 #define SERIES_COLUMN_STEP 3
6495
@@ -6573,17 +6594,15 @@
6573 #define LARGEST_UINT64 (0xffffffff|(((sqlite3_uint64)0xffffffff)<<32))
6574 #define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
6575 #endif
6576
6577 /*
6578 ** Return the rowid for the current row, logically equivalent to n+1 where
6579 ** "n" is the ascending integer in the aforesaid production definition.
6580 */
6581 static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
6582 series_cursor *pCur = (series_cursor*)cur;
6583 sqlite3_uint64 n = pCur->ss.uSeqIndexNow;
6584 *pRowid = (sqlite3_int64)((n<LARGEST_UINT64)? n+1 : 0);
6585 return SQLITE_OK;
6586 }
6587
6588 /*
6589 ** Return TRUE if the cursor has been moved off of the last
@@ -6692,29 +6711,56 @@
6692 if( idxNum & 0x3380 ){
6693 /* Extract the maximum range of output values determined by
6694 ** constraints on the "value" column.
6695 */
6696 if( idxNum & 0x0080 ){
6697 iMin = iMax = sqlite3_value_int64(argv[i++]);
 
 
 
 
 
 
 
 
 
6698 }else{
6699 if( idxNum & 0x0300 ){
6700 iMin = sqlite3_value_int64(argv[i++]);
6701 if( idxNum & 0x0200 ){
6702 if( iMin==LARGEST_INT64 ){
6703 returnNoRows = 1;
6704 }else{
6705 iMin++;
 
 
 
 
 
 
 
 
 
6706 }
6707 }
6708 }
6709 if( idxNum & 0x3000 ){
6710 iMax = sqlite3_value_int64(argv[i++]);
6711 if( idxNum & 0x2000 ){
6712 if( iMax==SMALLEST_INT64 ){
6713 returnNoRows = 1;
6714 }else{
6715 iMax--;
 
 
 
 
 
 
 
 
 
6716 }
6717 }
6718 }
6719 if( iMin>iMax ){
6720 returnNoRows = 1;
@@ -6867,11 +6913,14 @@
6867 idxNum |= 0x40;
6868 }
6869 continue;
6870 }
6871 if( pConstraint->iColumn<SERIES_COLUMN_START ){
6872 if( pConstraint->iColumn==SERIES_COLUMN_VALUE && pConstraint->usable ){
 
 
 
6873 switch( op ){
6874 case SQLITE_INDEX_CONSTRAINT_EQ:
6875 case SQLITE_INDEX_CONSTRAINT_IS: {
6876 idxNum |= 0x0080;
6877 idxNum &= ~0x3300;
@@ -24197,13 +24246,18 @@
24197 j++;
24198 }while( (n&7)!=0 && n<mxWidth );
24199 i++;
24200 continue;
24201 }
24202 n++;
24203 j += 3;
24204 i++;
 
 
 
 
 
24205 }
24206 if( n>=mxWidth && bWordWrap ){
24207 /* Perhaps try to back up to a better place to break the line */
24208 for(k=i; k>i/2; k--){
24209 if( IsSpace(z[k-1]) ) break;
@@ -24265,13 +24319,21 @@
24265 break;
24266 case SHELL_ESC_ASCII:
24267 zOut[j++] = '^';
24268 zOut[j++] = 0x40 + c;
24269 break;
24270 case SHELL_ESC_OFF:
24271 zOut[j++] = c;
 
 
 
 
 
 
 
24272 break;
 
24273 }
24274 i++;
24275 }
24276 zOut[j] = 0;
24277 return (char*)zOut;
@@ -27021,11 +27083,11 @@
27021 for(i=0; i<pgSz; i+=16){
27022 const u8 *aLine = aData+i;
27023 for(j=0; j<16 && aLine[j]==0; j++){}
27024 if( j==16 ) continue;
27025 if( !seenPageLabel ){
27026 sqlite3_fprintf(p->out, "| page %lld offset %lld\n", pgno, pgno*pgSz);
27027 seenPageLabel = 1;
27028 }
27029 sqlite3_fprintf(p->out, "| %5d:", i);
27030 for(j=0; j<16; j++) sqlite3_fprintf(p->out, " %02x", aLine[j]);
27031 sqlite3_fprintf(p->out, " ");
27032
--- extsrc/shell.c
+++ extsrc/shell.c
@@ -1162,10 +1162,27 @@
1162 }
1163 }
1164 return n;
1165 }
1166 #endif
1167
1168 /*
1169 ** Check to see if z[] is a valid VT100 escape. If it is, then
1170 ** return the number of bytes in the escape sequence. Return 0 if
1171 ** z[] is not a VT100 escape.
1172 **
1173 ** This routine assumes that z[0] is \033 (ESC).
1174 */
1175 static int isVt100(const unsigned char *z){
1176 int i;
1177 if( z[1]!='[' ) return 0;
1178 i = 2;
1179 while( z[i]>=0x30 && z[i]<=0x3f ){ i++; }
1180 while( z[i]>=0x20 && z[i]<=0x2f ){ i++; }
1181 if( z[i]<0x40 || z[i]>0x7e ) return 0;
1182 return i+1;
1183 }
1184
1185 /*
1186 ** Output string zUtf to stdout as w characters. If w is negative,
1187 ** then right-justify the text. W is the width in UTF-8 characters, not
1188 ** in bytes. This is different from the %*.*s specification in printf
@@ -1178,10 +1195,11 @@
1195 static void utf8_width_print(FILE *out, int w, const char *zUtf){
1196 const unsigned char *a = (const unsigned char*)zUtf;
1197 unsigned char c;
1198 int i = 0;
1199 int n = 0;
1200 int k;
1201 int aw = w<0 ? -w : w;
1202 if( zUtf==0 ) zUtf = "";
1203 while( (c = a[i])!=0 ){
1204 if( (c&0xc0)==0xc0 ){
1205 int u;
@@ -1190,10 +1208,12 @@
1208 if( x+n>aw ){
1209 break;
1210 }
1211 i += len;
1212 n += x;
1213 }else if( c==0x1b && (k = isVt100(&a[i]))>0 ){
1214 i += k;
1215 }else if( n>=aw ){
1216 break;
1217 }else{
1218 n++;
1219 i++;
@@ -6270,12 +6290,11 @@
6290 ** start HIDDEN,
6291 ** stop HIDDEN,
6292 ** step HIDDEN
6293 ** );
6294 **
6295 ** The virtual table also has a rowid which is an alias for the value.
 
6296 **
6297 ** Function arguments in queries against this virtual table are translated
6298 ** into equality constraints against successive hidden columns. In other
6299 ** words, the following pairs of queries are equivalent to each other:
6300 **
@@ -6326,10 +6345,11 @@
6345 /* #include "sqlite3ext.h" */
6346 SQLITE_EXTENSION_INIT1
6347 #include <assert.h>
6348 #include <string.h>
6349 #include <limits.h>
6350 #include <math.h>
6351
6352 #ifndef SQLITE_OMIT_VIRTUALTABLE
6353 /*
6354 ** Return that member of a generate_series(...) sequence whose 0-based
6355 ** index is ix. The 0th member is given by smBase. The sequence members
@@ -6486,10 +6506,11 @@
6506 ){
6507 sqlite3_vtab *pNew;
6508 int rc;
6509
6510 /* Column numbers */
6511 #define SERIES_COLUMN_ROWID (-1)
6512 #define SERIES_COLUMN_VALUE 0
6513 #define SERIES_COLUMN_START 1
6514 #define SERIES_COLUMN_STOP 2
6515 #define SERIES_COLUMN_STEP 3
6516
@@ -6573,17 +6594,15 @@
6594 #define LARGEST_UINT64 (0xffffffff|(((sqlite3_uint64)0xffffffff)<<32))
6595 #define SMALLEST_INT64 (((sqlite3_int64)-1) - LARGEST_INT64)
6596 #endif
6597
6598 /*
6599 ** The rowid is the same as the value.
 
6600 */
6601 static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
6602 series_cursor *pCur = (series_cursor*)cur;
6603 *pRowid = pCur->ss.iValueNow;
 
6604 return SQLITE_OK;
6605 }
6606
6607 /*
6608 ** Return TRUE if the cursor has been moved off of the last
@@ -6692,29 +6711,56 @@
6711 if( idxNum & 0x3380 ){
6712 /* Extract the maximum range of output values determined by
6713 ** constraints on the "value" column.
6714 */
6715 if( idxNum & 0x0080 ){
6716 if( sqlite3_value_numeric_type(argv[i])==SQLITE_FLOAT ){
6717 double r = sqlite3_value_double(argv[i++]);
6718 if( r==ceil(r) ){
6719 iMin = iMax = (sqlite3_int64)r;
6720 }else{
6721 returnNoRows = 1;
6722 }
6723 }else{
6724 iMin = iMax = sqlite3_value_int64(argv[i++]);
6725 }
6726 }else{
6727 if( idxNum & 0x0300 ){
6728 if( sqlite3_value_numeric_type(argv[i])==SQLITE_FLOAT ){
6729 double r = sqlite3_value_double(argv[i++]);
6730 if( idxNum & 0x0200 && r==ceil(r) ){
6731 iMin = (sqlite3_int64)ceil(r+1.0);
6732 }else{
6733 iMin = (sqlite3_int64)ceil(r);
6734 }
6735 }else{
6736 iMin = sqlite3_value_int64(argv[i++]);
6737 if( idxNum & 0x0200 ){
6738 if( iMin==LARGEST_INT64 ){
6739 returnNoRows = 1;
6740 }else{
6741 iMin++;
6742 }
6743 }
6744 }
6745 }
6746 if( idxNum & 0x3000 ){
6747 if( sqlite3_value_numeric_type(argv[i])==SQLITE_FLOAT ){
6748 double r = sqlite3_value_double(argv[i++]);
6749 if( (idxNum & 0x2000)!=0 && r==floor(r) ){
6750 iMax = (sqlite3_int64)(r-1.0);
6751 }else{
6752 iMax = (sqlite3_int64)floor(r);
6753 }
6754 }else{
6755 iMax = sqlite3_value_int64(argv[i++]);
6756 if( idxNum & 0x2000 ){
6757 if( iMax==SMALLEST_INT64 ){
6758 returnNoRows = 1;
6759 }else{
6760 iMax--;
6761 }
6762 }
6763 }
6764 }
6765 if( iMin>iMax ){
6766 returnNoRows = 1;
@@ -6867,11 +6913,14 @@
6913 idxNum |= 0x40;
6914 }
6915 continue;
6916 }
6917 if( pConstraint->iColumn<SERIES_COLUMN_START ){
6918 if( (pConstraint->iColumn==SERIES_COLUMN_VALUE ||
6919 pConstraint->iColumn==SERIES_COLUMN_ROWID)
6920 && pConstraint->usable
6921 ){
6922 switch( op ){
6923 case SQLITE_INDEX_CONSTRAINT_EQ:
6924 case SQLITE_INDEX_CONSTRAINT_IS: {
6925 idxNum |= 0x0080;
6926 idxNum &= ~0x3300;
@@ -24197,13 +24246,18 @@
24246 j++;
24247 }while( (n&7)!=0 && n<mxWidth );
24248 i++;
24249 continue;
24250 }
24251 if( c==0x1b && p->eEscMode==SHELL_ESC_OFF && (k = isVt100(&z[i]))>0 ){
24252 i += k;
24253 j += k;
24254 }else{
24255 n++;
24256 j += 3;
24257 i++;
24258 }
24259 }
24260 if( n>=mxWidth && bWordWrap ){
24261 /* Perhaps try to back up to a better place to break the line */
24262 for(k=i; k>i/2; k--){
24263 if( IsSpace(z[k-1]) ) break;
@@ -24265,13 +24319,21 @@
24319 break;
24320 case SHELL_ESC_ASCII:
24321 zOut[j++] = '^';
24322 zOut[j++] = 0x40 + c;
24323 break;
24324 case SHELL_ESC_OFF: {
24325 int nn;
24326 if( c==0x1b && (nn = isVt100(&z[i]))>0 ){
24327 memcpy(&zOut[j], &z[i], nn);
24328 j += nn;
24329 i += nn - 1;
24330 }else{
24331 zOut[j++] = c;
24332 }
24333 break;
24334 }
24335 }
24336 i++;
24337 }
24338 zOut[j] = 0;
24339 return (char*)zOut;
@@ -27021,11 +27083,11 @@
27083 for(i=0; i<pgSz; i+=16){
27084 const u8 *aLine = aData+i;
27085 for(j=0; j<16 && aLine[j]==0; j++){}
27086 if( j==16 ) continue;
27087 if( !seenPageLabel ){
27088 sqlite3_fprintf(p->out, "| page %lld offset %lld\n",pgno,(pgno-1)*pgSz);
27089 seenPageLabel = 1;
27090 }
27091 sqlite3_fprintf(p->out, "| %5d:", i);
27092 for(j=0; j<16; j++) sqlite3_fprintf(p->out, " %02x", aLine[j]);
27093 sqlite3_fprintf(p->out, " ");
27094
+428 -115
--- extsrc/sqlite3.c
+++ extsrc/sqlite3.c
@@ -16,11 +16,11 @@
1616
** if you want a wrapper to interface SQLite with your choice of programming
1717
** language. The code for the "sqlite3" command-line shell is also in a
1818
** separate file. This file contains only code for the core SQLite library.
1919
**
2020
** The content in this amalgamation comes from Fossil check-in
21
-** 18bda13e197e4b4ec7464b3e70012f71edc0 with changes in files:
21
+** 121f4d97f9a855131859d342bc2ade5f8c34 with changes in files:
2222
**
2323
**
2424
*/
2525
#ifndef SQLITE_AMALGAMATION
2626
#define SQLITE_CORE 1
@@ -465,11 +465,11 @@
465465
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
466466
** [sqlite_version()] and [sqlite_source_id()].
467467
*/
468468
#define SQLITE_VERSION "3.50.0"
469469
#define SQLITE_VERSION_NUMBER 3050000
470
-#define SQLITE_SOURCE_ID "2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185"
470
+#define SQLITE_SOURCE_ID "2025-03-27 23:29:25 121f4d97f9a855131859d342bc2ade5f8c34ba7732029ae156d02cec7cb6dd85"
471471
472472
/*
473473
** CAPI3REF: Run-Time Library Version Numbers
474474
** KEYWORDS: sqlite3_version sqlite3_sourceid
475475
**
@@ -30270,10 +30270,12 @@
3027030270
*/
3027130271
#include "windows.h"
3027230272
3027330273
#ifdef __CYGWIN__
3027430274
# include <sys/cygwin.h>
30275
+# include <sys/stat.h> /* amalgamator: dontcache */
30276
+# include <unistd.h> /* amalgamator: dontcache */
3027530277
# include <errno.h> /* amalgamator: dontcache */
3027630278
#endif
3027730279
3027830280
/*
3027930281
** Determine if we are dealing with Windows NT.
@@ -47726,20 +47728,20 @@
4772647728
{ "FileTimeToLocalFileTime", (SYSCALL)FileTimeToLocalFileTime, 0 },
4772747729
#else
4772847730
{ "FileTimeToLocalFileTime", (SYSCALL)0, 0 },
4772947731
#endif
4773047732
47731
-#define osFileTimeToLocalFileTime ((BOOL(WINAPI*)(CONST FILETIME*, \
47733
+#define osFileTimeToLocalFileTime ((BOOL(WINAPI*)(const FILETIME*, \
4773247734
LPFILETIME))aSyscall[11].pCurrent)
4773347735
4773447736
#if SQLITE_OS_WINCE
4773547737
{ "FileTimeToSystemTime", (SYSCALL)FileTimeToSystemTime, 0 },
4773647738
#else
4773747739
{ "FileTimeToSystemTime", (SYSCALL)0, 0 },
4773847740
#endif
4773947741
47740
-#define osFileTimeToSystemTime ((BOOL(WINAPI*)(CONST FILETIME*, \
47742
+#define osFileTimeToSystemTime ((BOOL(WINAPI*)(const FILETIME*, \
4774147743
LPSYSTEMTIME))aSyscall[12].pCurrent)
4774247744
4774347745
{ "FlushFileBuffers", (SYSCALL)FlushFileBuffers, 0 },
4774447746
4774547747
#define osFlushFileBuffers ((BOOL(WINAPI*)(HANDLE))aSyscall[13].pCurrent)
@@ -48015,11 +48017,11 @@
4801548017
{ "LockFile", (SYSCALL)LockFile, 0 },
4801648018
#else
4801748019
{ "LockFile", (SYSCALL)0, 0 },
4801848020
#endif
4801948021
48020
-#ifndef osLockFile
48022
+#if !defined(osLockFile) && defined(SQLITE_WIN32_HAS_ANSI)
4802148023
#define osLockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
4802248024
DWORD))aSyscall[47].pCurrent)
4802348025
#endif
4802448026
4802548027
#if !SQLITE_OS_WINCE
@@ -48079,20 +48081,20 @@
4807948081
4808048082
#define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[55].pCurrent)
4808148083
4808248084
{ "SystemTimeToFileTime", (SYSCALL)SystemTimeToFileTime, 0 },
4808348085
48084
-#define osSystemTimeToFileTime ((BOOL(WINAPI*)(CONST SYSTEMTIME*, \
48086
+#define osSystemTimeToFileTime ((BOOL(WINAPI*)(const SYSTEMTIME*, \
4808548087
LPFILETIME))aSyscall[56].pCurrent)
4808648088
4808748089
#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
4808848090
{ "UnlockFile", (SYSCALL)UnlockFile, 0 },
4808948091
#else
4809048092
{ "UnlockFile", (SYSCALL)0, 0 },
4809148093
#endif
4809248094
48093
-#ifndef osUnlockFile
48095
+#if !defined(osUnlockFile) && defined(SQLITE_WIN32_HAS_ANSI)
4809448096
#define osUnlockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
4809548097
DWORD))aSyscall[57].pCurrent)
4809648098
#endif
4809748099
4809848100
#if !SQLITE_OS_WINCE
@@ -48316,10 +48318,67 @@
4831648318
{ "CancelIo", (SYSCALL)0, 0 },
4831748319
#endif
4831848320
4831948321
#define osCancelIo ((BOOL(WINAPI*)(HANDLE))aSyscall[81].pCurrent)
4832048322
48323
+#if defined(SQLITE_WIN32_HAS_WIDE) && defined(_WIN32)
48324
+ { "GetModuleHandleW", (SYSCALL)GetModuleHandleW, 0 },
48325
+#else
48326
+ { "GetModuleHandleW", (SYSCALL)0, 0 },
48327
+#endif
48328
+
48329
+#define osGetModuleHandleW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[82].pCurrent)
48330
+
48331
+#ifndef _WIN32
48332
+ { "getenv", (SYSCALL)getenv, 0 },
48333
+#else
48334
+ { "getenv", (SYSCALL)0, 0 },
48335
+#endif
48336
+
48337
+#define osGetenv ((const char *(*)(const char *))aSyscall[83].pCurrent)
48338
+
48339
+#ifndef _WIN32
48340
+ { "getcwd", (SYSCALL)getcwd, 0 },
48341
+#else
48342
+ { "getcwd", (SYSCALL)0, 0 },
48343
+#endif
48344
+
48345
+#define osGetcwd ((char*(*)(char*,size_t))aSyscall[84].pCurrent)
48346
+
48347
+#ifndef _WIN32
48348
+ { "readlink", (SYSCALL)readlink, 0 },
48349
+#else
48350
+ { "readlink", (SYSCALL)0, 0 },
48351
+#endif
48352
+
48353
+#define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[85].pCurrent)
48354
+
48355
+#ifndef _WIN32
48356
+ { "lstat", (SYSCALL)lstat, 0 },
48357
+#else
48358
+ { "lstat", (SYSCALL)0, 0 },
48359
+#endif
48360
+
48361
+#define osLstat ((int(*)(const char*,struct stat*))aSyscall[86].pCurrent)
48362
+
48363
+#ifndef _WIN32
48364
+ { "__errno", (SYSCALL)__errno, 0 },
48365
+#else
48366
+ { "__errno", (SYSCALL)0, 0 },
48367
+#endif
48368
+
48369
+#define osErrno (*((int*(*)(void))aSyscall[87].pCurrent)())
48370
+
48371
+#ifndef _WIN32
48372
+ { "cygwin_conv_path", (SYSCALL)cygwin_conv_path, 0 },
48373
+#else
48374
+ { "cygwin_conv_path", (SYSCALL)0, 0 },
48375
+#endif
48376
+
48377
+#define osCygwin_conv_path ((size_t(*)(unsigned int, \
48378
+ const void *, void *, size_t))aSyscall[88].pCurrent)
48379
+
4832148380
}; /* End of the overrideable system calls */
4832248381
4832348382
/*
4832448383
** This is the xSetSystemCall() method of sqlite3_vfs for all of the
4832548384
** "win32" VFSes. Return SQLITE_OK upon successfully updating the
@@ -48489,10 +48548,11 @@
4848948548
sqlite3_mutex_leave(pMainMtx);
4849048549
return rc;
4849148550
}
4849248551
#endif /* SQLITE_WIN32_MALLOC */
4849348552
48553
+#ifdef _WIN32
4849448554
/*
4849548555
** This function outputs the specified (ANSI) string to the Win32 debugger
4849648556
** (if available).
4849748557
*/
4849848558
@@ -48531,10 +48591,11 @@
4853148591
}else{
4853248592
fprintf(stderr, "%s", zBuf);
4853348593
}
4853448594
#endif
4853548595
}
48596
+#endif /* _WIN32 */
4853648597
4853748598
/*
4853848599
** The following routine suspends the current thread for at least ms
4853948600
** milliseconds. This is equivalent to the Win32 Sleep() interface.
4854048601
*/
@@ -48831,10 +48892,11 @@
4883148892
SQLITE_PRIVATE void sqlite3MemSetDefault(void){
4883248893
sqlite3_config(SQLITE_CONFIG_MALLOC, sqlite3MemGetWin32());
4883348894
}
4883448895
#endif /* SQLITE_WIN32_MALLOC */
4883548896
48897
+#ifdef _WIN32
4883648898
/*
4883748899
** Convert a UTF-8 string to Microsoft Unicode.
4883848900
**
4883948901
** Space to hold the returned string is obtained from sqlite3_malloc().
4884048902
*/
@@ -48856,10 +48918,11 @@
4885648918
sqlite3_free(zWideText);
4885748919
zWideText = 0;
4885848920
}
4885948921
return zWideText;
4886048922
}
48923
+#endif /* _WIN32 */
4886148924
4886248925
/*
4886348926
** Convert a Microsoft Unicode string to UTF-8.
4886448927
**
4886548928
** Space to hold the returned string is obtained from sqlite3_malloc().
@@ -48890,32 +48953,33 @@
4889048953
** code page.
4889148954
**
4889248955
** Space to hold the returned string is obtained from sqlite3_malloc().
4889348956
*/
4889448957
static LPWSTR winMbcsToUnicode(const char *zText, int useAnsi){
48895
- int nByte;
48958
+ int nWideChar;
4889648959
LPWSTR zMbcsText;
4889748960
int codepage = useAnsi ? CP_ACP : CP_OEMCP;
4889848961
48899
- nByte = osMultiByteToWideChar(codepage, 0, zText, -1, NULL,
48900
- 0)*sizeof(WCHAR);
48901
- if( nByte==0 ){
48962
+ nWideChar = osMultiByteToWideChar(codepage, 0, zText, -1, NULL,
48963
+ 0);
48964
+ if( nWideChar==0 ){
4890248965
return 0;
4890348966
}
48904
- zMbcsText = sqlite3MallocZero( nByte*sizeof(WCHAR) );
48967
+ zMbcsText = sqlite3MallocZero( nWideChar*sizeof(WCHAR) );
4890548968
if( zMbcsText==0 ){
4890648969
return 0;
4890748970
}
48908
- nByte = osMultiByteToWideChar(codepage, 0, zText, -1, zMbcsText,
48909
- nByte);
48910
- if( nByte==0 ){
48971
+ nWideChar = osMultiByteToWideChar(codepage, 0, zText, -1, zMbcsText,
48972
+ nWideChar);
48973
+ if( nWideChar==0 ){
4891148974
sqlite3_free(zMbcsText);
4891248975
zMbcsText = 0;
4891348976
}
4891448977
return zMbcsText;
4891548978
}
4891648979
48980
+#ifdef _WIN32
4891748981
/*
4891848982
** Convert a Microsoft Unicode string to a multi-byte character string,
4891948983
** using the ANSI or OEM code page.
4892048984
**
4892148985
** Space to hold the returned string is obtained from sqlite3_malloc().
@@ -48939,10 +49003,11 @@
4893949003
sqlite3_free(zText);
4894049004
zText = 0;
4894149005
}
4894249006
return zText;
4894349007
}
49008
+#endif /* _WIN32 */
4894449009
4894549010
/*
4894649011
** Convert a multi-byte character string to UTF-8.
4894749012
**
4894849013
** Space to hold the returned string is obtained from sqlite3_malloc().
@@ -48958,10 +49023,11 @@
4895849023
zTextUtf8 = winUnicodeToUtf8(zTmpWide);
4895949024
sqlite3_free(zTmpWide);
4896049025
return zTextUtf8;
4896149026
}
4896249027
49028
+#ifdef _WIN32
4896349029
/*
4896449030
** Convert a UTF-8 string to a multi-byte character string.
4896549031
**
4896649032
** Space to hold the returned string is obtained from sqlite3_malloc().
4896749033
*/
@@ -49007,10 +49073,11 @@
4900749073
#ifndef SQLITE_OMIT_AUTOINIT
4900849074
if( sqlite3_initialize() ) return 0;
4900949075
#endif
4901049076
return winUnicodeToUtf8(zWideText);
4901149077
}
49078
+#endif /* _WIN32 */
4901249079
4901349080
/*
4901449081
** This is a public wrapper for the winMbcsToUtf8() function.
4901549082
*/
4901649083
SQLITE_API char *sqlite3_win32_mbcs_to_utf8(const char *zText){
@@ -49024,10 +49091,11 @@
4902449091
if( sqlite3_initialize() ) return 0;
4902549092
#endif
4902649093
return winMbcsToUtf8(zText, osAreFileApisANSI());
4902749094
}
4902849095
49096
+#ifdef _WIN32
4902949097
/*
4903049098
** This is a public wrapper for the winMbcsToUtf8() function.
4903149099
*/
4903249100
SQLITE_API char *sqlite3_win32_mbcs_to_utf8_v2(const char *zText, int useAnsi){
4903349101
#ifdef SQLITE_ENABLE_API_ARMOR
@@ -49148,10 +49216,11 @@
4914849216
unsigned long type, /* Identifier for directory being set or reset */
4914949217
void *zValue /* New value for directory being set or reset */
4915049218
){
4915149219
return sqlite3_win32_set_directory16(type, zValue);
4915249220
}
49221
+#endif /* _WIN32 */
4915349222
4915449223
/*
4915549224
** The return value of winGetLastErrorMsg
4915649225
** is zero if the error message fits in the buffer, or non-zero
4915749226
** otherwise (if the message was truncated).
@@ -49696,13 +49765,15 @@
4969649765
OVERLAPPED ovlp;
4969749766
memset(&ovlp, 0, sizeof(OVERLAPPED));
4969849767
ovlp.Offset = offsetLow;
4969949768
ovlp.OffsetHigh = offsetHigh;
4970049769
return osLockFileEx(*phFile, flags, 0, numBytesLow, numBytesHigh, &ovlp);
49770
+#ifdef SQLITE_WIN32_HAS_ANSI
4970149771
}else{
4970249772
return osLockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
4970349773
numBytesHigh);
49774
+#endif
4970449775
}
4970549776
#endif
4970649777
}
4970749778
4970849779
/*
@@ -49806,13 +49877,15 @@
4980649877
OVERLAPPED ovlp;
4980749878
memset(&ovlp, 0, sizeof(OVERLAPPED));
4980849879
ovlp.Offset = offsetLow;
4980949880
ovlp.OffsetHigh = offsetHigh;
4981049881
return osUnlockFileEx(*phFile, 0, numBytesLow, numBytesHigh, &ovlp);
49882
+#ifdef SQLITE_WIN32_HAS_ANSI
4981149883
}else{
4981249884
return osUnlockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
4981349885
numBytesHigh);
49886
+#endif
4981449887
}
4981549888
#endif
4981649889
}
4981749890
4981849891
/*
@@ -51222,18 +51295,95 @@
5122251295
5122351296
/*
5122451297
** Convert a UTF-8 filename into whatever form the underlying
5122551298
** operating system wants filenames in. Space to hold the result
5122651299
** is obtained from malloc and must be freed by the calling
51227
-** function.
51300
+** function
51301
+**
51302
+** On Cygwin, 3 possible input forms are accepted:
51303
+** - If the filename starts with "<drive>:/" or "<drive>:\",
51304
+** it is converted to UTF-16 as-is.
51305
+** - If the filename contains '/', it is assumed to be a
51306
+** Cygwin absolute path, it is converted to a win32
51307
+** absolute path in UTF-16.
51308
+** - Otherwise it must be a filename only, the win32 filename
51309
+** is returned in UTF-16.
51310
+** Note: If the function cygwin_conv_path() fails, only
51311
+** UTF-8 -> UTF-16 conversion will be done. This can only
51312
+** happen when the file path >32k, in which case winUtf8ToUnicode()
51313
+** will fail too.
5122851314
*/
5122951315
static void *winConvertFromUtf8Filename(const char *zFilename){
5123051316
void *zConverted = 0;
5123151317
if( osIsNT() ){
51318
+#ifdef __CYGWIN__
51319
+ int nChar;
51320
+ LPWSTR zWideFilename;
51321
+
51322
+ if( osCygwin_conv_path && !(winIsDriveLetterAndColon(zFilename)
51323
+ && winIsDirSep(zFilename[2])) ){
51324
+ int nByte;
51325
+ int convertflag = CCP_POSIX_TO_WIN_W;
51326
+ if( !strchr(zFilename, '/') ) convertflag |= CCP_RELATIVE;
51327
+ nByte = (int)osCygwin_conv_path(convertflag,
51328
+ zFilename, 0, 0);
51329
+ if( nByte>0 ){
51330
+ zConverted = sqlite3MallocZero(nByte+12);
51331
+ if ( zConverted==0 ){
51332
+ return zConverted;
51333
+ }
51334
+ zWideFilename = zConverted;
51335
+ /* Filenames should be prefixed, except when converted
51336
+ * full path already starts with "\\?\". */
51337
+ if( osCygwin_conv_path(convertflag, zFilename,
51338
+ zWideFilename+4, nByte)==0 ){
51339
+ if( (convertflag&CCP_RELATIVE) ){
51340
+ memmove(zWideFilename, zWideFilename+4, nByte);
51341
+ }else if( memcmp(zWideFilename+4, L"\\\\", 4) ){
51342
+ memcpy(zWideFilename, L"\\\\?\\", 8);
51343
+ }else if( zWideFilename[6]!='?' ){
51344
+ memmove(zWideFilename+6, zWideFilename+4, nByte);
51345
+ memcpy(zWideFilename, L"\\\\?\\UNC", 14);
51346
+ }else{
51347
+ memmove(zWideFilename, zWideFilename+4, nByte);
51348
+ }
51349
+ return zConverted;
51350
+ }
51351
+ sqlite3_free(zConverted);
51352
+ }
51353
+ }
51354
+ nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0);
51355
+ if( nChar==0 ){
51356
+ return 0;
51357
+ }
51358
+ zWideFilename = sqlite3MallocZero( nChar*sizeof(WCHAR)+12 );
51359
+ if( zWideFilename==0 ){
51360
+ return 0;
51361
+ }
51362
+ nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1,
51363
+ zWideFilename, nChar);
51364
+ if( nChar==0 ){
51365
+ sqlite3_free(zWideFilename);
51366
+ zWideFilename = 0;
51367
+ }else if( nChar>MAX_PATH
51368
+ && winIsDriveLetterAndColon(zFilename)
51369
+ && winIsDirSep(zFilename[2]) ){
51370
+ memmove(zWideFilename+4, zWideFilename, nChar*sizeof(WCHAR));
51371
+ zWideFilename[2] = '\\';
51372
+ memcpy(zWideFilename, L"\\\\?\\", 8);
51373
+ }else if( nChar>MAX_PATH
51374
+ && winIsDirSep(zFilename[0]) && winIsDirSep(zFilename[1])
51375
+ && zFilename[2] != '?' ){
51376
+ memmove(zWideFilename+6, zWideFilename, nChar*sizeof(WCHAR));
51377
+ memcpy(zWideFilename, L"\\\\?\\UNC", 14);
51378
+ }
51379
+ zConverted = zWideFilename;
51380
+#else
5123251381
zConverted = winUtf8ToUnicode(zFilename);
51382
+#endif /* __CYGWIN__ */
5123351383
}
51234
-#ifdef SQLITE_WIN32_HAS_ANSI
51384
+#if defined(SQLITE_WIN32_HAS_ANSI) && defined(_WIN32)
5123551385
else{
5123651386
zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI());
5123751387
}
5123851388
#endif
5123951389
/* caller will handle out of memory */
@@ -52058,11 +52208,11 @@
5205852208
**
5205952209
** This division contains the implementation of methods on the
5206052210
** sqlite3_vfs object.
5206152211
*/
5206252212
52063
-#if defined(__CYGWIN__)
52213
+#if 0 /* No longer necessary */
5206452214
/*
5206552215
** Convert a filename from whatever the underlying operating system
5206652216
** supports for filenames into UTF-8. Space to hold the result is
5206752217
** obtained from malloc and must be freed by the calling function.
5206852218
*/
@@ -52091,11 +52241,18 @@
5209152241
int nLen = sqlite3Strlen30(zBuf);
5209252242
if( nLen>0 ){
5209352243
if( winIsDirSep(zBuf[nLen-1]) ){
5209452244
return 1;
5209552245
}else if( nLen+1<nBuf ){
52096
- zBuf[nLen] = winGetDirSep();
52246
+ if( !osGetenv ){
52247
+ zBuf[nLen] = winGetDirSep();
52248
+ }else if( winIsDriveLetterAndColon(zBuf) && winIsDirSep(zBuf[2]) ){
52249
+ zBuf[nLen] = '\\';
52250
+ zBuf[2]='\\';
52251
+ }else{
52252
+ zBuf[nLen] = '/';
52253
+ }
5209752254
zBuf[nLen+1] = '\0';
5209852255
return 1;
5209952256
}
5210052257
}
5210152258
}
@@ -52118,11 +52275,11 @@
5211852275
/*
5211952276
** Create a temporary file name and store the resulting pointer into pzBuf.
5212052277
** The pointer returned in pzBuf must be freed via sqlite3_free().
5212152278
*/
5212252279
static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
52123
- static char zChars[] =
52280
+ static const char zChars[] =
5212452281
"abcdefghijklmnopqrstuvwxyz"
5212552282
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
5212652283
"0123456789";
5212752284
size_t i, j;
5212852285
DWORD pid;
@@ -52169,11 +52326,11 @@
5216952326
}
5217052327
sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
5217152328
}
5217252329
5217352330
#if defined(__CYGWIN__)
52174
- else{
52331
+ else if( osGetenv!=NULL ){
5217552332
static const char *azDirs[] = {
5217652333
0, /* getenv("SQLITE_TMPDIR") */
5217752334
0, /* getenv("TMPDIR") */
5217852335
0, /* getenv("TMP") */
5217952336
0, /* getenv("TEMP") */
@@ -52185,24 +52342,24 @@
5218552342
0 /* List terminator */
5218652343
};
5218752344
unsigned int i;
5218852345
const char *zDir = 0;
5218952346
52190
- if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR");
52191
- if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR");
52192
- if( !azDirs[2] ) azDirs[2] = getenv("TMP");
52193
- if( !azDirs[3] ) azDirs[3] = getenv("TEMP");
52194
- if( !azDirs[4] ) azDirs[4] = getenv("USERPROFILE");
52347
+ if( !azDirs[0] ) azDirs[0] = osGetenv("SQLITE_TMPDIR");
52348
+ if( !azDirs[1] ) azDirs[1] = osGetenv("TMPDIR");
52349
+ if( !azDirs[2] ) azDirs[2] = osGetenv("TMP");
52350
+ if( !azDirs[3] ) azDirs[3] = osGetenv("TEMP");
52351
+ if( !azDirs[4] ) azDirs[4] = osGetenv("USERPROFILE");
5219552352
for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){
5219652353
void *zConverted;
5219752354
if( zDir==0 ) continue;
5219852355
/* If the path starts with a drive letter followed by the colon
5219952356
** character, assume it is already a native Win32 path; otherwise,
5220052357
** it must be converted to a native Win32 path via the Cygwin API
5220152358
** prior to using it.
5220252359
*/
52203
- if( winIsDriveLetterAndColon(zDir) ){
52360
+ {
5220452361
zConverted = winConvertFromUtf8Filename(zDir);
5220552362
if( !zConverted ){
5220652363
sqlite3_free(zBuf);
5220752364
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
5220852365
return SQLITE_IOERR_NOMEM_BKPT;
@@ -52211,19 +52368,20 @@
5221152368
sqlite3_snprintf(nMax, zBuf, "%s", zDir);
5221252369
sqlite3_free(zConverted);
5221352370
break;
5221452371
}
5221552372
sqlite3_free(zConverted);
52373
+#if 0 /* No longer necessary */
5221652374
}else{
5221752375
zConverted = sqlite3MallocZero( nMax+1 );
5221852376
if( !zConverted ){
5221952377
sqlite3_free(zBuf);
5222052378
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
5222152379
return SQLITE_IOERR_NOMEM_BKPT;
5222252380
}
52223
- if( cygwin_conv_path(
52224
- osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A, zDir,
52381
+ if( osCygwin_conv_path(
52382
+ CCP_POSIX_TO_WIN_W, zDir,
5222552383
zConverted, nMax+1)<0 ){
5222652384
sqlite3_free(zConverted);
5222752385
sqlite3_free(zBuf);
5222852386
OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_CONVPATH\n"));
5222952387
return winLogError(SQLITE_IOERR_CONVPATH, (DWORD)errno,
@@ -52245,14 +52403,17 @@
5224552403
sqlite3_free(zUtf8);
5224652404
sqlite3_free(zConverted);
5224752405
break;
5224852406
}
5224952407
sqlite3_free(zConverted);
52408
+#endif /* No longer necessary */
5225052409
}
5225152410
}
5225252411
}
52253
-#elif !SQLITE_OS_WINRT && !defined(__CYGWIN__)
52412
+#endif
52413
+
52414
+#if !SQLITE_OS_WINRT && defined(_WIN32)
5225452415
else if( osIsNT() ){
5225552416
char *zMulti;
5225652417
LPWSTR zWidePath = sqlite3MallocZero( nMax*sizeof(WCHAR) );
5225752418
if( !zWidePath ){
5225852419
sqlite3_free(zBuf);
@@ -52372,11 +52533,11 @@
5237252533
&sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){}
5237352534
if( !rc ){
5237452535
return 0; /* Invalid name? */
5237552536
}
5237652537
attr = sAttrData.dwFileAttributes;
52377
-#if SQLITE_OS_WINCE==0
52538
+#if SQLITE_OS_WINCE==0 && defined(SQLITE_WIN32_HAS_ANSI)
5237852539
}else{
5237952540
attr = osGetFileAttributesA((char*)zConverted);
5238052541
#endif
5238152542
}
5238252543
return (attr!=INVALID_FILE_ATTRIBUTES) && (attr&FILE_ATTRIBUTE_DIRECTORY);
@@ -52388,10 +52549,16 @@
5238852549
const char *zFilename, /* Name of file to check */
5238952550
int flags, /* Type of test to make on this file */
5239052551
int *pResOut /* OUT: Result */
5239152552
);
5239252553
52554
+/*
52555
+** The Windows version of xAccess() accepts an extra bit in the flags
52556
+** parameter that prevents an anti-virus retry loop.
52557
+*/
52558
+#define NORETRY 0x4000
52559
+
5239352560
/*
5239452561
** Open a file.
5239552562
*/
5239652563
static int winOpen(
5239752564
sqlite3_vfs *pVfs, /* Used to get maximum path length and AppData */
@@ -52412,10 +52579,11 @@
5241252579
winVfsAppData *pAppData;
5241352580
winFile *pFile = (winFile*)id;
5241452581
void *zConverted; /* Filename in OS encoding */
5241552582
const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
5241652583
int cnt = 0;
52584
+ int isRO = 0; /* file is known to be accessible readonly */
5241752585
5241852586
/* If argument zPath is a NULL pointer, this function is required to open
5241952587
** a temporary file. Use this buffer to store the file name in.
5242052588
*/
5242152589
char *zTmpname = 0; /* For temporary filename, if necessary. */
@@ -52576,13 +52744,13 @@
5257652744
dwShareMode,
5257752745
dwCreationDisposition,
5257852746
&extendedParameters);
5257952747
if( h!=INVALID_HANDLE_VALUE ) break;
5258052748
if( isReadWrite ){
52581
- int rc2, isRO = 0;
52749
+ int rc2;
5258252750
sqlite3BeginBenignMalloc();
52583
- rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO);
52751
+ rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ|NORETRY, &isRO);
5258452752
sqlite3EndBenignMalloc();
5258552753
if( rc2==SQLITE_OK && isRO ) break;
5258652754
}
5258752755
}while( winRetryIoerr(&cnt, &lastErrno) );
5258852756
#else
@@ -52593,13 +52761,13 @@
5259352761
dwCreationDisposition,
5259452762
dwFlagsAndAttributes,
5259552763
NULL);
5259652764
if( h!=INVALID_HANDLE_VALUE ) break;
5259752765
if( isReadWrite ){
52598
- int rc2, isRO = 0;
52766
+ int rc2;
5259952767
sqlite3BeginBenignMalloc();
52600
- rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO);
52768
+ rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ|NORETRY, &isRO);
5260152769
sqlite3EndBenignMalloc();
5260252770
if( rc2==SQLITE_OK && isRO ) break;
5260352771
}
5260452772
}while( winRetryIoerr(&cnt, &lastErrno) );
5260552773
#endif
@@ -52613,13 +52781,13 @@
5261352781
dwCreationDisposition,
5261452782
dwFlagsAndAttributes,
5261552783
NULL);
5261652784
if( h!=INVALID_HANDLE_VALUE ) break;
5261752785
if( isReadWrite ){
52618
- int rc2, isRO = 0;
52786
+ int rc2;
5261952787
sqlite3BeginBenignMalloc();
52620
- rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO);
52788
+ rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ|NORETRY, &isRO);
5262152789
sqlite3EndBenignMalloc();
5262252790
if( rc2==SQLITE_OK && isRO ) break;
5262352791
}
5262452792
}while( winRetryIoerr(&cnt, &lastErrno) );
5262552793
}
@@ -52630,11 +52798,11 @@
5263052798
dwDesiredAccess, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok"));
5263152799
5263252800
if( h==INVALID_HANDLE_VALUE ){
5263352801
sqlite3_free(zConverted);
5263452802
sqlite3_free(zTmpname);
52635
- if( isReadWrite && !isExclusive ){
52803
+ if( isReadWrite && isRO && !isExclusive ){
5263652804
return winOpen(pVfs, zName, id,
5263752805
((flags|SQLITE_OPEN_READONLY) &
5263852806
~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)),
5263952807
pOutFlags);
5264052808
}else{
@@ -52832,11 +53000,17 @@
5283253000
){
5283353001
DWORD attr;
5283453002
int rc = 0;
5283553003
DWORD lastErrno = 0;
5283653004
void *zConverted;
53005
+ int noRetry = 0; /* Do not use winRetryIoerr() */
5283753006
UNUSED_PARAMETER(pVfs);
53007
+
53008
+ if( (flags & NORETRY)!=0 ){
53009
+ noRetry = 1;
53010
+ flags &= ~NORETRY;
53011
+ }
5283853012
5283953013
SimulateIOError( return SQLITE_IOERR_ACCESS; );
5284053014
OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n",
5284153015
zFilename, flags, pResOut));
5284253016
@@ -52856,11 +53030,14 @@
5285653030
int cnt = 0;
5285753031
WIN32_FILE_ATTRIBUTE_DATA sAttrData;
5285853032
memset(&sAttrData, 0, sizeof(sAttrData));
5285953033
while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted,
5286053034
GetFileExInfoStandard,
52861
- &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){}
53035
+ &sAttrData))
53036
+ && !noRetry
53037
+ && winRetryIoerr(&cnt, &lastErrno)
53038
+ ){ /* Loop until true */}
5286253039
if( rc ){
5286353040
/* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file
5286453041
** as if it does not exist.
5286553042
*/
5286653043
if( flags==SQLITE_ACCESS_EXISTS
@@ -52924,10 +53101,11 @@
5292453101
const char *zPathname
5292553102
){
5292653103
return ( sqlite3Isalpha(zPathname[0]) && zPathname[1]==':' );
5292753104
}
5292853105
53106
+#ifdef _WIN32
5292953107
/*
5293053108
** Returns non-zero if the specified path name should be used verbatim. If
5293153109
** non-zero is returned from this function, the calling function must simply
5293253110
** use the provided path name verbatim -OR- resolve it into a full path name
5293353111
** using the GetFullPathName Win32 API function (if available).
@@ -52960,10 +53138,74 @@
5296053138
** If we get to this point, the path name should almost certainly be a purely
5296153139
** relative one (i.e. not a UNC name, not absolute, and not volume relative).
5296253140
*/
5296353141
return FALSE;
5296453142
}
53143
+#endif /* _WIN32 */
53144
+
53145
+#ifdef __CYGWIN__
53146
+/*
53147
+** Simplify a filename into its canonical form
53148
+** by making the following changes:
53149
+**
53150
+** * convert any '/' to '\' (win32) or reverse (Cygwin)
53151
+** * removing any trailing and duplicate / (except for UNC paths)
53152
+** * convert /./ into just /
53153
+**
53154
+** Changes are made in-place. Return the new name length.
53155
+**
53156
+** The original filename is in z[0..]. If the path is shortened,
53157
+** no-longer used bytes will be written by '\0'.
53158
+*/
53159
+static void winSimplifyName(char *z){
53160
+ int i, j;
53161
+ for(i=j=0; z[i]; ++i){
53162
+ if( winIsDirSep(z[i]) ){
53163
+#if !defined(SQLITE_TEST)
53164
+ /* Some test-cases assume that "./foo" and "foo" are different */
53165
+ if( z[i+1]=='.' && winIsDirSep(z[i+2]) ){
53166
+ ++i;
53167
+ continue;
53168
+ }
53169
+#endif
53170
+ if( !z[i+1] || (winIsDirSep(z[i+1]) && (i!=0)) ){
53171
+ continue;
53172
+ }
53173
+ z[j++] = osGetenv?'/':'\\';
53174
+ }else{
53175
+ z[j++] = z[i];
53176
+ }
53177
+ }
53178
+ while(j<i) z[j++] = '\0';
53179
+}
53180
+
53181
+#define SQLITE_MAX_SYMLINKS 100
53182
+
53183
+static int mkFullPathname(
53184
+ const char *zPath, /* Input path */
53185
+ char *zOut, /* Output buffer */
53186
+ int nOut /* Allocated size of buffer zOut */
53187
+){
53188
+ int nPath = sqlite3Strlen30(zPath);
53189
+ int iOff = 0;
53190
+ if( zPath[0]!='/' ){
53191
+ if( osGetcwd(zOut, nOut-2)==0 ){
53192
+ return winLogError(SQLITE_CANTOPEN_BKPT, (DWORD)osErrno, "getcwd", zPath);
53193
+ }
53194
+ iOff = sqlite3Strlen30(zOut);
53195
+ zOut[iOff++] = '/';
53196
+ }
53197
+ if( (iOff+nPath+1)>nOut ){
53198
+ /* SQLite assumes that xFullPathname() nul-terminates the output buffer
53199
+ ** even if it returns an error. */
53200
+ zOut[iOff] = '\0';
53201
+ return SQLITE_CANTOPEN_BKPT;
53202
+ }
53203
+ sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath);
53204
+ return SQLITE_OK;
53205
+}
53206
+#endif /* __CYGWIN__ */
5296553207
5296653208
/*
5296753209
** Turn a relative pathname into a full pathname. Write the full
5296853210
** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname
5296953211
** bytes in size.
@@ -52972,12 +53214,12 @@
5297253214
sqlite3_vfs *pVfs, /* Pointer to vfs object */
5297353215
const char *zRelative, /* Possibly relative input path */
5297453216
int nFull, /* Size of output buffer in bytes */
5297553217
char *zFull /* Output buffer */
5297653218
){
52977
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__)
52978
- DWORD nByte;
53219
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
53220
+ int nByte;
5297953221
void *zConverted;
5298053222
char *zOut;
5298153223
#endif
5298253224
5298353225
/* If this path name begins with "/X:" or "\\?\", where "X" is any
@@ -52986,68 +53228,114 @@
5298653228
if( zRelative[0]=='/' && (winIsDriveLetterAndColon(zRelative+1)
5298753229
|| winIsLongPathPrefix(zRelative+1)) ){
5298853230
zRelative++;
5298953231
}
5299053232
52991
-#if defined(__CYGWIN__)
53233
+ SimulateIOError( return SQLITE_ERROR );
53234
+
53235
+#ifdef __CYGWIN__
53236
+ if( osGetcwd ){
53237
+ zFull[nFull-1] = '\0';
53238
+ if( !winIsDriveLetterAndColon(zRelative) || !winIsDirSep(zRelative[2]) ){
53239
+ int rc = SQLITE_OK;
53240
+ int nLink = 1; /* Number of symbolic links followed so far */
53241
+ const char *zIn = zRelative; /* Input path for each iteration of loop */
53242
+ char *zDel = 0;
53243
+ struct stat buf;
53244
+
53245
+ UNUSED_PARAMETER(pVfs);
53246
+
53247
+ do {
53248
+ /* Call lstat() on path zIn. Set bLink to true if the path is a symbolic
53249
+ ** link, or false otherwise. */
53250
+ int bLink = 0;
53251
+ if( osLstat && osReadlink ) {
53252
+ if( osLstat(zIn, &buf)!=0 ){
53253
+ int myErrno = osErrno;
53254
+ if( myErrno!=ENOENT ){
53255
+ rc = winLogError(SQLITE_CANTOPEN_BKPT, (DWORD)myErrno, "lstat", zIn);
53256
+ }
53257
+ }else{
53258
+ bLink = ((buf.st_mode & 0170000) == 0120000);
53259
+ }
53260
+
53261
+ if( bLink ){
53262
+ if( zDel==0 ){
53263
+ zDel = sqlite3MallocZero(nFull);
53264
+ if( zDel==0 ) rc = SQLITE_NOMEM;
53265
+ }else if( ++nLink>SQLITE_MAX_SYMLINKS ){
53266
+ rc = SQLITE_CANTOPEN_BKPT;
53267
+ }
53268
+
53269
+ if( rc==SQLITE_OK ){
53270
+ nByte = osReadlink(zIn, zDel, nFull-1);
53271
+ if( nByte ==(DWORD)-1 ){
53272
+ rc = winLogError(SQLITE_CANTOPEN_BKPT, (DWORD)osErrno, "readlink", zIn);
53273
+ }else{
53274
+ if( zDel[0]!='/' ){
53275
+ int n;
53276
+ for(n = sqlite3Strlen30(zIn); n>0 && zIn[n-1]!='/'; n--);
53277
+ if( nByte+n+1>nFull ){
53278
+ rc = SQLITE_CANTOPEN_BKPT;
53279
+ }else{
53280
+ memmove(&zDel[n], zDel, nByte+1);
53281
+ memcpy(zDel, zIn, n);
53282
+ nByte += n;
53283
+ }
53284
+ }
53285
+ zDel[nByte] = '\0';
53286
+ }
53287
+ }
53288
+
53289
+ zIn = zDel;
53290
+ }
53291
+ }
53292
+
53293
+ assert( rc!=SQLITE_OK || zIn!=zFull || zIn[0]=='/' );
53294
+ if( rc==SQLITE_OK && zIn!=zFull ){
53295
+ rc = mkFullPathname(zIn, zFull, nFull);
53296
+ }
53297
+ if( bLink==0 ) break;
53298
+ zIn = zFull;
53299
+ }while( rc==SQLITE_OK );
53300
+
53301
+ sqlite3_free(zDel);
53302
+ winSimplifyName(zFull);
53303
+ return rc;
53304
+ }
53305
+ }
53306
+#endif /* __CYGWIN__ */
53307
+#if 0 /* This doesn't work correctly at all! See:
53308
+ <https://marc.info/?l=sqlite-users&m=139299149416314&w=2>
53309
+*/
5299253310
SimulateIOError( return SQLITE_ERROR );
5299353311
UNUSED_PARAMETER(nFull);
5299453312
assert( nFull>=pVfs->mxPathname );
52995
- if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
52996
- /*
52997
- ** NOTE: We are dealing with a relative path name and the data
52998
- ** directory has been set. Therefore, use it as the basis
52999
- ** for converting the relative path name to an absolute
53000
- ** one by prepending the data directory and a slash.
53001
- */
53002
- char *zOut = sqlite3MallocZero( 1+(u64)pVfs->mxPathname );
53003
- if( !zOut ){
53004
- return SQLITE_IOERR_NOMEM_BKPT;
53005
- }
53006
- if( cygwin_conv_path(
53007
- (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A) |
53008
- CCP_RELATIVE, zRelative, zOut, pVfs->mxPathname+1)<0 ){
53009
- sqlite3_free(zOut);
53010
- return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno,
53011
- "winFullPathname1", zRelative);
53012
- }else{
53013
- char *zUtf8 = winConvertToUtf8Filename(zOut);
53014
- if( !zUtf8 ){
53015
- sqlite3_free(zOut);
53016
- return SQLITE_IOERR_NOMEM_BKPT;
53017
- }
53018
- sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s",
53019
- sqlite3_data_directory, winGetDirSep(), zUtf8);
53020
- sqlite3_free(zUtf8);
53021
- sqlite3_free(zOut);
53022
- }
53023
- }else{
53024
- char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 );
53025
- if( !zOut ){
53026
- return SQLITE_IOERR_NOMEM_BKPT;
53027
- }
53028
- if( cygwin_conv_path(
53029
- (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A),
53030
- zRelative, zOut, pVfs->mxPathname+1)<0 ){
53031
- sqlite3_free(zOut);
53032
- return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno,
53033
- "winFullPathname2", zRelative);
53034
- }else{
53035
- char *zUtf8 = winConvertToUtf8Filename(zOut);
53036
- if( !zUtf8 ){
53037
- sqlite3_free(zOut);
53038
- return SQLITE_IOERR_NOMEM_BKPT;
53039
- }
53040
- sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zUtf8);
53041
- sqlite3_free(zUtf8);
53042
- sqlite3_free(zOut);
53043
- }
53044
- }
53045
- return SQLITE_OK;
53046
-#endif
53047
-
53048
-#if (SQLITE_OS_WINCE || SQLITE_OS_WINRT) && !defined(__CYGWIN__)
53313
+ char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 );
53314
+ if( !zOut ){
53315
+ return SQLITE_IOERR_NOMEM_BKPT;
53316
+ }
53317
+ if( osCygwin_conv_path(
53318
+ CCP_POSIX_TO_WIN_W,
53319
+ zRelative, zOut, pVfs->mxPathname+1)<0 ){
53320
+ sqlite3_free(zOut);
53321
+ return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno,
53322
+ "winFullPathname2", zRelative);
53323
+ }else{
53324
+ char *zUtf8 = winConvertToUtf8Filename(zOut);
53325
+ if( !zUtf8 ){
53326
+ sqlite3_free(zOut);
53327
+ return SQLITE_IOERR_NOMEM_BKPT;
53328
+ }
53329
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zUtf8);
53330
+ sqlite3_free(zUtf8);
53331
+ sqlite3_free(zOut);
53332
+ }
53333
+ return SQLITE_OK;
53334
+#endif
53335
+
53336
+#if (SQLITE_OS_WINCE || SQLITE_OS_WINRT) && defined(_WIN32)
5304953337
SimulateIOError( return SQLITE_ERROR );
5305053338
/* WinCE has no concept of a relative pathname, or so I am told. */
5305153339
/* WinRT has no way to convert a relative path to an absolute one. */
5305253340
if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
5305353341
/*
@@ -53062,11 +53350,12 @@
5306253350
sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zRelative);
5306353351
}
5306453352
return SQLITE_OK;
5306553353
#endif
5306653354
53067
-#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__)
53355
+#if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
53356
+#if defined(_WIN32)
5306853357
/* It's odd to simulate an io-error here, but really this is just
5306953358
** using the io-error infrastructure to test that SQLite handles this
5307053359
** function failing. This function could fail if, for example, the
5307153360
** current working directory has been unlinked.
5307253361
*/
@@ -53080,10 +53369,11 @@
5308053369
*/
5308153370
sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s",
5308253371
sqlite3_data_directory, winGetDirSep(), zRelative);
5308353372
return SQLITE_OK;
5308453373
}
53374
+#endif
5308553375
zConverted = winConvertFromUtf8Filename(zRelative);
5308653376
if( zConverted==0 ){
5308753377
return SQLITE_IOERR_NOMEM_BKPT;
5308853378
}
5308953379
if( osIsNT() ){
@@ -53092,16 +53382,17 @@
5309253382
if( nByte==0 ){
5309353383
sqlite3_free(zConverted);
5309453384
return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
5309553385
"winFullPathname1", zRelative);
5309653386
}
53097
- zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) + 3*sizeof(zTemp[0]) );
53387
+ nByte += 3;
53388
+ zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) );
5309853389
if( zTemp==0 ){
5309953390
sqlite3_free(zConverted);
5310053391
return SQLITE_IOERR_NOMEM_BKPT;
5310153392
}
53102
- nByte = osGetFullPathNameW((LPCWSTR)zConverted, nByte+3, zTemp, 0);
53393
+ nByte = osGetFullPathNameW((LPCWSTR)zConverted, nByte, zTemp, 0);
5310353394
if( nByte==0 ){
5310453395
sqlite3_free(zConverted);
5310553396
sqlite3_free(zTemp);
5310653397
return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
5310753398
"winFullPathname2", zRelative);
@@ -53135,11 +53426,30 @@
5313553426
zOut = winMbcsToUtf8(zTemp, osAreFileApisANSI());
5313653427
sqlite3_free(zTemp);
5313753428
}
5313853429
#endif
5313953430
if( zOut ){
53431
+#ifdef __CYGWIN__
53432
+ if( memcmp(zOut, "\\\\?\\", 4) ){
53433
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut);
53434
+ }else if( memcmp(zOut+4, "UNC\\", 4) ){
53435
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut+4);
53436
+ }else{
53437
+ char *p = zOut+6;
53438
+ *p = '\\';
53439
+ if( osGetcwd ){
53440
+ /* On Cygwin, UNC paths use forward slashes */
53441
+ while( *p ){
53442
+ if( *p=='\\' ) *p = '/';
53443
+ ++p;
53444
+ }
53445
+ }
53446
+ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut+6);
53447
+ }
53448
+#else
5314053449
sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut);
53450
+#endif /* __CYGWIN__ */
5314153451
sqlite3_free(zOut);
5314253452
return SQLITE_OK;
5314353453
}else{
5314453454
return SQLITE_IOERR_NOMEM_BKPT;
5314553455
}
@@ -53165,11 +53475,13 @@
5316553475
** Interfaces for opening a shared library, finding entry points
5316653476
** within the shared library, and closing the shared library.
5316753477
*/
5316853478
static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){
5316953479
HANDLE h;
53170
-#if defined(__CYGWIN__)
53480
+#if 0 /* This doesn't work correctly at all! See:
53481
+ <https://marc.info/?l=sqlite-users&m=139299149416314&w=2>
53482
+*/
5317153483
int nFull = pVfs->mxPathname+1;
5317253484
char *zFull = sqlite3MallocZero( nFull );
5317353485
void *zConverted = 0;
5317453486
if( zFull==0 ){
5317553487
OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0));
@@ -53532,11 +53844,11 @@
5353253844
};
5353353845
#endif
5353453846
5353553847
/* Double-check that the aSyscall[] array has been constructed
5353653848
** correctly. See ticket [bb3a86e890c8e96ab] */
53537
- assert( ArraySize(aSyscall)==82 );
53849
+ assert( ArraySize(aSyscall)==89 );
5353853850
5353953851
/* get memory map allocation granularity */
5354053852
memset(&winSysInfo, 0, sizeof(SYSTEM_INFO));
5354153853
#if SQLITE_OS_WINRT
5354253854
osGetNativeSystemInfo(&winSysInfo);
@@ -66508,14 +66820,12 @@
6650866820
s2 = aIn[1];
6650966821
}else{
6651066822
s1 = s2 = 0;
6651166823
}
6651266824
66513
- assert( nByte>=8 );
66514
- assert( (nByte&0x00000007)==0 );
66515
- assert( nByte<=65536 );
66516
- assert( nByte%4==0 );
66825
+ /* nByte is a multiple of 8 between 8 and 65536 */
66826
+ assert( nByte>=8 && (nByte&7)==0 && nByte<=65536 );
6651766827
6651866828
if( !nativeCksum ){
6651966829
do {
6652066830
s1 += BYTESWAP32(aData[0]) + s2;
6652166831
s2 += BYTESWAP32(aData[1]) + s1;
@@ -147785,10 +148095,11 @@
147785148095
}
147786148096
147787148097
multi_select_end:
147788148098
pDest->iSdst = dest.iSdst;
147789148099
pDest->nSdst = dest.nSdst;
148100
+ pDest->iSDParm2 = dest.iSDParm2;
147790148101
if( pDelete ){
147791148102
sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pDelete);
147792148103
}
147793148104
return rc;
147794148105
}
@@ -188065,10 +188376,17 @@
188065188376
******************************************************************************
188066188377
**
188067188378
*/
188068188379
#ifndef _FTSINT_H
188069188380
#define _FTSINT_H
188381
+
188382
+/* #include <assert.h> */
188383
+/* #include <stdlib.h> */
188384
+/* #include <stddef.h> */
188385
+/* #include <stdio.h> */
188386
+/* #include <string.h> */
188387
+/* #include <stdarg.h> */
188070188388
188071188389
#if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
188072188390
# define NDEBUG 1
188073188391
#endif
188074188392
@@ -189017,16 +189335,10 @@
189017189335
189018189336
#if defined(SQLITE_ENABLE_FTS3) && !defined(SQLITE_CORE)
189019189337
# define SQLITE_CORE 1
189020189338
#endif
189021189339
189022
-/* #include <assert.h> */
189023
-/* #include <stdlib.h> */
189024
-/* #include <stddef.h> */
189025
-/* #include <stdio.h> */
189026
-/* #include <string.h> */
189027
-/* #include <stdarg.h> */
189028189340
189029189341
/* #include "fts3.h" */
189030189342
#ifndef SQLITE_CORE
189031189343
/* # include "sqlite3ext.h" */
189032189344
SQLITE_EXTENSION_INIT1
@@ -227533,12 +227845,12 @@
227533227845
){
227534227846
if( sqlite3_value_type(argv[3])==SQLITE_NULL && isInsert && pgno>1 ){
227535227847
/* "INSERT INTO dbpage($PGNO,NULL)" causes page number $PGNO and
227536227848
** all subsequent pages to be deleted. */
227537227849
pTab->iDbTrunc = iDb;
227538
- pgno--;
227539
- pTab->pgnoTrunc = pgno;
227850
+ pTab->pgnoTrunc = pgno-1;
227851
+ pgno = 1;
227540227852
}else{
227541227853
zErr = "bad page value";
227542227854
goto update_fail;
227543227855
}
227544227856
}
@@ -241869,11 +242181,12 @@
241869242181
sqlite3Fts5ParseError(
241870242182
pParse, "expected integer, got \"%.*s\"", p->n, p->p
241871242183
);
241872242184
return;
241873242185
}
241874
- nNear = nNear * 10 + (p->p[i] - '0');
242186
+ if( nNear<214748363 ) nNear = nNear * 10 + (p->p[i] - '0');
242187
+ /* ^^^^^^^^^^^^^^^--- Prevent integer overflow */
241875242188
}
241876242189
}else{
241877242190
nNear = FTS5_DEFAULT_NEARDIST;
241878242191
}
241879242192
pNear->nNear = nNear;
@@ -256775,11 +257088,11 @@
256775257088
int nArg, /* Number of args */
256776257089
sqlite3_value **apUnused /* Function arguments */
256777257090
){
256778257091
assert( nArg==0 );
256779257092
UNUSED_PARAM2(nArg, apUnused);
256780
- sqlite3_result_text(pCtx, "fts5: 2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185", -1, SQLITE_TRANSIENT);
257093
+ sqlite3_result_text(pCtx, "fts5: 2025-03-27 23:29:25 121f4d97f9a855131859d342bc2ade5f8c34ba7732029ae156d02cec7cb6dd85", -1, SQLITE_TRANSIENT);
256781257094
}
256782257095
256783257096
/*
256784257097
** Implementation of fts5_locale(LOCALE, TEXT) function.
256785257098
**
256786257099
--- extsrc/sqlite3.c
+++ extsrc/sqlite3.c
@@ -16,11 +16,11 @@
16 ** if you want a wrapper to interface SQLite with your choice of programming
17 ** language. The code for the "sqlite3" command-line shell is also in a
18 ** separate file. This file contains only code for the core SQLite library.
19 **
20 ** The content in this amalgamation comes from Fossil check-in
21 ** 18bda13e197e4b4ec7464b3e70012f71edc0 with changes in files:
22 **
23 **
24 */
25 #ifndef SQLITE_AMALGAMATION
26 #define SQLITE_CORE 1
@@ -465,11 +465,11 @@
465 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
466 ** [sqlite_version()] and [sqlite_source_id()].
467 */
468 #define SQLITE_VERSION "3.50.0"
469 #define SQLITE_VERSION_NUMBER 3050000
470 #define SQLITE_SOURCE_ID "2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185"
471
472 /*
473 ** CAPI3REF: Run-Time Library Version Numbers
474 ** KEYWORDS: sqlite3_version sqlite3_sourceid
475 **
@@ -30270,10 +30270,12 @@
30270 */
30271 #include "windows.h"
30272
30273 #ifdef __CYGWIN__
30274 # include <sys/cygwin.h>
 
 
30275 # include <errno.h> /* amalgamator: dontcache */
30276 #endif
30277
30278 /*
30279 ** Determine if we are dealing with Windows NT.
@@ -47726,20 +47728,20 @@
47726 { "FileTimeToLocalFileTime", (SYSCALL)FileTimeToLocalFileTime, 0 },
47727 #else
47728 { "FileTimeToLocalFileTime", (SYSCALL)0, 0 },
47729 #endif
47730
47731 #define osFileTimeToLocalFileTime ((BOOL(WINAPI*)(CONST FILETIME*, \
47732 LPFILETIME))aSyscall[11].pCurrent)
47733
47734 #if SQLITE_OS_WINCE
47735 { "FileTimeToSystemTime", (SYSCALL)FileTimeToSystemTime, 0 },
47736 #else
47737 { "FileTimeToSystemTime", (SYSCALL)0, 0 },
47738 #endif
47739
47740 #define osFileTimeToSystemTime ((BOOL(WINAPI*)(CONST FILETIME*, \
47741 LPSYSTEMTIME))aSyscall[12].pCurrent)
47742
47743 { "FlushFileBuffers", (SYSCALL)FlushFileBuffers, 0 },
47744
47745 #define osFlushFileBuffers ((BOOL(WINAPI*)(HANDLE))aSyscall[13].pCurrent)
@@ -48015,11 +48017,11 @@
48015 { "LockFile", (SYSCALL)LockFile, 0 },
48016 #else
48017 { "LockFile", (SYSCALL)0, 0 },
48018 #endif
48019
48020 #ifndef osLockFile
48021 #define osLockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
48022 DWORD))aSyscall[47].pCurrent)
48023 #endif
48024
48025 #if !SQLITE_OS_WINCE
@@ -48079,20 +48081,20 @@
48079
48080 #define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[55].pCurrent)
48081
48082 { "SystemTimeToFileTime", (SYSCALL)SystemTimeToFileTime, 0 },
48083
48084 #define osSystemTimeToFileTime ((BOOL(WINAPI*)(CONST SYSTEMTIME*, \
48085 LPFILETIME))aSyscall[56].pCurrent)
48086
48087 #if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
48088 { "UnlockFile", (SYSCALL)UnlockFile, 0 },
48089 #else
48090 { "UnlockFile", (SYSCALL)0, 0 },
48091 #endif
48092
48093 #ifndef osUnlockFile
48094 #define osUnlockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
48095 DWORD))aSyscall[57].pCurrent)
48096 #endif
48097
48098 #if !SQLITE_OS_WINCE
@@ -48316,10 +48318,67 @@
48316 { "CancelIo", (SYSCALL)0, 0 },
48317 #endif
48318
48319 #define osCancelIo ((BOOL(WINAPI*)(HANDLE))aSyscall[81].pCurrent)
48320
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48321 }; /* End of the overrideable system calls */
48322
48323 /*
48324 ** This is the xSetSystemCall() method of sqlite3_vfs for all of the
48325 ** "win32" VFSes. Return SQLITE_OK upon successfully updating the
@@ -48489,10 +48548,11 @@
48489 sqlite3_mutex_leave(pMainMtx);
48490 return rc;
48491 }
48492 #endif /* SQLITE_WIN32_MALLOC */
48493
 
48494 /*
48495 ** This function outputs the specified (ANSI) string to the Win32 debugger
48496 ** (if available).
48497 */
48498
@@ -48531,10 +48591,11 @@
48531 }else{
48532 fprintf(stderr, "%s", zBuf);
48533 }
48534 #endif
48535 }
 
48536
48537 /*
48538 ** The following routine suspends the current thread for at least ms
48539 ** milliseconds. This is equivalent to the Win32 Sleep() interface.
48540 */
@@ -48831,10 +48892,11 @@
48831 SQLITE_PRIVATE void sqlite3MemSetDefault(void){
48832 sqlite3_config(SQLITE_CONFIG_MALLOC, sqlite3MemGetWin32());
48833 }
48834 #endif /* SQLITE_WIN32_MALLOC */
48835
 
48836 /*
48837 ** Convert a UTF-8 string to Microsoft Unicode.
48838 **
48839 ** Space to hold the returned string is obtained from sqlite3_malloc().
48840 */
@@ -48856,10 +48918,11 @@
48856 sqlite3_free(zWideText);
48857 zWideText = 0;
48858 }
48859 return zWideText;
48860 }
 
48861
48862 /*
48863 ** Convert a Microsoft Unicode string to UTF-8.
48864 **
48865 ** Space to hold the returned string is obtained from sqlite3_malloc().
@@ -48890,32 +48953,33 @@
48890 ** code page.
48891 **
48892 ** Space to hold the returned string is obtained from sqlite3_malloc().
48893 */
48894 static LPWSTR winMbcsToUnicode(const char *zText, int useAnsi){
48895 int nByte;
48896 LPWSTR zMbcsText;
48897 int codepage = useAnsi ? CP_ACP : CP_OEMCP;
48898
48899 nByte = osMultiByteToWideChar(codepage, 0, zText, -1, NULL,
48900 0)*sizeof(WCHAR);
48901 if( nByte==0 ){
48902 return 0;
48903 }
48904 zMbcsText = sqlite3MallocZero( nByte*sizeof(WCHAR) );
48905 if( zMbcsText==0 ){
48906 return 0;
48907 }
48908 nByte = osMultiByteToWideChar(codepage, 0, zText, -1, zMbcsText,
48909 nByte);
48910 if( nByte==0 ){
48911 sqlite3_free(zMbcsText);
48912 zMbcsText = 0;
48913 }
48914 return zMbcsText;
48915 }
48916
 
48917 /*
48918 ** Convert a Microsoft Unicode string to a multi-byte character string,
48919 ** using the ANSI or OEM code page.
48920 **
48921 ** Space to hold the returned string is obtained from sqlite3_malloc().
@@ -48939,10 +49003,11 @@
48939 sqlite3_free(zText);
48940 zText = 0;
48941 }
48942 return zText;
48943 }
 
48944
48945 /*
48946 ** Convert a multi-byte character string to UTF-8.
48947 **
48948 ** Space to hold the returned string is obtained from sqlite3_malloc().
@@ -48958,10 +49023,11 @@
48958 zTextUtf8 = winUnicodeToUtf8(zTmpWide);
48959 sqlite3_free(zTmpWide);
48960 return zTextUtf8;
48961 }
48962
 
48963 /*
48964 ** Convert a UTF-8 string to a multi-byte character string.
48965 **
48966 ** Space to hold the returned string is obtained from sqlite3_malloc().
48967 */
@@ -49007,10 +49073,11 @@
49007 #ifndef SQLITE_OMIT_AUTOINIT
49008 if( sqlite3_initialize() ) return 0;
49009 #endif
49010 return winUnicodeToUtf8(zWideText);
49011 }
 
49012
49013 /*
49014 ** This is a public wrapper for the winMbcsToUtf8() function.
49015 */
49016 SQLITE_API char *sqlite3_win32_mbcs_to_utf8(const char *zText){
@@ -49024,10 +49091,11 @@
49024 if( sqlite3_initialize() ) return 0;
49025 #endif
49026 return winMbcsToUtf8(zText, osAreFileApisANSI());
49027 }
49028
 
49029 /*
49030 ** This is a public wrapper for the winMbcsToUtf8() function.
49031 */
49032 SQLITE_API char *sqlite3_win32_mbcs_to_utf8_v2(const char *zText, int useAnsi){
49033 #ifdef SQLITE_ENABLE_API_ARMOR
@@ -49148,10 +49216,11 @@
49148 unsigned long type, /* Identifier for directory being set or reset */
49149 void *zValue /* New value for directory being set or reset */
49150 ){
49151 return sqlite3_win32_set_directory16(type, zValue);
49152 }
 
49153
49154 /*
49155 ** The return value of winGetLastErrorMsg
49156 ** is zero if the error message fits in the buffer, or non-zero
49157 ** otherwise (if the message was truncated).
@@ -49696,13 +49765,15 @@
49696 OVERLAPPED ovlp;
49697 memset(&ovlp, 0, sizeof(OVERLAPPED));
49698 ovlp.Offset = offsetLow;
49699 ovlp.OffsetHigh = offsetHigh;
49700 return osLockFileEx(*phFile, flags, 0, numBytesLow, numBytesHigh, &ovlp);
 
49701 }else{
49702 return osLockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
49703 numBytesHigh);
 
49704 }
49705 #endif
49706 }
49707
49708 /*
@@ -49806,13 +49877,15 @@
49806 OVERLAPPED ovlp;
49807 memset(&ovlp, 0, sizeof(OVERLAPPED));
49808 ovlp.Offset = offsetLow;
49809 ovlp.OffsetHigh = offsetHigh;
49810 return osUnlockFileEx(*phFile, 0, numBytesLow, numBytesHigh, &ovlp);
 
49811 }else{
49812 return osUnlockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
49813 numBytesHigh);
 
49814 }
49815 #endif
49816 }
49817
49818 /*
@@ -51222,18 +51295,95 @@
51222
51223 /*
51224 ** Convert a UTF-8 filename into whatever form the underlying
51225 ** operating system wants filenames in. Space to hold the result
51226 ** is obtained from malloc and must be freed by the calling
51227 ** function.
 
 
 
 
 
 
 
 
 
 
 
 
 
51228 */
51229 static void *winConvertFromUtf8Filename(const char *zFilename){
51230 void *zConverted = 0;
51231 if( osIsNT() ){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51232 zConverted = winUtf8ToUnicode(zFilename);
 
51233 }
51234 #ifdef SQLITE_WIN32_HAS_ANSI
51235 else{
51236 zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI());
51237 }
51238 #endif
51239 /* caller will handle out of memory */
@@ -52058,11 +52208,11 @@
52058 **
52059 ** This division contains the implementation of methods on the
52060 ** sqlite3_vfs object.
52061 */
52062
52063 #if defined(__CYGWIN__)
52064 /*
52065 ** Convert a filename from whatever the underlying operating system
52066 ** supports for filenames into UTF-8. Space to hold the result is
52067 ** obtained from malloc and must be freed by the calling function.
52068 */
@@ -52091,11 +52241,18 @@
52091 int nLen = sqlite3Strlen30(zBuf);
52092 if( nLen>0 ){
52093 if( winIsDirSep(zBuf[nLen-1]) ){
52094 return 1;
52095 }else if( nLen+1<nBuf ){
52096 zBuf[nLen] = winGetDirSep();
 
 
 
 
 
 
 
52097 zBuf[nLen+1] = '\0';
52098 return 1;
52099 }
52100 }
52101 }
@@ -52118,11 +52275,11 @@
52118 /*
52119 ** Create a temporary file name and store the resulting pointer into pzBuf.
52120 ** The pointer returned in pzBuf must be freed via sqlite3_free().
52121 */
52122 static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
52123 static char zChars[] =
52124 "abcdefghijklmnopqrstuvwxyz"
52125 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
52126 "0123456789";
52127 size_t i, j;
52128 DWORD pid;
@@ -52169,11 +52326,11 @@
52169 }
52170 sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
52171 }
52172
52173 #if defined(__CYGWIN__)
52174 else{
52175 static const char *azDirs[] = {
52176 0, /* getenv("SQLITE_TMPDIR") */
52177 0, /* getenv("TMPDIR") */
52178 0, /* getenv("TMP") */
52179 0, /* getenv("TEMP") */
@@ -52185,24 +52342,24 @@
52185 0 /* List terminator */
52186 };
52187 unsigned int i;
52188 const char *zDir = 0;
52189
52190 if( !azDirs[0] ) azDirs[0] = getenv("SQLITE_TMPDIR");
52191 if( !azDirs[1] ) azDirs[1] = getenv("TMPDIR");
52192 if( !azDirs[2] ) azDirs[2] = getenv("TMP");
52193 if( !azDirs[3] ) azDirs[3] = getenv("TEMP");
52194 if( !azDirs[4] ) azDirs[4] = getenv("USERPROFILE");
52195 for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){
52196 void *zConverted;
52197 if( zDir==0 ) continue;
52198 /* If the path starts with a drive letter followed by the colon
52199 ** character, assume it is already a native Win32 path; otherwise,
52200 ** it must be converted to a native Win32 path via the Cygwin API
52201 ** prior to using it.
52202 */
52203 if( winIsDriveLetterAndColon(zDir) ){
52204 zConverted = winConvertFromUtf8Filename(zDir);
52205 if( !zConverted ){
52206 sqlite3_free(zBuf);
52207 OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
52208 return SQLITE_IOERR_NOMEM_BKPT;
@@ -52211,19 +52368,20 @@
52211 sqlite3_snprintf(nMax, zBuf, "%s", zDir);
52212 sqlite3_free(zConverted);
52213 break;
52214 }
52215 sqlite3_free(zConverted);
 
52216 }else{
52217 zConverted = sqlite3MallocZero( nMax+1 );
52218 if( !zConverted ){
52219 sqlite3_free(zBuf);
52220 OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
52221 return SQLITE_IOERR_NOMEM_BKPT;
52222 }
52223 if( cygwin_conv_path(
52224 osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A, zDir,
52225 zConverted, nMax+1)<0 ){
52226 sqlite3_free(zConverted);
52227 sqlite3_free(zBuf);
52228 OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_CONVPATH\n"));
52229 return winLogError(SQLITE_IOERR_CONVPATH, (DWORD)errno,
@@ -52245,14 +52403,17 @@
52245 sqlite3_free(zUtf8);
52246 sqlite3_free(zConverted);
52247 break;
52248 }
52249 sqlite3_free(zConverted);
 
52250 }
52251 }
52252 }
52253 #elif !SQLITE_OS_WINRT && !defined(__CYGWIN__)
 
 
52254 else if( osIsNT() ){
52255 char *zMulti;
52256 LPWSTR zWidePath = sqlite3MallocZero( nMax*sizeof(WCHAR) );
52257 if( !zWidePath ){
52258 sqlite3_free(zBuf);
@@ -52372,11 +52533,11 @@
52372 &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){}
52373 if( !rc ){
52374 return 0; /* Invalid name? */
52375 }
52376 attr = sAttrData.dwFileAttributes;
52377 #if SQLITE_OS_WINCE==0
52378 }else{
52379 attr = osGetFileAttributesA((char*)zConverted);
52380 #endif
52381 }
52382 return (attr!=INVALID_FILE_ATTRIBUTES) && (attr&FILE_ATTRIBUTE_DIRECTORY);
@@ -52388,10 +52549,16 @@
52388 const char *zFilename, /* Name of file to check */
52389 int flags, /* Type of test to make on this file */
52390 int *pResOut /* OUT: Result */
52391 );
52392
 
 
 
 
 
 
52393 /*
52394 ** Open a file.
52395 */
52396 static int winOpen(
52397 sqlite3_vfs *pVfs, /* Used to get maximum path length and AppData */
@@ -52412,10 +52579,11 @@
52412 winVfsAppData *pAppData;
52413 winFile *pFile = (winFile*)id;
52414 void *zConverted; /* Filename in OS encoding */
52415 const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
52416 int cnt = 0;
 
52417
52418 /* If argument zPath is a NULL pointer, this function is required to open
52419 ** a temporary file. Use this buffer to store the file name in.
52420 */
52421 char *zTmpname = 0; /* For temporary filename, if necessary. */
@@ -52576,13 +52744,13 @@
52576 dwShareMode,
52577 dwCreationDisposition,
52578 &extendedParameters);
52579 if( h!=INVALID_HANDLE_VALUE ) break;
52580 if( isReadWrite ){
52581 int rc2, isRO = 0;
52582 sqlite3BeginBenignMalloc();
52583 rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO);
52584 sqlite3EndBenignMalloc();
52585 if( rc2==SQLITE_OK && isRO ) break;
52586 }
52587 }while( winRetryIoerr(&cnt, &lastErrno) );
52588 #else
@@ -52593,13 +52761,13 @@
52593 dwCreationDisposition,
52594 dwFlagsAndAttributes,
52595 NULL);
52596 if( h!=INVALID_HANDLE_VALUE ) break;
52597 if( isReadWrite ){
52598 int rc2, isRO = 0;
52599 sqlite3BeginBenignMalloc();
52600 rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO);
52601 sqlite3EndBenignMalloc();
52602 if( rc2==SQLITE_OK && isRO ) break;
52603 }
52604 }while( winRetryIoerr(&cnt, &lastErrno) );
52605 #endif
@@ -52613,13 +52781,13 @@
52613 dwCreationDisposition,
52614 dwFlagsAndAttributes,
52615 NULL);
52616 if( h!=INVALID_HANDLE_VALUE ) break;
52617 if( isReadWrite ){
52618 int rc2, isRO = 0;
52619 sqlite3BeginBenignMalloc();
52620 rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ, &isRO);
52621 sqlite3EndBenignMalloc();
52622 if( rc2==SQLITE_OK && isRO ) break;
52623 }
52624 }while( winRetryIoerr(&cnt, &lastErrno) );
52625 }
@@ -52630,11 +52798,11 @@
52630 dwDesiredAccess, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok"));
52631
52632 if( h==INVALID_HANDLE_VALUE ){
52633 sqlite3_free(zConverted);
52634 sqlite3_free(zTmpname);
52635 if( isReadWrite && !isExclusive ){
52636 return winOpen(pVfs, zName, id,
52637 ((flags|SQLITE_OPEN_READONLY) &
52638 ~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)),
52639 pOutFlags);
52640 }else{
@@ -52832,11 +53000,17 @@
52832 ){
52833 DWORD attr;
52834 int rc = 0;
52835 DWORD lastErrno = 0;
52836 void *zConverted;
 
52837 UNUSED_PARAMETER(pVfs);
 
 
 
 
 
52838
52839 SimulateIOError( return SQLITE_IOERR_ACCESS; );
52840 OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n",
52841 zFilename, flags, pResOut));
52842
@@ -52856,11 +53030,14 @@
52856 int cnt = 0;
52857 WIN32_FILE_ATTRIBUTE_DATA sAttrData;
52858 memset(&sAttrData, 0, sizeof(sAttrData));
52859 while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted,
52860 GetFileExInfoStandard,
52861 &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){}
 
 
 
52862 if( rc ){
52863 /* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file
52864 ** as if it does not exist.
52865 */
52866 if( flags==SQLITE_ACCESS_EXISTS
@@ -52924,10 +53101,11 @@
52924 const char *zPathname
52925 ){
52926 return ( sqlite3Isalpha(zPathname[0]) && zPathname[1]==':' );
52927 }
52928
 
52929 /*
52930 ** Returns non-zero if the specified path name should be used verbatim. If
52931 ** non-zero is returned from this function, the calling function must simply
52932 ** use the provided path name verbatim -OR- resolve it into a full path name
52933 ** using the GetFullPathName Win32 API function (if available).
@@ -52960,10 +53138,74 @@
52960 ** If we get to this point, the path name should almost certainly be a purely
52961 ** relative one (i.e. not a UNC name, not absolute, and not volume relative).
52962 */
52963 return FALSE;
52964 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52965
52966 /*
52967 ** Turn a relative pathname into a full pathname. Write the full
52968 ** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname
52969 ** bytes in size.
@@ -52972,12 +53214,12 @@
52972 sqlite3_vfs *pVfs, /* Pointer to vfs object */
52973 const char *zRelative, /* Possibly relative input path */
52974 int nFull, /* Size of output buffer in bytes */
52975 char *zFull /* Output buffer */
52976 ){
52977 #if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__)
52978 DWORD nByte;
52979 void *zConverted;
52980 char *zOut;
52981 #endif
52982
52983 /* If this path name begins with "/X:" or "\\?\", where "X" is any
@@ -52986,68 +53228,114 @@
52986 if( zRelative[0]=='/' && (winIsDriveLetterAndColon(zRelative+1)
52987 || winIsLongPathPrefix(zRelative+1)) ){
52988 zRelative++;
52989 }
52990
52991 #if defined(__CYGWIN__)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52992 SimulateIOError( return SQLITE_ERROR );
52993 UNUSED_PARAMETER(nFull);
52994 assert( nFull>=pVfs->mxPathname );
52995 if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
52996 /*
52997 ** NOTE: We are dealing with a relative path name and the data
52998 ** directory has been set. Therefore, use it as the basis
52999 ** for converting the relative path name to an absolute
53000 ** one by prepending the data directory and a slash.
53001 */
53002 char *zOut = sqlite3MallocZero( 1+(u64)pVfs->mxPathname );
53003 if( !zOut ){
53004 return SQLITE_IOERR_NOMEM_BKPT;
53005 }
53006 if( cygwin_conv_path(
53007 (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A) |
53008 CCP_RELATIVE, zRelative, zOut, pVfs->mxPathname+1)<0 ){
53009 sqlite3_free(zOut);
53010 return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno,
53011 "winFullPathname1", zRelative);
53012 }else{
53013 char *zUtf8 = winConvertToUtf8Filename(zOut);
53014 if( !zUtf8 ){
53015 sqlite3_free(zOut);
53016 return SQLITE_IOERR_NOMEM_BKPT;
53017 }
53018 sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s",
53019 sqlite3_data_directory, winGetDirSep(), zUtf8);
53020 sqlite3_free(zUtf8);
53021 sqlite3_free(zOut);
53022 }
53023 }else{
53024 char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 );
53025 if( !zOut ){
53026 return SQLITE_IOERR_NOMEM_BKPT;
53027 }
53028 if( cygwin_conv_path(
53029 (osIsNT() ? CCP_POSIX_TO_WIN_W : CCP_POSIX_TO_WIN_A),
53030 zRelative, zOut, pVfs->mxPathname+1)<0 ){
53031 sqlite3_free(zOut);
53032 return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno,
53033 "winFullPathname2", zRelative);
53034 }else{
53035 char *zUtf8 = winConvertToUtf8Filename(zOut);
53036 if( !zUtf8 ){
53037 sqlite3_free(zOut);
53038 return SQLITE_IOERR_NOMEM_BKPT;
53039 }
53040 sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zUtf8);
53041 sqlite3_free(zUtf8);
53042 sqlite3_free(zOut);
53043 }
53044 }
53045 return SQLITE_OK;
53046 #endif
53047
53048 #if (SQLITE_OS_WINCE || SQLITE_OS_WINRT) && !defined(__CYGWIN__)
53049 SimulateIOError( return SQLITE_ERROR );
53050 /* WinCE has no concept of a relative pathname, or so I am told. */
53051 /* WinRT has no way to convert a relative path to an absolute one. */
53052 if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
53053 /*
@@ -53062,11 +53350,12 @@
53062 sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zRelative);
53063 }
53064 return SQLITE_OK;
53065 #endif
53066
53067 #if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT && !defined(__CYGWIN__)
 
53068 /* It's odd to simulate an io-error here, but really this is just
53069 ** using the io-error infrastructure to test that SQLite handles this
53070 ** function failing. This function could fail if, for example, the
53071 ** current working directory has been unlinked.
53072 */
@@ -53080,10 +53369,11 @@
53080 */
53081 sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s",
53082 sqlite3_data_directory, winGetDirSep(), zRelative);
53083 return SQLITE_OK;
53084 }
 
53085 zConverted = winConvertFromUtf8Filename(zRelative);
53086 if( zConverted==0 ){
53087 return SQLITE_IOERR_NOMEM_BKPT;
53088 }
53089 if( osIsNT() ){
@@ -53092,16 +53382,17 @@
53092 if( nByte==0 ){
53093 sqlite3_free(zConverted);
53094 return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
53095 "winFullPathname1", zRelative);
53096 }
53097 zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) + 3*sizeof(zTemp[0]) );
 
53098 if( zTemp==0 ){
53099 sqlite3_free(zConverted);
53100 return SQLITE_IOERR_NOMEM_BKPT;
53101 }
53102 nByte = osGetFullPathNameW((LPCWSTR)zConverted, nByte+3, zTemp, 0);
53103 if( nByte==0 ){
53104 sqlite3_free(zConverted);
53105 sqlite3_free(zTemp);
53106 return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
53107 "winFullPathname2", zRelative);
@@ -53135,11 +53426,30 @@
53135 zOut = winMbcsToUtf8(zTemp, osAreFileApisANSI());
53136 sqlite3_free(zTemp);
53137 }
53138 #endif
53139 if( zOut ){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53140 sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut);
 
53141 sqlite3_free(zOut);
53142 return SQLITE_OK;
53143 }else{
53144 return SQLITE_IOERR_NOMEM_BKPT;
53145 }
@@ -53165,11 +53475,13 @@
53165 ** Interfaces for opening a shared library, finding entry points
53166 ** within the shared library, and closing the shared library.
53167 */
53168 static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){
53169 HANDLE h;
53170 #if defined(__CYGWIN__)
 
 
53171 int nFull = pVfs->mxPathname+1;
53172 char *zFull = sqlite3MallocZero( nFull );
53173 void *zConverted = 0;
53174 if( zFull==0 ){
53175 OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0));
@@ -53532,11 +53844,11 @@
53532 };
53533 #endif
53534
53535 /* Double-check that the aSyscall[] array has been constructed
53536 ** correctly. See ticket [bb3a86e890c8e96ab] */
53537 assert( ArraySize(aSyscall)==82 );
53538
53539 /* get memory map allocation granularity */
53540 memset(&winSysInfo, 0, sizeof(SYSTEM_INFO));
53541 #if SQLITE_OS_WINRT
53542 osGetNativeSystemInfo(&winSysInfo);
@@ -66508,14 +66820,12 @@
66508 s2 = aIn[1];
66509 }else{
66510 s1 = s2 = 0;
66511 }
66512
66513 assert( nByte>=8 );
66514 assert( (nByte&0x00000007)==0 );
66515 assert( nByte<=65536 );
66516 assert( nByte%4==0 );
66517
66518 if( !nativeCksum ){
66519 do {
66520 s1 += BYTESWAP32(aData[0]) + s2;
66521 s2 += BYTESWAP32(aData[1]) + s1;
@@ -147785,10 +148095,11 @@
147785 }
147786
147787 multi_select_end:
147788 pDest->iSdst = dest.iSdst;
147789 pDest->nSdst = dest.nSdst;
 
147790 if( pDelete ){
147791 sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pDelete);
147792 }
147793 return rc;
147794 }
@@ -188065,10 +188376,17 @@
188065 ******************************************************************************
188066 **
188067 */
188068 #ifndef _FTSINT_H
188069 #define _FTSINT_H
 
 
 
 
 
 
 
188070
188071 #if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
188072 # define NDEBUG 1
188073 #endif
188074
@@ -189017,16 +189335,10 @@
189017
189018 #if defined(SQLITE_ENABLE_FTS3) && !defined(SQLITE_CORE)
189019 # define SQLITE_CORE 1
189020 #endif
189021
189022 /* #include <assert.h> */
189023 /* #include <stdlib.h> */
189024 /* #include <stddef.h> */
189025 /* #include <stdio.h> */
189026 /* #include <string.h> */
189027 /* #include <stdarg.h> */
189028
189029 /* #include "fts3.h" */
189030 #ifndef SQLITE_CORE
189031 /* # include "sqlite3ext.h" */
189032 SQLITE_EXTENSION_INIT1
@@ -227533,12 +227845,12 @@
227533 ){
227534 if( sqlite3_value_type(argv[3])==SQLITE_NULL && isInsert && pgno>1 ){
227535 /* "INSERT INTO dbpage($PGNO,NULL)" causes page number $PGNO and
227536 ** all subsequent pages to be deleted. */
227537 pTab->iDbTrunc = iDb;
227538 pgno--;
227539 pTab->pgnoTrunc = pgno;
227540 }else{
227541 zErr = "bad page value";
227542 goto update_fail;
227543 }
227544 }
@@ -241869,11 +242181,12 @@
241869 sqlite3Fts5ParseError(
241870 pParse, "expected integer, got \"%.*s\"", p->n, p->p
241871 );
241872 return;
241873 }
241874 nNear = nNear * 10 + (p->p[i] - '0');
 
241875 }
241876 }else{
241877 nNear = FTS5_DEFAULT_NEARDIST;
241878 }
241879 pNear->nNear = nNear;
@@ -256775,11 +257088,11 @@
256775 int nArg, /* Number of args */
256776 sqlite3_value **apUnused /* Function arguments */
256777 ){
256778 assert( nArg==0 );
256779 UNUSED_PARAM2(nArg, apUnused);
256780 sqlite3_result_text(pCtx, "fts5: 2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185", -1, SQLITE_TRANSIENT);
256781 }
256782
256783 /*
256784 ** Implementation of fts5_locale(LOCALE, TEXT) function.
256785 **
256786
--- extsrc/sqlite3.c
+++ extsrc/sqlite3.c
@@ -16,11 +16,11 @@
16 ** if you want a wrapper to interface SQLite with your choice of programming
17 ** language. The code for the "sqlite3" command-line shell is also in a
18 ** separate file. This file contains only code for the core SQLite library.
19 **
20 ** The content in this amalgamation comes from Fossil check-in
21 ** 121f4d97f9a855131859d342bc2ade5f8c34 with changes in files:
22 **
23 **
24 */
25 #ifndef SQLITE_AMALGAMATION
26 #define SQLITE_CORE 1
@@ -465,11 +465,11 @@
465 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
466 ** [sqlite_version()] and [sqlite_source_id()].
467 */
468 #define SQLITE_VERSION "3.50.0"
469 #define SQLITE_VERSION_NUMBER 3050000
470 #define SQLITE_SOURCE_ID "2025-03-27 23:29:25 121f4d97f9a855131859d342bc2ade5f8c34ba7732029ae156d02cec7cb6dd85"
471
472 /*
473 ** CAPI3REF: Run-Time Library Version Numbers
474 ** KEYWORDS: sqlite3_version sqlite3_sourceid
475 **
@@ -30270,10 +30270,12 @@
30270 */
30271 #include "windows.h"
30272
30273 #ifdef __CYGWIN__
30274 # include <sys/cygwin.h>
30275 # include <sys/stat.h> /* amalgamator: dontcache */
30276 # include <unistd.h> /* amalgamator: dontcache */
30277 # include <errno.h> /* amalgamator: dontcache */
30278 #endif
30279
30280 /*
30281 ** Determine if we are dealing with Windows NT.
@@ -47726,20 +47728,20 @@
47728 { "FileTimeToLocalFileTime", (SYSCALL)FileTimeToLocalFileTime, 0 },
47729 #else
47730 { "FileTimeToLocalFileTime", (SYSCALL)0, 0 },
47731 #endif
47732
47733 #define osFileTimeToLocalFileTime ((BOOL(WINAPI*)(const FILETIME*, \
47734 LPFILETIME))aSyscall[11].pCurrent)
47735
47736 #if SQLITE_OS_WINCE
47737 { "FileTimeToSystemTime", (SYSCALL)FileTimeToSystemTime, 0 },
47738 #else
47739 { "FileTimeToSystemTime", (SYSCALL)0, 0 },
47740 #endif
47741
47742 #define osFileTimeToSystemTime ((BOOL(WINAPI*)(const FILETIME*, \
47743 LPSYSTEMTIME))aSyscall[12].pCurrent)
47744
47745 { "FlushFileBuffers", (SYSCALL)FlushFileBuffers, 0 },
47746
47747 #define osFlushFileBuffers ((BOOL(WINAPI*)(HANDLE))aSyscall[13].pCurrent)
@@ -48015,11 +48017,11 @@
48017 { "LockFile", (SYSCALL)LockFile, 0 },
48018 #else
48019 { "LockFile", (SYSCALL)0, 0 },
48020 #endif
48021
48022 #if !defined(osLockFile) && defined(SQLITE_WIN32_HAS_ANSI)
48023 #define osLockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
48024 DWORD))aSyscall[47].pCurrent)
48025 #endif
48026
48027 #if !SQLITE_OS_WINCE
@@ -48079,20 +48081,20 @@
48081
48082 #define osSleep ((VOID(WINAPI*)(DWORD))aSyscall[55].pCurrent)
48083
48084 { "SystemTimeToFileTime", (SYSCALL)SystemTimeToFileTime, 0 },
48085
48086 #define osSystemTimeToFileTime ((BOOL(WINAPI*)(const SYSTEMTIME*, \
48087 LPFILETIME))aSyscall[56].pCurrent)
48088
48089 #if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
48090 { "UnlockFile", (SYSCALL)UnlockFile, 0 },
48091 #else
48092 { "UnlockFile", (SYSCALL)0, 0 },
48093 #endif
48094
48095 #if !defined(osUnlockFile) && defined(SQLITE_WIN32_HAS_ANSI)
48096 #define osUnlockFile ((BOOL(WINAPI*)(HANDLE,DWORD,DWORD,DWORD, \
48097 DWORD))aSyscall[57].pCurrent)
48098 #endif
48099
48100 #if !SQLITE_OS_WINCE
@@ -48316,10 +48318,67 @@
48318 { "CancelIo", (SYSCALL)0, 0 },
48319 #endif
48320
48321 #define osCancelIo ((BOOL(WINAPI*)(HANDLE))aSyscall[81].pCurrent)
48322
48323 #if defined(SQLITE_WIN32_HAS_WIDE) && defined(_WIN32)
48324 { "GetModuleHandleW", (SYSCALL)GetModuleHandleW, 0 },
48325 #else
48326 { "GetModuleHandleW", (SYSCALL)0, 0 },
48327 #endif
48328
48329 #define osGetModuleHandleW ((HMODULE(WINAPI*)(LPCWSTR))aSyscall[82].pCurrent)
48330
48331 #ifndef _WIN32
48332 { "getenv", (SYSCALL)getenv, 0 },
48333 #else
48334 { "getenv", (SYSCALL)0, 0 },
48335 #endif
48336
48337 #define osGetenv ((const char *(*)(const char *))aSyscall[83].pCurrent)
48338
48339 #ifndef _WIN32
48340 { "getcwd", (SYSCALL)getcwd, 0 },
48341 #else
48342 { "getcwd", (SYSCALL)0, 0 },
48343 #endif
48344
48345 #define osGetcwd ((char*(*)(char*,size_t))aSyscall[84].pCurrent)
48346
48347 #ifndef _WIN32
48348 { "readlink", (SYSCALL)readlink, 0 },
48349 #else
48350 { "readlink", (SYSCALL)0, 0 },
48351 #endif
48352
48353 #define osReadlink ((ssize_t(*)(const char*,char*,size_t))aSyscall[85].pCurrent)
48354
48355 #ifndef _WIN32
48356 { "lstat", (SYSCALL)lstat, 0 },
48357 #else
48358 { "lstat", (SYSCALL)0, 0 },
48359 #endif
48360
48361 #define osLstat ((int(*)(const char*,struct stat*))aSyscall[86].pCurrent)
48362
48363 #ifndef _WIN32
48364 { "__errno", (SYSCALL)__errno, 0 },
48365 #else
48366 { "__errno", (SYSCALL)0, 0 },
48367 #endif
48368
48369 #define osErrno (*((int*(*)(void))aSyscall[87].pCurrent)())
48370
48371 #ifndef _WIN32
48372 { "cygwin_conv_path", (SYSCALL)cygwin_conv_path, 0 },
48373 #else
48374 { "cygwin_conv_path", (SYSCALL)0, 0 },
48375 #endif
48376
48377 #define osCygwin_conv_path ((size_t(*)(unsigned int, \
48378 const void *, void *, size_t))aSyscall[88].pCurrent)
48379
48380 }; /* End of the overrideable system calls */
48381
48382 /*
48383 ** This is the xSetSystemCall() method of sqlite3_vfs for all of the
48384 ** "win32" VFSes. Return SQLITE_OK upon successfully updating the
@@ -48489,10 +48548,11 @@
48548 sqlite3_mutex_leave(pMainMtx);
48549 return rc;
48550 }
48551 #endif /* SQLITE_WIN32_MALLOC */
48552
48553 #ifdef _WIN32
48554 /*
48555 ** This function outputs the specified (ANSI) string to the Win32 debugger
48556 ** (if available).
48557 */
48558
@@ -48531,10 +48591,11 @@
48591 }else{
48592 fprintf(stderr, "%s", zBuf);
48593 }
48594 #endif
48595 }
48596 #endif /* _WIN32 */
48597
48598 /*
48599 ** The following routine suspends the current thread for at least ms
48600 ** milliseconds. This is equivalent to the Win32 Sleep() interface.
48601 */
@@ -48831,10 +48892,11 @@
48892 SQLITE_PRIVATE void sqlite3MemSetDefault(void){
48893 sqlite3_config(SQLITE_CONFIG_MALLOC, sqlite3MemGetWin32());
48894 }
48895 #endif /* SQLITE_WIN32_MALLOC */
48896
48897 #ifdef _WIN32
48898 /*
48899 ** Convert a UTF-8 string to Microsoft Unicode.
48900 **
48901 ** Space to hold the returned string is obtained from sqlite3_malloc().
48902 */
@@ -48856,10 +48918,11 @@
48918 sqlite3_free(zWideText);
48919 zWideText = 0;
48920 }
48921 return zWideText;
48922 }
48923 #endif /* _WIN32 */
48924
48925 /*
48926 ** Convert a Microsoft Unicode string to UTF-8.
48927 **
48928 ** Space to hold the returned string is obtained from sqlite3_malloc().
@@ -48890,32 +48953,33 @@
48953 ** code page.
48954 **
48955 ** Space to hold the returned string is obtained from sqlite3_malloc().
48956 */
48957 static LPWSTR winMbcsToUnicode(const char *zText, int useAnsi){
48958 int nWideChar;
48959 LPWSTR zMbcsText;
48960 int codepage = useAnsi ? CP_ACP : CP_OEMCP;
48961
48962 nWideChar = osMultiByteToWideChar(codepage, 0, zText, -1, NULL,
48963 0);
48964 if( nWideChar==0 ){
48965 return 0;
48966 }
48967 zMbcsText = sqlite3MallocZero( nWideChar*sizeof(WCHAR) );
48968 if( zMbcsText==0 ){
48969 return 0;
48970 }
48971 nWideChar = osMultiByteToWideChar(codepage, 0, zText, -1, zMbcsText,
48972 nWideChar);
48973 if( nWideChar==0 ){
48974 sqlite3_free(zMbcsText);
48975 zMbcsText = 0;
48976 }
48977 return zMbcsText;
48978 }
48979
48980 #ifdef _WIN32
48981 /*
48982 ** Convert a Microsoft Unicode string to a multi-byte character string,
48983 ** using the ANSI or OEM code page.
48984 **
48985 ** Space to hold the returned string is obtained from sqlite3_malloc().
@@ -48939,10 +49003,11 @@
49003 sqlite3_free(zText);
49004 zText = 0;
49005 }
49006 return zText;
49007 }
49008 #endif /* _WIN32 */
49009
49010 /*
49011 ** Convert a multi-byte character string to UTF-8.
49012 **
49013 ** Space to hold the returned string is obtained from sqlite3_malloc().
@@ -48958,10 +49023,11 @@
49023 zTextUtf8 = winUnicodeToUtf8(zTmpWide);
49024 sqlite3_free(zTmpWide);
49025 return zTextUtf8;
49026 }
49027
49028 #ifdef _WIN32
49029 /*
49030 ** Convert a UTF-8 string to a multi-byte character string.
49031 **
49032 ** Space to hold the returned string is obtained from sqlite3_malloc().
49033 */
@@ -49007,10 +49073,11 @@
49073 #ifndef SQLITE_OMIT_AUTOINIT
49074 if( sqlite3_initialize() ) return 0;
49075 #endif
49076 return winUnicodeToUtf8(zWideText);
49077 }
49078 #endif /* _WIN32 */
49079
49080 /*
49081 ** This is a public wrapper for the winMbcsToUtf8() function.
49082 */
49083 SQLITE_API char *sqlite3_win32_mbcs_to_utf8(const char *zText){
@@ -49024,10 +49091,11 @@
49091 if( sqlite3_initialize() ) return 0;
49092 #endif
49093 return winMbcsToUtf8(zText, osAreFileApisANSI());
49094 }
49095
49096 #ifdef _WIN32
49097 /*
49098 ** This is a public wrapper for the winMbcsToUtf8() function.
49099 */
49100 SQLITE_API char *sqlite3_win32_mbcs_to_utf8_v2(const char *zText, int useAnsi){
49101 #ifdef SQLITE_ENABLE_API_ARMOR
@@ -49148,10 +49216,11 @@
49216 unsigned long type, /* Identifier for directory being set or reset */
49217 void *zValue /* New value for directory being set or reset */
49218 ){
49219 return sqlite3_win32_set_directory16(type, zValue);
49220 }
49221 #endif /* _WIN32 */
49222
49223 /*
49224 ** The return value of winGetLastErrorMsg
49225 ** is zero if the error message fits in the buffer, or non-zero
49226 ** otherwise (if the message was truncated).
@@ -49696,13 +49765,15 @@
49765 OVERLAPPED ovlp;
49766 memset(&ovlp, 0, sizeof(OVERLAPPED));
49767 ovlp.Offset = offsetLow;
49768 ovlp.OffsetHigh = offsetHigh;
49769 return osLockFileEx(*phFile, flags, 0, numBytesLow, numBytesHigh, &ovlp);
49770 #ifdef SQLITE_WIN32_HAS_ANSI
49771 }else{
49772 return osLockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
49773 numBytesHigh);
49774 #endif
49775 }
49776 #endif
49777 }
49778
49779 /*
@@ -49806,13 +49877,15 @@
49877 OVERLAPPED ovlp;
49878 memset(&ovlp, 0, sizeof(OVERLAPPED));
49879 ovlp.Offset = offsetLow;
49880 ovlp.OffsetHigh = offsetHigh;
49881 return osUnlockFileEx(*phFile, 0, numBytesLow, numBytesHigh, &ovlp);
49882 #ifdef SQLITE_WIN32_HAS_ANSI
49883 }else{
49884 return osUnlockFile(*phFile, offsetLow, offsetHigh, numBytesLow,
49885 numBytesHigh);
49886 #endif
49887 }
49888 #endif
49889 }
49890
49891 /*
@@ -51222,18 +51295,95 @@
51295
51296 /*
51297 ** Convert a UTF-8 filename into whatever form the underlying
51298 ** operating system wants filenames in. Space to hold the result
51299 ** is obtained from malloc and must be freed by the calling
51300 ** function
51301 **
51302 ** On Cygwin, 3 possible input forms are accepted:
51303 ** - If the filename starts with "<drive>:/" or "<drive>:\",
51304 ** it is converted to UTF-16 as-is.
51305 ** - If the filename contains '/', it is assumed to be a
51306 ** Cygwin absolute path, it is converted to a win32
51307 ** absolute path in UTF-16.
51308 ** - Otherwise it must be a filename only, the win32 filename
51309 ** is returned in UTF-16.
51310 ** Note: If the function cygwin_conv_path() fails, only
51311 ** UTF-8 -> UTF-16 conversion will be done. This can only
51312 ** happen when the file path >32k, in which case winUtf8ToUnicode()
51313 ** will fail too.
51314 */
51315 static void *winConvertFromUtf8Filename(const char *zFilename){
51316 void *zConverted = 0;
51317 if( osIsNT() ){
51318 #ifdef __CYGWIN__
51319 int nChar;
51320 LPWSTR zWideFilename;
51321
51322 if( osCygwin_conv_path && !(winIsDriveLetterAndColon(zFilename)
51323 && winIsDirSep(zFilename[2])) ){
51324 int nByte;
51325 int convertflag = CCP_POSIX_TO_WIN_W;
51326 if( !strchr(zFilename, '/') ) convertflag |= CCP_RELATIVE;
51327 nByte = (int)osCygwin_conv_path(convertflag,
51328 zFilename, 0, 0);
51329 if( nByte>0 ){
51330 zConverted = sqlite3MallocZero(nByte+12);
51331 if ( zConverted==0 ){
51332 return zConverted;
51333 }
51334 zWideFilename = zConverted;
51335 /* Filenames should be prefixed, except when converted
51336 * full path already starts with "\\?\". */
51337 if( osCygwin_conv_path(convertflag, zFilename,
51338 zWideFilename+4, nByte)==0 ){
51339 if( (convertflag&CCP_RELATIVE) ){
51340 memmove(zWideFilename, zWideFilename+4, nByte);
51341 }else if( memcmp(zWideFilename+4, L"\\\\", 4) ){
51342 memcpy(zWideFilename, L"\\\\?\\", 8);
51343 }else if( zWideFilename[6]!='?' ){
51344 memmove(zWideFilename+6, zWideFilename+4, nByte);
51345 memcpy(zWideFilename, L"\\\\?\\UNC", 14);
51346 }else{
51347 memmove(zWideFilename, zWideFilename+4, nByte);
51348 }
51349 return zConverted;
51350 }
51351 sqlite3_free(zConverted);
51352 }
51353 }
51354 nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1, NULL, 0);
51355 if( nChar==0 ){
51356 return 0;
51357 }
51358 zWideFilename = sqlite3MallocZero( nChar*sizeof(WCHAR)+12 );
51359 if( zWideFilename==0 ){
51360 return 0;
51361 }
51362 nChar = osMultiByteToWideChar(CP_UTF8, 0, zFilename, -1,
51363 zWideFilename, nChar);
51364 if( nChar==0 ){
51365 sqlite3_free(zWideFilename);
51366 zWideFilename = 0;
51367 }else if( nChar>MAX_PATH
51368 && winIsDriveLetterAndColon(zFilename)
51369 && winIsDirSep(zFilename[2]) ){
51370 memmove(zWideFilename+4, zWideFilename, nChar*sizeof(WCHAR));
51371 zWideFilename[2] = '\\';
51372 memcpy(zWideFilename, L"\\\\?\\", 8);
51373 }else if( nChar>MAX_PATH
51374 && winIsDirSep(zFilename[0]) && winIsDirSep(zFilename[1])
51375 && zFilename[2] != '?' ){
51376 memmove(zWideFilename+6, zWideFilename, nChar*sizeof(WCHAR));
51377 memcpy(zWideFilename, L"\\\\?\\UNC", 14);
51378 }
51379 zConverted = zWideFilename;
51380 #else
51381 zConverted = winUtf8ToUnicode(zFilename);
51382 #endif /* __CYGWIN__ */
51383 }
51384 #if defined(SQLITE_WIN32_HAS_ANSI) && defined(_WIN32)
51385 else{
51386 zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI());
51387 }
51388 #endif
51389 /* caller will handle out of memory */
@@ -52058,11 +52208,11 @@
52208 **
52209 ** This division contains the implementation of methods on the
52210 ** sqlite3_vfs object.
52211 */
52212
52213 #if 0 /* No longer necessary */
52214 /*
52215 ** Convert a filename from whatever the underlying operating system
52216 ** supports for filenames into UTF-8. Space to hold the result is
52217 ** obtained from malloc and must be freed by the calling function.
52218 */
@@ -52091,11 +52241,18 @@
52241 int nLen = sqlite3Strlen30(zBuf);
52242 if( nLen>0 ){
52243 if( winIsDirSep(zBuf[nLen-1]) ){
52244 return 1;
52245 }else if( nLen+1<nBuf ){
52246 if( !osGetenv ){
52247 zBuf[nLen] = winGetDirSep();
52248 }else if( winIsDriveLetterAndColon(zBuf) && winIsDirSep(zBuf[2]) ){
52249 zBuf[nLen] = '\\';
52250 zBuf[2]='\\';
52251 }else{
52252 zBuf[nLen] = '/';
52253 }
52254 zBuf[nLen+1] = '\0';
52255 return 1;
52256 }
52257 }
52258 }
@@ -52118,11 +52275,11 @@
52275 /*
52276 ** Create a temporary file name and store the resulting pointer into pzBuf.
52277 ** The pointer returned in pzBuf must be freed via sqlite3_free().
52278 */
52279 static int winGetTempname(sqlite3_vfs *pVfs, char **pzBuf){
52280 static const char zChars[] =
52281 "abcdefghijklmnopqrstuvwxyz"
52282 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
52283 "0123456789";
52284 size_t i, j;
52285 DWORD pid;
@@ -52169,11 +52326,11 @@
52326 }
52327 sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_TEMPDIR));
52328 }
52329
52330 #if defined(__CYGWIN__)
52331 else if( osGetenv!=NULL ){
52332 static const char *azDirs[] = {
52333 0, /* getenv("SQLITE_TMPDIR") */
52334 0, /* getenv("TMPDIR") */
52335 0, /* getenv("TMP") */
52336 0, /* getenv("TEMP") */
@@ -52185,24 +52342,24 @@
52342 0 /* List terminator */
52343 };
52344 unsigned int i;
52345 const char *zDir = 0;
52346
52347 if( !azDirs[0] ) azDirs[0] = osGetenv("SQLITE_TMPDIR");
52348 if( !azDirs[1] ) azDirs[1] = osGetenv("TMPDIR");
52349 if( !azDirs[2] ) azDirs[2] = osGetenv("TMP");
52350 if( !azDirs[3] ) azDirs[3] = osGetenv("TEMP");
52351 if( !azDirs[4] ) azDirs[4] = osGetenv("USERPROFILE");
52352 for(i=0; i<sizeof(azDirs)/sizeof(azDirs[0]); zDir=azDirs[i++]){
52353 void *zConverted;
52354 if( zDir==0 ) continue;
52355 /* If the path starts with a drive letter followed by the colon
52356 ** character, assume it is already a native Win32 path; otherwise,
52357 ** it must be converted to a native Win32 path via the Cygwin API
52358 ** prior to using it.
52359 */
52360 {
52361 zConverted = winConvertFromUtf8Filename(zDir);
52362 if( !zConverted ){
52363 sqlite3_free(zBuf);
52364 OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
52365 return SQLITE_IOERR_NOMEM_BKPT;
@@ -52211,19 +52368,20 @@
52368 sqlite3_snprintf(nMax, zBuf, "%s", zDir);
52369 sqlite3_free(zConverted);
52370 break;
52371 }
52372 sqlite3_free(zConverted);
52373 #if 0 /* No longer necessary */
52374 }else{
52375 zConverted = sqlite3MallocZero( nMax+1 );
52376 if( !zConverted ){
52377 sqlite3_free(zBuf);
52378 OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_NOMEM\n"));
52379 return SQLITE_IOERR_NOMEM_BKPT;
52380 }
52381 if( osCygwin_conv_path(
52382 CCP_POSIX_TO_WIN_W, zDir,
52383 zConverted, nMax+1)<0 ){
52384 sqlite3_free(zConverted);
52385 sqlite3_free(zBuf);
52386 OSTRACE(("TEMP-FILENAME rc=SQLITE_IOERR_CONVPATH\n"));
52387 return winLogError(SQLITE_IOERR_CONVPATH, (DWORD)errno,
@@ -52245,14 +52403,17 @@
52403 sqlite3_free(zUtf8);
52404 sqlite3_free(zConverted);
52405 break;
52406 }
52407 sqlite3_free(zConverted);
52408 #endif /* No longer necessary */
52409 }
52410 }
52411 }
52412 #endif
52413
52414 #if !SQLITE_OS_WINRT && defined(_WIN32)
52415 else if( osIsNT() ){
52416 char *zMulti;
52417 LPWSTR zWidePath = sqlite3MallocZero( nMax*sizeof(WCHAR) );
52418 if( !zWidePath ){
52419 sqlite3_free(zBuf);
@@ -52372,11 +52533,11 @@
52533 &sAttrData)) && winRetryIoerr(&cnt, &lastErrno) ){}
52534 if( !rc ){
52535 return 0; /* Invalid name? */
52536 }
52537 attr = sAttrData.dwFileAttributes;
52538 #if SQLITE_OS_WINCE==0 && defined(SQLITE_WIN32_HAS_ANSI)
52539 }else{
52540 attr = osGetFileAttributesA((char*)zConverted);
52541 #endif
52542 }
52543 return (attr!=INVALID_FILE_ATTRIBUTES) && (attr&FILE_ATTRIBUTE_DIRECTORY);
@@ -52388,10 +52549,16 @@
52549 const char *zFilename, /* Name of file to check */
52550 int flags, /* Type of test to make on this file */
52551 int *pResOut /* OUT: Result */
52552 );
52553
52554 /*
52555 ** The Windows version of xAccess() accepts an extra bit in the flags
52556 ** parameter that prevents an anti-virus retry loop.
52557 */
52558 #define NORETRY 0x4000
52559
52560 /*
52561 ** Open a file.
52562 */
52563 static int winOpen(
52564 sqlite3_vfs *pVfs, /* Used to get maximum path length and AppData */
@@ -52412,10 +52579,11 @@
52579 winVfsAppData *pAppData;
52580 winFile *pFile = (winFile*)id;
52581 void *zConverted; /* Filename in OS encoding */
52582 const char *zUtf8Name = zName; /* Filename in UTF-8 encoding */
52583 int cnt = 0;
52584 int isRO = 0; /* file is known to be accessible readonly */
52585
52586 /* If argument zPath is a NULL pointer, this function is required to open
52587 ** a temporary file. Use this buffer to store the file name in.
52588 */
52589 char *zTmpname = 0; /* For temporary filename, if necessary. */
@@ -52576,13 +52744,13 @@
52744 dwShareMode,
52745 dwCreationDisposition,
52746 &extendedParameters);
52747 if( h!=INVALID_HANDLE_VALUE ) break;
52748 if( isReadWrite ){
52749 int rc2;
52750 sqlite3BeginBenignMalloc();
52751 rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ|NORETRY, &isRO);
52752 sqlite3EndBenignMalloc();
52753 if( rc2==SQLITE_OK && isRO ) break;
52754 }
52755 }while( winRetryIoerr(&cnt, &lastErrno) );
52756 #else
@@ -52593,13 +52761,13 @@
52761 dwCreationDisposition,
52762 dwFlagsAndAttributes,
52763 NULL);
52764 if( h!=INVALID_HANDLE_VALUE ) break;
52765 if( isReadWrite ){
52766 int rc2;
52767 sqlite3BeginBenignMalloc();
52768 rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ|NORETRY, &isRO);
52769 sqlite3EndBenignMalloc();
52770 if( rc2==SQLITE_OK && isRO ) break;
52771 }
52772 }while( winRetryIoerr(&cnt, &lastErrno) );
52773 #endif
@@ -52613,13 +52781,13 @@
52781 dwCreationDisposition,
52782 dwFlagsAndAttributes,
52783 NULL);
52784 if( h!=INVALID_HANDLE_VALUE ) break;
52785 if( isReadWrite ){
52786 int rc2;
52787 sqlite3BeginBenignMalloc();
52788 rc2 = winAccess(pVfs, zUtf8Name, SQLITE_ACCESS_READ|NORETRY, &isRO);
52789 sqlite3EndBenignMalloc();
52790 if( rc2==SQLITE_OK && isRO ) break;
52791 }
52792 }while( winRetryIoerr(&cnt, &lastErrno) );
52793 }
@@ -52630,11 +52798,11 @@
52798 dwDesiredAccess, (h==INVALID_HANDLE_VALUE) ? "failed" : "ok"));
52799
52800 if( h==INVALID_HANDLE_VALUE ){
52801 sqlite3_free(zConverted);
52802 sqlite3_free(zTmpname);
52803 if( isReadWrite && isRO && !isExclusive ){
52804 return winOpen(pVfs, zName, id,
52805 ((flags|SQLITE_OPEN_READONLY) &
52806 ~(SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE)),
52807 pOutFlags);
52808 }else{
@@ -52832,11 +53000,17 @@
53000 ){
53001 DWORD attr;
53002 int rc = 0;
53003 DWORD lastErrno = 0;
53004 void *zConverted;
53005 int noRetry = 0; /* Do not use winRetryIoerr() */
53006 UNUSED_PARAMETER(pVfs);
53007
53008 if( (flags & NORETRY)!=0 ){
53009 noRetry = 1;
53010 flags &= ~NORETRY;
53011 }
53012
53013 SimulateIOError( return SQLITE_IOERR_ACCESS; );
53014 OSTRACE(("ACCESS name=%s, flags=%x, pResOut=%p\n",
53015 zFilename, flags, pResOut));
53016
@@ -52856,11 +53030,14 @@
53030 int cnt = 0;
53031 WIN32_FILE_ATTRIBUTE_DATA sAttrData;
53032 memset(&sAttrData, 0, sizeof(sAttrData));
53033 while( !(rc = osGetFileAttributesExW((LPCWSTR)zConverted,
53034 GetFileExInfoStandard,
53035 &sAttrData))
53036 && !noRetry
53037 && winRetryIoerr(&cnt, &lastErrno)
53038 ){ /* Loop until true */}
53039 if( rc ){
53040 /* For an SQLITE_ACCESS_EXISTS query, treat a zero-length file
53041 ** as if it does not exist.
53042 */
53043 if( flags==SQLITE_ACCESS_EXISTS
@@ -52924,10 +53101,11 @@
53101 const char *zPathname
53102 ){
53103 return ( sqlite3Isalpha(zPathname[0]) && zPathname[1]==':' );
53104 }
53105
53106 #ifdef _WIN32
53107 /*
53108 ** Returns non-zero if the specified path name should be used verbatim. If
53109 ** non-zero is returned from this function, the calling function must simply
53110 ** use the provided path name verbatim -OR- resolve it into a full path name
53111 ** using the GetFullPathName Win32 API function (if available).
@@ -52960,10 +53138,74 @@
53138 ** If we get to this point, the path name should almost certainly be a purely
53139 ** relative one (i.e. not a UNC name, not absolute, and not volume relative).
53140 */
53141 return FALSE;
53142 }
53143 #endif /* _WIN32 */
53144
53145 #ifdef __CYGWIN__
53146 /*
53147 ** Simplify a filename into its canonical form
53148 ** by making the following changes:
53149 **
53150 ** * convert any '/' to '\' (win32) or reverse (Cygwin)
53151 ** * removing any trailing and duplicate / (except for UNC paths)
53152 ** * convert /./ into just /
53153 **
53154 ** Changes are made in-place. Return the new name length.
53155 **
53156 ** The original filename is in z[0..]. If the path is shortened,
53157 ** no-longer used bytes will be written by '\0'.
53158 */
53159 static void winSimplifyName(char *z){
53160 int i, j;
53161 for(i=j=0; z[i]; ++i){
53162 if( winIsDirSep(z[i]) ){
53163 #if !defined(SQLITE_TEST)
53164 /* Some test-cases assume that "./foo" and "foo" are different */
53165 if( z[i+1]=='.' && winIsDirSep(z[i+2]) ){
53166 ++i;
53167 continue;
53168 }
53169 #endif
53170 if( !z[i+1] || (winIsDirSep(z[i+1]) && (i!=0)) ){
53171 continue;
53172 }
53173 z[j++] = osGetenv?'/':'\\';
53174 }else{
53175 z[j++] = z[i];
53176 }
53177 }
53178 while(j<i) z[j++] = '\0';
53179 }
53180
53181 #define SQLITE_MAX_SYMLINKS 100
53182
53183 static int mkFullPathname(
53184 const char *zPath, /* Input path */
53185 char *zOut, /* Output buffer */
53186 int nOut /* Allocated size of buffer zOut */
53187 ){
53188 int nPath = sqlite3Strlen30(zPath);
53189 int iOff = 0;
53190 if( zPath[0]!='/' ){
53191 if( osGetcwd(zOut, nOut-2)==0 ){
53192 return winLogError(SQLITE_CANTOPEN_BKPT, (DWORD)osErrno, "getcwd", zPath);
53193 }
53194 iOff = sqlite3Strlen30(zOut);
53195 zOut[iOff++] = '/';
53196 }
53197 if( (iOff+nPath+1)>nOut ){
53198 /* SQLite assumes that xFullPathname() nul-terminates the output buffer
53199 ** even if it returns an error. */
53200 zOut[iOff] = '\0';
53201 return SQLITE_CANTOPEN_BKPT;
53202 }
53203 sqlite3_snprintf(nOut-iOff, &zOut[iOff], "%s", zPath);
53204 return SQLITE_OK;
53205 }
53206 #endif /* __CYGWIN__ */
53207
53208 /*
53209 ** Turn a relative pathname into a full pathname. Write the full
53210 ** pathname into zOut[]. zOut[] will be at least pVfs->mxPathname
53211 ** bytes in size.
@@ -52972,12 +53214,12 @@
53214 sqlite3_vfs *pVfs, /* Pointer to vfs object */
53215 const char *zRelative, /* Possibly relative input path */
53216 int nFull, /* Size of output buffer in bytes */
53217 char *zFull /* Output buffer */
53218 ){
53219 #if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
53220 int nByte;
53221 void *zConverted;
53222 char *zOut;
53223 #endif
53224
53225 /* If this path name begins with "/X:" or "\\?\", where "X" is any
@@ -52986,68 +53228,114 @@
53228 if( zRelative[0]=='/' && (winIsDriveLetterAndColon(zRelative+1)
53229 || winIsLongPathPrefix(zRelative+1)) ){
53230 zRelative++;
53231 }
53232
53233 SimulateIOError( return SQLITE_ERROR );
53234
53235 #ifdef __CYGWIN__
53236 if( osGetcwd ){
53237 zFull[nFull-1] = '\0';
53238 if( !winIsDriveLetterAndColon(zRelative) || !winIsDirSep(zRelative[2]) ){
53239 int rc = SQLITE_OK;
53240 int nLink = 1; /* Number of symbolic links followed so far */
53241 const char *zIn = zRelative; /* Input path for each iteration of loop */
53242 char *zDel = 0;
53243 struct stat buf;
53244
53245 UNUSED_PARAMETER(pVfs);
53246
53247 do {
53248 /* Call lstat() on path zIn. Set bLink to true if the path is a symbolic
53249 ** link, or false otherwise. */
53250 int bLink = 0;
53251 if( osLstat && osReadlink ) {
53252 if( osLstat(zIn, &buf)!=0 ){
53253 int myErrno = osErrno;
53254 if( myErrno!=ENOENT ){
53255 rc = winLogError(SQLITE_CANTOPEN_BKPT, (DWORD)myErrno, "lstat", zIn);
53256 }
53257 }else{
53258 bLink = ((buf.st_mode & 0170000) == 0120000);
53259 }
53260
53261 if( bLink ){
53262 if( zDel==0 ){
53263 zDel = sqlite3MallocZero(nFull);
53264 if( zDel==0 ) rc = SQLITE_NOMEM;
53265 }else if( ++nLink>SQLITE_MAX_SYMLINKS ){
53266 rc = SQLITE_CANTOPEN_BKPT;
53267 }
53268
53269 if( rc==SQLITE_OK ){
53270 nByte = osReadlink(zIn, zDel, nFull-1);
53271 if( nByte ==(DWORD)-1 ){
53272 rc = winLogError(SQLITE_CANTOPEN_BKPT, (DWORD)osErrno, "readlink", zIn);
53273 }else{
53274 if( zDel[0]!='/' ){
53275 int n;
53276 for(n = sqlite3Strlen30(zIn); n>0 && zIn[n-1]!='/'; n--);
53277 if( nByte+n+1>nFull ){
53278 rc = SQLITE_CANTOPEN_BKPT;
53279 }else{
53280 memmove(&zDel[n], zDel, nByte+1);
53281 memcpy(zDel, zIn, n);
53282 nByte += n;
53283 }
53284 }
53285 zDel[nByte] = '\0';
53286 }
53287 }
53288
53289 zIn = zDel;
53290 }
53291 }
53292
53293 assert( rc!=SQLITE_OK || zIn!=zFull || zIn[0]=='/' );
53294 if( rc==SQLITE_OK && zIn!=zFull ){
53295 rc = mkFullPathname(zIn, zFull, nFull);
53296 }
53297 if( bLink==0 ) break;
53298 zIn = zFull;
53299 }while( rc==SQLITE_OK );
53300
53301 sqlite3_free(zDel);
53302 winSimplifyName(zFull);
53303 return rc;
53304 }
53305 }
53306 #endif /* __CYGWIN__ */
53307 #if 0 /* This doesn't work correctly at all! See:
53308 <https://marc.info/?l=sqlite-users&m=139299149416314&w=2>
53309 */
53310 SimulateIOError( return SQLITE_ERROR );
53311 UNUSED_PARAMETER(nFull);
53312 assert( nFull>=pVfs->mxPathname );
53313 char *zOut = sqlite3MallocZero( pVfs->mxPathname+1 );
53314 if( !zOut ){
53315 return SQLITE_IOERR_NOMEM_BKPT;
53316 }
53317 if( osCygwin_conv_path(
53318 CCP_POSIX_TO_WIN_W,
53319 zRelative, zOut, pVfs->mxPathname+1)<0 ){
53320 sqlite3_free(zOut);
53321 return winLogError(SQLITE_CANTOPEN_CONVPATH, (DWORD)errno,
53322 "winFullPathname2", zRelative);
53323 }else{
53324 char *zUtf8 = winConvertToUtf8Filename(zOut);
53325 if( !zUtf8 ){
53326 sqlite3_free(zOut);
53327 return SQLITE_IOERR_NOMEM_BKPT;
53328 }
53329 sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zUtf8);
53330 sqlite3_free(zUtf8);
53331 sqlite3_free(zOut);
53332 }
53333 return SQLITE_OK;
53334 #endif
53335
53336 #if (SQLITE_OS_WINCE || SQLITE_OS_WINRT) && defined(_WIN32)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53337 SimulateIOError( return SQLITE_ERROR );
53338 /* WinCE has no concept of a relative pathname, or so I am told. */
53339 /* WinRT has no way to convert a relative path to an absolute one. */
53340 if ( sqlite3_data_directory && !winIsVerbatimPathname(zRelative) ){
53341 /*
@@ -53062,11 +53350,12 @@
53350 sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zRelative);
53351 }
53352 return SQLITE_OK;
53353 #endif
53354
53355 #if !SQLITE_OS_WINCE && !SQLITE_OS_WINRT
53356 #if defined(_WIN32)
53357 /* It's odd to simulate an io-error here, but really this is just
53358 ** using the io-error infrastructure to test that SQLite handles this
53359 ** function failing. This function could fail if, for example, the
53360 ** current working directory has been unlinked.
53361 */
@@ -53080,10 +53369,11 @@
53369 */
53370 sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s%c%s",
53371 sqlite3_data_directory, winGetDirSep(), zRelative);
53372 return SQLITE_OK;
53373 }
53374 #endif
53375 zConverted = winConvertFromUtf8Filename(zRelative);
53376 if( zConverted==0 ){
53377 return SQLITE_IOERR_NOMEM_BKPT;
53378 }
53379 if( osIsNT() ){
@@ -53092,16 +53382,17 @@
53382 if( nByte==0 ){
53383 sqlite3_free(zConverted);
53384 return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
53385 "winFullPathname1", zRelative);
53386 }
53387 nByte += 3;
53388 zTemp = sqlite3MallocZero( nByte*sizeof(zTemp[0]) );
53389 if( zTemp==0 ){
53390 sqlite3_free(zConverted);
53391 return SQLITE_IOERR_NOMEM_BKPT;
53392 }
53393 nByte = osGetFullPathNameW((LPCWSTR)zConverted, nByte, zTemp, 0);
53394 if( nByte==0 ){
53395 sqlite3_free(zConverted);
53396 sqlite3_free(zTemp);
53397 return winLogError(SQLITE_CANTOPEN_FULLPATH, osGetLastError(),
53398 "winFullPathname2", zRelative);
@@ -53135,11 +53426,30 @@
53426 zOut = winMbcsToUtf8(zTemp, osAreFileApisANSI());
53427 sqlite3_free(zTemp);
53428 }
53429 #endif
53430 if( zOut ){
53431 #ifdef __CYGWIN__
53432 if( memcmp(zOut, "\\\\?\\", 4) ){
53433 sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut);
53434 }else if( memcmp(zOut+4, "UNC\\", 4) ){
53435 sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut+4);
53436 }else{
53437 char *p = zOut+6;
53438 *p = '\\';
53439 if( osGetcwd ){
53440 /* On Cygwin, UNC paths use forward slashes */
53441 while( *p ){
53442 if( *p=='\\' ) *p = '/';
53443 ++p;
53444 }
53445 }
53446 sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut+6);
53447 }
53448 #else
53449 sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zOut);
53450 #endif /* __CYGWIN__ */
53451 sqlite3_free(zOut);
53452 return SQLITE_OK;
53453 }else{
53454 return SQLITE_IOERR_NOMEM_BKPT;
53455 }
@@ -53165,11 +53475,13 @@
53475 ** Interfaces for opening a shared library, finding entry points
53476 ** within the shared library, and closing the shared library.
53477 */
53478 static void *winDlOpen(sqlite3_vfs *pVfs, const char *zFilename){
53479 HANDLE h;
53480 #if 0 /* This doesn't work correctly at all! See:
53481 <https://marc.info/?l=sqlite-users&m=139299149416314&w=2>
53482 */
53483 int nFull = pVfs->mxPathname+1;
53484 char *zFull = sqlite3MallocZero( nFull );
53485 void *zConverted = 0;
53486 if( zFull==0 ){
53487 OSTRACE(("DLOPEN name=%s, handle=%p\n", zFilename, (void*)0));
@@ -53532,11 +53844,11 @@
53844 };
53845 #endif
53846
53847 /* Double-check that the aSyscall[] array has been constructed
53848 ** correctly. See ticket [bb3a86e890c8e96ab] */
53849 assert( ArraySize(aSyscall)==89 );
53850
53851 /* get memory map allocation granularity */
53852 memset(&winSysInfo, 0, sizeof(SYSTEM_INFO));
53853 #if SQLITE_OS_WINRT
53854 osGetNativeSystemInfo(&winSysInfo);
@@ -66508,14 +66820,12 @@
66820 s2 = aIn[1];
66821 }else{
66822 s1 = s2 = 0;
66823 }
66824
66825 /* nByte is a multiple of 8 between 8 and 65536 */
66826 assert( nByte>=8 && (nByte&7)==0 && nByte<=65536 );
 
 
66827
66828 if( !nativeCksum ){
66829 do {
66830 s1 += BYTESWAP32(aData[0]) + s2;
66831 s2 += BYTESWAP32(aData[1]) + s1;
@@ -147785,10 +148095,11 @@
148095 }
148096
148097 multi_select_end:
148098 pDest->iSdst = dest.iSdst;
148099 pDest->nSdst = dest.nSdst;
148100 pDest->iSDParm2 = dest.iSDParm2;
148101 if( pDelete ){
148102 sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pDelete);
148103 }
148104 return rc;
148105 }
@@ -188065,10 +188376,17 @@
188376 ******************************************************************************
188377 **
188378 */
188379 #ifndef _FTSINT_H
188380 #define _FTSINT_H
188381
188382 /* #include <assert.h> */
188383 /* #include <stdlib.h> */
188384 /* #include <stddef.h> */
188385 /* #include <stdio.h> */
188386 /* #include <string.h> */
188387 /* #include <stdarg.h> */
188388
188389 #if !defined(NDEBUG) && !defined(SQLITE_DEBUG)
188390 # define NDEBUG 1
188391 #endif
188392
@@ -189017,16 +189335,10 @@
189335
189336 #if defined(SQLITE_ENABLE_FTS3) && !defined(SQLITE_CORE)
189337 # define SQLITE_CORE 1
189338 #endif
189339
 
 
 
 
 
 
189340
189341 /* #include "fts3.h" */
189342 #ifndef SQLITE_CORE
189343 /* # include "sqlite3ext.h" */
189344 SQLITE_EXTENSION_INIT1
@@ -227533,12 +227845,12 @@
227845 ){
227846 if( sqlite3_value_type(argv[3])==SQLITE_NULL && isInsert && pgno>1 ){
227847 /* "INSERT INTO dbpage($PGNO,NULL)" causes page number $PGNO and
227848 ** all subsequent pages to be deleted. */
227849 pTab->iDbTrunc = iDb;
227850 pTab->pgnoTrunc = pgno-1;
227851 pgno = 1;
227852 }else{
227853 zErr = "bad page value";
227854 goto update_fail;
227855 }
227856 }
@@ -241869,11 +242181,12 @@
242181 sqlite3Fts5ParseError(
242182 pParse, "expected integer, got \"%.*s\"", p->n, p->p
242183 );
242184 return;
242185 }
242186 if( nNear<214748363 ) nNear = nNear * 10 + (p->p[i] - '0');
242187 /* ^^^^^^^^^^^^^^^--- Prevent integer overflow */
242188 }
242189 }else{
242190 nNear = FTS5_DEFAULT_NEARDIST;
242191 }
242192 pNear->nNear = nNear;
@@ -256775,11 +257088,11 @@
257088 int nArg, /* Number of args */
257089 sqlite3_value **apUnused /* Function arguments */
257090 ){
257091 assert( nArg==0 );
257092 UNUSED_PARAM2(nArg, apUnused);
257093 sqlite3_result_text(pCtx, "fts5: 2025-03-27 23:29:25 121f4d97f9a855131859d342bc2ade5f8c34ba7732029ae156d02cec7cb6dd85", -1, SQLITE_TRANSIENT);
257094 }
257095
257096 /*
257097 ** Implementation of fts5_locale(LOCALE, TEXT) function.
257098 **
257099
--- extsrc/sqlite3.h
+++ extsrc/sqlite3.h
@@ -146,11 +146,11 @@
146146
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147147
** [sqlite_version()] and [sqlite_source_id()].
148148
*/
149149
#define SQLITE_VERSION "3.50.0"
150150
#define SQLITE_VERSION_NUMBER 3050000
151
-#define SQLITE_SOURCE_ID "2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185"
151
+#define SQLITE_SOURCE_ID "2025-03-27 23:29:25 121f4d97f9a855131859d342bc2ade5f8c34ba7732029ae156d02cec7cb6dd85"
152152
153153
/*
154154
** CAPI3REF: Run-Time Library Version Numbers
155155
** KEYWORDS: sqlite3_version sqlite3_sourceid
156156
**
157157
--- extsrc/sqlite3.h
+++ extsrc/sqlite3.h
@@ -146,11 +146,11 @@
146 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147 ** [sqlite_version()] and [sqlite_source_id()].
148 */
149 #define SQLITE_VERSION "3.50.0"
150 #define SQLITE_VERSION_NUMBER 3050000
151 #define SQLITE_SOURCE_ID "2025-03-16 00:13:29 18bda13e197e4b4ec7464b3e70012f71edc05f73d8b14bb48bad452f81c7e185"
152
153 /*
154 ** CAPI3REF: Run-Time Library Version Numbers
155 ** KEYWORDS: sqlite3_version sqlite3_sourceid
156 **
157
--- extsrc/sqlite3.h
+++ extsrc/sqlite3.h
@@ -146,11 +146,11 @@
146 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147 ** [sqlite_version()] and [sqlite_source_id()].
148 */
149 #define SQLITE_VERSION "3.50.0"
150 #define SQLITE_VERSION_NUMBER 3050000
151 #define SQLITE_SOURCE_ID "2025-03-27 23:29:25 121f4d97f9a855131859d342bc2ade5f8c34ba7732029ae156d02cec7cb6dd85"
152
153 /*
154 ** CAPI3REF: Run-Time Library Version Numbers
155 ** KEYWORDS: sqlite3_version sqlite3_sourceid
156 **
157
+2 -2
--- src/backlink.c
+++ src/backlink.c
@@ -431,13 +431,13 @@
431431
blob_reset(&in);
432432
}
433433
434434
435435
/*
436
-** COMMAND: test-wiki-relink
436
+** COMMAND: test-relink-wiki
437437
**
438
-** Usage: %fossil test-wiki-relink WIKI-PAGE-NAME
438
+** Usage: %fossil test-relink-wiki WIKI-PAGE-NAME
439439
**
440440
** Run the backlink_wiki_refresh() procedure on the wiki page
441441
** named. WIKI-PAGE-NAME can be a glob pattern or a prefix
442442
** of the wiki page.
443443
*/
444444
--- src/backlink.c
+++ src/backlink.c
@@ -431,13 +431,13 @@
431 blob_reset(&in);
432 }
433
434
435 /*
436 ** COMMAND: test-wiki-relink
437 **
438 ** Usage: %fossil test-wiki-relink WIKI-PAGE-NAME
439 **
440 ** Run the backlink_wiki_refresh() procedure on the wiki page
441 ** named. WIKI-PAGE-NAME can be a glob pattern or a prefix
442 ** of the wiki page.
443 */
444
--- src/backlink.c
+++ src/backlink.c
@@ -431,13 +431,13 @@
431 blob_reset(&in);
432 }
433
434
435 /*
436 ** COMMAND: test-relink-wiki
437 **
438 ** Usage: %fossil test-relink-wiki WIKI-PAGE-NAME
439 **
440 ** Run the backlink_wiki_refresh() procedure on the wiki page
441 ** named. WIKI-PAGE-NAME can be a glob pattern or a prefix
442 ** of the wiki page.
443 */
444
+2 -1
--- src/branch.c
+++ src/branch.c
@@ -870,11 +870,12 @@
870870
const char *zLastCkin = db_column_text(&q, 5);
871871
const char *zBgClr = db_column_text(&q, 6);
872872
char *zAge = human_readable_age(rNow - rMtime);
873873
sqlite3_int64 iMtime = (sqlite3_int64)(rMtime*86400.0);
874874
if( zMergeTo && zMergeTo[0]==0 ) zMergeTo = 0;
875
- if( zBgClr == 0 ){
875
+ if( zBgClr ) zBgClr = reasonable_bg_color(zBgClr, 0);
876
+ if( zBgClr==0 ){
876877
if( zBranch==0 || strcmp(zBranch,"trunk")==0 ){
877878
zBgClr = 0;
878879
}else{
879880
zBgClr = hash_color(zBranch);
880881
}
881882
--- src/branch.c
+++ src/branch.c
@@ -870,11 +870,12 @@
870 const char *zLastCkin = db_column_text(&q, 5);
871 const char *zBgClr = db_column_text(&q, 6);
872 char *zAge = human_readable_age(rNow - rMtime);
873 sqlite3_int64 iMtime = (sqlite3_int64)(rMtime*86400.0);
874 if( zMergeTo && zMergeTo[0]==0 ) zMergeTo = 0;
875 if( zBgClr == 0 ){
 
876 if( zBranch==0 || strcmp(zBranch,"trunk")==0 ){
877 zBgClr = 0;
878 }else{
879 zBgClr = hash_color(zBranch);
880 }
881
--- src/branch.c
+++ src/branch.c
@@ -870,11 +870,12 @@
870 const char *zLastCkin = db_column_text(&q, 5);
871 const char *zBgClr = db_column_text(&q, 6);
872 char *zAge = human_readable_age(rNow - rMtime);
873 sqlite3_int64 iMtime = (sqlite3_int64)(rMtime*86400.0);
874 if( zMergeTo && zMergeTo[0]==0 ) zMergeTo = 0;
875 if( zBgClr ) zBgClr = reasonable_bg_color(zBgClr, 0);
876 if( zBgClr==0 ){
877 if( zBranch==0 || strcmp(zBranch,"trunk")==0 ){
878 zBgClr = 0;
879 }else{
880 zBgClr = hash_color(zBranch);
881 }
882
+95 -125
--- src/checkin.c
+++ src/checkin.c
@@ -1467,10 +1467,11 @@
14671467
CheckinInfo *p,
14681468
int parent_rid,
14691469
int dryRunFlag
14701470
){
14711471
Blob prompt;
1472
+ int wikiFlags;
14721473
#if defined(_WIN32) || defined(__CYGWIN__)
14731474
int bomSize;
14741475
const unsigned char *bom = get_utf8_bom(&bomSize);
14751476
blob_init(&prompt, (const char *) bom, bomSize);
14761477
if( zInit && zInit[0]){
@@ -1479,14 +1480,37 @@
14791480
#else
14801481
blob_init(&prompt, zInit, -1);
14811482
#endif
14821483
blob_append(&prompt,
14831484
"\n"
1484
- "# Enter a commit message for this check-in."
1485
- " Lines beginning with # are ignored.\n"
1486
- "#\n", -1
1485
+ "# Enter the commit message. Formatting rules:\n"
1486
+ "# * Lines beginning with # are ignored.\n",
1487
+ -1
14871488
);
1489
+ wikiFlags = wiki_convert_flags(1);
1490
+ if( wikiFlags & WIKI_LINKSONLY ){
1491
+ blob_append(&prompt,"# * Hyperlinks inside of [...]\n", -1);
1492
+ if( wikiFlags & WIKI_NEWLINE ){
1493
+ blob_append(&prompt,
1494
+ "# * Newlines are significant and are displayed as written\n", -1);
1495
+ }else{
1496
+ blob_append(&prompt,
1497
+ "# * Newlines are interpreted as ordinary spaces\n",
1498
+ -1
1499
+ );
1500
+ }
1501
+ blob_append(&prompt,
1502
+ "# * All other text will be displayed as written\n", -1);
1503
+ }else{
1504
+ blob_append(&prompt,
1505
+ "# * Hyperlinks: [target] or [target|display-text]\n"
1506
+ "# * Blank lines cause a paragraph break\n"
1507
+ "# * Other text rendered as if it where HTML\n", -1
1508
+ );
1509
+ }
1510
+ blob_append(&prompt, "#\n", 2);
1511
+
14881512
if( dryRunFlag ){
14891513
blob_appendf(&prompt, "# DRY-RUN: This is a test commit. No changes "
14901514
"will be made to the repository\n#\n");
14911515
}
14921516
blob_appendf(&prompt, "# user: %s\n",
@@ -2289,134 +2313,82 @@
22892313
**
22902314
** This setting determines how much sanity checking, if any, the
22912315
** "fossil commit" and "fossil amend" commands do against check-in
22922316
** comments. Recognized values:
22932317
**
2294
-** on (Default) Check for bad syntax in check-in comments
2295
-** and offer the user a chance to continue editing for
2296
-** interactive sessions, or simply abort the commit if
2297
-** commit was entered using -m or -M
2298
-**
2299
-** off Do not do syntax checking of any kind
2300
-**
2301
-** links Similar to "on", except only check for bad hyperlinks
2302
-**
2303
-** preview Do all the same checks as "on" but also preview the
2304
-** check-in comment to the user during interactive sessions
2305
-** and provide an opportunity to accept or re-edit
2318
+** on (Default) Check for bad syntax and/or broken hyperlinks
2319
+** in check-in comments and offer the user a chance to
2320
+** continue editing for interactive sessions, or simply
2321
+** abort the commit if the comment was entered using -m or -M
2322
+**
2323
+** off Do not do syntax checking of any kind
2324
+**
2325
+** preview Do all the same checks as "on" but also always preview the
2326
+** check-in comment to the user during interactive sessions
2327
+** even if no obvious errors are found, and provide an
2328
+** opportunity to accept or re-edit
23062329
*/
23072330
23082331
#if INTERFACE
2309
-#define COMCK_LINKS 0x01 /* Check for back hyperlinks */
2310
-#define COMCK_MARKUP 0x02 /* Check markup */
2311
-#define COMCK_PREVIEW 0x04 /* Always preview, even if no issues found */
2312
-#define COMCK_NOPREVIEW 0x08 /* Never preview, even for other errors */
2332
+#define COMCK_MARKUP 0x01 /* Check for mistakes */
2333
+#define COMCK_PREVIEW 0x02 /* Always preview, even if no issues found */
23132334
#endif /* INTERFACE */
23142335
23152336
/*
23162337
** Check for possible formatting errors in the comment string pComment.
23172338
**
2318
-** If concerns are found, write a description of the problem(s) to
2319
-** stdout and return non-zero. The return value is some combination
2320
-** of the COMCK_* flags, depending on what went wrong.
2339
+** If issues are found, write an appropriate error notice, probably also
2340
+** including the complete text of the comment formatted to highlight the
2341
+** problem, to stdout and return non-zero. The return value is some
2342
+** combination of the COMCK_* flags, depending on what went wrong.
23212343
**
23222344
** If no issues are seen, do not output anything and return zero.
23232345
*/
2324
-int suspicious_comment(Blob *pComment, int mFlags){
2325
- char *zStart = blob_str(pComment);
2326
- char *z;
2327
- char *zEnd, *zEnd2;
2328
- char *zSep;
2329
- char cSave1;
2330
- int nIssue = 0;
2346
+int verify_comment(Blob *pComment, int mFlags){
2347
+ Blob in, html;
2348
+ int mResult;
23312349
int rc = mFlags & COMCK_PREVIEW;
2332
- Blob out;
2333
- static const char zSpecial[] = "\\&<*_`[";
2350
+ int wFlags;
23342351
23352352
if( mFlags==0 ) return 0;
2336
- z = zStart;
2337
- blob_init(&out, 0, 0);
2338
- if( mFlags & COMCK_LINKS ){
2339
- while( (z = strchr(z,'['))!=0 ){
2340
- zEnd = strchr(z,']');
2341
- if( zEnd==0 ){
2342
- blob_appendf(&out,"\n (%d) ", ++nIssue);
2343
- blob_appendf(&out, "Unterminated hyperlink \"%.12s...\"", z);
2344
- break;
2345
- }
2346
- if( zEnd[1]=='(' && (zEnd2 = strchr(zEnd,')'))!=0 ){
2347
- blob_appendf(&out,"\n (%d) ", ++nIssue);
2348
- blob_appendf(&out, "Markdown hyperlink syntax: %.*s",
2349
- (int)(zEnd2+1-z), z);
2350
- z = zEnd2;
2351
- continue;
2352
- }
2353
- zSep = strchr(z+1,'|');
2354
- if( zSep==0 || zSep>zEnd ) zSep = zEnd;
2355
- while( zSep>z && fossil_isspace(zSep[-1]) ) zSep--;
2356
- cSave1 = zSep[0];
2357
- zSep[0] = 0;
2358
- if( !wiki_valid_link_target(z+1) ){
2359
- blob_appendf(&out,"\n (%d) ", ++nIssue);
2360
- blob_appendf(&out, "Broken hyperlink: [%s]", z+1);
2361
- }
2362
- zSep[0] = cSave1;
2363
- z = zEnd;
2364
- }
2365
- }
2366
-
2367
- if( nIssue>0
2368
- || (mFlags & COMCK_PREVIEW)!=0
2369
- || ((mFlags & COMCK_MARKUP)!=0 && strcspn(zStart,zSpecial)<strlen(zStart))
2370
- ){
2371
- char zGot[16];
2372
- int nGot = 0;
2373
- int i;
2374
- if( (mFlags & COMCK_MARKUP)!=0 ){
2375
- for(i=0; zSpecial[i]; i++){
2376
- if( strchr(zStart,zSpecial[i]) ) zGot[nGot++] = zSpecial[i];
2377
- }
2378
- }
2379
- zGot[nGot] = 0;
2380
- if( nGot>0 ) rc |= COMCK_MARKUP;
2381
- if( nGot>0 && nIssue>0 ){
2382
- blob_appendf(&out,"\n (%d) Comment uses special character%s \"%s\"",
2383
- ++nIssue, (nGot>1 ? "s" : ""), zGot);
2384
- nGot = 0;
2385
- }
2386
- if( nIssue ){
2387
- rc |= COMCK_LINKS;
2388
- fossil_print(
2389
- "Possible comment formatting error%s:%b\n",
2390
- nIssue>1 ? "s" : "", &out
2391
- );
2392
- }
2393
- if( (mFlags & COMCK_NOPREVIEW)==0 ){
2394
- Blob in, html, txt;
2395
- blob_init(&in, blob_str(pComment), -1);
2396
- blob_init(&html, 0, 0);
2397
- blob_init(&txt, 0, 0);
2398
- wiki_convert(&in, &html, WIKI_INLINE);
2399
- html_to_plaintext(blob_str(&html), &txt);
2400
- if( nGot>0 ){
2401
- fossil_print(
2402
- "The comment uses special character%s \"%s\". "
2403
- "Does it render as you expect?\n\n ",
2404
- (nGot>1 ? "s" : ""), zGot
2405
- );
2406
- }else{
2407
- fossil_print("Preview of the check-in comment:\n\n ");
2408
- }
2353
+ blob_init(&in, blob_str(pComment), -1);
2354
+ blob_init(&html, 0, 0);
2355
+ wFlags = wiki_convert_flags(0);
2356
+ wFlags &= ~WIKI_NOBADLINKS;
2357
+ wFlags |= WIKI_MARK;
2358
+ mResult = wiki_convert(&in, &html, wFlags);
2359
+ if( mResult & RENDER_ANYERROR ) rc |= COMCK_MARKUP;
2360
+ if( rc ){
2361
+ int htot = ((wFlags & WIKI_NEWLINE)!=0 ? 0 : HTOT_FLOW)|HTOT_TRIM;
2362
+ Blob txt;
2363
+ if( terminal_is_vt100() ) htot |= HTOT_VT100;
2364
+ blob_init(&txt, 0, 0);
2365
+ html_to_plaintext(blob_str(&html), &txt, htot);
2366
+ if( rc & COMCK_MARKUP ){
2367
+ fossil_print("Possible format errors in the check-in comment:\n\n ");
2368
+ }else{
2369
+ fossil_print("Preview of the check-in comment:\n\n ");
2370
+ }
2371
+ if( wFlags & WIKI_NEWLINE ){
2372
+ Blob line;
2373
+ char *zIndent = "";
2374
+ while( blob_line(&txt, &line) ){
2375
+ fossil_print("%s%b", zIndent, &line);
2376
+ zIndent = " ";
2377
+ }
2378
+ fossil_print("\n");
2379
+ }else{
24092380
comment_print(blob_str(&txt), 0, 3, -1, get_comment_format());
2410
- blob_reset(&in);
2411
- blob_reset(&html);
2412
- blob_reset(&txt);
24132381
}
2382
+ fossil_print("\n");
2383
+ fflush(stdout);
2384
+ blob_reset(&txt);
24142385
}
2415
- blob_reset(&out);
2386
+ blob_reset(&html);
2387
+ blob_reset(&in);
24162388
return rc;
2417
-}
2389
+}
24182390
24192391
/*
24202392
** COMMAND: ci#
24212393
** COMMAND: commit
24222394
**
@@ -2464,11 +2436,13 @@
24642436
** --allow-conflict Allow unresolved merge conflicts
24652437
** --allow-empty Allow a commit with no changes
24662438
** --allow-fork Allow the commit to fork
24672439
** --allow-older Allow a commit older than its ancestor
24682440
** --baseline Use a baseline manifest in the commit process
2441
+** --bgcolor COLOR Apply COLOR to this one check-in only
24692442
** --branch NEW-BRANCH-NAME Check in to this new branch
2443
+** --branchcolor COLOR Apply given COLOR to the branch
24702444
** --close Close the branch being committed
24712445
** --date-override DATETIME Make DATETIME the time of the check-in.
24722446
** Useful when importing historical check-ins
24732447
** from another version control system.
24742448
** --delta Use a delta manifest in the commit process
@@ -2556,11 +2530,11 @@
25562530
int bRecheck = 0; /* Repeat fork and closed-branch checks*/
25572531
int bIgnoreSkew = 0; /* --ignore-clock-skew flag */
25582532
int mxSize;
25592533
char *zCurBranch = 0; /* The current branch name of checkout */
25602534
char *zNewBranch = 0; /* The branch name after update */
2561
- int ckComFlgs; /* Flags passed to suspicious_comment() */
2535
+ int ckComFlgs; /* Flags passed to verify_comment() */
25622536
25632537
memset(&sCiInfo, 0, sizeof(sCiInfo));
25642538
url_proxy_options();
25652539
/* --sha1sum is an undocumented alias for --hash for backwards compatiblity */
25662540
useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
@@ -2928,15 +2902,13 @@
29282902
}else{
29292903
const char *zVerComs = db_get("verify-comments","on");
29302904
if( is_false(zVerComs) ){
29312905
ckComFlgs = 0;
29322906
}else if( strcmp(zVerComs,"preview")==0 ){
2933
- ckComFlgs = COMCK_PREVIEW | COMCK_LINKS | COMCK_MARKUP;
2934
- }else if( strcmp(zVerComs,"links")==0 ){
2935
- ckComFlgs = COMCK_LINKS;
2907
+ ckComFlgs = COMCK_PREVIEW | COMCK_MARKUP;
29362908
}else{
2937
- ckComFlgs = COMCK_LINKS | COMCK_MARKUP;
2909
+ ckComFlgs = COMCK_MARKUP;
29382910
}
29392911
}
29402912
29412913
/* Get the check-in comment. This might involve prompting the
29422914
** user for the check-in comment, in which case we should resync
@@ -2943,23 +2915,21 @@
29432915
** to renew the check-in lock and repeat the checks for conflicts.
29442916
*/
29452917
if( zComment ){
29462918
blob_zero(&comment);
29472919
blob_append(&comment, zComment, -1);
2948
- ckComFlgs &= ~(COMCK_PREVIEW|COMCK_MARKUP);
2949
- ckComFlgs |= COMCK_NOPREVIEW;
2950
- if( suspicious_comment(&comment, ckComFlgs) ){
2920
+ ckComFlgs &= ~COMCK_PREVIEW;
2921
+ if( verify_comment(&comment, ckComFlgs) ){
29512922
fossil_fatal("Commit aborted; "
29522923
"use --no-verify-comment to override");
29532924
}
29542925
}else if( zComFile ){
29552926
blob_zero(&comment);
29562927
blob_read_from_file(&comment, zComFile, ExtFILE);
29572928
blob_to_utf8_no_bom(&comment, 1);
2958
- ckComFlgs &= ~(COMCK_PREVIEW|COMCK_MARKUP);
2959
- ckComFlgs |= COMCK_NOPREVIEW;
2960
- if( suspicious_comment(&comment, ckComFlgs) ){
2929
+ ckComFlgs &= ~COMCK_PREVIEW;
2930
+ if( verify_comment(&comment, ckComFlgs) ){
29612931
fossil_fatal("Commit aborted; "
29622932
"use --no-verify-comment to override");
29632933
}
29642934
}else if( !noPrompt ){
29652935
while( 1/*exit-by-break*/ ){
@@ -2966,23 +2936,23 @@
29662936
int rc;
29672937
char *zInit;
29682938
zInit = db_text(0,"SELECT value FROM vvar WHERE name='ci-comment'");
29692939
prepare_commit_comment(&comment, zInit, &sCiInfo, vid, dryRunFlag);
29702940
db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment);
2971
- if( (rc = suspicious_comment(&comment, ckComFlgs))!=0 ){
2941
+ if( (rc = verify_comment(&comment, ckComFlgs))!=0 ){
29722942
if( rc==COMCK_PREVIEW ){
2973
- prompt_user("\nContinue (Y/n/e=edit)? ", &ans);
2943
+ prompt_user("Continue, abort, or edit? (C/a/e)? ", &ans);
29742944
}else{
2975
- prompt_user("\nContinue (y/n/E=edit)? ", &ans);
2945
+ prompt_user("Edit, abort, or continue (E/a/c)? ", &ans);
29762946
}
29772947
cReply = blob_str(&ans)[0];
29782948
cReply = fossil_tolower(cReply);
29792949
blob_reset(&ans);
2980
- if( cReply=='n' ){
2950
+ if( cReply=='a' ){
29812951
fossil_fatal("Commit aborted.");
29822952
}
2983
- if( cReply=='e' || (cReply!='y' && rc!=COMCK_PREVIEW) ){
2953
+ if( cReply=='e' || (cReply!='c' && rc!=COMCK_PREVIEW) ){
29842954
fossil_free(zInit);
29852955
continue;
29862956
}
29872957
}
29882958
if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){
29892959
--- src/checkin.c
+++ src/checkin.c
@@ -1467,10 +1467,11 @@
1467 CheckinInfo *p,
1468 int parent_rid,
1469 int dryRunFlag
1470 ){
1471 Blob prompt;
 
1472 #if defined(_WIN32) || defined(__CYGWIN__)
1473 int bomSize;
1474 const unsigned char *bom = get_utf8_bom(&bomSize);
1475 blob_init(&prompt, (const char *) bom, bomSize);
1476 if( zInit && zInit[0]){
@@ -1479,14 +1480,37 @@
1479 #else
1480 blob_init(&prompt, zInit, -1);
1481 #endif
1482 blob_append(&prompt,
1483 "\n"
1484 "# Enter a commit message for this check-in."
1485 " Lines beginning with # are ignored.\n"
1486 "#\n", -1
1487 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1488 if( dryRunFlag ){
1489 blob_appendf(&prompt, "# DRY-RUN: This is a test commit. No changes "
1490 "will be made to the repository\n#\n");
1491 }
1492 blob_appendf(&prompt, "# user: %s\n",
@@ -2289,134 +2313,82 @@
2289 **
2290 ** This setting determines how much sanity checking, if any, the
2291 ** "fossil commit" and "fossil amend" commands do against check-in
2292 ** comments. Recognized values:
2293 **
2294 ** on (Default) Check for bad syntax in check-in comments
2295 ** and offer the user a chance to continue editing for
2296 ** interactive sessions, or simply abort the commit if
2297 ** commit was entered using -m or -M
2298 **
2299 ** off Do not do syntax checking of any kind
2300 **
2301 ** links Similar to "on", except only check for bad hyperlinks
2302 **
2303 ** preview Do all the same checks as "on" but also preview the
2304 ** check-in comment to the user during interactive sessions
2305 ** and provide an opportunity to accept or re-edit
2306 */
2307
2308 #if INTERFACE
2309 #define COMCK_LINKS 0x01 /* Check for back hyperlinks */
2310 #define COMCK_MARKUP 0x02 /* Check markup */
2311 #define COMCK_PREVIEW 0x04 /* Always preview, even if no issues found */
2312 #define COMCK_NOPREVIEW 0x08 /* Never preview, even for other errors */
2313 #endif /* INTERFACE */
2314
2315 /*
2316 ** Check for possible formatting errors in the comment string pComment.
2317 **
2318 ** If concerns are found, write a description of the problem(s) to
2319 ** stdout and return non-zero. The return value is some combination
2320 ** of the COMCK_* flags, depending on what went wrong.
 
2321 **
2322 ** If no issues are seen, do not output anything and return zero.
2323 */
2324 int suspicious_comment(Blob *pComment, int mFlags){
2325 char *zStart = blob_str(pComment);
2326 char *z;
2327 char *zEnd, *zEnd2;
2328 char *zSep;
2329 char cSave1;
2330 int nIssue = 0;
2331 int rc = mFlags & COMCK_PREVIEW;
2332 Blob out;
2333 static const char zSpecial[] = "\\&<*_`[";
2334
2335 if( mFlags==0 ) return 0;
2336 z = zStart;
2337 blob_init(&out, 0, 0);
2338 if( mFlags & COMCK_LINKS ){
2339 while( (z = strchr(z,'['))!=0 ){
2340 zEnd = strchr(z,']');
2341 if( zEnd==0 ){
2342 blob_appendf(&out,"\n (%d) ", ++nIssue);
2343 blob_appendf(&out, "Unterminated hyperlink \"%.12s...\"", z);
2344 break;
2345 }
2346 if( zEnd[1]=='(' && (zEnd2 = strchr(zEnd,')'))!=0 ){
2347 blob_appendf(&out,"\n (%d) ", ++nIssue);
2348 blob_appendf(&out, "Markdown hyperlink syntax: %.*s",
2349 (int)(zEnd2+1-z), z);
2350 z = zEnd2;
2351 continue;
2352 }
2353 zSep = strchr(z+1,'|');
2354 if( zSep==0 || zSep>zEnd ) zSep = zEnd;
2355 while( zSep>z && fossil_isspace(zSep[-1]) ) zSep--;
2356 cSave1 = zSep[0];
2357 zSep[0] = 0;
2358 if( !wiki_valid_link_target(z+1) ){
2359 blob_appendf(&out,"\n (%d) ", ++nIssue);
2360 blob_appendf(&out, "Broken hyperlink: [%s]", z+1);
2361 }
2362 zSep[0] = cSave1;
2363 z = zEnd;
2364 }
2365 }
2366
2367 if( nIssue>0
2368 || (mFlags & COMCK_PREVIEW)!=0
2369 || ((mFlags & COMCK_MARKUP)!=0 && strcspn(zStart,zSpecial)<strlen(zStart))
2370 ){
2371 char zGot[16];
2372 int nGot = 0;
2373 int i;
2374 if( (mFlags & COMCK_MARKUP)!=0 ){
2375 for(i=0; zSpecial[i]; i++){
2376 if( strchr(zStart,zSpecial[i]) ) zGot[nGot++] = zSpecial[i];
2377 }
2378 }
2379 zGot[nGot] = 0;
2380 if( nGot>0 ) rc |= COMCK_MARKUP;
2381 if( nGot>0 && nIssue>0 ){
2382 blob_appendf(&out,"\n (%d) Comment uses special character%s \"%s\"",
2383 ++nIssue, (nGot>1 ? "s" : ""), zGot);
2384 nGot = 0;
2385 }
2386 if( nIssue ){
2387 rc |= COMCK_LINKS;
2388 fossil_print(
2389 "Possible comment formatting error%s:%b\n",
2390 nIssue>1 ? "s" : "", &out
2391 );
2392 }
2393 if( (mFlags & COMCK_NOPREVIEW)==0 ){
2394 Blob in, html, txt;
2395 blob_init(&in, blob_str(pComment), -1);
2396 blob_init(&html, 0, 0);
2397 blob_init(&txt, 0, 0);
2398 wiki_convert(&in, &html, WIKI_INLINE);
2399 html_to_plaintext(blob_str(&html), &txt);
2400 if( nGot>0 ){
2401 fossil_print(
2402 "The comment uses special character%s \"%s\". "
2403 "Does it render as you expect?\n\n ",
2404 (nGot>1 ? "s" : ""), zGot
2405 );
2406 }else{
2407 fossil_print("Preview of the check-in comment:\n\n ");
2408 }
2409 comment_print(blob_str(&txt), 0, 3, -1, get_comment_format());
2410 blob_reset(&in);
2411 blob_reset(&html);
2412 blob_reset(&txt);
2413 }
 
 
 
2414 }
2415 blob_reset(&out);
 
2416 return rc;
2417 }
2418
2419 /*
2420 ** COMMAND: ci#
2421 ** COMMAND: commit
2422 **
@@ -2464,11 +2436,13 @@
2464 ** --allow-conflict Allow unresolved merge conflicts
2465 ** --allow-empty Allow a commit with no changes
2466 ** --allow-fork Allow the commit to fork
2467 ** --allow-older Allow a commit older than its ancestor
2468 ** --baseline Use a baseline manifest in the commit process
 
2469 ** --branch NEW-BRANCH-NAME Check in to this new branch
 
2470 ** --close Close the branch being committed
2471 ** --date-override DATETIME Make DATETIME the time of the check-in.
2472 ** Useful when importing historical check-ins
2473 ** from another version control system.
2474 ** --delta Use a delta manifest in the commit process
@@ -2556,11 +2530,11 @@
2556 int bRecheck = 0; /* Repeat fork and closed-branch checks*/
2557 int bIgnoreSkew = 0; /* --ignore-clock-skew flag */
2558 int mxSize;
2559 char *zCurBranch = 0; /* The current branch name of checkout */
2560 char *zNewBranch = 0; /* The branch name after update */
2561 int ckComFlgs; /* Flags passed to suspicious_comment() */
2562
2563 memset(&sCiInfo, 0, sizeof(sCiInfo));
2564 url_proxy_options();
2565 /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */
2566 useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
@@ -2928,15 +2902,13 @@
2928 }else{
2929 const char *zVerComs = db_get("verify-comments","on");
2930 if( is_false(zVerComs) ){
2931 ckComFlgs = 0;
2932 }else if( strcmp(zVerComs,"preview")==0 ){
2933 ckComFlgs = COMCK_PREVIEW | COMCK_LINKS | COMCK_MARKUP;
2934 }else if( strcmp(zVerComs,"links")==0 ){
2935 ckComFlgs = COMCK_LINKS;
2936 }else{
2937 ckComFlgs = COMCK_LINKS | COMCK_MARKUP;
2938 }
2939 }
2940
2941 /* Get the check-in comment. This might involve prompting the
2942 ** user for the check-in comment, in which case we should resync
@@ -2943,23 +2915,21 @@
2943 ** to renew the check-in lock and repeat the checks for conflicts.
2944 */
2945 if( zComment ){
2946 blob_zero(&comment);
2947 blob_append(&comment, zComment, -1);
2948 ckComFlgs &= ~(COMCK_PREVIEW|COMCK_MARKUP);
2949 ckComFlgs |= COMCK_NOPREVIEW;
2950 if( suspicious_comment(&comment, ckComFlgs) ){
2951 fossil_fatal("Commit aborted; "
2952 "use --no-verify-comment to override");
2953 }
2954 }else if( zComFile ){
2955 blob_zero(&comment);
2956 blob_read_from_file(&comment, zComFile, ExtFILE);
2957 blob_to_utf8_no_bom(&comment, 1);
2958 ckComFlgs &= ~(COMCK_PREVIEW|COMCK_MARKUP);
2959 ckComFlgs |= COMCK_NOPREVIEW;
2960 if( suspicious_comment(&comment, ckComFlgs) ){
2961 fossil_fatal("Commit aborted; "
2962 "use --no-verify-comment to override");
2963 }
2964 }else if( !noPrompt ){
2965 while( 1/*exit-by-break*/ ){
@@ -2966,23 +2936,23 @@
2966 int rc;
2967 char *zInit;
2968 zInit = db_text(0,"SELECT value FROM vvar WHERE name='ci-comment'");
2969 prepare_commit_comment(&comment, zInit, &sCiInfo, vid, dryRunFlag);
2970 db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment);
2971 if( (rc = suspicious_comment(&comment, ckComFlgs))!=0 ){
2972 if( rc==COMCK_PREVIEW ){
2973 prompt_user("\nContinue (Y/n/e=edit)? ", &ans);
2974 }else{
2975 prompt_user("\nContinue (y/n/E=edit)? ", &ans);
2976 }
2977 cReply = blob_str(&ans)[0];
2978 cReply = fossil_tolower(cReply);
2979 blob_reset(&ans);
2980 if( cReply=='n' ){
2981 fossil_fatal("Commit aborted.");
2982 }
2983 if( cReply=='e' || (cReply!='y' && rc!=COMCK_PREVIEW) ){
2984 fossil_free(zInit);
2985 continue;
2986 }
2987 }
2988 if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){
2989
--- src/checkin.c
+++ src/checkin.c
@@ -1467,10 +1467,11 @@
1467 CheckinInfo *p,
1468 int parent_rid,
1469 int dryRunFlag
1470 ){
1471 Blob prompt;
1472 int wikiFlags;
1473 #if defined(_WIN32) || defined(__CYGWIN__)
1474 int bomSize;
1475 const unsigned char *bom = get_utf8_bom(&bomSize);
1476 blob_init(&prompt, (const char *) bom, bomSize);
1477 if( zInit && zInit[0]){
@@ -1479,14 +1480,37 @@
1480 #else
1481 blob_init(&prompt, zInit, -1);
1482 #endif
1483 blob_append(&prompt,
1484 "\n"
1485 "# Enter the commit message. Formatting rules:\n"
1486 "# * Lines beginning with # are ignored.\n",
1487 -1
1488 );
1489 wikiFlags = wiki_convert_flags(1);
1490 if( wikiFlags & WIKI_LINKSONLY ){
1491 blob_append(&prompt,"# * Hyperlinks inside of [...]\n", -1);
1492 if( wikiFlags & WIKI_NEWLINE ){
1493 blob_append(&prompt,
1494 "# * Newlines are significant and are displayed as written\n", -1);
1495 }else{
1496 blob_append(&prompt,
1497 "# * Newlines are interpreted as ordinary spaces\n",
1498 -1
1499 );
1500 }
1501 blob_append(&prompt,
1502 "# * All other text will be displayed as written\n", -1);
1503 }else{
1504 blob_append(&prompt,
1505 "# * Hyperlinks: [target] or [target|display-text]\n"
1506 "# * Blank lines cause a paragraph break\n"
1507 "# * Other text rendered as if it where HTML\n", -1
1508 );
1509 }
1510 blob_append(&prompt, "#\n", 2);
1511
1512 if( dryRunFlag ){
1513 blob_appendf(&prompt, "# DRY-RUN: This is a test commit. No changes "
1514 "will be made to the repository\n#\n");
1515 }
1516 blob_appendf(&prompt, "# user: %s\n",
@@ -2289,134 +2313,82 @@
2313 **
2314 ** This setting determines how much sanity checking, if any, the
2315 ** "fossil commit" and "fossil amend" commands do against check-in
2316 ** comments. Recognized values:
2317 **
2318 ** on (Default) Check for bad syntax and/or broken hyperlinks
2319 ** in check-in comments and offer the user a chance to
2320 ** continue editing for interactive sessions, or simply
2321 ** abort the commit if the comment was entered using -m or -M
2322 **
2323 ** off Do not do syntax checking of any kind
2324 **
2325 ** preview Do all the same checks as "on" but also always preview the
2326 ** check-in comment to the user during interactive sessions
2327 ** even if no obvious errors are found, and provide an
2328 ** opportunity to accept or re-edit
 
2329 */
2330
2331 #if INTERFACE
2332 #define COMCK_MARKUP 0x01 /* Check for mistakes */
2333 #define COMCK_PREVIEW 0x02 /* Always preview, even if no issues found */
 
 
2334 #endif /* INTERFACE */
2335
2336 /*
2337 ** Check for possible formatting errors in the comment string pComment.
2338 **
2339 ** If issues are found, write an appropriate error notice, probably also
2340 ** including the complete text of the comment formatted to highlight the
2341 ** problem, to stdout and return non-zero. The return value is some
2342 ** combination of the COMCK_* flags, depending on what went wrong.
2343 **
2344 ** If no issues are seen, do not output anything and return zero.
2345 */
2346 int verify_comment(Blob *pComment, int mFlags){
2347 Blob in, html;
2348 int mResult;
 
 
 
 
2349 int rc = mFlags & COMCK_PREVIEW;
2350 int wFlags;
 
2351
2352 if( mFlags==0 ) return 0;
2353 blob_init(&in, blob_str(pComment), -1);
2354 blob_init(&html, 0, 0);
2355 wFlags = wiki_convert_flags(0);
2356 wFlags &= ~WIKI_NOBADLINKS;
2357 wFlags |= WIKI_MARK;
2358 mResult = wiki_convert(&in, &html, wFlags);
2359 if( mResult & RENDER_ANYERROR ) rc |= COMCK_MARKUP;
2360 if( rc ){
2361 int htot = ((wFlags & WIKI_NEWLINE)!=0 ? 0 : HTOT_FLOW)|HTOT_TRIM;
2362 Blob txt;
2363 if( terminal_is_vt100() ) htot |= HTOT_VT100;
2364 blob_init(&txt, 0, 0);
2365 html_to_plaintext(blob_str(&html), &txt, htot);
2366 if( rc & COMCK_MARKUP ){
2367 fossil_print("Possible format errors in the check-in comment:\n\n ");
2368 }else{
2369 fossil_print("Preview of the check-in comment:\n\n ");
2370 }
2371 if( wFlags & WIKI_NEWLINE ){
2372 Blob line;
2373 char *zIndent = "";
2374 while( blob_line(&txt, &line) ){
2375 fossil_print("%s%b", zIndent, &line);
2376 zIndent = " ";
2377 }
2378 fossil_print("\n");
2379 }else{
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2380 comment_print(blob_str(&txt), 0, 3, -1, get_comment_format());
 
 
 
2381 }
2382 fossil_print("\n");
2383 fflush(stdout);
2384 blob_reset(&txt);
2385 }
2386 blob_reset(&html);
2387 blob_reset(&in);
2388 return rc;
2389 }
2390
2391 /*
2392 ** COMMAND: ci#
2393 ** COMMAND: commit
2394 **
@@ -2464,11 +2436,13 @@
2436 ** --allow-conflict Allow unresolved merge conflicts
2437 ** --allow-empty Allow a commit with no changes
2438 ** --allow-fork Allow the commit to fork
2439 ** --allow-older Allow a commit older than its ancestor
2440 ** --baseline Use a baseline manifest in the commit process
2441 ** --bgcolor COLOR Apply COLOR to this one check-in only
2442 ** --branch NEW-BRANCH-NAME Check in to this new branch
2443 ** --branchcolor COLOR Apply given COLOR to the branch
2444 ** --close Close the branch being committed
2445 ** --date-override DATETIME Make DATETIME the time of the check-in.
2446 ** Useful when importing historical check-ins
2447 ** from another version control system.
2448 ** --delta Use a delta manifest in the commit process
@@ -2556,11 +2530,11 @@
2530 int bRecheck = 0; /* Repeat fork and closed-branch checks*/
2531 int bIgnoreSkew = 0; /* --ignore-clock-skew flag */
2532 int mxSize;
2533 char *zCurBranch = 0; /* The current branch name of checkout */
2534 char *zNewBranch = 0; /* The branch name after update */
2535 int ckComFlgs; /* Flags passed to verify_comment() */
2536
2537 memset(&sCiInfo, 0, sizeof(sCiInfo));
2538 url_proxy_options();
2539 /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */
2540 useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
@@ -2928,15 +2902,13 @@
2902 }else{
2903 const char *zVerComs = db_get("verify-comments","on");
2904 if( is_false(zVerComs) ){
2905 ckComFlgs = 0;
2906 }else if( strcmp(zVerComs,"preview")==0 ){
2907 ckComFlgs = COMCK_PREVIEW | COMCK_MARKUP;
 
 
2908 }else{
2909 ckComFlgs = COMCK_MARKUP;
2910 }
2911 }
2912
2913 /* Get the check-in comment. This might involve prompting the
2914 ** user for the check-in comment, in which case we should resync
@@ -2943,23 +2915,21 @@
2915 ** to renew the check-in lock and repeat the checks for conflicts.
2916 */
2917 if( zComment ){
2918 blob_zero(&comment);
2919 blob_append(&comment, zComment, -1);
2920 ckComFlgs &= ~COMCK_PREVIEW;
2921 if( verify_comment(&comment, ckComFlgs) ){
 
2922 fossil_fatal("Commit aborted; "
2923 "use --no-verify-comment to override");
2924 }
2925 }else if( zComFile ){
2926 blob_zero(&comment);
2927 blob_read_from_file(&comment, zComFile, ExtFILE);
2928 blob_to_utf8_no_bom(&comment, 1);
2929 ckComFlgs &= ~COMCK_PREVIEW;
2930 if( verify_comment(&comment, ckComFlgs) ){
 
2931 fossil_fatal("Commit aborted; "
2932 "use --no-verify-comment to override");
2933 }
2934 }else if( !noPrompt ){
2935 while( 1/*exit-by-break*/ ){
@@ -2966,23 +2936,23 @@
2936 int rc;
2937 char *zInit;
2938 zInit = db_text(0,"SELECT value FROM vvar WHERE name='ci-comment'");
2939 prepare_commit_comment(&comment, zInit, &sCiInfo, vid, dryRunFlag);
2940 db_multi_exec("REPLACE INTO vvar VALUES('ci-comment',%B)", &comment);
2941 if( (rc = verify_comment(&comment, ckComFlgs))!=0 ){
2942 if( rc==COMCK_PREVIEW ){
2943 prompt_user("Continue, abort, or edit? (C/a/e)? ", &ans);
2944 }else{
2945 prompt_user("Edit, abort, or continue (E/a/c)? ", &ans);
2946 }
2947 cReply = blob_str(&ans)[0];
2948 cReply = fossil_tolower(cReply);
2949 blob_reset(&ans);
2950 if( cReply=='a' ){
2951 fossil_fatal("Commit aborted.");
2952 }
2953 if( cReply=='e' || (cReply!='c' && rc!=COMCK_PREVIEW) ){
2954 fossil_free(zInit);
2955 continue;
2956 }
2957 }
2958 if( zInit && zInit[0] && fossil_strcmp(zInit, blob_str(&comment))==0 ){
2959
+10 -4
--- src/checkout.c
+++ src/checkout.c
@@ -276,13 +276,14 @@
276276
**
277277
** The --latest flag can be used in place of VERSION to check-out the
278278
** latest version in the repository.
279279
**
280280
** Options:
281
-** --force Ignore edited files in the current check-out
282
-** --keep Only update the manifest file(s)
281
+** -f|--force Ignore edited files in the current check-out
282
+** -k|--keep Only update the manifest file(s)
283283
** --force-missing Force check-out even if content is missing
284
+** --prompt Prompt before overwriting when --force is used
284285
** --setmtime Set timestamps of all files to match their SCM-side
285286
** times (the timestamp of the last check-in which modified
286287
** them)
287288
**
288289
** See also: [[update]]
@@ -298,16 +299,21 @@
298299
int setmtimeFlag; /* --setmtime. Set mtimes on files */
299300
Blob cksum1, cksum1b, cksum2;
300301
301302
db_must_be_within_tree();
302303
db_begin_transaction();
303
- forceFlag = find_option("force","f",0)!=0;
304304
forceMissingFlag = find_option("force-missing",0,0)!=0;
305
- keepFlag = find_option("keep",0,0)!=0;
305
+ keepFlag = find_option("keep","k",0)!=0;
306
+ forceFlag = find_option("force","f",0)!=0;
306307
latestFlag = find_option("latest",0,0)!=0;
307308
promptFlag = find_option("prompt",0,0)!=0 || forceFlag==0;
308309
setmtimeFlag = find_option("setmtime",0,0)!=0;
310
+
311
+ if( keepFlag != 0 ){
312
+ /* After flag collection, in order not to affect promptFlag */
313
+ forceFlag=1;
314
+ }
309315
310316
/* We should be done with options.. */
311317
verify_all_options();
312318
313319
if( (latestFlag!=0 && g.argc!=2) || (latestFlag==0 && g.argc!=3) ){
314320
--- src/checkout.c
+++ src/checkout.c
@@ -276,13 +276,14 @@
276 **
277 ** The --latest flag can be used in place of VERSION to check-out the
278 ** latest version in the repository.
279 **
280 ** Options:
281 ** --force Ignore edited files in the current check-out
282 ** --keep Only update the manifest file(s)
283 ** --force-missing Force check-out even if content is missing
 
284 ** --setmtime Set timestamps of all files to match their SCM-side
285 ** times (the timestamp of the last check-in which modified
286 ** them)
287 **
288 ** See also: [[update]]
@@ -298,16 +299,21 @@
298 int setmtimeFlag; /* --setmtime. Set mtimes on files */
299 Blob cksum1, cksum1b, cksum2;
300
301 db_must_be_within_tree();
302 db_begin_transaction();
303 forceFlag = find_option("force","f",0)!=0;
304 forceMissingFlag = find_option("force-missing",0,0)!=0;
305 keepFlag = find_option("keep",0,0)!=0;
 
306 latestFlag = find_option("latest",0,0)!=0;
307 promptFlag = find_option("prompt",0,0)!=0 || forceFlag==0;
308 setmtimeFlag = find_option("setmtime",0,0)!=0;
 
 
 
 
 
309
310 /* We should be done with options.. */
311 verify_all_options();
312
313 if( (latestFlag!=0 && g.argc!=2) || (latestFlag==0 && g.argc!=3) ){
314
--- src/checkout.c
+++ src/checkout.c
@@ -276,13 +276,14 @@
276 **
277 ** The --latest flag can be used in place of VERSION to check-out the
278 ** latest version in the repository.
279 **
280 ** Options:
281 ** -f|--force Ignore edited files in the current check-out
282 ** -k|--keep Only update the manifest file(s)
283 ** --force-missing Force check-out even if content is missing
284 ** --prompt Prompt before overwriting when --force is used
285 ** --setmtime Set timestamps of all files to match their SCM-side
286 ** times (the timestamp of the last check-in which modified
287 ** them)
288 **
289 ** See also: [[update]]
@@ -298,16 +299,21 @@
299 int setmtimeFlag; /* --setmtime. Set mtimes on files */
300 Blob cksum1, cksum1b, cksum2;
301
302 db_must_be_within_tree();
303 db_begin_transaction();
 
304 forceMissingFlag = find_option("force-missing",0,0)!=0;
305 keepFlag = find_option("keep","k",0)!=0;
306 forceFlag = find_option("force","f",0)!=0;
307 latestFlag = find_option("latest",0,0)!=0;
308 promptFlag = find_option("prompt",0,0)!=0 || forceFlag==0;
309 setmtimeFlag = find_option("setmtime",0,0)!=0;
310
311 if( keepFlag != 0 ){
312 /* After flag collection, in order not to affect promptFlag */
313 forceFlag=1;
314 }
315
316 /* We should be done with options.. */
317 verify_all_options();
318
319 if( (latestFlag!=0 && g.argc!=2) || (latestFlag==0 && g.argc!=3) ){
320
+333
--- src/color.c
+++ src/color.c
@@ -20,10 +20,262 @@
2020
**
2121
*/
2222
#include "config.h"
2323
#include <string.h>
2424
#include "color.h"
25
+
26
+/*
27
+** 140 standard CSS color names and their corresponding RGB values,
28
+** in alphabetical order by name so that we can do a binary search
29
+** for lookup.
30
+*/
31
+static const struct CssColors {
32
+ const char *zName; /* CSS Color name, lower case */
33
+ unsigned int iRGB; /* Corresponding RGB value */
34
+} aCssColors[] = {
35
+ { "aliceblue", 0xf0f8ff },
36
+ { "antiquewhite", 0xfaebd7 },
37
+ { "aqua", 0x00ffff },
38
+ { "aquamarine", 0x7fffd4 },
39
+ { "azure", 0xf0ffff },
40
+ { "beige", 0xf5f5dc },
41
+ { "bisque", 0xffe4c4 },
42
+ { "black", 0x000000 },
43
+ { "blanchedalmond", 0xffebcd },
44
+ { "blue", 0x0000ff },
45
+ { "blueviolet", 0x8a2be2 },
46
+ { "brown", 0xa52a2a },
47
+ { "burlywood", 0xdeb887 },
48
+ { "cadetblue", 0x5f9ea0 },
49
+ { "chartreuse", 0x7fff00 },
50
+ { "chocolate", 0xd2691e },
51
+ { "coral", 0xff7f50 },
52
+ { "cornflowerblue", 0x6495ed },
53
+ { "cornsilk", 0xfff8dc },
54
+ { "crimson", 0xdc143c },
55
+ { "cyan", 0x00ffff },
56
+ { "darkblue", 0x00008b },
57
+ { "darkcyan", 0x008b8b },
58
+ { "darkgoldenrod", 0xb8860b },
59
+ { "darkgray", 0xa9a9a9 },
60
+ { "darkgreen", 0x006400 },
61
+ { "darkkhaki", 0xbdb76b },
62
+ { "darkmagenta", 0x8b008b },
63
+ { "darkolivegreen", 0x556b2f },
64
+ { "darkorange", 0xff8c00 },
65
+ { "darkorchid", 0x9932cc },
66
+ { "darkred", 0x8b0000 },
67
+ { "darksalmon", 0xe9967a },
68
+ { "darkseagreen", 0x8fbc8f },
69
+ { "darkslateblue", 0x483d8b },
70
+ { "darkslategray", 0x2f4f4f },
71
+ { "darkturquoise", 0x00ced1 },
72
+ { "darkviolet", 0x9400d3 },
73
+ { "deeppink", 0xff1493 },
74
+ { "deepskyblue", 0x00bfff },
75
+ { "dimgray", 0x696969 },
76
+ { "dodgerblue", 0x1e90ff },
77
+ { "firebrick", 0xb22222 },
78
+ { "floralwhite", 0xfffaf0 },
79
+ { "forestgreen", 0x228b22 },
80
+ { "fuchsia", 0xff00ff },
81
+ { "gainsboro", 0xdcdcdc },
82
+ { "ghostwhite", 0xf8f8ff },
83
+ { "gold", 0xffd700 },
84
+ { "goldenrod", 0xdaa520 },
85
+ { "gray", 0x808080 },
86
+ { "green", 0x008000 },
87
+ { "greenyellow", 0xadff2f },
88
+ { "honeydew", 0xf0fff0 },
89
+ { "hotpink", 0xff69b4 },
90
+ { "indianred", 0xcd5c5c },
91
+ { "indigo", 0x4b0082 },
92
+ { "ivory", 0xfffff0 },
93
+ { "khaki", 0xf0e68c },
94
+ { "lavender", 0xe6e6fa },
95
+ { "lavenderblush", 0xfff0f5 },
96
+ { "lawngreen", 0x7cfc00 },
97
+ { "lemonchiffon", 0xfffacd },
98
+ { "lightblue", 0xadd8e6 },
99
+ { "lightcoral", 0xf08080 },
100
+ { "lightcyan", 0xe0ffff },
101
+ { "lightgoldenrodyellow", 0xfafad2 },
102
+ { "lightgrey", 0xd3d3d3 },
103
+ { "lightgreen", 0x90ee90 },
104
+ { "lightpink", 0xffb6c1 },
105
+ { "lightsalmon", 0xffa07a },
106
+ { "lightseagreen", 0x20b2aa },
107
+ { "lightskyblue", 0x87cefa },
108
+ { "lightslategray", 0x778899 },
109
+ { "lightsteelblue", 0xb0c4de },
110
+ { "lightyellow", 0xffffe0 },
111
+ { "lime", 0x00ff00 },
112
+ { "limegreen", 0x32cd32 },
113
+ { "linen", 0xfaf0e6 },
114
+ { "magenta", 0xff00ff },
115
+ { "maroon", 0x800000 },
116
+ { "mediumaquamarine", 0x66cdaa },
117
+ { "mediumblue", 0x0000cd },
118
+ { "mediumorchid", 0xba55d3 },
119
+ { "mediumpurple", 0x9370d8 },
120
+ { "mediumseagreen", 0x3cb371 },
121
+ { "mediumslateblue", 0x7b68ee },
122
+ { "mediumspringgreen", 0x00fa9a },
123
+ { "mediumturquoise", 0x48d1cc },
124
+ { "mediumvioletred", 0xc71585 },
125
+ { "midnightblue", 0x191970 },
126
+ { "mintcream", 0xf5fffa },
127
+ { "mistyrose", 0xffe4e1 },
128
+ { "moccasin", 0xffe4b5 },
129
+ { "navajowhite", 0xffdead },
130
+ { "navy", 0x000080 },
131
+ { "oldlace", 0xfdf5e6 },
132
+ { "olive", 0x808000 },
133
+ { "olivedrab", 0x6b8e23 },
134
+ { "orange", 0xffa500 },
135
+ { "orangered", 0xff4500 },
136
+ { "orchid", 0xda70d6 },
137
+ { "palegoldenrod", 0xeee8aa },
138
+ { "palegreen", 0x98fb98 },
139
+ { "paleturquoise", 0xafeeee },
140
+ { "palevioletred", 0xd87093 },
141
+ { "papayawhip", 0xffefd5 },
142
+ { "peachpuff", 0xffdab9 },
143
+ { "peru", 0xcd853f },
144
+ { "pink", 0xffc0cb },
145
+ { "plum", 0xdda0dd },
146
+ { "powderblue", 0xb0e0e6 },
147
+ { "purple", 0x800080 },
148
+ { "red", 0xff0000 },
149
+ { "rosybrown", 0xbc8f8f },
150
+ { "royalblue", 0x4169e1 },
151
+ { "saddlebrown", 0x8b4513 },
152
+ { "salmon", 0xfa8072 },
153
+ { "sandybrown", 0xf4a460 },
154
+ { "seagreen", 0x2e8b57 },
155
+ { "seashell", 0xfff5ee },
156
+ { "sienna", 0xa0522d },
157
+ { "silver", 0xc0c0c0 },
158
+ { "skyblue", 0x87ceeb },
159
+ { "slateblue", 0x6a5acd },
160
+ { "slategray", 0x708090 },
161
+ { "snow", 0xfffafa },
162
+ { "springgreen", 0x00ff7f },
163
+ { "steelblue", 0x4682b4 },
164
+ { "tan", 0xd2b48c },
165
+ { "teal", 0x008080 },
166
+ { "thistle", 0xd8bfd8 },
167
+ { "tomato", 0xff6347 },
168
+ { "turquoise", 0x40e0d0 },
169
+ { "violet", 0xee82ee },
170
+ { "wheat", 0xf5deb3 },
171
+ { "white", 0xffffff },
172
+ { "whitesmoke", 0xf5f5f5 },
173
+ { "yellow", 0xffff00 },
174
+ { "yellowgreen", 0x9acd32 },
175
+};
176
+
177
+/*
178
+** Attempt to translate a CSS color name into an integer that
179
+** represents the equivalent RGB value. Ignore alpha if provided.
180
+** If the name cannot be translated, return -1.
181
+*/
182
+int color_name_to_rgb(const char *zName){
183
+ if( zName==0 || zName[0]==0 ) return -1;
184
+ if( zName[0]=='#' ){
185
+ int i, v = 0;
186
+ for(i=1; i<=6 && fossil_isxdigit(zName[i]); i++){
187
+ v = v*16 + fossil_hexvalue(zName[i]);
188
+ }
189
+ if( i==4 ){
190
+ v = fossil_hexvalue(zName[1])*0x110000 +
191
+ fossil_hexvalue(zName[2])*0x1100 +
192
+ fossil_hexvalue(zName[3])*0x11;
193
+ return v;
194
+ }
195
+ if( i==7 ){
196
+ return v;
197
+ }
198
+ return -1;
199
+ }else{
200
+ int iMin = 0;
201
+ int iMax = count(aCssColors)-1;
202
+ while( iMin<=iMax ){
203
+ int iMid = (iMin+iMax)/2;
204
+ int c = sqlite3_stricmp(aCssColors[iMid].zName, zName);
205
+ if( c==0 ) return aCssColors[iMid].iRGB;
206
+ if( c<0 ){
207
+ iMin = iMid+1;
208
+ }else{
209
+ iMax = iMid-1;
210
+ }
211
+ }
212
+ return -1;
213
+ }
214
+}
215
+
216
+/*
217
+** SETTING: raw-bgcolor boolean default=off
218
+**
219
+** Fossil usually tries to adjust user-specified background colors
220
+** for checkins so that the text is readable and so that the color
221
+** is not too garish. This setting disables that filter. When
222
+** this setting is on, the user-selected background colors are shown
223
+** exactly as requested.
224
+*/
225
+
226
+/*
227
+** Shift a color provided by the user so that it is suitable
228
+** for use as a background color in the current skin.
229
+**
230
+** The return value is a #HHHHHH color name contained in
231
+** static space that is overwritten on the next call.
232
+**
233
+** If we cannot make sense of the background color recommendation
234
+** that is the input, then return NULL.
235
+**
236
+** The iFgClr parameter is normally 0. But for testing purposes, set
237
+** it to 1 for a black foregrounds and 2 for a white foreground.
238
+*/
239
+const char *reasonable_bg_color(const char *zRequested, int iFgClr){
240
+ int iRGB = color_name_to_rgb(zRequested);
241
+ int r, g, b; /* RGB components of requested color */
242
+ static int systemFg = 0; /* 1==black-foreground 2==white-foreground */
243
+ int fg; /* Foreground color to actually use */
244
+ static char zColor[10]; /* Return value */
245
+
246
+ if( iFgClr ){
247
+ fg = iFgClr;
248
+ }else if( systemFg==0 ){
249
+ if( db_get_boolean("raw-bgcolor",0) ){
250
+ fg = systemFg = 3;
251
+ }else{
252
+ fg = systemFg = skin_detail_boolean("white-foreground") ? 2 : 1;
253
+ }
254
+ }else{
255
+ fg = systemFg;
256
+ }
257
+ if( fg>=3 ) return zRequested;
258
+
259
+ if( iRGB<0 ) return 0;
260
+ r = (iRGB>>16) & 0xff;
261
+ g = (iRGB>>8) & 0xff;
262
+ b = iRGB & 0xff;
263
+ if( fg==1 ){
264
+ const int K = 70;
265
+ r = (K*r)/255 + (255-K);
266
+ g = (K*g)/255 + (255-K);
267
+ b = (K*b)/255 + (255-K);
268
+ }else{
269
+ const int K = 90;
270
+ r = (K*r)/255;
271
+ g = (K*g)/255;
272
+ b = (K*b)/255;
273
+ }
274
+ sqlite3_snprintf(8, zColor, "#%02x%02x%02x", r,g,b);
275
+ return zColor;
276
+}
25277
26278
/*
27279
** Compute a hash on a branch or user name
28280
*/
29281
static unsigned int hash_of_name(const char *z){
@@ -185,5 +437,86 @@
185437
@ <input type="submit" value="Submit">
186438
@ <input type="submit" name="rand" value="Random">
187439
@ </form>
188440
style_finish_page();
189441
}
442
+
443
+/*
444
+** WEBPAGE: test-bgcolor
445
+**
446
+** Show how user-specified background colors will be rendered
447
+** using the reasonable_bg_color() algorithm.
448
+*/
449
+void test_bgcolor_page(void){
450
+ const char *zReq; /* Requested color name */
451
+ const char *zBG; /* Actual color provided */
452
+ const char *zBg1;
453
+ char zNm[10];
454
+ static const char *azDflt[] = {
455
+ "red", "orange", "yellow", "green", "blue", "indigo", "violet",
456
+ "tan", "brown", "gray"
457
+ };
458
+ int i, cnt, iClr, r, g, b;
459
+ char *zFg;
460
+ login_check_credentials();
461
+ style_set_current_feature("test");
462
+ style_header("Background Color Test");
463
+ for(i=cnt=0; i<10; i++){
464
+ sqlite3_snprintf(sizeof(zNm),zNm,"b%d",i);
465
+ zReq = PD(zNm,azDflt[i]);
466
+ if( zReq==0 || zReq[0]==0 ) continue;
467
+ if( cnt==0 ){
468
+ @ <table border="1" cellspacing="0" cellpadding="10">
469
+ @ <tr>
470
+ @ <th>Requested Background
471
+ @ <th>Light mode
472
+ @ <th>Dark mode
473
+ @ </tr>
474
+ }
475
+ cnt++;
476
+ zBG = reasonable_bg_color(zReq, 0);
477
+ if( zBG==0 ){
478
+ @ <tr><td colspan="3" align="center">\
479
+ @ "%h(zReq)" is not a recognized color name</td></tr>
480
+ continue;
481
+ }
482
+ iClr = color_name_to_rgb(zReq);
483
+ r = (iClr>>16) & 0xff;
484
+ g = (iClr>>8) & 0xff;
485
+ b = iClr & 0xff;
486
+ if( 3*r + 7*g + b > 6*255 ){
487
+ zFg = "black";
488
+ }else{
489
+ zFg = "white";
490
+ }
491
+ if( zReq[0]!='#' ){
492
+ char zReqRGB[12];
493
+ sqlite3_snprintf(sizeof(zReqRGB),zReqRGB,"#%06x",color_name_to_rgb(zReq));
494
+ @ <tr><td style='color:%h(zFg);background-color:%h(zReq);'>\
495
+ @ Requested color "%h(zReq)" (%h(zReqRGB))</td>
496
+ }else{
497
+ @ <tr><td style='color:%h(zFg);background-color:%s(zReq);'>\
498
+ @ Requested color "%h(zReq)"</td>
499
+ }
500
+ zBg1 = reasonable_bg_color(zReq,1);
501
+ @ <td style='color:black;background-color:%h(zBg1);'>\
502
+ @ Background color for dark text: %h(zBg1)</td>
503
+ zBg1 = reasonable_bg_color(zReq,2);
504
+ @ <td style='color:white;background-color:%h(zBg1);'>\
505
+ @ Background color for light text: %h(zBg1)</td></tr>
506
+ }
507
+ if( cnt ){
508
+ @ </table>
509
+ @ <hr>
510
+ }
511
+ @ <form method="POST">
512
+ @ <p>Enter CSS color names below and see them shifted into corresponding
513
+ @ background colors above.</p>
514
+ for(i=0; i<10; i++){
515
+ sqlite3_snprintf(sizeof(zNm),zNm,"b%d",i);
516
+ @ <input type="text" size="30" name='%s(zNm)' \
517
+ @ value='%h(PD(zNm,azDflt[i]))'><br>
518
+ }
519
+ @ <input type="submit" value="Submit">
520
+ @ </form>
521
+ style_finish_page();
522
+}
190523
--- src/color.c
+++ src/color.c
@@ -20,10 +20,262 @@
20 **
21 */
22 #include "config.h"
23 #include <string.h>
24 #include "color.h"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
26 /*
27 ** Compute a hash on a branch or user name
28 */
29 static unsigned int hash_of_name(const char *z){
@@ -185,5 +437,86 @@
185 @ <input type="submit" value="Submit">
186 @ <input type="submit" name="rand" value="Random">
187 @ </form>
188 style_finish_page();
189 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
--- src/color.c
+++ src/color.c
@@ -20,10 +20,262 @@
20 **
21 */
22 #include "config.h"
23 #include <string.h>
24 #include "color.h"
25
26 /*
27 ** 140 standard CSS color names and their corresponding RGB values,
28 ** in alphabetical order by name so that we can do a binary search
29 ** for lookup.
30 */
31 static const struct CssColors {
32 const char *zName; /* CSS Color name, lower case */
33 unsigned int iRGB; /* Corresponding RGB value */
34 } aCssColors[] = {
35 { "aliceblue", 0xf0f8ff },
36 { "antiquewhite", 0xfaebd7 },
37 { "aqua", 0x00ffff },
38 { "aquamarine", 0x7fffd4 },
39 { "azure", 0xf0ffff },
40 { "beige", 0xf5f5dc },
41 { "bisque", 0xffe4c4 },
42 { "black", 0x000000 },
43 { "blanchedalmond", 0xffebcd },
44 { "blue", 0x0000ff },
45 { "blueviolet", 0x8a2be2 },
46 { "brown", 0xa52a2a },
47 { "burlywood", 0xdeb887 },
48 { "cadetblue", 0x5f9ea0 },
49 { "chartreuse", 0x7fff00 },
50 { "chocolate", 0xd2691e },
51 { "coral", 0xff7f50 },
52 { "cornflowerblue", 0x6495ed },
53 { "cornsilk", 0xfff8dc },
54 { "crimson", 0xdc143c },
55 { "cyan", 0x00ffff },
56 { "darkblue", 0x00008b },
57 { "darkcyan", 0x008b8b },
58 { "darkgoldenrod", 0xb8860b },
59 { "darkgray", 0xa9a9a9 },
60 { "darkgreen", 0x006400 },
61 { "darkkhaki", 0xbdb76b },
62 { "darkmagenta", 0x8b008b },
63 { "darkolivegreen", 0x556b2f },
64 { "darkorange", 0xff8c00 },
65 { "darkorchid", 0x9932cc },
66 { "darkred", 0x8b0000 },
67 { "darksalmon", 0xe9967a },
68 { "darkseagreen", 0x8fbc8f },
69 { "darkslateblue", 0x483d8b },
70 { "darkslategray", 0x2f4f4f },
71 { "darkturquoise", 0x00ced1 },
72 { "darkviolet", 0x9400d3 },
73 { "deeppink", 0xff1493 },
74 { "deepskyblue", 0x00bfff },
75 { "dimgray", 0x696969 },
76 { "dodgerblue", 0x1e90ff },
77 { "firebrick", 0xb22222 },
78 { "floralwhite", 0xfffaf0 },
79 { "forestgreen", 0x228b22 },
80 { "fuchsia", 0xff00ff },
81 { "gainsboro", 0xdcdcdc },
82 { "ghostwhite", 0xf8f8ff },
83 { "gold", 0xffd700 },
84 { "goldenrod", 0xdaa520 },
85 { "gray", 0x808080 },
86 { "green", 0x008000 },
87 { "greenyellow", 0xadff2f },
88 { "honeydew", 0xf0fff0 },
89 { "hotpink", 0xff69b4 },
90 { "indianred", 0xcd5c5c },
91 { "indigo", 0x4b0082 },
92 { "ivory", 0xfffff0 },
93 { "khaki", 0xf0e68c },
94 { "lavender", 0xe6e6fa },
95 { "lavenderblush", 0xfff0f5 },
96 { "lawngreen", 0x7cfc00 },
97 { "lemonchiffon", 0xfffacd },
98 { "lightblue", 0xadd8e6 },
99 { "lightcoral", 0xf08080 },
100 { "lightcyan", 0xe0ffff },
101 { "lightgoldenrodyellow", 0xfafad2 },
102 { "lightgrey", 0xd3d3d3 },
103 { "lightgreen", 0x90ee90 },
104 { "lightpink", 0xffb6c1 },
105 { "lightsalmon", 0xffa07a },
106 { "lightseagreen", 0x20b2aa },
107 { "lightskyblue", 0x87cefa },
108 { "lightslategray", 0x778899 },
109 { "lightsteelblue", 0xb0c4de },
110 { "lightyellow", 0xffffe0 },
111 { "lime", 0x00ff00 },
112 { "limegreen", 0x32cd32 },
113 { "linen", 0xfaf0e6 },
114 { "magenta", 0xff00ff },
115 { "maroon", 0x800000 },
116 { "mediumaquamarine", 0x66cdaa },
117 { "mediumblue", 0x0000cd },
118 { "mediumorchid", 0xba55d3 },
119 { "mediumpurple", 0x9370d8 },
120 { "mediumseagreen", 0x3cb371 },
121 { "mediumslateblue", 0x7b68ee },
122 { "mediumspringgreen", 0x00fa9a },
123 { "mediumturquoise", 0x48d1cc },
124 { "mediumvioletred", 0xc71585 },
125 { "midnightblue", 0x191970 },
126 { "mintcream", 0xf5fffa },
127 { "mistyrose", 0xffe4e1 },
128 { "moccasin", 0xffe4b5 },
129 { "navajowhite", 0xffdead },
130 { "navy", 0x000080 },
131 { "oldlace", 0xfdf5e6 },
132 { "olive", 0x808000 },
133 { "olivedrab", 0x6b8e23 },
134 { "orange", 0xffa500 },
135 { "orangered", 0xff4500 },
136 { "orchid", 0xda70d6 },
137 { "palegoldenrod", 0xeee8aa },
138 { "palegreen", 0x98fb98 },
139 { "paleturquoise", 0xafeeee },
140 { "palevioletred", 0xd87093 },
141 { "papayawhip", 0xffefd5 },
142 { "peachpuff", 0xffdab9 },
143 { "peru", 0xcd853f },
144 { "pink", 0xffc0cb },
145 { "plum", 0xdda0dd },
146 { "powderblue", 0xb0e0e6 },
147 { "purple", 0x800080 },
148 { "red", 0xff0000 },
149 { "rosybrown", 0xbc8f8f },
150 { "royalblue", 0x4169e1 },
151 { "saddlebrown", 0x8b4513 },
152 { "salmon", 0xfa8072 },
153 { "sandybrown", 0xf4a460 },
154 { "seagreen", 0x2e8b57 },
155 { "seashell", 0xfff5ee },
156 { "sienna", 0xa0522d },
157 { "silver", 0xc0c0c0 },
158 { "skyblue", 0x87ceeb },
159 { "slateblue", 0x6a5acd },
160 { "slategray", 0x708090 },
161 { "snow", 0xfffafa },
162 { "springgreen", 0x00ff7f },
163 { "steelblue", 0x4682b4 },
164 { "tan", 0xd2b48c },
165 { "teal", 0x008080 },
166 { "thistle", 0xd8bfd8 },
167 { "tomato", 0xff6347 },
168 { "turquoise", 0x40e0d0 },
169 { "violet", 0xee82ee },
170 { "wheat", 0xf5deb3 },
171 { "white", 0xffffff },
172 { "whitesmoke", 0xf5f5f5 },
173 { "yellow", 0xffff00 },
174 { "yellowgreen", 0x9acd32 },
175 };
176
177 /*
178 ** Attempt to translate a CSS color name into an integer that
179 ** represents the equivalent RGB value. Ignore alpha if provided.
180 ** If the name cannot be translated, return -1.
181 */
182 int color_name_to_rgb(const char *zName){
183 if( zName==0 || zName[0]==0 ) return -1;
184 if( zName[0]=='#' ){
185 int i, v = 0;
186 for(i=1; i<=6 && fossil_isxdigit(zName[i]); i++){
187 v = v*16 + fossil_hexvalue(zName[i]);
188 }
189 if( i==4 ){
190 v = fossil_hexvalue(zName[1])*0x110000 +
191 fossil_hexvalue(zName[2])*0x1100 +
192 fossil_hexvalue(zName[3])*0x11;
193 return v;
194 }
195 if( i==7 ){
196 return v;
197 }
198 return -1;
199 }else{
200 int iMin = 0;
201 int iMax = count(aCssColors)-1;
202 while( iMin<=iMax ){
203 int iMid = (iMin+iMax)/2;
204 int c = sqlite3_stricmp(aCssColors[iMid].zName, zName);
205 if( c==0 ) return aCssColors[iMid].iRGB;
206 if( c<0 ){
207 iMin = iMid+1;
208 }else{
209 iMax = iMid-1;
210 }
211 }
212 return -1;
213 }
214 }
215
216 /*
217 ** SETTING: raw-bgcolor boolean default=off
218 **
219 ** Fossil usually tries to adjust user-specified background colors
220 ** for checkins so that the text is readable and so that the color
221 ** is not too garish. This setting disables that filter. When
222 ** this setting is on, the user-selected background colors are shown
223 ** exactly as requested.
224 */
225
226 /*
227 ** Shift a color provided by the user so that it is suitable
228 ** for use as a background color in the current skin.
229 **
230 ** The return value is a #HHHHHH color name contained in
231 ** static space that is overwritten on the next call.
232 **
233 ** If we cannot make sense of the background color recommendation
234 ** that is the input, then return NULL.
235 **
236 ** The iFgClr parameter is normally 0. But for testing purposes, set
237 ** it to 1 for a black foregrounds and 2 for a white foreground.
238 */
239 const char *reasonable_bg_color(const char *zRequested, int iFgClr){
240 int iRGB = color_name_to_rgb(zRequested);
241 int r, g, b; /* RGB components of requested color */
242 static int systemFg = 0; /* 1==black-foreground 2==white-foreground */
243 int fg; /* Foreground color to actually use */
244 static char zColor[10]; /* Return value */
245
246 if( iFgClr ){
247 fg = iFgClr;
248 }else if( systemFg==0 ){
249 if( db_get_boolean("raw-bgcolor",0) ){
250 fg = systemFg = 3;
251 }else{
252 fg = systemFg = skin_detail_boolean("white-foreground") ? 2 : 1;
253 }
254 }else{
255 fg = systemFg;
256 }
257 if( fg>=3 ) return zRequested;
258
259 if( iRGB<0 ) return 0;
260 r = (iRGB>>16) & 0xff;
261 g = (iRGB>>8) & 0xff;
262 b = iRGB & 0xff;
263 if( fg==1 ){
264 const int K = 70;
265 r = (K*r)/255 + (255-K);
266 g = (K*g)/255 + (255-K);
267 b = (K*b)/255 + (255-K);
268 }else{
269 const int K = 90;
270 r = (K*r)/255;
271 g = (K*g)/255;
272 b = (K*b)/255;
273 }
274 sqlite3_snprintf(8, zColor, "#%02x%02x%02x", r,g,b);
275 return zColor;
276 }
277
278 /*
279 ** Compute a hash on a branch or user name
280 */
281 static unsigned int hash_of_name(const char *z){
@@ -185,5 +437,86 @@
437 @ <input type="submit" value="Submit">
438 @ <input type="submit" name="rand" value="Random">
439 @ </form>
440 style_finish_page();
441 }
442
443 /*
444 ** WEBPAGE: test-bgcolor
445 **
446 ** Show how user-specified background colors will be rendered
447 ** using the reasonable_bg_color() algorithm.
448 */
449 void test_bgcolor_page(void){
450 const char *zReq; /* Requested color name */
451 const char *zBG; /* Actual color provided */
452 const char *zBg1;
453 char zNm[10];
454 static const char *azDflt[] = {
455 "red", "orange", "yellow", "green", "blue", "indigo", "violet",
456 "tan", "brown", "gray"
457 };
458 int i, cnt, iClr, r, g, b;
459 char *zFg;
460 login_check_credentials();
461 style_set_current_feature("test");
462 style_header("Background Color Test");
463 for(i=cnt=0; i<10; i++){
464 sqlite3_snprintf(sizeof(zNm),zNm,"b%d",i);
465 zReq = PD(zNm,azDflt[i]);
466 if( zReq==0 || zReq[0]==0 ) continue;
467 if( cnt==0 ){
468 @ <table border="1" cellspacing="0" cellpadding="10">
469 @ <tr>
470 @ <th>Requested Background
471 @ <th>Light mode
472 @ <th>Dark mode
473 @ </tr>
474 }
475 cnt++;
476 zBG = reasonable_bg_color(zReq, 0);
477 if( zBG==0 ){
478 @ <tr><td colspan="3" align="center">\
479 @ "%h(zReq)" is not a recognized color name</td></tr>
480 continue;
481 }
482 iClr = color_name_to_rgb(zReq);
483 r = (iClr>>16) & 0xff;
484 g = (iClr>>8) & 0xff;
485 b = iClr & 0xff;
486 if( 3*r + 7*g + b > 6*255 ){
487 zFg = "black";
488 }else{
489 zFg = "white";
490 }
491 if( zReq[0]!='#' ){
492 char zReqRGB[12];
493 sqlite3_snprintf(sizeof(zReqRGB),zReqRGB,"#%06x",color_name_to_rgb(zReq));
494 @ <tr><td style='color:%h(zFg);background-color:%h(zReq);'>\
495 @ Requested color "%h(zReq)" (%h(zReqRGB))</td>
496 }else{
497 @ <tr><td style='color:%h(zFg);background-color:%s(zReq);'>\
498 @ Requested color "%h(zReq)"</td>
499 }
500 zBg1 = reasonable_bg_color(zReq,1);
501 @ <td style='color:black;background-color:%h(zBg1);'>\
502 @ Background color for dark text: %h(zBg1)</td>
503 zBg1 = reasonable_bg_color(zReq,2);
504 @ <td style='color:white;background-color:%h(zBg1);'>\
505 @ Background color for light text: %h(zBg1)</td></tr>
506 }
507 if( cnt ){
508 @ </table>
509 @ <hr>
510 }
511 @ <form method="POST">
512 @ <p>Enter CSS color names below and see them shifted into corresponding
513 @ background colors above.</p>
514 for(i=0; i<10; i++){
515 sqlite3_snprintf(sizeof(zNm),zNm,"b%d",i);
516 @ <input type="text" size="30" name='%s(zNm)' \
517 @ value='%h(PD(zNm,azDflt[i]))'><br>
518 }
519 @ <input type="submit" value="Submit">
520 @ </form>
521 style_finish_page();
522 }
523
+4 -4
--- src/comformat.c
+++ src/comformat.c
@@ -266,14 +266,14 @@
266266
int i = 0; /* Counted bytes. */
267267
int cchUTF8 = 1; /* Code units consumed. */
268268
int maxUTF8 = 1; /* Expected sequence length. */
269269
char c = z[i++];
270270
if( c==0x1b && z[i]=='[' ){
271
- do{
272
- i++;
273
- }while( i<fossil_isdigit(z[i]) || z[i]==';' );
274
- if( fossil_isalpha(z[i]) ){
271
+ i++;
272
+ while( z[i]>=0x30 && z[i]<=0x3f ){ i++; }
273
+ while( z[i]>=0x20 && z[i]<=0x2f ){ i++; }
274
+ if( z[i]>=0x40 && z[i]<=0x7e ){
275275
*pCchUTF8 = i+1;
276276
*pUtf32 = 0x301; /* A zero-width character */
277277
return;
278278
}
279279
}
280280
--- src/comformat.c
+++ src/comformat.c
@@ -266,14 +266,14 @@
266 int i = 0; /* Counted bytes. */
267 int cchUTF8 = 1; /* Code units consumed. */
268 int maxUTF8 = 1; /* Expected sequence length. */
269 char c = z[i++];
270 if( c==0x1b && z[i]=='[' ){
271 do{
272 i++;
273 }while( i<fossil_isdigit(z[i]) || z[i]==';' );
274 if( fossil_isalpha(z[i]) ){
275 *pCchUTF8 = i+1;
276 *pUtf32 = 0x301; /* A zero-width character */
277 return;
278 }
279 }
280
--- src/comformat.c
+++ src/comformat.c
@@ -266,14 +266,14 @@
266 int i = 0; /* Counted bytes. */
267 int cchUTF8 = 1; /* Code units consumed. */
268 int maxUTF8 = 1; /* Expected sequence length. */
269 char c = z[i++];
270 if( c==0x1b && z[i]=='[' ){
271 i++;
272 while( z[i]>=0x30 && z[i]<=0x3f ){ i++; }
273 while( z[i]>=0x20 && z[i]<=0x2f ){ i++; }
274 if( z[i]>=0x40 && z[i]<=0x7e ){
275 *pCchUTF8 = i+1;
276 *pUtf32 = 0x301; /* A zero-width character */
277 return;
278 }
279 }
280
--- src/configure.c
+++ src/configure.c
@@ -100,11 +100,10 @@
100100
{ "logo-image", CONFIGSET_SKIN },
101101
{ "background-mimetype", CONFIGSET_SKIN },
102102
{ "background-image", CONFIGSET_SKIN },
103103
{ "icon-mimetype", CONFIGSET_SKIN },
104104
{ "icon-image", CONFIGSET_SKIN },
105
- { "timeline-block-markup", CONFIGSET_SKIN },
106105
{ "timeline-date-format", CONFIGSET_SKIN },
107106
{ "timeline-default-style", CONFIGSET_SKIN },
108107
{ "timeline-dwelltime", CONFIGSET_SKIN },
109108
{ "timeline-closetime", CONFIGSET_SKIN },
110109
{ "timeline-hard-newlines", CONFIGSET_SKIN },
111110
--- src/configure.c
+++ src/configure.c
@@ -100,11 +100,10 @@
100 { "logo-image", CONFIGSET_SKIN },
101 { "background-mimetype", CONFIGSET_SKIN },
102 { "background-image", CONFIGSET_SKIN },
103 { "icon-mimetype", CONFIGSET_SKIN },
104 { "icon-image", CONFIGSET_SKIN },
105 { "timeline-block-markup", CONFIGSET_SKIN },
106 { "timeline-date-format", CONFIGSET_SKIN },
107 { "timeline-default-style", CONFIGSET_SKIN },
108 { "timeline-dwelltime", CONFIGSET_SKIN },
109 { "timeline-closetime", CONFIGSET_SKIN },
110 { "timeline-hard-newlines", CONFIGSET_SKIN },
111
--- src/configure.c
+++ src/configure.c
@@ -100,11 +100,10 @@
100 { "logo-image", CONFIGSET_SKIN },
101 { "background-mimetype", CONFIGSET_SKIN },
102 { "background-image", CONFIGSET_SKIN },
103 { "icon-mimetype", CONFIGSET_SKIN },
104 { "icon-image", CONFIGSET_SKIN },
 
105 { "timeline-date-format", CONFIGSET_SKIN },
106 { "timeline-default-style", CONFIGSET_SKIN },
107 { "timeline-dwelltime", CONFIGSET_SKIN },
108 { "timeline-closetime", CONFIGSET_SKIN },
109 { "timeline-hard-newlines", CONFIGSET_SKIN },
110
+4 -1
--- src/db.c
+++ src/db.c
@@ -1629,10 +1629,12 @@
16291629
sqlite3_create_function(db, "chat_msg_from_event", 4,
16301630
SQLITE_UTF8 | SQLITE_INNOCUOUS, 0,
16311631
chat_msg_from_event, 0, 0);
16321632
sqlite3_create_function(db, "inode", 1, SQLITE_UTF8, 0,
16331633
file_inode_sql_func,0,0);
1634
+ sqlite3_create_function(db, "artifact_to_json", 1, SQLITE_UTF8, 0,
1635
+ artifact_to_json_sql_func,0,0);
16341636
16351637
}
16361638
16371639
#if USE_SEE
16381640
/*
@@ -5067,11 +5069,12 @@
50675069
**
50685070
** All repositories are searched (in lexicographical order) and the first
50695071
** repository with a non-zero "repolist-skin" value is used as the skin
50705072
** for the repository list page. If none of the repositories on the list
50715073
** have a non-zero "repolist-skin" setting then the repository list is
5072
-** displayed using unadorned HTML ("skinless").
5074
+** displayed using unadorned HTML ("skinless"), with the page title taken
5075
+** from the FOSSIL_REPOLIST_TITLE environment variable.
50735076
**
50745077
** If repolist-skin has a value of 2, then the repository is omitted from
50755078
** the list in use cases 1 through 4, but not for 5 and 6.
50765079
*/
50775080
/*
50785081
--- src/db.c
+++ src/db.c
@@ -1629,10 +1629,12 @@
1629 sqlite3_create_function(db, "chat_msg_from_event", 4,
1630 SQLITE_UTF8 | SQLITE_INNOCUOUS, 0,
1631 chat_msg_from_event, 0, 0);
1632 sqlite3_create_function(db, "inode", 1, SQLITE_UTF8, 0,
1633 file_inode_sql_func,0,0);
 
 
1634
1635 }
1636
1637 #if USE_SEE
1638 /*
@@ -5067,11 +5069,12 @@
5067 **
5068 ** All repositories are searched (in lexicographical order) and the first
5069 ** repository with a non-zero "repolist-skin" value is used as the skin
5070 ** for the repository list page. If none of the repositories on the list
5071 ** have a non-zero "repolist-skin" setting then the repository list is
5072 ** displayed using unadorned HTML ("skinless").
 
5073 **
5074 ** If repolist-skin has a value of 2, then the repository is omitted from
5075 ** the list in use cases 1 through 4, but not for 5 and 6.
5076 */
5077 /*
5078
--- src/db.c
+++ src/db.c
@@ -1629,10 +1629,12 @@
1629 sqlite3_create_function(db, "chat_msg_from_event", 4,
1630 SQLITE_UTF8 | SQLITE_INNOCUOUS, 0,
1631 chat_msg_from_event, 0, 0);
1632 sqlite3_create_function(db, "inode", 1, SQLITE_UTF8, 0,
1633 file_inode_sql_func,0,0);
1634 sqlite3_create_function(db, "artifact_to_json", 1, SQLITE_UTF8, 0,
1635 artifact_to_json_sql_func,0,0);
1636
1637 }
1638
1639 #if USE_SEE
1640 /*
@@ -5067,11 +5069,12 @@
5069 **
5070 ** All repositories are searched (in lexicographical order) and the first
5071 ** repository with a non-zero "repolist-skin" value is used as the skin
5072 ** for the repository list page. If none of the repositories on the list
5073 ** have a non-zero "repolist-skin" setting then the repository list is
5074 ** displayed using unadorned HTML ("skinless"), with the page title taken
5075 ** from the FOSSIL_REPOLIST_TITLE environment variable.
5076 **
5077 ** If repolist-skin has a value of 2, then the repository is omitted from
5078 ** the list in use cases 1 through 4, but not for 5 and 6.
5079 */
5080 /*
5081
+1 -1
--- src/doc.c
+++ src/doc.c
@@ -639,11 +639,11 @@
639639
** are special to HTML encoded. We need to decode these before turning
640640
** the text into a title, as the title text will be reencoded later */
641641
char *zTitle = mprintf("%.*s", nValue, zValue);
642642
int i;
643643
for(i=0; fossil_isspace(zTitle[i]); i++){}
644
- html_to_plaintext(zTitle+i, pTitle);
644
+ html_to_plaintext(zTitle+i, pTitle, 0);
645645
fossil_free(zTitle);
646646
seenTitle = 1;
647647
if( seenClass ) return 1;
648648
}
649649
}
650650
--- src/doc.c
+++ src/doc.c
@@ -639,11 +639,11 @@
639 ** are special to HTML encoded. We need to decode these before turning
640 ** the text into a title, as the title text will be reencoded later */
641 char *zTitle = mprintf("%.*s", nValue, zValue);
642 int i;
643 for(i=0; fossil_isspace(zTitle[i]); i++){}
644 html_to_plaintext(zTitle+i, pTitle);
645 fossil_free(zTitle);
646 seenTitle = 1;
647 if( seenClass ) return 1;
648 }
649 }
650
--- src/doc.c
+++ src/doc.c
@@ -639,11 +639,11 @@
639 ** are special to HTML encoded. We need to decode these before turning
640 ** the text into a title, as the title text will be reencoded later */
641 char *zTitle = mprintf("%.*s", nValue, zValue);
642 int i;
643 for(i=0; fossil_isspace(zTitle[i]); i++){}
644 html_to_plaintext(zTitle+i, pTitle, 0);
645 fossil_free(zTitle);
646 seenTitle = 1;
647 if( seenClass ) return 1;
648 }
649 }
650
+3 -3
--- src/encode.c
+++ src/encode.c
@@ -244,11 +244,11 @@
244244
}
245245
246246
/*
247247
** Convert a single HEX digit to an integer
248248
*/
249
-static int AsciiToHex(int c){
249
+int fossil_hexvalue(int c){
250250
if( c>='a' && c<='f' ){
251251
c += 10 - 'a';
252252
}else if( c>='A' && c<='F' ){
253253
c += 10 - 'A';
254254
}else if( c>='0' && c<='9' ){
@@ -272,12 +272,12 @@
272272
i = j = 0;
273273
while( z[i] ){
274274
switch( z[i] ){
275275
case '%':
276276
if( z[i+1] && z[i+2] ){
277
- z[j] = AsciiToHex(z[i+1]) << 4;
278
- z[j] |= AsciiToHex(z[i+2]);
277
+ z[j] = fossil_hexvalue(z[i+1]) << 4;
278
+ z[j] |= fossil_hexvalue(z[i+2]);
279279
i += 2;
280280
}
281281
break;
282282
case '+':
283283
z[j] = ' ';
284284
--- src/encode.c
+++ src/encode.c
@@ -244,11 +244,11 @@
244 }
245
246 /*
247 ** Convert a single HEX digit to an integer
248 */
249 static int AsciiToHex(int c){
250 if( c>='a' && c<='f' ){
251 c += 10 - 'a';
252 }else if( c>='A' && c<='F' ){
253 c += 10 - 'A';
254 }else if( c>='0' && c<='9' ){
@@ -272,12 +272,12 @@
272 i = j = 0;
273 while( z[i] ){
274 switch( z[i] ){
275 case '%':
276 if( z[i+1] && z[i+2] ){
277 z[j] = AsciiToHex(z[i+1]) << 4;
278 z[j] |= AsciiToHex(z[i+2]);
279 i += 2;
280 }
281 break;
282 case '+':
283 z[j] = ' ';
284
--- src/encode.c
+++ src/encode.c
@@ -244,11 +244,11 @@
244 }
245
246 /*
247 ** Convert a single HEX digit to an integer
248 */
249 int fossil_hexvalue(int c){
250 if( c>='a' && c<='f' ){
251 c += 10 - 'a';
252 }else if( c>='A' && c<='F' ){
253 c += 10 - 'A';
254 }else if( c>='0' && c<='9' ){
@@ -272,12 +272,12 @@
272 i = j = 0;
273 while( z[i] ){
274 switch( z[i] ){
275 case '%':
276 if( z[i+1] && z[i+2] ){
277 z[j] = fossil_hexvalue(z[i+1]) << 4;
278 z[j] |= fossil_hexvalue(z[i+2]);
279 i += 2;
280 }
281 break;
282 case '+':
283 z[j] = ' ';
284
--- src/finfo.c
+++ src/finfo.c
@@ -636,10 +636,12 @@
636636
if( zBr==0 ) zBr = "trunk";
637637
if( uBg ){
638638
zBgClr = user_color(zUser);
639639
}else if( brBg || zBgClr==0 || zBgClr[0]==0 ){
640640
zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr);
641
+ }else if( zBgClr ){
642
+ zBgClr = reasonable_bg_color(zBgClr,0);
641643
}
642644
gidx = graph_add_row(pGraph,
643645
frid>0 ? (GraphRowId)frid*(mxfnid+1)+fnid : fpid+1000000000,
644646
nParent, 0, aParent, zBr, zBgClr,
645647
zUuid, 0);
646648
--- src/finfo.c
+++ src/finfo.c
@@ -636,10 +636,12 @@
636 if( zBr==0 ) zBr = "trunk";
637 if( uBg ){
638 zBgClr = user_color(zUser);
639 }else if( brBg || zBgClr==0 || zBgClr[0]==0 ){
640 zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr);
 
 
641 }
642 gidx = graph_add_row(pGraph,
643 frid>0 ? (GraphRowId)frid*(mxfnid+1)+fnid : fpid+1000000000,
644 nParent, 0, aParent, zBr, zBgClr,
645 zUuid, 0);
646
--- src/finfo.c
+++ src/finfo.c
@@ -636,10 +636,12 @@
636 if( zBr==0 ) zBr = "trunk";
637 if( uBg ){
638 zBgClr = user_color(zUser);
639 }else if( brBg || zBgClr==0 || zBgClr[0]==0 ){
640 zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr);
641 }else if( zBgClr ){
642 zBgClr = reasonable_bg_color(zBgClr,0);
643 }
644 gidx = graph_add_row(pGraph,
645 frid>0 ? (GraphRowId)frid*(mxfnid+1)+fnid : fpid+1000000000,
646 nParent, 0, aParent, zBr, zBgClr,
647 zUuid, 0);
648
+29 -11
--- src/graph.c
+++ src/graph.c
@@ -59,11 +59,11 @@
5959
** check-ins and many files. For this reason, we make the identifier
6060
** a 64-bit integer, to dramatically reduce the risk of an overflow.
6161
*/
6262
typedef sqlite3_int64 GraphRowId;
6363
64
-#define GR_MAX_RAIL 40 /* Max number of "rails" to display */
64
+#define GR_MAX_RAIL 64 /* Max number of "rails" to display */
6565
6666
/* The graph appears vertically beside a timeline. Each row in the
6767
** timeline corresponds to a row in the graph. GraphRow.idx is 0 for
6868
** the top-most row and increases moving down. Hence (in the absence of
6969
** time skew) parents have a larger index than their children.
@@ -118,10 +118,11 @@
118118
char **azBranch; /* Names of the branches */
119119
int nRow; /* Number of rows */
120120
int nHash; /* Number of slots in apHash[] */
121121
u8 hasOffsetMergeRiser; /* Merge arrow from leaf goes up on a different
122122
** rail that the node */
123
+ u8 bOverfull; /* Unable to allocate sufficient rails */
123124
u64 mergeRail; /* Rails used for merge lines */
124125
GraphRow **apHash; /* Hash table of GraphRow objects. Key: rid */
125126
u8 aiRailMap[GR_MAX_RAIL]; /* Mapping of rails to actually columns */
126127
};
127128
@@ -336,11 +337,18 @@
336337
iBestDist = dist;
337338
iBest = i;
338339
}
339340
}
340341
}
341
- if( iBestDist>1000 ) p->nErr++;
342
+ if( iBestDist>1000 ){
343
+ p->bOverfull = 1;
344
+ iBest = GR_MAX_RAIL;
345
+ }
346
+ if( iBest>GR_MAX_RAIL ){
347
+ p->bOverfull = 1;
348
+ iBest = GR_MAX_RAIL;
349
+ }
342350
if( iBest>p->mxRail ) p->mxRail = iBest;
343351
if( bMergeRail ) p->mergeRail |= BIT(iBest);
344352
return iBest;
345353
}
346354
@@ -709,11 +717,11 @@
709717
if( pRow->iRail>=0 ) continue;
710718
if( pRow->isDup ) continue;
711719
if( pRow->nParent<0 ) continue;
712720
if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){
713721
pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx+riserMargin,0,0);
714
- if( p->mxRail>=GR_MAX_RAIL ) return;
722
+ /* if( p->mxRail>=GR_MAX_RAIL ) return; */
715723
mask = BIT(pRow->iRail);
716724
if( !omitDescenders ){
717725
int n = RISER_MARGIN;
718726
pRow->bDescender = pRow->nParent>0;
719727
for(pLoop=pRow; pLoop && (n--)>0; pLoop=pLoop->pNext){
@@ -744,28 +752,38 @@
744752
assert( pRow->nParent>0 );
745753
parentRid = pRow->aParent[0];
746754
pParent = hashFind(p, parentRid);
747755
if( pParent==0 ){
748756
pRow->iRail = ++p->mxRail;
749
- if( p->mxRail>=GR_MAX_RAIL ) return;
757
+ if( p->mxRail>=GR_MAX_RAIL ){
758
+ pRow->iRail = p->mxRail = GR_MAX_RAIL;
759
+ p->bOverfull = 1;
760
+ }
750761
pRow->railInUse = BIT(pRow->iRail);
751762
continue;
752763
}
753764
if( pParent->idx>pRow->idx ){
754765
/* Common case: Child occurs after parent and is above the
755766
** parent in the timeline */
756767
pRow->iRail = findFreeRail(p, pRow->idxTop, pParent->idx,
757768
pParent->iRail, 0);
758
- if( p->mxRail>=GR_MAX_RAIL ) return;
769
+ /* if( p->mxRail>=GR_MAX_RAIL ) return; */
759770
pParent->aiRiser[pRow->iRail] = pRow->idx;
760771
}else{
761772
/* Timewarp case: Child occurs earlier in time than parent and
762773
** appears below the parent in the timeline. */
763774
int iDownRail = ++p->mxRail;
764775
if( iDownRail<1 ) iDownRail = ++p->mxRail;
776
+ if( p->mxRail>GR_MAX_RAIL ){
777
+ iDownRail = p->mxRail = GR_MAX_RAIL;
778
+ p->bOverfull = 1;
779
+ }
765780
pRow->iRail = ++p->mxRail;
766
- if( p->mxRail>=GR_MAX_RAIL ) return;
781
+ if( p->mxRail>=GR_MAX_RAIL ){
782
+ pRow->iRail = p->mxRail = GR_MAX_RAIL;
783
+ p->bOverfull = 1;
784
+ }
767785
pRow->railInUse = BIT(pRow->iRail);
768786
pParent->aiRiser[iDownRail] = pRow->idx;
769787
mask = BIT(iDownRail);
770788
for(pLoop=p->pFirst; pLoop; pLoop=pLoop->pNext){
771789
pLoop->railInUse |= mask;
@@ -824,11 +842,11 @@
824842
break;
825843
}
826844
}
827845
if( iMrail==-1 ){
828846
iMrail = findFreeRail(p, pRow->idx, p->pLast->idx, 0, 1);
829
- if( p->mxRail>=GR_MAX_RAIL ) return;
847
+ /*if( p->mxRail>=GR_MAX_RAIL ) return;*/
830848
mergeRiserFrom[iMrail] = parentRid;
831849
}
832850
iReuseIdx = p->nRow+1;
833851
iReuseRail = iMrail;
834852
mask = BIT(iMrail);
@@ -856,11 +874,11 @@
856874
pDesc->mergeUpto = pDesc->idx;
857875
}
858876
}else{
859877
/* Create a new merge for an on-screen node */
860878
createMergeRiser(p, pDesc, pRow, isCherrypick);
861
- if( p->mxRail>=GR_MAX_RAIL ) return;
879
+ /* if( p->mxRail>=GR_MAX_RAIL ) return; */
862880
if( iReuseIdx<0
863881
&& pDesc->nMergeChild==1
864882
&& (pDesc->iRail!=pDesc->mergeOut || pDesc->isLeaf)
865883
){
866884
iReuseIdx = pDesc->idx;
@@ -872,17 +890,17 @@
872890
}
873891
874892
/*
875893
** Insert merge rails from primaries to duplicates.
876894
*/
877
- if( hasDup ){
895
+ if( hasDup && p->mxRail<GR_MAX_RAIL ){
878896
int dupRail;
879897
int mxRail;
880898
find_max_rail(p);
881899
mxRail = p->mxRail;
882900
dupRail = mxRail+1;
883
- if( p->mxRail>=GR_MAX_RAIL ) return;
901
+ /* if( p->mxRail>=GR_MAX_RAIL ) return; */
884902
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
885903
if( !pRow->isDup ) continue;
886904
pRow->iRail = dupRail;
887905
pDesc = hashFind(p, pRow->rid);
888906
assert( pDesc!=0 && pDesc!=pRow );
@@ -893,11 +911,11 @@
893911
dupRail = mxRail+1;
894912
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
895913
if( pRow->isDup ) pRow->iRail = dupRail;
896914
}
897915
}
898
- if( mxRail>=GR_MAX_RAIL ) return;
916
+ /* if( mxRail>=GR_MAX_RAIL ) return; */
899917
}
900918
901919
/*
902920
** Find the maximum rail number.
903921
*/
904922
--- src/graph.c
+++ src/graph.c
@@ -59,11 +59,11 @@
59 ** check-ins and many files. For this reason, we make the identifier
60 ** a 64-bit integer, to dramatically reduce the risk of an overflow.
61 */
62 typedef sqlite3_int64 GraphRowId;
63
64 #define GR_MAX_RAIL 40 /* Max number of "rails" to display */
65
66 /* The graph appears vertically beside a timeline. Each row in the
67 ** timeline corresponds to a row in the graph. GraphRow.idx is 0 for
68 ** the top-most row and increases moving down. Hence (in the absence of
69 ** time skew) parents have a larger index than their children.
@@ -118,10 +118,11 @@
118 char **azBranch; /* Names of the branches */
119 int nRow; /* Number of rows */
120 int nHash; /* Number of slots in apHash[] */
121 u8 hasOffsetMergeRiser; /* Merge arrow from leaf goes up on a different
122 ** rail that the node */
 
123 u64 mergeRail; /* Rails used for merge lines */
124 GraphRow **apHash; /* Hash table of GraphRow objects. Key: rid */
125 u8 aiRailMap[GR_MAX_RAIL]; /* Mapping of rails to actually columns */
126 };
127
@@ -336,11 +337,18 @@
336 iBestDist = dist;
337 iBest = i;
338 }
339 }
340 }
341 if( iBestDist>1000 ) p->nErr++;
 
 
 
 
 
 
 
342 if( iBest>p->mxRail ) p->mxRail = iBest;
343 if( bMergeRail ) p->mergeRail |= BIT(iBest);
344 return iBest;
345 }
346
@@ -709,11 +717,11 @@
709 if( pRow->iRail>=0 ) continue;
710 if( pRow->isDup ) continue;
711 if( pRow->nParent<0 ) continue;
712 if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){
713 pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx+riserMargin,0,0);
714 if( p->mxRail>=GR_MAX_RAIL ) return;
715 mask = BIT(pRow->iRail);
716 if( !omitDescenders ){
717 int n = RISER_MARGIN;
718 pRow->bDescender = pRow->nParent>0;
719 for(pLoop=pRow; pLoop && (n--)>0; pLoop=pLoop->pNext){
@@ -744,28 +752,38 @@
744 assert( pRow->nParent>0 );
745 parentRid = pRow->aParent[0];
746 pParent = hashFind(p, parentRid);
747 if( pParent==0 ){
748 pRow->iRail = ++p->mxRail;
749 if( p->mxRail>=GR_MAX_RAIL ) return;
 
 
 
750 pRow->railInUse = BIT(pRow->iRail);
751 continue;
752 }
753 if( pParent->idx>pRow->idx ){
754 /* Common case: Child occurs after parent and is above the
755 ** parent in the timeline */
756 pRow->iRail = findFreeRail(p, pRow->idxTop, pParent->idx,
757 pParent->iRail, 0);
758 if( p->mxRail>=GR_MAX_RAIL ) return;
759 pParent->aiRiser[pRow->iRail] = pRow->idx;
760 }else{
761 /* Timewarp case: Child occurs earlier in time than parent and
762 ** appears below the parent in the timeline. */
763 int iDownRail = ++p->mxRail;
764 if( iDownRail<1 ) iDownRail = ++p->mxRail;
 
 
 
 
765 pRow->iRail = ++p->mxRail;
766 if( p->mxRail>=GR_MAX_RAIL ) return;
 
 
 
767 pRow->railInUse = BIT(pRow->iRail);
768 pParent->aiRiser[iDownRail] = pRow->idx;
769 mask = BIT(iDownRail);
770 for(pLoop=p->pFirst; pLoop; pLoop=pLoop->pNext){
771 pLoop->railInUse |= mask;
@@ -824,11 +842,11 @@
824 break;
825 }
826 }
827 if( iMrail==-1 ){
828 iMrail = findFreeRail(p, pRow->idx, p->pLast->idx, 0, 1);
829 if( p->mxRail>=GR_MAX_RAIL ) return;
830 mergeRiserFrom[iMrail] = parentRid;
831 }
832 iReuseIdx = p->nRow+1;
833 iReuseRail = iMrail;
834 mask = BIT(iMrail);
@@ -856,11 +874,11 @@
856 pDesc->mergeUpto = pDesc->idx;
857 }
858 }else{
859 /* Create a new merge for an on-screen node */
860 createMergeRiser(p, pDesc, pRow, isCherrypick);
861 if( p->mxRail>=GR_MAX_RAIL ) return;
862 if( iReuseIdx<0
863 && pDesc->nMergeChild==1
864 && (pDesc->iRail!=pDesc->mergeOut || pDesc->isLeaf)
865 ){
866 iReuseIdx = pDesc->idx;
@@ -872,17 +890,17 @@
872 }
873
874 /*
875 ** Insert merge rails from primaries to duplicates.
876 */
877 if( hasDup ){
878 int dupRail;
879 int mxRail;
880 find_max_rail(p);
881 mxRail = p->mxRail;
882 dupRail = mxRail+1;
883 if( p->mxRail>=GR_MAX_RAIL ) return;
884 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
885 if( !pRow->isDup ) continue;
886 pRow->iRail = dupRail;
887 pDesc = hashFind(p, pRow->rid);
888 assert( pDesc!=0 && pDesc!=pRow );
@@ -893,11 +911,11 @@
893 dupRail = mxRail+1;
894 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
895 if( pRow->isDup ) pRow->iRail = dupRail;
896 }
897 }
898 if( mxRail>=GR_MAX_RAIL ) return;
899 }
900
901 /*
902 ** Find the maximum rail number.
903 */
904
--- src/graph.c
+++ src/graph.c
@@ -59,11 +59,11 @@
59 ** check-ins and many files. For this reason, we make the identifier
60 ** a 64-bit integer, to dramatically reduce the risk of an overflow.
61 */
62 typedef sqlite3_int64 GraphRowId;
63
64 #define GR_MAX_RAIL 64 /* Max number of "rails" to display */
65
66 /* The graph appears vertically beside a timeline. Each row in the
67 ** timeline corresponds to a row in the graph. GraphRow.idx is 0 for
68 ** the top-most row and increases moving down. Hence (in the absence of
69 ** time skew) parents have a larger index than their children.
@@ -118,10 +118,11 @@
118 char **azBranch; /* Names of the branches */
119 int nRow; /* Number of rows */
120 int nHash; /* Number of slots in apHash[] */
121 u8 hasOffsetMergeRiser; /* Merge arrow from leaf goes up on a different
122 ** rail that the node */
123 u8 bOverfull; /* Unable to allocate sufficient rails */
124 u64 mergeRail; /* Rails used for merge lines */
125 GraphRow **apHash; /* Hash table of GraphRow objects. Key: rid */
126 u8 aiRailMap[GR_MAX_RAIL]; /* Mapping of rails to actually columns */
127 };
128
@@ -336,11 +337,18 @@
337 iBestDist = dist;
338 iBest = i;
339 }
340 }
341 }
342 if( iBestDist>1000 ){
343 p->bOverfull = 1;
344 iBest = GR_MAX_RAIL;
345 }
346 if( iBest>GR_MAX_RAIL ){
347 p->bOverfull = 1;
348 iBest = GR_MAX_RAIL;
349 }
350 if( iBest>p->mxRail ) p->mxRail = iBest;
351 if( bMergeRail ) p->mergeRail |= BIT(iBest);
352 return iBest;
353 }
354
@@ -709,11 +717,11 @@
717 if( pRow->iRail>=0 ) continue;
718 if( pRow->isDup ) continue;
719 if( pRow->nParent<0 ) continue;
720 if( pRow->nParent==0 || hashFind(p,pRow->aParent[0])==0 ){
721 pRow->iRail = findFreeRail(p, pRow->idxTop, pRow->idx+riserMargin,0,0);
722 /* if( p->mxRail>=GR_MAX_RAIL ) return; */
723 mask = BIT(pRow->iRail);
724 if( !omitDescenders ){
725 int n = RISER_MARGIN;
726 pRow->bDescender = pRow->nParent>0;
727 for(pLoop=pRow; pLoop && (n--)>0; pLoop=pLoop->pNext){
@@ -744,28 +752,38 @@
752 assert( pRow->nParent>0 );
753 parentRid = pRow->aParent[0];
754 pParent = hashFind(p, parentRid);
755 if( pParent==0 ){
756 pRow->iRail = ++p->mxRail;
757 if( p->mxRail>=GR_MAX_RAIL ){
758 pRow->iRail = p->mxRail = GR_MAX_RAIL;
759 p->bOverfull = 1;
760 }
761 pRow->railInUse = BIT(pRow->iRail);
762 continue;
763 }
764 if( pParent->idx>pRow->idx ){
765 /* Common case: Child occurs after parent and is above the
766 ** parent in the timeline */
767 pRow->iRail = findFreeRail(p, pRow->idxTop, pParent->idx,
768 pParent->iRail, 0);
769 /* if( p->mxRail>=GR_MAX_RAIL ) return; */
770 pParent->aiRiser[pRow->iRail] = pRow->idx;
771 }else{
772 /* Timewarp case: Child occurs earlier in time than parent and
773 ** appears below the parent in the timeline. */
774 int iDownRail = ++p->mxRail;
775 if( iDownRail<1 ) iDownRail = ++p->mxRail;
776 if( p->mxRail>GR_MAX_RAIL ){
777 iDownRail = p->mxRail = GR_MAX_RAIL;
778 p->bOverfull = 1;
779 }
780 pRow->iRail = ++p->mxRail;
781 if( p->mxRail>=GR_MAX_RAIL ){
782 pRow->iRail = p->mxRail = GR_MAX_RAIL;
783 p->bOverfull = 1;
784 }
785 pRow->railInUse = BIT(pRow->iRail);
786 pParent->aiRiser[iDownRail] = pRow->idx;
787 mask = BIT(iDownRail);
788 for(pLoop=p->pFirst; pLoop; pLoop=pLoop->pNext){
789 pLoop->railInUse |= mask;
@@ -824,11 +842,11 @@
842 break;
843 }
844 }
845 if( iMrail==-1 ){
846 iMrail = findFreeRail(p, pRow->idx, p->pLast->idx, 0, 1);
847 /*if( p->mxRail>=GR_MAX_RAIL ) return;*/
848 mergeRiserFrom[iMrail] = parentRid;
849 }
850 iReuseIdx = p->nRow+1;
851 iReuseRail = iMrail;
852 mask = BIT(iMrail);
@@ -856,11 +874,11 @@
874 pDesc->mergeUpto = pDesc->idx;
875 }
876 }else{
877 /* Create a new merge for an on-screen node */
878 createMergeRiser(p, pDesc, pRow, isCherrypick);
879 /* if( p->mxRail>=GR_MAX_RAIL ) return; */
880 if( iReuseIdx<0
881 && pDesc->nMergeChild==1
882 && (pDesc->iRail!=pDesc->mergeOut || pDesc->isLeaf)
883 ){
884 iReuseIdx = pDesc->idx;
@@ -872,17 +890,17 @@
890 }
891
892 /*
893 ** Insert merge rails from primaries to duplicates.
894 */
895 if( hasDup && p->mxRail<GR_MAX_RAIL ){
896 int dupRail;
897 int mxRail;
898 find_max_rail(p);
899 mxRail = p->mxRail;
900 dupRail = mxRail+1;
901 /* if( p->mxRail>=GR_MAX_RAIL ) return; */
902 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
903 if( !pRow->isDup ) continue;
904 pRow->iRail = dupRail;
905 pDesc = hashFind(p, pRow->rid);
906 assert( pDesc!=0 && pDesc!=pRow );
@@ -893,11 +911,11 @@
911 dupRail = mxRail+1;
912 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
913 if( pRow->isDup ) pRow->iRail = dupRail;
914 }
915 }
916 /* if( mxRail>=GR_MAX_RAIL ) return; */
917 }
918
919 /*
920 ** Find the maximum rail number.
921 */
922
+10 -1
--- src/http.c
+++ src/http.c
@@ -512,10 +512,13 @@
512512
transport_send(&g.url, &hdr);
513513
transport_send(&g.url, &payload);
514514
blob_reset(&hdr);
515515
blob_reset(&payload);
516516
transport_flip(&g.url);
517
+ if( mHttpFlags & HTTP_VERBOSE ){
518
+ fossil_print("IP-Address: %s\n", g.zIpAddr);
519
+ }
517520
518521
/*
519522
** Read and interpret the server reply
520523
*/
521524
closeConnection = 1;
@@ -572,10 +575,11 @@
572575
}
573576
}else if( ( rc==301 || rc==302 || rc==307 || rc==308 ) &&
574577
fossil_strnicmp(zLine, "location:", 9)==0 ){
575578
int i, j;
576579
int wasHttps;
580
+ int priorUrlFlags;
577581
578582
if ( --maxRedirect == 0){
579583
fossil_warning("redirect limit exceeded");
580584
goto write_err;
581585
}
@@ -596,10 +600,11 @@
596600
fossil_warning("cannot redirect from %s to %s", g.url.canonical,
597601
&zLine[i]);
598602
goto write_err;
599603
}
600604
wasHttps = g.url.isHttps;
605
+ priorUrlFlags = g.url.flags;
601606
url_parse(&zLine[i], 0);
602607
if( wasHttps && !g.url.isHttps ){
603608
fossil_warning("cannot redirect from HTTPS to HTTP");
604609
goto write_err;
605610
}
@@ -610,11 +615,14 @@
610615
transport_close(&g.url);
611616
transport_global_shutdown(&g.url);
612617
fSeenHttpAuth = 0;
613618
if( g.zHttpAuth ) free(g.zHttpAuth);
614619
g.zHttpAuth = get_httpauth();
615
- if( rc==301 || rc==308 ) url_remember();
620
+ if( (rc==301 || rc==308) && (priorUrlFlags & URL_REMEMBER)!=0 ){
621
+ g.url.flags |= URL_REMEMBER;
622
+ url_remember();
623
+ }
616624
return http_exchange(pSend, pReply, mHttpFlags,
617625
maxRedirect, zAltMimetype);
618626
}else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){
619627
if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){
620628
isCompressed = 0;
@@ -793,10 +801,11 @@
793801
}
794802
if( find_option("xfer",0,0)!=0 ){
795803
mHttpFlags |= HTTP_USE_LOGIN;
796804
mHttpFlags &= ~HTTP_GENERIC;
797805
}
806
+ if( find_option("ipv4",0,0) ) g.fIPv4 = 1;
798807
verify_all_options();
799808
if( g.argc<3 || g.argc>5 ){
800809
usage("URL ?PAYLOAD? ?OUTPUT?");
801810
}
802811
zInFile = g.argc>=4 ? g.argv[3] : 0;
803812
--- src/http.c
+++ src/http.c
@@ -512,10 +512,13 @@
512 transport_send(&g.url, &hdr);
513 transport_send(&g.url, &payload);
514 blob_reset(&hdr);
515 blob_reset(&payload);
516 transport_flip(&g.url);
 
 
 
517
518 /*
519 ** Read and interpret the server reply
520 */
521 closeConnection = 1;
@@ -572,10 +575,11 @@
572 }
573 }else if( ( rc==301 || rc==302 || rc==307 || rc==308 ) &&
574 fossil_strnicmp(zLine, "location:", 9)==0 ){
575 int i, j;
576 int wasHttps;
 
577
578 if ( --maxRedirect == 0){
579 fossil_warning("redirect limit exceeded");
580 goto write_err;
581 }
@@ -596,10 +600,11 @@
596 fossil_warning("cannot redirect from %s to %s", g.url.canonical,
597 &zLine[i]);
598 goto write_err;
599 }
600 wasHttps = g.url.isHttps;
 
601 url_parse(&zLine[i], 0);
602 if( wasHttps && !g.url.isHttps ){
603 fossil_warning("cannot redirect from HTTPS to HTTP");
604 goto write_err;
605 }
@@ -610,11 +615,14 @@
610 transport_close(&g.url);
611 transport_global_shutdown(&g.url);
612 fSeenHttpAuth = 0;
613 if( g.zHttpAuth ) free(g.zHttpAuth);
614 g.zHttpAuth = get_httpauth();
615 if( rc==301 || rc==308 ) url_remember();
 
 
 
616 return http_exchange(pSend, pReply, mHttpFlags,
617 maxRedirect, zAltMimetype);
618 }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){
619 if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){
620 isCompressed = 0;
@@ -793,10 +801,11 @@
793 }
794 if( find_option("xfer",0,0)!=0 ){
795 mHttpFlags |= HTTP_USE_LOGIN;
796 mHttpFlags &= ~HTTP_GENERIC;
797 }
 
798 verify_all_options();
799 if( g.argc<3 || g.argc>5 ){
800 usage("URL ?PAYLOAD? ?OUTPUT?");
801 }
802 zInFile = g.argc>=4 ? g.argv[3] : 0;
803
--- src/http.c
+++ src/http.c
@@ -512,10 +512,13 @@
512 transport_send(&g.url, &hdr);
513 transport_send(&g.url, &payload);
514 blob_reset(&hdr);
515 blob_reset(&payload);
516 transport_flip(&g.url);
517 if( mHttpFlags & HTTP_VERBOSE ){
518 fossil_print("IP-Address: %s\n", g.zIpAddr);
519 }
520
521 /*
522 ** Read and interpret the server reply
523 */
524 closeConnection = 1;
@@ -572,10 +575,11 @@
575 }
576 }else if( ( rc==301 || rc==302 || rc==307 || rc==308 ) &&
577 fossil_strnicmp(zLine, "location:", 9)==0 ){
578 int i, j;
579 int wasHttps;
580 int priorUrlFlags;
581
582 if ( --maxRedirect == 0){
583 fossil_warning("redirect limit exceeded");
584 goto write_err;
585 }
@@ -596,10 +600,11 @@
600 fossil_warning("cannot redirect from %s to %s", g.url.canonical,
601 &zLine[i]);
602 goto write_err;
603 }
604 wasHttps = g.url.isHttps;
605 priorUrlFlags = g.url.flags;
606 url_parse(&zLine[i], 0);
607 if( wasHttps && !g.url.isHttps ){
608 fossil_warning("cannot redirect from HTTPS to HTTP");
609 goto write_err;
610 }
@@ -610,11 +615,14 @@
615 transport_close(&g.url);
616 transport_global_shutdown(&g.url);
617 fSeenHttpAuth = 0;
618 if( g.zHttpAuth ) free(g.zHttpAuth);
619 g.zHttpAuth = get_httpauth();
620 if( (rc==301 || rc==308) && (priorUrlFlags & URL_REMEMBER)!=0 ){
621 g.url.flags |= URL_REMEMBER;
622 url_remember();
623 }
624 return http_exchange(pSend, pReply, mHttpFlags,
625 maxRedirect, zAltMimetype);
626 }else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){
627 if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){
628 isCompressed = 0;
@@ -793,10 +801,11 @@
801 }
802 if( find_option("xfer",0,0)!=0 ){
803 mHttpFlags |= HTTP_USE_LOGIN;
804 mHttpFlags &= ~HTTP_GENERIC;
805 }
806 if( find_option("ipv4",0,0) ) g.fIPv4 = 1;
807 verify_all_options();
808 if( g.argc<3 || g.argc>5 ){
809 usage("URL ?PAYLOAD? ?OUTPUT?");
810 }
811 zInFile = g.argc>=4 ? g.argv[3] : 0;
812
+16 -2
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -451,11 +451,18 @@
451451
ssl_global_init_client();
452452
if( pUrlData->useProxy ){
453453
int rc;
454454
char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port);
455455
BIO *sBio = BIO_new_connect(connStr);
456
- free(connStr);
456
+ if( g.fIPv4 ){
457
+#ifdef BIO_FAMILY_IPV4
458
+ BIO_set_conn_ip_family(sBio, BIO_FAMILY_IPV4);
459
+#else
460
+ fossil_warning("The --ipv4 option is not supported in this build\n");
461
+#endif
462
+ }
463
+ fossil_free(connStr);
457464
if( BIO_do_connect(sBio)<=0 ){
458465
ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)",
459466
pUrlData->name, pUrlData->port,
460467
ERR_reason_error_string(ERR_get_error()));
461468
ssl_close_client();
@@ -503,11 +510,18 @@
503510
#endif
504511
505512
if( !pUrlData->useProxy ){
506513
char *connStr = mprintf("%s:%d", pUrlData->name, pUrlData->port);
507514
BIO_set_conn_hostname(iBio, connStr);
508
- free(connStr);
515
+ fossil_free(connStr);
516
+ if( g.fIPv4 ){
517
+#ifdef BIO_FAMILY_IPV4
518
+ BIO_set_conn_ip_family(iBio, BIO_FAMILY_IPV4);
519
+#else
520
+ fossil_warning("The --ipv4 option is not supported in this build\n");
521
+#endif
522
+ }
509523
if( BIO_do_connect(iBio)<=0 ){
510524
ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
511525
pUrlData->name, pUrlData->port,
512526
ERR_reason_error_string(ERR_get_error()));
513527
ssl_close_client();
514528
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -451,11 +451,18 @@
451 ssl_global_init_client();
452 if( pUrlData->useProxy ){
453 int rc;
454 char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port);
455 BIO *sBio = BIO_new_connect(connStr);
456 free(connStr);
 
 
 
 
 
 
 
457 if( BIO_do_connect(sBio)<=0 ){
458 ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)",
459 pUrlData->name, pUrlData->port,
460 ERR_reason_error_string(ERR_get_error()));
461 ssl_close_client();
@@ -503,11 +510,18 @@
503 #endif
504
505 if( !pUrlData->useProxy ){
506 char *connStr = mprintf("%s:%d", pUrlData->name, pUrlData->port);
507 BIO_set_conn_hostname(iBio, connStr);
508 free(connStr);
 
 
 
 
 
 
 
509 if( BIO_do_connect(iBio)<=0 ){
510 ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
511 pUrlData->name, pUrlData->port,
512 ERR_reason_error_string(ERR_get_error()));
513 ssl_close_client();
514
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -451,11 +451,18 @@
451 ssl_global_init_client();
452 if( pUrlData->useProxy ){
453 int rc;
454 char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port);
455 BIO *sBio = BIO_new_connect(connStr);
456 if( g.fIPv4 ){
457 #ifdef BIO_FAMILY_IPV4
458 BIO_set_conn_ip_family(sBio, BIO_FAMILY_IPV4);
459 #else
460 fossil_warning("The --ipv4 option is not supported in this build\n");
461 #endif
462 }
463 fossil_free(connStr);
464 if( BIO_do_connect(sBio)<=0 ){
465 ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)",
466 pUrlData->name, pUrlData->port,
467 ERR_reason_error_string(ERR_get_error()));
468 ssl_close_client();
@@ -503,11 +510,18 @@
510 #endif
511
512 if( !pUrlData->useProxy ){
513 char *connStr = mprintf("%s:%d", pUrlData->name, pUrlData->port);
514 BIO_set_conn_hostname(iBio, connStr);
515 fossil_free(connStr);
516 if( g.fIPv4 ){
517 #ifdef BIO_FAMILY_IPV4
518 BIO_set_conn_ip_family(iBio, BIO_FAMILY_IPV4);
519 #else
520 fossil_warning("The --ipv4 option is not supported in this build\n");
521 #endif
522 }
523 if( BIO_do_connect(iBio)<=0 ){
524 ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
525 pUrlData->name, pUrlData->port,
526 ERR_reason_error_string(ERR_get_error()));
527 ssl_close_client();
528
+68 -34
--- src/info.c
+++ src/info.c
@@ -2622,31 +2622,36 @@
26222622
26232623
/*
26242624
** WEBPAGE: artifact
26252625
** WEBPAGE: file
26262626
** WEBPAGE: whatis
2627
+** WEBPAGE: docfile
26272628
**
26282629
** Typical usage:
26292630
**
26302631
** /artifact/HASH
26312632
** /whatis/HASH
26322633
** /file/NAME
2634
+** /docfile/NAME
26332635
**
26342636
** Additional query parameters:
26352637
**
26362638
** ln - show line numbers
26372639
** ln=N - highlight line number N
26382640
** ln=M-N - highlight lines M through N inclusive
26392641
** ln=M-N+Y-Z - highlight lines M through N and Y through Z (inclusive)
26402642
** verbose - show more detail in the description
2643
+** brief - show just the document, not the metadata. The
2644
+** /docfile page is an alias for /file?brief
26412645
** download - redirect to the download (artifact page only)
26422646
** name=NAME - filename or hash as a query parameter
26432647
** filename=NAME - alternative spelling for "name="
26442648
** fn=NAME - alternative spelling for "name="
26452649
** ci=VERSION - The specific check-in to use with "name=" to
26462650
** identify the file.
26472651
** txt - Force display of unformatted source text
2652
+** hash - Output only the hash of the artifact
26482653
**
26492654
** The /artifact page show the complete content of a file
26502655
** identified by HASH. The /whatis page shows only a description
26512656
** of how the artifact is used. The /file page shows the most recent
26522657
** version of the file or directory called NAME, or a list of the
@@ -2665,18 +2670,21 @@
26652670
void artifact_page(void){
26662671
int rid = 0;
26672672
Blob content;
26682673
const char *zMime;
26692674
Blob downloadName;
2675
+ Blob uuid;
26702676
int renderAsWiki = 0;
26712677
int renderAsHtml = 0;
26722678
int renderAsSvg = 0;
26732679
int objType;
26742680
int asText;
26752681
const char *zUuid = 0;
26762682
u32 objdescFlags = OBJDESC_BASE;
26772683
int descOnly = fossil_strcmp(g.zPath,"whatis")==0;
2684
+ int hashOnly = P("hash")!=0;
2685
+ int docOnly = P("brief")!=0;
26782686
int isFile = fossil_strcmp(g.zPath,"file")==0;
26792687
const char *zLn = P("ln");
26802688
const char *zName = P("name");
26812689
const char *zCI = P("ci");
26822690
HQuery url;
@@ -2687,10 +2695,14 @@
26872695
26882696
login_check_credentials();
26892697
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
26902698
cgi_check_for_malice();
26912699
style_set_current_feature("artifact");
2700
+ if( fossil_strcmp(g.zPath, "docfile")==0 ){
2701
+ isFile = 1;
2702
+ docOnly = 1;
2703
+ }
26922704
26932705
/* Capture and normalize the name= and ci= query parameters */
26942706
if( zName==0 ){
26952707
zName = P("filename");
26962708
if( zName==0 ){
@@ -2789,14 +2801,23 @@
27892801
url_add_parameter(&url, "verbose", "1");
27902802
objdescFlags |= OBJDESC_DETAIL;
27912803
}
27922804
zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
27932805
etag_check(ETAG_HASH, zUuid);
2806
+
2807
+ if( descOnly && hashOnly ){
2808
+ blob_set(&uuid, zUuid);
2809
+ cgi_set_content_type("text/plain");
2810
+ cgi_set_content(&uuid);
2811
+ return;
2812
+ }
27942813
27952814
asText = P("txt")!=0;
27962815
if( isFile ){
2797
- if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){
2816
+ if( docOnly ){
2817
+ /* No header */
2818
+ }else if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){
27982819
zCI = "tip";
27992820
@ <h2>File %z(href("%R/finfo?name=%T&m&ci=tip",zName))%h(zName)</a>
28002821
@ from the %z(href("%R/info/tip"))latest check-in</a></h2>
28012822
}else{
28022823
const char *zPath;
@@ -2814,17 +2835,19 @@
28142835
}else{
28152836
@ part of check-in %z(href("%R/info/%!S",zCIUuid))%S(zCIUuid)</a></h2>
28162837
}
28172838
blob_reset(&path);
28182839
}
2819
- style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
28202840
zMime = mimetype_from_name(zName);
2821
- style_submenu_element("Annotate", "%R/annotate?filename=%T&checkin=%T",
2822
- zName, zCI);
2823
- style_submenu_element("Blame", "%R/blame?filename=%T&checkin=%T",
2824
- zName, zCI);
2825
- style_submenu_element("Doc", "%R/doc/%T/%T", zCI, zName);
2841
+ if( !docOnly ){
2842
+ style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
2843
+ style_submenu_element("Annotate", "%R/annotate?filename=%T&checkin=%T",
2844
+ zName, zCI);
2845
+ style_submenu_element("Blame", "%R/blame?filename=%T&checkin=%T",
2846
+ zName, zCI);
2847
+ style_submenu_element("Doc", "%R/doc/%T/%T", zCI, zName);
2848
+ }
28262849
blob_init(&downloadName, zName, -1);
28272850
objType = OBJTYPE_CONTENT;
28282851
}else{
28292852
@ <h2>Artifact
28302853
style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);
@@ -2843,11 +2866,11 @@
28432866
cgi_redirectf("%R/raw/%s?at=%T",
28442867
db_text("x", "SELECT uuid FROM blob WHERE rid=%d", rid),
28452868
file_tail(blob_str(&downloadName)));
28462869
/*NOTREACHED*/
28472870
}
2848
- if( g.perm.Admin ){
2871
+ if( g.perm.Admin && !docOnly ){
28492872
const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
28502873
if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){
28512874
style_submenu_element("Unshun", "%R/shun?accept=%s&sub=1#accshun", zUuid);
28522875
}else{
28532876
style_submenu_element("Shun", "%R/shun?shun=%s#addshun",zUuid);
@@ -2888,41 +2911,49 @@
28882911
const char *zIp = db_column_text(&q,2);
28892912
@ <p>Received on %s(zDate) from %h(zUser) at %h(zIp).</p>
28902913
}
28912914
db_finalize(&q);
28922915
}
2893
- style_submenu_element("Download", "%R/raw/%s?at=%T", zUuid, file_tail(zName));
2894
- if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){
2895
- style_submenu_element("Check-ins Using", "%R/timeline?uf=%s", zUuid);
2916
+ if( !docOnly ){
2917
+ style_submenu_element("Download", "%R/raw/%s?at=%T",zUuid,file_tail(zName));
2918
+ if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){
2919
+ style_submenu_element("Check-ins Using", "%R/timeline?uf=%s", zUuid);
2920
+ }
28962921
}
28972922
if( zMime ){
28982923
if( fossil_strcmp(zMime, "text/html")==0 ){
28992924
if( asText ){
29002925
style_submenu_element("Html", "%s", url_render(&url, "txt", 0, 0, 0));
29012926
}else{
29022927
renderAsHtml = 1;
2903
- style_submenu_element("Text", "%s", url_render(&url, "txt", "1", 0, 0));
2928
+ if( !docOnly ){
2929
+ style_submenu_element("Text", "%s", url_render(&url, "txt","1",0,0));
2930
+ }
29042931
}
29052932
}else if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0
29062933
|| fossil_strcmp(zMime, "text/x-markdown")==0
29072934
|| fossil_strcmp(zMime, "text/x-pikchr")==0 ){
29082935
if( asText ){
29092936
style_submenu_element(zMime[7]=='p' ? "Pikchr" : "Wiki",
29102937
"%s", url_render(&url, "txt", 0, 0, 0));
29112938
}else{
29122939
renderAsWiki = 1;
2913
- style_submenu_element("Text", "%s", url_render(&url, "txt", "1", 0, 0));
2940
+ if( !docOnly ){
2941
+ style_submenu_element("Text", "%s", url_render(&url, "txt","1",0,0));
2942
+ }
29142943
}
29152944
}else if( fossil_strcmp(zMime, "image/svg+xml")==0 ){
29162945
if( asText ){
29172946
style_submenu_element("Svg", "%s", url_render(&url, "txt", 0, 0, 0));
29182947
}else{
29192948
renderAsSvg = 1;
2920
- style_submenu_element("Text", "%s", url_render(&url, "txt", "1", 0, 0));
2949
+ if( !docOnly ){
2950
+ style_submenu_element("Text", "%s", url_render(&url, "txt","1",0,0));
2951
+ }
29212952
}
29222953
}
2923
- if( fileedit_is_editable(zName) ){
2954
+ if( !docOnly && fileedit_is_editable(zName) ){
29242955
style_submenu_element("Edit",
29252956
"%R/fileedit?filename=%T&checkin=%!S",
29262957
zName, zCI);
29272958
}
29282959
}
@@ -2930,11 +2961,13 @@
29302961
style_submenu_element("Parsed", "%R/info/%s", zUuid);
29312962
}
29322963
if( descOnly ){
29332964
style_submenu_element("Content", "%R/artifact/%s", zUuid);
29342965
}else{
2935
- @ <hr>
2966
+ if( !docOnly || !isFile ){
2967
+ @ <hr>
2968
+ }
29362969
content_get(rid, &content);
29372970
if( renderAsWiki ){
29382971
safe_html_context(DOCSRC_FILE);
29392972
wiki_render_by_mimetype(&content, zMime);
29402973
document_emit_js();
@@ -3600,10 +3633,11 @@
36003633
zNewColorFlag = P("newclr") ? " checked" : "";
36013634
zNewTagFlag = P("newtag") ? " checked" : "";
36023635
zNewTag = PDT("tagname","");
36033636
zNewBrFlag = P("newbr") ? " checked" : "";
36043637
zNewBranch = PDT("brname","");
3638
+ zBranchName = branch_of_rid(rid);
36053639
zCloseFlag = P("close") ? " checked" : "";
36063640
zHideFlag = P("hide") ? " checked" : "";
36073641
if( P("apply") && cgi_csrf_safe(2) ){
36083642
Blob ctrl;
36093643
char *zNow;
@@ -3647,17 +3681,25 @@
36473681
zUuid[10] = 0;
36483682
style_header("Edit Check-in [%s]", zUuid);
36493683
if( P("preview") ){
36503684
Blob suffix;
36513685
int nTag = 0;
3686
+ const char *zDplyBr; /* Branch name used to determine BG color */
3687
+ if( zNewBrFlag[0] && zNewBranch[0] ){
3688
+ zDplyBr = zNewBranch;
3689
+ }else{
3690
+ zDplyBr = zBranchName;
3691
+ }
36523692
@ <b>Preview:</b>
36533693
@ <blockquote>
36543694
@ <table border=0>
36553695
if( zNewColorFlag[0] && zNewColor && zNewColor[0] ){
3656
- @ <tr><td style="background-color: %h(zNewColor);">
3696
+ @ <tr><td style="background-color:%h(reasonable_bg_color(zNewColor,0));">
36573697
}else if( zColor[0] ){
3658
- @ <tr><td style="background-color: %h(zColor);">
3698
+ @ <tr><td style="background-color:%h(reasonable_bg_color(zColor,0));">
3699
+ }else if( zDplyBr && fossil_strcmp(zDplyBr,"trunk")!=0 ){
3700
+ @ <tr><td style="background-color:%h(hash_color(zDplyBr));">
36593701
}else{
36603702
@ <tr><td>
36613703
}
36623704
@ %!W(blob_str(&comment))
36633705
blob_zero(&suffix);
@@ -3738,13 +3780,10 @@
37383780
@ <tr><th align="right" valign="top">Tags:</th>
37393781
@ <td valign="top">
37403782
@ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag)>
37413783
@ Add the following new tag name to this check-in:</label>
37423784
@ <input size="15" name="tagname" id="tagname" value="%h(zNewTag)">
3743
- zBranchName = db_text(0, "SELECT value FROM tagxref, tag"
3744
- " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid"
3745
- " AND tagxref.tagid=%d", rid, TAG_BRANCH);
37463785
db_prepare(&q,
37473786
"SELECT tag.tagid, tagname, tagxref.value FROM tagxref, tag"
37483787
" WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid"
37493788
" ORDER BY CASE WHEN tagname GLOB 'sym-*' THEN substr(tagname,5)"
37503789
" ELSE tagname END /*sort*/",
@@ -3937,11 +3976,11 @@
39373976
Blob comment;
39383977
char *zNow;
39393978
int nTags, nCancels;
39403979
int i;
39413980
Stmt q;
3942
- int ckComFlgs; /* Flags passed to suspicious_comment() */
3981
+ int ckComFlgs; /* Flags passed to verify_comment() */
39433982
39443983
39453984
fEditComment = find_option("edit-comment","e",0)!=0;
39463985
zNewComment = find_option("comment","m",1);
39473986
zComFile = find_option("message-file","M",1);
@@ -4030,18 +4069,13 @@
40304069
}else{
40314070
const char *zVerComs = db_get("verify-comments","on");
40324071
if( is_false(zVerComs) ){
40334072
ckComFlgs = 0;
40344073
}else if( strcmp(zVerComs,"preview")==0 ){
4035
- ckComFlgs = COMCK_PREVIEW | COMCK_LINKS | COMCK_MARKUP;
4036
- }else if( strcmp(zVerComs,"links")==0 ){
4037
- ckComFlgs = COMCK_LINKS;
4074
+ ckComFlgs = COMCK_PREVIEW | COMCK_MARKUP;
40384075
}else{
4039
- ckComFlgs = COMCK_LINKS | COMCK_MARKUP;
4040
- }
4041
- if( zNewComment || zComFile ){
4042
- ckComFlgs = (ckComFlgs & COMCK_LINKS) | COMCK_NOPREVIEW;
4076
+ ckComFlgs = COMCK_MARKUP;
40434077
}
40444078
}
40454079
if( fEditComment ){
40464080
prepare_amend_comment(&comment, zComment, zUuid);
40474081
}else if( zComFile ){
@@ -4052,29 +4086,29 @@
40524086
}
40534087
if( blob_size(&comment)>0
40544088
&& comment_compare(zComment, blob_str(&comment))==0
40554089
){
40564090
int rc;
4057
- while( (rc = suspicious_comment(&comment, ckComFlgs))!=0 ){
4091
+ while( (rc = verify_comment(&comment, ckComFlgs))!=0 ){
40584092
char cReply;
40594093
Blob ans;
40604094
if( !fEditComment ){
40614095
fossil_fatal("Amend aborted; "
40624096
"use --no-verify-comment to override");
40634097
}
40644098
if( rc==COMCK_PREVIEW ){
4065
- prompt_user("\nContinue (Y/n/e=edit)? ", &ans);
4099
+ prompt_user("Continue, abort, or edit (C/a/e)? ", &ans);
40664100
}else{
4067
- prompt_user("\nContinue (y/n/E=edit)? ", &ans);
4101
+ prompt_user("Edit, abort, or continue (E/a/c)? ", &ans);
40684102
}
40694103
cReply = blob_str(&ans)[0];
40704104
cReply = fossil_tolower(cReply);
40714105
blob_reset(&ans);
4072
- if( cReply=='n' ){
4106
+ if( cReply=='a' ){
40734107
fossil_fatal("Amend aborted.");
40744108
}
4075
- if( cReply=='e' || (cReply!='y' && rc!=COMCK_PREVIEW) ){
4109
+ if( cReply=='e' || (cReply!='c' && rc!=COMCK_PREVIEW) ){
40764110
char *zPrior = blob_materialize(&comment);
40774111
blob_init(&comment, 0, 0);
40784112
prepare_amend_comment(&comment, zPrior, zUuid);
40794113
fossil_free(zPrior);
40804114
continue;
40814115
--- src/info.c
+++ src/info.c
@@ -2622,31 +2622,36 @@
2622
2623 /*
2624 ** WEBPAGE: artifact
2625 ** WEBPAGE: file
2626 ** WEBPAGE: whatis
 
2627 **
2628 ** Typical usage:
2629 **
2630 ** /artifact/HASH
2631 ** /whatis/HASH
2632 ** /file/NAME
 
2633 **
2634 ** Additional query parameters:
2635 **
2636 ** ln - show line numbers
2637 ** ln=N - highlight line number N
2638 ** ln=M-N - highlight lines M through N inclusive
2639 ** ln=M-N+Y-Z - highlight lines M through N and Y through Z (inclusive)
2640 ** verbose - show more detail in the description
 
 
2641 ** download - redirect to the download (artifact page only)
2642 ** name=NAME - filename or hash as a query parameter
2643 ** filename=NAME - alternative spelling for "name="
2644 ** fn=NAME - alternative spelling for "name="
2645 ** ci=VERSION - The specific check-in to use with "name=" to
2646 ** identify the file.
2647 ** txt - Force display of unformatted source text
 
2648 **
2649 ** The /artifact page show the complete content of a file
2650 ** identified by HASH. The /whatis page shows only a description
2651 ** of how the artifact is used. The /file page shows the most recent
2652 ** version of the file or directory called NAME, or a list of the
@@ -2665,18 +2670,21 @@
2665 void artifact_page(void){
2666 int rid = 0;
2667 Blob content;
2668 const char *zMime;
2669 Blob downloadName;
 
2670 int renderAsWiki = 0;
2671 int renderAsHtml = 0;
2672 int renderAsSvg = 0;
2673 int objType;
2674 int asText;
2675 const char *zUuid = 0;
2676 u32 objdescFlags = OBJDESC_BASE;
2677 int descOnly = fossil_strcmp(g.zPath,"whatis")==0;
 
 
2678 int isFile = fossil_strcmp(g.zPath,"file")==0;
2679 const char *zLn = P("ln");
2680 const char *zName = P("name");
2681 const char *zCI = P("ci");
2682 HQuery url;
@@ -2687,10 +2695,14 @@
2687
2688 login_check_credentials();
2689 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
2690 cgi_check_for_malice();
2691 style_set_current_feature("artifact");
 
 
 
 
2692
2693 /* Capture and normalize the name= and ci= query parameters */
2694 if( zName==0 ){
2695 zName = P("filename");
2696 if( zName==0 ){
@@ -2789,14 +2801,23 @@
2789 url_add_parameter(&url, "verbose", "1");
2790 objdescFlags |= OBJDESC_DETAIL;
2791 }
2792 zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
2793 etag_check(ETAG_HASH, zUuid);
 
 
 
 
 
 
 
2794
2795 asText = P("txt")!=0;
2796 if( isFile ){
2797 if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){
 
 
2798 zCI = "tip";
2799 @ <h2>File %z(href("%R/finfo?name=%T&m&ci=tip",zName))%h(zName)</a>
2800 @ from the %z(href("%R/info/tip"))latest check-in</a></h2>
2801 }else{
2802 const char *zPath;
@@ -2814,17 +2835,19 @@
2814 }else{
2815 @ part of check-in %z(href("%R/info/%!S",zCIUuid))%S(zCIUuid)</a></h2>
2816 }
2817 blob_reset(&path);
2818 }
2819 style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
2820 zMime = mimetype_from_name(zName);
2821 style_submenu_element("Annotate", "%R/annotate?filename=%T&checkin=%T",
2822 zName, zCI);
2823 style_submenu_element("Blame", "%R/blame?filename=%T&checkin=%T",
2824 zName, zCI);
2825 style_submenu_element("Doc", "%R/doc/%T/%T", zCI, zName);
 
 
 
2826 blob_init(&downloadName, zName, -1);
2827 objType = OBJTYPE_CONTENT;
2828 }else{
2829 @ <h2>Artifact
2830 style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);
@@ -2843,11 +2866,11 @@
2843 cgi_redirectf("%R/raw/%s?at=%T",
2844 db_text("x", "SELECT uuid FROM blob WHERE rid=%d", rid),
2845 file_tail(blob_str(&downloadName)));
2846 /*NOTREACHED*/
2847 }
2848 if( g.perm.Admin ){
2849 const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
2850 if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){
2851 style_submenu_element("Unshun", "%R/shun?accept=%s&sub=1#accshun", zUuid);
2852 }else{
2853 style_submenu_element("Shun", "%R/shun?shun=%s#addshun",zUuid);
@@ -2888,41 +2911,49 @@
2888 const char *zIp = db_column_text(&q,2);
2889 @ <p>Received on %s(zDate) from %h(zUser) at %h(zIp).</p>
2890 }
2891 db_finalize(&q);
2892 }
2893 style_submenu_element("Download", "%R/raw/%s?at=%T", zUuid, file_tail(zName));
2894 if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){
2895 style_submenu_element("Check-ins Using", "%R/timeline?uf=%s", zUuid);
 
 
2896 }
2897 if( zMime ){
2898 if( fossil_strcmp(zMime, "text/html")==0 ){
2899 if( asText ){
2900 style_submenu_element("Html", "%s", url_render(&url, "txt", 0, 0, 0));
2901 }else{
2902 renderAsHtml = 1;
2903 style_submenu_element("Text", "%s", url_render(&url, "txt", "1", 0, 0));
 
 
2904 }
2905 }else if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0
2906 || fossil_strcmp(zMime, "text/x-markdown")==0
2907 || fossil_strcmp(zMime, "text/x-pikchr")==0 ){
2908 if( asText ){
2909 style_submenu_element(zMime[7]=='p' ? "Pikchr" : "Wiki",
2910 "%s", url_render(&url, "txt", 0, 0, 0));
2911 }else{
2912 renderAsWiki = 1;
2913 style_submenu_element("Text", "%s", url_render(&url, "txt", "1", 0, 0));
 
 
2914 }
2915 }else if( fossil_strcmp(zMime, "image/svg+xml")==0 ){
2916 if( asText ){
2917 style_submenu_element("Svg", "%s", url_render(&url, "txt", 0, 0, 0));
2918 }else{
2919 renderAsSvg = 1;
2920 style_submenu_element("Text", "%s", url_render(&url, "txt", "1", 0, 0));
 
 
2921 }
2922 }
2923 if( fileedit_is_editable(zName) ){
2924 style_submenu_element("Edit",
2925 "%R/fileedit?filename=%T&checkin=%!S",
2926 zName, zCI);
2927 }
2928 }
@@ -2930,11 +2961,13 @@
2930 style_submenu_element("Parsed", "%R/info/%s", zUuid);
2931 }
2932 if( descOnly ){
2933 style_submenu_element("Content", "%R/artifact/%s", zUuid);
2934 }else{
2935 @ <hr>
 
 
2936 content_get(rid, &content);
2937 if( renderAsWiki ){
2938 safe_html_context(DOCSRC_FILE);
2939 wiki_render_by_mimetype(&content, zMime);
2940 document_emit_js();
@@ -3600,10 +3633,11 @@
3600 zNewColorFlag = P("newclr") ? " checked" : "";
3601 zNewTagFlag = P("newtag") ? " checked" : "";
3602 zNewTag = PDT("tagname","");
3603 zNewBrFlag = P("newbr") ? " checked" : "";
3604 zNewBranch = PDT("brname","");
 
3605 zCloseFlag = P("close") ? " checked" : "";
3606 zHideFlag = P("hide") ? " checked" : "";
3607 if( P("apply") && cgi_csrf_safe(2) ){
3608 Blob ctrl;
3609 char *zNow;
@@ -3647,17 +3681,25 @@
3647 zUuid[10] = 0;
3648 style_header("Edit Check-in [%s]", zUuid);
3649 if( P("preview") ){
3650 Blob suffix;
3651 int nTag = 0;
 
 
 
 
 
 
3652 @ <b>Preview:</b>
3653 @ <blockquote>
3654 @ <table border=0>
3655 if( zNewColorFlag[0] && zNewColor && zNewColor[0] ){
3656 @ <tr><td style="background-color: %h(zNewColor);">
3657 }else if( zColor[0] ){
3658 @ <tr><td style="background-color: %h(zColor);">
 
 
3659 }else{
3660 @ <tr><td>
3661 }
3662 @ %!W(blob_str(&comment))
3663 blob_zero(&suffix);
@@ -3738,13 +3780,10 @@
3738 @ <tr><th align="right" valign="top">Tags:</th>
3739 @ <td valign="top">
3740 @ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag)>
3741 @ Add the following new tag name to this check-in:</label>
3742 @ <input size="15" name="tagname" id="tagname" value="%h(zNewTag)">
3743 zBranchName = db_text(0, "SELECT value FROM tagxref, tag"
3744 " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid"
3745 " AND tagxref.tagid=%d", rid, TAG_BRANCH);
3746 db_prepare(&q,
3747 "SELECT tag.tagid, tagname, tagxref.value FROM tagxref, tag"
3748 " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid"
3749 " ORDER BY CASE WHEN tagname GLOB 'sym-*' THEN substr(tagname,5)"
3750 " ELSE tagname END /*sort*/",
@@ -3937,11 +3976,11 @@
3937 Blob comment;
3938 char *zNow;
3939 int nTags, nCancels;
3940 int i;
3941 Stmt q;
3942 int ckComFlgs; /* Flags passed to suspicious_comment() */
3943
3944
3945 fEditComment = find_option("edit-comment","e",0)!=0;
3946 zNewComment = find_option("comment","m",1);
3947 zComFile = find_option("message-file","M",1);
@@ -4030,18 +4069,13 @@
4030 }else{
4031 const char *zVerComs = db_get("verify-comments","on");
4032 if( is_false(zVerComs) ){
4033 ckComFlgs = 0;
4034 }else if( strcmp(zVerComs,"preview")==0 ){
4035 ckComFlgs = COMCK_PREVIEW | COMCK_LINKS | COMCK_MARKUP;
4036 }else if( strcmp(zVerComs,"links")==0 ){
4037 ckComFlgs = COMCK_LINKS;
4038 }else{
4039 ckComFlgs = COMCK_LINKS | COMCK_MARKUP;
4040 }
4041 if( zNewComment || zComFile ){
4042 ckComFlgs = (ckComFlgs & COMCK_LINKS) | COMCK_NOPREVIEW;
4043 }
4044 }
4045 if( fEditComment ){
4046 prepare_amend_comment(&comment, zComment, zUuid);
4047 }else if( zComFile ){
@@ -4052,29 +4086,29 @@
4052 }
4053 if( blob_size(&comment)>0
4054 && comment_compare(zComment, blob_str(&comment))==0
4055 ){
4056 int rc;
4057 while( (rc = suspicious_comment(&comment, ckComFlgs))!=0 ){
4058 char cReply;
4059 Blob ans;
4060 if( !fEditComment ){
4061 fossil_fatal("Amend aborted; "
4062 "use --no-verify-comment to override");
4063 }
4064 if( rc==COMCK_PREVIEW ){
4065 prompt_user("\nContinue (Y/n/e=edit)? ", &ans);
4066 }else{
4067 prompt_user("\nContinue (y/n/E=edit)? ", &ans);
4068 }
4069 cReply = blob_str(&ans)[0];
4070 cReply = fossil_tolower(cReply);
4071 blob_reset(&ans);
4072 if( cReply=='n' ){
4073 fossil_fatal("Amend aborted.");
4074 }
4075 if( cReply=='e' || (cReply!='y' && rc!=COMCK_PREVIEW) ){
4076 char *zPrior = blob_materialize(&comment);
4077 blob_init(&comment, 0, 0);
4078 prepare_amend_comment(&comment, zPrior, zUuid);
4079 fossil_free(zPrior);
4080 continue;
4081
--- src/info.c
+++ src/info.c
@@ -2622,31 +2622,36 @@
2622
2623 /*
2624 ** WEBPAGE: artifact
2625 ** WEBPAGE: file
2626 ** WEBPAGE: whatis
2627 ** WEBPAGE: docfile
2628 **
2629 ** Typical usage:
2630 **
2631 ** /artifact/HASH
2632 ** /whatis/HASH
2633 ** /file/NAME
2634 ** /docfile/NAME
2635 **
2636 ** Additional query parameters:
2637 **
2638 ** ln - show line numbers
2639 ** ln=N - highlight line number N
2640 ** ln=M-N - highlight lines M through N inclusive
2641 ** ln=M-N+Y-Z - highlight lines M through N and Y through Z (inclusive)
2642 ** verbose - show more detail in the description
2643 ** brief - show just the document, not the metadata. The
2644 ** /docfile page is an alias for /file?brief
2645 ** download - redirect to the download (artifact page only)
2646 ** name=NAME - filename or hash as a query parameter
2647 ** filename=NAME - alternative spelling for "name="
2648 ** fn=NAME - alternative spelling for "name="
2649 ** ci=VERSION - The specific check-in to use with "name=" to
2650 ** identify the file.
2651 ** txt - Force display of unformatted source text
2652 ** hash - Output only the hash of the artifact
2653 **
2654 ** The /artifact page show the complete content of a file
2655 ** identified by HASH. The /whatis page shows only a description
2656 ** of how the artifact is used. The /file page shows the most recent
2657 ** version of the file or directory called NAME, or a list of the
@@ -2665,18 +2670,21 @@
2670 void artifact_page(void){
2671 int rid = 0;
2672 Blob content;
2673 const char *zMime;
2674 Blob downloadName;
2675 Blob uuid;
2676 int renderAsWiki = 0;
2677 int renderAsHtml = 0;
2678 int renderAsSvg = 0;
2679 int objType;
2680 int asText;
2681 const char *zUuid = 0;
2682 u32 objdescFlags = OBJDESC_BASE;
2683 int descOnly = fossil_strcmp(g.zPath,"whatis")==0;
2684 int hashOnly = P("hash")!=0;
2685 int docOnly = P("brief")!=0;
2686 int isFile = fossil_strcmp(g.zPath,"file")==0;
2687 const char *zLn = P("ln");
2688 const char *zName = P("name");
2689 const char *zCI = P("ci");
2690 HQuery url;
@@ -2687,10 +2695,14 @@
2695
2696 login_check_credentials();
2697 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
2698 cgi_check_for_malice();
2699 style_set_current_feature("artifact");
2700 if( fossil_strcmp(g.zPath, "docfile")==0 ){
2701 isFile = 1;
2702 docOnly = 1;
2703 }
2704
2705 /* Capture and normalize the name= and ci= query parameters */
2706 if( zName==0 ){
2707 zName = P("filename");
2708 if( zName==0 ){
@@ -2789,14 +2801,23 @@
2801 url_add_parameter(&url, "verbose", "1");
2802 objdescFlags |= OBJDESC_DETAIL;
2803 }
2804 zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
2805 etag_check(ETAG_HASH, zUuid);
2806
2807 if( descOnly && hashOnly ){
2808 blob_set(&uuid, zUuid);
2809 cgi_set_content_type("text/plain");
2810 cgi_set_content(&uuid);
2811 return;
2812 }
2813
2814 asText = P("txt")!=0;
2815 if( isFile ){
2816 if( docOnly ){
2817 /* No header */
2818 }else if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){
2819 zCI = "tip";
2820 @ <h2>File %z(href("%R/finfo?name=%T&m&ci=tip",zName))%h(zName)</a>
2821 @ from the %z(href("%R/info/tip"))latest check-in</a></h2>
2822 }else{
2823 const char *zPath;
@@ -2814,17 +2835,19 @@
2835 }else{
2836 @ part of check-in %z(href("%R/info/%!S",zCIUuid))%S(zCIUuid)</a></h2>
2837 }
2838 blob_reset(&path);
2839 }
 
2840 zMime = mimetype_from_name(zName);
2841 if( !docOnly ){
2842 style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
2843 style_submenu_element("Annotate", "%R/annotate?filename=%T&checkin=%T",
2844 zName, zCI);
2845 style_submenu_element("Blame", "%R/blame?filename=%T&checkin=%T",
2846 zName, zCI);
2847 style_submenu_element("Doc", "%R/doc/%T/%T", zCI, zName);
2848 }
2849 blob_init(&downloadName, zName, -1);
2850 objType = OBJTYPE_CONTENT;
2851 }else{
2852 @ <h2>Artifact
2853 style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);
@@ -2843,11 +2866,11 @@
2866 cgi_redirectf("%R/raw/%s?at=%T",
2867 db_text("x", "SELECT uuid FROM blob WHERE rid=%d", rid),
2868 file_tail(blob_str(&downloadName)));
2869 /*NOTREACHED*/
2870 }
2871 if( g.perm.Admin && !docOnly ){
2872 const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
2873 if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){
2874 style_submenu_element("Unshun", "%R/shun?accept=%s&sub=1#accshun", zUuid);
2875 }else{
2876 style_submenu_element("Shun", "%R/shun?shun=%s#addshun",zUuid);
@@ -2888,41 +2911,49 @@
2911 const char *zIp = db_column_text(&q,2);
2912 @ <p>Received on %s(zDate) from %h(zUser) at %h(zIp).</p>
2913 }
2914 db_finalize(&q);
2915 }
2916 if( !docOnly ){
2917 style_submenu_element("Download", "%R/raw/%s?at=%T",zUuid,file_tail(zName));
2918 if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){
2919 style_submenu_element("Check-ins Using", "%R/timeline?uf=%s", zUuid);
2920 }
2921 }
2922 if( zMime ){
2923 if( fossil_strcmp(zMime, "text/html")==0 ){
2924 if( asText ){
2925 style_submenu_element("Html", "%s", url_render(&url, "txt", 0, 0, 0));
2926 }else{
2927 renderAsHtml = 1;
2928 if( !docOnly ){
2929 style_submenu_element("Text", "%s", url_render(&url, "txt","1",0,0));
2930 }
2931 }
2932 }else if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0
2933 || fossil_strcmp(zMime, "text/x-markdown")==0
2934 || fossil_strcmp(zMime, "text/x-pikchr")==0 ){
2935 if( asText ){
2936 style_submenu_element(zMime[7]=='p' ? "Pikchr" : "Wiki",
2937 "%s", url_render(&url, "txt", 0, 0, 0));
2938 }else{
2939 renderAsWiki = 1;
2940 if( !docOnly ){
2941 style_submenu_element("Text", "%s", url_render(&url, "txt","1",0,0));
2942 }
2943 }
2944 }else if( fossil_strcmp(zMime, "image/svg+xml")==0 ){
2945 if( asText ){
2946 style_submenu_element("Svg", "%s", url_render(&url, "txt", 0, 0, 0));
2947 }else{
2948 renderAsSvg = 1;
2949 if( !docOnly ){
2950 style_submenu_element("Text", "%s", url_render(&url, "txt","1",0,0));
2951 }
2952 }
2953 }
2954 if( !docOnly && fileedit_is_editable(zName) ){
2955 style_submenu_element("Edit",
2956 "%R/fileedit?filename=%T&checkin=%!S",
2957 zName, zCI);
2958 }
2959 }
@@ -2930,11 +2961,13 @@
2961 style_submenu_element("Parsed", "%R/info/%s", zUuid);
2962 }
2963 if( descOnly ){
2964 style_submenu_element("Content", "%R/artifact/%s", zUuid);
2965 }else{
2966 if( !docOnly || !isFile ){
2967 @ <hr>
2968 }
2969 content_get(rid, &content);
2970 if( renderAsWiki ){
2971 safe_html_context(DOCSRC_FILE);
2972 wiki_render_by_mimetype(&content, zMime);
2973 document_emit_js();
@@ -3600,10 +3633,11 @@
3633 zNewColorFlag = P("newclr") ? " checked" : "";
3634 zNewTagFlag = P("newtag") ? " checked" : "";
3635 zNewTag = PDT("tagname","");
3636 zNewBrFlag = P("newbr") ? " checked" : "";
3637 zNewBranch = PDT("brname","");
3638 zBranchName = branch_of_rid(rid);
3639 zCloseFlag = P("close") ? " checked" : "";
3640 zHideFlag = P("hide") ? " checked" : "";
3641 if( P("apply") && cgi_csrf_safe(2) ){
3642 Blob ctrl;
3643 char *zNow;
@@ -3647,17 +3681,25 @@
3681 zUuid[10] = 0;
3682 style_header("Edit Check-in [%s]", zUuid);
3683 if( P("preview") ){
3684 Blob suffix;
3685 int nTag = 0;
3686 const char *zDplyBr; /* Branch name used to determine BG color */
3687 if( zNewBrFlag[0] && zNewBranch[0] ){
3688 zDplyBr = zNewBranch;
3689 }else{
3690 zDplyBr = zBranchName;
3691 }
3692 @ <b>Preview:</b>
3693 @ <blockquote>
3694 @ <table border=0>
3695 if( zNewColorFlag[0] && zNewColor && zNewColor[0] ){
3696 @ <tr><td style="background-color:%h(reasonable_bg_color(zNewColor,0));">
3697 }else if( zColor[0] ){
3698 @ <tr><td style="background-color:%h(reasonable_bg_color(zColor,0));">
3699 }else if( zDplyBr && fossil_strcmp(zDplyBr,"trunk")!=0 ){
3700 @ <tr><td style="background-color:%h(hash_color(zDplyBr));">
3701 }else{
3702 @ <tr><td>
3703 }
3704 @ %!W(blob_str(&comment))
3705 blob_zero(&suffix);
@@ -3738,13 +3780,10 @@
3780 @ <tr><th align="right" valign="top">Tags:</th>
3781 @ <td valign="top">
3782 @ <label><input type="checkbox" id="newtag" name="newtag"%s(zNewTagFlag)>
3783 @ Add the following new tag name to this check-in:</label>
3784 @ <input size="15" name="tagname" id="tagname" value="%h(zNewTag)">
 
 
 
3785 db_prepare(&q,
3786 "SELECT tag.tagid, tagname, tagxref.value FROM tagxref, tag"
3787 " WHERE tagxref.rid=%d AND tagtype>0 AND tagxref.tagid=tag.tagid"
3788 " ORDER BY CASE WHEN tagname GLOB 'sym-*' THEN substr(tagname,5)"
3789 " ELSE tagname END /*sort*/",
@@ -3937,11 +3976,11 @@
3976 Blob comment;
3977 char *zNow;
3978 int nTags, nCancels;
3979 int i;
3980 Stmt q;
3981 int ckComFlgs; /* Flags passed to verify_comment() */
3982
3983
3984 fEditComment = find_option("edit-comment","e",0)!=0;
3985 zNewComment = find_option("comment","m",1);
3986 zComFile = find_option("message-file","M",1);
@@ -4030,18 +4069,13 @@
4069 }else{
4070 const char *zVerComs = db_get("verify-comments","on");
4071 if( is_false(zVerComs) ){
4072 ckComFlgs = 0;
4073 }else if( strcmp(zVerComs,"preview")==0 ){
4074 ckComFlgs = COMCK_PREVIEW | COMCK_MARKUP;
 
 
4075 }else{
4076 ckComFlgs = COMCK_MARKUP;
 
 
 
4077 }
4078 }
4079 if( fEditComment ){
4080 prepare_amend_comment(&comment, zComment, zUuid);
4081 }else if( zComFile ){
@@ -4052,29 +4086,29 @@
4086 }
4087 if( blob_size(&comment)>0
4088 && comment_compare(zComment, blob_str(&comment))==0
4089 ){
4090 int rc;
4091 while( (rc = verify_comment(&comment, ckComFlgs))!=0 ){
4092 char cReply;
4093 Blob ans;
4094 if( !fEditComment ){
4095 fossil_fatal("Amend aborted; "
4096 "use --no-verify-comment to override");
4097 }
4098 if( rc==COMCK_PREVIEW ){
4099 prompt_user("Continue, abort, or edit (C/a/e)? ", &ans);
4100 }else{
4101 prompt_user("Edit, abort, or continue (E/a/c)? ", &ans);
4102 }
4103 cReply = blob_str(&ans)[0];
4104 cReply = fossil_tolower(cReply);
4105 blob_reset(&ans);
4106 if( cReply=='a' ){
4107 fossil_fatal("Amend aborted.");
4108 }
4109 if( cReply=='e' || (cReply!='c' && rc!=COMCK_PREVIEW) ){
4110 char *zPrior = blob_materialize(&comment);
4111 blob_init(&comment, 0, 0);
4112 prepare_amend_comment(&comment, zPrior, zUuid);
4113 fossil_free(zPrior);
4114 continue;
4115
--- src/json_config.c
+++ src/json_config.c
@@ -86,11 +86,10 @@
8686
{ "logo-image", CONFIGSET_SKIN },
8787
{ "background-mimetype", CONFIGSET_SKIN },
8888
{ "background-image", CONFIGSET_SKIN },
8989
{ "icon-mimetype", CONFIGSET_SKIN },
9090
{ "icon-image", CONFIGSET_SKIN },
91
-{ "timeline-block-markup", CONFIGSET_SKIN },
9291
{ "timeline-date-format", CONFIGSET_SKIN },
9392
{ "timeline-default-style", CONFIGSET_SKIN },
9493
{ "timeline-dwelltime", CONFIGSET_SKIN },
9594
{ "timeline-closetime", CONFIGSET_SKIN },
9695
{ "timeline-hard-newlines", CONFIGSET_SKIN },
9796
--- src/json_config.c
+++ src/json_config.c
@@ -86,11 +86,10 @@
86 { "logo-image", CONFIGSET_SKIN },
87 { "background-mimetype", CONFIGSET_SKIN },
88 { "background-image", CONFIGSET_SKIN },
89 { "icon-mimetype", CONFIGSET_SKIN },
90 { "icon-image", CONFIGSET_SKIN },
91 { "timeline-block-markup", CONFIGSET_SKIN },
92 { "timeline-date-format", CONFIGSET_SKIN },
93 { "timeline-default-style", CONFIGSET_SKIN },
94 { "timeline-dwelltime", CONFIGSET_SKIN },
95 { "timeline-closetime", CONFIGSET_SKIN },
96 { "timeline-hard-newlines", CONFIGSET_SKIN },
97
--- src/json_config.c
+++ src/json_config.c
@@ -86,11 +86,10 @@
86 { "logo-image", CONFIGSET_SKIN },
87 { "background-mimetype", CONFIGSET_SKIN },
88 { "background-image", CONFIGSET_SKIN },
89 { "icon-mimetype", CONFIGSET_SKIN },
90 { "icon-image", CONFIGSET_SKIN },
 
91 { "timeline-date-format", CONFIGSET_SKIN },
92 { "timeline-default-style", CONFIGSET_SKIN },
93 { "timeline-dwelltime", CONFIGSET_SKIN },
94 { "timeline-closetime", CONFIGSET_SKIN },
95 { "timeline-hard-newlines", CONFIGSET_SKIN },
96
+28 -5
--- src/main.c
+++ src/main.c
@@ -2542,12 +2542,16 @@
25422542
** setenv: NAME
25432543
**
25442544
** Sets environment variable NAME to VALUE. If VALUE is omitted, then
25452545
** the environment variable is unset.
25462546
*/
2547
- blob_token(&line,&value2);
2548
- fossil_setenv(blob_str(&value), blob_str(&value2));
2547
+ char *zValue;
2548
+ blob_tail(&line,&value2);
2549
+ blob_trim(&value2);
2550
+ zValue = blob_str(&value2);
2551
+ while( fossil_isspace(zValue[0]) ){ zValue++; }
2552
+ fossil_setenv(blob_str(&value), zValue);
25492553
blob_reset(&value);
25502554
blob_reset(&value2);
25512555
continue;
25522556
}
25532557
if( blob_eq(&key, "errorlog:") && blob_token(&line, &value) ){
@@ -3200,10 +3204,13 @@
32003204
** --chroot DIR Use directory for chroot instead of repository path
32013205
** --ckout-alias NAME Treat URIs of the form /doc/NAME/... as if they were
32023206
** /doc/ckout/...
32033207
** --create Create a new REPOSITORY if it does not already exist
32043208
** --errorlog FILE Append HTTP error messages to FILE
3209
+** --extpage FILE Shortcut for "--extroot DIR --page ext/TAIL" where
3210
+** DIR is the directory holding FILE and TAIL is the
3211
+** filename at the end of FILE. Only works for "ui".
32053212
** --extroot DIR Document root for the /ext extension mechanism
32063213
** --files GLOBLIST Comma-separated list of glob patterns for static files
32073214
** --fossilcmd PATH The pathname of the "fossil" executable on the remote
32083215
** system when REPOSITORY is remote.
32093216
** --from PATH Use PATH as the diff baseline for the /ckout page
@@ -3278,10 +3285,11 @@
32783285
int findServerArg = 2; /* argv index for find_server_repository() */
32793286
char *zRemote = 0; /* Remote host on which to run "fossil ui" */
32803287
const char *zJsMode; /* The --jsmode parameter */
32813288
const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */
32823289
const char *zFrom; /* Value for --from */
3290
+ const char *zExtPage = 0; /* Argument to --extpage */
32833291
32843292
32853293
#if USE_SEE
32863294
db_setup_for_saved_encryption_key();
32873295
#endif
@@ -3319,12 +3327,20 @@
33193327
zFrom = find_option("from", 0, 1);
33203328
if( zFrom && zFrom==file_tail(zFrom) ){
33213329
fossil_fatal("the argument to --from must be a pathname for"
33223330
" the \"ui\" command");
33233331
}
3324
- zInitPage = find_option("page", "p", 1);
3325
- if( zInitPage && zInitPage[0]=='/' ) zInitPage++;
3332
+ zExtPage = find_option("extpage",0,1);
3333
+ if( zExtPage ){
3334
+ char *zFullPath = file_canonical_name_dup(zExtPage);
3335
+ g.zExtRoot = file_dirname(zFullPath);
3336
+ zInitPage = mprintf("ext/%s",file_tail(zFullPath));
3337
+ fossil_free(zFullPath);
3338
+ }else{
3339
+ zInitPage = find_option("page", "p", 1);
3340
+ if( zInitPage && zInitPage[0]=='/' ) zInitPage++;
3341
+ }
33263342
zFossilCmd = find_option("fossilcmd", 0, 1);
33273343
if( zFrom && zInitPage==0 ){
33283344
zInitPage = mprintf("ckout?exbase=%H", zFrom);
33293345
}
33303346
}
@@ -3493,11 +3509,18 @@
34933509
}
34943510
blob_appendf(&ssh, " ui --nobrowser --localauth --port %d", iPort);
34953511
if( zNotFound ) blob_appendf(&ssh, " --notfound %!$", zNotFound);
34963512
if( zFileGlob ) blob_appendf(&ssh, " --files-urlenc %T", zFileGlob);
34973513
if( g.zCkoutAlias ) blob_appendf(&ssh," --ckout-alias %!$",g.zCkoutAlias);
3498
- if( g.zExtRoot ) blob_appendf(&ssh, " --extroot %$", g.zExtRoot);
3514
+ if( zExtPage ){
3515
+ if( !file_is_absolute_path(zExtPage) ){
3516
+ zExtPage = mprintf("%s/%s", g.argv[2], zExtPage);
3517
+ }
3518
+ blob_appendf(&ssh, " --extpage %$", zExtPage);
3519
+ }else if( g.zExtRoot ){
3520
+ blob_appendf(&ssh, " --extroot %$", g.zExtRoot);
3521
+ }
34993522
if( skin_in_use() ) blob_appendf(&ssh, " --skin %s", skin_in_use());
35003523
if( zJsMode ) blob_appendf(&ssh, " --jsmode %s", zJsMode);
35013524
if( fCreate ) blob_appendf(&ssh, " --create");
35023525
blob_appendf(&ssh, " %$", g.argv[2]);
35033526
if( isRetry ){
35043527
--- src/main.c
+++ src/main.c
@@ -2542,12 +2542,16 @@
2542 ** setenv: NAME
2543 **
2544 ** Sets environment variable NAME to VALUE. If VALUE is omitted, then
2545 ** the environment variable is unset.
2546 */
2547 blob_token(&line,&value2);
2548 fossil_setenv(blob_str(&value), blob_str(&value2));
 
 
 
 
2549 blob_reset(&value);
2550 blob_reset(&value2);
2551 continue;
2552 }
2553 if( blob_eq(&key, "errorlog:") && blob_token(&line, &value) ){
@@ -3200,10 +3204,13 @@
3200 ** --chroot DIR Use directory for chroot instead of repository path
3201 ** --ckout-alias NAME Treat URIs of the form /doc/NAME/... as if they were
3202 ** /doc/ckout/...
3203 ** --create Create a new REPOSITORY if it does not already exist
3204 ** --errorlog FILE Append HTTP error messages to FILE
 
 
 
3205 ** --extroot DIR Document root for the /ext extension mechanism
3206 ** --files GLOBLIST Comma-separated list of glob patterns for static files
3207 ** --fossilcmd PATH The pathname of the "fossil" executable on the remote
3208 ** system when REPOSITORY is remote.
3209 ** --from PATH Use PATH as the diff baseline for the /ckout page
@@ -3278,10 +3285,11 @@
3278 int findServerArg = 2; /* argv index for find_server_repository() */
3279 char *zRemote = 0; /* Remote host on which to run "fossil ui" */
3280 const char *zJsMode; /* The --jsmode parameter */
3281 const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */
3282 const char *zFrom; /* Value for --from */
 
3283
3284
3285 #if USE_SEE
3286 db_setup_for_saved_encryption_key();
3287 #endif
@@ -3319,12 +3327,20 @@
3319 zFrom = find_option("from", 0, 1);
3320 if( zFrom && zFrom==file_tail(zFrom) ){
3321 fossil_fatal("the argument to --from must be a pathname for"
3322 " the \"ui\" command");
3323 }
3324 zInitPage = find_option("page", "p", 1);
3325 if( zInitPage && zInitPage[0]=='/' ) zInitPage++;
 
 
 
 
 
 
 
 
3326 zFossilCmd = find_option("fossilcmd", 0, 1);
3327 if( zFrom && zInitPage==0 ){
3328 zInitPage = mprintf("ckout?exbase=%H", zFrom);
3329 }
3330 }
@@ -3493,11 +3509,18 @@
3493 }
3494 blob_appendf(&ssh, " ui --nobrowser --localauth --port %d", iPort);
3495 if( zNotFound ) blob_appendf(&ssh, " --notfound %!$", zNotFound);
3496 if( zFileGlob ) blob_appendf(&ssh, " --files-urlenc %T", zFileGlob);
3497 if( g.zCkoutAlias ) blob_appendf(&ssh," --ckout-alias %!$",g.zCkoutAlias);
3498 if( g.zExtRoot ) blob_appendf(&ssh, " --extroot %$", g.zExtRoot);
 
 
 
 
 
 
 
3499 if( skin_in_use() ) blob_appendf(&ssh, " --skin %s", skin_in_use());
3500 if( zJsMode ) blob_appendf(&ssh, " --jsmode %s", zJsMode);
3501 if( fCreate ) blob_appendf(&ssh, " --create");
3502 blob_appendf(&ssh, " %$", g.argv[2]);
3503 if( isRetry ){
3504
--- src/main.c
+++ src/main.c
@@ -2542,12 +2542,16 @@
2542 ** setenv: NAME
2543 **
2544 ** Sets environment variable NAME to VALUE. If VALUE is omitted, then
2545 ** the environment variable is unset.
2546 */
2547 char *zValue;
2548 blob_tail(&line,&value2);
2549 blob_trim(&value2);
2550 zValue = blob_str(&value2);
2551 while( fossil_isspace(zValue[0]) ){ zValue++; }
2552 fossil_setenv(blob_str(&value), zValue);
2553 blob_reset(&value);
2554 blob_reset(&value2);
2555 continue;
2556 }
2557 if( blob_eq(&key, "errorlog:") && blob_token(&line, &value) ){
@@ -3200,10 +3204,13 @@
3204 ** --chroot DIR Use directory for chroot instead of repository path
3205 ** --ckout-alias NAME Treat URIs of the form /doc/NAME/... as if they were
3206 ** /doc/ckout/...
3207 ** --create Create a new REPOSITORY if it does not already exist
3208 ** --errorlog FILE Append HTTP error messages to FILE
3209 ** --extpage FILE Shortcut for "--extroot DIR --page ext/TAIL" where
3210 ** DIR is the directory holding FILE and TAIL is the
3211 ** filename at the end of FILE. Only works for "ui".
3212 ** --extroot DIR Document root for the /ext extension mechanism
3213 ** --files GLOBLIST Comma-separated list of glob patterns for static files
3214 ** --fossilcmd PATH The pathname of the "fossil" executable on the remote
3215 ** system when REPOSITORY is remote.
3216 ** --from PATH Use PATH as the diff baseline for the /ckout page
@@ -3278,10 +3285,11 @@
3285 int findServerArg = 2; /* argv index for find_server_repository() */
3286 char *zRemote = 0; /* Remote host on which to run "fossil ui" */
3287 const char *zJsMode; /* The --jsmode parameter */
3288 const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */
3289 const char *zFrom; /* Value for --from */
3290 const char *zExtPage = 0; /* Argument to --extpage */
3291
3292
3293 #if USE_SEE
3294 db_setup_for_saved_encryption_key();
3295 #endif
@@ -3319,12 +3327,20 @@
3327 zFrom = find_option("from", 0, 1);
3328 if( zFrom && zFrom==file_tail(zFrom) ){
3329 fossil_fatal("the argument to --from must be a pathname for"
3330 " the \"ui\" command");
3331 }
3332 zExtPage = find_option("extpage",0,1);
3333 if( zExtPage ){
3334 char *zFullPath = file_canonical_name_dup(zExtPage);
3335 g.zExtRoot = file_dirname(zFullPath);
3336 zInitPage = mprintf("ext/%s",file_tail(zFullPath));
3337 fossil_free(zFullPath);
3338 }else{
3339 zInitPage = find_option("page", "p", 1);
3340 if( zInitPage && zInitPage[0]=='/' ) zInitPage++;
3341 }
3342 zFossilCmd = find_option("fossilcmd", 0, 1);
3343 if( zFrom && zInitPage==0 ){
3344 zInitPage = mprintf("ckout?exbase=%H", zFrom);
3345 }
3346 }
@@ -3493,11 +3509,18 @@
3509 }
3510 blob_appendf(&ssh, " ui --nobrowser --localauth --port %d", iPort);
3511 if( zNotFound ) blob_appendf(&ssh, " --notfound %!$", zNotFound);
3512 if( zFileGlob ) blob_appendf(&ssh, " --files-urlenc %T", zFileGlob);
3513 if( g.zCkoutAlias ) blob_appendf(&ssh," --ckout-alias %!$",g.zCkoutAlias);
3514 if( zExtPage ){
3515 if( !file_is_absolute_path(zExtPage) ){
3516 zExtPage = mprintf("%s/%s", g.argv[2], zExtPage);
3517 }
3518 blob_appendf(&ssh, " --extpage %$", zExtPage);
3519 }else if( g.zExtRoot ){
3520 blob_appendf(&ssh, " --extroot %$", g.zExtRoot);
3521 }
3522 if( skin_in_use() ) blob_appendf(&ssh, " --skin %s", skin_in_use());
3523 if( zJsMode ) blob_appendf(&ssh, " --jsmode %s", zJsMode);
3524 if( fCreate ) blob_appendf(&ssh, " --create");
3525 blob_appendf(&ssh, " %$", g.argv[2]);
3526 if( isRetry ){
3527
--- src/manifest.c
+++ src/manifest.c
@@ -2911,5 +2911,300 @@
29112911
if( g.argc!=3 ) usage("RECORDID");
29122912
rid = name_to_rid(g.argv[2]);
29132913
content_get(rid, &content);
29142914
manifest_crosslink(rid, &content, MC_NONE);
29152915
}
2916
+
2917
+/*
2918
+** For a given CATYPE_... value, returns a human-friendly name, or
2919
+** NULL if typeId is unknown or is CFTYPE_ANY. The names returned by
2920
+** this function are geared towards use with artifact_to_json(), and
2921
+** may differ from some historical uses. e.g. CFTYPE_CONTROL artifacts
2922
+** are called "tag" artifacts by this function.
2923
+*/
2924
+const char * artifact_type_to_name(int typeId){
2925
+ switch(typeId){
2926
+ case CFTYPE_MANIFEST: return "checkin";
2927
+ case CFTYPE_CLUSTER: return "cluster";
2928
+ case CFTYPE_CONTROL: return "tag";
2929
+ case CFTYPE_WIKI: return "wiki";
2930
+ case CFTYPE_TICKET: return "ticket";
2931
+ case CFTYPE_ATTACHMENT: return "attachment";
2932
+ case CFTYPE_EVENT: return "technote";
2933
+ case CFTYPE_FORUM: return "forumpost";
2934
+ }
2935
+ return NULL;
2936
+}
2937
+
2938
+/*
2939
+** Creates a JSON representation of p, appending it to b.
2940
+**
2941
+** b is not cleared before rendering, so the caller needs to do that
2942
+** if it's important for their use case.
2943
+**
2944
+** Pedantic note: this routine traverses p->aFile directly, rather
2945
+** than using manifest_file_next(), so that delta manifests are
2946
+** rendered as-is instead of containing their derived F-cards. If that
2947
+** policy is ever changed, p will need to be non-const.
2948
+*/
2949
+void artifact_to_json(Manifest const *p, Blob *b){
2950
+ int i;
2951
+
2952
+ blob_append_literal(b, "{");
2953
+ blob_appendf(b, "\"uuid\":\"%z\"", rid_to_uuid(p->rid));
2954
+ /*blob_appendf(b, ", \"rid\": %d", p->rid); not portable across repos*/
2955
+ blob_appendf(b, ",\"type\":%!j", artifact_type_to_name(p->type));
2956
+#define ISA(TYPE) if( p->type==TYPE )
2957
+#define CARD_LETTER(LETTER) \
2958
+ blob_append_literal(b, ",\"" #LETTER "\":")
2959
+#define CARD_STR(LETTER, VAL) \
2960
+ assert( VAL ); CARD_LETTER(LETTER); blob_appendf(b, "%!j", VAL)
2961
+#define CARD_STR2(LETTER, VAL) \
2962
+ if( VAL ) { CARD_STR(LETTER, VAL); } (void)0
2963
+#define STR_OR_NULL(VAL) \
2964
+ if( VAL ) blob_appendf(b, "%!j", VAL); \
2965
+ else blob_append(b, "null", 4)
2966
+#define KVP_STR(ADDCOMMA, KEY,VAL) \
2967
+ if(ADDCOMMA) blob_append_char(b, ','); \
2968
+ blob_appendf(b, "%!j:", #KEY); \
2969
+ STR_OR_NULL(VAL)
2970
+
2971
+ ISA( CFTYPE_ATTACHMENT ){
2972
+ CARD_LETTER(A);
2973
+ blob_append_char(b, '{');
2974
+ KVP_STR(0, filename, p->zAttachName);
2975
+ KVP_STR(1, target, p->zAttachTarget);
2976
+ KVP_STR(1, source, p->zAttachSrc);
2977
+ blob_append_char(b, '}');
2978
+ }
2979
+ CARD_STR2(B, p->zBaseline);
2980
+ CARD_STR2(C, p->zComment);
2981
+ CARD_LETTER(D); blob_appendf(b, "%f", p->rDate);
2982
+ ISA( CFTYPE_EVENT ){
2983
+ blob_appendf(b, ", \"E\":{\"time\":%f,\"id\":%!j}",
2984
+ p->rEventDate, p->zEventId);
2985
+ }
2986
+ ISA( CFTYPE_MANIFEST ){
2987
+ CARD_LETTER(F);
2988
+ blob_append_char(b, '[');
2989
+ for( i = 0; i < p->nFile; ++i ){
2990
+ ManifestFile const * const pF = &p->aFile[i];
2991
+ if( i>0 ) blob_append_char(b, ',');
2992
+ blob_append_char(b, '{');
2993
+ KVP_STR(0, name, pF->zName);
2994
+ KVP_STR(1, uuid, pF->zUuid);
2995
+ KVP_STR(1, perm, pF->zPerm);
2996
+ KVP_STR(1, rename, pF->zPrior);
2997
+ blob_append_char(b, '}');
2998
+ }
2999
+ /* Special case: model checkins with no F-card as having an empty
3000
+ ** array, rather than no F-cards, to hypothetically simplify
3001
+ ** handling in JSON queries. */
3002
+ blob_append_char(b, ']');
3003
+ }
3004
+ CARD_STR2(G, p->zThreadRoot);
3005
+ ISA( CFTYPE_FORUM ){
3006
+ CARD_LETTER(H);
3007
+ STR_OR_NULL( (p->zThreadTitle && *p->zThreadTitle) ? p->zThreadTitle : NULL);
3008
+ CARD_STR2(I, p->zInReplyTo);
3009
+ }
3010
+ if( p->nField ){
3011
+ CARD_LETTER(J);
3012
+ blob_append_char(b, '[');
3013
+ for( i = 0; i < p->nField; ++i ){
3014
+ const char * zName = p->aField[i].zName;
3015
+ if( i>0 ) blob_append_char(b, ',');
3016
+ blob_append_char(b, '{');
3017
+ KVP_STR(0, name, '+'==*zName ? &zName[1] : zName);
3018
+ KVP_STR(1, value, p->aField[i].zValue);
3019
+ blob_appendf(b, ",\"append\":%s", '+'==*zName ? "true" : "false");
3020
+ blob_append_char(b, '}');
3021
+ }
3022
+ blob_append_char(b, ']');
3023
+ }
3024
+ CARD_STR2(K, p->zTicketUuid);
3025
+ CARD_STR2(L, p->zWikiTitle);
3026
+ ISA( CFTYPE_CLUSTER ){
3027
+ CARD_LETTER(M);
3028
+ blob_append_char(b, '[');
3029
+ for( int i = 0; i < p->nCChild; ++i ){
3030
+ if( i>0 ) blob_append_char(b, ',');
3031
+ blob_appendf(b, "%!j", p->azCChild[i]);
3032
+ }
3033
+ blob_append_char(b, ']');
3034
+ }
3035
+ CARD_STR2(N, p->zMimetype);
3036
+ ISA( CFTYPE_MANIFEST || p->nParent>0 ){
3037
+ CARD_LETTER(P);
3038
+ blob_append_char(b, '[');
3039
+ for( i = 0; i < p->nParent; ++i ){
3040
+ if( i>0 ) blob_append_char(b, ',');
3041
+ blob_appendf(b, "%!j", p->azParent[i]);
3042
+ }
3043
+ /* Special case: model checkins with no P-card as having an empty
3044
+ ** array, as per F-cards. */
3045
+ blob_append_char(b, ']');
3046
+ }
3047
+ if( p->nCherrypick ){
3048
+ CARD_LETTER(Q);
3049
+ blob_append_char(b, '[');
3050
+ for( i = 0; i < p->nCherrypick; ++i ){
3051
+ if( i>0 ) blob_append_char(b, ',');
3052
+ blob_append_char(b, '{');
3053
+ blob_appendf(b, "\"type\":\"%c\"", p->aCherrypick[i].zCPTarget[0]);
3054
+ KVP_STR(1, target, &p->aCherrypick[i].zCPTarget[1]);
3055
+ KVP_STR(1, base, p->aCherrypick[i].zCPBase);
3056
+ blob_append_char(b, '}');
3057
+ }
3058
+ blob_append_char(b, ']');
3059
+ }
3060
+ CARD_STR2(R, p->zRepoCksum);
3061
+ if( p->nTag ){
3062
+ CARD_LETTER(T);
3063
+ blob_append_char(b, '[');
3064
+ for( int i = 0; i < p->nTag; ++i ){
3065
+ const char *zName = p->aTag[i].zName;
3066
+ if( i>0 ) blob_append_char(b, ',');
3067
+ blob_append_char(b, '{');
3068
+ blob_appendf(b, "\"type\":\"%c\"", *zName);
3069
+ KVP_STR(1, name, &zName[1]);
3070
+ KVP_STR(1, target, p->aTag[i].zUuid ? p->aTag[i].zUuid : "*")
3071
+ /* We could arguably resolve the "*" as null or p's uuid. */;
3072
+ KVP_STR(1, value, p->aTag[i].zValue);
3073
+ blob_append_char(b, '}');
3074
+ }
3075
+ blob_append_char(b, ']');
3076
+ }
3077
+ CARD_STR2(U, p->zUser);
3078
+ if( p->zWiki || CFTYPE_WIKI==p->type || CFTYPE_FORUM==p->type
3079
+ || CFTYPE_EVENT==p->type ){
3080
+ CARD_LETTER(W);
3081
+ STR_OR_NULL((p->zWiki && *p->zWiki) ? p->zWiki : NULL);
3082
+ }
3083
+ blob_append_literal(b, "}");
3084
+#undef CARD_FMT
3085
+#undef CARD_LETTER
3086
+#undef CARD_STR
3087
+#undef CARD_STR2
3088
+#undef ISA
3089
+#undef KVP_STR
3090
+#undef STR_OR_NULL
3091
+}
3092
+
3093
+/*
3094
+** Convenience wrapper around artifact_to_json() which expects rid to
3095
+** be the blob.rid of any artifact type. If it can load a Manifest
3096
+** with that rid, it returns rid, else it returns 0.
3097
+*/
3098
+int artifact_to_json_by_rid(int rid, Blob *pOut){
3099
+ Manifest * const p = manifest_get(rid, CFTYPE_ANY, 0);
3100
+ if( p ){
3101
+ artifact_to_json(p, pOut);
3102
+ manifest_destroy(p);
3103
+ }else{
3104
+ rid = 0;
3105
+ }
3106
+ return rid;
3107
+}
3108
+
3109
+/*
3110
+** Convenience wrapper around artifact_to_json() which accepts any
3111
+** artifact name which is legal for symbolic_name_to_rid(). On success
3112
+** it returns the rid of the artifact. Returns 0 if no such artifact
3113
+** exists and a negative value if the name is ambiguous.
3114
+**
3115
+** pOut is not cleared before rendering, so the caller needs to do
3116
+** that if it's important for their use case.
3117
+*/
3118
+int artifact_to_json_by_name(const char *zName, Blob *pOut){
3119
+ const int rid = symbolic_name_to_rid(zName, 0);
3120
+ return rid>0
3121
+ ? artifact_to_json_by_rid(rid, pOut)
3122
+ : rid;
3123
+}
3124
+
3125
+/*
3126
+** SQLite UDF for artifact_to_json(). Its single argument should be
3127
+** either an INTEGER (blob.rid value) or a TEXT symbolic artifact
3128
+** name, as per symbolic_name_to_rid(). If an artifact is found then
3129
+** the result of the UDF is that JSON as a string, else it evaluates
3130
+** to NULL.
3131
+*/
3132
+void artifact_to_json_sql_func(
3133
+ sqlite3_context *context,
3134
+ int argc,
3135
+ sqlite3_value **argv
3136
+){
3137
+ int rid = 0;
3138
+ Blob b = empty_blob;
3139
+
3140
+ if(1 != argc){
3141
+ goto error_usage;
3142
+ }
3143
+ switch( sqlite3_value_type(argv[0]) ){
3144
+ case SQLITE_INTEGER:
3145
+ rid = artifact_to_json_by_rid(sqlite3_value_int(argv[0]), &b);
3146
+ break;
3147
+ case SQLITE_TEXT:{
3148
+ const char * z = (const char *)sqlite3_value_text(argv[0]);
3149
+ if( z ){
3150
+ rid = artifact_to_json_by_name(z, &b);
3151
+ }
3152
+ break;
3153
+ }
3154
+ default:
3155
+ goto error_usage;
3156
+ }
3157
+ if( rid>0 ){
3158
+ sqlite3_result_text(context, blob_str(&b), blob_size(&b),
3159
+ SQLITE_TRANSIENT);
3160
+ blob_reset(&b);
3161
+ }else{
3162
+ /* We should arguably error out if rid<0 (ambiguous name) */
3163
+ sqlite3_result_null(context);
3164
+ }
3165
+ return;
3166
+error_usage:
3167
+ sqlite3_result_error(context, "Expecting one argument: blob.rid or "
3168
+ "artifact symbolic name", -1);
3169
+}
3170
+
3171
+
3172
+
3173
+/*
3174
+** COMMAND: test-artifact-to-json
3175
+**
3176
+** Usage: %fossil test-artifact-to-json ?-pretty|-p? symbolic-name [...names]
3177
+**
3178
+** Tests the artifact_to_json() and artifact_to_json_by_name() APIs.
3179
+*/
3180
+void test_manifest_to_json(void){
3181
+ int i;
3182
+ Blob b = empty_blob;
3183
+ Stmt q;
3184
+ const int bPretty = find_option("pretty","p",0)!=0;
3185
+ int nErr = 0;
3186
+
3187
+ db_find_and_open_repository(0,0);
3188
+ db_prepare(&q, "select json_pretty(:json)");
3189
+ for( i=2; i<g.argc; ++i ){
3190
+ char const *zName = g.argv[i];
3191
+ const int rc = artifact_to_json_by_name(zName, &b);
3192
+ if( rc<=0 ){
3193
+ ++nErr;
3194
+ fossil_warning("Error reading artifact %Q", zName);
3195
+ continue;
3196
+ }else if( bPretty ){
3197
+ db_bind_blob(&q, ":json", &b);
3198
+ b.nUsed = 0;
3199
+ db_step(&q);
3200
+ db_column_blob(&q, 0, &b);
3201
+ db_reset(&q);
3202
+ }
3203
+ fossil_print("%b\n", &b);
3204
+ blob_reset(&b);
3205
+ }
3206
+ db_finalize(&q);
3207
+ if( nErr ){
3208
+ fossil_warning("Error count: %d", nErr);
3209
+ }
3210
+}
29163211
--- src/manifest.c
+++ src/manifest.c
@@ -2911,5 +2911,300 @@
2911 if( g.argc!=3 ) usage("RECORDID");
2912 rid = name_to_rid(g.argv[2]);
2913 content_get(rid, &content);
2914 manifest_crosslink(rid, &content, MC_NONE);
2915 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2916
--- src/manifest.c
+++ src/manifest.c
@@ -2911,5 +2911,300 @@
2911 if( g.argc!=3 ) usage("RECORDID");
2912 rid = name_to_rid(g.argv[2]);
2913 content_get(rid, &content);
2914 manifest_crosslink(rid, &content, MC_NONE);
2915 }
2916
2917 /*
2918 ** For a given CATYPE_... value, returns a human-friendly name, or
2919 ** NULL if typeId is unknown or is CFTYPE_ANY. The names returned by
2920 ** this function are geared towards use with artifact_to_json(), and
2921 ** may differ from some historical uses. e.g. CFTYPE_CONTROL artifacts
2922 ** are called "tag" artifacts by this function.
2923 */
2924 const char * artifact_type_to_name(int typeId){
2925 switch(typeId){
2926 case CFTYPE_MANIFEST: return "checkin";
2927 case CFTYPE_CLUSTER: return "cluster";
2928 case CFTYPE_CONTROL: return "tag";
2929 case CFTYPE_WIKI: return "wiki";
2930 case CFTYPE_TICKET: return "ticket";
2931 case CFTYPE_ATTACHMENT: return "attachment";
2932 case CFTYPE_EVENT: return "technote";
2933 case CFTYPE_FORUM: return "forumpost";
2934 }
2935 return NULL;
2936 }
2937
2938 /*
2939 ** Creates a JSON representation of p, appending it to b.
2940 **
2941 ** b is not cleared before rendering, so the caller needs to do that
2942 ** if it's important for their use case.
2943 **
2944 ** Pedantic note: this routine traverses p->aFile directly, rather
2945 ** than using manifest_file_next(), so that delta manifests are
2946 ** rendered as-is instead of containing their derived F-cards. If that
2947 ** policy is ever changed, p will need to be non-const.
2948 */
2949 void artifact_to_json(Manifest const *p, Blob *b){
2950 int i;
2951
2952 blob_append_literal(b, "{");
2953 blob_appendf(b, "\"uuid\":\"%z\"", rid_to_uuid(p->rid));
2954 /*blob_appendf(b, ", \"rid\": %d", p->rid); not portable across repos*/
2955 blob_appendf(b, ",\"type\":%!j", artifact_type_to_name(p->type));
2956 #define ISA(TYPE) if( p->type==TYPE )
2957 #define CARD_LETTER(LETTER) \
2958 blob_append_literal(b, ",\"" #LETTER "\":")
2959 #define CARD_STR(LETTER, VAL) \
2960 assert( VAL ); CARD_LETTER(LETTER); blob_appendf(b, "%!j", VAL)
2961 #define CARD_STR2(LETTER, VAL) \
2962 if( VAL ) { CARD_STR(LETTER, VAL); } (void)0
2963 #define STR_OR_NULL(VAL) \
2964 if( VAL ) blob_appendf(b, "%!j", VAL); \
2965 else blob_append(b, "null", 4)
2966 #define KVP_STR(ADDCOMMA, KEY,VAL) \
2967 if(ADDCOMMA) blob_append_char(b, ','); \
2968 blob_appendf(b, "%!j:", #KEY); \
2969 STR_OR_NULL(VAL)
2970
2971 ISA( CFTYPE_ATTACHMENT ){
2972 CARD_LETTER(A);
2973 blob_append_char(b, '{');
2974 KVP_STR(0, filename, p->zAttachName);
2975 KVP_STR(1, target, p->zAttachTarget);
2976 KVP_STR(1, source, p->zAttachSrc);
2977 blob_append_char(b, '}');
2978 }
2979 CARD_STR2(B, p->zBaseline);
2980 CARD_STR2(C, p->zComment);
2981 CARD_LETTER(D); blob_appendf(b, "%f", p->rDate);
2982 ISA( CFTYPE_EVENT ){
2983 blob_appendf(b, ", \"E\":{\"time\":%f,\"id\":%!j}",
2984 p->rEventDate, p->zEventId);
2985 }
2986 ISA( CFTYPE_MANIFEST ){
2987 CARD_LETTER(F);
2988 blob_append_char(b, '[');
2989 for( i = 0; i < p->nFile; ++i ){
2990 ManifestFile const * const pF = &p->aFile[i];
2991 if( i>0 ) blob_append_char(b, ',');
2992 blob_append_char(b, '{');
2993 KVP_STR(0, name, pF->zName);
2994 KVP_STR(1, uuid, pF->zUuid);
2995 KVP_STR(1, perm, pF->zPerm);
2996 KVP_STR(1, rename, pF->zPrior);
2997 blob_append_char(b, '}');
2998 }
2999 /* Special case: model checkins with no F-card as having an empty
3000 ** array, rather than no F-cards, to hypothetically simplify
3001 ** handling in JSON queries. */
3002 blob_append_char(b, ']');
3003 }
3004 CARD_STR2(G, p->zThreadRoot);
3005 ISA( CFTYPE_FORUM ){
3006 CARD_LETTER(H);
3007 STR_OR_NULL( (p->zThreadTitle && *p->zThreadTitle) ? p->zThreadTitle : NULL);
3008 CARD_STR2(I, p->zInReplyTo);
3009 }
3010 if( p->nField ){
3011 CARD_LETTER(J);
3012 blob_append_char(b, '[');
3013 for( i = 0; i < p->nField; ++i ){
3014 const char * zName = p->aField[i].zName;
3015 if( i>0 ) blob_append_char(b, ',');
3016 blob_append_char(b, '{');
3017 KVP_STR(0, name, '+'==*zName ? &zName[1] : zName);
3018 KVP_STR(1, value, p->aField[i].zValue);
3019 blob_appendf(b, ",\"append\":%s", '+'==*zName ? "true" : "false");
3020 blob_append_char(b, '}');
3021 }
3022 blob_append_char(b, ']');
3023 }
3024 CARD_STR2(K, p->zTicketUuid);
3025 CARD_STR2(L, p->zWikiTitle);
3026 ISA( CFTYPE_CLUSTER ){
3027 CARD_LETTER(M);
3028 blob_append_char(b, '[');
3029 for( int i = 0; i < p->nCChild; ++i ){
3030 if( i>0 ) blob_append_char(b, ',');
3031 blob_appendf(b, "%!j", p->azCChild[i]);
3032 }
3033 blob_append_char(b, ']');
3034 }
3035 CARD_STR2(N, p->zMimetype);
3036 ISA( CFTYPE_MANIFEST || p->nParent>0 ){
3037 CARD_LETTER(P);
3038 blob_append_char(b, '[');
3039 for( i = 0; i < p->nParent; ++i ){
3040 if( i>0 ) blob_append_char(b, ',');
3041 blob_appendf(b, "%!j", p->azParent[i]);
3042 }
3043 /* Special case: model checkins with no P-card as having an empty
3044 ** array, as per F-cards. */
3045 blob_append_char(b, ']');
3046 }
3047 if( p->nCherrypick ){
3048 CARD_LETTER(Q);
3049 blob_append_char(b, '[');
3050 for( i = 0; i < p->nCherrypick; ++i ){
3051 if( i>0 ) blob_append_char(b, ',');
3052 blob_append_char(b, '{');
3053 blob_appendf(b, "\"type\":\"%c\"", p->aCherrypick[i].zCPTarget[0]);
3054 KVP_STR(1, target, &p->aCherrypick[i].zCPTarget[1]);
3055 KVP_STR(1, base, p->aCherrypick[i].zCPBase);
3056 blob_append_char(b, '}');
3057 }
3058 blob_append_char(b, ']');
3059 }
3060 CARD_STR2(R, p->zRepoCksum);
3061 if( p->nTag ){
3062 CARD_LETTER(T);
3063 blob_append_char(b, '[');
3064 for( int i = 0; i < p->nTag; ++i ){
3065 const char *zName = p->aTag[i].zName;
3066 if( i>0 ) blob_append_char(b, ',');
3067 blob_append_char(b, '{');
3068 blob_appendf(b, "\"type\":\"%c\"", *zName);
3069 KVP_STR(1, name, &zName[1]);
3070 KVP_STR(1, target, p->aTag[i].zUuid ? p->aTag[i].zUuid : "*")
3071 /* We could arguably resolve the "*" as null or p's uuid. */;
3072 KVP_STR(1, value, p->aTag[i].zValue);
3073 blob_append_char(b, '}');
3074 }
3075 blob_append_char(b, ']');
3076 }
3077 CARD_STR2(U, p->zUser);
3078 if( p->zWiki || CFTYPE_WIKI==p->type || CFTYPE_FORUM==p->type
3079 || CFTYPE_EVENT==p->type ){
3080 CARD_LETTER(W);
3081 STR_OR_NULL((p->zWiki && *p->zWiki) ? p->zWiki : NULL);
3082 }
3083 blob_append_literal(b, "}");
3084 #undef CARD_FMT
3085 #undef CARD_LETTER
3086 #undef CARD_STR
3087 #undef CARD_STR2
3088 #undef ISA
3089 #undef KVP_STR
3090 #undef STR_OR_NULL
3091 }
3092
3093 /*
3094 ** Convenience wrapper around artifact_to_json() which expects rid to
3095 ** be the blob.rid of any artifact type. If it can load a Manifest
3096 ** with that rid, it returns rid, else it returns 0.
3097 */
3098 int artifact_to_json_by_rid(int rid, Blob *pOut){
3099 Manifest * const p = manifest_get(rid, CFTYPE_ANY, 0);
3100 if( p ){
3101 artifact_to_json(p, pOut);
3102 manifest_destroy(p);
3103 }else{
3104 rid = 0;
3105 }
3106 return rid;
3107 }
3108
3109 /*
3110 ** Convenience wrapper around artifact_to_json() which accepts any
3111 ** artifact name which is legal for symbolic_name_to_rid(). On success
3112 ** it returns the rid of the artifact. Returns 0 if no such artifact
3113 ** exists and a negative value if the name is ambiguous.
3114 **
3115 ** pOut is not cleared before rendering, so the caller needs to do
3116 ** that if it's important for their use case.
3117 */
3118 int artifact_to_json_by_name(const char *zName, Blob *pOut){
3119 const int rid = symbolic_name_to_rid(zName, 0);
3120 return rid>0
3121 ? artifact_to_json_by_rid(rid, pOut)
3122 : rid;
3123 }
3124
3125 /*
3126 ** SQLite UDF for artifact_to_json(). Its single argument should be
3127 ** either an INTEGER (blob.rid value) or a TEXT symbolic artifact
3128 ** name, as per symbolic_name_to_rid(). If an artifact is found then
3129 ** the result of the UDF is that JSON as a string, else it evaluates
3130 ** to NULL.
3131 */
3132 void artifact_to_json_sql_func(
3133 sqlite3_context *context,
3134 int argc,
3135 sqlite3_value **argv
3136 ){
3137 int rid = 0;
3138 Blob b = empty_blob;
3139
3140 if(1 != argc){
3141 goto error_usage;
3142 }
3143 switch( sqlite3_value_type(argv[0]) ){
3144 case SQLITE_INTEGER:
3145 rid = artifact_to_json_by_rid(sqlite3_value_int(argv[0]), &b);
3146 break;
3147 case SQLITE_TEXT:{
3148 const char * z = (const char *)sqlite3_value_text(argv[0]);
3149 if( z ){
3150 rid = artifact_to_json_by_name(z, &b);
3151 }
3152 break;
3153 }
3154 default:
3155 goto error_usage;
3156 }
3157 if( rid>0 ){
3158 sqlite3_result_text(context, blob_str(&b), blob_size(&b),
3159 SQLITE_TRANSIENT);
3160 blob_reset(&b);
3161 }else{
3162 /* We should arguably error out if rid<0 (ambiguous name) */
3163 sqlite3_result_null(context);
3164 }
3165 return;
3166 error_usage:
3167 sqlite3_result_error(context, "Expecting one argument: blob.rid or "
3168 "artifact symbolic name", -1);
3169 }
3170
3171
3172
3173 /*
3174 ** COMMAND: test-artifact-to-json
3175 **
3176 ** Usage: %fossil test-artifact-to-json ?-pretty|-p? symbolic-name [...names]
3177 **
3178 ** Tests the artifact_to_json() and artifact_to_json_by_name() APIs.
3179 */
3180 void test_manifest_to_json(void){
3181 int i;
3182 Blob b = empty_blob;
3183 Stmt q;
3184 const int bPretty = find_option("pretty","p",0)!=0;
3185 int nErr = 0;
3186
3187 db_find_and_open_repository(0,0);
3188 db_prepare(&q, "select json_pretty(:json)");
3189 for( i=2; i<g.argc; ++i ){
3190 char const *zName = g.argv[i];
3191 const int rc = artifact_to_json_by_name(zName, &b);
3192 if( rc<=0 ){
3193 ++nErr;
3194 fossil_warning("Error reading artifact %Q", zName);
3195 continue;
3196 }else if( bPretty ){
3197 db_bind_blob(&q, ":json", &b);
3198 b.nUsed = 0;
3199 db_step(&q);
3200 db_column_blob(&q, 0, &b);
3201 db_reset(&q);
3202 }
3203 fossil_print("%b\n", &b);
3204 blob_reset(&b);
3205 }
3206 db_finalize(&q);
3207 if( nErr ){
3208 fossil_warning("Error count: %d", nErr);
3209 }
3210 }
3211
+56 -1
--- src/name.c
+++ src/name.c
@@ -70,11 +70,11 @@
7070
** ^^^^--- Added
7171
**
7272
** 202503171234 -> 2025-03-17 12:34:59.999
7373
** ^^^^^^^--- Added
7474
** 20250317 -> 2025-03-17 23:59:59.999
75
-** ^^^^^^^^^^^^--- Added
75
+** ^^^^^^^^^^^^^--- Added
7676
**
7777
** If the bVerifyNotAHash flag is true, then a check is made to see if
7878
** the input string is a hash prefix and NULL is returned if it is. If the
7979
** bVerifyNotAHash flag is false, then the result is determined by syntax
8080
** of the input string only, without reference to the artifact table.
@@ -718,10 +718,65 @@
718718
rid = symbolic_name_to_rid(zNew,zType);
719719
fossil_free(zNew);
720720
}
721721
return rid;
722722
}
723
+
724
+/*
725
+** Convert a symbolic name used as an argument to the a=, b=, or c=
726
+** query parameters of timeline into a julianday mtime value.
727
+**
728
+** If pzDisplay is not null, then display text for the symbolic name might
729
+** be written into *pzDisplay. But that is not guaranteed.
730
+**
731
+** If bRoundUp is true and the symbolic name is a timestamp with less
732
+** than millisecond resolution, then the timestamp is rounding up to the
733
+** largest millisecond consistent with that timestamp. If bRoundUp is
734
+** false, then the resulting time is obtained by extending the timestamp
735
+** with zeros (hence rounding down). Use bRoundUp==1 if the result
736
+** will be used in mtime<=$RESULT and use bRoundUp==0 if the result
737
+** will be used in mtime>=$RESULT.
738
+*/
739
+double symbolic_name_to_mtime(
740
+ const char *z, /* Input symbolic name */
741
+ const char **pzDisplay, /* Perhaps write display text here, if not NULL */
742
+ int bRoundUp /* Round up if true */
743
+){
744
+ double mtime;
745
+ int rid;
746
+ const char *zDate;
747
+ if( z==0 ) return -1.0;
748
+ if( fossil_isdate(z) ){
749
+ mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z);
750
+ if( mtime>0.0 ) return mtime;
751
+ }
752
+ zDate = fossil_expand_datetime(z, 1, bRoundUp);
753
+ if( zDate!=0 ){
754
+ mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())",
755
+ bRoundUp ? fossil_roundup_date(zDate) : zDate);
756
+ if( mtime>0.0 ){
757
+ if( pzDisplay ){
758
+ zDate = fossil_expand_datetime(z,0,0);
759
+ *pzDisplay = fossil_strdup(zDate);
760
+ }
761
+ return mtime;
762
+ }
763
+ }
764
+ rid = symbolic_name_to_rid(z, "*");
765
+ if( rid ){
766
+ mtime = mtime_of_rid(rid, 0.0);
767
+ }else{
768
+ mtime = db_double(-1.0,
769
+ "SELECT max(event.mtime) FROM event, tag, tagxref"
770
+ " WHERE tag.tagname GLOB 'event-%q*'"
771
+ " AND tagxref.tagid=tag.tagid AND tagxref.tagtype"
772
+ " AND event.objid=tagxref.rid",
773
+ z
774
+ );
775
+ }
776
+ return mtime;
777
+}
723778
724779
/*
725780
** This routine takes a user-entered string and tries to convert it to
726781
** an artifact hash.
727782
**
728783
--- src/name.c
+++ src/name.c
@@ -70,11 +70,11 @@
70 ** ^^^^--- Added
71 **
72 ** 202503171234 -> 2025-03-17 12:34:59.999
73 ** ^^^^^^^--- Added
74 ** 20250317 -> 2025-03-17 23:59:59.999
75 ** ^^^^^^^^^^^^--- Added
76 **
77 ** If the bVerifyNotAHash flag is true, then a check is made to see if
78 ** the input string is a hash prefix and NULL is returned if it is. If the
79 ** bVerifyNotAHash flag is false, then the result is determined by syntax
80 ** of the input string only, without reference to the artifact table.
@@ -718,10 +718,65 @@
718 rid = symbolic_name_to_rid(zNew,zType);
719 fossil_free(zNew);
720 }
721 return rid;
722 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
723
724 /*
725 ** This routine takes a user-entered string and tries to convert it to
726 ** an artifact hash.
727 **
728
--- src/name.c
+++ src/name.c
@@ -70,11 +70,11 @@
70 ** ^^^^--- Added
71 **
72 ** 202503171234 -> 2025-03-17 12:34:59.999
73 ** ^^^^^^^--- Added
74 ** 20250317 -> 2025-03-17 23:59:59.999
75 ** ^^^^^^^^^^^^^--- Added
76 **
77 ** If the bVerifyNotAHash flag is true, then a check is made to see if
78 ** the input string is a hash prefix and NULL is returned if it is. If the
79 ** bVerifyNotAHash flag is false, then the result is determined by syntax
80 ** of the input string only, without reference to the artifact table.
@@ -718,10 +718,65 @@
718 rid = symbolic_name_to_rid(zNew,zType);
719 fossil_free(zNew);
720 }
721 return rid;
722 }
723
724 /*
725 ** Convert a symbolic name used as an argument to the a=, b=, or c=
726 ** query parameters of timeline into a julianday mtime value.
727 **
728 ** If pzDisplay is not null, then display text for the symbolic name might
729 ** be written into *pzDisplay. But that is not guaranteed.
730 **
731 ** If bRoundUp is true and the symbolic name is a timestamp with less
732 ** than millisecond resolution, then the timestamp is rounding up to the
733 ** largest millisecond consistent with that timestamp. If bRoundUp is
734 ** false, then the resulting time is obtained by extending the timestamp
735 ** with zeros (hence rounding down). Use bRoundUp==1 if the result
736 ** will be used in mtime<=$RESULT and use bRoundUp==0 if the result
737 ** will be used in mtime>=$RESULT.
738 */
739 double symbolic_name_to_mtime(
740 const char *z, /* Input symbolic name */
741 const char **pzDisplay, /* Perhaps write display text here, if not NULL */
742 int bRoundUp /* Round up if true */
743 ){
744 double mtime;
745 int rid;
746 const char *zDate;
747 if( z==0 ) return -1.0;
748 if( fossil_isdate(z) ){
749 mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z);
750 if( mtime>0.0 ) return mtime;
751 }
752 zDate = fossil_expand_datetime(z, 1, bRoundUp);
753 if( zDate!=0 ){
754 mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())",
755 bRoundUp ? fossil_roundup_date(zDate) : zDate);
756 if( mtime>0.0 ){
757 if( pzDisplay ){
758 zDate = fossil_expand_datetime(z,0,0);
759 *pzDisplay = fossil_strdup(zDate);
760 }
761 return mtime;
762 }
763 }
764 rid = symbolic_name_to_rid(z, "*");
765 if( rid ){
766 mtime = mtime_of_rid(rid, 0.0);
767 }else{
768 mtime = db_double(-1.0,
769 "SELECT max(event.mtime) FROM event, tag, tagxref"
770 " WHERE tag.tagname GLOB 'event-%q*'"
771 " AND tagxref.tagid=tag.tagid AND tagxref.tagtype"
772 " AND event.objid=tagxref.rid",
773 z
774 );
775 }
776 return mtime;
777 }
778
779 /*
780 ** This routine takes a user-entered string and tries to convert it to
781 ** an artifact hash.
782 **
783
+9 -24
--- src/printf.c
+++ src/printf.c
@@ -248,18 +248,10 @@
248248
** If enabled, no wiki-formatting is done for timeline comment messages.
249249
** Hyperlinks are activated, but they show up on screen using the
250250
** complete input text, not just the display text. No other formatting
251251
** is done.
252252
*/
253
-/*
254
-** SETTING: timeline-block-markup boolean default=off
255
-**
256
-** If enabled, block markup (paragraph brakes, tables, lists, headings, etc)
257
-** is enabled while rendering check-in comment message on the timeline.
258
-** This is disabled by default, because the timeline works best if the
259
-** check-in comments are short and do not take up too much vertical space.
260
-*/
261253
/*
262254
** SETTING: timeline-hard-newlines boolean default=off
263255
**
264256
** If enabled, the timeline honors newline characters in check-in comments.
265257
** In other words, newlines are coverted into <br> for HTML display.
@@ -271,36 +263,29 @@
271263
** Return an appropriate set of flags for wiki_convert() for displaying
272264
** comments on a timeline. These flag settings are determined by
273265
** configuration parameters.
274266
**
275267
** The altForm2 argument is true for "%!W" (with the "!" alternate-form-2
276
-** flags) and is false for plain "%W". The ! indicates that the text is
277
-** to be rendered on a form rather than the timeline and that block markup
278
-** is acceptable even if the "timeline-block-markup" setting is false.
268
+** flags) and is false for plain "%W". The ! flag indicates that the
269
+** formatting is for display of a check-in comment on the timeline. Such
270
+** comments used to be renderedd differently, but ever since 2020, they
271
+** have been rendered identially, so the ! flag does not make any different
272
+** in the output any more.
279273
*/
280
-static int wiki_convert_flags(int altForm2){
274
+int wiki_convert_flags(int altForm2){
281275
static int wikiFlags = 0;
276
+ (void)altForm2;
282277
if( wikiFlags==0 ){
283
- if( db_get_boolean("timeline-block-markup", 0) ){
284
- wikiFlags = WIKI_INLINE | WIKI_NOBADLINKS;
285
- }else{
286
- wikiFlags = WIKI_INLINE | WIKI_NOBLOCK | WIKI_NOBADLINKS;
287
- }
278
+ wikiFlags = WIKI_INLINE | WIKI_NOBADLINKS;
288279
if( db_get_boolean("timeline-plaintext", 0) ){
289280
wikiFlags |= WIKI_LINKSONLY;
290281
}
291282
if( db_get_boolean("timeline-hard-newlines", 0) ){
292283
wikiFlags |= WIKI_NEWLINE;
293284
}
294285
}
295
- if( altForm2 ){
296
- /* block markup (ex: <p>, <table>) allowed */
297
- return wikiFlags & ~WIKI_NOBLOCK;
298
- }else{
299
- /* Do not allow any block format. Everything in a <span> */
300
- return wikiFlags;
301
- }
286
+ return wikiFlags;
302287
}
303288
304289
305290
306291
/*
307292
--- src/printf.c
+++ src/printf.c
@@ -248,18 +248,10 @@
248 ** If enabled, no wiki-formatting is done for timeline comment messages.
249 ** Hyperlinks are activated, but they show up on screen using the
250 ** complete input text, not just the display text. No other formatting
251 ** is done.
252 */
253 /*
254 ** SETTING: timeline-block-markup boolean default=off
255 **
256 ** If enabled, block markup (paragraph brakes, tables, lists, headings, etc)
257 ** is enabled while rendering check-in comment message on the timeline.
258 ** This is disabled by default, because the timeline works best if the
259 ** check-in comments are short and do not take up too much vertical space.
260 */
261 /*
262 ** SETTING: timeline-hard-newlines boolean default=off
263 **
264 ** If enabled, the timeline honors newline characters in check-in comments.
265 ** In other words, newlines are coverted into <br> for HTML display.
@@ -271,36 +263,29 @@
271 ** Return an appropriate set of flags for wiki_convert() for displaying
272 ** comments on a timeline. These flag settings are determined by
273 ** configuration parameters.
274 **
275 ** The altForm2 argument is true for "%!W" (with the "!" alternate-form-2
276 ** flags) and is false for plain "%W". The ! indicates that the text is
277 ** to be rendered on a form rather than the timeline and that block markup
278 ** is acceptable even if the "timeline-block-markup" setting is false.
 
 
279 */
280 static int wiki_convert_flags(int altForm2){
281 static int wikiFlags = 0;
 
282 if( wikiFlags==0 ){
283 if( db_get_boolean("timeline-block-markup", 0) ){
284 wikiFlags = WIKI_INLINE | WIKI_NOBADLINKS;
285 }else{
286 wikiFlags = WIKI_INLINE | WIKI_NOBLOCK | WIKI_NOBADLINKS;
287 }
288 if( db_get_boolean("timeline-plaintext", 0) ){
289 wikiFlags |= WIKI_LINKSONLY;
290 }
291 if( db_get_boolean("timeline-hard-newlines", 0) ){
292 wikiFlags |= WIKI_NEWLINE;
293 }
294 }
295 if( altForm2 ){
296 /* block markup (ex: <p>, <table>) allowed */
297 return wikiFlags & ~WIKI_NOBLOCK;
298 }else{
299 /* Do not allow any block format. Everything in a <span> */
300 return wikiFlags;
301 }
302 }
303
304
305
306 /*
307
--- src/printf.c
+++ src/printf.c
@@ -248,18 +248,10 @@
248 ** If enabled, no wiki-formatting is done for timeline comment messages.
249 ** Hyperlinks are activated, but they show up on screen using the
250 ** complete input text, not just the display text. No other formatting
251 ** is done.
252 */
 
 
 
 
 
 
 
 
253 /*
254 ** SETTING: timeline-hard-newlines boolean default=off
255 **
256 ** If enabled, the timeline honors newline characters in check-in comments.
257 ** In other words, newlines are coverted into <br> for HTML display.
@@ -271,36 +263,29 @@
263 ** Return an appropriate set of flags for wiki_convert() for displaying
264 ** comments on a timeline. These flag settings are determined by
265 ** configuration parameters.
266 **
267 ** The altForm2 argument is true for "%!W" (with the "!" alternate-form-2
268 ** flags) and is false for plain "%W". The ! flag indicates that the
269 ** formatting is for display of a check-in comment on the timeline. Such
270 ** comments used to be renderedd differently, but ever since 2020, they
271 ** have been rendered identially, so the ! flag does not make any different
272 ** in the output any more.
273 */
274 int wiki_convert_flags(int altForm2){
275 static int wikiFlags = 0;
276 (void)altForm2;
277 if( wikiFlags==0 ){
278 wikiFlags = WIKI_INLINE | WIKI_NOBADLINKS;
 
 
 
 
279 if( db_get_boolean("timeline-plaintext", 0) ){
280 wikiFlags |= WIKI_LINKSONLY;
281 }
282 if( db_get_boolean("timeline-hard-newlines", 0) ){
283 wikiFlags |= WIKI_NEWLINE;
284 }
285 }
286 return wikiFlags;
 
 
 
 
 
 
287 }
288
289
290
291 /*
292
+3 -2
--- src/repolist.c
+++ src/repolist.c
@@ -316,20 +316,21 @@
316316
style_header("Repository List");
317317
@ %s(blob_str(&html))
318318
style_table_sorter();
319319
style_finish_page();
320320
}else{
321
+ const char *zTitle = PD("FOSSIL_REPOLIST_TITLE","Repository List");
321322
/* If no repositories were found that had the "repolist_skin"
322323
** property set, then use a default skin */
323324
@ <html>
324325
@ <head>
325326
@ <base href="%s(g.zBaseURL)/">
326327
@ <meta name="viewport" content="width=device-width, initial-scale=1.0">
327
- @ <title>Repository List</title>
328
+ @ <title>%h(zTitle)</title>
328329
@ </head>
329330
@ <body>
330
- @ <h1 align="center">Fossil Repositories</h1>
331
+ @ <h1 align="center">%h(zTitle)</h1>
331332
@ %s(blob_str(&html))
332333
@ <script>%s(builtin_text("sorttable.js"))</script>
333334
@ </body>
334335
@ </html>
335336
}
336337
--- src/repolist.c
+++ src/repolist.c
@@ -316,20 +316,21 @@
316 style_header("Repository List");
317 @ %s(blob_str(&html))
318 style_table_sorter();
319 style_finish_page();
320 }else{
 
321 /* If no repositories were found that had the "repolist_skin"
322 ** property set, then use a default skin */
323 @ <html>
324 @ <head>
325 @ <base href="%s(g.zBaseURL)/">
326 @ <meta name="viewport" content="width=device-width, initial-scale=1.0">
327 @ <title>Repository List</title>
328 @ </head>
329 @ <body>
330 @ <h1 align="center">Fossil Repositories</h1>
331 @ %s(blob_str(&html))
332 @ <script>%s(builtin_text("sorttable.js"))</script>
333 @ </body>
334 @ </html>
335 }
336
--- src/repolist.c
+++ src/repolist.c
@@ -316,20 +316,21 @@
316 style_header("Repository List");
317 @ %s(blob_str(&html))
318 style_table_sorter();
319 style_finish_page();
320 }else{
321 const char *zTitle = PD("FOSSIL_REPOLIST_TITLE","Repository List");
322 /* If no repositories were found that had the "repolist_skin"
323 ** property set, then use a default skin */
324 @ <html>
325 @ <head>
326 @ <base href="%s(g.zBaseURL)/">
327 @ <meta name="viewport" content="width=device-width, initial-scale=1.0">
328 @ <title>%h(zTitle)</title>
329 @ </head>
330 @ <body>
331 @ <h1 align="center">%h(zTitle)</h1>
332 @ %s(blob_str(&html))
333 @ <script>%s(builtin_text("sorttable.js"))</script>
334 @ </body>
335 @ </html>
336 }
337
+9 -95
--- src/search.c
+++ src/search.c
@@ -559,93 +559,10 @@
559559
search_body_sqlfunc, 0, 0);
560560
sqlite3_create_function(db, "urlencode", 1, enc, 0,
561561
search_urlencode_sqlfunc, 0, 0);
562562
}
563563
564
-/*
565
-** The pSnip input contains snippet text from a search formatted
566
-** as HTML. Attempt to make that text more readable on a TTY.
567
-**
568
-** If nTty is positive, use ANSI escape codes "\e[Nm" where N is nTty
569
-** to highly marked text.
570
-*/
571
-void search_snippet_to_plaintext(Blob *pSnip, int nTty){
572
- char *zSnip;
573
- unsigned int j, k;
574
-
575
- zSnip = pSnip->aData;
576
- for(j=k=0; j<pSnip->nUsed; j++){
577
- char c = zSnip[j];
578
- if( c=='<' ){
579
- if( memcmp(&zSnip[j],"<mark>",6)==0 ){
580
- if( nTty ){
581
- zSnip[k++] = 0x1b;
582
- zSnip[k++] = '[';
583
- if( nTty>=10 ) zSnip[k++] = (nTty/10)%10 + '0';
584
- zSnip[k++] = nTty%10 + '0';
585
- zSnip[k++] = 'm';
586
- }
587
- j += 5;
588
- }else if( memcmp(&zSnip[j],"</mark>",7)==0 ){
589
- if( nTty ){
590
- zSnip[k++] = 0x1b;
591
- zSnip[k++] = '[';
592
- zSnip[k++] = '0';
593
- zSnip[k++] = 'm';
594
- }
595
- j += 6;
596
- }else{
597
- zSnip[k++] = zSnip[j];
598
- }
599
- }else if( fossil_isspace(c) ){
600
- zSnip[k++] = ' ';
601
- while( fossil_isspace(zSnip[j+1]) ) j++;
602
- }else if( c=='&' ){
603
- if( zSnip[j+1]=='#' && fossil_isdigit(zSnip[j+2]) ){
604
- int n = 3;
605
- int x = zSnip[j+2] - '0';
606
- if( fossil_isdigit(zSnip[j+3]) ){
607
- x = x*10 + zSnip[j+3] - '0';
608
- n++;
609
- if( fossil_isdigit(zSnip[j+4]) ){
610
- x = x*10 + zSnip[j+4] - '0';
611
- n++;
612
- }
613
- }
614
- if( zSnip[j+n]==';' ){
615
- zSnip[k++] = (char)x;
616
- j += n;
617
- }else{
618
- zSnip[k++] = c;
619
- }
620
- }else if( memcmp(&zSnip[j],"&lt;",4)==0 ){
621
- zSnip[k++] = '<';
622
- j += 3;
623
- }else if( memcmp(&zSnip[j],"&gt;",4)==0 ){
624
- zSnip[k++] = '>';
625
- j += 3;
626
- }else if( memcmp(&zSnip[j],"&quot;",6)==0 ){
627
- zSnip[k++] = '"';
628
- j += 5;
629
- }else if( memcmp(&zSnip[j],"&amp;",5)==0 ){
630
- zSnip[k++] = '&';
631
- j += 4;
632
- }else{
633
- zSnip[k++] = c;
634
- }
635
- }else if( c=='%' && strncmp(&zSnip[j],"%fossil",7)==0 ){
636
- /* no-op */
637
- }else if( (c=='[' || c==']') && zSnip[j+1]==c ){
638
- j++;
639
- }else{
640
- zSnip[k++] = c;
641
- }
642
- }
643
- zSnip[k] = 0;
644
- pSnip->nUsed = k;
645
-}
646
-
647564
/*
648565
** Testing the search function.
649566
**
650567
** COMMAND: search*
651568
**
@@ -702,18 +619,11 @@
702619
int nLimit = zLimit ? atoi(zLimit) : -1000;
703620
int width;
704621
int nTty = 0; /* VT100 highlight color for matching text */
705622
const char *zHighlight = 0;
706623
707
- /* Only colorize the output if talking to a tty and NO_COLOR does not
708
- ** exist or is false. */
709
- if( fossil_isatty(1) ){
710
- char *zNoColor = fossil_getenv("NO_COLOR");
711
- if( zNoColor==0 || zNoColor[0]==0 || is_false(zNoColor) ){
712
- nTty = 91;
713
- }
714
- }
624
+ nTty = terminal_is_vt100();
715625
716626
/* Undocumented option to change highlight color */
717627
zHighlight = find_option("highlight",0,1);
718628
if( zHighlight ) nTty = atoi(zHighlight);
719629
@@ -805,19 +715,23 @@
805715
db_prepare(&q, "SELECT snip, label, score, id, date"
806716
" FROM x"
807717
" ORDER BY score DESC, date DESC;");
808718
blob_init(&com, 0, 0);
809719
blob_init(&snip, 0, 0);
810
- if( width<0 ) width = 80;
720
+ if( width<0 ) width = terminal_get_width(80);
811721
while( db_step(&q)==SQLITE_ROW ){
812722
const char *zSnippet = db_column_text(&q, 0);
813723
const char *zLabel = db_column_text(&q, 1);
814724
const char *zDate = db_column_text(&q, 4);
815725
const char *zScore = db_column_text(&q, 2);
816726
const char *zId = db_column_text(&q, 3);
727
+ char *zOrig;
817728
blob_appendf(&snip, "%s", zSnippet);
818
- search_snippet_to_plaintext(&snip, nTty);
729
+ zOrig = blob_materialize(&snip);
730
+ blob_init(&snip, 0, 0);
731
+ html_to_plaintext(zOrig, &snip, (nTty?HTOT_VT100:0)|HTOT_FLOW|HTOT_TRIM);
732
+ fossil_free(zOrig);
819733
blob_appendf(&com, "%s\n%s\n%s", zLabel, blob_str(&snip), zDate);
820734
if( bDebug ){
821735
blob_appendf(&com," score: %s id: %s", zScore, zId);
822736
}
823737
comment_print(blob_str(&com), 0, 5, width,
@@ -1557,20 +1471,20 @@
15571471
}else{
15581472
blob_append(pOut, "\n", 1);
15591473
wiki_convert(pIn, &html, 0);
15601474
}
15611475
}
1562
- html_to_plaintext(blob_str(&html), pOut);
1476
+ html_to_plaintext(blob_str(&html), pOut, 0);
15631477
}else if( fossil_strcmp(zMimetype,"text/x-markdown")==0 ){
15641478
markdown_to_html(pIn, blob_size(&title) ? NULL : &title, &html);
15651479
}else if( fossil_strcmp(zMimetype,"text/html")==0 ){
15661480
if( blob_size(&title)==0 ) doc_is_embedded_html(pIn, &title);
15671481
pHtml = pIn;
15681482
}
15691483
blob_appendf(pOut, "%s\n", blob_str(&title));
15701484
if( blob_size(pHtml) ){
1571
- html_to_plaintext(blob_str(pHtml), pOut);
1485
+ html_to_plaintext(blob_str(pHtml), pOut, 0);
15721486
}else{
15731487
blob_append(pOut, blob_buffer(pIn), blob_size(pIn));
15741488
}
15751489
blob_reset(&html);
15761490
blob_reset(&title);
15771491
--- src/search.c
+++ src/search.c
@@ -559,93 +559,10 @@
559 search_body_sqlfunc, 0, 0);
560 sqlite3_create_function(db, "urlencode", 1, enc, 0,
561 search_urlencode_sqlfunc, 0, 0);
562 }
563
564 /*
565 ** The pSnip input contains snippet text from a search formatted
566 ** as HTML. Attempt to make that text more readable on a TTY.
567 **
568 ** If nTty is positive, use ANSI escape codes "\e[Nm" where N is nTty
569 ** to highly marked text.
570 */
571 void search_snippet_to_plaintext(Blob *pSnip, int nTty){
572 char *zSnip;
573 unsigned int j, k;
574
575 zSnip = pSnip->aData;
576 for(j=k=0; j<pSnip->nUsed; j++){
577 char c = zSnip[j];
578 if( c=='<' ){
579 if( memcmp(&zSnip[j],"<mark>",6)==0 ){
580 if( nTty ){
581 zSnip[k++] = 0x1b;
582 zSnip[k++] = '[';
583 if( nTty>=10 ) zSnip[k++] = (nTty/10)%10 + '0';
584 zSnip[k++] = nTty%10 + '0';
585 zSnip[k++] = 'm';
586 }
587 j += 5;
588 }else if( memcmp(&zSnip[j],"</mark>",7)==0 ){
589 if( nTty ){
590 zSnip[k++] = 0x1b;
591 zSnip[k++] = '[';
592 zSnip[k++] = '0';
593 zSnip[k++] = 'm';
594 }
595 j += 6;
596 }else{
597 zSnip[k++] = zSnip[j];
598 }
599 }else if( fossil_isspace(c) ){
600 zSnip[k++] = ' ';
601 while( fossil_isspace(zSnip[j+1]) ) j++;
602 }else if( c=='&' ){
603 if( zSnip[j+1]=='#' && fossil_isdigit(zSnip[j+2]) ){
604 int n = 3;
605 int x = zSnip[j+2] - '0';
606 if( fossil_isdigit(zSnip[j+3]) ){
607 x = x*10 + zSnip[j+3] - '0';
608 n++;
609 if( fossil_isdigit(zSnip[j+4]) ){
610 x = x*10 + zSnip[j+4] - '0';
611 n++;
612 }
613 }
614 if( zSnip[j+n]==';' ){
615 zSnip[k++] = (char)x;
616 j += n;
617 }else{
618 zSnip[k++] = c;
619 }
620 }else if( memcmp(&zSnip[j],"&lt;",4)==0 ){
621 zSnip[k++] = '<';
622 j += 3;
623 }else if( memcmp(&zSnip[j],"&gt;",4)==0 ){
624 zSnip[k++] = '>';
625 j += 3;
626 }else if( memcmp(&zSnip[j],"&quot;",6)==0 ){
627 zSnip[k++] = '"';
628 j += 5;
629 }else if( memcmp(&zSnip[j],"&amp;",5)==0 ){
630 zSnip[k++] = '&';
631 j += 4;
632 }else{
633 zSnip[k++] = c;
634 }
635 }else if( c=='%' && strncmp(&zSnip[j],"%fossil",7)==0 ){
636 /* no-op */
637 }else if( (c=='[' || c==']') && zSnip[j+1]==c ){
638 j++;
639 }else{
640 zSnip[k++] = c;
641 }
642 }
643 zSnip[k] = 0;
644 pSnip->nUsed = k;
645 }
646
647 /*
648 ** Testing the search function.
649 **
650 ** COMMAND: search*
651 **
@@ -702,18 +619,11 @@
702 int nLimit = zLimit ? atoi(zLimit) : -1000;
703 int width;
704 int nTty = 0; /* VT100 highlight color for matching text */
705 const char *zHighlight = 0;
706
707 /* Only colorize the output if talking to a tty and NO_COLOR does not
708 ** exist or is false. */
709 if( fossil_isatty(1) ){
710 char *zNoColor = fossil_getenv("NO_COLOR");
711 if( zNoColor==0 || zNoColor[0]==0 || is_false(zNoColor) ){
712 nTty = 91;
713 }
714 }
715
716 /* Undocumented option to change highlight color */
717 zHighlight = find_option("highlight",0,1);
718 if( zHighlight ) nTty = atoi(zHighlight);
719
@@ -805,19 +715,23 @@
805 db_prepare(&q, "SELECT snip, label, score, id, date"
806 " FROM x"
807 " ORDER BY score DESC, date DESC;");
808 blob_init(&com, 0, 0);
809 blob_init(&snip, 0, 0);
810 if( width<0 ) width = 80;
811 while( db_step(&q)==SQLITE_ROW ){
812 const char *zSnippet = db_column_text(&q, 0);
813 const char *zLabel = db_column_text(&q, 1);
814 const char *zDate = db_column_text(&q, 4);
815 const char *zScore = db_column_text(&q, 2);
816 const char *zId = db_column_text(&q, 3);
 
817 blob_appendf(&snip, "%s", zSnippet);
818 search_snippet_to_plaintext(&snip, nTty);
 
 
 
819 blob_appendf(&com, "%s\n%s\n%s", zLabel, blob_str(&snip), zDate);
820 if( bDebug ){
821 blob_appendf(&com," score: %s id: %s", zScore, zId);
822 }
823 comment_print(blob_str(&com), 0, 5, width,
@@ -1557,20 +1471,20 @@
1557 }else{
1558 blob_append(pOut, "\n", 1);
1559 wiki_convert(pIn, &html, 0);
1560 }
1561 }
1562 html_to_plaintext(blob_str(&html), pOut);
1563 }else if( fossil_strcmp(zMimetype,"text/x-markdown")==0 ){
1564 markdown_to_html(pIn, blob_size(&title) ? NULL : &title, &html);
1565 }else if( fossil_strcmp(zMimetype,"text/html")==0 ){
1566 if( blob_size(&title)==0 ) doc_is_embedded_html(pIn, &title);
1567 pHtml = pIn;
1568 }
1569 blob_appendf(pOut, "%s\n", blob_str(&title));
1570 if( blob_size(pHtml) ){
1571 html_to_plaintext(blob_str(pHtml), pOut);
1572 }else{
1573 blob_append(pOut, blob_buffer(pIn), blob_size(pIn));
1574 }
1575 blob_reset(&html);
1576 blob_reset(&title);
1577
--- src/search.c
+++ src/search.c
@@ -559,93 +559,10 @@
559 search_body_sqlfunc, 0, 0);
560 sqlite3_create_function(db, "urlencode", 1, enc, 0,
561 search_urlencode_sqlfunc, 0, 0);
562 }
563
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
564 /*
565 ** Testing the search function.
566 **
567 ** COMMAND: search*
568 **
@@ -702,18 +619,11 @@
619 int nLimit = zLimit ? atoi(zLimit) : -1000;
620 int width;
621 int nTty = 0; /* VT100 highlight color for matching text */
622 const char *zHighlight = 0;
623
624 nTty = terminal_is_vt100();
 
 
 
 
 
 
 
625
626 /* Undocumented option to change highlight color */
627 zHighlight = find_option("highlight",0,1);
628 if( zHighlight ) nTty = atoi(zHighlight);
629
@@ -805,19 +715,23 @@
715 db_prepare(&q, "SELECT snip, label, score, id, date"
716 " FROM x"
717 " ORDER BY score DESC, date DESC;");
718 blob_init(&com, 0, 0);
719 blob_init(&snip, 0, 0);
720 if( width<0 ) width = terminal_get_width(80);
721 while( db_step(&q)==SQLITE_ROW ){
722 const char *zSnippet = db_column_text(&q, 0);
723 const char *zLabel = db_column_text(&q, 1);
724 const char *zDate = db_column_text(&q, 4);
725 const char *zScore = db_column_text(&q, 2);
726 const char *zId = db_column_text(&q, 3);
727 char *zOrig;
728 blob_appendf(&snip, "%s", zSnippet);
729 zOrig = blob_materialize(&snip);
730 blob_init(&snip, 0, 0);
731 html_to_plaintext(zOrig, &snip, (nTty?HTOT_VT100:0)|HTOT_FLOW|HTOT_TRIM);
732 fossil_free(zOrig);
733 blob_appendf(&com, "%s\n%s\n%s", zLabel, blob_str(&snip), zDate);
734 if( bDebug ){
735 blob_appendf(&com," score: %s id: %s", zScore, zId);
736 }
737 comment_print(blob_str(&com), 0, 5, width,
@@ -1557,20 +1471,20 @@
1471 }else{
1472 blob_append(pOut, "\n", 1);
1473 wiki_convert(pIn, &html, 0);
1474 }
1475 }
1476 html_to_plaintext(blob_str(&html), pOut, 0);
1477 }else if( fossil_strcmp(zMimetype,"text/x-markdown")==0 ){
1478 markdown_to_html(pIn, blob_size(&title) ? NULL : &title, &html);
1479 }else if( fossil_strcmp(zMimetype,"text/html")==0 ){
1480 if( blob_size(&title)==0 ) doc_is_embedded_html(pIn, &title);
1481 pHtml = pIn;
1482 }
1483 blob_appendf(pOut, "%s\n", blob_str(&title));
1484 if( blob_size(pHtml) ){
1485 html_to_plaintext(blob_str(pHtml), pOut, 0);
1486 }else{
1487 blob_append(pOut, blob_buffer(pIn), blob_size(pIn));
1488 }
1489 blob_reset(&html);
1490 blob_reset(&title);
1491
--- src/security_audit.c
+++ src/security_audit.c
@@ -814,10 +814,11 @@
814814
**
815815
** y=0x01 Show only hack attempts
816816
** y=0x02 Show only panics and assertion faults
817817
** y=0x04 Show hung backoffice processes
818818
** y=0x08 Show POST requests from a different origin
819
+** y=0x10 Show SQLITE_AUTH and similar
819820
** y=0x40 Show other uncategorized messages
820821
**
821822
** If y is omitted or is zero, a count of the various message types is
822823
** shown.
823824
*/
@@ -824,19 +825,20 @@
824825
void errorlog_page(void){
825826
i64 szFile;
826827
FILE *in;
827828
char *zLog;
828829
const char *zType = P("y");
829
- static const int eAllTypes = 0x4f;
830
+ static const int eAllTypes = 0x5f;
830831
long eType = 0;
831832
int bOutput = 0;
832833
int prevWasTime = 0;
833834
int nHack = 0;
834835
int nPanic = 0;
835836
int nOther = 0;
836837
int nHang = 0;
837838
int nXPost = 0;
839
+ int nAuth = 0;
838840
char z[10000];
839841
char zTime[10000];
840842
841843
login_check_credentials();
842844
if( !g.perm.Admin ){
@@ -906,10 +908,13 @@
906908
@ <li>Hung backoffice processes
907909
}
908910
if( eType & 0x08 ){
909911
@ <li>POST requests from different origin
910912
}
913
+ if( eType & 0x10 ){
914
+ @ <li>SQLITE_AUTH and similar errors
915
+ }
911916
if( eType & 0x40 ){
912917
@ <li>Other uncategorized messages
913918
}
914919
@ </ul>
915920
}
@@ -933,10 +938,16 @@
933938
}else
934939
if( sqlite3_strglob("warning: POST from different origin*",z)==0 ){
935940
bOutput = (eType & 0x08)!=0;
936941
nXPost++;
937942
}else
943
+ if( sqlite3_strglob("SECURITY: authorizer blocks*",z)==0
944
+ || sqlite3_strglob("warning: SQLITE_AUTH*",z)==0
945
+ ){
946
+ bOutput = (eType & 0x10)!=0;
947
+ nAuth++;
948
+ }else
938949
{
939950
bOutput = (eType & 0x40)!=0;
940951
nOther++;
941952
}
942953
if( bOutput ){
@@ -958,11 +969,11 @@
958969
fclose(in);
959970
if( eType ){
960971
@ </pre>
961972
}
962973
if( eType==0 ){
963
- int nNonHack = nPanic + nHang + nOther;
974
+ int nNonHack = nPanic + nHang + nAuth + nOther;
964975
int nTotal = nNonHack + nHack + nXPost;
965976
@ <p><table border="a" cellspacing="0" cellpadding="5">
966977
if( nPanic>0 ){
967978
@ <tr><td align="right">%d(nPanic)</td>
968979
@ <td><a href="./errorlog?y=2">Panics</a></td>
@@ -971,23 +982,23 @@
971982
@ <tr><td align="right">%d(nHack)</td>
972983
@ <td><a href="./errorlog?y=1">Hack Attempts</a></td>
973984
}
974985
if( nHang>0 ){
975986
@ <tr><td align="right">%d(nHang)</td>
976
- @ <td><a href="./errorlog?y=4/">Hung Backoffice</a></td>
987
+ @ <td><a href="./errorlog?y=4">Hung Backoffice</a></td>
977988
}
978989
if( nXPost>0 ){
979990
@ <tr><td align="right">%d(nXPost)</td>
980
- @ <td><a href="./errorlog?y=8/">POSTs from different origin</a></td>
991
+ @ <td><a href="./errorlog?y=8">POSTs from different origin</a></td>
992
+ }
993
+ if( nAuth>0 ){
994
+ @ <tr><td align="right">%d(nAuth)</td>
995
+ @ <td><a href="./errorlog?y=16">SQLITE_AUTH and similar</a></td>
981996
}
982997
if( nOther>0 ){
983998
@ <tr><td align="right">%d(nOther)</td>
984
- @ <td><a href="./errorlog?y=64/">Other</a></td>
985
- }
986
- if( nHack+nXPost>0 && nNonHack>0 ){
987
- @ <tr><td align="right">%d(nNonHack)</td>
988
- @ <td><a href="%R/errorlog?y=70">Other than hack attempts</a></td>
999
+ @ <td><a href="./errorlog?y=64">Other</a></td>
9891000
}
9901001
@ <tr><td align="right">%d(nTotal)</td>
9911002
if( nTotal>0 ){
9921003
@ <td><a href="./errorlog?y=255">All Messages</a></td>
9931004
}else{
9941005
--- src/security_audit.c
+++ src/security_audit.c
@@ -814,10 +814,11 @@
814 **
815 ** y=0x01 Show only hack attempts
816 ** y=0x02 Show only panics and assertion faults
817 ** y=0x04 Show hung backoffice processes
818 ** y=0x08 Show POST requests from a different origin
 
819 ** y=0x40 Show other uncategorized messages
820 **
821 ** If y is omitted or is zero, a count of the various message types is
822 ** shown.
823 */
@@ -824,19 +825,20 @@
824 void errorlog_page(void){
825 i64 szFile;
826 FILE *in;
827 char *zLog;
828 const char *zType = P("y");
829 static const int eAllTypes = 0x4f;
830 long eType = 0;
831 int bOutput = 0;
832 int prevWasTime = 0;
833 int nHack = 0;
834 int nPanic = 0;
835 int nOther = 0;
836 int nHang = 0;
837 int nXPost = 0;
 
838 char z[10000];
839 char zTime[10000];
840
841 login_check_credentials();
842 if( !g.perm.Admin ){
@@ -906,10 +908,13 @@
906 @ <li>Hung backoffice processes
907 }
908 if( eType & 0x08 ){
909 @ <li>POST requests from different origin
910 }
 
 
 
911 if( eType & 0x40 ){
912 @ <li>Other uncategorized messages
913 }
914 @ </ul>
915 }
@@ -933,10 +938,16 @@
933 }else
934 if( sqlite3_strglob("warning: POST from different origin*",z)==0 ){
935 bOutput = (eType & 0x08)!=0;
936 nXPost++;
937 }else
 
 
 
 
 
 
938 {
939 bOutput = (eType & 0x40)!=0;
940 nOther++;
941 }
942 if( bOutput ){
@@ -958,11 +969,11 @@
958 fclose(in);
959 if( eType ){
960 @ </pre>
961 }
962 if( eType==0 ){
963 int nNonHack = nPanic + nHang + nOther;
964 int nTotal = nNonHack + nHack + nXPost;
965 @ <p><table border="a" cellspacing="0" cellpadding="5">
966 if( nPanic>0 ){
967 @ <tr><td align="right">%d(nPanic)</td>
968 @ <td><a href="./errorlog?y=2">Panics</a></td>
@@ -971,23 +982,23 @@
971 @ <tr><td align="right">%d(nHack)</td>
972 @ <td><a href="./errorlog?y=1">Hack Attempts</a></td>
973 }
974 if( nHang>0 ){
975 @ <tr><td align="right">%d(nHang)</td>
976 @ <td><a href="./errorlog?y=4/">Hung Backoffice</a></td>
977 }
978 if( nXPost>0 ){
979 @ <tr><td align="right">%d(nXPost)</td>
980 @ <td><a href="./errorlog?y=8/">POSTs from different origin</a></td>
 
 
 
 
981 }
982 if( nOther>0 ){
983 @ <tr><td align="right">%d(nOther)</td>
984 @ <td><a href="./errorlog?y=64/">Other</a></td>
985 }
986 if( nHack+nXPost>0 && nNonHack>0 ){
987 @ <tr><td align="right">%d(nNonHack)</td>
988 @ <td><a href="%R/errorlog?y=70">Other than hack attempts</a></td>
989 }
990 @ <tr><td align="right">%d(nTotal)</td>
991 if( nTotal>0 ){
992 @ <td><a href="./errorlog?y=255">All Messages</a></td>
993 }else{
994
--- src/security_audit.c
+++ src/security_audit.c
@@ -814,10 +814,11 @@
814 **
815 ** y=0x01 Show only hack attempts
816 ** y=0x02 Show only panics and assertion faults
817 ** y=0x04 Show hung backoffice processes
818 ** y=0x08 Show POST requests from a different origin
819 ** y=0x10 Show SQLITE_AUTH and similar
820 ** y=0x40 Show other uncategorized messages
821 **
822 ** If y is omitted or is zero, a count of the various message types is
823 ** shown.
824 */
@@ -824,19 +825,20 @@
825 void errorlog_page(void){
826 i64 szFile;
827 FILE *in;
828 char *zLog;
829 const char *zType = P("y");
830 static const int eAllTypes = 0x5f;
831 long eType = 0;
832 int bOutput = 0;
833 int prevWasTime = 0;
834 int nHack = 0;
835 int nPanic = 0;
836 int nOther = 0;
837 int nHang = 0;
838 int nXPost = 0;
839 int nAuth = 0;
840 char z[10000];
841 char zTime[10000];
842
843 login_check_credentials();
844 if( !g.perm.Admin ){
@@ -906,10 +908,13 @@
908 @ <li>Hung backoffice processes
909 }
910 if( eType & 0x08 ){
911 @ <li>POST requests from different origin
912 }
913 if( eType & 0x10 ){
914 @ <li>SQLITE_AUTH and similar errors
915 }
916 if( eType & 0x40 ){
917 @ <li>Other uncategorized messages
918 }
919 @ </ul>
920 }
@@ -933,10 +938,16 @@
938 }else
939 if( sqlite3_strglob("warning: POST from different origin*",z)==0 ){
940 bOutput = (eType & 0x08)!=0;
941 nXPost++;
942 }else
943 if( sqlite3_strglob("SECURITY: authorizer blocks*",z)==0
944 || sqlite3_strglob("warning: SQLITE_AUTH*",z)==0
945 ){
946 bOutput = (eType & 0x10)!=0;
947 nAuth++;
948 }else
949 {
950 bOutput = (eType & 0x40)!=0;
951 nOther++;
952 }
953 if( bOutput ){
@@ -958,11 +969,11 @@
969 fclose(in);
970 if( eType ){
971 @ </pre>
972 }
973 if( eType==0 ){
974 int nNonHack = nPanic + nHang + nAuth + nOther;
975 int nTotal = nNonHack + nHack + nXPost;
976 @ <p><table border="a" cellspacing="0" cellpadding="5">
977 if( nPanic>0 ){
978 @ <tr><td align="right">%d(nPanic)</td>
979 @ <td><a href="./errorlog?y=2">Panics</a></td>
@@ -971,23 +982,23 @@
982 @ <tr><td align="right">%d(nHack)</td>
983 @ <td><a href="./errorlog?y=1">Hack Attempts</a></td>
984 }
985 if( nHang>0 ){
986 @ <tr><td align="right">%d(nHang)</td>
987 @ <td><a href="./errorlog?y=4">Hung Backoffice</a></td>
988 }
989 if( nXPost>0 ){
990 @ <tr><td align="right">%d(nXPost)</td>
991 @ <td><a href="./errorlog?y=8">POSTs from different origin</a></td>
992 }
993 if( nAuth>0 ){
994 @ <tr><td align="right">%d(nAuth)</td>
995 @ <td><a href="./errorlog?y=16">SQLITE_AUTH and similar</a></td>
996 }
997 if( nOther>0 ){
998 @ <tr><td align="right">%d(nOther)</td>
999 @ <td><a href="./errorlog?y=64">Other</a></td>
 
 
 
 
1000 }
1001 @ <tr><td align="right">%d(nTotal)</td>
1002 if( nTotal>0 ){
1003 @ <td><a href="./errorlog?y=255">All Messages</a></td>
1004 }else{
1005
--- src/setup.c
+++ src/setup.c
@@ -981,17 +981,10 @@
981981
db_begin_transaction();
982982
@ <form action="%R/setup_timeline" method="post"><div>
983983
login_insert_csrf_secret();
984984
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
985985
986
- @ <hr>
987
- onoff_attribute("Allow block-markup in timeline",
988
- "timeline-block-markup", "tbm", 0, 0);
989
- @ <p>In timeline displays, check-in comments can be displayed with or
990
- @ without block markup such as paragraphs, tables, etc.
991
- @ (Property: "timeline-block-markup")</p>
992
-
993986
@ <hr>
994987
onoff_attribute("Plaintext comments on timelines",
995988
"timeline-plaintext", "tpt", 0, 0);
996989
@ <p>In timeline displays, check-in comments are displayed literally,
997990
@ without any wiki or HTML interpretation. Use CSS to change
998991
--- src/setup.c
+++ src/setup.c
@@ -981,17 +981,10 @@
981 db_begin_transaction();
982 @ <form action="%R/setup_timeline" method="post"><div>
983 login_insert_csrf_secret();
984 @ <p><input type="submit" name="submit" value="Apply Changes"></p>
985
986 @ <hr>
987 onoff_attribute("Allow block-markup in timeline",
988 "timeline-block-markup", "tbm", 0, 0);
989 @ <p>In timeline displays, check-in comments can be displayed with or
990 @ without block markup such as paragraphs, tables, etc.
991 @ (Property: "timeline-block-markup")</p>
992
993 @ <hr>
994 onoff_attribute("Plaintext comments on timelines",
995 "timeline-plaintext", "tpt", 0, 0);
996 @ <p>In timeline displays, check-in comments are displayed literally,
997 @ without any wiki or HTML interpretation. Use CSS to change
998
--- src/setup.c
+++ src/setup.c
@@ -981,17 +981,10 @@
981 db_begin_transaction();
982 @ <form action="%R/setup_timeline" method="post"><div>
983 login_insert_csrf_secret();
984 @ <p><input type="submit" name="submit" value="Apply Changes"></p>
985
 
 
 
 
 
 
 
986 @ <hr>
987 onoff_attribute("Plaintext comments on timelines",
988 "timeline-plaintext", "tpt", 0, 0);
989 @ <p>In timeline displays, check-in comments are displayed literally,
990 @ without any wiki or HTML interpretation. Use CSS to change
991
--- src/sitemap.c
+++ src/sitemap.c
@@ -293,10 +293,11 @@
293293
@ <li>%z(href("%R/test-rename-list"))List of file renames</a></li>
294294
}
295295
@ <li>%z(href("%R/test-builtin-files"))List of built-in files</a></li>
296296
@ <li>%z(href("%R/mimetype_list"))List of MIME types</a></li>
297297
@ <li>%z(href("%R/hash-color-test"))Hash color test</a>
298
+ @ <li>%z(href("%R/test-bgcolor"))Background color test</a>
298299
if( g.perm.Admin ){
299300
@ <li>%z(href("%R/test-backlinks"))List of backlinks</a></li>
300301
@ <li>%z(href("%R/test-backlink-timeline"))Backlink timeline</a></li>
301302
@ <li>%z(href("%R/phantoms"))List of phantom artifacts</a></li>
302303
@ <li>%z(href("%R/test-warning"))Error Log test page</a></li>
303304
--- src/sitemap.c
+++ src/sitemap.c
@@ -293,10 +293,11 @@
293 @ <li>%z(href("%R/test-rename-list"))List of file renames</a></li>
294 }
295 @ <li>%z(href("%R/test-builtin-files"))List of built-in files</a></li>
296 @ <li>%z(href("%R/mimetype_list"))List of MIME types</a></li>
297 @ <li>%z(href("%R/hash-color-test"))Hash color test</a>
 
298 if( g.perm.Admin ){
299 @ <li>%z(href("%R/test-backlinks"))List of backlinks</a></li>
300 @ <li>%z(href("%R/test-backlink-timeline"))Backlink timeline</a></li>
301 @ <li>%z(href("%R/phantoms"))List of phantom artifacts</a></li>
302 @ <li>%z(href("%R/test-warning"))Error Log test page</a></li>
303
--- src/sitemap.c
+++ src/sitemap.c
@@ -293,10 +293,11 @@
293 @ <li>%z(href("%R/test-rename-list"))List of file renames</a></li>
294 }
295 @ <li>%z(href("%R/test-builtin-files"))List of built-in files</a></li>
296 @ <li>%z(href("%R/mimetype_list"))List of MIME types</a></li>
297 @ <li>%z(href("%R/hash-color-test"))Hash color test</a>
298 @ <li>%z(href("%R/test-bgcolor"))Background color test</a>
299 if( g.perm.Admin ){
300 @ <li>%z(href("%R/test-backlinks"))List of backlinks</a></li>
301 @ <li>%z(href("%R/test-backlink-timeline"))Backlink timeline</a></li>
302 @ <li>%z(href("%R/phantoms"))List of phantom artifacts</a></li>
303 @ <li>%z(href("%R/test-warning"))Error Log test page</a></li>
304
+5 -3
--- src/statrep.c
+++ src/statrep.c
@@ -318,15 +318,16 @@
318318
&& nMaxEvents>0
319319
){
320320
/* If the timespan covered by this row contains "now", then project
321321
** the number of changes until the completion of the timespan and
322322
** show a dashed box of that projection. */
323
+ int nProj = (int)(((double)nCount)/rNowFraction);
323324
int nExtra = (int)(((double)nCount)/rNowFraction) - nCount;
324325
int nXSize = (100 * nExtra)/nMaxEvents;
325326
@ <span class='statistics-report-graph-line' \
326327
@ style='display:inline-block;min-width:%d(nSize)%%;'>&nbsp;</span>\
327
- @ <span class='statistics-report-graph-extra' \
328
+ @ <span class='statistics-report-graph-extra' title='%d(nProj)' \
328329
@ style='display:inline-block;min-width:%d(nXSize)%%;'>&nbsp;</span>\
329330
}else{
330331
@ <div class='statistics-report-graph-line' \
331332
@ style='width:%d(nSize)%%;'>&nbsp;</div> \
332333
}
@@ -747,18 +748,19 @@
747748
if( zCurrentWeek!=0
748749
&& strcmp(zWeek, zCurrentWeek)==0
749750
&& rNowFraction>0.05
750751
&& nMaxEvents>0
751752
){
752
- /* If the covered covered by this row contains "now", then project
753
+ /* If the timespan covered by this row contains "now", then project
753754
** the number of changes until the completion of the week and
754755
** show a dashed box of that projection. */
756
+ int nProj = (int)(((double)nCount)/rNowFraction);
755757
int nExtra = (int)(((double)nCount)/rNowFraction) - nCount;
756758
int nXSize = (100 * nExtra)/nMaxEvents;
757759
@ <span class='statistics-report-graph-line' \
758760
@ style='display:inline-block;min-width:%d(nSize)%%;'>&nbsp;</span>\
759
- @ <span class='statistics-report-graph-extra' \
761
+ @ <span class='statistics-report-graph-extra' title='%d(nProj)' \
760762
@ style='display:inline-block;min-width:%d(nXSize)%%;'>&nbsp;</span>\
761763
}else{
762764
@ <div class='statistics-report-graph-line' \
763765
@ style='width:%d(nSize)%%;'>&nbsp;</div> \
764766
}
765767
--- src/statrep.c
+++ src/statrep.c
@@ -318,15 +318,16 @@
318 && nMaxEvents>0
319 ){
320 /* If the timespan covered by this row contains "now", then project
321 ** the number of changes until the completion of the timespan and
322 ** show a dashed box of that projection. */
 
323 int nExtra = (int)(((double)nCount)/rNowFraction) - nCount;
324 int nXSize = (100 * nExtra)/nMaxEvents;
325 @ <span class='statistics-report-graph-line' \
326 @ style='display:inline-block;min-width:%d(nSize)%%;'>&nbsp;</span>\
327 @ <span class='statistics-report-graph-extra' \
328 @ style='display:inline-block;min-width:%d(nXSize)%%;'>&nbsp;</span>\
329 }else{
330 @ <div class='statistics-report-graph-line' \
331 @ style='width:%d(nSize)%%;'>&nbsp;</div> \
332 }
@@ -747,18 +748,19 @@
747 if( zCurrentWeek!=0
748 && strcmp(zWeek, zCurrentWeek)==0
749 && rNowFraction>0.05
750 && nMaxEvents>0
751 ){
752 /* If the covered covered by this row contains "now", then project
753 ** the number of changes until the completion of the week and
754 ** show a dashed box of that projection. */
 
755 int nExtra = (int)(((double)nCount)/rNowFraction) - nCount;
756 int nXSize = (100 * nExtra)/nMaxEvents;
757 @ <span class='statistics-report-graph-line' \
758 @ style='display:inline-block;min-width:%d(nSize)%%;'>&nbsp;</span>\
759 @ <span class='statistics-report-graph-extra' \
760 @ style='display:inline-block;min-width:%d(nXSize)%%;'>&nbsp;</span>\
761 }else{
762 @ <div class='statistics-report-graph-line' \
763 @ style='width:%d(nSize)%%;'>&nbsp;</div> \
764 }
765
--- src/statrep.c
+++ src/statrep.c
@@ -318,15 +318,16 @@
318 && nMaxEvents>0
319 ){
320 /* If the timespan covered by this row contains "now", then project
321 ** the number of changes until the completion of the timespan and
322 ** show a dashed box of that projection. */
323 int nProj = (int)(((double)nCount)/rNowFraction);
324 int nExtra = (int)(((double)nCount)/rNowFraction) - nCount;
325 int nXSize = (100 * nExtra)/nMaxEvents;
326 @ <span class='statistics-report-graph-line' \
327 @ style='display:inline-block;min-width:%d(nSize)%%;'>&nbsp;</span>\
328 @ <span class='statistics-report-graph-extra' title='%d(nProj)' \
329 @ style='display:inline-block;min-width:%d(nXSize)%%;'>&nbsp;</span>\
330 }else{
331 @ <div class='statistics-report-graph-line' \
332 @ style='width:%d(nSize)%%;'>&nbsp;</div> \
333 }
@@ -747,18 +748,19 @@
748 if( zCurrentWeek!=0
749 && strcmp(zWeek, zCurrentWeek)==0
750 && rNowFraction>0.05
751 && nMaxEvents>0
752 ){
753 /* If the timespan covered by this row contains "now", then project
754 ** the number of changes until the completion of the week and
755 ** show a dashed box of that projection. */
756 int nProj = (int)(((double)nCount)/rNowFraction);
757 int nExtra = (int)(((double)nCount)/rNowFraction) - nCount;
758 int nXSize = (100 * nExtra)/nMaxEvents;
759 @ <span class='statistics-report-graph-line' \
760 @ style='display:inline-block;min-width:%d(nSize)%%;'>&nbsp;</span>\
761 @ <span class='statistics-report-graph-extra' title='%d(nProj)' \
762 @ style='display:inline-block;min-width:%d(nXSize)%%;'>&nbsp;</span>\
763 }else{
764 @ <div class='statistics-report-graph-line' \
765 @ style='width:%d(nSize)%%;'>&nbsp;</div> \
766 }
767
--- src/terminal.c
+++ src/terminal.c
@@ -138,5 +138,57 @@
138138
void test_terminal_size_cmd(void){
139139
TerminalSize ts;
140140
terminal_get_size(&ts);
141141
fossil_print("%d %d\n", ts.nColumns, ts.nLines);
142142
}
143
+
144
+/*
145
+** Return true if it is reasonable is emit VT100 escape codes.
146
+*/
147
+int terminal_is_vt100(void){
148
+ char *zNoColor;
149
+#ifdef _WIN32
150
+ if( !win32_terminal_is_vt100(1) ) return 0;
151
+#endif /* _WIN32 */
152
+ if( !fossil_isatty(1) ) return 0;
153
+ zNoColor =fossil_getenv("NO_COLOR");
154
+ if( zNoColor==0 ) return 1;
155
+ if( zNoColor[0]==0 ) return 1;
156
+ if( is_false(zNoColor) ) return 1;
157
+ return 0;
158
+}
159
+
160
+#ifdef _WIN32
161
+/*
162
+** Return true if the Windows console supports VT100 escape codes.
163
+**
164
+** Support for VT100 escape codes is enabled by default in Windows Terminal
165
+** on Windows 10 and Windows 11, and disabled by default in Legacy Consoles
166
+** and on older versions of Windows. Programs can turn on VT100 support for
167
+** Legacy Consoles using the ENABLE_VIRTUAL_TERMINAL_PROCESSING flag.
168
+**
169
+** NOTE: If this function needs to be called in more complex scenarios with
170
+** reassigned stdout and stderr streams, the following CRT calls are useful
171
+** to translate from CRT streams to file descriptors and to Win32 handles:
172
+**
173
+** HANDLE hOutputHandle = (HANDLE)_get_osfhandle(_fileno(<FILE*>));
174
+*/
175
+#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
176
+#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
177
+#endif
178
+int win32_terminal_is_vt100(int fd){
179
+ HANDLE hConsole = NULL;
180
+ DWORD dwConsoleMode;
181
+ switch( fd ){
182
+ case 1:
183
+ hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
184
+ break;
185
+ case 2:
186
+ hConsole = GetStdHandle(STD_ERROR_HANDLE);
187
+ break;
188
+ }
189
+ if( GetConsoleMode(hConsole,&dwConsoleMode) ){
190
+ return (dwConsoleMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)!=0;
191
+ }
192
+ return 0;
193
+}
194
+#endif /* _WIN32 */
143195
--- src/terminal.c
+++ src/terminal.c
@@ -138,5 +138,57 @@
138 void test_terminal_size_cmd(void){
139 TerminalSize ts;
140 terminal_get_size(&ts);
141 fossil_print("%d %d\n", ts.nColumns, ts.nLines);
142 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
143
--- src/terminal.c
+++ src/terminal.c
@@ -138,5 +138,57 @@
138 void test_terminal_size_cmd(void){
139 TerminalSize ts;
140 terminal_get_size(&ts);
141 fossil_print("%d %d\n", ts.nColumns, ts.nLines);
142 }
143
144 /*
145 ** Return true if it is reasonable is emit VT100 escape codes.
146 */
147 int terminal_is_vt100(void){
148 char *zNoColor;
149 #ifdef _WIN32
150 if( !win32_terminal_is_vt100(1) ) return 0;
151 #endif /* _WIN32 */
152 if( !fossil_isatty(1) ) return 0;
153 zNoColor =fossil_getenv("NO_COLOR");
154 if( zNoColor==0 ) return 1;
155 if( zNoColor[0]==0 ) return 1;
156 if( is_false(zNoColor) ) return 1;
157 return 0;
158 }
159
160 #ifdef _WIN32
161 /*
162 ** Return true if the Windows console supports VT100 escape codes.
163 **
164 ** Support for VT100 escape codes is enabled by default in Windows Terminal
165 ** on Windows 10 and Windows 11, and disabled by default in Legacy Consoles
166 ** and on older versions of Windows. Programs can turn on VT100 support for
167 ** Legacy Consoles using the ENABLE_VIRTUAL_TERMINAL_PROCESSING flag.
168 **
169 ** NOTE: If this function needs to be called in more complex scenarios with
170 ** reassigned stdout and stderr streams, the following CRT calls are useful
171 ** to translate from CRT streams to file descriptors and to Win32 handles:
172 **
173 ** HANDLE hOutputHandle = (HANDLE)_get_osfhandle(_fileno(<FILE*>));
174 */
175 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
176 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
177 #endif
178 int win32_terminal_is_vt100(int fd){
179 HANDLE hConsole = NULL;
180 DWORD dwConsoleMode;
181 switch( fd ){
182 case 1:
183 hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
184 break;
185 case 2:
186 hConsole = GetStdHandle(STD_ERROR_HANDLE);
187 break;
188 }
189 if( GetConsoleMode(hConsole,&dwConsoleMode) ){
190 return (dwConsoleMode & ENABLE_VIRTUAL_TERMINAL_PROCESSING)!=0;
191 }
192 return 0;
193 }
194 #endif /* _WIN32 */
195
+29 -72
--- src/timeline.c
+++ src/timeline.c
@@ -221,28 +221,28 @@
221221
vid = db_lget_int("checkout", 0);
222222
}
223223
zPrevDate[0] = 0;
224224
mxWikiLen = db_get_int("timeline-max-comment", 0);
225225
dateFormat = db_get_int("timeline-date-format", 0);
226
- /*
227
- ** SETTING: timeline-truncate-at-blank boolean default=off
228
- **
229
- ** If enabled, check-in comments displayed on the timeline are truncated
230
- ** at the first blank line of the comment text. The comment text after
231
- ** the first blank line is only seen in the /info or similar pages that
232
- ** show details about the check-in.
233
- */
226
+/*
227
+** SETTING: timeline-truncate-at-blank boolean default=off
228
+**
229
+** If enabled, check-in comments displayed on the timeline are truncated
230
+** at the first blank line of the comment text. The comment text after
231
+** the first blank line is only seen in the /info or similar pages that
232
+** show details about the check-in.
233
+*/
234234
bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0);
235
- /*
236
- ** SETTING: timeline-tslink-info boolean default=off
237
- **
238
- ** The hyperlink on the timestamp associated with each timeline entry,
239
- ** on the far left-hand side of the screen, normally targets another
240
- ** /timeline page that shows the entry in context. However, if this
241
- ** option is turned on, that hyperlink targets the /info page showing
242
- ** the details of the entry.
243
- */
235
+/*
236
+** SETTING: timeline-tslink-info boolean default=off
237
+**
238
+** The hyperlink on the timestamp associated with each timeline entry,
239
+** on the far left-hand side of the screen, normally targets another
240
+** /timeline page that shows the entry in context. However, if this
241
+** option is turned on, that hyperlink targets the /info page showing
242
+** the details of the entry.
243
+*/
244244
bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0);
245245
if( (tmFlags & TIMELINE_VIEWS)==0 ){
246246
tmFlags |= timeline_ss_cookie();
247247
}
248248
if( tmFlags & TIMELINE_COLUMNAR ){
@@ -402,10 +402,12 @@
402402
zDateLink = mprintf("<a>");
403403
}
404404
@ <td class="timelineTime">%z(zDateLink)%s(zTime)</a></td>
405405
@ <td class="timelineGraph">
406406
if( tmFlags & (TIMELINE_UCOLOR|TIMELINE_DELTA|TIMELINE_NOCOLOR) ){
407
+ /* Don't use the requested background color. Use the background color
408
+ ** override from query parameters instead. */
407409
if( tmFlags & TIMELINE_UCOLOR ){
408410
zBgClr = zUser ? user_color(zUser) : 0;
409411
}else if( tmFlags & TIMELINE_NOCOLOR ){
410412
zBgClr = 0;
411413
}else if( zType[0]=='c' ){
@@ -420,16 +422,21 @@
420422
}else{
421423
zBgClr = hash_color("f"); /* delta manifest */
422424
}
423425
db_reset(&qdelta);
424426
}
427
+ }else{
428
+ /* Make sure the user-specified background color is reasonable */
429
+ zBgClr = reasonable_bg_color(zBgClr, 0);
425430
}
426431
if( zType[0]=='c'
427432
&& (pGraph || zBgClr==0 || (tmFlags & (TIMELINE_BRCOLOR|TIMELINE_DELTA))!=0)
428433
){
429434
zBr = branch_of_rid(rid);
430435
if( zBgClr==0 || (tmFlags & TIMELINE_BRCOLOR)!=0 ){
436
+ /* If no background color is specified, use a color based on the
437
+ ** branch name */
431438
if( tmFlags & (TIMELINE_DELTA|TIMELINE_NOCOLOR) ){
432439
}else if( zBr==0 || strcmp(zBr,"trunk")==0 ){
433440
zBgClr = 0;
434441
}else{
435442
zBgClr = hash_color(zBr);
@@ -1106,62 +1113,10 @@
11061113
@ WHERE blob.rid=event.objid
11071114
;
11081115
return zBase;
11091116
}
11101117
1111
-/*
1112
-** Convert a symbolic name used as an argument to the a=, b=, or c=
1113
-** query parameters of timeline into a julianday mtime value.
1114
-**
1115
-** If pzDisplay is not null, then display text for the symbolic name might
1116
-** be written into *pzDisplay. But that is not guaranteed.
1117
-**
1118
-** If bRoundUp is true and the symbolic name is a timestamp with less
1119
-** than millisecond resolution, then the timestamp is rounding up to the
1120
-** largest millisecond consistent with that timestamp. If bRoundUp is
1121
-** false, then the resulting time is obtained by extending the timestamp
1122
-** with zeros (hence rounding down). Use bRoundUp==1 if the result
1123
-** will be used in mtime<=$RESULT and use bRoundUp==0 if the result
1124
-** will be used in mtime>=$RESULT.
1125
-*/
1126
-double symbolic_name_to_mtime(
1127
- const char *z, /* Input symbolic name */
1128
- const char **pzDisplay, /* Perhaps write display text here, if not NULL */
1129
- int bRoundUp /* Round up if true */
1130
-){
1131
- double mtime;
1132
- int rid;
1133
- const char *zDate;
1134
- if( z==0 ) return -1.0;
1135
- if( fossil_isdate(z) ){
1136
- mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z);
1137
- if( mtime>0.0 ) return mtime;
1138
- }
1139
- zDate = fossil_expand_datetime(z, 1, bRoundUp);
1140
- if( zDate!=0 ){
1141
- mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())",
1142
- bRoundUp ? fossil_roundup_date(zDate) : zDate);
1143
- if( mtime>0.0 ){
1144
- if( pzDisplay ) *pzDisplay = fossil_strdup(zDate);
1145
- return mtime;
1146
- }
1147
- }
1148
- rid = symbolic_name_to_rid(z, "*");
1149
- if( rid ){
1150
- mtime = mtime_of_rid(rid, 0.0);
1151
- }else{
1152
- mtime = db_double(-1.0,
1153
- "SELECT max(event.mtime) FROM event, tag, tagxref"
1154
- " WHERE tag.tagname GLOB 'event-%q*'"
1155
- " AND tagxref.tagid=tag.tagid AND tagxref.tagtype"
1156
- " AND event.objid=tagxref.rid",
1157
- z
1158
- );
1159
- }
1160
- return mtime;
1161
-}
1162
-
11631118
/*
11641119
** zDate is a localtime date. Insert records into the
11651120
** "timeline" table to cause <hr> to be inserted on zDate.
11661121
*/
11671122
static int timeline_add_divider(double rDate){
@@ -3471,11 +3426,13 @@
34713426
}
34723427
34733428
/*
34743429
** wiki_to_text(TEXT)
34753430
**
3476
-** Return a plain-text rendering of Fossil-Wiki TEXT.
3431
+** Return a text rendering of Fossil-Wiki TEXT, intended for display
3432
+** on a timeline. The timeline-plaintext and timeline-hard-newlines
3433
+** settings are considered when doing this rendering.
34773434
*/
34783435
static void wiki_to_text_sqlfunc(
34793436
sqlite3_context *context,
34803437
int argc,
34813438
sqlite3_value **argv
@@ -3486,14 +3443,14 @@
34863443
zIn = (const char*)sqlite3_value_text(argv[0]);
34873444
if( zIn==0 ) return;
34883445
nIn = sqlite3_value_bytes(argv[0]);
34893446
blob_init(&in, zIn, nIn);
34903447
blob_init(&html, 0, 0);
3491
- wiki_convert(&in, &html, WIKI_INLINE);
3448
+ wiki_convert(&in, &html, wiki_convert_flags(0));
34923449
blob_reset(&in);
34933450
blob_init(&txt, 0, 0);
3494
- html_to_plaintext(blob_str(&html), &txt);
3451
+ html_to_plaintext(blob_str(&html), &txt, 0);
34953452
blob_reset(&html);
34963453
nOut = blob_size(&txt);
34973454
zOut = blob_str(&txt);
34983455
while( fossil_isspace(zOut[0]) ){ zOut++; nOut--; }
34993456
while( nOut>0 && fossil_isspace(zOut[nOut-1]) ){ nOut--; }
35003457
--- src/timeline.c
+++ src/timeline.c
@@ -221,28 +221,28 @@
221 vid = db_lget_int("checkout", 0);
222 }
223 zPrevDate[0] = 0;
224 mxWikiLen = db_get_int("timeline-max-comment", 0);
225 dateFormat = db_get_int("timeline-date-format", 0);
226 /*
227 ** SETTING: timeline-truncate-at-blank boolean default=off
228 **
229 ** If enabled, check-in comments displayed on the timeline are truncated
230 ** at the first blank line of the comment text. The comment text after
231 ** the first blank line is only seen in the /info or similar pages that
232 ** show details about the check-in.
233 */
234 bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0);
235 /*
236 ** SETTING: timeline-tslink-info boolean default=off
237 **
238 ** The hyperlink on the timestamp associated with each timeline entry,
239 ** on the far left-hand side of the screen, normally targets another
240 ** /timeline page that shows the entry in context. However, if this
241 ** option is turned on, that hyperlink targets the /info page showing
242 ** the details of the entry.
243 */
244 bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0);
245 if( (tmFlags & TIMELINE_VIEWS)==0 ){
246 tmFlags |= timeline_ss_cookie();
247 }
248 if( tmFlags & TIMELINE_COLUMNAR ){
@@ -402,10 +402,12 @@
402 zDateLink = mprintf("<a>");
403 }
404 @ <td class="timelineTime">%z(zDateLink)%s(zTime)</a></td>
405 @ <td class="timelineGraph">
406 if( tmFlags & (TIMELINE_UCOLOR|TIMELINE_DELTA|TIMELINE_NOCOLOR) ){
 
 
407 if( tmFlags & TIMELINE_UCOLOR ){
408 zBgClr = zUser ? user_color(zUser) : 0;
409 }else if( tmFlags & TIMELINE_NOCOLOR ){
410 zBgClr = 0;
411 }else if( zType[0]=='c' ){
@@ -420,16 +422,21 @@
420 }else{
421 zBgClr = hash_color("f"); /* delta manifest */
422 }
423 db_reset(&qdelta);
424 }
 
 
 
425 }
426 if( zType[0]=='c'
427 && (pGraph || zBgClr==0 || (tmFlags & (TIMELINE_BRCOLOR|TIMELINE_DELTA))!=0)
428 ){
429 zBr = branch_of_rid(rid);
430 if( zBgClr==0 || (tmFlags & TIMELINE_BRCOLOR)!=0 ){
 
 
431 if( tmFlags & (TIMELINE_DELTA|TIMELINE_NOCOLOR) ){
432 }else if( zBr==0 || strcmp(zBr,"trunk")==0 ){
433 zBgClr = 0;
434 }else{
435 zBgClr = hash_color(zBr);
@@ -1106,62 +1113,10 @@
1106 @ WHERE blob.rid=event.objid
1107 ;
1108 return zBase;
1109 }
1110
1111 /*
1112 ** Convert a symbolic name used as an argument to the a=, b=, or c=
1113 ** query parameters of timeline into a julianday mtime value.
1114 **
1115 ** If pzDisplay is not null, then display text for the symbolic name might
1116 ** be written into *pzDisplay. But that is not guaranteed.
1117 **
1118 ** If bRoundUp is true and the symbolic name is a timestamp with less
1119 ** than millisecond resolution, then the timestamp is rounding up to the
1120 ** largest millisecond consistent with that timestamp. If bRoundUp is
1121 ** false, then the resulting time is obtained by extending the timestamp
1122 ** with zeros (hence rounding down). Use bRoundUp==1 if the result
1123 ** will be used in mtime<=$RESULT and use bRoundUp==0 if the result
1124 ** will be used in mtime>=$RESULT.
1125 */
1126 double symbolic_name_to_mtime(
1127 const char *z, /* Input symbolic name */
1128 const char **pzDisplay, /* Perhaps write display text here, if not NULL */
1129 int bRoundUp /* Round up if true */
1130 ){
1131 double mtime;
1132 int rid;
1133 const char *zDate;
1134 if( z==0 ) return -1.0;
1135 if( fossil_isdate(z) ){
1136 mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z);
1137 if( mtime>0.0 ) return mtime;
1138 }
1139 zDate = fossil_expand_datetime(z, 1, bRoundUp);
1140 if( zDate!=0 ){
1141 mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())",
1142 bRoundUp ? fossil_roundup_date(zDate) : zDate);
1143 if( mtime>0.0 ){
1144 if( pzDisplay ) *pzDisplay = fossil_strdup(zDate);
1145 return mtime;
1146 }
1147 }
1148 rid = symbolic_name_to_rid(z, "*");
1149 if( rid ){
1150 mtime = mtime_of_rid(rid, 0.0);
1151 }else{
1152 mtime = db_double(-1.0,
1153 "SELECT max(event.mtime) FROM event, tag, tagxref"
1154 " WHERE tag.tagname GLOB 'event-%q*'"
1155 " AND tagxref.tagid=tag.tagid AND tagxref.tagtype"
1156 " AND event.objid=tagxref.rid",
1157 z
1158 );
1159 }
1160 return mtime;
1161 }
1162
1163 /*
1164 ** zDate is a localtime date. Insert records into the
1165 ** "timeline" table to cause <hr> to be inserted on zDate.
1166 */
1167 static int timeline_add_divider(double rDate){
@@ -3471,11 +3426,13 @@
3471 }
3472
3473 /*
3474 ** wiki_to_text(TEXT)
3475 **
3476 ** Return a plain-text rendering of Fossil-Wiki TEXT.
 
 
3477 */
3478 static void wiki_to_text_sqlfunc(
3479 sqlite3_context *context,
3480 int argc,
3481 sqlite3_value **argv
@@ -3486,14 +3443,14 @@
3486 zIn = (const char*)sqlite3_value_text(argv[0]);
3487 if( zIn==0 ) return;
3488 nIn = sqlite3_value_bytes(argv[0]);
3489 blob_init(&in, zIn, nIn);
3490 blob_init(&html, 0, 0);
3491 wiki_convert(&in, &html, WIKI_INLINE);
3492 blob_reset(&in);
3493 blob_init(&txt, 0, 0);
3494 html_to_plaintext(blob_str(&html), &txt);
3495 blob_reset(&html);
3496 nOut = blob_size(&txt);
3497 zOut = blob_str(&txt);
3498 while( fossil_isspace(zOut[0]) ){ zOut++; nOut--; }
3499 while( nOut>0 && fossil_isspace(zOut[nOut-1]) ){ nOut--; }
3500
--- src/timeline.c
+++ src/timeline.c
@@ -221,28 +221,28 @@
221 vid = db_lget_int("checkout", 0);
222 }
223 zPrevDate[0] = 0;
224 mxWikiLen = db_get_int("timeline-max-comment", 0);
225 dateFormat = db_get_int("timeline-date-format", 0);
226 /*
227 ** SETTING: timeline-truncate-at-blank boolean default=off
228 **
229 ** If enabled, check-in comments displayed on the timeline are truncated
230 ** at the first blank line of the comment text. The comment text after
231 ** the first blank line is only seen in the /info or similar pages that
232 ** show details about the check-in.
233 */
234 bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0);
235 /*
236 ** SETTING: timeline-tslink-info boolean default=off
237 **
238 ** The hyperlink on the timestamp associated with each timeline entry,
239 ** on the far left-hand side of the screen, normally targets another
240 ** /timeline page that shows the entry in context. However, if this
241 ** option is turned on, that hyperlink targets the /info page showing
242 ** the details of the entry.
243 */
244 bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0);
245 if( (tmFlags & TIMELINE_VIEWS)==0 ){
246 tmFlags |= timeline_ss_cookie();
247 }
248 if( tmFlags & TIMELINE_COLUMNAR ){
@@ -402,10 +402,12 @@
402 zDateLink = mprintf("<a>");
403 }
404 @ <td class="timelineTime">%z(zDateLink)%s(zTime)</a></td>
405 @ <td class="timelineGraph">
406 if( tmFlags & (TIMELINE_UCOLOR|TIMELINE_DELTA|TIMELINE_NOCOLOR) ){
407 /* Don't use the requested background color. Use the background color
408 ** override from query parameters instead. */
409 if( tmFlags & TIMELINE_UCOLOR ){
410 zBgClr = zUser ? user_color(zUser) : 0;
411 }else if( tmFlags & TIMELINE_NOCOLOR ){
412 zBgClr = 0;
413 }else if( zType[0]=='c' ){
@@ -420,16 +422,21 @@
422 }else{
423 zBgClr = hash_color("f"); /* delta manifest */
424 }
425 db_reset(&qdelta);
426 }
427 }else{
428 /* Make sure the user-specified background color is reasonable */
429 zBgClr = reasonable_bg_color(zBgClr, 0);
430 }
431 if( zType[0]=='c'
432 && (pGraph || zBgClr==0 || (tmFlags & (TIMELINE_BRCOLOR|TIMELINE_DELTA))!=0)
433 ){
434 zBr = branch_of_rid(rid);
435 if( zBgClr==0 || (tmFlags & TIMELINE_BRCOLOR)!=0 ){
436 /* If no background color is specified, use a color based on the
437 ** branch name */
438 if( tmFlags & (TIMELINE_DELTA|TIMELINE_NOCOLOR) ){
439 }else if( zBr==0 || strcmp(zBr,"trunk")==0 ){
440 zBgClr = 0;
441 }else{
442 zBgClr = hash_color(zBr);
@@ -1106,62 +1113,10 @@
1113 @ WHERE blob.rid=event.objid
1114 ;
1115 return zBase;
1116 }
1117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1118 /*
1119 ** zDate is a localtime date. Insert records into the
1120 ** "timeline" table to cause <hr> to be inserted on zDate.
1121 */
1122 static int timeline_add_divider(double rDate){
@@ -3471,11 +3426,13 @@
3426 }
3427
3428 /*
3429 ** wiki_to_text(TEXT)
3430 **
3431 ** Return a text rendering of Fossil-Wiki TEXT, intended for display
3432 ** on a timeline. The timeline-plaintext and timeline-hard-newlines
3433 ** settings are considered when doing this rendering.
3434 */
3435 static void wiki_to_text_sqlfunc(
3436 sqlite3_context *context,
3437 int argc,
3438 sqlite3_value **argv
@@ -3486,14 +3443,14 @@
3443 zIn = (const char*)sqlite3_value_text(argv[0]);
3444 if( zIn==0 ) return;
3445 nIn = sqlite3_value_bytes(argv[0]);
3446 blob_init(&in, zIn, nIn);
3447 blob_init(&html, 0, 0);
3448 wiki_convert(&in, &html, wiki_convert_flags(0));
3449 blob_reset(&in);
3450 blob_init(&txt, 0, 0);
3451 html_to_plaintext(blob_str(&html), &txt, 0);
3452 blob_reset(&html);
3453 nOut = blob_size(&txt);
3454 zOut = blob_str(&txt);
3455 while( fossil_isspace(zOut[0]) ){ zOut++; nOut--; }
3456 while( nOut>0 && fossil_isspace(zOut[nOut-1]) ){ nOut--; }
3457
+322 -185
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -23,23 +23,43 @@
2323
2424
#if INTERFACE
2525
/*
2626
** Allowed wiki transformation operations
2727
*/
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
-#define WIKI_NEWLINE 0x040 /* Honor \n - break lines at each \n */
35
-#define WIKI_MARKDOWNLINKS 0x080 /* Resolve hyperlinks as in markdown */
36
-#define WIKI_SAFE 0x100 /* Make the result safe for embedding */
37
-#define WIKI_TARGET_BLANK 0x200 /* Hyperlinks go to a new window */
38
-#define WIKI_NOBRACKET 0x400 /* Omit extra [..] around hyperlinks */
39
-#define WIKI_ADMIN 0x800 /* Ignore g.perm.Hyperlink */
40
-#endif
28
+#define WIKI_HTMLONLY 0x0001 /* HTML markup only. No wiki */
29
+#define WIKI_INLINE 0x0002 /* Do not surround with <p>..</p> */
30
+/* avalable for reuse: 0x0004 --- formerly WIKI_NOBLOCK */
31
+#define WIKI_BUTTONS 0x0008 /* Allow sub-menu buttons */
32
+#define WIKI_NOBADLINKS 0x0010 /* Ignore broken hyperlinks */
33
+#define WIKI_LINKSONLY 0x0020 /* No markup. Only decorate links */
34
+#define WIKI_NEWLINE 0x0040 /* Honor \n - break lines at each \n */
35
+#define WIKI_MARKDOWNLINKS 0x0080 /* Resolve hyperlinks as in markdown */
36
+#define WIKI_SAFE 0x0100 /* Make the result safe for embedding */
37
+#define WIKI_TARGET_BLANK 0x0200 /* Hyperlinks go to a new window */
38
+#define WIKI_NOBRACKET 0x0400 /* Omit extra [..] around hyperlinks */
39
+#define WIKI_ADMIN 0x0800 /* Ignore g.perm.Hyperlink */
40
+#define WIKI_MARK 0x1000 /* Add <mark>..</mark> around problems */
41
+
42
+/*
43
+** Return values from wiki_convert
44
+*/
45
+#define RENDER_LINK 0x0001 /* One or more hyperlinks rendered */
46
+#define RENDER_ENTITY 0x0002 /* One or more HTML entities (ex: &lt;) */
47
+#define RENDER_TAG 0x0004 /* One or more HTML tags */
48
+#define RENDER_BLOCKTAG 0x0008 /* One or more HTML block tags (ex: <p>) */
49
+#define RENDER_BLOCK 0x0010 /* Block wiki (paragraphs, etc.) */
50
+#define RENDER_MARK 0x0020 /* Output contains <mark>..</mark> */
51
+#define RENDER_BADLINK 0x0100 /* Bad hyperlink syntax seen */
52
+#define RENDER_BADTARGET 0x0200 /* Bad hyperlink target */
53
+#define RENDER_BADTAG 0x0400 /* Bad HTML tag or tag syntax */
54
+#define RENDER_BADENTITY 0x0800 /* Bad HTML entity syntax */
55
+#define RENDER_BADHTML 0x1000 /* Bad HTML seen */
56
+#define RENDER_ERROR 0x8000 /* Some other kind of error */
57
+/* Composite values: */
58
+#define RENDER_ANYERROR 0x9f00 /* Mask for any kind of error */
59
+
60
+#endif /* INTERFACE */
4161
4262
4363
/*
4464
** These are the only markup attributes allowed.
4565
*/
@@ -445,20 +465,20 @@
445465
#define AT_NEWLINE 0x0010000 /* At start of a line */
446466
#define AT_PARAGRAPH 0x0020000 /* At start of a paragraph */
447467
#define ALLOW_WIKI 0x0040000 /* Allow wiki markup */
448468
#define ALLOW_LINKS 0x0080000 /* Allow [...] hyperlinks */
449469
#define FONT_MARKUP_ONLY 0x0100000 /* Only allow MUTYPE_FONT markup */
450
-#define INLINE_MARKUP_ONLY 0x0200000 /* Allow only "inline" markup */
451
-#define IN_LIST 0x0400000 /* Within wiki <ul> or <ol> */
470
+#define IN_LIST 0x0200000 /* Within wiki <ul> or <ol> */
452471
453472
/*
454473
** Current state of the rendering engine
455474
*/
456475
typedef struct Renderer Renderer;
457476
struct Renderer {
458477
Blob *pOut; /* Output appended to this blob */
459478
int state; /* Flag that govern rendering */
479
+ int mRender; /* Mask of RENDER_* values to return */
460480
unsigned renderFlags; /* Flags from the client */
461481
int wikiList; /* Current wiki list type */
462482
int inVerbatim; /* True in <verbatim> mode */
463483
int preVerbState; /* Value of state prior to verbatim */
464484
int wantAutoParagraph; /* True if a <p> is desired */
@@ -680,21 +700,26 @@
680700
static int nextWikiToken(const char *z, Renderer *p, int *pTokenType){
681701
int n;
682702
if( z[0]=='<' ){
683703
n = html_tag_length(z);
684704
if( n>0 ){
705
+ p->mRender |= RENDER_TAG;
685706
*pTokenType = TOKEN_MARKUP;
686707
return n;
687708
}else{
709
+ p->mRender |= RENDER_BADTAG;
710
+ *pTokenType = TOKEN_CHARACTER;
711
+ return 1;
712
+ }
713
+ }
714
+ if( z[0]=='&' ){
715
+ p->mRender |= RENDER_ENTITY;
716
+ if( (p->inVerbatim || !isElement(z)) ){
688717
*pTokenType = TOKEN_CHARACTER;
689718
return 1;
690719
}
691720
}
692
- if( z[0]=='&' && (p->inVerbatim || !isElement(z)) ){
693
- *pTokenType = TOKEN_CHARACTER;
694
- return 1;
695
- }
696721
if( (p->state & ALLOW_WIKI)!=0 ){
697722
if( z[0]=='\n' ){
698723
n = paragraphBreakLength(z);
699724
if( n>0 ){
700725
*pTokenType = TOKEN_PARAGRAPH;
@@ -726,17 +751,31 @@
726751
if( n>0 ){
727752
*pTokenType = TOKEN_INDENT;
728753
return n;
729754
}
730755
}
731
- if( z[0]=='[' && (n = linkLength(z))>0 ){
756
+ if( z[0]=='[' ){
757
+ if( (n = linkLength(z))>0 ){
758
+ *pTokenType = TOKEN_LINK;
759
+ return n;
760
+ }else if( p->state & WIKI_MARK ){
761
+ blob_append_string(p->pOut, "<mark>");
762
+ p->mRender |= RENDER_BADLINK|RENDER_MARK;
763
+ }else{
764
+ p->mRender |= RENDER_BADLINK;
765
+ }
766
+ }
767
+ }else if( (p->state & ALLOW_LINKS)!=0 && z[0]=='[' ){
768
+ if( (n = linkLength(z))>0 ){
732769
*pTokenType = TOKEN_LINK;
733770
return n;
771
+ }else if( p->state & WIKI_MARK ){
772
+ blob_append_string(p->pOut, "<mark>");
773
+ p->mRender |= RENDER_BADLINK|RENDER_MARK;
774
+ }else{
775
+ p->mRender |= RENDER_BADLINK;
734776
}
735
- }else if( (p->state & ALLOW_LINKS)!=0 && z[0]=='[' && (n = linkLength(z))>0 ){
736
- *pTokenType = TOKEN_LINK;
737
- return n;
738777
}
739778
*pTokenType = TOKEN_TEXT;
740779
return 1 + textLength(z+1, p->state);
741780
}
742781
@@ -746,13 +785,20 @@
746785
** z points to the start of a token. Return the number of
747786
** characters in that token. Write the token type into *pTokenType.
748787
*/
749788
static int nextRawToken(const char *z, Renderer *p, int *pTokenType){
750789
int n;
751
- if( z[0]=='[' && (n = linkLength(z))>0 ){
752
- *pTokenType = TOKEN_LINK;
753
- return n;
790
+ if( z[0]=='[' ){
791
+ if( (n = linkLength(z))>0 ){
792
+ *pTokenType = TOKEN_LINK;
793
+ return n;
794
+ }else if( p->state & WIKI_MARK ){
795
+ blob_append_string(p->pOut, "<mark>");
796
+ p->mRender |= RENDER_BADLINK|RENDER_MARK;
797
+ }else{
798
+ p->mRender |= RENDER_BADLINK;
799
+ }
754800
}
755801
*pTokenType = TOKEN_RAW;
756802
return 1 + textLength(z+1, p->state);
757803
}
758804
@@ -1249,12 +1295,16 @@
12491295
** [wiki:WikiPageName]
12501296
**
12511297
** [2010-02-27 07:13]
12521298
**
12531299
** [InterMap:Link] -> Interwiki link
1300
+**
1301
+** The return value is a mask of RENDER_* values indicating what happened.
1302
+** Probably the return value is 0 on success and RENDER_BADTARGET or
1303
+** RENDER_BADLINK if there are problems.
12541304
*/
1255
-void wiki_resolve_hyperlink(
1305
+int wiki_resolve_hyperlink(
12561306
Blob *pOut, /* Write the HTML output here */
12571307
int mFlags, /* Rendering option flags */
12581308
const char *zTarget, /* Hyperlink target; text within [...] */
12591309
char *zClose, /* Write hyperlink closing text here */
12601310
int nClose, /* Bytes available in zClose[] */
@@ -1264,10 +1314,11 @@
12641314
const char *zTerm = "</a>";
12651315
const char *z;
12661316
char *zExtra = 0;
12671317
const char *zExtraNS = 0;
12681318
char *zRemote = 0;
1319
+ int rc = 0;
12691320
12701321
if( zTitle ){
12711322
zExtra = mprintf(" title='%h'", zTitle);
12721323
zExtraNS = zExtra+1;
12731324
}else if( mFlags & WIKI_TARGET_BLANK ){
@@ -1317,14 +1368,19 @@
13171368
}
13181369
}
13191370
}else if( !in_this_repo(zTarget) ){
13201371
if( (mFlags & (WIKI_LINKSONLY|WIKI_NOBADLINKS))!=0 ){
13211372
zTerm = "";
1373
+ }else if( (mFlags & WIKI_MARK)!=0 ){
1374
+ blob_appendf(pOut, "<mark>%s", zLB);
1375
+ zTerm = "]</mark>";
1376
+ rc |= RENDER_MARK;
13221377
}else{
13231378
blob_appendf(pOut, "<span class=\"brokenlink\">%s", zLB);
13241379
zTerm = "]</span>";
13251380
}
1381
+ rc |= RENDER_BADTARGET;
13261382
}else if( g.perm.Hyperlink || (mFlags & WIKI_ADMIN)!=0 ){
13271383
blob_appendf(pOut, "%z%s",xhref(zExtraNS, "%R/info/%s", zTarget), zLB);
13281384
zTerm = "]</a>";
13291385
}else{
13301386
zTerm = "";
@@ -1341,33 +1397,40 @@
13411397
}else{
13421398
blob_appendf(pOut, "<a href=\"%R/wiki?name=%T\"%s>", z, zExtra);
13431399
}
13441400
}else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
13451401
&& db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){
1346
- /* Dates or date-and-times in ISO8610 resolve to a link to the
1402
+ /* Dates or date-and-times in ISO8601 resolve to a link to the
13471403
** timeline for that date */
13481404
blob_appendf(pOut, "<a href=\"%R/timeline?c=%T\"%s>", zTarget, zExtra);
13491405
}else if( mFlags & WIKI_MARKDOWNLINKS ){
13501406
/* If none of the above, and if rendering links for markdown, then
13511407
** create a link to the literal text of the target */
13521408
blob_appendf(pOut, "<a href=\"%h\"%s>", zTarget, zExtra);
1409
+ }else if( mFlags & WIKI_MARK ){
1410
+ blob_appendf(pOut, "<mark>[");
1411
+ zTerm = "]</mark>";
1412
+ rc |= RENDER_BADTARGET|RENDER_MARK;
13531413
}else if( zOrig && zTarget>=&zOrig[2]
13541414
&& zTarget[-1]=='[' && !fossil_isspace(zTarget[-2]) ){
13551415
/* If the hyperlink markup is not preceded by whitespace, then it
13561416
** is probably a C-language subscript or similar, not really a
13571417
** hyperlink. Just ignore it. */
13581418
zTerm = "";
13591419
}else if( (mFlags & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){
13601420
/* Also ignore the link if various flags are set */
13611421
zTerm = "";
1422
+ rc |= RENDER_BADTARGET;
13621423
}else{
13631424
blob_appendf(pOut, "<span class=\"brokenlink\">[%h]", zTarget);
13641425
zTerm = "</span>";
1426
+ rc |= RENDER_BADTARGET;
13651427
}
13661428
if( zExtra ) fossil_free(zExtra);
13671429
assert( (int)strlen(zTerm)<nClose );
13681430
sqlite3_snprintf(nClose, zClose, "%s", zTerm);
1431
+ return rc;
13691432
}
13701433
13711434
/*
13721435
** Check zTarget to see if it looks like a valid hyperlink target.
13731436
** Return true if it does seem valid and false if not.
@@ -1465,11 +1528,10 @@
14651528
*/
14661529
static void wiki_render(Renderer *p, char *z){
14671530
int tokenType;
14681531
ParsedMarkup markup;
14691532
int n;
1470
- int inlineOnly = (p->state & INLINE_MARKUP_ONLY)!=0;
14711533
int wikiHtmlOnly = (p->state & (WIKI_HTMLONLY | WIKI_LINKSONLY))!=0;
14721534
int linksOnly = (p->state & WIKI_LINKSONLY)!=0;
14731535
char *zOrig = z;
14741536
14751537
/* Make sure the attribute constants and names still align
@@ -1483,22 +1545,17 @@
14831545
n = nextWikiToken(z, p, &tokenType);
14841546
}
14851547
p->state &= ~(AT_NEWLINE|AT_PARAGRAPH);
14861548
switch( tokenType ){
14871549
case TOKEN_PARAGRAPH: {
1488
- if( inlineOnly ){
1489
- /* blob_append_string(p->pOut, " &para; "); */
1490
- blob_append_string(p->pOut, " &nbsp;&nbsp; ");
1491
- }else{
1492
- if( p->wikiList ){
1493
- popStackToTag(p, p->wikiList);
1494
- p->wikiList = 0;
1495
- }
1496
- endAutoParagraph(p);
1497
- blob_append_string(p->pOut, "\n\n");
1498
- p->wantAutoParagraph = 1;
1499
- }
1550
+ if( p->wikiList ){
1551
+ popStackToTag(p, p->wikiList);
1552
+ p->wikiList = 0;
1553
+ }
1554
+ endAutoParagraph(p);
1555
+ blob_append_string(p->pOut, "\n\n");
1556
+ p->wantAutoParagraph = 1;
15001557
p->state |= AT_PARAGRAPH|AT_NEWLINE;
15011558
break;
15021559
}
15031560
case TOKEN_NEWLINE: {
15041561
if( p->renderFlags & WIKI_NEWLINE ){
@@ -1508,86 +1565,91 @@
15081565
}
15091566
p->state |= AT_NEWLINE;
15101567
break;
15111568
}
15121569
case TOKEN_BUL_LI: {
1513
- if( inlineOnly ){
1514
- blob_append_string(p->pOut, " &bull; ");
1515
- }else{
1516
- if( p->wikiList!=MARKUP_UL ){
1517
- if( p->wikiList ){
1518
- popStackToTag(p, p->wikiList);
1519
- }
1520
- endAutoParagraph(p);
1521
- pushStack(p, MARKUP_UL);
1522
- blob_append_string(p->pOut, "<ul>");
1523
- p->wikiList = MARKUP_UL;
1524
- }
1525
- popStackToTag(p, MARKUP_LI);
1526
- startAutoParagraph(p);
1527
- pushStack(p, MARKUP_LI);
1528
- blob_append_string(p->pOut, "<li>");
1529
- }
1570
+ p->mRender |= RENDER_BLOCK;
1571
+ if( p->wikiList!=MARKUP_UL ){
1572
+ if( p->wikiList ){
1573
+ popStackToTag(p, p->wikiList);
1574
+ }
1575
+ endAutoParagraph(p);
1576
+ pushStack(p, MARKUP_UL);
1577
+ blob_append_string(p->pOut, "<ul>");
1578
+ p->wikiList = MARKUP_UL;
1579
+ }
1580
+ popStackToTag(p, MARKUP_LI);
1581
+ startAutoParagraph(p);
1582
+ pushStack(p, MARKUP_LI);
1583
+ blob_append_string(p->pOut, "<li>");
15301584
break;
15311585
}
15321586
case TOKEN_NUM_LI: {
1533
- if( inlineOnly ){
1534
- blob_append_string(p->pOut, " # ");
1535
- }else{
1536
- if( p->wikiList!=MARKUP_OL ){
1537
- if( p->wikiList ){
1538
- popStackToTag(p, p->wikiList);
1539
- }
1540
- endAutoParagraph(p);
1541
- pushStack(p, MARKUP_OL);
1542
- blob_append_string(p->pOut, "<ol>");
1543
- p->wikiList = MARKUP_OL;
1544
- }
1545
- popStackToTag(p, MARKUP_LI);
1546
- startAutoParagraph(p);
1547
- pushStack(p, MARKUP_LI);
1548
- blob_append_string(p->pOut, "<li>");
1549
- }
1587
+ p->mRender |= RENDER_BLOCK;
1588
+ if( p->wikiList!=MARKUP_OL ){
1589
+ if( p->wikiList ){
1590
+ popStackToTag(p, p->wikiList);
1591
+ }
1592
+ endAutoParagraph(p);
1593
+ pushStack(p, MARKUP_OL);
1594
+ blob_append_string(p->pOut, "<ol>");
1595
+ p->wikiList = MARKUP_OL;
1596
+ }
1597
+ popStackToTag(p, MARKUP_LI);
1598
+ startAutoParagraph(p);
1599
+ pushStack(p, MARKUP_LI);
1600
+ blob_append_string(p->pOut, "<li>");
15501601
break;
15511602
}
15521603
case TOKEN_ENUM: {
1553
- if( inlineOnly ){
1554
- blob_appendf(p->pOut, " (%d) ", atoi(z));
1555
- }else{
1556
- if( p->wikiList!=MARKUP_OL ){
1557
- if( p->wikiList ){
1558
- popStackToTag(p, p->wikiList);
1559
- }
1560
- endAutoParagraph(p);
1561
- pushStack(p, MARKUP_OL);
1562
- blob_append_string(p->pOut, "<ol>");
1563
- p->wikiList = MARKUP_OL;
1564
- }
1565
- popStackToTag(p, MARKUP_LI);
1566
- startAutoParagraph(p);
1567
- pushStack(p, MARKUP_LI);
1568
- blob_appendf(p->pOut, "<li value=\"%d\">", atoi(z));
1569
- }
1604
+ p->mRender |= RENDER_BLOCK;
1605
+ if( p->wikiList!=MARKUP_OL ){
1606
+ if( p->wikiList ){
1607
+ popStackToTag(p, p->wikiList);
1608
+ }
1609
+ endAutoParagraph(p);
1610
+ pushStack(p, MARKUP_OL);
1611
+ blob_append_string(p->pOut, "<ol>");
1612
+ p->wikiList = MARKUP_OL;
1613
+ }
1614
+ popStackToTag(p, MARKUP_LI);
1615
+ startAutoParagraph(p);
1616
+ pushStack(p, MARKUP_LI);
1617
+ blob_appendf(p->pOut, "<li value=\"%d\">", atoi(z));
15701618
break;
15711619
}
15721620
case TOKEN_INDENT: {
1573
- if( !inlineOnly ){
1574
- assert( p->wikiList==0 );
1575
- pushStack(p, MARKUP_BLOCKQUOTE);
1576
- blob_append_string(p->pOut, "<blockquote>");
1577
- p->wantAutoParagraph = 0;
1578
- p->wikiList = MARKUP_BLOCKQUOTE;
1579
- }
1621
+ p->mRender |= RENDER_BLOCK;
1622
+ assert( p->wikiList==0 );
1623
+ pushStack(p, MARKUP_BLOCKQUOTE);
1624
+ blob_append_string(p->pOut, "<blockquote>");
1625
+ p->wantAutoParagraph = 0;
1626
+ p->wikiList = MARKUP_BLOCKQUOTE;
15801627
break;
15811628
}
15821629
case TOKEN_CHARACTER: {
15831630
startAutoParagraph(p);
1631
+ if( p->state & WIKI_MARK ){
1632
+ blob_append_string(p->pOut, "<mark>");
1633
+ p->mRender |= RENDER_MARK;
1634
+ }
15841635
if( z[0]=='<' ){
1636
+ p->mRender |= RENDER_BADTAG;
15851637
blob_append_string(p->pOut, "&lt;");
15861638
}else if( z[0]=='&' ){
1639
+ p->mRender |= RENDER_BADENTITY;
15871640
blob_append_string(p->pOut, "&amp;");
15881641
}
1642
+ if( p->state & WIKI_MARK ){
1643
+ if( fossil_isalnum(z[1]) || (z[1]=='/' && fossil_isalnum(z[2])) ){
1644
+ int kk;
1645
+ for(kk=2; fossil_isalnum(z[kk]); kk++){}
1646
+ blob_append(p->pOut, &z[1], kk-1);
1647
+ n = kk;
1648
+ }
1649
+ blob_append_string(p->pOut, "</mark>");
1650
+ }
15891651
break;
15901652
}
15911653
case TOKEN_LINK: {
15921654
char *zTarget;
15931655
char *zDisplay = 0;
@@ -1596,10 +1658,11 @@
15961658
char zClose[20];
15971659
char cS1 = 0;
15981660
int iS1 = 0;
15991661
16001662
startAutoParagraph(p);
1663
+ p->mRender |= RENDER_LINK;
16011664
zTarget = &z[1];
16021665
for(i=1; z[i] && z[i]!=']'; i++){
16031666
if( z[i]=='|' && zDisplay==0 ){
16041667
zDisplay = &z[i+1];
16051668
for(j=i; j>0 && fossil_isspace(z[j-1]); j--){}
@@ -1612,11 +1675,11 @@
16121675
if( zDisplay==0 ){
16131676
zDisplay = zTarget + interwiki_removable_prefix(zTarget);
16141677
}else{
16151678
while( fossil_isspace(*zDisplay) ) zDisplay++;
16161679
}
1617
- wiki_resolve_hyperlink(p->pOut, p->state,
1680
+ p->mRender |= wiki_resolve_hyperlink(p->pOut, p->state,
16181681
zTarget, zClose, sizeof(zClose), zOrig, 0);
16191682
if( linksOnly || zClose[0]==0 || p->inVerbatim ){
16201683
if( cS1 ) z[iS1] = cS1;
16211684
if( zClose[0]!=']' ){
16221685
blob_appendf(p->pOut, "[%h]%s", zTarget, zClose);
@@ -1702,14 +1765,22 @@
17021765
17031766
/* Render invalid markup literally. The markup appears in the
17041767
** final output as plain text.
17051768
*/
17061769
if( markup.iCode==MARKUP_INVALID ){
1770
+ p->mRender |= RENDER_BADTAG;
17071771
unparseMarkup(&markup);
17081772
startAutoParagraph(p);
1709
- blob_append_string(p->pOut, "&lt;");
1710
- n = 1;
1773
+ if( p->state & WIKI_MARK ){
1774
+ p->mRender |= RENDER_MARK;
1775
+ blob_append_string(p->pOut, "<mark>");
1776
+ htmlize_to_blob(p->pOut, z, n);
1777
+ blob_append_string(p->pOut, "</mark>");
1778
+ }else{
1779
+ blob_append_string(p->pOut, "&lt;");
1780
+ htmlize_to_blob(p->pOut, z+1, n-1);
1781
+ }
17111782
}else
17121783
17131784
/* If the markup is not font-change markup ignore it if the
17141785
** font-change-only flag is set.
17151786
*/
@@ -1723,16 +1794,10 @@
17231794
}else{
17241795
p->state &= ~ALLOW_WIKI;
17251796
}
17261797
}else
17271798
1728
- /* Ignore block markup for in-line rendering.
1729
- */
1730
- if( inlineOnly && (markup.iType&MUTYPE_INLINE)==0 ){
1731
- /* Do nothing */
1732
- }else
1733
-
17341799
/* Generate end-tags */
17351800
if( markup.endTag ){
17361801
popStackToTag(p, markup.iCode);
17371802
}else
17381803
@@ -1814,10 +1879,11 @@
18141879
}else
18151880
{
18161881
if( markup.iType==MUTYPE_FONT ){
18171882
startAutoParagraph(p);
18181883
}else if( markup.iType==MUTYPE_BLOCK || markup.iType==MUTYPE_LIST ){
1884
+ p->mRender |= RENDER_BLOCKTAG;
18191885
p->wantAutoParagraph = 0;
18201886
}
18211887
if( markup.iCode==MARKUP_HR
18221888
|| markup.iCode==MARKUP_H1
18231889
|| markup.iCode==MARKUP_H2
@@ -1844,12 +1910,14 @@
18441910
** Transform the text in the pIn blob. Write the results
18451911
** into the pOut blob. The pOut blob should already be
18461912
** initialized. The output is merely appended to pOut.
18471913
** If pOut is NULL, then the output is appended to the CGI
18481914
** reply.
1915
+**
1916
+** Return a mask of RENDER_ flags indicating what happened.
18491917
*/
1850
-void wiki_convert(Blob *pIn, Blob *pOut, int flags){
1918
+int wiki_convert(Blob *pIn, Blob *pOut, int flags){
18511919
Renderer renderer;
18521920
18531921
memset(&renderer, 0, sizeof(renderer));
18541922
renderer.renderFlags = flags;
18551923
renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH|flags;
@@ -1873,10 +1941,11 @@
18731941
while( renderer.nStack ){
18741942
popStack(&renderer);
18751943
}
18761944
blob_append_char(renderer.pOut, '\n');
18771945
free(renderer.aStack);
1946
+ return renderer.mRender;
18781947
}
18791948
18801949
/*
18811950
** COMMAND: test-wiki-render
18821951
**
@@ -1886,45 +1955,84 @@
18861955
** the resulting HTML on standard output.
18871956
**
18881957
** Options:
18891958
** --buttons Set the WIKI_BUTTONS flag
18901959
** --dark-pikchr Render pikchrs in dark mode
1960
+** --flow Render as text using comment_format
18911961
** --htmlonly Set the WIKI_HTMLONLY flag
18921962
** --inline Set the WIKI_INLINE flag
18931963
** --linksonly Set the WIKI_LINKSONLY flag
1964
+** -m TEXT Use TEXT in place of the content of FILE
1965
+** --mark Add <mark>...</mark> around problems
18941966
** --nobadlinks Set the WIKI_NOBADLINKS flag
1895
-** --noblock Set the WIKI_NOBLOCK flag
1896
-** --text Run the output through html_to_plaintext().
1967
+** --text Run the output through html_to_plaintext()
1968
+** --type Break down the return code from wiki_convert()
18971969
*/
18981970
void test_wiki_render(void){
18991971
Blob in, out;
19001972
int flags = 0;
19011973
int bText;
1974
+ int bFlow = 0;
1975
+ int showType = 0;
1976
+ int mType;
1977
+ const char *zIn;
19021978
if( find_option("buttons",0,0)!=0 ) flags |= WIKI_BUTTONS;
19031979
if( find_option("htmlonly",0,0)!=0 ) flags |= WIKI_HTMLONLY;
19041980
if( find_option("linksonly",0,0)!=0 ) flags |= WIKI_LINKSONLY;
19051981
if( find_option("nobadlinks",0,0)!=0 ) flags |= WIKI_NOBADLINKS;
19061982
if( find_option("inline",0,0)!=0 ) flags |= WIKI_INLINE;
1907
- if( find_option("noblock",0,0)!=0 ) flags |= WIKI_NOBLOCK;
1983
+ if( find_option("mark",0,0)!=0 ) flags |= WIKI_MARK;
19081984
if( find_option("dark-pikchr",0,0)!=0 ){
19091985
pikchr_to_html_add_flags( PIKCHR_PROCESS_DARK_MODE );
19101986
}
19111987
bText = find_option("text",0,0)!=0;
1988
+ bFlow = find_option("flow",0,0)!=0;
1989
+ showType = find_option("type",0,0)!=0;
1990
+ zIn = find_option("msg","m",1);
19121991
db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
19131992
verify_all_options();
1914
- if( g.argc!=3 ) usage("FILE");
1993
+ if( (zIn==0 && g.argc!=3) || (zIn!=0 && g.argc!=2) ) usage("FILE");
19151994
blob_zero(&out);
1916
- blob_read_from_file(&in, g.argv[2], ExtFILE);
1917
- wiki_convert(&in, &out, flags);
1995
+ if( zIn ){
1996
+ blob_init(&in, zIn, -1);
1997
+ }else{
1998
+ blob_read_from_file(&in, g.argv[2], ExtFILE);
1999
+ }
2000
+ mType = wiki_convert(&in, &out, flags);
19182001
if( bText ){
19192002
Blob txt;
2003
+ int htot = HTOT_TRIM;
2004
+ if( terminal_is_vt100() ) htot |= HTOT_VT100;
2005
+ if( bFlow ) htot |= HTOT_FLOW;
19202006
blob_init(&txt, 0, 0);
1921
- html_to_plaintext(blob_str(&out),&txt);
2007
+ html_to_plaintext(blob_str(&out),&txt, htot);
19222008
blob_reset(&out);
19232009
out = txt;
19242010
}
1925
- blob_write_to_file(&out, "-");
2011
+ if( bFlow ){
2012
+ fossil_print(" ");
2013
+ comment_print(blob_str(&out), 0, 3, terminal_get_width(80)-3,
2014
+ get_comment_format());
2015
+ }else{
2016
+ blob_write_to_file(&out, "-");
2017
+ }
2018
+ if( showType ){
2019
+ fossil_print("%.*c\nResult Codes:", terminal_get_width(80)-1, '*');
2020
+ if( mType & RENDER_LINK ) fossil_print(" LINK");
2021
+ if( mType & RENDER_ENTITY ) fossil_print(" ENTITY");
2022
+ if( mType & RENDER_TAG ) fossil_print(" TAG");
2023
+ if( mType & RENDER_BLOCKTAG ) fossil_print(" BLOCKTAG");
2024
+ if( mType & RENDER_BLOCK ) fossil_print(" BLOCK");
2025
+ if( mType & RENDER_MARK ) fossil_print(" MARK");
2026
+ if( mType & RENDER_BADLINK ) fossil_print(" BADLINK");
2027
+ if( mType & RENDER_BADTARGET ) fossil_print(" BADTARGET");
2028
+ if( mType & RENDER_BADTAG ) fossil_print(" BADTAG");
2029
+ if( mType & RENDER_BADENTITY ) fossil_print(" BADENTITY");
2030
+ if( mType & RENDER_BADHTML ) fossil_print(" BADHTML");
2031
+ if( mType & RENDER_ERROR ) fossil_print(" ERROR");
2032
+ fossil_print("\n");
2033
+ }
19262034
}
19272035
19282036
/*
19292037
** COMMAND: test-markdown-render
19302038
**
@@ -1960,11 +2068,11 @@
19602068
safe_html_context( bSafe ? DOCSRC_UNTRUSTED : DOCSRC_TRUSTED );
19612069
safe_html(&out);
19622070
if( bText ){
19632071
Blob txt;
19642072
blob_init(&txt, 0, 0);
1965
- html_to_plaintext(blob_str(&out), &txt);
2073
+ html_to_plaintext(blob_str(&out), &txt, HTOT_VT100);
19662074
blob_reset(&out);
19672075
out = txt;
19682076
}
19692077
blob_write_to_file(&out, "-");
19702078
blob_reset(&in);
@@ -2024,33 +2132,30 @@
20242132
** [target|...]
20252133
**
20262134
** Where "target" can be either an artifact ID prefix or a wiki page
20272135
** name. For each such hyperlink found, add an entry to the
20282136
** backlink table.
2137
+**
2138
+** The return value is a mask of RENDER_ flags.
20292139
*/
2030
-void wiki_extract_links(
2140
+int wiki_extract_links(
20312141
char *z, /* The wiki text from which to extract links */
20322142
Backlink *pBklnk, /* Backlink extraction context */
20332143
int flags /* wiki parsing flags */
20342144
){
20352145
Renderer renderer;
20362146
int tokenType;
20372147
ParsedMarkup markup;
20382148
int n;
2039
- int inlineOnly;
20402149
int wikiHtmlOnly = 0;
20412150
20422151
memset(&renderer, 0, sizeof(renderer));
20432152
renderer.state = ALLOW_WIKI|AT_NEWLINE|AT_PARAGRAPH;
2044
- if( flags & WIKI_NOBLOCK ){
2045
- renderer.state |= INLINE_MARKUP_ONLY;
2046
- }
20472153
if( wikiUsesHtml() ){
20482154
renderer.state |= WIKI_HTMLONLY;
20492155
wikiHtmlOnly = 1;
20502156
}
2051
- inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0;
20522157
20532158
while( z[0] ){
20542159
if( wikiHtmlOnly ){
20552160
n = nextRawToken(z, &renderer, &tokenType);
20562161
}else{
@@ -2127,16 +2232,10 @@
21272232
}else{
21282233
renderer.state &= ~ALLOW_WIKI;
21292234
}
21302235
}else
21312236
2132
- /* Ignore block markup for in-line rendering.
2133
- */
2134
- if( inlineOnly && (markup.iType&MUTYPE_INLINE)==0 ){
2135
- /* Do nothing */
2136
- }else
2137
-
21382237
/* Generate end-tags */
21392238
if( markup.endTag ){
21402239
popStackToTag(&renderer, markup.iCode);
21412240
}else
21422241
@@ -2175,10 +2274,11 @@
21752274
}
21762275
}
21772276
z += n;
21782277
}
21792278
free(renderer.aStack);
2279
+ return renderer.mRender;
21802280
}
21812281
21822282
/*
21832283
** Return the length, in bytes, of the HTML token that z is pointing to.
21842284
*/
@@ -2427,33 +2527,61 @@
24272527
blob_reset(&in);
24282528
fossil_puts(blob_buffer(&out), 0, blob_size(&out));
24292529
blob_reset(&out);
24302530
}
24312531
}
2532
+
2533
+#if INTERFACE
2534
+/*
2535
+** Allowed flag options for html_to_plaintext().
2536
+*/
2537
+#define HTOT_VT100 0x01 /* <mark> becomes ^[[91m */
2538
+#define HTOT_FLOW 0x02 /* Collapse internal whitespace to a single space */
2539
+#define HTOT_TRIM 0x04 /* Trim off leading and trailing whitespace */
2540
+
2541
+#endif /* INTERFACE */
2542
+
2543
+/*
2544
+** Add <MARK> or </MARK> to the output, or similar VT-100 escape
2545
+** codes.
2546
+*/
2547
+static void addMark(Blob *pOut, int mFlags, int isClose){
2548
+ static const char *az[4] = { "<MARK>", "</MARK>", "\033[91m", "\033[0m" };
2549
+ int i = 0;
2550
+ if( isClose ) i++;
2551
+ if( mFlags & HTOT_VT100 ) i += 2;
2552
+ blob_append(pOut, az[i], -1);
2553
+}
24322554
24332555
/*
24342556
** Remove all HTML markup from the input text. The output written into