Fossil SCM

Merge from trunk.

brickviking 2024-12-25 13:19 bv-infotool merge
Commit 331e5138804213edd8a41a17011adac4a47c52a52828a20f33d10130de313061
+6 -6
--- Dockerfile
+++ Dockerfile
@@ -3,19 +3,19 @@
33
44
## ---------------------------------------------------------------------
55
## STAGE 1: Build static Fossil binary
66
## ---------------------------------------------------------------------
77
8
-### We aren't pinning to a more stable version of Alpine because we want
8
+### We don't pin a more stable version of our base layer because we want
99
### to build with the latest tools and libraries available in case they
1010
### fixed something that matters to us since the last build. Everything
1111
### below depends on this layer, and so, alas, we toss this container's
1212
### cache on Alpine's release schedule, roughly once a month.
1313
FROM alpine:latest AS bld
1414
WORKDIR /fsl
1515
16
-### Bake the basic Alpine Linux into a base layer so it only changes
16
+### Bake the build-time userland into a base layer so it only changes
1717
### when the upstream image is updated or we change the package set.
1818
RUN set -x \
1919
&& apk update \
2020
&& apk upgrade --no-cache \
2121
&& apk add --no-cache \
@@ -23,19 +23,19 @@
2323
linux-headers musl-dev \
2424
openssl-dev openssl-libs-static \
2525
zlib-dev zlib-static
2626
2727
### Build Fossil as a separate layer so we don't have to rebuild the
28
-### Alpine environment for each iteration of Fossil's dev cycle.
28
+### userland for each iteration of Fossil's dev cycle.
2929
###
3030
### We must cope with a bizarre ADD misfeature here: it unpacks tarballs
3131
### automatically when you give it a local file name but not if you give
3232
### it a /tarball URL! It matters because we default to a URL in case
3333
### you're building outside a Fossil checkout, but when building via the
34
-### container-image target, we avoid a costly hit on fossil-scm.org
35
-### by leveraging its DVCS nature via the "tarball" command and passing
36
-### the resulting file's name in.
34
+### container-image target, we avoid a costly hit on fossil-scm.org by
35
+### leveraging its DVCS nature via the "tarball" command and passing the
36
+### resulting file's name in.
3737
ARG FSLCFG=""
3838
ARG FSLVER="trunk"
3939
ARG FSLURL="https://fossil-scm.org/home/tarball/src?r=${FSLVER}"
4040
ENV FSLSTB=/fsl/src.tar.gz
4141
ADD $FSLURL $FSLSTB
4242
--- Dockerfile
+++ Dockerfile
@@ -3,19 +3,19 @@
3
4 ## ---------------------------------------------------------------------
5 ## STAGE 1: Build static Fossil binary
6 ## ---------------------------------------------------------------------
7
8 ### We aren't pinning to a more stable version of Alpine because we want
9 ### to build with the latest tools and libraries available in case they
10 ### fixed something that matters to us since the last build. Everything
11 ### below depends on this layer, and so, alas, we toss this container's
12 ### cache on Alpine's release schedule, roughly once a month.
13 FROM alpine:latest AS bld
14 WORKDIR /fsl
15
16 ### Bake the basic Alpine Linux into a base layer so it only changes
17 ### when the upstream image is updated or we change the package set.
18 RUN set -x \
19 && apk update \
20 && apk upgrade --no-cache \
21 && apk add --no-cache \
@@ -23,19 +23,19 @@
23 linux-headers musl-dev \
24 openssl-dev openssl-libs-static \
25 zlib-dev zlib-static
26
27 ### Build Fossil as a separate layer so we don't have to rebuild the
28 ### Alpine environment for each iteration of Fossil's dev cycle.
29 ###
30 ### We must cope with a bizarre ADD misfeature here: it unpacks tarballs
31 ### automatically when you give it a local file name but not if you give
32 ### it a /tarball URL! It matters because we default to a URL in case
33 ### you're building outside a Fossil checkout, but when building via the
34 ### container-image target, we avoid a costly hit on fossil-scm.org
35 ### by leveraging its DVCS nature via the "tarball" command and passing
36 ### the resulting file's name in.
37 ARG FSLCFG=""
38 ARG FSLVER="trunk"
39 ARG FSLURL="https://fossil-scm.org/home/tarball/src?r=${FSLVER}"
40 ENV FSLSTB=/fsl/src.tar.gz
41 ADD $FSLURL $FSLSTB
42
--- Dockerfile
+++ Dockerfile
@@ -3,19 +3,19 @@
3
4 ## ---------------------------------------------------------------------
5 ## STAGE 1: Build static Fossil binary
6 ## ---------------------------------------------------------------------
7
8 ### We don't pin a more stable version of our base layer because we want
9 ### to build with the latest tools and libraries available in case they
10 ### fixed something that matters to us since the last build. Everything
11 ### below depends on this layer, and so, alas, we toss this container's
12 ### cache on Alpine's release schedule, roughly once a month.
13 FROM alpine:latest AS bld
14 WORKDIR /fsl
15
16 ### Bake the build-time userland into a base layer so it only changes
17 ### when the upstream image is updated or we change the package set.
18 RUN set -x \
19 && apk update \
20 && apk upgrade --no-cache \
21 && apk add --no-cache \
@@ -23,19 +23,19 @@
23 linux-headers musl-dev \
24 openssl-dev openssl-libs-static \
25 zlib-dev zlib-static
26
27 ### Build Fossil as a separate layer so we don't have to rebuild the
28 ### userland for each iteration of Fossil's dev cycle.
29 ###
30 ### We must cope with a bizarre ADD misfeature here: it unpacks tarballs
31 ### automatically when you give it a local file name but not if you give
32 ### it a /tarball URL! It matters because we default to a URL in case
33 ### you're building outside a Fossil checkout, but when building via the
34 ### container-image target, we avoid a costly hit on fossil-scm.org by
35 ### leveraging its DVCS nature via the "tarball" command and passing the
36 ### resulting file's name in.
37 ARG FSLCFG=""
38 ARG FSLVER="trunk"
39 ARG FSLURL="https://fossil-scm.org/home/tarball/src?r=${FSLVER}"
40 ENV FSLSTB=/fsl/src.tar.gz
41 ADD $FSLURL $FSLSTB
42
+191 -178
--- extsrc/sqlite3.c
+++ extsrc/sqlite3.c
@@ -16,11 +16,11 @@
1616
** if you want a wrapper to interface SQLite with your choice of programming
1717
** language. The code for the "sqlite3" command-line shell is also in a
1818
** separate file. This file contains only code for the core SQLite library.
1919
**
2020
** The content in this amalgamation comes from Fossil check-in
21
-** e2bae4143afd07de1ae55a6d2606a3b541a5 with changes in files:
21
+** e6c30ee52c5cdc193804cec63374d558b45e with changes in files:
2222
**
2323
**
2424
*/
2525
#ifndef SQLITE_AMALGAMATION
2626
#define SQLITE_CORE 1
@@ -465,11 +465,11 @@
465465
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
466466
** [sqlite_version()] and [sqlite_source_id()].
467467
*/
468468
#define SQLITE_VERSION "3.48.0"
469469
#define SQLITE_VERSION_NUMBER 3048000
470
-#define SQLITE_SOURCE_ID "2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653"
470
+#define SQLITE_SOURCE_ID "2024-12-19 19:02:09 e6c30ee52c5cdc193804cec63374d558b45e4d67fc6bde58771ca78485ca0acf"
471471
472472
/*
473473
** CAPI3REF: Run-Time Library Version Numbers
474474
** KEYWORDS: sqlite3_version sqlite3_sourceid
475475
**
@@ -14046,13 +14046,17 @@
1404614046
# define SQLITE_MAX_VDBE_OP 250000000
1404714047
#endif
1404814048
1404914049
/*
1405014050
** The maximum number of arguments to an SQL function.
14051
+**
14052
+** This value has a hard upper limit of 32767 due to storage
14053
+** constraints (it needs to fit inside a i16). We keep it
14054
+** lower than that to prevent abuse.
1405114055
*/
1405214056
#ifndef SQLITE_MAX_FUNCTION_ARG
14053
-# define SQLITE_MAX_FUNCTION_ARG 127
14057
+# define SQLITE_MAX_FUNCTION_ARG 1000
1405414058
#endif
1405514059
1405614060
/*
1405714061
** The suggested maximum number of in-memory pages to use for
1405814062
** the main database table and for temporary tables.
@@ -16049,10 +16053,26 @@
1604916053
#define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */
1605016054
#define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */
1605116055
#define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */
1605216056
#define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */
1605316057
#define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */
16058
+
16059
+#define isWalMode(x) ((x)==PAGER_JOURNALMODE_WAL)
16060
+
16061
+/*
16062
+** The argument to this macro is a file descriptor (type sqlite3_file*).
16063
+** Return 0 if it is not open, or non-zero (but not 1) if it is.
16064
+**
16065
+** This is so that expressions can be written as:
16066
+**
16067
+** if( isOpen(pPager->jfd) ){ ...
16068
+**
16069
+** instead of
16070
+**
16071
+** if( pPager->jfd->pMethods ){ ...
16072
+*/
16073
+#define isOpen(pFd) ((pFd)->pMethods!=0)
1605416074
1605516075
/*
1605616076
** Flags that make up the mask passed to sqlite3PagerGet().
1605716077
*/
1605816078
#define PAGER_GET_NOCONTENT 0x01 /* Do not load data from disk */
@@ -18113,11 +18133,11 @@
1811318133
**
1811418134
** The u.pHash field is used by the global built-ins. The u.pDestructor
1811518135
** field is used by per-connection app-def functions.
1811618136
*/
1811718137
struct FuncDef {
18118
- i8 nArg; /* Number of arguments. -1 means unlimited */
18138
+ i16 nArg; /* Number of arguments. -1 means unlimited */
1811918139
u32 funcFlags; /* Some combination of SQLITE_FUNC_* */
1812018140
void *pUserData; /* User data parameter */
1812118141
FuncDef *pNext; /* Next function with same name */
1812218142
void (*xSFunc)(sqlite3_context*,int,sqlite3_value**); /* func or agg-step */
1812318143
void (*xFinalize)(sqlite3_context*); /* Agg finalizer */
@@ -23708,11 +23728,11 @@
2370823728
Vdbe *pVdbe; /* The VM that owns this context */
2370923729
int iOp; /* Instruction number of OP_Function */
2371023730
int isError; /* Error code returned by the function. */
2371123731
u8 enc; /* Encoding to use for results */
2371223732
u8 skipFlag; /* Skip accumulator loading if true */
23713
- u8 argc; /* Number of arguments */
23733
+ u16 argc; /* Number of arguments */
2371423734
sqlite3_value *argv[1]; /* Argument set */
2371523735
};
2371623736
2371723737
/* A bitfield type for use inside of structures. Always follow with :N where
2371823738
** N is the number of bits.
@@ -58014,24 +58034,10 @@
5801458034
# define USEFETCH(x) ((x)->bUseFetch)
5801558035
#else
5801658036
# define USEFETCH(x) 0
5801758037
#endif
5801858038
58019
-/*
58020
-** The argument to this macro is a file descriptor (type sqlite3_file*).
58021
-** Return 0 if it is not open, or non-zero (but not 1) if it is.
58022
-**
58023
-** This is so that expressions can be written as:
58024
-**
58025
-** if( isOpen(pPager->jfd) ){ ...
58026
-**
58027
-** instead of
58028
-**
58029
-** if( pPager->jfd->pMethods ){ ...
58030
-*/
58031
-#define isOpen(pFd) ((pFd)->pMethods!=0)
58032
-
5803358039
#ifdef SQLITE_DIRECT_OVERFLOW_READ
5803458040
/*
5803558041
** Return true if page pgno can be read directly from the database file
5803658042
** by the b-tree layer. This is the case if:
5803758043
**
@@ -59313,11 +59319,11 @@
5931359319
rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags);
5931459320
}
5931559321
}
5931659322
pPager->journalOff = 0;
5931759323
}else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST
59318
- || (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL)
59324
+ || (pPager->exclusiveMode && pPager->journalMode<PAGER_JOURNALMODE_WAL)
5931959325
){
5932059326
rc = zeroJournalHdr(pPager, hasSuper||pPager->tempFile);
5932159327
pPager->journalOff = 0;
5932259328
}else{
5932359329
/* This branch may be executed with Pager.journalMode==MEMORY if
@@ -68023,15 +68029,11 @@
6802368029
** so it takes care to hold an exclusive lock on the corresponding
6802468030
** WAL_READ_LOCK() while changing values.
6802568031
*/
6802668032
static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){
6802768033
volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */
68028
- u32 mxReadMark; /* Largest aReadMark[] value */
68029
- int mxI; /* Index of largest aReadMark[] value */
68030
- int i; /* Loop counter */
6803168034
int rc = SQLITE_OK; /* Return code */
68032
- u32 mxFrame; /* Wal frame to lock to */
6803368035
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
6803468036
int nBlockTmout = 0;
6803568037
#endif
6803668038
6803768039
assert( pWal->readLock<0 ); /* Not currently locked */
@@ -68133,145 +68135,151 @@
6813368135
6813468136
assert( pWal->nWiData>0 );
6813568137
assert( pWal->apWiData[0]!=0 );
6813668138
pInfo = walCkptInfo(pWal);
6813768139
SEH_INJECT_FAULT;
68138
- if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame
68139
-#ifdef SQLITE_ENABLE_SNAPSHOT
68140
- && ((pWal->bGetSnapshot==0 && pWal->pSnapshot==0) || pWal->hdr.mxFrame==0)
68141
-#endif
68142
- ){
68143
- /* The WAL has been completely backfilled (or it is empty).
68144
- ** and can be safely ignored.
68145
- */
68146
- rc = walLockShared(pWal, WAL_READ_LOCK(0));
68147
- walShmBarrier(pWal);
68148
- if( rc==SQLITE_OK ){
68149
- if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){
68150
- /* It is not safe to allow the reader to continue here if frames
68151
- ** may have been appended to the log before READ_LOCK(0) was obtained.
68152
- ** When holding READ_LOCK(0), the reader ignores the entire log file,
68153
- ** which implies that the database file contains a trustworthy
68154
- ** snapshot. Since holding READ_LOCK(0) prevents a checkpoint from
68155
- ** happening, this is usually correct.
68156
- **
68157
- ** However, if frames have been appended to the log (or if the log
68158
- ** is wrapped and written for that matter) before the READ_LOCK(0)
68159
- ** is obtained, that is not necessarily true. A checkpointer may
68160
- ** have started to backfill the appended frames but crashed before
68161
- ** it finished. Leaving a corrupt image in the database file.
68162
- */
68163
- walUnlockShared(pWal, WAL_READ_LOCK(0));
68164
- return WAL_RETRY;
68165
- }
68166
- pWal->readLock = 0;
68167
- return SQLITE_OK;
68168
- }else if( rc!=SQLITE_BUSY ){
68169
- return rc;
68170
- }
68171
- }
68172
-
68173
- /* If we get this far, it means that the reader will want to use
68174
- ** the WAL to get at content from recent commits. The job now is
68175
- ** to select one of the aReadMark[] entries that is closest to
68176
- ** but not exceeding pWal->hdr.mxFrame and lock that entry.
68177
- */
68178
- mxReadMark = 0;
68179
- mxI = 0;
68180
- mxFrame = pWal->hdr.mxFrame;
68181
-#ifdef SQLITE_ENABLE_SNAPSHOT
68182
- if( pWal->pSnapshot && pWal->pSnapshot->mxFrame<mxFrame ){
68183
- mxFrame = pWal->pSnapshot->mxFrame;
68184
- }
68185
-#endif
68186
- for(i=1; i<WAL_NREADER; i++){
68187
- u32 thisMark = AtomicLoad(pInfo->aReadMark+i); SEH_INJECT_FAULT;
68188
- if( mxReadMark<=thisMark && thisMark<=mxFrame ){
68189
- assert( thisMark!=READMARK_NOT_USED );
68190
- mxReadMark = thisMark;
68191
- mxI = i;
68192
- }
68193
- }
68194
- if( (pWal->readOnly & WAL_SHM_RDONLY)==0
68195
- && (mxReadMark<mxFrame || mxI==0)
68196
- ){
68197
- for(i=1; i<WAL_NREADER; i++){
68198
- rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
68199
- if( rc==SQLITE_OK ){
68200
- AtomicStore(pInfo->aReadMark+i,mxFrame);
68201
- mxReadMark = mxFrame;
68202
- mxI = i;
68203
- walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
68204
- break;
68205
- }else if( rc!=SQLITE_BUSY ){
68206
- return rc;
68207
- }
68208
- }
68209
- }
68210
- if( mxI==0 ){
68211
- assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 );
68212
- return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT;
68213
- }
68214
-
68215
- (void)walEnableBlockingMs(pWal, nBlockTmout);
68216
- rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
68217
- walDisableBlocking(pWal);
68218
- if( rc ){
68219
-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
68220
- if( rc==SQLITE_BUSY_TIMEOUT ){
68221
- *pCnt |= WAL_RETRY_BLOCKED_MASK;
68222
- }
68223
-#else
68224
- assert( rc!=SQLITE_BUSY_TIMEOUT );
68225
-#endif
68226
- assert( (rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT );
68227
- return (rc&0xFF)==SQLITE_BUSY ? WAL_RETRY : rc;
68228
- }
68229
- /* Now that the read-lock has been obtained, check that neither the
68230
- ** value in the aReadMark[] array or the contents of the wal-index
68231
- ** header have changed.
68232
- **
68233
- ** It is necessary to check that the wal-index header did not change
68234
- ** between the time it was read and when the shared-lock was obtained
68235
- ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility
68236
- ** that the log file may have been wrapped by a writer, or that frames
68237
- ** that occur later in the log than pWal->hdr.mxFrame may have been
68238
- ** copied into the database by a checkpointer. If either of these things
68239
- ** happened, then reading the database with the current value of
68240
- ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry
68241
- ** instead.
68242
- **
68243
- ** Before checking that the live wal-index header has not changed
68244
- ** since it was read, set Wal.minFrame to the first frame in the wal
68245
- ** file that has not yet been checkpointed. This client will not need
68246
- ** to read any frames earlier than minFrame from the wal file - they
68247
- ** can be safely read directly from the database file.
68248
- **
68249
- ** Because a ShmBarrier() call is made between taking the copy of
68250
- ** nBackfill and checking that the wal-header in shared-memory still
68251
- ** matches the one cached in pWal->hdr, it is guaranteed that the
68252
- ** checkpointer that set nBackfill was not working with a wal-index
68253
- ** header newer than that cached in pWal->hdr. If it were, that could
68254
- ** cause a problem. The checkpointer could omit to checkpoint
68255
- ** a version of page X that lies before pWal->minFrame (call that version
68256
- ** A) on the basis that there is a newer version (version B) of the same
68257
- ** page later in the wal file. But if version B happens to like past
68258
- ** frame pWal->hdr.mxFrame - then the client would incorrectly assume
68259
- ** that it can read version A from the database file. However, since
68260
- ** we can guarantee that the checkpointer that set nBackfill could not
68261
- ** see any pages past pWal->hdr.mxFrame, this problem does not come up.
68262
- */
68263
- pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1; SEH_INJECT_FAULT;
68264
- walShmBarrier(pWal);
68265
- if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark
68266
- || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
68267
- ){
68268
- walUnlockShared(pWal, WAL_READ_LOCK(mxI));
68269
- return WAL_RETRY;
68270
- }else{
68271
- assert( mxReadMark<=pWal->hdr.mxFrame );
68272
- pWal->readLock = (i16)mxI;
68140
+ {
68141
+ u32 mxReadMark; /* Largest aReadMark[] value */
68142
+ int mxI; /* Index of largest aReadMark[] value */
68143
+ int i; /* Loop counter */
68144
+ u32 mxFrame; /* Wal frame to lock to */
68145
+ if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame
68146
+#ifdef SQLITE_ENABLE_SNAPSHOT
68147
+ && ((pWal->bGetSnapshot==0 && pWal->pSnapshot==0) || pWal->hdr.mxFrame==0)
68148
+#endif
68149
+ ){
68150
+ /* The WAL has been completely backfilled (or it is empty).
68151
+ ** and can be safely ignored.
68152
+ */
68153
+ rc = walLockShared(pWal, WAL_READ_LOCK(0));
68154
+ walShmBarrier(pWal);
68155
+ if( rc==SQLITE_OK ){
68156
+ if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr,sizeof(WalIndexHdr)) ){
68157
+ /* It is not safe to allow the reader to continue here if frames
68158
+ ** may have been appended to the log before READ_LOCK(0) was obtained.
68159
+ ** When holding READ_LOCK(0), the reader ignores the entire log file,
68160
+ ** which implies that the database file contains a trustworthy
68161
+ ** snapshot. Since holding READ_LOCK(0) prevents a checkpoint from
68162
+ ** happening, this is usually correct.
68163
+ **
68164
+ ** However, if frames have been appended to the log (or if the log
68165
+ ** is wrapped and written for that matter) before the READ_LOCK(0)
68166
+ ** is obtained, that is not necessarily true. A checkpointer may
68167
+ ** have started to backfill the appended frames but crashed before
68168
+ ** it finished. Leaving a corrupt image in the database file.
68169
+ */
68170
+ walUnlockShared(pWal, WAL_READ_LOCK(0));
68171
+ return WAL_RETRY;
68172
+ }
68173
+ pWal->readLock = 0;
68174
+ return SQLITE_OK;
68175
+ }else if( rc!=SQLITE_BUSY ){
68176
+ return rc;
68177
+ }
68178
+ }
68179
+
68180
+ /* If we get this far, it means that the reader will want to use
68181
+ ** the WAL to get at content from recent commits. The job now is
68182
+ ** to select one of the aReadMark[] entries that is closest to
68183
+ ** but not exceeding pWal->hdr.mxFrame and lock that entry.
68184
+ */
68185
+ mxReadMark = 0;
68186
+ mxI = 0;
68187
+ mxFrame = pWal->hdr.mxFrame;
68188
+#ifdef SQLITE_ENABLE_SNAPSHOT
68189
+ if( pWal->pSnapshot && pWal->pSnapshot->mxFrame<mxFrame ){
68190
+ mxFrame = pWal->pSnapshot->mxFrame;
68191
+ }
68192
+#endif
68193
+ for(i=1; i<WAL_NREADER; i++){
68194
+ u32 thisMark = AtomicLoad(pInfo->aReadMark+i); SEH_INJECT_FAULT;
68195
+ if( mxReadMark<=thisMark && thisMark<=mxFrame ){
68196
+ assert( thisMark!=READMARK_NOT_USED );
68197
+ mxReadMark = thisMark;
68198
+ mxI = i;
68199
+ }
68200
+ }
68201
+ if( (pWal->readOnly & WAL_SHM_RDONLY)==0
68202
+ && (mxReadMark<mxFrame || mxI==0)
68203
+ ){
68204
+ for(i=1; i<WAL_NREADER; i++){
68205
+ rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
68206
+ if( rc==SQLITE_OK ){
68207
+ AtomicStore(pInfo->aReadMark+i,mxFrame);
68208
+ mxReadMark = mxFrame;
68209
+ mxI = i;
68210
+ walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
68211
+ break;
68212
+ }else if( rc!=SQLITE_BUSY ){
68213
+ return rc;
68214
+ }
68215
+ }
68216
+ }
68217
+ if( mxI==0 ){
68218
+ assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 );
68219
+ return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT;
68220
+ }
68221
+
68222
+ (void)walEnableBlockingMs(pWal, nBlockTmout);
68223
+ rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
68224
+ walDisableBlocking(pWal);
68225
+ if( rc ){
68226
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
68227
+ if( rc==SQLITE_BUSY_TIMEOUT ){
68228
+ *pCnt |= WAL_RETRY_BLOCKED_MASK;
68229
+ }
68230
+#else
68231
+ assert( rc!=SQLITE_BUSY_TIMEOUT );
68232
+#endif
68233
+ assert((rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT);
68234
+ return (rc&0xFF)==SQLITE_BUSY ? WAL_RETRY : rc;
68235
+ }
68236
+ /* Now that the read-lock has been obtained, check that neither the
68237
+ ** value in the aReadMark[] array or the contents of the wal-index
68238
+ ** header have changed.
68239
+ **
68240
+ ** It is necessary to check that the wal-index header did not change
68241
+ ** between the time it was read and when the shared-lock was obtained
68242
+ ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility
68243
+ ** that the log file may have been wrapped by a writer, or that frames
68244
+ ** that occur later in the log than pWal->hdr.mxFrame may have been
68245
+ ** copied into the database by a checkpointer. If either of these things
68246
+ ** happened, then reading the database with the current value of
68247
+ ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry
68248
+ ** instead.
68249
+ **
68250
+ ** Before checking that the live wal-index header has not changed
68251
+ ** since it was read, set Wal.minFrame to the first frame in the wal
68252
+ ** file that has not yet been checkpointed. This client will not need
68253
+ ** to read any frames earlier than minFrame from the wal file - they
68254
+ ** can be safely read directly from the database file.
68255
+ **
68256
+ ** Because a ShmBarrier() call is made between taking the copy of
68257
+ ** nBackfill and checking that the wal-header in shared-memory still
68258
+ ** matches the one cached in pWal->hdr, it is guaranteed that the
68259
+ ** checkpointer that set nBackfill was not working with a wal-index
68260
+ ** header newer than that cached in pWal->hdr. If it were, that could
68261
+ ** cause a problem. The checkpointer could omit to checkpoint
68262
+ ** a version of page X that lies before pWal->minFrame (call that version
68263
+ ** A) on the basis that there is a newer version (version B) of the same
68264
+ ** page later in the wal file. But if version B happens to like past
68265
+ ** frame pWal->hdr.mxFrame - then the client would incorrectly assume
68266
+ ** that it can read version A from the database file. However, since
68267
+ ** we can guarantee that the checkpointer that set nBackfill could not
68268
+ ** see any pages past pWal->hdr.mxFrame, this problem does not come up.
68269
+ */
68270
+ pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1; SEH_INJECT_FAULT;
68271
+ walShmBarrier(pWal);
68272
+ if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark
68273
+ || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
68274
+ ){
68275
+ walUnlockShared(pWal, WAL_READ_LOCK(mxI));
68276
+ return WAL_RETRY;
68277
+ }else{
68278
+ assert( mxReadMark<=pWal->hdr.mxFrame );
68279
+ pWal->readLock = (i16)mxI;
68280
+ }
6827368281
}
6827468282
return rc;
6827568283
}
6827668284
6827768285
#ifdef SQLITE_ENABLE_SNAPSHOT
@@ -91889,11 +91897,11 @@
9188991897
**
9189091898
** sqlite3_column_int()
9189191899
** sqlite3_column_int64()
9189291900
** sqlite3_column_text()
9189391901
** sqlite3_column_text16()
91894
-** sqlite3_column_real()
91902
+** sqlite3_column_double()
9189591903
** sqlite3_column_bytes()
9189691904
** sqlite3_column_bytes16()
9189791905
** sqlite3_column_blob()
9189891906
*/
9189991907
static void columnMallocFailure(sqlite3_stmt *pStmt)
@@ -109896,11 +109904,11 @@
109896109904
p4 = sqlite3BinaryCompareCollSeq(pParse, pLeft, pRight);
109897109905
}
109898109906
p5 = binaryCompareP5(pLeft, pRight, jumpIfNull);
109899109907
addr = sqlite3VdbeAddOp4(pParse->pVdbe, opcode, in2, dest, in1,
109900109908
(void*)p4, P4_COLLSEQ);
109901
- sqlite3VdbeChangeP5(pParse->pVdbe, (u8)p5);
109909
+ sqlite3VdbeChangeP5(pParse->pVdbe, (u16)p5);
109902109910
return addr;
109903109911
}
109904109912
109905109913
/*
109906109914
** Return true if expression pExpr is a vector, or false otherwise.
@@ -129732,11 +129740,11 @@
129732129740
|| (argc==3 && sqlite3_value_type(argv[2])==SQLITE_NULL)
129733129741
){
129734129742
return;
129735129743
}
129736129744
p0type = sqlite3_value_type(argv[0]);
129737
- p1 = sqlite3_value_int(argv[1]);
129745
+ p1 = sqlite3_value_int64(argv[1]);
129738129746
if( p0type==SQLITE_BLOB ){
129739129747
len = sqlite3_value_bytes(argv[0]);
129740129748
z = sqlite3_value_blob(argv[0]);
129741129749
if( z==0 ) return;
129742129750
assert( len==sqlite3_value_bytes(argv[0]) );
@@ -129757,11 +129765,11 @@
129757129765
** from 2009-02-02 for compatibility of applications that exploited the
129758129766
** old buggy behavior. */
129759129767
if( p1==0 ) p1 = 1; /* <rdar://problem/6778339> */
129760129768
#endif
129761129769
if( argc==3 ){
129762
- p2 = sqlite3_value_int(argv[2]);
129770
+ p2 = sqlite3_value_int64(argv[2]);
129763129771
if( p2<0 ){
129764129772
p2 = -p2;
129765129773
negP2 = 1;
129766129774
}
129767129775
}else{
@@ -129796,13 +129804,15 @@
129796129804
SQLITE_SKIP_UTF8(z2);
129797129805
}
129798129806
sqlite3_result_text64(context, (char*)z, z2-z, SQLITE_TRANSIENT,
129799129807
SQLITE_UTF8);
129800129808
}else{
129801
- if( p1+p2>len ){
129809
+ if( p1>=len ){
129810
+ p1 = p2 = 0;
129811
+ }else if( p2>len-p1 ){
129802129812
p2 = len-p1;
129803
- if( p2<0 ) p2 = 0;
129813
+ assert( p2>0 );
129804129814
}
129805129815
sqlite3_result_blob64(context, (char*)&z[p1], (u64)p2, SQLITE_TRANSIENT);
129806129816
}
129807129817
}
129808129818
@@ -129809,17 +129819,17 @@
129809129819
/*
129810129820
** Implementation of the round() function
129811129821
*/
129812129822
#ifndef SQLITE_OMIT_FLOATING_POINT
129813129823
static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
129814
- int n = 0;
129824
+ i64 n = 0;
129815129825
double r;
129816129826
char *zBuf;
129817129827
assert( argc==1 || argc==2 );
129818129828
if( argc==2 ){
129819129829
if( SQLITE_NULL==sqlite3_value_type(argv[1]) ) return;
129820
- n = sqlite3_value_int(argv[1]);
129830
+ n = sqlite3_value_int64(argv[1]);
129821129831
if( n>30 ) n = 30;
129822129832
if( n<0 ) n = 0;
129823129833
}
129824129834
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
129825129835
r = sqlite3_value_double(argv[0]);
@@ -141316,11 +141326,11 @@
141316141326
sqlite3VdbeAddOp3(v, OP_Null, 0, 8, 8+cnt);
141317141327
sqlite3ClearTempRegCache(pParse);
141318141328
141319141329
/* Do the b-tree integrity checks */
141320141330
sqlite3VdbeAddOp4(v, OP_IntegrityCk, 1, cnt, 8, (char*)aRoot,P4_INTARRAY);
141321
- sqlite3VdbeChangeP5(v, (u8)i);
141331
+ sqlite3VdbeChangeP5(v, (u16)i);
141322141332
addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v);
141323141333
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
141324141334
sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zDbSName),
141325141335
P4_DYNAMIC);
141326141336
sqlite3VdbeAddOp3(v, OP_Concat, 2, 3, 3);
@@ -150549,11 +150559,11 @@
150549150559
}
150550150560
sqlite3ReleaseTempReg(pParse, regSubtype);
150551150561
}
150552150562
sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
150553150563
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
150554
- sqlite3VdbeChangeP5(v, (u8)nArg);
150564
+ sqlite3VdbeChangeP5(v, (u16)nArg);
150555150565
sqlite3VdbeAddOp2(v, OP_Next, pF->iOBTab, iTop+1); VdbeCoverage(v);
150556150566
sqlite3VdbeJumpHere(v, iTop);
150557150567
sqlite3ReleaseTempRange(pParse, regAgg, nArg);
150558150568
}
150559150569
sqlite3VdbeAddOp2(v, OP_AggFinal, AggInfoFuncReg(pAggInfo,i),
@@ -150712,11 +150722,11 @@
150712150722
sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0,
150713150723
(char *)pColl, P4_COLLSEQ);
150714150724
}
150715150725
sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
150716150726
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
150717
- sqlite3VdbeChangeP5(v, (u8)nArg);
150727
+ sqlite3VdbeChangeP5(v, (u16)nArg);
150718150728
sqlite3ReleaseTempRange(pParse, regAgg, nArg);
150719150729
}
150720150730
if( addrNext ){
150721150731
sqlite3VdbeResolveLabel(v, addrNext);
150722150732
}
@@ -154106,11 +154116,11 @@
154106154116
/* Set the P5 operand of the OP_Program instruction to non-zero if
154107154117
** recursive invocation of this trigger program is disallowed. Recursive
154108154118
** invocation is disallowed if (a) the sub-program is really a trigger,
154109154119
** not a foreign key action, and (b) the flag to enable recursive triggers
154110154120
** is clear. */
154111
- sqlite3VdbeChangeP5(v, (u8)bRecursive);
154121
+ sqlite3VdbeChangeP5(v, (u16)bRecursive);
154112154122
}
154113154123
}
154114154124
154115154125
/*
154116154126
** This is called to code the required FOR EACH ROW triggers for an operation
@@ -170414,10 +170424,11 @@
170414170424
int pc,
170415170425
VdbeOp *pOp
170416170426
){
170417170427
if( (db->flags & SQLITE_VdbeAddopTrace)==0 ) return;
170418170428
sqlite3VdbePrintOp(0, pc, pOp);
170429
+ sqlite3ShowWhereTerm(0); /* So compiler won't complain about unused func */
170419170430
}
170420170431
#endif
170421170432
170422170433
/*
170423170434
** Generate the end of the WHERE loop. See comments on
@@ -172523,11 +172534,11 @@
172523172534
sqlite3VdbeAddOp4(v, OP_CollSeq, 0,0,0, (const char*)pColl, P4_COLLSEQ);
172524172535
}
172525172536
sqlite3VdbeAddOp3(v, bInverse? OP_AggInverse : OP_AggStep,
172526172537
bInverse, regArg, pWin->regAccum);
172527172538
sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF);
172528
- sqlite3VdbeChangeP5(v, (u8)nArg);
172539
+ sqlite3VdbeChangeP5(v, (u16)nArg);
172529172540
if( pWin->bExprArgs ){
172530172541
sqlite3ReleaseTempRange(pParse, regArg, nArg);
172531172542
}
172532172543
}
172533172544
@@ -184078,12 +184089,12 @@
184078184089
# error SQLITE_MAX_COMPOUND_SELECT must be at least 2
184079184090
#endif
184080184091
#if SQLITE_MAX_VDBE_OP<40
184081184092
# error SQLITE_MAX_VDBE_OP must be at least 40
184082184093
#endif
184083
-#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>127
184084
-# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 127
184094
+#if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>32767
184095
+# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 32767
184085184096
#endif
184086184097
#if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>125
184087184098
# error SQLITE_MAX_ATTACHED must be between 0 and 125
184088184099
#endif
184089184100
#if SQLITE_MAX_LIKE_PATTERN_LENGTH<1
@@ -185492,11 +185503,10 @@
185492185503
#if defined(SQLITE_DEBUG)
185493185504
/* Invoke these debugging routines so that the compiler does not
185494185505
** issue "defined but not used" warnings. */
185495185506
if( x==9999 ){
185496185507
sqlite3ShowExpr(0);
185497
- sqlite3ShowExpr(0);
185498185508
sqlite3ShowExprList(0);
185499185509
sqlite3ShowIdList(0);
185500185510
sqlite3ShowSrcList(0);
185501185511
sqlite3ShowWith(0);
185502185512
sqlite3ShowUpsert(0);
@@ -185509,11 +185519,10 @@
185509185519
#ifndef SQLITE_OMIT_WINDOWFUNC
185510185520
sqlite3ShowWindow(0);
185511185521
sqlite3ShowWinFunc(0);
185512185522
#endif
185513185523
sqlite3ShowSelect(0);
185514
- sqlite3ShowWhereTerm(0);
185515185524
}
185516185525
#endif
185517185526
break;
185518185527
}
185519185528
@@ -226344,11 +226353,15 @@
226344226353
static int dbpageSync(sqlite3_vtab *pVtab){
226345226354
DbpageTable *pTab = (DbpageTable *)pVtab;
226346226355
if( pTab->pgnoTrunc>0 ){
226347226356
Btree *pBt = pTab->db->aDb[pTab->iDbTrunc].pBt;
226348226357
Pager *pPager = sqlite3BtreePager(pBt);
226349
- sqlite3PagerTruncateImage(pPager, pTab->pgnoTrunc);
226358
+ sqlite3BtreeEnter(pBt);
226359
+ if( pTab->pgnoTrunc<sqlite3BtreeLastPage(pBt) ){
226360
+ sqlite3PagerTruncateImage(pPager, pTab->pgnoTrunc);
226361
+ }
226362
+ sqlite3BtreeLeave(pBt);
226350226363
}
226351226364
pTab->pgnoTrunc = 0;
226352226365
return SQLITE_OK;
226353226366
}
226354226367
@@ -255419,11 +255432,11 @@
255419255432
int nArg, /* Number of args */
255420255433
sqlite3_value **apUnused /* Function arguments */
255421255434
){
255422255435
assert( nArg==0 );
255423255436
UNUSED_PARAM2(nArg, apUnused);
255424
- sqlite3_result_text(pCtx, "fts5: 2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653", -1, SQLITE_TRANSIENT);
255437
+ sqlite3_result_text(pCtx, "fts5: 2024-12-19 14:20:47 5b96dcf5f6bf41dcb89ced64efd4585e36dce718c428c2324d94e4942905c3bb", -1, SQLITE_TRANSIENT);
255425255438
}
255426255439
255427255440
/*
255428255441
** Implementation of fts5_locale(LOCALE, TEXT) function.
255429255442
**
255430255443
--- 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 ** e2bae4143afd07de1ae55a6d2606a3b541a5 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.48.0"
469 #define SQLITE_VERSION_NUMBER 3048000
470 #define SQLITE_SOURCE_ID "2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653"
471
472 /*
473 ** CAPI3REF: Run-Time Library Version Numbers
474 ** KEYWORDS: sqlite3_version sqlite3_sourceid
475 **
@@ -14046,13 +14046,17 @@
14046 # define SQLITE_MAX_VDBE_OP 250000000
14047 #endif
14048
14049 /*
14050 ** The maximum number of arguments to an SQL function.
 
 
 
 
14051 */
14052 #ifndef SQLITE_MAX_FUNCTION_ARG
14053 # define SQLITE_MAX_FUNCTION_ARG 127
14054 #endif
14055
14056 /*
14057 ** The suggested maximum number of in-memory pages to use for
14058 ** the main database table and for temporary tables.
@@ -16049,10 +16053,26 @@
16049 #define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */
16050 #define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */
16051 #define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */
16052 #define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */
16053 #define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16054
16055 /*
16056 ** Flags that make up the mask passed to sqlite3PagerGet().
16057 */
16058 #define PAGER_GET_NOCONTENT 0x01 /* Do not load data from disk */
@@ -18113,11 +18133,11 @@
18113 **
18114 ** The u.pHash field is used by the global built-ins. The u.pDestructor
18115 ** field is used by per-connection app-def functions.
18116 */
18117 struct FuncDef {
18118 i8 nArg; /* Number of arguments. -1 means unlimited */
18119 u32 funcFlags; /* Some combination of SQLITE_FUNC_* */
18120 void *pUserData; /* User data parameter */
18121 FuncDef *pNext; /* Next function with same name */
18122 void (*xSFunc)(sqlite3_context*,int,sqlite3_value**); /* func or agg-step */
18123 void (*xFinalize)(sqlite3_context*); /* Agg finalizer */
@@ -23708,11 +23728,11 @@
23708 Vdbe *pVdbe; /* The VM that owns this context */
23709 int iOp; /* Instruction number of OP_Function */
23710 int isError; /* Error code returned by the function. */
23711 u8 enc; /* Encoding to use for results */
23712 u8 skipFlag; /* Skip accumulator loading if true */
23713 u8 argc; /* Number of arguments */
23714 sqlite3_value *argv[1]; /* Argument set */
23715 };
23716
23717 /* A bitfield type for use inside of structures. Always follow with :N where
23718 ** N is the number of bits.
@@ -58014,24 +58034,10 @@
58014 # define USEFETCH(x) ((x)->bUseFetch)
58015 #else
58016 # define USEFETCH(x) 0
58017 #endif
58018
58019 /*
58020 ** The argument to this macro is a file descriptor (type sqlite3_file*).
58021 ** Return 0 if it is not open, or non-zero (but not 1) if it is.
58022 **
58023 ** This is so that expressions can be written as:
58024 **
58025 ** if( isOpen(pPager->jfd) ){ ...
58026 **
58027 ** instead of
58028 **
58029 ** if( pPager->jfd->pMethods ){ ...
58030 */
58031 #define isOpen(pFd) ((pFd)->pMethods!=0)
58032
58033 #ifdef SQLITE_DIRECT_OVERFLOW_READ
58034 /*
58035 ** Return true if page pgno can be read directly from the database file
58036 ** by the b-tree layer. This is the case if:
58037 **
@@ -59313,11 +59319,11 @@
59313 rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags);
59314 }
59315 }
59316 pPager->journalOff = 0;
59317 }else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST
59318 || (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL)
59319 ){
59320 rc = zeroJournalHdr(pPager, hasSuper||pPager->tempFile);
59321 pPager->journalOff = 0;
59322 }else{
59323 /* This branch may be executed with Pager.journalMode==MEMORY if
@@ -68023,15 +68029,11 @@
68023 ** so it takes care to hold an exclusive lock on the corresponding
68024 ** WAL_READ_LOCK() while changing values.
68025 */
68026 static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){
68027 volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */
68028 u32 mxReadMark; /* Largest aReadMark[] value */
68029 int mxI; /* Index of largest aReadMark[] value */
68030 int i; /* Loop counter */
68031 int rc = SQLITE_OK; /* Return code */
68032 u32 mxFrame; /* Wal frame to lock to */
68033 #ifdef SQLITE_ENABLE_SETLK_TIMEOUT
68034 int nBlockTmout = 0;
68035 #endif
68036
68037 assert( pWal->readLock<0 ); /* Not currently locked */
@@ -68133,145 +68135,151 @@
68133
68134 assert( pWal->nWiData>0 );
68135 assert( pWal->apWiData[0]!=0 );
68136 pInfo = walCkptInfo(pWal);
68137 SEH_INJECT_FAULT;
68138 if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame
68139 #ifdef SQLITE_ENABLE_SNAPSHOT
68140 && ((pWal->bGetSnapshot==0 && pWal->pSnapshot==0) || pWal->hdr.mxFrame==0)
68141 #endif
68142 ){
68143 /* The WAL has been completely backfilled (or it is empty).
68144 ** and can be safely ignored.
68145 */
68146 rc = walLockShared(pWal, WAL_READ_LOCK(0));
68147 walShmBarrier(pWal);
68148 if( rc==SQLITE_OK ){
68149 if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr)) ){
68150 /* It is not safe to allow the reader to continue here if frames
68151 ** may have been appended to the log before READ_LOCK(0) was obtained.
68152 ** When holding READ_LOCK(0), the reader ignores the entire log file,
68153 ** which implies that the database file contains a trustworthy
68154 ** snapshot. Since holding READ_LOCK(0) prevents a checkpoint from
68155 ** happening, this is usually correct.
68156 **
68157 ** However, if frames have been appended to the log (or if the log
68158 ** is wrapped and written for that matter) before the READ_LOCK(0)
68159 ** is obtained, that is not necessarily true. A checkpointer may
68160 ** have started to backfill the appended frames but crashed before
68161 ** it finished. Leaving a corrupt image in the database file.
68162 */
68163 walUnlockShared(pWal, WAL_READ_LOCK(0));
68164 return WAL_RETRY;
68165 }
68166 pWal->readLock = 0;
68167 return SQLITE_OK;
68168 }else if( rc!=SQLITE_BUSY ){
68169 return rc;
68170 }
68171 }
68172
68173 /* If we get this far, it means that the reader will want to use
68174 ** the WAL to get at content from recent commits. The job now is
68175 ** to select one of the aReadMark[] entries that is closest to
68176 ** but not exceeding pWal->hdr.mxFrame and lock that entry.
68177 */
68178 mxReadMark = 0;
68179 mxI = 0;
68180 mxFrame = pWal->hdr.mxFrame;
68181 #ifdef SQLITE_ENABLE_SNAPSHOT
68182 if( pWal->pSnapshot && pWal->pSnapshot->mxFrame<mxFrame ){
68183 mxFrame = pWal->pSnapshot->mxFrame;
68184 }
68185 #endif
68186 for(i=1; i<WAL_NREADER; i++){
68187 u32 thisMark = AtomicLoad(pInfo->aReadMark+i); SEH_INJECT_FAULT;
68188 if( mxReadMark<=thisMark && thisMark<=mxFrame ){
68189 assert( thisMark!=READMARK_NOT_USED );
68190 mxReadMark = thisMark;
68191 mxI = i;
68192 }
68193 }
68194 if( (pWal->readOnly & WAL_SHM_RDONLY)==0
68195 && (mxReadMark<mxFrame || mxI==0)
68196 ){
68197 for(i=1; i<WAL_NREADER; i++){
68198 rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
68199 if( rc==SQLITE_OK ){
68200 AtomicStore(pInfo->aReadMark+i,mxFrame);
68201 mxReadMark = mxFrame;
68202 mxI = i;
68203 walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
68204 break;
68205 }else if( rc!=SQLITE_BUSY ){
68206 return rc;
68207 }
68208 }
68209 }
68210 if( mxI==0 ){
68211 assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 );
68212 return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT;
68213 }
68214
68215 (void)walEnableBlockingMs(pWal, nBlockTmout);
68216 rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
68217 walDisableBlocking(pWal);
68218 if( rc ){
68219 #ifdef SQLITE_ENABLE_SETLK_TIMEOUT
68220 if( rc==SQLITE_BUSY_TIMEOUT ){
68221 *pCnt |= WAL_RETRY_BLOCKED_MASK;
68222 }
68223 #else
68224 assert( rc!=SQLITE_BUSY_TIMEOUT );
68225 #endif
68226 assert( (rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT );
68227 return (rc&0xFF)==SQLITE_BUSY ? WAL_RETRY : rc;
68228 }
68229 /* Now that the read-lock has been obtained, check that neither the
68230 ** value in the aReadMark[] array or the contents of the wal-index
68231 ** header have changed.
68232 **
68233 ** It is necessary to check that the wal-index header did not change
68234 ** between the time it was read and when the shared-lock was obtained
68235 ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility
68236 ** that the log file may have been wrapped by a writer, or that frames
68237 ** that occur later in the log than pWal->hdr.mxFrame may have been
68238 ** copied into the database by a checkpointer. If either of these things
68239 ** happened, then reading the database with the current value of
68240 ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry
68241 ** instead.
68242 **
68243 ** Before checking that the live wal-index header has not changed
68244 ** since it was read, set Wal.minFrame to the first frame in the wal
68245 ** file that has not yet been checkpointed. This client will not need
68246 ** to read any frames earlier than minFrame from the wal file - they
68247 ** can be safely read directly from the database file.
68248 **
68249 ** Because a ShmBarrier() call is made between taking the copy of
68250 ** nBackfill and checking that the wal-header in shared-memory still
68251 ** matches the one cached in pWal->hdr, it is guaranteed that the
68252 ** checkpointer that set nBackfill was not working with a wal-index
68253 ** header newer than that cached in pWal->hdr. If it were, that could
68254 ** cause a problem. The checkpointer could omit to checkpoint
68255 ** a version of page X that lies before pWal->minFrame (call that version
68256 ** A) on the basis that there is a newer version (version B) of the same
68257 ** page later in the wal file. But if version B happens to like past
68258 ** frame pWal->hdr.mxFrame - then the client would incorrectly assume
68259 ** that it can read version A from the database file. However, since
68260 ** we can guarantee that the checkpointer that set nBackfill could not
68261 ** see any pages past pWal->hdr.mxFrame, this problem does not come up.
68262 */
68263 pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1; SEH_INJECT_FAULT;
68264 walShmBarrier(pWal);
68265 if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark
68266 || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
68267 ){
68268 walUnlockShared(pWal, WAL_READ_LOCK(mxI));
68269 return WAL_RETRY;
68270 }else{
68271 assert( mxReadMark<=pWal->hdr.mxFrame );
68272 pWal->readLock = (i16)mxI;
 
 
 
 
 
 
68273 }
68274 return rc;
68275 }
68276
68277 #ifdef SQLITE_ENABLE_SNAPSHOT
@@ -91889,11 +91897,11 @@
91889 **
91890 ** sqlite3_column_int()
91891 ** sqlite3_column_int64()
91892 ** sqlite3_column_text()
91893 ** sqlite3_column_text16()
91894 ** sqlite3_column_real()
91895 ** sqlite3_column_bytes()
91896 ** sqlite3_column_bytes16()
91897 ** sqlite3_column_blob()
91898 */
91899 static void columnMallocFailure(sqlite3_stmt *pStmt)
@@ -109896,11 +109904,11 @@
109896 p4 = sqlite3BinaryCompareCollSeq(pParse, pLeft, pRight);
109897 }
109898 p5 = binaryCompareP5(pLeft, pRight, jumpIfNull);
109899 addr = sqlite3VdbeAddOp4(pParse->pVdbe, opcode, in2, dest, in1,
109900 (void*)p4, P4_COLLSEQ);
109901 sqlite3VdbeChangeP5(pParse->pVdbe, (u8)p5);
109902 return addr;
109903 }
109904
109905 /*
109906 ** Return true if expression pExpr is a vector, or false otherwise.
@@ -129732,11 +129740,11 @@
129732 || (argc==3 && sqlite3_value_type(argv[2])==SQLITE_NULL)
129733 ){
129734 return;
129735 }
129736 p0type = sqlite3_value_type(argv[0]);
129737 p1 = sqlite3_value_int(argv[1]);
129738 if( p0type==SQLITE_BLOB ){
129739 len = sqlite3_value_bytes(argv[0]);
129740 z = sqlite3_value_blob(argv[0]);
129741 if( z==0 ) return;
129742 assert( len==sqlite3_value_bytes(argv[0]) );
@@ -129757,11 +129765,11 @@
129757 ** from 2009-02-02 for compatibility of applications that exploited the
129758 ** old buggy behavior. */
129759 if( p1==0 ) p1 = 1; /* <rdar://problem/6778339> */
129760 #endif
129761 if( argc==3 ){
129762 p2 = sqlite3_value_int(argv[2]);
129763 if( p2<0 ){
129764 p2 = -p2;
129765 negP2 = 1;
129766 }
129767 }else{
@@ -129796,13 +129804,15 @@
129796 SQLITE_SKIP_UTF8(z2);
129797 }
129798 sqlite3_result_text64(context, (char*)z, z2-z, SQLITE_TRANSIENT,
129799 SQLITE_UTF8);
129800 }else{
129801 if( p1+p2>len ){
 
 
129802 p2 = len-p1;
129803 if( p2<0 ) p2 = 0;
129804 }
129805 sqlite3_result_blob64(context, (char*)&z[p1], (u64)p2, SQLITE_TRANSIENT);
129806 }
129807 }
129808
@@ -129809,17 +129819,17 @@
129809 /*
129810 ** Implementation of the round() function
129811 */
129812 #ifndef SQLITE_OMIT_FLOATING_POINT
129813 static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
129814 int n = 0;
129815 double r;
129816 char *zBuf;
129817 assert( argc==1 || argc==2 );
129818 if( argc==2 ){
129819 if( SQLITE_NULL==sqlite3_value_type(argv[1]) ) return;
129820 n = sqlite3_value_int(argv[1]);
129821 if( n>30 ) n = 30;
129822 if( n<0 ) n = 0;
129823 }
129824 if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
129825 r = sqlite3_value_double(argv[0]);
@@ -141316,11 +141326,11 @@
141316 sqlite3VdbeAddOp3(v, OP_Null, 0, 8, 8+cnt);
141317 sqlite3ClearTempRegCache(pParse);
141318
141319 /* Do the b-tree integrity checks */
141320 sqlite3VdbeAddOp4(v, OP_IntegrityCk, 1, cnt, 8, (char*)aRoot,P4_INTARRAY);
141321 sqlite3VdbeChangeP5(v, (u8)i);
141322 addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v);
141323 sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
141324 sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zDbSName),
141325 P4_DYNAMIC);
141326 sqlite3VdbeAddOp3(v, OP_Concat, 2, 3, 3);
@@ -150549,11 +150559,11 @@
150549 }
150550 sqlite3ReleaseTempReg(pParse, regSubtype);
150551 }
150552 sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
150553 sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
150554 sqlite3VdbeChangeP5(v, (u8)nArg);
150555 sqlite3VdbeAddOp2(v, OP_Next, pF->iOBTab, iTop+1); VdbeCoverage(v);
150556 sqlite3VdbeJumpHere(v, iTop);
150557 sqlite3ReleaseTempRange(pParse, regAgg, nArg);
150558 }
150559 sqlite3VdbeAddOp2(v, OP_AggFinal, AggInfoFuncReg(pAggInfo,i),
@@ -150712,11 +150722,11 @@
150712 sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0,
150713 (char *)pColl, P4_COLLSEQ);
150714 }
150715 sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
150716 sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
150717 sqlite3VdbeChangeP5(v, (u8)nArg);
150718 sqlite3ReleaseTempRange(pParse, regAgg, nArg);
150719 }
150720 if( addrNext ){
150721 sqlite3VdbeResolveLabel(v, addrNext);
150722 }
@@ -154106,11 +154116,11 @@
154106 /* Set the P5 operand of the OP_Program instruction to non-zero if
154107 ** recursive invocation of this trigger program is disallowed. Recursive
154108 ** invocation is disallowed if (a) the sub-program is really a trigger,
154109 ** not a foreign key action, and (b) the flag to enable recursive triggers
154110 ** is clear. */
154111 sqlite3VdbeChangeP5(v, (u8)bRecursive);
154112 }
154113 }
154114
154115 /*
154116 ** This is called to code the required FOR EACH ROW triggers for an operation
@@ -170414,10 +170424,11 @@
170414 int pc,
170415 VdbeOp *pOp
170416 ){
170417 if( (db->flags & SQLITE_VdbeAddopTrace)==0 ) return;
170418 sqlite3VdbePrintOp(0, pc, pOp);
 
170419 }
170420 #endif
170421
170422 /*
170423 ** Generate the end of the WHERE loop. See comments on
@@ -172523,11 +172534,11 @@
172523 sqlite3VdbeAddOp4(v, OP_CollSeq, 0,0,0, (const char*)pColl, P4_COLLSEQ);
172524 }
172525 sqlite3VdbeAddOp3(v, bInverse? OP_AggInverse : OP_AggStep,
172526 bInverse, regArg, pWin->regAccum);
172527 sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF);
172528 sqlite3VdbeChangeP5(v, (u8)nArg);
172529 if( pWin->bExprArgs ){
172530 sqlite3ReleaseTempRange(pParse, regArg, nArg);
172531 }
172532 }
172533
@@ -184078,12 +184089,12 @@
184078 # error SQLITE_MAX_COMPOUND_SELECT must be at least 2
184079 #endif
184080 #if SQLITE_MAX_VDBE_OP<40
184081 # error SQLITE_MAX_VDBE_OP must be at least 40
184082 #endif
184083 #if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>127
184084 # error SQLITE_MAX_FUNCTION_ARG must be between 0 and 127
184085 #endif
184086 #if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>125
184087 # error SQLITE_MAX_ATTACHED must be between 0 and 125
184088 #endif
184089 #if SQLITE_MAX_LIKE_PATTERN_LENGTH<1
@@ -185492,11 +185503,10 @@
185492 #if defined(SQLITE_DEBUG)
185493 /* Invoke these debugging routines so that the compiler does not
185494 ** issue "defined but not used" warnings. */
185495 if( x==9999 ){
185496 sqlite3ShowExpr(0);
185497 sqlite3ShowExpr(0);
185498 sqlite3ShowExprList(0);
185499 sqlite3ShowIdList(0);
185500 sqlite3ShowSrcList(0);
185501 sqlite3ShowWith(0);
185502 sqlite3ShowUpsert(0);
@@ -185509,11 +185519,10 @@
185509 #ifndef SQLITE_OMIT_WINDOWFUNC
185510 sqlite3ShowWindow(0);
185511 sqlite3ShowWinFunc(0);
185512 #endif
185513 sqlite3ShowSelect(0);
185514 sqlite3ShowWhereTerm(0);
185515 }
185516 #endif
185517 break;
185518 }
185519
@@ -226344,11 +226353,15 @@
226344 static int dbpageSync(sqlite3_vtab *pVtab){
226345 DbpageTable *pTab = (DbpageTable *)pVtab;
226346 if( pTab->pgnoTrunc>0 ){
226347 Btree *pBt = pTab->db->aDb[pTab->iDbTrunc].pBt;
226348 Pager *pPager = sqlite3BtreePager(pBt);
226349 sqlite3PagerTruncateImage(pPager, pTab->pgnoTrunc);
 
 
 
 
226350 }
226351 pTab->pgnoTrunc = 0;
226352 return SQLITE_OK;
226353 }
226354
@@ -255419,11 +255432,11 @@
255419 int nArg, /* Number of args */
255420 sqlite3_value **apUnused /* Function arguments */
255421 ){
255422 assert( nArg==0 );
255423 UNUSED_PARAM2(nArg, apUnused);
255424 sqlite3_result_text(pCtx, "fts5: 2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653", -1, SQLITE_TRANSIENT);
255425 }
255426
255427 /*
255428 ** Implementation of fts5_locale(LOCALE, TEXT) function.
255429 **
255430
--- 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 ** e6c30ee52c5cdc193804cec63374d558b45e 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.48.0"
469 #define SQLITE_VERSION_NUMBER 3048000
470 #define SQLITE_SOURCE_ID "2024-12-19 19:02:09 e6c30ee52c5cdc193804cec63374d558b45e4d67fc6bde58771ca78485ca0acf"
471
472 /*
473 ** CAPI3REF: Run-Time Library Version Numbers
474 ** KEYWORDS: sqlite3_version sqlite3_sourceid
475 **
@@ -14046,13 +14046,17 @@
14046 # define SQLITE_MAX_VDBE_OP 250000000
14047 #endif
14048
14049 /*
14050 ** The maximum number of arguments to an SQL function.
14051 **
14052 ** This value has a hard upper limit of 32767 due to storage
14053 ** constraints (it needs to fit inside a i16). We keep it
14054 ** lower than that to prevent abuse.
14055 */
14056 #ifndef SQLITE_MAX_FUNCTION_ARG
14057 # define SQLITE_MAX_FUNCTION_ARG 1000
14058 #endif
14059
14060 /*
14061 ** The suggested maximum number of in-memory pages to use for
14062 ** the main database table and for temporary tables.
@@ -16049,10 +16053,26 @@
16053 #define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */
16054 #define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */
16055 #define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */
16056 #define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */
16057 #define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */
16058
16059 #define isWalMode(x) ((x)==PAGER_JOURNALMODE_WAL)
16060
16061 /*
16062 ** The argument to this macro is a file descriptor (type sqlite3_file*).
16063 ** Return 0 if it is not open, or non-zero (but not 1) if it is.
16064 **
16065 ** This is so that expressions can be written as:
16066 **
16067 ** if( isOpen(pPager->jfd) ){ ...
16068 **
16069 ** instead of
16070 **
16071 ** if( pPager->jfd->pMethods ){ ...
16072 */
16073 #define isOpen(pFd) ((pFd)->pMethods!=0)
16074
16075 /*
16076 ** Flags that make up the mask passed to sqlite3PagerGet().
16077 */
16078 #define PAGER_GET_NOCONTENT 0x01 /* Do not load data from disk */
@@ -18113,11 +18133,11 @@
18133 **
18134 ** The u.pHash field is used by the global built-ins. The u.pDestructor
18135 ** field is used by per-connection app-def functions.
18136 */
18137 struct FuncDef {
18138 i16 nArg; /* Number of arguments. -1 means unlimited */
18139 u32 funcFlags; /* Some combination of SQLITE_FUNC_* */
18140 void *pUserData; /* User data parameter */
18141 FuncDef *pNext; /* Next function with same name */
18142 void (*xSFunc)(sqlite3_context*,int,sqlite3_value**); /* func or agg-step */
18143 void (*xFinalize)(sqlite3_context*); /* Agg finalizer */
@@ -23708,11 +23728,11 @@
23728 Vdbe *pVdbe; /* The VM that owns this context */
23729 int iOp; /* Instruction number of OP_Function */
23730 int isError; /* Error code returned by the function. */
23731 u8 enc; /* Encoding to use for results */
23732 u8 skipFlag; /* Skip accumulator loading if true */
23733 u16 argc; /* Number of arguments */
23734 sqlite3_value *argv[1]; /* Argument set */
23735 };
23736
23737 /* A bitfield type for use inside of structures. Always follow with :N where
23738 ** N is the number of bits.
@@ -58014,24 +58034,10 @@
58034 # define USEFETCH(x) ((x)->bUseFetch)
58035 #else
58036 # define USEFETCH(x) 0
58037 #endif
58038
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58039 #ifdef SQLITE_DIRECT_OVERFLOW_READ
58040 /*
58041 ** Return true if page pgno can be read directly from the database file
58042 ** by the b-tree layer. This is the case if:
58043 **
@@ -59313,11 +59319,11 @@
59319 rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags);
59320 }
59321 }
59322 pPager->journalOff = 0;
59323 }else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST
59324 || (pPager->exclusiveMode && pPager->journalMode<PAGER_JOURNALMODE_WAL)
59325 ){
59326 rc = zeroJournalHdr(pPager, hasSuper||pPager->tempFile);
59327 pPager->journalOff = 0;
59328 }else{
59329 /* This branch may be executed with Pager.journalMode==MEMORY if
@@ -68023,15 +68029,11 @@
68029 ** so it takes care to hold an exclusive lock on the corresponding
68030 ** WAL_READ_LOCK() while changing values.
68031 */
68032 static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){
68033 volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */
 
 
 
68034 int rc = SQLITE_OK; /* Return code */
 
68035 #ifdef SQLITE_ENABLE_SETLK_TIMEOUT
68036 int nBlockTmout = 0;
68037 #endif
68038
68039 assert( pWal->readLock<0 ); /* Not currently locked */
@@ -68133,145 +68135,151 @@
68135
68136 assert( pWal->nWiData>0 );
68137 assert( pWal->apWiData[0]!=0 );
68138 pInfo = walCkptInfo(pWal);
68139 SEH_INJECT_FAULT;
68140 {
68141 u32 mxReadMark; /* Largest aReadMark[] value */
68142 int mxI; /* Index of largest aReadMark[] value */
68143 int i; /* Loop counter */
68144 u32 mxFrame; /* Wal frame to lock to */
68145 if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame
68146 #ifdef SQLITE_ENABLE_SNAPSHOT
68147 && ((pWal->bGetSnapshot==0 && pWal->pSnapshot==0) || pWal->hdr.mxFrame==0)
68148 #endif
68149 ){
68150 /* The WAL has been completely backfilled (or it is empty).
68151 ** and can be safely ignored.
68152 */
68153 rc = walLockShared(pWal, WAL_READ_LOCK(0));
68154 walShmBarrier(pWal);
68155 if( rc==SQLITE_OK ){
68156 if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr,sizeof(WalIndexHdr)) ){
68157 /* It is not safe to allow the reader to continue here if frames
68158 ** may have been appended to the log before READ_LOCK(0) was obtained.
68159 ** When holding READ_LOCK(0), the reader ignores the entire log file,
68160 ** which implies that the database file contains a trustworthy
68161 ** snapshot. Since holding READ_LOCK(0) prevents a checkpoint from
68162 ** happening, this is usually correct.
68163 **
68164 ** However, if frames have been appended to the log (or if the log
68165 ** is wrapped and written for that matter) before the READ_LOCK(0)
68166 ** is obtained, that is not necessarily true. A checkpointer may
68167 ** have started to backfill the appended frames but crashed before
68168 ** it finished. Leaving a corrupt image in the database file.
68169 */
68170 walUnlockShared(pWal, WAL_READ_LOCK(0));
68171 return WAL_RETRY;
68172 }
68173 pWal->readLock = 0;
68174 return SQLITE_OK;
68175 }else if( rc!=SQLITE_BUSY ){
68176 return rc;
68177 }
68178 }
68179
68180 /* If we get this far, it means that the reader will want to use
68181 ** the WAL to get at content from recent commits. The job now is
68182 ** to select one of the aReadMark[] entries that is closest to
68183 ** but not exceeding pWal->hdr.mxFrame and lock that entry.
68184 */
68185 mxReadMark = 0;
68186 mxI = 0;
68187 mxFrame = pWal->hdr.mxFrame;
68188 #ifdef SQLITE_ENABLE_SNAPSHOT
68189 if( pWal->pSnapshot && pWal->pSnapshot->mxFrame<mxFrame ){
68190 mxFrame = pWal->pSnapshot->mxFrame;
68191 }
68192 #endif
68193 for(i=1; i<WAL_NREADER; i++){
68194 u32 thisMark = AtomicLoad(pInfo->aReadMark+i); SEH_INJECT_FAULT;
68195 if( mxReadMark<=thisMark && thisMark<=mxFrame ){
68196 assert( thisMark!=READMARK_NOT_USED );
68197 mxReadMark = thisMark;
68198 mxI = i;
68199 }
68200 }
68201 if( (pWal->readOnly & WAL_SHM_RDONLY)==0
68202 && (mxReadMark<mxFrame || mxI==0)
68203 ){
68204 for(i=1; i<WAL_NREADER; i++){
68205 rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
68206 if( rc==SQLITE_OK ){
68207 AtomicStore(pInfo->aReadMark+i,mxFrame);
68208 mxReadMark = mxFrame;
68209 mxI = i;
68210 walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
68211 break;
68212 }else if( rc!=SQLITE_BUSY ){
68213 return rc;
68214 }
68215 }
68216 }
68217 if( mxI==0 ){
68218 assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 );
68219 return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT;
68220 }
68221
68222 (void)walEnableBlockingMs(pWal, nBlockTmout);
68223 rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
68224 walDisableBlocking(pWal);
68225 if( rc ){
68226 #ifdef SQLITE_ENABLE_SETLK_TIMEOUT
68227 if( rc==SQLITE_BUSY_TIMEOUT ){
68228 *pCnt |= WAL_RETRY_BLOCKED_MASK;
68229 }
68230 #else
68231 assert( rc!=SQLITE_BUSY_TIMEOUT );
68232 #endif
68233 assert((rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT);
68234 return (rc&0xFF)==SQLITE_BUSY ? WAL_RETRY : rc;
68235 }
68236 /* Now that the read-lock has been obtained, check that neither the
68237 ** value in the aReadMark[] array or the contents of the wal-index
68238 ** header have changed.
68239 **
68240 ** It is necessary to check that the wal-index header did not change
68241 ** between the time it was read and when the shared-lock was obtained
68242 ** on WAL_READ_LOCK(mxI) was obtained to account for the possibility
68243 ** that the log file may have been wrapped by a writer, or that frames
68244 ** that occur later in the log than pWal->hdr.mxFrame may have been
68245 ** copied into the database by a checkpointer. If either of these things
68246 ** happened, then reading the database with the current value of
68247 ** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry
68248 ** instead.
68249 **
68250 ** Before checking that the live wal-index header has not changed
68251 ** since it was read, set Wal.minFrame to the first frame in the wal
68252 ** file that has not yet been checkpointed. This client will not need
68253 ** to read any frames earlier than minFrame from the wal file - they
68254 ** can be safely read directly from the database file.
68255 **
68256 ** Because a ShmBarrier() call is made between taking the copy of
68257 ** nBackfill and checking that the wal-header in shared-memory still
68258 ** matches the one cached in pWal->hdr, it is guaranteed that the
68259 ** checkpointer that set nBackfill was not working with a wal-index
68260 ** header newer than that cached in pWal->hdr. If it were, that could
68261 ** cause a problem. The checkpointer could omit to checkpoint
68262 ** a version of page X that lies before pWal->minFrame (call that version
68263 ** A) on the basis that there is a newer version (version B) of the same
68264 ** page later in the wal file. But if version B happens to like past
68265 ** frame pWal->hdr.mxFrame - then the client would incorrectly assume
68266 ** that it can read version A from the database file. However, since
68267 ** we can guarantee that the checkpointer that set nBackfill could not
68268 ** see any pages past pWal->hdr.mxFrame, this problem does not come up.
68269 */
68270 pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1; SEH_INJECT_FAULT;
68271 walShmBarrier(pWal);
68272 if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark
68273 || memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
68274 ){
68275 walUnlockShared(pWal, WAL_READ_LOCK(mxI));
68276 return WAL_RETRY;
68277 }else{
68278 assert( mxReadMark<=pWal->hdr.mxFrame );
68279 pWal->readLock = (i16)mxI;
68280 }
68281 }
68282 return rc;
68283 }
68284
68285 #ifdef SQLITE_ENABLE_SNAPSHOT
@@ -91889,11 +91897,11 @@
91897 **
91898 ** sqlite3_column_int()
91899 ** sqlite3_column_int64()
91900 ** sqlite3_column_text()
91901 ** sqlite3_column_text16()
91902 ** sqlite3_column_double()
91903 ** sqlite3_column_bytes()
91904 ** sqlite3_column_bytes16()
91905 ** sqlite3_column_blob()
91906 */
91907 static void columnMallocFailure(sqlite3_stmt *pStmt)
@@ -109896,11 +109904,11 @@
109904 p4 = sqlite3BinaryCompareCollSeq(pParse, pLeft, pRight);
109905 }
109906 p5 = binaryCompareP5(pLeft, pRight, jumpIfNull);
109907 addr = sqlite3VdbeAddOp4(pParse->pVdbe, opcode, in2, dest, in1,
109908 (void*)p4, P4_COLLSEQ);
109909 sqlite3VdbeChangeP5(pParse->pVdbe, (u16)p5);
109910 return addr;
109911 }
109912
109913 /*
109914 ** Return true if expression pExpr is a vector, or false otherwise.
@@ -129732,11 +129740,11 @@
129740 || (argc==3 && sqlite3_value_type(argv[2])==SQLITE_NULL)
129741 ){
129742 return;
129743 }
129744 p0type = sqlite3_value_type(argv[0]);
129745 p1 = sqlite3_value_int64(argv[1]);
129746 if( p0type==SQLITE_BLOB ){
129747 len = sqlite3_value_bytes(argv[0]);
129748 z = sqlite3_value_blob(argv[0]);
129749 if( z==0 ) return;
129750 assert( len==sqlite3_value_bytes(argv[0]) );
@@ -129757,11 +129765,11 @@
129765 ** from 2009-02-02 for compatibility of applications that exploited the
129766 ** old buggy behavior. */
129767 if( p1==0 ) p1 = 1; /* <rdar://problem/6778339> */
129768 #endif
129769 if( argc==3 ){
129770 p2 = sqlite3_value_int64(argv[2]);
129771 if( p2<0 ){
129772 p2 = -p2;
129773 negP2 = 1;
129774 }
129775 }else{
@@ -129796,13 +129804,15 @@
129804 SQLITE_SKIP_UTF8(z2);
129805 }
129806 sqlite3_result_text64(context, (char*)z, z2-z, SQLITE_TRANSIENT,
129807 SQLITE_UTF8);
129808 }else{
129809 if( p1>=len ){
129810 p1 = p2 = 0;
129811 }else if( p2>len-p1 ){
129812 p2 = len-p1;
129813 assert( p2>0 );
129814 }
129815 sqlite3_result_blob64(context, (char*)&z[p1], (u64)p2, SQLITE_TRANSIENT);
129816 }
129817 }
129818
@@ -129809,17 +129819,17 @@
129819 /*
129820 ** Implementation of the round() function
129821 */
129822 #ifndef SQLITE_OMIT_FLOATING_POINT
129823 static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
129824 i64 n = 0;
129825 double r;
129826 char *zBuf;
129827 assert( argc==1 || argc==2 );
129828 if( argc==2 ){
129829 if( SQLITE_NULL==sqlite3_value_type(argv[1]) ) return;
129830 n = sqlite3_value_int64(argv[1]);
129831 if( n>30 ) n = 30;
129832 if( n<0 ) n = 0;
129833 }
129834 if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
129835 r = sqlite3_value_double(argv[0]);
@@ -141316,11 +141326,11 @@
141326 sqlite3VdbeAddOp3(v, OP_Null, 0, 8, 8+cnt);
141327 sqlite3ClearTempRegCache(pParse);
141328
141329 /* Do the b-tree integrity checks */
141330 sqlite3VdbeAddOp4(v, OP_IntegrityCk, 1, cnt, 8, (char*)aRoot,P4_INTARRAY);
141331 sqlite3VdbeChangeP5(v, (u16)i);
141332 addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v);
141333 sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
141334 sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zDbSName),
141335 P4_DYNAMIC);
141336 sqlite3VdbeAddOp3(v, OP_Concat, 2, 3, 3);
@@ -150549,11 +150559,11 @@
150559 }
150560 sqlite3ReleaseTempReg(pParse, regSubtype);
150561 }
150562 sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
150563 sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
150564 sqlite3VdbeChangeP5(v, (u16)nArg);
150565 sqlite3VdbeAddOp2(v, OP_Next, pF->iOBTab, iTop+1); VdbeCoverage(v);
150566 sqlite3VdbeJumpHere(v, iTop);
150567 sqlite3ReleaseTempRange(pParse, regAgg, nArg);
150568 }
150569 sqlite3VdbeAddOp2(v, OP_AggFinal, AggInfoFuncReg(pAggInfo,i),
@@ -150712,11 +150722,11 @@
150722 sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0,
150723 (char *)pColl, P4_COLLSEQ);
150724 }
150725 sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
150726 sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
150727 sqlite3VdbeChangeP5(v, (u16)nArg);
150728 sqlite3ReleaseTempRange(pParse, regAgg, nArg);
150729 }
150730 if( addrNext ){
150731 sqlite3VdbeResolveLabel(v, addrNext);
150732 }
@@ -154106,11 +154116,11 @@
154116 /* Set the P5 operand of the OP_Program instruction to non-zero if
154117 ** recursive invocation of this trigger program is disallowed. Recursive
154118 ** invocation is disallowed if (a) the sub-program is really a trigger,
154119 ** not a foreign key action, and (b) the flag to enable recursive triggers
154120 ** is clear. */
154121 sqlite3VdbeChangeP5(v, (u16)bRecursive);
154122 }
154123 }
154124
154125 /*
154126 ** This is called to code the required FOR EACH ROW triggers for an operation
@@ -170414,10 +170424,11 @@
170424 int pc,
170425 VdbeOp *pOp
170426 ){
170427 if( (db->flags & SQLITE_VdbeAddopTrace)==0 ) return;
170428 sqlite3VdbePrintOp(0, pc, pOp);
170429 sqlite3ShowWhereTerm(0); /* So compiler won't complain about unused func */
170430 }
170431 #endif
170432
170433 /*
170434 ** Generate the end of the WHERE loop. See comments on
@@ -172523,11 +172534,11 @@
172534 sqlite3VdbeAddOp4(v, OP_CollSeq, 0,0,0, (const char*)pColl, P4_COLLSEQ);
172535 }
172536 sqlite3VdbeAddOp3(v, bInverse? OP_AggInverse : OP_AggStep,
172537 bInverse, regArg, pWin->regAccum);
172538 sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF);
172539 sqlite3VdbeChangeP5(v, (u16)nArg);
172540 if( pWin->bExprArgs ){
172541 sqlite3ReleaseTempRange(pParse, regArg, nArg);
172542 }
172543 }
172544
@@ -184078,12 +184089,12 @@
184089 # error SQLITE_MAX_COMPOUND_SELECT must be at least 2
184090 #endif
184091 #if SQLITE_MAX_VDBE_OP<40
184092 # error SQLITE_MAX_VDBE_OP must be at least 40
184093 #endif
184094 #if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>32767
184095 # error SQLITE_MAX_FUNCTION_ARG must be between 0 and 32767
184096 #endif
184097 #if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>125
184098 # error SQLITE_MAX_ATTACHED must be between 0 and 125
184099 #endif
184100 #if SQLITE_MAX_LIKE_PATTERN_LENGTH<1
@@ -185492,11 +185503,10 @@
185503 #if defined(SQLITE_DEBUG)
185504 /* Invoke these debugging routines so that the compiler does not
185505 ** issue "defined but not used" warnings. */
185506 if( x==9999 ){
185507 sqlite3ShowExpr(0);
 
185508 sqlite3ShowExprList(0);
185509 sqlite3ShowIdList(0);
185510 sqlite3ShowSrcList(0);
185511 sqlite3ShowWith(0);
185512 sqlite3ShowUpsert(0);
@@ -185509,11 +185519,10 @@
185519 #ifndef SQLITE_OMIT_WINDOWFUNC
185520 sqlite3ShowWindow(0);
185521 sqlite3ShowWinFunc(0);
185522 #endif
185523 sqlite3ShowSelect(0);
 
185524 }
185525 #endif
185526 break;
185527 }
185528
@@ -226344,11 +226353,15 @@
226353 static int dbpageSync(sqlite3_vtab *pVtab){
226354 DbpageTable *pTab = (DbpageTable *)pVtab;
226355 if( pTab->pgnoTrunc>0 ){
226356 Btree *pBt = pTab->db->aDb[pTab->iDbTrunc].pBt;
226357 Pager *pPager = sqlite3BtreePager(pBt);
226358 sqlite3BtreeEnter(pBt);
226359 if( pTab->pgnoTrunc<sqlite3BtreeLastPage(pBt) ){
226360 sqlite3PagerTruncateImage(pPager, pTab->pgnoTrunc);
226361 }
226362 sqlite3BtreeLeave(pBt);
226363 }
226364 pTab->pgnoTrunc = 0;
226365 return SQLITE_OK;
226366 }
226367
@@ -255419,11 +255432,11 @@
255432 int nArg, /* Number of args */
255433 sqlite3_value **apUnused /* Function arguments */
255434 ){
255435 assert( nArg==0 );
255436 UNUSED_PARAM2(nArg, apUnused);
255437 sqlite3_result_text(pCtx, "fts5: 2024-12-19 14:20:47 5b96dcf5f6bf41dcb89ced64efd4585e36dce718c428c2324d94e4942905c3bb", -1, SQLITE_TRANSIENT);
255438 }
255439
255440 /*
255441 ** Implementation of fts5_locale(LOCALE, TEXT) function.
255442 **
255443
--- extsrc/sqlite3.h
+++ extsrc/sqlite3.h
@@ -146,11 +146,11 @@
146146
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147147
** [sqlite_version()] and [sqlite_source_id()].
148148
*/
149149
#define SQLITE_VERSION "3.48.0"
150150
#define SQLITE_VERSION_NUMBER 3048000
151
-#define SQLITE_SOURCE_ID "2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653"
151
+#define SQLITE_SOURCE_ID "2024-12-19 19:02:09 e6c30ee52c5cdc193804cec63374d558b45e4d67fc6bde58771ca78485ca0acf"
152152
153153
/*
154154
** CAPI3REF: Run-Time Library Version Numbers
155155
** KEYWORDS: sqlite3_version sqlite3_sourceid
156156
**
157157
--- extsrc/sqlite3.h
+++ extsrc/sqlite3.h
@@ -146,11 +146,11 @@
146 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147 ** [sqlite_version()] and [sqlite_source_id()].
148 */
149 #define SQLITE_VERSION "3.48.0"
150 #define SQLITE_VERSION_NUMBER 3048000
151 #define SQLITE_SOURCE_ID "2024-12-09 20:46:36 e2bae4143afd07de1ae55a6d2606a3b541a5b94568aa41f6a96e5d1245471653"
152
153 /*
154 ** CAPI3REF: Run-Time Library Version Numbers
155 ** KEYWORDS: sqlite3_version sqlite3_sourceid
156 **
157
--- extsrc/sqlite3.h
+++ extsrc/sqlite3.h
@@ -146,11 +146,11 @@
146 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
147 ** [sqlite_version()] and [sqlite_source_id()].
148 */
149 #define SQLITE_VERSION "3.48.0"
150 #define SQLITE_VERSION_NUMBER 3048000
151 #define SQLITE_SOURCE_ID "2024-12-19 19:02:09 e6c30ee52c5cdc193804cec63374d558b45e4d67fc6bde58771ca78485ca0acf"
152
153 /*
154 ** CAPI3REF: Run-Time Library Version Numbers
155 ** KEYWORDS: sqlite3_version sqlite3_sourceid
156 **
157
+26 -23
--- src/bisect.c
+++ src/bisect.c
@@ -197,10 +197,26 @@
197197
static void bisect_append_skip(int rid){
198198
db_multi_exec(
199199
"UPDATE vvar SET value=value||' s%d' WHERE name='bisect-log'", rid
200200
);
201201
}
202
+
203
+/*
204
+** Append a VALUES entry to the bilog table insert
205
+*/
206
+static void bisect_log_append(Blob *pSql,int iSeq,const char *zStat,int iRid){
207
+ if( (iSeq%6)==3 ){
208
+ blob_append_sql(pSql, ",\n ");
209
+ }else if( iSeq>1 ){
210
+ blob_append_sql(pSql, ",");
211
+ }
212
+ if( zStat ){
213
+ blob_append_sql(pSql, "(%d,%Q,%d)", iSeq, zStat, iRid);
214
+ }else{
215
+ blob_append_sql(pSql, "(NULL,NULL,%d)", iRid);
216
+ }
217
+}
202218
203219
/*
204220
** Create a TEMP table named "bilog" that contains the complete history
205221
** of the current bisect.
206222
**
@@ -215,14 +231,14 @@
215231
** in between the inner-most GOOD and BAD nodes.
216232
*/
217233
int bisect_create_bilog_table(int iCurrent, const char *zDesc, int bDetail){
218234
char *zLog;
219235
Blob log, id;
220
- Stmt q;
221236
int cnt = 0;
222237
int lastGood = -1;
223238
int lastBad = -1;
239
+ Blob ins = BLOB_INITIALIZER;
224240
225241
if( zDesc!=0 ){
226242
blob_init(&log, 0, 0);
227243
while( zDesc[0]=='y' || zDesc[0]=='n' || zDesc[0]=='s' ){
228244
int i;
@@ -253,55 +269,42 @@
253269
" rid INTEGER PRIMARY KEY," /* Sequence of events */
254270
" stat TEXT," /* Type of occurrence */
255271
" seq INTEGER UNIQUE" /* Check-in number */
256272
");"
257273
);
258
- db_prepare(&q, "INSERT OR IGNORE INTO bilog(seq,stat,rid)"
259
- " VALUES(:seq,:stat,:rid)");
274
+ blob_append_sql(&ins, "INSERT OR IGNORE INTO bilog(seq,stat,rid) VALUES");
260275
while( blob_token(&log, &id) ){
261276
int rid;
262
- db_bind_int(&q, ":seq", ++cnt);
277
+ cnt++;
263278
if( blob_str(&id)[0]=='s' ){
264279
rid = atoi(blob_str(&id)+1);
265
- db_bind_text(&q, ":stat", "SKIP");
266
- db_bind_int(&q, ":rid", rid);
280
+ bisect_log_append(&ins, cnt, "SKIP", rid);
267281
}else{
268282
rid = atoi(blob_str(&id));
269283
if( rid>0 ){
270
- db_bind_text(&q, ":stat","GOOD");
271
- db_bind_int(&q, ":rid", rid);
284
+ bisect_log_append(&ins, cnt, "GOOD", rid);
272285
lastGood = rid;
273286
}else{
274
- db_bind_text(&q, ":stat", "BAD");
275
- db_bind_int(&q, ":rid", -rid);
287
+ bisect_log_append(&ins, cnt, "BAD", rid);
276288
lastBad = -rid;
277289
}
278290
}
279
- db_step(&q);
280
- db_reset(&q);
281291
}
282292
if( iCurrent>0 ){
283
- db_bind_int(&q, ":seq", ++cnt);
284
- db_bind_text(&q, ":stat", "CURRENT");
285
- db_bind_int(&q, ":rid", iCurrent);
286
- db_step(&q);
287
- db_reset(&q);
293
+ bisect_log_append(&ins, ++cnt, "CURRENT", iCurrent);
288294
}
289295
if( bDetail && lastGood>0 && lastBad>0 ){
290296
PathNode *p;
291297
p = path_shortest(lastGood, lastBad, bisect_option("direct-only"),0, 0);
292298
while( p ){
293
- db_bind_null(&q, ":seq");
294
- db_bind_null(&q, ":stat");
295
- db_bind_int(&q, ":rid", p->rid);
296
- db_step(&q);
297
- db_reset(&q);
299
+ bisect_log_append(&ins, ++cnt, 0, p->rid);
298300
p = p->u.pTo;
299301
}
300302
path_reset();
301303
}
302
- db_finalize(&q);
304
+ db_exec_sql(blob_sql_text(&ins));
305
+ blob_reset(&ins);
303306
return 1;
304307
}
305308
306309
/* Return a permalink description of a bisect. Space is obtained from
307310
** fossil_malloc() and should be freed by the caller.
308311
--- src/bisect.c
+++ src/bisect.c
@@ -197,10 +197,26 @@
197 static void bisect_append_skip(int rid){
198 db_multi_exec(
199 "UPDATE vvar SET value=value||' s%d' WHERE name='bisect-log'", rid
200 );
201 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
203 /*
204 ** Create a TEMP table named "bilog" that contains the complete history
205 ** of the current bisect.
206 **
@@ -215,14 +231,14 @@
215 ** in between the inner-most GOOD and BAD nodes.
216 */
217 int bisect_create_bilog_table(int iCurrent, const char *zDesc, int bDetail){
218 char *zLog;
219 Blob log, id;
220 Stmt q;
221 int cnt = 0;
222 int lastGood = -1;
223 int lastBad = -1;
 
224
225 if( zDesc!=0 ){
226 blob_init(&log, 0, 0);
227 while( zDesc[0]=='y' || zDesc[0]=='n' || zDesc[0]=='s' ){
228 int i;
@@ -253,55 +269,42 @@
253 " rid INTEGER PRIMARY KEY," /* Sequence of events */
254 " stat TEXT," /* Type of occurrence */
255 " seq INTEGER UNIQUE" /* Check-in number */
256 ");"
257 );
258 db_prepare(&q, "INSERT OR IGNORE INTO bilog(seq,stat,rid)"
259 " VALUES(:seq,:stat,:rid)");
260 while( blob_token(&log, &id) ){
261 int rid;
262 db_bind_int(&q, ":seq", ++cnt);
263 if( blob_str(&id)[0]=='s' ){
264 rid = atoi(blob_str(&id)+1);
265 db_bind_text(&q, ":stat", "SKIP");
266 db_bind_int(&q, ":rid", rid);
267 }else{
268 rid = atoi(blob_str(&id));
269 if( rid>0 ){
270 db_bind_text(&q, ":stat","GOOD");
271 db_bind_int(&q, ":rid", rid);
272 lastGood = rid;
273 }else{
274 db_bind_text(&q, ":stat", "BAD");
275 db_bind_int(&q, ":rid", -rid);
276 lastBad = -rid;
277 }
278 }
279 db_step(&q);
280 db_reset(&q);
281 }
282 if( iCurrent>0 ){
283 db_bind_int(&q, ":seq", ++cnt);
284 db_bind_text(&q, ":stat", "CURRENT");
285 db_bind_int(&q, ":rid", iCurrent);
286 db_step(&q);
287 db_reset(&q);
288 }
289 if( bDetail && lastGood>0 && lastBad>0 ){
290 PathNode *p;
291 p = path_shortest(lastGood, lastBad, bisect_option("direct-only"),0, 0);
292 while( p ){
293 db_bind_null(&q, ":seq");
294 db_bind_null(&q, ":stat");
295 db_bind_int(&q, ":rid", p->rid);
296 db_step(&q);
297 db_reset(&q);
298 p = p->u.pTo;
299 }
300 path_reset();
301 }
302 db_finalize(&q);
 
303 return 1;
304 }
305
306 /* Return a permalink description of a bisect. Space is obtained from
307 ** fossil_malloc() and should be freed by the caller.
308
--- src/bisect.c
+++ src/bisect.c
@@ -197,10 +197,26 @@
197 static void bisect_append_skip(int rid){
198 db_multi_exec(
199 "UPDATE vvar SET value=value||' s%d' WHERE name='bisect-log'", rid
200 );
201 }
202
203 /*
204 ** Append a VALUES entry to the bilog table insert
205 */
206 static void bisect_log_append(Blob *pSql,int iSeq,const char *zStat,int iRid){
207 if( (iSeq%6)==3 ){
208 blob_append_sql(pSql, ",\n ");
209 }else if( iSeq>1 ){
210 blob_append_sql(pSql, ",");
211 }
212 if( zStat ){
213 blob_append_sql(pSql, "(%d,%Q,%d)", iSeq, zStat, iRid);
214 }else{
215 blob_append_sql(pSql, "(NULL,NULL,%d)", iRid);
216 }
217 }
218
219 /*
220 ** Create a TEMP table named "bilog" that contains the complete history
221 ** of the current bisect.
222 **
@@ -215,14 +231,14 @@
231 ** in between the inner-most GOOD and BAD nodes.
232 */
233 int bisect_create_bilog_table(int iCurrent, const char *zDesc, int bDetail){
234 char *zLog;
235 Blob log, id;
 
236 int cnt = 0;
237 int lastGood = -1;
238 int lastBad = -1;
239 Blob ins = BLOB_INITIALIZER;
240
241 if( zDesc!=0 ){
242 blob_init(&log, 0, 0);
243 while( zDesc[0]=='y' || zDesc[0]=='n' || zDesc[0]=='s' ){
244 int i;
@@ -253,55 +269,42 @@
269 " rid INTEGER PRIMARY KEY," /* Sequence of events */
270 " stat TEXT," /* Type of occurrence */
271 " seq INTEGER UNIQUE" /* Check-in number */
272 ");"
273 );
274 blob_append_sql(&ins, "INSERT OR IGNORE INTO bilog(seq,stat,rid) VALUES");
 
275 while( blob_token(&log, &id) ){
276 int rid;
277 cnt++;
278 if( blob_str(&id)[0]=='s' ){
279 rid = atoi(blob_str(&id)+1);
280 bisect_log_append(&ins, cnt, "SKIP", rid);
 
281 }else{
282 rid = atoi(blob_str(&id));
283 if( rid>0 ){
284 bisect_log_append(&ins, cnt, "GOOD", rid);
 
285 lastGood = rid;
286 }else{
287 bisect_log_append(&ins, cnt, "BAD", rid);
 
288 lastBad = -rid;
289 }
290 }
 
 
291 }
292 if( iCurrent>0 ){
293 bisect_log_append(&ins, ++cnt, "CURRENT", iCurrent);
 
 
 
 
294 }
295 if( bDetail && lastGood>0 && lastBad>0 ){
296 PathNode *p;
297 p = path_shortest(lastGood, lastBad, bisect_option("direct-only"),0, 0);
298 while( p ){
299 bisect_log_append(&ins, ++cnt, 0, p->rid);
 
 
 
 
300 p = p->u.pTo;
301 }
302 path_reset();
303 }
304 db_exec_sql(blob_sql_text(&ins));
305 blob_reset(&ins);
306 return 1;
307 }
308
309 /* Return a permalink description of a bisect. Space is obtained from
310 ** fossil_malloc() and should be freed by the caller.
311
+3 -4
--- src/checkout.c
+++ src/checkout.c
@@ -171,23 +171,22 @@
171171
** each character as a flag to enable writing "manifest", "manifest.uuid" or
172172
** "manifest.tags".
173173
*/
174174
void manifest_to_disk(int vid){
175175
char *zManFile;
176
- Blob manifest;
177
- Blob taglist;
178176
int flg;
179177
180178
flg = db_get_manifest_setting();
181179
182180
if( flg & MFESTFLG_RAW ){
183
- blob_zero(&manifest);
181
+ Blob manifest = BLOB_INITIALIZER;
184182
content_get(vid, &manifest);
185183
sterilize_manifest(&manifest, CFTYPE_MANIFEST);
186184
zManFile = mprintf("%smanifest", g.zLocalRoot);
187185
blob_write_to_file(&manifest, zManFile);
188186
free(zManFile);
187
+ blob_reset(&manifest);
189188
}else{
190189
if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest'") ){
191190
zManFile = mprintf("%smanifest", g.zLocalRoot);
192191
file_delete(zManFile);
193192
free(zManFile);
@@ -207,11 +206,11 @@
207206
file_delete(zManFile);
208207
free(zManFile);
209208
}
210209
}
211210
if( flg & MFESTFLG_TAGS ){
212
- blob_zero(&taglist);
211
+ Blob taglist = BLOB_INITIALIZER;
213212
zManFile = mprintf("%smanifest.tags", g.zLocalRoot);
214213
get_checkin_taglist(vid, &taglist);
215214
blob_write_to_file(&taglist, zManFile);
216215
free(zManFile);
217216
blob_reset(&taglist);
218217
--- src/checkout.c
+++ src/checkout.c
@@ -171,23 +171,22 @@
171 ** each character as a flag to enable writing "manifest", "manifest.uuid" or
172 ** "manifest.tags".
173 */
174 void manifest_to_disk(int vid){
175 char *zManFile;
176 Blob manifest;
177 Blob taglist;
178 int flg;
179
180 flg = db_get_manifest_setting();
181
182 if( flg & MFESTFLG_RAW ){
183 blob_zero(&manifest);
184 content_get(vid, &manifest);
185 sterilize_manifest(&manifest, CFTYPE_MANIFEST);
186 zManFile = mprintf("%smanifest", g.zLocalRoot);
187 blob_write_to_file(&manifest, zManFile);
188 free(zManFile);
 
189 }else{
190 if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest'") ){
191 zManFile = mprintf("%smanifest", g.zLocalRoot);
192 file_delete(zManFile);
193 free(zManFile);
@@ -207,11 +206,11 @@
207 file_delete(zManFile);
208 free(zManFile);
209 }
210 }
211 if( flg & MFESTFLG_TAGS ){
212 blob_zero(&taglist);
213 zManFile = mprintf("%smanifest.tags", g.zLocalRoot);
214 get_checkin_taglist(vid, &taglist);
215 blob_write_to_file(&taglist, zManFile);
216 free(zManFile);
217 blob_reset(&taglist);
218
--- src/checkout.c
+++ src/checkout.c
@@ -171,23 +171,22 @@
171 ** each character as a flag to enable writing "manifest", "manifest.uuid" or
172 ** "manifest.tags".
173 */
174 void manifest_to_disk(int vid){
175 char *zManFile;
 
 
176 int flg;
177
178 flg = db_get_manifest_setting();
179
180 if( flg & MFESTFLG_RAW ){
181 Blob manifest = BLOB_INITIALIZER;
182 content_get(vid, &manifest);
183 sterilize_manifest(&manifest, CFTYPE_MANIFEST);
184 zManFile = mprintf("%smanifest", g.zLocalRoot);
185 blob_write_to_file(&manifest, zManFile);
186 free(zManFile);
187 blob_reset(&manifest);
188 }else{
189 if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest'") ){
190 zManFile = mprintf("%smanifest", g.zLocalRoot);
191 file_delete(zManFile);
192 free(zManFile);
@@ -207,11 +206,11 @@
206 file_delete(zManFile);
207 free(zManFile);
208 }
209 }
210 if( flg & MFESTFLG_TAGS ){
211 Blob taglist = BLOB_INITIALIZER;
212 zManFile = mprintf("%smanifest.tags", g.zLocalRoot);
213 get_checkin_taglist(vid, &taglist);
214 blob_write_to_file(&taglist, zManFile);
215 free(zManFile);
216 blob_reset(&taglist);
217
+43 -1
--- src/db.c
+++ src/db.c
@@ -170,10 +170,12 @@
170170
void *pAuthArg; /* Argument to the authorizer */
171171
const char *zAuthName; /* Name of the authorizer */
172172
int bProtectTriggers; /* True if protection triggers already exist */
173173
int nProtect; /* Slots of aProtect used */
174174
unsigned aProtect[12]; /* Saved values of protectMask */
175
+ int pauseDmlLog; /* Ignore pDmlLog if positive */
176
+ Blob *pDmlLog; /* Append DML statements here, of not NULL */
175177
} db = {
176178
PROTECT_USER|PROTECT_CONFIG|PROTECT_BASELINE, /* protectMask */
177179
0, 0, 0, 0, 0, 0, 0, {{0}}, {0}, {0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0}};
178180
179181
/*
@@ -643,10 +645,43 @@
643645
*/
644646
#define DB_PREPARE_IGNORE_ERROR 0x001 /* Suppress errors */
645647
#define DB_PREPARE_PERSISTENT 0x002 /* Stmt will stick around for a while */
646648
#endif
647649
650
+/*
651
+** If zSql is a DML statement, append it db.pDmlLog.
652
+*/
653
+static void db_append_dml(const char *zSql){
654
+ size_t nSql;
655
+ if( db.pDmlLog==0 ) return;
656
+ if( db.pauseDmlLog ) return;
657
+ if( zSql==0 ) return;
658
+ nSql = strlen(zSql);
659
+ while( nSql>0 && fossil_isspace(zSql[0]) ){ nSql--; zSql++; }
660
+ while( nSql>0 && fossil_isspace(zSql[nSql-1]) ) nSql--;
661
+ if( nSql<6 ) return;
662
+ if( fossil_strnicmp(zSql, "SELECT", 6)==0 ) return;
663
+ if( fossil_strnicmp(zSql, "PRAGMA", 6)==0 ) return;
664
+ blob_append(db.pDmlLog, zSql, nSql);
665
+ if( zSql[nSql-1]!=';' ) blob_append_char(db.pDmlLog, ';');
666
+ blob_append_char(db.pDmlLog, '\n');
667
+}
668
+
669
+/*
670
+** Set the Blob to which DML statement text should be appended. Set it
671
+** to zero to stop appending DML statement text.
672
+*/
673
+void db_append_dml_to_blob(Blob *pBlob){
674
+ db.pDmlLog = pBlob;
675
+}
676
+
677
+/*
678
+** Pause or unpause the DML log
679
+*/
680
+void db_pause_dml_log(void){ db.pauseDmlLog++; }
681
+void db_unpause_dml_log(void){ db.pauseDmlLog--; }
682
+
648683
/*
649684
** Prepare a Stmt. Assume that the Stmt is previously uninitialized.
650685
** If the input string contains multiple SQL statements, only the first
651686
** one is processed. All statements beyond the first are silently ignored.
652687
*/
@@ -658,10 +693,11 @@
658693
blob_zero(&pStmt->sql);
659694
blob_vappendf(&pStmt->sql, zFormat, ap);
660695
va_end(ap);
661696
zSql = blob_str(&pStmt->sql);
662697
db.nPrepare++;
698
+ db_append_dml(zSql);
663699
if( flags & DB_PREPARE_PERSISTENT ){
664700
prepFlags = SQLITE_PREPARE_PERSISTENT;
665701
}
666702
rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, &zExtra);
667703
if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)==0 ){
@@ -1047,10 +1083,11 @@
10471083
rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd);
10481084
if( rc ){
10491085
db_err("%s: {%s}", sqlite3_errmsg(g.db), z);
10501086
}else if( pStmt ){
10511087
db.nPrepare++;
1088
+ db_append_dml(sqlite3_sql(pStmt));
10521089
while( sqlite3_step(pStmt)==SQLITE_ROW ){}
10531090
rc = sqlite3_finalize(pStmt);
10541091
if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z);
10551092
}
10561093
z = zEnd;
@@ -2105,11 +2142,11 @@
21052142
sqlite3 *, char **, const sqlite3_api_routines *
21062143
);
21072144
sqlite3_appendvfs_init(0,0,0);
21082145
g.zVfsName = "apndvfs";
21092146
}
2110
- blob_zero(&bNameCheck);
2147
+ blob_reset(&bNameCheck);
21112148
rc = sqlite3_open_v2(
21122149
zDbName, &db,
21132150
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
21142151
g.zVfsName
21152152
);
@@ -2625,13 +2662,18 @@
26252662
return res;
26262663
}
26272664
26282665
/*
26292666
** COMMAND: test-is-repo
2667
+** Usage: %fossil test-is-repo FILENAME...
2668
+**
2669
+** Test whether the specified files look like a SQLite database
2670
+** containing a Fossil repository schema.
26302671
*/
26312672
void test_is_repo(void){
26322673
int i;
2674
+ verify_all_options();
26332675
for(i=2; i<g.argc; i++){
26342676
fossil_print("%s: %s\n",
26352677
db_looks_like_a_repository(g.argv[i]) ? "yes" : " no",
26362678
g.argv[i]
26372679
);
26382680
--- src/db.c
+++ src/db.c
@@ -170,10 +170,12 @@
170 void *pAuthArg; /* Argument to the authorizer */
171 const char *zAuthName; /* Name of the authorizer */
172 int bProtectTriggers; /* True if protection triggers already exist */
173 int nProtect; /* Slots of aProtect used */
174 unsigned aProtect[12]; /* Saved values of protectMask */
 
 
175 } db = {
176 PROTECT_USER|PROTECT_CONFIG|PROTECT_BASELINE, /* protectMask */
177 0, 0, 0, 0, 0, 0, 0, {{0}}, {0}, {0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0}};
178
179 /*
@@ -643,10 +645,43 @@
643 */
644 #define DB_PREPARE_IGNORE_ERROR 0x001 /* Suppress errors */
645 #define DB_PREPARE_PERSISTENT 0x002 /* Stmt will stick around for a while */
646 #endif
647
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
648 /*
649 ** Prepare a Stmt. Assume that the Stmt is previously uninitialized.
650 ** If the input string contains multiple SQL statements, only the first
651 ** one is processed. All statements beyond the first are silently ignored.
652 */
@@ -658,10 +693,11 @@
658 blob_zero(&pStmt->sql);
659 blob_vappendf(&pStmt->sql, zFormat, ap);
660 va_end(ap);
661 zSql = blob_str(&pStmt->sql);
662 db.nPrepare++;
 
663 if( flags & DB_PREPARE_PERSISTENT ){
664 prepFlags = SQLITE_PREPARE_PERSISTENT;
665 }
666 rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, &zExtra);
667 if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)==0 ){
@@ -1047,10 +1083,11 @@
1047 rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd);
1048 if( rc ){
1049 db_err("%s: {%s}", sqlite3_errmsg(g.db), z);
1050 }else if( pStmt ){
1051 db.nPrepare++;
 
1052 while( sqlite3_step(pStmt)==SQLITE_ROW ){}
1053 rc = sqlite3_finalize(pStmt);
1054 if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z);
1055 }
1056 z = zEnd;
@@ -2105,11 +2142,11 @@
2105 sqlite3 *, char **, const sqlite3_api_routines *
2106 );
2107 sqlite3_appendvfs_init(0,0,0);
2108 g.zVfsName = "apndvfs";
2109 }
2110 blob_zero(&bNameCheck);
2111 rc = sqlite3_open_v2(
2112 zDbName, &db,
2113 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
2114 g.zVfsName
2115 );
@@ -2625,13 +2662,18 @@
2625 return res;
2626 }
2627
2628 /*
2629 ** COMMAND: test-is-repo
 
 
 
 
2630 */
2631 void test_is_repo(void){
2632 int i;
 
2633 for(i=2; i<g.argc; i++){
2634 fossil_print("%s: %s\n",
2635 db_looks_like_a_repository(g.argv[i]) ? "yes" : " no",
2636 g.argv[i]
2637 );
2638
--- src/db.c
+++ src/db.c
@@ -170,10 +170,12 @@
170 void *pAuthArg; /* Argument to the authorizer */
171 const char *zAuthName; /* Name of the authorizer */
172 int bProtectTriggers; /* True if protection triggers already exist */
173 int nProtect; /* Slots of aProtect used */
174 unsigned aProtect[12]; /* Saved values of protectMask */
175 int pauseDmlLog; /* Ignore pDmlLog if positive */
176 Blob *pDmlLog; /* Append DML statements here, of not NULL */
177 } db = {
178 PROTECT_USER|PROTECT_CONFIG|PROTECT_BASELINE, /* protectMask */
179 0, 0, 0, 0, 0, 0, 0, {{0}}, {0}, {0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0}};
180
181 /*
@@ -643,10 +645,43 @@
645 */
646 #define DB_PREPARE_IGNORE_ERROR 0x001 /* Suppress errors */
647 #define DB_PREPARE_PERSISTENT 0x002 /* Stmt will stick around for a while */
648 #endif
649
650 /*
651 ** If zSql is a DML statement, append it db.pDmlLog.
652 */
653 static void db_append_dml(const char *zSql){
654 size_t nSql;
655 if( db.pDmlLog==0 ) return;
656 if( db.pauseDmlLog ) return;
657 if( zSql==0 ) return;
658 nSql = strlen(zSql);
659 while( nSql>0 && fossil_isspace(zSql[0]) ){ nSql--; zSql++; }
660 while( nSql>0 && fossil_isspace(zSql[nSql-1]) ) nSql--;
661 if( nSql<6 ) return;
662 if( fossil_strnicmp(zSql, "SELECT", 6)==0 ) return;
663 if( fossil_strnicmp(zSql, "PRAGMA", 6)==0 ) return;
664 blob_append(db.pDmlLog, zSql, nSql);
665 if( zSql[nSql-1]!=';' ) blob_append_char(db.pDmlLog, ';');
666 blob_append_char(db.pDmlLog, '\n');
667 }
668
669 /*
670 ** Set the Blob to which DML statement text should be appended. Set it
671 ** to zero to stop appending DML statement text.
672 */
673 void db_append_dml_to_blob(Blob *pBlob){
674 db.pDmlLog = pBlob;
675 }
676
677 /*
678 ** Pause or unpause the DML log
679 */
680 void db_pause_dml_log(void){ db.pauseDmlLog++; }
681 void db_unpause_dml_log(void){ db.pauseDmlLog--; }
682
683 /*
684 ** Prepare a Stmt. Assume that the Stmt is previously uninitialized.
685 ** If the input string contains multiple SQL statements, only the first
686 ** one is processed. All statements beyond the first are silently ignored.
687 */
@@ -658,10 +693,11 @@
693 blob_zero(&pStmt->sql);
694 blob_vappendf(&pStmt->sql, zFormat, ap);
695 va_end(ap);
696 zSql = blob_str(&pStmt->sql);
697 db.nPrepare++;
698 db_append_dml(zSql);
699 if( flags & DB_PREPARE_PERSISTENT ){
700 prepFlags = SQLITE_PREPARE_PERSISTENT;
701 }
702 rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, &zExtra);
703 if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)==0 ){
@@ -1047,10 +1083,11 @@
1083 rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd);
1084 if( rc ){
1085 db_err("%s: {%s}", sqlite3_errmsg(g.db), z);
1086 }else if( pStmt ){
1087 db.nPrepare++;
1088 db_append_dml(sqlite3_sql(pStmt));
1089 while( sqlite3_step(pStmt)==SQLITE_ROW ){}
1090 rc = sqlite3_finalize(pStmt);
1091 if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z);
1092 }
1093 z = zEnd;
@@ -2105,11 +2142,11 @@
2142 sqlite3 *, char **, const sqlite3_api_routines *
2143 );
2144 sqlite3_appendvfs_init(0,0,0);
2145 g.zVfsName = "apndvfs";
2146 }
2147 blob_reset(&bNameCheck);
2148 rc = sqlite3_open_v2(
2149 zDbName, &db,
2150 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
2151 g.zVfsName
2152 );
@@ -2625,13 +2662,18 @@
2662 return res;
2663 }
2664
2665 /*
2666 ** COMMAND: test-is-repo
2667 ** Usage: %fossil test-is-repo FILENAME...
2668 **
2669 ** Test whether the specified files look like a SQLite database
2670 ** containing a Fossil repository schema.
2671 */
2672 void test_is_repo(void){
2673 int i;
2674 verify_all_options();
2675 for(i=2; i<g.argc; i++){
2676 fossil_print("%s: %s\n",
2677 db_looks_like_a_repository(g.argv[i]) ? "yes" : " no",
2678 g.argv[i]
2679 );
2680
+49 -39
--- src/descendants.c
+++ src/descendants.c
@@ -202,29 +202,28 @@
202202
rLimitMtime = db_double(0.0,
203203
"SELECT mtime FROM event WHERE objid=%d",
204204
ridBackTo);
205205
}
206206
db_multi_exec(
207
- "WITH RECURSIVE "
208
- " parent(pid,cid,isCP) AS ("
209
- " SELECT plink.pid, plink.cid, 0 AS xisCP FROM plink"
210
- " UNION ALL"
211
- " SELECT parentid, childid, 1 FROM cherrypick WHERE NOT isExclude"
212
- " ),"
213
- " ancestor(rid, mtime, isCP) AS ("
214
- " SELECT %d, mtime, 0 FROM event WHERE objid=%d "
215
- " UNION "
216
- " SELECT parent.pid, event.mtime, parent.isCP"
217
- " FROM ancestor, parent, event"
218
- " WHERE parent.cid=ancestor.rid"
219
- " AND event.objid=parent.pid"
220
- " AND NOT ancestor.isCP"
221
- " AND (event.mtime>=%.17g OR parent.pid=%d)"
222
- " ORDER BY mtime DESC LIMIT %d"
223
- " )"
224
- "INSERT OR IGNORE INTO ok"
225
- " SELECT rid FROM ancestor;",
207
+ "WITH RECURSIVE\n"
208
+ " parent(pid,cid,isCP) AS (\n"
209
+ " SELECT plink.pid, plink.cid, 0 AS xisCP FROM plink\n"
210
+ " UNION ALL\n"
211
+ " SELECT parentid, childid, 1 FROM cherrypick WHERE NOT isExclude\n"
212
+ " ),\n"
213
+ " ancestor(rid, mtime, isCP) AS (\n"
214
+ " SELECT %d, mtime, 0 FROM event WHERE objid=%d\n"
215
+ " UNION\n"
216
+ " SELECT parent.pid, event.mtime, parent.isCP\n"
217
+ " FROM ancestor, parent, event\n"
218
+ " WHERE parent.cid=ancestor.rid\n"
219
+ " AND event.objid=parent.pid\n"
220
+ " AND NOT ancestor.isCP\n"
221
+ " AND (event.mtime>=%.17g OR parent.pid=%d)\n"
222
+ " ORDER BY mtime DESC LIMIT %d\n"
223
+ " )\n"
224
+ "INSERT OR IGNORE INTO ok SELECT rid FROM ancestor;",
226225
rid, rid, rLimitMtime, ridBackTo, N
227226
);
228227
if( ridBackTo && db_changes()>1 ){
229228
db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", ridBackTo);
230229
}
@@ -322,18 +321,18 @@
322321
N = -1;
323322
}else if( N<0 ){
324323
N = -N;
325324
}
326325
db_multi_exec(
327
- "WITH RECURSIVE"
328
- " dx(rid,mtime) AS ("
329
- " SELECT %d, 0"
330
- " UNION"
331
- " SELECT plink.cid, plink.mtime FROM dx, plink"
332
- " WHERE plink.pid=dx.rid"
333
- " ORDER BY 2"
334
- " )"
326
+ "WITH RECURSIVE\n"
327
+ " dx(rid,mtime) AS (\n"
328
+ " SELECT %d, 0\n"
329
+ " UNION\n"
330
+ " SELECT plink.cid, plink.mtime FROM dx, plink\n"
331
+ " WHERE plink.pid=dx.rid\n"
332
+ " ORDER BY 2\n"
333
+ " )\n"
335334
"INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d",
336335
rid, N
337336
);
338337
}
339338
@@ -639,44 +638,56 @@
639638
/* Flag parameters to compute_uses_file() */
640639
#define USESFILE_DELETE 0x01 /* Include the check-ins where file deleted */
641640
642641
#endif
643642
643
+/*
644
+** Append a new VALUES term.
645
+*/
646
+static void uses_file_append_term(Blob *pSql, int *pnCnt, int rid){
647
+ if( *pnCnt==0 ){
648
+ blob_append_sql(pSql, "(%d)", rid);
649
+ *pnCnt = 4;
650
+ }else if( (*pnCnt)%10==9 ){
651
+ blob_append_sql(pSql, ",\n (%d)", rid);
652
+ }else{
653
+ blob_append_sql(pSql, ",(%d)", rid);
654
+ }
655
+ ++*pnCnt;
656
+}
657
+
644658
645659
/*
646660
** Add to table zTab the record ID (rid) of every check-in that contains
647661
** the file fid.
648662
*/
649663
void compute_uses_file(const char *zTab, int fid, int usesFlags){
650664
Bag seen;
651665
Bag pending;
652
- Stmt ins;
666
+ Blob ins = BLOB_INITIALIZER;
667
+ int nIns = 0;
653668
Stmt q;
654669
int rid;
655670
656671
bag_init(&seen);
657672
bag_init(&pending);
658
- db_prepare(&ins, "INSERT OR IGNORE INTO \"%w\" VALUES(:rid)", zTab);
673
+ blob_append_sql(&ins, "INSERT OR IGNORE INTO \"%w\" VALUES", zTab);
659674
db_prepare(&q, "SELECT mid FROM mlink WHERE fid=%d", fid);
660675
while( db_step(&q)==SQLITE_ROW ){
661676
int mid = db_column_int(&q, 0);
662677
bag_insert(&pending, mid);
663678
bag_insert(&seen, mid);
664
- db_bind_int(&ins, ":rid", mid);
665
- db_step(&ins);
666
- db_reset(&ins);
679
+ uses_file_append_term(&ins, &nIns, mid);
667680
}
668681
db_finalize(&q);
669682
670683
db_prepare(&q, "SELECT mid FROM mlink WHERE pid=%d", fid);
671684
while( db_step(&q)==SQLITE_ROW ){
672685
int mid = db_column_int(&q, 0);
673686
bag_insert(&seen, mid);
674687
if( usesFlags & USESFILE_DELETE ){
675
- db_bind_int(&ins, ":rid", mid);
676
- db_step(&ins);
677
- db_reset(&ins);
688
+ uses_file_append_term(&ins, &nIns, mid);
678689
}
679690
}
680691
db_finalize(&q);
681692
db_prepare(&q, "SELECT cid FROM plink WHERE pid=:rid AND isprim");
682693
@@ -686,16 +697,15 @@
686697
while( db_step(&q)==SQLITE_ROW ){
687698
int mid = db_column_int(&q, 0);
688699
if( bag_find(&seen, mid) ) continue;
689700
bag_insert(&seen, mid);
690701
bag_insert(&pending, mid);
691
- db_bind_int(&ins, ":rid", mid);
692
- db_step(&ins);
693
- db_reset(&ins);
702
+ uses_file_append_term(&ins, &nIns, mid);
694703
}
695704
db_reset(&q);
696705
}
697706
db_finalize(&q);
698
- db_finalize(&ins);
707
+ db_exec_sql(blob_str(&ins));
708
+ blob_reset(&ins);
699709
bag_clear(&seen);
700710
bag_clear(&pending);
701711
}
702712
--- src/descendants.c
+++ src/descendants.c
@@ -202,29 +202,28 @@
202 rLimitMtime = db_double(0.0,
203 "SELECT mtime FROM event WHERE objid=%d",
204 ridBackTo);
205 }
206 db_multi_exec(
207 "WITH RECURSIVE "
208 " parent(pid,cid,isCP) AS ("
209 " SELECT plink.pid, plink.cid, 0 AS xisCP FROM plink"
210 " UNION ALL"
211 " SELECT parentid, childid, 1 FROM cherrypick WHERE NOT isExclude"
212 " ),"
213 " ancestor(rid, mtime, isCP) AS ("
214 " SELECT %d, mtime, 0 FROM event WHERE objid=%d "
215 " UNION "
216 " SELECT parent.pid, event.mtime, parent.isCP"
217 " FROM ancestor, parent, event"
218 " WHERE parent.cid=ancestor.rid"
219 " AND event.objid=parent.pid"
220 " AND NOT ancestor.isCP"
221 " AND (event.mtime>=%.17g OR parent.pid=%d)"
222 " ORDER BY mtime DESC LIMIT %d"
223 " )"
224 "INSERT OR IGNORE INTO ok"
225 " SELECT rid FROM ancestor;",
226 rid, rid, rLimitMtime, ridBackTo, N
227 );
228 if( ridBackTo && db_changes()>1 ){
229 db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", ridBackTo);
230 }
@@ -322,18 +321,18 @@
322 N = -1;
323 }else if( N<0 ){
324 N = -N;
325 }
326 db_multi_exec(
327 "WITH RECURSIVE"
328 " dx(rid,mtime) AS ("
329 " SELECT %d, 0"
330 " UNION"
331 " SELECT plink.cid, plink.mtime FROM dx, plink"
332 " WHERE plink.pid=dx.rid"
333 " ORDER BY 2"
334 " )"
335 "INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d",
336 rid, N
337 );
338 }
339
@@ -639,44 +638,56 @@
639 /* Flag parameters to compute_uses_file() */
640 #define USESFILE_DELETE 0x01 /* Include the check-ins where file deleted */
641
642 #endif
643
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
644
645 /*
646 ** Add to table zTab the record ID (rid) of every check-in that contains
647 ** the file fid.
648 */
649 void compute_uses_file(const char *zTab, int fid, int usesFlags){
650 Bag seen;
651 Bag pending;
652 Stmt ins;
 
653 Stmt q;
654 int rid;
655
656 bag_init(&seen);
657 bag_init(&pending);
658 db_prepare(&ins, "INSERT OR IGNORE INTO \"%w\" VALUES(:rid)", zTab);
659 db_prepare(&q, "SELECT mid FROM mlink WHERE fid=%d", fid);
660 while( db_step(&q)==SQLITE_ROW ){
661 int mid = db_column_int(&q, 0);
662 bag_insert(&pending, mid);
663 bag_insert(&seen, mid);
664 db_bind_int(&ins, ":rid", mid);
665 db_step(&ins);
666 db_reset(&ins);
667 }
668 db_finalize(&q);
669
670 db_prepare(&q, "SELECT mid FROM mlink WHERE pid=%d", fid);
671 while( db_step(&q)==SQLITE_ROW ){
672 int mid = db_column_int(&q, 0);
673 bag_insert(&seen, mid);
674 if( usesFlags & USESFILE_DELETE ){
675 db_bind_int(&ins, ":rid", mid);
676 db_step(&ins);
677 db_reset(&ins);
678 }
679 }
680 db_finalize(&q);
681 db_prepare(&q, "SELECT cid FROM plink WHERE pid=:rid AND isprim");
682
@@ -686,16 +697,15 @@
686 while( db_step(&q)==SQLITE_ROW ){
687 int mid = db_column_int(&q, 0);
688 if( bag_find(&seen, mid) ) continue;
689 bag_insert(&seen, mid);
690 bag_insert(&pending, mid);
691 db_bind_int(&ins, ":rid", mid);
692 db_step(&ins);
693 db_reset(&ins);
694 }
695 db_reset(&q);
696 }
697 db_finalize(&q);
698 db_finalize(&ins);
 
699 bag_clear(&seen);
700 bag_clear(&pending);
701 }
702
--- src/descendants.c
+++ src/descendants.c
@@ -202,29 +202,28 @@
202 rLimitMtime = db_double(0.0,
203 "SELECT mtime FROM event WHERE objid=%d",
204 ridBackTo);
205 }
206 db_multi_exec(
207 "WITH RECURSIVE\n"
208 " parent(pid,cid,isCP) AS (\n"
209 " SELECT plink.pid, plink.cid, 0 AS xisCP FROM plink\n"
210 " UNION ALL\n"
211 " SELECT parentid, childid, 1 FROM cherrypick WHERE NOT isExclude\n"
212 " ),\n"
213 " ancestor(rid, mtime, isCP) AS (\n"
214 " SELECT %d, mtime, 0 FROM event WHERE objid=%d\n"
215 " UNION\n"
216 " SELECT parent.pid, event.mtime, parent.isCP\n"
217 " FROM ancestor, parent, event\n"
218 " WHERE parent.cid=ancestor.rid\n"
219 " AND event.objid=parent.pid\n"
220 " AND NOT ancestor.isCP\n"
221 " AND (event.mtime>=%.17g OR parent.pid=%d)\n"
222 " ORDER BY mtime DESC LIMIT %d\n"
223 " )\n"
224 "INSERT OR IGNORE INTO ok SELECT rid FROM ancestor;",
 
225 rid, rid, rLimitMtime, ridBackTo, N
226 );
227 if( ridBackTo && db_changes()>1 ){
228 db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", ridBackTo);
229 }
@@ -322,18 +321,18 @@
321 N = -1;
322 }else if( N<0 ){
323 N = -N;
324 }
325 db_multi_exec(
326 "WITH RECURSIVE\n"
327 " dx(rid,mtime) AS (\n"
328 " SELECT %d, 0\n"
329 " UNION\n"
330 " SELECT plink.cid, plink.mtime FROM dx, plink\n"
331 " WHERE plink.pid=dx.rid\n"
332 " ORDER BY 2\n"
333 " )\n"
334 "INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d",
335 rid, N
336 );
337 }
338
@@ -639,44 +638,56 @@
638 /* Flag parameters to compute_uses_file() */
639 #define USESFILE_DELETE 0x01 /* Include the check-ins where file deleted */
640
641 #endif
642
643 /*
644 ** Append a new VALUES term.
645 */
646 static void uses_file_append_term(Blob *pSql, int *pnCnt, int rid){
647 if( *pnCnt==0 ){
648 blob_append_sql(pSql, "(%d)", rid);
649 *pnCnt = 4;
650 }else if( (*pnCnt)%10==9 ){
651 blob_append_sql(pSql, ",\n (%d)", rid);
652 }else{
653 blob_append_sql(pSql, ",(%d)", rid);
654 }
655 ++*pnCnt;
656 }
657
658
659 /*
660 ** Add to table zTab the record ID (rid) of every check-in that contains
661 ** the file fid.
662 */
663 void compute_uses_file(const char *zTab, int fid, int usesFlags){
664 Bag seen;
665 Bag pending;
666 Blob ins = BLOB_INITIALIZER;
667 int nIns = 0;
668 Stmt q;
669 int rid;
670
671 bag_init(&seen);
672 bag_init(&pending);
673 blob_append_sql(&ins, "INSERT OR IGNORE INTO \"%w\" VALUES", zTab);
674 db_prepare(&q, "SELECT mid FROM mlink WHERE fid=%d", fid);
675 while( db_step(&q)==SQLITE_ROW ){
676 int mid = db_column_int(&q, 0);
677 bag_insert(&pending, mid);
678 bag_insert(&seen, mid);
679 uses_file_append_term(&ins, &nIns, mid);
 
 
680 }
681 db_finalize(&q);
682
683 db_prepare(&q, "SELECT mid FROM mlink WHERE pid=%d", fid);
684 while( db_step(&q)==SQLITE_ROW ){
685 int mid = db_column_int(&q, 0);
686 bag_insert(&seen, mid);
687 if( usesFlags & USESFILE_DELETE ){
688 uses_file_append_term(&ins, &nIns, mid);
 
 
689 }
690 }
691 db_finalize(&q);
692 db_prepare(&q, "SELECT cid FROM plink WHERE pid=:rid AND isprim");
693
@@ -686,16 +697,15 @@
697 while( db_step(&q)==SQLITE_ROW ){
698 int mid = db_column_int(&q, 0);
699 if( bag_find(&seen, mid) ) continue;
700 bag_insert(&seen, mid);
701 bag_insert(&pending, mid);
702 uses_file_append_term(&ins, &nIns, mid);
 
 
703 }
704 db_reset(&q);
705 }
706 db_finalize(&q);
707 db_exec_sql(blob_str(&ins));
708 blob_reset(&ins);
709 bag_clear(&seen);
710 bag_clear(&pending);
711 }
712
+8 -1
--- src/doc.c
+++ src/doc.c
@@ -788,11 +788,11 @@
788788
){
789789
Blob title;
790790
int isPopup = P("popup")!=0;
791791
blob_init(&title,0,0);
792792
if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){
793
- Blob tail;
793
+ Blob tail = BLOB_INITIALIZER;
794794
style_adunit_config(ADUNIT_RIGHT_OK);
795795
if( wiki_find_title(pBody, &title, &tail) ){
796796
if( !isPopup ) style_header("%s", blob_str(&title));
797797
wiki_convert(&tail, 0, WIKI_BUTTONS);
798798
}else{
@@ -801,10 +801,11 @@
801801
}
802802
if( !isPopup ){
803803
document_emit_js();
804804
style_finish_page();
805805
}
806
+ blob_reset(&tail);
806807
}else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
807808
Blob tail = BLOB_INITIALIZER;
808809
markdown_to_html(pBody, &title, &tail);
809810
if( !isPopup ){
810811
if( blob_size(&title)>0 ){
@@ -816,10 +817,11 @@
816817
convert_href_and_output(&tail);
817818
if( !isPopup ){
818819
document_emit_js();
819820
style_finish_page();
820821
}
822
+ blob_reset(&tail);
821823
}else if( fossil_strcmp(zMime, "text/plain")==0 ){
822824
style_header("%s", zDefaultTitle);
823825
@ <blockquote><pre>
824826
@ %h(blob_str(pBody))
825827
@ </pre></blockquote>
@@ -949,10 +951,11 @@
949951
950952
login_check_credentials();
951953
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
952954
style_set_current_feature("doc");
953955
blob_init(&title, 0, 0);
956
+ blob_init(&filebody, 0, 0);
954957
zDfltTitle = isUV ? "" : "Documentation";
955958
db_begin_transaction();
956959
while( rid==0 && (++nMiss)<=count(azSuffix) ){
957960
zName = P("name");
958961
if( isUV ){
@@ -1059,10 +1062,12 @@
10591062
}
10601063
cgi_check_for_malice();
10611064
document_render(&filebody, zMime, zDfltTitle, zName);
10621065
if( nMiss>=count(azSuffix) ) cgi_set_status(404, "Not Found");
10631066
db_end_transaction(0);
1067
+ blob_reset(&title);
1068
+ blob_reset(&filebody);
10641069
return;
10651070
10661071
/* Jump here when unable to locate the document */
10671072
doc_not_found:
10681073
db_end_transaction(0);
@@ -1075,10 +1080,12 @@
10751080
@ <p>Document %h(zOrigName) not found
10761081
if( fossil_strcmp(zCheckin,"ckout")!=0 ){
10771082
@ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a>
10781083
}
10791084
style_finish_page();
1085
+ blob_reset(&title);
1086
+ blob_reset(&filebody);
10801087
return;
10811088
}
10821089
10831090
/*
10841091
** The default logo.
10851092
--- src/doc.c
+++ src/doc.c
@@ -788,11 +788,11 @@
788 ){
789 Blob title;
790 int isPopup = P("popup")!=0;
791 blob_init(&title,0,0);
792 if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){
793 Blob tail;
794 style_adunit_config(ADUNIT_RIGHT_OK);
795 if( wiki_find_title(pBody, &title, &tail) ){
796 if( !isPopup ) style_header("%s", blob_str(&title));
797 wiki_convert(&tail, 0, WIKI_BUTTONS);
798 }else{
@@ -801,10 +801,11 @@
801 }
802 if( !isPopup ){
803 document_emit_js();
804 style_finish_page();
805 }
 
806 }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
807 Blob tail = BLOB_INITIALIZER;
808 markdown_to_html(pBody, &title, &tail);
809 if( !isPopup ){
810 if( blob_size(&title)>0 ){
@@ -816,10 +817,11 @@
816 convert_href_and_output(&tail);
817 if( !isPopup ){
818 document_emit_js();
819 style_finish_page();
820 }
 
821 }else if( fossil_strcmp(zMime, "text/plain")==0 ){
822 style_header("%s", zDefaultTitle);
823 @ <blockquote><pre>
824 @ %h(blob_str(pBody))
825 @ </pre></blockquote>
@@ -949,10 +951,11 @@
949
950 login_check_credentials();
951 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
952 style_set_current_feature("doc");
953 blob_init(&title, 0, 0);
 
954 zDfltTitle = isUV ? "" : "Documentation";
955 db_begin_transaction();
956 while( rid==0 && (++nMiss)<=count(azSuffix) ){
957 zName = P("name");
958 if( isUV ){
@@ -1059,10 +1062,12 @@
1059 }
1060 cgi_check_for_malice();
1061 document_render(&filebody, zMime, zDfltTitle, zName);
1062 if( nMiss>=count(azSuffix) ) cgi_set_status(404, "Not Found");
1063 db_end_transaction(0);
 
 
1064 return;
1065
1066 /* Jump here when unable to locate the document */
1067 doc_not_found:
1068 db_end_transaction(0);
@@ -1075,10 +1080,12 @@
1075 @ <p>Document %h(zOrigName) not found
1076 if( fossil_strcmp(zCheckin,"ckout")!=0 ){
1077 @ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a>
1078 }
1079 style_finish_page();
 
 
1080 return;
1081 }
1082
1083 /*
1084 ** The default logo.
1085
--- src/doc.c
+++ src/doc.c
@@ -788,11 +788,11 @@
788 ){
789 Blob title;
790 int isPopup = P("popup")!=0;
791 blob_init(&title,0,0);
792 if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){
793 Blob tail = BLOB_INITIALIZER;
794 style_adunit_config(ADUNIT_RIGHT_OK);
795 if( wiki_find_title(pBody, &title, &tail) ){
796 if( !isPopup ) style_header("%s", blob_str(&title));
797 wiki_convert(&tail, 0, WIKI_BUTTONS);
798 }else{
@@ -801,10 +801,11 @@
801 }
802 if( !isPopup ){
803 document_emit_js();
804 style_finish_page();
805 }
806 blob_reset(&tail);
807 }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
808 Blob tail = BLOB_INITIALIZER;
809 markdown_to_html(pBody, &title, &tail);
810 if( !isPopup ){
811 if( blob_size(&title)>0 ){
@@ -816,10 +817,11 @@
817 convert_href_and_output(&tail);
818 if( !isPopup ){
819 document_emit_js();
820 style_finish_page();
821 }
822 blob_reset(&tail);
823 }else if( fossil_strcmp(zMime, "text/plain")==0 ){
824 style_header("%s", zDefaultTitle);
825 @ <blockquote><pre>
826 @ %h(blob_str(pBody))
827 @ </pre></blockquote>
@@ -949,10 +951,11 @@
951
952 login_check_credentials();
953 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
954 style_set_current_feature("doc");
955 blob_init(&title, 0, 0);
956 blob_init(&filebody, 0, 0);
957 zDfltTitle = isUV ? "" : "Documentation";
958 db_begin_transaction();
959 while( rid==0 && (++nMiss)<=count(azSuffix) ){
960 zName = P("name");
961 if( isUV ){
@@ -1059,10 +1062,12 @@
1062 }
1063 cgi_check_for_malice();
1064 document_render(&filebody, zMime, zDfltTitle, zName);
1065 if( nMiss>=count(azSuffix) ) cgi_set_status(404, "Not Found");
1066 db_end_transaction(0);
1067 blob_reset(&title);
1068 blob_reset(&filebody);
1069 return;
1070
1071 /* Jump here when unable to locate the document */
1072 doc_not_found:
1073 db_end_transaction(0);
@@ -1075,10 +1080,12 @@
1080 @ <p>Document %h(zOrigName) not found
1081 if( fossil_strcmp(zCheckin,"ckout")!=0 ){
1082 @ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a>
1083 }
1084 style_finish_page();
1085 blob_reset(&title);
1086 blob_reset(&filebody);
1087 return;
1088 }
1089
1090 /*
1091 ** The default logo.
1092
+18
--- src/encode.c
+++ src/encode.c
@@ -729,10 +729,28 @@
729729
while( *z && n-- ){
730730
*z = zEncode[zDecode[(*z)&0x7f]&0x1f];
731731
z++;
732732
}
733733
}
734
+
735
+/*
736
+** Decode hexadecimal into a string and return the new string. Space to
737
+** hold the string is obtained from fossil_malloc() and should be released
738
+** by the caller.
739
+**
740
+** If the input is not hex, return NULL.
741
+*/
742
+char *decode16_dup(const char *zIn){
743
+ int nIn = (int)strlen(zIn);
744
+ char *zOut;
745
+ if( !validate16(zIn, nIn) ) return 0;
746
+ zOut = fossil_malloc(nIn/2+1);
747
+ decode16((const u8*)zIn, (u8*)zOut, nIn);
748
+ zOut[nIn/2] = 0;
749
+ return zOut;
750
+}
751
+
734752
735753
/*
736754
** Decode a string encoded using "quoted-printable".
737755
**
738756
** (1) "=" followed by two hex digits becomes a single
739757
--- src/encode.c
+++ src/encode.c
@@ -729,10 +729,28 @@
729 while( *z && n-- ){
730 *z = zEncode[zDecode[(*z)&0x7f]&0x1f];
731 z++;
732 }
733 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
734
735 /*
736 ** Decode a string encoded using "quoted-printable".
737 **
738 ** (1) "=" followed by two hex digits becomes a single
739
--- src/encode.c
+++ src/encode.c
@@ -729,10 +729,28 @@
729 while( *z && n-- ){
730 *z = zEncode[zDecode[(*z)&0x7f]&0x1f];
731 z++;
732 }
733 }
734
735 /*
736 ** Decode hexadecimal into a string and return the new string. Space to
737 ** hold the string is obtained from fossil_malloc() and should be released
738 ** by the caller.
739 **
740 ** If the input is not hex, return NULL.
741 */
742 char *decode16_dup(const char *zIn){
743 int nIn = (int)strlen(zIn);
744 char *zOut;
745 if( !validate16(zIn, nIn) ) return 0;
746 zOut = fossil_malloc(nIn/2+1);
747 decode16((const u8*)zIn, (u8*)zOut, nIn);
748 zOut[nIn/2] = 0;
749 return zOut;
750 }
751
752
753 /*
754 ** Decode a string encoded using "quoted-printable".
755 **
756 ** (1) "=" followed by two hex digits becomes a single
757
--- src/forum.c
+++ src/forum.c
@@ -1830,11 +1830,10 @@
18301830
if( db_int(0, "SELECT count(*) FROM user "
18311831
" WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'")==0 ){
18321832
@ <p>No non-supervisor moderators
18331833
}else{
18341834
Stmt q = empty_Stmt;
1835
- int nRows = 0;
18361835
db_prepare(&q, "SELECT uid, login, cap FROM user "
18371836
"WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'"
18381837
" ORDER BY login");
18391838
@ <table class='bordered'>
18401839
@ <thead><tr><th>User</th><th>Capabilities</th></tr></thead>
@@ -1841,11 +1840,10 @@
18411840
@ <tbody>
18421841
while( SQLITE_ROW==db_step(&q) ){
18431842
const int iUid = db_column_int(&q, 0);
18441843
const char *zUser = db_column_text(&q, 1);
18451844
const char *zCap = db_column_text(&q, 2);
1846
- ++nRows;
18471845
@ <tr>
18481846
@ <td><a href='%R/setup_uedit?id=%d(iUid)'>%h(zUser)</a></td>
18491847
@ <td>(%h(zCap))</td>
18501848
@ </tr>
18511849
}
18521850
--- src/forum.c
+++ src/forum.c
@@ -1830,11 +1830,10 @@
1830 if( db_int(0, "SELECT count(*) FROM user "
1831 " WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'")==0 ){
1832 @ <p>No non-supervisor moderators
1833 }else{
1834 Stmt q = empty_Stmt;
1835 int nRows = 0;
1836 db_prepare(&q, "SELECT uid, login, cap FROM user "
1837 "WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'"
1838 " ORDER BY login");
1839 @ <table class='bordered'>
1840 @ <thead><tr><th>User</th><th>Capabilities</th></tr></thead>
@@ -1841,11 +1840,10 @@
1841 @ <tbody>
1842 while( SQLITE_ROW==db_step(&q) ){
1843 const int iUid = db_column_int(&q, 0);
1844 const char *zUser = db_column_text(&q, 1);
1845 const char *zCap = db_column_text(&q, 2);
1846 ++nRows;
1847 @ <tr>
1848 @ <td><a href='%R/setup_uedit?id=%d(iUid)'>%h(zUser)</a></td>
1849 @ <td>(%h(zCap))</td>
1850 @ </tr>
1851 }
1852
--- src/forum.c
+++ src/forum.c
@@ -1830,11 +1830,10 @@
1830 if( db_int(0, "SELECT count(*) FROM user "
1831 " WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'")==0 ){
1832 @ <p>No non-supervisor moderators
1833 }else{
1834 Stmt q = empty_Stmt;
 
1835 db_prepare(&q, "SELECT uid, login, cap FROM user "
1836 "WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'"
1837 " ORDER BY login");
1838 @ <table class='bordered'>
1839 @ <thead><tr><th>User</th><th>Capabilities</th></tr></thead>
@@ -1841,11 +1840,10 @@
1840 @ <tbody>
1841 while( SQLITE_ROW==db_step(&q) ){
1842 const int iUid = db_column_int(&q, 0);
1843 const char *zUser = db_column_text(&q, 1);
1844 const char *zCap = db_column_text(&q, 2);
 
1845 @ <tr>
1846 @ <td><a href='%R/setup_uedit?id=%d(iUid)'>%h(zUser)</a></td>
1847 @ <td>(%h(zCap))</td>
1848 @ </tr>
1849 }
1850
+28 -12
--- src/graph.c
+++ src/graph.c
@@ -495,11 +495,15 @@
495495
**
496496
** TIMELINE_DISJOINT: Omit descenders
497497
** TIMELINE_FILLGAPS: Use step-children
498498
** TIMELINE_XMERGE: Omit off-graph merge lines
499499
*/
500
-void graph_finish(GraphContext *p, const char *zLeftBranch, u32 tmFlags){
500
+void graph_finish(
501
+ GraphContext *p, /* The graph to be laid out */
502
+ Matcher *pLeftBranch, /* Compares true for left-most branch */
503
+ u32 tmFlags /* TIMELINE flags */
504
+){
501505
GraphRow *pRow, *pDesc, *pDup, *pLoop, *pParent;
502506
int i, j;
503507
u64 mask;
504508
int hasDup = 0; /* True if one or more isDup entries */
505509
const char *zTrunk;
@@ -963,12 +967,12 @@
963967
}
964968
}
965969
966970
/*
967971
** Compute the rail mapping that tries to put the branch named
968
- ** zLeftBranch at the left margin. Other branches that merge
969
- ** with zLeftBranch are to the right with merge rails in between.
972
+ ** pLeftBranch at the left margin. Other branches that merge
973
+ ** with pLeftBranch are to the right with merge rails in between.
970974
**
971975
** aMap[X]=Y means that the X-th rail is drawn as the Y-th rail.
972976
**
973977
** Do not move rails around if there are timewarps, because that can
974978
** seriously mess up the display of timewarps. Timewarps should be
@@ -975,10 +979,11 @@
975979
** rare so this should not be a serious limitation to the algorithm.
976980
*/
977981
aMap = p->aiRailMap;
978982
for(i=0; i<=p->mxRail; i++) aMap[i] = i; /* Set up a default mapping */
979983
if( nTimewarp==0 ){
984
+ int kk;
980985
/* Priority bits:
981986
**
982987
** 0x04 The preferred branch
983988
**
984989
** 0x02 A merge rail - a rail that contains merge lines into
@@ -986,17 +991,20 @@
986991
** is defined. This improves the display of r=BRANCH
987992
** options to /timeline.
988993
**
989994
** 0x01 A rail that merges with the preferred branch
990995
*/
991
- u8 aPriority[GR_MAX_RAIL];
992
- memset(aPriority, 0, p->mxRail+1);
993
- if( zLeftBranch ){
994
- char *zLeft = persistBranchName(p, zLeftBranch);
996
+ u16 aPriority[GR_MAX_RAIL];
997
+ int mxMatch = 0;
998
+ memset(aPriority, 0, (p->mxRail+1)*sizeof(aPriority[0]));
999
+ if( pLeftBranch ){
9951000
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
996
- if( pRow->zBranch==zLeft ){
997
- aPriority[pRow->iRail] |= 4;
1001
+ int iMatch = match_text(pLeftBranch, pRow->zBranch);
1002
+ if( iMatch>0 ){
1003
+ if( iMatch>10 ) iMatch = 10;
1004
+ aPriority[pRow->iRail] |= 1<<(iMatch+1);
1005
+ if( mxMatch<iMatch ) mxMatch = iMatch;
9981006
for(i=0; i<=p->mxRail; i++){
9991007
if( pRow->mergeIn[i] ) aPriority[i] |= 1;
10001008
}
10011009
if( pRow->mergeOut>=0 ) aPriority[pRow->mergeOut] |= 1;
10021010
}
@@ -1007,10 +1015,11 @@
10071015
}
10081016
}
10091017
}else{
10101018
j = 1;
10111019
aPriority[0] = 4;
1020
+ mxMatch = 1;
10121021
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
10131022
if( pRow->iRail==0 ){
10141023
for(i=0; i<=p->mxRail; i++){
10151024
if( pRow->mergeIn[i] ) aPriority[i] |= 1;
10161025
}
@@ -1020,17 +1029,24 @@
10201029
}
10211030
10221031
#if 0
10231032
fprintf(stderr,"mergeRail: 0x%llx\n", p->mergeRail);
10241033
fprintf(stderr,"Priority:");
1025
- for(i=0; i<=p->mxRail; i++) fprintf(stderr," %d", aPriority[i]);
1034
+ for(i=0; i<=p->mxRail; i++){
1035
+ fprintf(stderr," %x.%x",
1036
+ aPriority[i]/4, aPriority[i]&3);
1037
+ }
10261038
fprintf(stderr,"\n");
10271039
#endif
10281040
10291041
j = 0;
1030
- for(i=0; i<=p->mxRail; i++){
1031
- if( aPriority[i]>=4 ) aMap[i] = j++;
1042
+ for(kk=4; kk<=1<<(mxMatch+1); kk*=2){
1043
+ for(i=0; i<=p->mxRail; i++){
1044
+ if( aPriority[i]>=kk && aPriority[i]<kk*2 ){
1045
+ aMap[i] = j++;
1046
+ }
1047
+ }
10321048
}
10331049
for(i=p->mxRail; i>=0; i--){
10341050
if( aPriority[i]==3 ) aMap[i] = j++;
10351051
}
10361052
for(i=0; i<=p->mxRail; i++){
10371053
--- src/graph.c
+++ src/graph.c
@@ -495,11 +495,15 @@
495 **
496 ** TIMELINE_DISJOINT: Omit descenders
497 ** TIMELINE_FILLGAPS: Use step-children
498 ** TIMELINE_XMERGE: Omit off-graph merge lines
499 */
500 void graph_finish(GraphContext *p, const char *zLeftBranch, u32 tmFlags){
 
 
 
 
501 GraphRow *pRow, *pDesc, *pDup, *pLoop, *pParent;
502 int i, j;
503 u64 mask;
504 int hasDup = 0; /* True if one or more isDup entries */
505 const char *zTrunk;
@@ -963,12 +967,12 @@
963 }
964 }
965
966 /*
967 ** Compute the rail mapping that tries to put the branch named
968 ** zLeftBranch at the left margin. Other branches that merge
969 ** with zLeftBranch are to the right with merge rails in between.
970 **
971 ** aMap[X]=Y means that the X-th rail is drawn as the Y-th rail.
972 **
973 ** Do not move rails around if there are timewarps, because that can
974 ** seriously mess up the display of timewarps. Timewarps should be
@@ -975,10 +979,11 @@
975 ** rare so this should not be a serious limitation to the algorithm.
976 */
977 aMap = p->aiRailMap;
978 for(i=0; i<=p->mxRail; i++) aMap[i] = i; /* Set up a default mapping */
979 if( nTimewarp==0 ){
 
980 /* Priority bits:
981 **
982 ** 0x04 The preferred branch
983 **
984 ** 0x02 A merge rail - a rail that contains merge lines into
@@ -986,17 +991,20 @@
986 ** is defined. This improves the display of r=BRANCH
987 ** options to /timeline.
988 **
989 ** 0x01 A rail that merges with the preferred branch
990 */
991 u8 aPriority[GR_MAX_RAIL];
992 memset(aPriority, 0, p->mxRail+1);
993 if( zLeftBranch ){
994 char *zLeft = persistBranchName(p, zLeftBranch);
995 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
996 if( pRow->zBranch==zLeft ){
997 aPriority[pRow->iRail] |= 4;
 
 
 
998 for(i=0; i<=p->mxRail; i++){
999 if( pRow->mergeIn[i] ) aPriority[i] |= 1;
1000 }
1001 if( pRow->mergeOut>=0 ) aPriority[pRow->mergeOut] |= 1;
1002 }
@@ -1007,10 +1015,11 @@
1007 }
1008 }
1009 }else{
1010 j = 1;
1011 aPriority[0] = 4;
 
1012 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
1013 if( pRow->iRail==0 ){
1014 for(i=0; i<=p->mxRail; i++){
1015 if( pRow->mergeIn[i] ) aPriority[i] |= 1;
1016 }
@@ -1020,17 +1029,24 @@
1020 }
1021
1022 #if 0
1023 fprintf(stderr,"mergeRail: 0x%llx\n", p->mergeRail);
1024 fprintf(stderr,"Priority:");
1025 for(i=0; i<=p->mxRail; i++) fprintf(stderr," %d", aPriority[i]);
 
 
 
1026 fprintf(stderr,"\n");
1027 #endif
1028
1029 j = 0;
1030 for(i=0; i<=p->mxRail; i++){
1031 if( aPriority[i]>=4 ) aMap[i] = j++;
 
 
 
 
1032 }
1033 for(i=p->mxRail; i>=0; i--){
1034 if( aPriority[i]==3 ) aMap[i] = j++;
1035 }
1036 for(i=0; i<=p->mxRail; i++){
1037
--- src/graph.c
+++ src/graph.c
@@ -495,11 +495,15 @@
495 **
496 ** TIMELINE_DISJOINT: Omit descenders
497 ** TIMELINE_FILLGAPS: Use step-children
498 ** TIMELINE_XMERGE: Omit off-graph merge lines
499 */
500 void graph_finish(
501 GraphContext *p, /* The graph to be laid out */
502 Matcher *pLeftBranch, /* Compares true for left-most branch */
503 u32 tmFlags /* TIMELINE flags */
504 ){
505 GraphRow *pRow, *pDesc, *pDup, *pLoop, *pParent;
506 int i, j;
507 u64 mask;
508 int hasDup = 0; /* True if one or more isDup entries */
509 const char *zTrunk;
@@ -963,12 +967,12 @@
967 }
968 }
969
970 /*
971 ** Compute the rail mapping that tries to put the branch named
972 ** pLeftBranch at the left margin. Other branches that merge
973 ** with pLeftBranch are to the right with merge rails in between.
974 **
975 ** aMap[X]=Y means that the X-th rail is drawn as the Y-th rail.
976 **
977 ** Do not move rails around if there are timewarps, because that can
978 ** seriously mess up the display of timewarps. Timewarps should be
@@ -975,10 +979,11 @@
979 ** rare so this should not be a serious limitation to the algorithm.
980 */
981 aMap = p->aiRailMap;
982 for(i=0; i<=p->mxRail; i++) aMap[i] = i; /* Set up a default mapping */
983 if( nTimewarp==0 ){
984 int kk;
985 /* Priority bits:
986 **
987 ** 0x04 The preferred branch
988 **
989 ** 0x02 A merge rail - a rail that contains merge lines into
@@ -986,17 +991,20 @@
991 ** is defined. This improves the display of r=BRANCH
992 ** options to /timeline.
993 **
994 ** 0x01 A rail that merges with the preferred branch
995 */
996 u16 aPriority[GR_MAX_RAIL];
997 int mxMatch = 0;
998 memset(aPriority, 0, (p->mxRail+1)*sizeof(aPriority[0]));
999 if( pLeftBranch ){
1000 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
1001 int iMatch = match_text(pLeftBranch, pRow->zBranch);
1002 if( iMatch>0 ){
1003 if( iMatch>10 ) iMatch = 10;
1004 aPriority[pRow->iRail] |= 1<<(iMatch+1);
1005 if( mxMatch<iMatch ) mxMatch = iMatch;
1006 for(i=0; i<=p->mxRail; i++){
1007 if( pRow->mergeIn[i] ) aPriority[i] |= 1;
1008 }
1009 if( pRow->mergeOut>=0 ) aPriority[pRow->mergeOut] |= 1;
1010 }
@@ -1007,10 +1015,11 @@
1015 }
1016 }
1017 }else{
1018 j = 1;
1019 aPriority[0] = 4;
1020 mxMatch = 1;
1021 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
1022 if( pRow->iRail==0 ){
1023 for(i=0; i<=p->mxRail; i++){
1024 if( pRow->mergeIn[i] ) aPriority[i] |= 1;
1025 }
@@ -1020,17 +1029,24 @@
1029 }
1030
1031 #if 0
1032 fprintf(stderr,"mergeRail: 0x%llx\n", p->mergeRail);
1033 fprintf(stderr,"Priority:");
1034 for(i=0; i<=p->mxRail; i++){
1035 fprintf(stderr," %x.%x",
1036 aPriority[i]/4, aPriority[i]&3);
1037 }
1038 fprintf(stderr,"\n");
1039 #endif
1040
1041 j = 0;
1042 for(kk=4; kk<=1<<(mxMatch+1); kk*=2){
1043 for(i=0; i<=p->mxRail; i++){
1044 if( aPriority[i]>=kk && aPriority[i]<kk*2 ){
1045 aMap[i] = j++;
1046 }
1047 }
1048 }
1049 for(i=p->mxRail; i>=0; i--){
1050 if( aPriority[i]==3 ) aMap[i] = j++;
1051 }
1052 for(i=0; i<=p->mxRail; i++){
1053
+149 -12
--- src/info.c
+++ src/info.c
@@ -320,10 +320,11 @@
320320
|TIMELINE_NOSCROLL
321321
|TIMELINE_XMERGE
322322
|TIMELINE_CHPICK,
323323
0, 0, 0, rid, rid2, 0);
324324
db_finalize(&q);
325
+ blob_reset(&sql);
325326
}
326327
327328
328329
/*
329330
** Append the difference between artifacts to the output
@@ -624,11 +625,10 @@
624625
pCfg = construct_diff_flags(diffType, &DCfg);
625626
nChng = db_int(0, "SELECT count(*) FROM vfile"
626627
" WHERE vid=%d AND (deleted OR chnged OR rid==0)", vid);
627628
if( nChng==0 ){
628629
@ <p>No uncommitted changes</p>
629
- style_finish_page();
630630
return;
631631
}
632632
db_prepare(&q,
633633
/* 0 1 2 3 4 5 6 */
634634
"SELECT pathname, deleted, chnged , rid==0, rid, islink, uuid"
@@ -785,17 +785,12 @@
785785
@ Changes to %h(zFile)
786786
@ </span></div>
787787
if( pCfg ){
788788
char *zFullFN;
789789
char *zHexFN;
790
- int nFullFN;
791790
zFullFN = file_canonical_name_dup(zLhs);
792
- nFullFN = (int)strlen(zFullFN);
793
- zHexFN = fossil_malloc( nFullFN*2 + 5 );
794
- zHexFN[0] = 'x';
795
- encode16((const u8*)zFullFN, (u8*)(zHexFN+1), nFullFN);
796
- zHexFN[1+nFullFN*2] = 0;
791
+ zHexFN = mprintf("x%H", zFullFN);
797792
fossil_free(zFullFN);
798793
pCfg->zLeftHash = zHexFN;
799794
text_diff(&lhs, &rhs, cgi_output_blob(), pCfg);
800795
pCfg->zLeftHash = 0;
801796
fossil_free(zHexFN);
@@ -820,11 +815,14 @@
820815
**
821816
** If the "exbase=PATH" query parameter is provided, then the diff shown
822817
** uses the files in PATH as the baseline. This is the same as using
823818
** the "--from PATH" argument to the "fossil diff" command-line. In fact,
824819
** when using "fossil ui --from PATH", the --from argument becomes the value
825
-** of the exbase query parameter for the start page.
820
+** of the exbase query parameter for the start page. Note that if PATH
821
+** is a pure hexadecimal string, it is decoded first before being used as
822
+** the pathname. Real pathnames should contain at least one directory
823
+** separator character.
826824
**
827825
** Other query parameters related to diffs are also accepted.
828826
*/
829827
void ckout_page(void){
830828
int vid;
@@ -832,11 +830,11 @@
832830
int nHome;
833831
const char *zExBase;
834832
char *zHostname;
835833
char *zCwd;
836834
837
- if( !db_open_local(0) || !cgi_is_loopback(g.zIpAddr) ){
835
+ if( !cgi_is_loopback(g.zIpAddr) || !db_open_local(0) ){
838836
cgi_redirectf("%R/home");
839837
return;
840838
}
841839
file_chdir(g.zLocalRoot, 0);
842840
vid = db_lget_int("checkout", 0);
@@ -862,18 +860,20 @@
862860
}
863861
render_checkin_context(vid, 0, 0, 0);
864862
@ <hr>
865863
zExBase = P("exbase");
866864
if( zExBase && zExBase[0] ){
867
- char *zCBase = file_canonical_name_dup(zExBase);
865
+ char *zPath = decode16_dup(zExBase);
866
+ char *zCBase = file_canonical_name_dup(zPath?zPath:zExBase);
868867
if( nHome && strncmp(zCBase, zHome, nHome)==0 && zCBase[nHome]=='/' ){
869868
@ <p>Using external baseline: ~%h(zCBase+nHome)</p>
870869
}else{
871870
@ <p>Using external baseline: %h(zCBase)</p>
872871
}
873872
ckout_external_base_diff(vid, zCBase);
874873
fossil_free(zCBase);
874
+ fossil_free(zPath);
875875
}else{
876876
ckout_normal_diff(vid);
877877
}
878878
style_finish_page();
879879
}
@@ -1933,11 +1933,11 @@
19331933
tag_private_status(rid);
19341934
}
19351935
db_finalize(&q);
19361936
if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d AND tagid=%d",
19371937
rid, TAG_CLUSTER) ){
1938
- @ Cluster
1938
+ @ Cluster %z(href("%R/info/%S",zUuid))%S(zUuid)</a>.
19391939
cnt++;
19401940
}
19411941
if( cnt==0 ){
19421942
@ Unrecognized artifact
19431943
if( pDownloadName && blob_size(pDownloadName)==0 ){
@@ -2249,12 +2249,12 @@
22492249
}
22502250
if( zName[0]=='x'
22512251
&& ((nName-1)&1)==0
22522252
&& validate16(&zName[1],nName-1)
22532253
&& g.perm.Admin
2254
- && db_open_local(0)
22552254
&& cgi_is_loopback(g.zIpAddr)
2255
+ && db_open_local(0)
22562256
){
22572257
/* Treat the HASH as a hex-encoded filename */
22582258
int n = (nName-1)/2;
22592259
char *zFN = fossil_malloc(n+1);
22602260
decode16((const u8*)&zName[1], (u8*)zFN, nName-1);
@@ -3168,10 +3168,143 @@
31683168
ticket_output_change_artifact(pTktChng, 0, 1, 0);
31693169
manifest_destroy(pTktChng);
31703170
style_finish_page();
31713171
}
31723172
3173
+/*
3174
+** rid is a cluster. Paint a page that contains detailed information
3175
+** about that cluster.
3176
+*/
3177
+static void cluster_info(int rid, const char *zName){
3178
+ Manifest *pCluster;
3179
+ int i;
3180
+ Blob where = BLOB_INITIALIZER;
3181
+ Blob unks = BLOB_INITIALIZER;
3182
+ Stmt q;
3183
+ char *zSha1Bg;
3184
+ char *zSha3Bg;
3185
+ int badRid = 0;
3186
+ int rcvid;
3187
+ int hashClr = PB("hclr");
3188
+ const char *zDate;
3189
+
3190
+ pCluster = manifest_get(rid, CFTYPE_CLUSTER, 0);
3191
+ if( pCluster==0 ){
3192
+ artifact_page();
3193
+ return;
3194
+ }
3195
+ style_header("Cluster %S", zName);
3196
+ rcvid = db_int(0, "SELECT rcvid FROM blob WHERE rid=%d", rid);
3197
+ if( rcvid==0 ){
3198
+ zDate = 0;
3199
+ }else{
3200
+ zDate = db_text(0, "SELECT datetime(mtime) FROM rcvfrom WHERE rcvid=%d",
3201
+ rcvid);
3202
+ }
3203
+ @ <p>Artifact %z(href("%R/artifact/%h",zName))%S(zName)</a> is a cluster
3204
+ @ with %d(pCluster->nCChild) entries
3205
+ if( g.perm.Admin ){
3206
+ @ received <a href="%R/rcvfrom?rcvid=%d(rcvid)">%h(zDate)</a>:
3207
+ }else{
3208
+ @ received %h(zDate):
3209
+ }
3210
+ blob_appendf(&where,"IN(0");
3211
+ for(i=0; i<pCluster->nCChild; i++){
3212
+ int rid = fast_uuid_to_rid(pCluster->azCChild[i]);
3213
+ if( rid ){
3214
+ blob_appendf(&where,",%d", rid);
3215
+ }else{
3216
+ if( blob_size(&unks)>0 ) blob_append_char(&unks, ',');
3217
+ badRid++;
3218
+ blob_append_sql(&unks,"(%d,%Q)",-badRid,pCluster->azCChild[i]);
3219
+ }
3220
+ }
3221
+ blob_append_char(&where,')');
3222
+ describe_artifacts(blob_str(&where));
3223
+ blob_reset(&where);
3224
+ if( badRid>0 ){
3225
+ db_multi_exec(
3226
+ "WITH unks(rx,hx) AS (VALUES %s)\n"
3227
+ "INSERT INTO description(rid,uuid,type,summary) "
3228
+ " SELECT rx, hx, 'phantom', '' FROM unks;",
3229
+ blob_sql_text(&unks)
3230
+ );
3231
+ }
3232
+ blob_reset(&unks);
3233
+ db_prepare(&q,
3234
+ "SELECT rid, uuid, summary, isPrivate, type='phantom', rcvid, ref"
3235
+ " FROM description ORDER BY uuid"
3236
+ );
3237
+ if( skin_detail_boolean("white-foreground") ){
3238
+ zSha1Bg = "#714417";
3239
+ zSha3Bg = "#177117";
3240
+ }else{
3241
+ zSha1Bg = "#ebffb0";
3242
+ zSha3Bg = "#b0ffb0";
3243
+ }
3244
+ @ <table cellpadding="2" cellspacing="0" border="1">
3245
+ if( g.perm.Admin ){
3246
+ @ <tr><th>RID<th>Hash<th>Rcvid<th>Description<th>Ref<th>Remarks
3247
+ }else{
3248
+ @ <tr><th>RID<th>Hash<th>Description<th>Ref<th>Remarks
3249
+ }
3250
+ while( db_step(&q)==SQLITE_ROW ){
3251
+ int rid = db_column_int(&q,0);
3252
+ const char *zUuid = db_column_text(&q, 1);
3253
+ const char *zDesc = db_column_text(&q, 2);
3254
+ int isPriv = db_column_int(&q,3);
3255
+ int isPhantom = db_column_int(&q,4);
3256
+ const char *zRef = db_column_text(&q,6);
3257
+ if( isPriv && !isPhantom && !g.perm.Private && !g.perm.Admin ){
3258
+ /* Don't show private artifacts to users without Private (x) permission */
3259
+ continue;
3260
+ }
3261
+ if( rid<=0 ){
3262
+ @ <tr><td>&nbsp;</td>
3263
+ }else if( hashClr ){
3264
+ const char *zClr = db_column_bytes(&q,1)>40 ? zSha3Bg : zSha1Bg;
3265
+ @ <tr style='background-color:%s(zClr);'><td align="right">%d(rid)</td>
3266
+ }else{
3267
+ @ <tr><td align="right">%d(rid)</td>
3268
+ }
3269
+ if( rid<=0 ){
3270
+ @ <td>&nbsp;%S(zUuid)&nbsp;</td>
3271
+ }else{
3272
+ @ <td>&nbsp;%z(href("%R/info/%!S",zUuid))%S(zUuid)</a>&nbsp;</td>
3273
+ }
3274
+ if( g.perm.Admin ){
3275
+ int rcvid = db_column_int(&q,5);
3276
+ if( rcvid<=0 ){
3277
+ @ <td>&nbsp;
3278
+ }else{
3279
+ @ <td><a href='%R/rcvfrom?rcvid=%d(rcvid)'>%d(rcvid)</a>
3280
+ }
3281
+ }
3282
+ @ <td align="left">%h(zDesc)</td>
3283
+ if( zRef && zRef[0] ){
3284
+ @ <td>%z(href("%R/info/%!S",zRef))%S(zRef)</a>
3285
+ }else{
3286
+ @ <td>&nbsp;
3287
+ }
3288
+ if( isPriv || isPhantom ){
3289
+ if( isPriv==0 ){
3290
+ @ <td>phantom</td>
3291
+ }else if( isPhantom==0 ){
3292
+ @ <td>private</td>
3293
+ }else{
3294
+ @ <td>private,phantom</td>
3295
+ }
3296
+ }else{
3297
+ @ <td>&nbsp;
3298
+ }
3299
+ @ </tr>
3300
+ }
3301
+ @ </table>
3302
+ db_finalize(&q);
3303
+ style_finish_page();
3304
+}
3305
+
31733306
31743307
/*
31753308
** WEBPAGE: info
31763309
** URL: info/NAME
31773310
**
@@ -3256,10 +3389,14 @@
32563389
if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){
32573390
ci_page();
32583391
}else
32593392
if( db_exists("SELECT 1 FROM attachment WHERE attachid=%d", rid) ){
32603393
ainfo_page();
3394
+ }else
3395
+ if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d AND tagid=%d",
3396
+ rid, TAG_CLUSTER) ){
3397
+ cluster_info(rid, zName);
32613398
}else
32623399
{
32633400
artifact_page();
32643401
}
32653402
}
32663403
--- src/info.c
+++ src/info.c
@@ -320,10 +320,11 @@
320 |TIMELINE_NOSCROLL
321 |TIMELINE_XMERGE
322 |TIMELINE_CHPICK,
323 0, 0, 0, rid, rid2, 0);
324 db_finalize(&q);
 
325 }
326
327
328 /*
329 ** Append the difference between artifacts to the output
@@ -624,11 +625,10 @@
624 pCfg = construct_diff_flags(diffType, &DCfg);
625 nChng = db_int(0, "SELECT count(*) FROM vfile"
626 " WHERE vid=%d AND (deleted OR chnged OR rid==0)", vid);
627 if( nChng==0 ){
628 @ <p>No uncommitted changes</p>
629 style_finish_page();
630 return;
631 }
632 db_prepare(&q,
633 /* 0 1 2 3 4 5 6 */
634 "SELECT pathname, deleted, chnged , rid==0, rid, islink, uuid"
@@ -785,17 +785,12 @@
785 @ Changes to %h(zFile)
786 @ </span></div>
787 if( pCfg ){
788 char *zFullFN;
789 char *zHexFN;
790 int nFullFN;
791 zFullFN = file_canonical_name_dup(zLhs);
792 nFullFN = (int)strlen(zFullFN);
793 zHexFN = fossil_malloc( nFullFN*2 + 5 );
794 zHexFN[0] = 'x';
795 encode16((const u8*)zFullFN, (u8*)(zHexFN+1), nFullFN);
796 zHexFN[1+nFullFN*2] = 0;
797 fossil_free(zFullFN);
798 pCfg->zLeftHash = zHexFN;
799 text_diff(&lhs, &rhs, cgi_output_blob(), pCfg);
800 pCfg->zLeftHash = 0;
801 fossil_free(zHexFN);
@@ -820,11 +815,14 @@
820 **
821 ** If the "exbase=PATH" query parameter is provided, then the diff shown
822 ** uses the files in PATH as the baseline. This is the same as using
823 ** the "--from PATH" argument to the "fossil diff" command-line. In fact,
824 ** when using "fossil ui --from PATH", the --from argument becomes the value
825 ** of the exbase query parameter for the start page.
 
 
 
826 **
827 ** Other query parameters related to diffs are also accepted.
828 */
829 void ckout_page(void){
830 int vid;
@@ -832,11 +830,11 @@
832 int nHome;
833 const char *zExBase;
834 char *zHostname;
835 char *zCwd;
836
837 if( !db_open_local(0) || !cgi_is_loopback(g.zIpAddr) ){
838 cgi_redirectf("%R/home");
839 return;
840 }
841 file_chdir(g.zLocalRoot, 0);
842 vid = db_lget_int("checkout", 0);
@@ -862,18 +860,20 @@
862 }
863 render_checkin_context(vid, 0, 0, 0);
864 @ <hr>
865 zExBase = P("exbase");
866 if( zExBase && zExBase[0] ){
867 char *zCBase = file_canonical_name_dup(zExBase);
 
868 if( nHome && strncmp(zCBase, zHome, nHome)==0 && zCBase[nHome]=='/' ){
869 @ <p>Using external baseline: ~%h(zCBase+nHome)</p>
870 }else{
871 @ <p>Using external baseline: %h(zCBase)</p>
872 }
873 ckout_external_base_diff(vid, zCBase);
874 fossil_free(zCBase);
 
875 }else{
876 ckout_normal_diff(vid);
877 }
878 style_finish_page();
879 }
@@ -1933,11 +1933,11 @@
1933 tag_private_status(rid);
1934 }
1935 db_finalize(&q);
1936 if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d AND tagid=%d",
1937 rid, TAG_CLUSTER) ){
1938 @ Cluster
1939 cnt++;
1940 }
1941 if( cnt==0 ){
1942 @ Unrecognized artifact
1943 if( pDownloadName && blob_size(pDownloadName)==0 ){
@@ -2249,12 +2249,12 @@
2249 }
2250 if( zName[0]=='x'
2251 && ((nName-1)&1)==0
2252 && validate16(&zName[1],nName-1)
2253 && g.perm.Admin
2254 && db_open_local(0)
2255 && cgi_is_loopback(g.zIpAddr)
 
2256 ){
2257 /* Treat the HASH as a hex-encoded filename */
2258 int n = (nName-1)/2;
2259 char *zFN = fossil_malloc(n+1);
2260 decode16((const u8*)&zName[1], (u8*)zFN, nName-1);
@@ -3168,10 +3168,143 @@
3168 ticket_output_change_artifact(pTktChng, 0, 1, 0);
3169 manifest_destroy(pTktChng);
3170 style_finish_page();
3171 }
3172
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3173
3174 /*
3175 ** WEBPAGE: info
3176 ** URL: info/NAME
3177 **
@@ -3256,10 +3389,14 @@
3256 if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){
3257 ci_page();
3258 }else
3259 if( db_exists("SELECT 1 FROM attachment WHERE attachid=%d", rid) ){
3260 ainfo_page();
 
 
 
 
3261 }else
3262 {
3263 artifact_page();
3264 }
3265 }
3266
--- src/info.c
+++ src/info.c
@@ -320,10 +320,11 @@
320 |TIMELINE_NOSCROLL
321 |TIMELINE_XMERGE
322 |TIMELINE_CHPICK,
323 0, 0, 0, rid, rid2, 0);
324 db_finalize(&q);
325 blob_reset(&sql);
326 }
327
328
329 /*
330 ** Append the difference between artifacts to the output
@@ -624,11 +625,10 @@
625 pCfg = construct_diff_flags(diffType, &DCfg);
626 nChng = db_int(0, "SELECT count(*) FROM vfile"
627 " WHERE vid=%d AND (deleted OR chnged OR rid==0)", vid);
628 if( nChng==0 ){
629 @ <p>No uncommitted changes</p>
 
630 return;
631 }
632 db_prepare(&q,
633 /* 0 1 2 3 4 5 6 */
634 "SELECT pathname, deleted, chnged , rid==0, rid, islink, uuid"
@@ -785,17 +785,12 @@
785 @ Changes to %h(zFile)
786 @ </span></div>
787 if( pCfg ){
788 char *zFullFN;
789 char *zHexFN;
 
790 zFullFN = file_canonical_name_dup(zLhs);
791 zHexFN = mprintf("x%H", zFullFN);
 
 
 
 
792 fossil_free(zFullFN);
793 pCfg->zLeftHash = zHexFN;
794 text_diff(&lhs, &rhs, cgi_output_blob(), pCfg);
795 pCfg->zLeftHash = 0;
796 fossil_free(zHexFN);
@@ -820,11 +815,14 @@
815 **
816 ** If the "exbase=PATH" query parameter is provided, then the diff shown
817 ** uses the files in PATH as the baseline. This is the same as using
818 ** the "--from PATH" argument to the "fossil diff" command-line. In fact,
819 ** when using "fossil ui --from PATH", the --from argument becomes the value
820 ** of the exbase query parameter for the start page. Note that if PATH
821 ** is a pure hexadecimal string, it is decoded first before being used as
822 ** the pathname. Real pathnames should contain at least one directory
823 ** separator character.
824 **
825 ** Other query parameters related to diffs are also accepted.
826 */
827 void ckout_page(void){
828 int vid;
@@ -832,11 +830,11 @@
830 int nHome;
831 const char *zExBase;
832 char *zHostname;
833 char *zCwd;
834
835 if( !cgi_is_loopback(g.zIpAddr) || !db_open_local(0) ){
836 cgi_redirectf("%R/home");
837 return;
838 }
839 file_chdir(g.zLocalRoot, 0);
840 vid = db_lget_int("checkout", 0);
@@ -862,18 +860,20 @@
860 }
861 render_checkin_context(vid, 0, 0, 0);
862 @ <hr>
863 zExBase = P("exbase");
864 if( zExBase && zExBase[0] ){
865 char *zPath = decode16_dup(zExBase);
866 char *zCBase = file_canonical_name_dup(zPath?zPath:zExBase);
867 if( nHome && strncmp(zCBase, zHome, nHome)==0 && zCBase[nHome]=='/' ){
868 @ <p>Using external baseline: ~%h(zCBase+nHome)</p>
869 }else{
870 @ <p>Using external baseline: %h(zCBase)</p>
871 }
872 ckout_external_base_diff(vid, zCBase);
873 fossil_free(zCBase);
874 fossil_free(zPath);
875 }else{
876 ckout_normal_diff(vid);
877 }
878 style_finish_page();
879 }
@@ -1933,11 +1933,11 @@
1933 tag_private_status(rid);
1934 }
1935 db_finalize(&q);
1936 if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d AND tagid=%d",
1937 rid, TAG_CLUSTER) ){
1938 @ Cluster %z(href("%R/info/%S",zUuid))%S(zUuid)</a>.
1939 cnt++;
1940 }
1941 if( cnt==0 ){
1942 @ Unrecognized artifact
1943 if( pDownloadName && blob_size(pDownloadName)==0 ){
@@ -2249,12 +2249,12 @@
2249 }
2250 if( zName[0]=='x'
2251 && ((nName-1)&1)==0
2252 && validate16(&zName[1],nName-1)
2253 && g.perm.Admin
 
2254 && cgi_is_loopback(g.zIpAddr)
2255 && db_open_local(0)
2256 ){
2257 /* Treat the HASH as a hex-encoded filename */
2258 int n = (nName-1)/2;
2259 char *zFN = fossil_malloc(n+1);
2260 decode16((const u8*)&zName[1], (u8*)zFN, nName-1);
@@ -3168,10 +3168,143 @@
3168 ticket_output_change_artifact(pTktChng, 0, 1, 0);
3169 manifest_destroy(pTktChng);
3170 style_finish_page();
3171 }
3172
3173 /*
3174 ** rid is a cluster. Paint a page that contains detailed information
3175 ** about that cluster.
3176 */
3177 static void cluster_info(int rid, const char *zName){
3178 Manifest *pCluster;
3179 int i;
3180 Blob where = BLOB_INITIALIZER;
3181 Blob unks = BLOB_INITIALIZER;
3182 Stmt q;
3183 char *zSha1Bg;
3184 char *zSha3Bg;
3185 int badRid = 0;
3186 int rcvid;
3187 int hashClr = PB("hclr");
3188 const char *zDate;
3189
3190 pCluster = manifest_get(rid, CFTYPE_CLUSTER, 0);
3191 if( pCluster==0 ){
3192 artifact_page();
3193 return;
3194 }
3195 style_header("Cluster %S", zName);
3196 rcvid = db_int(0, "SELECT rcvid FROM blob WHERE rid=%d", rid);
3197 if( rcvid==0 ){
3198 zDate = 0;
3199 }else{
3200 zDate = db_text(0, "SELECT datetime(mtime) FROM rcvfrom WHERE rcvid=%d",
3201 rcvid);
3202 }
3203 @ <p>Artifact %z(href("%R/artifact/%h",zName))%S(zName)</a> is a cluster
3204 @ with %d(pCluster->nCChild) entries
3205 if( g.perm.Admin ){
3206 @ received <a href="%R/rcvfrom?rcvid=%d(rcvid)">%h(zDate)</a>:
3207 }else{
3208 @ received %h(zDate):
3209 }
3210 blob_appendf(&where,"IN(0");
3211 for(i=0; i<pCluster->nCChild; i++){
3212 int rid = fast_uuid_to_rid(pCluster->azCChild[i]);
3213 if( rid ){
3214 blob_appendf(&where,",%d", rid);
3215 }else{
3216 if( blob_size(&unks)>0 ) blob_append_char(&unks, ',');
3217 badRid++;
3218 blob_append_sql(&unks,"(%d,%Q)",-badRid,pCluster->azCChild[i]);
3219 }
3220 }
3221 blob_append_char(&where,')');
3222 describe_artifacts(blob_str(&where));
3223 blob_reset(&where);
3224 if( badRid>0 ){
3225 db_multi_exec(
3226 "WITH unks(rx,hx) AS (VALUES %s)\n"
3227 "INSERT INTO description(rid,uuid,type,summary) "
3228 " SELECT rx, hx, 'phantom', '' FROM unks;",
3229 blob_sql_text(&unks)
3230 );
3231 }
3232 blob_reset(&unks);
3233 db_prepare(&q,
3234 "SELECT rid, uuid, summary, isPrivate, type='phantom', rcvid, ref"
3235 " FROM description ORDER BY uuid"
3236 );
3237 if( skin_detail_boolean("white-foreground") ){
3238 zSha1Bg = "#714417";
3239 zSha3Bg = "#177117";
3240 }else{
3241 zSha1Bg = "#ebffb0";
3242 zSha3Bg = "#b0ffb0";
3243 }
3244 @ <table cellpadding="2" cellspacing="0" border="1">
3245 if( g.perm.Admin ){
3246 @ <tr><th>RID<th>Hash<th>Rcvid<th>Description<th>Ref<th>Remarks
3247 }else{
3248 @ <tr><th>RID<th>Hash<th>Description<th>Ref<th>Remarks
3249 }
3250 while( db_step(&q)==SQLITE_ROW ){
3251 int rid = db_column_int(&q,0);
3252 const char *zUuid = db_column_text(&q, 1);
3253 const char *zDesc = db_column_text(&q, 2);
3254 int isPriv = db_column_int(&q,3);
3255 int isPhantom = db_column_int(&q,4);
3256 const char *zRef = db_column_text(&q,6);
3257 if( isPriv && !isPhantom && !g.perm.Private && !g.perm.Admin ){
3258 /* Don't show private artifacts to users without Private (x) permission */
3259 continue;
3260 }
3261 if( rid<=0 ){
3262 @ <tr><td>&nbsp;</td>
3263 }else if( hashClr ){
3264 const char *zClr = db_column_bytes(&q,1)>40 ? zSha3Bg : zSha1Bg;
3265 @ <tr style='background-color:%s(zClr);'><td align="right">%d(rid)</td>
3266 }else{
3267 @ <tr><td align="right">%d(rid)</td>
3268 }
3269 if( rid<=0 ){
3270 @ <td>&nbsp;%S(zUuid)&nbsp;</td>
3271 }else{
3272 @ <td>&nbsp;%z(href("%R/info/%!S",zUuid))%S(zUuid)</a>&nbsp;</td>
3273 }
3274 if( g.perm.Admin ){
3275 int rcvid = db_column_int(&q,5);
3276 if( rcvid<=0 ){
3277 @ <td>&nbsp;
3278 }else{
3279 @ <td><a href='%R/rcvfrom?rcvid=%d(rcvid)'>%d(rcvid)</a>
3280 }
3281 }
3282 @ <td align="left">%h(zDesc)</td>
3283 if( zRef && zRef[0] ){
3284 @ <td>%z(href("%R/info/%!S",zRef))%S(zRef)</a>
3285 }else{
3286 @ <td>&nbsp;
3287 }
3288 if( isPriv || isPhantom ){
3289 if( isPriv==0 ){
3290 @ <td>phantom</td>
3291 }else if( isPhantom==0 ){
3292 @ <td>private</td>
3293 }else{
3294 @ <td>private,phantom</td>
3295 }
3296 }else{
3297 @ <td>&nbsp;
3298 }
3299 @ </tr>
3300 }
3301 @ </table>
3302 db_finalize(&q);
3303 style_finish_page();
3304 }
3305
3306
3307 /*
3308 ** WEBPAGE: info
3309 ** URL: info/NAME
3310 **
@@ -3256,10 +3389,14 @@
3389 if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){
3390 ci_page();
3391 }else
3392 if( db_exists("SELECT 1 FROM attachment WHERE attachid=%d", rid) ){
3393 ainfo_page();
3394 }else
3395 if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d AND tagid=%d",
3396 rid, TAG_CLUSTER) ){
3397 cluster_info(rid, zName);
3398 }else
3399 {
3400 artifact_page();
3401 }
3402 }
3403
+2 -1
--- src/main.c
+++ src/main.c
@@ -1330,10 +1330,11 @@
13301330
13311331
/* We should be done with options.. */
13321332
verify_all_options();
13331333
fossil_version_blob(&versionInfo, verboseFlag);
13341334
fossil_print("%s", blob_str(&versionInfo));
1335
+ blob_reset(&versionInfo);
13351336
}
13361337
13371338
13381339
/*
13391340
** WEBPAGE: version
@@ -3297,11 +3298,11 @@
32973298
}
32983299
zInitPage = find_option("page", "p", 1);
32993300
if( zInitPage && zInitPage[0]=='/' ) zInitPage++;
33003301
zFossilCmd = find_option("fossilcmd", 0, 1);
33013302
if( zFrom && zInitPage==0 ){
3302
- zInitPage = mprintf("ckout?exbase=%T", zFrom);
3303
+ zInitPage = mprintf("ckout?exbase=%H", zFrom);
33033304
}
33043305
}
33053306
zNotFound = find_option("notfound", 0, 1);
33063307
allowRepoList = find_option("repolist",0,0)!=0;
33073308
if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1;
33083309
--- src/main.c
+++ src/main.c
@@ -1330,10 +1330,11 @@
1330
1331 /* We should be done with options.. */
1332 verify_all_options();
1333 fossil_version_blob(&versionInfo, verboseFlag);
1334 fossil_print("%s", blob_str(&versionInfo));
 
1335 }
1336
1337
1338 /*
1339 ** WEBPAGE: version
@@ -3297,11 +3298,11 @@
3297 }
3298 zInitPage = find_option("page", "p", 1);
3299 if( zInitPage && zInitPage[0]=='/' ) zInitPage++;
3300 zFossilCmd = find_option("fossilcmd", 0, 1);
3301 if( zFrom && zInitPage==0 ){
3302 zInitPage = mprintf("ckout?exbase=%T", zFrom);
3303 }
3304 }
3305 zNotFound = find_option("notfound", 0, 1);
3306 allowRepoList = find_option("repolist",0,0)!=0;
3307 if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1;
3308
--- src/main.c
+++ src/main.c
@@ -1330,10 +1330,11 @@
1330
1331 /* We should be done with options.. */
1332 verify_all_options();
1333 fossil_version_blob(&versionInfo, verboseFlag);
1334 fossil_print("%s", blob_str(&versionInfo));
1335 blob_reset(&versionInfo);
1336 }
1337
1338
1339 /*
1340 ** WEBPAGE: version
@@ -3297,11 +3298,11 @@
3298 }
3299 zInitPage = find_option("page", "p", 1);
3300 if( zInitPage && zInitPage[0]=='/' ) zInitPage++;
3301 zFossilCmd = find_option("fossilcmd", 0, 1);
3302 if( zFrom && zInitPage==0 ){
3303 zInitPage = mprintf("ckout?exbase=%H", zFrom);
3304 }
3305 }
3306 zNotFound = find_option("notfound", 0, 1);
3307 allowRepoList = find_option("repolist",0,0)!=0;
3308 if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1;
3309
+12
--- src/main.mk
+++ src/main.mk
@@ -99,10 +99,11 @@
9999
$(SRCDIR)/lookslike.c \
100100
$(SRCDIR)/main.c \
101101
$(SRCDIR)/manifest.c \
102102
$(SRCDIR)/markdown.c \
103103
$(SRCDIR)/markdown_html.c \
104
+ $(SRCDIR)/match.c \
104105
$(SRCDIR)/md5.c \
105106
$(SRCDIR)/merge.c \
106107
$(SRCDIR)/merge3.c \
107108
$(SRCDIR)/moderate.c \
108109
$(SRCDIR)/name.c \
@@ -364,10 +365,11 @@
364365
$(OBJDIR)/lookslike_.c \
365366
$(OBJDIR)/main_.c \
366367
$(OBJDIR)/manifest_.c \
367368
$(OBJDIR)/markdown_.c \
368369
$(OBJDIR)/markdown_html_.c \
370
+ $(OBJDIR)/match_.c \
369371
$(OBJDIR)/md5_.c \
370372
$(OBJDIR)/merge_.c \
371373
$(OBJDIR)/merge3_.c \
372374
$(OBJDIR)/moderate_.c \
373375
$(OBJDIR)/name_.c \
@@ -513,10 +515,11 @@
513515
$(OBJDIR)/lookslike.o \
514516
$(OBJDIR)/main.o \
515517
$(OBJDIR)/manifest.o \
516518
$(OBJDIR)/markdown.o \
517519
$(OBJDIR)/markdown_html.o \
520
+ $(OBJDIR)/match.o \
518521
$(OBJDIR)/md5.o \
519522
$(OBJDIR)/merge.o \
520523
$(OBJDIR)/merge3.o \
521524
$(OBJDIR)/moderate.o \
522525
$(OBJDIR)/name.o \
@@ -848,10 +851,11 @@
848851
$(OBJDIR)/lookslike_.c:$(OBJDIR)/lookslike.h \
849852
$(OBJDIR)/main_.c:$(OBJDIR)/main.h \
850853
$(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \
851854
$(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \
852855
$(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \
856
+ $(OBJDIR)/match_.c:$(OBJDIR)/match.h \
853857
$(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \
854858
$(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \
855859
$(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \
856860
$(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \
857861
$(OBJDIR)/name_.c:$(OBJDIR)/name.h \
@@ -1597,10 +1601,18 @@
15971601
15981602
$(OBJDIR)/markdown_html.o: $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h $(SRCDIR)/config.h
15991603
$(XTCC) -o $(OBJDIR)/markdown_html.o -c $(OBJDIR)/markdown_html_.c
16001604
16011605
$(OBJDIR)/markdown_html.h: $(OBJDIR)/headers
1606
+
1607
+$(OBJDIR)/match_.c: $(SRCDIR)/match.c $(OBJDIR)/translate
1608
+ $(OBJDIR)/translate $(SRCDIR)/match.c >$@
1609
+
1610
+$(OBJDIR)/match.o: $(OBJDIR)/match_.c $(OBJDIR)/match.h $(SRCDIR)/config.h
1611
+ $(XTCC) -o $(OBJDIR)/match.o -c $(OBJDIR)/match_.c
1612
+
1613
+$(OBJDIR)/match.h: $(OBJDIR)/headers
16021614
16031615
$(OBJDIR)/md5_.c: $(SRCDIR)/md5.c $(OBJDIR)/translate
16041616
$(OBJDIR)/translate $(SRCDIR)/md5.c >$@
16051617
16061618
$(OBJDIR)/md5.o: $(OBJDIR)/md5_.c $(OBJDIR)/md5.h $(SRCDIR)/config.h
16071619
16081620
ADDED src/match.c
--- src/main.mk
+++ src/main.mk
@@ -99,10 +99,11 @@
99 $(SRCDIR)/lookslike.c \
100 $(SRCDIR)/main.c \
101 $(SRCDIR)/manifest.c \
102 $(SRCDIR)/markdown.c \
103 $(SRCDIR)/markdown_html.c \
 
104 $(SRCDIR)/md5.c \
105 $(SRCDIR)/merge.c \
106 $(SRCDIR)/merge3.c \
107 $(SRCDIR)/moderate.c \
108 $(SRCDIR)/name.c \
@@ -364,10 +365,11 @@
364 $(OBJDIR)/lookslike_.c \
365 $(OBJDIR)/main_.c \
366 $(OBJDIR)/manifest_.c \
367 $(OBJDIR)/markdown_.c \
368 $(OBJDIR)/markdown_html_.c \
 
369 $(OBJDIR)/md5_.c \
370 $(OBJDIR)/merge_.c \
371 $(OBJDIR)/merge3_.c \
372 $(OBJDIR)/moderate_.c \
373 $(OBJDIR)/name_.c \
@@ -513,10 +515,11 @@
513 $(OBJDIR)/lookslike.o \
514 $(OBJDIR)/main.o \
515 $(OBJDIR)/manifest.o \
516 $(OBJDIR)/markdown.o \
517 $(OBJDIR)/markdown_html.o \
 
518 $(OBJDIR)/md5.o \
519 $(OBJDIR)/merge.o \
520 $(OBJDIR)/merge3.o \
521 $(OBJDIR)/moderate.o \
522 $(OBJDIR)/name.o \
@@ -848,10 +851,11 @@
848 $(OBJDIR)/lookslike_.c:$(OBJDIR)/lookslike.h \
849 $(OBJDIR)/main_.c:$(OBJDIR)/main.h \
850 $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \
851 $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \
852 $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \
 
853 $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \
854 $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \
855 $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \
856 $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \
857 $(OBJDIR)/name_.c:$(OBJDIR)/name.h \
@@ -1597,10 +1601,18 @@
1597
1598 $(OBJDIR)/markdown_html.o: $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h $(SRCDIR)/config.h
1599 $(XTCC) -o $(OBJDIR)/markdown_html.o -c $(OBJDIR)/markdown_html_.c
1600
1601 $(OBJDIR)/markdown_html.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
 
1602
1603 $(OBJDIR)/md5_.c: $(SRCDIR)/md5.c $(OBJDIR)/translate
1604 $(OBJDIR)/translate $(SRCDIR)/md5.c >$@
1605
1606 $(OBJDIR)/md5.o: $(OBJDIR)/md5_.c $(OBJDIR)/md5.h $(SRCDIR)/config.h
1607
1608 DDED src/match.c
--- src/main.mk
+++ src/main.mk
@@ -99,10 +99,11 @@
99 $(SRCDIR)/lookslike.c \
100 $(SRCDIR)/main.c \
101 $(SRCDIR)/manifest.c \
102 $(SRCDIR)/markdown.c \
103 $(SRCDIR)/markdown_html.c \
104 $(SRCDIR)/match.c \
105 $(SRCDIR)/md5.c \
106 $(SRCDIR)/merge.c \
107 $(SRCDIR)/merge3.c \
108 $(SRCDIR)/moderate.c \
109 $(SRCDIR)/name.c \
@@ -364,10 +365,11 @@
365 $(OBJDIR)/lookslike_.c \
366 $(OBJDIR)/main_.c \
367 $(OBJDIR)/manifest_.c \
368 $(OBJDIR)/markdown_.c \
369 $(OBJDIR)/markdown_html_.c \
370 $(OBJDIR)/match_.c \
371 $(OBJDIR)/md5_.c \
372 $(OBJDIR)/merge_.c \
373 $(OBJDIR)/merge3_.c \
374 $(OBJDIR)/moderate_.c \
375 $(OBJDIR)/name_.c \
@@ -513,10 +515,11 @@
515 $(OBJDIR)/lookslike.o \
516 $(OBJDIR)/main.o \
517 $(OBJDIR)/manifest.o \
518 $(OBJDIR)/markdown.o \
519 $(OBJDIR)/markdown_html.o \
520 $(OBJDIR)/match.o \
521 $(OBJDIR)/md5.o \
522 $(OBJDIR)/merge.o \
523 $(OBJDIR)/merge3.o \
524 $(OBJDIR)/moderate.o \
525 $(OBJDIR)/name.o \
@@ -848,10 +851,11 @@
851 $(OBJDIR)/lookslike_.c:$(OBJDIR)/lookslike.h \
852 $(OBJDIR)/main_.c:$(OBJDIR)/main.h \
853 $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \
854 $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \
855 $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \
856 $(OBJDIR)/match_.c:$(OBJDIR)/match.h \
857 $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \
858 $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \
859 $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \
860 $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \
861 $(OBJDIR)/name_.c:$(OBJDIR)/name.h \
@@ -1597,10 +1601,18 @@
1601
1602 $(OBJDIR)/markdown_html.o: $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h $(SRCDIR)/config.h
1603 $(XTCC) -o $(OBJDIR)/markdown_html.o -c $(OBJDIR)/markdown_html_.c
1604
1605 $(OBJDIR)/markdown_html.h: $(OBJDIR)/headers
1606
1607 $(OBJDIR)/match_.c: $(SRCDIR)/match.c $(OBJDIR)/translate
1608 $(OBJDIR)/translate $(SRCDIR)/match.c >$@
1609
1610 $(OBJDIR)/match.o: $(OBJDIR)/match_.c $(OBJDIR)/match.h $(SRCDIR)/config.h
1611 $(XTCC) -o $(OBJDIR)/match.o -c $(OBJDIR)/match_.c
1612
1613 $(OBJDIR)/match.h: $(OBJDIR)/headers
1614
1615 $(OBJDIR)/md5_.c: $(SRCDIR)/md5.c $(OBJDIR)/translate
1616 $(OBJDIR)/translate $(SRCDIR)/md5.c >$@
1617
1618 $(OBJDIR)/md5.o: $(OBJDIR)/md5_.c $(OBJDIR)/md5.h $(SRCDIR)/config.h
1619
1620 DDED src/match.c
+386
--- a/src/match.c
+++ b/src/match.c
@@ -0,0 +1,386 @@
1
+/*
2
+** Copyright (c) 2007 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
+** This file contains code to implement string comparisons using a
19
+** variety of algorithm. The comparison algorithm can be any of:
20
+**
21
+** MS_EXACT The string must exactly match the pattern.
22
+**
23
+** MS_BRLIST The pattern is a space- and/or comma-separated
24
+** list of strings, any one of which may match
25
+** the input string.
26
+**
27
+** MS_GLOB Like BRLIST, except each component of the pattern
28
+** is a GLOB expression.
29
+**
30
+** MS_LIKE Like BRLIST, except each component of the pattern
31
+** is an SQL LIKE expression.
32
+**
33
+** MS_REGEXP Like BRLIST, except each component of the pattern
34
+** is a regular expression.
35
+**
36
+*/
37
+#include "config.h"
38
+#include <string.h>
39
+#include "match.h"
40
+
41
+#if INTERFACE
42
+/*
43
+** Types of comparisons that we are able to perform:
44
+*/
45
+typedef enum {
46
+ MS_EXACT=1, /* Exact string comparison */
47
+ MS_GLOB=2, /* Matches against a list of GLOB patterns. */
48
+ MS_LIKE=3, /* Matches against a list of LIKE patterns. */
49
+ MS_REGEXP=4, /* Matches against a list of regular expressions. */
50
+ MS_BRLIST=5, /* Matches any element of a list */
51
+} MatchStyle;
52
+
53
+/*
54
+** The following object represents a precompiled pattern to use for
55
+** string matching.
56
+**
57
+** * Create an instance of this object using match_create().
58
+** * Do comparisons using match_text().
59
+** * Destroy using match_free() when you are done.
60
+**
61
+*/
62
+struct Matcher {
63
+ MatchStyle style; /* Which algorithm to use */
64
+ int nPattern; /* How many patterns are their */
65
+ char **azPattern; /* List of patterns */
66
+ ReCompiled **aRe; /* List of compiled regular expressions */
67
+};
68
+
69
+#endif /*INTERFACE*/
70
+
71
+/*
72
+** Translate a "match style" text name into the MS_* enum value.
73
+** Return eDflt if no match is found.
74
+*/
75
+MatchStyle match_style(const char *zStyle, MatchStyle eDflt){
76
+ if( zStyle==0 ) return eDflt;
77
+ if( fossil_stricmp(zStyle, "brlist")==0 ) return MS_BRLIST;
78
+ if( fossil_stricmp(zStyle, "list")==0 ) return MS_BRLIST;
79
+ if( fossil_stricmp(zStyle, "regexp")==0 ) return MS_REGEXP;
80
+ if( fossil_stricmp(zStyle, "re")==0 ) return MS_REGEXP;
81
+ if( fossil_stricmp(zStyle, "glob")==0 ) return MS_GLOB;
82
+ if( fossil_stricmp(zStyle, "like")==0 ) return MS_LIKE;
83
+ if( fossil_stricmp(zStyle, "exact")==0 ) return MS_EXACT;
84
+ return eDflt;
85
+}
86
+
87
+
88
+/*
89
+** Create a new Matcher object using the pattern provided.
90
+*/
91
+Matcher *match_create(MatchStyle style, const char *zPat){
92
+ char cDel; /* Delimiter character */
93
+ int i; /* Loop counter */
94
+ Matcher *p; /* The new Matcher to be constructed */
95
+ char *zOne; /* One element of the pattern */
96
+
97
+ if( zPat==0 ) return 0;
98
+ p = fossil_malloc( sizeof(*p) );
99
+ memset(p, 0, sizeof(*p));
100
+ p->style = style;
101
+
102
+ if( style==MS_EXACT ){
103
+ p->nPattern = 1;
104
+ p->azPattern = fossil_malloc( sizeof(p->azPattern[0]) );
105
+ p->azPattern[0] = fossil_strdup(zPat);
106
+ return p;
107
+ }
108
+
109
+ while( 1 ){
110
+ /* Skip leading delimiters. */
111
+ for( ; fossil_isspace(*zPat) || *zPat==','; ++zPat );
112
+
113
+ /* Next non-delimiter character determines quoting. */
114
+ if( zPat[0]==0 ){
115
+ /* Terminate loop at end of string. */
116
+ break;
117
+ }else if( zPat[0]=='\'' || zPat[0]=='"' ){
118
+ /* If word is quoted, prepare to stop at end quote. */
119
+ cDel = zPat[0];
120
+ ++zPat;
121
+ }else{
122
+ /* If word is not quoted, prepare to stop at delimiter. */
123
+ cDel = ',';
124
+ }
125
+
126
+ /* Find the next delimiter character or end of string. */
127
+ for( i=0; zPat[i] && zPat[i]!=cDel; ++i ){
128
+ /* If delimiter is comma, also recognize spaces as delimiters. */
129
+ if( cDel==',' && fossil_isspace(zPat[i]) ){
130
+ break;
131
+ }
132
+
133
+ /* In regexp mode, ignore delimiters following backslashes. */
134
+ if( style==MS_REGEXP && zPat[i]=='\\' && zPat[i+1] ){
135
+ ++i;
136
+ }
137
+ }
138
+
139
+ /* zOne is a zero-terminated copy of the pattern, without delimiters */
140
+ zOne = fossil_strndup(zPat, i);
141
+ zPat += i;
142
+ if( zPat[0] ) zPat++;
143
+
144
+ /* Check for regular expression syntax errors. */
145
+ if( style==MS_REGEXP ){
146
+ ReCompiled *regexp;
147
+ const char *zFail = re_compile(&regexp, zOne, 0);
148
+ if( zFail ){
149
+ re_free(regexp);
150
+ continue;
151
+ }
152
+ p->nPattern++;
153
+ p->aRe = fossil_realloc(p->aRe, sizeof(p->aRe)*p->nPattern);
154
+ p->aRe[p->nPattern-1] = regexp;
155
+ fossil_free(zOne);
156
+ }else{
157
+ p->nPattern++;
158
+ p->azPattern = fossil_realloc(p->azPattern, sizeof(char*)*p->nPattern);
159
+ p->azPattern[p->nPattern-1] = zOne;
160
+ }
161
+ }
162
+ return p;
163
+}
164
+
165
+/*
166
+** Return non-zero (true) if the input string matches the pattern
167
+** described by the matcher.
168
+**
169
+** The return value is really the 1-based index of the particular
170
+** pattern that matched.
171
+*/
172
+int match_text(Matcher *p, const char *zText){
173
+ int i;
174
+ if( p==0 ){
175
+ return zText==0;
176
+ }
177
+ switch( p->style ){
178
+ case MS_BRLIST:
179
+ case MS_EXACT: {
180
+ for(i=0; i<p->nPattern; i++){
181
+ if( strcmp(p->azPattern[i], zText)==0 ) return i+1;
182
+ }
183
+ break;
184
+ }
185
+ case MS_GLOB: {
186
+ for(i=0; i<p->nPattern; i++){
187
+ if( sqlite3_strglob(p->azPattern[i], zText)==0 ) return i+1;
188
+ }
189
+ break;
190
+ }
191
+ case MS_LIKE: {
192
+ for(i=0; i<p->nPattern; i++){
193
+ if( sqlite3_strlike(p->azPattern[i], zText, 0)==0 ) return i+1;
194
+ }
195
+ break;
196
+ }
197
+ case MS_REGEXP: {
198
+ int nText = (int)strlen(zText);
199
+ for(i=0; i<p->nPattern; i++){
200
+ if( re_match(p->aRe[i], (const u8*)zText, nText) ) return i+1;
201
+ }
202
+ break;
203
+ }
204
+ }
205
+ return 0n 0;
206
+}
207
+
208
+
209
+/*
210
+** Destroy a previously allocated Matcher object.
211
+*/
212
+void match_free(Matcher *p){
213
+ int i;
214
+ if( p==0 ) return;
215
+ if( p->style==MS_REGEXP ){
216
+ for(i=0; i<p->nPattern; i++) re_free(p->aRe[i]);
217
+ fossil_free(p->aRe);
218
+ }else{
219
+ for(i=0; i<p->nPattern; i++) fossil_free(p->azPattern[i]);
220
+ fossil_free(p->azPattern);
221
+ }
222
+ memset(p, 0, sizeof(*p));
223
+ fossil_free(p);
224
+}
225
+
226
+
227
+
228
+/*
229
+** Quote a tag string by surrounding it with double quotes and preceding
230
+** internal double quotes and backslashes with backslashes.
231
+*/
232
+static const char *tagQuote(
233
+ int len, /* Maximum length of zTag, or negative for unlimited */
234
+ const char *zTag /* Tag string */
235
+){
236
+ Blob blob = BLOB_INITIALIZER;
237
+ int i, j;
238
+ blob_zero(&blob);
239
+ blob_append(&blob, "\"", 1);
240
+ for( i=j=0; zTag[j] && (len<0 || j<len); ++j ){
241
+ if( zTag[j]=='\"' || zTag[j]=='\\' ){
242
+ if( j>i ){
243
+ blob_append(&blob, zTag+i, j-i);
244
+ }
245
+ blob_append(&blob, "\\", 1);
246
+ i = j;
247
+ }
248
+ }
249
+ if( j>i ){
250
+ blob_append(&blob, zTag+i, j-i);
251
+ }
252
+ blob_append(&blob, "\"", 1);
253
+ return blob_str(&blob);
254
+}
255
+
256
+/*
257
+** Construct the SQL expression that goes into the WHERE clause of a join
258
+** that involves the TAG table and that selects a particular tag out of
259
+** that table.
260
+**
261
+** This function is adapted from glob_expr() to support the MS_EXACT, MS_GLOB,
262
+** MS_LIKE, MS_REGEXP, and MS_BRLIST match styles.
263
+**
264
+** For MS_EXACT, the returned expression
265
+** checks for integer match against the tag ID which is looked up directly by
266
+** this function. For the other modes, the returned SQL expression performs
267
+** string comparisons against the tag names, so it is necessary to join against
268
+** the tag table to access the "tagname" column.
269
+**
270
+** Each pattern is adjusted to start with "sym-" and be anchored at end.
271
+**
272
+** In MS_REGEXP mode, backslash can be used to protect delimiter characters.
273
+** The backslashes are not removed from the regular expression.
274
+**
275
+** In addition to assembling and returning an SQL expression, this function
276
+** makes an English-language description of the patterns being matched, suitable
277
+** for display in the web interface.
278
+**
279
+** If any errors arise during processing, *zError is set to an error message.
280
+** Otherwise it is set to NULL.
281
+*/
282
+const char *match_tag_sqlexpr(
283
+ MatchStyle matchStyle, /* Match style code */
284
+ const char *zTag, /* Tag name, match pattern, or pattern list */
285
+ const char **zDesc, /* Output expression description string */
286
+ const char **zError /* Output error string */
287
+){
288
+ Blob expr = BLOB_INITIALIZER; /* SQL expression string assembly buffer */
289
+ Blob desc = BLOB_INITIALIZER; /* English description of match patterns */
290
+ Blob err = BLOB_INITIALIZER; /* Error text assembly buffer */
291
+ const char *zStart; /* Text at start of expression */
292
+ const char *zDelimiter; /* Text between expression terms */
293
+ const char *zEnd; /* Text at end of expression */
294
+ const char *zPrefix; /* Text before each match pattern */
295
+ const char *zSuffix; /* Text after each match pattern */
296
+ const char *zIntro; /* Text introducing pattern description */
297
+ const char *zPattern = 0; /* Previous quoted pattern */
298
+ const char *zFail = 0; /* Current failure message or NULL if okay */
299
+ const char *zOr = " or "; /* Text before final quoted pattern */
300
+ char cDel; /* Input delimiter character */
301
+ int i; /* Input match pattern length counter */
302
+
303
+ /* Optimize exact matches by looking up the ID in advance to create a simple
304
+ * numeric comparison. Bypass the remainder of this function. */
305
+ if( matchStyle==MS_EXACT ){
306
+ *zDesc = tagQuote(-1, zTag);
307
+ return mprintf("(tagid=%d)", db_int(-1,
308
+ "SELECT tagid FROM tag WHERE tagname='sym-%q'", zTag));
309
+ }
310
+
311
+ /* Decide pattern prefix and suffix strings according to match style. */
312
+ if( matchStyle==MS_GLOB ){
313
+ zStart = "(";
314
+ zDelimiter = " OR ";
315
+ zEnd = ")";
316
+ zPrefix = "tagname GLOB 'sym-";
317
+ zSuffix = "'";
318
+ zIntro = "glob pattern ";
319
+ }else irt = "(";
320
+ zDelimiter = " OR ";
321
+ zEnd = ")";
322
+ zPrefix = "tagname LIKE 'sym-";
323
+ zSuffix = "'";
324
+ zIntro = "SQL LIKE pattern ";
325
+ }else if( matchStyle==MS_REGEXP ){
326
+ zStart = "(tagname REGEXP '^sym-(";
327
+ zDelimiter = "|";
328
+ zEnd = ")$')";
329
+ zPrefix = "";
330
+ zSuffix = "";
331
+ zIntro = "regular expression ";
332
+ }else/* if( matchStyle==MS_BRLIST )*/{
333
+ zStart = "tagname IN ('sym-";
334
+ zDelimiter = "','sym-";
335
+ zEnd = "')";
336
+ zPrefix = "";
337
+ zSuffix = "";
338
+ zIntro = "";
339
+ }
340
+
341
+ /* Convert the list of matches into an SQL expression and text description. */
342
+ blob_zero(&expr);
343
+ blob_zero(&desc);
344
+ blob_zero(&err);
345
+ while( 1 ){
346
+ /* Skip leading delimiters. */
347
+ for( ; fossil_isspace(*zTag) || *zTag==','; ++zTag );
348
+
349
+ /* Next non-delimiter character determines quoting. */
350
+ if( !*zTag ){
351
+ /* Terminate loop at end of string. */
352
+ break;
353
+ }else if( *zTag=='\'' || *zTag=='"' ){
354
+ /* If word is quoted, prepare to stop at end quote. */
355
+ cDel = *zTag;
356
+ ++zTag;
357
+ }else{
358
+ /* If word is not quoted, prepare to stop at delimiter. */
359
+ cDel = ',';
360
+ }
361
+
362
+ /* Find the next delimiter character or end of string. */
363
+ for( i=0; zTag[i] && zTag[i]!=cDel; ++i ){
364
+ /* If delimiter is comma, also recognize spaces as delimiters. */
365
+ if( cDel==',' && fossil_isspace(zTag[i]) ){
366
+ break;
367
+ }
368
+
369
+ /* In regexp mode, ignore delimiters following backslashes. */
370
+ if( matchStyle==MS_REGEXP && zTag[i]=='\\' && zTag[i+1] ){
371
+ ++i;
372
+ }
373
+ }
374
+
375
+ /* Check for regular expression syntax errors. */
376
+ if( matchStyle==MS_REGEXP ){
377
+ ReCompiled *regexp;
378
+ char *zTagDup = fossil_strndup(zTag, i);
379
+ zFail = fossil_re_compile(&regexp, zTagDup, 0);
380
+ re_free(regexp);
381
+ fossil_free(zTagDup);
382
+ }
383
+
384
+ /* Process success and error results. */
385
+ if( !zFail ){
386
+ /* Incorporate
--- a/src/match.c
+++ b/src/match.c
@@ -0,0 +1,386 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/src/match.c
+++ b/src/match.c
@@ -0,0 +1,386 @@
1 /*
2 ** Copyright (c) 2007 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 ** This file contains code to implement string comparisons using a
19 ** variety of algorithm. The comparison algorithm can be any of:
20 **
21 ** MS_EXACT The string must exactly match the pattern.
22 **
23 ** MS_BRLIST The pattern is a space- and/or comma-separated
24 ** list of strings, any one of which may match
25 ** the input string.
26 **
27 ** MS_GLOB Like BRLIST, except each component of the pattern
28 ** is a GLOB expression.
29 **
30 ** MS_LIKE Like BRLIST, except each component of the pattern
31 ** is an SQL LIKE expression.
32 **
33 ** MS_REGEXP Like BRLIST, except each component of the pattern
34 ** is a regular expression.
35 **
36 */
37 #include "config.h"
38 #include <string.h>
39 #include "match.h"
40
41 #if INTERFACE
42 /*
43 ** Types of comparisons that we are able to perform:
44 */
45 typedef enum {
46 MS_EXACT=1, /* Exact string comparison */
47 MS_GLOB=2, /* Matches against a list of GLOB patterns. */
48 MS_LIKE=3, /* Matches against a list of LIKE patterns. */
49 MS_REGEXP=4, /* Matches against a list of regular expressions. */
50 MS_BRLIST=5, /* Matches any element of a list */
51 } MatchStyle;
52
53 /*
54 ** The following object represents a precompiled pattern to use for
55 ** string matching.
56 **
57 ** * Create an instance of this object using match_create().
58 ** * Do comparisons using match_text().
59 ** * Destroy using match_free() when you are done.
60 **
61 */
62 struct Matcher {
63 MatchStyle style; /* Which algorithm to use */
64 int nPattern; /* How many patterns are their */
65 char **azPattern; /* List of patterns */
66 ReCompiled **aRe; /* List of compiled regular expressions */
67 };
68
69 #endif /*INTERFACE*/
70
71 /*
72 ** Translate a "match style" text name into the MS_* enum value.
73 ** Return eDflt if no match is found.
74 */
75 MatchStyle match_style(const char *zStyle, MatchStyle eDflt){
76 if( zStyle==0 ) return eDflt;
77 if( fossil_stricmp(zStyle, "brlist")==0 ) return MS_BRLIST;
78 if( fossil_stricmp(zStyle, "list")==0 ) return MS_BRLIST;
79 if( fossil_stricmp(zStyle, "regexp")==0 ) return MS_REGEXP;
80 if( fossil_stricmp(zStyle, "re")==0 ) return MS_REGEXP;
81 if( fossil_stricmp(zStyle, "glob")==0 ) return MS_GLOB;
82 if( fossil_stricmp(zStyle, "like")==0 ) return MS_LIKE;
83 if( fossil_stricmp(zStyle, "exact")==0 ) return MS_EXACT;
84 return eDflt;
85 }
86
87
88 /*
89 ** Create a new Matcher object using the pattern provided.
90 */
91 Matcher *match_create(MatchStyle style, const char *zPat){
92 char cDel; /* Delimiter character */
93 int i; /* Loop counter */
94 Matcher *p; /* The new Matcher to be constructed */
95 char *zOne; /* One element of the pattern */
96
97 if( zPat==0 ) return 0;
98 p = fossil_malloc( sizeof(*p) );
99 memset(p, 0, sizeof(*p));
100 p->style = style;
101
102 if( style==MS_EXACT ){
103 p->nPattern = 1;
104 p->azPattern = fossil_malloc( sizeof(p->azPattern[0]) );
105 p->azPattern[0] = fossil_strdup(zPat);
106 return p;
107 }
108
109 while( 1 ){
110 /* Skip leading delimiters. */
111 for( ; fossil_isspace(*zPat) || *zPat==','; ++zPat );
112
113 /* Next non-delimiter character determines quoting. */
114 if( zPat[0]==0 ){
115 /* Terminate loop at end of string. */
116 break;
117 }else if( zPat[0]=='\'' || zPat[0]=='"' ){
118 /* If word is quoted, prepare to stop at end quote. */
119 cDel = zPat[0];
120 ++zPat;
121 }else{
122 /* If word is not quoted, prepare to stop at delimiter. */
123 cDel = ',';
124 }
125
126 /* Find the next delimiter character or end of string. */
127 for( i=0; zPat[i] && zPat[i]!=cDel; ++i ){
128 /* If delimiter is comma, also recognize spaces as delimiters. */
129 if( cDel==',' && fossil_isspace(zPat[i]) ){
130 break;
131 }
132
133 /* In regexp mode, ignore delimiters following backslashes. */
134 if( style==MS_REGEXP && zPat[i]=='\\' && zPat[i+1] ){
135 ++i;
136 }
137 }
138
139 /* zOne is a zero-terminated copy of the pattern, without delimiters */
140 zOne = fossil_strndup(zPat, i);
141 zPat += i;
142 if( zPat[0] ) zPat++;
143
144 /* Check for regular expression syntax errors. */
145 if( style==MS_REGEXP ){
146 ReCompiled *regexp;
147 const char *zFail = re_compile(&regexp, zOne, 0);
148 if( zFail ){
149 re_free(regexp);
150 continue;
151 }
152 p->nPattern++;
153 p->aRe = fossil_realloc(p->aRe, sizeof(p->aRe)*p->nPattern);
154 p->aRe[p->nPattern-1] = regexp;
155 fossil_free(zOne);
156 }else{
157 p->nPattern++;
158 p->azPattern = fossil_realloc(p->azPattern, sizeof(char*)*p->nPattern);
159 p->azPattern[p->nPattern-1] = zOne;
160 }
161 }
162 return p;
163 }
164
165 /*
166 ** Return non-zero (true) if the input string matches the pattern
167 ** described by the matcher.
168 **
169 ** The return value is really the 1-based index of the particular
170 ** pattern that matched.
171 */
172 int match_text(Matcher *p, const char *zText){
173 int i;
174 if( p==0 ){
175 return zText==0;
176 }
177 switch( p->style ){
178 case MS_BRLIST:
179 case MS_EXACT: {
180 for(i=0; i<p->nPattern; i++){
181 if( strcmp(p->azPattern[i], zText)==0 ) return i+1;
182 }
183 break;
184 }
185 case MS_GLOB: {
186 for(i=0; i<p->nPattern; i++){
187 if( sqlite3_strglob(p->azPattern[i], zText)==0 ) return i+1;
188 }
189 break;
190 }
191 case MS_LIKE: {
192 for(i=0; i<p->nPattern; i++){
193 if( sqlite3_strlike(p->azPattern[i], zText, 0)==0 ) return i+1;
194 }
195 break;
196 }
197 case MS_REGEXP: {
198 int nText = (int)strlen(zText);
199 for(i=0; i<p->nPattern; i++){
200 if( re_match(p->aRe[i], (const u8*)zText, nText) ) return i+1;
201 }
202 break;
203 }
204 }
205 return 0n 0;
206 }
207
208
209 /*
210 ** Destroy a previously allocated Matcher object.
211 */
212 void match_free(Matcher *p){
213 int i;
214 if( p==0 ) return;
215 if( p->style==MS_REGEXP ){
216 for(i=0; i<p->nPattern; i++) re_free(p->aRe[i]);
217 fossil_free(p->aRe);
218 }else{
219 for(i=0; i<p->nPattern; i++) fossil_free(p->azPattern[i]);
220 fossil_free(p->azPattern);
221 }
222 memset(p, 0, sizeof(*p));
223 fossil_free(p);
224 }
225
226
227
228 /*
229 ** Quote a tag string by surrounding it with double quotes and preceding
230 ** internal double quotes and backslashes with backslashes.
231 */
232 static const char *tagQuote(
233 int len, /* Maximum length of zTag, or negative for unlimited */
234 const char *zTag /* Tag string */
235 ){
236 Blob blob = BLOB_INITIALIZER;
237 int i, j;
238 blob_zero(&blob);
239 blob_append(&blob, "\"", 1);
240 for( i=j=0; zTag[j] && (len<0 || j<len); ++j ){
241 if( zTag[j]=='\"' || zTag[j]=='\\' ){
242 if( j>i ){
243 blob_append(&blob, zTag+i, j-i);
244 }
245 blob_append(&blob, "\\", 1);
246 i = j;
247 }
248 }
249 if( j>i ){
250 blob_append(&blob, zTag+i, j-i);
251 }
252 blob_append(&blob, "\"", 1);
253 return blob_str(&blob);
254 }
255
256 /*
257 ** Construct the SQL expression that goes into the WHERE clause of a join
258 ** that involves the TAG table and that selects a particular tag out of
259 ** that table.
260 **
261 ** This function is adapted from glob_expr() to support the MS_EXACT, MS_GLOB,
262 ** MS_LIKE, MS_REGEXP, and MS_BRLIST match styles.
263 **
264 ** For MS_EXACT, the returned expression
265 ** checks for integer match against the tag ID which is looked up directly by
266 ** this function. For the other modes, the returned SQL expression performs
267 ** string comparisons against the tag names, so it is necessary to join against
268 ** the tag table to access the "tagname" column.
269 **
270 ** Each pattern is adjusted to start with "sym-" and be anchored at end.
271 **
272 ** In MS_REGEXP mode, backslash can be used to protect delimiter characters.
273 ** The backslashes are not removed from the regular expression.
274 **
275 ** In addition to assembling and returning an SQL expression, this function
276 ** makes an English-language description of the patterns being matched, suitable
277 ** for display in the web interface.
278 **
279 ** If any errors arise during processing, *zError is set to an error message.
280 ** Otherwise it is set to NULL.
281 */
282 const char *match_tag_sqlexpr(
283 MatchStyle matchStyle, /* Match style code */
284 const char *zTag, /* Tag name, match pattern, or pattern list */
285 const char **zDesc, /* Output expression description string */
286 const char **zError /* Output error string */
287 ){
288 Blob expr = BLOB_INITIALIZER; /* SQL expression string assembly buffer */
289 Blob desc = BLOB_INITIALIZER; /* English description of match patterns */
290 Blob err = BLOB_INITIALIZER; /* Error text assembly buffer */
291 const char *zStart; /* Text at start of expression */
292 const char *zDelimiter; /* Text between expression terms */
293 const char *zEnd; /* Text at end of expression */
294 const char *zPrefix; /* Text before each match pattern */
295 const char *zSuffix; /* Text after each match pattern */
296 const char *zIntro; /* Text introducing pattern description */
297 const char *zPattern = 0; /* Previous quoted pattern */
298 const char *zFail = 0; /* Current failure message or NULL if okay */
299 const char *zOr = " or "; /* Text before final quoted pattern */
300 char cDel; /* Input delimiter character */
301 int i; /* Input match pattern length counter */
302
303 /* Optimize exact matches by looking up the ID in advance to create a simple
304 * numeric comparison. Bypass the remainder of this function. */
305 if( matchStyle==MS_EXACT ){
306 *zDesc = tagQuote(-1, zTag);
307 return mprintf("(tagid=%d)", db_int(-1,
308 "SELECT tagid FROM tag WHERE tagname='sym-%q'", zTag));
309 }
310
311 /* Decide pattern prefix and suffix strings according to match style. */
312 if( matchStyle==MS_GLOB ){
313 zStart = "(";
314 zDelimiter = " OR ";
315 zEnd = ")";
316 zPrefix = "tagname GLOB 'sym-";
317 zSuffix = "'";
318 zIntro = "glob pattern ";
319 }else irt = "(";
320 zDelimiter = " OR ";
321 zEnd = ")";
322 zPrefix = "tagname LIKE 'sym-";
323 zSuffix = "'";
324 zIntro = "SQL LIKE pattern ";
325 }else if( matchStyle==MS_REGEXP ){
326 zStart = "(tagname REGEXP '^sym-(";
327 zDelimiter = "|";
328 zEnd = ")$')";
329 zPrefix = "";
330 zSuffix = "";
331 zIntro = "regular expression ";
332 }else/* if( matchStyle==MS_BRLIST )*/{
333 zStart = "tagname IN ('sym-";
334 zDelimiter = "','sym-";
335 zEnd = "')";
336 zPrefix = "";
337 zSuffix = "";
338 zIntro = "";
339 }
340
341 /* Convert the list of matches into an SQL expression and text description. */
342 blob_zero(&expr);
343 blob_zero(&desc);
344 blob_zero(&err);
345 while( 1 ){
346 /* Skip leading delimiters. */
347 for( ; fossil_isspace(*zTag) || *zTag==','; ++zTag );
348
349 /* Next non-delimiter character determines quoting. */
350 if( !*zTag ){
351 /* Terminate loop at end of string. */
352 break;
353 }else if( *zTag=='\'' || *zTag=='"' ){
354 /* If word is quoted, prepare to stop at end quote. */
355 cDel = *zTag;
356 ++zTag;
357 }else{
358 /* If word is not quoted, prepare to stop at delimiter. */
359 cDel = ',';
360 }
361
362 /* Find the next delimiter character or end of string. */
363 for( i=0; zTag[i] && zTag[i]!=cDel; ++i ){
364 /* If delimiter is comma, also recognize spaces as delimiters. */
365 if( cDel==',' && fossil_isspace(zTag[i]) ){
366 break;
367 }
368
369 /* In regexp mode, ignore delimiters following backslashes. */
370 if( matchStyle==MS_REGEXP && zTag[i]=='\\' && zTag[i+1] ){
371 ++i;
372 }
373 }
374
375 /* Check for regular expression syntax errors. */
376 if( matchStyle==MS_REGEXP ){
377 ReCompiled *regexp;
378 char *zTagDup = fossil_strndup(zTag, i);
379 zFail = fossil_re_compile(&regexp, zTagDup, 0);
380 re_free(regexp);
381 fossil_free(zTagDup);
382 }
383
384 /* Process success and error results. */
385 if( !zFail ){
386 /* Incorporate
+119 -15
--- src/name.c
+++ src/name.c
@@ -1676,34 +1676,44 @@
16761676
** n=N Show N artifacts
16771677
** s=S Start with artifact number S
16781678
** priv Show only unpublished or private artifacts
16791679
** phan Show only phantom artifacts
16801680
** hclr Color code hash types (SHA1 vs SHA3)
1681
+** recent Show the most recent N artifacts
16811682
*/
16821683
void bloblist_page(void){
16831684
Stmt q;
16841685
int s = atoi(PD("s","0"));
16851686
int n = atoi(PD("n","5000"));
16861687
int mx = db_int(0, "SELECT max(rid) FROM blob");
16871688
int privOnly = PB("priv");
16881689
int phantomOnly = PB("phan");
16891690
int hashClr = PB("hclr");
1691
+ int bRecent = PB("recent");
1692
+ int bUnclst = PB("unclustered");
16901693
char *zRange;
16911694
char *zSha1Bg;
16921695
char *zSha3Bg;
16931696
16941697
login_check_credentials();
16951698
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
16961699
cgi_check_for_malice();
16971700
style_header("List Of Artifacts");
16981701
style_submenu_element("250 Largest", "bigbloblist");
1702
+ if( bRecent==0 || n!=250 ){
1703
+ style_submenu_element("Recent","bloblist?n=250&recent");
1704
+ }
1705
+ if( bUnclst==0 ){
1706
+ style_submenu_element("Unclustered","bloblist?unclustered");
1707
+ }
16991708
if( g.perm.Admin ){
17001709
style_submenu_element("Artifact Log", "rcvfromlist");
17011710
}
17021711
if( !phantomOnly ){
17031712
style_submenu_element("Phantoms", "bloblist?phan");
17041713
}
1714
+ style_submenu_element("Clusters","clusterlist");
17051715
if( g.perm.Private || g.perm.Admin ){
17061716
if( !privOnly ){
17071717
style_submenu_element("Private", "bloblist?priv");
17081718
}
17091719
}else{
@@ -1710,58 +1720,70 @@
17101720
privOnly = 0;
17111721
}
17121722
if( g.perm.Write ){
17131723
style_submenu_element("Artifact Stats", "artifact_stats");
17141724
}
1715
- if( !privOnly && !phantomOnly && mx>n && P("s")==0 ){
1725
+ if( !privOnly && !phantomOnly && mx>n && P("s")==0 && !bRecent && !bUnclst ){
17161726
int i;
17171727
@ <p>Select a range of artifacts to view:</p>
17181728
@ <ul>
17191729
for(i=1; i<=mx; i+=n){
17201730
@ <li> %z(href("%R/bloblist?s=%d&n=%d",i,n))
17211731
@ %d(i)..%d(i+n-1<mx?i+n-1:mx)</a>
17221732
}
1733
+ @ <li> %z(href("%R/bloblist?n=250&recent"))250 most recent</a>
1734
+ @ <li> %z(href("%R/bloblist?unclustered"))All unclustered</a>
17231735
@ </ul>
17241736
style_finish_page();
17251737
return;
17261738
}
17271739
if( phantomOnly || privOnly || mx>n ){
17281740
style_submenu_element("Index", "bloblist");
17291741
}
17301742
if( privOnly ){
1743
+ @ <h2>Private Artifacts</h2>
17311744
zRange = mprintf("IN private");
17321745
}else if( phantomOnly ){
1746
+ @ <h2>Phantom Artifacts</h2>
17331747
zRange = mprintf("IN phantom");
1748
+ }else if( bUnclst ){
1749
+ @ <h2>Unclustered Artifacts</h2>
1750
+ zRange = mprintf("IN unclustered");
1751
+ }else if( bRecent ){
1752
+ @ <h2>%d(n) Most Recent Artifacts</h2>
1753
+ zRange = mprintf(">=(SELECT rid FROM blob"
1754
+ " ORDER BY rid DESC LIMIT 1 OFFSET %d)",n);
17341755
}else{
17351756
zRange = mprintf("BETWEEN %d AND %d", s, s+n-1);
17361757
}
17371758
describe_artifacts(zRange);
17381759
fossil_free(zRange);
17391760
db_prepare(&q,
1740
- "SELECT rid, uuid, summary, isPrivate, type='phantom', rcvid, ref"
1741
- " FROM description ORDER BY rid"
1761
+ /* 0 1 2 3 4 5 6 */
1762
+ "SELECT rid, uuid, summary, isPrivate, type='phantom', ref, rcvid, "
1763
+ " datetime(rcvfrom.mtime)"
1764
+ " FROM description LEFT JOIN rcvfrom USING(rcvid)"
1765
+ " ORDER BY rid %s",
1766
+ ((bRecent||bUnclst)?"DESC":"ASC")/*safe-for-%s*/
17421767
);
17431768
if( skin_detail_boolean("white-foreground") ){
17441769
zSha1Bg = "#714417";
17451770
zSha3Bg = "#177117";
17461771
}else{
17471772
zSha1Bg = "#ebffb0";
17481773
zSha3Bg = "#b0ffb0";
17491774
}
17501775
@ <table cellpadding="2" cellspacing="0" border="1">
1751
- if( g.perm.Admin ){
1752
- @ <tr><th>RID<th>Hash<th>Rcvid<th>Description<th>Ref<th>Remarks
1753
- }else{
1754
- @ <tr><th>RID<th>Hash<th>Description<th>Ref<th>Remarks
1755
- }
1776
+ @ <tr><th>RID<th>Hash<th>Received<th>Description<th>Ref<th>Remarks
17561777
while( db_step(&q)==SQLITE_ROW ){
17571778
int rid = db_column_int(&q,0);
17581779
const char *zUuid = db_column_text(&q, 1);
17591780
const char *zDesc = db_column_text(&q, 2);
17601781
int isPriv = db_column_int(&q,3);
17611782
int isPhantom = db_column_int(&q,4);
1762
- const char *zRef = db_column_text(&q,6);
1783
+ const char *zRef = db_column_text(&q,5);
1784
+ const char *zDate = db_column_text(&q,7);
17631785
if( isPriv && !isPhantom && !g.perm.Private && !g.perm.Admin ){
17641786
/* Don't show private artifacts to users without Private (x) permission */
17651787
continue;
17661788
}
17671789
if( hashClr ){
@@ -1770,16 +1792,14 @@
17701792
}else{
17711793
@ <tr><td align="right">%d(rid)</td>
17721794
}
17731795
@ <td>&nbsp;%z(href("%R/info/%!S",zUuid))%S(zUuid)</a>&nbsp;</td>
17741796
if( g.perm.Admin ){
1775
- int rcvid = db_column_int(&q,5);
1776
- if( rcvid<=0 ){
1777
- @ <td>&nbsp;
1778
- }else{
1779
- @ <td><a href='%R/rcvfrom?rcvid=%d(rcvid)'>%d(rcvid)</a>
1780
- }
1797
+ int rcvid = db_column_int(&q, 6);
1798
+ @ <td><a href='%R/rcvfrom?rcvid=%d(rcvid)'>%h(zDate)</a>
1799
+ }else{
1800
+ @ <td>%h(zDate)
17811801
}
17821802
@ <td align="left">%h(zDesc)</td>
17831803
if( zRef && zRef[0] ){
17841804
@ <td>%z(href("%R/info/%!S",zRef))%S(zRef)</a>
17851805
}else{
@@ -2163,5 +2183,89 @@
21632183
" ORDER BY 1");
21642184
@ <h1>Hash Prefix Collisions on All Artifacts</h1>
21652185
collision_report("SELECT uuid FROM blob ORDER BY 1");
21662186
style_finish_page();
21672187
}
2188
+
2189
+/*
2190
+** WEBPAGE: clusterlist
2191
+**
2192
+** Show information about all cluster artifacts in the database.
2193
+*/
2194
+void clusterlist_page(void){
2195
+ Stmt q;
2196
+ int cnt = 1;
2197
+ sqlite3_int64 szTotal = 0;
2198
+ sqlite3_int64 szCTotal = 0;
2199
+ login_check_credentials();
2200
+ if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
2201
+ style_header("All Cluster Artifacts");
2202
+ style_submenu_element("All Artifactst", "bloblist");
2203
+ if( g.perm.Admin ){
2204
+ style_submenu_element("Artifact Log", "rcvfromlist");
2205
+ }
2206
+ style_submenu_element("Phantoms", "bloblist?phan");
2207
+ if( g.perm.Write ){
2208
+ style_submenu_element("Artifact Stats", "artifact_stats");
2209
+ }
2210
+
2211
+ db_prepare(&q,
2212
+ "SELECT blob.uuid, "
2213
+ " blob.size, "
2214
+ " octet_length(blob.content), "
2215
+ " datetime(rcvfrom.mtime),"
2216
+ " user.login,"
2217
+ " rcvfrom.ipaddr"
2218
+ " FROM tagxref JOIN blob ON tagxref.rid=blob.rid"
2219
+ " LEFT JOIN rcvfrom ON blob.rcvid=rcvfrom.rcvid"
2220
+ " LEFT JOIN user ON user.uid=rcvfrom.uid"
2221
+ " WHERE tagxref.tagid=%d"
2222
+ " ORDER BY rcvfrom.mtime, blob.uuid",
2223
+ TAG_CLUSTER
2224
+ );
2225
+ @ <table cellpadding="2" cellspacing="0" border="1">
2226
+ @ <tr><th>&nbsp;
2227
+ @ <th>Hash
2228
+ @ <th>Date&nbsp;Received
2229
+ @ <th>Size
2230
+ @ <th>Compressed&nbsp;Size
2231
+ if( g.perm.Admin ){
2232
+ @ <th>User<th>IP-Address
2233
+ }
2234
+ while( db_step(&q)==SQLITE_ROW ){
2235
+ const char *zUuid = db_column_text(&q, 0);
2236
+ sqlite3_int64 sz = db_column_int64(&q, 1);
2237
+ sqlite3_int64 szC = db_column_int64(&q, 2);
2238
+ const char *zDate = db_column_text(&q, 3);
2239
+ const char *zUser = db_column_text(&q, 4);
2240
+ const char *zIp = db_column_text(&q, 5);
2241
+ szTotal += sz;
2242
+ szCTotal += szC;
2243
+ @ <tr><td align="right">%d(cnt++)
2244
+ @ <td><a href="%R/info/%S(zUuid)">%S(zUuid)</a>
2245
+ if( zDate ){
2246
+ @ <td>%h(zDate)
2247
+ }else{
2248
+ @ <td>&nbsp;
2249
+ }
2250
+ @ <td align="right">%,lld(sz)
2251
+ @ <td align="right">%,lld(szC)
2252
+ if( g.perm.Admin ){
2253
+ if( zUser ){
2254
+ @ <td>%h(zUser)
2255
+ }else{
2256
+ @ <td>&nbsp;
2257
+ }
2258
+ if( zIp ){
2259
+ @ <td>%h(zIp)
2260
+ }else{
2261
+ @ <td>&nbsp;
2262
+ }
2263
+ }
2264
+ @ </tr>
2265
+ }
2266
+ @ </table>
2267
+ db_finalize(&q);
2268
+ @ <p>Total size of all clusters: %,lld(szTotal) bytes,
2269
+ @ %,lld(szCTotal) bytes compressed</p>
2270
+ style_finish_page();
2271
+}
21682272
--- src/name.c
+++ src/name.c
@@ -1676,34 +1676,44 @@
1676 ** n=N Show N artifacts
1677 ** s=S Start with artifact number S
1678 ** priv Show only unpublished or private artifacts
1679 ** phan Show only phantom artifacts
1680 ** hclr Color code hash types (SHA1 vs SHA3)
 
1681 */
1682 void bloblist_page(void){
1683 Stmt q;
1684 int s = atoi(PD("s","0"));
1685 int n = atoi(PD("n","5000"));
1686 int mx = db_int(0, "SELECT max(rid) FROM blob");
1687 int privOnly = PB("priv");
1688 int phantomOnly = PB("phan");
1689 int hashClr = PB("hclr");
 
 
1690 char *zRange;
1691 char *zSha1Bg;
1692 char *zSha3Bg;
1693
1694 login_check_credentials();
1695 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1696 cgi_check_for_malice();
1697 style_header("List Of Artifacts");
1698 style_submenu_element("250 Largest", "bigbloblist");
 
 
 
 
 
 
1699 if( g.perm.Admin ){
1700 style_submenu_element("Artifact Log", "rcvfromlist");
1701 }
1702 if( !phantomOnly ){
1703 style_submenu_element("Phantoms", "bloblist?phan");
1704 }
 
1705 if( g.perm.Private || g.perm.Admin ){
1706 if( !privOnly ){
1707 style_submenu_element("Private", "bloblist?priv");
1708 }
1709 }else{
@@ -1710,58 +1720,70 @@
1710 privOnly = 0;
1711 }
1712 if( g.perm.Write ){
1713 style_submenu_element("Artifact Stats", "artifact_stats");
1714 }
1715 if( !privOnly && !phantomOnly && mx>n && P("s")==0 ){
1716 int i;
1717 @ <p>Select a range of artifacts to view:</p>
1718 @ <ul>
1719 for(i=1; i<=mx; i+=n){
1720 @ <li> %z(href("%R/bloblist?s=%d&n=%d",i,n))
1721 @ %d(i)..%d(i+n-1<mx?i+n-1:mx)</a>
1722 }
 
 
1723 @ </ul>
1724 style_finish_page();
1725 return;
1726 }
1727 if( phantomOnly || privOnly || mx>n ){
1728 style_submenu_element("Index", "bloblist");
1729 }
1730 if( privOnly ){
 
1731 zRange = mprintf("IN private");
1732 }else if( phantomOnly ){
 
1733 zRange = mprintf("IN phantom");
 
 
 
 
 
 
 
1734 }else{
1735 zRange = mprintf("BETWEEN %d AND %d", s, s+n-1);
1736 }
1737 describe_artifacts(zRange);
1738 fossil_free(zRange);
1739 db_prepare(&q,
1740 "SELECT rid, uuid, summary, isPrivate, type='phantom', rcvid, ref"
1741 " FROM description ORDER BY rid"
 
 
 
 
1742 );
1743 if( skin_detail_boolean("white-foreground") ){
1744 zSha1Bg = "#714417";
1745 zSha3Bg = "#177117";
1746 }else{
1747 zSha1Bg = "#ebffb0";
1748 zSha3Bg = "#b0ffb0";
1749 }
1750 @ <table cellpadding="2" cellspacing="0" border="1">
1751 if( g.perm.Admin ){
1752 @ <tr><th>RID<th>Hash<th>Rcvid<th>Description<th>Ref<th>Remarks
1753 }else{
1754 @ <tr><th>RID<th>Hash<th>Description<th>Ref<th>Remarks
1755 }
1756 while( db_step(&q)==SQLITE_ROW ){
1757 int rid = db_column_int(&q,0);
1758 const char *zUuid = db_column_text(&q, 1);
1759 const char *zDesc = db_column_text(&q, 2);
1760 int isPriv = db_column_int(&q,3);
1761 int isPhantom = db_column_int(&q,4);
1762 const char *zRef = db_column_text(&q,6);
 
1763 if( isPriv && !isPhantom && !g.perm.Private && !g.perm.Admin ){
1764 /* Don't show private artifacts to users without Private (x) permission */
1765 continue;
1766 }
1767 if( hashClr ){
@@ -1770,16 +1792,14 @@
1770 }else{
1771 @ <tr><td align="right">%d(rid)</td>
1772 }
1773 @ <td>&nbsp;%z(href("%R/info/%!S",zUuid))%S(zUuid)</a>&nbsp;</td>
1774 if( g.perm.Admin ){
1775 int rcvid = db_column_int(&q,5);
1776 if( rcvid<=0 ){
1777 @ <td>&nbsp;
1778 }else{
1779 @ <td><a href='%R/rcvfrom?rcvid=%d(rcvid)'>%d(rcvid)</a>
1780 }
1781 }
1782 @ <td align="left">%h(zDesc)</td>
1783 if( zRef && zRef[0] ){
1784 @ <td>%z(href("%R/info/%!S",zRef))%S(zRef)</a>
1785 }else{
@@ -2163,5 +2183,89 @@
2163 " ORDER BY 1");
2164 @ <h1>Hash Prefix Collisions on All Artifacts</h1>
2165 collision_report("SELECT uuid FROM blob ORDER BY 1");
2166 style_finish_page();
2167 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2168
--- src/name.c
+++ src/name.c
@@ -1676,34 +1676,44 @@
1676 ** n=N Show N artifacts
1677 ** s=S Start with artifact number S
1678 ** priv Show only unpublished or private artifacts
1679 ** phan Show only phantom artifacts
1680 ** hclr Color code hash types (SHA1 vs SHA3)
1681 ** recent Show the most recent N artifacts
1682 */
1683 void bloblist_page(void){
1684 Stmt q;
1685 int s = atoi(PD("s","0"));
1686 int n = atoi(PD("n","5000"));
1687 int mx = db_int(0, "SELECT max(rid) FROM blob");
1688 int privOnly = PB("priv");
1689 int phantomOnly = PB("phan");
1690 int hashClr = PB("hclr");
1691 int bRecent = PB("recent");
1692 int bUnclst = PB("unclustered");
1693 char *zRange;
1694 char *zSha1Bg;
1695 char *zSha3Bg;
1696
1697 login_check_credentials();
1698 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1699 cgi_check_for_malice();
1700 style_header("List Of Artifacts");
1701 style_submenu_element("250 Largest", "bigbloblist");
1702 if( bRecent==0 || n!=250 ){
1703 style_submenu_element("Recent","bloblist?n=250&recent");
1704 }
1705 if( bUnclst==0 ){
1706 style_submenu_element("Unclustered","bloblist?unclustered");
1707 }
1708 if( g.perm.Admin ){
1709 style_submenu_element("Artifact Log", "rcvfromlist");
1710 }
1711 if( !phantomOnly ){
1712 style_submenu_element("Phantoms", "bloblist?phan");
1713 }
1714 style_submenu_element("Clusters","clusterlist");
1715 if( g.perm.Private || g.perm.Admin ){
1716 if( !privOnly ){
1717 style_submenu_element("Private", "bloblist?priv");
1718 }
1719 }else{
@@ -1710,58 +1720,70 @@
1720 privOnly = 0;
1721 }
1722 if( g.perm.Write ){
1723 style_submenu_element("Artifact Stats", "artifact_stats");
1724 }
1725 if( !privOnly && !phantomOnly && mx>n && P("s")==0 && !bRecent && !bUnclst ){
1726 int i;
1727 @ <p>Select a range of artifacts to view:</p>
1728 @ <ul>
1729 for(i=1; i<=mx; i+=n){
1730 @ <li> %z(href("%R/bloblist?s=%d&n=%d",i,n))
1731 @ %d(i)..%d(i+n-1<mx?i+n-1:mx)</a>
1732 }
1733 @ <li> %z(href("%R/bloblist?n=250&recent"))250 most recent</a>
1734 @ <li> %z(href("%R/bloblist?unclustered"))All unclustered</a>
1735 @ </ul>
1736 style_finish_page();
1737 return;
1738 }
1739 if( phantomOnly || privOnly || mx>n ){
1740 style_submenu_element("Index", "bloblist");
1741 }
1742 if( privOnly ){
1743 @ <h2>Private Artifacts</h2>
1744 zRange = mprintf("IN private");
1745 }else if( phantomOnly ){
1746 @ <h2>Phantom Artifacts</h2>
1747 zRange = mprintf("IN phantom");
1748 }else if( bUnclst ){
1749 @ <h2>Unclustered Artifacts</h2>
1750 zRange = mprintf("IN unclustered");
1751 }else if( bRecent ){
1752 @ <h2>%d(n) Most Recent Artifacts</h2>
1753 zRange = mprintf(">=(SELECT rid FROM blob"
1754 " ORDER BY rid DESC LIMIT 1 OFFSET %d)",n);
1755 }else{
1756 zRange = mprintf("BETWEEN %d AND %d", s, s+n-1);
1757 }
1758 describe_artifacts(zRange);
1759 fossil_free(zRange);
1760 db_prepare(&q,
1761 /* 0 1 2 3 4 5 6 */
1762 "SELECT rid, uuid, summary, isPrivate, type='phantom', ref, rcvid, "
1763 " datetime(rcvfrom.mtime)"
1764 " FROM description LEFT JOIN rcvfrom USING(rcvid)"
1765 " ORDER BY rid %s",
1766 ((bRecent||bUnclst)?"DESC":"ASC")/*safe-for-%s*/
1767 );
1768 if( skin_detail_boolean("white-foreground") ){
1769 zSha1Bg = "#714417";
1770 zSha3Bg = "#177117";
1771 }else{
1772 zSha1Bg = "#ebffb0";
1773 zSha3Bg = "#b0ffb0";
1774 }
1775 @ <table cellpadding="2" cellspacing="0" border="1">
1776 @ <tr><th>RID<th>Hash<th>Received<th>Description<th>Ref<th>Remarks
 
 
 
 
1777 while( db_step(&q)==SQLITE_ROW ){
1778 int rid = db_column_int(&q,0);
1779 const char *zUuid = db_column_text(&q, 1);
1780 const char *zDesc = db_column_text(&q, 2);
1781 int isPriv = db_column_int(&q,3);
1782 int isPhantom = db_column_int(&q,4);
1783 const char *zRef = db_column_text(&q,5);
1784 const char *zDate = db_column_text(&q,7);
1785 if( isPriv && !isPhantom && !g.perm.Private && !g.perm.Admin ){
1786 /* Don't show private artifacts to users without Private (x) permission */
1787 continue;
1788 }
1789 if( hashClr ){
@@ -1770,16 +1792,14 @@
1792 }else{
1793 @ <tr><td align="right">%d(rid)</td>
1794 }
1795 @ <td>&nbsp;%z(href("%R/info/%!S",zUuid))%S(zUuid)</a>&nbsp;</td>
1796 if( g.perm.Admin ){
1797 int rcvid = db_column_int(&q, 6);
1798 @ <td><a href='%R/rcvfrom?rcvid=%d(rcvid)'>%h(zDate)</a>
1799 }else{
1800 @ <td>%h(zDate)
 
 
1801 }
1802 @ <td align="left">%h(zDesc)</td>
1803 if( zRef && zRef[0] ){
1804 @ <td>%z(href("%R/info/%!S",zRef))%S(zRef)</a>
1805 }else{
@@ -2163,5 +2183,89 @@
2183 " ORDER BY 1");
2184 @ <h1>Hash Prefix Collisions on All Artifacts</h1>
2185 collision_report("SELECT uuid FROM blob ORDER BY 1");
2186 style_finish_page();
2187 }
2188
2189 /*
2190 ** WEBPAGE: clusterlist
2191 **
2192 ** Show information about all cluster artifacts in the database.
2193 */
2194 void clusterlist_page(void){
2195 Stmt q;
2196 int cnt = 1;
2197 sqlite3_int64 szTotal = 0;
2198 sqlite3_int64 szCTotal = 0;
2199 login_check_credentials();
2200 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
2201 style_header("All Cluster Artifacts");
2202 style_submenu_element("All Artifactst", "bloblist");
2203 if( g.perm.Admin ){
2204 style_submenu_element("Artifact Log", "rcvfromlist");
2205 }
2206 style_submenu_element("Phantoms", "bloblist?phan");
2207 if( g.perm.Write ){
2208 style_submenu_element("Artifact Stats", "artifact_stats");
2209 }
2210
2211 db_prepare(&q,
2212 "SELECT blob.uuid, "
2213 " blob.size, "
2214 " octet_length(blob.content), "
2215 " datetime(rcvfrom.mtime),"
2216 " user.login,"
2217 " rcvfrom.ipaddr"
2218 " FROM tagxref JOIN blob ON tagxref.rid=blob.rid"
2219 " LEFT JOIN rcvfrom ON blob.rcvid=rcvfrom.rcvid"
2220 " LEFT JOIN user ON user.uid=rcvfrom.uid"
2221 " WHERE tagxref.tagid=%d"
2222 " ORDER BY rcvfrom.mtime, blob.uuid",
2223 TAG_CLUSTER
2224 );
2225 @ <table cellpadding="2" cellspacing="0" border="1">
2226 @ <tr><th>&nbsp;
2227 @ <th>Hash
2228 @ <th>Date&nbsp;Received
2229 @ <th>Size
2230 @ <th>Compressed&nbsp;Size
2231 if( g.perm.Admin ){
2232 @ <th>User<th>IP-Address
2233 }
2234 while( db_step(&q)==SQLITE_ROW ){
2235 const char *zUuid = db_column_text(&q, 0);
2236 sqlite3_int64 sz = db_column_int64(&q, 1);
2237 sqlite3_int64 szC = db_column_int64(&q, 2);
2238 const char *zDate = db_column_text(&q, 3);
2239 const char *zUser = db_column_text(&q, 4);
2240 const char *zIp = db_column_text(&q, 5);
2241 szTotal += sz;
2242 szCTotal += szC;
2243 @ <tr><td align="right">%d(cnt++)
2244 @ <td><a href="%R/info/%S(zUuid)">%S(zUuid)</a>
2245 if( zDate ){
2246 @ <td>%h(zDate)
2247 }else{
2248 @ <td>&nbsp;
2249 }
2250 @ <td align="right">%,lld(sz)
2251 @ <td align="right">%,lld(szC)
2252 if( g.perm.Admin ){
2253 if( zUser ){
2254 @ <td>%h(zUser)
2255 }else{
2256 @ <td>&nbsp;
2257 }
2258 if( zIp ){
2259 @ <td>%h(zIp)
2260 }else{
2261 @ <td>&nbsp;
2262 }
2263 }
2264 @ </tr>
2265 }
2266 @ </table>
2267 db_finalize(&q);
2268 @ <p>Total size of all clusters: %,lld(szTotal) bytes,
2269 @ %,lld(szCTotal) bytes compressed</p>
2270 style_finish_page();
2271 }
2272
+8 -14
--- src/patch.c
+++ src/patch.c
@@ -81,13 +81,12 @@
8181
sqlite3_context *context,
8282
int argc,
8383
sqlite3_value **argv
8484
){
8585
const char *zFile;
86
- Blob x, y;
86
+ Blob x, y, out;
8787
int rid;
88
- char *aOut;
8988
int nOut;
9089
sqlite3_int64 sz;
9190
9291
rid = sqlite3_value_int(argv[0]);
9392
if( !content_get(rid, &x) ){
@@ -104,34 +103,29 @@
104103
if( sz<0 ){
105104
sqlite3_result_error(context, "mkdelta(X,Y): cannot read file Y", -1);
106105
blob_reset(&x);
107106
return;
108107
}
109
- aOut = sqlite3_malloc64(sz+70);
110
- if( aOut==0 ){
111
- sqlite3_result_error_nomem(context);
112
- blob_reset(&y);
113
- blob_reset(&x);
114
- return;
115
- }
108
+ blob_init(&out, 0, 0);
109
+ blob_resize(&out, sz+70);
116110
if( blob_size(&x)==blob_size(&y)
117111
&& memcmp(blob_buffer(&x), blob_buffer(&y), blob_size(&x))==0
118112
){
119113
blob_reset(&y);
120114
blob_reset(&x);
121115
sqlite3_result_blob64(context, "", 0, SQLITE_STATIC);
122116
return;
123117
}
124118
nOut = delta_create(blob_buffer(&x),blob_size(&x),
125
- blob_buffer(&y),blob_size(&y), aOut);
119
+ blob_buffer(&y),blob_size(&y), blob_buffer(&out));
120
+ blob_resize(&out, nOut);
126121
blob_reset(&x);
127122
blob_reset(&y);
128
- blob_init(&x, aOut, nOut);
129
- blob_compress(&x, &x);
130
- sqlite3_result_blob64(context, blob_buffer(&x), blob_size(&x),
123
+ blob_compress(&out, &out);
124
+ sqlite3_result_blob64(context, blob_buffer(&out), blob_size(&out),
131125
SQLITE_TRANSIENT);
132
- blob_reset(&x);
126
+ blob_reset(&out);
133127
}
134128
135129
136130
/*
137131
** Generate a binary patch file and store it into the file
138132
--- src/patch.c
+++ src/patch.c
@@ -81,13 +81,12 @@
81 sqlite3_context *context,
82 int argc,
83 sqlite3_value **argv
84 ){
85 const char *zFile;
86 Blob x, y;
87 int rid;
88 char *aOut;
89 int nOut;
90 sqlite3_int64 sz;
91
92 rid = sqlite3_value_int(argv[0]);
93 if( !content_get(rid, &x) ){
@@ -104,34 +103,29 @@
104 if( sz<0 ){
105 sqlite3_result_error(context, "mkdelta(X,Y): cannot read file Y", -1);
106 blob_reset(&x);
107 return;
108 }
109 aOut = sqlite3_malloc64(sz+70);
110 if( aOut==0 ){
111 sqlite3_result_error_nomem(context);
112 blob_reset(&y);
113 blob_reset(&x);
114 return;
115 }
116 if( blob_size(&x)==blob_size(&y)
117 && memcmp(blob_buffer(&x), blob_buffer(&y), blob_size(&x))==0
118 ){
119 blob_reset(&y);
120 blob_reset(&x);
121 sqlite3_result_blob64(context, "", 0, SQLITE_STATIC);
122 return;
123 }
124 nOut = delta_create(blob_buffer(&x),blob_size(&x),
125 blob_buffer(&y),blob_size(&y), aOut);
 
126 blob_reset(&x);
127 blob_reset(&y);
128 blob_init(&x, aOut, nOut);
129 blob_compress(&x, &x);
130 sqlite3_result_blob64(context, blob_buffer(&x), blob_size(&x),
131 SQLITE_TRANSIENT);
132 blob_reset(&x);
133 }
134
135
136 /*
137 ** Generate a binary patch file and store it into the file
138
--- src/patch.c
+++ src/patch.c
@@ -81,13 +81,12 @@
81 sqlite3_context *context,
82 int argc,
83 sqlite3_value **argv
84 ){
85 const char *zFile;
86 Blob x, y, out;
87 int rid;
 
88 int nOut;
89 sqlite3_int64 sz;
90
91 rid = sqlite3_value_int(argv[0]);
92 if( !content_get(rid, &x) ){
@@ -104,34 +103,29 @@
103 if( sz<0 ){
104 sqlite3_result_error(context, "mkdelta(X,Y): cannot read file Y", -1);
105 blob_reset(&x);
106 return;
107 }
108 blob_init(&out, 0, 0);
109 blob_resize(&out, sz+70);
 
 
 
 
 
110 if( blob_size(&x)==blob_size(&y)
111 && memcmp(blob_buffer(&x), blob_buffer(&y), blob_size(&x))==0
112 ){
113 blob_reset(&y);
114 blob_reset(&x);
115 sqlite3_result_blob64(context, "", 0, SQLITE_STATIC);
116 return;
117 }
118 nOut = delta_create(blob_buffer(&x),blob_size(&x),
119 blob_buffer(&y),blob_size(&y), blob_buffer(&out));
120 blob_resize(&out, nOut);
121 blob_reset(&x);
122 blob_reset(&y);
123 blob_compress(&out, &out);
124 sqlite3_result_blob64(context, blob_buffer(&out), blob_size(&out),
 
125 SQLITE_TRANSIENT);
126 blob_reset(&out);
127 }
128
129
130 /*
131 ** Generate a binary patch file and store it into the file
132
+14 -1
--- src/printf.c
+++ src/printf.c
@@ -105,10 +105,11 @@
105105
Use %!j to include double-quotes around it. */
106106
#define etSHELLESC 26 /* Escape a filename for use in a shell command: %$
107107
See blob_append_escaped_arg() for details
108108
"%$" -> adds "./" prefix if necessary.
109109
"%!$" -> omits the "./" prefix. */
110
+#define etHEX 27 /* Encode a string as hexadecimal */
110111
111112
112113
/*
113114
** An "etByte" is an 8-bit unsigned value.
114115
*/
@@ -142,11 +143,11 @@
142143
** NB: When modifying this table is it vital that you also update the fmtchr[]
143144
** variable to match!!!
144145
*/
145146
static const char aDigits[] = "0123456789ABCDEF0123456789abcdef";
146147
static const char aPrefix[] = "-x0\000X0";
147
-static const char fmtchr[] = "dsgzqQbBWhRtTwFSjcouxXfeEGin%p/$";
148
+static const char fmtchr[] = "dsgzqQbBWhRtTwFSjcouxXfeEGin%p/$H";
148149
static const et_info fmtinfo[] = {
149150
{ 'd', 10, 1, etRADIX, 0, 0 },
150151
{ 's', 0, 4, etSTRING, 0, 0 },
151152
{ 'g', 0, 1, etGENERIC, 30, 0 },
152153
{ 'z', 0, 6, etDYNSTRING, 0, 0 },
@@ -176,10 +177,11 @@
176177
{ 'n', 0, 0, etSIZE, 0, 0 },
177178
{ '%', 0, 0, etPERCENT, 0, 0 },
178179
{ 'p', 16, 0, etPOINTER, 0, 1 },
179180
{ '/', 0, 0, etPATH, 0, 0 },
180181
{ '$', 0, 0, etSHELLESC, 0, 0 },
182
+ { 'H', 0, 0, etHEX, 0, 0 },
181183
{ etERROR, 0,0,0,0,0} /* Must be last */
182184
};
183185
#define etNINFO count(fmtinfo)
184186
185187
/*
@@ -843,10 +845,21 @@
843845
case etSHELLESC: {
844846
char *zArg = va_arg(ap, char*);
845847
blob_append_escaped_arg(pBlob, zArg, !flag_altform2);
846848
length = width = 0;
847849
break;
850
+ }
851
+ case etHEX: {
852
+ char *zArg = va_arg(ap, char*);
853
+ int szArg = (int)strlen(zArg);
854
+ int szBlob = blob_size(pBlob);
855
+ u8 *aBuf;
856
+ blob_resize(pBlob, szBlob+szArg*2+1);
857
+ aBuf = (u8*)&blob_buffer(pBlob)[szBlob];
858
+ encode16((const u8*)zArg, aBuf, szArg);
859
+ length = width = 0;
860
+ break;
848861
}
849862
case etERROR:
850863
buf[0] = '%';
851864
buf[1] = c;
852865
errorflag = 0;
853866
--- src/printf.c
+++ src/printf.c
@@ -105,10 +105,11 @@
105 Use %!j to include double-quotes around it. */
106 #define etSHELLESC 26 /* Escape a filename for use in a shell command: %$
107 See blob_append_escaped_arg() for details
108 "%$" -> adds "./" prefix if necessary.
109 "%!$" -> omits the "./" prefix. */
 
110
111
112 /*
113 ** An "etByte" is an 8-bit unsigned value.
114 */
@@ -142,11 +143,11 @@
142 ** NB: When modifying this table is it vital that you also update the fmtchr[]
143 ** variable to match!!!
144 */
145 static const char aDigits[] = "0123456789ABCDEF0123456789abcdef";
146 static const char aPrefix[] = "-x0\000X0";
147 static const char fmtchr[] = "dsgzqQbBWhRtTwFSjcouxXfeEGin%p/$";
148 static const et_info fmtinfo[] = {
149 { 'd', 10, 1, etRADIX, 0, 0 },
150 { 's', 0, 4, etSTRING, 0, 0 },
151 { 'g', 0, 1, etGENERIC, 30, 0 },
152 { 'z', 0, 6, etDYNSTRING, 0, 0 },
@@ -176,10 +177,11 @@
176 { 'n', 0, 0, etSIZE, 0, 0 },
177 { '%', 0, 0, etPERCENT, 0, 0 },
178 { 'p', 16, 0, etPOINTER, 0, 1 },
179 { '/', 0, 0, etPATH, 0, 0 },
180 { '$', 0, 0, etSHELLESC, 0, 0 },
 
181 { etERROR, 0,0,0,0,0} /* Must be last */
182 };
183 #define etNINFO count(fmtinfo)
184
185 /*
@@ -843,10 +845,21 @@
843 case etSHELLESC: {
844 char *zArg = va_arg(ap, char*);
845 blob_append_escaped_arg(pBlob, zArg, !flag_altform2);
846 length = width = 0;
847 break;
 
 
 
 
 
 
 
 
 
 
 
848 }
849 case etERROR:
850 buf[0] = '%';
851 buf[1] = c;
852 errorflag = 0;
853
--- src/printf.c
+++ src/printf.c
@@ -105,10 +105,11 @@
105 Use %!j to include double-quotes around it. */
106 #define etSHELLESC 26 /* Escape a filename for use in a shell command: %$
107 See blob_append_escaped_arg() for details
108 "%$" -> adds "./" prefix if necessary.
109 "%!$" -> omits the "./" prefix. */
110 #define etHEX 27 /* Encode a string as hexadecimal */
111
112
113 /*
114 ** An "etByte" is an 8-bit unsigned value.
115 */
@@ -142,11 +143,11 @@
143 ** NB: When modifying this table is it vital that you also update the fmtchr[]
144 ** variable to match!!!
145 */
146 static const char aDigits[] = "0123456789ABCDEF0123456789abcdef";
147 static const char aPrefix[] = "-x0\000X0";
148 static const char fmtchr[] = "dsgzqQbBWhRtTwFSjcouxXfeEGin%p/$H";
149 static const et_info fmtinfo[] = {
150 { 'd', 10, 1, etRADIX, 0, 0 },
151 { 's', 0, 4, etSTRING, 0, 0 },
152 { 'g', 0, 1, etGENERIC, 30, 0 },
153 { 'z', 0, 6, etDYNSTRING, 0, 0 },
@@ -176,10 +177,11 @@
177 { 'n', 0, 0, etSIZE, 0, 0 },
178 { '%', 0, 0, etPERCENT, 0, 0 },
179 { 'p', 16, 0, etPOINTER, 0, 1 },
180 { '/', 0, 0, etPATH, 0, 0 },
181 { '$', 0, 0, etSHELLESC, 0, 0 },
182 { 'H', 0, 0, etHEX, 0, 0 },
183 { etERROR, 0,0,0,0,0} /* Must be last */
184 };
185 #define etNINFO count(fmtinfo)
186
187 /*
@@ -843,10 +845,21 @@
845 case etSHELLESC: {
846 char *zArg = va_arg(ap, char*);
847 blob_append_escaped_arg(pBlob, zArg, !flag_altform2);
848 length = width = 0;
849 break;
850 }
851 case etHEX: {
852 char *zArg = va_arg(ap, char*);
853 int szArg = (int)strlen(zArg);
854 int szBlob = blob_size(pBlob);
855 u8 *aBuf;
856 blob_resize(pBlob, szBlob+szArg*2+1);
857 aBuf = (u8*)&blob_buffer(pBlob)[szBlob];
858 encode16((const u8*)zArg, aBuf, szArg);
859 length = width = 0;
860 break;
861 }
862 case etERROR:
863 buf[0] = '%';
864 buf[1] = c;
865 errorflag = 0;
866
+1 -1
--- src/sitemap.c
+++ src/sitemap.c
@@ -116,11 +116,11 @@
116116
if( inSublist ){
117117
@ </ul>
118118
inSublist = 0;
119119
}
120120
@ </li>
121
- if( db_open_local(0) && cgi_is_loopback(g.zIpAddr) ){
121
+ if( cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){
122122
@ <li>%z(href("%R/ckout"))Checkout Status</a></li>
123123
}
124124
if( g.perm.Read ){
125125
const char *zEditGlob = db_get("fileedit-glob","");
126126
@ <li>%z(href("%R/tree"))File Browser</a>
127127
--- src/sitemap.c
+++ src/sitemap.c
@@ -116,11 +116,11 @@
116 if( inSublist ){
117 @ </ul>
118 inSublist = 0;
119 }
120 @ </li>
121 if( db_open_local(0) && cgi_is_loopback(g.zIpAddr) ){
122 @ <li>%z(href("%R/ckout"))Checkout Status</a></li>
123 }
124 if( g.perm.Read ){
125 const char *zEditGlob = db_get("fileedit-glob","");
126 @ <li>%z(href("%R/tree"))File Browser</a>
127
--- src/sitemap.c
+++ src/sitemap.c
@@ -116,11 +116,11 @@
116 if( inSublist ){
117 @ </ul>
118 inSublist = 0;
119 }
120 @ </li>
121 if( cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){
122 @ <li>%z(href("%R/ckout"))Checkout Status</a></li>
123 }
124 if( g.perm.Read ){
125 const char *zEditGlob = db_get("fileedit-glob","");
126 @ <li>%z(href("%R/tree"))File Browser</a>
127
--- src/style.c
+++ src/style.c
@@ -821,10 +821,11 @@
821821
if( g.perm.Debug && P("showqp") ){
822822
@ <div class="debug">
823823
cgi_print_all(0, 0, 0);
824824
@ </div>
825825
}
826
+ fossil_free(zTitle);
826827
}
827828
828829
#if INTERFACE
829830
/* Allowed parameters for style_adunit() */
830831
#define ADUNIT_OFF 0x0001 /* Do not allow ads on this page */
@@ -1300,10 +1301,11 @@
13001301
Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL);
13011302
Th_Store("home", g.zTop);
13021303
image_url_var("logo");
13031304
image_url_var("background");
13041305
Th_Render(blob_str(&css));
1306
+ blob_reset(&css);
13051307
13061308
/* Tell CGI that the content returned by this page is considered cacheable */
13071309
g.isConst = 1;
13081310
}
13091311
13101312
--- src/style.c
+++ src/style.c
@@ -821,10 +821,11 @@
821 if( g.perm.Debug && P("showqp") ){
822 @ <div class="debug">
823 cgi_print_all(0, 0, 0);
824 @ </div>
825 }
 
826 }
827
828 #if INTERFACE
829 /* Allowed parameters for style_adunit() */
830 #define ADUNIT_OFF 0x0001 /* Do not allow ads on this page */
@@ -1300,10 +1301,11 @@
1300 Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL);
1301 Th_Store("home", g.zTop);
1302 image_url_var("logo");
1303 image_url_var("background");
1304 Th_Render(blob_str(&css));
 
1305
1306 /* Tell CGI that the content returned by this page is considered cacheable */
1307 g.isConst = 1;
1308 }
1309
1310
--- src/style.c
+++ src/style.c
@@ -821,10 +821,11 @@
821 if( g.perm.Debug && P("showqp") ){
822 @ <div class="debug">
823 cgi_print_all(0, 0, 0);
824 @ </div>
825 }
826 fossil_free(zTitle);
827 }
828
829 #if INTERFACE
830 /* Allowed parameters for style_adunit() */
831 #define ADUNIT_OFF 0x0001 /* Do not allow ads on this page */
@@ -1300,10 +1301,11 @@
1301 Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL);
1302 Th_Store("home", g.zTop);
1303 image_url_var("logo");
1304 image_url_var("background");
1305 Th_Render(blob_str(&css));
1306 blob_reset(&css);
1307
1308 /* Tell CGI that the content returned by this page is considered cacheable */
1309 g.isConst = 1;
1310 }
1311
1312
+241 -369
--- src/timeline.c
+++ src/timeline.c
@@ -191,11 +191,11 @@
191191
void www_print_timeline(
192192
Stmt *pQuery, /* Query to implement the timeline */
193193
int tmFlags, /* Flags controlling display behavior */
194194
const char *zThisUser, /* Suppress links to this user */
195195
const char *zThisTag, /* Suppress links to this tag */
196
- const char *zLeftBranch, /* Strive to put this branch on the left margin */
196
+ Matcher *pLeftBranch, /* Comparison function to use for zLeftBranch */
197197
int selectedRid, /* Highlight the line with this RID value or zero */
198198
int secondRid, /* Secondary highlight (or zero) */
199199
void (*xExtra)(int) /* Routine to call on each line of display */
200200
){
201201
int mxWikiLen;
@@ -813,11 +813,11 @@
813813
}
814814
if( pendingEndTr ){
815815
@ </td></tr>
816816
}
817817
if( pGraph ){
818
- graph_finish(pGraph, zLeftBranch, tmFlags);
818
+ graph_finish(pGraph, pLeftBranch, tmFlags);
819819
if( pGraph->nErr ){
820820
graph_free(pGraph);
821821
pGraph = 0;
822822
}else{
823823
@ <tr class="timelineBottom" id="btm-%d(iTableId)">\
@@ -1290,12 +1290,13 @@
12901290
const char *zChng, /* The filename GLOB list */
12911291
Blob *pSql /* The SELECT statement under construction */
12921292
){
12931293
if( zChng==0 || zChng[0]==0 ) return;
12941294
blob_append_sql(pSql," AND event.objid IN ("
1295
- "SELECT mlink.mid FROM mlink, filename"
1296
- " WHERE mlink.fnid=filename.fnid AND %s)",
1295
+ "SELECT mlink.mid FROM mlink, filename\n"
1296
+ " WHERE mlink.fnid=filename.fnid\n"
1297
+ " AND %s)",
12971298
glob_expr("filename.name", mprintf("\"%s\"", zChng)));
12981299
}
12991300
static void addFileGlobDescription(
13001301
const char *zChng, /* The filename GLOB list */
13011302
Blob *pDescription /* Result description */
@@ -1303,237 +1304,10 @@
13031304
if( zChng==0 || zChng[0]==0 ) return;
13041305
blob_appendf(pDescription, " that include changes to files matching '%h'",
13051306
zChng);
13061307
}
13071308
1308
-/*
1309
-** Tag match expression type code.
1310
-*/
1311
-typedef enum {
1312
- MS_EXACT, /* Matches a single tag by exact string comparison. */
1313
- MS_GLOB, /* Matches tags against a list of GLOB patterns. */
1314
- MS_LIKE, /* Matches tags against a list of LIKE patterns. */
1315
- MS_REGEXP, /* Matches tags against a list of regular expressions. */
1316
- MS_BRLIST, /* Same as REGEXP, except the regular expression is a list
1317
- ** of branch names */
1318
-} MatchStyle;
1319
-
1320
-/*
1321
-** Quote a tag string by surrounding it with double quotes and preceding
1322
-** internal double quotes and backslashes with backslashes.
1323
-*/
1324
-static const char *tagQuote(
1325
- int len, /* Maximum length of zTag, or negative for unlimited */
1326
- const char *zTag /* Tag string */
1327
-){
1328
- Blob blob = BLOB_INITIALIZER;
1329
- int i, j;
1330
- blob_zero(&blob);
1331
- blob_append(&blob, "\"", 1);
1332
- for( i=j=0; zTag[j] && (len<0 || j<len); ++j ){
1333
- if( zTag[j]=='\"' || zTag[j]=='\\' ){
1334
- if( j>i ){
1335
- blob_append(&blob, zTag+i, j-i);
1336
- }
1337
- blob_append(&blob, "\\", 1);
1338
- i = j;
1339
- }
1340
- }
1341
- if( j>i ){
1342
- blob_append(&blob, zTag+i, j-i);
1343
- }
1344
- blob_append(&blob, "\"", 1);
1345
- return blob_str(&blob);
1346
-}
1347
-
1348
-/*
1349
-** Construct the tag match SQL expression.
1350
-**
1351
-** This function is adapted from glob_expr() to support the MS_EXACT, MS_GLOB,
1352
-** MS_LIKE, MS_REGEXP, and MS_BRLIST match styles.
1353
-**
1354
-** For MS_EXACT, the returned expression
1355
-** checks for integer match against the tag ID which is looked up directly by
1356
-** this function. For the other modes, the returned SQL expression performs
1357
-** string comparisons against the tag names, so it is necessary to join against
1358
-** the tag table to access the "tagname" column.
1359
-**
1360
-** Each pattern is adjusted to to start with "sym-" and be anchored at end.
1361
-**
1362
-** In MS_REGEXP mode, backslash can be used to protect delimiter characters.
1363
-** The backslashes are not removed from the regular expression.
1364
-**
1365
-** In addition to assembling and returning an SQL expression, this function
1366
-** makes an English-language description of the patterns being matched, suitable
1367
-** for display in the web interface.
1368
-**
1369
-** If any errors arise during processing, *zError is set to an error message.
1370
-** Otherwise it is set to NULL.
1371
-*/
1372
-static const char *tagMatchExpression(
1373
- MatchStyle matchStyle, /* Match style code */
1374
- const char *zTag, /* Tag name, match pattern, or pattern list */
1375
- const char **zDesc, /* Output expression description string */
1376
- const char **zError /* Output error string */
1377
-){
1378
- Blob expr = BLOB_INITIALIZER; /* SQL expression string assembly buffer */
1379
- Blob desc = BLOB_INITIALIZER; /* English description of match patterns */
1380
- Blob err = BLOB_INITIALIZER; /* Error text assembly buffer */
1381
- const char *zStart; /* Text at start of expression */
1382
- const char *zDelimiter; /* Text between expression terms */
1383
- const char *zEnd; /* Text at end of expression */
1384
- const char *zPrefix; /* Text before each match pattern */
1385
- const char *zSuffix; /* Text after each match pattern */
1386
- const char *zIntro; /* Text introducing pattern description */
1387
- const char *zPattern = 0; /* Previous quoted pattern */
1388
- const char *zFail = 0; /* Current failure message or NULL if okay */
1389
- const char *zOr = " or "; /* Text before final quoted pattern */
1390
- char cDel; /* Input delimiter character */
1391
- int i; /* Input match pattern length counter */
1392
-
1393
- /* Optimize exact matches by looking up the ID in advance to create a simple
1394
- * numeric comparison. Bypass the remainder of this function. */
1395
- if( matchStyle==MS_EXACT ){
1396
- *zDesc = tagQuote(-1, zTag);
1397
- return mprintf("(tagid=%d)", db_int(-1,
1398
- "SELECT tagid FROM tag WHERE tagname='sym-%q'", zTag));
1399
- }
1400
-
1401
- /* Decide pattern prefix and suffix strings according to match style. */
1402
- if( matchStyle==MS_GLOB ){
1403
- zStart = "(";
1404
- zDelimiter = " OR ";
1405
- zEnd = ")";
1406
- zPrefix = "tagname GLOB 'sym-";
1407
- zSuffix = "'";
1408
- zIntro = "glob pattern ";
1409
- }else if( matchStyle==MS_LIKE ){
1410
- zStart = "(";
1411
- zDelimiter = " OR ";
1412
- zEnd = ")";
1413
- zPrefix = "tagname LIKE 'sym-";
1414
- zSuffix = "'";
1415
- zIntro = "SQL LIKE pattern ";
1416
- }else if( matchStyle==MS_REGEXP ){
1417
- zStart = "(tagname REGEXP '^sym-(";
1418
- zDelimiter = "|";
1419
- zEnd = ")$')";
1420
- zPrefix = "";
1421
- zSuffix = "";
1422
- zIntro = "regular expression ";
1423
- }else/* if( matchStyle==MS_BRLIST )*/{
1424
- zStart = "tagname IN ('sym-";
1425
- zDelimiter = "','sym-";
1426
- zEnd = "')";
1427
- zPrefix = "";
1428
- zSuffix = "";
1429
- zIntro = "";
1430
- }
1431
-
1432
- /* Convert the list of matches into an SQL expression and text description. */
1433
- blob_zero(&expr);
1434
- blob_zero(&desc);
1435
- blob_zero(&err);
1436
- while( 1 ){
1437
- /* Skip leading delimiters. */
1438
- for( ; fossil_isspace(*zTag) || *zTag==','; ++zTag );
1439
-
1440
- /* Next non-delimiter character determines quoting. */
1441
- if( !*zTag ){
1442
- /* Terminate loop at end of string. */
1443
- break;
1444
- }else if( *zTag=='\'' || *zTag=='"' ){
1445
- /* If word is quoted, prepare to stop at end quote. */
1446
- cDel = *zTag;
1447
- ++zTag;
1448
- }else{
1449
- /* If word is not quoted, prepare to stop at delimiter. */
1450
- cDel = ',';
1451
- }
1452
-
1453
- /* Find the next delimiter character or end of string. */
1454
- for( i=0; zTag[i] && zTag[i]!=cDel; ++i ){
1455
- /* If delimiter is comma, also recognize spaces as delimiters. */
1456
- if( cDel==',' && fossil_isspace(zTag[i]) ){
1457
- break;
1458
- }
1459
-
1460
- /* In regexp mode, ignore delimiters following backslashes. */
1461
- if( matchStyle==MS_REGEXP && zTag[i]=='\\' && zTag[i+1] ){
1462
- ++i;
1463
- }
1464
- }
1465
-
1466
- /* Check for regular expression syntax errors. */
1467
- if( matchStyle==MS_REGEXP ){
1468
- ReCompiled *regexp;
1469
- char *zTagDup = fossil_strndup(zTag, i);
1470
- zFail = re_compile(&regexp, zTagDup, 0);
1471
- re_free(regexp);
1472
- fossil_free(zTagDup);
1473
- }
1474
-
1475
- /* Process success and error results. */
1476
- if( !zFail ){
1477
- /* Incorporate the match word into the output expression. %q is used to
1478
- * protect against SQL injection attacks by replacing ' with ''. */
1479
- blob_appendf(&expr, "%s%s%#q%s", blob_size(&expr) ? zDelimiter : zStart,
1480
- zPrefix, i, zTag, zSuffix);
1481
-
1482
- /* Build up the description string. */
1483
- if( !blob_size(&desc) ){
1484
- /* First tag: start with intro followed by first quoted tag. */
1485
- blob_append(&desc, zIntro, -1);
1486
- blob_append(&desc, tagQuote(i, zTag), -1);
1487
- }else{
1488
- if( zPattern ){
1489
- /* Third and subsequent tags: append comma then previous tag. */
1490
- blob_append(&desc, ", ", 2);
1491
- blob_append(&desc, zPattern, -1);
1492
- zOr = ", or ";
1493
- }
1494
-
1495
- /* Second and subsequent tags: store quoted tag for next iteration. */
1496
- zPattern = tagQuote(i, zTag);
1497
- }
1498
- }else{
1499
- /* On error, skip the match word and build up the error message buffer. */
1500
- if( !blob_size(&err) ){
1501
- blob_append(&err, "Error: ", 7);
1502
- }else{
1503
- blob_append(&err, ", ", 2);
1504
- }
1505
- blob_appendf(&err, "(%s%s: %s)", zIntro, tagQuote(i, zTag), zFail);
1506
- }
1507
-
1508
- /* Advance past all consumed input characters. */
1509
- zTag += i;
1510
- if( cDel!=',' && *zTag==cDel ){
1511
- ++zTag;
1512
- }
1513
- }
1514
-
1515
- /* Finalize and extract the pattern description. */
1516
- if( zPattern ){
1517
- blob_append(&desc, zOr, -1);
1518
- blob_append(&desc, zPattern, -1);
1519
- }
1520
- *zDesc = blob_str(&desc);
1521
-
1522
- /* Finalize and extract the error text. */
1523
- *zError = blob_size(&err) ? blob_str(&err) : 0;
1524
-
1525
- /* Finalize and extract the SQL expression. */
1526
- if( blob_size(&expr) ){
1527
- blob_append(&expr, zEnd, -1);
1528
- return blob_str(&expr);
1529
- }
1530
-
1531
- /* If execution reaches this point, the pattern was empty. Return NULL. */
1532
- return 0;
1533
-}
1534
-
15351309
/*
15361310
** Similar to fossil_expand_datetime()
15371311
**
15381312
** Add missing "-" characters into a date/time. Examples:
15391313
**
@@ -1591,10 +1365,11 @@
15911365
tagId = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", zEnd);
15921366
if( tagId==0 ){
15931367
endId = symbolic_name_to_rid(zEnd, "ci");
15941368
if( endId==0 ) return 0;
15951369
}
1370
+ db_pause_dml_log();
15961371
if( bForward ){
15971372
if( tagId ){
15981373
db_prepare(&q,
15991374
"WITH RECURSIVE dx(id,mtime) AS ("
16001375
" SELECT %d, event.mtime FROM event WHERE objid=%d"
@@ -1658,12 +1433,54 @@
16581433
}
16591434
if( db_step(&q)==SQLITE_ROW ){
16601435
ans = db_column_int(&q, 0);
16611436
}
16621437
db_finalize(&q);
1438
+ db_unpause_dml_log();
16631439
return ans;
16641440
}
1441
+
1442
+/*
1443
+** Add to the (temp) table zTab, RID values for every check-in
1444
+** identifier found on the zExtra string. Check-in names can be separated
1445
+** by commas or by whitespace.
1446
+*/
1447
+static void add_extra_rids(const char *zTab, const char *zExtra){
1448
+ int ii;
1449
+ int rid;
1450
+ int cnt;
1451
+ Blob sql;
1452
+ char *zX;
1453
+ char *zToDel;
1454
+ if( zExtra==0 ) return;
1455
+ cnt = 0;
1456
+ blob_init(&sql, 0, 0);
1457
+ zX = zToDel = fossil_strdup(zExtra);
1458
+ blob_append_sql(&sql, "INSERT OR IGNORE INTO \"%w\" VALUES", zTab);
1459
+ while( zX[0] ){
1460
+ char c;
1461
+ if( zX[0]==',' || zX[0]==' ' ){ zX++; continue; }
1462
+ for(ii=1; zX[ii] && zX[ii]!=',' && zX[ii]!=' '; ii++){}
1463
+ c = zX[ii];
1464
+ zX[ii] = 0;
1465
+ rid = name_to_rid(zX);
1466
+ if( rid>0 ){
1467
+ if( (cnt%10)==4 ){
1468
+ blob_append_sql(&sql,",\n ");
1469
+ }else if( cnt>0 ){
1470
+ blob_append_sql(&sql,",");
1471
+ }
1472
+ blob_append_sql(&sql, "(%d)", rid);
1473
+ cnt++;
1474
+ }
1475
+ zX[ii] = c;
1476
+ zX += ii;
1477
+ }
1478
+ if( cnt ) db_exec_sql(blob_sql_text(&sql));
1479
+ blob_reset(&sql);
1480
+ fossil_free(zToDel);
1481
+}
16651482
16661483
/*
16671484
** COMMAND: test-endpoint
16681485
**
16691486
** Usage: fossil test-endpoint BASE TAG ?OPTIONS?
@@ -1698,21 +1515,21 @@
16981515
/*
16991516
** WEBPAGE: timeline
17001517
**
17011518
** Query parameters:
17021519
**
1703
-** a=TIMEORTAG Show events after TIMEORTAG
1704
-** b=TIMEORTAG Show events before TIMEORTAG
1520
+** a=TIMEORTAG Show events after TIMEORTAG.
1521
+** b=TIMEORTAG Show events before TIMEORTAG.
17051522
** c=TIMEORTAG Show events that happen "circa" TIMEORTAG
17061523
** cf=FILEHASH Show events around the time of the first use of
1707
-** the file with FILEHASH
1524
+** the file with FILEHASH.
17081525
** m=TIMEORTAG Highlight the event at TIMEORTAG, or the closest available
17091526
** event if TIMEORTAG is not part of the timeline. If
17101527
** the t= or r= is used, the m event is added to the timeline
17111528
** if it isn't there already.
1712
-** x=HASHLIST Show all check-ins in the comma-separated HASHLIST
1713
-** in addition to check-ins specified by t= or r=
1529
+** x=LIST Show check-ins in the comma- or space-separated LIST
1530
+** in addition to check-ins specified by other parameters.
17141531
** sel1=TIMEORTAG Highlight the check-in at TIMEORTAG if it is part of
17151532
** the timeline. Similar to m= except TIMEORTAG must
17161533
** match a check-in that is already in the timeline.
17171534
** sel2=TIMEORTAG Like sel1= but use the secondary highlight.
17181535
** n=COUNT Maximum number of events. "all" for no limit
@@ -1732,42 +1549,48 @@
17321549
** from=CX ... shortest path from CX back to CHECKIN
17331550
** ft=CHECKIN "Forward To": Show decendents forward to CHECKIN
17341551
** d=CX ... from CX up to the time of CHECKIN
17351552
** from=CX ... shortest path from CX up to CHECKIN
17361553
** t=TAG Show only check-ins with the given TAG
1737
-** r=TAG Show check-ins related to TAG, equivalent to t=TAG&rel
1738
-** tl=TAGLIST Shorthand for t=TAGLIST&ms=brlist
1739
-** rl=TAGLIST Shorthand for r=TAGLIST&ms=brlist
1554
+** r=TAG Same as 't=TAG&rel'. Mnemonic: "Related"
1555
+** tl=TAGLIST Same as 't=TAGLIST&ms=brlist'. Mnemonic: "Tag List"
1556
+** rl=TAGLIST Same as 'r=TAGLIST&ms=brlist'. Mnemonic: "Related List"
1557
+** ml=TAGLIST Same as 'tl=TAGLIST&mionly'. Mnemonic: "Merge-in List"
1558
+** sl=TAGLIST "Sort List". Draw TAGLIST branches ordered left to right.
17401559
** rel Show related check-ins as well as those matching t=TAG
1741
-** mionly Limit rel to show ancestors but not descendants
1560
+** mionly Show related parents but not related children.
17421561
** nowiki Do not show wiki associated with branch or tag
1743
-** ms=MATCHSTYLE Set tag match style to EXACT, GLOB, LIKE, REGEXP
1562
+** ms=MATCHSTYLE Set tag name match algorithm. One of "exact", "glob",
1563
+** "like", or "regexp".
17441564
** u=USER Only show items associated with USER
17451565
** y=TYPE 'ci', 'w', 't', 'n', 'e', 'f', or 'all'.
17461566
** ss=VIEWSTYLE c: "Compact", v: "Verbose", m: "Modern", j: "Columnar",
1747
-** x: "Classic".
1567
+* x: "Classic".
17481568
** advm Use the "Advanced" or "Busy" menu design.
17491569
** ng No Graph.
17501570
** ncp Omit cherrypick merges
17511571
** nd Do not highlight the focus check-in
17521572
** nsm Omit the submenu
17531573
** nc Omit all graph colors other than highlights
17541574
** v Show details of files changed
17551575
** vfx Show complete text of forum messages
1756
-** f=CHECKIN Show family (immediate parents and children) of CHECKIN
1757
-** from=CHECKIN Path from...
1576
+** f=CHECKIN Family (immediate parents and children) of CHECKIN
1577
+** from=CHECKIN Path through common ancestor from...
17581578
** to=CHECKIN ... to this
17591579
** to2=CHECKIN ... backup name if to= doesn't resolve
17601580
** shortest ... show only the shortest path
17611581
** rel ... also show related checkins
17621582
** bt=PRIOR ... path from CHECKIN back to PRIOR
17631583
** ft=LATER ... path from CHECKIN forward to LATER
1584
+** me=CHECKIN Most direct path from...
1585
+** you=CHECKIN ... to this
1586
+** rel ... also show related checkins
17641587
** uf=FILE_HASH Show only check-ins that contain the given file version
1765
-** All qualifying check-ins are shown unless there is
1766
-** also an n= or n1= query parameter.
1588
+** All qualifying check-ins are shown unless there is
1589
+** also an n= or n1= query parameter.
17671590
** chng=GLOBLIST Show only check-ins that involve changes to a file whose
1768
-** name matches one of the comma-separate GLOBLIST
1591
+** name matches one of the comma-separate GLOBLIST
17691592
** brbg Background color determined by branch name
17701593
** ubg Background color determined by user
17711594
** deltabg Background color red for delta manifests or green
17721595
** for baseline manifests
17731596
** namechng Show only check-ins that have filename changes
@@ -1784,11 +1607,11 @@
17841607
** datefmt=N Override the date format: 0=HH:MM, 1=HH:MM:SS,
17851608
** 2=YYYY-MM-DD HH:MM:SS, 3=YYMMDD HH:MM, and 4 means "off".
17861609
** bisect Show the check-ins that are in the current bisect
17871610
** oldestfirst Show events oldest first.
17881611
** showid Show RIDs
1789
-** showsql Show the SQL text
1612
+** showsql Show the SQL used to generate the report
17901613
**
17911614
** p= and d= can appear individually or together. If either p= or d=
17921615
** appear, then u=, y=, a=, and b= are ignored.
17931616
**
17941617
** If both a= and b= appear then both upper and lower bounds are honored.
@@ -1862,14 +1685,20 @@
18621685
int advancedMenu = 0; /* Use the advanced menu design */
18631686
char *zPlural; /* Ending for plural forms */
18641687
int showCherrypicks = 1; /* True to show cherrypick merges */
18651688
int haveParameterN; /* True if n= query parameter present */
18661689
int from_to_mode = 0; /* 0: from,to. 1: from,ft 2: from,bt */
1690
+ int showSql = PB("showsql"); /* True to show the SQL */
1691
+ Blob allSql; /* Copy of all SQL text */
18671692
18681693
login_check_credentials();
18691694
url_initialize(&url, "timeline");
18701695
cgi_query_parameters_to_url(&url);
1696
+ blob_init(&allSql, 0, 0);
1697
+
1698
+ /* The "mionly" query parameter is like "rel", but shows merge-ins only */
1699
+ if( P("mionly")!=0 ) related = 2;
18711700
18721701
(void)P_NoBot("ss")
18731702
/* "ss" is processed via the udc but at least one spider likes to
18741703
** try to SQL inject via this argument, so let's catch that. */;
18751704
@@ -1984,49 +1813,52 @@
19841813
19851814
/* Check for tl=TAGLIST and rl=TAGLIST which are abbreviations for
19861815
** t=TAGLIST&ms=brlist and r=TAGLIST&ms=brlist repectively. */
19871816
if( zBrName==0 && zTagName==0 ){
19881817
const char *z;
1818
+ const char *zPattern = 0;
19891819
if( (z = P("tl"))!=0 ){
1990
- zTagName = z;
1991
- zMatchStyle = "brlist";
1820
+ zPattern = zTagName = z;
1821
+ }else if( (z = P("rl"))!=0 ){
1822
+ zPattern = zBrName = z;
1823
+ if( related==0 ) related = 1;
1824
+ }else if( (z = P("ml"))!=0 ){
1825
+ zPattern = zBrName = z;
1826
+ if( related==0 ) related = 2;
19921827
}
1993
- if( (z = P("rl"))!=0 ){
1994
- zBrName = z;
1995
- related = 1;
1996
- zMatchStyle = "brlist";
1828
+ if( zPattern!=0 && zMatchStyle==0 ){
1829
+ /* If there was no ms= query parameter, set the match style to
1830
+ ** "glob" if the pattern appears to contain GLOB character, or
1831
+ ** "brlist" if it does not. */
1832
+ if( strpbrk(zPattern,"*[?") ){
1833
+ zMatchStyle = "glob";
1834
+ }else{
1835
+ zMatchStyle = "brlist";
1836
+ }
19971837
}
19981838
}
19991839
20001840
/* Convert r=TAG to t=TAG&rel in order to populate the UI style widgets. */
2001
- if( zBrName && !related ){
1841
+ if( zBrName ){
20021842
cgi_delete_query_parameter("r");
20031843
cgi_set_query_parameter("t", zBrName); (void)P("t");
20041844
cgi_set_query_parameter("rel", "1");
20051845
zTagName = zBrName;
2006
- related = 1;
1846
+ if( related==0 ) related = 1;
20071847
zType = "ci";
20081848
}
20091849
20101850
/* Ignore empty tag query strings. */
20111851
if( zTagName && !*zTagName ){
20121852
zTagName = 0;
20131853
}
20141854
20151855
/* Finish preliminary processing of tag match queries. */
1856
+ matchStyle = match_style(zMatchStyle, MS_EXACT);
20161857
if( zTagName ){
20171858
zType = "ci";
2018
- /* Interpet the tag style string. */
2019
- if( fossil_stricmp(zMatchStyle, "glob")==0 ){
2020
- matchStyle = MS_GLOB;
2021
- }else if( fossil_stricmp(zMatchStyle, "like")==0 ){
2022
- matchStyle = MS_LIKE;
2023
- }else if( fossil_stricmp(zMatchStyle, "regexp")==0 ){
2024
- matchStyle = MS_REGEXP;
2025
- }else if( fossil_stricmp(zMatchStyle, "brlist")==0 ){
2026
- matchStyle = MS_BRLIST;
2027
- }else{
1859
+ if( matchStyle==MS_EXACT ){
20281860
/* For exact maching, inhibit links to the selected tag. */
20291861
zThisTag = zTagName;
20301862
Th_Store("current_checkin", zTagName);
20311863
}
20321864
@@ -2034,11 +1866,11 @@
20341866
if( advancedMenu ){
20351867
style_submenu_checkbox("rel", "Related", 0, 0);
20361868
}
20371869
20381870
/* Construct the tag match expression. */
2039
- zTagSql = tagMatchExpression(matchStyle, zTagName, &zMatchDesc, &zError);
1871
+ zTagSql = match_tag_sqlexpr(matchStyle, zTagName, &zMatchDesc, &zError);
20401872
}
20411873
20421874
if( zMark && zMark[0]==0 ){
20431875
if( zAfter ) zMark = zAfter;
20441876
if( zBefore ) zMark = zBefore;
@@ -2082,10 +1914,11 @@
20821914
}
20831915
if( PB("nc") ){
20841916
tmFlags &= ~(TIMELINE_DELTA|TIMELINE_BRCOLOR|TIMELINE_UCOLOR);
20851917
tmFlags |= TIMELINE_NOCOLOR;
20861918
}
1919
+ if( showSql ) db_append_dml_to_blob(&allSql);
20871920
if( zUses!=0 ){
20881921
int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses);
20891922
if( ufid ){
20901923
zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid);
20911924
db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)");
@@ -2099,42 +1932,42 @@
20991932
}
21001933
if( renameOnly ){
21011934
db_multi_exec(
21021935
"CREATE TEMP TABLE rnfile(rid INTEGER PRIMARY KEY);"
21031936
"INSERT OR IGNORE INTO rnfile"
2104
- " SELECT mid FROM mlink WHERE pfnid>0 AND pfnid!=fnid;"
1937
+ " SELECT mid FROM mlink WHERE pfnid>0 AND pfnid!=fnid;"
21051938
);
21061939
disableY = 1;
21071940
}
21081941
if( forkOnly ){
21091942
db_multi_exec(
21101943
"CREATE TEMP TABLE rnfork(rid INTEGER PRIMARY KEY);\n"
21111944
"INSERT OR IGNORE INTO rnfork(rid)\n"
21121945
" SELECT pid FROM plink\n"
2113
- " WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)=="
2114
- " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
2115
- " GROUP BY pid"
1946
+ " WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==\n"
1947
+ " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
1948
+ " GROUP BY pid\n"
21161949
" HAVING count(*)>1;\n"
2117
- "INSERT OR IGNORE INTO rnfork(rid)"
1950
+ "INSERT OR IGNORE INTO rnfork(rid)\n"
21181951
" SELECT cid FROM plink\n"
2119
- " WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)=="
2120
- " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
2121
- " GROUP BY cid"
1952
+ " WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==\n"
1953
+ " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
1954
+ " GROUP BY cid\n"
21221955
" HAVING count(*)>1;\n",
21231956
TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH
21241957
);
21251958
db_multi_exec(
21261959
"INSERT OR IGNORE INTO rnfork(rid)\n"
21271960
" SELECT cid FROM plink\n"
2128
- " WHERE pid IN rnfork"
2129
- " AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)=="
2130
- " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
2131
- " UNION "
1961
+ " WHERE pid IN rnfork\n"
1962
+ " AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==\n"
1963
+ " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
1964
+ " UNION\n"
21321965
" SELECT pid FROM plink\n"
2133
- " WHERE cid IN rnfork"
2134
- " AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)=="
2135
- " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n",
1966
+ " WHERE cid IN rnfork\n"
1967
+ " AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==\n"
1968
+ " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n",
21361969
TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH
21371970
);
21381971
tmFlags |= TIMELINE_UNHIDE;
21391972
zType = "ci";
21401973
disableY = 1;
@@ -2207,77 +2040,114 @@
22072040
PathNode *p = 0;
22082041
const char *zFrom = 0;
22092042
const char *zTo = 0;
22102043
Blob ins;
22112044
int nNodeOnPath = 0;
2045
+ int commonAncs = 0; /* Common ancestors of me_rid and you_rid. */
2046
+ int earlierRid = 0, laterRid = 0;
22122047
22132048
if( from_rid && to_rid ){
22142049
if( from_to_mode==0 ){
22152050
p = path_shortest(from_rid, to_rid, noMerge, 0, 0);
22162051
}else if( from_to_mode==1 ){
22172052
p = path_shortest(from_rid, to_rid, 0, 1, 0);
2053
+ earlierRid = commonAncs = from_rid;
2054
+ laterRid = to_rid;
22182055
}else{
22192056
p = path_shortest(to_rid, from_rid, 0, 1, 0);
2057
+ earlierRid = commonAncs = to_rid;
2058
+ laterRid = from_rid;
22202059
}
22212060
zFrom = P("from");
22222061
zTo = zTo2 ? zTo2 : P("to");
22232062
}else{
2224
- if( path_common_ancestor(me_rid, you_rid) ){
2063
+ commonAncs = path_common_ancestor(me_rid, you_rid);
2064
+ if( commonAncs!=0 ){
22252065
p = path_first();
22262066
}
2227
- zFrom = P("me");
2228
- zTo = P("you");
2067
+ if( commonAncs==you_rid ){
2068
+ zFrom = P("you");
2069
+ zTo = P("me");
2070
+ earlierRid = you_rid;
2071
+ laterRid = me_rid;
2072
+ }else{
2073
+ zFrom = P("me");
2074
+ zTo = P("you");
2075
+ earlierRid = me_rid;
2076
+ laterRid = you_rid;
2077
+ }
22292078
}
22302079
blob_init(&ins, 0, 0);
22312080
db_multi_exec(
2232
- "CREATE TABLE IF NOT EXISTS temp.pathnode(x INTEGER PRIMARY KEY);"
2081
+ "CREATE TEMP TABLE IF NOT EXISTS pathnode(x INTEGER PRIMARY KEY);"
22332082
);
22342083
if( p ){
2084
+ int cnt = 4;
22352085
blob_init(&ins, 0, 0);
22362086
blob_append_sql(&ins, "INSERT INTO pathnode(x) VALUES(%d)", p->rid);
22372087
p = p->u.pTo;
22382088
while( p ){
2239
- blob_append_sql(&ins, ",(%d)", p->rid);
2089
+ if( cnt==8 ){
2090
+ blob_append_sql(&ins, ",\n (%d)", p->rid);
2091
+ cnt = 0;
2092
+ }else{
2093
+ cnt++;
2094
+ blob_append_sql(&ins, ",(%d)", p->rid);
2095
+ }
22402096
p = p->u.pTo;
22412097
}
22422098
}
22432099
path_reset();
22442100
db_multi_exec("%s", blob_str(&ins)/*safe-for-%s*/);
22452101
blob_reset(&ins);
2246
- if( related || P("mionly") ){
2102
+ if( related ){
22472103
db_multi_exec(
2248
- "CREATE TABLE IF NOT EXISTS temp.related(x INTEGER PRIMARY KEY);"
2104
+ "CREATE TEMP TABLE IF NOT EXISTS related(x INTEGER PRIMARY KEY);"
22492105
"INSERT OR IGNORE INTO related(x)"
2250
- " SELECT pid FROM plink WHERE cid IN pathnode AND NOT isprim;"
2106
+ " SELECT pid FROM plink WHERE cid IN pathnode AND NOT isprim;"
22512107
);
2252
- if( P("mionly")==0 ){
2108
+ if( related==1 ){
22532109
db_multi_exec(
22542110
"INSERT OR IGNORE INTO related(x)"
2255
- " SELECT cid FROM plink WHERE pid IN pathnode;"
2111
+ " SELECT cid FROM plink WHERE pid IN pathnode;"
22562112
);
22572113
}
22582114
if( showCherrypicks ){
22592115
db_multi_exec(
22602116
"INSERT OR IGNORE INTO related(x)"
2261
- " SELECT parentid FROM cherrypick WHERE childid IN pathnode;"
2117
+ " SELECT parentid FROM cherrypick WHERE childid IN pathnode;"
22622118
);
2263
- if( P("mionly")==0 ){
2119
+ if( related==1 ){
22642120
db_multi_exec(
22652121
"INSERT OR IGNORE INTO related(x)"
2266
- " SELECT childid FROM cherrypick WHERE parentid IN pathnode;"
2122
+ " SELECT childid FROM cherrypick WHERE parentid IN pathnode;"
22672123
);
22682124
}
2125
+ }
2126
+ if( earlierRid && laterRid && commonAncs==earlierRid ){
2127
+ /* On a query with me=XXX, you=YYY, and rel, omit all nodes that
2128
+ ** are not ancestors of either XXX or YYY, as those nodes tend to
2129
+ ** be extraneous */
2130
+ db_multi_exec(
2131
+ "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
2132
+ );
2133
+ compute_ancestors(laterRid, 0, 0, earlierRid);
2134
+ db_multi_exec(
2135
+ "DELETE FROM related WHERE x NOT IN ok;"
2136
+ );
22692137
}
22702138
db_multi_exec("INSERT OR IGNORE INTO pathnode SELECT x FROM related");
22712139
}
2140
+ add_extra_rids("pathnode",P("x"));
22722141
blob_append_sql(&sql, " AND event.objid IN pathnode");
22732142
if( zChng && zChng[0] ){
22742143
db_multi_exec(
2275
- "DELETE FROM pathnode "
2276
- " WHERE NOT EXISTS(SELECT 1 FROM mlink, filename"
2277
- " WHERE mlink.mid=x"
2278
- " AND mlink.fnid=filename.fnid AND %s)",
2144
+ "DELETE FROM pathnode\n"
2145
+ " WHERE NOT EXISTS(SELECT 1 FROM mlink, filename\n"
2146
+ " WHERE mlink.mid=x\n"
2147
+ " AND mlink.fnid=filename.fnid\n"
2148
+ " AND %s)",
22792149
glob_expr("filename.name", zChng)
22802150
);
22812151
}
22822152
tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
22832153
db_multi_exec("%s", blob_sql_text(&sql));
@@ -2331,18 +2201,18 @@
23312201
if( !haveParameterN ) nEntry = 10;
23322202
}
23332203
db_multi_exec(
23342204
"CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
23352205
);
2206
+ add_extra_rids("ok", P("x"));
23362207
zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d",
23372208
p_rid ? p_rid : d_rid);
23382209
zCiName = zDPName;
23392210
if( zCiName==0 ) zCiName = zUuid;
23402211
blob_append_sql(&sql, " AND event.objid IN ok");
23412212
nd = 0;
23422213
if( d_rid ){
2343
- Stmt s;
23442214
double rStopTime = 9e99;
23452215
zFwdTo = P("ft");
23462216
if( zFwdTo ){
23472217
double rStartDate = db_double(0.0,
23482218
"SELECT mtime FROM event WHERE objid=%d", d_rid);
@@ -2354,27 +2224,25 @@
23542224
if( !haveParameterN ) nEntry = 0;
23552225
rStopTime = db_double(9e99,
23562226
"SELECT mtime FROM event WHERE objid=%d", ridFwdTo);
23572227
}
23582228
}
2359
- db_prepare(&s,
2360
- "WITH RECURSIVE"
2361
- " dx(rid,mtime) AS ("
2362
- " SELECT %d, 0"
2363
- " UNION"
2364
- " SELECT plink.cid, plink.mtime FROM dx, plink"
2365
- " WHERE plink.pid=dx.rid"
2366
- " AND (:stop>=8e99 OR plink.mtime<=:stop)"
2367
- " ORDER BY 2"
2368
- " )"
2229
+ if( rStopTime<9e99 ){
2230
+ rStopTime += 5.8e-6; /* Round up by 1/2 second */
2231
+ }
2232
+ db_multi_exec(
2233
+ "WITH RECURSIVE dx(rid,mtime) AS (\n"
2234
+ " SELECT %d, 0\n"
2235
+ " UNION\n"
2236
+ " SELECT plink.cid, plink.mtime FROM dx, plink\n"
2237
+ " WHERE plink.pid=dx.rid\n"
2238
+ " AND plink.mtime<=%.*g\n"
2239
+ " ORDER BY 2\n"
2240
+ ")\n"
23692241
"INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d",
2370
- d_rid, nEntry<=0 ? -1 : nEntry+1
2242
+ d_rid, rStopTime<8e99 ? 17 : 2, rStopTime, nEntry<=0 ? -1 : nEntry+1
23712243
);
2372
- db_bind_double(&s, ":stop", rStopTime);
2373
- db_step(&s);
2374
- db_finalize(&s);
2375
- /* compute_descendants(d_rid, nEntry==0 ? 0 : nEntry+1); */
23762244
nd = db_int(0, "SELECT count(*)-1 FROM ok");
23772245
if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql));
23782246
if( nd>0 || p_rid==0 ){
23792247
blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
23802248
}
@@ -2483,28 +2351,28 @@
24832351
if( zChng && *zChng ){
24842352
addFileGlobExclusion(zChng, &cond);
24852353
tmFlags |= TIMELINE_XMERGE;
24862354
}
24872355
if( zUses ){
2488
- blob_append_sql(&cond, " AND event.objid IN usesfile ");
2356
+ blob_append_sql(&cond, " AND event.objid IN usesfile\n");
24892357
}
24902358
if( renameOnly ){
2491
- blob_append_sql(&cond, " AND event.objid IN rnfile ");
2359
+ blob_append_sql(&cond, " AND event.objid IN rnfile\n");
24922360
}
24932361
if( forkOnly ){
2494
- blob_append_sql(&cond, " AND event.objid IN rnfork ");
2362
+ blob_append_sql(&cond, " AND event.objid IN rnfork\n");
24952363
}
24962364
if( cpOnly && showCherrypicks ){
24972365
db_multi_exec(
24982366
"CREATE TEMP TABLE IF NOT EXISTS cpnodes(rid INTEGER PRIMARY KEY);"
24992367
"INSERT OR IGNORE INTO cpnodes SELECT childid FROM cherrypick;"
25002368
"INSERT OR IGNORE INTO cpnodes SELECT parentid FROM cherrypick;"
25012369
);
2502
- blob_append_sql(&cond, " AND event.objid IN cpnodes ");
2370
+ blob_append_sql(&cond, " AND event.objid IN cpnodes\n");
25032371
}
25042372
if( bisectLocal || zBisect!=0 ){
2505
- blob_append_sql(&cond, " AND event.objid IN (SELECT rid FROM bilog) ");
2373
+ blob_append_sql(&cond, " AND event.objid IN (SELECT rid FROM bilog)\n");
25062374
}
25072375
if( zYearMonth ){
25082376
char *zNext;
25092377
zYearMonth = timeline_expand_datetime(zYearMonth);
25102378
if( strlen(zYearMonth)>7 ){
@@ -2663,39 +2531,24 @@
26632531
nEntry = -1;
26642532
}
26652533
if( zTagSql ){
26662534
db_multi_exec(
26672535
"CREATE TEMP TABLE selected_nodes(rid INTEGER PRIMARY KEY);"
2668
- "INSERT OR IGNORE INTO selected_nodes"
2669
- " SELECT tagxref.rid FROM tagxref NATURAL JOIN tag"
2670
- " WHERE %s AND tagtype>0", zTagSql/*safe-for-%s*/
2536
+ "INSERT OR IGNORE INTO selected_nodes\n"
2537
+ " SELECT tagxref.rid FROM tagxref NATURAL JOIN tag\n"
2538
+ " WHERE tagtype>0\n"
2539
+ " AND %s", zTagSql/*safe-for-%s*/
26712540
);
26722541
if( zMark ){
26732542
/* If the t=release option is used with m=UUID, then also
26742543
** include the UUID check-in in the display list */
26752544
int ridMark = name_to_rid(zMark);
26762545
db_multi_exec(
26772546
"INSERT OR IGNORE INTO selected_nodes(rid) VALUES(%d)", ridMark);
26782547
}
2679
- if( P("x")!=0 ){
2680
- char *zX = fossil_strdup(P("x"));
2681
- int ii;
2682
- int ridX;
2683
- while( zX[0] ){
2684
- char c;
2685
- if( zX[0]==',' || zX[0]==' ' ){ zX++; continue; }
2686
- for(ii=1; zX[ii] && zX[ii]!=',' && zX[ii]!=' '; ii++){}
2687
- c = zX[ii];
2688
- zX[ii] = 0;
2689
- ridX = name_to_rid(zX);
2690
- db_multi_exec(
2691
- "INSERT OR IGNORE INTO selected_nodes(rid) VALUES(%d)", ridX);
2692
- zX[ii] = c;
2693
- zX += ii;
2694
- }
2695
- }
2696
- if( !related ){
2548
+ add_extra_rids("selected_nodes",P("x"));
2549
+ if( related==0 ){
26972550
blob_append_sql(&cond, " AND blob.rid IN selected_nodes");
26982551
}else{
26992552
db_multi_exec(
27002553
"CREATE TEMP TABLE related_nodes(rid INTEGER PRIMARY KEY);"
27012554
"INSERT INTO related_nodes SELECT rid FROM selected_nodes;"
@@ -2706,40 +2559,42 @@
27062559
** branch to be included in the report. These related check-ins are
27072560
** useful in helping to visualize what has happened on a quiescent
27082561
** branch that is infrequently merged with a much more activate branch.
27092562
*/
27102563
db_multi_exec(
2711
- "INSERT OR IGNORE INTO related_nodes"
2712
- " SELECT pid FROM selected_nodes CROSS JOIN plink"
2713
- " WHERE selected_nodes.rid=plink.cid;"
2564
+ "INSERT OR IGNORE INTO related_nodes\n"
2565
+ " SELECT pid FROM selected_nodes CROSS JOIN plink\n"
2566
+ " WHERE selected_nodes.rid=plink.cid;"
27142567
);
2715
- if( P("mionly")==0 ){
2568
+ if( related==1 ){
27162569
db_multi_exec(
2717
- "INSERT OR IGNORE INTO related_nodes"
2718
- " SELECT cid FROM selected_nodes CROSS JOIN plink"
2719
- " WHERE selected_nodes.rid=plink.pid;"
2570
+ "INSERT OR IGNORE INTO related_nodes\n"
2571
+ " SELECT cid FROM selected_nodes CROSS JOIN plink\n"
2572
+ " WHERE selected_nodes.rid=plink.pid;"
27202573
);
27212574
if( showCherrypicks ){
27222575
db_multi_exec(
2723
- "INSERT OR IGNORE INTO related_nodes"
2724
- " SELECT childid FROM selected_nodes CROSS JOIN cherrypick"
2725
- " WHERE selected_nodes.rid=cherrypick.parentid;"
2576
+ "INSERT OR IGNORE INTO related_nodes\n"
2577
+ " SELECT childid FROM selected_nodes CROSS JOIN cherrypick\n"
2578
+ " WHERE selected_nodes.rid=cherrypick.parentid;"
27262579
);
27272580
}
27282581
}
27292582
if( showCherrypicks ){
27302583
db_multi_exec(
2731
- "INSERT OR IGNORE INTO related_nodes"
2732
- " SELECT parentid FROM selected_nodes CROSS JOIN cherrypick"
2733
- " WHERE selected_nodes.rid=cherrypick.childid;"
2584
+ "INSERT OR IGNORE INTO related_nodes\n"
2585
+ " SELECT parentid FROM selected_nodes CROSS JOIN cherrypick\n"
2586
+ " WHERE selected_nodes.rid=cherrypick.childid;"
27342587
);
27352588
}
27362589
if( (tmFlags & TIMELINE_UNHIDE)==0 ){
27372590
db_multi_exec(
2738
- "DELETE FROM related_nodes WHERE rid IN "
2739
- " (SELECT related_nodes.rid FROM related_nodes, tagxref"
2740
- " WHERE tagid=%d AND tagtype>0 AND tagxref.rid=related_nodes.rid)",
2591
+ "DELETE FROM related_nodes\n"
2592
+ " WHERE rid IN (SELECT related_nodes.rid\n"
2593
+ " FROM related_nodes, tagxref\n"
2594
+ " WHERE tagid=%d AND tagtype>0\n"
2595
+ " AND tagxref.rid=related_nodes.rid)",
27412596
TAG_HIDDEN
27422597
);
27432598
}
27442599
}
27452600
}
@@ -2829,45 +2684,42 @@
28292684
rCirca = symbolic_name_to_mtime(zCirca, &zCirca);
28302685
blob_append_sql(&sql, "%s", blob_sql_text(&cond));
28312686
if( rAfter>0.0 ){
28322687
if( rBefore>0.0 ){
28332688
blob_append_sql(&sql,
2834
- " AND event.mtime>=%.17g AND event.mtime<=%.17g"
2689
+ " AND event.mtime>=%.17g AND event.mtime<=%.17g\n"
28352690
" ORDER BY event.mtime ASC", rAfter-ONE_SECOND, rBefore+ONE_SECOND);
28362691
nEntry = -1;
28372692
}else{
28382693
blob_append_sql(&sql,
2839
- " AND event.mtime>=%.17g ORDER BY event.mtime ASC",
2694
+ " AND event.mtime>=%.17g\n ORDER BY event.mtime ASC",
28402695
rAfter-ONE_SECOND);
28412696
}
28422697
zCirca = 0;
28432698
url_add_parameter(&url, "c", 0);
28442699
}else if( rBefore>0.0 ){
28452700
blob_append_sql(&sql,
2846
- " AND event.mtime<=%.17g ORDER BY event.mtime DESC",
2701
+ " AND event.mtime<=%.17g\n ORDER BY event.mtime DESC",
28472702
rBefore+ONE_SECOND);
28482703
zCirca = 0;
28492704
url_add_parameter(&url, "c", 0);
28502705
}else if( rCirca>0.0 ){
28512706
Blob sql2;
28522707
blob_init(&sql2, blob_sql_text(&sql), -1);
28532708
blob_append_sql(&sql2,
2854
- " AND event.mtime>=%f ORDER BY event.mtime ASC", rCirca);
2709
+ " AND event.mtime>=%f\n ORDER BY event.mtime ASC", rCirca);
28552710
if( nEntry>0 ){
28562711
blob_append_sql(&sql2," LIMIT %d", (nEntry+1)/2);
28572712
}
2858
- if( PB("showsql") ){
2859
- @ <pre>%h(blob_sql_text(&sql2))</pre>
2860
- }
28612713
db_multi_exec("%s", blob_sql_text(&sql2));
28622714
if( nEntry>0 ){
28632715
nEntry -= db_int(0,"select count(*) from timeline");
28642716
if( nEntry<=0 ) nEntry = 1;
28652717
}
28662718
blob_reset(&sql2);
28672719
blob_append_sql(&sql,
2868
- " AND event.mtime<=%f ORDER BY event.mtime DESC",
2720
+ " AND event.mtime<=%f\n ORDER BY event.mtime DESC",
28692721
rCirca
28702722
);
28712723
if( zMark==0 ) zMark = zCirca;
28722724
}else{
28732725
blob_append_sql(&sql, " ORDER BY event.mtime DESC");
@@ -3010,12 +2862,14 @@
30102862
style_submenu_multichoice("ms", count(azMatchStyles)/2,azMatchStyles,0);
30112863
}
30122864
}
30132865
blob_zero(&cond);
30142866
}
3015
- if( PB("showsql") ){
3016
- @ <pre>%h(blob_sql_text(&sql))</pre>
2867
+ if( showSql ){
2868
+ db_append_dml_to_blob(0);
2869
+ @ <pre>%h(blob_str(&allSql))</pre>
2870
+ blob_reset(&allSql);
30172871
}
30182872
if( search_restrict(SRCH_CKIN)!=0 ){
30192873
style_submenu_element("Search", "%R/search?y=c");
30202874
}
30212875
if( advancedMenu ){
@@ -3069,18 +2923,36 @@
30692923
if( zNewerButton ){
30702924
@ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\
30712925
@ &nbsp;&uarr;</a>
30722926
}
30732927
cgi_check_for_malice();
3074
- www_print_timeline(&q, tmFlags, zThisUser, zThisTag, zBrName,
3075
- selectedRid, secondaryRid, 0);
2928
+ {
2929
+ Matcher *pLeftBranch;
2930
+ const char *zPattern = P("sl");
2931
+ if( zPattern!=0 ){
2932
+ MatchStyle ms;
2933
+ if( zMatchStyle!=0 ){
2934
+ ms = matchStyle;
2935
+ }else{
2936
+ ms = strpbrk(zPattern,"*[?")!=0 ? MS_GLOB : MS_BRLIST;
2937
+ }
2938
+ pLeftBranch = match_create(ms,zPattern);
2939
+ }else{
2940
+ pLeftBranch = match_create(matchStyle, zBrName?zBrName:zTagName);
2941
+ }
2942
+ www_print_timeline(&q, tmFlags, zThisUser, zThisTag, pLeftBranch,
2943
+ selectedRid, secondaryRid, 0);
2944
+ match_free(pLeftBranch);
2945
+ }
30762946
db_finalize(&q);
30772947
if( zOlderButton ){
30782948
@ %z(chref("button","%s",zOlderButton))%h(zOlderButtonLabel)\
30792949
@ &nbsp;&darr;</a>
30802950
}
30812951
document_emit_js(/*handles pikchrs rendered above*/);
2952
+ blob_reset(&sql);
2953
+ blob_reset(&desc);
30822954
style_finish_page();
30832955
}
30842956
30852957
/*
30862958
** Translate a timeline entry into the printable format by
30872959
--- src/timeline.c
+++ src/timeline.c
@@ -191,11 +191,11 @@
191 void www_print_timeline(
192 Stmt *pQuery, /* Query to implement the timeline */
193 int tmFlags, /* Flags controlling display behavior */
194 const char *zThisUser, /* Suppress links to this user */
195 const char *zThisTag, /* Suppress links to this tag */
196 const char *zLeftBranch, /* Strive to put this branch on the left margin */
197 int selectedRid, /* Highlight the line with this RID value or zero */
198 int secondRid, /* Secondary highlight (or zero) */
199 void (*xExtra)(int) /* Routine to call on each line of display */
200 ){
201 int mxWikiLen;
@@ -813,11 +813,11 @@
813 }
814 if( pendingEndTr ){
815 @ </td></tr>
816 }
817 if( pGraph ){
818 graph_finish(pGraph, zLeftBranch, tmFlags);
819 if( pGraph->nErr ){
820 graph_free(pGraph);
821 pGraph = 0;
822 }else{
823 @ <tr class="timelineBottom" id="btm-%d(iTableId)">\
@@ -1290,12 +1290,13 @@
1290 const char *zChng, /* The filename GLOB list */
1291 Blob *pSql /* The SELECT statement under construction */
1292 ){
1293 if( zChng==0 || zChng[0]==0 ) return;
1294 blob_append_sql(pSql," AND event.objid IN ("
1295 "SELECT mlink.mid FROM mlink, filename"
1296 " WHERE mlink.fnid=filename.fnid AND %s)",
 
1297 glob_expr("filename.name", mprintf("\"%s\"", zChng)));
1298 }
1299 static void addFileGlobDescription(
1300 const char *zChng, /* The filename GLOB list */
1301 Blob *pDescription /* Result description */
@@ -1303,237 +1304,10 @@
1303 if( zChng==0 || zChng[0]==0 ) return;
1304 blob_appendf(pDescription, " that include changes to files matching '%h'",
1305 zChng);
1306 }
1307
1308 /*
1309 ** Tag match expression type code.
1310 */
1311 typedef enum {
1312 MS_EXACT, /* Matches a single tag by exact string comparison. */
1313 MS_GLOB, /* Matches tags against a list of GLOB patterns. */
1314 MS_LIKE, /* Matches tags against a list of LIKE patterns. */
1315 MS_REGEXP, /* Matches tags against a list of regular expressions. */
1316 MS_BRLIST, /* Same as REGEXP, except the regular expression is a list
1317 ** of branch names */
1318 } MatchStyle;
1319
1320 /*
1321 ** Quote a tag string by surrounding it with double quotes and preceding
1322 ** internal double quotes and backslashes with backslashes.
1323 */
1324 static const char *tagQuote(
1325 int len, /* Maximum length of zTag, or negative for unlimited */
1326 const char *zTag /* Tag string */
1327 ){
1328 Blob blob = BLOB_INITIALIZER;
1329 int i, j;
1330 blob_zero(&blob);
1331 blob_append(&blob, "\"", 1);
1332 for( i=j=0; zTag[j] && (len<0 || j<len); ++j ){
1333 if( zTag[j]=='\"' || zTag[j]=='\\' ){
1334 if( j>i ){
1335 blob_append(&blob, zTag+i, j-i);
1336 }
1337 blob_append(&blob, "\\", 1);
1338 i = j;
1339 }
1340 }
1341 if( j>i ){
1342 blob_append(&blob, zTag+i, j-i);
1343 }
1344 blob_append(&blob, "\"", 1);
1345 return blob_str(&blob);
1346 }
1347
1348 /*
1349 ** Construct the tag match SQL expression.
1350 **
1351 ** This function is adapted from glob_expr() to support the MS_EXACT, MS_GLOB,
1352 ** MS_LIKE, MS_REGEXP, and MS_BRLIST match styles.
1353 **
1354 ** For MS_EXACT, the returned expression
1355 ** checks for integer match against the tag ID which is looked up directly by
1356 ** this function. For the other modes, the returned SQL expression performs
1357 ** string comparisons against the tag names, so it is necessary to join against
1358 ** the tag table to access the "tagname" column.
1359 **
1360 ** Each pattern is adjusted to to start with "sym-" and be anchored at end.
1361 **
1362 ** In MS_REGEXP mode, backslash can be used to protect delimiter characters.
1363 ** The backslashes are not removed from the regular expression.
1364 **
1365 ** In addition to assembling and returning an SQL expression, this function
1366 ** makes an English-language description of the patterns being matched, suitable
1367 ** for display in the web interface.
1368 **
1369 ** If any errors arise during processing, *zError is set to an error message.
1370 ** Otherwise it is set to NULL.
1371 */
1372 static const char *tagMatchExpression(
1373 MatchStyle matchStyle, /* Match style code */
1374 const char *zTag, /* Tag name, match pattern, or pattern list */
1375 const char **zDesc, /* Output expression description string */
1376 const char **zError /* Output error string */
1377 ){
1378 Blob expr = BLOB_INITIALIZER; /* SQL expression string assembly buffer */
1379 Blob desc = BLOB_INITIALIZER; /* English description of match patterns */
1380 Blob err = BLOB_INITIALIZER; /* Error text assembly buffer */
1381 const char *zStart; /* Text at start of expression */
1382 const char *zDelimiter; /* Text between expression terms */
1383 const char *zEnd; /* Text at end of expression */
1384 const char *zPrefix; /* Text before each match pattern */
1385 const char *zSuffix; /* Text after each match pattern */
1386 const char *zIntro; /* Text introducing pattern description */
1387 const char *zPattern = 0; /* Previous quoted pattern */
1388 const char *zFail = 0; /* Current failure message or NULL if okay */
1389 const char *zOr = " or "; /* Text before final quoted pattern */
1390 char cDel; /* Input delimiter character */
1391 int i; /* Input match pattern length counter */
1392
1393 /* Optimize exact matches by looking up the ID in advance to create a simple
1394 * numeric comparison. Bypass the remainder of this function. */
1395 if( matchStyle==MS_EXACT ){
1396 *zDesc = tagQuote(-1, zTag);
1397 return mprintf("(tagid=%d)", db_int(-1,
1398 "SELECT tagid FROM tag WHERE tagname='sym-%q'", zTag));
1399 }
1400
1401 /* Decide pattern prefix and suffix strings according to match style. */
1402 if( matchStyle==MS_GLOB ){
1403 zStart = "(";
1404 zDelimiter = " OR ";
1405 zEnd = ")";
1406 zPrefix = "tagname GLOB 'sym-";
1407 zSuffix = "'";
1408 zIntro = "glob pattern ";
1409 }else if( matchStyle==MS_LIKE ){
1410 zStart = "(";
1411 zDelimiter = " OR ";
1412 zEnd = ")";
1413 zPrefix = "tagname LIKE 'sym-";
1414 zSuffix = "'";
1415 zIntro = "SQL LIKE pattern ";
1416 }else if( matchStyle==MS_REGEXP ){
1417 zStart = "(tagname REGEXP '^sym-(";
1418 zDelimiter = "|";
1419 zEnd = ")$')";
1420 zPrefix = "";
1421 zSuffix = "";
1422 zIntro = "regular expression ";
1423 }else/* if( matchStyle==MS_BRLIST )*/{
1424 zStart = "tagname IN ('sym-";
1425 zDelimiter = "','sym-";
1426 zEnd = "')";
1427 zPrefix = "";
1428 zSuffix = "";
1429 zIntro = "";
1430 }
1431
1432 /* Convert the list of matches into an SQL expression and text description. */
1433 blob_zero(&expr);
1434 blob_zero(&desc);
1435 blob_zero(&err);
1436 while( 1 ){
1437 /* Skip leading delimiters. */
1438 for( ; fossil_isspace(*zTag) || *zTag==','; ++zTag );
1439
1440 /* Next non-delimiter character determines quoting. */
1441 if( !*zTag ){
1442 /* Terminate loop at end of string. */
1443 break;
1444 }else if( *zTag=='\'' || *zTag=='"' ){
1445 /* If word is quoted, prepare to stop at end quote. */
1446 cDel = *zTag;
1447 ++zTag;
1448 }else{
1449 /* If word is not quoted, prepare to stop at delimiter. */
1450 cDel = ',';
1451 }
1452
1453 /* Find the next delimiter character or end of string. */
1454 for( i=0; zTag[i] && zTag[i]!=cDel; ++i ){
1455 /* If delimiter is comma, also recognize spaces as delimiters. */
1456 if( cDel==',' && fossil_isspace(zTag[i]) ){
1457 break;
1458 }
1459
1460 /* In regexp mode, ignore delimiters following backslashes. */
1461 if( matchStyle==MS_REGEXP && zTag[i]=='\\' && zTag[i+1] ){
1462 ++i;
1463 }
1464 }
1465
1466 /* Check for regular expression syntax errors. */
1467 if( matchStyle==MS_REGEXP ){
1468 ReCompiled *regexp;
1469 char *zTagDup = fossil_strndup(zTag, i);
1470 zFail = re_compile(&regexp, zTagDup, 0);
1471 re_free(regexp);
1472 fossil_free(zTagDup);
1473 }
1474
1475 /* Process success and error results. */
1476 if( !zFail ){
1477 /* Incorporate the match word into the output expression. %q is used to
1478 * protect against SQL injection attacks by replacing ' with ''. */
1479 blob_appendf(&expr, "%s%s%#q%s", blob_size(&expr) ? zDelimiter : zStart,
1480 zPrefix, i, zTag, zSuffix);
1481
1482 /* Build up the description string. */
1483 if( !blob_size(&desc) ){
1484 /* First tag: start with intro followed by first quoted tag. */
1485 blob_append(&desc, zIntro, -1);
1486 blob_append(&desc, tagQuote(i, zTag), -1);
1487 }else{
1488 if( zPattern ){
1489 /* Third and subsequent tags: append comma then previous tag. */
1490 blob_append(&desc, ", ", 2);
1491 blob_append(&desc, zPattern, -1);
1492 zOr = ", or ";
1493 }
1494
1495 /* Second and subsequent tags: store quoted tag for next iteration. */
1496 zPattern = tagQuote(i, zTag);
1497 }
1498 }else{
1499 /* On error, skip the match word and build up the error message buffer. */
1500 if( !blob_size(&err) ){
1501 blob_append(&err, "Error: ", 7);
1502 }else{
1503 blob_append(&err, ", ", 2);
1504 }
1505 blob_appendf(&err, "(%s%s: %s)", zIntro, tagQuote(i, zTag), zFail);
1506 }
1507
1508 /* Advance past all consumed input characters. */
1509 zTag += i;
1510 if( cDel!=',' && *zTag==cDel ){
1511 ++zTag;
1512 }
1513 }
1514
1515 /* Finalize and extract the pattern description. */
1516 if( zPattern ){
1517 blob_append(&desc, zOr, -1);
1518 blob_append(&desc, zPattern, -1);
1519 }
1520 *zDesc = blob_str(&desc);
1521
1522 /* Finalize and extract the error text. */
1523 *zError = blob_size(&err) ? blob_str(&err) : 0;
1524
1525 /* Finalize and extract the SQL expression. */
1526 if( blob_size(&expr) ){
1527 blob_append(&expr, zEnd, -1);
1528 return blob_str(&expr);
1529 }
1530
1531 /* If execution reaches this point, the pattern was empty. Return NULL. */
1532 return 0;
1533 }
1534
1535 /*
1536 ** Similar to fossil_expand_datetime()
1537 **
1538 ** Add missing "-" characters into a date/time. Examples:
1539 **
@@ -1591,10 +1365,11 @@
1591 tagId = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", zEnd);
1592 if( tagId==0 ){
1593 endId = symbolic_name_to_rid(zEnd, "ci");
1594 if( endId==0 ) return 0;
1595 }
 
1596 if( bForward ){
1597 if( tagId ){
1598 db_prepare(&q,
1599 "WITH RECURSIVE dx(id,mtime) AS ("
1600 " SELECT %d, event.mtime FROM event WHERE objid=%d"
@@ -1658,12 +1433,54 @@
1658 }
1659 if( db_step(&q)==SQLITE_ROW ){
1660 ans = db_column_int(&q, 0);
1661 }
1662 db_finalize(&q);
 
1663 return ans;
1664 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1665
1666 /*
1667 ** COMMAND: test-endpoint
1668 **
1669 ** Usage: fossil test-endpoint BASE TAG ?OPTIONS?
@@ -1698,21 +1515,21 @@
1698 /*
1699 ** WEBPAGE: timeline
1700 **
1701 ** Query parameters:
1702 **
1703 ** a=TIMEORTAG Show events after TIMEORTAG
1704 ** b=TIMEORTAG Show events before TIMEORTAG
1705 ** c=TIMEORTAG Show events that happen "circa" TIMEORTAG
1706 ** cf=FILEHASH Show events around the time of the first use of
1707 ** the file with FILEHASH
1708 ** m=TIMEORTAG Highlight the event at TIMEORTAG, or the closest available
1709 ** event if TIMEORTAG is not part of the timeline. If
1710 ** the t= or r= is used, the m event is added to the timeline
1711 ** if it isn't there already.
1712 ** x=HASHLIST Show all check-ins in the comma-separated HASHLIST
1713 ** in addition to check-ins specified by t= or r=
1714 ** sel1=TIMEORTAG Highlight the check-in at TIMEORTAG if it is part of
1715 ** the timeline. Similar to m= except TIMEORTAG must
1716 ** match a check-in that is already in the timeline.
1717 ** sel2=TIMEORTAG Like sel1= but use the secondary highlight.
1718 ** n=COUNT Maximum number of events. "all" for no limit
@@ -1732,42 +1549,48 @@
1732 ** from=CX ... shortest path from CX back to CHECKIN
1733 ** ft=CHECKIN "Forward To": Show decendents forward to CHECKIN
1734 ** d=CX ... from CX up to the time of CHECKIN
1735 ** from=CX ... shortest path from CX up to CHECKIN
1736 ** t=TAG Show only check-ins with the given TAG
1737 ** r=TAG Show check-ins related to TAG, equivalent to t=TAG&rel
1738 ** tl=TAGLIST Shorthand for t=TAGLIST&ms=brlist
1739 ** rl=TAGLIST Shorthand for r=TAGLIST&ms=brlist
 
 
1740 ** rel Show related check-ins as well as those matching t=TAG
1741 ** mionly Limit rel to show ancestors but not descendants
1742 ** nowiki Do not show wiki associated with branch or tag
1743 ** ms=MATCHSTYLE Set tag match style to EXACT, GLOB, LIKE, REGEXP
 
1744 ** u=USER Only show items associated with USER
1745 ** y=TYPE 'ci', 'w', 't', 'n', 'e', 'f', or 'all'.
1746 ** ss=VIEWSTYLE c: "Compact", v: "Verbose", m: "Modern", j: "Columnar",
1747 ** x: "Classic".
1748 ** advm Use the "Advanced" or "Busy" menu design.
1749 ** ng No Graph.
1750 ** ncp Omit cherrypick merges
1751 ** nd Do not highlight the focus check-in
1752 ** nsm Omit the submenu
1753 ** nc Omit all graph colors other than highlights
1754 ** v Show details of files changed
1755 ** vfx Show complete text of forum messages
1756 ** f=CHECKIN Show family (immediate parents and children) of CHECKIN
1757 ** from=CHECKIN Path from...
1758 ** to=CHECKIN ... to this
1759 ** to2=CHECKIN ... backup name if to= doesn't resolve
1760 ** shortest ... show only the shortest path
1761 ** rel ... also show related checkins
1762 ** bt=PRIOR ... path from CHECKIN back to PRIOR
1763 ** ft=LATER ... path from CHECKIN forward to LATER
 
 
 
1764 ** uf=FILE_HASH Show only check-ins that contain the given file version
1765 ** All qualifying check-ins are shown unless there is
1766 ** also an n= or n1= query parameter.
1767 ** chng=GLOBLIST Show only check-ins that involve changes to a file whose
1768 ** name matches one of the comma-separate GLOBLIST
1769 ** brbg Background color determined by branch name
1770 ** ubg Background color determined by user
1771 ** deltabg Background color red for delta manifests or green
1772 ** for baseline manifests
1773 ** namechng Show only check-ins that have filename changes
@@ -1784,11 +1607,11 @@
1784 ** datefmt=N Override the date format: 0=HH:MM, 1=HH:MM:SS,
1785 ** 2=YYYY-MM-DD HH:MM:SS, 3=YYMMDD HH:MM, and 4 means "off".
1786 ** bisect Show the check-ins that are in the current bisect
1787 ** oldestfirst Show events oldest first.
1788 ** showid Show RIDs
1789 ** showsql Show the SQL text
1790 **
1791 ** p= and d= can appear individually or together. If either p= or d=
1792 ** appear, then u=, y=, a=, and b= are ignored.
1793 **
1794 ** If both a= and b= appear then both upper and lower bounds are honored.
@@ -1862,14 +1685,20 @@
1862 int advancedMenu = 0; /* Use the advanced menu design */
1863 char *zPlural; /* Ending for plural forms */
1864 int showCherrypicks = 1; /* True to show cherrypick merges */
1865 int haveParameterN; /* True if n= query parameter present */
1866 int from_to_mode = 0; /* 0: from,to. 1: from,ft 2: from,bt */
 
 
1867
1868 login_check_credentials();
1869 url_initialize(&url, "timeline");
1870 cgi_query_parameters_to_url(&url);
 
 
 
 
1871
1872 (void)P_NoBot("ss")
1873 /* "ss" is processed via the udc but at least one spider likes to
1874 ** try to SQL inject via this argument, so let's catch that. */;
1875
@@ -1984,49 +1813,52 @@
1984
1985 /* Check for tl=TAGLIST and rl=TAGLIST which are abbreviations for
1986 ** t=TAGLIST&ms=brlist and r=TAGLIST&ms=brlist repectively. */
1987 if( zBrName==0 && zTagName==0 ){
1988 const char *z;
 
1989 if( (z = P("tl"))!=0 ){
1990 zTagName = z;
1991 zMatchStyle = "brlist";
 
 
 
 
 
1992 }
1993 if( (z = P("rl"))!=0 ){
1994 zBrName = z;
1995 related = 1;
1996 zMatchStyle = "brlist";
 
 
 
 
 
1997 }
1998 }
1999
2000 /* Convert r=TAG to t=TAG&rel in order to populate the UI style widgets. */
2001 if( zBrName && !related ){
2002 cgi_delete_query_parameter("r");
2003 cgi_set_query_parameter("t", zBrName); (void)P("t");
2004 cgi_set_query_parameter("rel", "1");
2005 zTagName = zBrName;
2006 related = 1;
2007 zType = "ci";
2008 }
2009
2010 /* Ignore empty tag query strings. */
2011 if( zTagName && !*zTagName ){
2012 zTagName = 0;
2013 }
2014
2015 /* Finish preliminary processing of tag match queries. */
 
2016 if( zTagName ){
2017 zType = "ci";
2018 /* Interpet the tag style string. */
2019 if( fossil_stricmp(zMatchStyle, "glob")==0 ){
2020 matchStyle = MS_GLOB;
2021 }else if( fossil_stricmp(zMatchStyle, "like")==0 ){
2022 matchStyle = MS_LIKE;
2023 }else if( fossil_stricmp(zMatchStyle, "regexp")==0 ){
2024 matchStyle = MS_REGEXP;
2025 }else if( fossil_stricmp(zMatchStyle, "brlist")==0 ){
2026 matchStyle = MS_BRLIST;
2027 }else{
2028 /* For exact maching, inhibit links to the selected tag. */
2029 zThisTag = zTagName;
2030 Th_Store("current_checkin", zTagName);
2031 }
2032
@@ -2034,11 +1866,11 @@
2034 if( advancedMenu ){
2035 style_submenu_checkbox("rel", "Related", 0, 0);
2036 }
2037
2038 /* Construct the tag match expression. */
2039 zTagSql = tagMatchExpression(matchStyle, zTagName, &zMatchDesc, &zError);
2040 }
2041
2042 if( zMark && zMark[0]==0 ){
2043 if( zAfter ) zMark = zAfter;
2044 if( zBefore ) zMark = zBefore;
@@ -2082,10 +1914,11 @@
2082 }
2083 if( PB("nc") ){
2084 tmFlags &= ~(TIMELINE_DELTA|TIMELINE_BRCOLOR|TIMELINE_UCOLOR);
2085 tmFlags |= TIMELINE_NOCOLOR;
2086 }
 
2087 if( zUses!=0 ){
2088 int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses);
2089 if( ufid ){
2090 zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid);
2091 db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)");
@@ -2099,42 +1932,42 @@
2099 }
2100 if( renameOnly ){
2101 db_multi_exec(
2102 "CREATE TEMP TABLE rnfile(rid INTEGER PRIMARY KEY);"
2103 "INSERT OR IGNORE INTO rnfile"
2104 " SELECT mid FROM mlink WHERE pfnid>0 AND pfnid!=fnid;"
2105 );
2106 disableY = 1;
2107 }
2108 if( forkOnly ){
2109 db_multi_exec(
2110 "CREATE TEMP TABLE rnfork(rid INTEGER PRIMARY KEY);\n"
2111 "INSERT OR IGNORE INTO rnfork(rid)\n"
2112 " SELECT pid FROM plink\n"
2113 " WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)=="
2114 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
2115 " GROUP BY pid"
2116 " HAVING count(*)>1;\n"
2117 "INSERT OR IGNORE INTO rnfork(rid)"
2118 " SELECT cid FROM plink\n"
2119 " WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)=="
2120 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
2121 " GROUP BY cid"
2122 " HAVING count(*)>1;\n",
2123 TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH
2124 );
2125 db_multi_exec(
2126 "INSERT OR IGNORE INTO rnfork(rid)\n"
2127 " SELECT cid FROM plink\n"
2128 " WHERE pid IN rnfork"
2129 " AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)=="
2130 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
2131 " UNION "
2132 " SELECT pid FROM plink\n"
2133 " WHERE cid IN rnfork"
2134 " AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)=="
2135 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n",
2136 TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH
2137 );
2138 tmFlags |= TIMELINE_UNHIDE;
2139 zType = "ci";
2140 disableY = 1;
@@ -2207,77 +2040,114 @@
2207 PathNode *p = 0;
2208 const char *zFrom = 0;
2209 const char *zTo = 0;
2210 Blob ins;
2211 int nNodeOnPath = 0;
 
 
2212
2213 if( from_rid && to_rid ){
2214 if( from_to_mode==0 ){
2215 p = path_shortest(from_rid, to_rid, noMerge, 0, 0);
2216 }else if( from_to_mode==1 ){
2217 p = path_shortest(from_rid, to_rid, 0, 1, 0);
 
 
2218 }else{
2219 p = path_shortest(to_rid, from_rid, 0, 1, 0);
 
 
2220 }
2221 zFrom = P("from");
2222 zTo = zTo2 ? zTo2 : P("to");
2223 }else{
2224 if( path_common_ancestor(me_rid, you_rid) ){
 
2225 p = path_first();
2226 }
2227 zFrom = P("me");
2228 zTo = P("you");
 
 
 
 
 
 
 
 
 
2229 }
2230 blob_init(&ins, 0, 0);
2231 db_multi_exec(
2232 "CREATE TABLE IF NOT EXISTS temp.pathnode(x INTEGER PRIMARY KEY);"
2233 );
2234 if( p ){
 
2235 blob_init(&ins, 0, 0);
2236 blob_append_sql(&ins, "INSERT INTO pathnode(x) VALUES(%d)", p->rid);
2237 p = p->u.pTo;
2238 while( p ){
2239 blob_append_sql(&ins, ",(%d)", p->rid);
 
 
 
 
 
 
2240 p = p->u.pTo;
2241 }
2242 }
2243 path_reset();
2244 db_multi_exec("%s", blob_str(&ins)/*safe-for-%s*/);
2245 blob_reset(&ins);
2246 if( related || P("mionly") ){
2247 db_multi_exec(
2248 "CREATE TABLE IF NOT EXISTS temp.related(x INTEGER PRIMARY KEY);"
2249 "INSERT OR IGNORE INTO related(x)"
2250 " SELECT pid FROM plink WHERE cid IN pathnode AND NOT isprim;"
2251 );
2252 if( P("mionly")==0 ){
2253 db_multi_exec(
2254 "INSERT OR IGNORE INTO related(x)"
2255 " SELECT cid FROM plink WHERE pid IN pathnode;"
2256 );
2257 }
2258 if( showCherrypicks ){
2259 db_multi_exec(
2260 "INSERT OR IGNORE INTO related(x)"
2261 " SELECT parentid FROM cherrypick WHERE childid IN pathnode;"
2262 );
2263 if( P("mionly")==0 ){
2264 db_multi_exec(
2265 "INSERT OR IGNORE INTO related(x)"
2266 " SELECT childid FROM cherrypick WHERE parentid IN pathnode;"
2267 );
2268 }
 
 
 
 
 
 
 
 
 
 
 
 
2269 }
2270 db_multi_exec("INSERT OR IGNORE INTO pathnode SELECT x FROM related");
2271 }
 
2272 blob_append_sql(&sql, " AND event.objid IN pathnode");
2273 if( zChng && zChng[0] ){
2274 db_multi_exec(
2275 "DELETE FROM pathnode "
2276 " WHERE NOT EXISTS(SELECT 1 FROM mlink, filename"
2277 " WHERE mlink.mid=x"
2278 " AND mlink.fnid=filename.fnid AND %s)",
 
2279 glob_expr("filename.name", zChng)
2280 );
2281 }
2282 tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
2283 db_multi_exec("%s", blob_sql_text(&sql));
@@ -2331,18 +2201,18 @@
2331 if( !haveParameterN ) nEntry = 10;
2332 }
2333 db_multi_exec(
2334 "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
2335 );
 
2336 zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d",
2337 p_rid ? p_rid : d_rid);
2338 zCiName = zDPName;
2339 if( zCiName==0 ) zCiName = zUuid;
2340 blob_append_sql(&sql, " AND event.objid IN ok");
2341 nd = 0;
2342 if( d_rid ){
2343 Stmt s;
2344 double rStopTime = 9e99;
2345 zFwdTo = P("ft");
2346 if( zFwdTo ){
2347 double rStartDate = db_double(0.0,
2348 "SELECT mtime FROM event WHERE objid=%d", d_rid);
@@ -2354,27 +2224,25 @@
2354 if( !haveParameterN ) nEntry = 0;
2355 rStopTime = db_double(9e99,
2356 "SELECT mtime FROM event WHERE objid=%d", ridFwdTo);
2357 }
2358 }
2359 db_prepare(&s,
2360 "WITH RECURSIVE"
2361 " dx(rid,mtime) AS ("
2362 " SELECT %d, 0"
2363 " UNION"
2364 " SELECT plink.cid, plink.mtime FROM dx, plink"
2365 " WHERE plink.pid=dx.rid"
2366 " AND (:stop>=8e99 OR plink.mtime<=:stop)"
2367 " ORDER BY 2"
2368 " )"
 
 
2369 "INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d",
2370 d_rid, nEntry<=0 ? -1 : nEntry+1
2371 );
2372 db_bind_double(&s, ":stop", rStopTime);
2373 db_step(&s);
2374 db_finalize(&s);
2375 /* compute_descendants(d_rid, nEntry==0 ? 0 : nEntry+1); */
2376 nd = db_int(0, "SELECT count(*)-1 FROM ok");
2377 if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql));
2378 if( nd>0 || p_rid==0 ){
2379 blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
2380 }
@@ -2483,28 +2351,28 @@
2483 if( zChng && *zChng ){
2484 addFileGlobExclusion(zChng, &cond);
2485 tmFlags |= TIMELINE_XMERGE;
2486 }
2487 if( zUses ){
2488 blob_append_sql(&cond, " AND event.objid IN usesfile ");
2489 }
2490 if( renameOnly ){
2491 blob_append_sql(&cond, " AND event.objid IN rnfile ");
2492 }
2493 if( forkOnly ){
2494 blob_append_sql(&cond, " AND event.objid IN rnfork ");
2495 }
2496 if( cpOnly && showCherrypicks ){
2497 db_multi_exec(
2498 "CREATE TEMP TABLE IF NOT EXISTS cpnodes(rid INTEGER PRIMARY KEY);"
2499 "INSERT OR IGNORE INTO cpnodes SELECT childid FROM cherrypick;"
2500 "INSERT OR IGNORE INTO cpnodes SELECT parentid FROM cherrypick;"
2501 );
2502 blob_append_sql(&cond, " AND event.objid IN cpnodes ");
2503 }
2504 if( bisectLocal || zBisect!=0 ){
2505 blob_append_sql(&cond, " AND event.objid IN (SELECT rid FROM bilog) ");
2506 }
2507 if( zYearMonth ){
2508 char *zNext;
2509 zYearMonth = timeline_expand_datetime(zYearMonth);
2510 if( strlen(zYearMonth)>7 ){
@@ -2663,39 +2531,24 @@
2663 nEntry = -1;
2664 }
2665 if( zTagSql ){
2666 db_multi_exec(
2667 "CREATE TEMP TABLE selected_nodes(rid INTEGER PRIMARY KEY);"
2668 "INSERT OR IGNORE INTO selected_nodes"
2669 " SELECT tagxref.rid FROM tagxref NATURAL JOIN tag"
2670 " WHERE %s AND tagtype>0", zTagSql/*safe-for-%s*/
 
2671 );
2672 if( zMark ){
2673 /* If the t=release option is used with m=UUID, then also
2674 ** include the UUID check-in in the display list */
2675 int ridMark = name_to_rid(zMark);
2676 db_multi_exec(
2677 "INSERT OR IGNORE INTO selected_nodes(rid) VALUES(%d)", ridMark);
2678 }
2679 if( P("x")!=0 ){
2680 char *zX = fossil_strdup(P("x"));
2681 int ii;
2682 int ridX;
2683 while( zX[0] ){
2684 char c;
2685 if( zX[0]==',' || zX[0]==' ' ){ zX++; continue; }
2686 for(ii=1; zX[ii] && zX[ii]!=',' && zX[ii]!=' '; ii++){}
2687 c = zX[ii];
2688 zX[ii] = 0;
2689 ridX = name_to_rid(zX);
2690 db_multi_exec(
2691 "INSERT OR IGNORE INTO selected_nodes(rid) VALUES(%d)", ridX);
2692 zX[ii] = c;
2693 zX += ii;
2694 }
2695 }
2696 if( !related ){
2697 blob_append_sql(&cond, " AND blob.rid IN selected_nodes");
2698 }else{
2699 db_multi_exec(
2700 "CREATE TEMP TABLE related_nodes(rid INTEGER PRIMARY KEY);"
2701 "INSERT INTO related_nodes SELECT rid FROM selected_nodes;"
@@ -2706,40 +2559,42 @@
2706 ** branch to be included in the report. These related check-ins are
2707 ** useful in helping to visualize what has happened on a quiescent
2708 ** branch that is infrequently merged with a much more activate branch.
2709 */
2710 db_multi_exec(
2711 "INSERT OR IGNORE INTO related_nodes"
2712 " SELECT pid FROM selected_nodes CROSS JOIN plink"
2713 " WHERE selected_nodes.rid=plink.cid;"
2714 );
2715 if( P("mionly")==0 ){
2716 db_multi_exec(
2717 "INSERT OR IGNORE INTO related_nodes"
2718 " SELECT cid FROM selected_nodes CROSS JOIN plink"
2719 " WHERE selected_nodes.rid=plink.pid;"
2720 );
2721 if( showCherrypicks ){
2722 db_multi_exec(
2723 "INSERT OR IGNORE INTO related_nodes"
2724 " SELECT childid FROM selected_nodes CROSS JOIN cherrypick"
2725 " WHERE selected_nodes.rid=cherrypick.parentid;"
2726 );
2727 }
2728 }
2729 if( showCherrypicks ){
2730 db_multi_exec(
2731 "INSERT OR IGNORE INTO related_nodes"
2732 " SELECT parentid FROM selected_nodes CROSS JOIN cherrypick"
2733 " WHERE selected_nodes.rid=cherrypick.childid;"
2734 );
2735 }
2736 if( (tmFlags & TIMELINE_UNHIDE)==0 ){
2737 db_multi_exec(
2738 "DELETE FROM related_nodes WHERE rid IN "
2739 " (SELECT related_nodes.rid FROM related_nodes, tagxref"
2740 " WHERE tagid=%d AND tagtype>0 AND tagxref.rid=related_nodes.rid)",
 
 
2741 TAG_HIDDEN
2742 );
2743 }
2744 }
2745 }
@@ -2829,45 +2684,42 @@
2829 rCirca = symbolic_name_to_mtime(zCirca, &zCirca);
2830 blob_append_sql(&sql, "%s", blob_sql_text(&cond));
2831 if( rAfter>0.0 ){
2832 if( rBefore>0.0 ){
2833 blob_append_sql(&sql,
2834 " AND event.mtime>=%.17g AND event.mtime<=%.17g"
2835 " ORDER BY event.mtime ASC", rAfter-ONE_SECOND, rBefore+ONE_SECOND);
2836 nEntry = -1;
2837 }else{
2838 blob_append_sql(&sql,
2839 " AND event.mtime>=%.17g ORDER BY event.mtime ASC",
2840 rAfter-ONE_SECOND);
2841 }
2842 zCirca = 0;
2843 url_add_parameter(&url, "c", 0);
2844 }else if( rBefore>0.0 ){
2845 blob_append_sql(&sql,
2846 " AND event.mtime<=%.17g ORDER BY event.mtime DESC",
2847 rBefore+ONE_SECOND);
2848 zCirca = 0;
2849 url_add_parameter(&url, "c", 0);
2850 }else if( rCirca>0.0 ){
2851 Blob sql2;
2852 blob_init(&sql2, blob_sql_text(&sql), -1);
2853 blob_append_sql(&sql2,
2854 " AND event.mtime>=%f ORDER BY event.mtime ASC", rCirca);
2855 if( nEntry>0 ){
2856 blob_append_sql(&sql2," LIMIT %d", (nEntry+1)/2);
2857 }
2858 if( PB("showsql") ){
2859 @ <pre>%h(blob_sql_text(&sql2))</pre>
2860 }
2861 db_multi_exec("%s", blob_sql_text(&sql2));
2862 if( nEntry>0 ){
2863 nEntry -= db_int(0,"select count(*) from timeline");
2864 if( nEntry<=0 ) nEntry = 1;
2865 }
2866 blob_reset(&sql2);
2867 blob_append_sql(&sql,
2868 " AND event.mtime<=%f ORDER BY event.mtime DESC",
2869 rCirca
2870 );
2871 if( zMark==0 ) zMark = zCirca;
2872 }else{
2873 blob_append_sql(&sql, " ORDER BY event.mtime DESC");
@@ -3010,12 +2862,14 @@
3010 style_submenu_multichoice("ms", count(azMatchStyles)/2,azMatchStyles,0);
3011 }
3012 }
3013 blob_zero(&cond);
3014 }
3015 if( PB("showsql") ){
3016 @ <pre>%h(blob_sql_text(&sql))</pre>
 
 
3017 }
3018 if( search_restrict(SRCH_CKIN)!=0 ){
3019 style_submenu_element("Search", "%R/search?y=c");
3020 }
3021 if( advancedMenu ){
@@ -3069,18 +2923,36 @@
3069 if( zNewerButton ){
3070 @ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\
3071 @ &nbsp;&uarr;</a>
3072 }
3073 cgi_check_for_malice();
3074 www_print_timeline(&q, tmFlags, zThisUser, zThisTag, zBrName,
3075 selectedRid, secondaryRid, 0);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3076 db_finalize(&q);
3077 if( zOlderButton ){
3078 @ %z(chref("button","%s",zOlderButton))%h(zOlderButtonLabel)\
3079 @ &nbsp;&darr;</a>
3080 }
3081 document_emit_js(/*handles pikchrs rendered above*/);
 
 
3082 style_finish_page();
3083 }
3084
3085 /*
3086 ** Translate a timeline entry into the printable format by
3087
--- src/timeline.c
+++ src/timeline.c
@@ -191,11 +191,11 @@
191 void www_print_timeline(
192 Stmt *pQuery, /* Query to implement the timeline */
193 int tmFlags, /* Flags controlling display behavior */
194 const char *zThisUser, /* Suppress links to this user */
195 const char *zThisTag, /* Suppress links to this tag */
196 Matcher *pLeftBranch, /* Comparison function to use for zLeftBranch */
197 int selectedRid, /* Highlight the line with this RID value or zero */
198 int secondRid, /* Secondary highlight (or zero) */
199 void (*xExtra)(int) /* Routine to call on each line of display */
200 ){
201 int mxWikiLen;
@@ -813,11 +813,11 @@
813 }
814 if( pendingEndTr ){
815 @ </td></tr>
816 }
817 if( pGraph ){
818 graph_finish(pGraph, pLeftBranch, tmFlags);
819 if( pGraph->nErr ){
820 graph_free(pGraph);
821 pGraph = 0;
822 }else{
823 @ <tr class="timelineBottom" id="btm-%d(iTableId)">\
@@ -1290,12 +1290,13 @@
1290 const char *zChng, /* The filename GLOB list */
1291 Blob *pSql /* The SELECT statement under construction */
1292 ){
1293 if( zChng==0 || zChng[0]==0 ) return;
1294 blob_append_sql(pSql," AND event.objid IN ("
1295 "SELECT mlink.mid FROM mlink, filename\n"
1296 " WHERE mlink.fnid=filename.fnid\n"
1297 " AND %s)",
1298 glob_expr("filename.name", mprintf("\"%s\"", zChng)));
1299 }
1300 static void addFileGlobDescription(
1301 const char *zChng, /* The filename GLOB list */
1302 Blob *pDescription /* Result description */
@@ -1303,237 +1304,10 @@
1304 if( zChng==0 || zChng[0]==0 ) return;
1305 blob_appendf(pDescription, " that include changes to files matching '%h'",
1306 zChng);
1307 }
1308
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1309 /*
1310 ** Similar to fossil_expand_datetime()
1311 **
1312 ** Add missing "-" characters into a date/time. Examples:
1313 **
@@ -1591,10 +1365,11 @@
1365 tagId = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", zEnd);
1366 if( tagId==0 ){
1367 endId = symbolic_name_to_rid(zEnd, "ci");
1368 if( endId==0 ) return 0;
1369 }
1370 db_pause_dml_log();
1371 if( bForward ){
1372 if( tagId ){
1373 db_prepare(&q,
1374 "WITH RECURSIVE dx(id,mtime) AS ("
1375 " SELECT %d, event.mtime FROM event WHERE objid=%d"
@@ -1658,12 +1433,54 @@
1433 }
1434 if( db_step(&q)==SQLITE_ROW ){
1435 ans = db_column_int(&q, 0);
1436 }
1437 db_finalize(&q);
1438 db_unpause_dml_log();
1439 return ans;
1440 }
1441
1442 /*
1443 ** Add to the (temp) table zTab, RID values for every check-in
1444 ** identifier found on the zExtra string. Check-in names can be separated
1445 ** by commas or by whitespace.
1446 */
1447 static void add_extra_rids(const char *zTab, const char *zExtra){
1448 int ii;
1449 int rid;
1450 int cnt;
1451 Blob sql;
1452 char *zX;
1453 char *zToDel;
1454 if( zExtra==0 ) return;
1455 cnt = 0;
1456 blob_init(&sql, 0, 0);
1457 zX = zToDel = fossil_strdup(zExtra);
1458 blob_append_sql(&sql, "INSERT OR IGNORE INTO \"%w\" VALUES", zTab);
1459 while( zX[0] ){
1460 char c;
1461 if( zX[0]==',' || zX[0]==' ' ){ zX++; continue; }
1462 for(ii=1; zX[ii] && zX[ii]!=',' && zX[ii]!=' '; ii++){}
1463 c = zX[ii];
1464 zX[ii] = 0;
1465 rid = name_to_rid(zX);
1466 if( rid>0 ){
1467 if( (cnt%10)==4 ){
1468 blob_append_sql(&sql,",\n ");
1469 }else if( cnt>0 ){
1470 blob_append_sql(&sql,",");
1471 }
1472 blob_append_sql(&sql, "(%d)", rid);
1473 cnt++;
1474 }
1475 zX[ii] = c;
1476 zX += ii;
1477 }
1478 if( cnt ) db_exec_sql(blob_sql_text(&sql));
1479 blob_reset(&sql);
1480 fossil_free(zToDel);
1481 }
1482
1483 /*
1484 ** COMMAND: test-endpoint
1485 **
1486 ** Usage: fossil test-endpoint BASE TAG ?OPTIONS?
@@ -1698,21 +1515,21 @@
1515 /*
1516 ** WEBPAGE: timeline
1517 **
1518 ** Query parameters:
1519 **
1520 ** a=TIMEORTAG Show events after TIMEORTAG.
1521 ** b=TIMEORTAG Show events before TIMEORTAG.
1522 ** c=TIMEORTAG Show events that happen "circa" TIMEORTAG
1523 ** cf=FILEHASH Show events around the time of the first use of
1524 ** the file with FILEHASH.
1525 ** m=TIMEORTAG Highlight the event at TIMEORTAG, or the closest available
1526 ** event if TIMEORTAG is not part of the timeline. If
1527 ** the t= or r= is used, the m event is added to the timeline
1528 ** if it isn't there already.
1529 ** x=LIST Show check-ins in the comma- or space-separated LIST
1530 ** in addition to check-ins specified by other parameters.
1531 ** sel1=TIMEORTAG Highlight the check-in at TIMEORTAG if it is part of
1532 ** the timeline. Similar to m= except TIMEORTAG must
1533 ** match a check-in that is already in the timeline.
1534 ** sel2=TIMEORTAG Like sel1= but use the secondary highlight.
1535 ** n=COUNT Maximum number of events. "all" for no limit
@@ -1732,42 +1549,48 @@
1549 ** from=CX ... shortest path from CX back to CHECKIN
1550 ** ft=CHECKIN "Forward To": Show decendents forward to CHECKIN
1551 ** d=CX ... from CX up to the time of CHECKIN
1552 ** from=CX ... shortest path from CX up to CHECKIN
1553 ** t=TAG Show only check-ins with the given TAG
1554 ** r=TAG Same as 't=TAG&rel'. Mnemonic: "Related"
1555 ** tl=TAGLIST Same as 't=TAGLIST&ms=brlist'. Mnemonic: "Tag List"
1556 ** rl=TAGLIST Same as 'r=TAGLIST&ms=brlist'. Mnemonic: "Related List"
1557 ** ml=TAGLIST Same as 'tl=TAGLIST&mionly'. Mnemonic: "Merge-in List"
1558 ** sl=TAGLIST "Sort List". Draw TAGLIST branches ordered left to right.
1559 ** rel Show related check-ins as well as those matching t=TAG
1560 ** mionly Show related parents but not related children.
1561 ** nowiki Do not show wiki associated with branch or tag
1562 ** ms=MATCHSTYLE Set tag name match algorithm. One of "exact", "glob",
1563 ** "like", or "regexp".
1564 ** u=USER Only show items associated with USER
1565 ** y=TYPE 'ci', 'w', 't', 'n', 'e', 'f', or 'all'.
1566 ** ss=VIEWSTYLE c: "Compact", v: "Verbose", m: "Modern", j: "Columnar",
1567 * x: "Classic".
1568 ** advm Use the "Advanced" or "Busy" menu design.
1569 ** ng No Graph.
1570 ** ncp Omit cherrypick merges
1571 ** nd Do not highlight the focus check-in
1572 ** nsm Omit the submenu
1573 ** nc Omit all graph colors other than highlights
1574 ** v Show details of files changed
1575 ** vfx Show complete text of forum messages
1576 ** f=CHECKIN Family (immediate parents and children) of CHECKIN
1577 ** from=CHECKIN Path through common ancestor from...
1578 ** to=CHECKIN ... to this
1579 ** to2=CHECKIN ... backup name if to= doesn't resolve
1580 ** shortest ... show only the shortest path
1581 ** rel ... also show related checkins
1582 ** bt=PRIOR ... path from CHECKIN back to PRIOR
1583 ** ft=LATER ... path from CHECKIN forward to LATER
1584 ** me=CHECKIN Most direct path from...
1585 ** you=CHECKIN ... to this
1586 ** rel ... also show related checkins
1587 ** uf=FILE_HASH Show only check-ins that contain the given file version
1588 ** All qualifying check-ins are shown unless there is
1589 ** also an n= or n1= query parameter.
1590 ** chng=GLOBLIST Show only check-ins that involve changes to a file whose
1591 ** name matches one of the comma-separate GLOBLIST
1592 ** brbg Background color determined by branch name
1593 ** ubg Background color determined by user
1594 ** deltabg Background color red for delta manifests or green
1595 ** for baseline manifests
1596 ** namechng Show only check-ins that have filename changes
@@ -1784,11 +1607,11 @@
1607 ** datefmt=N Override the date format: 0=HH:MM, 1=HH:MM:SS,
1608 ** 2=YYYY-MM-DD HH:MM:SS, 3=YYMMDD HH:MM, and 4 means "off".
1609 ** bisect Show the check-ins that are in the current bisect
1610 ** oldestfirst Show events oldest first.
1611 ** showid Show RIDs
1612 ** showsql Show the SQL used to generate the report
1613 **
1614 ** p= and d= can appear individually or together. If either p= or d=
1615 ** appear, then u=, y=, a=, and b= are ignored.
1616 **
1617 ** If both a= and b= appear then both upper and lower bounds are honored.
@@ -1862,14 +1685,20 @@
1685 int advancedMenu = 0; /* Use the advanced menu design */
1686 char *zPlural; /* Ending for plural forms */
1687 int showCherrypicks = 1; /* True to show cherrypick merges */
1688 int haveParameterN; /* True if n= query parameter present */
1689 int from_to_mode = 0; /* 0: from,to. 1: from,ft 2: from,bt */
1690 int showSql = PB("showsql"); /* True to show the SQL */
1691 Blob allSql; /* Copy of all SQL text */
1692
1693 login_check_credentials();
1694 url_initialize(&url, "timeline");
1695 cgi_query_parameters_to_url(&url);
1696 blob_init(&allSql, 0, 0);
1697
1698 /* The "mionly" query parameter is like "rel", but shows merge-ins only */
1699 if( P("mionly")!=0 ) related = 2;
1700
1701 (void)P_NoBot("ss")
1702 /* "ss" is processed via the udc but at least one spider likes to
1703 ** try to SQL inject via this argument, so let's catch that. */;
1704
@@ -1984,49 +1813,52 @@
1813
1814 /* Check for tl=TAGLIST and rl=TAGLIST which are abbreviations for
1815 ** t=TAGLIST&ms=brlist and r=TAGLIST&ms=brlist repectively. */
1816 if( zBrName==0 && zTagName==0 ){
1817 const char *z;
1818 const char *zPattern = 0;
1819 if( (z = P("tl"))!=0 ){
1820 zPattern = zTagName = z;
1821 }else if( (z = P("rl"))!=0 ){
1822 zPattern = zBrName = z;
1823 if( related==0 ) related = 1;
1824 }else if( (z = P("ml"))!=0 ){
1825 zPattern = zBrName = z;
1826 if( related==0 ) related = 2;
1827 }
1828 if( zPattern!=0 && zMatchStyle==0 ){
1829 /* If there was no ms= query parameter, set the match style to
1830 ** "glob" if the pattern appears to contain GLOB character, or
1831 ** "brlist" if it does not. */
1832 if( strpbrk(zPattern,"*[?") ){
1833 zMatchStyle = "glob";
1834 }else{
1835 zMatchStyle = "brlist";
1836 }
1837 }
1838 }
1839
1840 /* Convert r=TAG to t=TAG&rel in order to populate the UI style widgets. */
1841 if( zBrName ){
1842 cgi_delete_query_parameter("r");
1843 cgi_set_query_parameter("t", zBrName); (void)P("t");
1844 cgi_set_query_parameter("rel", "1");
1845 zTagName = zBrName;
1846 if( related==0 ) related = 1;
1847 zType = "ci";
1848 }
1849
1850 /* Ignore empty tag query strings. */
1851 if( zTagName && !*zTagName ){
1852 zTagName = 0;
1853 }
1854
1855 /* Finish preliminary processing of tag match queries. */
1856 matchStyle = match_style(zMatchStyle, MS_EXACT);
1857 if( zTagName ){
1858 zType = "ci";
1859 if( matchStyle==MS_EXACT ){
 
 
 
 
 
 
 
 
 
1860 /* For exact maching, inhibit links to the selected tag. */
1861 zThisTag = zTagName;
1862 Th_Store("current_checkin", zTagName);
1863 }
1864
@@ -2034,11 +1866,11 @@
1866 if( advancedMenu ){
1867 style_submenu_checkbox("rel", "Related", 0, 0);
1868 }
1869
1870 /* Construct the tag match expression. */
1871 zTagSql = match_tag_sqlexpr(matchStyle, zTagName, &zMatchDesc, &zError);
1872 }
1873
1874 if( zMark && zMark[0]==0 ){
1875 if( zAfter ) zMark = zAfter;
1876 if( zBefore ) zMark = zBefore;
@@ -2082,10 +1914,11 @@
1914 }
1915 if( PB("nc") ){
1916 tmFlags &= ~(TIMELINE_DELTA|TIMELINE_BRCOLOR|TIMELINE_UCOLOR);
1917 tmFlags |= TIMELINE_NOCOLOR;
1918 }
1919 if( showSql ) db_append_dml_to_blob(&allSql);
1920 if( zUses!=0 ){
1921 int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses);
1922 if( ufid ){
1923 zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid);
1924 db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)");
@@ -2099,42 +1932,42 @@
1932 }
1933 if( renameOnly ){
1934 db_multi_exec(
1935 "CREATE TEMP TABLE rnfile(rid INTEGER PRIMARY KEY);"
1936 "INSERT OR IGNORE INTO rnfile"
1937 " SELECT mid FROM mlink WHERE pfnid>0 AND pfnid!=fnid;"
1938 );
1939 disableY = 1;
1940 }
1941 if( forkOnly ){
1942 db_multi_exec(
1943 "CREATE TEMP TABLE rnfork(rid INTEGER PRIMARY KEY);\n"
1944 "INSERT OR IGNORE INTO rnfork(rid)\n"
1945 " SELECT pid FROM plink\n"
1946 " WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==\n"
1947 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
1948 " GROUP BY pid\n"
1949 " HAVING count(*)>1;\n"
1950 "INSERT OR IGNORE INTO rnfork(rid)\n"
1951 " SELECT cid FROM plink\n"
1952 " WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==\n"
1953 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
1954 " GROUP BY cid\n"
1955 " HAVING count(*)>1;\n",
1956 TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH
1957 );
1958 db_multi_exec(
1959 "INSERT OR IGNORE INTO rnfork(rid)\n"
1960 " SELECT cid FROM plink\n"
1961 " WHERE pid IN rnfork\n"
1962 " AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==\n"
1963 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
1964 " UNION\n"
1965 " SELECT pid FROM plink\n"
1966 " WHERE cid IN rnfork\n"
1967 " AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==\n"
1968 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n",
1969 TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH
1970 );
1971 tmFlags |= TIMELINE_UNHIDE;
1972 zType = "ci";
1973 disableY = 1;
@@ -2207,77 +2040,114 @@
2040 PathNode *p = 0;
2041 const char *zFrom = 0;
2042 const char *zTo = 0;
2043 Blob ins;
2044 int nNodeOnPath = 0;
2045 int commonAncs = 0; /* Common ancestors of me_rid and you_rid. */
2046 int earlierRid = 0, laterRid = 0;
2047
2048 if( from_rid && to_rid ){
2049 if( from_to_mode==0 ){
2050 p = path_shortest(from_rid, to_rid, noMerge, 0, 0);
2051 }else if( from_to_mode==1 ){
2052 p = path_shortest(from_rid, to_rid, 0, 1, 0);
2053 earlierRid = commonAncs = from_rid;
2054 laterRid = to_rid;
2055 }else{
2056 p = path_shortest(to_rid, from_rid, 0, 1, 0);
2057 earlierRid = commonAncs = to_rid;
2058 laterRid = from_rid;
2059 }
2060 zFrom = P("from");
2061 zTo = zTo2 ? zTo2 : P("to");
2062 }else{
2063 commonAncs = path_common_ancestor(me_rid, you_rid);
2064 if( commonAncs!=0 ){
2065 p = path_first();
2066 }
2067 if( commonAncs==you_rid ){
2068 zFrom = P("you");
2069 zTo = P("me");
2070 earlierRid = you_rid;
2071 laterRid = me_rid;
2072 }else{
2073 zFrom = P("me");
2074 zTo = P("you");
2075 earlierRid = me_rid;
2076 laterRid = you_rid;
2077 }
2078 }
2079 blob_init(&ins, 0, 0);
2080 db_multi_exec(
2081 "CREATE TEMP TABLE IF NOT EXISTS pathnode(x INTEGER PRIMARY KEY);"
2082 );
2083 if( p ){
2084 int cnt = 4;
2085 blob_init(&ins, 0, 0);
2086 blob_append_sql(&ins, "INSERT INTO pathnode(x) VALUES(%d)", p->rid);
2087 p = p->u.pTo;
2088 while( p ){
2089 if( cnt==8 ){
2090 blob_append_sql(&ins, ",\n (%d)", p->rid);
2091 cnt = 0;
2092 }else{
2093 cnt++;
2094 blob_append_sql(&ins, ",(%d)", p->rid);
2095 }
2096 p = p->u.pTo;
2097 }
2098 }
2099 path_reset();
2100 db_multi_exec("%s", blob_str(&ins)/*safe-for-%s*/);
2101 blob_reset(&ins);
2102 if( related ){
2103 db_multi_exec(
2104 "CREATE TEMP TABLE IF NOT EXISTS related(x INTEGER PRIMARY KEY);"
2105 "INSERT OR IGNORE INTO related(x)"
2106 " SELECT pid FROM plink WHERE cid IN pathnode AND NOT isprim;"
2107 );
2108 if( related==1 ){
2109 db_multi_exec(
2110 "INSERT OR IGNORE INTO related(x)"
2111 " SELECT cid FROM plink WHERE pid IN pathnode;"
2112 );
2113 }
2114 if( showCherrypicks ){
2115 db_multi_exec(
2116 "INSERT OR IGNORE INTO related(x)"
2117 " SELECT parentid FROM cherrypick WHERE childid IN pathnode;"
2118 );
2119 if( related==1 ){
2120 db_multi_exec(
2121 "INSERT OR IGNORE INTO related(x)"
2122 " SELECT childid FROM cherrypick WHERE parentid IN pathnode;"
2123 );
2124 }
2125 }
2126 if( earlierRid && laterRid && commonAncs==earlierRid ){
2127 /* On a query with me=XXX, you=YYY, and rel, omit all nodes that
2128 ** are not ancestors of either XXX or YYY, as those nodes tend to
2129 ** be extraneous */
2130 db_multi_exec(
2131 "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
2132 );
2133 compute_ancestors(laterRid, 0, 0, earlierRid);
2134 db_multi_exec(
2135 "DELETE FROM related WHERE x NOT IN ok;"
2136 );
2137 }
2138 db_multi_exec("INSERT OR IGNORE INTO pathnode SELECT x FROM related");
2139 }
2140 add_extra_rids("pathnode",P("x"));
2141 blob_append_sql(&sql, " AND event.objid IN pathnode");
2142 if( zChng && zChng[0] ){
2143 db_multi_exec(
2144 "DELETE FROM pathnode\n"
2145 " WHERE NOT EXISTS(SELECT 1 FROM mlink, filename\n"
2146 " WHERE mlink.mid=x\n"
2147 " AND mlink.fnid=filename.fnid\n"
2148 " AND %s)",
2149 glob_expr("filename.name", zChng)
2150 );
2151 }
2152 tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
2153 db_multi_exec("%s", blob_sql_text(&sql));
@@ -2331,18 +2201,18 @@
2201 if( !haveParameterN ) nEntry = 10;
2202 }
2203 db_multi_exec(
2204 "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
2205 );
2206 add_extra_rids("ok", P("x"));
2207 zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d",
2208 p_rid ? p_rid : d_rid);
2209 zCiName = zDPName;
2210 if( zCiName==0 ) zCiName = zUuid;
2211 blob_append_sql(&sql, " AND event.objid IN ok");
2212 nd = 0;
2213 if( d_rid ){
 
2214 double rStopTime = 9e99;
2215 zFwdTo = P("ft");
2216 if( zFwdTo ){
2217 double rStartDate = db_double(0.0,
2218 "SELECT mtime FROM event WHERE objid=%d", d_rid);
@@ -2354,27 +2224,25 @@
2224 if( !haveParameterN ) nEntry = 0;
2225 rStopTime = db_double(9e99,
2226 "SELECT mtime FROM event WHERE objid=%d", ridFwdTo);
2227 }
2228 }
2229 if( rStopTime<9e99 ){
2230 rStopTime += 5.8e-6; /* Round up by 1/2 second */
2231 }
2232 db_multi_exec(
2233 "WITH RECURSIVE dx(rid,mtime) AS (\n"
2234 " SELECT %d, 0\n"
2235 " UNION\n"
2236 " SELECT plink.cid, plink.mtime FROM dx, plink\n"
2237 " WHERE plink.pid=dx.rid\n"
2238 " AND plink.mtime<=%.*g\n"
2239 " ORDER BY 2\n"
2240 ")\n"
2241 "INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d",
2242 d_rid, rStopTime<8e99 ? 17 : 2, rStopTime, nEntry<=0 ? -1 : nEntry+1
2243 );
 
 
 
 
2244 nd = db_int(0, "SELECT count(*)-1 FROM ok");
2245 if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql));
2246 if( nd>0 || p_rid==0 ){
2247 blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
2248 }
@@ -2483,28 +2351,28 @@
2351 if( zChng && *zChng ){
2352 addFileGlobExclusion(zChng, &cond);
2353 tmFlags |= TIMELINE_XMERGE;
2354 }
2355 if( zUses ){
2356 blob_append_sql(&cond, " AND event.objid IN usesfile\n");
2357 }
2358 if( renameOnly ){
2359 blob_append_sql(&cond, " AND event.objid IN rnfile\n");
2360 }
2361 if( forkOnly ){
2362 blob_append_sql(&cond, " AND event.objid IN rnfork\n");
2363 }
2364 if( cpOnly && showCherrypicks ){
2365 db_multi_exec(
2366 "CREATE TEMP TABLE IF NOT EXISTS cpnodes(rid INTEGER PRIMARY KEY);"
2367 "INSERT OR IGNORE INTO cpnodes SELECT childid FROM cherrypick;"
2368 "INSERT OR IGNORE INTO cpnodes SELECT parentid FROM cherrypick;"
2369 );
2370 blob_append_sql(&cond, " AND event.objid IN cpnodes\n");
2371 }
2372 if( bisectLocal || zBisect!=0 ){
2373 blob_append_sql(&cond, " AND event.objid IN (SELECT rid FROM bilog)\n");
2374 }
2375 if( zYearMonth ){
2376 char *zNext;
2377 zYearMonth = timeline_expand_datetime(zYearMonth);
2378 if( strlen(zYearMonth)>7 ){
@@ -2663,39 +2531,24 @@
2531 nEntry = -1;
2532 }
2533 if( zTagSql ){
2534 db_multi_exec(
2535 "CREATE TEMP TABLE selected_nodes(rid INTEGER PRIMARY KEY);"
2536 "INSERT OR IGNORE INTO selected_nodes\n"
2537 " SELECT tagxref.rid FROM tagxref NATURAL JOIN tag\n"
2538 " WHERE tagtype>0\n"
2539 " AND %s", zTagSql/*safe-for-%s*/
2540 );
2541 if( zMark ){
2542 /* If the t=release option is used with m=UUID, then also
2543 ** include the UUID check-in in the display list */
2544 int ridMark = name_to_rid(zMark);
2545 db_multi_exec(
2546 "INSERT OR IGNORE INTO selected_nodes(rid) VALUES(%d)", ridMark);
2547 }
2548 add_extra_rids("selected_nodes",P("x"));
2549 if( related==0 ){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2550 blob_append_sql(&cond, " AND blob.rid IN selected_nodes");
2551 }else{
2552 db_multi_exec(
2553 "CREATE TEMP TABLE related_nodes(rid INTEGER PRIMARY KEY);"
2554 "INSERT INTO related_nodes SELECT rid FROM selected_nodes;"
@@ -2706,40 +2559,42 @@
2559 ** branch to be included in the report. These related check-ins are
2560 ** useful in helping to visualize what has happened on a quiescent
2561 ** branch that is infrequently merged with a much more activate branch.
2562 */
2563 db_multi_exec(
2564 "INSERT OR IGNORE INTO related_nodes\n"
2565 " SELECT pid FROM selected_nodes CROSS JOIN plink\n"
2566 " WHERE selected_nodes.rid=plink.cid;"
2567 );
2568 if( related==1 ){
2569 db_multi_exec(
2570 "INSERT OR IGNORE INTO related_nodes\n"
2571 " SELECT cid FROM selected_nodes CROSS JOIN plink\n"
2572 " WHERE selected_nodes.rid=plink.pid;"
2573 );
2574 if( showCherrypicks ){
2575 db_multi_exec(
2576 "INSERT OR IGNORE INTO related_nodes\n"
2577 " SELECT childid FROM selected_nodes CROSS JOIN cherrypick\n"
2578 " WHERE selected_nodes.rid=cherrypick.parentid;"
2579 );
2580 }
2581 }
2582 if( showCherrypicks ){
2583 db_multi_exec(
2584 "INSERT OR IGNORE INTO related_nodes\n"
2585 " SELECT parentid FROM selected_nodes CROSS JOIN cherrypick\n"
2586 " WHERE selected_nodes.rid=cherrypick.childid;"
2587 );
2588 }
2589 if( (tmFlags & TIMELINE_UNHIDE)==0 ){
2590 db_multi_exec(
2591 "DELETE FROM related_nodes\n"
2592 " WHERE rid IN (SELECT related_nodes.rid\n"
2593 " FROM related_nodes, tagxref\n"
2594 " WHERE tagid=%d AND tagtype>0\n"
2595 " AND tagxref.rid=related_nodes.rid)",
2596 TAG_HIDDEN
2597 );
2598 }
2599 }
2600 }
@@ -2829,45 +2684,42 @@
2684 rCirca = symbolic_name_to_mtime(zCirca, &zCirca);
2685 blob_append_sql(&sql, "%s", blob_sql_text(&cond));
2686 if( rAfter>0.0 ){
2687 if( rBefore>0.0 ){
2688 blob_append_sql(&sql,
2689 " AND event.mtime>=%.17g AND event.mtime<=%.17g\n"
2690 " ORDER BY event.mtime ASC", rAfter-ONE_SECOND, rBefore+ONE_SECOND);
2691 nEntry = -1;
2692 }else{
2693 blob_append_sql(&sql,
2694 " AND event.mtime>=%.17g\n ORDER BY event.mtime ASC",
2695 rAfter-ONE_SECOND);
2696 }
2697 zCirca = 0;
2698 url_add_parameter(&url, "c", 0);
2699 }else if( rBefore>0.0 ){
2700 blob_append_sql(&sql,
2701 " AND event.mtime<=%.17g\n ORDER BY event.mtime DESC",
2702 rBefore+ONE_SECOND);
2703 zCirca = 0;
2704 url_add_parameter(&url, "c", 0);
2705 }else if( rCirca>0.0 ){
2706 Blob sql2;
2707 blob_init(&sql2, blob_sql_text(&sql), -1);
2708 blob_append_sql(&sql2,
2709 " AND event.mtime>=%f\n ORDER BY event.mtime ASC", rCirca);
2710 if( nEntry>0 ){
2711 blob_append_sql(&sql2," LIMIT %d", (nEntry+1)/2);
2712 }
 
 
 
2713 db_multi_exec("%s", blob_sql_text(&sql2));
2714 if( nEntry>0 ){
2715 nEntry -= db_int(0,"select count(*) from timeline");
2716 if( nEntry<=0 ) nEntry = 1;
2717 }
2718 blob_reset(&sql2);
2719 blob_append_sql(&sql,
2720 " AND event.mtime<=%f\n ORDER BY event.mtime DESC",
2721 rCirca
2722 );
2723 if( zMark==0 ) zMark = zCirca;
2724 }else{
2725 blob_append_sql(&sql, " ORDER BY event.mtime DESC");
@@ -3010,12 +2862,14 @@
2862 style_submenu_multichoice("ms", count(azMatchStyles)/2,azMatchStyles,0);
2863 }
2864 }
2865 blob_zero(&cond);
2866 }
2867 if( showSql ){
2868 db_append_dml_to_blob(0);
2869 @ <pre>%h(blob_str(&allSql))</pre>
2870 blob_reset(&allSql);
2871 }
2872 if( search_restrict(SRCH_CKIN)!=0 ){
2873 style_submenu_element("Search", "%R/search?y=c");
2874 }
2875 if( advancedMenu ){
@@ -3069,18 +2923,36 @@
2923 if( zNewerButton ){
2924 @ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\
2925 @ &nbsp;&uarr;</a>
2926 }
2927 cgi_check_for_malice();
2928 {
2929 Matcher *pLeftBranch;
2930 const char *zPattern = P("sl");
2931 if( zPattern!=0 ){
2932 MatchStyle ms;
2933 if( zMatchStyle!=0 ){
2934 ms = matchStyle;
2935 }else{
2936 ms = strpbrk(zPattern,"*[?")!=0 ? MS_GLOB : MS_BRLIST;
2937 }
2938 pLeftBranch = match_create(ms,zPattern);
2939 }else{
2940 pLeftBranch = match_create(matchStyle, zBrName?zBrName:zTagName);
2941 }
2942 www_print_timeline(&q, tmFlags, zThisUser, zThisTag, pLeftBranch,
2943 selectedRid, secondaryRid, 0);
2944 match_free(pLeftBranch);
2945 }
2946 db_finalize(&q);
2947 if( zOlderButton ){
2948 @ %z(chref("button","%s",zOlderButton))%h(zOlderButtonLabel)\
2949 @ &nbsp;&darr;</a>
2950 }
2951 document_emit_js(/*handles pikchrs rendered above*/);
2952 blob_reset(&sql);
2953 blob_reset(&desc);
2954 style_finish_page();
2955 }
2956
2957 /*
2958 ** Translate a timeline entry into the printable format by
2959
+6 -7
--- src/update.c
+++ src/update.c
@@ -514,14 +514,10 @@
514514
zDir = zNext;
515515
}
516516
}
517517
}
518518
}else if( idt>0 && idv>0 && ridt!=ridv && chnged ){
519
- /* Merge the changes in the current tree into the target version */
520
- Blob r, t, v;
521
- int rc;
522
-
523519
if( nameChng ){
524520
fossil_print("MERGE %s -> %s\n", zName, zNewName);
525521
}else{
526522
fossil_print("MERGE %s\n", zName);
527523
}
@@ -528,10 +524,13 @@
528524
if( islinkv || islinkt ){
529525
fossil_print("***** Cannot merge symlink %s\n", zNewName);
530526
zOp = "CONFLICT";
531527
nConflict++;
532528
}else{
529
+ /* Merge the changes in the current tree into the target version */
530
+ Blob r, t, v;
531
+ int rc;
533532
unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0;
534533
if(keepMergeFlag!=0) mergeFlags |= MERGE_KEEP_FILES;
535534
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
536535
content_get(ridt, &t);
537536
content_get(ridv, &v);
@@ -571,15 +570,15 @@
571570
nConflict++;
572571
zOp = "ERROR";
573572
zErrMsg = "cannot merge binary file";
574573
nc = 1;
575574
}
575
+ blob_reset(&v);
576
+ blob_reset(&t);
577
+ blob_reset(&r);
576578
}
577579
if( nameChng && !dryRunFlag ) file_delete(zFullPath);
578
- blob_reset(&v);
579
- blob_reset(&t);
580
- blob_reset(&r);
581580
}else{
582581
nUpdate--;
583582
if( chnged ){
584583
if( verboseFlag ) fossil_print("EDITED %s\n", zName);
585584
}else{
586585
--- src/update.c
+++ src/update.c
@@ -514,14 +514,10 @@
514 zDir = zNext;
515 }
516 }
517 }
518 }else if( idt>0 && idv>0 && ridt!=ridv && chnged ){
519 /* Merge the changes in the current tree into the target version */
520 Blob r, t, v;
521 int rc;
522
523 if( nameChng ){
524 fossil_print("MERGE %s -> %s\n", zName, zNewName);
525 }else{
526 fossil_print("MERGE %s\n", zName);
527 }
@@ -528,10 +524,13 @@
528 if( islinkv || islinkt ){
529 fossil_print("***** Cannot merge symlink %s\n", zNewName);
530 zOp = "CONFLICT";
531 nConflict++;
532 }else{
 
 
 
533 unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0;
534 if(keepMergeFlag!=0) mergeFlags |= MERGE_KEEP_FILES;
535 if( !dryRunFlag && !internalUpdate ) undo_save(zName);
536 content_get(ridt, &t);
537 content_get(ridv, &v);
@@ -571,15 +570,15 @@
571 nConflict++;
572 zOp = "ERROR";
573 zErrMsg = "cannot merge binary file";
574 nc = 1;
575 }
 
 
 
576 }
577 if( nameChng && !dryRunFlag ) file_delete(zFullPath);
578 blob_reset(&v);
579 blob_reset(&t);
580 blob_reset(&r);
581 }else{
582 nUpdate--;
583 if( chnged ){
584 if( verboseFlag ) fossil_print("EDITED %s\n", zName);
585 }else{
586
--- src/update.c
+++ src/update.c
@@ -514,14 +514,10 @@
514 zDir = zNext;
515 }
516 }
517 }
518 }else if( idt>0 && idv>0 && ridt!=ridv && chnged ){
 
 
 
 
519 if( nameChng ){
520 fossil_print("MERGE %s -> %s\n", zName, zNewName);
521 }else{
522 fossil_print("MERGE %s\n", zName);
523 }
@@ -528,10 +524,13 @@
524 if( islinkv || islinkt ){
525 fossil_print("***** Cannot merge symlink %s\n", zNewName);
526 zOp = "CONFLICT";
527 nConflict++;
528 }else{
529 /* Merge the changes in the current tree into the target version */
530 Blob r, t, v;
531 int rc;
532 unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0;
533 if(keepMergeFlag!=0) mergeFlags |= MERGE_KEEP_FILES;
534 if( !dryRunFlag && !internalUpdate ) undo_save(zName);
535 content_get(ridt, &t);
536 content_get(ridv, &v);
@@ -571,15 +570,15 @@
570 nConflict++;
571 zOp = "ERROR";
572 zErrMsg = "cannot merge binary file";
573 nc = 1;
574 }
575 blob_reset(&v);
576 blob_reset(&t);
577 blob_reset(&r);
578 }
579 if( nameChng && !dryRunFlag ) file_delete(zFullPath);
 
 
 
580 }else{
581 nUpdate--;
582 if( chnged ){
583 if( verboseFlag ) fossil_print("EDITED %s\n", zName);
584 }else{
585
+55 -2
--- src/xfer.c
+++ src/xfer.c
@@ -1041,10 +1041,43 @@
10411041
}
10421042
db_finalize(&q);
10431043
if( cnt==0 ) pXfer->resync = 0;
10441044
return cnt;
10451045
}
1046
+
1047
+/*
1048
+** Send an igot message for every cluster artifact that is not a phantom,
1049
+** is not shunned, is not private, and that is not in the UNCLUSTERED table.
1050
+** Return the number of cards sent.
1051
+*/
1052
+static int send_all_clusters(Xfer *pXfer){
1053
+ Stmt q;
1054
+ int cnt = 0;
1055
+ const char *zExtra;
1056
+ if( db_table_exists("temp","onremote") ){
1057
+ zExtra = " AND NOT EXISTS(SELECT 1 FROM onremote WHERE rid=blob.rid)";
1058
+ }else{
1059
+ zExtra = "";
1060
+ }
1061
+ db_prepare(&q,
1062
+ "SELECT uuid"
1063
+ " FROM tagxref JOIN blob ON tagxref.rid=blob.rid AND tagxref.tagid=%d"
1064
+ " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
1065
+ " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)"
1066
+ " AND NOT EXISTS(SELECT 1 FROM unclustered WHERE rid=blob.rid)"
1067
+ " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)%s",
1068
+ TAG_CLUSTER, zExtra /*safe-for-%s*/
1069
+ );
1070
+ while( db_step(&q)==SQLITE_ROW ){
1071
+ if( cnt==0 ) blob_appendf(pXfer->pOut, "# sending-clusters\n");
1072
+ blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
1073
+ cnt++;
1074
+ }
1075
+ db_finalize(&q);
1076
+ if( cnt ) blob_appendf(pXfer->pOut, "# end-of-clusters\n");
1077
+ return cnt;
1078
+}
10461079
10471080
/*
10481081
** Send an igot message for every artifact.
10491082
*/
10501083
static void send_all(Xfer *pXfer){
@@ -1781,10 +1814,19 @@
17811814
** The client sends this message to the server to ask the server
17821815
** to tell it about alternative repositories in the reply.
17831816
*/
17841817
if( blob_eq(&xfer.aToken[1], "req-links") ){
17851818
bSendLinks = 1;
1819
+ }else
1820
+
1821
+ /* pragma req-clusters
1822
+ **
1823
+ ** This pragma requests that the server send igot cards for every
1824
+ ** cluster artifact that it knows about.
1825
+ */
1826
+ if( blob_eq(&xfer.aToken[1], "req-clusters") ){
1827
+ send_all_clusters(&xfer);
17861828
}
17871829
17881830
}else
17891831
17901832
/* Unknown message
@@ -1981,11 +2023,11 @@
19812023
int nCardSent = 0; /* Number of cards sent */
19822024
int nCardRcvd = 0; /* Number of cards received */
19832025
int nCycle = 0; /* Number of round trips to the server */
19842026
int size; /* Size of a config value or uvfile */
19852027
int origConfigRcvMask; /* Original value of configRcvMask */
1986
- int nFileRecv; /* Number of files received */
2028
+ int nFileRecv = 0; /* Number of files received */
19872029
int mxPhantomReq = 200; /* Max number of phantoms to request per comm */
19882030
const char *zCookie; /* Server cookie */
19892031
i64 nUncSent, nUncRcvd; /* Bytes sent and received (before compression) */
19902032
i64 nSent, nRcvd; /* Bytes sent and received (after compression) */
19912033
int cloneSeqno = 1; /* Sequence number for clones */
@@ -2007,10 +2049,11 @@
20072049
int uvHashSent = 0; /* The "pragma uv-hash" message has been sent */
20082050
int uvDoPush = 0; /* Generate uvfile messages to send to server */
20092051
int uvPullOnly = 0; /* 1: pull-only. 2: pull-only warning issued */
20102052
int nUvGimmeSent = 0; /* Number of uvgimme cards sent on this cycle */
20112053
int nUvFileRcvd = 0; /* Number of uvfile cards received on this cycle */
2054
+ int nGimmeRcvd = 0; /* Number of gimme cards recevied on the prev cycle */
20122055
sqlite3_int64 mtime; /* Modification time on a UV file */
20132056
int autopushFailed = 0; /* Autopush following commit failed if true */
20142057
const char *zCkinLock; /* Name of check-in to lock. NULL for none */
20152058
const char *zClientId; /* A unique identifier for this check-out */
20162059
unsigned int mHttpFlags;/* Flags for the http_exchange() subsystem */
@@ -2181,15 +2224,21 @@
21812224
*/
21822225
if( (syncFlags & SYNC_PULL)!=0
21832226
|| ((syncFlags & SYNC_CLONE)!=0 && cloneSeqno==1)
21842227
){
21852228
request_phantoms(&xfer, mxPhantomReq);
2229
+ if( xfer.nGimmeSent>0 && nCycle==2 && (syncFlags & SYNC_PULL)!=0 ){
2230
+ blob_appendf(&send, "pragma req-clusters\n");
2231
+ }
21862232
}
21872233
if( syncFlags & SYNC_PUSH ){
21882234
send_unsent(&xfer);
21892235
nCardSent += send_unclustered(&xfer);
21902236
if( syncFlags & SYNC_PRIVATE ) send_private(&xfer);
2237
+ if( nGimmeRcvd>0 && nCycle==2 ){
2238
+ send_all_clusters(&xfer);
2239
+ }
21912240
}
21922241
21932242
/* Client sends configuration parameter requests. On a clone, delay sending
21942243
** this until the second cycle since the login card might fail on
21952244
** the first cycle.
@@ -2369,10 +2418,11 @@
23692418
nCardSent++;
23702419
}
23712420
go = 0;
23722421
nUvGimmeSent = 0;
23732422
nUvFileRcvd = 0;
2423
+ nGimmeRcvd = 0;
23742424
nPriorArtifact = nArtifactRcvd;
23752425
23762426
/* Process the reply that came back from the server */
23772427
while( blob_line(&recv, &xfer.line) ){
23782428
if( blob_buffer(&xfer.line)[0]=='#' ){
@@ -2449,11 +2499,14 @@
24492499
&& blob_is_hname(&xfer.aToken[1])
24502500
){
24512501
remote_unk(&xfer.aToken[1]);
24522502
if( syncFlags & SYNC_PUSH ){
24532503
int rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
2454
- if( rid ) send_file(&xfer, rid, &xfer.aToken[1], 0);
2504
+ if( rid ){
2505
+ send_file(&xfer, rid, &xfer.aToken[1], 0);
2506
+ nGimmeRcvd++;
2507
+ }
24552508
}
24562509
}else
24572510
24582511
/* igot HASH ?PRIVATEFLAG?
24592512
**
24602513
--- src/xfer.c
+++ src/xfer.c
@@ -1041,10 +1041,43 @@
1041 }
1042 db_finalize(&q);
1043 if( cnt==0 ) pXfer->resync = 0;
1044 return cnt;
1045 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1046
1047 /*
1048 ** Send an igot message for every artifact.
1049 */
1050 static void send_all(Xfer *pXfer){
@@ -1781,10 +1814,19 @@
1781 ** The client sends this message to the server to ask the server
1782 ** to tell it about alternative repositories in the reply.
1783 */
1784 if( blob_eq(&xfer.aToken[1], "req-links") ){
1785 bSendLinks = 1;
 
 
 
 
 
 
 
 
 
1786 }
1787
1788 }else
1789
1790 /* Unknown message
@@ -1981,11 +2023,11 @@
1981 int nCardSent = 0; /* Number of cards sent */
1982 int nCardRcvd = 0; /* Number of cards received */
1983 int nCycle = 0; /* Number of round trips to the server */
1984 int size; /* Size of a config value or uvfile */
1985 int origConfigRcvMask; /* Original value of configRcvMask */
1986 int nFileRecv; /* Number of files received */
1987 int mxPhantomReq = 200; /* Max number of phantoms to request per comm */
1988 const char *zCookie; /* Server cookie */
1989 i64 nUncSent, nUncRcvd; /* Bytes sent and received (before compression) */
1990 i64 nSent, nRcvd; /* Bytes sent and received (after compression) */
1991 int cloneSeqno = 1; /* Sequence number for clones */
@@ -2007,10 +2049,11 @@
2007 int uvHashSent = 0; /* The "pragma uv-hash" message has been sent */
2008 int uvDoPush = 0; /* Generate uvfile messages to send to server */
2009 int uvPullOnly = 0; /* 1: pull-only. 2: pull-only warning issued */
2010 int nUvGimmeSent = 0; /* Number of uvgimme cards sent on this cycle */
2011 int nUvFileRcvd = 0; /* Number of uvfile cards received on this cycle */
 
2012 sqlite3_int64 mtime; /* Modification time on a UV file */
2013 int autopushFailed = 0; /* Autopush following commit failed if true */
2014 const char *zCkinLock; /* Name of check-in to lock. NULL for none */
2015 const char *zClientId; /* A unique identifier for this check-out */
2016 unsigned int mHttpFlags;/* Flags for the http_exchange() subsystem */
@@ -2181,15 +2224,21 @@
2181 */
2182 if( (syncFlags & SYNC_PULL)!=0
2183 || ((syncFlags & SYNC_CLONE)!=0 && cloneSeqno==1)
2184 ){
2185 request_phantoms(&xfer, mxPhantomReq);
 
 
 
2186 }
2187 if( syncFlags & SYNC_PUSH ){
2188 send_unsent(&xfer);
2189 nCardSent += send_unclustered(&xfer);
2190 if( syncFlags & SYNC_PRIVATE ) send_private(&xfer);
 
 
 
2191 }
2192
2193 /* Client sends configuration parameter requests. On a clone, delay sending
2194 ** this until the second cycle since the login card might fail on
2195 ** the first cycle.
@@ -2369,10 +2418,11 @@
2369 nCardSent++;
2370 }
2371 go = 0;
2372 nUvGimmeSent = 0;
2373 nUvFileRcvd = 0;
 
2374 nPriorArtifact = nArtifactRcvd;
2375
2376 /* Process the reply that came back from the server */
2377 while( blob_line(&recv, &xfer.line) ){
2378 if( blob_buffer(&xfer.line)[0]=='#' ){
@@ -2449,11 +2499,14 @@
2449 && blob_is_hname(&xfer.aToken[1])
2450 ){
2451 remote_unk(&xfer.aToken[1]);
2452 if( syncFlags & SYNC_PUSH ){
2453 int rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
2454 if( rid ) send_file(&xfer, rid, &xfer.aToken[1], 0);
 
 
 
2455 }
2456 }else
2457
2458 /* igot HASH ?PRIVATEFLAG?
2459 **
2460
--- src/xfer.c
+++ src/xfer.c
@@ -1041,10 +1041,43 @@
1041 }
1042 db_finalize(&q);
1043 if( cnt==0 ) pXfer->resync = 0;
1044 return cnt;
1045 }
1046
1047 /*
1048 ** Send an igot message for every cluster artifact that is not a phantom,
1049 ** is not shunned, is not private, and that is not in the UNCLUSTERED table.
1050 ** Return the number of cards sent.
1051 */
1052 static int send_all_clusters(Xfer *pXfer){
1053 Stmt q;
1054 int cnt = 0;
1055 const char *zExtra;
1056 if( db_table_exists("temp","onremote") ){
1057 zExtra = " AND NOT EXISTS(SELECT 1 FROM onremote WHERE rid=blob.rid)";
1058 }else{
1059 zExtra = "";
1060 }
1061 db_prepare(&q,
1062 "SELECT uuid"
1063 " FROM tagxref JOIN blob ON tagxref.rid=blob.rid AND tagxref.tagid=%d"
1064 " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
1065 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)"
1066 " AND NOT EXISTS(SELECT 1 FROM unclustered WHERE rid=blob.rid)"
1067 " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)%s",
1068 TAG_CLUSTER, zExtra /*safe-for-%s*/
1069 );
1070 while( db_step(&q)==SQLITE_ROW ){
1071 if( cnt==0 ) blob_appendf(pXfer->pOut, "# sending-clusters\n");
1072 blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
1073 cnt++;
1074 }
1075 db_finalize(&q);
1076 if( cnt ) blob_appendf(pXfer->pOut, "# end-of-clusters\n");
1077 return cnt;
1078 }
1079
1080 /*
1081 ** Send an igot message for every artifact.
1082 */
1083 static void send_all(Xfer *pXfer){
@@ -1781,10 +1814,19 @@
1814 ** The client sends this message to the server to ask the server
1815 ** to tell it about alternative repositories in the reply.
1816 */
1817 if( blob_eq(&xfer.aToken[1], "req-links") ){
1818 bSendLinks = 1;
1819 }else
1820
1821 /* pragma req-clusters
1822 **
1823 ** This pragma requests that the server send igot cards for every
1824 ** cluster artifact that it knows about.
1825 */
1826 if( blob_eq(&xfer.aToken[1], "req-clusters") ){
1827 send_all_clusters(&xfer);
1828 }
1829
1830 }else
1831
1832 /* Unknown message
@@ -1981,11 +2023,11 @@
2023 int nCardSent = 0; /* Number of cards sent */
2024 int nCardRcvd = 0; /* Number of cards received */
2025 int nCycle = 0; /* Number of round trips to the server */
2026 int size; /* Size of a config value or uvfile */
2027 int origConfigRcvMask; /* Original value of configRcvMask */
2028 int nFileRecv = 0; /* Number of files received */
2029 int mxPhantomReq = 200; /* Max number of phantoms to request per comm */
2030 const char *zCookie; /* Server cookie */
2031 i64 nUncSent, nUncRcvd; /* Bytes sent and received (before compression) */
2032 i64 nSent, nRcvd; /* Bytes sent and received (after compression) */
2033 int cloneSeqno = 1; /* Sequence number for clones */
@@ -2007,10 +2049,11 @@
2049 int uvHashSent = 0; /* The "pragma uv-hash" message has been sent */
2050 int uvDoPush = 0; /* Generate uvfile messages to send to server */
2051 int uvPullOnly = 0; /* 1: pull-only. 2: pull-only warning issued */
2052 int nUvGimmeSent = 0; /* Number of uvgimme cards sent on this cycle */
2053 int nUvFileRcvd = 0; /* Number of uvfile cards received on this cycle */
2054 int nGimmeRcvd = 0; /* Number of gimme cards recevied on the prev cycle */
2055 sqlite3_int64 mtime; /* Modification time on a UV file */
2056 int autopushFailed = 0; /* Autopush following commit failed if true */
2057 const char *zCkinLock; /* Name of check-in to lock. NULL for none */
2058 const char *zClientId; /* A unique identifier for this check-out */
2059 unsigned int mHttpFlags;/* Flags for the http_exchange() subsystem */
@@ -2181,15 +2224,21 @@
2224 */
2225 if( (syncFlags & SYNC_PULL)!=0
2226 || ((syncFlags & SYNC_CLONE)!=0 && cloneSeqno==1)
2227 ){
2228 request_phantoms(&xfer, mxPhantomReq);
2229 if( xfer.nGimmeSent>0 && nCycle==2 && (syncFlags & SYNC_PULL)!=0 ){
2230 blob_appendf(&send, "pragma req-clusters\n");
2231 }
2232 }
2233 if( syncFlags & SYNC_PUSH ){
2234 send_unsent(&xfer);
2235 nCardSent += send_unclustered(&xfer);
2236 if( syncFlags & SYNC_PRIVATE ) send_private(&xfer);
2237 if( nGimmeRcvd>0 && nCycle==2 ){
2238 send_all_clusters(&xfer);
2239 }
2240 }
2241
2242 /* Client sends configuration parameter requests. On a clone, delay sending
2243 ** this until the second cycle since the login card might fail on
2244 ** the first cycle.
@@ -2369,10 +2418,11 @@
2418 nCardSent++;
2419 }
2420 go = 0;
2421 nUvGimmeSent = 0;
2422 nUvFileRcvd = 0;
2423 nGimmeRcvd = 0;
2424 nPriorArtifact = nArtifactRcvd;
2425
2426 /* Process the reply that came back from the server */
2427 while( blob_line(&recv, &xfer.line) ){
2428 if( blob_buffer(&xfer.line)[0]=='#' ){
@@ -2449,11 +2499,14 @@
2499 && blob_is_hname(&xfer.aToken[1])
2500 ){
2501 remote_unk(&xfer.aToken[1]);
2502 if( syncFlags & SYNC_PUSH ){
2503 int rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
2504 if( rid ){
2505 send_file(&xfer, rid, &xfer.aToken[1], 0);
2506 nGimmeRcvd++;
2507 }
2508 }
2509 }else
2510
2511 /* igot HASH ?PRIVATEFLAG?
2512 **
2513
--- test/merge1.test
+++ test/merge1.test
@@ -75,10 +75,12 @@
7575
555 - we think it well and other stuff too - 5555
7676
}
7777
write_file_indented t23 {
7878
<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
7979
111 - This is line ONE of the demo program - 1111
80
+ ####### SUGGESTED CONFLICT RESOLUTION follows ###################
81
+ 111 - This is line ONE OF the demo program - 1111
8082
||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
8183
111 - This is line one of the demo program - 1111
8284
======= MERGED IN content follows =============================== (line 1)
8385
111 - This is line one OF the demo program - 1111
8486
>>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@@ -88,10 +90,12 @@
8890
555 - we think it well and other stuff too - 5555
8991
}
9092
write_file_indented t32 {
9193
<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
9294
111 - This is line one OF the demo program - 1111
95
+ ####### SUGGESTED CONFLICT RESOLUTION follows ###################
96
+ 111 - This is line ONE OF the demo program - 1111
9397
||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
9498
111 - This is line one of the demo program - 1111
9599
======= MERGED IN content follows =============================== (line 1)
96100
111 - This is line ONE of the demo program - 1111
97101
>>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@@ -159,10 +163,13 @@
159163
444 - If all goes well, we will be pleased - 4444
160164
555 - we think it well and other stuff too - 5555
161165
}
162166
write_file_indented t32 {
163167
<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
168
+ ####### SUGGESTED CONFLICT RESOLUTION follows ###################
169
+ 000 - Zero lines added to the beginning of - 0000
170
+ 111 - This is line one of the demo program - 1111
164171
||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
165172
111 - This is line one of the demo program - 1111
166173
======= MERGED IN content follows =============================== (line 1)
167174
000 - Zero lines added to the beginning of - 0000
168175
111 - This is line one of the demo program - 1111
@@ -305,10 +312,19 @@
305312
mnop 2
306313
qrst
307314
uvwx
308315
yzAB 2
309316
CDEF 2
317
+ GHIJ 2
318
+ ####### SUGGESTED CONFLICT RESOLUTION follows ###################
319
+ efgh 2
320
+ ijkl 2
321
+ mnop 3
322
+ qrst 3
323
+ uvwx 3
324
+ yzAB 3
325
+ CDEF 2
310326
GHIJ 2
311327
||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2)
312328
efgh
313329
ijkl
314330
mnop
@@ -372,10 +388,19 @@
372388
ijkl 2
373389
mnop
374390
qrst
375391
uvwx
376392
yzAB 2
393
+ CDEF 2
394
+ GHIJ 2
395
+ ####### SUGGESTED CONFLICT RESOLUTION follows ###################
396
+ efgh 2
397
+ ijkl 2
398
+ mnop 3
399
+ qrst 3
400
+ uvwx 3
401
+ yzAB 3
377402
CDEF 2
378403
GHIJ 2
379404
||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2)
380405
efgh
381406
ijkl
382407
--- test/merge1.test
+++ test/merge1.test
@@ -75,10 +75,12 @@
75 555 - we think it well and other stuff too - 5555
76 }
77 write_file_indented t23 {
78 <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
79 111 - This is line ONE of the demo program - 1111
 
 
80 ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
81 111 - This is line one of the demo program - 1111
82 ======= MERGED IN content follows =============================== (line 1)
83 111 - This is line one OF the demo program - 1111
84 >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@@ -88,10 +90,12 @@
88 555 - we think it well and other stuff too - 5555
89 }
90 write_file_indented t32 {
91 <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
92 111 - This is line one OF the demo program - 1111
 
 
93 ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
94 111 - This is line one of the demo program - 1111
95 ======= MERGED IN content follows =============================== (line 1)
96 111 - This is line ONE of the demo program - 1111
97 >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@@ -159,10 +163,13 @@
159 444 - If all goes well, we will be pleased - 4444
160 555 - we think it well and other stuff too - 5555
161 }
162 write_file_indented t32 {
163 <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
 
 
 
164 ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
165 111 - This is line one of the demo program - 1111
166 ======= MERGED IN content follows =============================== (line 1)
167 000 - Zero lines added to the beginning of - 0000
168 111 - This is line one of the demo program - 1111
@@ -305,10 +312,19 @@
305 mnop 2
306 qrst
307 uvwx
308 yzAB 2
309 CDEF 2
 
 
 
 
 
 
 
 
 
310 GHIJ 2
311 ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2)
312 efgh
313 ijkl
314 mnop
@@ -372,10 +388,19 @@
372 ijkl 2
373 mnop
374 qrst
375 uvwx
376 yzAB 2
 
 
 
 
 
 
 
 
 
377 CDEF 2
378 GHIJ 2
379 ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2)
380 efgh
381 ijkl
382
--- test/merge1.test
+++ test/merge1.test
@@ -75,10 +75,12 @@
75 555 - we think it well and other stuff too - 5555
76 }
77 write_file_indented t23 {
78 <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
79 111 - This is line ONE of the demo program - 1111
80 ####### SUGGESTED CONFLICT RESOLUTION follows ###################
81 111 - This is line ONE OF the demo program - 1111
82 ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
83 111 - This is line one of the demo program - 1111
84 ======= MERGED IN content follows =============================== (line 1)
85 111 - This is line one OF the demo program - 1111
86 >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@@ -88,10 +90,12 @@
90 555 - we think it well and other stuff too - 5555
91 }
92 write_file_indented t32 {
93 <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
94 111 - This is line one OF the demo program - 1111
95 ####### SUGGESTED CONFLICT RESOLUTION follows ###################
96 111 - This is line ONE OF the demo program - 1111
97 ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
98 111 - This is line one of the demo program - 1111
99 ======= MERGED IN content follows =============================== (line 1)
100 111 - This is line ONE of the demo program - 1111
101 >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
@@ -159,10 +163,13 @@
163 444 - If all goes well, we will be pleased - 4444
164 555 - we think it well and other stuff too - 5555
165 }
166 write_file_indented t32 {
167 <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
168 ####### SUGGESTED CONFLICT RESOLUTION follows ###################
169 000 - Zero lines added to the beginning of - 0000
170 111 - This is line one of the demo program - 1111
171 ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
172 111 - This is line one of the demo program - 1111
173 ======= MERGED IN content follows =============================== (line 1)
174 000 - Zero lines added to the beginning of - 0000
175 111 - This is line one of the demo program - 1111
@@ -305,10 +312,19 @@
312 mnop 2
313 qrst
314 uvwx
315 yzAB 2
316 CDEF 2
317 GHIJ 2
318 ####### SUGGESTED CONFLICT RESOLUTION follows ###################
319 efgh 2
320 ijkl 2
321 mnop 3
322 qrst 3
323 uvwx 3
324 yzAB 3
325 CDEF 2
326 GHIJ 2
327 ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2)
328 efgh
329 ijkl
330 mnop
@@ -372,10 +388,19 @@
388 ijkl 2
389 mnop
390 qrst
391 uvwx
392 yzAB 2
393 CDEF 2
394 GHIJ 2
395 ####### SUGGESTED CONFLICT RESOLUTION follows ###################
396 efgh 2
397 ijkl 2
398 mnop 3
399 qrst 3
400 uvwx 3
401 yzAB 3
402 CDEF 2
403 GHIJ 2
404 ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2)
405 efgh
406 ijkl
407
+28 -25
--- test/merge3.test
+++ test/merge3.test
@@ -27,10 +27,13 @@
2727
fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args
2828
set x [read_file t4]
2929
regsub -all \
3030
{<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <+ \(line \d+\)} \
3131
$x {MINE:} x
32
+ regsub -all \
33
+ {####### SUGGESTED CONFLICT RESOLUTION follows #+} \
34
+ $x {BOT:} x
3235
regsub -all \
3336
{\|\|\|\|\|\|\| COMMON ANCESTOR content follows \|+ \(line \d+\)} \
3437
$x {COM:} x
3538
regsub -all \
3639
{======= MERGED IN content follows =+ \(line \d+\)} \
@@ -73,56 +76,56 @@
7376
} {
7477
1 2 3b 4b 5b 6 7 8 9
7578
} {
7679
1 2 3 4 5c 6 7 8 9
7780
} {
78
- 1 2 MINE: 3b 4b 5b COM: 3 4 5 YOURS: 3 4 5c END 6 7 8 9
81
+ 1 2 MINE: 3b 4b 5b BOT: 3b 4b 5c COM: 3 4 5 YOURS: 3 4 5c END 6 7 8 9
7982
} -expectError
8083
merge-test 4 {
8184
1 2 3 4 5 6 7 8 9
8285
} {
8386
1 2 3b 4b 5b 6b 7 8 9
8487
} {
8588
1 2 3 4 5c 6 7 8 9
8689
} {
87
- 1 2 MINE: 3b 4b 5b 6b COM: 3 4 5 6 YOURS: 3 4 5c 6 END 7 8 9
90
+ 1 2 MINE: 3b 4b 5b 6b BOT: 3b 4b 5b 5c 6 COM: 3 4 5 6 YOURS: 3 4 5c 6 END 7 8 9
8891
} -expectError
8992
merge-test 5 {
9093
1 2 3 4 5 6 7 8 9
9194
} {
9295
1 2 3b 4b 5b 6b 7 8 9
9396
} {
9497
1 2 3 4 5c 6c 7c 8 9
9598
} {
96
- 1 2 MINE: 3b 4b 5b 6b 7 COM: 3 4 5 6 7 YOURS: 3 4 5c 6c 7c END 8 9
99
+ 1 2 MINE: 3b 4b 5b 6b 7 BOT: 3b 4b 5b 5c 6c 7c COM: 3 4 5 6 7 YOURS: 3 4 5c 6c 7c END 8 9
97100
} -expectError
98101
merge-test 6 {
99102
1 2 3 4 5 6 7 8 9
100103
} {
101104
1 2 3b 4b 5b 6b 7 8b 9
102105
} {
103106
1 2 3 4 5c 6c 7c 8 9
104107
} {
105
- 1 2 MINE: 3b 4b 5b 6b 7 COM: 3 4 5 6 7 YOURS: 3 4 5c 6c 7c END 8b 9
108
+ 1 2 MINE: 3b 4b 5b 6b 7 BOT: 3b 4b 5b 5c 6c 7c COM: 3 4 5 6 7 YOURS: 3 4 5c 6c 7c END 8b 9
106109
} -expectError
107110
merge-test 7 {
108111
1 2 3 4 5 6 7 8 9
109112
} {
110113
1 2 3b 4b 5b 6b 7 8b 9
111114
} {
112115
1 2 3 4 5c 6c 7c 8c 9
113116
} {
114
- 1 2 MINE: 3b 4b 5b 6b 7 8b COM: 3 4 5 6 7 8 YOURS: 3 4 5c 6c 7c 8c END 9
117
+ 1 2 MINE: 3b 4b 5b 6b 7 8b BOT: 3b 4b 5b 5c 6c 7c 8c COM: 3 4 5 6 7 8 YOURS: 3 4 5c 6c 7c 8c END 9
115118
} -expectError
116119
merge-test 8 {
117120
1 2 3 4 5 6 7 8 9
118121
} {
119122
1 2 3b 4b 5b 6b 7 8b 9b
120123
} {
121124
1 2 3 4 5c 6c 7c 8c 9
122125
} {
123
- 1 2 MINE: 3b 4b 5b 6b 7 8b 9b COM: 3 4 5 6 7 8 9 YOURS: 3 4 5c 6c 7c 8c 9 END
126
+ 1 2 MINE: 3b 4b 5b 6b 7 8b 9b BOT: 3b 4b 5b 5c 6c 7c 8c 9b COM: 3 4 5 6 7 8 9 YOURS: 3 4 5c 6c 7c 8c 9 END
124127
} -expectError
125128
merge-test 9 {
126129
1 2 3 4 5 6 7 8 9
127130
} {
128131
1 2 3b 4b 5 6 7 8b 9b
@@ -146,11 +149,11 @@
146149
} {
147150
1 2 3b 4b 5 6 7 8b 9b
148151
} {
149152
1 2 3b 4c 5 6c 7c 8 9
150153
} {
151
- 1 2 MINE: 3b 4b COM: 3 4 YOURS: 3b 4c END 5 6c 7c 8b 9b
154
+ 1 2 MINE: 3b 4b BOT: 3b 4c COM: 3 4 YOURS: 3b 4c END 5 6c 7c 8b 9b
152155
} -expectError
153156
merge-test 12 {
154157
1 2 3 4 5 6 7 8 9
155158
} {
156159
1 2 3b4b 5 6 7 8b 9b
@@ -201,20 +204,20 @@
201204
} {
202205
1 6 7 8 9
203206
} {
204207
1 2 3 4 9
205208
} {
206
- 1 MINE: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9
209
+ 1 MINE: 6 7 8 BOT: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9
207210
} -expectError
208211
merge-test 25 {
209212
1 2 3 4 5 6 7 8 9
210213
} {
211214
1 7 8 9
212215
} {
213216
1 2 3 9
214217
} {
215
- 1 MINE: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9
218
+ 1 MINE: 7 8 BOT: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9
216219
} -expectError
217220
218221
merge-test 30 {
219222
1 2 3 4 5 6 7 8 9
220223
} {
@@ -256,20 +259,20 @@
256259
} {
257260
1 2 3 4 9
258261
} {
259262
1 6 7 8 9
260263
} {
261
- 1 MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END 9
264
+ 1 MINE: 2 3 4 BOT: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END 9
262265
} -expectError
263266
merge-test 35 {
264267
1 2 3 4 5 6 7 8 9
265268
} {
266269
1 2 3 9
267270
} {
268271
1 7 8 9
269272
} {
270
- 1 MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END 9
273
+ 1 MINE: 2 3 BOT: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END 9
271274
} -expectError
272275
273276
merge-test 40 {
274277
2 3 4 5 6 7 8
275278
} {
@@ -311,20 +314,20 @@
311314
} {
312315
6 7 8
313316
} {
314317
2 3 4
315318
} {
316
- MINE: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END
319
+ MINE: 6 7 8 BOT: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END
317320
} -expectError
318321
merge-test 45 {
319322
2 3 4 5 6 7 8
320323
} {
321324
7 8
322325
} {
323326
2 3
324327
} {
325
- MINE: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END
328
+ MINE: 7 8 BOT: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END
326329
} -expectError
327330
328331
merge-test 50 {
329332
2 3 4 5 6 7 8
330333
} {
@@ -365,20 +368,20 @@
365368
} {
366369
2 3 4
367370
} {
368371
6 7 8
369372
} {
370
- MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END
373
+ MINE: 2 3 4 BOT: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END
371374
} -expectError
372375
merge-test 55 {
373376
2 3 4 5 6 7 8
374377
} {
375378
2 3
376379
} {
377380
7 8
378381
} {
379
- MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END
382
+ MINE: 2 3 BOT: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END
380383
} -expectError
381384
382385
merge-test 60 {
383386
1 2 3 4 5 6 7 8 9
384387
} {
@@ -420,20 +423,20 @@
420423
} {
421424
1 2b 3b 4b 5b 6 7 8 9
422425
} {
423426
1 2 3 4 9
424427
} {
425
- 1 MINE: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9
428
+ 1 MINE: 2b 3b 4b 5b 6 7 8 BOT: 2b 3b 4b 4 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9
426429
} -expectError
427430
merge-test 65 {
428431
1 2 3 4 5 6 7 8 9
429432
} {
430433
1 2b 3b 4b 5b 6b 7 8 9
431434
} {
432435
1 2 3 9
433436
} {
434
- 1 MINE: 2b 3b 4b 5b 6b 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9
437
+ 1 MINE: 2b 3b 4b 5b 6b 7 8 BOT: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9
435438
} -expectError
436439
437440
merge-test 70 {
438441
1 2 3 4 5 6 7 8 9
439442
} {
@@ -475,20 +478,20 @@
475478
} {
476479
1 2 3 4 9
477480
} {
478481
1 2b 3b 4b 5b 6 7 8 9
479482
} {
480
- 1 MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END 9
483
+ 1 MINE: 2 3 4 BOT: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END 9
481484
} -expectError
482485
merge-test 75 {
483486
1 2 3 4 5 6 7 8 9
484487
} {
485488
1 2 3 9
486489
} {
487490
1 2b 3b 4b 5b 6b 7 8 9
488491
} {
489
- 1 MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6b 7 8 END 9
492
+ 1 MINE: 2 3 BOT: 2b 3b 4b 5b 6b 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6b 7 8 END 9
490493
} -expectError
491494
492495
merge-test 80 {
493496
2 3 4 5 6 7 8
494497
} {
@@ -530,20 +533,20 @@
530533
} {
531534
2b 3b 4b 5b 6 7 8
532535
} {
533536
2 3 4
534537
} {
535
- MINE: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END
538
+ MINE: 2b 3b 4b 5b 6 7 8 BOT: 2b 3b 4b 4 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END
536539
} -expectError
537540
merge-test 85 {
538541
2 3 4 5 6 7 8
539542
} {
540543
2b 3b 4b 5b 6b 7 8
541544
} {
542545
2 3
543546
} {
544
- MINE: 2b 3b 4b 5b 6b 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END
547
+ MINE: 2b 3b 4b 5b 6b 7 8 BOT: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END
545548
} -expectError
546549
547550
merge-test 90 {
548551
2 3 4 5 6 7 8
549552
} {
@@ -585,20 +588,20 @@
585588
} {
586589
2 3 4
587590
} {
588591
2b 3b 4b 5b 6 7 8
589592
} {
590
- MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END
593
+ MINE: 2 3 4 BOT: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END
591594
} -expectError
592595
merge-test 95 {
593596
2 3 4 5 6 7 8
594597
} {
595598
2 3
596599
} {
597600
2b 3b 4b 5b 6b 7 8
598601
} {
599
- MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6b 7 8 END
602
+ MINE: 2 3 BOT: 2b 3b 4b 5b 6b 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6b 7 8 END
600603
} -expectError
601604
602605
merge-test 100 {
603606
1 2 3 4 5 6 7 8 9
604607
} {
@@ -631,20 +634,20 @@
631634
} {
632635
1 2 3 4 5 7 8 9b
633636
} {
634637
1 2 3 4 5 7 8 9b a b c d e
635638
} {
636
- 1 2 3 4 5 7 8 MINE: 9b COM: 9 YOURS: 9b a b c d e END
639
+ 1 2 3 4 5 7 8 MINE: 9b BOT: 9b a b c d e COM: 9 YOURS: 9b a b c d e END
637640
} -expectError
638641
merge-test 104 {
639642
1 2 3 4 5 6 7 8 9
640643
} {
641644
1 2 3 4 5 7 8 9b a b c d e
642645
} {
643646
1 2 3 4 5 7 8 9b
644647
} {
645
- 1 2 3 4 5 7 8 MINE: 9b a b c d e COM: 9 YOURS: 9b END
648
+ 1 2 3 4 5 7 8 MINE: 9b a b c d e BOT: 9b COM: 9 YOURS: 9b END
646649
} -expectError
647650
648651
###############################################################################
649652
650653
test_cleanup
651654
--- test/merge3.test
+++ test/merge3.test
@@ -27,10 +27,13 @@
27 fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args
28 set x [read_file t4]
29 regsub -all \
30 {<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <+ \(line \d+\)} \
31 $x {MINE:} x
 
 
 
32 regsub -all \
33 {\|\|\|\|\|\|\| COMMON ANCESTOR content follows \|+ \(line \d+\)} \
34 $x {COM:} x
35 regsub -all \
36 {======= MERGED IN content follows =+ \(line \d+\)} \
@@ -73,56 +76,56 @@
73 } {
74 1 2 3b 4b 5b 6 7 8 9
75 } {
76 1 2 3 4 5c 6 7 8 9
77 } {
78 1 2 MINE: 3b 4b 5b COM: 3 4 5 YOURS: 3 4 5c END 6 7 8 9
79 } -expectError
80 merge-test 4 {
81 1 2 3 4 5 6 7 8 9
82 } {
83 1 2 3b 4b 5b 6b 7 8 9
84 } {
85 1 2 3 4 5c 6 7 8 9
86 } {
87 1 2 MINE: 3b 4b 5b 6b COM: 3 4 5 6 YOURS: 3 4 5c 6 END 7 8 9
88 } -expectError
89 merge-test 5 {
90 1 2 3 4 5 6 7 8 9
91 } {
92 1 2 3b 4b 5b 6b 7 8 9
93 } {
94 1 2 3 4 5c 6c 7c 8 9
95 } {
96 1 2 MINE: 3b 4b 5b 6b 7 COM: 3 4 5 6 7 YOURS: 3 4 5c 6c 7c END 8 9
97 } -expectError
98 merge-test 6 {
99 1 2 3 4 5 6 7 8 9
100 } {
101 1 2 3b 4b 5b 6b 7 8b 9
102 } {
103 1 2 3 4 5c 6c 7c 8 9
104 } {
105 1 2 MINE: 3b 4b 5b 6b 7 COM: 3 4 5 6 7 YOURS: 3 4 5c 6c 7c END 8b 9
106 } -expectError
107 merge-test 7 {
108 1 2 3 4 5 6 7 8 9
109 } {
110 1 2 3b 4b 5b 6b 7 8b 9
111 } {
112 1 2 3 4 5c 6c 7c 8c 9
113 } {
114 1 2 MINE: 3b 4b 5b 6b 7 8b COM: 3 4 5 6 7 8 YOURS: 3 4 5c 6c 7c 8c END 9
115 } -expectError
116 merge-test 8 {
117 1 2 3 4 5 6 7 8 9
118 } {
119 1 2 3b 4b 5b 6b 7 8b 9b
120 } {
121 1 2 3 4 5c 6c 7c 8c 9
122 } {
123 1 2 MINE: 3b 4b 5b 6b 7 8b 9b COM: 3 4 5 6 7 8 9 YOURS: 3 4 5c 6c 7c 8c 9 END
124 } -expectError
125 merge-test 9 {
126 1 2 3 4 5 6 7 8 9
127 } {
128 1 2 3b 4b 5 6 7 8b 9b
@@ -146,11 +149,11 @@
146 } {
147 1 2 3b 4b 5 6 7 8b 9b
148 } {
149 1 2 3b 4c 5 6c 7c 8 9
150 } {
151 1 2 MINE: 3b 4b COM: 3 4 YOURS: 3b 4c END 5 6c 7c 8b 9b
152 } -expectError
153 merge-test 12 {
154 1 2 3 4 5 6 7 8 9
155 } {
156 1 2 3b4b 5 6 7 8b 9b
@@ -201,20 +204,20 @@
201 } {
202 1 6 7 8 9
203 } {
204 1 2 3 4 9
205 } {
206 1 MINE: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9
207 } -expectError
208 merge-test 25 {
209 1 2 3 4 5 6 7 8 9
210 } {
211 1 7 8 9
212 } {
213 1 2 3 9
214 } {
215 1 MINE: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9
216 } -expectError
217
218 merge-test 30 {
219 1 2 3 4 5 6 7 8 9
220 } {
@@ -256,20 +259,20 @@
256 } {
257 1 2 3 4 9
258 } {
259 1 6 7 8 9
260 } {
261 1 MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END 9
262 } -expectError
263 merge-test 35 {
264 1 2 3 4 5 6 7 8 9
265 } {
266 1 2 3 9
267 } {
268 1 7 8 9
269 } {
270 1 MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END 9
271 } -expectError
272
273 merge-test 40 {
274 2 3 4 5 6 7 8
275 } {
@@ -311,20 +314,20 @@
311 } {
312 6 7 8
313 } {
314 2 3 4
315 } {
316 MINE: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END
317 } -expectError
318 merge-test 45 {
319 2 3 4 5 6 7 8
320 } {
321 7 8
322 } {
323 2 3
324 } {
325 MINE: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END
326 } -expectError
327
328 merge-test 50 {
329 2 3 4 5 6 7 8
330 } {
@@ -365,20 +368,20 @@
365 } {
366 2 3 4
367 } {
368 6 7 8
369 } {
370 MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END
371 } -expectError
372 merge-test 55 {
373 2 3 4 5 6 7 8
374 } {
375 2 3
376 } {
377 7 8
378 } {
379 MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END
380 } -expectError
381
382 merge-test 60 {
383 1 2 3 4 5 6 7 8 9
384 } {
@@ -420,20 +423,20 @@
420 } {
421 1 2b 3b 4b 5b 6 7 8 9
422 } {
423 1 2 3 4 9
424 } {
425 1 MINE: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9
426 } -expectError
427 merge-test 65 {
428 1 2 3 4 5 6 7 8 9
429 } {
430 1 2b 3b 4b 5b 6b 7 8 9
431 } {
432 1 2 3 9
433 } {
434 1 MINE: 2b 3b 4b 5b 6b 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9
435 } -expectError
436
437 merge-test 70 {
438 1 2 3 4 5 6 7 8 9
439 } {
@@ -475,20 +478,20 @@
475 } {
476 1 2 3 4 9
477 } {
478 1 2b 3b 4b 5b 6 7 8 9
479 } {
480 1 MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END 9
481 } -expectError
482 merge-test 75 {
483 1 2 3 4 5 6 7 8 9
484 } {
485 1 2 3 9
486 } {
487 1 2b 3b 4b 5b 6b 7 8 9
488 } {
489 1 MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6b 7 8 END 9
490 } -expectError
491
492 merge-test 80 {
493 2 3 4 5 6 7 8
494 } {
@@ -530,20 +533,20 @@
530 } {
531 2b 3b 4b 5b 6 7 8
532 } {
533 2 3 4
534 } {
535 MINE: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END
536 } -expectError
537 merge-test 85 {
538 2 3 4 5 6 7 8
539 } {
540 2b 3b 4b 5b 6b 7 8
541 } {
542 2 3
543 } {
544 MINE: 2b 3b 4b 5b 6b 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END
545 } -expectError
546
547 merge-test 90 {
548 2 3 4 5 6 7 8
549 } {
@@ -585,20 +588,20 @@
585 } {
586 2 3 4
587 } {
588 2b 3b 4b 5b 6 7 8
589 } {
590 MINE: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END
591 } -expectError
592 merge-test 95 {
593 2 3 4 5 6 7 8
594 } {
595 2 3
596 } {
597 2b 3b 4b 5b 6b 7 8
598 } {
599 MINE: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6b 7 8 END
600 } -expectError
601
602 merge-test 100 {
603 1 2 3 4 5 6 7 8 9
604 } {
@@ -631,20 +634,20 @@
631 } {
632 1 2 3 4 5 7 8 9b
633 } {
634 1 2 3 4 5 7 8 9b a b c d e
635 } {
636 1 2 3 4 5 7 8 MINE: 9b COM: 9 YOURS: 9b a b c d e END
637 } -expectError
638 merge-test 104 {
639 1 2 3 4 5 6 7 8 9
640 } {
641 1 2 3 4 5 7 8 9b a b c d e
642 } {
643 1 2 3 4 5 7 8 9b
644 } {
645 1 2 3 4 5 7 8 MINE: 9b a b c d e COM: 9 YOURS: 9b END
646 } -expectError
647
648 ###############################################################################
649
650 test_cleanup
651
--- test/merge3.test
+++ test/merge3.test
@@ -27,10 +27,13 @@
27 fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args
28 set x [read_file t4]
29 regsub -all \
30 {<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <+ \(line \d+\)} \
31 $x {MINE:} x
32 regsub -all \
33 {####### SUGGESTED CONFLICT RESOLUTION follows #+} \
34 $x {BOT:} x
35 regsub -all \
36 {\|\|\|\|\|\|\| COMMON ANCESTOR content follows \|+ \(line \d+\)} \
37 $x {COM:} x
38 regsub -all \
39 {======= MERGED IN content follows =+ \(line \d+\)} \
@@ -73,56 +76,56 @@
76 } {
77 1 2 3b 4b 5b 6 7 8 9
78 } {
79 1 2 3 4 5c 6 7 8 9
80 } {
81 1 2 MINE: 3b 4b 5b BOT: 3b 4b 5c COM: 3 4 5 YOURS: 3 4 5c END 6 7 8 9
82 } -expectError
83 merge-test 4 {
84 1 2 3 4 5 6 7 8 9
85 } {
86 1 2 3b 4b 5b 6b 7 8 9
87 } {
88 1 2 3 4 5c 6 7 8 9
89 } {
90 1 2 MINE: 3b 4b 5b 6b BOT: 3b 4b 5b 5c 6 COM: 3 4 5 6 YOURS: 3 4 5c 6 END 7 8 9
91 } -expectError
92 merge-test 5 {
93 1 2 3 4 5 6 7 8 9
94 } {
95 1 2 3b 4b 5b 6b 7 8 9
96 } {
97 1 2 3 4 5c 6c 7c 8 9
98 } {
99 1 2 MINE: 3b 4b 5b 6b 7 BOT: 3b 4b 5b 5c 6c 7c COM: 3 4 5 6 7 YOURS: 3 4 5c 6c 7c END 8 9
100 } -expectError
101 merge-test 6 {
102 1 2 3 4 5 6 7 8 9
103 } {
104 1 2 3b 4b 5b 6b 7 8b 9
105 } {
106 1 2 3 4 5c 6c 7c 8 9
107 } {
108 1 2 MINE: 3b 4b 5b 6b 7 BOT: 3b 4b 5b 5c 6c 7c COM: 3 4 5 6 7 YOURS: 3 4 5c 6c 7c END 8b 9
109 } -expectError
110 merge-test 7 {
111 1 2 3 4 5 6 7 8 9
112 } {
113 1 2 3b 4b 5b 6b 7 8b 9
114 } {
115 1 2 3 4 5c 6c 7c 8c 9
116 } {
117 1 2 MINE: 3b 4b 5b 6b 7 8b BOT: 3b 4b 5b 5c 6c 7c 8c COM: 3 4 5 6 7 8 YOURS: 3 4 5c 6c 7c 8c END 9
118 } -expectError
119 merge-test 8 {
120 1 2 3 4 5 6 7 8 9
121 } {
122 1 2 3b 4b 5b 6b 7 8b 9b
123 } {
124 1 2 3 4 5c 6c 7c 8c 9
125 } {
126 1 2 MINE: 3b 4b 5b 6b 7 8b 9b BOT: 3b 4b 5b 5c 6c 7c 8c 9b COM: 3 4 5 6 7 8 9 YOURS: 3 4 5c 6c 7c 8c 9 END
127 } -expectError
128 merge-test 9 {
129 1 2 3 4 5 6 7 8 9
130 } {
131 1 2 3b 4b 5 6 7 8b 9b
@@ -146,11 +149,11 @@
149 } {
150 1 2 3b 4b 5 6 7 8b 9b
151 } {
152 1 2 3b 4c 5 6c 7c 8 9
153 } {
154 1 2 MINE: 3b 4b BOT: 3b 4c COM: 3 4 YOURS: 3b 4c END 5 6c 7c 8b 9b
155 } -expectError
156 merge-test 12 {
157 1 2 3 4 5 6 7 8 9
158 } {
159 1 2 3b4b 5 6 7 8b 9b
@@ -201,20 +204,20 @@
204 } {
205 1 6 7 8 9
206 } {
207 1 2 3 4 9
208 } {
209 1 MINE: 6 7 8 BOT: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9
210 } -expectError
211 merge-test 25 {
212 1 2 3 4 5 6 7 8 9
213 } {
214 1 7 8 9
215 } {
216 1 2 3 9
217 } {
218 1 MINE: 7 8 BOT: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9
219 } -expectError
220
221 merge-test 30 {
222 1 2 3 4 5 6 7 8 9
223 } {
@@ -256,20 +259,20 @@
259 } {
260 1 2 3 4 9
261 } {
262 1 6 7 8 9
263 } {
264 1 MINE: 2 3 4 BOT: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END 9
265 } -expectError
266 merge-test 35 {
267 1 2 3 4 5 6 7 8 9
268 } {
269 1 2 3 9
270 } {
271 1 7 8 9
272 } {
273 1 MINE: 2 3 BOT: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END 9
274 } -expectError
275
276 merge-test 40 {
277 2 3 4 5 6 7 8
278 } {
@@ -311,20 +314,20 @@
314 } {
315 6 7 8
316 } {
317 2 3 4
318 } {
319 MINE: 6 7 8 BOT: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END
320 } -expectError
321 merge-test 45 {
322 2 3 4 5 6 7 8
323 } {
324 7 8
325 } {
326 2 3
327 } {
328 MINE: 7 8 BOT: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END
329 } -expectError
330
331 merge-test 50 {
332 2 3 4 5 6 7 8
333 } {
@@ -365,20 +368,20 @@
368 } {
369 2 3 4
370 } {
371 6 7 8
372 } {
373 MINE: 2 3 4 BOT: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END
374 } -expectError
375 merge-test 55 {
376 2 3 4 5 6 7 8
377 } {
378 2 3
379 } {
380 7 8
381 } {
382 MINE: 2 3 BOT: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END
383 } -expectError
384
385 merge-test 60 {
386 1 2 3 4 5 6 7 8 9
387 } {
@@ -420,20 +423,20 @@
423 } {
424 1 2b 3b 4b 5b 6 7 8 9
425 } {
426 1 2 3 4 9
427 } {
428 1 MINE: 2b 3b 4b 5b 6 7 8 BOT: 2b 3b 4b 4 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9
429 } -expectError
430 merge-test 65 {
431 1 2 3 4 5 6 7 8 9
432 } {
433 1 2b 3b 4b 5b 6b 7 8 9
434 } {
435 1 2 3 9
436 } {
437 1 MINE: 2b 3b 4b 5b 6b 7 8 BOT: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9
438 } -expectError
439
440 merge-test 70 {
441 1 2 3 4 5 6 7 8 9
442 } {
@@ -475,20 +478,20 @@
478 } {
479 1 2 3 4 9
480 } {
481 1 2b 3b 4b 5b 6 7 8 9
482 } {
483 1 MINE: 2 3 4 BOT: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END 9
484 } -expectError
485 merge-test 75 {
486 1 2 3 4 5 6 7 8 9
487 } {
488 1 2 3 9
489 } {
490 1 2b 3b 4b 5b 6b 7 8 9
491 } {
492 1 MINE: 2 3 BOT: 2b 3b 4b 5b 6b 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6b 7 8 END 9
493 } -expectError
494
495 merge-test 80 {
496 2 3 4 5 6 7 8
497 } {
@@ -530,20 +533,20 @@
533 } {
534 2b 3b 4b 5b 6 7 8
535 } {
536 2 3 4
537 } {
538 MINE: 2b 3b 4b 5b 6 7 8 BOT: 2b 3b 4b 4 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END
539 } -expectError
540 merge-test 85 {
541 2 3 4 5 6 7 8
542 } {
543 2b 3b 4b 5b 6b 7 8
544 } {
545 2 3
546 } {
547 MINE: 2b 3b 4b 5b 6b 7 8 BOT: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END
548 } -expectError
549
550 merge-test 90 {
551 2 3 4 5 6 7 8
552 } {
@@ -585,20 +588,20 @@
588 } {
589 2 3 4
590 } {
591 2b 3b 4b 5b 6 7 8
592 } {
593 MINE: 2 3 4 BOT: 2b 3b 4b 5b 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6 7 8 END
594 } -expectError
595 merge-test 95 {
596 2 3 4 5 6 7 8
597 } {
598 2 3
599 } {
600 2b 3b 4b 5b 6b 7 8
601 } {
602 MINE: 2 3 BOT: 2b 3b 4b 5b 6b 7 8 COM: 2 3 4 5 6 7 8 YOURS: 2b 3b 4b 5b 6b 7 8 END
603 } -expectError
604
605 merge-test 100 {
606 1 2 3 4 5 6 7 8 9
607 } {
@@ -631,20 +634,20 @@
634 } {
635 1 2 3 4 5 7 8 9b
636 } {
637 1 2 3 4 5 7 8 9b a b c d e
638 } {
639 1 2 3 4 5 7 8 MINE: 9b BOT: 9b a b c d e COM: 9 YOURS: 9b a b c d e END
640 } -expectError
641 merge-test 104 {
642 1 2 3 4 5 6 7 8 9
643 } {
644 1 2 3 4 5 7 8 9b a b c d e
645 } {
646 1 2 3 4 5 7 8 9b
647 } {
648 1 2 3 4 5 7 8 MINE: 9b a b c d e BOT: 9b COM: 9 YOURS: 9b END
649 } -expectError
650
651 ###############################################################################
652
653 test_cleanup
654
--- test/merge4.test
+++ test/merge4.test
@@ -26,15 +26,17 @@
2626
write_file t3 [join [string trim $v2] \n]\n
2727
fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args
2828
fossil 3-way-merge t1 t3 t2 t5 {*}$fossil_args
2929
set x [read_file t4]
3030
regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $x {>} x
31
+ regsub -all {####### SUGGESTED CONFLICT RESOLUTION.*##} $x {#} x
3132
regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $x {=} x
3233
regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $x {<} x
3334
set x [split [string trim $x] \n]
3435
set y [read_file t5]
3536
regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $y {>} y
37
+ regsub -all {####### SUGGESTED CONFLICT RESOLUTION.*##} $y {#} y
3638
regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $y {=} y
3739
regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $y {<} y
3840
set y [split [string trim $y] \n]
3941
set result1 [string trim $result1]
4042
if {$x!=$result1} {
@@ -58,13 +60,13 @@
5860
} {
5961
1 2b 3b 4b 5 6b 7b 8b 9
6062
} {
6163
1 2 3 4c 5c 6c 7 8 9
6264
} {
63
- 1 > 2b 3b 4b 5 6b 7b 8b = 2 3 4c 5c 6c 7 8 < 9
65
+ 1 > 2b 3b 4b 5 6b 7b 8b # 2b 3b 4c 5c 6c 7b 8b = 2 3 4c 5c 6c 7 8 < 9
6466
} {
65
- 1 > 2 3 4c 5c 6c 7 8 = 2b 3b 4b 5 6b 7b 8b < 9
67
+ 1 > 2 3 4c 5c 6c 7 8 # 2b 3b 4b 5c 6b 7b 8b = 2b 3b 4b 5 6b 7b 8b < 9
6668
} -expectError
6769
merge-test 1001 {
6870
1 2 3 4 5 6 7 8 9
6971
} {
7072
1 2b 3b 4 5 6 7b 8b 9
@@ -80,13 +82,13 @@
8082
} {
8183
2b 3b 4b 5 6b 7b 8b
8284
} {
8385
2 3 4c 5c 6c 7 8
8486
} {
85
- > 2b 3b 4b 5 6b 7b 8b = 2 3 4c 5c 6c 7 8 <
87
+ > 2b 3b 4b 5 6b 7b 8b # 2b 3b 4c 5c 6c 7b 8b = 2 3 4c 5c 6c 7 8 <
8688
} {
87
- > 2 3 4c 5c 6c 7 8 = 2b 3b 4b 5 6b 7b 8b <
89
+ > 2 3 4c 5c 6c 7 8 # 2b 3b 4b 5c 6b 7b 8b = 2b 3b 4b 5 6b 7b 8b <
8890
} -expectError
8991
merge-test 1003 {
9092
2 3 4 5 6 7 8
9193
} {
9294
2b 3b 4 5 6 7b 8b
9395
--- test/merge4.test
+++ test/merge4.test
@@ -26,15 +26,17 @@
26 write_file t3 [join [string trim $v2] \n]\n
27 fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args
28 fossil 3-way-merge t1 t3 t2 t5 {*}$fossil_args
29 set x [read_file t4]
30 regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $x {>} x
 
31 regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $x {=} x
32 regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $x {<} x
33 set x [split [string trim $x] \n]
34 set y [read_file t5]
35 regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $y {>} y
 
36 regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $y {=} y
37 regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $y {<} y
38 set y [split [string trim $y] \n]
39 set result1 [string trim $result1]
40 if {$x!=$result1} {
@@ -58,13 +60,13 @@
58 } {
59 1 2b 3b 4b 5 6b 7b 8b 9
60 } {
61 1 2 3 4c 5c 6c 7 8 9
62 } {
63 1 > 2b 3b 4b 5 6b 7b 8b = 2 3 4c 5c 6c 7 8 < 9
64 } {
65 1 > 2 3 4c 5c 6c 7 8 = 2b 3b 4b 5 6b 7b 8b < 9
66 } -expectError
67 merge-test 1001 {
68 1 2 3 4 5 6 7 8 9
69 } {
70 1 2b 3b 4 5 6 7b 8b 9
@@ -80,13 +82,13 @@
80 } {
81 2b 3b 4b 5 6b 7b 8b
82 } {
83 2 3 4c 5c 6c 7 8
84 } {
85 > 2b 3b 4b 5 6b 7b 8b = 2 3 4c 5c 6c 7 8 <
86 } {
87 > 2 3 4c 5c 6c 7 8 = 2b 3b 4b 5 6b 7b 8b <
88 } -expectError
89 merge-test 1003 {
90 2 3 4 5 6 7 8
91 } {
92 2b 3b 4 5 6 7b 8b
93
--- test/merge4.test
+++ test/merge4.test
@@ -26,15 +26,17 @@
26 write_file t3 [join [string trim $v2] \n]\n
27 fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args
28 fossil 3-way-merge t1 t3 t2 t5 {*}$fossil_args
29 set x [read_file t4]
30 regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $x {>} x
31 regsub -all {####### SUGGESTED CONFLICT RESOLUTION.*##} $x {#} x
32 regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $x {=} x
33 regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $x {<} x
34 set x [split [string trim $x] \n]
35 set y [read_file t5]
36 regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $y {>} y
37 regsub -all {####### SUGGESTED CONFLICT RESOLUTION.*##} $y {#} y
38 regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $y {=} y
39 regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $y {<} y
40 set y [split [string trim $y] \n]
41 set result1 [string trim $result1]
42 if {$x!=$result1} {
@@ -58,13 +60,13 @@
60 } {
61 1 2b 3b 4b 5 6b 7b 8b 9
62 } {
63 1 2 3 4c 5c 6c 7 8 9
64 } {
65 1 > 2b 3b 4b 5 6b 7b 8b # 2b 3b 4c 5c 6c 7b 8b = 2 3 4c 5c 6c 7 8 < 9
66 } {
67 1 > 2 3 4c 5c 6c 7 8 # 2b 3b 4b 5c 6b 7b 8b = 2b 3b 4b 5 6b 7b 8b < 9
68 } -expectError
69 merge-test 1001 {
70 1 2 3 4 5 6 7 8 9
71 } {
72 1 2b 3b 4 5 6 7b 8b 9
@@ -80,13 +82,13 @@
82 } {
83 2b 3b 4b 5 6b 7b 8b
84 } {
85 2 3 4c 5c 6c 7 8
86 } {
87 > 2b 3b 4b 5 6b 7b 8b # 2b 3b 4c 5c 6c 7b 8b = 2 3 4c 5c 6c 7 8 <
88 } {
89 > 2 3 4c 5c 6c 7 8 # 2b 3b 4b 5c 6b 7b 8b = 2b 3b 4b 5 6b 7b 8b <
90 } -expectError
91 merge-test 1003 {
92 2 3 4 5 6 7 8
93 } {
94 2b 3b 4 5 6 7b 8b
95
--- test/tester.tcl
+++ test/tester.tcl
@@ -332,10 +332,11 @@
332332
encoding-glob \
333333
exec-rel-paths \
334334
fileedit-glob \
335335
forbid-delta-manifests \
336336
forum-close-policy \
337
+ forum-title \
337338
gdiff-command \
338339
gmerge-command \
339340
hash-digits \
340341
hooks \
341342
http-port \
342343
--- test/tester.tcl
+++ test/tester.tcl
@@ -332,10 +332,11 @@
332 encoding-glob \
333 exec-rel-paths \
334 fileedit-glob \
335 forbid-delta-manifests \
336 forum-close-policy \
 
337 gdiff-command \
338 gmerge-command \
339 hash-digits \
340 hooks \
341 http-port \
342
--- test/tester.tcl
+++ test/tester.tcl
@@ -332,10 +332,11 @@
332 encoding-glob \
333 exec-rel-paths \
334 fileedit-glob \
335 forbid-delta-manifests \
336 forum-close-policy \
337 forum-title \
338 gdiff-command \
339 gmerge-command \
340 hash-digits \
341 hooks \
342 http-port \
343
--- test/update.test
+++ test/update.test
@@ -57,11 +57,11 @@
5757
5858
###############################################################################
5959
6060
fossil update --verbose
6161
test update-already-up-to-date {
62
- [regexp {^-{79}\ncheckout: .*\nchanges: +None. Already up-to-date$} $RESULT]
62
+ [regexp {^-{79}\ncheckout: .*\nchanges: +None. Already up-to-date.$} $RESULT]
6363
}
6464
6565
# Remaining tests are carried out in the order update_cmd() performs checks.
6666
#
6767
# Common approach for tests below:
6868
--- test/update.test
+++ test/update.test
@@ -57,11 +57,11 @@
57
58 ###############################################################################
59
60 fossil update --verbose
61 test update-already-up-to-date {
62 [regexp {^-{79}\ncheckout: .*\nchanges: +None. Already up-to-date$} $RESULT]
63 }
64
65 # Remaining tests are carried out in the order update_cmd() performs checks.
66 #
67 # Common approach for tests below:
68
--- test/update.test
+++ test/update.test
@@ -57,11 +57,11 @@
57
58 ###############################################################################
59
60 fossil update --verbose
61 test update-already-up-to-date {
62 [regexp {^-{79}\ncheckout: .*\nchanges: +None. Already up-to-date.$} $RESULT]
63 }
64
65 # Remaining tests are carried out in the order update_cmd() performs checks.
66 #
67 # Common approach for tests below:
68
--- tools/makemake.tcl
+++ tools/makemake.tcl
@@ -132,10 +132,11 @@
132132
lookslike
133133
main
134134
manifest
135135
markdown
136136
markdown_html
137
+ match
137138
md5
138139
merge
139140
merge3
140141
moderate
141142
name
142143
--- tools/makemake.tcl
+++ tools/makemake.tcl
@@ -132,10 +132,11 @@
132 lookslike
133 main
134 manifest
135 markdown
136 markdown_html
 
137 md5
138 merge
139 merge3
140 moderate
141 name
142
--- tools/makemake.tcl
+++ tools/makemake.tcl
@@ -132,10 +132,11 @@
132 lookslike
133 main
134 manifest
135 markdown
136 markdown_html
137 match
138 md5
139 merge
140 merge3
141 moderate
142 name
143
--- tools/translate.c
+++ tools/translate.c
@@ -78,10 +78,21 @@
7878
7979
/*
8080
** Name of files being processed
8181
*/
8282
static const char *zInFile = "(stdin)";
83
+
84
+/*
85
+** The `fossil_isspace()' function copied from the Fossil source code.
86
+** Some MSVC runtime library versions of `isspace()' break with an `assert()' if
87
+** the input is smaller than -1 or greater than 255 in debug builds, due to sign
88
+** extension when promoting `signed char' to `int' for non-ASCII characters. Use
89
+** an `isspace()' replacement instead of explicit type casts to `unsigned char'.
90
+*/
91
+int fossil_isspace(char c){
92
+ return c==' ' || (c<='\r' && c>='\t');
93
+}
8394
8495
/*
8596
** Terminate an active cgi_printf() or free string
8697
*/
8798
static void end_block(FILE *out){
@@ -106,21 +117,21 @@
106117
char zOut[4000]; /* The input line translated into appropriate output */
107118
108119
c1 = c2 = '-';
109120
while( fgets(zLine, sizeof(zLine), in) ){
110121
lineNo++;
111
- for(i=0; zLine[i] && isspace(zLine[i]); i++){}
122
+ for(i=0; zLine[i] && fossil_isspace(zLine[i]); i++){}
112123
if( zLine[i]!='@' ){
113124
if( inPrint || inStr ) end_block(out);
114125
fprintf(out,"%s",zLine);
115126
/* 0123456789 12345 */
116127
if( strncmp(zLine, "/* @-comment: ", 14)==0 ){
117128
c1 = zLine[14];
118129
c2 = zLine[15];
119130
}
120131
i += strlen(&zLine[i]);
121
- while( i>0 && isspace(zLine[i-1]) ){ i--; }
132
+ while( i>0 && fossil_isspace(zLine[i-1]) ){ i--; }
122133
lastWasEq = i>0 && zLine[i-1]=='=';
123134
lastWasComma = i>0 && zLine[i-1]==',';
124135
}else if( lastWasEq || lastWasComma){
125136
/* If the last non-whitespace character before the first @ was
126137
** an "="(var init/set) or a ","(const definition in list) then
@@ -129,11 +140,11 @@
129140
** and end of line.
130141
*/
131142
int indent, omitline;
132143
char *zNewline = "\\n";
133144
i++;
134
- if( isspace(zLine[i]) ){ i++; }
145
+ if( fossil_isspace(zLine[i]) ){ i++; }
135146
indent = i - 2;
136147
if( indent<0 ) indent = 0;
137148
omitline = 0;
138149
for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){
139150
if( zLine[i]==c1 && (c2==' ' || zLine[i+1]==c2) ){
@@ -147,11 +158,11 @@
147158
break;
148159
}
149160
if( zLine[i]=='\\' || zLine[i]=='"' ){ zOut[j++] = '\\'; }
150161
zOut[j++] = zLine[i];
151162
}
152
- if( zNewline[0] ) while( j>0 && isspace(zOut[j-1]) ){ j--; }
163
+ if( zNewline[0] ) while( j>0 && fossil_isspace(zOut[j-1]) ){ j--; }
153164
zOut[j] = 0;
154165
if( j<=0 && omitline ){
155166
fprintf(out,"\n");
156167
}else{
157168
fprintf(out,"%*s\"%s%s\"\n",indent, "", zOut, zNewline);
@@ -171,11 +182,11 @@
171182
int indent;
172183
int nC;
173184
int nParam;
174185
char c;
175186
i++;
176
- if( isspace(zLine[i]) ){ i++; }
187
+ if( fossil_isspace(zLine[i]) ){ i++; }
177188
indent = i;
178189
for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){
179190
if( zLine[i]=='\\' && (!zLine[i+1] || zLine[i+1]=='\r'
180191
|| zLine[i+1]=='\n') ){
181192
zNewline = "";
182193
--- tools/translate.c
+++ tools/translate.c
@@ -78,10 +78,21 @@
78
79 /*
80 ** Name of files being processed
81 */
82 static const char *zInFile = "(stdin)";
 
 
 
 
 
 
 
 
 
 
 
83
84 /*
85 ** Terminate an active cgi_printf() or free string
86 */
87 static void end_block(FILE *out){
@@ -106,21 +117,21 @@
106 char zOut[4000]; /* The input line translated into appropriate output */
107
108 c1 = c2 = '-';
109 while( fgets(zLine, sizeof(zLine), in) ){
110 lineNo++;
111 for(i=0; zLine[i] && isspace(zLine[i]); i++){}
112 if( zLine[i]!='@' ){
113 if( inPrint || inStr ) end_block(out);
114 fprintf(out,"%s",zLine);
115 /* 0123456789 12345 */
116 if( strncmp(zLine, "/* @-comment: ", 14)==0 ){
117 c1 = zLine[14];
118 c2 = zLine[15];
119 }
120 i += strlen(&zLine[i]);
121 while( i>0 && isspace(zLine[i-1]) ){ i--; }
122 lastWasEq = i>0 && zLine[i-1]=='=';
123 lastWasComma = i>0 && zLine[i-1]==',';
124 }else if( lastWasEq || lastWasComma){
125 /* If the last non-whitespace character before the first @ was
126 ** an "="(var init/set) or a ","(const definition in list) then
@@ -129,11 +140,11 @@
129 ** and end of line.
130 */
131 int indent, omitline;
132 char *zNewline = "\\n";
133 i++;
134 if( isspace(zLine[i]) ){ i++; }
135 indent = i - 2;
136 if( indent<0 ) indent = 0;
137 omitline = 0;
138 for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){
139 if( zLine[i]==c1 && (c2==' ' || zLine[i+1]==c2) ){
@@ -147,11 +158,11 @@
147 break;
148 }
149 if( zLine[i]=='\\' || zLine[i]=='"' ){ zOut[j++] = '\\'; }
150 zOut[j++] = zLine[i];
151 }
152 if( zNewline[0] ) while( j>0 && isspace(zOut[j-1]) ){ j--; }
153 zOut[j] = 0;
154 if( j<=0 && omitline ){
155 fprintf(out,"\n");
156 }else{
157 fprintf(out,"%*s\"%s%s\"\n",indent, "", zOut, zNewline);
@@ -171,11 +182,11 @@
171 int indent;
172 int nC;
173 int nParam;
174 char c;
175 i++;
176 if( isspace(zLine[i]) ){ i++; }
177 indent = i;
178 for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){
179 if( zLine[i]=='\\' && (!zLine[i+1] || zLine[i+1]=='\r'
180 || zLine[i+1]=='\n') ){
181 zNewline = "";
182
--- tools/translate.c
+++ tools/translate.c
@@ -78,10 +78,21 @@
78
79 /*
80 ** Name of files being processed
81 */
82 static const char *zInFile = "(stdin)";
83
84 /*
85 ** The `fossil_isspace()' function copied from the Fossil source code.
86 ** Some MSVC runtime library versions of `isspace()' break with an `assert()' if
87 ** the input is smaller than -1 or greater than 255 in debug builds, due to sign
88 ** extension when promoting `signed char' to `int' for non-ASCII characters. Use
89 ** an `isspace()' replacement instead of explicit type casts to `unsigned char'.
90 */
91 int fossil_isspace(char c){
92 return c==' ' || (c<='\r' && c>='\t');
93 }
94
95 /*
96 ** Terminate an active cgi_printf() or free string
97 */
98 static void end_block(FILE *out){
@@ -106,21 +117,21 @@
117 char zOut[4000]; /* The input line translated into appropriate output */
118
119 c1 = c2 = '-';
120 while( fgets(zLine, sizeof(zLine), in) ){
121 lineNo++;
122 for(i=0; zLine[i] && fossil_isspace(zLine[i]); i++){}
123 if( zLine[i]!='@' ){
124 if( inPrint || inStr ) end_block(out);
125 fprintf(out,"%s",zLine);
126 /* 0123456789 12345 */
127 if( strncmp(zLine, "/* @-comment: ", 14)==0 ){
128 c1 = zLine[14];
129 c2 = zLine[15];
130 }
131 i += strlen(&zLine[i]);
132 while( i>0 && fossil_isspace(zLine[i-1]) ){ i--; }
133 lastWasEq = i>0 && zLine[i-1]=='=';
134 lastWasComma = i>0 && zLine[i-1]==',';
135 }else if( lastWasEq || lastWasComma){
136 /* If the last non-whitespace character before the first @ was
137 ** an "="(var init/set) or a ","(const definition in list) then
@@ -129,11 +140,11 @@
140 ** and end of line.
141 */
142 int indent, omitline;
143 char *zNewline = "\\n";
144 i++;
145 if( fossil_isspace(zLine[i]) ){ i++; }
146 indent = i - 2;
147 if( indent<0 ) indent = 0;
148 omitline = 0;
149 for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){
150 if( zLine[i]==c1 && (c2==' ' || zLine[i+1]==c2) ){
@@ -147,11 +158,11 @@
158 break;
159 }
160 if( zLine[i]=='\\' || zLine[i]=='"' ){ zOut[j++] = '\\'; }
161 zOut[j++] = zLine[i];
162 }
163 if( zNewline[0] ) while( j>0 && fossil_isspace(zOut[j-1]) ){ j--; }
164 zOut[j] = 0;
165 if( j<=0 && omitline ){
166 fprintf(out,"\n");
167 }else{
168 fprintf(out,"%*s\"%s%s\"\n",indent, "", zOut, zNewline);
@@ -171,11 +182,11 @@
182 int indent;
183 int nC;
184 int nParam;
185 char c;
186 i++;
187 if( fossil_isspace(zLine[i]) ){ i++; }
188 indent = i;
189 for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){
190 if( zLine[i]=='\\' && (!zLine[i+1] || zLine[i+1]=='\r'
191 || zLine[i+1]=='\n') ){
192 zNewline = "";
193
+10 -4
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -32,13 +32,13 @@
3232
3333
SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -DHAVE_USLEEP -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
3434
3535
PIKCHR_OPTIONS = -DPIKCHR_TOKEN_LIMIT=10000
3636
37
-SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c patch_.c path_.c piechart_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c
37
+SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c match_.c md5_.c merge_.c merge3_.c moderate_.c name_.c patch_.c path_.c piechart_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c
3838
39
-OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\patch$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchrshow$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
39
+OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\match$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\patch$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchrshow$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
4040
4141
4242
RC=$(DMDIR)\bin\rcc
4343
RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
4444
@@ -53,11 +53,11 @@
5353
5454
$(OBJDIR)\fossil.res: $B\win\fossil.rc
5555
$(RC) $(RCFLAGS) -o$@ $**
5656
5757
$(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
58
- +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name patch path piechart pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@
58
+ +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html match md5 merge merge3 moderate name patch path piechart pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@
5959
+echo fossil >> $@
6060
+echo fossil >> $@
6161
+echo $(LIBS) >> $@
6262
+echo. >> $@
6363
+echo fossil >> $@
@@ -635,10 +635,16 @@
635635
$(OBJDIR)\markdown_html$O : markdown_html_.c markdown_html.h
636636
$(TCC) -o$@ -c markdown_html_.c
637637
638638
markdown_html_.c : $(SRCDIR)\markdown_html.c
639639
+translate$E $** > $@
640
+
641
+$(OBJDIR)\match$O : match_.c match.h
642
+ $(TCC) -o$@ -c match_.c
643
+
644
+match_.c : $(SRCDIR)\match.c
645
+ +translate$E $** > $@
640646
641647
$(OBJDIR)\md5$O : md5_.c md5.h
642648
$(TCC) -o$@ -c md5_.c
643649
644650
md5_.c : $(SRCDIR)\md5.c
@@ -1009,7 +1015,7 @@
10091015
10101016
zip_.c : $(SRCDIR)\zip.c
10111017
+translate$E $** > $@
10121018
10131019
headers: makeheaders$E page_index.h builtin_data.h VERSION.h
1014
- +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h interwiki_.c:interwiki.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h patch_.c:patch.h path_.c:path.h piechart_.c:piechart.h pikchrshow_.c:pikchrshow.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR_extsrc)\pikchr.c:pikchr.h $(SRCDIR_extsrc)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR_extsrc)\cson_amalgamation.h
1020
+ +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h interwiki_.c:interwiki.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h match_.c:match.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h patch_.c:patch.h path_.c:path.h piechart_.c:piechart.h pikchrshow_.c:pikchrshow.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR_extsrc)\pikchr.c:pikchr.h $(SRCDIR_extsrc)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR_extsrc)\cson_amalgamation.h
10151021
@copy /Y nul: headers
10161022
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -32,13 +32,13 @@
32
33 SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -DHAVE_USLEEP -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
34
35 PIKCHR_OPTIONS = -DPIKCHR_TOKEN_LIMIT=10000
36
37 SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c patch_.c path_.c piechart_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c
38
39 OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\patch$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchrshow$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
40
41
42 RC=$(DMDIR)\bin\rcc
43 RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
44
@@ -53,11 +53,11 @@
53
54 $(OBJDIR)\fossil.res: $B\win\fossil.rc
55 $(RC) $(RCFLAGS) -o$@ $**
56
57 $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
58 +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name patch path piechart pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@
59 +echo fossil >> $@
60 +echo fossil >> $@
61 +echo $(LIBS) >> $@
62 +echo. >> $@
63 +echo fossil >> $@
@@ -635,10 +635,16 @@
635 $(OBJDIR)\markdown_html$O : markdown_html_.c markdown_html.h
636 $(TCC) -o$@ -c markdown_html_.c
637
638 markdown_html_.c : $(SRCDIR)\markdown_html.c
639 +translate$E $** > $@
 
 
 
 
 
 
640
641 $(OBJDIR)\md5$O : md5_.c md5.h
642 $(TCC) -o$@ -c md5_.c
643
644 md5_.c : $(SRCDIR)\md5.c
@@ -1009,7 +1015,7 @@
1009
1010 zip_.c : $(SRCDIR)\zip.c
1011 +translate$E $** > $@
1012
1013 headers: makeheaders$E page_index.h builtin_data.h VERSION.h
1014 +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h interwiki_.c:interwiki.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h patch_.c:patch.h path_.c:path.h piechart_.c:piechart.h pikchrshow_.c:pikchrshow.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR_extsrc)\pikchr.c:pikchr.h $(SRCDIR_extsrc)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR_extsrc)\cson_amalgamation.h
1015 @copy /Y nul: headers
1016
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -32,13 +32,13 @@
32
33 SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -DHAVE_USLEEP -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
34
35 PIKCHR_OPTIONS = -DPIKCHR_TOKEN_LIMIT=10000
36
37 SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c match_.c md5_.c merge_.c merge3_.c moderate_.c name_.c patch_.c path_.c piechart_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c
38
39 OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\match$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\patch$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchrshow$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
40
41
42 RC=$(DMDIR)\bin\rcc
43 RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
44
@@ -53,11 +53,11 @@
53
54 $(OBJDIR)\fossil.res: $B\win\fossil.rc
55 $(RC) $(RCFLAGS) -o$@ $**
56
57 $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
58 +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html match md5 merge merge3 moderate name patch path piechart pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@
59 +echo fossil >> $@
60 +echo fossil >> $@
61 +echo $(LIBS) >> $@
62 +echo. >> $@
63 +echo fossil >> $@
@@ -635,10 +635,16 @@
635 $(OBJDIR)\markdown_html$O : markdown_html_.c markdown_html.h
636 $(TCC) -o$@ -c markdown_html_.c
637
638 markdown_html_.c : $(SRCDIR)\markdown_html.c
639 +translate$E $** > $@
640
641 $(OBJDIR)\match$O : match_.c match.h
642 $(TCC) -o$@ -c match_.c
643
644 match_.c : $(SRCDIR)\match.c
645 +translate$E $** > $@
646
647 $(OBJDIR)\md5$O : md5_.c md5.h
648 $(TCC) -o$@ -c md5_.c
649
650 md5_.c : $(SRCDIR)\md5.c
@@ -1009,7 +1015,7 @@
1015
1016 zip_.c : $(SRCDIR)\zip.c
1017 +translate$E $** > $@
1018
1019 headers: makeheaders$E page_index.h builtin_data.h VERSION.h
1020 +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h interwiki_.c:interwiki.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h match_.c:match.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h patch_.c:patch.h path_.c:path.h piechart_.c:piechart.h pikchrshow_.c:pikchrshow.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR_extsrc)\pikchr.c:pikchr.h $(SRCDIR_extsrc)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR_extsrc)\cson_amalgamation.h
1021 @copy /Y nul: headers
1022
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -485,10 +485,11 @@
485485
$(SRCDIR)/lookslike.c \
486486
$(SRCDIR)/main.c \
487487
$(SRCDIR)/manifest.c \
488488
$(SRCDIR)/markdown.c \
489489
$(SRCDIR)/markdown_html.c \
490
+ $(SRCDIR)/match.c \
490491
$(SRCDIR)/md5.c \
491492
$(SRCDIR)/merge.c \
492493
$(SRCDIR)/merge3.c \
493494
$(SRCDIR)/moderate.c \
494495
$(SRCDIR)/name.c \
@@ -750,10 +751,11 @@
750751
$(OBJDIR)/lookslike_.c \
751752
$(OBJDIR)/main_.c \
752753
$(OBJDIR)/manifest_.c \
753754
$(OBJDIR)/markdown_.c \
754755
$(OBJDIR)/markdown_html_.c \
756
+ $(OBJDIR)/match_.c \
755757
$(OBJDIR)/md5_.c \
756758
$(OBJDIR)/merge_.c \
757759
$(OBJDIR)/merge3_.c \
758760
$(OBJDIR)/moderate_.c \
759761
$(OBJDIR)/name_.c \
@@ -899,10 +901,11 @@
899901
$(OBJDIR)/lookslike.o \
900902
$(OBJDIR)/main.o \
901903
$(OBJDIR)/manifest.o \
902904
$(OBJDIR)/markdown.o \
903905
$(OBJDIR)/markdown_html.o \
906
+ $(OBJDIR)/match.o \
904907
$(OBJDIR)/md5.o \
905908
$(OBJDIR)/merge.o \
906909
$(OBJDIR)/merge3.o \
907910
$(OBJDIR)/moderate.o \
908911
$(OBJDIR)/name.o \
@@ -1252,10 +1255,11 @@
12521255
$(OBJDIR)/lookslike_.c:$(OBJDIR)/lookslike.h \
12531256
$(OBJDIR)/main_.c:$(OBJDIR)/main.h \
12541257
$(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \
12551258
$(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \
12561259
$(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \
1260
+ $(OBJDIR)/match_.c:$(OBJDIR)/match.h \
12571261
$(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \
12581262
$(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \
12591263
$(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \
12601264
$(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \
12611265
$(OBJDIR)/name_.c:$(OBJDIR)/name.h \
@@ -2003,10 +2007,18 @@
20032007
20042008
$(OBJDIR)/markdown_html.o: $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h $(SRCDIR)/config.h
20052009
$(XTCC) -o $(OBJDIR)/markdown_html.o -c $(OBJDIR)/markdown_html_.c
20062010
20072011
$(OBJDIR)/markdown_html.h: $(OBJDIR)/headers
2012
+
2013
+$(OBJDIR)/match_.c: $(SRCDIR)/match.c $(TRANSLATE)
2014
+ $(TRANSLATE) $(SRCDIR)/match.c >$@
2015
+
2016
+$(OBJDIR)/match.o: $(OBJDIR)/match_.c $(OBJDIR)/match.h $(SRCDIR)/config.h
2017
+ $(XTCC) -o $(OBJDIR)/match.o -c $(OBJDIR)/match_.c
2018
+
2019
+$(OBJDIR)/match.h: $(OBJDIR)/headers
20082020
20092021
$(OBJDIR)/md5_.c: $(SRCDIR)/md5.c $(TRANSLATE)
20102022
$(TRANSLATE) $(SRCDIR)/md5.c >$@
20112023
20122024
$(OBJDIR)/md5.o: $(OBJDIR)/md5_.c $(OBJDIR)/md5.h $(SRCDIR)/config.h
20132025
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -485,10 +485,11 @@
485 $(SRCDIR)/lookslike.c \
486 $(SRCDIR)/main.c \
487 $(SRCDIR)/manifest.c \
488 $(SRCDIR)/markdown.c \
489 $(SRCDIR)/markdown_html.c \
 
490 $(SRCDIR)/md5.c \
491 $(SRCDIR)/merge.c \
492 $(SRCDIR)/merge3.c \
493 $(SRCDIR)/moderate.c \
494 $(SRCDIR)/name.c \
@@ -750,10 +751,11 @@
750 $(OBJDIR)/lookslike_.c \
751 $(OBJDIR)/main_.c \
752 $(OBJDIR)/manifest_.c \
753 $(OBJDIR)/markdown_.c \
754 $(OBJDIR)/markdown_html_.c \
 
755 $(OBJDIR)/md5_.c \
756 $(OBJDIR)/merge_.c \
757 $(OBJDIR)/merge3_.c \
758 $(OBJDIR)/moderate_.c \
759 $(OBJDIR)/name_.c \
@@ -899,10 +901,11 @@
899 $(OBJDIR)/lookslike.o \
900 $(OBJDIR)/main.o \
901 $(OBJDIR)/manifest.o \
902 $(OBJDIR)/markdown.o \
903 $(OBJDIR)/markdown_html.o \
 
904 $(OBJDIR)/md5.o \
905 $(OBJDIR)/merge.o \
906 $(OBJDIR)/merge3.o \
907 $(OBJDIR)/moderate.o \
908 $(OBJDIR)/name.o \
@@ -1252,10 +1255,11 @@
1252 $(OBJDIR)/lookslike_.c:$(OBJDIR)/lookslike.h \
1253 $(OBJDIR)/main_.c:$(OBJDIR)/main.h \
1254 $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \
1255 $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \
1256 $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \
 
1257 $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \
1258 $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \
1259 $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \
1260 $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \
1261 $(OBJDIR)/name_.c:$(OBJDIR)/name.h \
@@ -2003,10 +2007,18 @@
2003
2004 $(OBJDIR)/markdown_html.o: $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h $(SRCDIR)/config.h
2005 $(XTCC) -o $(OBJDIR)/markdown_html.o -c $(OBJDIR)/markdown_html_.c
2006
2007 $(OBJDIR)/markdown_html.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
 
2008
2009 $(OBJDIR)/md5_.c: $(SRCDIR)/md5.c $(TRANSLATE)
2010 $(TRANSLATE) $(SRCDIR)/md5.c >$@
2011
2012 $(OBJDIR)/md5.o: $(OBJDIR)/md5_.c $(OBJDIR)/md5.h $(SRCDIR)/config.h
2013
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -485,10 +485,11 @@
485 $(SRCDIR)/lookslike.c \
486 $(SRCDIR)/main.c \
487 $(SRCDIR)/manifest.c \
488 $(SRCDIR)/markdown.c \
489 $(SRCDIR)/markdown_html.c \
490 $(SRCDIR)/match.c \
491 $(SRCDIR)/md5.c \
492 $(SRCDIR)/merge.c \
493 $(SRCDIR)/merge3.c \
494 $(SRCDIR)/moderate.c \
495 $(SRCDIR)/name.c \
@@ -750,10 +751,11 @@
751 $(OBJDIR)/lookslike_.c \
752 $(OBJDIR)/main_.c \
753 $(OBJDIR)/manifest_.c \
754 $(OBJDIR)/markdown_.c \
755 $(OBJDIR)/markdown_html_.c \
756 $(OBJDIR)/match_.c \
757 $(OBJDIR)/md5_.c \
758 $(OBJDIR)/merge_.c \
759 $(OBJDIR)/merge3_.c \
760 $(OBJDIR)/moderate_.c \
761 $(OBJDIR)/name_.c \
@@ -899,10 +901,11 @@
901 $(OBJDIR)/lookslike.o \
902 $(OBJDIR)/main.o \
903 $(OBJDIR)/manifest.o \
904 $(OBJDIR)/markdown.o \
905 $(OBJDIR)/markdown_html.o \
906 $(OBJDIR)/match.o \
907 $(OBJDIR)/md5.o \
908 $(OBJDIR)/merge.o \
909 $(OBJDIR)/merge3.o \
910 $(OBJDIR)/moderate.o \
911 $(OBJDIR)/name.o \
@@ -1252,10 +1255,11 @@
1255 $(OBJDIR)/lookslike_.c:$(OBJDIR)/lookslike.h \
1256 $(OBJDIR)/main_.c:$(OBJDIR)/main.h \
1257 $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \
1258 $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \
1259 $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \
1260 $(OBJDIR)/match_.c:$(OBJDIR)/match.h \
1261 $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \
1262 $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \
1263 $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \
1264 $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \
1265 $(OBJDIR)/name_.c:$(OBJDIR)/name.h \
@@ -2003,10 +2007,18 @@
2007
2008 $(OBJDIR)/markdown_html.o: $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h $(SRCDIR)/config.h
2009 $(XTCC) -o $(OBJDIR)/markdown_html.o -c $(OBJDIR)/markdown_html_.c
2010
2011 $(OBJDIR)/markdown_html.h: $(OBJDIR)/headers
2012
2013 $(OBJDIR)/match_.c: $(SRCDIR)/match.c $(TRANSLATE)
2014 $(TRANSLATE) $(SRCDIR)/match.c >$@
2015
2016 $(OBJDIR)/match.o: $(OBJDIR)/match_.c $(OBJDIR)/match.h $(SRCDIR)/config.h
2017 $(XTCC) -o $(OBJDIR)/match.o -c $(OBJDIR)/match_.c
2018
2019 $(OBJDIR)/match.h: $(OBJDIR)/headers
2020
2021 $(OBJDIR)/md5_.c: $(SRCDIR)/md5.c $(TRANSLATE)
2022 $(TRANSLATE) $(SRCDIR)/md5.c >$@
2023
2024 $(OBJDIR)/md5.o: $(OBJDIR)/md5_.c $(OBJDIR)/md5.h $(SRCDIR)/config.h
2025
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -443,10 +443,11 @@
443443
"$(OX)\lookslike_.c" \
444444
"$(OX)\main_.c" \
445445
"$(OX)\manifest_.c" \
446446
"$(OX)\markdown_.c" \
447447
"$(OX)\markdown_html_.c" \
448
+ "$(OX)\match_.c" \
448449
"$(OX)\md5_.c" \
449450
"$(OX)\merge_.c" \
450451
"$(OX)\merge3_.c" \
451452
"$(OX)\moderate_.c" \
452453
"$(OX)\name_.c" \
@@ -708,10 +709,11 @@
708709
"$(OX)\lookslike$O" \
709710
"$(OX)\main$O" \
710711
"$(OX)\manifest$O" \
711712
"$(OX)\markdown$O" \
712713
"$(OX)\markdown_html$O" \
714
+ "$(OX)\match$O" \
713715
"$(OX)\md5$O" \
714716
"$(OX)\merge$O" \
715717
"$(OX)\merge3$O" \
716718
"$(OX)\moderate$O" \
717719
"$(OX)\name$O" \
@@ -957,10 +959,11 @@
957959
echo "$(OX)\lookslike.obj" >> $@
958960
echo "$(OX)\main.obj" >> $@
959961
echo "$(OX)\manifest.obj" >> $@
960962
echo "$(OX)\markdown.obj" >> $@
961963
echo "$(OX)\markdown_html.obj" >> $@
964
+ echo "$(OX)\match.obj" >> $@
962965
echo "$(OX)\md5.obj" >> $@
963966
echo "$(OX)\merge.obj" >> $@
964967
echo "$(OX)\merge3.obj" >> $@
965968
echo "$(OX)\moderate.obj" >> $@
966969
echo "$(OX)\name.obj" >> $@
@@ -1762,10 +1765,16 @@
17621765
"$(OX)\markdown_html$O" : "$(OX)\markdown_html_.c" "$(OX)\markdown_html.h"
17631766
$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\markdown_html_.c"
17641767
17651768
"$(OX)\markdown_html_.c" : "$(SRCDIR)\markdown_html.c"
17661769
"$(OBJDIR)\translate$E" $** > $@
1770
+
1771
+"$(OX)\match$O" : "$(OX)\match_.c" "$(OX)\match.h"
1772
+ $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\match_.c"
1773
+
1774
+"$(OX)\match_.c" : "$(SRCDIR)\match.c"
1775
+ "$(OBJDIR)\translate$E" $** > $@
17671776
17681777
"$(OX)\md5$O" : "$(OX)\md5_.c" "$(OX)\md5.h"
17691778
$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\md5_.c"
17701779
17711780
"$(OX)\md5_.c" : "$(SRCDIR)\md5.c"
@@ -2224,10 +2233,11 @@
22242233
"$(OX)\lookslike_.c":"$(OX)\lookslike.h" \
22252234
"$(OX)\main_.c":"$(OX)\main.h" \
22262235
"$(OX)\manifest_.c":"$(OX)\manifest.h" \
22272236
"$(OX)\markdown_.c":"$(OX)\markdown.h" \
22282237
"$(OX)\markdown_html_.c":"$(OX)\markdown_html.h" \
2238
+ "$(OX)\match_.c":"$(OX)\match.h" \
22292239
"$(OX)\md5_.c":"$(OX)\md5.h" \
22302240
"$(OX)\merge_.c":"$(OX)\merge.h" \
22312241
"$(OX)\merge3_.c":"$(OX)\merge3.h" \
22322242
"$(OX)\moderate_.c":"$(OX)\moderate.h" \
22332243
"$(OX)\name_.c":"$(OX)\name.h" \
22342244
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -443,10 +443,11 @@
443 "$(OX)\lookslike_.c" \
444 "$(OX)\main_.c" \
445 "$(OX)\manifest_.c" \
446 "$(OX)\markdown_.c" \
447 "$(OX)\markdown_html_.c" \
 
448 "$(OX)\md5_.c" \
449 "$(OX)\merge_.c" \
450 "$(OX)\merge3_.c" \
451 "$(OX)\moderate_.c" \
452 "$(OX)\name_.c" \
@@ -708,10 +709,11 @@
708 "$(OX)\lookslike$O" \
709 "$(OX)\main$O" \
710 "$(OX)\manifest$O" \
711 "$(OX)\markdown$O" \
712 "$(OX)\markdown_html$O" \
 
713 "$(OX)\md5$O" \
714 "$(OX)\merge$O" \
715 "$(OX)\merge3$O" \
716 "$(OX)\moderate$O" \
717 "$(OX)\name$O" \
@@ -957,10 +959,11 @@
957 echo "$(OX)\lookslike.obj" >> $@
958 echo "$(OX)\main.obj" >> $@
959 echo "$(OX)\manifest.obj" >> $@
960 echo "$(OX)\markdown.obj" >> $@
961 echo "$(OX)\markdown_html.obj" >> $@
 
962 echo "$(OX)\md5.obj" >> $@
963 echo "$(OX)\merge.obj" >> $@
964 echo "$(OX)\merge3.obj" >> $@
965 echo "$(OX)\moderate.obj" >> $@
966 echo "$(OX)\name.obj" >> $@
@@ -1762,10 +1765,16 @@
1762 "$(OX)\markdown_html$O" : "$(OX)\markdown_html_.c" "$(OX)\markdown_html.h"
1763 $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\markdown_html_.c"
1764
1765 "$(OX)\markdown_html_.c" : "$(SRCDIR)\markdown_html.c"
1766 "$(OBJDIR)\translate$E" $** > $@
 
 
 
 
 
 
1767
1768 "$(OX)\md5$O" : "$(OX)\md5_.c" "$(OX)\md5.h"
1769 $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\md5_.c"
1770
1771 "$(OX)\md5_.c" : "$(SRCDIR)\md5.c"
@@ -2224,10 +2233,11 @@
2224 "$(OX)\lookslike_.c":"$(OX)\lookslike.h" \
2225 "$(OX)\main_.c":"$(OX)\main.h" \
2226 "$(OX)\manifest_.c":"$(OX)\manifest.h" \
2227 "$(OX)\markdown_.c":"$(OX)\markdown.h" \
2228 "$(OX)\markdown_html_.c":"$(OX)\markdown_html.h" \
 
2229 "$(OX)\md5_.c":"$(OX)\md5.h" \
2230 "$(OX)\merge_.c":"$(OX)\merge.h" \
2231 "$(OX)\merge3_.c":"$(OX)\merge3.h" \
2232 "$(OX)\moderate_.c":"$(OX)\moderate.h" \
2233 "$(OX)\name_.c":"$(OX)\name.h" \
2234
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -443,10 +443,11 @@
443 "$(OX)\lookslike_.c" \
444 "$(OX)\main_.c" \
445 "$(OX)\manifest_.c" \
446 "$(OX)\markdown_.c" \
447 "$(OX)\markdown_html_.c" \
448 "$(OX)\match_.c" \
449 "$(OX)\md5_.c" \
450 "$(OX)\merge_.c" \
451 "$(OX)\merge3_.c" \
452 "$(OX)\moderate_.c" \
453 "$(OX)\name_.c" \
@@ -708,10 +709,11 @@
709 "$(OX)\lookslike$O" \
710 "$(OX)\main$O" \
711 "$(OX)\manifest$O" \
712 "$(OX)\markdown$O" \
713 "$(OX)\markdown_html$O" \
714 "$(OX)\match$O" \
715 "$(OX)\md5$O" \
716 "$(OX)\merge$O" \
717 "$(OX)\merge3$O" \
718 "$(OX)\moderate$O" \
719 "$(OX)\name$O" \
@@ -957,10 +959,11 @@
959 echo "$(OX)\lookslike.obj" >> $@
960 echo "$(OX)\main.obj" >> $@
961 echo "$(OX)\manifest.obj" >> $@
962 echo "$(OX)\markdown.obj" >> $@
963 echo "$(OX)\markdown_html.obj" >> $@
964 echo "$(OX)\match.obj" >> $@
965 echo "$(OX)\md5.obj" >> $@
966 echo "$(OX)\merge.obj" >> $@
967 echo "$(OX)\merge3.obj" >> $@
968 echo "$(OX)\moderate.obj" >> $@
969 echo "$(OX)\name.obj" >> $@
@@ -1762,10 +1765,16 @@
1765 "$(OX)\markdown_html$O" : "$(OX)\markdown_html_.c" "$(OX)\markdown_html.h"
1766 $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\markdown_html_.c"
1767
1768 "$(OX)\markdown_html_.c" : "$(SRCDIR)\markdown_html.c"
1769 "$(OBJDIR)\translate$E" $** > $@
1770
1771 "$(OX)\match$O" : "$(OX)\match_.c" "$(OX)\match.h"
1772 $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\match_.c"
1773
1774 "$(OX)\match_.c" : "$(SRCDIR)\match.c"
1775 "$(OBJDIR)\translate$E" $** > $@
1776
1777 "$(OX)\md5$O" : "$(OX)\md5_.c" "$(OX)\md5.h"
1778 $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\md5_.c"
1779
1780 "$(OX)\md5_.c" : "$(SRCDIR)\md5.c"
@@ -2224,10 +2233,11 @@
2233 "$(OX)\lookslike_.c":"$(OX)\lookslike.h" \
2234 "$(OX)\main_.c":"$(OX)\main.h" \
2235 "$(OX)\manifest_.c":"$(OX)\manifest.h" \
2236 "$(OX)\markdown_.c":"$(OX)\markdown.h" \
2237 "$(OX)\markdown_html_.c":"$(OX)\markdown_html.h" \
2238 "$(OX)\match_.c":"$(OX)\match.h" \
2239 "$(OX)\md5_.c":"$(OX)\md5.h" \
2240 "$(OX)\merge_.c":"$(OX)\merge.h" \
2241 "$(OX)\merge3_.c":"$(OX)\merge3.h" \
2242 "$(OX)\moderate_.c":"$(OX)\moderate.h" \
2243 "$(OX)\name_.c":"$(OX)\name.h" \
2244
--- www/changes.wiki
+++ www/changes.wiki
@@ -16,10 +16,38 @@
1616
merge or update operation.
1717
* Issue a warning if a user tries to commit on a check-in where the
1818
branch has been changed.
1919
* When a merge conflict occurs, a new section is added to the conflict
2020
text that shows Fossil's suggested resolution to the conflict.
21
+ * Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show
22
+ diffs of multiple files.
23
+ * Enhancements to the [/help?cmd=/timeline|/timeline page]:
24
+ <ol type="a">
25
+ <li> Added the "ml=" ("Merge-in List") query parameter that works
26
+ like "rl=" ("Related List") but adds "mionly" style related
27
+ check-ins instead of the full "rel" style.
28
+ <li> For "tl=", "rl=", and "ml=", the order of the branches in the
29
+ graph now tries to match the order of the branches named in
30
+ the list.
31
+ <li> The "ms=" ("Match Style") query parameter is honored for
32
+ "tl=", "rl=", and "ml=".
33
+ <li> New query parameter "sl=BRANCHLIST" ("Sort List") strives to
34
+ put branches in the specified order in the graph. This
35
+ overrides any "tl=" or similar ordering.
36
+ <li> In the various "from=","to=" query formats, if the one of the
37
+ end points is an ancestor of the other, then the "rel" modifier
38
+ omits check-ins that are not ancestors of the newer endpoint.
39
+ <li> For "tl=" and similar query parameters, if the pattern contains
40
+ GLOB characters, then the matching style ("ms=") is set to GLOB
41
+ automatically and the "ms=" query parameter can be omitted.
42
+ </ol>
43
+ * Added the [/help?cmd=/clusterlist|/clusterlist page] for analysis
44
+ and debugging
45
+ * Fix a bug in [/help?cmd=patch|fossil patch create] that causes
46
+ [/help?cmd=revert|fossil revert] operations that happened on individual
47
+ files after a [/help?cmd=merge|fossil merge] to be omitted from the
48
+ patch.
2149
2250
<h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2>
2351
2452
* The "[/help?cmd=ui|fossil ui /]" command now works even for repositories
2553
that have non-ASCII filenames
2654
--- www/changes.wiki
+++ www/changes.wiki
@@ -16,10 +16,38 @@
16 merge or update operation.
17 * Issue a warning if a user tries to commit on a check-in where the
18 branch has been changed.
19 * When a merge conflict occurs, a new section is added to the conflict
20 text that shows Fossil's suggested resolution to the conflict.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
22 <h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2>
23
24 * The "[/help?cmd=ui|fossil ui /]" command now works even for repositories
25 that have non-ASCII filenames
26
--- www/changes.wiki
+++ www/changes.wiki
@@ -16,10 +16,38 @@
16 merge or update operation.
17 * Issue a warning if a user tries to commit on a check-in where the
18 branch has been changed.
19 * When a merge conflict occurs, a new section is added to the conflict
20 text that shows Fossil's suggested resolution to the conflict.
21 * Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show
22 diffs of multiple files.
23 * Enhancements to the [/help?cmd=/timeline|/timeline page]:
24 <ol type="a">
25 <li> Added the "ml=" ("Merge-in List") query parameter that works
26 like "rl=" ("Related List") but adds "mionly" style related
27 check-ins instead of the full "rel" style.
28 <li> For "tl=", "rl=", and "ml=", the order of the branches in the
29 graph now tries to match the order of the branches named in
30 the list.
31 <li> The "ms=" ("Match Style") query parameter is honored for
32 "tl=", "rl=", and "ml=".
33 <li> New query parameter "sl=BRANCHLIST" ("Sort List") strives to
34 put branches in the specified order in the graph. This
35 overrides any "tl=" or similar ordering.
36 <li> In the various "from=","to=" query formats, if the one of the
37 end points is an ancestor of the other, then the "rel" modifier
38 omits check-ins that are not ancestors of the newer endpoint.
39 <li> For "tl=" and similar query parameters, if the pattern contains
40 GLOB characters, then the matching style ("ms=") is set to GLOB
41 automatically and the "ms=" query parameter can be omitted.
42 </ol>
43 * Added the [/help?cmd=/clusterlist|/clusterlist page] for analysis
44 and debugging
45 * Fix a bug in [/help?cmd=patch|fossil patch create] that causes
46 [/help?cmd=revert|fossil revert] operations that happened on individual
47 files after a [/help?cmd=merge|fossil merge] to be omitted from the
48 patch.
49
50 <h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2>
51
52 * The "[/help?cmd=ui|fossil ui /]" command now works even for repositories
53 that have non-ASCII filenames
54
+197 -88
--- www/ssl-server.md
+++ www/ssl-server.md
@@ -12,76 +12,107 @@
1212
1313
[0]: ./ssl.wiki
1414
[1]: /timeline?c=b05cb4a0e15d0712&y=ci&n=13
1515
1616
Beginning in [late December 2021](/timeline?c=f6263bb64195b07f&y=a&n=13),
17
-this has been fixed. Commands like
17
+Fossil servers are now able to converse directly over TLS. Commands like
1818
1919
* "[fossil server](/help?cmd=server)"
2020
* "[fossil ui](/help?cmd=ui)", and
2121
* "[fossil http](/help?cmd=http)"
2222
23
-now all handle server-mode SSL/TLS encryption natively. It is now possible
24
-to run a secure Fossil server without having to put Fossil behind an encrypting
25
-web server or reverse proxy. Hence, it is now possible to stand up a complete
26
-Fossil project website on an inexpensive VPS with no added software other than
27
-Fossil itself and something like [certbot](https://certbot.eff.org) for
28
-obtaining a CA-signed certificate.
29
-
30
-## Usage
23
+may now handle the encryption natively when suitably configured, without
24
+requiring a third-party proxy layer.
25
+
26
+## <a id="usage"></a>Usage
3127
3228
To put any of the Fossil server commands into SSL/TLS mode, simply
33
-add the "--cert" command-line option.
29
+add the "`--cert`" command-line option:
3430
3531
fossil ui --cert unsafe-builtin
3632
37
-The --cert option is what tells Fossil to use TLS encryption.
38
-Normally, the argument to --cert is the name of a file containing
39
-the certificate (the "fullchain.pem" file) for the website. In this
40
-example, the magic name "unsafe-builtin" is used, which causes Fossil
41
-to use a self-signed cert rather than a real cert obtained from a
42
-[Certificate Authority](https://en.wikipedia.org/wiki/Certificate_authority)
43
-or "CA". As the name implies, this self-signed cert is not secure and
44
-should only be used for testing. Your web-browser will complain bitterly
45
-and will refuse to display the pages using the "unsafe-builtin" cert.
46
-Firefox will allow you to click an "I know the risks" button and continue.
47
-Other web browsers will stubornly refuse to display the page, under the theory
48
-that weak encryption is worse than no encryption at all. Continue reading
49
-to see how to solve this.
50
-
51
-## About Certs
52
-
53
-Certs are based on public-key or asymmetric cryptography. To create a cert,
54
-you first create a new "key pair" consisting of a public key and a private key.
55
-The public key can be freely shared with the world, but you must keep the
56
-private key secret. If anyone gains access to your private key then he will be
57
-able to impersonate you and break into your system.
58
-
59
-To obtain a cert, you send your public key and the name of the domain you
60
-want to protect to a certificate authority. The CA then digitally signs
61
-the combination of those two things using their own private key and sends
62
-the signed combination back to you. The CA's digital signature of your
63
-public key and domain name is the cert.
64
-
65
-SSL/TLS servers need two things in order to prove their identity to clients:
66
-
67
- 1. The cert that was signed by a CA
68
- 2. The private key
69
-
70
-The SSL/TLS servers send the cert to each client, so that the client
71
-can verify it. But the private key is kept strictly private and is never
72
-shared with anyone.
73
-
74
-## How To Tell Fossil About Your Cert And Private Key
75
-
76
-If you do not have your own cert and private key, you can ask Fossil
33
+Here, we are passing the magic name "unsafe-builtin" to cause Fossil to
34
+use a [hard-coded self-signed cert][hcssc] rather than one obtained from
35
+a recognized [Certificate Authority][CA], or "CA".
36
+
37
+As the name implies, this self-signed cert is _not secure_ and should
38
+only be used for testing. Your web browser is likely to complain
39
+bitterly about it and will refuse to display the pages using the
40
+"unsafe-builtin" cert until you placate it. The complexity of the
41
+ceremony demanded depends on how paranoid your browser’s creators have
42
+decided to be. It may require as little as clicking a single big "I know
43
+the risks" type of button, or it may require a sequence be several
44
+clicks designed to discourage the “yes, yes, just let me do the thing”
45
+crowd lest they run themselves into trouble by disregarding well-meant
46
+warnings.
47
+
48
+Our purpose here is to show you an alternate path that will avoid the
49
+issue entirely, not weigh in on which browser handles self-signed
50
+certificates best.
51
+
52
+[CA]: https://en.wikipedia.org/wiki/Certificate_authority
53
+[hcssc]: /info/c2a7b14c3f541edb96?ln=89-116
54
+
55
+## <a id="about"></a>About Certs
56
+
57
+The X.509 certificate system used by browsers to secure TLS connections
58
+is based on asymmetric public-key cryptography. The methods for
59
+obtaining one vary widely, with a resulting tradeoff we may summarize as
60
+trustworthiness versus convenience, the latter characteristic falling as
61
+the former rises.(^No strict correlation exists. CAs have invented
62
+highly inconvenient certification schemes that offer little additional
63
+real-world trustworthiness. Extreme cases along this axis may be fairly
64
+characterized as [security theater][st]. We focus in this document on
65
+well-balanced trade-offs between decreasing convenience and useful
66
+levels of trustworthiness gained thereby.)
67
+
68
+The self-signed method demonstrated above offers approximately zero
69
+trustworthiness, though not zero _value_ since it does still provide
70
+connection encryption.
71
+
72
+More trustworthy methods are necessarily less convenient. One such is to
73
+send your public key and the name of the domain you want to protect to a
74
+recognized CA, which then performs one or more tests to convince itself
75
+that the requester is in control of that domain. If the CA’s tests all
76
+pass, it produces an X.509 certificate bound to that domain, which
77
+includes assorted other information under the CA’s digital signature
78
+attesting to the validity of the document’s contents. The result is sent
79
+back to the requester, which may then use it to transitively attest to
80
+these tests’ success: presuming one cannot fake the type of signature
81
+used, the document must have been signed by the trusted, recognized CA.
82
+
83
+There is one element of the assorted information included with a
84
+certificate that is neither supplied by the requester nor rubber-stamped
85
+on it in passing by the CA. It also generates a one-time key pair and
86
+stores the public half in the certificate. The cryptosystem this keypair
87
+is intended to work with varies both by the CA and by time, as older
88
+systems become obsolete. Details aside, the CA then puts this matching
89
+private half of the key in a separate file, often encrypted under a
90
+separate cryptosystem for security.
91
+
92
+SSL/TLS servers need both resulting halves to make these attestations,
93
+but they send only the public half to the client when establishing the
94
+connection. The client then makes its own checks to determine whether it
95
+trusts the attestations being made.
96
+
97
+A properly written and administered server never releases the private
98
+key to anyone. Ideally, it goes directly from the CA to the requesting
99
+server and never moves from there; then when it expires, the server
100
+deletes it permanently.
101
+
102
+[st]: https://en.wikipedia.org/wiki/Security_theater
103
+
104
+## <a id="startup"></a>How To Tell Fossil About Your Cert And Private Key
105
+
106
+As we saw [above](#usage),
107
+if you do not have your own cert and private key, you can ask Fossil
77108
to use "unsafe-builtin", which is a self-signed cert that is built into
78
-Fossil. This is wildly insecure, since the private key is not really private -
79
-it is [in plain sight](/info/c2a7b14c3f541edb96?ln=89-116) in the Fossil
109
+Fossil. This is wildly insecure, since the private key is not really private;
110
+it is [in plain sight][hcssc] in the Fossil
80111
source tree for anybody to read. <b>Never add the private key that is
81112
built into Fossil to your OS's trust store</b> as doing so will severely
82
-compromise your computer. The built-in cert is only useful for testing.
113
+compromise your computer.[^ssattack] This built-in cert is only useful for testing.
83114
If you want actual security, you will need to come up with your own private
84115
key and cert.
85116
86117
Fossil wants to read certs and public keys in the
87118
[PEM format](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail).
@@ -98,66 +129,142 @@
98129
*base-64 encoding of the certificate*
99130
-----END CERTIFICATE-----
100131
101132
In both formats, text outside of the delimiters is ignored. That means
102133
that if you have a PEM-formatted private key and a separate PEM-formatted
103
-certificate, you can concatenate the two into a single file and the
134
+certificate, you can concatenate the two into a single file, and the
104135
individual components will still be easily accessible.
105136
106
-If you have a single file that holds both your private key and your
137
+### <a id="cat"></a>Separate or Concatenated?
138
+
139
+Given a single concatenated file that holds both your private key and your
107140
cert, you can hand it off to the "[fossil server](/help?cmd=server)"
108
-command using the --cert option. Like this:
141
+command using the `--cert` option, like this:
109142
110143
fossil server --port 443 --cert mycert.pem /home/www/myproject.fossil
111144
112145
The command above is sufficient to run a fully-encrypted web site for
113146
the "myproject.fossil" Fossil repository. This command must be run as
114147
root, since it wants to listen on TCP port 443, and only root processes are
115148
allowed to do that. This is safe, however, since before reading any
116
-information off of the wire, Fossil will put itself inside a chroot jail
117
-at /home/www and drop all root privileges.
118
-
119
-### Keeping The Cert And Private Key In Separate Files
120
-
121
-If you do not want to combine your cert and private key into a single
122
-big PEM file, you can keep them separate using the --pkey option to
123
-Fossil.
149
+information off of the wire, Fossil will [put itself inside a chroot
150
+jail](./chroot.md) at `/home/www` and drop all root privileges.
151
+
152
+This method of combining your cert and private key into a single big PEM
153
+file carries risks, one of which is that the system administrator must
154
+make both halves readable by the user running the Fossil server. Given
155
+the chroot jail feature, a more secure scheme separates the halves so
156
+that only root can read the private half, which then means that when
157
+Fossil drops its root privileges, it becomes unable to access the
158
+private key on disk. Fossil’s `server` feature includes the `--pkey`
159
+option to allow for that use case:
124160
125161
fossil server --port 443 --cert fullchain.pem --pkey privkey.pem /home/www/myproject.fossil
126162
127
-## The ACME Protocol
163
+[^ssattack]: ^How, you ask? Because the keys are known, they can be used
164
+ to provide signed certificates for **any** other domain. One foolish
165
+ enough to tell their OS’s TLS mechanisms to trust the signing
166
+ certificate is implicitly handing over all TLS encryption controls
167
+ to any attacker that knows they did this. Don’t do it.
168
+
169
+### <a id="chain"></a>Chains and Links
170
+
171
+The file name “`fullchain.pem`” used above is a reference to a term of
172
+art within this world of TLS protocols and their associated X.509
173
+certificates. Within the simplistic scheme originally envisioned by the
174
+creators of SSL — the predecessor to TLS — we were all expected to agree
175
+on a single set of CA root authorities, and we would all agree to get
176
+our certificates from one of them. The real world is more complicated:
177
+
178
+* The closest we have to universal acceptance of CAs is via the
179
+ [CA/Browser Forum][CAB], and even within its select membership there
180
+ is continual argument over which roots are trustworthy. (Hashing
181
+ that out is arguably this group’s key purpose.)
182
+
183
+* CAB’s decision regarding trustworthiness may not match that of any
184
+ given system’s administrator. There are solid, defensible reasons to
185
+ prune back the stock CA root set included with your browser, then to
186
+ augment it with ones CAB _doesn’t_ trust.
187
+
188
+* TLS isn’t limited to use between web browsers and public Internet
189
+ sites. Several common use cases preclude use of the process CAB
190
+ envisions, with servers able to contact Internet-based CA roots as
191
+ part of proving their identity. Different use cases demand different
192
+ CA root authority stores.
193
+
194
+ The most common of these divergent cases are servers behind strict
195
+ firewalls and edge devices that never interact with the public
196
+ Internet. This class ranges from cheap home IoT devices to the
197
+ internal equipment managed by IT for a massive global corporation.
198
+
199
+Your private Fossil server is liable to fall into that last category.
200
+This may then require that you generate a more complicated “chain” of
201
+certificates for Fossil to use here, without which the client may not be
202
+able to get back to a CA root it trusts. This is true regardless of
203
+whether that client is another copy of Fossil or a web browser
204
+traversing Fossil’s web UI, though that fact complicates matters by
205
+allowing for multiple classes of client, each of which may have their
206
+own rules for modifying the stock certificate scheme.
207
+
208
+This is distressingly common, in fact: Fossil links to OpenSSL to
209
+provide its TLS support, but there is a good chance that your browser
210
+uses another TLS implementation entirely. They may or may not agree on a
211
+single CA root store.
212
+
213
+How you accommodate all this complexity varies by the CA and other
214
+details. As but one example, Firefox’s “View Certificate” feature offers
215
+_two_ ways to download a given web site’s certificate: the cert alone or
216
+the “chain” leading back to the root. Depending on the use case, the
217
+standalone certificate might suffice, or you might need some type of
218
+cert chain. Complicating this is that the last link in the chain may be
219
+left off when it is for a mutually trusted CA root, implicitly
220
+completing the chain.
221
+
222
+[CAB]: https://en.wikipedia.org/wiki/CA/Browser_Forum
223
+
224
+## <a id="acme"></a>The ACME Protocol
225
+
226
+The [ACME Protocol][2] simplifies all this by automating the process of
227
+proving to a recognized public CA that you are in control of a given
228
+website. Without this proof, no valid CA will issue a cert for that
229
+domain, as that allows fraudulent impersonation.
128230
129
-The [ACME Protocol][2] is used to prove to a CA that you control a
130
-website. CAs require proof that you control a domain before they
131
-will issue a cert for that domain. The usual means of dealing
132
-with ACME is to run the separate [certbot](https://certbot.eff.org) tool.
231
+The primary implementation of ACME is [certbot], a product of the Let’s
232
+Encrypt organization.
133233
Here is, in a nutshell, what certbot will do to obtain your cert:
134234
135
- 1. Certbot sends your "signing request" (the document that contains
235
+ 1. It sends your "signing request" (the document that contains
136236
your public key and your domain name) to the CA.
137237
138
- 2. After receiving the signing request, the CA needs to verify that you
139
- control the domain of the cert. To do this (or, one common
140
- way of doing this, at least) the CA sends a secret token back to
141
- certbot through a secure backchannel, and instructs certbot to make
142
- that token accessible on the (unencrypted, ordinary "http:") web site
143
- for the domain in a particular file under the ".well-known" subdirectory.
144
-
145
- 3. Certbot puts the token where the CA requested it, then notifies the
146
- CA that it is there.
147
-
148
- 4. The CA accesses the token to confirm that you do indeed control the
149
- website. It then creates the cert and sends it back to certbot.
150
-
151
- 5. Certbot stores your cert and deletes the ".well-known" token.
238
+ 2. After receiving the signing request, the CA needs to verify that
239
+ you control the domain of the cert. One of several methods certbot has
240
+ for accomplishing this is to create a secret token and place it at
241
+ a well-known location, then tell the CA about it over ACME.
242
+
243
+ 3. The CA then tries pulling that token, which if successful proves
244
+ that the requester is able to create arbitrary data on the server,
245
+ implicitly proving control over that server. This must be done
246
+ over the unencrypted HTTP protocol since TLS isn’t working yet.
247
+
248
+ 4. If satisfied by this proof of control, the CA then creates the
249
+ keypair described above and bakes the public half into the
250
+ certificate it signs. It then sends this and the private half of
251
+ the key back to certbot.
252
+
253
+ 5. Certbot stores these halves separately for the reasons sketched
254
+ out above.
255
+
256
+ 6. It then deletes the secret one-time-use token it used to prove
257
+ domain control. ACME’s design precludes replay attacks.
152258
153259
In order for all of this to happen, certbot needs to be able to create
154
-a subdirectory named ".well-known", within a directory you specify, and
260
+a subdirectory named ".well-known", within a directory you specify,
155261
then populate that subdirectory with a token file of some kind. To support
156262
this, the "[fossil server](/help?cmd=server)" and
157263
"[fossil http](/help?cmd=http)" commands have the --acme option.
158
-When the --acme option is specified and Fossil sees a URL where the path
264
+
265
+When specified, Fossil sees a URL where the path
159266
begins with ".well-known", then instead of doing its normal processing, it
160267
looks for a file with that pathname and returns it to the client. If
161268
the "server" or "http" command is referencing a single Fossil repository,
162269
then the ".well-known" sub-directory should be in the same directory as
163270
the repository file. If the "server" or "http" command are run against
@@ -172,9 +279,11 @@
172279
Then you create your public/private key pair and run certbot, giving it
173280
a --webroot of /home/www. Certbot will create the sub-directory
174281
named "/home/www/.well-known" and put token files there, which the CA
175282
will verify. Then certbot will store your new cert in a particular file.
176283
177
-Once certbot has obtained your cert, then you can concatenate that
178
-cert with your private key and run Fossil in SSL/TLS mode as shown above.
284
+Once certbot has obtained your cert, you may either pass the two halves
285
+to Fossil separately using the `--pkey` and `--cert` options described
286
+above, or you may concatenate them and pass that via `--cert` alone.
179287
180288
[2]: https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment
289
+[certbot]: https://certbot.eff.org
181290
--- www/ssl-server.md
+++ www/ssl-server.md
@@ -12,76 +12,107 @@
12
13 [0]: ./ssl.wiki
14 [1]: /timeline?c=b05cb4a0e15d0712&y=ci&n=13
15
16 Beginning in [late December 2021](/timeline?c=f6263bb64195b07f&y=a&n=13),
17 this has been fixed. Commands like
18
19 * "[fossil server](/help?cmd=server)"
20 * "[fossil ui](/help?cmd=ui)", and
21 * "[fossil http](/help?cmd=http)"
22
23 now all handle server-mode SSL/TLS encryption natively. It is now possible
24 to run a secure Fossil server without having to put Fossil behind an encrypting
25 web server or reverse proxy. Hence, it is now possible to stand up a complete
26 Fossil project website on an inexpensive VPS with no added software other than
27 Fossil itself and something like [certbot](https://certbot.eff.org) for
28 obtaining a CA-signed certificate.
29
30 ## Usage
31
32 To put any of the Fossil server commands into SSL/TLS mode, simply
33 add the "--cert" command-line option.
34
35 fossil ui --cert unsafe-builtin
36
37 The --cert option is what tells Fossil to use TLS encryption.
38 Normally, the argument to --cert is the name of a file containing
39 the certificate (the "fullchain.pem" file) for the website. In this
40 example, the magic name "unsafe-builtin" is used, which causes Fossil
41 to use a self-signed cert rather than a real cert obtained from a
42 [Certificate Authority](https://en.wikipedia.org/wiki/Certificate_authority)
43 or "CA". As the name implies, this self-signed cert is not secure and
44 should only be used for testing. Your web-browser will complain bitterly
45 and will refuse to display the pages using the "unsafe-builtin" cert.
46 Firefox will allow you to click an "I know the risks" button and continue.
47 Other web browsers will stubornly refuse to display the page, under the theory
48 that weak encryption is worse than no encryption at all. Continue reading
49 to see how to solve this.
50
51 ## About Certs
52
53 Certs are based on public-key or asymmetric cryptography. To create a cert,
54 you first create a new "key pair" consisting of a public key and a private key.
55 The public key can be freely shared with the world, but you must keep the
56 private key secret. If anyone gains access to your private key then he will be
57 able to impersonate you and break into your system.
58
59 To obtain a cert, you send your public key and the name of the domain you
60 want to protect to a certificate authority. The CA then digitally signs
61 the combination of those two things using their own private key and sends
62 the signed combination back to you. The CA's digital signature of your
63 public key and domain name is the cert.
64
65 SSL/TLS servers need two things in order to prove their identity to clients:
66
67 1. The cert that was signed by a CA
68 2. The private key
69
70 The SSL/TLS servers send the cert to each client, so that the client
71 can verify it. But the private key is kept strictly private and is never
72 shared with anyone.
73
74 ## How To Tell Fossil About Your Cert And Private Key
75
76 If you do not have your own cert and private key, you can ask Fossil
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77 to use "unsafe-builtin", which is a self-signed cert that is built into
78 Fossil. This is wildly insecure, since the private key is not really private -
79 it is [in plain sight](/info/c2a7b14c3f541edb96?ln=89-116) in the Fossil
80 source tree for anybody to read. <b>Never add the private key that is
81 built into Fossil to your OS's trust store</b> as doing so will severely
82 compromise your computer. The built-in cert is only useful for testing.
83 If you want actual security, you will need to come up with your own private
84 key and cert.
85
86 Fossil wants to read certs and public keys in the
87 [PEM format](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail).
@@ -98,66 +129,142 @@
98 *base-64 encoding of the certificate*
99 -----END CERTIFICATE-----
100
101 In both formats, text outside of the delimiters is ignored. That means
102 that if you have a PEM-formatted private key and a separate PEM-formatted
103 certificate, you can concatenate the two into a single file and the
104 individual components will still be easily accessible.
105
106 If you have a single file that holds both your private key and your
 
 
107 cert, you can hand it off to the "[fossil server](/help?cmd=server)"
108 command using the --cert option. Like this:
109
110 fossil server --port 443 --cert mycert.pem /home/www/myproject.fossil
111
112 The command above is sufficient to run a fully-encrypted web site for
113 the "myproject.fossil" Fossil repository. This command must be run as
114 root, since it wants to listen on TCP port 443, and only root processes are
115 allowed to do that. This is safe, however, since before reading any
116 information off of the wire, Fossil will put itself inside a chroot jail
117 at /home/www and drop all root privileges.
118
119 ### Keeping The Cert And Private Key In Separate Files
120
121 If you do not want to combine your cert and private key into a single
122 big PEM file, you can keep them separate using the --pkey option to
123 Fossil.
 
 
 
124
125 fossil server --port 443 --cert fullchain.pem --pkey privkey.pem /home/www/myproject.fossil
126
127 ## The ACME Protocol
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
129 The [ACME Protocol][2] is used to prove to a CA that you control a
130 website. CAs require proof that you control a domain before they
131 will issue a cert for that domain. The usual means of dealing
132 with ACME is to run the separate [certbot](https://certbot.eff.org) tool.
133 Here is, in a nutshell, what certbot will do to obtain your cert:
134
135 1. Certbot sends your "signing request" (the document that contains
136 your public key and your domain name) to the CA.
137
138 2. After receiving the signing request, the CA needs to verify that you
139 control the domain of the cert. To do this (or, one common
140 way of doing this, at least) the CA sends a secret token back to
141 certbot through a secure backchannel, and instructs certbot to make
142 that token accessible on the (unencrypted, ordinary "http:") web site
143 for the domain in a particular file under the ".well-known" subdirectory.
144
145 3. Certbot puts the token where the CA requested it, then notifies the
146 CA that it is there.
147
148 4. The CA accesses the token to confirm that you do indeed control the
149 website. It then creates the cert and sends it back to certbot.
150
151 5. Certbot stores your cert and deletes the ".well-known" token.
 
 
 
 
 
 
152
153 In order for all of this to happen, certbot needs to be able to create
154 a subdirectory named ".well-known", within a directory you specify, and
155 then populate that subdirectory with a token file of some kind. To support
156 this, the "[fossil server](/help?cmd=server)" and
157 "[fossil http](/help?cmd=http)" commands have the --acme option.
158 When the --acme option is specified and Fossil sees a URL where the path
 
159 begins with ".well-known", then instead of doing its normal processing, it
160 looks for a file with that pathname and returns it to the client. If
161 the "server" or "http" command is referencing a single Fossil repository,
162 then the ".well-known" sub-directory should be in the same directory as
163 the repository file. If the "server" or "http" command are run against
@@ -172,9 +279,11 @@
172 Then you create your public/private key pair and run certbot, giving it
173 a --webroot of /home/www. Certbot will create the sub-directory
174 named "/home/www/.well-known" and put token files there, which the CA
175 will verify. Then certbot will store your new cert in a particular file.
176
177 Once certbot has obtained your cert, then you can concatenate that
178 cert with your private key and run Fossil in SSL/TLS mode as shown above.
 
179
180 [2]: https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment
 
181
--- www/ssl-server.md
+++ www/ssl-server.md
@@ -12,76 +12,107 @@
12
13 [0]: ./ssl.wiki
14 [1]: /timeline?c=b05cb4a0e15d0712&y=ci&n=13
15
16 Beginning in [late December 2021](/timeline?c=f6263bb64195b07f&y=a&n=13),
17 Fossil servers are now able to converse directly over TLS. Commands like
18
19 * "[fossil server](/help?cmd=server)"
20 * "[fossil ui](/help?cmd=ui)", and
21 * "[fossil http](/help?cmd=http)"
22
23 may now handle the encryption natively when suitably configured, without
24 requiring a third-party proxy layer.
25
26 ## <a id="usage"></a>Usage
 
 
 
 
27
28 To put any of the Fossil server commands into SSL/TLS mode, simply
29 add the "`--cert`" command-line option:
30
31 fossil ui --cert unsafe-builtin
32
33 Here, we are passing the magic name "unsafe-builtin" to cause Fossil to
34 use a [hard-coded self-signed cert][hcssc] rather than one obtained from
35 a recognized [Certificate Authority][CA], or "CA".
36
37 As the name implies, this self-signed cert is _not secure_ and should
38 only be used for testing. Your web browser is likely to complain
39 bitterly about it and will refuse to display the pages using the
40 "unsafe-builtin" cert until you placate it. The complexity of the
41 ceremony demanded depends on how paranoid your browser’s creators have
42 decided to be. It may require as little as clicking a single big "I know
43 the risks" type of button, or it may require a sequence be several
44 clicks designed to discourage the “yes, yes, just let me do the thing”
45 crowd lest they run themselves into trouble by disregarding well-meant
46 warnings.
47
48 Our purpose here is to show you an alternate path that will avoid the
49 issue entirely, not weigh in on which browser handles self-signed
50 certificates best.
51
52 [CA]: https://en.wikipedia.org/wiki/Certificate_authority
53 [hcssc]: /info/c2a7b14c3f541edb96?ln=89-116
54
55 ## <a id="about"></a>About Certs
56
57 The X.509 certificate system used by browsers to secure TLS connections
58 is based on asymmetric public-key cryptography. The methods for
59 obtaining one vary widely, with a resulting tradeoff we may summarize as
60 trustworthiness versus convenience, the latter characteristic falling as
61 the former rises.(^No strict correlation exists. CAs have invented
62 highly inconvenient certification schemes that offer little additional
63 real-world trustworthiness. Extreme cases along this axis may be fairly
64 characterized as [security theater][st]. We focus in this document on
65 well-balanced trade-offs between decreasing convenience and useful
66 levels of trustworthiness gained thereby.)
67
68 The self-signed method demonstrated above offers approximately zero
69 trustworthiness, though not zero _value_ since it does still provide
70 connection encryption.
71
72 More trustworthy methods are necessarily less convenient. One such is to
73 send your public key and the name of the domain you want to protect to a
74 recognized CA, which then performs one or more tests to convince itself
75 that the requester is in control of that domain. If the CA’s tests all
76 pass, it produces an X.509 certificate bound to that domain, which
77 includes assorted other information under the CA’s digital signature
78 attesting to the validity of the document’s contents. The result is sent
79 back to the requester, which may then use it to transitively attest to
80 these tests’ success: presuming one cannot fake the type of signature
81 used, the document must have been signed by the trusted, recognized CA.
82
83 There is one element of the assorted information included with a
84 certificate that is neither supplied by the requester nor rubber-stamped
85 on it in passing by the CA. It also generates a one-time key pair and
86 stores the public half in the certificate. The cryptosystem this keypair
87 is intended to work with varies both by the CA and by time, as older
88 systems become obsolete. Details aside, the CA then puts this matching
89 private half of the key in a separate file, often encrypted under a
90 separate cryptosystem for security.
91
92 SSL/TLS servers need both resulting halves to make these attestations,
93 but they send only the public half to the client when establishing the
94 connection. The client then makes its own checks to determine whether it
95 trusts the attestations being made.
96
97 A properly written and administered server never releases the private
98 key to anyone. Ideally, it goes directly from the CA to the requesting
99 server and never moves from there; then when it expires, the server
100 deletes it permanently.
101
102 [st]: https://en.wikipedia.org/wiki/Security_theater
103
104 ## <a id="startup"></a>How To Tell Fossil About Your Cert And Private Key
105
106 As we saw [above](#usage),
107 if you do not have your own cert and private key, you can ask Fossil
108 to use "unsafe-builtin", which is a self-signed cert that is built into
109 Fossil. This is wildly insecure, since the private key is not really private;
110 it is [in plain sight][hcssc] in the Fossil
111 source tree for anybody to read. <b>Never add the private key that is
112 built into Fossil to your OS's trust store</b> as doing so will severely
113 compromise your computer.[^ssattack] This built-in cert is only useful for testing.
114 If you want actual security, you will need to come up with your own private
115 key and cert.
116
117 Fossil wants to read certs and public keys in the
118 [PEM format](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail).
@@ -98,66 +129,142 @@
129 *base-64 encoding of the certificate*
130 -----END CERTIFICATE-----
131
132 In both formats, text outside of the delimiters is ignored. That means
133 that if you have a PEM-formatted private key and a separate PEM-formatted
134 certificate, you can concatenate the two into a single file, and the
135 individual components will still be easily accessible.
136
137 ### <a id="cat"></a>Separate or Concatenated?
138
139 Given a single concatenated file that holds both your private key and your
140 cert, you can hand it off to the "[fossil server](/help?cmd=server)"
141 command using the `--cert` option, like this:
142
143 fossil server --port 443 --cert mycert.pem /home/www/myproject.fossil
144
145 The command above is sufficient to run a fully-encrypted web site for
146 the "myproject.fossil" Fossil repository. This command must be run as
147 root, since it wants to listen on TCP port 443, and only root processes are
148 allowed to do that. This is safe, however, since before reading any
149 information off of the wire, Fossil will [put itself inside a chroot
150 jail](./chroot.md) at `/home/www` and drop all root privileges.
151
152 This method of combining your cert and private key into a single big PEM
153 file carries risks, one of which is that the system administrator must
154 make both halves readable by the user running the Fossil server. Given
155 the chroot jail feature, a more secure scheme separates the halves so
156 that only root can read the private half, which then means that when
157 Fossil drops its root privileges, it becomes unable to access the
158 private key on disk. Fossil’s `server` feature includes the `--pkey`
159 option to allow for that use case:
160
161 fossil server --port 443 --cert fullchain.pem --pkey privkey.pem /home/www/myproject.fossil
162
163 [^ssattack]: ^How, you ask? Because the keys are known, they can be used
164 to provide signed certificates for **any** other domain. One foolish
165 enough to tell their OS’s TLS mechanisms to trust the signing
166 certificate is implicitly handing over all TLS encryption controls
167 to any attacker that knows they did this. Don’t do it.
168
169 ### <a id="chain"></a>Chains and Links
170
171 The file name “`fullchain.pem`” used above is a reference to a term of
172 art within this world of TLS protocols and their associated X.509
173 certificates. Within the simplistic scheme originally envisioned by the
174 creators of SSL — the predecessor to TLS — we were all expected to agree
175 on a single set of CA root authorities, and we would all agree to get
176 our certificates from one of them. The real world is more complicated:
177
178 * The closest we have to universal acceptance of CAs is via the
179 [CA/Browser Forum][CAB], and even within its select membership there
180 is continual argument over which roots are trustworthy. (Hashing
181 that out is arguably this group’s key purpose.)
182
183 * CAB’s decision regarding trustworthiness may not match that of any
184 given system’s administrator. There are solid, defensible reasons to
185 prune back the stock CA root set included with your browser, then to
186 augment it with ones CAB _doesn’t_ trust.
187
188 * TLS isn’t limited to use between web browsers and public Internet
189 sites. Several common use cases preclude use of the process CAB
190 envisions, with servers able to contact Internet-based CA roots as
191 part of proving their identity. Different use cases demand different
192 CA root authority stores.
193
194 The most common of these divergent cases are servers behind strict
195 firewalls and edge devices that never interact with the public
196 Internet. This class ranges from cheap home IoT devices to the
197 internal equipment managed by IT for a massive global corporation.
198
199 Your private Fossil server is liable to fall into that last category.
200 This may then require that you generate a more complicated “chain” of
201 certificates for Fossil to use here, without which the client may not be
202 able to get back to a CA root it trusts. This is true regardless of
203 whether that client is another copy of Fossil or a web browser
204 traversing Fossil’s web UI, though that fact complicates matters by
205 allowing for multiple classes of client, each of which may have their
206 own rules for modifying the stock certificate scheme.
207
208 This is distressingly common, in fact: Fossil links to OpenSSL to
209 provide its TLS support, but there is a good chance that your browser
210 uses another TLS implementation entirely. They may or may not agree on a
211 single CA root store.
212
213 How you accommodate all this complexity varies by the CA and other
214 details. As but one example, Firefox’s “View Certificate” feature offers
215 _two_ ways to download a given web site’s certificate: the cert alone or
216 the “chain” leading back to the root. Depending on the use case, the
217 standalone certificate might suffice, or you might need some type of
218 cert chain. Complicating this is that the last link in the chain may be
219 left off when it is for a mutually trusted CA root, implicitly
220 completing the chain.
221
222 [CAB]: https://en.wikipedia.org/wiki/CA/Browser_Forum
223
224 ## <a id="acme"></a>The ACME Protocol
225
226 The [ACME Protocol][2] simplifies all this by automating the process of
227 proving to a recognized public CA that you are in control of a given
228 website. Without this proof, no valid CA will issue a cert for that
229 domain, as that allows fraudulent impersonation.
230
231 The primary implementation of ACME is [certbot], a product of the Let’s
232 Encrypt organization.
 
 
233 Here is, in a nutshell, what certbot will do to obtain your cert:
234
235 1. It sends your "signing request" (the document that contains
236 your public key and your domain name) to the CA.
237
238 2. After receiving the signing request, the CA needs to verify that
239 you control the domain of the cert. One of several methods certbot has
240 for accomplishing this is to create a secret token and place it at
241 a well-known location, then tell the CA about it over ACME.
242
243 3. The CA then tries pulling that token, which if successful proves
244 that the requester is able to create arbitrary data on the server,
245 implicitly proving control over that server. This must be done
246 over the unencrypted HTTP protocol since TLS isn’t working yet.
247
248 4. If satisfied by this proof of control, the CA then creates the
249 keypair described above and bakes the public half into the
250 certificate it signs. It then sends this and the private half of
251 the key back to certbot.
252
253 5. Certbot stores these halves separately for the reasons sketched
254 out above.
255
256 6. It then deletes the secret one-time-use token it used to prove
257 domain control. ACME’s design precludes replay attacks.
258
259 In order for all of this to happen, certbot needs to be able to create
260 a subdirectory named ".well-known", within a directory you specify,
261 then populate that subdirectory with a token file of some kind. To support
262 this, the "[fossil server](/help?cmd=server)" and
263 "[fossil http](/help?cmd=http)" commands have the --acme option.
264
265 When specified, Fossil sees a URL where the path
266 begins with ".well-known", then instead of doing its normal processing, it
267 looks for a file with that pathname and returns it to the client. If
268 the "server" or "http" command is referencing a single Fossil repository,
269 then the ".well-known" sub-directory should be in the same directory as
270 the repository file. If the "server" or "http" command are run against
@@ -172,9 +279,11 @@
279 Then you create your public/private key pair and run certbot, giving it
280 a --webroot of /home/www. Certbot will create the sub-directory
281 named "/home/www/.well-known" and put token files there, which the CA
282 will verify. Then certbot will store your new cert in a particular file.
283
284 Once certbot has obtained your cert, you may either pass the two halves
285 to Fossil separately using the `--pkey` and `--cert` options described
286 above, or you may concatenate them and pass that via `--cert` alone.
287
288 [2]: https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment
289 [certbot]: https://certbot.eff.org
290
--- www/sync.wiki
+++ www/sync.wiki
@@ -789,10 +789,18 @@
789789
a successful commit. This instructs the server to release
790790
any lock on any check-in previously held by that client.
791791
The ci-unlock pragma helps to avoid false-positive lock warnings
792792
that might arise if a check-in is aborted and then restarted
793793
on a branch.
794
+
795
+<li><b>req-clusters</b> A client sends the "req-clusters" pragma
796
+to the server to ask the server to reply with "igot" cards for
797
+every [./fileformat.wiki#cluster|cluster artifact] that it holds. The
798
+client typically does this when it thinks that it might be attempting
799
+to pull a long chain of cluster artifacts. Sending the artifacts
800
+all at once can dramatically reduce the number of round trip
801
+messages needed to complete the synchronization.
794802
</ol>
795803
796804
<h3 id="comment">3.12 Comment Cards</h3>
797805
798806
Any card that begins with "#" (ASCII 0x23) is a comment card and
799807
--- www/sync.wiki
+++ www/sync.wiki
@@ -789,10 +789,18 @@
789 a successful commit. This instructs the server to release
790 any lock on any check-in previously held by that client.
791 The ci-unlock pragma helps to avoid false-positive lock warnings
792 that might arise if a check-in is aborted and then restarted
793 on a branch.
 
 
 
 
 
 
 
 
794 </ol>
795
796 <h3 id="comment">3.12 Comment Cards</h3>
797
798 Any card that begins with "#" (ASCII 0x23) is a comment card and
799
--- www/sync.wiki
+++ www/sync.wiki
@@ -789,10 +789,18 @@
789 a successful commit. This instructs the server to release
790 any lock on any check-in previously held by that client.
791 The ci-unlock pragma helps to avoid false-positive lock warnings
792 that might arise if a check-in is aborted and then restarted
793 on a branch.
794
795 <li><b>req-clusters</b> A client sends the "req-clusters" pragma
796 to the server to ask the server to reply with "igot" cards for
797 every [./fileformat.wiki#cluster|cluster artifact] that it holds. The
798 client typically does this when it thinks that it might be attempting
799 to pull a long chain of cluster artifacts. Sending the artifacts
800 all at once can dramatically reduce the number of round trip
801 messages needed to complete the synchronization.
802 </ol>
803
804 <h3 id="comment">3.12 Comment Cards</h3>
805
806 Any card that begins with "#" (ASCII 0x23) is a comment card and
807

Keyboard Shortcuts

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