Fossil SCM

Merge trunk

js 2025-05-18 02:50 morphos merge
Commit f993a870a130f4b58fdfdda465853ceb6de6206a109799e53940c50f091720ee
+1 -1
--- VERSION
+++ VERSION
@@ -1,1 +1,1 @@
1
-2.26
1
+2.27
22
--- VERSION
+++ VERSION
@@ -1,1 +1,1 @@
1 2.26
2
--- VERSION
+++ VERSION
@@ -1,1 +1,1 @@
1 2.27
2
+208 -192
--- extsrc/shell.c
+++ extsrc/shell.c
@@ -1622,34 +1622,10 @@
16221622
if( n>350 ) n = 350;
16231623
sqlite3_snprintf(sizeof(z), z, "%#+.*e", n, r);
16241624
sqlite3_result_text(pCtx, z, -1, SQLITE_TRANSIENT);
16251625
}
16261626
1627
-
1628
-/*
1629
-** SQL function: shell_module_schema(X)
1630
-**
1631
-** Return a fake schema for the table-valued function or eponymous virtual
1632
-** table X.
1633
-*/
1634
-static void shellModuleSchema(
1635
- sqlite3_context *pCtx,
1636
- int nVal,
1637
- sqlite3_value **apVal
1638
-){
1639
- const char *zName;
1640
- char *zFake;
1641
- UNUSED_PARAMETER(nVal);
1642
- zName = (const char*)sqlite3_value_text(apVal[0]);
1643
- zFake = zName? shellFakeSchema(sqlite3_context_db_handle(pCtx), 0, zName) : 0;
1644
- if( zFake ){
1645
- sqlite3_result_text(pCtx, sqlite3_mprintf("/* %s */", zFake),
1646
- -1, sqlite3_free);
1647
- free(zFake);
1648
- }
1649
-}
1650
-
16511627
/*
16521628
** SQL function: shell_add_schema(S,X)
16531629
**
16541630
** Add the schema name X to the CREATE statement in S and return the result.
16551631
** Examples:
@@ -1820,10 +1796,11 @@
18201796
# ifdef FILENAME_MAX
18211797
# define NAME_MAX (FILENAME_MAX)
18221798
# else
18231799
# define NAME_MAX (260)
18241800
# endif
1801
+# define DIRENT_NAME_MAX (NAME_MAX)
18251802
#endif
18261803
18271804
/*
18281805
** We need to define "NULL_INTPTR_T" and "BAD_INTPTR_T".
18291806
*/
@@ -1863,12 +1840,11 @@
18631840
#endif
18641841
18651842
/*
18661843
** Provide a macro, for use by the implementation, to determine if a
18671844
** particular directory entry should be skipped over when searching for
1868
-** the next directory entry that should be returned by the readdir() or
1869
-** readdir_r() functions.
1845
+** the next directory entry that should be returned by the readdir().
18701846
*/
18711847
18721848
#ifndef is_filtered
18731849
# define is_filtered(a) ((((a).attrib)&_A_HIDDEN) || (((a).attrib)&_A_SYSTEM))
18741850
#endif
@@ -1880,16 +1856,15 @@
18801856
18811857
extern const char *windirent_getenv(const char *name);
18821858
18831859
/*
18841860
** Finally, we can provide the function prototypes for the opendir(),
1885
-** readdir(), readdir_r(), and closedir() POSIX functions.
1861
+** readdir(), and closedir() POSIX functions.
18861862
*/
18871863
18881864
extern LPDIR opendir(const char *dirname);
18891865
extern LPDIRENT readdir(LPDIR dirp);
1890
-extern INT readdir_r(LPDIR dirp, LPDIRENT entry, LPDIRENT *result);
18911866
extern INT closedir(LPDIR dirp);
18921867
18931868
#endif /* defined(WIN32) && defined(_MSC_VER) */
18941869
18951870
/************************* End test_windirent.h ********************/
@@ -1942,27 +1917,45 @@
19421917
19431918
/*
19441919
** Implementation of the POSIX opendir() function using the MSVCRT.
19451920
*/
19461921
LPDIR opendir(
1947
- const char *dirname
1922
+ const char *dirname /* Directory name, UTF8 encoding */
19481923
){
1949
- struct _finddata_t data;
1924
+ struct _wfinddata_t data;
19501925
LPDIR dirp = (LPDIR)sqlite3_malloc(sizeof(DIR));
19511926
SIZE_T namesize = sizeof(data.name) / sizeof(data.name[0]);
1927
+ wchar_t *b1;
1928
+ sqlite3_int64 sz;
19521929
19531930
if( dirp==NULL ) return NULL;
19541931
memset(dirp, 0, sizeof(DIR));
19551932
19561933
/* TODO: Remove this if Unix-style root paths are not used. */
19571934
if( sqlite3_stricmp(dirname, "/")==0 ){
19581935
dirname = windirent_getenv("SystemDrive");
19591936
}
19601937
1961
- memset(&data, 0, sizeof(struct _finddata_t));
1962
- _snprintf(data.name, namesize, "%s\\*", dirname);
1963
- dirp->d_handle = _findfirst(data.name, &data);
1938
+ memset(&data, 0, sizeof(data));
1939
+ sz = strlen(dirname);
1940
+ b1 = sqlite3_malloc64( (sz+3)*sizeof(b1[0]) );
1941
+ if( b1==0 ){
1942
+ closedir(dirp);
1943
+ return NULL;
1944
+ }
1945
+ sz = MultiByteToWideChar(CP_UTF8, 0, dirname, sz, b1, sz);
1946
+ b1[sz++] = '\\';
1947
+ b1[sz++] = '*';
1948
+ b1[sz] = 0;
1949
+ if( sz+1>(sqlite3_int64)namesize ){
1950
+ closedir(dirp);
1951
+ sqlite3_free(b1);
1952
+ return NULL;
1953
+ }
1954
+ memcpy(data.name, b1, (sz+1)*sizeof(b1[0]));
1955
+ sqlite3_free(b1);
1956
+ dirp->d_handle = _wfindfirst(data.name, &data);
19641957
19651958
if( dirp->d_handle==BAD_INTPTR_T ){
19661959
closedir(dirp);
19671960
return NULL;
19681961
}
@@ -1969,34 +1962,33 @@
19691962
19701963
/* TODO: Remove this block to allow hidden and/or system files. */
19711964
if( is_filtered(data) ){
19721965
next:
19731966
1974
- memset(&data, 0, sizeof(struct _finddata_t));
1975
- if( _findnext(dirp->d_handle, &data)==-1 ){
1967
+ memset(&data, 0, sizeof(data));
1968
+ if( _wfindnext(dirp->d_handle, &data)==-1 ){
19761969
closedir(dirp);
19771970
return NULL;
19781971
}
19791972
19801973
/* TODO: Remove this block to allow hidden and/or system files. */
19811974
if( is_filtered(data) ) goto next;
19821975
}
19831976
19841977
dirp->d_first.d_attributes = data.attrib;
1985
- strncpy(dirp->d_first.d_name, data.name, NAME_MAX);
1986
- dirp->d_first.d_name[NAME_MAX] = '\0';
1987
-
1978
+ WideCharToMultiByte(CP_UTF8, 0, data.name, -1,
1979
+ dirp->d_first.d_name, DIRENT_NAME_MAX, 0, 0);
19881980
return dirp;
19891981
}
19901982
19911983
/*
19921984
** Implementation of the POSIX readdir() function using the MSVCRT.
19931985
*/
19941986
LPDIRENT readdir(
19951987
LPDIR dirp
19961988
){
1997
- struct _finddata_t data;
1989
+ struct _wfinddata_t data;
19981990
19991991
if( dirp==NULL ) return NULL;
20001992
20011993
if( dirp->d_first.d_ino==0 ){
20021994
dirp->d_first.d_ino++;
@@ -2005,69 +1997,23 @@
20051997
return &dirp->d_first;
20061998
}
20071999
20082000
next:
20092001
2010
- memset(&data, 0, sizeof(struct _finddata_t));
2011
- if( _findnext(dirp->d_handle, &data)==-1 ) return NULL;
2002
+ memset(&data, 0, sizeof(data));
2003
+ if( _wfindnext(dirp->d_handle, &data)==-1 ) return NULL;
20122004
20132005
/* TODO: Remove this block to allow hidden and/or system files. */
20142006
if( is_filtered(data) ) goto next;
20152007
20162008
dirp->d_next.d_ino++;
20172009
dirp->d_next.d_attributes = data.attrib;
2018
- strncpy(dirp->d_next.d_name, data.name, NAME_MAX);
2019
- dirp->d_next.d_name[NAME_MAX] = '\0';
2020
-
2010
+ WideCharToMultiByte(CP_UTF8, 0, data.name, -1,
2011
+ dirp->d_next.d_name, DIRENT_NAME_MAX, 0, 0);
20212012
return &dirp->d_next;
20222013
}
20232014
2024
-/*
2025
-** Implementation of the POSIX readdir_r() function using the MSVCRT.
2026
-*/
2027
-INT readdir_r(
2028
- LPDIR dirp,
2029
- LPDIRENT entry,
2030
- LPDIRENT *result
2031
-){
2032
- struct _finddata_t data;
2033
-
2034
- if( dirp==NULL ) return EBADF;
2035
-
2036
- if( dirp->d_first.d_ino==0 ){
2037
- dirp->d_first.d_ino++;
2038
- dirp->d_next.d_ino++;
2039
-
2040
- entry->d_ino = dirp->d_first.d_ino;
2041
- entry->d_attributes = dirp->d_first.d_attributes;
2042
- strncpy(entry->d_name, dirp->d_first.d_name, NAME_MAX);
2043
- entry->d_name[NAME_MAX] = '\0';
2044
-
2045
- *result = entry;
2046
- return 0;
2047
- }
2048
-
2049
-next:
2050
-
2051
- memset(&data, 0, sizeof(struct _finddata_t));
2052
- if( _findnext(dirp->d_handle, &data)==-1 ){
2053
- *result = NULL;
2054
- return ENOENT;
2055
- }
2056
-
2057
- /* TODO: Remove this block to allow hidden and/or system files. */
2058
- if( is_filtered(data) ) goto next;
2059
-
2060
- entry->d_ino = (ino_t)-1; /* not available */
2061
- entry->d_attributes = data.attrib;
2062
- strncpy(entry->d_name, data.name, NAME_MAX);
2063
- entry->d_name[NAME_MAX] = '\0';
2064
-
2065
- *result = entry;
2066
- return 0;
2067
-}
2068
-
20692015
/*
20702016
** Implementation of the POSIX closedir() function using the MSVCRT.
20712017
*/
20722018
INT closedir(
20732019
LPDIR dirp
@@ -8085,18 +8031,13 @@
80858031
# include "windows.h"
80868032
# include <io.h>
80878033
# include <direct.h>
80888034
/* # include "test_windirent.h" */
80898035
# define dirent DIRENT
8090
-# ifndef chmod
8091
-# define chmod _chmod
8092
-# endif
8093
-# ifndef stat
8094
-# define stat _stat
8095
-# endif
8096
-# define mkdir(path,mode) _mkdir(path)
8097
-# define lstat(path,buf) stat(path,buf)
8036
+# define stat _stat
8037
+# define chmod(path,mode) fileio_chmod(path,mode)
8038
+# define mkdir(path,mode) fileio_mkdir(path)
80988039
#endif
80998040
#include <time.h>
81008041
#include <errno.h>
81018042
81028043
/* When used as part of the CLI, the sqlite3_stdio.h module will have
@@ -8117,10 +8058,44 @@
81178058
#define FSDIR_COLUMN_MTIME 2 /* Last modification time */
81188059
#define FSDIR_COLUMN_DATA 3 /* File content */
81198060
#define FSDIR_COLUMN_PATH 4 /* Path to top of search */
81208061
#define FSDIR_COLUMN_DIR 5 /* Path is relative to this directory */
81218062
8063
+/*
8064
+** UTF8 chmod() function for Windows
8065
+*/
8066
+#if defined(_WIN32) || defined(WIN32)
8067
+static int fileio_chmod(const char *zPath, int pmode){
8068
+ sqlite3_int64 sz = strlen(zPath);
8069
+ wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) );
8070
+ int rc;
8071
+ if( b1==0 ) return -1;
8072
+ sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz);
8073
+ b1[sz] = 0;
8074
+ rc = _wchmod(b1, pmode);
8075
+ sqlite3_free(b1);
8076
+ return rc;
8077
+}
8078
+#endif
8079
+
8080
+/*
8081
+** UTF8 mkdir() function for Windows
8082
+*/
8083
+#if defined(_WIN32) || defined(WIN32)
8084
+static int fileio_mkdir(const char *zPath){
8085
+ sqlite3_int64 sz = strlen(zPath);
8086
+ wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) );
8087
+ int rc;
8088
+ if( b1==0 ) return -1;
8089
+ sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz);
8090
+ b1[sz] = 0;
8091
+ rc = _wmkdir(b1);
8092
+ sqlite3_free(b1);
8093
+ return rc;
8094
+}
8095
+#endif
8096
+
81228097
81238098
/*
81248099
** Set the result stored by context ctx to a blob containing the
81258100
** contents of file zName. Or, leave the result unchanged (NULL)
81268101
** if the file does not exist or is unreadable.
@@ -8278,11 +8253,17 @@
82788253
static int fileStat(
82798254
const char *zPath,
82808255
struct stat *pStatBuf
82818256
){
82828257
#if defined(_WIN32)
8283
- int rc = stat(zPath, pStatBuf);
8258
+ sqlite3_int64 sz = strlen(zPath);
8259
+ wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) );
8260
+ int rc;
8261
+ if( b1==0 ) return 1;
8262
+ sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz);
8263
+ b1[sz] = 0;
8264
+ rc = _wstat(b1, pStatBuf);
82848265
if( rc==0 ) statTimesToUtc(zPath, pStatBuf);
82858266
return rc;
82868267
#else
82878268
return stat(zPath, pStatBuf);
82888269
#endif
@@ -8296,13 +8277,11 @@
82968277
static int fileLinkStat(
82978278
const char *zPath,
82988279
struct stat *pStatBuf
82998280
){
83008281
#if defined(_WIN32)
8301
- int rc = lstat(zPath, pStatBuf);
8302
- if( rc==0 ) statTimesToUtc(zPath, pStatBuf);
8303
- return rc;
8282
+ return fileStat(zPath, pStatBuf);
83048283
#else
83058284
return lstat(zPath, pStatBuf);
83068285
#endif
83078286
}
83088287
@@ -16808,11 +16787,11 @@
1680816787
case SQLITE_FCNTL_POWERSAFE_OVERWRITE: zOp = "POWERSAFE_OVERWRITE"; break;
1680916788
case SQLITE_FCNTL_PRAGMA: {
1681016789
const char *const* a = (const char*const*)pArg;
1681116790
if( a[1] && strcmp(a[1],"vfstrace")==0 && a[2] ){
1681216791
const u8 *zArg = (const u8*)a[2];
16813
- if( zArg[0]>='0' && zArg[0]<=9 ){
16792
+ if( zArg[0]>='0' && zArg[0]<='9' ){
1681416793
pInfo->mTrace = (sqlite3_uint64)strtoll(a[2], 0, 0);
1681516794
}else{
1681616795
static const struct {
1681716796
const char *z;
1681816797
unsigned int m;
@@ -18709,10 +18688,13 @@
1870918688
rc = sqlite3_create_module(db, "sqlite_dbptr", &dbdata_module, (void*)1);
1871018689
}
1871118690
return rc;
1871218691
}
1871318692
18693
+#ifdef _WIN32
18694
+
18695
+#endif
1871418696
int sqlite3_dbdata_init(
1871518697
sqlite3 *db,
1871618698
char **pzErrMsg,
1871718699
const sqlite3_api_routines *pApi
1871818700
){
@@ -25592,107 +25574,108 @@
2559225574
" --plain Show results as text/plain, not as HTML",
2559325575
#endif
2559425576
};
2559525577
2559625578
/*
25597
-** Output help text.
25579
+** Output help text for commands that match zPattern.
25580
+**
25581
+** * If zPattern is NULL, then show all documented commands, but
25582
+** only give a one-line summary of each.
25583
+**
25584
+** * If zPattern is "-a" or "-all" or "--all" then show all help text
25585
+** for all commands except undocumented commands.
25586
+**
25587
+** * If zPattern is "0" then show all help for undocumented commands.
25588
+** Undocumented commands begin with "," instead of "." in the azHelp[]
25589
+** array.
25590
+**
25591
+** * If zPattern is a prefix for one or more documented commands, then
25592
+** show help for those commands. If only a single command matches the
25593
+** prefix, show the full text of the help. If multiple commands match,
25594
+** Only show just the first line of each.
2559825595
**
25599
-** zPattern describes the set of commands for which help text is provided.
25600
-** If zPattern is NULL, then show all commands, but only give a one-line
25601
-** description of each.
25596
+** * Otherwise, show the complete text of any documented command for which
25597
+** zPattern is a LIKE match for any text within that command help
25598
+** text.
2560225599
**
25603
-** Return the number of matches.
25600
+** Return the number commands that match zPattern.
2560425601
*/
2560525602
static int showHelp(FILE *out, const char *zPattern){
2560625603
int i = 0;
2560725604
int j = 0;
2560825605
int n = 0;
2560925606
char *zPat;
25610
- if( zPattern==0
25611
- || zPattern[0]=='0'
25612
- || cli_strcmp(zPattern,"-a")==0
25613
- || cli_strcmp(zPattern,"-all")==0
25614
- || cli_strcmp(zPattern,"--all")==0
25615
- ){
25616
- enum HelpWanted { HW_NoCull = 0, HW_SummaryOnly = 1, HW_Undoc = 2 };
25617
- enum HelpHave { HH_Undoc = 2, HH_Summary = 1, HH_More = 0 };
25618
- /* Show all or most commands
25619
- ** *zPattern==0 => summary of documented commands only
25620
- ** *zPattern=='0' => whole help for undocumented commands
25621
- ** Otherwise => whole help for documented commands
25622
- */
25623
- enum HelpWanted hw = HW_SummaryOnly;
25624
- enum HelpHave hh = HH_More;
25625
- if( zPattern!=0 ){
25626
- hw = (*zPattern=='0')? HW_NoCull|HW_Undoc : HW_NoCull;
25627
- }
25628
- for(i=0; i<ArraySize(azHelp); i++){
25629
- switch( azHelp[i][0] ){
25630
- case ',':
25631
- hh = HH_Summary|HH_Undoc;
25632
- break;
25633
- case '.':
25634
- hh = HH_Summary;
25635
- break;
25636
- default:
25637
- hh &= ~HH_Summary;
25638
- break;
25639
- }
25640
- if( ((hw^hh)&HH_Undoc)==0 ){
25641
- if( (hh&HH_Summary)!=0 ){
25642
- sqlite3_fprintf(out, ".%s\n", azHelp[i]+1);
25643
- ++n;
25644
- }else if( (hw&HW_SummaryOnly)==0 ){
25645
- sqlite3_fprintf(out, "%s\n", azHelp[i]);
25646
- }
25647
- }
25648
- }
25649
- }else{
25650
- /* Seek documented commands for which zPattern is an exact prefix */
25651
- zPat = sqlite3_mprintf(".%s*", zPattern);
25652
- shell_check_oom(zPat);
25653
- for(i=0; i<ArraySize(azHelp); i++){
25654
- if( sqlite3_strglob(zPat, azHelp[i])==0 ){
25655
- sqlite3_fprintf(out, "%s\n", azHelp[i]);
25656
- j = i+1;
25657
- n++;
25658
- }
25659
- }
25660
- sqlite3_free(zPat);
25661
- if( n ){
25662
- if( n==1 ){
25663
- /* when zPattern is a prefix of exactly one command, then include
25664
- ** the details of that command, which should begin at offset j */
25665
- while( j<ArraySize(azHelp)-1 && azHelp[j][0]==' ' ){
25666
- sqlite3_fprintf(out, "%s\n", azHelp[j]);
25667
- j++;
25668
- }
25669
- }
25670
- return n;
25671
- }
25672
- /* Look for documented commands that contain zPattern anywhere.
25673
- ** Show complete text of all documented commands that match. */
25674
- zPat = sqlite3_mprintf("%%%s%%", zPattern);
25675
- shell_check_oom(zPat);
25676
- for(i=0; i<ArraySize(azHelp); i++){
25677
- if( azHelp[i][0]==',' ){
25678
- while( i<ArraySize(azHelp)-1 && azHelp[i+1][0]==' ' ) ++i;
25679
- continue;
25680
- }
25681
- if( azHelp[i][0]=='.' ) j = i;
25682
- if( sqlite3_strlike(zPat, azHelp[i], 0)==0 ){
25683
- sqlite3_fprintf(out, "%s\n", azHelp[j]);
25684
- while( j<ArraySize(azHelp)-1 && azHelp[j+1][0]==' ' ){
25685
- j++;
25686
- sqlite3_fprintf(out, "%s\n", azHelp[j]);
25687
- }
25688
- i = j;
25689
- n++;
25690
- }
25691
- }
25692
- sqlite3_free(zPat);
25693
- }
25607
+ if( zPattern==0 ){
25608
+ /* Show just the first line for all help topics */
25609
+ zPattern = "[a-z]";
25610
+ }else if( cli_strcmp(zPattern,"-a")==0
25611
+ || cli_strcmp(zPattern,"-all")==0
25612
+ || cli_strcmp(zPattern,"--all")==0
25613
+ ){
25614
+ /* Show everything except undocumented commands */
25615
+ zPattern = ".";
25616
+ }else if( cli_strcmp(zPattern,"0")==0 ){
25617
+ /* Show complete help text of undocumented commands */
25618
+ int show = 0;
25619
+ for(i=0; i<ArraySize(azHelp); i++){
25620
+ if( azHelp[i][0]=='.' ){
25621
+ show = 0;
25622
+ }else if( azHelp[i][0]==',' ){
25623
+ show = 1;
25624
+ sqlite3_fprintf(out, ".%s\n", &azHelp[i][1]);
25625
+ n++;
25626
+ }else if( show ){
25627
+ sqlite3_fprintf(out, "%s\n", azHelp[i]);
25628
+ }
25629
+ }
25630
+ return n;
25631
+ }
25632
+
25633
+ /* Seek documented commands for which zPattern is an exact prefix */
25634
+ zPat = sqlite3_mprintf(".%s*", zPattern);
25635
+ shell_check_oom(zPat);
25636
+ for(i=0; i<ArraySize(azHelp); i++){
25637
+ if( sqlite3_strglob(zPat, azHelp[i])==0 ){
25638
+ sqlite3_fprintf(out, "%s\n", azHelp[i]);
25639
+ j = i+1;
25640
+ n++;
25641
+ }
25642
+ }
25643
+ sqlite3_free(zPat);
25644
+ if( n ){
25645
+ if( n==1 ){
25646
+ /* when zPattern is a prefix of exactly one command, then include
25647
+ ** the details of that command, which should begin at offset j */
25648
+ while( j<ArraySize(azHelp)-1 && azHelp[j][0]==' ' ){
25649
+ sqlite3_fprintf(out, "%s\n", azHelp[j]);
25650
+ j++;
25651
+ }
25652
+ }
25653
+ return n;
25654
+ }
25655
+
25656
+ /* Look for documented commands that contain zPattern anywhere.
25657
+ ** Show complete text of all documented commands that match. */
25658
+ zPat = sqlite3_mprintf("%%%s%%", zPattern);
25659
+ shell_check_oom(zPat);
25660
+ for(i=0; i<ArraySize(azHelp); i++){
25661
+ if( azHelp[i][0]==',' ){
25662
+ while( i<ArraySize(azHelp)-1 && azHelp[i+1][0]==' ' ) ++i;
25663
+ continue;
25664
+ }
25665
+ if( azHelp[i][0]=='.' ) j = i;
25666
+ if( sqlite3_strlike(zPat, azHelp[i], 0)==0 ){
25667
+ sqlite3_fprintf(out, "%s\n", azHelp[j]);
25668
+ while( j<ArraySize(azHelp)-1 && azHelp[j+1][0]==' ' ){
25669
+ j++;
25670
+ sqlite3_fprintf(out, "%s\n", azHelp[j]);
25671
+ }
25672
+ i = j;
25673
+ n++;
25674
+ }
25675
+ }
25676
+ sqlite3_free(zPat);
2569425677
return n;
2569525678
}
2569625679
2569725680
/* Forward reference */
2569825681
static int process_input(ShellState *p);
@@ -25937,10 +25920,43 @@
2593725920
int sleep = sqlite3_value_int(argv[0]);
2593825921
(void)argcUnused;
2593925922
sqlite3_sleep(sleep/1000);
2594025923
sqlite3_result_int(context, sleep);
2594125924
}
25925
+
25926
+/*
25927
+** SQL function: shell_module_schema(X)
25928
+**
25929
+** Return a fake schema for the table-valued function or eponymous virtual
25930
+** table X.
25931
+*/
25932
+static void shellModuleSchema(
25933
+ sqlite3_context *pCtx,
25934
+ int nVal,
25935
+ sqlite3_value **apVal
25936
+){
25937
+ const char *zName;
25938
+ char *zFake;
25939
+ ShellState *p = (ShellState*)sqlite3_user_data(pCtx);
25940
+ FILE *pSavedLog = p->pLog;
25941
+ UNUSED_PARAMETER(nVal);
25942
+ zName = (const char*)sqlite3_value_text(apVal[0]);
25943
+
25944
+ /* Temporarily disable the ".log" when calling shellFakeSchema() because
25945
+ ** shellFakeSchema() might generate failures for some ephemeral virtual
25946
+ ** tables due to missing arguments. Example: fts4aux.
25947
+ ** https://sqlite.org/forum/forumpost/42fe6520b803be51 */
25948
+ p->pLog = 0;
25949
+ zFake = zName? shellFakeSchema(sqlite3_context_db_handle(pCtx), 0, zName) : 0;
25950
+ p->pLog = pSavedLog;
25951
+
25952
+ if( zFake ){
25953
+ sqlite3_result_text(pCtx, sqlite3_mprintf("/* %s */", zFake),
25954
+ -1, sqlite3_free);
25955
+ free(zFake);
25956
+ }
25957
+}
2594225958
2594325959
/* Flags for open_db().
2594425960
**
2594525961
** The default behavior of open_db() is to exit(1) if the database fails to
2594625962
** open. The OPEN_DB_KEEPALIVE flag changes that so that it prints an error
@@ -26081,11 +26097,11 @@
2608126097
shellDtostr, 0, 0);
2608226098
sqlite3_create_function(p->db, "dtostr", 2, SQLITE_UTF8, 0,
2608326099
shellDtostr, 0, 0);
2608426100
sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0,
2608526101
shellAddSchemaName, 0, 0);
26086
- sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, 0,
26102
+ sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, p,
2608726103
shellModuleSchema, 0, 0);
2608826104
sqlite3_create_function(p->db, "shell_putsnl", 1, SQLITE_UTF8, p,
2608926105
shellPutsFunc, 0, 0);
2609026106
sqlite3_create_function(p->db, "usleep",1,SQLITE_UTF8,0,
2609126107
shellUSleepFunc, 0, 0);
2609226108
--- extsrc/shell.c
+++ extsrc/shell.c
@@ -1622,34 +1622,10 @@
1622 if( n>350 ) n = 350;
1623 sqlite3_snprintf(sizeof(z), z, "%#+.*e", n, r);
1624 sqlite3_result_text(pCtx, z, -1, SQLITE_TRANSIENT);
1625 }
1626
1627
1628 /*
1629 ** SQL function: shell_module_schema(X)
1630 **
1631 ** Return a fake schema for the table-valued function or eponymous virtual
1632 ** table X.
1633 */
1634 static void shellModuleSchema(
1635 sqlite3_context *pCtx,
1636 int nVal,
1637 sqlite3_value **apVal
1638 ){
1639 const char *zName;
1640 char *zFake;
1641 UNUSED_PARAMETER(nVal);
1642 zName = (const char*)sqlite3_value_text(apVal[0]);
1643 zFake = zName? shellFakeSchema(sqlite3_context_db_handle(pCtx), 0, zName) : 0;
1644 if( zFake ){
1645 sqlite3_result_text(pCtx, sqlite3_mprintf("/* %s */", zFake),
1646 -1, sqlite3_free);
1647 free(zFake);
1648 }
1649 }
1650
1651 /*
1652 ** SQL function: shell_add_schema(S,X)
1653 **
1654 ** Add the schema name X to the CREATE statement in S and return the result.
1655 ** Examples:
@@ -1820,10 +1796,11 @@
1820 # ifdef FILENAME_MAX
1821 # define NAME_MAX (FILENAME_MAX)
1822 # else
1823 # define NAME_MAX (260)
1824 # endif
 
1825 #endif
1826
1827 /*
1828 ** We need to define "NULL_INTPTR_T" and "BAD_INTPTR_T".
1829 */
@@ -1863,12 +1840,11 @@
1863 #endif
1864
1865 /*
1866 ** Provide a macro, for use by the implementation, to determine if a
1867 ** particular directory entry should be skipped over when searching for
1868 ** the next directory entry that should be returned by the readdir() or
1869 ** readdir_r() functions.
1870 */
1871
1872 #ifndef is_filtered
1873 # define is_filtered(a) ((((a).attrib)&_A_HIDDEN) || (((a).attrib)&_A_SYSTEM))
1874 #endif
@@ -1880,16 +1856,15 @@
1880
1881 extern const char *windirent_getenv(const char *name);
1882
1883 /*
1884 ** Finally, we can provide the function prototypes for the opendir(),
1885 ** readdir(), readdir_r(), and closedir() POSIX functions.
1886 */
1887
1888 extern LPDIR opendir(const char *dirname);
1889 extern LPDIRENT readdir(LPDIR dirp);
1890 extern INT readdir_r(LPDIR dirp, LPDIRENT entry, LPDIRENT *result);
1891 extern INT closedir(LPDIR dirp);
1892
1893 #endif /* defined(WIN32) && defined(_MSC_VER) */
1894
1895 /************************* End test_windirent.h ********************/
@@ -1942,27 +1917,45 @@
1942
1943 /*
1944 ** Implementation of the POSIX opendir() function using the MSVCRT.
1945 */
1946 LPDIR opendir(
1947 const char *dirname
1948 ){
1949 struct _finddata_t data;
1950 LPDIR dirp = (LPDIR)sqlite3_malloc(sizeof(DIR));
1951 SIZE_T namesize = sizeof(data.name) / sizeof(data.name[0]);
 
 
1952
1953 if( dirp==NULL ) return NULL;
1954 memset(dirp, 0, sizeof(DIR));
1955
1956 /* TODO: Remove this if Unix-style root paths are not used. */
1957 if( sqlite3_stricmp(dirname, "/")==0 ){
1958 dirname = windirent_getenv("SystemDrive");
1959 }
1960
1961 memset(&data, 0, sizeof(struct _finddata_t));
1962 _snprintf(data.name, namesize, "%s\\*", dirname);
1963 dirp->d_handle = _findfirst(data.name, &data);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1964
1965 if( dirp->d_handle==BAD_INTPTR_T ){
1966 closedir(dirp);
1967 return NULL;
1968 }
@@ -1969,34 +1962,33 @@
1969
1970 /* TODO: Remove this block to allow hidden and/or system files. */
1971 if( is_filtered(data) ){
1972 next:
1973
1974 memset(&data, 0, sizeof(struct _finddata_t));
1975 if( _findnext(dirp->d_handle, &data)==-1 ){
1976 closedir(dirp);
1977 return NULL;
1978 }
1979
1980 /* TODO: Remove this block to allow hidden and/or system files. */
1981 if( is_filtered(data) ) goto next;
1982 }
1983
1984 dirp->d_first.d_attributes = data.attrib;
1985 strncpy(dirp->d_first.d_name, data.name, NAME_MAX);
1986 dirp->d_first.d_name[NAME_MAX] = '\0';
1987
1988 return dirp;
1989 }
1990
1991 /*
1992 ** Implementation of the POSIX readdir() function using the MSVCRT.
1993 */
1994 LPDIRENT readdir(
1995 LPDIR dirp
1996 ){
1997 struct _finddata_t data;
1998
1999 if( dirp==NULL ) return NULL;
2000
2001 if( dirp->d_first.d_ino==0 ){
2002 dirp->d_first.d_ino++;
@@ -2005,69 +1997,23 @@
2005 return &dirp->d_first;
2006 }
2007
2008 next:
2009
2010 memset(&data, 0, sizeof(struct _finddata_t));
2011 if( _findnext(dirp->d_handle, &data)==-1 ) return NULL;
2012
2013 /* TODO: Remove this block to allow hidden and/or system files. */
2014 if( is_filtered(data) ) goto next;
2015
2016 dirp->d_next.d_ino++;
2017 dirp->d_next.d_attributes = data.attrib;
2018 strncpy(dirp->d_next.d_name, data.name, NAME_MAX);
2019 dirp->d_next.d_name[NAME_MAX] = '\0';
2020
2021 return &dirp->d_next;
2022 }
2023
2024 /*
2025 ** Implementation of the POSIX readdir_r() function using the MSVCRT.
2026 */
2027 INT readdir_r(
2028 LPDIR dirp,
2029 LPDIRENT entry,
2030 LPDIRENT *result
2031 ){
2032 struct _finddata_t data;
2033
2034 if( dirp==NULL ) return EBADF;
2035
2036 if( dirp->d_first.d_ino==0 ){
2037 dirp->d_first.d_ino++;
2038 dirp->d_next.d_ino++;
2039
2040 entry->d_ino = dirp->d_first.d_ino;
2041 entry->d_attributes = dirp->d_first.d_attributes;
2042 strncpy(entry->d_name, dirp->d_first.d_name, NAME_MAX);
2043 entry->d_name[NAME_MAX] = '\0';
2044
2045 *result = entry;
2046 return 0;
2047 }
2048
2049 next:
2050
2051 memset(&data, 0, sizeof(struct _finddata_t));
2052 if( _findnext(dirp->d_handle, &data)==-1 ){
2053 *result = NULL;
2054 return ENOENT;
2055 }
2056
2057 /* TODO: Remove this block to allow hidden and/or system files. */
2058 if( is_filtered(data) ) goto next;
2059
2060 entry->d_ino = (ino_t)-1; /* not available */
2061 entry->d_attributes = data.attrib;
2062 strncpy(entry->d_name, data.name, NAME_MAX);
2063 entry->d_name[NAME_MAX] = '\0';
2064
2065 *result = entry;
2066 return 0;
2067 }
2068
2069 /*
2070 ** Implementation of the POSIX closedir() function using the MSVCRT.
2071 */
2072 INT closedir(
2073 LPDIR dirp
@@ -8085,18 +8031,13 @@
8085 # include "windows.h"
8086 # include <io.h>
8087 # include <direct.h>
8088 /* # include "test_windirent.h" */
8089 # define dirent DIRENT
8090 # ifndef chmod
8091 # define chmod _chmod
8092 # endif
8093 # ifndef stat
8094 # define stat _stat
8095 # endif
8096 # define mkdir(path,mode) _mkdir(path)
8097 # define lstat(path,buf) stat(path,buf)
8098 #endif
8099 #include <time.h>
8100 #include <errno.h>
8101
8102 /* When used as part of the CLI, the sqlite3_stdio.h module will have
@@ -8117,10 +8058,44 @@
8117 #define FSDIR_COLUMN_MTIME 2 /* Last modification time */
8118 #define FSDIR_COLUMN_DATA 3 /* File content */
8119 #define FSDIR_COLUMN_PATH 4 /* Path to top of search */
8120 #define FSDIR_COLUMN_DIR 5 /* Path is relative to this directory */
8121
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8122
8123 /*
8124 ** Set the result stored by context ctx to a blob containing the
8125 ** contents of file zName. Or, leave the result unchanged (NULL)
8126 ** if the file does not exist or is unreadable.
@@ -8278,11 +8253,17 @@
8278 static int fileStat(
8279 const char *zPath,
8280 struct stat *pStatBuf
8281 ){
8282 #if defined(_WIN32)
8283 int rc = stat(zPath, pStatBuf);
 
 
 
 
 
 
8284 if( rc==0 ) statTimesToUtc(zPath, pStatBuf);
8285 return rc;
8286 #else
8287 return stat(zPath, pStatBuf);
8288 #endif
@@ -8296,13 +8277,11 @@
8296 static int fileLinkStat(
8297 const char *zPath,
8298 struct stat *pStatBuf
8299 ){
8300 #if defined(_WIN32)
8301 int rc = lstat(zPath, pStatBuf);
8302 if( rc==0 ) statTimesToUtc(zPath, pStatBuf);
8303 return rc;
8304 #else
8305 return lstat(zPath, pStatBuf);
8306 #endif
8307 }
8308
@@ -16808,11 +16787,11 @@
16808 case SQLITE_FCNTL_POWERSAFE_OVERWRITE: zOp = "POWERSAFE_OVERWRITE"; break;
16809 case SQLITE_FCNTL_PRAGMA: {
16810 const char *const* a = (const char*const*)pArg;
16811 if( a[1] && strcmp(a[1],"vfstrace")==0 && a[2] ){
16812 const u8 *zArg = (const u8*)a[2];
16813 if( zArg[0]>='0' && zArg[0]<=9 ){
16814 pInfo->mTrace = (sqlite3_uint64)strtoll(a[2], 0, 0);
16815 }else{
16816 static const struct {
16817 const char *z;
16818 unsigned int m;
@@ -18709,10 +18688,13 @@
18709 rc = sqlite3_create_module(db, "sqlite_dbptr", &dbdata_module, (void*)1);
18710 }
18711 return rc;
18712 }
18713
 
 
 
18714 int sqlite3_dbdata_init(
18715 sqlite3 *db,
18716 char **pzErrMsg,
18717 const sqlite3_api_routines *pApi
18718 ){
@@ -25592,107 +25574,108 @@
25592 " --plain Show results as text/plain, not as HTML",
25593 #endif
25594 };
25595
25596 /*
25597 ** Output help text.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25598 **
25599 ** zPattern describes the set of commands for which help text is provided.
25600 ** If zPattern is NULL, then show all commands, but only give a one-line
25601 ** description of each.
25602 **
25603 ** Return the number of matches.
25604 */
25605 static int showHelp(FILE *out, const char *zPattern){
25606 int i = 0;
25607 int j = 0;
25608 int n = 0;
25609 char *zPat;
25610 if( zPattern==0
25611 || zPattern[0]=='0'
25612 || cli_strcmp(zPattern,"-a")==0
25613 || cli_strcmp(zPattern,"-all")==0
25614 || cli_strcmp(zPattern,"--all")==0
25615 ){
25616 enum HelpWanted { HW_NoCull = 0, HW_SummaryOnly = 1, HW_Undoc = 2 };
25617 enum HelpHave { HH_Undoc = 2, HH_Summary = 1, HH_More = 0 };
25618 /* Show all or most commands
25619 ** *zPattern==0 => summary of documented commands only
25620 ** *zPattern=='0' => whole help for undocumented commands
25621 ** Otherwise => whole help for documented commands
25622 */
25623 enum HelpWanted hw = HW_SummaryOnly;
25624 enum HelpHave hh = HH_More;
25625 if( zPattern!=0 ){
25626 hw = (*zPattern=='0')? HW_NoCull|HW_Undoc : HW_NoCull;
25627 }
25628 for(i=0; i<ArraySize(azHelp); i++){
25629 switch( azHelp[i][0] ){
25630 case ',':
25631 hh = HH_Summary|HH_Undoc;
25632 break;
25633 case '.':
25634 hh = HH_Summary;
25635 break;
25636 default:
25637 hh &= ~HH_Summary;
25638 break;
25639 }
25640 if( ((hw^hh)&HH_Undoc)==0 ){
25641 if( (hh&HH_Summary)!=0 ){
25642 sqlite3_fprintf(out, ".%s\n", azHelp[i]+1);
25643 ++n;
25644 }else if( (hw&HW_SummaryOnly)==0 ){
25645 sqlite3_fprintf(out, "%s\n", azHelp[i]);
25646 }
25647 }
25648 }
25649 }else{
25650 /* Seek documented commands for which zPattern is an exact prefix */
25651 zPat = sqlite3_mprintf(".%s*", zPattern);
25652 shell_check_oom(zPat);
25653 for(i=0; i<ArraySize(azHelp); i++){
25654 if( sqlite3_strglob(zPat, azHelp[i])==0 ){
25655 sqlite3_fprintf(out, "%s\n", azHelp[i]);
25656 j = i+1;
25657 n++;
25658 }
25659 }
25660 sqlite3_free(zPat);
25661 if( n ){
25662 if( n==1 ){
25663 /* when zPattern is a prefix of exactly one command, then include
25664 ** the details of that command, which should begin at offset j */
25665 while( j<ArraySize(azHelp)-1 && azHelp[j][0]==' ' ){
25666 sqlite3_fprintf(out, "%s\n", azHelp[j]);
25667 j++;
25668 }
25669 }
25670 return n;
25671 }
25672 /* Look for documented commands that contain zPattern anywhere.
25673 ** Show complete text of all documented commands that match. */
25674 zPat = sqlite3_mprintf("%%%s%%", zPattern);
25675 shell_check_oom(zPat);
25676 for(i=0; i<ArraySize(azHelp); i++){
25677 if( azHelp[i][0]==',' ){
25678 while( i<ArraySize(azHelp)-1 && azHelp[i+1][0]==' ' ) ++i;
25679 continue;
25680 }
25681 if( azHelp[i][0]=='.' ) j = i;
25682 if( sqlite3_strlike(zPat, azHelp[i], 0)==0 ){
25683 sqlite3_fprintf(out, "%s\n", azHelp[j]);
25684 while( j<ArraySize(azHelp)-1 && azHelp[j+1][0]==' ' ){
25685 j++;
25686 sqlite3_fprintf(out, "%s\n", azHelp[j]);
25687 }
25688 i = j;
25689 n++;
25690 }
25691 }
25692 sqlite3_free(zPat);
25693 }
25694 return n;
25695 }
25696
25697 /* Forward reference */
25698 static int process_input(ShellState *p);
@@ -25937,10 +25920,43 @@
25937 int sleep = sqlite3_value_int(argv[0]);
25938 (void)argcUnused;
25939 sqlite3_sleep(sleep/1000);
25940 sqlite3_result_int(context, sleep);
25941 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25942
25943 /* Flags for open_db().
25944 **
25945 ** The default behavior of open_db() is to exit(1) if the database fails to
25946 ** open. The OPEN_DB_KEEPALIVE flag changes that so that it prints an error
@@ -26081,11 +26097,11 @@
26081 shellDtostr, 0, 0);
26082 sqlite3_create_function(p->db, "dtostr", 2, SQLITE_UTF8, 0,
26083 shellDtostr, 0, 0);
26084 sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0,
26085 shellAddSchemaName, 0, 0);
26086 sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, 0,
26087 shellModuleSchema, 0, 0);
26088 sqlite3_create_function(p->db, "shell_putsnl", 1, SQLITE_UTF8, p,
26089 shellPutsFunc, 0, 0);
26090 sqlite3_create_function(p->db, "usleep",1,SQLITE_UTF8,0,
26091 shellUSleepFunc, 0, 0);
26092
--- extsrc/shell.c
+++ extsrc/shell.c
@@ -1622,34 +1622,10 @@
1622 if( n>350 ) n = 350;
1623 sqlite3_snprintf(sizeof(z), z, "%#+.*e", n, r);
1624 sqlite3_result_text(pCtx, z, -1, SQLITE_TRANSIENT);
1625 }
1626
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1627 /*
1628 ** SQL function: shell_add_schema(S,X)
1629 **
1630 ** Add the schema name X to the CREATE statement in S and return the result.
1631 ** Examples:
@@ -1820,10 +1796,11 @@
1796 # ifdef FILENAME_MAX
1797 # define NAME_MAX (FILENAME_MAX)
1798 # else
1799 # define NAME_MAX (260)
1800 # endif
1801 # define DIRENT_NAME_MAX (NAME_MAX)
1802 #endif
1803
1804 /*
1805 ** We need to define "NULL_INTPTR_T" and "BAD_INTPTR_T".
1806 */
@@ -1863,12 +1840,11 @@
1840 #endif
1841
1842 /*
1843 ** Provide a macro, for use by the implementation, to determine if a
1844 ** particular directory entry should be skipped over when searching for
1845 ** the next directory entry that should be returned by the readdir().
 
1846 */
1847
1848 #ifndef is_filtered
1849 # define is_filtered(a) ((((a).attrib)&_A_HIDDEN) || (((a).attrib)&_A_SYSTEM))
1850 #endif
@@ -1880,16 +1856,15 @@
1856
1857 extern const char *windirent_getenv(const char *name);
1858
1859 /*
1860 ** Finally, we can provide the function prototypes for the opendir(),
1861 ** readdir(), and closedir() POSIX functions.
1862 */
1863
1864 extern LPDIR opendir(const char *dirname);
1865 extern LPDIRENT readdir(LPDIR dirp);
 
1866 extern INT closedir(LPDIR dirp);
1867
1868 #endif /* defined(WIN32) && defined(_MSC_VER) */
1869
1870 /************************* End test_windirent.h ********************/
@@ -1942,27 +1917,45 @@
1917
1918 /*
1919 ** Implementation of the POSIX opendir() function using the MSVCRT.
1920 */
1921 LPDIR opendir(
1922 const char *dirname /* Directory name, UTF8 encoding */
1923 ){
1924 struct _wfinddata_t data;
1925 LPDIR dirp = (LPDIR)sqlite3_malloc(sizeof(DIR));
1926 SIZE_T namesize = sizeof(data.name) / sizeof(data.name[0]);
1927 wchar_t *b1;
1928 sqlite3_int64 sz;
1929
1930 if( dirp==NULL ) return NULL;
1931 memset(dirp, 0, sizeof(DIR));
1932
1933 /* TODO: Remove this if Unix-style root paths are not used. */
1934 if( sqlite3_stricmp(dirname, "/")==0 ){
1935 dirname = windirent_getenv("SystemDrive");
1936 }
1937
1938 memset(&data, 0, sizeof(data));
1939 sz = strlen(dirname);
1940 b1 = sqlite3_malloc64( (sz+3)*sizeof(b1[0]) );
1941 if( b1==0 ){
1942 closedir(dirp);
1943 return NULL;
1944 }
1945 sz = MultiByteToWideChar(CP_UTF8, 0, dirname, sz, b1, sz);
1946 b1[sz++] = '\\';
1947 b1[sz++] = '*';
1948 b1[sz] = 0;
1949 if( sz+1>(sqlite3_int64)namesize ){
1950 closedir(dirp);
1951 sqlite3_free(b1);
1952 return NULL;
1953 }
1954 memcpy(data.name, b1, (sz+1)*sizeof(b1[0]));
1955 sqlite3_free(b1);
1956 dirp->d_handle = _wfindfirst(data.name, &data);
1957
1958 if( dirp->d_handle==BAD_INTPTR_T ){
1959 closedir(dirp);
1960 return NULL;
1961 }
@@ -1969,34 +1962,33 @@
1962
1963 /* TODO: Remove this block to allow hidden and/or system files. */
1964 if( is_filtered(data) ){
1965 next:
1966
1967 memset(&data, 0, sizeof(data));
1968 if( _wfindnext(dirp->d_handle, &data)==-1 ){
1969 closedir(dirp);
1970 return NULL;
1971 }
1972
1973 /* TODO: Remove this block to allow hidden and/or system files. */
1974 if( is_filtered(data) ) goto next;
1975 }
1976
1977 dirp->d_first.d_attributes = data.attrib;
1978 WideCharToMultiByte(CP_UTF8, 0, data.name, -1,
1979 dirp->d_first.d_name, DIRENT_NAME_MAX, 0, 0);
 
1980 return dirp;
1981 }
1982
1983 /*
1984 ** Implementation of the POSIX readdir() function using the MSVCRT.
1985 */
1986 LPDIRENT readdir(
1987 LPDIR dirp
1988 ){
1989 struct _wfinddata_t data;
1990
1991 if( dirp==NULL ) return NULL;
1992
1993 if( dirp->d_first.d_ino==0 ){
1994 dirp->d_first.d_ino++;
@@ -2005,69 +1997,23 @@
1997 return &dirp->d_first;
1998 }
1999
2000 next:
2001
2002 memset(&data, 0, sizeof(data));
2003 if( _wfindnext(dirp->d_handle, &data)==-1 ) return NULL;
2004
2005 /* TODO: Remove this block to allow hidden and/or system files. */
2006 if( is_filtered(data) ) goto next;
2007
2008 dirp->d_next.d_ino++;
2009 dirp->d_next.d_attributes = data.attrib;
2010 WideCharToMultiByte(CP_UTF8, 0, data.name, -1,
2011 dirp->d_next.d_name, DIRENT_NAME_MAX, 0, 0);
 
2012 return &dirp->d_next;
2013 }
2014
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2015 /*
2016 ** Implementation of the POSIX closedir() function using the MSVCRT.
2017 */
2018 INT closedir(
2019 LPDIR dirp
@@ -8085,18 +8031,13 @@
8031 # include "windows.h"
8032 # include <io.h>
8033 # include <direct.h>
8034 /* # include "test_windirent.h" */
8035 # define dirent DIRENT
8036 # define stat _stat
8037 # define chmod(path,mode) fileio_chmod(path,mode)
8038 # define mkdir(path,mode) fileio_mkdir(path)
 
 
 
 
 
8039 #endif
8040 #include <time.h>
8041 #include <errno.h>
8042
8043 /* When used as part of the CLI, the sqlite3_stdio.h module will have
@@ -8117,10 +8058,44 @@
8058 #define FSDIR_COLUMN_MTIME 2 /* Last modification time */
8059 #define FSDIR_COLUMN_DATA 3 /* File content */
8060 #define FSDIR_COLUMN_PATH 4 /* Path to top of search */
8061 #define FSDIR_COLUMN_DIR 5 /* Path is relative to this directory */
8062
8063 /*
8064 ** UTF8 chmod() function for Windows
8065 */
8066 #if defined(_WIN32) || defined(WIN32)
8067 static int fileio_chmod(const char *zPath, int pmode){
8068 sqlite3_int64 sz = strlen(zPath);
8069 wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) );
8070 int rc;
8071 if( b1==0 ) return -1;
8072 sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz);
8073 b1[sz] = 0;
8074 rc = _wchmod(b1, pmode);
8075 sqlite3_free(b1);
8076 return rc;
8077 }
8078 #endif
8079
8080 /*
8081 ** UTF8 mkdir() function for Windows
8082 */
8083 #if defined(_WIN32) || defined(WIN32)
8084 static int fileio_mkdir(const char *zPath){
8085 sqlite3_int64 sz = strlen(zPath);
8086 wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) );
8087 int rc;
8088 if( b1==0 ) return -1;
8089 sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz);
8090 b1[sz] = 0;
8091 rc = _wmkdir(b1);
8092 sqlite3_free(b1);
8093 return rc;
8094 }
8095 #endif
8096
8097
8098 /*
8099 ** Set the result stored by context ctx to a blob containing the
8100 ** contents of file zName. Or, leave the result unchanged (NULL)
8101 ** if the file does not exist or is unreadable.
@@ -8278,11 +8253,17 @@
8253 static int fileStat(
8254 const char *zPath,
8255 struct stat *pStatBuf
8256 ){
8257 #if defined(_WIN32)
8258 sqlite3_int64 sz = strlen(zPath);
8259 wchar_t *b1 = sqlite3_malloc64( (sz+1)*sizeof(b1[0]) );
8260 int rc;
8261 if( b1==0 ) return 1;
8262 sz = MultiByteToWideChar(CP_UTF8, 0, zPath, sz, b1, sz);
8263 b1[sz] = 0;
8264 rc = _wstat(b1, pStatBuf);
8265 if( rc==0 ) statTimesToUtc(zPath, pStatBuf);
8266 return rc;
8267 #else
8268 return stat(zPath, pStatBuf);
8269 #endif
@@ -8296,13 +8277,11 @@
8277 static int fileLinkStat(
8278 const char *zPath,
8279 struct stat *pStatBuf
8280 ){
8281 #if defined(_WIN32)
8282 return fileStat(zPath, pStatBuf);
 
 
8283 #else
8284 return lstat(zPath, pStatBuf);
8285 #endif
8286 }
8287
@@ -16808,11 +16787,11 @@
16787 case SQLITE_FCNTL_POWERSAFE_OVERWRITE: zOp = "POWERSAFE_OVERWRITE"; break;
16788 case SQLITE_FCNTL_PRAGMA: {
16789 const char *const* a = (const char*const*)pArg;
16790 if( a[1] && strcmp(a[1],"vfstrace")==0 && a[2] ){
16791 const u8 *zArg = (const u8*)a[2];
16792 if( zArg[0]>='0' && zArg[0]<='9' ){
16793 pInfo->mTrace = (sqlite3_uint64)strtoll(a[2], 0, 0);
16794 }else{
16795 static const struct {
16796 const char *z;
16797 unsigned int m;
@@ -18709,10 +18688,13 @@
18688 rc = sqlite3_create_module(db, "sqlite_dbptr", &dbdata_module, (void*)1);
18689 }
18690 return rc;
18691 }
18692
18693 #ifdef _WIN32
18694
18695 #endif
18696 int sqlite3_dbdata_init(
18697 sqlite3 *db,
18698 char **pzErrMsg,
18699 const sqlite3_api_routines *pApi
18700 ){
@@ -25592,107 +25574,108 @@
25574 " --plain Show results as text/plain, not as HTML",
25575 #endif
25576 };
25577
25578 /*
25579 ** Output help text for commands that match zPattern.
25580 **
25581 ** * If zPattern is NULL, then show all documented commands, but
25582 ** only give a one-line summary of each.
25583 **
25584 ** * If zPattern is "-a" or "-all" or "--all" then show all help text
25585 ** for all commands except undocumented commands.
25586 **
25587 ** * If zPattern is "0" then show all help for undocumented commands.
25588 ** Undocumented commands begin with "," instead of "." in the azHelp[]
25589 ** array.
25590 **
25591 ** * If zPattern is a prefix for one or more documented commands, then
25592 ** show help for those commands. If only a single command matches the
25593 ** prefix, show the full text of the help. If multiple commands match,
25594 ** Only show just the first line of each.
25595 **
25596 ** * Otherwise, show the complete text of any documented command for which
25597 ** zPattern is a LIKE match for any text within that command help
25598 ** text.
25599 **
25600 ** Return the number commands that match zPattern.
25601 */
25602 static int showHelp(FILE *out, const char *zPattern){
25603 int i = 0;
25604 int j = 0;
25605 int n = 0;
25606 char *zPat;
25607 if( zPattern==0 ){
25608 /* Show just the first line for all help topics */
25609 zPattern = "[a-z]";
25610 }else if( cli_strcmp(zPattern,"-a")==0
25611 || cli_strcmp(zPattern,"-all")==0
25612 || cli_strcmp(zPattern,"--all")==0
25613 ){
25614 /* Show everything except undocumented commands */
25615 zPattern = ".";
25616 }else if( cli_strcmp(zPattern,"0")==0 ){
25617 /* Show complete help text of undocumented commands */
25618 int show = 0;
25619 for(i=0; i<ArraySize(azHelp); i++){
25620 if( azHelp[i][0]=='.' ){
25621 show = 0;
25622 }else if( azHelp[i][0]==',' ){
25623 show = 1;
25624 sqlite3_fprintf(out, ".%s\n", &azHelp[i][1]);
25625 n++;
25626 }else if( show ){
25627 sqlite3_fprintf(out, "%s\n", azHelp[i]);
25628 }
25629 }
25630 return n;
25631 }
25632
25633 /* Seek documented commands for which zPattern is an exact prefix */
25634 zPat = sqlite3_mprintf(".%s*", zPattern);
25635 shell_check_oom(zPat);
25636 for(i=0; i<ArraySize(azHelp); i++){
25637 if( sqlite3_strglob(zPat, azHelp[i])==0 ){
25638 sqlite3_fprintf(out, "%s\n", azHelp[i]);
25639 j = i+1;
25640 n++;
25641 }
25642 }
25643 sqlite3_free(zPat);
25644 if( n ){
25645 if( n==1 ){
25646 /* when zPattern is a prefix of exactly one command, then include
25647 ** the details of that command, which should begin at offset j */
25648 while( j<ArraySize(azHelp)-1 && azHelp[j][0]==' ' ){
25649 sqlite3_fprintf(out, "%s\n", azHelp[j]);
25650 j++;
25651 }
25652 }
25653 return n;
25654 }
25655
25656 /* Look for documented commands that contain zPattern anywhere.
25657 ** Show complete text of all documented commands that match. */
25658 zPat = sqlite3_mprintf("%%%s%%", zPattern);
25659 shell_check_oom(zPat);
25660 for(i=0; i<ArraySize(azHelp); i++){
25661 if( azHelp[i][0]==',' ){
25662 while( i<ArraySize(azHelp)-1 && azHelp[i+1][0]==' ' ) ++i;
25663 continue;
25664 }
25665 if( azHelp[i][0]=='.' ) j = i;
25666 if( sqlite3_strlike(zPat, azHelp[i], 0)==0 ){
25667 sqlite3_fprintf(out, "%s\n", azHelp[j]);
25668 while( j<ArraySize(azHelp)-1 && azHelp[j+1][0]==' ' ){
25669 j++;
25670 sqlite3_fprintf(out, "%s\n", azHelp[j]);
25671 }
25672 i = j;
25673 n++;
25674 }
25675 }
25676 sqlite3_free(zPat);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25677 return n;
25678 }
25679
25680 /* Forward reference */
25681 static int process_input(ShellState *p);
@@ -25937,10 +25920,43 @@
25920 int sleep = sqlite3_value_int(argv[0]);
25921 (void)argcUnused;
25922 sqlite3_sleep(sleep/1000);
25923 sqlite3_result_int(context, sleep);
25924 }
25925
25926 /*
25927 ** SQL function: shell_module_schema(X)
25928 **
25929 ** Return a fake schema for the table-valued function or eponymous virtual
25930 ** table X.
25931 */
25932 static void shellModuleSchema(
25933 sqlite3_context *pCtx,
25934 int nVal,
25935 sqlite3_value **apVal
25936 ){
25937 const char *zName;
25938 char *zFake;
25939 ShellState *p = (ShellState*)sqlite3_user_data(pCtx);
25940 FILE *pSavedLog = p->pLog;
25941 UNUSED_PARAMETER(nVal);
25942 zName = (const char*)sqlite3_value_text(apVal[0]);
25943
25944 /* Temporarily disable the ".log" when calling shellFakeSchema() because
25945 ** shellFakeSchema() might generate failures for some ephemeral virtual
25946 ** tables due to missing arguments. Example: fts4aux.
25947 ** https://sqlite.org/forum/forumpost/42fe6520b803be51 */
25948 p->pLog = 0;
25949 zFake = zName? shellFakeSchema(sqlite3_context_db_handle(pCtx), 0, zName) : 0;
25950 p->pLog = pSavedLog;
25951
25952 if( zFake ){
25953 sqlite3_result_text(pCtx, sqlite3_mprintf("/* %s */", zFake),
25954 -1, sqlite3_free);
25955 free(zFake);
25956 }
25957 }
25958
25959 /* Flags for open_db().
25960 **
25961 ** The default behavior of open_db() is to exit(1) if the database fails to
25962 ** open. The OPEN_DB_KEEPALIVE flag changes that so that it prints an error
@@ -26081,11 +26097,11 @@
26097 shellDtostr, 0, 0);
26098 sqlite3_create_function(p->db, "dtostr", 2, SQLITE_UTF8, 0,
26099 shellDtostr, 0, 0);
26100 sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0,
26101 shellAddSchemaName, 0, 0);
26102 sqlite3_create_function(p->db, "shell_module_schema", 1, SQLITE_UTF8, p,
26103 shellModuleSchema, 0, 0);
26104 sqlite3_create_function(p->db, "shell_putsnl", 1, SQLITE_UTF8, p,
26105 shellPutsFunc, 0, 0);
26106 sqlite3_create_function(p->db, "usleep",1,SQLITE_UTF8,0,
26107 shellUSleepFunc, 0, 0);
26108
+204 -142
--- extsrc/sqlite3.c
+++ extsrc/sqlite3.c
@@ -16,11 +16,11 @@
1616
** if you want a wrapper to interface SQLite with your choice of programming
1717
** language. The code for the "sqlite3" command-line shell is also in a
1818
** separate file. This file contains only code for the core SQLite library.
1919
**
2020
** The content in this amalgamation comes from Fossil check-in
21
-** d22475b81c4e26ccc50f3b5626d43b32f7a2 with changes in files:
21
+** 336ceeccc6f85bd78f4a26648af7edf9056d with changes in files:
2222
**
2323
**
2424
*/
2525
#ifndef SQLITE_AMALGAMATION
2626
#define SQLITE_CORE 1
@@ -465,11 +465,11 @@
465465
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
466466
** [sqlite_version()] and [sqlite_source_id()].
467467
*/
468468
#define SQLITE_VERSION "3.50.0"
469469
#define SQLITE_VERSION_NUMBER 3050000
470
-#define SQLITE_SOURCE_ID "2025-04-15 21:59:38 d22475b81c4e26ccc50f3b5626d43b32f7a2de34e5a764539554665bdda735d5"
470
+#define SQLITE_SOURCE_ID "2025-05-15 11:20:54 336ceeccc6f85bd78f4a26648af7edf9056d569a767b4120f125a02b2090a349"
471471
472472
/*
473473
** CAPI3REF: Run-Time Library Version Numbers
474474
** KEYWORDS: sqlite3_version sqlite3_sourceid
475475
**
@@ -11872,13 +11872,14 @@
1187211872
** This may appear to have some counter-intuitive effects if a single row
1187311873
** is written to more than once during a session. For example, if a row
1187411874
** is inserted while a session object is enabled, then later deleted while
1187511875
** the same session object is disabled, no INSERT record will appear in the
1187611876
** changeset, even though the delete took place while the session was disabled.
11877
-** Or, if one field of a row is updated while a session is disabled, and
11878
-** another field of the same row is updated while the session is enabled, the
11879
-** resulting changeset will contain an UPDATE change that updates both fields.
11877
+** Or, if one field of a row is updated while a session is enabled, and
11878
+** then another field of the same row is updated while the session is disabled,
11879
+** the resulting changeset will contain an UPDATE change that updates both
11880
+** fields.
1188011881
*/
1188111882
SQLITE_API int sqlite3session_changeset(
1188211883
sqlite3_session *pSession, /* Session object */
1188311884
int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
1188411885
void **ppChangeset /* OUT: Buffer containing changeset */
@@ -12083,11 +12084,11 @@
1208312084
** CAPI3REF: Flags for sqlite3changeset_start_v2
1208412085
**
1208512086
** The following flags may passed via the 4th parameter to
1208612087
** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
1208712088
**
12088
-** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
12089
+** <dt>SQLITE_CHANGESETSTART_INVERT <dd>
1208912090
** Invert the changeset while iterating through it. This is equivalent to
1209012091
** inverting a changeset using sqlite3changeset_invert() before applying it.
1209112092
** It is an error to specify this flag with a patchset.
1209212093
*/
1209312094
#define SQLITE_CHANGESETSTART_INVERT 0x0002
@@ -19160,11 +19161,10 @@
1916019161
unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */
1916119162
unsigned isResized:1; /* True if resizeIndexObject() has been called */
1916219163
unsigned isCovering:1; /* True if this is a covering index */
1916319164
unsigned noSkipScan:1; /* Do not try to use skip-scan if true */
1916419165
unsigned hasStat1:1; /* aiRowLogEst values come from sqlite_stat1 */
19165
- unsigned bLowQual:1; /* sqlite_stat1 says this is a low-quality index */
1916619166
unsigned bNoQuery:1; /* Do not use this index to optimize queries */
1916719167
unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */
1916819168
unsigned bIdxRowid:1; /* One or more of the index keys is the ROWID */
1916919169
unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */
1917019170
unsigned bHasExpr:1; /* Index contains an expression, either a literal
@@ -22424,10 +22424,13 @@
2242422424
#ifdef SQLITE_BITMASK_TYPE
2242522425
"BITMASK_TYPE=" CTIMEOPT_VAL(SQLITE_BITMASK_TYPE),
2242622426
#endif
2242722427
#ifdef SQLITE_BUG_COMPATIBLE_20160819
2242822428
"BUG_COMPATIBLE_20160819",
22429
+#endif
22430
+#ifdef SQLITE_BUG_COMPATIBLE_20250510
22431
+ "BUG_COMPATIBLE_20250510",
2242922432
#endif
2243022433
#ifdef SQLITE_CASE_SENSITIVE_LIKE
2243122434
"CASE_SENSITIVE_LIKE",
2243222435
#endif
2243322436
#ifdef SQLITE_CHECK_PAGES
@@ -32987,10 +32990,19 @@
3298732990
va_end(ap);
3298832991
zBuf[acc.nChar] = 0;
3298932992
return zBuf;
3299032993
}
3299132994
32995
+/* Maximum size of an sqlite3_log() message. */
32996
+#if defined(SQLITE_MAX_LOG_MESSAGE)
32997
+ /* Leave the definition as supplied */
32998
+#elif SQLITE_PRINT_BUF_SIZE*10>10000
32999
+# define SQLITE_MAX_LOG_MESSAGE 10000
33000
+#else
33001
+# define SQLITE_MAX_LOG_MESSAGE (SQLITE_PRINT_BUF_SIZE*10)
33002
+#endif
33003
+
3299233004
/*
3299333005
** This is the routine that actually formats the sqlite3_log() message.
3299433006
** We house it in a separate routine from sqlite3_log() to avoid using
3299533007
** stack space on small-stack systems when logging is disabled.
3299633008
**
@@ -33003,11 +33015,11 @@
3300333015
** Care must be taken that any sqlite3_log() calls that occur while the
3300433016
** memory mutex is held do not use these mechanisms.
3300533017
*/
3300633018
static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){
3300733019
StrAccum acc; /* String accumulator */
33008
- char zMsg[SQLITE_PRINT_BUF_SIZE*3]; /* Complete log message */
33020
+ char zMsg[SQLITE_MAX_LOG_MESSAGE]; /* Complete log message */
3300933021
3301033022
sqlite3StrAccumInit(&acc, 0, zMsg, sizeof(zMsg), 0);
3301133023
sqlite3_str_vappendf(&acc, zFormat, ap);
3301233024
sqlite3GlobalConfig.xLog(sqlite3GlobalConfig.pLogArg, iErrCode,
3301333025
sqlite3StrAccumFinish(&acc));
@@ -87262,10 +87274,13 @@
8726287274
** into register iDest, then add the OPFLAG_TYPEOFARG flag to that
8726387275
** opcode.
8726487276
*/
8726587277
SQLITE_PRIVATE void sqlite3VdbeTypeofColumn(Vdbe *p, int iDest){
8726687278
VdbeOp *pOp = sqlite3VdbeGetLastOp(p);
87279
+#ifdef SQLITE_DEBUG
87280
+ while( pOp->opcode==OP_ReleaseReg ) pOp--;
87281
+#endif
8726787282
if( pOp->p3==iDest && pOp->opcode==OP_Column ){
8726887283
pOp->p5 |= OPFLAG_TYPEOFARG;
8726987284
}
8727087285
}
8727187286
@@ -95716,11 +95731,11 @@
9571695731
}
9571795732
}else{
9571895733
sqlite3VdbeError(p, "%s", pOp->p4.z);
9571995734
}
9572095735
pcx = (int)(pOp - aOp);
95721
- sqlite3_log(pOp->p1, "abort at %d in [%s]: %s", pcx, p->zSql, p->zErrMsg);
95736
+ sqlite3_log(pOp->p1, "abort at %d: %s; [%s]", pcx, p->zErrMsg, p->zSql);
9572295737
}
9572395738
rc = sqlite3VdbeHalt(p);
9572495739
assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR );
9572595740
if( rc==SQLITE_BUSY ){
9572695741
p->rc = SQLITE_BUSY;
@@ -97042,11 +97057,11 @@
9704297057
pOut->u.i = ~sqlite3VdbeIntValue(pIn1);
9704397058
}
9704497059
break;
9704597060
}
9704697061
97047
-/* Opcode: Once P1 P2 * * *
97062
+/* Opcode: Once P1 P2 P3 * *
9704897063
**
9704997064
** Fall through to the next instruction the first time this opcode is
9705097065
** encountered on each invocation of the byte-code program. Jump to P2
9705197066
** on the second and all subsequent encounters during the same invocation.
9705297067
**
@@ -97058,10 +97073,16 @@
9705897073
**
9705997074
** For subprograms, there is a bitmask in the VdbeFrame that determines
9706097075
** whether or not the jump should be taken. The bitmask is necessary
9706197076
** because the self-altering code trick does not work for recursive
9706297077
** triggers.
97078
+**
97079
+** The P3 operand is not used directly by this opcode. However P3 is
97080
+** used by the code generator as follows: If this opcode is the start
97081
+** of a subroutine and that subroutine uses a Bloom filter, then P3 will
97082
+** be the register that holds that Bloom filter. See tag-202407032019
97083
+** in the source code for implementation details.
9706397084
*/
9706497085
case OP_Once: { /* jump */
9706597086
u32 iAddr; /* Address of this instruction */
9706697087
assert( p->aOp[0].opcode==OP_Init );
9706797088
if( p->pFrame ){
@@ -98103,10 +98124,11 @@
9810398124
}
9810498125
}else{
9810598126
zHdr += sqlite3PutVarint(zHdr, serial_type);
9810698127
if( pRec->n ){
9810798128
assert( pRec->z!=0 );
98129
+ assert( pRec->z!=(const char*)sqlite3CtypeMap );
9810898130
memcpy(zPayload, pRec->z, pRec->n);
9810998131
zPayload += pRec->n;
9811098132
}
9811198133
}
9811298134
if( pRec==pLast ) break;
@@ -103551,12 +103573,12 @@
103551103573
sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc));
103552103574
}
103553103575
p->rc = rc;
103554103576
sqlite3SystemError(db, rc);
103555103577
testcase( sqlite3GlobalConfig.xLog!=0 );
103556
- sqlite3_log(rc, "statement aborts at %d: [%s] %s",
103557
- (int)(pOp - aOp), p->zSql, p->zErrMsg);
103578
+ sqlite3_log(rc, "statement aborts at %d: %s; [%s]",
103579
+ (int)(pOp - aOp), p->zErrMsg, p->zSql);
103558103580
if( p->eVdbeState==VDBE_RUN_STATE ) sqlite3VdbeHalt(p);
103559103581
if( rc==SQLITE_IOERR_NOMEM ) sqlite3OomFault(db);
103560103582
if( rc==SQLITE_CORRUPT && db->autoCommit==0 ){
103561103583
db->flags |= SQLITE_CorruptRdOnly;
103562103584
}
@@ -109287,17 +109309,16 @@
109287109309
/* Clearly non-deterministic functions like random(), but also
109288109310
** date/time functions that use 'now', and other functions like
109289109311
** sqlite_version() that might change over time cannot be used
109290109312
** in an index or generated column. Curiously, they can be used
109291109313
** in a CHECK constraint. SQLServer, MySQL, and PostgreSQL all
109292
- ** all this. */
109314
+ ** allow this. */
109293109315
sqlite3ResolveNotValid(pParse, pNC, "non-deterministic functions",
109294109316
NC_IdxExpr|NC_PartIdx|NC_GenCol, 0, pExpr);
109295109317
}else{
109296109318
assert( (NC_SelfRef & 0xff)==NC_SelfRef ); /* Must fit in 8 bits */
109297109319
pExpr->op2 = pNC->ncFlags & NC_SelfRef;
109298
- if( pNC->ncFlags & NC_FromDDL ) ExprSetProperty(pExpr, EP_FromDDL);
109299109320
}
109300109321
if( (pDef->funcFlags & SQLITE_FUNC_INTERNAL)!=0
109301109322
&& pParse->nested==0
109302109323
&& (pParse->db->mDbFlags & DBFLAG_InternalFunc)==0
109303109324
){
@@ -109309,10 +109330,11 @@
109309109330
pDef = 0;
109310109331
}else
109311109332
if( (pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE))!=0
109312109333
&& !IN_RENAME_OBJECT
109313109334
){
109335
+ if( pNC->ncFlags & NC_FromDDL ) ExprSetProperty(pExpr, EP_FromDDL);
109314109336
sqlite3ExprFunctionUsable(pParse, pExpr, pDef);
109315109337
}
109316109338
}
109317109339
109318109340
if( 0==IN_RENAME_OBJECT ){
@@ -114022,15 +114044,16 @@
114022114044
pCopy = sqlite3SelectDup(pParse->db, pSelect, 0);
114023114045
rc = pParse->db->mallocFailed ? 1 :sqlite3Select(pParse, pCopy, &dest);
114024114046
sqlite3SelectDelete(pParse->db, pCopy);
114025114047
sqlite3DbFree(pParse->db, dest.zAffSdst);
114026114048
if( addrBloom ){
114049
+ /* Remember that location of the Bloom filter in the P3 operand
114050
+ ** of the OP_Once that began this subroutine. tag-202407032019 */
114027114051
sqlite3VdbeGetOp(v, addrOnce)->p3 = dest.iSDParm2;
114028114052
if( dest.iSDParm2==0 ){
114029
- sqlite3VdbeChangeToNoop(v, addrBloom);
114030
- }else{
114031
- sqlite3VdbeGetOp(v, addrOnce)->p3 = dest.iSDParm2;
114053
+ /* If the Bloom filter won't actually be used, keep it small */
114054
+ sqlite3VdbeGetOp(v, addrBloom)->p1 = 10;
114032114055
}
114033114056
}
114034114057
if( rc ){
114035114058
sqlite3KeyInfoUnref(pKeyInfo);
114036114059
return;
@@ -114473,11 +114496,11 @@
114473114496
if( destIfFalse==destIfNull ){
114474114497
/* Combine Step 3 and Step 5 into a single opcode */
114475114498
if( ExprHasProperty(pExpr, EP_Subrtn) ){
114476114499
const VdbeOp *pOp = sqlite3VdbeGetOp(v, pExpr->y.sub.iAddr);
114477114500
assert( pOp->opcode==OP_Once || pParse->nErr );
114478
- if( pOp->opcode==OP_Once && pOp->p3>0 ){
114501
+ if( pOp->opcode==OP_Once && pOp->p3>0 ){ /* tag-202407032019 */
114479114502
assert( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) );
114480114503
sqlite3VdbeAddOp4Int(v, OP_Filter, pOp->p3, destIfFalse,
114481114504
rLhs, nVector); VdbeCoverage(v);
114482114505
}
114483114506
}
@@ -116322,15 +116345,15 @@
116322116345
case TK_ISNULL:
116323116346
case TK_NOTNULL: {
116324116347
assert( TK_ISNULL==OP_IsNull ); testcase( op==TK_ISNULL );
116325116348
assert( TK_NOTNULL==OP_NotNull ); testcase( op==TK_NOTNULL );
116326116349
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
116327
- sqlite3VdbeTypeofColumn(v, r1);
116350
+ assert( regFree1==0 || regFree1==r1 );
116351
+ if( regFree1 ) sqlite3VdbeTypeofColumn(v, r1);
116328116352
sqlite3VdbeAddOp2(v, op, r1, dest);
116329116353
VdbeCoverageIf(v, op==TK_ISNULL);
116330116354
VdbeCoverageIf(v, op==TK_NOTNULL);
116331
- testcase( regFree1==0 );
116332116355
break;
116333116356
}
116334116357
case TK_BETWEEN: {
116335116358
testcase( jumpIfNull==0 );
116336116359
exprCodeBetween(pParse, pExpr, dest, sqlite3ExprIfTrue, jumpIfNull);
@@ -116497,15 +116520,15 @@
116497116520
break;
116498116521
}
116499116522
case TK_ISNULL:
116500116523
case TK_NOTNULL: {
116501116524
r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
116502
- sqlite3VdbeTypeofColumn(v, r1);
116525
+ assert( regFree1==0 || regFree1==r1 );
116526
+ if( regFree1 ) sqlite3VdbeTypeofColumn(v, r1);
116503116527
sqlite3VdbeAddOp2(v, op, r1, dest);
116504116528
testcase( op==TK_ISNULL ); VdbeCoverageIf(v, op==TK_ISNULL);
116505116529
testcase( op==TK_NOTNULL ); VdbeCoverageIf(v, op==TK_NOTNULL);
116506
- testcase( regFree1==0 );
116507116530
break;
116508116531
}
116509116532
case TK_BETWEEN: {
116510116533
testcase( jumpIfNull==0 );
116511116534
exprCodeBetween(pParse, pExpr, dest, sqlite3ExprIfFalse, jumpIfNull);
@@ -121683,20 +121706,10 @@
121683121706
}
121684121707
#endif
121685121708
while( z[0]!=0 && z[0]!=' ' ) z++;
121686121709
while( z[0]==' ' ) z++;
121687121710
}
121688
-
121689
- /* Set the bLowQual flag if the peak number of rows obtained
121690
- ** from a full equality match is so large that a full table scan
121691
- ** seems likely to be faster than using the index.
121692
- */
121693
- if( aLog[0] > 66 /* Index has more than 100 rows */
121694
- && aLog[0] <= aLog[nOut-1] /* And only a single value seen */
121695
- ){
121696
- pIndex->bLowQual = 1;
121697
- }
121698121711
}
121699121712
}
121700121713
121701121714
/*
121702121715
** This callback is invoked once for each index when reading the
@@ -124091,11 +124104,11 @@
124091124104
*/
124092124105
SQLITE_PRIVATE int sqlite3TableColumnToIndex(Index *pIdx, int iCol){
124093124106
int i;
124094124107
i16 iCol16;
124095124108
assert( iCol>=(-1) && iCol<=SQLITE_MAX_COLUMN );
124096
- assert( pIdx->nColumn<=SQLITE_MAX_COLUMN );
124109
+ assert( pIdx->nColumn<=SQLITE_MAX_COLUMN+1 );
124097124110
iCol16 = iCol;
124098124111
for(i=0; i<pIdx->nColumn; i++){
124099124112
if( iCol16==pIdx->aiColumn[i] ){
124100124113
return i;
124101124114
}
@@ -167620,15 +167633,12 @@
167620167633
opMask = WO_LT|WO_LE;
167621167634
}else{
167622167635
assert( pNew->u.btree.nBtm==0 );
167623167636
opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS;
167624167637
}
167625
- if( pProbe->bUnordered || pProbe->bLowQual ){
167626
- if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
167627
- if( pProbe->bLowQual && pSrc->fg.isIndexedBy==0 ){
167628
- opMask &= ~(WO_EQ|WO_IN|WO_IS);
167629
- }
167638
+ if( pProbe->bUnordered ){
167639
+ opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
167630167640
}
167631167641
167632167642
assert( pNew->u.btree.nEq<pProbe->nColumn );
167633167643
assert( pNew->u.btree.nEq<pProbe->nKeyCol
167634167644
|| pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY );
@@ -168561,11 +168571,11 @@
168561168571
}
168562168572
}else if( m==0
168563168573
&& (HasRowid(pTab) || pWInfo->pSelect!=0 || sqlite3FaultSim(700))
168564168574
){
168565168575
WHERETRACE(0x200,
168566
- ("-> %s a covering index according to bitmasks\n",
168576
+ ("-> %s is a covering index according to bitmasks\n",
168567168577
pProbe->zName, m==0 ? "is" : "is not"));
168568168578
pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED;
168569168579
}
168570168580
}
168571168581
@@ -207968,60 +207978,113 @@
207968207978
** Growing our own isspace() routine this way is twice as fast as
207969207979
** the library isspace() function, resulting in a 7% overall performance
207970207980
** increase for the text-JSON parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os).
207971207981
*/
207972207982
static const char jsonIsSpace[] = {
207973
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
207974
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207975
- 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207976
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207977
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207978
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207979
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207980
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207981
-
207982
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207983
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207984
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207985
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207986
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207987
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207988
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207989
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207983
+#ifdef SQLITE_ASCII
207984
+/*0 1 2 3 4 5 6 7 8 9 a b c d e f */
207985
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0 */
207986
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1 */
207987
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
207988
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 3 */
207989
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 4 */
207990
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 5 */
207991
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 6 */
207992
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 7 */
207993
+
207994
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 8 */
207995
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 9 */
207996
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* a */
207997
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* b */
207998
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* c */
207999
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */
208000
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* e */
208001
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* f */
208002
+#endif
208003
+#ifdef SQLITE_EBCDIC
208004
+/*0 1 2 3 4 5 6 7 8 9 a b c d e f */
208005
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, /* 0 */
208006
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1 */
208007
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
208008
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 3 */
208009
+ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 4 */
208010
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 5 */
208011
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 6 */
208012
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 7 */
208013
+
208014
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 8 */
208015
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 9 */
208016
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* a */
208017
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* b */
208018
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* c */
208019
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */
208020
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* e */
208021
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* f */
208022
+#endif
208023
+
207990208024
};
207991208025
#define jsonIsspace(x) (jsonIsSpace[(unsigned char)x])
207992208026
207993208027
/*
207994208028
** The set of all space characters recognized by jsonIsspace().
207995208029
** Useful as the second argument to strspn().
207996208030
*/
208031
+#ifdef SQLITE_ASCII
207997208032
static const char jsonSpaces[] = "\011\012\015\040";
208033
+#endif
208034
+#ifdef SQLITE_EBCDIC
208035
+static const char jsonSpaces[] = "\005\045\015\100";
208036
+#endif
208037
+
207998208038
207999208039
/*
208000208040
** Characters that are special to JSON. Control characters,
208001208041
** '"' and '\\' and '\''. Actually, '\'' is not special to
208002208042
** canonical JSON, but it is special in JSON-5, so we include
208003208043
** it in the set of special characters.
208004208044
*/
208005208045
static const char jsonIsOk[256] = {
208006
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
208007
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
208008
- 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
208009
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
208010
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
208011
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
208012
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
208013
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
208014
-
208015
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
208016
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
208017
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
208018
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
208019
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
208020
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
208021
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
208022
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
208046
+#ifdef SQLITE_ASCII
208047
+/*0 1 2 3 4 5 6 7 8 9 a b c d e f */
208048
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */
208049
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1 */
208050
+ 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, /* 2 */
208051
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 3 */
208052
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4 */
208053
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, /* 5 */
208054
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */
208055
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 7 */
208056
+
208057
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 8 */
208058
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 9 */
208059
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* a */
208060
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* b */
208061
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* c */
208062
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* d */
208063
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */
208064
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* f */
208065
+#endif
208066
+#ifdef SQLITE_EBCDIC
208067
+/*0 1 2 3 4 5 6 7 8 9 a b c d e f */
208068
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */
208069
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1 */
208070
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
208071
+ 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, /* 3 */
208072
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4 */
208073
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 5 */
208074
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */
208075
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, /* 7 */
208076
+
208077
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 8 */
208078
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 9 */
208079
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* a */
208080
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* b */
208081
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* c */
208082
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* d */
208083
+ 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */
208084
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* f */
208085
+#endif
208023208086
};
208024208087
208025208088
/* Objects */
208026208089
typedef struct JsonCache JsonCache;
208027208090
typedef struct JsonString JsonString;
@@ -208162,11 +208225,11 @@
208162208225
208163208226
/**************************************************************************
208164208227
** Forward references
208165208228
**************************************************************************/
208166208229
static void jsonReturnStringAsBlob(JsonString*);
208167
-static int jsonFuncArgMightBeBinary(sqlite3_value *pJson);
208230
+static int jsonArgIsJsonb(sqlite3_value *pJson, JsonParse *p);
208168208231
static u32 jsonTranslateBlobToText(const JsonParse*,u32,JsonString*);
208169208232
static void jsonReturnParse(sqlite3_context*,JsonParse*);
208170208233
static JsonParse *jsonParseFuncArg(sqlite3_context*,sqlite3_value*,u32);
208171208234
static void jsonParseFree(JsonParse*);
208172208235
static u32 jsonbPayloadSize(const JsonParse*, u32, u32*);
@@ -208580,15 +208643,13 @@
208580208643
jsonAppendString(p, z, n);
208581208644
}
208582208645
break;
208583208646
}
208584208647
default: {
208585
- if( jsonFuncArgMightBeBinary(pValue) ){
208586
- JsonParse px;
208587
- memset(&px, 0, sizeof(px));
208588
- px.aBlob = (u8*)sqlite3_value_blob(pValue);
208589
- px.nBlob = sqlite3_value_bytes(pValue);
208648
+ JsonParse px;
208649
+ memset(&px, 0, sizeof(px));
208650
+ if( jsonArgIsJsonb(pValue, &px) ){
208590208651
jsonTranslateBlobToText(&px, 0, p);
208591208652
}else if( p->eErr==0 ){
208592208653
sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1);
208593208654
p->eErr = JSTRING_ERR;
208594208655
jsonStringReset(p);
@@ -209522,11 +209583,16 @@
209522209583
c = z[++j];
209523209584
if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f'
209524209585
|| c=='n' || c=='r' || c=='t'
209525209586
|| (c=='u' && jsonIs4Hex(&z[j+1])) ){
209526209587
if( opcode==JSONB_TEXT ) opcode = JSONB_TEXTJ;
209527
- }else if( c=='\'' || c=='0' || c=='v' || c=='\n'
209588
+ }else if( c=='\'' || c=='v' || c=='\n'
209589
+#ifdef SQLITE_BUG_COMPATIBLE_20250510
209590
+ || (c=='0') /* Legacy bug compatible */
209591
+#else
209592
+ || (c=='0' && !sqlite3Isdigit(z[j+1])) /* Correct implementation */
209593
+#endif
209528209594
|| (0xe2==(u8)c && 0x80==(u8)z[j+1]
209529209595
&& (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2]))
209530209596
|| (c=='x' && jsonIs2Hex(&z[j+1])) ){
209531209597
opcode = JSONB_TEXT5;
209532209598
pParse->hasNonstd = 1;
@@ -209909,11 +209975,11 @@
209909209975
|| pParse->aBlob[i+4]!=0
209910209976
){
209911209977
*pSz = 0;
209912209978
return 0;
209913209979
}
209914
- sz = (pParse->aBlob[i+5]<<24) + (pParse->aBlob[i+6]<<16) +
209980
+ sz = ((u32)pParse->aBlob[i+5]<<24) + (pParse->aBlob[i+6]<<16) +
209915209981
(pParse->aBlob[i+7]<<8) + pParse->aBlob[i+8];
209916209982
n = 9;
209917209983
}
209918209984
if( (i64)i+sz+n > pParse->nBlob
209919209985
&& (i64)i+sz+n > pParse->nBlob-pParse->delta
@@ -210258,37 +210324,10 @@
210258210324
}
210259210325
}
210260210326
return i;
210261210327
}
210262210328
210263
-
210264
-/* Return true if the input pJson
210265
-**
210266
-** For performance reasons, this routine does not do a detailed check of the
210267
-** input BLOB to ensure that it is well-formed. Hence, false positives are
210268
-** possible. False negatives should never occur, however.
210269
-*/
210270
-static int jsonFuncArgMightBeBinary(sqlite3_value *pJson){
210271
- u32 sz, n;
210272
- const u8 *aBlob;
210273
- int nBlob;
210274
- JsonParse s;
210275
- if( sqlite3_value_type(pJson)!=SQLITE_BLOB ) return 0;
210276
- aBlob = sqlite3_value_blob(pJson);
210277
- nBlob = sqlite3_value_bytes(pJson);
210278
- if( nBlob<1 ) return 0;
210279
- if( NEVER(aBlob==0) || (aBlob[0] & 0x0f)>JSONB_OBJECT ) return 0;
210280
- memset(&s, 0, sizeof(s));
210281
- s.aBlob = (u8*)aBlob;
210282
- s.nBlob = nBlob;
210283
- n = jsonbPayloadSize(&s, 0, &sz);
210284
- if( n==0 ) return 0;
210285
- if( sz+n!=(u32)nBlob ) return 0;
210286
- if( (aBlob[0] & 0x0f)<=JSONB_FALSE && sz>0 ) return 0;
210287
- return sz+n==(u32)nBlob;
210288
-}
210289
-
210290210329
/*
210291210330
** Given that a JSONB_ARRAY object starts at offset i, return
210292210331
** the number of entries in that array.
210293210332
*/
210294210333
static u32 jsonbArrayCount(JsonParse *pParse, u32 iRoot){
@@ -210517,11 +210556,25 @@
210517210556
case 'f': { *piOut = '\f'; return 2; }
210518210557
case 'n': { *piOut = '\n'; return 2; }
210519210558
case 'r': { *piOut = '\r'; return 2; }
210520210559
case 't': { *piOut = '\t'; return 2; }
210521210560
case 'v': { *piOut = '\v'; return 2; }
210522
- case '0': { *piOut = 0; return 2; }
210561
+ case '0': {
210562
+ /* JSON5 requires that the \0 escape not be followed by a digit.
210563
+ ** But SQLite did not enforce this restriction in versions 3.42.0
210564
+ ** through 3.49.2. That was a bug. But some applications might have
210565
+ ** come to depend on that bug. Use the SQLITE_BUG_COMPATIBLE_20250510
210566
+ ** option to restore the old buggy behavior. */
210567
+#ifdef SQLITE_BUG_COMPATIBLE_20250510
210568
+ /* Legacy bug-compatible behavior */
210569
+ *piOut = 0;
210570
+#else
210571
+ /* Correct behavior */
210572
+ *piOut = (n>2 && sqlite3Isdigit(z[2])) ? JSON_INVALID_CHAR : 0;
210573
+#endif
210574
+ return 2;
210575
+ }
210523210576
case '\'':
210524210577
case '"':
210525210578
case '/':
210526210579
case '\\':{ *piOut = z[1]; return 2; }
210527210580
case 'x': {
@@ -211112,14 +211165,11 @@
211112211165
pParse->aBlob = aNull;
211113211166
pParse->nBlob = 1;
211114211167
return 0;
211115211168
}
211116211169
case SQLITE_BLOB: {
211117
- if( jsonFuncArgMightBeBinary(pArg) ){
211118
- pParse->aBlob = (u8*)sqlite3_value_blob(pArg);
211119
- pParse->nBlob = sqlite3_value_bytes(pArg);
211120
- }else{
211170
+ if( !jsonArgIsJsonb(pArg, pParse) ){
211121211171
sqlite3_result_error(ctx, "JSON cannot hold BLOB values", -1);
211122211172
return 1;
211123211173
}
211124211174
break;
211125211175
}
@@ -211266,31 +211316,50 @@
211266211316
}
211267211317
211268211318
/*
211269211319
** If pArg is a blob that seems like a JSONB blob, then initialize
211270211320
** p to point to that JSONB and return TRUE. If pArg does not seem like
211271
-** a JSONB blob, then return FALSE;
211321
+** a JSONB blob, then return FALSE.
211272211322
**
211273
-** This routine is only called if it is already known that pArg is a
211274
-** blob. The only open question is whether or not the blob appears
211275
-** to be a JSONB blob.
211323
+** For small BLOBs (having no more than 7 bytes of payload) a full
211324
+** validity check is done. So for small BLOBs this routine only returns
211325
+** true if the value is guaranteed to be a valid JSONB. For larger BLOBs
211326
+** (8 byte or more of payload) only the size of the outermost element is
211327
+** checked to verify that the BLOB is superficially valid JSONB.
211328
+**
211329
+** A full JSONB validation is done on smaller BLOBs because those BLOBs might
211330
+** also be text JSON that has been incorrectly cast into a BLOB.
211331
+** (See tag-20240123-a and https://sqlite.org/forum/forumpost/012136abd5)
211332
+** If the BLOB is 9 bytes are larger, then it is not possible for the
211333
+** superficial size check done here to pass if the input is really text
211334
+** JSON so we do not need to look deeper in that case.
211335
+**
211336
+** Why we only need to do full JSONB validation for smaller BLOBs:
211337
+**
211338
+** The first byte of valid JSON text must be one of: '{', '[', '"', ' ', '\n',
211339
+** '\r', '\t', '-', or a digit '0' through '9'. Of these, only a subset
211340
+** can also be the first byte of JSONB: '{', '[', and digits '3'
211341
+** through '9'. In every one of those cases, the payload size is 7 bytes
211342
+** or less. So if we do full JSONB validation for every BLOB where the
211343
+** payload is less than 7 bytes, we will never get a false positive for
211344
+** JSONB on an input that is really text JSON.
211276211345
*/
211277211346
static int jsonArgIsJsonb(sqlite3_value *pArg, JsonParse *p){
211278211347
u32 n, sz = 0;
211348
+ u8 c;
211349
+ if( sqlite3_value_type(pArg)!=SQLITE_BLOB ) return 0;
211279211350
p->aBlob = (u8*)sqlite3_value_blob(pArg);
211280211351
p->nBlob = (u32)sqlite3_value_bytes(pArg);
211281
- if( p->nBlob==0 ){
211282
- p->aBlob = 0;
211283
- return 0;
211284
- }
211285
- if( NEVER(p->aBlob==0) ){
211286
- return 0;
211287
- }
211288
- if( (p->aBlob[0] & 0x0f)<=JSONB_OBJECT
211352
+ if( p->nBlob>0
211353
+ && ALWAYS(p->aBlob!=0)
211354
+ && ((c = p->aBlob[0]) & 0x0f)<=JSONB_OBJECT
211289211355
&& (n = jsonbPayloadSize(p, 0, &sz))>0
211290211356
&& sz+n==p->nBlob
211291
- && ((p->aBlob[0] & 0x0f)>JSONB_FALSE || sz==0)
211357
+ && ((c & 0x0f)>JSONB_FALSE || sz==0)
211358
+ && (sz>7
211359
+ || (c!=0x7b && c!=0x5b && !sqlite3Isdigit(c))
211360
+ || jsonbValidityCheck(p, 0, p->nBlob, 1)==0)
211292211361
){
211293211362
return 1;
211294211363
}
211295211364
p->aBlob = 0;
211296211365
p->nBlob = 0;
@@ -212379,25 +212448,21 @@
212379212448
sqlite3_result_int(ctx, 0);
212380212449
#endif
212381212450
return;
212382212451
}
212383212452
case SQLITE_BLOB: {
212384
- if( jsonFuncArgMightBeBinary(argv[0]) ){
212453
+ JsonParse py;
212454
+ memset(&py, 0, sizeof(py));
212455
+ if( jsonArgIsJsonb(argv[0], &py) ){
212385212456
if( flags & 0x04 ){
212386212457
/* Superficial checking only - accomplished by the
212387
- ** jsonFuncArgMightBeBinary() call above. */
212458
+ ** jsonArgIsJsonb() call above. */
212388212459
res = 1;
212389212460
}else if( flags & 0x08 ){
212390212461
/* Strict checking. Check by translating BLOB->TEXT->BLOB. If
212391212462
** no errors occur, call that a "strict check". */
212392
- JsonParse px;
212393
- u32 iErr;
212394
- memset(&px, 0, sizeof(px));
212395
- px.aBlob = (u8*)sqlite3_value_blob(argv[0]);
212396
- px.nBlob = sqlite3_value_bytes(argv[0]);
212397
- iErr = jsonbValidityCheck(&px, 0, px.nBlob, 1);
212398
- res = iErr==0;
212463
+ res = 0==jsonbValidityCheck(&py, 0, py.nBlob, 1);
212399212464
}
212400212465
break;
212401212466
}
212402212467
/* Fall through into interpreting the input as text. See note
212403212468
** above at tag-20240123-a. */
@@ -212451,13 +212516,11 @@
212451212516
212452212517
assert( argc==1 );
212453212518
UNUSED_PARAMETER(argc);
212454212519
memset(&s, 0, sizeof(s));
212455212520
s.db = sqlite3_context_db_handle(ctx);
212456
- if( jsonFuncArgMightBeBinary(argv[0]) ){
212457
- s.aBlob = (u8*)sqlite3_value_blob(argv[0]);
212458
- s.nBlob = sqlite3_value_bytes(argv[0]);
212521
+ if( jsonArgIsJsonb(argv[0], &s) ){
212459212522
iErrPos = (i64)jsonbValidityCheck(&s, 0, s.nBlob, 1);
212460212523
}else{
212461212524
s.zJson = (char*)sqlite3_value_text(argv[0]);
212462212525
if( s.zJson==0 ) return; /* NULL input or OOM */
212463212526
s.nJson = sqlite3_value_bytes(argv[0]);
@@ -213138,13 +213201,12 @@
213138213201
jsonEachCursorReset(p);
213139213202
if( idxNum==0 ) return SQLITE_OK;
213140213203
memset(&p->sParse, 0, sizeof(p->sParse));
213141213204
p->sParse.nJPRef = 1;
213142213205
p->sParse.db = p->db;
213143
- if( jsonFuncArgMightBeBinary(argv[0]) ){
213144
- p->sParse.nBlob = sqlite3_value_bytes(argv[0]);
213145
- p->sParse.aBlob = (u8*)sqlite3_value_blob(argv[0]);
213206
+ if( jsonArgIsJsonb(argv[0], &p->sParse) ){
213207
+ /* We have JSONB */
213146213208
}else{
213147213209
p->sParse.zJson = (char*)sqlite3_value_text(argv[0]);
213148213210
p->sParse.nJson = sqlite3_value_bytes(argv[0]);
213149213211
if( p->sParse.zJson==0 ){
213150213212
p->i = p->iEnd = 0;
@@ -257213,11 +257275,11 @@
257213257275
int nArg, /* Number of args */
257214257276
sqlite3_value **apUnused /* Function arguments */
257215257277
){
257216257278
assert( nArg==0 );
257217257279
UNUSED_PARAM2(nArg, apUnused);
257218
- sqlite3_result_text(pCtx, "fts5: 2025-04-15 21:59:38 d22475b81c4e26ccc50f3b5626d43b32f7a2de34e5a764539554665bdda735d5", -1, SQLITE_TRANSIENT);
257280
+ sqlite3_result_text(pCtx, "fts5: 2025-05-15 11:20:54 336ceeccc6f85bd78f4a26648af7edf9056d569a767b4120f125a02b2090a349", -1, SQLITE_TRANSIENT);
257219257281
}
257220257282
257221257283
/*
257222257284
** Implementation of fts5_locale(LOCALE, TEXT) function.
257223257285
**
257224257286
--- extsrc/sqlite3.c
+++ extsrc/sqlite3.c
@@ -16,11 +16,11 @@
16 ** if you want a wrapper to interface SQLite with your choice of programming
17 ** language. The code for the "sqlite3" command-line shell is also in a
18 ** separate file. This file contains only code for the core SQLite library.
19 **
20 ** The content in this amalgamation comes from Fossil check-in
21 ** d22475b81c4e26ccc50f3b5626d43b32f7a2 with changes in files:
22 **
23 **
24 */
25 #ifndef SQLITE_AMALGAMATION
26 #define SQLITE_CORE 1
@@ -465,11 +465,11 @@
465 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
466 ** [sqlite_version()] and [sqlite_source_id()].
467 */
468 #define SQLITE_VERSION "3.50.0"
469 #define SQLITE_VERSION_NUMBER 3050000
470 #define SQLITE_SOURCE_ID "2025-04-15 21:59:38 d22475b81c4e26ccc50f3b5626d43b32f7a2de34e5a764539554665bdda735d5"
471
472 /*
473 ** CAPI3REF: Run-Time Library Version Numbers
474 ** KEYWORDS: sqlite3_version sqlite3_sourceid
475 **
@@ -11872,13 +11872,14 @@
11872 ** This may appear to have some counter-intuitive effects if a single row
11873 ** is written to more than once during a session. For example, if a row
11874 ** is inserted while a session object is enabled, then later deleted while
11875 ** the same session object is disabled, no INSERT record will appear in the
11876 ** changeset, even though the delete took place while the session was disabled.
11877 ** Or, if one field of a row is updated while a session is disabled, and
11878 ** another field of the same row is updated while the session is enabled, the
11879 ** resulting changeset will contain an UPDATE change that updates both fields.
 
11880 */
11881 SQLITE_API int sqlite3session_changeset(
11882 sqlite3_session *pSession, /* Session object */
11883 int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
11884 void **ppChangeset /* OUT: Buffer containing changeset */
@@ -12083,11 +12084,11 @@
12083 ** CAPI3REF: Flags for sqlite3changeset_start_v2
12084 **
12085 ** The following flags may passed via the 4th parameter to
12086 ** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
12087 **
12088 ** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
12089 ** Invert the changeset while iterating through it. This is equivalent to
12090 ** inverting a changeset using sqlite3changeset_invert() before applying it.
12091 ** It is an error to specify this flag with a patchset.
12092 */
12093 #define SQLITE_CHANGESETSTART_INVERT 0x0002
@@ -19160,11 +19161,10 @@
19160 unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */
19161 unsigned isResized:1; /* True if resizeIndexObject() has been called */
19162 unsigned isCovering:1; /* True if this is a covering index */
19163 unsigned noSkipScan:1; /* Do not try to use skip-scan if true */
19164 unsigned hasStat1:1; /* aiRowLogEst values come from sqlite_stat1 */
19165 unsigned bLowQual:1; /* sqlite_stat1 says this is a low-quality index */
19166 unsigned bNoQuery:1; /* Do not use this index to optimize queries */
19167 unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */
19168 unsigned bIdxRowid:1; /* One or more of the index keys is the ROWID */
19169 unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */
19170 unsigned bHasExpr:1; /* Index contains an expression, either a literal
@@ -22424,10 +22424,13 @@
22424 #ifdef SQLITE_BITMASK_TYPE
22425 "BITMASK_TYPE=" CTIMEOPT_VAL(SQLITE_BITMASK_TYPE),
22426 #endif
22427 #ifdef SQLITE_BUG_COMPATIBLE_20160819
22428 "BUG_COMPATIBLE_20160819",
 
 
 
22429 #endif
22430 #ifdef SQLITE_CASE_SENSITIVE_LIKE
22431 "CASE_SENSITIVE_LIKE",
22432 #endif
22433 #ifdef SQLITE_CHECK_PAGES
@@ -32987,10 +32990,19 @@
32987 va_end(ap);
32988 zBuf[acc.nChar] = 0;
32989 return zBuf;
32990 }
32991
 
 
 
 
 
 
 
 
 
32992 /*
32993 ** This is the routine that actually formats the sqlite3_log() message.
32994 ** We house it in a separate routine from sqlite3_log() to avoid using
32995 ** stack space on small-stack systems when logging is disabled.
32996 **
@@ -33003,11 +33015,11 @@
33003 ** Care must be taken that any sqlite3_log() calls that occur while the
33004 ** memory mutex is held do not use these mechanisms.
33005 */
33006 static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){
33007 StrAccum acc; /* String accumulator */
33008 char zMsg[SQLITE_PRINT_BUF_SIZE*3]; /* Complete log message */
33009
33010 sqlite3StrAccumInit(&acc, 0, zMsg, sizeof(zMsg), 0);
33011 sqlite3_str_vappendf(&acc, zFormat, ap);
33012 sqlite3GlobalConfig.xLog(sqlite3GlobalConfig.pLogArg, iErrCode,
33013 sqlite3StrAccumFinish(&acc));
@@ -87262,10 +87274,13 @@
87262 ** into register iDest, then add the OPFLAG_TYPEOFARG flag to that
87263 ** opcode.
87264 */
87265 SQLITE_PRIVATE void sqlite3VdbeTypeofColumn(Vdbe *p, int iDest){
87266 VdbeOp *pOp = sqlite3VdbeGetLastOp(p);
 
 
 
87267 if( pOp->p3==iDest && pOp->opcode==OP_Column ){
87268 pOp->p5 |= OPFLAG_TYPEOFARG;
87269 }
87270 }
87271
@@ -95716,11 +95731,11 @@
95716 }
95717 }else{
95718 sqlite3VdbeError(p, "%s", pOp->p4.z);
95719 }
95720 pcx = (int)(pOp - aOp);
95721 sqlite3_log(pOp->p1, "abort at %d in [%s]: %s", pcx, p->zSql, p->zErrMsg);
95722 }
95723 rc = sqlite3VdbeHalt(p);
95724 assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR );
95725 if( rc==SQLITE_BUSY ){
95726 p->rc = SQLITE_BUSY;
@@ -97042,11 +97057,11 @@
97042 pOut->u.i = ~sqlite3VdbeIntValue(pIn1);
97043 }
97044 break;
97045 }
97046
97047 /* Opcode: Once P1 P2 * * *
97048 **
97049 ** Fall through to the next instruction the first time this opcode is
97050 ** encountered on each invocation of the byte-code program. Jump to P2
97051 ** on the second and all subsequent encounters during the same invocation.
97052 **
@@ -97058,10 +97073,16 @@
97058 **
97059 ** For subprograms, there is a bitmask in the VdbeFrame that determines
97060 ** whether or not the jump should be taken. The bitmask is necessary
97061 ** because the self-altering code trick does not work for recursive
97062 ** triggers.
 
 
 
 
 
 
97063 */
97064 case OP_Once: { /* jump */
97065 u32 iAddr; /* Address of this instruction */
97066 assert( p->aOp[0].opcode==OP_Init );
97067 if( p->pFrame ){
@@ -98103,10 +98124,11 @@
98103 }
98104 }else{
98105 zHdr += sqlite3PutVarint(zHdr, serial_type);
98106 if( pRec->n ){
98107 assert( pRec->z!=0 );
 
98108 memcpy(zPayload, pRec->z, pRec->n);
98109 zPayload += pRec->n;
98110 }
98111 }
98112 if( pRec==pLast ) break;
@@ -103551,12 +103573,12 @@
103551 sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc));
103552 }
103553 p->rc = rc;
103554 sqlite3SystemError(db, rc);
103555 testcase( sqlite3GlobalConfig.xLog!=0 );
103556 sqlite3_log(rc, "statement aborts at %d: [%s] %s",
103557 (int)(pOp - aOp), p->zSql, p->zErrMsg);
103558 if( p->eVdbeState==VDBE_RUN_STATE ) sqlite3VdbeHalt(p);
103559 if( rc==SQLITE_IOERR_NOMEM ) sqlite3OomFault(db);
103560 if( rc==SQLITE_CORRUPT && db->autoCommit==0 ){
103561 db->flags |= SQLITE_CorruptRdOnly;
103562 }
@@ -109287,17 +109309,16 @@
109287 /* Clearly non-deterministic functions like random(), but also
109288 ** date/time functions that use 'now', and other functions like
109289 ** sqlite_version() that might change over time cannot be used
109290 ** in an index or generated column. Curiously, they can be used
109291 ** in a CHECK constraint. SQLServer, MySQL, and PostgreSQL all
109292 ** all this. */
109293 sqlite3ResolveNotValid(pParse, pNC, "non-deterministic functions",
109294 NC_IdxExpr|NC_PartIdx|NC_GenCol, 0, pExpr);
109295 }else{
109296 assert( (NC_SelfRef & 0xff)==NC_SelfRef ); /* Must fit in 8 bits */
109297 pExpr->op2 = pNC->ncFlags & NC_SelfRef;
109298 if( pNC->ncFlags & NC_FromDDL ) ExprSetProperty(pExpr, EP_FromDDL);
109299 }
109300 if( (pDef->funcFlags & SQLITE_FUNC_INTERNAL)!=0
109301 && pParse->nested==0
109302 && (pParse->db->mDbFlags & DBFLAG_InternalFunc)==0
109303 ){
@@ -109309,10 +109330,11 @@
109309 pDef = 0;
109310 }else
109311 if( (pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE))!=0
109312 && !IN_RENAME_OBJECT
109313 ){
 
109314 sqlite3ExprFunctionUsable(pParse, pExpr, pDef);
109315 }
109316 }
109317
109318 if( 0==IN_RENAME_OBJECT ){
@@ -114022,15 +114044,16 @@
114022 pCopy = sqlite3SelectDup(pParse->db, pSelect, 0);
114023 rc = pParse->db->mallocFailed ? 1 :sqlite3Select(pParse, pCopy, &dest);
114024 sqlite3SelectDelete(pParse->db, pCopy);
114025 sqlite3DbFree(pParse->db, dest.zAffSdst);
114026 if( addrBloom ){
 
 
114027 sqlite3VdbeGetOp(v, addrOnce)->p3 = dest.iSDParm2;
114028 if( dest.iSDParm2==0 ){
114029 sqlite3VdbeChangeToNoop(v, addrBloom);
114030 }else{
114031 sqlite3VdbeGetOp(v, addrOnce)->p3 = dest.iSDParm2;
114032 }
114033 }
114034 if( rc ){
114035 sqlite3KeyInfoUnref(pKeyInfo);
114036 return;
@@ -114473,11 +114496,11 @@
114473 if( destIfFalse==destIfNull ){
114474 /* Combine Step 3 and Step 5 into a single opcode */
114475 if( ExprHasProperty(pExpr, EP_Subrtn) ){
114476 const VdbeOp *pOp = sqlite3VdbeGetOp(v, pExpr->y.sub.iAddr);
114477 assert( pOp->opcode==OP_Once || pParse->nErr );
114478 if( pOp->opcode==OP_Once && pOp->p3>0 ){
114479 assert( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) );
114480 sqlite3VdbeAddOp4Int(v, OP_Filter, pOp->p3, destIfFalse,
114481 rLhs, nVector); VdbeCoverage(v);
114482 }
114483 }
@@ -116322,15 +116345,15 @@
116322 case TK_ISNULL:
116323 case TK_NOTNULL: {
116324 assert( TK_ISNULL==OP_IsNull ); testcase( op==TK_ISNULL );
116325 assert( TK_NOTNULL==OP_NotNull ); testcase( op==TK_NOTNULL );
116326 r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
116327 sqlite3VdbeTypeofColumn(v, r1);
 
116328 sqlite3VdbeAddOp2(v, op, r1, dest);
116329 VdbeCoverageIf(v, op==TK_ISNULL);
116330 VdbeCoverageIf(v, op==TK_NOTNULL);
116331 testcase( regFree1==0 );
116332 break;
116333 }
116334 case TK_BETWEEN: {
116335 testcase( jumpIfNull==0 );
116336 exprCodeBetween(pParse, pExpr, dest, sqlite3ExprIfTrue, jumpIfNull);
@@ -116497,15 +116520,15 @@
116497 break;
116498 }
116499 case TK_ISNULL:
116500 case TK_NOTNULL: {
116501 r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
116502 sqlite3VdbeTypeofColumn(v, r1);
 
116503 sqlite3VdbeAddOp2(v, op, r1, dest);
116504 testcase( op==TK_ISNULL ); VdbeCoverageIf(v, op==TK_ISNULL);
116505 testcase( op==TK_NOTNULL ); VdbeCoverageIf(v, op==TK_NOTNULL);
116506 testcase( regFree1==0 );
116507 break;
116508 }
116509 case TK_BETWEEN: {
116510 testcase( jumpIfNull==0 );
116511 exprCodeBetween(pParse, pExpr, dest, sqlite3ExprIfFalse, jumpIfNull);
@@ -121683,20 +121706,10 @@
121683 }
121684 #endif
121685 while( z[0]!=0 && z[0]!=' ' ) z++;
121686 while( z[0]==' ' ) z++;
121687 }
121688
121689 /* Set the bLowQual flag if the peak number of rows obtained
121690 ** from a full equality match is so large that a full table scan
121691 ** seems likely to be faster than using the index.
121692 */
121693 if( aLog[0] > 66 /* Index has more than 100 rows */
121694 && aLog[0] <= aLog[nOut-1] /* And only a single value seen */
121695 ){
121696 pIndex->bLowQual = 1;
121697 }
121698 }
121699 }
121700
121701 /*
121702 ** This callback is invoked once for each index when reading the
@@ -124091,11 +124104,11 @@
124091 */
124092 SQLITE_PRIVATE int sqlite3TableColumnToIndex(Index *pIdx, int iCol){
124093 int i;
124094 i16 iCol16;
124095 assert( iCol>=(-1) && iCol<=SQLITE_MAX_COLUMN );
124096 assert( pIdx->nColumn<=SQLITE_MAX_COLUMN );
124097 iCol16 = iCol;
124098 for(i=0; i<pIdx->nColumn; i++){
124099 if( iCol16==pIdx->aiColumn[i] ){
124100 return i;
124101 }
@@ -167620,15 +167633,12 @@
167620 opMask = WO_LT|WO_LE;
167621 }else{
167622 assert( pNew->u.btree.nBtm==0 );
167623 opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS;
167624 }
167625 if( pProbe->bUnordered || pProbe->bLowQual ){
167626 if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
167627 if( pProbe->bLowQual && pSrc->fg.isIndexedBy==0 ){
167628 opMask &= ~(WO_EQ|WO_IN|WO_IS);
167629 }
167630 }
167631
167632 assert( pNew->u.btree.nEq<pProbe->nColumn );
167633 assert( pNew->u.btree.nEq<pProbe->nKeyCol
167634 || pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY );
@@ -168561,11 +168571,11 @@
168561 }
168562 }else if( m==0
168563 && (HasRowid(pTab) || pWInfo->pSelect!=0 || sqlite3FaultSim(700))
168564 ){
168565 WHERETRACE(0x200,
168566 ("-> %s a covering index according to bitmasks\n",
168567 pProbe->zName, m==0 ? "is" : "is not"));
168568 pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED;
168569 }
168570 }
168571
@@ -207968,60 +207978,113 @@
207968 ** Growing our own isspace() routine this way is twice as fast as
207969 ** the library isspace() function, resulting in a 7% overall performance
207970 ** increase for the text-JSON parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os).
207971 */
207972 static const char jsonIsSpace[] = {
207973 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0,
207974 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207975 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207976 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207977 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207978 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207979 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207980 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207981
207982 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207983 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207984 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207985 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207986 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207987 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207988 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
207989 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
207990 };
207991 #define jsonIsspace(x) (jsonIsSpace[(unsigned char)x])
207992
207993 /*
207994 ** The set of all space characters recognized by jsonIsspace().
207995 ** Useful as the second argument to strspn().
207996 */
 
207997 static const char jsonSpaces[] = "\011\012\015\040";
 
 
 
 
 
207998
207999 /*
208000 ** Characters that are special to JSON. Control characters,
208001 ** '"' and '\\' and '\''. Actually, '\'' is not special to
208002 ** canonical JSON, but it is special in JSON-5, so we include
208003 ** it in the set of special characters.
208004 */
208005 static const char jsonIsOk[256] = {
208006 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
208007 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
208008 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
208009 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
208010 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
208011 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1,
208012 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
208013 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
208014
208015 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
208016 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
208017 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
208018 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
208019 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
208020 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
208021 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
208022 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
208023 };
208024
208025 /* Objects */
208026 typedef struct JsonCache JsonCache;
208027 typedef struct JsonString JsonString;
@@ -208162,11 +208225,11 @@
208162
208163 /**************************************************************************
208164 ** Forward references
208165 **************************************************************************/
208166 static void jsonReturnStringAsBlob(JsonString*);
208167 static int jsonFuncArgMightBeBinary(sqlite3_value *pJson);
208168 static u32 jsonTranslateBlobToText(const JsonParse*,u32,JsonString*);
208169 static void jsonReturnParse(sqlite3_context*,JsonParse*);
208170 static JsonParse *jsonParseFuncArg(sqlite3_context*,sqlite3_value*,u32);
208171 static void jsonParseFree(JsonParse*);
208172 static u32 jsonbPayloadSize(const JsonParse*, u32, u32*);
@@ -208580,15 +208643,13 @@
208580 jsonAppendString(p, z, n);
208581 }
208582 break;
208583 }
208584 default: {
208585 if( jsonFuncArgMightBeBinary(pValue) ){
208586 JsonParse px;
208587 memset(&px, 0, sizeof(px));
208588 px.aBlob = (u8*)sqlite3_value_blob(pValue);
208589 px.nBlob = sqlite3_value_bytes(pValue);
208590 jsonTranslateBlobToText(&px, 0, p);
208591 }else if( p->eErr==0 ){
208592 sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1);
208593 p->eErr = JSTRING_ERR;
208594 jsonStringReset(p);
@@ -209522,11 +209583,16 @@
209522 c = z[++j];
209523 if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f'
209524 || c=='n' || c=='r' || c=='t'
209525 || (c=='u' && jsonIs4Hex(&z[j+1])) ){
209526 if( opcode==JSONB_TEXT ) opcode = JSONB_TEXTJ;
209527 }else if( c=='\'' || c=='0' || c=='v' || c=='\n'
 
 
 
 
 
209528 || (0xe2==(u8)c && 0x80==(u8)z[j+1]
209529 && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2]))
209530 || (c=='x' && jsonIs2Hex(&z[j+1])) ){
209531 opcode = JSONB_TEXT5;
209532 pParse->hasNonstd = 1;
@@ -209909,11 +209975,11 @@
209909 || pParse->aBlob[i+4]!=0
209910 ){
209911 *pSz = 0;
209912 return 0;
209913 }
209914 sz = (pParse->aBlob[i+5]<<24) + (pParse->aBlob[i+6]<<16) +
209915 (pParse->aBlob[i+7]<<8) + pParse->aBlob[i+8];
209916 n = 9;
209917 }
209918 if( (i64)i+sz+n > pParse->nBlob
209919 && (i64)i+sz+n > pParse->nBlob-pParse->delta
@@ -210258,37 +210324,10 @@
210258 }
210259 }
210260 return i;
210261 }
210262
210263
210264 /* Return true if the input pJson
210265 **
210266 ** For performance reasons, this routine does not do a detailed check of the
210267 ** input BLOB to ensure that it is well-formed. Hence, false positives are
210268 ** possible. False negatives should never occur, however.
210269 */
210270 static int jsonFuncArgMightBeBinary(sqlite3_value *pJson){
210271 u32 sz, n;
210272 const u8 *aBlob;
210273 int nBlob;
210274 JsonParse s;
210275 if( sqlite3_value_type(pJson)!=SQLITE_BLOB ) return 0;
210276 aBlob = sqlite3_value_blob(pJson);
210277 nBlob = sqlite3_value_bytes(pJson);
210278 if( nBlob<1 ) return 0;
210279 if( NEVER(aBlob==0) || (aBlob[0] & 0x0f)>JSONB_OBJECT ) return 0;
210280 memset(&s, 0, sizeof(s));
210281 s.aBlob = (u8*)aBlob;
210282 s.nBlob = nBlob;
210283 n = jsonbPayloadSize(&s, 0, &sz);
210284 if( n==0 ) return 0;
210285 if( sz+n!=(u32)nBlob ) return 0;
210286 if( (aBlob[0] & 0x0f)<=JSONB_FALSE && sz>0 ) return 0;
210287 return sz+n==(u32)nBlob;
210288 }
210289
210290 /*
210291 ** Given that a JSONB_ARRAY object starts at offset i, return
210292 ** the number of entries in that array.
210293 */
210294 static u32 jsonbArrayCount(JsonParse *pParse, u32 iRoot){
@@ -210517,11 +210556,25 @@
210517 case 'f': { *piOut = '\f'; return 2; }
210518 case 'n': { *piOut = '\n'; return 2; }
210519 case 'r': { *piOut = '\r'; return 2; }
210520 case 't': { *piOut = '\t'; return 2; }
210521 case 'v': { *piOut = '\v'; return 2; }
210522 case '0': { *piOut = 0; return 2; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210523 case '\'':
210524 case '"':
210525 case '/':
210526 case '\\':{ *piOut = z[1]; return 2; }
210527 case 'x': {
@@ -211112,14 +211165,11 @@
211112 pParse->aBlob = aNull;
211113 pParse->nBlob = 1;
211114 return 0;
211115 }
211116 case SQLITE_BLOB: {
211117 if( jsonFuncArgMightBeBinary(pArg) ){
211118 pParse->aBlob = (u8*)sqlite3_value_blob(pArg);
211119 pParse->nBlob = sqlite3_value_bytes(pArg);
211120 }else{
211121 sqlite3_result_error(ctx, "JSON cannot hold BLOB values", -1);
211122 return 1;
211123 }
211124 break;
211125 }
@@ -211266,31 +211316,50 @@
211266 }
211267
211268 /*
211269 ** If pArg is a blob that seems like a JSONB blob, then initialize
211270 ** p to point to that JSONB and return TRUE. If pArg does not seem like
211271 ** a JSONB blob, then return FALSE;
211272 **
211273 ** This routine is only called if it is already known that pArg is a
211274 ** blob. The only open question is whether or not the blob appears
211275 ** to be a JSONB blob.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
211276 */
211277 static int jsonArgIsJsonb(sqlite3_value *pArg, JsonParse *p){
211278 u32 n, sz = 0;
 
 
211279 p->aBlob = (u8*)sqlite3_value_blob(pArg);
211280 p->nBlob = (u32)sqlite3_value_bytes(pArg);
211281 if( p->nBlob==0 ){
211282 p->aBlob = 0;
211283 return 0;
211284 }
211285 if( NEVER(p->aBlob==0) ){
211286 return 0;
211287 }
211288 if( (p->aBlob[0] & 0x0f)<=JSONB_OBJECT
211289 && (n = jsonbPayloadSize(p, 0, &sz))>0
211290 && sz+n==p->nBlob
211291 && ((p->aBlob[0] & 0x0f)>JSONB_FALSE || sz==0)
 
 
 
211292 ){
211293 return 1;
211294 }
211295 p->aBlob = 0;
211296 p->nBlob = 0;
@@ -212379,25 +212448,21 @@
212379 sqlite3_result_int(ctx, 0);
212380 #endif
212381 return;
212382 }
212383 case SQLITE_BLOB: {
212384 if( jsonFuncArgMightBeBinary(argv[0]) ){
 
 
212385 if( flags & 0x04 ){
212386 /* Superficial checking only - accomplished by the
212387 ** jsonFuncArgMightBeBinary() call above. */
212388 res = 1;
212389 }else if( flags & 0x08 ){
212390 /* Strict checking. Check by translating BLOB->TEXT->BLOB. If
212391 ** no errors occur, call that a "strict check". */
212392 JsonParse px;
212393 u32 iErr;
212394 memset(&px, 0, sizeof(px));
212395 px.aBlob = (u8*)sqlite3_value_blob(argv[0]);
212396 px.nBlob = sqlite3_value_bytes(argv[0]);
212397 iErr = jsonbValidityCheck(&px, 0, px.nBlob, 1);
212398 res = iErr==0;
212399 }
212400 break;
212401 }
212402 /* Fall through into interpreting the input as text. See note
212403 ** above at tag-20240123-a. */
@@ -212451,13 +212516,11 @@
212451
212452 assert( argc==1 );
212453 UNUSED_PARAMETER(argc);
212454 memset(&s, 0, sizeof(s));
212455 s.db = sqlite3_context_db_handle(ctx);
212456 if( jsonFuncArgMightBeBinary(argv[0]) ){
212457 s.aBlob = (u8*)sqlite3_value_blob(argv[0]);
212458 s.nBlob = sqlite3_value_bytes(argv[0]);
212459 iErrPos = (i64)jsonbValidityCheck(&s, 0, s.nBlob, 1);
212460 }else{
212461 s.zJson = (char*)sqlite3_value_text(argv[0]);
212462 if( s.zJson==0 ) return; /* NULL input or OOM */
212463 s.nJson = sqlite3_value_bytes(argv[0]);
@@ -213138,13 +213201,12 @@
213138 jsonEachCursorReset(p);
213139 if( idxNum==0 ) return SQLITE_OK;
213140 memset(&p->sParse, 0, sizeof(p->sParse));
213141 p->sParse.nJPRef = 1;
213142 p->sParse.db = p->db;
213143 if( jsonFuncArgMightBeBinary(argv[0]) ){
213144 p->sParse.nBlob = sqlite3_value_bytes(argv[0]);
213145 p->sParse.aBlob = (u8*)sqlite3_value_blob(argv[0]);
213146 }else{
213147 p->sParse.zJson = (char*)sqlite3_value_text(argv[0]);
213148 p->sParse.nJson = sqlite3_value_bytes(argv[0]);
213149 if( p->sParse.zJson==0 ){
213150 p->i = p->iEnd = 0;
@@ -257213,11 +257275,11 @@
257213 int nArg, /* Number of args */
257214 sqlite3_value **apUnused /* Function arguments */
257215 ){
257216 assert( nArg==0 );
257217 UNUSED_PARAM2(nArg, apUnused);
257218 sqlite3_result_text(pCtx, "fts5: 2025-04-15 21:59:38 d22475b81c4e26ccc50f3b5626d43b32f7a2de34e5a764539554665bdda735d5", -1, SQLITE_TRANSIENT);
257219 }
257220
257221 /*
257222 ** Implementation of fts5_locale(LOCALE, TEXT) function.
257223 **
257224
--- extsrc/sqlite3.c
+++ extsrc/sqlite3.c
@@ -16,11 +16,11 @@
16 ** if you want a wrapper to interface SQLite with your choice of programming
17 ** language. The code for the "sqlite3" command-line shell is also in a
18 ** separate file. This file contains only code for the core SQLite library.
19 **
20 ** The content in this amalgamation comes from Fossil check-in
21 ** 336ceeccc6f85bd78f4a26648af7edf9056d with changes in files:
22 **
23 **
24 */
25 #ifndef SQLITE_AMALGAMATION
26 #define SQLITE_CORE 1
@@ -465,11 +465,11 @@
465 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
466 ** [sqlite_version()] and [sqlite_source_id()].
467 */
468 #define SQLITE_VERSION "3.50.0"
469 #define SQLITE_VERSION_NUMBER 3050000
470 #define SQLITE_SOURCE_ID "2025-05-15 11:20:54 336ceeccc6f85bd78f4a26648af7edf9056d569a767b4120f125a02b2090a349"
471
472 /*
473 ** CAPI3REF: Run-Time Library Version Numbers
474 ** KEYWORDS: sqlite3_version sqlite3_sourceid
475 **
@@ -11872,13 +11872,14 @@
11872 ** This may appear to have some counter-intuitive effects if a single row
11873 ** is written to more than once during a session. For example, if a row
11874 ** is inserted while a session object is enabled, then later deleted while
11875 ** the same session object is disabled, no INSERT record will appear in the
11876 ** changeset, even though the delete took place while the session was disabled.
11877 ** Or, if one field of a row is updated while a session is enabled, and
11878 ** then another field of the same row is updated while the session is disabled,
11879 ** the resulting changeset will contain an UPDATE change that updates both
11880 ** fields.
11881 */
11882 SQLITE_API int sqlite3session_changeset(
11883 sqlite3_session *pSession, /* Session object */
11884 int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
11885 void **ppChangeset /* OUT: Buffer containing changeset */
@@ -12083,11 +12084,11 @@
12084 ** CAPI3REF: Flags for sqlite3changeset_start_v2
12085 **
12086 ** The following flags may passed via the 4th parameter to
12087 ** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
12088 **
12089 ** <dt>SQLITE_CHANGESETSTART_INVERT <dd>
12090 ** Invert the changeset while iterating through it. This is equivalent to
12091 ** inverting a changeset using sqlite3changeset_invert() before applying it.
12092 ** It is an error to specify this flag with a patchset.
12093 */
12094 #define SQLITE_CHANGESETSTART_INVERT 0x0002
@@ -19160,11 +19161,10 @@
19161 unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */
19162 unsigned isResized:1; /* True if resizeIndexObject() has been called */
19163 unsigned isCovering:1; /* True if this is a covering index */
19164 unsigned noSkipScan:1; /* Do not try to use skip-scan if true */
19165 unsigned hasStat1:1; /* aiRowLogEst values come from sqlite_stat1 */
 
19166 unsigned bNoQuery:1; /* Do not use this index to optimize queries */
19167 unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */
19168 unsigned bIdxRowid:1; /* One or more of the index keys is the ROWID */
19169 unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */
19170 unsigned bHasExpr:1; /* Index contains an expression, either a literal
@@ -22424,10 +22424,13 @@
22424 #ifdef SQLITE_BITMASK_TYPE
22425 "BITMASK_TYPE=" CTIMEOPT_VAL(SQLITE_BITMASK_TYPE),
22426 #endif
22427 #ifdef SQLITE_BUG_COMPATIBLE_20160819
22428 "BUG_COMPATIBLE_20160819",
22429 #endif
22430 #ifdef SQLITE_BUG_COMPATIBLE_20250510
22431 "BUG_COMPATIBLE_20250510",
22432 #endif
22433 #ifdef SQLITE_CASE_SENSITIVE_LIKE
22434 "CASE_SENSITIVE_LIKE",
22435 #endif
22436 #ifdef SQLITE_CHECK_PAGES
@@ -32987,10 +32990,19 @@
32990 va_end(ap);
32991 zBuf[acc.nChar] = 0;
32992 return zBuf;
32993 }
32994
32995 /* Maximum size of an sqlite3_log() message. */
32996 #if defined(SQLITE_MAX_LOG_MESSAGE)
32997 /* Leave the definition as supplied */
32998 #elif SQLITE_PRINT_BUF_SIZE*10>10000
32999 # define SQLITE_MAX_LOG_MESSAGE 10000
33000 #else
33001 # define SQLITE_MAX_LOG_MESSAGE (SQLITE_PRINT_BUF_SIZE*10)
33002 #endif
33003
33004 /*
33005 ** This is the routine that actually formats the sqlite3_log() message.
33006 ** We house it in a separate routine from sqlite3_log() to avoid using
33007 ** stack space on small-stack systems when logging is disabled.
33008 **
@@ -33003,11 +33015,11 @@
33015 ** Care must be taken that any sqlite3_log() calls that occur while the
33016 ** memory mutex is held do not use these mechanisms.
33017 */
33018 static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){
33019 StrAccum acc; /* String accumulator */
33020 char zMsg[SQLITE_MAX_LOG_MESSAGE]; /* Complete log message */
33021
33022 sqlite3StrAccumInit(&acc, 0, zMsg, sizeof(zMsg), 0);
33023 sqlite3_str_vappendf(&acc, zFormat, ap);
33024 sqlite3GlobalConfig.xLog(sqlite3GlobalConfig.pLogArg, iErrCode,
33025 sqlite3StrAccumFinish(&acc));
@@ -87262,10 +87274,13 @@
87274 ** into register iDest, then add the OPFLAG_TYPEOFARG flag to that
87275 ** opcode.
87276 */
87277 SQLITE_PRIVATE void sqlite3VdbeTypeofColumn(Vdbe *p, int iDest){
87278 VdbeOp *pOp = sqlite3VdbeGetLastOp(p);
87279 #ifdef SQLITE_DEBUG
87280 while( pOp->opcode==OP_ReleaseReg ) pOp--;
87281 #endif
87282 if( pOp->p3==iDest && pOp->opcode==OP_Column ){
87283 pOp->p5 |= OPFLAG_TYPEOFARG;
87284 }
87285 }
87286
@@ -95716,11 +95731,11 @@
95731 }
95732 }else{
95733 sqlite3VdbeError(p, "%s", pOp->p4.z);
95734 }
95735 pcx = (int)(pOp - aOp);
95736 sqlite3_log(pOp->p1, "abort at %d: %s; [%s]", pcx, p->zErrMsg, p->zSql);
95737 }
95738 rc = sqlite3VdbeHalt(p);
95739 assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR );
95740 if( rc==SQLITE_BUSY ){
95741 p->rc = SQLITE_BUSY;
@@ -97042,11 +97057,11 @@
97057 pOut->u.i = ~sqlite3VdbeIntValue(pIn1);
97058 }
97059 break;
97060 }
97061
97062 /* Opcode: Once P1 P2 P3 * *
97063 **
97064 ** Fall through to the next instruction the first time this opcode is
97065 ** encountered on each invocation of the byte-code program. Jump to P2
97066 ** on the second and all subsequent encounters during the same invocation.
97067 **
@@ -97058,10 +97073,16 @@
97073 **
97074 ** For subprograms, there is a bitmask in the VdbeFrame that determines
97075 ** whether or not the jump should be taken. The bitmask is necessary
97076 ** because the self-altering code trick does not work for recursive
97077 ** triggers.
97078 **
97079 ** The P3 operand is not used directly by this opcode. However P3 is
97080 ** used by the code generator as follows: If this opcode is the start
97081 ** of a subroutine and that subroutine uses a Bloom filter, then P3 will
97082 ** be the register that holds that Bloom filter. See tag-202407032019
97083 ** in the source code for implementation details.
97084 */
97085 case OP_Once: { /* jump */
97086 u32 iAddr; /* Address of this instruction */
97087 assert( p->aOp[0].opcode==OP_Init );
97088 if( p->pFrame ){
@@ -98103,10 +98124,11 @@
98124 }
98125 }else{
98126 zHdr += sqlite3PutVarint(zHdr, serial_type);
98127 if( pRec->n ){
98128 assert( pRec->z!=0 );
98129 assert( pRec->z!=(const char*)sqlite3CtypeMap );
98130 memcpy(zPayload, pRec->z, pRec->n);
98131 zPayload += pRec->n;
98132 }
98133 }
98134 if( pRec==pLast ) break;
@@ -103551,12 +103573,12 @@
103573 sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc));
103574 }
103575 p->rc = rc;
103576 sqlite3SystemError(db, rc);
103577 testcase( sqlite3GlobalConfig.xLog!=0 );
103578 sqlite3_log(rc, "statement aborts at %d: %s; [%s]",
103579 (int)(pOp - aOp), p->zErrMsg, p->zSql);
103580 if( p->eVdbeState==VDBE_RUN_STATE ) sqlite3VdbeHalt(p);
103581 if( rc==SQLITE_IOERR_NOMEM ) sqlite3OomFault(db);
103582 if( rc==SQLITE_CORRUPT && db->autoCommit==0 ){
103583 db->flags |= SQLITE_CorruptRdOnly;
103584 }
@@ -109287,17 +109309,16 @@
109309 /* Clearly non-deterministic functions like random(), but also
109310 ** date/time functions that use 'now', and other functions like
109311 ** sqlite_version() that might change over time cannot be used
109312 ** in an index or generated column. Curiously, they can be used
109313 ** in a CHECK constraint. SQLServer, MySQL, and PostgreSQL all
109314 ** allow this. */
109315 sqlite3ResolveNotValid(pParse, pNC, "non-deterministic functions",
109316 NC_IdxExpr|NC_PartIdx|NC_GenCol, 0, pExpr);
109317 }else{
109318 assert( (NC_SelfRef & 0xff)==NC_SelfRef ); /* Must fit in 8 bits */
109319 pExpr->op2 = pNC->ncFlags & NC_SelfRef;
 
109320 }
109321 if( (pDef->funcFlags & SQLITE_FUNC_INTERNAL)!=0
109322 && pParse->nested==0
109323 && (pParse->db->mDbFlags & DBFLAG_InternalFunc)==0
109324 ){
@@ -109309,10 +109330,11 @@
109330 pDef = 0;
109331 }else
109332 if( (pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE))!=0
109333 && !IN_RENAME_OBJECT
109334 ){
109335 if( pNC->ncFlags & NC_FromDDL ) ExprSetProperty(pExpr, EP_FromDDL);
109336 sqlite3ExprFunctionUsable(pParse, pExpr, pDef);
109337 }
109338 }
109339
109340 if( 0==IN_RENAME_OBJECT ){
@@ -114022,15 +114044,16 @@
114044 pCopy = sqlite3SelectDup(pParse->db, pSelect, 0);
114045 rc = pParse->db->mallocFailed ? 1 :sqlite3Select(pParse, pCopy, &dest);
114046 sqlite3SelectDelete(pParse->db, pCopy);
114047 sqlite3DbFree(pParse->db, dest.zAffSdst);
114048 if( addrBloom ){
114049 /* Remember that location of the Bloom filter in the P3 operand
114050 ** of the OP_Once that began this subroutine. tag-202407032019 */
114051 sqlite3VdbeGetOp(v, addrOnce)->p3 = dest.iSDParm2;
114052 if( dest.iSDParm2==0 ){
114053 /* If the Bloom filter won't actually be used, keep it small */
114054 sqlite3VdbeGetOp(v, addrBloom)->p1 = 10;
 
114055 }
114056 }
114057 if( rc ){
114058 sqlite3KeyInfoUnref(pKeyInfo);
114059 return;
@@ -114473,11 +114496,11 @@
114496 if( destIfFalse==destIfNull ){
114497 /* Combine Step 3 and Step 5 into a single opcode */
114498 if( ExprHasProperty(pExpr, EP_Subrtn) ){
114499 const VdbeOp *pOp = sqlite3VdbeGetOp(v, pExpr->y.sub.iAddr);
114500 assert( pOp->opcode==OP_Once || pParse->nErr );
114501 if( pOp->opcode==OP_Once && pOp->p3>0 ){ /* tag-202407032019 */
114502 assert( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) );
114503 sqlite3VdbeAddOp4Int(v, OP_Filter, pOp->p3, destIfFalse,
114504 rLhs, nVector); VdbeCoverage(v);
114505 }
114506 }
@@ -116322,15 +116345,15 @@
116345 case TK_ISNULL:
116346 case TK_NOTNULL: {
116347 assert( TK_ISNULL==OP_IsNull ); testcase( op==TK_ISNULL );
116348 assert( TK_NOTNULL==OP_NotNull ); testcase( op==TK_NOTNULL );
116349 r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
116350 assert( regFree1==0 || regFree1==r1 );
116351 if( regFree1 ) sqlite3VdbeTypeofColumn(v, r1);
116352 sqlite3VdbeAddOp2(v, op, r1, dest);
116353 VdbeCoverageIf(v, op==TK_ISNULL);
116354 VdbeCoverageIf(v, op==TK_NOTNULL);
 
116355 break;
116356 }
116357 case TK_BETWEEN: {
116358 testcase( jumpIfNull==0 );
116359 exprCodeBetween(pParse, pExpr, dest, sqlite3ExprIfTrue, jumpIfNull);
@@ -116497,15 +116520,15 @@
116520 break;
116521 }
116522 case TK_ISNULL:
116523 case TK_NOTNULL: {
116524 r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, &regFree1);
116525 assert( regFree1==0 || regFree1==r1 );
116526 if( regFree1 ) sqlite3VdbeTypeofColumn(v, r1);
116527 sqlite3VdbeAddOp2(v, op, r1, dest);
116528 testcase( op==TK_ISNULL ); VdbeCoverageIf(v, op==TK_ISNULL);
116529 testcase( op==TK_NOTNULL ); VdbeCoverageIf(v, op==TK_NOTNULL);
 
116530 break;
116531 }
116532 case TK_BETWEEN: {
116533 testcase( jumpIfNull==0 );
116534 exprCodeBetween(pParse, pExpr, dest, sqlite3ExprIfFalse, jumpIfNull);
@@ -121683,20 +121706,10 @@
121706 }
121707 #endif
121708 while( z[0]!=0 && z[0]!=' ' ) z++;
121709 while( z[0]==' ' ) z++;
121710 }
 
 
 
 
 
 
 
 
 
 
121711 }
121712 }
121713
121714 /*
121715 ** This callback is invoked once for each index when reading the
@@ -124091,11 +124104,11 @@
124104 */
124105 SQLITE_PRIVATE int sqlite3TableColumnToIndex(Index *pIdx, int iCol){
124106 int i;
124107 i16 iCol16;
124108 assert( iCol>=(-1) && iCol<=SQLITE_MAX_COLUMN );
124109 assert( pIdx->nColumn<=SQLITE_MAX_COLUMN+1 );
124110 iCol16 = iCol;
124111 for(i=0; i<pIdx->nColumn; i++){
124112 if( iCol16==pIdx->aiColumn[i] ){
124113 return i;
124114 }
@@ -167620,15 +167633,12 @@
167633 opMask = WO_LT|WO_LE;
167634 }else{
167635 assert( pNew->u.btree.nBtm==0 );
167636 opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS;
167637 }
167638 if( pProbe->bUnordered ){
167639 opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
 
 
 
167640 }
167641
167642 assert( pNew->u.btree.nEq<pProbe->nColumn );
167643 assert( pNew->u.btree.nEq<pProbe->nKeyCol
167644 || pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY );
@@ -168561,11 +168571,11 @@
168571 }
168572 }else if( m==0
168573 && (HasRowid(pTab) || pWInfo->pSelect!=0 || sqlite3FaultSim(700))
168574 ){
168575 WHERETRACE(0x200,
168576 ("-> %s is a covering index according to bitmasks\n",
168577 pProbe->zName, m==0 ? "is" : "is not"));
168578 pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED;
168579 }
168580 }
168581
@@ -207968,60 +207978,113 @@
207978 ** Growing our own isspace() routine this way is twice as fast as
207979 ** the library isspace() function, resulting in a 7% overall performance
207980 ** increase for the text-JSON parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os).
207981 */
207982 static const char jsonIsSpace[] = {
207983 #ifdef SQLITE_ASCII
207984 /*0 1 2 3 4 5 6 7 8 9 a b c d e f */
207985 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, /* 0 */
207986 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1 */
207987 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
207988 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 3 */
207989 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 4 */
207990 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 5 */
207991 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 6 */
207992 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 7 */
207993
207994 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 8 */
207995 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 9 */
207996 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* a */
207997 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* b */
207998 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* c */
207999 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */
208000 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* e */
208001 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* f */
208002 #endif
208003 #ifdef SQLITE_EBCDIC
208004 /*0 1 2 3 4 5 6 7 8 9 a b c d e f */
208005 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, /* 0 */
208006 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1 */
208007 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
208008 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 3 */
208009 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 4 */
208010 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 5 */
208011 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 6 */
208012 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 7 */
208013
208014 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 8 */
208015 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 9 */
208016 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* a */
208017 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* b */
208018 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* c */
208019 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* d */
208020 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* e */
208021 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* f */
208022 #endif
208023
208024 };
208025 #define jsonIsspace(x) (jsonIsSpace[(unsigned char)x])
208026
208027 /*
208028 ** The set of all space characters recognized by jsonIsspace().
208029 ** Useful as the second argument to strspn().
208030 */
208031 #ifdef SQLITE_ASCII
208032 static const char jsonSpaces[] = "\011\012\015\040";
208033 #endif
208034 #ifdef SQLITE_EBCDIC
208035 static const char jsonSpaces[] = "\005\045\015\100";
208036 #endif
208037
208038
208039 /*
208040 ** Characters that are special to JSON. Control characters,
208041 ** '"' and '\\' and '\''. Actually, '\'' is not special to
208042 ** canonical JSON, but it is special in JSON-5, so we include
208043 ** it in the set of special characters.
208044 */
208045 static const char jsonIsOk[256] = {
208046 #ifdef SQLITE_ASCII
208047 /*0 1 2 3 4 5 6 7 8 9 a b c d e f */
208048 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */
208049 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1 */
208050 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, /* 2 */
208051 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 3 */
208052 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4 */
208053 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, /* 5 */
208054 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */
208055 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 7 */
208056
208057 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 8 */
208058 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 9 */
208059 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* a */
208060 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* b */
208061 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* c */
208062 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* d */
208063 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */
208064 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* f */
208065 #endif
208066 #ifdef SQLITE_EBCDIC
208067 /*0 1 2 3 4 5 6 7 8 9 a b c d e f */
208068 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0 */
208069 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1 */
208070 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2 */
208071 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, /* 3 */
208072 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4 */
208073 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 5 */
208074 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6 */
208075 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, /* 7 */
208076
208077 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 8 */
208078 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 9 */
208079 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* a */
208080 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* b */
208081 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* c */
208082 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* d */
208083 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* e */
208084 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 /* f */
208085 #endif
208086 };
208087
208088 /* Objects */
208089 typedef struct JsonCache JsonCache;
208090 typedef struct JsonString JsonString;
@@ -208162,11 +208225,11 @@
208225
208226 /**************************************************************************
208227 ** Forward references
208228 **************************************************************************/
208229 static void jsonReturnStringAsBlob(JsonString*);
208230 static int jsonArgIsJsonb(sqlite3_value *pJson, JsonParse *p);
208231 static u32 jsonTranslateBlobToText(const JsonParse*,u32,JsonString*);
208232 static void jsonReturnParse(sqlite3_context*,JsonParse*);
208233 static JsonParse *jsonParseFuncArg(sqlite3_context*,sqlite3_value*,u32);
208234 static void jsonParseFree(JsonParse*);
208235 static u32 jsonbPayloadSize(const JsonParse*, u32, u32*);
@@ -208580,15 +208643,13 @@
208643 jsonAppendString(p, z, n);
208644 }
208645 break;
208646 }
208647 default: {
208648 JsonParse px;
208649 memset(&px, 0, sizeof(px));
208650 if( jsonArgIsJsonb(pValue, &px) ){
 
 
208651 jsonTranslateBlobToText(&px, 0, p);
208652 }else if( p->eErr==0 ){
208653 sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1);
208654 p->eErr = JSTRING_ERR;
208655 jsonStringReset(p);
@@ -209522,11 +209583,16 @@
209583 c = z[++j];
209584 if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f'
209585 || c=='n' || c=='r' || c=='t'
209586 || (c=='u' && jsonIs4Hex(&z[j+1])) ){
209587 if( opcode==JSONB_TEXT ) opcode = JSONB_TEXTJ;
209588 }else if( c=='\'' || c=='v' || c=='\n'
209589 #ifdef SQLITE_BUG_COMPATIBLE_20250510
209590 || (c=='0') /* Legacy bug compatible */
209591 #else
209592 || (c=='0' && !sqlite3Isdigit(z[j+1])) /* Correct implementation */
209593 #endif
209594 || (0xe2==(u8)c && 0x80==(u8)z[j+1]
209595 && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2]))
209596 || (c=='x' && jsonIs2Hex(&z[j+1])) ){
209597 opcode = JSONB_TEXT5;
209598 pParse->hasNonstd = 1;
@@ -209909,11 +209975,11 @@
209975 || pParse->aBlob[i+4]!=0
209976 ){
209977 *pSz = 0;
209978 return 0;
209979 }
209980 sz = ((u32)pParse->aBlob[i+5]<<24) + (pParse->aBlob[i+6]<<16) +
209981 (pParse->aBlob[i+7]<<8) + pParse->aBlob[i+8];
209982 n = 9;
209983 }
209984 if( (i64)i+sz+n > pParse->nBlob
209985 && (i64)i+sz+n > pParse->nBlob-pParse->delta
@@ -210258,37 +210324,10 @@
210324 }
210325 }
210326 return i;
210327 }
210328
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210329 /*
210330 ** Given that a JSONB_ARRAY object starts at offset i, return
210331 ** the number of entries in that array.
210332 */
210333 static u32 jsonbArrayCount(JsonParse *pParse, u32 iRoot){
@@ -210517,11 +210556,25 @@
210556 case 'f': { *piOut = '\f'; return 2; }
210557 case 'n': { *piOut = '\n'; return 2; }
210558 case 'r': { *piOut = '\r'; return 2; }
210559 case 't': { *piOut = '\t'; return 2; }
210560 case 'v': { *piOut = '\v'; return 2; }
210561 case '0': {
210562 /* JSON5 requires that the \0 escape not be followed by a digit.
210563 ** But SQLite did not enforce this restriction in versions 3.42.0
210564 ** through 3.49.2. That was a bug. But some applications might have
210565 ** come to depend on that bug. Use the SQLITE_BUG_COMPATIBLE_20250510
210566 ** option to restore the old buggy behavior. */
210567 #ifdef SQLITE_BUG_COMPATIBLE_20250510
210568 /* Legacy bug-compatible behavior */
210569 *piOut = 0;
210570 #else
210571 /* Correct behavior */
210572 *piOut = (n>2 && sqlite3Isdigit(z[2])) ? JSON_INVALID_CHAR : 0;
210573 #endif
210574 return 2;
210575 }
210576 case '\'':
210577 case '"':
210578 case '/':
210579 case '\\':{ *piOut = z[1]; return 2; }
210580 case 'x': {
@@ -211112,14 +211165,11 @@
211165 pParse->aBlob = aNull;
211166 pParse->nBlob = 1;
211167 return 0;
211168 }
211169 case SQLITE_BLOB: {
211170 if( !jsonArgIsJsonb(pArg, pParse) ){
 
 
 
211171 sqlite3_result_error(ctx, "JSON cannot hold BLOB values", -1);
211172 return 1;
211173 }
211174 break;
211175 }
@@ -211266,31 +211316,50 @@
211316 }
211317
211318 /*
211319 ** If pArg is a blob that seems like a JSONB blob, then initialize
211320 ** p to point to that JSONB and return TRUE. If pArg does not seem like
211321 ** a JSONB blob, then return FALSE.
211322 **
211323 ** For small BLOBs (having no more than 7 bytes of payload) a full
211324 ** validity check is done. So for small BLOBs this routine only returns
211325 ** true if the value is guaranteed to be a valid JSONB. For larger BLOBs
211326 ** (8 byte or more of payload) only the size of the outermost element is
211327 ** checked to verify that the BLOB is superficially valid JSONB.
211328 **
211329 ** A full JSONB validation is done on smaller BLOBs because those BLOBs might
211330 ** also be text JSON that has been incorrectly cast into a BLOB.
211331 ** (See tag-20240123-a and https://sqlite.org/forum/forumpost/012136abd5)
211332 ** If the BLOB is 9 bytes are larger, then it is not possible for the
211333 ** superficial size check done here to pass if the input is really text
211334 ** JSON so we do not need to look deeper in that case.
211335 **
211336 ** Why we only need to do full JSONB validation for smaller BLOBs:
211337 **
211338 ** The first byte of valid JSON text must be one of: '{', '[', '"', ' ', '\n',
211339 ** '\r', '\t', '-', or a digit '0' through '9'. Of these, only a subset
211340 ** can also be the first byte of JSONB: '{', '[', and digits '3'
211341 ** through '9'. In every one of those cases, the payload size is 7 bytes
211342 ** or less. So if we do full JSONB validation for every BLOB where the
211343 ** payload is less than 7 bytes, we will never get a false positive for
211344 ** JSONB on an input that is really text JSON.
211345 */
211346 static int jsonArgIsJsonb(sqlite3_value *pArg, JsonParse *p){
211347 u32 n, sz = 0;
211348 u8 c;
211349 if( sqlite3_value_type(pArg)!=SQLITE_BLOB ) return 0;
211350 p->aBlob = (u8*)sqlite3_value_blob(pArg);
211351 p->nBlob = (u32)sqlite3_value_bytes(pArg);
211352 if( p->nBlob>0
211353 && ALWAYS(p->aBlob!=0)
211354 && ((c = p->aBlob[0]) & 0x0f)<=JSONB_OBJECT
 
 
 
 
 
211355 && (n = jsonbPayloadSize(p, 0, &sz))>0
211356 && sz+n==p->nBlob
211357 && ((c & 0x0f)>JSONB_FALSE || sz==0)
211358 && (sz>7
211359 || (c!=0x7b && c!=0x5b && !sqlite3Isdigit(c))
211360 || jsonbValidityCheck(p, 0, p->nBlob, 1)==0)
211361 ){
211362 return 1;
211363 }
211364 p->aBlob = 0;
211365 p->nBlob = 0;
@@ -212379,25 +212448,21 @@
212448 sqlite3_result_int(ctx, 0);
212449 #endif
212450 return;
212451 }
212452 case SQLITE_BLOB: {
212453 JsonParse py;
212454 memset(&py, 0, sizeof(py));
212455 if( jsonArgIsJsonb(argv[0], &py) ){
212456 if( flags & 0x04 ){
212457 /* Superficial checking only - accomplished by the
212458 ** jsonArgIsJsonb() call above. */
212459 res = 1;
212460 }else if( flags & 0x08 ){
212461 /* Strict checking. Check by translating BLOB->TEXT->BLOB. If
212462 ** no errors occur, call that a "strict check". */
212463 res = 0==jsonbValidityCheck(&py, 0, py.nBlob, 1);
 
 
 
 
 
 
212464 }
212465 break;
212466 }
212467 /* Fall through into interpreting the input as text. See note
212468 ** above at tag-20240123-a. */
@@ -212451,13 +212516,11 @@
212516
212517 assert( argc==1 );
212518 UNUSED_PARAMETER(argc);
212519 memset(&s, 0, sizeof(s));
212520 s.db = sqlite3_context_db_handle(ctx);
212521 if( jsonArgIsJsonb(argv[0], &s) ){
 
 
212522 iErrPos = (i64)jsonbValidityCheck(&s, 0, s.nBlob, 1);
212523 }else{
212524 s.zJson = (char*)sqlite3_value_text(argv[0]);
212525 if( s.zJson==0 ) return; /* NULL input or OOM */
212526 s.nJson = sqlite3_value_bytes(argv[0]);
@@ -213138,13 +213201,12 @@
213201 jsonEachCursorReset(p);
213202 if( idxNum==0 ) return SQLITE_OK;
213203 memset(&p->sParse, 0, sizeof(p->sParse));
213204 p->sParse.nJPRef = 1;
213205 p->sParse.db = p->db;
213206 if( jsonArgIsJsonb(argv[0], &p->sParse) ){
213207 /* We have JSONB */
 
213208 }else{
213209 p->sParse.zJson = (char*)sqlite3_value_text(argv[0]);
213210 p->sParse.nJson = sqlite3_value_bytes(argv[0]);
213211 if( p->sParse.zJson==0 ){
213212 p->i = p->iEnd = 0;
@@ -257213,11 +257275,11 @@
257275 int nArg, /* Number of args */
257276 sqlite3_value **apUnused /* Function arguments */
257277 ){
257278 assert( nArg==0 );
257279 UNUSED_PARAM2(nArg, apUnused);
257280 sqlite3_result_text(pCtx, "fts5: 2025-05-15 11:20:54 336ceeccc6f85bd78f4a26648af7edf9056d569a767b4120f125a02b2090a349", -1, SQLITE_TRANSIENT);
257281 }
257282
257283 /*
257284 ** Implementation of fts5_locale(LOCALE, TEXT) function.
257285 **
257286
--- extsrc/sqlite3.h
+++ extsrc/sqlite3.h
@@ -146,11 +146,11 @@
146146
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147147
** [sqlite_version()] and [sqlite_source_id()].
148148
*/
149149
#define SQLITE_VERSION "3.50.0"
150150
#define SQLITE_VERSION_NUMBER 3050000
151
-#define SQLITE_SOURCE_ID "2025-04-15 21:59:38 d22475b81c4e26ccc50f3b5626d43b32f7a2de34e5a764539554665bdda735d5"
151
+#define SQLITE_SOURCE_ID "2025-05-15 11:20:54 336ceeccc6f85bd78f4a26648af7edf9056d569a767b4120f125a02b2090a349"
152152
153153
/*
154154
** CAPI3REF: Run-Time Library Version Numbers
155155
** KEYWORDS: sqlite3_version sqlite3_sourceid
156156
**
@@ -11553,13 +11553,14 @@
1155311553
** This may appear to have some counter-intuitive effects if a single row
1155411554
** is written to more than once during a session. For example, if a row
1155511555
** is inserted while a session object is enabled, then later deleted while
1155611556
** the same session object is disabled, no INSERT record will appear in the
1155711557
** changeset, even though the delete took place while the session was disabled.
11558
-** Or, if one field of a row is updated while a session is disabled, and
11559
-** another field of the same row is updated while the session is enabled, the
11560
-** resulting changeset will contain an UPDATE change that updates both fields.
11558
+** Or, if one field of a row is updated while a session is enabled, and
11559
+** then another field of the same row is updated while the session is disabled,
11560
+** the resulting changeset will contain an UPDATE change that updates both
11561
+** fields.
1156111562
*/
1156211563
SQLITE_API int sqlite3session_changeset(
1156311564
sqlite3_session *pSession, /* Session object */
1156411565
int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
1156511566
void **ppChangeset /* OUT: Buffer containing changeset */
@@ -11764,11 +11765,11 @@
1176411765
** CAPI3REF: Flags for sqlite3changeset_start_v2
1176511766
**
1176611767
** The following flags may passed via the 4th parameter to
1176711768
** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
1176811769
**
11769
-** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
11770
+** <dt>SQLITE_CHANGESETSTART_INVERT <dd>
1177011771
** Invert the changeset while iterating through it. This is equivalent to
1177111772
** inverting a changeset using sqlite3changeset_invert() before applying it.
1177211773
** It is an error to specify this flag with a patchset.
1177311774
*/
1177411775
#define SQLITE_CHANGESETSTART_INVERT 0x0002
1177511776
--- extsrc/sqlite3.h
+++ extsrc/sqlite3.h
@@ -146,11 +146,11 @@
146 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147 ** [sqlite_version()] and [sqlite_source_id()].
148 */
149 #define SQLITE_VERSION "3.50.0"
150 #define SQLITE_VERSION_NUMBER 3050000
151 #define SQLITE_SOURCE_ID "2025-04-15 21:59:38 d22475b81c4e26ccc50f3b5626d43b32f7a2de34e5a764539554665bdda735d5"
152
153 /*
154 ** CAPI3REF: Run-Time Library Version Numbers
155 ** KEYWORDS: sqlite3_version sqlite3_sourceid
156 **
@@ -11553,13 +11553,14 @@
11553 ** This may appear to have some counter-intuitive effects if a single row
11554 ** is written to more than once during a session. For example, if a row
11555 ** is inserted while a session object is enabled, then later deleted while
11556 ** the same session object is disabled, no INSERT record will appear in the
11557 ** changeset, even though the delete took place while the session was disabled.
11558 ** Or, if one field of a row is updated while a session is disabled, and
11559 ** another field of the same row is updated while the session is enabled, the
11560 ** resulting changeset will contain an UPDATE change that updates both fields.
 
11561 */
11562 SQLITE_API int sqlite3session_changeset(
11563 sqlite3_session *pSession, /* Session object */
11564 int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
11565 void **ppChangeset /* OUT: Buffer containing changeset */
@@ -11764,11 +11765,11 @@
11764 ** CAPI3REF: Flags for sqlite3changeset_start_v2
11765 **
11766 ** The following flags may passed via the 4th parameter to
11767 ** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
11768 **
11769 ** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
11770 ** Invert the changeset while iterating through it. This is equivalent to
11771 ** inverting a changeset using sqlite3changeset_invert() before applying it.
11772 ** It is an error to specify this flag with a patchset.
11773 */
11774 #define SQLITE_CHANGESETSTART_INVERT 0x0002
11775
--- extsrc/sqlite3.h
+++ extsrc/sqlite3.h
@@ -146,11 +146,11 @@
146 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147 ** [sqlite_version()] and [sqlite_source_id()].
148 */
149 #define SQLITE_VERSION "3.50.0"
150 #define SQLITE_VERSION_NUMBER 3050000
151 #define SQLITE_SOURCE_ID "2025-05-15 11:20:54 336ceeccc6f85bd78f4a26648af7edf9056d569a767b4120f125a02b2090a349"
152
153 /*
154 ** CAPI3REF: Run-Time Library Version Numbers
155 ** KEYWORDS: sqlite3_version sqlite3_sourceid
156 **
@@ -11553,13 +11553,14 @@
11553 ** This may appear to have some counter-intuitive effects if a single row
11554 ** is written to more than once during a session. For example, if a row
11555 ** is inserted while a session object is enabled, then later deleted while
11556 ** the same session object is disabled, no INSERT record will appear in the
11557 ** changeset, even though the delete took place while the session was disabled.
11558 ** Or, if one field of a row is updated while a session is enabled, and
11559 ** then another field of the same row is updated while the session is disabled,
11560 ** the resulting changeset will contain an UPDATE change that updates both
11561 ** fields.
11562 */
11563 SQLITE_API int sqlite3session_changeset(
11564 sqlite3_session *pSession, /* Session object */
11565 int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */
11566 void **ppChangeset /* OUT: Buffer containing changeset */
@@ -11764,11 +11765,11 @@
11765 ** CAPI3REF: Flags for sqlite3changeset_start_v2
11766 **
11767 ** The following flags may passed via the 4th parameter to
11768 ** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
11769 **
11770 ** <dt>SQLITE_CHANGESETSTART_INVERT <dd>
11771 ** Invert the changeset while iterating through it. This is equivalent to
11772 ** inverting a changeset using sqlite3changeset_invert() before applying it.
11773 ** It is an error to specify this flag with a patchset.
11774 */
11775 #define SQLITE_CHANGESETSTART_INVERT 0x0002
11776
+31 -17
--- src/alerts.c
+++ src/alerts.c
@@ -984,13 +984,10 @@
984984
blob_appendf(pOut, "Sender: <%s>\r\n", p->zFrom);
985985
}else{
986986
blob_appendf(pOut, "From: <%s>\r\n", p->zFrom);
987987
}
988988
blob_appendf(pOut, "Date: %z\r\n", cgi_rfc822_datestamp(time(0)));
989
- if( p->zListId && p->zListId[0] ){
990
- blob_appendf(pOut, "List-Id: %s\r\n", p->zListId);
991
- }
992989
if( strstr(blob_str(pHdr), "\r\nMessage-Id:")==0 ){
993990
/* Message-id format: "<$(date)x$(random)@$(from-host)>" where $(date) is
994991
** the current unix-time in hex, $(random) is a 64-bit random number,
995992
** and $(from) is the domain part of the email-self setting. */
996993
sqlite3_randomness(sizeof(r1), &r1);
@@ -3215,18 +3212,21 @@
32153212
Blob fhdr, fbody;
32163213
blob_init(&fhdr, 0, 0);
32173214
blob_appendf(&fhdr, "To: <%s>\r\n", zEmail);
32183215
blob_append(&fhdr, blob_buffer(&p->hdr), blob_size(&p->hdr));
32193216
blob_init(&fbody, blob_buffer(&p->txt), blob_size(&p->txt));
3220
- blob_appendf(&fhdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n",
3221
- zUrl, zCode);
3222
- blob_appendf(&fhdr,
3223
- "List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
3224
- blob_appendf(&fbody, "\n-- \nUnsubscribe: %s/unsubscribe/%s\n",
3225
- zUrl, zCode);
3226
- /* blob_appendf(&fbody, "Subscription settings: %s/alerts/%s\n",
3227
- ** zUrl, zCode); */
3217
+ if( pSender->zListId && pSender->zListId[0] ){
3218
+ blob_appendf(&fhdr, "List-Id: %s\r\n", pSender->zListId);
3219
+ blob_appendf(&fhdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n",
3220
+ zUrl, zCode);
3221
+ blob_appendf(&fhdr,
3222
+ "List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
3223
+ blob_appendf(&fbody, "\n-- \nUnsubscribe: %s/unsubscribe/%s\n",
3224
+ zUrl, zCode);
3225
+ /* blob_appendf(&fbody, "Subscription settings: %s/alerts/%s\n",
3226
+ ** zUrl, zCode); */
3227
+ }
32283228
alert_send(pSender,&fhdr,&fbody,p->zFromName);
32293229
nSent++;
32303230
blob_reset(&fhdr);
32313231
blob_reset(&fbody);
32323232
}else{
@@ -3245,15 +3245,19 @@
32453245
blob_append(&body, "\n", 1);
32463246
blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt));
32473247
}
32483248
}
32493249
if( nHit==0 ) continue;
3250
- blob_appendf(&hdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n",
3251
- zUrl, zCode);
3252
- blob_appendf(&hdr, "List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
3253
- blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n",
3254
- zUrl, zCode);
3250
+ if( pSender->zListId && pSender->zListId[0] ){
3251
+ blob_appendf(&hdr, "List-Id: %s\r\n", pSender->zListId);
3252
+ blob_appendf(&hdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n",
3253
+ zUrl, zCode);
3254
+ blob_appendf(&hdr,
3255
+ "List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
3256
+ blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n",
3257
+ zUrl, zCode);
3258
+ }
32553259
alert_send(pSender,&hdr,&body,0);
32563260
nSent++;
32573261
blob_truncate(&hdr, 0);
32583262
blob_truncate(&body, 0);
32593263
}
@@ -3295,18 +3299,28 @@
32953299
" AND length(sdigest)>0",
32963300
iNewWarn, iOldWarn
32973301
);
32983302
while( db_step(&q)==SQLITE_ROW ){
32993303
Blob hdr, body;
3304
+ const char *zCode = db_column_text(&q,0);
33003305
blob_init(&hdr, 0, 0);
33013306
blob_init(&body, 0, 0);
33023307
alert_renewal_msg(&hdr, &body,
3303
- db_column_text(&q,0),
3308
+ zCode,
33043309
db_column_int(&q,1),
33053310
db_column_text(&q,2),
33063311
db_column_text(&q,3),
33073312
zRepoName, zUrl);
3313
+ if( pSender->zListId && pSender->zListId[0] ){
3314
+ blob_appendf(&hdr, "List-Id: %s\r\n", pSender->zListId);
3315
+ blob_appendf(&hdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n",
3316
+ zUrl, zCode);
3317
+ blob_appendf(&hdr,
3318
+ "List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
3319
+ blob_appendf(&body, "\n-- \nUnsubscribe: %s/unsubscribe/%s\n",
3320
+ zUrl, zCode);
3321
+ }
33083322
alert_send(pSender,&hdr,&body,0);
33093323
blob_reset(&hdr);
33103324
blob_reset(&body);
33113325
}
33123326
db_finalize(&q);
33133327
--- src/alerts.c
+++ src/alerts.c
@@ -984,13 +984,10 @@
984 blob_appendf(pOut, "Sender: <%s>\r\n", p->zFrom);
985 }else{
986 blob_appendf(pOut, "From: <%s>\r\n", p->zFrom);
987 }
988 blob_appendf(pOut, "Date: %z\r\n", cgi_rfc822_datestamp(time(0)));
989 if( p->zListId && p->zListId[0] ){
990 blob_appendf(pOut, "List-Id: %s\r\n", p->zListId);
991 }
992 if( strstr(blob_str(pHdr), "\r\nMessage-Id:")==0 ){
993 /* Message-id format: "<$(date)x$(random)@$(from-host)>" where $(date) is
994 ** the current unix-time in hex, $(random) is a 64-bit random number,
995 ** and $(from) is the domain part of the email-self setting. */
996 sqlite3_randomness(sizeof(r1), &r1);
@@ -3215,18 +3212,21 @@
3215 Blob fhdr, fbody;
3216 blob_init(&fhdr, 0, 0);
3217 blob_appendf(&fhdr, "To: <%s>\r\n", zEmail);
3218 blob_append(&fhdr, blob_buffer(&p->hdr), blob_size(&p->hdr));
3219 blob_init(&fbody, blob_buffer(&p->txt), blob_size(&p->txt));
3220 blob_appendf(&fhdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n",
3221 zUrl, zCode);
3222 blob_appendf(&fhdr,
3223 "List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
3224 blob_appendf(&fbody, "\n-- \nUnsubscribe: %s/unsubscribe/%s\n",
3225 zUrl, zCode);
3226 /* blob_appendf(&fbody, "Subscription settings: %s/alerts/%s\n",
3227 ** zUrl, zCode); */
 
 
 
3228 alert_send(pSender,&fhdr,&fbody,p->zFromName);
3229 nSent++;
3230 blob_reset(&fhdr);
3231 blob_reset(&fbody);
3232 }else{
@@ -3245,15 +3245,19 @@
3245 blob_append(&body, "\n", 1);
3246 blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt));
3247 }
3248 }
3249 if( nHit==0 ) continue;
3250 blob_appendf(&hdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n",
3251 zUrl, zCode);
3252 blob_appendf(&hdr, "List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
3253 blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n",
3254 zUrl, zCode);
 
 
 
 
3255 alert_send(pSender,&hdr,&body,0);
3256 nSent++;
3257 blob_truncate(&hdr, 0);
3258 blob_truncate(&body, 0);
3259 }
@@ -3295,18 +3299,28 @@
3295 " AND length(sdigest)>0",
3296 iNewWarn, iOldWarn
3297 );
3298 while( db_step(&q)==SQLITE_ROW ){
3299 Blob hdr, body;
 
3300 blob_init(&hdr, 0, 0);
3301 blob_init(&body, 0, 0);
3302 alert_renewal_msg(&hdr, &body,
3303 db_column_text(&q,0),
3304 db_column_int(&q,1),
3305 db_column_text(&q,2),
3306 db_column_text(&q,3),
3307 zRepoName, zUrl);
 
 
 
 
 
 
 
 
 
3308 alert_send(pSender,&hdr,&body,0);
3309 blob_reset(&hdr);
3310 blob_reset(&body);
3311 }
3312 db_finalize(&q);
3313
--- src/alerts.c
+++ src/alerts.c
@@ -984,13 +984,10 @@
984 blob_appendf(pOut, "Sender: <%s>\r\n", p->zFrom);
985 }else{
986 blob_appendf(pOut, "From: <%s>\r\n", p->zFrom);
987 }
988 blob_appendf(pOut, "Date: %z\r\n", cgi_rfc822_datestamp(time(0)));
 
 
 
989 if( strstr(blob_str(pHdr), "\r\nMessage-Id:")==0 ){
990 /* Message-id format: "<$(date)x$(random)@$(from-host)>" where $(date) is
991 ** the current unix-time in hex, $(random) is a 64-bit random number,
992 ** and $(from) is the domain part of the email-self setting. */
993 sqlite3_randomness(sizeof(r1), &r1);
@@ -3215,18 +3212,21 @@
3212 Blob fhdr, fbody;
3213 blob_init(&fhdr, 0, 0);
3214 blob_appendf(&fhdr, "To: <%s>\r\n", zEmail);
3215 blob_append(&fhdr, blob_buffer(&p->hdr), blob_size(&p->hdr));
3216 blob_init(&fbody, blob_buffer(&p->txt), blob_size(&p->txt));
3217 if( pSender->zListId && pSender->zListId[0] ){
3218 blob_appendf(&fhdr, "List-Id: %s\r\n", pSender->zListId);
3219 blob_appendf(&fhdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n",
3220 zUrl, zCode);
3221 blob_appendf(&fhdr,
3222 "List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
3223 blob_appendf(&fbody, "\n-- \nUnsubscribe: %s/unsubscribe/%s\n",
3224 zUrl, zCode);
3225 /* blob_appendf(&fbody, "Subscription settings: %s/alerts/%s\n",
3226 ** zUrl, zCode); */
3227 }
3228 alert_send(pSender,&fhdr,&fbody,p->zFromName);
3229 nSent++;
3230 blob_reset(&fhdr);
3231 blob_reset(&fbody);
3232 }else{
@@ -3245,15 +3245,19 @@
3245 blob_append(&body, "\n", 1);
3246 blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt));
3247 }
3248 }
3249 if( nHit==0 ) continue;
3250 if( pSender->zListId && pSender->zListId[0] ){
3251 blob_appendf(&hdr, "List-Id: %s\r\n", pSender->zListId);
3252 blob_appendf(&hdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n",
3253 zUrl, zCode);
3254 blob_appendf(&hdr,
3255 "List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
3256 blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n",
3257 zUrl, zCode);
3258 }
3259 alert_send(pSender,&hdr,&body,0);
3260 nSent++;
3261 blob_truncate(&hdr, 0);
3262 blob_truncate(&body, 0);
3263 }
@@ -3295,18 +3299,28 @@
3299 " AND length(sdigest)>0",
3300 iNewWarn, iOldWarn
3301 );
3302 while( db_step(&q)==SQLITE_ROW ){
3303 Blob hdr, body;
3304 const char *zCode = db_column_text(&q,0);
3305 blob_init(&hdr, 0, 0);
3306 blob_init(&body, 0, 0);
3307 alert_renewal_msg(&hdr, &body,
3308 zCode,
3309 db_column_int(&q,1),
3310 db_column_text(&q,2),
3311 db_column_text(&q,3),
3312 zRepoName, zUrl);
3313 if( pSender->zListId && pSender->zListId[0] ){
3314 blob_appendf(&hdr, "List-Id: %s\r\n", pSender->zListId);
3315 blob_appendf(&hdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n",
3316 zUrl, zCode);
3317 blob_appendf(&hdr,
3318 "List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
3319 blob_appendf(&body, "\n-- \nUnsubscribe: %s/unsubscribe/%s\n",
3320 zUrl, zCode);
3321 }
3322 alert_send(pSender,&hdr,&body,0);
3323 blob_reset(&hdr);
3324 blob_reset(&body);
3325 }
3326 db_finalize(&q);
3327
+1 -1
--- src/cgi.c
+++ src/cgi.c
@@ -2673,11 +2673,11 @@
26732673
listen4 = socket(AF_INET, SOCK_STREAM, 0);
26742674
if( listen4>0 ){
26752675
setsockopt(listen4, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
26762676
rc = bind(listen4, (struct sockaddr*)&inaddr4, sizeof(inaddr4));
26772677
if( rc<0 ){
2678
- close(listen6);
2678
+ close(listen4);
26792679
listen4 = -1;
26802680
}
26812681
}
26822682
if( listen4<0 ){
26832683
fossil_fatal("cannot open a listening socket on %s:%d",
26842684
--- src/cgi.c
+++ src/cgi.c
@@ -2673,11 +2673,11 @@
2673 listen4 = socket(AF_INET, SOCK_STREAM, 0);
2674 if( listen4>0 ){
2675 setsockopt(listen4, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
2676 rc = bind(listen4, (struct sockaddr*)&inaddr4, sizeof(inaddr4));
2677 if( rc<0 ){
2678 close(listen6);
2679 listen4 = -1;
2680 }
2681 }
2682 if( listen4<0 ){
2683 fossil_fatal("cannot open a listening socket on %s:%d",
2684
--- src/cgi.c
+++ src/cgi.c
@@ -2673,11 +2673,11 @@
2673 listen4 = socket(AF_INET, SOCK_STREAM, 0);
2674 if( listen4>0 ){
2675 setsockopt(listen4, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
2676 rc = bind(listen4, (struct sockaddr*)&inaddr4, sizeof(inaddr4));
2677 if( rc<0 ){
2678 close(listen4);
2679 listen4 = -1;
2680 }
2681 }
2682 if( listen4<0 ){
2683 fossil_fatal("cannot open a listening socket on %s:%d",
2684
+1 -1
--- src/cgi.c
+++ src/cgi.c
@@ -2673,11 +2673,11 @@
26732673
listen4 = socket(AF_INET, SOCK_STREAM, 0);
26742674
if( listen4>0 ){
26752675
setsockopt(listen4, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
26762676
rc = bind(listen4, (struct sockaddr*)&inaddr4, sizeof(inaddr4));
26772677
if( rc<0 ){
2678
- close(listen6);
2678
+ close(listen4);
26792679
listen4 = -1;
26802680
}
26812681
}
26822682
if( listen4<0 ){
26832683
fossil_fatal("cannot open a listening socket on %s:%d",
26842684
--- src/cgi.c
+++ src/cgi.c
@@ -2673,11 +2673,11 @@
2673 listen4 = socket(AF_INET, SOCK_STREAM, 0);
2674 if( listen4>0 ){
2675 setsockopt(listen4, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
2676 rc = bind(listen4, (struct sockaddr*)&inaddr4, sizeof(inaddr4));
2677 if( rc<0 ){
2678 close(listen6);
2679 listen4 = -1;
2680 }
2681 }
2682 if( listen4<0 ){
2683 fossil_fatal("cannot open a listening socket on %s:%d",
2684
--- src/cgi.c
+++ src/cgi.c
@@ -2673,11 +2673,11 @@
2673 listen4 = socket(AF_INET, SOCK_STREAM, 0);
2674 if( listen4>0 ){
2675 setsockopt(listen4, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
2676 rc = bind(listen4, (struct sockaddr*)&inaddr4, sizeof(inaddr4));
2677 if( rc<0 ){
2678 close(listen4);
2679 listen4 = -1;
2680 }
2681 }
2682 if( listen4<0 ){
2683 fossil_fatal("cannot open a listening socket on %s:%d",
2684
+4 -2
--- src/clone.c
+++ src/clone.c
@@ -430,12 +430,14 @@
430430
const char *zNm = db_get("short-project-name","download");
431431
char *zUrl = href("%R/zip/%t/%t.zip", zDLTag, zNm);
432432
@ <p>ZIP Archive: %z(zUrl)%h(zNm).zip</a>
433433
zUrl = href("%R/tarball/%t/%t.tar.gz", zDLTag, zNm);
434434
@ <p>Tarball: %z(zUrl)%h(zNm).tar.gz</a>
435
- zUrl = href("%R/sqlar/%t/%t.sqlar", zDLTag, zNm);
436
- @ <p>SQLite Archive: %z(zUrl)%h(zNm).sqlar</a>
435
+ if( g.zLogin!=0 ){
436
+ zUrl = href("%R/sqlar/%t/%t.sqlar", zDLTag, zNm);
437
+ @ <p>SQLite Archive: %z(zUrl)%h(zNm).sqlar</a>
438
+ }
437439
}
438440
if( !g.perm.Clone ){
439441
@ <p>You are not authorized to clone this repository.
440442
if( g.zLogin==0 || g.zLogin[0]==0 ){
441443
@ Maybe you would be able to clone if you
442444
--- src/clone.c
+++ src/clone.c
@@ -430,12 +430,14 @@
430 const char *zNm = db_get("short-project-name","download");
431 char *zUrl = href("%R/zip/%t/%t.zip", zDLTag, zNm);
432 @ <p>ZIP Archive: %z(zUrl)%h(zNm).zip</a>
433 zUrl = href("%R/tarball/%t/%t.tar.gz", zDLTag, zNm);
434 @ <p>Tarball: %z(zUrl)%h(zNm).tar.gz</a>
435 zUrl = href("%R/sqlar/%t/%t.sqlar", zDLTag, zNm);
436 @ <p>SQLite Archive: %z(zUrl)%h(zNm).sqlar</a>
 
 
437 }
438 if( !g.perm.Clone ){
439 @ <p>You are not authorized to clone this repository.
440 if( g.zLogin==0 || g.zLogin[0]==0 ){
441 @ Maybe you would be able to clone if you
442
--- src/clone.c
+++ src/clone.c
@@ -430,12 +430,14 @@
430 const char *zNm = db_get("short-project-name","download");
431 char *zUrl = href("%R/zip/%t/%t.zip", zDLTag, zNm);
432 @ <p>ZIP Archive: %z(zUrl)%h(zNm).zip</a>
433 zUrl = href("%R/tarball/%t/%t.tar.gz", zDLTag, zNm);
434 @ <p>Tarball: %z(zUrl)%h(zNm).tar.gz</a>
435 if( g.zLogin!=0 ){
436 zUrl = href("%R/sqlar/%t/%t.sqlar", zDLTag, zNm);
437 @ <p>SQLite Archive: %z(zUrl)%h(zNm).sqlar</a>
438 }
439 }
440 if( !g.perm.Clone ){
441 @ <p>You are not authorized to clone this repository.
442 if( g.zLogin==0 || g.zLogin[0]==0 ){
443 @ Maybe you would be able to clone if you
444
+2 -2
--- src/db.c
+++ src/db.c
@@ -3369,11 +3369,11 @@
33693369
if( zProjectName ) fossil_print("project-name: %s\n", zProjectName);
33703370
if( zProjectDesc ) fossil_print("project-description: %s\n", zProjectDesc);
33713371
fossil_print("project-id: %s\n", db_get("project-code", 0));
33723372
fossil_print("server-id: %s\n", db_get("server-code", 0));
33733373
zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
3374
- fossil_print("admin-user: %s (initial password is \"%s\")\n",
3374
+ fossil_print("admin-user: %s (initial remote-access password is \"%s\")\n",
33753375
g.zLogin, zPassword);
33763376
hash_user_password(g.zLogin);
33773377
}
33783378
33793379
/*
@@ -4758,11 +4758,11 @@
47584758
** SETTING: comment-format width=16 default=1
47594759
** Set the algorithm for printing timeline comments to the console.
47604760
**
47614761
** Possible values are:
47624762
** 1 Use the original comment printing algorithm:
4763
-** * Leading and trialing whitespace is removed
4763
+** * Leading and trailing whitespace is removed
47644764
** * Internal whitespace is converted into a single space (0x20)
47654765
** * Line breaks occurs at whitespace or hyphens if possible
47664766
** This is the recommended value and the default.
47674767
**
47684768
** Or a bitwise combination of the following flags:
47694769
--- src/db.c
+++ src/db.c
@@ -3369,11 +3369,11 @@
3369 if( zProjectName ) fossil_print("project-name: %s\n", zProjectName);
3370 if( zProjectDesc ) fossil_print("project-description: %s\n", zProjectDesc);
3371 fossil_print("project-id: %s\n", db_get("project-code", 0));
3372 fossil_print("server-id: %s\n", db_get("server-code", 0));
3373 zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
3374 fossil_print("admin-user: %s (initial password is \"%s\")\n",
3375 g.zLogin, zPassword);
3376 hash_user_password(g.zLogin);
3377 }
3378
3379 /*
@@ -4758,11 +4758,11 @@
4758 ** SETTING: comment-format width=16 default=1
4759 ** Set the algorithm for printing timeline comments to the console.
4760 **
4761 ** Possible values are:
4762 ** 1 Use the original comment printing algorithm:
4763 ** * Leading and trialing whitespace is removed
4764 ** * Internal whitespace is converted into a single space (0x20)
4765 ** * Line breaks occurs at whitespace or hyphens if possible
4766 ** This is the recommended value and the default.
4767 **
4768 ** Or a bitwise combination of the following flags:
4769
--- src/db.c
+++ src/db.c
@@ -3369,11 +3369,11 @@
3369 if( zProjectName ) fossil_print("project-name: %s\n", zProjectName);
3370 if( zProjectDesc ) fossil_print("project-description: %s\n", zProjectDesc);
3371 fossil_print("project-id: %s\n", db_get("project-code", 0));
3372 fossil_print("server-id: %s\n", db_get("server-code", 0));
3373 zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
3374 fossil_print("admin-user: %s (initial remote-access password is \"%s\")\n",
3375 g.zLogin, zPassword);
3376 hash_user_password(g.zLogin);
3377 }
3378
3379 /*
@@ -4758,11 +4758,11 @@
4758 ** SETTING: comment-format width=16 default=1
4759 ** Set the algorithm for printing timeline comments to the console.
4760 **
4761 ** Possible values are:
4762 ** 1 Use the original comment printing algorithm:
4763 ** * Leading and trailing whitespace is removed
4764 ** * Internal whitespace is converted into a single space (0x20)
4765 ** * Line breaks occurs at whitespace or hyphens if possible
4766 ** This is the recommended value and the default.
4767 **
4768 ** Or a bitwise combination of the following flags:
4769
+7 -1
--- src/diff.tcl
+++ src/diff.tcl
@@ -500,10 +500,12 @@
500500
if {$fn==""} return
501501
set out [open $fn wb]
502502
puts $out "#!/usr/bin/tclsh\n#\n# Run this script using 'tclsh' or 'wish'"
503503
puts $out "# to see the graphical diff.\n#"
504504
puts $out "set fossilcmd {}"
505
+ puts $out "set darkmode $::darkmode"
506
+ puts $out "set debug $::debug"
505507
puts $out "set prog [list $::prog]"
506508
puts $out "set difftxt \173"
507509
foreach e $::difftxt {puts $out [list $e]}
508510
puts $out "\175"
509511
puts $out "eval \$prog"
@@ -606,11 +608,15 @@
606608
::ttk::button .bb.quit -text {Quit} -command exit
607609
::ttk::button .bb.reload -text {Reload} -command reloadDiff
608610
::ttk::button .bb.invert -text {Invert} -command invertDiff
609611
::ttk::button .bb.save -text {Save As...} -command saveDiff
610612
::ttk::button .bb.search -text {Search} -command searchOnOff
611
-pack .bb.quit .bb.reload .bb.invert -side left
613
+pack .bb.quit -side left
614
+if {$fossilcmd ne ""} {
615
+ pack .bb.reload -side left
616
+}
617
+pack .bb.invert -side left
612618
if {$fossilcmd!=""} {pack .bb.save -side left}
613619
pack .bb.files .bb.search -side left
614620
grid rowconfigure . 1 -weight 1
615621
grid columnconfigure . 1 -weight 1
616622
grid columnconfigure . 4 -weight 1
617623
--- src/diff.tcl
+++ src/diff.tcl
@@ -500,10 +500,12 @@
500 if {$fn==""} return
501 set out [open $fn wb]
502 puts $out "#!/usr/bin/tclsh\n#\n# Run this script using 'tclsh' or 'wish'"
503 puts $out "# to see the graphical diff.\n#"
504 puts $out "set fossilcmd {}"
 
 
505 puts $out "set prog [list $::prog]"
506 puts $out "set difftxt \173"
507 foreach e $::difftxt {puts $out [list $e]}
508 puts $out "\175"
509 puts $out "eval \$prog"
@@ -606,11 +608,15 @@
606 ::ttk::button .bb.quit -text {Quit} -command exit
607 ::ttk::button .bb.reload -text {Reload} -command reloadDiff
608 ::ttk::button .bb.invert -text {Invert} -command invertDiff
609 ::ttk::button .bb.save -text {Save As...} -command saveDiff
610 ::ttk::button .bb.search -text {Search} -command searchOnOff
611 pack .bb.quit .bb.reload .bb.invert -side left
 
 
 
 
612 if {$fossilcmd!=""} {pack .bb.save -side left}
613 pack .bb.files .bb.search -side left
614 grid rowconfigure . 1 -weight 1
615 grid columnconfigure . 1 -weight 1
616 grid columnconfigure . 4 -weight 1
617
--- src/diff.tcl
+++ src/diff.tcl
@@ -500,10 +500,12 @@
500 if {$fn==""} return
501 set out [open $fn wb]
502 puts $out "#!/usr/bin/tclsh\n#\n# Run this script using 'tclsh' or 'wish'"
503 puts $out "# to see the graphical diff.\n#"
504 puts $out "set fossilcmd {}"
505 puts $out "set darkmode $::darkmode"
506 puts $out "set debug $::debug"
507 puts $out "set prog [list $::prog]"
508 puts $out "set difftxt \173"
509 foreach e $::difftxt {puts $out [list $e]}
510 puts $out "\175"
511 puts $out "eval \$prog"
@@ -606,11 +608,15 @@
608 ::ttk::button .bb.quit -text {Quit} -command exit
609 ::ttk::button .bb.reload -text {Reload} -command reloadDiff
610 ::ttk::button .bb.invert -text {Invert} -command invertDiff
611 ::ttk::button .bb.save -text {Save As...} -command saveDiff
612 ::ttk::button .bb.search -text {Search} -command searchOnOff
613 pack .bb.quit -side left
614 if {$fossilcmd ne ""} {
615 pack .bb.reload -side left
616 }
617 pack .bb.invert -side left
618 if {$fossilcmd!=""} {pack .bb.save -side left}
619 pack .bb.files .bb.search -side left
620 grid rowconfigure . 1 -weight 1
621 grid columnconfigure . 1 -weight 1
622 grid columnconfigure . 4 -weight 1
623
+2 -1
--- src/http.c
+++ src/http.c
@@ -52,11 +52,12 @@
5252
** Construct the "login" card with the client credentials.
5353
**
5454
** login LOGIN NONCE SIGNATURE
5555
**
5656
** The LOGIN is the user id of the client. NONCE is the sha1 checksum
57
-** of all payload that follows the login card. SIGNATURE is the sha1
57
+** of all payload that follows the login card. Randomness for the NONCE
58
+** must be provided in the payload (in xfer.c). SIGNATURE is the sha1
5859
** checksum of the nonce followed by the user password.
5960
**
6061
** Write the constructed login card into pLogin. pLogin is initialized
6162
** by this routine.
6263
*/
6364
--- src/http.c
+++ src/http.c
@@ -52,11 +52,12 @@
52 ** Construct the "login" card with the client credentials.
53 **
54 ** login LOGIN NONCE SIGNATURE
55 **
56 ** The LOGIN is the user id of the client. NONCE is the sha1 checksum
57 ** of all payload that follows the login card. SIGNATURE is the sha1
 
58 ** checksum of the nonce followed by the user password.
59 **
60 ** Write the constructed login card into pLogin. pLogin is initialized
61 ** by this routine.
62 */
63
--- src/http.c
+++ src/http.c
@@ -52,11 +52,12 @@
52 ** Construct the "login" card with the client credentials.
53 **
54 ** login LOGIN NONCE SIGNATURE
55 **
56 ** The LOGIN is the user id of the client. NONCE is the sha1 checksum
57 ** of all payload that follows the login card. Randomness for the NONCE
58 ** must be provided in the payload (in xfer.c). SIGNATURE is the sha1
59 ** checksum of the nonce followed by the user password.
60 **
61 ** Write the constructed login card into pLogin. pLogin is initialized
62 ** by this routine.
63 */
64
+6 -2
--- src/info.c
+++ src/info.c
@@ -993,12 +993,14 @@
993993
}
994994
zUrl = mprintf("%R/tarball/%S/%t-%S.tar.gz", zUuid, zPJ, zUuid);
995995
@ <tr><th>Downloads:</th><td>
996996
@ %z(href("%s",zUrl))Tarball</a>
997997
@ | %z(href("%R/zip/%S/%t-%S.zip",zUuid, zPJ,zUuid))ZIP archive</a>
998
- @ | %z(href("%R/sqlar/%S/%t-%S.sqlar",zUuid,zPJ,zUuid))\
999
- @ SQL archive</a></td></tr>
998
+ if( g.zLogin!=0 ){
999
+ @ | %z(href("%R/sqlar/%S/%t-%S.sqlar",zUuid,zPJ,zUuid))\
1000
+ @ SQL archive</a></td></tr>
1001
+ }
10001002
fossil_free(zUrl);
10011003
blob_reset(&projName);
10021004
}
10031005
10041006
@ <tr><th>Timelines:</th><td>
@@ -3930,10 +3932,11 @@
39303932
** --cancel TAG Cancel TAG from this check-in
39313933
** --close Mark this "leaf" as closed
39323934
** --date DATETIME Make DATETIME the check-in time
39333935
** --date-override DATETIME Set the change time on the control artifact
39343936
** -e|--edit-comment Launch editor to revise comment
3937
+** --editor NAME Text editor to use for check-in comment
39353938
** --hide Hide branch starting from this check-in
39363939
** -m|--comment COMMENT Make COMMENT the check-in comment
39373940
** -M|--message-file FILE Read the amended comment from FILE
39383941
** -n|--dry-run Print control artifact, but make no changes
39393942
** --no-verify-comment Do not validate the check-in comment
@@ -4003,10 +4006,11 @@
40034006
if( zChngTime==0 ) zChngTime = find_option("chngtime",0,1);
40044007
zUserOvrd = find_option("user-override",0,1);
40054008
noVerifyCom = find_option("no-verify-comment",0,0)!=0;
40064009
db_find_and_open_repository(0,0);
40074010
user_select();
4011
+ (void)fossil_text_editor();
40084012
verify_all_options();
40094013
if( g.argc<3 || g.argc>=4 ) usage(AMEND_USAGE_STMT);
40104014
rid = name_to_typed_rid(g.argv[2], "ci");
40114015
if( rid==0 && !is_a_version(rid) ) fossil_fatal("no such check-in");
40124016
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
40134017
--- src/info.c
+++ src/info.c
@@ -993,12 +993,14 @@
993 }
994 zUrl = mprintf("%R/tarball/%S/%t-%S.tar.gz", zUuid, zPJ, zUuid);
995 @ <tr><th>Downloads:</th><td>
996 @ %z(href("%s",zUrl))Tarball</a>
997 @ | %z(href("%R/zip/%S/%t-%S.zip",zUuid, zPJ,zUuid))ZIP archive</a>
998 @ | %z(href("%R/sqlar/%S/%t-%S.sqlar",zUuid,zPJ,zUuid))\
999 @ SQL archive</a></td></tr>
 
 
1000 fossil_free(zUrl);
1001 blob_reset(&projName);
1002 }
1003
1004 @ <tr><th>Timelines:</th><td>
@@ -3930,10 +3932,11 @@
3930 ** --cancel TAG Cancel TAG from this check-in
3931 ** --close Mark this "leaf" as closed
3932 ** --date DATETIME Make DATETIME the check-in time
3933 ** --date-override DATETIME Set the change time on the control artifact
3934 ** -e|--edit-comment Launch editor to revise comment
 
3935 ** --hide Hide branch starting from this check-in
3936 ** -m|--comment COMMENT Make COMMENT the check-in comment
3937 ** -M|--message-file FILE Read the amended comment from FILE
3938 ** -n|--dry-run Print control artifact, but make no changes
3939 ** --no-verify-comment Do not validate the check-in comment
@@ -4003,10 +4006,11 @@
4003 if( zChngTime==0 ) zChngTime = find_option("chngtime",0,1);
4004 zUserOvrd = find_option("user-override",0,1);
4005 noVerifyCom = find_option("no-verify-comment",0,0)!=0;
4006 db_find_and_open_repository(0,0);
4007 user_select();
 
4008 verify_all_options();
4009 if( g.argc<3 || g.argc>=4 ) usage(AMEND_USAGE_STMT);
4010 rid = name_to_typed_rid(g.argv[2], "ci");
4011 if( rid==0 && !is_a_version(rid) ) fossil_fatal("no such check-in");
4012 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
4013
--- src/info.c
+++ src/info.c
@@ -993,12 +993,14 @@
993 }
994 zUrl = mprintf("%R/tarball/%S/%t-%S.tar.gz", zUuid, zPJ, zUuid);
995 @ <tr><th>Downloads:</th><td>
996 @ %z(href("%s",zUrl))Tarball</a>
997 @ | %z(href("%R/zip/%S/%t-%S.zip",zUuid, zPJ,zUuid))ZIP archive</a>
998 if( g.zLogin!=0 ){
999 @ | %z(href("%R/sqlar/%S/%t-%S.sqlar",zUuid,zPJ,zUuid))\
1000 @ SQL archive</a></td></tr>
1001 }
1002 fossil_free(zUrl);
1003 blob_reset(&projName);
1004 }
1005
1006 @ <tr><th>Timelines:</th><td>
@@ -3930,10 +3932,11 @@
3932 ** --cancel TAG Cancel TAG from this check-in
3933 ** --close Mark this "leaf" as closed
3934 ** --date DATETIME Make DATETIME the check-in time
3935 ** --date-override DATETIME Set the change time on the control artifact
3936 ** -e|--edit-comment Launch editor to revise comment
3937 ** --editor NAME Text editor to use for check-in comment
3938 ** --hide Hide branch starting from this check-in
3939 ** -m|--comment COMMENT Make COMMENT the check-in comment
3940 ** -M|--message-file FILE Read the amended comment from FILE
3941 ** -n|--dry-run Print control artifact, but make no changes
3942 ** --no-verify-comment Do not validate the check-in comment
@@ -4003,10 +4006,11 @@
4006 if( zChngTime==0 ) zChngTime = find_option("chngtime",0,1);
4007 zUserOvrd = find_option("user-override",0,1);
4008 noVerifyCom = find_option("no-verify-comment",0,0)!=0;
4009 db_find_and_open_repository(0,0);
4010 user_select();
4011 (void)fossil_text_editor();
4012 verify_all_options();
4013 if( g.argc<3 || g.argc>=4 ) usage(AMEND_USAGE_STMT);
4014 rid = name_to_typed_rid(g.argv[2], "ci");
4015 if( rid==0 && !is_a_version(rid) ) fossil_fatal("no such check-in");
4016 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
4017
+15 -7
--- src/main.mk
+++ src/main.mk
@@ -578,39 +578,41 @@
578578
$(OBJDIR)/winfile.o \
579579
$(OBJDIR)/winhttp.o \
580580
$(OBJDIR)/xfer.o \
581581
$(OBJDIR)/xfersetup.o \
582582
$(OBJDIR)/zip.o
583
-all: $(OBJDIR) $(APPNAME)
583
+all: $(APPNAME)
584584
585585
install: all
586586
mkdir -p $(INSTALLDIR)
587587
cp $(APPNAME) $(INSTALLDIR)
588588
589589
codecheck: $(TRANS_SRC) $(OBJDIR)/codecheck1
590590
$(OBJDIR)/codecheck1 $(TRANS_SRC)
591591
592
-$(OBJDIR):
593
- -mkdir $(OBJDIR)
594
-
595592
$(OBJDIR)/translate: $(SRCDIR_tools)/translate.c
596593
-mkdir -p $(OBJDIR)
597594
$(XBCC) -o $(OBJDIR)/translate $(SRCDIR_tools)/translate.c
598595
599596
$(OBJDIR)/makeheaders: $(SRCDIR_tools)/makeheaders.c
597
+ -mkdir -p $(OBJDIR)
600598
$(XBCC) -o $(OBJDIR)/makeheaders $(SRCDIR_tools)/makeheaders.c
601599
602600
$(OBJDIR)/mkindex: $(SRCDIR_tools)/mkindex.c
601
+ -mkdir -p $(OBJDIR)
603602
$(XBCC) -o $(OBJDIR)/mkindex $(SRCDIR_tools)/mkindex.c
604603
605604
$(OBJDIR)/mkbuiltin: $(SRCDIR_tools)/mkbuiltin.c
605
+ -mkdir -p $(OBJDIR)
606606
$(XBCC) -o $(OBJDIR)/mkbuiltin $(SRCDIR_tools)/mkbuiltin.c
607607
608608
$(OBJDIR)/mkversion: $(SRCDIR_tools)/mkversion.c
609
+ -mkdir -p $(OBJDIR)
609610
$(XBCC) -o $(OBJDIR)/mkversion $(SRCDIR_tools)/mkversion.c
610611
611612
$(OBJDIR)/codecheck1: $(SRCDIR_tools)/codecheck1.c
613
+ -mkdir -p $(OBJDIR)
612614
$(XBCC) -o $(OBJDIR)/codecheck1 $(SRCDIR_tools)/codecheck1.c
613615
614616
# Run the test suite.
615617
# Other flags that can be included in TESTFLAGS are:
616618
#
@@ -622,11 +624,11 @@
622624
# -strict Treat known bugs as failures
623625
#
624626
# TESTFLAGS can also include names of specific test files to limit
625627
# the run to just those test cases.
626628
#
627
-test: $(OBJDIR) $(APPNAME)
629
+test: $(APPNAME)
628630
$(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(TESTFLAGS)
629631
630632
$(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion $(OBJDIR)/phony.h
631633
$(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid \
632634
$(SRCDIR)/../manifest \
@@ -2119,23 +2121,29 @@
21192121
21202122
$(OBJDIR)/linenoise.o: $(SRCDIR_extsrc)/linenoise.c $(SRCDIR_extsrc)/linenoise.h
21212123
$(XTCC) -c $(SRCDIR_extsrc)/linenoise.c -o $@
21222124
21232125
$(OBJDIR)/th.o: $(SRCDIR)/th.c
2126
+ -mkdir -p $(OBJDIR)
2127
+
21242128
$(XTCC) -c $(SRCDIR)/th.c -o $@
21252129
21262130
$(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c
2131
+ -mkdir -p $(OBJDIR)
2132
+
21272133
$(XTCC) -c $(SRCDIR)/th_lang.c -o $@
21282134
21292135
$(OBJDIR)/th_tcl.o: $(SRCDIR)/th_tcl.c
2136
+ -mkdir -p $(OBJDIR)
2137
+
21302138
$(XTCC) -c $(SRCDIR)/th_tcl.c -o $@
21312139
21322140
2133
-$(OBJDIR)/pikchr.o: $(SRCDIR_extsrc)/pikchr.c
2141
+$(OBJDIR)/pikchr.o: $(SRCDIR_extsrc)/pikchr.c $(OBJDIR)/mkversion
21342142
$(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@
21352143
2136
-$(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c
2144
+$(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c $(OBJDIR)/mkversion
21372145
$(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@
21382146
21392147
$(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c $(MAKEFILE_LIST)
21402148
$(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry \
21412149
-sEXPORTED_RUNTIME_METHODS=cwrap,ccall,setValue,getValue,stackSave,stackAlloc,stackRestore \
21422150
--- src/main.mk
+++ src/main.mk
@@ -578,39 +578,41 @@
578 $(OBJDIR)/winfile.o \
579 $(OBJDIR)/winhttp.o \
580 $(OBJDIR)/xfer.o \
581 $(OBJDIR)/xfersetup.o \
582 $(OBJDIR)/zip.o
583 all: $(OBJDIR) $(APPNAME)
584
585 install: all
586 mkdir -p $(INSTALLDIR)
587 cp $(APPNAME) $(INSTALLDIR)
588
589 codecheck: $(TRANS_SRC) $(OBJDIR)/codecheck1
590 $(OBJDIR)/codecheck1 $(TRANS_SRC)
591
592 $(OBJDIR):
593 -mkdir $(OBJDIR)
594
595 $(OBJDIR)/translate: $(SRCDIR_tools)/translate.c
596 -mkdir -p $(OBJDIR)
597 $(XBCC) -o $(OBJDIR)/translate $(SRCDIR_tools)/translate.c
598
599 $(OBJDIR)/makeheaders: $(SRCDIR_tools)/makeheaders.c
 
600 $(XBCC) -o $(OBJDIR)/makeheaders $(SRCDIR_tools)/makeheaders.c
601
602 $(OBJDIR)/mkindex: $(SRCDIR_tools)/mkindex.c
 
603 $(XBCC) -o $(OBJDIR)/mkindex $(SRCDIR_tools)/mkindex.c
604
605 $(OBJDIR)/mkbuiltin: $(SRCDIR_tools)/mkbuiltin.c
 
606 $(XBCC) -o $(OBJDIR)/mkbuiltin $(SRCDIR_tools)/mkbuiltin.c
607
608 $(OBJDIR)/mkversion: $(SRCDIR_tools)/mkversion.c
 
609 $(XBCC) -o $(OBJDIR)/mkversion $(SRCDIR_tools)/mkversion.c
610
611 $(OBJDIR)/codecheck1: $(SRCDIR_tools)/codecheck1.c
 
612 $(XBCC) -o $(OBJDIR)/codecheck1 $(SRCDIR_tools)/codecheck1.c
613
614 # Run the test suite.
615 # Other flags that can be included in TESTFLAGS are:
616 #
@@ -622,11 +624,11 @@
622 # -strict Treat known bugs as failures
623 #
624 # TESTFLAGS can also include names of specific test files to limit
625 # the run to just those test cases.
626 #
627 test: $(OBJDIR) $(APPNAME)
628 $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(TESTFLAGS)
629
630 $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion $(OBJDIR)/phony.h
631 $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid \
632 $(SRCDIR)/../manifest \
@@ -2119,23 +2121,29 @@
2119
2120 $(OBJDIR)/linenoise.o: $(SRCDIR_extsrc)/linenoise.c $(SRCDIR_extsrc)/linenoise.h
2121 $(XTCC) -c $(SRCDIR_extsrc)/linenoise.c -o $@
2122
2123 $(OBJDIR)/th.o: $(SRCDIR)/th.c
 
 
2124 $(XTCC) -c $(SRCDIR)/th.c -o $@
2125
2126 $(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c
 
 
2127 $(XTCC) -c $(SRCDIR)/th_lang.c -o $@
2128
2129 $(OBJDIR)/th_tcl.o: $(SRCDIR)/th_tcl.c
 
 
2130 $(XTCC) -c $(SRCDIR)/th_tcl.c -o $@
2131
2132
2133 $(OBJDIR)/pikchr.o: $(SRCDIR_extsrc)/pikchr.c
2134 $(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@
2135
2136 $(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c
2137 $(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@
2138
2139 $(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c $(MAKEFILE_LIST)
2140 $(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry \
2141 -sEXPORTED_RUNTIME_METHODS=cwrap,ccall,setValue,getValue,stackSave,stackAlloc,stackRestore \
2142
--- src/main.mk
+++ src/main.mk
@@ -578,39 +578,41 @@
578 $(OBJDIR)/winfile.o \
579 $(OBJDIR)/winhttp.o \
580 $(OBJDIR)/xfer.o \
581 $(OBJDIR)/xfersetup.o \
582 $(OBJDIR)/zip.o
583 all: $(APPNAME)
584
585 install: all
586 mkdir -p $(INSTALLDIR)
587 cp $(APPNAME) $(INSTALLDIR)
588
589 codecheck: $(TRANS_SRC) $(OBJDIR)/codecheck1
590 $(OBJDIR)/codecheck1 $(TRANS_SRC)
591
 
 
 
592 $(OBJDIR)/translate: $(SRCDIR_tools)/translate.c
593 -mkdir -p $(OBJDIR)
594 $(XBCC) -o $(OBJDIR)/translate $(SRCDIR_tools)/translate.c
595
596 $(OBJDIR)/makeheaders: $(SRCDIR_tools)/makeheaders.c
597 -mkdir -p $(OBJDIR)
598 $(XBCC) -o $(OBJDIR)/makeheaders $(SRCDIR_tools)/makeheaders.c
599
600 $(OBJDIR)/mkindex: $(SRCDIR_tools)/mkindex.c
601 -mkdir -p $(OBJDIR)
602 $(XBCC) -o $(OBJDIR)/mkindex $(SRCDIR_tools)/mkindex.c
603
604 $(OBJDIR)/mkbuiltin: $(SRCDIR_tools)/mkbuiltin.c
605 -mkdir -p $(OBJDIR)
606 $(XBCC) -o $(OBJDIR)/mkbuiltin $(SRCDIR_tools)/mkbuiltin.c
607
608 $(OBJDIR)/mkversion: $(SRCDIR_tools)/mkversion.c
609 -mkdir -p $(OBJDIR)
610 $(XBCC) -o $(OBJDIR)/mkversion $(SRCDIR_tools)/mkversion.c
611
612 $(OBJDIR)/codecheck1: $(SRCDIR_tools)/codecheck1.c
613 -mkdir -p $(OBJDIR)
614 $(XBCC) -o $(OBJDIR)/codecheck1 $(SRCDIR_tools)/codecheck1.c
615
616 # Run the test suite.
617 # Other flags that can be included in TESTFLAGS are:
618 #
@@ -622,11 +624,11 @@
624 # -strict Treat known bugs as failures
625 #
626 # TESTFLAGS can also include names of specific test files to limit
627 # the run to just those test cases.
628 #
629 test: $(APPNAME)
630 $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(TESTFLAGS)
631
632 $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion $(OBJDIR)/phony.h
633 $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid \
634 $(SRCDIR)/../manifest \
@@ -2119,23 +2121,29 @@
2121
2122 $(OBJDIR)/linenoise.o: $(SRCDIR_extsrc)/linenoise.c $(SRCDIR_extsrc)/linenoise.h
2123 $(XTCC) -c $(SRCDIR_extsrc)/linenoise.c -o $@
2124
2125 $(OBJDIR)/th.o: $(SRCDIR)/th.c
2126 -mkdir -p $(OBJDIR)
2127
2128 $(XTCC) -c $(SRCDIR)/th.c -o $@
2129
2130 $(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c
2131 -mkdir -p $(OBJDIR)
2132
2133 $(XTCC) -c $(SRCDIR)/th_lang.c -o $@
2134
2135 $(OBJDIR)/th_tcl.o: $(SRCDIR)/th_tcl.c
2136 -mkdir -p $(OBJDIR)
2137
2138 $(XTCC) -c $(SRCDIR)/th_tcl.c -o $@
2139
2140
2141 $(OBJDIR)/pikchr.o: $(SRCDIR_extsrc)/pikchr.c $(OBJDIR)/mkversion
2142 $(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@
2143
2144 $(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c $(OBJDIR)/mkversion
2145 $(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@
2146
2147 $(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c $(MAKEFILE_LIST)
2148 $(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry \
2149 -sEXPORTED_RUNTIME_METHODS=cwrap,ccall,setValue,getValue,stackSave,stackAlloc,stackRestore \
2150
+2 -2
--- src/manifest.c
+++ src/manifest.c
@@ -3024,11 +3024,11 @@
30243024
CARD_STR2(K, p->zTicketUuid);
30253025
CARD_STR2(L, p->zWikiTitle);
30263026
ISA( CFTYPE_CLUSTER ){
30273027
CARD_LETTER(M);
30283028
blob_append_char(b, '[');
3029
- for( int i = 0; i < p->nCChild; ++i ){
3029
+ for( i = 0; i < p->nCChild; ++i ){
30303030
if( i>0 ) blob_append_char(b, ',');
30313031
blob_appendf(b, "%!j", p->azCChild[i]);
30323032
}
30333033
blob_append_char(b, ']');
30343034
}
@@ -3059,11 +3059,11 @@
30593059
}
30603060
CARD_STR2(R, p->zRepoCksum);
30613061
if( p->nTag ){
30623062
CARD_LETTER(T);
30633063
blob_append_char(b, '[');
3064
- for( int i = 0; i < p->nTag; ++i ){
3064
+ for( i = 0; i < p->nTag; ++i ){
30653065
const char *zName = p->aTag[i].zName;
30663066
if( i>0 ) blob_append_char(b, ',');
30673067
blob_append_char(b, '{');
30683068
blob_appendf(b, "\"type\":\"%c\"", *zName);
30693069
KVP_STR(1, name, &zName[1]);
30703070
--- src/manifest.c
+++ src/manifest.c
@@ -3024,11 +3024,11 @@
3024 CARD_STR2(K, p->zTicketUuid);
3025 CARD_STR2(L, p->zWikiTitle);
3026 ISA( CFTYPE_CLUSTER ){
3027 CARD_LETTER(M);
3028 blob_append_char(b, '[');
3029 for( int i = 0; i < p->nCChild; ++i ){
3030 if( i>0 ) blob_append_char(b, ',');
3031 blob_appendf(b, "%!j", p->azCChild[i]);
3032 }
3033 blob_append_char(b, ']');
3034 }
@@ -3059,11 +3059,11 @@
3059 }
3060 CARD_STR2(R, p->zRepoCksum);
3061 if( p->nTag ){
3062 CARD_LETTER(T);
3063 blob_append_char(b, '[');
3064 for( int i = 0; i < p->nTag; ++i ){
3065 const char *zName = p->aTag[i].zName;
3066 if( i>0 ) blob_append_char(b, ',');
3067 blob_append_char(b, '{');
3068 blob_appendf(b, "\"type\":\"%c\"", *zName);
3069 KVP_STR(1, name, &zName[1]);
3070
--- src/manifest.c
+++ src/manifest.c
@@ -3024,11 +3024,11 @@
3024 CARD_STR2(K, p->zTicketUuid);
3025 CARD_STR2(L, p->zWikiTitle);
3026 ISA( CFTYPE_CLUSTER ){
3027 CARD_LETTER(M);
3028 blob_append_char(b, '[');
3029 for( i = 0; i < p->nCChild; ++i ){
3030 if( i>0 ) blob_append_char(b, ',');
3031 blob_appendf(b, "%!j", p->azCChild[i]);
3032 }
3033 blob_append_char(b, ']');
3034 }
@@ -3059,11 +3059,11 @@
3059 }
3060 CARD_STR2(R, p->zRepoCksum);
3061 if( p->nTag ){
3062 CARD_LETTER(T);
3063 blob_append_char(b, '[');
3064 for( i = 0; i < p->nTag; ++i ){
3065 const char *zName = p->aTag[i].zName;
3066 if( i>0 ) blob_append_char(b, ',');
3067 blob_append_char(b, '{');
3068 blob_appendf(b, "\"type\":\"%c\"", *zName);
3069 KVP_STR(1, name, &zName[1]);
3070
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -278,11 +278,11 @@
278278
struct Blob *head_row,
279279
struct Blob *rows,
280280
void *opaque
281281
){
282282
INTER_BLOCK(ob);
283
- blob_append_literal(ob, "<table>\n");
283
+ blob_append_literal(ob, "<table class='md-table'>\n");
284284
if( head_row && blob_size(head_row)>0 ){
285285
blob_append_literal(ob, "<thead>\n");
286286
blob_appendb(ob, head_row);
287287
blob_append_literal(ob, "</thead>\n<tbody>\n");
288288
}
@@ -696,11 +696,11 @@
696696
){
697697
blob_appendf(&bSrc, "fontscale = %.13g\n", rPikVar);
698698
}
699699
blob_append(&bSrc, zSrc, nSrc)
700700
/*have to dup input to ensure a NUL-terminated source string */;
701
- pikchr_process(blob_str(&bSrc), pikFlags, 0, ob);
701
+ pikchr_process(blob_str(&bSrc), pikFlags, ob);
702702
blob_reset(&bSrc);
703703
}
704704
705705
/* Invoked for `...` blocks where there are nSep grave accents in a
706706
** row that serve as the delimiter. According to CommonMark:
707707
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -278,11 +278,11 @@
278 struct Blob *head_row,
279 struct Blob *rows,
280 void *opaque
281 ){
282 INTER_BLOCK(ob);
283 blob_append_literal(ob, "<table>\n");
284 if( head_row && blob_size(head_row)>0 ){
285 blob_append_literal(ob, "<thead>\n");
286 blob_appendb(ob, head_row);
287 blob_append_literal(ob, "</thead>\n<tbody>\n");
288 }
@@ -696,11 +696,11 @@
696 ){
697 blob_appendf(&bSrc, "fontscale = %.13g\n", rPikVar);
698 }
699 blob_append(&bSrc, zSrc, nSrc)
700 /*have to dup input to ensure a NUL-terminated source string */;
701 pikchr_process(blob_str(&bSrc), pikFlags, 0, ob);
702 blob_reset(&bSrc);
703 }
704
705 /* Invoked for `...` blocks where there are nSep grave accents in a
706 ** row that serve as the delimiter. According to CommonMark:
707
--- src/markdown_html.c
+++ src/markdown_html.c
@@ -278,11 +278,11 @@
278 struct Blob *head_row,
279 struct Blob *rows,
280 void *opaque
281 ){
282 INTER_BLOCK(ob);
283 blob_append_literal(ob, "<table class='md-table'>\n");
284 if( head_row && blob_size(head_row)>0 ){
285 blob_append_literal(ob, "<thead>\n");
286 blob_appendb(ob, head_row);
287 blob_append_literal(ob, "</thead>\n<tbody>\n");
288 }
@@ -696,11 +696,11 @@
696 ){
697 blob_appendf(&bSrc, "fontscale = %.13g\n", rPikVar);
698 }
699 blob_append(&bSrc, zSrc, nSrc)
700 /*have to dup input to ensure a NUL-terminated source string */;
701 pikchr_process(blob_str(&bSrc), pikFlags, ob);
702 blob_reset(&bSrc);
703 }
704
705 /* Invoked for `...` blocks where there are nSep grave accents in a
706 ** row that serve as the delimiter. According to CommonMark:
707
+89 -182
--- src/pikchrshow.c
+++ src/pikchrshow.c
@@ -27,12 +27,10 @@
2727
/* The first two must match the values from pikchr.c */
2828
#define PIKCHR_PROCESS_PLAINTEXT_ERRORS 0x0001
2929
#define PIKCHR_PROCESS_DARK_MODE 0x0002
3030
/* end of flags supported directly by pikchr() */
3131
#define PIKCHR_PROCESS_PASSTHROUGH 0x0003 /* Pass through these flags */
32
-#define PIKCHR_PROCESS_TH1 0x0004
33
-#define PIKCHR_PROCESS_TH1_NOSVG 0x0008
3432
#define PIKCHR_PROCESS_NONCE 0x0010
3533
#define PIKCHR_PROCESS_ERR_PRE 0x0020
3634
#define PIKCHR_PROCESS_SRC 0x0040
3735
#define PIKCHR_PROCESS_DIV 0x0080
3836
#define PIKCHR_PROCESS_DIV_INDENT 0x0100
@@ -43,36 +41,20 @@
4341
#define PIKCHR_PROCESS_DIV_SOURCE 0x2000
4442
#define PIKCHR_PROCESS_DIV_SOURCE_INLINE 0x4000
4543
#endif
4644
4745
/*
48
-** Processes a pikchr script, optionally with embedded TH1, and
49
-** produces HTML code for it. zIn is the NUL-terminated input
46
+** Processes a pikchr script. zIn is the NUL-terminated input
5047
** script. pikFlags may be a bitmask of any of the PIKCHR_PROCESS_xxx
51
-** flags documented below. thFlags may be a bitmask of any of the
52
-** TH_INIT_xxx and/or TH_R2B_xxx flags. Output is sent to pOut,
53
-** appending to it without modifying any prior contents.
48
+** flags documented below. Output is sent to pOut,
5449
**
55
-** Returns 0 on success, 1 if TH1 processing failed, or 2 if pikchr
56
-** processing failed. In either case, the error message (if any) from
57
-** TH1 or pikchr will be appended to pOut.
50
+** Returns 0 on success, or non-zero if pikchr processing failed.
51
+** In either case, the error message (if any) from pikchr will be
52
+** appended to pOut.
5853
**
5954
** pikFlags flag descriptions:
6055
**
61
-** - PIKCHR_PROCESS_TH1 means to run zIn through TH1, using the TH1
62
-** init flags specified in the 3rd argument. If thFlags is non-0 then
63
-** this flag is assumed even if it is not specified.
64
-**
65
-** - PIKCHR_PROCESS_TH1_NOSVG means that processing stops after the
66
-** TH1 eval step, thus the output will be (presumably) a
67
-** TH1-generated/processed pikchr script (or whatever else the TH1
68
-** outputs). If this flag is set, PIKCHR_PROCESS_TH1 is assumed even
69
-** if it is not specified.
70
-**
71
-** All of the remaining flags listed below are ignored if
72
-** PIKCHR_PROCESS_TH1_NOSVG is specified!
73
-**
7456
** - PIKCHR_PROCESS_DIV: if set, the SVG result is wrapped in a DIV
7557
** element which specifies a max-width style value based on the SVG's
7658
** calculated size. This flag has multiple mutually exclusive forms:
7759
**
7860
** - PIKCHR_PROCESS_DIV uses default element alignment.
@@ -116,14 +98,14 @@
11698
**
11799
** - PIKCHR_PROCESS_ERR_PRE: if set and pikchr() fails, the resulting
118100
** error report is wrapped in a PRE element, else it is retained
119101
** as-is (intended only for console output).
120102
*/
121
-int pikchr_process(const char * zIn, int pikFlags, int thFlags,
122
- Blob * pOut){
123
- Blob bIn = empty_blob;
103
+int pikchr_process(const char *zIn, int pikFlags, Blob * pOut){
124104
int isErr = 0;
105
+ int w = 0, h = 0;
106
+ char *zOut;
125107
const char *zNonce = (PIKCHR_PROCESS_NONCE & pikFlags)
126108
? safe_html_nonce(1) : 0;
127109
128110
if(!(PIKCHR_PROCESS_DIV & pikFlags)
129111
/* If any DIV_xxx flags are set, set DIV */
@@ -135,115 +117,87 @@
135117
| PIKCHR_PROCESS_DIV_SOURCE_INLINE
136118
| PIKCHR_PROCESS_DIV_TOGGLE
137119
) & pikFlags){
138120
pikFlags |= PIKCHR_PROCESS_DIV;
139121
}
140
- if(!(PIKCHR_PROCESS_TH1 & pikFlags)
141
- /* If any TH1_xxx flags are set, set TH1 */
142
- && (PIKCHR_PROCESS_TH1_NOSVG & pikFlags || thFlags!=0)){
143
- pikFlags |= PIKCHR_PROCESS_TH1;
144
- }
145
- if(zNonce){
146
- blob_appendf(pOut, "%s\n", zNonce);
147
- }
148
- if(PIKCHR_PROCESS_TH1 & pikFlags){
149
- Blob out = empty_blob;
150
- isErr = Th_RenderToBlob(zIn, &out, thFlags)
151
- ? 1 : 0;
152
- if(isErr){
153
- blob_append(pOut, blob_str(&out), blob_size(&out));
154
- blob_reset(&out);
155
- }else{
156
- bIn = out;
157
- }
158
- }else{
159
- blob_init(&bIn, zIn, -1);
160
- }
161
- if(!isErr){
162
- if(PIKCHR_PROCESS_TH1_NOSVG & pikFlags){
163
- blob_append(pOut, blob_str(&bIn), blob_size(&bIn));
164
- }else{
165
- int w = 0, h = 0;
166
- const char * zContent = blob_str(&bIn);
167
- char *zOut;
168
- zOut = pikchr(zContent, "pikchr",
169
- 0x01 | (pikFlags&PIKCHR_PROCESS_PASSTHROUGH),
170
- &w, &h);
171
- if( w>0 && h>0 ){
172
- const char * zClassToggle = "";
173
- const char * zClassSource = "";
174
- const char * zWrapperClass = "";
175
- if(PIKCHR_PROCESS_DIV & pikFlags){
176
- if(PIKCHR_PROCESS_DIV_CENTER & pikFlags){
177
- zWrapperClass = " center";
178
- }else if(PIKCHR_PROCESS_DIV_INDENT & pikFlags){
179
- zWrapperClass = " indent";
180
- }else if(PIKCHR_PROCESS_DIV_FLOAT_LEFT & pikFlags){
181
- zWrapperClass = " float-left";
182
- }else if(PIKCHR_PROCESS_DIV_FLOAT_RIGHT & pikFlags){
183
- zWrapperClass = " float-right";
184
- }
185
- if(PIKCHR_PROCESS_DIV_TOGGLE & pikFlags){
186
- zClassToggle = " toggle";
187
- }
188
- if(PIKCHR_PROCESS_DIV_SOURCE_INLINE & pikFlags){
189
- if(PIKCHR_PROCESS_DIV_SOURCE & pikFlags){
190
- zClassSource = " source source-inline";
191
- }else{
192
- zClassSource = " source-inline";
193
- }
194
- pikFlags |= PIKCHR_PROCESS_SRC;
195
- }else if(PIKCHR_PROCESS_DIV_SOURCE & pikFlags){
196
- zClassSource = " source";
197
- pikFlags |= PIKCHR_PROCESS_SRC;
198
- }
199
- blob_appendf(pOut,"<div class='pikchr-wrapper"
200
- "%s%s%s'>"
201
- "<div class=\"pikchr-svg\" "
202
- "style=\"max-width:%dpx\">\n",
203
- zWrapperClass/*safe-for-%s*/,
204
- zClassToggle/*safe-for-%s*/,
205
- zClassSource/*safe-for-%s*/, w);
206
- }
207
- blob_append(pOut, zOut, -1);
208
- if(PIKCHR_PROCESS_DIV & pikFlags){
209
- blob_append(pOut, "</div>\n", 7);
210
- }
211
- if(PIKCHR_PROCESS_SRC & pikFlags){
212
- static int counter = 0;
213
- ++counter;
214
- blob_appendf(pOut, "<div class='pikchr-src'>"
215
- "<pre id='pikchr-src-%d'>%h</pre>"
216
- "<span class='hidden'>"
217
- "<a href='%R/pikchrshow?fromSession' "
218
- "class='pikchr-src-pikchrshow' target='_new-%d' "
219
- "data-pikchrid='pikchr-src-%d' "
220
- "title='Open this pikchr in /pikchrshow'"
221
- ">&rarr; /pikchrshow</a></span>"
222
- "</div>\n",
223
- counter, blob_str(&bIn), counter, counter);
224
- }
225
- if(PIKCHR_PROCESS_DIV & pikFlags){
226
- blob_append(pOut, "</div>\n", 7);
227
- }
228
- }else{
229
- isErr = 2;
230
- if(PIKCHR_PROCESS_ERR_PRE & pikFlags){
231
- blob_append(pOut, "<pre class='error'>\n", 20);
232
- }
233
- blob_appendf(pOut, "%h", zOut);
234
- if(PIKCHR_PROCESS_ERR_PRE & pikFlags){
235
- blob_append(pOut, "\n</pre>\n", 8);
236
- }
237
- }
238
- fossil_free(zOut);
239
- }
240
- }
241
- if(zNonce){
242
- blob_appendf(pOut, "%s\n", zNonce);
243
- }
244
- blob_reset(&bIn);
122
+ if(zNonce){
123
+ blob_appendf(pOut, "%s\n", zNonce);
124
+ }
125
+ zOut = pikchr(zIn, "pikchr",
126
+ 0x01 | (pikFlags&PIKCHR_PROCESS_PASSTHROUGH),
127
+ &w, &h);
128
+ if( w>0 && h>0 ){
129
+ const char * zClassToggle = "";
130
+ const char * zClassSource = "";
131
+ const char * zWrapperClass = "";
132
+ if(PIKCHR_PROCESS_DIV & pikFlags){
133
+ if(PIKCHR_PROCESS_DIV_CENTER & pikFlags){
134
+ zWrapperClass = " center";
135
+ }else if(PIKCHR_PROCESS_DIV_INDENT & pikFlags){
136
+ zWrapperClass = " indent";
137
+ }else if(PIKCHR_PROCESS_DIV_FLOAT_LEFT & pikFlags){
138
+ zWrapperClass = " float-left";
139
+ }else if(PIKCHR_PROCESS_DIV_FLOAT_RIGHT & pikFlags){
140
+ zWrapperClass = " float-right";
141
+ }
142
+ if(PIKCHR_PROCESS_DIV_TOGGLE & pikFlags){
143
+ zClassToggle = " toggle";
144
+ }
145
+ if(PIKCHR_PROCESS_DIV_SOURCE_INLINE & pikFlags){
146
+ if(PIKCHR_PROCESS_DIV_SOURCE & pikFlags){
147
+ zClassSource = " source source-inline";
148
+ }else{
149
+ zClassSource = " source-inline";
150
+ }
151
+ pikFlags |= PIKCHR_PROCESS_SRC;
152
+ }else if(PIKCHR_PROCESS_DIV_SOURCE & pikFlags){
153
+ zClassSource = " source";
154
+ pikFlags |= PIKCHR_PROCESS_SRC;
155
+ }
156
+ blob_appendf(pOut,"<div class='pikchr-wrapper"
157
+ "%s%s%s'>"
158
+ "<div class=\"pikchr-svg\" "
159
+ "style=\"max-width:%dpx\">\n",
160
+ zWrapperClass/*safe-for-%s*/,
161
+ zClassToggle/*safe-for-%s*/,
162
+ zClassSource/*safe-for-%s*/, w);
163
+ }
164
+ blob_append(pOut, zOut, -1);
165
+ if(PIKCHR_PROCESS_DIV & pikFlags){
166
+ blob_append(pOut, "</div>\n", 7);
167
+ }
168
+ if(PIKCHR_PROCESS_SRC & pikFlags){
169
+ static int counter = 0;
170
+ ++counter;
171
+ blob_appendf(pOut, "<div class='pikchr-src'>"
172
+ "<pre id='pikchr-src-%d'>%h</pre>"
173
+ "<span class='hidden'>"
174
+ "<a href='%R/pikchrshow?fromSession' "
175
+ "class='pikchr-src-pikchrshow' target='_new-%d' "
176
+ "data-pikchrid='pikchr-src-%d' "
177
+ "title='Open this pikchr in /pikchrshow'"
178
+ ">&rarr; /pikchrshow</a></span>"
179
+ "</div>\n",
180
+ counter, zIn, counter, counter);
181
+ }
182
+ if(PIKCHR_PROCESS_DIV & pikFlags){
183
+ blob_append(pOut, "</div>\n", 7);
184
+ }
185
+ }else{
186
+ isErr = 2;
187
+ if(PIKCHR_PROCESS_ERR_PRE & pikFlags){
188
+ blob_append(pOut, "<pre class='error'>\n", 20);
189
+ }
190
+ blob_appendf(pOut, "%h", zOut);
191
+ if(PIKCHR_PROCESS_ERR_PRE & pikFlags){
192
+ blob_append(pOut, "\n</pre>\n", 8);
193
+ }
194
+ }
195
+ fossil_free(zOut);
196
+ if(zNonce){
197
+ blob_appendf(pOut, "%s\n", zNonce);
198
+ }
245199
return isErr;
246200
}
247201
248202
/*
249203
** Legacy impl of /pikchrshow. pikchrshow_page() will delegate to
@@ -279,11 +233,11 @@
279233
TODO: respond with JSON instead.*/
280234
cgi_set_content_type("text/html");
281235
if(zContent && *zContent){
282236
Blob out = empty_blob;
283237
const int isErr =
284
- pikchr_process(zContent, pikFlags, 0, &out);
238
+ pikchr_process(zContent, pikFlags, &out);
285239
if(isErr){
286240
cgi_printf_header("x-pikchrshow-is-error: %d\r\n", isErr);
287241
}
288242
CX("%b", &out);
289243
blob_reset(&out);
@@ -384,11 +338,11 @@
384338
/* Reminder: Firefox does not properly flexbox a LEGEND
385339
element, always flowing it in column mode. */);
386340
CX("<div id='pikchrshow-output'>");
387341
if(*zContent){
388342
Blob out = empty_blob;
389
- pikchr_process(zContent, pikFlags, 0, &out);
343
+ pikchr_process(zContent, pikFlags, &out);
390344
CX("%b", &out);
391345
blob_reset(&out);
392346
} CX("</div>"/*#pikchrshow-output*/);
393347
} CX("</fieldset>"/*#pikchrshow-output-wrapper*/);
394348
} CX("</div>"/*sbs-wrapper*/);
@@ -561,60 +515,23 @@
561515
**
562516
** -src Store the input pikchr's source code in the output as
563517
** a separate element adjacent to the SVG one. Implied
564518
** by -div-source.
565519
**
566
-**
567
-** -th Process the input using TH1 before passing it to pikchr
568
-**
569
-** -th-novar Disable $var and $<var> TH1 processing. Use this if the
570
-** pikchr script uses '$' for its own purposes and that
571
-** causes issues. This only affects parsing of '$' outside
572
-** of TH1 script blocks. Code in such blocks is unaffected.
573
-**
574
-** -th-nosvg When using -th, output the post-TH1'd script
575
-** instead of the pikchr-rendered output
576
-**
577
-** -th-trace Trace TH1 execution (for debugging purposes)
578
-**
579520
** -dark Change pikchr colors to assume a dark-mode theme.
580521
**
581522
**
582523
** The -div-indent/center/left/right flags may not be combined.
583
-**
584
-** TH1-related Notes and Caveats:
585
-**
586
-** If the -th flag is used, this command must open a fossil database
587
-** for certain functionality to work (via a check-out or the -R REPO
588
-** flag). If opening a db fails, execution will continue but any TH1
589
-** commands which require a db will trigger a fatal error.
590
-**
591
-** In Fossil skins, TH1 variables in the form $varName are expanded
592
-** as-is and those in the form $<varName> are htmlized in the
593
-** resulting output. This processor disables the htmlizing step, so $x
594
-** and $<x> are equivalent unless the TH1-processed pikchr script
595
-** invokes the TH1 command [enable_htmlify 1] to enable it. Normally
596
-** that option will interfere with pikchr output, however, e.g. by
597
-** HTML-encoding double-quotes.
598
-**
599
-** Many of the fossil-installed TH1 functions simply do not make any
600
-** sense for pikchr scripts.
601524
*/
602525
void pikchr_cmd(void){
603526
Blob bIn = empty_blob;
604527
Blob bOut = empty_blob;
605528
const char * zInfile = "-";
606529
const char * zOutfile = "-";
607
- const int fTh1 = find_option("th",0,0)!=0;
608
- const int fNosvg = find_option("th-nosvg",0,0)!=0;
609530
int isErr = 0;
610531
int pikFlags = find_option("src",0,0)!=0
611532
? PIKCHR_PROCESS_SRC : 0;
612
- u32 fThFlags = TH_INIT_NO_ENCODE
613
- | (find_option("th-novar",0,0)!=0 ? TH_R2B_NO_VARS : 0);
614
-
615
- Th_InitTraceLog()/*processes -th-trace flag*/;
616533
617534
if(find_option("div",0,0)!=0){
618535
pikFlags |= PIKCHR_PROCESS_DIV;
619536
}else if(find_option("div-indent",0,0)!=0){
620537
pikFlags |= PIKCHR_PROCESS_DIV_INDENT;
@@ -644,24 +561,14 @@
644561
}
645562
if(g.argc>3){
646563
zOutfile = g.argv[3];
647564
}
648565
blob_read_from_file(&bIn, zInfile, ExtFILE);
649
- if(fTh1){
650
- db_find_and_open_repository(OPEN_ANY_SCHEMA | OPEN_OK_NOT_FOUND, 0)
651
- /* ^^^ needed for certain TH1 functions to work */;
652
- pikFlags |= PIKCHR_PROCESS_TH1;
653
- if(fNosvg) pikFlags |= PIKCHR_PROCESS_TH1_NOSVG;
654
- }
655
- isErr = pikchr_process(blob_str(&bIn), pikFlags,
656
- fTh1 ? fThFlags : 0, &bOut);
566
+ isErr = pikchr_process(blob_str(&bIn), pikFlags, &bOut);
657567
if(isErr){
658
- fossil_fatal("%s ERROR:%c%b", 1==isErr ? "TH1" : "pikchr",
659
- 1==isErr ? ' ' : '\n',
660
- &bOut);
568
+ fossil_fatal("pikchr ERROR: %b", &bOut);
661569
}else{
662570
blob_write_to_file(&bOut, zOutfile);
663571
}
664
- Th_PrintTraceLog();
665572
blob_reset(&bIn);
666573
blob_reset(&bOut);
667574
}
668575
--- src/pikchrshow.c
+++ src/pikchrshow.c
@@ -27,12 +27,10 @@
27 /* The first two must match the values from pikchr.c */
28 #define PIKCHR_PROCESS_PLAINTEXT_ERRORS 0x0001
29 #define PIKCHR_PROCESS_DARK_MODE 0x0002
30 /* end of flags supported directly by pikchr() */
31 #define PIKCHR_PROCESS_PASSTHROUGH 0x0003 /* Pass through these flags */
32 #define PIKCHR_PROCESS_TH1 0x0004
33 #define PIKCHR_PROCESS_TH1_NOSVG 0x0008
34 #define PIKCHR_PROCESS_NONCE 0x0010
35 #define PIKCHR_PROCESS_ERR_PRE 0x0020
36 #define PIKCHR_PROCESS_SRC 0x0040
37 #define PIKCHR_PROCESS_DIV 0x0080
38 #define PIKCHR_PROCESS_DIV_INDENT 0x0100
@@ -43,36 +41,20 @@
43 #define PIKCHR_PROCESS_DIV_SOURCE 0x2000
44 #define PIKCHR_PROCESS_DIV_SOURCE_INLINE 0x4000
45 #endif
46
47 /*
48 ** Processes a pikchr script, optionally with embedded TH1, and
49 ** produces HTML code for it. zIn is the NUL-terminated input
50 ** script. pikFlags may be a bitmask of any of the PIKCHR_PROCESS_xxx
51 ** flags documented below. thFlags may be a bitmask of any of the
52 ** TH_INIT_xxx and/or TH_R2B_xxx flags. Output is sent to pOut,
53 ** appending to it without modifying any prior contents.
54 **
55 ** Returns 0 on success, 1 if TH1 processing failed, or 2 if pikchr
56 ** processing failed. In either case, the error message (if any) from
57 ** TH1 or pikchr will be appended to pOut.
58 **
59 ** pikFlags flag descriptions:
60 **
61 ** - PIKCHR_PROCESS_TH1 means to run zIn through TH1, using the TH1
62 ** init flags specified in the 3rd argument. If thFlags is non-0 then
63 ** this flag is assumed even if it is not specified.
64 **
65 ** - PIKCHR_PROCESS_TH1_NOSVG means that processing stops after the
66 ** TH1 eval step, thus the output will be (presumably) a
67 ** TH1-generated/processed pikchr script (or whatever else the TH1
68 ** outputs). If this flag is set, PIKCHR_PROCESS_TH1 is assumed even
69 ** if it is not specified.
70 **
71 ** All of the remaining flags listed below are ignored if
72 ** PIKCHR_PROCESS_TH1_NOSVG is specified!
73 **
74 ** - PIKCHR_PROCESS_DIV: if set, the SVG result is wrapped in a DIV
75 ** element which specifies a max-width style value based on the SVG's
76 ** calculated size. This flag has multiple mutually exclusive forms:
77 **
78 ** - PIKCHR_PROCESS_DIV uses default element alignment.
@@ -116,14 +98,14 @@
116 **
117 ** - PIKCHR_PROCESS_ERR_PRE: if set and pikchr() fails, the resulting
118 ** error report is wrapped in a PRE element, else it is retained
119 ** as-is (intended only for console output).
120 */
121 int pikchr_process(const char * zIn, int pikFlags, int thFlags,
122 Blob * pOut){
123 Blob bIn = empty_blob;
124 int isErr = 0;
 
 
125 const char *zNonce = (PIKCHR_PROCESS_NONCE & pikFlags)
126 ? safe_html_nonce(1) : 0;
127
128 if(!(PIKCHR_PROCESS_DIV & pikFlags)
129 /* If any DIV_xxx flags are set, set DIV */
@@ -135,115 +117,87 @@
135 | PIKCHR_PROCESS_DIV_SOURCE_INLINE
136 | PIKCHR_PROCESS_DIV_TOGGLE
137 ) & pikFlags){
138 pikFlags |= PIKCHR_PROCESS_DIV;
139 }
140 if(!(PIKCHR_PROCESS_TH1 & pikFlags)
141 /* If any TH1_xxx flags are set, set TH1 */
142 && (PIKCHR_PROCESS_TH1_NOSVG & pikFlags || thFlags!=0)){
143 pikFlags |= PIKCHR_PROCESS_TH1;
144 }
145 if(zNonce){
146 blob_appendf(pOut, "%s\n", zNonce);
147 }
148 if(PIKCHR_PROCESS_TH1 & pikFlags){
149 Blob out = empty_blob;
150 isErr = Th_RenderToBlob(zIn, &out, thFlags)
151 ? 1 : 0;
152 if(isErr){
153 blob_append(pOut, blob_str(&out), blob_size(&out));
154 blob_reset(&out);
155 }else{
156 bIn = out;
157 }
158 }else{
159 blob_init(&bIn, zIn, -1);
160 }
161 if(!isErr){
162 if(PIKCHR_PROCESS_TH1_NOSVG & pikFlags){
163 blob_append(pOut, blob_str(&bIn), blob_size(&bIn));
164 }else{
165 int w = 0, h = 0;
166 const char * zContent = blob_str(&bIn);
167 char *zOut;
168 zOut = pikchr(zContent, "pikchr",
169 0x01 | (pikFlags&PIKCHR_PROCESS_PASSTHROUGH),
170 &w, &h);
171 if( w>0 && h>0 ){
172 const char * zClassToggle = "";
173 const char * zClassSource = "";
174 const char * zWrapperClass = "";
175 if(PIKCHR_PROCESS_DIV & pikFlags){
176 if(PIKCHR_PROCESS_DIV_CENTER & pikFlags){
177 zWrapperClass = " center";
178 }else if(PIKCHR_PROCESS_DIV_INDENT & pikFlags){
179 zWrapperClass = " indent";
180 }else if(PIKCHR_PROCESS_DIV_FLOAT_LEFT & pikFlags){
181 zWrapperClass = " float-left";
182 }else if(PIKCHR_PROCESS_DIV_FLOAT_RIGHT & pikFlags){
183 zWrapperClass = " float-right";
184 }
185 if(PIKCHR_PROCESS_DIV_TOGGLE & pikFlags){
186 zClassToggle = " toggle";
187 }
188 if(PIKCHR_PROCESS_DIV_SOURCE_INLINE & pikFlags){
189 if(PIKCHR_PROCESS_DIV_SOURCE & pikFlags){
190 zClassSource = " source source-inline";
191 }else{
192 zClassSource = " source-inline";
193 }
194 pikFlags |= PIKCHR_PROCESS_SRC;
195 }else if(PIKCHR_PROCESS_DIV_SOURCE & pikFlags){
196 zClassSource = " source";
197 pikFlags |= PIKCHR_PROCESS_SRC;
198 }
199 blob_appendf(pOut,"<div class='pikchr-wrapper"
200 "%s%s%s'>"
201 "<div class=\"pikchr-svg\" "
202 "style=\"max-width:%dpx\">\n",
203 zWrapperClass/*safe-for-%s*/,
204 zClassToggle/*safe-for-%s*/,
205 zClassSource/*safe-for-%s*/, w);
206 }
207 blob_append(pOut, zOut, -1);
208 if(PIKCHR_PROCESS_DIV & pikFlags){
209 blob_append(pOut, "</div>\n", 7);
210 }
211 if(PIKCHR_PROCESS_SRC & pikFlags){
212 static int counter = 0;
213 ++counter;
214 blob_appendf(pOut, "<div class='pikchr-src'>"
215 "<pre id='pikchr-src-%d'>%h</pre>"
216 "<span class='hidden'>"
217 "<a href='%R/pikchrshow?fromSession' "
218 "class='pikchr-src-pikchrshow' target='_new-%d' "
219 "data-pikchrid='pikchr-src-%d' "
220 "title='Open this pikchr in /pikchrshow'"
221 ">&rarr; /pikchrshow</a></span>"
222 "</div>\n",
223 counter, blob_str(&bIn), counter, counter);
224 }
225 if(PIKCHR_PROCESS_DIV & pikFlags){
226 blob_append(pOut, "</div>\n", 7);
227 }
228 }else{
229 isErr = 2;
230 if(PIKCHR_PROCESS_ERR_PRE & pikFlags){
231 blob_append(pOut, "<pre class='error'>\n", 20);
232 }
233 blob_appendf(pOut, "%h", zOut);
234 if(PIKCHR_PROCESS_ERR_PRE & pikFlags){
235 blob_append(pOut, "\n</pre>\n", 8);
236 }
237 }
238 fossil_free(zOut);
239 }
240 }
241 if(zNonce){
242 blob_appendf(pOut, "%s\n", zNonce);
243 }
244 blob_reset(&bIn);
245 return isErr;
246 }
247
248 /*
249 ** Legacy impl of /pikchrshow. pikchrshow_page() will delegate to
@@ -279,11 +233,11 @@
279 TODO: respond with JSON instead.*/
280 cgi_set_content_type("text/html");
281 if(zContent && *zContent){
282 Blob out = empty_blob;
283 const int isErr =
284 pikchr_process(zContent, pikFlags, 0, &out);
285 if(isErr){
286 cgi_printf_header("x-pikchrshow-is-error: %d\r\n", isErr);
287 }
288 CX("%b", &out);
289 blob_reset(&out);
@@ -384,11 +338,11 @@
384 /* Reminder: Firefox does not properly flexbox a LEGEND
385 element, always flowing it in column mode. */);
386 CX("<div id='pikchrshow-output'>");
387 if(*zContent){
388 Blob out = empty_blob;
389 pikchr_process(zContent, pikFlags, 0, &out);
390 CX("%b", &out);
391 blob_reset(&out);
392 } CX("</div>"/*#pikchrshow-output*/);
393 } CX("</fieldset>"/*#pikchrshow-output-wrapper*/);
394 } CX("</div>"/*sbs-wrapper*/);
@@ -561,60 +515,23 @@
561 **
562 ** -src Store the input pikchr's source code in the output as
563 ** a separate element adjacent to the SVG one. Implied
564 ** by -div-source.
565 **
566 **
567 ** -th Process the input using TH1 before passing it to pikchr
568 **
569 ** -th-novar Disable $var and $<var> TH1 processing. Use this if the
570 ** pikchr script uses '$' for its own purposes and that
571 ** causes issues. This only affects parsing of '$' outside
572 ** of TH1 script blocks. Code in such blocks is unaffected.
573 **
574 ** -th-nosvg When using -th, output the post-TH1'd script
575 ** instead of the pikchr-rendered output
576 **
577 ** -th-trace Trace TH1 execution (for debugging purposes)
578 **
579 ** -dark Change pikchr colors to assume a dark-mode theme.
580 **
581 **
582 ** The -div-indent/center/left/right flags may not be combined.
583 **
584 ** TH1-related Notes and Caveats:
585 **
586 ** If the -th flag is used, this command must open a fossil database
587 ** for certain functionality to work (via a check-out or the -R REPO
588 ** flag). If opening a db fails, execution will continue but any TH1
589 ** commands which require a db will trigger a fatal error.
590 **
591 ** In Fossil skins, TH1 variables in the form $varName are expanded
592 ** as-is and those in the form $<varName> are htmlized in the
593 ** resulting output. This processor disables the htmlizing step, so $x
594 ** and $<x> are equivalent unless the TH1-processed pikchr script
595 ** invokes the TH1 command [enable_htmlify 1] to enable it. Normally
596 ** that option will interfere with pikchr output, however, e.g. by
597 ** HTML-encoding double-quotes.
598 **
599 ** Many of the fossil-installed TH1 functions simply do not make any
600 ** sense for pikchr scripts.
601 */
602 void pikchr_cmd(void){
603 Blob bIn = empty_blob;
604 Blob bOut = empty_blob;
605 const char * zInfile = "-";
606 const char * zOutfile = "-";
607 const int fTh1 = find_option("th",0,0)!=0;
608 const int fNosvg = find_option("th-nosvg",0,0)!=0;
609 int isErr = 0;
610 int pikFlags = find_option("src",0,0)!=0
611 ? PIKCHR_PROCESS_SRC : 0;
612 u32 fThFlags = TH_INIT_NO_ENCODE
613 | (find_option("th-novar",0,0)!=0 ? TH_R2B_NO_VARS : 0);
614
615 Th_InitTraceLog()/*processes -th-trace flag*/;
616
617 if(find_option("div",0,0)!=0){
618 pikFlags |= PIKCHR_PROCESS_DIV;
619 }else if(find_option("div-indent",0,0)!=0){
620 pikFlags |= PIKCHR_PROCESS_DIV_INDENT;
@@ -644,24 +561,14 @@
644 }
645 if(g.argc>3){
646 zOutfile = g.argv[3];
647 }
648 blob_read_from_file(&bIn, zInfile, ExtFILE);
649 if(fTh1){
650 db_find_and_open_repository(OPEN_ANY_SCHEMA | OPEN_OK_NOT_FOUND, 0)
651 /* ^^^ needed for certain TH1 functions to work */;
652 pikFlags |= PIKCHR_PROCESS_TH1;
653 if(fNosvg) pikFlags |= PIKCHR_PROCESS_TH1_NOSVG;
654 }
655 isErr = pikchr_process(blob_str(&bIn), pikFlags,
656 fTh1 ? fThFlags : 0, &bOut);
657 if(isErr){
658 fossil_fatal("%s ERROR:%c%b", 1==isErr ? "TH1" : "pikchr",
659 1==isErr ? ' ' : '\n',
660 &bOut);
661 }else{
662 blob_write_to_file(&bOut, zOutfile);
663 }
664 Th_PrintTraceLog();
665 blob_reset(&bIn);
666 blob_reset(&bOut);
667 }
668
--- src/pikchrshow.c
+++ src/pikchrshow.c
@@ -27,12 +27,10 @@
27 /* The first two must match the values from pikchr.c */
28 #define PIKCHR_PROCESS_PLAINTEXT_ERRORS 0x0001
29 #define PIKCHR_PROCESS_DARK_MODE 0x0002
30 /* end of flags supported directly by pikchr() */
31 #define PIKCHR_PROCESS_PASSTHROUGH 0x0003 /* Pass through these flags */
 
 
32 #define PIKCHR_PROCESS_NONCE 0x0010
33 #define PIKCHR_PROCESS_ERR_PRE 0x0020
34 #define PIKCHR_PROCESS_SRC 0x0040
35 #define PIKCHR_PROCESS_DIV 0x0080
36 #define PIKCHR_PROCESS_DIV_INDENT 0x0100
@@ -43,36 +41,20 @@
41 #define PIKCHR_PROCESS_DIV_SOURCE 0x2000
42 #define PIKCHR_PROCESS_DIV_SOURCE_INLINE 0x4000
43 #endif
44
45 /*
46 ** Processes a pikchr script. zIn is the NUL-terminated input
 
47 ** script. pikFlags may be a bitmask of any of the PIKCHR_PROCESS_xxx
48 ** flags documented below. Output is sent to pOut,
 
 
49 **
50 ** Returns 0 on success, or non-zero if pikchr processing failed.
51 ** In either case, the error message (if any) from pikchr will be
52 ** appended to pOut.
53 **
54 ** pikFlags flag descriptions:
55 **
 
 
 
 
 
 
 
 
 
 
 
 
 
56 ** - PIKCHR_PROCESS_DIV: if set, the SVG result is wrapped in a DIV
57 ** element which specifies a max-width style value based on the SVG's
58 ** calculated size. This flag has multiple mutually exclusive forms:
59 **
60 ** - PIKCHR_PROCESS_DIV uses default element alignment.
@@ -116,14 +98,14 @@
98 **
99 ** - PIKCHR_PROCESS_ERR_PRE: if set and pikchr() fails, the resulting
100 ** error report is wrapped in a PRE element, else it is retained
101 ** as-is (intended only for console output).
102 */
103 int pikchr_process(const char *zIn, int pikFlags, Blob * pOut){
 
 
104 int isErr = 0;
105 int w = 0, h = 0;
106 char *zOut;
107 const char *zNonce = (PIKCHR_PROCESS_NONCE & pikFlags)
108 ? safe_html_nonce(1) : 0;
109
110 if(!(PIKCHR_PROCESS_DIV & pikFlags)
111 /* If any DIV_xxx flags are set, set DIV */
@@ -135,115 +117,87 @@
117 | PIKCHR_PROCESS_DIV_SOURCE_INLINE
118 | PIKCHR_PROCESS_DIV_TOGGLE
119 ) & pikFlags){
120 pikFlags |= PIKCHR_PROCESS_DIV;
121 }
122 if(zNonce){
123 blob_appendf(pOut, "%s\n", zNonce);
124 }
125 zOut = pikchr(zIn, "pikchr",
126 0x01 | (pikFlags&PIKCHR_PROCESS_PASSTHROUGH),
127 &w, &h);
128 if( w>0 && h>0 ){
129 const char * zClassToggle = "";
130 const char * zClassSource = "";
131 const char * zWrapperClass = "";
132 if(PIKCHR_PROCESS_DIV & pikFlags){
133 if(PIKCHR_PROCESS_DIV_CENTER & pikFlags){
134 zWrapperClass = " center";
135 }else if(PIKCHR_PROCESS_DIV_INDENT & pikFlags){
136 zWrapperClass = " indent";
137 }else if(PIKCHR_PROCESS_DIV_FLOAT_LEFT & pikFlags){
138 zWrapperClass = " float-left";
139 }else if(PIKCHR_PROCESS_DIV_FLOAT_RIGHT & pikFlags){
140 zWrapperClass = " float-right";
141 }
142 if(PIKCHR_PROCESS_DIV_TOGGLE & pikFlags){
143 zClassToggle = " toggle";
144 }
145 if(PIKCHR_PROCESS_DIV_SOURCE_INLINE & pikFlags){
146 if(PIKCHR_PROCESS_DIV_SOURCE & pikFlags){
147 zClassSource = " source source-inline";
148 }else{
149 zClassSource = " source-inline";
150 }
151 pikFlags |= PIKCHR_PROCESS_SRC;
152 }else if(PIKCHR_PROCESS_DIV_SOURCE & pikFlags){
153 zClassSource = " source";
154 pikFlags |= PIKCHR_PROCESS_SRC;
155 }
156 blob_appendf(pOut,"<div class='pikchr-wrapper"
157 "%s%s%s'>"
158 "<div class=\"pikchr-svg\" "
159 "style=\"max-width:%dpx\">\n",
160 zWrapperClass/*safe-for-%s*/,
161 zClassToggle/*safe-for-%s*/,
162 zClassSource/*safe-for-%s*/, w);
163 }
164 blob_append(pOut, zOut, -1);
165 if(PIKCHR_PROCESS_DIV & pikFlags){
166 blob_append(pOut, "</div>\n", 7);
167 }
168 if(PIKCHR_PROCESS_SRC & pikFlags){
169 static int counter = 0;
170 ++counter;
171 blob_appendf(pOut, "<div class='pikchr-src'>"
172 "<pre id='pikchr-src-%d'>%h</pre>"
173 "<span class='hidden'>"
174 "<a href='%R/pikchrshow?fromSession' "
175 "class='pikchr-src-pikchrshow' target='_new-%d' "
176 "data-pikchrid='pikchr-src-%d' "
177 "title='Open this pikchr in /pikchrshow'"
178 ">&rarr; /pikchrshow</a></span>"
179 "</div>\n",
180 counter, zIn, counter, counter);
181 }
182 if(PIKCHR_PROCESS_DIV & pikFlags){
183 blob_append(pOut, "</div>\n", 7);
184 }
185 }else{
186 isErr = 2;
187 if(PIKCHR_PROCESS_ERR_PRE & pikFlags){
188 blob_append(pOut, "<pre class='error'>\n", 20);
189 }
190 blob_appendf(pOut, "%h", zOut);
191 if(PIKCHR_PROCESS_ERR_PRE & pikFlags){
192 blob_append(pOut, "\n</pre>\n", 8);
193 }
194 }
195 fossil_free(zOut);
196 if(zNonce){
197 blob_appendf(pOut, "%s\n", zNonce);
198 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199 return isErr;
200 }
201
202 /*
203 ** Legacy impl of /pikchrshow. pikchrshow_page() will delegate to
@@ -279,11 +233,11 @@
233 TODO: respond with JSON instead.*/
234 cgi_set_content_type("text/html");
235 if(zContent && *zContent){
236 Blob out = empty_blob;
237 const int isErr =
238 pikchr_process(zContent, pikFlags, &out);
239 if(isErr){
240 cgi_printf_header("x-pikchrshow-is-error: %d\r\n", isErr);
241 }
242 CX("%b", &out);
243 blob_reset(&out);
@@ -384,11 +338,11 @@
338 /* Reminder: Firefox does not properly flexbox a LEGEND
339 element, always flowing it in column mode. */);
340 CX("<div id='pikchrshow-output'>");
341 if(*zContent){
342 Blob out = empty_blob;
343 pikchr_process(zContent, pikFlags, &out);
344 CX("%b", &out);
345 blob_reset(&out);
346 } CX("</div>"/*#pikchrshow-output*/);
347 } CX("</fieldset>"/*#pikchrshow-output-wrapper*/);
348 } CX("</div>"/*sbs-wrapper*/);
@@ -561,60 +515,23 @@
515 **
516 ** -src Store the input pikchr's source code in the output as
517 ** a separate element adjacent to the SVG one. Implied
518 ** by -div-source.
519 **
 
 
 
 
 
 
 
 
 
 
 
 
 
520 ** -dark Change pikchr colors to assume a dark-mode theme.
521 **
522 **
523 ** The -div-indent/center/left/right flags may not be combined.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
524 */
525 void pikchr_cmd(void){
526 Blob bIn = empty_blob;
527 Blob bOut = empty_blob;
528 const char * zInfile = "-";
529 const char * zOutfile = "-";
 
 
530 int isErr = 0;
531 int pikFlags = find_option("src",0,0)!=0
532 ? PIKCHR_PROCESS_SRC : 0;
 
 
 
 
533
534 if(find_option("div",0,0)!=0){
535 pikFlags |= PIKCHR_PROCESS_DIV;
536 }else if(find_option("div-indent",0,0)!=0){
537 pikFlags |= PIKCHR_PROCESS_DIV_INDENT;
@@ -644,24 +561,14 @@
561 }
562 if(g.argc>3){
563 zOutfile = g.argv[3];
564 }
565 blob_read_from_file(&bIn, zInfile, ExtFILE);
566 isErr = pikchr_process(blob_str(&bIn), pikFlags, &bOut);
 
 
 
 
 
 
 
567 if(isErr){
568 fossil_fatal("pikchr ERROR: %b", &bOut);
 
 
569 }else{
570 blob_write_to_file(&bOut, zOutfile);
571 }
 
572 blob_reset(&bIn);
573 blob_reset(&bOut);
574 }
575
+1 -22
--- src/repolist.c
+++ src/repolist.c
@@ -101,25 +101,10 @@
101101
finish_repo_list:
102102
g.dbIgnoreErrors--;
103103
sqlite3_close(db);
104104
}
105105
106
-/*
107
-** SETTING: show-repolist-desc boolean default=off
108
-**
109
-** If the value of this setting is "1" globally, then the repository-list
110
-** page will show the description of each repository. This setting only
111
-** has effect when it is in the global setting database.
112
-*/
113
-/*
114
-** SETTING: show-repolist-lg boolean default=off
115
-**
116
-** If the value of this setting is "1" globally, then the repository-list
117
-** page will show the login-group for each repository. This setting only
118
-** has effect when it is in the global setting database.
119
-*/
120
-
121106
/*
122107
** Generate a web-page that lists all repositories located under the
123108
** g.zRepositoryName directory and return non-zero.
124109
**
125110
** For the special case when g.zRepositoryName is a non-chroot-jail "/",
@@ -150,17 +135,10 @@
150135
assert( g.db==0 );
151136
zShow = P("FOSSIL_REPOLIST_SHOW");
152137
if( zShow ){
153138
bShowDesc = strstr(zShow,"description")!=0;
154139
bShowLg = strstr(zShow,"login-group")!=0;
155
- }else if( db_open_config(1, 1)
156
- && db_table_exists("configdb", "global_config")
157
- ){
158
- bShowDesc = db_int(bShowDesc, "SELECT value FROM global_config"
159
- " WHERE name='show-repolist-desc'");
160
- bShowLg = db_int(bShowLg, "SELECT value FROM global_config"
161
- " WHERE name='show-repolist-lg'");
162140
}
163141
blob_init(&html, 0, 0);
164142
if( fossil_strcmp(g.zRepositoryName,"/")==0 && !g.fJail ){
165143
/* For the special case of the "repository directory" being "/",
166144
** show all of the repositories named in the ~/.fossil database.
@@ -168,10 +146,11 @@
168146
** On unix systems, then entries are of the form "repo:/home/..."
169147
** and on Windows systems they are like on unix, starting with a "/"
170148
** or they can begin with a drive letter: "repo:C:/Users/...". In either
171149
** case, we want returned path to omit any initial "/".
172150
*/
151
+ db_open_config(1, 0);
173152
db_multi_exec(
174153
"CREATE TEMP VIEW sfile AS"
175154
" SELECT ltrim(substr(name,6),'/') AS 'pathname' FROM global_config"
176155
" WHERE name GLOB 'repo:*'"
177156
);
178157
--- src/repolist.c
+++ src/repolist.c
@@ -101,25 +101,10 @@
101 finish_repo_list:
102 g.dbIgnoreErrors--;
103 sqlite3_close(db);
104 }
105
106 /*
107 ** SETTING: show-repolist-desc boolean default=off
108 **
109 ** If the value of this setting is "1" globally, then the repository-list
110 ** page will show the description of each repository. This setting only
111 ** has effect when it is in the global setting database.
112 */
113 /*
114 ** SETTING: show-repolist-lg boolean default=off
115 **
116 ** If the value of this setting is "1" globally, then the repository-list
117 ** page will show the login-group for each repository. This setting only
118 ** has effect when it is in the global setting database.
119 */
120
121 /*
122 ** Generate a web-page that lists all repositories located under the
123 ** g.zRepositoryName directory and return non-zero.
124 **
125 ** For the special case when g.zRepositoryName is a non-chroot-jail "/",
@@ -150,17 +135,10 @@
150 assert( g.db==0 );
151 zShow = P("FOSSIL_REPOLIST_SHOW");
152 if( zShow ){
153 bShowDesc = strstr(zShow,"description")!=0;
154 bShowLg = strstr(zShow,"login-group")!=0;
155 }else if( db_open_config(1, 1)
156 && db_table_exists("configdb", "global_config")
157 ){
158 bShowDesc = db_int(bShowDesc, "SELECT value FROM global_config"
159 " WHERE name='show-repolist-desc'");
160 bShowLg = db_int(bShowLg, "SELECT value FROM global_config"
161 " WHERE name='show-repolist-lg'");
162 }
163 blob_init(&html, 0, 0);
164 if( fossil_strcmp(g.zRepositoryName,"/")==0 && !g.fJail ){
165 /* For the special case of the "repository directory" being "/",
166 ** show all of the repositories named in the ~/.fossil database.
@@ -168,10 +146,11 @@
168 ** On unix systems, then entries are of the form "repo:/home/..."
169 ** and on Windows systems they are like on unix, starting with a "/"
170 ** or they can begin with a drive letter: "repo:C:/Users/...". In either
171 ** case, we want returned path to omit any initial "/".
172 */
 
173 db_multi_exec(
174 "CREATE TEMP VIEW sfile AS"
175 " SELECT ltrim(substr(name,6),'/') AS 'pathname' FROM global_config"
176 " WHERE name GLOB 'repo:*'"
177 );
178
--- src/repolist.c
+++ src/repolist.c
@@ -101,25 +101,10 @@
101 finish_repo_list:
102 g.dbIgnoreErrors--;
103 sqlite3_close(db);
104 }
105
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106 /*
107 ** Generate a web-page that lists all repositories located under the
108 ** g.zRepositoryName directory and return non-zero.
109 **
110 ** For the special case when g.zRepositoryName is a non-chroot-jail "/",
@@ -150,17 +135,10 @@
135 assert( g.db==0 );
136 zShow = P("FOSSIL_REPOLIST_SHOW");
137 if( zShow ){
138 bShowDesc = strstr(zShow,"description")!=0;
139 bShowLg = strstr(zShow,"login-group")!=0;
 
 
 
 
 
 
 
140 }
141 blob_init(&html, 0, 0);
142 if( fossil_strcmp(g.zRepositoryName,"/")==0 && !g.fJail ){
143 /* For the special case of the "repository directory" being "/",
144 ** show all of the repositories named in the ~/.fossil database.
@@ -168,10 +146,11 @@
146 ** On unix systems, then entries are of the form "repo:/home/..."
147 ** and on Windows systems they are like on unix, starting with a "/"
148 ** or they can begin with a drive letter: "repo:C:/Users/...". In either
149 ** case, we want returned path to omit any initial "/".
150 */
151 db_open_config(1, 0);
152 db_multi_exec(
153 "CREATE TEMP VIEW sfile AS"
154 " SELECT ltrim(substr(name,6),'/') AS 'pathname' FROM global_config"
155 " WHERE name GLOB 'repo:*'"
156 );
157
--- src/security_audit.c
+++ src/security_audit.c
@@ -100,10 +100,11 @@
100100
const char *zReadCap; /* Capabilities of user group "reader" */
101101
const char *zPubPages; /* GLOB pattern for public pages */
102102
const char *zSelfCap; /* Capabilities of self-registered users */
103103
int hasSelfReg = 0; /* True if able to self-register */
104104
const char *zPublicUrl; /* Canonical access URL */
105
+ const char *zVulnReport; /* The vuln-report setting */
105106
Blob cmd;
106107
char *z;
107108
int n, i;
108109
CapabilityString *pCap;
109110
char **azCSP; /* Parsed content security policy */
@@ -362,10 +363,22 @@
362363
@ <li><p><b>WARNING:</b>
363364
@ The "strict-manifest-syntax" flag is off. This is a security
364365
@ risk. Turn this setting on (its default) to protect the users
365366
@ of this repository.
366367
}
368
+
369
+ zVulnReport = db_get("vuln-report","log");
370
+ if( fossil_strcmp(zVulnReport,"block")!=0
371
+ && fossil_strcmp(zVulnReport,"fatal")!=0
372
+ ){
373
+ @ <li><p><b>WARNING:</b>
374
+ @ The <a href="%R/help?cmd=vuln-report">vuln-report setting</a>
375
+ @ has a value of "%h(zVulnReport)". This disables defenses against
376
+ @ XSS or SQL-injection vulnerabilities caused by coding errors in
377
+ @ custom TH1 scripts. For the best security, change
378
+ @ the value of the vuln-report setting to "block" or "fatal".
379
+ }
367380
368381
/* Obsolete: */
369382
if( hasAnyCap(zAnonCap, "d") ||
370383
hasAnyCap(zDevCap, "d") ||
371384
hasAnyCap(zReadCap, "d") ){
372385
--- src/security_audit.c
+++ src/security_audit.c
@@ -100,10 +100,11 @@
100 const char *zReadCap; /* Capabilities of user group "reader" */
101 const char *zPubPages; /* GLOB pattern for public pages */
102 const char *zSelfCap; /* Capabilities of self-registered users */
103 int hasSelfReg = 0; /* True if able to self-register */
104 const char *zPublicUrl; /* Canonical access URL */
 
105 Blob cmd;
106 char *z;
107 int n, i;
108 CapabilityString *pCap;
109 char **azCSP; /* Parsed content security policy */
@@ -362,10 +363,22 @@
362 @ <li><p><b>WARNING:</b>
363 @ The "strict-manifest-syntax" flag is off. This is a security
364 @ risk. Turn this setting on (its default) to protect the users
365 @ of this repository.
366 }
 
 
 
 
 
 
 
 
 
 
 
 
367
368 /* Obsolete: */
369 if( hasAnyCap(zAnonCap, "d") ||
370 hasAnyCap(zDevCap, "d") ||
371 hasAnyCap(zReadCap, "d") ){
372
--- src/security_audit.c
+++ src/security_audit.c
@@ -100,10 +100,11 @@
100 const char *zReadCap; /* Capabilities of user group "reader" */
101 const char *zPubPages; /* GLOB pattern for public pages */
102 const char *zSelfCap; /* Capabilities of self-registered users */
103 int hasSelfReg = 0; /* True if able to self-register */
104 const char *zPublicUrl; /* Canonical access URL */
105 const char *zVulnReport; /* The vuln-report setting */
106 Blob cmd;
107 char *z;
108 int n, i;
109 CapabilityString *pCap;
110 char **azCSP; /* Parsed content security policy */
@@ -362,10 +363,22 @@
363 @ <li><p><b>WARNING:</b>
364 @ The "strict-manifest-syntax" flag is off. This is a security
365 @ risk. Turn this setting on (its default) to protect the users
366 @ of this repository.
367 }
368
369 zVulnReport = db_get("vuln-report","log");
370 if( fossil_strcmp(zVulnReport,"block")!=0
371 && fossil_strcmp(zVulnReport,"fatal")!=0
372 ){
373 @ <li><p><b>WARNING:</b>
374 @ The <a href="%R/help?cmd=vuln-report">vuln-report setting</a>
375 @ has a value of "%h(zVulnReport)". This disables defenses against
376 @ XSS or SQL-injection vulnerabilities caused by coding errors in
377 @ custom TH1 scripts. For the best security, change
378 @ the value of the vuln-report setting to "block" or "fatal".
379 }
380
381 /* Obsolete: */
382 if( hasAnyCap(zAnonCap, "d") ||
383 hasAnyCap(zDevCap, "d") ||
384 hasAnyCap(zReadCap, "d") ){
385
+1 -1
--- src/tag.c
+++ src/tag.c
@@ -412,11 +412,11 @@
412412
** non-CHECK-IN artifacts.
413413
** --user-override USER Name USER when adding the tag
414414
**
415415
** The --date-override and --user-override options support
416416
** importing history from other SCM systems. DATETIME has
417
-** the form 'YYYY-MMM-DD HH:MM:SS'.
417
+** the form 'YYYY-MM-DD HH:MM:SS'.
418418
**
419419
** Note that fossil uses some tag prefixes internally and this
420420
** command will reject tags with these prefixes to avoid
421421
** causing problems or confusion: "wiki-", "tkt-", "event-".
422422
**
423423
--- src/tag.c
+++ src/tag.c
@@ -412,11 +412,11 @@
412 ** non-CHECK-IN artifacts.
413 ** --user-override USER Name USER when adding the tag
414 **
415 ** The --date-override and --user-override options support
416 ** importing history from other SCM systems. DATETIME has
417 ** the form 'YYYY-MMM-DD HH:MM:SS'.
418 **
419 ** Note that fossil uses some tag prefixes internally and this
420 ** command will reject tags with these prefixes to avoid
421 ** causing problems or confusion: "wiki-", "tkt-", "event-".
422 **
423
--- src/tag.c
+++ src/tag.c
@@ -412,11 +412,11 @@
412 ** non-CHECK-IN artifacts.
413 ** --user-override USER Name USER when adding the tag
414 **
415 ** The --date-override and --user-override options support
416 ** importing history from other SCM systems. DATETIME has
417 ** the form 'YYYY-MM-DD HH:MM:SS'.
418 **
419 ** Note that fossil uses some tag prefixes internally and this
420 ** command will reject tags with these prefixes to avoid
421 ** causing problems or confusion: "wiki-", "tkt-", "event-".
422 **
423
+7 -1
--- src/th.c
+++ src/th.c
@@ -1794,15 +1794,19 @@
17941794
int hasSpecialChar = 0; /* Whitespace or {}[]'" */
17951795
int hasEscapeChar = 0; /* '}' without matching '{' to the left or a '\\' */
17961796
int nBrace = 0;
17971797
17981798
output.zBuf = *pzList;
1799
- output.nBuf = *pnList;
1799
+ output.nBuf = TH1_LEN(*pnList);
18001800
output.nBufAlloc = output.nBuf;
1801
+ output.bTaint = 0;
1802
+ TH1_XFER_TAINT(output.bTaint, *pnList);
18011803
18021804
if( nElem<0 ){
18031805
nElem = th_strlen(zElem);
1806
+ }else{
1807
+ nElem = TH1_LEN(nElem);
18041808
}
18051809
if( output.nBuf>0 ){
18061810
thBufferAddChar(interp, &output, ' ');
18071811
}
18081812
@@ -2127,16 +2131,18 @@
21272131
/* Evaluate left and right arguments, if they exist. */
21282132
if( pExpr->pLeft ){
21292133
rc = exprEval(interp, pExpr->pLeft);
21302134
if( rc==TH_OK ){
21312135
zLeft = Th_TakeResult(interp, &nLeft);
2136
+ nLeft = TH1_LEN(nLeft);
21322137
}
21332138
}
21342139
if( rc==TH_OK && pExpr->pRight ){
21352140
rc = exprEval(interp, pExpr->pRight);
21362141
if( rc==TH_OK ){
21372142
zRight = Th_TakeResult(interp, &nRight);
2143
+ nRight = TH1_LEN(nRight);
21382144
}
21392145
}
21402146
21412147
/* Convert arguments to their required forms. */
21422148
if( rc==TH_OK ){
21432149
--- src/th.c
+++ src/th.c
@@ -1794,15 +1794,19 @@
1794 int hasSpecialChar = 0; /* Whitespace or {}[]'" */
1795 int hasEscapeChar = 0; /* '}' without matching '{' to the left or a '\\' */
1796 int nBrace = 0;
1797
1798 output.zBuf = *pzList;
1799 output.nBuf = *pnList;
1800 output.nBufAlloc = output.nBuf;
 
 
1801
1802 if( nElem<0 ){
1803 nElem = th_strlen(zElem);
 
 
1804 }
1805 if( output.nBuf>0 ){
1806 thBufferAddChar(interp, &output, ' ');
1807 }
1808
@@ -2127,16 +2131,18 @@
2127 /* Evaluate left and right arguments, if they exist. */
2128 if( pExpr->pLeft ){
2129 rc = exprEval(interp, pExpr->pLeft);
2130 if( rc==TH_OK ){
2131 zLeft = Th_TakeResult(interp, &nLeft);
 
2132 }
2133 }
2134 if( rc==TH_OK && pExpr->pRight ){
2135 rc = exprEval(interp, pExpr->pRight);
2136 if( rc==TH_OK ){
2137 zRight = Th_TakeResult(interp, &nRight);
 
2138 }
2139 }
2140
2141 /* Convert arguments to their required forms. */
2142 if( rc==TH_OK ){
2143
--- src/th.c
+++ src/th.c
@@ -1794,15 +1794,19 @@
1794 int hasSpecialChar = 0; /* Whitespace or {}[]'" */
1795 int hasEscapeChar = 0; /* '}' without matching '{' to the left or a '\\' */
1796 int nBrace = 0;
1797
1798 output.zBuf = *pzList;
1799 output.nBuf = TH1_LEN(*pnList);
1800 output.nBufAlloc = output.nBuf;
1801 output.bTaint = 0;
1802 TH1_XFER_TAINT(output.bTaint, *pnList);
1803
1804 if( nElem<0 ){
1805 nElem = th_strlen(zElem);
1806 }else{
1807 nElem = TH1_LEN(nElem);
1808 }
1809 if( output.nBuf>0 ){
1810 thBufferAddChar(interp, &output, ' ');
1811 }
1812
@@ -2127,16 +2131,18 @@
2131 /* Evaluate left and right arguments, if they exist. */
2132 if( pExpr->pLeft ){
2133 rc = exprEval(interp, pExpr->pLeft);
2134 if( rc==TH_OK ){
2135 zLeft = Th_TakeResult(interp, &nLeft);
2136 nLeft = TH1_LEN(nLeft);
2137 }
2138 }
2139 if( rc==TH_OK && pExpr->pRight ){
2140 rc = exprEval(interp, pExpr->pRight);
2141 if( rc==TH_OK ){
2142 zRight = Th_TakeResult(interp, &nRight);
2143 nRight = TH1_LEN(nRight);
2144 }
2145 }
2146
2147 /* Convert arguments to their required forms. */
2148 if( rc==TH_OK ){
2149
+22 -5
--- src/th_lang.c
+++ src/th_lang.c
@@ -180,20 +180,24 @@
180180
int nVar;
181181
char **azValue = 0;
182182
int *anValue;
183183
int nValue;
184184
int ii, jj;
185
+ int bTaint = 0;
185186
186187
if( argc!=4 ){
187188
return Th_WrongNumArgs(interp, "foreach varlist list script");
188189
}
189190
rc = Th_SplitList(interp, argv[1], argl[1], &azVar, &anVar, &nVar);
190191
if( rc ) return rc;
192
+ TH1_XFER_TAINT(bTaint, argl[2]);
191193
rc = Th_SplitList(interp, argv[2], argl[2], &azValue, &anValue, &nValue);
192194
for(ii=0; rc==TH_OK && ii<=nValue-nVar; ii+=nVar){
193195
for(jj=0; jj<nVar; jj++){
194
- Th_SetVar(interp, azVar[jj], anVar[jj], azValue[ii+jj], anValue[ii+jj]);
196
+ int x = anValue[ii+jj];
197
+ TH1_XFER_TAINT(x, bTaint);
198
+ Th_SetVar(interp, azVar[jj], anVar[jj], azValue[ii+jj], x);
195199
}
196200
rc = eval_loopbody(interp, argv[3], argl[3]);
197201
}
198202
if( rc==TH_BREAK ) rc = TH_OK;
199203
Th_Free(interp, azVar);
@@ -257,10 +261,11 @@
257261
rc = Th_GetVar(interp, argv[1], argl[1]);
258262
if( rc==TH_OK ){
259263
zList = Th_TakeResult(interp, &nList);
260264
}
261265
266
+ TH1_XFER_TAINT(bTaint, nList);
262267
for(i=2; i<argc; i++){
263268
TH1_XFER_TAINT(bTaint, argl[i]);
264269
Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]);
265270
}
266271
@@ -289,23 +294,27 @@
289294
int rc;
290295
291296
char **azElem;
292297
int *anElem;
293298
int nCount;
299
+ int bTaint = 0;
294300
295301
if( argc!=3 ){
296302
return Th_WrongNumArgs(interp, "lindex list index");
297303
}
298304
299305
if( TH_OK!=Th_ToInt(interp, argv[2], argl[2], &iElem) ){
300306
return TH_ERROR;
301307
}
302308
309
+ TH1_XFER_TAINT(bTaint, argl[1]);
303310
rc = Th_SplitList(interp, argv[1], argl[1], &azElem, &anElem, &nCount);
304311
if( rc==TH_OK ){
305312
if( iElem<nCount && iElem>=0 ){
306
- Th_SetResult(interp, azElem[iElem], anElem[iElem]);
313
+ int sz = anElem[iElem];
314
+ TH1_XFER_TAINT(sz, bTaint);
315
+ Th_SetResult(interp, azElem[iElem], sz);
307316
}else{
308317
Th_SetResult(interp, 0, 0);
309318
}
310319
Th_Free(interp, azElem);
311320
}
@@ -586,11 +595,13 @@
586595
/* If the last parameter in the parameter list is "args", then set the
587596
** ProcDefn.hasArgs flag. The "args" parameter does not require an
588597
** entry in the ProcDefn.azParam[] or ProcDefn.azDefault[] arrays.
589598
*/
590599
if( nParam>0 ){
591
- if( anParam[nParam-1]==4 && 0==memcmp(azParam[nParam-1], "args", 4) ){
600
+ if( TH1_LEN(anParam[nParam-1])==4
601
+ && 0==memcmp(azParam[nParam-1], "args", 4)
602
+ ){
592603
p->hasArgs = 1;
593604
nParam--;
594605
}
595606
}
596607
@@ -830,11 +841,13 @@
830841
interp, "Expected \"end\" or integer, got:", argv[3], argl[3]);
831842
return TH_ERROR;
832843
}
833844
834845
if( iIndex>=0 && iIndex<TH1_LEN(argl[2]) ){
835
- return Th_SetResult(interp, &argv[2][iIndex], 1);
846
+ int sz = 1;
847
+ TH1_XFER_TAINT(sz, argl[2]);
848
+ return Th_SetResult(interp, &argv[2][iIndex], sz);
836849
}else{
837850
return Th_SetResult(interp, 0, 0);
838851
}
839852
}
840853
@@ -968,10 +981,11 @@
968981
static int string_range_command(
969982
Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
970983
){
971984
int iStart;
972985
int iEnd;
986
+ int sz;
973987
974988
if( argc!=5 ){
975989
return Th_WrongNumArgs(interp, "string range string first last");
976990
}
977991
@@ -987,12 +1001,14 @@
9871001
}
9881002
9891003
if( iStart<0 ) iStart = 0;
9901004
if( iEnd>=TH1_LEN(argl[2]) ) iEnd = TH1_LEN(argl[2])-1;
9911005
if( iStart>iEnd ) iEnd = iStart-1;
1006
+ sz = iEnd - iStart + 1;
1007
+ TH1_XFER_TAINT(sz, argl[2]);
9921008
993
- return Th_SetResult(interp, &argv[2][iStart], iEnd-iStart+1);
1009
+ return Th_SetResult(interp, &argv[2][iStart], sz);
9941010
}
9951011
9961012
/*
9971013
** TH Syntax:
9981014
**
@@ -1052,10 +1068,11 @@
10521068
while( n && th_isspace(z[0]) ){ z++; n--; }
10531069
}
10541070
if( TH1_LEN(argl[1])<5 || argv[1][4]=='r' ){
10551071
while( n && th_isspace(z[n-1]) ){ n--; }
10561072
}
1073
+ TH1_XFER_TAINT(n, argl[2]);
10571074
Th_SetResult(interp, z, n);
10581075
return TH_OK;
10591076
}
10601077
10611078
/*
10621079
--- src/th_lang.c
+++ src/th_lang.c
@@ -180,20 +180,24 @@
180 int nVar;
181 char **azValue = 0;
182 int *anValue;
183 int nValue;
184 int ii, jj;
 
185
186 if( argc!=4 ){
187 return Th_WrongNumArgs(interp, "foreach varlist list script");
188 }
189 rc = Th_SplitList(interp, argv[1], argl[1], &azVar, &anVar, &nVar);
190 if( rc ) return rc;
 
191 rc = Th_SplitList(interp, argv[2], argl[2], &azValue, &anValue, &nValue);
192 for(ii=0; rc==TH_OK && ii<=nValue-nVar; ii+=nVar){
193 for(jj=0; jj<nVar; jj++){
194 Th_SetVar(interp, azVar[jj], anVar[jj], azValue[ii+jj], anValue[ii+jj]);
 
 
195 }
196 rc = eval_loopbody(interp, argv[3], argl[3]);
197 }
198 if( rc==TH_BREAK ) rc = TH_OK;
199 Th_Free(interp, azVar);
@@ -257,10 +261,11 @@
257 rc = Th_GetVar(interp, argv[1], argl[1]);
258 if( rc==TH_OK ){
259 zList = Th_TakeResult(interp, &nList);
260 }
261
 
262 for(i=2; i<argc; i++){
263 TH1_XFER_TAINT(bTaint, argl[i]);
264 Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]);
265 }
266
@@ -289,23 +294,27 @@
289 int rc;
290
291 char **azElem;
292 int *anElem;
293 int nCount;
 
294
295 if( argc!=3 ){
296 return Th_WrongNumArgs(interp, "lindex list index");
297 }
298
299 if( TH_OK!=Th_ToInt(interp, argv[2], argl[2], &iElem) ){
300 return TH_ERROR;
301 }
302
 
303 rc = Th_SplitList(interp, argv[1], argl[1], &azElem, &anElem, &nCount);
304 if( rc==TH_OK ){
305 if( iElem<nCount && iElem>=0 ){
306 Th_SetResult(interp, azElem[iElem], anElem[iElem]);
 
 
307 }else{
308 Th_SetResult(interp, 0, 0);
309 }
310 Th_Free(interp, azElem);
311 }
@@ -586,11 +595,13 @@
586 /* If the last parameter in the parameter list is "args", then set the
587 ** ProcDefn.hasArgs flag. The "args" parameter does not require an
588 ** entry in the ProcDefn.azParam[] or ProcDefn.azDefault[] arrays.
589 */
590 if( nParam>0 ){
591 if( anParam[nParam-1]==4 && 0==memcmp(azParam[nParam-1], "args", 4) ){
 
 
592 p->hasArgs = 1;
593 nParam--;
594 }
595 }
596
@@ -830,11 +841,13 @@
830 interp, "Expected \"end\" or integer, got:", argv[3], argl[3]);
831 return TH_ERROR;
832 }
833
834 if( iIndex>=0 && iIndex<TH1_LEN(argl[2]) ){
835 return Th_SetResult(interp, &argv[2][iIndex], 1);
 
 
836 }else{
837 return Th_SetResult(interp, 0, 0);
838 }
839 }
840
@@ -968,10 +981,11 @@
968 static int string_range_command(
969 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
970 ){
971 int iStart;
972 int iEnd;
 
973
974 if( argc!=5 ){
975 return Th_WrongNumArgs(interp, "string range string first last");
976 }
977
@@ -987,12 +1001,14 @@
987 }
988
989 if( iStart<0 ) iStart = 0;
990 if( iEnd>=TH1_LEN(argl[2]) ) iEnd = TH1_LEN(argl[2])-1;
991 if( iStart>iEnd ) iEnd = iStart-1;
 
 
992
993 return Th_SetResult(interp, &argv[2][iStart], iEnd-iStart+1);
994 }
995
996 /*
997 ** TH Syntax:
998 **
@@ -1052,10 +1068,11 @@
1052 while( n && th_isspace(z[0]) ){ z++; n--; }
1053 }
1054 if( TH1_LEN(argl[1])<5 || argv[1][4]=='r' ){
1055 while( n && th_isspace(z[n-1]) ){ n--; }
1056 }
 
1057 Th_SetResult(interp, z, n);
1058 return TH_OK;
1059 }
1060
1061 /*
1062
--- src/th_lang.c
+++ src/th_lang.c
@@ -180,20 +180,24 @@
180 int nVar;
181 char **azValue = 0;
182 int *anValue;
183 int nValue;
184 int ii, jj;
185 int bTaint = 0;
186
187 if( argc!=4 ){
188 return Th_WrongNumArgs(interp, "foreach varlist list script");
189 }
190 rc = Th_SplitList(interp, argv[1], argl[1], &azVar, &anVar, &nVar);
191 if( rc ) return rc;
192 TH1_XFER_TAINT(bTaint, argl[2]);
193 rc = Th_SplitList(interp, argv[2], argl[2], &azValue, &anValue, &nValue);
194 for(ii=0; rc==TH_OK && ii<=nValue-nVar; ii+=nVar){
195 for(jj=0; jj<nVar; jj++){
196 int x = anValue[ii+jj];
197 TH1_XFER_TAINT(x, bTaint);
198 Th_SetVar(interp, azVar[jj], anVar[jj], azValue[ii+jj], x);
199 }
200 rc = eval_loopbody(interp, argv[3], argl[3]);
201 }
202 if( rc==TH_BREAK ) rc = TH_OK;
203 Th_Free(interp, azVar);
@@ -257,10 +261,11 @@
261 rc = Th_GetVar(interp, argv[1], argl[1]);
262 if( rc==TH_OK ){
263 zList = Th_TakeResult(interp, &nList);
264 }
265
266 TH1_XFER_TAINT(bTaint, nList);
267 for(i=2; i<argc; i++){
268 TH1_XFER_TAINT(bTaint, argl[i]);
269 Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]);
270 }
271
@@ -289,23 +294,27 @@
294 int rc;
295
296 char **azElem;
297 int *anElem;
298 int nCount;
299 int bTaint = 0;
300
301 if( argc!=3 ){
302 return Th_WrongNumArgs(interp, "lindex list index");
303 }
304
305 if( TH_OK!=Th_ToInt(interp, argv[2], argl[2], &iElem) ){
306 return TH_ERROR;
307 }
308
309 TH1_XFER_TAINT(bTaint, argl[1]);
310 rc = Th_SplitList(interp, argv[1], argl[1], &azElem, &anElem, &nCount);
311 if( rc==TH_OK ){
312 if( iElem<nCount && iElem>=0 ){
313 int sz = anElem[iElem];
314 TH1_XFER_TAINT(sz, bTaint);
315 Th_SetResult(interp, azElem[iElem], sz);
316 }else{
317 Th_SetResult(interp, 0, 0);
318 }
319 Th_Free(interp, azElem);
320 }
@@ -586,11 +595,13 @@
595 /* If the last parameter in the parameter list is "args", then set the
596 ** ProcDefn.hasArgs flag. The "args" parameter does not require an
597 ** entry in the ProcDefn.azParam[] or ProcDefn.azDefault[] arrays.
598 */
599 if( nParam>0 ){
600 if( TH1_LEN(anParam[nParam-1])==4
601 && 0==memcmp(azParam[nParam-1], "args", 4)
602 ){
603 p->hasArgs = 1;
604 nParam--;
605 }
606 }
607
@@ -830,11 +841,13 @@
841 interp, "Expected \"end\" or integer, got:", argv[3], argl[3]);
842 return TH_ERROR;
843 }
844
845 if( iIndex>=0 && iIndex<TH1_LEN(argl[2]) ){
846 int sz = 1;
847 TH1_XFER_TAINT(sz, argl[2]);
848 return Th_SetResult(interp, &argv[2][iIndex], sz);
849 }else{
850 return Th_SetResult(interp, 0, 0);
851 }
852 }
853
@@ -968,10 +981,11 @@
981 static int string_range_command(
982 Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl
983 ){
984 int iStart;
985 int iEnd;
986 int sz;
987
988 if( argc!=5 ){
989 return Th_WrongNumArgs(interp, "string range string first last");
990 }
991
@@ -987,12 +1001,14 @@
1001 }
1002
1003 if( iStart<0 ) iStart = 0;
1004 if( iEnd>=TH1_LEN(argl[2]) ) iEnd = TH1_LEN(argl[2])-1;
1005 if( iStart>iEnd ) iEnd = iStart-1;
1006 sz = iEnd - iStart + 1;
1007 TH1_XFER_TAINT(sz, argl[2]);
1008
1009 return Th_SetResult(interp, &argv[2][iStart], sz);
1010 }
1011
1012 /*
1013 ** TH Syntax:
1014 **
@@ -1052,10 +1068,11 @@
1068 while( n && th_isspace(z[0]) ){ z++; n--; }
1069 }
1070 if( TH1_LEN(argl[1])<5 || argv[1][4]=='r' ){
1071 while( n && th_isspace(z[n-1]) ){ n--; }
1072 }
1073 TH1_XFER_TAINT(n, argl[2]);
1074 Th_SetResult(interp, z, n);
1075 return TH_OK;
1076 }
1077
1078 /*
1079
+20 -59
--- src/th_main.c
+++ src/th_main.c
@@ -31,13 +31,11 @@
3131
#define TH_INIT_NEED_CONFIG ((u32)0x00000001) /* Open configuration first? */
3232
#define TH_INIT_FORCE_TCL ((u32)0x00000002) /* Force Tcl to be enabled? */
3333
#define TH_INIT_FORCE_RESET ((u32)0x00000004) /* Force TH1 commands re-added? */
3434
#define TH_INIT_FORCE_SETUP ((u32)0x00000008) /* Force eval of setup script? */
3535
#define TH_INIT_NO_REPO ((u32)0x00000010) /* Skip opening repository. */
36
-#define TH_INIT_NO_ENCODE ((u32)0x00000020) /* Do not html-encode sendText()*/
37
- /* output. */
38
-#define TH_INIT_MASK ((u32)0x0000003F) /* All possible init flags. */
36
+#define TH_INIT_MASK ((u32)0x0000001F) /* All possible init flags. */
3937
4038
/*
4139
** Useful and/or "well-known" combinations of flag values.
4240
*/
4341
#define TH_INIT_DEFAULT (TH_INIT_NONE) /* Default flags. */
@@ -297,50 +295,10 @@
297295
TH1_LEN(argl[1]),argv[1],enableOutput);
298296
}
299297
return rc;
300298
}
301299
302
-/*
303
-** TH1 command: enable_htmlify ?BOOLEAN?
304
-**
305
-** Enable or disable the HTML escaping done by all output which
306
-** originates from TH1 (via sendText()).
307
-**
308
-** If passed no arguments it instead returns 0 or 1 to indicate the
309
-** current state.
310
-*/
311
-static int enableHtmlifyCmd(
312
- Th_Interp *interp,
313
- void *p,
314
- int argc,
315
- const char **argv,
316
- int *argl
317
-){
318
- int rc = 0, buul;
319
- if( argc>3 ){
320
- return Th_WrongNumArgs(interp,
321
- "enable_htmlify [TRACE_LABEL] ?BOOLEAN?");
322
- }
323
- buul = (TH_INIT_NO_ENCODE & g.th1Flags) ? 0 : 1;
324
- Th_SetResultInt(g.interp, buul);
325
- if(argc>1){
326
- if( g.thTrace ){
327
- Th_Trace("enable_htmlify {%.*s} -> %d<br>\n",
328
- TH1_LEN(argl[1]),argv[1],buul);
329
- }
330
- rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &buul);
331
- if(!rc){
332
- if(buul){
333
- g.th1Flags &= ~TH_INIT_NO_ENCODE;
334
- }else{
335
- g.th1Flags |= TH_INIT_NO_ENCODE;
336
- }
337
- }
338
- }
339
- return rc;
340
-}
341
-
342300
/*
343301
** Returns a name for a TH1 return code.
344302
*/
345303
const char *Th_ReturnCodeName(int rc, int nullIfOk){
346304
static char zRc[32];
@@ -376,23 +334,19 @@
376334
377335
/*
378336
** Send text to the appropriate output: If pOut is not NULL, it is
379337
** appended there, else to the console or to the CGI reply buffer.
380338
** Escape all characters with special meaning to HTML if the encode
381
-** parameter is true, with the exception that that flag is ignored if
382
-** g.th1Flags has the TH_INIT_NO_ENCODE flag.
339
+** parameter is true.
383340
**
384341
** If pOut is NULL and the global pThOut is not then that blob
385342
** is used for output.
386343
*/
387344
static void sendText(Blob *pOut, const char *z, int n, int encode){
388345
if(0==pOut && pThOut!=0){
389346
pOut = pThOut;
390347
}
391
- if(TH_INIT_NO_ENCODE & g.th1Flags){
392
- encode = 0;
393
- }
394348
if( enableOutput && n ){
395349
if( n<0 ){
396350
n = strlen(z);
397351
}else{
398352
n = TH1_LEN(n);
@@ -540,13 +494,12 @@
540494
n = argl[1];
541495
if( encode==0 && n>0 && TH1_TAINTED(n) ){
542496
if( Th_ReportTaint(interp, "output string", argv[1], n) ){
543497
return TH_ERROR;
544498
}
545
- n = TH1_LEN(n);
546499
}
547
- sendText(0,(char*)argv[1], n, encode);
500
+ sendText(0,(char*)argv[1], TH1_LEN(n), encode);
548501
return TH_OK;
549502
}
550503
551504
/*
552505
** TH1 command: redirect URL ?withMethod?
@@ -1450,17 +1403,21 @@
14501403
int argc,
14511404
const char **argv,
14521405
int *argl
14531406
){
14541407
const char *zDefault = 0;
1408
+ const char *zVal;
1409
+ int sz;
14551410
if( argc!=2 && argc!=3 ){
14561411
return Th_WrongNumArgs(interp, "getParameter NAME ?DEFAULT?");
14571412
}
14581413
if( argc==3 ){
14591414
zDefault = argv[2];
14601415
}
1461
- Th_SetResult(interp, cgi_parameter(argv[1], zDefault), -1);
1416
+ zVal = cgi_parameter(argv[1], zDefault);
1417
+ sz = th_strlen(zVal);
1418
+ Th_SetResult(interp, zVal, TH1_ADD_TAINT(sz));
14621419
return TH_OK;
14631420
}
14641421
14651422
/*
14661423
** TH1 command: setParameter NAME VALUE
@@ -2426,11 +2383,10 @@
24262383
{"copybtn", copybtnCmd, 0},
24272384
{"date", dateCmd, 0},
24282385
{"decorate", wikiCmd, (void*)&aFlags[2]},
24292386
{"defHeader", defHeaderCmd, 0},
24302387
{"dir", dirCmd, 0},
2431
- {"enable_htmlify",enableHtmlifyCmd, 0},
24322388
{"enable_output", enableOutputCmd, 0},
24332389
{"encode64", encode64Cmd, 0},
24342390
{"getParameter", getParameterCmd, 0},
24352391
{"glob_match", globMatchCmd, 0},
24362392
{"globalState", globalStateCmd, 0},
@@ -2984,11 +2940,16 @@
29842940
}
29852941
rc = Th_GetVar(g.interp, (char*)zVar, nVar);
29862942
z += i+1+n;
29872943
i = 0;
29882944
zResult = (char*)Th_GetResult(g.interp, &n);
2989
- sendText(pOut,(char*)zResult, n, encode);
2945
+ if( !TH1_TAINTED(n)
2946
+ || encode
2947
+ || Th_ReportTaint(g.interp, "inline variable", zVar, nVar)==TH_OK
2948
+ ){
2949
+ sendText(pOut,(char*)zResult, n, encode);
2950
+ }
29902951
}else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){
29912952
sendText(pOut,z, i, 0);
29922953
z += i+5;
29932954
for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){}
29942955
if( g.thTrace ){
@@ -3039,12 +3000,11 @@
30393000
** e.g. via the "render" script function binding, need to use the
30403001
** pThOut blob in order to avoid out-of-order output if
30413002
** Th_SetOutputBlob() has been called. If it has not been called,
30423003
** pThOut will be 0, which will redirect the output to CGI/stdout,
30433004
** as appropriate. We need to pass on g.th1Flags for the case of
3044
- ** recursive calls, so that, e.g., TH_INIT_NO_ENCODE does not get
3045
- ** inadvertently toggled off by a recursive call.
3005
+ ** recursive calls.
30463006
*/;
30473007
}
30483008
30493009
/*
30503010
** SETTING: vuln-report width=8 default=log
@@ -3076,22 +3036,22 @@
30763036
Th_Interp *interp, /* Report error here, if an error is reported */
30773037
const char *zWhere, /* Where the tainted string appears */
30783038
const char *zStr, /* The tainted string */
30793039
int nStr /* Length of the tainted string */
30803040
){
3081
- char *zDisp; /* Dispensation */
3082
- const char *zVulnType; /* Type of vulnerability */
3041
+ static const char *zDisp = 0; /* Dispensation; what to do with the error */
3042
+ const char *zVulnType; /* Type of vulnerability */
30833043
3084
- zDisp = db_get("vuln-report","log");
3044
+ if( zDisp==0 ) zDisp = db_get("vuln-report","log");
30853045
if( is_false(zDisp) ) return 0;
30863046
if( strstr(zWhere,"SQL")!=0 ){
30873047
zVulnType = "SQL-injection";
30883048
}else{
30893049
zVulnType = "XSS";
30903050
}
30913051
nStr = TH1_LEN(nStr);
3092
- fossil_errorlog("possible %s vulnerability due to tainted TH1 %s: \"%.*s\"",
3052
+ fossil_errorlog("possible TH1 %s vulnerability due to tainted %s: \"%.*s\"",
30933053
zVulnType, zWhere, nStr, zStr);
30943054
if( strcmp(zDisp,"log")==0 ){
30953055
return 0;
30963056
}
30973057
if( strcmp(zDisp,"block")==0 ){
@@ -3098,10 +3058,11 @@
30983058
char *z = mprintf("tainted %s: \"", zWhere);
30993059
Th_ErrorMessage(interp, z, zStr, nStr);
31003060
fossil_free(z);
31013061
}else{
31023062
char *z = mprintf("%#h", nStr, zStr);
3063
+ zDisp = "off";
31033064
cgi_reset_content();
31043065
style_submenu_enable(0);
31053066
style_set_current_feature("error");
31063067
style_header("Configuration Error");
31073068
@ <p>Error in a TH1 configuration script:
31083069
--- src/th_main.c
+++ src/th_main.c
@@ -31,13 +31,11 @@
31 #define TH_INIT_NEED_CONFIG ((u32)0x00000001) /* Open configuration first? */
32 #define TH_INIT_FORCE_TCL ((u32)0x00000002) /* Force Tcl to be enabled? */
33 #define TH_INIT_FORCE_RESET ((u32)0x00000004) /* Force TH1 commands re-added? */
34 #define TH_INIT_FORCE_SETUP ((u32)0x00000008) /* Force eval of setup script? */
35 #define TH_INIT_NO_REPO ((u32)0x00000010) /* Skip opening repository. */
36 #define TH_INIT_NO_ENCODE ((u32)0x00000020) /* Do not html-encode sendText()*/
37 /* output. */
38 #define TH_INIT_MASK ((u32)0x0000003F) /* All possible init flags. */
39
40 /*
41 ** Useful and/or "well-known" combinations of flag values.
42 */
43 #define TH_INIT_DEFAULT (TH_INIT_NONE) /* Default flags. */
@@ -297,50 +295,10 @@
297 TH1_LEN(argl[1]),argv[1],enableOutput);
298 }
299 return rc;
300 }
301
302 /*
303 ** TH1 command: enable_htmlify ?BOOLEAN?
304 **
305 ** Enable or disable the HTML escaping done by all output which
306 ** originates from TH1 (via sendText()).
307 **
308 ** If passed no arguments it instead returns 0 or 1 to indicate the
309 ** current state.
310 */
311 static int enableHtmlifyCmd(
312 Th_Interp *interp,
313 void *p,
314 int argc,
315 const char **argv,
316 int *argl
317 ){
318 int rc = 0, buul;
319 if( argc>3 ){
320 return Th_WrongNumArgs(interp,
321 "enable_htmlify [TRACE_LABEL] ?BOOLEAN?");
322 }
323 buul = (TH_INIT_NO_ENCODE & g.th1Flags) ? 0 : 1;
324 Th_SetResultInt(g.interp, buul);
325 if(argc>1){
326 if( g.thTrace ){
327 Th_Trace("enable_htmlify {%.*s} -> %d<br>\n",
328 TH1_LEN(argl[1]),argv[1],buul);
329 }
330 rc = Th_ToInt(interp, argv[argc-1], argl[argc-1], &buul);
331 if(!rc){
332 if(buul){
333 g.th1Flags &= ~TH_INIT_NO_ENCODE;
334 }else{
335 g.th1Flags |= TH_INIT_NO_ENCODE;
336 }
337 }
338 }
339 return rc;
340 }
341
342 /*
343 ** Returns a name for a TH1 return code.
344 */
345 const char *Th_ReturnCodeName(int rc, int nullIfOk){
346 static char zRc[32];
@@ -376,23 +334,19 @@
376
377 /*
378 ** Send text to the appropriate output: If pOut is not NULL, it is
379 ** appended there, else to the console or to the CGI reply buffer.
380 ** Escape all characters with special meaning to HTML if the encode
381 ** parameter is true, with the exception that that flag is ignored if
382 ** g.th1Flags has the TH_INIT_NO_ENCODE flag.
383 **
384 ** If pOut is NULL and the global pThOut is not then that blob
385 ** is used for output.
386 */
387 static void sendText(Blob *pOut, const char *z, int n, int encode){
388 if(0==pOut && pThOut!=0){
389 pOut = pThOut;
390 }
391 if(TH_INIT_NO_ENCODE & g.th1Flags){
392 encode = 0;
393 }
394 if( enableOutput && n ){
395 if( n<0 ){
396 n = strlen(z);
397 }else{
398 n = TH1_LEN(n);
@@ -540,13 +494,12 @@
540 n = argl[1];
541 if( encode==0 && n>0 && TH1_TAINTED(n) ){
542 if( Th_ReportTaint(interp, "output string", argv[1], n) ){
543 return TH_ERROR;
544 }
545 n = TH1_LEN(n);
546 }
547 sendText(0,(char*)argv[1], n, encode);
548 return TH_OK;
549 }
550
551 /*
552 ** TH1 command: redirect URL ?withMethod?
@@ -1450,17 +1403,21 @@
1450 int argc,
1451 const char **argv,
1452 int *argl
1453 ){
1454 const char *zDefault = 0;
 
 
1455 if( argc!=2 && argc!=3 ){
1456 return Th_WrongNumArgs(interp, "getParameter NAME ?DEFAULT?");
1457 }
1458 if( argc==3 ){
1459 zDefault = argv[2];
1460 }
1461 Th_SetResult(interp, cgi_parameter(argv[1], zDefault), -1);
 
 
1462 return TH_OK;
1463 }
1464
1465 /*
1466 ** TH1 command: setParameter NAME VALUE
@@ -2426,11 +2383,10 @@
2426 {"copybtn", copybtnCmd, 0},
2427 {"date", dateCmd, 0},
2428 {"decorate", wikiCmd, (void*)&aFlags[2]},
2429 {"defHeader", defHeaderCmd, 0},
2430 {"dir", dirCmd, 0},
2431 {"enable_htmlify",enableHtmlifyCmd, 0},
2432 {"enable_output", enableOutputCmd, 0},
2433 {"encode64", encode64Cmd, 0},
2434 {"getParameter", getParameterCmd, 0},
2435 {"glob_match", globMatchCmd, 0},
2436 {"globalState", globalStateCmd, 0},
@@ -2984,11 +2940,16 @@
2984 }
2985 rc = Th_GetVar(g.interp, (char*)zVar, nVar);
2986 z += i+1+n;
2987 i = 0;
2988 zResult = (char*)Th_GetResult(g.interp, &n);
2989 sendText(pOut,(char*)zResult, n, encode);
 
 
 
 
 
2990 }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){
2991 sendText(pOut,z, i, 0);
2992 z += i+5;
2993 for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){}
2994 if( g.thTrace ){
@@ -3039,12 +3000,11 @@
3039 ** e.g. via the "render" script function binding, need to use the
3040 ** pThOut blob in order to avoid out-of-order output if
3041 ** Th_SetOutputBlob() has been called. If it has not been called,
3042 ** pThOut will be 0, which will redirect the output to CGI/stdout,
3043 ** as appropriate. We need to pass on g.th1Flags for the case of
3044 ** recursive calls, so that, e.g., TH_INIT_NO_ENCODE does not get
3045 ** inadvertently toggled off by a recursive call.
3046 */;
3047 }
3048
3049 /*
3050 ** SETTING: vuln-report width=8 default=log
@@ -3076,22 +3036,22 @@
3076 Th_Interp *interp, /* Report error here, if an error is reported */
3077 const char *zWhere, /* Where the tainted string appears */
3078 const char *zStr, /* The tainted string */
3079 int nStr /* Length of the tainted string */
3080 ){
3081 char *zDisp; /* Dispensation */
3082 const char *zVulnType; /* Type of vulnerability */
3083
3084 zDisp = db_get("vuln-report","log");
3085 if( is_false(zDisp) ) return 0;
3086 if( strstr(zWhere,"SQL")!=0 ){
3087 zVulnType = "SQL-injection";
3088 }else{
3089 zVulnType = "XSS";
3090 }
3091 nStr = TH1_LEN(nStr);
3092 fossil_errorlog("possible %s vulnerability due to tainted TH1 %s: \"%.*s\"",
3093 zVulnType, zWhere, nStr, zStr);
3094 if( strcmp(zDisp,"log")==0 ){
3095 return 0;
3096 }
3097 if( strcmp(zDisp,"block")==0 ){
@@ -3098,10 +3058,11 @@
3098 char *z = mprintf("tainted %s: \"", zWhere);
3099 Th_ErrorMessage(interp, z, zStr, nStr);
3100 fossil_free(z);
3101 }else{
3102 char *z = mprintf("%#h", nStr, zStr);
 
3103 cgi_reset_content();
3104 style_submenu_enable(0);
3105 style_set_current_feature("error");
3106 style_header("Configuration Error");
3107 @ <p>Error in a TH1 configuration script:
3108
--- src/th_main.c
+++ src/th_main.c
@@ -31,13 +31,11 @@
31 #define TH_INIT_NEED_CONFIG ((u32)0x00000001) /* Open configuration first? */
32 #define TH_INIT_FORCE_TCL ((u32)0x00000002) /* Force Tcl to be enabled? */
33 #define TH_INIT_FORCE_RESET ((u32)0x00000004) /* Force TH1 commands re-added? */
34 #define TH_INIT_FORCE_SETUP ((u32)0x00000008) /* Force eval of setup script? */
35 #define TH_INIT_NO_REPO ((u32)0x00000010) /* Skip opening repository. */
36 #define TH_INIT_MASK ((u32)0x0000001F) /* All possible init flags. */
 
 
37
38 /*
39 ** Useful and/or "well-known" combinations of flag values.
40 */
41 #define TH_INIT_DEFAULT (TH_INIT_NONE) /* Default flags. */
@@ -297,50 +295,10 @@
295 TH1_LEN(argl[1]),argv[1],enableOutput);
296 }
297 return rc;
298 }
299
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
300 /*
301 ** Returns a name for a TH1 return code.
302 */
303 const char *Th_ReturnCodeName(int rc, int nullIfOk){
304 static char zRc[32];
@@ -376,23 +334,19 @@
334
335 /*
336 ** Send text to the appropriate output: If pOut is not NULL, it is
337 ** appended there, else to the console or to the CGI reply buffer.
338 ** Escape all characters with special meaning to HTML if the encode
339 ** parameter is true.
 
340 **
341 ** If pOut is NULL and the global pThOut is not then that blob
342 ** is used for output.
343 */
344 static void sendText(Blob *pOut, const char *z, int n, int encode){
345 if(0==pOut && pThOut!=0){
346 pOut = pThOut;
347 }
 
 
 
348 if( enableOutput && n ){
349 if( n<0 ){
350 n = strlen(z);
351 }else{
352 n = TH1_LEN(n);
@@ -540,13 +494,12 @@
494 n = argl[1];
495 if( encode==0 && n>0 && TH1_TAINTED(n) ){
496 if( Th_ReportTaint(interp, "output string", argv[1], n) ){
497 return TH_ERROR;
498 }
 
499 }
500 sendText(0,(char*)argv[1], TH1_LEN(n), encode);
501 return TH_OK;
502 }
503
504 /*
505 ** TH1 command: redirect URL ?withMethod?
@@ -1450,17 +1403,21 @@
1403 int argc,
1404 const char **argv,
1405 int *argl
1406 ){
1407 const char *zDefault = 0;
1408 const char *zVal;
1409 int sz;
1410 if( argc!=2 && argc!=3 ){
1411 return Th_WrongNumArgs(interp, "getParameter NAME ?DEFAULT?");
1412 }
1413 if( argc==3 ){
1414 zDefault = argv[2];
1415 }
1416 zVal = cgi_parameter(argv[1], zDefault);
1417 sz = th_strlen(zVal);
1418 Th_SetResult(interp, zVal, TH1_ADD_TAINT(sz));
1419 return TH_OK;
1420 }
1421
1422 /*
1423 ** TH1 command: setParameter NAME VALUE
@@ -2426,11 +2383,10 @@
2383 {"copybtn", copybtnCmd, 0},
2384 {"date", dateCmd, 0},
2385 {"decorate", wikiCmd, (void*)&aFlags[2]},
2386 {"defHeader", defHeaderCmd, 0},
2387 {"dir", dirCmd, 0},
 
2388 {"enable_output", enableOutputCmd, 0},
2389 {"encode64", encode64Cmd, 0},
2390 {"getParameter", getParameterCmd, 0},
2391 {"glob_match", globMatchCmd, 0},
2392 {"globalState", globalStateCmd, 0},
@@ -2984,11 +2940,16 @@
2940 }
2941 rc = Th_GetVar(g.interp, (char*)zVar, nVar);
2942 z += i+1+n;
2943 i = 0;
2944 zResult = (char*)Th_GetResult(g.interp, &n);
2945 if( !TH1_TAINTED(n)
2946 || encode
2947 || Th_ReportTaint(g.interp, "inline variable", zVar, nVar)==TH_OK
2948 ){
2949 sendText(pOut,(char*)zResult, n, encode);
2950 }
2951 }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){
2952 sendText(pOut,z, i, 0);
2953 z += i+5;
2954 for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){}
2955 if( g.thTrace ){
@@ -3039,12 +3000,11 @@
3000 ** e.g. via the "render" script function binding, need to use the
3001 ** pThOut blob in order to avoid out-of-order output if
3002 ** Th_SetOutputBlob() has been called. If it has not been called,
3003 ** pThOut will be 0, which will redirect the output to CGI/stdout,
3004 ** as appropriate. We need to pass on g.th1Flags for the case of
3005 ** recursive calls.
 
3006 */;
3007 }
3008
3009 /*
3010 ** SETTING: vuln-report width=8 default=log
@@ -3076,22 +3036,22 @@
3036 Th_Interp *interp, /* Report error here, if an error is reported */
3037 const char *zWhere, /* Where the tainted string appears */
3038 const char *zStr, /* The tainted string */
3039 int nStr /* Length of the tainted string */
3040 ){
3041 static const char *zDisp = 0; /* Dispensation; what to do with the error */
3042 const char *zVulnType; /* Type of vulnerability */
3043
3044 if( zDisp==0 ) zDisp = db_get("vuln-report","log");
3045 if( is_false(zDisp) ) return 0;
3046 if( strstr(zWhere,"SQL")!=0 ){
3047 zVulnType = "SQL-injection";
3048 }else{
3049 zVulnType = "XSS";
3050 }
3051 nStr = TH1_LEN(nStr);
3052 fossil_errorlog("possible TH1 %s vulnerability due to tainted %s: \"%.*s\"",
3053 zVulnType, zWhere, nStr, zStr);
3054 if( strcmp(zDisp,"log")==0 ){
3055 return 0;
3056 }
3057 if( strcmp(zDisp,"block")==0 ){
@@ -3098,10 +3058,11 @@
3058 char *z = mprintf("tainted %s: \"", zWhere);
3059 Th_ErrorMessage(interp, z, zStr, nStr);
3060 fossil_free(z);
3061 }else{
3062 char *z = mprintf("%#h", nStr, zStr);
3063 zDisp = "off";
3064 cgi_reset_content();
3065 style_submenu_enable(0);
3066 style_set_current_feature("error");
3067 style_header("Configuration Error");
3068 @ <p>Error in a TH1 configuration script:
3069
--- src/th_tcl.c
+++ src/th_tcl.c
@@ -24,10 +24,14 @@
2424
2525
#include "sqlite3.h"
2626
#include "th.h"
2727
#include "tcl.h"
2828
29
+#if TCL_MAJOR_VERSION<9 && !defined(Tcl_Size)
30
+# define Tcl_Size int
31
+#endif
32
+
2933
/*
3034
** This macro is used to verify that the header version of Tcl meets some
3135
** minimum requirement.
3236
*/
3337
#define MINIMUM_TCL_VERSION(major, minor) \
@@ -183,11 +187,15 @@
183187
** the only Tcl API functions that MUST be called prior to being able to call
184188
** Tcl_InitStubs (i.e. because it requires a Tcl interpreter). For complete
185189
** cleanup if the Tcl stubs initialization fails somehow, the Tcl_DeleteInterp
186190
** and Tcl_Finalize function types are also required.
187191
*/
192
+#if TCL_MAJOR_VERSION>=9
188193
typedef const char *(tcl_FindExecutableProc) (const char *);
194
+#else
195
+typedef void (tcl_FindExecutableProc) (const char *);
196
+#endif
189197
typedef Tcl_Interp *(tcl_CreateInterpProc) (void);
190198
typedef void (tcl_DeleteInterpProc) (Tcl_Interp *);
191199
typedef void (tcl_FinalizeProc) (void);
192200
193201
/*
194202
--- src/th_tcl.c
+++ src/th_tcl.c
@@ -24,10 +24,14 @@
24
25 #include "sqlite3.h"
26 #include "th.h"
27 #include "tcl.h"
28
 
 
 
 
29 /*
30 ** This macro is used to verify that the header version of Tcl meets some
31 ** minimum requirement.
32 */
33 #define MINIMUM_TCL_VERSION(major, minor) \
@@ -183,11 +187,15 @@
183 ** the only Tcl API functions that MUST be called prior to being able to call
184 ** Tcl_InitStubs (i.e. because it requires a Tcl interpreter). For complete
185 ** cleanup if the Tcl stubs initialization fails somehow, the Tcl_DeleteInterp
186 ** and Tcl_Finalize function types are also required.
187 */
 
188 typedef const char *(tcl_FindExecutableProc) (const char *);
 
 
 
189 typedef Tcl_Interp *(tcl_CreateInterpProc) (void);
190 typedef void (tcl_DeleteInterpProc) (Tcl_Interp *);
191 typedef void (tcl_FinalizeProc) (void);
192
193 /*
194
--- src/th_tcl.c
+++ src/th_tcl.c
@@ -24,10 +24,14 @@
24
25 #include "sqlite3.h"
26 #include "th.h"
27 #include "tcl.h"
28
29 #if TCL_MAJOR_VERSION<9 && !defined(Tcl_Size)
30 # define Tcl_Size int
31 #endif
32
33 /*
34 ** This macro is used to verify that the header version of Tcl meets some
35 ** minimum requirement.
36 */
37 #define MINIMUM_TCL_VERSION(major, minor) \
@@ -183,11 +187,15 @@
187 ** the only Tcl API functions that MUST be called prior to being able to call
188 ** Tcl_InitStubs (i.e. because it requires a Tcl interpreter). For complete
189 ** cleanup if the Tcl stubs initialization fails somehow, the Tcl_DeleteInterp
190 ** and Tcl_Finalize function types are also required.
191 */
192 #if TCL_MAJOR_VERSION>=9
193 typedef const char *(tcl_FindExecutableProc) (const char *);
194 #else
195 typedef void (tcl_FindExecutableProc) (const char *);
196 #endif
197 typedef Tcl_Interp *(tcl_CreateInterpProc) (void);
198 typedef void (tcl_DeleteInterpProc) (Tcl_Interp *);
199 typedef void (tcl_FinalizeProc) (void);
200
201 /*
202
+12 -6
--- src/timeline.c
+++ src/timeline.c
@@ -2350,15 +2350,15 @@
23502350
}
23512351
if( bSeparateDandP ){
23522352
int n = db_int(0, "SELECT count(*) FROM ok");
23532353
blob_reset(&desc);
23542354
blob_appendf(&desc,
2355
- "%d check-ins that are both ancestors of %z%h</a>"
2356
- " and descendants of %z%h</a>",
2355
+ "%d check-ins that are derived from %z%h</a>"
2356
+ " and contribute to %z%h</a>",
23572357
n,
2358
- href("%R/info?name=%h",zDPNameP),zDPNameP,
2359
- href("%R/info?name=%h",zDPNameD),zDPNameD
2358
+ href("%R/info?name=%h",zDPNameD),zDPNameD,
2359
+ href("%R/info?name=%h",zDPNameP),zDPNameP
23602360
);
23612361
ridBackTo = 0;
23622362
ridFwdTo = 0;
23632363
}else{
23642364
blob_appendf(&desc, " of %z%h</a>",
@@ -3816,13 +3816,19 @@
38163816
" AND (tagxref.value IS NULL OR tagxref.value='%q')",
38173817
zBr, zBr, zBr, TAG_BRANCH, zBr, zBr);
38183818
}
38193819
38203820
if( mode==TIMELINE_MODE_AFTER ){
3821
+ int lim = n;
3822
+ if( n == 0 ){
3823
+ lim = -1; /* 0 means no limit */
3824
+ }else if( n < 0 ){
3825
+ lim = -n;
3826
+ }
38213827
/* Complete the above outer select. */
38223828
blob_append_sql(&sql,
3823
- "\nORDER BY event.mtime LIMIT abs(%d)) t ORDER BY t.mDateTime DESC;", n);
3829
+ "\nORDER BY event.mtime LIMIT %d) t ORDER BY t.mDateTime DESC;", lim);
38243830
}else{
38253831
blob_append_sql(&sql, "\nORDER BY event.mtime DESC");
38263832
}
38273833
if( iOffset>0 ){
38283834
/* Don't handle LIMIT here, otherwise print_timeline()
@@ -3847,11 +3853,11 @@
38473853
** Query parameters:
38483854
**
38493855
** today=DATE Use DATE as today's date
38503856
*/
38513857
void thisdayinhistory_page(void){
3852
- static int aYearsAgo[] = { 1, 2, 3, 4, 5, 10, 15, 20, 30, 40, 50, 75, 100 };
3858
+ static int aYearsAgo[] = { 1,2,3,4,5,10,15,20,25,30,40,50,75,100 };
38533859
const char *zToday;
38543860
char *zStartOfProject;
38553861
int i;
38563862
Stmt q;
38573863
char *z;
38583864
--- src/timeline.c
+++ src/timeline.c
@@ -2350,15 +2350,15 @@
2350 }
2351 if( bSeparateDandP ){
2352 int n = db_int(0, "SELECT count(*) FROM ok");
2353 blob_reset(&desc);
2354 blob_appendf(&desc,
2355 "%d check-ins that are both ancestors of %z%h</a>"
2356 " and descendants of %z%h</a>",
2357 n,
2358 href("%R/info?name=%h",zDPNameP),zDPNameP,
2359 href("%R/info?name=%h",zDPNameD),zDPNameD
2360 );
2361 ridBackTo = 0;
2362 ridFwdTo = 0;
2363 }else{
2364 blob_appendf(&desc, " of %z%h</a>",
@@ -3816,13 +3816,19 @@
3816 " AND (tagxref.value IS NULL OR tagxref.value='%q')",
3817 zBr, zBr, zBr, TAG_BRANCH, zBr, zBr);
3818 }
3819
3820 if( mode==TIMELINE_MODE_AFTER ){
 
 
 
 
 
 
3821 /* Complete the above outer select. */
3822 blob_append_sql(&sql,
3823 "\nORDER BY event.mtime LIMIT abs(%d)) t ORDER BY t.mDateTime DESC;", n);
3824 }else{
3825 blob_append_sql(&sql, "\nORDER BY event.mtime DESC");
3826 }
3827 if( iOffset>0 ){
3828 /* Don't handle LIMIT here, otherwise print_timeline()
@@ -3847,11 +3853,11 @@
3847 ** Query parameters:
3848 **
3849 ** today=DATE Use DATE as today's date
3850 */
3851 void thisdayinhistory_page(void){
3852 static int aYearsAgo[] = { 1, 2, 3, 4, 5, 10, 15, 20, 30, 40, 50, 75, 100 };
3853 const char *zToday;
3854 char *zStartOfProject;
3855 int i;
3856 Stmt q;
3857 char *z;
3858
--- src/timeline.c
+++ src/timeline.c
@@ -2350,15 +2350,15 @@
2350 }
2351 if( bSeparateDandP ){
2352 int n = db_int(0, "SELECT count(*) FROM ok");
2353 blob_reset(&desc);
2354 blob_appendf(&desc,
2355 "%d check-ins that are derived from %z%h</a>"
2356 " and contribute to %z%h</a>",
2357 n,
2358 href("%R/info?name=%h",zDPNameD),zDPNameD,
2359 href("%R/info?name=%h",zDPNameP),zDPNameP
2360 );
2361 ridBackTo = 0;
2362 ridFwdTo = 0;
2363 }else{
2364 blob_appendf(&desc, " of %z%h</a>",
@@ -3816,13 +3816,19 @@
3816 " AND (tagxref.value IS NULL OR tagxref.value='%q')",
3817 zBr, zBr, zBr, TAG_BRANCH, zBr, zBr);
3818 }
3819
3820 if( mode==TIMELINE_MODE_AFTER ){
3821 int lim = n;
3822 if( n == 0 ){
3823 lim = -1; /* 0 means no limit */
3824 }else if( n < 0 ){
3825 lim = -n;
3826 }
3827 /* Complete the above outer select. */
3828 blob_append_sql(&sql,
3829 "\nORDER BY event.mtime LIMIT %d) t ORDER BY t.mDateTime DESC;", lim);
3830 }else{
3831 blob_append_sql(&sql, "\nORDER BY event.mtime DESC");
3832 }
3833 if( iOffset>0 ){
3834 /* Don't handle LIMIT here, otherwise print_timeline()
@@ -3847,11 +3853,11 @@
3853 ** Query parameters:
3854 **
3855 ** today=DATE Use DATE as today's date
3856 */
3857 void thisdayinhistory_page(void){
3858 static int aYearsAgo[] = { 1,2,3,4,5,10,15,20,25,30,40,50,75,100 };
3859 const char *zToday;
3860 char *zStartOfProject;
3861 int i;
3862 Stmt q;
3863 char *z;
3864
+4 -6
--- src/tkt.c
+++ src/tkt.c
@@ -188,17 +188,19 @@
188188
*/
189189
static void initializeVariablesFromDb(void){
190190
const char *zName;
191191
Stmt q;
192192
int i, n, size, j;
193
+ const char *zCTimeColumn = haveTicketCTime ? "tkt_ctime" : "tkt_mtime";
193194
194195
zName = PD("name","-none-");
195196
db_prepare(&q, "SELECT datetime(tkt_mtime,toLocal()) AS tkt_datetime, "
196
- "datetime(tkt_ctime,toLocal()) AS tkt_datetime_creation, "
197
+ "datetime(%s,toLocal()) AS tkt_datetime_creation, "
197198
"julianday('now') - tkt_mtime, "
198
- "julianday('now') - tkt_ctime, *"
199
+ "julianday('now') - %s, *"
199200
" FROM ticket WHERE tkt_uuid GLOB '%q*'",
201
+ zCTimeColumn/*safe-for-%s*/, zCTimeColumn/*safe-for-%s*/,
200202
zName);
201203
if( db_step(&q)==SQLITE_ROW ){
202204
n = db_column_count(&q);
203205
for(i=0; i<n; i++){
204206
const char *zVal = db_column_text(&q, i);
@@ -774,14 +776,10 @@
774776
style_submenu_element("Timeline", "%R/info/%T", zUuid);
775777
}
776778
zFullName = db_text(0,
777779
"SELECT tkt_uuid FROM ticket"
778780
" WHERE tkt_uuid GLOB '%q*'", zUuid);
779
- if( g.perm.WrWiki && g.perm.WrTkt ){
780
- style_submenu_element("Edit Description",
781
- "%R/wikiedit?name=ticket/%T", zFullName);
782
- }
783781
if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br>\n", -1);
784782
ticket_init();
785783
initializeVariablesFromCGI();
786784
getAllTicketFields();
787785
initializeVariablesFromDb();
788786
--- src/tkt.c
+++ src/tkt.c
@@ -188,17 +188,19 @@
188 */
189 static void initializeVariablesFromDb(void){
190 const char *zName;
191 Stmt q;
192 int i, n, size, j;
 
193
194 zName = PD("name","-none-");
195 db_prepare(&q, "SELECT datetime(tkt_mtime,toLocal()) AS tkt_datetime, "
196 "datetime(tkt_ctime,toLocal()) AS tkt_datetime_creation, "
197 "julianday('now') - tkt_mtime, "
198 "julianday('now') - tkt_ctime, *"
199 " FROM ticket WHERE tkt_uuid GLOB '%q*'",
 
200 zName);
201 if( db_step(&q)==SQLITE_ROW ){
202 n = db_column_count(&q);
203 for(i=0; i<n; i++){
204 const char *zVal = db_column_text(&q, i);
@@ -774,14 +776,10 @@
774 style_submenu_element("Timeline", "%R/info/%T", zUuid);
775 }
776 zFullName = db_text(0,
777 "SELECT tkt_uuid FROM ticket"
778 " WHERE tkt_uuid GLOB '%q*'", zUuid);
779 if( g.perm.WrWiki && g.perm.WrTkt ){
780 style_submenu_element("Edit Description",
781 "%R/wikiedit?name=ticket/%T", zFullName);
782 }
783 if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br>\n", -1);
784 ticket_init();
785 initializeVariablesFromCGI();
786 getAllTicketFields();
787 initializeVariablesFromDb();
788
--- src/tkt.c
+++ src/tkt.c
@@ -188,17 +188,19 @@
188 */
189 static void initializeVariablesFromDb(void){
190 const char *zName;
191 Stmt q;
192 int i, n, size, j;
193 const char *zCTimeColumn = haveTicketCTime ? "tkt_ctime" : "tkt_mtime";
194
195 zName = PD("name","-none-");
196 db_prepare(&q, "SELECT datetime(tkt_mtime,toLocal()) AS tkt_datetime, "
197 "datetime(%s,toLocal()) AS tkt_datetime_creation, "
198 "julianday('now') - tkt_mtime, "
199 "julianday('now') - %s, *"
200 " FROM ticket WHERE tkt_uuid GLOB '%q*'",
201 zCTimeColumn/*safe-for-%s*/, zCTimeColumn/*safe-for-%s*/,
202 zName);
203 if( db_step(&q)==SQLITE_ROW ){
204 n = db_column_count(&q);
205 for(i=0; i<n; i++){
206 const char *zVal = db_column_text(&q, i);
@@ -774,14 +776,10 @@
776 style_submenu_element("Timeline", "%R/info/%T", zUuid);
777 }
778 zFullName = db_text(0,
779 "SELECT tkt_uuid FROM ticket"
780 " WHERE tkt_uuid GLOB '%q*'", zUuid);
 
 
 
 
781 if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br>\n", -1);
782 ticket_init();
783 initializeVariablesFromCGI();
784 getAllTicketFields();
785 initializeVariablesFromDb();
786
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -496,10 +496,13 @@
496496
@ }
497497
@
498498
@ if {[capexpr {n}]} {
499499
@ submenu link "Copy Ticket" /tktnew/$tkt_uuid
500500
@ }
501
+@ if {[capexpr {nk}]} {
502
+@ submenu link "Edit Wiki" /wikiedit?name=ticket/$tkt_uuid
503
+@ }
501504
@ </th1>
502505
@ <tr><td class="tktDspLabel">Title:</td>
503506
@ <td class="tktDspValue" colspan="3">
504507
@ $<title>
505508
@ </td></tr>
506509
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -496,10 +496,13 @@
496 @ }
497 @
498 @ if {[capexpr {n}]} {
499 @ submenu link "Copy Ticket" /tktnew/$tkt_uuid
500 @ }
 
 
 
501 @ </th1>
502 @ <tr><td class="tktDspLabel">Title:</td>
503 @ <td class="tktDspValue" colspan="3">
504 @ $<title>
505 @ </td></tr>
506
--- src/tktsetup.c
+++ src/tktsetup.c
@@ -496,10 +496,13 @@
496 @ }
497 @
498 @ if {[capexpr {n}]} {
499 @ submenu link "Copy Ticket" /tktnew/$tkt_uuid
500 @ }
501 @ if {[capexpr {nk}]} {
502 @ submenu link "Edit Wiki" /wikiedit?name=ticket/$tkt_uuid
503 @ }
504 @ </th1>
505 @ <tr><td class="tktDspLabel">Title:</td>
506 @ <td class="tktDspValue" colspan="3">
507 @ $<title>
508 @ </td></tr>
509
+79 -41
--- src/user.c
+++ src/user.c
@@ -326,14 +326,21 @@
326326
**
327327
** > fossil user contact USERNAME ?CONTACT-INFO?
328328
**
329329
** Query or set contact information for user USERNAME
330330
**
331
-** > fossil user default ?USERNAME?
331
+** > fossil user default ?OPTIONS? ?USERNAME?
332332
**
333333
** Query or set the default user. The default user is the
334
-** user for command-line interaction.
334
+** user for command-line interaction. If USERNAME is an
335
+** empty string, then the default user is unset from the
336
+** repository and will subsequently be determined by the -U
337
+** command-line option or by environment variables
338
+** FOSSIL_USER, USER, LOGNAME, or USERNAME, in that order.
339
+** OPTIONS:
340
+**
341
+** -v|--verbose Show how the default user is computed
335342
**
336343
** > fossil user list | ls
337344
**
338345
** List all users known to the repository
339346
**
@@ -385,21 +392,50 @@
385392
&login, zPw, &caps, &contact
386393
);
387394
db_protect_pop();
388395
free(zPw);
389396
}else if( n>=2 && strncmp(g.argv[2],"default",n)==0 ){
390
- if( g.argc==3 ){
391
- user_select();
392
- fossil_print("%s\n", g.zLogin);
393
- }else{
394
- if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.argv[3]) ){
395
- fossil_fatal("no such user: %s", g.argv[3]);
396
- }
397
- if( g.localOpen ){
398
- db_lset("default-user", g.argv[3]);
399
- }else{
400
- db_set("default-user", g.argv[3], 0);
397
+ int eVerbose = find_option("verbose","v",0)!=0;
398
+ verify_all_options();
399
+ if( g.argc>3 ){
400
+ const char *zUser = g.argv[3];
401
+ if( fossil_strcmp(zUser,"")==0 || fossil_stricmp(zUser,"nobody")==0 ){
402
+ db_begin_transaction();
403
+ if( g.localOpen ){
404
+ db_multi_exec("DELETE FROM vvar WHERE name='default-user'");
405
+ }
406
+ db_unset("default-user",0);
407
+ db_commit_transaction();
408
+ }else{
409
+ if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.argv[3]) ){
410
+ fossil_fatal("no such user: %s", g.argv[3]);
411
+ }
412
+ if( g.localOpen ){
413
+ db_lset("default-user", g.argv[3]);
414
+ }else{
415
+ db_set("default-user", g.argv[3], 0);
416
+ }
417
+ }
418
+ }
419
+ if( g.argc==3 || eVerbose ){
420
+ int eHow = user_select();
421
+ const char *zHow = "???";
422
+ switch( eHow ){
423
+ case 1: zHow = "-U option"; break;
424
+ case 2: zHow = "previously set"; break;
425
+ case 3: zHow = "local check-out"; break;
426
+ case 4: zHow = "repository"; break;
427
+ case 5: zHow = "FOSSIL_USER"; break;
428
+ case 6: zHow = "USER"; break;
429
+ case 7: zHow = "LOGNAME"; break;
430
+ case 8: zHow = "USERNAME"; break;
431
+ case 9: zHow = "URL"; break;
432
+ }
433
+ if( eVerbose ){
434
+ fossil_print("%s (determined by %s)\n", g.zLogin, zHow);
435
+ }else{
436
+ fossil_print("%s\n", g.zLogin);
401437
}
402438
}
403439
}else if(( n>=2 && strncmp(g.argv[2],"list",n)==0 ) ||
404440
( n>=2 && strncmp(g.argv[2],"ls",n)==0 )){
405441
Stmt q;
@@ -496,52 +532,54 @@
496532
/*
497533
** Figure out what user is at the controls.
498534
**
499535
** (1) Use the --user and -U command-line options.
500536
**
501
-** (2) If the local database is open, check in VVAR.
502
-**
503
-** (3) Check the default user in the repository
504
-**
505
-** (4) Try the FOSSIL_USER environment variable.
506
-**
507
-** (5) Try the USER environment variable.
508
-**
509
-** (6) Try the LOGNAME environment variable.
510
-**
511
-** (7) Try the USERNAME environment variable.
512
-**
513
-** (8) Check if the user can be extracted from the remote URL.
537
+** (2) The name used for login (if there was a login).
538
+**
539
+** (3) If the local database is open, check in VVAR.
540
+**
541
+** (4) Check the default-user in the repository
542
+**
543
+** (5) Try the FOSSIL_USER environment variable.
544
+**
545
+** (6) Try the USER environment variable.
546
+**
547
+** (7) Try the LOGNAME environment variable.
548
+**
549
+** (8) Try the USERNAME environment variable.
550
+**
551
+** (9) Check if the user can be extracted from the remote URL.
514552
**
515553
** The user name is stored in g.zLogin. The uid is in g.userUid.
516554
*/
517
-void user_select(void){
555
+int user_select(void){
518556
UrlData url;
519
- if( g.userUid ) return;
557
+ if( g.userUid ) return 1;
520558
if( g.zLogin ){
521559
if( attempt_user(g.zLogin)==0 ){
522560
fossil_fatal("no such user: %s", g.zLogin);
523561
}else{
524
- return;
562
+ return 2;
525563
}
526564
}
527565
528
- if( g.localOpen && attempt_user(db_lget("default-user",0)) ) return;
529
-
530
- if( attempt_user(db_get("default-user", 0)) ) return;
531
-
532
- if( attempt_user(fossil_getenv("FOSSIL_USER")) ) return;
533
-
534
- if( attempt_user(fossil_getenv("USER")) ) return;
535
-
536
- if( attempt_user(fossil_getenv("LOGNAME")) ) return;
537
-
538
- if( attempt_user(fossil_getenv("USERNAME")) ) return;
566
+ if( g.localOpen && attempt_user(db_lget("default-user",0)) ) return 3;
567
+
568
+ if( attempt_user(db_get("default-user", 0)) ) return 4;
569
+
570
+ if( attempt_user(fossil_getenv("FOSSIL_USER")) ) return 5;
571
+
572
+ if( attempt_user(fossil_getenv("USER")) ) return 6;
573
+
574
+ if( attempt_user(fossil_getenv("LOGNAME")) ) return 7;
575
+
576
+ if( attempt_user(fossil_getenv("USERNAME")) ) return 8;
539577
540578
memset(&url, 0, sizeof(url));
541579
url_parse_local(0, URL_USE_CONFIG, &url);
542
- if( url.user && attempt_user(url.user) ) return;
580
+ if( url.user && attempt_user(url.user) ) return 9;
543581
544582
fossil_print(
545583
"Cannot figure out who you are! Consider using the --user\n"
546584
"command line option, setting your USER environment variable,\n"
547585
"or setting a default user with \"fossil user default USER\".\n"
548586
--- src/user.c
+++ src/user.c
@@ -326,14 +326,21 @@
326 **
327 ** > fossil user contact USERNAME ?CONTACT-INFO?
328 **
329 ** Query or set contact information for user USERNAME
330 **
331 ** > fossil user default ?USERNAME?
332 **
333 ** Query or set the default user. The default user is the
334 ** user for command-line interaction.
 
 
 
 
 
 
 
335 **
336 ** > fossil user list | ls
337 **
338 ** List all users known to the repository
339 **
@@ -385,21 +392,50 @@
385 &login, zPw, &caps, &contact
386 );
387 db_protect_pop();
388 free(zPw);
389 }else if( n>=2 && strncmp(g.argv[2],"default",n)==0 ){
390 if( g.argc==3 ){
391 user_select();
392 fossil_print("%s\n", g.zLogin);
393 }else{
394 if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.argv[3]) ){
395 fossil_fatal("no such user: %s", g.argv[3]);
396 }
397 if( g.localOpen ){
398 db_lset("default-user", g.argv[3]);
399 }else{
400 db_set("default-user", g.argv[3], 0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
401 }
402 }
403 }else if(( n>=2 && strncmp(g.argv[2],"list",n)==0 ) ||
404 ( n>=2 && strncmp(g.argv[2],"ls",n)==0 )){
405 Stmt q;
@@ -496,52 +532,54 @@
496 /*
497 ** Figure out what user is at the controls.
498 **
499 ** (1) Use the --user and -U command-line options.
500 **
501 ** (2) If the local database is open, check in VVAR.
502 **
503 ** (3) Check the default user in the repository
504 **
505 ** (4) Try the FOSSIL_USER environment variable.
506 **
507 ** (5) Try the USER environment variable.
508 **
509 ** (6) Try the LOGNAME environment variable.
510 **
511 ** (7) Try the USERNAME environment variable.
512 **
513 ** (8) Check if the user can be extracted from the remote URL.
 
 
514 **
515 ** The user name is stored in g.zLogin. The uid is in g.userUid.
516 */
517 void user_select(void){
518 UrlData url;
519 if( g.userUid ) return;
520 if( g.zLogin ){
521 if( attempt_user(g.zLogin)==0 ){
522 fossil_fatal("no such user: %s", g.zLogin);
523 }else{
524 return;
525 }
526 }
527
528 if( g.localOpen && attempt_user(db_lget("default-user",0)) ) return;
529
530 if( attempt_user(db_get("default-user", 0)) ) return;
531
532 if( attempt_user(fossil_getenv("FOSSIL_USER")) ) return;
533
534 if( attempt_user(fossil_getenv("USER")) ) return;
535
536 if( attempt_user(fossil_getenv("LOGNAME")) ) return;
537
538 if( attempt_user(fossil_getenv("USERNAME")) ) return;
539
540 memset(&url, 0, sizeof(url));
541 url_parse_local(0, URL_USE_CONFIG, &url);
542 if( url.user && attempt_user(url.user) ) return;
543
544 fossil_print(
545 "Cannot figure out who you are! Consider using the --user\n"
546 "command line option, setting your USER environment variable,\n"
547 "or setting a default user with \"fossil user default USER\".\n"
548
--- src/user.c
+++ src/user.c
@@ -326,14 +326,21 @@
326 **
327 ** > fossil user contact USERNAME ?CONTACT-INFO?
328 **
329 ** Query or set contact information for user USERNAME
330 **
331 ** > fossil user default ?OPTIONS? ?USERNAME?
332 **
333 ** Query or set the default user. The default user is the
334 ** user for command-line interaction. If USERNAME is an
335 ** empty string, then the default user is unset from the
336 ** repository and will subsequently be determined by the -U
337 ** command-line option or by environment variables
338 ** FOSSIL_USER, USER, LOGNAME, or USERNAME, in that order.
339 ** OPTIONS:
340 **
341 ** -v|--verbose Show how the default user is computed
342 **
343 ** > fossil user list | ls
344 **
345 ** List all users known to the repository
346 **
@@ -385,21 +392,50 @@
392 &login, zPw, &caps, &contact
393 );
394 db_protect_pop();
395 free(zPw);
396 }else if( n>=2 && strncmp(g.argv[2],"default",n)==0 ){
397 int eVerbose = find_option("verbose","v",0)!=0;
398 verify_all_options();
399 if( g.argc>3 ){
400 const char *zUser = g.argv[3];
401 if( fossil_strcmp(zUser,"")==0 || fossil_stricmp(zUser,"nobody")==0 ){
402 db_begin_transaction();
403 if( g.localOpen ){
404 db_multi_exec("DELETE FROM vvar WHERE name='default-user'");
405 }
406 db_unset("default-user",0);
407 db_commit_transaction();
408 }else{
409 if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.argv[3]) ){
410 fossil_fatal("no such user: %s", g.argv[3]);
411 }
412 if( g.localOpen ){
413 db_lset("default-user", g.argv[3]);
414 }else{
415 db_set("default-user", g.argv[3], 0);
416 }
417 }
418 }
419 if( g.argc==3 || eVerbose ){
420 int eHow = user_select();
421 const char *zHow = "???";
422 switch( eHow ){
423 case 1: zHow = "-U option"; break;
424 case 2: zHow = "previously set"; break;
425 case 3: zHow = "local check-out"; break;
426 case 4: zHow = "repository"; break;
427 case 5: zHow = "FOSSIL_USER"; break;
428 case 6: zHow = "USER"; break;
429 case 7: zHow = "LOGNAME"; break;
430 case 8: zHow = "USERNAME"; break;
431 case 9: zHow = "URL"; break;
432 }
433 if( eVerbose ){
434 fossil_print("%s (determined by %s)\n", g.zLogin, zHow);
435 }else{
436 fossil_print("%s\n", g.zLogin);
437 }
438 }
439 }else if(( n>=2 && strncmp(g.argv[2],"list",n)==0 ) ||
440 ( n>=2 && strncmp(g.argv[2],"ls",n)==0 )){
441 Stmt q;
@@ -496,52 +532,54 @@
532 /*
533 ** Figure out what user is at the controls.
534 **
535 ** (1) Use the --user and -U command-line options.
536 **
537 ** (2) The name used for login (if there was a login).
538 **
539 ** (3) If the local database is open, check in VVAR.
540 **
541 ** (4) Check the default-user in the repository
542 **
543 ** (5) Try the FOSSIL_USER environment variable.
544 **
545 ** (6) Try the USER environment variable.
546 **
547 ** (7) Try the LOGNAME environment variable.
548 **
549 ** (8) Try the USERNAME environment variable.
550 **
551 ** (9) Check if the user can be extracted from the remote URL.
552 **
553 ** The user name is stored in g.zLogin. The uid is in g.userUid.
554 */
555 int user_select(void){
556 UrlData url;
557 if( g.userUid ) return 1;
558 if( g.zLogin ){
559 if( attempt_user(g.zLogin)==0 ){
560 fossil_fatal("no such user: %s", g.zLogin);
561 }else{
562 return 2;
563 }
564 }
565
566 if( g.localOpen && attempt_user(db_lget("default-user",0)) ) return 3;
567
568 if( attempt_user(db_get("default-user", 0)) ) return 4;
569
570 if( attempt_user(fossil_getenv("FOSSIL_USER")) ) return 5;
571
572 if( attempt_user(fossil_getenv("USER")) ) return 6;
573
574 if( attempt_user(fossil_getenv("LOGNAME")) ) return 7;
575
576 if( attempt_user(fossil_getenv("USERNAME")) ) return 8;
577
578 memset(&url, 0, sizeof(url));
579 url_parse_local(0, URL_USE_CONFIG, &url);
580 if( url.user && attempt_user(url.user) ) return 9;
581
582 fossil_print(
583 "Cannot figure out who you are! Consider using the --user\n"
584 "command line option, setting your USER environment variable,\n"
585 "or setting a default user with \"fossil user default USER\".\n"
586
+34 -19
--- src/winfile.c
+++ src/winfile.c
@@ -304,39 +304,47 @@
304304
*/
305305
int win32_filenames_equal_nocase(
306306
const wchar_t *fn1,
307307
const wchar_t *fn2
308308
){
309
- static FARPROC fnCompareStringOrdinal;
310
- static FARPROC fnRtlInitUnicodeString;
311
- static FARPROC fnRtlEqualUnicodeString;
309
+ /* ---- Data types used by dynamically loaded API functions. -------------- */
310
+ typedef struct { /* UNICODE_STRING from <ntdef.h> */
311
+ USHORT Length;
312
+ USHORT MaximumLength;
313
+ PWSTR Buffer;
314
+ } MY_UNICODE_STRING;
315
+ /* ---- Prototypes for dynamically loaded API functions. ------------------ */
316
+ typedef int (WINAPI *FNCOMPARESTRINGORDINAL)(LPCWCH,int,LPCWCH,int,BOOL);
317
+ typedef VOID (NTAPI *FNRTLINITUNICODESTRING)(MY_UNICODE_STRING*,PCWSTR);
318
+ typedef BOOLEAN (NTAPI *FNRTLEQUALUNICODESTRING)
319
+ (MY_UNICODE_STRING*,MY_UNICODE_STRING*,BOOLEAN);
320
+ /* ------------------------------------------------------------------------ */
321
+ static FNCOMPARESTRINGORDINAL fnCompareStringOrdinal;
322
+ static FNRTLINITUNICODESTRING fnRtlInitUnicodeString;
323
+ static FNRTLEQUALUNICODESTRING fnRtlEqualUnicodeString;
312324
static int loaded_CompareStringOrdinal;
313325
static int loaded_RtlUnicodeStringAPIs;
314326
if( !loaded_CompareStringOrdinal ){
315
- fnCompareStringOrdinal =
327
+ fnCompareStringOrdinal = (FNCOMPARESTRINGORDINAL)
316328
GetProcAddress(GetModuleHandleA("kernel32"),"CompareStringOrdinal");
317329
loaded_CompareStringOrdinal = 1;
318330
}
319331
if( fnCompareStringOrdinal ){
320332
return fnCompareStringOrdinal(fn1,-1,fn2,-1,1)-2==0;
321333
}
322334
if( !loaded_RtlUnicodeStringAPIs ){
323
- fnRtlInitUnicodeString =
335
+ fnRtlInitUnicodeString = (FNRTLINITUNICODESTRING)
324336
GetProcAddress(GetModuleHandleA("ntdll"),"RtlInitUnicodeString");
325
- fnRtlEqualUnicodeString =
337
+ fnRtlEqualUnicodeString = (FNRTLEQUALUNICODESTRING)
326338
GetProcAddress(GetModuleHandleA("ntdll"),"RtlEqualUnicodeString");
327339
loaded_RtlUnicodeStringAPIs = 1;
328340
}
329341
if( fnRtlInitUnicodeString && fnRtlEqualUnicodeString ){
330
- struct { /* UNICODE_STRING from <ntdef.h> */
331
- unsigned short Length;
332
- unsigned short MaximumLength;
333
- wchar_t *Buffer;
334
- } u1, u2;
342
+ MY_UNICODE_STRING u1, u2;
335343
fnRtlInitUnicodeString(&u1,fn1);
336344
fnRtlInitUnicodeString(&u2,fn2);
337
- return (unsigned char)fnRtlEqualUnicodeString(&u1,&u2,1);
345
+ return (BOOLEAN/*unsigned char*/)fnRtlEqualUnicodeString(&u1,&u2,1);
338346
}
339347
/* In what kind of strange parallel universe are we? */
340348
return lstrcmpiW(fn1,fn2)==0;
341349
}
342350
@@ -461,11 +469,20 @@
461469
** is allocated by mprintf(), or NULL on failure.
462470
*/
463471
char *win32_file_id(
464472
const char *zFileName
465473
){
466
- static FARPROC fnGetFileInformationByHandleEx;
474
+ /* ---- Data types used by dynamically loaded API functions. -------------- */
475
+ typedef struct { /* FILE_ID_INFO from <winbase.h> */
476
+ ULONGLONG VolumeSerialNumber;
477
+ BYTE FileId[16];
478
+ } MY_FILE_ID_INFO;
479
+ /* ---- Prototypes for dynamically loaded API functions. ------------------ */
480
+ typedef int (WINAPI *FNGETFILEINFORMATIONBYHANDLEEX)
481
+ (HANDLE,int/*enum*/,MY_FILE_ID_INFO*,DWORD);
482
+ /* ------------------------------------------------------------------------ */
483
+ static FNGETFILEINFORMATIONBYHANDLEEX fnGetFileInformationByHandleEx;
467484
static int loaded_fnGetFileInformationByHandleEx;
468485
wchar_t *wzFileName = fossil_utf8_to_path(zFileName,0);
469486
HANDLE hFile;
470487
char *zFileId = 0;
471488
hFile = CreateFileW(
@@ -476,17 +493,15 @@
476493
OPEN_EXISTING,
477494
FILE_FLAG_BACKUP_SEMANTICS,
478495
NULL);
479496
if( hFile!=INVALID_HANDLE_VALUE ){
480497
BY_HANDLE_FILE_INFORMATION fi;
481
- struct { /* FILE_ID_INFO from <winbase.h> */
482
- u64 VolumeSerialNumber;
483
- unsigned char FileId[16];
484
- } fi2;
498
+ MY_FILE_ID_INFO fi2;
485499
if( !loaded_fnGetFileInformationByHandleEx ){
486
- fnGetFileInformationByHandleEx = GetProcAddress(
487
- GetModuleHandleA("kernel32"),"GetFileInformationByHandleEx");
500
+ fnGetFileInformationByHandleEx = (FNGETFILEINFORMATIONBYHANDLEEX)
501
+ GetProcAddress(
502
+ GetModuleHandleA("kernel32"),"GetFileInformationByHandleEx");
488503
loaded_fnGetFileInformationByHandleEx = 1;
489504
}
490505
if( fnGetFileInformationByHandleEx ){
491506
if( fnGetFileInformationByHandleEx(
492507
hFile,/*FileIdInfo*/0x12,&fi2,sizeof(fi2)) ){
493508
--- src/winfile.c
+++ src/winfile.c
@@ -304,39 +304,47 @@
304 */
305 int win32_filenames_equal_nocase(
306 const wchar_t *fn1,
307 const wchar_t *fn2
308 ){
309 static FARPROC fnCompareStringOrdinal;
310 static FARPROC fnRtlInitUnicodeString;
311 static FARPROC fnRtlEqualUnicodeString;
 
 
 
 
 
 
 
 
 
 
 
 
312 static int loaded_CompareStringOrdinal;
313 static int loaded_RtlUnicodeStringAPIs;
314 if( !loaded_CompareStringOrdinal ){
315 fnCompareStringOrdinal =
316 GetProcAddress(GetModuleHandleA("kernel32"),"CompareStringOrdinal");
317 loaded_CompareStringOrdinal = 1;
318 }
319 if( fnCompareStringOrdinal ){
320 return fnCompareStringOrdinal(fn1,-1,fn2,-1,1)-2==0;
321 }
322 if( !loaded_RtlUnicodeStringAPIs ){
323 fnRtlInitUnicodeString =
324 GetProcAddress(GetModuleHandleA("ntdll"),"RtlInitUnicodeString");
325 fnRtlEqualUnicodeString =
326 GetProcAddress(GetModuleHandleA("ntdll"),"RtlEqualUnicodeString");
327 loaded_RtlUnicodeStringAPIs = 1;
328 }
329 if( fnRtlInitUnicodeString && fnRtlEqualUnicodeString ){
330 struct { /* UNICODE_STRING from <ntdef.h> */
331 unsigned short Length;
332 unsigned short MaximumLength;
333 wchar_t *Buffer;
334 } u1, u2;
335 fnRtlInitUnicodeString(&u1,fn1);
336 fnRtlInitUnicodeString(&u2,fn2);
337 return (unsigned char)fnRtlEqualUnicodeString(&u1,&u2,1);
338 }
339 /* In what kind of strange parallel universe are we? */
340 return lstrcmpiW(fn1,fn2)==0;
341 }
342
@@ -461,11 +469,20 @@
461 ** is allocated by mprintf(), or NULL on failure.
462 */
463 char *win32_file_id(
464 const char *zFileName
465 ){
466 static FARPROC fnGetFileInformationByHandleEx;
 
 
 
 
 
 
 
 
 
467 static int loaded_fnGetFileInformationByHandleEx;
468 wchar_t *wzFileName = fossil_utf8_to_path(zFileName,0);
469 HANDLE hFile;
470 char *zFileId = 0;
471 hFile = CreateFileW(
@@ -476,17 +493,15 @@
476 OPEN_EXISTING,
477 FILE_FLAG_BACKUP_SEMANTICS,
478 NULL);
479 if( hFile!=INVALID_HANDLE_VALUE ){
480 BY_HANDLE_FILE_INFORMATION fi;
481 struct { /* FILE_ID_INFO from <winbase.h> */
482 u64 VolumeSerialNumber;
483 unsigned char FileId[16];
484 } fi2;
485 if( !loaded_fnGetFileInformationByHandleEx ){
486 fnGetFileInformationByHandleEx = GetProcAddress(
487 GetModuleHandleA("kernel32"),"GetFileInformationByHandleEx");
 
488 loaded_fnGetFileInformationByHandleEx = 1;
489 }
490 if( fnGetFileInformationByHandleEx ){
491 if( fnGetFileInformationByHandleEx(
492 hFile,/*FileIdInfo*/0x12,&fi2,sizeof(fi2)) ){
493
--- src/winfile.c
+++ src/winfile.c
@@ -304,39 +304,47 @@
304 */
305 int win32_filenames_equal_nocase(
306 const wchar_t *fn1,
307 const wchar_t *fn2
308 ){
309 /* ---- Data types used by dynamically loaded API functions. -------------- */
310 typedef struct { /* UNICODE_STRING from <ntdef.h> */
311 USHORT Length;
312 USHORT MaximumLength;
313 PWSTR Buffer;
314 } MY_UNICODE_STRING;
315 /* ---- Prototypes for dynamically loaded API functions. ------------------ */
316 typedef int (WINAPI *FNCOMPARESTRINGORDINAL)(LPCWCH,int,LPCWCH,int,BOOL);
317 typedef VOID (NTAPI *FNRTLINITUNICODESTRING)(MY_UNICODE_STRING*,PCWSTR);
318 typedef BOOLEAN (NTAPI *FNRTLEQUALUNICODESTRING)
319 (MY_UNICODE_STRING*,MY_UNICODE_STRING*,BOOLEAN);
320 /* ------------------------------------------------------------------------ */
321 static FNCOMPARESTRINGORDINAL fnCompareStringOrdinal;
322 static FNRTLINITUNICODESTRING fnRtlInitUnicodeString;
323 static FNRTLEQUALUNICODESTRING fnRtlEqualUnicodeString;
324 static int loaded_CompareStringOrdinal;
325 static int loaded_RtlUnicodeStringAPIs;
326 if( !loaded_CompareStringOrdinal ){
327 fnCompareStringOrdinal = (FNCOMPARESTRINGORDINAL)
328 GetProcAddress(GetModuleHandleA("kernel32"),"CompareStringOrdinal");
329 loaded_CompareStringOrdinal = 1;
330 }
331 if( fnCompareStringOrdinal ){
332 return fnCompareStringOrdinal(fn1,-1,fn2,-1,1)-2==0;
333 }
334 if( !loaded_RtlUnicodeStringAPIs ){
335 fnRtlInitUnicodeString = (FNRTLINITUNICODESTRING)
336 GetProcAddress(GetModuleHandleA("ntdll"),"RtlInitUnicodeString");
337 fnRtlEqualUnicodeString = (FNRTLEQUALUNICODESTRING)
338 GetProcAddress(GetModuleHandleA("ntdll"),"RtlEqualUnicodeString");
339 loaded_RtlUnicodeStringAPIs = 1;
340 }
341 if( fnRtlInitUnicodeString && fnRtlEqualUnicodeString ){
342 MY_UNICODE_STRING u1, u2;
 
 
 
 
343 fnRtlInitUnicodeString(&u1,fn1);
344 fnRtlInitUnicodeString(&u2,fn2);
345 return (BOOLEAN/*unsigned char*/)fnRtlEqualUnicodeString(&u1,&u2,1);
346 }
347 /* In what kind of strange parallel universe are we? */
348 return lstrcmpiW(fn1,fn2)==0;
349 }
350
@@ -461,11 +469,20 @@
469 ** is allocated by mprintf(), or NULL on failure.
470 */
471 char *win32_file_id(
472 const char *zFileName
473 ){
474 /* ---- Data types used by dynamically loaded API functions. -------------- */
475 typedef struct { /* FILE_ID_INFO from <winbase.h> */
476 ULONGLONG VolumeSerialNumber;
477 BYTE FileId[16];
478 } MY_FILE_ID_INFO;
479 /* ---- Prototypes for dynamically loaded API functions. ------------------ */
480 typedef int (WINAPI *FNGETFILEINFORMATIONBYHANDLEEX)
481 (HANDLE,int/*enum*/,MY_FILE_ID_INFO*,DWORD);
482 /* ------------------------------------------------------------------------ */
483 static FNGETFILEINFORMATIONBYHANDLEEX fnGetFileInformationByHandleEx;
484 static int loaded_fnGetFileInformationByHandleEx;
485 wchar_t *wzFileName = fossil_utf8_to_path(zFileName,0);
486 HANDLE hFile;
487 char *zFileId = 0;
488 hFile = CreateFileW(
@@ -476,17 +493,15 @@
493 OPEN_EXISTING,
494 FILE_FLAG_BACKUP_SEMANTICS,
495 NULL);
496 if( hFile!=INVALID_HANDLE_VALUE ){
497 BY_HANDLE_FILE_INFORMATION fi;
498 MY_FILE_ID_INFO fi2;
 
 
 
499 if( !loaded_fnGetFileInformationByHandleEx ){
500 fnGetFileInformationByHandleEx = (FNGETFILEINFORMATIONBYHANDLEEX)
501 GetProcAddress(
502 GetModuleHandleA("kernel32"),"GetFileInformationByHandleEx");
503 loaded_fnGetFileInformationByHandleEx = 1;
504 }
505 if( fnGetFileInformationByHandleEx ){
506 if( fnGetFileInformationByHandleEx(
507 hFile,/*FileIdInfo*/0x12,&fi2,sizeof(fi2)) ){
508
+2 -2
--- src/xfer.c
+++ src/xfer.c
@@ -2382,17 +2382,17 @@
23822382
if( nCycle==0 && db_is_writeable("repository") ){
23832383
xfer_syncwith(g.url.canonical, 0);
23842384
}
23852385
23862386
/* Output current stats */
2387
+ nRoundtrip++;
2388
+ nArtifactSent += xfer.nFileSent + xfer.nDeltaSent;
23872389
if( syncFlags & SYNC_VERBOSE ){
23882390
fossil_print(zValueFormat /*works-like:"%s%d%d%d%d"*/, "Sent:",
23892391
blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
23902392
xfer.nFileSent, xfer.nDeltaSent);
23912393
}else{
2392
- nRoundtrip++;
2393
- nArtifactSent += xfer.nFileSent + xfer.nDeltaSent;
23942394
if( bOutIsTty!=0 ){
23952395
fossil_print(zBriefFormat /*works-like:"%d%d%d"*/,
23962396
nRoundtrip, nArtifactSent, nArtifactRcvd);
23972397
}
23982398
}
23992399
--- src/xfer.c
+++ src/xfer.c
@@ -2382,17 +2382,17 @@
2382 if( nCycle==0 && db_is_writeable("repository") ){
2383 xfer_syncwith(g.url.canonical, 0);
2384 }
2385
2386 /* Output current stats */
 
 
2387 if( syncFlags & SYNC_VERBOSE ){
2388 fossil_print(zValueFormat /*works-like:"%s%d%d%d%d"*/, "Sent:",
2389 blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
2390 xfer.nFileSent, xfer.nDeltaSent);
2391 }else{
2392 nRoundtrip++;
2393 nArtifactSent += xfer.nFileSent + xfer.nDeltaSent;
2394 if( bOutIsTty!=0 ){
2395 fossil_print(zBriefFormat /*works-like:"%d%d%d"*/,
2396 nRoundtrip, nArtifactSent, nArtifactRcvd);
2397 }
2398 }
2399
--- src/xfer.c
+++ src/xfer.c
@@ -2382,17 +2382,17 @@
2382 if( nCycle==0 && db_is_writeable("repository") ){
2383 xfer_syncwith(g.url.canonical, 0);
2384 }
2385
2386 /* Output current stats */
2387 nRoundtrip++;
2388 nArtifactSent += xfer.nFileSent + xfer.nDeltaSent;
2389 if( syncFlags & SYNC_VERBOSE ){
2390 fossil_print(zValueFormat /*works-like:"%s%d%d%d%d"*/, "Sent:",
2391 blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent,
2392 xfer.nFileSent, xfer.nDeltaSent);
2393 }else{
 
 
2394 if( bOutIsTty!=0 ){
2395 fossil_print(zBriefFormat /*works-like:"%d%d%d"*/,
2396 nRoundtrip, nArtifactSent, nArtifactRcvd);
2397 }
2398 }
2399
+3
--- src/zip.c
+++ src/zip.c
@@ -927,10 +927,13 @@
927927
login_check_credentials();
928928
if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
929929
if( fossil_strcmp(g.zPath, "sqlar")==0 ){
930930
eType = ARCHIVE_SQLAR;
931931
zType = "SQL";
932
+ /* For some reason, SQL-archives are like catnip for robots. So
933
+ ** don't allow them to be downloaded by user "nobody" */
934
+ if( g.zLogin==0 ){ login_needed(g.anon.Zip); return; }
932935
}else{
933936
eType = ARCHIVE_ZIP;
934937
zType = "ZIP";
935938
}
936939
fossil_nice_default();
937940
--- src/zip.c
+++ src/zip.c
@@ -927,10 +927,13 @@
927 login_check_credentials();
928 if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
929 if( fossil_strcmp(g.zPath, "sqlar")==0 ){
930 eType = ARCHIVE_SQLAR;
931 zType = "SQL";
 
 
 
932 }else{
933 eType = ARCHIVE_ZIP;
934 zType = "ZIP";
935 }
936 fossil_nice_default();
937
--- src/zip.c
+++ src/zip.c
@@ -927,10 +927,13 @@
927 login_check_credentials();
928 if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; }
929 if( fossil_strcmp(g.zPath, "sqlar")==0 ){
930 eType = ARCHIVE_SQLAR;
931 zType = "SQL";
932 /* For some reason, SQL-archives are like catnip for robots. So
933 ** don't allow them to be downloaded by user "nobody" */
934 if( g.zLogin==0 ){ login_needed(g.anon.Zip); return; }
935 }else{
936 eType = ARCHIVE_ZIP;
937 zType = "ZIP";
938 }
939 fossil_nice_default();
940
--- test/tester.tcl
+++ test/tester.tcl
@@ -356,10 +356,11 @@
356356
mtime-changes \
357357
mv-rm-files \
358358
pgp-command \
359359
preferred-diff-type \
360360
proxy \
361
+ raw-bgcolor \
361362
redirect-to-https \
362363
relative-paths \
363364
repo-cksum \
364365
repolist-skin \
365366
robot-restrict \
@@ -373,13 +374,19 @@
373374
ssl-identity \
374375
tclsh \
375376
th1-setup \
376377
th1-uri-regexp \
377378
ticket-default-report \
379
+ timeline-hard-newlines \
380
+ timeline-plaintext \
381
+ timeline-truncate-at-blank \
382
+ timeline-tslink-info \
378383
timeline-utc \
379384
user-color-map \
385
+ verify-comments \
380386
uv-sync \
387
+ vuln-report \
381388
web-browser]
382389
383390
fossil test-th-eval "hasfeature legacyMvRm"
384391
385392
if {[normalize_result] eq "1"} {
386393
387394
ADDED test/th1-taint.test
--- test/tester.tcl
+++ test/tester.tcl
@@ -356,10 +356,11 @@
356 mtime-changes \
357 mv-rm-files \
358 pgp-command \
359 preferred-diff-type \
360 proxy \
 
361 redirect-to-https \
362 relative-paths \
363 repo-cksum \
364 repolist-skin \
365 robot-restrict \
@@ -373,13 +374,19 @@
373 ssl-identity \
374 tclsh \
375 th1-setup \
376 th1-uri-regexp \
377 ticket-default-report \
 
 
 
 
378 timeline-utc \
379 user-color-map \
 
380 uv-sync \
 
381 web-browser]
382
383 fossil test-th-eval "hasfeature legacyMvRm"
384
385 if {[normalize_result] eq "1"} {
386
387 DDED test/th1-taint.test
--- test/tester.tcl
+++ test/tester.tcl
@@ -356,10 +356,11 @@
356 mtime-changes \
357 mv-rm-files \
358 pgp-command \
359 preferred-diff-type \
360 proxy \
361 raw-bgcolor \
362 redirect-to-https \
363 relative-paths \
364 repo-cksum \
365 repolist-skin \
366 robot-restrict \
@@ -373,13 +374,19 @@
374 ssl-identity \
375 tclsh \
376 th1-setup \
377 th1-uri-regexp \
378 ticket-default-report \
379 timeline-hard-newlines \
380 timeline-plaintext \
381 timeline-truncate-at-blank \
382 timeline-tslink-info \
383 timeline-utc \
384 user-color-map \
385 verify-comments \
386 uv-sync \
387 vuln-report \
388 web-browser]
389
390 fossil test-th-eval "hasfeature legacyMvRm"
391
392 if {[normalize_result] eq "1"} {
393
394 DDED test/th1-taint.test
--- a/test/th1-taint.test
+++ b/test/th1-taint.test
@@ -0,0 +1,84 @@
1
+#
2
+# Copyright (c) 2025 D. Richard Hipp
3
+#
4
+# This program is free software; you can redistribute it and/or
5
+# modify it under the terms of the Simplified BSD License (also
6
+# known as the "2-Clause License" or "FreeBSD License".)
7
+#
8
+# This program is distributed in the hope that it will be useful,
9
+# but without any warranty; without even the implied warranty of
10
+# merchantability or fitness for a particular purpose.
11
+#
12
+# Author contact information:
13
+# [email protected]
14
+# http://www.hwaci.com/drh/
15
+#
16
+############################################################################
17
+#
18
+# TH1 Commands
19
+#
20
+
21
+set path [file dirname [info script]]; test_setup
22
+
23
+proc taint-test {testnum th1script expected} {
24
+ global fossilexe
25
+ set rc [catch {exec $fossilexe test-th-eval $th1script} got]
26
+ if {$rc} {
27
+ test th1-taint-$testnum 0
28
+ puts $got
29
+ return
30
+ }
31
+ if {$got ne $expected} {
32
+ test th1-taint-$testnum 0
33
+ puts " Expected: $expected"
34
+ puts " Got: $got"
35
+ } else {
36
+ test th1-taint-$testnum 1
37
+ }
38
+}
39
+
40
+taint-test 10 {string is tainted abcd} 0
41
+taint-test 20 {string is tainted [taint abcd]} 1
42
+taint-test 30 {string is tainted [untaint [taint abcd]]} 0
43
+taint-test 40 {string is tainted [untaint abcde]} 0
44
+taint-test 50 {string is tainted "abc[taint def]ghi"} 1
45
+taint-test 60 {set t1 [taint abc]; string is tainted "123 $t1 456"} 1
46
+
47
+taint-test 100 {set t1 [taint abc]; lappend t1 def; string is tainted $t1} 1
48
+taint-test 110 {set t1 abc; lappend t1 [taint def]; string is tainted $t1} 1
49
+
50
+taint-test 200 {string is tainted [list abc def ghi]} 0
51
+taint-test 210 {string is tainted [list [taint abc] def ghi]} 1
52
+taint-test 220 {string is tainted [list abc [taint def] ghi]} 1
53
+taint-test 230 {string is tainted [list abc def [taint ghi]]} 1
54
+
55
+taint-test 300 {
56
+ set res {}
57
+ foreach x [list abc [taint def] ghi] {
58
+ lappend res [string is tainted $x]
59
+ }
60
+ set res
61
+} {1 1 1}
62
+taint-test 310 {
63
+ set res {}
64
+ foreach {x y} [list abc [taint def] ghi jkl] {
65
+ lappend res [string is tainted $x] [string is tainted $y]
66
+ }
67
+ set res
68
+} {1 1 1 1}
69
+
70
+taint-test 400 {string is tainted [lindex "abc [taint def] ghi" 0]} 1
71
+taint-test 410 {string is tainted [lindex "abc [taint def] ghi" 1]} 1
72
+taint-test 420 {string is tainted [lindex "abc [taint def] ghi" 2]} 1
73
+taint-test 430 {string is tainted [lindex "abc [taint def] ghi" 3]} 0
74
+
75
+taint-test 500 {string is tainted [string index [taint abcdefg] 3]} 1
76
+
77
+taint-test 600 {string is tainted [string range [taint abcdefg] 3 5]} 1
78
+
79
+taint-test 700 {string is tainted [string trim [taint " abcdefg "]]} 1
80
+taint-test 710 {string is tainted [string trimright [taint " abcdefg "]]} 1
81
+taint-test 720 {string is tainted [string trimleft [taint " abcdefg "]]} 1
82
+
83
+
84
+test_cleanup
--- a/test/th1-taint.test
+++ b/test/th1-taint.test
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/test/th1-taint.test
+++ b/test/th1-taint.test
@@ -0,0 +1,84 @@
1 #
2 # Copyright (c) 2025 D. Richard Hipp
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the Simplified BSD License (also
6 # known as the "2-Clause License" or "FreeBSD License".)
7 #
8 # This program is distributed in the hope that it will be useful,
9 # but without any warranty; without even the implied warranty of
10 # merchantability or fitness for a particular purpose.
11 #
12 # Author contact information:
13 # [email protected]
14 # http://www.hwaci.com/drh/
15 #
16 ############################################################################
17 #
18 # TH1 Commands
19 #
20
21 set path [file dirname [info script]]; test_setup
22
23 proc taint-test {testnum th1script expected} {
24 global fossilexe
25 set rc [catch {exec $fossilexe test-th-eval $th1script} got]
26 if {$rc} {
27 test th1-taint-$testnum 0
28 puts $got
29 return
30 }
31 if {$got ne $expected} {
32 test th1-taint-$testnum 0
33 puts " Expected: $expected"
34 puts " Got: $got"
35 } else {
36 test th1-taint-$testnum 1
37 }
38 }
39
40 taint-test 10 {string is tainted abcd} 0
41 taint-test 20 {string is tainted [taint abcd]} 1
42 taint-test 30 {string is tainted [untaint [taint abcd]]} 0
43 taint-test 40 {string is tainted [untaint abcde]} 0
44 taint-test 50 {string is tainted "abc[taint def]ghi"} 1
45 taint-test 60 {set t1 [taint abc]; string is tainted "123 $t1 456"} 1
46
47 taint-test 100 {set t1 [taint abc]; lappend t1 def; string is tainted $t1} 1
48 taint-test 110 {set t1 abc; lappend t1 [taint def]; string is tainted $t1} 1
49
50 taint-test 200 {string is tainted [list abc def ghi]} 0
51 taint-test 210 {string is tainted [list [taint abc] def ghi]} 1
52 taint-test 220 {string is tainted [list abc [taint def] ghi]} 1
53 taint-test 230 {string is tainted [list abc def [taint ghi]]} 1
54
55 taint-test 300 {
56 set res {}
57 foreach x [list abc [taint def] ghi] {
58 lappend res [string is tainted $x]
59 }
60 set res
61 } {1 1 1}
62 taint-test 310 {
63 set res {}
64 foreach {x y} [list abc [taint def] ghi jkl] {
65 lappend res [string is tainted $x] [string is tainted $y]
66 }
67 set res
68 } {1 1 1 1}
69
70 taint-test 400 {string is tainted [lindex "abc [taint def] ghi" 0]} 1
71 taint-test 410 {string is tainted [lindex "abc [taint def] ghi" 1]} 1
72 taint-test 420 {string is tainted [lindex "abc [taint def] ghi" 2]} 1
73 taint-test 430 {string is tainted [lindex "abc [taint def] ghi" 3]} 0
74
75 taint-test 500 {string is tainted [string index [taint abcdefg] 3]} 1
76
77 taint-test 600 {string is tainted [string range [taint abcdefg] 3 5]} 1
78
79 taint-test 700 {string is tainted [string trim [taint " abcdefg "]]} 1
80 taint-test 710 {string is tainted [string trimright [taint " abcdefg "]]} 1
81 taint-test 720 {string is tainted [string trimleft [taint " abcdefg "]]} 1
82
83
84 test_cleanup
+36 -36
--- test/th1.test
+++ test/th1.test
@@ -795,23 +795,23 @@
795795
rpage-\$requested_page\
796796
cpage-\$canonical_page\">" [normalize_result]]}
797797
798798
###############################################################################
799799
800
-fossil test-th-eval "styleHeader {Page Title Here}"
801
-test th1-header-1 {$RESULT eq {TH_ERROR: repository unavailable}}
800
+#fossil test-th-eval "styleHeader {Page Title Here}"
801
+#test th1-header-1 {$RESULT eq {TH_ERROR: repository unavailable}}
802802
803803
###############################################################################
804804
805805
test_in_checkout th1-header-2 {
806806
fossil test-th-eval --open-config "styleHeader {Page Title Here}"
807807
} {[regexp -- {<title>Fossil: Page Title Here</title>} $RESULT]}
808808
809809
###############################################################################
810810
811
-fossil test-th-eval "styleFooter"
812
-test th1-footer-1 {$RESULT eq {TH_ERROR: repository unavailable}}
811
+#fossil test-th-eval "styleFooter"
812
+#test th1-footer-1 {$RESULT eq {TH_ERROR: repository unavailable}}
813813
814814
###############################################################################
815815
816816
fossil test-th-eval --open-config "styleFooter"
817817
test th1-footer-2 {$RESULT eq {}}
@@ -879,44 +879,44 @@
879879
test th1-artifact-1 {$RESULT eq \
880880
{TH_ERROR: wrong # args: should be "artifact ID ?FILENAME?"}}
881881
882882
###############################################################################
883883
884
-fossil test-th-eval "artifact tip"
885
-test th1-artifact-2 {$RESULT eq {TH_ERROR: repository unavailable}}
884
+#fossil test-th-eval "artifact tip"
885
+#test th1-artifact-2 {$RESULT eq {TH_ERROR: repository unavailable}}
886886
887887
###############################################################################
888888
889889
test_in_checkout th1-artifact-3 {
890890
fossil test-th-eval --open-config "artifact tip"
891891
} {[regexp -- {F test/th1\.test [0-9a-f]{40,64}} $RESULT]}
892892
893893
###############################################################################
894894
895
-fossil test-th-eval "artifact 0000000000"
896
-test th1-artifact-4 {$RESULT eq {TH_ERROR: repository unavailable}}
895
+#fossil test-th-eval "artifact 0000000000"
896
+#test th1-artifact-4 {$RESULT eq {TH_ERROR: repository unavailable}}
897897
898898
###############################################################################
899899
900900
fossil test-th-eval --open-config "artifact 0000000000"
901901
test th1-artifact-5 {$RESULT eq {TH_ERROR: name not found}}
902902
903903
###############################################################################
904904
905
-fossil test-th-eval "artifact tip test/th1.test"
906
-test th1-artifact-6 {$RESULT eq {TH_ERROR: repository unavailable}}
905
+#fossil test-th-eval "artifact tip test/th1.test"
906
+#test th1-artifact-6 {$RESULT eq {TH_ERROR: repository unavailable}}
907907
908908
###############################################################################
909909
910910
test_in_checkout th1-artifact-7 {
911911
fossil test-th-eval --open-config "artifact tip test/th1.test"
912912
} {[regexp -- {th1-artifact-7} $RESULT]}
913913
914914
###############################################################################
915915
916
-fossil test-th-eval "artifact 0000000000 test/th1.test"
917
-test th1-artifact-8 {$RESULT eq {TH_ERROR: repository unavailable}}
916
+#fossil test-th-eval "artifact 0000000000 test/th1.test"
917
+#test th1-artifact-8 {$RESULT eq {TH_ERROR: repository unavailable}}
918918
919919
###############################################################################
920920
921921
fossil test-th-eval --open-config "artifact 0000000000 test/th1.test"
922922
test th1-artifact-9 {$RESULT eq {TH_ERROR: manifest not found}}
@@ -947,12 +947,12 @@
947947
}
948948
}
949949
950950
###############################################################################
951951
952
-fossil test-th-eval "globalState configuration"
953
-test th1-globalState-3 {[string length $RESULT] == 0}
952
+#fossil test-th-eval "globalState configuration"
953
+#test th1-globalState-3 {[string length $RESULT] == 0}
954954
955955
###############################################################################
956956
957957
fossil test-th-eval --open-config "globalState configuration"
958958
test th1-globalState-4 {[string length $RESULT] > 0}
@@ -1041,12 +1041,12 @@
10411041
fossil test-th-eval "globalState flags"
10421042
test th1-globalState-16 {$RESULT eq "0"}
10431043
10441044
###############################################################################
10451045
1046
-fossil test-th-eval "reinitialize; globalState configuration"
1047
-test th1-reinitialize-1 {$RESULT eq ""}
1046
+#fossil test-th-eval "reinitialize; globalState configuration"
1047
+#test th1-reinitialize-1 {$RESULT eq ""}
10481048
10491049
###############################################################################
10501050
10511051
fossil test-th-eval "reinitialize 1; globalState configuration"
10521052
test th1-reinitialize-2 {$RESULT ne ""}
@@ -1056,29 +1056,29 @@
10561056
#
10571057
# NOTE: This test will fail if the command names are added to TH1, or
10581058
# moved from Tcl builds to plain or the reverse. Sorting the
10591059
# command lists eliminates a dependence on order.
10601060
#
1061
-fossil test-th-eval "info commands"
1062
-set sorted_result [lsort $RESULT]
1063
-protOut "Sorted: $sorted_result"
1064
-set base_commands {anoncap anycap array artifact break breakpoint \
1065
- builtin_request_js capexpr captureTh1 catch cgiHeaderLine checkout \
1066
- combobox continue copybtn date decorate defHeader dir enable_htmlify \
1067
- enable_output encode64 error expr for foreach getParameter glob_match \
1068
- globalState hascap hasfeature html htmlize http httpize if info \
1069
- insertCsrf lappend lindex linecount list llength lsearch markdown nonce \
1070
- proc puts query randhex redirect regexp reinitialize rename render \
1071
- repository return searchable set setParameter setting stime string \
1072
- styleFooter styleHeader styleScript submenu tclReady trace unset \
1073
- unversioned uplevel upvar utime verifyCsrf verifyLogin wiki}
1074
-set tcl_commands {tclEval tclExpr tclInvoke tclIsSafe tclMakeSafe}
1075
-if {$th1Tcl} {
1076
- test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands $tcl_commands"]}
1077
-} else {
1078
- test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands"]}
1079
-}
1061
+#fossil test-th-eval "info commands"
1062
+#set sorted_result [lsort $RESULT]
1063
+#protOut "Sorted: $sorted_result"
1064
+#set base_commands {anoncap anycap array artifact break breakpoint \
1065
+# builtin_request_js capexpr captureTh1 catch cgiHeaderLine checkout \
1066
+# combobox continue copybtn date decorate defHeader dir \
1067
+# enable_output encode64 error expr for foreach getParameter glob_match \
1068
+# globalState hascap hasfeature html htmlize http httpize if info \
1069
+# insertCsrf lappend lindex linecount list llength lsearch markdown nonce \
1070
+# proc puts query randhex redirect regexp reinitialize rename render \
1071
+# repository return searchable set setParameter setting stime string \
1072
+# styleFooter styleHeader styleScript submenu tclReady trace unset \
1073
+# unversioned uplevel upvar utime verifyCsrf verifyLogin wiki}
1074
+#set tcl_commands {tclEval tclExpr tclInvoke tclIsSafe tclMakeSafe}
1075
+#if {$th1Tcl} {
1076
+# test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands $tcl_commands"]}
1077
+#} else {
1078
+# test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands"]}
1079
+#}
10801080
10811081
###############################################################################
10821082
10831083
fossil test-th-eval "info vars"
10841084
@@ -1326,11 +1326,11 @@
13261326
13271327
###############################################################################
13281328
13291329
fossil test-th-eval {string is other 123}
13301330
test th1-string-is-4 {$RESULT eq \
1331
-"TH_ERROR: Expected alnum, double, integer, or list, got: other"}
1331
+"TH_ERROR: Expected alnum, double, integer, list, or tainted, got: other"}
13321332
13331333
###############################################################################
13341334
13351335
fossil test-th-eval {string is alnum 123}
13361336
test th1-string-is-5 {$RESULT eq "1"}
13371337
--- test/th1.test
+++ test/th1.test
@@ -795,23 +795,23 @@
795 rpage-\$requested_page\
796 cpage-\$canonical_page\">" [normalize_result]]}
797
798 ###############################################################################
799
800 fossil test-th-eval "styleHeader {Page Title Here}"
801 test th1-header-1 {$RESULT eq {TH_ERROR: repository unavailable}}
802
803 ###############################################################################
804
805 test_in_checkout th1-header-2 {
806 fossil test-th-eval --open-config "styleHeader {Page Title Here}"
807 } {[regexp -- {<title>Fossil: Page Title Here</title>} $RESULT]}
808
809 ###############################################################################
810
811 fossil test-th-eval "styleFooter"
812 test th1-footer-1 {$RESULT eq {TH_ERROR: repository unavailable}}
813
814 ###############################################################################
815
816 fossil test-th-eval --open-config "styleFooter"
817 test th1-footer-2 {$RESULT eq {}}
@@ -879,44 +879,44 @@
879 test th1-artifact-1 {$RESULT eq \
880 {TH_ERROR: wrong # args: should be "artifact ID ?FILENAME?"}}
881
882 ###############################################################################
883
884 fossil test-th-eval "artifact tip"
885 test th1-artifact-2 {$RESULT eq {TH_ERROR: repository unavailable}}
886
887 ###############################################################################
888
889 test_in_checkout th1-artifact-3 {
890 fossil test-th-eval --open-config "artifact tip"
891 } {[regexp -- {F test/th1\.test [0-9a-f]{40,64}} $RESULT]}
892
893 ###############################################################################
894
895 fossil test-th-eval "artifact 0000000000"
896 test th1-artifact-4 {$RESULT eq {TH_ERROR: repository unavailable}}
897
898 ###############################################################################
899
900 fossil test-th-eval --open-config "artifact 0000000000"
901 test th1-artifact-5 {$RESULT eq {TH_ERROR: name not found}}
902
903 ###############################################################################
904
905 fossil test-th-eval "artifact tip test/th1.test"
906 test th1-artifact-6 {$RESULT eq {TH_ERROR: repository unavailable}}
907
908 ###############################################################################
909
910 test_in_checkout th1-artifact-7 {
911 fossil test-th-eval --open-config "artifact tip test/th1.test"
912 } {[regexp -- {th1-artifact-7} $RESULT]}
913
914 ###############################################################################
915
916 fossil test-th-eval "artifact 0000000000 test/th1.test"
917 test th1-artifact-8 {$RESULT eq {TH_ERROR: repository unavailable}}
918
919 ###############################################################################
920
921 fossil test-th-eval --open-config "artifact 0000000000 test/th1.test"
922 test th1-artifact-9 {$RESULT eq {TH_ERROR: manifest not found}}
@@ -947,12 +947,12 @@
947 }
948 }
949
950 ###############################################################################
951
952 fossil test-th-eval "globalState configuration"
953 test th1-globalState-3 {[string length $RESULT] == 0}
954
955 ###############################################################################
956
957 fossil test-th-eval --open-config "globalState configuration"
958 test th1-globalState-4 {[string length $RESULT] > 0}
@@ -1041,12 +1041,12 @@
1041 fossil test-th-eval "globalState flags"
1042 test th1-globalState-16 {$RESULT eq "0"}
1043
1044 ###############################################################################
1045
1046 fossil test-th-eval "reinitialize; globalState configuration"
1047 test th1-reinitialize-1 {$RESULT eq ""}
1048
1049 ###############################################################################
1050
1051 fossil test-th-eval "reinitialize 1; globalState configuration"
1052 test th1-reinitialize-2 {$RESULT ne ""}
@@ -1056,29 +1056,29 @@
1056 #
1057 # NOTE: This test will fail if the command names are added to TH1, or
1058 # moved from Tcl builds to plain or the reverse. Sorting the
1059 # command lists eliminates a dependence on order.
1060 #
1061 fossil test-th-eval "info commands"
1062 set sorted_result [lsort $RESULT]
1063 protOut "Sorted: $sorted_result"
1064 set base_commands {anoncap anycap array artifact break breakpoint \
1065 builtin_request_js capexpr captureTh1 catch cgiHeaderLine checkout \
1066 combobox continue copybtn date decorate defHeader dir enable_htmlify \
1067 enable_output encode64 error expr for foreach getParameter glob_match \
1068 globalState hascap hasfeature html htmlize http httpize if info \
1069 insertCsrf lappend lindex linecount list llength lsearch markdown nonce \
1070 proc puts query randhex redirect regexp reinitialize rename render \
1071 repository return searchable set setParameter setting stime string \
1072 styleFooter styleHeader styleScript submenu tclReady trace unset \
1073 unversioned uplevel upvar utime verifyCsrf verifyLogin wiki}
1074 set tcl_commands {tclEval tclExpr tclInvoke tclIsSafe tclMakeSafe}
1075 if {$th1Tcl} {
1076 test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands $tcl_commands"]}
1077 } else {
1078 test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands"]}
1079 }
1080
1081 ###############################################################################
1082
1083 fossil test-th-eval "info vars"
1084
@@ -1326,11 +1326,11 @@
1326
1327 ###############################################################################
1328
1329 fossil test-th-eval {string is other 123}
1330 test th1-string-is-4 {$RESULT eq \
1331 "TH_ERROR: Expected alnum, double, integer, or list, got: other"}
1332
1333 ###############################################################################
1334
1335 fossil test-th-eval {string is alnum 123}
1336 test th1-string-is-5 {$RESULT eq "1"}
1337
--- test/th1.test
+++ test/th1.test
@@ -795,23 +795,23 @@
795 rpage-\$requested_page\
796 cpage-\$canonical_page\">" [normalize_result]]}
797
798 ###############################################################################
799
800 #fossil test-th-eval "styleHeader {Page Title Here}"
801 #test th1-header-1 {$RESULT eq {TH_ERROR: repository unavailable}}
802
803 ###############################################################################
804
805 test_in_checkout th1-header-2 {
806 fossil test-th-eval --open-config "styleHeader {Page Title Here}"
807 } {[regexp -- {<title>Fossil: Page Title Here</title>} $RESULT]}
808
809 ###############################################################################
810
811 #fossil test-th-eval "styleFooter"
812 #test th1-footer-1 {$RESULT eq {TH_ERROR: repository unavailable}}
813
814 ###############################################################################
815
816 fossil test-th-eval --open-config "styleFooter"
817 test th1-footer-2 {$RESULT eq {}}
@@ -879,44 +879,44 @@
879 test th1-artifact-1 {$RESULT eq \
880 {TH_ERROR: wrong # args: should be "artifact ID ?FILENAME?"}}
881
882 ###############################################################################
883
884 #fossil test-th-eval "artifact tip"
885 #test th1-artifact-2 {$RESULT eq {TH_ERROR: repository unavailable}}
886
887 ###############################################################################
888
889 test_in_checkout th1-artifact-3 {
890 fossil test-th-eval --open-config "artifact tip"
891 } {[regexp -- {F test/th1\.test [0-9a-f]{40,64}} $RESULT]}
892
893 ###############################################################################
894
895 #fossil test-th-eval "artifact 0000000000"
896 #test th1-artifact-4 {$RESULT eq {TH_ERROR: repository unavailable}}
897
898 ###############################################################################
899
900 fossil test-th-eval --open-config "artifact 0000000000"
901 test th1-artifact-5 {$RESULT eq {TH_ERROR: name not found}}
902
903 ###############################################################################
904
905 #fossil test-th-eval "artifact tip test/th1.test"
906 #test th1-artifact-6 {$RESULT eq {TH_ERROR: repository unavailable}}
907
908 ###############################################################################
909
910 test_in_checkout th1-artifact-7 {
911 fossil test-th-eval --open-config "artifact tip test/th1.test"
912 } {[regexp -- {th1-artifact-7} $RESULT]}
913
914 ###############################################################################
915
916 #fossil test-th-eval "artifact 0000000000 test/th1.test"
917 #test th1-artifact-8 {$RESULT eq {TH_ERROR: repository unavailable}}
918
919 ###############################################################################
920
921 fossil test-th-eval --open-config "artifact 0000000000 test/th1.test"
922 test th1-artifact-9 {$RESULT eq {TH_ERROR: manifest not found}}
@@ -947,12 +947,12 @@
947 }
948 }
949
950 ###############################################################################
951
952 #fossil test-th-eval "globalState configuration"
953 #test th1-globalState-3 {[string length $RESULT] == 0}
954
955 ###############################################################################
956
957 fossil test-th-eval --open-config "globalState configuration"
958 test th1-globalState-4 {[string length $RESULT] > 0}
@@ -1041,12 +1041,12 @@
1041 fossil test-th-eval "globalState flags"
1042 test th1-globalState-16 {$RESULT eq "0"}
1043
1044 ###############################################################################
1045
1046 #fossil test-th-eval "reinitialize; globalState configuration"
1047 #test th1-reinitialize-1 {$RESULT eq ""}
1048
1049 ###############################################################################
1050
1051 fossil test-th-eval "reinitialize 1; globalState configuration"
1052 test th1-reinitialize-2 {$RESULT ne ""}
@@ -1056,29 +1056,29 @@
1056 #
1057 # NOTE: This test will fail if the command names are added to TH1, or
1058 # moved from Tcl builds to plain or the reverse. Sorting the
1059 # command lists eliminates a dependence on order.
1060 #
1061 #fossil test-th-eval "info commands"
1062 #set sorted_result [lsort $RESULT]
1063 #protOut "Sorted: $sorted_result"
1064 #set base_commands {anoncap anycap array artifact break breakpoint \
1065 # builtin_request_js capexpr captureTh1 catch cgiHeaderLine checkout \
1066 # combobox continue copybtn date decorate defHeader dir \
1067 # enable_output encode64 error expr for foreach getParameter glob_match \
1068 # globalState hascap hasfeature html htmlize http httpize if info \
1069 # insertCsrf lappend lindex linecount list llength lsearch markdown nonce \
1070 # proc puts query randhex redirect regexp reinitialize rename render \
1071 # repository return searchable set setParameter setting stime string \
1072 # styleFooter styleHeader styleScript submenu tclReady trace unset \
1073 # unversioned uplevel upvar utime verifyCsrf verifyLogin wiki}
1074 #set tcl_commands {tclEval tclExpr tclInvoke tclIsSafe tclMakeSafe}
1075 #if {$th1Tcl} {
1076 # test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands $tcl_commands"]}
1077 #} else {
1078 # test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands"]}
1079 #}
1080
1081 ###############################################################################
1082
1083 fossil test-th-eval "info vars"
1084
@@ -1326,11 +1326,11 @@
1326
1327 ###############################################################################
1328
1329 fossil test-th-eval {string is other 123}
1330 test th1-string-is-4 {$RESULT eq \
1331 "TH_ERROR: Expected alnum, double, integer, list, or tainted, got: other"}
1332
1333 ###############################################################################
1334
1335 fossil test-th-eval {string is alnum 123}
1336 test th1-string-is-5 {$RESULT eq "1"}
1337
--- tools/makemake.tcl
+++ tools/makemake.tcl
@@ -368,39 +368,41 @@
368368
writeln [string map [list \
369369
<<<SQLITE_OPTIONS>>> [join $SQLITE_OPTIONS " \\\n "] \
370370
<<<SHELL_OPTIONS>>> [join $SHELL_OPTIONS " \\\n "] \
371371
<<<PIKCHR_OPTIONS>>> [join $PIKCHR_OPTIONS " \\\n "] \
372372
<<<NEXT_LINE>>> \\] {
373
-all: $(OBJDIR) $(APPNAME)
373
+all: $(APPNAME)
374374
375375
install: all
376376
mkdir -p $(INSTALLDIR)
377377
cp $(APPNAME) $(INSTALLDIR)
378378
379379
codecheck: $(TRANS_SRC) $(OBJDIR)/codecheck1
380380
$(OBJDIR)/codecheck1 $(TRANS_SRC)
381381
382
-$(OBJDIR):
383
- -mkdir $(OBJDIR)
384
-
385382
$(OBJDIR)/translate: $(SRCDIR_tools)/translate.c
386383
-mkdir -p $(OBJDIR)
387384
$(XBCC) -o $(OBJDIR)/translate $(SRCDIR_tools)/translate.c
388385
389386
$(OBJDIR)/makeheaders: $(SRCDIR_tools)/makeheaders.c
387
+ -mkdir -p $(OBJDIR)
390388
$(XBCC) -o $(OBJDIR)/makeheaders $(SRCDIR_tools)/makeheaders.c
391389
392390
$(OBJDIR)/mkindex: $(SRCDIR_tools)/mkindex.c
391
+ -mkdir -p $(OBJDIR)
393392
$(XBCC) -o $(OBJDIR)/mkindex $(SRCDIR_tools)/mkindex.c
394393
395394
$(OBJDIR)/mkbuiltin: $(SRCDIR_tools)/mkbuiltin.c
395
+ -mkdir -p $(OBJDIR)
396396
$(XBCC) -o $(OBJDIR)/mkbuiltin $(SRCDIR_tools)/mkbuiltin.c
397397
398398
$(OBJDIR)/mkversion: $(SRCDIR_tools)/mkversion.c
399
+ -mkdir -p $(OBJDIR)
399400
$(XBCC) -o $(OBJDIR)/mkversion $(SRCDIR_tools)/mkversion.c
400401
401402
$(OBJDIR)/codecheck1: $(SRCDIR_tools)/codecheck1.c
403
+ -mkdir -p $(OBJDIR)
402404
$(XBCC) -o $(OBJDIR)/codecheck1 $(SRCDIR_tools)/codecheck1.c
403405
404406
# Run the test suite.
405407
# Other flags that can be included in TESTFLAGS are:
406408
#
@@ -412,11 +414,11 @@
412414
# -strict Treat known bugs as failures
413415
#
414416
# TESTFLAGS can also include names of specific test files to limit
415417
# the run to just those test cases.
416418
#
417
-test: $(OBJDIR) $(APPNAME)
419
+test: $(APPNAME)
418420
$(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(TESTFLAGS)
419421
420422
$(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion $(OBJDIR)/phony.h
421423
$(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid <<<NEXT_LINE>>>
422424
$(SRCDIR)/../manifest <<<NEXT_LINE>>>
@@ -552,23 +554,26 @@
552554
553555
writeln "\$(OBJDIR)/linenoise.o:\t\$(SRCDIR_extsrc)/linenoise.c \$(SRCDIR_extsrc)/linenoise.h"
554556
writeln "\t\$(XTCC) -c \$(SRCDIR_extsrc)/linenoise.c -o \$@\n"
555557
556558
writeln "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c"
559
+writeln "\t-mkdir -p \$(OBJDIR)\n"
557560
writeln "\t\$(XTCC) -c \$(SRCDIR)/th.c -o \$@\n"
558561
559562
writeln "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c"
563
+writeln "\t-mkdir -p \$(OBJDIR)\n"
560564
writeln "\t\$(XTCC) -c \$(SRCDIR)/th_lang.c -o \$@\n"
561565
562566
writeln "\$(OBJDIR)/th_tcl.o:\t\$(SRCDIR)/th_tcl.c"
567
+writeln "\t-mkdir -p \$(OBJDIR)\n"
563568
writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$@\n"
564569
565570
writeln [string map [list <<<NEXT_LINE>>> \\] {
566
-$(OBJDIR)/pikchr.o: $(SRCDIR_extsrc)/pikchr.c
571
+$(OBJDIR)/pikchr.o: $(SRCDIR_extsrc)/pikchr.c $(OBJDIR)/mkversion
567572
$(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@
568573
569
-$(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c
574
+$(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c $(OBJDIR)/mkversion
570575
$(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@
571576
572577
$(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c $(MAKEFILE_LIST)
573578
$(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry <<<NEXT_LINE>>>
574579
-sEXPORTED_RUNTIME_METHODS=cwrap,ccall,setValue,getValue,stackSave,stackAlloc,stackRestore <<<NEXT_LINE>>>
575580
--- tools/makemake.tcl
+++ tools/makemake.tcl
@@ -368,39 +368,41 @@
368 writeln [string map [list \
369 <<<SQLITE_OPTIONS>>> [join $SQLITE_OPTIONS " \\\n "] \
370 <<<SHELL_OPTIONS>>> [join $SHELL_OPTIONS " \\\n "] \
371 <<<PIKCHR_OPTIONS>>> [join $PIKCHR_OPTIONS " \\\n "] \
372 <<<NEXT_LINE>>> \\] {
373 all: $(OBJDIR) $(APPNAME)
374
375 install: all
376 mkdir -p $(INSTALLDIR)
377 cp $(APPNAME) $(INSTALLDIR)
378
379 codecheck: $(TRANS_SRC) $(OBJDIR)/codecheck1
380 $(OBJDIR)/codecheck1 $(TRANS_SRC)
381
382 $(OBJDIR):
383 -mkdir $(OBJDIR)
384
385 $(OBJDIR)/translate: $(SRCDIR_tools)/translate.c
386 -mkdir -p $(OBJDIR)
387 $(XBCC) -o $(OBJDIR)/translate $(SRCDIR_tools)/translate.c
388
389 $(OBJDIR)/makeheaders: $(SRCDIR_tools)/makeheaders.c
 
390 $(XBCC) -o $(OBJDIR)/makeheaders $(SRCDIR_tools)/makeheaders.c
391
392 $(OBJDIR)/mkindex: $(SRCDIR_tools)/mkindex.c
 
393 $(XBCC) -o $(OBJDIR)/mkindex $(SRCDIR_tools)/mkindex.c
394
395 $(OBJDIR)/mkbuiltin: $(SRCDIR_tools)/mkbuiltin.c
 
396 $(XBCC) -o $(OBJDIR)/mkbuiltin $(SRCDIR_tools)/mkbuiltin.c
397
398 $(OBJDIR)/mkversion: $(SRCDIR_tools)/mkversion.c
 
399 $(XBCC) -o $(OBJDIR)/mkversion $(SRCDIR_tools)/mkversion.c
400
401 $(OBJDIR)/codecheck1: $(SRCDIR_tools)/codecheck1.c
 
402 $(XBCC) -o $(OBJDIR)/codecheck1 $(SRCDIR_tools)/codecheck1.c
403
404 # Run the test suite.
405 # Other flags that can be included in TESTFLAGS are:
406 #
@@ -412,11 +414,11 @@
412 # -strict Treat known bugs as failures
413 #
414 # TESTFLAGS can also include names of specific test files to limit
415 # the run to just those test cases.
416 #
417 test: $(OBJDIR) $(APPNAME)
418 $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(TESTFLAGS)
419
420 $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion $(OBJDIR)/phony.h
421 $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid <<<NEXT_LINE>>>
422 $(SRCDIR)/../manifest <<<NEXT_LINE>>>
@@ -552,23 +554,26 @@
552
553 writeln "\$(OBJDIR)/linenoise.o:\t\$(SRCDIR_extsrc)/linenoise.c \$(SRCDIR_extsrc)/linenoise.h"
554 writeln "\t\$(XTCC) -c \$(SRCDIR_extsrc)/linenoise.c -o \$@\n"
555
556 writeln "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c"
 
557 writeln "\t\$(XTCC) -c \$(SRCDIR)/th.c -o \$@\n"
558
559 writeln "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c"
 
560 writeln "\t\$(XTCC) -c \$(SRCDIR)/th_lang.c -o \$@\n"
561
562 writeln "\$(OBJDIR)/th_tcl.o:\t\$(SRCDIR)/th_tcl.c"
 
563 writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$@\n"
564
565 writeln [string map [list <<<NEXT_LINE>>> \\] {
566 $(OBJDIR)/pikchr.o: $(SRCDIR_extsrc)/pikchr.c
567 $(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@
568
569 $(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c
570 $(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@
571
572 $(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c $(MAKEFILE_LIST)
573 $(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry <<<NEXT_LINE>>>
574 -sEXPORTED_RUNTIME_METHODS=cwrap,ccall,setValue,getValue,stackSave,stackAlloc,stackRestore <<<NEXT_LINE>>>
575
--- tools/makemake.tcl
+++ tools/makemake.tcl
@@ -368,39 +368,41 @@
368 writeln [string map [list \
369 <<<SQLITE_OPTIONS>>> [join $SQLITE_OPTIONS " \\\n "] \
370 <<<SHELL_OPTIONS>>> [join $SHELL_OPTIONS " \\\n "] \
371 <<<PIKCHR_OPTIONS>>> [join $PIKCHR_OPTIONS " \\\n "] \
372 <<<NEXT_LINE>>> \\] {
373 all: $(APPNAME)
374
375 install: all
376 mkdir -p $(INSTALLDIR)
377 cp $(APPNAME) $(INSTALLDIR)
378
379 codecheck: $(TRANS_SRC) $(OBJDIR)/codecheck1
380 $(OBJDIR)/codecheck1 $(TRANS_SRC)
381
 
 
 
382 $(OBJDIR)/translate: $(SRCDIR_tools)/translate.c
383 -mkdir -p $(OBJDIR)
384 $(XBCC) -o $(OBJDIR)/translate $(SRCDIR_tools)/translate.c
385
386 $(OBJDIR)/makeheaders: $(SRCDIR_tools)/makeheaders.c
387 -mkdir -p $(OBJDIR)
388 $(XBCC) -o $(OBJDIR)/makeheaders $(SRCDIR_tools)/makeheaders.c
389
390 $(OBJDIR)/mkindex: $(SRCDIR_tools)/mkindex.c
391 -mkdir -p $(OBJDIR)
392 $(XBCC) -o $(OBJDIR)/mkindex $(SRCDIR_tools)/mkindex.c
393
394 $(OBJDIR)/mkbuiltin: $(SRCDIR_tools)/mkbuiltin.c
395 -mkdir -p $(OBJDIR)
396 $(XBCC) -o $(OBJDIR)/mkbuiltin $(SRCDIR_tools)/mkbuiltin.c
397
398 $(OBJDIR)/mkversion: $(SRCDIR_tools)/mkversion.c
399 -mkdir -p $(OBJDIR)
400 $(XBCC) -o $(OBJDIR)/mkversion $(SRCDIR_tools)/mkversion.c
401
402 $(OBJDIR)/codecheck1: $(SRCDIR_tools)/codecheck1.c
403 -mkdir -p $(OBJDIR)
404 $(XBCC) -o $(OBJDIR)/codecheck1 $(SRCDIR_tools)/codecheck1.c
405
406 # Run the test suite.
407 # Other flags that can be included in TESTFLAGS are:
408 #
@@ -412,11 +414,11 @@
414 # -strict Treat known bugs as failures
415 #
416 # TESTFLAGS can also include names of specific test files to limit
417 # the run to just those test cases.
418 #
419 test: $(APPNAME)
420 $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(TESTFLAGS)
421
422 $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion $(OBJDIR)/phony.h
423 $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid <<<NEXT_LINE>>>
424 $(SRCDIR)/../manifest <<<NEXT_LINE>>>
@@ -552,23 +554,26 @@
554
555 writeln "\$(OBJDIR)/linenoise.o:\t\$(SRCDIR_extsrc)/linenoise.c \$(SRCDIR_extsrc)/linenoise.h"
556 writeln "\t\$(XTCC) -c \$(SRCDIR_extsrc)/linenoise.c -o \$@\n"
557
558 writeln "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c"
559 writeln "\t-mkdir -p \$(OBJDIR)\n"
560 writeln "\t\$(XTCC) -c \$(SRCDIR)/th.c -o \$@\n"
561
562 writeln "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c"
563 writeln "\t-mkdir -p \$(OBJDIR)\n"
564 writeln "\t\$(XTCC) -c \$(SRCDIR)/th_lang.c -o \$@\n"
565
566 writeln "\$(OBJDIR)/th_tcl.o:\t\$(SRCDIR)/th_tcl.c"
567 writeln "\t-mkdir -p \$(OBJDIR)\n"
568 writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$@\n"
569
570 writeln [string map [list <<<NEXT_LINE>>> \\] {
571 $(OBJDIR)/pikchr.o: $(SRCDIR_extsrc)/pikchr.c $(OBJDIR)/mkversion
572 $(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@
573
574 $(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c $(OBJDIR)/mkversion
575 $(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@
576
577 $(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c $(MAKEFILE_LIST)
578 $(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry <<<NEXT_LINE>>>
579 -sEXPORTED_RUNTIME_METHODS=cwrap,ccall,setValue,getValue,stackSave,stackAlloc,stackRestore <<<NEXT_LINE>>>
580
+2 -3
--- www/cgi.wiki
+++ www/cgi.wiki
@@ -81,13 +81,12 @@
8181
the page title taken from the <tt>FOSSIL_REPOLIST_TITLE</tt>
8282
environment variable. The variable can be defined in the CGI
8383
control file using the [#setenv|<tt>setenv:</tt>] statement.
8484
8585
The "Project Description" and "Login-Group" columns on the repolist page
86
-are optional. They are hidden by default. Show them by putting value "1"
87
-in the global settings "show-repolist-desc" and "show-repolist-lg", or
88
-by setting the <tt>FOSSIL_REPOLIST_SHOW</tt> environment variable to
86
+are optional. They are hidden by default. Show them by
87
+etting the <tt>FOSSIL_REPOLIST_SHOW</tt> environment variable to
8988
a string that contains substrings "description" and/or "login-group".
9089
9190
The repolist-generated page recurses into subdirectories and will list
9291
all <tt>*.fossil</tt> files found, with the following exceptions:
9392
9493
--- www/cgi.wiki
+++ www/cgi.wiki
@@ -81,13 +81,12 @@
81 the page title taken from the <tt>FOSSIL_REPOLIST_TITLE</tt>
82 environment variable. The variable can be defined in the CGI
83 control file using the [#setenv|<tt>setenv:</tt>] statement.
84
85 The "Project Description" and "Login-Group" columns on the repolist page
86 are optional. They are hidden by default. Show them by putting value "1"
87 in the global settings "show-repolist-desc" and "show-repolist-lg", or
88 by setting the <tt>FOSSIL_REPOLIST_SHOW</tt> environment variable to
89 a string that contains substrings "description" and/or "login-group".
90
91 The repolist-generated page recurses into subdirectories and will list
92 all <tt>*.fossil</tt> files found, with the following exceptions:
93
94
--- www/cgi.wiki
+++ www/cgi.wiki
@@ -81,13 +81,12 @@
81 the page title taken from the <tt>FOSSIL_REPOLIST_TITLE</tt>
82 environment variable. The variable can be defined in the CGI
83 control file using the [#setenv|<tt>setenv:</tt>] statement.
84
85 The "Project Description" and "Login-Group" columns on the repolist page
86 are optional. They are hidden by default. Show them by
87 etting the <tt>FOSSIL_REPOLIST_SHOW</tt> environment variable to
 
88 a string that contains substrings "description" and/or "login-group".
89
90 The repolist-generated page recurses into subdirectories and will list
91 all <tt>*.fossil</tt> files found, with the following exceptions:
92
93
+54 -27
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,43 +1,49 @@
11
<title>Change Log</title>
22
3
-<h2 id='v2_26'>Changes for version 2.26 (pending)</h2>
3
+<h2 id='v2_27'>Changes for version 2.27 (pending)</h2>
44
5
- * Enhancements to [/help?cmd=diff|fossil diff] and similar:
5
+ * <i>(pending)</i>
6
+
7
+<h2 id='v2_26'>Changes for version 2.26 (2025-04-30)</h2><ol>
8
+ <li>Enhancements to [/help?cmd=diff|fossil diff] and similar:
69
<ol type="a">
7
- <li> The --from can optionally accept a directory name as its argument,
8
- and uses files under that directory as the baseline for the diff.
10
+ <li> The argument to the --from option can be a directory name, causing
11
+ Fossil to use files under that directory as the baseline for the diff.
912
<li> For "gdiff", if no [/help?cmd=gdiff-command|gdiff-command setting]
1013
is defined, Fossil tries to do a --tk diff if "tclsh" and "wish"
1114
are available, or a --by diff if not.
1215
<li> The "Reload" button is added to --tk diffs, to bring the displayed
1316
diff up to date with the latest changes on disk.
1417
<li> Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show
1518
diffs of multiple files.
1619
</ol>
17
- * Added the [/help?cmd=/ckout|/ckout web page] to provide information
20
+ <li>Added the [/help?cmd=/ckout|/ckout web page] to provide information
1821
about pending changes in a working check-out
19
- * Enhancements to the [/help?cmd=ui|fossil ui] command:
22
+ <li>Enhancements to the [/help?cmd=ui|fossil ui] command:
2023
<ol type="a">
2124
<li> Defaults to using the new [/help?cmd=/ckout|/ckout page] as its
2225
start page. Or, if the new "--from PATH" option is present, the
2326
default start page becomes "/ckout?exbase=PATH".
2427
<li> The new "--extpage FILENAME" option opens the named file as if it
2528
where in a [./serverext.wiki|CGI extension]. Example usage: the
2629
person editing this change log has
2730
"fossil ui --extpage www/changes.wiki" running and hence can
2831
press "Reload" on the web browser to view edits.
32
+ <li> Accept both IPv4 and IPv6 connections on all platforms, including
33
+ Windows and OpenBSD. This also applies to the "fossil server"
34
+ command.
2935
</ol>
30
- * Enhancements to [/help?cmd=merge|fossil merge]:
36
+ <li>Enhancements to [/help?cmd=merge|fossil merge]:
3137
<ol type="a">
3238
<li> Added the [/help?cmd=merge-info|fossil merge-info] command and
3339
especially the --tk option to that command, to provide analysis
3440
of the most recent merge or update operation.
3541
<li> When a merge conflict occurs, a new section is added to the conflict
3642
text that shows Fossil's suggested resolution to the conflict.
3743
</ol>
38
- * Enhancements to [/help?cmd=commit|fossil commit]:
44
+ <li>Enhancements to [/help?cmd=commit|fossil commit]:
3945
<ol type="a">
4046
<li> If Fossil sees potential formatting mistakes (ex: bad hyperlinks)
4147
in the check-in comment, it will alert the developer and give
4248
him or her the opportunity to edit the comment before continuing.
4349
This feature is controllable by the
@@ -49,17 +55,17 @@
4955
branch has been changed.
5056
<li> The interactive checkin comment prompt shows the formatting rules
5157
set for that repository.
5258
<li> Add the "--editor" option.
5359
</ol>
54
- * Deprecate the --comfmtflags and --comment-format global options and
60
+ <li>Deprecate the --comfmtflags and --comment-format global options and
5561
no longer list them in the built-in help, but keep them working for
5662
backwards compatibility.
5763
Alternative TTY comment formatting can still be specified using the
5864
[/help?cmd=comment-format|comment-format setting], if desired. The
5965
default comment format is now called "canonical", not "legacy".
60
- * Enhancements to the [/help?cmd=/timeline|/timeline page]:
66
+ <li>Enhancements to the [/help?cmd=/timeline|/timeline page]:
6167
<ol type="a">
6268
<li> Added the "ml=" ("Merge-in List") query parameter that works
6369
like "rl=" ("Related List") but adds "mionly" style related
6470
check-ins instead of the full "rel" style.
6571
<li> For "tl=", "rl=", and "ml=", the order of the branches in the
@@ -82,35 +88,35 @@
8288
<li> Accept the "Z" (Zulu-time) suffix on date arguments for the
8389
"ymd" and "yw" query parameters.
8490
<li> The new "min" query parameter, when added to a from=,to= query,
8591
collapses long runs of check-ins on the same branch into just
8692
end-points.
87
- <li> The p= and d= parameters an reference different check-ins, which
88
- case the timeline shows those check-ins that are both ancestors
89
- of p= and descendants of d=.
93
+ <li> The p= and d= parameters can now reference different check-ins,
94
+ in which case the timeline shows those check-ins that are both
95
+ ancestors of p= and descendants of d=.
9096
<li> The saturation and intensity of user-specified checkin and branch
9197
background colors are automatically adjusted to keep the colors
9298
compatible with the current skin, unless the
9399
[/help?cmd=raw-bgcolor|raw-bgcolor setting] is turned on.
94100
</ol>
95
- * The [/help?cmd=/docfile|/docfile webpage] was added. It works like
101
+ <li>The [/help?cmd=/docfile|/docfile webpage] was added. It works like
96102
/doc but keeps the title of markdown documents with the document rather
97103
that moving it up to the page title.
98
- * Added the [/help?cmd=/clusterlist|/clusterlist page] for analysis
104
+ <li>Added the [/help?cmd=/clusterlist|/clusterlist page] for analysis
99105
and debugging
100
- * Added the "artifact_to_json(NAME)" SQL function that returns a JSON
106
+ <li>Added the "artifact_to_json(NAME)" SQL function that returns a JSON
101107
decoding of the artifact described by NAME.
102
- * Improvements to the [/help?cmd=patch|fossil patch] command:
108
+ <li>Improvements to the [/help?cmd=patch|fossil patch] command:
103109
<ol type="a">
104110
<li> Fix a bug in "fossil patch create" that causes
105111
[/help?cmd=revert|fossil revert] operations that happened
106112
on individualfiles after a [/help?cmd=merge|fossil merge]
107113
to be omitted from the patch.
108114
<li> Added the [/help?cmd=patch|patch alias] command for managing
109115
aliases for remote checkout names.
110116
</ol>
111
- * Enhancements to on-line help and the [/help?cmd=help|fossil help] command:
117
+ <li>Enhancements to on-line help and the [/help?cmd=help|fossil help] command:
112118
<ol type="a">
113119
<li> Add the ability to search the help text, either in the UI
114120
(on the [/help?cmd=/search|/search page]) or from the command-line
115121
(using the "[/help?cmd=search|fossil search -h PATTERN]" command.)
116122
<li> Accepts an optional SUBCOMMAND argument following the
@@ -117,11 +123,11 @@
117123
COMMAND argument and only shows results for the specified
118124
subcommand, not the entire command.
119125
<li> The -u (--usage) option shows only the command-line syntax
120126
<li> The -o (--options) option shows only the command-line options
121127
</ol>
122
- * Enhancements to the ticket system:
128
+ <li>Enhancements to the [./tickets.wiki|ticket system]:
123129
<ol type="a">
124130
<li> Added the ability to attach wiki pages to a ticket for extended
125131
descriptions.
126132
<li> Added submenu to the 'View Ticket' page, to use it as
127133
template for a new ticket.
@@ -129,21 +135,43 @@
129135
in a row.
130136
<li> Link the version field in ticket view to a matching checkin or tag.
131137
<li> Show creation time in report and ticket view.
132138
<li> Show previous comments in edit ticket as reference.
133139
</ol>
134
- * Added the "hash" query parameter to the
140
+ <li>Added the "hash" query parameter to the
135141
[/help?cmd=/whatis|/whatis webpage].
136
- * Add a "user permissions changes" [/doc/trunk/www/alerts.md|subscription]
142
+ <li>Add a "user permissions changes" [/doc/trunk/www/alerts.md|subscription]
137143
which alerts subscribers when an admin creates a new user or
138144
when a user's permissions change.
139
- * Show project description on repository list.
140
- * Make [/help?cmd=/chat|/chat] better-behaved during server outages, reducing
145
+ <li>If the FOSSIL_REPOLIST_SHOW environment variable exists and contains
146
+ the substring "description", then the project description for each repository
147
+ is shown on the repository list page. The login-group for each project is
148
+ now only shown if the FOSSIL_REPOLIST_SHOW environment variable exists and
149
+ contains the substring "login-group". ([./cgi.wiki#repolist|More information])
150
+ <li>The [/doc/trunk/www/th1.md|TH1 script language] is enhanced for improved
151
+ security:
152
+ <ol type="a">
153
+ <li> TH1 now makes a distinction between
154
+ [/doc/trunk/www/th1.md#taint|tainted and untainted string values].
155
+ This makes it more difficult to write custom TH1 scripts that
156
+ contain XSS or SQL-injection bugs. The
157
+ [/help?cmd=vuln-report|vuln-report] setting was added to control
158
+ what Fossil does when it encounters a potential TH1
159
+ security problem.
160
+ <li> The "--th" option was removed from the [/help?cmd=pikchr|fossil pikchr]
161
+ command.
162
+ <li> The "enable_htmlify" TH1 command was removed.
163
+ </ol>
164
+ <li>Make [/help?cmd=/chat|/chat] better-behaved during server outages, reducing
141165
the frequency of reconnection attempts over time and providing feedback
142166
to the user when the connection is down.
143
- * Diverse minor fixes and additions.
144
-
167
+ <li>The [/help?cmd=/sqlar|/sqlar] page does not work for users who are not logged
168
+ in, nor are links to that page displayed to users who are not logged in. Being
169
+ logged in as "anonymous" is sufficient to overcome this restriction, assuming
170
+ that "anonymous" can download tarballs and ZIP archives.
171
+ <li>Many other minor fixes and additions.
172
+</ol>
145173
146174
<h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2>
147175
148176
* The "[/help?cmd=ui|fossil ui /]" command now works even for repositories
149177
that have non-ASCII filenames
@@ -1160,12 +1188,11 @@
11601188
of rows in a timeline) are held in a cookie and thus persist
11611189
across multiple pages.
11621190
* Rework the skin editing process so that changes are implemented
11631191
on one of nine /draft pages, evaluated, then merged back to the
11641192
default.
1165
- * Added the [https://fossil-scm.org/skins/ardoise/timeline|Ardoise]
1166
- skin.
1193
+ * Added the [/timeline?skin=ardoise&once|Ardoise] skin.
11671194
* Fix the "fossil server" command on Unix to be much more responsive
11681195
to multiple simultaneous web requests.
11691196
* Use the IPv6 stack for the "fossil ui" and "fossil server"
11701197
commands on Windows.
11711198
* Support for [https://sqlite.org/sqlar|SQL Archives] as a download
11721199
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,43 +1,49 @@
1 <title>Change Log</title>
2
3 <h2 id='v2_26'>Changes for version 2.26 (pending)</h2>
4
5 * Enhancements to [/help?cmd=diff|fossil diff] and similar:
 
 
 
6 <ol type="a">
7 <li> The --from can optionally accept a directory name as its argument,
8 and uses files under that directory as the baseline for the diff.
9 <li> For "gdiff", if no [/help?cmd=gdiff-command|gdiff-command setting]
10 is defined, Fossil tries to do a --tk diff if "tclsh" and "wish"
11 are available, or a --by diff if not.
12 <li> The "Reload" button is added to --tk diffs, to bring the displayed
13 diff up to date with the latest changes on disk.
14 <li> Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show
15 diffs of multiple files.
16 </ol>
17 * Added the [/help?cmd=/ckout|/ckout web page] to provide information
18 about pending changes in a working check-out
19 * Enhancements to the [/help?cmd=ui|fossil ui] command:
20 <ol type="a">
21 <li> Defaults to using the new [/help?cmd=/ckout|/ckout page] as its
22 start page. Or, if the new "--from PATH" option is present, the
23 default start page becomes "/ckout?exbase=PATH".
24 <li> The new "--extpage FILENAME" option opens the named file as if it
25 where in a [./serverext.wiki|CGI extension]. Example usage: the
26 person editing this change log has
27 "fossil ui --extpage www/changes.wiki" running and hence can
28 press "Reload" on the web browser to view edits.
 
 
 
29 </ol>
30 * Enhancements to [/help?cmd=merge|fossil merge]:
31 <ol type="a">
32 <li> Added the [/help?cmd=merge-info|fossil merge-info] command and
33 especially the --tk option to that command, to provide analysis
34 of the most recent merge or update operation.
35 <li> When a merge conflict occurs, a new section is added to the conflict
36 text that shows Fossil's suggested resolution to the conflict.
37 </ol>
38 * Enhancements to [/help?cmd=commit|fossil commit]:
39 <ol type="a">
40 <li> If Fossil sees potential formatting mistakes (ex: bad hyperlinks)
41 in the check-in comment, it will alert the developer and give
42 him or her the opportunity to edit the comment before continuing.
43 This feature is controllable by the
@@ -49,17 +55,17 @@
49 branch has been changed.
50 <li> The interactive checkin comment prompt shows the formatting rules
51 set for that repository.
52 <li> Add the "--editor" option.
53 </ol>
54 * Deprecate the --comfmtflags and --comment-format global options and
55 no longer list them in the built-in help, but keep them working for
56 backwards compatibility.
57 Alternative TTY comment formatting can still be specified using the
58 [/help?cmd=comment-format|comment-format setting], if desired. The
59 default comment format is now called "canonical", not "legacy".
60 * Enhancements to the [/help?cmd=/timeline|/timeline page]:
61 <ol type="a">
62 <li> Added the "ml=" ("Merge-in List") query parameter that works
63 like "rl=" ("Related List") but adds "mionly" style related
64 check-ins instead of the full "rel" style.
65 <li> For "tl=", "rl=", and "ml=", the order of the branches in the
@@ -82,35 +88,35 @@
82 <li> Accept the "Z" (Zulu-time) suffix on date arguments for the
83 "ymd" and "yw" query parameters.
84 <li> The new "min" query parameter, when added to a from=,to= query,
85 collapses long runs of check-ins on the same branch into just
86 end-points.
87 <li> The p= and d= parameters an reference different check-ins, which
88 case the timeline shows those check-ins that are both ancestors
89 of p= and descendants of d=.
90 <li> The saturation and intensity of user-specified checkin and branch
91 background colors are automatically adjusted to keep the colors
92 compatible with the current skin, unless the
93 [/help?cmd=raw-bgcolor|raw-bgcolor setting] is turned on.
94 </ol>
95 * The [/help?cmd=/docfile|/docfile webpage] was added. It works like
96 /doc but keeps the title of markdown documents with the document rather
97 that moving it up to the page title.
98 * Added the [/help?cmd=/clusterlist|/clusterlist page] for analysis
99 and debugging
100 * Added the "artifact_to_json(NAME)" SQL function that returns a JSON
101 decoding of the artifact described by NAME.
102 * Improvements to the [/help?cmd=patch|fossil patch] command:
103 <ol type="a">
104 <li> Fix a bug in "fossil patch create" that causes
105 [/help?cmd=revert|fossil revert] operations that happened
106 on individualfiles after a [/help?cmd=merge|fossil merge]
107 to be omitted from the patch.
108 <li> Added the [/help?cmd=patch|patch alias] command for managing
109 aliases for remote checkout names.
110 </ol>
111 * Enhancements to on-line help and the [/help?cmd=help|fossil help] command:
112 <ol type="a">
113 <li> Add the ability to search the help text, either in the UI
114 (on the [/help?cmd=/search|/search page]) or from the command-line
115 (using the "[/help?cmd=search|fossil search -h PATTERN]" command.)
116 <li> Accepts an optional SUBCOMMAND argument following the
@@ -117,11 +123,11 @@
117 COMMAND argument and only shows results for the specified
118 subcommand, not the entire command.
119 <li> The -u (--usage) option shows only the command-line syntax
120 <li> The -o (--options) option shows only the command-line options
121 </ol>
122 * Enhancements to the ticket system:
123 <ol type="a">
124 <li> Added the ability to attach wiki pages to a ticket for extended
125 descriptions.
126 <li> Added submenu to the 'View Ticket' page, to use it as
127 template for a new ticket.
@@ -129,21 +135,43 @@
129 in a row.
130 <li> Link the version field in ticket view to a matching checkin or tag.
131 <li> Show creation time in report and ticket view.
132 <li> Show previous comments in edit ticket as reference.
133 </ol>
134 * Added the "hash" query parameter to the
135 [/help?cmd=/whatis|/whatis webpage].
136 * Add a "user permissions changes" [/doc/trunk/www/alerts.md|subscription]
137 which alerts subscribers when an admin creates a new user or
138 when a user's permissions change.
139 * Show project description on repository list.
140 * Make [/help?cmd=/chat|/chat] better-behaved during server outages, reducing
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141 the frequency of reconnection attempts over time and providing feedback
142 to the user when the connection is down.
143 * Diverse minor fixes and additions.
144
 
 
 
 
145
146 <h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2>
147
148 * The "[/help?cmd=ui|fossil ui /]" command now works even for repositories
149 that have non-ASCII filenames
@@ -1160,12 +1188,11 @@
1160 of rows in a timeline) are held in a cookie and thus persist
1161 across multiple pages.
1162 * Rework the skin editing process so that changes are implemented
1163 on one of nine /draft pages, evaluated, then merged back to the
1164 default.
1165 * Added the [https://fossil-scm.org/skins/ardoise/timeline|Ardoise]
1166 skin.
1167 * Fix the "fossil server" command on Unix to be much more responsive
1168 to multiple simultaneous web requests.
1169 * Use the IPv6 stack for the "fossil ui" and "fossil server"
1170 commands on Windows.
1171 * Support for [https://sqlite.org/sqlar|SQL Archives] as a download
1172
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,43 +1,49 @@
1 <title>Change Log</title>
2
3 <h2 id='v2_27'>Changes for version 2.27 (pending)</h2>
4
5 * <i>(pending)</i>
6
7 <h2 id='v2_26'>Changes for version 2.26 (2025-04-30)</h2><ol>
8 <li>Enhancements to [/help?cmd=diff|fossil diff] and similar:
9 <ol type="a">
10 <li> The argument to the --from option can be a directory name, causing
11 Fossil to use files under that directory as the baseline for the diff.
12 <li> For "gdiff", if no [/help?cmd=gdiff-command|gdiff-command setting]
13 is defined, Fossil tries to do a --tk diff if "tclsh" and "wish"
14 are available, or a --by diff if not.
15 <li> The "Reload" button is added to --tk diffs, to bring the displayed
16 diff up to date with the latest changes on disk.
17 <li> Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show
18 diffs of multiple files.
19 </ol>
20 <li>Added the [/help?cmd=/ckout|/ckout web page] to provide information
21 about pending changes in a working check-out
22 <li>Enhancements to the [/help?cmd=ui|fossil ui] command:
23 <ol type="a">
24 <li> Defaults to using the new [/help?cmd=/ckout|/ckout page] as its
25 start page. Or, if the new "--from PATH" option is present, the
26 default start page becomes "/ckout?exbase=PATH".
27 <li> The new "--extpage FILENAME" option opens the named file as if it
28 where in a [./serverext.wiki|CGI extension]. Example usage: the
29 person editing this change log has
30 "fossil ui --extpage www/changes.wiki" running and hence can
31 press "Reload" on the web browser to view edits.
32 <li> Accept both IPv4 and IPv6 connections on all platforms, including
33 Windows and OpenBSD. This also applies to the "fossil server"
34 command.
35 </ol>
36 <li>Enhancements to [/help?cmd=merge|fossil merge]:
37 <ol type="a">
38 <li> Added the [/help?cmd=merge-info|fossil merge-info] command and
39 especially the --tk option to that command, to provide analysis
40 of the most recent merge or update operation.
41 <li> When a merge conflict occurs, a new section is added to the conflict
42 text that shows Fossil's suggested resolution to the conflict.
43 </ol>
44 <li>Enhancements to [/help?cmd=commit|fossil commit]:
45 <ol type="a">
46 <li> If Fossil sees potential formatting mistakes (ex: bad hyperlinks)
47 in the check-in comment, it will alert the developer and give
48 him or her the opportunity to edit the comment before continuing.
49 This feature is controllable by the
@@ -49,17 +55,17 @@
55 branch has been changed.
56 <li> The interactive checkin comment prompt shows the formatting rules
57 set for that repository.
58 <li> Add the "--editor" option.
59 </ol>
60 <li>Deprecate the --comfmtflags and --comment-format global options and
61 no longer list them in the built-in help, but keep them working for
62 backwards compatibility.
63 Alternative TTY comment formatting can still be specified using the
64 [/help?cmd=comment-format|comment-format setting], if desired. The
65 default comment format is now called "canonical", not "legacy".
66 <li>Enhancements to the [/help?cmd=/timeline|/timeline page]:
67 <ol type="a">
68 <li> Added the "ml=" ("Merge-in List") query parameter that works
69 like "rl=" ("Related List") but adds "mionly" style related
70 check-ins instead of the full "rel" style.
71 <li> For "tl=", "rl=", and "ml=", the order of the branches in the
@@ -82,35 +88,35 @@
88 <li> Accept the "Z" (Zulu-time) suffix on date arguments for the
89 "ymd" and "yw" query parameters.
90 <li> The new "min" query parameter, when added to a from=,to= query,
91 collapses long runs of check-ins on the same branch into just
92 end-points.
93 <li> The p= and d= parameters can now reference different check-ins,
94 in which case the timeline shows those check-ins that are both
95 ancestors of p= and descendants of d=.
96 <li> The saturation and intensity of user-specified checkin and branch
97 background colors are automatically adjusted to keep the colors
98 compatible with the current skin, unless the
99 [/help?cmd=raw-bgcolor|raw-bgcolor setting] is turned on.
100 </ol>
101 <li>The [/help?cmd=/docfile|/docfile webpage] was added. It works like
102 /doc but keeps the title of markdown documents with the document rather
103 that moving it up to the page title.
104 <li>Added the [/help?cmd=/clusterlist|/clusterlist page] for analysis
105 and debugging
106 <li>Added the "artifact_to_json(NAME)" SQL function that returns a JSON
107 decoding of the artifact described by NAME.
108 <li>Improvements to the [/help?cmd=patch|fossil patch] command:
109 <ol type="a">
110 <li> Fix a bug in "fossil patch create" that causes
111 [/help?cmd=revert|fossil revert] operations that happened
112 on individualfiles after a [/help?cmd=merge|fossil merge]
113 to be omitted from the patch.
114 <li> Added the [/help?cmd=patch|patch alias] command for managing
115 aliases for remote checkout names.
116 </ol>
117 <li>Enhancements to on-line help and the [/help?cmd=help|fossil help] command:
118 <ol type="a">
119 <li> Add the ability to search the help text, either in the UI
120 (on the [/help?cmd=/search|/search page]) or from the command-line
121 (using the "[/help?cmd=search|fossil search -h PATTERN]" command.)
122 <li> Accepts an optional SUBCOMMAND argument following the
@@ -117,11 +123,11 @@
123 COMMAND argument and only shows results for the specified
124 subcommand, not the entire command.
125 <li> The -u (--usage) option shows only the command-line syntax
126 <li> The -o (--options) option shows only the command-line options
127 </ol>
128 <li>Enhancements to the [./tickets.wiki|ticket system]:
129 <ol type="a">
130 <li> Added the ability to attach wiki pages to a ticket for extended
131 descriptions.
132 <li> Added submenu to the 'View Ticket' page, to use it as
133 template for a new ticket.
@@ -129,21 +135,43 @@
135 in a row.
136 <li> Link the version field in ticket view to a matching checkin or tag.
137 <li> Show creation time in report and ticket view.
138 <li> Show previous comments in edit ticket as reference.
139 </ol>
140 <li>Added the "hash" query parameter to the
141 [/help?cmd=/whatis|/whatis webpage].
142 <li>Add a "user permissions changes" [/doc/trunk/www/alerts.md|subscription]
143 which alerts subscribers when an admin creates a new user or
144 when a user's permissions change.
145 <li>If the FOSSIL_REPOLIST_SHOW environment variable exists and contains
146 the substring "description", then the project description for each repository
147 is shown on the repository list page. The login-group for each project is
148 now only shown if the FOSSIL_REPOLIST_SHOW environment variable exists and
149 contains the substring "login-group". ([./cgi.wiki#repolist|More information])
150 <li>The [/doc/trunk/www/th1.md|TH1 script language] is enhanced for improved
151 security:
152 <ol type="a">
153 <li> TH1 now makes a distinction between
154 [/doc/trunk/www/th1.md#taint|tainted and untainted string values].
155 This makes it more difficult to write custom TH1 scripts that
156 contain XSS or SQL-injection bugs. The
157 [/help?cmd=vuln-report|vuln-report] setting was added to control
158 what Fossil does when it encounters a potential TH1
159 security problem.
160 <li> The "--th" option was removed from the [/help?cmd=pikchr|fossil pikchr]
161 command.
162 <li> The "enable_htmlify" TH1 command was removed.
163 </ol>
164 <li>Make [/help?cmd=/chat|/chat] better-behaved during server outages, reducing
165 the frequency of reconnection attempts over time and providing feedback
166 to the user when the connection is down.
167 <li>The [/help?cmd=/sqlar|/sqlar] page does not work for users who are not logged
168 in, nor are links to that page displayed to users who are not logged in. Being
169 logged in as "anonymous" is sufficient to overcome this restriction, assuming
170 that "anonymous" can download tarballs and ZIP archives.
171 <li>Many other minor fixes and additions.
172 </ol>
173
174 <h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2>
175
176 * The "[/help?cmd=ui|fossil ui /]" command now works even for repositories
177 that have non-ASCII filenames
@@ -1160,12 +1188,11 @@
1188 of rows in a timeline) are held in a cookie and thus persist
1189 across multiple pages.
1190 * Rework the skin editing process so that changes are implemented
1191 on one of nine /draft pages, evaluated, then merged back to the
1192 default.
1193 * Added the [/timeline?skin=ardoise&once|Ardoise] skin.
 
1194 * Fix the "fossil server" command on Unix to be much more responsive
1195 to multiple simultaneous web requests.
1196 * Use the IPv6 stack for the "fossil ui" and "fossil server"
1197 commands on Windows.
1198 * Support for [https://sqlite.org/sqlar|SQL Archives] as a download
1199
--- www/containers.md
+++ www/containers.md
@@ -670,11 +670,11 @@
670670
671671
[pmmac]: https://podman.io/getting-started/installation.html#macos
672672
[pmwin]: https://github.com/containers/podman/blob/main/docs/tutorials/podman-for-windows.md
673673
[Podman]: https://podman.io/
674674
[rl]: https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md
675
-[whatis]: https://podman.io/whatis.html
675
+[whatis]: https://docs.podman.io/en/latest/index.html
676676
677677
678678
### 6.3 <a id="nspawn"></a>`systemd-container`
679679
680680
If even the Podman stack is too big for you, the next-best option I’m
681681
--- www/containers.md
+++ www/containers.md
@@ -670,11 +670,11 @@
670
671 [pmmac]: https://podman.io/getting-started/installation.html#macos
672 [pmwin]: https://github.com/containers/podman/blob/main/docs/tutorials/podman-for-windows.md
673 [Podman]: https://podman.io/
674 [rl]: https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md
675 [whatis]: https://podman.io/whatis.html
676
677
678 ### 6.3 <a id="nspawn"></a>`systemd-container`
679
680 If even the Podman stack is too big for you, the next-best option I’m
681
--- www/containers.md
+++ www/containers.md
@@ -670,11 +670,11 @@
670
671 [pmmac]: https://podman.io/getting-started/installation.html#macos
672 [pmwin]: https://github.com/containers/podman/blob/main/docs/tutorials/podman-for-windows.md
673 [Podman]: https://podman.io/
674 [rl]: https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md
675 [whatis]: https://docs.podman.io/en/latest/index.html
676
677
678 ### 6.3 <a id="nspawn"></a>`systemd-container`
679
680 If even the Podman stack is too big for you, the next-best option I’m
681
--- www/customskin.md
+++ www/customskin.md
@@ -310,10 +310,11 @@
310310
with the "--skin ./newskin" option. If the argument to the --skin
311311
option contains a "/" character, then the five control files are
312312
read out of the directory named. You can then edit the control
313313
files in the ./newskin folder using you favorite text editor, and
314314
press "Reload" on your browser to see the effects.
315
+
315316
316317
### Disabling The Web Browser Cache During Development
317318
318319
Fossil is aggressive about asking the web browser to cache
319320
resources. While developing a new skin, it is often helpful to
@@ -526,11 +527,46 @@
526527
Iterate until the desired look is achieved.
527528
528529
4. Copy/paste the resulting css.txt, details.txt,
529530
header.txt, and footer.txt files
530531
into the CSS, details, header, and footer configuration screens
531
- under the Admin/Skins menu.
532
+ under the Admin/Skins menu. Alternately, import them using the
533
+ process described below.
534
+
535
+An alternative to step 4 is to convert the skin files into a form
536
+which can be imported into a repository using `fossil config import`.
537
+It requires compiling [a small tool from the fossil source
538
+tree](/file/tools/skintxt2config.c):
539
+
540
+>
541
+```
542
+$ cc -o s2c /path/to/fossil/checkout/tools/skintxt2config.c
543
+```
544
+
545
+With that in place, the custom skin files can be converted with:
546
+
547
+>
548
+```
549
+$ ./s2c yourskin/*.txt > skin.config
550
+```
551
+
552
+It can be imported into an arbitrary fossil repository with:
553
+
554
+>
555
+```
556
+$ fossil config import skin.config
557
+```
558
+
559
+And it can be pushed to a remote repository with:
560
+
561
+>
562
+```
563
+$ fossil config push skin
564
+```
565
+
566
+That approach has proven to be an effective way to locally develop
567
+skin changes then push them to a "live" site.
532568
533569
534570
## See Also
535571
536572
* [Customizing the Timeline Graph](customgraph.md)
537573
--- www/customskin.md
+++ www/customskin.md
@@ -310,10 +310,11 @@
310 with the "--skin ./newskin" option. If the argument to the --skin
311 option contains a "/" character, then the five control files are
312 read out of the directory named. You can then edit the control
313 files in the ./newskin folder using you favorite text editor, and
314 press "Reload" on your browser to see the effects.
 
315
316 ### Disabling The Web Browser Cache During Development
317
318 Fossil is aggressive about asking the web browser to cache
319 resources. While developing a new skin, it is often helpful to
@@ -526,11 +527,46 @@
526 Iterate until the desired look is achieved.
527
528 4. Copy/paste the resulting css.txt, details.txt,
529 header.txt, and footer.txt files
530 into the CSS, details, header, and footer configuration screens
531 under the Admin/Skins menu.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
532
533
534 ## See Also
535
536 * [Customizing the Timeline Graph](customgraph.md)
537
--- www/customskin.md
+++ www/customskin.md
@@ -310,10 +310,11 @@
310 with the "--skin ./newskin" option. If the argument to the --skin
311 option contains a "/" character, then the five control files are
312 read out of the directory named. You can then edit the control
313 files in the ./newskin folder using you favorite text editor, and
314 press "Reload" on your browser to see the effects.
315
316
317 ### Disabling The Web Browser Cache During Development
318
319 Fossil is aggressive about asking the web browser to cache
320 resources. While developing a new skin, it is often helpful to
@@ -526,11 +527,46 @@
527 Iterate until the desired look is achieved.
528
529 4. Copy/paste the resulting css.txt, details.txt,
530 header.txt, and footer.txt files
531 into the CSS, details, header, and footer configuration screens
532 under the Admin/Skins menu. Alternately, import them using the
533 process described below.
534
535 An alternative to step 4 is to convert the skin files into a form
536 which can be imported into a repository using `fossil config import`.
537 It requires compiling [a small tool from the fossil source
538 tree](/file/tools/skintxt2config.c):
539
540 >
541 ```
542 $ cc -o s2c /path/to/fossil/checkout/tools/skintxt2config.c
543 ```
544
545 With that in place, the custom skin files can be converted with:
546
547 >
548 ```
549 $ ./s2c yourskin/*.txt > skin.config
550 ```
551
552 It can be imported into an arbitrary fossil repository with:
553
554 >
555 ```
556 $ fossil config import skin.config
557 ```
558
559 And it can be pushed to a remote repository with:
560
561 >
562 ```
563 $ fossil config push skin
564 ```
565
566 That approach has proven to be an effective way to locally develop
567 skin changes then push them to a "live" site.
568
569
570 ## See Also
571
572 * [Customizing the Timeline Graph](customgraph.md)
573
--- www/fossil-v-git.wiki
+++ www/fossil-v-git.wiki
@@ -342,11 +342,11 @@
342342
Fossil isn't entirely C and SQL code. Its web UI [./javascript.md |
343343
uses JavaScript where
344344
necessary]. The server-side
345345
UI scripting uses a custom minimal
346346
[https://en.wikipedia.org/wiki/Tcl|Tcl] dialect called
347
-[https://fossil-scm.org/xfer/doc/trunk/www/th1.md|TH1], which is
347
+[./th1.md|TH1], which is
348348
embedded into Fossil itself. Fossil's build system and test suite are
349349
largely based on Tcl.⁵ All of this is quite portable.
350350
351351
About half of Git's code is POSIX C, and about a third is POSIX shell
352352
code. This is largely why the so-called "Git for Windows" distributions
353353
--- www/fossil-v-git.wiki
+++ www/fossil-v-git.wiki
@@ -342,11 +342,11 @@
342 Fossil isn't entirely C and SQL code. Its web UI [./javascript.md |
343 uses JavaScript where
344 necessary]. The server-side
345 UI scripting uses a custom minimal
346 [https://en.wikipedia.org/wiki/Tcl|Tcl] dialect called
347 [https://fossil-scm.org/xfer/doc/trunk/www/th1.md|TH1], which is
348 embedded into Fossil itself. Fossil's build system and test suite are
349 largely based on Tcl.⁵ All of this is quite portable.
350
351 About half of Git's code is POSIX C, and about a third is POSIX shell
352 code. This is largely why the so-called "Git for Windows" distributions
353
--- www/fossil-v-git.wiki
+++ www/fossil-v-git.wiki
@@ -342,11 +342,11 @@
342 Fossil isn't entirely C and SQL code. Its web UI [./javascript.md |
343 uses JavaScript where
344 necessary]. The server-side
345 UI scripting uses a custom minimal
346 [https://en.wikipedia.org/wiki/Tcl|Tcl] dialect called
347 [./th1.md|TH1], which is
348 embedded into Fossil itself. Fossil's build system and test suite are
349 largely based on Tcl.⁵ All of this is quite portable.
350
351 About half of Git's code is POSIX C, and about a third is POSIX shell
352 code. This is largely why the so-called "Git for Windows" distributions
353
--- www/gsoc-ideas.md
+++ www/gsoc-ideas.md
@@ -24,11 +24,11 @@
2424
# UI, Look and Feel
2525
2626
Tasks for those interested in graphic/web design:
2727
2828
* Add a quote button to the Forum, such as [discussed in this thread](https://fossil-scm.org/forum/forumpost/7ad03cd73d)
29
-* Improve the documentation history-browsing page to enable selection of 2 arbitrary versions to diff, similar to the [Mediawiki history feature enabled on Wikipedia](https://en.wikipedia.org/w/index.php?title=Fossil_(software)&action=history)
29
+* Improve the documentation history-browsing page to enable selection of 2 arbitrary versions to diff, similar to the [Mediawiki history feature enabled on Wikipedia](https://en.wikipedia.org/w/index.php?title=Fossil_\(software\)&action=history)
3030
* Allow diffing of Forum posts
3131
* General touch-ups in the existing skins. This may, depending on how deep one
3232
cares to dig, require digging into C code to find, and potentially modify, how
3333
the HTML is generated.
3434
* Creation of one or more new skins. This does not specifically require any C
3535
--- www/gsoc-ideas.md
+++ www/gsoc-ideas.md
@@ -24,11 +24,11 @@
24 # UI, Look and Feel
25
26 Tasks for those interested in graphic/web design:
27
28 * Add a quote button to the Forum, such as [discussed in this thread](https://fossil-scm.org/forum/forumpost/7ad03cd73d)
29 * Improve the documentation history-browsing page to enable selection of 2 arbitrary versions to diff, similar to the [Mediawiki history feature enabled on Wikipedia](https://en.wikipedia.org/w/index.php?title=Fossil_(software)&action=history)
30 * Allow diffing of Forum posts
31 * General touch-ups in the existing skins. This may, depending on how deep one
32 cares to dig, require digging into C code to find, and potentially modify, how
33 the HTML is generated.
34 * Creation of one or more new skins. This does not specifically require any C
35
--- www/gsoc-ideas.md
+++ www/gsoc-ideas.md
@@ -24,11 +24,11 @@
24 # UI, Look and Feel
25
26 Tasks for those interested in graphic/web design:
27
28 * Add a quote button to the Forum, such as [discussed in this thread](https://fossil-scm.org/forum/forumpost/7ad03cd73d)
29 * Improve the documentation history-browsing page to enable selection of 2 arbitrary versions to diff, similar to the [Mediawiki history feature enabled on Wikipedia](https://en.wikipedia.org/w/index.php?title=Fossil_\(software\)&action=history)
30 * Allow diffing of Forum posts
31 * General touch-ups in the existing skins. This may, depending on how deep one
32 cares to dig, require digging into C code to find, and potentially modify, how
33 the HTML is generated.
34 * Creation of one or more new skins. This does not specifically require any C
35
+4 -4
--- www/index.wiki
+++ www/index.wiki
@@ -84,16 +84,16 @@
8484
the repository are consistent prior to each commit.
8585
8686
8. <b>Free and Open-Source</b> — [../COPYRIGHT-BSD2.txt|2-clause BSD license].
8787
8888
<hr>
89
-<h3>Latest Release: 2.25 ([/timeline?c=version-2.25|2024-11-06])</h3>
89
+<h3>Latest Release: 2.26 ([/timeline?c=version-2.26|2025-04-30])</h3>
9090
9191
* [/uv/download.html|Download]
92
- * [./changes.wiki#v2_25|Change Summary]
93
- * [/timeline?p=version-2.25&bt=version-2.24&y=ci|Check-ins in version 2.25]
94
- * [/timeline?df=version-2.25&y=ci|Check-ins derived from the 2.25 release]
92
+ * [./changes.wiki#v2_26|Change Summary]
93
+ * [/timeline?p=version-2.26&d=version-2.25&y=ci|Check-ins in version 2.26]
94
+ * [/timeline?df=version-2.26&y=ci|Check-ins derived from the 2.26 release]
9595
* [/timeline?t=release|Timeline of all past releases]
9696
9797
<hr>
9898
<h3>Quick Start</h3>
9999
100100
--- www/index.wiki
+++ www/index.wiki
@@ -84,16 +84,16 @@
84 the repository are consistent prior to each commit.
85
86 8. <b>Free and Open-Source</b> — [../COPYRIGHT-BSD2.txt|2-clause BSD license].
87
88 <hr>
89 <h3>Latest Release: 2.25 ([/timeline?c=version-2.25|2024-11-06])</h3>
90
91 * [/uv/download.html|Download]
92 * [./changes.wiki#v2_25|Change Summary]
93 * [/timeline?p=version-2.25&bt=version-2.24&y=ci|Check-ins in version 2.25]
94 * [/timeline?df=version-2.25&y=ci|Check-ins derived from the 2.25 release]
95 * [/timeline?t=release|Timeline of all past releases]
96
97 <hr>
98 <h3>Quick Start</h3>
99
100
--- www/index.wiki
+++ www/index.wiki
@@ -84,16 +84,16 @@
84 the repository are consistent prior to each commit.
85
86 8. <b>Free and Open-Source</b> — [../COPYRIGHT-BSD2.txt|2-clause BSD license].
87
88 <hr>
89 <h3>Latest Release: 2.26 ([/timeline?c=version-2.26|2025-04-30])</h3>
90
91 * [/uv/download.html|Download]
92 * [./changes.wiki#v2_26|Change Summary]
93 * [/timeline?p=version-2.26&d=version-2.25&y=ci|Check-ins in version 2.26]
94 * [/timeline?df=version-2.26&y=ci|Check-ins derived from the 2.26 release]
95 * [/timeline?t=release|Timeline of all past releases]
96
97 <hr>
98 <h3>Quick Start</h3>
99
100
--- www/quickstart.wiki
+++ www/quickstart.wiki
@@ -19,15 +19,13 @@
1919
This is fossil version 2.25 [8f798279d5] 2024-11-06 12:59:09 UTC
2020
</b></pre>
2121
2222
<h2 id="workflow" name="fslclone">General Work Flow</h2>
2323
24
-Fossil works with repository files (a database in a single file with the project's
25
-complete history) and with checked-out local trees (the working directory
26
-you use to do your work).
27
-(See [./glossary.md | the glossary] for more background.)
28
-The workflow looks like this:
24
+Fossil works with [./glossary.md#repository | repository files]
25
+and [./glossary.md#check-out | check-out directories] using a
26
+workflow like this:
2927
3028
<ul>
3129
<li>Create or clone a repository file. ([/help/init|fossil init] or
3230
[/help/clone | fossil clone])
3331
<li>Check out a local tree. ([/help/open | fossil open])
@@ -41,12 +39,11 @@
4139
The following sections give a brief overview of these
4240
operations.
4341
4442
<h2 id="new">Starting A New Project</h2>
4543
46
-To start a new project with fossil create a new empty repository
47
-this way: ([/help/init | more info])
44
+To start a new project with Fossil, [/help/init | create a new empty repository]:
4845
4946
<pre><b>fossil init</b> <i>repository-filename</i>
5047
</pre>
5148
5249
You can name the database anything you like, and you can place it anywhere in the filesystem.
@@ -82,14 +79,14 @@
8279
<h2 id="clone">Cloning An Existing Repository</h2>
8380
8481
Most fossil operations interact with a repository that is on the
8582
local disk drive, not on a remote system. Hence, before accessing
8683
a remote repository it is necessary to make a local copy of that
87
-repository. Making a local copy of a remote repository is called
88
-"cloning".
84
+repository, a process called
85
+"[/help/clone | cloning]".
8986
90
-Clone a remote repository as follows: ([/help/clone | more info])
87
+This is done as follows:
9188
9289
<pre><b>fossil clone</b> <i>URL repository-filename</i>
9390
</pre>
9491
9592
The <i>URL</i> specifies the fossil repository
@@ -107,12 +104,20 @@
107104
100% complete...
108105
Extra delta compression...
109106
Vacuuming the database...
110107
project-id: 94259BB9F186226D80E49D1FA2DB29F935CCA0333
111108
server-id: 016595e9043054038a9ea9bc526d7f33f7ac0e42
112
-admin-user: exampleuser (password is "yoWgDR42iv")>
109
+admin-user: exampleuser (intial remote-access password is "yoWgDR42iv")>
113110
</b></pre>
111
+
112
+This <i>exampleuser</i> will be used by Fossil as the author of commits when
113
+you checkin changes to the repository. It is also used by Fossil when you
114
+make your repository available to others using the built-in server mode by
115
+running <tt>[/help/server | fossil server]</tt> and will also be used when
116
+running <tt>[/help/ui | fossil ui]</tt> to view the repository through
117
+the Fossil UI. See the quick start topic for setting up a
118
+<a href="#server">server</a> for more details.
114119
115120
If the remote repository requires a login, include a
116121
userid in the URL like this:
117122
118123
<pre><b>fossil clone https://</b><i>remoteuserid</i><b>@www.example.org/ myclone.fossil</b></pre>
@@ -153,26 +158,23 @@
153158
154159
<h2 id="checkout">Checking Out A Local Tree</h2>
155160
156161
To work on a project in fossil, you need to check out a local
157162
copy of the source tree. Create the directory you want to be
158
-the root of your tree and cd into that directory. Then
159
-do this: ([/help/open | more info])
163
+the root of your tree, <tt>cd</tt> into that directory, and then:
160164
161165
<pre><b>fossil open</b> <i>repository-filename</i></pre>
162166
163
-for example:
167
+For example:
164168
165169
<pre><b>fossil open ../myclone.fossil
166170
BUILD.txt
167171
COPYRIGHT-BSD2.txt
168172
README.md
169173
170174
</tt></b></pre>
171175
172
-(or "fossil open ..\myclone.fossil" on Windows).
173
-
174176
This leaves you with the newest version of the tree
175177
checked out.
176178
From anywhere underneath the root of your local tree, you
177179
can type commands like the following to find out the status of
178180
your local tree:
@@ -320,41 +322,60 @@
320322
321323
This will get you started on identifying checkins. The
322324
<a href="./checkin_names.wiki">Checkin Names document</a> is a complete reference, including
323325
how timestamps can also be used.
324326
325
-<h2 id="config">Configuring Your Local Repository</h2>
327
+<h2 id="config">Accessing Your Local Repository's Web User Interface</h2>
326328
327
-When you create a new repository, either by cloning an existing
328
-project or create a new project of your own, you usually want to do some
329
-local configuration. This is easily accomplished using the web-server
330
-that is built into fossil. Start the fossil web server like this:
331
-([/help/ui | more info])
329
+After you create a new repository, you usually want to do some local
330
+configuration. This is most easily accomplished by firing up the Fossil
331
+UI:
332332
333333
<pre>
334334
<b>fossil ui</b> <i>repository-filename</i>
335335
</pre>
336336
337
-You can omit the <i>repository-filename</i> from the command above
337
+You can shorten that to just [/help/ui | <b>fossil ui</b>]
338338
if you are inside a checked-out local tree.
339339
340
-This starts a web server then automatically launches your
341
-web browser and makes it point to this web server. If your system
342
-has an unusual configuration, fossil might not be able to figure out
343
-how to start your web browser. In that case, first tell fossil
344
-where to find your web browser using a command like this:
340
+This command starts an internal web server, after which Fossil
341
+automatically launches your default browser, pointed at itself,
342
+presenting a special view of the repository, its web user interface.
343
+
344
+You may override Fossil's logic for selecting the default browser so:
345345
346346
<pre>
347347
<b>fossil setting web-browser</b> <i>path-to-web-browser</i>
348348
</pre>
349349
350
-By default, fossil does not require a login for HTTP connections
351
-coming in from the IP loopback address 127.0.0.1. You can, and perhaps
352
-should, change this after you create a few users.
350
+When launched this way, Fossil binds its internal web server to the IP
351
+loopback address, 127.0.0.1, which it treats specially, bypassing all
352
+user controls, effectively giving visitors the
353
+[./caps/admin-v-setup.md#apsu | all-powerful Setup capabliity].
354
+
355
+Why is that a good idea, you ask? Because it is a safe
356
+presumption that only someone with direct file access to the repository
357
+database file could be using the resulting web interface. Anyone who can
358
+modify the repo DB directly could give themselves any and all access
359
+with a SQL query, or even by direct file manipulation; no amount of
360
+access control matters to such a user.
361
+
362
+(Contrast the [#server | many <i>other</i> ways] of setting Fossil up
363
+as an HTTP server, where the repo DB is on the other side of the HTTP
364
+server wall, inaccessible by all means other than Fossil's own
365
+mediation. For this reason, the "localhost bypasses access control"
366
+policy does <i>not</i> apply to these other interfaces. That is a very
367
+good thing, since without this difference in policy, it would be unsafe
368
+to bind a [/help?cmd=server | <b>fossil server</b>] instance to
369
+localhost on a high-numbered port and then reverse-proxy it out to the
370
+world via HTTPS, a practice this author does engage in, with confidence.)
353371
354
-When you are finished configuring, just press Control-C or use
355
-the <b>kill</b> command to shut down the mini-server.
372
+Once you are finished configuring Fossil, you may safely Control-C out
373
+of the <b>fossil&nbsp;ui</b> command to shut down this privileged
374
+built-in web server. Moreover, you may by grace of SQLite do this <i>at
375
+any time</i>: all changes are either committed durably to the repo DB or
376
+rolled back, in their totality. This includes configuration changes.
356377
357378
<h2 id="sharing">Sharing Changes</h2>
358379
359380
When [./concepts.wiki#workflow|autosync] is turned off,
360381
the changes you [/help/commit | commit] are only
@@ -464,55 +485,44 @@
464485
level of undo/redo.
465486
466487
467488
<h2 id="server">Setting Up A Server</h2>
468489
469
-Fossil can act as a stand-alone web server using one of these
470
-commands:
490
+In addition to the inward-facing <b>fossil ui</b> mode covered [#config
491
+| above], Fossil can also act as an outward-facing web server:
471492
472493
<pre>
473494
<b>[/help/server | fossil server]</b> <i>repository-filename</i>
474
-<b>[/help/ui | fossil ui]</b> <i>repository-filename</i>
475495
</pre>
476496
477
-The <i>repository-filename</i> can be omitted when these commands
478
-are run from within an open check-out, which is a particularly useful
479
-shortcut with the <b>fossil ui</b> command.
480
-
481
-The <b>ui</b> command is intended for accessing the web user interface
482
-from a local desktop. (We sometimes call this mode "Fossil UI.")
483
-The <b>ui</b> command differs from the
484
-<b>server</b> command by binding to the loopback IP
485
-address only (thus making the web UI visible only on the
486
-local machine) and by automatically starting your default web browser,
487
-pointing it at the running UI
488
-server. The localhost restriction exists because it also gives anyone
489
-who can access the resulting web UI full control over the
490
-repository. (This is the [./caps/admin-v-setup.md#apsu | all-powerful
491
-Setup capabliity].)
492
-
493
-For cross-machine collaboration, use the <b>server</b> command instead,
494
-which binds on all IP addresses, does not try to start a web browser,
495
-and enforces [./caps/ | Fossil's role-based access control system].
496
-
497
-Servers are also easily configured as:
497
+Just as with <b>fossil ui</b>, you may omit the
498
+<i>repository-filename</i> parameter when running this from within an open
499
+check-out.
500
+
501
+<i>Unlike</i> <b>fossil ui</b> mode, Fossil binds to all network
502
+interfaces by default in this mode, and it enforces the configured
503
+[./caps/ | role-based access controls]. Further, because it is meant to
504
+provide external web service, it doesn't try to launch a local web
505
+browser pointing to a "Fossil UI" presentation; external visitors see
506
+your repository's configured home page instead.
507
+
508
+To serve varying needs, there are additional ways to serve a Fossil repo
509
+to external users:
498510
499511
<ul>
512
+<li>[./server/any/cgi.md|CGI], as used by Fossil's [./selfhost.wiki |
513
+ self-hosting repositories]
514
+<li>[./server/any/scgi.md|SCGI]
500515
<li>[./server/any/inetd.md|inetd]
501516
<li>[./server/debian/service.md|systemd]
502
-<li>[./server/any/cgi.md|CGI]
503
-<li>[./server/any/scgi.md|SCGI]
504517
</ul>
505518
506519
…along with [./server/#matrix | several other options].
507520
508
-The [./selfhost.wiki | self-hosting fossil repositories] use
509
-CGI.
510
-
511
-You might <i>need</i> to set up a server, whether you know it yet or
512
-not. See the [./server/whyuseaserver.wiki | Benefits of a Fossil Server]
513
-article for details.
521
+We recommend that you read the [./server/whyuseaserver.wiki | Benefits
522
+of a Fossil Server] article, because you might <i>need</i> to do this
523
+and not yet know it.
514524
515525
<h2 id="proxy">HTTP Proxies</h2>
516526
517527
If you are behind a restrictive firewall that requires you to use
518528
an HTTP proxy to reach the internet, then you can configure the proxy
519529
--- www/quickstart.wiki
+++ www/quickstart.wiki
@@ -19,15 +19,13 @@
19 This is fossil version 2.25 [8f798279d5] 2024-11-06 12:59:09 UTC
20 </b></pre>
21
22 <h2 id="workflow" name="fslclone">General Work Flow</h2>
23
24 Fossil works with repository files (a database in a single file with the project's
25 complete history) and with checked-out local trees (the working directory
26 you use to do your work).
27 (See [./glossary.md | the glossary] for more background.)
28 The workflow looks like this:
29
30 <ul>
31 <li>Create or clone a repository file. ([/help/init|fossil init] or
32 [/help/clone | fossil clone])
33 <li>Check out a local tree. ([/help/open | fossil open])
@@ -41,12 +39,11 @@
41 The following sections give a brief overview of these
42 operations.
43
44 <h2 id="new">Starting A New Project</h2>
45
46 To start a new project with fossil create a new empty repository
47 this way: ([/help/init | more info])
48
49 <pre><b>fossil init</b> <i>repository-filename</i>
50 </pre>
51
52 You can name the database anything you like, and you can place it anywhere in the filesystem.
@@ -82,14 +79,14 @@
82 <h2 id="clone">Cloning An Existing Repository</h2>
83
84 Most fossil operations interact with a repository that is on the
85 local disk drive, not on a remote system. Hence, before accessing
86 a remote repository it is necessary to make a local copy of that
87 repository. Making a local copy of a remote repository is called
88 "cloning".
89
90 Clone a remote repository as follows: ([/help/clone | more info])
91
92 <pre><b>fossil clone</b> <i>URL repository-filename</i>
93 </pre>
94
95 The <i>URL</i> specifies the fossil repository
@@ -107,12 +104,20 @@
107 100% complete...
108 Extra delta compression...
109 Vacuuming the database...
110 project-id: 94259BB9F186226D80E49D1FA2DB29F935CCA0333
111 server-id: 016595e9043054038a9ea9bc526d7f33f7ac0e42
112 admin-user: exampleuser (password is "yoWgDR42iv")>
113 </b></pre>
 
 
 
 
 
 
 
 
114
115 If the remote repository requires a login, include a
116 userid in the URL like this:
117
118 <pre><b>fossil clone https://</b><i>remoteuserid</i><b>@www.example.org/ myclone.fossil</b></pre>
@@ -153,26 +158,23 @@
153
154 <h2 id="checkout">Checking Out A Local Tree</h2>
155
156 To work on a project in fossil, you need to check out a local
157 copy of the source tree. Create the directory you want to be
158 the root of your tree and cd into that directory. Then
159 do this: ([/help/open | more info])
160
161 <pre><b>fossil open</b> <i>repository-filename</i></pre>
162
163 for example:
164
165 <pre><b>fossil open ../myclone.fossil
166 BUILD.txt
167 COPYRIGHT-BSD2.txt
168 README.md
169
170 </tt></b></pre>
171
172 (or "fossil open ..\myclone.fossil" on Windows).
173
174 This leaves you with the newest version of the tree
175 checked out.
176 From anywhere underneath the root of your local tree, you
177 can type commands like the following to find out the status of
178 your local tree:
@@ -320,41 +322,60 @@
320
321 This will get you started on identifying checkins. The
322 <a href="./checkin_names.wiki">Checkin Names document</a> is a complete reference, including
323 how timestamps can also be used.
324
325 <h2 id="config">Configuring Your Local Repository</h2>
326
327 When you create a new repository, either by cloning an existing
328 project or create a new project of your own, you usually want to do some
329 local configuration. This is easily accomplished using the web-server
330 that is built into fossil. Start the fossil web server like this:
331 ([/help/ui | more info])
332
333 <pre>
334 <b>fossil ui</b> <i>repository-filename</i>
335 </pre>
336
337 You can omit the <i>repository-filename</i> from the command above
338 if you are inside a checked-out local tree.
339
340 This starts a web server then automatically launches your
341 web browser and makes it point to this web server. If your system
342 has an unusual configuration, fossil might not be able to figure out
343 how to start your web browser. In that case, first tell fossil
344 where to find your web browser using a command like this:
345
346 <pre>
347 <b>fossil setting web-browser</b> <i>path-to-web-browser</i>
348 </pre>
349
350 By default, fossil does not require a login for HTTP connections
351 coming in from the IP loopback address 127.0.0.1. You can, and perhaps
352 should, change this after you create a few users.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
353
354 When you are finished configuring, just press Control-C or use
355 the <b>kill</b> command to shut down the mini-server.
 
 
 
356
357 <h2 id="sharing">Sharing Changes</h2>
358
359 When [./concepts.wiki#workflow|autosync] is turned off,
360 the changes you [/help/commit | commit] are only
@@ -464,55 +485,44 @@
464 level of undo/redo.
465
466
467 <h2 id="server">Setting Up A Server</h2>
468
469 Fossil can act as a stand-alone web server using one of these
470 commands:
471
472 <pre>
473 <b>[/help/server | fossil server]</b> <i>repository-filename</i>
474 <b>[/help/ui | fossil ui]</b> <i>repository-filename</i>
475 </pre>
476
477 The <i>repository-filename</i> can be omitted when these commands
478 are run from within an open check-out, which is a particularly useful
479 shortcut with the <b>fossil ui</b> command.
480
481 The <b>ui</b> command is intended for accessing the web user interface
482 from a local desktop. (We sometimes call this mode "Fossil UI.")
483 The <b>ui</b> command differs from the
484 <b>server</b> command by binding to the loopback IP
485 address only (thus making the web UI visible only on the
486 local machine) and by automatically starting your default web browser,
487 pointing it at the running UI
488 server. The localhost restriction exists because it also gives anyone
489 who can access the resulting web UI full control over the
490 repository. (This is the [./caps/admin-v-setup.md#apsu | all-powerful
491 Setup capabliity].)
492
493 For cross-machine collaboration, use the <b>server</b> command instead,
494 which binds on all IP addresses, does not try to start a web browser,
495 and enforces [./caps/ | Fossil's role-based access control system].
496
497 Servers are also easily configured as:
498
499 <ul>
 
 
 
500 <li>[./server/any/inetd.md|inetd]
501 <li>[./server/debian/service.md|systemd]
502 <li>[./server/any/cgi.md|CGI]
503 <li>[./server/any/scgi.md|SCGI]
504 </ul>
505
506 …along with [./server/#matrix | several other options].
507
508 The [./selfhost.wiki | self-hosting fossil repositories] use
509 CGI.
510
511 You might <i>need</i> to set up a server, whether you know it yet or
512 not. See the [./server/whyuseaserver.wiki | Benefits of a Fossil Server]
513 article for details.
514
515 <h2 id="proxy">HTTP Proxies</h2>
516
517 If you are behind a restrictive firewall that requires you to use
518 an HTTP proxy to reach the internet, then you can configure the proxy
519
--- www/quickstart.wiki
+++ www/quickstart.wiki
@@ -19,15 +19,13 @@
19 This is fossil version 2.25 [8f798279d5] 2024-11-06 12:59:09 UTC
20 </b></pre>
21
22 <h2 id="workflow" name="fslclone">General Work Flow</h2>
23
24 Fossil works with [./glossary.md#repository | repository files]
25 and [./glossary.md#check-out | check-out directories] using a
26 workflow like this:
 
 
27
28 <ul>
29 <li>Create or clone a repository file. ([/help/init|fossil init] or
30 [/help/clone | fossil clone])
31 <li>Check out a local tree. ([/help/open | fossil open])
@@ -41,12 +39,11 @@
39 The following sections give a brief overview of these
40 operations.
41
42 <h2 id="new">Starting A New Project</h2>
43
44 To start a new project with Fossil, [/help/init | create a new empty repository]:
 
45
46 <pre><b>fossil init</b> <i>repository-filename</i>
47 </pre>
48
49 You can name the database anything you like, and you can place it anywhere in the filesystem.
@@ -82,14 +79,14 @@
79 <h2 id="clone">Cloning An Existing Repository</h2>
80
81 Most fossil operations interact with a repository that is on the
82 local disk drive, not on a remote system. Hence, before accessing
83 a remote repository it is necessary to make a local copy of that
84 repository, a process called
85 "[/help/clone | cloning]".
86
87 This is done as follows:
88
89 <pre><b>fossil clone</b> <i>URL repository-filename</i>
90 </pre>
91
92 The <i>URL</i> specifies the fossil repository
@@ -107,12 +104,20 @@
104 100% complete...
105 Extra delta compression...
106 Vacuuming the database...
107 project-id: 94259BB9F186226D80E49D1FA2DB29F935CCA0333
108 server-id: 016595e9043054038a9ea9bc526d7f33f7ac0e42
109 admin-user: exampleuser (intial remote-access password is "yoWgDR42iv")>
110 </b></pre>
111
112 This <i>exampleuser</i> will be used by Fossil as the author of commits when
113 you checkin changes to the repository. It is also used by Fossil when you
114 make your repository available to others using the built-in server mode by
115 running <tt>[/help/server | fossil server]</tt> and will also be used when
116 running <tt>[/help/ui | fossil ui]</tt> to view the repository through
117 the Fossil UI. See the quick start topic for setting up a
118 <a href="#server">server</a> for more details.
119
120 If the remote repository requires a login, include a
121 userid in the URL like this:
122
123 <pre><b>fossil clone https://</b><i>remoteuserid</i><b>@www.example.org/ myclone.fossil</b></pre>
@@ -153,26 +158,23 @@
158
159 <h2 id="checkout">Checking Out A Local Tree</h2>
160
161 To work on a project in fossil, you need to check out a local
162 copy of the source tree. Create the directory you want to be
163 the root of your tree, <tt>cd</tt> into that directory, and then:
 
164
165 <pre><b>fossil open</b> <i>repository-filename</i></pre>
166
167 For example:
168
169 <pre><b>fossil open ../myclone.fossil
170 BUILD.txt
171 COPYRIGHT-BSD2.txt
172 README.md
173
174 </tt></b></pre>
175
 
 
176 This leaves you with the newest version of the tree
177 checked out.
178 From anywhere underneath the root of your local tree, you
179 can type commands like the following to find out the status of
180 your local tree:
@@ -320,41 +322,60 @@
322
323 This will get you started on identifying checkins. The
324 <a href="./checkin_names.wiki">Checkin Names document</a> is a complete reference, including
325 how timestamps can also be used.
326
327 <h2 id="config">Accessing Your Local Repository's Web User Interface</h2>
328
329 After you create a new repository, you usually want to do some local
330 configuration. This is most easily accomplished by firing up the Fossil
331 UI:
 
 
332
333 <pre>
334 <b>fossil ui</b> <i>repository-filename</i>
335 </pre>
336
337 You can shorten that to just [/help/ui | <b>fossil ui</b>]
338 if you are inside a checked-out local tree.
339
340 This command starts an internal web server, after which Fossil
341 automatically launches your default browser, pointed at itself,
342 presenting a special view of the repository, its web user interface.
343
344 You may override Fossil's logic for selecting the default browser so:
345
346 <pre>
347 <b>fossil setting web-browser</b> <i>path-to-web-browser</i>
348 </pre>
349
350 When launched this way, Fossil binds its internal web server to the IP
351 loopback address, 127.0.0.1, which it treats specially, bypassing all
352 user controls, effectively giving visitors the
353 [./caps/admin-v-setup.md#apsu | all-powerful Setup capabliity].
354
355 Why is that a good idea, you ask? Because it is a safe
356 presumption that only someone with direct file access to the repository
357 database file could be using the resulting web interface. Anyone who can
358 modify the repo DB directly could give themselves any and all access
359 with a SQL query, or even by direct file manipulation; no amount of
360 access control matters to such a user.
361
362 (Contrast the [#server | many <i>other</i> ways] of setting Fossil up
363 as an HTTP server, where the repo DB is on the other side of the HTTP
364 server wall, inaccessible by all means other than Fossil's own
365 mediation. For this reason, the "localhost bypasses access control"
366 policy does <i>not</i> apply to these other interfaces. That is a very
367 good thing, since without this difference in policy, it would be unsafe
368 to bind a [/help?cmd=server | <b>fossil server</b>] instance to
369 localhost on a high-numbered port and then reverse-proxy it out to the
370 world via HTTPS, a practice this author does engage in, with confidence.)
371
372 Once you are finished configuring Fossil, you may safely Control-C out
373 of the <b>fossil&nbsp;ui</b> command to shut down this privileged
374 built-in web server. Moreover, you may by grace of SQLite do this <i>at
375 any time</i>: all changes are either committed durably to the repo DB or
376 rolled back, in their totality. This includes configuration changes.
377
378 <h2 id="sharing">Sharing Changes</h2>
379
380 When [./concepts.wiki#workflow|autosync] is turned off,
381 the changes you [/help/commit | commit] are only
@@ -464,55 +485,44 @@
485 level of undo/redo.
486
487
488 <h2 id="server">Setting Up A Server</h2>
489
490 In addition to the inward-facing <b>fossil ui</b> mode covered [#config
491 | above], Fossil can also act as an outward-facing web server:
492
493 <pre>
494 <b>[/help/server | fossil server]</b> <i>repository-filename</i>
 
495 </pre>
496
497 Just as with <b>fossil ui</b>, you may omit the
498 <i>repository-filename</i> parameter when running this from within an open
499 check-out.
500
501 <i>Unlike</i> <b>fossil ui</b> mode, Fossil binds to all network
502 interfaces by default in this mode, and it enforces the configured
503 [./caps/ | role-based access controls]. Further, because it is meant to
504 provide external web service, it doesn't try to launch a local web
505 browser pointing to a "Fossil UI" presentation; external visitors see
506 your repository's configured home page instead.
507
508 To serve varying needs, there are additional ways to serve a Fossil repo
509 to external users:
 
 
 
 
 
 
 
 
510
511 <ul>
512 <li>[./server/any/cgi.md|CGI], as used by Fossil's [./selfhost.wiki |
513 self-hosting repositories]
514 <li>[./server/any/scgi.md|SCGI]
515 <li>[./server/any/inetd.md|inetd]
516 <li>[./server/debian/service.md|systemd]
 
 
517 </ul>
518
519 …along with [./server/#matrix | several other options].
520
521 We recommend that you read the [./server/whyuseaserver.wiki | Benefits
522 of a Fossil Server] article, because you might <i>need</i> to do this
523 and not yet know it.
 
 
 
524
525 <h2 id="proxy">HTTP Proxies</h2>
526
527 If you are behind a restrictive firewall that requires you to use
528 an HTTP proxy to reach the internet, then you can configure the proxy
529
+16 -18
--- www/reviews.wiki
+++ www/reviews.wiki
@@ -35,33 +35,31 @@
3535
</div>
3636
3737
<b>Stephan Beal writes on 2009-01-11:</b>
3838
3939
<div class="indent">
40
-Sometime in late 2007 I came across a link to fossil on
41
-<a href="http://www.sqlite.org/">sqlite.org</a>. It
42
-was a good thing I bookmarked it, because I was never able to find the
43
-link again (it might have been in a bug report or something). The
44
-reasons I first took a close look at it were (A) it stemmed from the
45
-sqlite project, which I've held in high regards for years (e.g. I
46
-wrote JavaScript bindings for it:
47
-<a href="http://spiderape.sourceforge.net/plugins/sqlite/">
48
-http://spiderape.sourceforge.net/plugins/sqlite/</a>), and (B) it could
49
-run as a CGI. That second point might seem a bit archaic, but in
50
-practice CGI is the only way most hosted sites can set up a shared
51
-source repository with multiple user IDs. (i'm not about to give out
52
-my only account password or SSH key for my hosted sites, no matter how
53
-much I trust the other developers, and none of my hosters allow me to
54
-run standalone servers or add Apache modules.)
40
+Sometime in late 2007 I came across a link to fossil on <a
41
+href="https://sqlite.org/">sqlite.org</a>. It was a good thing I
42
+bookmarked it, because I was never able to find the link again (it
43
+might have been in a bug report or something). The reasons I first
44
+took a close look at it were (A) it stemmed from the sqlite project,
45
+which I've held in high regards for years (e.g. I wrote bindings for
46
+it for Mozilla's SpiderMonkey JavaScript engine), and (B) it could run
47
+as a CGI. That second point might seem a bit archaic, but in practice
48
+CGI is the only way most hosted sites can set up a shared source
49
+repository with multiple user IDs. (i'm not about to give out my only
50
+account password or SSH key for my hosted sites, no matter how much I
51
+trust the other developers, and none of my hosters allow me to run
52
+standalone servers or add Apache modules.)
5553
5654
So I tried it out. The thing which bugged me most about it was having
5755
to type "commit" or "com" instead of "ci" for checking in (as is
5856
custom in all other systems I've used), despite the fact that fossil
5957
uses "ci" as a filter in things like the timeline view. Looking back
6058
now, I have used fossil for about about 95% of my work in the past
61
-year (<a href="http://blog.s11n.net/?p=71"><i>dead link</i></a>), in
62
-over 15 source trees, and I now get tripped up when I have to use svn or cvs.
59
+year, in over 15 source trees, and I now get tripped up when I have to
60
+use svn or cvs.
6361
6462
So, having got over typing "fossil com -m ...", here's why I love it so much...
6563
6664
Point #1: CGI
6765
@@ -70,11 +68,11 @@
7068
(they don't belong to those projects), which I cannot host in google
7169
code (because google code doesn't allow/recognize Public Domain as a
7270
license, and I refuse to relicense just to accommodate them), and for
7371
which SourceForge is overkill (and way too slow). With fossil I can
7472
create a new repo, have it installed on my hoster
75
-(http://fossil.wanderinghorse.net), and be commiting code to it within
73
+(https://fossil.wanderinghorse.net), and be commiting code to it within
7674
5 minutes.
7775
7876
Point #2: Wiki
7977
8078
I hate wikis. I really do. Always have. They all have a different
8179
--- www/reviews.wiki
+++ www/reviews.wiki
@@ -35,33 +35,31 @@
35 </div>
36
37 <b>Stephan Beal writes on 2009-01-11:</b>
38
39 <div class="indent">
40 Sometime in late 2007 I came across a link to fossil on
41 <a href="http://www.sqlite.org/">sqlite.org</a>. It
42 was a good thing I bookmarked it, because I was never able to find the
43 link again (it might have been in a bug report or something). The
44 reasons I first took a close look at it were (A) it stemmed from the
45 sqlite project, which I've held in high regards for years (e.g. I
46 wrote JavaScript bindings for it:
47 <a href="http://spiderape.sourceforge.net/plugins/sqlite/">
48 http://spiderape.sourceforge.net/plugins/sqlite/</a>), and (B) it could
49 run as a CGI. That second point might seem a bit archaic, but in
50 practice CGI is the only way most hosted sites can set up a shared
51 source repository with multiple user IDs. (i'm not about to give out
52 my only account password or SSH key for my hosted sites, no matter how
53 much I trust the other developers, and none of my hosters allow me to
54 run standalone servers or add Apache modules.)
55
56 So I tried it out. The thing which bugged me most about it was having
57 to type "commit" or "com" instead of "ci" for checking in (as is
58 custom in all other systems I've used), despite the fact that fossil
59 uses "ci" as a filter in things like the timeline view. Looking back
60 now, I have used fossil for about about 95% of my work in the past
61 year (<a href="http://blog.s11n.net/?p=71"><i>dead link</i></a>), in
62 over 15 source trees, and I now get tripped up when I have to use svn or cvs.
63
64 So, having got over typing "fossil com -m ...", here's why I love it so much...
65
66 Point #1: CGI
67
@@ -70,11 +68,11 @@
70 (they don't belong to those projects), which I cannot host in google
71 code (because google code doesn't allow/recognize Public Domain as a
72 license, and I refuse to relicense just to accommodate them), and for
73 which SourceForge is overkill (and way too slow). With fossil I can
74 create a new repo, have it installed on my hoster
75 (http://fossil.wanderinghorse.net), and be commiting code to it within
76 5 minutes.
77
78 Point #2: Wiki
79
80 I hate wikis. I really do. Always have. They all have a different
81
--- www/reviews.wiki
+++ www/reviews.wiki
@@ -35,33 +35,31 @@
35 </div>
36
37 <b>Stephan Beal writes on 2009-01-11:</b>
38
39 <div class="indent">
40 Sometime in late 2007 I came across a link to fossil on <a
41 href="https://sqlite.org/">sqlite.org</a>. It was a good thing I
42 bookmarked it, because I was never able to find the link again (it
43 might have been in a bug report or something). The reasons I first
44 took a close look at it were (A) it stemmed from the sqlite project,
45 which I've held in high regards for years (e.g. I wrote bindings for
46 it for Mozilla's SpiderMonkey JavaScript engine), and (B) it could run
47 as a CGI. That second point might seem a bit archaic, but in practice
48 CGI is the only way most hosted sites can set up a shared source
49 repository with multiple user IDs. (i'm not about to give out my only
50 account password or SSH key for my hosted sites, no matter how much I
51 trust the other developers, and none of my hosters allow me to run
52 standalone servers or add Apache modules.)
 
 
53
54 So I tried it out. The thing which bugged me most about it was having
55 to type "commit" or "com" instead of "ci" for checking in (as is
56 custom in all other systems I've used), despite the fact that fossil
57 uses "ci" as a filter in things like the timeline view. Looking back
58 now, I have used fossil for about about 95% of my work in the past
59 year, in over 15 source trees, and I now get tripped up when I have to
60 use svn or cvs.
61
62 So, having got over typing "fossil com -m ...", here's why I love it so much...
63
64 Point #1: CGI
65
@@ -70,11 +68,11 @@
68 (they don't belong to those projects), which I cannot host in google
69 code (because google code doesn't allow/recognize Public Domain as a
70 license, and I refuse to relicense just to accommodate them), and for
71 which SourceForge is overkill (and way too slow). With fossil I can
72 create a new repo, have it installed on my hoster
73 (https://fossil.wanderinghorse.net), and be commiting code to it within
74 5 minutes.
75
76 Point #2: Wiki
77
78 I hate wikis. I really do. Always have. They all have a different
79
+107 -29
--- www/th1.md
+++ www/th1.md
@@ -12,29 +12,43 @@
1212
time all of the test cases for SQLite were written in Tcl and Tcl could not
1313
be easily compiled on the SymbianOS. So TH1 was developed as a cut-down
1414
version of Tcl that would facilitate running the SQLite test scripts on
1515
SymbianOS.
1616
17
-Fossil was first being designed at about the same time that TH1 was
18
-being developed for testing SQLite on SymbianOS.
17
+Fossil was first being designed at about the same time.
1918
Early prototypes of Fossil were written in pure Tcl. But as the development
2019
shifted toward the use of C-code, the need arose to have a Tcl-like
2120
scripting language to help with code generation. TH1 was small and
2221
light-weight and used minimal resources and seemed ideally suited for the
2322
task.
2423
25
-The name "TH1" stands "Test Harness 1", since that was its original purpose.
24
+The name "TH1" stands for "Test Harness 1",
25
+since its original purpose was to serve as testing harness
26
+for SQLite.
2627
27
-Overview
---------
28
+Where TH1 Is Used In Fossil
29
+---------------------------
30
+
31
+ * In the header and footer for [skins](./customskin.md)
32
+ text within `<th1>...</th1>` is run as a TH1 script.
33
+ ([example](/builtin/skins/default/header.txt))
34
+
35
+ * This display of [tickets](./bugtheory.wiki) is controlled by TH1
36
+ scripts, so that the ticket format can be customized for each
37
+ project. Administrators can visit the <b>/tktsetup</b> page in
38
+ their repositories to view and customize these scripts.
39
+ ([example usage](./custom_ticket.wiki))
40
+
41
+Overview Of The Tcl/TH1 Language
42
+--------------------------------
2843
2944
TH1 is a string-processing language. All values are strings. Any numerical
3045
operations are accomplished by converting from string to numeric, performing
3146
the computation, then converting the result back into a string. (This might
3247
seem inefficient, but it is faster than people imagine, and numeric
3348
computations do not come up very often for the kinds of work that TH1 does,
34
-so it has never been a factor.)
49
+so it has never been an issue.)
3550
3651
A TH1 script consists of a sequence of commands.
3752
Each command is terminated by the first *unescaped* newline or ";" character.
3853
The text of the command (excluding the newline or semicolon terminator)
3954
is broken into space-separated tokens. The first token is the command
@@ -68,11 +82,11 @@
6882
are removed from each token by the command parser.) The third token
6983
is the `puts "hello"`, with its whitespace and newlines. The fourth token
7084
is `else` and the fifth and last token is `puts "world"`.
7185
7286
The `if` command evaluates its first argument (the second token)
73
-as an expression, and if that expression is true, evaluates its
87
+as an expression, and if that expression is true, it evaluates its
7488
second argument (the third token) as a TH1 script.
7589
If the expression is false and the third argument is `else`, then
7690
the fourth argument is evaluated as a TH1 expression.
7791
7892
So, you see, even though the example above spans five lines, it is really
@@ -106,10 +120,49 @@
106120
$repository "" info trunk]]] end]
107121
108122
Those backslashes allow the command to wrap nicely within a standard
109123
terminal width while telling the interpreter to consider those three
110124
lines as a single command.
125
+
126
+<a id="taint"></a>Tainted And Untainted Strings
127
+-----------------------------------------------
128
+
129
+Beginning with Fossil version 2.26 (circa 2025), TH1 distinguishes between
130
+"tainted" and "untainted" strings. Tainted strings are strings that are
131
+derived from user inputs that might contain text that is designed to subvert
132
+the script. Untainted strings are known to come from secure sources and
133
+are assumed to contain no malicious content.
134
+
135
+Beginning with Fossil version 2.26, and depending on the value of the
136
+[vuln-report setting](/help?cmd=vuln-report), TH1 will prevent tainted
137
+strings from being used in ways that might lead to XSS or SQL-injection
138
+attacks. This feature helps to ensure that XSS and SQL-injection
139
+vulnerabilities are not *accidentally* added to Fossil when
140
+custom TH1 scripts for headers or footers or tickets are added to a
141
+repository. Note that the tainted/untainted distinction in strings does
142
+not make it impossible to introduce XSS and SQL-injections vulnerabilities
143
+using poorly-written TH1 scripts; it just makes it more difficult and
144
+less likely to happen by accident. Developers must still consider the
145
+security implications TH1 customizations they add to Fossil, and take
146
+appropriate precautions when writing custom TH1. Peer review of TH1
147
+script changes is encouraged.
148
+
149
+In Fossil version 2.26, if the vuln-report setting is set to "block"
150
+or "fatal", the [html](#html) and [query](#query) TH1 commands will
151
+fail with an error if their argument is a tainted string. This helps
152
+to prevent XSS and SQL-injection attacks, respectively. Note that
153
+the default value of the vuln-report setting is "log", which allows those
154
+commands to continue working and only writes a warning message into the
155
+error log. <b>Future versions of Fossil may change the default value
156
+of the vuln-report setting to "block" or "fatal".</b> Fossil users
157
+with customized TH1 scripts are encouraged to audit their customizations
158
+and fix any potential vulnerabilities soon, so as to avoid breakage
159
+caused by future upgrades. <b>Future versions of Fossil might also
160
+place additional restrictions on the use of tainted strings.</b>
161
+For example, it is likely that future versions of Fossil will disallow
162
+using tainted strings as script, for example as the body of a "for"
163
+loop or of a "proc".
111164
112165
113166
Summary of Core TH1 Commands
114167
----------------------------
115168
@@ -147,10 +200,13 @@
147200
* string last NEEDLE HAYSTACK ?START-INDEX?
148201
* string match PATTERN STRING
149202
* string length STRING
150203
* string range STRING FIRST LAST
151204
* string repeat STRING COUNT
205
+ * string trim STRING
206
+ * string trimleft STRING
207
+ * string trimright STRING
152208
* unset VARNAME
153209
* uplevel ?LEVEL? SCRIPT
154210
* upvar ?FRAME? OTHERVAR MYVAR ?OTHERVAR MYVAR?
155211
156212
All of the above commands work as in the original Tcl. Refer to the
@@ -182,11 +238,10 @@
182238
* [copybtn](#copybtn)
183239
* [date](#date)
184240
* [decorate](#decorate)
185241
* [defHeader](#defHeader)
186242
* [dir](#dir)
187
- * [enable\_htmlify](#enable_htmlify)
188243
* [enable\_output](#enable_output)
189244
* [encode64](#encode64)
190245
* [getParameter](#getParameter)
191246
* [glob\_match](#glob_match)
192247
* [globalState](#globalState)
@@ -214,17 +269,19 @@
214269
* [stime](#stime)
215270
* [styleHeader](#styleHeader)
216271
* [styleFooter](#styleFooter)
217272
* [styleScript](#styleScript)
218273
* [submenu](#submenu)
274
+ * [taint](#taintCmd)
219275
* [tclEval](#tclEval)
220276
* [tclExpr](#tclExpr)
221277
* [tclInvoke](#tclInvoke)
222278
* [tclIsSafe](#tclIsSafe)
223279
* [tclMakeSafe](#tclMakeSafe)
224280
* [tclReady](#tclReady)
225281
* [trace](#trace)
282
+ * [untaint](#untaintCmd)
226283
* [unversioned content](#unversioned_content)
227284
* [unversioned list](#unversioned_list)
228285
* [utime](#utime)
229286
* [verifyCsrf](#verifyCsrf)
230287
* [verifyLogin](#verifyLogin)
@@ -413,28 +470,10 @@
413470
the files matching the pattern GLOB within CHECKIN will be returned.
414471
If DETAILS is non-zero, the result will be a list-of-lists, with each
415472
element containing at least three elements: the file name, the file
416473
size (in bytes), and the file last modification time (relative to the
417474
time zone configured for the repository).
418
-
419
-<a id="enable_htmlify"></a>TH1 enable\_htmlify Command
-------------------------------------------------------
420
-
421
- * enable\_htmlify
422
- * enable\_htmlify ?TRACE-LABEL? BOOLEAN
423
-
424
-By default, certain output from `puts` and similar commands is escaped
425
-for HTML. The first call form returns the current state of that
426
-feature: `1` for on and `0` for off. The second call form enables
427
-(non-0) or disables (0) that feature and returns the *pre-call* state
428
-of that feature (so that a second call can pass that value to restore
429
-it to its previous state). The optional `TRACE-LABEL` argument causes
430
-the TH1 tracing output (if enabled) to add a marker when the second
431
-form of this command is invoked, and includes that label and the
432
-boolean argument's value in the trace. If tracing is disabled, that
433
-argument has no effect.
434
-
435475
436476
<a id="enable_output"></a>TH1 enable\_output Command
437477
------------------------------------------------------
438478
439479
* enable\_output BOOLEAN
@@ -528,22 +567,24 @@
528567
-----------------------------------
529568
530569
* html STRING
531570
532571
Outputs the STRING literally. It is assumed that STRING contains
533
-valid HTML, or that if it text then any characters that are
572
+valid HTML, or that if STRING contains any characters that are
534573
significant to HTML (such as `<`, `>`, `'`, or `&`) have already
535
-been escaped, perhaps by the htmlize command. Use the
574
+been escaped, perhaps by the [htmlize](#htmlize) command. Use the
536575
[puts](#puts) command to output text that might contain unescaped
537576
HTML markup.
538577
539578
**Beware of XSS attacks!** If the STRING value to the html command
540579
can be controlled by a hostile user, then he might be able to sneak
541580
in malicious HTML or Javascript which could result in a
542581
cross-site scripting (XSS) attack. Be careful that all text that
543582
in STRING that might come from user input has been sanitized by the
544
-[htmlize](#htmlize) command or similar.
583
+[htmlize](#htmlize) command or similar. In recent versions of Fossil,
584
+the STRING value must be [untainted](#taint) or else the "html" command
585
+will fail.
545586
546587
<a id="htmlize"></a>TH1 htmlize Command
547588
-----------------------------------------
548589
549590
* htmlize STRING
@@ -610,11 +651,13 @@
610651
* puts STRING
611652
612653
Outputs STRING. Characters within STRING that have special meaning
613654
in HTML are escaped prior to being output. Thus is it safe for STRING
614655
to be derived from user inputs. See also the [html](#html) command
615
-which behaves similarly except does not escape HTML markup.
656
+which behaves similarly except does not escape HTML markup. This
657
+command ("puts") is safe to use on [tainted strings](#taint), but the "html"
658
+command is not.
616659
617660
<a id="query"></a>TH1 query Command
618661
-------------------------------------
619662
620663
* query ?-nocomplain? SQL CODE
@@ -622,11 +665,14 @@
622665
Runs the SQL query given by the SQL argument. For each row in the result
623666
set, run CODE.
624667
625668
In SQL, parameters such as $var are filled in using the value of variable
626669
"var". Result values are stored in variables with the column name prior
627
-to each invocation of CODE.
670
+to each invocation of CODE. The names of the variables in which results
671
+are stored can be controlled using "AS name" clauses in the SQL. As
672
+the database will often contain content that originates from untrusted
673
+users, all result values are marked as [tainted](#taint).
628674
629675
**Beware of SQL injections in the `query` command!**
630676
The SQL argument to the query command should always be literal SQL
631677
text enclosed in {...}. The SQL argument should never be a double-quoted
632678
string or the value of a \$variable, as those constructs can lead to
@@ -649,10 +695,14 @@
649695
~~~
650696
651697
In this second example, TH1 does the expansion of `$mykey` prior to passing
652698
the text down into SQLite. So if `$mykey` contains a single-quote character,
653699
followed by additional hostile text, that will result in an SQL injection.
700
+
701
+To help guard against SQL-injections, recent versions of Fossil require
702
+that the SQL argument be [untainted](#taint) or else the "query" command
703
+will fail.
654704
655705
<a id="randhex"></a>TH1 randhex Command
656706
-----------------------------------------
657707
658708
* randhex N
@@ -783,10 +833,24 @@
783833
784834
* submenu link LABEL URL
785835
786836
Add hyperlink to the submenu of the current page.
787837
838
+<a id="taintCmd"></a>TH1 taint Command
839
+-----------------------------------------
840
+
841
+ * taint STRING
842
+
843
+This command returns a copy of STRING that has been marked as
844
+[tainted](#taint). Tainted strings are strings which might be
845
+controlled by an attacker and might contain hostile inputs and
846
+are thus unsafe to use in certain contexts. For example, tainted
847
+strings should not be output as part of a webpage as they might
848
+contain rogue HTML or Javascript that could lead to an XSS
849
+vulnerability. Similarly, tainted strings should not be run as
850
+SQL since they might contain an SQL-injection vulerability.
851
+
788852
<a id="tclEval"></a>TH1 tclEval Command
789853
-----------------------------------------
790854
791855
**This command requires the Tcl integration feature.**
792856
@@ -854,10 +918,22 @@
854918
855919
* trace STRING
856920
857921
Generates a TH1 trace message if TH1 tracing is enabled.
858922
923
+<a id="untaintCmd"></a>TH1 taint Command
924
+-----------------------------------------
925
+
926
+ * untaint STRING
927
+
928
+This command returns a copy of STRING that has been marked as
929
+[untainted](#taint). Untainted strings are strings which are
930
+believed to be free of potentially hostile content. Use this
931
+command with caution, as it overwrites the tainted-string protection
932
+mechanisms that are built into TH1. If you do not understand all
933
+the implications of executing this command, then do not use it.
934
+
859935
<a id="unversioned_content"></a>TH1 unversioned content Command
860936
-----------------------------------------------------------------
861937
862938
* unversioned content FILENAME
863939
864940
--- www/th1.md
+++ www/th1.md
@@ -12,29 +12,43 @@
12 time all of the test cases for SQLite were written in Tcl and Tcl could not
13 be easily compiled on the SymbianOS. So TH1 was developed as a cut-down
14 version of Tcl that would facilitate running the SQLite test scripts on
15 SymbianOS.
16
17 Fossil was first being designed at about the same time that TH1 was
18 being developed for testing SQLite on SymbianOS.
19 Early prototypes of Fossil were written in pure Tcl. But as the development
20 shifted toward the use of C-code, the need arose to have a Tcl-like
21 scripting language to help with code generation. TH1 was small and
22 light-weight and used minimal resources and seemed ideally suited for the
23 task.
24
25 The name "TH1" stands "Test Harness 1", since that was its original purpose.
 
 
26
27 Overview
---------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
29 TH1 is a string-processing language. All values are strings. Any numerical
30 operations are accomplished by converting from string to numeric, performing
31 the computation, then converting the result back into a string. (This might
32 seem inefficient, but it is faster than people imagine, and numeric
33 computations do not come up very often for the kinds of work that TH1 does,
34 so it has never been a factor.)
35
36 A TH1 script consists of a sequence of commands.
37 Each command is terminated by the first *unescaped* newline or ";" character.
38 The text of the command (excluding the newline or semicolon terminator)
39 is broken into space-separated tokens. The first token is the command
@@ -68,11 +82,11 @@
68 are removed from each token by the command parser.) The third token
69 is the `puts "hello"`, with its whitespace and newlines. The fourth token
70 is `else` and the fifth and last token is `puts "world"`.
71
72 The `if` command evaluates its first argument (the second token)
73 as an expression, and if that expression is true, evaluates its
74 second argument (the third token) as a TH1 script.
75 If the expression is false and the third argument is `else`, then
76 the fourth argument is evaluated as a TH1 expression.
77
78 So, you see, even though the example above spans five lines, it is really
@@ -106,10 +120,49 @@
106 $repository "" info trunk]]] end]
107
108 Those backslashes allow the command to wrap nicely within a standard
109 terminal width while telling the interpreter to consider those three
110 lines as a single command.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
112
113 Summary of Core TH1 Commands
114 ----------------------------
115
@@ -147,10 +200,13 @@
147 * string last NEEDLE HAYSTACK ?START-INDEX?
148 * string match PATTERN STRING
149 * string length STRING
150 * string range STRING FIRST LAST
151 * string repeat STRING COUNT
 
 
 
152 * unset VARNAME
153 * uplevel ?LEVEL? SCRIPT
154 * upvar ?FRAME? OTHERVAR MYVAR ?OTHERVAR MYVAR?
155
156 All of the above commands work as in the original Tcl. Refer to the
@@ -182,11 +238,10 @@
182 * [copybtn](#copybtn)
183 * [date](#date)
184 * [decorate](#decorate)
185 * [defHeader](#defHeader)
186 * [dir](#dir)
187 * [enable\_htmlify](#enable_htmlify)
188 * [enable\_output](#enable_output)
189 * [encode64](#encode64)
190 * [getParameter](#getParameter)
191 * [glob\_match](#glob_match)
192 * [globalState](#globalState)
@@ -214,17 +269,19 @@
214 * [stime](#stime)
215 * [styleHeader](#styleHeader)
216 * [styleFooter](#styleFooter)
217 * [styleScript](#styleScript)
218 * [submenu](#submenu)
 
219 * [tclEval](#tclEval)
220 * [tclExpr](#tclExpr)
221 * [tclInvoke](#tclInvoke)
222 * [tclIsSafe](#tclIsSafe)
223 * [tclMakeSafe](#tclMakeSafe)
224 * [tclReady](#tclReady)
225 * [trace](#trace)
 
226 * [unversioned content](#unversioned_content)
227 * [unversioned list](#unversioned_list)
228 * [utime](#utime)
229 * [verifyCsrf](#verifyCsrf)
230 * [verifyLogin](#verifyLogin)
@@ -413,28 +470,10 @@
413 the files matching the pattern GLOB within CHECKIN will be returned.
414 If DETAILS is non-zero, the result will be a list-of-lists, with each
415 element containing at least three elements: the file name, the file
416 size (in bytes), and the file last modification time (relative to the
417 time zone configured for the repository).
418
419 <a id="enable_htmlify"></a>TH1 enable\_htmlify Command
-------------------------------------------------------
420
421 * enable\_htmlify
422 * enable\_htmlify ?TRACE-LABEL? BOOLEAN
423
424 By default, certain output from `puts` and similar commands is escaped
425 for HTML. The first call form returns the current state of that
426 feature: `1` for on and `0` for off. The second call form enables
427 (non-0) or disables (0) that feature and returns the *pre-call* state
428 of that feature (so that a second call can pass that value to restore
429 it to its previous state). The optional `TRACE-LABEL` argument causes
430 the TH1 tracing output (if enabled) to add a marker when the second
431 form of this command is invoked, and includes that label and the
432 boolean argument's value in the trace. If tracing is disabled, that
433 argument has no effect.
434
435
436 <a id="enable_output"></a>TH1 enable\_output Command
437 ------------------------------------------------------
438
439 * enable\_output BOOLEAN
@@ -528,22 +567,24 @@
528 -----------------------------------
529
530 * html STRING
531
532 Outputs the STRING literally. It is assumed that STRING contains
533 valid HTML, or that if it text then any characters that are
534 significant to HTML (such as `<`, `>`, `'`, or `&`) have already
535 been escaped, perhaps by the htmlize command. Use the
536 [puts](#puts) command to output text that might contain unescaped
537 HTML markup.
538
539 **Beware of XSS attacks!** If the STRING value to the html command
540 can be controlled by a hostile user, then he might be able to sneak
541 in malicious HTML or Javascript which could result in a
542 cross-site scripting (XSS) attack. Be careful that all text that
543 in STRING that might come from user input has been sanitized by the
544 [htmlize](#htmlize) command or similar.
 
 
545
546 <a id="htmlize"></a>TH1 htmlize Command
547 -----------------------------------------
548
549 * htmlize STRING
@@ -610,11 +651,13 @@
610 * puts STRING
611
612 Outputs STRING. Characters within STRING that have special meaning
613 in HTML are escaped prior to being output. Thus is it safe for STRING
614 to be derived from user inputs. See also the [html](#html) command
615 which behaves similarly except does not escape HTML markup.
 
 
616
617 <a id="query"></a>TH1 query Command
618 -------------------------------------
619
620 * query ?-nocomplain? SQL CODE
@@ -622,11 +665,14 @@
622 Runs the SQL query given by the SQL argument. For each row in the result
623 set, run CODE.
624
625 In SQL, parameters such as $var are filled in using the value of variable
626 "var". Result values are stored in variables with the column name prior
627 to each invocation of CODE.
 
 
 
628
629 **Beware of SQL injections in the `query` command!**
630 The SQL argument to the query command should always be literal SQL
631 text enclosed in {...}. The SQL argument should never be a double-quoted
632 string or the value of a \$variable, as those constructs can lead to
@@ -649,10 +695,14 @@
649 ~~~
650
651 In this second example, TH1 does the expansion of `$mykey` prior to passing
652 the text down into SQLite. So if `$mykey` contains a single-quote character,
653 followed by additional hostile text, that will result in an SQL injection.
 
 
 
 
654
655 <a id="randhex"></a>TH1 randhex Command
656 -----------------------------------------
657
658 * randhex N
@@ -783,10 +833,24 @@
783
784 * submenu link LABEL URL
785
786 Add hyperlink to the submenu of the current page.
787
 
 
 
 
 
 
 
 
 
 
 
 
 
 
788 <a id="tclEval"></a>TH1 tclEval Command
789 -----------------------------------------
790
791 **This command requires the Tcl integration feature.**
792
@@ -854,10 +918,22 @@
854
855 * trace STRING
856
857 Generates a TH1 trace message if TH1 tracing is enabled.
858
 
 
 
 
 
 
 
 
 
 
 
 
859 <a id="unversioned_content"></a>TH1 unversioned content Command
860 -----------------------------------------------------------------
861
862 * unversioned content FILENAME
863
864
--- www/th1.md
+++ www/th1.md
@@ -12,29 +12,43 @@
12 time all of the test cases for SQLite were written in Tcl and Tcl could not
13 be easily compiled on the SymbianOS. So TH1 was developed as a cut-down
14 version of Tcl that would facilitate running the SQLite test scripts on
15 SymbianOS.
16
17 Fossil was first being designed at about the same time.
 
18 Early prototypes of Fossil were written in pure Tcl. But as the development
19 shifted toward the use of C-code, the need arose to have a Tcl-like
20 scripting language to help with code generation. TH1 was small and
21 light-weight and used minimal resources and seemed ideally suited for the
22 task.
23
24 The name "TH1" stands for "Test Harness 1",
25 since its original purpose was to serve as testing harness
26 for SQLite.
27
 
---------
28 Where TH1 Is Used In Fossil
29 ---------------------------
30
31 * In the header and footer for [skins](./customskin.md)
32 text within `<th1>...</th1>` is run as a TH1 script.
33 ([example](/builtin/skins/default/header.txt))
34
35 * This display of [tickets](./bugtheory.wiki) is controlled by TH1
36 scripts, so that the ticket format can be customized for each
37 project. Administrators can visit the <b>/tktsetup</b> page in
38 their repositories to view and customize these scripts.
39 ([example usage](./custom_ticket.wiki))
40
41 Overview Of The Tcl/TH1 Language
42 --------------------------------
43
44 TH1 is a string-processing language. All values are strings. Any numerical
45 operations are accomplished by converting from string to numeric, performing
46 the computation, then converting the result back into a string. (This might
47 seem inefficient, but it is faster than people imagine, and numeric
48 computations do not come up very often for the kinds of work that TH1 does,
49 so it has never been an issue.)
50
51 A TH1 script consists of a sequence of commands.
52 Each command is terminated by the first *unescaped* newline or ";" character.
53 The text of the command (excluding the newline or semicolon terminator)
54 is broken into space-separated tokens. The first token is the command
@@ -68,11 +82,11 @@
82 are removed from each token by the command parser.) The third token
83 is the `puts "hello"`, with its whitespace and newlines. The fourth token
84 is `else` and the fifth and last token is `puts "world"`.
85
86 The `if` command evaluates its first argument (the second token)
87 as an expression, and if that expression is true, it evaluates its
88 second argument (the third token) as a TH1 script.
89 If the expression is false and the third argument is `else`, then
90 the fourth argument is evaluated as a TH1 expression.
91
92 So, you see, even though the example above spans five lines, it is really
@@ -106,10 +120,49 @@
120 $repository "" info trunk]]] end]
121
122 Those backslashes allow the command to wrap nicely within a standard
123 terminal width while telling the interpreter to consider those three
124 lines as a single command.
125
126 <a id="taint"></a>Tainted And Untainted Strings
127 -----------------------------------------------
128
129 Beginning with Fossil version 2.26 (circa 2025), TH1 distinguishes between
130 "tainted" and "untainted" strings. Tainted strings are strings that are
131 derived from user inputs that might contain text that is designed to subvert
132 the script. Untainted strings are known to come from secure sources and
133 are assumed to contain no malicious content.
134
135 Beginning with Fossil version 2.26, and depending on the value of the
136 [vuln-report setting](/help?cmd=vuln-report), TH1 will prevent tainted
137 strings from being used in ways that might lead to XSS or SQL-injection
138 attacks. This feature helps to ensure that XSS and SQL-injection
139 vulnerabilities are not *accidentally* added to Fossil when
140 custom TH1 scripts for headers or footers or tickets are added to a
141 repository. Note that the tainted/untainted distinction in strings does
142 not make it impossible to introduce XSS and SQL-injections vulnerabilities
143 using poorly-written TH1 scripts; it just makes it more difficult and
144 less likely to happen by accident. Developers must still consider the
145 security implications TH1 customizations they add to Fossil, and take
146 appropriate precautions when writing custom TH1. Peer review of TH1
147 script changes is encouraged.
148
149 In Fossil version 2.26, if the vuln-report setting is set to "block"
150 or "fatal", the [html](#html) and [query](#query) TH1 commands will
151 fail with an error if their argument is a tainted string. This helps
152 to prevent XSS and SQL-injection attacks, respectively. Note that
153 the default value of the vuln-report setting is "log", which allows those
154 commands to continue working and only writes a warning message into the
155 error log. <b>Future versions of Fossil may change the default value
156 of the vuln-report setting to "block" or "fatal".</b> Fossil users
157 with customized TH1 scripts are encouraged to audit their customizations
158 and fix any potential vulnerabilities soon, so as to avoid breakage
159 caused by future upgrades. <b>Future versions of Fossil might also
160 place additional restrictions on the use of tainted strings.</b>
161 For example, it is likely that future versions of Fossil will disallow
162 using tainted strings as script, for example as the body of a "for"
163 loop or of a "proc".
164
165
166 Summary of Core TH1 Commands
167 ----------------------------
168
@@ -147,10 +200,13 @@
200 * string last NEEDLE HAYSTACK ?START-INDEX?
201 * string match PATTERN STRING
202 * string length STRING
203 * string range STRING FIRST LAST
204 * string repeat STRING COUNT
205 * string trim STRING
206 * string trimleft STRING
207 * string trimright STRING
208 * unset VARNAME
209 * uplevel ?LEVEL? SCRIPT
210 * upvar ?FRAME? OTHERVAR MYVAR ?OTHERVAR MYVAR?
211
212 All of the above commands work as in the original Tcl. Refer to the
@@ -182,11 +238,10 @@
238 * [copybtn](#copybtn)
239 * [date](#date)
240 * [decorate](#decorate)
241 * [defHeader](#defHeader)
242 * [dir](#dir)
 
243 * [enable\_output](#enable_output)
244 * [encode64](#encode64)
245 * [getParameter](#getParameter)
246 * [glob\_match](#glob_match)
247 * [globalState](#globalState)
@@ -214,17 +269,19 @@
269 * [stime](#stime)
270 * [styleHeader](#styleHeader)
271 * [styleFooter](#styleFooter)
272 * [styleScript](#styleScript)
273 * [submenu](#submenu)
274 * [taint](#taintCmd)
275 * [tclEval](#tclEval)
276 * [tclExpr](#tclExpr)
277 * [tclInvoke](#tclInvoke)
278 * [tclIsSafe](#tclIsSafe)
279 * [tclMakeSafe](#tclMakeSafe)
280 * [tclReady](#tclReady)
281 * [trace](#trace)
282 * [untaint](#untaintCmd)
283 * [unversioned content](#unversioned_content)
284 * [unversioned list](#unversioned_list)
285 * [utime](#utime)
286 * [verifyCsrf](#verifyCsrf)
287 * [verifyLogin](#verifyLogin)
@@ -413,28 +470,10 @@
470 the files matching the pattern GLOB within CHECKIN will be returned.
471 If DETAILS is non-zero, the result will be a list-of-lists, with each
472 element containing at least three elements: the file name, the file
473 size (in bytes), and the file last modification time (relative to the
474 time zone configured for the repository).
 
 
-------------------------------------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
475
476 <a id="enable_output"></a>TH1 enable\_output Command
477 ------------------------------------------------------
478
479 * enable\_output BOOLEAN
@@ -528,22 +567,24 @@
567 -----------------------------------
568
569 * html STRING
570
571 Outputs the STRING literally. It is assumed that STRING contains
572 valid HTML, or that if STRING contains any characters that are
573 significant to HTML (such as `<`, `>`, `'`, or `&`) have already
574 been escaped, perhaps by the [htmlize](#htmlize) command. Use the
575 [puts](#puts) command to output text that might contain unescaped
576 HTML markup.
577
578 **Beware of XSS attacks!** If the STRING value to the html command
579 can be controlled by a hostile user, then he might be able to sneak
580 in malicious HTML or Javascript which could result in a
581 cross-site scripting (XSS) attack. Be careful that all text that
582 in STRING that might come from user input has been sanitized by the
583 [htmlize](#htmlize) command or similar. In recent versions of Fossil,
584 the STRING value must be [untainted](#taint) or else the "html" command
585 will fail.
586
587 <a id="htmlize"></a>TH1 htmlize Command
588 -----------------------------------------
589
590 * htmlize STRING
@@ -610,11 +651,13 @@
651 * puts STRING
652
653 Outputs STRING. Characters within STRING that have special meaning
654 in HTML are escaped prior to being output. Thus is it safe for STRING
655 to be derived from user inputs. See also the [html](#html) command
656 which behaves similarly except does not escape HTML markup. This
657 command ("puts") is safe to use on [tainted strings](#taint), but the "html"
658 command is not.
659
660 <a id="query"></a>TH1 query Command
661 -------------------------------------
662
663 * query ?-nocomplain? SQL CODE
@@ -622,11 +665,14 @@
665 Runs the SQL query given by the SQL argument. For each row in the result
666 set, run CODE.
667
668 In SQL, parameters such as $var are filled in using the value of variable
669 "var". Result values are stored in variables with the column name prior
670 to each invocation of CODE. The names of the variables in which results
671 are stored can be controlled using "AS name" clauses in the SQL. As
672 the database will often contain content that originates from untrusted
673 users, all result values are marked as [tainted](#taint).
674
675 **Beware of SQL injections in the `query` command!**
676 The SQL argument to the query command should always be literal SQL
677 text enclosed in {...}. The SQL argument should never be a double-quoted
678 string or the value of a \$variable, as those constructs can lead to
@@ -649,10 +695,14 @@
695 ~~~
696
697 In this second example, TH1 does the expansion of `$mykey` prior to passing
698 the text down into SQLite. So if `$mykey` contains a single-quote character,
699 followed by additional hostile text, that will result in an SQL injection.
700
701 To help guard against SQL-injections, recent versions of Fossil require
702 that the SQL argument be [untainted](#taint) or else the "query" command
703 will fail.
704
705 <a id="randhex"></a>TH1 randhex Command
706 -----------------------------------------
707
708 * randhex N
@@ -783,10 +833,24 @@
833
834 * submenu link LABEL URL
835
836 Add hyperlink to the submenu of the current page.
837
838 <a id="taintCmd"></a>TH1 taint Command
839 -----------------------------------------
840
841 * taint STRING
842
843 This command returns a copy of STRING that has been marked as
844 [tainted](#taint). Tainted strings are strings which might be
845 controlled by an attacker and might contain hostile inputs and
846 are thus unsafe to use in certain contexts. For example, tainted
847 strings should not be output as part of a webpage as they might
848 contain rogue HTML or Javascript that could lead to an XSS
849 vulnerability. Similarly, tainted strings should not be run as
850 SQL since they might contain an SQL-injection vulerability.
851
852 <a id="tclEval"></a>TH1 tclEval Command
853 -----------------------------------------
854
855 **This command requires the Tcl integration feature.**
856
@@ -854,10 +918,22 @@
918
919 * trace STRING
920
921 Generates a TH1 trace message if TH1 tracing is enabled.
922
923 <a id="untaintCmd"></a>TH1 taint Command
924 -----------------------------------------
925
926 * untaint STRING
927
928 This command returns a copy of STRING that has been marked as
929 [untainted](#taint). Untainted strings are strings which are
930 believed to be free of potentially hostile content. Use this
931 command with caution, as it overwrites the tainted-string protection
932 mechanisms that are built into TH1. If you do not understand all
933 the implications of executing this command, then do not use it.
934
935 <a id="unversioned_content"></a>TH1 unversioned content Command
936 -----------------------------------------------------------------
937
938 * unversioned content FILENAME
939
940

Keyboard Shortcuts

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