Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Merge from trunk. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | bv-infotool |
| Files: | files | file ages | folders |
| SHA3-256: |
0ffacb525a925ba8da3152c9041fdf98 |
| User & Date: | brickviking 2025-10-23 08:14:54.520 |
Context
|
2025-10-29
| ||
| 11:10 | Merge from trunk. ... (check-in: 50b56eec70 user: brickviking tags: bv-infotool) | |
|
2025-10-23
| ||
| 08:14 | Merge from trunk. ... (check-in: 0ffacb525a user: brickviking tags: bv-infotool) | |
| 07:57 | Small grammar change in the changes.wiki page. ... (check-in: 2ff040c438 user: brickviking tags: trunk) | |
|
2025-07-28
| ||
| 02:32 | Merge from trunk. ... (check-in: 53010e9688 user: brickviking tags: bv-infotool) | |
Changes
Changes to VERSION.
|
| | | 1 | 2.28 |
Changes to extsrc/shell.c.
| ︙ | ︙ | |||
120 121 122 123 124 125 126 127 128 129 130 131 132 133 | #include <math.h> #include "sqlite3.h" typedef sqlite3_int64 i64; typedef sqlite3_uint64 u64; typedef unsigned char u8; #include <ctype.h> #include <stdarg.h> #if !defined(_WIN32) && !defined(WIN32) # include <signal.h> # if !defined(__RTP__) && !defined(_WRS_KERNEL) && !defined(SQLITE_WASI) # include <pwd.h> # endif #endif | > > > | 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | #include <math.h> #include "sqlite3.h" typedef sqlite3_int64 i64; typedef sqlite3_uint64 u64; typedef unsigned char u8; #include <ctype.h> #include <stdarg.h> #ifndef _WIN32 # include <sys/time.h> #endif #if !defined(_WIN32) && !defined(WIN32) # include <signal.h> # if !defined(__RTP__) && !defined(_WRS_KERNEL) && !defined(SQLITE_WASI) # include <pwd.h> # endif #endif |
| ︙ | ︙ | |||
644 645 646 647 648 649 650 |
}
static int cli_strncmp(const char *a, const char *b, size_t n){
if( a==0 ) a = "";
if( b==0 ) b = "";
return strncmp(a,b,n);
}
| | > > > > > > > > > > > | > > > > > | 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 |
}
static int cli_strncmp(const char *a, const char *b, size_t n){
if( a==0 ) a = "";
if( b==0 ) b = "";
return strncmp(a,b,n);
}
/* Return the current wall-clock time in microseconds since the
** Unix epoch (1970-01-01T00:00:00Z)
*/
static sqlite3_int64 timeOfDay(void){
#if defined(_WIN64)
sqlite3_uint64 t;
FILETIME tm;
GetSystemTimePreciseAsFileTime(&tm);
t = ((u64)tm.dwHighDateTime<<32) | (u64)tm.dwLowDateTime;
t += 116444736000000000LL;
t /= 10;
return t;
#elif defined(_WIN32)
static sqlite3_vfs *clockVfs = 0;
sqlite3_int64 t;
if( clockVfs==0 ) clockVfs = sqlite3_vfs_find(0);
if( clockVfs==0 ) return 0; /* Never actually happens */
if( clockVfs->iVersion>=2 && clockVfs->xCurrentTimeInt64!=0 ){
clockVfs->xCurrentTimeInt64(clockVfs, &t);
}else{
double r;
clockVfs->xCurrentTime(clockVfs, &r);
t = (sqlite3_int64)(r*86400000.0);
}
return t*1000;
#else
struct timeval sNow;
(void)gettimeofday(&sNow,0);
return ((i64)sNow.tv_sec)*1000000 + sNow.tv_usec;
#endif
}
#if !defined(_WIN32) && !defined(WIN32) && !defined(__minux)
#include <sys/time.h>
#include <sys/resource.h>
/* VxWorks does not support getrusage() as far as we can determine */
|
| ︙ | ︙ | |||
702 703 704 705 706 707 708 |
** Print the timing results.
*/
static void endTimer(FILE *out){
if( enableTimer ){
sqlite3_int64 iEnd = timeOfDay();
struct rusage sEnd;
getrusage(RUSAGE_SELF, &sEnd);
| | | | 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 |
** Print the timing results.
*/
static void endTimer(FILE *out){
if( enableTimer ){
sqlite3_int64 iEnd = timeOfDay();
struct rusage sEnd;
getrusage(RUSAGE_SELF, &sEnd);
sqlite3_fprintf(out, "Run Time: real %.6f user %.6f sys %.6f\n",
(iEnd - iBegin)*0.000001,
timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
}
}
#define BEGIN_TIMER beginTimer()
#define END_TIMER(X) endTimer(X)
|
| ︙ | ︙ | |||
781 782 783 784 785 786 787 |
** Print the timing results.
*/
static void endTimer(FILE *out){
if( enableTimer && getProcessTimesAddr){
FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd;
sqlite3_int64 ftWallEnd = timeOfDay();
getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd);
| > > | | > > > > > > > | 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 |
** Print the timing results.
*/
static void endTimer(FILE *out){
if( enableTimer && getProcessTimesAddr){
FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd;
sqlite3_int64 ftWallEnd = timeOfDay();
getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd);
#ifdef _WIN64
/* microsecond precision on 64-bit windows */
sqlite3_fprintf(out, "Run Time: real %.6f user %f sys %f\n",
(ftWallEnd - ftWallBegin)*0.000001,
timeDiff(&ftUserBegin, &ftUserEnd),
timeDiff(&ftKernelBegin, &ftKernelEnd));
#else
/* millisecond precisino on 32-bit windows */
sqlite3_fprintf(out, "Run Time: real %.3f user %.3f sys %.3f\n",
(ftWallEnd - ftWallBegin)*0.000001,
timeDiff(&ftUserBegin, &ftUserEnd),
timeDiff(&ftKernelBegin, &ftKernelEnd));
#endif
}
}
#define BEGIN_TIMER beginTimer()
#define END_TIMER(X) endTimer(X)
#define HAS_TIMER hasTimer()
|
| ︙ | ︙ | |||
1124 1125 1126 1127 1128 1129 1130 |
*pU = ((z[0] & 0x0f)<<12) | ((z[1] & 0x3f)<<6) | (z[2] & 0x3f);
return 3;
}
if( (z[0] & 0xf8)==0xf0 && (z[1] & 0xc0)==0x80 && (z[2] & 0xc0)==0x80
&& (z[3] & 0xc0)==0x80
){
*pU = ((z[0] & 0x0f)<<18) | ((z[1] & 0x3f)<<12) | ((z[2] & 0x3f))<<6
| | | 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 |
*pU = ((z[0] & 0x0f)<<12) | ((z[1] & 0x3f)<<6) | (z[2] & 0x3f);
return 3;
}
if( (z[0] & 0xf8)==0xf0 && (z[1] & 0xc0)==0x80 && (z[2] & 0xc0)==0x80
&& (z[3] & 0xc0)==0x80
){
*pU = ((z[0] & 0x0f)<<18) | ((z[1] & 0x3f)<<12) | ((z[2] & 0x3f))<<6
| (z[3] & 0x3f);
return 4;
}
*pU = 0;
return 1;
}
|
| ︙ | ︙ | |||
1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 |
** then right-justify the text. W is the width in UTF-8 characters, not
** in bytes. This is different from the %*.*s specification in printf
** since with %*.*s the width is measured in bytes, not characters.
**
** Take into account zero-width and double-width Unicode characters.
** In other words, a zero-width character does not count toward the
** the w limit. A double-width character counts as two.
*/
static void utf8_width_print(FILE *out, int w, const char *zUtf){
const unsigned char *a = (const unsigned char*)zUtf;
unsigned char c;
int i = 0;
int n = 0;
int k;
| > > > > > > > > > > | | 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 |
** then right-justify the text. W is the width in UTF-8 characters, not
** in bytes. This is different from the %*.*s specification in printf
** since with %*.*s the width is measured in bytes, not characters.
**
** Take into account zero-width and double-width Unicode characters.
** In other words, a zero-width character does not count toward the
** the w limit. A double-width character counts as two.
**
** w should normally be a small number. A couple hundred at most. This
** routine caps w at 100 million to avoid integer overflow issues.
*/
static void utf8_width_print(FILE *out, int w, const char *zUtf){
const unsigned char *a = (const unsigned char*)zUtf;
static const int mxW = 10000000;
unsigned char c;
int i = 0;
int n = 0;
int k;
int aw;
if( w<-mxW ){
w = -mxW;
}else if( w>mxW ){
w= mxW;
}
aw = w<0 ? -w : w;
if( zUtf==0 ) zUtf = "";
while( (c = a[i])!=0 ){
if( (c&0xc0)==0xc0 ){
int u;
int len = decodeUtf8(a+i, &u);
int x = cli_wcwidth(u);
if( x+n>aw ){
|
| ︙ | ︙ | |||
1321 1322 1323 1324 1325 1326 1327 | #undef STAT_CHR_SRC } /* ** This routine reads a line of text from FILE in, stores ** the text in memory obtained from malloc() and returns a pointer ** to the text. NULL is returned at end of file, or if malloc() | | > > > > | 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 |
#undef STAT_CHR_SRC
}
/*
** This routine reads a line of text from FILE in, stores
** the text in memory obtained from malloc() and returns a pointer
** to the text. NULL is returned at end of file, or if malloc()
** fails, or if the length of the line is longer than about a gigabyte.
**
** If zLine is not NULL then it is a malloced buffer returned from
** a previous call to this routine that may be reused.
*/
static char *local_getline(char *zLine, FILE *in){
int nLine = zLine==0 ? 0 : 100;
int n = 0;
while( 1 ){
if( n+100>nLine ){
if( nLine>=1073741773 ){
free(zLine);
return 0;
}
nLine = nLine*2 + 100;
zLine = realloc(zLine, nLine);
shell_check_oom(zLine);
}
if( sqlite3_fgets(&zLine[n], nLine - n, in)==0 ){
if( n==0 ){
free(zLine);
|
| ︙ | ︙ | |||
1415 1416 1417 1418 1419 1420 1421 1422 1423 |
if( c>='a' && c<='f' ) return c - 'a' + 10;
if( c>='A' && c<='F' ) return c - 'A' + 10;
return -1;
}
/*
** Interpret zArg as an integer value, possibly with suffixes.
*/
static sqlite3_int64 integerValue(const char *zArg){
| > > > > | | | 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 |
if( c>='a' && c<='f' ) return c - 'a' + 10;
if( c>='A' && c<='F' ) return c - 'A' + 10;
return -1;
}
/*
** Interpret zArg as an integer value, possibly with suffixes.
**
** If the value specified by zArg is outside the range of values that
** can be represented using a 64-bit twos-complement integer, then return
** the nearest representable value.
*/
static sqlite3_int64 integerValue(const char *zArg){
sqlite3_uint64 v = 0;
static const struct { char *zSuffix; unsigned int iMult; } aMult[] = {
{ "KiB", 1024 },
{ "MiB", 1024*1024 },
{ "GiB", 1024*1024*1024 },
{ "KB", 1000 },
{ "MB", 1000000 },
{ "GB", 1000000000 },
{ "K", 1000 },
|
| ︙ | ︙ | |||
1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 |
}else if( zArg[0]=='+' ){
zArg++;
}
if( zArg[0]=='0' && zArg[1]=='x' ){
int x;
zArg += 2;
while( (x = hexDigitValue(zArg[0]))>=0 ){
v = (v<<4) + x;
zArg++;
}
}else{
while( IsDigit(zArg[0]) ){
| > > > > | > > | > > | | | | | 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 |
}else if( zArg[0]=='+' ){
zArg++;
}
if( zArg[0]=='0' && zArg[1]=='x' ){
int x;
zArg += 2;
while( (x = hexDigitValue(zArg[0]))>=0 ){
if( v > 0x0fffffffffffffffULL ) goto integer_overflow;
v = (v<<4) + x;
zArg++;
}
}else{
while( IsDigit(zArg[0]) ){
if( v>=922337203685477580LL ){
if( v>922337203685477580LL || zArg[0]>='8' ) goto integer_overflow;
}
v = v*10 + (zArg[0] - '0');
zArg++;
}
}
for(i=0; i<ArraySize(aMult); i++){
if( sqlite3_stricmp(aMult[i].zSuffix, zArg)==0 ){
if( 0x7fffffffffffffffULL/aMult[i].iMult < v ) goto integer_overflow;
v *= aMult[i].iMult;
break;
}
}
if( isNeg && v>0x7fffffffffffffffULL ) goto integer_overflow;
return isNeg? -(sqlite3_int64)v : (sqlite3_int64)v;
integer_overflow:
return isNeg ? (i64)0x8000000000000000LL : 0x7fffffffffffffffLL;
}
/*
** A variable length string to which one can append text.
*/
typedef struct ShellText ShellText;
struct ShellText {
char *zTxt; /* The text */
i64 n; /* Number of bytes of zTxt[] actually used */
i64 nAlloc; /* Number of bytes allocated for zTxt[] */
};
/*
** Initialize and destroy a ShellText object
*/
static void initText(ShellText *p){
memset(p, 0, sizeof(*p));
}
static void freeText(ShellText *p){
sqlite3_free(p->zTxt);
initText(p);
}
/* zIn is either a pointer to a NULL-terminated string in memory obtained
** from malloc(), or a NULL pointer. The string pointed to by zAppend is
** added to zIn, and the result returned in memory obtained from malloc().
** zIn, if it was not NULL, is freed.
|
| ︙ | ︙ | |||
1501 1502 1503 1504 1505 1506 1507 |
if( quote ){
len += 2;
for(i=0; i<nAppend; i++){
if( zAppend[i]==quote ) len++;
}
}
| | | | | | | | | 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 |
if( quote ){
len += 2;
for(i=0; i<nAppend; i++){
if( zAppend[i]==quote ) len++;
}
}
if( p->zTxt==0 || p->n+len>=p->nAlloc ){
p->nAlloc = p->nAlloc*2 + len + 20;
p->zTxt = sqlite3_realloc64(p->zTxt, p->nAlloc);
shell_check_oom(p->zTxt);
}
if( quote ){
char *zCsr = p->zTxt+p->n;
*zCsr++ = quote;
for(i=0; i<nAppend; i++){
*zCsr++ = zAppend[i];
if( zAppend[i]==quote ) *zCsr++ = quote;
}
*zCsr++ = quote;
p->n = (i64)(zCsr - p->zTxt);
*zCsr = '\0';
}else{
memcpy(p->zTxt+p->n, zAppend, nAppend);
p->n += nAppend;
p->zTxt[p->n] = '\0';
}
}
/*
** Attempt to determine if identifier zName needs to be quoted, either
** because it contains non-alphanumeric characters, or because it is an
** SQLite keyword. Be conservative in this estimate: When in doubt assume
|
| ︙ | ︙ | |||
1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 |
}
return sqlite3_keyword_check(zName, i) ? '"' : 0;
}
/*
** Construct a fake object name and column list to describe the structure
** of the view, virtual table, or table valued function zSchema.zName.
*/
static char *shellFakeSchema(
sqlite3 *db, /* The database connection containing the vtab */
const char *zSchema, /* Schema of the database holding the vtab */
const char *zName /* The name of the virtual table */
){
sqlite3_stmt *pStmt = 0;
| > > > | 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 |
}
return sqlite3_keyword_check(zName, i) ? '"' : 0;
}
/*
** Construct a fake object name and column list to describe the structure
** of the view, virtual table, or table valued function zSchema.zName.
**
** The returned string comes from sqlite3_mprintf() and should be freed
** by the caller using sqlite3_free().
*/
static char *shellFakeSchema(
sqlite3 *db, /* The database connection containing the vtab */
const char *zSchema, /* Schema of the database holding the vtab */
const char *zName /* The name of the virtual table */
){
sqlite3_stmt *pStmt = 0;
|
| ︙ | ︙ | |||
1585 1586 1587 1588 1589 1590 1591 |
cQuote = quoteChar(zCol);
appendText(&s, zCol, cQuote);
}
appendText(&s, ")", 0);
sqlite3_finalize(pStmt);
if( nRow==0 ){
freeText(&s);
| | | | 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 |
cQuote = quoteChar(zCol);
appendText(&s, zCol, cQuote);
}
appendText(&s, ")", 0);
sqlite3_finalize(pStmt);
if( nRow==0 ){
freeText(&s);
s.zTxt = 0;
}
return s.zTxt;
}
/*
** SQL function: strtod(X)
**
** Use the C-library strtod() function to convert string X into a double.
** Used for comparing the accuracy of SQLite's internal text-to-float conversion
|
| ︙ | ︙ | |||
1690 1691 1692 1693 1694 1695 1696 |
&& (zFake = shellFakeSchema(db, zSchema, zName))!=0
){
if( z==0 ){
z = sqlite3_mprintf("%s\n/* %s */", zIn, zFake);
}else{
z = sqlite3_mprintf("%z\n/* %s */", z, zFake);
}
| | | 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 |
&& (zFake = shellFakeSchema(db, zSchema, zName))!=0
){
if( z==0 ){
z = sqlite3_mprintf("%s\n/* %s */", zIn, zFake);
}else{
z = sqlite3_mprintf("%z\n/* %s */", z, zFake);
}
sqlite3_free(zFake);
}
if( z ){
sqlite3_result_text(pCtx, z, -1, sqlite3_free);
return;
}
}
}
|
| ︙ | ︙ | |||
3665 3666 3667 3668 3669 3670 3671 |
iExp = 0;
}else{
iExp -= p->nFrac;
p->nFrac = 0;
}
}
if( iExp>0 ){
| | > | > > > > > | 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 |
iExp = 0;
}else{
iExp -= p->nFrac;
p->nFrac = 0;
}
}
if( iExp>0 ){
p->a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
+ (sqlite3_int64)iExp + 1 );
if( p->a==0 ) goto new_from_text_failed;
memset(p->a+p->nDigit, 0, iExp);
p->nDigit += iExp;
}
}else if( iExp<0 ){
int nExtra;
iExp = -iExp;
nExtra = p->nDigit - p->nFrac - 1;
if( nExtra ){
if( nExtra>=iExp ){
p->nFrac += iExp;
iExp = 0;
}else{
iExp -= nExtra;
p->nFrac = p->nDigit - 1;
}
}
if( iExp>0 ){
p->a = sqlite3_realloc64(p->a, (sqlite3_int64)p->nDigit
+ (sqlite3_int64)iExp + 1 );
if( p->a==0 ) goto new_from_text_failed;
memmove(p->a+iExp, p->a, p->nDigit);
memset(p->a, 0, iExp);
p->nDigit += iExp;
p->nFrac += iExp;
}
}
if( p->sign ){
for(i=0; i<p->nDigit && p->a[i]==0; i++){}
if( i>=p->nDigit ) p->sign = 0;
}
return p;
new_from_text_failed:
if( p ){
if( p->a ) sqlite3_free(p->a);
sqlite3_free(p);
}
|
| ︙ | ︙ | |||
3784 3785 3786 3787 3788 3789 3790 |
sqlite3_result_error_nomem(pCtx);
return;
}
if( p->isNull ){
sqlite3_result_null(pCtx);
return;
}
| | | 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 |
sqlite3_result_error_nomem(pCtx);
return;
}
if( p->isNull ){
sqlite3_result_null(pCtx);
return;
}
z = sqlite3_malloc64( (sqlite3_int64)p->nDigit+4 );
if( z==0 ){
sqlite3_result_error_nomem(pCtx);
return;
}
i = 0;
if( p->nDigit==0 || (p->nDigit==1 && p->a[0]==0) ){
p->sign = 0;
|
| ︙ | ︙ | |||
3849 3850 3851 3852 3853 3854 3855 |
sqlite3_result_null(pCtx);
return;
}
for(nDigit=p->nDigit; nDigit>0 && p->a[nDigit-1]==0; nDigit--){}
for(nZero=0; nZero<nDigit && p->a[nZero]==0; nZero++){}
nFrac = p->nFrac + (nDigit - p->nDigit);
nDigit -= nZero;
| | | 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 |
sqlite3_result_null(pCtx);
return;
}
for(nDigit=p->nDigit; nDigit>0 && p->a[nDigit-1]==0; nDigit--){}
for(nZero=0; nZero<nDigit && p->a[nZero]==0; nZero++){}
nFrac = p->nFrac + (nDigit - p->nDigit);
nDigit -= nZero;
z = sqlite3_malloc64( (sqlite3_int64)nDigit+20 );
if( z==0 ){
sqlite3_result_error_nomem(pCtx);
return;
}
if( nDigit==0 ){
zero = 0;
a = &zero;
|
| ︙ | ︙ | |||
3894 3895 3896 3897 3898 3899 3900 | ** Preconditions for this routine: ** ** pA!=0 ** pA->isNull==0 ** pB!=0 ** pB->isNull==0 */ | | > > > > > > > > | | 3957 3958 3959 3960 3961 3962 3963 3964 3965 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 |
** Preconditions for this routine:
**
** pA!=0
** pA->isNull==0
** pB!=0
** pB->isNull==0
*/
static int decimal_cmp(Decimal *pA, Decimal *pB){
int nASig, nBSig, rc, n;
while( pA->nFrac>0 && pA->a[pA->nDigit-1]==0 ){
pA->nDigit--;
pA->nFrac--;
}
while( pB->nFrac>0 && pB->a[pB->nDigit-1]==0 ){
pB->nDigit--;
pB->nFrac--;
}
if( pA->sign!=pB->sign ){
return pA->sign ? -1 : +1;
}
if( pA->sign ){
Decimal *pTemp = pA;
pA = pB;
pB = pTemp;
}
nASig = pA->nDigit - pA->nFrac;
nBSig = pB->nDigit - pB->nFrac;
if( nASig!=nBSig ){
return nASig - nBSig;
|
| ︙ | ︙ | |||
4062 4063 4064 4065 4066 4067 4068 |
int minFrac;
if( pA==0 || pA->oom || pA->isNull
|| pB==0 || pB->oom || pB->isNull
){
goto mul_end;
}
| | > | 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 |
int minFrac;
if( pA==0 || pA->oom || pA->isNull
|| pB==0 || pB->oom || pB->isNull
){
goto mul_end;
}
acc = sqlite3_malloc64( (sqlite3_int64)pA->nDigit +
(sqlite3_int64)pB->nDigit + 2 );
if( acc==0 ){
pA->oom = 1;
goto mul_end;
}
memset(acc, 0, pA->nDigit + pB->nDigit + 2);
minFrac = pA->nFrac;
if( pB->nFrac<minFrac ) minFrac = pB->nFrac;
|
| ︙ | ︙ | |||
4149 4150 4151 4152 4153 4154 4155 |
if( r<0.0 ){
isNeg = 1;
r = -r;
}else{
isNeg = 0;
}
memcpy(&a,&r,sizeof(a));
| | | 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 |
if( r<0.0 ){
isNeg = 1;
r = -r;
}else{
isNeg = 0;
}
memcpy(&a,&r,sizeof(a));
if( a==0 || a==(sqlite3_int64)0x8000000000000000LL){
e = 0;
m = 0;
}else{
e = a>>52;
m = a & ((((sqlite3_int64)1)<<52)-1);
if( e==0 ){
m <<= 1;
|
| ︙ | ︙ | |||
4424 4425 4426 4427 4428 4429 4430 |
rc = sqlite3_create_collation(db, "decimal", SQLITE_UTF8,
0, decimalCollFunc);
}
return rc;
}
/************************* End ../ext/misc/decimal.c ********************/
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 4496 4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 |
rc = sqlite3_create_collation(db, "decimal", SQLITE_UTF8,
0, decimalCollFunc);
}
return rc;
}
/************************* End ../ext/misc/decimal.c ********************/
#undef sqlite3_base_init
#define sqlite3_base_init sqlite3_base64_init
/************************* Begin ../ext/misc/base64.c ******************/
/*
** 2022-11-18
**
** The author disclaims copyright to this source code. In place of
|
| ︙ | ︙ | |||
5142 5143 5144 5145 5146 5147 5148 |
pOut += nbo;
}
return pOut;
}
/* This function does the work for the SQLite base64(x) UDF. */
static void base64(sqlite3_context *context, int na, sqlite3_value *av[]){
| > | > | | 4708 4709 4710 4711 4712 4713 4714 4715 4716 4717 4718 4719 4720 4721 4722 4723 4724 4725 4726 4727 4728 4729 4730 4731 4732 4733 |
pOut += nbo;
}
return pOut;
}
/* This function does the work for the SQLite base64(x) UDF. */
static void base64(sqlite3_context *context, int na, sqlite3_value *av[]){
sqlite3_int64 nb;
sqlite3_int64 nv = sqlite3_value_bytes(av[0]);
sqlite3_int64 nc;
int nvMax = sqlite3_limit(sqlite3_context_db_handle(context),
SQLITE_LIMIT_LENGTH, -1);
char *cBuf;
u8 *bBuf;
assert(na==1);
switch( sqlite3_value_type(av[0]) ){
case SQLITE_BLOB:
nb = nv;
nc = 4*((nv+2)/3); /* quads needed */
nc += (nc+(B64_DARK_MAX-1))/B64_DARK_MAX + 1; /* LFs and a 0-terminator */
if( nvMax < nc ){
sqlite3_result_error(context, "blob expanded to base64 too big", -1);
return;
}
bBuf = (u8*)sqlite3_value_blob(av[0]);
if( !bBuf ){
|
| ︙ | ︙ | |||
5522 5523 5524 5525 5526 5527 5528 |
return;
}
}
# endif
/* This function does the work for the SQLite base85(x) UDF. */
static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
| | | 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 |
return;
}
}
# endif
/* This function does the work for the SQLite base85(x) UDF. */
static void base85(sqlite3_context *context, int na, sqlite3_value *av[]){
sqlite3_int64 nb, nc, nv = sqlite3_value_bytes(av[0]);
int nvMax = sqlite3_limit(sqlite3_context_db_handle(context),
SQLITE_LIMIT_LENGTH, -1);
char *cBuf;
u8 *bBuf;
assert(na==1);
switch( sqlite3_value_type(av[0]) ){
case SQLITE_BLOB:
|
| ︙ | ︙ | |||
5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 5839 5840 |
}else{
isNeg = 0;
}
memcpy(&a,&r,sizeof(a));
if( a==0 ){
e = 0;
m = 0;
}else{
e = a>>52;
m = a & ((((sqlite3_int64)1)<<52)-1);
if( e==0 ){
m <<= 1;
}else{
m |= ((sqlite3_int64)1)<<52;
| > > > | 5395 5396 5397 5398 5399 5400 5401 5402 5403 5404 5405 5406 5407 5408 5409 5410 5411 |
}else{
isNeg = 0;
}
memcpy(&a,&r,sizeof(a));
if( a==0 ){
e = 0;
m = 0;
}else if( a==(sqlite3_int64)0x8000000000000000LL ){
e = -1996;
m = -1;
}else{
e = a>>52;
m = a & ((((sqlite3_int64)1)<<52)-1);
if( e==0 ){
m <<= 1;
}else{
m |= ((sqlite3_int64)1)<<52;
|
| ︙ | ︙ | |||
6050 6051 6052 6053 6054 6055 6056 | ** The step parameter defaults to 1 and 0 is treated as 1. ** ** Examples: ** ** SELECT * FROM generate_series(0,100,5); ** ** The query above returns integers from 0 through 100 counting by steps | > | | | | | 5621 5622 5623 5624 5625 5626 5627 5628 5629 5630 5631 5632 5633 5634 5635 5636 5637 5638 5639 5640 5641 5642 5643 5644 5645 5646 5647 5648 | ** The step parameter defaults to 1 and 0 is treated as 1. ** ** Examples: ** ** SELECT * FROM generate_series(0,100,5); ** ** The query above returns integers from 0 through 100 counting by steps ** of 5. In other words, 0, 5, 10, 15, ..., 90, 95, 100. There are a total ** of 21 rows. ** ** SELECT * FROM generate_series(0,100); ** ** Integers from 0 through 100 with a step size of 1. 101 rows. ** ** SELECT * FROM generate_series(20) LIMIT 10; ** ** Integers 20 through 29. 10 rows. ** ** SELECT * FROM generate_series(0,-100,-5); ** ** Integers 0 -5 -10 ... -100. 21 rows. ** ** SELECT * FROM generate_series(0,-1); ** ** Empty sequence. ** ** HOW IT WORKS ** |
| ︙ | ︙ | |||
6138 6139 6140 6141 6142 6143 6144 | SQLITE_EXTENSION_INIT1 #include <assert.h> #include <string.h> #include <limits.h> #include <math.h> #ifndef SQLITE_OMIT_VIRTUALTABLE | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > | 5710 5711 5712 5713 5714 5715 5716 5717 5718 5719 5720 5721 5722 5723 5724 5725 5726 5727 5728 5729 5730 5731 5732 5733 5734 5735 5736 5737 5738 5739 5740 5741 5742 5743 5744 5745 5746 5747 5748 5749 5750 5751 5752 5753 5754 5755 5756 5757 5758 5759 5760 5761 5762 5763 5764 5765 5766 5767 5768 5769 5770 5771 5772 5773 5774 5775 5776 5777 5778 5779 5780 5781 5782 5783 5784 5785 5786 5787 5788 5789 5790 5791 5792 5793 5794 5795 5796 5797 5798 5799 5800 5801 5802 5803 5804 5805 |
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
#include <limits.h>
#include <math.h>
#ifndef SQLITE_OMIT_VIRTUALTABLE
/* series_cursor is a subclass of sqlite3_vtab_cursor which will
** serve as the underlying representation of a cursor that scans
** over rows of the result.
**
** iOBase, iOTerm, and iOStep are the original values of the
** start=, stop=, and step= constraints on the query. These are
** the values reported by the start, stop, and step columns of the
** virtual table.
**
** iBase, iTerm, iStep, and bDescp are the actual values used to generate
** the sequence. These might be different from the iOxxxx values.
** For example in
**
** SELECT value FROM generate_series(1,11,2)
** WHERE value BETWEEN 4 AND 8;
**
** The iOBase is 1, but the iBase is 5. iOTerm is 11 but iTerm is 7.
** Another example:
**
** SELECT value FROM generate_series(1,15,3) ORDER BY value DESC;
**
** The cursor initialization for the above query is:
**
** iOBase = 1 iBase = 13
** iOTerm = 15 iTerm = 1
** iOStep = 3 iStep = 3 bDesc = 1
**
** The actual step size is unsigned so that can have a value of
** +9223372036854775808 which is needed for querys like this:
**
** SELECT value
** FROM generate_series(9223372036854775807,
** -9223372036854775808,
** -9223372036854775808)
** ORDER BY value ASC;
**
** The setup for the previous query will be:
**
** iOBase = 9223372036854775807 iBase = -1
** iOTerm = -9223372036854775808 iTerm = 9223372036854775807
** iOStep = -9223372036854775808 iStep = 9223372036854775808 bDesc = 0
*/
/* typedef unsigned char u8; */
typedef struct series_cursor series_cursor;
struct series_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
sqlite3_int64 iOBase; /* Original starting value ("start") */
sqlite3_int64 iOTerm; /* Original terminal value ("stop") */
sqlite3_int64 iOStep; /* Original step value */
sqlite3_int64 iBase; /* Starting value to actually use */
sqlite3_int64 iTerm; /* Terminal value to actually use */
sqlite3_uint64 iStep; /* The step size */
sqlite3_int64 iValue; /* Current value */
u8 bDesc; /* iStep is really negative */
u8 bDone; /* True if stepped past last element */
};
/*
** Computed the difference between two 64-bit signed integers using a
** convoluted computation designed to work around the silly restriction
** against signed integer overflow in C.
*/
static sqlite3_uint64 span64(sqlite3_int64 a, sqlite3_int64 b){
assert( a>=b );
return (*(sqlite3_uint64*)&a) - (*(sqlite3_uint64*)&b);
}
/*
** Add or substract an unsigned 64-bit integer from a signed 64-bit integer
** and return the new signed 64-bit integer.
*/
static sqlite3_int64 add64(sqlite3_int64 a, sqlite3_uint64 b){
sqlite3_uint64 x = *(sqlite3_uint64*)&a;
x += b;
return *(sqlite3_int64*)&x;
}
static sqlite3_int64 sub64(sqlite3_int64 a, sqlite3_uint64 b){
sqlite3_uint64 x = *(sqlite3_uint64*)&a;
x -= b;
return *(sqlite3_int64*)&x;
}
/*
** The seriesConnect() method is invoked to create a new
** series_vtab that describes the generate_series virtual table.
**
** Think of this routine as the constructor for series_vtab objects.
**
|
| ︙ | ︙ | |||
6352 6353 6354 6355 6356 6357 6358 |
/*
** Advance a series_cursor to its next row of output.
*/
static int seriesNext(sqlite3_vtab_cursor *cur){
series_cursor *pCur = (series_cursor*)cur;
| > | > > > > > > > | | | | | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 5873 5874 5875 5876 5877 5878 5879 5880 5881 5882 5883 5884 5885 5886 5887 5888 5889 5890 5891 5892 5893 5894 5895 5896 5897 5898 5899 5900 5901 5902 5903 5904 5905 5906 5907 5908 5909 5910 5911 5912 5913 5914 5915 5916 5917 5918 5919 5920 5921 5922 5923 5924 5925 5926 5927 5928 5929 5930 5931 5932 5933 5934 5935 5936 5937 5938 5939 5940 5941 5942 5943 5944 5945 5946 5947 5948 5949 5950 5951 5952 5953 5954 5955 5956 5957 5958 5959 5960 5961 5962 5963 5964 5965 5966 5967 5968 5969 5970 5971 5972 5973 5974 5975 5976 5977 5978 5979 5980 5981 5982 5983 5984 5985 5986 5987 5988 5989 5990 5991 5992 5993 5994 5995 5996 5997 5998 5999 6000 6001 6002 6003 6004 |
/*
** Advance a series_cursor to its next row of output.
*/
static int seriesNext(sqlite3_vtab_cursor *cur){
series_cursor *pCur = (series_cursor*)cur;
if( pCur->iValue==pCur->iTerm ){
pCur->bDone = 1;
}else if( pCur->bDesc ){
pCur->iValue = sub64(pCur->iValue, pCur->iStep);
assert( pCur->iValue>=pCur->iTerm );
}else{
pCur->iValue = add64(pCur->iValue, pCur->iStep);
assert( pCur->iValue<=pCur->iTerm );
}
return SQLITE_OK;
}
/*
** Return values of columns for the row at which the series_cursor
** is currently pointing.
*/
static int seriesColumn(
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
int i /* Which column to return */
){
series_cursor *pCur = (series_cursor*)cur;
sqlite3_int64 x = 0;
switch( i ){
case SERIES_COLUMN_START: x = pCur->iOBase; break;
case SERIES_COLUMN_STOP: x = pCur->iOTerm; break;
case SERIES_COLUMN_STEP: x = pCur->iOStep; break;
default: x = pCur->iValue; break;
}
sqlite3_result_int64(ctx, x);
return SQLITE_OK;
}
#ifndef LARGEST_UINT64
#define LARGEST_INT64 ((sqlite3_int64)0x7fffffffffffffffLL)
#define LARGEST_UINT64 ((sqlite3_uint64)0xffffffffffffffffULL)
#define SMALLEST_INT64 ((sqlite3_int64)0x8000000000000000LL)
#endif
/*
** The rowid is the same as the value.
*/
static int seriesRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
series_cursor *pCur = (series_cursor*)cur;
*pRowid = pCur->iValue;
return SQLITE_OK;
}
/*
** Return TRUE if the cursor has been moved off of the last
** row of output.
*/
static int seriesEof(sqlite3_vtab_cursor *cur){
series_cursor *pCur = (series_cursor*)cur;
return pCur->bDone;
}
/* True to cause run-time checking of the start=, stop=, and/or step=
** parameters. The only reason to do this is for testing the
** constraint checking logic for virtual tables in the SQLite core.
*/
#ifndef SQLITE_SERIES_CONSTRAINT_VERIFY
# define SQLITE_SERIES_CONSTRAINT_VERIFY 0
#endif
/*
** Return the number of steps between pCur->iBase and pCur->iTerm if
** the step width is pCur->iStep.
*/
static sqlite3_uint64 seriesSteps(series_cursor *pCur){
if( pCur->bDesc ){
assert( pCur->iBase >= pCur->iTerm );
return span64(pCur->iBase, pCur->iTerm)/pCur->iStep;
}else{
assert( pCur->iBase <= pCur->iTerm );
return span64(pCur->iTerm, pCur->iBase)/pCur->iStep;
}
}
#if defined(SQLITE_ENABLE_MATH_FUNCTIONS) || defined(_WIN32)
/*
** Case 1 (the most common case):
** The standard math library is available so use ceil() and floor() from there.
*/
static double seriesCeil(double r){ return ceil(r); }
static double seriesFloor(double r){ return floor(r); }
#elif defined(__GNUC__) && !defined(SQLITE_DISABLE_INTRINSIC)
/*
** Case 2 (2nd most common): Use GCC/Clang builtins
*/
static double seriesCeil(double r){ return __builtin_ceil(r); }
static double seriesFloor(double r){ return __builtin_floor(r); }
#else
/*
** Case 3 (rarely happens): Use home-grown ceil() and floor() routines.
*/
static double seriesCeil(double r){
sqlite3_int64 x;
if( r!=r ) return r;
if( r<=(-4503599627370496.0) ) return r;
if( r>=(+4503599627370496.0) ) return r;
x = (sqlite3_int64)r;
if( r==(double)x ) return r;
if( r>(double)x ) x++;
return (double)x;
}
static double seriesFloor(double r){
sqlite3_int64 x;
if( r!=r ) return r;
if( r<=(-4503599627370496.0) ) return r;
if( r>=(+4503599627370496.0) ) return r;
x = (sqlite3_int64)r;
if( r==(double)x ) return r;
if( r<(double)x ) x--;
return (double)x;
}
#endif
/*
** This method is called to "rewind" the series_cursor object back
** to the first row of output. This method is always called at least
** once prior to any call to seriesColumn() or seriesRowid() or
** seriesEof().
**
** The query plan selected by seriesBestIndex is passed in the idxNum
|
| ︙ | ︙ | |||
6442 6443 6444 6445 6446 6447 6448 |
*/
static int seriesFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStrUnused,
int argc, sqlite3_value **argv
){
series_cursor *pCur = (series_cursor *)pVtabCursor;
| | | | | | | > > > > > > > > > > > > > | | | | | | < < < < | | | | | > > > > > > > > > > > > > > > | | < | | | > | | | | > > > | | | | | > > | | | | | | | | | > > | | | | | | | > > | | > > | | > | | | > | | | > > | | > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < | < | | | > > | | | | < < < | < | < | | | | < < | < < < | | | 6024 6025 6026 6027 6028 6029 6030 6031 6032 6033 6034 6035 6036 6037 6038 6039 6040 6041 6042 6043 6044 6045 6046 6047 6048 6049 6050 6051 6052 6053 6054 6055 6056 6057 6058 6059 6060 6061 6062 6063 6064 6065 6066 6067 6068 6069 6070 6071 6072 6073 6074 6075 6076 6077 6078 6079 6080 6081 6082 6083 6084 6085 6086 6087 6088 6089 6090 6091 6092 6093 6094 6095 6096 6097 6098 6099 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 6118 6119 6120 6121 6122 6123 6124 6125 6126 6127 6128 6129 6130 6131 6132 6133 6134 6135 6136 6137 6138 6139 6140 6141 6142 6143 6144 6145 6146 6147 6148 6149 6150 6151 6152 6153 6154 6155 6156 6157 6158 6159 6160 6161 6162 6163 6164 6165 6166 6167 6168 6169 6170 6171 6172 6173 6174 6175 6176 6177 6178 6179 6180 6181 6182 6183 6184 6185 6186 6187 6188 6189 6190 6191 6192 6193 6194 6195 6196 6197 6198 6199 6200 6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211 6212 6213 6214 6215 6216 6217 6218 6219 6220 6221 6222 6223 6224 6225 6226 6227 6228 6229 6230 6231 6232 6233 6234 6235 6236 6237 6238 6239 6240 6241 6242 6243 6244 6245 6246 6247 6248 6249 6250 6251 6252 6253 6254 6255 6256 6257 6258 6259 6260 6261 6262 6263 6264 6265 6266 6267 6268 6269 6270 |
*/
static int seriesFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStrUnused,
int argc, sqlite3_value **argv
){
series_cursor *pCur = (series_cursor *)pVtabCursor;
int iArg = 0; /* Arguments used so far */
int i; /* Loop counter */
sqlite3_int64 iMin = SMALLEST_INT64; /* Smallest allowed output value */
sqlite3_int64 iMax = LARGEST_INT64; /* Largest allowed output value */
sqlite3_int64 iLimit = 0; /* if >0, the value of the LIMIT */
sqlite3_int64 iOffset = 0; /* if >0, the value of the OFFSET */
(void)idxStrUnused;
/* If any constraints have a NULL value, then return no rows.
** See ticket https://sqlite.org/src/info/fac496b61722daf2
*/
for(i=0; i<argc; i++){
if( sqlite3_value_type(argv[i])==SQLITE_NULL ){
goto series_no_rows;
}
}
/* Capture the three HIDDEN parameters to the virtual table and insert
** default values for any parameters that are omitted.
*/
if( idxNum & 0x01 ){
pCur->iOBase = sqlite3_value_int64(argv[iArg++]);
}else{
pCur->iOBase = 0;
}
if( idxNum & 0x02 ){
pCur->iOTerm = sqlite3_value_int64(argv[iArg++]);
}else{
pCur->iOTerm = 0xffffffff;
}
if( idxNum & 0x04 ){
pCur->iOStep = sqlite3_value_int64(argv[iArg++]);
if( pCur->iOStep==0 ) pCur->iOStep = 1;
}else{
pCur->iOStep = 1;
}
/* If there are constraints on the value column but there are
** no constraints on the start, stop, and step columns, then
** initialize the default range to be the entire range of 64-bit signed
** integers. This range will contracted by the value column constraints
** further below.
*/
if( (idxNum & 0x05)==0 && (idxNum & 0x0380)!=0 ){
pCur->iOBase = SMALLEST_INT64;
}
if( (idxNum & 0x06)==0 && (idxNum & 0x3080)!=0 ){
pCur->iOTerm = LARGEST_INT64;
}
pCur->iBase = pCur->iOBase;
pCur->iTerm = pCur->iOTerm;
if( pCur->iOStep>0 ){
pCur->iStep = pCur->iOStep;
}else if( pCur->iOStep>SMALLEST_INT64 ){
pCur->iStep = -pCur->iOStep;
}else{
pCur->iStep = LARGEST_INT64;
pCur->iStep++;
}
pCur->bDesc = pCur->iOStep<0;
if( pCur->bDesc==0 && pCur->iBase>pCur->iTerm ){
goto series_no_rows;
}
if( pCur->bDesc!=0 && pCur->iBase<pCur->iTerm ){
goto series_no_rows;
}
/* Extract the LIMIT and OFFSET values, but do not apply them yet.
** The range must first be constrained by the limits on value.
*/
if( idxNum & 0x20 ){
iLimit = sqlite3_value_int64(argv[iArg++]);
if( idxNum & 0x40 ){
iOffset = sqlite3_value_int64(argv[iArg++]);
}
}
/* Narrow the range of iMin and iMax (the minimum and maximum outputs)
** based on equality and inequality constraints on the "value" column.
*/
if( idxNum & 0x3380 ){
if( idxNum & 0x0080 ){ /* value=X */
if( sqlite3_value_numeric_type(argv[iArg])==SQLITE_FLOAT ){
double r = sqlite3_value_double(argv[iArg++]);
if( r==seriesCeil(r)
&& r>=(double)SMALLEST_INT64
&& r<=(double)LARGEST_INT64
){
iMin = iMax = (sqlite3_int64)r;
}else{
goto series_no_rows;
}
}else{
iMin = iMax = sqlite3_value_int64(argv[iArg++]);
}
}else{
if( idxNum & 0x0300 ){ /* value>X or value>=X */
if( sqlite3_value_numeric_type(argv[iArg])==SQLITE_FLOAT ){
double r = sqlite3_value_double(argv[iArg++]);
if( r<(double)SMALLEST_INT64 ){
iMin = SMALLEST_INT64;
}else if( (idxNum & 0x0200)!=0 && r==seriesCeil(r) ){
iMin = (sqlite3_int64)seriesCeil(r+1.0);
}else{
iMin = (sqlite3_int64)seriesCeil(r);
}
}else{
iMin = sqlite3_value_int64(argv[iArg++]);
if( (idxNum & 0x0200)!=0 ){
if( iMin==LARGEST_INT64 ){
goto series_no_rows;
}else{
iMin++;
}
}
}
}
if( idxNum & 0x3000 ){ /* value<X or value<=X */
if( sqlite3_value_numeric_type(argv[iArg])==SQLITE_FLOAT ){
double r = sqlite3_value_double(argv[iArg++]);
if( r>(double)LARGEST_INT64 ){
iMax = LARGEST_INT64;
}else if( (idxNum & 0x2000)!=0 && r==seriesFloor(r) ){
iMax = (sqlite3_int64)(r-1.0);
}else{
iMax = (sqlite3_int64)seriesFloor(r);
}
}else{
iMax = sqlite3_value_int64(argv[iArg++]);
if( idxNum & 0x2000 ){
if( iMax==SMALLEST_INT64 ){
goto series_no_rows;
}else{
iMax--;
}
}
}
}
if( iMin>iMax ){
goto series_no_rows;
}
}
/* Try to reduce the range of values to be generated based on
** constraints on the "value" column.
*/
if( pCur->bDesc==0 ){
if( pCur->iBase<iMin ){
sqlite3_uint64 span = span64(iMin,pCur->iBase);
pCur->iBase = add64(pCur->iBase, (span/pCur->iStep)*pCur->iStep);
if( pCur->iBase<iMin ){
if( pCur->iBase > sub64(LARGEST_INT64, pCur->iStep) ){
goto series_no_rows;
}
pCur->iBase = add64(pCur->iBase, pCur->iStep);
}
}
if( pCur->iTerm>iMax ){
pCur->iTerm = iMax;
}
}else{
if( pCur->iBase>iMax ){
sqlite3_uint64 span = span64(pCur->iBase,iMax);
pCur->iBase = sub64(pCur->iBase, (span/pCur->iStep)*pCur->iStep);
if( pCur->iBase>iMax ){
if( pCur->iBase < add64(SMALLEST_INT64, pCur->iStep) ){
goto series_no_rows;
}
pCur->iBase = sub64(pCur->iBase, pCur->iStep);
}
}
if( pCur->iTerm<iMin ){
pCur->iTerm = iMin;
}
}
}
/* Adjust iTerm so that it is exactly the last value of the series.
*/
if( pCur->bDesc==0 ){
if( pCur->iBase>pCur->iTerm ){
goto series_no_rows;
}
pCur->iTerm = sub64(pCur->iTerm,
span64(pCur->iTerm,pCur->iBase) % pCur->iStep);
}else{
if( pCur->iBase<pCur->iTerm ){
goto series_no_rows;
}
pCur->iTerm = add64(pCur->iTerm,
span64(pCur->iBase,pCur->iTerm) % pCur->iStep);
}
/* Transform the series generator to output values in the requested
** order.
*/
if( ((idxNum & 0x0008)!=0 && pCur->bDesc==0)
|| ((idxNum & 0x0010)!=0 && pCur->bDesc!=0)
){
sqlite3_int64 tmp = pCur->iBase;
pCur->iBase = pCur->iTerm;
pCur->iTerm = tmp;
pCur->bDesc = !pCur->bDesc;
}
/* Apply LIMIT and OFFSET constraints, if any */
assert( pCur->iStep!=0 );
if( idxNum & 0x20 ){
if( iOffset>0 ){
if( seriesSteps(pCur) < (sqlite3_uint64)iOffset ){
goto series_no_rows;
}else if( pCur->bDesc ){
pCur->iBase = sub64(pCur->iBase, pCur->iStep*iOffset);
}else{
pCur->iBase = add64(pCur->iBase, pCur->iStep*iOffset);
}
}
if( iLimit>=0 && seriesSteps(pCur) > (sqlite3_uint64)iLimit ){
pCur->iTerm = add64(pCur->iBase, (iLimit - 1)*pCur->iStep);
}
}
pCur->iValue = pCur->iBase;
pCur->bDone = 0;
return SQLITE_OK;
series_no_rows:
pCur->iBase = 0;
pCur->iTerm = 0;
pCur->iStep = 1;
pCur->bDesc = 0;
pCur->bDone = 1;
return SQLITE_OK;
}
/*
** SQLite will invoke this method one or more times while planning a query
** that uses the generate_series virtual table. This routine needs to create
** a query plan for each invocation and compute an estimated cost for that
** plan.
|
| ︙ | ︙ | |||
6946 6947 6948 6949 6950 6951 6952 6953 6954 6955 6956 6957 6958 6959 |
** A nondeterministic finite automaton (NFA) is used for matching, so the
** performance is bounded by O(N*M) where N is the size of the regular
** expression and M is the size of the input string. The matcher never
** exhibits exponential behavior. Note that the X{p,q} operator expands
** to p copies of X following by q-p copies of X? and that the size of the
** regular expression in the O(N*M) performance bound is computed after
** this expansion.
*/
#include <string.h>
#include <stdlib.h>
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
/*
| > > | 6586 6587 6588 6589 6590 6591 6592 6593 6594 6595 6596 6597 6598 6599 6600 6601 |
** A nondeterministic finite automaton (NFA) is used for matching, so the
** performance is bounded by O(N*M) where N is the size of the regular
** expression and M is the size of the input string. The matcher never
** exhibits exponential behavior. Note that the X{p,q} operator expands
** to p copies of X following by q-p copies of X? and that the size of the
** regular expression in the O(N*M) performance bound is computed after
** this expansion.
**
** To help prevent DoS attacks, the maximum size of the NFA is restricted.
*/
#include <string.h>
#include <stdlib.h>
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
/*
|
| ︙ | ︙ | |||
6986 6987 6988 6989 6990 6991 6992 | #define RE_OP_NOTWORD 12 /* Not a perl word character */ #define RE_OP_DIGIT 13 /* digit: [0-9] */ #define RE_OP_NOTDIGIT 14 /* Not a digit */ #define RE_OP_SPACE 15 /* space: [ \t\n\r\v\f] */ #define RE_OP_NOTSPACE 16 /* Not a digit */ #define RE_OP_BOUNDARY 17 /* Boundary between word and non-word */ #define RE_OP_ATSTART 18 /* Currently at the start of the string */ | < < < < < < < < < < < < < < < < < < < < < < < < < < | 6628 6629 6630 6631 6632 6633 6634 6635 6636 6637 6638 6639 6640 6641 | #define RE_OP_NOTWORD 12 /* Not a perl word character */ #define RE_OP_DIGIT 13 /* digit: [0-9] */ #define RE_OP_NOTDIGIT 14 /* Not a digit */ #define RE_OP_SPACE 15 /* space: [ \t\n\r\v\f] */ #define RE_OP_NOTSPACE 16 /* Not a digit */ #define RE_OP_BOUNDARY 17 /* Boundary between word and non-word */ #define RE_OP_ATSTART 18 /* Currently at the start of the string */ /* Each opcode is a "state" in the NFA */ typedef unsigned short ReStateNumber; /* Because this is an NFA and not a DFA, multiple states can be active at ** once. An instance of the following object records all active states in ** the NFA. The implementation is optimized for the common case where the |
| ︙ | ︙ | |||
7049 7050 7051 7052 7053 7054 7055 7056 7057 7058 7059 7060 7061 7062 |
char *aOp; /* Operators for the virtual machine */
int *aArg; /* Arguments to each operator */
unsigned (*xNextChar)(ReInput*); /* Next character function */
unsigned char zInit[12]; /* Initial text to match */
int nInit; /* Number of bytes in zInit */
unsigned nState; /* Number of entries in aOp[] and aArg[] */
unsigned nAlloc; /* Slots allocated for aOp[] and aArg[] */
};
/* Add a state to the given state set if it is not already there */
static void re_add_state(ReStateSet *pSet, int newState){
unsigned i;
for(i=0; i<pSet->nState; i++) if( pSet->aState[i]==newState ) return;
pSet->aState[pSet->nState++] = (ReStateNumber)newState;
| > | 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679 |
char *aOp; /* Operators for the virtual machine */
int *aArg; /* Arguments to each operator */
unsigned (*xNextChar)(ReInput*); /* Next character function */
unsigned char zInit[12]; /* Initial text to match */
int nInit; /* Number of bytes in zInit */
unsigned nState; /* Number of entries in aOp[] and aArg[] */
unsigned nAlloc; /* Slots allocated for aOp[] and aArg[] */
unsigned mxAlloc; /* Complexity limit */
};
/* Add a state to the given state set if it is not already there */
static void re_add_state(ReStateSet *pSet, int newState){
unsigned i;
for(i=0; i<pSet->nState; i++) if( pSet->aState[i]==newState ) return;
pSet->aState[pSet->nState++] = (ReStateNumber)newState;
|
| ︙ | ︙ | |||
7263 7264 7265 7266 7267 7268 7269 | re_match_end: sqlite3_free(pToFree); return rc; } /* Resize the opcode and argument arrays for an RE under construction. */ | | > | | | 6880 6881 6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 6894 6895 6896 6897 6898 6899 6900 6901 6902 |
re_match_end:
sqlite3_free(pToFree);
return rc;
}
/* Resize the opcode and argument arrays for an RE under construction.
*/
static int re_resize(ReCompiled *p, unsigned int N){
char *aOp;
int *aArg;
if( N>p->mxAlloc ){ p->zErr = "REGEXP pattern too big"; return 1; }
aOp = sqlite3_realloc64(p->aOp, N*sizeof(p->aOp[0]));
if( aOp==0 ){ p->zErr = "out of memory"; return 1; }
p->aOp = aOp;
aArg = sqlite3_realloc64(p->aArg, N*sizeof(p->aArg[0]));
if( aArg==0 ){ p->zErr = "out of memory"; return 1; }
p->aArg = aArg;
p->nAlloc = N;
return 0;
}
/* Insert a new opcode and argument into an RE under construction. The
** insertion point is just prior to existing opcode iBefore.
|
| ︙ | ︙ | |||
7301 7302 7303 7304 7305 7306 7307 |
static int re_append(ReCompiled *p, int op, int arg){
return re_insert(p, p->nState, op, arg);
}
/* Make a copy of N opcodes starting at iStart onto the end of the RE
** under construction.
*/
| | | 6919 6920 6921 6922 6923 6924 6925 6926 6927 6928 6929 6930 6931 6932 6933 |
static int re_append(ReCompiled *p, int op, int arg){
return re_insert(p, p->nState, op, arg);
}
/* Make a copy of N opcodes starting at iStart onto the end of the RE
** under construction.
*/
static void re_copy(ReCompiled *p, int iStart, unsigned int N){
if( p->nState+N>=p->nAlloc && re_resize(p, p->nAlloc*2+N) ) return;
memcpy(&p->aOp[p->nState], &p->aOp[iStart], N*sizeof(p->aOp[0]));
memcpy(&p->aArg[p->nState], &p->aArg[iStart], N*sizeof(p->aArg[0]));
p->nState += N;
}
/* Return true if c is a hexadecimal digit character: [0-9a-fA-F]
|
| ︙ | ︙ | |||
7454 7455 7456 7457 7458 7459 7460 |
break;
}
case '^': {
re_append(p, RE_OP_ATSTART, 0);
break;
}
case '{': {
| | | | > > > > | > > > | > | | | 7072 7073 7074 7075 7076 7077 7078 7079 7080 7081 7082 7083 7084 7085 7086 7087 7088 7089 7090 7091 7092 7093 7094 7095 7096 7097 7098 7099 7100 7101 7102 7103 7104 7105 7106 7107 7108 7109 7110 7111 7112 7113 7114 7115 7116 7117 7118 7119 7120 7121 |
break;
}
case '^': {
re_append(p, RE_OP_ATSTART, 0);
break;
}
case '{': {
unsigned int m = 0, n = 0;
unsigned int sz, j;
if( iPrev<0 ) return "'{m,n}' without operand";
while( (c=rePeek(p))>='0' && c<='9' ){
m = m*10 + c - '0';
if( m*2>p->mxAlloc ) return "REGEXP pattern too big";
p->sIn.i++;
}
n = m;
if( c==',' ){
p->sIn.i++;
n = 0;
while( (c=rePeek(p))>='0' && c<='9' ){
n = n*10 + c-'0';
if( n*2>p->mxAlloc ) return "REGEXP pattern too big";
p->sIn.i++;
}
}
if( c!='}' ) return "unmatched '{'";
if( n<m ) return "n less than m in '{m,n}'";
p->sIn.i++;
sz = p->nState - iPrev;
if( m==0 ){
if( n==0 ) return "both m and n are zero in '{m,n}'";
re_insert(p, iPrev, RE_OP_FORK, sz+1);
iPrev++;
n--;
}else{
for(j=1; j<m; j++) re_copy(p, iPrev, sz);
}
for(j=m; j<n; j++){
re_append(p, RE_OP_FORK, sz+1);
re_copy(p, iPrev, sz);
}
if( n==0 && m>0 ){
re_append(p, RE_OP_FORK, -(int)sz);
}
break;
}
case '[': {
unsigned int iFirst = p->nState;
if( rePeek(p)=='^' ){
re_append(p, RE_OP_CC_EXC, 0);
|
| ︙ | ︙ | |||
7547 7548 7549 7550 7551 7552 7553 | return 0; } /* Free and reclaim all the memory used by a previously compiled ** regular expression. Applications should invoke this routine once ** for every call to re_compile() to avoid memory leaks. */ | | < | > > > > > > > | | 7173 7174 7175 7176 7177 7178 7179 7180 7181 7182 7183 7184 7185 7186 7187 7188 7189 7190 7191 7192 7193 7194 7195 7196 7197 7198 7199 7200 7201 7202 7203 7204 7205 7206 7207 7208 7209 7210 7211 7212 7213 7214 7215 7216 7217 7218 7219 7220 7221 7222 |
return 0;
}
/* Free and reclaim all the memory used by a previously compiled
** regular expression. Applications should invoke this routine once
** for every call to re_compile() to avoid memory leaks.
*/
static void re_free(ReCompiled *pRe){
if( pRe ){
sqlite3_free(pRe->aOp);
sqlite3_free(pRe->aArg);
sqlite3_free(pRe);
}
}
/*
** Compile a textual regular expression in zIn[] into a compiled regular
** expression suitable for us by re_match() and return a pointer to the
** compiled regular expression in *ppRe. Return NULL on success or an
** error message if something goes wrong.
*/
static const char *re_compile(
ReCompiled **ppRe, /* OUT: write compiled NFA here */
const char *zIn, /* Input regular expression */
int mxRe, /* Complexity limit */
int noCase /* True for caseless comparisons */
){
ReCompiled *pRe;
const char *zErr;
int i, j;
*ppRe = 0;
pRe = sqlite3_malloc( sizeof(*pRe) );
if( pRe==0 ){
return "out of memory";
}
memset(pRe, 0, sizeof(*pRe));
pRe->xNextChar = noCase ? re_next_char_nocase : re_next_char;
pRe->mxAlloc = mxRe;
if( re_resize(pRe, 30) ){
zErr = pRe->zErr;
re_free(pRe);
return zErr;
}
if( zIn[0]=='^' ){
zIn++;
}else{
re_append(pRe, RE_OP_ANYSTAR, 0);
}
pRe->sIn.z = (unsigned char*)zIn;
|
| ︙ | ︙ | |||
7628 7629 7630 7631 7632 7633 7634 7635 7636 7637 7638 7639 7640 7641 |
}
}
if( j>0 && pRe->zInit[j-1]==0 ) j--;
pRe->nInit = j;
}
return pRe->zErr;
}
/*
** Implementation of the regexp() SQL function. This function implements
** the build-in REGEXP operator. The first argument to the function is the
** pattern and the second argument is the string. So, the SQL statements:
**
** A REGEXP B
| > > > > > > > > | 7260 7261 7262 7263 7264 7265 7266 7267 7268 7269 7270 7271 7272 7273 7274 7275 7276 7277 7278 7279 7280 7281 |
}
}
if( j>0 && pRe->zInit[j-1]==0 ) j--;
pRe->nInit = j;
}
return pRe->zErr;
}
/*
** Compute a reasonable limit on the length of the REGEXP NFA.
*/
static int re_maxlen(sqlite3_context *context){
sqlite3 *db = sqlite3_context_db_handle(context);
return 75 + sqlite3_limit(db, SQLITE_LIMIT_LIKE_PATTERN_LENGTH,-1)/2;
}
/*
** Implementation of the regexp() SQL function. This function implements
** the build-in REGEXP operator. The first argument to the function is the
** pattern and the second argument is the string. So, the SQL statements:
**
** A REGEXP B
|
| ︙ | ︙ | |||
7654 7655 7656 7657 7658 7659 7660 |
int setAux = 0; /* True to invoke sqlite3_set_auxdata() */
(void)argc; /* Unused */
pRe = sqlite3_get_auxdata(context, 0);
if( pRe==0 ){
zPattern = (const char*)sqlite3_value_text(argv[0]);
if( zPattern==0 ) return;
| | > | 7294 7295 7296 7297 7298 7299 7300 7301 7302 7303 7304 7305 7306 7307 7308 7309 |
int setAux = 0; /* True to invoke sqlite3_set_auxdata() */
(void)argc; /* Unused */
pRe = sqlite3_get_auxdata(context, 0);
if( pRe==0 ){
zPattern = (const char*)sqlite3_value_text(argv[0]);
if( zPattern==0 ) return;
zErr = re_compile(&pRe, zPattern, re_maxlen(context),
sqlite3_user_data(context)!=0);
if( zErr ){
re_free(pRe);
sqlite3_result_error(context, zErr, -1);
return;
}
if( pRe==0 ){
sqlite3_result_error_nomem(context);
|
| ︙ | ︙ | |||
7696 7697 7698 7699 7700 7701 7702 7703 7704 7705 | const char *zErr; ReCompiled *pRe; sqlite3_str *pStr; int i; int n; char *z; (void)argc; zPattern = (const char*)sqlite3_value_text(argv[0]); if( zPattern==0 ) return; | > > > > > > > > > > > > > > > > > > > > > | > | 7337 7338 7339 7340 7341 7342 7343 7344 7345 7346 7347 7348 7349 7350 7351 7352 7353 7354 7355 7356 7357 7358 7359 7360 7361 7362 7363 7364 7365 7366 7367 7368 7369 7370 7371 7372 7373 7374 7375 7376 |
const char *zErr;
ReCompiled *pRe;
sqlite3_str *pStr;
int i;
int n;
char *z;
(void)argc;
static const char *ReOpName[] = {
"EOF",
"MATCH",
"ANY",
"ANYSTAR",
"FORK",
"GOTO",
"ACCEPT",
"CC_INC",
"CC_EXC",
"CC_VALUE",
"CC_RANGE",
"WORD",
"NOTWORD",
"DIGIT",
"NOTDIGIT",
"SPACE",
"NOTSPACE",
"BOUNDARY",
"ATSTART",
};
zPattern = (const char*)sqlite3_value_text(argv[0]);
if( zPattern==0 ) return;
zErr = re_compile(&pRe, zPattern, re_maxlen(context),
sqlite3_user_data(context)!=0);
if( zErr ){
re_free(pRe);
sqlite3_result_error(context, zErr, -1);
return;
}
if( pRe==0 ){
sqlite3_result_error_nomem(context);
|
| ︙ | ︙ | |||
9282 9283 9284 9285 9286 9287 9288 9289 9290 9291 9292 9293 9294 9295 9296 9297 9298 9299 9300 9301 9302 9303 9304 9305 9306 9307 9308 9309 9310 9311 9312 9313 9314 |
(void)(argc); /* Unused parameter */
completionCursorReset(pCur);
if( idxNum & 1 ){
pCur->nPrefix = sqlite3_value_bytes(argv[iArg]);
if( pCur->nPrefix>0 ){
pCur->zPrefix = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
if( pCur->zPrefix==0 ) return SQLITE_NOMEM;
}
iArg = 1;
}
if( idxNum & 2 ){
pCur->nLine = sqlite3_value_bytes(argv[iArg]);
if( pCur->nLine>0 ){
pCur->zLine = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
if( pCur->zLine==0 ) return SQLITE_NOMEM;
}
}
if( pCur->zLine!=0 && pCur->zPrefix==0 ){
int i = pCur->nLine;
while( i>0 && (IsAlnum(pCur->zLine[i-1]) || pCur->zLine[i-1]=='_') ){
i--;
}
pCur->nPrefix = pCur->nLine - i;
if( pCur->nPrefix>0 ){
pCur->zPrefix = sqlite3_mprintf("%.*s", pCur->nPrefix, pCur->zLine + i);
if( pCur->zPrefix==0 ) return SQLITE_NOMEM;
}
}
pCur->iRowid = 0;
pCur->ePhase = COMPLETION_FIRST_PHASE;
return completionNext(pVtabCursor);
}
| > > > | 8945 8946 8947 8948 8949 8950 8951 8952 8953 8954 8955 8956 8957 8958 8959 8960 8961 8962 8963 8964 8965 8966 8967 8968 8969 8970 8971 8972 8973 8974 8975 8976 8977 8978 8979 8980 |
(void)(argc); /* Unused parameter */
completionCursorReset(pCur);
if( idxNum & 1 ){
pCur->nPrefix = sqlite3_value_bytes(argv[iArg]);
if( pCur->nPrefix>0 ){
pCur->zPrefix = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
if( pCur->zPrefix==0 ) return SQLITE_NOMEM;
pCur->nPrefix = (int)strlen(pCur->zPrefix);
}
iArg = 1;
}
if( idxNum & 2 ){
pCur->nLine = sqlite3_value_bytes(argv[iArg]);
if( pCur->nLine>0 ){
pCur->zLine = sqlite3_mprintf("%s", sqlite3_value_text(argv[iArg]));
if( pCur->zLine==0 ) return SQLITE_NOMEM;
pCur->nLine = (int)strlen(pCur->zLine);
}
}
if( pCur->zLine!=0 && pCur->zPrefix==0 ){
int i = pCur->nLine;
while( i>0 && (IsAlnum(pCur->zLine[i-1]) || pCur->zLine[i-1]=='_') ){
i--;
}
pCur->nPrefix = pCur->nLine - i;
if( pCur->nPrefix>0 ){
pCur->zPrefix = sqlite3_mprintf("%.*s", pCur->nPrefix, pCur->zLine + i);
if( pCur->zPrefix==0 ) return SQLITE_NOMEM;
pCur->nPrefix = (int)strlen(pCur->zPrefix);
}
}
pCur->iRowid = 0;
pCur->ePhase = COMPLETION_FIRST_PHASE;
return completionNext(pVtabCursor);
}
|
| ︙ | ︙ | |||
10214 10215 10216 10217 10218 10219 10220 |
"rawdata," /* 4: Raw data */
"data," /* 5: Uncompressed data */
"method," /* 6: Compression method (integer) */
"z HIDDEN" /* 7: Name of zip file */
") WITHOUT ROWID;";
#define ZIPFILE_F_COLUMN_IDX 7 /* Index of column "file" in the above */
| > > > > > > | | 9880 9881 9882 9883 9884 9885 9886 9887 9888 9889 9890 9891 9892 9893 9894 9895 9896 9897 9898 9899 9900 |
"rawdata," /* 4: Raw data */
"data," /* 5: Uncompressed data */
"method," /* 6: Compression method (integer) */
"z HIDDEN" /* 7: Name of zip file */
") WITHOUT ROWID;";
#define ZIPFILE_F_COLUMN_IDX 7 /* Index of column "file" in the above */
#define ZIPFILE_MX_NAME (250) /* Windows limitation on filename size */
/*
** The buffer should be large enough to contain 3 65536 byte strings - the
** filename, the extra field and the file comment.
*/
#define ZIPFILE_BUFFER_SIZE (200*1024)
/*
** Magic numbers used to read and write zip files.
**
** ZIPFILE_NEWENTRY_MADEBY:
** Use this value for the "version-made-by" field in new zip file
|
| ︙ | ︙ | |||
10771 10772 10773 10774 10775 10776 10777 10778 10779 10780 10781 10782 10783 10784 |
pLFH->mTime = zipfileRead16(aRead);
pLFH->mDate = zipfileRead16(aRead);
pLFH->crc32 = zipfileRead32(aRead);
pLFH->szCompressed = zipfileRead32(aRead);
pLFH->szUncompressed = zipfileRead32(aRead);
pLFH->nFile = zipfileRead16(aRead);
pLFH->nExtra = zipfileRead16(aRead);
}
return rc;
}
/*
** Buffer aExtra (size nExtra bytes) contains zip archive "extra" fields.
| > | 10443 10444 10445 10446 10447 10448 10449 10450 10451 10452 10453 10454 10455 10456 10457 |
pLFH->mTime = zipfileRead16(aRead);
pLFH->mDate = zipfileRead16(aRead);
pLFH->crc32 = zipfileRead32(aRead);
pLFH->szCompressed = zipfileRead32(aRead);
pLFH->szUncompressed = zipfileRead32(aRead);
pLFH->nFile = zipfileRead16(aRead);
pLFH->nExtra = zipfileRead16(aRead);
if( pLFH->nFile>ZIPFILE_MX_NAME ) rc = SQLITE_ERROR;
}
return rc;
}
/*
** Buffer aExtra (size nExtra bytes) contains zip archive "extra" fields.
|
| ︙ | ︙ | |||
10896 10897 10898 10899 10900 10901 10902 10903 10904 10905 10906 10907 10908 10909 |
assert( mUnixTime<315507600
|| mUnixTime==zipfileMtime(pCds)
|| ((mUnixTime % 2) && mUnixTime-1==zipfileMtime(pCds))
/* || (mUnixTime % 2) */
);
}
/*
** If aBlob is not NULL, then it is a pointer to a buffer (nBlob bytes in
** size) containing an entire zip archive image. Or, if aBlob is NULL,
** then pFile is a file-handle open on a zip file. In either case, this
** function creates a ZipfileEntry object based on the zip archive entry
** for which the CDS record is at offset iOff.
| > > > > > > > > > | 10569 10570 10571 10572 10573 10574 10575 10576 10577 10578 10579 10580 10581 10582 10583 10584 10585 10586 10587 10588 10589 10590 10591 |
assert( mUnixTime<315507600
|| mUnixTime==zipfileMtime(pCds)
|| ((mUnixTime % 2) && mUnixTime-1==zipfileMtime(pCds))
/* || (mUnixTime % 2) */
);
}
/*
** Set (*pzErr) to point to a buffer from sqlite3_malloc() containing a
** generic corruption message and return SQLITE_CORRUPT;
*/
static int zipfileCorrupt(char **pzErr){
*pzErr = sqlite3_mprintf("zip archive is corrupt");
return SQLITE_CORRUPT;
}
/*
** If aBlob is not NULL, then it is a pointer to a buffer (nBlob bytes in
** size) containing an entire zip archive image. Or, if aBlob is NULL,
** then pFile is a file-handle open on a zip file. In either case, this
** function creates a ZipfileEntry object based on the zip archive entry
** for which the CDS record is at offset iOff.
|
| ︙ | ︙ | |||
10919 10920 10921 10922 10923 10924 10925 |
FILE *pFile, /* If aBlob==0, read from this file */
i64 iOff, /* Offset of CDS record */
ZipfileEntry **ppEntry /* OUT: Pointer to new object */
){
u8 *aRead;
char **pzErr = &pTab->base.zErrMsg;
int rc = SQLITE_OK;
| < > > > > | 10601 10602 10603 10604 10605 10606 10607 10608 10609 10610 10611 10612 10613 10614 10615 10616 10617 10618 10619 10620 10621 10622 10623 |
FILE *pFile, /* If aBlob==0, read from this file */
i64 iOff, /* Offset of CDS record */
ZipfileEntry **ppEntry /* OUT: Pointer to new object */
){
u8 *aRead;
char **pzErr = &pTab->base.zErrMsg;
int rc = SQLITE_OK;
if( aBlob==0 ){
aRead = pTab->aBuffer;
rc = zipfileReadData(pFile, aRead, ZIPFILE_CDS_FIXED_SZ, iOff, pzErr);
}else{
if( (iOff+ZIPFILE_CDS_FIXED_SZ)>nBlob ){
/* Not enough data for the CDS structure. Corruption. */
return zipfileCorrupt(pzErr);
}
aRead = (u8*)&aBlob[iOff];
}
if( rc==SQLITE_OK ){
sqlite3_int64 nAlloc;
ZipfileEntry *pNew;
|
| ︙ | ︙ | |||
10955 10956 10957 10958 10959 10960 10961 10962 10963 10964 10965 10966 10967 10968 |
*pzErr = sqlite3_mprintf("failed to read CDS at offset %lld", iOff);
}else if( aBlob==0 ){
rc = zipfileReadData(
pFile, aRead, nExtra+nFile, iOff+ZIPFILE_CDS_FIXED_SZ, pzErr
);
}else{
aRead = (u8*)&aBlob[iOff + ZIPFILE_CDS_FIXED_SZ];
}
}
if( rc==SQLITE_OK ){
u32 *pt = &pNew->mUnixTime;
pNew->cds.zFile = sqlite3_mprintf("%.*s", nFile, aRead);
pNew->aExtra = (u8*)&pNew[1];
| > > > | 10640 10641 10642 10643 10644 10645 10646 10647 10648 10649 10650 10651 10652 10653 10654 10655 10656 |
*pzErr = sqlite3_mprintf("failed to read CDS at offset %lld", iOff);
}else if( aBlob==0 ){
rc = zipfileReadData(
pFile, aRead, nExtra+nFile, iOff+ZIPFILE_CDS_FIXED_SZ, pzErr
);
}else{
aRead = (u8*)&aBlob[iOff + ZIPFILE_CDS_FIXED_SZ];
if( (iOff + ZIPFILE_LFH_FIXED_SZ + nFile + nExtra)>nBlob ){
rc = zipfileCorrupt(pzErr);
}
}
}
if( rc==SQLITE_OK ){
u32 *pt = &pNew->mUnixTime;
pNew->cds.zFile = sqlite3_mprintf("%.*s", nFile, aRead);
pNew->aExtra = (u8*)&pNew[1];
|
| ︙ | ︙ | |||
10977 10978 10979 10980 10981 10982 10983 10984 10985 10986 10987 10988 10989 10990 |
if( rc==SQLITE_OK ){
static const int szFix = ZIPFILE_LFH_FIXED_SZ;
ZipfileLFH lfh;
if( pFile ){
rc = zipfileReadData(pFile, aRead, szFix, pNew->cds.iOffset, pzErr);
}else{
aRead = (u8*)&aBlob[pNew->cds.iOffset];
}
if( rc==SQLITE_OK ) rc = zipfileReadLFH(aRead, &lfh);
if( rc==SQLITE_OK ){
pNew->iDataOff = pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ;
pNew->iDataOff += lfh.nFile + lfh.nExtra;
if( aBlob && pNew->cds.szCompressed ){
| > > > > > > | | > | 10665 10666 10667 10668 10669 10670 10671 10672 10673 10674 10675 10676 10677 10678 10679 10680 10681 10682 10683 10684 10685 10686 10687 10688 10689 10690 10691 10692 10693 10694 |
if( rc==SQLITE_OK ){
static const int szFix = ZIPFILE_LFH_FIXED_SZ;
ZipfileLFH lfh;
if( pFile ){
rc = zipfileReadData(pFile, aRead, szFix, pNew->cds.iOffset, pzErr);
}else{
aRead = (u8*)&aBlob[pNew->cds.iOffset];
if( (pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ)>nBlob ){
rc = zipfileCorrupt(pzErr);
}
}
if( rc==SQLITE_OK ) rc = zipfileReadLFH(aRead, &lfh);
if( rc==SQLITE_OK ){
pNew->iDataOff = pNew->cds.iOffset + ZIPFILE_LFH_FIXED_SZ;
pNew->iDataOff += lfh.nFile + lfh.nExtra;
if( aBlob && pNew->cds.szCompressed ){
if( pNew->iDataOff + pNew->cds.szCompressed > nBlob ){
rc = zipfileCorrupt(pzErr);
}else{
pNew->aData = &pNew->aExtra[nExtra];
memcpy(pNew->aData, &aBlob[pNew->iDataOff], pNew->cds.szCompressed);
}
}
}else{
*pzErr = sqlite3_mprintf("failed to read LFH at offset %d",
(int)pNew->cds.iOffset
);
}
}
|
| ︙ | ︙ | |||
11772 11773 11774 11775 11776 11777 11778 11779 11780 11781 11782 11783 11784 11785 |
rc = zipfileGetMode(apVal[3], bIsDir, &mode, &pTab->base.zErrMsg);
}
if( rc==SQLITE_OK ){
zPath = (const char*)sqlite3_value_text(apVal[2]);
if( zPath==0 ) zPath = "";
nPath = (int)strlen(zPath);
mTime = zipfileGetTime(apVal[4]);
}
if( rc==SQLITE_OK && bIsDir ){
/* For a directory, check that the last character in the path is a
** '/'. This appears to be required for compatibility with info-zip
** (the unzip command on unix). It does not create directories
| > > > > > | 11467 11468 11469 11470 11471 11472 11473 11474 11475 11476 11477 11478 11479 11480 11481 11482 11483 11484 11485 |
rc = zipfileGetMode(apVal[3], bIsDir, &mode, &pTab->base.zErrMsg);
}
if( rc==SQLITE_OK ){
zPath = (const char*)sqlite3_value_text(apVal[2]);
if( zPath==0 ) zPath = "";
nPath = (int)strlen(zPath);
if( nPath>ZIPFILE_MX_NAME ){
zipfileTableErr(pTab, "filename too long; max: %d bytes",
ZIPFILE_MX_NAME);
rc = SQLITE_CONSTRAINT;
}
mTime = zipfileGetTime(apVal[4]);
}
if( rc==SQLITE_OK && bIsDir ){
/* For a directory, check that the last character in the path is a
** '/'. This appears to be required for compatibility with info-zip
** (the unzip command on unix). It does not create directories
|
| ︙ | ︙ | |||
12133 12134 12135 12136 12137 12138 12139 12140 12141 12142 12143 12144 12145 12146 |
zName = (char*)sqlite3_value_text(pName);
nName = sqlite3_value_bytes(pName);
if( zName==0 ){
zErr = sqlite3_mprintf("first argument to zipfile() must be non-NULL");
rc = SQLITE_ERROR;
goto zipfile_step_out;
}
/* Inspect the 'method' parameter. This must be either 0 (store), 8 (use
** deflate compression) or NULL (choose automatically). */
if( pMethod && SQLITE_NULL!=sqlite3_value_type(pMethod) ){
iMethod = (int)sqlite3_value_int64(pMethod);
if( iMethod!=0 && iMethod!=8 ){
zErr = sqlite3_mprintf("illegal method value: %d", iMethod);
| > > > > > > > | 11833 11834 11835 11836 11837 11838 11839 11840 11841 11842 11843 11844 11845 11846 11847 11848 11849 11850 11851 11852 11853 |
zName = (char*)sqlite3_value_text(pName);
nName = sqlite3_value_bytes(pName);
if( zName==0 ){
zErr = sqlite3_mprintf("first argument to zipfile() must be non-NULL");
rc = SQLITE_ERROR;
goto zipfile_step_out;
}
if( nName>ZIPFILE_MX_NAME ){
zErr = sqlite3_mprintf(
"filename argument to zipfile() too big; max: %d bytes",
ZIPFILE_MX_NAME);
rc = SQLITE_ERROR;
goto zipfile_step_out;
}
/* Inspect the 'method' parameter. This must be either 0 (store), 8 (use
** deflate compression) or NULL (choose automatically). */
if( pMethod && SQLITE_NULL!=sqlite3_value_type(pMethod) ){
iMethod = (int)sqlite3_value_int64(pMethod);
if( iMethod!=0 && iMethod!=8 ){
zErr = sqlite3_mprintf("illegal method value: %d", iMethod);
|
| ︙ | ︙ | |||
12475 12476 12477 12478 12479 12480 12481 12482 12483 12484 12485 12486 12487 12488 |
sqlarUncompressFunc, 0, 0);
}
return rc;
}
/************************* End ../ext/misc/sqlar.c ********************/
#endif
/************************* Begin ../ext/expert/sqlite3expert.h ******************/
/*
** 2017 April 07
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
| > | 12182 12183 12184 12185 12186 12187 12188 12189 12190 12191 12192 12193 12194 12195 12196 |
sqlarUncompressFunc, 0, 0);
}
return rc;
}
/************************* End ../ext/misc/sqlar.c ********************/
#endif
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
/************************* Begin ../ext/expert/sqlite3expert.h ******************/
/*
** 2017 April 07
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
|
| ︙ | ︙ | |||
14883 14884 14885 14886 14887 14888 14889 14890 14891 14892 14893 14894 14895 14896 |
sqlite3_free(p);
}
}
#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
/************************* End ../ext/expert/sqlite3expert.c ********************/
/************************* Begin ../ext/intck/sqlite3intck.h ******************/
/*
** 2024-02-08
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
| > | 14591 14592 14593 14594 14595 14596 14597 14598 14599 14600 14601 14602 14603 14604 14605 |
sqlite3_free(p);
}
}
#endif /* ifndef SQLITE_OMIT_VIRTUALTABLE */
/************************* End ../ext/expert/sqlite3expert.c ********************/
#endif
/************************* Begin ../ext/intck/sqlite3intck.h ******************/
/*
** 2024-02-08
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
|
| ︙ | ︙ | |||
21523 21524 21525 21526 21527 21528 21529 21530 21531 21532 21533 21534 21535 21536 21537 21538 21539 21540 21541 |
char *zName; /* Symbolic name for this session */
int nFilter; /* Number of xFilter rejection GLOB patterns */
char **azFilter; /* Array of xFilter rejection GLOB patterns */
sqlite3_session *p; /* The open session */
};
#endif
typedef struct ExpertInfo ExpertInfo;
struct ExpertInfo {
sqlite3expert *pExpert;
int bVerbose;
};
/* A single line in the EQP output */
typedef struct EQPGraphRow EQPGraphRow;
struct EQPGraphRow {
int iEqpId; /* ID for this row */
int iParentId; /* ID of the parent row */
EQPGraphRow *pNext; /* Next row in sequence */
| > > | 21232 21233 21234 21235 21236 21237 21238 21239 21240 21241 21242 21243 21244 21245 21246 21247 21248 21249 21250 21251 21252 |
char *zName; /* Symbolic name for this session */
int nFilter; /* Number of xFilter rejection GLOB patterns */
char **azFilter; /* Array of xFilter rejection GLOB patterns */
sqlite3_session *p; /* The open session */
};
#endif
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
typedef struct ExpertInfo ExpertInfo;
struct ExpertInfo {
sqlite3expert *pExpert;
int bVerbose;
};
#endif
/* A single line in the EQP output */
typedef struct EQPGraphRow EQPGraphRow;
struct EQPGraphRow {
int iEqpId; /* ID for this row */
int iParentId; /* ID of the parent row */
EQPGraphRow *pNext; /* Next row in sequence */
|
| ︙ | ︙ | |||
21631 21632 21633 21634 21635 21636 21637 21638 21639 21640 21641 21642 21643 21644 21645 |
} aAuxDb[5], /* Array of all database connections */
*pAuxDb; /* Currently active database connection */
int *aiIndent; /* Array of indents used in MODE_Explain */
int nIndent; /* Size of array aiIndent[] */
int iIndent; /* Index of current op in aiIndent[] */
char *zNonce; /* Nonce for temporary safe-mode escapes */
EQPGraph sGraph; /* Information for the graphical EXPLAIN QUERY PLAN */
ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */
#ifdef SQLITE_SHELL_FIDDLE
struct {
const char * zInput; /* Input string from wasm/JS proxy */
const char * zPos; /* Cursor pos into zInput */
const char * zDefaultDbName; /* Default name for db file */
} wasm;
#endif
| > > | 21342 21343 21344 21345 21346 21347 21348 21349 21350 21351 21352 21353 21354 21355 21356 21357 21358 |
} aAuxDb[5], /* Array of all database connections */
*pAuxDb; /* Currently active database connection */
int *aiIndent; /* Array of indents used in MODE_Explain */
int nIndent; /* Size of array aiIndent[] */
int iIndent; /* Index of current op in aiIndent[] */
char *zNonce; /* Nonce for temporary safe-mode escapes */
EQPGraph sGraph; /* Information for the graphical EXPLAIN QUERY PLAN */
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
ExpertInfo expert; /* Valid if previous command was ".expert OPT..." */
#endif
#ifdef SQLITE_SHELL_FIDDLE
struct {
const char * zInput; /* Input string from wasm/JS proxy */
const char * zPos; /* Cursor pos into zInput */
const char * zDefaultDbName; /* Default name for db file */
} wasm;
#endif
|
| ︙ | ︙ | |||
21659 21660 21661 21662 21663 21664 21665 | /* Allowed values for ShellState.openMode */ #define SHELL_OPEN_UNSPEC 0 /* No open-mode specified */ #define SHELL_OPEN_NORMAL 1 /* Normal database file */ #define SHELL_OPEN_APPENDVFS 2 /* Use appendvfs */ #define SHELL_OPEN_ZIPFILE 3 /* Use the zipfile virtual table */ | < | | | 21372 21373 21374 21375 21376 21377 21378 21379 21380 21381 21382 21383 21384 21385 21386 21387 | /* Allowed values for ShellState.openMode */ #define SHELL_OPEN_UNSPEC 0 /* No open-mode specified */ #define SHELL_OPEN_NORMAL 1 /* Normal database file */ #define SHELL_OPEN_APPENDVFS 2 /* Use appendvfs */ #define SHELL_OPEN_ZIPFILE 3 /* Use the zipfile virtual table */ #define SHELL_OPEN_DESERIALIZE 4 /* Open using sqlite3_deserialize() */ #define SHELL_OPEN_HEXDB 5 /* Use "dbtotxt" output as data source */ /* Allowed values for ShellState.eTraceType */ #define SHELL_TRACE_PLAIN 0 /* Show input SQL text */ #define SHELL_TRACE_EXPANDED 1 /* Show expanded SQL text */ #define SHELL_TRACE_NORMALIZED 2 /* Show normalized SQL text */ |
| ︙ | ︙ | |||
22004 22005 22006 22007 22008 22009 22010 |
/*
** Output the given string as a hex-encoded blob (eg. X'1234' )
*/
static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){
int i;
unsigned char *aBlob = (unsigned char*)pBlob;
| | | | 21716 21717 21718 21719 21720 21721 21722 21723 21724 21725 21726 21727 21728 21729 21730 21731 21732 21733 21734 21735 21736 21737 21738 21739 21740 21741 21742 21743 21744 21745 21746 21747 21748 21749 21750 21751 |
/*
** Output the given string as a hex-encoded blob (eg. X'1234' )
*/
static void output_hex_blob(FILE *out, const void *pBlob, int nBlob){
int i;
unsigned char *aBlob = (unsigned char*)pBlob;
char *zStr = sqlite3_malloc64((i64)nBlob*2 + 1);
shell_check_oom(zStr);
for(i=0; i<nBlob; i++){
static const char aHex[] = {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
zStr[i*2] = aHex[ (aBlob[i] >> 4) ];
zStr[i*2+1] = aHex[ (aBlob[i] & 0x0F) ];
}
zStr[i*2] = '\0';
sqlite3_fprintf(out, "X'%s'", zStr);
sqlite3_free(zStr);
}
/*
** Output the given string as a quoted string using SQL quoting conventions:
**
** (1) Single quotes (') within the string are doubled
** (2) The while string is enclosed in '...'
** (3) Control characters other than \n, \t, and \r\n are escaped
** using \u00XX notation and if such substitutions occur,
** the whole string is enclosed in unistr('...') instead of '...'.
**
** Step (3) is omitted if the control-character escape mode is OFF.
**
** See also: output_quoted_escaped_string() which does the same except
|
| ︙ | ︙ | |||
22271 22272 22273 22274 22275 22276 22277 | ** Escape the input string if it is needed and in accordance with ** eEscMode. ** ** Escaping is needed if the string contains any control characters ** other than \t, \n, and \r\n ** ** If no escaping is needed (the common case) then set *ppFree to NULL | | | 21983 21984 21985 21986 21987 21988 21989 21990 21991 21992 21993 21994 21995 21996 21997 | ** Escape the input string if it is needed and in accordance with ** eEscMode. ** ** Escaping is needed if the string contains any control characters ** other than \t, \n, and \r\n ** ** If no escaping is needed (the common case) then set *ppFree to NULL ** and return the original string. If escaping is needed, write the ** escaped string into memory obtained from sqlite3_malloc64() or the ** equivalent, and return the new string and set *ppFree to the new string ** as well. ** ** The caller is responsible for freeing *ppFree if it is non-NULL in order ** to reclaim memory. */ |
| ︙ | ︙ | |||
23276 23277 23278 23279 23280 23281 23282 |
/*
** Set the destination table field of the ShellState structure to
** the name of the table given. Escape any quote characters in the
** table name.
*/
static void set_table_name(ShellState *p, const char *zName){
| < < < < | | < < | | < < | < < < < > | 22988 22989 22990 22991 22992 22993 22994 22995 22996 22997 22998 22999 23000 23001 23002 23003 23004 23005 23006 23007 23008 23009 23010 23011 23012 |
/*
** Set the destination table field of the ShellState structure to
** the name of the table given. Escape any quote characters in the
** table name.
*/
static void set_table_name(ShellState *p, const char *zName){
if( p->zDestTable ){
sqlite3_free(p->zDestTable);
p->zDestTable = 0;
}
if( zName==0 ) return;
if( quoteChar(zName) ){
p->zDestTable = sqlite3_mprintf("\"%w\"", zName);
}else{
p->zDestTable = sqlite3_mprintf("%s", zName);
}
shell_check_oom(p->zDestTable);
}
/*
** Maybe construct two lines of text that point out the position of a
** syntax error. Return a pointer to the text, in memory obtained from
** sqlite3_malloc(). Or, if the most recent error does not involve a
** specific token that we can point to, return an empty string.
|
| ︙ | ︙ | |||
23494 23495 23496 23497 23498 23499 23500 |
** Display memory stats.
*/
static int display_stats(
sqlite3 *db, /* Database to query */
ShellState *pArg, /* Pointer to ShellState */
int bReset /* True to reset the stats */
){
| | | | 23195 23196 23197 23198 23199 23200 23201 23202 23203 23204 23205 23206 23207 23208 23209 23210 |
** Display memory stats.
*/
static int display_stats(
sqlite3 *db, /* Database to query */
ShellState *pArg, /* Pointer to ShellState */
int bReset /* True to reset the stats */
){
int iCur, iHiwtr;
sqlite3_int64 iCur64, iHiwtr64;
FILE *out;
if( pArg==0 || pArg->out==0 ) return 0;
out = pArg->out;
if( pArg->pStmt && pArg->statsOn==2 ){
int nCol, i, x;
sqlite3_stmt *pStmt = pArg->pStmt;
|
| ︙ | ︙ | |||
23584 23585 23586 23587 23588 23589 23590 23591 23592 23593 23594 23595 23596 23597 23598 23599 23600 23601 23602 23603 23604 23605 |
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1);
sqlite3_fprintf(out,
"Page cache hits: %d\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1);
sqlite3_fprintf(out,
"Page cache misses: %d\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1);
sqlite3_fprintf(out,
"Page cache writes: %d\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_SPILL, &iCur, &iHiwtr, 1);
sqlite3_fprintf(out,
"Page cache spills: %d\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset);
sqlite3_fprintf(out,
"Schema Heap Usage: %d bytes\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset);
sqlite3_fprintf(out,
| > > > > > > > | 23285 23286 23287 23288 23289 23290 23291 23292 23293 23294 23295 23296 23297 23298 23299 23300 23301 23302 23303 23304 23305 23306 23307 23308 23309 23310 23311 23312 23313 |
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_HIT, &iCur, &iHiwtr, 1);
sqlite3_fprintf(out,
"Page cache hits: %d\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_MISS, &iCur, &iHiwtr, 1);
sqlite3_fprintf(out,
"Page cache misses: %d\n", iCur);
iHiwtr64 = iCur64 = -1;
sqlite3_db_status64(db, SQLITE_DBSTATUS_TEMPBUF_SPILL, &iCur64, &iHiwtr64,
0);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_WRITE, &iCur, &iHiwtr, 1);
sqlite3_fprintf(out,
"Page cache writes: %d\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_CACHE_SPILL, &iCur, &iHiwtr, 1);
sqlite3_fprintf(out,
"Page cache spills: %d\n", iCur);
sqlite3_fprintf(out,
"Temporary data spilled to disk: %lld\n", iCur64);
sqlite3_db_status64(db, SQLITE_DBSTATUS_TEMPBUF_SPILL, &iCur64, &iHiwtr64,
1);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_SCHEMA_USED, &iCur, &iHiwtr, bReset);
sqlite3_fprintf(out,
"Schema Heap Usage: %d bytes\n", iCur);
iHiwtr = iCur = -1;
sqlite3_db_status(db, SQLITE_DBSTATUS_STMT_USED, &iCur, &iHiwtr, bReset);
sqlite3_fprintf(out,
|
| ︙ | ︙ | |||
23997 23998 23999 24000 24001 24002 24003 24004 24005 24006 24007 24008 24009 24010 |
}else if( strncmp(zVar, "$text_", 6)==0 ){
size_t szVar = strlen(zVar);
char *zBuf = sqlite3_malloc64( szVar-5 );
if( zBuf ){
memcpy(zBuf, &zVar[6], szVar-5);
sqlite3_bind_text64(pStmt, i, zBuf, szVar-6, sqlite3_free, SQLITE_UTF8);
}
}else{
sqlite3_bind_null(pStmt, i);
}
sqlite3_reset(pQ);
}
sqlite3_finalize(pQ);
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 23705 23706 23707 23708 23709 23710 23711 23712 23713 23714 23715 23716 23717 23718 23719 23720 23721 23722 23723 23724 23725 23726 23727 23728 23729 23730 23731 23732 23733 23734 23735 23736 23737 23738 23739 23740 23741 23742 23743 23744 23745 23746 23747 23748 |
}else if( strncmp(zVar, "$text_", 6)==0 ){
size_t szVar = strlen(zVar);
char *zBuf = sqlite3_malloc64( szVar-5 );
if( zBuf ){
memcpy(zBuf, &zVar[6], szVar-5);
sqlite3_bind_text64(pStmt, i, zBuf, szVar-6, sqlite3_free, SQLITE_UTF8);
}
#ifdef SQLITE_ENABLE_CARRAY
}else if( strncmp(zVar, "$carray_", 8)==0 ){
static char *azColorNames[] = {
"azure", "black", "blue", "brown", "cyan", "fuchsia", "gold",
"gray", "green", "indigo", "khaki", "lime", "magenta", "maroon",
"navy", "olive", "orange", "pink", "purple", "red", "silver",
"tan", "teal", "violet", "white", "yellow"
};
static int aPrimes[] = {
1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
53, 59, 61, 67, 71, 73, 79, 83, 89, 97
};
/* Special bindings: carray($carray_clr), carray($carray_primes)
** with --unsafe-testing: carray($carray_clr_p,26,'char*'),
** carray($carray_primes_p,26,'int32')
*/
if( strcmp(zVar+8,"clr")==0 ){
sqlite3_carray_bind(pStmt,i,azColorNames,26,SQLITE_CARRAY_TEXT,0);
}else if( strcmp(zVar+8,"primes")==0 ){
sqlite3_carray_bind(pStmt,i,aPrimes,26,SQLITE_CARRAY_INT32,0);
}else if( strcmp(zVar+8,"clr_p")==0
&& ShellHasFlag(pArg,SHFLG_TestingMode) ){
sqlite3_bind_pointer(pStmt,i,azColorNames,"carray",0);
}else if( strcmp(zVar+8,"primes_p")==0
&& ShellHasFlag(pArg,SHFLG_TestingMode) ){
sqlite3_bind_pointer(pStmt,i,aPrimes,"carray",0);
}else{
sqlite3_bind_null(pStmt, i);
}
#endif
}else{
sqlite3_bind_null(pStmt, i);
}
sqlite3_reset(pQ);
}
sqlite3_finalize(pQ);
}
|
| ︙ | ︙ | |||
24579 24580 24581 24582 24583 24584 24585 |
nRow, nRow!=1 ? "s" : "");
printf("%s", zBuf);
}
}
}
}
| | | 24317 24318 24319 24320 24321 24322 24323 24324 24325 24326 24327 24328 24329 24330 24331 |
nRow, nRow!=1 ? "s" : "");
printf("%s", zBuf);
}
}
}
}
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
/*
** This function is called to process SQL if the previous shell command
** was ".expert". It passes the SQL in the second argument directly to
** the sqlite3expert object.
**
** If successful, SQLITE_OK is returned. Otherwise, an SQLite error
** code. In this case, (*pzErr) may be set to point to a buffer containing
|
| ︙ | ︙ | |||
24711 24712 24713 24714 24715 24716 24717 |
);
}
}
sqlite3_free(zErr);
return rc;
}
| | | 24449 24450 24451 24452 24453 24454 24455 24456 24457 24458 24459 24460 24461 24462 24463 |
);
}
}
sqlite3_free(zErr);
return rc;
}
#endif /* !SQLITE_OMIT_VIRTUALTABLE && !SQLITE_OMIT_AUTHORIZATION */
/*
** Execute a statement or set of statements. Print
** any result rows/columns depending on the current mode
** set via the supplied callback.
**
** This is very similar to SQLite's built-in sqlite3_exec()
|
| ︙ | ︙ | |||
24737 24738 24739 24740 24741 24742 24743 |
const char *zLeftover; /* Tail of unprocessed SQL */
sqlite3 *db = pArg->db;
if( pzErrMsg ){
*pzErrMsg = NULL;
}
| | | 24475 24476 24477 24478 24479 24480 24481 24482 24483 24484 24485 24486 24487 24488 24489 |
const char *zLeftover; /* Tail of unprocessed SQL */
sqlite3 *db = pArg->db;
if( pzErrMsg ){
*pzErrMsg = NULL;
}
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
if( pArg->expert.pExpert ){
rc = expertHandleSQL(pArg, zSql, pzErrMsg);
return expertFinish(pArg, (rc!=SQLITE_OK), pzErrMsg);
}
#endif
while( zSql[0] && (SQLITE_OK == rc) ){
|
| ︙ | ︙ | |||
24899 24900 24901 24902 24903 24904 24905 |
** by an entry with azCol[i]==0.
*/
static char **tableColumnList(ShellState *p, const char *zTab){
char **azCol = 0;
sqlite3_stmt *pStmt;
char *zSql;
int nCol = 0;
| | | | 24637 24638 24639 24640 24641 24642 24643 24644 24645 24646 24647 24648 24649 24650 24651 24652 24653 24654 24655 24656 24657 24658 24659 24660 24661 24662 24663 24664 24665 |
** by an entry with azCol[i]==0.
*/
static char **tableColumnList(ShellState *p, const char *zTab){
char **azCol = 0;
sqlite3_stmt *pStmt;
char *zSql;
int nCol = 0;
i64 nAlloc = 0;
int nPK = 0; /* Number of PRIMARY KEY columns seen */
int isIPK = 0; /* True if one PRIMARY KEY column of type INTEGER */
int preserveRowid = ShellHasFlag(p, SHFLG_PreserveRowid);
int rc;
zSql = sqlite3_mprintf("PRAGMA table_info=%Q", zTab);
shell_check_oom(zSql);
rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
if( rc ) return 0;
while( sqlite3_step(pStmt)==SQLITE_ROW ){
if( nCol>=nAlloc-2 ){
nAlloc = nAlloc*2 + nCol + 10;
azCol = sqlite3_realloc64(azCol, nAlloc*sizeof(azCol[0]));
shell_check_oom(azCol);
}
azCol[++nCol] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 1));
shell_check_oom(azCol[nCol]);
if( sqlite3_column_int(pStmt, 5) ){
nPK++;
if( nPK==1
|
| ︙ | ︙ | |||
25104 25105 25106 25107 25108 25109 25110 |
}
freeColumnList(azCol);
appendText(&sSelect, " FROM ", 0);
appendText(&sSelect, zTable, quoteChar(zTable));
savedDestTable = p->zDestTable;
savedMode = p->mode;
| | | | | 24842 24843 24844 24845 24846 24847 24848 24849 24850 24851 24852 24853 24854 24855 24856 24857 24858 24859 24860 24861 24862 |
}
freeColumnList(azCol);
appendText(&sSelect, " FROM ", 0);
appendText(&sSelect, zTable, quoteChar(zTable));
savedDestTable = p->zDestTable;
savedMode = p->mode;
p->zDestTable = sTable.zTxt;
p->mode = p->cMode = MODE_Insert;
rc = shell_exec(p, sSelect.zTxt, 0);
if( (rc&0xff)==SQLITE_CORRUPT ){
sqlite3_fputs("/****** CORRUPTION ERROR *******/\n", p->out);
toggleSelectOrder(p->db);
shell_exec(p, sSelect.zTxt, 0);
toggleSelectOrder(p->db);
}
p->zDestTable = savedDestTable;
p->mode = savedMode;
freeText(&sTable);
freeText(&sSelect);
if( rc ) p->nErr++;
|
| ︙ | ︙ | |||
25243 25244 25245 25246 25247 25248 25249 25250 25251 25252 25253 25254 25255 25256 25257 | #ifndef SQLITE_SHELL_FIDDLE ".excel Display the output of next command in spreadsheet", " --bom Put a UTF8 byte-order mark on intermediate file", #endif #ifndef SQLITE_SHELL_FIDDLE ".exit ?CODE? Exit this program with return-code CODE", #endif ".expert EXPERIMENTAL. Suggest indexes for queries", ".explain ?on|off|auto? Change the EXPLAIN formatting mode. Default: auto", ".filectrl CMD ... Run various sqlite3_file_control() operations", " --schema SCHEMA Use SCHEMA instead of \"main\"", " --help Show CMD details", ".fullschema ?--indent? Show schema and the content of sqlite_stat tables", ".headers on|off Turn display of headers on or off", ".help ?-all? ?PATTERN? Show help text for PATTERN", | > > | 24981 24982 24983 24984 24985 24986 24987 24988 24989 24990 24991 24992 24993 24994 24995 24996 24997 | #ifndef SQLITE_SHELL_FIDDLE ".excel Display the output of next command in spreadsheet", " --bom Put a UTF8 byte-order mark on intermediate file", #endif #ifndef SQLITE_SHELL_FIDDLE ".exit ?CODE? Exit this program with return-code CODE", #endif #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION) ".expert EXPERIMENTAL. Suggest indexes for queries", #endif ".explain ?on|off|auto? Change the EXPLAIN formatting mode. Default: auto", ".filectrl CMD ... Run various sqlite3_file_control() operations", " --schema SCHEMA Use SCHEMA instead of \"main\"", " --help Show CMD details", ".fullschema ?--indent? Show schema and the content of sqlite_stat tables", ".headers on|off Turn display of headers on or off", ".help ?-all? ?PATTERN? Show help text for PATTERN", |
| ︙ | ︙ | |||
25268 25269 25270 25271 25272 25273 25274 | " determines the column names.", " * If neither --csv or --ascii are used, the input mode is derived", " from the \".mode\" output mode", " * If FILE begins with \"|\" then it is a command that generates the", " input text.", #endif #ifndef SQLITE_OMIT_TEST_CONTROL | | | 25008 25009 25010 25011 25012 25013 25014 25015 25016 25017 25018 25019 25020 25021 25022 | " determines the column names.", " * If neither --csv or --ascii are used, the input mode is derived", " from the \".mode\" output mode", " * If FILE begins with \"|\" then it is a command that generates the", " input text.", #endif #ifndef SQLITE_OMIT_TEST_CONTROL ".imposter INDEX TABLE Create imposter table TABLE on index INDEX", #endif ".indexes ?TABLE? Show names of indexes", " If TABLE is specified, only show indexes for", " tables matching TABLE using the LIKE operator.", ".intck ?STEPS_PER_UNLOCK? Run an incremental integrity check on the db", #ifdef SQLITE_ENABLE_IOTRACE ",iotrace FILE Enable I/O diagnostic logging to FILE", |
| ︙ | ︙ | |||
25335 25336 25337 25338 25339 25340 25341 25342 25343 25344 25345 25346 25347 25348 25349 | ** end users, so is "undocumented." */ ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE", " Options:", " --append Use appendvfs to append database to the end of FILE", #endif #ifndef SQLITE_OMIT_DESERIALIZE " --deserialize Load into memory using sqlite3_deserialize()", " --hexdb Load the output of \"dbtotxt\" as an in-memory db", " --maxsize N Maximum size for --hexdb or --deserialized database", #endif " --new Initialize FILE to an empty database", " --nofollow Do not follow symbolic links", " --readonly Open FILE readonly", " --zip FILE is a ZIP archive", #ifndef SQLITE_SHELL_FIDDLE | > > > > > > | 25075 25076 25077 25078 25079 25080 25081 25082 25083 25084 25085 25086 25087 25088 25089 25090 25091 25092 25093 25094 25095 | ** end users, so is "undocumented." */ ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE", " Options:", " --append Use appendvfs to append database to the end of FILE", #endif #ifndef SQLITE_OMIT_DESERIALIZE " --deserialize Load into memory using sqlite3_deserialize()", #endif /*" --exclusive Set the SQLITE_OPEN_EXCLUSIVE flag", UNDOCUMENTED */ #ifndef SQLITE_OMIT_DESERIALIZE " --hexdb Load the output of \"dbtotxt\" as an in-memory db", #endif " --ifexist Only open if FILE already exists", #ifndef SQLITE_OMIT_DESERIALIZE " --maxsize N Maximum size for --hexdb or --deserialized database", #endif " --new Initialize FILE to an empty database", " --nofollow Do not follow symbolic links", " --readonly Open FILE readonly", " --zip FILE is a ZIP archive", #ifndef SQLITE_SHELL_FIDDLE |
| ︙ | ︙ | |||
25724 25725 25726 25727 25728 25729 25730 |
** Reconstruct an in-memory database using the output from the "dbtotxt"
** program. Read content from the file in p->aAuxDb[].zDbFilename.
** If p->aAuxDb[].zDbFilename is 0, then read from standard input.
*/
static unsigned char *readHexDb(ShellState *p, int *pnData){
unsigned char *a = 0;
int nLine;
| | > | < | 25470 25471 25472 25473 25474 25475 25476 25477 25478 25479 25480 25481 25482 25483 25484 25485 25486 25487 |
** Reconstruct an in-memory database using the output from the "dbtotxt"
** program. Read content from the file in p->aAuxDb[].zDbFilename.
** If p->aAuxDb[].zDbFilename is 0, then read from standard input.
*/
static unsigned char *readHexDb(ShellState *p, int *pnData){
unsigned char *a = 0;
int nLine;
int n = 0; /* Size of db per first line of hex dump */
i64 sz = 0; /* n rounded up to nearest page boundary */
int pgsz = 0;
i64 iOffset = 0;
int rc;
FILE *in;
const char *zDbFilename = p->pAuxDb->zDbFilename;
unsigned int x[16];
char zLine[1000];
if( zDbFilename ){
in = sqlite3_fopen(zDbFilename, "r");
|
| ︙ | ︙ | |||
25751 25752 25753 25754 25755 25756 25757 | } *pnData = 0; nLine++; if( sqlite3_fgets(zLine, sizeof(zLine), in)==0 ) goto readHexDb_error; rc = sscanf(zLine, "| size %d pagesize %d", &n, &pgsz); if( rc!=2 ) goto readHexDb_error; if( n<0 ) goto readHexDb_error; | < < < < < > > > > > > | | | | | 25497 25498 25499 25500 25501 25502 25503 25504 25505 25506 25507 25508 25509 25510 25511 25512 25513 25514 25515 25516 25517 25518 25519 25520 25521 25522 25523 25524 25525 25526 25527 25528 25529 25530 25531 25532 25533 25534 25535 25536 25537 25538 25539 25540 25541 |
}
*pnData = 0;
nLine++;
if( sqlite3_fgets(zLine, sizeof(zLine), in)==0 ) goto readHexDb_error;
rc = sscanf(zLine, "| size %d pagesize %d", &n, &pgsz);
if( rc!=2 ) goto readHexDb_error;
if( n<0 ) goto readHexDb_error;
if( pgsz<512 || pgsz>65536 || (pgsz & (pgsz-1))!=0 ){
sqlite3_fputs("invalid pagesize\n", stderr);
goto readHexDb_error;
}
sz = ((i64)n+pgsz-1)&~(pgsz-1); /* Round up to nearest multiple of pgsz */
a = sqlite3_malloc64( sz ? sz : 1 );
shell_check_oom(a);
memset(a, 0, sz);
for(nLine++; sqlite3_fgets(zLine, sizeof(zLine), in)!=0; nLine++){
int j = 0; /* Page number from "| page" line */
int k = 0; /* Offset from "| page" line */
rc = sscanf(zLine, "| page %d offset %d", &j, &k);
if( rc==2 ){
iOffset = k;
continue;
}
if( cli_strncmp(zLine, "| end ", 6)==0 ){
break;
}
rc = sscanf(zLine,"| %d: %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x",
&j, &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7],
&x[8], &x[9], &x[10], &x[11], &x[12], &x[13], &x[14], &x[15]);
if( rc==17 ){
i64 iOff = iOffset+j;
if( iOff+16<=sz && iOff>=0 ){
int ii;
for(ii=0; ii<16; ii++) a[iOff+ii] = x[ii]&0xff;
}
}
}
*pnData = sz;
if( in!=p->in ){
fclose(in);
}else{
p->lineno = nLine;
}
return a;
|
| ︙ | ︙ | |||
25847 25848 25849 25850 25851 25852 25853 |
p->pLog = 0;
zFake = zName? shellFakeSchema(sqlite3_context_db_handle(pCtx), 0, zName) : 0;
p->pLog = pSavedLog;
if( zFake ){
sqlite3_result_text(pCtx, sqlite3_mprintf("/* %s */", zFake),
-1, sqlite3_free);
| | | 25594 25595 25596 25597 25598 25599 25600 25601 25602 25603 25604 25605 25606 25607 25608 |
p->pLog = 0;
zFake = zName? shellFakeSchema(sqlite3_context_db_handle(pCtx), 0, zName) : 0;
p->pLog = pSavedLog;
if( zFake ){
sqlite3_result_text(pCtx, sqlite3_mprintf("/* %s */", zFake),
-1, sqlite3_free);
sqlite3_free(zFake);
}
}
/* Flags for open_db().
**
** The default behavior of open_db() is to exit(1) if the database fails to
** open. The OPEN_DB_KEEPALIVE flag changes that so that it prints an error
|
| ︙ | ︙ | |||
25879 25880 25881 25882 25883 25884 25885 25886 25887 |
if( zDbFilename==0 || zDbFilename[0]==0 ){
p->openMode = SHELL_OPEN_NORMAL;
}else{
p->openMode = (u8)deduceDatabaseType(zDbFilename,
(openFlags & OPEN_DB_ZIPFILE)!=0);
}
}
switch( p->openMode ){
case SHELL_OPEN_APPENDVFS: {
| > > > > | < < < < < < | < | 25626 25627 25628 25629 25630 25631 25632 25633 25634 25635 25636 25637 25638 25639 25640 25641 25642 25643 25644 25645 25646 25647 25648 25649 25650 25651 25652 25653 25654 25655 25656 25657 25658 25659 25660 |
if( zDbFilename==0 || zDbFilename[0]==0 ){
p->openMode = SHELL_OPEN_NORMAL;
}else{
p->openMode = (u8)deduceDatabaseType(zDbFilename,
(openFlags & OPEN_DB_ZIPFILE)!=0);
}
}
if( (p->openFlags & (SQLITE_OPEN_READONLY|SQLITE_OPEN_READWRITE))==0 ){
if( p->openFlags==0 ) p->openFlags = SQLITE_OPEN_CREATE;
p->openFlags |= SQLITE_OPEN_READWRITE;
}
switch( p->openMode ){
case SHELL_OPEN_APPENDVFS: {
sqlite3_open_v2(zDbFilename, &p->db, p->openFlags, "apndvfs");
break;
}
case SHELL_OPEN_HEXDB:
case SHELL_OPEN_DESERIALIZE: {
sqlite3_open(0, &p->db);
break;
}
case SHELL_OPEN_ZIPFILE: {
sqlite3_open(":memory:", &p->db);
break;
}
case SHELL_OPEN_UNSPEC:
case SHELL_OPEN_NORMAL: {
sqlite3_open_v2(zDbFilename, &p->db, p->openFlags, 0);
break;
}
}
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
sqlite3_fprintf(stderr,"Error: unable to open database \"%s\": %s\n",
zDbFilename, sqlite3_errmsg(p->db));
if( (openFlags & OPEN_DB_KEEPALIVE)==0 ){
|
| ︙ | ︙ | |||
25942 25943 25944 25945 25946 25947 25948 |
sqlite3_enable_load_extension(p->db, 1);
#endif
sqlite3_sha_init(p->db, 0, 0);
sqlite3_shathree_init(p->db, 0, 0);
sqlite3_uint_init(p->db, 0, 0);
sqlite3_stmtrand_init(p->db, 0, 0);
sqlite3_decimal_init(p->db, 0, 0);
| < | 25686 25687 25688 25689 25690 25691 25692 25693 25694 25695 25696 25697 25698 25699 |
sqlite3_enable_load_extension(p->db, 1);
#endif
sqlite3_sha_init(p->db, 0, 0);
sqlite3_shathree_init(p->db, 0, 0);
sqlite3_uint_init(p->db, 0, 0);
sqlite3_stmtrand_init(p->db, 0, 0);
sqlite3_decimal_init(p->db, 0, 0);
sqlite3_base64_init(p->db, 0, 0);
sqlite3_base85_init(p->db, 0, 0);
sqlite3_regexp_init(p->db, 0, 0);
sqlite3_ieee_init(p->db, 0, 0);
sqlite3_series_init(p->db, 0, 0);
#ifndef SQLITE_SHELL_FIDDLE
sqlite3_fileio_init(p->db, 0, 0);
|
| ︙ | ︙ | |||
26041 26042 26043 26044 26045 26046 26047 26048 26049 26050 26051 26052 26053 26054 26055 26056 26057 |
if( p->szMax>0 ){
sqlite3_file_control(p->db, "main", SQLITE_FCNTL_SIZE_LIMIT, &p->szMax);
}
}
#endif
}
if( p->db!=0 ){
if( p->bSafeModePersist ){
sqlite3_set_authorizer(p->db, safeModeAuth, p);
}
sqlite3_db_config(
p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->scanstatsOn, (int*)0
);
}
}
/*
| > > | 25784 25785 25786 25787 25788 25789 25790 25791 25792 25793 25794 25795 25796 25797 25798 25799 25800 25801 25802 |
if( p->szMax>0 ){
sqlite3_file_control(p->db, "main", SQLITE_FCNTL_SIZE_LIMIT, &p->szMax);
}
}
#endif
}
if( p->db!=0 ){
#ifndef SQLITE_OMIT_AUTHORIZATION
if( p->bSafeModePersist ){
sqlite3_set_authorizer(p->db, safeModeAuth, p);
}
#endif
sqlite3_db_config(
p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, p->scanstatsOn, (int*)0
);
}
}
/*
|
| ︙ | ︙ | |||
26356 26357 26358 26359 26360 26361 26362 |
*/
typedef struct ImportCtx ImportCtx;
struct ImportCtx {
const char *zFile; /* Name of the input file */
FILE *in; /* Read the CSV text from this input stream */
int (SQLITE_CDECL *xCloser)(FILE*); /* Func to close in */
char *z; /* Accumulated text for a field */
| | | | 26101 26102 26103 26104 26105 26106 26107 26108 26109 26110 26111 26112 26113 26114 26115 26116 |
*/
typedef struct ImportCtx ImportCtx;
struct ImportCtx {
const char *zFile; /* Name of the input file */
FILE *in; /* Read the CSV text from this input stream */
int (SQLITE_CDECL *xCloser)(FILE*); /* Func to close in */
char *z; /* Accumulated text for a field */
i64 n; /* Number of bytes in z */
i64 nAlloc; /* Space allocated for z[] */
int nLine; /* Current line number */
int nRow; /* Number of rows imported */
int nErr; /* Number of errors encountered */
int bNotFirst; /* True if one or more bytes already read */
int cTerm; /* Character that terminated the most recent field */
int cColSep; /* The column separator character. (Usually ",") */
int cRowSep; /* The row separator character. (Usually "\n") */
|
| ︙ | ︙ | |||
26827 26828 26829 26830 26831 26832 26833 |
}
#if SQLITE_SHELL_HAVE_RECOVER
/*
** Convert a 2-byte or 4-byte big-endian integer into a native integer
*/
static unsigned int get2byteInt(unsigned char *a){
| | > | > > | 26572 26573 26574 26575 26576 26577 26578 26579 26580 26581 26582 26583 26584 26585 26586 26587 26588 26589 26590 26591 26592 |
}
#if SQLITE_SHELL_HAVE_RECOVER
/*
** Convert a 2-byte or 4-byte big-endian integer into a native integer
*/
static unsigned int get2byteInt(unsigned char *a){
return ((unsigned int)a[0]<<8) + (unsigned int)a[1];
}
static unsigned int get4byteInt(unsigned char *a){
return ((unsigned int)a[0]<<24)
+ ((unsigned int)a[1]<<16)
+ ((unsigned int)a[2]<<8)
+ (unsigned int)a[3];
}
/*
** Implementation of the ".dbinfo" command.
**
** Return 1 on error, 2 to exit, and 0 otherwise.
*/
|
| ︙ | ︙ | |||
26967 26968 26969 26970 26971 26972 26973 | if( rc ) goto dbtotxt_error; rc = 0; if( sqlite3_step(pStmt)!=SQLITE_ROW ) goto dbtotxt_error; nPage = sqlite3_column_int64(pStmt, 0); sqlite3_finalize(pStmt); pStmt = 0; if( nPage<1 ) goto dbtotxt_error; | | > > > > > | 26715 26716 26717 26718 26719 26720 26721 26722 26723 26724 26725 26726 26727 26728 26729 26730 26731 26732 26733 26734 26735 26736 26737 26738 26739 26740 26741 26742 26743 26744 |
if( rc ) goto dbtotxt_error;
rc = 0;
if( sqlite3_step(pStmt)!=SQLITE_ROW ) goto dbtotxt_error;
nPage = sqlite3_column_int64(pStmt, 0);
sqlite3_finalize(pStmt);
pStmt = 0;
if( nPage<1 ) goto dbtotxt_error;
rc = sqlite3_prepare_v2(p->db, "PRAGMA database_list", -1, &pStmt, 0);
if( rc ) goto dbtotxt_error;
if( sqlite3_step(pStmt)!=SQLITE_ROW ){
zTail = "unk.db";
}else{
const char *zFilename = (const char*)sqlite3_column_text(pStmt, 2);
if( zFilename==0 || zFilename[0]==0 ) zFilename = "unk.db";
zTail = strrchr(zFilename, '/');
#if defined(_WIN32)
if( zTail==0 ) zTail = strrchr(zFilename, '\\');
#endif
if( zTail==0 ){
zTail = zFilename;
}else if( zTail[1]!=0 ){
zTail++;
}
}
zName = strdup(zTail);
shell_check_oom(zName);
sqlite3_fprintf(p->out, "| size %lld pagesize %d filename %s\n",
nPage*pgSz, pgSz, zName);
sqlite3_finalize(pStmt);
pStmt = 0;
|
| ︙ | ︙ | |||
27146 27147 27148 27149 27150 27151 27152 27153 27154 27155 27156 27157 27158 27159 |
*/
static int optionMatch(const char *zStr, const char *zOpt){
if( zStr[0]!='-' ) return 0;
zStr++;
if( zStr[0]=='-' ) zStr++;
return cli_strcmp(zStr, zOpt)==0;
}
/*
** Delete a file.
*/
int shellDeleteFile(const char *zFilename){
int rc;
#ifdef _WIN32
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 26899 26900 26901 26902 26903 26904 26905 26906 26907 26908 26909 26910 26911 26912 26913 26914 26915 26916 26917 26918 26919 26920 26921 26922 26923 26924 26925 26926 26927 26928 26929 26930 26931 26932 26933 26934 26935 26936 26937 26938 26939 26940 26941 26942 26943 26944 26945 26946 26947 26948 26949 |
*/
static int optionMatch(const char *zStr, const char *zOpt){
if( zStr[0]!='-' ) return 0;
zStr++;
if( zStr[0]=='-' ) zStr++;
return cli_strcmp(zStr, zOpt)==0;
}
/*
** The input zFN is guaranteed to start with "file:" and is thus a URI
** filename. Extract the actual filename and return a pointer to that
** filename in spaced obtained from sqlite3_malloc().
**
** The caller is responsible for freeing space using sqlite3_free() when
** it has finished with the filename.
*/
static char *shellFilenameFromUri(const char *zFN){
char *zOut;
int i, j, d1, d2;
assert( cli_strncmp(zFN,"file:",5)==0 );
zOut = sqlite3_mprintf("%s", zFN+5);
shell_check_oom(zOut);
for(i=j=0; zOut[i]!=0 && zOut[i]!='?'; i++){
if( zOut[i]!='%' ){
zOut[j++] = zOut[i];
continue;
}
d1 = hexDigitValue(zOut[i+1]);
if( d1<0 ){
zOut[j] = 0;
break;
}
d2 = hexDigitValue(zOut[i+2]);
if( d2<0 ){
zOut[j] = 0;
break;
}
zOut[j++] = d1*16 + d2;
i += 2;
}
zOut[j] = 0;
return zOut;
}
/*
** Delete a file.
*/
int shellDeleteFile(const char *zFilename){
int rc;
#ifdef _WIN32
|
| ︙ | ︙ | |||
28702 28703 28704 28705 28706 28707 28708 |
static int do_meta_command(char *zLine, ShellState *p){
int h = 1;
int nArg = 0;
int n, c;
int rc = 0;
char *azArg[52];
| | | 28492 28493 28494 28495 28496 28497 28498 28499 28500 28501 28502 28503 28504 28505 28506 |
static int do_meta_command(char *zLine, ShellState *p){
int h = 1;
int nArg = 0;
int n, c;
int rc = 0;
char *azArg[52];
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
if( p->expert.pExpert ){
expertFinish(p, 1, 0);
}
#endif
/* Parse the input line into tokens.
*/
|
| ︙ | ︙ | |||
29003 29004 29005 29006 29007 29008 29009 |
shellDatabaseError(p->db);
rc = 1;
}else{
while( sqlite3_step(pStmt)==SQLITE_ROW ){
const char *zSchema = (const char *)sqlite3_column_text(pStmt,1);
const char *zFile = (const char*)sqlite3_column_text(pStmt,2);
if( zSchema==0 || zFile==0 ) continue;
| | | 28793 28794 28795 28796 28797 28798 28799 28800 28801 28802 28803 28804 28805 28806 28807 |
shellDatabaseError(p->db);
rc = 1;
}else{
while( sqlite3_step(pStmt)==SQLITE_ROW ){
const char *zSchema = (const char *)sqlite3_column_text(pStmt,1);
const char *zFile = (const char*)sqlite3_column_text(pStmt,2);
if( zSchema==0 || zFile==0 ) continue;
azName = sqlite3_realloc64(azName, (nName+1)*2*sizeof(char*));
shell_check_oom(azName);
azName[nName*2] = strdup(zSchema);
azName[nName*2+1] = strdup(zFile);
nName++;
}
}
sqlite3_finalize(pStmt);
|
| ︙ | ︙ | |||
29206 29207 29208 29209 29210 29211 29212 29213 29214 29215 29216 29217 29218 29219 |
}else{
eputz("Usage: .echo on|off\n");
rc = 1;
}
}else
if( c=='d' && n>=3 && cli_strncmp(azArg[0], "dbtotxt", n)==0 ){
rc = shell_dbtotxt_command(p, nArg, azArg);
}else
if( c=='e' && cli_strncmp(azArg[0], "eqp", n)==0 ){
if( nArg==2 ){
p->autoEQPtest = 0;
if( p->autoEQPtrace ){
| > | 28996 28997 28998 28999 29000 29001 29002 29003 29004 29005 29006 29007 29008 29009 29010 |
}else{
eputz("Usage: .echo on|off\n");
rc = 1;
}
}else
if( c=='d' && n>=3 && cli_strncmp(azArg[0], "dbtotxt", n)==0 ){
open_db(p, 0);
rc = shell_dbtotxt_command(p, nArg, azArg);
}else
if( c=='e' && cli_strncmp(azArg[0], "eqp", n)==0 ){
if( nArg==2 ){
p->autoEQPtest = 0;
if( p->autoEQPtrace ){
|
| ︙ | ︙ | |||
29271 29272 29273 29274 29275 29276 29277 |
p->autoExplain = 0;
}else if( val==99 ){
if( p->mode==MODE_Explain ) p->mode = p->normalMode;
p->autoExplain = 1;
}
}else
| | | 29062 29063 29064 29065 29066 29067 29068 29069 29070 29071 29072 29073 29074 29075 29076 |
p->autoExplain = 0;
}else if( val==99 ){
if( p->mode==MODE_Explain ) p->mode = p->normalMode;
p->autoExplain = 1;
}
}else
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
if( c=='e' && cli_strncmp(azArg[0], "expert", n)==0 ){
if( p->bSafeMode ){
sqlite3_fprintf(stderr,
"Cannot run experimental commands such as \"%s\" in safe mode\n",
azArg[0]);
rc = 1;
}else{
|
| ︙ | ︙ | |||
29838 29839 29840 29841 29842 29843 29844 |
char *zSql;
char *zCollist = 0;
sqlite3_stmt *pStmt;
int tnum = 0;
int isWO = 0; /* True if making an imposter of a WITHOUT ROWID table */
int lenPK = 0; /* Length of the PRIMARY KEY string for isWO tables */
int i;
| < < < < < < | 29629 29630 29631 29632 29633 29634 29635 29636 29637 29638 29639 29640 29641 29642 |
char *zSql;
char *zCollist = 0;
sqlite3_stmt *pStmt;
int tnum = 0;
int isWO = 0; /* True if making an imposter of a WITHOUT ROWID table */
int lenPK = 0; /* Length of the PRIMARY KEY string for isWO tables */
int i;
if( !(nArg==3 || (nArg==2 && sqlite3_stricmp(azArg[1],"off")==0)) ){
eputz("Usage: .imposter INDEX IMPOSTER\n"
" .imposter off\n");
/* Also allowed, but not documented:
**
** .imposter TABLE IMPOSTER
**
|
| ︙ | ︙ | |||
29915 29916 29917 29918 29919 29920 29921 |
goto meta_command_exit;
}
if( lenPK==0 ) lenPK = 100000;
zSql = sqlite3_mprintf(
"CREATE TABLE \"%w\"(%s,PRIMARY KEY(%.*s))WITHOUT ROWID",
azArg[2], zCollist, lenPK, zCollist);
sqlite3_free(zCollist);
| | < < < | 29700 29701 29702 29703 29704 29705 29706 29707 29708 29709 29710 29711 29712 29713 29714 29715 29716 29717 29718 29719 29720 29721 29722 |
goto meta_command_exit;
}
if( lenPK==0 ) lenPK = 100000;
zSql = sqlite3_mprintf(
"CREATE TABLE \"%w\"(%s,PRIMARY KEY(%.*s))WITHOUT ROWID",
azArg[2], zCollist, lenPK, zCollist);
sqlite3_free(zCollist);
rc = sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 2, tnum);
if( rc==SQLITE_OK ){
rc = sqlite3_exec(p->db, zSql, 0, 0, 0);
sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, p->db, "main", 0, 0);
if( rc ){
sqlite3_fprintf(stderr,
"Error in [%s]: %s\n", zSql, sqlite3_errmsg(p->db));
}else{
sqlite3_fprintf(stdout, "%s;\n", zSql);
}
}else{
sqlite3_fprintf(stderr,"SQLITE_TESTCTRL_IMPOSTER returns %d\n", rc);
rc = 1;
}
sqlite3_free(zSql);
}else
|
| ︙ | ︙ | |||
30280 30281 30282 30283 30284 30285 30286 30287 30288 30289 30290 30291 30292 30293 30294 30295 30296 30297 30298 30299 30300 |
if( c=='o' && cli_strncmp(azArg[0], "open", n)==0 && n>=2 ){
const char *zFN = 0; /* Pointer to constant filename */
char *zNewFilename = 0; /* Name of the database file to open */
int iName = 1; /* Index in azArg[] of the filename */
int newFlag = 0; /* True to delete file before opening */
int openMode = SHELL_OPEN_UNSPEC;
/* Check for command-line arguments */
for(iName=1; iName<nArg; iName++){
const char *z = azArg[iName];
#ifndef SQLITE_SHELL_FIDDLE
if( optionMatch(z,"new") ){
newFlag = 1;
#ifdef SQLITE_HAVE_ZLIB
}else if( optionMatch(z, "zip") ){
openMode = SHELL_OPEN_ZIPFILE;
#endif
}else if( optionMatch(z, "append") ){
openMode = SHELL_OPEN_APPENDVFS;
}else if( optionMatch(z, "readonly") ){
| > > | > > > > | | 30062 30063 30064 30065 30066 30067 30068 30069 30070 30071 30072 30073 30074 30075 30076 30077 30078 30079 30080 30081 30082 30083 30084 30085 30086 30087 30088 30089 30090 30091 30092 30093 30094 30095 30096 30097 30098 |
if( c=='o' && cli_strncmp(azArg[0], "open", n)==0 && n>=2 ){
const char *zFN = 0; /* Pointer to constant filename */
char *zNewFilename = 0; /* Name of the database file to open */
int iName = 1; /* Index in azArg[] of the filename */
int newFlag = 0; /* True to delete file before opening */
int openMode = SHELL_OPEN_UNSPEC;
int openFlags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
/* Check for command-line arguments */
for(iName=1; iName<nArg; iName++){
const char *z = azArg[iName];
#ifndef SQLITE_SHELL_FIDDLE
if( optionMatch(z,"new") ){
newFlag = 1;
#ifdef SQLITE_HAVE_ZLIB
}else if( optionMatch(z, "zip") ){
openMode = SHELL_OPEN_ZIPFILE;
#endif
}else if( optionMatch(z, "append") ){
openMode = SHELL_OPEN_APPENDVFS;
}else if( optionMatch(z, "readonly") ){
openFlags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
openFlags |= SQLITE_OPEN_READONLY;
}else if( optionMatch(z, "exclusive") ){
openFlags |= SQLITE_OPEN_EXCLUSIVE;
}else if( optionMatch(z, "ifexists") ){
openFlags &= ~(SQLITE_OPEN_CREATE);
}else if( optionMatch(z, "nofollow") ){
openFlags |= SQLITE_OPEN_NOFOLLOW;
#ifndef SQLITE_OMIT_DESERIALIZE
}else if( optionMatch(z, "deserialize") ){
openMode = SHELL_OPEN_DESERIALIZE;
}else if( optionMatch(z, "hexdb") ){
openMode = SHELL_OPEN_HEXDB;
}else if( optionMatch(z, "maxsize") && iName+1<nArg ){
p->szMax = integerValue(azArg[++iName]);
|
| ︙ | ︙ | |||
30328 30329 30330 30331 30332 30333 30334 |
session_close_all(p, -1);
close_db(p->db);
p->db = 0;
p->pAuxDb->zDbFilename = 0;
sqlite3_free(p->pAuxDb->zFreeOnClose);
p->pAuxDb->zFreeOnClose = 0;
p->openMode = openMode;
| | | > > > > > > > > > | 30116 30117 30118 30119 30120 30121 30122 30123 30124 30125 30126 30127 30128 30129 30130 30131 30132 30133 30134 30135 30136 30137 30138 30139 30140 30141 30142 30143 30144 |
session_close_all(p, -1);
close_db(p->db);
p->db = 0;
p->pAuxDb->zDbFilename = 0;
sqlite3_free(p->pAuxDb->zFreeOnClose);
p->pAuxDb->zFreeOnClose = 0;
p->openMode = openMode;
p->openFlags = openFlags;
p->szMax = 0;
/* If a filename is specified, try to open it first */
if( zFN || p->openMode==SHELL_OPEN_HEXDB ){
if( newFlag && zFN && !p->bSafeMode ){
if( cli_strncmp(zFN,"file:",5)==0 ){
char *zDel = shellFilenameFromUri(zFN);
shell_check_oom(zDel);
shellDeleteFile(zDel);
sqlite3_free(zDel);
}else{
shellDeleteFile(zFN);
}
}
#ifndef SQLITE_SHELL_FIDDLE
if( p->bSafeMode
&& p->openMode!=SHELL_OPEN_HEXDB
&& zFN
&& cli_strcmp(zFN,":memory:")!=0
){
failIfSafeMode(p, "cannot open disk-based database files in safe mode");
|
| ︙ | ︙ | |||
30936 30937 30938 30939 30940 30941 30942 |
}
if( bNoSystemTabs ){
appendText(&sSelect, "name NOT LIKE 'sqlite__%%' ESCAPE '_' AND ", 0);
}
appendText(&sSelect, "sql IS NOT NULL"
" ORDER BY snum, rowid", 0);
if( bDebug ){
| | | | 30733 30734 30735 30736 30737 30738 30739 30740 30741 30742 30743 30744 30745 30746 30747 30748 30749 |
}
if( bNoSystemTabs ){
appendText(&sSelect, "name NOT LIKE 'sqlite__%%' ESCAPE '_' AND ", 0);
}
appendText(&sSelect, "sql IS NOT NULL"
" ORDER BY snum, rowid", 0);
if( bDebug ){
sqlite3_fprintf(p->out, "SQL: %s;\n", sSelect.zTxt);
}else{
rc = sqlite3_exec(p->db, sSelect.zTxt, callback, &data, &zErrMsg);
}
freeText(&sSelect);
}
if( zErrMsg ){
shellEmitError(zErrMsg);
sqlite3_free(zErrMsg);
rc = 1;
|
| ︙ | ︙ | |||
31070 31071 31072 31073 31074 31075 31076 |
}
}else
/* .session filter GLOB ....
** Set a list of GLOB patterns of table names to be excluded.
*/
if( cli_strcmp(azCmd[0], "filter")==0 ){
| | > | | 30867 30868 30869 30870 30871 30872 30873 30874 30875 30876 30877 30878 30879 30880 30881 30882 30883 30884 30885 30886 30887 30888 30889 30890 |
}
}else
/* .session filter GLOB ....
** Set a list of GLOB patterns of table names to be excluded.
*/
if( cli_strcmp(azCmd[0], "filter")==0 ){
int ii;
i64 nByte;
if( nCmd<2 ) goto session_syntax_error;
if( pAuxDb->nSession ){
for(ii=0; ii<pSession->nFilter; ii++){
sqlite3_free(pSession->azFilter[ii]);
}
sqlite3_free(pSession->azFilter);
nByte = sizeof(pSession->azFilter[0])*(nCmd-1);
pSession->azFilter = sqlite3_malloc64( nByte );
shell_check_oom( pSession->azFilter );
for(ii=1; ii<nCmd; ii++){
char *x = pSession->azFilter[ii-1] = sqlite3_mprintf("%s", azCmd[ii]);
shell_check_oom(x);
}
pSession->nFilter = ii-1;
}
|
| ︙ | ︙ | |||
31262 31263 31264 31265 31266 31267 31268 |
}
if( cli_strcmp(zOp,"memo")==0 ){
sqlite3_fprintf(p->out, "%s\n", zSql);
}else
if( cli_strcmp(zOp,"run")==0 ){
char *zErrMsg = 0;
str.n = 0;
| | | | | | 31060 31061 31062 31063 31064 31065 31066 31067 31068 31069 31070 31071 31072 31073 31074 31075 31076 31077 31078 31079 31080 31081 31082 31083 31084 31085 31086 31087 31088 31089 |
}
if( cli_strcmp(zOp,"memo")==0 ){
sqlite3_fprintf(p->out, "%s\n", zSql);
}else
if( cli_strcmp(zOp,"run")==0 ){
char *zErrMsg = 0;
str.n = 0;
str.zTxt[0] = 0;
rc = sqlite3_exec(p->db, zSql, captureOutputCallback, &str, &zErrMsg);
nTest++;
if( bVerbose ){
sqlite3_fprintf(p->out, "Result: %s\n", str.zTxt);
}
if( rc || zErrMsg ){
nErr++;
rc = 1;
sqlite3_fprintf(p->out, "%d: error-code-%d: %s\n", tno, rc,zErrMsg);
sqlite3_free(zErrMsg);
}else if( cli_strcmp(zAns,str.zTxt)!=0 ){
nErr++;
rc = 1;
sqlite3_fprintf(p->out, "%d: Expected: [%s]\n", tno, zAns);
sqlite3_fprintf(p->out, "%d: Got: [%s]\n", tno, str.zTxt);
}
}
else{
sqlite3_fprintf(stderr,
"Unknown operation \"%s\" on selftest line %d\n", zOp, tno);
rc = 1;
break;
|
| ︙ | ︙ | |||
31393 31394 31395 31396 31397 31398 31399 |
" ORDER BY tbl,idx;", 0);
}else if( cli_strcmp(zTab, "sqlite_stat4")==0 ){
appendText(&sQuery, "SELECT * FROM ", 0);
appendText(&sQuery, zTab, 0);
appendText(&sQuery, " ORDER BY tbl, idx, rowid;\n", 0);
}
appendText(&sSql, zSep, 0);
| | | | | 31191 31192 31193 31194 31195 31196 31197 31198 31199 31200 31201 31202 31203 31204 31205 31206 31207 31208 31209 31210 31211 31212 31213 31214 31215 31216 31217 31218 31219 31220 31221 31222 31223 |
" ORDER BY tbl,idx;", 0);
}else if( cli_strcmp(zTab, "sqlite_stat4")==0 ){
appendText(&sQuery, "SELECT * FROM ", 0);
appendText(&sQuery, zTab, 0);
appendText(&sQuery, " ORDER BY tbl, idx, rowid;\n", 0);
}
appendText(&sSql, zSep, 0);
appendText(&sSql, sQuery.zTxt, '\'');
sQuery.n = 0;
appendText(&sSql, ",", 0);
appendText(&sSql, zTab, '\'');
zSep = "),(";
}
sqlite3_finalize(pStmt);
if( bSeparate ){
zSql = sqlite3_mprintf(
"%s))"
" SELECT lower(hex(sha3_query(a,%d))) AS hash, b AS label"
" FROM [sha3sum$query]",
sSql.zTxt, iSize);
}else{
zSql = sqlite3_mprintf(
"%s))"
" SELECT lower(hex(sha3_query(group_concat(a,''),%d))) AS hash"
" FROM [sha3sum$query]",
sSql.zTxt, iSize);
}
shell_check_oom(zSql);
freeText(&sQuery);
freeText(&sSql);
if( bDebug ){
sqlite3_fprintf(p->out, "%s\n", zSql);
}else{
|
| ︙ | ︙ | |||
31611 31612 31613 31614 31615 31616 31617 |
rc = 1;
sqlite3_finalize(pStmt);
goto meta_command_exit;
}
for(ii=0; sqlite3_step(pStmt)==SQLITE_ROW; ii++){
const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1);
if( zDbName==0 ) continue;
| | | 31409 31410 31411 31412 31413 31414 31415 31416 31417 31418 31419 31420 31421 31422 31423 |
rc = 1;
sqlite3_finalize(pStmt);
goto meta_command_exit;
}
for(ii=0; sqlite3_step(pStmt)==SQLITE_ROW; ii++){
const char *zDbName = (const char*)sqlite3_column_text(pStmt, 1);
if( zDbName==0 ) continue;
if( s.zTxt && s.zTxt[0] ) appendText(&s, " UNION ALL ", 0);
if( sqlite3_stricmp(zDbName, "main")==0 ){
appendText(&s, "SELECT name FROM ", 0);
}else{
appendText(&s, "SELECT ", 0);
appendText(&s, zDbName, '\'');
appendText(&s, "||'.'||name FROM ", 0);
}
|
| ︙ | ︙ | |||
31633 31634 31635 31636 31637 31638 31639 |
appendText(&s," WHERE type='index'"
" AND tbl_name LIKE ?1", 0);
}
}
rc = sqlite3_finalize(pStmt);
if( rc==SQLITE_OK ){
appendText(&s, " ORDER BY 1", 0);
| | | | | 31431 31432 31433 31434 31435 31436 31437 31438 31439 31440 31441 31442 31443 31444 31445 31446 31447 31448 31449 31450 31451 31452 31453 31454 31455 31456 31457 31458 31459 31460 31461 31462 31463 31464 31465 |
appendText(&s," WHERE type='index'"
" AND tbl_name LIKE ?1", 0);
}
}
rc = sqlite3_finalize(pStmt);
if( rc==SQLITE_OK ){
appendText(&s, " ORDER BY 1", 0);
rc = sqlite3_prepare_v2(p->db, s.zTxt, -1, &pStmt, 0);
}
freeText(&s);
if( rc ) return shellDatabaseError(p->db);
/* Run the SQL statement prepared by the above block. Store the results
** as an array of nul-terminated strings in azResult[]. */
nRow = nAlloc = 0;
azResult = 0;
if( nArg>1 ){
sqlite3_bind_text(pStmt, 1, azArg[1], -1, SQLITE_TRANSIENT);
}else{
sqlite3_bind_text(pStmt, 1, "%", -1, SQLITE_STATIC);
}
while( sqlite3_step(pStmt)==SQLITE_ROW ){
if( nRow>=nAlloc ){
char **azNew;
sqlite3_int64 n2 = 2*(sqlite3_int64)nAlloc + 10;
azNew = sqlite3_realloc64(azResult, sizeof(azResult[0])*n2);
shell_check_oom(azNew);
nAlloc = (int)n2;
azResult = azNew;
}
azResult[nRow] = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
shell_check_oom(azResult[nRow]);
nRow++;
}
if( sqlite3_finalize(pStmt)!=SQLITE_OK ){
|
| ︙ | ︙ | |||
31816 31817 31818 31819 31820 31821 31822 |
{ 0x00000008, 1, "FactorOutConst" },
{ 0x00000010, 1, "DistinctOpt" },
{ 0x00000020, 1, "CoverIdxScan" },
{ 0x00000040, 1, "OrderByIdxJoin" },
{ 0x00000080, 1, "Transitive" },
{ 0x00000100, 1, "OmitNoopJoin" },
{ 0x00000200, 1, "CountOfView" },
| | | 31614 31615 31616 31617 31618 31619 31620 31621 31622 31623 31624 31625 31626 31627 31628 |
{ 0x00000008, 1, "FactorOutConst" },
{ 0x00000010, 1, "DistinctOpt" },
{ 0x00000020, 1, "CoverIdxScan" },
{ 0x00000040, 1, "OrderByIdxJoin" },
{ 0x00000080, 1, "Transitive" },
{ 0x00000100, 1, "OmitNoopJoin" },
{ 0x00000200, 1, "CountOfView" },
{ 0x00000400, 1, "CursorHints" },
{ 0x00000800, 1, "Stat4" },
{ 0x00001000, 1, "PushDown" },
{ 0x00002000, 1, "SimplifyJoin" },
{ 0x00004000, 1, "SkipScan" },
{ 0x00008000, 1, "PropagateConst" },
{ 0x00010000, 1, "MinMaxOpt" },
{ 0x00020000, 1, "SeekScan" },
|
| ︙ | ︙ | |||
32363 32364 32365 32366 32367 32368 32369 |
int j;
assert( nArg<=ArraySize(azArg) );
p->nWidth = nArg-1;
p->colWidth = realloc(p->colWidth, (p->nWidth+1)*sizeof(int)*2);
if( p->colWidth==0 && p->nWidth>0 ) shell_out_of_memory();
if( p->nWidth ) p->actualWidth = &p->colWidth[p->nWidth];
for(j=1; j<nArg; j++){
| > > > | | 32161 32162 32163 32164 32165 32166 32167 32168 32169 32170 32171 32172 32173 32174 32175 32176 32177 32178 |
int j;
assert( nArg<=ArraySize(azArg) );
p->nWidth = nArg-1;
p->colWidth = realloc(p->colWidth, (p->nWidth+1)*sizeof(int)*2);
if( p->colWidth==0 && p->nWidth>0 ) shell_out_of_memory();
if( p->nWidth ) p->actualWidth = &p->colWidth[p->nWidth];
for(j=1; j<nArg; j++){
i64 w = integerValue(azArg[j]);
if( w < -30000 ) w = -30000;
if( w > +30000 ) w = +30000;
p->colWidth[j-1] = (int)w;
}
}else
{
sqlite3_fprintf(stderr,"Error: unknown command or invalid arguments: "
" \"%s\". Enter \".help\" for help\n", azArg[0]);
rc = 1;
|
| ︙ | ︙ | |||
32886 32887 32888 32889 32890 32891 32892 |
home_dir = z;
}
return home_dir;
}
/*
| | > > | > > | < > > | > > > > > > > > | | | > | | | | > > | > | < | | | | | | | | | > > | > | < | > > | | < | > | > | 32687 32688 32689 32690 32691 32692 32693 32694 32695 32696 32697 32698 32699 32700 32701 32702 32703 32704 32705 32706 32707 32708 32709 32710 32711 32712 32713 32714 32715 32716 32717 32718 32719 32720 32721 32722 32723 32724 32725 32726 32727 32728 32729 32730 32731 32732 32733 32734 32735 32736 32737 32738 32739 32740 32741 32742 32743 32744 32745 32746 32747 32748 32749 32750 32751 32752 32753 32754 32755 32756 32757 32758 32759 32760 32761 32762 32763 32764 32765 32766 32767 32768 32769 32770 32771 32772 32773 32774 32775 32776 32777 32778 32779 32780 32781 32782 32783 32784 32785 32786 32787 32788 32789 32790 32791 32792 32793 32794 32795 32796 32797 32798 32799 32800 |
home_dir = z;
}
return home_dir;
}
/*
** On non-Windows platforms, look for:
**
** - ${zEnvVar}/${zBaseName}
** - ${HOME}/${zSubdir}/${zBaseName}
**
** $zEnvVar is intended to be the name of an XDG_... environment
** variable, e.g. XDG_CONFIG_HOME or XDG_STATE_HOME. If zEnvVar is
** NULL or getenv(zEnvVar) is NULL then fall back to the second
** option. If the selected option is not found in the filesystem,
** return 0.
**
** zSubdir may be NULL or empty, in which case ${HOME}/${zBaseName}
** becomes the fallback.
**
** Both zSubdir and zBaseName may contain subdirectory parts. zSubdir
** will conventionally be ".config" or ".local/state", which, not
** coincidentally, is the typical subdir of the corresponding XDG_...
** var with the XDG var's $HOME prefix.
**
** The returned string is obtained from sqlite3_malloc() and should be
** sqlite3_free()'d by the caller.
*/
static char *find_xdg_file(const char *zEnvVar, const char *zSubdir,
const char *zBaseName){
#if defined(_WIN32) || defined(WIN32) || defined(_WIN32_WCE) \
|| defined(__RTP__) || defined(_WRS_KERNEL)
return 0;
#else
char *zConfigFile = 0;
const char *zXdgDir;
zXdgDir = zEnvVar ? getenv(zEnvVar) : 0;
if( zXdgDir ){
zConfigFile = sqlite3_mprintf("%s/%s", zXdgDir, zBaseName);
}else{
const char * zHome = find_home_dir(0);
if( zHome==0 ) return 0;
zConfigFile = (zSubdir && *zSubdir)
? sqlite3_mprintf("%s/%s/%s", zHome, zSubdir, zBaseName)
: sqlite3_mprintf("%s/%s", zHome, zBaseName);
}
shell_check_oom(zConfigFile);
if( access(zConfigFile,0)!=0 ){
sqlite3_free(zConfigFile);
zConfigFile = 0;
}
return zConfigFile;
#endif
}
/*
** Read input from the file sqliterc_override. If that parameter is
** NULL, take it from find_xdg_file(), if found, or fall back to
** ~/.sqliterc.
**
** Failure to read the config is only considered a failure if
** sqliterc_override is not NULL, in which case this function may emit
** a warning or, if ::bail_on_error is true, fail fatally if the file
** named by sqliterc_override is not found.
*/
static void process_sqliterc(
ShellState *p, /* Configuration data */
const char *sqliterc_override /* Name of config file. NULL to use default */
){
char *home_dir = NULL;
char *sqliterc = (char*)sqliterc_override;
FILE *inSaved = p->in;
int savedLineno = p->lineno;
if( sqliterc == NULL ){
sqliterc = find_xdg_file("XDG_CONFIG_HOME",
".config",
"sqlite3/sqliterc");
}
if( sqliterc == NULL ){
home_dir = find_home_dir(0);
if( home_dir==0 ){
eputz("-- warning: cannot find home directory;"
" cannot read ~/.sqliterc\n");
return;
}
sqliterc = sqlite3_mprintf("%s/.sqliterc",home_dir);
shell_check_oom(sqliterc);
}
p->in = sqliterc ? sqlite3_fopen(sqliterc,"rb") : 0;
if( p->in ){
if( stdin_is_interactive ){
sqlite3_fprintf(stderr,"-- Loading resources from %s\n", sqliterc);
}
if( process_input(p) && bail_on_error ) exit(1);
fclose(p->in);
}else if( sqliterc_override!=0 ){
sqlite3_fprintf(stderr,"cannot open: \"%s\"\n", sqliterc);
if( bail_on_error ) exit(1);
}
p->in = inSaved;
p->lineno = savedLineno;
if( sqliterc != sqliterc_override ){
sqlite3_free(sqliterc);
}
}
/*
** Show available command line options
*/
static const char zOptions[] =
" -- treat no subsequent arguments as options\n"
|
| ︙ | ︙ | |||
32995 32996 32997 32998 32999 33000 33001 33002 33003 33004 33005 33006 33007 33008 | " -init FILENAME read/process named file\n" " -[no]header turn headers on or off\n" #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) " -heap SIZE Size of heap for memsys3 or memsys5\n" #endif " -help show this message\n" " -html set output mode to HTML\n" " -interactive force interactive I/O\n" " -json set output mode to 'json'\n" " -line set output mode to 'line'\n" " -list set output mode to 'list'\n" " -lookaside SIZE N use N entries of SZ bytes for lookaside memory\n" " -markdown set output mode to 'markdown'\n" #if !defined(SQLITE_OMIT_DESERIALIZE) | > | 32817 32818 32819 32820 32821 32822 32823 32824 32825 32826 32827 32828 32829 32830 32831 | " -init FILENAME read/process named file\n" " -[no]header turn headers on or off\n" #if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5) " -heap SIZE Size of heap for memsys3 or memsys5\n" #endif " -help show this message\n" " -html set output mode to HTML\n" " -ifexists only open if database already exists\n" " -interactive force interactive I/O\n" " -json set output mode to 'json'\n" " -line set output mode to 'line'\n" " -list set output mode to 'list'\n" " -lookaside SIZE N use N entries of SZ bytes for lookaside memory\n" " -markdown set output mode to 'markdown'\n" #if !defined(SQLITE_OMIT_DESERIALIZE) |
| ︙ | ︙ | |||
33339 33340 33341 33342 33343 33344 33345 |
sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64);
#else
(void)cmdline_option_value(argc, argv, ++i);
#endif
}else if( cli_strcmp(z,"-pagecache")==0 ){
sqlite3_int64 n, sz;
sz = integerValue(cmdline_option_value(argc,argv,++i));
| | > > > > > > > > | | 33162 33163 33164 33165 33166 33167 33168 33169 33170 33171 33172 33173 33174 33175 33176 33177 33178 33179 33180 33181 33182 33183 33184 33185 33186 33187 33188 33189 33190 33191 33192 33193 33194 33195 33196 33197 33198 33199 33200 33201 33202 |
sqlite3_config(SQLITE_CONFIG_HEAP, malloc((int)szHeap), (int)szHeap, 64);
#else
(void)cmdline_option_value(argc, argv, ++i);
#endif
}else if( cli_strcmp(z,"-pagecache")==0 ){
sqlite3_int64 n, sz;
sz = integerValue(cmdline_option_value(argc,argv,++i));
if( sz>65536 ) sz = 65536;
if( sz<0 ) sz = 0;
n = integerValue(cmdline_option_value(argc,argv,++i));
if( sz>0 && n>0 && 0xffffffffffffLL/sz<n ){
n = 0xffffffffffffLL/sz;
}
if( sz>0 && (sz & (sz-1))==0 ){
/* If SIZE is a power of two, round it up by the PCACHE_HDRSZ */
int szHdr = 0;
sqlite3_config(SQLITE_CONFIG_PCACHE_HDRSZ, &szHdr);
sz += szHdr;
sqlite3_fprintf(stdout, "Page cache size increased to %d to accommodate"
" the %d-byte headers\n", (int)sz, szHdr);
}
verify_uninitialized();
sqlite3_config(SQLITE_CONFIG_PAGECACHE,
(n>0 && sz>0) ? malloc(n*sz) : 0, sz, n);
data.shellFlgs |= SHFLG_Pagecache;
}else if( cli_strcmp(z,"-lookaside")==0 ){
int n, sz;
sz = (int)integerValue(cmdline_option_value(argc,argv,++i));
if( sz<0 ) sz = 0;
n = (int)integerValue(cmdline_option_value(argc,argv,++i));
if( n<0 ) n = 0;
verify_uninitialized();
sqlite3_config(SQLITE_CONFIG_LOOKASIDE, sz, n);
if( (i64)sz*(i64)n==0 ) data.shellFlgs &= ~SHFLG_Lookaside;
}else if( cli_strcmp(z,"-threadsafe")==0 ){
int n;
n = (int)integerValue(cmdline_option_value(argc,argv,++i));
verify_uninitialized();
switch( n ){
case 0: sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); break;
case 2: sqlite3_config(SQLITE_CONFIG_MULTITHREAD); break;
|
| ︙ | ︙ | |||
33399 33400 33401 33402 33403 33404 33405 |
#ifndef SQLITE_OMIT_DESERIALIZE
}else if( cli_strcmp(z,"-deserialize")==0 ){
data.openMode = SHELL_OPEN_DESERIALIZE;
}else if( cli_strcmp(z,"-maxsize")==0 && i+1<argc ){
data.szMax = integerValue(argv[++i]);
#endif
}else if( cli_strcmp(z,"-readonly")==0 ){
| > | | > > > > > | 33230 33231 33232 33233 33234 33235 33236 33237 33238 33239 33240 33241 33242 33243 33244 33245 33246 33247 33248 33249 33250 33251 33252 |
#ifndef SQLITE_OMIT_DESERIALIZE
}else if( cli_strcmp(z,"-deserialize")==0 ){
data.openMode = SHELL_OPEN_DESERIALIZE;
}else if( cli_strcmp(z,"-maxsize")==0 && i+1<argc ){
data.szMax = integerValue(argv[++i]);
#endif
}else if( cli_strcmp(z,"-readonly")==0 ){
data.openFlags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
data.openFlags |= SQLITE_OPEN_READONLY;
}else if( cli_strcmp(z,"-nofollow")==0 ){
data.openFlags |= SQLITE_OPEN_NOFOLLOW;
}else if( cli_strcmp(z,"-exclusive")==0 ){ /* UNDOCUMENTED */
data.openFlags |= SQLITE_OPEN_EXCLUSIVE;
}else if( cli_strcmp(z,"-ifexists")==0 ){
data.openFlags &= ~(SQLITE_OPEN_CREATE);
if( data.openFlags==0 ) data.openFlags = SQLITE_OPEN_READWRITE;
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
}else if( cli_strncmp(z, "-A",2)==0 ){
/* All remaining command-line arguments are passed to the ".archive"
** command, so ignore them */
break;
#endif
}else if( cli_strcmp(z, "-memtrace")==0 ){
|
| ︙ | ︙ | |||
33555 33556 33557 33558 33559 33560 33561 |
#ifndef SQLITE_OMIT_DESERIALIZE
}else if( cli_strcmp(z,"-deserialize")==0 ){
data.openMode = SHELL_OPEN_DESERIALIZE;
}else if( cli_strcmp(z,"-maxsize")==0 && i+1<argc ){
data.szMax = integerValue(argv[++i]);
#endif
}else if( cli_strcmp(z,"-readonly")==0 ){
| > | > > > > > | 33392 33393 33394 33395 33396 33397 33398 33399 33400 33401 33402 33403 33404 33405 33406 33407 33408 33409 33410 33411 33412 33413 33414 |
#ifndef SQLITE_OMIT_DESERIALIZE
}else if( cli_strcmp(z,"-deserialize")==0 ){
data.openMode = SHELL_OPEN_DESERIALIZE;
}else if( cli_strcmp(z,"-maxsize")==0 && i+1<argc ){
data.szMax = integerValue(argv[++i]);
#endif
}else if( cli_strcmp(z,"-readonly")==0 ){
data.openFlags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
data.openFlags |= SQLITE_OPEN_READONLY;
}else if( cli_strcmp(z,"-nofollow")==0 ){
data.openFlags |= SQLITE_OPEN_NOFOLLOW;
}else if( cli_strcmp(z,"-exclusive")==0 ){ /* UNDOCUMENTED */
data.openFlags |= SQLITE_OPEN_EXCLUSIVE;
}else if( cli_strcmp(z,"-ifexists")==0 ){
data.openFlags &= ~(SQLITE_OPEN_CREATE);
if( data.openFlags==0 ) data.openFlags = SQLITE_OPEN_READWRITE;
}else if( cli_strcmp(z,"-ascii")==0 ){
data.mode = MODE_Ascii;
sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,SEP_Unit);
sqlite3_snprintf(sizeof(data.rowSeparator), data.rowSeparator,SEP_Record);
}else if( cli_strcmp(z,"-tabs")==0 ){
data.mode = MODE_List;
sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,SEP_Tab);
|
| ︙ | ︙ | |||
33732 33733 33734 33735 33736 33737 33738 |
}
}else{
/* Run commands received from standard input
*/
if( stdin_is_interactive ){
char *zHome;
char *zHistory;
| < > | | | > | > | > | | | 33575 33576 33577 33578 33579 33580 33581 33582 33583 33584 33585 33586 33587 33588 33589 33590 33591 33592 33593 33594 33595 33596 33597 33598 33599 33600 33601 33602 33603 33604 33605 33606 33607 33608 33609 33610 33611 33612 33613 33614 33615 33616 33617 33618 33619 33620 33621 33622 33623 33624 33625 33626 33627 33628 33629 33630 33631 33632 33633 33634 33635 |
}
}else{
/* Run commands received from standard input
*/
if( stdin_is_interactive ){
char *zHome;
char *zHistory;
sqlite3_fprintf(stdout,
"SQLite version %s %.19s\n" /*extra-version-info*/
"Enter \".help\" for usage hints.\n",
sqlite3_libversion(), sqlite3_sourceid());
if( warnInmemoryDb ){
sputz(stdout, "Connected to a ");
printBold("transient in-memory database");
sputz(stdout, ".\nUse \".open FILENAME\" to reopen on a"
" persistent database.\n");
}
zHistory = getenv("SQLITE_HISTORY");
if( zHistory ){
zHistory = sqlite3_mprintf("%s", zHistory);
shell_check_oom(zHistory);
}else{
zHistory = find_xdg_file("XDG_STATE_HOME",
".local/state",
"sqlite_history");
if( 0==zHistory && (zHome = find_home_dir(0))!=0 ){
zHistory = sqlite3_mprintf("%s/.sqlite_history", zHome);
shell_check_oom(zHistory);
}
}
if( zHistory ){ shell_read_history(zHistory); }
#if (HAVE_READLINE || HAVE_EDITLINE) && !defined(SQLITE_OMIT_READLINE_COMPLETION)
rl_attempted_completion_function = readline_completion;
#elif HAVE_LINENOISE==1
linenoiseSetCompletionCallback(linenoise_completion);
#elif HAVE_LINENOISE==2
linenoiseSetCompletionCallback(linenoise_completion, NULL);
#endif
data.in = 0;
rc = process_input(&data);
if( zHistory ){
shell_stifle_history(2000);
shell_write_history(zHistory);
sqlite3_free(zHistory);
}
}else{
data.in = stdin;
rc = process_input(&data);
}
}
#ifndef SQLITE_SHELL_FIDDLE
/* In WASM mode we have to leave the db state in place so that
** client code can "push" SQL into it after this call returns. */
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_AUTHORIZATION)
if( data.expert.pExpert ){
expertFinish(&data, 1, 0);
}
#endif
shell_main_exit:
free(azCmd);
set_table_name(&data, 0);
|
| ︙ | ︙ |
Changes to extsrc/sqlite3.c.
| ︙ | ︙ | |||
14 15 16 17 18 19 20 | ** the text of this file. Search for "Begin file sqlite3.h" to find the start ** of the embedded sqlite3.h header file.) Additional code files may be needed ** if you want a wrapper to interface SQLite with your choice of programming ** language. The code for the "sqlite3" command-line shell is also in a ** separate file. This file contains only code for the core SQLite library. ** ** The content in this amalgamation comes from Fossil check-in | | | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | ** the text of this file. Search for "Begin file sqlite3.h" to find the start ** of the embedded sqlite3.h header file.) Additional code files may be needed ** if you want a wrapper to interface SQLite with your choice of programming ** language. The code for the "sqlite3" command-line shell is also in a ** separate file. This file contains only code for the core SQLite library. ** ** The content in this amalgamation comes from Fossil check-in ** 5cbccab499bc3983aac1f57355552db607de with changes in files: ** ** */ #ifndef SQLITE_AMALGAMATION #define SQLITE_CORE 1 #define SQLITE_AMALGAMATION 1 #ifndef SQLITE_PRIVATE |
| ︙ | ︙ | |||
166 167 168 169 170 171 172 | #define SQLITE_OS_OTHER 0 #define SQLITE_HOMEGROWN_RECURSIVE_MUTEX 1 #define SQLITE_OMIT_LOAD_EXTENSION 1 #define SQLITE_ENABLE_LOCKING_STYLE 0 #define HAVE_UTIME 1 #else /* This is not VxWorks. */ | > | > | 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 | #define SQLITE_OS_OTHER 0 #define SQLITE_HOMEGROWN_RECURSIVE_MUTEX 1 #define SQLITE_OMIT_LOAD_EXTENSION 1 #define SQLITE_ENABLE_LOCKING_STYLE 0 #define HAVE_UTIME 1 #else /* This is not VxWorks. */ #ifndef OS_VXWORKS # define OS_VXWORKS 0 #endif #define HAVE_FCHOWN 1 #define HAVE_READLINK 1 #define HAVE_LSTAT 1 #endif /* defined(_WRS_KERNEL) */ /************** End of vxworks.h *********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ |
| ︙ | ︙ | |||
463 464 465 466 467 468 469 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.51.0" #define SQLITE_VERSION_NUMBER 3051000 | | > > > | 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.51.0" #define SQLITE_VERSION_NUMBER 3051000 #define SQLITE_SOURCE_ID "2025-10-15 10:52:45 5cbccab499bc3983aac1f57355552db607dee6c7ef4eb00d794dbee89c18db70" #define SQLITE_SCM_BRANCH "trunk" #define SQLITE_SCM_TAGS "" #define SQLITE_SCM_DATETIME "2025-10-15T10:52:45.276Z" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros |
| ︙ | ︙ | |||
812 813 814 815 816 817 818 819 820 821 822 823 824 825 | ** [sqlite3_extended_result_codes()] API. Or, the extended code for ** the most recent error can be obtained using ** [sqlite3_extended_errcode()]. */ #define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8)) #define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8)) #define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3<<8)) #define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8)) #define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8)) #define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8)) #define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8)) #define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8)) #define SQLITE_IOERR_TRUNCATE (SQLITE_IOERR | (6<<8)) #define SQLITE_IOERR_FSTAT (SQLITE_IOERR | (7<<8)) | > > > | 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 | ** [sqlite3_extended_result_codes()] API. Or, the extended code for ** the most recent error can be obtained using ** [sqlite3_extended_errcode()]. */ #define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8)) #define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8)) #define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3<<8)) #define SQLITE_ERROR_RESERVESIZE (SQLITE_ERROR | (4<<8)) #define SQLITE_ERROR_KEY (SQLITE_ERROR | (5<<8)) #define SQLITE_ERROR_UNABLE (SQLITE_ERROR | (6<<8)) #define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8)) #define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8)) #define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8)) #define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8)) #define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8)) #define SQLITE_IOERR_TRUNCATE (SQLITE_IOERR | (6<<8)) #define SQLITE_IOERR_FSTAT (SQLITE_IOERR | (7<<8)) |
| ︙ | ︙ | |||
846 847 848 849 850 851 852 853 854 855 856 857 858 859 | #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8)) #define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8)) #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) #define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) | > > | 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 | #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8)) #define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8)) #define SQLITE_IOERR_BADKEY (SQLITE_IOERR | (35<<8)) #define SQLITE_IOERR_CODEC (SQLITE_IOERR | (36<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8)) #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) #define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) |
| ︙ | ︙ | |||
2650 2651 2652 2653 2654 2655 2656 | ** SQLite version 3.35.0, TEMP views are still allowed even if ** this option is off. So, in other words, this option now only disables ** views in the main database schema or in the schemas of ATTACH-ed ** databases.)^ </dd> ** ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]] ** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt> | | | | | | > | < > | | > | | > | 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 | ** SQLite version 3.35.0, TEMP views are still allowed even if ** this option is off. So, in other words, this option now only disables ** views in the main database schema or in the schemas of ATTACH-ed ** databases.)^ </dd> ** ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]] ** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt> ** <dd> ^This option is used to enable or disable using the ** [fts3_tokenizer()] function - part of the [FTS3] full-text search engine ** extension - without using bound parameters as the parameters. Doing so ** is disabled by default. There must be two additional arguments. The first ** argument is an integer. If it is passed 0, then using fts3_tokenizer() ** without bound parameters is disabled. If it is passed a positive value, ** then calling fts3_tokenizer without bound parameters is enabled. If it ** is passed a negative value, this setting is not modified - this can be ** used to query for the current setting. The second parameter is a pointer ** to an integer into which is written 0 or 1 to indicate the current value ** of this setting (after it is modified, if applicable). The second ** parameter may be a NULL pointer, in which case the value of the setting ** is not reported back. Refer to [FTS3] documentation for further details. ** </dd> ** ** [[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION]] ** <dt>SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION</dt> ** <dd> ^This option is used to enable or disable the [sqlite3_load_extension()] ** interface independently of the [load_extension()] SQL function. ** The [sqlite3_enable_load_extension()] API enables or disables both the ** C-API [sqlite3_load_extension()] and the SQL function [load_extension()]. |
| ︙ | ︙ | |||
4510 4511 4512 4513 4514 4515 4516 4517 4518 4519 4520 4521 4522 4523 |
SQLITE_API int sqlite3_errcode(sqlite3 *db);
SQLITE_API int sqlite3_extended_errcode(sqlite3 *db);
SQLITE_API const char *sqlite3_errmsg(sqlite3*);
SQLITE_API const void *sqlite3_errmsg16(sqlite3*);
SQLITE_API const char *sqlite3_errstr(int);
SQLITE_API int sqlite3_error_offset(sqlite3 *db);
/*
** CAPI3REF: Prepared Statement Object
** KEYWORDS: {prepared statement} {prepared statements}
**
** An instance of this object represents a single SQL statement that
** has been compiled into binary form and is ready to be evaluated.
**
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 4549 4550 4551 4552 4553 4554 4555 4556 4557 4558 4559 4560 4561 4562 4563 4564 |
SQLITE_API int sqlite3_errcode(sqlite3 *db);
SQLITE_API int sqlite3_extended_errcode(sqlite3 *db);
SQLITE_API const char *sqlite3_errmsg(sqlite3*);
SQLITE_API const void *sqlite3_errmsg16(sqlite3*);
SQLITE_API const char *sqlite3_errstr(int);
SQLITE_API int sqlite3_error_offset(sqlite3 *db);
/*
** CAPI3REF: Set Error Codes And Message
** METHOD: sqlite3
**
** Set the error code of the database handle passed as the first argument
** to errcode, and the error message to a copy of nul-terminated string
** zErrMsg. If zErrMsg is passed NULL, then the error message is set to
** the default message associated with the supplied error code. Subsequent
** calls to [sqlite3_errcode()] and [sqlite3_errmsg()] and similar will
** return the values set by this routine in place of what was previously
** set by SQLite itself.
**
** This function returns SQLITE_OK if the error code and error message are
** successfully set, SQLITE_NOMEM if an OOM occurs, and SQLITE_MISUSE if
** the database handle is NULL or invalid.
**
** The error code and message set by this routine remains in effect until
** they are changed, either by another call to this routine or until they are
** changed to by SQLite itself to reflect the result of some subsquent
** API call.
**
** This function is intended for use by SQLite extensions or wrappers. The
** idea is that an extension or wrapper can use this routine to set error
** messages and error codes and thus behave more like a core SQLite
** feature from the point of view of an application.
*/
SQLITE_API int sqlite3_set_errmsg(sqlite3 *db, int errcode, const char *zErrMsg);
/*
** CAPI3REF: Prepared Statement Object
** KEYWORDS: {prepared statement} {prepared statements}
**
** An instance of this object represents a single SQL statement that
** has been compiled into binary form and is ready to be evaluated.
**
|
| ︙ | ︙ | |||
6520 6521 6522 6523 6524 6525 6526 6527 6528 6529 6530 6531 6532 6533 | ** with a [database connection]. ** A call to sqlite3_set_clientdata(D,N,P,X) causes the pointer P ** to be attached to [database connection] D using name N. Subsequent ** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P ** or a NULL pointer if there were no prior calls to ** sqlite3_set_clientdata() with the same values of D and N. ** Names are compared using strcmp() and are thus case sensitive. ** ** If P and X are both non-NULL, then the destructor X is invoked with ** argument P on the first of the following occurrences: ** <ul> ** <li> An out-of-memory error occurs during the call to ** sqlite3_set_clientdata() which attempts to register pointer P. ** <li> A subsequent call to sqlite3_set_clientdata(D,N,P,X) is made | > | 6561 6562 6563 6564 6565 6566 6567 6568 6569 6570 6571 6572 6573 6574 6575 | ** with a [database connection]. ** A call to sqlite3_set_clientdata(D,N,P,X) causes the pointer P ** to be attached to [database connection] D using name N. Subsequent ** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P ** or a NULL pointer if there were no prior calls to ** sqlite3_set_clientdata() with the same values of D and N. ** Names are compared using strcmp() and are thus case sensitive. ** It returns 0 on success and SQLITE_NOMEM on allocation failure. ** ** If P and X are both non-NULL, then the destructor X is invoked with ** argument P on the first of the following occurrences: ** <ul> ** <li> An out-of-memory error occurs during the call to ** sqlite3_set_clientdata() which attempts to register pointer P. ** <li> A subsequent call to sqlite3_set_clientdata(D,N,P,X) is made |
| ︙ | ︙ | |||
9195 9196 9197 9198 9199 9200 9201 9202 9203 9204 9205 9206 9207 9208 9209 9210 9211 9212 |
** ^The current value of the requested parameter is written into *pCur
** and the highest instantaneous value is written into *pHiwtr. ^If
** the resetFlg is true, then the highest instantaneous value is
** reset back down to the current value.
**
** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a
** non-zero [error code] on failure.
**
** See also: [sqlite3_status()] and [sqlite3_stmt_status()].
*/
SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
/*
** CAPI3REF: Status Parameters for database connections
** KEYWORDS: {SQLITE_DBSTATUS options}
**
** These constants are the available integer "verbs" that can be passed as
** the second argument to the [sqlite3_db_status()] interface.
| > > > > > > > > > | 9237 9238 9239 9240 9241 9242 9243 9244 9245 9246 9247 9248 9249 9250 9251 9252 9253 9254 9255 9256 9257 9258 9259 9260 9261 9262 9263 |
** ^The current value of the requested parameter is written into *pCur
** and the highest instantaneous value is written into *pHiwtr. ^If
** the resetFlg is true, then the highest instantaneous value is
** reset back down to the current value.
**
** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a
** non-zero [error code] on failure.
**
** ^The sqlite3_db_status64(D,O,C,H,R) routine works exactly the same
** way as sqlite3_db_status(D,O,C,H,R) routine except that the C and H
** parameters are pointer to 64-bit integers (type: sqlite3_int64) instead
** of pointers to 32-bit integers, which allows larger status values
** to be returned. If a status value exceeds 2,147,483,647 then
** sqlite3_db_status() will truncate the value whereas sqlite3_db_status64()
** will return the full value.
**
** See also: [sqlite3_status()] and [sqlite3_stmt_status()].
*/
SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
SQLITE_API int sqlite3_db_status64(sqlite3*,int,sqlite3_int64*,sqlite3_int64*,int);
/*
** CAPI3REF: Status Parameters for database connections
** KEYWORDS: {SQLITE_DBSTATUS options}
**
** These constants are the available integer "verbs" that can be passed as
** the second argument to the [sqlite3_db_status()] interface.
|
| ︙ | ︙ | |||
9295 9296 9297 9298 9299 9300 9301 9302 9303 9304 9305 9306 9307 9308 9309 9310 9311 9312 9313 9314 9315 9316 9317 9318 9319 9320 9321 9322 9323 9324 9325 9326 9327 9328 9329 9330 9331 9332 | ** been written to disk. Specifically, the number of pages written to the ** wal file in wal mode databases, or the number of pages written to the ** database file in rollback mode databases. Any pages written as part of ** transaction rollback or database recovery operations are not included. ** If an IO or other error occurs while writing a page to disk, the effect ** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The ** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0. ** </dd> ** ** [[SQLITE_DBSTATUS_CACHE_SPILL]] ^(<dt>SQLITE_DBSTATUS_CACHE_SPILL</dt> ** <dd>This parameter returns the number of dirty cache entries that have ** been written to disk in the middle of a transaction due to the page ** cache overflowing. Transactions are more efficient if they are written ** to disk all at once. When pages spill mid-transaction, that introduces ** additional overhead. This parameter can be used to help identify ** inefficiencies that can be resolved by increasing the cache size. ** </dd> ** ** [[SQLITE_DBSTATUS_DEFERRED_FKS]] ^(<dt>SQLITE_DBSTATUS_DEFERRED_FKS</dt> ** <dd>This parameter returns zero for the current value if and only if ** all foreign key constraints (deferred or immediate) have been ** resolved.)^ ^The highwater mark is always 0. ** </dd> ** </dl> */ #define SQLITE_DBSTATUS_LOOKASIDE_USED 0 #define SQLITE_DBSTATUS_CACHE_USED 1 #define SQLITE_DBSTATUS_SCHEMA_USED 2 #define SQLITE_DBSTATUS_STMT_USED 3 #define SQLITE_DBSTATUS_LOOKASIDE_HIT 4 #define SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE 5 #define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL 6 #define SQLITE_DBSTATUS_CACHE_HIT 7 #define SQLITE_DBSTATUS_CACHE_MISS 8 #define SQLITE_DBSTATUS_CACHE_WRITE 9 #define SQLITE_DBSTATUS_DEFERRED_FKS 10 #define SQLITE_DBSTATUS_CACHE_USED_SHARED 11 #define SQLITE_DBSTATUS_CACHE_SPILL 12 | > > > > > > > > > > > > > > > > > | | 9346 9347 9348 9349 9350 9351 9352 9353 9354 9355 9356 9357 9358 9359 9360 9361 9362 9363 9364 9365 9366 9367 9368 9369 9370 9371 9372 9373 9374 9375 9376 9377 9378 9379 9380 9381 9382 9383 9384 9385 9386 9387 9388 9389 9390 9391 9392 9393 9394 9395 9396 9397 9398 9399 9400 9401 9402 9403 9404 9405 9406 9407 9408 | ** been written to disk. Specifically, the number of pages written to the ** wal file in wal mode databases, or the number of pages written to the ** database file in rollback mode databases. Any pages written as part of ** transaction rollback or database recovery operations are not included. ** If an IO or other error occurs while writing a page to disk, the effect ** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The ** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0. ** <p> ** ^(There is overlap between the quantities measured by this parameter ** (SQLITE_DBSTATUS_CACHE_WRITE) and SQLITE_DBSTATUS_TEMPBUF_SPILL. ** Resetting one will reduce the other.)^ ** </dd> ** ** [[SQLITE_DBSTATUS_CACHE_SPILL]] ^(<dt>SQLITE_DBSTATUS_CACHE_SPILL</dt> ** <dd>This parameter returns the number of dirty cache entries that have ** been written to disk in the middle of a transaction due to the page ** cache overflowing. Transactions are more efficient if they are written ** to disk all at once. When pages spill mid-transaction, that introduces ** additional overhead. This parameter can be used to help identify ** inefficiencies that can be resolved by increasing the cache size. ** </dd> ** ** [[SQLITE_DBSTATUS_DEFERRED_FKS]] ^(<dt>SQLITE_DBSTATUS_DEFERRED_FKS</dt> ** <dd>This parameter returns zero for the current value if and only if ** all foreign key constraints (deferred or immediate) have been ** resolved.)^ ^The highwater mark is always 0. ** ** [[SQLITE_DBSTATUS_TEMPBUF_SPILL] ^(<dt>SQLITE_DBSTATUS_TEMPBUF_SPILL</dt> ** <dd>^(This parameter returns the number of bytes written to temporary ** files on disk that could have been kept in memory had sufficient memory ** been available. This value includes writes to intermediate tables that ** are part of complex queries, external sorts that spill to disk, and ** writes to TEMP tables.)^ ** ^The highwater mark is always 0. ** <p> ** ^(There is overlap between the quantities measured by this parameter ** (SQLITE_DBSTATUS_TEMPBUF_SPILL) and SQLITE_DBSTATUS_CACHE_WRITE. ** Resetting one will reduce the other.)^ ** </dd> ** </dl> */ #define SQLITE_DBSTATUS_LOOKASIDE_USED 0 #define SQLITE_DBSTATUS_CACHE_USED 1 #define SQLITE_DBSTATUS_SCHEMA_USED 2 #define SQLITE_DBSTATUS_STMT_USED 3 #define SQLITE_DBSTATUS_LOOKASIDE_HIT 4 #define SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE 5 #define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL 6 #define SQLITE_DBSTATUS_CACHE_HIT 7 #define SQLITE_DBSTATUS_CACHE_MISS 8 #define SQLITE_DBSTATUS_CACHE_WRITE 9 #define SQLITE_DBSTATUS_DEFERRED_FKS 10 #define SQLITE_DBSTATUS_CACHE_USED_SHARED 11 #define SQLITE_DBSTATUS_CACHE_SPILL 12 #define SQLITE_DBSTATUS_TEMPBUF_SPILL 13 #define SQLITE_DBSTATUS_MAX 13 /* Largest defined DBSTATUS */ /* ** CAPI3REF: Prepared Statement Status ** METHOD: sqlite3_stmt ** ** ^(Each prepared statement maintains various |
| ︙ | ︙ | |||
10091 10092 10093 10094 10095 10096 10097 | ** is a copy of the third parameter passed to sqlite3_wal_hook() when ** registering the callback. ^The second is a copy of the database handle. ** ^The third parameter is the name of the database that was written to - ** either "main" or the name of an [ATTACH]-ed database. ^The fourth parameter ** is the number of pages currently in the write-ahead log file, ** including those that were just committed. ** | | | | | > > > | > | | | > > > > > > > > > | | > | | | 10159 10160 10161 10162 10163 10164 10165 10166 10167 10168 10169 10170 10171 10172 10173 10174 10175 10176 10177 10178 10179 10180 10181 10182 10183 10184 10185 10186 10187 10188 10189 10190 10191 10192 10193 10194 10195 10196 10197 10198 10199 10200 10201 10202 10203 10204 10205 10206 10207 10208 10209 10210 10211 10212 10213 10214 10215 10216 10217 10218 10219 10220 10221 10222 10223 10224 10225 10226 10227 10228 10229 10230 10231 10232 10233 10234 10235 10236 | ** is a copy of the third parameter passed to sqlite3_wal_hook() when ** registering the callback. ^The second is a copy of the database handle. ** ^The third parameter is the name of the database that was written to - ** either "main" or the name of an [ATTACH]-ed database. ^The fourth parameter ** is the number of pages currently in the write-ahead log file, ** including those that were just committed. ** ** ^The callback function should normally return [SQLITE_OK]. ^If an error ** code is returned, that error will propagate back up through the ** SQLite code base to cause the statement that provoked the callback ** to report an error, though the commit will have still occurred. If the ** callback returns [SQLITE_ROW] or [SQLITE_DONE], or if it returns a value ** that does not correspond to any valid SQLite error code, the results ** are undefined. ** ** ^A single database handle may have at most a single write-ahead log ** callback registered at one time. ^Calling [sqlite3_wal_hook()] ** replaces the default behavior or previously registered write-ahead ** log callback. ** ** ^The return value is a copy of the third parameter from the ** previous call, if any, or 0. ** ** ^The [sqlite3_wal_autocheckpoint()] interface and the ** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and ** will overwrite any prior [sqlite3_wal_hook()] settings. ** ** ^If a write-ahead log callback is set using this function then ** [sqlite3_wal_checkpoint_v2()] or [PRAGMA wal_checkpoint] ** should be invoked periodically to keep the write-ahead log file ** from growing without bound. ** ** ^Passing a NULL pointer for the callback disables automatic ** checkpointing entirely. To re-enable the default behavior, call ** sqlite3_wal_autocheckpoint(db,1000) or use [PRAGMA wal_checkpoint]. */ SQLITE_API void *sqlite3_wal_hook( sqlite3*, int(*)(void *,sqlite3*,const char*,int), void* ); /* ** CAPI3REF: Configure an auto-checkpoint ** METHOD: sqlite3 ** ** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around ** [sqlite3_wal_hook()] that causes any database on [database connection] D ** to automatically [checkpoint] ** after committing a transaction if there are N or ** more frames in the [write-ahead log] file. ^Passing zero or ** a negative value as the N parameter disables automatic ** checkpoints entirely. ** ** ^The callback registered by this function replaces any existing callback ** registered using [sqlite3_wal_hook()]. ^Likewise, registering a callback ** using [sqlite3_wal_hook()] disables the automatic checkpoint mechanism ** configured by this function. ** ** ^The [wal_autocheckpoint pragma] can be used to invoke this interface ** from SQL. ** ** ^Checkpoints initiated by this mechanism are ** [sqlite3_wal_checkpoint_v2|PASSIVE]. ** ** ^Every new [database connection] defaults to having the auto-checkpoint ** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT] ** pages. ** ** ^The use of this interface is only necessary if the default setting ** is found to be suboptimal for a particular application. */ SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N); /* ** CAPI3REF: Checkpoint a database ** METHOD: sqlite3 ** |
| ︙ | ︙ | |||
10205 10206 10207 10208 10209 10210 10211 10212 10213 10214 10215 10216 10217 10218 | ** ^Like SQLITE_CHECKPOINT_FULL, this mode blocks new ** database writer attempts while it is pending, but does not impede readers. ** ** <dt>SQLITE_CHECKPOINT_TRUNCATE<dd> ** ^This mode works the same way as SQLITE_CHECKPOINT_RESTART with the ** addition that it also truncates the log file to zero bytes just prior ** to a successful return. ** </dl> ** ** ^If pnLog is not NULL, then *pnLog is set to the total number of frames in ** the log file or to -1 if the checkpoint could not run because ** of an error or because the database is not in [WAL mode]. ^If pnCkpt is not ** NULL,then *pnCkpt is set to the total number of checkpointed frames in the ** log file (including any that were already checkpointed before the function | > > > > > | 10287 10288 10289 10290 10291 10292 10293 10294 10295 10296 10297 10298 10299 10300 10301 10302 10303 10304 10305 | ** ^Like SQLITE_CHECKPOINT_FULL, this mode blocks new ** database writer attempts while it is pending, but does not impede readers. ** ** <dt>SQLITE_CHECKPOINT_TRUNCATE<dd> ** ^This mode works the same way as SQLITE_CHECKPOINT_RESTART with the ** addition that it also truncates the log file to zero bytes just prior ** to a successful return. ** ** <dt>SQLITE_CHECKPOINT_NOOP<dd> ** ^This mode always checkpoints zero frames. The only reason to invoke ** a NOOP checkpoint is to access the values returned by ** sqlite3_wal_checkpoint_v2() via output parameters *pnLog and *pnCkpt. ** </dl> ** ** ^If pnLog is not NULL, then *pnLog is set to the total number of frames in ** the log file or to -1 if the checkpoint could not run because ** of an error or because the database is not in [WAL mode]. ^If pnCkpt is not ** NULL,then *pnCkpt is set to the total number of checkpointed frames in the ** log file (including any that were already checkpointed before the function |
| ︙ | ︙ | |||
10275 10276 10277 10278 10279 10280 10281 10282 10283 10284 10285 10286 10287 10288 |
** KEYWORDS: {checkpoint mode}
**
** These constants define all valid values for the "checkpoint mode" passed
** as the third parameter to the [sqlite3_wal_checkpoint_v2()] interface.
** See the [sqlite3_wal_checkpoint_v2()] documentation for details on the
** meaning of each of these checkpoint modes.
*/
#define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */
#define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */
#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */
#define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */
/*
** CAPI3REF: Virtual Table Interface Configuration
| > | 10362 10363 10364 10365 10366 10367 10368 10369 10370 10371 10372 10373 10374 10375 10376 |
** KEYWORDS: {checkpoint mode}
**
** These constants define all valid values for the "checkpoint mode" passed
** as the third parameter to the [sqlite3_wal_checkpoint_v2()] interface.
** See the [sqlite3_wal_checkpoint_v2()] documentation for details on the
** meaning of each of these checkpoint modes.
*/
#define SQLITE_CHECKPOINT_NOOP -1 /* Do no work at all */
#define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */
#define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */
#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */
#define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */
/*
** CAPI3REF: Virtual Table Interface Configuration
|
| ︙ | ︙ | |||
11102 11103 11104 11105 11106 11107 11108 | ** The [sqlite3_snapshot] object returned from a successful call to ** [sqlite3_snapshot_get()] must be freed using [sqlite3_snapshot_free()] ** to avoid a memory leak. ** ** The [sqlite3_snapshot_get()] interface is only available when the ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used. */ | | | 11190 11191 11192 11193 11194 11195 11196 11197 11198 11199 11200 11201 11202 11203 11204 | ** The [sqlite3_snapshot] object returned from a successful call to ** [sqlite3_snapshot_get()] must be freed using [sqlite3_snapshot_free()] ** to avoid a memory leak. ** ** The [sqlite3_snapshot_get()] interface is only available when the ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used. */ SQLITE_API int sqlite3_snapshot_get( sqlite3 *db, const char *zSchema, sqlite3_snapshot **ppSnapshot ); /* ** CAPI3REF: Start a read transaction on an historical snapshot |
| ︙ | ︙ | |||
11151 11152 11153 11154 11155 11156 11157 | ** after the most recent I/O on the database connection.)^ ** (Hint: Run "[PRAGMA application_id]" against a newly opened ** database connection in order to make it ready to use snapshots.) ** ** The [sqlite3_snapshot_open()] interface is only available when the ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used. */ | | | | 11239 11240 11241 11242 11243 11244 11245 11246 11247 11248 11249 11250 11251 11252 11253 11254 11255 11256 11257 11258 11259 11260 11261 11262 11263 11264 11265 11266 11267 11268 11269 11270 | ** after the most recent I/O on the database connection.)^ ** (Hint: Run "[PRAGMA application_id]" against a newly opened ** database connection in order to make it ready to use snapshots.) ** ** The [sqlite3_snapshot_open()] interface is only available when the ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used. */ SQLITE_API int sqlite3_snapshot_open( sqlite3 *db, const char *zSchema, sqlite3_snapshot *pSnapshot ); /* ** CAPI3REF: Destroy a snapshot ** DESTRUCTOR: sqlite3_snapshot ** ** ^The [sqlite3_snapshot_free(P)] interface destroys [sqlite3_snapshot] P. ** The application must eventually free every [sqlite3_snapshot] object ** using this routine to avoid a memory leak. ** ** The [sqlite3_snapshot_free()] interface is only available when the ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used. */ SQLITE_API void sqlite3_snapshot_free(sqlite3_snapshot*); /* ** CAPI3REF: Compare the ages of two snapshot handles. ** METHOD: sqlite3_snapshot ** ** The sqlite3_snapshot_cmp(P1, P2) interface is used to compare the ages ** of two valid snapshot handles. |
| ︙ | ︙ | |||
11195 11196 11197 11198 11199 11200 11201 | ** Otherwise, this API returns a negative value if P1 refers to an older ** snapshot than P2, zero if the two handles refer to the same database ** snapshot, and a positive value if P1 is a newer snapshot than P2. ** ** This interface is only available if SQLite is compiled with the ** [SQLITE_ENABLE_SNAPSHOT] option. */ | | | 11283 11284 11285 11286 11287 11288 11289 11290 11291 11292 11293 11294 11295 11296 11297 | ** Otherwise, this API returns a negative value if P1 refers to an older ** snapshot than P2, zero if the two handles refer to the same database ** snapshot, and a positive value if P1 is a newer snapshot than P2. ** ** This interface is only available if SQLite is compiled with the ** [SQLITE_ENABLE_SNAPSHOT] option. */ SQLITE_API int sqlite3_snapshot_cmp( sqlite3_snapshot *p1, sqlite3_snapshot *p2 ); /* ** CAPI3REF: Recover snapshots from a wal file ** METHOD: sqlite3_snapshot |
| ︙ | ︙ | |||
11223 11224 11225 11226 11227 11228 11229 | ** database. ** ** SQLITE_OK is returned if successful, or an SQLite error code otherwise. ** ** This interface is only available if SQLite is compiled with the ** [SQLITE_ENABLE_SNAPSHOT] option. */ | | | 11311 11312 11313 11314 11315 11316 11317 11318 11319 11320 11321 11322 11323 11324 11325 | ** database. ** ** SQLITE_OK is returned if successful, or an SQLite error code otherwise. ** ** This interface is only available if SQLite is compiled with the ** [SQLITE_ENABLE_SNAPSHOT] option. */ SQLITE_API int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb); /* ** CAPI3REF: Serialize a database ** ** The sqlite3_serialize(D,S,P,F) interface returns a pointer to ** memory that is a serialization of the S database on ** [database connection] D. If S is a NULL pointer, the main database is used. |
| ︙ | ︙ | |||
11297 11298 11299 11300 11301 11302 11303 | #define SQLITE_SERIALIZE_NOCOPY 0x001 /* Do no memory allocations */ /* ** CAPI3REF: Deserialize a database ** ** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the ** [database connection] D to disconnect from database S and then | | > | | | | | | 11385 11386 11387 11388 11389 11390 11391 11392 11393 11394 11395 11396 11397 11398 11399 11400 11401 11402 11403 11404 11405 | #define SQLITE_SERIALIZE_NOCOPY 0x001 /* Do no memory allocations */ /* ** CAPI3REF: Deserialize a database ** ** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the ** [database connection] D to disconnect from database S and then ** reopen S as an in-memory database based on the serialization ** contained in P. If S is a NULL pointer, the main database is ** used. The serialized database P is N bytes in size. M is the size ** of the buffer P, which might be larger than N. If M is larger than ** N, and the SQLITE_DESERIALIZE_READONLY bit is not set in F, then ** SQLite is permitted to add content to the in-memory database as ** long as the total size does not exceed M bytes. ** ** If the SQLITE_DESERIALIZE_FREEONCLOSE bit is set in F, then SQLite will ** invoke sqlite3_free() on the serialization buffer when the database ** connection closes. If the SQLITE_DESERIALIZE_RESIZEABLE bit is set, then ** SQLite will try to increase the buffer size using sqlite3_realloc64() ** if writes on the database cause it to grow larger than M bytes. ** |
| ︙ | ︙ | |||
11369 11370 11371 11372 11373 11374 11375 11376 11377 11378 11379 11380 11381 11382 | ** The SQLITE_DESERIALIZE_READONLY flag means that the deserialized database ** should be treated as read-only. */ #define SQLITE_DESERIALIZE_FREEONCLOSE 1 /* Call sqlite3_free() on close */ #define SQLITE_DESERIALIZE_RESIZEABLE 2 /* Resize using sqlite3_realloc64() */ #define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */ /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ #ifdef SQLITE_OMIT_FLOATING_POINT # undef double #endif | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 11458 11459 11460 11461 11462 11463 11464 11465 11466 11467 11468 11469 11470 11471 11472 11473 11474 11475 11476 11477 11478 11479 11480 11481 11482 11483 11484 11485 11486 11487 11488 11489 11490 11491 11492 11493 11494 11495 11496 11497 11498 11499 11500 11501 11502 11503 11504 11505 11506 11507 11508 11509 11510 11511 11512 11513 11514 11515 11516 11517 | ** The SQLITE_DESERIALIZE_READONLY flag means that the deserialized database ** should be treated as read-only. */ #define SQLITE_DESERIALIZE_FREEONCLOSE 1 /* Call sqlite3_free() on close */ #define SQLITE_DESERIALIZE_RESIZEABLE 2 /* Resize using sqlite3_realloc64() */ #define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */ /* ** CAPI3REF: Bind array values to the CARRAY table-valued function ** ** The sqlite3_carray_bind(S,I,P,N,F,X) interface binds an array value to ** one of the first argument of the [carray() table-valued function]. The ** S parameter is a pointer to the [prepared statement] that uses the carray() ** functions. I is the parameter index to be bound. P is a pointer to the ** array to be bound, and N is the number of eements in the array. The ** F argument is one of constants [SQLITE_CARRAY_INT32], [SQLITE_CARRAY_INT64], ** [SQLITE_CARRAY_DOUBLE], [SQLITE_CARRAY_TEXT], or [SQLITE_CARRAY_BLOB] to ** indicate the datatype of the array being bound. The X argument is not a ** NULL pointer, then SQLite will invoke the function X on the P parameter ** after it has finished using P. */ SQLITE_API SQLITE_API int sqlite3_carray_bind( sqlite3_stmt *pStmt, /* Statement to be bound */ int i, /* Parameter index */ void *aData, /* Pointer to array data */ int nData, /* Number of data elements */ int mFlags, /* CARRAY flags */ void (*xDel)(void*) /* Destructor for aData */ ); /* ** CAPI3REF: Datatypes for the CARRAY table-valued funtion ** ** The fifth argument to the [sqlite3_carray_bind()] interface musts be ** one of the following constants, to specify the datatype of the array ** that is being bound into the [carray table-valued function]. */ #define SQLITE_CARRAY_INT32 0 /* Data is 32-bit signed integers */ #define SQLITE_CARRAY_INT64 1 /* Data is 64-bit signed integers */ #define SQLITE_CARRAY_DOUBLE 2 /* Data is doubles */ #define SQLITE_CARRAY_TEXT 3 /* Data is char* */ #define SQLITE_CARRAY_BLOB 4 /* Data is struct iovec */ /* ** Versions of the above #defines that omit the initial SQLITE_, for ** legacy compatibility. */ #define CARRAY_INT32 0 /* Data is 32-bit signed integers */ #define CARRAY_INT64 1 /* Data is 64-bit signed integers */ #define CARRAY_DOUBLE 2 /* Data is doubles */ #define CARRAY_TEXT 3 /* Data is char* */ #define CARRAY_BLOB 4 /* Data is struct iovec */ /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ #ifdef SQLITE_OMIT_FLOATING_POINT # undef double #endif |
| ︙ | ︙ | |||
12627 12628 12629 12630 12631 12632 12633 12634 12635 12636 12637 12638 12639 12640 | /* ** CAPI3REF: Apply A Changeset To A Database ** ** Apply a changeset or patchset to a database. These functions attempt to ** update the "main" database attached to handle db with the changes found in ** the changeset passed via the second and third arguments. ** ** The fourth argument (xFilter) passed to these functions is the "filter ** callback". This may be passed NULL, in which case all changes in the ** changeset are applied to the database. For sqlite3changeset_apply() and ** sqlite3_changeset_apply_v2(), if it is not NULL, then it is invoked once ** for each table affected by at least one change in the changeset. In this ** case the table name is passed as the second argument, and a copy of | > > > > > > > > > | 12762 12763 12764 12765 12766 12767 12768 12769 12770 12771 12772 12773 12774 12775 12776 12777 12778 12779 12780 12781 12782 12783 12784 | /* ** CAPI3REF: Apply A Changeset To A Database ** ** Apply a changeset or patchset to a database. These functions attempt to ** update the "main" database attached to handle db with the changes found in ** the changeset passed via the second and third arguments. ** ** All changes made by these functions are enclosed in a savepoint transaction. ** If any other error (aside from a constraint failure when attempting to ** write to the target database) occurs, then the savepoint transaction is ** rolled back, restoring the target database to its original state, and an ** SQLite error code returned. Additionally, starting with version 3.51.0, ** an error code and error message that may be accessed using the ** [sqlite3_errcode()] and [sqlite3_errmsg()] APIs are left in the database ** handle. ** ** The fourth argument (xFilter) passed to these functions is the "filter ** callback". This may be passed NULL, in which case all changes in the ** changeset are applied to the database. For sqlite3changeset_apply() and ** sqlite3_changeset_apply_v2(), if it is not NULL, then it is invoked once ** for each table affected by at least one change in the changeset. In this ** case the table name is passed as the second argument, and a copy of |
| ︙ | ︙ | |||
12765 12766 12767 12768 12769 12770 12771 | ** </dl> ** ** It is safe to execute SQL statements, including those that write to the ** table that the callback related to, from within the xConflict callback. ** This can be used to further customize the application's conflict ** resolution strategy. ** | < < < < < < | 12909 12910 12911 12912 12913 12914 12915 12916 12917 12918 12919 12920 12921 12922 | ** </dl> ** ** It is safe to execute SQL statements, including those that write to the ** table that the callback related to, from within the xConflict callback. ** This can be used to further customize the application's conflict ** resolution strategy. ** ** If the output parameters (ppRebase) and (pnRebase) are non-NULL and ** the input is a changeset (not a patchset), then sqlite3changeset_apply_v2() ** may set (*ppRebase) to point to a "rebase" that may be used with the ** sqlite3_rebaser APIs buffer before returning. In this case (*pnRebase) ** is set to the size of the buffer in bytes. It is the responsibility of the ** caller to eventually free any such buffer using sqlite3_free(). The buffer ** is only allocated and populated if one or more conflicts were encountered |
| ︙ | ︙ | |||
14349 14350 14351 14352 14353 14354 14355 | #endif /* ** Maximum number of pages in one database file. ** ** This is really just the default value for the max_page_count pragma. | | | 14487 14488 14489 14490 14491 14492 14493 14494 14495 14496 14497 14498 14499 14500 14501 | #endif /* ** Maximum number of pages in one database file. ** ** This is really just the default value for the max_page_count pragma. ** This value can be lowered (or raised) at run-time using the ** max_page_count macro. */ #ifndef SQLITE_MAX_PAGE_COUNT # define SQLITE_MAX_PAGE_COUNT 0xfffffffe /* 4294967294 */ #endif /* |
| ︙ | ︙ | |||
15945 15946 15947 15948 15949 15950 15951 | ** If none of the macros are initially defined, then select either ** SQLITE_OS_UNIX or SQLITE_OS_WIN depending on the target platform. ** ** If SQLITE_OS_OTHER=1 is specified at compile-time, then the application ** must provide its own VFS implementation together with sqlite3_os_init() ** and sqlite3_os_end() routines. */ | | | | 16083 16084 16085 16086 16087 16088 16089 16090 16091 16092 16093 16094 16095 16096 16097 16098 |
** If none of the macros are initially defined, then select either
** SQLITE_OS_UNIX or SQLITE_OS_WIN depending on the target platform.
**
** If SQLITE_OS_OTHER=1 is specified at compile-time, then the application
** must provide its own VFS implementation together with sqlite3_os_init()
** and sqlite3_os_end() routines.
*/
#if SQLITE_OS_KV+1<=1 && SQLITE_OS_OTHER+1<=1 && \
SQLITE_OS_WIN+1<=1 && SQLITE_OS_UNIX+1<=1
# if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__) || \
defined(__MINGW32__) || defined(__BORLANDC__)
# define SQLITE_OS_WIN 1
# define SQLITE_OS_UNIX 0
# else
# define SQLITE_OS_WIN 0
# define SQLITE_OS_UNIX 1
|
| ︙ | ︙ | |||
17448 17449 17450 17451 17452 17453 17454 17455 17456 17457 17458 17459 17460 17461 | SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe*, int, u8); SQLITE_PRIVATE void sqlite3VdbeSetVarmask(Vdbe*, int); #ifndef SQLITE_OMIT_TRACE SQLITE_PRIVATE char *sqlite3VdbeExpandSql(Vdbe*, const char*); #endif SQLITE_PRIVATE int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*); SQLITE_PRIVATE int sqlite3BlobCompare(const Mem*, const Mem*); SQLITE_PRIVATE void sqlite3VdbeRecordUnpack(int,const void*,UnpackedRecord*); SQLITE_PRIVATE int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*); SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(int, const void *, UnpackedRecord *, int); SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo*); typedef int (*RecordCompare)(int,const void*,UnpackedRecord*); | > > > | 17586 17587 17588 17589 17590 17591 17592 17593 17594 17595 17596 17597 17598 17599 17600 17601 17602 | SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetBoundValue(Vdbe*, int, u8); SQLITE_PRIVATE void sqlite3VdbeSetVarmask(Vdbe*, int); #ifndef SQLITE_OMIT_TRACE SQLITE_PRIVATE char *sqlite3VdbeExpandSql(Vdbe*, const char*); #endif SQLITE_PRIVATE int sqlite3MemCompare(const Mem*, const Mem*, const CollSeq*); SQLITE_PRIVATE int sqlite3BlobCompare(const Mem*, const Mem*); #ifdef SQLITE_ENABLE_PERCENTILE SQLITE_PRIVATE const char *sqlite3VdbeFuncName(const sqlite3_context*); #endif SQLITE_PRIVATE void sqlite3VdbeRecordUnpack(int,const void*,UnpackedRecord*); SQLITE_PRIVATE int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*); SQLITE_PRIVATE int sqlite3VdbeRecordCompareWithSkip(int, const void *, UnpackedRecord *, int); SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeAllocUnpackedRecord(KeyInfo*); typedef int (*RecordCompare)(int,const void*,UnpackedRecord*); |
| ︙ | ︙ | |||
18120 18121 18122 18123 18124 18125 18126 |
int aLimit[SQLITE_N_LIMIT]; /* Limits */
int nMaxSorterMmap; /* Maximum size of regions mapped by sorter */
struct sqlite3InitInfo { /* Information used during initialization */
Pgno newTnum; /* Rootpage of table being initialized */
u8 iDb; /* Which db file is being initialized */
u8 busy; /* TRUE if currently initializing */
unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */
| | | 18261 18262 18263 18264 18265 18266 18267 18268 18269 18270 18271 18272 18273 18274 18275 |
int aLimit[SQLITE_N_LIMIT]; /* Limits */
int nMaxSorterMmap; /* Maximum size of regions mapped by sorter */
struct sqlite3InitInfo { /* Information used during initialization */
Pgno newTnum; /* Rootpage of table being initialized */
u8 iDb; /* Which db file is being initialized */
u8 busy; /* TRUE if currently initializing */
unsigned orphanTrigger : 1; /* Last statement is orphaned TEMP trigger */
unsigned imposterTable : 2; /* Building an imposter table */
unsigned reopenMemdb : 1; /* ATTACH is really a reopen using MemDB */
const char **azInit; /* "type", "name", and "tbl_name" columns */
} init;
int nVdbeActive; /* Number of VDBEs currently running */
int nVdbeRead; /* Number of active VDBEs that read or write */
int nVdbeWrite; /* Number of active VDBEs that read and write */
int nVdbeExec; /* Number of nested calls to VdbeExec() */
|
| ︙ | ︙ | |||
18203 18204 18205 18206 18207 18208 18209 18210 18211 18212 18213 18214 18215 18216 | #endif int nSavepoint; /* Number of non-transaction savepoints */ int nStatement; /* Number of nested statement-transactions */ i64 nDeferredCons; /* Net deferred constraints this transaction. */ i64 nDeferredImmCons; /* Net deferred immediate constraints */ int *pnBytesFreed; /* If not NULL, increment this in DbFree() */ DbClientData *pDbData; /* sqlite3_set_clientdata() content */ #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY /* The following variables are all protected by the STATIC_MAIN ** mutex, not by sqlite3.mutex. They are used by code in notify.c. ** ** When X.pUnlockConnection==Y, that means that X is waiting for Y to ** unlock so that it can proceed. ** | > | 18344 18345 18346 18347 18348 18349 18350 18351 18352 18353 18354 18355 18356 18357 18358 | #endif int nSavepoint; /* Number of non-transaction savepoints */ int nStatement; /* Number of nested statement-transactions */ i64 nDeferredCons; /* Net deferred constraints this transaction. */ i64 nDeferredImmCons; /* Net deferred immediate constraints */ int *pnBytesFreed; /* If not NULL, increment this in DbFree() */ DbClientData *pDbData; /* sqlite3_set_clientdata() content */ u64 nSpill; /* TEMP content spilled to disk */ #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY /* The following variables are all protected by the STATIC_MAIN ** mutex, not by sqlite3.mutex. They are used by code in notify.c. ** ** When X.pUnlockConnection==Y, that means that X is waiting for Y to ** unlock so that it can proceed. ** |
| ︙ | ︙ | |||
18913 18914 18915 18916 18917 18918 18919 18920 18921 18922 18923 18924 18925 18926 | #define TF_OOOHidden 0x00000400 /* Out-of-Order hidden columns */ #define TF_HasNotNull 0x00000800 /* Contains NOT NULL constraints */ #define TF_Shadow 0x00001000 /* True for a shadow table */ #define TF_HasStat4 0x00002000 /* STAT4 info available for this table */ #define TF_Ephemeral 0x00004000 /* An ephemeral table */ #define TF_Eponymous 0x00008000 /* An eponymous virtual table */ #define TF_Strict 0x00010000 /* STRICT mode */ /* ** Allowed values for Table.eTabType */ #define TABTYP_NORM 0 /* Ordinary table */ #define TABTYP_VTAB 1 /* Virtual table */ #define TABTYP_VIEW 2 /* A view */ | > | 19055 19056 19057 19058 19059 19060 19061 19062 19063 19064 19065 19066 19067 19068 19069 | #define TF_OOOHidden 0x00000400 /* Out-of-Order hidden columns */ #define TF_HasNotNull 0x00000800 /* Contains NOT NULL constraints */ #define TF_Shadow 0x00001000 /* True for a shadow table */ #define TF_HasStat4 0x00002000 /* STAT4 info available for this table */ #define TF_Ephemeral 0x00004000 /* An ephemeral table */ #define TF_Eponymous 0x00008000 /* An eponymous virtual table */ #define TF_Strict 0x00010000 /* STRICT mode */ #define TF_Imposter 0x00020000 /* An imposter table */ /* ** Allowed values for Table.eTabType */ #define TABTYP_NORM 0 /* Ordinary table */ #define TABTYP_VTAB 1 /* Virtual table */ #define TABTYP_VIEW 2 /* A view */ |
| ︙ | ︙ | |||
19501 19502 19503 19504 19505 19506 19507 19508 19509 19510 19511 19512 19513 19514 |
int iOfst; /* else: start of token from start of statement */
} w;
AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
union {
Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL
** for a column of an index on an expression */
Window *pWin; /* EP_WinFunc: Window/Filter defn for a function */
struct { /* TK_IN, TK_SELECT, and TK_EXISTS */
int iAddr; /* Subroutine entry address */
int regReturn; /* Register used to hold return address */
} sub;
} y;
};
| > | 19644 19645 19646 19647 19648 19649 19650 19651 19652 19653 19654 19655 19656 19657 19658 |
int iOfst; /* else: start of token from start of statement */
} w;
AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
union {
Table *pTab; /* TK_COLUMN: Table containing column. Can be NULL
** for a column of an index on an expression */
Window *pWin; /* EP_WinFunc: Window/Filter defn for a function */
int nReg; /* TK_NULLS: Number of registers to NULL out */
struct { /* TK_IN, TK_SELECT, and TK_EXISTS */
int iAddr; /* Subroutine entry address */
int regReturn; /* Register used to hold return address */
} sub;
} y;
};
|
| ︙ | ︙ | |||
20078 20079 20080 20081 20082 20083 20084 20085 20086 20087 20088 20089 20090 20091 |
#define SF_UFSrcCheck 0x0800000 /* Check pSrc as required by UPDATE...FROM */
#define SF_PushDown 0x1000000 /* Modified by WHERE-clause push-down opt */
#define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */
#define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */
#define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */
#define SF_UpdateFrom 0x10000000 /* Query originates with UPDATE FROM */
#define SF_Correlated 0x20000000 /* True if references the outer context */
/* True if SrcItem X is a subquery that has SF_NestedFrom */
#define IsNestedFrom(X) \
((X)->fg.isSubquery && \
((X)->u4.pSubq->pSelect->selFlags&SF_NestedFrom)!=0)
/*
| > | 20222 20223 20224 20225 20226 20227 20228 20229 20230 20231 20232 20233 20234 20235 20236 |
#define SF_UFSrcCheck 0x0800000 /* Check pSrc as required by UPDATE...FROM */
#define SF_PushDown 0x1000000 /* Modified by WHERE-clause push-down opt */
#define SF_MultiPart 0x2000000 /* Has multiple incompatible PARTITIONs */
#define SF_CopyCte 0x4000000 /* SELECT statement is a copy of a CTE */
#define SF_OrderByReqd 0x8000000 /* The ORDER BY clause may not be omitted */
#define SF_UpdateFrom 0x10000000 /* Query originates with UPDATE FROM */
#define SF_Correlated 0x20000000 /* True if references the outer context */
#define SF_OnToWhere 0x40000000 /* One or more ON clauses moved to WHERE */
/* True if SrcItem X is a subquery that has SF_NestedFrom */
#define IsNestedFrom(X) \
((X)->fg.isSubquery && \
((X)->u4.pSubq->pSelect->selFlags&SF_NestedFrom)!=0)
/*
|
| ︙ | ︙ | |||
20831 20832 20833 20834 20835 20836 20837 20838 20839 20840 20841 20842 20843 20844 |
struct WhereConst *pConst; /* WHERE clause constants */
struct RenameCtx *pRename; /* RENAME COLUMN context */
struct Table *pTab; /* Table of generated column */
struct CoveringIndexCheck *pCovIdxCk; /* Check for covering index */
SrcItem *pSrcItem; /* A single FROM clause item */
DbFixer *pFix; /* See sqlite3FixSelect() */
Mem *aMem; /* See sqlite3BtreeCursorHint() */
} u;
};
/*
** The following structure contains information used by the sqliteFix...
** routines as they walk the parse tree to make database references
** explicit.
| > | 20976 20977 20978 20979 20980 20981 20982 20983 20984 20985 20986 20987 20988 20989 20990 |
struct WhereConst *pConst; /* WHERE clause constants */
struct RenameCtx *pRename; /* RENAME COLUMN context */
struct Table *pTab; /* Table of generated column */
struct CoveringIndexCheck *pCovIdxCk; /* Check for covering index */
SrcItem *pSrcItem; /* A single FROM clause item */
DbFixer *pFix; /* See sqlite3FixSelect() */
Mem *aMem; /* See sqlite3BtreeCursorHint() */
struct CheckOnCtx *pCheckOnCtx; /* See selectCheckOnClauses() */
} u;
};
/*
** The following structure contains information used by the sqliteFix...
** routines as they walk the parse tree to make database references
** explicit.
|
| ︙ | ︙ | |||
21538 21539 21540 21541 21542 21543 21544 21545 21546 21547 21548 21549 21550 21551 | SQLITE_PRIVATE void sqlite3ExprCode(Parse*, Expr*, int); #ifndef SQLITE_OMIT_GENERATED_COLUMNS SQLITE_PRIVATE void sqlite3ExprCodeGeneratedColumn(Parse*, Table*, Column*, int); #endif SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse*, Expr*, int); SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse*, Expr*, int); SQLITE_PRIVATE int sqlite3ExprCodeRunJustOnce(Parse*, Expr*, int); SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse*, Expr*, int*); SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse*, Expr*, int); SQLITE_PRIVATE int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int, u8); #define SQLITE_ECEL_DUP 0x01 /* Deep, not shallow copies */ #define SQLITE_ECEL_FACTOR 0x02 /* Factor out constant terms */ #define SQLITE_ECEL_REF 0x04 /* Use ExprList.u.x.iOrderByCol */ #define SQLITE_ECEL_OMITREF 0x08 /* Omit if ExprList.u.x.iOrderByCol */ | > | 21684 21685 21686 21687 21688 21689 21690 21691 21692 21693 21694 21695 21696 21697 21698 | SQLITE_PRIVATE void sqlite3ExprCode(Parse*, Expr*, int); #ifndef SQLITE_OMIT_GENERATED_COLUMNS SQLITE_PRIVATE void sqlite3ExprCodeGeneratedColumn(Parse*, Table*, Column*, int); #endif SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse*, Expr*, int); SQLITE_PRIVATE void sqlite3ExprCodeFactorable(Parse*, Expr*, int); SQLITE_PRIVATE int sqlite3ExprCodeRunJustOnce(Parse*, Expr*, int); SQLITE_PRIVATE void sqlite3ExprNullRegisterRange(Parse*, int, int); SQLITE_PRIVATE int sqlite3ExprCodeTemp(Parse*, Expr*, int*); SQLITE_PRIVATE int sqlite3ExprCodeTarget(Parse*, Expr*, int); SQLITE_PRIVATE int sqlite3ExprCodeExprList(Parse*, ExprList*, int, int, u8); #define SQLITE_ECEL_DUP 0x01 /* Deep, not shallow copies */ #define SQLITE_ECEL_FACTOR 0x02 /* Factor out constant terms */ #define SQLITE_ECEL_REF 0x04 /* Use ExprList.u.x.iOrderByCol */ #define SQLITE_ECEL_OMITREF 0x08 /* Omit if ExprList.u.x.iOrderByCol */ |
| ︙ | ︙ | |||
21634 21635 21636 21637 21638 21639 21640 | SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum*,sqlite3_value*,int); SQLITE_PRIVATE int sqlite3AppendOneUtf8Character(char*, u32); SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void); SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void); SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void); SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3*); #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) | | > > > > | 21781 21782 21783 21784 21785 21786 21787 21788 21789 21790 21791 21792 21793 21794 21795 21796 21797 21798 21799 21800 21801 21802 21803 21804 | SQLITE_PRIVATE void sqlite3QuoteValue(StrAccum*,sqlite3_value*,int); SQLITE_PRIVATE int sqlite3AppendOneUtf8Character(char*, u32); SQLITE_PRIVATE void sqlite3RegisterBuiltinFunctions(void); SQLITE_PRIVATE void sqlite3RegisterDateTimeFunctions(void); SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void); SQLITE_PRIVATE void sqlite3RegisterPerConnectionBuiltinFunctions(sqlite3*); #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) SQLITE_PRIVATE Module *sqlite3JsonVtabRegister(sqlite3*,const char*); #endif SQLITE_PRIVATE int sqlite3SafetyCheckOk(sqlite3*); SQLITE_PRIVATE int sqlite3SafetyCheckSickOrOk(sqlite3*); SQLITE_PRIVATE void sqlite3ChangeCookie(Parse*, int); SQLITE_PRIVATE With *sqlite3WithDup(sqlite3 *db, With *p); #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_CARRAY) SQLITE_PRIVATE Module *sqlite3CarrayRegister(sqlite3*); #endif #if !defined(SQLITE_OMIT_VIEW) && !defined(SQLITE_OMIT_TRIGGER) SQLITE_PRIVATE void sqlite3MaterializeView(Parse*, Table*, Expr*, ExprList*,Expr*,int); #endif #ifndef SQLITE_OMIT_TRIGGER SQLITE_PRIVATE void sqlite3BeginTrigger(Parse*, Token*,Token*,int,int,IdList*,SrcList*, |
| ︙ | ︙ | |||
22626 22627 22628 22629 22630 22631 22632 22633 22634 22635 22636 22637 22638 22639 | "ENABLE_ATOMIC_WRITE", #endif #ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE "ENABLE_BATCH_ATOMIC_WRITE", #endif #ifdef SQLITE_ENABLE_BYTECODE_VTAB "ENABLE_BYTECODE_VTAB", #endif #ifdef SQLITE_ENABLE_CEROD "ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD), #endif #ifdef SQLITE_ENABLE_COLUMN_METADATA "ENABLE_COLUMN_METADATA", #endif | > > > | 22777 22778 22779 22780 22781 22782 22783 22784 22785 22786 22787 22788 22789 22790 22791 22792 22793 | "ENABLE_ATOMIC_WRITE", #endif #ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE "ENABLE_BATCH_ATOMIC_WRITE", #endif #ifdef SQLITE_ENABLE_BYTECODE_VTAB "ENABLE_BYTECODE_VTAB", #endif #ifdef SQLITE_ENABLE_CARRAY "ENABLE_CARRAY", #endif #ifdef SQLITE_ENABLE_CEROD "ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD), #endif #ifdef SQLITE_ENABLE_COLUMN_METADATA "ENABLE_COLUMN_METADATA", #endif |
| ︙ | ︙ | |||
22716 22717 22718 22719 22720 22721 22722 22723 22724 22725 22726 22727 22728 22729 | "ENABLE_OFFSET_SQL_FUNC", #endif #ifdef SQLITE_ENABLE_ORDERED_SET_AGGREGATES "ENABLE_ORDERED_SET_AGGREGATES", #endif #ifdef SQLITE_ENABLE_OVERSIZE_CELL_CHECK "ENABLE_OVERSIZE_CELL_CHECK", #endif #ifdef SQLITE_ENABLE_PREUPDATE_HOOK "ENABLE_PREUPDATE_HOOK", #endif #ifdef SQLITE_ENABLE_QPSG "ENABLE_QPSG", #endif | > > > | 22870 22871 22872 22873 22874 22875 22876 22877 22878 22879 22880 22881 22882 22883 22884 22885 22886 | "ENABLE_OFFSET_SQL_FUNC", #endif #ifdef SQLITE_ENABLE_ORDERED_SET_AGGREGATES "ENABLE_ORDERED_SET_AGGREGATES", #endif #ifdef SQLITE_ENABLE_OVERSIZE_CELL_CHECK "ENABLE_OVERSIZE_CELL_CHECK", #endif #ifdef SQLITE_ENABLE_PERCENTILE "ENABLE_PERCENTILE", #endif #ifdef SQLITE_ENABLE_PREUPDATE_HOOK "ENABLE_PREUPDATE_HOOK", #endif #ifdef SQLITE_ENABLE_QPSG "ENABLE_QPSG", #endif |
| ︙ | ︙ | |||
24200 24201 24202 24203 24204 24205 24206 | i64 iKey1; /* First key value passed to hook */ i64 iKey2; /* Second key value passed to hook */ Mem oldipk; /* Memory cell holding "old" IPK value */ Mem *aNew; /* Array of new.* values */ Table *pTab; /* Schema object being updated */ Index *pPk; /* PK index if pTab is WITHOUT ROWID */ sqlite3_value **apDflt; /* Array of default values, if required */ | > > | > | 24357 24358 24359 24360 24361 24362 24363 24364 24365 24366 24367 24368 24369 24370 24371 24372 24373 24374 |
i64 iKey1; /* First key value passed to hook */
i64 iKey2; /* Second key value passed to hook */
Mem oldipk; /* Memory cell holding "old" IPK value */
Mem *aNew; /* Array of new.* values */
Table *pTab; /* Schema object being updated */
Index *pPk; /* PK index if pTab is WITHOUT ROWID */
sqlite3_value **apDflt; /* Array of default values, if required */
union {
KeyInfo sKey;
u8 keyinfoSpace[SZ_KEYINFO_0]; /* Space to hold pKeyinfo[0] content */
} uKey;
};
/*
** An instance of this object is used to pass an vector of values into
** OP_VFilter, the xFilter method of a virtual table. The vector is the
** set of values on the right-hand side of an IN constraint.
**
|
| ︙ | ︙ | |||
24364 24365 24366 24367 24368 24369 24370 | #ifdef SQLITE_DEBUG SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe*,Mem*); SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem*); #endif #ifndef SQLITE_OMIT_FOREIGN_KEY | | > | > | 24524 24525 24526 24527 24528 24529 24530 24531 24532 24533 24534 24535 24536 24537 24538 24539 24540 24541 24542 | #ifdef SQLITE_DEBUG SQLITE_PRIVATE void sqlite3VdbeMemAboutToChange(Vdbe*,Mem*); SQLITE_PRIVATE int sqlite3VdbeCheckMemInvariants(Mem*); #endif #ifndef SQLITE_OMIT_FOREIGN_KEY SQLITE_PRIVATE int sqlite3VdbeCheckFkImmediate(Vdbe*); SQLITE_PRIVATE int sqlite3VdbeCheckFkDeferred(Vdbe*); #else # define sqlite3VdbeCheckFkImmediate(p) 0 # define sqlite3VdbeCheckFkDeferred(p) 0 #endif #ifdef SQLITE_DEBUG SQLITE_PRIVATE void sqlite3VdbePrintSql(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeMemPrettyPrint(Mem *pMem, StrAccum *pStr); #endif #ifndef SQLITE_OMIT_UTF16 |
| ︙ | ︙ | |||
24575 24576 24577 24578 24579 24580 24581 | if( pHighwater ) *pHighwater = (int)(db->lookaside.nSlot - nInit); return (int)(db->lookaside.nSlot - (nInit+nFree)); } /* ** Query status information for a single database connection */ | | | | | | | | > | > | 24737 24738 24739 24740 24741 24742 24743 24744 24745 24746 24747 24748 24749 24750 24751 24752 24753 24754 24755 24756 24757 24758 24759 24760 24761 24762 24763 24764 24765 24766 24767 24768 24769 |
if( pHighwater ) *pHighwater = (int)(db->lookaside.nSlot - nInit);
return (int)(db->lookaside.nSlot - (nInit+nFree));
}
/*
** Query status information for a single database connection
*/
SQLITE_API int sqlite3_db_status64(
sqlite3 *db, /* The database connection whose status is desired */
int op, /* Status verb */
sqlite3_int64 *pCurrent, /* Write current value here */
sqlite3_int64 *pHighwtr, /* Write high-water mark here */
int resetFlag /* Reset high-water mark if true */
){
int rc = SQLITE_OK; /* Return code */
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) || pCurrent==0|| pHighwtr==0 ){
return SQLITE_MISUSE_BKPT;
}
#endif
sqlite3_mutex_enter(db->mutex);
switch( op ){
case SQLITE_DBSTATUS_LOOKASIDE_USED: {
int H = 0;
*pCurrent = sqlite3LookasideUsed(db, &H);
*pHighwtr = H;
if( resetFlag ){
LookasideSlot *p = db->lookaside.pFree;
if( p ){
while( p->pNext ) p = p->pNext;
p->pNext = db->lookaside.pInit;
db->lookaside.pInit = db->lookaside.pFree;
db->lookaside.pFree = 0;
|
| ︙ | ︙ | |||
24622 24623 24624 24625 24626 24627 24628 |
case SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL: {
testcase( op==SQLITE_DBSTATUS_LOOKASIDE_HIT );
testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE );
testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL );
assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)>=0 );
assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)<3 );
*pCurrent = 0;
| | | | | | | | 24786 24787 24788 24789 24790 24791 24792 24793 24794 24795 24796 24797 24798 24799 24800 24801 24802 24803 24804 24805 24806 24807 24808 24809 24810 24811 24812 24813 24814 24815 24816 24817 24818 24819 24820 24821 24822 24823 24824 24825 24826 24827 24828 24829 24830 24831 24832 24833 24834 24835 24836 24837 24838 24839 24840 24841 |
case SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL: {
testcase( op==SQLITE_DBSTATUS_LOOKASIDE_HIT );
testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE );
testcase( op==SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL );
assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)>=0 );
assert( (op-SQLITE_DBSTATUS_LOOKASIDE_HIT)<3 );
*pCurrent = 0;
*pHighwtr = db->lookaside.anStat[op-SQLITE_DBSTATUS_LOOKASIDE_HIT];
if( resetFlag ){
db->lookaside.anStat[op - SQLITE_DBSTATUS_LOOKASIDE_HIT] = 0;
}
break;
}
/*
** Return an approximation for the amount of memory currently used
** by all pagers associated with the given database connection. The
** highwater mark is meaningless and is returned as zero.
*/
case SQLITE_DBSTATUS_CACHE_USED_SHARED:
case SQLITE_DBSTATUS_CACHE_USED: {
sqlite3_int64 totalUsed = 0;
int i;
sqlite3BtreeEnterAll(db);
for(i=0; i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( pBt ){
Pager *pPager = sqlite3BtreePager(pBt);
int nByte = sqlite3PagerMemUsed(pPager);
if( op==SQLITE_DBSTATUS_CACHE_USED_SHARED ){
nByte = nByte / sqlite3BtreeConnectionCount(pBt);
}
totalUsed += nByte;
}
}
sqlite3BtreeLeaveAll(db);
*pCurrent = totalUsed;
*pHighwtr = 0;
break;
}
/*
** *pCurrent gets an accurate estimate of the amount of memory used
** to store the schema for all databases (main, temp, and any ATTACHed
** databases. *pHighwtr is set to zero.
*/
case SQLITE_DBSTATUS_SCHEMA_USED: {
int i; /* Used to iterate through schemas */
int nByte = 0; /* Used to accumulate return value */
sqlite3BtreeEnterAll(db);
db->pnBytesFreed = &nByte;
assert( db->lookaside.pEnd==db->lookaside.pTrueEnd );
db->lookaside.pEnd = db->lookaside.pStart;
for(i=0; i<db->nDb; i++){
Schema *pSchema = db->aDb[i].pSchema;
|
| ︙ | ︙ | |||
24697 24698 24699 24700 24701 24702 24703 |
}
}
}
db->pnBytesFreed = 0;
db->lookaside.pEnd = db->lookaside.pTrueEnd;
sqlite3BtreeLeaveAll(db);
| | | | | | | > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > | 24861 24862 24863 24864 24865 24866 24867 24868 24869 24870 24871 24872 24873 24874 24875 24876 24877 24878 24879 24880 24881 24882 24883 24884 24885 24886 24887 24888 24889 24890 24891 24892 24893 24894 24895 24896 24897 24898 24899 24900 24901 24902 24903 24904 24905 24906 24907 24908 24909 24910 24911 24912 24913 24914 24915 24916 24917 24918 24919 24920 24921 24922 24923 24924 24925 24926 24927 24928 24929 24930 24931 24932 24933 24934 24935 24936 24937 24938 24939 24940 24941 24942 24943 24944 24945 24946 24947 24948 24949 24950 24951 24952 24953 24954 24955 24956 24957 24958 24959 24960 24961 24962 24963 24964 24965 24966 24967 24968 24969 24970 24971 24972 24973 24974 24975 24976 24977 24978 24979 24980 24981 24982 24983 24984 24985 24986 24987 24988 24989 24990 24991 24992 24993 24994 |
}
}
}
db->pnBytesFreed = 0;
db->lookaside.pEnd = db->lookaside.pTrueEnd;
sqlite3BtreeLeaveAll(db);
*pHighwtr = 0;
*pCurrent = nByte;
break;
}
/*
** *pCurrent gets an accurate estimate of the amount of memory used
** to store all prepared statements.
** *pHighwtr is set to zero.
*/
case SQLITE_DBSTATUS_STMT_USED: {
struct Vdbe *pVdbe; /* Used to iterate through VMs */
int nByte = 0; /* Used to accumulate return value */
db->pnBytesFreed = &nByte;
assert( db->lookaside.pEnd==db->lookaside.pTrueEnd );
db->lookaside.pEnd = db->lookaside.pStart;
for(pVdbe=db->pVdbe; pVdbe; pVdbe=pVdbe->pVNext){
sqlite3VdbeDelete(pVdbe);
}
db->lookaside.pEnd = db->lookaside.pTrueEnd;
db->pnBytesFreed = 0;
*pHighwtr = 0; /* IMP: R-64479-57858 */
*pCurrent = nByte;
break;
}
/*
** Set *pCurrent to the total cache hits or misses encountered by all
** pagers the database handle is connected to. *pHighwtr is always set
** to zero.
*/
case SQLITE_DBSTATUS_CACHE_SPILL:
op = SQLITE_DBSTATUS_CACHE_WRITE+1;
/* no break */ deliberate_fall_through
case SQLITE_DBSTATUS_CACHE_HIT:
case SQLITE_DBSTATUS_CACHE_MISS:
case SQLITE_DBSTATUS_CACHE_WRITE:{
int i;
u64 nRet = 0;
assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 );
assert( SQLITE_DBSTATUS_CACHE_WRITE==SQLITE_DBSTATUS_CACHE_HIT+2 );
for(i=0; i<db->nDb; i++){
if( db->aDb[i].pBt ){
Pager *pPager = sqlite3BtreePager(db->aDb[i].pBt);
sqlite3PagerCacheStat(pPager, op, resetFlag, &nRet);
}
}
*pHighwtr = 0; /* IMP: R-42420-56072 */
/* IMP: R-54100-20147 */
/* IMP: R-29431-39229 */
*pCurrent = nRet;
break;
}
/* Set *pCurrent to the number of bytes that the db database connection
** has spilled to the filesystem in temporary files that could have been
** stored in memory, had sufficient memory been available.
** The *pHighwater is always set to zero.
*/
case SQLITE_DBSTATUS_TEMPBUF_SPILL: {
u64 nRet = 0;
if( db->aDb[1].pBt ){
Pager *pPager = sqlite3BtreePager(db->aDb[1].pBt);
sqlite3PagerCacheStat(pPager, SQLITE_DBSTATUS_CACHE_WRITE,
resetFlag, &nRet);
nRet *= sqlite3BtreeGetPageSize(db->aDb[1].pBt);
}
nRet += db->nSpill;
if( resetFlag ) db->nSpill = 0;
*pHighwtr = 0;
*pCurrent = nRet;
break;
}
/* Set *pCurrent to non-zero if there are unresolved deferred foreign
** key constraints. Set *pCurrent to zero if all foreign key constraints
** have been satisfied. The *pHighwtr is always set to zero.
*/
case SQLITE_DBSTATUS_DEFERRED_FKS: {
*pHighwtr = 0; /* IMP: R-11967-56545 */
*pCurrent = db->nDeferredImmCons>0 || db->nDeferredCons>0;
break;
}
default: {
rc = SQLITE_ERROR;
}
}
sqlite3_mutex_leave(db->mutex);
return rc;
}
/*
** 32-bit variant of sqlite3_db_status64()
*/
SQLITE_API int sqlite3_db_status(
sqlite3 *db, /* The database connection whose status is desired */
int op, /* Status verb */
int *pCurrent, /* Write current value here */
int *pHighwtr, /* Write high-water mark here */
int resetFlag /* Reset high-water mark if true */
){
sqlite3_int64 C = 0, H = 0;
int rc;
#ifdef SQLITE_ENABLE_API_ARMOR
if( !sqlite3SafetyCheckOk(db) || pCurrent==0|| pHighwtr==0 ){
return SQLITE_MISUSE_BKPT;
}
#endif
rc = sqlite3_db_status64(db, op, &C, &H, resetFlag);
if( rc==0 ){
*pCurrent = C & 0x7fffffff;
*pHighwtr = H & 0x7fffffff;
}
return rc;
}
/************** End of status.c **********************************************/
/************** Begin file date.c ********************************************/
/*
** 2003 October 31
**
** The author disclaims copyright to this source code. In place of
|
| ︙ | ︙ | |||
24965 24966 24967 24968 24969 24970 24971 24972 24973 24974 24975 24976 24977 24978 |
}
zDate++;
if( getDigits(zDate, "20b:20e", &nHr, &nMn)!=2 ){
return 1;
}
zDate += 5;
p->tz = sgn*(nMn + nHr*60);
zulu_time:
while( sqlite3Isspace(*zDate) ){ zDate++; }
return *zDate!=0;
}
/*
** Parse times of the form HH:MM or HH:MM:SS or HH:MM:SS.FFFF.
| > > > > | 25174 25175 25176 25177 25178 25179 25180 25181 25182 25183 25184 25185 25186 25187 25188 25189 25190 25191 |
}
zDate++;
if( getDigits(zDate, "20b:20e", &nHr, &nMn)!=2 ){
return 1;
}
zDate += 5;
p->tz = sgn*(nMn + nHr*60);
if( p->tz==0 ){ /* Forum post 2025-09-17T10:12:14z */
p->isLocal = 0;
p->isUtc = 1;
}
zulu_time:
while( sqlite3Isspace(*zDate) ){ zDate++; }
return *zDate!=0;
}
/*
** Parse times of the form HH:MM or HH:MM:SS or HH:MM:SS.FFFF.
|
| ︙ | ︙ | |||
26160 26161 26162 26163 26164 26165 26166 | ** %k hour 0-24 (leading zero converted to space) ** %I hour 01-12 ** %j day of year 001-366 ** %J ** julian day number ** %l hour 1-12 (leading zero converted to space) ** %m month 01-12 ** %M minute 00-59 | | | | 26373 26374 26375 26376 26377 26378 26379 26380 26381 26382 26383 26384 26385 26386 26387 26388 | ** %k hour 0-24 (leading zero converted to space) ** %I hour 01-12 ** %j day of year 001-366 ** %J ** julian day number ** %l hour 1-12 (leading zero converted to space) ** %m month 01-12 ** %M minute 00-59 ** %p "AM" or "PM" ** %P "am" or "pm" ** %R time as HH:MM ** %s seconds since 1970-01-01 ** %S seconds 00-59 ** %T time as HH:MM:SS ** %u day of week 1-7 Monday==1, Sunday==7 ** %w day of week 0-6 Sunday==0, Monday==1 ** %U week of year 00-53 (First Sunday is start of week 01) |
| ︙ | ︙ | |||
31768 31769 31770 31771 31772 31773 31774 31775 31776 31777 31778 31779 31780 31781 31782 |
typedef struct et_info { /* Information about each format field */
char fmttype; /* The format field code letter */
etByte base; /* The base for radix conversion */
etByte flags; /* One or more of FLAG_ constants below */
etByte type; /* Conversion paradigm */
etByte charset; /* Offset into aDigits[] of the digits string */
etByte prefix; /* Offset into aPrefix[] of the prefix string */
} et_info;
/*
** Allowed values for et_info.flags
*/
#define FLAG_SIGNED 1 /* True if the value to convert is signed */
#define FLAG_STRING 4 /* Allow infinite precision */
| > < | > | > > > > > > > > > > > > > > > > > > > > > | < | > > | < < < | < < < | | < > | | | | < > | < | < < < | | > > > > > | | | 31981 31982 31983 31984 31985 31986 31987 31988 31989 31990 31991 31992 31993 31994 31995 31996 31997 31998 31999 32000 32001 32002 32003 32004 32005 32006 32007 32008 32009 32010 32011 32012 32013 32014 32015 32016 32017 32018 32019 32020 32021 32022 32023 32024 32025 32026 32027 32028 32029 32030 32031 32032 32033 32034 32035 32036 32037 32038 32039 32040 32041 32042 32043 32044 32045 32046 32047 32048 32049 32050 32051 32052 32053 32054 32055 32056 32057 32058 |
typedef struct et_info { /* Information about each format field */
char fmttype; /* The format field code letter */
etByte base; /* The base for radix conversion */
etByte flags; /* One or more of FLAG_ constants below */
etByte type; /* Conversion paradigm */
etByte charset; /* Offset into aDigits[] of the digits string */
etByte prefix; /* Offset into aPrefix[] of the prefix string */
char iNxt; /* Next with same hash, or 0 for end of chain */
} et_info;
/*
** Allowed values for et_info.flags
*/
#define FLAG_SIGNED 1 /* True if the value to convert is signed */
#define FLAG_STRING 4 /* Allow infinite precision */
/*
** The table is searched by hash. In the case of %C where C is the character
** and that character has ASCII value j, then the hash is j%23.
**
** The order of the entries in fmtinfo[] and the hash chain was entered
** manually, but based on the output of the following TCL script:
*/
#if 0 /***** Beginning of script ******/
foreach c {d s g z q Q w c o u x X f e E G i n % p T S r} {
scan $c %c x
set n($c) $x
}
set mx [llength [array names n]]
puts "count: $mx"
set mx 27
puts "*********** mx=$mx ************"
for {set r 0} {$r<$mx} {incr r} {
puts -nonewline [format %2d: $r]
foreach c [array names n] {
if {($n($c))%$mx==$r} {puts -nonewline " $c"}
}
puts ""
}
#endif /***** End of script ********/
static const char aDigits[] = "0123456789ABCDEF0123456789abcdef";
static const char aPrefix[] = "-x0\000X0";
static const et_info fmtinfo[23] = {
/* 0 */ { 's', 0, 4, etSTRING, 0, 0, 1 },
/* 1 */ { 'E', 0, 1, etEXP, 14, 0, 0 }, /* Hash: 0 */
/* 2 */ { 'u', 10, 0, etDECIMAL, 0, 0, 3 },
/* 3 */ { 'G', 0, 1, etGENERIC, 14, 0, 0 }, /* Hash: 2 */
/* 4 */ { 'w', 0, 4, etESCAPE_w, 0, 0, 0 },
/* 5 */ { 'x', 16, 0, etRADIX, 16, 1, 0 },
/* 6 */ { 'c', 0, 0, etCHARX, 0, 0, 0 }, /* Hash: 7 */
/* 7 */ { 'z', 0, 4, etDYNSTRING, 0, 0, 6 },
/* 8 */ { 'd', 10, 1, etDECIMAL, 0, 0, 0 },
/* 9 */ { 'e', 0, 1, etEXP, 30, 0, 0 },
/* 10 */ { 'f', 0, 1, etFLOAT, 0, 0, 0 },
/* 11 */ { 'g', 0, 1, etGENERIC, 30, 0, 0 },
/* 12 */ { 'Q', 0, 4, etESCAPE_Q, 0, 0, 0 },
/* 13 */ { 'i', 10, 1, etDECIMAL, 0, 0, 0 },
/* 14 */ { '%', 0, 0, etPERCENT, 0, 0, 16 },
/* 15 */ { 'T', 0, 0, etTOKEN, 0, 0, 0 },
/* 16 */ { 'S', 0, 0, etSRCITEM, 0, 0, 0 }, /* Hash: 14 */
/* 17 */ { 'X', 16, 0, etRADIX, 0, 4, 0 }, /* Hash: 19 */
/* 18 */ { 'n', 0, 0, etSIZE, 0, 0, 0 },
/* 19 */ { 'o', 8, 0, etRADIX, 0, 2, 17 },
/* 20 */ { 'p', 16, 0, etPOINTER, 0, 1, 0 },
/* 21 */ { 'q', 0, 4, etESCAPE_q, 0, 0, 0 },
/* 22 */ { 'r', 10, 1, etORDINAL, 0, 0, 0 }
};
/* Additional Notes:
**
** %S Takes a pointer to SrcItem. Shows name or database.name
** %!S Like %S but prefer the zName over the zAlias
*/
/*
** Set the StrAccum object to an error mode.
|
| ︙ | ︙ | |||
31940 31941 31942 31943 31944 31945 31946 |
}
for(; (c=(*fmt))!=0; ++fmt){
if( c!='%' ){
bufpt = (char *)fmt;
#if HAVE_STRCHRNUL
fmt = strchrnul(fmt, '%');
#else
| > | > > | 32171 32172 32173 32174 32175 32176 32177 32178 32179 32180 32181 32182 32183 32184 32185 32186 32187 32188 |
}
for(; (c=(*fmt))!=0; ++fmt){
if( c!='%' ){
bufpt = (char *)fmt;
#if HAVE_STRCHRNUL
fmt = strchrnul(fmt, '%');
#else
fmt = strchr(fmt, '%');
if( fmt==0 ){
fmt = bufpt + strlen(bufpt);
}
#endif
sqlite3_str_append(pAccum, bufpt, (int)(fmt - bufpt));
if( *fmt==0 ) break;
}
if( (c=(*++fmt))==0 ){
sqlite3_str_append(pAccum, "%", 1);
break;
|
| ︙ | ︙ | |||
32054 32055 32056 32057 32058 32059 32060 32061 32062 32063 32064 32065 32066 32067 32068 32069 32070 32071 32072 32073 32074 32075 32076 |
}
break;
}
}
}while( !done && (c=(*++fmt))!=0 );
/* Fetch the info entry for the field */
infop = &fmtinfo[0];
xtype = etINVALID;
for(idx=0; idx<ArraySize(fmtinfo); idx++){
if( c==fmtinfo[idx].fmttype ){
infop = &fmtinfo[idx];
xtype = infop->type;
break;
}
}
/*
** At this point, variables are initialized as follows:
**
** flag_alternateform TRUE if a '#' is present.
** flag_altform2 TRUE if a '!' is present.
** flag_prefix '+' or ' ' or zero
| > > > > > > > > > > > > > > > > > | 32288 32289 32290 32291 32292 32293 32294 32295 32296 32297 32298 32299 32300 32301 32302 32303 32304 32305 32306 32307 32308 32309 32310 32311 32312 32313 32314 32315 32316 32317 32318 32319 32320 32321 32322 32323 32324 32325 32326 32327 |
}
break;
}
}
}while( !done && (c=(*++fmt))!=0 );
/* Fetch the info entry for the field */
#ifdef SQLITE_EBCDIC
/* The hash table only works for ASCII. For EBCDIC, we need to do
** a linear search of the table */
infop = &fmtinfo[0];
xtype = etINVALID;
for(idx=0; idx<ArraySize(fmtinfo); idx++){
if( c==fmtinfo[idx].fmttype ){
infop = &fmtinfo[idx];
xtype = infop->type;
break;
}
}
#else
/* Fast hash-table lookup */
assert( ArraySize(fmtinfo)==23 );
idx = ((unsigned)c) % 23;
if( fmtinfo[idx].fmttype==c
|| fmtinfo[idx = fmtinfo[idx].iNxt].fmttype==c
){
infop = &fmtinfo[idx];
xtype = infop->type;
}else{
infop = &fmtinfo[0];
xtype = etINVALID;
}
#endif
/*
** At this point, variables are initialized as follows:
**
** flag_alternateform TRUE if a '#' is present.
** flag_altform2 TRUE if a '!' is present.
** flag_prefix '+' or ' ' or zero
|
| ︙ | ︙ | |||
32250 32251 32252 32253 32254 32255 32256 |
bufpt++;
}
length = sqlite3Strlen30(bufpt);
break;
}
}
if( s.sign=='-' ){
| > > > > > > > > > > > > > | > | 32501 32502 32503 32504 32505 32506 32507 32508 32509 32510 32511 32512 32513 32514 32515 32516 32517 32518 32519 32520 32521 32522 32523 32524 32525 32526 32527 32528 32529 |
bufpt++;
}
length = sqlite3Strlen30(bufpt);
break;
}
}
if( s.sign=='-' ){
if( flag_alternateform
&& !flag_prefix
&& xtype==etFLOAT
&& s.iDP<=iRound
){
/* Suppress the minus sign if all of the following are true:
** * The value displayed is zero
** * The '#' flag is used
** * The '+' flag is not used, and
** * The format is %f
*/
prefix = 0;
}else{
prefix = '-';
}
}else{
prefix = flag_prefix;
}
exp = s.iDP-1;
/*
|
| ︙ | ︙ | |||
33461 33462 33463 33464 33465 33466 33467 |
if( pItem->fg.isSubquery ) sqlite3_str_appendf(&x, " isSubquery");
sqlite3StrAccumFinish(&x);
sqlite3TreeViewItem(pView, zLine, i<pSrc->nSrc-1);
n = 0;
if( pItem->fg.isSubquery ) n++;
if( pItem->fg.isTabFunc ) n++;
| | > > > > | 33726 33727 33728 33729 33730 33731 33732 33733 33734 33735 33736 33737 33738 33739 33740 33741 33742 33743 33744 33745 33746 |
if( pItem->fg.isSubquery ) sqlite3_str_appendf(&x, " isSubquery");
sqlite3StrAccumFinish(&x);
sqlite3TreeViewItem(pView, zLine, i<pSrc->nSrc-1);
n = 0;
if( pItem->fg.isSubquery ) n++;
if( pItem->fg.isTabFunc ) n++;
if( pItem->fg.isUsing || pItem->u3.pOn!=0 ) n++;
if( pItem->fg.isUsing ){
sqlite3TreeViewIdList(pView, pItem->u3.pUsing, (--n)>0, "USING");
}else if( pItem->u3.pOn!=0 ){
sqlite3TreeViewItem(pView, "ON", (--n)>0);
sqlite3TreeViewExpr(pView, pItem->u3.pOn, 0);
sqlite3TreeViewPop(&pView);
}
if( pItem->fg.isSubquery ){
assert( n==1 );
if( pItem->pSTab ){
Table *pTab = pItem->pSTab;
sqlite3TreeViewColumnList(pView, pTab->aCol, pTab->nCol, 1);
}
|
| ︙ | ︙ | |||
38106 38107 38108 38109 38110 38111 38112 | */ static int kvstorageWrite(const char*, const char *zKey, const char *zData); static int kvstorageDelete(const char*, const char *zKey); static int kvstorageRead(const char*, const char *zKey, char *zBuf, int nBuf); #define KVSTORAGE_KEY_SZ 32 /* Expand the key name with an appropriate prefix and put the result | | | 38375 38376 38377 38378 38379 38380 38381 38382 38383 38384 38385 38386 38387 38388 38389 |
*/
static int kvstorageWrite(const char*, const char *zKey, const char *zData);
static int kvstorageDelete(const char*, const char *zKey);
static int kvstorageRead(const char*, const char *zKey, char *zBuf, int nBuf);
#define KVSTORAGE_KEY_SZ 32
/* Expand the key name with an appropriate prefix and put the result
** in zKeyOut[]. The zKeyOut[] buffer is assumed to hold at least
** KVSTORAGE_KEY_SZ bytes.
*/
static void kvstorageMakeKey(
const char *zClass,
const char *zKeyIn,
char *zKeyOut
){
|
| ︙ | ︙ | |||
38165 38166 38167 38168 38169 38170 38171 | ** by zClass and put the text data associated with that key in the first ** nBuf bytes of zBuf[]. The value might be truncated if zBuf is not large ** enough to hold it all. The value put into zBuf must always be zero ** terminated, even if it gets truncated because nBuf is not large enough. ** ** Return the total number of bytes in the data, without truncation, and ** not counting the final zero terminator. Return -1 if the key does | | | > > | | 38434 38435 38436 38437 38438 38439 38440 38441 38442 38443 38444 38445 38446 38447 38448 38449 38450 38451 38452 38453 |
** by zClass and put the text data associated with that key in the first
** nBuf bytes of zBuf[]. The value might be truncated if zBuf is not large
** enough to hold it all. The value put into zBuf must always be zero
** terminated, even if it gets truncated because nBuf is not large enough.
**
** Return the total number of bytes in the data, without truncation, and
** not counting the final zero terminator. Return -1 if the key does
** not exist or its key cannot be read.
**
** If nBuf<=0 then this routine simply returns the size of the data
** without actually reading it. Similarly, if nBuf==1 then it
** zero-terminates zBuf at zBuf[0] and returns the size of the data
** without reading it.
*/
static int kvstorageRead(
const char *zClass,
const char *zKey,
char *zBuf,
int nBuf
){
|
| ︙ | ︙ | |||
38217 38218 38219 38220 38221 38222 38223 | } /* ** An internal level of indirection which enables us to replace the ** kvvfs i/o methods with JavaScript implementations in WASM builds. ** Maintenance reminder: if this struct changes in any way, the JSON ** rendering of its structure must be updated in | | | < | < | | | 38488 38489 38490 38491 38492 38493 38494 38495 38496 38497 38498 38499 38500 38501 38502 38503 38504 38505 38506 38507 38508 38509 38510 38511 38512 38513 38514 38515 38516 38517 38518 38519 38520 38521 38522 |
}
/*
** An internal level of indirection which enables us to replace the
** kvvfs i/o methods with JavaScript implementations in WASM builds.
** Maintenance reminder: if this struct changes in any way, the JSON
** rendering of its structure must be updated in
** sqlite3-wasm.c:sqlite3__wasm_enum_json(). There are no binary
** compatibility concerns, so it does not need an iVersion
** member.
*/
typedef struct sqlite3_kvvfs_methods sqlite3_kvvfs_methods;
struct sqlite3_kvvfs_methods {
int (*xRead)(const char *zClass, const char *zKey, char *zBuf, int nBuf);
int (*xWrite)(const char *zClass, const char *zKey, const char *zData);
int (*xDelete)(const char *zClass, const char *zKey);
const int nKeySize;
};
/*
** This object holds the kvvfs I/O methods which may be swapped out
** for JavaScript-side implementations in WASM builds. In such builds
** it cannot be const, but in native builds it should be so that
** the compiler can hopefully optimize this level of indirection out.
** That said, kvvfs is intended primarily for use in WASM builds.
**
** This is not explicitly flagged as static because the amalgamation
** build will tag it with SQLITE_PRIVATE.
*/
#ifndef SQLITE_WASM
const
#endif
SQLITE_PRIVATE sqlite3_kvvfs_methods sqlite3KvvfsMethods = {
kvstorageRead,
kvstorageWrite,
|
| ︙ | ︙ | |||
39412 39413 39414 39415 39416 39417 39418 39419 39420 39421 |
{ "pwrite64", (sqlite3_syscall_ptr)0, 0 },
#endif
#define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off64_t))\
aSyscall[13].pCurrent)
#if defined(HAVE_FCHMOD)
{ "fchmod", (sqlite3_syscall_ptr)fchmod, 0 },
#else
{ "fchmod", (sqlite3_syscall_ptr)0, 0 },
#endif
| > > < | 39681 39682 39683 39684 39685 39686 39687 39688 39689 39690 39691 39692 39693 39694 39695 39696 39697 39698 39699 |
{ "pwrite64", (sqlite3_syscall_ptr)0, 0 },
#endif
#define osPwrite64 ((ssize_t(*)(int,const void*,size_t,off64_t))\
aSyscall[13].pCurrent)
#if defined(HAVE_FCHMOD)
{ "fchmod", (sqlite3_syscall_ptr)fchmod, 0 },
#define osFchmod ((int(*)(int,mode_t))aSyscall[14].pCurrent)
#else
{ "fchmod", (sqlite3_syscall_ptr)0, 0 },
#define osFchmod(FID,MODE) 0
#endif
#if defined(HAVE_POSIX_FALLOCATE) && HAVE_POSIX_FALLOCATE
{ "fallocate", (sqlite3_syscall_ptr)posix_fallocate, 0 },
#else
{ "fallocate", (sqlite3_syscall_ptr)0, 0 },
#endif
#define osFallocate ((int(*)(int,off_t,off_t))aSyscall[15].pCurrent)
|
| ︙ | ︙ | |||
39673 39674 39675 39676 39677 39678 39679 | #endif } return fd; } /* ** Helper functions to obtain and relinquish the global mutex. The | | < | | 39943 39944 39945 39946 39947 39948 39949 39950 39951 39952 39953 39954 39955 39956 39957 39958 | #endif } return fd; } /* ** Helper functions to obtain and relinquish the global mutex. The ** global mutex is used to protect the unixInodeInfo objects used by ** this file, all of which may be shared by multiple threads. ** ** Function unixMutexHeld() is used to assert() that the global mutex ** is held when required. This function is only used as part of assert() ** statements. e.g. ** ** unixEnterMutex() ** assert( unixMutexHeld() ); |
| ︙ | ︙ | |||
39877 39878 39879 39880 39881 39882 39883 39884 39885 39886 39887 39888 39889 39890 | #if OS_VXWORKS /* ** All unique filenames are held on a linked list headed by this ** variable: */ static struct vxworksFileId *vxworksFileList = 0; /* ** Simplify a filename into its canonical form ** by making the following changes: ** ** * removing any trailing and duplicate / ** * convert /./ into just / | > | 40146 40147 40148 40149 40150 40151 40152 40153 40154 40155 40156 40157 40158 40159 40160 | #if OS_VXWORKS /* ** All unique filenames are held on a linked list headed by this ** variable: */ static struct vxworksFileId *vxworksFileList = 0; static sqlite3_mutex *vxworksMutex = 0; /* ** Simplify a filename into its canonical form ** by making the following changes: ** ** * removing any trailing and duplicate / ** * convert /./ into just / |
| ︙ | ︙ | |||
39942 39943 39944 39945 39946 39947 39948 | memcpy(pNew->zCanonicalName, zAbsoluteName, n+1); n = vxworksSimplifyName(pNew->zCanonicalName, n); /* Search for an existing entry that matching the canonical name. ** If found, increment the reference count and return a pointer to ** the existing file ID. */ | | | | | | | 40212 40213 40214 40215 40216 40217 40218 40219 40220 40221 40222 40223 40224 40225 40226 40227 40228 40229 40230 40231 40232 40233 40234 40235 40236 40237 40238 40239 40240 40241 40242 40243 40244 40245 40246 40247 40248 40249 40250 40251 40252 40253 40254 40255 40256 40257 40258 40259 40260 40261 40262 |
memcpy(pNew->zCanonicalName, zAbsoluteName, n+1);
n = vxworksSimplifyName(pNew->zCanonicalName, n);
/* Search for an existing entry that matching the canonical name.
** If found, increment the reference count and return a pointer to
** the existing file ID.
*/
sqlite3_mutex_enter(vxworksMutex);
for(pCandidate=vxworksFileList; pCandidate; pCandidate=pCandidate->pNext){
if( pCandidate->nName==n
&& memcmp(pCandidate->zCanonicalName, pNew->zCanonicalName, n)==0
){
sqlite3_free(pNew);
pCandidate->nRef++;
sqlite3_mutex_leave(vxworksMutex);
return pCandidate;
}
}
/* No match was found. We will make a new file ID */
pNew->nRef = 1;
pNew->nName = n;
pNew->pNext = vxworksFileList;
vxworksFileList = pNew;
sqlite3_mutex_leave(vxworksMutex);
return pNew;
}
/*
** Decrement the reference count on a vxworksFileId object. Free
** the object when the reference count reaches zero.
*/
static void vxworksReleaseFileId(struct vxworksFileId *pId){
sqlite3_mutex_enter(vxworksMutex);
assert( pId->nRef>0 );
pId->nRef--;
if( pId->nRef==0 ){
struct vxworksFileId **pp;
for(pp=&vxworksFileList; *pp && *pp!=pId; pp = &((*pp)->pNext)){}
assert( *pp==pId );
*pp = pId->pNext;
sqlite3_free(pId);
}
sqlite3_mutex_leave(vxworksMutex);
}
#endif /* OS_VXWORKS */
/*************** End of Unique File ID Utility Used By VxWorks ****************
******************************************************************************/
/******************************************************************************
|
| ︙ | ︙ | |||
40366 40367 40368 40369 40370 40371 40372 40373 40374 40375 40376 40377 40378 40379 |
*/
if( statbuf.st_size==0 && (pFile->fsFlags & SQLITE_FSFLAGS_IS_MSDOS)!=0 ){
do{ rc = osWrite(fd, "S", 1); }while( rc<0 && errno==EINTR );
if( rc!=1 ){
storeLastErrno(pFile, errno);
return SQLITE_IOERR;
}
rc = osFstat(fd, &statbuf);
if( rc!=0 ){
storeLastErrno(pFile, errno);
return SQLITE_IOERR;
}
}
#endif
| > > > > | 40636 40637 40638 40639 40640 40641 40642 40643 40644 40645 40646 40647 40648 40649 40650 40651 40652 40653 |
*/
if( statbuf.st_size==0 && (pFile->fsFlags & SQLITE_FSFLAGS_IS_MSDOS)!=0 ){
do{ rc = osWrite(fd, "S", 1); }while( rc<0 && errno==EINTR );
if( rc!=1 ){
storeLastErrno(pFile, errno);
return SQLITE_IOERR;
}
if( fsync(fd) ){
storeLastErrno(pFile, errno);
return SQLITE_IOERR_FSYNC;
}
rc = osFstat(fd, &statbuf);
if( rc!=0 ){
storeLastErrno(pFile, errno);
return SQLITE_IOERR;
}
}
#endif
|
| ︙ | ︙ | |||
40535 40536 40537 40538 40539 40540 40541 |
# define osSetPosixAdvisoryLock(h,x,t) osFcntl(h,F_SETLK,x)
#else
static int osSetPosixAdvisoryLock(
int h, /* The file descriptor on which to take the lock */
struct flock *pLock, /* The description of the lock */
unixFile *pFile /* Structure holding timeout value */
){
| > > | > > | < > > > > | < < > > > | > > > > | > > > | | > > | | > > > > > > > | 40809 40810 40811 40812 40813 40814 40815 40816 40817 40818 40819 40820 40821 40822 40823 40824 40825 40826 40827 40828 40829 40830 40831 40832 40833 40834 40835 40836 40837 40838 40839 40840 40841 40842 40843 40844 40845 40846 40847 40848 40849 40850 40851 40852 40853 40854 40855 40856 40857 40858 |
# define osSetPosixAdvisoryLock(h,x,t) osFcntl(h,F_SETLK,x)
#else
static int osSetPosixAdvisoryLock(
int h, /* The file descriptor on which to take the lock */
struct flock *pLock, /* The description of the lock */
unixFile *pFile /* Structure holding timeout value */
){
int rc = 0;
if( pFile->iBusyTimeout==0 ){
/* unixFile->iBusyTimeout is set to 0. In this case, attempt a
** non-blocking lock. */
rc = osFcntl(h,F_SETLK,pLock);
}else{
/* unixFile->iBusyTimeout is set to greater than zero. In this case,
** attempt a blocking-lock with a unixFile->iBusyTimeout ms timeout.
**
** On systems that support some kind of blocking file lock operation,
** this block should be replaced by code to attempt a blocking lock
** with a timeout of unixFile->iBusyTimeout ms. The code below is
** placeholder code. If SQLITE_TEST is defined, the placeholder code
** retries the lock once every 1ms until it succeeds or the timeout
** is reached. Or, if SQLITE_TEST is not defined, the placeholder
** code attempts a non-blocking lock and sets unixFile->iBusyTimeout
** to 0. This causes the caller to return SQLITE_BUSY, instead of
** SQLITE_BUSY_TIMEOUT to SQLite - as required by a VFS that does not
** support blocking locks.
*/
#ifdef SQLITE_TEST
int tm = pFile->iBusyTimeout;
while( tm>0 ){
rc = osFcntl(h,F_SETLK,pLock);
if( rc==0 ) break;
unixSleep(0,1000);
tm--;
}
#else
rc = osFcntl(h,F_SETLK,pLock);
pFile->iBusyTimeout = 0;
#endif
/* End of code to replace with real blocking-locks code. */
}
return rc;
}
#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */
/*
** Attempt to set a system-lock on the file pFile. The lock is
|
| ︙ | ︙ | |||
44867 44868 44869 44870 44871 44872 44873 |
unixLeaveMutex();
}
#endif
storeLastErrno(pNew, 0);
#if OS_VXWORKS
if( rc!=SQLITE_OK ){
| > | | > > | > > > | > | 45165 45166 45167 45168 45169 45170 45171 45172 45173 45174 45175 45176 45177 45178 45179 45180 45181 45182 45183 45184 45185 45186 45187 45188 45189 |
unixLeaveMutex();
}
#endif
storeLastErrno(pNew, 0);
#if OS_VXWORKS
if( rc!=SQLITE_OK ){
if( h>=0 ){
robust_close(pNew, h, __LINE__);
h = -1;
}
if( pNew->ctrlFlags & UNIXFILE_DELETE ){
osUnlink(zFilename);
}
if( pNew->pId ){
vxworksReleaseFileId(pNew->pId);
pNew->pId = 0;
}
}
#endif
if( rc!=SQLITE_OK ){
if( h>=0 ) robust_close(pNew, h, __LINE__);
}else{
pId->pMethods = pLockingStyle;
OpenCounter(+1);
|
| ︙ | ︙ | |||
44914 44915 44916 44917 44918 44919 44920 44921 44922 44923 44924 44925 44926 44927 |
static const char *unixTempFileDir(void){
unsigned int i = 0;
struct stat buf;
const char *zDir = sqlite3_temp_directory;
while(1){
if( zDir!=0
&& osStat(zDir, &buf)==0
&& S_ISDIR(buf.st_mode)
&& osAccess(zDir, 03)==0
){
return zDir;
}
if( i>=sizeof(azTempDirs)/sizeof(azTempDirs[0]) ) break;
| > > > | 45219 45220 45221 45222 45223 45224 45225 45226 45227 45228 45229 45230 45231 45232 45233 45234 45235 |
static const char *unixTempFileDir(void){
unsigned int i = 0;
struct stat buf;
const char *zDir = sqlite3_temp_directory;
while(1){
if( zDir!=0
#if OS_VXWORKS
&& zDir[0]=='/'
#endif
&& osStat(zDir, &buf)==0
&& S_ISDIR(buf.st_mode)
&& osAccess(zDir, 03)==0
){
return zDir;
}
if( i>=sizeof(azTempDirs)/sizeof(azTempDirs[0]) ) break;
|
| ︙ | ︙ | |||
45227 45228 45229 45230 45231 45232 45233 45234 45235 45236 45237 45238 45239 45240 |
/* Assert that the upper layer has set one of the "file-type" flags. */
assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
|| eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL
|| eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_SUPER_JOURNAL
|| eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL
);
/* Detect a pid change and reset the PRNG. There is a race condition
** here such that two or more threads all trying to open databases at
** the same instant might all reset the PRNG. But multiple resets
** are harmless.
*/
if( randomnessPid!=osGetpid(0) ){
| > > > > > > | 45535 45536 45537 45538 45539 45540 45541 45542 45543 45544 45545 45546 45547 45548 45549 45550 45551 45552 45553 45554 |
/* Assert that the upper layer has set one of the "file-type" flags. */
assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB
|| eType==SQLITE_OPEN_MAIN_JOURNAL || eType==SQLITE_OPEN_TEMP_JOURNAL
|| eType==SQLITE_OPEN_SUBJOURNAL || eType==SQLITE_OPEN_SUPER_JOURNAL
|| eType==SQLITE_OPEN_TRANSIENT_DB || eType==SQLITE_OPEN_WAL
);
#if OS_VXWORKS
/* The file-ID mechanism used in Vxworks requires that all pathnames
** provided to unixOpen must be absolute pathnames. */
if( zPath!=0 && zPath[0]!='/' ){ return SQLITE_CANTOPEN; }
#endif
/* Detect a pid change and reset the PRNG. There is a race condition
** here such that two or more threads all trying to open databases at
** the same instant might all reset the PRNG. But multiple resets
** are harmless.
*/
if( randomnessPid!=osGetpid(0) ){
|
| ︙ | ︙ | |||
45428 45429 45430 45431 45432 45433 45434 |
}
}
goto open_finished;
}
}
#endif
| | > | > > | 45742 45743 45744 45745 45746 45747 45748 45749 45750 45751 45752 45753 45754 45755 45756 45757 45758 45759 45760 |
}
}
goto open_finished;
}
}
#endif
assert( zPath==0
|| zPath[0]=='/'
|| eType==SQLITE_OPEN_SUPER_JOURNAL
|| eType==SQLITE_OPEN_MAIN_JOURNAL
|| eType==SQLITE_OPEN_TEMP_JOURNAL
);
rc = fillInUnixFile(pVfs, fd, pFile, zPath, ctrlFlags);
open_finished:
if( rc!=SQLITE_OK ){
sqlite3_free(p->pPreallocatedUnused);
}
|
| ︙ | ︙ | |||
47158 47159 47160 47161 47162 47163 47164 47165 47166 47167 47168 47169 47170 47171 |
sqlite3_vfs_register(&aVfs[i], i==0);
#endif
}
#ifdef SQLITE_OS_KV_OPTIONAL
sqlite3KvvfsInit();
#endif
unixBigLock = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1);
#ifndef SQLITE_OMIT_WAL
/* Validate lock assumptions */
assert( SQLITE_SHM_NLOCK==8 ); /* Number of available locks */
assert( UNIX_SHM_BASE==120 ); /* Start of locking area */
/* Locks:
** WRITE UNIX_SHM_BASE 120
| > > > | 47475 47476 47477 47478 47479 47480 47481 47482 47483 47484 47485 47486 47487 47488 47489 47490 47491 |
sqlite3_vfs_register(&aVfs[i], i==0);
#endif
}
#ifdef SQLITE_OS_KV_OPTIONAL
sqlite3KvvfsInit();
#endif
unixBigLock = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS1);
#if OS_VXWORKS
vxworksMutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_VFS2);
#endif
#ifndef SQLITE_OMIT_WAL
/* Validate lock assumptions */
assert( SQLITE_SHM_NLOCK==8 ); /* Number of available locks */
assert( UNIX_SHM_BASE==120 ); /* Start of locking area */
/* Locks:
** WRITE UNIX_SHM_BASE 120
|
| ︙ | ︙ | |||
47192 47193 47194 47195 47196 47197 47198 47199 47200 47201 47202 47203 47204 47205 |
**
** Some operating systems might need to do some cleanup in this routine,
** to release dynamically allocated objects. But not on unix.
** This routine is a no-op for unix.
*/
SQLITE_API int sqlite3_os_end(void){
unixBigLock = 0;
return SQLITE_OK;
}
#endif /* SQLITE_OS_UNIX */
/************** End of os_unix.c *********************************************/
/************** Begin file os_win.c ******************************************/
| > > > | 47512 47513 47514 47515 47516 47517 47518 47519 47520 47521 47522 47523 47524 47525 47526 47527 47528 |
**
** Some operating systems might need to do some cleanup in this routine,
** to release dynamically allocated objects. But not on unix.
** This routine is a no-op for unix.
*/
SQLITE_API int sqlite3_os_end(void){
unixBigLock = 0;
#if OS_VXWORKS
vxworksMutex = 0;
#endif
return SQLITE_OK;
}
#endif /* SQLITE_OS_UNIX */
/************** End of os_unix.c *********************************************/
/************** Begin file os_win.c ******************************************/
|
| ︙ | ︙ | |||
51190 51191 51192 51193 51194 51195 51196 | /* ** Windows will only let you create file view mappings ** on allocation size granularity boundaries. ** During sqlite3_os_init() we do a GetSystemInfo() ** to get the granularity size. */ static SYSTEM_INFO winSysInfo; | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 51513 51514 51515 51516 51517 51518 51519 51520 51521 51522 51523 51524 51525 51526 | /* ** Windows will only let you create file view mappings ** on allocation size granularity boundaries. ** During sqlite3_os_init() we do a GetSystemInfo() ** to get the granularity size. */ static SYSTEM_INFO winSysInfo; /* ** Convert a UTF-8 filename into whatever form the underlying ** operating system wants filenames in. Space to hold the result ** is obtained from malloc and must be freed by the calling ** function ** |
| ︙ | ︙ | |||
51481 51482 51483 51484 51485 51486 51487 51488 51489 51490 51491 51492 51493 51494 |
else{
zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI());
}
#endif
/* caller will handle out of memory */
return zConverted;
}
/*
** This function is used to open a handle on a *-shm file.
**
** If SQLITE_ENABLE_SETLK_TIMEOUT is defined at build time, then the file
** is opened with FILE_FLAG_OVERLAPPED specified. If not, it is not.
*/
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 51610 51611 51612 51613 51614 51615 51616 51617 51618 51619 51620 51621 51622 51623 51624 51625 51626 51627 51628 51629 51630 51631 51632 51633 51634 51635 51636 51637 51638 51639 51640 51641 51642 51643 51644 51645 51646 51647 51648 51649 51650 51651 51652 51653 51654 51655 51656 51657 51658 51659 51660 51661 51662 51663 51664 51665 51666 51667 51668 51669 51670 51671 51672 51673 51674 51675 51676 51677 51678 51679 51680 51681 51682 51683 51684 51685 51686 51687 51688 51689 51690 51691 51692 51693 51694 51695 51696 51697 51698 51699 51700 51701 51702 51703 51704 51705 51706 51707 51708 51709 51710 51711 51712 51713 51714 51715 51716 51717 51718 51719 51720 51721 51722 51723 51724 51725 51726 51727 51728 51729 51730 51731 51732 51733 51734 51735 51736 51737 51738 51739 51740 51741 51742 51743 51744 51745 51746 51747 51748 51749 51750 51751 51752 51753 51754 51755 51756 51757 51758 51759 51760 51761 51762 51763 51764 51765 51766 51767 51768 51769 51770 51771 51772 51773 51774 51775 51776 51777 51778 51779 51780 51781 51782 51783 51784 51785 51786 51787 51788 51789 51790 51791 51792 51793 51794 51795 51796 51797 51798 51799 51800 51801 51802 51803 51804 51805 51806 51807 51808 51809 51810 51811 51812 51813 51814 51815 51816 51817 51818 51819 51820 51821 51822 51823 51824 51825 |
else{
zConverted = winUtf8ToMbcs(zFilename, osAreFileApisANSI());
}
#endif
/* caller will handle out of memory */
return zConverted;
}
#ifndef SQLITE_OMIT_WAL
/*
** Helper functions to obtain and relinquish the global mutex. The
** global mutex is used to protect the winLockInfo objects used by
** this file, all of which may be shared by multiple threads.
**
** Function winShmMutexHeld() is used to assert() that the global mutex
** is held when required. This function is only used as part of assert()
** statements. e.g.
**
** winShmEnterMutex()
** assert( winShmMutexHeld() );
** winShmLeaveMutex()
*/
static sqlite3_mutex *winBigLock = 0;
static void winShmEnterMutex(void){
sqlite3_mutex_enter(winBigLock);
}
static void winShmLeaveMutex(void){
sqlite3_mutex_leave(winBigLock);
}
#ifndef NDEBUG
static int winShmMutexHeld(void) {
return sqlite3_mutex_held(winBigLock);
}
#endif
/*
** Object used to represent a single file opened and mmapped to provide
** shared memory. When multiple threads all reference the same
** log-summary, each thread has its own winFile object, but they all
** point to a single instance of this object. In other words, each
** log-summary is opened only once per process.
**
** winShmMutexHeld() must be true when creating or destroying
** this object, or while editing the global linked list that starts
** at winShmNodeList.
**
** When reading or writing the linked list starting at winShmNode.pWinShmList,
** pShmNode->mutex must be held.
**
** The following fields are constant after the object is created:
**
** zFilename
** hSharedShm
** mutex
** bUseSharedLockHandle
**
** Either winShmNode.mutex must be held or winShmNode.pWinShmList==0 and
** winShmMutexHeld() is true when reading or writing any other field
** in this structure.
**
** File-handle hSharedShm is always used to (a) take the DMS lock, (b)
** truncate the *-shm file if the DMS-locking protocol demands it, and
** (c) map regions of the *-shm file into memory using MapViewOfFile()
** or similar. If bUseSharedLockHandle is true, then other locks are also
** taken on hSharedShm. Or, if bUseSharedLockHandle is false, then other
** locks are taken using each connection's winShm.hShm handles.
*/
struct winShmNode {
sqlite3_mutex *mutex; /* Mutex to access this object */
char *zFilename; /* Name of the file */
HANDLE hSharedShm; /* File handle open on zFilename */
int bUseSharedLockHandle; /* True to use hSharedShm for everything */
int isUnlocked; /* DMS lock has not yet been obtained */
int isReadonly; /* True if read-only */
int szRegion; /* Size of shared-memory regions */
int nRegion; /* Size of array apRegion */
struct ShmRegion {
HANDLE hMap; /* File handle from CreateFileMapping */
void *pMap;
} *aRegion;
DWORD lastErrno; /* The Windows errno from the last I/O error */
winShm *pWinShmList; /* List of winShm objects with ptrs to this */
winShmNode *pNext; /* Next in list of all winShmNode objects */
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
u8 nextShmId; /* Next available winShm.id value */
#endif
};
/*
** A global array of all winShmNode objects.
**
** The winShmMutexHeld() must be true while reading or writing this list.
*/
static winShmNode *winShmNodeList = 0;
/*
** Structure used internally by this VFS to record the state of an
** open shared memory connection. There is one such structure for each
** winFile open on a wal mode database.
*/
struct winShm {
winShmNode *pShmNode; /* The underlying winShmNode object */
u16 sharedMask; /* Mask of shared locks held */
u16 exclMask; /* Mask of exclusive locks held */
HANDLE hShm; /* File-handle on *-shm file. For locking. */
int bReadonly; /* True if hShm is opened read-only */
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
u8 id; /* Id of this connection with its winShmNode */
#endif
winShm *pWinShmNext; /* Next winShm object on same winShmNode */
};
/*
** Constants used for locking
*/
#define WIN_SHM_BASE ((22+SQLITE_SHM_NLOCK)*4) /* first lock byte */
#define WIN_SHM_DMS (WIN_SHM_BASE+SQLITE_SHM_NLOCK) /* deadman switch */
/* Forward references to VFS methods */
static int winOpen(sqlite3_vfs*,const char*,sqlite3_file*,int,int*);
static int winDelete(sqlite3_vfs *,const char*,int);
/*
** Purge the winShmNodeList list of all entries with winShmNode.pWinShmList==0.
**
** This is not a VFS shared-memory method; it is a utility function called
** by VFS shared-memory methods.
*/
static void winShmPurge(sqlite3_vfs *pVfs, int deleteFlag){
winShmNode **pp;
winShmNode *p;
assert( winShmMutexHeld() );
OSTRACE(("SHM-PURGE pid=%lu, deleteFlag=%d\n",
osGetCurrentProcessId(), deleteFlag));
pp = &winShmNodeList;
while( (p = *pp)!=0 ){
if( p->pWinShmList==0 ){
int i;
if( p->mutex ){ sqlite3_mutex_free(p->mutex); }
for(i=0; i<p->nRegion; i++){
BOOL bRc = osUnmapViewOfFile(p->aRegion[i].pMap);
OSTRACE(("SHM-PURGE-UNMAP pid=%lu, region=%d, rc=%s\n",
osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
UNUSED_VARIABLE_VALUE(bRc);
bRc = osCloseHandle(p->aRegion[i].hMap);
OSTRACE(("SHM-PURGE-CLOSE pid=%lu, region=%d, rc=%s\n",
osGetCurrentProcessId(), i, bRc ? "ok" : "failed"));
UNUSED_VARIABLE_VALUE(bRc);
}
winHandleClose(p->hSharedShm);
if( deleteFlag ){
SimulateIOErrorBenign(1);
sqlite3BeginBenignMalloc();
winDelete(pVfs, p->zFilename, 0);
sqlite3EndBenignMalloc();
SimulateIOErrorBenign(0);
}
*pp = p->pNext;
sqlite3_free(p->aRegion);
sqlite3_free(p);
}else{
pp = &p->pNext;
}
}
}
/*
** The DMS lock has not yet been taken on the shm file associated with
** pShmNode. Take the lock. Truncate the *-shm file if required.
** Return SQLITE_OK if successful, or an SQLite error code otherwise.
*/
static int winLockSharedMemory(winShmNode *pShmNode, DWORD nMs){
HANDLE h = pShmNode->hSharedShm;
int rc = SQLITE_OK;
assert( sqlite3_mutex_held(pShmNode->mutex) );
rc = winHandleLockTimeout(h, WIN_SHM_DMS, 1, 1, 0);
if( rc==SQLITE_OK ){
/* We have an EXCLUSIVE lock on the DMS byte. This means that this
** is the first process to open the file. Truncate it to zero bytes
** in this case. */
if( pShmNode->isReadonly ){
rc = SQLITE_READONLY_CANTINIT;
}else{
rc = winHandleTruncate(h, 0);
}
/* Release the EXCLUSIVE lock acquired above. */
winUnlockFile(&h, WIN_SHM_DMS, 0, 1, 0);
}else if( (rc & 0xFF)==SQLITE_BUSY ){
rc = SQLITE_OK;
}
if( rc==SQLITE_OK ){
/* Take a SHARED lock on the DMS byte. */
rc = winHandleLockTimeout(h, WIN_SHM_DMS, 1, 0, nMs);
if( rc==SQLITE_OK ){
pShmNode->isUnlocked = 0;
}
}
return rc;
}
/*
** This function is used to open a handle on a *-shm file.
**
** If SQLITE_ENABLE_SETLK_TIMEOUT is defined at build time, then the file
** is opened with FILE_FLAG_OVERLAPPED specified. If not, it is not.
*/
|
| ︙ | ︙ | |||
51577 51578 51579 51580 51581 51582 51583 51584 51585 51586 51587 51588 51589 51590 |
winopenfile_out:
sqlite3_free(zConverted);
*pbReadonly = bReadonly;
*ph = h;
return rc;
}
/*
** Open the shared-memory area associated with database file pDbFd.
*/
static int winOpenSharedMemory(winFile *pDbFd){
struct winShm *p; /* The connection to be opened */
winShmNode *pShmNode = 0; /* The underlying mmapped file */
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 51908 51909 51910 51911 51912 51913 51914 51915 51916 51917 51918 51919 51920 51921 51922 51923 51924 51925 51926 51927 51928 51929 51930 51931 51932 51933 51934 51935 51936 51937 51938 51939 51940 51941 51942 51943 51944 51945 51946 51947 51948 51949 51950 51951 51952 51953 51954 51955 51956 51957 51958 51959 51960 51961 51962 51963 51964 51965 51966 51967 51968 51969 51970 51971 51972 51973 51974 51975 |
winopenfile_out:
sqlite3_free(zConverted);
*pbReadonly = bReadonly;
*ph = h;
return rc;
}
/*
** Close pDbFd's connection to shared-memory. Delete the underlying
** *-shm file if deleteFlag is true.
*/
static int winCloseSharedMemory(winFile *pDbFd, int deleteFlag){
winShm *p; /* The connection to be closed */
winShm **pp; /* Iterator for pShmNode->pWinShmList */
winShmNode *pShmNode; /* The underlying shared-memory file */
p = pDbFd->pShm;
if( p==0 ) return SQLITE_OK;
if( p->hShm!=INVALID_HANDLE_VALUE ){
osCloseHandle(p->hShm);
}
winShmEnterMutex();
pShmNode = p->pShmNode;
/* Remove this connection from the winShmNode.pWinShmList list */
sqlite3_mutex_enter(pShmNode->mutex);
for(pp=&pShmNode->pWinShmList; *pp!=p; pp=&(*pp)->pWinShmNext){}
*pp = p->pWinShmNext;
sqlite3_mutex_leave(pShmNode->mutex);
winShmPurge(pDbFd->pVfs, deleteFlag);
winShmLeaveMutex();
/* Free the connection p */
sqlite3_free(p);
pDbFd->pShm = 0;
return SQLITE_OK;
}
/*
** testfixture builds may set this global variable to true via a
** Tcl interface. This forces the VFS to use the locking normally
** only used for UNC paths for all files.
*/
#ifdef SQLITE_TEST
SQLITE_API int sqlite3_win_test_unc_locking = 0;
#else
# define sqlite3_win_test_unc_locking 0
#endif
/*
** Return true if the string passed as the only argument is likely
** to be a UNC path. In other words, if it starts with "\\".
*/
static int winIsUNCPath(const char *zFile){
if( zFile[0]=='\\' && zFile[1]=='\\' ){
return 1;
}
return sqlite3_win_test_unc_locking;
}
/*
** Open the shared-memory area associated with database file pDbFd.
*/
static int winOpenSharedMemory(winFile *pDbFd){
struct winShm *p; /* The connection to be opened */
winShmNode *pShmNode = 0; /* The underlying mmapped file */
|
| ︙ | ︙ | |||
51603 51604 51605 51606 51607 51608 51609 51610 51611 51612 |
if( pNew==0 ){
sqlite3_free(p);
return SQLITE_IOERR_NOMEM_BKPT;
}
pNew->zFilename = (char*)&pNew[1];
pNew->hSharedShm = INVALID_HANDLE_VALUE;
pNew->isUnlocked = 1;
sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath);
sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename);
| > < < < < < < | | 51988 51989 51990 51991 51992 51993 51994 51995 51996 51997 51998 51999 52000 52001 52002 52003 52004 52005 52006 52007 52008 52009 52010 52011 52012 52013 52014 52015 52016 52017 52018 52019 52020 52021 52022 52023 52024 52025 52026 |
if( pNew==0 ){
sqlite3_free(p);
return SQLITE_IOERR_NOMEM_BKPT;
}
pNew->zFilename = (char*)&pNew[1];
pNew->hSharedShm = INVALID_HANDLE_VALUE;
pNew->isUnlocked = 1;
pNew->bUseSharedLockHandle = winIsUNCPath(pDbFd->zPath);
sqlite3_snprintf(nName+15, pNew->zFilename, "%s-shm", pDbFd->zPath);
sqlite3FileSuffix3(pDbFd->zPath, pNew->zFilename);
/* Look to see if there is an existing winShmNode that can be used.
** If no matching winShmNode currently exists, then create a new one. */
winShmEnterMutex();
for(pShmNode = winShmNodeList; pShmNode; pShmNode=pShmNode->pNext){
/* TBD need to come up with better match here. Perhaps
** use FILE_ID_BOTH_DIR_INFO Structure. */
if( sqlite3StrICmp(pShmNode->zFilename, pNew->zFilename)==0 ) break;
}
if( pShmNode==0 ){
pShmNode = pNew;
/* Allocate a mutex for this winShmNode object, if one is required. */
if( sqlite3GlobalConfig.bCoreMutex ){
pShmNode->mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
if( pShmNode->mutex==0 ) rc = SQLITE_IOERR_NOMEM_BKPT;
}
/* Open a file-handle to use for mappings, and for the DMS lock. */
if( rc==SQLITE_OK ){
HANDLE h = INVALID_HANDLE_VALUE;
pShmNode->isReadonly = sqlite3_uri_boolean(pDbFd->zPath,"readonly_shm",0);
rc = winHandleOpen(pNew->zFilename, &pShmNode->isReadonly, &h);
pShmNode->hSharedShm = h;
}
/* If successful, link the new winShmNode into the global list. If an
** error occurred, free the object. */
if( rc==SQLITE_OK ){
|
| ︙ | ︙ | |||
51654 51655 51656 51657 51658 51659 51660 51661 |
}
}
}
/* If no error has occurred, link the winShm object to the winShmNode and
** the winShm to pDbFd. */
if( rc==SQLITE_OK ){
p->pShmNode = pShmNode;
| > > | > < > > > > > > > > > > > > > < < < | < < < < < < < < < < < < < < < < < < < < < < < | 52034 52035 52036 52037 52038 52039 52040 52041 52042 52043 52044 52045 52046 52047 52048 52049 52050 52051 52052 52053 52054 52055 52056 52057 52058 52059 52060 52061 52062 52063 52064 52065 52066 52067 52068 52069 52070 52071 52072 52073 52074 52075 52076 52077 52078 52079 52080 52081 52082 52083 52084 52085 52086 52087 52088 |
}
}
}
/* If no error has occurred, link the winShm object to the winShmNode and
** the winShm to pDbFd. */
if( rc==SQLITE_OK ){
sqlite3_mutex_enter(pShmNode->mutex);
p->pShmNode = pShmNode;
p->pWinShmNext = pShmNode->pWinShmList;
pShmNode->pWinShmList = p;
#if defined(SQLITE_DEBUG) || defined(SQLITE_HAVE_OS_TRACE)
p->id = pShmNode->nextShmId++;
#endif
pDbFd->pShm = p;
sqlite3_mutex_leave(pShmNode->mutex);
}else if( p ){
sqlite3_free(p);
}
assert( rc!=SQLITE_OK || pShmNode->isUnlocked==0 || pShmNode->nRegion==0 );
winShmLeaveMutex();
sqlite3_free(pNew);
/* Open a file-handle on the *-shm file for this connection. This file-handle
** is only used for locking. The mapping of the *-shm file is created using
** the shared file handle in winShmNode.hSharedShm. */
if( rc==SQLITE_OK && pShmNode->bUseSharedLockHandle==0 ){
p->bReadonly = sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0);
rc = winHandleOpen(pShmNode->zFilename, &p->bReadonly, &p->hShm);
if( rc!=SQLITE_OK ){
assert( p->hShm==INVALID_HANDLE_VALUE );
winCloseSharedMemory(pDbFd, 0);
}
}
return rc;
}
/*
** Close a connection to shared-memory. Delete the underlying
** storage if deleteFlag is true.
*/
static int winShmUnmap(
sqlite3_file *fd, /* Database holding shared memory */
int deleteFlag /* Delete after closing if true */
){
return winCloseSharedMemory((winFile*)fd, deleteFlag);
}
/*
** Change the lock state for a shared-memory segment.
*/
static int winShmLock(
sqlite3_file *fd, /* Database file holding the shared memory */
|
| ︙ | ︙ | |||
51774 51775 51776 51777 51778 51779 51780 51781 51782 51783 51784 51785 51786 51787 51788 |
assert( flags!=(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK)
|| 0==(p->exclMask & mask)
);
if( ((flags & SQLITE_SHM_UNLOCK) && ((p->exclMask|p->sharedMask) & mask))
|| (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask))
|| (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK))
){
if( flags & SQLITE_SHM_UNLOCK ){
/* Case (a) - unlock. */
assert( (p->exclMask & p->sharedMask)==0 );
assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask );
assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask );
| > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > | > | 52143 52144 52145 52146 52147 52148 52149 52150 52151 52152 52153 52154 52155 52156 52157 52158 52159 52160 52161 52162 52163 52164 52165 52166 52167 52168 52169 52170 52171 52172 52173 52174 52175 52176 52177 52178 52179 52180 52181 52182 52183 52184 52185 52186 52187 52188 52189 52190 52191 52192 52193 52194 52195 52196 52197 52198 52199 52200 52201 52202 52203 52204 52205 52206 52207 52208 52209 52210 52211 52212 52213 52214 52215 52216 52217 52218 52219 52220 52221 |
assert( flags!=(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK)
|| 0==(p->exclMask & mask)
);
if( ((flags & SQLITE_SHM_UNLOCK) && ((p->exclMask|p->sharedMask) & mask))
|| (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask))
|| (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK))
){
HANDLE h = p->hShm;
if( flags & SQLITE_SHM_UNLOCK ){
/* Case (a) - unlock. */
assert( (p->exclMask & p->sharedMask)==0 );
assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask );
assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask );
assert( !(flags & SQLITE_SHM_SHARED) || n==1 );
if( pShmNode->bUseSharedLockHandle ){
h = pShmNode->hSharedShm;
if( flags & SQLITE_SHM_SHARED ){
winShm *pShm;
sqlite3_mutex_enter(pShmNode->mutex);
for(pShm=pShmNode->pWinShmList; pShm; pShm=pShm->pWinShmNext){
if( pShm!=p && (pShm->sharedMask & mask) ){
/* Another connection within this process is also holding this
** SHARED lock. So do not actually release the OS lock. */
h = INVALID_HANDLE_VALUE;
break;
}
}
sqlite3_mutex_leave(pShmNode->mutex);
}
}
if( h!=INVALID_HANDLE_VALUE ){
rc = winHandleUnlock(h, ofst+WIN_SHM_BASE, n);
}
/* If successful, also clear the bits in sharedMask/exclMask */
if( rc==SQLITE_OK ){
p->exclMask = (p->exclMask & ~mask);
p->sharedMask = (p->sharedMask & ~mask);
}
}else{
int bExcl = ((flags & SQLITE_SHM_EXCLUSIVE) ? 1 : 0);
DWORD nMs = winFileBusyTimeout(pDbFd);
if( pShmNode->bUseSharedLockHandle ){
winShm *pShm;
h = pShmNode->hSharedShm;
sqlite3_mutex_enter(pShmNode->mutex);
for(pShm=pShmNode->pWinShmList; pShm; pShm=pShm->pWinShmNext){
if( bExcl ){
if( (pShm->sharedMask|pShm->exclMask) & mask ){
rc = SQLITE_BUSY;
h = INVALID_HANDLE_VALUE;
}
}else{
if( pShm->sharedMask & mask ){
h = INVALID_HANDLE_VALUE;
}else if( pShm->exclMask & mask ){
rc = SQLITE_BUSY;
h = INVALID_HANDLE_VALUE;
}
}
}
sqlite3_mutex_leave(pShmNode->mutex);
}
if( h!=INVALID_HANDLE_VALUE ){
rc = winHandleLockTimeout(h, ofst+WIN_SHM_BASE, n, bExcl, nMs);
}
if( rc==SQLITE_OK ){
if( bExcl ){
p->exclMask = (p->exclMask | mask);
}else{
p->sharedMask = (p->sharedMask | mask);
}
}
|
| ︙ | ︙ | |||
61823 61824 61825 61826 61827 61828 61829 |
** and FULL=3.
*/
SQLITE_PRIVATE void sqlite3PagerSetFlags(
Pager *pPager, /* The pager to set safety level for */
unsigned pgFlags /* Various flags */
){
unsigned level = pgFlags & PAGER_SYNCHRONOUS_MASK;
| | | > > > > > > > > > > | > > > | 62238 62239 62240 62241 62242 62243 62244 62245 62246 62247 62248 62249 62250 62251 62252 62253 62254 62255 62256 62257 62258 62259 62260 62261 62262 62263 62264 62265 62266 62267 62268 62269 62270 62271 62272 |
** and FULL=3.
*/
SQLITE_PRIVATE void sqlite3PagerSetFlags(
Pager *pPager, /* The pager to set safety level for */
unsigned pgFlags /* Various flags */
){
unsigned level = pgFlags & PAGER_SYNCHRONOUS_MASK;
if( pPager->tempFile || level==PAGER_SYNCHRONOUS_OFF ){
pPager->noSync = 1;
pPager->fullSync = 0;
pPager->extraSync = 0;
}else{
pPager->noSync = 0;
pPager->fullSync = level>=PAGER_SYNCHRONOUS_FULL ?1:0;
/* Set Pager.extraSync if "PRAGMA synchronous=EXTRA" is requested, or
** if the file-system supports F2FS style atomic writes. If this flag
** is set, SQLite syncs the directory to disk immediately after deleting
** a journal file in "PRAGMA journal_mode=DELETE" mode. */
if( level==PAGER_SYNCHRONOUS_EXTRA
#ifdef SQLITE_ENABLE_BATCH_ATOMIC_WRITE
|| (sqlite3OsDeviceCharacteristics(pPager->fd) & SQLITE_IOCAP_BATCH_ATOMIC)
#endif
){
pPager->extraSync = 1;
}else{
pPager->extraSync = 0;
}
}
if( pPager->noSync ){
pPager->syncFlags = 0;
}else if( pgFlags & PAGER_FULLFSYNC ){
pPager->syncFlags = SQLITE_SYNC_FULL;
}else{
pPager->syncFlags = SQLITE_SYNC_NORMAL;
|
| ︙ | ︙ | |||
65723 65724 65725 65726 65727 65728 65729 |
** sqlite3_wal_checkpoint() call, but it happens very rarely.
** https://sqlite.org/forum/forumpost/fd0f19d229156939
*/
sqlite3_exec(db, "PRAGMA table_list",0,0,0);
}
if( pPager->pWal ){
rc = sqlite3WalCheckpoint(pPager->pWal, db, eMode,
| | | 66151 66152 66153 66154 66155 66156 66157 66158 66159 66160 66161 66162 66163 66164 66165 |
** sqlite3_wal_checkpoint() call, but it happens very rarely.
** https://sqlite.org/forum/forumpost/fd0f19d229156939
*/
sqlite3_exec(db, "PRAGMA table_list",0,0,0);
}
if( pPager->pWal ){
rc = sqlite3WalCheckpoint(pPager->pWal, db, eMode,
(eMode<=SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler),
pPager->pBusyHandlerArg,
pPager->walSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace,
pnLog, pnCkpt
);
}
return rc;
}
|
| ︙ | ︙ | |||
70328 70329 70330 70331 70332 70333 70334 | int (*xBusy2)(void*) = xBusy; /* Busy handler for eMode2 */ assert( pWal->ckptLock==0 ); assert( pWal->writeLock==0 ); /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked ** in the SQLITE_CHECKPOINT_PASSIVE mode. */ | > | > | | | | | | | | | | | | | | | | | | | | | | | | > > > | | | 70756 70757 70758 70759 70760 70761 70762 70763 70764 70765 70766 70767 70768 70769 70770 70771 70772 70773 70774 70775 70776 70777 70778 70779 70780 70781 70782 70783 70784 70785 70786 70787 70788 70789 70790 70791 70792 70793 70794 70795 70796 70797 70798 70799 70800 70801 70802 70803 70804 70805 70806 70807 70808 70809 70810 70811 70812 70813 70814 70815 70816 70817 70818 70819 70820 70821 70822 70823 70824 70825 70826 70827 70828 70829 70830 70831 70832 70833 70834 70835 70836 70837 70838 70839 70840 |
int (*xBusy2)(void*) = xBusy; /* Busy handler for eMode2 */
assert( pWal->ckptLock==0 );
assert( pWal->writeLock==0 );
/* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked
** in the SQLITE_CHECKPOINT_PASSIVE mode. */
assert( SQLITE_CHECKPOINT_NOOP<SQLITE_CHECKPOINT_PASSIVE );
assert( eMode>SQLITE_CHECKPOINT_PASSIVE || xBusy==0 );
if( pWal->readOnly ) return SQLITE_READONLY;
WALTRACE(("WAL%p: checkpoint begins\n", pWal));
/* Enable blocking locks, if possible. */
sqlite3WalDb(pWal, db);
if( xBusy2 ) (void)walEnableBlocking(pWal);
/* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive
** "checkpoint" lock on the database file.
** EVIDENCE-OF: R-10421-19736 If any other process is running a
** checkpoint operation at the same time, the lock cannot be obtained and
** SQLITE_BUSY is returned.
** EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured,
** it will not be invoked in this case.
*/
if( eMode!=SQLITE_CHECKPOINT_NOOP ){
rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
testcase( rc==SQLITE_BUSY );
testcase( rc!=SQLITE_OK && xBusy2!=0 );
if( rc==SQLITE_OK ){
pWal->ckptLock = 1;
/* IMPLEMENTATION-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART
** and TRUNCATE modes also obtain the exclusive "writer" lock on the
** database file.
**
** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained
** immediately, and a busy-handler is configured, it is invoked and the
** writer lock retried until either the busy-handler returns 0 or the
** lock is successfully obtained.
*/
if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
rc = walBusyLock(pWal, xBusy2, pBusyArg, WAL_WRITE_LOCK, 1);
if( rc==SQLITE_OK ){
pWal->writeLock = 1;
}else if( rc==SQLITE_BUSY ){
eMode2 = SQLITE_CHECKPOINT_PASSIVE;
xBusy2 = 0;
rc = SQLITE_OK;
}
}
}
}else{
rc = SQLITE_OK;
}
/* Read the wal-index header. */
SEH_TRY {
if( rc==SQLITE_OK ){
/* For a passive checkpoint, do not re-enable blocking locks after
** reading the wal-index header. A passive checkpoint should not block
** or invoke the busy handler. The only lock such a checkpoint may
** attempt to obtain is a lock on a read-slot, and it should give up
** immediately and do a partial checkpoint if it cannot obtain it. */
walDisableBlocking(pWal);
rc = walIndexReadHdr(pWal, &isChanged);
if( eMode2>SQLITE_CHECKPOINT_PASSIVE ) (void)walEnableBlocking(pWal);
if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){
sqlite3OsUnfetch(pWal->pDbFd, 0, 0);
}
}
/* Copy data from the log to the database file. */
if( rc==SQLITE_OK ){
if( pWal->hdr.mxFrame && walPagesize(pWal)!=nBuf ){
rc = SQLITE_CORRUPT_BKPT;
}else if( eMode2!=SQLITE_CHECKPOINT_NOOP ){
rc = walCheckpoint(pWal, db, eMode2, xBusy2, pBusyArg, sync_flags,zBuf);
}
/* If no error occurred, set the output variables. */
if( rc==SQLITE_OK || rc==SQLITE_BUSY ){
if( pnLog ) *pnLog = (int)pWal->hdr.mxFrame;
SEH_INJECT_FAULT;
|
| ︙ | ︙ | |||
89076 89077 89078 89079 89080 89081 89082 |
** string, it means the main database is :memory: or a temp file. In
** that case we do not support atomic multi-file commits, so use the
** simple case then too.
*/
if( 0==sqlite3Strlen30(sqlite3BtreeGetFilename(db->aDb[0].pBt))
|| nTrans<=1
){
| > | | | | > > | > | 89509 89510 89511 89512 89513 89514 89515 89516 89517 89518 89519 89520 89521 89522 89523 89524 89525 89526 89527 89528 89529 89530 89531 89532 89533 89534 89535 89536 89537 89538 89539 89540 89541 |
** string, it means the main database is :memory: or a temp file. In
** that case we do not support atomic multi-file commits, so use the
** simple case then too.
*/
if( 0==sqlite3Strlen30(sqlite3BtreeGetFilename(db->aDb[0].pBt))
|| nTrans<=1
){
if( needXcommit ){
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
if( sqlite3BtreeTxnState(pBt)>=SQLITE_TXN_WRITE ){
rc = sqlite3BtreeCommitPhaseOne(pBt, 0);
}
}
}
/* Do the commit only if all databases successfully complete phase 1.
** If one of the BtreeCommitPhaseOne() calls fails, this indicates an
** IO error while deleting or truncating a journal file. It is unlikely,
** but could happen. In this case abandon processing and return the error.
*/
for(i=0; rc==SQLITE_OK && i<db->nDb; i++){
Btree *pBt = db->aDb[i].pBt;
int txn = sqlite3BtreeTxnState(pBt);
if( txn!=SQLITE_TXN_NONE ){
assert( needXcommit || txn==SQLITE_TXN_READ );
rc = sqlite3BtreeCommitPhaseTwo(pBt, 0);
}
}
if( rc==SQLITE_OK ){
sqlite3VtabCommit(db);
}
}
|
| ︙ | ︙ | |||
89345 89346 89347 89348 89349 89350 89351 |
return vdbeCloseStatement(p, eOp);
}
return SQLITE_OK;
}
/*
| | | | | | | < < < < | | | | | | > | > > > > > > | 89782 89783 89784 89785 89786 89787 89788 89789 89790 89791 89792 89793 89794 89795 89796 89797 89798 89799 89800 89801 89802 89803 89804 89805 89806 89807 89808 89809 89810 89811 89812 89813 89814 89815 89816 89817 89818 89819 89820 |
return vdbeCloseStatement(p, eOp);
}
return SQLITE_OK;
}
/*
** These functions are called when a transaction opened by the database
** handle associated with the VM passed as an argument is about to be
** committed. If there are outstanding foreign key constraint violations
** return an error code. Otherwise, SQLITE_OK.
**
** If there are outstanding FK violations and this function returns
** non-zero, set the result of the VM to SQLITE_CONSTRAINT_FOREIGNKEY
** and write an error message to it.
*/
#ifndef SQLITE_OMIT_FOREIGN_KEY
static SQLITE_NOINLINE int vdbeFkError(Vdbe *p){
p->rc = SQLITE_CONSTRAINT_FOREIGNKEY;
p->errorAction = OE_Abort;
sqlite3VdbeError(p, "FOREIGN KEY constraint failed");
if( (p->prepFlags & SQLITE_PREPARE_SAVESQL)==0 ) return SQLITE_ERROR;
return SQLITE_CONSTRAINT_FOREIGNKEY;
}
SQLITE_PRIVATE int sqlite3VdbeCheckFkImmediate(Vdbe *p){
if( p->nFkConstraint==0 ) return SQLITE_OK;
return vdbeFkError(p);
}
SQLITE_PRIVATE int sqlite3VdbeCheckFkDeferred(Vdbe *p){
sqlite3 *db = p->db;
if( (db->nDeferredCons+db->nDeferredImmCons)==0 ) return SQLITE_OK;
return vdbeFkError(p);
}
#endif
/*
** This routine is called the when a VDBE tries to halt. If the VDBE
** has made changes and is in autocommit mode, then commit those
** changes. If a rollback is needed, then do the rollback.
|
| ︙ | ︙ | |||
89460 89461 89462 89463 89464 89465 89466 |
p->nChange = 0;
}
}
}
/* Check for immediate foreign key violations. */
if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
| | | | 89900 89901 89902 89903 89904 89905 89906 89907 89908 89909 89910 89911 89912 89913 89914 89915 89916 89917 89918 89919 89920 89921 89922 89923 89924 89925 89926 89927 89928 |
p->nChange = 0;
}
}
}
/* Check for immediate foreign key violations. */
if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
(void)sqlite3VdbeCheckFkImmediate(p);
}
/* If the auto-commit flag is set and this is the only active writer
** VM, then we do either a commit or rollback of the current transaction.
**
** Note: This block also runs if one of the special errors handled
** above has occurred.
*/
if( !sqlite3VtabInSync(db)
&& db->autoCommit
&& db->nVdbeWrite==(p->readOnly==0)
){
if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){
rc = sqlite3VdbeCheckFkDeferred(p);
if( rc!=SQLITE_OK ){
if( NEVER(p->readOnly) ){
sqlite3VdbeLeave(p);
return SQLITE_ERROR;
}
rc = SQLITE_CONSTRAINT_FOREIGNKEY;
}else if( db->flags & SQLITE_CorruptRdOnly ){
|
| ︙ | ︙ | |||
90339 90340 90341 90342 90343 90344 90345 |
pMem->enc = pKeyInfo->enc;
pMem->db = pKeyInfo->db;
/* pMem->flags = 0; // sqlite3VdbeSerialGet() will set this for us */
pMem->szMalloc = 0;
pMem->z = 0;
sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem);
d += sqlite3VdbeSerialTypeLen(serial_type);
| < > | | 90779 90780 90781 90782 90783 90784 90785 90786 90787 90788 90789 90790 90791 90792 90793 90794 90795 90796 90797 90798 90799 90800 90801 |
pMem->enc = pKeyInfo->enc;
pMem->db = pKeyInfo->db;
/* pMem->flags = 0; // sqlite3VdbeSerialGet() will set this for us */
pMem->szMalloc = 0;
pMem->z = 0;
sqlite3VdbeSerialGet(&aKey[d], serial_type, pMem);
d += sqlite3VdbeSerialTypeLen(serial_type);
if( (++u)>=p->nField ) break;
pMem++;
}
if( d>(u32)nKey && u ){
assert( CORRUPT_DB );
/* In a corrupt record entry, the last pMem might have been set up using
** uninitialized memory. Overwrite its value with NULL, to prevent
** warnings from MSAN. */
sqlite3VdbeMemSetNull(pMem-(u<p->nField));
}
testcase( u == pKeyInfo->nKeyField + 1 );
testcase( u < pKeyInfo->nKeyField + 1 );
assert( u<=pKeyInfo->nKeyField + 1 );
p->nField = u;
}
|
| ︙ | ︙ | |||
90518 90519 90520 90521 90522 90523 90524 90525 90526 90527 90528 90529 90530 90531 90532 90533 90534 90535 |
/*
** Both *pMem1 and *pMem2 contain string values. Compare the two values
** using the collation sequence pColl. As usual, return a negative , zero
** or positive value if *pMem1 is less than, equal to or greater than
** *pMem2, respectively. Similar in spirit to "rc = (*pMem1) - (*pMem2);".
*/
static int vdbeCompareMemString(
const Mem *pMem1,
const Mem *pMem2,
const CollSeq *pColl,
u8 *prcErr /* If an OOM occurs, set to SQLITE_NOMEM */
){
if( pMem1->enc==pColl->enc ){
/* The strings are already in the correct encoding. Call the
** comparison function directly */
return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z);
}else{
| > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < < < < < < < < | | 90958 90959 90960 90961 90962 90963 90964 90965 90966 90967 90968 90969 90970 90971 90972 90973 90974 90975 90976 90977 90978 90979 90980 90981 90982 90983 90984 90985 90986 90987 90988 90989 90990 90991 90992 90993 90994 90995 90996 90997 90998 90999 91000 91001 91002 91003 91004 91005 91006 91007 91008 91009 |
/*
** Both *pMem1 and *pMem2 contain string values. Compare the two values
** using the collation sequence pColl. As usual, return a negative , zero
** or positive value if *pMem1 is less than, equal to or greater than
** *pMem2, respectively. Similar in spirit to "rc = (*pMem1) - (*pMem2);".
*/
static SQLITE_NOINLINE int vdbeCompareMemStringWithEncodingChange(
const Mem *pMem1,
const Mem *pMem2,
const CollSeq *pColl,
u8 *prcErr /* If an OOM occurs, set to SQLITE_NOMEM */
){
int rc;
const void *v1, *v2;
Mem c1;
Mem c2;
sqlite3VdbeMemInit(&c1, pMem1->db, MEM_Null);
sqlite3VdbeMemInit(&c2, pMem1->db, MEM_Null);
sqlite3VdbeMemShallowCopy(&c1, pMem1, MEM_Ephem);
sqlite3VdbeMemShallowCopy(&c2, pMem2, MEM_Ephem);
v1 = sqlite3ValueText((sqlite3_value*)&c1, pColl->enc);
v2 = sqlite3ValueText((sqlite3_value*)&c2, pColl->enc);
if( (v1==0 || v2==0) ){
if( prcErr ) *prcErr = SQLITE_NOMEM_BKPT;
rc = 0;
}else{
rc = pColl->xCmp(pColl->pUser, c1.n, v1, c2.n, v2);
}
sqlite3VdbeMemReleaseMalloc(&c1);
sqlite3VdbeMemReleaseMalloc(&c2);
return rc;
}
static int vdbeCompareMemString(
const Mem *pMem1,
const Mem *pMem2,
const CollSeq *pColl,
u8 *prcErr /* If an OOM occurs, set to SQLITE_NOMEM */
){
if( pMem1->enc==pColl->enc ){
/* The strings are already in the correct encoding. Call the
** comparison function directly */
return pColl->xCmp(pColl->pUser,pMem1->n,pMem1->z,pMem2->n,pMem2->z);
}else{
return vdbeCompareMemStringWithEncodingChange(pMem1,pMem2,pColl,prcErr);
}
}
/*
** The input pBlob is guaranteed to be a Blob that is not marked
** with MEM_Zero. Return true if it could be a zero-blob.
*/
|
| ︙ | ︙ | |||
91605 91606 91607 91608 91609 91610 91611 |
|| (pCsr->nField==nRealCol+1 && op==SQLITE_DELETE && iReg==-1)
);
preupdate.v = v;
preupdate.pCsr = pCsr;
preupdate.op = op;
preupdate.iNewReg = iReg;
| | | 92053 92054 92055 92056 92057 92058 92059 92060 92061 92062 92063 92064 92065 92066 92067 |
|| (pCsr->nField==nRealCol+1 && op==SQLITE_DELETE && iReg==-1)
);
preupdate.v = v;
preupdate.pCsr = pCsr;
preupdate.op = op;
preupdate.iNewReg = iReg;
preupdate.pKeyinfo = &preupdate.uKey.sKey;
preupdate.pKeyinfo->db = db;
preupdate.pKeyinfo->enc = ENC(db);
preupdate.pKeyinfo->nKeyField = pTab->nCol;
preupdate.pKeyinfo->aSortFlags = 0; /* Indicate .aColl, .nAllField uninit */
preupdate.iKey1 = iKey1;
preupdate.iKey2 = iKey2;
preupdate.pTab = pTab;
|
| ︙ | ︙ | |||
91639 91640 91641 91642 91643 91644 91645 91646 91647 91648 91649 91650 91651 91652 |
sqlite3ValueFree(preupdate.apDflt[i]);
}
sqlite3DbFree(db, preupdate.apDflt);
}
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
/************** End of vdbeaux.c *********************************************/
/************** Begin file vdbeapi.c *****************************************/
/*
** 2004 May 26
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
| > > > > > > > > > > > | 92087 92088 92089 92090 92091 92092 92093 92094 92095 92096 92097 92098 92099 92100 92101 92102 92103 92104 92105 92106 92107 92108 92109 92110 92111 |
sqlite3ValueFree(preupdate.apDflt[i]);
}
sqlite3DbFree(db, preupdate.apDflt);
}
}
#endif /* SQLITE_ENABLE_PREUPDATE_HOOK */
#ifdef SQLITE_ENABLE_PERCENTILE
/*
** Return the name of an SQL function associated with the sqlite3_context.
*/
SQLITE_PRIVATE const char *sqlite3VdbeFuncName(const sqlite3_context *pCtx){
assert( pCtx!=0 );
assert( pCtx->pFunc!=0 );
return pCtx->pFunc->zName;
}
#endif /* SQLITE_ENABLE_PERCENTILE */
/************** End of vdbeaux.c *********************************************/
/************** Begin file vdbeapi.c *****************************************/
/*
** 2004 May 26
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
|
| ︙ | ︙ | |||
93336 93337 93338 93339 93340 93341 93342 |
rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */
if( zData!=0 ){
pVar = &p->aVar[i-1];
rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel);
| | > > > | > | 93795 93796 93797 93798 93799 93800 93801 93802 93803 93804 93805 93806 93807 93808 93809 93810 93811 93812 93813 93814 |
rc = vdbeUnbind(p, (u32)(i-1));
if( rc==SQLITE_OK ){
assert( p!=0 && p->aVar!=0 && i>0 && i<=p->nVar ); /* tag-20240917-01 */
if( zData!=0 ){
pVar = &p->aVar[i-1];
rc = sqlite3VdbeMemSetStr(pVar, zData, nData, encoding, xDel);
if( rc==SQLITE_OK ){
if( encoding==0 ){
pVar->enc = ENC(p->db);
}else{
rc = sqlite3VdbeChangeEncoding(pVar, ENC(p->db));
}
}
if( rc ){
sqlite3Error(p->db, rc);
rc = sqlite3ApiExit(p->db, rc);
}
}
sqlite3_mutex_leave(p->db->mutex);
|
| ︙ | ︙ | |||
95245 95246 95247 95248 95249 95250 95251 | ** the common case where all content fits on the page. Factoring out ** the code reduces register pressure and helps the common case ** to run faster. */ static SQLITE_NOINLINE int vdbeColumnFromOverflow( VdbeCursor *pC, /* The BTree cursor from which we are reading */ int iCol, /* The column to read */ | | | 95708 95709 95710 95711 95712 95713 95714 95715 95716 95717 95718 95719 95720 95721 95722 |
** the common case where all content fits on the page. Factoring out
** the code reduces register pressure and helps the common case
** to run faster.
*/
static SQLITE_NOINLINE int vdbeColumnFromOverflow(
VdbeCursor *pC, /* The BTree cursor from which we are reading */
int iCol, /* The column to read */
u32 t, /* The serial-type code for the column value */
i64 iOffset, /* Offset to the start of the content value */
u32 cacheStatus, /* Current Vdbe.cacheCtr value */
u32 colCacheCtr, /* Current value of the column cache counter */
Mem *pDest /* Store the value into this register. */
){
int rc;
sqlite3 *db = pDest->db;
|
| ︙ | ︙ | |||
96254 96255 96256 96257 96258 96259 96260 |
**
** FK constraint violations are also checked when the prepared statement
** exits. This opcode is used to raise foreign key constraint errors prior
** to returning results such as a row change count or the result of a
** RETURNING clause.
*/
case OP_FkCheck: {
| | | 96717 96718 96719 96720 96721 96722 96723 96724 96725 96726 96727 96728 96729 96730 96731 |
**
** FK constraint violations are also checked when the prepared statement
** exits. This opcode is used to raise foreign key constraint errors prior
** to returning results such as a row change count or the result of a
** RETURNING clause.
*/
case OP_FkCheck: {
if( (rc = sqlite3VdbeCheckFkImmediate(p))!=SQLITE_OK ){
goto abort_due_to_error;
}
break;
}
/* Opcode: ResultRow P1 P2 * * *
** Synopsis: output=r[P1@P2]
|
| ︙ | ︙ | |||
96346 96347 96348 96349 96350 96351 96352 |
if( (flags2 & (MEM_Str|MEM_Blob))==0 ){
if( sqlite3VdbeMemStringify(pIn2,encoding,0) ) goto no_mem;
flags2 = pIn2->flags & ~MEM_Str;
}else if( (flags2 & MEM_Zero)!=0 ){
if( sqlite3VdbeMemExpandBlob(pIn2) ) goto no_mem;
flags2 = pIn2->flags & ~MEM_Str;
}
| | > | 96809 96810 96811 96812 96813 96814 96815 96816 96817 96818 96819 96820 96821 96822 96823 96824 |
if( (flags2 & (MEM_Str|MEM_Blob))==0 ){
if( sqlite3VdbeMemStringify(pIn2,encoding,0) ) goto no_mem;
flags2 = pIn2->flags & ~MEM_Str;
}else if( (flags2 & MEM_Zero)!=0 ){
if( sqlite3VdbeMemExpandBlob(pIn2) ) goto no_mem;
flags2 = pIn2->flags & ~MEM_Str;
}
nByte = pIn1->n;
nByte += pIn2->n;
if( nByte>db->aLimit[SQLITE_LIMIT_LENGTH] ){
goto too_big;
}
if( sqlite3VdbeMemGrow(pOut, (int)nByte+2, pOut==pIn2) ){
goto no_mem;
}
MemSetTypeFlag(pOut, MEM_Str);
|
| ︙ | ︙ | |||
98171 98172 98173 98174 98175 98176 98177 |
pRec->uTemp = 7;
}else{
assert( db->mallocFailed || pRec->flags&(MEM_Str|MEM_Blob) );
assert( pRec->n>=0 );
len = (u32)pRec->n;
serial_type = (len*2) + 12 + ((pRec->flags & MEM_Str)!=0);
if( pRec->flags & MEM_Zero ){
| | | 98635 98636 98637 98638 98639 98640 98641 98642 98643 98644 98645 98646 98647 98648 98649 |
pRec->uTemp = 7;
}else{
assert( db->mallocFailed || pRec->flags&(MEM_Str|MEM_Blob) );
assert( pRec->n>=0 );
len = (u32)pRec->n;
serial_type = (len*2) + 12 + ((pRec->flags & MEM_Str)!=0);
if( pRec->flags & MEM_Zero ){
serial_type += (u32)pRec->u.nZero*2;
if( nData ){
if( sqlite3VdbeMemExpandBlob(pRec) ) goto no_mem;
len += pRec->u.nZero;
}else{
nZero += pRec->u.nZero;
}
}
|
| ︙ | ︙ | |||
98438 98439 98440 98441 98442 98443 98444 |
/* Determine whether or not this is a transaction savepoint. If so,
** and this is a RELEASE command, then the current transaction
** is committed.
*/
int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint;
if( isTransaction && p1==SAVEPOINT_RELEASE ){
| | | 98902 98903 98904 98905 98906 98907 98908 98909 98910 98911 98912 98913 98914 98915 98916 |
/* Determine whether or not this is a transaction savepoint. If so,
** and this is a RELEASE command, then the current transaction
** is committed.
*/
int isTransaction = pSavepoint->pNext==0 && db->isTransactionSavepoint;
if( isTransaction && p1==SAVEPOINT_RELEASE ){
if( (rc = sqlite3VdbeCheckFkDeferred(p))!=SQLITE_OK ){
goto vdbe_return;
}
db->autoCommit = 1;
if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
p->pc = (int)(pOp - aOp);
db->autoCommit = 0;
p->rc = rc = SQLITE_BUSY;
|
| ︙ | ︙ | |||
98556 98557 98558 98559 98560 98561 98562 |
/* If this instruction implements a COMMIT and other VMs are writing
** return an error indicating that the other VMs must complete first.
*/
sqlite3VdbeError(p, "cannot commit transaction - "
"SQL statements in progress");
rc = SQLITE_BUSY;
goto abort_due_to_error;
| | | 99020 99021 99022 99023 99024 99025 99026 99027 99028 99029 99030 99031 99032 99033 99034 |
/* If this instruction implements a COMMIT and other VMs are writing
** return an error indicating that the other VMs must complete first.
*/
sqlite3VdbeError(p, "cannot commit transaction - "
"SQL statements in progress");
rc = SQLITE_BUSY;
goto abort_due_to_error;
}else if( (rc = sqlite3VdbeCheckFkDeferred(p))!=SQLITE_OK ){
goto vdbe_return;
}else{
db->autoCommit = (u8)desiredAutoCommit;
}
if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){
p->pc = (int)(pOp - aOp);
db->autoCommit = (u8)(1-desiredAutoCommit);
|
| ︙ | ︙ | |||
102488 102489 102490 102491 102492 102493 102494 102495 102496 102497 102498 102499 102500 102501 |
assert( p->readOnly==0 );
aRes[0] = 0;
aRes[1] = aRes[2] = -1;
assert( pOp->p2==SQLITE_CHECKPOINT_PASSIVE
|| pOp->p2==SQLITE_CHECKPOINT_FULL
|| pOp->p2==SQLITE_CHECKPOINT_RESTART
|| pOp->p2==SQLITE_CHECKPOINT_TRUNCATE
);
rc = sqlite3Checkpoint(db, pOp->p1, pOp->p2, &aRes[1], &aRes[2]);
if( rc ){
if( rc!=SQLITE_BUSY ) goto abort_due_to_error;
rc = SQLITE_OK;
aRes[0] = 1;
}
| > | 102952 102953 102954 102955 102956 102957 102958 102959 102960 102961 102962 102963 102964 102965 102966 |
assert( p->readOnly==0 );
aRes[0] = 0;
aRes[1] = aRes[2] = -1;
assert( pOp->p2==SQLITE_CHECKPOINT_PASSIVE
|| pOp->p2==SQLITE_CHECKPOINT_FULL
|| pOp->p2==SQLITE_CHECKPOINT_RESTART
|| pOp->p2==SQLITE_CHECKPOINT_TRUNCATE
|| pOp->p2==SQLITE_CHECKPOINT_NOOP
);
rc = sqlite3Checkpoint(db, pOp->p1, pOp->p2, &aRes[1], &aRes[2]);
if( rc ){
if( rc!=SQLITE_BUSY ) goto abort_due_to_error;
rc = SQLITE_OK;
aRes[0] = 1;
}
|
| ︙ | ︙ | |||
104687 104688 104689 104690 104691 104692 104693 104694 104695 104696 104697 104698 104699 104700 | int nPMA; /* Number of PMAs currently in file */ VdbeSorter *pSorter; /* Sorter that owns this sub-task */ UnpackedRecord *pUnpacked; /* Space to unpack a record */ SorterList list; /* List for thread to write to a PMA */ SorterCompare xCompare; /* Compare function to use */ SorterFile file; /* Temp file for level-0 PMAs */ SorterFile file2; /* Space for other PMAs */ }; /* ** Main sorter structure. A single instance of this is allocated for each ** sorter cursor created by the VDBE. ** | > | 105152 105153 105154 105155 105156 105157 105158 105159 105160 105161 105162 105163 105164 105165 105166 | int nPMA; /* Number of PMAs currently in file */ VdbeSorter *pSorter; /* Sorter that owns this sub-task */ UnpackedRecord *pUnpacked; /* Space to unpack a record */ SorterList list; /* List for thread to write to a PMA */ SorterCompare xCompare; /* Compare function to use */ SorterFile file; /* Temp file for level-0 PMAs */ SorterFile file2; /* Space for other PMAs */ u64 nSpill; /* Total bytes written by this task */ }; /* ** Main sorter structure. A single instance of this is allocated for each ** sorter cursor created by the VDBE. ** |
| ︙ | ︙ | |||
104807 104808 104809 104810 104811 104812 104813 104814 104815 104816 104817 104818 104819 104820 | int eFWErr; /* Non-zero if in an error state */ u8 *aBuffer; /* Pointer to write buffer */ int nBuffer; /* Size of write buffer in bytes */ int iBufStart; /* First byte of buffer to write */ int iBufEnd; /* Last byte of buffer to write */ i64 iWriteOff; /* Offset of start of buffer in file */ sqlite3_file *pFd; /* File handle to write to */ }; /* ** This object is the header on a single record while that record is being ** held in memory and prior to being written out as part of a PMA. ** ** How the linked list is connected depends on how memory is being managed | > | 105273 105274 105275 105276 105277 105278 105279 105280 105281 105282 105283 105284 105285 105286 105287 | int eFWErr; /* Non-zero if in an error state */ u8 *aBuffer; /* Pointer to write buffer */ int nBuffer; /* Size of write buffer in bytes */ int iBufStart; /* First byte of buffer to write */ int iBufEnd; /* Last byte of buffer to write */ i64 iWriteOff; /* Offset of start of buffer in file */ sqlite3_file *pFd; /* File handle to write to */ u64 nPmaSpill; /* Total number of bytes written */ }; /* ** This object is the header on a single record while that record is being ** held in memory and prior to being written out as part of a PMA. ** ** How the linked list is connected depends on how memory is being managed |
| ︙ | ︙ | |||
105665 105666 105667 105668 105669 105670 105671 105672 105673 105674 105675 105676 105677 105678 |
** Free any cursor components allocated by sqlite3VdbeSorterXXX routines.
*/
SQLITE_PRIVATE void sqlite3VdbeSorterClose(sqlite3 *db, VdbeCursor *pCsr){
VdbeSorter *pSorter;
assert( pCsr->eCurType==CURTYPE_SORTER );
pSorter = pCsr->uc.pSorter;
if( pSorter ){
sqlite3VdbeSorterReset(db, pSorter);
sqlite3_free(pSorter->list.aMemory);
sqlite3DbFree(db, pSorter);
pCsr->uc.pSorter = 0;
}
}
| > > > > > > | 106132 106133 106134 106135 106136 106137 106138 106139 106140 106141 106142 106143 106144 106145 106146 106147 106148 106149 106150 106151 |
** Free any cursor components allocated by sqlite3VdbeSorterXXX routines.
*/
SQLITE_PRIVATE void sqlite3VdbeSorterClose(sqlite3 *db, VdbeCursor *pCsr){
VdbeSorter *pSorter;
assert( pCsr->eCurType==CURTYPE_SORTER );
pSorter = pCsr->uc.pSorter;
if( pSorter ){
/* Increment db->nSpill by the total number of bytes of data written
** to temp files by this sort operation. */
int ii;
for(ii=0; ii<pSorter->nTask; ii++){
db->nSpill += pSorter->aTask[ii].nSpill;
}
sqlite3VdbeSorterReset(db, pSorter);
sqlite3_free(pSorter->list.aMemory);
sqlite3DbFree(db, pSorter);
pCsr->uc.pSorter = 0;
}
}
|
| ︙ | ︙ | |||
105890 105891 105892 105893 105894 105895 105896 105897 105898 105899 105900 105901 105902 105903 105904 105905 105906 105907 105908 105909 105910 105911 105912 |
memcpy(&p->aBuffer[p->iBufEnd], &pData[nData-nRem], nCopy);
p->iBufEnd += nCopy;
if( p->iBufEnd==p->nBuffer ){
p->eFWErr = sqlite3OsWrite(p->pFd,
&p->aBuffer[p->iBufStart], p->iBufEnd - p->iBufStart,
p->iWriteOff + p->iBufStart
);
p->iBufStart = p->iBufEnd = 0;
p->iWriteOff += p->nBuffer;
}
assert( p->iBufEnd<p->nBuffer );
nRem -= nCopy;
}
}
/*
** Flush any buffered data to disk and clean up the PMA-writer object.
** The results of using the PMA-writer after this call are undefined.
** Return SQLITE_OK if flushing the buffered data succeeds or is not
** required. Otherwise, return an SQLite error code.
**
** Before returning, set *piEof to the offset immediately following the
| > | > | > > | 106363 106364 106365 106366 106367 106368 106369 106370 106371 106372 106373 106374 106375 106376 106377 106378 106379 106380 106381 106382 106383 106384 106385 106386 106387 106388 106389 106390 106391 106392 106393 106394 106395 106396 106397 106398 106399 106400 106401 106402 106403 106404 106405 106406 106407 |
memcpy(&p->aBuffer[p->iBufEnd], &pData[nData-nRem], nCopy);
p->iBufEnd += nCopy;
if( p->iBufEnd==p->nBuffer ){
p->eFWErr = sqlite3OsWrite(p->pFd,
&p->aBuffer[p->iBufStart], p->iBufEnd - p->iBufStart,
p->iWriteOff + p->iBufStart
);
p->nPmaSpill += (p->iBufEnd - p->iBufStart);
p->iBufStart = p->iBufEnd = 0;
p->iWriteOff += p->nBuffer;
}
assert( p->iBufEnd<p->nBuffer );
nRem -= nCopy;
}
}
/*
** Flush any buffered data to disk and clean up the PMA-writer object.
** The results of using the PMA-writer after this call are undefined.
** Return SQLITE_OK if flushing the buffered data succeeds or is not
** required. Otherwise, return an SQLite error code.
**
** Before returning, set *piEof to the offset immediately following the
** last byte written to the file. Also, increment (*pnSpill) by the total
** number of bytes written to the file.
*/
static int vdbePmaWriterFinish(PmaWriter *p, i64 *piEof, u64 *pnSpill){
int rc;
if( p->eFWErr==0 && ALWAYS(p->aBuffer) && p->iBufEnd>p->iBufStart ){
p->eFWErr = sqlite3OsWrite(p->pFd,
&p->aBuffer[p->iBufStart], p->iBufEnd - p->iBufStart,
p->iWriteOff + p->iBufStart
);
p->nPmaSpill += (p->iBufEnd - p->iBufStart);
}
*piEof = (p->iWriteOff + p->iBufEnd);
*pnSpill += p->nPmaSpill;
sqlite3_free(p->aBuffer);
rc = p->eFWErr;
memset(p, 0, sizeof(PmaWriter));
return rc;
}
/*
|
| ︙ | ︙ | |||
105996 105997 105998 105999 106000 106001 106002 |
for(p=pList->pList; p; p=pNext){
pNext = p->u.pNext;
vdbePmaWriteVarint(&writer, p->nVal);
vdbePmaWriteBlob(&writer, SRVAL(p), p->nVal);
if( pList->aMemory==0 ) sqlite3_free(p);
}
pList->pList = p;
| | | 106473 106474 106475 106476 106477 106478 106479 106480 106481 106482 106483 106484 106485 106486 106487 |
for(p=pList->pList; p; p=pNext){
pNext = p->u.pNext;
vdbePmaWriteVarint(&writer, p->nVal);
vdbePmaWriteBlob(&writer, SRVAL(p), p->nVal);
if( pList->aMemory==0 ) sqlite3_free(p);
}
pList->pList = p;
rc = vdbePmaWriterFinish(&writer, &pTask->file.iEof, &pTask->nSpill);
}
vdbeSorterWorkDebug(pTask, "exit");
assert( rc!=SQLITE_OK || pList->pList==0 );
assert( rc!=SQLITE_OK || pTask->file.iEof==iSz );
return rc;
}
|
| ︙ | ︙ | |||
106310 106311 106312 106313 106314 106315 106316 |
/* Write the next key to the output. */
vdbePmaWriteVarint(&writer, nKey);
vdbePmaWriteBlob(&writer, pReader->aKey, nKey);
assert( pIncr->pMerger->pTask==pTask );
rc = vdbeMergeEngineStep(pIncr->pMerger, &dummy);
}
| | | 106787 106788 106789 106790 106791 106792 106793 106794 106795 106796 106797 106798 106799 106800 106801 |
/* Write the next key to the output. */
vdbePmaWriteVarint(&writer, nKey);
vdbePmaWriteBlob(&writer, pReader->aKey, nKey);
assert( pIncr->pMerger->pTask==pTask );
rc = vdbeMergeEngineStep(pIncr->pMerger, &dummy);
}
rc2 = vdbePmaWriterFinish(&writer, &pOut->iEof, &pTask->nSpill);
if( rc==SQLITE_OK ) rc = rc2;
vdbeSorterPopulateDebug(pTask, "exit");
return rc;
}
#if SQLITE_MAX_WORKER_THREADS>0
/*
|
| ︙ | ︙ | |||
109254 109255 109256 109257 109258 109259 109260 | } #define sqlite3ResolveNotValid(P,N,M,X,E,R) \ assert( ((X)&~(NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol))==0 ); \ if( ((N)->ncFlags & (X))!=0 ) notValidImpl(P,N,M,E,R); /* ** Expression p should encode a floating point value between 1.0 and 0.0. | | | | 109731 109732 109733 109734 109735 109736 109737 109738 109739 109740 109741 109742 109743 109744 109745 109746 |
}
#define sqlite3ResolveNotValid(P,N,M,X,E,R) \
assert( ((X)&~(NC_IsCheck|NC_PartIdx|NC_IdxExpr|NC_GenCol))==0 ); \
if( ((N)->ncFlags & (X))!=0 ) notValidImpl(P,N,M,E,R);
/*
** Expression p should encode a floating point value between 1.0 and 0.0.
** Return 134,217,728 (2^27) times this value. Or return -1 if p is not
** a floating point value between 1.0 and 0.0.
*/
static int exprProbability(Expr *p){
double r = -1.0;
if( p->op!=TK_FLOAT ) return -1;
assert( !ExprHasProperty(p, EP_IntValue) );
sqlite3AtoF(p->u.zToken, &r, sqlite3Strlen30(p->u.zToken), SQLITE_UTF8);
assert( r>=0.0 );
|
| ︙ | ︙ | |||
110611 110612 110613 110614 110615 110616 110617 |
int type, /* NC_IsCheck, NC_PartIdx, NC_IdxExpr, NC_GenCol, or 0 */
Expr *pExpr, /* Expression to resolve. May be NULL. */
ExprList *pList /* Expression list to resolve. May be NULL. */
){
SrcList *pSrc; /* Fake SrcList for pParse->pNewTable */
NameContext sNC; /* Name context for pParse->pNewTable */
int rc;
| > > | > > | < | 111088 111089 111090 111091 111092 111093 111094 111095 111096 111097 111098 111099 111100 111101 111102 111103 111104 111105 111106 111107 111108 111109 111110 111111 111112 |
int type, /* NC_IsCheck, NC_PartIdx, NC_IdxExpr, NC_GenCol, or 0 */
Expr *pExpr, /* Expression to resolve. May be NULL. */
ExprList *pList /* Expression list to resolve. May be NULL. */
){
SrcList *pSrc; /* Fake SrcList for pParse->pNewTable */
NameContext sNC; /* Name context for pParse->pNewTable */
int rc;
union {
SrcList sSrc;
u8 srcSpace[SZ_SRCLIST_1]; /* Memory space for the fake SrcList */
} uSrc;
assert( type==0 || pTab!=0 );
assert( type==NC_IsCheck || type==NC_PartIdx || type==NC_IdxExpr
|| type==NC_GenCol || pTab==0 );
memset(&sNC, 0, sizeof(sNC));
memset(&uSrc, 0, sizeof(uSrc));
pSrc = &uSrc.sSrc;
if( pTab ){
pSrc->nSrc = 1;
pSrc->a[0].zName = pTab->zName;
pSrc->a[0].pSTab = pTab;
pSrc->a[0].iCursor = -1;
if( pTab->pSchema!=pParse->db->aDb[1].pSchema ){
/* Cause EP_FromDDL to be set on TK_FUNCTION nodes of non-TEMP
|
| ︙ | ︙ | |||
111881 111882 111883 111884 111885 111886 111887 111888 111889 111890 111891 111892 111893 111894 |
return;
}
if( IsWindowFunc(pExpr) ){
sqlite3ExprOrderByAggregateError(pParse, pExpr);
sqlite3ExprListDelete(db, pOrderBy);
return;
}
pOB = sqlite3ExprAlloc(db, TK_ORDER, 0, 0);
if( pOB==0 ){
sqlite3ExprListDelete(db, pOrderBy);
return;
}
pOB->x.pList = pOrderBy;
| > > > > > | 112361 112362 112363 112364 112365 112366 112367 112368 112369 112370 112371 112372 112373 112374 112375 112376 112377 112378 112379 |
return;
}
if( IsWindowFunc(pExpr) ){
sqlite3ExprOrderByAggregateError(pParse, pExpr);
sqlite3ExprListDelete(db, pOrderBy);
return;
}
if( pOrderBy->nExpr>db->aLimit[SQLITE_LIMIT_COLUMN] ){
sqlite3ErrorMsg(pParse, "too many terms in ORDER BY clause");
sqlite3ExprListDelete(db, pOrderBy);
return;
}
pOB = sqlite3ExprAlloc(db, TK_ORDER, 0, 0);
if( pOB==0 ){
sqlite3ExprListDelete(db, pOrderBy);
return;
}
pOB->x.pList = pOrderBy;
|
| ︙ | ︙ | |||
113077 113078 113079 113080 113081 113082 113083 |
r2 = 0; /* Silence a false-positive uninit-var warning in MSVC */
addrIsNull = 0;
}
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, pFree1);
if( addrIsNull==0 ){
/*
** If the right operand contains a subquery and the left operand does not
| | | < | 113562 113563 113564 113565 113566 113567 113568 113569 113570 113571 113572 113573 113574 113575 113576 113577 |
r2 = 0; /* Silence a false-positive uninit-var warning in MSVC */
addrIsNull = 0;
}
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, pFree1);
if( addrIsNull==0 ){
/*
** If the right operand contains a subquery and the left operand does not
** and the left operand might be NULL, then do an IsNull check
** check on the left operand before computing the right operand.
*/
if( ExprHasProperty(pExpr->pRight, EP_Subquery)
&& sqlite3ExprCanBeNull(pExpr->pLeft)
){
addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, r1);
VdbeComment((v, "skip right operand"));
VdbeCoverage(v);
|
| ︙ | ︙ | |||
114643 114644 114645 114646 114647 114648 114649 |
Expr *pExpr, /* The IN expression */
int destIfFalse, /* Jump here if LHS is not contained in the RHS */
int destIfNull /* Jump here if the results are unknown due to NULLs */
){
int rRhsHasNull = 0; /* Register that is true if RHS contains NULL values */
int eType; /* Type of the RHS */
int rLhs; /* Register(s) holding the LHS values */
| < | 115127 115128 115129 115130 115131 115132 115133 115134 115135 115136 115137 115138 115139 115140 |
Expr *pExpr, /* The IN expression */
int destIfFalse, /* Jump here if LHS is not contained in the RHS */
int destIfNull /* Jump here if the results are unknown due to NULLs */
){
int rRhsHasNull = 0; /* Register that is true if RHS contains NULL values */
int eType; /* Type of the RHS */
int rLhs; /* Register(s) holding the LHS values */
Vdbe *v; /* Statement under construction */
int *aiMap = 0; /* Map from vector field to index column */
char *zAff = 0; /* Affinity string for comparisons */
int nVector; /* Size of vectors for this IN operator */
int iDummy; /* Dummy parameter to exprCodeVector() */
Expr *pLeft; /* The LHS of the IN operator */
int i; /* loop counter */
|
| ︙ | ︙ | |||
114706 114707 114708 114709 114710 114711 114712 | ** the field order that matches the RHS index. ** ** Avoid factoring the LHS of the IN(...) expression out of the loop, ** even if it is constant, as OP_Affinity may be used on the register ** by code generated below. */ assert( pParse->okConstFactor==okConstFactor ); pParse->okConstFactor = 0; | | < < < < < < < < < < < > | 115189 115190 115191 115192 115193 115194 115195 115196 115197 115198 115199 115200 115201 115202 115203 115204 115205 115206 115207 115208 115209 115210 115211 115212 115213 115214 115215 115216 115217 115218 115219 |
** the field order that matches the RHS index.
**
** Avoid factoring the LHS of the IN(...) expression out of the loop,
** even if it is constant, as OP_Affinity may be used on the register
** by code generated below. */
assert( pParse->okConstFactor==okConstFactor );
pParse->okConstFactor = 0;
rLhs = exprCodeVector(pParse, pLeft, &iDummy);
pParse->okConstFactor = okConstFactor;
/* If sqlite3FindInIndex() did not find or create an index that is
** suitable for evaluating the IN operator, then evaluate using a
** sequence of comparisons.
**
** This is step (1) in the in-operator.md optimized algorithm.
*/
if( eType==IN_INDEX_NOOP ){
ExprList *pList;
CollSeq *pColl;
int labelOk = sqlite3VdbeMakeLabel(pParse);
int r2, regToFree;
int regCkNull = 0;
int ii;
assert( nVector==1 );
assert( ExprUseXList(pExpr) );
pList = pExpr->x.pList;
pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
if( destIfNull!=destIfFalse ){
regCkNull = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_BitAnd, rLhs, rLhs, regCkNull);
}
|
| ︙ | ︙ | |||
114773 114774 114775 114776 114777 114778 114779 114780 114781 114782 114783 114784 114785 114786 |
sqlite3VdbeAddOp2(v, OP_IsNull, regCkNull, destIfNull); VdbeCoverage(v);
sqlite3VdbeGoto(v, destIfFalse);
}
sqlite3VdbeResolveLabel(v, labelOk);
sqlite3ReleaseTempReg(pParse, regCkNull);
goto sqlite3ExprCodeIN_finished;
}
/* Step 2: Check to see if the LHS contains any NULL columns. If the
** LHS does contain NULLs then the result must be either FALSE or NULL.
** We will then skip the binary search of the RHS.
*/
if( destIfNull==destIfFalse ){
destStep2 = destIfFalse;
| > > > > > > > > > > > > > > > > > > > > | 115246 115247 115248 115249 115250 115251 115252 115253 115254 115255 115256 115257 115258 115259 115260 115261 115262 115263 115264 115265 115266 115267 115268 115269 115270 115271 115272 115273 115274 115275 115276 115277 115278 115279 |
sqlite3VdbeAddOp2(v, OP_IsNull, regCkNull, destIfNull); VdbeCoverage(v);
sqlite3VdbeGoto(v, destIfFalse);
}
sqlite3VdbeResolveLabel(v, labelOk);
sqlite3ReleaseTempReg(pParse, regCkNull);
goto sqlite3ExprCodeIN_finished;
}
if( eType!=IN_INDEX_ROWID ){
/* If this IN operator will use an index, then the order of columns in the
** vector might be different from the order in the index. In that case,
** we need to reorder the LHS values to be in index order. Run Affinity
** before reordering the columns, so that the affinity is correct.
*/
sqlite3VdbeAddOp4(v, OP_Affinity, rLhs, nVector, 0, zAff, nVector);
for(i=0; i<nVector && aiMap[i]==i; i++){} /* Are LHS fields reordered? */
if( i!=nVector ){
/* Need to reorder the LHS fields according to aiMap */
int rLhsOrig = rLhs;
rLhs = sqlite3GetTempRange(pParse, nVector);
for(i=0; i<nVector; i++){
sqlite3VdbeAddOp3(v, OP_Copy, rLhsOrig+i, rLhs+aiMap[i], 0);
}
sqlite3ReleaseTempReg(pParse, rLhsOrig);
}
}
/* Step 2: Check to see if the LHS contains any NULL columns. If the
** LHS does contain NULLs then the result must be either FALSE or NULL.
** We will then skip the binary search of the RHS.
*/
if( destIfNull==destIfFalse ){
destStep2 = destIfFalse;
|
| ︙ | ︙ | |||
114800 114801 114802 114803 114804 114805 114806 114807 114808 114809 114810 |
** of the RHS using the LHS as a probe. If found, the result is
** true.
*/
if( eType==IN_INDEX_ROWID ){
/* In this case, the RHS is the ROWID of table b-tree and so we also
** know that the RHS is non-NULL. Hence, we combine steps 3 and 4
** into a single opcode. */
sqlite3VdbeAddOp3(v, OP_SeekRowid, iTab, destIfFalse, rLhs);
VdbeCoverage(v);
addrTruthOp = sqlite3VdbeAddOp0(v, OP_Goto); /* Return True */
}else{
| > < | 115293 115294 115295 115296 115297 115298 115299 115300 115301 115302 115303 115304 115305 115306 115307 115308 115309 115310 115311 |
** of the RHS using the LHS as a probe. If found, the result is
** true.
*/
if( eType==IN_INDEX_ROWID ){
/* In this case, the RHS is the ROWID of table b-tree and so we also
** know that the RHS is non-NULL. Hence, we combine steps 3 and 4
** into a single opcode. */
assert( nVector==1 );
sqlite3VdbeAddOp3(v, OP_SeekRowid, iTab, destIfFalse, rLhs);
VdbeCoverage(v);
addrTruthOp = sqlite3VdbeAddOp0(v, OP_Goto); /* Return True */
}else{
if( destIfFalse==destIfNull ){
/* Combine Step 3 and Step 5 into a single opcode */
if( ExprHasProperty(pExpr, EP_Subrtn) ){
const VdbeOp *pOp = sqlite3VdbeGetOp(v, pExpr->y.sub.iAddr);
assert( pOp->opcode==OP_Once || pParse->nErr );
if( pOp->opcode==OP_Once && pOp->p3>0 ){ /* tag-202407032019 */
assert( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) );
|
| ︙ | ︙ | |||
114882 114883 114884 114885 114886 114887 114888 |
sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfFalse);
}
/* Jumps here in order to return true. */
sqlite3VdbeJumpHere(v, addrTruthOp);
sqlite3ExprCodeIN_finished:
| < | 115375 115376 115377 115378 115379 115380 115381 115382 115383 115384 115385 115386 115387 115388 |
sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfFalse);
}
/* Jumps here in order to return true. */
sqlite3VdbeJumpHere(v, addrTruthOp);
sqlite3ExprCodeIN_finished:
VdbeComment((v, "end IN expr"));
sqlite3ExprCodeIN_oom_error:
sqlite3DbFree(pParse->db, aiMap);
sqlite3DbFree(pParse->db, zAff);
}
#endif /* SQLITE_OMIT_SUBQUERY */
|
| ︙ | ︙ | |||
115440 115441 115442 115443 115444 115445 115446 115447 115448 115449 115450 115451 115452 115453 |
}
return ret;
}
}
return 0;
}
/*
** Generate code into the current Vdbe to evaluate the given
** expression. Attempt to store the results in register "target".
** Return the register where results are stored.
**
** With this routine, there is no guarantee that results will
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 115932 115933 115934 115935 115936 115937 115938 115939 115940 115941 115942 115943 115944 115945 115946 115947 115948 115949 115950 115951 115952 115953 115954 115955 115956 115957 115958 115959 115960 115961 115962 115963 115964 115965 115966 115967 115968 115969 115970 115971 115972 115973 115974 115975 115976 115977 115978 115979 115980 115981 115982 115983 115984 115985 115986 115987 115988 115989 115990 115991 115992 115993 115994 115995 115996 115997 115998 115999 116000 116001 116002 116003 116004 116005 116006 116007 116008 116009 116010 116011 116012 116013 116014 116015 116016 116017 116018 116019 |
}
return ret;
}
}
return 0;
}
/*
** Generate code that evaluates an AND or OR operator leaving a
** boolean result in a register. pExpr is the AND/OR expression.
** Store the result in the "target" register. Use short-circuit
** evaluation to avoid computing both operands, if possible.
**
** The code generated might require the use of a temporary register.
** If it does, then write the number of that temporary register
** into *pTmpReg. If not, leave *pTmpReg unchanged.
*/
static SQLITE_NOINLINE int exprCodeTargetAndOr(
Parse *pParse, /* Parsing context */
Expr *pExpr, /* AND or OR expression to be coded */
int target, /* Put result in this register, guaranteed */
int *pTmpReg /* Write a temporary register here */
){
int op; /* The opcode. TK_AND or TK_OR */
int skipOp; /* Opcode for the branch that skips one operand */
int addrSkip; /* Branch instruction that skips one of the operands */
int regSS = 0; /* Register holding computed operand when other omitted */
int r1, r2; /* Registers for left and right operands, respectively */
Expr *pAlt; /* Alternative, simplified expression */
Vdbe *v; /* statement being coded */
assert( pExpr!=0 );
op = pExpr->op;
assert( op==TK_AND || op==TK_OR );
assert( TK_AND==OP_And ); testcase( op==TK_AND );
assert( TK_OR==OP_Or ); testcase( op==TK_OR );
assert( pParse->pVdbe!=0 );
v = pParse->pVdbe;
pAlt = sqlite3ExprSimplifiedAndOr(pExpr);
if( pAlt!=pExpr ){
r1 = sqlite3ExprCodeTarget(pParse, pAlt, target);
sqlite3VdbeAddOp3(v, OP_BitAnd, r1, r1, target);
return target;
}
skipOp = op==TK_AND ? OP_IfNot : OP_If;
if( exprEvalRhsFirst(pExpr) ){
/* Compute the right operand first. Skip the computation of the left
** operand if the right operand fully determines the result */
r2 = regSS = sqlite3ExprCodeTarget(pParse, pExpr->pRight, target);
addrSkip = sqlite3VdbeAddOp1(v, skipOp, r2);
VdbeComment((v, "skip left operand"));
VdbeCoverage(v);
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, pTmpReg);
}else{
/* Compute the left operand first */
r1 = sqlite3ExprCodeTarget(pParse, pExpr->pLeft, target);
if( ExprHasProperty(pExpr->pRight, EP_Subquery) ){
/* Skip over the computation of the right operand if the right
** operand is a subquery and the left operand completely determines
** the result */
regSS = r1;
addrSkip = sqlite3VdbeAddOp1(v, skipOp, r1);
VdbeComment((v, "skip right operand"));
VdbeCoverage(v);
}else{
addrSkip = regSS = 0;
}
r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, pTmpReg);
}
sqlite3VdbeAddOp3(v, op, r2, r1, target);
testcase( (*pTmpReg)==0 );
if( addrSkip ){
sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+2);
sqlite3VdbeJumpHere(v, addrSkip);
sqlite3VdbeAddOp3(v, OP_Or, regSS, regSS, target);
VdbeComment((v, "short-circut value"));
}
return target;
}
/*
** Generate code into the current Vdbe to evaluate the given
** expression. Attempt to store the results in register "target".
** Return the register where results are stored.
**
** With this routine, there is no guarantee that results will
|
| ︙ | ︙ | |||
115631 115632 115633 115634 115635 115636 115637 115638 115639 115640 115641 115642 115643 115644 |
}
#endif
case TK_STRING: {
assert( !ExprHasProperty(pExpr, EP_IntValue) );
sqlite3VdbeLoadString(v, target, pExpr->u.zToken);
return target;
}
default: {
/* Make NULL the default case so that if a bug causes an illegal
** Expr node to be passed into this function, it will be handled
** sanely and not crash. But keep the assert() to bring the problem
** to the attention of the developers. */
assert( op==TK_NULL || op==TK_ERROR || pParse->db->mallocFailed );
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
| > > > > > > | 116197 116198 116199 116200 116201 116202 116203 116204 116205 116206 116207 116208 116209 116210 116211 116212 116213 116214 116215 116216 |
}
#endif
case TK_STRING: {
assert( !ExprHasProperty(pExpr, EP_IntValue) );
sqlite3VdbeLoadString(v, target, pExpr->u.zToken);
return target;
}
case TK_NULLS: {
/* Set a range of registers to NULL. pExpr->y.nReg registers starting
** with target */
sqlite3VdbeAddOp3(v, OP_Null, 0, target, target + pExpr->y.nReg - 1);
return target;
}
default: {
/* Make NULL the default case so that if a bug causes an illegal
** Expr node to be passed into this function, it will be handled
** sanely and not crash. But keep the assert() to bring the problem
** to the attention of the developers. */
assert( op==TK_NULL || op==TK_ERROR || pParse->db->mallocFailed );
sqlite3VdbeAddOp2(v, OP_Null, 0, target);
|
| ︙ | ︙ | |||
115722 115723 115724 115725 115726 115727 115728 |
sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+2);
sqlite3VdbeJumpHere(v, addrIsNull);
sqlite3VdbeAddOp2(v, OP_Null, 0, inReg);
}
}
testcase( regFree1==0 );
testcase( regFree2==0 );
| < | > > > < < | 116294 116295 116296 116297 116298 116299 116300 116301 116302 116303 116304 116305 116306 116307 116308 116309 116310 116311 116312 116313 116314 116315 116316 116317 116318 116319 116320 116321 116322 116323 116324 116325 116326 |
sqlite3VdbeAddOp2(v, OP_Goto, 0, sqlite3VdbeCurrentAddr(v)+2);
sqlite3VdbeJumpHere(v, addrIsNull);
sqlite3VdbeAddOp2(v, OP_Null, 0, inReg);
}
}
testcase( regFree1==0 );
testcase( regFree2==0 );
}
break;
}
case TK_AND:
case TK_OR: {
inReg = exprCodeTargetAndOr(pParse, pExpr, target, ®Free1);
break;
}
case TK_PLUS:
case TK_STAR:
case TK_MINUS:
case TK_REM:
case TK_BITAND:
case TK_BITOR:
case TK_SLASH:
case TK_LSHIFT:
case TK_RSHIFT:
case TK_CONCAT: {
int addrIsNull;
assert( TK_PLUS==OP_Add ); testcase( op==TK_PLUS );
assert( TK_MINUS==OP_Subtract ); testcase( op==TK_MINUS );
assert( TK_REM==OP_Remainder ); testcase( op==TK_REM );
assert( TK_BITAND==OP_BitAnd ); testcase( op==TK_BITAND );
assert( TK_BITOR==OP_BitOr ); testcase( op==TK_BITOR );
assert( TK_SLASH==OP_Divide ); testcase( op==TK_SLASH );
assert( TK_LSHIFT==OP_ShiftLeft ); testcase( op==TK_LSHIFT );
|
| ︙ | ︙ | |||
116339 116340 116341 116342 116343 116344 116345 116346 116347 116348 116349 116350 116351 116352 |
if( regDest<0 ) regDest = ++pParse->nMem;
pItem->u.iConstExprReg = regDest;
}
pParse->pConstExpr = p;
}
return regDest;
}
/*
** Generate code to evaluate an expression and store the results
** into a register. Return the register number where the results
** are stored.
**
** If the register is a temporary register that can be deallocated,
| > > > > > > > > > > > > > > > > > > > | 116911 116912 116913 116914 116915 116916 116917 116918 116919 116920 116921 116922 116923 116924 116925 116926 116927 116928 116929 116930 116931 116932 116933 116934 116935 116936 116937 116938 116939 116940 116941 116942 116943 |
if( regDest<0 ) regDest = ++pParse->nMem;
pItem->u.iConstExprReg = regDest;
}
pParse->pConstExpr = p;
}
return regDest;
}
/*
** Make arrangements to invoke OP_Null on a range of registers
** during initialization.
*/
SQLITE_PRIVATE SQLITE_NOINLINE void sqlite3ExprNullRegisterRange(
Parse *pParse, /* Parsing context */
int iReg, /* First register to set to NULL */
int nReg /* Number of sequential registers to NULL out */
){
u8 okConstFactor = pParse->okConstFactor;
Expr t;
memset(&t, 0, sizeof(t));
t.op = TK_NULLS;
t.y.nReg = nReg;
pParse->okConstFactor = 1;
sqlite3ExprCodeRunJustOnce(pParse, &t, iReg);
pParse->okConstFactor = okConstFactor;
}
/*
** Generate code to evaluate an expression and store the results
** into a register. Return the register number where the results
** are stored.
**
** If the register is a temporary register that can be deallocated,
|
| ︙ | ︙ | |||
123870 123871 123872 123873 123874 123875 123876 123877 123878 123879 123880 123881 123882 123883 |
** CREATE, then check to see if it is the name of an virtual table that
** can be an eponymous virtual table. */
if( (pParse->prepFlags & SQLITE_PREPARE_NO_VTAB)==0 && db->init.busy==0 ){
Module *pMod = (Module*)sqlite3HashFind(&db->aModule, zName);
if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){
pMod = sqlite3PragmaVtabRegister(db, zName);
}
if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){
testcase( pMod->pEpoTab==0 );
return pMod->pEpoTab;
}
}
#endif
if( flags & LOCATE_NOERR ) return 0;
| > > > > > > > > > > | 124461 124462 124463 124464 124465 124466 124467 124468 124469 124470 124471 124472 124473 124474 124475 124476 124477 124478 124479 124480 124481 124482 124483 124484 |
** CREATE, then check to see if it is the name of an virtual table that
** can be an eponymous virtual table. */
if( (pParse->prepFlags & SQLITE_PREPARE_NO_VTAB)==0 && db->init.busy==0 ){
Module *pMod = (Module*)sqlite3HashFind(&db->aModule, zName);
if( pMod==0 && sqlite3_strnicmp(zName, "pragma_", 7)==0 ){
pMod = sqlite3PragmaVtabRegister(db, zName);
}
#ifndef SQLITE_OMIT_JSON
if( pMod==0 && sqlite3_strnicmp(zName, "json", 4)==0 ){
pMod = sqlite3JsonVtabRegister(db, zName);
}
#endif
#ifdef SQLITE_ENABLE_CARRAY
if( pMod==0 && sqlite3_stricmp(zName, "carray")==0 ){
pMod = sqlite3CarrayRegister(db);
}
#endif
if( pMod && sqlite3VtabEponymousTableInit(pParse, pMod) ){
testcase( pMod->pEpoTab==0 );
return pMod->pEpoTab;
}
}
#endif
if( flags & LOCATE_NOERR ) return 0;
|
| ︙ | ︙ | |||
124508 124509 124510 124511 124512 124513 124514 |
** find the (first) offset of that column in index pIdx. Or return -1
** if column iCol is not used in index pIdx.
*/
SQLITE_PRIVATE int sqlite3TableColumnToIndex(Index *pIdx, int iCol){
int i;
i16 iCol16;
assert( iCol>=(-1) && iCol<=SQLITE_MAX_COLUMN );
| | | 125109 125110 125111 125112 125113 125114 125115 125116 125117 125118 125119 125120 125121 125122 125123 |
** find the (first) offset of that column in index pIdx. Or return -1
** if column iCol is not used in index pIdx.
*/
SQLITE_PRIVATE int sqlite3TableColumnToIndex(Index *pIdx, int iCol){
int i;
i16 iCol16;
assert( iCol>=(-1) && iCol<=SQLITE_MAX_COLUMN );
assert( pIdx->nColumn<=SQLITE_MAX_COLUMN*2 );
iCol16 = iCol;
for(i=0; i<pIdx->nColumn; i++){
if( iCol16==pIdx->aiColumn[i] ){
return i;
}
}
return -1;
|
| ︙ | ︙ | |||
124805 124806 124807 124808 124809 124810 124811 124812 124813 124814 124815 124816 124817 124818 |
}
sqlite3OpenSchemaTable(pParse, iDb);
sqlite3VdbeAddOp2(v, OP_NewRowid, 0, reg1);
sqlite3VdbeAddOp4(v, OP_Blob, 6, reg3, 0, nullRow, P4_STATIC);
sqlite3VdbeAddOp3(v, OP_Insert, 0, reg3, reg1);
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
sqlite3VdbeAddOp0(v, OP_Close);
}
/* Normal (non-error) return. */
return;
/* If an error occurs, we jump here */
begin_table_error:
| > > > | 125406 125407 125408 125409 125410 125411 125412 125413 125414 125415 125416 125417 125418 125419 125420 125421 125422 |
}
sqlite3OpenSchemaTable(pParse, iDb);
sqlite3VdbeAddOp2(v, OP_NewRowid, 0, reg1);
sqlite3VdbeAddOp4(v, OP_Blob, 6, reg3, 0, nullRow, P4_STATIC);
sqlite3VdbeAddOp3(v, OP_Insert, 0, reg3, reg1);
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
sqlite3VdbeAddOp0(v, OP_Close);
}else if( db->init.imposterTable ){
pTable->tabFlags |= TF_Imposter;
if( db->init.imposterTable>=2 ) pTable->tabFlags |= TF_Readonly;
}
/* Normal (non-error) return. */
return;
/* If an error occurs, we jump here */
begin_table_error:
|
| ︙ | ︙ | |||
129100 129101 129102 129103 129104 129105 129106 |
pKey->aColl[i] = zColl==sqlite3StrBINARY ? 0 :
sqlite3LocateCollSeq(pParse, zColl);
pKey->aSortFlags[i] = pIdx->aSortOrder[i];
assert( 0==(pKey->aSortFlags[i] & KEYINFO_ORDER_BIGNULL) );
}
if( pParse->nErr ){
assert( pParse->rc==SQLITE_ERROR_MISSING_COLLSEQ );
| | > > | > > > | 129704 129705 129706 129707 129708 129709 129710 129711 129712 129713 129714 129715 129716 129717 129718 129719 129720 129721 129722 129723 129724 129725 129726 129727 129728 129729 129730 |
pKey->aColl[i] = zColl==sqlite3StrBINARY ? 0 :
sqlite3LocateCollSeq(pParse, zColl);
pKey->aSortFlags[i] = pIdx->aSortOrder[i];
assert( 0==(pKey->aSortFlags[i] & KEYINFO_ORDER_BIGNULL) );
}
if( pParse->nErr ){
assert( pParse->rc==SQLITE_ERROR_MISSING_COLLSEQ );
if( pIdx->bNoQuery==0
&& sqlite3HashFind(&pIdx->pSchema->idxHash, pIdx->zName)
){
/* Deactivate the index because it contains an unknown collating
** sequence. The only way to reactive the index is to reload the
** schema. Adding the missing collating sequence later does not
** reactive the index. The application had the chance to register
** the missing index using the collation-needed callback. For
** simplicity, SQLite will not give the application a second chance.
**
** Except, do not do this if the index is not in the schema hash
** table. In this case the index is currently being constructed
** by a CREATE INDEX statement, and retrying will not help. */
pIdx->bNoQuery = 1;
pParse->rc = SQLITE_ERROR_RETRY;
}
sqlite3KeyInfoUnref(pKey);
pKey = 0;
}
}
|
| ︙ | ︙ | |||
131975 131976 131977 131978 131979 131980 131981 |
/*
** Return true if z[] begins with N hexadecimal digits, and write
** a decoding of those digits into *pVal. Or return false if any
** one of the first N characters in z[] is not a hexadecimal digit.
*/
static int isNHex(const char *z, int N, u32 *pVal){
int i;
| | | 132584 132585 132586 132587 132588 132589 132590 132591 132592 132593 132594 132595 132596 132597 132598 |
/*
** Return true if z[] begins with N hexadecimal digits, and write
** a decoding of those digits into *pVal. Or return false if any
** one of the first N characters in z[] is not a hexadecimal digit.
*/
static int isNHex(const char *z, int N, u32 *pVal){
int i;
u32 v = 0;
for(i=0; i<N; i++){
if( !sqlite3Isxdigit(z[i]) ) return 0;
v = (v<<4) + sqlite3HexToInt(z[i]);
}
*pVal = v;
return 1;
}
|
| ︙ | ︙ | |||
132488 132489 132490 132491 132492 132493 132494 132495 132496 132497 132498 132499 132500 132501 132502 132503 132504 132505 132506 132507 132508 132509 132510 |
int argc,
sqlite3_value **argv,
int nSep,
const char *zSep
){
i64 j, n = 0;
int i;
char *z;
for(i=0; i<argc; i++){
n += sqlite3_value_bytes(argv[i]);
}
n += (argc-1)*(i64)nSep;
z = sqlite3_malloc64(n+1);
if( z==0 ){
sqlite3_result_error_nomem(context);
return;
}
j = 0;
for(i=0; i<argc; i++){
if( sqlite3_value_type(argv[i])!=SQLITE_NULL ){
int k = sqlite3_value_bytes(argv[i]);
const char *v = (const char*)sqlite3_value_text(argv[i]);
if( v!=0 ){
| > | > | 133097 133098 133099 133100 133101 133102 133103 133104 133105 133106 133107 133108 133109 133110 133111 133112 133113 133114 133115 133116 133117 133118 133119 133120 133121 133122 133123 133124 133125 133126 133127 133128 133129 133130 133131 133132 133133 133134 |
int argc,
sqlite3_value **argv,
int nSep,
const char *zSep
){
i64 j, n = 0;
int i;
int bNotNull = 0; /* True after at least NOT NULL argument seen */
char *z;
for(i=0; i<argc; i++){
n += sqlite3_value_bytes(argv[i]);
}
n += (argc-1)*(i64)nSep;
z = sqlite3_malloc64(n+1);
if( z==0 ){
sqlite3_result_error_nomem(context);
return;
}
j = 0;
for(i=0; i<argc; i++){
if( sqlite3_value_type(argv[i])!=SQLITE_NULL ){
int k = sqlite3_value_bytes(argv[i]);
const char *v = (const char*)sqlite3_value_text(argv[i]);
if( v!=0 ){
if( bNotNull && nSep>0 ){
memcpy(&z[j], zSep, nSep);
j += nSep;
}
memcpy(&z[j], v, k);
j += k;
bNotNull = 1;
}
}
}
z[j] = 0;
assert( j<=n );
sqlite3_result_text64(context, z, j, sqlite3_free, SQLITE_UTF8);
}
|
| ︙ | ︙ | |||
133454 133455 133456 133457 133458 133459 133460 133461 133462 133463 133464 133465 133466 133467 | UNUSED_PARAMETER(argc); assert( argc==1 ); type0 = sqlite3_value_numeric_type(argv[0]); if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return; x = sqlite3_value_double(argv[0]); sqlite3_result_int(context, x<0.0 ? -1 : x>0.0 ? +1 : 0); } #ifdef SQLITE_DEBUG /* ** Implementation of fpdecode(x,y,z) function. ** ** x is a real number that is to be decoded. y is the precision. ** z is the maximum real precision. Return a string that shows the | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 134065 134066 134067 134068 134069 134070 134071 134072 134073 134074 134075 134076 134077 134078 134079 134080 134081 134082 134083 134084 134085 134086 134087 134088 134089 134090 134091 134092 134093 134094 134095 134096 134097 134098 134099 134100 134101 134102 134103 134104 134105 134106 134107 134108 134109 134110 134111 134112 134113 134114 134115 134116 134117 134118 134119 134120 134121 134122 134123 134124 134125 134126 134127 134128 134129 134130 134131 134132 134133 134134 134135 134136 134137 134138 134139 134140 134141 134142 134143 134144 134145 134146 134147 134148 134149 134150 134151 134152 134153 134154 134155 134156 134157 134158 134159 134160 134161 134162 134163 134164 134165 134166 134167 134168 134169 134170 134171 134172 134173 134174 134175 134176 134177 134178 134179 134180 134181 134182 134183 134184 134185 134186 134187 134188 134189 134190 134191 134192 134193 134194 134195 134196 134197 134198 134199 134200 134201 134202 134203 134204 134205 134206 134207 134208 134209 134210 134211 134212 134213 134214 134215 134216 134217 134218 134219 134220 134221 134222 134223 134224 134225 134226 134227 134228 134229 134230 134231 134232 134233 134234 134235 134236 134237 134238 134239 134240 134241 134242 134243 134244 134245 134246 134247 134248 134249 134250 134251 134252 134253 134254 134255 134256 134257 134258 134259 134260 134261 134262 134263 134264 134265 134266 134267 134268 134269 134270 134271 134272 134273 134274 134275 134276 134277 134278 134279 134280 134281 134282 134283 134284 134285 134286 134287 134288 134289 134290 134291 134292 134293 134294 134295 134296 134297 134298 134299 134300 134301 134302 134303 134304 134305 134306 134307 134308 134309 134310 134311 134312 134313 134314 134315 134316 134317 134318 134319 134320 134321 134322 134323 134324 134325 134326 134327 134328 134329 134330 134331 134332 134333 134334 134335 134336 134337 134338 134339 134340 134341 134342 134343 134344 134345 134346 134347 134348 134349 134350 134351 134352 134353 134354 134355 134356 134357 134358 134359 134360 134361 134362 134363 134364 134365 134366 134367 134368 134369 134370 134371 134372 134373 134374 134375 134376 134377 134378 134379 134380 134381 134382 134383 134384 134385 134386 134387 134388 134389 134390 134391 134392 134393 134394 134395 134396 134397 134398 134399 134400 134401 134402 134403 134404 134405 134406 134407 134408 134409 134410 134411 134412 134413 134414 134415 134416 134417 134418 134419 134420 134421 134422 134423 134424 134425 134426 134427 134428 134429 134430 134431 134432 134433 134434 134435 134436 134437 134438 134439 134440 134441 134442 134443 134444 134445 134446 134447 134448 134449 134450 134451 134452 134453 134454 134455 134456 134457 134458 134459 134460 134461 134462 134463 134464 134465 134466 134467 134468 134469 134470 134471 134472 134473 134474 134475 134476 134477 134478 134479 134480 134481 134482 134483 134484 134485 134486 134487 134488 134489 134490 134491 134492 134493 134494 134495 134496 134497 134498 134499 134500 134501 134502 134503 134504 134505 134506 134507 134508 134509 134510 134511 134512 134513 134514 134515 134516 134517 134518 134519 134520 134521 134522 134523 134524 |
UNUSED_PARAMETER(argc);
assert( argc==1 );
type0 = sqlite3_value_numeric_type(argv[0]);
if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return;
x = sqlite3_value_double(argv[0]);
sqlite3_result_int(context, x<0.0 ? -1 : x>0.0 ? +1 : 0);
}
#if defined(SQLITE_ENABLE_PERCENTILE)
/***********************************************************************
** This section implements the percentile(Y,P) SQL function and similar.
** Requirements:
**
** (1) The percentile(Y,P) function is an aggregate function taking
** exactly two arguments.
**
** (2) If the P argument to percentile(Y,P) is not the same for every
** row in the aggregate then an error is thrown. The word "same"
** in the previous sentence means that the value differ by less
** than 0.001.
**
** (3) If the P argument to percentile(Y,P) evaluates to anything other
** than a number in the range of 0.0 to 100.0 inclusive then an
** error is thrown.
**
** (4) If any Y argument to percentile(Y,P) evaluates to a value that
** is not NULL and is not numeric then an error is thrown.
**
** (5) If any Y argument to percentile(Y,P) evaluates to plus or minus
** infinity then an error is thrown. (SQLite always interprets NaN
** values as NULL.)
**
** (6) Both Y and P in percentile(Y,P) can be arbitrary expressions,
** including CASE WHEN expressions.
**
** (7) The percentile(Y,P) aggregate is able to handle inputs of at least
** one million (1,000,000) rows.
**
** (8) If there are no non-NULL values for Y, then percentile(Y,P)
** returns NULL.
**
** (9) If there is exactly one non-NULL value for Y, the percentile(Y,P)
** returns the one Y value.
**
** (10) If there N non-NULL values of Y where N is two or more and
** the Y values are ordered from least to greatest and a graph is
** drawn from 0 to N-1 such that the height of the graph at J is
** the J-th Y value and such that straight lines are drawn between
** adjacent Y values, then the percentile(Y,P) function returns
** the height of the graph at P*(N-1)/100.
**
** (11) The percentile(Y,P) function always returns either a floating
** point number or NULL.
**
** (12) The percentile(Y,P) is implemented as a single C99 source-code
** file that compiles into a shared-library or DLL that can be loaded
** into SQLite using the sqlite3_load_extension() interface.
**
** (13) A separate median(Y) function is the equivalent percentile(Y,50).
**
** (14) A separate percentile_cont(Y,P) function is equivalent to
** percentile(Y,P/100.0). In other words, the fraction value in
** the second argument is in the range of 0 to 1 instead of 0 to 100.
**
** (15) A separate percentile_disc(Y,P) function is like
** percentile_cont(Y,P) except that instead of returning the weighted
** average of the nearest two input values, it returns the next lower
** value. So the percentile_disc(Y,P) will always return a value
** that was one of the inputs.
**
** (16) All of median(), percentile(Y,P), percentile_cont(Y,P) and
** percentile_disc(Y,P) can be used as window functions.
**
** Differences from standard SQL:
**
** * The percentile_cont(X,P) function is equivalent to the following in
** standard SQL:
**
** (percentile_cont(P) WITHIN GROUP (ORDER BY X))
**
** The SQLite syntax is much more compact. The standard SQL syntax
** is also supported if SQLite is compiled with the
** -DSQLITE_ENABLE_ORDERED_SET_AGGREGATES option.
**
** * No median(X) function exists in the SQL standard. App developers
** are expected to write "percentile_cont(0.5)WITHIN GROUP(ORDER BY X)".
**
** * No percentile(Y,P) function exists in the SQL standard. Instead of
** percential(Y,P), developers must write this:
** "percentile_cont(P/100.0) WITHIN GROUP (ORDER BY Y)". Note that
** the fraction parameter to percentile() goes from 0 to 100 whereas
** the fraction parameter in SQL standard percentile_cont() goes from
** 0 to 1.
**
** Implementation notes as of 2024-08-31:
**
** * The regular aggregate-function versions of these routines work
** by accumulating all values in an array of doubles, then sorting
** that array using quicksort before computing the answer. Thus
** the runtime is O(NlogN) where N is the number of rows of input.
**
** * For the window-function versions of these routines, the array of
** inputs is sorted as soon as the first value is computed. Thereafter,
** the array is kept in sorted order using an insert-sort. This
** results in O(N*K) performance where K is the size of the window.
** One can imagine alternative implementations that give O(N*logN*logK)
** performance, but they require more complex logic and data structures.
** The developers have elected to keep the asymptotically slower
** algorithm for now, for simplicity, under the theory that window
** functions are seldom used and when they are, the window size K is
** often small. The developers might revisit that decision later,
** should the need arise.
*/
/* The following object is the group context for a single percentile()
** aggregate. Remember all input Y values until the very end.
** Those values are accumulated in the Percentile.a[] array.
*/
typedef struct Percentile Percentile;
struct Percentile {
unsigned nAlloc; /* Number of slots allocated for a[] */
unsigned nUsed; /* Number of slots actually used in a[] */
char bSorted; /* True if a[] is already in sorted order */
char bKeepSorted; /* True if advantageous to keep a[] sorted */
char bPctValid; /* True if rPct is valid */
double rPct; /* Fraction. 0.0 to 1.0 */
double *a; /* Array of Y values */
};
/*
** Return TRUE if the input floating-point number is an infinity.
*/
static int percentIsInfinity(double r){
sqlite3_uint64 u;
assert( sizeof(u)==sizeof(r) );
memcpy(&u, &r, sizeof(u));
return ((u>>52)&0x7ff)==0x7ff;
}
/*
** Return TRUE if two doubles differ by 0.001 or less.
*/
static int percentSameValue(double a, double b){
a -= b;
return a>=-0.001 && a<=0.001;
}
/*
** Search p (which must have p->bSorted) looking for an entry with
** value y. Return the index of that entry.
**
** If bExact is true, return -1 if the entry is not found.
**
** If bExact is false, return the index at which a new entry with
** value y should be insert in order to keep the values in sorted
** order. The smallest return value in this case will be 0, and
** the largest return value will be p->nUsed.
*/
static int percentBinarySearch(Percentile *p, double y, int bExact){
int iFirst = 0; /* First element of search range */
int iLast = p->nUsed - 1; /* Last element of search range */
while( iLast>=iFirst ){
int iMid = (iFirst+iLast)/2;
double x = p->a[iMid];
if( x<y ){
iFirst = iMid + 1;
}else if( x>y ){
iLast = iMid - 1;
}else{
return iMid;
}
}
if( bExact ) return -1;
return iFirst;
}
/*
** Generate an error for a percentile function.
**
** The error format string must have exactly one occurrence of "%%s()"
** (with two '%' characters). That substring will be replaced by the name
** of the function.
*/
static void percentError(sqlite3_context *pCtx, const char *zFormat, ...){
char *zMsg1;
char *zMsg2;
va_list ap;
va_start(ap, zFormat);
zMsg1 = sqlite3_vmprintf(zFormat, ap);
va_end(ap);
zMsg2 = zMsg1 ? sqlite3_mprintf(zMsg1, sqlite3VdbeFuncName(pCtx)) : 0;
sqlite3_result_error(pCtx, zMsg2, -1);
sqlite3_free(zMsg1);
sqlite3_free(zMsg2);
}
/*
** The "step" function for percentile(Y,P) is called once for each
** input row.
*/
static void percentStep(sqlite3_context *pCtx, int argc, sqlite3_value **argv){
Percentile *p;
double rPct;
int eType;
double y;
assert( argc==2 || argc==1 );
if( argc==1 ){
/* Requirement 13: median(Y) is the same as percentile(Y,50). */
rPct = 0.5;
}else{
/* P must be a number between 0 and 100 for percentile() or between
** 0.0 and 1.0 for percentile_cont() and percentile_disc().
**
** The user-data is an integer which is 10 times the upper bound.
*/
double mxFrac = (SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx))&2)? 100.0 : 1.0;
eType = sqlite3_value_numeric_type(argv[1]);
rPct = sqlite3_value_double(argv[1])/mxFrac;
if( (eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT)
|| rPct<0.0 || rPct>1.0
){
percentError(pCtx, "the fraction argument to %%s()"
" is not between 0.0 and %.1f",
(double)mxFrac);
return;
}
}
/* Allocate the session context. */
p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p));
if( p==0 ) return;
/* Remember the P value. Throw an error if the P value is different
** from any prior row, per Requirement (2). */
if( !p->bPctValid ){
p->rPct = rPct;
p->bPctValid = 1;
}else if( !percentSameValue(p->rPct,rPct) ){
percentError(pCtx, "the fraction argument to %%s()"
" is not the same for all input rows");
return;
}
/* Ignore rows for which Y is NULL */
eType = sqlite3_value_type(argv[0]);
if( eType==SQLITE_NULL ) return;
/* If not NULL, then Y must be numeric. Otherwise throw an error.
** Requirement 4 */
if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){
percentError(pCtx, "input to %%s() is not numeric");
return;
}
/* Throw an error if the Y value is infinity or NaN */
y = sqlite3_value_double(argv[0]);
if( percentIsInfinity(y) ){
percentError(pCtx, "Inf input to %%s()");
return;
}
/* Allocate and store the Y */
if( p->nUsed>=p->nAlloc ){
unsigned n = p->nAlloc*2 + 250;
double *a = sqlite3_realloc64(p->a, sizeof(double)*n);
if( a==0 ){
sqlite3_free(p->a);
memset(p, 0, sizeof(*p));
sqlite3_result_error_nomem(pCtx);
return;
}
p->nAlloc = n;
p->a = a;
}
if( p->nUsed==0 ){
p->a[p->nUsed++] = y;
p->bSorted = 1;
}else if( !p->bSorted || y>=p->a[p->nUsed-1] ){
p->a[p->nUsed++] = y;
}else if( p->bKeepSorted ){
int i;
i = percentBinarySearch(p, y, 0);
if( i<(int)p->nUsed ){
memmove(&p->a[i+1], &p->a[i], (p->nUsed-i)*sizeof(p->a[0]));
}
p->a[i] = y;
p->nUsed++;
}else{
p->a[p->nUsed++] = y;
p->bSorted = 0;
}
}
/*
** Interchange two doubles.
*/
#define SWAP_DOUBLE(X,Y) {double ttt=(X);(X)=(Y);(Y)=ttt;}
/*
** Sort an array of doubles.
**
** Algorithm: quicksort
**
** This is implemented separately rather than using the qsort() routine
** from the standard library because:
**
** (1) To avoid a dependency on qsort()
** (2) To avoid the function call to the comparison routine for each
** comparison.
*/
static void percentSort(double *a, unsigned int n){
int iLt; /* Entries before a[iLt] are less than rPivot */
int iGt; /* Entries at or after a[iGt] are greater than rPivot */
int i; /* Loop counter */
double rPivot; /* The pivot value */
assert( n>=2 );
if( a[0]>a[n-1] ){
SWAP_DOUBLE(a[0],a[n-1])
}
if( n==2 ) return;
iGt = n-1;
i = n/2;
if( a[0]>a[i] ){
SWAP_DOUBLE(a[0],a[i])
}else if( a[i]>a[iGt] ){
SWAP_DOUBLE(a[i],a[iGt])
}
if( n==3 ) return;
rPivot = a[i];
iLt = i = 1;
do{
if( a[i]<rPivot ){
if( i>iLt ) SWAP_DOUBLE(a[i],a[iLt])
iLt++;
i++;
}else if( a[i]>rPivot ){
do{
iGt--;
}while( iGt>i && a[iGt]>rPivot );
SWAP_DOUBLE(a[i],a[iGt])
}else{
i++;
}
}while( i<iGt );
if( iLt>=2 ) percentSort(a, iLt);
if( n-iGt>=2 ) percentSort(a+iGt, n-iGt);
/* Uncomment for testing */
#if 0
for(i=0; i<n-1; i++){
assert( a[i]<=a[i+1] );
}
#endif
}
/*
** The "inverse" function for percentile(Y,P) is called to remove a
** row that was previously inserted by "step".
*/
static void percentInverse(sqlite3_context *pCtx,int argc,sqlite3_value **argv){
Percentile *p;
int eType;
double y;
int i;
assert( argc==2 || argc==1 );
/* Allocate the session context. */
p = (Percentile*)sqlite3_aggregate_context(pCtx, sizeof(*p));
assert( p!=0 );
/* Ignore rows for which Y is NULL */
eType = sqlite3_value_type(argv[0]);
if( eType==SQLITE_NULL ) return;
/* If not NULL, then Y must be numeric. Otherwise throw an error.
** Requirement 4 */
if( eType!=SQLITE_INTEGER && eType!=SQLITE_FLOAT ){
return;
}
/* Ignore the Y value if it is infinity or NaN */
y = sqlite3_value_double(argv[0]);
if( percentIsInfinity(y) ){
return;
}
if( p->bSorted==0 ){
assert( p->nUsed>1 );
percentSort(p->a, p->nUsed);
p->bSorted = 1;
}
p->bKeepSorted = 1;
/* Find and remove the row */
i = percentBinarySearch(p, y, 1);
if( i>=0 ){
p->nUsed--;
if( i<(int)p->nUsed ){
memmove(&p->a[i], &p->a[i+1], (p->nUsed - i)*sizeof(p->a[0]));
}
}
}
/*
** Compute the final output of percentile(). Clean up all allocated
** memory if and only if bIsFinal is true.
*/
static void percentCompute(sqlite3_context *pCtx, int bIsFinal){
Percentile *p;
int settings = SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx))&1; /* Discrete? */
unsigned i1, i2;
double v1, v2;
double ix, vx;
p = (Percentile*)sqlite3_aggregate_context(pCtx, 0);
if( p==0 ) return;
if( p->a==0 ) return;
if( p->nUsed ){
if( p->bSorted==0 ){
assert( p->nUsed>1 );
percentSort(p->a, p->nUsed);
p->bSorted = 1;
}
ix = p->rPct*(p->nUsed-1);
i1 = (unsigned)ix;
if( settings & 1 ){
vx = p->a[i1];
}else{
i2 = ix==(double)i1 || i1==p->nUsed-1 ? i1 : i1+1;
v1 = p->a[i1];
v2 = p->a[i2];
vx = v1 + (v2-v1)*(ix-i1);
}
sqlite3_result_double(pCtx, vx);
}
if( bIsFinal ){
sqlite3_free(p->a);
memset(p, 0, sizeof(*p));
}else{
p->bKeepSorted = 1;
}
}
static void percentFinal(sqlite3_context *pCtx){
percentCompute(pCtx, 1);
}
static void percentValue(sqlite3_context *pCtx){
percentCompute(pCtx, 0);
}
/****** End of percentile family of functions ******/
#endif /* SQLITE_ENABLE_PERCENTILE */
#ifdef SQLITE_DEBUG
/*
** Implementation of fpdecode(x,y,z) function.
**
** x is a real number that is to be decoded. y is the precision.
** z is the maximum real precision. Return a string that shows the
|
| ︙ | ︙ | |||
133685 133686 133687 133688 133689 133690 133691 133692 133693 133694 133695 133696 133697 133698 |
WAGGREGATE(group_concat, 1, 0, 0, groupConcatStep,
groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
WAGGREGATE(group_concat, 2, 0, 0, groupConcatStep,
groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
WAGGREGATE(string_agg, 2, 0, 0, groupConcatStep,
groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
LIKEFUNC(glob, 2, &globInfo, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
#ifdef SQLITE_CASE_SENSITIVE_LIKE
LIKEFUNC(like, 2, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
LIKEFUNC(like, 3, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
#else
LIKEFUNC(like, 2, &likeInfoNorm, SQLITE_FUNC_LIKE),
LIKEFUNC(like, 3, &likeInfoNorm, SQLITE_FUNC_LIKE),
| > > > > > > > > > > > > > > > | 134742 134743 134744 134745 134746 134747 134748 134749 134750 134751 134752 134753 134754 134755 134756 134757 134758 134759 134760 134761 134762 134763 134764 134765 134766 134767 134768 134769 134770 |
WAGGREGATE(group_concat, 1, 0, 0, groupConcatStep,
groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
WAGGREGATE(group_concat, 2, 0, 0, groupConcatStep,
groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
WAGGREGATE(string_agg, 2, 0, 0, groupConcatStep,
groupConcatFinalize, groupConcatValue, groupConcatInverse, 0),
#ifdef SQLITE_ENABLE_PERCENTILE
WAGGREGATE(median, 1, 0,0, percentStep,
percentFinal, percentValue, percentInverse,
SQLITE_INNOCUOUS|SQLITE_SELFORDER1),
WAGGREGATE(percentile, 2, 0x2,0, percentStep,
percentFinal, percentValue, percentInverse,
SQLITE_INNOCUOUS|SQLITE_SELFORDER1),
WAGGREGATE(percentile_cont, 2, 0,0, percentStep,
percentFinal, percentValue, percentInverse,
SQLITE_INNOCUOUS|SQLITE_SELFORDER1),
WAGGREGATE(percentile_disc, 2, 0x1,0, percentStep,
percentFinal, percentValue, percentInverse,
SQLITE_INNOCUOUS|SQLITE_SELFORDER1),
#endif /* SQLITE_ENABLE_PERCENTILE */
LIKEFUNC(glob, 2, &globInfo, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
#ifdef SQLITE_CASE_SENSITIVE_LIKE
LIKEFUNC(like, 2, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
LIKEFUNC(like, 3, &likeInfoAlt, SQLITE_FUNC_LIKE|SQLITE_FUNC_CASE),
#else
LIKEFUNC(like, 2, &likeInfoNorm, SQLITE_FUNC_LIKE),
LIKEFUNC(like, 3, &likeInfoNorm, SQLITE_FUNC_LIKE),
|
| ︙ | ︙ | |||
139182 139183 139184 139185 139186 139187 139188 139189 139190 139191 139192 139193 139194 139195 | /* Version 3.43.0 and later */ int (*stmt_explain)(sqlite3_stmt*,int); /* Version 3.44.0 and later */ void *(*get_clientdata)(sqlite3*,const char*); int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*)); /* Version 3.50.0 and later */ int (*setlk_timeout)(sqlite3*,int,int); }; /* ** This is the function signature used for all extension entry points. It ** is also defined in the file "loadext.c". */ typedef int (*sqlite3_loadext_entry)( | > > > > | 140254 140255 140256 140257 140258 140259 140260 140261 140262 140263 140264 140265 140266 140267 140268 140269 140270 140271 | /* Version 3.43.0 and later */ int (*stmt_explain)(sqlite3_stmt*,int); /* Version 3.44.0 and later */ void *(*get_clientdata)(sqlite3*,const char*); int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*)); /* Version 3.50.0 and later */ int (*setlk_timeout)(sqlite3*,int,int); /* Version 3.51.0 and later */ int (*set_errmsg)(sqlite3*,int,const char*); int (*db_status64)(sqlite3*,int,sqlite3_int64*,sqlite3_int64*,int); }; /* ** This is the function signature used for all extension entry points. It ** is also defined in the file "loadext.c". */ typedef int (*sqlite3_loadext_entry)( |
| ︙ | ︙ | |||
139517 139518 139519 139520 139521 139522 139523 139524 139525 139526 139527 139528 139529 139530 | /* Version 3.43.0 and later */ #define sqlite3_stmt_explain sqlite3_api->stmt_explain /* Version 3.44.0 and later */ #define sqlite3_get_clientdata sqlite3_api->get_clientdata #define sqlite3_set_clientdata sqlite3_api->set_clientdata /* Version 3.50.0 and later */ #define sqlite3_setlk_timeout sqlite3_api->setlk_timeout #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) /* This case when the file really is being compiled as a loadable ** extension */ # define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0; # define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v; | > > > | 140593 140594 140595 140596 140597 140598 140599 140600 140601 140602 140603 140604 140605 140606 140607 140608 140609 | /* Version 3.43.0 and later */ #define sqlite3_stmt_explain sqlite3_api->stmt_explain /* Version 3.44.0 and later */ #define sqlite3_get_clientdata sqlite3_api->get_clientdata #define sqlite3_set_clientdata sqlite3_api->set_clientdata /* Version 3.50.0 and later */ #define sqlite3_setlk_timeout sqlite3_api->setlk_timeout /* Version 3.51.0 and later */ #define sqlite3_set_errmsg sqlite3_api->set_errmsg #define sqlite3_db_status64 sqlite3_api->db_status64 #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */ #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) /* This case when the file really is being compiled as a loadable ** extension */ # define SQLITE_EXTENSION_INIT1 const sqlite3_api_routines *sqlite3_api=0; # define SQLITE_EXTENSION_INIT2(v) sqlite3_api=v; |
| ︙ | ︙ | |||
140040 140041 140042 140043 140044 140045 140046 | sqlite3_is_interrupted, /* Version 3.43.0 and later */ sqlite3_stmt_explain, /* Version 3.44.0 and later */ sqlite3_get_clientdata, sqlite3_set_clientdata, /* Version 3.50.0 and later */ | | > > > | 141119 141120 141121 141122 141123 141124 141125 141126 141127 141128 141129 141130 141131 141132 141133 141134 141135 141136 | sqlite3_is_interrupted, /* Version 3.43.0 and later */ sqlite3_stmt_explain, /* Version 3.44.0 and later */ sqlite3_get_clientdata, sqlite3_set_clientdata, /* Version 3.50.0 and later */ sqlite3_setlk_timeout, /* Version 3.51.0 and later */ sqlite3_set_errmsg, sqlite3_db_status64 }; /* True if x is the directory separator character */ #if SQLITE_OS_WIN # define DirSep(X) ((X)=='/'||(X)=='\\') #else |
| ︙ | ︙ | |||
141501 141502 141503 141504 141505 141506 141507 141508 141509 141510 141511 141512 141513 141514 | int addr; sqlite3VdbeAddOp2(v, OP_ResultRow, 3, 1); addr = sqlite3VdbeAddOp3(v, OP_IfPos, 1, sqlite3VdbeCurrentAddr(v)+2, 1); VdbeCoverage(v); sqlite3VdbeAddOp0(v, OP_Halt); return addr; } /* ** Process a pragma statement. ** ** Pragmas are of this form: ** ** PRAGMA [schema.]id [= value] | > > > > > > > > > > > > > > > > | 142583 142584 142585 142586 142587 142588 142589 142590 142591 142592 142593 142594 142595 142596 142597 142598 142599 142600 142601 142602 142603 142604 142605 142606 142607 142608 142609 142610 142611 142612 |
int addr;
sqlite3VdbeAddOp2(v, OP_ResultRow, 3, 1);
addr = sqlite3VdbeAddOp3(v, OP_IfPos, 1, sqlite3VdbeCurrentAddr(v)+2, 1);
VdbeCoverage(v);
sqlite3VdbeAddOp0(v, OP_Halt);
return addr;
}
/*
** Should table pTab be skipped when doing an integrity_check?
** Return true or false.
**
** If pObjTab is not null, the return true if pTab matches pObjTab.
**
** If pObjTab is null, then return true only if pTab is an imposter table.
*/
static int tableSkipIntegrityCheck(const Table *pTab, const Table *pObjTab){
if( pObjTab ){
return pTab!=pObjTab;
}else{
return (pTab->tabFlags & TF_Imposter)!=0;
}
}
/*
** Process a pragma statement.
**
** Pragmas are of this form:
**
** PRAGMA [schema.]id [= value]
|
| ︙ | ︙ | |||
142847 142848 142849 142850 142851 142852 142853 |
*/
assert( sqlite3SchemaMutexHeld(db, i, 0) );
pTbls = &db->aDb[i].pSchema->tblHash;
for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
Table *pTab = sqliteHashData(x); /* Current table */
Index *pIdx; /* An index on pTab */
int nIdx; /* Number of indexes on pTab */
| | | | 143945 143946 143947 143948 143949 143950 143951 143952 143953 143954 143955 143956 143957 143958 143959 143960 143961 143962 143963 143964 143965 143966 143967 143968 143969 143970 143971 143972 |
*/
assert( sqlite3SchemaMutexHeld(db, i, 0) );
pTbls = &db->aDb[i].pSchema->tblHash;
for(cnt=0, x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
Table *pTab = sqliteHashData(x); /* Current table */
Index *pIdx; /* An index on pTab */
int nIdx; /* Number of indexes on pTab */
if( tableSkipIntegrityCheck(pTab,pObjTab) ) continue;
if( HasRowid(pTab) ) cnt++;
for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){ cnt++; }
}
if( cnt==0 ) continue;
if( pObjTab ) cnt++;
aRoot = sqlite3DbMallocRawNN(db, sizeof(int)*(cnt+1));
if( aRoot==0 ) break;
cnt = 0;
if( pObjTab ) aRoot[++cnt] = 0;
for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
Table *pTab = sqliteHashData(x);
Index *pIdx;
if( tableSkipIntegrityCheck(pTab,pObjTab) ) continue;
if( HasRowid(pTab) ) aRoot[++cnt] = pTab->tnum;
for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
aRoot[++cnt] = pIdx->tnum;
}
}
aRoot[0] = cnt;
|
| ︙ | ︙ | |||
142891 142892 142893 142894 142895 142896 142897 |
/* Check that the indexes all have the right number of rows */
cnt = pObjTab ? 1 : 0;
sqlite3VdbeLoadString(v, 2, "wrong # of entries in index ");
for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
int iTab = 0;
Table *pTab = sqliteHashData(x);
Index *pIdx;
| | | 143989 143990 143991 143992 143993 143994 143995 143996 143997 143998 143999 144000 144001 144002 144003 |
/* Check that the indexes all have the right number of rows */
cnt = pObjTab ? 1 : 0;
sqlite3VdbeLoadString(v, 2, "wrong # of entries in index ");
for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
int iTab = 0;
Table *pTab = sqliteHashData(x);
Index *pIdx;
if( tableSkipIntegrityCheck(pTab,pObjTab) ) continue;
if( HasRowid(pTab) ){
iTab = cnt++;
}else{
iTab = cnt;
for(pIdx=pTab->pIndex; ALWAYS(pIdx); pIdx=pIdx->pNext){
if( IsPrimaryKeyIndex(pIdx) ) break;
iTab++;
|
| ︙ | ︙ | |||
142927 142928 142929 142930 142931 142932 142933 |
int loopTop;
int iDataCur, iIdxCur;
int r1 = -1;
int bStrict; /* True for a STRICT table */
int r2; /* Previous key for WITHOUT ROWID tables */
int mxCol; /* Maximum non-virtual column number */
| | | 144025 144026 144027 144028 144029 144030 144031 144032 144033 144034 144035 144036 144037 144038 144039 |
int loopTop;
int iDataCur, iIdxCur;
int r1 = -1;
int bStrict; /* True for a STRICT table */
int r2; /* Previous key for WITHOUT ROWID tables */
int mxCol; /* Maximum non-virtual column number */
if( tableSkipIntegrityCheck(pTab,pObjTab) ) continue;
if( !IsOrdinaryTable(pTab) ) continue;
if( isQuick || HasRowid(pTab) ){
pPk = 0;
r2 = 0;
}else{
pPk = sqlite3PrimaryKeyIndex(pTab);
r2 = sqlite3GetTempRange(pParse, pPk->nKeyCol);
|
| ︙ | ︙ | |||
143251 143252 143253 143254 143255 143256 143257 |
/* Second pass to invoke the xIntegrity method on all virtual
** tables.
*/
for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
Table *pTab = sqliteHashData(x);
sqlite3_vtab *pVTab;
int a1;
| | | 144349 144350 144351 144352 144353 144354 144355 144356 144357 144358 144359 144360 144361 144362 144363 |
/* Second pass to invoke the xIntegrity method on all virtual
** tables.
*/
for(x=sqliteHashFirst(pTbls); x; x=sqliteHashNext(x)){
Table *pTab = sqliteHashData(x);
sqlite3_vtab *pVTab;
int a1;
if( tableSkipIntegrityCheck(pTab,pObjTab) ) continue;
if( IsOrdinaryTable(pTab) ) continue;
if( !IsVirtual(pTab) ) continue;
if( pTab->nCol<=0 ){
const char *zMod = pTab->u.vtab.azArg[0];
if( sqlite3HashFind(&db->aModule, zMod)==0 ) continue;
}
sqlite3ViewGetColumnNames(pParse, pTab);
|
| ︙ | ︙ | |||
143483 143484 143485 143486 143487 143488 143489 143490 143491 143492 143493 143494 143495 143496 |
if( zRight ){
if( sqlite3StrICmp(zRight, "full")==0 ){
eMode = SQLITE_CHECKPOINT_FULL;
}else if( sqlite3StrICmp(zRight, "restart")==0 ){
eMode = SQLITE_CHECKPOINT_RESTART;
}else if( sqlite3StrICmp(zRight, "truncate")==0 ){
eMode = SQLITE_CHECKPOINT_TRUNCATE;
}
}
pParse->nMem = 3;
sqlite3VdbeAddOp3(v, OP_Checkpoint, iBt, eMode, 1);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
}
break;
| > > | 144581 144582 144583 144584 144585 144586 144587 144588 144589 144590 144591 144592 144593 144594 144595 144596 |
if( zRight ){
if( sqlite3StrICmp(zRight, "full")==0 ){
eMode = SQLITE_CHECKPOINT_FULL;
}else if( sqlite3StrICmp(zRight, "restart")==0 ){
eMode = SQLITE_CHECKPOINT_RESTART;
}else if( sqlite3StrICmp(zRight, "truncate")==0 ){
eMode = SQLITE_CHECKPOINT_TRUNCATE;
}else if( sqlite3StrICmp(zRight, "noop")==0 ){
eMode = SQLITE_CHECKPOINT_NOOP;
}
}
pParse->nMem = 3;
sqlite3VdbeAddOp3(v, OP_Checkpoint, iBt, eMode, 1);
sqlite3VdbeAddOp2(v, OP_ResultRow, 1, 3);
}
break;
|
| ︙ | ︙ | |||
145049 145050 145051 145052 145053 145054 145055 |
do{
/* Make multiple attempts to compile the SQL, until it either succeeds
** or encounters a permanent error. A schema problem after one schema
** reset is considered a permanent error. */
rc = sqlite3Prepare(db, zSql, nBytes, prepFlags, pOld, ppStmt, pzTail);
assert( rc==SQLITE_OK || *ppStmt==0 );
if( rc==SQLITE_OK || db->mallocFailed ) break;
| > | | > | 146149 146150 146151 146152 146153 146154 146155 146156 146157 146158 146159 146160 146161 146162 146163 146164 146165 146166 146167 |
do{
/* Make multiple attempts to compile the SQL, until it either succeeds
** or encounters a permanent error. A schema problem after one schema
** reset is considered a permanent error. */
rc = sqlite3Prepare(db, zSql, nBytes, prepFlags, pOld, ppStmt, pzTail);
assert( rc==SQLITE_OK || *ppStmt==0 );
if( rc==SQLITE_OK || db->mallocFailed ) break;
cnt++;
}while( (rc==SQLITE_ERROR_RETRY && ALWAYS(cnt<=SQLITE_MAX_PREPARE_RETRY))
|| (rc==SQLITE_SCHEMA && (sqlite3ResetOneSchema(db,-1), cnt)==1) );
sqlite3BtreeLeaveAll(db);
assert( rc!=SQLITE_ERROR_RETRY );
rc = sqlite3ApiExit(db, rc);
assert( (rc&db->errMask)==rc );
db->busyHandler.nBusy = 0;
sqlite3_mutex_leave(db->mutex);
assert( rc==SQLITE_OK || (*ppStmt)==0 );
return rc;
}
|
| ︙ | ︙ | |||
145725 145726 145727 145728 145729 145730 145731 |
SQLITE_PRIVATE void sqlite3SetJoinExpr(Expr *p, int iTable, u32 joinFlag){
assert( joinFlag==EP_OuterON || joinFlag==EP_InnerON );
while( p ){
ExprSetProperty(p, joinFlag);
assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
ExprSetVVAProperty(p, EP_NoReduce);
p->w.iJoin = iTable;
| < | | 146827 146828 146829 146830 146831 146832 146833 146834 146835 146836 146837 146838 146839 146840 146841 |
SQLITE_PRIVATE void sqlite3SetJoinExpr(Expr *p, int iTable, u32 joinFlag){
assert( joinFlag==EP_OuterON || joinFlag==EP_InnerON );
while( p ){
ExprSetProperty(p, joinFlag);
assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
ExprSetVVAProperty(p, EP_NoReduce);
p->w.iJoin = iTable;
if( ExprUseXList(p) ){
if( p->x.pList ){
int i;
for(i=0; i<p->x.pList->nExpr; i++){
sqlite3SetJoinExpr(p->x.pList->a[i].pExpr, iTable, joinFlag);
}
}
}
|
| ︙ | ︙ | |||
145942 145943 145944 145945 145946 145947 145948 145949 145950 145951 145952 145953 145954 145955 |
** an AND operator.
*/
else if( pRight->u3.pOn ){
sqlite3SetJoinExpr(pRight->u3.pOn, pRight->iCursor, joinType);
p->pWhere = sqlite3ExprAnd(pParse, p->pWhere, pRight->u3.pOn);
pRight->u3.pOn = 0;
pRight->fg.isOn = 1;
}
}
return 0;
}
/*
** An instance of this object holds information (beyond pParse and pSelect)
| > | 147043 147044 147045 147046 147047 147048 147049 147050 147051 147052 147053 147054 147055 147056 147057 |
** an AND operator.
*/
else if( pRight->u3.pOn ){
sqlite3SetJoinExpr(pRight->u3.pOn, pRight->iCursor, joinType);
p->pWhere = sqlite3ExprAnd(pParse, p->pWhere, pRight->u3.pOn);
pRight->u3.pOn = 0;
pRight->fg.isOn = 1;
p->selFlags |= SF_OnToWhere;
}
}
return 0;
}
/*
** An instance of this object holds information (beyond pParse and pSelect)
|
| ︙ | ︙ | |||
146828 146829 146830 146831 146832 146833 146834 |
/*
** Allocate a KeyInfo object sufficient for an index of N key columns and
** X extra columns.
*/
SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
int nExtra = (N+X)*(sizeof(CollSeq*)+1);
| > > > | | 147930 147931 147932 147933 147934 147935 147936 147937 147938 147939 147940 147941 147942 147943 147944 147945 147946 147947 |
/*
** Allocate a KeyInfo object sufficient for an index of N key columns and
** X extra columns.
*/
SQLITE_PRIVATE KeyInfo *sqlite3KeyInfoAlloc(sqlite3 *db, int N, int X){
int nExtra = (N+X)*(sizeof(CollSeq*)+1);
KeyInfo *p;
assert( X>=0 );
if( NEVER(N+X>0xffff) ) return (KeyInfo*)sqlite3OomFault(db);
p = sqlite3DbMallocRawNN(db, SZ_KEYINFO(0) + nExtra);
if( p ){
p->aSortFlags = (u8*)&p->aColl[N+X];
p->nKeyField = (u16)N;
p->nAllField = (u16)(N+X);
p->enc = ENC(db);
p->db = db;
p->nRef = 1;
|
| ︙ | ︙ | |||
149147 149148 149149 149150 149151 149152 149153 | ** ** All references to columns in table iTable are to be replaced by corresponding ** expressions in pEList. ** ** ## About "isOuterJoin": ** ** The isOuterJoin column indicates that the replacement will occur into a | | | 150252 150253 150254 150255 150256 150257 150258 150259 150260 150261 150262 150263 150264 150265 150266 | ** ** All references to columns in table iTable are to be replaced by corresponding ** expressions in pEList. ** ** ## About "isOuterJoin": ** ** The isOuterJoin column indicates that the replacement will occur into a ** position in the parent that is NULL-able due to an OUTER JOIN. Either the ** target slot in the parent is the right operand of a LEFT JOIN, or one of ** the left operands of a RIGHT JOIN. In either case, we need to potentially ** bypass the substituted expression with OP_IfNullRow. ** ** Suppose the original expression is an integer constant. Even though the table ** has the nullRow flag set, because the expression is an integer constant, ** it will not be NULLed out. So instead, we insert an OP_IfNullRow opcode |
| ︙ | ︙ | |||
149985 149986 149987 149988 149989 149990 149991 |
** will scan expressions looking for iParent references and replace
** those references with expressions that resolve to the subquery FROM
** elements we are now copying in.
*/
pSub = pSub1;
for(pParent=p; pParent; pParent=pParent->pPrior, pSub=pSub->pPrior){
int nSubSrc;
| | < < < < < > | < | | 151090 151091 151092 151093 151094 151095 151096 151097 151098 151099 151100 151101 151102 151103 151104 151105 151106 151107 151108 151109 151110 151111 151112 151113 151114 151115 151116 151117 151118 151119 151120 151121 151122 151123 151124 151125 151126 151127 151128 151129 151130 151131 151132 151133 151134 151135 151136 151137 151138 151139 151140 151141 151142 151143 151144 151145 151146 151147 |
** will scan expressions looking for iParent references and replace
** those references with expressions that resolve to the subquery FROM
** elements we are now copying in.
*/
pSub = pSub1;
for(pParent=p; pParent; pParent=pParent->pPrior, pSub=pSub->pPrior){
int nSubSrc;
u8 jointype = pSubitem->fg.jointype;
assert( pSub!=0 );
pSubSrc = pSub->pSrc; /* FROM clause of subquery */
nSubSrc = pSubSrc->nSrc; /* Number of terms in subquery FROM clause */
pSrc = pParent->pSrc; /* FROM clause of the outer query */
/* The subquery uses a single slot of the FROM clause of the outer
** query. If the subquery has more than one element in its FROM clause,
** then expand the outer query to make space for it to hold all elements
** of the subquery.
**
** Example:
**
** SELECT * FROM tabA, (SELECT * FROM sub1, sub2), tabB;
**
** The outer query has 3 slots in its FROM clause. One slot of the
** outer query (the middle slot) is used by the subquery. The next
** block of code will expand the outer query FROM clause to 4 slots.
** The middle slot is expanded to two slots in order to make space
** for the two elements in the FROM clause of the subquery.
*/
if( nSubSrc>1 ){
pSrc = sqlite3SrcListEnlarge(pParse, pSrc, nSubSrc-1,iFrom+1);
if( pSrc==0 ) break;
pParent->pSrc = pSrc;
pSubitem = &pSrc->a[iFrom];
}
/* Transfer the FROM clause terms from the subquery into the
** outer query.
*/
iNewParent = pSubSrc->a[0].iCursor;
for(i=0; i<nSubSrc; i++){
SrcItem *pItem = &pSrc->a[i+iFrom];
assert( pItem->fg.isTabFunc==0 );
assert( pItem->fg.isSubquery
|| pItem->fg.fixedSchema
|| pItem->u4.zDatabase==0 );
if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing);
*pItem = pSubSrc->a[i];
pItem->fg.jointype |= (jointype & JT_LTORJ);
memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
}
pSubitem->fg.jointype |= jointype;
/* Now begin substituting subquery result set expressions for
** references to the iParent in the outer query.
**
** Example:
**
** SELECT a+5, b*10 FROM (SELECT x*3 AS a, y+10 AS b FROM t1) WHERE a>b;
|
| ︙ | ︙ | |||
152744 152745 152746 152747 152748 152749 152750 152751 152752 152753 152754 152755 152756 152757 |
}
else if( pWhere->op==TK_EXISTS ){
Select *pSub = pWhere->x.pSelect;
Expr *pSubWhere = pSub->pWhere;
if( pSub->pSrc->nSrc==1
&& (pSub->selFlags & SF_Aggregate)==0
&& !pSub->pSrc->a[0].fg.isSubquery
){
memset(pWhere, 0, sizeof(*pWhere));
pWhere->op = TK_INTEGER;
pWhere->u.iValue = 1;
ExprSetProperty(pWhere, EP_IntValue);
assert( p->pWhere!=0 );
| > | 153844 153845 153846 153847 153848 153849 153850 153851 153852 153853 153854 153855 153856 153857 153858 |
}
else if( pWhere->op==TK_EXISTS ){
Select *pSub = pWhere->x.pSelect;
Expr *pSubWhere = pSub->pWhere;
if( pSub->pSrc->nSrc==1
&& (pSub->selFlags & SF_Aggregate)==0
&& !pSub->pSrc->a[0].fg.isSubquery
&& pSub->pLimit==0
){
memset(pWhere, 0, sizeof(*pWhere));
pWhere->op = TK_INTEGER;
pWhere->u.iValue = 1;
ExprSetProperty(pWhere, EP_IntValue);
assert( p->pWhere!=0 );
|
| ︙ | ︙ | |||
152772 152773 152774 152775 152776 152777 152778 152779 152780 152781 152782 152783 152784 152785 |
}
#endif
existsToJoin(pParse, p, pSubWhere);
}
}
}
}
/*
** Generate byte-code for the SELECT statement given in the p argument.
**
** The results are returned according to the SelectDest structure.
** See comments in sqliteInt.h for further information.
**
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 153873 153874 153875 153876 153877 153878 153879 153880 153881 153882 153883 153884 153885 153886 153887 153888 153889 153890 153891 153892 153893 153894 153895 153896 153897 153898 153899 153900 153901 153902 153903 153904 153905 153906 153907 153908 153909 153910 153911 153912 153913 153914 153915 153916 153917 153918 153919 153920 153921 153922 153923 153924 153925 153926 153927 153928 153929 153930 153931 153932 153933 153934 153935 153936 153937 153938 153939 153940 153941 153942 153943 153944 153945 153946 153947 153948 153949 153950 153951 153952 153953 153954 153955 153956 153957 153958 153959 153960 153961 153962 153963 153964 153965 153966 153967 153968 153969 153970 153971 153972 153973 153974 153975 153976 153977 153978 153979 153980 153981 153982 153983 153984 153985 153986 153987 153988 153989 153990 153991 153992 153993 153994 153995 |
}
#endif
existsToJoin(pParse, p, pSubWhere);
}
}
}
}
/*
** Type used for Walker callbacks by selectCheckOnClauses().
*/
typedef struct CheckOnCtx CheckOnCtx;
struct CheckOnCtx {
SrcList *pSrc; /* SrcList for this context */
int iJoin; /* Cursor numbers must be =< than this */
CheckOnCtx *pParent; /* Parent context */
};
/*
** True if the SrcList passed as the only argument contains at least
** one RIGHT or FULL JOIN. False otherwise.
*/
#define hasRightJoin(pSrc) (((pSrc)->a[0].fg.jointype & JT_LTORJ)!=0)
/*
** The xExpr callback for the search of invalid ON clause terms.
*/
static int selectCheckOnClausesExpr(Walker *pWalker, Expr *pExpr){
CheckOnCtx *pCtx = pWalker->u.pCheckOnCtx;
/* Check if pExpr is root or near-root of an ON clause constraint that needs
** to be checked to ensure that it does not refer to tables in its FROM
** clause to the right of itself. i.e. it is either:
**
** + an ON clause on an OUTER join, or
** + an ON clause on an INNER join within a FROM that features at
** least one RIGHT or FULL join.
*/
if( (ExprHasProperty(pExpr, EP_OuterON))
|| (ExprHasProperty(pExpr, EP_InnerON) && hasRightJoin(pCtx->pSrc))
){
/* If CheckOnCtx.iJoin is already set, then fall through and process
** this expression node as normal. Or, if CheckOnCtx.iJoin is still 0,
** set it to the cursor number of the RHS of the join to which this
** ON expression was attached and then iterate through the entire
** expression. */
assert( pCtx->iJoin==0 || pCtx->iJoin==pExpr->w.iJoin );
if( pCtx->iJoin==0 ){
pCtx->iJoin = pExpr->w.iJoin;
sqlite3WalkExprNN(pWalker, pExpr);
pCtx->iJoin = 0;
return WRC_Prune;
}
}
if( pExpr->op==TK_COLUMN ){
/* A column expression. Find the SrcList (if any) to which it refers.
** Then, if CheckOnCtx.iJoin indicates that this expression is part of an
** ON clause from that SrcList (i.e. if iJoin is non-zero), check that it
** does not refer to a table to the right of CheckOnCtx.iJoin. */
do {
SrcList *pSrc = pCtx->pSrc;
int iTab = pExpr->iTable;
if( iTab>=pSrc->a[0].iCursor && iTab<=pSrc->a[pSrc->nSrc-1].iCursor ){
if( pCtx->iJoin && iTab>pCtx->iJoin ){
sqlite3ErrorMsg(pWalker->pParse,
"ON clause references tables to its right");
return WRC_Abort;
}
break;
}
pCtx = pCtx->pParent;
}while( pCtx );
}
return WRC_Continue;
}
/*
** The xSelect callback for the search of invalid ON clause terms.
*/
static int selectCheckOnClausesSelect(Walker *pWalker, Select *pSelect){
CheckOnCtx *pCtx = pWalker->u.pCheckOnCtx;
if( pSelect->pSrc==pCtx->pSrc || pSelect->pSrc->nSrc==0 ){
return WRC_Continue;
}else{
CheckOnCtx sCtx;
memset(&sCtx, 0, sizeof(sCtx));
sCtx.pSrc = pSelect->pSrc;
sCtx.pParent = pCtx;
pWalker->u.pCheckOnCtx = &sCtx;
sqlite3WalkSelect(pWalker, pSelect);
pWalker->u.pCheckOnCtx = pCtx;
pSelect->selFlags &= ~SF_OnToWhere;
return WRC_Prune;
}
}
/*
** Check all ON clauses in pSelect to verify that they do not reference
** columns to the right.
*/
static void selectCheckOnClauses(Parse *pParse, Select *pSelect){
Walker w;
CheckOnCtx sCtx;
assert( pSelect->selFlags & SF_OnToWhere );
assert( pSelect->pSrc!=0 && pSelect->pSrc->nSrc>=2 );
memset(&w, 0, sizeof(w));
w.pParse = pParse;
w.xExprCallback = selectCheckOnClausesExpr;
w.xSelectCallback = selectCheckOnClausesSelect;
w.u.pCheckOnCtx = &sCtx;
memset(&sCtx, 0, sizeof(sCtx));
sCtx.pSrc = pSelect->pSrc;
sqlite3WalkExprNN(&w, pSelect->pWhere);
pSelect->selFlags &= ~SF_OnToWhere;
}
/*
** Generate byte-code for the SELECT statement given in the p argument.
**
** The results are returned according to the SelectDest structure.
** See comments in sqliteInt.h for further information.
**
|
| ︙ | ︙ | |||
152899 152900 152901 152902 152903 152904 152905 152906 152907 152908 152909 152910 152911 152912 |
assert( p->pEList!=0 );
#if TREETRACE_ENABLED
if( sqlite3TreeTrace & 0x10 ){
TREETRACE(0x10,pParse,p, ("after name resolution:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
/* If the SF_UFSrcCheck flag is set, then this function is being called
** as part of populating the temp table for an UPDATE...FROM statement.
** In this case, it is an error if the target object (pSrc->a[0]) name
** or alias is duplicated within FROM clause (pSrc->a[1..n]).
**
** Postgres disallows this case too. The reason is that some other
| > > > > > > > > > > > > | 154109 154110 154111 154112 154113 154114 154115 154116 154117 154118 154119 154120 154121 154122 154123 154124 154125 154126 154127 154128 154129 154130 154131 154132 154133 154134 |
assert( p->pEList!=0 );
#if TREETRACE_ENABLED
if( sqlite3TreeTrace & 0x10 ){
TREETRACE(0x10,pParse,p, ("after name resolution:\n"));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
/* If the SELECT statement contains ON clauses that were moved into
** the WHERE clause, go through and verify that none of the terms
** in the ON clauses reference tables to the right of the ON clause.
** Do this now, after name resolution, but before query flattening
*/
if( p->selFlags & SF_OnToWhere ){
selectCheckOnClauses(pParse, p);
if( pParse->nErr ){
goto select_end;
}
}
/* If the SF_UFSrcCheck flag is set, then this function is being called
** as part of populating the temp table for an UPDATE...FROM statement.
** In this case, it is an error if the target object (pSrc->a[0]) name
** or alias is duplicated within FROM clause (pSrc->a[1..n]).
**
** Postgres disallows this case too. The reason is that some other
|
| ︙ | ︙ | |||
153764 153765 153766 153767 153768 153769 153770 153771 153772 153773 153774 153775 153776 153777 |
iAMem = pParse->nMem + 1;
pParse->nMem += pGroupBy->nExpr;
iBMem = pParse->nMem + 1;
pParse->nMem += pGroupBy->nExpr;
sqlite3VdbeAddOp2(v, OP_Integer, 0, iAbortFlag);
VdbeComment((v, "clear abort flag"));
sqlite3VdbeAddOp3(v, OP_Null, 0, iAMem, iAMem+pGroupBy->nExpr-1);
/* Begin a loop that will extract all source rows in GROUP BY order.
** This might involve two separate loops with an OP_Sort in between, or
** it might be a single loop that uses an index to extract information
** in the right order to begin with.
*/
sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
| > | 154986 154987 154988 154989 154990 154991 154992 154993 154994 154995 154996 154997 154998 154999 155000 |
iAMem = pParse->nMem + 1;
pParse->nMem += pGroupBy->nExpr;
iBMem = pParse->nMem + 1;
pParse->nMem += pGroupBy->nExpr;
sqlite3VdbeAddOp2(v, OP_Integer, 0, iAbortFlag);
VdbeComment((v, "clear abort flag"));
sqlite3VdbeAddOp3(v, OP_Null, 0, iAMem, iAMem+pGroupBy->nExpr-1);
sqlite3ExprNullRegisterRange(pParse, iAMem, pGroupBy->nExpr);
/* Begin a loop that will extract all source rows in GROUP BY order.
** This might involve two separate loops with an OP_Sort in between, or
** it might be a single loop that uses an index to extract information
** in the right order to begin with.
*/
sqlite3VdbeAddOp2(v, OP_Gosub, regReset, addrReset);
|
| ︙ | ︙ | |||
155464 155465 155466 155467 155468 155469 155470 |
){
Vdbe *v = pParse->pVdbe;
sqlite3 *db = pParse->db;
ExprList *pNew;
Returning *pReturning;
Select sSelect;
SrcList *pFrom;
| > > | > > | < | 156687 156688 156689 156690 156691 156692 156693 156694 156695 156696 156697 156698 156699 156700 156701 156702 156703 156704 156705 156706 156707 156708 156709 156710 156711 156712 156713 156714 156715 156716 156717 156718 156719 156720 156721 |
){
Vdbe *v = pParse->pVdbe;
sqlite3 *db = pParse->db;
ExprList *pNew;
Returning *pReturning;
Select sSelect;
SrcList *pFrom;
union {
SrcList sSrc;
u8 fromSpace[SZ_SRCLIST_1];
} uSrc;
assert( v!=0 );
if( !pParse->bReturning ){
/* This RETURNING trigger must be for a different statement as
** this statement lacks a RETURNING clause. */
return;
}
assert( db->pParse==pParse );
assert( !pParse->isCreate );
pReturning = pParse->u1.d.pReturning;
if( pTrigger != &(pReturning->retTrig) ){
/* This RETURNING trigger is for a different statement */
return;
}
memset(&sSelect, 0, sizeof(sSelect));
memset(&uSrc, 0, sizeof(uSrc));
pFrom = &uSrc.sSrc;
sSelect.pEList = sqlite3ExprListDup(db, pReturning->pReturnEL, 0);
sSelect.pSrc = pFrom;
pFrom->nSrc = 1;
pFrom->a[0].pSTab = pTab;
pFrom->a[0].zName = pTab->zName; /* tag-20240424-1 */
pFrom->a[0].iCursor = -1;
sqlite3SelectPrep(pParse, &sSelect, 0);
|
| ︙ | ︙ | |||
163012 163013 163014 163015 163016 163017 163018 | WhereRightJoin *pRJ = pLevel->pRJ; Expr *pSubWhere = 0; WhereClause *pWC = &pWInfo->sWC; WhereInfo *pSubWInfo; WhereLoop *pLoop = pLevel->pWLoop; SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom]; SrcList *pFrom; | > > | > | 164238 164239 164240 164241 164242 164243 164244 164245 164246 164247 164248 164249 164250 164251 164252 164253 164254 164255 |
WhereRightJoin *pRJ = pLevel->pRJ;
Expr *pSubWhere = 0;
WhereClause *pWC = &pWInfo->sWC;
WhereInfo *pSubWInfo;
WhereLoop *pLoop = pLevel->pWLoop;
SrcItem *pTabItem = &pWInfo->pTabList->a[pLevel->iFrom];
SrcList *pFrom;
union {
SrcList sSrc;
u8 fromSpace[SZ_SRCLIST_1];
} uSrc;
Bitmask mAll = 0;
int k;
ExplainQueryPlan((pParse, 1, "RIGHT-JOIN %s", pTabItem->pSTab->zName));
sqlite3VdbeNoJumpsOutsideSubrtn(v, pRJ->addrSubrtn, pRJ->endSubrtn,
pRJ->regReturn);
for(k=0; k<iLevel; k++){
|
| ︙ | ︙ | |||
163056 163057 163058 163059 163060 163061 163062 |
}
if( pTerm->prereqAll & ~mAll ) continue;
if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue;
pSubWhere = sqlite3ExprAnd(pParse, pSubWhere,
sqlite3ExprDup(pParse->db, pTerm->pExpr, 0));
}
}
| | | 164285 164286 164287 164288 164289 164290 164291 164292 164293 164294 164295 164296 164297 164298 164299 |
}
if( pTerm->prereqAll & ~mAll ) continue;
if( ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON) ) continue;
pSubWhere = sqlite3ExprAnd(pParse, pSubWhere,
sqlite3ExprDup(pParse->db, pTerm->pExpr, 0));
}
}
pFrom = &uSrc.sSrc;
pFrom->nSrc = 1;
pFrom->nAlloc = 1;
memcpy(&pFrom->a[0], pTabItem, sizeof(SrcItem));
pFrom->a[0].fg.jointype = 0;
assert( pParse->withinRJSubrtn < 100 );
pParse->withinRJSubrtn++;
pSubWInfo = sqlite3WhereBegin(pParse, pFrom, pSubWhere, 0, 0, 0,
|
| ︙ | ︙ | |||
164273 164274 164275 164276 164277 164278 164279 |
if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON) ){
Bitmask x = sqlite3WhereGetMask(pMaskSet, pExpr->w.iJoin);
if( ExprHasProperty(pExpr, EP_OuterON) ){
prereqAll |= x;
extraRight = x-1; /* ON clause terms may not be used with an index
** on left table of a LEFT JOIN. Ticket #3015 */
| < < < < < < < < < < < < < < | 165502 165503 165504 165505 165506 165507 165508 165509 165510 165511 165512 165513 165514 165515 165516 |
if( ExprHasProperty(pExpr, EP_OuterON|EP_InnerON) ){
Bitmask x = sqlite3WhereGetMask(pMaskSet, pExpr->w.iJoin);
if( ExprHasProperty(pExpr, EP_OuterON) ){
prereqAll |= x;
extraRight = x-1; /* ON clause terms may not be used with an index
** on left table of a LEFT JOIN. Ticket #3015 */
}else if( (prereqAll>>1)>=x ){
ExprClearProperty(pExpr, EP_InnerON);
}
}
pTerm->prereqAll = prereqAll;
pTerm->leftCursor = -1;
pTerm->iParent = -1;
pTerm->eOperator = 0;
|
| ︙ | ︙ | |||
169082 169083 169084 169085 169086 169087 169088 169089 169090 169091 169092 169093 169094 169095 |
continue; /* Partial index inappropriate for this query */
}
if( pProbe->bNoQuery ) continue;
rSize = pProbe->aiRowLogEst[0];
pNew->u.btree.nEq = 0;
pNew->u.btree.nBtm = 0;
pNew->u.btree.nTop = 0;
pNew->nSkip = 0;
pNew->nLTerm = 0;
pNew->iSortIdx = 0;
pNew->rSetup = 0;
pNew->prereq = mPrereq;
pNew->nOut = rSize;
pNew->u.btree.pIndex = pProbe;
| > | 170297 170298 170299 170300 170301 170302 170303 170304 170305 170306 170307 170308 170309 170310 170311 |
continue; /* Partial index inappropriate for this query */
}
if( pProbe->bNoQuery ) continue;
rSize = pProbe->aiRowLogEst[0];
pNew->u.btree.nEq = 0;
pNew->u.btree.nBtm = 0;
pNew->u.btree.nTop = 0;
pNew->u.btree.nDistinctCol = 0;
pNew->nSkip = 0;
pNew->nLTerm = 0;
pNew->iSortIdx = 0;
pNew->rSetup = 0;
pNew->prereq = mPrereq;
pNew->nOut = rSize;
pNew->u.btree.pIndex = pProbe;
|
| ︙ | ︙ | |||
170148 170149 170150 170151 170152 170153 170154 170155 170156 |
pLoop = pLast;
}
if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){
if( pLoop->u.vtab.isOrdered
&& ((wctrlFlags&(WHERE_DISTINCTBY|WHERE_SORTBYGROUP))!=WHERE_DISTINCTBY)
){
obSat = obDone;
}
break;
| > > > > < < | 171364 171365 171366 171367 171368 171369 171370 171371 171372 171373 171374 171375 171376 171377 171378 171379 171380 171381 171382 171383 |
pLoop = pLast;
}
if( pLoop->wsFlags & WHERE_VIRTUALTABLE ){
if( pLoop->u.vtab.isOrdered
&& ((wctrlFlags&(WHERE_DISTINCTBY|WHERE_SORTBYGROUP))!=WHERE_DISTINCTBY)
){
obSat = obDone;
}else{
/* No further ORDER BY terms may be matched. So this call should
** return >=0, not -1. Clear isOrderDistinct to ensure it does so. */
isOrderDistinct = 0;
}
break;
}
iCur = pWInfo->pTabList->a[pLoop->iTab].iCursor;
/* Mark off any ORDER BY term X that is a column in the table of
** the current loop for which there is term in the WHERE
** clause of the form X IS NULL or X=? that reference only outer
** loops.
|
| ︙ | ︙ | |||
170895 170896 170897 170898 170899 170900 170901 |
rUnsort -= 2; /* TUNING: Slight bias in favor of no-sort plans */
}
/* Check to see if pWLoop should be added to the set of
** mxChoice best-so-far paths.
**
** First look for an existing path among best-so-far paths
| > | > > > > > > | | | 172113 172114 172115 172116 172117 172118 172119 172120 172121 172122 172123 172124 172125 172126 172127 172128 172129 172130 172131 172132 172133 172134 172135 172136 172137 172138 172139 172140 172141 172142 172143 172144 |
rUnsort -= 2; /* TUNING: Slight bias in favor of no-sort plans */
}
/* Check to see if pWLoop should be added to the set of
** mxChoice best-so-far paths.
**
** First look for an existing path among best-so-far paths
** that:
** (1) covers the same set of loops, and
** (2) has a compatible isOrdered value.
**
** "Compatible isOrdered value" means either
** (A) both have isOrdered==-1, or
** (B) both have isOrder>=0, or
** (C) ordering does not matter because this is the last round
** of the solver.
**
** The term "((pTo->isOrdered^isOrdered)&0x80)==0" is equivalent
** to (pTo->isOrdered==(-1))==(isOrdered==(-1))" for the range
** of legal values for isOrdered, -1..64.
*/
testcase( nTo==0 );
for(jj=0, pTo=aTo; jj<nTo; jj++, pTo++){
if( pTo->maskLoop==maskNew
&& ( ((pTo->isOrdered^isOrdered)&0x80)==0 || iLoop==nLoop-1 )
){
testcase( jj==nTo-1 );
break;
}
}
if( jj>=nTo ){
/* None of the existing best-so-far paths match the candidate. */
|
| ︙ | ︙ | |||
171060 171061 171062 171063 171064 171065 171066 |
if( nFrom==0 ){
sqlite3ErrorMsg(pParse, "no query solution");
sqlite3StackFreeNN(pParse->db, pSpace);
return SQLITE_ERROR;
}
| | > < < | | 172285 172286 172287 172288 172289 172290 172291 172292 172293 172294 172295 172296 172297 172298 172299 172300 172301 172302 |
if( nFrom==0 ){
sqlite3ErrorMsg(pParse, "no query solution");
sqlite3StackFreeNN(pParse->db, pSpace);
return SQLITE_ERROR;
}
/* Only one path is available, which is the best path */
assert( nFrom==1 );
pFrom = aFrom;
assert( pWInfo->nLevel==nLoop );
/* Load the lowest cost path into pWInfo */
for(iLoop=0; iLoop<nLoop; iLoop++){
WhereLevel *pLevel = pWInfo->a + iLoop;
pLevel->pWLoop = pWLoop = pFrom->aLoop[iLoop];
pLevel->iFrom = pWLoop->iTab;
pLevel->iTabCur = pWInfo->pTabList->a[pLevel->iFrom].iCursor;
|
| ︙ | ︙ | |||
171197 171198 171199 171200 171201 171202 171203 |
int i;
#ifdef WHERETRACE_ENABLED
int once = 0;
#endif
for(i=0; i<pWInfo->nLevel; i++){
WhereLoop *p = pWInfo->a[i].pWLoop;
if( p==0 ) break;
| | > > > | 172421 172422 172423 172424 172425 172426 172427 172428 172429 172430 172431 172432 172433 172434 172435 172436 172437 172438 |
int i;
#ifdef WHERETRACE_ENABLED
int once = 0;
#endif
for(i=0; i<pWInfo->nLevel; i++){
WhereLoop *p = pWInfo->a[i].pWLoop;
if( p==0 ) break;
if( (p->wsFlags & WHERE_VIRTUALTABLE)!=0 ){
/* Treat a vtab scan as similar to a full-table scan */
break;
}
if( (p->wsFlags & (WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_IN))!=0 ){
u8 iTab = p->iTab;
WhereLoop *pLoop;
for(pLoop=pWInfo->pLoops; pLoop; pLoop=pLoop->pNextLoop){
if( pLoop->iTab!=iTab ) continue;
if( (pLoop->wsFlags & (WHERE_CONSTRAINT|WHERE_AUTO_INDEX))!=0 ){
/* Auto-index and index-constrained loops allowed to remain */
|
| ︙ | ︙ | |||
175310 175311 175312 175313 175314 175315 175316 | ** AGGSTEP ** } ** RETURN_ROW ** ** ** ROWS BETWEEN <expr1> FOLLOWING AND <expr2> FOLLOWING ** | | | 176537 176538 176539 176540 176541 176542 176543 176544 176545 176546 176547 176548 176549 176550 176551 |
** AGGSTEP
** }
** RETURN_ROW
**
**
** ROWS BETWEEN <expr1> FOLLOWING AND <expr2> FOLLOWING
**
** ... loop started by sqlite3WhereBegin() ...
** if( new partition ){
** Gosub flush
** }
** Insert new row into eph table.
** if( first row of partition ){
** Rewind(csrEnd) ; Rewind(csrStart) ; Rewind(csrCurrent)
** regEnd = <expr2>
|
| ︙ | ︙ | |||
175828 175829 175830 175831 175832 175833 175834 175835 175836 175837 175838 175839 175840 175841 |
}else
if( pMWin->eEnd==TK_UNBOUNDED ){
addrStart = sqlite3VdbeCurrentAddr(v);
addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regStart, 1);
addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, 0, 1);
}else{
assert( pMWin->eEnd==TK_FOLLOWING );
addrStart = sqlite3VdbeCurrentAddr(v);
addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 1);
addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 1);
}
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart);
sqlite3VdbeJumpHere(v, addrBreak2);
addrStart = sqlite3VdbeCurrentAddr(v);
| > > > > > > | 177055 177056 177057 177058 177059 177060 177061 177062 177063 177064 177065 177066 177067 177068 177069 177070 177071 177072 177073 177074 |
}else
if( pMWin->eEnd==TK_UNBOUNDED ){
addrStart = sqlite3VdbeCurrentAddr(v);
addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regStart, 1);
addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, 0, 1);
}else{
assert( pMWin->eEnd==TK_FOLLOWING );
/* assert( regStart>=0 );
** regEnd = regEnd - regStart;
** regStart = 0; */
sqlite3VdbeAddOp3(v, OP_Subtract, regStart, regEnd, regEnd);
sqlite3VdbeAddOp2(v, OP_Integer, 0, regStart);
addrStart = sqlite3VdbeCurrentAddr(v);
addrBreak1 = windowCodeOp(&s, WINDOW_RETURN_ROW, regEnd, 1);
addrBreak2 = windowCodeOp(&s, WINDOW_AGGINVERSE, regStart, 1);
}
sqlite3VdbeAddOp2(v, OP_Goto, 0, addrStart);
sqlite3VdbeJumpHere(v, addrBreak2);
addrStart = sqlite3VdbeCurrentAddr(v);
|
| ︙ | ︙ | |||
183315 183316 183317 183318 183319 183320 183321 | #ifdef SQLITE_ENABLE_DBPAGE_VTAB sqlite3DbpageRegister, #endif #ifdef SQLITE_ENABLE_DBSTAT_VTAB sqlite3DbstatRegister, #endif sqlite3TestExtInit, | < < < | 184548 184549 184550 184551 184552 184553 184554 184555 184556 184557 184558 184559 184560 184561 | #ifdef SQLITE_ENABLE_DBPAGE_VTAB sqlite3DbpageRegister, #endif #ifdef SQLITE_ENABLE_DBSTAT_VTAB sqlite3DbstatRegister, #endif sqlite3TestExtInit, #ifdef SQLITE_ENABLE_STMTVTAB sqlite3StmtVtabInit, #endif #ifdef SQLITE_ENABLE_BYTECODE_VTAB sqlite3VdbeBytecodeVtabInit, #endif #ifdef SQLITE_EXTRA_AUTOEXT |
| ︙ | ︙ | |||
184773 184774 184775 184776 184777 184778 184779 184780 184781 184782 184783 184784 184785 184786 |
const char *zName = 0;
int i, origRc = rc;
for(i=0; i<2 && zName==0; i++, rc &= 0xff){
switch( rc ){
case SQLITE_OK: zName = "SQLITE_OK"; break;
case SQLITE_ERROR: zName = "SQLITE_ERROR"; break;
case SQLITE_ERROR_SNAPSHOT: zName = "SQLITE_ERROR_SNAPSHOT"; break;
case SQLITE_INTERNAL: zName = "SQLITE_INTERNAL"; break;
case SQLITE_PERM: zName = "SQLITE_PERM"; break;
case SQLITE_ABORT: zName = "SQLITE_ABORT"; break;
case SQLITE_ABORT_ROLLBACK: zName = "SQLITE_ABORT_ROLLBACK"; break;
case SQLITE_BUSY: zName = "SQLITE_BUSY"; break;
case SQLITE_BUSY_RECOVERY: zName = "SQLITE_BUSY_RECOVERY"; break;
case SQLITE_BUSY_SNAPSHOT: zName = "SQLITE_BUSY_SNAPSHOT"; break;
| > > > | 186003 186004 186005 186006 186007 186008 186009 186010 186011 186012 186013 186014 186015 186016 186017 186018 186019 |
const char *zName = 0;
int i, origRc = rc;
for(i=0; i<2 && zName==0; i++, rc &= 0xff){
switch( rc ){
case SQLITE_OK: zName = "SQLITE_OK"; break;
case SQLITE_ERROR: zName = "SQLITE_ERROR"; break;
case SQLITE_ERROR_SNAPSHOT: zName = "SQLITE_ERROR_SNAPSHOT"; break;
case SQLITE_ERROR_RETRY: zName = "SQLITE_ERROR_RETRY"; break;
case SQLITE_ERROR_MISSING_COLLSEQ:
zName = "SQLITE_ERROR_MISSING_COLLSEQ"; break;
case SQLITE_INTERNAL: zName = "SQLITE_INTERNAL"; break;
case SQLITE_PERM: zName = "SQLITE_PERM"; break;
case SQLITE_ABORT: zName = "SQLITE_ABORT"; break;
case SQLITE_ABORT_ROLLBACK: zName = "SQLITE_ABORT_ROLLBACK"; break;
case SQLITE_BUSY: zName = "SQLITE_BUSY"; break;
case SQLITE_BUSY_RECOVERY: zName = "SQLITE_BUSY_RECOVERY"; break;
case SQLITE_BUSY_SNAPSHOT: zName = "SQLITE_BUSY_SNAPSHOT"; break;
|
| ︙ | ︙ | |||
185953 185954 185955 185956 185957 185958 185959 185960 185961 185962 185963 185964 185965 185966 |
if( z==0 ){
z = sqlite3ErrStr(db->errCode);
}
}
sqlite3_mutex_leave(db->mutex);
return z;
}
/*
** Return the byte offset of the most recent error
*/
SQLITE_API int sqlite3_error_offset(sqlite3 *db){
int iOffset = -1;
if( db && sqlite3SafetyCheckSickOrOk(db) && db->errCode ){
| > > > > > > > > > > > > > > > > > > > > > > > | 187186 187187 187188 187189 187190 187191 187192 187193 187194 187195 187196 187197 187198 187199 187200 187201 187202 187203 187204 187205 187206 187207 187208 187209 187210 187211 187212 187213 187214 187215 187216 187217 187218 187219 187220 187221 187222 |
if( z==0 ){
z = sqlite3ErrStr(db->errCode);
}
}
sqlite3_mutex_leave(db->mutex);
return z;
}
/*
** Set the error code and error message associated with the database handle.
**
** This routine is intended to be called by outside extensions (ex: the
** Session extension). Internal logic should invoke sqlite3Error() or
** sqlite3ErrorWithMsg() directly.
*/
SQLITE_API int sqlite3_set_errmsg(sqlite3 *db, int errcode, const char *zMsg){
int rc = SQLITE_OK;
if( !sqlite3SafetyCheckSickOrOk(db) ){
return SQLITE_MISUSE_BKPT;
}
sqlite3_mutex_enter(db->mutex);
if( zMsg ){
sqlite3ErrorWithMsg(db, errcode, "%s", zMsg);
}else{
sqlite3Error(db, errcode);
}
rc = sqlite3ApiExit(db, rc);
sqlite3_mutex_leave(db->mutex);
return rc;
}
/*
** Return the byte offset of the most recent error
*/
SQLITE_API int sqlite3_error_offset(sqlite3 *db){
int iOffset = -1;
if( db && sqlite3SafetyCheckSickOrOk(db) && db->errCode ){
|
| ︙ | ︙ | |||
187778 187779 187780 187781 187782 187783 187784 |
** not.
*/
case SQLITE_TESTCTRL_ISINIT: {
if( sqlite3GlobalConfig.isInit==0 ) rc = SQLITE_ERROR;
break;
}
| | | > > > | < | 189034 189035 189036 189037 189038 189039 189040 189041 189042 189043 189044 189045 189046 189047 189048 189049 189050 189051 189052 189053 189054 189055 189056 |
** not.
*/
case SQLITE_TESTCTRL_ISINIT: {
if( sqlite3GlobalConfig.isInit==0 ) rc = SQLITE_ERROR;
break;
}
/* sqlite3_test_control(SQLITE_TESTCTRL_IMPOSTER, db, dbName, mode, tnum);
**
** This test control is used to create imposter tables. "db" is a pointer
** to the database connection. dbName is the database name (ex: "main" or
** "temp") which will receive the imposter. "mode" turns imposter mode on
** or off. mode==0 means imposter mode is off. mode==1 means imposter mode
** is on. mode==2 means imposter mode is on but results in an imposter
** table that is read-only unless writable_schema is on. "tnum" is the
** root page of the b-tree to which the imposter table should connect.
**
** Enable imposter mode only when the schema has already been parsed. Then
** run a single CREATE TABLE statement to construct the imposter table in
** the parsed schema. Then turn imposter mode back off again.
**
** If onOff==0 and tnum>0 then reset the schema for all databases, causing
** the schema to be reparsed the next time it is needed. This has the
|
| ︙ | ︙ | |||
189021 189022 189023 189024 189025 189026 189027 189028 189029 189030 189031 189032 189033 189034 | ** ****************************************************************************** ** */ #ifndef _FTSINT_H #define _FTSINT_H /* #include <assert.h> */ /* #include <stdlib.h> */ /* #include <stddef.h> */ /* #include <stdio.h> */ /* #include <string.h> */ /* #include <stdarg.h> */ | > > > > > > > < < < < | 190279 190280 190281 190282 190283 190284 190285 190286 190287 190288 190289 190290 190291 190292 190293 190294 190295 190296 190297 190298 190299 190300 190301 190302 190303 190304 190305 190306 | ** ****************************************************************************** ** */ #ifndef _FTSINT_H #define _FTSINT_H /* ** Activate assert() only if SQLITE_TEST is enabled. */ #if !defined(NDEBUG) && !defined(SQLITE_DEBUG) # define NDEBUG 1 #endif /* #include <assert.h> */ /* #include <stdlib.h> */ /* #include <stddef.h> */ /* #include <stdio.h> */ /* #include <string.h> */ /* #include <stdarg.h> */ /* FTS3/FTS4 require virtual tables */ #ifdef SQLITE_OMIT_VIRTUALTABLE # undef SQLITE_ENABLE_FTS3 # undef SQLITE_ENABLE_FTS4 #endif /* |
| ︙ | ︙ | |||
189474 189475 189476 189477 189478 189479 189480 | typedef sqlite3_int64 i64; /* 8-byte signed integer */ /* ** Macro used to suppress compiler warnings for unused parameters. */ #define UNUSED_PARAMETER(x) (void)(x) | < < < < < < < | 190735 190736 190737 190738 190739 190740 190741 190742 190743 190744 190745 190746 190747 190748 | typedef sqlite3_int64 i64; /* 8-byte signed integer */ /* ** Macro used to suppress compiler warnings for unused parameters. */ #define UNUSED_PARAMETER(x) (void)(x) /* ** The TESTONLY macro is used to enclose variable declarations or ** other bits of code that are needed to support the arguments ** within testcase() and assert() macros. */ #if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) # define TESTONLY(X) X |
| ︙ | ︙ | |||
203755 203756 203757 203758 203759 203760 203761 |
};
/*
** An object of this type contains the state required to create or append
** to an appendable b-tree segment.
*/
struct IncrmergeWriter {
| | | | 205009 205010 205011 205012 205013 205014 205015 205016 205017 205018 205019 205020 205021 205022 205023 205024 |
};
/*
** An object of this type contains the state required to create or append
** to an appendable b-tree segment.
*/
struct IncrmergeWriter {
i64 nLeafEst; /* Space allocated for leaf blocks */
i64 nWork; /* Number of leaf pages flushed */
sqlite3_int64 iAbsLevel; /* Absolute level of input segments */
int iIdx; /* Index of *output* segment in iAbsLevel+1 */
sqlite3_int64 iStart; /* Block number of first allocated block */
sqlite3_int64 iEnd; /* Block number of last allocated block */
sqlite3_int64 nLeafData; /* Bytes of leaf page data so far */
u8 bNoLeafData; /* If true, store 0 for segment size */
NodeWriter aNodeWriter[FTS_MAX_APPENDABLE_HEIGHT];
|
| ︙ | ︙ | |||
204502 204503 204504 204505 204506 204507 204508 |
sqlite3_int64 iAbsLevel, /* Absolute level of input segments */
int iIdx, /* Index of new output segment */
Fts3MultiSegReader *pCsr, /* Cursor that data will be read from */
IncrmergeWriter *pWriter /* Populate this object */
){
int rc; /* Return Code */
int i; /* Iterator variable */
| | | | 205756 205757 205758 205759 205760 205761 205762 205763 205764 205765 205766 205767 205768 205769 205770 205771 205772 205773 205774 205775 205776 205777 205778 205779 205780 |
sqlite3_int64 iAbsLevel, /* Absolute level of input segments */
int iIdx, /* Index of new output segment */
Fts3MultiSegReader *pCsr, /* Cursor that data will be read from */
IncrmergeWriter *pWriter /* Populate this object */
){
int rc; /* Return Code */
int i; /* Iterator variable */
i64 nLeafEst = 0; /* Blocks allocated for leaf nodes */
sqlite3_stmt *pLeafEst = 0; /* SQL used to determine nLeafEst */
sqlite3_stmt *pFirstBlock = 0; /* SQL used to determine first block */
/* Calculate nLeafEst. */
rc = fts3SqlStmt(p, SQL_MAX_LEAF_NODE_ESTIMATE, &pLeafEst, 0);
if( rc==SQLITE_OK ){
sqlite3_bind_int64(pLeafEst, 1, iAbsLevel);
sqlite3_bind_int64(pLeafEst, 2, pCsr->nSegment);
if( SQLITE_ROW==sqlite3_step(pLeafEst) ){
nLeafEst = sqlite3_column_int64(pLeafEst, 0);
}
rc = sqlite3_reset(pLeafEst);
}
if( rc!=SQLITE_OK ) return rc;
/* Calculate the first block to use in the output segment */
rc = fts3SqlStmt(p, SQL_NEXT_SEGMENTS_ID, &pFirstBlock, 0);
|
| ︙ | ︙ | |||
205895 205896 205897 205898 205899 205900 205901 | /* #include "fts3Int.h" */ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) /* #include <string.h> */ /* #include <assert.h> */ | < < < < | 207149 207150 207151 207152 207153 207154 207155 207156 207157 207158 207159 207160 207161 207162 | /* #include "fts3Int.h" */ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) /* #include <string.h> */ /* #include <assert.h> */ /* ** Characters that may appear in the second argument to matchinfo(). */ #define FTS3_MATCHINFO_NPHRASE 'p' /* 1 value */ #define FTS3_MATCHINFO_NCOL 'c' /* 1 value */ #define FTS3_MATCHINFO_NDOC 'n' /* 1 value */ #define FTS3_MATCHINFO_AVGLENGTH 'a' /* nCol values */ |
| ︙ | ︙ | |||
210752 210753 210754 210755 210756 210757 210758 |
break;
}
switch( (u8)zIn[1] ){
case '\'':
jsonAppendChar(pOut, '\'');
break;
case 'v':
| | | 212002 212003 212004 212005 212006 212007 212008 212009 212010 212011 212012 212013 212014 212015 212016 |
break;
}
switch( (u8)zIn[1] ){
case '\'':
jsonAppendChar(pOut, '\'');
break;
case 'v':
jsonAppendRawNZ(pOut, "\\u000b", 6);
break;
case 'x':
if( sz2<4 ){
pOut->eErr |= JSTRING_MALFORMED;
sz2 = 2;
break;
}
|
| ︙ | ︙ | |||
211602 211603 211604 211605 211606 211607 211608 | /* ** Return the value of the BLOB node at index i. ** ** If the value is a primitive, return it as an SQL value. ** If the value is an array or object, return it as either | | > > > | > > > > | > | 212852 212853 212854 212855 212856 212857 212858 212859 212860 212861 212862 212863 212864 212865 212866 212867 212868 212869 212870 212871 212872 212873 212874 212875 212876 212877 212878 212879 212880 212881 212882 212883 212884 212885 212886 |
/*
** Return the value of the BLOB node at index i.
**
** If the value is a primitive, return it as an SQL value.
** If the value is an array or object, return it as either
** JSON text or the BLOB encoding, depending on the eMode flag
** as follows:
**
** eMode==0 JSONB if the JSON_B flag is set in userdata or
** text if the JSON_B flag is omitted from userdata.
**
** eMode==1 Text
**
** eMode==2 JSONB
*/
static void jsonReturnFromBlob(
JsonParse *pParse, /* Complete JSON parse tree */
u32 i, /* Index of the node */
sqlite3_context *pCtx, /* Return value for this function */
int eMode /* Format of return: text of JSONB */
){
u32 n, sz;
int rc;
sqlite3 *db = sqlite3_context_db_handle(pCtx);
assert( eMode>=0 && eMode<=2 );
n = jsonbPayloadSize(pParse, i, &sz);
if( n==0 ){
sqlite3_result_error(pCtx, "malformed JSON", -1);
return;
}
switch( pParse->aBlob[i] & 0x0f ){
case JSONB_NULL: {
|
| ︙ | ︙ | |||
211655 211656 211657 211658 211659 211660 211661 |
bNeg = 1;
}
z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz);
if( z==0 ) goto returnfromblob_oom;
rc = sqlite3DecOrHexToI64(z, &iRes);
sqlite3DbFree(db, z);
if( rc==0 ){
| > > > > > > > > > > > | > | 212913 212914 212915 212916 212917 212918 212919 212920 212921 212922 212923 212924 212925 212926 212927 212928 212929 212930 212931 212932 212933 212934 212935 212936 212937 212938 212939 |
bNeg = 1;
}
z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz);
if( z==0 ) goto returnfromblob_oom;
rc = sqlite3DecOrHexToI64(z, &iRes);
sqlite3DbFree(db, z);
if( rc==0 ){
if( iRes<0 ){
/* A hexadecimal literal with 16 significant digits and with the
** high-order bit set is a negative integer in SQLite (and hence
** iRes comes back as negative) but should be interpreted as a
** positive value if it occurs within JSON. The value is too
** large to appear as an SQLite integer so it must be converted
** into floating point. */
double r;
r = (double)*(sqlite3_uint64*)&iRes;
sqlite3_result_double(pCtx, bNeg ? -r : r);
}else{
sqlite3_result_int64(pCtx, bNeg ? -iRes : iRes);
}
}else if( rc==3 && bNeg ){
sqlite3_result_int64(pCtx, SMALLEST_INT64);
}else if( rc==1 ){
goto returnfromblob_malformed;
}else{
if( bNeg ){ n--; sz++; }
goto to_double;
|
| ︙ | ︙ | |||
211733 211734 211735 211736 211737 211738 211739 |
assert( iOut<=nOut );
zOut[iOut] = 0;
sqlite3_result_text(pCtx, zOut, iOut, SQLITE_DYNAMIC);
break;
}
case JSONB_ARRAY:
case JSONB_OBJECT: {
| > | > > > > > | | 213003 213004 213005 213006 213007 213008 213009 213010 213011 213012 213013 213014 213015 213016 213017 213018 213019 213020 213021 213022 213023 213024 |
assert( iOut<=nOut );
zOut[iOut] = 0;
sqlite3_result_text(pCtx, zOut, iOut, SQLITE_DYNAMIC);
break;
}
case JSONB_ARRAY:
case JSONB_OBJECT: {
if( eMode==0 ){
if( (SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx)) & JSON_BLOB)!=0 ){
eMode = 2;
}else{
eMode = 1;
}
}
if( eMode==2 ){
sqlite3_result_blob(pCtx, &pParse->aBlob[i], sz+n, SQLITE_TRANSIENT);
}else{
jsonReturnTextJsonFromBlob(pCtx, &pParse->aBlob[i], sz+n);
}
break;
}
default: {
|
| ︙ | ︙ | |||
213381 213382 213383 213384 213385 213386 213387 213388 213389 213390 213391 213392 213393 213394 213395 213396 213397 213398 213399 213400 213401 213402 213403 213404 213405 |
sqlite3_vtab_cursor base; /* Base class - must be first */
u32 iRowid; /* The rowid */
u32 i; /* Index in sParse.aBlob[] of current row */
u32 iEnd; /* EOF when i equals or exceeds this value */
u32 nRoot; /* Size of the root path in bytes */
u8 eType; /* Type of the container for element i */
u8 bRecursive; /* True for json_tree(). False for json_each() */
u32 nParent; /* Current nesting depth */
u32 nParentAlloc; /* Space allocated for aParent[] */
JsonParent *aParent; /* Parent elements of i */
sqlite3 *db; /* Database connection */
JsonString path; /* Current path */
JsonParse sParse; /* Parse of the input JSON */
};
typedef struct JsonEachConnection JsonEachConnection;
struct JsonEachConnection {
sqlite3_vtab base; /* Base class - must be first */
sqlite3 *db; /* Database connection */
};
/* Constructor for the json_each virtual table */
static int jsonEachConnect(
sqlite3 *db,
void *pAux,
| > > > | 214657 214658 214659 214660 214661 214662 214663 214664 214665 214666 214667 214668 214669 214670 214671 214672 214673 214674 214675 214676 214677 214678 214679 214680 214681 214682 214683 214684 |
sqlite3_vtab_cursor base; /* Base class - must be first */
u32 iRowid; /* The rowid */
u32 i; /* Index in sParse.aBlob[] of current row */
u32 iEnd; /* EOF when i equals or exceeds this value */
u32 nRoot; /* Size of the root path in bytes */
u8 eType; /* Type of the container for element i */
u8 bRecursive; /* True for json_tree(). False for json_each() */
u8 eMode; /* 1 for json_each(). 2 for jsonb_each() */
u32 nParent; /* Current nesting depth */
u32 nParentAlloc; /* Space allocated for aParent[] */
JsonParent *aParent; /* Parent elements of i */
sqlite3 *db; /* Database connection */
JsonString path; /* Current path */
JsonParse sParse; /* Parse of the input JSON */
};
typedef struct JsonEachConnection JsonEachConnection;
struct JsonEachConnection {
sqlite3_vtab base; /* Base class - must be first */
sqlite3 *db; /* Database connection */
u8 eMode; /* 1 for json_each(). 2 for jsonb_each() */
u8 bRecursive; /* True for json_tree(). False for json_each() */
};
/* Constructor for the json_each virtual table */
static int jsonEachConnect(
sqlite3 *db,
void *pAux,
|
| ︙ | ︙ | |||
213434 213435 213436 213437 213438 213439 213440 213441 213442 213443 213444 213445 213446 213447 213448 213449 213450 213451 |
"json HIDDEN,root HIDDEN)");
if( rc==SQLITE_OK ){
pNew = (JsonEachConnection*)sqlite3DbMallocZero(db, sizeof(*pNew));
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
pNew->db = db;
}
return rc;
}
/* destructor for json_each virtual table */
static int jsonEachDisconnect(sqlite3_vtab *pVtab){
JsonEachConnection *p = (JsonEachConnection*)pVtab;
sqlite3DbFree(p->db, pVtab);
return SQLITE_OK;
}
| > > | | > > < < < < < < < < < < | 214713 214714 214715 214716 214717 214718 214719 214720 214721 214722 214723 214724 214725 214726 214727 214728 214729 214730 214731 214732 214733 214734 214735 214736 214737 214738 214739 214740 214741 214742 214743 214744 214745 214746 214747 214748 214749 214750 214751 214752 214753 214754 214755 |
"json HIDDEN,root HIDDEN)");
if( rc==SQLITE_OK ){
pNew = (JsonEachConnection*)sqlite3DbMallocZero(db, sizeof(*pNew));
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
pNew->db = db;
pNew->eMode = argv[0][4]=='b' ? 2 : 1;
pNew->bRecursive = argv[0][4+pNew->eMode]=='t';
}
return rc;
}
/* destructor for json_each virtual table */
static int jsonEachDisconnect(sqlite3_vtab *pVtab){
JsonEachConnection *p = (JsonEachConnection*)pVtab;
sqlite3DbFree(p->db, pVtab);
return SQLITE_OK;
}
/* constructor for a JsonEachCursor object for json_each()/json_tree(). */
static int jsonEachOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
JsonEachConnection *pVtab = (JsonEachConnection*)p;
JsonEachCursor *pCur;
UNUSED_PARAMETER(p);
pCur = sqlite3DbMallocZero(pVtab->db, sizeof(*pCur));
if( pCur==0 ) return SQLITE_NOMEM;
pCur->db = pVtab->db;
pCur->eMode = pVtab->eMode;
pCur->bRecursive = pVtab->bRecursive;
jsonStringZero(&pCur->path);
*ppCursor = &pCur->base;
return SQLITE_OK;
}
/* Reset a JsonEachCursor back to its original state. Free any memory
** held. */
static void jsonEachCursorReset(JsonEachCursor *p){
jsonParseReset(&p->sParse);
jsonStringReset(&p->path);
sqlite3DbFree(p->db, p->aParent);
p->iRowid = 0;
|
| ︙ | ︙ | |||
213673 213674 213675 213676 213677 213678 213679 |
assert( p->eType==JSONB_ARRAY );
sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iKey);
}
break;
}
case JEACH_VALUE: {
u32 i = jsonSkipLabel(p);
| | | 214946 214947 214948 214949 214950 214951 214952 214953 214954 214955 214956 214957 214958 214959 214960 |
assert( p->eType==JSONB_ARRAY );
sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iKey);
}
break;
}
case JEACH_VALUE: {
u32 i = jsonSkipLabel(p);
jsonReturnFromBlob(&p->sParse, i, ctx, p->eMode);
if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY ){
sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
break;
}
case JEACH_TYPE: {
u32 i = jsonSkipLabel(p);
|
| ︙ | ︙ | |||
213917 213918 213919 213920 213921 213922 213923 |
static sqlite3_module jsonEachModule = {
0, /* iVersion */
0, /* xCreate */
jsonEachConnect, /* xConnect */
jsonEachBestIndex, /* xBestIndex */
jsonEachDisconnect, /* xDisconnect */
0, /* xDestroy */
| | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 215190 215191 215192 215193 215194 215195 215196 215197 215198 215199 215200 215201 215202 215203 215204 |
static sqlite3_module jsonEachModule = {
0, /* iVersion */
0, /* xCreate */
jsonEachConnect, /* xConnect */
jsonEachBestIndex, /* xBestIndex */
jsonEachDisconnect, /* xDisconnect */
0, /* xDestroy */
jsonEachOpen, /* xOpen - open a cursor */
jsonEachClose, /* xClose - close a cursor */
jsonEachFilter, /* xFilter - configure scan constraints */
jsonEachNext, /* xNext - advance a cursor */
jsonEachEof, /* xEof - check for end of scan */
jsonEachColumn, /* xColumn - read data */
jsonEachRowid, /* xRowid - read data */
0, /* xUpdate */
|
| ︙ | ︙ | |||
214035 214036 214037 214038 214039 214040 214041 | }; sqlite3InsertBuiltinFuncs(aJsonFunc, ArraySize(aJsonFunc)); #endif } #if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) /* | | > | | | < < < < | < > | > | | > | | 215279 215280 215281 215282 215283 215284 215285 215286 215287 215288 215289 215290 215291 215292 215293 215294 215295 215296 215297 215298 215299 215300 215301 215302 215303 215304 215305 215306 215307 |
};
sqlite3InsertBuiltinFuncs(aJsonFunc, ArraySize(aJsonFunc));
#endif
}
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON)
/*
** Register the JSON table-valued function named zName and return a
** pointer to its Module object. Return NULL if something goes wrong.
*/
SQLITE_PRIVATE Module *sqlite3JsonVtabRegister(sqlite3 *db, const char *zName){
unsigned int i;
static const char *azModule[] = {
"json_each", "json_tree", "jsonb_each", "jsonb_tree"
};
assert( sqlite3HashFind(&db->aModule, zName)==0 );
for(i=0; i<sizeof(azModule)/sizeof(azModule[0]); i++){
if( sqlite3StrICmp(azModule[i],zName)==0 ){
return sqlite3VtabCreateModule(db, azModule[i], &jsonEachModule, 0, 0);
}
}
return 0;
}
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) && !defined(SQLITE_OMIT_JSON) */
/************** End of json.c ************************************************/
/************** Begin file rtree.c *******************************************/
/*
** 2001 September 15
|
| ︙ | ︙ | |||
228287 228288 228289 228290 228291 228292 228293 |
&& !defined(SQLITE_OMIT_VIRTUALTABLE)
typedef struct DbpageTable DbpageTable;
typedef struct DbpageCursor DbpageCursor;
struct DbpageCursor {
sqlite3_vtab_cursor base; /* Base class. Must be first */
| | | | 229530 229531 229532 229533 229534 229535 229536 229537 229538 229539 229540 229541 229542 229543 229544 229545 |
&& !defined(SQLITE_OMIT_VIRTUALTABLE)
typedef struct DbpageTable DbpageTable;
typedef struct DbpageCursor DbpageCursor;
struct DbpageCursor {
sqlite3_vtab_cursor base; /* Base class. Must be first */
Pgno pgno; /* Current page number */
Pgno mxPgno; /* Last page to visit on this scan */
Pager *pPager; /* Pager being read/written */
DbPage *pPage1; /* Page 1 of the database */
int iDb; /* Index of database to analyze */
int szPage; /* Size of each page in bytes */
};
struct DbpageTable {
|
| ︙ | ︙ | |||
228425 228426 228427 228428 228429 228430 228431 |
pCsr = (DbpageCursor *)sqlite3_malloc64(sizeof(DbpageCursor));
if( pCsr==0 ){
return SQLITE_NOMEM_BKPT;
}else{
memset(pCsr, 0, sizeof(DbpageCursor));
pCsr->base.pVtab = pVTab;
| | | 229668 229669 229670 229671 229672 229673 229674 229675 229676 229677 229678 229679 229680 229681 229682 |
pCsr = (DbpageCursor *)sqlite3_malloc64(sizeof(DbpageCursor));
if( pCsr==0 ){
return SQLITE_NOMEM_BKPT;
}else{
memset(pCsr, 0, sizeof(DbpageCursor));
pCsr->base.pVtab = pVTab;
pCsr->pgno = 0;
}
*ppCursor = (sqlite3_vtab_cursor *)pCsr;
return SQLITE_OK;
}
/*
|
| ︙ | ︙ | |||
228525 228526 228527 228528 228529 228530 228531 |
sqlite3_context *ctx,
int i
){
DbpageCursor *pCsr = (DbpageCursor *)pCursor;
int rc = SQLITE_OK;
switch( i ){
case 0: { /* pgno */
| | | | 229768 229769 229770 229771 229772 229773 229774 229775 229776 229777 229778 229779 229780 229781 229782 229783 229784 229785 229786 229787 |
sqlite3_context *ctx,
int i
){
DbpageCursor *pCsr = (DbpageCursor *)pCursor;
int rc = SQLITE_OK;
switch( i ){
case 0: { /* pgno */
sqlite3_result_int64(ctx, (sqlite3_int64)pCsr->pgno);
break;
}
case 1: { /* data */
DbPage *pDbPage = 0;
if( pCsr->pgno==(Pgno)((PENDING_BYTE/pCsr->szPage)+1) ){
/* The pending byte page. Assume it is zeroed out. Attempting to
** request this page from the page is an SQLITE_CORRUPT error. */
sqlite3_result_zeroblob(ctx, pCsr->szPage);
}else{
rc = sqlite3PagerGet(pCsr->pPager, pCsr->pgno, (DbPage**)&pDbPage, 0);
if( rc==SQLITE_OK ){
sqlite3_result_blob(ctx, sqlite3PagerGetData(pDbPage), pCsr->szPage,
|
| ︙ | ︙ | |||
228604 228605 228606 228607 228608 228609 228610 |
goto update_fail;
}
if( argc==1 ){
zErr = "cannot delete";
goto update_fail;
}
if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
| | | | 229847 229848 229849 229850 229851 229852 229853 229854 229855 229856 229857 229858 229859 229860 229861 229862 229863 229864 |
goto update_fail;
}
if( argc==1 ){
zErr = "cannot delete";
goto update_fail;
}
if( sqlite3_value_type(argv[0])==SQLITE_NULL ){
pgno = (Pgno)sqlite3_value_int64(argv[2]);
isInsert = 1;
}else{
pgno = (Pgno)sqlite3_value_int64(argv[0]);
if( (Pgno)sqlite3_value_int(argv[1])!=pgno ){
zErr = "cannot insert";
goto update_fail;
}
isInsert = 0;
}
if( sqlite3_value_type(argv[4])==SQLITE_NULL ){
|
| ︙ | ︙ | |||
228742 228743 228744 228745 228746 228747 228748 228749 228750 228751 228752 228753 228754 228755 |
return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0);
}
#elif defined(SQLITE_ENABLE_DBPAGE_VTAB)
SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){ return SQLITE_OK; }
#endif /* SQLITE_ENABLE_DBSTAT_VTAB */
/************** End of dbpage.c **********************************************/
/************** Begin file sqlite3session.c **********************************/
#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
/* #include "sqlite3session.h" */
/* #include <assert.h> */
/* #include <string.h> */
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 229985 229986 229987 229988 229989 229990 229991 229992 229993 229994 229995 229996 229997 229998 229999 230000 230001 230002 230003 230004 230005 230006 230007 230008 230009 230010 230011 230012 230013 230014 230015 230016 230017 230018 230019 230020 230021 230022 230023 230024 230025 230026 230027 230028 230029 230030 230031 230032 230033 230034 230035 230036 230037 230038 230039 230040 230041 230042 230043 230044 230045 230046 230047 230048 230049 230050 230051 230052 230053 230054 230055 230056 230057 230058 230059 230060 230061 230062 230063 230064 230065 230066 230067 230068 230069 230070 230071 230072 230073 230074 230075 230076 230077 230078 230079 230080 230081 230082 230083 230084 230085 230086 230087 230088 230089 230090 230091 230092 230093 230094 230095 230096 230097 230098 230099 230100 230101 230102 230103 230104 230105 230106 230107 230108 230109 230110 230111 230112 230113 230114 230115 230116 230117 230118 230119 230120 230121 230122 230123 230124 230125 230126 230127 230128 230129 230130 230131 230132 230133 230134 230135 230136 230137 230138 230139 230140 230141 230142 230143 230144 230145 230146 230147 230148 230149 230150 230151 230152 230153 230154 230155 230156 230157 230158 230159 230160 230161 230162 230163 230164 230165 230166 230167 230168 230169 230170 230171 230172 230173 230174 230175 230176 230177 230178 230179 230180 230181 230182 230183 230184 230185 230186 230187 230188 230189 230190 230191 230192 230193 230194 230195 230196 230197 230198 230199 230200 230201 230202 230203 230204 230205 230206 230207 230208 230209 230210 230211 230212 230213 230214 230215 230216 230217 230218 230219 230220 230221 230222 230223 230224 230225 230226 230227 230228 230229 230230 230231 230232 230233 230234 230235 230236 230237 230238 230239 230240 230241 230242 230243 230244 230245 230246 230247 230248 230249 230250 230251 230252 230253 230254 230255 230256 230257 230258 230259 230260 230261 230262 230263 230264 230265 230266 230267 230268 230269 230270 230271 230272 230273 230274 230275 230276 230277 230278 230279 230280 230281 230282 230283 230284 230285 230286 230287 230288 230289 230290 230291 230292 230293 230294 230295 230296 230297 230298 230299 230300 230301 230302 230303 230304 230305 230306 230307 230308 230309 230310 230311 230312 230313 230314 230315 230316 230317 230318 230319 230320 230321 230322 230323 230324 230325 230326 230327 230328 230329 230330 230331 230332 230333 230334 230335 230336 230337 230338 230339 230340 230341 230342 230343 230344 230345 230346 230347 230348 230349 230350 230351 230352 230353 230354 230355 230356 230357 230358 230359 230360 230361 230362 230363 230364 230365 230366 230367 230368 230369 230370 230371 230372 230373 230374 230375 230376 230377 230378 230379 230380 230381 230382 230383 230384 230385 230386 230387 230388 230389 230390 230391 230392 230393 230394 230395 230396 230397 230398 230399 230400 230401 230402 230403 230404 230405 230406 230407 230408 230409 230410 230411 230412 230413 230414 230415 230416 230417 230418 230419 230420 230421 230422 230423 230424 230425 230426 230427 230428 230429 230430 230431 230432 230433 230434 230435 230436 230437 230438 230439 230440 230441 230442 230443 230444 230445 230446 230447 230448 230449 230450 230451 230452 230453 230454 230455 230456 230457 230458 230459 230460 230461 230462 230463 230464 230465 230466 230467 230468 230469 230470 230471 230472 230473 230474 230475 230476 230477 230478 230479 230480 230481 230482 230483 230484 230485 230486 230487 230488 230489 230490 230491 230492 230493 230494 230495 230496 230497 230498 230499 230500 230501 230502 230503 230504 230505 230506 230507 230508 230509 230510 230511 230512 230513 230514 230515 230516 230517 230518 230519 230520 230521 230522 230523 230524 230525 230526 230527 |
return sqlite3_create_module(db, "sqlite_dbpage", &dbpage_module, 0);
}
#elif defined(SQLITE_ENABLE_DBPAGE_VTAB)
SQLITE_PRIVATE int sqlite3DbpageRegister(sqlite3 *db){ return SQLITE_OK; }
#endif /* SQLITE_ENABLE_DBSTAT_VTAB */
/************** End of dbpage.c **********************************************/
/************** Begin file carray.c ******************************************/
/*
** 2016-06-29
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
**
** This file implements a table-valued-function that
** returns the values in a C-language array.
** Examples:
**
** SELECT * FROM carray($ptr,5)
**
** The query above returns 5 integers contained in a C-language array
** at the address $ptr. $ptr is a pointer to the array of integers.
** The pointer value must be assigned to $ptr using the
** sqlite3_bind_pointer() interface with a pointer type of "carray".
** For example:
**
** static int aX[] = { 53, 9, 17, 2231, 4, 99 };
** int i = sqlite3_bind_parameter_index(pStmt, "$ptr");
** sqlite3_bind_pointer(pStmt, i, aX, "carray", 0);
**
** There is an optional third parameter to determine the datatype of
** the C-language array. Allowed values of the third parameter are
** 'int32', 'int64', 'double', 'char*', 'struct iovec'. Example:
**
** SELECT * FROM carray($ptr,10,'char*');
**
** The default value of the third parameter is 'int32'.
**
** HOW IT WORKS
**
** The carray "function" is really a virtual table with the
** following schema:
**
** CREATE TABLE carray(
** value,
** pointer HIDDEN,
** count HIDDEN,
** ctype TEXT HIDDEN
** );
**
** If the hidden columns "pointer" and "count" are unconstrained, then
** the virtual table has no rows. Otherwise, the virtual table interprets
** the integer value of "pointer" as a pointer to the array and "count"
** as the number of elements in the array. The virtual table steps through
** the array, element by element.
*/
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_CARRAY)
/* #include "sqliteInt.h" */
#if defined(_WIN32) || defined(__RTP__) || defined(_WRS_KERNEL)
struct iovec {
void *iov_base;
size_t iov_len;
};
#else
# include <sys/uio.h>
#endif
/*
** Names of allowed datatypes
*/
static const char *azType[] = { "int32", "int64", "double", "char*",
"struct iovec" };
/*
** Structure used to hold the sqlite3_carray_bind() information
*/
typedef struct carray_bind carray_bind;
struct carray_bind {
void *aData; /* The data */
int nData; /* Number of elements */
int mFlags; /* Control flags */
void (*xDel)(void*); /* Destructor for aData */
};
/* carray_cursor is a subclass of sqlite3_vtab_cursor which will
** serve as the underlying representation of a cursor that scans
** over rows of the result
*/
typedef struct carray_cursor carray_cursor;
struct carray_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
sqlite3_int64 iRowid; /* The rowid */
void *pPtr; /* Pointer to the array of values */
sqlite3_int64 iCnt; /* Number of integers in the array */
unsigned char eType; /* One of the CARRAY_type values */
};
/*
** The carrayConnect() method is invoked to create a new
** carray_vtab that describes the carray virtual table.
**
** Think of this routine as the constructor for carray_vtab objects.
**
** All this routine needs to do is:
**
** (1) Allocate the carray_vtab object and initialize all fields.
**
** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
** result set of queries against carray will look like.
*/
static int carrayConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
sqlite3_vtab *pNew;
int rc;
/* Column numbers */
#define CARRAY_COLUMN_VALUE 0
#define CARRAY_COLUMN_POINTER 1
#define CARRAY_COLUMN_COUNT 2
#define CARRAY_COLUMN_CTYPE 3
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(value,pointer hidden,count hidden,ctype hidden)");
if( rc==SQLITE_OK ){
pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
}
return rc;
}
/*
** This method is the destructor for carray_cursor objects.
*/
static int carrayDisconnect(sqlite3_vtab *pVtab){
sqlite3_free(pVtab);
return SQLITE_OK;
}
/*
** Constructor for a new carray_cursor object.
*/
static int carrayOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
carray_cursor *pCur;
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
return SQLITE_OK;
}
/*
** Destructor for a carray_cursor.
*/
static int carrayClose(sqlite3_vtab_cursor *cur){
sqlite3_free(cur);
return SQLITE_OK;
}
/*
** Advance a carray_cursor to its next row of output.
*/
static int carrayNext(sqlite3_vtab_cursor *cur){
carray_cursor *pCur = (carray_cursor*)cur;
pCur->iRowid++;
return SQLITE_OK;
}
/*
** Return values of columns for the row at which the carray_cursor
** is currently pointing.
*/
static int carrayColumn(
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
int i /* Which column to return */
){
carray_cursor *pCur = (carray_cursor*)cur;
sqlite3_int64 x = 0;
switch( i ){
case CARRAY_COLUMN_POINTER: return SQLITE_OK;
case CARRAY_COLUMN_COUNT: x = pCur->iCnt; break;
case CARRAY_COLUMN_CTYPE: {
sqlite3_result_text(ctx, azType[pCur->eType], -1, SQLITE_STATIC);
return SQLITE_OK;
}
default: {
switch( pCur->eType ){
case CARRAY_INT32: {
int *p = (int*)pCur->pPtr;
sqlite3_result_int(ctx, p[pCur->iRowid-1]);
return SQLITE_OK;
}
case CARRAY_INT64: {
sqlite3_int64 *p = (sqlite3_int64*)pCur->pPtr;
sqlite3_result_int64(ctx, p[pCur->iRowid-1]);
return SQLITE_OK;
}
case CARRAY_DOUBLE: {
double *p = (double*)pCur->pPtr;
sqlite3_result_double(ctx, p[pCur->iRowid-1]);
return SQLITE_OK;
}
case CARRAY_TEXT: {
const char **p = (const char**)pCur->pPtr;
sqlite3_result_text(ctx, p[pCur->iRowid-1], -1, SQLITE_TRANSIENT);
return SQLITE_OK;
}
default: {
const struct iovec *p = (struct iovec*)pCur->pPtr;
assert( pCur->eType==CARRAY_BLOB );
sqlite3_result_blob(ctx, p[pCur->iRowid-1].iov_base,
(int)p[pCur->iRowid-1].iov_len, SQLITE_TRANSIENT);
return SQLITE_OK;
}
}
}
}
sqlite3_result_int64(ctx, x);
return SQLITE_OK;
}
/*
** Return the rowid for the current row. In this implementation, the
** rowid is the same as the output value.
*/
static int carrayRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
carray_cursor *pCur = (carray_cursor*)cur;
*pRowid = pCur->iRowid;
return SQLITE_OK;
}
/*
** Return TRUE if the cursor has been moved off of the last
** row of output.
*/
static int carrayEof(sqlite3_vtab_cursor *cur){
carray_cursor *pCur = (carray_cursor*)cur;
return pCur->iRowid>pCur->iCnt;
}
/*
** This method is called to "rewind" the carray_cursor object back
** to the first row of output.
*/
static int carrayFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
carray_cursor *pCur = (carray_cursor *)pVtabCursor;
pCur->pPtr = 0;
pCur->iCnt = 0;
switch( idxNum ){
case 1: {
carray_bind *pBind = sqlite3_value_pointer(argv[0], "carray-bind");
if( pBind==0 ) break;
pCur->pPtr = pBind->aData;
pCur->iCnt = pBind->nData;
pCur->eType = pBind->mFlags & 0x07;
break;
}
case 2:
case 3: {
pCur->pPtr = sqlite3_value_pointer(argv[0], "carray");
pCur->iCnt = pCur->pPtr ? sqlite3_value_int64(argv[1]) : 0;
if( idxNum<3 ){
pCur->eType = CARRAY_INT32;
}else{
unsigned char i;
const char *zType = (const char*)sqlite3_value_text(argv[2]);
for(i=0; i<sizeof(azType)/sizeof(azType[0]); i++){
if( sqlite3_stricmp(zType, azType[i])==0 ) break;
}
if( i>=sizeof(azType)/sizeof(azType[0]) ){
pVtabCursor->pVtab->zErrMsg = sqlite3_mprintf(
"unknown datatype: %Q", zType);
return SQLITE_ERROR;
}else{
pCur->eType = i;
}
}
break;
}
}
pCur->iRowid = 1;
return SQLITE_OK;
}
/*
** SQLite will invoke this method one or more times while planning a query
** that uses the carray virtual table. This routine needs to create
** a query plan for each invocation and compute an estimated cost for that
** plan.
**
** In this implementation idxNum is used to represent the
** query plan. idxStr is unused.
**
** idxNum is:
**
** 1 If only the pointer= constraint exists. In this case, the
** parameter must be bound using sqlite3_carray_bind().
**
** 2 if the pointer= and count= constraints exist.
**
** 3 if the ctype= constraint also exists.
**
** idxNum is 0 otherwise and carray becomes an empty table.
*/
static int carrayBestIndex(
sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo
){
int i; /* Loop over constraints */
int ptrIdx = -1; /* Index of the pointer= constraint, or -1 if none */
int cntIdx = -1; /* Index of the count= constraint, or -1 if none */
int ctypeIdx = -1; /* Index of the ctype= constraint, or -1 if none */
unsigned seen = 0; /* Bitmask of == constrainted columns */
const struct sqlite3_index_constraint *pConstraint;
pConstraint = pIdxInfo->aConstraint;
for(i=0; i<pIdxInfo->nConstraint; i++, pConstraint++){
if( pConstraint->op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
if( pConstraint->iColumn>=0 ) seen |= 1 << pConstraint->iColumn;
if( pConstraint->usable==0 ) continue;
switch( pConstraint->iColumn ){
case CARRAY_COLUMN_POINTER:
ptrIdx = i;
break;
case CARRAY_COLUMN_COUNT:
cntIdx = i;
break;
case CARRAY_COLUMN_CTYPE:
ctypeIdx = i;
break;
}
}
if( ptrIdx>=0 ){
pIdxInfo->aConstraintUsage[ptrIdx].argvIndex = 1;
pIdxInfo->aConstraintUsage[ptrIdx].omit = 1;
pIdxInfo->estimatedCost = (double)1;
pIdxInfo->estimatedRows = 100;
pIdxInfo->idxNum = 1;
if( cntIdx>=0 ){
pIdxInfo->aConstraintUsage[cntIdx].argvIndex = 2;
pIdxInfo->aConstraintUsage[cntIdx].omit = 1;
pIdxInfo->idxNum = 2;
if( ctypeIdx>=0 ){
pIdxInfo->aConstraintUsage[ctypeIdx].argvIndex = 3;
pIdxInfo->aConstraintUsage[ctypeIdx].omit = 1;
pIdxInfo->idxNum = 3;
}else if( seen & (1<<CARRAY_COLUMN_CTYPE) ){
/* In a three-argument carray(), we need to know the value of all
** three arguments */
return SQLITE_CONSTRAINT;
}
}else if( seen & (1<<CARRAY_COLUMN_COUNT) ){
/* In a two-argument carray(), we need to know the value of both
** arguments */
return SQLITE_CONSTRAINT;
}
}else{
pIdxInfo->estimatedCost = (double)2147483647;
pIdxInfo->estimatedRows = 2147483647;
pIdxInfo->idxNum = 0;
}
return SQLITE_OK;
}
/*
** This following structure defines all the methods for the
** carray virtual table.
*/
static sqlite3_module carrayModule = {
0, /* iVersion */
0, /* xCreate */
carrayConnect, /* xConnect */
carrayBestIndex, /* xBestIndex */
carrayDisconnect, /* xDisconnect */
0, /* xDestroy */
carrayOpen, /* xOpen - open a cursor */
carrayClose, /* xClose - close a cursor */
carrayFilter, /* xFilter - configure scan constraints */
carrayNext, /* xNext - advance a cursor */
carrayEof, /* xEof - check for end of scan */
carrayColumn, /* xColumn - read data */
carrayRowid, /* xRowid - read data */
0, /* xUpdate */
0, /* xBegin */
0, /* xSync */
0, /* xCommit */
0, /* xRollback */
0, /* xFindMethod */
0, /* xRename */
0, /* xSavepoint */
0, /* xRelease */
0, /* xRollbackTo */
0, /* xShadow */
0 /* xIntegrity */
};
/*
** Destructor for the carray_bind object
*/
static void carrayBindDel(void *pPtr){
carray_bind *p = (carray_bind*)pPtr;
if( p->xDel!=SQLITE_STATIC ){
p->xDel(p->aData);
}
sqlite3_free(p);
}
/*
** Invoke this interface in order to bind to the single-argument
** version of CARRAY().
*/
SQLITE_API int sqlite3_carray_bind(
sqlite3_stmt *pStmt,
int idx,
void *aData,
int nData,
int mFlags,
void (*xDestroy)(void*)
){
carray_bind *pNew = 0;
int i;
int rc = SQLITE_OK;
/* Ensure that the mFlags value is acceptable. */
assert( CARRAY_INT32==0 && CARRAY_INT64==1 && CARRAY_DOUBLE==2 );
assert( CARRAY_TEXT==3 && CARRAY_BLOB==4 );
if( mFlags<CARRAY_INT32 || mFlags>CARRAY_BLOB ){
rc = SQLITE_ERROR;
goto carray_bind_error;
}
pNew = sqlite3_malloc64(sizeof(*pNew));
if( pNew==0 ){
rc = SQLITE_NOMEM;
goto carray_bind_error;
}
pNew->nData = nData;
pNew->mFlags = mFlags;
if( xDestroy==SQLITE_TRANSIENT ){
sqlite3_int64 sz = nData;
switch( mFlags ){
case CARRAY_INT32: sz *= 4; break;
case CARRAY_INT64: sz *= 8; break;
case CARRAY_DOUBLE: sz *= 8; break;
case CARRAY_TEXT: sz *= sizeof(char*); break;
default: sz *= sizeof(struct iovec); break;
}
if( mFlags==CARRAY_TEXT ){
for(i=0; i<nData; i++){
const char *z = ((char**)aData)[i];
if( z ) sz += strlen(z) + 1;
}
}else if( mFlags==CARRAY_BLOB ){
for(i=0; i<nData; i++){
sz += ((struct iovec*)aData)[i].iov_len;
}
}
pNew->aData = sqlite3_malloc64( sz );
if( pNew->aData==0 ){
rc = SQLITE_NOMEM;
goto carray_bind_error;
}
if( mFlags==CARRAY_TEXT ){
char **az = (char**)pNew->aData;
char *z = (char*)&az[nData];
for(i=0; i<nData; i++){
const char *zData = ((char**)aData)[i];
sqlite3_int64 n;
if( zData==0 ){
az[i] = 0;
continue;
}
az[i] = z;
n = strlen(zData);
memcpy(z, zData, n+1);
z += n+1;
}
}else if( mFlags==CARRAY_BLOB ){
struct iovec *p = (struct iovec*)pNew->aData;
unsigned char *z = (unsigned char*)&p[nData];
for(i=0; i<nData; i++){
size_t n = ((struct iovec*)aData)[i].iov_len;
p[i].iov_len = n;
p[i].iov_base = z;
z += n;
memcpy(p[i].iov_base, ((struct iovec*)aData)[i].iov_base, n);
}
}else{
memcpy(pNew->aData, aData, sz);
}
pNew->xDel = sqlite3_free;
}else{
pNew->aData = aData;
pNew->xDel = xDestroy;
}
return sqlite3_bind_pointer(pStmt, idx, pNew, "carray-bind", carrayBindDel);
carray_bind_error:
if( xDestroy!=SQLITE_STATIC && xDestroy!=SQLITE_TRANSIENT ){
xDestroy(aData);
}
sqlite3_free(pNew);
return rc;
}
/*
** Invoke this routine to register the carray() function.
*/
SQLITE_PRIVATE Module *sqlite3CarrayRegister(sqlite3 *db){
return sqlite3VtabCreateModule(db, "carray", &carrayModule, 0, 0);
}
#endif /* !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_CARRAY) */
/************** End of carray.c **********************************************/
/************** Begin file sqlite3session.c **********************************/
#if defined(SQLITE_ENABLE_SESSION) && defined(SQLITE_ENABLE_PREUPDATE_HOOK)
/* #include "sqlite3session.h" */
/* #include <assert.h> */
/* #include <string.h> */
|
| ︙ | ︙ | |||
231559 231560 231561 231562 231563 231564 231565 231566 231567 231568 231569 231570 231571 231572 |
}
}
assert( (a - p->aRecord)==p->nRecord );
}
return rc;
}
/*
** Formulate and prepare a SELECT statement to retrieve a row from table
** zTab in database zDb based on its primary key. i.e.
**
** SELECT *, <noop-test> FROM zDb.zTab WHERE (pk1, pk2,...) IS (?1, ?2,...)
**
| > > > > > > > > > > > > > | 233331 233332 233333 233334 233335 233336 233337 233338 233339 233340 233341 233342 233343 233344 233345 233346 233347 233348 233349 233350 233351 233352 233353 233354 233355 233356 233357 |
}
}
assert( (a - p->aRecord)==p->nRecord );
}
return rc;
}
static int sessionPrepare(
sqlite3 *db,
sqlite3_stmt **pp,
char **pzErrmsg,
const char *zSql
){
int rc = sqlite3_prepare_v2(db, zSql, -1, pp, 0);
if( pzErrmsg && rc!=SQLITE_OK ){
*pzErrmsg = sqlite3_mprintf("%s", sqlite3_errmsg(db));
}
return rc;
}
/*
** Formulate and prepare a SELECT statement to retrieve a row from table
** zTab in database zDb based on its primary key. i.e.
**
** SELECT *, <noop-test> FROM zDb.zTab WHERE (pk1, pk2,...) IS (?1, ?2,...)
**
|
| ︙ | ︙ | |||
231581 231582 231583 231584 231585 231586 231587 | int bIgnoreNoop, const char *zDb, /* Database name */ const char *zTab, /* Table name */ int bRowid, int nCol, /* Number of columns in table */ const char **azCol, /* Names of table columns */ u8 *abPK, /* PRIMARY KEY array */ | | > < | 233366 233367 233368 233369 233370 233371 233372 233373 233374 233375 233376 233377 233378 233379 233380 233381 233382 233383 233384 233385 |
int bIgnoreNoop,
const char *zDb, /* Database name */
const char *zTab, /* Table name */
int bRowid,
int nCol, /* Number of columns in table */
const char **azCol, /* Names of table columns */
u8 *abPK, /* PRIMARY KEY array */
sqlite3_stmt **ppStmt, /* OUT: Prepared SELECT statement */
char **pzErrmsg /* OUT: Error message */
){
int rc = SQLITE_OK;
char *zSql = 0;
const char *zSep = "";
int i;
SessionBuffer cols = {0, 0, 0};
SessionBuffer nooptest = {0, 0, 0};
SessionBuffer pkfield = {0, 0, 0};
SessionBuffer pkvar = {0, 0, 0};
|
| ︙ | ︙ | |||
231666 231667 231668 231669 231670 231671 231672 |
}
zSql = (char*)buf.aBuf;
nSql = buf.nBuf;
}
#endif
if( rc==SQLITE_OK ){
| | | 233451 233452 233453 233454 233455 233456 233457 233458 233459 233460 233461 233462 233463 233464 233465 |
}
zSql = (char*)buf.aBuf;
nSql = buf.nBuf;
}
#endif
if( rc==SQLITE_OK ){
rc = sessionPrepare(db, ppStmt, pzErrmsg, zSql);
}
sqlite3_free(zSql);
sqlite3_free(nooptest.aBuf);
sqlite3_free(pkfield.aBuf);
sqlite3_free(pkvar.aBuf);
sqlite3_free(cols.aBuf);
return rc;
|
| ︙ | ︙ | |||
231830 231831 231832 231833 231834 231835 231836 |
/* Write a table header */
sessionAppendTableHdr(&buf, bPatchset, pTab, &rc);
/* Build and compile a statement to execute: */
if( rc==SQLITE_OK ){
rc = sessionSelectStmt(db, 0, pSession->zDb,
| | | 233615 233616 233617 233618 233619 233620 233621 233622 233623 233624 233625 233626 233627 233628 233629 |
/* Write a table header */
sessionAppendTableHdr(&buf, bPatchset, pTab, &rc);
/* Build and compile a statement to execute: */
if( rc==SQLITE_OK ){
rc = sessionSelectStmt(db, 0, pSession->zDb,
zName, pTab->bRowid, pTab->nCol, pTab->azCol, pTab->abPK, &pSel, 0
);
}
nNoop = buf.nBuf;
for(i=0; i<pTab->nChange && rc==SQLITE_OK; i++){
SessionChange *p; /* Used to iterate through changes */
|
| ︙ | ︙ | |||
233039 233040 233041 233042 233043 233044 233045 233046 233047 233048 233049 233050 233051 233052 | int bInvertConstraints; /* Invert when iterating constraints buffer */ SessionBuffer constraints; /* Deferred constraints are stored here */ SessionBuffer rebase; /* Rebase information (if any) here */ u8 bRebaseStarted; /* If table header is already in rebase */ u8 bRebase; /* True to collect rebase information */ u8 bIgnoreNoop; /* True to ignore no-op conflicts */ int bRowid; }; /* Number of prepared UPDATE statements to cache. */ #define SESSION_UPDATE_CACHE_SZ 12 /* ** Find a prepared UPDATE statement suitable for the UPDATE step currently | > | 234824 234825 234826 234827 234828 234829 234830 234831 234832 234833 234834 234835 234836 234837 234838 | int bInvertConstraints; /* Invert when iterating constraints buffer */ SessionBuffer constraints; /* Deferred constraints are stored here */ SessionBuffer rebase; /* Rebase information (if any) here */ u8 bRebaseStarted; /* If table header is already in rebase */ u8 bRebase; /* True to collect rebase information */ u8 bIgnoreNoop; /* True to ignore no-op conflicts */ int bRowid; char *zErr; /* Error message, if any */ }; /* Number of prepared UPDATE statements to cache. */ #define SESSION_UPDATE_CACHE_SZ 12 /* ** Find a prepared UPDATE statement suitable for the UPDATE step currently |
| ︙ | ︙ | |||
233264 233265 233266 233267 233268 233269 233270 |
zSep = "AND ";
}
}
sessionAppendStr(&buf, ")", &rc);
}
if( rc==SQLITE_OK ){
| | | 235050 235051 235052 235053 235054 235055 235056 235057 235058 235059 235060 235061 235062 235063 235064 |
zSep = "AND ";
}
}
sessionAppendStr(&buf, ")", &rc);
}
if( rc==SQLITE_OK ){
rc = sessionPrepare(db, &p->pDelete, &p->zErr, (char*)buf.aBuf);
}
sqlite3_free(buf.aBuf);
return rc;
}
/*
|
| ︙ | ︙ | |||
233291 233292 233293 233294 233295 233296 233297 |
static int sessionSelectRow(
sqlite3 *db, /* Database handle */
const char *zTab, /* Table name */
SessionApplyCtx *p /* Session changeset-apply context */
){
/* TODO */
return sessionSelectStmt(db, p->bIgnoreNoop,
| | | 235077 235078 235079 235080 235081 235082 235083 235084 235085 235086 235087 235088 235089 235090 235091 |
static int sessionSelectRow(
sqlite3 *db, /* Database handle */
const char *zTab, /* Table name */
SessionApplyCtx *p /* Session changeset-apply context */
){
/* TODO */
return sessionSelectStmt(db, p->bIgnoreNoop,
"main", zTab, p->bRowid, p->nCol, p->azCol, p->abPK, &p->pSelect, &p->zErr
);
}
/*
** Formulate and prepare an INSERT statement to add a record to table zTab.
** For example:
**
|
| ︙ | ︙ | |||
233328 233329 233330 233331 233332 233333 233334 |
sessionAppendStr(&buf, ") VALUES(?", &rc);
for(i=1; i<p->nCol; i++){
sessionAppendStr(&buf, ", ?", &rc);
}
sessionAppendStr(&buf, ")", &rc);
if( rc==SQLITE_OK ){
| | < < < < | | | 235114 235115 235116 235117 235118 235119 235120 235121 235122 235123 235124 235125 235126 235127 235128 235129 235130 235131 235132 235133 235134 235135 235136 235137 235138 235139 235140 235141 235142 235143 235144 235145 235146 235147 235148 235149 235150 |
sessionAppendStr(&buf, ") VALUES(?", &rc);
for(i=1; i<p->nCol; i++){
sessionAppendStr(&buf, ", ?", &rc);
}
sessionAppendStr(&buf, ")", &rc);
if( rc==SQLITE_OK ){
rc = sessionPrepare(db, &p->pInsert, &p->zErr, (char*)buf.aBuf);
}
sqlite3_free(buf.aBuf);
return rc;
}
/*
** Prepare statements for applying changes to the sqlite_stat1 table.
** These are similar to those created by sessionSelectRow(),
** sessionInsertRow(), sessionUpdateRow() and sessionDeleteRow() for
** other tables.
*/
static int sessionStat1Sql(sqlite3 *db, SessionApplyCtx *p){
int rc = sessionSelectRow(db, "sqlite_stat1", p);
if( rc==SQLITE_OK ){
rc = sessionPrepare(db, &p->pInsert, 0,
"INSERT INTO main.sqlite_stat1 VALUES(?1, "
"CASE WHEN length(?2)=0 AND typeof(?2)='blob' THEN NULL ELSE ?2 END, "
"?3)"
);
}
if( rc==SQLITE_OK ){
rc = sessionPrepare(db, &p->pDelete, 0,
"DELETE FROM main.sqlite_stat1 WHERE tbl=?1 AND idx IS "
"CASE WHEN length(?2)=0 AND typeof(?2)='blob' THEN NULL ELSE ?2 END "
"AND (?4 OR stat IS ?3)"
);
}
return rc;
}
|
| ︙ | ︙ | |||
233578 233579 233580 233581 233582 233583 233584 |
int eType, /* Either CHANGESET_DATA or CONFLICT */
SessionApplyCtx *p, /* changeset_apply() context */
sqlite3_changeset_iter *pIter, /* Changeset iterator */
int(*xConflict)(void *, int, sqlite3_changeset_iter*),
void *pCtx, /* First argument for conflict handler */
int *pbReplace /* OUT: Set to true if PK row is found */
){
| | | | < < | > > | 235360 235361 235362 235363 235364 235365 235366 235367 235368 235369 235370 235371 235372 235373 235374 235375 235376 235377 235378 235379 235380 235381 235382 235383 235384 235385 235386 235387 235388 235389 235390 235391 235392 235393 235394 235395 235396 235397 235398 235399 235400 235401 235402 235403 235404 235405 235406 235407 235408 235409 235410 235411 235412 235413 |
int eType, /* Either CHANGESET_DATA or CONFLICT */
SessionApplyCtx *p, /* changeset_apply() context */
sqlite3_changeset_iter *pIter, /* Changeset iterator */
int(*xConflict)(void *, int, sqlite3_changeset_iter*),
void *pCtx, /* First argument for conflict handler */
int *pbReplace /* OUT: Set to true if PK row is found */
){
int res = SQLITE_CHANGESET_OMIT;/* Value returned by conflict handler */
int rc;
int nCol;
int op;
const char *zDummy;
sqlite3changeset_op(pIter, &zDummy, &nCol, &op, 0);
assert( eType==SQLITE_CHANGESET_CONFLICT || eType==SQLITE_CHANGESET_DATA );
assert( SQLITE_CHANGESET_CONFLICT+1==SQLITE_CHANGESET_CONSTRAINT );
assert( SQLITE_CHANGESET_DATA+1==SQLITE_CHANGESET_NOTFOUND );
/* Bind the new.* PRIMARY KEY values to the SELECT statement. */
if( pbReplace ){
rc = sessionSeekToRow(pIter, p);
}else{
rc = SQLITE_OK;
}
if( rc==SQLITE_ROW ){
/* There exists another row with the new.* primary key. */
if( 0==p->bIgnoreNoop
|| 0==sqlite3_column_int(p->pSelect, sqlite3_column_count(p->pSelect)-1)
){
pIter->pConflict = p->pSelect;
res = xConflict(pCtx, eType, pIter);
pIter->pConflict = 0;
}
rc = sqlite3_reset(p->pSelect);
}else if( rc==SQLITE_OK ){
if( p->bDeferConstraints && eType==SQLITE_CHANGESET_CONFLICT ){
/* Instead of invoking the conflict handler, append the change blob
** to the SessionApplyCtx.constraints buffer. */
u8 *aBlob = &pIter->in.aData[pIter->in.iCurrent];
int nBlob = pIter->in.iNext - pIter->in.iCurrent;
sessionAppendBlob(&p->constraints, aBlob, nBlob, &rc);
return SQLITE_OK;
}else if( p->bIgnoreNoop==0 || op!=SQLITE_DELETE
|| eType==SQLITE_CHANGESET_CONFLICT
){
/* No other row with the new.* primary key. */
res = xConflict(pCtx, eType+1, pIter);
if( res==SQLITE_CHANGESET_REPLACE ) rc = SQLITE_MISUSE;
}
}
if( rc==SQLITE_OK ){
|
| ︙ | ︙ | |||
233715 233716 233717 233718 233719 233720 233721 |
if( rc==SQLITE_OK && sqlite3_bind_parameter_count(p->pDelete)>nCol ){
rc = sqlite3_bind_int(p->pDelete, nCol+1, (pbRetry==0 || abPK));
}
if( rc!=SQLITE_OK ) return rc;
sqlite3_step(p->pDelete);
rc = sqlite3_reset(p->pDelete);
| | | 235497 235498 235499 235500 235501 235502 235503 235504 235505 235506 235507 235508 235509 235510 235511 |
if( rc==SQLITE_OK && sqlite3_bind_parameter_count(p->pDelete)>nCol ){
rc = sqlite3_bind_int(p->pDelete, nCol+1, (pbRetry==0 || abPK));
}
if( rc!=SQLITE_OK ) return rc;
sqlite3_step(p->pDelete);
rc = sqlite3_reset(p->pDelete);
if( rc==SQLITE_OK && sqlite3_changes(p->db)==0 ){
rc = sessionConflictHandler(
SQLITE_CHANGESET_DATA, p, pIter, xConflict, pCtx, pbRetry
);
}else if( (rc&0xff)==SQLITE_CONSTRAINT ){
rc = sessionConflictHandler(
SQLITE_CHANGESET_CONFLICT, p, pIter, xConflict, pCtx, 0
);
|
| ︙ | ︙ | |||
234120 234121 234122 234123 234124 234125 234126 234127 234128 234129 234130 234131 234132 234133 234134 234135 234136 234137 234138 234139 234140 234141 234142 234143 234144 234145 234146 234147 234148 234149 234150 |
sqlite3_exec(db, "ROLLBACK TO changeset_apply", 0, 0, 0);
sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
}
}
assert( sApply.bRebase || sApply.rebase.nBuf==0 );
if( rc==SQLITE_OK && bPatchset==0 && sApply.bRebase ){
*ppRebase = (void*)sApply.rebase.aBuf;
*pnRebase = sApply.rebase.nBuf;
sApply.rebase.aBuf = 0;
}
sessionUpdateFree(&sApply);
sqlite3_finalize(sApply.pInsert);
sqlite3_finalize(sApply.pDelete);
sqlite3_finalize(sApply.pSelect);
sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */
sqlite3_free((char*)sApply.constraints.aBuf);
sqlite3_free((char*)sApply.rebase.aBuf);
if( (flags & SQLITE_CHANGESETAPPLY_FKNOACTION) && savedFlag==0 ){
assert( db->flags & SQLITE_FkNoAction );
db->flags &= ~((u64)SQLITE_FkNoAction);
db->aDb[0].pSchema->schema_cookie -= 32;
}
sqlite3_mutex_leave(sqlite3_db_mutex(db));
return rc;
}
/*
** This function is called by all six sqlite3changeset_apply() variants:
**
| > > > > > > | 235902 235903 235904 235905 235906 235907 235908 235909 235910 235911 235912 235913 235914 235915 235916 235917 235918 235919 235920 235921 235922 235923 235924 235925 235926 235927 235928 235929 235930 235931 235932 235933 235934 235935 235936 235937 235938 |
sqlite3_exec(db, "ROLLBACK TO changeset_apply", 0, 0, 0);
sqlite3_exec(db, "RELEASE changeset_apply", 0, 0, 0);
}
}
assert( sApply.bRebase || sApply.rebase.nBuf==0 );
if( rc==SQLITE_OK && bPatchset==0 && sApply.bRebase ){
assert( ppRebase!=0 && pnRebase!=0 );
*ppRebase = (void*)sApply.rebase.aBuf;
*pnRebase = sApply.rebase.nBuf;
sApply.rebase.aBuf = 0;
}
sessionUpdateFree(&sApply);
sqlite3_finalize(sApply.pInsert);
sqlite3_finalize(sApply.pDelete);
sqlite3_finalize(sApply.pSelect);
sqlite3_free((char*)sApply.azCol); /* cast works around VC++ bug */
sqlite3_free((char*)sApply.constraints.aBuf);
sqlite3_free((char*)sApply.rebase.aBuf);
if( (flags & SQLITE_CHANGESETAPPLY_FKNOACTION) && savedFlag==0 ){
assert( db->flags & SQLITE_FkNoAction );
db->flags &= ~((u64)SQLITE_FkNoAction);
db->aDb[0].pSchema->schema_cookie -= 32;
}
assert( rc!=SQLITE_OK || sApply.zErr==0 );
sqlite3_set_errmsg(db, rc, sApply.zErr);
sqlite3_free(sApply.zErr);
sqlite3_mutex_leave(sqlite3_db_mutex(db));
return rc;
}
/*
** This function is called by all six sqlite3changeset_apply() variants:
**
|
| ︙ | ︙ | |||
236346 236347 236348 236349 236350 236351 236352 | /* ** Constants for the largest and smallest possible 64-bit signed integers. */ # define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) # define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) | | > > > > > < < < < < < < < < | < < < | 238134 238135 238136 238137 238138 238139 238140 238141 238142 238143 238144 238145 238146 238147 238148 238149 238150 238151 238152 238153 238154 238155 | /* ** Constants for the largest and smallest possible 64-bit signed integers. */ # define LARGEST_INT64 (0xffffffff|(((i64)0x7fffffff)<<32)) # define SMALLEST_INT64 (((i64)-1) - LARGEST_INT64) /* ** This macro is used in a single assert() within fts5 to check that an ** allocation is aligned to an 8-byte boundary. But it is a complicated ** macro to get right for multiple platforms without generating warnings. ** So instead of reproducing the entire definition from sqliteInt.h, we ** just do without this assert() for the rare non-amalgamation builds. */ #define EIGHT_BYTE_ALIGNMENT(x) 1 /* ** Macros needed to provide flexible arrays in a portable way */ #ifndef offsetof # define offsetof(ST,M) ((size_t)((char*)&((ST*)0)->M - (char*)0)) #endif |
| ︙ | ︙ | |||
237108 237109 237110 237111 237112 237113 237114 |
** rc==SQLITE_OK && 0==sqlite3Fts5ExprEof(pExpr);
** rc = sqlite3Fts5ExprNext(pExpr)
** ){
** // The document with rowid iRowid matches the expression!
** i64 iRowid = sqlite3Fts5ExprRowid(pExpr);
** }
*/
| | | 238889 238890 238891 238892 238893 238894 238895 238896 238897 238898 238899 238900 238901 238902 238903 |
** rc==SQLITE_OK && 0==sqlite3Fts5ExprEof(pExpr);
** rc = sqlite3Fts5ExprNext(pExpr)
** ){
** // The document with rowid iRowid matches the expression!
** i64 iRowid = sqlite3Fts5ExprRowid(pExpr);
** }
*/
static int sqlite3Fts5ExprFirst(Fts5Expr*, Fts5Index *pIdx, i64 iMin, i64, int bDesc);
static int sqlite3Fts5ExprNext(Fts5Expr*, i64 iMax);
static int sqlite3Fts5ExprEof(Fts5Expr*);
static i64 sqlite3Fts5ExprRowid(Fts5Expr*);
static void sqlite3Fts5ExprFree(Fts5Expr*);
static int sqlite3Fts5ExprAnd(Fts5Expr **pp1, Fts5Expr *p2);
|
| ︙ | ︙ | |||
242677 242678 242679 242680 242681 242682 242683 | ** to parameter iFirst. Or, if iterating in ascending order (bDesc==1), ** then the first document visited must have a rowid smaller than or ** equal to iFirst. ** ** Return SQLITE_OK if successful, or an SQLite error code otherwise. It ** is not considered an error if the query does not match any documents. */ | | > > > > > > > > > | 244458 244459 244460 244461 244462 244463 244464 244465 244466 244467 244468 244469 244470 244471 244472 244473 244474 244475 244476 244477 244478 244479 244480 244481 244482 244483 244484 244485 244486 244487 244488 244489 244490 244491 244492 244493 244494 244495 244496 244497 244498 244499 244500 244501 |
** to parameter iFirst. Or, if iterating in ascending order (bDesc==1),
** then the first document visited must have a rowid smaller than or
** equal to iFirst.
**
** Return SQLITE_OK if successful, or an SQLite error code otherwise. It
** is not considered an error if the query does not match any documents.
*/
static int sqlite3Fts5ExprFirst(
Fts5Expr *p,
Fts5Index *pIdx,
i64 iFirst,
i64 iLast,
int bDesc
){
Fts5ExprNode *pRoot = p->pRoot;
int rc; /* Return code */
p->pIndex = pIdx;
p->bDesc = bDesc;
rc = fts5ExprNodeFirst(p, pRoot);
/* If not at EOF but the current rowid occurs earlier than iFirst in
** the iteration order, move to document iFirst or later. */
if( rc==SQLITE_OK
&& 0==pRoot->bEof
&& fts5RowidCmp(p, pRoot->iRowid, iFirst)<0
){
rc = fts5ExprNodeNext(p, pRoot, 1, iFirst);
}
/* If the iterator is not at a real match, skip forward until it is. */
while( pRoot->bNomatch && rc==SQLITE_OK ){
assert( pRoot->bEof==0 );
rc = fts5ExprNodeNext(p, pRoot, 0, 0);
}
if( fts5RowidCmp(p, pRoot->iRowid, iLast)>0 ){
pRoot->bEof = 1;
}
return rc;
}
/*
** Move to the next document
**
|
| ︙ | ︙ | |||
245874 245875 245876 245877 245878 245879 245880 |
** All the reasons those functions might return SQLITE_ERROR - missing
** table, missing row, non-blob/text in block column - indicate
** backing store corruption. */
if( rc==SQLITE_ERROR ) rc = FTS5_CORRUPT_ROWID(p, iRowid);
if( rc==SQLITE_OK ){
u8 *aOut = 0; /* Read blob data into this buffer */
| | | | | 247664 247665 247666 247667 247668 247669 247670 247671 247672 247673 247674 247675 247676 247677 247678 247679 247680 |
** All the reasons those functions might return SQLITE_ERROR - missing
** table, missing row, non-blob/text in block column - indicate
** backing store corruption. */
if( rc==SQLITE_ERROR ) rc = FTS5_CORRUPT_ROWID(p, iRowid);
if( rc==SQLITE_OK ){
u8 *aOut = 0; /* Read blob data into this buffer */
i64 nByte = sqlite3_blob_bytes(p->pReader);
i64 szData = (sizeof(Fts5Data) + 7) & ~7;
i64 nAlloc = szData + nByte + FTS5_DATA_PADDING;
pRet = (Fts5Data*)sqlite3_malloc64(nAlloc);
if( pRet ){
pRet->nn = nByte;
aOut = pRet->p = (u8*)pRet + szData;
}else{
rc = SQLITE_NOMEM;
}
|
| ︙ | ︙ | |||
251819 251820 251821 251822 251823 251824 251825 |
/*
** The %_data table is completely empty when this function is called. This
** function populates it with the initial structure objects for each index,
** and the initial version of the "averages" record (a zero-byte blob).
*/
static int sqlite3Fts5IndexReinit(Fts5Index *p){
Fts5Structure *pTmp;
| > > | > | | | 253609 253610 253611 253612 253613 253614 253615 253616 253617 253618 253619 253620 253621 253622 253623 253624 253625 253626 253627 253628 253629 253630 |
/*
** The %_data table is completely empty when this function is called. This
** function populates it with the initial structure objects for each index,
** and the initial version of the "averages" record (a zero-byte blob).
*/
static int sqlite3Fts5IndexReinit(Fts5Index *p){
Fts5Structure *pTmp;
union {
Fts5Structure sFts;
u8 tmpSpace[SZ_FTS5STRUCTURE(1)];
} uFts;
fts5StructureInvalidate(p);
fts5IndexDiscardData(p);
pTmp = &uFts.sFts;
memset(uFts.tmpSpace, 0, sizeof(uFts.tmpSpace));
if( p->pConfig->bContentlessDelete ){
pTmp->nOriginCntr = 1;
}
fts5DataWrite(p, FTS5_AVERAGES_ROWID, (const u8*)"", 0);
fts5StructureWrite(p, pTmp);
return fts5IndexReturn(p);
}
|
| ︙ | ︙ | |||
255043 255044 255045 255046 255047 255048 255049 255050 255051 255052 255053 255054 255055 255056 |
if( sqlite3_libversion_number()>=3008012 )
#endif
{
pIdxInfo->idxFlags |= SQLITE_INDEX_SCAN_UNIQUE;
}
#endif
}
static int fts5UsePatternMatch(
Fts5Config *pConfig,
struct sqlite3_index_constraint *p
){
assert( FTS5_PATTERN_GLOB==SQLITE_INDEX_CONSTRAINT_GLOB );
assert( FTS5_PATTERN_LIKE==SQLITE_INDEX_CONSTRAINT_LIKE );
| > > > > > > > > > > > | 256836 256837 256838 256839 256840 256841 256842 256843 256844 256845 256846 256847 256848 256849 256850 256851 256852 256853 256854 256855 256856 256857 256858 256859 256860 |
if( sqlite3_libversion_number()>=3008012 )
#endif
{
pIdxInfo->idxFlags |= SQLITE_INDEX_SCAN_UNIQUE;
}
#endif
}
static void fts5SetEstimatedRows(sqlite3_index_info *pIdxInfo, i64 nRow){
#if SQLITE_VERSION_NUMBER>=3008002
#ifndef SQLITE_CORE
if( sqlite3_libversion_number()>=3008002 )
#endif
{
pIdxInfo->estimatedRows = nRow;
}
#endif
}
static int fts5UsePatternMatch(
Fts5Config *pConfig,
struct sqlite3_index_constraint *p
){
assert( FTS5_PATTERN_GLOB==SQLITE_INDEX_CONSTRAINT_GLOB );
assert( FTS5_PATTERN_LIKE==SQLITE_INDEX_CONSTRAINT_LIKE );
|
| ︙ | ︙ | |||
255179 255180 255181 255182 255183 255184 255185 |
if( bSeenRank ) continue;
idxStr[iIdxStr++] = 'r';
bSeenRank = 1;
}else{
nSeenMatch++;
idxStr[iIdxStr++] = 'M';
sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol);
| | > | 256983 256984 256985 256986 256987 256988 256989 256990 256991 256992 256993 256994 256995 256996 256997 256998 256999 257000 257001 257002 257003 257004 257005 257006 257007 257008 257009 257010 257011 257012 257013 257014 257015 257016 |
if( bSeenRank ) continue;
idxStr[iIdxStr++] = 'r';
bSeenRank = 1;
}else{
nSeenMatch++;
idxStr[iIdxStr++] = 'M';
sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol);
iIdxStr += (int)strlen(&idxStr[iIdxStr]);
assert( idxStr[iIdxStr]=='\0' );
}
pInfo->aConstraintUsage[i].argvIndex = ++iCons;
pInfo->aConstraintUsage[i].omit = 1;
}
}else if( p->usable ){
if( iCol>=0 && iCol<nCol && fts5UsePatternMatch(pConfig, p) ){
assert( p->op==FTS5_PATTERN_LIKE || p->op==FTS5_PATTERN_GLOB );
idxStr[iIdxStr++] = p->op==FTS5_PATTERN_LIKE ? 'L' : 'G';
sqlite3_snprintf(6, &idxStr[iIdxStr], "%d", iCol);
idxStr += strlen(&idxStr[iIdxStr]);
pInfo->aConstraintUsage[i].argvIndex = ++iCons;
assert( idxStr[iIdxStr]=='\0' );
nSeenMatch++;
}else if( bSeenEq==0 && p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol<0 ){
idxStr[iIdxStr++] = '=';
bSeenEq = 1;
pInfo->aConstraintUsage[i].argvIndex = ++iCons;
pInfo->aConstraintUsage[i].omit = 1;
}
}
}
if( bSeenEq==0 ){
for(i=0; i<pInfo->nConstraint; i++){
struct sqlite3_index_constraint *p = &pInfo->aConstraint[i];
|
| ︙ | ︙ | |||
255245 255246 255247 255248 255249 255250 255251 |
idxFlags |= FTS5_BI_ORDER_DESC;
}
}
}
/* Calculate the estimated cost based on the flags set in idxFlags. */
if( bSeenEq ){
| | | > > | | | | | | | | | > > | 257050 257051 257052 257053 257054 257055 257056 257057 257058 257059 257060 257061 257062 257063 257064 257065 257066 257067 257068 257069 257070 257071 257072 257073 257074 257075 257076 257077 257078 |
idxFlags |= FTS5_BI_ORDER_DESC;
}
}
}
/* Calculate the estimated cost based on the flags set in idxFlags. */
if( bSeenEq ){
pInfo->estimatedCost = nSeenMatch ? 1000.0 : 25.0;
fts5SetUniqueFlag(pInfo);
fts5SetEstimatedRows(pInfo, 1);
}else{
if( bSeenLt && bSeenGt ){
pInfo->estimatedCost = nSeenMatch ? 5000.0 : 750000.0;
}else if( bSeenLt || bSeenGt ){
pInfo->estimatedCost = nSeenMatch ? 7500.0 : 2250000.0;
}else{
pInfo->estimatedCost = nSeenMatch ? 10000.0 : 3000000.0;
}
for(i=1; i<nSeenMatch; i++){
pInfo->estimatedCost *= 0.4;
}
fts5SetEstimatedRows(pInfo, (i64)(pInfo->estimatedCost / 4.0));
}
pInfo->idxNum = idxFlags;
return SQLITE_OK;
}
static int fts5NewTransaction(Fts5FullTable *pTab){
|
| ︙ | ︙ | |||
255454 255455 255456 255457 255458 255459 255460 |
int rc = SQLITE_OK;
assert( *pbSkip==0 );
if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_RESEEK) ){
Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
int bDesc = pCsr->bDesc;
i64 iRowid = sqlite3Fts5ExprRowid(pCsr->pExpr);
| | > > | 257263 257264 257265 257266 257267 257268 257269 257270 257271 257272 257273 257274 257275 257276 257277 257278 257279 |
int rc = SQLITE_OK;
assert( *pbSkip==0 );
if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_RESEEK) ){
Fts5FullTable *pTab = (Fts5FullTable*)(pCsr->base.pVtab);
int bDesc = pCsr->bDesc;
i64 iRowid = sqlite3Fts5ExprRowid(pCsr->pExpr);
rc = sqlite3Fts5ExprFirst(
pCsr->pExpr, pTab->p.pIndex, iRowid, pCsr->iLastRowid, bDesc
);
if( rc==SQLITE_OK && iRowid!=sqlite3Fts5ExprRowid(pCsr->pExpr) ){
*pbSkip = 1;
}
CsrFlagClear(pCsr, FTS5CSR_REQUIRE_RESEEK);
fts5CsrNewrow(pCsr);
if( sqlite3Fts5ExprEof(pCsr->pExpr) ){
|
| ︙ | ︙ | |||
255626 255627 255628 255629 255630 255631 255632 |
return rc;
}
static int fts5CursorFirst(Fts5FullTable *pTab, Fts5Cursor *pCsr, int bDesc){
int rc;
Fts5Expr *pExpr = pCsr->pExpr;
| | > > | 257437 257438 257439 257440 257441 257442 257443 257444 257445 257446 257447 257448 257449 257450 257451 257452 257453 |
return rc;
}
static int fts5CursorFirst(Fts5FullTable *pTab, Fts5Cursor *pCsr, int bDesc){
int rc;
Fts5Expr *pExpr = pCsr->pExpr;
rc = sqlite3Fts5ExprFirst(
pExpr, pTab->p.pIndex, pCsr->iFirstRowid, pCsr->iLastRowid, bDesc
);
if( sqlite3Fts5ExprEof(pExpr) ){
CsrFlagSet(pCsr, FTS5CSR_EOF);
}
fts5CsrNewrow(pCsr);
return rc;
}
|
| ︙ | ︙ | |||
258111 258112 258113 258114 258115 258116 258117 |
static void fts5SourceIdFunc(
sqlite3_context *pCtx, /* Function call context */
int nArg, /* Number of args */
sqlite3_value **apUnused /* Function arguments */
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
| | | 259924 259925 259926 259927 259928 259929 259930 259931 259932 259933 259934 259935 259936 259937 259938 |
static void fts5SourceIdFunc(
sqlite3_context *pCtx, /* Function call context */
int nArg, /* Number of args */
sqlite3_value **apUnused /* Function arguments */
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
sqlite3_result_text(pCtx, "fts5: 2025-10-15 10:52:45 5cbccab499bc3983aac1f57355552db607dee6c7ef4eb00d794dbee89c18db70", -1, SQLITE_TRANSIENT);
}
/*
** Implementation of fts5_locale(LOCALE, TEXT) function.
**
** If parameter LOCALE is NULL, or a zero-length string, then a copy of
** TEXT is returned. Otherwise, both LOCALE and TEXT are interpreted as
|
| ︙ | ︙ | |||
258134 258135 258136 258137 258138 258139 258140 |
*/
static void fts5LocaleFunc(
sqlite3_context *pCtx, /* Function call context */
int nArg, /* Number of args */
sqlite3_value **apArg /* Function arguments */
){
const char *zLocale = 0;
| | | | | | 259947 259948 259949 259950 259951 259952 259953 259954 259955 259956 259957 259958 259959 259960 259961 259962 259963 259964 259965 259966 259967 259968 259969 259970 259971 259972 259973 259974 259975 259976 259977 259978 259979 259980 259981 259982 259983 |
*/
static void fts5LocaleFunc(
sqlite3_context *pCtx, /* Function call context */
int nArg, /* Number of args */
sqlite3_value **apArg /* Function arguments */
){
const char *zLocale = 0;
i64 nLocale = 0;
const char *zText = 0;
i64 nText = 0;
assert( nArg==2 );
UNUSED_PARAM(nArg);
zLocale = (const char*)sqlite3_value_text(apArg[0]);
nLocale = sqlite3_value_bytes(apArg[0]);
zText = (const char*)sqlite3_value_text(apArg[1]);
nText = sqlite3_value_bytes(apArg[1]);
if( zLocale==0 || zLocale[0]=='\0' ){
sqlite3_result_text(pCtx, zText, nText, SQLITE_TRANSIENT);
}else{
Fts5Global *p = (Fts5Global*)sqlite3_user_data(pCtx);
u8 *pBlob = 0;
u8 *pCsr = 0;
i64 nBlob = 0;
nBlob = FTS5_LOCALE_HDR_SIZE + nLocale + 1 + nText;
pBlob = (u8*)sqlite3_malloc64(nBlob);
if( pBlob==0 ){
sqlite3_result_error_nomem(pCtx);
return;
}
pCsr = pBlob;
memcpy(pCsr, (const u8*)p->aLocaleHdr, FTS5_LOCALE_HDR_SIZE);
|
| ︙ | ︙ |
Changes to extsrc/sqlite3.h.
| ︙ | ︙ | |||
144 145 146 147 148 149 150 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.51.0" #define SQLITE_VERSION_NUMBER 3051000 | | > > > | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 | ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.51.0" #define SQLITE_VERSION_NUMBER 3051000 #define SQLITE_SOURCE_ID "2025-10-15 10:52:45 5cbccab499bc3983aac1f57355552db607dee6c7ef4eb00d794dbee89c18db70" #define SQLITE_SCM_BRANCH "trunk" #define SQLITE_SCM_TAGS "" #define SQLITE_SCM_DATETIME "2025-10-15T10:52:45.276Z" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros |
| ︙ | ︙ | |||
493 494 495 496 497 498 499 500 501 502 503 504 505 506 | ** [sqlite3_extended_result_codes()] API. Or, the extended code for ** the most recent error can be obtained using ** [sqlite3_extended_errcode()]. */ #define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8)) #define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8)) #define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3<<8)) #define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8)) #define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8)) #define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8)) #define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8)) #define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8)) #define SQLITE_IOERR_TRUNCATE (SQLITE_IOERR | (6<<8)) #define SQLITE_IOERR_FSTAT (SQLITE_IOERR | (7<<8)) | > > > | 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 | ** [sqlite3_extended_result_codes()] API. Or, the extended code for ** the most recent error can be obtained using ** [sqlite3_extended_errcode()]. */ #define SQLITE_ERROR_MISSING_COLLSEQ (SQLITE_ERROR | (1<<8)) #define SQLITE_ERROR_RETRY (SQLITE_ERROR | (2<<8)) #define SQLITE_ERROR_SNAPSHOT (SQLITE_ERROR | (3<<8)) #define SQLITE_ERROR_RESERVESIZE (SQLITE_ERROR | (4<<8)) #define SQLITE_ERROR_KEY (SQLITE_ERROR | (5<<8)) #define SQLITE_ERROR_UNABLE (SQLITE_ERROR | (6<<8)) #define SQLITE_IOERR_READ (SQLITE_IOERR | (1<<8)) #define SQLITE_IOERR_SHORT_READ (SQLITE_IOERR | (2<<8)) #define SQLITE_IOERR_WRITE (SQLITE_IOERR | (3<<8)) #define SQLITE_IOERR_FSYNC (SQLITE_IOERR | (4<<8)) #define SQLITE_IOERR_DIR_FSYNC (SQLITE_IOERR | (5<<8)) #define SQLITE_IOERR_TRUNCATE (SQLITE_IOERR | (6<<8)) #define SQLITE_IOERR_FSTAT (SQLITE_IOERR | (7<<8)) |
| ︙ | ︙ | |||
527 528 529 530 531 532 533 534 535 536 537 538 539 540 | #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8)) #define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8)) #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) #define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) | > > | 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 | #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8)) #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8)) #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8)) #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8)) #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8)) #define SQLITE_IOERR_CORRUPTFS (SQLITE_IOERR | (33<<8)) #define SQLITE_IOERR_IN_PAGE (SQLITE_IOERR | (34<<8)) #define SQLITE_IOERR_BADKEY (SQLITE_IOERR | (35<<8)) #define SQLITE_IOERR_CODEC (SQLITE_IOERR | (36<<8)) #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8)) #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8)) #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8)) #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8)) #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8)) #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8)) #define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8)) |
| ︙ | ︙ | |||
2331 2332 2333 2334 2335 2336 2337 | ** SQLite version 3.35.0, TEMP views are still allowed even if ** this option is off. So, in other words, this option now only disables ** views in the main database schema or in the schemas of ATTACH-ed ** databases.)^ </dd> ** ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]] ** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt> | | | | | | > | < > | | > | | > | 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 | ** SQLite version 3.35.0, TEMP views are still allowed even if ** this option is off. So, in other words, this option now only disables ** views in the main database schema or in the schemas of ATTACH-ed ** databases.)^ </dd> ** ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]] ** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt> ** <dd> ^This option is used to enable or disable using the ** [fts3_tokenizer()] function - part of the [FTS3] full-text search engine ** extension - without using bound parameters as the parameters. Doing so ** is disabled by default. There must be two additional arguments. The first ** argument is an integer. If it is passed 0, then using fts3_tokenizer() ** without bound parameters is disabled. If it is passed a positive value, ** then calling fts3_tokenizer without bound parameters is enabled. If it ** is passed a negative value, this setting is not modified - this can be ** used to query for the current setting. The second parameter is a pointer ** to an integer into which is written 0 or 1 to indicate the current value ** of this setting (after it is modified, if applicable). The second ** parameter may be a NULL pointer, in which case the value of the setting ** is not reported back. Refer to [FTS3] documentation for further details. ** </dd> ** ** [[SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION]] ** <dt>SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION</dt> ** <dd> ^This option is used to enable or disable the [sqlite3_load_extension()] ** interface independently of the [load_extension()] SQL function. ** The [sqlite3_enable_load_extension()] API enables or disables both the ** C-API [sqlite3_load_extension()] and the SQL function [load_extension()]. |
| ︙ | ︙ | |||
4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 |
SQLITE_API int sqlite3_errcode(sqlite3 *db);
SQLITE_API int sqlite3_extended_errcode(sqlite3 *db);
SQLITE_API const char *sqlite3_errmsg(sqlite3*);
SQLITE_API const void *sqlite3_errmsg16(sqlite3*);
SQLITE_API const char *sqlite3_errstr(int);
SQLITE_API int sqlite3_error_offset(sqlite3 *db);
/*
** CAPI3REF: Prepared Statement Object
** KEYWORDS: {prepared statement} {prepared statements}
**
** An instance of this object represents a single SQL statement that
** has been compiled into binary form and is ready to be evaluated.
**
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 4230 4231 4232 4233 4234 4235 4236 4237 4238 4239 4240 4241 4242 4243 |
SQLITE_API int sqlite3_errcode(sqlite3 *db);
SQLITE_API int sqlite3_extended_errcode(sqlite3 *db);
SQLITE_API const char *sqlite3_errmsg(sqlite3*);
SQLITE_API const void *sqlite3_errmsg16(sqlite3*);
SQLITE_API const char *sqlite3_errstr(int);
SQLITE_API int sqlite3_error_offset(sqlite3 *db);
/*
** CAPI3REF: Set Error Codes And Message
** METHOD: sqlite3
**
** Set the error code of the database handle passed as the first argument
** to errcode, and the error message to a copy of nul-terminated string
** zErrMsg. If zErrMsg is passed NULL, then the error message is set to
** the default message associated with the supplied error code. Subsequent
** calls to [sqlite3_errcode()] and [sqlite3_errmsg()] and similar will
** return the values set by this routine in place of what was previously
** set by SQLite itself.
**
** This function returns SQLITE_OK if the error code and error message are
** successfully set, SQLITE_NOMEM if an OOM occurs, and SQLITE_MISUSE if
** the database handle is NULL or invalid.
**
** The error code and message set by this routine remains in effect until
** they are changed, either by another call to this routine or until they are
** changed to by SQLite itself to reflect the result of some subsquent
** API call.
**
** This function is intended for use by SQLite extensions or wrappers. The
** idea is that an extension or wrapper can use this routine to set error
** messages and error codes and thus behave more like a core SQLite
** feature from the point of view of an application.
*/
SQLITE_API int sqlite3_set_errmsg(sqlite3 *db, int errcode, const char *zErrMsg);
/*
** CAPI3REF: Prepared Statement Object
** KEYWORDS: {prepared statement} {prepared statements}
**
** An instance of this object represents a single SQL statement that
** has been compiled into binary form and is ready to be evaluated.
**
|
| ︙ | ︙ | |||
6201 6202 6203 6204 6205 6206 6207 6208 6209 6210 6211 6212 6213 6214 | ** with a [database connection]. ** A call to sqlite3_set_clientdata(D,N,P,X) causes the pointer P ** to be attached to [database connection] D using name N. Subsequent ** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P ** or a NULL pointer if there were no prior calls to ** sqlite3_set_clientdata() with the same values of D and N. ** Names are compared using strcmp() and are thus case sensitive. ** ** If P and X are both non-NULL, then the destructor X is invoked with ** argument P on the first of the following occurrences: ** <ul> ** <li> An out-of-memory error occurs during the call to ** sqlite3_set_clientdata() which attempts to register pointer P. ** <li> A subsequent call to sqlite3_set_clientdata(D,N,P,X) is made | > | 6240 6241 6242 6243 6244 6245 6246 6247 6248 6249 6250 6251 6252 6253 6254 | ** with a [database connection]. ** A call to sqlite3_set_clientdata(D,N,P,X) causes the pointer P ** to be attached to [database connection] D using name N. Subsequent ** calls to sqlite3_get_clientdata(D,N) will return a copy of pointer P ** or a NULL pointer if there were no prior calls to ** sqlite3_set_clientdata() with the same values of D and N. ** Names are compared using strcmp() and are thus case sensitive. ** It returns 0 on success and SQLITE_NOMEM on allocation failure. ** ** If P and X are both non-NULL, then the destructor X is invoked with ** argument P on the first of the following occurrences: ** <ul> ** <li> An out-of-memory error occurs during the call to ** sqlite3_set_clientdata() which attempts to register pointer P. ** <li> A subsequent call to sqlite3_set_clientdata(D,N,P,X) is made |
| ︙ | ︙ | |||
8876 8877 8878 8879 8880 8881 8882 8883 8884 8885 8886 8887 8888 8889 8890 8891 8892 8893 |
** ^The current value of the requested parameter is written into *pCur
** and the highest instantaneous value is written into *pHiwtr. ^If
** the resetFlg is true, then the highest instantaneous value is
** reset back down to the current value.
**
** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a
** non-zero [error code] on failure.
**
** See also: [sqlite3_status()] and [sqlite3_stmt_status()].
*/
SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
/*
** CAPI3REF: Status Parameters for database connections
** KEYWORDS: {SQLITE_DBSTATUS options}
**
** These constants are the available integer "verbs" that can be passed as
** the second argument to the [sqlite3_db_status()] interface.
| > > > > > > > > > | 8916 8917 8918 8919 8920 8921 8922 8923 8924 8925 8926 8927 8928 8929 8930 8931 8932 8933 8934 8935 8936 8937 8938 8939 8940 8941 8942 |
** ^The current value of the requested parameter is written into *pCur
** and the highest instantaneous value is written into *pHiwtr. ^If
** the resetFlg is true, then the highest instantaneous value is
** reset back down to the current value.
**
** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a
** non-zero [error code] on failure.
**
** ^The sqlite3_db_status64(D,O,C,H,R) routine works exactly the same
** way as sqlite3_db_status(D,O,C,H,R) routine except that the C and H
** parameters are pointer to 64-bit integers (type: sqlite3_int64) instead
** of pointers to 32-bit integers, which allows larger status values
** to be returned. If a status value exceeds 2,147,483,647 then
** sqlite3_db_status() will truncate the value whereas sqlite3_db_status64()
** will return the full value.
**
** See also: [sqlite3_status()] and [sqlite3_stmt_status()].
*/
SQLITE_API int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg);
SQLITE_API int sqlite3_db_status64(sqlite3*,int,sqlite3_int64*,sqlite3_int64*,int);
/*
** CAPI3REF: Status Parameters for database connections
** KEYWORDS: {SQLITE_DBSTATUS options}
**
** These constants are the available integer "verbs" that can be passed as
** the second argument to the [sqlite3_db_status()] interface.
|
| ︙ | ︙ | |||
8976 8977 8978 8979 8980 8981 8982 8983 8984 8985 8986 8987 8988 8989 8990 8991 8992 8993 8994 8995 8996 8997 8998 8999 9000 9001 9002 9003 9004 9005 9006 9007 9008 9009 9010 9011 9012 9013 | ** been written to disk. Specifically, the number of pages written to the ** wal file in wal mode databases, or the number of pages written to the ** database file in rollback mode databases. Any pages written as part of ** transaction rollback or database recovery operations are not included. ** If an IO or other error occurs while writing a page to disk, the effect ** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The ** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0. ** </dd> ** ** [[SQLITE_DBSTATUS_CACHE_SPILL]] ^(<dt>SQLITE_DBSTATUS_CACHE_SPILL</dt> ** <dd>This parameter returns the number of dirty cache entries that have ** been written to disk in the middle of a transaction due to the page ** cache overflowing. Transactions are more efficient if they are written ** to disk all at once. When pages spill mid-transaction, that introduces ** additional overhead. This parameter can be used to help identify ** inefficiencies that can be resolved by increasing the cache size. ** </dd> ** ** [[SQLITE_DBSTATUS_DEFERRED_FKS]] ^(<dt>SQLITE_DBSTATUS_DEFERRED_FKS</dt> ** <dd>This parameter returns zero for the current value if and only if ** all foreign key constraints (deferred or immediate) have been ** resolved.)^ ^The highwater mark is always 0. ** </dd> ** </dl> */ #define SQLITE_DBSTATUS_LOOKASIDE_USED 0 #define SQLITE_DBSTATUS_CACHE_USED 1 #define SQLITE_DBSTATUS_SCHEMA_USED 2 #define SQLITE_DBSTATUS_STMT_USED 3 #define SQLITE_DBSTATUS_LOOKASIDE_HIT 4 #define SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE 5 #define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL 6 #define SQLITE_DBSTATUS_CACHE_HIT 7 #define SQLITE_DBSTATUS_CACHE_MISS 8 #define SQLITE_DBSTATUS_CACHE_WRITE 9 #define SQLITE_DBSTATUS_DEFERRED_FKS 10 #define SQLITE_DBSTATUS_CACHE_USED_SHARED 11 #define SQLITE_DBSTATUS_CACHE_SPILL 12 | > > > > > > > > > > > > > > > > > | | 9025 9026 9027 9028 9029 9030 9031 9032 9033 9034 9035 9036 9037 9038 9039 9040 9041 9042 9043 9044 9045 9046 9047 9048 9049 9050 9051 9052 9053 9054 9055 9056 9057 9058 9059 9060 9061 9062 9063 9064 9065 9066 9067 9068 9069 9070 9071 9072 9073 9074 9075 9076 9077 9078 9079 9080 9081 9082 9083 9084 9085 9086 9087 | ** been written to disk. Specifically, the number of pages written to the ** wal file in wal mode databases, or the number of pages written to the ** database file in rollback mode databases. Any pages written as part of ** transaction rollback or database recovery operations are not included. ** If an IO or other error occurs while writing a page to disk, the effect ** on subsequent SQLITE_DBSTATUS_CACHE_WRITE requests is undefined.)^ ^The ** highwater mark associated with SQLITE_DBSTATUS_CACHE_WRITE is always 0. ** <p> ** ^(There is overlap between the quantities measured by this parameter ** (SQLITE_DBSTATUS_CACHE_WRITE) and SQLITE_DBSTATUS_TEMPBUF_SPILL. ** Resetting one will reduce the other.)^ ** </dd> ** ** [[SQLITE_DBSTATUS_CACHE_SPILL]] ^(<dt>SQLITE_DBSTATUS_CACHE_SPILL</dt> ** <dd>This parameter returns the number of dirty cache entries that have ** been written to disk in the middle of a transaction due to the page ** cache overflowing. Transactions are more efficient if they are written ** to disk all at once. When pages spill mid-transaction, that introduces ** additional overhead. This parameter can be used to help identify ** inefficiencies that can be resolved by increasing the cache size. ** </dd> ** ** [[SQLITE_DBSTATUS_DEFERRED_FKS]] ^(<dt>SQLITE_DBSTATUS_DEFERRED_FKS</dt> ** <dd>This parameter returns zero for the current value if and only if ** all foreign key constraints (deferred or immediate) have been ** resolved.)^ ^The highwater mark is always 0. ** ** [[SQLITE_DBSTATUS_TEMPBUF_SPILL] ^(<dt>SQLITE_DBSTATUS_TEMPBUF_SPILL</dt> ** <dd>^(This parameter returns the number of bytes written to temporary ** files on disk that could have been kept in memory had sufficient memory ** been available. This value includes writes to intermediate tables that ** are part of complex queries, external sorts that spill to disk, and ** writes to TEMP tables.)^ ** ^The highwater mark is always 0. ** <p> ** ^(There is overlap between the quantities measured by this parameter ** (SQLITE_DBSTATUS_TEMPBUF_SPILL) and SQLITE_DBSTATUS_CACHE_WRITE. ** Resetting one will reduce the other.)^ ** </dd> ** </dl> */ #define SQLITE_DBSTATUS_LOOKASIDE_USED 0 #define SQLITE_DBSTATUS_CACHE_USED 1 #define SQLITE_DBSTATUS_SCHEMA_USED 2 #define SQLITE_DBSTATUS_STMT_USED 3 #define SQLITE_DBSTATUS_LOOKASIDE_HIT 4 #define SQLITE_DBSTATUS_LOOKASIDE_MISS_SIZE 5 #define SQLITE_DBSTATUS_LOOKASIDE_MISS_FULL 6 #define SQLITE_DBSTATUS_CACHE_HIT 7 #define SQLITE_DBSTATUS_CACHE_MISS 8 #define SQLITE_DBSTATUS_CACHE_WRITE 9 #define SQLITE_DBSTATUS_DEFERRED_FKS 10 #define SQLITE_DBSTATUS_CACHE_USED_SHARED 11 #define SQLITE_DBSTATUS_CACHE_SPILL 12 #define SQLITE_DBSTATUS_TEMPBUF_SPILL 13 #define SQLITE_DBSTATUS_MAX 13 /* Largest defined DBSTATUS */ /* ** CAPI3REF: Prepared Statement Status ** METHOD: sqlite3_stmt ** ** ^(Each prepared statement maintains various |
| ︙ | ︙ | |||
9772 9773 9774 9775 9776 9777 9778 | ** is a copy of the third parameter passed to sqlite3_wal_hook() when ** registering the callback. ^The second is a copy of the database handle. ** ^The third parameter is the name of the database that was written to - ** either "main" or the name of an [ATTACH]-ed database. ^The fourth parameter ** is the number of pages currently in the write-ahead log file, ** including those that were just committed. ** | | | | | > > > | > | | | > > > > > > > > > | | > | | | 9838 9839 9840 9841 9842 9843 9844 9845 9846 9847 9848 9849 9850 9851 9852 9853 9854 9855 9856 9857 9858 9859 9860 9861 9862 9863 9864 9865 9866 9867 9868 9869 9870 9871 9872 9873 9874 9875 9876 9877 9878 9879 9880 9881 9882 9883 9884 9885 9886 9887 9888 9889 9890 9891 9892 9893 9894 9895 9896 9897 9898 9899 9900 9901 9902 9903 9904 9905 9906 9907 9908 9909 9910 9911 9912 9913 9914 9915 | ** is a copy of the third parameter passed to sqlite3_wal_hook() when ** registering the callback. ^The second is a copy of the database handle. ** ^The third parameter is the name of the database that was written to - ** either "main" or the name of an [ATTACH]-ed database. ^The fourth parameter ** is the number of pages currently in the write-ahead log file, ** including those that were just committed. ** ** ^The callback function should normally return [SQLITE_OK]. ^If an error ** code is returned, that error will propagate back up through the ** SQLite code base to cause the statement that provoked the callback ** to report an error, though the commit will have still occurred. If the ** callback returns [SQLITE_ROW] or [SQLITE_DONE], or if it returns a value ** that does not correspond to any valid SQLite error code, the results ** are undefined. ** ** ^A single database handle may have at most a single write-ahead log ** callback registered at one time. ^Calling [sqlite3_wal_hook()] ** replaces the default behavior or previously registered write-ahead ** log callback. ** ** ^The return value is a copy of the third parameter from the ** previous call, if any, or 0. ** ** ^The [sqlite3_wal_autocheckpoint()] interface and the ** [wal_autocheckpoint pragma] both invoke [sqlite3_wal_hook()] and ** will overwrite any prior [sqlite3_wal_hook()] settings. ** ** ^If a write-ahead log callback is set using this function then ** [sqlite3_wal_checkpoint_v2()] or [PRAGMA wal_checkpoint] ** should be invoked periodically to keep the write-ahead log file ** from growing without bound. ** ** ^Passing a NULL pointer for the callback disables automatic ** checkpointing entirely. To re-enable the default behavior, call ** sqlite3_wal_autocheckpoint(db,1000) or use [PRAGMA wal_checkpoint]. */ SQLITE_API void *sqlite3_wal_hook( sqlite3*, int(*)(void *,sqlite3*,const char*,int), void* ); /* ** CAPI3REF: Configure an auto-checkpoint ** METHOD: sqlite3 ** ** ^The [sqlite3_wal_autocheckpoint(D,N)] is a wrapper around ** [sqlite3_wal_hook()] that causes any database on [database connection] D ** to automatically [checkpoint] ** after committing a transaction if there are N or ** more frames in the [write-ahead log] file. ^Passing zero or ** a negative value as the N parameter disables automatic ** checkpoints entirely. ** ** ^The callback registered by this function replaces any existing callback ** registered using [sqlite3_wal_hook()]. ^Likewise, registering a callback ** using [sqlite3_wal_hook()] disables the automatic checkpoint mechanism ** configured by this function. ** ** ^The [wal_autocheckpoint pragma] can be used to invoke this interface ** from SQL. ** ** ^Checkpoints initiated by this mechanism are ** [sqlite3_wal_checkpoint_v2|PASSIVE]. ** ** ^Every new [database connection] defaults to having the auto-checkpoint ** enabled with a threshold of 1000 or [SQLITE_DEFAULT_WAL_AUTOCHECKPOINT] ** pages. ** ** ^The use of this interface is only necessary if the default setting ** is found to be suboptimal for a particular application. */ SQLITE_API int sqlite3_wal_autocheckpoint(sqlite3 *db, int N); /* ** CAPI3REF: Checkpoint a database ** METHOD: sqlite3 ** |
| ︙ | ︙ | |||
9886 9887 9888 9889 9890 9891 9892 9893 9894 9895 9896 9897 9898 9899 | ** ^Like SQLITE_CHECKPOINT_FULL, this mode blocks new ** database writer attempts while it is pending, but does not impede readers. ** ** <dt>SQLITE_CHECKPOINT_TRUNCATE<dd> ** ^This mode works the same way as SQLITE_CHECKPOINT_RESTART with the ** addition that it also truncates the log file to zero bytes just prior ** to a successful return. ** </dl> ** ** ^If pnLog is not NULL, then *pnLog is set to the total number of frames in ** the log file or to -1 if the checkpoint could not run because ** of an error or because the database is not in [WAL mode]. ^If pnCkpt is not ** NULL,then *pnCkpt is set to the total number of checkpointed frames in the ** log file (including any that were already checkpointed before the function | > > > > > | 9966 9967 9968 9969 9970 9971 9972 9973 9974 9975 9976 9977 9978 9979 9980 9981 9982 9983 9984 | ** ^Like SQLITE_CHECKPOINT_FULL, this mode blocks new ** database writer attempts while it is pending, but does not impede readers. ** ** <dt>SQLITE_CHECKPOINT_TRUNCATE<dd> ** ^This mode works the same way as SQLITE_CHECKPOINT_RESTART with the ** addition that it also truncates the log file to zero bytes just prior ** to a successful return. ** ** <dt>SQLITE_CHECKPOINT_NOOP<dd> ** ^This mode always checkpoints zero frames. The only reason to invoke ** a NOOP checkpoint is to access the values returned by ** sqlite3_wal_checkpoint_v2() via output parameters *pnLog and *pnCkpt. ** </dl> ** ** ^If pnLog is not NULL, then *pnLog is set to the total number of frames in ** the log file or to -1 if the checkpoint could not run because ** of an error or because the database is not in [WAL mode]. ^If pnCkpt is not ** NULL,then *pnCkpt is set to the total number of checkpointed frames in the ** log file (including any that were already checkpointed before the function |
| ︙ | ︙ | |||
9956 9957 9958 9959 9960 9961 9962 9963 9964 9965 9966 9967 9968 9969 |
** KEYWORDS: {checkpoint mode}
**
** These constants define all valid values for the "checkpoint mode" passed
** as the third parameter to the [sqlite3_wal_checkpoint_v2()] interface.
** See the [sqlite3_wal_checkpoint_v2()] documentation for details on the
** meaning of each of these checkpoint modes.
*/
#define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */
#define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */
#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */
#define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */
/*
** CAPI3REF: Virtual Table Interface Configuration
| > | 10041 10042 10043 10044 10045 10046 10047 10048 10049 10050 10051 10052 10053 10054 10055 |
** KEYWORDS: {checkpoint mode}
**
** These constants define all valid values for the "checkpoint mode" passed
** as the third parameter to the [sqlite3_wal_checkpoint_v2()] interface.
** See the [sqlite3_wal_checkpoint_v2()] documentation for details on the
** meaning of each of these checkpoint modes.
*/
#define SQLITE_CHECKPOINT_NOOP -1 /* Do no work at all */
#define SQLITE_CHECKPOINT_PASSIVE 0 /* Do as much as possible w/o blocking */
#define SQLITE_CHECKPOINT_FULL 1 /* Wait for writers, then checkpoint */
#define SQLITE_CHECKPOINT_RESTART 2 /* Like FULL but wait for readers */
#define SQLITE_CHECKPOINT_TRUNCATE 3 /* Like RESTART but also truncate WAL */
/*
** CAPI3REF: Virtual Table Interface Configuration
|
| ︙ | ︙ | |||
10783 10784 10785 10786 10787 10788 10789 | ** The [sqlite3_snapshot] object returned from a successful call to ** [sqlite3_snapshot_get()] must be freed using [sqlite3_snapshot_free()] ** to avoid a memory leak. ** ** The [sqlite3_snapshot_get()] interface is only available when the ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used. */ | | | 10869 10870 10871 10872 10873 10874 10875 10876 10877 10878 10879 10880 10881 10882 10883 | ** The [sqlite3_snapshot] object returned from a successful call to ** [sqlite3_snapshot_get()] must be freed using [sqlite3_snapshot_free()] ** to avoid a memory leak. ** ** The [sqlite3_snapshot_get()] interface is only available when the ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used. */ SQLITE_API int sqlite3_snapshot_get( sqlite3 *db, const char *zSchema, sqlite3_snapshot **ppSnapshot ); /* ** CAPI3REF: Start a read transaction on an historical snapshot |
| ︙ | ︙ | |||
10832 10833 10834 10835 10836 10837 10838 | ** after the most recent I/O on the database connection.)^ ** (Hint: Run "[PRAGMA application_id]" against a newly opened ** database connection in order to make it ready to use snapshots.) ** ** The [sqlite3_snapshot_open()] interface is only available when the ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used. */ | | | | 10918 10919 10920 10921 10922 10923 10924 10925 10926 10927 10928 10929 10930 10931 10932 10933 10934 10935 10936 10937 10938 10939 10940 10941 10942 10943 10944 10945 10946 10947 10948 10949 | ** after the most recent I/O on the database connection.)^ ** (Hint: Run "[PRAGMA application_id]" against a newly opened ** database connection in order to make it ready to use snapshots.) ** ** The [sqlite3_snapshot_open()] interface is only available when the ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used. */ SQLITE_API int sqlite3_snapshot_open( sqlite3 *db, const char *zSchema, sqlite3_snapshot *pSnapshot ); /* ** CAPI3REF: Destroy a snapshot ** DESTRUCTOR: sqlite3_snapshot ** ** ^The [sqlite3_snapshot_free(P)] interface destroys [sqlite3_snapshot] P. ** The application must eventually free every [sqlite3_snapshot] object ** using this routine to avoid a memory leak. ** ** The [sqlite3_snapshot_free()] interface is only available when the ** [SQLITE_ENABLE_SNAPSHOT] compile-time option is used. */ SQLITE_API void sqlite3_snapshot_free(sqlite3_snapshot*); /* ** CAPI3REF: Compare the ages of two snapshot handles. ** METHOD: sqlite3_snapshot ** ** The sqlite3_snapshot_cmp(P1, P2) interface is used to compare the ages ** of two valid snapshot handles. |
| ︙ | ︙ | |||
10876 10877 10878 10879 10880 10881 10882 | ** Otherwise, this API returns a negative value if P1 refers to an older ** snapshot than P2, zero if the two handles refer to the same database ** snapshot, and a positive value if P1 is a newer snapshot than P2. ** ** This interface is only available if SQLite is compiled with the ** [SQLITE_ENABLE_SNAPSHOT] option. */ | | | 10962 10963 10964 10965 10966 10967 10968 10969 10970 10971 10972 10973 10974 10975 10976 | ** Otherwise, this API returns a negative value if P1 refers to an older ** snapshot than P2, zero if the two handles refer to the same database ** snapshot, and a positive value if P1 is a newer snapshot than P2. ** ** This interface is only available if SQLite is compiled with the ** [SQLITE_ENABLE_SNAPSHOT] option. */ SQLITE_API int sqlite3_snapshot_cmp( sqlite3_snapshot *p1, sqlite3_snapshot *p2 ); /* ** CAPI3REF: Recover snapshots from a wal file ** METHOD: sqlite3_snapshot |
| ︙ | ︙ | |||
10904 10905 10906 10907 10908 10909 10910 | ** database. ** ** SQLITE_OK is returned if successful, or an SQLite error code otherwise. ** ** This interface is only available if SQLite is compiled with the ** [SQLITE_ENABLE_SNAPSHOT] option. */ | | | 10990 10991 10992 10993 10994 10995 10996 10997 10998 10999 11000 11001 11002 11003 11004 | ** database. ** ** SQLITE_OK is returned if successful, or an SQLite error code otherwise. ** ** This interface is only available if SQLite is compiled with the ** [SQLITE_ENABLE_SNAPSHOT] option. */ SQLITE_API int sqlite3_snapshot_recover(sqlite3 *db, const char *zDb); /* ** CAPI3REF: Serialize a database ** ** The sqlite3_serialize(D,S,P,F) interface returns a pointer to ** memory that is a serialization of the S database on ** [database connection] D. If S is a NULL pointer, the main database is used. |
| ︙ | ︙ | |||
10978 10979 10980 10981 10982 10983 10984 | #define SQLITE_SERIALIZE_NOCOPY 0x001 /* Do no memory allocations */ /* ** CAPI3REF: Deserialize a database ** ** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the ** [database connection] D to disconnect from database S and then | | > | | | | | | 11064 11065 11066 11067 11068 11069 11070 11071 11072 11073 11074 11075 11076 11077 11078 11079 11080 11081 11082 11083 11084 | #define SQLITE_SERIALIZE_NOCOPY 0x001 /* Do no memory allocations */ /* ** CAPI3REF: Deserialize a database ** ** The sqlite3_deserialize(D,S,P,N,M,F) interface causes the ** [database connection] D to disconnect from database S and then ** reopen S as an in-memory database based on the serialization ** contained in P. If S is a NULL pointer, the main database is ** used. The serialized database P is N bytes in size. M is the size ** of the buffer P, which might be larger than N. If M is larger than ** N, and the SQLITE_DESERIALIZE_READONLY bit is not set in F, then ** SQLite is permitted to add content to the in-memory database as ** long as the total size does not exceed M bytes. ** ** If the SQLITE_DESERIALIZE_FREEONCLOSE bit is set in F, then SQLite will ** invoke sqlite3_free() on the serialization buffer when the database ** connection closes. If the SQLITE_DESERIALIZE_RESIZEABLE bit is set, then ** SQLite will try to increase the buffer size using sqlite3_realloc64() ** if writes on the database cause it to grow larger than M bytes. ** |
| ︙ | ︙ | |||
11050 11051 11052 11053 11054 11055 11056 11057 11058 11059 11060 11061 11062 11063 | ** The SQLITE_DESERIALIZE_READONLY flag means that the deserialized database ** should be treated as read-only. */ #define SQLITE_DESERIALIZE_FREEONCLOSE 1 /* Call sqlite3_free() on close */ #define SQLITE_DESERIALIZE_RESIZEABLE 2 /* Resize using sqlite3_realloc64() */ #define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */ /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ #ifdef SQLITE_OMIT_FLOATING_POINT # undef double #endif | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 11137 11138 11139 11140 11141 11142 11143 11144 11145 11146 11147 11148 11149 11150 11151 11152 11153 11154 11155 11156 11157 11158 11159 11160 11161 11162 11163 11164 11165 11166 11167 11168 11169 11170 11171 11172 11173 11174 11175 11176 11177 11178 11179 11180 11181 11182 11183 11184 11185 11186 11187 11188 11189 11190 11191 11192 11193 11194 11195 11196 | ** The SQLITE_DESERIALIZE_READONLY flag means that the deserialized database ** should be treated as read-only. */ #define SQLITE_DESERIALIZE_FREEONCLOSE 1 /* Call sqlite3_free() on close */ #define SQLITE_DESERIALIZE_RESIZEABLE 2 /* Resize using sqlite3_realloc64() */ #define SQLITE_DESERIALIZE_READONLY 4 /* Database is read-only */ /* ** CAPI3REF: Bind array values to the CARRAY table-valued function ** ** The sqlite3_carray_bind(S,I,P,N,F,X) interface binds an array value to ** one of the first argument of the [carray() table-valued function]. The ** S parameter is a pointer to the [prepared statement] that uses the carray() ** functions. I is the parameter index to be bound. P is a pointer to the ** array to be bound, and N is the number of eements in the array. The ** F argument is one of constants [SQLITE_CARRAY_INT32], [SQLITE_CARRAY_INT64], ** [SQLITE_CARRAY_DOUBLE], [SQLITE_CARRAY_TEXT], or [SQLITE_CARRAY_BLOB] to ** indicate the datatype of the array being bound. The X argument is not a ** NULL pointer, then SQLite will invoke the function X on the P parameter ** after it has finished using P. */ SQLITE_API SQLITE_API int sqlite3_carray_bind( sqlite3_stmt *pStmt, /* Statement to be bound */ int i, /* Parameter index */ void *aData, /* Pointer to array data */ int nData, /* Number of data elements */ int mFlags, /* CARRAY flags */ void (*xDel)(void*) /* Destructor for aData */ ); /* ** CAPI3REF: Datatypes for the CARRAY table-valued funtion ** ** The fifth argument to the [sqlite3_carray_bind()] interface musts be ** one of the following constants, to specify the datatype of the array ** that is being bound into the [carray table-valued function]. */ #define SQLITE_CARRAY_INT32 0 /* Data is 32-bit signed integers */ #define SQLITE_CARRAY_INT64 1 /* Data is 64-bit signed integers */ #define SQLITE_CARRAY_DOUBLE 2 /* Data is doubles */ #define SQLITE_CARRAY_TEXT 3 /* Data is char* */ #define SQLITE_CARRAY_BLOB 4 /* Data is struct iovec */ /* ** Versions of the above #defines that omit the initial SQLITE_, for ** legacy compatibility. */ #define CARRAY_INT32 0 /* Data is 32-bit signed integers */ #define CARRAY_INT64 1 /* Data is 64-bit signed integers */ #define CARRAY_DOUBLE 2 /* Data is doubles */ #define CARRAY_TEXT 3 /* Data is char* */ #define CARRAY_BLOB 4 /* Data is struct iovec */ /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. */ #ifdef SQLITE_OMIT_FLOATING_POINT # undef double #endif |
| ︙ | ︙ | |||
12308 12309 12310 12311 12312 12313 12314 12315 12316 12317 12318 12319 12320 12321 | /* ** CAPI3REF: Apply A Changeset To A Database ** ** Apply a changeset or patchset to a database. These functions attempt to ** update the "main" database attached to handle db with the changes found in ** the changeset passed via the second and third arguments. ** ** The fourth argument (xFilter) passed to these functions is the "filter ** callback". This may be passed NULL, in which case all changes in the ** changeset are applied to the database. For sqlite3changeset_apply() and ** sqlite3_changeset_apply_v2(), if it is not NULL, then it is invoked once ** for each table affected by at least one change in the changeset. In this ** case the table name is passed as the second argument, and a copy of | > > > > > > > > > | 12441 12442 12443 12444 12445 12446 12447 12448 12449 12450 12451 12452 12453 12454 12455 12456 12457 12458 12459 12460 12461 12462 12463 | /* ** CAPI3REF: Apply A Changeset To A Database ** ** Apply a changeset or patchset to a database. These functions attempt to ** update the "main" database attached to handle db with the changes found in ** the changeset passed via the second and third arguments. ** ** All changes made by these functions are enclosed in a savepoint transaction. ** If any other error (aside from a constraint failure when attempting to ** write to the target database) occurs, then the savepoint transaction is ** rolled back, restoring the target database to its original state, and an ** SQLite error code returned. Additionally, starting with version 3.51.0, ** an error code and error message that may be accessed using the ** [sqlite3_errcode()] and [sqlite3_errmsg()] APIs are left in the database ** handle. ** ** The fourth argument (xFilter) passed to these functions is the "filter ** callback". This may be passed NULL, in which case all changes in the ** changeset are applied to the database. For sqlite3changeset_apply() and ** sqlite3_changeset_apply_v2(), if it is not NULL, then it is invoked once ** for each table affected by at least one change in the changeset. In this ** case the table name is passed as the second argument, and a copy of |
| ︙ | ︙ | |||
12446 12447 12448 12449 12450 12451 12452 | ** </dl> ** ** It is safe to execute SQL statements, including those that write to the ** table that the callback related to, from within the xConflict callback. ** This can be used to further customize the application's conflict ** resolution strategy. ** | < < < < < < | 12588 12589 12590 12591 12592 12593 12594 12595 12596 12597 12598 12599 12600 12601 | ** </dl> ** ** It is safe to execute SQL statements, including those that write to the ** table that the callback related to, from within the xConflict callback. ** This can be used to further customize the application's conflict ** resolution strategy. ** ** If the output parameters (ppRebase) and (pnRebase) are non-NULL and ** the input is a changeset (not a patchset), then sqlite3changeset_apply_v2() ** may set (*ppRebase) to point to a "rebase" that may be used with the ** sqlite3_rebaser APIs buffer before returning. In this case (*pnRebase) ** is set to the size of the buffer in bytes. It is the responsibility of the ** caller to eventually free any such buffer using sqlite3_free(). The buffer ** is only allocated and populated if one or more conflicts were encountered |
| ︙ | ︙ |
Changes to src/add.c.
| ︙ | ︙ | |||
1004 1005 1006 1007 1008 1009 1010 | } } /* ** COMMAND: mv ** COMMAND: rename* ** | | | | 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 | } } /* ** COMMAND: mv ** COMMAND: rename* ** ** Usage: %fossil mv|rename ?OPTIONS? OLDNAME NEWNAME ** or: %fossil mv|rename ?OPTIONS? OLDNAME... DIR ** ** Move or rename one or more files or directories within the repository tree. ** You can either rename a file or directory or move it to another subdirectory. ** ** The 'mv' command does NOT normally rename or move the files on disk. ** This command merely records the fact that file names have changed so ** that appropriate notations can be made at the next [[commit]]. |
| ︙ | ︙ |
Changes to src/branch.c.
| ︙ | ︙ | |||
841 842 843 844 845 846 847 848 849 850 851 852 853 854 |
int show_colors = PB("colors");
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
style_set_current_feature("branch");
style_header("Branches");
style_adunit_config(ADUNIT_RIGHT_OK);
style_submenu_checkbox("colors", "Use Branch Colors", 0, 0);
login_anonymous_available();
brlist_create_temp_table();
db_prepare(&q, "SELECT * FROM tmp_brlist ORDER BY mtime DESC");
rNow = db_double(0.0, "SELECT julianday('now')");
@ <script id="brlist-data" type="application/json">\
@ {"timelineUrl":"%R/timeline"}</script>
| > | 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 |
int show_colors = PB("colors");
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
style_set_current_feature("branch");
style_header("Branches");
style_adunit_config(ADUNIT_RIGHT_OK);
style_submenu_checkbox("colors", "Use Branch Colors", 0, 0);
login_anonymous_available();
brlist_create_temp_table();
db_prepare(&q, "SELECT * FROM tmp_brlist ORDER BY mtime DESC");
rNow = db_double(0.0, "SELECT julianday('now')");
@ <script id="brlist-data" type="application/json">\
@ {"timelineUrl":"%R/timeline"}</script>
|
| ︙ | ︙ | |||
1017 1018 1019 1020 1021 1022 1023 | } /* ** This routine is called while for each check-in that is rendered by ** the timeline of a "brlist" page. Add some additional hyperlinks ** to the end of the line. */ | | | | | < | < < < > | > > | > > > > > > > | > | > > | > > < > | < < < < | > | | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 |
}
/*
** This routine is called while for each check-in that is rendered by
** the timeline of a "brlist" page. Add some additional hyperlinks
** to the end of the line.
*/
static void brtimeline_extra(
Stmt *pQuery, /* Current row of the timeline query */
int tmFlags, /* Flags to www_print_timeline() */
const char *zThisUser, /* Suppress links to this user */
const char *zThisTag /* Suppress links to this tag */
){
int rid;
int tmFlagsNew;
char *zBrName;
if( (tmFlags & TIMELINE_INLINE)!=0 ){
tmFlagsNew = (tmFlags & ~TIMELINE_VIEWS) | TIMELINE_MODERN;
cgi_printf("(");
}else{
tmFlagsNew = tmFlags;
}
timeline_extra(pQuery,tmFlagsNew,zThisUser,zThisTag);
if( !g.perm.Hyperlink ) return;
rid = db_column_int(pQuery,0);
zBrName = branch_of_rid(rid);
@ branch: <span class='timelineHash'>\
@ %z(href("%R/timeline?r=%T",zBrName))%h(zBrName)</a></span>
if( (tmFlags & TIMELINE_INLINE)!=0 ){
cgi_printf(")");
}
}
/*
** WEBPAGE: brtimeline
**
** List the first check of every branch, starting with the most recent
** and going backwards in time.
**
** Query parameters:
**
** ubg Color the graph by user, not by branch.
*/
void brtimeline_page(void){
Blob sql = empty_blob;
Stmt q;
int tmFlags; /* Timeline display flags */
int fNoHidden = PB("nohidden")!=0; /* The "nohidden" query parameter */
int fOnlyHidden = PB("onlyhidden")!=0; /* The "onlyhidden" query parameter */
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
if( robot_restrict("timelineX") ) return;
style_set_current_feature("branch");
style_header("Branches");
style_submenu_element("Branch List", "brlist");
login_anonymous_available();
timeline_ss_submenu();
cgi_check_for_malice();
@ <h2>The initial check-in for each branch:</h2>
blob_append(&sql, timeline_query_for_www(), -1);
blob_append_sql(&sql,
"AND blob.rid IN (SELECT rid FROM tagxref"
" WHERE tagtype>0 AND tagid=%d AND srcid!=0)", TAG_BRANCH);
if( fNoHidden || fOnlyHidden ){
const char* zUnaryOp = fNoHidden ? "NOT" : "";
blob_append_sql(&sql,
" AND %s EXISTS(SELECT 1 FROM tagxref"
" WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)\n",
zUnaryOp/*safe-for-%s*/, TAG_HIDDEN);
}
db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_sql_text(&sql));
blob_reset(&sql);
/* Always specify TIMELINE_DISJOINT, or graph_finish() may fail because of too
** many descenders to (off-screen) parents. */
tmFlags = TIMELINE_DISJOINT | TIMELINE_NOSCROLL;
if( PB("ubg")!=0 ){
tmFlags |= TIMELINE_UCOLOR;
}else{
tmFlags |= TIMELINE_BRCOLOR;
}
www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, brtimeline_extra);
db_finalize(&q);
style_finish_page();
}
/*
** Generate a multichoice submenu for the few recent active branches. zName is
** the query parameter used to select the current checkin. zCI is optional and
** represent the currently selected checkin, so if it is a checkin hash
** instead of a branch, it can be part of the multichoice menu.
*/
void generate_branch_submenu_multichoice(
const char* zName, /* Query parameter name */
const char* zCI /* Current checkin */
){
Stmt q;
const int brFlags = BRL_ORDERBY_MTIME | BRL_OPEN_ONLY;
static const char *zBranchMenuList[32*2]; /* 2 per entries */
const int nLimit = count(zBranchMenuList)/2;
int i = 0;
if( zName == 0 ) zName = "ci";
branch_prepare_list_query(&q, brFlags, 0, nLimit, 0);
zBranchMenuList[i++] = "";
zBranchMenuList[i++] = "All Checkins";
if( zCI ){
zCI = fossil_strdup(zCI);
zBranchMenuList[i++] = zCI;
zBranchMenuList[i++] = zCI;
}
/* If current checkin is not "tip", add it to the list */
if( zCI==0 || strcmp(zCI, "tip") ){
zBranchMenuList[i++] = "tip";
zBranchMenuList[i++] = "tip";
}
while( i/2 < nLimit && db_step(&q)==SQLITE_ROW ){
const char* zBr = fossil_strdup(db_column_text(&q, 0));
/* zCI is already in the list, don't add it twice */
if( zCI==0 || strcmp(zBr, zCI) ){
zBranchMenuList[i++] = zBr;
zBranchMenuList[i++] = zBr;
}
}
db_finalize(&q);
style_submenu_multichoice(zName, i/2, zBranchMenuList, 0);
}
|
Changes to src/browse.c.
| ︙ | ︙ | |||
169 170 171 172 173 174 175 |
char *zPrefix;
Stmt q;
const char *zCI = P("ci");
int rid = 0;
char *zUuid = 0;
Manifest *pM = 0;
const char *zSubdirLink;
| < < | 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
char *zPrefix;
Stmt q;
const char *zCI = P("ci");
int rid = 0;
char *zUuid = 0;
Manifest *pM = 0;
const char *zSubdirLink;
HQuery sURI;
int isSymbolicCI = 0; /* ci= is symbolic name, not a hash prefix */
int isBranchCI = 0; /* True if ci= refers to a branch name */
char *zHeader = 0;
const char *zRegexp; /* The re= query parameter */
char *zMatch; /* Extra title text describing the match */
int bDocDir = PB("dx") || strncmp(g.zPath, "docdir", 6)==0;
|
| ︙ | ︙ | |||
196 197 198 199 200 201 202 |
** specific check-in does not exist, clear zCI. zCI==0 will cause all
** files from all check-ins to be displayed.
*/
if( bDocDir && zCI==0 ) zCI = "trunk";
if( zCI ){
pM = manifest_get_by_name(zCI, &rid);
if( pM ){
| < < < | 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
** specific check-in does not exist, clear zCI. zCI==0 will cause all
** files from all check-ins to be displayed.
*/
if( bDocDir && zCI==0 ) zCI = "trunk";
if( zCI ){
pM = manifest_get_by_name(zCI, &rid);
if( pM ){
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI))!=0);
isBranchCI = branch_includes_uuid(zCI, zUuid);
if( bDocDir ) zCI = mprintf("%S", zUuid);
Th_StoreUnsafe("current_checkin", zCI);
}else{
zCI = 0;
|
| ︙ | ︙ | |||
232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
zHeader = mprintf("%z matching \"%s\"", zHeader, zRegexp);
zMatch = mprintf(" matching \"%h\"", zRegexp);
}else{
zMatch = "";
}
style_header("%s", zHeader);
fossil_free(zHeader);
style_adunit_config(ADUNIT_RIGHT_OK);
sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
pathelementFunc, 0, 0);
url_initialize(&sURI, "dir");
cgi_check_for_malice();
cgi_query_parameters_to_url(&sURI);
| > > > | 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
zHeader = mprintf("%z matching \"%s\"", zHeader, zRegexp);
zMatch = mprintf(" matching \"%h\"", zRegexp);
}else{
zMatch = "";
}
style_header("%s", zHeader);
fossil_free(zHeader);
if( rid && zD==0 && zMatch[0]==0 && g.perm.Zip ){
style_submenu_element("Download","%R/rchvdwnld/%!S",zUuid);
}
style_adunit_config(ADUNIT_RIGHT_OK);
sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
pathelementFunc, 0, 0);
url_initialize(&sURI, "dir");
cgi_check_for_malice();
cgi_query_parameters_to_url(&sURI);
|
| ︙ | ︙ | |||
281 282 283 284 285 286 287 |
if( nD==0 && !bDocDir ){
style_submenu_element("File Ages", "%R/fileage?name=%T", zCI);
}
}else{
@ in any check-in</h2>
zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
}
| < < < < < < < < > > > > > | 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
if( nD==0 && !bDocDir ){
style_submenu_element("File Ages", "%R/fileage?name=%T", zCI);
}
}else{
@ in any check-in</h2>
zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
}
if( zD && !bDocDir ){
style_submenu_element("History","%R/timeline?chng=%T/*", zD);
}
if( !bDocDir ){
style_submenu_element("Tree-View", "%s",
url_render(&sURI, "type", "tree", 0, 0));
}
if( !bDocDir ){
/* Generate the Branch list submenu */
generate_branch_submenu_multichoice("ci", zCI);
}
/* Compute the temporary table "localfiles" containing the names
** of all files and subdirectories in the zD[] directory.
**
** Subdirectory names begin with "/". This causes them to sort
** first and it also gives us an easy way to distinguish files
** from directories in the loop that follows.
|
| ︙ | ︙ | |||
703 704 705 706 707 708 709 |
char *zUuid = 0;
Blob dirname;
Manifest *pM = 0;
double rNow = 0;
char *zNow = 0;
int useMtime = atoi(PD("mtime","0"));
int sortOrder = atoi(PD("sort",useMtime?"1":"0"));
| < < | 698 699 700 701 702 703 704 705 706 707 708 709 710 711 |
char *zUuid = 0;
Blob dirname;
Manifest *pM = 0;
double rNow = 0;
char *zNow = 0;
int useMtime = atoi(PD("mtime","0"));
int sortOrder = atoi(PD("sort",useMtime?"1":"0"));
const char *zRE; /* the value for the re=REGEXP query parameter */
const char *zObjType; /* "files" by default or "folders" for "nofiles" */
char *zREx = ""; /* Extra parameters for path hyperlinks */
ReCompiled *pRE = 0; /* Compiled regular expression */
FileTreeNode *p; /* One line of the tree */
FileTree sTree; /* The complete tree of files */
HQuery sURI; /* Hyperlink */
|
| ︙ | ︙ | |||
745 746 747 748 749 750 751 |
}else{
startExpanded = 0;
}
/* If a regular expression is specified, compile it */
zRE = P("re");
if( zRE ){
| | < < < > > > | 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 |
}else{
startExpanded = 0;
}
/* If a regular expression is specified, compile it */
zRE = P("re");
if( zRE ){
fossil_re_compile(&pRE, zRE, 0);
zREx = mprintf("&re=%T", zRE);
}
cgi_check_for_malice();
/* If the name= parameter is an empty string, make it a NULL pointer */
if( zD && strlen(zD)==0 ){ zD = 0; }
/* If a specific check-in is requested, fetch and parse it. If the
** specific check-in does not exist, clear zCI. zCI==0 will cause all
** files from all check-ins to be displayed.
*/
if( zCI ){
pM = manifest_get_by_name(zCI, &rid);
if( pM ){
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
zNow = db_text("", "SELECT datetime(mtime,toLocal())"
" FROM event WHERE objid=%d", rid);
isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI)) != 0);
isBranchCI = branch_includes_uuid(zCI, zUuid);
Th_StoreUnsafe("current_checkin", zCI);
}else{
zCI = 0;
}
}
if( zCI==0 ){
rNow = db_double(0.0, "SELECT max(mtime) FROM event");
zNow = db_text("", "SELECT datetime(max(mtime),toLocal()) FROM event");
}
/* Generate the Branch list submenu */
generate_branch_submenu_multichoice("ci", zCI);
assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) );
if( zD==0 ){
if( zCI ){
zHeader = mprintf("Top-level Files of %s", zCI);
}else{
zHeader = mprintf("All Top-level Files");
}
|
| ︙ | ︙ | |||
816 817 818 819 820 821 822 |
"0", "Sort By Filename",
"1", "Sort By Age",
"2", "Sort By Size"
};
style_submenu_multichoice("sort", 3, sort_orders, 0);
}
if( zCI ){
| < < | | < | | < < | 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 |
"0", "Sort By Filename",
"1", "Sort By Age",
"2", "Sort By Size"
};
style_submenu_multichoice("sort", 3, sort_orders, 0);
}
if( zCI ){
if( nD==0 && !showDirOnly ){
style_submenu_element("File Ages", "%R/fileage?name=%T", zCI);
}
}
style_submenu_element("Flat-View", "%s",
url_render(&sURI, "type", "flat", 0, 0));
if( rid && zD==0 && zRE==0 && !showDirOnly && g.perm.Zip ){
style_submenu_element("Download","%R/rchvdwnld/%!S", zUuid);
}
/* Compute the file hierarchy.
*/
if( zCI ){
Stmt q;
compute_fileage(rid, 0);
db_prepare(&q,
|
| ︙ | ︙ | |||
1036 1037 1038 1039 1040 1041 1042 | */ static const char zComputeFileAgeSetup[] = @ CREATE TABLE IF NOT EXISTS temp.fileage( @ fnid INTEGER PRIMARY KEY, @ fid INTEGER, @ mid INTEGER, @ mtime DATETIME, | | > | | > | 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 | */ static const char zComputeFileAgeSetup[] = @ CREATE TABLE IF NOT EXISTS temp.fileage( @ fnid INTEGER PRIMARY KEY, @ fid INTEGER, @ mid INTEGER, @ mtime DATETIME, @ pathname TEXT, @ uuid TEXT @ ); @ CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin; ; static const char zComputeFileAgeRun[] = @ WITH RECURSIVE @ ckin(x) AS (VALUES(:ckin) @ UNION @ SELECT plink.pid @ FROM ckin, plink @ WHERE plink.cid=ckin.x) @ INSERT OR IGNORE INTO fileage(fnid, fid, mid, mtime, pathname, uuid) @ SELECT filename.fnid, mlink.fid, mlink.mid, event.mtime, filename.name, @ foci.uuid @ FROM foci, filename, blob, mlink, event @ WHERE foci.checkinID=:ckin @ AND foci.filename GLOB :glob @ AND filename.name=foci.filename @ AND blob.uuid=foci.uuid @ AND mlink.fid=blob.rid @ AND mlink.fid!=mlink.pid |
| ︙ | ︙ | |||
1160 1161 1162 1163 1164 1165 1166 |
const char *zNow; /* Time of check-in */
int isBranchCI; /* name= is a branch name */
int showId = PB("showid");
Stmt q1, q2;
double baseTime;
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
| < > > > > | 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 |
const char *zNow; /* Time of check-in */
int isBranchCI; /* name= is a branch name */
int showId = PB("showid");
Stmt q1, q2;
double baseTime;
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
zName = P("name");
if( zName==0 ) zName = "tip";
rid = symbolic_name_to_rid(zName, "ci");
if( rid==0 ){
fossil_fatal("not a valid check-in: %s", zName);
}
zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
isBranchCI = branch_includes_uuid(zName,zUuid);
baseTime = db_double(0.0,"SELECT mtime FROM event WHERE objid=%d", rid);
zNow = db_text("", "SELECT datetime(mtime,toLocal()) FROM event"
" WHERE objid=%d", rid);
style_submenu_element("Tree-View", "%R/tree?ci=%T&mtime=1&type=tree", zName);
/* Generate the Branch list submenu */
generate_branch_submenu_multichoice("name", zName);
style_header("File Ages");
zGlob = P("glob");
cgi_check_for_malice();
compute_fileage(rid,zGlob);
db_multi_exec("CREATE INDEX fileage_ix1 ON fileage(mid,pathname);");
if( fossil_strcmp(zName,"tip")==0 ){
|
| ︙ | ︙ |
Changes to src/captcha.c.
| ︙ | ︙ | |||
742 743 744 745 746 747 748 |
const char *zPw = P("name");
if( zPw==0 || zPw[0]==0 ){
(void)exclude_spiders(1);
@ <hr><p>The captcha is shown above. Add a name=HEX query parameter
@ to see how HEX would be rendered in the current captcha font.
@ <h2>Debug/Testing Values:</h2>
@ <ul>
| | > > > > > > > > < | < < | < | 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 |
const char *zPw = P("name");
if( zPw==0 || zPw[0]==0 ){
(void)exclude_spiders(1);
@ <hr><p>The captcha is shown above. Add a name=HEX query parameter
@ to see how HEX would be rendered in the current captcha font.
@ <h2>Debug/Testing Values:</h2>
@ <ul>
@ <li> g.isRobot = %d(g.isRobot)
@ <li> g.zLogin = %h(g.zLogin)
@ <li> login_cookie_welformed() = %d(login_cookie_wellformed())
@ <li> captcha_is_correct(1) = %d(captcha_is_correct(1)).
@ </ul>
style_finish_page();
}else{
style_set_current_feature("test");
style_header("Captcha Test");
@ <pre class="captcha">
@ %s(captcha_render(zPw))
@ </pre>
style_finish_page();
}
}
/*
** WEBPAGE: honeypot
** This page is a honeypot for spiders and bots.
*/
void honeypot_page(void){
(void)exclude_spiders(0);
}
/*
** Check to see if the current request is coming from an agent that
** self-identifies as a spider.
**
** If the agent does not claim to be a spider or if the user has logged
** in (even as anonymous), then return 0 without doing anything.
**
** But if the user agent does self-identify as a spider and there is
** no login, offer a captcha challenge to allow the user agent to prove
** that he is human and return non-zero.
**
** If the bTest argument is non-zero, then show the captcha regardless of
** how the agent identifies. This is used for testing only.
*/
int exclude_spiders(int bTest){
if( !bTest ){
if( g.zLogin!=0 ) return 0; /* Logged in. Consider them human */
if( login_cookie_wellformed() ){
/* Logged into another member of the login group */
return 0;
}
}
/* This appears to be a spider. Offer the captcha */
style_set_current_feature("captcha");
style_header("Captcha");
style_submenu_enable(0);
@ <form method='POST' action='%R/ityaar'>
@ <h2>Prove that you are human:
if( bTest ){
@ <input type="hidden" name="istest" value="1">
}
captcha_generate(3);
@ </form>
if( !bTest ){
if( P("fossil-goto")==0 ){
|
| ︙ | ︙ | |||
828 829 830 831 832 833 834 |
/* ^^^^--- Don't overwrite a valid login on another repo! */
login_set_anon_cookie(0, 0);
}
cgi_append_header("X-Robot: 0\r\n");
}
login_redirect_to_g();
}else{
| | | 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 |
/* ^^^^--- Don't overwrite a valid login on another repo! */
login_set_anon_cookie(0, 0);
}
cgi_append_header("X-Robot: 0\r\n");
}
login_redirect_to_g();
}else{
g.isRobot = 1;
(void)exclude_spiders(bTest);
if( bTest ){
@ <hr><p>Wrong code. Try again
style_finish_page();
}
}
}
|
| ︙ | ︙ |
Changes to src/cgi.c.
| ︙ | ︙ | |||
239 240 241 242 243 244 245 | cgi_combine_header_and_body(); return blob_buffer(&cgiContent[0]); } /* ** Additional information used to form the HTTP reply */ | | > > | | 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
cgi_combine_header_and_body();
return blob_buffer(&cgiContent[0]);
}
/*
** Additional information used to form the HTTP reply
*/
static const char *zReplyMimeType = "text/html"; /* Content type of the reply */
static const char *zReplyStatus = "OK"; /* Reply status description */
static int iReplyStatus = 200; /* Reply status code */
static Blob extraHeader = BLOB_INITIALIZER; /* Extra header text */
static int rangeStart = 0; /* Start of Range: */
static int rangeEnd = 0; /* End of Range: plus 1 */
/*
** Set the reply content type.
**
** The reply content type defaults to "text/html". It only needs to be
** changed (by calling this routine) in the exceptional case where some
** other content type is being returned.
*/
void cgi_set_content_type(const char *zType){
int i;
for(i=0; zType[i]>='+' && zType[i]<='z'; i++){}
zReplyMimeType = fossil_strndup(zType, i);
}
/*
** Erase any existing reply content. Replace is with a pNewContent.
**
** This routine erases pNewContent. In other words, it move pNewContent
** into the content buffer.
|
| ︙ | ︙ | |||
334 335 336 337 338 339 340 |
*/
static int is_gzippable(void){
if( g.fNoHttpCompress ) return 0;
if( strstr(PD("HTTP_ACCEPT_ENCODING", ""), "gzip")==0 ) return 0;
/* Maintenance note: this oddball structure is intended to make
** adding new mimetypes to this list less of a performance hit than
** doing a strcmp/glob over a growing set of compressible types. */
| | | | | | | | 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 |
*/
static int is_gzippable(void){
if( g.fNoHttpCompress ) return 0;
if( strstr(PD("HTTP_ACCEPT_ENCODING", ""), "gzip")==0 ) return 0;
/* Maintenance note: this oddball structure is intended to make
** adding new mimetypes to this list less of a performance hit than
** doing a strcmp/glob over a growing set of compressible types. */
switch(zReplyMimeType ? *zReplyMimeType : 0){
case (int)'a':
if(0==fossil_strncmp("application/",zReplyMimeType,12)){
const char * z = &zReplyMimeType[12];
switch(*z){
case (int)'j':
return fossil_strcmp("javascript", z)==0
|| fossil_strcmp("json", z)==0;
case (int)'w': return fossil_strcmp("wasm", z)==0;
case (int)'x':
return fossil_strcmp("x-tcl", z)==0
|| fossil_strcmp("x-tar", z)==0;
default:
return sqlite3_strglob("*xml", z)==0;
}
}
break;
case (int)'i':
return fossil_strcmp(zReplyMimeType, "image/svg+xml")==0
|| fossil_strcmp(zReplyMimeType, "image/vnd.microsoft.icon")==0;
case (int)'t':
return fossil_strncmp(zReplyMimeType, "text/", 5)==0;
}
return 0;
}
/*
** The following routines read or write content from/to the wire for
|
| ︙ | ︙ | |||
400 401 402 403 404 405 406 407 408 409 410 411 412 413 |
if( !g.httpUseSSL ){
return fread(ptr, 1, nmemb, g.httpIn);
}
#ifdef FOSSIL_ENABLE_SSL
return ssl_read_server(g.httpSSLConn, ptr, nmemb, 1);
#else
fossil_fatal("SSL not available");
#endif
}
/* Works like feof():
**
** Return true if end-of-input has been reached.
*/
| > > | 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 |
if( !g.httpUseSSL ){
return fread(ptr, 1, nmemb, g.httpIn);
}
#ifdef FOSSIL_ENABLE_SSL
return ssl_read_server(g.httpSSLConn, ptr, nmemb, 1);
#else
fossil_fatal("SSL not available");
/* NOT REACHED */
return 0;
#endif
}
/* Works like feof():
**
** Return true if end-of-input has been reached.
*/
|
| ︙ | ︙ | |||
447 448 449 450 451 452 453 |
fflush(g.httpOut);
}
}
/*
** Given a Content-Type value, returns a string suitable for appending
** to the Content-Type header for adding (or not) the "; charset=..."
| | | | | 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 |
fflush(g.httpOut);
}
}
/*
** Given a Content-Type value, returns a string suitable for appending
** to the Content-Type header for adding (or not) the "; charset=..."
** part. It returns an empty string for most types or if zReplyMimeType
** is NULL.
**
** See forum post f60dece061c364d1 for the discussions which lead to
** this. Previously we always appended the charset, but WASM loaders
** are pedantic and refuse to load any responses which have a
** charset. Also, adding a charset is not strictly appropriate for
** most types (and not required for many others which may ostensibly
** benefit from one, as detailed in that forum post).
*/
static const char * content_type_charset(const char *zReplyMimeType){
if(0==fossil_strncmp(zReplyMimeType,"text/",5)){
return "; charset=utf-8";
}
return "";
}
/*
** Generate the reply to a web request. The output might be an
|
| ︙ | ︙ | |||
498 499 500 501 502 503 504 |
blob_appendf(&hdr, "X-UA-Compatible: IE=edge\r\n");
}else{
assert( rangeEnd==0 );
blob_appendf(&hdr, "Status: %d %s\r\n", iReplyStatus, zReplyStatus);
}
if( etag_tag()[0]!=0
&& iReplyStatus==200
| | | 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 |
blob_appendf(&hdr, "X-UA-Compatible: IE=edge\r\n");
}else{
assert( rangeEnd==0 );
blob_appendf(&hdr, "Status: %d %s\r\n", iReplyStatus, zReplyStatus);
}
if( etag_tag()[0]!=0
&& iReplyStatus==200
&& strcmp(zReplyMimeType,"text/html")!=0
){
/* Do not cache HTML replies as those will have been generated and
** will likely, therefore, contains a nonce and we want that nonce to
** be different every time. */
blob_appendf(&hdr, "ETag: \"%s\"\r\n", etag_tag());
blob_appendf(&hdr, "Cache-Control: max-age=%d\r\n", etag_maxage());
if( etag_mtime()>0 ){
|
| ︙ | ︙ | |||
540 541 542 543 544 545 546 |
** highlighter scripts.
**
** These headers are probably best added by the web server hosting fossil as
** a CGI script.
*/
if( iReplyStatus!=304 ) {
| | | | | 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 |
** highlighter scripts.
**
** These headers are probably best added by the web server hosting fossil as
** a CGI script.
*/
if( iReplyStatus!=304 ) {
blob_appendf(&hdr, "Content-Type: %s%s\r\n", zReplyMimeType,
content_type_charset(zReplyMimeType));
if( fossil_strcmp(zReplyMimeType,"application/x-fossil")==0 ){
cgi_combine_header_and_body();
blob_compress(&cgiContent[0], &cgiContent[0]);
}
if( is_gzippable() && iReplyStatus!=206 ){
int i;
gzip_begin(0);
|
| ︙ | ︙ | |||
942 943 944 945 946 947 948 949 950 951 952 953 954 955 |
/*
** Add an environment varaible value to the parameter set. The zName
** portion is fixed but a copy is be made of zValue.
*/
void cgi_setenv(const char *zName, const char *zValue){
cgi_set_parameter_nocopy(zName, fossil_strdup(zValue), 0);
}
/*
** Add a list of query parameters or cookies to the parameter set.
**
** Each parameter is of the form NAME=VALUE. Both the NAME and the
** VALUE may be url-encoded ("+" for space, "%HH" for other special
** characters). But this routine assumes that NAME contains no
| > > > > > > > > > > | 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 |
/*
** Add an environment varaible value to the parameter set. The zName
** portion is fixed but a copy is be made of zValue.
*/
void cgi_setenv(const char *zName, const char *zValue){
cgi_set_parameter_nocopy(zName, fossil_strdup(zValue), 0);
}
/*
** Returns true if NUL-terminated z contains any non-NUL
** control characters (<0x20, 32d).
*/
static int contains_ctrl(const char *z){
assert(z);
for( ; *z>=0x20; ++z ){}
return 0!=*z;
}
/*
** Add a list of query parameters or cookies to the parameter set.
**
** Each parameter is of the form NAME=VALUE. Both the NAME and the
** VALUE may be url-encoded ("+" for space, "%HH" for other special
** characters). But this routine assumes that NAME contains no
|
| ︙ | ︙ | |||
972 973 974 975 976 977 978 979 | ** ** Parameters are separated by the "terminator" character. Whitespace ** before the NAME is ignored. ** ** The input string "z" is modified but no copies is made. "z" ** should not be deallocated or changed again after this routine ** returns or it will corrupt the parameter table. */ | > > > > | | 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 |
**
** Parameters are separated by the "terminator" character. Whitespace
** before the NAME is ignored.
**
** The input string "z" is modified but no copies is made. "z"
** should not be deallocated or changed again after this routine
** returns or it will corrupt the parameter table.
**
** If bPermitCtrl is false and the decoded value of any entry in z
** contains control characters (<0x20, 32d) then that key/value pair
** are skipped.
*/
static void add_param_list(char *z, int terminator, int bPermitCtrl){
int isQP = terminator=='&';
while( *z ){
char *zName;
char *zValue;
while( fossil_isspace(*z) ){ z++; }
zName = z;
while( *z && *z!='=' && *z!=terminator ){ z++; }
|
| ︙ | ︙ | |||
996 997 998 999 1000 1001 1002 |
}
dehttpize(zValue);
}else{
if( *z ){ *z++ = 0; }
zValue = "";
}
if( zName[0] && fossil_no_strange_characters(zName+1) ){
| > > > | | 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 |
}
dehttpize(zValue);
}else{
if( *z ){ *z++ = 0; }
zValue = "";
}
if( zName[0] && fossil_no_strange_characters(zName+1) ){
if( 0==bPermitCtrl && contains_ctrl(zValue) ){
continue /* Reject it. An argument could be made
** for break instead of continue. */;
}else if( fossil_islower(zName[0]) ){
cgi_set_parameter_nocopy(zName, zValue, isQP);
}else if( fossil_isupper(zName[0]) ){
cgi_set_parameter_nocopy_tolower(zName, zValue, isQP);
}
}
#ifdef FOSSIL_ENABLE_JSON
json_setenv( zName, cson_value_new_string(zValue,strlen(zValue)) );
|
| ︙ | ︙ | |||
1295 1296 1297 1298 1299 1300 1301 |
*/
int cgi_setup_query_string(void){
int rc = 0;
char * z = (char*)P("QUERY_STRING");
if( z ){
rc = 0x01;
z = fossil_strdup(z);
| | | 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 |
*/
int cgi_setup_query_string(void){
int rc = 0;
char * z = (char*)P("QUERY_STRING");
if( z ){
rc = 0x01;
z = fossil_strdup(z);
add_param_list(z, '&', 0);
z = (char*)P("skin");
if( z ){
char *zErr = skin_use_alternative(z, 2, SKIN_FROM_QPARAM);
rc |= 0x02;
if( !zErr && P("once")==0 ){
cookie_write_parameter("skin","skin",z);
/* Per /chat discussion, passing ?skin=... without "once"
|
| ︙ | ︙ | |||
1455 1456 1457 1458 1459 1460 1461 |
assert(!g.json.isJsonMode &&
"Internal misconfiguration of g.json.isJsonMode");
}
#endif
z = (char*)P("HTTP_COOKIE");
if( z ){
z = fossil_strdup(z);
| | | 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 |
assert(!g.json.isJsonMode &&
"Internal misconfiguration of g.json.isJsonMode");
}
#endif
z = (char*)P("HTTP_COOKIE");
if( z ){
z = fossil_strdup(z);
add_param_list(z, ';', 0);
z = (char*)cookie_value("skin",0);
if(z){
skin_use_alternative(z, 2, SKIN_FROM_COOKIE);
}
}
cgi_setup_query_string();
|
| ︙ | ︙ | |||
1518 1519 1520 1521 1522 1523 1524 |
if( len==0 ) return;
if( fossil_strcmp(g.zContentType,"application/x-www-form-urlencoded")==0
|| fossil_strncmp(g.zContentType,"multipart/form-data",19)==0
){
char *z = blob_str(&g.cgiIn);
cgi_trace(z);
if( g.zContentType[0]=='a' ){
| | | 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 |
if( len==0 ) return;
if( fossil_strcmp(g.zContentType,"application/x-www-form-urlencoded")==0
|| fossil_strncmp(g.zContentType,"multipart/form-data",19)==0
){
char *z = blob_str(&g.cgiIn);
cgi_trace(z);
if( g.zContentType[0]=='a' ){
add_param_list(z, '&', 1);
}else{
process_multipart_form_data(z, len);
}
blob_init(&g.cgiIn, 0, 0);
}
}
|
| ︙ | ︙ | |||
1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 |
CGIDEBUG(("env-match [%s] = [%s]\n", zName, zValue));
return zValue;
}
}
CGIDEBUG(("no-match [%s]\n", zName));
return zDefault;
}
/*
** Renders the "begone, spider" page and exits.
*/
static void cgi_begone_spider(const char *zName){
Blob content = empty_blob;
cgi_set_content(&content);
| > > > > > > > > > > > > > > > | 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 |
CGIDEBUG(("env-match [%s] = [%s]\n", zName, zValue));
return zValue;
}
}
CGIDEBUG(("no-match [%s]\n", zName));
return zDefault;
}
/*
** Return TRUE if the specific parameter exists and is a query parameter.
** Return FALSE if the parameter is a cookie or environment variable.
*/
int cgi_is_qp(const char *zName){
int i;
if( zName==0 || fossil_isupper(zName[0]) ) return 0;
for(i=0; i<nUsedQP; i++){
if( fossil_strcmp(aParamQP[i].zName,zName)==0 ){
return aParamQP[i].isQP;
}
}
return 0;
}
/*
** Renders the "begone, spider" page and exits.
*/
static void cgi_begone_spider(const char *zName){
Blob content = empty_blob;
cgi_set_content(&content);
|
| ︙ | ︙ | |||
2004 2005 2006 2007 2008 2009 2010 |
static NORETURN void malformed_request(const char *zMsg, ...){
va_list ap;
char *z;
va_start(ap, zMsg);
z = vmprintf(zMsg, ap);
va_end(ap);
cgi_set_status(400, "Bad Request");
| | | 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 |
static NORETURN void malformed_request(const char *zMsg, ...){
va_list ap;
char *z;
va_start(ap, zMsg);
z = vmprintf(zMsg, ap);
va_end(ap);
cgi_set_status(400, "Bad Request");
zReplyMimeType = "text/plain";
if( g.zReqType==0 ) g.zReqType = "WWW";
if( g.zReqType[0]=='C' && PD("SERVER_SOFTWARE",0)!=0 ){
const char *zServer = PD("SERVER_SOFTWARE","");
cgi_printf("Bad CGI Request from \"%s\": %s\n",zServer,z);
}else{
cgi_printf("Bad %s Request: %s\n", g.zReqType, z);
}
|
| ︙ | ︙ | |||
2252 2253 2254 2255 2256 2257 2258 |
**
** It is called in a loop so some variables will need to be replaced
*/
void cgi_handle_ssh_http_request(const char *zIpAddr){
static int nCycles = 0;
static char *zCmd = 0;
char *z, *zToken;
| | | > | 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 |
**
** It is called in a loop so some variables will need to be replaced
*/
void cgi_handle_ssh_http_request(const char *zIpAddr){
static int nCycles = 0;
static char *zCmd = 0;
char *z, *zToken;
char *zMethod;
int i;
size_t n;
char zLine[2000]; /* A single line of input. */
assert( !g.httpUseSSL );
#ifdef FOSSIL_ENABLE_JSON
if( nCycles==0 ){ json_bootstrap_early(); }
#endif
if( zIpAddr ){
|
| ︙ | ︙ | |||
2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 |
cgi_trace(zLine);
zToken = extract_token(zLine, &z);
if( zToken==0 ){
malformed_request("malformed HTTP header");
}
}
if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0
&& fossil_strcmp(zToken,"HEAD")!=0 ){
malformed_request("unsupported HTTP method");
}
if( nCycles==0 ){
cgi_setenv("GATEWAY_INTERFACE","CGI/1.0");
cgi_setenv("REQUEST_METHOD",zToken);
}
zToken = extract_token(z, &z);
if( zToken==0 ){
malformed_request("malformed URL in HTTP header");
}
if( nCycles==0 ){
cgi_setenv("REQUEST_URI", zToken);
cgi_setenv("SCRIPT_NAME", "");
}
for(i=0; zToken[i] && zToken[i]!='?'; i++){}
if( zToken[i] ) zToken[i++] = 0;
if( nCycles==0 ){
cgi_setenv("PATH_INFO", zToken);
}else{
cgi_replace_parameter("PATH_INFO", fossil_strdup(zToken));
}
/* Get all the optional fields that follow the first line.
*/
while( fgets(zLine,sizeof(zLine),g.httpIn) ){
char *zFieldName;
char *zVal;
cgi_trace(zLine);
zFieldName = extract_token(zLine,&zVal);
if( zFieldName==0 || *zFieldName==0 ) break;
while( fossil_isspace(*zVal) ){ zVal++; }
i = strlen(zVal);
while( i>0 && fossil_isspace(zVal[i-1]) ){ i--; }
zVal[i] = 0;
for(i=0; zFieldName[i]; i++){
zFieldName[i] = fossil_tolower(zFieldName[i]);
}
if( fossil_strcmp(zFieldName,"content-length:")==0 ){
| > > > > > > > > > > > > > > | > > > > | > | 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 |
cgi_trace(zLine);
zToken = extract_token(zLine, &z);
if( zToken==0 ){
malformed_request("malformed HTTP header");
}
}
zMethod = fossil_strdup(zToken);
if( fossil_strcmp(zToken,"GET")!=0 && fossil_strcmp(zToken,"POST")!=0
&& fossil_strcmp(zToken,"HEAD")!=0 ){
malformed_request("unsupported HTTP method");
}
if( nCycles==0 ){
cgi_setenv("GATEWAY_INTERFACE","CGI/1.0");
cgi_setenv("REQUEST_METHOD",zToken);
}
zToken = extract_token(z, &z);
if( zToken==0 ){
malformed_request("malformed URL in HTTP header");
}
n = strlen(g.zRepositoryName);
if( fossil_strncmp(g.zRepositoryName, zToken, n)==0
&& (zToken[n]=='/' || zToken[n]==0)
&& fossil_strcmp(zMethod,"GET")==0
){
zToken += n;
if( zToken && strlen(zToken)==0 ){
malformed_request("malformed URL in HTTP header");
}
}
if( nCycles==0 ){
cgi_setenv("REQUEST_URI", zToken);
cgi_setenv("SCRIPT_NAME", "");
}
for(i=0; zToken[i] && zToken[i]!='?'; i++){}
if( zToken[i] ) zToken[i++] = 0;
if( nCycles==0 ){
cgi_setenv("PATH_INFO", zToken);
cgi_setenv("QUERY_STRING",&zToken[i]);
}else{
cgi_replace_parameter("PATH_INFO", fossil_strdup(zToken));
cgi_replace_parameter("QUERY_STRING",fossil_strdup(&zToken[i]));
}
/* Get all the optional fields that follow the first line.
*/
while( fgets(zLine,sizeof(zLine),g.httpIn) ){
char *zFieldName;
char *zVal;
cgi_trace(zLine);
zFieldName = extract_token(zLine,&zVal);
if( zFieldName==0 || *zFieldName==0 ) break;
while( fossil_isspace(*zVal) ){ zVal++; }
i = strlen(zVal);
while( i>0 && fossil_isspace(zVal[i-1]) ){ i--; }
zVal[i] = 0;
for(i=0; zFieldName[i]; i++){
zFieldName[i] = fossil_tolower(zFieldName[i]);
}
if( fossil_strcmp(zFieldName,"content-length:")==0 ){
if( nCycles==0 ){
cgi_setenv("CONTENT_LENGTH", zVal);
}else{
cgi_replace_parameter("CONTENT_LENGTH", zVal);
}
}else if( fossil_strcmp(zFieldName,"content-type:")==0 ){
if( nCycles==0 ){
cgi_setenv("CONTENT_TYPE", zVal);
}
}else if( fossil_strcmp(zFieldName,"host:")==0 ){
if( nCycles==0 ){
cgi_setenv("HTTP_HOST", zVal);
}
}else if( fossil_strcmp(zFieldName,"user-agent:")==0 ){
if( nCycles==0 ){
cgi_setenv("HTTP_USER_AGENT", zVal);
|
| ︙ | ︙ | |||
2379 2380 2381 2382 2383 2384 2385 |
cgi_replace_parameter("REMOTE_ADDR", "127.0.0.1");
}
}
cgi_reset_content();
cgi_destination(CGI_BODY);
| < | < < < < < < < < < | 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 |
cgi_replace_parameter("REMOTE_ADDR", "127.0.0.1");
}
}
cgi_reset_content();
cgi_destination(CGI_BODY);
cgi_init();
cgi_trace(0);
nCycles++;
}
/*
** This routine handles the old fossil SSH probes
*/
|
| ︙ | ︙ |
Changes to src/chat.c.
| ︙ | ︙ | |||
387 388 389 390 391 392 393 394 395 396 397 398 399 400 |
if(fAsMessageList){
CX("}]}");
}else{
CX("}");
}
fossil_free(zTime);
}
/*
** WEBPAGE: chat-send hidden loadavg-exempt
**
** This page receives (via XHR) a new chat-message and/or a new file
** to be entered into the chat history.
**
| > > > > > > > > > > > > > > > | 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 |
if(fAsMessageList){
CX("}]}");
}else{
CX("}");
}
fossil_free(zTime);
}
/*
** Like chat_emit_permissions_error() but emits a single
** /chat-message-format JSON object about a CSRF violation.
*/
static void chat_emit_csrf_error(void){
char * zTime = cgi_iso8601_datestamp();
cgi_set_content_type("application/json");
CX("{");
CX("\"isError\": true, \"xfrom\": null,");
CX("\"mtime\": %!j, \"lmtime\": %!j,", zTime, zTime);
CX("\"xmsg\": \"CSRF validation failure.\"");
CX("}");
fossil_free(zTime);
}
/*
** WEBPAGE: chat-send hidden loadavg-exempt
**
** This page receives (via XHR) a new chat-message and/or a new file
** to be entered into the chat history.
**
|
| ︙ | ︙ | |||
419 420 421 422 423 424 425 426 427 428 429 430 431 432 |
void chat_send_webpage(void){
int nByte;
const char *zMsg;
const char *zUserName;
login_check_credentials();
if( 0==g.perm.Chat ) {
chat_emit_permissions_error(0);
return;
}
zUserName = (g.zLogin && g.zLogin[0]) ? g.zLogin : "nobody";
nByte = atoi(PD("file:bytes","0"));
zMsg = PD("msg","");
db_begin_write();
db_unprotect(PROTECT_READONLY);
| > > > | 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 |
void chat_send_webpage(void){
int nByte;
const char *zMsg;
const char *zUserName;
login_check_credentials();
if( 0==g.perm.Chat ) {
chat_emit_permissions_error(0);
return;
}else if( 0==cgi_csrf_safe(1) ){
chat_emit_csrf_error();
return;
}
zUserName = (g.zLogin && g.zLogin[0]) ? g.zLogin : "nobody";
nByte = atoi(PD("file:bytes","0"));
zMsg = PD("msg","");
db_begin_write();
db_unprotect(PROTECT_READONLY);
|
| ︙ | ︙ |
Changes to src/checkin.c.
| ︙ | ︙ | |||
716 717 718 719 720 721 722 723 724 725 726 727 728 729 |
/*
** Take care of -r version of ls command
*/
static void ls_cmd_rev(
const char *zRev, /* Revision string given */
int verboseFlag, /* Verbose flag given */
int showAge, /* Age flag given */
int timeOrder, /* Order by time flag given */
int treeFmt /* Show output in the tree format */
){
Stmt q;
char *zOrderBy = "pathname COLLATE nocase";
char *zName;
Blob where;
| > | 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 |
/*
** Take care of -r version of ls command
*/
static void ls_cmd_rev(
const char *zRev, /* Revision string given */
int verboseFlag, /* Verbose flag given */
int showAge, /* Age flag given */
int showHash, /* Show hash flag given */
int timeOrder, /* Order by time flag given */
int treeFmt /* Show output in the tree format */
){
Stmt q;
char *zOrderBy = "pathname COLLATE nocase";
char *zName;
Blob where;
|
| ︙ | ︙ | |||
761 762 763 764 765 766 767 |
if( timeOrder ){
zOrderBy = "mtime DESC";
}
compute_fileage(rid,0);
db_prepare(&q,
"SELECT datetime(fileage.mtime, toLocal()), fileage.pathname,\n"
| | > > > > | > | 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 |
if( timeOrder ){
zOrderBy = "mtime DESC";
}
compute_fileage(rid,0);
db_prepare(&q,
"SELECT datetime(fileage.mtime, toLocal()), fileage.pathname,\n"
" blob.size, fileage.uuid\n"
" FROM fileage, blob\n"
" WHERE blob.rid=fileage.fid %s\n"
" ORDER BY %s;", blob_sql_text(&where), zOrderBy /*safe-for-%s*/
);
blob_reset(&where);
if( treeFmt ) blob_init(&out, 0, 0);
while( db_step(&q)==SQLITE_ROW ){
const char *zTime = db_column_text(&q,0);
const char *zFile = db_column_text(&q,1);
int size = db_column_int(&q,2);
if( treeFmt ){
blob_appendf(&out, "%s\n", zFile);
}else if( verboseFlag ){
if( showHash ){
const char *zUuid = db_column_text(&q,3);
fossil_print("%s %7d [%S] %s\n", zTime, size, zUuid, zFile);
}else{
fossil_print("%s %7d %s\n", zTime, size, zFile);
}
}else if( showAge ){
fossil_print("%s %s\n", zTime, zFile);
}else{
fossil_print("%s\n", zFile);
}
}
db_finalize(&q);
|
| ︙ | ︙ | |||
809 810 811 812 813 814 815 | ** option as well, as explained below. ** ** The --age option displays file commit times. Like -r, --age has the ** side effect of making -t sort by commit time, not modification time. ** ** The -v option provides extra information about each file. Without -r, ** -v displays the change status, in the manner of the changes command. | | > > | 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 | ** option as well, as explained below. ** ** The --age option displays file commit times. Like -r, --age has the ** side effect of making -t sort by commit time, not modification time. ** ** The -v option provides extra information about each file. Without -r, ** -v displays the change status, in the manner of the changes command. ** With -r, -v shows the commit time and size of the checked-in files; in ** this combination, it additionally shows file hashes with -h. ** ** The -t option changes the sort order. Without -t, files are sorted by ** path and name (case insensitive sort if -r). If neither --age nor -r ** are used, -t sorts by modification time, otherwise by commit time. ** ** Options: ** --age Show when each file was committed ** -h With -v and -r, show file hashes ** --hash With -v, verify file status using hashing ** rather than relying on file sizes and mtimes ** -r VERSION The specific check-in to list ** -R|--repository REPO Extract info from repository REPO ** -t Sort output in time order ** --tree Tree format ** -v|--verbose Provide extra information about each file |
| ︙ | ︙ | |||
838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 |
int showAge;
int treeFmt;
int timeOrder;
char *zOrderBy = "pathname";
Blob where;
int i;
int useHash = 0;
const char *zName;
const char *zRev;
verboseFlag = find_option("verbose","v", 0)!=0;
if( !verboseFlag ){
verboseFlag = find_option("l","l", 0)!=0; /* deprecated */
}
showAge = find_option("age",0,0)!=0;
zRev = find_option("r","r",1);
timeOrder = find_option("t","t",0)!=0;
if( verboseFlag ){
useHash = find_option("hash",0,0)!=0;
}
treeFmt = find_option("tree",0,0)!=0;
if( treeFmt ){
if( zRev==0 ) zRev = "current";
}
if( zRev!=0 ){
db_find_and_open_repository(0, 0);
verify_all_options();
| > > | | 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 |
int showAge;
int treeFmt;
int timeOrder;
char *zOrderBy = "pathname";
Blob where;
int i;
int useHash = 0;
int showHash = 0;
const char *zName;
const char *zRev;
verboseFlag = find_option("verbose","v", 0)!=0;
if( !verboseFlag ){
verboseFlag = find_option("l","l", 0)!=0; /* deprecated */
}
showAge = find_option("age",0,0)!=0;
zRev = find_option("r","r",1);
timeOrder = find_option("t","t",0)!=0;
if( verboseFlag ){
useHash = find_option("hash",0,0)!=0;
showHash = find_option("h","h",0)!=0;
}
treeFmt = find_option("tree",0,0)!=0;
if( treeFmt ){
if( zRev==0 ) zRev = "current";
}
if( zRev!=0 ){
db_find_and_open_repository(0, 0);
verify_all_options();
ls_cmd_rev(zRev,verboseFlag,showAge,showHash,timeOrder,treeFmt);
return;
}else if( find_option("R",0,1)!=0 ){
fossil_fatal("the -r is required in addition to -R");
}
db_must_be_within_tree();
vid = db_lget_int("checkout", 0);
|
| ︙ | ︙ | |||
982 983 984 985 986 987 988 |
void tree_cmd(void){
const char *zRev;
zRev = find_option("r","r",1);
if( zRev==0 ) zRev = "current";
db_find_and_open_repository(0, 0);
verify_all_options();
| | | 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 |
void tree_cmd(void){
const char *zRev;
zRev = find_option("r","r",1);
if( zRev==0 ) zRev = "current";
db_find_and_open_repository(0, 0);
verify_all_options();
ls_cmd_rev(zRev,0,0,0,0,1);
}
/*
** COMMAND: extras
**
** Usage: %fossil extras ?OPTIONS? ?PATH1 ...?
**
|
| ︙ | ︙ | |||
1500 1501 1502 1503 1504 1505 1506 |
}
blob_append(&prompt,
"# * All other text will be displayed as written\n", -1);
}else{
blob_append(&prompt,
"# * Hyperlinks: [target] or [target|display-text]\n"
"# * Blank lines cause a paragraph break\n"
| | | 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 |
}
blob_append(&prompt,
"# * All other text will be displayed as written\n", -1);
}else{
blob_append(&prompt,
"# * Hyperlinks: [target] or [target|display-text]\n"
"# * Blank lines cause a paragraph break\n"
"# * Other text rendered as if it were HTML\n", -1
);
}
blob_append(&prompt, "#\n", 2);
if( dryRunFlag ){
blob_appendf(&prompt, "# DRY-RUN: This is a test commit. No changes "
"will be made to the repository\n#\n");
|
| ︙ | ︙ |
Changes to src/checkout.c.
| ︙ | ︙ | |||
17 18 19 20 21 22 23 24 25 26 27 28 29 30 | ** ** This file contains code used to check-out versions of the project ** from the local repository. */ #include "config.h" #include "checkout.h" #include <assert.h> /* ** Check to see if there is an existing check-out that has been ** modified. Return values: ** ** 0: There is an existing check-out but it is unmodified ** 1: There is a modified check-out - there are unsaved changes | > | 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | ** ** This file contains code used to check-out versions of the project ** from the local repository. */ #include "config.h" #include "checkout.h" #include <assert.h> #include <zlib.h> /* ** Check to see if there is an existing check-out that has been ** modified. Return values: ** ** 0: There is an existing check-out but it is unmodified ** 1: There is a modified check-out - there are unsaved changes |
| ︙ | ︙ | |||
427 428 429 430 431 432 433 |
if( db_is_writeable("repository") ){
db_unset_mprintf(1, "ckout:%q", g.zLocalRoot);
}
unlink_local_database(1);
db_close(1);
unlink_local_database(0);
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 |
if( db_is_writeable("repository") ){
db_unset_mprintf(1, "ckout:%q", g.zLocalRoot);
}
unlink_local_database(1);
db_close(1);
unlink_local_database(0);
}
/*
** COMMAND: get
**
** Usage: %fossil get URL ?VERSION? ?OPTIONS?
**
** Download a single check-in from a remote repository named URL and
** unpack all of the files locally. The check-in is identified by VERSION.
**
** URL can be a traditional URL like one of:
**
** * https://domain.com/project
** * ssh://my-server/project.fossil
** * file:/home/user/Fossils/project.fossil
**
** Or URL can be just the name of a local repository without the "file:"
** prefix.
**
** This command works by downloading an SQL archive of the requested
** check-in and then extracting all the files from the archive.
**
** Options:
** --dest DIRECTORY Extract files into DIRECTORY. Use "--dest ."
** to extract into the local directory.
**
** -f|--force Overwrite existing files
**
** --list List all the files that would have been checked
** out but do not actually write anything to the
** filesystem.
**
** --sqlar ARCHIVE Store the check-out in an SQL-archive rather
** than unpacking them into separate files.
**
** -v|--verbose Show all files as they are extracted
*/
void get_cmd(void){
int forceFlag = find_option("force","f",0)!=0;
int bVerbose = find_option("verbose","v",0)!=0;
int bQuiet = find_option("quiet","q",0)!=0;
int bDebug = find_option("debug",0,0)!=0;
int bList = find_option("list",0,0)!=0;
const char *zSqlArchive = find_option("sqlar",0,1);
const char *z;
char *zDest = 0; /* Where to store results */
char *zSql; /* SQL used to query the results */
const char *zUrl; /* Url to get */
const char *zVers; /* Version name to get */
unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
Blob in, out; /* I/O for the HTTP request */
Blob file; /* A file to extract */
sqlite3 *db; /* Database containing downloaded sqlar */
sqlite3_stmt *pStmt; /* Statement for querying the database */
int rc; /* Result of subroutine calls */
int nFile = 0; /* Number of files written */
int nDir = 0; /* Number of directories written */
i64 nByte = 0; /* Number of bytes written */
z = find_option("dest",0,1);
if( z ) zDest = fossil_strdup(z);
verify_all_options();
if( g.argc<3 || g.argc>4 ){
usage("URL ?VERSION? ?OPTIONS?");
}
zUrl = g.argv[2];
zVers = g.argc==4 ? g.argv[3] : "trunk";
/* Parse the URL of the repository */
url_parse(zUrl, 0);
/* Construct an appropriate name for the destination directory */
if( zDest==0 ){
int i;
const char *zTail;
const char *zDot;
int n;
if( g.url.isFile ){
zTail = file_tail(g.url.name);
}else{
zTail = file_tail(g.url.path);
}
zDot = strchr(zTail,'.');
if( zDot==0 ) zDot = zTail+strlen(zTail);
n = (int)(zDot - zTail);
zDest = mprintf("%.*s-%s", n, zTail, zVers);
for(i=0; zDest[i]; i++){
char c = zDest[i];
if( !fossil_isalnum(c) && c!='-' && c!='^' && c!='~' && c!='_' ){
zDest[i] = '-';
}
}
}
if( bDebug ){
fossil_print("dest = %s\n", zDest);
}
/* Error checking */
if( zDest!=file_tail(zDest) ){
fossil_fatal("--dest must be a simple directory name, not a path");
}
if( zVers!=file_tail(zVers) ){
fossil_fatal("The \"fossil get\" command does not currently work with"
" version names that contain \"/\". This will be fixed in"
" a future release.");
}
/* To relax the restrictions above, change the subpath URL formula below
** to use query parameters. Ex: /sqlar?r=%t&name=%t */
if( !forceFlag ){
if( zSqlArchive ){
if( file_isdir(zSqlArchive, ExtFILE)>0 ){
fossil_fatal("file already exists: \"%s\"", zSqlArchive);
}
}else if( file_isdir(zDest, ExtFILE)>0 ){
if( fossil_strcmp(zDest,".")==0 ){
if( file_directory_size(zDest,0,1) ){
fossil_fatal("current directory is not empty");
}
}else{
fossil_fatal("\"%s\" already exists", zDest);
}
}
}
/* Construct a subpath on the URL if necessary */
if( g.url.isFile ){
g.url.subpath = mprintf("/sqlar/%t/%t.sqlar", zVers, zDest);
}else{
g.url.subpath = mprintf("%s/sqlar/%t/%t.sqlar", g.url.path, zVers, zDest);
}
if( bDebug ){
urlparse_print(0);
}
/* Fetch the ZIP archive for the requested check-in */
blob_init(&in, 0, 0);
blob_init(&out, 0, 0);
if( bDebug ) mHttpFlags |= HTTP_VERBOSE;
if( bQuiet ) mHttpFlags |= HTTP_QUIET;
rc = http_exchange(&in, &out, mHttpFlags, 4, 0);
if( rc
|| out.nUsed<512
|| (out.nUsed%512)!=0
|| memcmp(out.aData,"SQLite format 3",16)!=0
){
fossil_fatal("Server did not return the requested check-in.");
}
if( zSqlArchive ){
blob_write_to_file(&out, zSqlArchive);
if( bVerbose ) fossil_print("%s\n", zSqlArchive);
return;
}
rc = sqlite3_open(":memory:", &db);
if( rc==SQLITE_OK ){
int sz = blob_size(&out);
rc = sqlite3_deserialize(db, 0, (unsigned char*)blob_buffer(&out), sz, sz,
SQLITE_DESERIALIZE_READONLY);
}
if( rc!=SQLITE_OK ){
fossil_fatal("Cannot create an in-memory database: %s",
sqlite3_errmsg(db));
}
zSql = mprintf("SELECT name, mode, sz, data FROM sqlar"
" WHERE name GLOB '%q*'", zDest);
rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
fossil_free(zSql);
if( rc!=0 ){
fossil_fatal("SQL error: %s\n", sqlite3_errmsg(db));
}
blob_init(&file, 0, 0);
while( sqlite3_step(pStmt)==SQLITE_ROW ){
const char *zFilename = (const char*)sqlite3_column_text(pStmt, 0);
int mode = sqlite3_column_int(pStmt, 1);
int sz = sqlite3_column_int(pStmt, 2);
if( bList ){
fossil_print("%s\n", zFilename);
}else if( mode & 0x4000 ){
/* A directory name */
nDir++;
file_mkdir(zFilename, ExtFILE, 1);
}else{
/* A file */
unsigned char *inBuf = (unsigned char*)sqlite3_column_blob(pStmt,3);
unsigned int nIn = (unsigned int)sqlite3_column_bytes(pStmt,3);
unsigned long int nOut2 = (unsigned long int)sz;
nFile++;
nByte += sz;
blob_resize(&file, sz);
if( nIn<sz ){
rc = uncompress((unsigned char*)blob_buffer(&file), &nOut2,
inBuf, nIn);
if( rc!=Z_OK ){
fossil_fatal("Failed to uncompress file %s", zFilename);
}
}else{
memcpy(blob_buffer(&file), inBuf, sz);
}
blob_write_to_file(&file, zFilename);
if( mode & 0x40 ){
file_setexe(zFilename, 1);
}
blob_zero(&file);
if( bVerbose ){
fossil_print("%s\n", zFilename);
}
}
}
sqlite3_finalize(pStmt);
sqlite3_close(db);
blob_zero(&out);
if( !bVerbose && !bQuiet && nFile>0 && zDest ){
fossil_print("%d files (%,lld bytes) written into %s",
nFile, nByte, zDest);
if( nDir>1 ){
fossil_print(" and %d subdirectories\n", nDir-1);
}else{
fossil_print("\n");
}
}
}
|
Changes to src/clearsign.c.
| ︙ | ︙ | |||
63 64 65 66 67 68 69 |
blob_read_from_file(&tmpBlob, zOut, ExtFILE);
/* Add armor header line and manifest */
blob_appendf(pOut, "%s", "-----BEGIN SSH SIGNED MESSAGE-----\n\n");
blob_appendf(pOut, "%s", blob_str(&tmpBlob));
blob_zero(&tmpBlob);
blob_read_from_file(&tmpBlob, zIn, ExtFILE);
/* Add signature - already armored by SSH */
| | | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
blob_read_from_file(&tmpBlob, zOut, ExtFILE);
/* Add armor header line and manifest */
blob_appendf(pOut, "%s", "-----BEGIN SSH SIGNED MESSAGE-----\n\n");
blob_appendf(pOut, "%s", blob_str(&tmpBlob));
blob_zero(&tmpBlob);
blob_read_from_file(&tmpBlob, zIn, ExtFILE);
/* Add signature - already armored by SSH */
blob_appendb(pOut, &tmpBlob);
}else{
/* Assume that the external command creates non-detached signatures */
blob_read_from_file(pOut, zIn, ExtFILE);
}
}else{
if( pOut!=pIn ){
blob_copy(pOut, pIn);
|
| ︙ | ︙ |
Changes to src/clone.c.
| ︙ | ︙ | |||
403 404 405 406 407 408 409 |
db_unprotect(PROTECT_ALL);
db_set("ssh-command", g.zSshCmd, 0);
db_protect_pop();
}
}
/*
| | < | | | < < < < < < < < < < < < < < < < < < < < < | > > | 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 |
db_unprotect(PROTECT_ALL);
db_set("ssh-command", g.zSshCmd, 0);
db_protect_pop();
}
}
/*
** WEBPAGE: howtoclone
**
** Provide instructions on how to clone this repository.
*/
void howtoclone_page(void){
login_check_credentials();
cgi_check_for_malice();
style_header("How To Clone This Repository");
if( !g.perm.Clone ){
@ <p>You are not authorized to clone this repository.
if( g.zLogin==0 || g.zLogin[0]==0 ){
@ Maybe you would be able to clone if you
@ %z(href("%R/login"))logged in</a>.
}else{
@ Contact the site administrator and ask them to give
@ you "Clone" privileges in order to clone.
}
}else{
const char *zNm = db_get("short-project-name","clone");
@ <p>Clone this repository by running a command like the following:
@ <blockquote><pre>
@ fossil clone %s(g.zBaseURL) %h(zNm).fossil
@ </pre></blockquote>
@ <p>Do a web search for "fossil clone" or similar to find additional
@ information about using a cloned Fossil repository.
}
style_finish_page();
}
|
Changes to src/comformat.c.
| ︙ | ︙ | |||
301 302 303 304 305 306 307 |
}
switch( cchUTF8 ){
case 4:
*pUtf32 =
( (z[0] & 0x0f)<<18 ) |
( (z[1] & 0x3f)<<12 ) |
( (z[2] & 0x3f)<< 6 ) |
| | | 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 |
}
switch( cchUTF8 ){
case 4:
*pUtf32 =
( (z[0] & 0x0f)<<18 ) |
( (z[1] & 0x3f)<<12 ) |
( (z[2] & 0x3f)<< 6 ) |
( (z[3] & 0x3f)<< 0 ) ;
break;
case 3:
*pUtf32 =
( (z[0] & 0x0f)<<12 ) |
( (z[1] & 0x3f)<< 6 ) |
( (z[2] & 0x3f)<< 0 ) ;
break;
|
| ︙ | ︙ |
Changes to src/content.c.
| ︙ | ︙ | |||
598 599 600 601 602 603 604 605 606 607 608 609 610 611 |
"VALUES(%d,%d,'%q',:data)",
g.rcvid, size, blob_str(&hash)
);
db_bind_blob(&s1, ":data", &cmpr);
db_exec(&s1);
rid = db_last_insert_rowid();
if( !pBlob ){
db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
}
}
if( g.markPrivate || isPrivate ){
db_multi_exec("INSERT OR IGNORE INTO private VALUES(%d)", rid);
markAsUnclustered = 0;
}
| > | 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 |
"VALUES(%d,%d,'%q',:data)",
g.rcvid, size, blob_str(&hash)
);
db_bind_blob(&s1, ":data", &cmpr);
db_exec(&s1);
rid = db_last_insert_rowid();
if( !pBlob ){
assert(!"cannot happen: pBlob is always non-NULL");
db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
}
}
if( g.markPrivate || isPrivate ){
db_multi_exec("INSERT OR IGNORE INTO private VALUES(%d)", rid);
markAsUnclustered = 0;
}
|
| ︙ | ︙ | |||
861 862 863 864 865 866 867 |
/* Loop over all candidate delta sources */
for(i=0; i<nSrc; i++){
int srcid = aSrc[i];
if( srcid==rid ) continue;
if( content_is_private(srcid) && !content_is_private(rid) ) continue;
/* Compute all ancestors of srcid and make sure rid is not one of them.
| | | 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 |
/* Loop over all candidate delta sources */
for(i=0; i<nSrc; i++){
int srcid = aSrc[i];
if( srcid==rid ) continue;
if( content_is_private(srcid) && !content_is_private(rid) ) continue;
/* Compute all ancestors of srcid and make sure rid is not one of them.
** If rid is an ancestor of srcid, then making rid a descendent of srcid
** would create a delta loop. */
s = srcid;
while( (s = delta_source_rid(s))>0 ){
if( s==rid ){
content_undelta(srcid);
break;
}
|
| ︙ | ︙ |
Changes to src/cookies.c.
| ︙ | ︙ | |||
254 255 256 257 258 259 260 |
for(i=0; cgi_param_info(i, &zName, &zValue, &isQP); i++){
char *zDel;
if( isQP ) continue;
if( fossil_isupper(zName[0]) ) continue;
if( bFDSonly && strcmp(zName, "fossil_display_settings")!=0 ) continue;
zDel = mprintf("del%s",zName);
if( P(zDel)!=0 ){
| > > | | 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
for(i=0; cgi_param_info(i, &zName, &zValue, &isQP); i++){
char *zDel;
if( isQP ) continue;
if( fossil_isupper(zName[0]) ) continue;
if( bFDSonly && strcmp(zName, "fossil_display_settings")!=0 ) continue;
zDel = mprintf("del%s",zName);
if( P(zDel)!=0 ){
const char *zPath = fossil_strcmp(ROBOT_COOKIE,zName)==0
? "/" : 0;
cgi_set_cookie(zName, "", zPath, -1);
cgi_redirect(g.zPath);
}
nCookie++;
@ <li><p><b>%h(zName)</b>: %h(zValue)
@ <input type="submit" name="%h(zDel)" value="Delete">
if( fossil_strcmp(zName, DISPLAY_SETTINGS_COOKIE)==0 && cookies.nParam>0 ){
int j;
|
| ︙ | ︙ | |||
280 281 282 283 284 285 286 287 288 289 290 291 292 293 |
if( fossil_strncmp(zName, "fossil-", 7)==0
&& strlen(zName)==23
&& hex_prefix_length(&zName[7])==16
&& hex_prefix_length(zValue)>24
){
@ <p>This appears to be a login cookie for another Fossil repository
@ in the same website.
}
else {
@ <p>This cookie was not generated by Fossil. It might be something
@ from another program on the same website.
}
fossil_free(zDel);
}
| > > > > | 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
if( fossil_strncmp(zName, "fossil-", 7)==0
&& strlen(zName)==23
&& hex_prefix_length(&zName[7])==16
&& hex_prefix_length(zValue)>24
){
@ <p>This appears to be a login cookie for another Fossil repository
@ in the same website.
}else
if( fossil_strcmp(zName, ROBOT_COOKIE)==0 ){
@ <p>This cookie shows that your web-browser has been tested is
@ believed to be operated by a human, not a robot.
}
else {
@ <p>This cookie was not generated by Fossil. It might be something
@ from another program on the same website.
}
fossil_free(zDel);
}
|
| ︙ | ︙ |
Changes to src/copybtn.js.
1 2 3 | /* Manage "Copy Buttons" linked to target elements, to copy the text (or, parts ** thereof) of the target elements to the clipboard. ** | | | | < < < < | > > > > | | > > | < < > > > > > | < < < < < < < < | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
/* Manage "Copy Buttons" linked to target elements, to copy the text (or, parts
** thereof) of the target elements to the clipboard.
**
** Newly created buttons are <button> elements plus a nested <span> element with
** an SVG background icon, defined by the "copy-button" class in the default CSS
** style sheet, and are assigned the element ID "copy-<idTarget>".
**
** For HTML-defined buttons, either initCopyButtonById(), or initCopyButton(),
** needs to be called to attach the "onclick" handler (done automatically from
** a handler attached to the "DOMContentLoaded" event). These functions create
** the nested <span> element if the <button> element has no child nodes. Using
** static HTML for the <span> element ensures the buttons are visible if there
** are script errors, which may be useful for Fossil JS hackers (as good parts
** of the Fossil web UI come down on JS errors, anyway).
**
** The initialization functions do not overwrite the "data-copytarget" and
** "data-copylength" attributes with empty or null values for <idTarget> and
** <cchLength>, respectively. Set <cchLength> to "-1" to explicitly remove the
** previous copy length limit.
**
** HTML snippet for statically created buttons:
**
** <button class="copy-button" id="copy-<idTarget>"
** data-copytarget="<idTarget>" data-copylength="<cchLength>">
** <span></span>
** </button>
*/
function makeCopyButton(idTarget,bFlipped,cchLength){
var elButton = document.createElement("button");
elButton.className = "copy-button";
if( bFlipped ) elButton.className += " copy-button-flipped";
elButton.id = "copy-" + idTarget;
initCopyButton(elButton,idTarget,cchLength);
return elButton;
}
function initCopyButtonById(idButton,idTarget,cchLength){
idButton = idButton || "copy-" + idTarget;
var elButton = document.getElementById(idButton);
if( elButton ) initCopyButton(elButton,idTarget,cchLength);
return elButton;
}
function initCopyButton(elButton,idTarget,cchLength){
if( idTarget ) elButton.setAttribute("data-copytarget",idTarget);
if( cchLength ) elButton.setAttribute("data-copylength",cchLength);
elButton.onclick = clickCopyButton;
/* Make sure the <button> contains a single nested <span>. */
if( elButton.childElementCount!=1 || elButton.firstChild.tagName!="SPAN" ){
while( elButton.firstChild ) elButton.removeChild(elButton.lastChild);
elButton.appendChild(document.createElement("span"));
}
return elButton;
}
setTimeout(function(){
var elButtons = document.getElementsByClassName("copy-button");
for ( var i=0; i<elButtons.length; i++ ){
initCopyButton(elButtons[i],0,0);
}
},1);
/* The onclick handler for the "Copy Button". */
function clickCopyButton(e){
e.preventDefault(); /* Mandatory for <a> and <button>. */
e.stopPropagation();
if( this.disabled ) return; /* This check is probably redundant. */
var idTarget = this.getAttribute("data-copytarget");
var elTarget = document.getElementById(idTarget);
if( elTarget ){
var text = elTarget.innerText.replace(/^\s+|\s+$/g,"");
var cchLength = parseInt(this.getAttribute("data-copylength"));
if( !isNaN(cchLength) && cchLength>0 ){
text = text.slice(0,cchLength); /* Assume single-byte chars. */
}
copyTextToClipboard(text);
}
}
/* Create a temporary <textarea> element and copy the contents to clipboard. */
function copyTextToClipboard(text){
if( window.clipboardData && window.clipboardData.setData ){
window.clipboardData.setData("Text",text);
}else{
var elTextarea = document.createElement("textarea");
|
| ︙ | ︙ |
Changes to src/db.c.
| ︙ | ︙ | |||
123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
}
else
#endif /* FOSSIL_ENABLE_JSON */
if( g.xferPanic && g.cgiOutput==1 ){
cgi_reset_content();
@ error Database\serror:\s%F(z)
cgi_reply();
}
fossil_fatal("Database error: %s", z);
}
/*
** Check a result code. If it is not SQLITE_OK, print the
** corresponding error message and exit.
| > > > > > > > > > > | 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
}
else
#endif /* FOSSIL_ENABLE_JSON */
if( g.xferPanic && g.cgiOutput==1 ){
cgi_reset_content();
@ error Database\serror:\s%F(z)
cgi_reply();
}
if( strstr(z,"attempt to write a readonly database") ){
static const char *azDbNames[] = { "repository", "localdb", "configdb" };
int i;
for(i=0; i<3; i++){
if( sqlite3_db_readonly(g.db, azDbNames[i])==1 ){
z = mprintf("\"%s\" is readonly.\n%s",
sqlite3_db_filename(g.db,azDbNames[i]), z);
}
}
}
fossil_fatal("Database error: %s", z);
}
/*
** Check a result code. If it is not SQLITE_OK, print the
** corresponding error message and exit.
|
| ︙ | ︙ | |||
1216 1217 1218 1219 1220 1221 1222 | } db_finalize(&s); } /* ** Execute a query. Return the first column of the first row ** of the result set as a string. Space to hold the string is | | | | 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 |
}
db_finalize(&s);
}
/*
** Execute a query. Return the first column of the first row
** of the result set as a string. Space to hold the string is
** obtained from fossil_strdup() and should be freed using fossil_free().
** If the result set is empty, return a copy of zDefault instead.
*/
char *db_text(const char *zDefault, const char *zSql, ...){
va_list ap;
Stmt s;
char *z;
va_start(ap, zSql);
db_vprepare(&s, 0, zSql, ap);
|
| ︙ | ︙ | |||
3587 3588 3589 3590 3591 3592 3593 |
return zOut;
}
/*
** Return true if the string zVal represents "true" (or "false").
*/
int is_truth(const char *zVal){
| | | | 3597 3598 3599 3600 3601 3602 3603 3604 3605 3606 3607 3608 3609 3610 3611 3612 3613 3614 3615 3616 |
return zOut;
}
/*
** Return true if the string zVal represents "true" (or "false").
*/
int is_truth(const char *zVal){
static const char *const azOn[] = { "on", "yes", "true" };
int i;
for(i=0; i<count(azOn); i++){
if( fossil_stricmp(zVal,azOn[i])==0 ) return 1;
}
return atoi(zVal);
}
int is_false(const char *zVal){
static const char *const azOff[] = { "off", "no", "false", "0" };
int i;
for(i=0; i<count(azOff); i++){
if( fossil_stricmp(zVal,azOff[i])==0 ) return 1;
}
|
| ︙ | ︙ | |||
4248 4249 4250 4251 4252 4253 4254 4255 4256 4257 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 |
** --setmtime Set timestamps of all files to match their SCM-side
** times (the timestamp of the last check-in which modified
** them).
** --verbose If passed a URI then this flag is passed on to the clone
** operation, otherwise it has no effect
** --workdir DIR Use DIR as the working directory instead of ".". The DIR
** directory is created if it does not exist.
**
** See also: [[close]], [[clone]]
*/
void cmd_open(void){
int emptyFlag;
int keepFlag;
int forceMissingFlag;
int allowNested;
int setmtimeFlag; /* --setmtime. Set mtimes on files */
int bForce = 0; /* --force. Open even if non-empty dir */
static char *azNewArgv[] = { 0, "checkout", "--prompt", 0, 0, 0, 0 };
const char *zWorkDir; /* --workdir value */
const char *zRepo = 0; /* Name of the repository file */
const char *zRepoDir = 0; /* --repodir value */
char *zPwd; /* Initial working directory */
int isUri = 0; /* True if REPOSITORY is a URI */
int nLocal; /* Number of preexisting files in cwd */
int bVerbose = 0; /* --verbose option for clone */
url_proxy_options();
emptyFlag = find_option("empty",0,0)!=0;
keepFlag = find_option("keep","k",0)!=0;
forceMissingFlag = find_option("force-missing",0,0)!=0;
allowNested = find_option("nested",0,0)!=0;
setmtimeFlag = find_option("setmtime",0,0)!=0;
| > > > > > > > > > > > > | 4258 4259 4260 4261 4262 4263 4264 4265 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 4283 4284 4285 4286 4287 4288 4289 4290 4291 4292 4293 4294 4295 4296 4297 4298 4299 4300 4301 |
** --setmtime Set timestamps of all files to match their SCM-side
** times (the timestamp of the last check-in which modified
** them).
** --verbose If passed a URI then this flag is passed on to the clone
** operation, otherwise it has no effect
** --workdir DIR Use DIR as the working directory instead of ".". The DIR
** directory is created if it does not exist.
** --reopen REPOFILE Changes the repository file used by the current checkout
** to REPOFILE. Use this after moving a checkout's
** repository. This may lose stash and bisect history.
**
** See also: [[close]], [[clone]]
*/
void cmd_open(void){
int emptyFlag;
int keepFlag;
int forceMissingFlag;
int allowNested;
int setmtimeFlag; /* --setmtime. Set mtimes on files */
int bForce = 0; /* --force. Open even if non-empty dir */
static char *azNewArgv[] = { 0, "checkout", "--prompt", 0, 0, 0, 0 };
const char *zWorkDir; /* --workdir value */
const char *zRepo = 0; /* Name of the repository file */
const char *zRepoDir = 0; /* --repodir value */
const char *zReopen = 0; /* --reopen REPOFILE */
char *zPwd; /* Initial working directory */
int isUri = 0; /* True if REPOSITORY is a URI */
int nLocal; /* Number of preexisting files in cwd */
int bVerbose = 0; /* --verbose option for clone */
zReopen = find_option("reopen",0,1);
if( 0!=zReopen ){
g.argc = 3;
g.argv[2] = (char*)zReopen;
move_repo_cmd();
return;
}
url_proxy_options();
emptyFlag = find_option("empty",0,0)!=0;
keepFlag = find_option("keep","k",0)!=0;
forceMissingFlag = find_option("force-missing",0,0)!=0;
allowNested = find_option("nested",0,0)!=0;
setmtimeFlag = find_option("setmtime",0,0)!=0;
|
| ︙ | ︙ |
Changes to src/default.css.
1 2 3 4 5 6 7 8 9 10 11 12 |
/* This CSS file holds the default implementations for all of fossil's
CSS classes. When /style.css is requested, the rules in this file
are emitted first, followed by (1) page-specific CSS (if any) and
(2) skin-specific CSS.
*/
div.sidebox {
float: right;
background-color: white;
border-width: medium;
border-style: double;
margin: 10px;
}
| > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/* This CSS file holds the default implementations for all of fossil's
CSS classes. When /style.css is requested, the rules in this file
are emitted first, followed by (1) page-specific CSS (if any) and
(2) skin-specific CSS.
*/
body {
z-index: 0 /* Used by robot.c:robot_proofofwork() and href.js */;
}
div.sidebox {
float: right;
background-color: white;
border-width: medium;
border-style: double;
margin: 10px;
}
|
| ︙ | ︙ | |||
52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
tr.timelineCurrent td {
border-radius: 0;
border-width: 0;
}
span.timelineLeaf {
font-weight: bold;
}
span.timelineHistDsp {
font-weight: bold;
}
td.timelineTime {
vertical-align: top;
text-align: right;
white-space: nowrap;
| > > > | 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
tr.timelineCurrent td {
border-radius: 0;
border-width: 0;
}
span.timelineLeaf {
font-weight: bold;
}
span.timelineHash {
font-weight: bold;
}
span.timelineHistDsp {
font-weight: bold;
}
td.timelineTime {
vertical-align: top;
text-align: right;
white-space: nowrap;
|
| ︙ | ︙ | |||
555 556 557 558 559 560 561 562 563 564 565 566 567 568 |
/* Rules governing diff layout and colors */
table.diff {
width: 100%;
border-spacing: 0;
border-radius: 5px;
border: 1px solid black;
font-size: 80%;
}
table.diff td.diffln{
padding: 0;
}
table.diff td.diffln > pre{
padding: 0 0.25em 0 0.5em;
| > | 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 |
/* Rules governing diff layout and colors */
table.diff {
width: 100%;
border-spacing: 0;
border-radius: 5px;
border: 1px solid black;
overflow: hidden; /* Prevent background from overlapping rounded borders. */
font-size: 80%;
}
table.diff td.diffln{
padding: 0;
}
table.diff td.diffln > pre{
padding: 0 0.25em 0 0.5em;
|
| ︙ | ︙ | |||
746 747 748 749 750 751 752 753 754 755 756 757 758 759 |
}
body.tkt div.content ol.tkt-changes > li:target > p > span {
border-bottom: 3px solid gold;
}
body.tkt div.content ol.tkt-changes > li:target > ol {
border-left: 1px solid gold;
}
body.cpage-ckout .file-change-line,
body.cpage-info .file-change-line,
body.cpage-vinfo .file-change-line,
body.cpage-ci .file-change-line,
body.cpage-vdiff .file-change-line {
margin-top: 16px;
margin-bottom: 16px;
| > > > > > > > > > > > > | 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 |
}
body.tkt div.content ol.tkt-changes > li:target > p > span {
border-bottom: 3px solid gold;
}
body.tkt div.content ol.tkt-changes > li:target > ol {
border-left: 1px solid gold;
}
body.tkt .tktCommentArea {
display: flex;
flex-direction: column;
}
body.tkt .newest-first-controls {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
}
body.tkt .tktCommentArea.reverse {
flex-direction: column-reverse;
}
body.cpage-ckout .file-change-line,
body.cpage-info .file-change-line,
body.cpage-vinfo .file-change-line,
body.cpage-ci .file-change-line,
body.cpage-vdiff .file-change-line {
margin-top: 16px;
margin-bottom: 16px;
|
| ︙ | ︙ | |||
1134 1135 1136 1137 1138 1139 1140 |
}
label {
white-space: nowrap;
}
label[for] {
cursor: pointer;
}
| | | > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < | 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 |
}
label {
white-space: nowrap;
}
label[for] {
cursor: pointer;
}
button.copy-button,
button.copy-button:hover,
button.copy-button:focus,
button.copy-button:active {
width: 14px;
height: 14px;
/*Note: .24em is slightly smaller than the average width of a normal space.*/
margin: -2px .24em 0 0;
padding: 0;
border: 0;
outline: 0;
background: none;
font-size: inherit; /* Required for horizontal spacing. */
vertical-align: middle;
user-select: none;
cursor: pointer;
}
button.copy-button-flipped,
button.copy-button-flipped:hover,
button.copy-button-flipped:focus,
button.copy-button-flipped:active {
margin: -2px 0 0 .24em;
}
button.copy-button span {
display: block;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
border: 0;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \
viewBox='0,0,14,14'%3E%3Cpath style='fill:black;opacity:0' \
d='M14,14H0V0h14v14z'/%3E%3Cpath style='fill:rgb(240,240,240)' \
d='M1,0h6.6l2,2h1l3.4,3.4v8.6h-10v-2h-3z'/%3E%3Cpath style='fill:rgb(64,64,64)' \
d='M2,1h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \
d='M3,2h3.6l2.4,2.4v5.6h-6z'/%3E%3Cpath style='fill:rgb(80,128,208)' \
d='M4,5h4v1h-4zm0,2h4v1h-4z'/%3E%3Cpath style='fill:rgb(64,64,64)' \
d='M5,3h5l3,3v7h-8z'/%3E%3Cpath style='fill:rgb(248,248,248)' \
d='M10,4.4v1.6h1.6zm-4,-0.6h3v3h-3zm0,3h6v5.4h-6z'/%3E%3Cpath style='fill:rgb(80,128,208)' \
d='M7,8h4v1h-4zm0,2h4v1h-4z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: center;
cursor: pointer;
}
button.copy-button:enabled:active span {
background-size: 90%;
}
button.copy-button:disabled span {
filter: grayscale(1);
opacity: 0.4;
}
.nobr {
white-space: nowrap;
}
.accordion {
cursor: pointer;
}
.accordion_btn {
|
| ︙ | ︙ |
Changes to src/deltafunc.c.
| ︙ | ︙ | |||
249 250 251 252 253 254 255 256 257 258 259 260 261 262 |
#define DELTAPARSEVTAB_A2 2
#define DELTAPARSEVTAB_DELTA 3
if( rc==SQLITE_OK ){
pNew = sqlite3_malloc64( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
}
return rc;
}
/*
** This method is the destructor for deltaparsevtab_vtab objects.
*/
| > | 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
#define DELTAPARSEVTAB_A2 2
#define DELTAPARSEVTAB_DELTA 3
if( rc==SQLITE_OK ){
pNew = sqlite3_malloc64( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
}
return rc;
}
/*
** This method is the destructor for deltaparsevtab_vtab objects.
*/
|
| ︙ | ︙ | |||
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 |
*/
static int deltaparsevtabNext(sqlite3_vtab_cursor *cur){
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
const char *z;
int i = 0;
pCur->iCursor = pCur->iNext;
z = pCur->aDelta + pCur->iCursor;
pCur->a1 = deltaGetInt(&z, &i);
switch( z[0] ){
case '@': {
z++;
pCur->a2 = deltaGetInt(&z, &i);
pCur->eOp = DELTAPARSE_OP_COPY;
pCur->iNext = (int)(&z[1] - pCur->aDelta);
break;
}
case ':': {
z++;
| > > > > > > > > > > | 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
*/
static int deltaparsevtabNext(sqlite3_vtab_cursor *cur){
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
const char *z;
int i = 0;
pCur->iCursor = pCur->iNext;
if( pCur->iCursor >= pCur->nDelta ){
pCur->eOp = DELTAPARSE_OP_ERROR;
pCur->iNext = pCur->nDelta;
return SQLITE_OK;
}
z = pCur->aDelta + pCur->iCursor;
pCur->a1 = deltaGetInt(&z, &i);
switch( z[0] ){
case '@': {
z++;
if( pCur->iNext>=pCur->nDelta ){
pCur->eOp = DELTAPARSE_OP_ERROR;
pCur->iNext = pCur->nDelta;
break;
}
pCur->a2 = deltaGetInt(&z, &i);
pCur->eOp = DELTAPARSE_OP_COPY;
pCur->iNext = (int)(&z[1] - pCur->aDelta);
break;
}
case ':': {
z++;
|
| ︙ | ︙ | |||
352 353 354 355 356 357 358 |
sqlite3_result_int(ctx, pCur->a1);
break;
}
case DELTAPARSEVTAB_A2: {
if( pCur->eOp==DELTAPARSE_OP_COPY ){
sqlite3_result_int(ctx, pCur->a2);
}else if( pCur->eOp==DELTAPARSE_OP_INSERT ){
| > > > | | > | 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 |
sqlite3_result_int(ctx, pCur->a1);
break;
}
case DELTAPARSEVTAB_A2: {
if( pCur->eOp==DELTAPARSE_OP_COPY ){
sqlite3_result_int(ctx, pCur->a2);
}else if( pCur->eOp==DELTAPARSE_OP_INSERT ){
if( pCur->a2 + pCur->a1 > pCur->nDelta ){
sqlite3_result_zeroblob(ctx, pCur->a1);
}else{
sqlite3_result_blob(ctx, pCur->aDelta+pCur->a2, pCur->a1,
SQLITE_TRANSIENT);
}
}
break;
}
case DELTAPARSEVTAB_DELTA: {
sqlite3_result_blob(ctx, pCur->aDelta, pCur->nDelta, SQLITE_TRANSIENT);
break;
}
|
| ︙ | ︙ | |||
381 382 383 384 385 386 387 |
/*
** Return TRUE if the cursor has been moved off of the last
** row of output.
*/
static int deltaparsevtabEof(sqlite3_vtab_cursor *cur){
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
| | | 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 |
/*
** Return TRUE if the cursor has been moved off of the last
** row of output.
*/
static int deltaparsevtabEof(sqlite3_vtab_cursor *cur){
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
return pCur->eOp==DELTAPARSE_OP_EOF || pCur->iCursor>=pCur->nDelta;
}
/*
** This method is called to "rewind" the deltaparsevtab_cursor object back
** to the first row of output. This method is always called at least
** once prior to any call to deltaparsevtabColumn() or deltaparsevtabRowid() or
** deltaparsevtabEof().
|
| ︙ | ︙ |
Changes to src/diff.c.
| ︙ | ︙ | |||
3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 |
Blob *pA_Blob, /* FROM file */
Blob *pB_Blob, /* TO file */
Blob *pOut, /* Write diff here if not NULL */
DiffConfig *pCfg /* Configuration options */
){
int ignoreWs; /* Ignore whitespace */
DContext c;
if( pCfg->diffFlags & DIFF_INVERT ){
Blob *pTemp = pA_Blob;
pA_Blob = pB_Blob;
pB_Blob = pTemp;
}
ignoreWs = (pCfg->diffFlags & DIFF_IGNORE_ALLWS)!=0;
| > | 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 |
Blob *pA_Blob, /* FROM file */
Blob *pB_Blob, /* TO file */
Blob *pOut, /* Write diff here if not NULL */
DiffConfig *pCfg /* Configuration options */
){
int ignoreWs; /* Ignore whitespace */
DContext c;
int nDel = 0, nIns = 0;
if( pCfg->diffFlags & DIFF_INVERT ){
Blob *pTemp = pA_Blob;
pA_Blob = pB_Blob;
pB_Blob = pTemp;
}
ignoreWs = (pCfg->diffFlags & DIFF_IGNORE_ALLWS)!=0;
|
| ︙ | ︙ | |||
3161 3162 3163 3164 3165 3166 3167 3168 |
c.aEdit[i] = sum;
for(k=0, sum=0; k<c.aEdit[i+1]; k++) sum += c.aFrom[iA++].n;
c.aEdit[i+1] = sum;
for(k=0, sum=0; k<c.aEdit[i+2]; k++) sum += c.aTo[iB++].n;
c.aEdit[i+2] = sum;
}
}
| < | | | | | | | | | | > > > > > > | 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 |
c.aEdit[i] = sum;
for(k=0, sum=0; k<c.aEdit[i+1]; k++) sum += c.aFrom[iA++].n;
c.aEdit[i+1] = sum;
for(k=0, sum=0; k<c.aEdit[i+2]; k++) sum += c.aTo[iB++].n;
c.aEdit[i+2] = sum;
}
}
if( pCfg->diffFlags & DIFF_NUMSTAT ){
int i;
for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
nDel += c.aEdit[i+1];
nIns += c.aEdit[i+2];
}
g.diffCnt[1] += nIns;
g.diffCnt[2] += nDel;
if( nIns+nDel ){
g.diffCnt[0]++;
}
}
if( pOut ){
if( pCfg->diffFlags & DIFF_NUMSTAT && !(pCfg->diffFlags & DIFF_HTML)){
if( nIns+nDel ){
if( !(pCfg->diffFlags & DIFF_BRIEF) ){
blob_appendf(pOut, "%10d %10d", nIns, nDel);
}
}
}else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){
const int *R = c.aEdit;
unsigned int r;
|
| ︙ | ︙ | |||
3378 3379 3380 3381 3382 3383 3384 |
return;
}
find_option("i",0,0);
find_option("v",0,0);
diff_options(&DCfg, 0, 0);
zRe = find_option("regexp","e",1);
if( zRe ){
| | | 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 |
return;
}
find_option("i",0,0);
find_option("v",0,0);
diff_options(&DCfg, 0, 0);
zRe = find_option("regexp","e",1);
if( zRe ){
const char *zErr = fossil_re_compile(&DCfg.pRe, zRe, 0);
if( zErr ) fossil_fatal("regex error: %s", zErr);
}
verify_all_options();
if( g.argc!=4 ) usage("FILE1 FILE2");
blob_zero(&out);
diff_begin(&DCfg);
diff_print_filenames(g.argv[2], g.argv[3], &DCfg, &out);
|
| ︙ | ︙ | |||
3421 3422 3423 3424 3425 3426 3427 |
return;
}
find_option("i",0,0);
find_option("v",0,0);
diff_options(&DCfg, 0, 0);
zRe = find_option("regexp","e",1);
if( zRe ){
| | | 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 |
return;
}
find_option("i",0,0);
find_option("v",0,0);
diff_options(&DCfg, 0, 0);
zRe = find_option("regexp","e",1);
if( zRe ){
const char *zErr = fossil_re_compile(&DCfg.pRe, zRe, 0);
if( zErr ) fossil_fatal("regex error: %s", zErr);
}
db_find_and_open_repository(0, 0);
verify_all_options();
if( g.argc!=4 ) usage("HASH1 HASH2");
blob_zero(&out);
diff_begin(&DCfg);
|
| ︙ | ︙ | |||
3779 3780 3781 3782 3783 3784 3785 | HQuery url; struct AnnVers *p; unsigned clr1, clr2, clr; int bBlame = g.zPath[0]!='a';/* True for BLAME output. False for ANNOTATE. */ /* Gather query parameters */ login_check_credentials(); | | | | 3785 3786 3787 3788 3789 3790 3791 3792 3793 3794 3795 3796 3797 3798 3799 3800 |
HQuery url;
struct AnnVers *p;
unsigned clr1, clr2, clr;
int bBlame = g.zPath[0]!='a';/* True for BLAME output. False for ANNOTATE. */
/* Gather query parameters */
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
if( robot_restrict("annotate") ) return;
fossil_nice_default();
zFilename = P("filename");
zRevision = PD("checkin",0);
zOrigin = P("origin");
zLimit = P("limit");
showLog = PB("log");
fileVers = PB("filevers");
|
| ︙ | ︙ |
Changes to src/diff.tcl.
| ︙ | ︙ | |||
279 280 281 282 283 284 285 |
foreach c [cols] {
set type [colType $c]
if {$type ne "txt"} {
$c config -width $widths($type)
}
$c config -state disabled
}
| < | > | 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 |
foreach c [cols] {
set type [colType $c]
if {$type ne "txt"} {
$c config -width $widths($type)
}
$c config -state disabled
}
.wfiles.lb config -height $nDiffs
if {$nDiffs <= [.wfiles.lb cget -height]} {
grid remove .wfiles.sb
}
return $nDiffs
}
proc viewDiff {idx} {
|
| ︙ | ︙ |
Changes to src/diffcmd.c.
| ︙ | ︙ | |||
1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 |
const char *zFrom = P("from");
const char *zTo = P("to");
DiffConfig DCfg;
cgi_check_for_malice();
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
if( zFrom==0 || zTo==0 ) fossil_redirect_home();
fossil_nice_default();
cgi_set_content_type("text/plain");
diff_config_init(&DCfg, DIFF_VERBOSE);
diff_two_versions(zFrom, zTo, &DCfg, 0);
}
| > | 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 |
const char *zFrom = P("from");
const char *zTo = P("to");
DiffConfig DCfg;
cgi_check_for_malice();
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
if( zFrom==0 || zTo==0 ) fossil_redirect_home();
if( robot_restrict("diff") ) return;
fossil_nice_default();
cgi_set_content_type("text/plain");
diff_config_init(&DCfg, DIFF_VERBOSE);
diff_two_versions(zFrom, zTo, &DCfg, 0);
}
|
Changes to src/dispatch.c.
| ︙ | ︙ | |||
57 58 59 60 61 62 63 | #define CMDFLAG_ALIAS 0x2000 /* Command aliases */ #define CMDFLAG_KEEPEMPTY 0x4000 /* Do not unset empty settings */ #define CMDFLAG_ABBREVSUBCMD 0x8000 /* Help text abbreviates subcommands */ /**************************************************************************/ /* Values for the 2nd parameter to dispatch_name_search() */ #define CMDFLAG_ANY 0x0038 /* Match anything */ | | | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 | #define CMDFLAG_ALIAS 0x2000 /* Command aliases */ #define CMDFLAG_KEEPEMPTY 0x4000 /* Do not unset empty settings */ #define CMDFLAG_ABBREVSUBCMD 0x8000 /* Help text abbreviates subcommands */ /**************************************************************************/ /* Values for the 2nd parameter to dispatch_name_search() */ #define CMDFLAG_ANY 0x0038 /* Match anything */ #define CMDFLAG_PREFIX 0x0200 /* Prefix match is OK */ #endif /* INTERFACE */ /* ** The page_index.h file contains the definition for aCommand[] - an array ** of CmdOrPage objects that defines all available commands and webpages ** known to Fossil. |
| ︙ | ︙ | |||
273 274 275 276 277 278 279 |
int j;
while( i<n ){
char c = z[i];
if( c=='[' && (j = help_is_link(z+i, n-i))>0 ){
if( i ) blob_append(pOut, z, i);
z += i+2;
n -= i+2;
| | | 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 |
int j;
while( i<n ){
char c = z[i];
if( c=='[' && (j = help_is_link(z+i, n-i))>0 ){
if( i ) blob_append(pOut, z, i);
z += i+2;
n -= i+2;
blob_appendf(pOut, "<a href='%R/help/%.*s'>%.*s</a>",
j-3, z, j-3, z);
z += j-1;
n -= j-1;
i = 0;
}else if( c=='%' && n-i>=7 && strncmp(z+i,"%fossil",7)==0 ){
if( i ) blob_append(pOut, z, i);
z += i+7;
|
| ︙ | ︙ | |||
833 834 835 836 837 838 839 |
if( 0==fossil_strcmp(zName, z) ) return 1;
}
return 0;
}
/*
** WEBPAGE: help
| | | > | > > > > > > > > | > > > > > | 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 |
if( 0==fossil_strcmp(zName, z) ) return 1;
}
return 0;
}
/*
** WEBPAGE: help
** URL: /help/CMD or /help/www/PAGE
**
** Show the built-in help text for CMD or PAGE. CMD can be a command-line
** interface command or a setting name. PAGE is the name of a
** web interface. /help//PAGE also works if the double-/ makes it through
** the main web server.
**
** Query parameters:
**
** name=CMD Show help for CMD where CMD is a command name or
** or setting name. If CMD beings with "/" it is
** interpreted as a PAGE name.
**
** name=www/PAGE Show help for web page PAGE.
**
** name=/PAGE The initial "www/" on web-page help can be abbreviated as
** just "/"
**
** plaintext Show the help within <pre>...</pre>, as if it were
** displayed using the "fossil help" command.
**
** raw Show the raw help text without any formatting.
** (Used for debugging.)
*/
void help_page(void){
const char *zCmd = P("cmd");
if( zCmd==0 ) zCmd = P("name");
cgi_check_for_malice();
if( zCmd && *zCmd ){
int rc;
const CmdOrPage *pCmd = 0;
style_set_current_feature("tkt");
style_submenu_element("Topic-List", "%R/help");
if( search_restrict(SRCH_HELP)!=0 ){
style_submenu_element("Search","%R/search?y=h");
}
if( strncmp(zCmd,"www/",4)==0 && zCmd[4]!=0 ){
/* Use https://domain/fossil/help/www/timeline or similar with the "www"
** intermediate tag to view web-page documentation. */
zCmd += 3;
}
rc = dispatch_name_search(zCmd, CMDFLAG_ANY|CMDFLAG_PREFIX, &pCmd);
if( pCmd ){
style_header("Help: %s", pCmd->zName);
}else{
style_header("Help");
}
|
| ︙ | ︙ | |||
933 934 935 936 937 938 939 |
const char *z = aCommand[i].zName;
const char *zBoldOn = aCommand[i].eCmdFlags&CMDFLAG_1ST_TIER?"<b>" :"";
const char *zBoldOff = aCommand[i].eCmdFlags&CMDFLAG_1ST_TIER?"</b>":"";
if( '/'==*z || strncmp(z,"test",4)==0 ) continue;
if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)!=0 ) continue;
else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue;
else if( (aCommand[i].eCmdFlags & CMDFLAG_ALIAS)!=0 ) continue;
| | | | | | | 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 |
const char *z = aCommand[i].zName;
const char *zBoldOn = aCommand[i].eCmdFlags&CMDFLAG_1ST_TIER?"<b>" :"";
const char *zBoldOff = aCommand[i].eCmdFlags&CMDFLAG_1ST_TIER?"</b>":"";
if( '/'==*z || strncmp(z,"test",4)==0 ) continue;
if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)!=0 ) continue;
else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue;
else if( (aCommand[i].eCmdFlags & CMDFLAG_ALIAS)!=0 ) continue;
@ <li><a href="%R/help/%s(z)">%s(zBoldOn)%s(z)%s(zBoldOff)</a>
/* Output aliases */
if( occHelp[aCommand[i].iHelp] > 1 ){
int j;
int aliases[MX_HELP_DUP], nAliases=0;
for(j=0; j<occHelp[aCommand[i].iHelp]; j++){
if( bktHelp[aCommand[i].iHelp][j] != i ){
if( aCommand[bktHelp[aCommand[i].iHelp][j]].eCmdFlags
& CMDFLAG_ALIAS ){
aliases[nAliases++] = bktHelp[aCommand[i].iHelp][j];
}
}
}
if( nAliases>0 ){
int k;
@(\
for(k=0; k<nAliases; k++){
@<a href="%R/help/%s(aCommand[aliases[k]].zName)">\
@%s(aCommand[aliases[k]].zName)</a>%s((k<nAliases-1)?", ":"")\
}
@)\
}
}
@ </li>
}
@ </ul></div>
@ <a name='webpages'></a>
@ <h1>Available web UI pages:</h1>
@ <div class="columns" style="column-width: %s(zWidth);">
@ <ul>
for(i=0; i<MX_COMMAND; i++){
const char *z = aCommand[i].zName;
if( '/'!=*z ) continue;
else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue;
if( aCommand[i].zHelp[0] ){
@ <li><a href="%R/help/www%s(z)">%s(z+1)</a></li>
}else{
@ <li>%s(z+1)</li>
}
}
@ </ul></div>
@ <a name='unsupported'></a>
@ <h1>Unsupported commands:</h1>
@ <div class="columns" style="column-width: %s(zWidth);">
@ <ul>
for(i=0; i<MX_COMMAND; i++){
const char *z = aCommand[i].zName;
if( strncmp(z,"test",4)!=0 ) continue;
else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue;
if( aCommand[i].zHelp[0] ){
@ <li><a href="%R/help/%s(z)">%s(z)</a></li>
}else{
@ <li>%s(z)</li>
}
}
@ </ul></div>
@ <a name='settings'></a>
@ <h1>Settings:</h1>
@ <div class="columns" style="column-width: %s(zWidth);">
@ <ul>
for(i=0; i<MX_COMMAND; i++){
const char *z = aCommand[i].zName;
if( (aCommand[i].eCmdFlags & CMDFLAG_SETTING)==0 ) continue;
else if( (aCommand[i].eCmdFlags & CMDFLAG_HIDDEN)!=0 ) continue;
if( aCommand[i].zHelp[0] ){
@ <li><a href="%R/help/%s(z)">%s(z)</a></li>
}else{
@ <li>%s(z)</li>
}
}
@ </ul></div>
}
|
| ︙ | ︙ | |||
1153 1154 1155 1156 1157 1158 1159 |
if( bAbbrevSubcmd ){
zPattern = mprintf(" ([a-z]+ ?\\| ?)*%s\\b", zQSub);
}else{
zPattern = mprintf("> ?fossil [-a-z]+ .*\\b%s\\b", zQSub);
}
fossil_free(zQTop);
fossil_free(zQSub);
| | | 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 |
if( bAbbrevSubcmd ){
zPattern = mprintf(" ([a-z]+ ?\\| ?)*%s\\b", zQSub);
}else{
zPattern = mprintf("> ?fossil [-a-z]+ .*\\b%s\\b", zQSub);
}
fossil_free(zQTop);
fossil_free(zQSub);
fossil_re_compile(&pRe, zPattern, 0);
fossil_free(zPattern);
blob_init(&in, z, -1);
while( blob_line(&in, &line) ){
if( re_match(pRe, (unsigned char*)blob_buffer(&line), blob_size(&line)) ){
int atStart = 1;
blob_appendb(pOut, &line);
n++;
|
| ︙ | ︙ | |||
1250 1251 1252 1253 1254 1255 1256 |
int bAbbrevSubcmd /* z[] uses abbreviated subcommands */
){
ReCompiled *pRe = 0;
Blob in, line;
int n = 0;
if( bAbbrevSubcmd ){
| | | | 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 |
int bAbbrevSubcmd /* z[] uses abbreviated subcommands */
){
ReCompiled *pRe = 0;
Blob in, line;
int n = 0;
if( bAbbrevSubcmd ){
fossil_re_compile(&pRe, "^(Usage: | [a-z][-a-z|]+ .*)", 0);
}else{
fossil_re_compile(&pRe, "^(Usage: | *[Oo]r: +%fossi |> ?fossil )", 0);
}
blob_init(&in, z, -1);
while( blob_line(&in, &line) ){
if( re_match(pRe, (unsigned char*)blob_buffer(&line), blob_strlen(&line)) ){
simplify_usage_line(&line, pOut, bAbbrevSubcmd, zTopic);
n++;
}
|
| ︙ | ︙ | |||
1283 1284 1285 1286 1287 1288 1289 | ReCompiled *pRe = 0; Blob txt, line, subsection; int n = 0; int bSubsectionSeen = 0; blob_init(&txt, z, -1); blob_init(&subsection, 0, 0); | | | 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 |
ReCompiled *pRe = 0;
Blob txt, line, subsection;
int n = 0;
int bSubsectionSeen = 0;
blob_init(&txt, z, -1);
blob_init(&subsection, 0, 0);
fossil_re_compile(&pRe, "^ +-.* ", 0);
while( blob_line(&txt, &line) ){
int len = blob_size(&line);
unsigned char *zLine = (unsigned char *)blob_buffer(&line);
if( re_match(pRe, zLine, len) ){
if( blob_size(&subsection) ){
simplify_usage_line(&subsection, pOut, bAbbrevSubcmd, zCmd);
blob_reset(&subsection);
|
| ︙ | ︙ |
Changes to src/doc.c.
| ︙ | ︙ | |||
518 519 520 521 522 523 524 |
void mimetype_list_page(void){
int i;
char *zCustomList = 0; /* value of the mimetypes setting */
int nCustomEntries = 0; /* number of entries in the mimetypes
** setting */
mimetype_verify();
style_header("Mimetype List");
| | | | | 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 |
void mimetype_list_page(void){
int i;
char *zCustomList = 0; /* value of the mimetypes setting */
int nCustomEntries = 0; /* number of entries in the mimetypes
** setting */
mimetype_verify();
style_header("Mimetype List");
@ <p>The Fossil <a href="%R/help/www/doc">/doc</a> page uses filename
@ suffixes and the following tables to guess at the appropriate mimetype
@ for each document. Mimetypes may be customized and overridden using
@ <a href="%R/help/mimetypes">the mimetypes config setting</a>.</p>
zCustomList = db_get("mimetypes",0);
if( zCustomList!=0 ){
Blob list, entry, key, val;
@ <h1>Repository-specific mimetypes</h1>
@ <p>The following extension-to-mimetype mappings are defined via
@ the <a href="%R/help/mimetypes">mimetypes setting</a>.</p>
@ <table class='sortable mimetypetable' border=1 cellpadding=0 \
@ data-column-types='tt' data-init-sort='0'>
@ <thead>
@ <tr><th>Suffix<th>Mimetype
@ </thead>
@ <tbody>
blob_set(&list, zCustomList);
|
| ︙ | ︙ |
Changes to src/extcgi.c.
| ︙ | ︙ | |||
227 228 229 230 231 232 233 |
}
if( nScript==0 ){
zFailReason = "path does not match any file or script";
goto ext_not_found;
}
assert( nScript>=nRoot+1 );
style_set_current_page("ext/%s", &zScript[nRoot+1]);
| > | | 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
}
if( nScript==0 ){
zFailReason = "path does not match any file or script";
goto ext_not_found;
}
assert( nScript>=nRoot+1 );
style_set_current_page("ext/%s", &zScript[nRoot+1]);
zMime = P("mimetype");
if( zMime==0 ) zMime = mimetype_from_name(zScript);
if( zMime==0 ) zMime = "application/octet-stream";
if( !file_isexe(zScript, ExtFILE) ){
/* File is not executable. Must be a regular file. In that case,
** disallow extra path elements */
if( zPath[nScript]!=0 ){
zFailReason = "extra path elements after filename";
goto ext_not_found;
|
| ︙ | ︙ |
Changes to src/finfo.c.
| ︙ | ︙ | |||
392 393 394 395 396 397 398 399 400 401 402 403 404 405 |
}
login_anonymous_available();
tmFlags = timeline_ss_submenu();
if( tmFlags & TIMELINE_COLUMNAR ){
zStyle = "Columnar";
}else if( tmFlags & TIMELINE_COMPACT ){
zStyle = "Compact";
}else if( tmFlags & TIMELINE_VERBOSE ){
zStyle = "Verbose";
}else if( tmFlags & TIMELINE_CLASSIC ){
zStyle = "Classic";
}else{
zStyle = "Modern";
}
| > > | 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 |
}
login_anonymous_available();
tmFlags = timeline_ss_submenu();
if( tmFlags & TIMELINE_COLUMNAR ){
zStyle = "Columnar";
}else if( tmFlags & TIMELINE_COMPACT ){
zStyle = "Compact";
}else if( tmFlags & TIMELINE_SIMPLE ){
zStyle = "Simple";
}else if( tmFlags & TIMELINE_VERBOSE ){
zStyle = "Verbose";
}else if( tmFlags & TIMELINE_CLASSIC ){
zStyle = "Classic";
}else{
zStyle = "Modern";
}
|
| ︙ | ︙ | |||
729 730 731 732 733 734 735 |
@ <td class="timelineDetailCell">
}
}
if( tmFlags & TIMELINE_COMPACT ){
cgi_printf("<span class='clutter' id='detail-%d'>",frid);
}
cgi_printf("<span class='timeline%sDetail'>", zStyle);
| | | > > > > > | | | 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 |
@ <td class="timelineDetailCell">
}
}
if( tmFlags & TIMELINE_COMPACT ){
cgi_printf("<span class='clutter' id='detail-%d'>",frid);
}
cgi_printf("<span class='timeline%sDetail'>", zStyle);
if( tmFlags & TIMELINE_INLINE ) cgi_printf("(");
if( zUuid && (tmFlags & TIMELINE_VERBOSE)==0 ){
@ file: %z(href("%R/file?name=%T&ci=%!S",zFName,zCkin))\
@ %S(zUuid)</a>
if( fShowId ){
int srcId = delta_source_rid(frid);
if( srcId>0 ){
@ id: %z(href("%R/deltachain/%d",frid))\
@ %d(frid)←%d(srcId)</a>
}else{
@ id: %z(href("%R/deltachain/%d",frid))%d(frid)</a>
}
}
}
if( tmFlags & TIMELINE_SIMPLE ){
@ <span class='timelineEllipsis' data-id='%d(frid)' \
@ id='ellipsis-%d(frid)'>...</span>
@ <span class='clutter' id='detail-%d(frid)'>
}
@ check-in: \
hyperlink_to_version(zCkin);
if( fShowId ){
@ (%d(fmid))
}
@ user: \
hyperlink_to_user(zUser, zDate, ",");
@ branch: %z(href("%R/timeline?t=%T",zBr))%h(zBr)</a>,
if( tmFlags & TIMELINE_INLINE ){
@ size: %d(szFile)
}else{
@ size: %d(szFile)
}
if( g.perm.Hyperlink && zUuid ){
const char *z = zFName;
@ <span id='links-%d(frid)'><span class='timelineExtraLinks'>
@ %z(href("%R/annotate?filename=%h&checkin=%s",z,zCkin))
|
| ︙ | ︙ | |||
791 792 793 794 795 796 797 |
}
}
zAncLink = href("%R/finfo?name=%T&from=%!S&debug=1",zFName,zCkin);
@ %z(zAncLink)[ancestry]</a>
}
tag_private_status(frid);
/* End timelineDetail */
| | | | > > | > | 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 |
}
}
zAncLink = href("%R/finfo?name=%T&from=%!S&debug=1",zFName,zCkin);
@ %z(zAncLink)[ancestry]</a>
}
tag_private_status(frid);
/* End timelineDetail */
if( tmFlags & (TIMELINE_COMPACT|TIMELINE_SIMPLE) ){
@ </span>)</span>
}else{
@ )</span>
}
@ </td></tr>
}
db_finalize(&q);
db_finalize(&qparent);
if( pGraph ){
graph_finish(pGraph, 0, TIMELINE_DISJOINT);
if( pGraph->nErr ){
graph_free(pGraph);
pGraph = 0;
}else{
@ <tr class="timelineBottom" id="btm-%d(iTableId)">\
@ <td></td><td></td><td></td></tr>
}
}
@ </table>
{
int tmFlags = TIMELINE_GRAPH | TIMELINE_FILEDIFF;
timeline_output_graph_javascript(pGraph, tmFlags, iTableId);
}
style_finish_page();
}
/*
** WEBPAGE: mlink
** URL: /mlink?name=FILENAME
** URL: /mlink?ci=NAME
|
| ︙ | ︙ |
Changes to src/forum.c.
| ︙ | ︙ | |||
1910 1911 1912 1913 1914 1915 1916 |
if( pSetting==0 ) continue;
zQP[0] = 'a'+i;
zQP[1] = zQP[0];
zQP[2] = 0;
if( pSetting->width==0 ){
/* Boolean setting */
@ <tr><td align="right">
| | | | 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 |
if( pSetting==0 ) continue;
zQP[0] = 'a'+i;
zQP[1] = zQP[0];
zQP[2] = 0;
if( pSetting->width==0 ){
/* Boolean setting */
@ <tr><td align="right">
@ <a href='%R/help/%h(pSetting->name)'>%h(pSetting->name)</a>:
@ </td><td>
onoff_attribute("", zQP, pSetting->name/*works-like:"x"*/, 0, 0);
@ </td></tr>
}else{
/* Text value setting */
@ <tr><td align="right">
@ <a href='%R/help/%h(pSetting->name)'>%h(pSetting->name)</a>:
@ </td><td>
entry_attribute("", 25, pSetting->name, zQP/*works-like:""*/,
pSetting->def, 0);
@ </td></tr>
}
}
@ </tbody></table>
|
| ︙ | ︙ |
Changes to src/fossil.copybutton.js.
| ︙ | ︙ | |||
40 41 42 43 44 45 46 |
.style: optional object of properties to copy directly into
e.style.
.oncopy: an optional callback function which is added as an event
listener for the 'text-copied' event (see below). There is
functionally no difference from setting this option or adding a
'text-copied' event listener to the element, and this option is
| | < < | | < < < < | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
.style: optional object of properties to copy directly into
e.style.
.oncopy: an optional callback function which is added as an event
listener for the 'text-copied' event (see below). There is
functionally no difference from setting this option or adding a
'text-copied' event listener to the element, and this option is
considered to be a convenience form of that.
Note that this function's own defaultOptions object holds default
values for some options. Any changes made to that object affect
any future calls to this function.
Be aware that clipboard functionality might or might not be
available in any given environment. If this button appears to
have no effect, that may be because it is not enabled/available
in the current platform.
The copy button emits custom event 'text-copied' after it has
successfully copied text to the clipboard. The event's "detail"
member is an object with a "text" property holding the copied
text. Other properties may be added in the future. The event is
not fired if copying to the clipboard fails (e.g. is not
available in the current environment).
The copy button's click handler is suppressed (becomes a no-op)
for as long as the element has the "disabled" attribute.
Returns the copy-initialized element.
Example:
const button = fossil.copyButton('#my-copy-button', {
copyFromId: 'some-other-element-id'
});
button.addEventListener('text-copied',function(ev){
console.debug("Copied text:",ev.detail.text);
});
*/
F.copyButton = function f(e, opt){
if('string'===typeof e){
e = document.querySelector(e);
}
|
| ︙ | ︙ | |||
101 102 103 104 105 106 107 |
);
D.copyStyle(e, opt.style);
e.addEventListener(
'click',
function(ev){
ev.preventDefault();
ev.stopPropagation();
| | > > > > | | 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
);
D.copyStyle(e, opt.style);
e.addEventListener(
'click',
function(ev){
ev.preventDefault();
ev.stopPropagation();
if(e.disabled) return; /* This check is probably redundant. */
const txt = extract.call(opt);
if(txt && D.copyTextToClipboard(txt)){
e.dispatchEvent(new CustomEvent('text-copied',{
detail: {text: txt}
}));
}
},
false
);
if('function' === typeof opt.oncopy){
e.addEventListener('text-copied', opt.oncopy, false);
}
/* Make sure the <button> contains a single nested <span>. */
if(e.childElementCount!=1 || e.firstChild.tagName!='SPAN'){
D.append(D.clearElement(e), D.span());
}
return e;
};
F.copyButton.defaultOptions = {
cssClass: 'copy-button',
oncopy: undefined,
style: {/*properties copied as-is into element.style*/}
};
})(window.fossil);
|
Changes to src/fossil.dom.js.
| ︙ | ︙ | |||
246 247 248 249 250 251 252 |
dom.createElemFactoryWithOptionalParent = function(childType){
return function(parent){
const e = this.create(childType);
if(parent) parent.appendChild(e);
return e;
};
};
| | | 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
dom.createElemFactoryWithOptionalParent = function(childType){
return function(parent){
const e = this.create(childType);
if(parent) parent.appendChild(e);
return e;
};
};
dom.table = dom.createElemFactory('table');
dom.thead = dom.createElemFactoryWithOptionalParent('thead');
dom.tbody = dom.createElemFactoryWithOptionalParent('tbody');
dom.tfoot = dom.createElemFactoryWithOptionalParent('tfoot');
dom.tr = dom.createElemFactoryWithOptionalParent('tr');
dom.td = dom.createElemFactoryWithOptionalParent('td');
dom.th = dom.createElemFactoryWithOptionalParent('th');
|
| ︙ | ︙ | |||
379 380 381 382 383 384 385 |
Internal impl for addClass(), removeClass().
*/
const domAddRemoveClass = function f(action,e){
if(!f.rxSPlus){
f.rxSPlus = /\s+/;
f.applyAction = function(e,a,v){
if(!e || !v
| | | | 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 |
Internal impl for addClass(), removeClass().
*/
const domAddRemoveClass = function f(action,e){
if(!f.rxSPlus){
f.rxSPlus = /\s+/;
f.applyAction = function(e,a,v){
if(!e || !v
/*silently skip empty strings/falsy
values, for usage convenience*/) return;
else if(e.forEach){
e.forEach((E)=>E.classList[a](v));
}else{
e.classList[a](v);
}
};
}
|
| ︙ | ︙ | |||
582 583 584 585 586 587 588 589 590 591 592 593 594 595 |
}else{
e.setAttribute(key,val);
}
}
return e;
};
const enableDisable = function f(enable){
var i = 1, n = arguments.length;
for( ; i < n; ++i ){
let e = arguments[i];
if(e.forEach){
e.forEach((x)=>f(enable,x));
}else{
| > | 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 |
}else{
e.setAttribute(key,val);
}
}
return e;
};
/* Impl for dom.enable() and dom.disable(). */
const enableDisable = function f(enable){
var i = 1, n = arguments.length;
for( ; i < n; ++i ){
let e = arguments[i];
if(e.forEach){
e.forEach((x)=>f(enable,x));
}else{
|
| ︙ | ︙ | |||
841 842 843 844 845 846 847 |
};
/**
Parses a string as HTML.
Usages:
| | | | 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 |
};
/**
Parses a string as HTML.
Usages:
Array parseHtml(htmlString)
DOMElement parseHtml(DOMElement target, htmlString)
The first form parses the string as HTML and returns an Array of
all elements parsed from it. If string is falsy then it returns
an empty array.
The second form parses the HTML string and appends all elements
to the given target element using dom.append(), then returns the
|
| ︙ | ︙ |
Changes to src/fossil.numbered-lines.js.
| ︙ | ︙ | |||
21 22 23 24 25 26 27 |
const tdLn = tbl.querySelector('td.line-numbers');
const urlArgsRaw = (window.location.search||'?')
.replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */
.replace(/&?\bln=[^&]*/,'') /* inbound line number/range */
.replace('?&','?');
const lineState = { urlArgs: urlArgsRaw, start: 0, end: 0 };
const lineTip = new F.PopupWidget({
| < < < | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
const tdLn = tbl.querySelector('td.line-numbers');
const urlArgsRaw = (window.location.search||'?')
.replace(/&?\budc=[^&]*/,'') /* "update display prefs cookie" */
.replace(/&?\bln=[^&]*/,'') /* inbound line number/range */
.replace('?&','?');
const lineState = { urlArgs: urlArgsRaw, start: 0, end: 0 };
const lineTip = new F.PopupWidget({
refresh: function(){
const link = this.state.link;
D.clearElement(link);
if(lineState.start){
const ls = [lineState.start];
if(lineState.end) ls.push(lineState.end);
link.dataset.url = (
|
| ︙ | ︙ | |||
46 47 48 49 50 51 52 |
);
}else{
D.append(link, "No lines selected.");
}
},
init: function(){
const e = this.e;
| | | | < | | 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
);
}else{
D.append(link, "No lines selected.");
}
},
init: function(){
const e = this.e;
const btnCopy = D.attr(D.button(), 'id', 'linenum-copy-button');
link = D.label('linenum-copy-button');
this.state = {link};
F.copyButton(btnCopy,{
copyFromElement: link,
extractText: ()=>link.dataset.url,
oncopy: (ev)=>{
setTimeout(()=>lineTip.hide(), 400);
// arguably too snazzy: F.toast.message("Copied link to clipboard.");
}
});
D.append(this.e, btnCopy, link);
}
});
tbl.addEventListener('click', ()=>lineTip.hide(), true);
tdLn.addEventListener('click', function f(ev){
if('SPAN'!==ev.target.tagName) return;
|
| ︙ | ︙ |
Changes to src/fossil.page.chat.js.
| ︙ | ︙ | |||
899 900 901 902 903 904 905 |
mouse-copying from that field collecting twice as many
newlines as it should (for unknown reasons). */
const cpId = 'copy-to-clipboard-'+id;
/* ^^^ copy button element ID, needed for LABEL element
pairing. Recall that we destroy all child elements of
`content` each time we hit this block, so we can reuse
that element ID on subsequent toggles. */
| | < | 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 |
mouse-copying from that field collecting twice as many
newlines as it should (for unknown reasons). */
const cpId = 'copy-to-clipboard-'+id;
/* ^^^ copy button element ID, needed for LABEL element
pairing. Recall that we destroy all child elements of
`content` each time we hit this block, so we can reuse
that element ID on subsequent toggles. */
const btnCp = D.attr(D.addClass(D.button(),'copy-button'), 'id', cpId);
F.copyButton(btnCp, {extractText: ()=>child._xmsgRaw});
const lblCp = D.label(cpId, "Copy unformatted text");
D.append(content, D.append(D.addClass(D.span(), 'nobr'), btnCp, lblCp));
}
delete e.$isToggling;
D.append(content, child);
return;
}
// We need to fetch the plain-text version...
|
| ︙ | ︙ | |||
1159 1160 1161 1162 1163 1164 1165 |
const iframe = msgObj.e.iframe;
const body = iframe.contentWindow.document.querySelector('body');
if(body && !body.style.fontSize){
/** _Attempt_ to force the iframe to inherit the message's text size
if the body has no explicit size set. On desktop systems
the size is apparently being inherited in that case, but on mobile
not. */
| | | 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 |
const iframe = msgObj.e.iframe;
const body = iframe.contentWindow.document.querySelector('body');
if(body && !body.style.fontSize){
/** _Attempt_ to force the iframe to inherit the message's text size
if the body has no explicit size set. On desktop systems
the size is apparently being inherited in that case, but on mobile
not. */
body.style.fontSize = window.getComputedStyle(msgObj.e.content).fontSize;
}
if('' === iframe.style.maxHeight){
/* Resize iframe height to fit the content. Workaround: if we
adjust the iframe height while it's hidden then its height
is 0, so we must briefly unhide it. */
const isHidden = iframe.classList.contains('hidden');
if(isHidden) D.removeClass(iframe, 'hidden');
|
| ︙ | ︙ | |||
1734 1735 1736 1737 1738 1739 1740 |
};
['drop','dragenter','dragleave','dragend'].forEach(
(k)=>Chat.e.inputX.addEventListener(k, noDragDropEvents, false)
);
return bxs;
})()/*drag/drop/paste*/;
| < < < < | 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 |
};
['drop','dragenter','dragleave','dragend'].forEach(
(k)=>Chat.e.inputX.addEventListener(k, noDragDropEvents, false)
);
return bxs;
})()/*drag/drop/paste*/;
const localTime8601 = function(d){
return [
d.getYear()+1900, '-', pad2(d.getMonth()+1), '-', pad2(d.getDate()),
'T', pad2(d.getHours()),':', pad2(d.getMinutes()),':',pad2(d.getSeconds())
].join('');
};
|
| ︙ | ︙ |
Changes to src/fossil.page.pikchrshow.js.
| ︙ | ︙ | |||
45 46 47 48 49 50 51 |
F.onPageLoad(function() {
document.body.classList.add('pikchrshow');
P.e = { /* various DOM elements we work with... */
previewTarget: E('#pikchrshow-output'),
previewLegend: E('#pikchrshow-output-wrapper > legend'),
previewCopyButton: D.attr(
| | | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
F.onPageLoad(function() {
document.body.classList.add('pikchrshow');
P.e = { /* various DOM elements we work with... */
previewTarget: E('#pikchrshow-output'),
previewLegend: E('#pikchrshow-output-wrapper > legend'),
previewCopyButton: D.attr(
D.addClass(D.button(),'copy-button'),
'id','preview-copy-button'
),
previewModeLabel: D.label('preview-copy-button'),
btnSubmit: E('#pikchr-submit-preview'),
btnStash: E('#pikchr-stash'),
btnUnstash: E('#pikchr-unstash'),
btnClearStash: E('#pikchr-clear-stash'),
|
| ︙ | ︙ | |||
117 118 119 120 121 122 123 |
return false;
}
}, false);
////////////////////////////////////////////////////////////
// Setup clipboard-copy of markup/SVG...
F.copyButton(P.e.previewCopyButton, {copyFromElement: P.e.taPreviewText});
| < | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
return false;
}
}, false);
////////////////////////////////////////////////////////////
// Setup clipboard-copy of markup/SVG...
F.copyButton(P.e.previewCopyButton, {copyFromElement: P.e.taPreviewText});
////////////////////////////////////////////////////////////
// Set up dark mode simulator...
P.e.cbDarkMode.addEventListener('change', function(ev){
if(ev.target.checked) D.addClass(P.e.previewTarget, 'dark-mode');
else D.removeClass(P.e.previewTarget, 'dark-mode');
}, false);
|
| ︙ | ︙ | |||
346 347 348 349 350 351 352 |
if(this.response.isError){
D.append(D.clearElement(preTgt), D.parseHtml(P.response.raw));
D.addClass(preTgt, 'error');
this.e.previewModeLabel.innerText = "Error";
return;
}
D.removeClass(preTgt, 'error');
| | | 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 |
if(this.response.isError){
D.append(D.clearElement(preTgt), D.parseHtml(P.response.raw));
D.addClass(preTgt, 'error');
this.e.previewModeLabel.innerText = "Error";
return;
}
D.removeClass(preTgt, 'error');
this.e.previewCopyButton.disabled = false;
D.removeClass(this.e.markupAlignWrapper, 'hidden');
D.enable(this.e.previewModeToggle, this.e.markupAlignRadios);
let label, svg;
switch(this.previewMode){
case 0:
label = "SVG";
f.showMarkupAlignment(false);
|
| ︙ | ︙ | |||
425 426 427 428 429 430 431 |
P.response.isError = isError;
D.enable(fp.toDisable);
P.renderPreview();
};
}
D.disable(fp.toDisable, this.e.previewModeToggle, this.e.markupAlignRadios);
D.addClass(this.e.markupAlignWrapper, 'hidden');
| | | 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 |
P.response.isError = isError;
D.enable(fp.toDisable);
P.renderPreview();
};
}
D.disable(fp.toDisable, this.e.previewModeToggle, this.e.markupAlignRadios);
D.addClass(this.e.markupAlignWrapper, 'hidden');
this.e.previewCopyButton.disabled = true;
const content = this.e.taContent.value.trim();
this.response.raw = this.response.rawSvg = undefined;
this.response.inputText = content;
const sampleScript = fp.$_sampleScript;
delete fp.$_sampleScript;
if(sampleScript && sampleScript.cached){
fp.updateView(sampleScript.cached, false);
|
| ︙ | ︙ |
Changes to src/fossil.page.pikchrshowasm.js.
| ︙ | ︙ | |||
310 311 312 313 314 315 316 |
modes.selectedIndex = (modes.selectedIndex + 1) % modes.length;
this.e.previewModeLabel.innerText = this.renderModeLabels[modes[modes.selectedIndex]];
if(this.e.pikOut.dataset.pikchr){
this.render(this.e.pikOut.dataset.pikchr);
}
}.bind(PS));
F.copyButton(PS.e.previewCopyButton, {copyFromElement: PS.e.outText});
| < | 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
modes.selectedIndex = (modes.selectedIndex + 1) % modes.length;
this.e.previewModeLabel.innerText = this.renderModeLabels[modes[modes.selectedIndex]];
if(this.e.pikOut.dataset.pikchr){
this.render(this.e.pikOut.dataset.pikchr);
}
}.bind(PS));
F.copyButton(PS.e.previewCopyButton, {copyFromElement: PS.e.outText});
PS.addMsgHandler('working',function f(ev){
switch(ev.data){
case 'start': /* See notes in preStartWork(). */; return;
case 'end':
//preStartWork._.pageTitle.innerText = preStartWork._.pageTitleOrig;
this.e.btnRender.removeAttribute('disabled');
|
| ︙ | ︙ |
Added src/fossil.page.ticket.js.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
/*
* This script adds a checkbox to reverse the sorting on any body.tkt
* pages which contain a .tktCommentArea element.
*/
window.addEventListener( 'load', function() {
const tgt = document.querySelectorAll('.tktCommentArea');
if( !tgt ) return;
const F = globalThis.fossil, D = F.dom;
let i = 0;
for(const e of tgt) {
++i;
const childs = e.querySelectorAll('.tktCommentEntry');
if( !childs || 1===childs.length ) continue;
const cbReverseKey = 'tktCommentArea:reverse';
const cbReverse = D.checkbox();
const cbId = cbReverseKey+':'+i;
cbReverse.setAttribute('id',cbId);
const widget = D.append(
D.div(),
cbReverse,
D.label(cbReverse, " Show newest first? ")
);
widget.classList.add('newest-first-controls');
e.parentElement.insertBefore(widget,e);
const cbReverseIt = ()=>{
e.classList[cbReverse.checked ? 'add' : 'remove']('reverse');
F.storage.set(cbReverseKey, cbReverse.checked ? 1 : 0);
};
cbReverse.addEventListener('change', cbReverseIt, true);
cbReverse.checked = !!(+F.storage.get(cbReverseKey, 0));
};
}); // window.addEventListener( 'load' ...
|
Changes to src/fossil.page.wikiedit.js.
| ︙ | ︙ | |||
1232 1233 1234 1235 1236 1237 1238 |
encodeURIComponent(wi.name),
"&file=",
encodeURIComponent(a.filename)
].join(''),
"raw/"+a.src
].forEach(function(url){
const imgUrl = D.append(D.addClass(D.span(), 'monospace'), url);
| | | | 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 |
encodeURIComponent(wi.name),
"&file=",
encodeURIComponent(a.filename)
].join(''),
"raw/"+a.src
].forEach(function(url){
const imgUrl = D.append(D.addClass(D.span(), 'monospace'), url);
const urlCopy = D.button();
const li = D.li(ul);
D.append(li, urlCopy, imgUrl);
F.copyButton(urlCopy, {copyFromElement: imgUrl});
});
});
return this;
};
/** Updates the in-tab title/edit status information */
|
| ︙ | ︙ |
Changes to src/graph.js.
| ︙ | ︙ | |||
724 725 726 727 728 729 730 |
if(x) x.style.display=value;
}
function toggleDetail(){
var id = parseInt(this.getAttribute('data-id'))
var x = document.getElementById("detail-"+id);
if( x.style.display=="inline" ){
x.style.display="none";
| | | | 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 |
if(x) x.style.display=value;
}
function toggleDetail(){
var id = parseInt(this.getAttribute('data-id'))
var x = document.getElementById("detail-"+id);
if( x.style.display=="inline" ){
x.style.display="none";
document.getElementById("ellipsis-"+id).textContent = "...";
changeDisplayById("links-"+id,"none");
}else{
x.style.display="inline";
document.getElementById("ellipsis-"+id).textContent = "←";
changeDisplayById("links-"+id,"inline");
}
checkHeight();
}
function scrollToSelected(){
var x = document.getElementsByClassName('timelineSelected');
if(x[0]){
|
| ︙ | ︙ | |||
762 763 764 765 766 767 768 |
}else{
function checkHeight(){}
}
if( tx.scrollToSelect ){
scrollToSelected();
}
| | | | 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 |
}else{
function checkHeight(){}
}
if( tx.scrollToSelect ){
scrollToSelected();
}
/* Set the onclick= attributes for elements of the "Compact" and
** "Simple" views so that clicking turns the details on and off.
*/
var lx = topObj.getElementsByClassName('timelineEllipsis');
var i;
for(i=0; i<lx.length; i++){
if( lx[i].hasAttribute('data-id') ) lx[i].onclick = toggleDetail;
}
lx = topObj.getElementsByClassName('timelineCompactComment');
|
| ︙ | ︙ |
Changes to src/href.js.
1 2 3 4 5 | /* As an anti-robot defense, <a> elements are initially coded with the ** href= set to the honeypot, and <form> elements are initialized with ** action= set to the login page. The real values for href= and action= ** are held in data-href= and data-action=. The following code moves ** data-href= into href= and data-action= into action= for all | | | > > | | | | | | | | | | > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
/* As an anti-robot defense, <a> elements are initially coded with the
** href= set to the honeypot, and <form> elements are initialized with
** action= set to the login page. The real values for href= and action=
** are held in data-href= and data-action=. The following code moves
** data-href= into href= and data-action= into action= for all
** <a> and <form> elements, after CSS has been loaded, and after a delay,
** and maybe also after mouse movement is seen.
**
** Before sourcing this script, create a separate <script> element
** (with type='application/json' to avoid Content Security Policy issues)
** containing:
**
** {"delay":MILLISECONDS, "mouseover":BOOLEAN}
**
** The <script> must have an id='href-data'. DELAY is the number
** milliseconds delay prior to populating href= and action=. If the
** mouseover boolean is true, then the href= rewrite is further delayed
** until the first mousedown event that occurs after the timer expires.
*/
var antiRobot = 0;
function antiRobotGo(){
if( antiRobot!=3 ) return;
var z = window.getComputedStyle(document.body).zIndex;
if( z==='0' || z===0 ){
antiRobot = 7;
var anchors = document.getElementsByTagName("a");
for(var i=0; i<anchors.length; i++){
var j = anchors[i];
if(j.hasAttribute("data-href")) j.href=j.getAttribute("data-href");
}
var forms = document.getElementsByTagName("form");
for(var i=0; i<forms.length; i++){
var j = forms[i];
if(j.hasAttribute("data-action")) j.action=j.getAttribute("data-action");
}
}
}
function antiRobotDefense(){
var x = document.getElementById("href-data");
var jx = x.textContent || x.innerText;
var g = JSON.parse(jx);
if( g.mouseover ){
|
| ︙ | ︙ | |||
54 55 56 57 58 59 60 61 62 63 |
setTimeout(function(){
antiRobot |= 1;
antiRobotGo();
}, g.delay)
}else{
antiRobot |= 1;
}
antiRobotGo();
}
antiRobotDefense();
| > | 57 58 59 60 61 62 63 64 65 66 67 |
setTimeout(function(){
antiRobot |= 1;
antiRobotGo();
}, g.delay)
}else{
antiRobot |= 1;
}
window.addEventListener('load',antiRobotGo);
antiRobotGo();
}
antiRobotDefense();
|
Changes to src/http.c.
| ︙ | ︙ | |||
139 140 141 142 143 144 145 146 147 148 |
static void http_build_header(
Blob *pPayload, /* the payload that will be sent */
Blob *pHdr, /* construct the header here */
Blob *pLogin, /* Login card header value or NULL */
const char *zAltMimetype /* Alternative mimetype */
){
int nPayload = pPayload ? blob_size(pPayload) : 0;
blob_zero(pHdr);
blob_appendf(pHdr, "%s %s HTTP/1.0\r\n",
| > > > > > > > > | < | 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 |
static void http_build_header(
Blob *pPayload, /* the payload that will be sent */
Blob *pHdr, /* construct the header here */
Blob *pLogin, /* Login card header value or NULL */
const char *zAltMimetype /* Alternative mimetype */
){
int nPayload = pPayload ? blob_size(pPayload) : 0;
const char *zPath;
blob_zero(pHdr);
if( g.url.subpath ){
zPath = g.url.subpath;
}else if( g.url.path==0 || g.url.path[0]==0 ){
zPath = "/";
}else{
zPath = g.url.path;
}
blob_appendf(pHdr, "%s %s HTTP/1.0\r\n",
nPayload>0 ? "POST" : "GET", zPath);
if( g.url.proxyAuth ){
blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.url.proxyAuth);
}
if( g.zHttpAuth && g.zHttpAuth[0] ){
const char *zCredentials = g.zHttpAuth;
char *zEncoded = encode64(zCredentials, -1);
blob_appendf(pHdr, "Authorization: Basic %s\r\n", zEncoded);
|
| ︙ | ︙ | |||
457 458 459 460 461 462 463 464 465 466 467 468 469 470 |
}
/* Activate the PATH= auxiliary argument to the ssh command if that
** is called for.
*/
if( g.url.isSsh
&& (g.url.flags & URL_SSH_RETRY)==0
&& ssh_needs_path_argument(g.url.hostname, -1)
){
g.url.flags |= URL_SSH_PATH;
}
if( transport_open(&g.url) ){
fossil_warning("%s", transport_errmsg(&g.url));
| > | 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 |
}
/* Activate the PATH= auxiliary argument to the ssh command if that
** is called for.
*/
if( g.url.isSsh
&& (g.url.flags & URL_SSH_RETRY)==0
&& g.db!=0
&& ssh_needs_path_argument(g.url.hostname, -1)
){
g.url.flags |= URL_SSH_PATH;
}
if( transport_open(&g.url) ){
fossil_warning("%s", transport_errmsg(&g.url));
|
| ︙ | ︙ | |||
676 677 678 679 680 681 682 |
*/
if( g.url.isSsh /* This is an SSH: sync */
&& (g.url.flags & URL_SSH_EXE)==0 /* Does not have ?fossil=.... */
&& (g.url.flags & URL_SSH_RETRY)==0 /* Not retried already */
){
/* Retry after flipping the SSH_PATH setting */
transport_close(&g.url);
| > | | | | | | > | | 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 |
*/
if( g.url.isSsh /* This is an SSH: sync */
&& (g.url.flags & URL_SSH_EXE)==0 /* Does not have ?fossil=.... */
&& (g.url.flags & URL_SSH_RETRY)==0 /* Not retried already */
){
/* Retry after flipping the SSH_PATH setting */
transport_close(&g.url);
if( (mHttpFlags & HTTP_QUIET)==0 ){
fossil_print(
"First attempt to run fossil on %s using SSH failed.\n"
"Retrying %s the PATH= argument.\n",
g.url.hostname,
(g.url.flags & URL_SSH_PATH)!=0 ? "without" : "with"
);
}
g.url.flags ^= URL_SSH_PATH|URL_SSH_RETRY;
rc = http_exchange(pSend,pReply,mHttpFlags,0,zAltMimetype);
if( rc==0 && g.db!=0 ){
(void)ssh_needs_path_argument(g.url.hostname,
(g.url.flags & URL_SSH_PATH)!=0);
}
return rc;
}else{
/* The problem could not be corrected by retrying. Report the
** the error. */
|
| ︙ | ︙ | |||
806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 |
** a GET request where there is no PAYLOAD.
**
** Options:
** --compress Use ZLIB compression on the payload
** --mimetype TYPE Mimetype of the payload
** --no-cert-verify Disable TLS cert verification
** --out FILE Store the reply in FILE
** -v Verbose output
** --xfer PAYLOAD in a Fossil xfer protocol message
*/
void test_httpmsg_command(void){
const char *zMimetype;
const char *zInFile;
const char *zOutFile;
Blob in, out;
unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
zMimetype = find_option("mimetype",0,1);
zOutFile = find_option("out","o",1);
if( find_option("verbose","v",0)!=0 ) mHttpFlags |= HTTP_VERBOSE;
if( find_option("compress",0,0)!=0 ) mHttpFlags &= ~HTTP_NOCOMPRESS;
if( find_option("no-cert-verify",0,0)!=0 ){
#ifdef FOSSIL_ENABLE_SSL
ssl_disable_cert_verification();
#endif
}
if( find_option("xfer",0,0)!=0 ){
mHttpFlags |= HTTP_USE_LOGIN;
mHttpFlags &= ~HTTP_GENERIC;
}
if( find_option("ipv4",0,0) ) g.fIPv4 = 1;
verify_all_options();
if( g.argc<3 || g.argc>5 ){
usage("URL ?PAYLOAD? ?OUTPUT?");
}
zInFile = g.argc>=4 ? g.argv[3] : 0;
if( g.argc==5 ){
if( zOutFile ){
fossil_fatal("output file specified twice: \"--out %s\" and \"%s\"",
zOutFile, g.argv[4]);
}
zOutFile = g.argv[4];
}
url_parse(g.argv[2], 0);
if( g.url.protocol[0]!='h' ){
| > > > > | > > > | 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 |
** a GET request where there is no PAYLOAD.
**
** Options:
** --compress Use ZLIB compression on the payload
** --mimetype TYPE Mimetype of the payload
** --no-cert-verify Disable TLS cert verification
** --out FILE Store the reply in FILE
** --subpath PATH HTTP request path for ssh: and file: URLs
** -v Verbose output
** --xfer PAYLOAD in a Fossil xfer protocol message
*/
void test_httpmsg_command(void){
const char *zMimetype;
const char *zInFile;
const char *zOutFile;
const char *zSubpath;
Blob in, out;
unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
zMimetype = find_option("mimetype",0,1);
zOutFile = find_option("out","o",1);
if( find_option("verbose","v",0)!=0 ) mHttpFlags |= HTTP_VERBOSE;
if( find_option("compress",0,0)!=0 ) mHttpFlags &= ~HTTP_NOCOMPRESS;
if( find_option("no-cert-verify",0,0)!=0 ){
#ifdef FOSSIL_ENABLE_SSL
ssl_disable_cert_verification();
#endif
}
if( find_option("xfer",0,0)!=0 ){
mHttpFlags |= HTTP_USE_LOGIN;
mHttpFlags &= ~HTTP_GENERIC;
}
if( find_option("ipv4",0,0) ) g.fIPv4 = 1;
zSubpath = find_option("subpath",0,1);
verify_all_options();
if( g.argc<3 || g.argc>5 ){
usage("URL ?PAYLOAD? ?OUTPUT?");
}
zInFile = g.argc>=4 ? g.argv[3] : 0;
if( g.argc==5 ){
if( zOutFile ){
fossil_fatal("output file specified twice: \"--out %s\" and \"%s\"",
zOutFile, g.argv[4]);
}
zOutFile = g.argv[4];
}
url_parse(g.argv[2], 0);
if( g.url.protocol[0]!='h' ){
if( zSubpath==0 ){
fossil_fatal("the --subpath option is required for %s://",g.url.protocol);
}else{
g.url.subpath = fossil_strdup(zSubpath);
}
}
if( zInFile ){
blob_read_from_file(&in, zInFile, ExtFILE);
if( zMimetype==0 && (mHttpFlags & HTTP_GENERIC)!=0 ){
if( fossil_strcmp(zInFile,"-")==0 ){
zMimetype = "application/x-unknown";
}else{
|
| ︙ | ︙ |
Changes to src/info.c.
| ︙ | ︙ | |||
399 400 401 402 403 404 405 406 407 408 409 |
@ Changes to %h(zName).
}
@ </span></div>
if( pCfg ){
append_diff(zOld, zNew, pCfg);
}
}else{
if( zOld && zNew ){
if( fossil_strcmp(zOld, zNew)!=0 ){
if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
@ Renamed and modified
| > > | | | | | | | | | 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 |
@ Changes to %h(zName).
}
@ </span></div>
if( pCfg ){
append_diff(zOld, zNew, pCfg);
}
}else{
const char *zCkin2 =
mprintf(validate16(zCkin, -1) ? "%!S" : "%T"/*works-like:"%s"*/, zCkin);
if( zOld && zNew ){
if( fossil_strcmp(zOld, zNew)!=0 ){
if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
@ Renamed and modified
@ %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zOldName,zOld,zCkin2))\
@ %h(zOldName)</a>
@ %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>
@ to %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\
@ %h(zName)</a>
@ %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
}else{
@ Modified %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\
@ %h(zName)</a>
@ from %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>
@ to %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
}
}else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
@ Name change
@ from %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zOldName,zOld,zCkin2))\
@ %h(zOldName)</a>
@ to %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\
@ %h(zName)</a>.
}else{
@ %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\
@ %h(zName)</a> became
if( mperm==PERM_EXE ){
@ executable with contents
}else if( mperm==PERM_LNK ){
@ a symlink with target
}else{
@ a regular file with contents
}
@ %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
}
}else if( zOld ){
@ Deleted %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zOld,zCkin2))\
@ %h(zName)</a> version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>.
}else{
@ Added %z(href("%R/finfo?name=%T&m=%!S&ci=%s",zName,zNew,zCkin2))\
@ %h(zName)</a> version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
}
if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
if( pCfg ){
@ </span></div>
append_diff(zOld, zNew, pCfg);
}else{
|
| ︙ | ︙ | |||
641 642 643 644 645 646 647 648 649 650 651 652 653 654 |
vid
);
if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
}else{
DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
}
@ <div class="sectionmenu info-changes-menu">
zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
if( diffType!=1 ){
@ %z(chref("button","%R?diff=1%s",zW))Unified Diff</a>
}
if( diffType!=2 ){
@ %z(chref("button","%R?diff=2%s",zW))Side-by-Side Diff</a>
| > > | 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 |
vid
);
if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
}else{
DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
}
@ <div class="section" id="changes_section">Changes</div>
DCfg.diffFlags |= DIFF_NUMSTAT; /* Show stats in the 'Changes' section */
@ <div class="sectionmenu info-changes-menu">
zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
if( diffType!=1 ){
@ %z(chref("button","%R?diff=1%s",zW))Unified Diff</a>
}
if( diffType!=2 ){
@ %z(chref("button","%R?diff=2%s",zW))Side-by-Side Diff</a>
|
| ︙ | ︙ | |||
716 717 718 719 720 721 722 723 724 725 726 727 728 729 |
blob_read_from_file(&new, zTreename, ExtFILE);
text_diff(&old, &new, cgi_output_blob(), pCfg);
blob_reset(&old);
blob_reset(&new);
}
}
db_finalize(&q);
append_diff_javascript(diffType);
}
/*
** Render a web-page diff of the changes in the working check-out to
** an external reference.
*/
| > > > > > > | 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 |
blob_read_from_file(&new, zTreename, ExtFILE);
text_diff(&old, &new, cgi_output_blob(), pCfg);
blob_reset(&old);
blob_reset(&new);
}
}
db_finalize(&q);
@ <script nonce='%h(style_nonce())'>;/* info.c:%d(__LINE__) */
@ document.getElementById('changes_section').textContent = 'Changes ' +
@ '(%d(g.diffCnt[0]) file' + (%d(g.diffCnt[0])===1 ? '' : 's') + ': ' +
@ '+%d(g.diffCnt[1]) ' +
@ '−%d(g.diffCnt[2]))'
@ </script>
append_diff_javascript(diffType);
}
/*
** Render a web-page diff of the changes in the working check-out to
** an external reference.
*/
|
| ︙ | ︙ | |||
739 740 741 742 743 744 745 746 747 748 749 750 751 752 |
"SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname", vid
);
if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
}else{
DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
}
@ <div class="sectionmenu info-changes-menu">
zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
if( diffType!=1 ){
@ %z(chref("button","%R?diff=1&exbase=%h%s",zExBase,zW))\
@ Unified Diff</a>
}
if( diffType!=2 ){
| > > | 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 |
"SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname", vid
);
if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
}else{
DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
}
@ <div class="section" id="changes_section">Changes</div>
DCfg.diffFlags |= DIFF_NUMSTAT; /* Show stats in the 'Changes' section */
@ <div class="sectionmenu info-changes-menu">
zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
if( diffType!=1 ){
@ %z(chref("button","%R?diff=1&exbase=%h%s",zExBase,zW))\
@ Unified Diff</a>
}
if( diffType!=2 ){
|
| ︙ | ︙ | |||
801 802 803 804 805 806 807 808 809 810 811 812 813 814 |
blob_reset(&lhs);
blob_reset(&rhs);
}
fossil_free(zLhs);
fossil_free(zRhs);
}
db_finalize(&q);
append_diff_javascript(diffType);
}
/*
** WEBPAGE: ckout
**
** Show information about the current checkout. This page only functions
| > > > > > > | 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 |
blob_reset(&lhs);
blob_reset(&rhs);
}
fossil_free(zLhs);
fossil_free(zRhs);
}
db_finalize(&q);
@ <script nonce='%h(style_nonce())'>;/* info.c:%d(__LINE__) */
@ document.getElementById('changes_section').textContent = 'Changes ' +
@ '(%d(g.diffCnt[0]) file' + (%d(g.diffCnt[0])===1 ? '' : 's') + ': ' +
@ '+%d(g.diffCnt[1]) ' +
@ '−%d(g.diffCnt[2]))'
@ </script>
append_diff_javascript(diffType);
}
/*
** WEBPAGE: ckout
**
** Show information about the current checkout. This page only functions
|
| ︙ | ︙ | |||
916 917 918 919 920 921 922 |
if( rid==0 ){
style_header("Check-in Information Error");
@ No such object: %h(zName)
style_finish_page();
return;
}
zRe = P("regex");
| | | 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 |
if( rid==0 ){
style_header("Check-in Information Error");
@ No such object: %h(zName)
style_finish_page();
return;
}
zRe = P("regex");
if( zRe ) fossil_re_compile(&pRe, zRe, 0);
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
zParent = db_text(0,
"SELECT uuid FROM plink, blob"
" WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
rid
);
isLeaf = !db_exists("SELECT 1 FROM plink WHERE pid=%d", rid);
|
| ︙ | ︙ | |||
973 974 975 976 977 978 979 |
@ <div class="accordion_panel">
@ <table class="label-value">
@ <tr><th>Comment:</th><td class="infoComment">\
@ %!W(zEComment?zEComment:zComment)</td></tr>
/* The Download: line */
if( g.perm.Zip ){
| < < < < < < < < < < < < < < < > > > > | | | | | | | < > | 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 |
@ <div class="accordion_panel">
@ <table class="label-value">
@ <tr><th>Comment:</th><td class="infoComment">\
@ %!W(zEComment?zEComment:zComment)</td></tr>
/* The Download: line */
if( g.perm.Zip ){
@ <tr><th>Downloads:</th><td>
if( robot_would_be_restricted("download") ){
@ See separate %z(href("%R/rchvdwnld/%!S",zUuid))download page</a>
}else{
char *zBase = archive_base_name(rid);
@ %z(href("%R/tarball/%s.tar.gz",zBase))Tarball</a>
@ | %z(href("%R/zip/%s.zip",zBase))ZIP archive</a>
if( g.zLogin!=0 ){
@ | %z(href("%R/sqlar/%s.sqlar",zBase))\
@ SQL archive</a></td></tr>
}
fossil_free(zBase);
}
}
@ <tr><th>Timelines:</th><td>
@ %z(href("%R/timeline?f=%!S&unhide",zUuid))family</a>
if( zParent ){
@ | %z(href("%R/timeline?p=%!S&unhide",zUuid))ancestors</a>
}
|
| ︙ | ︙ | |||
1165 1166 1167 1168 1169 1170 1171 |
if( !PB("nowiki") ){
wiki_render_associated("checkin", zUuid, 0);
}
render_backlink_graph(zUuid,
"<div class=\"section accordion\">References</div>\n");
@ <div class="section accordion">Context</div><div class="accordion_panel">
render_checkin_context(rid, 0, 0, 0);
| | > | 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 |
if( !PB("nowiki") ){
wiki_render_associated("checkin", zUuid, 0);
}
render_backlink_graph(zUuid,
"<div class=\"section accordion\">References</div>\n");
@ <div class="section accordion">Context</div><div class="accordion_panel">
render_checkin_context(rid, 0, 0, 0);
@ </div><div class="section accordion" id="changes_section">Changes</div>
@ <div class="accordion_panel">
@ <div class="sectionmenu info-changes-menu">
/* ^^^ .info-changes-menu is used by diff scroll sync */
pCfg = construct_diff_flags(diffType, &DCfg);
DCfg.diffFlags |= DIFF_NUMSTAT; /* Show stats in the 'Changes' section */
DCfg.pRe = pRe;
zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
if( diffType!=1 ){
@ %z(chref("button","%R/%s/%T?diff=1%s",zPage,zName,zW))\
@ Unified Diff</a>
}
if( diffType!=2 ){
|
| ︙ | ︙ | |||
1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 |
const char *zNew = db_column_text(&q3,3);
const char *zOldName = db_column_text(&q3, 4);
append_file_change_line(zUuid, zName, zOld, zNew, zOldName,
pCfg,mperm);
}
db_finalize(&q3);
@ </div>
append_diff_javascript(diffType);
style_finish_page();
}
/*
** WEBPAGE: winfo
** URL: /winfo?name=HASH
| > > > > > > > > | 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 |
const char *zNew = db_column_text(&q3,3);
const char *zOldName = db_column_text(&q3, 4);
append_file_change_line(zUuid, zName, zOld, zNew, zOldName,
pCfg,mperm);
}
db_finalize(&q3);
@ </div>
if( diffType!=0 ){
@ <script nonce='%h(style_nonce())'>;/* info.c:%d(__LINE__) */
@ document.getElementById('changes_section').textContent = 'Changes ' +
@ '(%d(g.diffCnt[0]) file' + (%d(g.diffCnt[0])===1 ? '' : 's') + ': ' +
@ '+%d(g.diffCnt[1]) ' +
@ '−%d(g.diffCnt[2]))'
@ </script>
}
append_diff_javascript(diffType);
style_finish_page();
}
/*
** WEBPAGE: winfo
** URL: /winfo?name=HASH
|
| ︙ | ︙ | |||
1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 |
int graphFlags = 0;
Blob qp; /* non-glob= query parameters for generated links */
Blob qpGlob; /* glob= query parameter for generated links */
int bInvert = PB("inv");
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
login_anonymous_available();
fossil_nice_default();
blob_init(&qp, 0, 0);
blob_init(&qpGlob, 0, 0);
diffType = preferred_diff_type();
zRe = P("regex");
| > | | 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 |
int graphFlags = 0;
Blob qp; /* non-glob= query parameters for generated links */
Blob qpGlob; /* glob= query parameter for generated links */
int bInvert = PB("inv");
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
if( robot_restrict("diff") ) return;
login_anonymous_available();
fossil_nice_default();
blob_init(&qp, 0, 0);
blob_init(&qpGlob, 0, 0);
diffType = preferred_diff_type();
zRe = P("regex");
if( zRe ) fossil_re_compile(&pRe, zRe, 0);
zBranch = P("branch");
if( zBranch && zBranch[0]==0 ) zBranch = 0;
if( zBranch ){
blob_appendf(&qp, "branch=%T", zBranch);
zMergeOrigin = mprintf("merge-in:%s", zBranch);
cgi_replace_parameter("from", zMergeOrigin);
cgi_replace_parameter("to", zBranch);
|
| ︙ | ︙ | |||
1916 1917 1918 1919 1920 1921 1922 1923 1924 |
** * The "diff" query parameter
** * The "diff" field of the user display cookie
** * The "preferred-diff-type" setting
** * 1 for mobile and 2 for desktop, based on the UserAgent
*/
int preferred_diff_type(void){
int dflt;
static char zDflt[2]
/*static b/c cookie_link_parameter() does not copy it!*/;
| > > > > > > | | > > | > > > > > | 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 |
** * The "diff" query parameter
** * The "diff" field of the user display cookie
** * The "preferred-diff-type" setting
** * 1 for mobile and 2 for desktop, based on the UserAgent
*/
int preferred_diff_type(void){
int dflt;
int res;
int isBot;
static char zDflt[2]
/*static b/c cookie_link_parameter() does not copy it!*/;
if( robot_would_be_restricted("diff") ){
dflt = 0;
isBot = 1;
}else{
dflt = db_get_int("preferred-diff-type",-99);
if( dflt<=0 ) dflt = user_agent_is_likely_mobile() ? 1 : 2;
isBot = 0;
}
zDflt[0] = dflt + '0';
zDflt[1] = 0;
cookie_link_parameter("diff","diff", zDflt);
res = atoi(PD_NoBot("diff",zDflt));
if( isBot && res>0 && robot_restrict("diff") ){
cgi_reply();
fossil_exit(0);
}
return res;
}
/*
** WEBPAGE: fdiff
** URL: fdiff?v1=HASH&v2=HASH
**
|
| ︙ | ︙ | |||
1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 |
ReCompiled *pRe = 0;
u32 objdescFlags = 0;
int verbose = PB("verbose");
DiffConfig DCfg;
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
diff_config_init(&DCfg, 0);
diffType = preferred_diff_type();
if( P("from") && P("to") ){
v1 = artifact_from_ci_and_filename("from");
v2 = artifact_from_ci_and_filename("to");
if( v1==0 || v2==0 ) fossil_redirect_home();
}else{
| > | 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 |
ReCompiled *pRe = 0;
u32 objdescFlags = 0;
int verbose = PB("verbose");
DiffConfig DCfg;
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
if( robot_restrict("diff") ) return;
diff_config_init(&DCfg, 0);
diffType = preferred_diff_type();
if( P("from") && P("to") ){
v1 = artifact_from_ci_and_filename("from");
v2 = artifact_from_ci_and_filename("to");
if( v1==0 || v2==0 ) fossil_redirect_home();
}else{
|
| ︙ | ︙ | |||
2007 2008 2009 2010 2011 2012 2013 |
"%R/annotate?origin=%s&checkin=%s&filename=%T",
zOrig, zCkin, zFN);
}
db_finalize(&q);
}
zRe = P("regex");
cgi_check_for_malice();
| | | 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 |
"%R/annotate?origin=%s&checkin=%s&filename=%T",
zOrig, zCkin, zFN);
}
db_finalize(&q);
}
zRe = P("regex");
cgi_check_for_malice();
if( zRe ) fossil_re_compile(&pRe, zRe, 0);
if( verbose ) objdescFlags |= OBJDESC_DETAIL;
if( isPatch ){
Blob c1, c2, *pOut;
DiffConfig DCfg;
pOut = cgi_output_blob();
cgi_set_content_type("text/plain");
DCfg.diffFlags = DIFF_VERBOSE;
|
| ︙ | ︙ | |||
2405 2406 2407 2408 2409 2410 2411 |
blob_zero(&downloadName);
if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL;
object_description(rid, objdescFlags, 0, &downloadName);
style_submenu_element("Download", "%R/raw/%s?at=%T",
zUuid, file_tail(blob_str(&downloadName)));
@ <hr>
content_get(rid, &content);
| | | | 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 |
blob_zero(&downloadName);
if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL;
object_description(rid, objdescFlags, 0, &downloadName);
style_submenu_element("Download", "%R/raw/%s?at=%T",
zUuid, file_tail(blob_str(&downloadName)));
@ <hr>
content_get(rid, &content);
if( blob_size(&content)>100000 ){
/* Prevent robots from running hexdump on megabyte-sized source files
** and there by eating up lots of CPU time and bandwidth. There is
** no good reason for a robot to need a hexdump. */
@ <p>A hex dump of this file is not available because it is too large.
@ Please download the raw binary file and generate a hex dump yourself.</p>
}else{
@ <blockquote><pre>
hexdump(&content);
@ </pre></blockquote>
}
style_finish_page();
|
| ︙ | ︙ | |||
2913 2914 2915 2916 2917 2918 2919 |
const char *zDate = db_column_text(&q,1);
const char *zIp = db_column_text(&q,2);
@ <p>Received on %s(zDate) from %h(zUser) at %h(zIp).</p>
}
db_finalize(&q);
}
if( !docOnly ){
| | > | 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 |
const char *zDate = db_column_text(&q,1);
const char *zIp = db_column_text(&q,2);
@ <p>Received on %s(zDate) from %h(zUser) at %h(zIp).</p>
}
db_finalize(&q);
}
if( !docOnly ){
style_submenu_element("Download", "%R/raw/%s?at=%T",
zUuid, file_tail(blob_str(&downloadName)));
if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){
style_submenu_element("Check-ins Using", "%R/timeline?uf=%s", zUuid);
}
}
if( zMime ){
if( fossil_strcmp(zMime, "text/html")==0 ){
if( asText ){
|
| ︙ | ︙ |
Changes to src/json.c.
| ︙ | ︙ | |||
76 77 78 79 80 81 82 |
/* When running in server/cgi "directory" mode, zPathInfo is
** prefixed with the repository's name, so in order to determine
** whether or not we're really running in json mode we have to try
** a bit harder. Problem reported here:
** https://fossil-scm.org/forum/forumpost/e4953666d6
*/
ReCompiled * pReg = 0;
| | | 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
/* When running in server/cgi "directory" mode, zPathInfo is
** prefixed with the repository's name, so in order to determine
** whether or not we're really running in json mode we have to try
** a bit harder. Problem reported here:
** https://fossil-scm.org/forum/forumpost/e4953666d6
*/
ReCompiled * pReg = 0;
const char * zErr = fossil_re_compile(&pReg, "^/[^/]+/json(/.*)?", 0);
assert(zErr==0 && "Regex compilation failed?");
if(zErr==0 &&
re_match(pReg, (const unsigned char *)zPathInfo, -1)){
rc = 2;
}
re_free(pReg);
}
|
| ︙ | ︙ |
Changes to src/login.c.
| ︙ | ︙ | |||
158 159 160 161 162 163 164 165 166 167 168 169 170 171 |
int uid; /* The user ID of anonymous */
int n = 0; /* Counter of captcha-secrets */
if( zUsername==0 ) return 0;
else if( zPassword==0 ) return 0;
else if( zCS==0 ) return 0;
else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0;
while( 1/*exit-by-break*/ ){
zPw = captcha_decode((unsigned int)atoi(zCS), n);
if( zPw==0 ) return 0;
if( fossil_stricmp(zPw, zPassword)==0 ) break;
n++;
}
uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'"
| > | 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
int uid; /* The user ID of anonymous */
int n = 0; /* Counter of captcha-secrets */
if( zUsername==0 ) return 0;
else if( zPassword==0 ) return 0;
else if( zCS==0 ) return 0;
else if( fossil_strcmp(zUsername,"anonymous")!=0 ) return 0;
else if( anon_cookie_lifespan()==0 ) return 0;
while( 1/*exit-by-break*/ ){
zPw = captcha_decode((unsigned int)atoi(zCS), n);
if( zPw==0 ) return 0;
if( fossil_stricmp(zPw, zPassword)==0 ) break;
n++;
}
uid = db_int(0, "SELECT uid FROM user WHERE login='anonymous'"
|
| ︙ | ︙ | |||
336 337 338 339 340 341 342 343 344 345 346 347 |
fossil_free(zHash);
if( zDest ){
*zDest = zCookie;
}else{
free(zCookie);
}
}
/* Sets a cookie for an anonymous user login, which looks like this:
**
** HASH/TIME/anonymous
**
| > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | > | | 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 |
fossil_free(zHash);
if( zDest ){
*zDest = zCookie;
}else{
free(zCookie);
}
}
/*
** SETTING: anon-cookie-lifespan width=10 default=480
** The number of minutes for which an anonymous login cookie is
** valid. Anonymous logins are prohibited if this value is zero.
*/
/*
** The default lifetime of an anoymous cookie, in minutes.
*/
#define ANONYMOUS_COOKIE_LIFESPAN (8*60)
/*
** Return the lifetime of an anonymous cookie, in minutes.
*/
int anon_cookie_lifespan(void){
static int lifespan = -1;
if( lifespan<0 ){
lifespan = db_get_int("anon-cookie-lifespan", ANONYMOUS_COOKIE_LIFESPAN);
if( lifespan<0 ) lifespan = 0;
}
return lifespan;
}
/* Sets a cookie for an anonymous user login, which looks like this:
**
** HASH/TIME/anonymous
**
** Where HASH is the sha1sum of TIME/USERAGENT/SECRET, in which SECRET
** is captcha-secret and USERAGENT is the HTTP_USER_AGENT value.
**
** If zCookieDest is not NULL then the generated cookie is assigned to
** *zCookieDest and the caller must eventually free() it.
**
** If bSessionCookie is true, the cookie will be a session cookie.
**
** Search for tag-20250817a to find the code that recognizes this cookie.
*/
void login_set_anon_cookie(char **zCookieDest, int bSessionCookie){
char *zNow; /* Current time (julian day number) */
char *zCookie; /* The login cookie */
const char *zUserAgent; /* The user agent */
const char *zCookieName; /* Name of the login cookie */
Blob b; /* Blob used during cookie construction */
int expires = bSessionCookie ? 0 : anon_cookie_lifespan();
zCookieName = login_cookie_name();
zNow = db_text("0", "SELECT julianday('now')");
assert( zCookieName && zNow );
blob_init(&b, zNow, -1);
zUserAgent = PD("HTTP_USER_AGENT","nil");
blob_appendf(&b, "/%s/%z", zUserAgent, captcha_secret(0));
sha1sum_blob(&b, &b);
zCookie = mprintf("%s/%s/anonymous", blob_buffer(&b), zNow);
blob_reset(&b);
cgi_set_cookie(zCookieName, zCookie, login_cookie_path(), expires);
if( zCookieDest ){
*zCookieDest = zCookie;
}else{
|
| ︙ | ︙ | |||
579 580 581 582 583 584 585 |
if( P("pwreset")!=0 && login_self_password_reset_available() ){
/* If the "Reset Password" button in the form was pressed, render
** the Request Password Reset page in place of this one. */
login_reqpwreset_page();
return;
}
| > > > > > > > > > > > > > > > | > > > | | 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 |
if( P("pwreset")!=0 && login_self_password_reset_available() ){
/* If the "Reset Password" button in the form was pressed, render
** the Request Password Reset page in place of this one. */
login_reqpwreset_page();
return;
}
/* If the "anon" query parameter is 1 or 2, that means rework the web-page
** to make it a more user-friendly captcha. Extraneous text and boxes
** are omitted. The user has just the captcha image and an entry box
** and a "Verify" button. Underneath is the same login page for user
** "anonymous", just displayed in an easier to digest format for one-time
** visitors.
**
** anon=1 is advisory and only has effect if there is not some other login
** cookie. anon=2 means always show the captcha.
*/
anonFlag = anon_cookie_lifespan()>0 ? atoi(PD("anon","0")) : 0;
if( anonFlag==2 ){
g.zLogin = 0;
}else{
login_check_credentials();
if( g.zLogin!=0 ) anonFlag = 0;
}
fossil_redirect_to_https_if_needed(1);
sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
constant_time_cmp_function, 0, 0);
zUsername = P("u");
zPasswd = P("p");
/* Handle log-out requests */
if( P("out") && cgi_csrf_safe(2) ){
login_clear_login_data();
login_redirect_to_g();
return;
}
|
| ︙ | ︙ | |||
715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 |
*/
login_set_user_cookie(zUsername, uid, NULL, rememberMe?0:1);
login_redirect_to_g();
}
}
style_set_current_feature("login");
style_header("Login/Logout");
style_adunit_config(ADUNIT_OFF);
@ %s(zErrMsg)
if( zGoto && !noAnon ){
char *zAbbrev = fossil_strdup(zGoto);
int i;
for(i=0; zAbbrev[i] && zAbbrev[i]!='?'; i++){}
zAbbrev[i] = 0;
if( g.zLogin ){
@ <p>Use a different login with greater privilege than <b>%h(g.zLogin)</b>
@ to access <b>%h(zAbbrev)</b>.
}else if( anonFlag ){
| > | | > | | > | | | | | > | > > > > > | > | | | | | | | | | | > > > > > > | | > | | > | | > | | | | | > > | 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 |
*/
login_set_user_cookie(zUsername, uid, NULL, rememberMe?0:1);
login_redirect_to_g();
}
}
style_set_current_feature("login");
style_header("Login/Logout");
if( anonFlag==2 ) g.zLogin = 0;
style_adunit_config(ADUNIT_OFF);
@ %s(zErrMsg)
if( zGoto && !noAnon ){
char *zAbbrev = fossil_strdup(zGoto);
int i;
for(i=0; zAbbrev[i] && zAbbrev[i]!='?'; i++){}
zAbbrev[i] = 0;
if( g.zLogin ){
@ <p>Use a different login with greater privilege than <b>%h(g.zLogin)</b>
@ to access <b>%h(zAbbrev)</b>.
}else if( anonFlag ){
@ <p><b>Verify that you are human by typing in the 8-character text
@ password shown below.</b></p>
}else{
@ <p>Login as a named user to access page <b>%h(zAbbrev)</b>.
}
fossil_free(zAbbrev);
}
if( g.sslNotAvailable==0
&& strncmp(g.zBaseURL,"https:",6)!=0
&& db_get_boolean("https-login",0)
){
form_begin(0, "https:%s/login", g.zBaseURL+5);
}else{
form_begin(0, "%R/login");
}
if( zGoto ){
@ <input type="hidden" name="g" value="%h(zGoto)">
}
if( anonFlag ){
@ <input type="hidden" name="anon" value="1">
@ <input type="hidden" name="u" value="anonymous">
}
if( g.zLogin ){
@ <p>Currently logged in as <b>%h(g.zLogin)</b>.
@ <input type="submit" name="out" value="Logout" autofocus></p>
@ </form>
}else{
unsigned int uSeed = captcha_seed();
if( g.zLogin==0 && (anonFlag || zGoto==0) && anon_cookie_lifespan()>0 ){
zAnonPw = db_text(0, "SELECT pw FROM user"
" WHERE login='anonymous'"
" AND cap!=''");
}else{
zAnonPw = 0;
}
@ <table class="login_out">
if( P("HTTPS")==0 && !anonFlag ){
@ <tr><td class="form_label">Warning:</td>
@ <td><span class='securityWarning'>
@ Login information, including the password,
@ will be sent in the clear over an unencrypted connection.
if( !g.sslNotAvailable ){
@ Consider logging in at
@ <a href='%s(g.zHttpsURL)'>%h(g.zHttpsURL)</a> instead.
}
@ </span></td></tr>
}
if( !anonFlag ){
@ <tr>
@ <td class="form_label" id="userlabel1">User ID:</td>
@ <td><input type="text" id="u" aria-labelledby="userlabel1" name="u" \
@ size="30" value="" autofocus></td>
@ </tr>
}
@ <tr>
@ <td class="form_label" id="pswdlabel">Password:</td>
@ <td><input aria-labelledby="pswdlabel" type="password" id="p" \
@ name="p" value="" size="30"%s(anonFlag ? " autofocus" : "")>
if( anonFlag ){
@ </td></tr>
@ <tr>
@ <td></td><td>\
captcha_speakit_button(uSeed, "Read the password out loud");
}else if( zAnonPw && !noAnon ){
captcha_speakit_button(uSeed, "Speak password for \"anonymous\"");
}
@ </td>
@ </tr>
if( !anonFlag ){
@ <tr>
@ <td></td>
@ <td><input type="checkbox" name="remember" value="1" \
@ id="remember-me" %s(rememberMe ? "checked=\"checked\"" : "")>
@ <label for="remember-me">Remember me?</label></td>
@ </tr>
@ <tr>
@ <td></td>
@ <td><input type="submit" name="in" value="Login">
@ </tr>
}else{
@ <tr>
@ <td></td>
@ <td><input type="submit" name="in" value="Verify that I am human">
@ </tr>
}
if( !anonFlag && !noAnon && login_self_register_available(0) ){
@ <tr>
@ <td></td>
@ <td><input type="submit" name="self" value="Create A New Account">
@ </tr>
}
if( !anonFlag && login_self_password_reset_available() ){
@ <tr>
@ <td></td>
@ <td><input type="submit" name="pwreset" value="Reset My Password">
@ </tr>
}
@ </table>
if( zAnonPw && !noAnon ){
const char *zDecoded = captcha_decode(uSeed, 0);
int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
char *zCaptcha = captcha_render(zDecoded);
@ <p><input type="hidden" name="cs" value="%u(uSeed)">
if( !anonFlag ){
@ Visitors may enter <b>anonymous</b> as the user-ID with
@ the 8-character hexadecimal password shown below:</p>
}
@ <div class="captcha"><table class="captcha"><tr><td>\
@ <pre class="captcha">
@ %h(zCaptcha)
@ </pre></td></tr></table>
if( bAutoCaptcha && !anonFlag ) {
@ <input type="button" value="Fill out captcha" id='autofillButton' \
@ data-af='%s(zDecoded)'>
builtin_request_js("login.js");
}
@ </div>
free(zCaptcha);
}
@ </form>
}
if( login_is_individual() && !anonFlag ){
if( g.perm.EmailAlert && alert_enabled() ){
@ <hr>
@ <p>Configure <a href="%R/alerts">Email Alerts</a>
@ for user <b>%h(g.zLogin)</b></p>
}
if( db_table_exists("repository","forumpost") ){
@ <hr><p>
@ <a href="%R/timeline?ss=v&y=f&vfx&u=%t(g.zLogin)">Forum
@ post timeline</a> for user <b>%h(g.zLogin)</b></p>
}
}
if( !anonFlag ){
@ <hr><p>
@ Select your preferred <a href="%R/skins">site skin</a>.
@ </p>
@ <hr><p>
@ Manage your <a href="%R/cookies">cookies</a> or your
@ <a href="%R/tokens">access tokens</a>.</p>
}
if( login_is_individual() ){
if( g.perm.Password ){
char *zRPW = fossil_random_password(12);
@ <hr>
@ <p>Change Password for user <b>%h(g.zLogin)</b>:</p>
form_begin(0, "%R/login");
@ <table>
|
| ︙ | ︙ | |||
1260 1261 1262 1263 1264 1265 1266 |
fossil_exit(0);
}
}
fossil_free(zDecode);
return uid;
}
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 |
fossil_exit(0);
}
}
fossil_free(zDecode);
return uid;
}
/*
** When this routine is called, we know that the request does not
** have a login on the present repository. This routine checks to
** see if their login cookie might be for another member of the
** login-group.
**
** If this repository is not a part of any login group, then this
|
| ︙ | ︙ | |||
1386 1387 1388 1389 1390 1391 1392 | ** is valid. If the login cookie checks out, it then sets global ** variables appropriately. ** ** g.userUid Database USER.UID value. Might be -1 for "nobody" ** g.zLogin Database USER.LOGIN value. NULL for user "nobody" ** g.perm Permissions granted to this user ** g.anon Permissions that would be available to anonymous | | | 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 |
** is valid. If the login cookie checks out, it then sets global
** variables appropriately.
**
** g.userUid Database USER.UID value. Might be -1 for "nobody"
** g.zLogin Database USER.LOGIN value. NULL for user "nobody"
** g.perm Permissions granted to this user
** g.anon Permissions that would be available to anonymous
** g.isRobot True if the client is known to be a spider or robot
** g.perm Populated based on user account's capabilities
**
*/
void login_check_credentials(void){
int uid = 0; /* User id */
const char *zCookie; /* Text of the login cookie */
const char *zIpAddr; /* Raw IP address of the requestor */
|
| ︙ | ︙ | |||
1427 1428 1429 1430 1431 1432 1433 |
uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin);
}else{
uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
}
g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
zCap = "sxy";
g.noPswd = 1;
| | | 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 |
uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin);
}else{
uid = db_int(0, "SELECT uid FROM user WHERE cap LIKE '%%s%%'");
}
g.zLogin = db_text("?", "SELECT login FROM user WHERE uid=%d", uid);
zCap = "sxy";
g.noPswd = 1;
g.isRobot = 0;
zSeed = db_text("??", "SELECT uid||quote(login)||quote(pw)||quote(cookie)"
" FROM user WHERE uid=%d", uid);
login_create_csrf_secret(zSeed);
fossil_free(zSeed);
}
/* Check the login cookie to see if it matches a known valid user.
|
| ︙ | ︙ | |||
1455 1456 1457 1458 1459 1460 1461 |
zUser = &zHash[i];
break;
}
}
}
if( zUser==0 ){
/* Invalid cookie */
| | > | > | > | > > | | | | 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 |
zUser = &zHash[i];
break;
}
}
}
if( zUser==0 ){
/* Invalid cookie */
}else if( fossil_strcmp(zUser, "anonymous")==0
&& anon_cookie_lifespan()>0 ){
/* Cookies of the form "HASH/TIME/anonymous". The TIME must
** not be more than ANONYMOUS_COOKIE_LIFESPAN seconds ago and
** the sha1 hash of TIME/USERAGENT/SECRET must match HASH. USERAGENT
** is the HTTP_USER_AGENT of the client and SECRET is the
** "captcha-secret" value in the repository. See tag-20250817a
** for the code the creates this cookie.
*/
double rTime = atof(zArg);
const char *zUserAgent = PD("HTTP_USER_AGENT","nil");
Blob b;
char *zSecret;
int n = 0;
do{
blob_zero(&b);
zSecret = captcha_secret(n++);
if( zSecret==0 ) break;
blob_appendf(&b, "%s/%s/%s", zArg, zUserAgent, zSecret);
sha1sum_blob(&b, &b);
if( fossil_strcmp(zHash, blob_str(&b))==0 ){
uid = db_int(0,
"SELECT uid FROM user WHERE login='anonymous'"
" AND octet_length(cap)>0"
" AND octet_length(pw)>0"
" AND %.17g>julianday('now')",
rTime+anon_cookie_lifespan()/1440.0
);
}
}while( uid==0 );
blob_reset(&b);
}else{
/* Cookies of the form "HASH/CODE/USER". Search first in the
** local user table, then the user table for project CODE if we
|
| ︙ | ︙ | |||
1557 1558 1559 1560 1561 1562 1563 |
zCap = "";
}
login_create_csrf_secret("none");
}
login_set_uid(uid, zCap);
| | | > > > | 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 |
zCap = "";
}
login_create_csrf_secret("none");
}
login_set_uid(uid, zCap);
/* Maybe restrict access by robots */
if( g.zLogin==0 && robot_restrict(g.zPath) ){
cgi_reply();
fossil_exit(0);
}
}
/*
** Set the current logged in user to be uid. zCap is precomputed
** (override) capabilities. If zCap==0, then look up the capabilities
** in the USER table.
*/
|
| ︙ | ︙ | |||
1597 1598 1599 1600 1601 1602 1603 |
** "nobody" user is a special case in that g.zLogin==0.
*/
g.userUid = uid;
if( fossil_strcmp(g.zLogin,"nobody")==0 ){
g.zLogin = 0;
}
if( PB("isrobot") ){
| | | | | | 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 |
** "nobody" user is a special case in that g.zLogin==0.
*/
g.userUid = uid;
if( fossil_strcmp(g.zLogin,"nobody")==0 ){
g.zLogin = 0;
}
if( PB("isrobot") ){
g.isRobot = 1;
}else if( g.zLogin==0 ){
g.isRobot = !isHuman(P("HTTP_USER_AGENT"));
}else{
g.isRobot = 0;
}
/* Set the capabilities */
login_replace_capabilities(zCap, 0);
/* The auto-hyperlink setting allows hyperlinks to be displayed for users
** who do not have the "h" permission as long as their UserAgent string
** makes it appear that they are human. Check to see if auto-hyperlink is
** enabled for this repository and make appropriate adjustments to the
** permission flags if it is. This should be done before the permissions
** are (potentially) copied to the anonymous permission set; otherwise,
** those will be out-of-sync.
*/
if( zCap[0] && !g.perm.Hyperlink && !g.isRobot ){
int autoLink = db_get_int("auto-hyperlink",1);
if( autoLink==1 ){
g.jsHref = 1;
g.perm.Hyperlink = 1;
}else if( autoLink==2 ){
g.perm.Hyperlink = 1;
}
|
| ︙ | ︙ | |||
1925 1926 1927 1928 1929 1930 1931 |
blob_appendf(&redir, "%s/login?g=%T", g.zHttpsURL, zPathInfo);
}else{
blob_appendf(&redir, "%R/login?g=%T", zPathInfo);
}
if( zQS && zQS[0] ){
blob_appendf(&redir, "%%3f%T", zQS);
}
| | | | 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 |
blob_appendf(&redir, "%s/login?g=%T", g.zHttpsURL, zPathInfo);
}else{
blob_appendf(&redir, "%R/login?g=%T", zPathInfo);
}
if( zQS && zQS[0] ){
blob_appendf(&redir, "%%3f%T", zQS);
}
if( anonOk ) blob_append(&redir, "&anon=1", 7);
cgi_redirect(blob_str(&redir));
/* NOTREACHED */
assert(0);
}
}
/*
** Call this routine if the user lacks g.perm.Hyperlink permission. If
** the anonymous user has Hyperlink permission, then paint a mesage
** to inform the user that much more information is available by
** logging in as anonymous.
*/
void login_anonymous_available(void){
if( !g.perm.Hyperlink && g.anon.Hyperlink && anon_cookie_lifespan()>0 ){
const char *zUrl = PD("PATH_INFO", "");
@ <p>Many <span class="disabled">hyperlinks are disabled.</span><br>
@ Use <a href="%R/login?anon=1&g=%T(zUrl)">anonymous login</a>
@ to enable hyperlinks.</p>
}
}
|
| ︙ | ︙ |
Changes to src/main.c.
| ︙ | ︙ | |||
231 232 233 234 235 236 237 |
#if USE_SEE
const char *zPidKey; /* Saved value of the --usepidkey option. Only
* applicable when using SEE on Windows or Linux. */
#endif
int useLocalauth; /* No login required if from 127.0.0.1 */
int noPswd; /* Logged in without password (on 127.0.0.1) */
int userUid; /* Integer user id */
| | > | 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
#if USE_SEE
const char *zPidKey; /* Saved value of the --usepidkey option. Only
* applicable when using SEE on Windows or Linux. */
#endif
int useLocalauth; /* No login required if from 127.0.0.1 */
int noPswd; /* Logged in without password (on 127.0.0.1) */
int userUid; /* Integer user id */
int isRobot; /* True if the client is definitely a robot. False
** negatives are common for this flag */
int comFmtFlags; /* Zero or more "COMMENT_PRINT_*" bit flags, should be
** accessed through get_comment_format(). */
const char *zSockName; /* Name of the unix-domain socket file */
const char *zSockMode; /* File permissions for unix-domain socket */
const char *zSockOwner; /* Owner, or owner:group for unix-domain socket */
/* Information used to populate the RCVFROM table */
|
| ︙ | ︙ | |||
2437 2438 2439 2440 2441 2442 2443 | ** REPO for a check-in or ticket that matches the ** value of "name", then redirect to URL. There ** can be multiple "redirect:" lines that are ** processed in order. If the REPO is "*", then ** an unconditional redirect to URL is taken. ** When "*" is used a 301 permanent redirect is ** issued and the tail and query string from the | | | 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 | ** REPO for a check-in or ticket that matches the ** value of "name", then redirect to URL. There ** can be multiple "redirect:" lines that are ** processed in order. If the REPO is "*", then ** an unconditional redirect to URL is taken. ** When "*" is used a 301 permanent redirect is ** issued and the tail and query string from the ** original query are appended onto URL. ** ** jsmode: VALUE Specifies the delivery mode for JavaScript ** files. See the help text for the --jsmode ** flag of the http command. ** ** mainmenu: FILE Override the mainmenu config setting with the ** contents of the given file. |
| ︙ | ︙ | |||
3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 |
** using this command interactively over SSH. A better solution would be
** to use a different command for "ssh" sync, but we cannot do that without
** breaking legacy.
**
** Options:
** --csrf-safe N Set cgi_csrf_safe() to to return N
** --nobody Pretend to be user "nobody"
** --test Do not do special "sync" processing when operating
** over an SSH link
** --th-trace Trace TH1 execution (for debugging purposes)
** --usercap CAP User capability string (Default: "sxy")
*/
void cmd_test_http(void){
const char *zIpAddr; /* IP address of remote client */
const char *zUserCap;
int bTest = 0;
const char *zCsrfSafe = find_option("csrf-safe",0,1);
Th_InitTraceLog();
if( zCsrfSafe ) g.okCsrf = atoi(zCsrfSafe);
zUserCap = find_option("usercap",0,1);
if( !find_option("nobody",0,0) ){
if( zUserCap==0 ){
g.useLocalauth = 1;
zUserCap = "sxy";
| > > > > | 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 |
** using this command interactively over SSH. A better solution would be
** to use a different command for "ssh" sync, but we cannot do that without
** breaking legacy.
**
** Options:
** --csrf-safe N Set cgi_csrf_safe() to to return N
** --nobody Pretend to be user "nobody"
** --ssh-sim Pretend to be over an SSH connection
** --test Do not do special "sync" processing when operating
** over an SSH link
** --th-trace Trace TH1 execution (for debugging purposes)
** --usercap CAP User capability string (Default: "sxy")
*/
void cmd_test_http(void){
const char *zIpAddr; /* IP address of remote client */
const char *zUserCap;
int bTest = 0;
const char *zCsrfSafe = find_option("csrf-safe",0,1);
if( find_option("ssh-sim",0,0)!=0 ){
putenv("SSH_CONNECTION=127.0.0.1 12345 127.0.0.2 23456");
}
Th_InitTraceLog();
if( zCsrfSafe ) g.okCsrf = atoi(zCsrfSafe);
zUserCap = find_option("usercap",0,1);
if( !find_option("nobody",0,0) ){
if( zUserCap==0 ){
g.useLocalauth = 1;
zUserCap = "sxy";
|
| ︙ | ︙ |
Changes to src/main.mk.
| ︙ | ︙ | |||
117 118 119 120 121 122 123 124 125 126 127 128 129 130 | $(SRCDIR)/printf.c \ $(SRCDIR)/publish.c \ $(SRCDIR)/purge.c \ $(SRCDIR)/rebuild.c \ $(SRCDIR)/regexp.c \ $(SRCDIR)/repolist.c \ $(SRCDIR)/report.c \ $(SRCDIR)/rss.c \ $(SRCDIR)/schema.c \ $(SRCDIR)/search.c \ $(SRCDIR)/security_audit.c \ $(SRCDIR)/setup.c \ $(SRCDIR)/setupuser.c \ $(SRCDIR)/sha1.c \ | > | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | $(SRCDIR)/printf.c \ $(SRCDIR)/publish.c \ $(SRCDIR)/purge.c \ $(SRCDIR)/rebuild.c \ $(SRCDIR)/regexp.c \ $(SRCDIR)/repolist.c \ $(SRCDIR)/report.c \ $(SRCDIR)/robot.c \ $(SRCDIR)/rss.c \ $(SRCDIR)/schema.c \ $(SRCDIR)/search.c \ $(SRCDIR)/security_audit.c \ $(SRCDIR)/setup.c \ $(SRCDIR)/setupuser.c \ $(SRCDIR)/sha1.c \ |
| ︙ | ︙ | |||
234 235 236 237 238 239 240 241 242 243 244 245 246 247 | $(SRCDIR)/fossil.numbered-lines.js \ $(SRCDIR)/fossil.page.brlist.js \ $(SRCDIR)/fossil.page.chat.js \ $(SRCDIR)/fossil.page.fileedit.js \ $(SRCDIR)/fossil.page.forumpost.js \ $(SRCDIR)/fossil.page.pikchrshow.js \ $(SRCDIR)/fossil.page.pikchrshowasm.js \ $(SRCDIR)/fossil.page.whistory.js \ $(SRCDIR)/fossil.page.wikiedit.js \ $(SRCDIR)/fossil.pikchr.js \ $(SRCDIR)/fossil.popupwidget.js \ $(SRCDIR)/fossil.storage.js \ $(SRCDIR)/fossil.tabs.js \ $(SRCDIR)/fossil.wikiedit-wysiwyg.js \ | > | 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 | $(SRCDIR)/fossil.numbered-lines.js \ $(SRCDIR)/fossil.page.brlist.js \ $(SRCDIR)/fossil.page.chat.js \ $(SRCDIR)/fossil.page.fileedit.js \ $(SRCDIR)/fossil.page.forumpost.js \ $(SRCDIR)/fossil.page.pikchrshow.js \ $(SRCDIR)/fossil.page.pikchrshowasm.js \ $(SRCDIR)/fossil.page.ticket.js \ $(SRCDIR)/fossil.page.whistory.js \ $(SRCDIR)/fossil.page.wikiedit.js \ $(SRCDIR)/fossil.pikchr.js \ $(SRCDIR)/fossil.popupwidget.js \ $(SRCDIR)/fossil.storage.js \ $(SRCDIR)/fossil.tabs.js \ $(SRCDIR)/fossil.wikiedit-wysiwyg.js \ |
| ︙ | ︙ | |||
383 384 385 386 387 388 389 390 391 392 393 394 395 396 | $(OBJDIR)/printf_.c \ $(OBJDIR)/publish_.c \ $(OBJDIR)/purge_.c \ $(OBJDIR)/rebuild_.c \ $(OBJDIR)/regexp_.c \ $(OBJDIR)/repolist_.c \ $(OBJDIR)/report_.c \ $(OBJDIR)/rss_.c \ $(OBJDIR)/schema_.c \ $(OBJDIR)/search_.c \ $(OBJDIR)/security_audit_.c \ $(OBJDIR)/setup_.c \ $(OBJDIR)/setupuser_.c \ $(OBJDIR)/sha1_.c \ | > | 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 | $(OBJDIR)/printf_.c \ $(OBJDIR)/publish_.c \ $(OBJDIR)/purge_.c \ $(OBJDIR)/rebuild_.c \ $(OBJDIR)/regexp_.c \ $(OBJDIR)/repolist_.c \ $(OBJDIR)/report_.c \ $(OBJDIR)/robot_.c \ $(OBJDIR)/rss_.c \ $(OBJDIR)/schema_.c \ $(OBJDIR)/search_.c \ $(OBJDIR)/security_audit_.c \ $(OBJDIR)/setup_.c \ $(OBJDIR)/setupuser_.c \ $(OBJDIR)/sha1_.c \ |
| ︙ | ︙ | |||
533 534 535 536 537 538 539 540 541 542 543 544 545 546 | $(OBJDIR)/printf.o \ $(OBJDIR)/publish.o \ $(OBJDIR)/purge.o \ $(OBJDIR)/rebuild.o \ $(OBJDIR)/regexp.o \ $(OBJDIR)/repolist.o \ $(OBJDIR)/report.o \ $(OBJDIR)/rss.o \ $(OBJDIR)/schema.o \ $(OBJDIR)/search.o \ $(OBJDIR)/security_audit.o \ $(OBJDIR)/setup.o \ $(OBJDIR)/setupuser.o \ $(OBJDIR)/sha1.o \ | > | 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 | $(OBJDIR)/printf.o \ $(OBJDIR)/publish.o \ $(OBJDIR)/purge.o \ $(OBJDIR)/rebuild.o \ $(OBJDIR)/regexp.o \ $(OBJDIR)/repolist.o \ $(OBJDIR)/report.o \ $(OBJDIR)/robot.o \ $(OBJDIR)/rss.o \ $(OBJDIR)/schema.o \ $(OBJDIR)/search.o \ $(OBJDIR)/security_audit.o \ $(OBJDIR)/setup.o \ $(OBJDIR)/setupuser.o \ $(OBJDIR)/sha1.o \ |
| ︙ | ︙ | |||
876 877 878 879 880 881 882 883 884 885 886 887 888 889 | $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \ $(OBJDIR)/publish_.c:$(OBJDIR)/publish.h \ $(OBJDIR)/purge_.c:$(OBJDIR)/purge.h \ $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \ $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \ $(OBJDIR)/repolist_.c:$(OBJDIR)/repolist.h \ $(OBJDIR)/report_.c:$(OBJDIR)/report.h \ $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \ $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \ $(OBJDIR)/search_.c:$(OBJDIR)/search.h \ $(OBJDIR)/security_audit_.c:$(OBJDIR)/security_audit.h \ $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ $(OBJDIR)/setupuser_.c:$(OBJDIR)/setupuser.h \ $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h \ | > | 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 | $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \ $(OBJDIR)/publish_.c:$(OBJDIR)/publish.h \ $(OBJDIR)/purge_.c:$(OBJDIR)/purge.h \ $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \ $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \ $(OBJDIR)/repolist_.c:$(OBJDIR)/repolist.h \ $(OBJDIR)/report_.c:$(OBJDIR)/report.h \ $(OBJDIR)/robot_.c:$(OBJDIR)/robot.h \ $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \ $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \ $(OBJDIR)/search_.c:$(OBJDIR)/search.h \ $(OBJDIR)/security_audit_.c:$(OBJDIR)/security_audit.h \ $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ $(OBJDIR)/setupuser_.c:$(OBJDIR)/setupuser.h \ $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h \ |
| ︙ | ︙ | |||
1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 | $(OBJDIR)/report_.c: $(SRCDIR)/report.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/report.c >$@ $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c $(OBJDIR)/report.h: $(OBJDIR)/headers $(OBJDIR)/rss_.c: $(SRCDIR)/rss.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/rss.c >$@ $(OBJDIR)/rss.o: $(OBJDIR)/rss_.c $(OBJDIR)/rss.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/rss.o -c $(OBJDIR)/rss_.c | > > > > > > > > | 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 | $(OBJDIR)/report_.c: $(SRCDIR)/report.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/report.c >$@ $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c $(OBJDIR)/report.h: $(OBJDIR)/headers $(OBJDIR)/robot_.c: $(SRCDIR)/robot.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/robot.c >$@ $(OBJDIR)/robot.o: $(OBJDIR)/robot_.c $(OBJDIR)/robot.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/robot.o -c $(OBJDIR)/robot_.c $(OBJDIR)/robot.h: $(OBJDIR)/headers $(OBJDIR)/rss_.c: $(SRCDIR)/rss.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/rss.c >$@ $(OBJDIR)/rss.o: $(OBJDIR)/rss_.c $(OBJDIR)/rss.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/rss.o -c $(OBJDIR)/rss_.c |
| ︙ | ︙ |
Changes to src/manifest.c.
| ︙ | ︙ | |||
2732 2733 2734 2735 2736 2737 2738 |
zUuid = fossil_strdup(zTagUuid);
}
}
zName = p->aTag[i].zName;
zValue = p->aTag[i].zValue;
if( strcmp(zName, "*branch")==0 ){
blob_appendf(&comment,
| | | 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 |
zUuid = fossil_strdup(zTagUuid);
}
}
zName = p->aTag[i].zName;
zValue = p->aTag[i].zValue;
if( strcmp(zName, "*branch")==0 ){
blob_appendf(&comment,
" Move to branch [/timeline?r=%t&nd&dp=%!S&unhide | %h].",
zValue, zTagUuid, zValue);
branchMove = 1;
continue;
}else if( strcmp(zName, "*bgcolor")==0 ){
blob_appendf(&comment,
" Change branch background color to \"%h\".", zValue);
continue;
|
| ︙ | ︙ |
Changes to src/markdown.md.
| ︙ | ︙ | |||
186 187 188 189 190 191 192 | > Both a footnote's text and a fragment to which a footnote applies > are subject to further interpretation as Markdown sources. ## Miscellaneous ## > * In-line images are made using **\!\[alt-text\]\(image-URL\)**. > * Use HTML for advanced formatting such as forms, noting that certain | | | 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | > Both a footnote's text and a fragment to which a footnote applies > are subject to further interpretation as Markdown sources. ## Miscellaneous ## > * In-line images are made using **\!\[alt-text\]\(image-URL\)**. > * Use HTML for advanced formatting such as forms, noting that certain > tags are [disallowed in some contexts](/help/safe-html). > * **\<!--** HTML-style comments **-->** are supported. > * Escape special characters (ex: **\[** **\(** **\|** **\***) > using backslash (ex: **\\\[** **\\\(** **\\\|** **\\\***). > * A line consisting of **---**, **\*\*\***, or **\_\_\_** is a horizontal > rule. Spaces and extra **-**/**\***/**_** are allowed. > * Paragraphs enclosed in **\<html\>...\</html\>** is passed through unchanged. > * See [daringfireball.net][] for additional information. |
| ︙ | ︙ |
Changes to src/match.c.
| ︙ | ︙ | |||
140 141 142 143 144 145 146 |
zOne = fossil_strndup(zPat, i);
zPat += i;
if( zPat[0] ) zPat++;
/* Check for regular expression syntax errors. */
if( style==MS_REGEXP ){
ReCompiled *regexp;
| | | 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
zOne = fossil_strndup(zPat, i);
zPat += i;
if( zPat[0] ) zPat++;
/* Check for regular expression syntax errors. */
if( style==MS_REGEXP ){
ReCompiled *regexp;
const char *zFail = fossil_re_compile(®exp, zOne, 0);
if( zFail ){
re_free(regexp);
continue;
}
p->nPattern++;
p->aRe = fossil_realloc(p->aRe, sizeof(p->aRe)*p->nPattern);
p->aRe[p->nPattern-1] = regexp;
|
| ︙ | ︙ | |||
373 374 375 376 377 378 379 |
}
}
/* Check for regular expression syntax errors. */
if( matchStyle==MS_REGEXP ){
ReCompiled *regexp;
char *zTagDup = fossil_strndup(zTag, i);
| | | 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 |
}
}
/* Check for regular expression syntax errors. */
if( matchStyle==MS_REGEXP ){
ReCompiled *regexp;
char *zTagDup = fossil_strndup(zTag, i);
zFail = fossil_re_compile(®exp, zTagDup, 0);
re_free(regexp);
fossil_free(zTagDup);
}
/* Process success and error results. */
if( !zFail ){
/* Incorporate the match word into the output expression. %q is used to
|
| ︙ | ︙ |
Changes to src/pikchrshow.c.
| ︙ | ︙ | |||
460 461 462 463 464 465 466 |
CX(" selected, only that part is evaluated.\n*/\n");
CX("%s</textarea></div>",zContent/*safe-for-%s*/);
} CX("</fieldset><!-- .zone-wrapper.input -->");
CX("<fieldset class='zone-wrapper output'>"); {
CX("<legend><div class='button-bar'>");
CX("<button id='btn-render-mode'>Render Mode</button> ");
CX("<span style='white-space:nowrap'>"
| | | | 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 |
CX(" selected, only that part is evaluated.\n*/\n");
CX("%s</textarea></div>",zContent/*safe-for-%s*/);
} CX("</fieldset><!-- .zone-wrapper.input -->");
CX("<fieldset class='zone-wrapper output'>"); {
CX("<legend><div class='button-bar'>");
CX("<button id='btn-render-mode'>Render Mode</button> ");
CX("<span style='white-space:nowrap'>"
"<button id='preview-copy-button' "
"title='Tap to copy to clipboard.'><span></span></button>"
"<label for='preview-copy-button' "
"title='Tap to copy to clipboard.'></label>"
"</span>");
CX("</div></legend>");
CX("<div id='pikchr-output-wrapper'>");
CX("<div id='pikchr-output'></div>");
CX("<textarea class='hidden' id='pikchr-output-text'></textarea>");
|
| ︙ | ︙ |
Changes to src/printf.c.
| ︙ | ︙ | |||
250 251 252 253 254 255 256 | ** complete input text, not just the display text. No other formatting ** is done. */ /* ** SETTING: timeline-hard-newlines boolean default=off ** ** If enabled, the timeline honors newline characters in check-in comments. | | | 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 | ** complete input text, not just the display text. No other formatting ** is done. */ /* ** SETTING: timeline-hard-newlines boolean default=off ** ** If enabled, the timeline honors newline characters in check-in comments. ** In other words, newlines are converted into <br> for HTML display. ** The default behavior, when this setting is off, is that newlines are ** treated like any other whitespace character. */ /* ** Return an appropriate set of flags for wiki_convert() for displaying ** comments on a timeline. These flag settings are determined by |
| ︙ | ︙ |
Changes to src/regexp.c.
| ︙ | ︙ | |||
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
** A nondeterministic finite automaton (NFA) is used for matching, so the
** performance is bounded by O(N*M) where N is the size of the regular
** expression and M is the size of the input string. The matcher never
** exhibits exponential behavior. Note that the X{p,q} operator expands
** to p copies of X following by q-p copies of X? and that the size of the
** regular expression in the O(N*M) performance bound is computed after
** this expansion.
*/
#include "config.h"
#include "regexp.h"
/* The end-of-input character */
#define RE_EOF 0 /* End of input */
/* The NFA is implemented as sequence of opcodes taken from the following
** set. Each opcode has a single integer argument.
*/
#define RE_OP_MATCH 1 /* Match the one character in the argument */
#define RE_OP_ANY 2 /* Match any one character. (Implements ".") */
#define RE_OP_ANYSTAR 3 /* Special optimized version of .* */
| > > > | 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
** A nondeterministic finite automaton (NFA) is used for matching, so the
** performance is bounded by O(N*M) where N is the size of the regular
** expression and M is the size of the input string. The matcher never
** exhibits exponential behavior. Note that the X{p,q} operator expands
** to p copies of X following by q-p copies of X? and that the size of the
** regular expression in the O(N*M) performance bound is computed after
** this expansion.
**
** To help prevent DoS attacks, the maximum size of the NFA is restricted.
*/
#include "config.h"
#include "regexp.h"
/* The end-of-input character */
#define RE_EOF 0 /* End of input */
#define RE_START 0xfffffff /* Start of input - larger than an UTF-8 */
/* The NFA is implemented as sequence of opcodes taken from the following
** set. Each opcode has a single integer argument.
*/
#define RE_OP_MATCH 1 /* Match the one character in the argument */
#define RE_OP_ANY 2 /* Match any one character. (Implements ".") */
#define RE_OP_ANYSTAR 3 /* Special optimized version of .* */
|
| ︙ | ︙ | |||
78 79 80 81 82 83 84 85 86 87 88 89 90 91 | #define RE_OP_WORD 11 /* Perl word character [A-Za-z0-9_] */ #define RE_OP_NOTWORD 12 /* Not a perl word character */ #define RE_OP_DIGIT 13 /* digit: [0-9] */ #define RE_OP_NOTDIGIT 14 /* Not a digit */ #define RE_OP_SPACE 15 /* space: [ \t\n\r\v\f] */ #define RE_OP_NOTSPACE 16 /* Not a digit */ #define RE_OP_BOUNDARY 17 /* Boundary between word and non-word */ /* Each opcode is a "state" in the NFA */ typedef unsigned short ReStateNumber; /* Because this is an NFA and not a DFA, multiple states can be active at ** once. An instance of the following object records all active states in ** the NFA. The implementation is optimized for the common case where the | > | 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | #define RE_OP_WORD 11 /* Perl word character [A-Za-z0-9_] */ #define RE_OP_NOTWORD 12 /* Not a perl word character */ #define RE_OP_DIGIT 13 /* digit: [0-9] */ #define RE_OP_NOTDIGIT 14 /* Not a digit */ #define RE_OP_SPACE 15 /* space: [ \t\n\r\v\f] */ #define RE_OP_NOTSPACE 16 /* Not a digit */ #define RE_OP_BOUNDARY 17 /* Boundary between word and non-word */ #define RE_OP_ATSTART 18 /* Currently at the start of the string */ /* Each opcode is a "state" in the NFA */ typedef unsigned short ReStateNumber; /* Because this is an NFA and not a DFA, multiple states can be active at ** once. An instance of the following object records all active states in ** the NFA. The implementation is optimized for the common case where the |
| ︙ | ︙ | |||
111 112 113 114 115 116 117 |
struct ReCompiled {
ReInput sIn; /* Regular expression text */
const char *zErr; /* Error message to return */
char *aOp; /* Operators for the virtual machine */
int *aArg; /* Arguments to each operator */
unsigned (*xNextChar)(ReInput*); /* Next character function */
unsigned char zInit[12]; /* Initial text to match */
| | > | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
struct ReCompiled {
ReInput sIn; /* Regular expression text */
const char *zErr; /* Error message to return */
char *aOp; /* Operators for the virtual machine */
int *aArg; /* Arguments to each operator */
unsigned (*xNextChar)(ReInput*); /* Next character function */
unsigned char zInit[12]; /* Initial text to match */
int nInit; /* Number of bytes in zInit */
unsigned nState; /* Number of entries in aOp[] and aArg[] */
unsigned nAlloc; /* Slots allocated for aOp[] and aArg[] */
unsigned mxAlloc; /* Complexity limit */
};
#endif
/* Add a state to the given state set if it is not already there */
static void re_add_state(ReStateSet *pSet, int newState){
unsigned i;
for(i=0; i<pSet->nState; i++) if( pSet->aState[i]==newState ) return;
|
| ︙ | ︙ | |||
142 143 144 145 146 147 148 |
c = (c&0x1f)<<6 | (p->z[p->i++]&0x3f);
if( c<0x80 ) c = 0xfffd;
}else if( (c&0xf0)==0xe0 && p->i+1<p->mx && (p->z[p->i]&0xc0)==0x80
&& (p->z[p->i+1]&0xc0)==0x80 ){
c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f);
p->i += 2;
if( c<=0x7ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd;
| | | 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
c = (c&0x1f)<<6 | (p->z[p->i++]&0x3f);
if( c<0x80 ) c = 0xfffd;
}else if( (c&0xf0)==0xe0 && p->i+1<p->mx && (p->z[p->i]&0xc0)==0x80
&& (p->z[p->i+1]&0xc0)==0x80 ){
c = (c&0x0f)<<12 | ((p->z[p->i]&0x3f)<<6) | (p->z[p->i+1]&0x3f);
p->i += 2;
if( c<=0x7ff || (c>=0xd800 && c<=0xdfff) ) c = 0xfffd;
}else if( (c&0xf8)==0xf0 && p->i+2<p->mx && (p->z[p->i]&0xc0)==0x80
&& (p->z[p->i+1]&0xc0)==0x80 && (p->z[p->i+2]&0xc0)==0x80 ){
c = (c&0x07)<<18 | ((p->z[p->i]&0x3f)<<12) | ((p->z[p->i+1]&0x3f)<<6)
| (p->z[p->i+2]&0x3f);
p->i += 3;
if( c<=0xffff || c>0x10ffff ) c = 0xfffd;
}else{
c = 0xfffd;
|
| ︙ | ︙ | |||
183 184 185 186 187 188 189 |
*/
int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){
ReStateSet aStateSet[2], *pThis, *pNext;
ReStateNumber aSpace[100];
ReStateNumber *pToFree;
unsigned int i = 0;
unsigned int iSwap = 0;
| | > | 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
*/
int re_match(ReCompiled *pRe, const unsigned char *zIn, int nIn){
ReStateSet aStateSet[2], *pThis, *pNext;
ReStateNumber aSpace[100];
ReStateNumber *pToFree;
unsigned int i = 0;
unsigned int iSwap = 0;
int c = RE_START;
int cPrev = 0;
int rc = 0;
ReInput in;
in.z = zIn;
in.i = 0;
in.mx = nIn>=0 ? nIn : (int)strlen((char const*)zIn);
/* Look for the initial prefix match, if there is one. */
if( pRe->nInit ){
unsigned char x = pRe->zInit[0];
while( in.i+pRe->nInit<=in.mx
&& (zIn[in.i]!=x ||
strncmp((const char*)zIn+in.i, (const char*)pRe->zInit, pRe->nInit)!=0)
){
in.i++;
}
if( in.i+pRe->nInit>in.mx ) return 0;
c = RE_START-1;
}
if( pRe->nState<=(sizeof(aSpace)/(sizeof(aSpace[0])*2)) ){
pToFree = 0;
aStateSet[0].aState = aSpace;
}else{
pToFree = fossil_malloc( sizeof(ReStateNumber)*2*pRe->nState );
|
| ︙ | ︙ | |||
229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
pNext->nState = 0;
for(i=0; i<pThis->nState; i++){
int x = pThis->aState[i];
switch( pRe->aOp[x] ){
case RE_OP_MATCH: {
if( pRe->aArg[x]==c ) re_add_state(pNext, x+1);
break;
}
case RE_OP_ANY: {
if( c!=0 ) re_add_state(pNext, x+1);
break;
}
case RE_OP_WORD: {
if( re_word_char(c) ) re_add_state(pNext, x+1);
| > > > > | 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 |
pNext->nState = 0;
for(i=0; i<pThis->nState; i++){
int x = pThis->aState[i];
switch( pRe->aOp[x] ){
case RE_OP_MATCH: {
if( pRe->aArg[x]==c ) re_add_state(pNext, x+1);
break;
}
case RE_OP_ATSTART: {
if( cPrev==RE_START ) re_add_state(pThis, x+1);
break;
}
case RE_OP_ANY: {
if( c!=0 ) re_add_state(pNext, x+1);
break;
}
case RE_OP_WORD: {
if( re_word_char(c) ) re_add_state(pNext, x+1);
|
| ︙ | ︙ | |||
282 283 284 285 286 287 288 |
}
case RE_OP_ACCEPT: {
rc = 1;
goto re_match_end;
}
case RE_OP_CC_EXC: {
if( c==0 ) break;
| | | | 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
}
case RE_OP_ACCEPT: {
rc = 1;
goto re_match_end;
}
case RE_OP_CC_EXC: {
if( c==0 ) break;
/* fall-through */ goto re_op_cc_inc;
}
case RE_OP_CC_INC: re_op_cc_inc: {
int j = 1;
int n = pRe->aArg[x];
int hit = 0;
for(j=1; j>0 && j<n; j++){
if( pRe->aOp[x+j]==RE_OP_CC_VALUE ){
if( pRe->aArg[x+j]==c ){
hit = 1;
|
| ︙ | ︙ | |||
311 312 313 314 315 316 317 |
if( hit ) re_add_state(pNext, x+n);
break;
}
}
}
}
for(i=0; i<pNext->nState; i++){
| > > | > | | | 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 |
if( hit ) re_add_state(pNext, x+n);
break;
}
}
}
}
for(i=0; i<pNext->nState; i++){
int x = pNext->aState[i];
while( pRe->aOp[x]==RE_OP_GOTO ) x += pRe->aArg[x];
if( pRe->aOp[x]==RE_OP_ACCEPT ){ rc = 1; break; }
}
re_match_end:
fossil_free(pToFree);
return rc;
}
/* Resize the opcode and argument arrays for an RE under construction.
*/
static int re_resize(ReCompiled *p, int N){
char *aOp;
int *aArg;
if( N>p->mxAlloc ){ p->zErr = "REGEXP pattern too big"; return 1; }
aOp = fossil_realloc(p->aOp, N*sizeof(p->aOp[0]));
if( aOp==0 ){ p->zErr = "out of memory"; return 1; }
p->aOp = aOp;
aArg = fossil_realloc(p->aArg, N*sizeof(p->aArg[0]));
if( aArg==0 ){ p->zErr = "out of memory"; return 1; }
p->aArg = aArg;
p->nAlloc = N;
return 0;
}
/* Insert a new opcode and argument into an RE under construction. The
** insertion point is just prior to existing opcode iBefore.
|
| ︙ | ︙ | |||
466 467 468 469 470 471 472 |
int iStart;
unsigned c;
const char *zErr;
while( (c = p->xNextChar(&p->sIn))!=0 ){
iStart = p->nState;
switch( c ){
case '|':
| < | 479 480 481 482 483 484 485 486 487 488 489 490 491 492 |
int iStart;
unsigned c;
const char *zErr;
while( (c = p->xNextChar(&p->sIn))!=0 ){
iStart = p->nState;
switch( c ){
case '|':
case ')': {
p->sIn.i--;
return 0;
}
case '(': {
zErr = re_subcompile_re(p);
if( zErr ) return zErr;
|
| ︙ | ︙ | |||
502 503 504 505 506 507 508 509 510 |
re_append(p, RE_OP_FORK, iPrev - p->nState);
break;
}
case '?': {
if( iPrev<0 ) return "'?' without operand";
re_insert(p, iPrev, RE_OP_FORK, p->nState - iPrev+1);
break;
}
case '{': {
| > > > > > > > > | | | > > > > | > > > | > | > | | | 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 |
re_append(p, RE_OP_FORK, iPrev - p->nState);
break;
}
case '?': {
if( iPrev<0 ) return "'?' without operand";
re_insert(p, iPrev, RE_OP_FORK, p->nState - iPrev+1);
break;
}
case '$': {
re_append(p, RE_OP_MATCH, RE_EOF);
break;
}
case '^': {
re_append(p, RE_OP_ATSTART, 0);
break;
}
case '{': {
unsigned int m = 0, n = 0;
unsigned int sz, j;
if( iPrev<0 ) return "'{m,n}' without operand";
while( (c=rePeek(p))>='0' && c<='9' ){
m = m*10 + c - '0';
if( m*2>p->mxAlloc ) return "REGEXP pattern too big";
p->sIn.i++;
}
n = m;
if( c==',' ){
p->sIn.i++;
n = 0;
while( (c=rePeek(p))>='0' && c<='9' ){
n = n*10 + c-'0';
if( n*2>p->mxAlloc ) return "REGEXP pattern too big";
p->sIn.i++;
}
}
if( c!='}' ) return "unmatched '{'";
if( n<m ) return "n less than m in '{m,n}'";
p->sIn.i++;
sz = p->nState - iPrev;
if( m==0 ){
if( n==0 ) return "both m and n are zero in '{m,n}'";
re_insert(p, iPrev, RE_OP_FORK, sz+1);
iPrev++;
n--;
}else{
for(j=1; j<m; j++) re_copy(p, iPrev, sz);
}
for(j=m; j<n; j++){
re_append(p, RE_OP_FORK, sz+1);
re_copy(p, iPrev, sz);
}
if( n==0 && m>0 ){
re_append(p, RE_OP_FORK, -(int)sz);
}
break;
}
case '[': {
unsigned int iFirst = p->nState;
if( rePeek(p)=='^' ){
re_append(p, RE_OP_CC_EXC, 0);
p->sIn.i++;
}else{
re_append(p, RE_OP_CC_INC, 0);
}
while( (c = p->xNextChar(&p->sIn))!=0 ){
|
| ︙ | ︙ | |||
559 560 561 562 563 564 565 |
re_append(p, RE_OP_CC_RANGE, c);
}else{
re_append(p, RE_OP_CC_VALUE, c);
}
if( rePeek(p)==']' ){ p->sIn.i++; break; }
}
if( c==0 ) return "unclosed '['";
| | | 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 |
re_append(p, RE_OP_CC_RANGE, c);
}else{
re_append(p, RE_OP_CC_VALUE, c);
}
if( rePeek(p)==']' ){ p->sIn.i++; break; }
}
if( c==0 ) return "unclosed '['";
if( p->nState>iFirst ) p->aArg[iFirst] = p->nState - iFirst;
break;
}
case '\\': {
int specialOp = 0;
switch( rePeek(p) ){
case 'b': specialOp = RE_OP_BOUNDARY; break;
case 'd': specialOp = RE_OP_DIGIT; break;
|
| ︙ | ︙ | |||
610 611 612 613 614 615 616 | /* ** Compile a textual regular expression in zIn[] into a compiled regular ** expression suitable for us by re_match() and return a pointer to the ** compiled regular expression in *ppRe. Return NULL on success or an ** error message if something goes wrong. */ | | > > > > > > > | < < < < | | | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 |
/*
** Compile a textual regular expression in zIn[] into a compiled regular
** expression suitable for us by re_match() and return a pointer to the
** compiled regular expression in *ppRe. Return NULL on success or an
** error message if something goes wrong.
*/
static const char *re_compile(
ReCompiled **ppRe, /* OUT: write compiled NFA here */
const char *zIn, /* Input regular expression */
int mxRe, /* Complexity limit */
int noCase /* True for caseless comparisons */
){
ReCompiled *pRe;
const char *zErr;
int i, j;
*ppRe = 0;
pRe = fossil_malloc( sizeof(*pRe) );
if( pRe==0 ){
return "out of memory";
}
memset(pRe, 0, sizeof(*pRe));
pRe->xNextChar = noCase ? re_next_char_nocase : re_next_char;
pRe->mxAlloc = mxRe;
if( re_resize(pRe, 30) ){
zErr = pRe->zErr;
re_free(pRe);
return zErr;
}
if( zIn[0]=='^' ){
zIn++;
}else{
re_append(pRe, RE_OP_ANYSTAR, 0);
}
pRe->sIn.z = (unsigned char*)zIn;
pRe->sIn.i = 0;
pRe->sIn.mx = (int)strlen(zIn);
zErr = re_subcompile_re(pRe);
if( zErr ){
re_free(pRe);
return zErr;
}
if( pRe->sIn.i>=pRe->sIn.mx ){
re_append(pRe, RE_OP_ACCEPT, 0);
*ppRe = pRe;
}else{
re_free(pRe);
return "unrecognized character";
}
/* The following is a performance optimization. If the regex begins with
** ".*" (if the input regex lacks an initial "^") and afterwards there are
** one or more matching characters, enter those matching characters into
** zInit[]. The re_match() routine can then search ahead in the input
** string looking for the initial match without having to run the whole
** regex engine over the string. Do not worry about trying to match
** unicode characters beyond plane 0 - those are very rare and this is
** just an optimization. */
if( pRe->aOp[0]==RE_OP_ANYSTAR && !noCase ){
for(j=0, i=1; j<(int)sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){
unsigned x = pRe->aArg[i];
if( x<=0x7f ){
pRe->zInit[j++] = (unsigned char)x;
}else if( x<=0x7ff ){
pRe->zInit[j++] = (unsigned char)(0xc0 | (x>>6));
pRe->zInit[j++] = 0x80 | (x&0x3f);
}else if( x<=0xffff ){
pRe->zInit[j++] = (unsigned char)(0xe0 | (x>>12));
pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f);
pRe->zInit[j++] = 0x80 | (x&0x3f);
}else{
break;
}
}
if( j>0 && pRe->zInit[j-1]==0 ) j--;
pRe->nInit = j;
}
return pRe->zErr;
}
/*
** Implementation of the regexp() SQL function. This function implements
** the build-in REGEXP operator. The first argument to the function is the
** pattern and the second argument is the string. So, the SQL statements:
**
** A REGEXP B
**
** is implemented as regexp(B,A).
*/
static void re_sql_func(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
ReCompiled *pRe; /* Compiled regular expression */
const char *zPattern; /* The regular expression */
const unsigned char *zStr;/* String being searched */
const char *zErr; /* Compile error message */
int setAux = 0; /* True to invoke sqlite3_set_auxdata() */
(void)argc; /* Unused */
pRe = sqlite3_get_auxdata(context, 0);
if( pRe==0 ){
zPattern = (const char*)sqlite3_value_text(argv[0]);
if( zPattern==0 ) return;
zErr = fossil_re_compile(&pRe, zPattern, sqlite3_user_data(context)!=0);
if( zErr ){
re_free(pRe);
/* The original SQLite function from which this code was copied raises
** an error if the REGEXP contained a syntax error. This variant
** silently fails to match, as that works better for Fossil.
** sqlite3_result_error(context, zErr, -1); */
sqlite3_result_int(context, 0);
return;
}
if( pRe==0 ){
sqlite3_result_error_nomem(context);
return;
}
setAux = 1;
}
zStr = (const unsigned char*)sqlite3_value_text(argv[1]);
if( zStr!=0 ){
sqlite3_result_int(context, re_match(pRe, zStr, -1));
}
if( setAux ){
sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free);
}
}
/*
** Invoke this routine to register the regexp() function with the
** SQLite database connection.
*/
int re_add_sql_func(sqlite3 *db){
int rc;
rc = sqlite3_create_function(db, "regexp", 2,
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
0, re_sql_func, 0, 0);
if( rc==SQLITE_OK ){
/* The regexpi(PATTERN,STRING) function is a case-insensitive version
** of regexp(PATTERN,STRING). */
rc = sqlite3_create_function(db, "regexpi", 2,
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_DETERMINISTIC,
(void*)db, re_sql_func, 0, 0);
}
return rc;
}
/*
** The input zIn is a string that we want to match exactly as part of
** a regular expression. Return a new string (in space obtained from
** fossil_malloc() or the equivalent) that escapes all regexp syntax
** characters in zIn.
*/
|
| ︙ | ︙ | |||
721 722 723 724 725 726 727 |
zIn++;
}
blob_materialize(&out);
return out.aData;
}
/*
| | < < < > > | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > < > > > | < < | < | < < < | | 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 |
zIn++;
}
blob_materialize(&out);
return out.aData;
}
/*
** SETTING: regexp-limit width=8 default=1000
**
** Limit the size of the bytecode used to implement a regular expression
** to this many steps. It is important to limit this to avoid possible
** DoS attacks.
*/
/*
** Compile an RE using re_maxlen().
*/
const char *fossil_re_compile(
ReCompiled **ppRe, /* OUT: write compiled NFA here */
const char *zIn, /* Input regular expression */
int noCase /* True for caseless comparisons */
){
int mxLen = g.db ? db_get_int("regexp-limit",1000) : 1000;
return re_compile(ppRe, zIn, mxLen, noCase);
}
/*
** Run a "grep" over a single file read from disk.
*/
static void grep_file(ReCompiled *pRe, const char *zFile, FILE *in){
int ln = 0;
|
| ︙ | ︙ | |||
848 849 850 851 852 853 854 855 856 857 858 859 |
** Usage: %fossil test-grep REGEXP [FILE...]
**
** Run a regular expression match over the named disk files, or against
** standard input if no disk files are named on the command-line.
**
** Options:
** -i|--ignore-case Ignore case
*/
void re_test_grep(void){
ReCompiled *pRe;
const char *zErr;
int ignoreCase = find_option("ignore-case","i",0)!=0;
| > > > > > > > > > > > > | | | | > | | | 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 |
** Usage: %fossil test-grep REGEXP [FILE...]
**
** Run a regular expression match over the named disk files, or against
** standard input if no disk files are named on the command-line.
**
** Options:
** -i|--ignore-case Ignore case
** --robot-exception Use the robot-exception setting as the REGEXP
*/
void re_test_grep(void){
ReCompiled *pRe;
const char *zErr;
int iFileList = 3;
int ignoreCase = find_option("ignore-case","i",0)!=0;
int bRobot = find_option("robot-exception",0,0)!=0;
if( bRobot ){
const char *zRe;
db_find_and_open_repository(0,0);
verify_all_options();
zRe = db_get("robot-exception","^$");
zErr = fossil_re_compile(&pRe, zRe, ignoreCase);
iFileList = 2;
}else{
verify_all_options();
if( g.argc<3 ){
usage("REGEXP [FILE...]");
}
zErr = fossil_re_compile(&pRe, g.argv[2], ignoreCase);
}
if( zErr ) fossil_fatal("%s", zErr);
if( g.argc==iFileList ){
grep_file(pRe, "-", stdin);
}else{
int i;
for(i=iFileList; i<g.argc; i++){
FILE *in = fossil_fopen(g.argv[i], "rb");
if( in==0 ){
fossil_warning("cannot open \"%s\"", g.argv[i]);
}else{
grep_file(pRe, g.argv[i], in);
fclose(in);
}
|
| ︙ | ︙ | |||
937 938 939 940 941 942 943 |
flags |= GREP_QUIET|GREP_EXISTS;
}
db_find_and_open_repository(0, 0);
verify_all_options();
if( g.argc<4 ){
usage("REGEXP FILENAME ...");
}
| | | 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 |
flags |= GREP_QUIET|GREP_EXISTS;
}
db_find_and_open_repository(0, 0);
verify_all_options();
if( g.argc<4 ){
usage("REGEXP FILENAME ...");
}
zErr = fossil_re_compile(&pRe, g.argv[2], ignoreCase);
if( zErr ) fossil_fatal("%s", zErr);
add_content_sql_commands(g.db);
db_multi_exec("CREATE TEMP TABLE arglist(iname,fname,fnid);");
for(ii=3; ii<g.argc; ii++){
const char *zTarget = g.argv[ii];
if( file_tree_name(zTarget, &fullName, 0, 1) ){
|
| ︙ | ︙ | |||
1014 1015 1016 1017 1018 1019 1020 |
if( bInvert ){
fossil_print("%d\n", nSearch-nMatch);
}else{
fossil_print("%d\n", nMatch);
}
}
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 |
if( bInvert ){
fossil_print("%d\n", nSearch-nMatch);
}else{
fossil_print("%d\n", nMatch);
}
}
}
/*
** WEBPAGE: re_rules
**
** Show a summary of the regular expression matching rules for Fossil.
*/
void re_rules_page(void){
style_set_current_feature("wiki");
style_header("Regular Expression Syntax");
@ <p>Syntax rules for regular expression matching in Fossil:</p>
@
@ <table border="0" cellpadding="0" cellspacing="0">
@ <tr><th>   <th>Pattern
@ <th>   <th align="left">Match
@ <tr><td><td><i>X</i><b>*</b>
@ <td><td>Zero or more occurrences of <i>X</i>
@ <tr><td><td><i>X</i><b>+</b>
@ <td><td>One or more occurrences of <i>X</i>
@ <tr><td><td><i>X</i><b>?</b>
@ <td><td>Zero or one occurrences of <i>X</i>
@ <tr><td><td><i>X</i><b>{</b><i>P</i><b>,</b><i>Q</i><b>}</b>
@ <td><td>Between P and Q occurrences of <i>X</i>
@ <tr><td><td><b>(</b><i>X</i><b>)</b>
@ <td><td><i>X</i>
@ <tr><td><td><i>X</i><b>|</b><i>Y</i>
@ <td><td><i>X</i> or <i>Y</i>
@ <tr><td><td><b>^</b><i>X</i>
@ <td><td><i>X</i> at the beginning of the string
@ <tr><td><td><i>X</i><b>$</b>
@ <td><td><i>X</i> at the end of the string
@ <tr><td><td><b>.</b>
@ <td><td>Any single character
@ <tr><td><td><b>\</b><i>C</i>
@ <td><td>Character <i>C</i> if <i>C</i> is one of: <b>\{}()[]|*+?</b>
@ <tr><td><td><b>\</b><i>C</i>
@ <td><td>C-language escapes if <i>C</i> is one of: <b>afnrtv</b>
@ <tr><td><td><b>\u</b><i>HHHH</i>
@ <td><td>Unicode character U+HHHH where <i>HHHH</i> is four hex digits
@ <tr><td><td><b>\</b><i>HH</i>
@ <td><td>Unicode character U+00HH where <i>HH</i> is two hex digits
@ <tr><td><td><b>[</b><i>abc</i><b>]</b>
@ <td><td>Any single character from <i>abc</i>
@ <tr><td><td><b>[^</b><i>abc</i><b>]</b>
@ <td><td>Any single character not in <i>abc</i>
@ <tr><td><td><b>[</b><i>a-z</i><b>]</b>
@ <td><td>Any single character between <i>a</i> and <i>z</i>, inclusive
@ <tr><td><td><b>[^</b><i>a-z</i><b>]</b>
@ <td><td>Any single character not between <i>a</i> and <i>z</i>
@ <tr><td><td><b>\b</b>
@ <td><td>Word boundary
@ <tr><td><td><b>\w</b>
@ <td><td>A word character: a-zA-Z0-9 or _
@ <tr><td><td><b>\W</b>
@ <td><td>A non-word character
@ <tr><td><td><b>\d</b>
@ <td><td>A digit. 0-9
@ <tr><td><td><b>\D</b>
@ <td><td>A non-digit character
@ <tr><td><td><b>\s</b>
@ <td><td>A whitespace character
@ <tr><td><td><b>\S</b>
@ <td><td>A non-whitespace character
@ </table>
@
@ <p>In the "Pattern" column of the table above:</p>
@ <ul>
@ <li> "<i>X</i>" and "<i>Y</i>" mean any subpattern
@ <li> "<i>P</i>" and "<i>Q</i>" mean integers
@ <li> "<i>C</i>" means a single character
@ <li> "<i>H</i>" means a hexadecimal digit
@ <li> "<i>abc</i>" means any sequences of one or more characters
@ <li> "<i>a-z</i>" means any single character, a single "<b>-</b>"
@ character, and then one additional character.
@ <li> All other symbols in the patterns are literal text
@ </ul>
@
@ <p>The "<i>X</i><b>|</b><i>Y</i>" pattern has lower precedence
@ than the others. Use "<b>(</b>...<b>)</b>" for grouping, as
@ necessary.
style_finish_page();
}
|
Added src/robot.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 |
/*
** Copyright (c) 2025 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)
** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code that attempts to prevent robots and
** especially bot-nets from consume excess CPU and bandwidth when
** Fossil is run as a service.
*/
#include "config.h"
#include "robot.h"
#include <assert.h>
#include <time.h>
/*
** The name of the cookie used to demonstrate that the client has been
** tested and is believed to be operated by a human, not by a robot.
*/
#if INTERFACE
#define ROBOT_COOKIE "fossil-client-ok"
#endif
/*
** Values computed only once and then cached.
*/
static struct RobotCache {
unsigned int h1, h2; /* Proof-of-work hash values */
unsigned int resultCache; /* 0: unknown. 1: human 2: might-be-robot */
} robot = { 0, 0, 0 };
/*
** Allowed values for robot.resultCache.
**
** The names are slightly misleading. KNOWN_NOT_ROBOT might be set even
** if the client is a robot, but only if the robot is an approved robot.
** A better name might be "KNOWN_NOT_UNAUTHORIZED_ROBOT", but that is too
** long of a name.
*/
#define KNOWN_NOT_ROBOT 1 /* Approved to consume CPU and bandwidth */
#define MIGHT_BE_ROBOT 2 /* Might be an unapproved robot */
/*
** Compute two hashes, robot.h1 and robot.h2, that are used as
** part of determining whether or not the HTTP client is a robot.
** These hashes are based on current time, client IP address,
** and User-Agent. robot.h1 is for the current time slot and
** robot.h2 is the previous.
**
** The hashes are integer values between 100,000,000 and 999,999,999
** inclusive.
*/
static void robot_pow_hash(void){
const char *az[2], *z;
sqlite3_int64 tm;
unsigned int h1, h2, k;
if( robot.h1 ) return; /* Already computed */
/* Construct a proof-of-work value based on the IP address of the
** sender and the sender's user-agent string. The current time also
** affects the pow value, so actually compute two values, one for the
** current 900-second interval and one for the previous. Either can
** match. The pow-value is an integer between 100,000,000 and
** 999,999,999.
*/
az[0] = P("REMOTE_ADDR");
az[1] = P("HTTP_USER_AGENT");
tm = time(0);
h1 = (unsigned)(tm/900)&0xffffffff;
h2 = h1 - 1;
for(k=0; k<2; k++){
z = az[k];
if( z==0 ) continue;
while( *z ){
h1 = (h1 + *(unsigned char*)z)*0x9e3779b1;
h2 = (h2 + *(unsigned char*)z)*0x9e3779b1;
z++;
}
}
robot.h1 = (h1 % 900000000) + 100000000;
robot.h2 = (h2 % 900000000) + 100000000;
}
/*
** Return true if the HTTP client has not demonstrated that it is
** human interactive. Return false is the HTTP client might be
** a non-interactive robot.
**
** For this routine, any of the following is considered proof that
** the HTTP client is not a robot:
**
** 1. There is a valid login, including "anonymous". User "nobody"
** is not a valid login, but every other user is.
**
** 2. There exists a ROBOT_COOKIE with the correct proof-of-work
** value.
**
** 3. There exists a proof=VALUE query parameter where VALUE is
** a correct proof-of-work value.
**
** 4. There exists a valid token=VALUE query parameter.
**
** After being run once, this routine caches its findings and
** returns very quickly on subsequent invocations.
*/
int client_might_be_a_robot(void){
const char *z;
/* Only do this computation once, then cache the results for future
** use */
if( robot.resultCache ){
return robot.resultCache==MIGHT_BE_ROBOT;
}
/* Condition 1: Is there a valid login?
*/
if( g.userUid==0 ){
login_check_credentials();
}
if( g.zLogin!=0 ){
robot.resultCache = KNOWN_NOT_ROBOT;
return 0;
}
/* Condition 2: If there is already a proof-of-work cookie
** with a correct value, then the user agent has been authenticated.
*/
z = P(ROBOT_COOKIE);
if( z ){
unsigned h = atoi(z);
robot_pow_hash();
if( (h==robot.h1 || h==robot.h2) && !cgi_is_qp(ROBOT_COOKIE) ){
robot.resultCache = KNOWN_NOT_ROBOT;
return 0;
}
}
/* Condition 3: There is a "proof=VALUE" query parameter with a valid
** VALUE attached. If this is the case, also set the robot cookie
** so that future requests will hit condition 2 above.
*/
z = P("proof");
if( z ){
unsigned h = atoi(z);
robot_pow_hash();
if( h==robot.h1 || h==robot.h2 ){
cgi_set_cookie(ROBOT_COOKIE,z,"/",900);
robot.resultCache = KNOWN_NOT_ROBOT;
return 0;
}
cgi_tag_query_parameter("proof");
}
/* Condition 4: If there is a "token=VALUE" query parameter with a
** valid VALUE argument, then assume that the request is coming from
** either an interactive human session, or an authorized robot that we
** want to treat as human. All it through and also set the robot cookie.
*/
z = P("token");
if( z!=0 ){
if( db_exists("SELECT 1 FROM config"
" WHERE name='token-%q'"
" AND json_valid(value,6)"
" AND value->>'user' IS NOT NULL", z)
){
char *zVal;
robot_pow_hash();
zVal = mprintf("%u", robot.h1);
cgi_set_cookie(ROBOT_COOKIE,zVal,"/",900);
fossil_free(zVal);
robot.resultCache = KNOWN_NOT_ROBOT;
return 0; /* There is a valid token= query parameter */
}
cgi_tag_query_parameter("token");
}
/* We have no proof that the request is coming from an interactive
** human session, so assume the request comes from a robot.
*/
robot.resultCache = MIGHT_BE_ROBOT;
return 1;
}
/*
** Rewrite the current page with content that attempts
** to prove that the client is not a robot.
*/
static void ask_for_proof_that_client_is_not_robot(void){
unsigned p1, p2, p3, p4, p5, k2, k3;
int k;
/* Ask the client to present proof-of-work */
cgi_reset_content();
cgi_set_content_type("text/html");
style_header("Browser Verification");
@ <h1 id="x1">Checking to see if you are a robot<span id="x2"></span></h1>
@ <form method="GET" id="x6"><p>
@ <span id="x3" style="visibility:hidden;">\
@ Press <input type="submit" id="x5" value="Ok" focus> to continue</span>
@ <span id="x7" style="visibility:hidden;">You appear to be a robot.</span>\
@ </p>
cgi_tag_query_parameter("name");
cgi_query_parameters_to_hidden();
@ <input id="x4" type="hidden" name="proof" value="0">
@ </form>
@ <script nonce='%s(style_nonce())'>
@ function aaa(x){return document.getElementById(x);}\
@ function bbb(h,a){\
@ aaa("x4").value=h;\
@ if((a%%75)==0){\
@ aaa("x2").textContent=aaa("x2").textContent+".";\
@ }var z;\
@ if(a>0){\
@ setTimeout(bbb,1,h+a,a-1);\
@ }else if((z=window.getComputedStyle(document.body).zIndex)==='0'||z===0){\
@ aaa("x3").style.visibility="visible";\
@ aaa("x2").textContent="";\
@ aaa("x1").textContent="All clear";\
@ aaa("x6").onsubmit=function(){aaa("x3").style.visibility="hidden";};\
@ aaa("x5").focus();\
@ }else{\
@ aaa("x7").style.visibility="visible";\
@ aaa("x2").textContent="";\
@ aaa("x3").style.display="none";\
@ aaa("x1").textContent="Access Denied";\
@ }\
@ }\
robot_pow_hash();
k = 400 + robot.h2%299;
k2 = (robot.h2/299)%99 + 973;
k3 = (robot.h2/(299*99))%99 + 811;
p1 = (k*k + k)/2;
p2 = robot.h1-p1;
p3 = p2%k2;
p4 = (p2/k2)%k3;
p5 = p2/(k2*k3);
@ function ccc(a,b,c){return (a*%u(k3)+b)*%u(k2)+c;}\
@ window.addEventListener('load',function(){\
@ bbb(ccc(%u(p5),%u(p4),%u(p3)),%u(k));},false);
/* Prevent successfully completed robot checks from reappearing and force
** incomplete checks to start over when navigating back and forward. More
** information: <https://stackoverflow.com/a/43043658>. */
@ window.addEventListener('pageshow',function(e){if(e.persisted)\
@ window.location.reload();});
@ </script>
style_finish_page();
}
/*
** SETTING: robot-restrict width=40 block-text
** The VALUE of this setting is a list of GLOB patterns that match
** pages for which complex HTTP requests from unauthenticated clients
** should be disallowed. "Unauthenticated" means the user is "nobody".
** The recommended value for this setting is:
**
** timelineX,diff,annotate,fileage,file,finfo,reports,tree,download,hexdump
**
** The "diff" tag covers all diffing pages such as /vdiff, /fdiff, and
** /vpatch. The "annotate" tag also covers /blame and /praise. "zip"
** also covers /tarball and /sqlar. If a tag has an "X" character appended
** then it only applies if query parameters are such that the page is
** particularly difficult to compute. In all other case, the tag should
** exactly match the page name. Useful "X" tags include "timelineX" and
** "zipX". See the [[robot-zip-leaf]] and [[robot-zip-tag]] settings
** for additional controls associated with the "zipX" restriction.
**
** Change this setting "off" to disable all robot restrictions.
*/
/*
** SETTING: robot-exception width=40 block-text
**
** The value of this setting should be a regular expression.
** If it matches the REQUEST_URI without the SCRIPT_NAME prefix
** matches this regular expression, then the request is an exception
** to anti-robot defenses and should be allowed through. For
** example, to allow robots to download tarballs or ZIP archives
** for named versions and releases, you could use an expression like
** this:
**
** ^/tarball/(version-[0-9.]+|release)/
**
** This setting can hold multiple regular expressions, one
** regular expression per line. The input URL is exempted from
** anti-robot defenses if any of the multiple regular expressions
** matches.
*/
/*
** SETTING: robot-zip-leaf boolean
**
** If this setting is true, the robots are allowed to download tarballs,
** ZIP-archives, and SQL-archives even though "zipX" is found in
** the [[robot-restrict]] setting as long as the specific check-in being
** downloaded is a leaf check-in.
*/
/*
** SETTING: robot-zip-tag width=40 block-text
**
** If this setting is a list of GLOB patterns matching tags,
** then robots are allowed to download tarballs, ZIP-archives, and
** SQL-archives even though "zipX" appears in [[robot-restrict]], as long as
** the specific check-in being downloaded has a tags that matches
** the GLOB list of this setting. Recommended value:
** "release,robot-access".
*/
/*
** Return the default restriction GLOB
*/
const char *robot_restrict_default(void){
return "timelineX,diff,annotate,fileage,file,finfo,reports,"
"tree,hexdump,download";
}
/*
** Return true if zTag matches one of the tags in the robot-restrict
** setting.
*/
static int robot_restrict_has_tag(const char *zTag){
static const char *zGlob = 0;
if( zGlob==0 ){
zGlob = db_get("robot-restrict",robot_restrict_default());
if( zGlob==0 ) zGlob = "";
}
if( zGlob[0]==0 || fossil_strcmp(zGlob, "off")==0 ){
return 0;
}
return glob_multi_match(zGlob,zTag);
}
/*
** Check the request URI to see if it matches one of the URI
** exceptions listed in the robot-exception setting. Return true
** if it does. Return false if it does not.
**
** For the purposes of this routine, the "request URI" means
** the REQUEST_URI value with the SCRIPT_NAME prefix removed and
** with QUERY_STRING appended with a "?" separator if QUERY_STRING
** is not empty.
**
** If the robot-exception setting does not exist or is an empty
** string, then return false.
*/
int robot_exception(void){
const char *zRE = db_get("robot-exception",0);
const char *zQS; /* QUERY_STRING */
const char *zURI; /* REQUEST_URI */
const char *zSN; /* SCRIPT_NAME */
const char *zNL; /* Next newline character */
char *zRequest; /* REQUEST_URL w/o SCRIPT_NAME prefix + QUERY_STRING */
int nRequest; /* Length of zRequest in bytes */
size_t nURI, nSN; /* Length of zURI and zSN */
int bMatch = 0; /* True if there is a match */
if( zRE==0 ) return 0;
if( zRE[0]==0 ) return 0;
zURI = PD("REQUEST_URI","");
nURI = strlen(zURI);
zSN = PD("SCRIPT_NAME","");
nSN = strlen(zSN);
if( nSN<=nURI ) zURI += nSN;
zQS = P("QUERY_STRING");
if( zQS && zQS[0] ){
zRequest = mprintf("%s?%s", zURI, zQS);
}else{
zRequest = fossil_strdup(zURI);
}
nRequest = (int)strlen(zRequest);
while( zRE[0] && bMatch==0 ){
char *z;
const char *zErr;
size_t n;
ReCompiled *pRe;
zNL = strchr(zRE,'\n');
if( zNL ){
n = (size_t)(zNL - zRE)+1;
while( zNL>zRE && fossil_isspace(zNL[0]) ) zNL--;
if( zNL==zRE ){
zRE += n;
continue;
}
}else{
n = strlen(zRE);
}
z = mprintf("%.*s", (int)(zNL - zRE)+1, zRE);
zRE += n;
zErr = fossil_re_compile(&pRe, z, 0);
if( zErr ){
fossil_warning("robot-exception error \"%s\" in expression \"%s\"\n",
zErr, z);
fossil_free(z);
continue;
}
fossil_free(z);
bMatch = re_match(pRe, (const unsigned char*)zRequest, nRequest);
re_free(pRe);
}
fossil_free(zRequest);
return bMatch;
}
/*
** Return true if one or more of the conditions below are true.
** Return false if all of the following are false:
**
** * The zTag is on the robot-restrict list
**
** * The client that submitted the HTTP request might be
** a robot
**
** * The Request URI does not match any of the exceptions
** in the robot-exception setting.
**
** In other words, return true if a call to robot_restrict() would
** return true and false if a call to robot_restrict() would return
** false.
**
** The difference between this routine an robot_restrict() is that
** this routine does not generate a proof-of-work captcha. This
** routine does not change the HTTP reply in any way. It simply
** returns true or false.
*/
int robot_would_be_restricted(const char *zTag){
if( robot.resultCache==KNOWN_NOT_ROBOT ) return 0;
if( !robot_restrict_has_tag(zTag) ) return 0;
if( !client_might_be_a_robot() ) return 0;
if( robot_exception() ){
robot.resultCache = KNOWN_NOT_ROBOT;
return 0;
}
return 1;
}
/*
** Check to see if the page named in the argument is on the
** robot-restrict list. If it is on the list and if the user
** is might be a robot, then bring up a captcha to test to make
** sure that client is not a robot.
**
** This routine returns true if a captcha was rendered and if subsequent
** page generation should be aborted. It returns false if the page
** should not be restricted and should be rendered normally.
*/
int robot_restrict(const char *zTag){
if( robot_would_be_restricted(zTag) ){
/* Generate the proof-of-work captcha */
ask_for_proof_that_client_is_not_robot();
return 1;
}else{
return 0;
}
}
/*
** Check to see if a robot is allowed to download a tarball, ZIP archive,
** or SQL Archive for a particular check-in identified by the "rid"
** argument. Return true to block the download. Return false to
** continue. Prior to returning true, a captcha is presented to the user.
** No output is generated when returning false.
**
** The rules:
**
** (1) If "zipX" is missing from the robot-restrict setting, then robots
** are allowed to download any archive. None of the remaining rules
** below are consulted unless "zipX" is on the robot-restrict setting.
**
** (2) If the robot-zip-leaf setting is true, then robots are allowed
** to download archives for any leaf check-in. This allows URL like
** /tarball/trunk/archive.tar.gz to work since branch labels like "trunk"
** always resolve to a leaf.
**
** (3) If the robot-zip-tag setting is a comma-separated tags, then any
** check-in that contains one of the tags on that list is allowed to
** be downloaded. This allows check-ins with tags like "release" or
** "robot-access" to be downloaded by robots.
*/
int robot_restrict_zip(int rid){
const char *zTag;
if( !robot_restrict_has_tag("zipX") || !client_might_be_a_robot() ){
return 0; /* Rule (1) */
}
if( db_get_boolean("robot-zip-leaf",0) && is_a_leaf(rid) ){
return 0; /* Rule (2) */
}
zTag = db_get("robot-zip-tag",0);
if( zTag && zTag[0] && fossil_strcmp(zTag,"off")!=0 ){
int ok = 0;
Stmt q;
db_prepare(&q,
"SELECT substr(tagname,5) FROM tagxref, tag"
" WHERE tagxref.rid=%d"
" AND tag.tagid=tagxref.tagid"
" AND tagxref.tagtype=1"
" AND tag.tagname GLOB 'sym-*'",
rid
);
while( !ok && db_step(&q)==SQLITE_ROW ){
if( glob_multi_match(zTag, db_column_text(&q,0)) ) ok = 1;
}
db_finalize(&q);
if( ok ) return 0; /* Rule (3) */
}
/* Generate the proof-of-work captcha */
ask_for_proof_that_client_is_not_robot();
return 1;
}
/*
** WEBPAGE: test-robotck
**
** Run the robot_restrict() function using the value of the "name="
** query parameter as an argument. Used for testing the robot_restrict()
** logic.
**
** Whenever this page is successfully rendered (when it doesn't go to
** the captcha) it deletes the proof-of-work cookie. So reloading the
** page will reset the cookie and restart the verification.
**
** If the zip=CHECKIN query parameter is provided, then also invoke
** robot_restrict_archive() on the RID of CHECKIN.
*/
void robot_restrict_test_page(void){
const char *zName = P("name");
const char *zZip = P("zip");
const char *zP1 = P("proof");
const char *zP2 = P(ROBOT_COOKIE);
const char *z;
int rid = 0;
if( zName==0 || zName[0]==0 ) zName = g.zPath;
login_check_credentials();
if( g.zLogin==0 ){ login_needed(1); return; }
g.zLogin = 0;
if( robot_restrict(zName) ) return;
if( zZip && zZip[0] ){
rid = symbolic_name_to_rid(zZip, "ci");
if( rid && robot_restrict_zip(rid) ) return;
}
style_set_current_feature("test");
style_header("robot_restrict() test");
@ <h1>Captcha passed</h1>
@
@ <p>
if( zP1 && zP1[0] ){
@ proof=%h(zP1)<br>
}
if( zP2 && zP2[0] ){
@ %h(ROBOT_COOKIE)=%h(zP2)<br>
cgi_set_cookie(ROBOT_COOKIE,"",0,-1);
}
if( zZip && zZip[0] ){
@ zip=%h(zZip)<br>
@ rid=%d(rid)<br>
}
if( g.perm.Admin ){
z = db_get("robot-restrict",robot_restrict_default());
if( z && z[0] ){
@ robot-restrict=%h(z)</br>
}
@ robot.h1=%u(robot.h1)<br>
@ robot.h2=%u(robot.h2)<br>
switch( robot.resultCache ){
case MIGHT_BE_ROBOT: {
@ robot.resultCache=MIGHT_BE_ROBOT<br>
break;
}
case KNOWN_NOT_ROBOT: {
@ robot.resultCache=KNOWN_NOT_ROBOT<br>
break;
}
default: {
@ robot.resultCache=OTHER (%d(robot.resultCache))<br>
break;
}
}
}
@ </p>
@ <p><a href="%R/test-robotck/%h(zName)">Retry</a>
style_finish_page();
}
/*
** WEBPAGE: tokens
**
** Allow users to create, delete, and view their access token.
**
** The access token is a string TOKEN which if included in a query
** parameter like "token=TOKEN" authenticates a request as coming
** from an authorized agent. This can be used, for example, by
** script to access content without running into problems with
** robot defenses.
*/
void tokens_page(void){
char *zMyToken;
login_check_credentials();
style_set_current_feature("tokens");
style_header("Access Tokens");
if( g.zLogin==0 || fossil_strcmp(g.zLogin,"anonymous")==0 ){
@ User "%h(g.zLogin?g.zLogin:"anonymous")" is not allowed to
@ own or use access tokens.
style_finish_page();
return;
}
if( g.perm.Admin && P("del")!=0 ){
const char *zDel = P("del");
db_unprotect(PROTECT_CONFIG);
db_multi_exec(
"DELETE FROM config WHERE name='token-%q'",
zDel);
db_protect_pop();
}
zMyToken = db_text(0,
"SELECT substr(name,7) FROM config"
" WHERE name GLOB 'token-*'"
" AND json_valid(value,6)"
" AND value->>'user' = %Q",
g.zLogin
);
if( zMyToken==0 && P("new") ){
sqlite3_uint64 r;
sqlite3_randomness(sizeof(r),&r);
zMyToken = mprintf("%016llx", r);
db_unprotect(PROTECT_CONFIG);
db_multi_exec(
"INSERT INTO config(name,value,mtime)"
"VALUES('token-%q','{user:%!j}',now())",
zMyToken, g.zLogin
);
db_protect_pop();
}else if( zMyToken!=0 && P("selfdel")
&& fossil_strcmp(zMyToken,P("selfdel"))==0 ){
db_unprotect(PROTECT_CONFIG);
db_multi_exec(
"DELETE FROM config WHERE name='token-%q'",
zMyToken);
db_protect_pop();
zMyToken = 0;
}
if( zMyToken==0 ){
@ <p>You do not currently have an access token.
@ <a href="%R/tokens?new=true">Create one</a>
}else{
@ <p>Your access token is "%h(zMyToken)".
@ <p>Use this token as the value of the token= query parameter
@ to bypass robot defenses on unauthenticated queries to this
@ server (%R). Do not misuse your token. Keep it confidential.
@ If you misuse your token, or if somebody else steals your token
@ and misuses, that can result in loss of access privileges to this
@ server.
@ <p><a href="%R/tokens?selfdel=%h(zMyToken)">Delete my token</a>
}
if( g.perm.Admin ){
int nTok = 0;
Stmt s;
db_prepare(&s,
"SELECT substr(name,7), value->>'user', datetime(mtime,'unixepoch')"
" FROM config"
" WHERE name GLOB 'token-*'"
" AND json_valid(value,6)"
);
while( db_step(&s)==SQLITE_ROW ){
if( nTok==0 ){
@ <hr>
@ <p>All tokens</p>
@ <table border="1" cellpadding="5" cellspacing="0">
@ <tr><th>User <th>Token <th>Date <th> </tr>
}
nTok++;
@ <tr><td>%h(db_column_text(&s,1))
@ <td>%h(db_column_text(&s,0))
@ <td>%h(db_column_text(&s,2))
@ <td><a href="%R/tokens?del=%h(db_column_text(&s,0))">delete</a>
@ </tr>
}
db_finalize(&s);
if( nTok==0 ){
@ <hr>
@ <p>There are access tokens defined for this repository.
}else{
@ </table>
}
}
style_finish_page();
}
|
Changes to src/search.c.
| ︙ | ︙ | |||
128 129 130 131 132 133 134 |
if( fSrchFlg & SRCHFLG_STATIC ){
p = &gSearch;
search_end(p);
}else{
p = fossil_malloc(sizeof(*p));
memset(p, 0, sizeof(*p));
}
| | | | | | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
if( fSrchFlg & SRCHFLG_STATIC ){
p = &gSearch;
search_end(p);
}else{
p = fossil_malloc(sizeof(*p));
memset(p, 0, sizeof(*p));
}
p->zPattern = z = mprintf("%s",zPattern);
p->zMarkBegin = mprintf("%s",zMarkBegin);
p->zMarkEnd = mprintf("%s",zMarkEnd);
p->zMarkGap = mprintf("%s",zMarkGap);
p->fSrchFlg = fSrchFlg;
blob_init(&p->snip, 0, 0);
while( *z && p->nTerm<SEARCH_MAX_TERM ){
while( *z && !ISALNUM(*z) ){ z++; }
if( *z==0 ) break;
p->a[p->nTerm].z = z;
for(i=1; ISALNUM(z[i]); i++){}
|
| ︙ | ︙ | |||
981 982 983 984 985 986 987 |
zPrefix = "The";
}else{
zPrefix = "Built-in help for the";
}
db_multi_exec(
"INSERT INTO x(label,url,score,id,snip)"
" SELECT format('%q \"%%s\" %%s',name,type),"
| | | 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 |
zPrefix = "The";
}else{
zPrefix = "Built-in help for the";
}
db_multi_exec(
"INSERT INTO x(label,url,score,id,snip)"
" SELECT format('%q \"%%s\" %%s',name,type),"
" '/help/'||name,"
" search_score(),"
" 'h'||rowid,"
" search_snippet()"
" FROM helptext"
" WHERE search_match(format('the \"%%s\" %%s',name,type),"
" helptext.helptext);",
zPrefix
|
| ︙ | ︙ | |||
1074 1075 1076 1077 1078 1079 1080 |
** replaces all non-alphanum ASCII characters with a space, and
** lower-cases all upper-case ASCII characters. The intent is to avoid
** causing errors in FTS5 searches with inputs which contain AND, OR,
** and symbols like #. The caller is responsible for passing the
** result to fossil_free().
*/
char *search_simplify_pattern(const char * zPattern){
| | | 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 |
** replaces all non-alphanum ASCII characters with a space, and
** lower-cases all upper-case ASCII characters. The intent is to avoid
** causing errors in FTS5 searches with inputs which contain AND, OR,
** and symbols like #. The caller is responsible for passing the
** result to fossil_free().
*/
char *search_simplify_pattern(const char * zPattern){
char *zPat = mprintf("%s",zPattern);
int i;
for(i=0; zPat[i]; i++){
if( (zPat[i]&0x80)==0 && !fossil_isalnum(zPat[i]) ) zPat[i] = ' ';
if( fossil_isupper(zPat[i]) ) zPat[i] = fossil_tolower(zPat[i]);
}
for(i--; i>=0 && zPat[i]==' '; i--){}
if( i<0 ){
|
| ︙ | ︙ |
Changes to src/security_audit.c.
| ︙ | ︙ | |||
367 368 369 370 371 372 373 |
}
zVulnReport = db_get("vuln-report","log");
if( fossil_strcmp(zVulnReport,"block")!=0
&& fossil_strcmp(zVulnReport,"fatal")!=0
){
@ <li><p><b>WARNING:</b>
| | | 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 |
}
zVulnReport = db_get("vuln-report","log");
if( fossil_strcmp(zVulnReport,"block")!=0
&& fossil_strcmp(zVulnReport,"fatal")!=0
){
@ <li><p><b>WARNING:</b>
@ The <a href="%R/help/vuln-report">vuln-report setting</a>
@ has a value of "%h(zVulnReport)". This disables defenses against
@ XSS or SQL-injection vulnerabilities caused by coding errors in
@ custom TH1 scripts. For the best security, change
@ the value of the vuln-report setting to "block" or "fatal".
}
/* Obsolete: */
|
| ︙ | ︙ | |||
952 953 954 955 956 957 958 |
}
while( fgets(z, sizeof(z), in) ){
if( prevWasTime ){
if( strncmp(z,"possible hack attempt - 418 ", 27)==0 ){
bOutput = (eType & 0x01)!=0;
nHack++;
}else
| | > > | 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 |
}
while( fgets(z, sizeof(z), in) ){
if( prevWasTime ){
if( strncmp(z,"possible hack attempt - 418 ", 27)==0 ){
bOutput = (eType & 0x01)!=0;
nHack++;
}else
if( (strncmp(z,"panic: ", 7)==0 && strncmp(z+7,"Timeout",7)!=0)
|| strstr(z," assertion fault ")!=0
){
bOutput = (eType & 0x02)!=0;
nPanic++;
}else
if( strncmp(z,"SMTP:", 5)==0 ){
bOutput = (eType & 0x20)!=0;
nSmtp++;
}else
|
| ︙ | ︙ |
Changes to src/setup.c.
| ︙ | ︙ | |||
116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
setup_menu_entry("Robot-Defense", "setup_robot",
"Settings for configure defense against robots");
setup_menu_entry("Settings", "setup_settings",
"Web interface to the \"fossil settings\" command");
}
setup_menu_entry("Timeline", "setup_timeline",
"Timeline display preferences");
if( setup_user ){
setup_menu_entry("Login-Group", "setup_login_group",
"Manage single sign-on between this repository and others"
" on the same server");
setup_menu_entry("Tickets", "tktsetup",
"Configure the trouble-ticketing system for this repository");
setup_menu_entry("Wiki", "setup_wiki",
| > > | 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
setup_menu_entry("Robot-Defense", "setup_robot",
"Settings for configure defense against robots");
setup_menu_entry("Settings", "setup_settings",
"Web interface to the \"fossil settings\" command");
}
setup_menu_entry("Timeline", "setup_timeline",
"Timeline display preferences");
setup_menu_entry("Tarballs and ZIPs", "setup_download",
"Preferences for auto-generated tarballs and ZIP files");
if( setup_user ){
setup_menu_entry("Login-Group", "setup_login_group",
"Manage single sign-on between this repository and others"
" on the same server");
setup_menu_entry("Tickets", "tktsetup",
"Configure the trouble-ticketing system for this repository");
setup_menu_entry("Wiki", "setup_wiki",
|
| ︙ | ︙ | |||
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
setup_menu_entry("Search","srchsetup",
"Configure the built-in search engine");
setup_menu_entry("URL Aliases", "waliassetup",
"Configure URL aliases");
if( setup_user ){
setup_menu_entry("Notification", "setup_notification",
"Automatic notifications of changes via outbound email");
setup_menu_entry("Transfers", "xfersetup",
"Configure the transfer system for this repository");
}
setup_menu_entry("Skins", "setup_skin_admin",
"Select and/or modify the web interface \"skins\"");
setup_menu_entry("Moderation", "setup_modreq",
"Enable/Disable requiring moderator approval of Wiki and/or Ticket"
" changes and attachments.");
setup_menu_entry("Ad-Unit", "setup_adunit",
| > > | 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
setup_menu_entry("Search","srchsetup",
"Configure the built-in search engine");
setup_menu_entry("URL Aliases", "waliassetup",
"Configure URL aliases");
if( setup_user ){
setup_menu_entry("Notification", "setup_notification",
"Automatic notifications of changes via outbound email");
#if 0 /* Disabled for now. Does this even work? */
setup_menu_entry("Transfers", "xfersetup",
"Configure the transfer system for this repository");
#endif
}
setup_menu_entry("Skins", "setup_skin_admin",
"Select and/or modify the web interface \"skins\"");
setup_menu_entry("Moderation", "setup_modreq",
"Enable/Disable requiring moderator approval of Wiki and/or Ticket"
" changes and attachments.");
setup_menu_entry("Ad-Unit", "setup_adunit",
|
| ︙ | ︙ | |||
419 420 421 422 423 424 425 426 |
"2", "UserAgent only",
"1", "UserAgent and Javascript",
};
multiple_choice_attribute(
"Enable hyperlinks base on User-Agent and/or Javascript",
"auto-hyperlink", "autohyperlink", "1",
count(azDefenseOpts)/2, azDefenseOpts);
@ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users,
| > > > > > > | < > > | | | < < < < < < < < < < < < | < < < < < < < < < < < < | > | | < < | 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 |
"2", "UserAgent only",
"1", "UserAgent and Javascript",
};
multiple_choice_attribute(
"Enable hyperlinks base on User-Agent and/or Javascript",
"auto-hyperlink", "autohyperlink", "1",
count(azDefenseOpts)/2, azDefenseOpts);
@ <br>
entry_attribute("Delay in milliseconds before enabling hyperlinks", 5,
"auto-hyperlink-delay", "ah-delay", "50", 0);
@ <br>
onoff_attribute("Also require a mouse event before enabling hyperlinks",
"auto-hyperlink-mouseover", "ahmo", 0, 0);
@ <p>Enable hyperlinks (the equivalent of the "h" permission) for all users,
@ including user "nobody" if the request appears to be from a human.
@ Disabling hyperlinks helps prevent robots from walking your site and
@ soaking up all your CPU and bandwidth.
@ If this setting is "UserAgent only" (2) then the
@ UserAgent string is the only factor considered. If the value of this
@ setting is "UserAgent And Javascript" (1) then Javascript is added that
@ runs after the page loads and fills in the href= values of <a>
@ elements. In either case, <a> tags are not generated if the
@ UserAgent string indicates that the client is a robot.
@ (Property: "auto-hyperlink")</p>
@
@ <p>For maximum robot defense, "Delay" should be at least 50 milliseconds
@ and "require a mouse event" should be turned on. These values only come
@ into play when the main auto-hyperlink settings is 2 ("UserAgent and
@ Javascript").
@ (Properties: "auto-hyperlink-delay" and "auto-hyperlink-mouseover")</p>
@
@ <p>To see if Javascript-base hyperlink enabling mechanism is working,
@ visit the <a href="%R/test-env">/test-env</a> page from a separate
@ web browser that is not logged in, even as "anonymous" and verify
@ that the "g.jsHref" value is "1".</p>
}
/*
** WEBPAGE: setup_robot
**
** Settings associated with defense against robots. Requires setup privilege.
*/
|
| ︙ | ︙ | |||
486 487 488 489 490 491 492 |
style_header("Robot Defense Settings");
db_begin_transaction();
@ <p>A Fossil website can have billions of pages in its tree, even for a
@ modest project. Many of those pages (examples: diffs and tarballs)
@ might be expensive to compute. A robot that tries to walk the entire
@ website can present a crippling CPU and bandwidth load.
@
| | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < < < < < < < < < < < < < < < < < < < < < < | 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 |
style_header("Robot Defense Settings");
db_begin_transaction();
@ <p>A Fossil website can have billions of pages in its tree, even for a
@ modest project. Many of those pages (examples: diffs and tarballs)
@ might be expensive to compute. A robot that tries to walk the entire
@ website can present a crippling CPU and bandwidth load.
@
@ <p>The settings on this page are intended to help administrators
@ defend against abusive robots.
@
@ <form action="%R/setup_robot" method="post"><div>
login_insert_csrf_secret();
@ <input type="submit" name="submit" value="Apply Changes"></p>
@ <hr>
@ <p><b>Do not allow robots access to these pages.</b><br>
@ If the page name matches the GLOB pattern of this setting, and the
@ users is "nobody", and the client has not previously passed a captcha
@ test to show that it is not a robot, then the page is not displayed.
@ A captcha test is is rendered instead.
@ The default value for this setting is:
@ <p>
@    <tt>%h(robot_restrict_default())</tt>
@ <p>
@ The "diff" tag covers all diffing pages such as /vdiff, /fdiff, and
@ /vpatch. The "annotate" tag covers /annotate and also /blame and
@ /praise. The "zip" covers itself and also /tarball and /sqlar.
@ If a tag has an "X" character appended (ex: "timelineX") then it only
@ applies if query parameters are such that the page is expensive
@ and/or unusual. In all other case, the tag should exactly match
@ the page name.
@
@ To disable robot restrictions, change this setting to "off".
@ (Property: robot-restrict)
@ <br>
textarea_attribute("", 2, 80,
"robot-restrict", "rbrestrict", robot_restrict_default(), 0);
@ <p><b>Exception #1</b><br>
@ If "zipX" appears in the robot-restrict list above, then tarballs,
@ ZIP-archives, and SQL-archives may be downloaded by robots if
@ the check-in is a leaf (robot-zip-leaf):<br>
onoff_attribute("Allow tarballs for leaf check-ins",
"robot-zip-leaf", "rzleaf", 0, 0);
@ <p><b>Exception #2</b><br>
@ If "zipX" appears in the robot-restrict list above, then tarballs,
@ ZIP-archives, and SQL-archives may be downloaded by robots if
@ the check-in has one or more tags that match the following
@ list of GLOB patterns: (robot-zip-tag)<br>
textarea_attribute("", 2, 80,
"robot-zip-tag", "rztag", "", 0);
@ <p><b>Exception #3</b><br>
@ If the request URI matches any of the following
@ <a href="%R/re_rules">regular expressions</a> (one per line), then the
@ request is exempt from anti-robot defenses.
@ The regular expression is matched against the REQUEST_URI with the
@ SCRIPT_NAME prefix removed, and with QUERY_STRING appended following
@ a "?" if QUERY_STRING exists. (Property: robot-exception)<br>
textarea_attribute("", 3, 80,
"robot-exception", "rbexcept", "", 0);
@ <hr>
addAutoHyperlinkSettings();
@ <hr>
entry_attribute("Anonymous Login Validity", 11, "anon-cookie-lifespan",
"anoncookls", "840", 0);
@ <p>The number of minutes for which an anonymous login cookie is valid.
@ Set to zero to disable anonymous login.
@ (property: anon-cookie-lifespan)
@ <hr>
entry_attribute("Server Load Average Limit", 11, "max-loadavg", "mxldavg",
"0.0", 0);
@ <p>Some expensive operations (such as computing tarballs, zip archives,
@ or annotation/blame pages) are prohibited if the load average on the host
@ computer is too large. Set the threshold for disallowing expensive
@ computations here. Set this to 0.0 to disable the load average limit.
@ This limit is only enforced on Unix servers. On Linux systems,
@ access to the /proc virtual filesystem is required, which means this limit
@ might not work inside a chroot() jail.
@ (Property: "max-loadavg")</p>
@
@ <hr>
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
@ </div></form>
db_end_transaction(0);
style_finish_page();
}
|
| ︙ | ︙ | |||
772 773 774 775 776 777 778 779 780 781 782 783 784 785 |
"auto-captcha", "autocaptcha", 0, 0);
@ <p>When enabled, a button appears on the login screen for user
@ "anonymous" that will automatically fill in the CAPTCHA password.
@ This is less secure than forcing the user to do it manually, but is
@ probably secure enough and it is certainly more convenient for
@ anonymous users. (Property: "auto-captcha")</p>
@ <hr>
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
@ </div></form>
db_end_transaction(0);
style_finish_page();
}
| > > > > > > > | 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 |
"auto-captcha", "autocaptcha", 0, 0);
@ <p>When enabled, a button appears on the login screen for user
@ "anonymous" that will automatically fill in the CAPTCHA password.
@ This is less secure than forcing the user to do it manually, but is
@ probably secure enough and it is certainly more convenient for
@ anonymous users. (Property: "auto-captcha")</p>
@ <hr>
entry_attribute("Anonymous Login Validity", 11, "anon-cookie-lifespan",
"anoncookls", "840", 0);
@ <p>The number of minutes for which an anonymous login cookie is valid.
@ Set to zero to disable anonymous logins.
@ (property: anon-cookie-lifespan)
@ <hr>
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
@ </div></form>
db_end_transaction(0);
style_finish_page();
}
|
| ︙ | ︙ | |||
966 967 968 969 970 971 972 973 974 975 976 977 978 979 |
static const char *const azTimeFormats[] = {
"0", "HH:MM",
"1", "HH:MM:SS",
"2", "YYYY-MM-DD HH:MM",
"3", "YYMMDD HH:MM",
"4", "(off)"
};
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
return;
}
style_set_current_feature("setup");
| > > > > > | 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 |
static const char *const azTimeFormats[] = {
"0", "HH:MM",
"1", "HH:MM:SS",
"2", "YYYY-MM-DD HH:MM",
"3", "YYMMDD HH:MM",
"4", "(off)"
};
static const char *const azLeafMark[] = {
"0", "No",
"1", "Yes",
"2", "Yes - with emphasis",
};
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
return;
}
style_set_current_feature("setup");
|
| ︙ | ︙ | |||
1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 |
"tdf", "0", count(azTimeFormats)/2, azTimeFormats);
@ <p>If the "HH:MM" or "HH:MM:SS" format is selected, then the date is shown
@ in a separate box (using CSS class "timelineDate") whenever the date
@ changes. With the "YYYY-MM-DD HH:MM" and "YYMMDD ..." formats,
@ the complete date and time is shown on every timeline entry using the
@ CSS class "timelineTime". (Property: "timeline-date-format")</p>
@ <hr>
entry_attribute("Max timeline comment length", 6,
"timeline-max-comment", "tmc", "0", 0);
@ <p>The maximum length of a comment to be displayed in a timeline.
@ "0" there is no length limit.
@ (Property: "timeline-max-comment")</p>
| > > > > > > | 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 |
"tdf", "0", count(azTimeFormats)/2, azTimeFormats);
@ <p>If the "HH:MM" or "HH:MM:SS" format is selected, then the date is shown
@ in a separate box (using CSS class "timelineDate") whenever the date
@ changes. With the "YYYY-MM-DD HH:MM" and "YYMMDD ..." formats,
@ the complete date and time is shown on every timeline entry using the
@ CSS class "timelineTime". (Property: "timeline-date-format")</p>
@ <hr>
multiple_choice_attribute("Leaf Markings", "timeline-mark-leaves",
"tml", "1", count(azLeafMark)/2, azLeafMark);
@ <p>Should timeline entries for leaf check-ins be identified in the
@ detail section. (Property: "timeline-mark-leaves")</p>
@ <hr>
entry_attribute("Max timeline comment length", 6,
"timeline-max-comment", "tmc", "0", 0);
@ <p>The maximum length of a comment to be displayed in a timeline.
@ "0" there is no length limit.
@ (Property: "timeline-max-comment")</p>
|
| ︙ | ︙ | |||
1156 1157 1158 1159 1160 1161 1162 |
(db_get_versioned(pSet->name, NULL, NULL)!=0);
if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
continue;
}
onoff_attribute("", pSet->name,
pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
is_truth(pSet->def), hasVersionableValue);
| | | | | 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 |
(db_get_versioned(pSet->name, NULL, NULL)!=0);
if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
continue;
}
onoff_attribute("", pSet->name,
pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
is_truth(pSet->def), hasVersionableValue);
@ <a href='%R/help/%s(pSet->name)'>%h(pSet->name)</a>
if( pSet->versionable ){
@ (v)<br>
} else {
@ <br>
}
}
}
@ <br><input type="submit" name="submit" value="Apply Changes">
@ </td><td style="width:50px;"></td><td valign="top">
@ <table>
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
if( pSet->width>0 && !pSet->forceTextArea ){
int hasVersionableValue = pSet->versionable &&
(db_get_versioned(pSet->name, NULL, NULL)!=0);
if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
continue;
}
@ <tr><td>
@ <a href='%R/help/%s(pSet->name)'>%h(pSet->name)</a>
if( pSet->versionable ){
@ (v)
} else {
@
}
@</td><td>
entry_attribute("", /*pSet->width*/ 25, pSet->name,
pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
(char*)pSet->def, hasVersionableValue);
@</td></tr>
}
}
@</table>
@ </td><td style="width:50px;"></td><td valign="top">
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
if( pSet->width>0 && pSet->forceTextArea ){
int hasVersionableValue = db_get_versioned(pSet->name, NULL, NULL)!=0;
if( bIfChng && setting_has_default_value(pSet, db_get(pSet->name,0)) ){
continue;
}
@ <a href='%R/help/%s(pSet->name)'>%s(pSet->name)</a>
if( pSet->versionable ){
@ (v)<br>
} else {
@ <br>
}
textarea_attribute("", /*rows*/ 2, /*cols*/ 35, pSet->name,
pSet->var!=0 ? pSet->var : pSet->name /*works-like:"x"*/,
|
| ︙ | ︙ | |||
1307 1308 1309 1310 1311 1312 1313 | @ Other repositories use this URL to clone or sync against this repository. @ This is also the basename for hyperlinks included in email alert text. @ Omit the trailing "/". @ If this repo will not be set up as a persistent server and will not @ be sending email alerts, then leave this entry blank. @ Suggested value: "%h(g.zBaseURL)" @ (Property: "email-url")</p> | < < < < < < < < < < < < < < < < < < | 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 |
@ Other repositories use this URL to clone or sync against this repository.
@ This is also the basename for hyperlinks included in email alert text.
@ Omit the trailing "/".
@ If this repo will not be set up as a persistent server and will not
@ be sending email alerts, then leave this entry blank.
@ Suggested value: "%h(g.zBaseURL)"
@ (Property: "email-url")</p>
@ <hr>
entry_attribute("Index Page", 60, "index-page", "idxpg", "/home", 0);
@ <p>Enter the pathname of the page to display when the "Home" menu
@ option is selected and when no pathname is
@ specified in the URL. For example, if you visit the url:</p>
@
@ <blockquote><p>%h(g.zBaseURL)</p></blockquote>
|
| ︙ | ︙ | |||
1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 |
@ </ol>
@
@ <p>The default value is blank, meaning no added entries.
@ (Property: sitemap-extra)
@ <p>
textarea_attribute("Custom Sitemap Entries", 8, 80,
"sitemap-extra", "smextra", "", 0);
@ <hr>
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
@ </div></form>
db_end_transaction(0);
style_finish_page();
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 |
@ </ol>
@
@ <p>The default value is blank, meaning no added entries.
@ (Property: sitemap-extra)
@ <p>
textarea_attribute("Custom Sitemap Entries", 8, 80,
"sitemap-extra", "smextra", "", 0);
@ <hr>
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
@ </div></form>
db_end_transaction(0);
style_finish_page();
}
/*
** WEBPAGE: setup_download
**
** The "Admin/Download" page. Requires Setup privilege.
*/
void setup_download(void){
login_check_credentials();
if( !g.perm.Setup ){
login_needed(0);
return;
}
style_set_current_feature("setup");
style_header("Tarball and ZIP Downloads");
db_begin_transaction();
@ <form action="%R/setup_download" method="post"><div>
login_insert_csrf_secret();
@ <input type="submit" name="submit" value="Apply Changes"></p>
@ <hr>
entry_attribute("Tarball and ZIP Name Prefix", 20, "short-project-name",
"spn", "", 0);
@ <p>This is used as a prefix for the names of generated tarballs and
@ ZIP archive. Keep this prefix brief and use only lower-case ASCII
@ characters, digits, "_", "-" in the name. If this setting is blank,
@ then the full <a href='%R/help/project-name'>project-name</a> setting
@ is used instead.
@ (Property: "short-project-name")
@ </p>
@ <hr>
@ <p><b>Configuration for the <a href="%R/download">/download</a> page.</b>
@ <p>The value is a TCL list divided into groups of four tokens:
@ <ol>
@ <li> Maximum number of matches (COUNT).
@ <li> Tag to match using glob (TAG).
@ <li> Maximum age of check-ins to match (MAX_AGE).
@ <li> Comment to apply to matches (COMMENT).
@ </ol>
@ Each 4-tuple will match zero or more check-ins. The /download page
@ displays the union of matches from all 4-tuples.
@ See the <a href="%R/help/suggested-downloads">suggested-downloads</a>
@ setting documentation for further detail.
@ <p>
@ The /download page is omitted from the <a href="%R/sitemap">/sitemap</a>
@ if the first token is "0" or "off" or "no". The default value
@ for this setting is "off".
@ (Property: <a href="%R/help/suggested-downloads">suggested-downloads</a>)
@ <p>
textarea_attribute("", 4, 80,
"suggested-downloads", "sgtrlst", "off", 0);
@ <hr>
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
@ </div></form>
db_end_transaction(0);
style_finish_page();
}
|
| ︙ | ︙ |
Changes to src/sitemap.c.
| ︙ | ︙ | |||
130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
@ <li>%z(href("%R/tree?type=flat"))Flat-view</a></li>
@ <li>%z(href("%R/fileage?name=trunk"))File ages for Trunk</a></li>
@ <li>%z(href("%R/uvlist"))Unversioned Files</a>
if( g.perm.Write && zEditGlob[0]!=0 ){
@ <li>%z(href("%R/fileedit"))On-line File Editor</li>
}
@ </ul>
}
if( g.perm.Read ){
@ <li>%z(href("%R/timeline"))Project Timeline</a>
@ <ul>
@ <li>%z(href("%R/reports"))Activity Reports</a></li>
@ <li>%z(href("%R/sitemap-timeline"))Other timelines</a></li>
@ </ul>
| > > > | 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
@ <li>%z(href("%R/tree?type=flat"))Flat-view</a></li>
@ <li>%z(href("%R/fileage?name=trunk"))File ages for Trunk</a></li>
@ <li>%z(href("%R/uvlist"))Unversioned Files</a>
if( g.perm.Write && zEditGlob[0]!=0 ){
@ <li>%z(href("%R/fileedit"))On-line File Editor</li>
}
@ </ul>
}
if( g.perm.Zip && db_get_boolean("suggested-downloads",0)!=0 ){
@ <li>%z(href("%R/download"))Tarballs and ZIPs</a>
}
if( g.perm.Read ){
@ <li>%z(href("%R/timeline"))Project Timeline</a>
@ <ul>
@ <li>%z(href("%R/reports"))Activity Reports</a></li>
@ <li>%z(href("%R/sitemap-timeline"))Other timelines</a></li>
@ </ul>
|
| ︙ | ︙ |
Changes to src/skins.c.
| ︙ | ︙ | |||
1343 1344 1345 1346 1347 1348 1349 |
builtin_request_js("skin.js");
style_finish_page();
}
/*
** WEBPAGE: skins
**
| | | 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 |
builtin_request_js("skin.js");
style_finish_page();
}
/*
** WEBPAGE: skins
**
** Show a list of all of the built-in skins, plus the repository skin,
** and provide the user with an opportunity to change to any of them.
*/
void skins_page(void){
int i;
char *zBase = fossil_strdup(g.zTop);
size_t nBase = strlen(zBase);
login_check_credentials();
|
| ︙ | ︙ | |||
1371 1372 1373 1374 1375 1376 1377 |
@ <p class="warning">Warning:
if( iDraftSkin>0 ){
@ you are using a draft skin,
}else{
@ this fossil instance was started with a hard-coded skin
@ value
}
| | | 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 |
@ <p class="warning">Warning:
if( iDraftSkin>0 ){
@ you are using a draft skin,
}else{
@ this fossil instance was started with a hard-coded skin
@ value
}
@ which supersedes any option selected below. A skin selected
@ below will be recorded in your
@ "%z(href("%R/fdscookie"))fossil_display_settings</a>" cookie
@ but will not be used so long as the site has a
@ higher-priority skin in place.
@ </p>
}
@ <p>The following skins are available for this repository:</p>
|
| ︙ | ︙ |
Changes to src/stash.c.
| ︙ | ︙ | |||
548 549 550 551 552 553 554 | ** ** > fossil stash goto ?STASHID? ** ** Update to the baseline check-out for STASHID then apply the ** changes of STASHID. Keep STASHID so that it can be reused ** This command is undoable. ** | | | | | > > > > | 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 |
**
** > fossil stash goto ?STASHID?
**
** Update to the baseline check-out for STASHID then apply the
** changes of STASHID. Keep STASHID so that it can be reused
** This command is undoable.
**
** > fossil stash drop|rm ?STASHIDs...? ?-a|--all?
**
** Forget everything about the given STASHIDs. Forget the whole
** stash if the -a|--all flag is used. Individual drops are
** undoable but -a|--all is not.
**
** > fossil stash diff ?STASHID? ?DIFF-OPTIONS?
** > fossil stash gdiff ?STASHID? ?DIFF-OPTIONS?
**
** Show diffs of the current working directory and what that
** directory would be if STASHID were applied. With gdiff,
** gdiff-command is used instead of internal diff logic.
**
** > fossil stash rename STASHID NEW-NAME
**
** Change the description of the given STASHID entry to NEW-NAME.
*/
void stash_cmd(void){
const char *zCmd;
int nCmd;
int stashid = 0;
undo_capture_command_line();
db_must_be_within_tree();
|
| ︙ | ︙ | |||
769 770 771 772 773 774 775 |
diff_tk(fBaseline ? "stash show" : "stash diff", 3);
return;
}
diff_options(&DCfg, zCmd[0]=='g', 0);
stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
stash_diff(stashid, fBaseline, &DCfg);
}else
| > > > > > > | | 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 |
diff_tk(fBaseline ? "stash show" : "stash diff", 3);
return;
}
diff_options(&DCfg, zCmd[0]=='g', 0);
stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
stash_diff(stashid, fBaseline, &DCfg);
}else
if( strncmp(zCmd, "rename", nCmd)==0 ){
if( g.argc!=5 ) usage("rename STASHID NAME");
stashid = stash_get_id(g.argv[3]);
db_multi_exec("UPDATE STASH SET COMMENT=%Q WHERE stashid=%d",
g.argv[4], stashid);
}
else if( strncmp(zCmd, "help", nCmd)==0 ){
g.argv[1] = "help";
g.argv[2] = "stash";
g.argc = 3;
help_cmd();
}else
{
usage("SUBCOMMAND ARGS...");
}
db_end_transaction(0);
}
|
Changes to src/style.c.
| ︙ | ︙ | |||
476 477 478 479 480 481 482 | } /* ** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js ** Javascript module, and generates HTML elements with the following IDs: ** ** TARGETID: The <span> wrapper around TEXT. | | | 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 | } /* ** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js ** Javascript module, and generates HTML elements with the following IDs: ** ** TARGETID: The <span> wrapper around TEXT. ** copy-TARGETID: The <button> for the copy button. ** ** If the FLIPPED argument is non-zero, the copy button is displayed after TEXT. ** ** The COPYLENGTH argument defines the length of the substring of TEXT copied to ** clipboard: ** ** <= 0: No limit (default if the argument is omitted). |
| ︙ | ︙ | |||
508 509 510 511 512 513 514 |
zText = vmprintf(zTextFmt/*works-like:?*/,ap);
va_end(ap);
if( cchLength==1 ) cchLength = hash_digits(0);
else if( cchLength==2 ) cchLength = hash_digits(1);
if( !bFlipped ){
const char *zBtnFmt =
"<span class=\"nobr\">"
| | | | | | > | > | | | | | | | > | > | 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 |
zText = vmprintf(zTextFmt/*works-like:?*/,ap);
va_end(ap);
if( cchLength==1 ) cchLength = hash_digits(0);
else if( cchLength==2 ) cchLength = hash_digits(1);
if( !bFlipped ){
const char *zBtnFmt =
"<span class=\"nobr\">"
"<button "
"class=\"copy-button\" "
"id=\"copy-%h\" "
"data-copytarget=\"%h\" "
"data-copylength=\"%d\">"
"<span>"
"</span>"
"</button>"
"<span id=\"%h\">"
"%s"
"</span>"
"</span>";
if( bOutputCGI ){
cgi_printf(
zBtnFmt/*works-like:"%h%h%d%h%s"*/,
zTargetId,zTargetId,cchLength,zTargetId,zText);
}else{
zResult = mprintf(
zBtnFmt/*works-like:"%h%h%d%h%s"*/,
zTargetId,zTargetId,cchLength,zTargetId,zText);
}
}else{
const char *zBtnFmt =
"<span class=\"nobr\">"
"<span id=\"%h\">"
"%s"
"</span>"
"<button "
"class=\"copy-button copy-button-flipped\" "
"id=\"copy-%h\" "
"data-copytarget=\"%h\" "
"data-copylength=\"%d\">"
"<span>"
"</span>"
"</button>"
"</span>";
if( bOutputCGI ){
cgi_printf(
zBtnFmt/*works-like:"%h%s%h%h%d"*/,
zTargetId,zText,zTargetId,zTargetId,cchLength);
}else{
zResult = mprintf(
|
| ︙ | ︙ | |||
1384 1385 1386 1387 1388 1389 1390 |
** Display CGI-variables and other aspects of the run-time
** environment, for debugging and trouble-shooting purposes.
*/
void page_test_env(void){
webpage_error("");
}
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 |
** Display CGI-variables and other aspects of the run-time
** environment, for debugging and trouble-shooting purposes.
*/
void page_test_env(void){
webpage_error("");
}
/*
** Webpages that encounter an error due to missing or incorrect
** query parameters can jump to this routine to render an error
** message screen.
**
** For administators, or if the test_env_enable setting is true, then
** details of the request environment are displayed. Otherwise, just
|
| ︙ | ︙ | |||
1481 1482 1483 1484 1485 1486 1487 |
#endif
@ g.zBaseURL = %h(g.zBaseURL)<br>
@ g.zHttpsURL = %h(g.zHttpsURL)<br>
@ g.zTop = %h(g.zTop)<br>
@ g.zPath = %h(g.zPath)<br>
@ g.userUid = %d(g.userUid)<br>
@ g.zLogin = %h(g.zLogin)<br>
| | | 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 |
#endif
@ g.zBaseURL = %h(g.zBaseURL)<br>
@ g.zHttpsURL = %h(g.zHttpsURL)<br>
@ g.zTop = %h(g.zTop)<br>
@ g.zPath = %h(g.zPath)<br>
@ g.userUid = %d(g.userUid)<br>
@ g.zLogin = %h(g.zLogin)<br>
@ g.isRobot = %d(g.isRobot)<br>
@ g.jsHref = %d(g.jsHref)<br>
if( g.zLocalRoot ){
@ g.zLocalRoot = %h(g.zLocalRoot)<br>
}else{
@ g.zLocalRoot = <i>none</i><br>
}
if( g.nRequest ){
|
| ︙ | ︙ |
Changes to src/tag.c.
| ︙ | ︙ | |||
216 217 218 219 220 221 222 |
rid
);
}
}
if( zCol ){
db_multi_exec("UPDATE event SET \"%w\"=%Q WHERE objid=%d",
zCol, zValue, rid);
| | | 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
rid
);
}
}
if( zCol ){
db_multi_exec("UPDATE event SET \"%w\"=%Q WHERE objid=%d",
zCol, zValue, rid);
if( tagid==TAG_COMMENT && zValue!=0 ){
char *zCopy = fossil_strdup(zValue);
backlink_extract(zCopy, MT_NONE, rid, BKLNK_COMMENT, mtime, 1);
free(zCopy);
}
}
if( tagid==TAG_DATE ){
db_multi_exec("UPDATE event "
|
| ︙ | ︙ |
Changes to src/tar.c.
| ︙ | ︙ | |||
29 30 31 32 33 34 35 36 37 38 39 40 41 42 | unsigned char *aHdr; /* Space for building headers */ char *zSpaces; /* Spaces for padding */ char *zPrevDir; /* Name of directory for previous entry */ int nPrevDirAlloc; /* size of zPrevDir */ Blob pax; /* PAX data */ } tball; /* ** field lengths of 'ustar' name and prefix fields. */ #define USTAR_NAME_LEN 100 #define USTAR_PREFIX_LEN 155 | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
unsigned char *aHdr; /* Space for building headers */
char *zSpaces; /* Spaces for padding */
char *zPrevDir; /* Name of directory for previous entry */
int nPrevDirAlloc; /* size of zPrevDir */
Blob pax; /* PAX data */
} tball;
/*
** Convert a string so that it contains only lower-case ASCII, digits,
** "_" and "-". Changes are made in-place.
*/
static void sanitize_name(char *zName){
int i;
char c;
for(i=0; (c = zName[i])!=0; i++){
if( fossil_isupper(c) ){
zName[i] = fossil_tolower(c);
}else if( !fossil_isalnum(c) && c!='_' && c!='-' ){
if( c<=0x7f ){
zName[i] = '_';
}else{
/* 123456789 123456789 123456 */
zName[i] = "abcdefghijklmnopqrstuvwxyz"[(unsigned)c%26];
}
}
}
}
/*
** Compute a sensible base-name for an archive file (tarball, ZIP, or SQLAR)
** based on the rid of the check-in contained in that file.
**
** PROJECTNAME-DATETIME-HASHPREFIX
**
** So that the name will be safe to use as a URL or a filename on any system,
** the name is only allowed to contain lower-case ASCII alphabetics,
** digits, '_' and '-'. Upper-case ASCII is converted to lower-case. All
** other bytes are mapped into a lower-case alphabetic.
**
** The value returned is obtained from mprintf() or fossil_strdup() and should
** be released by the caller using fossil_free().
*/
char *archive_base_name(int rid){
char *zPrefix;
char *zName;
zPrefix = db_get("short-project-name",0);
if( zPrefix==0 || zPrefix[0]==0 ){
zPrefix = db_get("project-name","unnamed");
}
zName = db_text(0,
"SELECT %Q||"
" strftime('-%%Y%%m%%d%%H%%M%%S-',event.mtime)||"
" substr(blob.uuid,1,10)"
" FROM blob, event LEFT JOIN config"
" WHERE blob.rid=%d"
" AND event.objid=%d"
" AND config.name='project-name'",
zPrefix, rid, rid);
fossil_free(zPrefix);
sanitize_name(zName);
return zName;
}
/*
** field lengths of 'ustar' name and prefix fields.
*/
#define USTAR_NAME_LEN 100
#define USTAR_PREFIX_LEN 155
|
| ︙ | ︙ | |||
651 652 653 654 655 656 657 |
}
zOut = g.argv[3];
if( fossil_strcmp("/dev/null",zOut)==0 || fossil_strcmp("",zOut)==0 ){
zOut = 0;
}
if( zName==0 ){
| | < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > | | > > > | > | > | > > > > > > > > > > > > > > > > > > > > > | > > > | | | > > > | > | | > | > > > | > > > > | | | > > > > > > > > > > > | > > > > > > > > > > > > > | > > > > > > > | > > | > | 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 |
}
zOut = g.argv[3];
if( fossil_strcmp("/dev/null",zOut)==0 || fossil_strcmp("",zOut)==0 ){
zOut = 0;
}
if( zName==0 ){
zName = archive_base_name(rid);
}
tarball_of_checkin(rid, zOut ? &tarball : 0,
zName, pInclude, pExclude, listFlag);
glob_free(pInclude);
glob_free(pExclude);
if( listFlag ) fflush(stdout);
if( zOut ){
blob_write_to_file(&tarball, zOut);
blob_reset(&tarball);
}
}
/*
** This is a helper routine for tar_uuid_from_name(). It handles
** the case where *pzName contains no "/" character. Check for
** format (3). Return the hash if the name matches format (3),
** or return NULL if it does not.
*/
static char *format_three_parser(const char *zName){
int iDot = 0; /* Index in zName[] of the first '.' */
int iDash1 = 0; /* Index in zName[] of the '-' before the timestamp */
int iDash2 = 0; /* Index in zName[] of the '-' between timestamp and hash */
int nHash; /* Size of the hash */
char *zHash; /* A copy of the hash value */
char *zDate; /* Copy of the timestamp */
char *zUuid; /* Final result */
int i; /* Loop query */
Stmt q; /* Query to verify that hash and timestamp agree */
for(i=0; zName[i]; i++){
char c = zName[i];
if( c=='.' ){ iDot = i; break; }
if( c=='-' ){ iDash1 = iDash2; iDash2 = i; }
if( !fossil_isalnum(c) && c!='_' && c!='-' ){ break; }
}
if( iDot==0 ) return 0;
if( iDash1==0 ) return 0;
nHash = iDot - iDash2 - 1;
if( nHash<8 ) return 0; /* HASH value too short */
if( (iDash2 - iDash1)!=15 ) return 0; /* Wrong timestamp size */
zHash = fossil_strndup(&zName[iDash2+1], nHash);
zDate = fossil_strndup(&zName[iDash1+1], 14);
db_prepare(&q,
"SELECT blob.uuid"
" FROM blob JOIN event ON event.objid=blob.rid"
" WHERE blob.uuid GLOB '%q*'"
" AND strftime('%%Y%%m%%d%%H%%M%%S',event.mtime)='%q'",
zHash, zDate
);
fossil_free(zHash);
fossil_free(zDate);
if( db_step(&q)==SQLITE_ROW ){
zUuid = fossil_strdup(db_column_text(&q,0));
}else{
zUuid = 0;
}
db_finalize(&q);
return zUuid;
}
/*
** Check to see if the input string is of one of the following
** two the forms:
**
** check-in-name/filename.ext (1)
** tag-name/check-in-name/filename.ext (2)
** project-datetime-hash.ext (3)
**
** In other words, check to see if the input string contains either
** a check-in name or a tag-name and a check-in name separated by
** a slash. There must be between 0 or 2 "/" characters. In the
** second form, tag-name must be an individual tag (not a branch-tag)
** that is found on the check-in identified by the check-in-name.
**
** If the condition is true, then:
**
** * Make *pzName point to the filename suffix only
** * return a copy of the check-in name in memory from mprintf().
**
** If the condition is false, leave *pzName unchanged and return either
** NULL or an empty string. Normally NULL is returned, however an
** empty string is returned for format (2) if check-in-name does not
** match tag-name.
**
** Format (2) is specifically designed to allow URLs like this:
**
** /tarball/release/UUID/PROJECT.tar.gz
**
** Such URLs will pass through most anti-robot filters because of the
** "/tarball/release" prefix will match the suggested "robot-exception"
** pattern and can still refer to an historic release rather than just
** the most recent release.
**
** Format (3) is designed to allow URLs like this:
**
** /tarball/fossil-20251018193920-d6c9aee97df.tar.gz
**
** In other words, filename itself contains sufficient information to
** uniquely identify the check-in, including a timestamp of the form
** YYYYMMDDHHMMSS and a prefix of the check-in hash. The timestamp
** and hash must immediately preceed the first "." in the name.
*/
char *tar_uuid_from_name(char **pzName){
char *zName = *pzName; /* Original input */
int n1 = 0; /* Bytes in first prefix (tag-name) */
int n2 = 0; /* Bytes in second prefix (check-in-name) */
int n = 0; /* max(n1,n2) */
int i; /* Loop counter */
for(i=n1=n2=0; zName[i]; i++){
if( zName[i]=='/' ){
if( n1==0 ){
n = n1 = i;
}else if( n2==0 ){
n = n2 = i;
}else{
return 0; /* More than two "/" characters seen */
}
}
}
if( n1==0 ){
/* Check for format (3) */
return format_three_parser(*pzName);
}
if( zName[n+1]==0 ){
return 0; /* No filename suffix */
}
if( n2==0 ){
/* Format (1): check-in name only. The check-in-name is not verified */
zName[n1] = 0;
*pzName = fossil_strdup(&zName[n1+1]);
return zName;
}else if( n2>n1+1 ){
/* Format (2): tag-name/check-in-name. Verify that check-in-name is real
** and that the check-in has the tag named by tag-name.
*/
char *zCkin = mprintf("%.*s", n2-n1-1, &zName[n1+1]);
char *zTag;
int rid = symbolic_name_to_rid(zCkin,"ci");
int hasTag;
if( rid<=0 ){
fossil_free(zCkin);
return fossil_strdup("");
}
zTag = mprintf("%.*s", n1, zName);
hasTag = db_exists(
"SELECT 1 FROM tagxref, tag"
" WHERE tagxref.rid=%d"
" AND tag.tagid=tagxref.tagid"
" AND tagxref.tagtype=1"
" AND tag.tagname='sym-%q'",
rid, zTag
);
fossil_free(zTag);
if( !hasTag ){
fossil_free(zCkin);
return fossil_strdup("");
}
*pzName = fossil_strdup(&zName[n2+1]);
return zCkin;
}else{
return 0;
}
}
/*
** WEBPAGE: tarball
** URL: /tarball/NAME.tar.gz
** or: /tarball/VERSION/NAME.tar.gz
** or: /tarball/TAG/VERSION/NAME.tar.gz
**
** Generate a compressed tarball for the check-in specified by VERSION.
** The tarball is called NAME.tar.gz and has a top-level directory called
** NAME. If TAG is provided, then VERSION must hold TAG or else an error
** is returned.
**
** The optional VERSION element defaults to "trunk" per the r= rules below.
** All of the following URLs are equivalent:
**
** /tarball/release/xyz.tar.gz
** /tarball?r=release&name=xyz.tar.gz
** /tarball/xyz.tar.gz?r=release
|
| ︙ | ︙ | |||
743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 |
** in=PATTERN Only include files that match the comma-separate
** list of GLOB patterns in PATTERN, as with ex=
**
** ex=PATTERN Omit any file that match PATTERN. PATTERN is a
** comma-separated list of GLOB patterns, where each
** pattern can optionally be quoted using ".." or '..'.
** Any file matching both ex= and in= is excluded.
*/
void tarball_page(void){
int rid;
char *zName, *zRid, *zKey;
int nName, nRid;
const char *zInclude; /* The in= query parameter */
const char *zExclude; /* The ex= query parameter */
Blob cacheKey; /* The key to cache */
Glob *pInclude = 0; /* The compiled in= glob pattern */
Glob *pExclude = 0; /* The compiled ex= glob pattern */
Blob tarball; /* Tarball accumulated here */
const char *z;
login_check_credentials();
if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
fossil_nice_default();
zName = fossil_strdup(PD("name",""));
z = P("r");
if( z==0 ) z = P("uuid");
if( z==0 ) z = tar_uuid_from_name(&zName);
if( z==0 ) z = "trunk";
g.zOpenRevision = zRid = fossil_strdup(z);
| > > > > > > > > > > > > > > > > | 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 |
** in=PATTERN Only include files that match the comma-separate
** list of GLOB patterns in PATTERN, as with ex=
**
** ex=PATTERN Omit any file that match PATTERN. PATTERN is a
** comma-separated list of GLOB patterns, where each
** pattern can optionally be quoted using ".." or '..'.
** Any file matching both ex= and in= is excluded.
**
** Robot Defenses:
**
** * If "zip" appears in the robot-restrict setting, then robots are
** not allowed to access this page. Suspected robots will be
** presented with a captcha.
**
** * If "zipX" appears in the robot-restrict setting, then robots are
** restricted in the same way as with "zip", but with exceptions.
** If the check-in for which an archive is requested is a leaf check-in
** and if the robot-zip-leaf setting is true, then the request is
** allowed. Or if the check-in has a tag that matches any of the
** GLOB patterns on the list in the robot-zip-tag setting, then the
** request is allowed. Otherwise, the usual robot defenses are
** activated.
*/
void tarball_page(void){
int rid;
char *zName, *zRid, *zKey;
int nName, nRid;
const char *zInclude; /* The in= query parameter */
const char *zExclude; /* The ex= query parameter */
Blob cacheKey; /* The key to cache */
Glob *pInclude = 0; /* The compiled in= glob pattern */
Glob *pExclude = 0; /* The compiled ex= glob pattern */
Blob tarball; /* Tarball accumulated here */
const char *z;
login_check_credentials();
if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
if( robot_restrict("zip") ) return;
fossil_nice_default();
zName = fossil_strdup(PD("name",""));
z = P("r");
if( z==0 ) z = P("uuid");
if( z==0 ) z = tar_uuid_from_name(&zName);
if( z==0 ) z = "trunk";
g.zOpenRevision = zRid = fossil_strdup(z);
|
| ︙ | ︙ | |||
794 795 796 797 798 799 800 801 802 803 804 805 806 807 |
}
rid = symbolic_name_to_rid(nRid?zRid:zName, "ci");
if( rid==0 ){
cgi_set_status(404, "Not Found");
@ Not found
return;
}
if( nRid==0 && nName>10 ) zName[10] = 0;
/* Compute a unique key for the cache entry based on query parameters */
blob_init(&cacheKey, 0, 0);
blob_appendf(&cacheKey, "/tarball/%z", rid_to_uuid(rid));
blob_appendf(&cacheKey, "/%q", zName);
if( zInclude ) blob_appendf(&cacheKey, ",in=%Q", zInclude);
| > | 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 |
}
rid = symbolic_name_to_rid(nRid?zRid:zName, "ci");
if( rid==0 ){
cgi_set_status(404, "Not Found");
@ Not found
return;
}
if( robot_restrict_zip(rid) ) return;
if( nRid==0 && nName>10 ) zName[10] = 0;
/* Compute a unique key for the cache entry based on query parameters */
blob_init(&cacheKey, 0, 0);
blob_appendf(&cacheKey, "/tarball/%z", rid_to_uuid(rid));
blob_appendf(&cacheKey, "/%q", zName);
if( zInclude ) blob_appendf(&cacheKey, ",in=%Q", zInclude);
|
| ︙ | ︙ | |||
845 846 847 848 849 850 851 |
fossil_free(zName);
fossil_free(zRid);
g.zOpenRevision = 0;
blob_reset(&cacheKey);
cgi_set_content(&tarball);
cgi_set_content_type("application/x-compressed");
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 |
fossil_free(zName);
fossil_free(zRid);
g.zOpenRevision = 0;
blob_reset(&cacheKey);
cgi_set_content(&tarball);
cgi_set_content_type("application/x-compressed");
}
/*
** This routine is called for each check-in on the /download page to
** construct the "extra" information after the description.
*/
void download_extra(
Stmt *pQuery, /* Current row of the timeline query */
int tmFlags, /* Flags to www_print_timeline() */
const char *zThisUser, /* Suppress links to this user */
const char *zThisTag /* Suppress links to this tag */
){
const char *zType = db_column_text(pQuery, 7);
assert( zType!=0 );
if( zType[0]!='c' ){
timeline_extra(pQuery, tmFlags, zThisUser, zThisTag);
}else{
int rid = db_column_int(pQuery, 0);
const char *zUuid = db_column_text(pQuery, 1);
char *zBrName = branch_of_rid(rid);
char *zNm;
if( tmFlags & TIMELINE_COLUMNAR ){
@ <nobr>check-in: \
@ %z(href("%R/info/%!S",zUuid))<span class='timelineHash'>\
@ %S(zUuid)</span></a></nobr><br>
if( fossil_strcmp(zBrName,"trunk")!=0 ){
@ <nobr>branch: \
@ %z(href("%R/timeline?r=%t",zBrName))%h(zBrName)</a></nobr><br>\
}
}else{
if( (tmFlags & TIMELINE_CLASSIC)==0 ){
@ check-in: %z(href("%R/info/%!S",zUuid))\
@ <span class='timelineHash'>%S(zUuid)</span></a>
}
if( (tmFlags & TIMELINE_GRAPH)==0 && fossil_strcmp(zBrName,"trunk")!=0 ){
@ branch: \
@ %z(href("%R/timeline?r=%t",zBrName))%h(zBrName)</a>
}
}
zNm = archive_base_name(rid);
@ %z(href("%R/tarball/%s.tar.gz",zNm))\
@ <button>Tarball</button></a>
@ %z(href("%R/zip/%s.zip",zNm))\
@ <button>ZIP Archive</button></a>
fossil_free(zBrName);
fossil_free(zNm);
}
}
/*
** SETTING: suggested-downloads width=70 block-text
**
** This setting controls the suggested tarball/ZIP downloads on the
** [[/download]] page. The value is a TCL list. Each set of four items
** defines a set of check-ins to be added to the suggestion list.
** The items in each group are:
**
** | COUNT TAG MAX_AGE COMMENT
**
** COUNT is the number of check-ins to match, starting with the most
** recent and working bacwards in time. Check-ins match if they contain
** the tag TAG. If MAX_AGE is not an empty string, then it specifies
** the maximum age of any matching check-in. COMMENT is an optional
** comment for each match.
**
** The special value of "OPEN-LEAF" for TAG matches any check-in that
** is an open leaf.
**
** MAX_AGE is of the form "{AMT UNITS}" where AMT is a floating point
** value and UNITS is one of "seconds", "hours", "days", "weeks", "months",
** or "years". If MAX_AGE is an empty string then there is no age limit.
**
** If COMMENT is not an empty string, then it is an additional comment
** added to the output description of the suggested download. The idea of
** COMMENT is to explain to the reader why a check-in is a suggested
** download.
**
** Example:
**
** | 1 trunk {} {Latest Trunk Check-in}
** | 5 OPEN-LEAF {1 month} {Active Branch}
** | 999 release {1 year} {Official Release}
**
** The value causes the /download page to show the union of the most
** recent trunk check-in of any age, the five most recent
** open leaves within the past month, and essentially
** all releases within the past year. If the same check-in matches more
** than one rule, the COMMENT of the first match is used.
*/
/*
** WEBPAGE: /download
**
** Show a special no-graph timeline of recent important check-ins with
** an opportunity to pull tarballs and ZIPs.
*/
void download_page(void){
Stmt q; /* The actual timeline query */
const char *zTarlistCfg; /* Configuration string */
char **azItem; /* Decomposed elements of zTarlistCfg */
int *anItem; /* Bytes in each term of azItem[] */
int nItem; /* Number of terms in azItem[] */
int i; /* Loop counter */
int tmFlags; /* Timeline display flags */
int n; /* Number of suggested downloads */
double rNow; /* Current time. Julian day number */
int bPlainTextCom; /* Use plain-text comments */
login_check_credentials();
if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
style_set_current_feature("timeline");
style_header("Suggested Downloads");
zTarlistCfg = db_get("suggested-downloads","off");
db_multi_exec(
"CREATE TEMP TABLE tarlist(rid INTEGER PRIMARY KEY, com TEXT);"
);
rNow = db_double(0.0,"SELECT julianday()");
if( !g.interp ) Th_FossilInit(0);
Th_SplitList(g.interp, zTarlistCfg, (int)strlen(zTarlistCfg),
&azItem, &anItem, &nItem);
bPlainTextCom = db_get_boolean("timeline-plaintext",0);
for(i=0; i<nItem-3; i+=4){
int cnt; /* The number of instances of zLabel to use */
char *zLabel; /* The label to match */
double rStart; /* Starting time, julian day number */
char *zComment = 0; /* Comment to apply */
if( anItem[i]==1 && azItem[i][0]=='*' ){
cnt = -1;
}else if( anItem[i]<1 ){
cnt = 0;
}else{
cnt = atoi(azItem[i]);
}
if( cnt==0 ) continue;
zLabel = fossil_strndup(azItem[i+1],anItem[i+1]);
if( anItem[i+2]==0 ){
rStart = 0.0;
}else{
char *zMax = fossil_strndup(azItem[i+2], anItem[i+2]);
double r = atof(zMax);
if( strstr(zMax,"sec") ){
rStart = rNow - r/86400.0;
}else
if( strstr(zMax,"hou") ){
rStart = rNow - r/24.0;
}else
if( strstr(zMax,"da") ){
rStart = rNow - r;
}else
if( strstr(zMax,"wee") ){
rStart = rNow - r*7.0;
}else
if( strstr(zMax,"mon") ){
rStart = rNow - r*30.44;
}else
if( strstr(zMax,"yea") ){
rStart = rNow - r*365.24;
}else
{ /* Default to seconds */
rStart = rNow - r/86400.0;
}
}
if( anItem[i+3]==0 ){
zComment = fossil_strdup("");
}else if( bPlainTextCom ){
zComment = mprintf("** %.*s ** ", anItem[i+3], azItem[i+3]);
}else{
zComment = mprintf("<b>%.*s</b>\n<p>", anItem[i+3], azItem[i+3]);
}
if( fossil_strcmp("OPEN-LEAF",zLabel)==0 ){
db_multi_exec(
"INSERT OR IGNORE INTO tarlist(rid,com)"
" SELECT leaf.rid, %Q FROM leaf, event"
" WHERE event.objid=leaf.rid"
" AND event.mtime>=%.6f"
" AND NOT EXISTS(SELECT 1 FROM tagxref"
" WHERE tagxref.rid=leaf.rid"
" AND tagid=%d AND tagtype>0)"
" ORDER BY event.mtime DESC LIMIT %d",
zComment, rStart, TAG_CLOSED, cnt
);
}else{
db_multi_exec(
"WITH taglist(tid) AS"
" (SELECT tagid FROM tag WHERE tagname GLOB 'sym-%q')"
"INSERT OR IGNORE INTO tarlist(rid,com)"
" SELECT event.objid, %Q FROM event CROSS JOIN tagxref"
" WHERE event.type='ci'"
" AND event.mtime>=%.6f"
" AND tagxref.tagid IN taglist"
" AND tagtype>0"
" AND tagxref.rid=event.objid"
" ORDER BY event.mtime DESC LIMIT %d",
zLabel, zComment, rStart, cnt
);
}
fossil_free(zLabel);
fossil_free(zComment);
}
Th_Free(g.interp, azItem);
n = db_int(0, "SELECT count(*) FROM tarlist");
if( n==0 ){
@ <h2>No tarball/ZIP suggestions are available at this time</h2>
}else{
@ <h2>%d(n) Tarball/ZIP Download Suggestion%s(n>1?"s":""):</h2>
db_prepare(&q,
"WITH matches AS (%s AND blob.rid IN (SELECT rid FROM tarlist))\n"
"SELECT blobRid, uuid, timestamp,"
" com||comment,"
" user, leaf, bgColor, eventType, tags, tagid, brief, mtime"
" FROM matches JOIN tarlist ON tarlist.rid=blobRid"
" ORDER BY matches.mtime DESC",
timeline_query_for_www()
);
tmFlags = TIMELINE_DISJOINT | TIMELINE_NOSCROLL | TIMELINE_COLUMNAR
| TIMELINE_BRCOLOR;
www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, download_extra);
db_finalize(&q);
}
if( g.perm.Clone ){
char *zNm = fossil_strdup(db_get("project-name","clone"));
sanitize_name(zNm);
@ <hr>
@ <h2>You Can Clone This Repository</h2>
@
@ <p>Clone this repository by running a command similar to the following:
@ <blockquote><pre>
@ fossil clone %s(g.zBaseURL) %h(zNm).fossil
@ </pre></blockquote>
@ <p>A clone gives you local access to all historical content.
@ Cloning is a bandwidth- and CPU-efficient alternative to extracting
@ multiple tarballs and ZIPs.
@ Do a web search for "fossil clone" or similar to find additional
@ information about using a cloned Fossil repository. Or ask your
@ favorite AI how to extract content from a Fossil clone.
fossil_free(zNm);
}
style_finish_page();
}
/*
** WEBPAGE: rchvdwnld
**
** Short for "archive download". This page should have a single name=
** query parameter that is a check-in hash. It present a menu of possible
** download options for that check-in, including tarball, ZIP, or SQLAR.
**
** This is a utility page. The /dir and /tree pages sometimes have a
** "Download" option in their submenu which redirects here. Those pages
** used to have separate "Tarball" and "ZIP" submenu entries, but as
** submenu entries appear in alphabetical order, that caused the two
** submenu entries to be separated from one another, which is distracting.
*/
void rchvdwnld_page(void){
const char *zUuid;
char *zBase;
int nUuid;
int rid;
login_check_credentials();
if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
if( robot_restrict("zip") || robot_restrict("download") ) return;
zUuid = P("name");
if( zUuid==0
|| (nUuid = (int)strlen(zUuid))<6
|| !validate16(zUuid,-1)
|| (rid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUuid))==0
|| !db_exists("SELECT 1 from event WHERE type='ci' AND objid=%d",rid)
){
fossil_redirect_home();
}
zUuid = db_text(zUuid, "SELECT uuid FROM blob WHERE rid=%d", rid);
style_header("Downloads For Check-in %!S", zUuid);
zBase = archive_base_name(rid);
@ <div class="section accordion">Downloads for check-in \
@ %z(href("%R/info/%!S",zUuid))%S(zUuid)</a></div>
@ <div class="accordion_panel">
@ <table class="label-value">
@ <tr>
@ <th>Tarball:</th>
@ <td>%z(href("%R/tarball/%s.tar.gz",zBase))\
@ %s(g.zBaseURL)/tarball/%s(zBase).tar.gz</a></td>
@ </tr>
@
@ <tr>
@ <th>ZIP:</th>
@ <td>%z(href("%R/zip/%s.zip",zBase))\
@ %s(g.zBaseURL)/zip/%s(zBase).zip</a></td>
@ </tr>
@
@ <tr>
@ <th>SQLAR:</th>
@ <td>%z(href("%R/sqlar/%s.sqlar",zBase))\
@ %s(g.zBaseURL)/sqlar/%s(zBase).sqlar</a></td>
@ </tr>
@ </table></div>
fossil_free(zBase);
@ <div class="section accordion">Context</div><div class="accordion_panel">
render_checkin_context(rid, 0, 0, 0);
@ </div>
style_finish_page();
}
|
Changes to src/th_lang.c.
| ︙ | ︙ | |||
954 955 956 957 958 959 960 |
**
** string match PATTERN STRING
**
*/
static int string_match_command(
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
){
| | | 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 |
**
** string match PATTERN STRING
**
*/
static int string_match_command(
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
){
extern char *fossil_strndup(const char*,ssize_t);
extern void fossil_free(void*);
char *zPat, *zStr;
int rc;
if( argc!=4 ){
return Th_WrongNumArgs(interp, "string match pattern string");
}
zPat = fossil_strndup(argv[2],TH1_LEN(argl[2]));
|
| ︙ | ︙ |
Changes to src/th_main.c.
| ︙ | ︙ | |||
2143 2144 2145 2146 2147 2148 2149 |
if( fossil_strcmp(argv[nArg], "-nocase")==0 ){
noCase = 1; nArg++;
}
if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++;
if( nArg+2!=argc ){
return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS);
}
| | | 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 |
if( fossil_strcmp(argv[nArg], "-nocase")==0 ){
noCase = 1; nArg++;
}
if( fossil_strcmp(argv[nArg], "--")==0 ) nArg++;
if( nArg+2!=argc ){
return Th_WrongNumArgs(interp, REGEXP_WRONGNUMARGS);
}
zErr = fossil_re_compile(&pRe, argv[nArg], noCase);
if( !zErr ){
Th_SetResultInt(interp, re_match(pRe,
(const unsigned char *)argv[nArg+1], TH1_LEN(argl[nArg+1])));
rc = TH_OK;
}else{
Th_SetResult(interp, zErr, -1);
rc = TH_ERROR;
|
| ︙ | ︙ | |||
2200 2201 2202 2203 2204 2205 2206 |
url_parse_local(argv[nArg], 0, &urlData);
if( urlData.isSsh || urlData.isFile ){
Th_ErrorMessage(interp, "url must be http:// or https://", 0, 0);
return TH_ERROR;
}
zRegexp = db_get("th1-uri-regexp", 0);
if( zRegexp && zRegexp[0] ){
| | | 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 |
url_parse_local(argv[nArg], 0, &urlData);
if( urlData.isSsh || urlData.isFile ){
Th_ErrorMessage(interp, "url must be http:// or https://", 0, 0);
return TH_ERROR;
}
zRegexp = db_get("th1-uri-regexp", 0);
if( zRegexp && zRegexp[0] ){
const char *zErr = fossil_re_compile(&pRe, zRegexp, 0);
if( zErr ){
Th_SetResult(interp, zErr, -1);
return TH_ERROR;
}
}
if( !pRe || !re_match(pRe, (const unsigned char *)urlData.canonical, -1) ){
Th_SetResult(interp, "url not allowed", -1);
|
| ︙ | ︙ | |||
2963 2964 2965 2966 2967 2968 2969 |
Th_ReturnCodeName(rc, 0), TH1_LEN(nTrRes), zTrRes);
}
if( rc!=TH_OK ) break;
z += i;
if( z[0] ){ z += 6; }
i = 0;
}else{
| | | 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 |
Th_ReturnCodeName(rc, 0), TH1_LEN(nTrRes), zTrRes);
}
if( rc!=TH_OK ) break;
z += i;
if( z[0] ){ z += 6; }
i = 0;
}else{
i += strcspn(&z[i+1], "<$") + 1;
}
}
if( rc==TH_ERROR ){
zResult = (char*)Th_GetResult(g.interp, &n);
sendError(pOut,zResult, n, 1);
}else{
sendText(pOut,z, i, 0);
|
| ︙ | ︙ |
Changes to src/timeline.c.
| ︙ | ︙ | |||
113 114 115 116 117 118 119 | #define TIMELINE_SHOWRID 0x0000400 /* Show RID values in addition to hashes */ #define TIMELINE_BISECT 0x0000800 /* Show supplemental bisect information */ #define TIMELINE_COMPACT 0x0001000 /* Use the "compact" view style */ #define TIMELINE_VERBOSE 0x0002000 /* Use the "detailed" view style */ #define TIMELINE_MODERN 0x0004000 /* Use the "modern" view style */ #define TIMELINE_COLUMNAR 0x0008000 /* Use the "columns" view style */ #define TIMELINE_CLASSIC 0x0010000 /* Use the "classic" view style */ | > > | | 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 | #define TIMELINE_SHOWRID 0x0000400 /* Show RID values in addition to hashes */ #define TIMELINE_BISECT 0x0000800 /* Show supplemental bisect information */ #define TIMELINE_COMPACT 0x0001000 /* Use the "compact" view style */ #define TIMELINE_VERBOSE 0x0002000 /* Use the "detailed" view style */ #define TIMELINE_MODERN 0x0004000 /* Use the "modern" view style */ #define TIMELINE_COLUMNAR 0x0008000 /* Use the "columns" view style */ #define TIMELINE_CLASSIC 0x0010000 /* Use the "classic" view style */ #define TIMELINE_SIMPLE 0x0020000 /* Use the "simple" view style */ #define TIMELINE_INLINE 0x0033000 /* Mask for views with in-line display */ #define TIMELINE_VIEWS 0x003f000 /* Mask for all of the view styles */ #define TIMELINE_NOSCROLL 0x0100000 /* Don't scroll to the selection */ #define TIMELINE_FILEDIFF 0x0200000 /* Show File differences, not ckin diffs */ #define TIMELINE_CHPICK 0x0400000 /* Show cherrypick merges */ #define TIMELINE_FILLGAPS 0x0800000 /* Dotted lines for missing nodes */ #define TIMELINE_XMERGE 0x1000000 /* Omit merges from off-graph nodes */ #define TIMELINE_NOTKT 0x2000000 /* Omit extra ticket classes */ #define TIMELINE_FORUMTXT 0x4000000 /* Render all forum messages */ |
| ︙ | ︙ | |||
168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
if( pPost ){
sqlite3_result_text(context, pPost->zWiki, -1, SQLITE_TRANSIENT);
manifest_destroy(pPost);
}
}
/*
** Output a timeline in the web format given a query. The query
** should return these columns:
**
** 0. rid
** 1. artifact hash
** 2. Date/Time
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 |
if( pPost ){
sqlite3_result_text(context, pPost->zWiki, -1, SQLITE_TRANSIENT);
manifest_destroy(pPost);
}
}
/*
** This routine generates the default "extra" text after the description
** in a timeline.
**
** Example: "(check-in: [abcdefg], user: drh, tags: trunk)"
**
** This routine is used if no xExtra argument is supplied to
** www_print_timeline().
*/
void timeline_extra(
Stmt *pQuery, /* Current row of the timeline query */
int tmFlags, /* Flags to www_print_timeline() */
const char *zThisUser, /* Suppress links to this user */
const char *zThisTag /* Suppress links to this tag */
){
int rid = db_column_int(pQuery, 0);
const char *zUuid = db_column_text(pQuery, 1);
const char *zDate = db_column_text(pQuery, 2);
const char *zType = db_column_text(pQuery, 7);
const char *zUser = db_column_text(pQuery, 4);
const char *zTagList = db_column_text(pQuery, 8);
int tagid = db_column_int(pQuery, 9);
const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
if( (tmFlags & TIMELINE_INLINE)!=0 ){
cgi_printf("(");
}
if( (tmFlags & TIMELINE_CLASSIC)==0 ){
if( zType[0]=='c' ){
const char *zPrefix = 0;
static int markLeaves = -1;
if( markLeaves<0 ){
markLeaves = db_get_int("timeline-mark-leaves",1);
if( markLeaves<0 ) markLeaves = 1;
}
if( strcmp(zUuid, MANIFEST_UUID)==0 ){
/* This will only ever happen when Fossil is drawing a timeline for
** its own self-host repository. If the timeline shows the specific
** check-in corresponding to the current executable, then tag that
** check-in with "self" */
zPrefix = "self ";
}else if( markLeaves && db_column_int(pQuery,5) ){
if( markLeaves==1 ){
zPrefix = has_closed_tag(rid) ? "closed " : "leaf ";
}else{
zPrefix = has_closed_tag(rid) ?
"<span class='timelineLeaf'>Closed-Leaf</span>\n" :
"<span class='timelineLeaf'>Leaf</span>\n";
}
}
cgi_printf("%scheck-in: %z<span class='timelineHash'>"
"%S</span></a> ",
zPrefix, href("%R/info/%!S",zUuid),zUuid);
}else if( zType[0]=='e' && tagid ){
cgi_printf("technote: ");
hyperlink_to_event_tagid(tagid<0?-tagid:tagid);
}else{
cgi_printf("artifact: %z%S</a> ",
href("%R/info/%!S",zUuid),zUuid);
}
}else if( zType[0]=='g' || zType[0]=='w' || zType[0]=='t'
|| zType[0]=='n' || zType[0]=='f'){
cgi_printf("artifact: %z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
}
if( (tmFlags & TIMELINE_SIMPLE)!=0 ){
@ <span class='timelineEllipsis' id='ellipsis-%d(rid)' \
@ data-id='%d(rid)'>...</span>
@ <span class='clutter' id='detail-%d(rid)'>
}
if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){
char *zLink;
if( zType[0]!='f' || (tmFlags & TIMELINE_FORUMTXT)==0 ){
zLink = mprintf("%R/timeline?u=%h&c=%t&y=a", zDispUser, zDate);
}else{
zLink = mprintf("%R/timeline?u=%h&c=%t&y=a&vfx", zDispUser, zDate);
}
cgi_printf("user: %z%h</a>", href("%z",zLink), zDispUser);
}else{
cgi_printf("user: %h", zDispUser);
}
/* Generate the "tags: TAGLIST" at the end of the comment, together
** with hyperlinks to the tag list.
*/
if( zTagList && zTagList[0]==0 ) zTagList = 0;
if( zTagList ){
if( g.perm.Hyperlink ){
int i;
const char *z = zTagList;
Blob links;
blob_zero(&links);
while( z && z[0] ){
for(i=0; z[i] && (z[i]!=',' || z[i+1]!=' '); i++){}
if( zThisTag==0 || memcmp(z, zThisTag, i)!=0 || zThisTag[i]!=0 ){
blob_appendf(&links,
"%z%#h</a>%.2s",
href("%R/timeline?r=%#t&c=%t",i,z,zDate), i,z, &z[i]
);
}else{
blob_appendf(&links, "%#h", i+2, z);
}
if( z[i]==0 ) break;
z += i+2;
}
cgi_printf(" tags: %s", blob_str(&links));
blob_reset(&links);
}else{
cgi_printf(" tags: %h", zTagList);
}
}
if( tmFlags & TIMELINE_SHOWRID ){
int srcId = delta_source_rid(rid);
if( srcId ){
cgi_printf(" id: %z%d←%d</a>",
href("%R/deltachain/%d",rid), rid, srcId);
}else{
cgi_printf(" id: %z%d</a>",
href("%R/deltachain/%d",rid), rid);
}
}
tag_private_status(rid);
if( (tmFlags & TIMELINE_SIMPLE)!=0 ){
cgi_printf("</span>"); /* End of the declutter span */
}
/* End timelineDetail */
if( (tmFlags & TIMELINE_INLINE)!=0 ){
cgi_printf(")");
}
}
/*
** SETTING: timeline-truncate-at-blank boolean default=off
**
** If enabled, check-in comments displayed on the timeline are truncated
** at the first blank line of the comment text. The comment text after
** the first blank line is only seen in the /info or similar pages that
** show details about the check-in.
*/
/*
** SETTING: timeline-tslink-info boolean default=off
**
** The hyperlink on the timestamp associated with each timeline entry,
** on the far left-hand side of the screen, normally targets another
** /timeline page that shows the entry in context. However, if this
** option is turned on, that hyperlink targets the /info page showing
** the details of the entry.
*/
/*
** SETTING: timeline-mark-leaves width=5 default=1
**
** Determine whether or not leaf check-ins are marked as such in the
** details section of the timeline. The value is an integer between 0
** and 2:
**
** 0 Do not show any special marking for leaf check-ins.
**
** 1 Show just "leaf" or "closed"
**
** 2 Show "Leaf" or "Closed-Leaf" with emphasis
**
** The default is currently 1. Prior to 2025-10-19, the default was 2.
** This setting has no effect on the "Classic" view, which always behaves
** as if the setting were 2.
*/
/*
** Output a timeline in the web format given a query. The query
** should return these columns:
**
** 0. rid
** 1. artifact hash
** 2. Date/Time
|
| ︙ | ︙ | |||
192 193 194 195 196 197 198 | Stmt *pQuery, /* Query to implement the timeline */ int tmFlags, /* Flags controlling display behavior */ const char *zThisUser, /* Suppress links to this user */ const char *zThisTag, /* Suppress links to this tag */ Matcher *pLeftBranch, /* Comparison function to use for zLeftBranch */ int selectedRid, /* Highlight the line with this RID value or zero */ int secondRid, /* Secondary highlight (or zero) */ | | > > > < < < < < < < < < < < < < < < < < > > < | < < < | 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 |
Stmt *pQuery, /* Query to implement the timeline */
int tmFlags, /* Flags controlling display behavior */
const char *zThisUser, /* Suppress links to this user */
const char *zThisTag, /* Suppress links to this tag */
Matcher *pLeftBranch, /* Comparison function to use for zLeftBranch */
int selectedRid, /* Highlight the line with this RID value or zero */
int secondRid, /* Secondary highlight (or zero) */
void (*xExtra)(Stmt*,int,const char*,const char*) /* generate "extra" text */
){
int mxWikiLen;
Blob comment;
int prevTagid = 0;
int suppressCnt = 0;
char zPrevDate[20];
GraphContext *pGraph = 0;
int prevWasDivider = 0; /* True if previous output row was <hr> */
int fchngQueryInit = 0; /* True if fchngQuery is initialized */
Stmt fchngQuery; /* Query for file changes on check-ins */
int pendingEndTr = 0; /* True if a </td></tr> is needed */
int vid = 0; /* Current check-out version */
int dateFormat = 0; /* 0: HH:MM (default) */
int bCommentGitStyle = 0; /* Only show comments through first blank line */
const char *zStyle; /* Sub-name for classes for the style */
const char *zDateFmt;
int iTableId = timeline_tableid();
int bTimestampLinksToInfo; /* True if timestamp hyperlinks go to the /info
** page rather than the /timeline page */
char *zMainBranch = db_get("main-branch","trunk");
if( cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){
vid = db_lget_int("checkout", 0);
}
if( xExtra==0 ) xExtra = timeline_extra;
zPrevDate[0] = 0;
mxWikiLen = db_get_int("timeline-max-comment", 0);
dateFormat = db_get_int("timeline-date-format", 0);
bCommentGitStyle = db_get_int("timeline-truncate-at-blank", 0);
bTimestampLinksToInfo = db_get_boolean("timeline-tslink-info", 0);
if( (tmFlags & TIMELINE_VIEWS)==0 ){
tmFlags |= timeline_ss_cookie();
}
if( tmFlags & TIMELINE_COLUMNAR ){
zStyle = "Columnar";
}else if( tmFlags & TIMELINE_COMPACT ){
zStyle = "Compact";
}else if( tmFlags & TIMELINE_SIMPLE ){
zStyle = "Simple";
}else if( tmFlags & TIMELINE_VERBOSE ){
zStyle = "Verbose";
}else if( tmFlags & TIMELINE_CLASSIC ){
zStyle = "Classic";
}else{
zStyle = "Modern";
}
zDateFmt = P("datefmt");
if( zDateFmt ) dateFormat = atoi(zDateFmt);
pGraph = graph_init();
if( (tmFlags & TIMELINE_CHPICK)!=0
&& !db_table_exists("repository","cherrypick")
){
tmFlags &= ~TIMELINE_CHPICK;
}
@ <table id="timelineTable%d(iTableId)" class="timelineTable"> \
@ <!-- tmFlags: 0x%x(tmFlags) -->
blob_zero(&comment);
while( db_step(pQuery)==SQLITE_ROW ){
int rid = db_column_int(pQuery, 0);
const char *zUuid = db_column_text(pQuery, 1);
int isLeaf = db_column_int(pQuery, 5);
const char *zBgClr = db_column_text(pQuery, 6);
const char *zDate = db_column_text(pQuery, 2);
const char *zType = db_column_text(pQuery, 7);
const char *zUser = db_column_text(pQuery, 4);
int tagid = db_column_int(pQuery, 9);
char *zBr = 0; /* Branch */
int commentColumn = 3; /* Column containing comment text */
int modPending; /* Pending moderation */
char *zDateLink; /* URL for the link on the timestamp */
int drawDetailEllipsis; /* True to show ellipsis in place of detail */
int gidx = 0; /* Graph row identifier */
int isSelectedOrCurrent = 0; /* True if current row is selected */
|
| ︙ | ︙ | |||
432 433 434 435 436 437 438 |
&& (pGraph || zBgClr==0 || (tmFlags & (TIMELINE_BRCOLOR|TIMELINE_DELTA))!=0)
){
zBr = branch_of_rid(rid);
if( zBgClr==0 || (tmFlags & TIMELINE_BRCOLOR)!=0 ){
/* If no background color is specified, use a color based on the
** branch name */
if( tmFlags & (TIMELINE_DELTA|TIMELINE_NOCOLOR) ){
| | > | | | | | | | | | | | | | | | | | | | | | | > | 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 |
&& (pGraph || zBgClr==0 || (tmFlags & (TIMELINE_BRCOLOR|TIMELINE_DELTA))!=0)
){
zBr = branch_of_rid(rid);
if( zBgClr==0 || (tmFlags & TIMELINE_BRCOLOR)!=0 ){
/* If no background color is specified, use a color based on the
** branch name */
if( tmFlags & (TIMELINE_DELTA|TIMELINE_NOCOLOR) ){
}else if( zBr==0 || strcmp(zBr,zMainBranch)==0 ){
zBgClr = 0;
}else{
zBgClr = hash_color(zBr);
}
}
}
if( zType[0]=='c' && pGraph ){
int nParent = 0;
int nCherrypick = 0;
GraphRowId aParent[GR_MAX_RAIL];
static Stmt qparent;
if( tmFlags & TIMELINE_GRAPH ){
db_static_prepare(&qparent,
"SELECT pid FROM plink"
" WHERE cid=:rid AND pid NOT IN phantom"
" ORDER BY isprim DESC /*sort*/"
);
db_bind_int(&qparent, ":rid", rid);
while( db_step(&qparent)==SQLITE_ROW && nParent<count(aParent) ){
aParent[nParent++] = db_column_int(&qparent, 0);
}
db_reset(&qparent);
if( (tmFlags & TIMELINE_CHPICK)!=0 && nParent>0 ){
static Stmt qcherrypick;
db_static_prepare(&qcherrypick,
"SELECT parentid FROM cherrypick"
" WHERE childid=:rid AND parentid NOT IN phantom"
);
db_bind_int(&qcherrypick, ":rid", rid);
while( db_step(&qcherrypick)==SQLITE_ROW && nParent<count(aParent) ){
aParent[nParent++] = db_column_int(&qcherrypick, 0);
nCherrypick++;
}
db_reset(&qcherrypick);
}
}
gidx = graph_add_row(pGraph, rid, nParent, nCherrypick, aParent,
zBr, zBgClr, zUuid,
isLeaf ? isLeaf + 2 * has_closed_tag(rid) : 0);
@ <div id="m%d(gidx)" class="tl-nodemark"></div>
}else if( zType[0]=='e' && pGraph && zBgClr && zBgClr[0] ){
/* For technotes, make a graph node with nParent==(-1). This will
|
| ︙ | ︙ | |||
596 597 598 599 600 601 602 |
@ %W(blob_str(&truncated))
blob_reset(&truncated);
drawDetailEllipsis = 0;
}else{
cgi_printf("%W",blob_str(&comment));
}
}
| < < < < < < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < < < | | 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 |
@ %W(blob_str(&truncated))
blob_reset(&truncated);
drawDetailEllipsis = 0;
}else{
cgi_printf("%W",blob_str(&comment));
}
}
@ </span>
blob_reset(&comment);
/* Generate extra information and hyperlinks that follow the comment.
** Example: "(check-in: [abcdefg], user: drh, tags: trunk)"
*/
if( drawDetailEllipsis ){
@ <span class='timelineEllipsis' id='ellipsis-%d(rid)' \
@ data-id='%d(rid)'>...</span>
}
if( tmFlags & TIMELINE_COLUMNAR ){
if( !isSelectedOrCurrent ){
@ <td class="timelineDetailCell%s(zExtraClass)" id='md%d(gidx)'>
}else{
@ <td class="timelineDetailCell%s(zExtraClass)">
}
}
if( tmFlags & TIMELINE_COMPACT ){
cgi_printf("<span class='clutter' id='detail-%d'>",rid);
}
cgi_printf("<span class='timeline%sDetail'>", zStyle);
xExtra(pQuery, tmFlags, zThisUser, zThisTag);
if( tmFlags & TIMELINE_COMPACT ){
@ </span></span>
}else{
@ </span>
}
/* Generate the file-change list if requested */
if( (tmFlags & (TIMELINE_FCHANGES|TIMELINE_FRENAMES))!=0
&& zType[0]=='c' && g.perm.Hyperlink
){
int inUl = 0;
if( !fchngQueryInit ){
db_prepare(&fchngQuery,
|
| ︙ | ︙ | |||
906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 |
int iTopRow; /* Index of the top row of the graph */
int fileDiff; /* True for file diff. False for check-in diff */
int omitDescenders; /* True to omit descenders */
int scrollToSelect; /* True to scroll to the selection */
int dwellTimeout; /* Milliseconds to wait for tooltips to show */
int closeTimeout; /* Milliseconds to wait for tooltips to close */
u8 *aiMap; /* The rail map */
iRailPitch = atoi(PD("railpitch","0"));
showArrowheads = skin_detail_boolean("timeline-arrowheads");
circleNodes = skin_detail_boolean("timeline-circle-nodes");
colorGraph = skin_detail_boolean("timeline-color-graph-lines");
iTopRow = pGraph->pFirst ? pGraph->pFirst->idx : 0;
omitDescenders = (tmFlags & TIMELINE_DISJOINT)!=0;
fileDiff = (tmFlags & TIMELINE_FILEDIFF)!=0;
| > > | 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 |
int iTopRow; /* Index of the top row of the graph */
int fileDiff; /* True for file diff. False for check-in diff */
int omitDescenders; /* True to omit descenders */
int scrollToSelect; /* True to scroll to the selection */
int dwellTimeout; /* Milliseconds to wait for tooltips to show */
int closeTimeout; /* Milliseconds to wait for tooltips to close */
u8 *aiMap; /* The rail map */
u8 bNoGraph; /* True to show a minimal graph */
bNoGraph = (tmFlags & TIMELINE_GRAPH)==0;
iRailPitch = atoi(PD("railpitch","0"));
showArrowheads = skin_detail_boolean("timeline-arrowheads");
circleNodes = skin_detail_boolean("timeline-circle-nodes");
colorGraph = skin_detail_boolean("timeline-color-graph-lines");
iTopRow = pGraph->pFirst ? pGraph->pFirst->idx : 0;
omitDescenders = (tmFlags & TIMELINE_DISJOINT)!=0;
fileDiff = (tmFlags & TIMELINE_FILEDIFF)!=0;
|
| ︙ | ︙ | |||
928 929 930 931 932 933 934 |
@ "iRailPitch": %d(iRailPitch),
@ "colorGraph": %d(colorGraph),
@ "nomo": %d(PB("nomo")),
@ "iTopRow": %d(iTopRow),
@ "omitDescenders": %d(omitDescenders),
@ "fileDiff": %d(fileDiff),
@ "scrollToSelect": %d(scrollToSelect),
| | | 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 |
@ "iRailPitch": %d(iRailPitch),
@ "colorGraph": %d(colorGraph),
@ "nomo": %d(PB("nomo")),
@ "iTopRow": %d(iTopRow),
@ "omitDescenders": %d(omitDescenders),
@ "fileDiff": %d(fileDiff),
@ "scrollToSelect": %d(scrollToSelect),
@ "nrail": %d(bNoGraph?1:pGraph->mxRail+1),
@ "baseUrl": "%R",
@ "dwellTimeout": %d(dwellTimeout),
@ "closeTimeout": %d(closeTimeout),
@ "hashDigits": %d(hash_digits(1)),
@ "bottomRowId": "btm-%d(iTableId)",
if( pGraph->nRow==0 ){
@ "rowinfo": null
|
| ︙ | ︙ | |||
990 991 992 993 994 995 996 |
* br: The branch to which the artifact belongs
*/
aiMap = pGraph->aiRailMap;
for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
int k = 0;
cgi_printf("{\"id\":%d,", pRow->idx);
cgi_printf("\"bg\":\"%s\",", pRow->zBgClr);
| > > > | > | > > | | 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 |
* br: The branch to which the artifact belongs
*/
aiMap = pGraph->aiRailMap;
for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
int k = 0;
cgi_printf("{\"id\":%d,", pRow->idx);
cgi_printf("\"bg\":\"%s\",", pRow->zBgClr);
if( bNoGraph ){
cgi_printf("\"r\":0,"); /* Chng to ":-1" to omit node circles */
}else{
cgi_printf("\"r\":%d,", pRow->iRail>=0 ? aiMap[pRow->iRail] : -1);
}
if( pRow->bDescender && !bNoGraph ){
cgi_printf("\"d\":%d,", pRow->bDescender);
}
if( pRow->mergeOut>=0 ){
cgi_printf("\"mo\":%d,", aiMap[pRow->mergeOut]);
if( pRow->mergeUpto==0 ) pRow->mergeUpto = pRow->idx;
cgi_printf("\"mu\":%d,", pRow->mergeUpto);
if( pRow->cherrypickUpto>0 && pRow->cherrypickUpto<=pRow->mergeUpto ){
cgi_printf("\"cu\":%d,", pRow->cherrypickUpto);
}
}
if( bNoGraph ){
cgi_printf("\"u\":-1,");
}else if( pRow->isStepParent ){
cgi_printf("\"sb\":%d,", pRow->aiRiser[pRow->iRail]);
}else{
cgi_printf("\"u\":%d,", pRow->aiRiser[pRow->iRail]);
}
k = 0;
if( pRow->isLeaf ) k |= 1;
if( pRow->isLeaf & 2) k |= 2;
|
| ︙ | ︙ | |||
1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 |
assert( i<=count(az) );
}
if( i>2 ){
style_submenu_multichoice("y", i/2, az, isDisabled);
}
}
/*
** Return the default value for the "ss" cookie or query parameter.
** The "ss" cookie determines the graph style. See the
** timeline_view_styles[] global constant for a list of choices.
*/
const char *timeline_default_ss(void){
static const char *zSs = 0;
| > > > > > > > > > > > > > > > > | 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 |
assert( i<=count(az) );
}
if( i>2 ){
style_submenu_multichoice("y", i/2, az, isDisabled);
}
}
/*
** SETTING: timeline-default-style width=5 default=m
**
** This setting determines the default "view style" for timelines.
** The setting should be a single character, one of the following:
**
** c Compact
** j Columnar
** m Modern
** s Simple
** v Verbose
** x Classic
**
** The default value is m (Modern).
*/
/*
** Return the default value for the "ss" cookie or query parameter.
** The "ss" cookie determines the graph style. See the
** timeline_view_styles[] global constant for a list of choices.
*/
const char *timeline_default_ss(void){
static const char *zSs = 0;
|
| ︙ | ︙ | |||
1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 |
const char *v = cookie_value("ss",0);
if( v==0 ) v = timeline_default_ss();
switch( v[0] ){
case 'c': tmFlags = TIMELINE_COMPACT; break;
case 'v': tmFlags = TIMELINE_VERBOSE; break;
case 'j': tmFlags = TIMELINE_COLUMNAR; break;
case 'x': tmFlags = TIMELINE_CLASSIC; break;
default: tmFlags = TIMELINE_MODERN; break;
}
return tmFlags;
}
/* Available timeline display styles, together with their y= query
** parameter names.
*/
const char *const timeline_view_styles[] = {
"m", "Modern View",
"j", "Columnar View",
"c", "Compact View",
"v", "Verbose View",
"x", "Classic View",
};
#if INTERFACE
| > > | | 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 |
const char *v = cookie_value("ss",0);
if( v==0 ) v = timeline_default_ss();
switch( v[0] ){
case 'c': tmFlags = TIMELINE_COMPACT; break;
case 'v': tmFlags = TIMELINE_VERBOSE; break;
case 'j': tmFlags = TIMELINE_COLUMNAR; break;
case 'x': tmFlags = TIMELINE_CLASSIC; break;
case 's': tmFlags = TIMELINE_SIMPLE; break;
default: tmFlags = TIMELINE_MODERN; break;
}
return tmFlags;
}
/* Available timeline display styles, together with their y= query
** parameter names.
*/
const char *const timeline_view_styles[] = {
"m", "Modern View",
"j", "Columnar View",
"c", "Compact View",
"s", "Simple View",
"v", "Verbose View",
"x", "Classic View",
};
#if INTERFACE
# define N_TIMELINE_VIEW_STYLE 6
#endif
/*
** Add the select/option box to the timeline submenu that is used to
** set the ss= parameter that determines the viewing mode.
**
** Return the TIMELINE_* value appropriate for the view-style.
|
| ︙ | ︙ | |||
1361 1362 1363 1364 1365 1366 1367 |
**
** with an optional "Z" timeline modifier at the end. Return true if
** the input is a valid date space and false if not.
*/
static int timeline_is_datespan(const char *zDay){
size_t n = strlen(zDay);
int i, d, m;
| | | 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 |
**
** with an optional "Z" timeline modifier at the end. Return true if
** the input is a valid date space and false if not.
*/
static int timeline_is_datespan(const char *zDay){
size_t n = strlen(zDay);
int i, d, m;
if( n<17 || n>18 ) return 0;
if( n==18 ){
if( zDay[17]!='Z' && zDay[17]!='z' ) return 0;
n--;
}
if( zDay[8]!='-' ) return 0;
for(i=0; i<17 && (fossil_isdigit(zDay[i]) || i==8); i++){}
|
| ︙ | ︙ | |||
1387 1388 1389 1390 1391 1392 1393 | return 1; } /* ** Find the first check-in encountered with a particular tag ** when moving either forwards are backwards in time from a ** particular starting point (iFrom). Return the rid of that | | | | 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 |
return 1;
}
/*
** Find the first check-in encountered with a particular tag
** when moving either forwards are backwards in time from a
** particular starting point (iFrom). Return the rid of that
** first check-in. If there are no check-ins in the descendent
** or ancestor set of check-in iFrom that match the tag, then
** return 0.
*/
static int timeline_endpoint(
int iFrom, /* Starting point */
const char *zEnd, /* Tag we are searching for */
int bForward /* 1: forwards in time (descendants) 0: backwards */
){
int tagId;
int endId = 0;
Stmt q;
int ans = 0;
|
| ︙ | ︙ | |||
1588 1589 1590 1591 1592 1593 1594 | ** ft=DESCENDANT ... going forward to DESCENDANT ** dp=CHECKIN Same as 'd=CHECKIN&p=CHECKIN' ** dp2=CKIN2 Same as 'd2=CKIN2&p2=CKIN2' ** df=CHECKIN Same as 'd=CHECKIN&n1=all&nd'. Mnemonic: "Derived From" ** bt=CHECKIN "Back To". Show ancenstors going back to CHECKIN ** p=CX ... from CX back to time of CHECKIN ** from=CX ... path from CX back to CHECKIN | | | | 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 | ** ft=DESCENDANT ... going forward to DESCENDANT ** dp=CHECKIN Same as 'd=CHECKIN&p=CHECKIN' ** dp2=CKIN2 Same as 'd2=CKIN2&p2=CKIN2' ** df=CHECKIN Same as 'd=CHECKIN&n1=all&nd'. Mnemonic: "Derived From" ** bt=CHECKIN "Back To". Show ancenstors going back to CHECKIN ** p=CX ... from CX back to time of CHECKIN ** from=CX ... path from CX back to CHECKIN ** ft=CHECKIN "Forward To": Show descendents forward to CHECKIN ** d=CX ... from CX up to the time of CHECKIN ** from=CX ... path from CX up to CHECKIN ** t=TAG Show only check-ins with the given TAG ** r=TAG Same as 't=TAG&rel'. Mnemonic: "Related" ** tl=TAGLIST Same as 't=TAGLIST&ms=brlist'. Mnemonic: "Tag List" ** rl=TAGLIST Same as 'r=TAGLIST&ms=brlist'. Mnemonic: "Related List" ** ml=TAGLIST Same as 'tl=TAGLIST&mionly'. Mnemonic: "Merge-in List" ** sl=TAGLIST "Sort List". Draw TAGLIST branches ordered left to right. ** rel Show related check-ins as well as those matching t=TAG ** mionly Show related parents but not related children. ** nowiki Do not show wiki associated with branch or tag ** ms=MATCHSTYLE Set tag name match algorithm. One of "exact", "glob", ** "like", or "regexp". ** u=USER Only show items associated with USER ** y=TYPE 'ci', 'w', 't', 'n', 'e', 'f', or 'all'. ** ss=VIEWSTYLE c: "Compact", v: "Verbose", m: "Modern", j: "Columnar", ** x: "Classic". ** advm Use the "Advanced" or "Busy" menu design. ** ng No Graph. ** ncp Omit cherrypick merges ** nd Do not highlight the focus check-in ** nsm Omit the submenu ** nc Omit all graph colors other than highlights ** v Show details of files changed |
| ︙ | ︙ | |||
1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 |
}
if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum)
|| (bisectLocal && !g.perm.Setup)
){
login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
return;
}
if( !bisectLocal ){
etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA|ETAG_CONFIG, 0);
}
cookie_read_parameter("y","y");
zType = P("y");
if( zType==0 ){
zType = g.perm.Read ? "ci" : "all";
| > > > | 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 |
}
if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum)
|| (bisectLocal && !g.perm.Setup)
){
login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
return;
}
if( zBefore || zCirca ){
if( robot_restrict("timelineX") ) return;
}
if( !bisectLocal ){
etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA|ETAG_CONFIG, 0);
}
cookie_read_parameter("y","y");
zType = P("y");
if( zType==0 ){
zType = g.perm.Read ? "ci" : "all";
|
| ︙ | ︙ | |||
1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 |
tmFlags &= ~(TIMELINE_DELTA|TIMELINE_BRCOLOR|TIMELINE_UCOLOR);
tmFlags |= TIMELINE_NOCOLOR;
}
if( showSql ) db_append_dml_to_blob(&allSql);
if( zUses!=0 ){
int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses);
if( ufid ){
zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid);
db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)");
compute_uses_file("usesfile", ufid, 0);
zType = "ci";
disableY = 1;
if( !haveParameterN ) nEntry = 0;
}else{
| > | 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 |
tmFlags &= ~(TIMELINE_DELTA|TIMELINE_BRCOLOR|TIMELINE_UCOLOR);
tmFlags |= TIMELINE_NOCOLOR;
}
if( showSql ) db_append_dml_to_blob(&allSql);
if( zUses!=0 ){
int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses);
if( ufid ){
if( robot_restrict("timelineX") ) return;
zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid);
db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)");
compute_uses_file("usesfile", ufid, 0);
zType = "ci";
disableY = 1;
if( !haveParameterN ) nEntry = 0;
}else{
|
| ︙ | ︙ | |||
2040 2041 2042 2043 2044 2045 2046 |
disableY = 1;
}else{
zBisect = 0;
}
style_header("Timeline");
if( advancedMenu ){
| | | 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 |
disableY = 1;
}else{
zBisect = 0;
}
style_header("Timeline");
if( advancedMenu ){
style_submenu_element("Help", "%R/help/www/timeline");
}
login_anonymous_available();
timeline_temp_table();
blob_zero(&sql);
blob_zero(&desc);
blob_append(&sql, "INSERT OR IGNORE INTO timeline ", -1);
blob_append(&sql, timeline_query_for_www(), -1);
|
| ︙ | ︙ | |||
2281 2282 2283 2284 2285 2286 2287 |
zFwdTo = P("ft");
if( zFwdTo && bSeparateDandP ){
if( zError==0 ){
zError = "Cannot use the ft= query parameter when both p= and d= "
"are used and have distinct values.";
}
zFwdTo = 0;
| | | 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 |
zFwdTo = P("ft");
if( zFwdTo && bSeparateDandP ){
if( zError==0 ){
zError = "Cannot use the ft= query parameter when both p= and d= "
"are used and have distinct values.";
}
zFwdTo = 0;
}
if( zFwdTo ){
double rStartDate = mtime_of_rid(d_rid, 0.0);
ridFwdTo = first_checkin_with_tag_after_date(zFwdTo, rStartDate);
if( ridFwdTo==0 ){
ridFwdTo = name_to_typed_rid(zBackTo,"ci");
}
if( ridFwdTo ){
|
| ︙ | ︙ | |||
2340 2341 2342 2343 2344 2345 2346 |
zBackTo = P("bt");
if( zBackTo && bSeparateDandP ){
if( zError==0 ){
zError = "Cannot use the bt= query parameter when both p= and d= "
"are used and have distinct values.";
}
zBackTo = 0;
| | | 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 |
zBackTo = P("bt");
if( zBackTo && bSeparateDandP ){
if( zError==0 ){
zError = "Cannot use the bt= query parameter when both p= and d= "
"are used and have distinct values.";
}
zBackTo = 0;
}
if( zBackTo ){
double rDateLimit = mtime_of_rid(p_rid, 0.0);
ridBackTo = last_checkin_with_tag_before_date(zBackTo, rDateLimit);
if( ridBackTo==0 ){
ridBackTo = name_to_typed_rid(zBackTo,"ci");
}
if( ridBackTo && !haveParameterN ) nEntry = 0;
|
| ︙ | ︙ | |||
2366 2367 2368 2369 2370 2371 2372 |
removeFileGlobFromOk(zChng);
db_multi_exec("%s", blob_sql_text(&sql));
}else{
removeFileGlobFromOk(zChng);
np = db_int(0, "SELECT count(*)-1 FROM ok");
if( np>0 || nd==0 ){
if( nd>0 ) blob_appendf(&desc, " and ");
| | | 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 |
removeFileGlobFromOk(zChng);
db_multi_exec("%s", blob_sql_text(&sql));
}else{
removeFileGlobFromOk(zChng);
np = db_int(0, "SELECT count(*)-1 FROM ok");
if( np>0 || nd==0 ){
if( nd>0 ) blob_appendf(&desc, " and ");
blob_appendf(&desc, "%d ancestor%s",
np>=0 ? np : 0, (1==np)?"":"s");
db_multi_exec("%s", blob_sql_text(&sql));
}
if( useDividers && !selectedRid ) selectedRid = p_rid;
}
}
|
| ︙ | ︙ | |||
2400 2401 2402 2403 2404 2405 2406 |
blob_appendf(&desc,
"Check-in %z%h</a> only (%z%h</a> does not precede it)",
href("%R/info?name=%h",zBaseName), zBaseName,
href("%R/info?name=%h",zBackTo), zBackTo);
}else{
blob_appendf(&desc, " back to %z%h</a>%s",
href("%R/info?name=%h",zBackTo), zBackTo,
| | | 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 |
blob_appendf(&desc,
"Check-in %z%h</a> only (%z%h</a> does not precede it)",
href("%R/info?name=%h",zBaseName), zBaseName,
href("%R/info?name=%h",zBackTo), zBackTo);
}else{
blob_appendf(&desc, " back to %z%h</a>%s",
href("%R/info?name=%h",zBackTo), zBackTo,
bBackAdded ? " (not a direct ancestor)" : "");
if( ridFwdTo && zFwdTo ){
blob_appendf(&desc, " and up to %z%h</a>%s",
href("%R/info?name=%h",zFwdTo), zFwdTo,
bFwdAdded ? " (not a direct descendant)" : "");
}
}
}else if( ridFwdTo ){
|
| ︙ | ︙ | |||
2649 2650 2651 2652 2653 2654 2655 |
fossil_free(zNext);
}
blob_append_sql(&cond,
" AND event.mtime>=julianday(%Q,%Q)"
" AND event.mtime<julianday(%Q,%Q,'+1 day')\n",
zStart, zTZMod, zEnd, zTZMod);
nEntry = -1;
| | | 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 |
fossil_free(zNext);
}
blob_append_sql(&cond,
" AND event.mtime>=julianday(%Q,%Q)"
" AND event.mtime<julianday(%Q,%Q,'+1 day')\n",
zStart, zTZMod, zEnd, zTZMod);
nEntry = -1;
if( fossil_ui_localtime() && bZulu ){
zDay = mprintf("%d days between %zZ and %zZ", nDay, zStart, zEnd);
}else{
zDay = mprintf("%d days between %z and %z", nDay, zStart, zEnd);
}
}
else if( zDay ){
|
| ︙ | ︙ | |||
3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 |
if( useDividers && zMark && zMark[0] ){
double r = symbolic_name_to_mtime(zMark, 0, 0);
if( r>0.0 && !selectedRid ) selectedRid = timeline_add_divider(r);
}
blob_zero(&sql);
if( PB("oldestfirst") ){
db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby ASC /*scan*/");
}else{
db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/");
}
if( fossil_islower(desc.aData[0]) ){
desc.aData[0] = fossil_toupper(desc.aData[0]);
}
if( zBrName ){
| > | 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 |
if( useDividers && zMark && zMark[0] ){
double r = symbolic_name_to_mtime(zMark, 0, 0);
if( r>0.0 && !selectedRid ) selectedRid = timeline_add_divider(r);
}
blob_zero(&sql);
if( PB("oldestfirst") ){
db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby ASC /*scan*/");
tmFlags &= ~(TIMELINE_GRAPH|TIMELINE_CHPICK);
}else{
db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/");
}
if( fossil_islower(desc.aData[0]) ){
desc.aData[0] = fossil_toupper(desc.aData[0]);
}
if( zBrName ){
|
| ︙ | ︙ | |||
3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 |
}
blob_reset(&desc);
/* Report any errors. */
if( zError ){
@ <p class="generalError">%h(zError)</p>
}
if( zNewerButton ){
@ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\
@ ↑</a>
}
cgi_check_for_malice();
{
| > > > > > > > > > > | 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 |
}
blob_reset(&desc);
/* Report any errors. */
if( zError ){
@ <p class="generalError">%h(zError)</p>
}
/* Swap zNewer and zOlder buttons if we display oldestfirst */
if( PB("oldestfirst") ){
char *zSwap = zNewerButton;
char *zSwapLabel = zNewerButtonLabel;
zNewerButton = zOlderButton;
zNewerButtonLabel = zOlderButtonLabel;
zOlderButton = zSwap;
zOlderButtonLabel = zSwapLabel;
}
if( zNewerButton ){
@ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\
@ ↑</a>
}
cgi_check_for_malice();
{
|
| ︙ | ︙ | |||
3618 3619 3620 3621 3622 3623 3624 3625 3626 3627 3628 3629 3630 3631 3632 3633 3634 3635 3636 3637 3638 3639 3640 3641 3642 3643 3644 3645 3646 3647 3648 3649 3650 3651 3652 3653 3654 |
** --full Extra verbose entry formatting
** -n|--limit N If N is positive, output the first N entries. If
** N is negative, output the first -N lines. If N is
** zero, no limit. Default is -20 meaning 20 lines.
** --offset P Skip P changes
** -p|--path PATH Output items affecting PATH only.
** PATH can be a file or a sub directory.
** -R REPO_FILE Specifies the repository db to use. Default is
** the current check-out's repository.
** --sql Show the SQL used to generate the timeline
** -t|--type TYPE Output items from the given types only, such as:
** ci = file commits only
** e = technical notes only
** f = forum posts only
** t = tickets only
** w = wiki commits only
** -v|--verbose Output the list of files changed by each commit
** and the type of each change (edited, deleted,
** etc.) after the check-in comment.
** -W|--width N Width of lines (default is to auto-detect). N must be
** either greater than 20 or it must be zero 0 to
** indicate no limit, resulting in a single line per
** entry.
*/
void timeline_cmd(void){
Stmt q;
int n, k, width;
const char *zLimit;
const char *zWidth;
const char *zOffset;
const char *zType;
char *zOrigin;
char *zDate;
Blob sql;
int objid = 0;
Blob uuid;
int mode = TIMELINE_MODE_NONE;
| > > > | > > | 3727 3728 3729 3730 3731 3732 3733 3734 3735 3736 3737 3738 3739 3740 3741 3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 3779 3780 3781 3782 3783 3784 3785 3786 3787 3788 3789 3790 3791 |
** --full Extra verbose entry formatting
** -n|--limit N If N is positive, output the first N entries. If
** N is negative, output the first -N lines. If N is
** zero, no limit. Default is -20 meaning 20 lines.
** --offset P Skip P changes
** -p|--path PATH Output items affecting PATH only.
** PATH can be a file or a sub directory.
** -r|--reverse Show items in chronological order.
** -R REPO_FILE Specifies the repository db to use. Default is
** the current check-out's repository.
** --sql Show the SQL used to generate the timeline
** -t|--type TYPE Output items from the given types only, such as:
** ci = file commits only
** e = technical notes only
** f = forum posts only
** t = tickets only
** w = wiki commits only
** -u|--for-user USER Only show items associated with USER
** -v|--verbose Output the list of files changed by each commit
** and the type of each change (edited, deleted,
** etc.) after the check-in comment.
** -W|--width N Width of lines (default is to auto-detect). N must be
** either greater than 20 or it must be zero 0 to
** indicate no limit, resulting in a single line per
** entry.
*/
void timeline_cmd(void){
Stmt q;
int n, k, width;
const char *zLimit;
const char *zWidth;
const char *zOffset;
const char *zType;
const char *zUser;
char *zOrigin;
char *zDate;
Blob sql;
int objid = 0;
Blob uuid;
int mode = TIMELINE_MODE_NONE;
int verboseFlag = 0;
int reverseFlag = 0;
int iOffset;
const char *zFilePattern = 0;
const char *zFormat = 0;
const char *zBr = 0;
Blob treeName;
int showSql = 0;
verboseFlag = find_option("verbose","v", 0)!=0;
if( !verboseFlag){
verboseFlag = find_option("showfiles","f", 0)!=0; /* deprecated */
}
db_find_and_open_repository(0, 0);
zLimit = find_option("limit","n",1);
zWidth = find_option("width","W",1);
zType = find_option("type","t",1);
zUser = find_option("for-user","u",1);
zFilePattern = find_option("path","p",1);
zFormat = find_option("format","F",1);
zBr = find_option("branch","b",1);
if( find_option("current-branch","c",0)!=0 ){
if( !g.localOpen ){
fossil_fatal("not within an open check-out");
}else{
|
| ︙ | ︙ | |||
3705 3706 3707 3708 3709 3710 3711 3712 3713 3714 3715 3716 3717 3718 3719 3720 3721 3722 3723 3724 3725 3726 3727 3728 3729 3730 3731 |
fossil_fatal("-W|--width value must be >20 or 0");
}
}else{
width = -1;
}
zOffset = find_option("offset",0,1);
iOffset = zOffset ? atoi(zOffset) : 0;
/* We should be done with options.. */
verify_all_options();
if( g.argc>=4 ){
k = strlen(g.argv[2]);
if( strncmp(g.argv[2],"before",k)==0 ){
mode = TIMELINE_MODE_BEFORE;
}else if( strncmp(g.argv[2],"after",k)==0 && k>1 ){
mode = TIMELINE_MODE_AFTER;
}else if( strncmp(g.argv[2],"descendants",k)==0 ){
mode = TIMELINE_MODE_CHILDREN;
}else if( strncmp(g.argv[2],"children",k)==0 ){
mode = TIMELINE_MODE_CHILDREN;
}else if( strncmp(g.argv[2],"ancestors",k)==0 && k>1 ){
mode = TIMELINE_MODE_PARENTS;
}else if( strncmp(g.argv[2],"parents",k)==0 ){
mode = TIMELINE_MODE_PARENTS;
}else if(!zType && !zLimit){
usage("?WHEN? ?CHECKIN|DATETIME? ?-n|--limit #? ?-t|--type TYPE? "
| > | | 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 |
fossil_fatal("-W|--width value must be >20 or 0");
}
}else{
width = -1;
}
zOffset = find_option("offset",0,1);
iOffset = zOffset ? atoi(zOffset) : 0;
reverseFlag = find_option("reverse","r",0)!=0;
/* We should be done with options.. */
verify_all_options();
if( g.argc>=4 ){
k = strlen(g.argv[2]);
if( strncmp(g.argv[2],"before",k)==0 ){
mode = TIMELINE_MODE_BEFORE;
}else if( strncmp(g.argv[2],"after",k)==0 && k>1 ){
mode = TIMELINE_MODE_AFTER;
}else if( strncmp(g.argv[2],"descendants",k)==0 ){
mode = TIMELINE_MODE_CHILDREN;
}else if( strncmp(g.argv[2],"children",k)==0 ){
mode = TIMELINE_MODE_CHILDREN;
}else if( strncmp(g.argv[2],"ancestors",k)==0 && k>1 ){
mode = TIMELINE_MODE_PARENTS;
}else if( strncmp(g.argv[2],"parents",k)==0 ){
mode = TIMELINE_MODE_PARENTS;
}else if(!zType && !zLimit){
usage("?WHEN? ?CHECKIN|DATETIME? ?-n|--limit #? ?-t|--type TYPE? "
"?-W|--width WIDTH? ?-p|--path PATH? ?-r|--reverse?");
}
if( '-' != *g.argv[3] ){
zOrigin = g.argv[3];
}else{
zOrigin = "now";
}
}else if( g.argc==3 ){
|
| ︙ | ︙ | |||
3795 3796 3797 3798 3799 3800 3801 3802 3803 3804 3805 3806 3807 3808 |
blob_append_sql(&sql, "\n AND event.mtime %s %s",
( mode==TIMELINE_MODE_BEFORE ||
mode==TIMELINE_MODE_PARENTS ) ? "<=" : ">=", zDate /*safe-for-%s*/
);
if( zType && (zType[0]!='a') ){
blob_append_sql(&sql, "\n AND event.type=%Q ", zType);
}
/* When zFilePattern is specified, compute complete ancestry;
* limit later at print_timeline() */
if( mode==TIMELINE_MODE_CHILDREN || mode==TIMELINE_MODE_PARENTS ){
db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
if( mode==TIMELINE_MODE_CHILDREN ){
compute_descendants(objid, (zFilePattern ? 0 : n));
| > > > | 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 |
blob_append_sql(&sql, "\n AND event.mtime %s %s",
( mode==TIMELINE_MODE_BEFORE ||
mode==TIMELINE_MODE_PARENTS ) ? "<=" : ">=", zDate /*safe-for-%s*/
);
if( zType && (zType[0]!='a') ){
blob_append_sql(&sql, "\n AND event.type=%Q ", zType);
}
if( zUser && (zUser[0]!='\0') ){
blob_append_sql(&sql, "\n AND user0=%Q ", zUser);
}
/* When zFilePattern is specified, compute complete ancestry;
* limit later at print_timeline() */
if( mode==TIMELINE_MODE_CHILDREN || mode==TIMELINE_MODE_PARENTS ){
db_multi_exec("CREATE TEMP TABLE ok(rid INTEGER PRIMARY KEY)");
if( mode==TIMELINE_MODE_CHILDREN ){
compute_descendants(objid, (zFilePattern ? 0 : n));
|
| ︙ | ︙ | |||
3848 3849 3850 3851 3852 3853 3854 |
" AND e.comment LIKE '_checkin/%%'\n"
" LEFT JOIN tagxref tx ON tx.rid=b.rid AND tx.tagid=%d\n"
" WHERE tx.value='%q'\n"
")\n" /* No merge closures */
" AND (tagxref.value IS NULL OR tagxref.value='%q')",
zBr, zBr, zBr, TAG_BRANCH, zBr, zBr);
}
| | | | > | > | 3966 3967 3968 3969 3970 3971 3972 3973 3974 3975 3976 3977 3978 3979 3980 3981 3982 3983 3984 3985 3986 3987 3988 3989 3990 3991 3992 3993 3994 |
" AND e.comment LIKE '_checkin/%%'\n"
" LEFT JOIN tagxref tx ON tx.rid=b.rid AND tx.tagid=%d\n"
" WHERE tx.value='%q'\n"
")\n" /* No merge closures */
" AND (tagxref.value IS NULL OR tagxref.value='%q')",
zBr, zBr, zBr, TAG_BRANCH, zBr, zBr);
}
if( mode==TIMELINE_MODE_AFTER ){
int lim = n;
if( n == 0 ){
lim = -1; /* 0 means no limit */
}else if( n < 0 ){
lim = -n;
}
/* Complete the above outer select. */
blob_append_sql(&sql,
"\nORDER BY event.mtime LIMIT %d) t ORDER BY t.mDateTime %s",
lim, reverseFlag ? "" : "DESC");
}else{
blob_append_sql(&sql,
"\nORDER BY event.mtime %s", reverseFlag ? "" : "DESC");
}
if( iOffset>0 ){
/* Don't handle LIMIT here, otherwise print_timeline()
* will not determine the end-marker correctly! */
blob_append_sql(&sql, "\n LIMIT -1 OFFSET %d", iOffset);
}
if( showSql ){
|
| ︙ | ︙ |
Changes to src/tkt.c.
| ︙ | ︙ | |||
789 790 791 792 793 794 795 796 797 798 799 800 801 802 |
safe_html_context(DOCSRC_TICKET);
Th_Render(zScript);
if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1);
if( zFullName ){
attachment_list(zFullName, "<h2>Attachments:</h2>", 1);
}
style_finish_page();
}
/*
** TH1 command: append_field FIELD STRING
**
| > > > > | 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 |
safe_html_context(DOCSRC_TICKET);
Th_Render(zScript);
if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1);
if( zFullName ){
attachment_list(zFullName, "<h2>Attachments:</h2>", 1);
}
builtin_fossil_js_bundle_or("dom", "storage", NULL);
builtin_request_js("fossil.page.ticket.js");
builtin_fulfill_js_requests();
style_finish_page();
}
/*
** TH1 command: append_field FIELD STRING
**
|
| ︙ | ︙ |
Changes to src/tktsetup.c.
| ︙ | ︙ | |||
601 602 603 604 605 606 607 |
@ } else {
@ wiki $comment
@ }
@ }
@ }
@ set seenRow 0
@ set alwaysPlaintext [info exists plaintext]
| | | > | 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 |
@ } else {
@ wiki $comment
@ }
@ }
@ }
@ set seenRow 0
@ set alwaysPlaintext [info exists plaintext]
@ query {SELECT datetime(tkt_mtime,toLocal()) AS xdate, login AS xlogin,
@ mimetype as xmimetype, icomment AS xcomment,
@ username AS xusername
@ FROM ticketchng
@ WHERE tkt_id=$tkt_id AND length(icomment)>0} {
@ if {$seenRow} {
@ html "<hr>\n"
@ } else {
@ html "<tr><td class='tktDspLabel' style='text-align:left'>\n"
@ html "User Comments:</td></tr>\n"
@ html "<tr><td colspan='5' class='tktDspValue'><div class='tktCommentArea'>\n"
@ set seenRow 1
@ }
@ html "<div class='tktCommentEntry'>"
@ html "<span class='tktDspCommenter'>"
@ puts $xlogin
@ if {$xlogin ne $xusername && [string length $xusername]>0} {
@ puts " (claiming to be $xusername)"
@ }
@ puts " added on $xdate:"
@ html "</span>\n"
|
| ︙ | ︙ | |||
635 636 637 638 639 640 641 642 |
@ html [lindex [markdown $xcomment] 1]
@ } elseif {$xmimetype eq "text/html"} {
@ wiki "<p><nowiki>\n[string trimright $xcomment]\n</nowiki>\n"
@ } else {
@ set r [randhex]
@ wiki "<verbatim-$r links>[string trimright $xcomment]</verbatim-$r>\n"
@ }
@ }
| > | | 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 |
@ html [lindex [markdown $xcomment] 1]
@ } elseif {$xmimetype eq "text/html"} {
@ wiki "<p><nowiki>\n[string trimright $xcomment]\n</nowiki>\n"
@ } else {
@ set r [randhex]
@ wiki "<verbatim-$r links>[string trimright $xcomment]</verbatim-$r>\n"
@ }
@ html "</div>"; # .tktCommentEntry
@ }
@ if {$seenRow} {html "</div></td></tr>\n"}
@ </th1>
@ </table>
;
/*
** Return the code used to generate the view ticket page
|
| ︙ | ︙ | |||
787 788 789 790 791 792 793 | @ </td> @ <td>Abandon this edit</td> @ </tr> @ @ <th1> @ set seenRow 0 @ set alwaysPlaintext [info exists plaintext] | | | > | 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 |
@ </td>
@ <td>Abandon this edit</td>
@ </tr>
@
@ <th1>
@ set seenRow 0
@ set alwaysPlaintext [info exists plaintext]
@ query {SELECT datetime(tkt_mtime,toLocal()) AS xdate, login AS xlogin,
@ mimetype as xmimetype, icomment AS xcomment,
@ username AS xusername
@ FROM ticketchng
@ WHERE tkt_id=$tkt_id AND length(icomment)>0} {
@ if {$seenRow} {
@ html "<hr>\n"
@ } else {
@ html "<tr><td colspan='2'><hr></td></tr>\n"
@ html "<tr><td colspan='2' class='tktDspLabel' style='text-align:left'>\n"
@ html "Previous User Comments:</td></tr>\n"
@ html "<tr><td colspan='2' class='tktDspValue'><div class='tktCommentArea'>\n"
@ set seenRow 1
@ }
@ html "<div class='tktCommentEntry'>"
@ html "<span class='tktDspCommenter'>"
@ puts $xlogin
@ if {$xlogin ne $xusername && [string length $xusername]>0} {
@ puts " (claiming to be $xusername)"
@ }
@ puts " added on $xdate:"
@ html "</span>\n"
|
| ︙ | ︙ | |||
822 823 824 825 826 827 828 829 |
@ html [lindex [markdown $xcomment] 1]
@ } elseif {$xmimetype eq "text/html"} {
@ wiki "<p><nowiki>\n[string trimright $xcomment]\n</nowiki>\n"
@ } else {
@ set r [randhex]
@ wiki "<verbatim-$r links>[string trimright $xcomment]</verbatim-$r>\n"
@ }
@ }
| > | | 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 |
@ html [lindex [markdown $xcomment] 1]
@ } elseif {$xmimetype eq "text/html"} {
@ wiki "<p><nowiki>\n[string trimright $xcomment]\n</nowiki>\n"
@ } else {
@ set r [randhex]
@ wiki "<verbatim-$r links>[string trimright $xcomment]</verbatim-$r>\n"
@ }
@ html "</div>"; # .tktCommentEntry
@ }
@ if {$seenRow} {html "</div></td></tr>\n"}
@ </th1>
@
@ </table>
;
/*
** Return the code used to generate the edit ticket page
|
| ︙ | ︙ | |||
924 925 926 927 928 929 930 |
@ CASE WHEN status IN ('Open','Verified') THEN '#f2dcdc'
@ WHEN status='Review' THEN '#e8e8e8'
@ WHEN status='Fixed' THEN '#cfe8bd'
@ WHEN status='Tested' THEN '#bde5d6'
@ WHEN status='Deferred' THEN '#cacae5'
@ ELSE '#c8c8c8' END AS 'bgcolor',
@ substr(tkt_uuid,1,10) AS '#',
| | | | 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 |
@ CASE WHEN status IN ('Open','Verified') THEN '#f2dcdc'
@ WHEN status='Review' THEN '#e8e8e8'
@ WHEN status='Fixed' THEN '#cfe8bd'
@ WHEN status='Tested' THEN '#bde5d6'
@ WHEN status='Deferred' THEN '#cacae5'
@ ELSE '#c8c8c8' END AS 'bgcolor',
@ substr(tkt_uuid,1,10) AS '#',
@ datetime(tkt_ctime,toLocal()) AS 'created',
@ datetime(tkt_mtime,toLocal()) AS 'modified',
@ type,
@ status,
@ subsystem,
@ title,
@ comment AS '_comments'
@ FROM ticket
;
|
| ︙ | ︙ |
Changes to src/unversioned.c.
| ︙ | ︙ | |||
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 |
}
if( strncmp(zCmd, "add", nCmd)==0 ){
const char *zError = 0;
const char *zIn;
const char *zAs;
Blob file;
int i;
zAs = find_option("as",0,1);
verify_all_options();
if( zAs && g.argc!=4 ) usage("add DISKFILE --as UVFILE");
db_begin_transaction();
content_rcvid_init("#!fossil unversioned add");
for(i=3; i<g.argc; i++){
zIn = zAs ? zAs : g.argv[i];
if( zIn[0]==0 ){
zError = "be empty string";
}else if( zIn[0]=='/' ){
zError = "be absolute";
}else if ( !file_is_simple_pathname(zIn,1) ){
zError = "contain complex paths";
}else if( contains_whitespace(zIn) ){
zError = "contain whitespace";
}
if( zError ){
fossil_fatal("unversioned filenames may not %s: %Q", zError, zIn);
}
blob_init(&file,0,0);
blob_read_from_file(&file, g.argv[i], ExtFILE);
unversioned_write(zIn, &file, mtime);
blob_reset(&file);
}
db_end_transaction(0);
| > > > > > > > > > | 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 |
}
if( strncmp(zCmd, "add", nCmd)==0 ){
const char *zError = 0;
const char *zIn;
const char *zAs;
Blob file;
int i;
i64 mxSize = sqlite3_limit(g.db,SQLITE_LIMIT_LENGTH,-1) - 850;
/* Extra space for other fields ------^^^ */
/* of the UNVESIONED table row. */
zAs = find_option("as",0,1);
verify_all_options();
if( zAs && g.argc!=4 ) usage("add DISKFILE --as UVFILE");
db_begin_transaction();
content_rcvid_init("#!fossil unversioned add");
for(i=3; i<g.argc; i++){
zIn = zAs ? zAs : g.argv[i];
if( zIn[0]==0 ){
zError = "be empty string";
}else if( zIn[0]=='/' ){
zError = "be absolute";
}else if ( !file_is_simple_pathname(zIn,1) ){
zError = "contain complex paths";
}else if( contains_whitespace(zIn) ){
zError = "contain whitespace";
}else if( strlen(zIn)>500 ){
zError = "be more than 500 bytes long";
}
if( zError ){
fossil_fatal("unversioned filenames may not %s: %Q", zError, zIn);
}
if( file_size(g.argv[i], ExtFILE)>mxSize ){
fossil_fatal("file \"%s\" is too big; max size: %,lld bytes",
g.argv[i], mxSize);
}
blob_init(&file,0,0);
blob_read_from_file(&file, g.argv[i], ExtFILE);
unversioned_write(zIn, &file, mtime);
blob_reset(&file);
}
db_end_transaction(0);
|
| ︙ | ︙ |
Changes to src/url.c.
| ︙ | ︙ | |||
56 57 58 59 60 61 62 63 64 65 66 67 68 69 | int dfltPort; /* The default port for the given protocol */ char *path; /* Pathname for http: */ char *user; /* User id for http: */ char *passwd; /* Password for http: */ char *canonical; /* Canonical representation of the URL */ char *proxyAuth; /* Proxy-Authorizer: string */ char *fossil; /* The fossil query parameter on ssh: */ char *pwConfig; /* CONFIG table entry that gave us the password */ unsigned flags; /* Boolean flags controlling URL processing */ int useProxy; /* Used to remember that a proxy is in use */ int proxyOrigPort; /* Tunneled port number for https through proxy */ char *proxyUrlPath; /* Remember path when proxy is use */ char *proxyUrlCanonical; /* Remember canonical path when proxy is use */ }; | > | 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 | int dfltPort; /* The default port for the given protocol */ char *path; /* Pathname for http: */ char *user; /* User id for http: */ char *passwd; /* Password for http: */ char *canonical; /* Canonical representation of the URL */ char *proxyAuth; /* Proxy-Authorizer: string */ char *fossil; /* The fossil query parameter on ssh: */ char *subpath; /* Secondary HTTP request path for ssh: and file: */ char *pwConfig; /* CONFIG table entry that gave us the password */ unsigned flags; /* Boolean flags controlling URL processing */ int useProxy; /* Used to remember that a proxy is in use */ int proxyOrigPort; /* Tunneled port number for https through proxy */ char *proxyUrlPath; /* Remember path when proxy is use */ char *proxyUrlCanonical; /* Remember canonical path when proxy is use */ }; |
| ︙ | ︙ | |||
404 405 406 407 408 409 410 411 412 413 414 415 416 417 | } fossil_free(p->canonical); fossil_free(p->name); fossil_free(p->path); fossil_free(p->user); fossil_free(p->passwd); fossil_free(p->fossil); fossil_free(p->pwConfig); memset(p, 0, sizeof(*p)); } /* ** Move a URL parse from one UrlData object to another. */ | > | 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 | } fossil_free(p->canonical); fossil_free(p->name); fossil_free(p->path); fossil_free(p->user); fossil_free(p->passwd); fossil_free(p->fossil); fossil_free(p->subpath); fossil_free(p->pwConfig); memset(p, 0, sizeof(*p)); } /* ** Move a URL parse from one UrlData object to another. */ |
| ︙ | ︙ | |||
481 482 483 484 485 486 487 488 489 490 491 492 493 494 |
fossil_print("g.url.passwd = %s\n", g.url.passwd);
}else{
fossil_print("g.url.passwd = ************\n");
}
fossil_print("g.url.pwConfig = %s\n", g.url.pwConfig);
fossil_print("g.url.canonical = %s\n", g.url.canonical);
fossil_print("g.url.fossil = %s\n", g.url.fossil);
fossil_print("g.url.flags = 0x%04x\n", g.url.flags);
fossil_print("url_full(g.url) = %z\n", url_full(&g.url));
}
/*
** COMMAND: test-urlparser
**
| > | 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 |
fossil_print("g.url.passwd = %s\n", g.url.passwd);
}else{
fossil_print("g.url.passwd = ************\n");
}
fossil_print("g.url.pwConfig = %s\n", g.url.pwConfig);
fossil_print("g.url.canonical = %s\n", g.url.canonical);
fossil_print("g.url.fossil = %s\n", g.url.fossil);
fossil_print("g.url.subpath = %s\n", g.url.subpath);
fossil_print("g.url.flags = 0x%04x\n", g.url.flags);
fossil_print("url_full(g.url) = %z\n", url_full(&g.url));
}
/*
** COMMAND: test-urlparser
**
|
| ︙ | ︙ |
Changes to src/user.c.
| ︙ | ︙ | |||
107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
assert( zPwd==zPwdBuffer );
return zPwd;
}
void freepass(){
if( !zPwdBuffer ) return;
assert( nPwdBuffer>0 );
fossil_secure_free_page(zPwdBuffer, nPwdBuffer);
}
#endif
/*
** Scramble substitution matrix:
*/
static char aSubst[256];
| > > | 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
assert( zPwd==zPwdBuffer );
return zPwd;
}
void freepass(){
if( !zPwdBuffer ) return;
assert( nPwdBuffer>0 );
fossil_secure_free_page(zPwdBuffer, nPwdBuffer);
zPwdBuffer = 0;
nPwdBuffer = 0;
}
#endif
/*
** Scramble substitution matrix:
*/
static char aSubst[256];
|
| ︙ | ︙ | |||
284 285 286 287 288 289 290 |
*/
char *prompt_for_user_password(const char *zUser){
char *zPrompt = mprintf("\rpassword for %s: ", zUser);
char *zPw;
Blob x;
fossil_force_newline();
prompt_for_password(zPrompt, &x, 0);
| | | < | 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 |
*/
char *prompt_for_user_password(const char *zUser){
char *zPrompt = mprintf("\rpassword for %s: ", zUser);
char *zPw;
Blob x;
fossil_force_newline();
prompt_for_password(zPrompt, &x, 0);
fossil_free(zPrompt);
zPw = blob_str(&x)/*transfer ownership*/;
return zPw;
}
/*
** Prompt the user to enter a single line of text.
*/
void prompt_user(const char *zPrompt, Blob *pIn){
|
| ︙ | ︙ |
Changes to src/util.c.
| ︙ | ︙ | |||
897 898 899 900 901 902 903 904 905 906 907 908 909 910 |
fossil_print("%s (%d bits of entropy)\n", zPassword,
(int)(log(et)/log(2.0)));
}else{
fossil_print("%s\n", zPassword);
}
fossil_free(zPassword);
}
/*
** Return the number of decimal digits in a nonnegative integer. This is useful
** when formatting text.
*/
int fossil_num_digits(int n){
return n< 10 ? 1 : n< 100 ? 2 : n< 1000 ? 3
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 |
fossil_print("%s (%d bits of entropy)\n", zPassword,
(int)(log(et)/log(2.0)));
}else{
fossil_print("%s\n", zPassword);
}
fossil_free(zPassword);
}
/*
** Generate a version 4 ("random"), variant 1 UUID (RFC 9562, Section 5.4).
**
** Format: xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
** where M=4 and N=8, 9, a, or b (this leaves 122 random bits)
*/
char* fossil_generate_uuid() {
static const char zDigits[] = "0123456789abcdef";
unsigned char aBlob[16];
unsigned char zStr[37];
unsigned char *p = zStr;
int i, k;
sqlite3_randomness(16, aBlob);
aBlob[6] = (aBlob[6]&0x0f) + 0x40; /* Version byte: 0100 xxxx */
aBlob[8] = (aBlob[8]&0x3f) + 0x80; /* Variant byte: 1000 xxxx */
for(i=0, k=0x550; i<16; i++, k=k>>1){
if( k&1 ){
*p++ = '-'; /* Add a dash after byte 4, 6, 8, and 12 */
}
*p++ = zDigits[aBlob[i]>>4];
*p++ = zDigits[aBlob[i]&0xf];
}
*p = 0;
return fossil_strdup((char*)zStr);
}
/*
** COMMAND: test-generate-uuid
**
** Usage: %fossil test-generate-uuid
**
** Generate a version 4 ("random"), variant 1 UUID (RFC 9562, Section 5.4):
**
** xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx - where M=4 and N=8, 9, a, or b
*/
void test_generate_uuid(void){
fossil_print("%s\n", fossil_generate_uuid());
}
/*
** Return the number of decimal digits in a nonnegative integer. This is useful
** when formatting text.
*/
int fossil_num_digits(int n){
return n< 10 ? 1 : n< 100 ? 2 : n< 1000 ? 3
|
| ︙ | ︙ |
Changes to src/xfer.c.
| ︙ | ︙ | |||
1331 1332 1333 1334 1335 1336 1337 |
zScript = xfer_push_code();
if( zScript ){ /* NOTE: Are TH1 transfer hooks enabled? */
pzUuidList = &zUuidList;
pnUuidList = &nUuidList;
}
if( g.syncInfo.zLoginCard ){
/* Login card received via HTTP Cookie header */
| < | 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 |
zScript = xfer_push_code();
if( zScript ){ /* NOTE: Are TH1 transfer hooks enabled? */
pzUuidList = &zUuidList;
pnUuidList = &nUuidList;
}
if( g.syncInfo.zLoginCard ){
/* Login card received via HTTP Cookie header */
blob_zero(&xfer.line);
blob_append(&xfer.line, g.syncInfo.zLoginCard, -1);
xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken,
count(xfer.aToken));
fossil_free( g.syncInfo.zLoginCard );
g.syncInfo.zLoginCard = 0;
if( xfer.nToken==4
|
| ︙ | ︙ |
Changes to src/zip.c.
| ︙ | ︙ | |||
404 405 406 407 408 409 410 |
assert( p->db );
blob_zero(&p->tmp);
sqlite3_exec(p->db,
"PRAGMA page_size=512;"
"PRAGMA journal_mode = off;"
"PRAGMA cache_spill = off;"
"BEGIN;"
| | | | | | | | 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 |
assert( p->db );
blob_zero(&p->tmp);
sqlite3_exec(p->db,
"PRAGMA page_size=512;"
"PRAGMA journal_mode = off;"
"PRAGMA cache_spill = off;"
"BEGIN;"
"CREATE TABLE sqlar(\n"
" name TEXT PRIMARY KEY, -- name of the file\n"
" mode INT, -- access permissions\n"
" mtime INT, -- last modification time\n"
" sz INT, -- original file size\n"
" data BLOB -- compressed content\n"
");", 0, 0, 0
);
sqlite3_prepare(p->db,
"INSERT INTO sqlar VALUES(?, ?, ?, ?, ?)", -1,
&p->pInsert, 0
);
assert( p->pInsert );
|
| ︙ | ︙ | |||
862 863 864 865 866 867 868 |
}
zOut = g.argv[3];
if( fossil_strcmp(zOut,"")==0 || fossil_strcmp(zOut,"/dev/null")==0 ){
zOut = 0;
}
if( zName==0 ){
| | < < < < < < < < | 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 |
}
zOut = g.argv[3];
if( fossil_strcmp(zOut,"")==0 || fossil_strcmp(zOut,"/dev/null")==0 ){
zOut = 0;
}
if( zName==0 ){
zName = archive_base_name(rid);
}
zip_of_checkin(eType, rid, zOut ? &zip : 0,
zName, pInclude, pExclude, listFlag);
glob_free(pInclude);
glob_free(pExclude);
if( zOut ){
blob_write_to_file(&zip, zOut);
|
| ︙ | ︙ | |||
993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 |
** in=PATTERN Only include files that match the comma-separate
** list of GLOB patterns in PATTERN, as with ex=
**
** ex=PATTERN Omit any file that match PATTERN. PATTERN is a
** comma-separated list of GLOB patterns, where each
** pattern can optionally be quoted using ".." or '..'.
** Any file matching both ex= and in= is excluded.
*/
void baseline_zip_page(void){
int rid;
const char *z;
char *zName, *zRid, *zKey;
int nName, nRid;
const char *zInclude; /* The in= query parameter */
const char *zExclude; /* The ex= query parameter */
Blob cacheKey; /* The key to cache */
Glob *pInclude = 0; /* The compiled in= glob pattern */
Glob *pExclude = 0; /* The compiled ex= glob pattern */
Blob zip; /* ZIP archive accumulated here */
int eType = ARCHIVE_ZIP; /* Type of archive to generate */
char *zType; /* Human-readable archive type */
login_check_credentials();
if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
if( fossil_strcmp(g.zPath, "sqlar")==0 ){
eType = ARCHIVE_SQLAR;
zType = "SQL";
| > > > > > > > > > > > > > > > > < < < | 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 |
** in=PATTERN Only include files that match the comma-separate
** list of GLOB patterns in PATTERN, as with ex=
**
** ex=PATTERN Omit any file that match PATTERN. PATTERN is a
** comma-separated list of GLOB patterns, where each
** pattern can optionally be quoted using ".." or '..'.
** Any file matching both ex= and in= is excluded.
**
** Robot Defenses:
**
** * If "zip" appears in the robot-restrict setting, then robots are
** not allowed to access this page. Suspected robots will be
** presented with a captcha.
**
** * If "zipX" appears in the robot-restrict setting, then robots are
** restricted in the same way as with "zip", but with exceptions.
** If the check-in for which an archive is requested is a leaf check-in
** and if the robot-zip-leaf setting is true, then the request is
** allowed. Or if the check-in has a tag that matches any of the
** GLOB patterns on the list in the robot-zip-tag setting, then the
** request is allowed. Otherwise, the usual robot defenses are
** activated.
*/
void baseline_zip_page(void){
int rid;
const char *z;
char *zName, *zRid, *zKey;
int nName, nRid;
const char *zInclude; /* The in= query parameter */
const char *zExclude; /* The ex= query parameter */
Blob cacheKey; /* The key to cache */
Glob *pInclude = 0; /* The compiled in= glob pattern */
Glob *pExclude = 0; /* The compiled ex= glob pattern */
Blob zip; /* ZIP archive accumulated here */
int eType = ARCHIVE_ZIP; /* Type of archive to generate */
char *zType; /* Human-readable archive type */
login_check_credentials();
if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
if( robot_restrict("zip") ) return;
if( fossil_strcmp(g.zPath, "sqlar")==0 ){
eType = ARCHIVE_SQLAR;
zType = "SQL";
}else{
eType = ARCHIVE_ZIP;
zType = "ZIP";
}
fossil_nice_default();
zName = fossil_strdup(PD("name",""));
z = P("r");
|
| ︙ | ︙ | |||
1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 |
}
rid = symbolic_name_to_rid(nRid?zRid:zName, "ci");
if( rid<=0 ){
cgi_set_status(404, "Not Found");
@ Not found
return;
}
if( nRid==0 && nName>10 ) zName[10] = 0;
/* Compute a unique key for the cache entry based on query parameters */
blob_init(&cacheKey, 0, 0);
blob_appendf(&cacheKey, "/%s/%z", g.zPath, rid_to_uuid(rid));
blob_appendf(&cacheKey, "/%q", zName);
if( zInclude ) blob_appendf(&cacheKey, ",in=%Q", zInclude);
| > | 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 |
}
rid = symbolic_name_to_rid(nRid?zRid:zName, "ci");
if( rid<=0 ){
cgi_set_status(404, "Not Found");
@ Not found
return;
}
if( robot_restrict_zip(rid) ) return;
if( nRid==0 && nName>10 ) zName[10] = 0;
/* Compute a unique key for the cache entry based on query parameters */
blob_init(&cacheKey, 0, 0);
blob_appendf(&cacheKey, "/%s/%z", g.zPath, rid_to_uuid(rid));
blob_appendf(&cacheKey, "/%q", zName);
if( zInclude ) blob_appendf(&cacheKey, ",in=%Q", zInclude);
|
| ︙ | ︙ |
Changes to test/commit-warning.test.
| ︙ | ︙ | |||
158 159 160 161 162 163 164 | 1\tutf-nobom-16be.txt\tbinary data 1\tutf-nobom-16le.txt\tbinary data 1}]]} ############################################################################### | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 158 159 160 161 162 163 164 165 166 | 1\tutf-nobom-16be.txt\tbinary data 1\tutf-nobom-16le.txt\tbinary data 1}]]} ############################################################################### test_cleanup |
Changes to test/json.test.
| ︙ | ︙ | |||
174 175 176 177 178 179 180 |
# specific fields and that it lacks specific fields.
proc test_json_payload {testname okfields badfields} {
test_dict_keys $testname [dict get $::JR payload] $okfields $badfields
}
#### VERSION AKA HAI
| | | 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# specific fields and that it lacks specific fields.
proc test_json_payload {testname okfields badfields} {
test_dict_keys $testname [dict get $::JR payload] $okfields $badfields
}
#### VERSION AKA HAI
# The JSON API generally assumes we have a repository, so let it have one.
# Set FOSSIL_USER to ensure consistent results in "json user list"
set _fossil_user ""
if [info exists env(FOSSIL_USER)] {
set _fossil_user $env(FOSSIL_USER)
}
set ::env(FOSSIL_USER) "JSON-TEST-USER"
|
| ︙ | ︙ |
Changes to test/set-manifest.test.
| ︙ | ︙ | |||
36 37 38 39 40 41 42 |
# On ActiveTcl, add it with teacup. On other platforms, YMMV.
# teacup install sha1
if {[catch {package require sha1}] != 0} {
puts "The \"sha1\" package is not available."
test_cleanup_then_return
}
| | | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# On ActiveTcl, add it with teacup. On other platforms, YMMV.
# teacup install sha1
if {[catch {package require sha1}] != 0} {
puts "The \"sha1\" package is not available."
test_cleanup_then_return
}
# We need a repository, so let it have one.
test_setup
#### Verify classic behavior of the manifest setting
# Setting is off by default, and there are no extra files.
fossil settings manifest
test "set-manifest-1" {[regexp {^manifest *$} $RESULT]}
|
| ︙ | ︙ |
Name change from test/settings.test to test/settings.test.off.
| ︙ | ︙ |
Changes to tools/makeheaders.c.
| ︙ | ︙ | |||
484 485 486 487 488 489 490 |
#define StringGet(S) ((S)->zText?(S)->zText:"")
/*
** Compute a hash on a string. The number returned is a non-negative
** value between 0 and 2**31 - 1
*/
static int Hash(const char *z, int n){
| | | | 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 |
#define StringGet(S) ((S)->zText?(S)->zText:"")
/*
** Compute a hash on a string. The number returned is a non-negative
** value between 0 and 2**31 - 1
*/
static int Hash(const char *z, int n){
unsigned int h = 0;
if( n<=0 ){
n = strlen(z);
}
while( n-- ){
h = h ^ (h<<5) ^ *z++;
}
return (int)(h & 0x7fffffff);
}
/*
** Given an identifier name, try to find a declaration for that
** identifier in the hash table. If found, return a pointer to
** the Decl structure. If not found, return 0.
*/
|
| ︙ | ︙ |
Changes to tools/makemake.tcl.
| ︙ | ︙ | |||
150 151 152 153 154 155 156 157 158 159 160 161 162 163 | printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 | > | 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | printf publish purge rebuild regexp repolist report robot rss schema search security_audit setup setupuser sha1 |
| ︙ | ︙ |
Changes to win/Makefile.dmc.
| ︙ | ︙ | |||
30 31 32 33 34 35 36 | SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_MATH_FUNCTIONS -DSQLITE_ENABLE_SETLK_TIMEOUT -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -DHAVE_USLEEP SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_MATH_FUNCTIONS -DSQLITE_ENABLE_SETLK_TIMEOUT -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -DHAVE_USLEEP -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen PIKCHR_OPTIONS = -DPIKCHR_TOKEN_LIMIT=10000 | | | | | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_MATH_FUNCTIONS -DSQLITE_ENABLE_SETLK_TIMEOUT -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -DHAVE_USLEEP SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_MATH_FUNCTIONS -DSQLITE_ENABLE_SETLK_TIMEOUT -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -DHAVE_USLEEP -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen PIKCHR_OPTIONS = -DPIKCHR_TOKEN_LIMIT=10000 SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c match_.c md5_.c merge_.c merge3_.c moderate_.c name_.c patch_.c path_.c piechart_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c robot_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\match$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\patch$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchrshow$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\robot$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O RC=$(DMDIR)\bin\rcc RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ APPNAME = $(OBJDIR)\fossil$(E) all: $(APPNAME) $(APPNAME) : translate$E mkindex$E codecheck1$E headers $(OBJ) $(OBJDIR)\link cd $(OBJDIR) codecheck1$E $(SRC) $(DMDIR)\bin\link @link $(OBJDIR)\fossil.res: $B\win\fossil.rc $(RC) $(RCFLAGS) -o$@ $** $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html match md5 merge merge3 moderate name patch path piechart pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report robot rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@ +echo fossil >> $@ +echo fossil >> $@ +echo $(LIBS) >> $@ +echo. >> $@ +echo fossil >> $@ translate$E: $(SRCDIR_tools)\translate.c |
| ︙ | ︙ | |||
753 754 755 756 757 758 759 760 761 762 763 764 765 766 | +translate$E $** > $@ $(OBJDIR)\report$O : report_.c report.h $(TCC) -o$@ -c report_.c report_.c : $(SRCDIR)\report.c +translate$E $** > $@ $(OBJDIR)\rss$O : rss_.c rss.h $(TCC) -o$@ -c rss_.c rss_.c : $(SRCDIR)\rss.c +translate$E $** > $@ | > > > > > > | 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 | +translate$E $** > $@ $(OBJDIR)\report$O : report_.c report.h $(TCC) -o$@ -c report_.c report_.c : $(SRCDIR)\report.c +translate$E $** > $@ $(OBJDIR)\robot$O : robot_.c robot.h $(TCC) -o$@ -c robot_.c robot_.c : $(SRCDIR)\robot.c +translate$E $** > $@ $(OBJDIR)\rss$O : rss_.c rss.h $(TCC) -o$@ -c rss_.c rss_.c : $(SRCDIR)\rss.c +translate$E $** > $@ |
| ︙ | ︙ | |||
1013 1014 1015 1016 1017 1018 1019 | $(OBJDIR)\zip$O : zip_.c zip.h $(TCC) -o$@ -c zip_.c zip_.c : $(SRCDIR)\zip.c +translate$E $** > $@ headers: makeheaders$E page_index.h builtin_data.h VERSION.h | | | 1019 1020 1021 1022 1023 1024 1025 1026 1027 | $(OBJDIR)\zip$O : zip_.c zip.h $(TCC) -o$@ -c zip_.c zip_.c : $(SRCDIR)\zip.c +translate$E $** > $@ headers: makeheaders$E page_index.h builtin_data.h VERSION.h +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h interwiki_.c:interwiki.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h match_.c:match.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h patch_.c:patch.h path_.c:path.h piechart_.c:piechart.h pikchrshow_.c:pikchrshow.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h robot_.c:robot.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR_extsrc)\pikchr.c:pikchr.h $(SRCDIR_extsrc)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR_extsrc)\cson_amalgamation.h @copy /Y nul: headers |
Changes to win/Makefile.mingw.
| ︙ | ︙ | |||
503 504 505 506 507 508 509 510 511 512 513 514 515 516 | $(SRCDIR)/printf.c \ $(SRCDIR)/publish.c \ $(SRCDIR)/purge.c \ $(SRCDIR)/rebuild.c \ $(SRCDIR)/regexp.c \ $(SRCDIR)/repolist.c \ $(SRCDIR)/report.c \ $(SRCDIR)/rss.c \ $(SRCDIR)/schema.c \ $(SRCDIR)/search.c \ $(SRCDIR)/security_audit.c \ $(SRCDIR)/setup.c \ $(SRCDIR)/setupuser.c \ $(SRCDIR)/sha1.c \ | > | 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 | $(SRCDIR)/printf.c \ $(SRCDIR)/publish.c \ $(SRCDIR)/purge.c \ $(SRCDIR)/rebuild.c \ $(SRCDIR)/regexp.c \ $(SRCDIR)/repolist.c \ $(SRCDIR)/report.c \ $(SRCDIR)/robot.c \ $(SRCDIR)/rss.c \ $(SRCDIR)/schema.c \ $(SRCDIR)/search.c \ $(SRCDIR)/security_audit.c \ $(SRCDIR)/setup.c \ $(SRCDIR)/setupuser.c \ $(SRCDIR)/sha1.c \ |
| ︙ | ︙ | |||
620 621 622 623 624 625 626 627 628 629 630 631 632 633 | $(SRCDIR)/fossil.numbered-lines.js \ $(SRCDIR)/fossil.page.brlist.js \ $(SRCDIR)/fossil.page.chat.js \ $(SRCDIR)/fossil.page.fileedit.js \ $(SRCDIR)/fossil.page.forumpost.js \ $(SRCDIR)/fossil.page.pikchrshow.js \ $(SRCDIR)/fossil.page.pikchrshowasm.js \ $(SRCDIR)/fossil.page.whistory.js \ $(SRCDIR)/fossil.page.wikiedit.js \ $(SRCDIR)/fossil.pikchr.js \ $(SRCDIR)/fossil.popupwidget.js \ $(SRCDIR)/fossil.storage.js \ $(SRCDIR)/fossil.tabs.js \ $(SRCDIR)/fossil.wikiedit-wysiwyg.js \ | > | 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 | $(SRCDIR)/fossil.numbered-lines.js \ $(SRCDIR)/fossil.page.brlist.js \ $(SRCDIR)/fossil.page.chat.js \ $(SRCDIR)/fossil.page.fileedit.js \ $(SRCDIR)/fossil.page.forumpost.js \ $(SRCDIR)/fossil.page.pikchrshow.js \ $(SRCDIR)/fossil.page.pikchrshowasm.js \ $(SRCDIR)/fossil.page.ticket.js \ $(SRCDIR)/fossil.page.whistory.js \ $(SRCDIR)/fossil.page.wikiedit.js \ $(SRCDIR)/fossil.pikchr.js \ $(SRCDIR)/fossil.popupwidget.js \ $(SRCDIR)/fossil.storage.js \ $(SRCDIR)/fossil.tabs.js \ $(SRCDIR)/fossil.wikiedit-wysiwyg.js \ |
| ︙ | ︙ | |||
769 770 771 772 773 774 775 776 777 778 779 780 781 782 | $(OBJDIR)/printf_.c \ $(OBJDIR)/publish_.c \ $(OBJDIR)/purge_.c \ $(OBJDIR)/rebuild_.c \ $(OBJDIR)/regexp_.c \ $(OBJDIR)/repolist_.c \ $(OBJDIR)/report_.c \ $(OBJDIR)/rss_.c \ $(OBJDIR)/schema_.c \ $(OBJDIR)/search_.c \ $(OBJDIR)/security_audit_.c \ $(OBJDIR)/setup_.c \ $(OBJDIR)/setupuser_.c \ $(OBJDIR)/sha1_.c \ | > | 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 | $(OBJDIR)/printf_.c \ $(OBJDIR)/publish_.c \ $(OBJDIR)/purge_.c \ $(OBJDIR)/rebuild_.c \ $(OBJDIR)/regexp_.c \ $(OBJDIR)/repolist_.c \ $(OBJDIR)/report_.c \ $(OBJDIR)/robot_.c \ $(OBJDIR)/rss_.c \ $(OBJDIR)/schema_.c \ $(OBJDIR)/search_.c \ $(OBJDIR)/security_audit_.c \ $(OBJDIR)/setup_.c \ $(OBJDIR)/setupuser_.c \ $(OBJDIR)/sha1_.c \ |
| ︙ | ︙ | |||
919 920 921 922 923 924 925 926 927 928 929 930 931 932 | $(OBJDIR)/printf.o \ $(OBJDIR)/publish.o \ $(OBJDIR)/purge.o \ $(OBJDIR)/rebuild.o \ $(OBJDIR)/regexp.o \ $(OBJDIR)/repolist.o \ $(OBJDIR)/report.o \ $(OBJDIR)/rss.o \ $(OBJDIR)/schema.o \ $(OBJDIR)/search.o \ $(OBJDIR)/security_audit.o \ $(OBJDIR)/setup.o \ $(OBJDIR)/setupuser.o \ $(OBJDIR)/sha1.o \ | > | 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 | $(OBJDIR)/printf.o \ $(OBJDIR)/publish.o \ $(OBJDIR)/purge.o \ $(OBJDIR)/rebuild.o \ $(OBJDIR)/regexp.o \ $(OBJDIR)/repolist.o \ $(OBJDIR)/report.o \ $(OBJDIR)/robot.o \ $(OBJDIR)/rss.o \ $(OBJDIR)/schema.o \ $(OBJDIR)/search.o \ $(OBJDIR)/security_audit.o \ $(OBJDIR)/setup.o \ $(OBJDIR)/setupuser.o \ $(OBJDIR)/sha1.o \ |
| ︙ | ︙ | |||
1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 | $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \ $(OBJDIR)/publish_.c:$(OBJDIR)/publish.h \ $(OBJDIR)/purge_.c:$(OBJDIR)/purge.h \ $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \ $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \ $(OBJDIR)/repolist_.c:$(OBJDIR)/repolist.h \ $(OBJDIR)/report_.c:$(OBJDIR)/report.h \ $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \ $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \ $(OBJDIR)/search_.c:$(OBJDIR)/search.h \ $(OBJDIR)/security_audit_.c:$(OBJDIR)/security_audit.h \ $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ $(OBJDIR)/setupuser_.c:$(OBJDIR)/setupuser.h \ $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h \ | > | 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 | $(OBJDIR)/printf_.c:$(OBJDIR)/printf.h \ $(OBJDIR)/publish_.c:$(OBJDIR)/publish.h \ $(OBJDIR)/purge_.c:$(OBJDIR)/purge.h \ $(OBJDIR)/rebuild_.c:$(OBJDIR)/rebuild.h \ $(OBJDIR)/regexp_.c:$(OBJDIR)/regexp.h \ $(OBJDIR)/repolist_.c:$(OBJDIR)/repolist.h \ $(OBJDIR)/report_.c:$(OBJDIR)/report.h \ $(OBJDIR)/robot_.c:$(OBJDIR)/robot.h \ $(OBJDIR)/rss_.c:$(OBJDIR)/rss.h \ $(OBJDIR)/schema_.c:$(OBJDIR)/schema.h \ $(OBJDIR)/search_.c:$(OBJDIR)/search.h \ $(OBJDIR)/security_audit_.c:$(OBJDIR)/security_audit.h \ $(OBJDIR)/setup_.c:$(OBJDIR)/setup.h \ $(OBJDIR)/setupuser_.c:$(OBJDIR)/setupuser.h \ $(OBJDIR)/sha1_.c:$(OBJDIR)/sha1.h \ |
| ︙ | ︙ | |||
2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 | $(OBJDIR)/report_.c: $(SRCDIR)/report.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/report.c >$@ $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c $(OBJDIR)/report.h: $(OBJDIR)/headers $(OBJDIR)/rss_.c: $(SRCDIR)/rss.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/rss.c >$@ $(OBJDIR)/rss.o: $(OBJDIR)/rss_.c $(OBJDIR)/rss.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/rss.o -c $(OBJDIR)/rss_.c | > > > > > > > > | 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 | $(OBJDIR)/report_.c: $(SRCDIR)/report.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/report.c >$@ $(OBJDIR)/report.o: $(OBJDIR)/report_.c $(OBJDIR)/report.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/report.o -c $(OBJDIR)/report_.c $(OBJDIR)/report.h: $(OBJDIR)/headers $(OBJDIR)/robot_.c: $(SRCDIR)/robot.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/robot.c >$@ $(OBJDIR)/robot.o: $(OBJDIR)/robot_.c $(OBJDIR)/robot.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/robot.o -c $(OBJDIR)/robot_.c $(OBJDIR)/robot.h: $(OBJDIR)/headers $(OBJDIR)/rss_.c: $(SRCDIR)/rss.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/rss.c >$@ $(OBJDIR)/rss.o: $(OBJDIR)/rss_.c $(OBJDIR)/rss.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/rss.o -c $(OBJDIR)/rss_.c |
| ︙ | ︙ |
Changes to win/Makefile.msc.
| ︙ | ︙ | |||
465 466 467 468 469 470 471 472 473 474 475 476 477 478 |
"$(OX)\printf_.c" \
"$(OX)\publish_.c" \
"$(OX)\purge_.c" \
"$(OX)\rebuild_.c" \
"$(OX)\regexp_.c" \
"$(OX)\repolist_.c" \
"$(OX)\report_.c" \
"$(OX)\rss_.c" \
"$(OX)\schema_.c" \
"$(OX)\search_.c" \
"$(OX)\security_audit_.c" \
"$(OX)\setup_.c" \
"$(OX)\setupuser_.c" \
"$(OX)\sha1_.c" \
| > | 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 |
"$(OX)\printf_.c" \
"$(OX)\publish_.c" \
"$(OX)\purge_.c" \
"$(OX)\rebuild_.c" \
"$(OX)\regexp_.c" \
"$(OX)\repolist_.c" \
"$(OX)\report_.c" \
"$(OX)\robot_.c" \
"$(OX)\rss_.c" \
"$(OX)\schema_.c" \
"$(OX)\search_.c" \
"$(OX)\security_audit_.c" \
"$(OX)\setup_.c" \
"$(OX)\setupuser_.c" \
"$(OX)\sha1_.c" \
|
| ︙ | ︙ | |||
582 583 584 585 586 587 588 589 590 591 592 593 594 595 |
"$(SRCDIR)\fossil.numbered-lines.js" \
"$(SRCDIR)\fossil.page.brlist.js" \
"$(SRCDIR)\fossil.page.chat.js" \
"$(SRCDIR)\fossil.page.fileedit.js" \
"$(SRCDIR)\fossil.page.forumpost.js" \
"$(SRCDIR)\fossil.page.pikchrshow.js" \
"$(SRCDIR)\fossil.page.pikchrshowasm.js" \
"$(SRCDIR)\fossil.page.whistory.js" \
"$(SRCDIR)\fossil.page.wikiedit.js" \
"$(SRCDIR)\fossil.pikchr.js" \
"$(SRCDIR)\fossil.popupwidget.js" \
"$(SRCDIR)\fossil.storage.js" \
"$(SRCDIR)\fossil.tabs.js" \
"$(SRCDIR)\fossil.wikiedit-wysiwyg.js" \
| > | 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 |
"$(SRCDIR)\fossil.numbered-lines.js" \
"$(SRCDIR)\fossil.page.brlist.js" \
"$(SRCDIR)\fossil.page.chat.js" \
"$(SRCDIR)\fossil.page.fileedit.js" \
"$(SRCDIR)\fossil.page.forumpost.js" \
"$(SRCDIR)\fossil.page.pikchrshow.js" \
"$(SRCDIR)\fossil.page.pikchrshowasm.js" \
"$(SRCDIR)\fossil.page.ticket.js" \
"$(SRCDIR)\fossil.page.whistory.js" \
"$(SRCDIR)\fossil.page.wikiedit.js" \
"$(SRCDIR)\fossil.pikchr.js" \
"$(SRCDIR)\fossil.popupwidget.js" \
"$(SRCDIR)\fossil.storage.js" \
"$(SRCDIR)\fossil.tabs.js" \
"$(SRCDIR)\fossil.wikiedit-wysiwyg.js" \
|
| ︙ | ︙ | |||
732 733 734 735 736 737 738 739 740 741 742 743 744 745 |
"$(OX)\printf$O" \
"$(OX)\publish$O" \
"$(OX)\purge$O" \
"$(OX)\rebuild$O" \
"$(OX)\regexp$O" \
"$(OX)\repolist$O" \
"$(OX)\report$O" \
"$(OX)\rss$O" \
"$(OX)\schema$O" \
"$(OX)\search$O" \
"$(OX)\security_audit$O" \
"$(OX)\setup$O" \
"$(OX)\setupuser$O" \
"$(OX)\sha1$O" \
| > | 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 |
"$(OX)\printf$O" \
"$(OX)\publish$O" \
"$(OX)\purge$O" \
"$(OX)\rebuild$O" \
"$(OX)\regexp$O" \
"$(OX)\repolist$O" \
"$(OX)\report$O" \
"$(OX)\robot$O" \
"$(OX)\rss$O" \
"$(OX)\schema$O" \
"$(OX)\search$O" \
"$(OX)\security_audit$O" \
"$(OX)\setup$O" \
"$(OX)\setupuser$O" \
"$(OX)\sha1$O" \
|
| ︙ | ︙ | |||
982 983 984 985 986 987 988 989 990 991 992 993 994 995 | echo "$(OX)\printf.obj" >> $@ echo "$(OX)\publish.obj" >> $@ echo "$(OX)\purge.obj" >> $@ echo "$(OX)\rebuild.obj" >> $@ echo "$(OX)\regexp.obj" >> $@ echo "$(OX)\repolist.obj" >> $@ echo "$(OX)\report.obj" >> $@ echo "$(OX)\rss.obj" >> $@ echo "$(OX)\schema.obj" >> $@ echo "$(OX)\search.obj" >> $@ echo "$(OX)\security_audit.obj" >> $@ echo "$(OX)\setup.obj" >> $@ echo "$(OX)\setupuser.obj" >> $@ echo "$(OX)\sha1.obj" >> $@ | > | 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 | echo "$(OX)\printf.obj" >> $@ echo "$(OX)\publish.obj" >> $@ echo "$(OX)\purge.obj" >> $@ echo "$(OX)\rebuild.obj" >> $@ echo "$(OX)\regexp.obj" >> $@ echo "$(OX)\repolist.obj" >> $@ echo "$(OX)\report.obj" >> $@ echo "$(OX)\robot.obj" >> $@ echo "$(OX)\rss.obj" >> $@ echo "$(OX)\schema.obj" >> $@ echo "$(OX)\search.obj" >> $@ echo "$(OX)\security_audit.obj" >> $@ echo "$(OX)\setup.obj" >> $@ echo "$(OX)\setupuser.obj" >> $@ echo "$(OX)\sha1.obj" >> $@ |
| ︙ | ︙ | |||
1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 | echo "$(SRCDIR)\fossil.numbered-lines.js" >> $@ echo "$(SRCDIR)\fossil.page.brlist.js" >> $@ echo "$(SRCDIR)\fossil.page.chat.js" >> $@ echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@ echo "$(SRCDIR)\fossil.page.forumpost.js" >> $@ echo "$(SRCDIR)\fossil.page.pikchrshow.js" >> $@ echo "$(SRCDIR)\fossil.page.pikchrshowasm.js" >> $@ echo "$(SRCDIR)\fossil.page.whistory.js" >> $@ echo "$(SRCDIR)\fossil.page.wikiedit.js" >> $@ echo "$(SRCDIR)\fossil.pikchr.js" >> $@ echo "$(SRCDIR)\fossil.popupwidget.js" >> $@ echo "$(SRCDIR)\fossil.storage.js" >> $@ echo "$(SRCDIR)\fossil.tabs.js" >> $@ echo "$(SRCDIR)\fossil.wikiedit-wysiwyg.js" >> $@ | > | 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 | echo "$(SRCDIR)\fossil.numbered-lines.js" >> $@ echo "$(SRCDIR)\fossil.page.brlist.js" >> $@ echo "$(SRCDIR)\fossil.page.chat.js" >> $@ echo "$(SRCDIR)\fossil.page.fileedit.js" >> $@ echo "$(SRCDIR)\fossil.page.forumpost.js" >> $@ echo "$(SRCDIR)\fossil.page.pikchrshow.js" >> $@ echo "$(SRCDIR)\fossil.page.pikchrshowasm.js" >> $@ echo "$(SRCDIR)\fossil.page.ticket.js" >> $@ echo "$(SRCDIR)\fossil.page.whistory.js" >> $@ echo "$(SRCDIR)\fossil.page.wikiedit.js" >> $@ echo "$(SRCDIR)\fossil.pikchr.js" >> $@ echo "$(SRCDIR)\fossil.popupwidget.js" >> $@ echo "$(SRCDIR)\fossil.storage.js" >> $@ echo "$(SRCDIR)\fossil.tabs.js" >> $@ echo "$(SRCDIR)\fossil.wikiedit-wysiwyg.js" >> $@ |
| ︙ | ︙ | |||
1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 | "$(OBJDIR)\translate$E" $** > $@ "$(OX)\report$O" : "$(OX)\report_.c" "$(OX)\report.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\report_.c" "$(OX)\report_.c" : "$(SRCDIR)\report.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\rss$O" : "$(OX)\rss_.c" "$(OX)\rss.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\rss_.c" "$(OX)\rss_.c" : "$(SRCDIR)\rss.c" "$(OBJDIR)\translate$E" $** > $@ | > > > > > > | 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 | "$(OBJDIR)\translate$E" $** > $@ "$(OX)\report$O" : "$(OX)\report_.c" "$(OX)\report.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\report_.c" "$(OX)\report_.c" : "$(SRCDIR)\report.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\robot$O" : "$(OX)\robot_.c" "$(OX)\robot.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\robot_.c" "$(OX)\robot_.c" : "$(SRCDIR)\robot.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\rss$O" : "$(OX)\rss_.c" "$(OX)\rss.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\rss_.c" "$(OX)\rss_.c" : "$(SRCDIR)\rss.c" "$(OBJDIR)\translate$E" $** > $@ |
| ︙ | ︙ | |||
2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 | "$(OX)\printf_.c":"$(OX)\printf.h" \ "$(OX)\publish_.c":"$(OX)\publish.h" \ "$(OX)\purge_.c":"$(OX)\purge.h" \ "$(OX)\rebuild_.c":"$(OX)\rebuild.h" \ "$(OX)\regexp_.c":"$(OX)\regexp.h" \ "$(OX)\repolist_.c":"$(OX)\repolist.h" \ "$(OX)\report_.c":"$(OX)\report.h" \ "$(OX)\rss_.c":"$(OX)\rss.h" \ "$(OX)\schema_.c":"$(OX)\schema.h" \ "$(OX)\search_.c":"$(OX)\search.h" \ "$(OX)\security_audit_.c":"$(OX)\security_audit.h" \ "$(OX)\setup_.c":"$(OX)\setup.h" \ "$(OX)\setupuser_.c":"$(OX)\setupuser.h" \ "$(OX)\sha1_.c":"$(OX)\sha1.h" \ | > | 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 | "$(OX)\printf_.c":"$(OX)\printf.h" \ "$(OX)\publish_.c":"$(OX)\publish.h" \ "$(OX)\purge_.c":"$(OX)\purge.h" \ "$(OX)\rebuild_.c":"$(OX)\rebuild.h" \ "$(OX)\regexp_.c":"$(OX)\regexp.h" \ "$(OX)\repolist_.c":"$(OX)\repolist.h" \ "$(OX)\report_.c":"$(OX)\report.h" \ "$(OX)\robot_.c":"$(OX)\robot.h" \ "$(OX)\rss_.c":"$(OX)\rss.h" \ "$(OX)\schema_.c":"$(OX)\schema.h" \ "$(OX)\search_.c":"$(OX)\search.h" \ "$(OX)\security_audit_.c":"$(OX)\security_audit.h" \ "$(OX)\setup_.c":"$(OX)\setup.h" \ "$(OX)\setupuser_.c":"$(OX)\setupuser.h" \ "$(OX)\sha1_.c":"$(OX)\sha1.h" \ |
| ︙ | ︙ |
Changes to www/aboutcgi.wiki.
| ︙ | ︙ | |||
129 130 131 132 133 134 135 | out what web page is being requested, generates that one web page, then exits. Usually, the webpage being requested is the first term of the PATH_INFO environment variable. (Exceptions to this rule are noted in the sequel.) For our example, the first term of PATH_INFO is "timeline", which means that Fossil will generate | | | | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | out what web page is being requested, generates that one web page, then exits. Usually, the webpage being requested is the first term of the PATH_INFO environment variable. (Exceptions to this rule are noted in the sequel.) For our example, the first term of PATH_INFO is "timeline", which means that Fossil will generate the [/help/www/timeline|/timeline] webpage. With Fossil, terms of PATH_INFO beyond the webpage name are converted into the "name" query parameter. Hence, the following two URLs mean exactly the same thing to Fossil: <ol type='A'> <li> [https://fossil-scm.org/home/info/c14ecc43] <li> [https://fossil-scm.org/home/info?name=c14ecc43] </ol> In both cases, the CGI script is called "/fossil". For case (A), the PATH_INFO variable will be "info/c14ecc43" and so the "[/help/www/info|/info]" webpage will be generated and the suffix of PATH_INFO will be converted into the "name" query parameter, which identifies the artifact about which information is requested. In case (B), the PATH_INFO is just "info", but the same "name" query parameter is set explicitly by the URL itself. <h2>Serving Multiple Fossil Repositories From One CGI Script</h2> |
| ︙ | ︙ | |||
280 281 282 283 284 285 286 | source is seen as a space of key/value pairs which are loaded into an internal property hash table. The code that runs to generate the reply can then reference various properties values. Fossil does not care where the value of each property comes from (POST content, cookies, or query parameters) only that the property exists and has a value.</p></li> <li><p> | | | 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 | source is seen as a space of key/value pairs which are loaded into an internal property hash table. The code that runs to generate the reply can then reference various properties values. Fossil does not care where the value of each property comes from (POST content, cookies, or query parameters) only that the property exists and has a value.</p></li> <li><p> The "[/help/ui|fossil ui]" and "[/help/server|fossil server]" commands are implemented using a simple built-in web server that accepts incoming HTTP requests, translates each request into a CGI invocation, then creates a separate child Fossil process to handle each request. In other words, CGI is used internally to implement "fossil ui/server". <br><br> SCGI is processed using the same built-in web server, just modified to parse SCGI requests instead of HTTP requests. Each SCGI request is |
| ︙ | ︙ |
Changes to www/aboutdownload.wiki.
1 2 3 4 5 6 7 8 | <title>How The Fossil Download Page Works</title> <h2>1.0 Overview</h2> The [/uv/download.html|Download] page for the Fossil self-hosting repository is implemented using [./unvers.wiki|unversioned files]. The "download.html" screen itself, and the various build products are all stored as unversioned content. The download.html page | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | <title>How The Fossil Download Page Works</title> <h2>1.0 Overview</h2> The [/uv/download.html|Download] page for the Fossil self-hosting repository is implemented using [./unvers.wiki|unversioned files]. The "download.html" screen itself, and the various build products are all stored as unversioned content. The download.html page uses XMLHttpRequest() to retrieve the [/help/www/juvlist|/juvlist] webpage for a list of all unversioned files. Javascript in the [/uv/download.js?mimetype=text/plain|download.js] file (which is sourced by "download.html") then figures out which unversioned files are build products and paints appropriate icons on the displayed download page. Except, the "Source Tarball" download products are not stored as unversioned files. They are computed on-demand by the [/help/www/tarball|/tarball web page]. When a new version is generated, the developers use the [/help/uv|fossil uv edit] command to make minor changes to the "[/uv/download.js?mimetype=text/plain|download.js]" file so that it knows about the new version number. Then the developers run the [/help/uv|fossil uv add] command for each build product. Finally, the [/help/uv|fossil uv sync] command is run to push all the content up to servers. All [./selfhost.wiki|three self-hosting repositories] for Fossil are updated automatically. <h2>2.0 Details</h2> The current text of the "download.html" and "download.js" files can |
| ︙ | ︙ | |||
49 50 51 52 53 54 55 | Fossil knows to add its standard header and footer information to the document, making it look just like any other page. See "[./embeddeddoc.wiki|embedded documentation]" for further details on how this <div class='fossil-doc'> markup works. With each new release, the "releases" variable in the javascript on the [/uv/download.js?mimetype=text/plain|download.js] page is | | | | | | | | 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 | Fossil knows to add its standard header and footer information to the document, making it look just like any other page. See "[./embeddeddoc.wiki|embedded documentation]" for further details on how this <div class='fossil-doc'> markup works. With each new release, the "releases" variable in the javascript on the [/uv/download.js?mimetype=text/plain|download.js] page is edited (using "[/help/uv|fossil uv edit download.js]") to add details of the release. When the JavaScript in the "download.js" file runs, it requests a listing of all unversioned content using the /juvlist URL. ([/juvlist|sample /juvlist output]). The content of the download page is constructed by matching unversioned files against regular expressions in the "releases" variable. Build products need to be constructed on different machines. The precompiled binary for Linux is compiled on Linux, the precompiled binary for Windows is compiled on Windows11, and so forth. After a new release is tagged, the release manager goes around to each of the target platforms, checks out the release and compiles it, then runs [/help/uv|fossil uv add] for the build product followed by [/help/uv|fossil uv sync] to push the new build product to the [./selfhost.wiki|various servers]. This process is repeated for each build product. When older builds are retired from the download page, the [/uv/download.js?mimetype=text/plain|download.js] page is again edited to remove the corresponding entry from the "release" variable and the edit is synced using [/help/uv|fossil uv sync]. This causes the build products to disappear from the download page immediately. But those build products are still taking up space in the unversioned content table of the server repository. To purge the obsolete build products, one or more [/help/uv|fossil uv rm] commands are run, followed by another [/help/uv|fossil uv sync]. It is important to purge obsolete build products since they take up a lot of space. At [/repo-tabsize] you can see that the unversioned table takes up a substantial fraction of the repository. <h2>3.0 Security</h2> Only users with the [/setup_ulist_notes|"y" permission] are allowed |
| ︙ | ︙ |
Changes to www/alerts.md.
1 2 3 4 5 6 7 | # Email Alerts ## Overview Beginning with version 2.7, Fossil can send email messages to subscribers to alert them to changes in the repository: | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # Email Alerts ## Overview Beginning with version 2.7, Fossil can send email messages to subscribers to alert them to changes in the repository: * New [checkins](/help/ci) * [Ticket](./tickets.wiki) changes * [Wiki](./wikitheory.wiki) page changes * New and edited [forum](./forum.wiki) posts * Users receiving [new permissions](./caps/index.md) (admins only) * Announcements Subscribers can elect to receive emails as soon as these events happen, |
| ︙ | ︙ | |||
539 540 541 542 543 544 545 | This section collects the list of Fossil UI pages and CLI commands that control the email alert system, some of which have not been mentioned so far: Commands: | | | | | | | | | | | | 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 | This section collects the list of Fossil UI pages and CLI commands that control the email alert system, some of which have not been mentioned so far: Commands: * The [`alerts`](/help/alerts) command * The [`test-alert`](/help/test-alert) command * The [`test-add-alerts`](/help/test-add-alerts) command Web pages available to users and subscribers: * The [`/subscribe`](/help/www/subscribe) page * The [`/alerts`](/help/www/alerts) page * The [`/unsubscribe`](/help/www/unsubscribe) page * The [`/renew`](/help/www/renew) page * The [`/contact_admin`](/help/www/contact_admin) page Administrator-only web pages: * The [`/setup_notification`](/help/www/setup_notification) page * The [`/subscribers`](/help/www/subscribers) page <a id="design"></a> ## Design of Email Alerts This section describes the low-level design of the email alert system in Fossil. This expands on the high-level administration focused material |
| ︙ | ︙ |
Changes to www/antibot.wiki.
1 2 | <title>Defense Against Robots</title> | | | | > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | <title>Defense Against Robots</title> A typical Fossil website can have billions and billions of pages, and many of those pages (for example diffs and annotations and tarballs) can be expensive to compute. If a robot walks a Fossil-generated website, it can present a crippling bandwidth and CPU load. A "robots.txt" file can help, but in practice, most robots these days ignore the robots.txt file, so it won't help much. A Fossil website is intended to be used interactively by humans, not walked by robots. This article describes the techniques used by Fossil to try to welcome human users while keeping out robots. <h2>Defenses Are Enabled By Default</h2> In the latest implementations of Fossil, most robot defenses are enabled by default. You can probably get by with standing up a public-facing Fossil instance in the default configuration. But you can also customize the defenses to serve your particular needs. <h2>Customizing Anti-Robot Defenses</h2> Admin users can configure robot defenses on the "Robot Defense Settings" page (/setup_robot). That page is accessible (to Admin users) from the default menu bar by click on the "Admin" menu choice, then selecting the "Robot-Defense" link from the list. <h2>The Hyperlink User Capability</h2> Every Fossil web session has a "user". For random passers-by on the internet (and for robots) that user is "nobody". The "anonymous" user is also available for humans who do not wish to identify themselves. The difference is that "anonymous" requires a login (using a password supplied via |
| ︙ | ︙ | |||
34 35 36 37 38 39 40 | A text message appears at the top of each page in this situation to invite humans to log in as anonymous in order to activate hyperlinks. But requiring a login, even an anonymous login, can be annoying. Fossil provides other techniques for blocking robots which are less cumbersome to humans. | | | 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | A text message appears at the top of each page in this situation to invite humans to log in as anonymous in order to activate hyperlinks. But requiring a login, even an anonymous login, can be annoying. Fossil provides other techniques for blocking robots which are less cumbersome to humans. <h2>Automatic Hyperlinks Based on UserAgent and Javascript</h2> Fossil has the ability to selectively enable hyperlinks for users that lack the <b>Hyperlink</b> capability based on their UserAgent string in the HTTP request header and on the browsers ability to run Javascript. The UserAgent string is a text identifier that is included in the header of most HTTP requests that identifies the specific maker and version of |
| ︙ | ︙ | |||
66 67 68 69 70 71 72 | of the requester and so a malicious robot can forge a UserAgent string that makes it look like a human. But most robots want to "play nicely" on the internet and are quite open about the fact that they are a robot. And so the UserAgent string provides a good first-guess about whether or not a request originates from a human or a robot. | | | > | | | | < | < < < < < < < < < < < < < < < | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
of the requester and so a malicious robot can forge a UserAgent
string that makes it look like a human. But most robots want
to "play nicely" on the internet and are quite open
about the fact that they are a robot. And so the UserAgent string
provides a good first-guess about whether or not a request originates
from a human or a robot.
The [/help/auto-hyperlink|auto-hyperlink] setting, shown as
"<b>Enable hyperlinks based on User-Agent and/or Javascript</b>" on
the Robot Defense Settings page,
can be set to "UserAgent only" or "UserAgent and Javascript" or "off".
If the UserAgent string looks like a human and not a robot, then
Fossil will enable hyperlinks even if the <b>Hyperlink</b> capability
is omitted from the user permissions. This setting gives humans easy
access to the hyperlinks while preventing robots
from walking the billions of pages on a typical Fossil site.
If the setting is "UserAgent only" (2), then the hyperlinks are simply
enabled and that is all. But if the setting is "UserAgent and Javascript" (1),
then the hyperlinks are not enabled directly.
Instead, the HTML code that is generated contains anchor tags ("<a>")
with "href=" attributes that point to [/honeypot] rather than the correct
link. JavaScript code is added to the end of the page that goes back and
fills in the correct "href=" attributes of
the anchor tags with the true hyperlink targets, thus enabling the hyperlinks.
This extra step of using JavaScript to enable the hyperlink targets
is a security measure against robots that forge a human-looking
UserAgent string. Most robots do not bother to run JavaScript and
so to the robot the empty anchor tag will be useless. But all modern
web browsers implement JavaScript, so hyperlinks will show up
normally for human users.
If the [/help/auto-hyperlink|"auto-hyperlink"] setting is (2)
"<b>Enable hyperlinks using User-Agent and/or Javascript</b>",
then there are now two additional sub-settings that control when
hyperlinks are enabled.
The first new sub-setting is a delay (in milliseconds) before setting
the "href=" attributes on anchor tags. The default value for this
delay is 10 milliseconds. The idea here is that a robots will try to
interpret the links on the page immediately, and will not wait for delayed
scripts to be run, and thus will never enable the true links.
The second sub-setting waits to run the
JavaScript that sets the "href=" attributes on anchor tags until after
at least one "mousedown" or "mousemove" event has been detected on the
<body> element of the page. The thinking here is that robots will not be
simulating mouse motion and so no mouse events will ever occur and
hence the hyperlinks will never become enabled for robots.
See also [./loadmgmt.md|Managing Server Load] for a description
of how expensive pages can be disabled when the server is under heavy
load.
<h2>Do Not Allow Robot Access To Certain Pages</h2>
The [/help/robot-restrict|robot-restrict setting] is a comma-separated
list of GLOB patterns for pages for which robot access is prohibited.
The default value is:
<blockquote><pre>
timelineX,diff,annotate,fileage,file,finfo,reports
</pre></blockquote>
Each entry corresponds to the first path element on the URI for a
Fossil-generated page. If Fossil does not know for certain that the
HTTP request is coming from a human, then any attempt to access one of
these pages brings up a javascript-powered captcha. The user has to
click the accept button the captcha once, and that sets a cookie allowing
the user to continue surfing without interruption for 15 minutes or so
before being presented with another captcha.
Some path elements have special meanings:
* <b>timelineX →</b>
This means a subset of /timeline/ pages that are considered
"expensive". The exact definition of which timeline pages are
expensive and which are not is still the subject of active
experimentation and is likely to change by the time you read this
text. The idea is that anybody (including robots) can see a timeline
of the most recent changes, but timelines of long-ago change or that
contain lists of file changes or other harder-to-compute values are
prohibited.
* <b>zip →</b>
The special "zip" keyword also matches "/tarball/" and "/sqlar/".
* <b>zipX →</b>
This is like "zip" in that it restricts access to "/zip/", "/tarball"/
and "/sqlar/" but with exceptions:<ol type="a">
<li><p> If the [/help/robot-zip-leaf|robot-zip-leaf] setting is
true, then tarballs of leaf check-ins are allowed. This permits
URLs that attempt to download the latest check-in on trunk or
from a named branch, for example.
<li><p> If a check-in has a tag that matches the GLOB list in
[/help/robot-zip-tag|robot-zip-tag], then tarballs of that
check-in are allowed. This allow check-ins tagged with
"release" or "allow-robots" (for example) to be downloaded
without restriction.
</ol>
The "zipX" restriction is not in the default robot-restrict setting.
This is something you might want to add, depending on your needs.
* <b>diff →</b>
This matches /vdiff/ and /fdiff/ and /vpatch/ and any other page that
is primarily about showing the difference between two check-ins or two
file versioons.
* <b>annotate →</b>
This also matches /blame/ and /praise/.
Other special keywords may be added in the future.
The default [/help/robot-restrict|robot-restrict]
setting has been shown in practice to do a good job of keeping
robots from consuming all available CPU and bandwidth while will
still allowing humans access to the full power of the site without
having to be logged in.
One possible enhancement is to add "zipX" to the
[/help/robot-restrict|robot-restrict] setting,
and enable [help?cmd=robot-zip-leaf|robot-zip-leaf]
and configure [help?cmd=robot-zip-tag|robot-zip-tag].
Do this if you find that robots downloading lots of
obscure tarballs is causing load issues on your site.
<h2>Anti-robot Exception RegExps</h2>
The [/help/robot-exception|robot-exception setting] under the name
of <b>Exceptions to anti-robot restrictions</b> is a list of
[/re_rules|regular expressions], one per line, that match
URIs that will bypass the captcha and allow robots full access. The
intent of this setting is to allow automated build scripts
to download specific tarballs of project snapshots.
The recommended value for this setting allows robots to use URIs of the
following form:
<blockquote>
<b>https://</b><i>DOMAIN</i><b>/tarball/release/</b><i>HASH</i><b>/</b><i>NAME</i><b>.tar.gz</b>
</blockquote>
The <i>HASH</i> part of this URL can be any valid
[./checkin_names.wiki|check-in name]. The link works as long as that
check-in is tagged with the "release" symbolic tag. In this way,
robots are permitted to download tarballs (and ZIP archives) of official
releases, but not every intermediate check-in between releases. Humans
who are willing to click the captcha can still download whatever they
want, but robots are blocked by the captcha. This prevents aggressive
robots from downloading tarballs of every historical check-in of your
project, once per day, which many robots these days seem eager to do.
For example, on the Fossil project itself, this URL will work, even for
robots:
<blockquote>
https://fossil-scm.org/home/tarball/release/version-2.27/fossil-scm.tar.gz
</blockquote>
But the next URL will not work for robots because check-in 3bbd18a284c8bd6a
is not tagged as a "release":
<blockquote>
https://fossil-scm.org/home/tarball/release/3bbd18a284c8bd6a/fossil-scm.tar.gz
</blockquote>
The second URL will work for humans, just not robots.
<h2>The Ongoing Struggle</h2>
Fossil currently does a good job of providing easy access to humans
while keeping out troublesome robots. However, robots
continue to grow more sophisticated, requiring ever more advanced
defenses. This "arms race" is unlikely to ever end. The developers of
Fossil will continue to try improve the robot defenses of Fossil so
check back from time to time for the latest releases and updates.
Readers of this page who have suggestions on how to improve the robot
defenses in Fossil are invited to submit your ideas to the Fossil Users
forum:
[https://fossil-scm.org/forum].
|
Changes to www/backoffice.md.
| ︙ | ︙ | |||
32 33 34 35 36 37 38 | request. After each webpage is generated, Fossil checks to see if any backoffice work needs to be done. If there is work to do, and no other process is already assigned to do the work, then a new backoffice process is started to do the work. This happens for every webpage, regardless of how that webpage is launched, and regardless of the purpose of the webpage. This also happens on the | | | | | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | request. After each webpage is generated, Fossil checks to see if any backoffice work needs to be done. If there is work to do, and no other process is already assigned to do the work, then a new backoffice process is started to do the work. This happens for every webpage, regardless of how that webpage is launched, and regardless of the purpose of the webpage. This also happens on the server for "[fossil sync](/help/sync)" and [fossil clone](/help/clone)" commands which are implemented as web requests - albeit requests that the human user never sees. Web requests can arrive at the Fossil server via direct TCP/IP (for example when Fossil is started using commands like "[fossil server](/help/server)") or via [CGI](./server/any/cgi.md) or [SCGI](./server/any/scgi.md) or via SSH. A backoffice process might be started regardless of the origin of the request. The backoffice is not a daemon. Each backoffice process runs for a short while and then exits. This helps keep Fossil easy to manage, since there |
| ︙ | ︙ | |||
98 99 100 101 102 103 104 | some system - OpenBSD in particular. We still do not understand why this is. (If you have insights, please share them on the [Fossil Forum](https://fossil-scm.org/forum) so that we can perhaps fix the problem.) For now, the backoffice must be run manually on OpenBSD systems. To set up fully-manual backoffice, first disable the automatic backoffice | | | | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
some system - OpenBSD in particular. We still do not understand why
this is. (If you have insights, please share them on the
[Fossil Forum](https://fossil-scm.org/forum) so that we can perhaps
fix the problem.) For now, the backoffice must be run manually
on OpenBSD systems.
To set up fully-manual backoffice, first disable the automatic backoffice
using the "[backoffice-disable](/help/backoffice-disable)" setting.
fossil setting backoffice-disable on
Then arrange to invoke the backoffice separately using a command
like this:
fossil backoffice --poll 30 _REPOSITORY-LIST_
Multiple repositories can be named. This one command will handle
launching the backoffice for all of them. There are additional useful
command-line options. See the "[fossil backoffice](/help/backoffice)"
documentation for details.
The backoffice processes run manually using the "fossil backoffice"
command do not normally use a lease. That means that if you run the
"fossil backoffice" command with --poll and you forget to disable
automatic backoffice by setting the "backoffice-disable" flag, then
you might have one backoffice running due to a command and another due
|
| ︙ | ︙ |
Changes to www/backup.md.
| ︙ | ︙ | |||
66 67 68 69 70 71 72 | #### <a id="other-cfg"></a> Others A repo’s URL aliases, [interwiki configuration](./interwiki.md), and [ticket customizations](./custom_tcket.wiki) also do not normally sync. | | | 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | #### <a id="other-cfg"></a> Others A repo’s URL aliases, [interwiki configuration](./interwiki.md), and [ticket customizations](./custom_tcket.wiki) also do not normally sync. [cfg]: /help/configuration ## <a id="private"></a> Private Branches The very nature of Fossil’s [private branch feature][pbr] ensures that remote clones don’t get a copy of those branches. Normally this is |
| ︙ | ︙ | |||
287 288 289 290 291 292 293 | pipe the decrypted SQL dump into `fossil sql`, because on startup, Fossil normally goes looking for tables created by `fossil init`, and it won’t find them in a newly-created repo DB. We get around this by passing the `--no-repository` flag, which suppresses this behavior. Doing it this way saves you from needing to go and build a matching version of `sqlite3` just to restore the backup. | | | 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 | pipe the decrypted SQL dump into `fossil sql`, because on startup, Fossil normally goes looking for tables created by `fossil init`, and it won’t find them in a newly-created repo DB. We get around this by passing the `--no-repository` flag, which suppresses this behavior. Doing it this way saves you from needing to go and build a matching version of `sqlite3` just to restore the backup. [bu]: /help/backup [grcp]: https://www.grc.com/passwords.htm [hb]: https://brew.sh [hbul]: https://docs.brew.sh/FAQ#what-does-keg-only-mean [lz4]: https://lz4.github.io/lz4/ [pbr]: ./private.wiki [rint]: https://www.random.org/integers/?num=1&min=100000&max=1000000&col=5&base=10&format=html&rnd=new [Setup]: ./caps/admin-v-setup.md#apsu [shun]: ./shunning.wiki [tkt]: ./tickets.wiki [uv]: ./unvers.wiki |
Changes to www/blame.wiki.
1 2 3 4 | <title>The Annotate Algorithm</title> <h2>1.0 Introduction</h2> | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <title>The Annotate Algorithm</title> <h2>1.0 Introduction</h2> The [/help/annotate|fossil annotate], [/help/blame|fossil blame], and [/help/praise|fossil praise] commands, and the [/help/www/annotate|/annotate], [/help/www/blame|/blame], and [/help/www/praise|/praise] web pages are all used to show the most recent check-in that modified each line of a particular file. This article overviews the algorithm used to compute the annotation for a file in Fossil. <h2>2.0 Algorithm</h2> <ol type='1'> |
| ︙ | ︙ | |||
43 44 45 46 47 48 49 | <h2>3.0 Discussion and Notes</h2> The time-consuming part of this algorithm is step 6b - computing the diff from all historical versions of the file to the version of the file under analysis. For a large file that has many historical changes, this can take several seconds. For this reason, the default | | | 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | <h2>3.0 Discussion and Notes</h2> The time-consuming part of this algorithm is step 6b - computing the diff from all historical versions of the file to the version of the file under analysis. For a large file that has many historical changes, this can take several seconds. For this reason, the default [/help/www/annotate|/annotate] webpage only shows those lines that were changed by the 20 most recent modifications to the file. This allows the loop on step 6 to terminate after only 19 diffs instead of the hundreds or thousands of diffs that might be required for a frequently modified file. As currently implemented (as of 2015-12-12) the annotate algorithm does not follow files across name changes. File name change information is available in the database, and so the algorithm could be enhanced to follow files across name changes by modifications to step 3. Step 2 is interesting in that it is [/artifact/6cb824a0417?ln=196-201 | implemented] using a [https://www.sqlite.org/lang_with.html#recursivecte|recursive common table expression]. |
Changes to www/blockchain.md.
| ︙ | ︙ | |||
206 207 208 209 210 211 212 | This much is certain: Fossil is definitely not a cryptocurrency. Whether this makes it “not a blockchain” is a subjective matter. [arh]: ./hooks.md [bse]: https://www.researchgate.net/publication/311572122_What_is_Blockchain_a_Gentle_Introduction [caps]: ./caps/ | | | 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 | This much is certain: Fossil is definitely not a cryptocurrency. Whether this makes it “not a blockchain” is a subjective matter. [arh]: ./hooks.md [bse]: https://www.researchgate.net/publication/311572122_What_is_Blockchain_a_Gentle_Introduction [caps]: ./caps/ [cs]: /help/clearsign [dboc]: https://en.wikipedia.org/wiki/Debasement [dsig]: https://en.wikipedia.org/wiki/Digital_signature [fb]: ./branching.wiki [GPG]: https://gnupg.org/ [PGP]: https://www.openpgp.org/ [PII]: https://en.wikipedia.org/wiki/Personal_data [PKI]: https://en.wikipedia.org/wiki/Public_key_infrastructure |
| ︙ | ︙ | |||
276 277 278 279 280 281 282 | [ctap]: ./cap-theorem.md#ap [ctca]: ./cap-theorem.md#ca [ctcp]: ./cap-theorem.md#cp [cap]: https://en.wikipedia.org/wiki/CAP_theorem [dlt]: https://en.wikipedia.org/wiki/Distributed_ledger [DVCS]: https://en.wikipedia.org/wiki/Distributed_version_control [fc]: https://en.wikipedia.org/wiki/Fiat_money | | | 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 | [ctap]: ./cap-theorem.md#ap [ctca]: ./cap-theorem.md#ca [ctcp]: ./cap-theorem.md#cp [cap]: https://en.wikipedia.org/wiki/CAP_theorem [dlt]: https://en.wikipedia.org/wiki/Distributed_ledger [DVCS]: https://en.wikipedia.org/wiki/Distributed_version_control [fc]: https://en.wikipedia.org/wiki/Fiat_money [purge]: /help/purge [shun]: ./shunning.wiki <a id="dpc"></a> ## Distributed Partial Consensus If we can’t get DLT, can we at least get some kind of distributed |
| ︙ | ︙ | |||
333 334 335 336 337 338 339 | would mean that querying whether a given commit is part of the “blockchain” would be as simple as going down the list of servers and sending each an HTTP GET `/info` query for the artifact ID, concluding that the commit is legitimate once you get enough HTTP 200 status codes back. All of this is hypothetical, because Fossil doesn’t do this today. [AGI]: https://en.wikipedia.org/wiki/Artificial_general_intelligence | | | 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 | would mean that querying whether a given commit is part of the “blockchain” would be as simple as going down the list of servers and sending each an HTTP GET `/info` query for the artifact ID, concluding that the commit is legitimate once you get enough HTTP 200 status codes back. All of this is hypothetical, because Fossil doesn’t do this today. [AGI]: https://en.wikipedia.org/wiki/Artificial_general_intelligence [rcks]: /help/repo-cksum <a id="anon"></a> ## Anonymity Many blockchain based technologies go to extraordinary lengths to |
| ︙ | ︙ | |||
445 446 447 448 449 450 451 | on their scores under these metrics. We may well prefer to use the fork of a software program that took *less* effort, being smaller, more self-contained, and with a smaller attack surface. [alert]: ./alerts.md [capi]: ./caps/ref.html#i | | | | | 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 | on their scores under these metrics. We may well prefer to use the fork of a software program that took *less* effort, being smaller, more self-contained, and with a smaller attack surface. [alert]: ./alerts.md [capi]: ./caps/ref.html#i [mrep]: /help/remote [scost]: https://en.wikipedia.org/wiki/Software_development_effort_estimation [scrub]: /help/scrub [sreg]: /help/self-register # Conclusion This author believes it is technologically indefensible to call Fossil a “blockchain” in any sense likely to be understood by a majority of those you’re communicating with. Using a term in a nonstandard way just because you can |
| ︙ | ︙ |
Changes to www/branching.wiki.
| ︙ | ︙ | |||
73 74 75 76 77 78 79 | works. This is also how Fossil works in autosync mode. But perhaps Bob is off-network when he does his commit, so he has no way of knowing that Alice has already committed her changes. Or, it could be that Bob has turned off "autosync" mode in Fossil. Or, maybe Bob just doesn't want to merge in Alice's changes before he has saved his own, so he forces the commit to occur using the "--allow-fork" option to | | | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | works. This is also how Fossil works in autosync mode. But perhaps Bob is off-network when he does his commit, so he has no way of knowing that Alice has already committed her changes. Or, it could be that Bob has turned off "autosync" mode in Fossil. Or, maybe Bob just doesn't want to merge in Alice's changes before he has saved his own, so he forces the commit to occur using the "--allow-fork" option to the <b>[/help/commit | fossil commit]</b> command. For any of these reasons, two commits against check-in 2 have occurred, so the DAG now has two leaves. In such a condition, a person working with this repository has a dilemma: which version of the project is the "latest" in the sense of having the most features and the most bug fixes? When there is more than one leaf in the graph, you don't really know, which is why we |
| ︙ | ︙ | |||
109 110 111 112 113 114 115 | with "--allow-fork". (Prime example: trunk.) The inverse case — intentional forks on short-lived single-developer branches — is far easier to justify, since presumably the lone developer is never confused about why there are two or more leaves on that branch. Further justifications for intentional forking are [#forking | given below]. Let us return to Figure 2. To resolve such situations before they can | | | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | with "--allow-fork". (Prime example: trunk.) The inverse case — intentional forks on short-lived single-developer branches — is far easier to justify, since presumably the lone developer is never confused about why there are two or more leaves on that branch. Further justifications for intentional forking are [#forking | given below]. Let us return to Figure 2. To resolve such situations before they can become a real problem, Alice can use the <b>[/help/merge | fossil merge]</b> command to merge Bob's changes into her local copy of check-in 3. Without arguments, that command merges all leaves on the current branch. Alice can then verify that the merge is sensible and if so, commit the results as check-in 5. This results in a DAG as shown in Figure 3. <verbatim type="pikchr center toggle"> |
| ︙ | ︙ | |||
347 348 349 350 351 352 353 |
<li><p id="automation">You've automated Fossil, so you use
<b>fossil commit --allow-fork</b> commands to prevent Fossil from
refusing the check-in simply because it would create a fork.
<br><br>
If you are writing such a tool — e.g. a shell script to make
multiple manipulations on a Fossil repo — it's better to make it
smart enough to detect this condition and cope with it, such as
| | | 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 |
<li><p id="automation">You've automated Fossil, so you use
<b>fossil commit --allow-fork</b> commands to prevent Fossil from
refusing the check-in simply because it would create a fork.
<br><br>
If you are writing such a tool — e.g. a shell script to make
multiple manipulations on a Fossil repo — it's better to make it
smart enough to detect this condition and cope with it, such as
by making a call to <b>[/help/update | fossil update]</b>
and checking for a merge conflict. That said, if the alternative is
losing information, you may feel justified in creating forks that an
interactive user must later manually clean up with <b>fossil merge</b>
commands.</p></li>
</ol>
That leaves only one case where we can recommend use of "--allow-fork"
|
| ︙ | ︙ | |||
598 599 600 601 602 603 604 |
on a different branch at the time Alan made check-in 3, so Fossil
sees that as the tip at the time she switches her working directory
to that branch with a <b>fossil update $BRANCH</b> command. (There is an
implicit autosync in that command, if the option was enabled at the
time of the update.)</p></li>
<li><p>The same thing, only in a fresh checkout directory with a
| | | 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 |
on a different branch at the time Alan made check-in 3, so Fossil
sees that as the tip at the time she switches her working directory
to that branch with a <b>fossil update $BRANCH</b> command. (There is an
implicit autosync in that command, if the option was enabled at the
time of the update.)</p></li>
<li><p>The same thing, only in a fresh checkout directory with a
<b>[/help/open | fossil open $REPO $BRANCH]</b> command.</p></li>
<li><p>Alan makes his check-in 3 while Betty has check-in 1 or 2 as
the tip in her local clone, but because she's working with an
autosync'd connection to the same upstream repository as Alan, on
attempting what will become check-in 4, she gets the "would fork"
message from <b>fossil commit</b>, so she dutifully updates her clone
and tries again, moving her work to be a child of the new tip,
|
| ︙ | ︙ | |||
749 750 751 752 753 754 755 | This fact is helpful because it means you can reuse branch names, which is especially useful with utility branches. There are several of these in the SQLite and Fossil repositories: "broken-build," "declined," "mistake," etc. As you might guess from these names, such branch names are used in renaming the tip of one branch to shunt it off away from the mainline of that branch due to some human error. (See | | | 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 | This fact is helpful because it means you can reuse branch names, which is especially useful with utility branches. There are several of these in the SQLite and Fossil repositories: "broken-build," "declined," "mistake," etc. As you might guess from these names, such branch names are used in renaming the tip of one branch to shunt it off away from the mainline of that branch due to some human error. (See <b>[/help/amend | fossil amend]</b> and the Fossil UI check-in amendment features.) This is a workaround for Fossil's [./shunning.wiki|normal inability to forget history]: we usually don't want to actually <i>remove</i> history, but would like to sometimes set some of it aside under a new label. Because some VCSes can't cope with duplicate branch names, Fossil collapses such names down on export using the same time stamp based arbitration logic, so that only the branch with the newest check-in gets the branch name in the export. All of the above is true of tags in general, not just branches. |
Changes to www/build.wiki.
| ︙ | ︙ | |||
333 334 335 336 337 338 339 | This feature is primarily intended for fossil's developers and may change at any time. It is only known to work on Linux systems and has been seen to work on x86/64 and ARM. Fossil has builtin support for processing specific features using <tt>libfuzzer</tt>. The features which can be tested this way are | | | 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 |
This feature is primarily intended for fossil's developers and may
change at any time. It is only known to work on Linux systems and has
been seen to work on x86/64 and ARM.
Fossil has builtin support for processing specific features using
<tt>libfuzzer</tt>. The features which can be tested this way are
found in the help text for the [/help/test-fuzz|test-fuzz
command].
Fuzzing requires:
* The clang C compiler.
* libfuzzer. On Ubuntu-derived systems, it can be installed with
<tt>apt install libfuzzer-XYZ</tt>, where XYZ is a version number
|
| ︙ | ︙ | |||
508 509 510 511 512 513 514 | changed the format of list-type arguments at some point. The required minimum version is unknown, but any SDK version from May 2022 or later "should" (as of this writing) suffice. Any older version may or may not work.</div> After that succeeds, we need to run the normal build so that those generated files can be compiled in to the fossil binary, accessible | | | 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 | changed the format of list-type arguments at some point. The required minimum version is unknown, but any SDK version from May 2022 or later "should" (as of this writing) suffice. Any older version may or may not work.</div> After that succeeds, we need to run the normal build so that those generated files can be compiled in to the fossil binary, accessible via the [/help/www/builtin|/builtin page]: <pre><code>$ make</code></pre> Before checking in those newly-built files, they need to be tested by running the [/pikchrshow] page. If that page loads, the compilation process fundamentally worked (a load failure will be made obvious to the viewer). If it fails to load then the browser's dev tools console |
| ︙ | ︙ |
Changes to www/caps/admin-v-setup.md.
| ︙ | ︙ | |||
259 260 261 262 263 264 265 | In addition, Setup users can use every feature of the Fossil UI. If Fossil can do a thing, a Setup user on that repo can make Fossil do it. Setup users can do many things that Admin users cannot. They may not only use all of the Admin UI features, they may also: * See record IDs (RIDs) on screens that show them | | | | | 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 |
In addition, Setup users can use every feature of the Fossil UI. If Fossil can do a
thing, a Setup user on that repo can make Fossil do it.
Setup users can do many things that Admin users cannot. They may not
only use all of the Admin UI features, they may also:
* See record IDs (RIDs) on screens that show them
* See the MIME type of attachments on [`/ainfo` pages](/help/www/ainfo)
* See a remote repo’s HTTP [cache status](/help/www/cachestat)
and [pull cache entries](/help/www/cacheget)
* Edit a Setup user’s account!
The “Admin” feature of Fossil UI is so-named because Admin users can use
about half of its functions, but only Setup can use these pages:
* **Access**: This page falls under the [Security](#security)
category above, but like Configuration, it's generally something set
|
| ︙ | ︙ | |||
393 394 395 396 397 398 399 | Fossil makes these users grant themselves (or others) these capabilities deliberately, hopefully after careful consideration. ### <a id="y"></a>Write Unversioned Fossil currently doesn’t distinguish the sub-operations of | | | 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 | Fossil makes these users grant themselves (or others) these capabilities deliberately, hopefully after careful consideration. ### <a id="y"></a>Write Unversioned Fossil currently doesn’t distinguish the sub-operations of [`fossil uv`](/help/uv); they’re all covered by [**WrUnver**][capy] (“y”) capability. Since some of these operations are unconditionally destructive due to the nature of unversioned content, and since this goes against Fossil’s philosophy of immutable history, nobody gets cap “y” on a Fossil repo by default, not even the Setup or Admin users. A Setup or Admin user must grant cap “y” to someone — not necessarily themselves! — before modifications to remote unversioned content are possible. |
| ︙ | ︙ | |||
444 445 446 447 448 449 450 | [capa]: ./ref.html#a [caps]: ./ref.html#s [capx]: ./ref.html#x [capy]: ./ref.html#y | | | | 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 | [capa]: ./ref.html#a [caps]: ./ref.html#s [capx]: ./ref.html#x [capy]: ./ref.html#y [fcp]: https://fossil-scm.org/home/help/configuration [fdp]: ../fossil-v-git.wiki#devorg [forum]: https://fossil-scm.org/forum/ [fui]: /help/ui [lg]: ./login-groups.md [rs]: https://fossil-scm.org/home/doc/trunk/www/settings.wiki [sia]: https://fossil-scm.org/home/artifact?ln=1259-1260&name=0fda31b6683c206a [snoy]: https://fossil-scm.org/forum/forumpost/00e1c4ecff [th1]: ../th1.md [tt]: https://en.wikipedia.org/wiki/Tiger_team#Security [webo]: ./#webonly |
Changes to www/caps/login-groups.md.
| ︙ | ︙ | |||
115 116 117 118 119 120 121 | That creates login group G joining repo A to B, then joins C to B. Although we didn’t explicitly tie C to A, a successful login on C gets you into both A and B, within the restrictions set out above. Changes are transitive in the same way, provided you check that “apply to all” box on the user edit screen. | | | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | That creates login group G joining repo A to B, then joins C to B. Although we didn’t explicitly tie C to A, a successful login on C gets you into both A and B, within the restrictions set out above. Changes are transitive in the same way, provided you check that “apply to all” box on the user edit screen. [lg]: /help/login-group [sh]: ../server/any/http-over-ssh.md [wo]: ./index.md#webonly ----- *[Back to Administering User Capabilities](./)* |
Changes to www/caps/ref.html.
| ︙ | ︙ | |||
313 314 315 316 317 318 319 |
</tr>
<tr id="z">
<th>z</th>
<th>Zip</th>
<td>
Pull archives of particular repository versions via <a
| | | | | 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 |
</tr>
<tr id="z">
<th>z</th>
<th>Zip</th>
<td>
Pull archives of particular repository versions via <a
href="/help/www/zip"><tt>/zip</tt></a>, <a
href="/help/www/tarball"><tt>/tarball</tt></a>, and <a
href="/help/www/sqlar"><tt>/sqlar</tt></a> URLs. This is an
expensive capability to grant, because creating such archives can
put a large load on <a href="../server/">a Fossil server</a> which
you may then need to <a href="../loadmgmt.md">manage</a>.
Mnemonic: <b>z</b>ip file download.
</td>
</tr>
|
| ︙ | ︙ |
Changes to www/cgi.wiki.
| ︙ | ︙ | |||
70 71 72 73 74 75 76 | This is a Boolean property. If it is present, and if the [#directory:|<b>directory:</b>] option is used, and if the PATH_INFO string is empty, then Fossil will show a list of available Fossil repositories. The "skin" of the reply is determined by the first repository in the list that has a non-zero | | | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | This is a Boolean property. If it is present, and if the [#directory:|<b>directory:</b>] option is used, and if the PATH_INFO string is empty, then Fossil will show a list of available Fossil repositories. The "skin" of the reply is determined by the first repository in the list that has a non-zero [/help/repolist-skin|repolist-skin] setting. If no repository has such a non-zero repolist-skin setting, then the repository list is generic HTML without any decoration, with the page title taken from the <tt>FOSSIL_REPOLIST_TITLE</tt> environment variable. The variable can be defined in the CGI control file using the [#setenv|<tt>setenv:</tt>] statement. |
| ︙ | ︙ | |||
191 192 193 194 195 196 197 | ticket that matches the value of "name", then redirect to URL. There can be multiple "redirect:" lines that are processed in order. If the repo name is "*", then an unconditional redirect to URL is taken. <h2 id="jsmode">jsmode: <i>VALUE</i></h2> | | | 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 | ticket that matches the value of "name", then redirect to URL. There can be multiple "redirect:" lines that are processed in order. If the repo name is "*", then an unconditional redirect to URL is taken. <h2 id="jsmode">jsmode: <i>VALUE</i></h2> Specifies the delivery mode for JavaScript files. See "[/help/http | http --jsmode]" for the allowed values and their meanings. <h2 id="mainmenu">mainmenu: <i>FILE</i></h2> This parameter causes the contents of the given file to override the site's <tt>mainmenu</tt> configuration setting, in much the same way that the <tt>skin</tt> setting overrides the skin. This can be used to apply a common main menu to a number of sites, and centrally maintain it, without having to copy its contents into each site. Note, however, that the contents of this setting are not stored in the repository and will not be cloned along with the repository. |
Changes to www/changes.wiki.
1 2 | <title>Change Log</title> | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | > > > > | < > > > > > > > | | | > > | < < | > > > > | | | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
<title>Change Log</title>
<h2 id='v2_28'>Changes for version 2.28 (pending)</h2><ol>
<li> Improvements to [./antibot.wiki|anti-robot defenses]:<ol type="a">
<li> The default configuration now allows robots to download any tarball
or similar, to better support of automated build systems.
<li> New special tag "zipX" for the [/help/robot-restrict|robot-restrict]
setting blocks robot access to tarballs, but with exceptions to support
automated build systems.
<li> Enhancements to the default value for the
[/help/robot-restrict|robot-restrict setting].
</ol>
<li> A drop-down menu of recent branches is now possible for the submenu, and
is used in the [/dir?ci=trunk|code browser].
<li> Easier access to generated tarballs and ZIPs:<ol type="a">
<li> When in the [/dir?ci=trunk|code browser] at the top-level,
a new "Download" submenu option is available to take the
user to a page where he can download a tarball or ZIP archive.
<li> New [/help/www/download|/download page] is available. When
configured using the new
[/help/suggested-downloads|suggested-downloads setting], a
link to [/download] named "Tarballs and ZIPs" appears in the
[/sitemap] and thus on the hamburger menu.
<li> The filenames for tarballs and ZIPs are now standardized to
include a timestamp and a hash prefix.
</ol>
<li> Timeline enhancements:<ol type="a">
<li> A new "Simple" view is available. This is compromise between "Verbose"
and "Compact" that shows only the check-in hash rather than the full
detail section. There is an ellipsis that one can click on to see the
full detail text.
<li> The artifact hash in the detail section of each timeline entry is now
emphasized (controlled by CSS) to make it easier to locate amid all
the other text and links.
<li> When clicking on the ellipsis in "Compact" or "Simple" views, the ellipsis
is replaced by a left arrow ("←") which can be clicked to hide the extra
detail again.
<li> New setting [/help/timeline-mark-leaves|timeline-mark-leaves] controls
whether or not leaf check-ins are marked in the timeline.
<li> "No-graph" timelines (using the "ng" query parameter) now show
branch colors and bare check-in circles on the left. The check-in
circles appear, but no lines connecting them.
([/timeline?ng|example]).
</ol>
<li> The [/help/timeline|timeline command] is enhanced with the new
"<tt>-u|--for-user</tt>" option.
<li> The [/help/open|open command]'s new "<tt>--reopen REPOFILE</tt>" flag
can be used to fix a checkout after moving its repository file.
</ol>
<h2 id='v2_27'>Changes for version 2.27 (2025-09-30)</h2><ol>
<li> Close a potential Denial-of-Service attack against any public-facing Fossil
server involving exponential behavior in Fossil's regexp implementation.
<li> Fix a SQL injection on the [/help/www/file|/file page]. Thanks to
additional defenses built into Fossil, as well as good luck, this injection
is not exploitable for either data exfiltration or privilege escalation. The
only possible result of invoking the injection is a harmless SQL syntax error.
<li> Strengthen robot defenses to help prevent public-facing servers from being
overwhelmed by the latest generation of AI spiders.
<ol type="a">
<li> New javascript captcha used to restrict access by user "nobody" to pages
listed in the [/help/robot-restrict|robot-restrict setting].
<li> The [/help/robot-exception|robot-exception setting] is available to allow
access to pages that match a regular expression. Use this, for example, to
allow curl scripts and similar to download release tarballs.
<li> Require at least an anonymous login to access the /blame page and similar.
</ol>
<li> [/help/www/timeline|Timeline] enhancements:
<ol type="a">
<li> The chng= query parameter on the [/help/www/timeline|timeline page]
so that it works with other query parameters like p=, d=, from=, and to=.
<li> Always include nodes identify by sel1= and sel2= in the /timeline display.
<li> Improved title when p= and d= are different.
</ol>
<li> Enable the --editor option on the [/help/amend|fossil amend] command.
<li> When walking the filesystem looking for Fossil repositories, avoid descending
into directories named "/proc".
<li> Reduce memory requirements for sending authenticated sync protocol
messages.
<li> Show numstat-style change statistics in the /info and /ckout pages.
<li> Add the [/help/stash | stash rename] subcommand.
<li> Add the "-h" option to the "[/help/ls|ls]" command to display
file hashes for a specific check-in when in verbose mode.
</ol>
<h2 id='v2_26'>Changes for version 2.26 (2025-04-30)</h2><ol>
<li>Enhancements to [/help/diff|fossil diff] and similar:
<ol type="a">
<li> The argument to the --from option can be a directory name, causing
Fossil to use files under that directory as the baseline for the diff.
<li> For "gdiff", if no [/help/gdiff-command|gdiff-command setting]
is defined, Fossil tries to do a --tk diff if "tclsh" and "wish"
are available, or a --by diff if not.
<li> The "Reload" button is added to --tk diffs, to bring the displayed
diff up to date with the latest changes on disk.
<li> Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show
diffs of multiple files.
</ol>
<li>Added the [/help/www/ckout|/ckout web page] to provide information
about pending changes in a working check-out
<li>Enhancements to the [/help/ui|fossil ui] command:
<ol type="a">
<li> Defaults to using the new [/help/www/ckout|/ckout page] as its
start page. Or, if the new "--from PATH" option is present, the
default start page becomes "/ckout?exbase=PATH".
<li> The new "--extpage FILENAME" option opens the named file as if it
where in a [./serverext.wiki|CGI extension]. Example usage: the
person editing this change log has
"fossil ui --extpage www/changes.wiki" running and hence can
press "Reload" on the web browser to view edits.
<li> Accept both IPv4 and IPv6 connections on all platforms, including
Windows and OpenBSD. This also applies to the "fossil server"
command.
</ol>
<li>Enhancements to [/help/merge|fossil merge]:
<ol type="a">
<li> Added the [/help/merge-info|fossil merge-info] command and
especially the --tk option to that command, to provide analysis
of the most recent merge or update operation.
<li> When a merge conflict occurs, a new section is added to the conflict
text that shows Fossil's suggested resolution to the conflict.
</ol>
<li>Enhancements to [/help/commit|fossil commit]:
<ol type="a">
<li> If Fossil sees potential formatting mistakes (ex: bad hyperlinks)
in the check-in comment, it will alert the developer and give
him or her the opportunity to edit the comment before continuing.
This feature is controllable by the
[/help/verify-comments|verify-comments setting].
<li> The new "--if-changes" option causes the commit to become
a quiet no-op if there are no pending changes.
<li> Added the ability to sign check-ins with SSH keys. Artifacts signed
this way are ignored by all previous fossil versions, as if they
were plain-text file content instead of Fossil artifacts.
<li> Issue a warning if a user tries to commit on a check-in where the
branch has been changed.
<li> The interactive checkin comment prompt shows the formatting rules
set for that repository.
<li> Add the "--editor" option.
</ol>
<li>Deprecate the --comfmtflags and --comment-format global options and
no longer list them in the built-in help, but keep them working for
backwards compatibility.
Alternative TTY comment formatting can still be specified using the
[/help/comment-format|comment-format setting], if desired. The
default comment format is now called "canonical", not "legacy".
<li>Enhancements to the [/help/www/timeline|/timeline page]:
<ol type="a">
<li> Added the "ml=" ("Merge-in List") query parameter that works
like "rl=" ("Related List") but adds "mionly" style related
check-ins instead of the full "rel" style.
<li> For "tl=", "rl=", and "ml=", the order of the branches in the
graph now tries to match the order of the branches named in
the list.
|
| ︙ | ︙ | |||
109 110 111 112 113 114 115 |
end-points.
<li> The p= and d= parameters can now reference different check-ins,
in which case the timeline shows those check-ins that are both
ancestors of p= and descendants of d=.
<li> The saturation and intensity of user-specified checkin and branch
background colors are automatically adjusted to keep the colors
compatible with the current skin, unless the
| | | | | | | | | | | | | | | | | | | | | | | | | 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
end-points.
<li> The p= and d= parameters can now reference different check-ins,
in which case the timeline shows those check-ins that are both
ancestors of p= and descendants of d=.
<li> The saturation and intensity of user-specified checkin and branch
background colors are automatically adjusted to keep the colors
compatible with the current skin, unless the
[/help/raw-bgcolor|raw-bgcolor setting] is turned on.
</ol>
<li>The [/help/www/docfile|/docfile webpage] was added. It works like
/doc but keeps the title of markdown documents with the document rather
that moving it up to the page title.
<li>Added the [/help/www/clusterlist|/clusterlist page] for analysis
and debugging
<li>Added the "artifact_to_json(NAME)" SQL function that returns a JSON
decoding of the artifact described by NAME.
<li>Improvements to the [/help/patch|fossil patch] command:
<ol type="a">
<li> Fix a bug in "fossil patch create" that causes
[/help/revert|fossil revert] operations that happened
on individualfiles after a [/help/merge|fossil merge]
to be omitted from the patch.
<li> Added the [/help/patch|patch alias] command for managing
aliases for remote checkout names.
</ol>
<li>Enhancements to on-line help and the [/help/help|fossil help] command:
<ol type="a">
<li> Add the ability to search the help text, either in the UI
(on the [/help/www/search|/search page]) or from the command-line
(using the "[/help/search|fossil search -h PATTERN]" command.)
<li> Accepts an optional SUBCOMMAND argument following the
COMMAND argument and only shows results for the specified
subcommand, not the entire command.
<li> The -u (--usage) option shows only the command-line syntax
<li> The -o (--options) option shows only the command-line options
</ol>
<li>Enhancements to the [./tickets.wiki|ticket system]:
<ol type="a">
<li> Added the ability to attach wiki pages to a ticket for extended
descriptions.
<li> Added submenu to the 'View Ticket' page, to use it as
template for a new ticket.
<li> Added button 'Submit and New' to create multiple tickets
in a row.
<li> Link the version field in ticket view to a matching checkin or tag.
<li> Show creation time in report and ticket view.
<li> Show previous comments in edit ticket as reference.
</ol>
<li>Added the "hash" query parameter to the
[/help/www/whatis|/whatis webpage].
<li>Add a "user permissions changes" [/doc/trunk/www/alerts.md|subscription]
which alerts subscribers when an admin creates a new user or
when a user's permissions change.
<li>If the FOSSIL_REPOLIST_SHOW environment variable exists and contains
the substring "description", then the project description for each repository
is shown on the repository list page. The login-group for each project is
now only shown if the FOSSIL_REPOLIST_SHOW environment variable exists and
contains the substring "login-group". ([./cgi.wiki#repolist|More information])
<li>The [/doc/trunk/www/th1.md|TH1 script language] is enhanced for improved
security:
<ol type="a">
<li> TH1 now makes a distinction between
[/doc/trunk/www/th1.md#taint|tainted and untainted string values].
This makes it more difficult to write custom TH1 scripts that
contain XSS or SQL-injection bugs. The
[/help/vuln-report|vuln-report] setting was added to control
what Fossil does when it encounters a potential TH1
security problem.
<li> The "--th" option was removed from the [/help/pikchr|fossil pikchr]
command.
<li> The "enable_htmlify" TH1 command was removed.
</ol>
<li>Make [/help/www/chat|/chat] better-behaved during server outages, reducing
the frequency of reconnection attempts over time and providing feedback
to the user when the connection is down.
<li>The [/help/www/sqlar|/sqlar] page does not work for users who are not logged
in, nor are links to that page displayed to users who are not logged in. Being
logged in as "anonymous" is sufficient to overcome this restriction, assuming
that "anonymous" can download tarballs and ZIP archives.
<li>Many other minor fixes and additions.
</ol>
<h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2>
* The "[/help/ui|fossil ui /]" command now works even for repositories
that have non-ASCII filenames
* Add the [/help/tree|fossil tree] command.
* On case-insensitive filesystems, store files using the filesystem's
preferred case rather than the case typed in by the user.
* Change the name "fossil cherry-pick" command to "fossil cherrypick",
which is more familiar to Git users. Retain the legacy name for
compatibility.
* Add new query parameters to the [/help/www/timeline|/timeline page]:
d2=, p2=, and dp2=.
* Add options to the [/help/tag|fossil tag] command that will list tag values.
* Add the -b|--brief option to the [/help/status|fossil status] command.
* Add ability to upload unversioned files via the [/help/www/uvlist|/uvlist page].
* Add history search to the [/help/www/chat|/chat page].
* Add Unix socket support to the [/help/server|server command].
* On Windows, use the root certificates managed by the operating system
(requires OpenSSL 3.2.0 or greater).
* Take into account zero-width and double-width unicode characters when
formatting the command-line timeline.
* Update the built-in SQLite to version 3.47.0. Precompiled binaries are
linked against OpenSSL 3.4.0.
* Numerous minor fixes and additions.
|
| ︙ | ︙ | |||
241 242 243 244 245 246 247 |
select one of the built-in skins as a default, or to specify a
custom skin.
</ul>
* If an "ssh:" sync fails in a way that suggests that the fossil executable
could not be found on the remote host, then retry after adding a PATH=
prefix to the command. This helps "ssh:" to "just work" when the server
is a Mac.
| | | | | | | | | | | | | | | 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 |
select one of the built-in skins as a default, or to specify a
custom skin.
</ul>
* If an "ssh:" sync fails in a way that suggests that the fossil executable
could not be found on the remote host, then retry after adding a PATH=
prefix to the command. This helps "ssh:" to "just work" when the server
is a Mac.
* Enhancements to the [/help/www/timeline|/timeline page]:
<ul>
<li> Add the x= query paramater
<li> Add the shortcut tl= and rl= query parameters
<li> Add support for from=,ft= and from=,bt= query parameter combinations
<li> Automatically highlight the endpoints for from=,to= queries.
<li> Add the to2=Z query parameter to augment from=X,to=Y so that the
path from X to Z is shown if Y cannot be found.
</ul>
* Moved the /museum/repo.fossil file referenced from the Dockerfile from
the ENTRYPOINT to the CMD part to allow use of --repolist mode.
* The [/uvlist] page now shows the hash algorithm used so that
viewers don't have to guess. The hash is shown in a fixed-width
font for a more visually pleasing display.
* If the [/help/autosync|autosync setting] contains keyword "all",
the automatic sync occurs against all defined remote repositories, not
just the default.
* Markdown formatter: improved handling of indented fenced code blocks
that contain blank lines.
* When doing a "[/help/add|fossil add]" on a system with case-insensitive
but case-preserving filenames (Mac and Windows) try to use the filename
case as it is known to the filesystem, not the case entered by the
user on the command-line. See
[forum:/forumpost/30d9c0d131610f53|forum thread 30d9c0d131610f53].
* Fix problems with one-click unsubscribe on email notifications.
* Import the latest [/doc/trunk/www/pikchr.md|Pikchr] containing support
for "diamond" objects.
* Add ability to render committed Pikchr files to SVG via
<samp>/doc/…/foo.pikchr?popup</samp> URLs.
* Update Fossil's internal robot detection logic so that it correctly
identifies the new GoogleOther crawler as a robot.
<h2 id='v2_23'>Changes for version 2.23 (2023-11-01)</h2>
* Add ability to "close" forum threads, such that unprivileged users
may no longer respond to them. Only administrators can close
threads or respond to them by default, and the
[/help/forum-close-policy|forum-close-policy setting] can be
used to add that capability to moderators.
* Add the [/help/all|fossil all whatis] command.
* The [/help/status|fossil status] command and relevant UI pages now
correctly report files which were both renamed <b>and</b> edited as such.
* Show default value of settings that have a default in
[/help/help|fossil help SETTING] output.
* On timeline graphs, show closed check-ins using an X in the middle of the
node circle or box.
* New options for email notification: Get email only for the first
post in each new thread, and/or posts that are in reply to my posts.
* Fix a regression bug introduced in version 2.22 that caused FTS5 searches
to fail for terms containing non-ASCII characters.
* Improved defense-in-depth against malicious attack:
<ul>
<li> When an attempted SQL injection attack is detected, return
HTTP result code 418, which can signal the web server to sanction
the attacking IP address.
<li> Better defense against cross-site request forgery (CSRF)
attacks.
<li> Improvements to static analysis of source code (the codecheck1.c
file in the source tree).
</ul>
* Enhance the [/help/www/dir|treeview file listings]
([/dir?type=tree&ci=trunk|example]) by displaying file sizes
and adding the option to sort by file size.
* The [/help/fts-config|fossil fts-config] command now shows how much
repository space is used by the full-text index.
* Changing a setting to an empty string is now the same as deleting the
setting, in most cases. There are a few exceptions, indicated by the
keep-empty flag on the setting definition.
* The [/help/branch|fossil branch list] command can now filter branches
that have/have not been merged into the current branch.
* Improvements to interactions with remote repositories over SSH:
<ul>
<li> Print the text of the SSH command that is run to do remote interaction,
for full disclosure to the operator.
<li> Add a PATH= argument to the [/help/ui|fossil ui remote:/] and
[/help/patch|fossil patch push/pull remote:...] commands so that
they work when the "remote" machine is a Mac and the "fossil"
executable is in the $HOME/bin directory.
</ul>
* Update built-in libraries SQLite, ZLib, Pikchr to their latest versions.
* Documentation enhancements and typo fixes.
<h2 id='v2_22'>Changes for version 2.22 (2023-05-31)</h2>
* Enhancements to the [/help/www/timeline|/timeline webpage]: <ol type="a">
<li> Add the ft=TAG query parameter which in combination with d=Y
shows all descendants of Y up to TAG
<li> Enhance the s=PATTERN (search) query parameter so that forum post
text is also searched when the "vfx" query parameter is used
<li> Fix the u= (user) query parameter so that it works with a= and b=
<li> Add the oldestfirst query parameter to show the events in reverse order.
Useful in combination with y=f and vfs and perhaps also u= to show all
|
| ︙ | ︙ | |||
353 354 355 356 357 358 359 |
inside the jail for Fossil's use.
* Add support for the trigram tokenizer for FTS5 search to enable
searching in Chinese.
* Comment lines (starting with a '#') are now supported inside
[./settings.wiki#versionable|versioned settings].
* Default permissions for anonymous users in new repositories are
changed to "hz".
| | | | | | | | | | | | | | | 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 |
inside the jail for Fossil's use.
* Add support for the trigram tokenizer for FTS5 search to enable
searching in Chinese.
* Comment lines (starting with a '#') are now supported inside
[./settings.wiki#versionable|versioned settings].
* Default permissions for anonymous users in new repositories are
changed to "hz".
* The [/help/status|fossil status] command now detects when a
file used to be a symlink and has been replaced by a regular file.
(It previously checked for the inverse case only.)
* The [/help/empty-dirs|empty-dirs setting] now reuses the same
parser as the *-glob settings instead of its prior idiosyncratic
parser, allowing quoted whitespace in patterns.
* Enhancements to the [/help/www/reports|/reports webpage]:
<ol type="a">
<li> The by-week, by-month, and by-year options now show an estimated
size of the current week, month, or year as a dashed box.
<li> New sub-categories "Merge Check-ins" and "Non-Merge Check-ins".
</ol>
<h2 id='v2_21'>Changes for version 2.21 (2023-02-25)</h2>
* Users can request a password reset. This feature is disabled by default.
Use the new [/help/self-pw-reset|self-pw-reset property] to enable it.
New web pages [/help/www/resetpw|/resetpw] and
[/help/www/reqpwreset|/reqpwreset] added.
* Add the [/help/repack|fossil repack] command (together with
[/help/all|fossil all repack]) as a convenient way to optimize the
size of one or all of the repositories on a system.
* Add the ability to put text descriptions on ticket report formats.
* Upgrade the test-find-pivot command to the [/help/merge-base|merge-base command].
* The [/help/www/chat|/chat page] can now embed fossil-rendered
views of wiki/markdown/pikchr file attachments with the caveat that such
embedding happens in an iframe and thus does not inherit styles and such
from the containing browser window.
* The [/help/all|fossil all remote] subcommand added to "fossil all".
* Passwords for remembered remote repositories are now stored as irreversible
hashes rather than obscured clear-text, for improved security.
* Add the "nossl" and "nocompress" options to CGI.
* Update search infrastructure from FTS4 to FTS5.
* Add the [/help/www/deltachain|/deltachain] page for debugging purposes.
* Writes to the database are disabled by default if the HTTP request
does not come from the same origin. This enhancement is a defense in depth
measure only; it does not address any known vulnerabilities.
* Improvements to automatic detection and mitigation of attacks from
malicious robots.
<h2 id='v2_20'>Changes for version 2.20 (2022-11-16)</h2>
* Added the [/help/chat-timeline-user|chat-timeline-user setting]. If
it is not an empty string, then any changes that would appear on the timeline
are announced in [./chat.md|the chat room].
* The /unsubscribe page now requests confirmation. [./alerts.md|Email notifications]
now contain only an "Unsubscribe" link, and not a link to subscription management.
* Added the "[/help/branch|fossil branch lsh]" subcommand to list the
most recently modified branches.
* More elements of the /info page are now inside of an accordion.
* Replace the <tt>--dryrun</tt> flag with <tt>--dry-run</tt> in all
commands which still used the former name, for consistency.
* Rebuilt [/file/Dockerfile | the stock Dockerfile] to create a "from scratch"
Busybox based container image via an Alpine Linux intermediary
* Added [/doc/trunk/www/containers.md | a new document] describing how to
|
| ︙ | ︙ | |||
432 433 434 435 436 437 438 |
* Performance enhancement for the
[./checkin_names.wiki#root|"root:BRANCHNAME" style of tag],
accomplished using a Common Table Expression in the underlying SQL.
* Sort tag listings (command line and webpage) by taking numbers into
consideration so as to cater for tags that follow semantic versioning.
* On the wiki listings, omit by default wiki pages that are associated with
check-ins and branches.
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 |
* Performance enhancement for the
[./checkin_names.wiki#root|"root:BRANCHNAME" style of tag],
accomplished using a Common Table Expression in the underlying SQL.
* Sort tag listings (command line and webpage) by taking numbers into
consideration so as to cater for tags that follow semantic versioning.
* On the wiki listings, omit by default wiki pages that are associated with
check-ins and branches.
* Add the new "[/help/describe|fossil describe]" command.
* Markdown subsystem extended with [../src/markdown.md#ftnts|footnotes support].
See corresponding [../test/markdown-test3.md|test cases],
[/wiki?name=branch/markdown-footnotes#il|known limitations] and
[forum:/forumthread/ee1f1597e46ec07a|discussion].
* Add the new special name "start:BRANCH" to refer to the first check-in of
the branch.
* Support [/wiki?name=branch/generated-tkt-mimetype&p|generated "mimetype"]
columns in the <var>TICKET</var> and <var>TICKETCHNG</var> tables.
* Fix [/timeline?r=fix_remote_url_overwrite_with_proxy|remote-url-overwrite]
bug where remote-url is overwritten by the proxy setting during sync
operation. Also require explicit "system" proxy setting to use
"http_proxy" environment variable.
* Reimplemented the [/pikchrshow] app to use a WebAssembly build of
pikchr so that it can render pikchrs on the client instead of requiring
a server round-trip.
* Add the [/help/email-listid|email-listid setting]. If set, it is
used as the List-ID header for all outbound notification emails.
* Add the "--branch" option to the "[/help/timeline|timeline]" command
to restrict the displayed items to a specific branch.
* Add the "--versions" option to "[/help/diff|fossil diff]"
to display details about the compared versions into the patch header.
* Numerous other minor enhancements.
<h2 id='v2_18'>Changes for version 2.18 (2022-02-23)</h2>
* Added support for [./ssl-server.md|SSL/TLS server mode] for commands
like "[/help/server|fossil server]" and "[/help/http|fossil http]"
* The new [/help/cherry-pick|cherry-pick command] is an alias for
[/help/merge|merge --cherrypick].
* Add new setting "[/help/large-file-size|large-file-size]". If the size
of any file in a commit exceeds this size, a warning is issued.
* Query parameter "year=YYYY" is now accepted by [/help/www/timeline|/timeline].
* The [/help/tar|tar] and [/help/zip|zip commands] no longer
sterilize the manifest file.
* Further improvement to diff alignment in cases that involve both
edits and indentation changes.
* [/doc/trunk/www/chat.md|Chat] improvements:<ul>
<li> [/help/www/chat|The /chat page] input options have been reworked
again for better cross-browser portability.
<li> When sending a [/help/www/chat|/chat] message fails, it is no longer
immediately lost and sending may optionally be retried.
<li> [/help/www/chat|/chat] can now optionally embed attachments of certain
types directly into message bodies via an iframe.
<li> Add the "--as FILENAME" option to the "[/help/chat|fossil chat send]"
command.
<li> Added the "[/help/chat|fossil chat pull]" command, available to
administrators only, for backing up the chat conversation.
</ul>
* Promote the test-detach command into the [/help/detach|detach command].
* For "[/help/pull|fossil pull]" with the --from-parent-project option,
if no URL is specified then use the last URL from the most recent prior
"fossil pull --from-parent-project".
* Add options --project-name and --project-desc to the
"[/help/init|fossil init]" command.
* The [/help/www/ext|/ext page] generates the SERVER_SOFTWARE environment
variable for clients.
* Fix the REQUEST_URI [/doc/trunk/www/aboutcgi.wiki#cgivar|CGI variable] such
that it includes the query string. This is how most other systems understand
REQUEST_URI.
* Added the --transport-command option to [/help/sync|fossil sync]
and similar.
<h2 id='v2_17'>Changes for version 2.17 (2021-10-09)</h2>
* Major improvements to the "diff" subsystem, including: <ul>
<li> Added new [/help/diff|formatting options]: --by, -b, --webpage, --json, --tcl.
<li> Partial-line matching for unified diffs
<li> Better partial-line matching for side-by-side diffs
<li> Buttons on web-based diffs to show more context
<li> Performance improvements
</ul>
* The --branchcolor option on [/help/commit|fossil commit] and
[/help/amend|fossil amend] can now take the value "auto" to
force Fossil to use its built-in automatic color choosing algorithm.
* Fossil now [./concepts.wiki#workflow|autosyncs] prior to running
[/help/open|fossil open].
* Add the [/help/ticket-default-report|ticket-default-report setting],
which if set to the title of a ticket report causes that ticket report
to be displayed below the search box in the /ticket page.
* The "nc" query parameter to the [/help/www/timeline|/timeline] page
causes all graph coloring to be omitted.
* Improvements and bug fixes to the new "[/help/ui|fossil ui REMOTE]"
feature so that it works better on a wider variety of platforms.
* In [/help/www/wikiedit|/wikiedit], show the list of attachments for
the current page and list URLs suitable for pasting them into the page.
* Add the --no-http-compression option to [/help/sync|fossil sync]
and similar.
* Print total payload bytes on a [/help/sync|fossil sync] when using
the --verbose option.
* Add the <tt>close</tt>, <tt>reopen</tt>, <tt>hide</tt>, and
</tt>unhide</tt> subcommands to [/help/branch|the branch command].
* The "-p" option to [/help/branch|fossil branch list] shows only
private branches.
* The [/md_rules|Markdown formatter] now interprets the content of
block HTML markup (such as <table>) in most cases. Only content
of <pre> and <script> is passed through verbatim.
* The [/help/wiki|wiki list command] no longer lists "deleted"
pages by default. Use the new <tt>--all</tt> option to include deleted
pages in the output.
* The [/help/all|fossil all git status] command only shows reports for
the subset of repositories that have a configured Git export.
* The [/help/www/chat|/chat] configuration was reimplemented and
provides new options, including the ability for a repository
administrator to
[./chat.md#notifications|extend the selection of notification sounds]
using unversioned files.
* Chat now uses fossil's full complement of markdown features,
instead of the prior small subset of markup it previously supported.
This retroactively applies to all chat messages, as they are
markdown-processed when they are sent instead of when they
are saved.
* Added a chat message preview mode so messages can be previewed
before being sent. Similarly, added a per-message ability to view
the raw un-parsed message text.
* The hotkey to activate preview mode in [/help/www/wikiedit|/wikiedit],
[/help/www/fileedit|/fileedit], and [/help/www/pikchrshow|/pikchrshow]
was changed from ctrl-enter to shift-enter in order to align with
[/help/www/chat|/chat]'s new preview feature and related future
changes.
<h2 id='v2_16'>Changes for Version 2.16 (2021-07-02)</h2>
* <b>Security:</b> Fix the client-side TLS so that it verifies that the
server hostname matches its certificate.
* The default "ssh" command on Windows is changed to "ssh" instead of the
legacy "plink", as ssh is now generally available on Windows systems.
Installations that still need to use the legacy "plink" can make that
happen by running: '<tt>fossil set ssh-command "plink -ssh" --global</tt>'.
* Added the [./patchcmd.md|fossil patch] command.
* The [/help/ui|fossil ui] command is enhanced in multiple ways:<ol>
<li> The REPOSITORY argument can be the name of a check-out directory.
<li> If the REPOSITORY argument is prefixed by "HOST:" or "USER@HOST:"
then the ui is run on the remote machine and tunnelled back to the local
machine using ssh. (The latest version of fossil must be installed on
both the local and the remote for this to work correctly.)
<li> The new --nobrowser and --fossilcmd options is provided.
</ol>
* The [/brlist|/brlist web page] allows the user to
select multiple branches to be displayed together in a single
timeline.
* The [./forum.wiki|Forum] provides a hyperlink on the author of each
post that goes to a timeline of recent posts by that same author.
* Added the "[/help/bisect|fossil bisect run]" command for improved
automation of bisects.
* The [/help/merge|fossil merge] command now does a better job merging
branches where files have been renamed between the current branch and the
branch being merged.
* The [/help/open|fossil open] command allows the repository file
to be inside the working directory without requiring the --force flag.
* The [/help/www/wikiedit|/wikiedit] and [/help/www/wikinew|/wikinew]
pages now default to markdown format.
* The [/help/www/login|/login] page now links to a user's forum post
timeline if the repository has forum posts.
* Tags may now be propagated for forum posts, wiki pages, and technotes.
The [/help/tag|tag command] can now manipulate and list such tags.
* [./caps/login-groups.md|Login-Groups] are now shown on the repository
list of the "[/help/all|fossil all ui]" command.
* Administrators can configure [./alerts.md|email alerts] to expire
a specific number of days (ex: 365) after the last user contact with
the Fossil server. This prevents alert emails being sent to
abandoned email accounts forever.
* SQL that defines [/tktsetup_tab|database objects for tickets] now
[/timeline?c=c717f1ef9a1a4c91|can DROP] a VIEW or an INDEX provided
that its name starts with '<code>ticket</code>' or '<code>fx_</code>'.
* Update the built-in SQLite to version 3.36.0.
* Numerous other minor enhancements.
<h2 id='v2_15'>Changes for Version 2.15 (2021-03-26) and Patch 2.15.1 on (2021-04-07)
and 2.15.2 on (2021-06-15)</h2>
* <b>Patch 2.15.2:</b> Fix the client-side TLS so that it verifies that the
server hostname matches its certificate. <b>Upgrading to
the patch is recommended.</b>
* <b>Patch 2.15.1:</b> Fix a data exfiltration bug in the server. <b>Upgrading to
the patch is recommended.</b>
* The [./defcsp.md|default CSP] has been relaxed slightly to allow
images to be loaded from any URL. All other resources are still
locked down by default.
* The built-in skins all use the "[/help/mainmenu|mainmenu]"
setting to determine the content of the main menu.
The ability to edit the
"mainmenu" setting is added on the /Admin/Configuration page.
* The hamburger menu is now available on most of the built-in skins.
* Any built-in skin named "X" can be used instead of the standard
repository skin by adding the URL parameter <tt>skin=X</tt> to the
request. The selection is persisted using the display
preferences cookie unless the "once" query parameter is also
included. The [/skins] page may be used to select a skin.
* The [/cookies] page now gives the user an opportunity to delete
individual cookies. And the /cookies page is linked from the
/sitemap, so that it appears in hamburger menus.
* The [/sitemap] extensions are now specified by a single new
"[/help/sitemap-extra|sitemap-extra setting]",
rather than a cluster of various
"sitemap-*" settings. The older settings are no longer used.
<b>This change might require minor server configuration
adjustments on servers that use /sitemap extensions.</b>
The /Admin/Configuration page provides the ability to edit
the new "sitemap-extra" setting.
* Added the "--ckout-alias NAME" option to
[/help/ui|fossil ui], [/help/server|fossil server], and
[/help/http|fossil http]. This option causes Fossil to
understand URIs of the form "/doc/NAME/..." as if they were
"[/help/www/doc|/doc/ckout/...]", to facilitate testing of
[./embeddeddoc.wiki|embedded documentation] changes prior to
check-in.
* For diff web pages, if the diff type (unified versus side-by-side)
is not specified by a query parameter, and if the
"[/help/preferred-diff-type|preferred-diff-type]"
setting is omitted or less than 1, then select the diff type based
on a guess of whether or not the request is coming from a mobile
device. Mobile gets unified and desktop gets side-by-side.
* The various pages which show diffs now have toggles to show/hide
individual diffs.
* Add the "[/help/preferred-diff-type|preferred-diff-type]"
setting to allow an admin to force a default diff type.
* The "pikchr-background" setting is now available in
"detail.txt" skin files, for better control of Pikchr
colors in inverted color schemes.
* Add the <tt>--list</tt> option to the
[/help/tarball|tarball],
[/help/zip|zip], and [/help/sqlar|sqlar]
commands.
* The javascript used to implement the hamburger menu on the
default built-in skin has been made generic so that it is usable
by a variety of skins, and promoted to an ordinary built-in
javascript file.
* New TH1 commands:
"[/doc/trunk/www/th1.md#bireqjs|builtin_request_js]",
"[/doc/trunk/www/th1.md#capexpr|capexpr]",
"foreach", "lappend", and "string match"
* The [/help/leaves|leaves command] now shows the branch point
of each leaf.
* The [/help/add|fossil add] command refuses to add files whose
names are reserved by Windows (ex: "aux") unless the --allow-reserved
option is included. This helps prevent Unix users from accidentally
creating check-ins that are unreadable by Windows users.
* Add the "re=" query parameter to the [/help/www/dir|/dir] webpage,
for symmetry with the [/help/www/tree|/tree] page.
* Update the built-in SQLite to version 3.35.0.
* The ./configure script now has the --print-minimum-sqlite-version option
that prints the minimum SQLite version required by the current version
of Fossil. This might be used by integrators who insist on building
Fossil to link against the system SQLite library rather than the
built-in copy of SQLite, to verify that their system SQLite library
is recent enough.
* Webpage that shows [/help/www/whistory|history of a wiki page]
gained client-side UI to help with comparison between two arbitrary
versions of a wiki (by the means of anchoring a "baseline" version)
and the ability to squeeze several sequential edits made by the same
user into a single "recycled" row (the latest edit in that sequence).
<h2 id='v2_14'>Changes for Version 2.14 (2021-01-20) and Patch 2.14.1 on (2021-04-07)
and 2.14.2 on (2021-06-15)</h2>
|
| ︙ | ︙ | |||
700 701 702 703 704 705 706 |
add content to a repository using Fossil 2.14 or later. No
action is needed on your part. However, if you upgrade to
version 2.14 and then later downgrade or otherwise use an earlier
version of Fossil, the email notification mechanism may fail
to send out notifications for some events, due to the missing
trigger. If you want to
permanently downgrade an installation, then you should run
| | | | | | | | | | | | 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 |
add content to a repository using Fossil 2.14 or later. No
action is needed on your part. However, if you upgrade to
version 2.14 and then later downgrade or otherwise use an earlier
version of Fossil, the email notification mechanism may fail
to send out notifications for some events, due to the missing
trigger. If you want to
permanently downgrade an installation, then you should run
"[/help/rebuild|fossil rebuild]" after the downgrade
to get email notifications working again. If you are not using
email notification, then the schema change will not affect you in
any way.
* <b>Schema Update Notice #2:</b>
This release changes how the descriptions of wiki edits are stored
in the EVENT table, for improved display on timelines. You must
run "[/help/rebuild|fossil rebuild]" to take advantage of
this enhancement. Everything will still work without
"fossil rebuild", except you will get goofy descriptions of
wiki updates in the timeline.
* Add support for [./chat.md|Fossil chat].
* The "[/help/clone|fossil clone]" command is enhanced so that
if the repository filename is omitted, an appropriate name is derived
from the remote URL and the newly cloned repo is opened. This makes
the clone command work more like Git, thus making it easier for
people transitioning from Git.
* Added the --mainbranch option to the [/help/git|fossil git export]
command.
* Added the --format option to the
"[/help/timeline|fossil timeline]" command.
* Enhance the --numstat option on the
"[/help/diff|fossil diff]" command so that it shows a total
number of lines added and deleted and total number of files
modified.
* Add the "contact" sub-command to [/help/user|fossil user].
* Added commands "[/help/all|fossil all git export]" and
"[/help/all|fossil all git status]".
* Added the "df=CHECKIN" query parameter to the
[/help/www/timeline|/timeline page].
* Improvements to the "[/sitemap]" page. Add subpages
[/sitemap-timeline] and [/sitemap-test].
* Better text position in cylinder objects of Pikchr diagrams.
* New "details.txt" settings available to custom skins to better control
the rendering of Pikchr diagrams:
<ul>
<li> pikchr-foreground
|
| ︙ | ︙ | |||
755 756 757 758 759 760 761 |
<h2 id='v2_13'>Changes for Version 2.13 (2020-11-01)</h2>
* Added support for [./interwiki.md|interwiki links].
* Enable <del> and <ins> markup in wiki.
* Improvements to the Forum threading display.
* Added support for embedding [./pikchr.md|pikchr]
markup in markdown and fossil-wiki content.
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 |
<h2 id='v2_13'>Changes for Version 2.13 (2020-11-01)</h2>
* Added support for [./interwiki.md|interwiki links].
* Enable <del> and <ins> markup in wiki.
* Improvements to the Forum threading display.
* Added support for embedding [./pikchr.md|pikchr]
markup in markdown and fossil-wiki content.
* The new "[/help/pikchr|pikchr]" command can render
pikchr scripts, optionally pre-processed with
[/doc/trunk/www/th1.md|TH1] blocks and variables exactly like
site skins are.
* The new [/help/www/pikchrshow|pikchrshow] page provides an
editor and previewer for pikchr markup.
* In [/help/www/wikiedit|/wikiedit] and
[/help/www/fileedit|/fileedit], Ctrl-Enter can now be used
initiate a preview and to toggle between the editor and preview
tabs.
* The <tt>/artifact</tt> and <tt>/file</tt> views, when in
line-number mode, now support interactive selection of a range
of lines to hyperlink to.
* Enhance the [/help/www/finfo|/finfo] webpage so that when query
parameters identify both a filename and a checkin, the resulting
graph tracks the identified file across renames.
* The built-in SQLite is updated to an alpha of version 3.34.0, and
the minimum SQLite version is increased to 3.34.0 because the
/finfo change in the previous bullet depends on enhancements to
recursive common table expressions that are only available in
SQLite 3.34.0 and later.
* Countless other minor refinements and documentation improvements.
<h2 id='v2_12'>Changes for Version 2.12.1 (2020-08-20)</h2>
* (2.12.1): Fix client-side vulnerabilities discovered by Max Justicz.
* Security fix in the "[/help/git|fossil git export]" command.
The same fix is also backported to version 2.10.1 and 2.11.1.
New "safety-net" features were added to prevent similar problems
in the future.
* Enhancements to the graph display for cases when there are
many cherry-pick merges into a single check-in.
[/timeline?f=2d75e87b760c0a9|Example]
* Enhance the [/help/open|fossil open] command with the new
--workdir option and the ability to accept a URL as the repository
name, causing the remote repository to be cloned automatically.
Do not allow "fossil open" to open in a non-empty working directory
unless the --keep option or the new --force option is used.
* Enhance the markdown formatter to more closely follow the
[https://spec.commonmark.org/0.29/#emphasis-and-strong-emphasis|CommonMark specification]
with regard to text highlighting.
Underscores in the middle of identifiers (ex: fossil_printf())
no longer need to be escaped.
* The markdown-to-html translator can prevent unsafe HTML
(for example: <script>) on user-contributed pages like forum and
tickets and wiki. The admin can adjust this behavior using
the [/help/safe-html|safe-html setting] on the Admin/Wiki page.
The default is to disallow unsafe HTML everywhere.
[https://fossil-scm.org/forum/forumpost/3714e6568f|Example].
* Added the "collapse" and "expand" capability for long forum posts.
[https://fossil-scm.org/forum/forumpost/9297029862|Example]
* The "[/help/remote-url|fossil remote]" command now has options for
specifying multiple persistent remotes with symbolic names. Currently
only one remote can be used at a time, but that might change in the
future.
* Add the "Remember me?" checkbox on the login page. Use a session
cookie for the login if it is not checked.
* Added the experimental "[/help/hook|fossil hook]" command for
managing "hook scripts" that run before checkin or after a push.
* Enhance the [/help/revert|fossil revert] command so that it
is able to revert all files beneath a directory.
* Add the [/help/bisect|fossil bisect skip] command.
* Add the [/help/backup|fossil backup] command.
* Enhance [/help/bisect|fossil bisect ui] so that it shows all unchecked
check-ins in between the innermost "good" and "bad" check-ins.
* Added the <tt>--reset</tt> flag to the "[/help/add|fossil add]",
"[/help/rm|fossil rm]", and
"[/help/addremove|fossil addremove]" commands.
* Added the "<tt>--min</tt> <i>N</i>" and "<tt>--logfile</tt> <i>FILENAME</i>"
flags to the [/help/backoffice|backoffice] command, as well as other
enhancements to make the backoffice command a viable replacement for
automatic backoffice. Other incremental backoffice improvements.
* Added the [/help/www/fileedit|/fileedit page], which allows
editing of text files online. Requires explicit activation by
a setup user.
* Translate built-in help text into HTML for display on web pages.
[/help/help|Example].
* On the [/help/www/timeline|/timeline] webpage, the combination
of query parameters "p=CHECKIN" and "bt=ANCESTOR" draws all
ancestors of CHECKIN going back to ANCESTOR. For example,
[/timeline?p=202006271506&bt=version-2.11] shows all ancestors
of the checkin that occurred on 2020-06-27 15:06 going back to
the 2.11 release.
* Update the built-in SQLite so that the
"[/help/sql|fossil sql]" command supports new output
modes ".mode box" and ".mode json".
* Add the "<tt>obscure()</tt>" SQL function to the
"[/help/sql|fossil sql]" command.
* Added virtual tables "<tt>helptext</tt>" and "<tt>builtin</tt>" to
the "[/help/sql|fossil sql]" command, providing access to the
dispatch table including all help text, and the builtin data files,
respectively.
* [./delta_format.wiki|Delta compression] is now applied to forum edits.
* The [/help/www/wikiedit|wiki editor] has been modernized and is
now Ajax-based. The WYSIWYG editing option for Fossil-format wiki
pages was removed. (Please let us know, via the site's Forum menu,
if that removal unduly impacts you.) This also changes the semantics
of the wiki "Sandbox": that pseudo-page may be freely edited but
no longer saved via the UI (the [/help/wiki|wiki CLI command]
can, though).
* The [/help/allow-symlinks|allow-symlinks setting] no longer
syncs. It must be activated individually on any clones which require
symlinks.
* Countless documentation enhancements.
<h2 id='v2_11'>Changes for Version 2.11 (2020-05-25)</h2>
* (2.11.2): Backport security fixes from 2.12.1
* (2.11.1): Backport security fix for the "fossil git export" command.
* Support [/md_rules|Markdown] in the default ticket configuration.
* Timestamp strings in [./checkin_names.wiki|object names]
can now omit punctation. So, for example, "202004181942" and
"2020-04-18 19:42" mean the same thing.
* Enhance backlink processing so that it works with Markdown-formatted
tickets and so that it works for wiki pages.
Ticket [a3572c6a5b47cd5a].
<ul><li> "[/help/rebuild|fossil rebuild]" is needed to
take full advantage of this fix. Fossil will continue
to work without the rebuild, but the new backlinks will be missing.</ul>
* The algorithm for finding the
[./tech_overview.wiki#configloc|location of the configuration database]
is enhanced to be XDG-compliant.
* Add a hide/show feature to
[./wikitheory.wiki#assocwiki|associated wiki] display on
check-in and branch information pages.
* Enhance the "[/help/info|fossil info]" command so that it
works with no arguments even if not within an open check-out.
* Many improvements to the forum and especially email notification
of forum posts, in response to community feedback after switching
SQLite support from a mailing list over to the forum.
* Minimum length of a self-registered user ID increased from 3 to 6
characters.
* When the "vfx" query parameter is used on the
"[/help/www/timeline|/timeline]" page, it causes the complete
text of forum posts to be displayed.
* Rework the "[/help/grep|fossil grep]" command to be more useful.
* Expose the [/help/redirect-to-https|redirect-to-https]
setting to the [/help/settings|settings] command.
* Improve support for CGI on IIS web servers.
* The [./serverext.wiki|/ext page] can now render index files,
in the same way as the embedded docs.
* Most commands now support the Unix-conventional "<tt>--</tt>"
flag to treat all following arguments as filenames
instead of flags.
* Added the [/help/mimetypes|mimetypes config setting]
(versionable) to enable mimetype overrides and custom definitions.
* Add an option on the /Admin/Timeline setup page to set a default
timeline style other than "Modern".
* In [./embeddeddoc.wiki|embedded documentation], hyperlink URLs
of the form "/doc/$CURRENT/..." the "$CURRENT" text is translated
into the check-in hash for the document currently being viewed.
* Added the [/help/www/phantoms|/phantoms] webpage that shows all
phantom artifacts.
* Enhancements to phantom processing to try to reduce
bandwidth-using chatter about phantoms on the sync protocol.
* Security: Fossil now assumes that the schema of every
database it opens has been tampered with by an adversary and takes
extra precautions to ensure that such tampering is harmless.
* Security: Fossil now puts the Content-Security-Policy in the
HTTP reply header, in addition to also leaving it in the
HTML <head> section, so that it is always available, even
if a custom skin overrides the HTML <head> and omits
the CSP in the process.
* Output of the [/help/diff|fossil diff -y] command automatically
adjusts according to the terminal width.
* The Content-Security-Policy is now set using the
[/help/default-csp|default-csp setting].
* Merge conflicts caused via the [/help/merge|merge] and
[/help/update|update] commands no longer leave temporary
files behind unless the new <tt>--keep-merge-files</tt> flag
is used.
* The [/help/www/artifact_stats|/artifact_stats page] is now accessible
to all users if the new "artifact_stats_enable" setting is turned
on. There is a new checkbox under the /Admin/Access menu to turn
that capability on and off.
* Add the [/help/tls-config|fossil tls-config] command for viewing
the TLS configuration and the list of SSL Cert exceptions.
* Captchas all include a button to read the captcha using an audio
file, so that they can be completed by the visually impaired.
* Stop using the IP address as part of the login cookie.
* Bug fix: fix the SSL cert validation logic so that if an exception
is allowed for particular site, the exception expires as soon as the
cert changes values.
|
| ︙ | ︙ | |||
955 956 957 958 959 960 961 | * Many minor enhancements to existing features. <h2 id='v2_10'>Changes for Version 2.10 (2019-10-04)</h2> * (2.10.2): backport security fixes from 2.12.1 * (2.10.1): backport security fix for the "fossil git export" command. * Added support for [./serverext.wiki|CGI-based Server Extensions]. | | | | | | | | | | | | | | | | | | 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 |
* Many minor enhancements to existing features.
<h2 id='v2_10'>Changes for Version 2.10 (2019-10-04)</h2>
* (2.10.2): backport security fixes from 2.12.1
* (2.10.1): backport security fix for the "fossil git export" command.
* Added support for [./serverext.wiki|CGI-based Server Extensions].
* Added the [/help/repolist-skin|repolist-skin] setting used to
add style to repository list pages.
* Enhance the hierarchical display of Forum threads to do less
indentation and to provide links back to the previous message
in the thread. Provide sequential numbers for all messages in
a forum thread.
* Add support for fenced code blocks and improved hyperlink
processing to the [/md_rules|markdown formatter].
* Add support for hyperlinks to wiki pages in the
[/md_rules|markdown formatter].
* Enhance the [/help/www/stat|/stat] page so that it gives the
option to show a breakdown of forum posts.
* The special check-in name "merge-in:BRANCH" means the source of
the most recent merge-in from the parent branch of BRANCH.
* Add hyperlinks to branch-diffs on the /info page and from
timelines of a branch.
* Add graphical context on the [/help/www/vdiff|/vdiff] page.
* Uppercase query parameters, POST parameters, and cookie names are
converted to all lowercase and entered into the parameter set,
instead of being discarded.
* Change the default [./hashpolicy.wiki|hash policy] to SHA3.
* Timeout [./server/any/cgi.md|CGI requests] after 300 seconds, or
some other value set by the
[./cgi.wiki#timeout|"timeout:" property] in the CGI script.
* The check-in lock interval is reduced from 24 hours to 60 seconds,
though the interval is now configurable using a setting.
An additional check for conflicts is added after interactive
check-in comment entry, to compensate for the reduced lock interval.
* Performance optimizations.
* Many documentation improvements.
<h2 id='v2_9'>Changes for Version 2.9 (2019-07-13)</h2>
* Added the [/help/git|fossil git export] command and instructions
for [./mirrortogithub.md|creating a GitHub mirror of a Fossil project].
* Improved handling of relative hyperlinks on the
[/help/www/artifact|/artifact] pages for wiki. For example,
hyperlinks and the lizard <img> now work correctly
for both [/artifact/2ff24ab0887cf522] and
[/doc/0d7ac90d575004c2415/www/index.wiki].
* Enhancements to the timeline graph layout, to show more information
with less clutter.
* Added tool-tips to the /timeline graph. On by default but can be
disabled by setting the "Tooltip dwell time" to 0 in the timeline
configuration.
* Copy buttons added to various check-in hash and branch name links.
* Double-clicking on a /timeline graph node now jumps to the /info page
for the check-in. So, repurpose the timestamp hyperlink to show all
activity around that check-in in time.
* Added the [/help/touch|fossil touch] command, and the --setmtime
option on the [/help/open|fossil open] and
[/help/update|fossil update] commands.
* Many documentation enhancements.
* For the "[/help/update|fossil update]" and
"[/help/checkout|fossil checkout]" commands, if a
managed file is removed because it is no longer part of the target
check-in and the directory containing the file is empty after the
file is removed and the directory is not the current working
directory and is not on the [/help/empty-dirs|empty-dirs]
list, then also remove the directory.
* Update internal Unicode character tables, used in regular expression
handling, from version 11.0 to 12.1.
* In "[/help/regexp|fossil regexp]", "[/help/grep|fossil grep]"
and the TH1 "regexp" command, the -nocase option now removes multiple
diacritics from the same character (derived from SQLite's
remove_diacritics=2)
* Added the [/help/www/secureraw|/secureraw] page that requires the
complete SHA1 or SHA3 hash, not just a prefix, before it will deliver
content.
* Accept purely numeric ISO8601 date/time strings as long as they
do not conflict with a hash. Example: "20190510134217" instead of
"2019-05-10 13:42:17". This helps keep URLs shorter and less
complicated
* Support both "1)" and "1." for numbered lists in markdown, as
commonmark does.
* The sync and clone HTTP requests omit the extra /xfer path element
from the end of the request URI. All servers since 2010 know that
the HTTP request is for a sync or clone from the mimetype so the
extra path element is not needed.
* If an automatic sync gets a permanent redirect request, then update
the saved remote URL to the new address.
* Temporary filenames (for example used for external "diff" commands)
try to preserve the suffix of the original file.
* Added the [/help/www/thisdayinhistory|/thisdayinhistory] web page.
* Enhanced parsing of [/help/www/timeline|/timeline] query parameters
"ymd=", "ym=", and "yw=". All arguments are option (in which case they
default to the current time) and all accept ISO8601 date/times without
punctuation.
* Automatically disapprove pending moderation requests for a user when
that user is deleted. This helps in dealing with spam-bots.
* Improvements to the "Capability Summary" section in the
[/help/www/secaudit0|Security Audit] web-page.
* Use new "ci-lock" and "ci-lock-failed" pragmas in the
[./sync.wiki|sync protocol] to try to prevent accident forks
caused by concurrent commits when operating in auto-sync mode.
* Fix a bug ([https://fossil-scm.org/forum/forumpost/c51b9a1169|details])
that can cause repository databases to be overwritten with debugging
output, thus corrupting the repository. This is only a factor when
CGI debugging is enabled, and even then is a rare occurrence, but it is
|
| ︙ | ︙ | |||
1151 1152 1153 1154 1155 1156 1157 |
* There is an optional "js" file for each skin that can be used to
hold javascript. This file can be loaded by reference or can be
included in the header or footer.
* Add the [./backoffice.md|backoffice].
* Update internal Unicode character tables, used in regular expression
handling, from version 10.0 to 11.0.
* Improvements to the "Security Audit" administration page
| | | 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 |
* There is an optional "js" file for each skin that can be used to
hold javascript. This file can be loaded by reference or can be
included in the header or footer.
* Add the [./backoffice.md|backoffice].
* Update internal Unicode character tables, used in regular expression
handling, from version 10.0 to 11.0.
* Improvements to the "Security Audit" administration page
* Add the [/help/branch|fossil branch current] command.
* Add the [./grep.md|grep] command.
* Update the built-in SQLite to version 3.25.1.
* Some code and interfaces are in place to support sending and
receiving email directly via SMTP, but this feature is not yet
complete or ready for production use.
* The `mv-rm-files` setting is now compiled into Fossil in the
default Fossil configuration; no longer must you say
|
| ︙ | ︙ | |||
1174 1175 1176 1177 1178 1179 1180 |
repository. This fix is the main reason for the current release.
* Added the new "Classic" timeline viewing mode. "Classic" is the
same as "Verbose" in the previous release. The "Verbose" mode is
now like "Compact" except the extra check-in details are shown by
default.
* Add support for ETags:, Last-Modified:, and If-Modified-Since:
cache control mechanisms.
| | | | | | | 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 |
repository. This fix is the main reason for the current release.
* Added the new "Classic" timeline viewing mode. "Classic" is the
same as "Verbose" in the previous release. The "Verbose" mode is
now like "Compact" except the extra check-in details are shown by
default.
* Add support for ETags:, Last-Modified:, and If-Modified-Since:
cache control mechanisms.
* Enhance the [/help/www/tarball|/tarball],
[/help/www/zip|/zip], and
[/help/www/sqlar|/sqlar] pages so that the checkin
name to be downloaded can be expressed as part of the URI,
and without the need for query parameters.
* On the [/help/www/timeline|/timeline] webpage, add the days=N
query parameter and enhance the ymd=DATE and yw=DATE query parameters
to accept 'now' as an argument to show the latest day or week.
* In the web page that comes up in response to the
[/help/all|fossil all ui] command, show the last modification
time for each repository, and allow click-to-sort on the modification
time column.
* In the tarball cache replacement algorithm, give extra weight to
tarballs that have been accessed more than once.
* Additional defenses against web-based attacks. There have not been
any known vulnerabilities. We are just being paranoid.
* Update the built-in SQLite to an alpha version of 3.24.0.
|
| ︙ | ︙ | |||
1234 1235 1236 1237 1238 1239 1240 |
* New feature: URL Aliases. URL Aliases allow an administrator
to define their own URLs on the web interface that are rewritten to
built-in URLs with specific parameters. Create and configure URL Aliases
using the /Setup/URL_Aliases menu option in the web interface.
* Add tech-note search capability.
* Add the -r|--revision and -o|--origin options to the
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 |
* New feature: URL Aliases. URL Aliases allow an administrator
to define their own URLs on the web interface that are rewritten to
built-in URLs with specific parameters. Create and configure URL Aliases
using the /Setup/URL_Aliases menu option in the web interface.
* Add tech-note search capability.
* Add the -r|--revision and -o|--origin options to the
[/help/annotate|annotate] command.
* Add the origin= query parameter to the [/help/www/annotate|/annotate]
webpage.
* The [/help/annotate|fossil annotate] command and the
[/help/www/annotate|/annotate] web page go backwards in time as far
as can be computed in 30 milliseconds by default, rather than stopping
after 20 steps. The new limit= query parameter or the --limit command-line
option can be used to alter this timeout.
* Provide separate [/help#settings|on-line help screens for each setting].
* Back out support for the --no-dir-symlinks option
* Remove support from the legacy configuration sync protocol. The only
way now to do a configuration push or pull is to use the new protocol that
was added in 2011.
* Add the from= and to= query parameters to [/help/www/fdiff|/fdiff]
in order to get a diff of two files in the same check-in.
* Fix the "ssh://" protocol to prevent an attack whereby the attacker convinces
a victim to run a "clone" with a dodgy URL and thereby gains access to their
system.
* Provide a checkbox that will temporarily disable all ad-units.
* Improvements to the [/help/www/stat|/stat] page
* Various new hyperlinks to the [/help/www/bloblist|/bloblist]
and [/help/www/bigbloblist|/bigbloblist] pages.
* Correct the [/help/www/doc|/doc] page to support read-only repositories.
* Correct [/help/www/zip|/zip], [/help/www/tarball|/tarball],
[/help/zip|zip], and [/help/tarball|tarball] pages and commands to
honor the versioned manifest setting when outside of an open checkout
directory.
* The admin-log and access-log settings are now on by default for
new repositories.
* Update the built-in SQLite to version 3.21.0.
<h2 id='v2_3'>Changes for Version 2.3 (2017-07-21)</h2>
* Update the built-in SQLite to version 3.20.0 (beta).
* Update internal Unicode character tables, used in regular expression
handling, from version 9.0 to 10.0.
* Show the last-sync-URL on the [/help/www/urllist|/urllist] page.
* Added the "Event Summary" activity report.
[/reports?type=ci&view=lastchng|example]
* Added the "Security Audit" page, available to administrators only
* Added the Last Login time to the user list page, for administrators only
* Added the --numstat option to the [/help/diff|fossil diff] command
* Limit the size of the heap and stack on unix systems, as a proactive
defense against the
[https://www.qualys.com/2017/06/19/stack-clash/stack-clash.txt|Stack Clash]
attack.
* Fix "database locked" warnings caused by "PRAGMA optimize".
* Fix a potential XSS vulnerability on the
[/help/www/help|/help] webpage.
* Documentation updates
<h2 id='v2_2'>Changes for Version 2.2 (2017-04-11)</h2>
* GIT comment tags are now handled by Fossil during import/export.
* Show the content of README files on directory listings.
([/file/skins|example])
* Support for Basic Authentication if enabled (default off).
* Show the hash algorithms used on the
[/help/www/rcvfromlist|/rcvfromlist] page.
* The [/help/www/tarball|/tarball] and [/help/www/zip|/zip] pages
now use the the r= query parameter
to select which check-in to deliver. The uuid= query parameter
is still accepted for backwards compatibility.
* Update the built-in SQLite to version 3.18.0.
* Run "[https://www.sqlite.org/pragma.html#pragma_optimize|PRAGMA optimize]"
on the database connection as it is closing.
<h2 id='v2_1'>Changes for Version 2.1 (2017-03-10)</h2>
* Add support for [./hashpolicy.wiki|hash policies] that control which
of the Hardened-SHA1 or SHA3-256 algorithms is used to name new
artifacts.
* Add the "gshow" and "gcat" subcommands to [/help/stash|fossil stash].
* Add the [/help/www/juvlist|/juvlist] web page and use it to construct
the [/uv/download.html|Download Page] of the Fossil self-hosting website
using Ajax.
<h2 id='v2_0'>Changes for Version 2.0 (2017-03-03)</h2>
* Use the
[https://github.com/cr-marcstevens/sha1collisiondetection|hardened SHA1]
implementation by Marc Stevens and Dan Shumow.
* Add the ability to read and understand
[./fileformat.wiki#names|artifact names] that are based on SHA3-256
rather than SHA1, but do not actually generate any such names.
* Added the [/help/sha3sum|sha3sum] command.
* Update the built-in SQLite to version 3.17.0.
<h2 id='v1_37'>Changes for Version 1.37 (2017-01-16)</h2>
* Add checkbox widgets to various web pages. See [/technote/8d18bf27e9|
this technote] for more information. To get the checkboxes to look as
intended, you must update the CSS in your repository and all clones.
* Add the [/help/all|fossil all ui] command
* Add the [/help/www/file|/file] webpage
* Enhance the [/help/www/brlist|/brlist] webpage to make use of branch colors.
* Add support for the ms=EXACT|LIKE|GLOB|REGEXP query parameter on the
[/help/www/timeline|/timeline] webpage, with associated form widgets.
* Enhance the [/help/changes|changes] and [/help/status|status] commands
with many new filter options so that specific kinds of changes can be
found without having to pipe through grep or sed.
* Enhanced the [/help/sqlite3|fossil sql] command so that it opens the
[./tech_overview.wiki#localdb|checkout database] and the
[./tech_overview.wiki#configdb|configuration database] in addition to the
repository database.
* TH1 enhancements:
<ul><li>Add <nowiki>[unversioned content]</nowiki> command.</li>
<li>Add <nowiki>[unversioned list]</nowiki> command.</li>
<li>Add project_description variable.</li>
</ul>
* Rename crnl-glob [/help/settings|setting] to crlf-glob, but keep
crnl-glob as a compatibility alias.
* Added the --command option to the [/help/diff|diff] command.
* Fix a C99-ism that prevents the 1.36 release from building with MSVC.
* Fix [/help/ticket|ticket set] when using the "+" prefix with fields
from the "ticketchng" table.
* Remove the "fusefs" command from builds that do not have the underlying
support enabled.
* Fixes for incremental git import/export.
* Minor security enhancements to
[./encryptedrepos.wiki|encrypted repositories].
* Update the built-in SQLite to version 3.16.2.
* Update the built-in Zlib to version 1.2.11.
<h2 id='v1_36'>Changes for Version 1.36 (2016-10-24)</h2>
* Add support for [./unvers.wiki|unversioned content],
the [/help/unversioned|fossil unversioned] command and the
[/help/www/uv|/uv] and [/uvlist] web pages.
* The [/uv/download.html|download page] is moved into
[./unvers.wiki|unversioned content] so that the self-hosting Fossil
websites no longer uses any external content.
* Added the "Search" button to the graphical diff generated by
the --tk option on the [/help/diff|diff] command.
* Added the "--checkin VERSION" option to the
[/help/diff|diff] command.
* Various performance enhancements to the [/help/diff|diff] command.
* Update internal Unicode character tables, used in regular expression
handling, from version 8.0 to 9.0.
* Update the built-in SQLite to version 3.15. Fossil now requires
the SQLITE_DBCONFIG_MAINDBNAME interface of SQLite which is only available
in SQLite version 3.15 and later and so Fossil will not work with
earlier SQLite versions.
* Fix [https://www.mail-archive.com/fossil-users@lists.fossil-scm.org/msg23618.html|multi-line timeline bug]
* Enhance the [/help/purge|fossil purge] command.
* New command [/help/shell|fossil shell].
* SQL parameters whose names are all lower-case in Ticket Report SQL
queries are filled in using HTTP query parameter values.
* Added support for [./childprojects.wiki|child projects] that are
able to pull from their parent but not push.
* Added the -nocomplain option to the TH1 "query" command.
* Added support for the chng=GLOBLIST query parameter on the
[/help/www/timeline|/timeline] webpage.
<h2 id='v1_35'>Changes for Version 1.35 (2016-06-14)</h2>
* Enable symlinks by default on all non-Windows platforms.
* Enhance the [/md_rules|Markdown formatting] so that hyperlinks that begin
with "/" are relative to the root of the Fossil repository.
* Rework the [/help/www/setup_ulist|/setup_list page] (the User List page)
to display all users in a click-to-sort table.
* Fix backslash-octal escape on filenames while importing from git
* When markdown documents begin with <h1> HTML elements, use that
header at the document title.
* Added the [/help/www/bigbloblist|/bigbloblist page].
* Enhance the [/help/www/finfo|/finfo page] so that when it is showing
the ancestors of a particular file version, it only shows direct
ancestors and omits changes on branches, thus making it show the same set
of ancestors that are used for [/help/www/blame|/blame].
* Added the --page option to the [/help/ui|fossil ui] command
* Added the [/help/bisect|fossil bisect ui] command
* Enhanced the [/help/diff|fossil diff] command so that it accepts
directory names as arguments and computes diffs on all files contained
within those directories.
* Fix the [/help/add|fossil add] command so that it shows "SKIP" for
files added that were already under management.
* TH1 enhancements:
<ul><li>Add <nowiki>[array exists]</nowiki> command.</li>
<li>Add minimal <nowiki>[array names]</nowiki> command.</li>
<li>Add tcl_platform(engine) and tcl_platform(platform) array
elements.</li>
</ul>
* Get autosetup working with MinGW.
* Fix autosetup detection of zlib in the source tree.
* Added autosetup detection of OpenSSL when it may be present under the
"compat" subdirectory of the source tree.
* Added the [/help/reparent|fossil reparent] command
* Added --include and --exclude options to [/help/tarball|fossil tarball]
and [/help/zip|fossil zip] and the in= and ex= query parameters to the
[/help/www/tarball|/tarball] and [/help/www/zip|/zip] web pages.
* Add support for [./encryptedrepos.wiki|encrypted Fossil repositories].
* If the FOSSIL_PWREADER environment variable is set, then use the program it
names in place of getpass() to read passwords and passphrases
* Option --baseurl now works on Windows.
* Numerous documentation improvements.
* Update the built-in SQLite to version 3.13.0.
<h2 id='v1_34'>Changes for Version 1.34 (2015-11-02)</h2>
* Make the [/help/clean|fossil clean] command undoable for files less
than 10MiB.
* Update internal Unicode character tables, used in regular expression
handling, from version 7.0 to 8.0.
* Add the new [/help/amend|amend] command which is used to modify
tags of a "check-in".
* Fix bug in [/help/import|import] command, handling version 3 of
the svndump format for subversion.
* Add the [/help/all|all cache] command.
* TH1 enhancements:
<ul><li>Add minimal <nowiki>[lsearch]</nowiki> command. Only exact
case-sensitive matching is supported.</li>
<li>Add the <nowiki>[glob_match]</nowiki>, <nowiki>[markdown]</nowiki>,
<nowiki>[dir]</nowiki>, and <nowiki>[encode64]</nowiki> commands.</li>
<li>Add the <nowiki>[tclIsSafe] and [tclMakeSafe]</nowiki> commands to
the Tcl integration subsystem.</li>
<li>Add 'double', 'integer', and 'list' classes to the
<nowiki>[string is]</nowiki> command.</li>
</ul>
* Add the --undo option to the [/help/diff|diff] command.
* Build-in Antirez's "linenoise" command-line editing library for use with
the [/help/sqlite3|fossil sql] command on Unix platforms.
* Add [/help/stash|stash cat] as an alias for the
[/help/stash|stash show] command.
* Automatically pull before [/help/merge|fossil merge] when auto-sync
is enabled.
* Fix --hard option to [/help/mv|fossil mv] and [/help/rm|fossil rm]
to enable them to work properly with certain relative paths.
* Change the mimetype for ".n" and ".man" files to text/plain.
* Display improvements in the [/help/bisect|fossil bisect chart] command.
* Updated the built-in SQLite to version 3.9.1 and activated JSON1 and FTS5
support (both currently unused within Fossil).
<h2 id='v1_33'>Changes for Version 1.33 (2015-05-23)</h2>
* Improved fork detection on [/help/update|fossil update],
[/help/status|fossil status] and related commands.
* Change the default skin to what used to be called "San Francisco Modern".
* Add the [/repo-tabsize] web page
* Add [/help/import|fossil import --svn], for importing a subversion
repository into fossil which was exported using "svnadmin dump".
* Add the "--compress-only" option to [/help/rebuild|fossil rebuild].
* Use a pie chart on the [/reports?view=byuser] page.
* Enhanced [/help/clean|fossil clean --verily] so that it ignores
keep-glob and ignore-glob settings. Added the -x alias for --verily.
* Add the --soft and --hard options to [/help/rm|fossil rm] and
[/help/mv|fossil mv]. The default is still --soft, but that is
now configurable at compile-time or by the mv-rm-files setting.
* Improved ability to [./customgraph.md|customize the timeline graph].
* Improvements to the [/sitemap] page.
* Automatically adjust the [/help/timeline|CLI timeline] to the terminal
width on Linux.
* Added <nowiki>[info commands] and [info vars]</nowiki> commands to TH1.
These commands perform the same function as their Tcl counterparts,
except they do not accept a pattern argument.
* Fix some obscure issues with TH1 expression processing.
* Fix titles in search results for documents that are not wiki, markdown,
or HTML.
* Formally translate TH1 to Tcl return codes and vice-versa, where
necessary, in the Tcl integration subsystem.
* Add [/help/leaves|fossil leaves -multiple], for finding multiple
leaves on the same branch.
* Added the "Blitz" skin option.
* Removed the ".fossil-settings/keep-glob" file. It should not have been
checked into the repository.
* Update the built-in SQLite to version 3.8.10.2.
* Make [/help/open|fossil open] honor ".fossil-settings/allow-symlinks".
* Allow [/help/add|fossil add] to be used on symlinks to nonexistent or
unreadable files in the same way as [/help/addremove|fossil addremove].
* Added fork warning to be issued if sync produced a fork
* Update the [/help/www/info|info] page to report when a file becomes a
symlink. Additionally show the UUID for files whose types have changed
without changing contents or symlink target.
* Have [/help/changes|fossil changes] and
[/help/status|fossil status] report when executable or symlink status
changes on otherwise unmodified files.
* Permit filtering weekday and file [/help/www/reports|reports] by user.
Also ensure the user parameter is preserved when changing types. Add a
field for direct entry of the user name to each applicable report.
* Create parent directories of [/help/settings|empty-dirs] if they don't
already exist.
* Inhibit timeline links to wiki pages that have been deleted.
<h2 id='v1_33'>Changes for Version 1.32 (2015-03-14)</h2>
* When creating a new repository using [/help/init|fossil init], ensure
that the new repository is fully compatible with historical versions of
Fossil by having a valid manifest as RID 1.
* Anti-aliased rendering of arrowheads on timeline graphs.
* Added vi/less-style key bindings to the --tk diff GUI.
* Documentation updates to fix spellings and changes all "checkins" to
"check-ins".
* Add the --repolist option to server commands such as
[/help/server|fossil server] or [/help/http|fossil http].
* Added the "Xekri" skin.
* Enhance the "ln=" query parameter on artifact displays to accept multiple
ranges, separate by spaces (or "+" when URL-encoded).
* Added [/help/forget|fossil forget] as an alias for
[/help/rm|fossil rm].
<h2 id='v1_31'>Changes For Version 1.31 (2015-02-23)</h2>
* Change the auxiliary schema by adding columns MLINK.ISAUX and MLINK.PMID
columns to the schema, to support better drawing of file change graphs.
A [/help/rebuild|fossil rebuild] is recommended but is not required.
so that the new graph drawing logic can work effectively.
* Added [/search|search] over Check-in comments, Documents, Tickets and
Wiki. Disabled by default. The search can be either a full-scan or it
can use an index that is kept up-to-date automatically. The new
/srchsetup web-page and the [/help/fts-config|fts-config] command
were added to help configure the search capability. Expect further
enhancements to the search capabilities in subsequent releases.
* Added form elements to some submenus (in particular the /timeline)
for easier operation.
* Added the --ifneeded option to [/help/rebuild|fossil rebuild].
* Added "override skins" using the "skin:" line of the CGI script or
using the --skin LABEL option on the [/help/server|server],
[/help/ui|ui], or [/help/http|http] commands.
* Embedded html documents that begin with
<doc class="fossil-doc"> are displayed with standard
headers and footers added.
* Allow <div style='...'> markup in [/wiki_rules|wiki].
* Renamed "Events" to "Technical Notes", while updating the technote
display and control pages. Add support for technotes as plain text
or as Markdown.
* Added the [/md_rules] pages containing summary instructions on the
Markdown format.
* Added the --repolist and --nojail options to the various server commands
(ex: [/help/server|fossil server]).
* Added the [/help/all|fossil all add] subcommand to "fossil all".
* Improvements to the /login page. Some hyperlinks to pages that require
"anonymous" privileges are displayed even if the current user is "nobody"
but automatically redirect to /login.
* The [/help/www/doc|/doc] web-page will now try to deliver the file
"404.md" from the top-level directory (if such a file exists) in
place of its built-in 404 text.
* Download of Tarballs and ZIP Archives by user "nobody" is now enabled
by default in new repositories.
* Enhancements to the table sorting controls. More display tables
are now sortable.
* Add IPv6 support to [/help/sync|fossil sync] and
[/help/clone|fossil clone]
* Add more skins such as "San Francisco Modern" and "Eagle".
* During shutdown, check to see if the check-out database (".fslckout")
contains a lot of free space, and if it does, VACUUM it.
* Added the [/mimetype_list] page.
* Added the [/hash-collisions] page.
* Allow the user of Common Table Expressions in the SQL that defaults
ticket reports.
* Break out the components (css, footer, and header) for the
various built-in skins into separate files in the source tree.
<h2 id='v1_30'>Changes For Version 1.30 (2015-01-19)</h2>
* Added the [/help/bundle|fossil bundle] command.
* Added the [/help/purge|fossil purge] command.
* Added the [/help/publish|fossil publish] command.
* Added the [/help/unpublished|fossil unpublished] command.
* Enhance the [/tree] webpage to show the age of each file with the option
to sort by age.
* Enhance the [/brlist] webpage to show additional information about each branch
and to be sortable by clicking on column headers.
* Add support for Docker. Just install docker and type
"sudo docker run -d -p 8080:8080 nijtmans/fossil" to get it running.
* Add the [/help/fusefs|fossil fusefs DIRECTORY] command that mounts a
Fuse Filesystem at the given DIRECTORY and populates it with read-only
copies of all historical check-ins. This only works on systems that
support FuseFS.
* Add the administrative log that records all configuration.
* Added the [/sitemap] webpage.
* Added the [/bloblist] web page.
* Let [/help/new|fossil new] no longer create an initial empty commit
by default. The first commit after checking out an empty repository will
become the initial commit.
* Added the [/help/all|fossil all dbstat] and
[/help/all|fossil all info] commands.
* Update SQLite to version 3.8.8.
* Added the --verily option to the [/help/clean|fossil clean] command.
* Add the "autosync-tries" setting to control the number of autosync attempts
before returning an error.
* Added a compile-time option (--with-miniz) to build using miniz instead
of zlib. Disabled by default.
* Support customization of commands and webpages, including the ability to
add new ones, via the "TH1 hooks" feature. Disabled by default. Enabled
via a compile-time option.
* Add the <nowiki>[checkout], [render], [styleHeader], [styleFooter],
[trace], [getParameter], [setParameter], [artifact], and
[globalState]</nowiki> commands to TH1, primarily for use by TH1 hooks.
* Automatically adjust the width of command-line timeline output according to the
detected width of the terminal.
* Prompt the user to optionally fix invalid UTF-8 at check-in.
* Added a line-number toggle option to the [/help/www/info|/info]
and [/help/www/artifact|/artifact] pages.
* Most commands now issue errors rather than silently ignoring unrecognized
command-line options.
* Use full 40-character SHA1 hashes (instead of abbreviations) in most
internal URLs.
* The "ssh:" sync method on Windows now uses "plink.exe" instead of "ssh" as
the secure-shell client program.
* Prevent a partial clone when the connection is lost.
* Make the distinction between 301 and 302 redirects.
* Allow commits against a closed check-in as long as the commit goes onto
a different branch.
* Improved cache control in the web interface reduces unnecessary requests
for common resources like the page logo and CSS.
* Fix a rare and long-standing sync protocol bug
that would silently prevent the sync from running to completion. Before
this bug-fix it was sometimes necessary to do "fossil sync --verily" to
get two repositories in sync.
* Add the [/finfo?name=src/foci.c|files_of_checkin] virtual table - useful
for ad hoc queries in the [/help/sqlite3|fossil sql] interface,
and also used internally.
* Added the "$secureurl" TH1 variable for use in headers and footers.
* (Internal:) Add the ability to include resources as separate files in the
source tree that are converted into constant byte arrays in the compiled
binary. Use this feature to store the Tk script that implements the --tk
diff option in a separate file for easier editing.
* (Internal:) Implement a system of compile-time checks to help ensure
|
| ︙ | ︙ | |||
1664 1665 1666 1667 1668 1669 1670 |
to the graphical diff display that results
from using the --tk option with the [/help/diff | fossil diff] command.
* The [/reports] page now requires Read ("o") permissions. The "byweek"
report now properly propagates the selected year through the event type
filter links.
* The [/help/info | info command] now shows leaf status of the checkout.
* Add support for tunneling https through a http proxy (Ticket [e854101c4f]).
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 |
to the graphical diff display that results
from using the --tk option with the [/help/diff | fossil diff] command.
* The [/reports] page now requires Read ("o") permissions. The "byweek"
report now properly propagates the selected year through the event type
filter links.
* The [/help/info | info command] now shows leaf status of the checkout.
* Add support for tunneling https through a http proxy (Ticket [e854101c4f]).
* Add option --empty to the "[/help/open | fossil open]" command.
* Enhanced [/help/www/fileage|the fileage page] to support a glob parameter.
* Add -w|--ignore-all-space and -Z|--ignore-trailing-space options to
[/help/annotate|fossil annotate], [/help/blame|fossil blame],
[/help/diff|fossil (g)diff], [/help/stash|fossil stash diff].
* Add --strip-trailing-cr option to [/help/diff|fossil (g)diff] and
[/help/stash|fossil stash diff].
* Add button "Ignore Whitespace" to /annotate, /blame, /ci, /fdiff
and /vdiff UI pages.
* Enhance [/reports?view=byweekday|/reports] with a "byweekday" view.
* Enhance the [/help/cat|fossil cat] command so that it works outside
of a checkout when using the -R command-line option.
* Use full-length SHA1 hashes, not abbreviations, in most hyperlinks.
* Correctly render the <title> markup on wiki pages in the
[/help/www/artifact|/artifact] webpage.
* Enhance the [/help/whatis|fossil whatis] command to report on attachments
and cluster artifacts. Added the [/help/test-whatis-all] command for
testing purposes.
* Add support for HTTP Basic Authentication on [/help/clone|clone] and
[/help/sync|sync].
* Fix the [/help/stash|stash] so that it remembers added files and re-adds
them when the stash is applied.
* Fix the server so that it avoids writing to the database (and thus avoids
database locking issues) on a
[/help/pull|pull] or [/help/clone|clone].
* Add support for [./server.wiki#loadmgmt|server load management] using both
a cache of expensive pages (the [/help/cache|fossil cache] command)
and by rejecting expensive page requests when the server load average is too
high.
* Add the [/help/praise|fossil praise] command as an alias for
[/help/blame|fossil blame] for subversion compatibility.
* Enhance the [/help/test-diff|fossil test-diff] command with -y or --tk
options so that it shows both filenames above their respective columns in
the side-by-side diff output.
* Issue a warning if a [/help/add|fossil add] command tries to add a file
that matches the ignore-glob.
* Add option -W|--width to "[/help/stash|fossil stash ls]"
and "[/help/leaves|fossil leaves]" commands.
* Enhance support for running as the root user. Now works on Haiku.
* Added the <tt>-empty</tt> option to [/help/new|fossil new], which
causes it to not create an initial empty commit. The first commit after
checking out a repo created this way will become the initial commit.
* Enhance sync operations by committing each round-trip to minimize number
of retransmits when autosync fails. Include option for
[/help/update| fossil update] and [/help/merge| fossil merge] to
continue even if missing content.
* Minor portability fixes for platforms where the char type is unsigned
by default.
<h2>Changes For Version 1.28 (2014-01-27)</h2>
* Enhance [/help/www/reports | /reports] to support event type filtering.
* When cloning a repository, the user name passed via the URL (if any)
is now used as the default local admin user's name.
* Enhance the SSH transport mechanism so that it runs a single instance of
the "fossil" executable on the remote side, obviating the need for a shell
on the remote side. Some users may need to add the "?fossil=/path/to/fossil"
query parameter to "ssh:" URIs if their fossil binary is not in a standard
place.
* Add the "[/help/blame | fossil blame]" command that works just like
"fossil annotate" but uses a different output format that includes the
user who made each change and omits line numbers.
* Add the "Tarball and ZIP-archive Prefix" configuration parameter under
Admin/Configuration.
* Fix CGI processing so that it works on web servers that do not
supply REQUEST_URI.
* Add options --dirsonly, --emptydirs, and --allckouts to the
"[/help/clean | fossil clean]" command.
* Ten-fold performance improvement in large "fossil blame" or
"fossil annotate" commands.
* Add option -W|--width and --offset to "[/help/timeline | fossil timeline]"
and "[/help/finfo | fossil finfo]" commands.
* Option -n|--limit of "[/help/timeline | fossil timeline]" now
specifies the number of entries, just like all other commands which
have the -n|--limit option. The various timeline-related functions
now output "--- ?? limit (??) reached ---" at the end whenever
appropriate. Use "-n 0" if no limit is desired.
* Fix handling of password embedded in Fossil URL.
* New <tt>--once</tt> option to [/help/clone | fossil clone] command
which does not store the URL or password when cloning.
* Modify [/help/ui | fossil ui] to respect "default user" in an open
repository.
* Fossil now hides check-ins that have the "hidden" tag in timeline webpages.
* Enhance <tt>/ci_edit</tt> page to add the "hidden" tag to check-ins.
* Advanced possibilities for commit and ticket change notifications over
http using TH1 scripting.
* Add --sha1sum and --integrate options
to the "[/help/commit | fossil commit]" command.
* Add the "clean" and "extra" subcommands to the
"[/help/all | fossil all]" command
* Add the --whatif option to "[/help/clean|fossil clean]" that works the
same as "--dry-run",
so that the name does not collide with the --dry-run option of "fossil all".
* Provide a configuration option to show dates on the web timeline
as "YYMMMDD HH:MM"
* Add an option to the "stats" webpage that allows an administrator to see
the current repository schema.
* Enhancements to the "[/help/www/vdiff|/vdiff]" webpage for more difference
display options.
* Added the "[/tree?ci=trunk&expand | /tree]" webpage as an alternative
to "/dir" and make it the default way of showing file lists.
* Send gzipped HTTP responses to clients that support it.
<h2>Changes For Version 1.27 (2013-09-11)</h2>
* Enhance the [/help/changes | fossil changes],
[/help/clean | fossil clean], [/help/extras | fossil extras],
[/help/ls | fossil ls] and [/help/status | fossil status] commands
to restrict operation to files and directories named on the command-line.
* New --integrate option to [/help/merge | fossil merge], which
automatically closes the merged branch when committing.
* Renamed <tt>/stats_report</tt> page to [/reports]. Graph width is now
relative, not absolute.
* Added <tt>yw=YYYY-WW</tt> (year-week) filter to timeline to limit the results
to a specific year and calendar week number, e.g. [/timeline?yw=2013-01].
* Updates to SQLite to prevent opening a repository file using file descriptors
1 or 2 on Unix. This fixes a bug under which an assertion failure could
overwrite part of a repository database file, corrupting it.
* Added support for unlimited line lengths in side-by-side diffs.
* New --close option to [/help/commit | fossil commit], which
immediately closes the branch being committed.
* Added <tt>chart</tt> option to [/help/bisect | fossil bisect].
* Improvements to the "human or bot?" determination.
* Reports errors about missing CGI-standard environment variables for HTTP
servers which do not support them.
* Minor improvements to sync support on Windows.
* Added <tt>--scgi</tt> option to [/help/server | fossil server].
* Internal improvements to the sync process.
* The internals of the JSON API are now MIT-licensed, so downstream
users/packagers are no longer affected by the "do no evil" license
clause.
<h2>Changes For Version 1.26 (2013-06-18)</h2>
* The argument to the --port option for the [/help/ui | fossil ui] and
[/help/server | fossil server] commands can take an IP address in addition
to the port number, causing Fossil to bind to just that one IP address.
* After prompting for a password, also ask if that password should be
remembered.
* Performance improvements to the diff engine.
* Fix the side-by-side diff engine to work better with multi-byte Unicode text.
* Color-coding in the web-based annotation (blame) display. Fix the annotation
engine so that it is no longer confused by time-warps.
* The markdown formatter is now available by default and can be used for
tickets, wiki, and embedded documentation.
* Add subcommands "fossil bisect log" and "fossil bisect status" to the
[/help/bisect | fossil bisect] command, as well as other bisect enhancements.
* Enhanced defenses that prevent spiders from using excessive CPU and bandwidth.
* Consistent use of the -n or --dry-run command line options.
* Win32: Fossil now understands Cygwin paths containing one or more of
the characters <nowiki>"*:<>?|</nowiki>. Those are normally forbidden in
win32. This means that the win32 fossil.exe is better usable in a Cygwin
environment. See
[http://cygwin.com/cygwin-ug-net/using-specialnames.html#pathnames-specialchars].
|
| ︙ | ︙ |
Changes to www/chat.md.
| ︙ | ︙ | |||
53 54 55 56 57 58 59 | cases those settings can be ignored. The settings control things like the amount of time that chat messages are retained before being purged from the repository database. ## <a id="usage"></a>Usage For users with appropriate permissions, simply browse to the | | | 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | cases those settings can be ignored. The settings control things like the amount of time that chat messages are retained before being purged from the repository database. ## <a id="usage"></a>Usage For users with appropriate permissions, simply browse to the [/chat](/help/www/chat) to start up a chat session. The default skin includes a "Chat" entry on the menu bar on wide screens for people with chat privilege. There is also a "Chat" option on the [Sitemap page](/sitemap), which means that chat will appear as an option under the hamburger menu for many [skins](./customskin.md). Chat messages are subject to [Fossil's full range of Markdown processing](/md_rules). Because chat messages are |
| ︙ | ︙ | |||
120 121 122 123 124 125 126 | show up in that list, nor does the chat infrastructure have a way to track and present those. That list can be used to filter messages on a specific user by tapping on that user's name, tapping a second time to remove the filter. ### <a id="cli"></a> The `fossil chat` Command | | | | | 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | show up in that list, nor does the chat infrastructure have a way to track and present those. That list can be used to filter messages on a specific user by tapping on that user's name, tapping a second time to remove the filter. ### <a id="cli"></a> The `fossil chat` Command Type [fossil chat](/help/chat) from within any open check-out to bring up a chatroom for the project that is in that checkout. The new chat window will attempt to connect to the default sync target for that check-out (the server whose URL is shown by the [fossil remote](/help/remote) command). ### <a id="robots"></a> Chat Messages From Robots The [fossil chat send](/help/chat) can be used by project-specific robots to send notifications to the chatroom. For example, on the [SQLite project](https://sqlite.org/) (for which the Fossil chatroom feature, and indeed all of Fossil, was invented) there are long-running fuzz servers that sometimes run across obscure problems. Whenever this happens, a message is sent to the SQLite developers chatroom alerting them to the problem. |
| ︙ | ︙ | |||
153 154 155 156 157 158 159 | ~~~~ Substitute the appropriate project URL, robot account name and password, message text and file attachment, of course. ### <a id="chat-robot"></a> Chat Messages For Timeline Events | | | 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 | ~~~~ Substitute the appropriate project URL, robot account name and password, message text and file attachment, of course. ### <a id="chat-robot"></a> Chat Messages For Timeline Events If the [chat-timeline-user setting](/help/chat-timeline-user) is not an empty string, then any change to the repository that would normally result in a new timeline entry is announced in the chatroom. The announcement appears to come from a user whose name is given by the chat-timeline-user setting. This mechanism is similar to [email notification](./alerts.md) except that the notification is sent via chat instead of via email. |
| ︙ | ︙ | |||
187 188 189 190 191 192 193 | ## Implementation Details *You do not need to understand how Fossil chat works in order to use it. But many developers prefer to know how their tools work. This section is provided for the benefit of those curious developers.* | | | 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
## Implementation Details
*You do not need to understand how Fossil chat works in order to use it.
But many developers prefer to know how their tools work.
This section is provided for the benefit of those curious developers.*
The [/chat](/help/www/chat) webpage downloads a small amount of HTML
and a small amount of javascript to run the chat session. The
javascript uses XMLHttpRequest (XHR) to download chat content, post
new content, or delete historical messages. The following web
interfaces are used by the XHR:
* [/chat-poll](/help?name=/chat-poll) →
Downloads chat content as JSON.
|
| ︙ | ︙ | |||
244 245 246 247 248 249 250 | file BLOB -- Text of the uploaded file, or NULL ); ~~~ The CHAT table is not cross-linked with any other tables in the repository schema. An administrator can "DROP TABLE chat;" at any time, without harm (apart from deleting all chat history, of course). The CHAT table | | | | 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 | file BLOB -- Text of the uploaded file, or NULL ); ~~~ The CHAT table is not cross-linked with any other tables in the repository schema. An administrator can "DROP TABLE chat;" at any time, without harm (apart from deleting all chat history, of course). The CHAT table is dropped when running [fossil scrub --verily](/help/scrub). On the server-side, message text is stored exactly as entered by the users. The /chat-poll page queries the CHAT table and constructs a JSON reply described in the [/chat-poll documentation](/help/www/chat-poll). The message text is translated into HTML before being converted to JSON so that the text can be safely added to the display using assignment to `innerHTML`. Though `innerHTML` assignment is generally considered unsafe, it is only so with untrusted content from untrusted sources. The chat content goes through sanitization steps which eliminate any potential security vulnerabilities of assigning that content to `innerHTML`. |
Changes to www/childprojects.wiki.
| ︙ | ︙ | |||
45 46 47 48 49 50 51 | The repository is now a separate project, independent from its parent. Clone the new project to the developers as needed. The child project and the parent project will not normally be able to sync with one another, since they are now separate projects with distinct project codes. However, if the "--from-parent-project" command-line option is provided to the | | | 45 46 47 48 49 50 51 52 53 54 55 56 57 | The repository is now a separate project, independent from its parent. Clone the new project to the developers as needed. The child project and the parent project will not normally be able to sync with one another, since they are now separate projects with distinct project codes. However, if the "--from-parent-project" command-line option is provided to the "[/help/pull|fossil pull]" command in the child, and the URL of parent repository is also provided on the command-line, then updates to the parent project that occurred after the child was created will be added to the child repository. Thus, by periodically doing a pull --from-parent-project, the child project is able to stay up to date with all the latest changes in the parent. |
Changes to www/chroot.md.
| ︙ | ︙ | |||
28 29 30 31 32 33 34 |
* any shared libraries your `fossil` binary is linked to, unless you
[configured Fossil with `--static`][bld] to avoid it
Fossil does all of this as one of many layers of defense against
hacks and exploits. You can prevent Fossil from entering the chroot
jail using the <tt>--nojail</tt> option to the
| | | 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
* any shared libraries your `fossil` binary is linked to, unless you
[configured Fossil with `--static`][bld] to avoid it
Fossil does all of this as one of many layers of defense against
hacks and exploits. You can prevent Fossil from entering the chroot
jail using the <tt>--nojail</tt> option to the
[fossil server command](/help/server)
but you cannot make Fossil hold onto root privileges. Fossil always drops
root privilege before accepting inputs, for security.
[bld]: https://fossil-scm.org/home/doc/trunk/www/build.wiki
[cj]: https://en.wikipedia.org/wiki/Chroot
[fls]: ./loadmgmt.md
|
| ︙ | ︙ |
Changes to www/ckout-workflows.md.
| ︙ | ︙ | |||
133 134 135 136 137 138 139 |
fossil clone https://dev.example.com/repo/my-project
The `/repo` addition is the key: whatever comes after is used as the
repository name. [See the docs][clone] for more details.
[caod]: https://fossil-scm.org/forum/forumpost/3f143cec74
| | | 133 134 135 136 137 138 139 140 141 142 |
fossil clone https://dev.example.com/repo/my-project
The `/repo` addition is the key: whatever comes after is used as the
repository name. [See the docs][clone] for more details.
[caod]: https://fossil-scm.org/forum/forumpost/3f143cec74
[clone]: /help/clone
<div style="height:50em" id="this-space-intentionally-left-blank"></div>
|
Changes to www/co-vs-up.md.
| ︙ | ︙ | |||
22 23 24 25 26 27 28 |
provide a good reason to develop a habit of using `fossil checkout`
instead.
In summary, these are two separate commands; neither is an alias for the
other. They overlap enough that they can be used interchangeably for
some use cases, but `update` is more powerful and more broadly useful.
| | | | 22 23 24 25 26 27 28 29 30 31 |
provide a good reason to develop a habit of using `fossil checkout`
instead.
In summary, these are two separate commands; neither is an alias for the
other. They overlap enough that they can be used interchangeably for
some use cases, but `update` is more powerful and more broadly useful.
[co]: /help/checkout
[cvsmu]: http://web.mit.edu/gnu/doc/html/cvs_7.html#SEC37
[up]: /help/update
|
Changes to www/concepts.wiki.
| ︙ | ︙ | |||
433 434 435 436 437 438 439 | With other configuration management software, setting up a server is a lot of work and normally takes time, patience, and a lot of system knowledge. Fossil is designed to avoid this frustration. Setting up a server with Fossil is ridiculously easy. You have four options: # <b>Stand-alone server.</b> | | | | | | 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 |
With other configuration management software, setting up a server is
a lot of work and normally takes time, patience, and a lot of system
knowledge. Fossil is designed to avoid this frustration. Setting up
a server with Fossil is ridiculously easy. You have four options:
# <b>Stand-alone server.</b>
Simply run the [/help/server|fossil server] or
[/help/ui|fossil ui] command from the command-line.
<br><br>
# <b>CGI.</b>
Install a 2-line CGI script on a CGI-enabled web-server like Apache.
<br><br>
# <b>SCGI.</b>
Start an SCGI server using the
[/help/server| fossil server --scgi] command for handling
SCGI requests from web-servers like Nginx.
<br><br>
# <b>Inetd or Stunnel.</b>
Configure programs like inetd, xinetd, or stunnel to hand off HTTP requests
directly to the [/help/http|fossil http] command.
See the [./server/ | How To Configure A Fossil Server] document
for details.
<h2>6.0 Review Of Key Concepts</h2>
<ul>
|
| ︙ | ︙ |
Changes to www/containers.md.
| ︙ | ︙ | |||
539 540 541 542 543 544 545 |
[Install]
WantedBy=default.target
I was then able to enable email alert forwarding for select repositories
after configuring them per [the docs](./alerts.md) by saying:
$ systemctl --user daemon-reload
| | < | 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 |
[Install]
WantedBy=default.target
I was then able to enable email alert forwarding for select repositories
after configuring them per [the docs](./alerts.md) by saying:
$ systemctl --user daemon-reload
$ systemctl --user enable --now alert-sender@myproject
Because this is a parameterized script and we’ve set our repository
paths predictably, you can do this for as many repositories as you need
to by passing their names after the “`@`” sign in the commands above.
## 6. <a id="light"></a>Lightweight Alternatives to Docker
|
| ︙ | ︙ |
Changes to www/contribute.wiki.
| ︙ | ︙ | |||
38 39 40 41 42 43 44 | [https://fossil-scm.org/forum | the forum] or email them to <a href="mailto:drh@sqlite.org">drh@sqlite.org</a>. Be sure to describe in detail what the patch does and which version of Fossil it is written against. It's best to make patches against tip-of-trunk rather than against past releases. If your change is more complicated than a patch can properly encode, you | | | 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | [https://fossil-scm.org/forum | the forum] or email them to <a href="mailto:drh@sqlite.org">drh@sqlite.org</a>. Be sure to describe in detail what the patch does and which version of Fossil it is written against. It's best to make patches against tip-of-trunk rather than against past releases. If your change is more complicated than a patch can properly encode, you may submit [/help/bundle | a Fossil bundle] instead. Unlike patches, bundles can contain multiple commits, check-in comments, file renames, file deletions, branching decisions, and more which <tt>patch(1)</tt> files cannot. It's best to make a bundle of a new branch so the change can be integrated, tested, enhanced, and merged down to trunk in a controlled fashion. A contributor agreement is not strictly necessary to submit a patch or bundle, |
| ︙ | ︙ |
Changes to www/customskin.md.
| ︙ | ︙ | |||
21 22 23 24 25 26 27 | * css.txt * details.txt * footer.txt * header.txt * js.txt Try out the built-in skins by using the --skin option on the | | | | 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | * css.txt * details.txt * footer.txt * header.txt * js.txt Try out the built-in skins by using the --skin option on the [fossil ui](/help/ui) or [fossil server](/help/server) commands. ## <a id="sharing"></a>Sharing Skins The skin of a repository is not part of the versioned state and does not "push" or "pull" like checked-in files. The skin is local to the repository. However, skins can be shared between repositories using the [fossil config](/help/configuration) command. The "fossil config push skin" command will send the local skin to a remote repository and the "fossil config pull skin" command will import a skin from a remote repository. The "fossil config export skin FILENAME" will export the skin for a repository into a file FILENAME. This file can then be imported into a different repository using the "fossil config import FILENAME" command. Unlike "push" and "pull", the "export" and "import" commands are able to move skins between |
| ︙ | ︙ | |||
302 303 304 305 306 307 308 | working as desired, the draft skin is "published" and becomes the new live skin that most users see. ### Skin Development Using A Local Text Editor An alternative approach is to copy the five control files for your baseline skin into a temporary working directory (here called | | | 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 | working as desired, the draft skin is "published" and becomes the new live skin that most users see. ### Skin Development Using A Local Text Editor An alternative approach is to copy the five control files for your baseline skin into a temporary working directory (here called "./newskin") and then launch the [fossil ui](/help/ui) command with the "--skin ./newskin" option. If the argument to the --skin option contains a "/" character, then the five control files are read out of the directory named. You can then edit the control files in the ./newskin folder using you favorite text editor, and press "Reload" on your browser to see the effects. |
| ︙ | ︙ | |||
513 514 515 516 517 518 519 |
be copied directly out of one of the subdirectories under skins. If
sources are not easily at hand, then a copy/paste out of the
CSS, footer, and header editing screens under the Admin menu will
work just as well. The important point is that the three files
be named exactly "css.txt", "footer.txt", and "header.txt" and that
they all be in the same directory.
| | | 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 |
be copied directly out of one of the subdirectories under skins. If
sources are not easily at hand, then a copy/paste out of the
CSS, footer, and header editing screens under the Admin menu will
work just as well. The important point is that the three files
be named exactly "css.txt", "footer.txt", and "header.txt" and that
they all be in the same directory.
2. Run the [fossil ui](/help/ui) command with an extra
option "--skin SKINDIR" where SKINDIR is the name of the directory
in which the three txt files were stored in step 1. This will bring
up the Fossil website using the tree files in SKINDIR.
3. Edit the *.txt files in SKINDIR. After making each small change,
press Reload on the web browser to see the effect of that change.
Iterate until the desired look is achieved.
|
| ︙ | ︙ |
Changes to www/defcsp.md.
| ︙ | ︙ | |||
29 30 31 32 33 34 35 | script-src 'self' 'nonce-$nonce'; style-src 'self' 'unsafe-inline'; img-src * data:; </pre> The default is recommended for most installations. However, the site administrators can overwrite this default CSP using the | | | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
script-src 'self' 'nonce-$nonce';
style-src 'self' 'unsafe-inline';
img-src * data:;
</pre>
The default is recommended for most installations. However,
the site administrators can overwrite this default CSP using the
[default-csp setting](/help/default-csp). For example,
CSP restrictions can be completely disabled by setting the default-csp to:
default-src *;
The following sections detail the maining of the default CSP setting.
### <a id="base"></a> default-src 'self' data:
|
| ︙ | ︙ | |||
284 285 286 287 288 289 290 |
external resources in the backup copies, so that when the main repo
site disappears, so do those files.
Unversioned content is in the middle of the first list above — between
fully-external content and fully in-repo content — because it isn’t
included in a clone unless you give the `--unversioned` flag. If you
then want updates to the unversioned content to be included in syncs,
| | | | | | | 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 |
external resources in the backup copies, so that when the main repo
site disappears, so do those files.
Unversioned content is in the middle of the first list above — between
fully-external content and fully in-repo content — because it isn’t
included in a clone unless you give the `--unversioned` flag. If you
then want updates to the unversioned content to be included in syncs,
you have to give the same flag to [a `sync` command](/help/sync).
There is no equivalent with other commands such as `up` and `pull`, so
you must then remember to give `fossil uv` commands when necessary to
pull new unversioned content down.
Thus our recommendation that you refer to in-repo resources exclusively.
[du]: /help/www/doc
[fp]: ./forum.wiki
[ru]: /help/www/raw
[spof]: https://en.wikipedia.org/wiki/Single_point_of_failure
[tkt]: ./tickets.wiki
[tn]: ./event.wiki
[tls]: ./server/debian/nginx.md
[uu]: /help/www/uv
[uv]: ./unvers.wiki
[wiki]: ./wikitheory.wiki
## <a id="override"></a>Overriding the Default CSP
If you wish to relax the default CSP’s restrictions or to tighten them
further, there are multiple ways to accomplish that.
The following methods are listed in top-down order to give the simplest
and most straightforward method first. Further methods dig down deeper
into the stack, which is helpful to understand even if you end up using
a higher-level method.
### <a id="cspsetting"></a>The `default-csp` Setting
If the [`default-csp` setting](/help/default-csp) is defined and is
not an empty string, its value is injected into the page using
[TH1](./th1.md) via one or more of the methods below, depending on the
skin you’re using and local configuration.
Changing this setting is the easiest way to set a nonstandard CSP on
your site.
|
| ︙ | ︙ |
Changes to www/delta_format.wiki.
| ︙ | ︙ | |||
24 25 26 27 28 29 30 | contained in the [../src/deltacmd.c|deltacmd.c] source file. SQL functions to create, apply, and analyze deltas are implemented by code in the [../src/deltafunc.c|deltafunc.c] source file. The following command-line tools are available to create and apply deltas and to test the delta logic: | | | | | | | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
contained in the [../src/deltacmd.c|deltacmd.c] source file. SQL functions
to create, apply, and analyze deltas are implemented by code in the
[../src/deltafunc.c|deltafunc.c] source file.
The following command-line tools are available to create and apply
deltas and to test the delta logic:
* [/help/test-delta|fossil test-delta] → Run self-tests of
the delta logic
* [/help/test-delta-create|fossil test-delta-create X Y] → compute
a delta that converts file X into file Y. Output that delta.
* [/help/test-delta-apply|fossil test-delta-apply X D] → apply
delta D to input file X and output the result.
* [/help/test-delta-analyze|fossil test-delta-analyze X Y] → compute
and delta that converts file X into file Y but instead of writing the
delta to output, write performance information about the delta.
When running the [/help/sqlite3|fossil sql] command to get an
interactive SQL session connected to the repository, the following
additional SQL functions are provided:
* <b>delta_create(</b><i>X</i><b>,</b><i>Y</i><b>)</b> →
Compute a data that carries blob X into blob Y and return that delta
as a blob.
|
| ︙ | ︙ |
Changes to www/embeddeddoc.wiki.
| ︙ | ︙ | |||
34 35 36 37 38 39 40 | <pre> <i><baseurl></i><big><b>/doc/</b></big><i><version></i><big><b>/</b></big><i><filename></i> </pre> The <i><baseurl></i> is the main URL used to access the fossil web server. For example, the <i><baseurl></i> for the fossil project itself is [https://fossil-scm.org/home]. | | | | 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | <pre> <i><baseurl></i><big><b>/doc/</b></big><i><version></i><big><b>/</b></big><i><filename></i> </pre> The <i><baseurl></i> is the main URL used to access the fossil web server. For example, the <i><baseurl></i> for the fossil project itself is [https://fossil-scm.org/home]. If you launch the web server using the "[/help/ui|fossil ui]" command line, then the <i><baseurl></i> is usually <b>http://localhost:8080/</b>. The <i><version></i> is the [./checkin_names.wiki|name of a check-in] that contains the embedded document. This might be a hash prefix for the check-in, or it might be the name of a branch or tag, or it might be a timestamp. See the prior link for more possibilities and examples. The <i id="ckout"><version></i> can also be the special identifier "<b>ckout</b>". The "<b>ckout</b>" keywords means to pull the documentation file from the local source tree on disk, not from the any check-in. The "<b>ckout</b>" keyword only works when you start your server using the "[/help/server|fossil server]" or "[/help?cmd=ui|fossil ui]" commands. The "/doc/ckout" URL is intended to show a preview of the documentation you are currently editing but have not yet checked in. The original designed purpose of the "ckout" feature is to allow the user to preview local changes to documentation before committing the change. This is an important facility, since unlike other document languages like HTML, there is still a lot of variation among rendering |
| ︙ | ︙ |
Changes to www/env-opts.md.
| ︙ | ︙ | |||
251 252 253 254 255 256 257 | `REQUEST_URI`: If defined, included in error log messages. `SCRIPT_NAME`: If defined, included in error log messages. `SSH_CONNECTION`: Informs CGI processing if the remote client is SSH. `SSL_CERT_FILE`, `SSL_CERT_DIR`: Override the [`ssl-ca-location`] | | | 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | `REQUEST_URI`: If defined, included in error log messages. `SCRIPT_NAME`: If defined, included in error log messages. `SSH_CONNECTION`: Informs CGI processing if the remote client is SSH. `SSL_CERT_FILE`, `SSL_CERT_DIR`: Override the [`ssl-ca-location`] (/help/ssl-ca-location) setting. `SQLITE_FORCE_PROXY_LOCKING`: From `sqlite3.c`, 1 means force always use proxy, 0 means never use proxy, and undefined means use proxy for non-local files only. `SQLITE_TMPDIR`: Names the temporary file location for SQLite. When set, this will be used instead of `TMPDIR`. |
| ︙ | ︙ |
Changes to www/event.wiki.
| ︙ | ︙ | |||
105 106 107 108 109 110 111 | Release Notes 2021-07-02 This note describes changes in the Fossil snapshot for ... </verbatim> The <b>-t|--technote</b> option to the <b>export</b> subcommand takes one of three identifiers: <b>DATETIME</b>; <b>TECHNOTE-ID</b>; and <b>TAG</b>. | | | 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | Release Notes 2021-07-02 This note describes changes in the Fossil snapshot for ... </verbatim> The <b>-t|--technote</b> option to the <b>export</b> subcommand takes one of three identifiers: <b>DATETIME</b>; <b>TECHNOTE-ID</b>; and <b>TAG</b>. See the [/help/wiki | wiki help] for specifics. Users must have check-in privileges (permission "i") in order to create or edit technotes. In addition, users must have create-wiki privilege (permission "f") to create new technotes and edit-wiki privilege (permission "k") in order to edit existing technotes. Technote content may be formatted as [/wiki_rules | Fossil wiki], |
| ︙ | ︙ |
Changes to www/fileedit-page.md.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 | browser halfway around the world comes with several obligatory caveats and disclaimers... ## <a id="cap"></a> `/fileedit` Does *Nothing* by Default. In order to "activate" it, a user with [the "setup" permission](./caps/index.md) must set the | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | browser halfway around the world comes with several obligatory caveats and disclaimers... ## <a id="cap"></a> `/fileedit` Does *Nothing* by Default. In order to "activate" it, a user with [the "setup" permission](./caps/index.md) must set the [fileedit-glob](/help/fileedit-glob) repository setting to a comma- or newline-delimited list of globs representing a whitelist of files which may be edited online. Any user with commit access may then edit files matching one of those globs. Certain pages within the UI get an "edit" link added to them when the current user's permissions and the whitelist both permit editing of that file. ## <a id="csrf"></a> CSRF & HTTP Referrer Headers |
| ︙ | ︙ |
Changes to www/forum.wiki.
| ︙ | ︙ | |||
370 371 372 373 374 375 376 | </ol> <h2 id="close-post">Closing Forum Posts</h2> As of version 2.23, the forum interface supports the notion of "closing" posts. By default, only users with the [./caps/index.md|'s' and 'a' capabilities] may close or re-open posts, or reply to closed | | | 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 | </ol> <h2 id="close-post">Closing Forum Posts</h2> As of version 2.23, the forum interface supports the notion of "closing" posts. By default, only users with the [./caps/index.md|'s' and 'a' capabilities] may close or re-open posts, or reply to closed posts. If the [/help/forum-close-policy|forum-close-policy configuration option] is enabled then users with [./caps/index.md|forum-moderator permissions] may also perform those actions. Closing a post has the following implications: * Only authorized users may edit or respond to such posts, recursively |
| ︙ | ︙ |
Changes to www/fossil-v-git.wiki.
| ︙ | ︙ | |||
103 104 105 106 107 108 109 | Git provides file versioning services only, whereas Fossil adds an integrated [./wikitheory.wiki | wiki], [./bugtheory.wiki | ticketing & bug tracking], [./embeddeddoc.wiki | embedded documentation], [./event.wiki | technical notes], a [./forum.wiki | web forum], and a [./chat.md | chat service], all within a single nicely-designed [./customskin.md|skinnable] web | | | 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | Git provides file versioning services only, whereas Fossil adds an integrated [./wikitheory.wiki | wiki], [./bugtheory.wiki | ticketing & bug tracking], [./embeddeddoc.wiki | embedded documentation], [./event.wiki | technical notes], a [./forum.wiki | web forum], and a [./chat.md | chat service], all within a single nicely-designed [./customskin.md|skinnable] web [/help/ui|UI], protected by [./caps/ | a fine-grained role-based access control system]. These additional capabilities are available for Git as 3rd-party add-ons, but with Fossil they are integrated into the design, to the point that it approximates "[https://github.com/ | GitHub]-in-a-box." |
| ︙ | ︙ | |||
128 129 130 131 132 133 134 | You get the same capability with several other Fossil sub-commands as well, such as "<tt>fossil all changes</tt>" to get a list of files that you forgot to commit prior to the end of your working day, across all repos. Whenever Fossil is told to modify the local checkout in some destructive | | | | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | You get the same capability with several other Fossil sub-commands as well, such as "<tt>fossil all changes</tt>" to get a list of files that you forgot to commit prior to the end of your working day, across all repos. Whenever Fossil is told to modify the local checkout in some destructive way ([/help/rm|fossil rm], [/help/update|fossil update], [/help/revert|fossil revert], etc.) Fossil remembers the prior state and is able to return the check-out directory to that state with a <tt>fossil undo</tt> command. While you cannot undo a commit in Fossil — [#history | on purpose!] — as long as the change remains confined to the local check-out directory only, Fossil makes undo [https://git-scm.com/book/en/v2/Git-Basics-Undoing-Things|easier than in Git]. |
| ︙ | ︙ | |||
441 442 443 444 445 446 447 |
organizations].
* <b>No easy drive-by contributions:</b> Git
[https://www.git-scm.com/docs/git-request-pull|pull requests] offer
a low-friction path to accepting
[https://www.jonobacon.com/2012/07/25/building-strong-community-structural-integrity/|drive-by
contributions]. Fossil's closest equivalents are its unique
| | | | 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 |
organizations].
* <b>No easy drive-by contributions:</b> Git
[https://www.git-scm.com/docs/git-request-pull|pull requests] offer
a low-friction path to accepting
[https://www.jonobacon.com/2012/07/25/building-strong-community-structural-integrity/|drive-by
contributions]. Fossil's closest equivalents are its unique
[/help/bundle|bundle] and [/help/patch|patch] features, which require higher engagement
than firing off a PR.⁷ This difference comes directly from the
initial designed purpose for each tool: the SQLite project doesn't
accept outside contributions from previously-unknown developers, but
the Linux kernel does.
* <b>No rebasing:</b> When your local repo clone syncs changes
up to its parent, those changes are sent exactly as they were
committed locally. [#history|There is no rebasing mechanism in
Fossil, on purpose.]
* <b>Sync over push:</b> Explicit pushes are uncommon in
Fossil-based projects: the default is to rely on
[/help/autosync|autosync mode] instead, in which each commit
syncs immediately to its parent repository. This is a mode so you
can turn it off temporarily when needed, such as when working
offline. Fossil is still a truly distributed version control system;
it's just that its starting default is to assume you're rarely out
of communication with the parent repo.
<br><br>
This is not merely a reflection of modern always-connected computing
|
| ︙ | ︙ | |||
596 597 598 599 600 601 602 | Because Git commingles the repository data with the initial checkout of that repository, the default mode of operation in Git is to stick to that single work/repo tree, even when that's a shortsighted way of working. Fossil doesn't work that way. A Fossil repository is an SQLite database file which is normally stored outside the working checkout directory. You can | | | 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 | Because Git commingles the repository data with the initial checkout of that repository, the default mode of operation in Git is to stick to that single work/repo tree, even when that's a shortsighted way of working. Fossil doesn't work that way. A Fossil repository is an SQLite database file which is normally stored outside the working checkout directory. You can [/help/open | open] a Fossil repository any number of times into any number of working directories. A common usage pattern is to have one working directory per active working branch, so that switching branches is done with a <tt>cd</tt> command rather than by checking out the branches successively in a single working directory. Fossil does allow you to switch branches within a working checkout directory, and this is also often done. It is simply that there is no |
| ︙ | ︙ | |||
680 681 682 683 684 685 686 | including all of the messy errors, dead-ends, experimental branches, and so forth. One might argue that this makes the history of a Fossil project "messy," but another point of view is that this makes the history "accurate." In actual practice, the superior reporting tools available in Fossil mean that this incidental mess is not a factor. | | | 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 | including all of the messy errors, dead-ends, experimental branches, and so forth. One might argue that this makes the history of a Fossil project "messy," but another point of view is that this makes the history "accurate." In actual practice, the superior reporting tools available in Fossil mean that this incidental mess is not a factor. Like Git, Fossil has an [/help/amend|amend command] for modifying prior commits, but unlike in Git, this works not by replacing data in the repository, but by adding a correction record to the repository that affects how later Fossil operations present the corrected data. The old information is still there in the repository, it is just overridden from the amendment point forward. Fossil lacks almost every other history rewriting mechanism listed on |
| ︙ | ︙ | |||
710 711 712 713 714 715 716 | shun certain committed artifacts, but a person cannot force their local shun requests into another repo without having admin-level control over the receiving repo as well. Fossil's shun feature isn't for fixing up everyday bad commits, it's for dealing with extreme situations: public commits of secret material, ticket/wiki/forum spam, law enforcement takedown demands, etc. | | | 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 | shun certain committed artifacts, but a person cannot force their local shun requests into another repo without having admin-level control over the receiving repo as well. Fossil's shun feature isn't for fixing up everyday bad commits, it's for dealing with extreme situations: public commits of secret material, ticket/wiki/forum spam, law enforcement takedown demands, etc. There is also the experimental [/help/purge | <tt>purge</tt> command], which differs from shunning in ways that aren't especially important in the context of this document. At a 30000 foot level, you can think of purging as useful only when you've turned off Fossil's autosync feature and want to pluck artifacts out of its hash tree before they get pushed. In that sense, it's approximately the same as <tt>git rebase -i, drop</tt>. However, given that Fossil defaults to having autosync enabled [#devorg | for good reason], the purge command |
| ︙ | ︙ | |||
819 820 821 822 823 824 825 | much work gets applied — just one check-in or a whole branch — and the merge direction. This is the sort of thing we mean when we point out that Fossil's command interface is simpler than Git's: there are fewer concepts to keep track of in your mental model of Fossil's internal operation. Fossil's implementation of the feature is also simpler to describe. The | | | 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 | much work gets applied — just one check-in or a whole branch — and the merge direction. This is the sort of thing we mean when we point out that Fossil's command interface is simpler than Git's: there are fewer concepts to keep track of in your mental model of Fossil's internal operation. Fossil's implementation of the feature is also simpler to describe. The brief online help for <tt>[/help/merge | fossil merge]</tt> is currently 41 lines long, to which you want to add the 600 lines of [./branching.wiki | the branching document]. The equivalent documentation in Git is the aggregation of the man pages for the above three commands, which is over 1000 lines, much of it mutually redundant. (e.g. Git's <tt>--edit</tt> and <tt>--no-commit</tt> options get described three times, each time differently.) Fossil's documentation is not only more concise, it gives a nice split of brief |
| ︙ | ︙ |
Changes to www/gitusers.md.
| ︙ | ︙ | |||
35 36 37 38 39 40 41 | Forum][ffor]. While we do try to explain Fossil-specific terminology inline here as-needed, you may find it helpful to skim [the Fossil glossary][gloss]. It will give you another take on our definitions here, and it may help you to understand some of the other Fossil docs better. | | | 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | Forum][ffor]. While we do try to explain Fossil-specific terminology inline here as-needed, you may find it helpful to skim [the Fossil glossary][gloss]. It will give you another take on our definitions here, and it may help you to understand some of the other Fossil docs better. [fbis]: /help/bisect [gbis]: https://git-scm.com/docs/git-bisect [ffor]: https://fossil-scm.org/forum [fvg]: ./fossil-v-git.wiki <a id="mwd"></a> ## Repositories and Checkouts Are Distinct |
| ︙ | ︙ | |||
88 89 90 91 92 93 94 | than `fossil checkout`. That said, one of those differences does match up with Git users’ expectations: `fossil checkout` doesn’t pull changes from the remote repository into the local clone as `fossil update` does. We think this is less broadly useful, but that’s the subject of the next section. [ckwf]: ./ckout-workflows.md | | | 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | than `fossil checkout`. That said, one of those differences does match up with Git users’ expectations: `fossil checkout` doesn’t pull changes from the remote repository into the local clone as `fossil update` does. We think this is less broadly useful, but that’s the subject of the next section. [ckwf]: ./ckout-workflows.md [co]: /help/checkout #### <a id="pullup"></a> Update vs Pull The closest equivalent to [`git pull`][gpull] is not [`fossil pull`][fpull], but in fact [`fossil up`][up]. |
| ︙ | ︙ | |||
129 130 131 132 133 134 135 | In fact, these are the same operation, so they’re the same command in Fossil. The first form simply allows the `VERSION` to be implicit: the tip of the current branch. We think this is a more sensible command design than `git pull` vs `git checkout`. ([…vs `git checkout` vs `git checkout`!][gcokoan]) | | | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | In fact, these are the same operation, so they’re the same command in Fossil. The first form simply allows the `VERSION` to be implicit: the tip of the current branch. We think this is a more sensible command design than `git pull` vs `git checkout`. ([…vs `git checkout` vs `git checkout`!][gcokoan]) [fpull]: /help/pull [gpull]: https://git-scm.com/docs/git-pull [gcokoan]: https://stevelosh.com/blog/2013/04/git-koans/#s2-one-thing-well #### <a id="close" name="dotfile"></a> Closing a Check-Out The [`fossil close`][close] command dissociates a check-out directory from the |
| ︙ | ︙ | |||
167 168 169 170 171 172 173 | Closing a check-out directory is a rare operation. One use case is that you’re about to delete the directory, so you want Fossil to forget about it for the purposes of commands like [`fossil all`][all]. Even that isn’t necessary, because Fossil will detect that this has happened and forget the working directory for you. | | | 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | Closing a check-out directory is a rare operation. One use case is that you’re about to delete the directory, so you want Fossil to forget about it for the purposes of commands like [`fossil all`][all]. Even that isn’t necessary, because Fossil will detect that this has happened and forget the working directory for you. [all]: /help/all #### <a id="worktree"></a> Git Worktrees There are at least three different ways to get [Fossil-style multiple check-out directories][mcw] with Git. |
| ︙ | ︙ | |||
269 270 271 272 273 274 275 | Git, not to commend this `.fsl`-at-project-root trick to you. A better choice would be `~/museum/home/long-established-project.fossil`, if you’re following [the directory scheme exemplified in the glossary](./glossary.md#repository). That said, it does emphasize an earlier point: Fossil doesn’t care where you put the repo DB file or what you name it. | | | | | | | | | 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 | Git, not to commend this `.fsl`-at-project-root trick to you. A better choice would be `~/museum/home/long-established-project.fossil`, if you’re following [the directory scheme exemplified in the glossary](./glossary.md#repository). That said, it does emphasize an earlier point: Fossil doesn’t care where you put the repo DB file or what you name it. [clone]: /help/clone [close]: /help/close [gloss]: ./glossary.md [open]: /help/open [set]: /help/setting [server]: /help/server [stash]: /help/stash [undo]: /help/undo ## <a id="log"></a> Fossil’s Timeline Is the “Log” Git users often need to use the `git log` command to dig linearly through commit histories due to its [weak data model][wdm], giving [O(n) performance][ocomp]. |
| ︙ | ︙ | |||
414 415 416 417 418 419 420 | Granted, that’s rather obscure, but you you can also choose something intermediate like “`f time desc curr`”, which is reasonably clear. [35pct]: https://www.sqlite.org/fasterthanfs.html [btree]: https://sqlite.org/btreemodule.html [gcn]: https://git-scm.com/docs/gitrevisions | | | | | | | 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 | Granted, that’s rather obscure, but you you can also choose something intermediate like “`f time desc curr`”, which is reasonably clear. [35pct]: https://www.sqlite.org/fasterthanfs.html [btree]: https://sqlite.org/btreemodule.html [gcn]: https://git-scm.com/docs/gitrevisions [infoc]: /help/info [infow]: /help/www/info [ocomp]: https://www.bigocheatsheet.com/ [tlc]: /help/timeline [tlw]: /help/www/timeline [up]: /help/update [wdm]: ./fossil-v-git.wiki#durable ## <a id="dhead"></a> Detached HEAD State The SQL indexes in Fossil which we brought up above have a useful side benefit: you cannot have a [detached HEAD state][gdh] in Fossil, |
| ︙ | ︙ | |||
629 630 631 632 633 634 635 | Fossil doesn’t need to be told what to push or where to push it: it just keeps using the same remote server URL you gave it last until you [tell it to do something different][rem]. It pushes all branches, not just one named local branch. [capt]: ./cap-theorem.md | | | 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 | Fossil doesn’t need to be told what to push or where to push it: it just keeps using the same remote server URL you gave it last until you [tell it to do something different][rem]. It pushes all branches, not just one named local branch. [capt]: ./cap-theorem.md [rem]: /help/remote <a id="autosync"></a> ## Autosync Fossil’s [autosync][wflow] feature, normally enabled, has no equivalent in Git. If you want Fossil to behave like Git, you can turn |
| ︙ | ︙ | |||
939 940 941 942 943 944 945 | diff implementation, bypassing [your local `diff-command` setting][dcset] since the `--numstat` option has no effect when you have an external diff command set. If you leave off the `-v` flag in the second example, the `diffstat` output won’t include info about any newly-added files. | | | 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 | diff implementation, bypassing [your local `diff-command` setting][dcset] since the `--numstat` option has no effect when you have an external diff command set. If you leave off the `-v` flag in the second example, the `diffstat` output won’t include info about any newly-added files. [dcset]: https://fossil-scm.org/home/help/diff-command [dst]: https://invisible-island.net/diffstat/diffstat.html <a id="btnames"></a> ## Branch and Tag Names Fossil has no special restrictions on the names of tags and branches, |
| ︙ | ︙ | |||
962 963 964 965 966 967 968 | release in a project that uses this tagging convention. [The `fossil git export` command][fge] squashes repeated tags down to a single instance to avoid confusing Git, exporting only the newest tag, emulating Fossil’s own ambiguity resolution rule as best it can within Git’s limitations. | | | | 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 | release in a project that uses this tagging convention. [The `fossil git export` command][fge] squashes repeated tags down to a single instance to avoid confusing Git, exporting only the newest tag, emulating Fossil’s own ambiguity resolution rule as best it can within Git’s limitations. [fge]: /help/git [gcrf]: https://git-scm.com/docs/git-check-ref-format <a id="cpickrev"></a> ## Cherry-Picking and Reverting Commits Git’s separate "`git cherry-pick`" and “`git revert`” commands are options to the [`fossil merge` command][merge]: `--cherrypick` and `--backout`, respectively. We view this as sensible, since these are both merge operations, and the two actions differ only in direction. Unlike in Git, the Fossil file format remembers cherrypicks and backouts and can later show them as dashed lines on the graphical timeline. [merge]: /help/merge <a id="mvrm"></a> ## File Moves and Renames Are Soft by Default The "[`fossil mv`][mv]" and "[`fossil rm`][rm]" commands work like they |
| ︙ | ︙ | |||
1003 1004 1005 1006 1007 1008 1009 |
this setting hasn’t been overridden locally.
If you want to keep Fossil’s soft `mv/rm` behavior most of the time, you
can cast it away on a per-command basis:
fossil mv --hard old-name new-name
| | | | 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 |
this setting hasn’t been overridden locally.
If you want to keep Fossil’s soft `mv/rm` behavior most of the time, you
can cast it away on a per-command basis:
fossil mv --hard old-name new-name
[mv]: /help/mv
[rm]: /help/rm
----
## <a id="cvdate" name="cs1"></a> Case Study 1: Checking Out a Version by Date
|
| ︙ | ︙ |
Changes to www/globs.md.
| ︙ | ︙ | |||
240 241 242 243 244 245 246 | than archiving the entire checkin. The commands [`http`][], [`cgi`][], [`server`][], and [`ui`][] that implement or support with web servers provide a mechanism to name some files to serve with static content where a list of glob patterns specifies what content may be served. | | | | | | | | | | | | | | | | | | | | | | 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | than archiving the entire checkin. The commands [`http`][], [`cgi`][], [`server`][], and [`ui`][] that implement or support with web servers provide a mechanism to name some files to serve with static content where a list of glob patterns specifies what content may be served. [`add`]: /help/add [`addremove`]: /help/addremove [`changes`]: /help/changes [`clean`]: /help/clean [`commit`]: /help/commit [`extras`]: /help/extras [`merge`]: /help/merge [`settings`]: /help/settings [`status`]: /help/status [`touch`]: /help/touch [`unset`]: /help/unset [`tarball`]: /help/tarball [`zip`]: /help/zip [`http`]: /help/http [`cgi`]: /help/cgi [`server`]: /help/server [`ui`]: /help/ui ### Web Pages that Refer to Globs The [`/timeline`][] page supports the query parameter `chng=GLOBLIST` that names a list of glob patterns defining which files to focus the timeline on. It also has the query parameters `t=TAG` and `r=TAG` that names a tag to focus on, which can be configured with `ms=STYLE` to use a glob pattern to match tag names instead of the default exact match or a couple of other comparison styles. The pages [`/tarball`][] and [`/zip`][] generate compressed archives of a specific checkin. They may be further restricted by query parameters that specify glob patterns that name files to include or exclude rather than taking the entire checkin. [`/timeline`]: /help/www/timeline [`/tarball`]: /help/www/tarball [`/zip`]: /help/www/zip ## Platform Quirks Fossil glob patterns are based on the glob pattern feature of POSIX shells. Fossil glob patterns also have a quoting mechanism, discussed [above](#syntax). Because other parts of your operating system may interpret glob |
| ︙ | ︙ | |||
510 511 512 513 514 515 516 |
$ fossil test-echo setting crlf-glob "*"
C:\> echo * | fossil test-echo setting crlf-glob --args -
The [`test-glob`][] command is also handy to test if a string
matches a glob pattern.
| | | | 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 |
$ fossil test-echo setting crlf-glob "*"
C:\> echo * | fossil test-echo setting crlf-glob --args -
The [`test-glob`][] command is also handy to test if a string
matches a glob pattern.
[`test-echo`]: /help/test-echo
[`test-glob`]: /help/test-glob
## Converting `.gitignore` to `ignore-glob`
Many other version control systems handle the specific case of
ignoring certain files differently from Fossil: they have you create
individual "ignore" files in each folder, which specify things ignored
|
| ︙ | ︙ |
Changes to www/glossary.md.
| ︙ | ︙ | |||
88 89 90 91 92 93 94 |
backup utility.
As a counterexample, a project tracking your [Vim] configuration
history is a much better use of Fossil, because it’s all held within
`~/.vim`, and your user has full rights to that subdirectory.
[AIF]: https://docs.asciidoctor.org/asciidoc/latest/directives/include/
| | | | | | | 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
backup utility.
As a counterexample, a project tracking your [Vim] configuration
history is a much better use of Fossil, because it’s all held within
`~/.vim`, and your user has full rights to that subdirectory.
[AIF]: https://docs.asciidoctor.org/asciidoc/latest/directives/include/
[IGS]: /help/ignore-glob
[IFRS]: ./image-format-vs-repo-size.md
[tarball]: /help/tarball
[tw]: /help/www/tarball
[Vim]: https://www.vim.org/
[zip]: /help/zip
[zw]: /help/www/zip
## Repository <a id="repository" name="repo"></a>
A single file that contains all historical versions of all files in a
project, which can be [cloned] to other machines and
[synchronized][sync] with them. Jargon: repo.
|
| ︙ | ︙ | |||
193 194 195 196 197 198 199 | box "other/" fit move right 0.1 line dotted right until even with previous line.end move right 0.05 box invis "clones of Fossil itself, SQLite, etc." ljust ``` | | | | | | | | 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 | box "other/" fit move right 0.1 line dotted right until even with previous line.end move right 0.05 box invis "clones of Fossil itself, SQLite, etc." ljust ``` [asdis]: /help/autosync [backup]: ./backup.md [CAP]: ./cap-theorem.md [cloned]: /help/clone [pull]: /help/pull [push]: /help/push [svrcmd]: /help/server [sync]: /help/sync [repository]: #repo [repositories]: #repo ## Version / Revision / Hash / UUID <a id="version" name="hash"></a> |
| ︙ | ︙ | |||
308 309 310 311 312 313 314 | that Fossil will capture only changes to files you’ve [added][add] to the [repository], not to everything in [the check-out directory](#co) at the time of the snapshot. (Thus [the `extras` command][extras].) Contrast a snapshot taken by a virtual machine system or a [snapshotting file system][snfs], which captures changes to everything on the managed storage volume. | | | | | | 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 | that Fossil will capture only changes to files you’ve [added][add] to the [repository], not to everything in [the check-out directory](#co) at the time of the snapshot. (Thus [the `extras` command][extras].) Contrast a snapshot taken by a virtual machine system or a [snapshotting file system][snfs], which captures changes to everything on the managed storage volume. [add]: /help/add [ciname]: ./checkin_names.wiki [extras]: /help/extras [stash]: /help/stash [undo]: /help/undo ## Check-out <a id="check-out" name="co"></a> A set of files extracted from a [repository] that represent a particular [check-in](#ci) of the [project](#project). |
| ︙ | ︙ | |||
363 364 365 366 367 368 369 |
Fossil plugin for Visual Studio Code][fpvsc] defaults to storing the
repo clone within the project directory as a file called `.fsl`, but
this is because VSCode’s version control features assume it’s being
used with Git, where the repository is the `.git` subdirectory
contents. With Fossil, [different check-out workflows][cwork] are
preferred.
| | | | | 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 |
Fossil plugin for Visual Studio Code][fpvsc] defaults to storing the
repo clone within the project directory as a file called `.fsl`, but
this is because VSCode’s version control features assume it’s being
used with Git, where the repository is the `.git` subdirectory
contents. With Fossil, [different check-out workflows][cwork] are
preferred.
[commit]: /help/commit
[cwork]: ./ckout-workflows.md
[h2cflp]: https://www.sqlite.org/howtocorrupt.html#_file_locking_problems
[fpvsc]: https://marketplace.visualstudio.com/items?itemName=koog1000.fossil
[open]: /help/open
[mwd]: ./ckout-workflows.md#mcw
[update]: /help/update
## <a id="docs"></a>Embedded Documentation
Serving as an alternative to Fossil’s built-in [wiki], the [embedded
documentation feature][edoc] stores the same type of marked-up text
files, but under Fossil’s powerful version control features.
|
| ︙ | ︙ |
Changes to www/grep.md.
| ︙ | ︙ | |||
97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
| `\S` | Non-whitespace character: `[^ \t\r\n\v\f]`
There are several restrictions in Fossil `grep` relative to a fully
POSIX compatible regular expression engine. Among them are:
* There is currently no support for POSIX character classes such as
`[:lower:]`.
* Fossil `grep` does not currently attempt to take your operating
system's locale settings into account when doing this match. Since
Fossil has no way to mark a given file as having a particular
encoding, Fossil `grep` assumes the input files are in UTF-8 format.
This means Fossil `grep` will not work correctly if the files in
| > > > > > | 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
| `\S` | Non-whitespace character: `[^ \t\r\n\v\f]`
There are several restrictions in Fossil `grep` relative to a fully
POSIX compatible regular expression engine. Among them are:
* There is currently no support for POSIX character classes such as
`[:lower:]`.
* The values of `p` and `q` in the "`{p,q}`" syntax can be no greater
than 999. This is because the NFA that is used for regular expression
matching is proportional in size to the largest p or q value, and hence
allowing arbitrarily large values could result in a DoS attack.
* Fossil `grep` does not currently attempt to take your operating
system's locale settings into account when doing this match. Since
Fossil has no way to mark a given file as having a particular
encoding, Fossil `grep` assumes the input files are in UTF-8 format.
This means Fossil `grep` will not work correctly if the files in
|
| ︙ | ︙ |
Changes to www/hashes.md.
| ︙ | ︙ | |||
143 144 145 146 147 148 149 | [cin]: ./checkin_names.wiki [ctkt]: ./custom_ticket.wiki [hpol]: ./hashpolicy.wiki [japi]: ./json-api/ [jart]: ./json-api/api-artifact.md [jtim]: ./json-api/api-timeline.md | | | | 143 144 145 146 147 148 149 150 151 152 153 154 | [cin]: ./checkin_names.wiki [ctkt]: ./custom_ticket.wiki [hpol]: ./hashpolicy.wiki [japi]: ./json-api/ [jart]: ./json-api/api-artifact.md [jtim]: ./json-api/api-timeline.md [mset]: /help/manifest [th1]: ./th1.md [trss]: /help/www/timeline.rss [tvb]: ./branching.wiki [uuid]: https://en.wikipedia.org/wiki/Universally_unique_identifier |
Changes to www/hashpolicy.wiki.
| ︙ | ︙ | |||
156 157 158 159 160 161 162 | automatically switched to "sha3" mode and thereafter generated only SHA3 hashes. When a new repository is created by cloning, the hash policy is copied from the parent. For new repositories created using the | | | 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | automatically switched to "sha3" mode and thereafter generated only SHA3 hashes. When a new repository is created by cloning, the hash policy is copied from the parent. For new repositories created using the [/help/new|fossil new] command the default hash policy is "sha3". That means new repositories will normally hold nothing except SHA3 hashes. The hash policy for new repositories can be overridden using the "--sha1" option to the "fossil new" command. If you are still on Fossil 2.1 through 2.9 but you want Fossil to go ahead and start using SHA3 hashes, change the hash policy to |
| ︙ | ︙ |
Changes to www/hints.wiki.
1 2 3 4 5 6 7 |
<title>Fossil Tips And Usage Hints</title>
A collection of useful hints and tricks in no particular order:
1. Click on two nodes of any timeline graph in succession
to see a diff between the two versions.
| | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
<title>Fossil Tips And Usage Hints</title>
A collection of useful hints and tricks in no particular order:
1. Click on two nodes of any timeline graph in succession
to see a diff between the two versions.
2. Add the "--tk" option to "[/help/diff | fossil diff]" commands
to get a pop-up
window containing a complete side-by-side diff. (NB: The pop-up
window is run as a separate Tcl/Tk process, so you will need to
have Tcl/Tk installed on your machine for this to work. Visit
[http://www.activestate.com/activetcl] for a quick download of
Tcl/Tk if you do not already have it on your system.)
3. The "[/help/clean | fossil clean -x]" command is a great
alternative to "make clean". You can use "[/help/clean | fossil clean -f]"
as a slightly safer alternative if the "ignore-glob" setting is
not set. WARNING: make sure you did a "fossil add" for all source-files
you plan to commit, otherwise those files will be deleted without warning.
4. Use "[/help/all | fossil all changes]" to look for any uncommitted
edits in any of your Fossil projects. Use
"[/help/all | fossil all pull]" on your laptop
prior to going off network (for example, on a long plane ride)
to make sure you have all the latest content locally. Then run
"[/help/all|fossil all push]" when you get back online to upload
your changes.
5. To see an entire timeline, type "all" into the "Max:" entry box.
6. You can manually add a "c=CHECKIN" query parameter to the timeline
URL to get a snapshot of what was going on about the time of some
check-in. The "CHECKIN" can be
[./checkin_names.wiki | any valid check-in or version name], including
tags, branch names, and dates. For example, to see what was going
on in the Fossil repository on 2008-01-01, visit
[/timeline?c=2008-01-01].
7. Further to the previous two hints, there are lots of query parameters
that you can add to timeline pages. The available query parameters
are tersely documented [/help/www/timeline | here].
8. You can run "[/help/xdiff | fossil xdiff --tk $file1 $file2]"
to get a Tk pop-up window with side-by-side diffs of two files, even if
neither of the two files is part of any Fossil repository. Note that
this command is "xdiff", not "diff". Change <nobr>--tk</nobr> to
<nobr>--by</nobr> to see the diff in your web browser.
9. On web pages showing the content of a file (for example
[/artifact/c7dd1de9f]) you can manually
|
| ︙ | ︙ | |||
59 60 61 62 63 64 65 |
a mimetype of text/plain, of course.
10. When editing documentation to be checked in as managed files, you can
preview what the documentation will look like by using the special
"ckout" branch name in the "doc" URL while running "fossil ui".
See the [./embeddeddoc.wiki | embedded documentation] for details.
| | | | 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
a mimetype of text/plain, of course.
10. When editing documentation to be checked in as managed files, you can
preview what the documentation will look like by using the special
"ckout" branch name in the "doc" URL while running "fossil ui".
See the [./embeddeddoc.wiki | embedded documentation] for details.
11. Use the "[/help/ui|fossil ui /]" command to bring up a menu of
all of your local Fossil repositories in your web browser.
12. If you have a bunch of Fossil repositories living on a remote machine
that you are able to access using ssh using a command like
"ssh login@remote", then you can bring up a user interface for all
those remote repositories using the command:
"[/help/ui|fossil ui login@remote:/]". This works by tunneling
all HTTP traffic through SSH to the remote machine.
|
Changes to www/index.wiki.
| ︙ | ︙ | |||
82 83 84 85 86 87 88 |
atomic even if interrupted by a power loss or system crash.
Automatic [./selfcheck.wiki | self-checks] verify that all aspects of
the repository are consistent prior to each commit.
8. <b>Free and Open-Source</b> — [../COPYRIGHT-BSD2.txt|2-clause BSD license].
<hr>
| | | | | | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
atomic even if interrupted by a power loss or system crash.
Automatic [./selfcheck.wiki | self-checks] verify that all aspects of
the repository are consistent prior to each commit.
8. <b>Free and Open-Source</b> — [../COPYRIGHT-BSD2.txt|2-clause BSD license].
<hr>
<h3>Latest Release: 2.27 ([/timeline?c=version-2.27|2025-09-30])</h3>
* [/uv/download.html|Download]
* [./changes.wiki#v2_27|Change Summary]
* [/timeline?p=version-2.27&d=version-2.26&y=ci|Check-ins in version 2.27]
* [/timeline?df=version-2.27&y=ci|Check-ins derived from the 2.27 release]
* [/timeline?t=release|Timeline of all past releases]
<hr>
<h3>Quick Start</h3>
1. [/uv/download.html|Download] or install using a package manager or
[./build.wiki|compile from sources].
|
| ︙ | ︙ |
Changes to www/inout.wiki.
| ︙ | ︙ | |||
25 26 27 28 29 30 31 | interchange formats, and so for compatibility, use of the --git option is recommended. <a id="fx_git"></a> Note that in new imports, Fossil defaults to using the email component of the Git <em>committer</em> (or <em>author</em> if <code>--use-author</code> is passed) to attribute check-ins in the imported repository. Alternatively, the | | | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | interchange formats, and so for compatibility, use of the --git option is recommended. <a id="fx_git"></a> Note that in new imports, Fossil defaults to using the email component of the Git <em>committer</em> (or <em>author</em> if <code>--use-author</code> is passed) to attribute check-ins in the imported repository. Alternatively, the [/help/import | <code>--attribute</code>] option can be passed to have all commits by a given committer attributed to a desired username. This will create and populate the new <code>fx_git</code> table in the repository database to maintain a record of correspondent usernames and email addresses that can be used in subsequent exports or incremental imports. <h3>Converting Repositories on Windows</h3> |
| ︙ | ︙ |
Changes to www/interwiki.md.
| ︙ | ︙ | |||
62 63 64 65 66 67 68 | <a id="intermap"></a> ## Intermap The intermap defines a mapping from interwiki Tags to full URLs. The Intermap can be viewed and managed using the [fossil interwiki][iwiki] command or the [/intermap][imap] webpage. | | | 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | <a id="intermap"></a> ## Intermap The intermap defines a mapping from interwiki Tags to full URLs. The Intermap can be viewed and managed using the [fossil interwiki][iwiki] command or the [/intermap][imap] webpage. [iwiki]: /help/interwiki [imap]: /intermap The current intermap for a server is seen on the [/intermap][imap] page (which is read-only for non-Setup users) and at the bottom of the built-in [Fossil Wiki rules](/wiki_rules) and [Markdown rules](/md_rules) documentation pages. |
| ︙ | ︙ | |||
98 99 100 101 102 103 104 |
shows up in the "References" section of the target check-in.
([example](31af805348690958). In other words, Fossil tracks not just
"_source→target_", but it also tracks "_target→source_".
But backtracking does not work for interwiki links, since the Fossil
running on the target has no way of scanning the source text and
hence has no way of knowing that it is a target of a link from the source.
| | | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
shows up in the "References" section of the target check-in.
([example](31af805348690958). In other words, Fossil tracks not just
"_source→target_", but it also tracks "_target→source_".
But backtracking does not work for interwiki links, since the Fossil
running on the target has no way of scanning the source text and
hence has no way of knowing that it is a target of a link from the source.
[fcfg]: /help/config
## Intermap Storage Details
The intermap is stored in the CONFIG table of the repository database,
in entries with names of the form "<tt>interwiki:</tt><i>Tag</i>". The
value for each such entry is a JSON string that defines the base URL
and extensions for Hash and Wiki links.
## See Also
1. [](https://www.mediawiki.org/wiki/Manual:Interwiki)
2. [](https://duckduckgo.com/?q=interwiki+links&ia=web)
|
Changes to www/javascript.md.
| ︙ | ︙ | |||
261 262 263 264 265 266 267 | [2cbsd]: https://fossil-scm.org/home/doc/trunk/COPYRIGHT-BSD2.txt [ciu]: https://caniuse.com/ [cskin]: ./customskin.md [dcsp]: ./defcsp.md [es2015]: https://ecma-international.org/ecma-262/6.0/ [es6dep]: https://caniuse.com/#feat=es6 | | | | 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 | [2cbsd]: https://fossil-scm.org/home/doc/trunk/COPYRIGHT-BSD2.txt [ciu]: https://caniuse.com/ [cskin]: ./customskin.md [dcsp]: ./defcsp.md [es2015]: https://ecma-international.org/ecma-262/6.0/ [es6dep]: https://caniuse.com/#feat=es6 [fcgi]: /help/cgi [ffor]: https://fossil-scm.org/forum/ [flic]: /doc/trunk/COPYRIGHT-BSD2.txt [fshome]: /doc/trunk/www/server/ [fslpl]: /doc/trunk/www/fossil-v-git.wiki#portable [fsrc]: https://fossil-scm.org/home/file/src [fsrv]: /help/server [hljs]: https://fossil-scm.org/forum/forumpost/9150bc22ca [ie11x]: https://techcommunity.microsoft.com/t5/microsoft-365-blog/microsoft-365-apps-say-farewell-to-internet-explorer-11-and/ba-p/1591666 [ns]: https://noscript.net/ [pjs]: https://fossil-scm.org/forum/forumpost/1198651c6d [s1]: https://blockmetry.com/blog/javascript-disabled [s2]: https://gds.blog.gov.uk/2013/10/21/how-many-people-are-missing-out-on-javascript-enhancement/ [s3]: https://w3techs.com/technologies/overview/client_side_language/all |
| ︙ | ︙ | |||
388 389 390 391 392 393 394 | ...write, write, write yet more... :w !fossil wiki commit - # vi buffer updates article ``` Extending this concept to other text editors is an exercise left to the reader. | | | 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 | ...write, write, write yet more... :w !fossil wiki commit - # vi buffer updates article ``` Extending this concept to other text editors is an exercise left to the reader. [fwc]: /help/wiki [fwt]: ./wikitheory.wiki ### <a id="fedit"></a>The File Editor Fossil’s [optional file editor feature][fedit] works much like [the new wiki editor](#wedit), only on files committed to the |
| ︙ | ︙ | |||
428 429 430 431 432 433 434 | ### <a id="ln"></a>Line Numbering When viewing source files, Fossil offers to show line numbers in some cases. ([Example][mainc].) Toggling them on and off is currently handled in JavaScript, rather than forcing a page-reload via a button click. _Workaround:_ Manually edit the URL to give the “`ln`” query parameter | | | 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 | ### <a id="ln"></a>Line Numbering When viewing source files, Fossil offers to show line numbers in some cases. ([Example][mainc].) Toggling them on and off is currently handled in JavaScript, rather than forcing a page-reload via a button click. _Workaround:_ Manually edit the URL to give the “`ln`” query parameter per [the `/file` docs](/help/www/file). _Potential Better Workaround:_ Someone sufficiently interested could [provide a patch][cg] to add a `<noscript>` wrapped HTML button that would reload the page with this parameter included/excluded to implement the toggle via a server round-trip. A related feature is Fossil’s JavaScript-based interactive method |
| ︙ | ︙ |
Changes to www/json-api/intro.md.
| ︙ | ︙ | |||
144 145 146 147 148 149 150 |
- **Binary data:** JSON is a text serialization method, and it takes
up the “payload” area of each HTTP request, so there is no
reasonable way to include binary data in the JSON message without
some sort of codec like Base64, for which there is no provision in
the current JSON API. You will therefore find no JSON API for
committing changes to a file in the repository, for example. Other
| | | 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
- **Binary data:** JSON is a text serialization method, and it takes
up the “payload” area of each HTTP request, so there is no
reasonable way to include binary data in the JSON message without
some sort of codec like Base64, for which there is no provision in
the current JSON API. You will therefore find no JSON API for
committing changes to a file in the repository, for example. Other
Fossil APIs such as [`/raw`](/help/www/raw) or
[`/fileedit`](../fileedit-page.md) may serve you better.
- **64-bit integers:** The JSON standard does not specify integer precision,
because it targets many different platforms, and not all of
them can support more than 32 bits. JavaScript (from which JSON
derives) supports 53 bits of integer precision, which may affect how
a given client-side JSON implementation sends large integers to Fossil’s JSON
API. Our JSON parser can cope with integers larger than 32 bits on input, and it
|
| ︙ | ︙ |
Changes to www/loadmgmt.md.
| ︙ | ︙ | |||
86 87 88 89 90 91 92 | unable to find the load average. This can either be because it is in a `chroot(2)` jail without `/proc` access, or because it is running on a system that does not support `getloadavg()` and so the load-average limiter will not function. [503]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4 | | | 86 87 88 89 90 91 92 93 94 95 96 | unable to find the load average. This can either be because it is in a `chroot(2)` jail without `/proc` access, or because it is running on a system that does not support `getloadavg()` and so the load-average limiter will not function. [503]: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4 [hte]: /help/www/test-env [gla]: https://linux.die.net/man/3/getloadavg [lin]: http://www.linode.com [sh]: ./selfhost.wiki |
Changes to www/makefile.wiki.
| ︙ | ︙ | |||
39 40 41 42 43 44 45 | 6. shell.c All three SQLite source files are byte-for-byte copies of files by the same name in the standard [http://www.sqlite.org/amalgamation.html | amalgamation]. The sqlite3.c file implements the database engine. The shell.c file implements the command-line shell, which is accessed in fossil using | | | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | 6. shell.c All three SQLite source files are byte-for-byte copies of files by the same name in the standard [http://www.sqlite.org/amalgamation.html | amalgamation]. The sqlite3.c file implements the database engine. The shell.c file implements the command-line shell, which is accessed in fossil using the [/help/sqlite3 | fossil sql] command. The shell.c command-line shell uses the [https://github.com/antirez/linenoise | linenoise] library to implement line editing. linenoise comprises two source files which were copied from the upstream repository with only very minor portability edits: 7. linenoise.c |
| ︙ | ︙ | |||
70 71 72 73 74 75 76 | byte-array constants that contain various resources such as scripts and images. The builtin_data.h header file is generated from the original resource files using a small program called: 12 [/file/tools/mkbuiltin.c | mkbuiltin.c] Examples of built-in resources include the [/file/src/diff.tcl | diff.tcl] | | | 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | byte-array constants that contain various resources such as scripts and images. The builtin_data.h header file is generated from the original resource files using a small program called: 12 [/file/tools/mkbuiltin.c | mkbuiltin.c] Examples of built-in resources include the [/file/src/diff.tcl | diff.tcl] script used to implement the --tk option to [/help/diff| fossil diff], the [/file/src/markdown.md | markdown documentation], and the various CSS scripts, headers, and footers used to implement built-in skins. New resources files are added to the "extra_files" variable in [/file/tools/makemake.tcl | makemake.tcl]. The src/ subdirectory also contains documentation about the makeheaders preprocessor program: |
| ︙ | ︙ | |||
263 264 265 266 267 268 269 | * -DSQLITE_LIKE_DOESNT_MATCH_BLOBS=1 * -DSQLITE_THREADSAFE=0 * -DSQLITE_DEFAULT_FILE_FORMAT=4 * -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1 The first three symbol definitions above are required; the others are merely recommended. Extension loading is omitted as a security measure. The dbstat | | | | 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 | * -DSQLITE_LIKE_DOESNT_MATCH_BLOBS=1 * -DSQLITE_THREADSAFE=0 * -DSQLITE_DEFAULT_FILE_FORMAT=4 * -DSQLITE_ENABLE_EXPLAIN_COMMENTS=1 The first three symbol definitions above are required; the others are merely recommended. Extension loading is omitted as a security measure. The dbstat virtual table is needed for the [/help/www/repo-tabsize|/repo-tabsize] page. FTS4 is needed for the search feature. Fossil is single-threaded so mutexing is disabled in SQLite as a performance enhancement. The SQLITE_ENABLE_EXPLAIN_COMMENTS option makes the output of "EXPLAIN" queries in the "[/help/sqlite3|fossil sql]" command much more readable. When compiling the shell.c source file, these macros are required: * -Dmain=sqlite3_main * -DSQLITE_OMIT_LOAD_EXTENSION=1 The "main()" routine in the shell must be changed into sqlite3_main() |
| ︙ | ︙ |
Changes to www/mdtest/test1.md.
| ︙ | ︙ | |||
29 30 31 32 33 34 35 | The $ROOT prefix on markdown links is superfluous. The same link works without the $ROOT prefix. (Though: the $ROOT prefix is required for HTML documents.) * Timeline: [](/timeline) | | | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | The $ROOT prefix on markdown links is superfluous. The same link works without the $ROOT prefix. (Though: the $ROOT prefix is required for HTML documents.) * Timeline: [](/timeline) * Help: [](/help/help) * Site-map: [](/sitemap) ## The Magic $CURRENT Document Version Translation In URI text of the form `.../doc/$CURRENT/...` the $CURRENT value is converted to the version number of the document |
| ︙ | ︙ |
Changes to www/mirrorlimitations.md.
1 2 | # Limitations On Git Mirrors | | | 1 2 3 4 5 6 7 8 9 10 | # Limitations On Git Mirrors The "<tt>[fossil git export](/help/git)</tt>" command can be used to mirror a Fossil repository to Git. ([Setup instructions](./mirrortogithub.md) and an [example](https://github.com/drhsqlite/fossil-mirror).) But the export to Git is not perfect. Some information is lost during export due to limitations in Git. This page describes what content of Fossil is not included in an export to Git. |
| ︙ | ︙ |
Changes to www/mirrortogithub.md.
| ︙ | ︙ | |||
128 129 130 131 132 133 134 |
Fossil [UI][ui] browser window or with the [`user contact`][usercmd]
subcommand on the command line. Alternatively, if this repository was
previously imported from Git using the [`--attribute`][attr] option, the
[`fx_git`][fxgit] table will be queried for correspondent email addresses.
Only if neither of these methods produce a user specified email will the
abovementioned generic address be used.
| | | | | 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
Fossil [UI][ui] browser window or with the [`user contact`][usercmd]
subcommand on the command line. Alternatively, if this repository was
previously imported from Git using the [`--attribute`][attr] option, the
[`fx_git`][fxgit] table will be queried for correspondent email addresses.
Only if neither of these methods produce a user specified email will the
abovementioned generic address be used.
[attr]: /help/import
[fxgit]: ./inout.wiki#fx_git
[ui]: /help/ui
[usercmd]: /help/user
## <a id='ex1'></a>Example GitHub Mirrors
As of this writing (2019-03-16) Fossil’s own repository is mirrored
on GitHub at:
|
| ︙ | ︙ |
Changes to www/password.wiki.
| ︙ | ︙ | |||
62 63 64 65 66 67 68 | "developer", "reader", or "nobody" and the authentication protocol for "anonymous" uses one-time captchas not persistent passwords. <h2>Web Interface Authentication</h2> When a user logs into Fossil using the web interface, the login name and password are sent in the clear to the server. For most modern fossil | | | 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 | "developer", "reader", or "nobody" and the authentication protocol for "anonymous" uses one-time captchas not persistent passwords. <h2>Web Interface Authentication</h2> When a user logs into Fossil using the web interface, the login name and password are sent in the clear to the server. For most modern fossil server setups with [/help/redirect-to-https|redirect-to-https] enabled, this will be protected by the SSL connection over HTTPS so it cannot be easily viewed. The server then hashes the password and compares it against the value stored in USER.PW. If they match, the server sets a cookie on the client to record the login. This cookie contains a large amount of high-quality randomness and is thus intractable to guess. The value of the cookie and the IP address of the client is stored in the USER.COOKIE and USER.IPADDR fields |
| ︙ | ︙ |
Changes to www/patchcmd.md.
1 2 | # The "fossil patch" command | | | 1 2 3 4 5 6 7 8 9 10 | # The "fossil patch" command The "[fossil patch](/help/patch)" command is designed to transfer uncommitted changes from one check-out to another, including transfering those changes to other machines. For example, if you are working on a Windows desktop and you want to test your changes on a Linux server before you commit, you can use the "fossil patch push" command to make a copy of all your changes on the remote Linux server: |
| ︙ | ︙ |
Changes to www/private.wiki.
| ︙ | ︙ | |||
45 46 47 48 49 50 51 | <div class="sidebar"> To avoid generating a missing artifact reference on peer repositories without the private branch, the merge parent is not recorded when merging the private branch into a public branch. As a consequence, the web UI timeline does not draw a merge line from the private merge parent to the public merge child. Moreover, repeat private-to-public | | | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | <div class="sidebar"> To avoid generating a missing artifact reference on peer repositories without the private branch, the merge parent is not recorded when merging the private branch into a public branch. As a consequence, the web UI timeline does not draw a merge line from the private merge parent to the public merge child. Moreover, repeat private-to-public merge operations (without the [/help/merge | --force option]) with files added on the private branch may only work once, but later abort with "WARNING: no common ancestor for FILE", as the parent-child relationship is not recorded. (See the [/doc/trunk/www/branching.wiki | Branching, Forking, Merging, and Tagging] document for more information.) </div> The <code>--integrate</code> option of <code>fossil merge</code> (to close |
| ︙ | ︙ |
Changes to www/quickstart.wiki.
| ︙ | ︙ | |||
252 253 254 255 256 257 258 | the repository, being what you get when you "fossil open" a repository without specifying a version, populating the working directory. To see the most recent changes made to the repository by other users, use "fossil timeline" to find out the most recent commit, and then "fossil diff" between that commit and the current tree: | | | | | 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
the repository, being what you get when you "fossil open" a repository
without specifying a version, populating the working directory.
To see the most recent changes made to the repository by other users, use "fossil timeline" to
find out the most recent commit, and then "fossil diff" between that commit and the
current tree:
<pre><b><verbatim>fossil timeline
=== 2021-03-28 ===
03:18:54 [ad75dfa4a0] *CURRENT* Added details to frobnicate command (user: user-one tags: trunk)
=== 2021-03-27 ===
23:58:05 [ab975c6632] Update README.md. (user: user-two tags: trunk)
⋮
fossil diff --from current --to ab975c6632
Index: frobnicate.c
============================================================
--- frobnicate.c
+++ frobnicate.c
@@ -1,10 +1,11 @@
+/* made a change to the source file */
# Original text
</verbatim></b></pre>
"current" is an alias for the checkout version, so the command
"fossil diff --from ad75dfa4a0 --to ab975c6632" gives identical results.
To commit your changes to a local-only repository:
<pre><b>fossil commit</b> <i>(... Fossil will start your editor, if defined)</i><b>
|
| ︙ | ︙ | |||
294 295 296 297 298 299 300 | specified Fossil uses line-editing in the terminal. To commit your changes to a repository that was cloned from a remote repository, you give the same command, but the results are different. Fossil defaults to [./concepts.wiki#workflow|autosync] mode, a single-stage commit that sends all changes committed to the local repository immediately on to the remote parent repository. This only | | | 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 | specified Fossil uses line-editing in the terminal. To commit your changes to a repository that was cloned from a remote repository, you give the same command, but the results are different. Fossil defaults to [./concepts.wiki#workflow|autosync] mode, a single-stage commit that sends all changes committed to the local repository immediately on to the remote parent repository. This only works if you have write permission to the remote repository. <h2 id="naming">Naming of Files, Checkins, and Branches</h2> Fossil deals with information artifacts. This Quickstart document only deals with files and collections of files, but be aware there are also tickets, wiki pages and more. Every artifact in Fossil has a universally-unique hash id, and may also have a human-readable name. |
| ︙ | ︙ | |||
361 362 363 364 365 366 367 | (Contrast the [#server | many <i>other</i> ways] of setting Fossil up as an HTTP server, where the repo DB is on the other side of the HTTP server wall, inaccessible by all means other than Fossil's own mediation. For this reason, the "localhost bypasses access control" policy does <i>not</i> apply to these other interfaces. That is a very good thing, since without this difference in policy, it would be unsafe | | | 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 | (Contrast the [#server | many <i>other</i> ways] of setting Fossil up as an HTTP server, where the repo DB is on the other side of the HTTP server wall, inaccessible by all means other than Fossil's own mediation. For this reason, the "localhost bypasses access control" policy does <i>not</i> apply to these other interfaces. That is a very good thing, since without this difference in policy, it would be unsafe to bind a [/help/server | <b>fossil server</b>] instance to localhost on a high-numbered port and then reverse-proxy it out to the world via HTTPS, a practice this author does engage in, with confidence.) Once you are finished configuring Fossil, you may safely Control-C out of the <b>fossil ui</b> command to shut down this privileged built-in web server. Moreover, you may by grace of SQLite do this <i>at any time</i>: all changes are either committed durably to the repo DB or |
| ︙ | ︙ |
Changes to www/rebaseharm.md.
| ︙ | ︙ | |||
371 372 373 374 375 376 377 | developers to make intuitive leaps that the original developer was unable to make. In other words, you are asking your future maintenance developers to be smarter than the original developers! That's a beautiful wish, but there's a sharp limit to how far you can carry it. Eventually you hit the limits of human brilliance. When the operation of some bit of code is not obvious, both Fossil and | | | 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 | developers to make intuitive leaps that the original developer was unable to make. In other words, you are asking your future maintenance developers to be smarter than the original developers! That's a beautiful wish, but there's a sharp limit to how far you can carry it. Eventually you hit the limits of human brilliance. When the operation of some bit of code is not obvious, both Fossil and Git let you run a [`blame`](/help/blame) on the code file to get information about each line of code, and from that which check-in last touched a given line of code. If you squash the check-ins on a branch down to a single check-in, you throw away the information leading up to that finished form. Fossil not only preserves the check-ins surrounding the one that included the line of code you're trying to understand, its [superior data model][sdm] lets you see the surrounding check-ins in both directions; not only what lead up to it, but what came next. Git |
| ︙ | ︙ |
Changes to www/relatedwork.md.
| ︙ | ︙ | |||
64 65 66 67 68 69 70 | [ChiselApp]: https://chiselapp.com/ [CLion]: https://www.jetbrains.com/clion/ [corec66]: https://corecursive.com/066-sqlite-with-richard-hipp/ [Darcs]: http://darcs.net/ [db2w]: https://youtu.be/2eaQzahCeh4 [emacsfsl]: https://chiselapp.com/user/venks/repository/emacs-fossil/doc/tip/README.md [floss26]: https://twit.tv/shows/floss-weekly/episodes/26 | | | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | [ChiselApp]: https://chiselapp.com/ [CLion]: https://www.jetbrains.com/clion/ [corec66]: https://corecursive.com/066-sqlite-with-richard-hipp/ [Darcs]: http://darcs.net/ [db2w]: https://youtu.be/2eaQzahCeh4 [emacsfsl]: https://chiselapp.com/user/venks/repository/emacs-fossil/doc/tip/README.md [floss26]: https://twit.tv/shows/floss-weekly/episodes/26 [fnc]: https://fnc.sh [fsl]: http://fossil.0branch.com/fsl [Fuel]: https://fuel-scm.org/fossil/index [Git]: https://git-scm.com [GoLand]: https://www.jetbrains.com/go/ [got]: https://gameoftrees.org [Inskinerator]: https://tangentsoft.com/inskinerator [IntelliJ]: https://www.jetbrains.com/idea/ |
| ︙ | ︙ |
Changes to www/selfhost.wiki.
| ︙ | ︙ | |||
70 71 72 73 74 75 76 | /usr/local/bin/fossil all sync -u </pre> Server (2) is a <a href="http://www.linode.com/">Linode</a> located in Newark, NJ and set up just like the canonical server (1) with the addition of a cron job for synchronization. The same cron job also runs the | | | 70 71 72 73 74 75 76 77 78 | /usr/local/bin/fossil all sync -u </pre> Server (2) is a <a href="http://www.linode.com/">Linode</a> located in Newark, NJ and set up just like the canonical server (1) with the addition of a cron job for synchronization. The same cron job also runs the [/help/git|fossil git export] command after each sync in order to [./mirrortogithub.md#ex1|mirror all changes to GitHub]. |
Changes to www/server/index.html.
| ︙ | ︙ | |||
36 37 38 39 40 41 42 | Fossil server, with links to more detailed instructions specific to particular systems, should you want extra help.</p> <h2 id="prep">Repository Prep</h2> <p>Prior to serving a Fossil repository to others, consider running <a | | | | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | Fossil server, with links to more detailed instructions specific to particular systems, should you want extra help.</p> <h2 id="prep">Repository Prep</h2> <p>Prior to serving a Fossil repository to others, consider running <a href="$ROOT/help/ui"><tt>fossil ui</tt></a> locally and taking these minimum recommended preparation steps:</p> <ol> <li><p>Fossil creates only one user in a <a href="$ROOT/help/new">new repository</a> and gives it the <a href="../caps/admin-v-setup.md#apsu">all-powerful Setup capability</a>. The 10-digit random password generated for that user is fairly strong against remote attack, even without explicit password guess rate limiting, but because that user has so much power, you may want to give it a much stronger password under Admin → Users.</a></li> <li><p>Run the Admin → Security-Audit tool to verify that other |
| ︙ | ︙ | |||
93 94 95 96 97 98 99 | <h3 id="cgi">CGI</h3> <p>Most ordinary web servers can <a href="any/cgi.md">run Fossil as a CGI script</a>. This method is known to work with Apache, <tt>lighttpd</tt>, and <a href="any/althttpd.md"><tt>althttpd</tt></a>. The Fossil server | | | | | | 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 | <h3 id="cgi">CGI</h3> <p>Most ordinary web servers can <a href="any/cgi.md">run Fossil as a CGI script</a>. This method is known to work with Apache, <tt>lighttpd</tt>, and <a href="any/althttpd.md"><tt>althttpd</tt></a>. The Fossil server administrator places a <a href="$ROOT/help/cgi">short CGI script</a> in the web server's document hierarchy and when a client requests the URL that corresponds to that script, Fossil runs and generates the response.</p> <p>CGI is a good choice for merging Fossil into an existing web site, particularly on hosts that have CGI set up and working. The Fossil <a href="../selfhost.wiki">self-hosting repositories</a> are implemented with CGI underneath <tt>althttpd</tt>.</p> <h3 id="slist">Socket Listener</h3> <p>Socket listener daemons such as <a id="inetd" href="any/inetd.md"><tt>inetd</tt></a>, <a id="xinetd" href="any/xinetd.md"><tt>xinetd</tt></a>, <a id="stunnel" href="any/stunnel.md"><tt>stunnel</tt></a>, <a href="macos/service.md"><tt>launchd</tt></a>, and <a href="debian/service.md"><tt>systemd</tt></a> can be configured to invoke the the <a href="$ROOT/help/http"><tt>fossil http</tt></a> command to handle each incoming HTTP request. The "<tt>fossil http</tt>" command reads the HTTP request off of standard input, computes an appropriate reply, and writes the reply on standard output. There is a separate invocation of the "<tt>fossil http</tt>" command for each HTTP request. The socket listener daemon takes care of relaying content to and from the client, and (in the case of <a href="any/stunnel.md">stunnel</a>) handling TLS decryption and encryption. <h3 id="standalone">Stand-alone HTTP Server</h3> <p>This is the <a href="any/none.md">easiest method</a>. A stand-alone server uses the <a href="$ROOT/help/server"><tt>fossil server</tt></a> command to run a process that listens for incoming HTTP requests on a socket and then dispatches a copy of itself to deal with each incoming request. You can expose Fossil directly to the clients in this way or you can interpose a <a href="https://en.wikipedia.org/wiki/Reverse_proxy">reverse proxy</a> layer between the clients and Fossil.</p> <h3 id="scgi">SCGI</h3> <p>The Fossil standalone server can also handle <a href="any/scgi.md">SCGI</a>. When the <a href="$ROOT/help/server"><tt>fossil server</tt></a> command is run with the extra <tt>--scgi</tt> option, it listens for incoming SCGI requests rather than HTTP requests. This allows Fossil to respond to requests from web servers <a href="debian/nginx.md">such as nginx</a> that don't support CGI. SCGI is a simpler protocol to proxy than HTTP, since the HTTP doesn't have to be re-interpreted in terms of the proxy's existing HTTP implementation, but it's more complex to set up because you also have to set up an SCGI-to-HTTP proxy for it. It is |
| ︙ | ︙ | |||
285 286 287 288 289 290 291 | <li><p>If the repository includes <a href="../embeddeddoc.wiki">embedded documentation</a>, consider activating the search feature (Admin → Search) so that visitors can do full-text search on your documentation.</p></li> <li><p>Now that others can be making changes to the repository, consider monitoring them via <a href="../alerts.md">email alerts</a> | | | 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 | <li><p>If the repository includes <a href="../embeddeddoc.wiki">embedded documentation</a>, consider activating the search feature (Admin → Search) so that visitors can do full-text search on your documentation.</p></li> <li><p>Now that others can be making changes to the repository, consider monitoring them via <a href="../alerts.md">email alerts</a> or the <a href="$ROOT/help/www/timeline.rss">timeline RSS feed</a>.</p></li> <li><p>Turn on the various logging features.</p></li> </ol> <p>Reload the Admin → Security-Audit page occasionally during this process to double check that you have not mistakenly configured the |
| ︙ | ︙ |
Changes to www/server/windows/service.md.
| ︙ | ︙ | |||
60 61 62 63 64 65 66 | your system by accessing the `/test-env` webpage. Excluding this subdirectory will avoid certain rare failures where the fossil.exe process is unable to use the directory normally during a scan. ### <a id='PowerShell'></a>Advanced service installation using PowerShell As great as `fossil winsrv` is, it does not have one to one reflection of all of | | | | 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | your system by accessing the `/test-env` webpage. Excluding this subdirectory will avoid certain rare failures where the fossil.exe process is unable to use the directory normally during a scan. ### <a id='PowerShell'></a>Advanced service installation using PowerShell As great as `fossil winsrv` is, it does not have one to one reflection of all of the `fossil server` [options](/help/server). When you need to use some of the more advanced options, such as `--https`, `--skin`, or `--extroot`, you will need to use PowerShell to configure and install the Windows service. PowerShell provides the [New-Service](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.management/new-service?view=powershell-5.1) command, which we can use to install and configure Fossil as a service. The below should all be entered as a single line in an Administrative PowerShell console. ```PowerShell New-Service -Name fossil -DisplayName fossil -BinaryPathName '"C:\Program Files\FossilSCM\fossil.exe" server --port 8080 --repolist "D:/Path/to/Repos"' -StartupType Automatic ``` Please note the use of forward slashes in the repolist path passed to Fossil. Windows will accept either back slashes or forward slashes in path names, but Fossil has a preference for forward slashes. The use of `--repolist` will make this a multiple repository server. If you want to serve only a single repository, then leave off the `--repolist` parameter and provide the full path to the proper repository file. Other options are listed in the [fossil server](/help/server) documentation. The service will be installed by default to use the Local Service account. Since Fossil only needs access to local files, this is fine and causes no issues. The service will not be running once installed. You will need to start it to proceed (the `-StartupType Automatic` parameter to `New-Service` will result in the service auto-starting on boot). This can be done by entering |
| ︙ | ︙ |
Changes to www/serverext.wiki.
| ︙ | ︙ | |||
47 48 49 50 51 52 53 |
Files in the DOCUMENT_ROOT are accessed via URLs like this:
<pre>
https://example-project.org/ext/<i>FILENAME</i>
</pre>
In other words, access files in DOCUMENT_ROOT by appending the filename
| | | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
Files in the DOCUMENT_ROOT are accessed via URLs like this:
<pre>
https://example-project.org/ext/<i>FILENAME</i>
</pre>
In other words, access files in DOCUMENT_ROOT by appending the filename
relative to DOCUMENT_ROOT to the [/help/www/ext|/ext]
page of the Fossil server.
* Files that are readable but not executable are returned as static
content.
* Files that are executable are run as CGI.
|
| ︙ | ︙ | |||
122 123 124 125 126 127 128 | the user upload a file using a form, and then displays that file in the reply. There is a link on the page that causes the fileup1 script to return a copy of its own source-code, so you can see how it works. <h3>2.3 Example #3</h3> For Fossil versions dated 2025-03-23 and later, the "--extpage FILENAME" | | | 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | the user upload a file using a form, and then displays that file in the reply. There is a link on the page that causes the fileup1 script to return a copy of its own source-code, so you can see how it works. <h3>2.3 Example #3</h3> For Fossil versions dated 2025-03-23 and later, the "--extpage FILENAME" option to the [/help/ui|fossil ui] command is a short cut that treats FILENAME as a CGI extension. When the ui command starts up a new web browser pages, it points that page to the FILENAME extension. So if FILENAME is a static content file (such as an HTML file or [/md_rules|Markdown] or [/wiki_rules|Wiki] document), then the rendered content of the file is displayed. Meanwhile, the user can be editing the source text for that document in a separate window, and periodically pressing "Reload" on the web browser to instantly view the |
| ︙ | ︙ |
Changes to www/ssl-server.md.
1 2 3 4 5 | # SSL/TLS Server Mode ## History Fossil has supported [client-side SSL/TLS][0] since [2010][1]. This means | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | # SSL/TLS Server Mode ## History Fossil has supported [client-side SSL/TLS][0] since [2010][1]. This means that commands like "[fossil sync](/help/sync)" could use SSL/TLS when contacting a server. But on the server side, commands like "[fossil server](/help/server)" operated in clear-text only. To implement an encrypted server, you had to put Fossil behind a web server or reverse proxy that handled the SSL/TLS decryption/encryption and passed cleartext down to Fossil. [0]: ./ssl.wiki [1]: /timeline?c=b05cb4a0e15d0712&y=ci&n=13 Beginning in [late December 2021](/timeline?c=f6263bb64195b07f&y=a&n=13), Fossil servers are now able to converse directly over TLS. Commands like * "[fossil server](/help/server)" * "[fossil ui](/help/ui)", and * "[fossil http](/help/http)" may now handle the encryption natively when suitably configured, without requiring a third-party proxy layer. ## <a id="usage"></a>Usage To put any of the Fossil server commands into SSL/TLS mode, simply |
| ︙ | ︙ | |||
133 134 135 136 137 138 139 | that if you have a PEM-formatted private key and a separate PEM-formatted certificate, you can concatenate the two into a single file, and the individual components will still be easily accessible. ### <a id="cat"></a>Separate or Concatenated? Given a single concatenated file that holds both your private key and your | | | 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
that if you have a PEM-formatted private key and a separate PEM-formatted
certificate, you can concatenate the two into a single file, and the
individual components will still be easily accessible.
### <a id="cat"></a>Separate or Concatenated?
Given a single concatenated file that holds both your private key and your
cert, you can hand it off to the "[fossil server](/help/server)"
command using the `--cert` option, like this:
fossil server --port 443 --cert mycert.pem /home/www/myproject.fossil
The command above is sufficient to run a fully-encrypted web site for
the "myproject.fossil" Fossil repository. This command must be run as
root, since it wants to listen on TCP port 443, and only root processes are
|
| ︙ | ︙ | |||
255 256 257 258 259 260 261 |
6. It then deletes the secret one-time-use token it used to prove
domain control. ACME’s design precludes replay attacks.
In order for all of this to happen, certbot needs to be able to create
a subdirectory named ".well-known", within a directory you specify,
then populate that subdirectory with a token file of some kind. To support
| | | | 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
6. It then deletes the secret one-time-use token it used to prove
domain control. ACME’s design precludes replay attacks.
In order for all of this to happen, certbot needs to be able to create
a subdirectory named ".well-known", within a directory you specify,
then populate that subdirectory with a token file of some kind. To support
this, the "[fossil server](/help/server)" and
"[fossil http](/help/http)" commands have the --acme option.
When specified, Fossil sees a URL where the path
begins with ".well-known", then instead of doing its normal processing, it
looks for a file with that pathname and returns it to the client. If
the "server" or "http" command is referencing a single Fossil repository,
then the ".well-known" sub-directory should be in the same directory as
the repository file. If the "server" or "http" command are run against
|
| ︙ | ︙ |
Changes to www/sync.wiki.
| ︙ | ︙ | |||
22 23 24 25 26 27 28 | employed that usually reduce the number of hashes that need to be shared to a few dozen. Each repository also has local state. The local state determines the web-page formatting preferences, authorized users, ticket formats, and similar information that varies from one repository to another. The local state is not usually transferred during a sync. Except, | | | | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | employed that usually reduce the number of hashes that need to be shared to a few dozen. Each repository also has local state. The local state determines the web-page formatting preferences, authorized users, ticket formats, and similar information that varies from one repository to another. The local state is not usually transferred during a sync. Except, some local state is transferred during a [/help/clone|clone] in order to initialize the local state of the new repository. Also, an administrator can sync local state using the [/help/configuration|config push] and [/help/configuration|config pull] commands. <h3 id="crdt">1.1 Conflict-Free Replicated Datatypes</h3> The "bag of artifacts" data model used by Fossil is apparently an implementation of a particular [https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type|Conflict-Free |
| ︙ | ︙ | |||
54 55 56 57 58 59 60 | All communication between client and server is via HTTP requests. The server is listening for incoming HTTP requests. The client issues one or more HTTP requests and receives replies for each request. The server might be running as an independent server | | | | | | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | All communication between client and server is via HTTP requests. The server is listening for incoming HTTP requests. The client issues one or more HTTP requests and receives replies for each request. The server might be running as an independent server using the [/help/server|"fossil server" command], or it might be launched from inetd or xinetd using the [/help/http|"fossil http" command]. Or the server might be [./server/any/cgi.md|launched from CGI] or from [./server/any/scgi.md|SCGI]. (See "[./server/|How To Configure A Fossil Server]" for details.) The specifics of how the server listens for incoming HTTP requests is immaterial to this protocol. The important point is that the server is listening for requests and the client is the issuer of the requests. A single [/help/push|push], [/help/pull|pull], or [/help?cmd=sync|sync] might involve multiple HTTP requests. The client maintains state between all requests. But on the server side, each request is independent. The server does not preserve any information about the client from one request to the next. Note: Throughout this article, we use the terms "server" and "client" to represent the listener and initiator of the interaction, respectively. |
| ︙ | ︙ |
Changes to www/tech_overview.wiki.
| ︙ | ︙ | |||
160 161 162 163 164 165 166 | The second case is the one that usually determines the name. Note that the FOSSIL_HOME environment variable can always be set to determine the location of the configuration database. Note also that the configuration database file itself is called ".fossil" or "fossil.db" on unix but "_fossil" on windows. | | | 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | The second case is the one that usually determines the name. Note that the FOSSIL_HOME environment variable can always be set to determine the location of the configuration database. Note also that the configuration database file itself is called ".fossil" or "fossil.db" on unix but "_fossil" on windows. The [/help/info|fossil info] command will show the location of the configuration database on a line that starts with "config-db:". <h3>2.2 Repository Databases</h3> The repository database is the file that is commonly referred to as "the repository". This is because the repository database contains, among other things, the complete revision, ticket, and wiki history for |
| ︙ | ︙ |
Changes to www/th1.md.
| ︙ | ︙ | |||
129 130 131 132 133 134 135 | Beginning with Fossil version 2.26 (circa 2025), TH1 distinguishes between "tainted" and "untainted" strings. Tainted strings are strings that are derived from user inputs that might contain text that is designed to subvert the script. Untainted strings are known to come from secure sources and are assumed to contain no malicious content. Beginning with Fossil version 2.26, and depending on the value of the | | | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | Beginning with Fossil version 2.26 (circa 2025), TH1 distinguishes between "tainted" and "untainted" strings. Tainted strings are strings that are derived from user inputs that might contain text that is designed to subvert the script. Untainted strings are known to come from secure sources and are assumed to contain no malicious content. Beginning with Fossil version 2.26, and depending on the value of the [vuln-report setting](/help/vuln-report), TH1 will prevent tainted strings from being used in ways that might lead to XSS or SQL-injection attacks. This feature helps to ensure that XSS and SQL-injection vulnerabilities are not *accidentally* added to Fossil when custom TH1 scripts for headers or footers or tickets are added to a repository. Note that the tainted/untainted distinction in strings does not make it impossible to introduce XSS and SQL-injections vulnerabilities using poorly-written TH1 scripts; it just makes it more difficult and |
| ︙ | ︙ |
Changes to www/unvers.wiki.
| ︙ | ︙ | |||
22 23 24 25 26 27 28 | Unversioned files are intended to be accessible as web pages using URLs of the form: "<tt>https://example.com/cgi-script/<b>uv</b>/<i>FILENAME</i></tt>". In other words, the URI method "<b>uv</b>" (short for "unversioned") followed by the name of the unversioned file will retrieve the content of the file. The MIME type is inferred from the filename suffix. The content of unversioned files can also be retrieved using the | | | | | | | | 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | Unversioned files are intended to be accessible as web pages using URLs of the form: "<tt>https://example.com/cgi-script/<b>uv</b>/<i>FILENAME</i></tt>". In other words, the URI method "<b>uv</b>" (short for "unversioned") followed by the name of the unversioned file will retrieve the content of the file. The MIME type is inferred from the filename suffix. The content of unversioned files can also be retrieved using the [/help/unversioned|fossil unvers cat <i>FILENAME...</i>] or [/help/unversioned|fossil unvers export <i>FILENAME</i>] commands. A list of all unversioned files on a server can be seen using the [/help/www/uvlist|/uvlist] URL. ([/uvlist|example].) <h2>Syncing Unversioned Files</h2> Unversioned content does not sync between repositories by default. One must request it via commands such as: <pre> fossil sync <b>-u</b> fossil clone <b>-u</b> <i>URL local-repo-name</i> fossil unversioned sync </pre> The [/help/sync|fossil sync] and [/help/clone|fossil clone] commands will synchronize unversioned content if and only if they're given the "-u" (or "--unversioned") command-line option. The [/help/unversioned|fossil unversioned sync] command synchronizes the unversioned content without synchronizing anything else. Notice that the "-u" option does not work on [/help/push|fossil push] or [/help?cmd=pull|fossil pull]. The "-u" option is only available on "sync" and "clone". A rough equivalent of an unversioned pull would be the [/help?cmd=unversioned|fossil unversioned revert] command. The "unversioned revert" command causes the unversioned content on the local repository to be overwritten by the unversioned content found on the remote repository. |
| ︙ | ︙ |