Fossil SCM
Merge from trunk.
Commit
331e5138804213edd8a41a17011adac4a47c52a52828a20f33d10130de313061
Parent
a71499a27f57540…
36 files changed
+6
-6
+191
-178
+1
-1
+26
-23
+3
-4
+43
-1
+49
-39
+8
-1
+18
-2
+28
-12
+149
-12
+2
-1
+12
+386
+119
-15
+8
-14
+14
-1
+1
-1
+2
+241
-369
+6
-7
+55
-2
+25
+28
-25
+6
-4
+1
+1
-1
+1
+16
-5
+10
-4
+12
+10
+28
+197
-88
+8
~
Dockerfile
~
extsrc/sqlite3.c
~
extsrc/sqlite3.h
~
src/bisect.c
~
src/checkout.c
~
src/db.c
~
src/descendants.c
~
src/doc.c
~
src/encode.c
~
src/forum.c
~
src/graph.c
~
src/info.c
~
src/main.c
~
src/main.mk
~
src/match.c
~
src/name.c
~
src/patch.c
~
src/printf.c
~
src/sitemap.c
~
src/style.c
~
src/timeline.c
~
src/update.c
~
src/xfer.c
~
test/merge1.test
~
test/merge3.test
~
test/merge4.test
~
test/tester.tcl
~
test/update.test
~
tools/makemake.tcl
~
tools/translate.c
~
win/Makefile.dmc
~
win/Makefile.mingw
~
win/Makefile.msc
~
www/changes.wiki
~
www/ssl-server.md
~
www/sync.wiki
+6
-6
| --- Dockerfile | ||
| +++ Dockerfile | ||
| @@ -3,19 +3,19 @@ | ||
| 3 | 3 | |
| 4 | 4 | ## --------------------------------------------------------------------- |
| 5 | 5 | ## STAGE 1: Build static Fossil binary |
| 6 | 6 | ## --------------------------------------------------------------------- |
| 7 | 7 | |
| 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 | |
| 9 | 9 | ### to build with the latest tools and libraries available in case they |
| 10 | 10 | ### fixed something that matters to us since the last build. Everything |
| 11 | 11 | ### below depends on this layer, and so, alas, we toss this container's |
| 12 | 12 | ### cache on Alpine's release schedule, roughly once a month. |
| 13 | 13 | FROM alpine:latest AS bld |
| 14 | 14 | WORKDIR /fsl |
| 15 | 15 | |
| 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 | |
| 17 | 17 | ### when the upstream image is updated or we change the package set. |
| 18 | 18 | RUN set -x \ |
| 19 | 19 | && apk update \ |
| 20 | 20 | && apk upgrade --no-cache \ |
| 21 | 21 | && apk add --no-cache \ |
| @@ -23,19 +23,19 @@ | ||
| 23 | 23 | linux-headers musl-dev \ |
| 24 | 24 | openssl-dev openssl-libs-static \ |
| 25 | 25 | zlib-dev zlib-static |
| 26 | 26 | |
| 27 | 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. | |
| 28 | +### userland for each iteration of Fossil's dev cycle. | |
| 29 | 29 | ### |
| 30 | 30 | ### We must cope with a bizarre ADD misfeature here: it unpacks tarballs |
| 31 | 31 | ### automatically when you give it a local file name but not if you give |
| 32 | 32 | ### it a /tarball URL! It matters because we default to a URL in case |
| 33 | 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. | |
| 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 | 37 | ARG FSLCFG="" |
| 38 | 38 | ARG FSLVER="trunk" |
| 39 | 39 | ARG FSLURL="https://fossil-scm.org/home/tarball/src?r=${FSLVER}" |
| 40 | 40 | ENV FSLSTB=/fsl/src.tar.gz |
| 41 | 41 | ADD $FSLURL $FSLSTB |
| 42 | 42 |
| --- 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 @@ | ||
| 16 | 16 | ** if you want a wrapper to interface SQLite with your choice of programming |
| 17 | 17 | ** language. The code for the "sqlite3" command-line shell is also in a |
| 18 | 18 | ** separate file. This file contains only code for the core SQLite library. |
| 19 | 19 | ** |
| 20 | 20 | ** The content in this amalgamation comes from Fossil check-in |
| 21 | -** e2bae4143afd07de1ae55a6d2606a3b541a5 with changes in files: | |
| 21 | +** e6c30ee52c5cdc193804cec63374d558b45e with changes in files: | |
| 22 | 22 | ** |
| 23 | 23 | ** |
| 24 | 24 | */ |
| 25 | 25 | #ifndef SQLITE_AMALGAMATION |
| 26 | 26 | #define SQLITE_CORE 1 |
| @@ -465,11 +465,11 @@ | ||
| 465 | 465 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 466 | 466 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 467 | 467 | */ |
| 468 | 468 | #define SQLITE_VERSION "3.48.0" |
| 469 | 469 | #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" | |
| 471 | 471 | |
| 472 | 472 | /* |
| 473 | 473 | ** CAPI3REF: Run-Time Library Version Numbers |
| 474 | 474 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 475 | 475 | ** |
| @@ -14046,13 +14046,17 @@ | ||
| 14046 | 14046 | # define SQLITE_MAX_VDBE_OP 250000000 |
| 14047 | 14047 | #endif |
| 14048 | 14048 | |
| 14049 | 14049 | /* |
| 14050 | 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. | |
| 14051 | 14055 | */ |
| 14052 | 14056 | #ifndef SQLITE_MAX_FUNCTION_ARG |
| 14053 | -# define SQLITE_MAX_FUNCTION_ARG 127 | |
| 14057 | +# define SQLITE_MAX_FUNCTION_ARG 1000 | |
| 14054 | 14058 | #endif |
| 14055 | 14059 | |
| 14056 | 14060 | /* |
| 14057 | 14061 | ** The suggested maximum number of in-memory pages to use for |
| 14058 | 14062 | ** the main database table and for temporary tables. |
| @@ -16049,10 +16053,26 @@ | ||
| 16049 | 16053 | #define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */ |
| 16050 | 16054 | #define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */ |
| 16051 | 16055 | #define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */ |
| 16052 | 16056 | #define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */ |
| 16053 | 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) | |
| 16054 | 16074 | |
| 16055 | 16075 | /* |
| 16056 | 16076 | ** Flags that make up the mask passed to sqlite3PagerGet(). |
| 16057 | 16077 | */ |
| 16058 | 16078 | #define PAGER_GET_NOCONTENT 0x01 /* Do not load data from disk */ |
| @@ -18113,11 +18133,11 @@ | ||
| 18113 | 18133 | ** |
| 18114 | 18134 | ** The u.pHash field is used by the global built-ins. The u.pDestructor |
| 18115 | 18135 | ** field is used by per-connection app-def functions. |
| 18116 | 18136 | */ |
| 18117 | 18137 | struct FuncDef { |
| 18118 | - i8 nArg; /* Number of arguments. -1 means unlimited */ | |
| 18138 | + i16 nArg; /* Number of arguments. -1 means unlimited */ | |
| 18119 | 18139 | u32 funcFlags; /* Some combination of SQLITE_FUNC_* */ |
| 18120 | 18140 | void *pUserData; /* User data parameter */ |
| 18121 | 18141 | FuncDef *pNext; /* Next function with same name */ |
| 18122 | 18142 | void (*xSFunc)(sqlite3_context*,int,sqlite3_value**); /* func or agg-step */ |
| 18123 | 18143 | void (*xFinalize)(sqlite3_context*); /* Agg finalizer */ |
| @@ -23708,11 +23728,11 @@ | ||
| 23708 | 23728 | Vdbe *pVdbe; /* The VM that owns this context */ |
| 23709 | 23729 | int iOp; /* Instruction number of OP_Function */ |
| 23710 | 23730 | int isError; /* Error code returned by the function. */ |
| 23711 | 23731 | u8 enc; /* Encoding to use for results */ |
| 23712 | 23732 | u8 skipFlag; /* Skip accumulator loading if true */ |
| 23713 | - u8 argc; /* Number of arguments */ | |
| 23733 | + u16 argc; /* Number of arguments */ | |
| 23714 | 23734 | sqlite3_value *argv[1]; /* Argument set */ |
| 23715 | 23735 | }; |
| 23716 | 23736 | |
| 23717 | 23737 | /* A bitfield type for use inside of structures. Always follow with :N where |
| 23718 | 23738 | ** N is the number of bits. |
| @@ -58014,24 +58034,10 @@ | ||
| 58014 | 58034 | # define USEFETCH(x) ((x)->bUseFetch) |
| 58015 | 58035 | #else |
| 58016 | 58036 | # define USEFETCH(x) 0 |
| 58017 | 58037 | #endif |
| 58018 | 58038 | |
| 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 | 58039 | #ifdef SQLITE_DIRECT_OVERFLOW_READ |
| 58034 | 58040 | /* |
| 58035 | 58041 | ** Return true if page pgno can be read directly from the database file |
| 58036 | 58042 | ** by the b-tree layer. This is the case if: |
| 58037 | 58043 | ** |
| @@ -59313,11 +59319,11 @@ | ||
| 59313 | 59319 | rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags); |
| 59314 | 59320 | } |
| 59315 | 59321 | } |
| 59316 | 59322 | pPager->journalOff = 0; |
| 59317 | 59323 | }else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST |
| 59318 | - || (pPager->exclusiveMode && pPager->journalMode!=PAGER_JOURNALMODE_WAL) | |
| 59324 | + || (pPager->exclusiveMode && pPager->journalMode<PAGER_JOURNALMODE_WAL) | |
| 59319 | 59325 | ){ |
| 59320 | 59326 | rc = zeroJournalHdr(pPager, hasSuper||pPager->tempFile); |
| 59321 | 59327 | pPager->journalOff = 0; |
| 59322 | 59328 | }else{ |
| 59323 | 59329 | /* This branch may be executed with Pager.journalMode==MEMORY if |
| @@ -68023,15 +68029,11 @@ | ||
| 68023 | 68029 | ** so it takes care to hold an exclusive lock on the corresponding |
| 68024 | 68030 | ** WAL_READ_LOCK() while changing values. |
| 68025 | 68031 | */ |
| 68026 | 68032 | static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){ |
| 68027 | 68033 | 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 | 68034 | int rc = SQLITE_OK; /* Return code */ |
| 68032 | - u32 mxFrame; /* Wal frame to lock to */ | |
| 68033 | 68035 | #ifdef SQLITE_ENABLE_SETLK_TIMEOUT |
| 68034 | 68036 | int nBlockTmout = 0; |
| 68035 | 68037 | #endif |
| 68036 | 68038 | |
| 68037 | 68039 | assert( pWal->readLock<0 ); /* Not currently locked */ |
| @@ -68133,145 +68135,151 @@ | ||
| 68133 | 68135 | |
| 68134 | 68136 | assert( pWal->nWiData>0 ); |
| 68135 | 68137 | assert( pWal->apWiData[0]!=0 ); |
| 68136 | 68138 | pInfo = walCkptInfo(pWal); |
| 68137 | 68139 | 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 | + } | |
| 68273 | 68281 | } |
| 68274 | 68282 | return rc; |
| 68275 | 68283 | } |
| 68276 | 68284 | |
| 68277 | 68285 | #ifdef SQLITE_ENABLE_SNAPSHOT |
| @@ -91889,11 +91897,11 @@ | ||
| 91889 | 91897 | ** |
| 91890 | 91898 | ** sqlite3_column_int() |
| 91891 | 91899 | ** sqlite3_column_int64() |
| 91892 | 91900 | ** sqlite3_column_text() |
| 91893 | 91901 | ** sqlite3_column_text16() |
| 91894 | -** sqlite3_column_real() | |
| 91902 | +** sqlite3_column_double() | |
| 91895 | 91903 | ** sqlite3_column_bytes() |
| 91896 | 91904 | ** sqlite3_column_bytes16() |
| 91897 | 91905 | ** sqlite3_column_blob() |
| 91898 | 91906 | */ |
| 91899 | 91907 | static void columnMallocFailure(sqlite3_stmt *pStmt) |
| @@ -109896,11 +109904,11 @@ | ||
| 109896 | 109904 | p4 = sqlite3BinaryCompareCollSeq(pParse, pLeft, pRight); |
| 109897 | 109905 | } |
| 109898 | 109906 | p5 = binaryCompareP5(pLeft, pRight, jumpIfNull); |
| 109899 | 109907 | addr = sqlite3VdbeAddOp4(pParse->pVdbe, opcode, in2, dest, in1, |
| 109900 | 109908 | (void*)p4, P4_COLLSEQ); |
| 109901 | - sqlite3VdbeChangeP5(pParse->pVdbe, (u8)p5); | |
| 109909 | + sqlite3VdbeChangeP5(pParse->pVdbe, (u16)p5); | |
| 109902 | 109910 | return addr; |
| 109903 | 109911 | } |
| 109904 | 109912 | |
| 109905 | 109913 | /* |
| 109906 | 109914 | ** Return true if expression pExpr is a vector, or false otherwise. |
| @@ -129732,11 +129740,11 @@ | ||
| 129732 | 129740 | || (argc==3 && sqlite3_value_type(argv[2])==SQLITE_NULL) |
| 129733 | 129741 | ){ |
| 129734 | 129742 | return; |
| 129735 | 129743 | } |
| 129736 | 129744 | p0type = sqlite3_value_type(argv[0]); |
| 129737 | - p1 = sqlite3_value_int(argv[1]); | |
| 129745 | + p1 = sqlite3_value_int64(argv[1]); | |
| 129738 | 129746 | if( p0type==SQLITE_BLOB ){ |
| 129739 | 129747 | len = sqlite3_value_bytes(argv[0]); |
| 129740 | 129748 | z = sqlite3_value_blob(argv[0]); |
| 129741 | 129749 | if( z==0 ) return; |
| 129742 | 129750 | assert( len==sqlite3_value_bytes(argv[0]) ); |
| @@ -129757,11 +129765,11 @@ | ||
| 129757 | 129765 | ** from 2009-02-02 for compatibility of applications that exploited the |
| 129758 | 129766 | ** old buggy behavior. */ |
| 129759 | 129767 | if( p1==0 ) p1 = 1; /* <rdar://problem/6778339> */ |
| 129760 | 129768 | #endif |
| 129761 | 129769 | if( argc==3 ){ |
| 129762 | - p2 = sqlite3_value_int(argv[2]); | |
| 129770 | + p2 = sqlite3_value_int64(argv[2]); | |
| 129763 | 129771 | if( p2<0 ){ |
| 129764 | 129772 | p2 = -p2; |
| 129765 | 129773 | negP2 = 1; |
| 129766 | 129774 | } |
| 129767 | 129775 | }else{ |
| @@ -129796,13 +129804,15 @@ | ||
| 129796 | 129804 | SQLITE_SKIP_UTF8(z2); |
| 129797 | 129805 | } |
| 129798 | 129806 | sqlite3_result_text64(context, (char*)z, z2-z, SQLITE_TRANSIENT, |
| 129799 | 129807 | SQLITE_UTF8); |
| 129800 | 129808 | }else{ |
| 129801 | - if( p1+p2>len ){ | |
| 129809 | + if( p1>=len ){ | |
| 129810 | + p1 = p2 = 0; | |
| 129811 | + }else if( p2>len-p1 ){ | |
| 129802 | 129812 | p2 = len-p1; |
| 129803 | - if( p2<0 ) p2 = 0; | |
| 129813 | + assert( p2>0 ); | |
| 129804 | 129814 | } |
| 129805 | 129815 | sqlite3_result_blob64(context, (char*)&z[p1], (u64)p2, SQLITE_TRANSIENT); |
| 129806 | 129816 | } |
| 129807 | 129817 | } |
| 129808 | 129818 | |
| @@ -129809,17 +129819,17 @@ | ||
| 129809 | 129819 | /* |
| 129810 | 129820 | ** Implementation of the round() function |
| 129811 | 129821 | */ |
| 129812 | 129822 | #ifndef SQLITE_OMIT_FLOATING_POINT |
| 129813 | 129823 | static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ |
| 129814 | - int n = 0; | |
| 129824 | + i64 n = 0; | |
| 129815 | 129825 | double r; |
| 129816 | 129826 | char *zBuf; |
| 129817 | 129827 | assert( argc==1 || argc==2 ); |
| 129818 | 129828 | if( argc==2 ){ |
| 129819 | 129829 | if( SQLITE_NULL==sqlite3_value_type(argv[1]) ) return; |
| 129820 | - n = sqlite3_value_int(argv[1]); | |
| 129830 | + n = sqlite3_value_int64(argv[1]); | |
| 129821 | 129831 | if( n>30 ) n = 30; |
| 129822 | 129832 | if( n<0 ) n = 0; |
| 129823 | 129833 | } |
| 129824 | 129834 | if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return; |
| 129825 | 129835 | r = sqlite3_value_double(argv[0]); |
| @@ -141316,11 +141326,11 @@ | ||
| 141316 | 141326 | sqlite3VdbeAddOp3(v, OP_Null, 0, 8, 8+cnt); |
| 141317 | 141327 | sqlite3ClearTempRegCache(pParse); |
| 141318 | 141328 | |
| 141319 | 141329 | /* Do the b-tree integrity checks */ |
| 141320 | 141330 | sqlite3VdbeAddOp4(v, OP_IntegrityCk, 1, cnt, 8, (char*)aRoot,P4_INTARRAY); |
| 141321 | - sqlite3VdbeChangeP5(v, (u8)i); | |
| 141331 | + sqlite3VdbeChangeP5(v, (u16)i); | |
| 141322 | 141332 | addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v); |
| 141323 | 141333 | sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, |
| 141324 | 141334 | sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zDbSName), |
| 141325 | 141335 | P4_DYNAMIC); |
| 141326 | 141336 | sqlite3VdbeAddOp3(v, OP_Concat, 2, 3, 3); |
| @@ -150549,11 +150559,11 @@ | ||
| 150549 | 150559 | } |
| 150550 | 150560 | sqlite3ReleaseTempReg(pParse, regSubtype); |
| 150551 | 150561 | } |
| 150552 | 150562 | sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i)); |
| 150553 | 150563 | sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); |
| 150554 | - sqlite3VdbeChangeP5(v, (u8)nArg); | |
| 150564 | + sqlite3VdbeChangeP5(v, (u16)nArg); | |
| 150555 | 150565 | sqlite3VdbeAddOp2(v, OP_Next, pF->iOBTab, iTop+1); VdbeCoverage(v); |
| 150556 | 150566 | sqlite3VdbeJumpHere(v, iTop); |
| 150557 | 150567 | sqlite3ReleaseTempRange(pParse, regAgg, nArg); |
| 150558 | 150568 | } |
| 150559 | 150569 | sqlite3VdbeAddOp2(v, OP_AggFinal, AggInfoFuncReg(pAggInfo,i), |
| @@ -150712,11 +150722,11 @@ | ||
| 150712 | 150722 | sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0, |
| 150713 | 150723 | (char *)pColl, P4_COLLSEQ); |
| 150714 | 150724 | } |
| 150715 | 150725 | sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i)); |
| 150716 | 150726 | sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF); |
| 150717 | - sqlite3VdbeChangeP5(v, (u8)nArg); | |
| 150727 | + sqlite3VdbeChangeP5(v, (u16)nArg); | |
| 150718 | 150728 | sqlite3ReleaseTempRange(pParse, regAgg, nArg); |
| 150719 | 150729 | } |
| 150720 | 150730 | if( addrNext ){ |
| 150721 | 150731 | sqlite3VdbeResolveLabel(v, addrNext); |
| 150722 | 150732 | } |
| @@ -154106,11 +154116,11 @@ | ||
| 154106 | 154116 | /* Set the P5 operand of the OP_Program instruction to non-zero if |
| 154107 | 154117 | ** recursive invocation of this trigger program is disallowed. Recursive |
| 154108 | 154118 | ** invocation is disallowed if (a) the sub-program is really a trigger, |
| 154109 | 154119 | ** not a foreign key action, and (b) the flag to enable recursive triggers |
| 154110 | 154120 | ** is clear. */ |
| 154111 | - sqlite3VdbeChangeP5(v, (u8)bRecursive); | |
| 154121 | + sqlite3VdbeChangeP5(v, (u16)bRecursive); | |
| 154112 | 154122 | } |
| 154113 | 154123 | } |
| 154114 | 154124 | |
| 154115 | 154125 | /* |
| 154116 | 154126 | ** This is called to code the required FOR EACH ROW triggers for an operation |
| @@ -170414,10 +170424,11 @@ | ||
| 170414 | 170424 | int pc, |
| 170415 | 170425 | VdbeOp *pOp |
| 170416 | 170426 | ){ |
| 170417 | 170427 | if( (db->flags & SQLITE_VdbeAddopTrace)==0 ) return; |
| 170418 | 170428 | sqlite3VdbePrintOp(0, pc, pOp); |
| 170429 | + sqlite3ShowWhereTerm(0); /* So compiler won't complain about unused func */ | |
| 170419 | 170430 | } |
| 170420 | 170431 | #endif |
| 170421 | 170432 | |
| 170422 | 170433 | /* |
| 170423 | 170434 | ** Generate the end of the WHERE loop. See comments on |
| @@ -172523,11 +172534,11 @@ | ||
| 172523 | 172534 | sqlite3VdbeAddOp4(v, OP_CollSeq, 0,0,0, (const char*)pColl, P4_COLLSEQ); |
| 172524 | 172535 | } |
| 172525 | 172536 | sqlite3VdbeAddOp3(v, bInverse? OP_AggInverse : OP_AggStep, |
| 172526 | 172537 | bInverse, regArg, pWin->regAccum); |
| 172527 | 172538 | sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF); |
| 172528 | - sqlite3VdbeChangeP5(v, (u8)nArg); | |
| 172539 | + sqlite3VdbeChangeP5(v, (u16)nArg); | |
| 172529 | 172540 | if( pWin->bExprArgs ){ |
| 172530 | 172541 | sqlite3ReleaseTempRange(pParse, regArg, nArg); |
| 172531 | 172542 | } |
| 172532 | 172543 | } |
| 172533 | 172544 | |
| @@ -184078,12 +184089,12 @@ | ||
| 184078 | 184089 | # error SQLITE_MAX_COMPOUND_SELECT must be at least 2 |
| 184079 | 184090 | #endif |
| 184080 | 184091 | #if SQLITE_MAX_VDBE_OP<40 |
| 184081 | 184092 | # error SQLITE_MAX_VDBE_OP must be at least 40 |
| 184082 | 184093 | #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 | |
| 184085 | 184096 | #endif |
| 184086 | 184097 | #if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>125 |
| 184087 | 184098 | # error SQLITE_MAX_ATTACHED must be between 0 and 125 |
| 184088 | 184099 | #endif |
| 184089 | 184100 | #if SQLITE_MAX_LIKE_PATTERN_LENGTH<1 |
| @@ -185492,11 +185503,10 @@ | ||
| 185492 | 185503 | #if defined(SQLITE_DEBUG) |
| 185493 | 185504 | /* Invoke these debugging routines so that the compiler does not |
| 185494 | 185505 | ** issue "defined but not used" warnings. */ |
| 185495 | 185506 | if( x==9999 ){ |
| 185496 | 185507 | sqlite3ShowExpr(0); |
| 185497 | - sqlite3ShowExpr(0); | |
| 185498 | 185508 | sqlite3ShowExprList(0); |
| 185499 | 185509 | sqlite3ShowIdList(0); |
| 185500 | 185510 | sqlite3ShowSrcList(0); |
| 185501 | 185511 | sqlite3ShowWith(0); |
| 185502 | 185512 | sqlite3ShowUpsert(0); |
| @@ -185509,11 +185519,10 @@ | ||
| 185509 | 185519 | #ifndef SQLITE_OMIT_WINDOWFUNC |
| 185510 | 185520 | sqlite3ShowWindow(0); |
| 185511 | 185521 | sqlite3ShowWinFunc(0); |
| 185512 | 185522 | #endif |
| 185513 | 185523 | sqlite3ShowSelect(0); |
| 185514 | - sqlite3ShowWhereTerm(0); | |
| 185515 | 185524 | } |
| 185516 | 185525 | #endif |
| 185517 | 185526 | break; |
| 185518 | 185527 | } |
| 185519 | 185528 | |
| @@ -226344,11 +226353,15 @@ | ||
| 226344 | 226353 | static int dbpageSync(sqlite3_vtab *pVtab){ |
| 226345 | 226354 | DbpageTable *pTab = (DbpageTable *)pVtab; |
| 226346 | 226355 | if( pTab->pgnoTrunc>0 ){ |
| 226347 | 226356 | Btree *pBt = pTab->db->aDb[pTab->iDbTrunc].pBt; |
| 226348 | 226357 | 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); | |
| 226350 | 226363 | } |
| 226351 | 226364 | pTab->pgnoTrunc = 0; |
| 226352 | 226365 | return SQLITE_OK; |
| 226353 | 226366 | } |
| 226354 | 226367 | |
| @@ -255419,11 +255432,11 @@ | ||
| 255419 | 255432 | int nArg, /* Number of args */ |
| 255420 | 255433 | sqlite3_value **apUnused /* Function arguments */ |
| 255421 | 255434 | ){ |
| 255422 | 255435 | assert( nArg==0 ); |
| 255423 | 255436 | 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); | |
| 255425 | 255438 | } |
| 255426 | 255439 | |
| 255427 | 255440 | /* |
| 255428 | 255441 | ** Implementation of fts5_locale(LOCALE, TEXT) function. |
| 255429 | 255442 | ** |
| 255430 | 255443 |
| --- 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 |
+1
-1
| --- extsrc/sqlite3.h | ||
| +++ extsrc/sqlite3.h | ||
| @@ -146,11 +146,11 @@ | ||
| 146 | 146 | ** [sqlite3_libversion_number()], [sqlite3_sourceid()], |
| 147 | 147 | ** [sqlite_version()] and [sqlite_source_id()]. |
| 148 | 148 | */ |
| 149 | 149 | #define SQLITE_VERSION "3.48.0" |
| 150 | 150 | #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" | |
| 152 | 152 | |
| 153 | 153 | /* |
| 154 | 154 | ** CAPI3REF: Run-Time Library Version Numbers |
| 155 | 155 | ** KEYWORDS: sqlite3_version sqlite3_sourceid |
| 156 | 156 | ** |
| 157 | 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-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 @@ | ||
| 197 | 197 | static void bisect_append_skip(int rid){ |
| 198 | 198 | db_multi_exec( |
| 199 | 199 | "UPDATE vvar SET value=value||' s%d' WHERE name='bisect-log'", rid |
| 200 | 200 | ); |
| 201 | 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 | +} | |
| 202 | 218 | |
| 203 | 219 | /* |
| 204 | 220 | ** Create a TEMP table named "bilog" that contains the complete history |
| 205 | 221 | ** of the current bisect. |
| 206 | 222 | ** |
| @@ -215,14 +231,14 @@ | ||
| 215 | 231 | ** in between the inner-most GOOD and BAD nodes. |
| 216 | 232 | */ |
| 217 | 233 | int bisect_create_bilog_table(int iCurrent, const char *zDesc, int bDetail){ |
| 218 | 234 | char *zLog; |
| 219 | 235 | Blob log, id; |
| 220 | - Stmt q; | |
| 221 | 236 | int cnt = 0; |
| 222 | 237 | int lastGood = -1; |
| 223 | 238 | int lastBad = -1; |
| 239 | + Blob ins = BLOB_INITIALIZER; | |
| 224 | 240 | |
| 225 | 241 | if( zDesc!=0 ){ |
| 226 | 242 | blob_init(&log, 0, 0); |
| 227 | 243 | while( zDesc[0]=='y' || zDesc[0]=='n' || zDesc[0]=='s' ){ |
| 228 | 244 | int i; |
| @@ -253,55 +269,42 @@ | ||
| 253 | 269 | " rid INTEGER PRIMARY KEY," /* Sequence of events */ |
| 254 | 270 | " stat TEXT," /* Type of occurrence */ |
| 255 | 271 | " seq INTEGER UNIQUE" /* Check-in number */ |
| 256 | 272 | ");" |
| 257 | 273 | ); |
| 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"); | |
| 260 | 275 | while( blob_token(&log, &id) ){ |
| 261 | 276 | int rid; |
| 262 | - db_bind_int(&q, ":seq", ++cnt); | |
| 277 | + cnt++; | |
| 263 | 278 | if( blob_str(&id)[0]=='s' ){ |
| 264 | 279 | 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); | |
| 267 | 281 | }else{ |
| 268 | 282 | rid = atoi(blob_str(&id)); |
| 269 | 283 | 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); | |
| 272 | 285 | lastGood = rid; |
| 273 | 286 | }else{ |
| 274 | - db_bind_text(&q, ":stat", "BAD"); | |
| 275 | - db_bind_int(&q, ":rid", -rid); | |
| 287 | + bisect_log_append(&ins, cnt, "BAD", rid); | |
| 276 | 288 | lastBad = -rid; |
| 277 | 289 | } |
| 278 | 290 | } |
| 279 | - db_step(&q); | |
| 280 | - db_reset(&q); | |
| 281 | 291 | } |
| 282 | 292 | 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); | |
| 288 | 294 | } |
| 289 | 295 | if( bDetail && lastGood>0 && lastBad>0 ){ |
| 290 | 296 | PathNode *p; |
| 291 | 297 | p = path_shortest(lastGood, lastBad, bisect_option("direct-only"),0, 0); |
| 292 | 298 | 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); | |
| 298 | 300 | p = p->u.pTo; |
| 299 | 301 | } |
| 300 | 302 | path_reset(); |
| 301 | 303 | } |
| 302 | - db_finalize(&q); | |
| 304 | + db_exec_sql(blob_sql_text(&ins)); | |
| 305 | + blob_reset(&ins); | |
| 303 | 306 | return 1; |
| 304 | 307 | } |
| 305 | 308 | |
| 306 | 309 | /* Return a permalink description of a bisect. Space is obtained from |
| 307 | 310 | ** fossil_malloc() and should be freed by the caller. |
| 308 | 311 |
| --- 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 @@ | ||
| 171 | 171 | ** each character as a flag to enable writing "manifest", "manifest.uuid" or |
| 172 | 172 | ** "manifest.tags". |
| 173 | 173 | */ |
| 174 | 174 | void manifest_to_disk(int vid){ |
| 175 | 175 | char *zManFile; |
| 176 | - Blob manifest; | |
| 177 | - Blob taglist; | |
| 178 | 176 | int flg; |
| 179 | 177 | |
| 180 | 178 | flg = db_get_manifest_setting(); |
| 181 | 179 | |
| 182 | 180 | if( flg & MFESTFLG_RAW ){ |
| 183 | - blob_zero(&manifest); | |
| 181 | + Blob manifest = BLOB_INITIALIZER; | |
| 184 | 182 | content_get(vid, &manifest); |
| 185 | 183 | sterilize_manifest(&manifest, CFTYPE_MANIFEST); |
| 186 | 184 | zManFile = mprintf("%smanifest", g.zLocalRoot); |
| 187 | 185 | blob_write_to_file(&manifest, zManFile); |
| 188 | 186 | free(zManFile); |
| 187 | + blob_reset(&manifest); | |
| 189 | 188 | }else{ |
| 190 | 189 | if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest'") ){ |
| 191 | 190 | zManFile = mprintf("%smanifest", g.zLocalRoot); |
| 192 | 191 | file_delete(zManFile); |
| 193 | 192 | free(zManFile); |
| @@ -207,11 +206,11 @@ | ||
| 207 | 206 | file_delete(zManFile); |
| 208 | 207 | free(zManFile); |
| 209 | 208 | } |
| 210 | 209 | } |
| 211 | 210 | if( flg & MFESTFLG_TAGS ){ |
| 212 | - blob_zero(&taglist); | |
| 211 | + Blob taglist = BLOB_INITIALIZER; | |
| 213 | 212 | zManFile = mprintf("%smanifest.tags", g.zLocalRoot); |
| 214 | 213 | get_checkin_taglist(vid, &taglist); |
| 215 | 214 | blob_write_to_file(&taglist, zManFile); |
| 216 | 215 | free(zManFile); |
| 217 | 216 | blob_reset(&taglist); |
| 218 | 217 |
| --- 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 |
M
src/db.c
+43
-1
| --- src/db.c | ||
| +++ src/db.c | ||
| @@ -170,10 +170,12 @@ | ||
| 170 | 170 | void *pAuthArg; /* Argument to the authorizer */ |
| 171 | 171 | const char *zAuthName; /* Name of the authorizer */ |
| 172 | 172 | int bProtectTriggers; /* True if protection triggers already exist */ |
| 173 | 173 | int nProtect; /* Slots of aProtect used */ |
| 174 | 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 */ | |
| 175 | 177 | } db = { |
| 176 | 178 | PROTECT_USER|PROTECT_CONFIG|PROTECT_BASELINE, /* protectMask */ |
| 177 | 179 | 0, 0, 0, 0, 0, 0, 0, {{0}}, {0}, {0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0}}; |
| 178 | 180 | |
| 179 | 181 | /* |
| @@ -643,10 +645,43 @@ | ||
| 643 | 645 | */ |
| 644 | 646 | #define DB_PREPARE_IGNORE_ERROR 0x001 /* Suppress errors */ |
| 645 | 647 | #define DB_PREPARE_PERSISTENT 0x002 /* Stmt will stick around for a while */ |
| 646 | 648 | #endif |
| 647 | 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 | + | |
| 648 | 683 | /* |
| 649 | 684 | ** Prepare a Stmt. Assume that the Stmt is previously uninitialized. |
| 650 | 685 | ** If the input string contains multiple SQL statements, only the first |
| 651 | 686 | ** one is processed. All statements beyond the first are silently ignored. |
| 652 | 687 | */ |
| @@ -658,10 +693,11 @@ | ||
| 658 | 693 | blob_zero(&pStmt->sql); |
| 659 | 694 | blob_vappendf(&pStmt->sql, zFormat, ap); |
| 660 | 695 | va_end(ap); |
| 661 | 696 | zSql = blob_str(&pStmt->sql); |
| 662 | 697 | db.nPrepare++; |
| 698 | + db_append_dml(zSql); | |
| 663 | 699 | if( flags & DB_PREPARE_PERSISTENT ){ |
| 664 | 700 | prepFlags = SQLITE_PREPARE_PERSISTENT; |
| 665 | 701 | } |
| 666 | 702 | rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, &zExtra); |
| 667 | 703 | if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)==0 ){ |
| @@ -1047,10 +1083,11 @@ | ||
| 1047 | 1083 | rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd); |
| 1048 | 1084 | if( rc ){ |
| 1049 | 1085 | db_err("%s: {%s}", sqlite3_errmsg(g.db), z); |
| 1050 | 1086 | }else if( pStmt ){ |
| 1051 | 1087 | db.nPrepare++; |
| 1088 | + db_append_dml(sqlite3_sql(pStmt)); | |
| 1052 | 1089 | while( sqlite3_step(pStmt)==SQLITE_ROW ){} |
| 1053 | 1090 | rc = sqlite3_finalize(pStmt); |
| 1054 | 1091 | if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z); |
| 1055 | 1092 | } |
| 1056 | 1093 | z = zEnd; |
| @@ -2105,11 +2142,11 @@ | ||
| 2105 | 2142 | sqlite3 *, char **, const sqlite3_api_routines * |
| 2106 | 2143 | ); |
| 2107 | 2144 | sqlite3_appendvfs_init(0,0,0); |
| 2108 | 2145 | g.zVfsName = "apndvfs"; |
| 2109 | 2146 | } |
| 2110 | - blob_zero(&bNameCheck); | |
| 2147 | + blob_reset(&bNameCheck); | |
| 2111 | 2148 | rc = sqlite3_open_v2( |
| 2112 | 2149 | zDbName, &db, |
| 2113 | 2150 | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, |
| 2114 | 2151 | g.zVfsName |
| 2115 | 2152 | ); |
| @@ -2625,13 +2662,18 @@ | ||
| 2625 | 2662 | return res; |
| 2626 | 2663 | } |
| 2627 | 2664 | |
| 2628 | 2665 | /* |
| 2629 | 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. | |
| 2630 | 2671 | */ |
| 2631 | 2672 | void test_is_repo(void){ |
| 2632 | 2673 | int i; |
| 2674 | + verify_all_options(); | |
| 2633 | 2675 | for(i=2; i<g.argc; i++){ |
| 2634 | 2676 | fossil_print("%s: %s\n", |
| 2635 | 2677 | db_looks_like_a_repository(g.argv[i]) ? "yes" : " no", |
| 2636 | 2678 | g.argv[i] |
| 2637 | 2679 | ); |
| 2638 | 2680 |
| --- 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 @@ | ||
| 202 | 202 | rLimitMtime = db_double(0.0, |
| 203 | 203 | "SELECT mtime FROM event WHERE objid=%d", |
| 204 | 204 | ridBackTo); |
| 205 | 205 | } |
| 206 | 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;", | |
| 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;", | |
| 226 | 225 | rid, rid, rLimitMtime, ridBackTo, N |
| 227 | 226 | ); |
| 228 | 227 | if( ridBackTo && db_changes()>1 ){ |
| 229 | 228 | db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", ridBackTo); |
| 230 | 229 | } |
| @@ -322,18 +321,18 @@ | ||
| 322 | 321 | N = -1; |
| 323 | 322 | }else if( N<0 ){ |
| 324 | 323 | N = -N; |
| 325 | 324 | } |
| 326 | 325 | 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" | |
| 335 | 334 | "INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d", |
| 336 | 335 | rid, N |
| 337 | 336 | ); |
| 338 | 337 | } |
| 339 | 338 | |
| @@ -639,44 +638,56 @@ | ||
| 639 | 638 | /* Flag parameters to compute_uses_file() */ |
| 640 | 639 | #define USESFILE_DELETE 0x01 /* Include the check-ins where file deleted */ |
| 641 | 640 | |
| 642 | 641 | #endif |
| 643 | 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 | + | |
| 644 | 658 | |
| 645 | 659 | /* |
| 646 | 660 | ** Add to table zTab the record ID (rid) of every check-in that contains |
| 647 | 661 | ** the file fid. |
| 648 | 662 | */ |
| 649 | 663 | void compute_uses_file(const char *zTab, int fid, int usesFlags){ |
| 650 | 664 | Bag seen; |
| 651 | 665 | Bag pending; |
| 652 | - Stmt ins; | |
| 666 | + Blob ins = BLOB_INITIALIZER; | |
| 667 | + int nIns = 0; | |
| 653 | 668 | Stmt q; |
| 654 | 669 | int rid; |
| 655 | 670 | |
| 656 | 671 | bag_init(&seen); |
| 657 | 672 | 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); | |
| 659 | 674 | db_prepare(&q, "SELECT mid FROM mlink WHERE fid=%d", fid); |
| 660 | 675 | while( db_step(&q)==SQLITE_ROW ){ |
| 661 | 676 | int mid = db_column_int(&q, 0); |
| 662 | 677 | bag_insert(&pending, mid); |
| 663 | 678 | 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); | |
| 667 | 680 | } |
| 668 | 681 | db_finalize(&q); |
| 669 | 682 | |
| 670 | 683 | db_prepare(&q, "SELECT mid FROM mlink WHERE pid=%d", fid); |
| 671 | 684 | while( db_step(&q)==SQLITE_ROW ){ |
| 672 | 685 | int mid = db_column_int(&q, 0); |
| 673 | 686 | bag_insert(&seen, mid); |
| 674 | 687 | 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); | |
| 678 | 689 | } |
| 679 | 690 | } |
| 680 | 691 | db_finalize(&q); |
| 681 | 692 | db_prepare(&q, "SELECT cid FROM plink WHERE pid=:rid AND isprim"); |
| 682 | 693 | |
| @@ -686,16 +697,15 @@ | ||
| 686 | 697 | while( db_step(&q)==SQLITE_ROW ){ |
| 687 | 698 | int mid = db_column_int(&q, 0); |
| 688 | 699 | if( bag_find(&seen, mid) ) continue; |
| 689 | 700 | bag_insert(&seen, mid); |
| 690 | 701 | 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); | |
| 694 | 703 | } |
| 695 | 704 | db_reset(&q); |
| 696 | 705 | } |
| 697 | 706 | db_finalize(&q); |
| 698 | - db_finalize(&ins); | |
| 707 | + db_exec_sql(blob_str(&ins)); | |
| 708 | + blob_reset(&ins); | |
| 699 | 709 | bag_clear(&seen); |
| 700 | 710 | bag_clear(&pending); |
| 701 | 711 | } |
| 702 | 712 |
| --- 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 @@ | ||
| 788 | 788 | ){ |
| 789 | 789 | Blob title; |
| 790 | 790 | int isPopup = P("popup")!=0; |
| 791 | 791 | blob_init(&title,0,0); |
| 792 | 792 | if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){ |
| 793 | - Blob tail; | |
| 793 | + Blob tail = BLOB_INITIALIZER; | |
| 794 | 794 | style_adunit_config(ADUNIT_RIGHT_OK); |
| 795 | 795 | if( wiki_find_title(pBody, &title, &tail) ){ |
| 796 | 796 | if( !isPopup ) style_header("%s", blob_str(&title)); |
| 797 | 797 | wiki_convert(&tail, 0, WIKI_BUTTONS); |
| 798 | 798 | }else{ |
| @@ -801,10 +801,11 @@ | ||
| 801 | 801 | } |
| 802 | 802 | if( !isPopup ){ |
| 803 | 803 | document_emit_js(); |
| 804 | 804 | style_finish_page(); |
| 805 | 805 | } |
| 806 | + blob_reset(&tail); | |
| 806 | 807 | }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){ |
| 807 | 808 | Blob tail = BLOB_INITIALIZER; |
| 808 | 809 | markdown_to_html(pBody, &title, &tail); |
| 809 | 810 | if( !isPopup ){ |
| 810 | 811 | if( blob_size(&title)>0 ){ |
| @@ -816,10 +817,11 @@ | ||
| 816 | 817 | convert_href_and_output(&tail); |
| 817 | 818 | if( !isPopup ){ |
| 818 | 819 | document_emit_js(); |
| 819 | 820 | style_finish_page(); |
| 820 | 821 | } |
| 822 | + blob_reset(&tail); | |
| 821 | 823 | }else if( fossil_strcmp(zMime, "text/plain")==0 ){ |
| 822 | 824 | style_header("%s", zDefaultTitle); |
| 823 | 825 | @ <blockquote><pre> |
| 824 | 826 | @ %h(blob_str(pBody)) |
| 825 | 827 | @ </pre></blockquote> |
| @@ -949,10 +951,11 @@ | ||
| 949 | 951 | |
| 950 | 952 | login_check_credentials(); |
| 951 | 953 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 952 | 954 | style_set_current_feature("doc"); |
| 953 | 955 | blob_init(&title, 0, 0); |
| 956 | + blob_init(&filebody, 0, 0); | |
| 954 | 957 | zDfltTitle = isUV ? "" : "Documentation"; |
| 955 | 958 | db_begin_transaction(); |
| 956 | 959 | while( rid==0 && (++nMiss)<=count(azSuffix) ){ |
| 957 | 960 | zName = P("name"); |
| 958 | 961 | if( isUV ){ |
| @@ -1059,10 +1062,12 @@ | ||
| 1059 | 1062 | } |
| 1060 | 1063 | cgi_check_for_malice(); |
| 1061 | 1064 | document_render(&filebody, zMime, zDfltTitle, zName); |
| 1062 | 1065 | if( nMiss>=count(azSuffix) ) cgi_set_status(404, "Not Found"); |
| 1063 | 1066 | db_end_transaction(0); |
| 1067 | + blob_reset(&title); | |
| 1068 | + blob_reset(&filebody); | |
| 1064 | 1069 | return; |
| 1065 | 1070 | |
| 1066 | 1071 | /* Jump here when unable to locate the document */ |
| 1067 | 1072 | doc_not_found: |
| 1068 | 1073 | db_end_transaction(0); |
| @@ -1075,10 +1080,12 @@ | ||
| 1075 | 1080 | @ <p>Document %h(zOrigName) not found |
| 1076 | 1081 | if( fossil_strcmp(zCheckin,"ckout")!=0 ){ |
| 1077 | 1082 | @ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a> |
| 1078 | 1083 | } |
| 1079 | 1084 | style_finish_page(); |
| 1085 | + blob_reset(&title); | |
| 1086 | + blob_reset(&filebody); | |
| 1080 | 1087 | return; |
| 1081 | 1088 | } |
| 1082 | 1089 | |
| 1083 | 1090 | /* |
| 1084 | 1091 | ** The default logo. |
| 1085 | 1092 |
| --- 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 @@ | ||
| 729 | 729 | while( *z && n-- ){ |
| 730 | 730 | *z = zEncode[zDecode[(*z)&0x7f]&0x1f]; |
| 731 | 731 | z++; |
| 732 | 732 | } |
| 733 | 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 | + | |
| 734 | 752 | |
| 735 | 753 | /* |
| 736 | 754 | ** Decode a string encoded using "quoted-printable". |
| 737 | 755 | ** |
| 738 | 756 | ** (1) "=" followed by two hex digits becomes a single |
| 739 | 757 |
| --- 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 |
-2
| --- src/forum.c | ||
| +++ src/forum.c | ||
| @@ -1830,11 +1830,10 @@ | ||
| 1830 | 1830 | if( db_int(0, "SELECT count(*) FROM user " |
| 1831 | 1831 | " WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'")==0 ){ |
| 1832 | 1832 | @ <p>No non-supervisor moderators |
| 1833 | 1833 | }else{ |
| 1834 | 1834 | Stmt q = empty_Stmt; |
| 1835 | - int nRows = 0; | |
| 1836 | 1835 | db_prepare(&q, "SELECT uid, login, cap FROM user " |
| 1837 | 1836 | "WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'" |
| 1838 | 1837 | " ORDER BY login"); |
| 1839 | 1838 | @ <table class='bordered'> |
| 1840 | 1839 | @ <thead><tr><th>User</th><th>Capabilities</th></tr></thead> |
| @@ -1841,11 +1840,10 @@ | ||
| 1841 | 1840 | @ <tbody> |
| 1842 | 1841 | while( SQLITE_ROW==db_step(&q) ){ |
| 1843 | 1842 | const int iUid = db_column_int(&q, 0); |
| 1844 | 1843 | const char *zUser = db_column_text(&q, 1); |
| 1845 | 1844 | const char *zCap = db_column_text(&q, 2); |
| 1846 | - ++nRows; | |
| 1847 | 1845 | @ <tr> |
| 1848 | 1846 | @ <td><a href='%R/setup_uedit?id=%d(iUid)'>%h(zUser)</a></td> |
| 1849 | 1847 | @ <td>(%h(zCap))</td> |
| 1850 | 1848 | @ </tr> |
| 1851 | 1849 | } |
| 1852 | 1850 |
| --- 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 @@ | ||
| 495 | 495 | ** |
| 496 | 496 | ** TIMELINE_DISJOINT: Omit descenders |
| 497 | 497 | ** TIMELINE_FILLGAPS: Use step-children |
| 498 | 498 | ** TIMELINE_XMERGE: Omit off-graph merge lines |
| 499 | 499 | */ |
| 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 | +){ | |
| 501 | 505 | GraphRow *pRow, *pDesc, *pDup, *pLoop, *pParent; |
| 502 | 506 | int i, j; |
| 503 | 507 | u64 mask; |
| 504 | 508 | int hasDup = 0; /* True if one or more isDup entries */ |
| 505 | 509 | const char *zTrunk; |
| @@ -963,12 +967,12 @@ | ||
| 963 | 967 | } |
| 964 | 968 | } |
| 965 | 969 | |
| 966 | 970 | /* |
| 967 | 971 | ** 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. | |
| 970 | 974 | ** |
| 971 | 975 | ** aMap[X]=Y means that the X-th rail is drawn as the Y-th rail. |
| 972 | 976 | ** |
| 973 | 977 | ** Do not move rails around if there are timewarps, because that can |
| 974 | 978 | ** seriously mess up the display of timewarps. Timewarps should be |
| @@ -975,10 +979,11 @@ | ||
| 975 | 979 | ** rare so this should not be a serious limitation to the algorithm. |
| 976 | 980 | */ |
| 977 | 981 | aMap = p->aiRailMap; |
| 978 | 982 | for(i=0; i<=p->mxRail; i++) aMap[i] = i; /* Set up a default mapping */ |
| 979 | 983 | if( nTimewarp==0 ){ |
| 984 | + int kk; | |
| 980 | 985 | /* Priority bits: |
| 981 | 986 | ** |
| 982 | 987 | ** 0x04 The preferred branch |
| 983 | 988 | ** |
| 984 | 989 | ** 0x02 A merge rail - a rail that contains merge lines into |
| @@ -986,17 +991,20 @@ | ||
| 986 | 991 | ** is defined. This improves the display of r=BRANCH |
| 987 | 992 | ** options to /timeline. |
| 988 | 993 | ** |
| 989 | 994 | ** 0x01 A rail that merges with the preferred branch |
| 990 | 995 | */ |
| 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 ){ | |
| 995 | 1000 | 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; | |
| 998 | 1006 | for(i=0; i<=p->mxRail; i++){ |
| 999 | 1007 | if( pRow->mergeIn[i] ) aPriority[i] |= 1; |
| 1000 | 1008 | } |
| 1001 | 1009 | if( pRow->mergeOut>=0 ) aPriority[pRow->mergeOut] |= 1; |
| 1002 | 1010 | } |
| @@ -1007,10 +1015,11 @@ | ||
| 1007 | 1015 | } |
| 1008 | 1016 | } |
| 1009 | 1017 | }else{ |
| 1010 | 1018 | j = 1; |
| 1011 | 1019 | aPriority[0] = 4; |
| 1020 | + mxMatch = 1; | |
| 1012 | 1021 | for(pRow=p->pFirst; pRow; pRow=pRow->pNext){ |
| 1013 | 1022 | if( pRow->iRail==0 ){ |
| 1014 | 1023 | for(i=0; i<=p->mxRail; i++){ |
| 1015 | 1024 | if( pRow->mergeIn[i] ) aPriority[i] |= 1; |
| 1016 | 1025 | } |
| @@ -1020,17 +1029,24 @@ | ||
| 1020 | 1029 | } |
| 1021 | 1030 | |
| 1022 | 1031 | #if 0 |
| 1023 | 1032 | fprintf(stderr,"mergeRail: 0x%llx\n", p->mergeRail); |
| 1024 | 1033 | 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 | + } | |
| 1026 | 1038 | fprintf(stderr,"\n"); |
| 1027 | 1039 | #endif |
| 1028 | 1040 | |
| 1029 | 1041 | 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 | + } | |
| 1032 | 1048 | } |
| 1033 | 1049 | for(i=p->mxRail; i>=0; i--){ |
| 1034 | 1050 | if( aPriority[i]==3 ) aMap[i] = j++; |
| 1035 | 1051 | } |
| 1036 | 1052 | for(i=0; i<=p->mxRail; i++){ |
| 1037 | 1053 |
| --- 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 @@ | ||
| 320 | 320 | |TIMELINE_NOSCROLL |
| 321 | 321 | |TIMELINE_XMERGE |
| 322 | 322 | |TIMELINE_CHPICK, |
| 323 | 323 | 0, 0, 0, rid, rid2, 0); |
| 324 | 324 | db_finalize(&q); |
| 325 | + blob_reset(&sql); | |
| 325 | 326 | } |
| 326 | 327 | |
| 327 | 328 | |
| 328 | 329 | /* |
| 329 | 330 | ** Append the difference between artifacts to the output |
| @@ -624,11 +625,10 @@ | ||
| 624 | 625 | pCfg = construct_diff_flags(diffType, &DCfg); |
| 625 | 626 | nChng = db_int(0, "SELECT count(*) FROM vfile" |
| 626 | 627 | " WHERE vid=%d AND (deleted OR chnged OR rid==0)", vid); |
| 627 | 628 | if( nChng==0 ){ |
| 628 | 629 | @ <p>No uncommitted changes</p> |
| 629 | - style_finish_page(); | |
| 630 | 630 | return; |
| 631 | 631 | } |
| 632 | 632 | db_prepare(&q, |
| 633 | 633 | /* 0 1 2 3 4 5 6 */ |
| 634 | 634 | "SELECT pathname, deleted, chnged , rid==0, rid, islink, uuid" |
| @@ -785,17 +785,12 @@ | ||
| 785 | 785 | @ Changes to %h(zFile) |
| 786 | 786 | @ </span></div> |
| 787 | 787 | if( pCfg ){ |
| 788 | 788 | char *zFullFN; |
| 789 | 789 | char *zHexFN; |
| 790 | - int nFullFN; | |
| 791 | 790 | 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); | |
| 797 | 792 | fossil_free(zFullFN); |
| 798 | 793 | pCfg->zLeftHash = zHexFN; |
| 799 | 794 | text_diff(&lhs, &rhs, cgi_output_blob(), pCfg); |
| 800 | 795 | pCfg->zLeftHash = 0; |
| 801 | 796 | fossil_free(zHexFN); |
| @@ -820,11 +815,14 @@ | ||
| 820 | 815 | ** |
| 821 | 816 | ** If the "exbase=PATH" query parameter is provided, then the diff shown |
| 822 | 817 | ** uses the files in PATH as the baseline. This is the same as using |
| 823 | 818 | ** the "--from PATH" argument to the "fossil diff" command-line. In fact, |
| 824 | 819 | ** 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. | |
| 826 | 824 | ** |
| 827 | 825 | ** Other query parameters related to diffs are also accepted. |
| 828 | 826 | */ |
| 829 | 827 | void ckout_page(void){ |
| 830 | 828 | int vid; |
| @@ -832,11 +830,11 @@ | ||
| 832 | 830 | int nHome; |
| 833 | 831 | const char *zExBase; |
| 834 | 832 | char *zHostname; |
| 835 | 833 | char *zCwd; |
| 836 | 834 | |
| 837 | - if( !db_open_local(0) || !cgi_is_loopback(g.zIpAddr) ){ | |
| 835 | + if( !cgi_is_loopback(g.zIpAddr) || !db_open_local(0) ){ | |
| 838 | 836 | cgi_redirectf("%R/home"); |
| 839 | 837 | return; |
| 840 | 838 | } |
| 841 | 839 | file_chdir(g.zLocalRoot, 0); |
| 842 | 840 | vid = db_lget_int("checkout", 0); |
| @@ -862,18 +860,20 @@ | ||
| 862 | 860 | } |
| 863 | 861 | render_checkin_context(vid, 0, 0, 0); |
| 864 | 862 | @ <hr> |
| 865 | 863 | zExBase = P("exbase"); |
| 866 | 864 | 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); | |
| 868 | 867 | if( nHome && strncmp(zCBase, zHome, nHome)==0 && zCBase[nHome]=='/' ){ |
| 869 | 868 | @ <p>Using external baseline: ~%h(zCBase+nHome)</p> |
| 870 | 869 | }else{ |
| 871 | 870 | @ <p>Using external baseline: %h(zCBase)</p> |
| 872 | 871 | } |
| 873 | 872 | ckout_external_base_diff(vid, zCBase); |
| 874 | 873 | fossil_free(zCBase); |
| 874 | + fossil_free(zPath); | |
| 875 | 875 | }else{ |
| 876 | 876 | ckout_normal_diff(vid); |
| 877 | 877 | } |
| 878 | 878 | style_finish_page(); |
| 879 | 879 | } |
| @@ -1933,11 +1933,11 @@ | ||
| 1933 | 1933 | tag_private_status(rid); |
| 1934 | 1934 | } |
| 1935 | 1935 | db_finalize(&q); |
| 1936 | 1936 | if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d AND tagid=%d", |
| 1937 | 1937 | rid, TAG_CLUSTER) ){ |
| 1938 | - @ Cluster | |
| 1938 | + @ Cluster %z(href("%R/info/%S",zUuid))%S(zUuid)</a>. | |
| 1939 | 1939 | cnt++; |
| 1940 | 1940 | } |
| 1941 | 1941 | if( cnt==0 ){ |
| 1942 | 1942 | @ Unrecognized artifact |
| 1943 | 1943 | if( pDownloadName && blob_size(pDownloadName)==0 ){ |
| @@ -2249,12 +2249,12 @@ | ||
| 2249 | 2249 | } |
| 2250 | 2250 | if( zName[0]=='x' |
| 2251 | 2251 | && ((nName-1)&1)==0 |
| 2252 | 2252 | && validate16(&zName[1],nName-1) |
| 2253 | 2253 | && g.perm.Admin |
| 2254 | - && db_open_local(0) | |
| 2255 | 2254 | && cgi_is_loopback(g.zIpAddr) |
| 2255 | + && db_open_local(0) | |
| 2256 | 2256 | ){ |
| 2257 | 2257 | /* Treat the HASH as a hex-encoded filename */ |
| 2258 | 2258 | int n = (nName-1)/2; |
| 2259 | 2259 | char *zFN = fossil_malloc(n+1); |
| 2260 | 2260 | decode16((const u8*)&zName[1], (u8*)zFN, nName-1); |
| @@ -3168,10 +3168,143 @@ | ||
| 3168 | 3168 | ticket_output_change_artifact(pTktChng, 0, 1, 0); |
| 3169 | 3169 | manifest_destroy(pTktChng); |
| 3170 | 3170 | style_finish_page(); |
| 3171 | 3171 | } |
| 3172 | 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> </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> %S(zUuid) </td> | |
| 3271 | + }else{ | |
| 3272 | + @ <td> %z(href("%R/info/%!S",zUuid))%S(zUuid)</a> </td> | |
| 3273 | + } | |
| 3274 | + if( g.perm.Admin ){ | |
| 3275 | + int rcvid = db_column_int(&q,5); | |
| 3276 | + if( rcvid<=0 ){ | |
| 3277 | + @ <td> | |
| 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> | |
| 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> | |
| 3298 | + } | |
| 3299 | + @ </tr> | |
| 3300 | + } | |
| 3301 | + @ </table> | |
| 3302 | + db_finalize(&q); | |
| 3303 | + style_finish_page(); | |
| 3304 | +} | |
| 3305 | + | |
| 3173 | 3306 | |
| 3174 | 3307 | /* |
| 3175 | 3308 | ** WEBPAGE: info |
| 3176 | 3309 | ** URL: info/NAME |
| 3177 | 3310 | ** |
| @@ -3256,10 +3389,14 @@ | ||
| 3256 | 3389 | if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){ |
| 3257 | 3390 | ci_page(); |
| 3258 | 3391 | }else |
| 3259 | 3392 | if( db_exists("SELECT 1 FROM attachment WHERE attachid=%d", rid) ){ |
| 3260 | 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); | |
| 3261 | 3398 | }else |
| 3262 | 3399 | { |
| 3263 | 3400 | artifact_page(); |
| 3264 | 3401 | } |
| 3265 | 3402 | } |
| 3266 | 3403 |
| --- 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> </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> %S(zUuid) </td> |
| 3271 | }else{ |
| 3272 | @ <td> %z(href("%R/info/%!S",zUuid))%S(zUuid)</a> </td> |
| 3273 | } |
| 3274 | if( g.perm.Admin ){ |
| 3275 | int rcvid = db_column_int(&q,5); |
| 3276 | if( rcvid<=0 ){ |
| 3277 | @ <td> |
| 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> |
| 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> |
| 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 @@ | ||
| 1330 | 1330 | |
| 1331 | 1331 | /* We should be done with options.. */ |
| 1332 | 1332 | verify_all_options(); |
| 1333 | 1333 | fossil_version_blob(&versionInfo, verboseFlag); |
| 1334 | 1334 | fossil_print("%s", blob_str(&versionInfo)); |
| 1335 | + blob_reset(&versionInfo); | |
| 1335 | 1336 | } |
| 1336 | 1337 | |
| 1337 | 1338 | |
| 1338 | 1339 | /* |
| 1339 | 1340 | ** WEBPAGE: version |
| @@ -3297,11 +3298,11 @@ | ||
| 3297 | 3298 | } |
| 3298 | 3299 | zInitPage = find_option("page", "p", 1); |
| 3299 | 3300 | if( zInitPage && zInitPage[0]=='/' ) zInitPage++; |
| 3300 | 3301 | zFossilCmd = find_option("fossilcmd", 0, 1); |
| 3301 | 3302 | if( zFrom && zInitPage==0 ){ |
| 3302 | - zInitPage = mprintf("ckout?exbase=%T", zFrom); | |
| 3303 | + zInitPage = mprintf("ckout?exbase=%H", zFrom); | |
| 3303 | 3304 | } |
| 3304 | 3305 | } |
| 3305 | 3306 | zNotFound = find_option("notfound", 0, 1); |
| 3306 | 3307 | allowRepoList = find_option("repolist",0,0)!=0; |
| 3307 | 3308 | if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1; |
| 3308 | 3309 |
| --- 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 @@ | ||
| 99 | 99 | $(SRCDIR)/lookslike.c \ |
| 100 | 100 | $(SRCDIR)/main.c \ |
| 101 | 101 | $(SRCDIR)/manifest.c \ |
| 102 | 102 | $(SRCDIR)/markdown.c \ |
| 103 | 103 | $(SRCDIR)/markdown_html.c \ |
| 104 | + $(SRCDIR)/match.c \ | |
| 104 | 105 | $(SRCDIR)/md5.c \ |
| 105 | 106 | $(SRCDIR)/merge.c \ |
| 106 | 107 | $(SRCDIR)/merge3.c \ |
| 107 | 108 | $(SRCDIR)/moderate.c \ |
| 108 | 109 | $(SRCDIR)/name.c \ |
| @@ -364,10 +365,11 @@ | ||
| 364 | 365 | $(OBJDIR)/lookslike_.c \ |
| 365 | 366 | $(OBJDIR)/main_.c \ |
| 366 | 367 | $(OBJDIR)/manifest_.c \ |
| 367 | 368 | $(OBJDIR)/markdown_.c \ |
| 368 | 369 | $(OBJDIR)/markdown_html_.c \ |
| 370 | + $(OBJDIR)/match_.c \ | |
| 369 | 371 | $(OBJDIR)/md5_.c \ |
| 370 | 372 | $(OBJDIR)/merge_.c \ |
| 371 | 373 | $(OBJDIR)/merge3_.c \ |
| 372 | 374 | $(OBJDIR)/moderate_.c \ |
| 373 | 375 | $(OBJDIR)/name_.c \ |
| @@ -513,10 +515,11 @@ | ||
| 513 | 515 | $(OBJDIR)/lookslike.o \ |
| 514 | 516 | $(OBJDIR)/main.o \ |
| 515 | 517 | $(OBJDIR)/manifest.o \ |
| 516 | 518 | $(OBJDIR)/markdown.o \ |
| 517 | 519 | $(OBJDIR)/markdown_html.o \ |
| 520 | + $(OBJDIR)/match.o \ | |
| 518 | 521 | $(OBJDIR)/md5.o \ |
| 519 | 522 | $(OBJDIR)/merge.o \ |
| 520 | 523 | $(OBJDIR)/merge3.o \ |
| 521 | 524 | $(OBJDIR)/moderate.o \ |
| 522 | 525 | $(OBJDIR)/name.o \ |
| @@ -848,10 +851,11 @@ | ||
| 848 | 851 | $(OBJDIR)/lookslike_.c:$(OBJDIR)/lookslike.h \ |
| 849 | 852 | $(OBJDIR)/main_.c:$(OBJDIR)/main.h \ |
| 850 | 853 | $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \ |
| 851 | 854 | $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \ |
| 852 | 855 | $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \ |
| 856 | + $(OBJDIR)/match_.c:$(OBJDIR)/match.h \ | |
| 853 | 857 | $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \ |
| 854 | 858 | $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \ |
| 855 | 859 | $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \ |
| 856 | 860 | $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \ |
| 857 | 861 | $(OBJDIR)/name_.c:$(OBJDIR)/name.h \ |
| @@ -1597,10 +1601,18 @@ | ||
| 1597 | 1601 | |
| 1598 | 1602 | $(OBJDIR)/markdown_html.o: $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h $(SRCDIR)/config.h |
| 1599 | 1603 | $(XTCC) -o $(OBJDIR)/markdown_html.o -c $(OBJDIR)/markdown_html_.c |
| 1600 | 1604 | |
| 1601 | 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 | |
| 1602 | 1614 | |
| 1603 | 1615 | $(OBJDIR)/md5_.c: $(SRCDIR)/md5.c $(OBJDIR)/translate |
| 1604 | 1616 | $(OBJDIR)/translate $(SRCDIR)/md5.c >$@ |
| 1605 | 1617 | |
| 1606 | 1618 | $(OBJDIR)/md5.o: $(OBJDIR)/md5_.c $(OBJDIR)/md5.h $(SRCDIR)/config.h |
| 1607 | 1619 | |
| 1608 | 1620 | 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(®exp, 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(®exp, 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(®exp, 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(®exp, 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 @@ | ||
| 1676 | 1676 | ** n=N Show N artifacts |
| 1677 | 1677 | ** s=S Start with artifact number S |
| 1678 | 1678 | ** priv Show only unpublished or private artifacts |
| 1679 | 1679 | ** phan Show only phantom artifacts |
| 1680 | 1680 | ** hclr Color code hash types (SHA1 vs SHA3) |
| 1681 | +** recent Show the most recent N artifacts | |
| 1681 | 1682 | */ |
| 1682 | 1683 | void bloblist_page(void){ |
| 1683 | 1684 | Stmt q; |
| 1684 | 1685 | int s = atoi(PD("s","0")); |
| 1685 | 1686 | int n = atoi(PD("n","5000")); |
| 1686 | 1687 | int mx = db_int(0, "SELECT max(rid) FROM blob"); |
| 1687 | 1688 | int privOnly = PB("priv"); |
| 1688 | 1689 | int phantomOnly = PB("phan"); |
| 1689 | 1690 | int hashClr = PB("hclr"); |
| 1691 | + int bRecent = PB("recent"); | |
| 1692 | + int bUnclst = PB("unclustered"); | |
| 1690 | 1693 | char *zRange; |
| 1691 | 1694 | char *zSha1Bg; |
| 1692 | 1695 | char *zSha3Bg; |
| 1693 | 1696 | |
| 1694 | 1697 | login_check_credentials(); |
| 1695 | 1698 | if( !g.perm.Read ){ login_needed(g.anon.Read); return; } |
| 1696 | 1699 | cgi_check_for_malice(); |
| 1697 | 1700 | style_header("List Of Artifacts"); |
| 1698 | 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 | + } | |
| 1699 | 1708 | if( g.perm.Admin ){ |
| 1700 | 1709 | style_submenu_element("Artifact Log", "rcvfromlist"); |
| 1701 | 1710 | } |
| 1702 | 1711 | if( !phantomOnly ){ |
| 1703 | 1712 | style_submenu_element("Phantoms", "bloblist?phan"); |
| 1704 | 1713 | } |
| 1714 | + style_submenu_element("Clusters","clusterlist"); | |
| 1705 | 1715 | if( g.perm.Private || g.perm.Admin ){ |
| 1706 | 1716 | if( !privOnly ){ |
| 1707 | 1717 | style_submenu_element("Private", "bloblist?priv"); |
| 1708 | 1718 | } |
| 1709 | 1719 | }else{ |
| @@ -1710,58 +1720,70 @@ | ||
| 1710 | 1720 | privOnly = 0; |
| 1711 | 1721 | } |
| 1712 | 1722 | if( g.perm.Write ){ |
| 1713 | 1723 | style_submenu_element("Artifact Stats", "artifact_stats"); |
| 1714 | 1724 | } |
| 1715 | - if( !privOnly && !phantomOnly && mx>n && P("s")==0 ){ | |
| 1725 | + if( !privOnly && !phantomOnly && mx>n && P("s")==0 && !bRecent && !bUnclst ){ | |
| 1716 | 1726 | int i; |
| 1717 | 1727 | @ <p>Select a range of artifacts to view:</p> |
| 1718 | 1728 | @ <ul> |
| 1719 | 1729 | for(i=1; i<=mx; i+=n){ |
| 1720 | 1730 | @ <li> %z(href("%R/bloblist?s=%d&n=%d",i,n)) |
| 1721 | 1731 | @ %d(i)..%d(i+n-1<mx?i+n-1:mx)</a> |
| 1722 | 1732 | } |
| 1733 | + @ <li> %z(href("%R/bloblist?n=250&recent"))250 most recent</a> | |
| 1734 | + @ <li> %z(href("%R/bloblist?unclustered"))All unclustered</a> | |
| 1723 | 1735 | @ </ul> |
| 1724 | 1736 | style_finish_page(); |
| 1725 | 1737 | return; |
| 1726 | 1738 | } |
| 1727 | 1739 | if( phantomOnly || privOnly || mx>n ){ |
| 1728 | 1740 | style_submenu_element("Index", "bloblist"); |
| 1729 | 1741 | } |
| 1730 | 1742 | if( privOnly ){ |
| 1743 | + @ <h2>Private Artifacts</h2> | |
| 1731 | 1744 | zRange = mprintf("IN private"); |
| 1732 | 1745 | }else if( phantomOnly ){ |
| 1746 | + @ <h2>Phantom Artifacts</h2> | |
| 1733 | 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); | |
| 1734 | 1755 | }else{ |
| 1735 | 1756 | zRange = mprintf("BETWEEN %d AND %d", s, s+n-1); |
| 1736 | 1757 | } |
| 1737 | 1758 | describe_artifacts(zRange); |
| 1738 | 1759 | fossil_free(zRange); |
| 1739 | 1760 | 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*/ | |
| 1742 | 1767 | ); |
| 1743 | 1768 | if( skin_detail_boolean("white-foreground") ){ |
| 1744 | 1769 | zSha1Bg = "#714417"; |
| 1745 | 1770 | zSha3Bg = "#177117"; |
| 1746 | 1771 | }else{ |
| 1747 | 1772 | zSha1Bg = "#ebffb0"; |
| 1748 | 1773 | zSha3Bg = "#b0ffb0"; |
| 1749 | 1774 | } |
| 1750 | 1775 | @ <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 | |
| 1756 | 1777 | while( db_step(&q)==SQLITE_ROW ){ |
| 1757 | 1778 | int rid = db_column_int(&q,0); |
| 1758 | 1779 | const char *zUuid = db_column_text(&q, 1); |
| 1759 | 1780 | const char *zDesc = db_column_text(&q, 2); |
| 1760 | 1781 | int isPriv = db_column_int(&q,3); |
| 1761 | 1782 | 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); | |
| 1763 | 1785 | if( isPriv && !isPhantom && !g.perm.Private && !g.perm.Admin ){ |
| 1764 | 1786 | /* Don't show private artifacts to users without Private (x) permission */ |
| 1765 | 1787 | continue; |
| 1766 | 1788 | } |
| 1767 | 1789 | if( hashClr ){ |
| @@ -1770,16 +1792,14 @@ | ||
| 1770 | 1792 | }else{ |
| 1771 | 1793 | @ <tr><td align="right">%d(rid)</td> |
| 1772 | 1794 | } |
| 1773 | 1795 | @ <td> %z(href("%R/info/%!S",zUuid))%S(zUuid)</a> </td> |
| 1774 | 1796 | if( g.perm.Admin ){ |
| 1775 | - int rcvid = db_column_int(&q,5); | |
| 1776 | - if( rcvid<=0 ){ | |
| 1777 | - @ <td> | |
| 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) | |
| 1781 | 1801 | } |
| 1782 | 1802 | @ <td align="left">%h(zDesc)</td> |
| 1783 | 1803 | if( zRef && zRef[0] ){ |
| 1784 | 1804 | @ <td>%z(href("%R/info/%!S",zRef))%S(zRef)</a> |
| 1785 | 1805 | }else{ |
| @@ -2163,5 +2183,89 @@ | ||
| 2163 | 2183 | " ORDER BY 1"); |
| 2164 | 2184 | @ <h1>Hash Prefix Collisions on All Artifacts</h1> |
| 2165 | 2185 | collision_report("SELECT uuid FROM blob ORDER BY 1"); |
| 2166 | 2186 | style_finish_page(); |
| 2167 | 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> | |
| 2227 | + @ <th>Hash | |
| 2228 | + @ <th>Date Received | |
| 2229 | + @ <th>Size | |
| 2230 | + @ <th>Compressed 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> | |
| 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> | |
| 2257 | + } | |
| 2258 | + if( zIp ){ | |
| 2259 | + @ <td>%h(zIp) | |
| 2260 | + }else{ | |
| 2261 | + @ <td> | |
| 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 | +} | |
| 2168 | 2272 |
| --- 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> %z(href("%R/info/%!S",zUuid))%S(zUuid)</a> </td> |
| 1774 | if( g.perm.Admin ){ |
| 1775 | int rcvid = db_column_int(&q,5); |
| 1776 | if( rcvid<=0 ){ |
| 1777 | @ <td> |
| 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> %z(href("%R/info/%!S",zUuid))%S(zUuid)</a> </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> |
| 2227 | @ <th>Hash |
| 2228 | @ <th>Date Received |
| 2229 | @ <th>Size |
| 2230 | @ <th>Compressed 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> |
| 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> |
| 2257 | } |
| 2258 | if( zIp ){ |
| 2259 | @ <td>%h(zIp) |
| 2260 | }else{ |
| 2261 | @ <td> |
| 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 @@ | ||
| 81 | 81 | sqlite3_context *context, |
| 82 | 82 | int argc, |
| 83 | 83 | sqlite3_value **argv |
| 84 | 84 | ){ |
| 85 | 85 | const char *zFile; |
| 86 | - Blob x, y; | |
| 86 | + Blob x, y, out; | |
| 87 | 87 | int rid; |
| 88 | - char *aOut; | |
| 89 | 88 | int nOut; |
| 90 | 89 | sqlite3_int64 sz; |
| 91 | 90 | |
| 92 | 91 | rid = sqlite3_value_int(argv[0]); |
| 93 | 92 | if( !content_get(rid, &x) ){ |
| @@ -104,34 +103,29 @@ | ||
| 104 | 103 | if( sz<0 ){ |
| 105 | 104 | sqlite3_result_error(context, "mkdelta(X,Y): cannot read file Y", -1); |
| 106 | 105 | blob_reset(&x); |
| 107 | 106 | return; |
| 108 | 107 | } |
| 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); | |
| 116 | 110 | if( blob_size(&x)==blob_size(&y) |
| 117 | 111 | && memcmp(blob_buffer(&x), blob_buffer(&y), blob_size(&x))==0 |
| 118 | 112 | ){ |
| 119 | 113 | blob_reset(&y); |
| 120 | 114 | blob_reset(&x); |
| 121 | 115 | sqlite3_result_blob64(context, "", 0, SQLITE_STATIC); |
| 122 | 116 | return; |
| 123 | 117 | } |
| 124 | 118 | 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); | |
| 126 | 121 | blob_reset(&x); |
| 127 | 122 | 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), | |
| 131 | 125 | SQLITE_TRANSIENT); |
| 132 | - blob_reset(&x); | |
| 126 | + blob_reset(&out); | |
| 133 | 127 | } |
| 134 | 128 | |
| 135 | 129 | |
| 136 | 130 | /* |
| 137 | 131 | ** Generate a binary patch file and store it into the file |
| 138 | 132 |
| --- 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 @@ | ||
| 105 | 105 | Use %!j to include double-quotes around it. */ |
| 106 | 106 | #define etSHELLESC 26 /* Escape a filename for use in a shell command: %$ |
| 107 | 107 | See blob_append_escaped_arg() for details |
| 108 | 108 | "%$" -> adds "./" prefix if necessary. |
| 109 | 109 | "%!$" -> omits the "./" prefix. */ |
| 110 | +#define etHEX 27 /* Encode a string as hexadecimal */ | |
| 110 | 111 | |
| 111 | 112 | |
| 112 | 113 | /* |
| 113 | 114 | ** An "etByte" is an 8-bit unsigned value. |
| 114 | 115 | */ |
| @@ -142,11 +143,11 @@ | ||
| 142 | 143 | ** NB: When modifying this table is it vital that you also update the fmtchr[] |
| 143 | 144 | ** variable to match!!! |
| 144 | 145 | */ |
| 145 | 146 | static const char aDigits[] = "0123456789ABCDEF0123456789abcdef"; |
| 146 | 147 | static const char aPrefix[] = "-x0\000X0"; |
| 147 | -static const char fmtchr[] = "dsgzqQbBWhRtTwFSjcouxXfeEGin%p/$"; | |
| 148 | +static const char fmtchr[] = "dsgzqQbBWhRtTwFSjcouxXfeEGin%p/$H"; | |
| 148 | 149 | static const et_info fmtinfo[] = { |
| 149 | 150 | { 'd', 10, 1, etRADIX, 0, 0 }, |
| 150 | 151 | { 's', 0, 4, etSTRING, 0, 0 }, |
| 151 | 152 | { 'g', 0, 1, etGENERIC, 30, 0 }, |
| 152 | 153 | { 'z', 0, 6, etDYNSTRING, 0, 0 }, |
| @@ -176,10 +177,11 @@ | ||
| 176 | 177 | { 'n', 0, 0, etSIZE, 0, 0 }, |
| 177 | 178 | { '%', 0, 0, etPERCENT, 0, 0 }, |
| 178 | 179 | { 'p', 16, 0, etPOINTER, 0, 1 }, |
| 179 | 180 | { '/', 0, 0, etPATH, 0, 0 }, |
| 180 | 181 | { '$', 0, 0, etSHELLESC, 0, 0 }, |
| 182 | + { 'H', 0, 0, etHEX, 0, 0 }, | |
| 181 | 183 | { etERROR, 0,0,0,0,0} /* Must be last */ |
| 182 | 184 | }; |
| 183 | 185 | #define etNINFO count(fmtinfo) |
| 184 | 186 | |
| 185 | 187 | /* |
| @@ -843,10 +845,21 @@ | ||
| 843 | 845 | case etSHELLESC: { |
| 844 | 846 | char *zArg = va_arg(ap, char*); |
| 845 | 847 | blob_append_escaped_arg(pBlob, zArg, !flag_altform2); |
| 846 | 848 | length = width = 0; |
| 847 | 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; | |
| 848 | 861 | } |
| 849 | 862 | case etERROR: |
| 850 | 863 | buf[0] = '%'; |
| 851 | 864 | buf[1] = c; |
| 852 | 865 | errorflag = 0; |
| 853 | 866 |
| --- 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 @@ | ||
| 116 | 116 | if( inSublist ){ |
| 117 | 117 | @ </ul> |
| 118 | 118 | inSublist = 0; |
| 119 | 119 | } |
| 120 | 120 | @ </li> |
| 121 | - if( db_open_local(0) && cgi_is_loopback(g.zIpAddr) ){ | |
| 121 | + if( cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){ | |
| 122 | 122 | @ <li>%z(href("%R/ckout"))Checkout Status</a></li> |
| 123 | 123 | } |
| 124 | 124 | if( g.perm.Read ){ |
| 125 | 125 | const char *zEditGlob = db_get("fileedit-glob",""); |
| 126 | 126 | @ <li>%z(href("%R/tree"))File Browser</a> |
| 127 | 127 |
| --- 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 |
+2
| --- src/style.c | ||
| +++ src/style.c | ||
| @@ -821,10 +821,11 @@ | ||
| 821 | 821 | if( g.perm.Debug && P("showqp") ){ |
| 822 | 822 | @ <div class="debug"> |
| 823 | 823 | cgi_print_all(0, 0, 0); |
| 824 | 824 | @ </div> |
| 825 | 825 | } |
| 826 | + fossil_free(zTitle); | |
| 826 | 827 | } |
| 827 | 828 | |
| 828 | 829 | #if INTERFACE |
| 829 | 830 | /* Allowed parameters for style_adunit() */ |
| 830 | 831 | #define ADUNIT_OFF 0x0001 /* Do not allow ads on this page */ |
| @@ -1300,10 +1301,11 @@ | ||
| 1300 | 1301 | Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL); |
| 1301 | 1302 | Th_Store("home", g.zTop); |
| 1302 | 1303 | image_url_var("logo"); |
| 1303 | 1304 | image_url_var("background"); |
| 1304 | 1305 | Th_Render(blob_str(&css)); |
| 1306 | + blob_reset(&css); | |
| 1305 | 1307 | |
| 1306 | 1308 | /* Tell CGI that the content returned by this page is considered cacheable */ |
| 1307 | 1309 | g.isConst = 1; |
| 1308 | 1310 | } |
| 1309 | 1311 | |
| 1310 | 1312 |
| --- 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 @@ | ||
| 191 | 191 | void www_print_timeline( |
| 192 | 192 | Stmt *pQuery, /* Query to implement the timeline */ |
| 193 | 193 | int tmFlags, /* Flags controlling display behavior */ |
| 194 | 194 | const char *zThisUser, /* Suppress links to this user */ |
| 195 | 195 | 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 */ | |
| 197 | 197 | int selectedRid, /* Highlight the line with this RID value or zero */ |
| 198 | 198 | int secondRid, /* Secondary highlight (or zero) */ |
| 199 | 199 | void (*xExtra)(int) /* Routine to call on each line of display */ |
| 200 | 200 | ){ |
| 201 | 201 | int mxWikiLen; |
| @@ -813,11 +813,11 @@ | ||
| 813 | 813 | } |
| 814 | 814 | if( pendingEndTr ){ |
| 815 | 815 | @ </td></tr> |
| 816 | 816 | } |
| 817 | 817 | if( pGraph ){ |
| 818 | - graph_finish(pGraph, zLeftBranch, tmFlags); | |
| 818 | + graph_finish(pGraph, pLeftBranch, tmFlags); | |
| 819 | 819 | if( pGraph->nErr ){ |
| 820 | 820 | graph_free(pGraph); |
| 821 | 821 | pGraph = 0; |
| 822 | 822 | }else{ |
| 823 | 823 | @ <tr class="timelineBottom" id="btm-%d(iTableId)">\ |
| @@ -1290,12 +1290,13 @@ | ||
| 1290 | 1290 | const char *zChng, /* The filename GLOB list */ |
| 1291 | 1291 | Blob *pSql /* The SELECT statement under construction */ |
| 1292 | 1292 | ){ |
| 1293 | 1293 | if( zChng==0 || zChng[0]==0 ) return; |
| 1294 | 1294 | 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)", | |
| 1297 | 1298 | glob_expr("filename.name", mprintf("\"%s\"", zChng))); |
| 1298 | 1299 | } |
| 1299 | 1300 | static void addFileGlobDescription( |
| 1300 | 1301 | const char *zChng, /* The filename GLOB list */ |
| 1301 | 1302 | Blob *pDescription /* Result description */ |
| @@ -1303,237 +1304,10 @@ | ||
| 1303 | 1304 | if( zChng==0 || zChng[0]==0 ) return; |
| 1304 | 1305 | blob_appendf(pDescription, " that include changes to files matching '%h'", |
| 1305 | 1306 | zChng); |
| 1306 | 1307 | } |
| 1307 | 1308 | |
| 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(®exp, 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 | 1309 | /* |
| 1536 | 1310 | ** Similar to fossil_expand_datetime() |
| 1537 | 1311 | ** |
| 1538 | 1312 | ** Add missing "-" characters into a date/time. Examples: |
| 1539 | 1313 | ** |
| @@ -1591,10 +1365,11 @@ | ||
| 1591 | 1365 | tagId = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", zEnd); |
| 1592 | 1366 | if( tagId==0 ){ |
| 1593 | 1367 | endId = symbolic_name_to_rid(zEnd, "ci"); |
| 1594 | 1368 | if( endId==0 ) return 0; |
| 1595 | 1369 | } |
| 1370 | + db_pause_dml_log(); | |
| 1596 | 1371 | if( bForward ){ |
| 1597 | 1372 | if( tagId ){ |
| 1598 | 1373 | db_prepare(&q, |
| 1599 | 1374 | "WITH RECURSIVE dx(id,mtime) AS (" |
| 1600 | 1375 | " SELECT %d, event.mtime FROM event WHERE objid=%d" |
| @@ -1658,12 +1433,54 @@ | ||
| 1658 | 1433 | } |
| 1659 | 1434 | if( db_step(&q)==SQLITE_ROW ){ |
| 1660 | 1435 | ans = db_column_int(&q, 0); |
| 1661 | 1436 | } |
| 1662 | 1437 | db_finalize(&q); |
| 1438 | + db_unpause_dml_log(); | |
| 1663 | 1439 | return ans; |
| 1664 | 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 | +} | |
| 1665 | 1482 | |
| 1666 | 1483 | /* |
| 1667 | 1484 | ** COMMAND: test-endpoint |
| 1668 | 1485 | ** |
| 1669 | 1486 | ** Usage: fossil test-endpoint BASE TAG ?OPTIONS? |
| @@ -1698,21 +1515,21 @@ | ||
| 1698 | 1515 | /* |
| 1699 | 1516 | ** WEBPAGE: timeline |
| 1700 | 1517 | ** |
| 1701 | 1518 | ** Query parameters: |
| 1702 | 1519 | ** |
| 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. | |
| 1705 | 1522 | ** c=TIMEORTAG Show events that happen "circa" TIMEORTAG |
| 1706 | 1523 | ** cf=FILEHASH Show events around the time of the first use of |
| 1707 | -** the file with FILEHASH | |
| 1524 | +** the file with FILEHASH. | |
| 1708 | 1525 | ** m=TIMEORTAG Highlight the event at TIMEORTAG, or the closest available |
| 1709 | 1526 | ** event if TIMEORTAG is not part of the timeline. If |
| 1710 | 1527 | ** the t= or r= is used, the m event is added to the timeline |
| 1711 | 1528 | ** 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. | |
| 1714 | 1531 | ** sel1=TIMEORTAG Highlight the check-in at TIMEORTAG if it is part of |
| 1715 | 1532 | ** the timeline. Similar to m= except TIMEORTAG must |
| 1716 | 1533 | ** match a check-in that is already in the timeline. |
| 1717 | 1534 | ** sel2=TIMEORTAG Like sel1= but use the secondary highlight. |
| 1718 | 1535 | ** n=COUNT Maximum number of events. "all" for no limit |
| @@ -1732,42 +1549,48 @@ | ||
| 1732 | 1549 | ** from=CX ... shortest path from CX back to CHECKIN |
| 1733 | 1550 | ** ft=CHECKIN "Forward To": Show decendents forward to CHECKIN |
| 1734 | 1551 | ** d=CX ... from CX up to the time of CHECKIN |
| 1735 | 1552 | ** from=CX ... shortest path from CX up to CHECKIN |
| 1736 | 1553 | ** 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. | |
| 1740 | 1559 | ** 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. | |
| 1742 | 1561 | ** 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". | |
| 1744 | 1564 | ** u=USER Only show items associated with USER |
| 1745 | 1565 | ** y=TYPE 'ci', 'w', 't', 'n', 'e', 'f', or 'all'. |
| 1746 | 1566 | ** ss=VIEWSTYLE c: "Compact", v: "Verbose", m: "Modern", j: "Columnar", |
| 1747 | -** x: "Classic". | |
| 1567 | +* x: "Classic". | |
| 1748 | 1568 | ** advm Use the "Advanced" or "Busy" menu design. |
| 1749 | 1569 | ** ng No Graph. |
| 1750 | 1570 | ** ncp Omit cherrypick merges |
| 1751 | 1571 | ** nd Do not highlight the focus check-in |
| 1752 | 1572 | ** nsm Omit the submenu |
| 1753 | 1573 | ** nc Omit all graph colors other than highlights |
| 1754 | 1574 | ** v Show details of files changed |
| 1755 | 1575 | ** 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... | |
| 1758 | 1578 | ** to=CHECKIN ... to this |
| 1759 | 1579 | ** to2=CHECKIN ... backup name if to= doesn't resolve |
| 1760 | 1580 | ** shortest ... show only the shortest path |
| 1761 | 1581 | ** rel ... also show related checkins |
| 1762 | 1582 | ** bt=PRIOR ... path from CHECKIN back to PRIOR |
| 1763 | 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 | |
| 1764 | 1587 | ** 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. | |
| 1767 | 1590 | ** 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 | |
| 1769 | 1592 | ** brbg Background color determined by branch name |
| 1770 | 1593 | ** ubg Background color determined by user |
| 1771 | 1594 | ** deltabg Background color red for delta manifests or green |
| 1772 | 1595 | ** for baseline manifests |
| 1773 | 1596 | ** namechng Show only check-ins that have filename changes |
| @@ -1784,11 +1607,11 @@ | ||
| 1784 | 1607 | ** datefmt=N Override the date format: 0=HH:MM, 1=HH:MM:SS, |
| 1785 | 1608 | ** 2=YYYY-MM-DD HH:MM:SS, 3=YYMMDD HH:MM, and 4 means "off". |
| 1786 | 1609 | ** bisect Show the check-ins that are in the current bisect |
| 1787 | 1610 | ** oldestfirst Show events oldest first. |
| 1788 | 1611 | ** showid Show RIDs |
| 1789 | -** showsql Show the SQL text | |
| 1612 | +** showsql Show the SQL used to generate the report | |
| 1790 | 1613 | ** |
| 1791 | 1614 | ** p= and d= can appear individually or together. If either p= or d= |
| 1792 | 1615 | ** appear, then u=, y=, a=, and b= are ignored. |
| 1793 | 1616 | ** |
| 1794 | 1617 | ** If both a= and b= appear then both upper and lower bounds are honored. |
| @@ -1862,14 +1685,20 @@ | ||
| 1862 | 1685 | int advancedMenu = 0; /* Use the advanced menu design */ |
| 1863 | 1686 | char *zPlural; /* Ending for plural forms */ |
| 1864 | 1687 | int showCherrypicks = 1; /* True to show cherrypick merges */ |
| 1865 | 1688 | int haveParameterN; /* True if n= query parameter present */ |
| 1866 | 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 */ | |
| 1867 | 1692 | |
| 1868 | 1693 | login_check_credentials(); |
| 1869 | 1694 | url_initialize(&url, "timeline"); |
| 1870 | 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; | |
| 1871 | 1700 | |
| 1872 | 1701 | (void)P_NoBot("ss") |
| 1873 | 1702 | /* "ss" is processed via the udc but at least one spider likes to |
| 1874 | 1703 | ** try to SQL inject via this argument, so let's catch that. */; |
| 1875 | 1704 | |
| @@ -1984,49 +1813,52 @@ | ||
| 1984 | 1813 | |
| 1985 | 1814 | /* Check for tl=TAGLIST and rl=TAGLIST which are abbreviations for |
| 1986 | 1815 | ** t=TAGLIST&ms=brlist and r=TAGLIST&ms=brlist repectively. */ |
| 1987 | 1816 | if( zBrName==0 && zTagName==0 ){ |
| 1988 | 1817 | const char *z; |
| 1818 | + const char *zPattern = 0; | |
| 1989 | 1819 | 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; | |
| 1992 | 1827 | } |
| 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 | + } | |
| 1997 | 1837 | } |
| 1998 | 1838 | } |
| 1999 | 1839 | |
| 2000 | 1840 | /* Convert r=TAG to t=TAG&rel in order to populate the UI style widgets. */ |
| 2001 | - if( zBrName && !related ){ | |
| 1841 | + if( zBrName ){ | |
| 2002 | 1842 | cgi_delete_query_parameter("r"); |
| 2003 | 1843 | cgi_set_query_parameter("t", zBrName); (void)P("t"); |
| 2004 | 1844 | cgi_set_query_parameter("rel", "1"); |
| 2005 | 1845 | zTagName = zBrName; |
| 2006 | - related = 1; | |
| 1846 | + if( related==0 ) related = 1; | |
| 2007 | 1847 | zType = "ci"; |
| 2008 | 1848 | } |
| 2009 | 1849 | |
| 2010 | 1850 | /* Ignore empty tag query strings. */ |
| 2011 | 1851 | if( zTagName && !*zTagName ){ |
| 2012 | 1852 | zTagName = 0; |
| 2013 | 1853 | } |
| 2014 | 1854 | |
| 2015 | 1855 | /* Finish preliminary processing of tag match queries. */ |
| 1856 | + matchStyle = match_style(zMatchStyle, MS_EXACT); | |
| 2016 | 1857 | if( zTagName ){ |
| 2017 | 1858 | 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 ){ | |
| 2028 | 1860 | /* For exact maching, inhibit links to the selected tag. */ |
| 2029 | 1861 | zThisTag = zTagName; |
| 2030 | 1862 | Th_Store("current_checkin", zTagName); |
| 2031 | 1863 | } |
| 2032 | 1864 | |
| @@ -2034,11 +1866,11 @@ | ||
| 2034 | 1866 | if( advancedMenu ){ |
| 2035 | 1867 | style_submenu_checkbox("rel", "Related", 0, 0); |
| 2036 | 1868 | } |
| 2037 | 1869 | |
| 2038 | 1870 | /* Construct the tag match expression. */ |
| 2039 | - zTagSql = tagMatchExpression(matchStyle, zTagName, &zMatchDesc, &zError); | |
| 1871 | + zTagSql = match_tag_sqlexpr(matchStyle, zTagName, &zMatchDesc, &zError); | |
| 2040 | 1872 | } |
| 2041 | 1873 | |
| 2042 | 1874 | if( zMark && zMark[0]==0 ){ |
| 2043 | 1875 | if( zAfter ) zMark = zAfter; |
| 2044 | 1876 | if( zBefore ) zMark = zBefore; |
| @@ -2082,10 +1914,11 @@ | ||
| 2082 | 1914 | } |
| 2083 | 1915 | if( PB("nc") ){ |
| 2084 | 1916 | tmFlags &= ~(TIMELINE_DELTA|TIMELINE_BRCOLOR|TIMELINE_UCOLOR); |
| 2085 | 1917 | tmFlags |= TIMELINE_NOCOLOR; |
| 2086 | 1918 | } |
| 1919 | + if( showSql ) db_append_dml_to_blob(&allSql); | |
| 2087 | 1920 | if( zUses!=0 ){ |
| 2088 | 1921 | int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses); |
| 2089 | 1922 | if( ufid ){ |
| 2090 | 1923 | zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid); |
| 2091 | 1924 | db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)"); |
| @@ -2099,42 +1932,42 @@ | ||
| 2099 | 1932 | } |
| 2100 | 1933 | if( renameOnly ){ |
| 2101 | 1934 | db_multi_exec( |
| 2102 | 1935 | "CREATE TEMP TABLE rnfile(rid INTEGER PRIMARY KEY);" |
| 2103 | 1936 | "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;" | |
| 2105 | 1938 | ); |
| 2106 | 1939 | disableY = 1; |
| 2107 | 1940 | } |
| 2108 | 1941 | if( forkOnly ){ |
| 2109 | 1942 | db_multi_exec( |
| 2110 | 1943 | "CREATE TEMP TABLE rnfork(rid INTEGER PRIMARY KEY);\n" |
| 2111 | 1944 | "INSERT OR IGNORE INTO rnfork(rid)\n" |
| 2112 | 1945 | " 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" | |
| 2116 | 1949 | " HAVING count(*)>1;\n" |
| 2117 | - "INSERT OR IGNORE INTO rnfork(rid)" | |
| 1950 | + "INSERT OR IGNORE INTO rnfork(rid)\n" | |
| 2118 | 1951 | " 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" | |
| 2122 | 1955 | " HAVING count(*)>1;\n", |
| 2123 | 1956 | TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH |
| 2124 | 1957 | ); |
| 2125 | 1958 | db_multi_exec( |
| 2126 | 1959 | "INSERT OR IGNORE INTO rnfork(rid)\n" |
| 2127 | 1960 | " 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" | |
| 2132 | 1965 | " 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", | |
| 2136 | 1969 | TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH |
| 2137 | 1970 | ); |
| 2138 | 1971 | tmFlags |= TIMELINE_UNHIDE; |
| 2139 | 1972 | zType = "ci"; |
| 2140 | 1973 | disableY = 1; |
| @@ -2207,77 +2040,114 @@ | ||
| 2207 | 2040 | PathNode *p = 0; |
| 2208 | 2041 | const char *zFrom = 0; |
| 2209 | 2042 | const char *zTo = 0; |
| 2210 | 2043 | Blob ins; |
| 2211 | 2044 | int nNodeOnPath = 0; |
| 2045 | + int commonAncs = 0; /* Common ancestors of me_rid and you_rid. */ | |
| 2046 | + int earlierRid = 0, laterRid = 0; | |
| 2212 | 2047 | |
| 2213 | 2048 | if( from_rid && to_rid ){ |
| 2214 | 2049 | if( from_to_mode==0 ){ |
| 2215 | 2050 | p = path_shortest(from_rid, to_rid, noMerge, 0, 0); |
| 2216 | 2051 | }else if( from_to_mode==1 ){ |
| 2217 | 2052 | p = path_shortest(from_rid, to_rid, 0, 1, 0); |
| 2053 | + earlierRid = commonAncs = from_rid; | |
| 2054 | + laterRid = to_rid; | |
| 2218 | 2055 | }else{ |
| 2219 | 2056 | p = path_shortest(to_rid, from_rid, 0, 1, 0); |
| 2057 | + earlierRid = commonAncs = to_rid; | |
| 2058 | + laterRid = from_rid; | |
| 2220 | 2059 | } |
| 2221 | 2060 | zFrom = P("from"); |
| 2222 | 2061 | zTo = zTo2 ? zTo2 : P("to"); |
| 2223 | 2062 | }else{ |
| 2224 | - if( path_common_ancestor(me_rid, you_rid) ){ | |
| 2063 | + commonAncs = path_common_ancestor(me_rid, you_rid); | |
| 2064 | + if( commonAncs!=0 ){ | |
| 2225 | 2065 | p = path_first(); |
| 2226 | 2066 | } |
| 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 | + } | |
| 2229 | 2078 | } |
| 2230 | 2079 | blob_init(&ins, 0, 0); |
| 2231 | 2080 | 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);" | |
| 2233 | 2082 | ); |
| 2234 | 2083 | if( p ){ |
| 2084 | + int cnt = 4; | |
| 2235 | 2085 | blob_init(&ins, 0, 0); |
| 2236 | 2086 | blob_append_sql(&ins, "INSERT INTO pathnode(x) VALUES(%d)", p->rid); |
| 2237 | 2087 | p = p->u.pTo; |
| 2238 | 2088 | 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 | + } | |
| 2240 | 2096 | p = p->u.pTo; |
| 2241 | 2097 | } |
| 2242 | 2098 | } |
| 2243 | 2099 | path_reset(); |
| 2244 | 2100 | db_multi_exec("%s", blob_str(&ins)/*safe-for-%s*/); |
| 2245 | 2101 | blob_reset(&ins); |
| 2246 | - if( related || P("mionly") ){ | |
| 2102 | + if( related ){ | |
| 2247 | 2103 | 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);" | |
| 2249 | 2105 | "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;" | |
| 2251 | 2107 | ); |
| 2252 | - if( P("mionly")==0 ){ | |
| 2108 | + if( related==1 ){ | |
| 2253 | 2109 | db_multi_exec( |
| 2254 | 2110 | "INSERT OR IGNORE INTO related(x)" |
| 2255 | - " SELECT cid FROM plink WHERE pid IN pathnode;" | |
| 2111 | + " SELECT cid FROM plink WHERE pid IN pathnode;" | |
| 2256 | 2112 | ); |
| 2257 | 2113 | } |
| 2258 | 2114 | if( showCherrypicks ){ |
| 2259 | 2115 | db_multi_exec( |
| 2260 | 2116 | "INSERT OR IGNORE INTO related(x)" |
| 2261 | - " SELECT parentid FROM cherrypick WHERE childid IN pathnode;" | |
| 2117 | + " SELECT parentid FROM cherrypick WHERE childid IN pathnode;" | |
| 2262 | 2118 | ); |
| 2263 | - if( P("mionly")==0 ){ | |
| 2119 | + if( related==1 ){ | |
| 2264 | 2120 | db_multi_exec( |
| 2265 | 2121 | "INSERT OR IGNORE INTO related(x)" |
| 2266 | - " SELECT childid FROM cherrypick WHERE parentid IN pathnode;" | |
| 2122 | + " SELECT childid FROM cherrypick WHERE parentid IN pathnode;" | |
| 2267 | 2123 | ); |
| 2268 | 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 | + ); | |
| 2269 | 2137 | } |
| 2270 | 2138 | db_multi_exec("INSERT OR IGNORE INTO pathnode SELECT x FROM related"); |
| 2271 | 2139 | } |
| 2140 | + add_extra_rids("pathnode",P("x")); | |
| 2272 | 2141 | blob_append_sql(&sql, " AND event.objid IN pathnode"); |
| 2273 | 2142 | if( zChng && zChng[0] ){ |
| 2274 | 2143 | 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)", | |
| 2279 | 2149 | glob_expr("filename.name", zChng) |
| 2280 | 2150 | ); |
| 2281 | 2151 | } |
| 2282 | 2152 | tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS; |
| 2283 | 2153 | db_multi_exec("%s", blob_sql_text(&sql)); |
| @@ -2331,18 +2201,18 @@ | ||
| 2331 | 2201 | if( !haveParameterN ) nEntry = 10; |
| 2332 | 2202 | } |
| 2333 | 2203 | db_multi_exec( |
| 2334 | 2204 | "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)" |
| 2335 | 2205 | ); |
| 2206 | + add_extra_rids("ok", P("x")); | |
| 2336 | 2207 | zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", |
| 2337 | 2208 | p_rid ? p_rid : d_rid); |
| 2338 | 2209 | zCiName = zDPName; |
| 2339 | 2210 | if( zCiName==0 ) zCiName = zUuid; |
| 2340 | 2211 | blob_append_sql(&sql, " AND event.objid IN ok"); |
| 2341 | 2212 | nd = 0; |
| 2342 | 2213 | if( d_rid ){ |
| 2343 | - Stmt s; | |
| 2344 | 2214 | double rStopTime = 9e99; |
| 2345 | 2215 | zFwdTo = P("ft"); |
| 2346 | 2216 | if( zFwdTo ){ |
| 2347 | 2217 | double rStartDate = db_double(0.0, |
| 2348 | 2218 | "SELECT mtime FROM event WHERE objid=%d", d_rid); |
| @@ -2354,27 +2224,25 @@ | ||
| 2354 | 2224 | if( !haveParameterN ) nEntry = 0; |
| 2355 | 2225 | rStopTime = db_double(9e99, |
| 2356 | 2226 | "SELECT mtime FROM event WHERE objid=%d", ridFwdTo); |
| 2357 | 2227 | } |
| 2358 | 2228 | } |
| 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" | |
| 2369 | 2241 | "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 | |
| 2371 | 2243 | ); |
| 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 | 2244 | nd = db_int(0, "SELECT count(*)-1 FROM ok"); |
| 2377 | 2245 | if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql)); |
| 2378 | 2246 | if( nd>0 || p_rid==0 ){ |
| 2379 | 2247 | blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s"); |
| 2380 | 2248 | } |
| @@ -2483,28 +2351,28 @@ | ||
| 2483 | 2351 | if( zChng && *zChng ){ |
| 2484 | 2352 | addFileGlobExclusion(zChng, &cond); |
| 2485 | 2353 | tmFlags |= TIMELINE_XMERGE; |
| 2486 | 2354 | } |
| 2487 | 2355 | if( zUses ){ |
| 2488 | - blob_append_sql(&cond, " AND event.objid IN usesfile "); | |
| 2356 | + blob_append_sql(&cond, " AND event.objid IN usesfile\n"); | |
| 2489 | 2357 | } |
| 2490 | 2358 | if( renameOnly ){ |
| 2491 | - blob_append_sql(&cond, " AND event.objid IN rnfile "); | |
| 2359 | + blob_append_sql(&cond, " AND event.objid IN rnfile\n"); | |
| 2492 | 2360 | } |
| 2493 | 2361 | if( forkOnly ){ |
| 2494 | - blob_append_sql(&cond, " AND event.objid IN rnfork "); | |
| 2362 | + blob_append_sql(&cond, " AND event.objid IN rnfork\n"); | |
| 2495 | 2363 | } |
| 2496 | 2364 | if( cpOnly && showCherrypicks ){ |
| 2497 | 2365 | db_multi_exec( |
| 2498 | 2366 | "CREATE TEMP TABLE IF NOT EXISTS cpnodes(rid INTEGER PRIMARY KEY);" |
| 2499 | 2367 | "INSERT OR IGNORE INTO cpnodes SELECT childid FROM cherrypick;" |
| 2500 | 2368 | "INSERT OR IGNORE INTO cpnodes SELECT parentid FROM cherrypick;" |
| 2501 | 2369 | ); |
| 2502 | - blob_append_sql(&cond, " AND event.objid IN cpnodes "); | |
| 2370 | + blob_append_sql(&cond, " AND event.objid IN cpnodes\n"); | |
| 2503 | 2371 | } |
| 2504 | 2372 | 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"); | |
| 2506 | 2374 | } |
| 2507 | 2375 | if( zYearMonth ){ |
| 2508 | 2376 | char *zNext; |
| 2509 | 2377 | zYearMonth = timeline_expand_datetime(zYearMonth); |
| 2510 | 2378 | if( strlen(zYearMonth)>7 ){ |
| @@ -2663,39 +2531,24 @@ | ||
| 2663 | 2531 | nEntry = -1; |
| 2664 | 2532 | } |
| 2665 | 2533 | if( zTagSql ){ |
| 2666 | 2534 | db_multi_exec( |
| 2667 | 2535 | "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*/ | |
| 2671 | 2540 | ); |
| 2672 | 2541 | if( zMark ){ |
| 2673 | 2542 | /* If the t=release option is used with m=UUID, then also |
| 2674 | 2543 | ** include the UUID check-in in the display list */ |
| 2675 | 2544 | int ridMark = name_to_rid(zMark); |
| 2676 | 2545 | db_multi_exec( |
| 2677 | 2546 | "INSERT OR IGNORE INTO selected_nodes(rid) VALUES(%d)", ridMark); |
| 2678 | 2547 | } |
| 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 ){ | |
| 2697 | 2550 | blob_append_sql(&cond, " AND blob.rid IN selected_nodes"); |
| 2698 | 2551 | }else{ |
| 2699 | 2552 | db_multi_exec( |
| 2700 | 2553 | "CREATE TEMP TABLE related_nodes(rid INTEGER PRIMARY KEY);" |
| 2701 | 2554 | "INSERT INTO related_nodes SELECT rid FROM selected_nodes;" |
| @@ -2706,40 +2559,42 @@ | ||
| 2706 | 2559 | ** branch to be included in the report. These related check-ins are |
| 2707 | 2560 | ** useful in helping to visualize what has happened on a quiescent |
| 2708 | 2561 | ** branch that is infrequently merged with a much more activate branch. |
| 2709 | 2562 | */ |
| 2710 | 2563 | 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;" | |
| 2714 | 2567 | ); |
| 2715 | - if( P("mionly")==0 ){ | |
| 2568 | + if( related==1 ){ | |
| 2716 | 2569 | 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;" | |
| 2720 | 2573 | ); |
| 2721 | 2574 | if( showCherrypicks ){ |
| 2722 | 2575 | 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;" | |
| 2726 | 2579 | ); |
| 2727 | 2580 | } |
| 2728 | 2581 | } |
| 2729 | 2582 | if( showCherrypicks ){ |
| 2730 | 2583 | 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;" | |
| 2734 | 2587 | ); |
| 2735 | 2588 | } |
| 2736 | 2589 | if( (tmFlags & TIMELINE_UNHIDE)==0 ){ |
| 2737 | 2590 | 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)", | |
| 2741 | 2596 | TAG_HIDDEN |
| 2742 | 2597 | ); |
| 2743 | 2598 | } |
| 2744 | 2599 | } |
| 2745 | 2600 | } |
| @@ -2829,45 +2684,42 @@ | ||
| 2829 | 2684 | rCirca = symbolic_name_to_mtime(zCirca, &zCirca); |
| 2830 | 2685 | blob_append_sql(&sql, "%s", blob_sql_text(&cond)); |
| 2831 | 2686 | if( rAfter>0.0 ){ |
| 2832 | 2687 | if( rBefore>0.0 ){ |
| 2833 | 2688 | blob_append_sql(&sql, |
| 2834 | - " AND event.mtime>=%.17g AND event.mtime<=%.17g" | |
| 2689 | + " AND event.mtime>=%.17g AND event.mtime<=%.17g\n" | |
| 2835 | 2690 | " ORDER BY event.mtime ASC", rAfter-ONE_SECOND, rBefore+ONE_SECOND); |
| 2836 | 2691 | nEntry = -1; |
| 2837 | 2692 | }else{ |
| 2838 | 2693 | 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", | |
| 2840 | 2695 | rAfter-ONE_SECOND); |
| 2841 | 2696 | } |
| 2842 | 2697 | zCirca = 0; |
| 2843 | 2698 | url_add_parameter(&url, "c", 0); |
| 2844 | 2699 | }else if( rBefore>0.0 ){ |
| 2845 | 2700 | 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", | |
| 2847 | 2702 | rBefore+ONE_SECOND); |
| 2848 | 2703 | zCirca = 0; |
| 2849 | 2704 | url_add_parameter(&url, "c", 0); |
| 2850 | 2705 | }else if( rCirca>0.0 ){ |
| 2851 | 2706 | Blob sql2; |
| 2852 | 2707 | blob_init(&sql2, blob_sql_text(&sql), -1); |
| 2853 | 2708 | 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); | |
| 2855 | 2710 | if( nEntry>0 ){ |
| 2856 | 2711 | blob_append_sql(&sql2," LIMIT %d", (nEntry+1)/2); |
| 2857 | 2712 | } |
| 2858 | - if( PB("showsql") ){ | |
| 2859 | - @ <pre>%h(blob_sql_text(&sql2))</pre> | |
| 2860 | - } | |
| 2861 | 2713 | db_multi_exec("%s", blob_sql_text(&sql2)); |
| 2862 | 2714 | if( nEntry>0 ){ |
| 2863 | 2715 | nEntry -= db_int(0,"select count(*) from timeline"); |
| 2864 | 2716 | if( nEntry<=0 ) nEntry = 1; |
| 2865 | 2717 | } |
| 2866 | 2718 | blob_reset(&sql2); |
| 2867 | 2719 | 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", | |
| 2869 | 2721 | rCirca |
| 2870 | 2722 | ); |
| 2871 | 2723 | if( zMark==0 ) zMark = zCirca; |
| 2872 | 2724 | }else{ |
| 2873 | 2725 | blob_append_sql(&sql, " ORDER BY event.mtime DESC"); |
| @@ -3010,12 +2862,14 @@ | ||
| 3010 | 2862 | style_submenu_multichoice("ms", count(azMatchStyles)/2,azMatchStyles,0); |
| 3011 | 2863 | } |
| 3012 | 2864 | } |
| 3013 | 2865 | blob_zero(&cond); |
| 3014 | 2866 | } |
| 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); | |
| 3017 | 2871 | } |
| 3018 | 2872 | if( search_restrict(SRCH_CKIN)!=0 ){ |
| 3019 | 2873 | style_submenu_element("Search", "%R/search?y=c"); |
| 3020 | 2874 | } |
| 3021 | 2875 | if( advancedMenu ){ |
| @@ -3069,18 +2923,36 @@ | ||
| 3069 | 2923 | if( zNewerButton ){ |
| 3070 | 2924 | @ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\ |
| 3071 | 2925 | @ ↑</a> |
| 3072 | 2926 | } |
| 3073 | 2927 | 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 | + } | |
| 3076 | 2946 | db_finalize(&q); |
| 3077 | 2947 | if( zOlderButton ){ |
| 3078 | 2948 | @ %z(chref("button","%s",zOlderButton))%h(zOlderButtonLabel)\ |
| 3079 | 2949 | @ ↓</a> |
| 3080 | 2950 | } |
| 3081 | 2951 | document_emit_js(/*handles pikchrs rendered above*/); |
| 2952 | + blob_reset(&sql); | |
| 2953 | + blob_reset(&desc); | |
| 3082 | 2954 | style_finish_page(); |
| 3083 | 2955 | } |
| 3084 | 2956 | |
| 3085 | 2957 | /* |
| 3086 | 2958 | ** Translate a timeline entry into the printable format by |
| 3087 | 2959 |
| --- 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(®exp, 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 | @ ↑</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 | @ ↓</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 | @ ↑</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 | @ ↓</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 @@ | ||
| 514 | 514 | zDir = zNext; |
| 515 | 515 | } |
| 516 | 516 | } |
| 517 | 517 | } |
| 518 | 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 | 519 | if( nameChng ){ |
| 524 | 520 | fossil_print("MERGE %s -> %s\n", zName, zNewName); |
| 525 | 521 | }else{ |
| 526 | 522 | fossil_print("MERGE %s\n", zName); |
| 527 | 523 | } |
| @@ -528,10 +524,13 @@ | ||
| 528 | 524 | if( islinkv || islinkt ){ |
| 529 | 525 | fossil_print("***** Cannot merge symlink %s\n", zNewName); |
| 530 | 526 | zOp = "CONFLICT"; |
| 531 | 527 | nConflict++; |
| 532 | 528 | }else{ |
| 529 | + /* Merge the changes in the current tree into the target version */ | |
| 530 | + Blob r, t, v; | |
| 531 | + int rc; | |
| 533 | 532 | unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0; |
| 534 | 533 | if(keepMergeFlag!=0) mergeFlags |= MERGE_KEEP_FILES; |
| 535 | 534 | if( !dryRunFlag && !internalUpdate ) undo_save(zName); |
| 536 | 535 | content_get(ridt, &t); |
| 537 | 536 | content_get(ridv, &v); |
| @@ -571,15 +570,15 @@ | ||
| 571 | 570 | nConflict++; |
| 572 | 571 | zOp = "ERROR"; |
| 573 | 572 | zErrMsg = "cannot merge binary file"; |
| 574 | 573 | nc = 1; |
| 575 | 574 | } |
| 575 | + blob_reset(&v); | |
| 576 | + blob_reset(&t); | |
| 577 | + blob_reset(&r); | |
| 576 | 578 | } |
| 577 | 579 | if( nameChng && !dryRunFlag ) file_delete(zFullPath); |
| 578 | - blob_reset(&v); | |
| 579 | - blob_reset(&t); | |
| 580 | - blob_reset(&r); | |
| 581 | 580 | }else{ |
| 582 | 581 | nUpdate--; |
| 583 | 582 | if( chnged ){ |
| 584 | 583 | if( verboseFlag ) fossil_print("EDITED %s\n", zName); |
| 585 | 584 | }else{ |
| 586 | 585 |
| --- 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 @@ | ||
| 1041 | 1041 | } |
| 1042 | 1042 | db_finalize(&q); |
| 1043 | 1043 | if( cnt==0 ) pXfer->resync = 0; |
| 1044 | 1044 | return cnt; |
| 1045 | 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 | +} | |
| 1046 | 1079 | |
| 1047 | 1080 | /* |
| 1048 | 1081 | ** Send an igot message for every artifact. |
| 1049 | 1082 | */ |
| 1050 | 1083 | static void send_all(Xfer *pXfer){ |
| @@ -1781,10 +1814,19 @@ | ||
| 1781 | 1814 | ** The client sends this message to the server to ask the server |
| 1782 | 1815 | ** to tell it about alternative repositories in the reply. |
| 1783 | 1816 | */ |
| 1784 | 1817 | if( blob_eq(&xfer.aToken[1], "req-links") ){ |
| 1785 | 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); | |
| 1786 | 1828 | } |
| 1787 | 1829 | |
| 1788 | 1830 | }else |
| 1789 | 1831 | |
| 1790 | 1832 | /* Unknown message |
| @@ -1981,11 +2023,11 @@ | ||
| 1981 | 2023 | int nCardSent = 0; /* Number of cards sent */ |
| 1982 | 2024 | int nCardRcvd = 0; /* Number of cards received */ |
| 1983 | 2025 | int nCycle = 0; /* Number of round trips to the server */ |
| 1984 | 2026 | int size; /* Size of a config value or uvfile */ |
| 1985 | 2027 | int origConfigRcvMask; /* Original value of configRcvMask */ |
| 1986 | - int nFileRecv; /* Number of files received */ | |
| 2028 | + int nFileRecv = 0; /* Number of files received */ | |
| 1987 | 2029 | int mxPhantomReq = 200; /* Max number of phantoms to request per comm */ |
| 1988 | 2030 | const char *zCookie; /* Server cookie */ |
| 1989 | 2031 | i64 nUncSent, nUncRcvd; /* Bytes sent and received (before compression) */ |
| 1990 | 2032 | i64 nSent, nRcvd; /* Bytes sent and received (after compression) */ |
| 1991 | 2033 | int cloneSeqno = 1; /* Sequence number for clones */ |
| @@ -2007,10 +2049,11 @@ | ||
| 2007 | 2049 | int uvHashSent = 0; /* The "pragma uv-hash" message has been sent */ |
| 2008 | 2050 | int uvDoPush = 0; /* Generate uvfile messages to send to server */ |
| 2009 | 2051 | int uvPullOnly = 0; /* 1: pull-only. 2: pull-only warning issued */ |
| 2010 | 2052 | int nUvGimmeSent = 0; /* Number of uvgimme cards sent on this cycle */ |
| 2011 | 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 */ | |
| 2012 | 2055 | sqlite3_int64 mtime; /* Modification time on a UV file */ |
| 2013 | 2056 | int autopushFailed = 0; /* Autopush following commit failed if true */ |
| 2014 | 2057 | const char *zCkinLock; /* Name of check-in to lock. NULL for none */ |
| 2015 | 2058 | const char *zClientId; /* A unique identifier for this check-out */ |
| 2016 | 2059 | unsigned int mHttpFlags;/* Flags for the http_exchange() subsystem */ |
| @@ -2181,15 +2224,21 @@ | ||
| 2181 | 2224 | */ |
| 2182 | 2225 | if( (syncFlags & SYNC_PULL)!=0 |
| 2183 | 2226 | || ((syncFlags & SYNC_CLONE)!=0 && cloneSeqno==1) |
| 2184 | 2227 | ){ |
| 2185 | 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 | + } | |
| 2186 | 2232 | } |
| 2187 | 2233 | if( syncFlags & SYNC_PUSH ){ |
| 2188 | 2234 | send_unsent(&xfer); |
| 2189 | 2235 | nCardSent += send_unclustered(&xfer); |
| 2190 | 2236 | if( syncFlags & SYNC_PRIVATE ) send_private(&xfer); |
| 2237 | + if( nGimmeRcvd>0 && nCycle==2 ){ | |
| 2238 | + send_all_clusters(&xfer); | |
| 2239 | + } | |
| 2191 | 2240 | } |
| 2192 | 2241 | |
| 2193 | 2242 | /* Client sends configuration parameter requests. On a clone, delay sending |
| 2194 | 2243 | ** this until the second cycle since the login card might fail on |
| 2195 | 2244 | ** the first cycle. |
| @@ -2369,10 +2418,11 @@ | ||
| 2369 | 2418 | nCardSent++; |
| 2370 | 2419 | } |
| 2371 | 2420 | go = 0; |
| 2372 | 2421 | nUvGimmeSent = 0; |
| 2373 | 2422 | nUvFileRcvd = 0; |
| 2423 | + nGimmeRcvd = 0; | |
| 2374 | 2424 | nPriorArtifact = nArtifactRcvd; |
| 2375 | 2425 | |
| 2376 | 2426 | /* Process the reply that came back from the server */ |
| 2377 | 2427 | while( blob_line(&recv, &xfer.line) ){ |
| 2378 | 2428 | if( blob_buffer(&xfer.line)[0]=='#' ){ |
| @@ -2449,11 +2499,14 @@ | ||
| 2449 | 2499 | && blob_is_hname(&xfer.aToken[1]) |
| 2450 | 2500 | ){ |
| 2451 | 2501 | remote_unk(&xfer.aToken[1]); |
| 2452 | 2502 | if( syncFlags & SYNC_PUSH ){ |
| 2453 | 2503 | 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 | + } | |
| 2455 | 2508 | } |
| 2456 | 2509 | }else |
| 2457 | 2510 | |
| 2458 | 2511 | /* igot HASH ?PRIVATEFLAG? |
| 2459 | 2512 | ** |
| 2460 | 2513 |
| --- 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 |
+25
| --- test/merge1.test | ||
| +++ test/merge1.test | ||
| @@ -75,10 +75,12 @@ | ||
| 75 | 75 | 555 - we think it well and other stuff too - 5555 |
| 76 | 76 | } |
| 77 | 77 | write_file_indented t23 { |
| 78 | 78 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1) |
| 79 | 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 | |
| 80 | 82 | ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1) |
| 81 | 83 | 111 - This is line one of the demo program - 1111 |
| 82 | 84 | ======= MERGED IN content follows =============================== (line 1) |
| 83 | 85 | 111 - This is line one OF the demo program - 1111 |
| 84 | 86 | >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
| @@ -88,10 +90,12 @@ | ||
| 88 | 90 | 555 - we think it well and other stuff too - 5555 |
| 89 | 91 | } |
| 90 | 92 | write_file_indented t32 { |
| 91 | 93 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1) |
| 92 | 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 | |
| 93 | 97 | ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1) |
| 94 | 98 | 111 - This is line one of the demo program - 1111 |
| 95 | 99 | ======= MERGED IN content follows =============================== (line 1) |
| 96 | 100 | 111 - This is line ONE of the demo program - 1111 |
| 97 | 101 | >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> |
| @@ -159,10 +163,13 @@ | ||
| 159 | 163 | 444 - If all goes well, we will be pleased - 4444 |
| 160 | 164 | 555 - we think it well and other stuff too - 5555 |
| 161 | 165 | } |
| 162 | 166 | write_file_indented t32 { |
| 163 | 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 | |
| 164 | 171 | ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1) |
| 165 | 172 | 111 - This is line one of the demo program - 1111 |
| 166 | 173 | ======= MERGED IN content follows =============================== (line 1) |
| 167 | 174 | 000 - Zero lines added to the beginning of - 0000 |
| 168 | 175 | 111 - This is line one of the demo program - 1111 |
| @@ -305,10 +312,19 @@ | ||
| 305 | 312 | mnop 2 |
| 306 | 313 | qrst |
| 307 | 314 | uvwx |
| 308 | 315 | yzAB 2 |
| 309 | 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 | |
| 310 | 326 | GHIJ 2 |
| 311 | 327 | ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2) |
| 312 | 328 | efgh |
| 313 | 329 | ijkl |
| 314 | 330 | mnop |
| @@ -372,10 +388,19 @@ | ||
| 372 | 388 | ijkl 2 |
| 373 | 389 | mnop |
| 374 | 390 | qrst |
| 375 | 391 | uvwx |
| 376 | 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 | |
| 377 | 402 | CDEF 2 |
| 378 | 403 | GHIJ 2 |
| 379 | 404 | ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2) |
| 380 | 405 | efgh |
| 381 | 406 | ijkl |
| 382 | 407 |
| --- 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 @@ | ||
| 27 | 27 | fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args |
| 28 | 28 | set x [read_file t4] |
| 29 | 29 | regsub -all \ |
| 30 | 30 | {<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <+ \(line \d+\)} \ |
| 31 | 31 | $x {MINE:} x |
| 32 | + regsub -all \ | |
| 33 | + {####### SUGGESTED CONFLICT RESOLUTION follows #+} \ | |
| 34 | + $x {BOT:} x | |
| 32 | 35 | regsub -all \ |
| 33 | 36 | {\|\|\|\|\|\|\| COMMON ANCESTOR content follows \|+ \(line \d+\)} \ |
| 34 | 37 | $x {COM:} x |
| 35 | 38 | regsub -all \ |
| 36 | 39 | {======= MERGED IN content follows =+ \(line \d+\)} \ |
| @@ -73,56 +76,56 @@ | ||
| 73 | 76 | } { |
| 74 | 77 | 1 2 3b 4b 5b 6 7 8 9 |
| 75 | 78 | } { |
| 76 | 79 | 1 2 3 4 5c 6 7 8 9 |
| 77 | 80 | } { |
| 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 | |
| 79 | 82 | } -expectError |
| 80 | 83 | merge-test 4 { |
| 81 | 84 | 1 2 3 4 5 6 7 8 9 |
| 82 | 85 | } { |
| 83 | 86 | 1 2 3b 4b 5b 6b 7 8 9 |
| 84 | 87 | } { |
| 85 | 88 | 1 2 3 4 5c 6 7 8 9 |
| 86 | 89 | } { |
| 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 | |
| 88 | 91 | } -expectError |
| 89 | 92 | merge-test 5 { |
| 90 | 93 | 1 2 3 4 5 6 7 8 9 |
| 91 | 94 | } { |
| 92 | 95 | 1 2 3b 4b 5b 6b 7 8 9 |
| 93 | 96 | } { |
| 94 | 97 | 1 2 3 4 5c 6c 7c 8 9 |
| 95 | 98 | } { |
| 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 | |
| 97 | 100 | } -expectError |
| 98 | 101 | merge-test 6 { |
| 99 | 102 | 1 2 3 4 5 6 7 8 9 |
| 100 | 103 | } { |
| 101 | 104 | 1 2 3b 4b 5b 6b 7 8b 9 |
| 102 | 105 | } { |
| 103 | 106 | 1 2 3 4 5c 6c 7c 8 9 |
| 104 | 107 | } { |
| 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 | |
| 106 | 109 | } -expectError |
| 107 | 110 | merge-test 7 { |
| 108 | 111 | 1 2 3 4 5 6 7 8 9 |
| 109 | 112 | } { |
| 110 | 113 | 1 2 3b 4b 5b 6b 7 8b 9 |
| 111 | 114 | } { |
| 112 | 115 | 1 2 3 4 5c 6c 7c 8c 9 |
| 113 | 116 | } { |
| 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 | |
| 115 | 118 | } -expectError |
| 116 | 119 | merge-test 8 { |
| 117 | 120 | 1 2 3 4 5 6 7 8 9 |
| 118 | 121 | } { |
| 119 | 122 | 1 2 3b 4b 5b 6b 7 8b 9b |
| 120 | 123 | } { |
| 121 | 124 | 1 2 3 4 5c 6c 7c 8c 9 |
| 122 | 125 | } { |
| 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 | |
| 124 | 127 | } -expectError |
| 125 | 128 | merge-test 9 { |
| 126 | 129 | 1 2 3 4 5 6 7 8 9 |
| 127 | 130 | } { |
| 128 | 131 | 1 2 3b 4b 5 6 7 8b 9b |
| @@ -146,11 +149,11 @@ | ||
| 146 | 149 | } { |
| 147 | 150 | 1 2 3b 4b 5 6 7 8b 9b |
| 148 | 151 | } { |
| 149 | 152 | 1 2 3b 4c 5 6c 7c 8 9 |
| 150 | 153 | } { |
| 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 | |
| 152 | 155 | } -expectError |
| 153 | 156 | merge-test 12 { |
| 154 | 157 | 1 2 3 4 5 6 7 8 9 |
| 155 | 158 | } { |
| 156 | 159 | 1 2 3b4b 5 6 7 8b 9b |
| @@ -201,20 +204,20 @@ | ||
| 201 | 204 | } { |
| 202 | 205 | 1 6 7 8 9 |
| 203 | 206 | } { |
| 204 | 207 | 1 2 3 4 9 |
| 205 | 208 | } { |
| 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 | |
| 207 | 210 | } -expectError |
| 208 | 211 | merge-test 25 { |
| 209 | 212 | 1 2 3 4 5 6 7 8 9 |
| 210 | 213 | } { |
| 211 | 214 | 1 7 8 9 |
| 212 | 215 | } { |
| 213 | 216 | 1 2 3 9 |
| 214 | 217 | } { |
| 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 | |
| 216 | 219 | } -expectError |
| 217 | 220 | |
| 218 | 221 | merge-test 30 { |
| 219 | 222 | 1 2 3 4 5 6 7 8 9 |
| 220 | 223 | } { |
| @@ -256,20 +259,20 @@ | ||
| 256 | 259 | } { |
| 257 | 260 | 1 2 3 4 9 |
| 258 | 261 | } { |
| 259 | 262 | 1 6 7 8 9 |
| 260 | 263 | } { |
| 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 | |
| 262 | 265 | } -expectError |
| 263 | 266 | merge-test 35 { |
| 264 | 267 | 1 2 3 4 5 6 7 8 9 |
| 265 | 268 | } { |
| 266 | 269 | 1 2 3 9 |
| 267 | 270 | } { |
| 268 | 271 | 1 7 8 9 |
| 269 | 272 | } { |
| 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 | |
| 271 | 274 | } -expectError |
| 272 | 275 | |
| 273 | 276 | merge-test 40 { |
| 274 | 277 | 2 3 4 5 6 7 8 |
| 275 | 278 | } { |
| @@ -311,20 +314,20 @@ | ||
| 311 | 314 | } { |
| 312 | 315 | 6 7 8 |
| 313 | 316 | } { |
| 314 | 317 | 2 3 4 |
| 315 | 318 | } { |
| 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 | |
| 317 | 320 | } -expectError |
| 318 | 321 | merge-test 45 { |
| 319 | 322 | 2 3 4 5 6 7 8 |
| 320 | 323 | } { |
| 321 | 324 | 7 8 |
| 322 | 325 | } { |
| 323 | 326 | 2 3 |
| 324 | 327 | } { |
| 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 | |
| 326 | 329 | } -expectError |
| 327 | 330 | |
| 328 | 331 | merge-test 50 { |
| 329 | 332 | 2 3 4 5 6 7 8 |
| 330 | 333 | } { |
| @@ -365,20 +368,20 @@ | ||
| 365 | 368 | } { |
| 366 | 369 | 2 3 4 |
| 367 | 370 | } { |
| 368 | 371 | 6 7 8 |
| 369 | 372 | } { |
| 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 | |
| 371 | 374 | } -expectError |
| 372 | 375 | merge-test 55 { |
| 373 | 376 | 2 3 4 5 6 7 8 |
| 374 | 377 | } { |
| 375 | 378 | 2 3 |
| 376 | 379 | } { |
| 377 | 380 | 7 8 |
| 378 | 381 | } { |
| 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 | |
| 380 | 383 | } -expectError |
| 381 | 384 | |
| 382 | 385 | merge-test 60 { |
| 383 | 386 | 1 2 3 4 5 6 7 8 9 |
| 384 | 387 | } { |
| @@ -420,20 +423,20 @@ | ||
| 420 | 423 | } { |
| 421 | 424 | 1 2b 3b 4b 5b 6 7 8 9 |
| 422 | 425 | } { |
| 423 | 426 | 1 2 3 4 9 |
| 424 | 427 | } { |
| 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 | |
| 426 | 429 | } -expectError |
| 427 | 430 | merge-test 65 { |
| 428 | 431 | 1 2 3 4 5 6 7 8 9 |
| 429 | 432 | } { |
| 430 | 433 | 1 2b 3b 4b 5b 6b 7 8 9 |
| 431 | 434 | } { |
| 432 | 435 | 1 2 3 9 |
| 433 | 436 | } { |
| 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 | |
| 435 | 438 | } -expectError |
| 436 | 439 | |
| 437 | 440 | merge-test 70 { |
| 438 | 441 | 1 2 3 4 5 6 7 8 9 |
| 439 | 442 | } { |
| @@ -475,20 +478,20 @@ | ||
| 475 | 478 | } { |
| 476 | 479 | 1 2 3 4 9 |
| 477 | 480 | } { |
| 478 | 481 | 1 2b 3b 4b 5b 6 7 8 9 |
| 479 | 482 | } { |
| 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 | |
| 481 | 484 | } -expectError |
| 482 | 485 | merge-test 75 { |
| 483 | 486 | 1 2 3 4 5 6 7 8 9 |
| 484 | 487 | } { |
| 485 | 488 | 1 2 3 9 |
| 486 | 489 | } { |
| 487 | 490 | 1 2b 3b 4b 5b 6b 7 8 9 |
| 488 | 491 | } { |
| 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 | |
| 490 | 493 | } -expectError |
| 491 | 494 | |
| 492 | 495 | merge-test 80 { |
| 493 | 496 | 2 3 4 5 6 7 8 |
| 494 | 497 | } { |
| @@ -530,20 +533,20 @@ | ||
| 530 | 533 | } { |
| 531 | 534 | 2b 3b 4b 5b 6 7 8 |
| 532 | 535 | } { |
| 533 | 536 | 2 3 4 |
| 534 | 537 | } { |
| 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 | |
| 536 | 539 | } -expectError |
| 537 | 540 | merge-test 85 { |
| 538 | 541 | 2 3 4 5 6 7 8 |
| 539 | 542 | } { |
| 540 | 543 | 2b 3b 4b 5b 6b 7 8 |
| 541 | 544 | } { |
| 542 | 545 | 2 3 |
| 543 | 546 | } { |
| 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 | |
| 545 | 548 | } -expectError |
| 546 | 549 | |
| 547 | 550 | merge-test 90 { |
| 548 | 551 | 2 3 4 5 6 7 8 |
| 549 | 552 | } { |
| @@ -585,20 +588,20 @@ | ||
| 585 | 588 | } { |
| 586 | 589 | 2 3 4 |
| 587 | 590 | } { |
| 588 | 591 | 2b 3b 4b 5b 6 7 8 |
| 589 | 592 | } { |
| 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 | |
| 591 | 594 | } -expectError |
| 592 | 595 | merge-test 95 { |
| 593 | 596 | 2 3 4 5 6 7 8 |
| 594 | 597 | } { |
| 595 | 598 | 2 3 |
| 596 | 599 | } { |
| 597 | 600 | 2b 3b 4b 5b 6b 7 8 |
| 598 | 601 | } { |
| 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 | |
| 600 | 603 | } -expectError |
| 601 | 604 | |
| 602 | 605 | merge-test 100 { |
| 603 | 606 | 1 2 3 4 5 6 7 8 9 |
| 604 | 607 | } { |
| @@ -631,20 +634,20 @@ | ||
| 631 | 634 | } { |
| 632 | 635 | 1 2 3 4 5 7 8 9b |
| 633 | 636 | } { |
| 634 | 637 | 1 2 3 4 5 7 8 9b a b c d e |
| 635 | 638 | } { |
| 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 | |
| 637 | 640 | } -expectError |
| 638 | 641 | merge-test 104 { |
| 639 | 642 | 1 2 3 4 5 6 7 8 9 |
| 640 | 643 | } { |
| 641 | 644 | 1 2 3 4 5 7 8 9b a b c d e |
| 642 | 645 | } { |
| 643 | 646 | 1 2 3 4 5 7 8 9b |
| 644 | 647 | } { |
| 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 | |
| 646 | 649 | } -expectError |
| 647 | 650 | |
| 648 | 651 | ############################################################################### |
| 649 | 652 | |
| 650 | 653 | test_cleanup |
| 651 | 654 |
| --- 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 |
+6
-4
| --- test/merge4.test | ||
| +++ test/merge4.test | ||
| @@ -26,15 +26,17 @@ | ||
| 26 | 26 | write_file t3 [join [string trim $v2] \n]\n |
| 27 | 27 | fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args |
| 28 | 28 | fossil 3-way-merge t1 t3 t2 t5 {*}$fossil_args |
| 29 | 29 | set x [read_file t4] |
| 30 | 30 | regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $x {>} x |
| 31 | + regsub -all {####### SUGGESTED CONFLICT RESOLUTION.*##} $x {#} x | |
| 31 | 32 | regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $x {=} x |
| 32 | 33 | regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $x {<} x |
| 33 | 34 | set x [split [string trim $x] \n] |
| 34 | 35 | set y [read_file t5] |
| 35 | 36 | regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $y {>} y |
| 37 | + regsub -all {####### SUGGESTED CONFLICT RESOLUTION.*##} $y {#} y | |
| 36 | 38 | regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $y {=} y |
| 37 | 39 | regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $y {<} y |
| 38 | 40 | set y [split [string trim $y] \n] |
| 39 | 41 | set result1 [string trim $result1] |
| 40 | 42 | if {$x!=$result1} { |
| @@ -58,13 +60,13 @@ | ||
| 58 | 60 | } { |
| 59 | 61 | 1 2b 3b 4b 5 6b 7b 8b 9 |
| 60 | 62 | } { |
| 61 | 63 | 1 2 3 4c 5c 6c 7 8 9 |
| 62 | 64 | } { |
| 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 | |
| 64 | 66 | } { |
| 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 | |
| 66 | 68 | } -expectError |
| 67 | 69 | merge-test 1001 { |
| 68 | 70 | 1 2 3 4 5 6 7 8 9 |
| 69 | 71 | } { |
| 70 | 72 | 1 2b 3b 4 5 6 7b 8b 9 |
| @@ -80,13 +82,13 @@ | ||
| 80 | 82 | } { |
| 81 | 83 | 2b 3b 4b 5 6b 7b 8b |
| 82 | 84 | } { |
| 83 | 85 | 2 3 4c 5c 6c 7 8 |
| 84 | 86 | } { |
| 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 < | |
| 86 | 88 | } { |
| 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 < | |
| 88 | 90 | } -expectError |
| 89 | 91 | merge-test 1003 { |
| 90 | 92 | 2 3 4 5 6 7 8 |
| 91 | 93 | } { |
| 92 | 94 | 2b 3b 4 5 6 7b 8b |
| 93 | 95 |
| --- 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 |
+1
| --- test/tester.tcl | ||
| +++ test/tester.tcl | ||
| @@ -332,10 +332,11 @@ | ||
| 332 | 332 | encoding-glob \ |
| 333 | 333 | exec-rel-paths \ |
| 334 | 334 | fileedit-glob \ |
| 335 | 335 | forbid-delta-manifests \ |
| 336 | 336 | forum-close-policy \ |
| 337 | + forum-title \ | |
| 337 | 338 | gdiff-command \ |
| 338 | 339 | gmerge-command \ |
| 339 | 340 | hash-digits \ |
| 340 | 341 | hooks \ |
| 341 | 342 | http-port \ |
| 342 | 343 |
| --- 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 |
+1
-1
| --- test/update.test | ||
| +++ test/update.test | ||
| @@ -57,11 +57,11 @@ | ||
| 57 | 57 | |
| 58 | 58 | ############################################################################### |
| 59 | 59 | |
| 60 | 60 | fossil update --verbose |
| 61 | 61 | 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] | |
| 63 | 63 | } |
| 64 | 64 | |
| 65 | 65 | # Remaining tests are carried out in the order update_cmd() performs checks. |
| 66 | 66 | # |
| 67 | 67 | # Common approach for tests below: |
| 68 | 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 |
| --- 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 |
+1
| --- tools/makemake.tcl | ||
| +++ tools/makemake.tcl | ||
| @@ -132,10 +132,11 @@ | ||
| 132 | 132 | lookslike |
| 133 | 133 | main |
| 134 | 134 | manifest |
| 135 | 135 | markdown |
| 136 | 136 | markdown_html |
| 137 | + match | |
| 137 | 138 | md5 |
| 138 | 139 | merge |
| 139 | 140 | merge3 |
| 140 | 141 | moderate |
| 141 | 142 | name |
| 142 | 143 |
| --- 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 |
+16
-5
| --- tools/translate.c | ||
| +++ tools/translate.c | ||
| @@ -78,10 +78,21 @@ | ||
| 78 | 78 | |
| 79 | 79 | /* |
| 80 | 80 | ** Name of files being processed |
| 81 | 81 | */ |
| 82 | 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 | +} | |
| 83 | 94 | |
| 84 | 95 | /* |
| 85 | 96 | ** Terminate an active cgi_printf() or free string |
| 86 | 97 | */ |
| 87 | 98 | static void end_block(FILE *out){ |
| @@ -106,21 +117,21 @@ | ||
| 106 | 117 | char zOut[4000]; /* The input line translated into appropriate output */ |
| 107 | 118 | |
| 108 | 119 | c1 = c2 = '-'; |
| 109 | 120 | while( fgets(zLine, sizeof(zLine), in) ){ |
| 110 | 121 | lineNo++; |
| 111 | - for(i=0; zLine[i] && isspace(zLine[i]); i++){} | |
| 122 | + for(i=0; zLine[i] && fossil_isspace(zLine[i]); i++){} | |
| 112 | 123 | if( zLine[i]!='@' ){ |
| 113 | 124 | if( inPrint || inStr ) end_block(out); |
| 114 | 125 | fprintf(out,"%s",zLine); |
| 115 | 126 | /* 0123456789 12345 */ |
| 116 | 127 | if( strncmp(zLine, "/* @-comment: ", 14)==0 ){ |
| 117 | 128 | c1 = zLine[14]; |
| 118 | 129 | c2 = zLine[15]; |
| 119 | 130 | } |
| 120 | 131 | i += strlen(&zLine[i]); |
| 121 | - while( i>0 && isspace(zLine[i-1]) ){ i--; } | |
| 132 | + while( i>0 && fossil_isspace(zLine[i-1]) ){ i--; } | |
| 122 | 133 | lastWasEq = i>0 && zLine[i-1]=='='; |
| 123 | 134 | lastWasComma = i>0 && zLine[i-1]==','; |
| 124 | 135 | }else if( lastWasEq || lastWasComma){ |
| 125 | 136 | /* If the last non-whitespace character before the first @ was |
| 126 | 137 | ** an "="(var init/set) or a ","(const definition in list) then |
| @@ -129,11 +140,11 @@ | ||
| 129 | 140 | ** and end of line. |
| 130 | 141 | */ |
| 131 | 142 | int indent, omitline; |
| 132 | 143 | char *zNewline = "\\n"; |
| 133 | 144 | i++; |
| 134 | - if( isspace(zLine[i]) ){ i++; } | |
| 145 | + if( fossil_isspace(zLine[i]) ){ i++; } | |
| 135 | 146 | indent = i - 2; |
| 136 | 147 | if( indent<0 ) indent = 0; |
| 137 | 148 | omitline = 0; |
| 138 | 149 | for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){ |
| 139 | 150 | if( zLine[i]==c1 && (c2==' ' || zLine[i+1]==c2) ){ |
| @@ -147,11 +158,11 @@ | ||
| 147 | 158 | break; |
| 148 | 159 | } |
| 149 | 160 | if( zLine[i]=='\\' || zLine[i]=='"' ){ zOut[j++] = '\\'; } |
| 150 | 161 | zOut[j++] = zLine[i]; |
| 151 | 162 | } |
| 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--; } | |
| 153 | 164 | zOut[j] = 0; |
| 154 | 165 | if( j<=0 && omitline ){ |
| 155 | 166 | fprintf(out,"\n"); |
| 156 | 167 | }else{ |
| 157 | 168 | fprintf(out,"%*s\"%s%s\"\n",indent, "", zOut, zNewline); |
| @@ -171,11 +182,11 @@ | ||
| 171 | 182 | int indent; |
| 172 | 183 | int nC; |
| 173 | 184 | int nParam; |
| 174 | 185 | char c; |
| 175 | 186 | i++; |
| 176 | - if( isspace(zLine[i]) ){ i++; } | |
| 187 | + if( fossil_isspace(zLine[i]) ){ i++; } | |
| 177 | 188 | indent = i; |
| 178 | 189 | for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){ |
| 179 | 190 | if( zLine[i]=='\\' && (!zLine[i+1] || zLine[i+1]=='\r' |
| 180 | 191 | || zLine[i+1]=='\n') ){ |
| 181 | 192 | zNewline = ""; |
| 182 | 193 |
| --- 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 @@ | ||
| 32 | 32 | |
| 33 | 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 | 34 | |
| 35 | 35 | PIKCHR_OPTIONS = -DPIKCHR_TOKEN_LIMIT=10000 |
| 36 | 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 | |
| 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 | 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 | |
| 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 | 40 | |
| 41 | 41 | |
| 42 | 42 | RC=$(DMDIR)\bin\rcc |
| 43 | 43 | RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ |
| 44 | 44 | |
| @@ -53,11 +53,11 @@ | ||
| 53 | 53 | |
| 54 | 54 | $(OBJDIR)\fossil.res: $B\win\fossil.rc |
| 55 | 55 | $(RC) $(RCFLAGS) -o$@ $** |
| 56 | 56 | |
| 57 | 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 > $@ | |
| 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 | 59 | +echo fossil >> $@ |
| 60 | 60 | +echo fossil >> $@ |
| 61 | 61 | +echo $(LIBS) >> $@ |
| 62 | 62 | +echo. >> $@ |
| 63 | 63 | +echo fossil >> $@ |
| @@ -635,10 +635,16 @@ | ||
| 635 | 635 | $(OBJDIR)\markdown_html$O : markdown_html_.c markdown_html.h |
| 636 | 636 | $(TCC) -o$@ -c markdown_html_.c |
| 637 | 637 | |
| 638 | 638 | markdown_html_.c : $(SRCDIR)\markdown_html.c |
| 639 | 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 $** > $@ | |
| 640 | 646 | |
| 641 | 647 | $(OBJDIR)\md5$O : md5_.c md5.h |
| 642 | 648 | $(TCC) -o$@ -c md5_.c |
| 643 | 649 | |
| 644 | 650 | md5_.c : $(SRCDIR)\md5.c |
| @@ -1009,7 +1015,7 @@ | ||
| 1009 | 1015 | |
| 1010 | 1016 | zip_.c : $(SRCDIR)\zip.c |
| 1011 | 1017 | +translate$E $** > $@ |
| 1012 | 1018 | |
| 1013 | 1019 | 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 | |
| 1015 | 1021 | @copy /Y nul: headers |
| 1016 | 1022 |
| --- 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 |
+12
| --- win/Makefile.mingw | ||
| +++ win/Makefile.mingw | ||
| @@ -485,10 +485,11 @@ | ||
| 485 | 485 | $(SRCDIR)/lookslike.c \ |
| 486 | 486 | $(SRCDIR)/main.c \ |
| 487 | 487 | $(SRCDIR)/manifest.c \ |
| 488 | 488 | $(SRCDIR)/markdown.c \ |
| 489 | 489 | $(SRCDIR)/markdown_html.c \ |
| 490 | + $(SRCDIR)/match.c \ | |
| 490 | 491 | $(SRCDIR)/md5.c \ |
| 491 | 492 | $(SRCDIR)/merge.c \ |
| 492 | 493 | $(SRCDIR)/merge3.c \ |
| 493 | 494 | $(SRCDIR)/moderate.c \ |
| 494 | 495 | $(SRCDIR)/name.c \ |
| @@ -750,10 +751,11 @@ | ||
| 750 | 751 | $(OBJDIR)/lookslike_.c \ |
| 751 | 752 | $(OBJDIR)/main_.c \ |
| 752 | 753 | $(OBJDIR)/manifest_.c \ |
| 753 | 754 | $(OBJDIR)/markdown_.c \ |
| 754 | 755 | $(OBJDIR)/markdown_html_.c \ |
| 756 | + $(OBJDIR)/match_.c \ | |
| 755 | 757 | $(OBJDIR)/md5_.c \ |
| 756 | 758 | $(OBJDIR)/merge_.c \ |
| 757 | 759 | $(OBJDIR)/merge3_.c \ |
| 758 | 760 | $(OBJDIR)/moderate_.c \ |
| 759 | 761 | $(OBJDIR)/name_.c \ |
| @@ -899,10 +901,11 @@ | ||
| 899 | 901 | $(OBJDIR)/lookslike.o \ |
| 900 | 902 | $(OBJDIR)/main.o \ |
| 901 | 903 | $(OBJDIR)/manifest.o \ |
| 902 | 904 | $(OBJDIR)/markdown.o \ |
| 903 | 905 | $(OBJDIR)/markdown_html.o \ |
| 906 | + $(OBJDIR)/match.o \ | |
| 904 | 907 | $(OBJDIR)/md5.o \ |
| 905 | 908 | $(OBJDIR)/merge.o \ |
| 906 | 909 | $(OBJDIR)/merge3.o \ |
| 907 | 910 | $(OBJDIR)/moderate.o \ |
| 908 | 911 | $(OBJDIR)/name.o \ |
| @@ -1252,10 +1255,11 @@ | ||
| 1252 | 1255 | $(OBJDIR)/lookslike_.c:$(OBJDIR)/lookslike.h \ |
| 1253 | 1256 | $(OBJDIR)/main_.c:$(OBJDIR)/main.h \ |
| 1254 | 1257 | $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \ |
| 1255 | 1258 | $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \ |
| 1256 | 1259 | $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \ |
| 1260 | + $(OBJDIR)/match_.c:$(OBJDIR)/match.h \ | |
| 1257 | 1261 | $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \ |
| 1258 | 1262 | $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \ |
| 1259 | 1263 | $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \ |
| 1260 | 1264 | $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \ |
| 1261 | 1265 | $(OBJDIR)/name_.c:$(OBJDIR)/name.h \ |
| @@ -2003,10 +2007,18 @@ | ||
| 2003 | 2007 | |
| 2004 | 2008 | $(OBJDIR)/markdown_html.o: $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h $(SRCDIR)/config.h |
| 2005 | 2009 | $(XTCC) -o $(OBJDIR)/markdown_html.o -c $(OBJDIR)/markdown_html_.c |
| 2006 | 2010 | |
| 2007 | 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 | |
| 2008 | 2020 | |
| 2009 | 2021 | $(OBJDIR)/md5_.c: $(SRCDIR)/md5.c $(TRANSLATE) |
| 2010 | 2022 | $(TRANSLATE) $(SRCDIR)/md5.c >$@ |
| 2011 | 2023 | |
| 2012 | 2024 | $(OBJDIR)/md5.o: $(OBJDIR)/md5_.c $(OBJDIR)/md5.h $(SRCDIR)/config.h |
| 2013 | 2025 |
| --- 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 |
+10
| --- win/Makefile.msc | ||
| +++ win/Makefile.msc | ||
| @@ -443,10 +443,11 @@ | ||
| 443 | 443 | "$(OX)\lookslike_.c" \ |
| 444 | 444 | "$(OX)\main_.c" \ |
| 445 | 445 | "$(OX)\manifest_.c" \ |
| 446 | 446 | "$(OX)\markdown_.c" \ |
| 447 | 447 | "$(OX)\markdown_html_.c" \ |
| 448 | + "$(OX)\match_.c" \ | |
| 448 | 449 | "$(OX)\md5_.c" \ |
| 449 | 450 | "$(OX)\merge_.c" \ |
| 450 | 451 | "$(OX)\merge3_.c" \ |
| 451 | 452 | "$(OX)\moderate_.c" \ |
| 452 | 453 | "$(OX)\name_.c" \ |
| @@ -708,10 +709,11 @@ | ||
| 708 | 709 | "$(OX)\lookslike$O" \ |
| 709 | 710 | "$(OX)\main$O" \ |
| 710 | 711 | "$(OX)\manifest$O" \ |
| 711 | 712 | "$(OX)\markdown$O" \ |
| 712 | 713 | "$(OX)\markdown_html$O" \ |
| 714 | + "$(OX)\match$O" \ | |
| 713 | 715 | "$(OX)\md5$O" \ |
| 714 | 716 | "$(OX)\merge$O" \ |
| 715 | 717 | "$(OX)\merge3$O" \ |
| 716 | 718 | "$(OX)\moderate$O" \ |
| 717 | 719 | "$(OX)\name$O" \ |
| @@ -957,10 +959,11 @@ | ||
| 957 | 959 | echo "$(OX)\lookslike.obj" >> $@ |
| 958 | 960 | echo "$(OX)\main.obj" >> $@ |
| 959 | 961 | echo "$(OX)\manifest.obj" >> $@ |
| 960 | 962 | echo "$(OX)\markdown.obj" >> $@ |
| 961 | 963 | echo "$(OX)\markdown_html.obj" >> $@ |
| 964 | + echo "$(OX)\match.obj" >> $@ | |
| 962 | 965 | echo "$(OX)\md5.obj" >> $@ |
| 963 | 966 | echo "$(OX)\merge.obj" >> $@ |
| 964 | 967 | echo "$(OX)\merge3.obj" >> $@ |
| 965 | 968 | echo "$(OX)\moderate.obj" >> $@ |
| 966 | 969 | echo "$(OX)\name.obj" >> $@ |
| @@ -1762,10 +1765,16 @@ | ||
| 1762 | 1765 | "$(OX)\markdown_html$O" : "$(OX)\markdown_html_.c" "$(OX)\markdown_html.h" |
| 1763 | 1766 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\markdown_html_.c" |
| 1764 | 1767 | |
| 1765 | 1768 | "$(OX)\markdown_html_.c" : "$(SRCDIR)\markdown_html.c" |
| 1766 | 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" $** > $@ | |
| 1767 | 1776 | |
| 1768 | 1777 | "$(OX)\md5$O" : "$(OX)\md5_.c" "$(OX)\md5.h" |
| 1769 | 1778 | $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\md5_.c" |
| 1770 | 1779 | |
| 1771 | 1780 | "$(OX)\md5_.c" : "$(SRCDIR)\md5.c" |
| @@ -2224,10 +2233,11 @@ | ||
| 2224 | 2233 | "$(OX)\lookslike_.c":"$(OX)\lookslike.h" \ |
| 2225 | 2234 | "$(OX)\main_.c":"$(OX)\main.h" \ |
| 2226 | 2235 | "$(OX)\manifest_.c":"$(OX)\manifest.h" \ |
| 2227 | 2236 | "$(OX)\markdown_.c":"$(OX)\markdown.h" \ |
| 2228 | 2237 | "$(OX)\markdown_html_.c":"$(OX)\markdown_html.h" \ |
| 2238 | + "$(OX)\match_.c":"$(OX)\match.h" \ | |
| 2229 | 2239 | "$(OX)\md5_.c":"$(OX)\md5.h" \ |
| 2230 | 2240 | "$(OX)\merge_.c":"$(OX)\merge.h" \ |
| 2231 | 2241 | "$(OX)\merge3_.c":"$(OX)\merge3.h" \ |
| 2232 | 2242 | "$(OX)\moderate_.c":"$(OX)\moderate.h" \ |
| 2233 | 2243 | "$(OX)\name_.c":"$(OX)\name.h" \ |
| 2234 | 2244 |
| --- 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 |
+28
| --- www/changes.wiki | ||
| +++ www/changes.wiki | ||
| @@ -16,10 +16,38 @@ | ||
| 16 | 16 | merge or update operation. |
| 17 | 17 | * Issue a warning if a user tries to commit on a check-in where the |
| 18 | 18 | branch has been changed. |
| 19 | 19 | * When a merge conflict occurs, a new section is added to the conflict |
| 20 | 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. | |
| 21 | 49 | |
| 22 | 50 | <h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2> |
| 23 | 51 | |
| 24 | 52 | * The "[/help?cmd=ui|fossil ui /]" command now works even for repositories |
| 25 | 53 | that have non-ASCII filenames |
| 26 | 54 |
| --- 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 @@ | ||
| 12 | 12 | |
| 13 | 13 | [0]: ./ssl.wiki |
| 14 | 14 | [1]: /timeline?c=b05cb4a0e15d0712&y=ci&n=13 |
| 15 | 15 | |
| 16 | 16 | 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 | |
| 18 | 18 | |
| 19 | 19 | * "[fossil server](/help?cmd=server)" |
| 20 | 20 | * "[fossil ui](/help?cmd=ui)", and |
| 21 | 21 | * "[fossil http](/help?cmd=http)" |
| 22 | 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 | |
| 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 | |
| 31 | 27 | |
| 32 | 28 | 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: | |
| 34 | 30 | |
| 35 | 31 | fossil ui --cert unsafe-builtin |
| 36 | 32 | |
| 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 | |
| 77 | 108 | 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 | |
| 80 | 111 | source tree for anybody to read. <b>Never add the private key that is |
| 81 | 112 | 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. | |
| 83 | 114 | If you want actual security, you will need to come up with your own private |
| 84 | 115 | key and cert. |
| 85 | 116 | |
| 86 | 117 | Fossil wants to read certs and public keys in the |
| 87 | 118 | [PEM format](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail). |
| @@ -98,66 +129,142 @@ | ||
| 98 | 129 | *base-64 encoding of the certificate* |
| 99 | 130 | -----END CERTIFICATE----- |
| 100 | 131 | |
| 101 | 132 | In both formats, text outside of the delimiters is ignored. That means |
| 102 | 133 | 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 | |
| 104 | 135 | individual components will still be easily accessible. |
| 105 | 136 | |
| 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 | |
| 107 | 140 | 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: | |
| 109 | 142 | |
| 110 | 143 | fossil server --port 443 --cert mycert.pem /home/www/myproject.fossil |
| 111 | 144 | |
| 112 | 145 | The command above is sufficient to run a fully-encrypted web site for |
| 113 | 146 | the "myproject.fossil" Fossil repository. This command must be run as |
| 114 | 147 | root, since it wants to listen on TCP port 443, and only root processes are |
| 115 | 148 | 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: | |
| 124 | 160 | |
| 125 | 161 | fossil server --port 443 --cert fullchain.pem --pkey privkey.pem /home/www/myproject.fossil |
| 126 | 162 | |
| 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. | |
| 128 | 230 | |
| 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. | |
| 133 | 233 | Here is, in a nutshell, what certbot will do to obtain your cert: |
| 134 | 234 | |
| 135 | - 1. Certbot sends your "signing request" (the document that contains | |
| 235 | + 1. It sends your "signing request" (the document that contains | |
| 136 | 236 | your public key and your domain name) to the CA. |
| 137 | 237 | |
| 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. | |
| 152 | 258 | |
| 153 | 259 | 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, | |
| 155 | 261 | then populate that subdirectory with a token file of some kind. To support |
| 156 | 262 | this, the "[fossil server](/help?cmd=server)" and |
| 157 | 263 | "[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 | |
| 159 | 266 | begins with ".well-known", then instead of doing its normal processing, it |
| 160 | 267 | looks for a file with that pathname and returns it to the client. If |
| 161 | 268 | the "server" or "http" command is referencing a single Fossil repository, |
| 162 | 269 | then the ".well-known" sub-directory should be in the same directory as |
| 163 | 270 | the repository file. If the "server" or "http" command are run against |
| @@ -172,9 +279,11 @@ | ||
| 172 | 279 | Then you create your public/private key pair and run certbot, giving it |
| 173 | 280 | a --webroot of /home/www. Certbot will create the sub-directory |
| 174 | 281 | named "/home/www/.well-known" and put token files there, which the CA |
| 175 | 282 | will verify. Then certbot will store your new cert in a particular file. |
| 176 | 283 | |
| 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. | |
| 179 | 287 | |
| 180 | 288 | [2]: https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment |
| 289 | +[certbot]: https://certbot.eff.org | |
| 181 | 290 |
| --- 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 |
+8
| --- www/sync.wiki | ||
| +++ www/sync.wiki | ||
| @@ -789,10 +789,18 @@ | ||
| 789 | 789 | a successful commit. This instructs the server to release |
| 790 | 790 | any lock on any check-in previously held by that client. |
| 791 | 791 | The ci-unlock pragma helps to avoid false-positive lock warnings |
| 792 | 792 | that might arise if a check-in is aborted and then restarted |
| 793 | 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. | |
| 794 | 802 | </ol> |
| 795 | 803 | |
| 796 | 804 | <h3 id="comment">3.12 Comment Cards</h3> |
| 797 | 805 | |
| 798 | 806 | Any card that begins with "#" (ASCII 0x23) is a comment card and |
| 799 | 807 |
| --- 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 |