Fossil SCM
Merge trunk
Commit
f993a870a130f4b58fdfdda465853ceb6de6206a109799e53940c50f091720ee
Parent
d913362cc492b60…
44 files changed
+1
-1
+208
-192
+204
-142
+6
-5
+31
-17
+1
-1
+1
-1
+4
-2
+2
-2
+7
-1
+2
-1
+6
-2
+15
-7
+2
-2
+2
-2
+89
-182
+1
-22
+13
+1
-1
+7
-1
+22
-5
+20
-59
+8
+12
-6
+4
-6
+3
+79
-41
+34
-19
+2
-2
+3
+7
+84
+36
-36
+12
-7
+2
-3
+54
-27
+1
-1
+37
-1
+1
-1
+1
-1
+4
-4
+75
-65
+16
-18
+107
-29
~
VERSION
~
extsrc/shell.c
~
extsrc/sqlite3.c
~
extsrc/sqlite3.h
~
src/alerts.c
~
src/cgi.c
~
src/cgi.c
~
src/clone.c
~
src/db.c
~
src/diff.tcl
~
src/http.c
~
src/info.c
~
src/main.mk
~
src/manifest.c
~
src/markdown_html.c
~
src/pikchrshow.c
~
src/repolist.c
~
src/security_audit.c
~
src/tag.c
~
src/th.c
~
src/th_lang.c
~
src/th_main.c
~
src/th_tcl.c
~
src/timeline.c
~
src/tkt.c
~
src/tktsetup.c
~
src/user.c
~
src/winfile.c
~
src/xfer.c
~
src/zip.c
~
test/tester.tcl
~
test/th1-taint.test
~
test/th1.test
~
tools/makemake.tcl
~
www/cgi.wiki
~
www/changes.wiki
~
www/containers.md
~
www/customskin.md
~
www/fossil-v-git.wiki
~
www/gsoc-ideas.md
~
www/index.wiki
~
www/quickstart.wiki
~
www/reviews.wiki
~
www/th1.md
M
VERSION
+1
-1
| --- VERSION | ||
| +++ VERSION | ||
| @@ -1,1 +1,1 @@ | ||
| 1 | -2.26 | |
| 1 | +2.27 | |
| 2 | 2 |
| --- 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 @@ | ||
| 1622 | 1622 | if( n>350 ) n = 350; |
| 1623 | 1623 | sqlite3_snprintf(sizeof(z), z, "%#+.*e", n, r); |
| 1624 | 1624 | sqlite3_result_text(pCtx, z, -1, SQLITE_TRANSIENT); |
| 1625 | 1625 | } |
| 1626 | 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 | 1627 | /* |
| 1652 | 1628 | ** SQL function: shell_add_schema(S,X) |
| 1653 | 1629 | ** |
| 1654 | 1630 | ** Add the schema name X to the CREATE statement in S and return the result. |
| 1655 | 1631 | ** Examples: |
| @@ -1820,10 +1796,11 @@ | ||
| 1820 | 1796 | # ifdef FILENAME_MAX |
| 1821 | 1797 | # define NAME_MAX (FILENAME_MAX) |
| 1822 | 1798 | # else |
| 1823 | 1799 | # define NAME_MAX (260) |
| 1824 | 1800 | # endif |
| 1801 | +# define DIRENT_NAME_MAX (NAME_MAX) | |
| 1825 | 1802 | #endif |
| 1826 | 1803 | |
| 1827 | 1804 | /* |
| 1828 | 1805 | ** We need to define "NULL_INTPTR_T" and "BAD_INTPTR_T". |
| 1829 | 1806 | */ |
| @@ -1863,12 +1840,11 @@ | ||
| 1863 | 1840 | #endif |
| 1864 | 1841 | |
| 1865 | 1842 | /* |
| 1866 | 1843 | ** Provide a macro, for use by the implementation, to determine if a |
| 1867 | 1844 | ** 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(). | |
| 1870 | 1846 | */ |
| 1871 | 1847 | |
| 1872 | 1848 | #ifndef is_filtered |
| 1873 | 1849 | # define is_filtered(a) ((((a).attrib)&_A_HIDDEN) || (((a).attrib)&_A_SYSTEM)) |
| 1874 | 1850 | #endif |
| @@ -1880,16 +1856,15 @@ | ||
| 1880 | 1856 | |
| 1881 | 1857 | extern const char *windirent_getenv(const char *name); |
| 1882 | 1858 | |
| 1883 | 1859 | /* |
| 1884 | 1860 | ** Finally, we can provide the function prototypes for the opendir(), |
| 1885 | -** readdir(), readdir_r(), and closedir() POSIX functions. | |
| 1861 | +** readdir(), and closedir() POSIX functions. | |
| 1886 | 1862 | */ |
| 1887 | 1863 | |
| 1888 | 1864 | extern LPDIR opendir(const char *dirname); |
| 1889 | 1865 | extern LPDIRENT readdir(LPDIR dirp); |
| 1890 | -extern INT readdir_r(LPDIR dirp, LPDIRENT entry, LPDIRENT *result); | |
| 1891 | 1866 | extern INT closedir(LPDIR dirp); |
| 1892 | 1867 | |
| 1893 | 1868 | #endif /* defined(WIN32) && defined(_MSC_VER) */ |
| 1894 | 1869 | |
| 1895 | 1870 | /************************* End test_windirent.h ********************/ |
| @@ -1942,27 +1917,45 @@ | ||
| 1942 | 1917 | |
| 1943 | 1918 | /* |
| 1944 | 1919 | ** Implementation of the POSIX opendir() function using the MSVCRT. |
| 1945 | 1920 | */ |
| 1946 | 1921 | LPDIR opendir( |
| 1947 | - const char *dirname | |
| 1922 | + const char *dirname /* Directory name, UTF8 encoding */ | |
| 1948 | 1923 | ){ |
| 1949 | - struct _finddata_t data; | |
| 1924 | + struct _wfinddata_t data; | |
| 1950 | 1925 | LPDIR dirp = (LPDIR)sqlite3_malloc(sizeof(DIR)); |
| 1951 | 1926 | SIZE_T namesize = sizeof(data.name) / sizeof(data.name[0]); |
| 1927 | + wchar_t *b1; | |
| 1928 | + sqlite3_int64 sz; | |
| 1952 | 1929 | |
| 1953 | 1930 | if( dirp==NULL ) return NULL; |
| 1954 | 1931 | memset(dirp, 0, sizeof(DIR)); |
| 1955 | 1932 | |
| 1956 | 1933 | /* TODO: Remove this if Unix-style root paths are not used. */ |
| 1957 | 1934 | if( sqlite3_stricmp(dirname, "/")==0 ){ |
| 1958 | 1935 | dirname = windirent_getenv("SystemDrive"); |
| 1959 | 1936 | } |
| 1960 | 1937 | |
| 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); | |
| 1964 | 1957 | |
| 1965 | 1958 | if( dirp->d_handle==BAD_INTPTR_T ){ |
| 1966 | 1959 | closedir(dirp); |
| 1967 | 1960 | return NULL; |
| 1968 | 1961 | } |
| @@ -1969,34 +1962,33 @@ | ||
| 1969 | 1962 | |
| 1970 | 1963 | /* TODO: Remove this block to allow hidden and/or system files. */ |
| 1971 | 1964 | if( is_filtered(data) ){ |
| 1972 | 1965 | next: |
| 1973 | 1966 | |
| 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 ){ | |
| 1976 | 1969 | closedir(dirp); |
| 1977 | 1970 | return NULL; |
| 1978 | 1971 | } |
| 1979 | 1972 | |
| 1980 | 1973 | /* TODO: Remove this block to allow hidden and/or system files. */ |
| 1981 | 1974 | if( is_filtered(data) ) goto next; |
| 1982 | 1975 | } |
| 1983 | 1976 | |
| 1984 | 1977 | 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); | |
| 1988 | 1980 | return dirp; |
| 1989 | 1981 | } |
| 1990 | 1982 | |
| 1991 | 1983 | /* |
| 1992 | 1984 | ** Implementation of the POSIX readdir() function using the MSVCRT. |
| 1993 | 1985 | */ |
| 1994 | 1986 | LPDIRENT readdir( |
| 1995 | 1987 | LPDIR dirp |
| 1996 | 1988 | ){ |
| 1997 | - struct _finddata_t data; | |
| 1989 | + struct _wfinddata_t data; | |
| 1998 | 1990 | |
| 1999 | 1991 | if( dirp==NULL ) return NULL; |
| 2000 | 1992 | |
| 2001 | 1993 | if( dirp->d_first.d_ino==0 ){ |
| 2002 | 1994 | dirp->d_first.d_ino++; |
| @@ -2005,69 +1997,23 @@ | ||
| 2005 | 1997 | return &dirp->d_first; |
| 2006 | 1998 | } |
| 2007 | 1999 | |
| 2008 | 2000 | next: |
| 2009 | 2001 | |
| 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; | |
| 2012 | 2004 | |
| 2013 | 2005 | /* TODO: Remove this block to allow hidden and/or system files. */ |
| 2014 | 2006 | if( is_filtered(data) ) goto next; |
| 2015 | 2007 | |
| 2016 | 2008 | dirp->d_next.d_ino++; |
| 2017 | 2009 | 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); | |
| 2021 | 2012 | return &dirp->d_next; |
| 2022 | 2013 | } |
| 2023 | 2014 | |
| 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 | 2015 | /* |
| 2070 | 2016 | ** Implementation of the POSIX closedir() function using the MSVCRT. |
| 2071 | 2017 | */ |
| 2072 | 2018 | INT closedir( |
| 2073 | 2019 | LPDIR dirp |
| @@ -8085,18 +8031,13 @@ | ||
| 8085 | 8031 | # include "windows.h" |
| 8086 | 8032 | # include <io.h> |
| 8087 | 8033 | # include <direct.h> |
| 8088 | 8034 | /* # include "test_windirent.h" */ |
| 8089 | 8035 | # 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) | |
| 8098 | 8039 | #endif |
| 8099 | 8040 | #include <time.h> |
| 8100 | 8041 | #include <errno.h> |
| 8101 | 8042 | |
| 8102 | 8043 | /* When used as part of the CLI, the sqlite3_stdio.h module will have |
| @@ -8117,10 +8058,44 @@ | ||
| 8117 | 8058 | #define FSDIR_COLUMN_MTIME 2 /* Last modification time */ |
| 8118 | 8059 | #define FSDIR_COLUMN_DATA 3 /* File content */ |
| 8119 | 8060 | #define FSDIR_COLUMN_PATH 4 /* Path to top of search */ |
| 8120 | 8061 | #define FSDIR_COLUMN_DIR 5 /* Path is relative to this directory */ |
| 8121 | 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 | + | |
| 8122 | 8097 | |
| 8123 | 8098 | /* |
| 8124 | 8099 | ** Set the result stored by context ctx to a blob containing the |
| 8125 | 8100 | ** contents of file zName. Or, leave the result unchanged (NULL) |
| 8126 | 8101 | ** if the file does not exist or is unreadable. |
| @@ -8278,11 +8253,17 @@ | ||
| 8278 | 8253 | static int fileStat( |
| 8279 | 8254 | const char *zPath, |
| 8280 | 8255 | struct stat *pStatBuf |
| 8281 | 8256 | ){ |
| 8282 | 8257 | #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); | |
| 8284 | 8265 | if( rc==0 ) statTimesToUtc(zPath, pStatBuf); |
| 8285 | 8266 | return rc; |
| 8286 | 8267 | #else |
| 8287 | 8268 | return stat(zPath, pStatBuf); |
| 8288 | 8269 | #endif |
| @@ -8296,13 +8277,11 @@ | ||
| 8296 | 8277 | static int fileLinkStat( |
| 8297 | 8278 | const char *zPath, |
| 8298 | 8279 | struct stat *pStatBuf |
| 8299 | 8280 | ){ |
| 8300 | 8281 | #if defined(_WIN32) |
| 8301 | - int rc = lstat(zPath, pStatBuf); | |
| 8302 | - if( rc==0 ) statTimesToUtc(zPath, pStatBuf); | |
| 8303 | - return rc; | |
| 8282 | + return fileStat(zPath, pStatBuf); | |
| 8304 | 8283 | #else |
| 8305 | 8284 | return lstat(zPath, pStatBuf); |
| 8306 | 8285 | #endif |
| 8307 | 8286 | } |
| 8308 | 8287 | |
| @@ -16808,11 +16787,11 @@ | ||
| 16808 | 16787 | case SQLITE_FCNTL_POWERSAFE_OVERWRITE: zOp = "POWERSAFE_OVERWRITE"; break; |
| 16809 | 16788 | case SQLITE_FCNTL_PRAGMA: { |
| 16810 | 16789 | const char *const* a = (const char*const*)pArg; |
| 16811 | 16790 | if( a[1] && strcmp(a[1],"vfstrace")==0 && a[2] ){ |
| 16812 | 16791 | const u8 *zArg = (const u8*)a[2]; |
| 16813 | - if( zArg[0]>='0' && zArg[0]<=9 ){ | |
| 16792 | + if( zArg[0]>='0' && zArg[0]<='9' ){ | |
| 16814 | 16793 | pInfo->mTrace = (sqlite3_uint64)strtoll(a[2], 0, 0); |
| 16815 | 16794 | }else{ |
| 16816 | 16795 | static const struct { |
| 16817 | 16796 | const char *z; |
| 16818 | 16797 | unsigned int m; |
| @@ -18709,10 +18688,13 @@ | ||
| 18709 | 18688 | rc = sqlite3_create_module(db, "sqlite_dbptr", &dbdata_module, (void*)1); |
| 18710 | 18689 | } |
| 18711 | 18690 | return rc; |
| 18712 | 18691 | } |
| 18713 | 18692 | |
| 18693 | +#ifdef _WIN32 | |
| 18694 | + | |
| 18695 | +#endif | |
| 18714 | 18696 | int sqlite3_dbdata_init( |
| 18715 | 18697 | sqlite3 *db, |
| 18716 | 18698 | char **pzErrMsg, |
| 18717 | 18699 | const sqlite3_api_routines *pApi |
| 18718 | 18700 | ){ |
| @@ -25592,107 +25574,108 @@ | ||
| 25592 | 25574 | " --plain Show results as text/plain, not as HTML", |
| 25593 | 25575 | #endif |
| 25594 | 25576 | }; |
| 25595 | 25577 | |
| 25596 | 25578 | /* |
| 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. | |
| 25598 | 25595 | ** |
| 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. | |
| 25602 | 25599 | ** |
| 25603 | -** Return the number of matches. | |
| 25600 | +** Return the number commands that match zPattern. | |
| 25604 | 25601 | */ |
| 25605 | 25602 | static int showHelp(FILE *out, const char *zPattern){ |
| 25606 | 25603 | int i = 0; |
| 25607 | 25604 | int j = 0; |
| 25608 | 25605 | int n = 0; |
| 25609 | 25606 | 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); | |
| 25694 | 25677 | return n; |
| 25695 | 25678 | } |
| 25696 | 25679 | |
| 25697 | 25680 | /* Forward reference */ |
| 25698 | 25681 | static int process_input(ShellState *p); |
| @@ -25937,10 +25920,43 @@ | ||
| 25937 | 25920 | int sleep = sqlite3_value_int(argv[0]); |
| 25938 | 25921 | (void)argcUnused; |
| 25939 | 25922 | sqlite3_sleep(sleep/1000); |
| 25940 | 25923 | sqlite3_result_int(context, sleep); |
| 25941 | 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 | +} | |
| 25942 | 25958 | |
| 25943 | 25959 | /* Flags for open_db(). |
| 25944 | 25960 | ** |
| 25945 | 25961 | ** The default behavior of open_db() is to exit(1) if the database fails to |
| 25946 | 25962 | ** open. The OPEN_DB_KEEPALIVE flag changes that so that it prints an error |
| @@ -26081,11 +26097,11 @@ | ||
| 26081 | 26097 | shellDtostr, 0, 0); |
| 26082 | 26098 | sqlite3_create_function(p->db, "dtostr", 2, SQLITE_UTF8, 0, |
| 26083 | 26099 | shellDtostr, 0, 0); |
| 26084 | 26100 | sqlite3_create_function(p->db, "shell_add_schema", 3, SQLITE_UTF8, 0, |
| 26085 | 26101 | 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, | |
| 26087 | 26103 | shellModuleSchema, 0, 0); |
| 26088 | 26104 | sqlite3_create_function(p->db, "shell_putsnl", 1, SQLITE_UTF8, p, |
| 26089 | 26105 | shellPutsFunc, 0, 0); |
| 26090 | 26106 | sqlite3_create_function(p->db, "usleep",1,SQLITE_UTF8,0, |
| 26091 | 26107 | shellUSleepFunc, 0, 0); |
| 26092 | 26108 |
| --- 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 @@ | ||
| 16 | 16 | ** if you want a wrapper to interface SQLite with your choice of programming |
| 17 | 17 | ** language. The code for the "sqlite3" command-line shell is also in a |
| 18 | 18 | ** separate file. This file contains only code for the core SQLite library. |
| 19 | 19 | ** |
| 20 | 20 | ** The content in this amalgamation comes from Fossil check-in |
| 21 | -** d22475b81c4e26ccc50f3b5626d43b32f7a2 with changes in files: | |
| 21 | +** 336ceeccc6f85bd78f4a26648af7edf9056d with changes in files: | |
| 22 | 22 | ** |
| 23 | 23 | ** |
| 24 | 24 | */ |
| 25 | 25 | #ifndef SQLITE_AMALGAMATION |
| 26 | 26 | #define SQLITE_CORE 1 |
| @@ -465,11 +465,11 @@ | ||
| 465 | 465 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 466 | 466 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 467 | 467 | */ |
| 468 | 468 | #define SQLITE_VERSION "3.50.0" |
| 469 | 469 | #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" | |
| 471 | 471 | |
| 472 | 472 | /* |
| 473 | 473 | ** CAPI3REF: Run-Time Library Version Numbers |
| 474 | 474 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 475 | 475 | ** |
| @@ -11872,13 +11872,14 @@ | ||
| 11872 | 11872 | ** This may appear to have some counter-intuitive effects if a single row |
| 11873 | 11873 | ** is written to more than once during a session. For example, if a row |
| 11874 | 11874 | ** is inserted while a session object is enabled, then later deleted while |
| 11875 | 11875 | ** the same session object is disabled, no INSERT record will appear in the |
| 11876 | 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. | |
| 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. | |
| 11880 | 11881 | */ |
| 11881 | 11882 | SQLITE_API int sqlite3session_changeset( |
| 11882 | 11883 | sqlite3_session *pSession, /* Session object */ |
| 11883 | 11884 | int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */ |
| 11884 | 11885 | void **ppChangeset /* OUT: Buffer containing changeset */ |
| @@ -12083,11 +12084,11 @@ | ||
| 12083 | 12084 | ** CAPI3REF: Flags for sqlite3changeset_start_v2 |
| 12084 | 12085 | ** |
| 12085 | 12086 | ** The following flags may passed via the 4th parameter to |
| 12086 | 12087 | ** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]: |
| 12087 | 12088 | ** |
| 12088 | -** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd> | |
| 12089 | +** <dt>SQLITE_CHANGESETSTART_INVERT <dd> | |
| 12089 | 12090 | ** Invert the changeset while iterating through it. This is equivalent to |
| 12090 | 12091 | ** inverting a changeset using sqlite3changeset_invert() before applying it. |
| 12091 | 12092 | ** It is an error to specify this flag with a patchset. |
| 12092 | 12093 | */ |
| 12093 | 12094 | #define SQLITE_CHANGESETSTART_INVERT 0x0002 |
| @@ -19160,11 +19161,10 @@ | ||
| 19160 | 19161 | unsigned uniqNotNull:1; /* True if UNIQUE and NOT NULL for all columns */ |
| 19161 | 19162 | unsigned isResized:1; /* True if resizeIndexObject() has been called */ |
| 19162 | 19163 | unsigned isCovering:1; /* True if this is a covering index */ |
| 19163 | 19164 | unsigned noSkipScan:1; /* Do not try to use skip-scan if true */ |
| 19164 | 19165 | unsigned hasStat1:1; /* aiRowLogEst values come from sqlite_stat1 */ |
| 19165 | - unsigned bLowQual:1; /* sqlite_stat1 says this is a low-quality index */ | |
| 19166 | 19166 | unsigned bNoQuery:1; /* Do not use this index to optimize queries */ |
| 19167 | 19167 | unsigned bAscKeyBug:1; /* True if the bba7b69f9849b5bf bug applies */ |
| 19168 | 19168 | unsigned bIdxRowid:1; /* One or more of the index keys is the ROWID */ |
| 19169 | 19169 | unsigned bHasVCol:1; /* Index references one or more VIRTUAL columns */ |
| 19170 | 19170 | unsigned bHasExpr:1; /* Index contains an expression, either a literal |
| @@ -22424,10 +22424,13 @@ | ||
| 22424 | 22424 | #ifdef SQLITE_BITMASK_TYPE |
| 22425 | 22425 | "BITMASK_TYPE=" CTIMEOPT_VAL(SQLITE_BITMASK_TYPE), |
| 22426 | 22426 | #endif |
| 22427 | 22427 | #ifdef SQLITE_BUG_COMPATIBLE_20160819 |
| 22428 | 22428 | "BUG_COMPATIBLE_20160819", |
| 22429 | +#endif | |
| 22430 | +#ifdef SQLITE_BUG_COMPATIBLE_20250510 | |
| 22431 | + "BUG_COMPATIBLE_20250510", | |
| 22429 | 22432 | #endif |
| 22430 | 22433 | #ifdef SQLITE_CASE_SENSITIVE_LIKE |
| 22431 | 22434 | "CASE_SENSITIVE_LIKE", |
| 22432 | 22435 | #endif |
| 22433 | 22436 | #ifdef SQLITE_CHECK_PAGES |
| @@ -32987,10 +32990,19 @@ | ||
| 32987 | 32990 | va_end(ap); |
| 32988 | 32991 | zBuf[acc.nChar] = 0; |
| 32989 | 32992 | return zBuf; |
| 32990 | 32993 | } |
| 32991 | 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 | + | |
| 32992 | 33004 | /* |
| 32993 | 33005 | ** This is the routine that actually formats the sqlite3_log() message. |
| 32994 | 33006 | ** We house it in a separate routine from sqlite3_log() to avoid using |
| 32995 | 33007 | ** stack space on small-stack systems when logging is disabled. |
| 32996 | 33008 | ** |
| @@ -33003,11 +33015,11 @@ | ||
| 33003 | 33015 | ** Care must be taken that any sqlite3_log() calls that occur while the |
| 33004 | 33016 | ** memory mutex is held do not use these mechanisms. |
| 33005 | 33017 | */ |
| 33006 | 33018 | static void renderLogMsg(int iErrCode, const char *zFormat, va_list ap){ |
| 33007 | 33019 | 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 */ | |
| 33009 | 33021 | |
| 33010 | 33022 | sqlite3StrAccumInit(&acc, 0, zMsg, sizeof(zMsg), 0); |
| 33011 | 33023 | sqlite3_str_vappendf(&acc, zFormat, ap); |
| 33012 | 33024 | sqlite3GlobalConfig.xLog(sqlite3GlobalConfig.pLogArg, iErrCode, |
| 33013 | 33025 | sqlite3StrAccumFinish(&acc)); |
| @@ -87262,10 +87274,13 @@ | ||
| 87262 | 87274 | ** into register iDest, then add the OPFLAG_TYPEOFARG flag to that |
| 87263 | 87275 | ** opcode. |
| 87264 | 87276 | */ |
| 87265 | 87277 | SQLITE_PRIVATE void sqlite3VdbeTypeofColumn(Vdbe *p, int iDest){ |
| 87266 | 87278 | VdbeOp *pOp = sqlite3VdbeGetLastOp(p); |
| 87279 | +#ifdef SQLITE_DEBUG | |
| 87280 | + while( pOp->opcode==OP_ReleaseReg ) pOp--; | |
| 87281 | +#endif | |
| 87267 | 87282 | if( pOp->p3==iDest && pOp->opcode==OP_Column ){ |
| 87268 | 87283 | pOp->p5 |= OPFLAG_TYPEOFARG; |
| 87269 | 87284 | } |
| 87270 | 87285 | } |
| 87271 | 87286 | |
| @@ -95716,11 +95731,11 @@ | ||
| 95716 | 95731 | } |
| 95717 | 95732 | }else{ |
| 95718 | 95733 | sqlite3VdbeError(p, "%s", pOp->p4.z); |
| 95719 | 95734 | } |
| 95720 | 95735 | 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); | |
| 95722 | 95737 | } |
| 95723 | 95738 | rc = sqlite3VdbeHalt(p); |
| 95724 | 95739 | assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR ); |
| 95725 | 95740 | if( rc==SQLITE_BUSY ){ |
| 95726 | 95741 | p->rc = SQLITE_BUSY; |
| @@ -97042,11 +97057,11 @@ | ||
| 97042 | 97057 | pOut->u.i = ~sqlite3VdbeIntValue(pIn1); |
| 97043 | 97058 | } |
| 97044 | 97059 | break; |
| 97045 | 97060 | } |
| 97046 | 97061 | |
| 97047 | -/* Opcode: Once P1 P2 * * * | |
| 97062 | +/* Opcode: Once P1 P2 P3 * * | |
| 97048 | 97063 | ** |
| 97049 | 97064 | ** Fall through to the next instruction the first time this opcode is |
| 97050 | 97065 | ** encountered on each invocation of the byte-code program. Jump to P2 |
| 97051 | 97066 | ** on the second and all subsequent encounters during the same invocation. |
| 97052 | 97067 | ** |
| @@ -97058,10 +97073,16 @@ | ||
| 97058 | 97073 | ** |
| 97059 | 97074 | ** For subprograms, there is a bitmask in the VdbeFrame that determines |
| 97060 | 97075 | ** whether or not the jump should be taken. The bitmask is necessary |
| 97061 | 97076 | ** because the self-altering code trick does not work for recursive |
| 97062 | 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. | |
| 97063 | 97084 | */ |
| 97064 | 97085 | case OP_Once: { /* jump */ |
| 97065 | 97086 | u32 iAddr; /* Address of this instruction */ |
| 97066 | 97087 | assert( p->aOp[0].opcode==OP_Init ); |
| 97067 | 97088 | if( p->pFrame ){ |
| @@ -98103,10 +98124,11 @@ | ||
| 98103 | 98124 | } |
| 98104 | 98125 | }else{ |
| 98105 | 98126 | zHdr += sqlite3PutVarint(zHdr, serial_type); |
| 98106 | 98127 | if( pRec->n ){ |
| 98107 | 98128 | assert( pRec->z!=0 ); |
| 98129 | + assert( pRec->z!=(const char*)sqlite3CtypeMap ); | |
| 98108 | 98130 | memcpy(zPayload, pRec->z, pRec->n); |
| 98109 | 98131 | zPayload += pRec->n; |
| 98110 | 98132 | } |
| 98111 | 98133 | } |
| 98112 | 98134 | if( pRec==pLast ) break; |
| @@ -103551,12 +103573,12 @@ | ||
| 103551 | 103573 | sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc)); |
| 103552 | 103574 | } |
| 103553 | 103575 | p->rc = rc; |
| 103554 | 103576 | sqlite3SystemError(db, rc); |
| 103555 | 103577 | 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); | |
| 103558 | 103580 | if( p->eVdbeState==VDBE_RUN_STATE ) sqlite3VdbeHalt(p); |
| 103559 | 103581 | if( rc==SQLITE_IOERR_NOMEM ) sqlite3OomFault(db); |
| 103560 | 103582 | if( rc==SQLITE_CORRUPT && db->autoCommit==0 ){ |
| 103561 | 103583 | db->flags |= SQLITE_CorruptRdOnly; |
| 103562 | 103584 | } |
| @@ -109287,17 +109309,16 @@ | ||
| 109287 | 109309 | /* Clearly non-deterministic functions like random(), but also |
| 109288 | 109310 | ** date/time functions that use 'now', and other functions like |
| 109289 | 109311 | ** sqlite_version() that might change over time cannot be used |
| 109290 | 109312 | ** in an index or generated column. Curiously, they can be used |
| 109291 | 109313 | ** in a CHECK constraint. SQLServer, MySQL, and PostgreSQL all |
| 109292 | - ** all this. */ | |
| 109314 | + ** allow this. */ | |
| 109293 | 109315 | sqlite3ResolveNotValid(pParse, pNC, "non-deterministic functions", |
| 109294 | 109316 | NC_IdxExpr|NC_PartIdx|NC_GenCol, 0, pExpr); |
| 109295 | 109317 | }else{ |
| 109296 | 109318 | assert( (NC_SelfRef & 0xff)==NC_SelfRef ); /* Must fit in 8 bits */ |
| 109297 | 109319 | pExpr->op2 = pNC->ncFlags & NC_SelfRef; |
| 109298 | - if( pNC->ncFlags & NC_FromDDL ) ExprSetProperty(pExpr, EP_FromDDL); | |
| 109299 | 109320 | } |
| 109300 | 109321 | if( (pDef->funcFlags & SQLITE_FUNC_INTERNAL)!=0 |
| 109301 | 109322 | && pParse->nested==0 |
| 109302 | 109323 | && (pParse->db->mDbFlags & DBFLAG_InternalFunc)==0 |
| 109303 | 109324 | ){ |
| @@ -109309,10 +109330,11 @@ | ||
| 109309 | 109330 | pDef = 0; |
| 109310 | 109331 | }else |
| 109311 | 109332 | if( (pDef->funcFlags & (SQLITE_FUNC_DIRECT|SQLITE_FUNC_UNSAFE))!=0 |
| 109312 | 109333 | && !IN_RENAME_OBJECT |
| 109313 | 109334 | ){ |
| 109335 | + if( pNC->ncFlags & NC_FromDDL ) ExprSetProperty(pExpr, EP_FromDDL); | |
| 109314 | 109336 | sqlite3ExprFunctionUsable(pParse, pExpr, pDef); |
| 109315 | 109337 | } |
| 109316 | 109338 | } |
| 109317 | 109339 | |
| 109318 | 109340 | if( 0==IN_RENAME_OBJECT ){ |
| @@ -114022,15 +114044,16 @@ | ||
| 114022 | 114044 | pCopy = sqlite3SelectDup(pParse->db, pSelect, 0); |
| 114023 | 114045 | rc = pParse->db->mallocFailed ? 1 :sqlite3Select(pParse, pCopy, &dest); |
| 114024 | 114046 | sqlite3SelectDelete(pParse->db, pCopy); |
| 114025 | 114047 | sqlite3DbFree(pParse->db, dest.zAffSdst); |
| 114026 | 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 */ | |
| 114027 | 114051 | sqlite3VdbeGetOp(v, addrOnce)->p3 = dest.iSDParm2; |
| 114028 | 114052 | 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; | |
| 114032 | 114055 | } |
| 114033 | 114056 | } |
| 114034 | 114057 | if( rc ){ |
| 114035 | 114058 | sqlite3KeyInfoUnref(pKeyInfo); |
| 114036 | 114059 | return; |
| @@ -114473,11 +114496,11 @@ | ||
| 114473 | 114496 | if( destIfFalse==destIfNull ){ |
| 114474 | 114497 | /* Combine Step 3 and Step 5 into a single opcode */ |
| 114475 | 114498 | if( ExprHasProperty(pExpr, EP_Subrtn) ){ |
| 114476 | 114499 | const VdbeOp *pOp = sqlite3VdbeGetOp(v, pExpr->y.sub.iAddr); |
| 114477 | 114500 | 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 */ | |
| 114479 | 114502 | assert( OptimizationEnabled(pParse->db, SQLITE_BloomFilter) ); |
| 114480 | 114503 | sqlite3VdbeAddOp4Int(v, OP_Filter, pOp->p3, destIfFalse, |
| 114481 | 114504 | rLhs, nVector); VdbeCoverage(v); |
| 114482 | 114505 | } |
| 114483 | 114506 | } |
| @@ -116322,15 +116345,15 @@ | ||
| 116322 | 116345 | case TK_ISNULL: |
| 116323 | 116346 | case TK_NOTNULL: { |
| 116324 | 116347 | assert( TK_ISNULL==OP_IsNull ); testcase( op==TK_ISNULL ); |
| 116325 | 116348 | assert( TK_NOTNULL==OP_NotNull ); testcase( op==TK_NOTNULL ); |
| 116326 | 116349 | r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); |
| 116327 | - sqlite3VdbeTypeofColumn(v, r1); | |
| 116350 | + assert( regFree1==0 || regFree1==r1 ); | |
| 116351 | + if( regFree1 ) sqlite3VdbeTypeofColumn(v, r1); | |
| 116328 | 116352 | sqlite3VdbeAddOp2(v, op, r1, dest); |
| 116329 | 116353 | VdbeCoverageIf(v, op==TK_ISNULL); |
| 116330 | 116354 | VdbeCoverageIf(v, op==TK_NOTNULL); |
| 116331 | - testcase( regFree1==0 ); | |
| 116332 | 116355 | break; |
| 116333 | 116356 | } |
| 116334 | 116357 | case TK_BETWEEN: { |
| 116335 | 116358 | testcase( jumpIfNull==0 ); |
| 116336 | 116359 | exprCodeBetween(pParse, pExpr, dest, sqlite3ExprIfTrue, jumpIfNull); |
| @@ -116497,15 +116520,15 @@ | ||
| 116497 | 116520 | break; |
| 116498 | 116521 | } |
| 116499 | 116522 | case TK_ISNULL: |
| 116500 | 116523 | case TK_NOTNULL: { |
| 116501 | 116524 | r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); |
| 116502 | - sqlite3VdbeTypeofColumn(v, r1); | |
| 116525 | + assert( regFree1==0 || regFree1==r1 ); | |
| 116526 | + if( regFree1 ) sqlite3VdbeTypeofColumn(v, r1); | |
| 116503 | 116527 | sqlite3VdbeAddOp2(v, op, r1, dest); |
| 116504 | 116528 | testcase( op==TK_ISNULL ); VdbeCoverageIf(v, op==TK_ISNULL); |
| 116505 | 116529 | testcase( op==TK_NOTNULL ); VdbeCoverageIf(v, op==TK_NOTNULL); |
| 116506 | - testcase( regFree1==0 ); | |
| 116507 | 116530 | break; |
| 116508 | 116531 | } |
| 116509 | 116532 | case TK_BETWEEN: { |
| 116510 | 116533 | testcase( jumpIfNull==0 ); |
| 116511 | 116534 | exprCodeBetween(pParse, pExpr, dest, sqlite3ExprIfFalse, jumpIfNull); |
| @@ -121683,20 +121706,10 @@ | ||
| 121683 | 121706 | } |
| 121684 | 121707 | #endif |
| 121685 | 121708 | while( z[0]!=0 && z[0]!=' ' ) z++; |
| 121686 | 121709 | while( z[0]==' ' ) z++; |
| 121687 | 121710 | } |
| 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 | 121711 | } |
| 121699 | 121712 | } |
| 121700 | 121713 | |
| 121701 | 121714 | /* |
| 121702 | 121715 | ** This callback is invoked once for each index when reading the |
| @@ -124091,11 +124104,11 @@ | ||
| 124091 | 124104 | */ |
| 124092 | 124105 | SQLITE_PRIVATE int sqlite3TableColumnToIndex(Index *pIdx, int iCol){ |
| 124093 | 124106 | int i; |
| 124094 | 124107 | i16 iCol16; |
| 124095 | 124108 | assert( iCol>=(-1) && iCol<=SQLITE_MAX_COLUMN ); |
| 124096 | - assert( pIdx->nColumn<=SQLITE_MAX_COLUMN ); | |
| 124109 | + assert( pIdx->nColumn<=SQLITE_MAX_COLUMN+1 ); | |
| 124097 | 124110 | iCol16 = iCol; |
| 124098 | 124111 | for(i=0; i<pIdx->nColumn; i++){ |
| 124099 | 124112 | if( iCol16==pIdx->aiColumn[i] ){ |
| 124100 | 124113 | return i; |
| 124101 | 124114 | } |
| @@ -167620,15 +167633,12 @@ | ||
| 167620 | 167633 | opMask = WO_LT|WO_LE; |
| 167621 | 167634 | }else{ |
| 167622 | 167635 | assert( pNew->u.btree.nBtm==0 ); |
| 167623 | 167636 | opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS; |
| 167624 | 167637 | } |
| 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); | |
| 167630 | 167640 | } |
| 167631 | 167641 | |
| 167632 | 167642 | assert( pNew->u.btree.nEq<pProbe->nColumn ); |
| 167633 | 167643 | assert( pNew->u.btree.nEq<pProbe->nKeyCol |
| 167634 | 167644 | || pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY ); |
| @@ -168561,11 +168571,11 @@ | ||
| 168561 | 168571 | } |
| 168562 | 168572 | }else if( m==0 |
| 168563 | 168573 | && (HasRowid(pTab) || pWInfo->pSelect!=0 || sqlite3FaultSim(700)) |
| 168564 | 168574 | ){ |
| 168565 | 168575 | WHERETRACE(0x200, |
| 168566 | - ("-> %s a covering index according to bitmasks\n", | |
| 168576 | + ("-> %s is a covering index according to bitmasks\n", | |
| 168567 | 168577 | pProbe->zName, m==0 ? "is" : "is not")); |
| 168568 | 168578 | pNew->wsFlags = WHERE_IDX_ONLY | WHERE_INDEXED; |
| 168569 | 168579 | } |
| 168570 | 168580 | } |
| 168571 | 168581 | |
| @@ -207968,60 +207978,113 @@ | ||
| 207968 | 207978 | ** Growing our own isspace() routine this way is twice as fast as |
| 207969 | 207979 | ** the library isspace() function, resulting in a 7% overall performance |
| 207970 | 207980 | ** increase for the text-JSON parser. (Ubuntu14.10 gcc 4.8.4 x64 with -Os). |
| 207971 | 207981 | */ |
| 207972 | 207982 | 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 | + | |
| 207990 | 208024 | }; |
| 207991 | 208025 | #define jsonIsspace(x) (jsonIsSpace[(unsigned char)x]) |
| 207992 | 208026 | |
| 207993 | 208027 | /* |
| 207994 | 208028 | ** The set of all space characters recognized by jsonIsspace(). |
| 207995 | 208029 | ** Useful as the second argument to strspn(). |
| 207996 | 208030 | */ |
| 208031 | +#ifdef SQLITE_ASCII | |
| 207997 | 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 | + | |
| 207998 | 208038 | |
| 207999 | 208039 | /* |
| 208000 | 208040 | ** Characters that are special to JSON. Control characters, |
| 208001 | 208041 | ** '"' and '\\' and '\''. Actually, '\'' is not special to |
| 208002 | 208042 | ** canonical JSON, but it is special in JSON-5, so we include |
| 208003 | 208043 | ** it in the set of special characters. |
| 208004 | 208044 | */ |
| 208005 | 208045 | 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 | |
| 208023 | 208086 | }; |
| 208024 | 208087 | |
| 208025 | 208088 | /* Objects */ |
| 208026 | 208089 | typedef struct JsonCache JsonCache; |
| 208027 | 208090 | typedef struct JsonString JsonString; |
| @@ -208162,11 +208225,11 @@ | ||
| 208162 | 208225 | |
| 208163 | 208226 | /************************************************************************** |
| 208164 | 208227 | ** Forward references |
| 208165 | 208228 | **************************************************************************/ |
| 208166 | 208229 | static void jsonReturnStringAsBlob(JsonString*); |
| 208167 | -static int jsonFuncArgMightBeBinary(sqlite3_value *pJson); | |
| 208230 | +static int jsonArgIsJsonb(sqlite3_value *pJson, JsonParse *p); | |
| 208168 | 208231 | static u32 jsonTranslateBlobToText(const JsonParse*,u32,JsonString*); |
| 208169 | 208232 | static void jsonReturnParse(sqlite3_context*,JsonParse*); |
| 208170 | 208233 | static JsonParse *jsonParseFuncArg(sqlite3_context*,sqlite3_value*,u32); |
| 208171 | 208234 | static void jsonParseFree(JsonParse*); |
| 208172 | 208235 | static u32 jsonbPayloadSize(const JsonParse*, u32, u32*); |
| @@ -208580,15 +208643,13 @@ | ||
| 208580 | 208643 | jsonAppendString(p, z, n); |
| 208581 | 208644 | } |
| 208582 | 208645 | break; |
| 208583 | 208646 | } |
| 208584 | 208647 | 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) ){ | |
| 208590 | 208651 | jsonTranslateBlobToText(&px, 0, p); |
| 208591 | 208652 | }else if( p->eErr==0 ){ |
| 208592 | 208653 | sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1); |
| 208593 | 208654 | p->eErr = JSTRING_ERR; |
| 208594 | 208655 | jsonStringReset(p); |
| @@ -209522,11 +209583,16 @@ | ||
| 209522 | 209583 | c = z[++j]; |
| 209523 | 209584 | if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f' |
| 209524 | 209585 | || c=='n' || c=='r' || c=='t' |
| 209525 | 209586 | || (c=='u' && jsonIs4Hex(&z[j+1])) ){ |
| 209526 | 209587 | 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 | |
| 209528 | 209594 | || (0xe2==(u8)c && 0x80==(u8)z[j+1] |
| 209529 | 209595 | && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2])) |
| 209530 | 209596 | || (c=='x' && jsonIs2Hex(&z[j+1])) ){ |
| 209531 | 209597 | opcode = JSONB_TEXT5; |
| 209532 | 209598 | pParse->hasNonstd = 1; |
| @@ -209909,11 +209975,11 @@ | ||
| 209909 | 209975 | || pParse->aBlob[i+4]!=0 |
| 209910 | 209976 | ){ |
| 209911 | 209977 | *pSz = 0; |
| 209912 | 209978 | return 0; |
| 209913 | 209979 | } |
| 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) + | |
| 209915 | 209981 | (pParse->aBlob[i+7]<<8) + pParse->aBlob[i+8]; |
| 209916 | 209982 | n = 9; |
| 209917 | 209983 | } |
| 209918 | 209984 | if( (i64)i+sz+n > pParse->nBlob |
| 209919 | 209985 | && (i64)i+sz+n > pParse->nBlob-pParse->delta |
| @@ -210258,37 +210324,10 @@ | ||
| 210258 | 210324 | } |
| 210259 | 210325 | } |
| 210260 | 210326 | return i; |
| 210261 | 210327 | } |
| 210262 | 210328 | |
| 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 | 210329 | /* |
| 210291 | 210330 | ** Given that a JSONB_ARRAY object starts at offset i, return |
| 210292 | 210331 | ** the number of entries in that array. |
| 210293 | 210332 | */ |
| 210294 | 210333 | static u32 jsonbArrayCount(JsonParse *pParse, u32 iRoot){ |
| @@ -210517,11 +210556,25 @@ | ||
| 210517 | 210556 | case 'f': { *piOut = '\f'; return 2; } |
| 210518 | 210557 | case 'n': { *piOut = '\n'; return 2; } |
| 210519 | 210558 | case 'r': { *piOut = '\r'; return 2; } |
| 210520 | 210559 | case 't': { *piOut = '\t'; return 2; } |
| 210521 | 210560 | 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 | + } | |
| 210523 | 210576 | case '\'': |
| 210524 | 210577 | case '"': |
| 210525 | 210578 | case '/': |
| 210526 | 210579 | case '\\':{ *piOut = z[1]; return 2; } |
| 210527 | 210580 | case 'x': { |
| @@ -211112,14 +211165,11 @@ | ||
| 211112 | 211165 | pParse->aBlob = aNull; |
| 211113 | 211166 | pParse->nBlob = 1; |
| 211114 | 211167 | return 0; |
| 211115 | 211168 | } |
| 211116 | 211169 | 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) ){ | |
| 211121 | 211171 | sqlite3_result_error(ctx, "JSON cannot hold BLOB values", -1); |
| 211122 | 211172 | return 1; |
| 211123 | 211173 | } |
| 211124 | 211174 | break; |
| 211125 | 211175 | } |
| @@ -211266,31 +211316,50 @@ | ||
| 211266 | 211316 | } |
| 211267 | 211317 | |
| 211268 | 211318 | /* |
| 211269 | 211319 | ** If pArg is a blob that seems like a JSONB blob, then initialize |
| 211270 | 211320 | ** 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. | |
| 211272 | 211322 | ** |
| 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. | |
| 211276 | 211345 | */ |
| 211277 | 211346 | static int jsonArgIsJsonb(sqlite3_value *pArg, JsonParse *p){ |
| 211278 | 211347 | u32 n, sz = 0; |
| 211348 | + u8 c; | |
| 211349 | + if( sqlite3_value_type(pArg)!=SQLITE_BLOB ) return 0; | |
| 211279 | 211350 | p->aBlob = (u8*)sqlite3_value_blob(pArg); |
| 211280 | 211351 | 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 | |
| 211289 | 211355 | && (n = jsonbPayloadSize(p, 0, &sz))>0 |
| 211290 | 211356 | && 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) | |
| 211292 | 211361 | ){ |
| 211293 | 211362 | return 1; |
| 211294 | 211363 | } |
| 211295 | 211364 | p->aBlob = 0; |
| 211296 | 211365 | p->nBlob = 0; |
| @@ -212379,25 +212448,21 @@ | ||
| 212379 | 212448 | sqlite3_result_int(ctx, 0); |
| 212380 | 212449 | #endif |
| 212381 | 212450 | return; |
| 212382 | 212451 | } |
| 212383 | 212452 | case SQLITE_BLOB: { |
| 212384 | - if( jsonFuncArgMightBeBinary(argv[0]) ){ | |
| 212453 | + JsonParse py; | |
| 212454 | + memset(&py, 0, sizeof(py)); | |
| 212455 | + if( jsonArgIsJsonb(argv[0], &py) ){ | |
| 212385 | 212456 | if( flags & 0x04 ){ |
| 212386 | 212457 | /* Superficial checking only - accomplished by the |
| 212387 | - ** jsonFuncArgMightBeBinary() call above. */ | |
| 212458 | + ** jsonArgIsJsonb() call above. */ | |
| 212388 | 212459 | res = 1; |
| 212389 | 212460 | }else if( flags & 0x08 ){ |
| 212390 | 212461 | /* Strict checking. Check by translating BLOB->TEXT->BLOB. If |
| 212391 | 212462 | ** 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); | |
| 212399 | 212464 | } |
| 212400 | 212465 | break; |
| 212401 | 212466 | } |
| 212402 | 212467 | /* Fall through into interpreting the input as text. See note |
| 212403 | 212468 | ** above at tag-20240123-a. */ |
| @@ -212451,13 +212516,11 @@ | ||
| 212451 | 212516 | |
| 212452 | 212517 | assert( argc==1 ); |
| 212453 | 212518 | UNUSED_PARAMETER(argc); |
| 212454 | 212519 | memset(&s, 0, sizeof(s)); |
| 212455 | 212520 | 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) ){ | |
| 212459 | 212522 | iErrPos = (i64)jsonbValidityCheck(&s, 0, s.nBlob, 1); |
| 212460 | 212523 | }else{ |
| 212461 | 212524 | s.zJson = (char*)sqlite3_value_text(argv[0]); |
| 212462 | 212525 | if( s.zJson==0 ) return; /* NULL input or OOM */ |
| 212463 | 212526 | s.nJson = sqlite3_value_bytes(argv[0]); |
| @@ -213138,13 +213201,12 @@ | ||
| 213138 | 213201 | jsonEachCursorReset(p); |
| 213139 | 213202 | if( idxNum==0 ) return SQLITE_OK; |
| 213140 | 213203 | memset(&p->sParse, 0, sizeof(p->sParse)); |
| 213141 | 213204 | p->sParse.nJPRef = 1; |
| 213142 | 213205 | 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 */ | |
| 213146 | 213208 | }else{ |
| 213147 | 213209 | p->sParse.zJson = (char*)sqlite3_value_text(argv[0]); |
| 213148 | 213210 | p->sParse.nJson = sqlite3_value_bytes(argv[0]); |
| 213149 | 213211 | if( p->sParse.zJson==0 ){ |
| 213150 | 213212 | p->i = p->iEnd = 0; |
| @@ -257213,11 +257275,11 @@ | ||
| 257213 | 257275 | int nArg, /* Number of args */ |
| 257214 | 257276 | sqlite3_value **apUnused /* Function arguments */ |
| 257215 | 257277 | ){ |
| 257216 | 257278 | assert( nArg==0 ); |
| 257217 | 257279 | 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); | |
| 257219 | 257281 | } |
| 257220 | 257282 | |
| 257221 | 257283 | /* |
| 257222 | 257284 | ** Implementation of fts5_locale(LOCALE, TEXT) function. |
| 257223 | 257285 | ** |
| 257224 | 257286 |
| --- 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, ®Free1); |
| 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, ®Free1); |
| 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, ®Free1); |
| 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, ®Free1); |
| 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 |
+6
-5
| --- extsrc/sqlite3.h | ||
| +++ extsrc/sqlite3.h | ||
| @@ -146,11 +146,11 @@ | ||
| 146 | 146 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 147 | 147 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 148 | 148 | */ |
| 149 | 149 | #define SQLITE_VERSION "3.50.0" |
| 150 | 150 | #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" | |
| 152 | 152 | |
| 153 | 153 | /* |
| 154 | 154 | ** CAPI3REF: Run-Time Library Version Numbers |
| 155 | 155 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 156 | 156 | ** |
| @@ -11553,13 +11553,14 @@ | ||
| 11553 | 11553 | ** This may appear to have some counter-intuitive effects if a single row |
| 11554 | 11554 | ** is written to more than once during a session. For example, if a row |
| 11555 | 11555 | ** is inserted while a session object is enabled, then later deleted while |
| 11556 | 11556 | ** the same session object is disabled, no INSERT record will appear in the |
| 11557 | 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. | |
| 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. | |
| 11561 | 11562 | */ |
| 11562 | 11563 | SQLITE_API int sqlite3session_changeset( |
| 11563 | 11564 | sqlite3_session *pSession, /* Session object */ |
| 11564 | 11565 | int *pnChangeset, /* OUT: Size of buffer at *ppChangeset */ |
| 11565 | 11566 | void **ppChangeset /* OUT: Buffer containing changeset */ |
| @@ -11764,11 +11765,11 @@ | ||
| 11764 | 11765 | ** CAPI3REF: Flags for sqlite3changeset_start_v2 |
| 11765 | 11766 | ** |
| 11766 | 11767 | ** The following flags may passed via the 4th parameter to |
| 11767 | 11768 | ** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]: |
| 11768 | 11769 | ** |
| 11769 | -** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd> | |
| 11770 | +** <dt>SQLITE_CHANGESETSTART_INVERT <dd> | |
| 11770 | 11771 | ** Invert the changeset while iterating through it. This is equivalent to |
| 11771 | 11772 | ** inverting a changeset using sqlite3changeset_invert() before applying it. |
| 11772 | 11773 | ** It is an error to specify this flag with a patchset. |
| 11773 | 11774 | */ |
| 11774 | 11775 | #define SQLITE_CHANGESETSTART_INVERT 0x0002 |
| 11775 | 11776 |
| --- 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 @@ | ||
| 984 | 984 | blob_appendf(pOut, "Sender: <%s>\r\n", p->zFrom); |
| 985 | 985 | }else{ |
| 986 | 986 | blob_appendf(pOut, "From: <%s>\r\n", p->zFrom); |
| 987 | 987 | } |
| 988 | 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 | 989 | if( strstr(blob_str(pHdr), "\r\nMessage-Id:")==0 ){ |
| 993 | 990 | /* Message-id format: "<$(date)x$(random)@$(from-host)>" where $(date) is |
| 994 | 991 | ** the current unix-time in hex, $(random) is a 64-bit random number, |
| 995 | 992 | ** and $(from) is the domain part of the email-self setting. */ |
| 996 | 993 | sqlite3_randomness(sizeof(r1), &r1); |
| @@ -3215,18 +3212,21 @@ | ||
| 3215 | 3212 | Blob fhdr, fbody; |
| 3216 | 3213 | blob_init(&fhdr, 0, 0); |
| 3217 | 3214 | blob_appendf(&fhdr, "To: <%s>\r\n", zEmail); |
| 3218 | 3215 | blob_append(&fhdr, blob_buffer(&p->hdr), blob_size(&p->hdr)); |
| 3219 | 3216 | 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 | + } | |
| 3228 | 3228 | alert_send(pSender,&fhdr,&fbody,p->zFromName); |
| 3229 | 3229 | nSent++; |
| 3230 | 3230 | blob_reset(&fhdr); |
| 3231 | 3231 | blob_reset(&fbody); |
| 3232 | 3232 | }else{ |
| @@ -3245,15 +3245,19 @@ | ||
| 3245 | 3245 | blob_append(&body, "\n", 1); |
| 3246 | 3246 | blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt)); |
| 3247 | 3247 | } |
| 3248 | 3248 | } |
| 3249 | 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); | |
| 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 | + } | |
| 3255 | 3259 | alert_send(pSender,&hdr,&body,0); |
| 3256 | 3260 | nSent++; |
| 3257 | 3261 | blob_truncate(&hdr, 0); |
| 3258 | 3262 | blob_truncate(&body, 0); |
| 3259 | 3263 | } |
| @@ -3295,18 +3299,28 @@ | ||
| 3295 | 3299 | " AND length(sdigest)>0", |
| 3296 | 3300 | iNewWarn, iOldWarn |
| 3297 | 3301 | ); |
| 3298 | 3302 | while( db_step(&q)==SQLITE_ROW ){ |
| 3299 | 3303 | Blob hdr, body; |
| 3304 | + const char *zCode = db_column_text(&q,0); | |
| 3300 | 3305 | blob_init(&hdr, 0, 0); |
| 3301 | 3306 | blob_init(&body, 0, 0); |
| 3302 | 3307 | alert_renewal_msg(&hdr, &body, |
| 3303 | - db_column_text(&q,0), | |
| 3308 | + zCode, | |
| 3304 | 3309 | db_column_int(&q,1), |
| 3305 | 3310 | db_column_text(&q,2), |
| 3306 | 3311 | db_column_text(&q,3), |
| 3307 | 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 | + } | |
| 3308 | 3322 | alert_send(pSender,&hdr,&body,0); |
| 3309 | 3323 | blob_reset(&hdr); |
| 3310 | 3324 | blob_reset(&body); |
| 3311 | 3325 | } |
| 3312 | 3326 | db_finalize(&q); |
| 3313 | 3327 |
| --- 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 @@ | ||
| 2673 | 2673 | listen4 = socket(AF_INET, SOCK_STREAM, 0); |
| 2674 | 2674 | if( listen4>0 ){ |
| 2675 | 2675 | setsockopt(listen4, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); |
| 2676 | 2676 | rc = bind(listen4, (struct sockaddr*)&inaddr4, sizeof(inaddr4)); |
| 2677 | 2677 | if( rc<0 ){ |
| 2678 | - close(listen6); | |
| 2678 | + close(listen4); | |
| 2679 | 2679 | listen4 = -1; |
| 2680 | 2680 | } |
| 2681 | 2681 | } |
| 2682 | 2682 | if( listen4<0 ){ |
| 2683 | 2683 | fossil_fatal("cannot open a listening socket on %s:%d", |
| 2684 | 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(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 @@ | ||
| 2673 | 2673 | listen4 = socket(AF_INET, SOCK_STREAM, 0); |
| 2674 | 2674 | if( listen4>0 ){ |
| 2675 | 2675 | setsockopt(listen4, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); |
| 2676 | 2676 | rc = bind(listen4, (struct sockaddr*)&inaddr4, sizeof(inaddr4)); |
| 2677 | 2677 | if( rc<0 ){ |
| 2678 | - close(listen6); | |
| 2678 | + close(listen4); | |
| 2679 | 2679 | listen4 = -1; |
| 2680 | 2680 | } |
| 2681 | 2681 | } |
| 2682 | 2682 | if( listen4<0 ){ |
| 2683 | 2683 | fossil_fatal("cannot open a listening socket on %s:%d", |
| 2684 | 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(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 @@ | ||
| 430 | 430 | const char *zNm = db_get("short-project-name","download"); |
| 431 | 431 | char *zUrl = href("%R/zip/%t/%t.zip", zDLTag, zNm); |
| 432 | 432 | @ <p>ZIP Archive: %z(zUrl)%h(zNm).zip</a> |
| 433 | 433 | zUrl = href("%R/tarball/%t/%t.tar.gz", zDLTag, zNm); |
| 434 | 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> | |
| 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 | + } | |
| 437 | 439 | } |
| 438 | 440 | if( !g.perm.Clone ){ |
| 439 | 441 | @ <p>You are not authorized to clone this repository. |
| 440 | 442 | if( g.zLogin==0 || g.zLogin[0]==0 ){ |
| 441 | 443 | @ Maybe you would be able to clone if you |
| 442 | 444 |
| --- 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 |
M
src/db.c
+2
-2
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -3369,11 +3369,11 @@ | ||
| 3369 | 3369 | if( zProjectName ) fossil_print("project-name: %s\n", zProjectName); |
| 3370 | 3370 | if( zProjectDesc ) fossil_print("project-description: %s\n", zProjectDesc); |
| 3371 | 3371 | fossil_print("project-id: %s\n", db_get("project-code", 0)); |
| 3372 | 3372 | fossil_print("server-id: %s\n", db_get("server-code", 0)); |
| 3373 | 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", | |
| 3374 | + fossil_print("admin-user: %s (initial remote-access password is \"%s\")\n", | |
| 3375 | 3375 | g.zLogin, zPassword); |
| 3376 | 3376 | hash_user_password(g.zLogin); |
| 3377 | 3377 | } |
| 3378 | 3378 | |
| 3379 | 3379 | /* |
| @@ -4758,11 +4758,11 @@ | ||
| 4758 | 4758 | ** SETTING: comment-format width=16 default=1 |
| 4759 | 4759 | ** Set the algorithm for printing timeline comments to the console. |
| 4760 | 4760 | ** |
| 4761 | 4761 | ** Possible values are: |
| 4762 | 4762 | ** 1 Use the original comment printing algorithm: |
| 4763 | -** * Leading and trialing whitespace is removed | |
| 4763 | +** * Leading and trailing whitespace is removed | |
| 4764 | 4764 | ** * Internal whitespace is converted into a single space (0x20) |
| 4765 | 4765 | ** * Line breaks occurs at whitespace or hyphens if possible |
| 4766 | 4766 | ** This is the recommended value and the default. |
| 4767 | 4767 | ** |
| 4768 | 4768 | ** Or a bitwise combination of the following flags: |
| 4769 | 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 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 @@ | ||
| 500 | 500 | if {$fn==""} return |
| 501 | 501 | set out [open $fn wb] |
| 502 | 502 | puts $out "#!/usr/bin/tclsh\n#\n# Run this script using 'tclsh' or 'wish'" |
| 503 | 503 | puts $out "# to see the graphical diff.\n#" |
| 504 | 504 | puts $out "set fossilcmd {}" |
| 505 | + puts $out "set darkmode $::darkmode" | |
| 506 | + puts $out "set debug $::debug" | |
| 505 | 507 | puts $out "set prog [list $::prog]" |
| 506 | 508 | puts $out "set difftxt \173" |
| 507 | 509 | foreach e $::difftxt {puts $out [list $e]} |
| 508 | 510 | puts $out "\175" |
| 509 | 511 | puts $out "eval \$prog" |
| @@ -606,11 +608,15 @@ | ||
| 606 | 608 | ::ttk::button .bb.quit -text {Quit} -command exit |
| 607 | 609 | ::ttk::button .bb.reload -text {Reload} -command reloadDiff |
| 608 | 610 | ::ttk::button .bb.invert -text {Invert} -command invertDiff |
| 609 | 611 | ::ttk::button .bb.save -text {Save As...} -command saveDiff |
| 610 | 612 | ::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 | |
| 612 | 618 | if {$fossilcmd!=""} {pack .bb.save -side left} |
| 613 | 619 | pack .bb.files .bb.search -side left |
| 614 | 620 | grid rowconfigure . 1 -weight 1 |
| 615 | 621 | grid columnconfigure . 1 -weight 1 |
| 616 | 622 | grid columnconfigure . 4 -weight 1 |
| 617 | 623 |
| --- 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 @@ | ||
| 52 | 52 | ** Construct the "login" card with the client credentials. |
| 53 | 53 | ** |
| 54 | 54 | ** login LOGIN NONCE SIGNATURE |
| 55 | 55 | ** |
| 56 | 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 | |
| 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 | |
| 58 | 59 | ** checksum of the nonce followed by the user password. |
| 59 | 60 | ** |
| 60 | 61 | ** Write the constructed login card into pLogin. pLogin is initialized |
| 61 | 62 | ** by this routine. |
| 62 | 63 | */ |
| 63 | 64 |
| --- 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 @@ | ||
| 993 | 993 | } |
| 994 | 994 | zUrl = mprintf("%R/tarball/%S/%t-%S.tar.gz", zUuid, zPJ, zUuid); |
| 995 | 995 | @ <tr><th>Downloads:</th><td> |
| 996 | 996 | @ %z(href("%s",zUrl))Tarball</a> |
| 997 | 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> | |
| 998 | + if( g.zLogin!=0 ){ | |
| 999 | + @ | %z(href("%R/sqlar/%S/%t-%S.sqlar",zUuid,zPJ,zUuid))\ | |
| 1000 | + @ SQL archive</a></td></tr> | |
| 1001 | + } | |
| 1000 | 1002 | fossil_free(zUrl); |
| 1001 | 1003 | blob_reset(&projName); |
| 1002 | 1004 | } |
| 1003 | 1005 | |
| 1004 | 1006 | @ <tr><th>Timelines:</th><td> |
| @@ -3930,10 +3932,11 @@ | ||
| 3930 | 3932 | ** --cancel TAG Cancel TAG from this check-in |
| 3931 | 3933 | ** --close Mark this "leaf" as closed |
| 3932 | 3934 | ** --date DATETIME Make DATETIME the check-in time |
| 3933 | 3935 | ** --date-override DATETIME Set the change time on the control artifact |
| 3934 | 3936 | ** -e|--edit-comment Launch editor to revise comment |
| 3937 | +** --editor NAME Text editor to use for check-in comment | |
| 3935 | 3938 | ** --hide Hide branch starting from this check-in |
| 3936 | 3939 | ** -m|--comment COMMENT Make COMMENT the check-in comment |
| 3937 | 3940 | ** -M|--message-file FILE Read the amended comment from FILE |
| 3938 | 3941 | ** -n|--dry-run Print control artifact, but make no changes |
| 3939 | 3942 | ** --no-verify-comment Do not validate the check-in comment |
| @@ -4003,10 +4006,11 @@ | ||
| 4003 | 4006 | if( zChngTime==0 ) zChngTime = find_option("chngtime",0,1); |
| 4004 | 4007 | zUserOvrd = find_option("user-override",0,1); |
| 4005 | 4008 | noVerifyCom = find_option("no-verify-comment",0,0)!=0; |
| 4006 | 4009 | db_find_and_open_repository(0,0); |
| 4007 | 4010 | user_select(); |
| 4011 | + (void)fossil_text_editor(); | |
| 4008 | 4012 | verify_all_options(); |
| 4009 | 4013 | if( g.argc<3 || g.argc>=4 ) usage(AMEND_USAGE_STMT); |
| 4010 | 4014 | rid = name_to_typed_rid(g.argv[2], "ci"); |
| 4011 | 4015 | if( rid==0 && !is_a_version(rid) ) fossil_fatal("no such check-in"); |
| 4012 | 4016 | zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid); |
| 4013 | 4017 |
| --- 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 @@ | ||
| 578 | 578 | $(OBJDIR)/winfile.o \ |
| 579 | 579 | $(OBJDIR)/winhttp.o \ |
| 580 | 580 | $(OBJDIR)/xfer.o \ |
| 581 | 581 | $(OBJDIR)/xfersetup.o \ |
| 582 | 582 | $(OBJDIR)/zip.o |
| 583 | -all: $(OBJDIR) $(APPNAME) | |
| 583 | +all: $(APPNAME) | |
| 584 | 584 | |
| 585 | 585 | install: all |
| 586 | 586 | mkdir -p $(INSTALLDIR) |
| 587 | 587 | cp $(APPNAME) $(INSTALLDIR) |
| 588 | 588 | |
| 589 | 589 | codecheck: $(TRANS_SRC) $(OBJDIR)/codecheck1 |
| 590 | 590 | $(OBJDIR)/codecheck1 $(TRANS_SRC) |
| 591 | 591 | |
| 592 | -$(OBJDIR): | |
| 593 | - -mkdir $(OBJDIR) | |
| 594 | - | |
| 595 | 592 | $(OBJDIR)/translate: $(SRCDIR_tools)/translate.c |
| 596 | 593 | -mkdir -p $(OBJDIR) |
| 597 | 594 | $(XBCC) -o $(OBJDIR)/translate $(SRCDIR_tools)/translate.c |
| 598 | 595 | |
| 599 | 596 | $(OBJDIR)/makeheaders: $(SRCDIR_tools)/makeheaders.c |
| 597 | + -mkdir -p $(OBJDIR) | |
| 600 | 598 | $(XBCC) -o $(OBJDIR)/makeheaders $(SRCDIR_tools)/makeheaders.c |
| 601 | 599 | |
| 602 | 600 | $(OBJDIR)/mkindex: $(SRCDIR_tools)/mkindex.c |
| 601 | + -mkdir -p $(OBJDIR) | |
| 603 | 602 | $(XBCC) -o $(OBJDIR)/mkindex $(SRCDIR_tools)/mkindex.c |
| 604 | 603 | |
| 605 | 604 | $(OBJDIR)/mkbuiltin: $(SRCDIR_tools)/mkbuiltin.c |
| 605 | + -mkdir -p $(OBJDIR) | |
| 606 | 606 | $(XBCC) -o $(OBJDIR)/mkbuiltin $(SRCDIR_tools)/mkbuiltin.c |
| 607 | 607 | |
| 608 | 608 | $(OBJDIR)/mkversion: $(SRCDIR_tools)/mkversion.c |
| 609 | + -mkdir -p $(OBJDIR) | |
| 609 | 610 | $(XBCC) -o $(OBJDIR)/mkversion $(SRCDIR_tools)/mkversion.c |
| 610 | 611 | |
| 611 | 612 | $(OBJDIR)/codecheck1: $(SRCDIR_tools)/codecheck1.c |
| 613 | + -mkdir -p $(OBJDIR) | |
| 612 | 614 | $(XBCC) -o $(OBJDIR)/codecheck1 $(SRCDIR_tools)/codecheck1.c |
| 613 | 615 | |
| 614 | 616 | # Run the test suite. |
| 615 | 617 | # Other flags that can be included in TESTFLAGS are: |
| 616 | 618 | # |
| @@ -622,11 +624,11 @@ | ||
| 622 | 624 | # -strict Treat known bugs as failures |
| 623 | 625 | # |
| 624 | 626 | # TESTFLAGS can also include names of specific test files to limit |
| 625 | 627 | # the run to just those test cases. |
| 626 | 628 | # |
| 627 | -test: $(OBJDIR) $(APPNAME) | |
| 629 | +test: $(APPNAME) | |
| 628 | 630 | $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(TESTFLAGS) |
| 629 | 631 | |
| 630 | 632 | $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion $(OBJDIR)/phony.h |
| 631 | 633 | $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid \ |
| 632 | 634 | $(SRCDIR)/../manifest \ |
| @@ -2119,23 +2121,29 @@ | ||
| 2119 | 2121 | |
| 2120 | 2122 | $(OBJDIR)/linenoise.o: $(SRCDIR_extsrc)/linenoise.c $(SRCDIR_extsrc)/linenoise.h |
| 2121 | 2123 | $(XTCC) -c $(SRCDIR_extsrc)/linenoise.c -o $@ |
| 2122 | 2124 | |
| 2123 | 2125 | $(OBJDIR)/th.o: $(SRCDIR)/th.c |
| 2126 | + -mkdir -p $(OBJDIR) | |
| 2127 | + | |
| 2124 | 2128 | $(XTCC) -c $(SRCDIR)/th.c -o $@ |
| 2125 | 2129 | |
| 2126 | 2130 | $(OBJDIR)/th_lang.o: $(SRCDIR)/th_lang.c |
| 2131 | + -mkdir -p $(OBJDIR) | |
| 2132 | + | |
| 2127 | 2133 | $(XTCC) -c $(SRCDIR)/th_lang.c -o $@ |
| 2128 | 2134 | |
| 2129 | 2135 | $(OBJDIR)/th_tcl.o: $(SRCDIR)/th_tcl.c |
| 2136 | + -mkdir -p $(OBJDIR) | |
| 2137 | + | |
| 2130 | 2138 | $(XTCC) -c $(SRCDIR)/th_tcl.c -o $@ |
| 2131 | 2139 | |
| 2132 | 2140 | |
| 2133 | -$(OBJDIR)/pikchr.o: $(SRCDIR_extsrc)/pikchr.c | |
| 2141 | +$(OBJDIR)/pikchr.o: $(SRCDIR_extsrc)/pikchr.c $(OBJDIR)/mkversion | |
| 2134 | 2142 | $(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@ |
| 2135 | 2143 | |
| 2136 | -$(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c | |
| 2144 | +$(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c $(OBJDIR)/mkversion | |
| 2137 | 2145 | $(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@ |
| 2138 | 2146 | |
| 2139 | 2147 | $(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c $(MAKEFILE_LIST) |
| 2140 | 2148 | $(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry \ |
| 2141 | 2149 | -sEXPORTED_RUNTIME_METHODS=cwrap,ccall,setValue,getValue,stackSave,stackAlloc,stackRestore \ |
| 2142 | 2150 |
| --- 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 @@ | ||
| 3024 | 3024 | CARD_STR2(K, p->zTicketUuid); |
| 3025 | 3025 | CARD_STR2(L, p->zWikiTitle); |
| 3026 | 3026 | ISA( CFTYPE_CLUSTER ){ |
| 3027 | 3027 | CARD_LETTER(M); |
| 3028 | 3028 | blob_append_char(b, '['); |
| 3029 | - for( int i = 0; i < p->nCChild; ++i ){ | |
| 3029 | + for( i = 0; i < p->nCChild; ++i ){ | |
| 3030 | 3030 | if( i>0 ) blob_append_char(b, ','); |
| 3031 | 3031 | blob_appendf(b, "%!j", p->azCChild[i]); |
| 3032 | 3032 | } |
| 3033 | 3033 | blob_append_char(b, ']'); |
| 3034 | 3034 | } |
| @@ -3059,11 +3059,11 @@ | ||
| 3059 | 3059 | } |
| 3060 | 3060 | CARD_STR2(R, p->zRepoCksum); |
| 3061 | 3061 | if( p->nTag ){ |
| 3062 | 3062 | CARD_LETTER(T); |
| 3063 | 3063 | blob_append_char(b, '['); |
| 3064 | - for( int i = 0; i < p->nTag; ++i ){ | |
| 3064 | + for( i = 0; i < p->nTag; ++i ){ | |
| 3065 | 3065 | const char *zName = p->aTag[i].zName; |
| 3066 | 3066 | if( i>0 ) blob_append_char(b, ','); |
| 3067 | 3067 | blob_append_char(b, '{'); |
| 3068 | 3068 | blob_appendf(b, "\"type\":\"%c\"", *zName); |
| 3069 | 3069 | KVP_STR(1, name, &zName[1]); |
| 3070 | 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( 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 |
+2
-2
| --- src/markdown_html.c | ||
| +++ src/markdown_html.c | ||
| @@ -278,11 +278,11 @@ | ||
| 278 | 278 | struct Blob *head_row, |
| 279 | 279 | struct Blob *rows, |
| 280 | 280 | void *opaque |
| 281 | 281 | ){ |
| 282 | 282 | INTER_BLOCK(ob); |
| 283 | - blob_append_literal(ob, "<table>\n"); | |
| 283 | + blob_append_literal(ob, "<table class='md-table'>\n"); | |
| 284 | 284 | if( head_row && blob_size(head_row)>0 ){ |
| 285 | 285 | blob_append_literal(ob, "<thead>\n"); |
| 286 | 286 | blob_appendb(ob, head_row); |
| 287 | 287 | blob_append_literal(ob, "</thead>\n<tbody>\n"); |
| 288 | 288 | } |
| @@ -696,11 +696,11 @@ | ||
| 696 | 696 | ){ |
| 697 | 697 | blob_appendf(&bSrc, "fontscale = %.13g\n", rPikVar); |
| 698 | 698 | } |
| 699 | 699 | blob_append(&bSrc, zSrc, nSrc) |
| 700 | 700 | /*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); | |
| 702 | 702 | blob_reset(&bSrc); |
| 703 | 703 | } |
| 704 | 704 | |
| 705 | 705 | /* Invoked for `...` blocks where there are nSep grave accents in a |
| 706 | 706 | ** row that serve as the delimiter. According to CommonMark: |
| 707 | 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>\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 @@ | ||
| 27 | 27 | /* The first two must match the values from pikchr.c */ |
| 28 | 28 | #define PIKCHR_PROCESS_PLAINTEXT_ERRORS 0x0001 |
| 29 | 29 | #define PIKCHR_PROCESS_DARK_MODE 0x0002 |
| 30 | 30 | /* end of flags supported directly by pikchr() */ |
| 31 | 31 | #define PIKCHR_PROCESS_PASSTHROUGH 0x0003 /* Pass through these flags */ |
| 32 | -#define PIKCHR_PROCESS_TH1 0x0004 | |
| 33 | -#define PIKCHR_PROCESS_TH1_NOSVG 0x0008 | |
| 34 | 32 | #define PIKCHR_PROCESS_NONCE 0x0010 |
| 35 | 33 | #define PIKCHR_PROCESS_ERR_PRE 0x0020 |
| 36 | 34 | #define PIKCHR_PROCESS_SRC 0x0040 |
| 37 | 35 | #define PIKCHR_PROCESS_DIV 0x0080 |
| 38 | 36 | #define PIKCHR_PROCESS_DIV_INDENT 0x0100 |
| @@ -43,36 +41,20 @@ | ||
| 43 | 41 | #define PIKCHR_PROCESS_DIV_SOURCE 0x2000 |
| 44 | 42 | #define PIKCHR_PROCESS_DIV_SOURCE_INLINE 0x4000 |
| 45 | 43 | #endif |
| 46 | 44 | |
| 47 | 45 | /* |
| 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 | |
| 50 | 47 | ** 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, | |
| 54 | 49 | ** |
| 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. | |
| 58 | 53 | ** |
| 59 | 54 | ** pikFlags flag descriptions: |
| 60 | 55 | ** |
| 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 | 56 | ** - PIKCHR_PROCESS_DIV: if set, the SVG result is wrapped in a DIV |
| 75 | 57 | ** element which specifies a max-width style value based on the SVG's |
| 76 | 58 | ** calculated size. This flag has multiple mutually exclusive forms: |
| 77 | 59 | ** |
| 78 | 60 | ** - PIKCHR_PROCESS_DIV uses default element alignment. |
| @@ -116,14 +98,14 @@ | ||
| 116 | 98 | ** |
| 117 | 99 | ** - PIKCHR_PROCESS_ERR_PRE: if set and pikchr() fails, the resulting |
| 118 | 100 | ** error report is wrapped in a PRE element, else it is retained |
| 119 | 101 | ** as-is (intended only for console output). |
| 120 | 102 | */ |
| 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){ | |
| 124 | 104 | int isErr = 0; |
| 105 | + int w = 0, h = 0; | |
| 106 | + char *zOut; | |
| 125 | 107 | const char *zNonce = (PIKCHR_PROCESS_NONCE & pikFlags) |
| 126 | 108 | ? safe_html_nonce(1) : 0; |
| 127 | 109 | |
| 128 | 110 | if(!(PIKCHR_PROCESS_DIV & pikFlags) |
| 129 | 111 | /* If any DIV_xxx flags are set, set DIV */ |
| @@ -135,115 +117,87 @@ | ||
| 135 | 117 | | PIKCHR_PROCESS_DIV_SOURCE_INLINE |
| 136 | 118 | | PIKCHR_PROCESS_DIV_TOGGLE |
| 137 | 119 | ) & pikFlags){ |
| 138 | 120 | pikFlags |= PIKCHR_PROCESS_DIV; |
| 139 | 121 | } |
| 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 | - ">→ /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 | + ">→ /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 | + } | |
| 245 | 199 | return isErr; |
| 246 | 200 | } |
| 247 | 201 | |
| 248 | 202 | /* |
| 249 | 203 | ** Legacy impl of /pikchrshow. pikchrshow_page() will delegate to |
| @@ -279,11 +233,11 @@ | ||
| 279 | 233 | TODO: respond with JSON instead.*/ |
| 280 | 234 | cgi_set_content_type("text/html"); |
| 281 | 235 | if(zContent && *zContent){ |
| 282 | 236 | Blob out = empty_blob; |
| 283 | 237 | const int isErr = |
| 284 | - pikchr_process(zContent, pikFlags, 0, &out); | |
| 238 | + pikchr_process(zContent, pikFlags, &out); | |
| 285 | 239 | if(isErr){ |
| 286 | 240 | cgi_printf_header("x-pikchrshow-is-error: %d\r\n", isErr); |
| 287 | 241 | } |
| 288 | 242 | CX("%b", &out); |
| 289 | 243 | blob_reset(&out); |
| @@ -384,11 +338,11 @@ | ||
| 384 | 338 | /* Reminder: Firefox does not properly flexbox a LEGEND |
| 385 | 339 | element, always flowing it in column mode. */); |
| 386 | 340 | CX("<div id='pikchrshow-output'>"); |
| 387 | 341 | if(*zContent){ |
| 388 | 342 | Blob out = empty_blob; |
| 389 | - pikchr_process(zContent, pikFlags, 0, &out); | |
| 343 | + pikchr_process(zContent, pikFlags, &out); | |
| 390 | 344 | CX("%b", &out); |
| 391 | 345 | blob_reset(&out); |
| 392 | 346 | } CX("</div>"/*#pikchrshow-output*/); |
| 393 | 347 | } CX("</fieldset>"/*#pikchrshow-output-wrapper*/); |
| 394 | 348 | } CX("</div>"/*sbs-wrapper*/); |
| @@ -561,60 +515,23 @@ | ||
| 561 | 515 | ** |
| 562 | 516 | ** -src Store the input pikchr's source code in the output as |
| 563 | 517 | ** a separate element adjacent to the SVG one. Implied |
| 564 | 518 | ** by -div-source. |
| 565 | 519 | ** |
| 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 | 520 | ** -dark Change pikchr colors to assume a dark-mode theme. |
| 580 | 521 | ** |
| 581 | 522 | ** |
| 582 | 523 | ** 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 | 524 | */ |
| 602 | 525 | void pikchr_cmd(void){ |
| 603 | 526 | Blob bIn = empty_blob; |
| 604 | 527 | Blob bOut = empty_blob; |
| 605 | 528 | const char * zInfile = "-"; |
| 606 | 529 | 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 | 530 | int isErr = 0; |
| 610 | 531 | int pikFlags = find_option("src",0,0)!=0 |
| 611 | 532 | ? 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 | 533 | |
| 617 | 534 | if(find_option("div",0,0)!=0){ |
| 618 | 535 | pikFlags |= PIKCHR_PROCESS_DIV; |
| 619 | 536 | }else if(find_option("div-indent",0,0)!=0){ |
| 620 | 537 | pikFlags |= PIKCHR_PROCESS_DIV_INDENT; |
| @@ -644,24 +561,14 @@ | ||
| 644 | 561 | } |
| 645 | 562 | if(g.argc>3){ |
| 646 | 563 | zOutfile = g.argv[3]; |
| 647 | 564 | } |
| 648 | 565 | 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); | |
| 657 | 567 | 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); | |
| 661 | 569 | }else{ |
| 662 | 570 | blob_write_to_file(&bOut, zOutfile); |
| 663 | 571 | } |
| 664 | - Th_PrintTraceLog(); | |
| 665 | 572 | blob_reset(&bIn); |
| 666 | 573 | blob_reset(&bOut); |
| 667 | 574 | } |
| 668 | 575 |
| --- 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 | ">→ /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 | ">→ /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 @@ | ||
| 101 | 101 | finish_repo_list: |
| 102 | 102 | g.dbIgnoreErrors--; |
| 103 | 103 | sqlite3_close(db); |
| 104 | 104 | } |
| 105 | 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 | 106 | /* |
| 122 | 107 | ** Generate a web-page that lists all repositories located under the |
| 123 | 108 | ** g.zRepositoryName directory and return non-zero. |
| 124 | 109 | ** |
| 125 | 110 | ** For the special case when g.zRepositoryName is a non-chroot-jail "/", |
| @@ -150,17 +135,10 @@ | ||
| 150 | 135 | assert( g.db==0 ); |
| 151 | 136 | zShow = P("FOSSIL_REPOLIST_SHOW"); |
| 152 | 137 | if( zShow ){ |
| 153 | 138 | bShowDesc = strstr(zShow,"description")!=0; |
| 154 | 139 | 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 | 140 | } |
| 163 | 141 | blob_init(&html, 0, 0); |
| 164 | 142 | if( fossil_strcmp(g.zRepositoryName,"/")==0 && !g.fJail ){ |
| 165 | 143 | /* For the special case of the "repository directory" being "/", |
| 166 | 144 | ** show all of the repositories named in the ~/.fossil database. |
| @@ -168,10 +146,11 @@ | ||
| 168 | 146 | ** On unix systems, then entries are of the form "repo:/home/..." |
| 169 | 147 | ** and on Windows systems they are like on unix, starting with a "/" |
| 170 | 148 | ** or they can begin with a drive letter: "repo:C:/Users/...". In either |
| 171 | 149 | ** case, we want returned path to omit any initial "/". |
| 172 | 150 | */ |
| 151 | + db_open_config(1, 0); | |
| 173 | 152 | db_multi_exec( |
| 174 | 153 | "CREATE TEMP VIEW sfile AS" |
| 175 | 154 | " SELECT ltrim(substr(name,6),'/') AS 'pathname' FROM global_config" |
| 176 | 155 | " WHERE name GLOB 'repo:*'" |
| 177 | 156 | ); |
| 178 | 157 |
| --- 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 |
+13
| --- src/security_audit.c | ||
| +++ src/security_audit.c | ||
| @@ -100,10 +100,11 @@ | ||
| 100 | 100 | const char *zReadCap; /* Capabilities of user group "reader" */ |
| 101 | 101 | const char *zPubPages; /* GLOB pattern for public pages */ |
| 102 | 102 | const char *zSelfCap; /* Capabilities of self-registered users */ |
| 103 | 103 | int hasSelfReg = 0; /* True if able to self-register */ |
| 104 | 104 | const char *zPublicUrl; /* Canonical access URL */ |
| 105 | + const char *zVulnReport; /* The vuln-report setting */ | |
| 105 | 106 | Blob cmd; |
| 106 | 107 | char *z; |
| 107 | 108 | int n, i; |
| 108 | 109 | CapabilityString *pCap; |
| 109 | 110 | char **azCSP; /* Parsed content security policy */ |
| @@ -362,10 +363,22 @@ | ||
| 362 | 363 | @ <li><p><b>WARNING:</b> |
| 363 | 364 | @ The "strict-manifest-syntax" flag is off. This is a security |
| 364 | 365 | @ risk. Turn this setting on (its default) to protect the users |
| 365 | 366 | @ of this repository. |
| 366 | 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 | + } | |
| 367 | 380 | |
| 368 | 381 | /* Obsolete: */ |
| 369 | 382 | if( hasAnyCap(zAnonCap, "d") || |
| 370 | 383 | hasAnyCap(zDevCap, "d") || |
| 371 | 384 | hasAnyCap(zReadCap, "d") ){ |
| 372 | 385 |
| --- 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 @@ | ||
| 412 | 412 | ** non-CHECK-IN artifacts. |
| 413 | 413 | ** --user-override USER Name USER when adding the tag |
| 414 | 414 | ** |
| 415 | 415 | ** The --date-override and --user-override options support |
| 416 | 416 | ** 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'. | |
| 418 | 418 | ** |
| 419 | 419 | ** Note that fossil uses some tag prefixes internally and this |
| 420 | 420 | ** command will reject tags with these prefixes to avoid |
| 421 | 421 | ** causing problems or confusion: "wiki-", "tkt-", "event-". |
| 422 | 422 | ** |
| 423 | 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-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 |
M
src/th.c
+7
-1
| --- src/th.c | ||
| +++ src/th.c | ||
| @@ -1794,15 +1794,19 @@ | ||
| 1794 | 1794 | int hasSpecialChar = 0; /* Whitespace or {}[]'" */ |
| 1795 | 1795 | int hasEscapeChar = 0; /* '}' without matching '{' to the left or a '\\' */ |
| 1796 | 1796 | int nBrace = 0; |
| 1797 | 1797 | |
| 1798 | 1798 | output.zBuf = *pzList; |
| 1799 | - output.nBuf = *pnList; | |
| 1799 | + output.nBuf = TH1_LEN(*pnList); | |
| 1800 | 1800 | output.nBufAlloc = output.nBuf; |
| 1801 | + output.bTaint = 0; | |
| 1802 | + TH1_XFER_TAINT(output.bTaint, *pnList); | |
| 1801 | 1803 | |
| 1802 | 1804 | if( nElem<0 ){ |
| 1803 | 1805 | nElem = th_strlen(zElem); |
| 1806 | + }else{ | |
| 1807 | + nElem = TH1_LEN(nElem); | |
| 1804 | 1808 | } |
| 1805 | 1809 | if( output.nBuf>0 ){ |
| 1806 | 1810 | thBufferAddChar(interp, &output, ' '); |
| 1807 | 1811 | } |
| 1808 | 1812 | |
| @@ -2127,16 +2131,18 @@ | ||
| 2127 | 2131 | /* Evaluate left and right arguments, if they exist. */ |
| 2128 | 2132 | if( pExpr->pLeft ){ |
| 2129 | 2133 | rc = exprEval(interp, pExpr->pLeft); |
| 2130 | 2134 | if( rc==TH_OK ){ |
| 2131 | 2135 | zLeft = Th_TakeResult(interp, &nLeft); |
| 2136 | + nLeft = TH1_LEN(nLeft); | |
| 2132 | 2137 | } |
| 2133 | 2138 | } |
| 2134 | 2139 | if( rc==TH_OK && pExpr->pRight ){ |
| 2135 | 2140 | rc = exprEval(interp, pExpr->pRight); |
| 2136 | 2141 | if( rc==TH_OK ){ |
| 2137 | 2142 | zRight = Th_TakeResult(interp, &nRight); |
| 2143 | + nRight = TH1_LEN(nRight); | |
| 2138 | 2144 | } |
| 2139 | 2145 | } |
| 2140 | 2146 | |
| 2141 | 2147 | /* Convert arguments to their required forms. */ |
| 2142 | 2148 | if( rc==TH_OK ){ |
| 2143 | 2149 |
| --- 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 @@ | ||
| 180 | 180 | int nVar; |
| 181 | 181 | char **azValue = 0; |
| 182 | 182 | int *anValue; |
| 183 | 183 | int nValue; |
| 184 | 184 | int ii, jj; |
| 185 | + int bTaint = 0; | |
| 185 | 186 | |
| 186 | 187 | if( argc!=4 ){ |
| 187 | 188 | return Th_WrongNumArgs(interp, "foreach varlist list script"); |
| 188 | 189 | } |
| 189 | 190 | rc = Th_SplitList(interp, argv[1], argl[1], &azVar, &anVar, &nVar); |
| 190 | 191 | if( rc ) return rc; |
| 192 | + TH1_XFER_TAINT(bTaint, argl[2]); | |
| 191 | 193 | rc = Th_SplitList(interp, argv[2], argl[2], &azValue, &anValue, &nValue); |
| 192 | 194 | for(ii=0; rc==TH_OK && ii<=nValue-nVar; ii+=nVar){ |
| 193 | 195 | 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); | |
| 195 | 199 | } |
| 196 | 200 | rc = eval_loopbody(interp, argv[3], argl[3]); |
| 197 | 201 | } |
| 198 | 202 | if( rc==TH_BREAK ) rc = TH_OK; |
| 199 | 203 | Th_Free(interp, azVar); |
| @@ -257,10 +261,11 @@ | ||
| 257 | 261 | rc = Th_GetVar(interp, argv[1], argl[1]); |
| 258 | 262 | if( rc==TH_OK ){ |
| 259 | 263 | zList = Th_TakeResult(interp, &nList); |
| 260 | 264 | } |
| 261 | 265 | |
| 266 | + TH1_XFER_TAINT(bTaint, nList); | |
| 262 | 267 | for(i=2; i<argc; i++){ |
| 263 | 268 | TH1_XFER_TAINT(bTaint, argl[i]); |
| 264 | 269 | Th_ListAppend(interp, &zList, &nList, argv[i], argl[i]); |
| 265 | 270 | } |
| 266 | 271 | |
| @@ -289,23 +294,27 @@ | ||
| 289 | 294 | int rc; |
| 290 | 295 | |
| 291 | 296 | char **azElem; |
| 292 | 297 | int *anElem; |
| 293 | 298 | int nCount; |
| 299 | + int bTaint = 0; | |
| 294 | 300 | |
| 295 | 301 | if( argc!=3 ){ |
| 296 | 302 | return Th_WrongNumArgs(interp, "lindex list index"); |
| 297 | 303 | } |
| 298 | 304 | |
| 299 | 305 | if( TH_OK!=Th_ToInt(interp, argv[2], argl[2], &iElem) ){ |
| 300 | 306 | return TH_ERROR; |
| 301 | 307 | } |
| 302 | 308 | |
| 309 | + TH1_XFER_TAINT(bTaint, argl[1]); | |
| 303 | 310 | rc = Th_SplitList(interp, argv[1], argl[1], &azElem, &anElem, &nCount); |
| 304 | 311 | if( rc==TH_OK ){ |
| 305 | 312 | 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); | |
| 307 | 316 | }else{ |
| 308 | 317 | Th_SetResult(interp, 0, 0); |
| 309 | 318 | } |
| 310 | 319 | Th_Free(interp, azElem); |
| 311 | 320 | } |
| @@ -586,11 +595,13 @@ | ||
| 586 | 595 | /* If the last parameter in the parameter list is "args", then set the |
| 587 | 596 | ** ProcDefn.hasArgs flag. The "args" parameter does not require an |
| 588 | 597 | ** entry in the ProcDefn.azParam[] or ProcDefn.azDefault[] arrays. |
| 589 | 598 | */ |
| 590 | 599 | 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 | + ){ | |
| 592 | 603 | p->hasArgs = 1; |
| 593 | 604 | nParam--; |
| 594 | 605 | } |
| 595 | 606 | } |
| 596 | 607 | |
| @@ -830,11 +841,13 @@ | ||
| 830 | 841 | interp, "Expected \"end\" or integer, got:", argv[3], argl[3]); |
| 831 | 842 | return TH_ERROR; |
| 832 | 843 | } |
| 833 | 844 | |
| 834 | 845 | 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); | |
| 836 | 849 | }else{ |
| 837 | 850 | return Th_SetResult(interp, 0, 0); |
| 838 | 851 | } |
| 839 | 852 | } |
| 840 | 853 | |
| @@ -968,10 +981,11 @@ | ||
| 968 | 981 | static int string_range_command( |
| 969 | 982 | Th_Interp *interp, void *ctx, int argc, const char **argv, int *argl |
| 970 | 983 | ){ |
| 971 | 984 | int iStart; |
| 972 | 985 | int iEnd; |
| 986 | + int sz; | |
| 973 | 987 | |
| 974 | 988 | if( argc!=5 ){ |
| 975 | 989 | return Th_WrongNumArgs(interp, "string range string first last"); |
| 976 | 990 | } |
| 977 | 991 | |
| @@ -987,12 +1001,14 @@ | ||
| 987 | 1001 | } |
| 988 | 1002 | |
| 989 | 1003 | if( iStart<0 ) iStart = 0; |
| 990 | 1004 | if( iEnd>=TH1_LEN(argl[2]) ) iEnd = TH1_LEN(argl[2])-1; |
| 991 | 1005 | if( iStart>iEnd ) iEnd = iStart-1; |
| 1006 | + sz = iEnd - iStart + 1; | |
| 1007 | + TH1_XFER_TAINT(sz, argl[2]); | |
| 992 | 1008 | |
| 993 | - return Th_SetResult(interp, &argv[2][iStart], iEnd-iStart+1); | |
| 1009 | + return Th_SetResult(interp, &argv[2][iStart], sz); | |
| 994 | 1010 | } |
| 995 | 1011 | |
| 996 | 1012 | /* |
| 997 | 1013 | ** TH Syntax: |
| 998 | 1014 | ** |
| @@ -1052,10 +1068,11 @@ | ||
| 1052 | 1068 | while( n && th_isspace(z[0]) ){ z++; n--; } |
| 1053 | 1069 | } |
| 1054 | 1070 | if( TH1_LEN(argl[1])<5 || argv[1][4]=='r' ){ |
| 1055 | 1071 | while( n && th_isspace(z[n-1]) ){ n--; } |
| 1056 | 1072 | } |
| 1073 | + TH1_XFER_TAINT(n, argl[2]); | |
| 1057 | 1074 | Th_SetResult(interp, z, n); |
| 1058 | 1075 | return TH_OK; |
| 1059 | 1076 | } |
| 1060 | 1077 | |
| 1061 | 1078 | /* |
| 1062 | 1079 |
| --- 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 @@ | ||
| 31 | 31 | #define TH_INIT_NEED_CONFIG ((u32)0x00000001) /* Open configuration first? */ |
| 32 | 32 | #define TH_INIT_FORCE_TCL ((u32)0x00000002) /* Force Tcl to be enabled? */ |
| 33 | 33 | #define TH_INIT_FORCE_RESET ((u32)0x00000004) /* Force TH1 commands re-added? */ |
| 34 | 34 | #define TH_INIT_FORCE_SETUP ((u32)0x00000008) /* Force eval of setup script? */ |
| 35 | 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. */ | |
| 36 | +#define TH_INIT_MASK ((u32)0x0000001F) /* All possible init flags. */ | |
| 39 | 37 | |
| 40 | 38 | /* |
| 41 | 39 | ** Useful and/or "well-known" combinations of flag values. |
| 42 | 40 | */ |
| 43 | 41 | #define TH_INIT_DEFAULT (TH_INIT_NONE) /* Default flags. */ |
| @@ -297,50 +295,10 @@ | ||
| 297 | 295 | TH1_LEN(argl[1]),argv[1],enableOutput); |
| 298 | 296 | } |
| 299 | 297 | return rc; |
| 300 | 298 | } |
| 301 | 299 | |
| 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 | 300 | /* |
| 343 | 301 | ** Returns a name for a TH1 return code. |
| 344 | 302 | */ |
| 345 | 303 | const char *Th_ReturnCodeName(int rc, int nullIfOk){ |
| 346 | 304 | static char zRc[32]; |
| @@ -376,23 +334,19 @@ | ||
| 376 | 334 | |
| 377 | 335 | /* |
| 378 | 336 | ** Send text to the appropriate output: If pOut is not NULL, it is |
| 379 | 337 | ** appended there, else to the console or to the CGI reply buffer. |
| 380 | 338 | ** 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. | |
| 383 | 340 | ** |
| 384 | 341 | ** If pOut is NULL and the global pThOut is not then that blob |
| 385 | 342 | ** is used for output. |
| 386 | 343 | */ |
| 387 | 344 | static void sendText(Blob *pOut, const char *z, int n, int encode){ |
| 388 | 345 | if(0==pOut && pThOut!=0){ |
| 389 | 346 | pOut = pThOut; |
| 390 | 347 | } |
| 391 | - if(TH_INIT_NO_ENCODE & g.th1Flags){ | |
| 392 | - encode = 0; | |
| 393 | - } | |
| 394 | 348 | if( enableOutput && n ){ |
| 395 | 349 | if( n<0 ){ |
| 396 | 350 | n = strlen(z); |
| 397 | 351 | }else{ |
| 398 | 352 | n = TH1_LEN(n); |
| @@ -540,13 +494,12 @@ | ||
| 540 | 494 | n = argl[1]; |
| 541 | 495 | if( encode==0 && n>0 && TH1_TAINTED(n) ){ |
| 542 | 496 | if( Th_ReportTaint(interp, "output string", argv[1], n) ){ |
| 543 | 497 | return TH_ERROR; |
| 544 | 498 | } |
| 545 | - n = TH1_LEN(n); | |
| 546 | 499 | } |
| 547 | - sendText(0,(char*)argv[1], n, encode); | |
| 500 | + sendText(0,(char*)argv[1], TH1_LEN(n), encode); | |
| 548 | 501 | return TH_OK; |
| 549 | 502 | } |
| 550 | 503 | |
| 551 | 504 | /* |
| 552 | 505 | ** TH1 command: redirect URL ?withMethod? |
| @@ -1450,17 +1403,21 @@ | ||
| 1450 | 1403 | int argc, |
| 1451 | 1404 | const char **argv, |
| 1452 | 1405 | int *argl |
| 1453 | 1406 | ){ |
| 1454 | 1407 | const char *zDefault = 0; |
| 1408 | + const char *zVal; | |
| 1409 | + int sz; | |
| 1455 | 1410 | if( argc!=2 && argc!=3 ){ |
| 1456 | 1411 | return Th_WrongNumArgs(interp, "getParameter NAME ?DEFAULT?"); |
| 1457 | 1412 | } |
| 1458 | 1413 | if( argc==3 ){ |
| 1459 | 1414 | zDefault = argv[2]; |
| 1460 | 1415 | } |
| 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)); | |
| 1462 | 1419 | return TH_OK; |
| 1463 | 1420 | } |
| 1464 | 1421 | |
| 1465 | 1422 | /* |
| 1466 | 1423 | ** TH1 command: setParameter NAME VALUE |
| @@ -2426,11 +2383,10 @@ | ||
| 2426 | 2383 | {"copybtn", copybtnCmd, 0}, |
| 2427 | 2384 | {"date", dateCmd, 0}, |
| 2428 | 2385 | {"decorate", wikiCmd, (void*)&aFlags[2]}, |
| 2429 | 2386 | {"defHeader", defHeaderCmd, 0}, |
| 2430 | 2387 | {"dir", dirCmd, 0}, |
| 2431 | - {"enable_htmlify",enableHtmlifyCmd, 0}, | |
| 2432 | 2388 | {"enable_output", enableOutputCmd, 0}, |
| 2433 | 2389 | {"encode64", encode64Cmd, 0}, |
| 2434 | 2390 | {"getParameter", getParameterCmd, 0}, |
| 2435 | 2391 | {"glob_match", globMatchCmd, 0}, |
| 2436 | 2392 | {"globalState", globalStateCmd, 0}, |
| @@ -2984,11 +2940,16 @@ | ||
| 2984 | 2940 | } |
| 2985 | 2941 | rc = Th_GetVar(g.interp, (char*)zVar, nVar); |
| 2986 | 2942 | z += i+1+n; |
| 2987 | 2943 | i = 0; |
| 2988 | 2944 | 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 | + } | |
| 2990 | 2951 | }else if( z[i]=='<' && isBeginScriptTag(&z[i]) ){ |
| 2991 | 2952 | sendText(pOut,z, i, 0); |
| 2992 | 2953 | z += i+5; |
| 2993 | 2954 | for(i=0; z[i] && (z[i]!='<' || !isEndScriptTag(&z[i])); i++){} |
| 2994 | 2955 | if( g.thTrace ){ |
| @@ -3039,12 +3000,11 @@ | ||
| 3039 | 3000 | ** e.g. via the "render" script function binding, need to use the |
| 3040 | 3001 | ** pThOut blob in order to avoid out-of-order output if |
| 3041 | 3002 | ** Th_SetOutputBlob() has been called. If it has not been called, |
| 3042 | 3003 | ** pThOut will be 0, which will redirect the output to CGI/stdout, |
| 3043 | 3004 | ** 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. | |
| 3046 | 3006 | */; |
| 3047 | 3007 | } |
| 3048 | 3008 | |
| 3049 | 3009 | /* |
| 3050 | 3010 | ** SETTING: vuln-report width=8 default=log |
| @@ -3076,22 +3036,22 @@ | ||
| 3076 | 3036 | Th_Interp *interp, /* Report error here, if an error is reported */ |
| 3077 | 3037 | const char *zWhere, /* Where the tainted string appears */ |
| 3078 | 3038 | const char *zStr, /* The tainted string */ |
| 3079 | 3039 | int nStr /* Length of the tainted string */ |
| 3080 | 3040 | ){ |
| 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 */ | |
| 3083 | 3043 | |
| 3084 | - zDisp = db_get("vuln-report","log"); | |
| 3044 | + if( zDisp==0 ) zDisp = db_get("vuln-report","log"); | |
| 3085 | 3045 | if( is_false(zDisp) ) return 0; |
| 3086 | 3046 | if( strstr(zWhere,"SQL")!=0 ){ |
| 3087 | 3047 | zVulnType = "SQL-injection"; |
| 3088 | 3048 | }else{ |
| 3089 | 3049 | zVulnType = "XSS"; |
| 3090 | 3050 | } |
| 3091 | 3051 | 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\"", | |
| 3093 | 3053 | zVulnType, zWhere, nStr, zStr); |
| 3094 | 3054 | if( strcmp(zDisp,"log")==0 ){ |
| 3095 | 3055 | return 0; |
| 3096 | 3056 | } |
| 3097 | 3057 | if( strcmp(zDisp,"block")==0 ){ |
| @@ -3098,10 +3058,11 @@ | ||
| 3098 | 3058 | char *z = mprintf("tainted %s: \"", zWhere); |
| 3099 | 3059 | Th_ErrorMessage(interp, z, zStr, nStr); |
| 3100 | 3060 | fossil_free(z); |
| 3101 | 3061 | }else{ |
| 3102 | 3062 | char *z = mprintf("%#h", nStr, zStr); |
| 3063 | + zDisp = "off"; | |
| 3103 | 3064 | cgi_reset_content(); |
| 3104 | 3065 | style_submenu_enable(0); |
| 3105 | 3066 | style_set_current_feature("error"); |
| 3106 | 3067 | style_header("Configuration Error"); |
| 3107 | 3068 | @ <p>Error in a TH1 configuration script: |
| 3108 | 3069 |
| --- 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 |
+8
| --- src/th_tcl.c | ||
| +++ src/th_tcl.c | ||
| @@ -24,10 +24,14 @@ | ||
| 24 | 24 | |
| 25 | 25 | #include "sqlite3.h" |
| 26 | 26 | #include "th.h" |
| 27 | 27 | #include "tcl.h" |
| 28 | 28 | |
| 29 | +#if TCL_MAJOR_VERSION<9 && !defined(Tcl_Size) | |
| 30 | +# define Tcl_Size int | |
| 31 | +#endif | |
| 32 | + | |
| 29 | 33 | /* |
| 30 | 34 | ** This macro is used to verify that the header version of Tcl meets some |
| 31 | 35 | ** minimum requirement. |
| 32 | 36 | */ |
| 33 | 37 | #define MINIMUM_TCL_VERSION(major, minor) \ |
| @@ -183,11 +187,15 @@ | ||
| 183 | 187 | ** the only Tcl API functions that MUST be called prior to being able to call |
| 184 | 188 | ** Tcl_InitStubs (i.e. because it requires a Tcl interpreter). For complete |
| 185 | 189 | ** cleanup if the Tcl stubs initialization fails somehow, the Tcl_DeleteInterp |
| 186 | 190 | ** and Tcl_Finalize function types are also required. |
| 187 | 191 | */ |
| 192 | +#if TCL_MAJOR_VERSION>=9 | |
| 188 | 193 | typedef const char *(tcl_FindExecutableProc) (const char *); |
| 194 | +#else | |
| 195 | +typedef void (tcl_FindExecutableProc) (const char *); | |
| 196 | +#endif | |
| 189 | 197 | typedef Tcl_Interp *(tcl_CreateInterpProc) (void); |
| 190 | 198 | typedef void (tcl_DeleteInterpProc) (Tcl_Interp *); |
| 191 | 199 | typedef void (tcl_FinalizeProc) (void); |
| 192 | 200 | |
| 193 | 201 | /* |
| 194 | 202 |
| --- 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 @@ | ||
| 2350 | 2350 | } |
| 2351 | 2351 | if( bSeparateDandP ){ |
| 2352 | 2352 | int n = db_int(0, "SELECT count(*) FROM ok"); |
| 2353 | 2353 | blob_reset(&desc); |
| 2354 | 2354 | 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>", | |
| 2357 | 2357 | 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 | |
| 2360 | 2360 | ); |
| 2361 | 2361 | ridBackTo = 0; |
| 2362 | 2362 | ridFwdTo = 0; |
| 2363 | 2363 | }else{ |
| 2364 | 2364 | blob_appendf(&desc, " of %z%h</a>", |
| @@ -3816,13 +3816,19 @@ | ||
| 3816 | 3816 | " AND (tagxref.value IS NULL OR tagxref.value='%q')", |
| 3817 | 3817 | zBr, zBr, zBr, TAG_BRANCH, zBr, zBr); |
| 3818 | 3818 | } |
| 3819 | 3819 | |
| 3820 | 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 | + } | |
| 3821 | 3827 | /* Complete the above outer select. */ |
| 3822 | 3828 | 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); | |
| 3824 | 3830 | }else{ |
| 3825 | 3831 | blob_append_sql(&sql, "\nORDER BY event.mtime DESC"); |
| 3826 | 3832 | } |
| 3827 | 3833 | if( iOffset>0 ){ |
| 3828 | 3834 | /* Don't handle LIMIT here, otherwise print_timeline() |
| @@ -3847,11 +3853,11 @@ | ||
| 3847 | 3853 | ** Query parameters: |
| 3848 | 3854 | ** |
| 3849 | 3855 | ** today=DATE Use DATE as today's date |
| 3850 | 3856 | */ |
| 3851 | 3857 | 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 }; | |
| 3853 | 3859 | const char *zToday; |
| 3854 | 3860 | char *zStartOfProject; |
| 3855 | 3861 | int i; |
| 3856 | 3862 | Stmt q; |
| 3857 | 3863 | char *z; |
| 3858 | 3864 |
| --- 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 @@ | ||
| 188 | 188 | */ |
| 189 | 189 | static void initializeVariablesFromDb(void){ |
| 190 | 190 | const char *zName; |
| 191 | 191 | Stmt q; |
| 192 | 192 | int i, n, size, j; |
| 193 | + const char *zCTimeColumn = haveTicketCTime ? "tkt_ctime" : "tkt_mtime"; | |
| 193 | 194 | |
| 194 | 195 | zName = PD("name","-none-"); |
| 195 | 196 | 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, " | |
| 197 | 198 | "julianday('now') - tkt_mtime, " |
| 198 | - "julianday('now') - tkt_ctime, *" | |
| 199 | + "julianday('now') - %s, *" | |
| 199 | 200 | " FROM ticket WHERE tkt_uuid GLOB '%q*'", |
| 201 | + zCTimeColumn/*safe-for-%s*/, zCTimeColumn/*safe-for-%s*/, | |
| 200 | 202 | zName); |
| 201 | 203 | if( db_step(&q)==SQLITE_ROW ){ |
| 202 | 204 | n = db_column_count(&q); |
| 203 | 205 | for(i=0; i<n; i++){ |
| 204 | 206 | const char *zVal = db_column_text(&q, i); |
| @@ -774,14 +776,10 @@ | ||
| 774 | 776 | style_submenu_element("Timeline", "%R/info/%T", zUuid); |
| 775 | 777 | } |
| 776 | 778 | zFullName = db_text(0, |
| 777 | 779 | "SELECT tkt_uuid FROM ticket" |
| 778 | 780 | " 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 | 781 | if( g.thTrace ) Th_Trace("BEGIN_TKTVIEW<br>\n", -1); |
| 784 | 782 | ticket_init(); |
| 785 | 783 | initializeVariablesFromCGI(); |
| 786 | 784 | getAllTicketFields(); |
| 787 | 785 | initializeVariablesFromDb(); |
| 788 | 786 |
| --- 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 |
+3
| --- src/tktsetup.c | ||
| +++ src/tktsetup.c | ||
| @@ -496,10 +496,13 @@ | ||
| 496 | 496 | @ } |
| 497 | 497 | @ |
| 498 | 498 | @ if {[capexpr {n}]} { |
| 499 | 499 | @ submenu link "Copy Ticket" /tktnew/$tkt_uuid |
| 500 | 500 | @ } |
| 501 | +@ if {[capexpr {nk}]} { | |
| 502 | +@ submenu link "Edit Wiki" /wikiedit?name=ticket/$tkt_uuid | |
| 503 | +@ } | |
| 501 | 504 | @ </th1> |
| 502 | 505 | @ <tr><td class="tktDspLabel">Title:</td> |
| 503 | 506 | @ <td class="tktDspValue" colspan="3"> |
| 504 | 507 | @ $<title> |
| 505 | 508 | @ </td></tr> |
| 506 | 509 |
| --- 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 @@ | ||
| 326 | 326 | ** |
| 327 | 327 | ** > fossil user contact USERNAME ?CONTACT-INFO? |
| 328 | 328 | ** |
| 329 | 329 | ** Query or set contact information for user USERNAME |
| 330 | 330 | ** |
| 331 | -** > fossil user default ?USERNAME? | |
| 331 | +** > fossil user default ?OPTIONS? ?USERNAME? | |
| 332 | 332 | ** |
| 333 | 333 | ** 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 | |
| 335 | 342 | ** |
| 336 | 343 | ** > fossil user list | ls |
| 337 | 344 | ** |
| 338 | 345 | ** List all users known to the repository |
| 339 | 346 | ** |
| @@ -385,21 +392,50 @@ | ||
| 385 | 392 | &login, zPw, &caps, &contact |
| 386 | 393 | ); |
| 387 | 394 | db_protect_pop(); |
| 388 | 395 | free(zPw); |
| 389 | 396 | }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); | |
| 401 | 437 | } |
| 402 | 438 | } |
| 403 | 439 | }else if(( n>=2 && strncmp(g.argv[2],"list",n)==0 ) || |
| 404 | 440 | ( n>=2 && strncmp(g.argv[2],"ls",n)==0 )){ |
| 405 | 441 | Stmt q; |
| @@ -496,52 +532,54 @@ | ||
| 496 | 532 | /* |
| 497 | 533 | ** Figure out what user is at the controls. |
| 498 | 534 | ** |
| 499 | 535 | ** (1) Use the --user and -U command-line options. |
| 500 | 536 | ** |
| 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. | |
| 514 | 552 | ** |
| 515 | 553 | ** The user name is stored in g.zLogin. The uid is in g.userUid. |
| 516 | 554 | */ |
| 517 | -void user_select(void){ | |
| 555 | +int user_select(void){ | |
| 518 | 556 | UrlData url; |
| 519 | - if( g.userUid ) return; | |
| 557 | + if( g.userUid ) return 1; | |
| 520 | 558 | if( g.zLogin ){ |
| 521 | 559 | if( attempt_user(g.zLogin)==0 ){ |
| 522 | 560 | fossil_fatal("no such user: %s", g.zLogin); |
| 523 | 561 | }else{ |
| 524 | - return; | |
| 562 | + return 2; | |
| 525 | 563 | } |
| 526 | 564 | } |
| 527 | 565 | |
| 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; | |
| 539 | 577 | |
| 540 | 578 | memset(&url, 0, sizeof(url)); |
| 541 | 579 | 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; | |
| 543 | 581 | |
| 544 | 582 | fossil_print( |
| 545 | 583 | "Cannot figure out who you are! Consider using the --user\n" |
| 546 | 584 | "command line option, setting your USER environment variable,\n" |
| 547 | 585 | "or setting a default user with \"fossil user default USER\".\n" |
| 548 | 586 |
| --- 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 @@ | ||
| 304 | 304 | */ |
| 305 | 305 | int win32_filenames_equal_nocase( |
| 306 | 306 | const wchar_t *fn1, |
| 307 | 307 | const wchar_t *fn2 |
| 308 | 308 | ){ |
| 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; | |
| 312 | 324 | static int loaded_CompareStringOrdinal; |
| 313 | 325 | static int loaded_RtlUnicodeStringAPIs; |
| 314 | 326 | if( !loaded_CompareStringOrdinal ){ |
| 315 | - fnCompareStringOrdinal = | |
| 327 | + fnCompareStringOrdinal = (FNCOMPARESTRINGORDINAL) | |
| 316 | 328 | GetProcAddress(GetModuleHandleA("kernel32"),"CompareStringOrdinal"); |
| 317 | 329 | loaded_CompareStringOrdinal = 1; |
| 318 | 330 | } |
| 319 | 331 | if( fnCompareStringOrdinal ){ |
| 320 | 332 | return fnCompareStringOrdinal(fn1,-1,fn2,-1,1)-2==0; |
| 321 | 333 | } |
| 322 | 334 | if( !loaded_RtlUnicodeStringAPIs ){ |
| 323 | - fnRtlInitUnicodeString = | |
| 335 | + fnRtlInitUnicodeString = (FNRTLINITUNICODESTRING) | |
| 324 | 336 | GetProcAddress(GetModuleHandleA("ntdll"),"RtlInitUnicodeString"); |
| 325 | - fnRtlEqualUnicodeString = | |
| 337 | + fnRtlEqualUnicodeString = (FNRTLEQUALUNICODESTRING) | |
| 326 | 338 | GetProcAddress(GetModuleHandleA("ntdll"),"RtlEqualUnicodeString"); |
| 327 | 339 | loaded_RtlUnicodeStringAPIs = 1; |
| 328 | 340 | } |
| 329 | 341 | 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; | |
| 335 | 343 | fnRtlInitUnicodeString(&u1,fn1); |
| 336 | 344 | fnRtlInitUnicodeString(&u2,fn2); |
| 337 | - return (unsigned char)fnRtlEqualUnicodeString(&u1,&u2,1); | |
| 345 | + return (BOOLEAN/*unsigned char*/)fnRtlEqualUnicodeString(&u1,&u2,1); | |
| 338 | 346 | } |
| 339 | 347 | /* In what kind of strange parallel universe are we? */ |
| 340 | 348 | return lstrcmpiW(fn1,fn2)==0; |
| 341 | 349 | } |
| 342 | 350 | |
| @@ -461,11 +469,20 @@ | ||
| 461 | 469 | ** is allocated by mprintf(), or NULL on failure. |
| 462 | 470 | */ |
| 463 | 471 | char *win32_file_id( |
| 464 | 472 | const char *zFileName |
| 465 | 473 | ){ |
| 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; | |
| 467 | 484 | static int loaded_fnGetFileInformationByHandleEx; |
| 468 | 485 | wchar_t *wzFileName = fossil_utf8_to_path(zFileName,0); |
| 469 | 486 | HANDLE hFile; |
| 470 | 487 | char *zFileId = 0; |
| 471 | 488 | hFile = CreateFileW( |
| @@ -476,17 +493,15 @@ | ||
| 476 | 493 | OPEN_EXISTING, |
| 477 | 494 | FILE_FLAG_BACKUP_SEMANTICS, |
| 478 | 495 | NULL); |
| 479 | 496 | if( hFile!=INVALID_HANDLE_VALUE ){ |
| 480 | 497 | 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; | |
| 485 | 499 | if( !loaded_fnGetFileInformationByHandleEx ){ |
| 486 | - fnGetFileInformationByHandleEx = GetProcAddress( | |
| 487 | - GetModuleHandleA("kernel32"),"GetFileInformationByHandleEx"); | |
| 500 | + fnGetFileInformationByHandleEx = (FNGETFILEINFORMATIONBYHANDLEEX) | |
| 501 | + GetProcAddress( | |
| 502 | + GetModuleHandleA("kernel32"),"GetFileInformationByHandleEx"); | |
| 488 | 503 | loaded_fnGetFileInformationByHandleEx = 1; |
| 489 | 504 | } |
| 490 | 505 | if( fnGetFileInformationByHandleEx ){ |
| 491 | 506 | if( fnGetFileInformationByHandleEx( |
| 492 | 507 | hFile,/*FileIdInfo*/0x12,&fi2,sizeof(fi2)) ){ |
| 493 | 508 |
| --- 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 @@ | ||
| 2382 | 2382 | if( nCycle==0 && db_is_writeable("repository") ){ |
| 2383 | 2383 | xfer_syncwith(g.url.canonical, 0); |
| 2384 | 2384 | } |
| 2385 | 2385 | |
| 2386 | 2386 | /* Output current stats */ |
| 2387 | + nRoundtrip++; | |
| 2388 | + nArtifactSent += xfer.nFileSent + xfer.nDeltaSent; | |
| 2387 | 2389 | if( syncFlags & SYNC_VERBOSE ){ |
| 2388 | 2390 | fossil_print(zValueFormat /*works-like:"%s%d%d%d%d"*/, "Sent:", |
| 2389 | 2391 | blob_size(&send), nCardSent+xfer.nGimmeSent+xfer.nIGotSent, |
| 2390 | 2392 | xfer.nFileSent, xfer.nDeltaSent); |
| 2391 | 2393 | }else{ |
| 2392 | - nRoundtrip++; | |
| 2393 | - nArtifactSent += xfer.nFileSent + xfer.nDeltaSent; | |
| 2394 | 2394 | if( bOutIsTty!=0 ){ |
| 2395 | 2395 | fossil_print(zBriefFormat /*works-like:"%d%d%d"*/, |
| 2396 | 2396 | nRoundtrip, nArtifactSent, nArtifactRcvd); |
| 2397 | 2397 | } |
| 2398 | 2398 | } |
| 2399 | 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 | 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 @@ | ||
| 927 | 927 | login_check_credentials(); |
| 928 | 928 | if( !g.perm.Zip ){ login_needed(g.anon.Zip); return; } |
| 929 | 929 | if( fossil_strcmp(g.zPath, "sqlar")==0 ){ |
| 930 | 930 | eType = ARCHIVE_SQLAR; |
| 931 | 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; } | |
| 932 | 935 | }else{ |
| 933 | 936 | eType = ARCHIVE_ZIP; |
| 934 | 937 | zType = "ZIP"; |
| 935 | 938 | } |
| 936 | 939 | fossil_nice_default(); |
| 937 | 940 |
| --- 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 |
+7
| --- test/tester.tcl | ||
| +++ test/tester.tcl | ||
| @@ -356,10 +356,11 @@ | ||
| 356 | 356 | mtime-changes \ |
| 357 | 357 | mv-rm-files \ |
| 358 | 358 | pgp-command \ |
| 359 | 359 | preferred-diff-type \ |
| 360 | 360 | proxy \ |
| 361 | + raw-bgcolor \ | |
| 361 | 362 | redirect-to-https \ |
| 362 | 363 | relative-paths \ |
| 363 | 364 | repo-cksum \ |
| 364 | 365 | repolist-skin \ |
| 365 | 366 | robot-restrict \ |
| @@ -373,13 +374,19 @@ | ||
| 373 | 374 | ssl-identity \ |
| 374 | 375 | tclsh \ |
| 375 | 376 | th1-setup \ |
| 376 | 377 | th1-uri-regexp \ |
| 377 | 378 | ticket-default-report \ |
| 379 | + timeline-hard-newlines \ | |
| 380 | + timeline-plaintext \ | |
| 381 | + timeline-truncate-at-blank \ | |
| 382 | + timeline-tslink-info \ | |
| 378 | 383 | timeline-utc \ |
| 379 | 384 | user-color-map \ |
| 385 | + verify-comments \ | |
| 380 | 386 | uv-sync \ |
| 387 | + vuln-report \ | |
| 381 | 388 | web-browser] |
| 382 | 389 | |
| 383 | 390 | fossil test-th-eval "hasfeature legacyMvRm" |
| 384 | 391 | |
| 385 | 392 | if {[normalize_result] eq "1"} { |
| 386 | 393 | |
| 387 | 394 | 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 |
+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 |
| --- 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 @@ | ||
| 795 | 795 | rpage-\$requested_page\ |
| 796 | 796 | cpage-\$canonical_page\">" [normalize_result]]} |
| 797 | 797 | |
| 798 | 798 | ############################################################################### |
| 799 | 799 | |
| 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}} | |
| 802 | 802 | |
| 803 | 803 | ############################################################################### |
| 804 | 804 | |
| 805 | 805 | test_in_checkout th1-header-2 { |
| 806 | 806 | fossil test-th-eval --open-config "styleHeader {Page Title Here}" |
| 807 | 807 | } {[regexp -- {<title>Fossil: Page Title Here</title>} $RESULT]} |
| 808 | 808 | |
| 809 | 809 | ############################################################################### |
| 810 | 810 | |
| 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}} | |
| 813 | 813 | |
| 814 | 814 | ############################################################################### |
| 815 | 815 | |
| 816 | 816 | fossil test-th-eval --open-config "styleFooter" |
| 817 | 817 | test th1-footer-2 {$RESULT eq {}} |
| @@ -879,44 +879,44 @@ | ||
| 879 | 879 | test th1-artifact-1 {$RESULT eq \ |
| 880 | 880 | {TH_ERROR: wrong # args: should be "artifact ID ?FILENAME?"}} |
| 881 | 881 | |
| 882 | 882 | ############################################################################### |
| 883 | 883 | |
| 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}} | |
| 886 | 886 | |
| 887 | 887 | ############################################################################### |
| 888 | 888 | |
| 889 | 889 | test_in_checkout th1-artifact-3 { |
| 890 | 890 | fossil test-th-eval --open-config "artifact tip" |
| 891 | 891 | } {[regexp -- {F test/th1\.test [0-9a-f]{40,64}} $RESULT]} |
| 892 | 892 | |
| 893 | 893 | ############################################################################### |
| 894 | 894 | |
| 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}} | |
| 897 | 897 | |
| 898 | 898 | ############################################################################### |
| 899 | 899 | |
| 900 | 900 | fossil test-th-eval --open-config "artifact 0000000000" |
| 901 | 901 | test th1-artifact-5 {$RESULT eq {TH_ERROR: name not found}} |
| 902 | 902 | |
| 903 | 903 | ############################################################################### |
| 904 | 904 | |
| 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}} | |
| 907 | 907 | |
| 908 | 908 | ############################################################################### |
| 909 | 909 | |
| 910 | 910 | test_in_checkout th1-artifact-7 { |
| 911 | 911 | fossil test-th-eval --open-config "artifact tip test/th1.test" |
| 912 | 912 | } {[regexp -- {th1-artifact-7} $RESULT]} |
| 913 | 913 | |
| 914 | 914 | ############################################################################### |
| 915 | 915 | |
| 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}} | |
| 918 | 918 | |
| 919 | 919 | ############################################################################### |
| 920 | 920 | |
| 921 | 921 | fossil test-th-eval --open-config "artifact 0000000000 test/th1.test" |
| 922 | 922 | test th1-artifact-9 {$RESULT eq {TH_ERROR: manifest not found}} |
| @@ -947,12 +947,12 @@ | ||
| 947 | 947 | } |
| 948 | 948 | } |
| 949 | 949 | |
| 950 | 950 | ############################################################################### |
| 951 | 951 | |
| 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} | |
| 954 | 954 | |
| 955 | 955 | ############################################################################### |
| 956 | 956 | |
| 957 | 957 | fossil test-th-eval --open-config "globalState configuration" |
| 958 | 958 | test th1-globalState-4 {[string length $RESULT] > 0} |
| @@ -1041,12 +1041,12 @@ | ||
| 1041 | 1041 | fossil test-th-eval "globalState flags" |
| 1042 | 1042 | test th1-globalState-16 {$RESULT eq "0"} |
| 1043 | 1043 | |
| 1044 | 1044 | ############################################################################### |
| 1045 | 1045 | |
| 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 ""} | |
| 1048 | 1048 | |
| 1049 | 1049 | ############################################################################### |
| 1050 | 1050 | |
| 1051 | 1051 | fossil test-th-eval "reinitialize 1; globalState configuration" |
| 1052 | 1052 | test th1-reinitialize-2 {$RESULT ne ""} |
| @@ -1056,29 +1056,29 @@ | ||
| 1056 | 1056 | # |
| 1057 | 1057 | # NOTE: This test will fail if the command names are added to TH1, or |
| 1058 | 1058 | # moved from Tcl builds to plain or the reverse. Sorting the |
| 1059 | 1059 | # command lists eliminates a dependence on order. |
| 1060 | 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 | -} | |
| 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 | 1080 | |
| 1081 | 1081 | ############################################################################### |
| 1082 | 1082 | |
| 1083 | 1083 | fossil test-th-eval "info vars" |
| 1084 | 1084 | |
| @@ -1326,11 +1326,11 @@ | ||
| 1326 | 1326 | |
| 1327 | 1327 | ############################################################################### |
| 1328 | 1328 | |
| 1329 | 1329 | fossil test-th-eval {string is other 123} |
| 1330 | 1330 | 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"} | |
| 1332 | 1332 | |
| 1333 | 1333 | ############################################################################### |
| 1334 | 1334 | |
| 1335 | 1335 | fossil test-th-eval {string is alnum 123} |
| 1336 | 1336 | test th1-string-is-5 {$RESULT eq "1"} |
| 1337 | 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 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 |
+12
-7
| --- tools/makemake.tcl | ||
| +++ tools/makemake.tcl | ||
| @@ -368,39 +368,41 @@ | ||
| 368 | 368 | writeln [string map [list \ |
| 369 | 369 | <<<SQLITE_OPTIONS>>> [join $SQLITE_OPTIONS " \\\n "] \ |
| 370 | 370 | <<<SHELL_OPTIONS>>> [join $SHELL_OPTIONS " \\\n "] \ |
| 371 | 371 | <<<PIKCHR_OPTIONS>>> [join $PIKCHR_OPTIONS " \\\n "] \ |
| 372 | 372 | <<<NEXT_LINE>>> \\] { |
| 373 | -all: $(OBJDIR) $(APPNAME) | |
| 373 | +all: $(APPNAME) | |
| 374 | 374 | |
| 375 | 375 | install: all |
| 376 | 376 | mkdir -p $(INSTALLDIR) |
| 377 | 377 | cp $(APPNAME) $(INSTALLDIR) |
| 378 | 378 | |
| 379 | 379 | codecheck: $(TRANS_SRC) $(OBJDIR)/codecheck1 |
| 380 | 380 | $(OBJDIR)/codecheck1 $(TRANS_SRC) |
| 381 | 381 | |
| 382 | -$(OBJDIR): | |
| 383 | - -mkdir $(OBJDIR) | |
| 384 | - | |
| 385 | 382 | $(OBJDIR)/translate: $(SRCDIR_tools)/translate.c |
| 386 | 383 | -mkdir -p $(OBJDIR) |
| 387 | 384 | $(XBCC) -o $(OBJDIR)/translate $(SRCDIR_tools)/translate.c |
| 388 | 385 | |
| 389 | 386 | $(OBJDIR)/makeheaders: $(SRCDIR_tools)/makeheaders.c |
| 387 | + -mkdir -p $(OBJDIR) | |
| 390 | 388 | $(XBCC) -o $(OBJDIR)/makeheaders $(SRCDIR_tools)/makeheaders.c |
| 391 | 389 | |
| 392 | 390 | $(OBJDIR)/mkindex: $(SRCDIR_tools)/mkindex.c |
| 391 | + -mkdir -p $(OBJDIR) | |
| 393 | 392 | $(XBCC) -o $(OBJDIR)/mkindex $(SRCDIR_tools)/mkindex.c |
| 394 | 393 | |
| 395 | 394 | $(OBJDIR)/mkbuiltin: $(SRCDIR_tools)/mkbuiltin.c |
| 395 | + -mkdir -p $(OBJDIR) | |
| 396 | 396 | $(XBCC) -o $(OBJDIR)/mkbuiltin $(SRCDIR_tools)/mkbuiltin.c |
| 397 | 397 | |
| 398 | 398 | $(OBJDIR)/mkversion: $(SRCDIR_tools)/mkversion.c |
| 399 | + -mkdir -p $(OBJDIR) | |
| 399 | 400 | $(XBCC) -o $(OBJDIR)/mkversion $(SRCDIR_tools)/mkversion.c |
| 400 | 401 | |
| 401 | 402 | $(OBJDIR)/codecheck1: $(SRCDIR_tools)/codecheck1.c |
| 403 | + -mkdir -p $(OBJDIR) | |
| 402 | 404 | $(XBCC) -o $(OBJDIR)/codecheck1 $(SRCDIR_tools)/codecheck1.c |
| 403 | 405 | |
| 404 | 406 | # Run the test suite. |
| 405 | 407 | # Other flags that can be included in TESTFLAGS are: |
| 406 | 408 | # |
| @@ -412,11 +414,11 @@ | ||
| 412 | 414 | # -strict Treat known bugs as failures |
| 413 | 415 | # |
| 414 | 416 | # TESTFLAGS can also include names of specific test files to limit |
| 415 | 417 | # the run to just those test cases. |
| 416 | 418 | # |
| 417 | -test: $(OBJDIR) $(APPNAME) | |
| 419 | +test: $(APPNAME) | |
| 418 | 420 | $(TCLSH) $(SRCDIR)/../test/tester.tcl $(APPNAME) $(TESTFLAGS) |
| 419 | 421 | |
| 420 | 422 | $(OBJDIR)/VERSION.h: $(SRCDIR)/../manifest.uuid $(SRCDIR)/../manifest $(SRCDIR)/../VERSION $(OBJDIR)/mkversion $(OBJDIR)/phony.h |
| 421 | 423 | $(OBJDIR)/mkversion $(SRCDIR)/../manifest.uuid <<<NEXT_LINE>>> |
| 422 | 424 | $(SRCDIR)/../manifest <<<NEXT_LINE>>> |
| @@ -552,23 +554,26 @@ | ||
| 552 | 554 | |
| 553 | 555 | writeln "\$(OBJDIR)/linenoise.o:\t\$(SRCDIR_extsrc)/linenoise.c \$(SRCDIR_extsrc)/linenoise.h" |
| 554 | 556 | writeln "\t\$(XTCC) -c \$(SRCDIR_extsrc)/linenoise.c -o \$@\n" |
| 555 | 557 | |
| 556 | 558 | writeln "\$(OBJDIR)/th.o:\t\$(SRCDIR)/th.c" |
| 559 | +writeln "\t-mkdir -p \$(OBJDIR)\n" | |
| 557 | 560 | writeln "\t\$(XTCC) -c \$(SRCDIR)/th.c -o \$@\n" |
| 558 | 561 | |
| 559 | 562 | writeln "\$(OBJDIR)/th_lang.o:\t\$(SRCDIR)/th_lang.c" |
| 563 | +writeln "\t-mkdir -p \$(OBJDIR)\n" | |
| 560 | 564 | writeln "\t\$(XTCC) -c \$(SRCDIR)/th_lang.c -o \$@\n" |
| 561 | 565 | |
| 562 | 566 | writeln "\$(OBJDIR)/th_tcl.o:\t\$(SRCDIR)/th_tcl.c" |
| 567 | +writeln "\t-mkdir -p \$(OBJDIR)\n" | |
| 563 | 568 | writeln "\t\$(XTCC) -c \$(SRCDIR)/th_tcl.c -o \$@\n" |
| 564 | 569 | |
| 565 | 570 | writeln [string map [list <<<NEXT_LINE>>> \\] { |
| 566 | -$(OBJDIR)/pikchr.o: $(SRCDIR_extsrc)/pikchr.c | |
| 571 | +$(OBJDIR)/pikchr.o: $(SRCDIR_extsrc)/pikchr.c $(OBJDIR)/mkversion | |
| 567 | 572 | $(XTCC) $(PIKCHR_OPTIONS) -c $(SRCDIR_extsrc)/pikchr.c -o $@ |
| 568 | 573 | |
| 569 | -$(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c | |
| 574 | +$(OBJDIR)/cson_amalgamation.o: $(SRCDIR_extsrc)/cson_amalgamation.c $(OBJDIR)/mkversion | |
| 570 | 575 | $(XTCC) -c $(SRCDIR_extsrc)/cson_amalgamation.c -o $@ |
| 571 | 576 | |
| 572 | 577 | $(SRCDIR_extsrc)/pikchr.js: $(SRCDIR_extsrc)/pikchr.c $(MAKEFILE_LIST) |
| 573 | 578 | $(EMCC_WRAPPER) -o $@ $(EMCC_OPT) --no-entry <<<NEXT_LINE>>> |
| 574 | 579 | -sEXPORTED_RUNTIME_METHODS=cwrap,ccall,setValue,getValue,stackSave,stackAlloc,stackRestore <<<NEXT_LINE>>> |
| 575 | 580 |
| --- 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 @@ | ||
| 81 | 81 | the page title taken from the <tt>FOSSIL_REPOLIST_TITLE</tt> |
| 82 | 82 | environment variable. The variable can be defined in the CGI |
| 83 | 83 | control file using the [#setenv|<tt>setenv:</tt>] statement. |
| 84 | 84 | |
| 85 | 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 | |
| 86 | +are optional. They are hidden by default. Show them by | |
| 87 | +etting the <tt>FOSSIL_REPOLIST_SHOW</tt> environment variable to | |
| 89 | 88 | a string that contains substrings "description" and/or "login-group". |
| 90 | 89 | |
| 91 | 90 | The repolist-generated page recurses into subdirectories and will list |
| 92 | 91 | all <tt>*.fossil</tt> files found, with the following exceptions: |
| 93 | 92 | |
| 94 | 93 |
| --- 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 @@ | ||
| 1 | 1 | <title>Change Log</title> |
| 2 | 2 | |
| 3 | -<h2 id='v2_26'>Changes for version 2.26 (pending)</h2> | |
| 3 | +<h2 id='v2_27'>Changes for version 2.27 (pending)</h2> | |
| 4 | 4 | |
| 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: | |
| 6 | 9 | <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. | |
| 9 | 12 | <li> For "gdiff", if no [/help?cmd=gdiff-command|gdiff-command setting] |
| 10 | 13 | is defined, Fossil tries to do a --tk diff if "tclsh" and "wish" |
| 11 | 14 | are available, or a --by diff if not. |
| 12 | 15 | <li> The "Reload" button is added to --tk diffs, to bring the displayed |
| 13 | 16 | diff up to date with the latest changes on disk. |
| 14 | 17 | <li> Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show |
| 15 | 18 | diffs of multiple files. |
| 16 | 19 | </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 | |
| 18 | 21 | 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: | |
| 20 | 23 | <ol type="a"> |
| 21 | 24 | <li> Defaults to using the new [/help?cmd=/ckout|/ckout page] as its |
| 22 | 25 | start page. Or, if the new "--from PATH" option is present, the |
| 23 | 26 | default start page becomes "/ckout?exbase=PATH". |
| 24 | 27 | <li> The new "--extpage FILENAME" option opens the named file as if it |
| 25 | 28 | where in a [./serverext.wiki|CGI extension]. Example usage: the |
| 26 | 29 | person editing this change log has |
| 27 | 30 | "fossil ui --extpage www/changes.wiki" running and hence can |
| 28 | 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. | |
| 29 | 35 | </ol> |
| 30 | - * Enhancements to [/help?cmd=merge|fossil merge]: | |
| 36 | + <li>Enhancements to [/help?cmd=merge|fossil merge]: | |
| 31 | 37 | <ol type="a"> |
| 32 | 38 | <li> Added the [/help?cmd=merge-info|fossil merge-info] command and |
| 33 | 39 | especially the --tk option to that command, to provide analysis |
| 34 | 40 | of the most recent merge or update operation. |
| 35 | 41 | <li> When a merge conflict occurs, a new section is added to the conflict |
| 36 | 42 | text that shows Fossil's suggested resolution to the conflict. |
| 37 | 43 | </ol> |
| 38 | - * Enhancements to [/help?cmd=commit|fossil commit]: | |
| 44 | + <li>Enhancements to [/help?cmd=commit|fossil commit]: | |
| 39 | 45 | <ol type="a"> |
| 40 | 46 | <li> If Fossil sees potential formatting mistakes (ex: bad hyperlinks) |
| 41 | 47 | in the check-in comment, it will alert the developer and give |
| 42 | 48 | him or her the opportunity to edit the comment before continuing. |
| 43 | 49 | This feature is controllable by the |
| @@ -49,17 +55,17 @@ | ||
| 49 | 55 | branch has been changed. |
| 50 | 56 | <li> The interactive checkin comment prompt shows the formatting rules |
| 51 | 57 | set for that repository. |
| 52 | 58 | <li> Add the "--editor" option. |
| 53 | 59 | </ol> |
| 54 | - * Deprecate the --comfmtflags and --comment-format global options and | |
| 60 | + <li>Deprecate the --comfmtflags and --comment-format global options and | |
| 55 | 61 | no longer list them in the built-in help, but keep them working for |
| 56 | 62 | backwards compatibility. |
| 57 | 63 | Alternative TTY comment formatting can still be specified using the |
| 58 | 64 | [/help?cmd=comment-format|comment-format setting], if desired. The |
| 59 | 65 | 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]: | |
| 61 | 67 | <ol type="a"> |
| 62 | 68 | <li> Added the "ml=" ("Merge-in List") query parameter that works |
| 63 | 69 | like "rl=" ("Related List") but adds "mionly" style related |
| 64 | 70 | check-ins instead of the full "rel" style. |
| 65 | 71 | <li> For "tl=", "rl=", and "ml=", the order of the branches in the |
| @@ -82,35 +88,35 @@ | ||
| 82 | 88 | <li> Accept the "Z" (Zulu-time) suffix on date arguments for the |
| 83 | 89 | "ymd" and "yw" query parameters. |
| 84 | 90 | <li> The new "min" query parameter, when added to a from=,to= query, |
| 85 | 91 | collapses long runs of check-ins on the same branch into just |
| 86 | 92 | 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=. | |
| 90 | 96 | <li> The saturation and intensity of user-specified checkin and branch |
| 91 | 97 | background colors are automatically adjusted to keep the colors |
| 92 | 98 | compatible with the current skin, unless the |
| 93 | 99 | [/help?cmd=raw-bgcolor|raw-bgcolor setting] is turned on. |
| 94 | 100 | </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 | |
| 96 | 102 | /doc but keeps the title of markdown documents with the document rather |
| 97 | 103 | 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 | |
| 99 | 105 | 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 | |
| 101 | 107 | 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: | |
| 103 | 109 | <ol type="a"> |
| 104 | 110 | <li> Fix a bug in "fossil patch create" that causes |
| 105 | 111 | [/help?cmd=revert|fossil revert] operations that happened |
| 106 | 112 | on individualfiles after a [/help?cmd=merge|fossil merge] |
| 107 | 113 | to be omitted from the patch. |
| 108 | 114 | <li> Added the [/help?cmd=patch|patch alias] command for managing |
| 109 | 115 | aliases for remote checkout names. |
| 110 | 116 | </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: | |
| 112 | 118 | <ol type="a"> |
| 113 | 119 | <li> Add the ability to search the help text, either in the UI |
| 114 | 120 | (on the [/help?cmd=/search|/search page]) or from the command-line |
| 115 | 121 | (using the "[/help?cmd=search|fossil search -h PATTERN]" command.) |
| 116 | 122 | <li> Accepts an optional SUBCOMMAND argument following the |
| @@ -117,11 +123,11 @@ | ||
| 117 | 123 | COMMAND argument and only shows results for the specified |
| 118 | 124 | subcommand, not the entire command. |
| 119 | 125 | <li> The -u (--usage) option shows only the command-line syntax |
| 120 | 126 | <li> The -o (--options) option shows only the command-line options |
| 121 | 127 | </ol> |
| 122 | - * Enhancements to the ticket system: | |
| 128 | + <li>Enhancements to the [./tickets.wiki|ticket system]: | |
| 123 | 129 | <ol type="a"> |
| 124 | 130 | <li> Added the ability to attach wiki pages to a ticket for extended |
| 125 | 131 | descriptions. |
| 126 | 132 | <li> Added submenu to the 'View Ticket' page, to use it as |
| 127 | 133 | template for a new ticket. |
| @@ -129,21 +135,43 @@ | ||
| 129 | 135 | in a row. |
| 130 | 136 | <li> Link the version field in ticket view to a matching checkin or tag. |
| 131 | 137 | <li> Show creation time in report and ticket view. |
| 132 | 138 | <li> Show previous comments in edit ticket as reference. |
| 133 | 139 | </ol> |
| 134 | - * Added the "hash" query parameter to the | |
| 140 | + <li>Added the "hash" query parameter to the | |
| 135 | 141 | [/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] | |
| 137 | 143 | which alerts subscribers when an admin creates a new user or |
| 138 | 144 | 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 | |
| 141 | 165 | the frequency of reconnection attempts over time and providing feedback |
| 142 | 166 | 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> | |
| 145 | 173 | |
| 146 | 174 | <h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2> |
| 147 | 175 | |
| 148 | 176 | * The "[/help?cmd=ui|fossil ui /]" command now works even for repositories |
| 149 | 177 | that have non-ASCII filenames |
| @@ -1160,12 +1188,11 @@ | ||
| 1160 | 1188 | of rows in a timeline) are held in a cookie and thus persist |
| 1161 | 1189 | across multiple pages. |
| 1162 | 1190 | * Rework the skin editing process so that changes are implemented |
| 1163 | 1191 | on one of nine /draft pages, evaluated, then merged back to the |
| 1164 | 1192 | default. |
| 1165 | - * Added the [https://fossil-scm.org/skins/ardoise/timeline|Ardoise] | |
| 1166 | - skin. | |
| 1193 | + * Added the [/timeline?skin=ardoise&once|Ardoise] skin. | |
| 1167 | 1194 | * Fix the "fossil server" command on Unix to be much more responsive |
| 1168 | 1195 | to multiple simultaneous web requests. |
| 1169 | 1196 | * Use the IPv6 stack for the "fossil ui" and "fossil server" |
| 1170 | 1197 | commands on Windows. |
| 1171 | 1198 | * Support for [https://sqlite.org/sqlar|SQL Archives] as a download |
| 1172 | 1199 |
| --- 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 |
+1
-1
| --- www/containers.md | ||
| +++ www/containers.md | ||
| @@ -670,11 +670,11 @@ | ||
| 670 | 670 | |
| 671 | 671 | [pmmac]: https://podman.io/getting-started/installation.html#macos |
| 672 | 672 | [pmwin]: https://github.com/containers/podman/blob/main/docs/tutorials/podman-for-windows.md |
| 673 | 673 | [Podman]: https://podman.io/ |
| 674 | 674 | [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 | |
| 676 | 676 | |
| 677 | 677 | |
| 678 | 678 | ### 6.3 <a id="nspawn"></a>`systemd-container` |
| 679 | 679 | |
| 680 | 680 | If even the Podman stack is too big for you, the next-best option I’m |
| 681 | 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://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 |
+37
-1
| --- www/customskin.md | ||
| +++ www/customskin.md | ||
| @@ -310,10 +310,11 @@ | ||
| 310 | 310 | with the "--skin ./newskin" option. If the argument to the --skin |
| 311 | 311 | option contains a "/" character, then the five control files are |
| 312 | 312 | read out of the directory named. You can then edit the control |
| 313 | 313 | files in the ./newskin folder using you favorite text editor, and |
| 314 | 314 | press "Reload" on your browser to see the effects. |
| 315 | + | |
| 315 | 316 | |
| 316 | 317 | ### Disabling The Web Browser Cache During Development |
| 317 | 318 | |
| 318 | 319 | Fossil is aggressive about asking the web browser to cache |
| 319 | 320 | resources. While developing a new skin, it is often helpful to |
| @@ -526,11 +527,46 @@ | ||
| 526 | 527 | Iterate until the desired look is achieved. |
| 527 | 528 | |
| 528 | 529 | 4. Copy/paste the resulting css.txt, details.txt, |
| 529 | 530 | header.txt, and footer.txt files |
| 530 | 531 | 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. | |
| 532 | 568 | |
| 533 | 569 | |
| 534 | 570 | ## See Also |
| 535 | 571 | |
| 536 | 572 | * [Customizing the Timeline Graph](customgraph.md) |
| 537 | 573 |
| --- 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 |
+1
-1
| --- www/fossil-v-git.wiki | ||
| +++ www/fossil-v-git.wiki | ||
| @@ -342,11 +342,11 @@ | ||
| 342 | 342 | Fossil isn't entirely C and SQL code. Its web UI [./javascript.md | |
| 343 | 343 | uses JavaScript where |
| 344 | 344 | necessary]. The server-side |
| 345 | 345 | UI scripting uses a custom minimal |
| 346 | 346 | [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 | |
| 348 | 348 | embedded into Fossil itself. Fossil's build system and test suite are |
| 349 | 349 | largely based on Tcl.⁵ All of this is quite portable. |
| 350 | 350 | |
| 351 | 351 | About half of Git's code is POSIX C, and about a third is POSIX shell |
| 352 | 352 | code. This is largely why the so-called "Git for Windows" distributions |
| 353 | 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 | [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 |
+1
-1
| --- www/gsoc-ideas.md | ||
| +++ www/gsoc-ideas.md | ||
| @@ -24,11 +24,11 @@ | ||
| 24 | 24 | # UI, Look and Feel |
| 25 | 25 | |
| 26 | 26 | Tasks for those interested in graphic/web design: |
| 27 | 27 | |
| 28 | 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) | |
| 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 | 30 | * Allow diffing of Forum posts |
| 31 | 31 | * General touch-ups in the existing skins. This may, depending on how deep one |
| 32 | 32 | cares to dig, require digging into C code to find, and potentially modify, how |
| 33 | 33 | the HTML is generated. |
| 34 | 34 | * Creation of one or more new skins. This does not specifically require any C |
| 35 | 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 |
| --- 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 @@ | ||
| 84 | 84 | the repository are consistent prior to each commit. |
| 85 | 85 | |
| 86 | 86 | 8. <b>Free and Open-Source</b> — [../COPYRIGHT-BSD2.txt|2-clause BSD license]. |
| 87 | 87 | |
| 88 | 88 | <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> | |
| 90 | 90 | |
| 91 | 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] | |
| 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 | 95 | * [/timeline?t=release|Timeline of all past releases] |
| 96 | 96 | |
| 97 | 97 | <hr> |
| 98 | 98 | <h3>Quick Start</h3> |
| 99 | 99 | |
| 100 | 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.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 |
+75
-65
| --- www/quickstart.wiki | ||
| +++ www/quickstart.wiki | ||
| @@ -19,15 +19,13 @@ | ||
| 19 | 19 | This is fossil version 2.25 [8f798279d5] 2024-11-06 12:59:09 UTC |
| 20 | 20 | </b></pre> |
| 21 | 21 | |
| 22 | 22 | <h2 id="workflow" name="fslclone">General Work Flow</h2> |
| 23 | 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: | |
| 24 | +Fossil works with [./glossary.md#repository | repository files] | |
| 25 | +and [./glossary.md#check-out | check-out directories] using a | |
| 26 | +workflow like this: | |
| 29 | 27 | |
| 30 | 28 | <ul> |
| 31 | 29 | <li>Create or clone a repository file. ([/help/init|fossil init] or |
| 32 | 30 | [/help/clone | fossil clone]) |
| 33 | 31 | <li>Check out a local tree. ([/help/open | fossil open]) |
| @@ -41,12 +39,11 @@ | ||
| 41 | 39 | The following sections give a brief overview of these |
| 42 | 40 | operations. |
| 43 | 41 | |
| 44 | 42 | <h2 id="new">Starting A New Project</h2> |
| 45 | 43 | |
| 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]: | |
| 48 | 45 | |
| 49 | 46 | <pre><b>fossil init</b> <i>repository-filename</i> |
| 50 | 47 | </pre> |
| 51 | 48 | |
| 52 | 49 | You can name the database anything you like, and you can place it anywhere in the filesystem. |
| @@ -82,14 +79,14 @@ | ||
| 82 | 79 | <h2 id="clone">Cloning An Existing Repository</h2> |
| 83 | 80 | |
| 84 | 81 | Most fossil operations interact with a repository that is on the |
| 85 | 82 | local disk drive, not on a remote system. Hence, before accessing |
| 86 | 83 | 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]". | |
| 89 | 86 | |
| 90 | -Clone a remote repository as follows: ([/help/clone | more info]) | |
| 87 | +This is done as follows: | |
| 91 | 88 | |
| 92 | 89 | <pre><b>fossil clone</b> <i>URL repository-filename</i> |
| 93 | 90 | </pre> |
| 94 | 91 | |
| 95 | 92 | The <i>URL</i> specifies the fossil repository |
| @@ -107,12 +104,20 @@ | ||
| 107 | 104 | 100% complete... |
| 108 | 105 | Extra delta compression... |
| 109 | 106 | Vacuuming the database... |
| 110 | 107 | project-id: 94259BB9F186226D80E49D1FA2DB29F935CCA0333 |
| 111 | 108 | server-id: 016595e9043054038a9ea9bc526d7f33f7ac0e42 |
| 112 | -admin-user: exampleuser (password is "yoWgDR42iv")> | |
| 109 | +admin-user: exampleuser (intial remote-access password is "yoWgDR42iv")> | |
| 113 | 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. | |
| 114 | 119 | |
| 115 | 120 | If the remote repository requires a login, include a |
| 116 | 121 | userid in the URL like this: |
| 117 | 122 | |
| 118 | 123 | <pre><b>fossil clone https://</b><i>remoteuserid</i><b>@www.example.org/ myclone.fossil</b></pre> |
| @@ -153,26 +158,23 @@ | ||
| 153 | 158 | |
| 154 | 159 | <h2 id="checkout">Checking Out A Local Tree</h2> |
| 155 | 160 | |
| 156 | 161 | To work on a project in fossil, you need to check out a local |
| 157 | 162 | 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: | |
| 160 | 164 | |
| 161 | 165 | <pre><b>fossil open</b> <i>repository-filename</i></pre> |
| 162 | 166 | |
| 163 | -for example: | |
| 167 | +For example: | |
| 164 | 168 | |
| 165 | 169 | <pre><b>fossil open ../myclone.fossil |
| 166 | 170 | BUILD.txt |
| 167 | 171 | COPYRIGHT-BSD2.txt |
| 168 | 172 | README.md |
| 169 | 173 | ︙ |
| 170 | 174 | </tt></b></pre> |
| 171 | 175 | |
| 172 | -(or "fossil open ..\myclone.fossil" on Windows). | |
| 173 | - | |
| 174 | 176 | This leaves you with the newest version of the tree |
| 175 | 177 | checked out. |
| 176 | 178 | From anywhere underneath the root of your local tree, you |
| 177 | 179 | can type commands like the following to find out the status of |
| 178 | 180 | your local tree: |
| @@ -320,41 +322,60 @@ | ||
| 320 | 322 | |
| 321 | 323 | This will get you started on identifying checkins. The |
| 322 | 324 | <a href="./checkin_names.wiki">Checkin Names document</a> is a complete reference, including |
| 323 | 325 | how timestamps can also be used. |
| 324 | 326 | |
| 325 | -<h2 id="config">Configuring Your Local Repository</h2> | |
| 327 | +<h2 id="config">Accessing Your Local Repository's Web User Interface</h2> | |
| 326 | 328 | |
| 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: | |
| 332 | 332 | |
| 333 | 333 | <pre> |
| 334 | 334 | <b>fossil ui</b> <i>repository-filename</i> |
| 335 | 335 | </pre> |
| 336 | 336 | |
| 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>] | |
| 338 | 338 | if you are inside a checked-out local tree. |
| 339 | 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: | |
| 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 | 345 | |
| 346 | 346 | <pre> |
| 347 | 347 | <b>fossil setting web-browser</b> <i>path-to-web-browser</i> |
| 348 | 348 | </pre> |
| 349 | 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. | |
| 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.) | |
| 353 | 371 | |
| 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 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. | |
| 356 | 377 | |
| 357 | 378 | <h2 id="sharing">Sharing Changes</h2> |
| 358 | 379 | |
| 359 | 380 | When [./concepts.wiki#workflow|autosync] is turned off, |
| 360 | 381 | the changes you [/help/commit | commit] are only |
| @@ -464,55 +485,44 @@ | ||
| 464 | 485 | level of undo/redo. |
| 465 | 486 | |
| 466 | 487 | |
| 467 | 488 | <h2 id="server">Setting Up A Server</h2> |
| 468 | 489 | |
| 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: | |
| 471 | 492 | |
| 472 | 493 | <pre> |
| 473 | 494 | <b>[/help/server | fossil server]</b> <i>repository-filename</i> |
| 474 | -<b>[/help/ui | fossil ui]</b> <i>repository-filename</i> | |
| 475 | 495 | </pre> |
| 476 | 496 | |
| 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: | |
| 498 | 510 | |
| 499 | 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] | |
| 500 | 515 | <li>[./server/any/inetd.md|inetd] |
| 501 | 516 | <li>[./server/debian/service.md|systemd] |
| 502 | -<li>[./server/any/cgi.md|CGI] | |
| 503 | -<li>[./server/any/scgi.md|SCGI] | |
| 504 | 517 | </ul> |
| 505 | 518 | |
| 506 | 519 | …along with [./server/#matrix | several other options]. |
| 507 | 520 | |
| 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. | |
| 514 | 524 | |
| 515 | 525 | <h2 id="proxy">HTTP Proxies</h2> |
| 516 | 526 | |
| 517 | 527 | If you are behind a restrictive firewall that requires you to use |
| 518 | 528 | an HTTP proxy to reach the internet, then you can configure the proxy |
| 519 | 529 |
| --- 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 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 @@ | ||
| 35 | 35 | </div> |
| 36 | 36 | |
| 37 | 37 | <b>Stephan Beal writes on 2009-01-11:</b> |
| 38 | 38 | |
| 39 | 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.) | |
| 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.) | |
| 55 | 53 | |
| 56 | 54 | So I tried it out. The thing which bugged me most about it was having |
| 57 | 55 | to type "commit" or "com" instead of "ci" for checking in (as is |
| 58 | 56 | custom in all other systems I've used), despite the fact that fossil |
| 59 | 57 | uses "ci" as a filter in things like the timeline view. Looking back |
| 60 | 58 | 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. | |
| 63 | 61 | |
| 64 | 62 | So, having got over typing "fossil com -m ...", here's why I love it so much... |
| 65 | 63 | |
| 66 | 64 | Point #1: CGI |
| 67 | 65 | |
| @@ -70,11 +68,11 @@ | ||
| 70 | 68 | (they don't belong to those projects), which I cannot host in google |
| 71 | 69 | code (because google code doesn't allow/recognize Public Domain as a |
| 72 | 70 | license, and I refuse to relicense just to accommodate them), and for |
| 73 | 71 | which SourceForge is overkill (and way too slow). With fossil I can |
| 74 | 72 | 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 | |
| 76 | 74 | 5 minutes. |
| 77 | 75 | |
| 78 | 76 | Point #2: Wiki |
| 79 | 77 | |
| 80 | 78 | I hate wikis. I really do. Always have. They all have a different |
| 81 | 79 |
| --- 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 @@ | ||
| 12 | 12 | time all of the test cases for SQLite were written in Tcl and Tcl could not |
| 13 | 13 | be easily compiled on the SymbianOS. So TH1 was developed as a cut-down |
| 14 | 14 | version of Tcl that would facilitate running the SQLite test scripts on |
| 15 | 15 | SymbianOS. |
| 16 | 16 | |
| 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. | |
| 19 | 18 | Early prototypes of Fossil were written in pure Tcl. But as the development |
| 20 | 19 | shifted toward the use of C-code, the need arose to have a Tcl-like |
| 21 | 20 | scripting language to help with code generation. TH1 was small and |
| 22 | 21 | light-weight and used minimal resources and seemed ideally suited for the |
| 23 | 22 | task. |
| 24 | 23 | |
| 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. | |
| 26 | 27 | |
| 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 | +-------------------------------- | |
| 28 | 43 | |
| 29 | 44 | TH1 is a string-processing language. All values are strings. Any numerical |
| 30 | 45 | operations are accomplished by converting from string to numeric, performing |
| 31 | 46 | the computation, then converting the result back into a string. (This might |
| 32 | 47 | seem inefficient, but it is faster than people imagine, and numeric |
| 33 | 48 | 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.) | |
| 35 | 50 | |
| 36 | 51 | A TH1 script consists of a sequence of commands. |
| 37 | 52 | Each command is terminated by the first *unescaped* newline or ";" character. |
| 38 | 53 | The text of the command (excluding the newline or semicolon terminator) |
| 39 | 54 | is broken into space-separated tokens. The first token is the command |
| @@ -68,11 +82,11 @@ | ||
| 68 | 82 | are removed from each token by the command parser.) The third token |
| 69 | 83 | is the `puts "hello"`, with its whitespace and newlines. The fourth token |
| 70 | 84 | is `else` and the fifth and last token is `puts "world"`. |
| 71 | 85 | |
| 72 | 86 | 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 | |
| 74 | 88 | second argument (the third token) as a TH1 script. |
| 75 | 89 | If the expression is false and the third argument is `else`, then |
| 76 | 90 | the fourth argument is evaluated as a TH1 expression. |
| 77 | 91 | |
| 78 | 92 | So, you see, even though the example above spans five lines, it is really |
| @@ -106,10 +120,49 @@ | ||
| 106 | 120 | $repository "" info trunk]]] end] |
| 107 | 121 | |
| 108 | 122 | Those backslashes allow the command to wrap nicely within a standard |
| 109 | 123 | terminal width while telling the interpreter to consider those three |
| 110 | 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". | |
| 111 | 164 | |
| 112 | 165 | |
| 113 | 166 | Summary of Core TH1 Commands |
| 114 | 167 | ---------------------------- |
| 115 | 168 | |
| @@ -147,10 +200,13 @@ | ||
| 147 | 200 | * string last NEEDLE HAYSTACK ?START-INDEX? |
| 148 | 201 | * string match PATTERN STRING |
| 149 | 202 | * string length STRING |
| 150 | 203 | * string range STRING FIRST LAST |
| 151 | 204 | * string repeat STRING COUNT |
| 205 | + * string trim STRING | |
| 206 | + * string trimleft STRING | |
| 207 | + * string trimright STRING | |
| 152 | 208 | * unset VARNAME |
| 153 | 209 | * uplevel ?LEVEL? SCRIPT |
| 154 | 210 | * upvar ?FRAME? OTHERVAR MYVAR ?OTHERVAR MYVAR? |
| 155 | 211 | |
| 156 | 212 | All of the above commands work as in the original Tcl. Refer to the |
| @@ -182,11 +238,10 @@ | ||
| 182 | 238 | * [copybtn](#copybtn) |
| 183 | 239 | * [date](#date) |
| 184 | 240 | * [decorate](#decorate) |
| 185 | 241 | * [defHeader](#defHeader) |
| 186 | 242 | * [dir](#dir) |
| 187 | - * [enable\_htmlify](#enable_htmlify) | |
| 188 | 243 | * [enable\_output](#enable_output) |
| 189 | 244 | * [encode64](#encode64) |
| 190 | 245 | * [getParameter](#getParameter) |
| 191 | 246 | * [glob\_match](#glob_match) |
| 192 | 247 | * [globalState](#globalState) |
| @@ -214,17 +269,19 @@ | ||
| 214 | 269 | * [stime](#stime) |
| 215 | 270 | * [styleHeader](#styleHeader) |
| 216 | 271 | * [styleFooter](#styleFooter) |
| 217 | 272 | * [styleScript](#styleScript) |
| 218 | 273 | * [submenu](#submenu) |
| 274 | + * [taint](#taintCmd) | |
| 219 | 275 | * [tclEval](#tclEval) |
| 220 | 276 | * [tclExpr](#tclExpr) |
| 221 | 277 | * [tclInvoke](#tclInvoke) |
| 222 | 278 | * [tclIsSafe](#tclIsSafe) |
| 223 | 279 | * [tclMakeSafe](#tclMakeSafe) |
| 224 | 280 | * [tclReady](#tclReady) |
| 225 | 281 | * [trace](#trace) |
| 282 | + * [untaint](#untaintCmd) | |
| 226 | 283 | * [unversioned content](#unversioned_content) |
| 227 | 284 | * [unversioned list](#unversioned_list) |
| 228 | 285 | * [utime](#utime) |
| 229 | 286 | * [verifyCsrf](#verifyCsrf) |
| 230 | 287 | * [verifyLogin](#verifyLogin) |
| @@ -413,28 +470,10 @@ | ||
| 413 | 470 | the files matching the pattern GLOB within CHECKIN will be returned. |
| 414 | 471 | If DETAILS is non-zero, the result will be a list-of-lists, with each |
| 415 | 472 | element containing at least three elements: the file name, the file |
| 416 | 473 | size (in bytes), and the file last modification time (relative to the |
| 417 | 474 | 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 | 475 | |
| 436 | 476 | <a id="enable_output"></a>TH1 enable\_output Command |
| 437 | 477 | ------------------------------------------------------ |
| 438 | 478 | |
| 439 | 479 | * enable\_output BOOLEAN |
| @@ -528,22 +567,24 @@ | ||
| 528 | 567 | ----------------------------------- |
| 529 | 568 | |
| 530 | 569 | * html STRING |
| 531 | 570 | |
| 532 | 571 | 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 | |
| 534 | 573 | 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 | |
| 536 | 575 | [puts](#puts) command to output text that might contain unescaped |
| 537 | 576 | HTML markup. |
| 538 | 577 | |
| 539 | 578 | **Beware of XSS attacks!** If the STRING value to the html command |
| 540 | 579 | can be controlled by a hostile user, then he might be able to sneak |
| 541 | 580 | in malicious HTML or Javascript which could result in a |
| 542 | 581 | cross-site scripting (XSS) attack. Be careful that all text that |
| 543 | 582 | 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. | |
| 545 | 586 | |
| 546 | 587 | <a id="htmlize"></a>TH1 htmlize Command |
| 547 | 588 | ----------------------------------------- |
| 548 | 589 | |
| 549 | 590 | * htmlize STRING |
| @@ -610,11 +651,13 @@ | ||
| 610 | 651 | * puts STRING |
| 611 | 652 | |
| 612 | 653 | Outputs STRING. Characters within STRING that have special meaning |
| 613 | 654 | in HTML are escaped prior to being output. Thus is it safe for STRING |
| 614 | 655 | 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. | |
| 616 | 659 | |
| 617 | 660 | <a id="query"></a>TH1 query Command |
| 618 | 661 | ------------------------------------- |
| 619 | 662 | |
| 620 | 663 | * query ?-nocomplain? SQL CODE |
| @@ -622,11 +665,14 @@ | ||
| 622 | 665 | Runs the SQL query given by the SQL argument. For each row in the result |
| 623 | 666 | set, run CODE. |
| 624 | 667 | |
| 625 | 668 | In SQL, parameters such as $var are filled in using the value of variable |
| 626 | 669 | "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). | |
| 628 | 674 | |
| 629 | 675 | **Beware of SQL injections in the `query` command!** |
| 630 | 676 | The SQL argument to the query command should always be literal SQL |
| 631 | 677 | text enclosed in {...}. The SQL argument should never be a double-quoted |
| 632 | 678 | string or the value of a \$variable, as those constructs can lead to |
| @@ -649,10 +695,14 @@ | ||
| 649 | 695 | ~~~ |
| 650 | 696 | |
| 651 | 697 | In this second example, TH1 does the expansion of `$mykey` prior to passing |
| 652 | 698 | the text down into SQLite. So if `$mykey` contains a single-quote character, |
| 653 | 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. | |
| 654 | 704 | |
| 655 | 705 | <a id="randhex"></a>TH1 randhex Command |
| 656 | 706 | ----------------------------------------- |
| 657 | 707 | |
| 658 | 708 | * randhex N |
| @@ -783,10 +833,24 @@ | ||
| 783 | 833 | |
| 784 | 834 | * submenu link LABEL URL |
| 785 | 835 | |
| 786 | 836 | Add hyperlink to the submenu of the current page. |
| 787 | 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 | + | |
| 788 | 852 | <a id="tclEval"></a>TH1 tclEval Command |
| 789 | 853 | ----------------------------------------- |
| 790 | 854 | |
| 791 | 855 | **This command requires the Tcl integration feature.** |
| 792 | 856 | |
| @@ -854,10 +918,22 @@ | ||
| 854 | 918 | |
| 855 | 919 | * trace STRING |
| 856 | 920 | |
| 857 | 921 | Generates a TH1 trace message if TH1 tracing is enabled. |
| 858 | 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 | + | |
| 859 | 935 | <a id="unversioned_content"></a>TH1 unversioned content Command |
| 860 | 936 | ----------------------------------------------------------------- |
| 861 | 937 | |
| 862 | 938 | * unversioned content FILENAME |
| 863 | 939 | |
| 864 | 940 |
| --- 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 |