Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Merge trunk. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | ssh-signing |
| Files: | files | file ages | folders |
| SHA3-256: |
02cdfa5e0866368cbb2d5bf881b544fb |
| User & Date: | danield 2025-01-04 23:28:33.061 |
Context
|
2025-01-09
| ||
| 07:10 | Slight wording change. check-in: d4fae7d7b6 user: brickviking tags: ssh-signing | |
|
2025-01-04
| ||
| 23:28 | Merge trunk. check-in: 02cdfa5e08 user: danield tags: ssh-signing | |
| 23:18 | More robust test for the ssh case, minor refactoring. check-in: d2bfab5888 user: danield tags: ssh-signing | |
| 14:40 | Update tests for new setting added with [b1bb31e838]. check-in: 787a45e58a user: florian tags: trunk | |
Changes
Changes to BUILD.txt.
| ︙ | ︙ | |||
40 41 42 43 44 45 46 | For example: mkdir build cd build ../configure make | | | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | For example: mkdir build cd build ../configure make This will now keep all generated files separate from the maintained source code. -------------------------------------------------------------------------- Here are some notes on what is happening behind the scenes: * The configure script (if used) examines the options given |
| ︙ | ︙ |
Changes to Dockerfile.
1 2 3 4 5 6 7 | # syntax=docker/dockerfile:1.3 # See www/containers.md for documentation on how to use this file. ## --------------------------------------------------------------------- ## STAGE 1: Build static Fossil binary ## --------------------------------------------------------------------- | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# syntax=docker/dockerfile:1.3
# See www/containers.md for documentation on how to use this file.
## ---------------------------------------------------------------------
## STAGE 1: Build static Fossil binary
## ---------------------------------------------------------------------
### We don't pin a more stable version of our base layer because we want
### to build with the latest tools and libraries available in case they
### fixed something that matters to us since the last build. Everything
### below depends on this layer, and so, alas, we toss this container's
### cache on Alpine's release schedule, roughly once a month.
FROM alpine:latest AS bld
WORKDIR /fsl
### Bake the build-time userland into a base layer so it only changes
### when the upstream image is updated or we change the package set.
RUN set -x \
&& apk update \
&& apk upgrade --no-cache \
&& apk add --no-cache \
gcc make \
linux-headers musl-dev \
openssl-dev openssl-libs-static \
zlib-dev zlib-static
### Build Fossil as a separate layer so we don't have to rebuild the
### userland for each iteration of Fossil's dev cycle.
###
### We must cope with a bizarre ADD misfeature here: it unpacks tarballs
### automatically when you give it a local file name but not if you give
### it a /tarball URL! It matters because we default to a URL in case
### you're building outside a Fossil checkout, but when building via the
### container-image target, we avoid a costly hit on fossil-scm.org by
### leveraging its DVCS nature via the "tarball" command and passing the
### resulting file's name in.
ARG FSLCFG=""
ARG FSLVER="trunk"
ARG FSLURL="https://fossil-scm.org/home/tarball/src?r=${FSLVER}"
ENV FSLSTB=/fsl/src.tar.gz
ADD $FSLURL $FSLSTB
RUN set -x \
&& if [ -d $FSLSTB ] ; \
|
| ︙ | ︙ |
Changes to VERSION.
|
| | | 1 | 2.26 |
Changes to extsrc/shell.c.
| ︙ | ︙ | |||
118 119 120 121 122 123 124 | #include <stdio.h> #include <assert.h> #include <math.h> #include "sqlite3.h" typedef sqlite3_int64 i64; typedef sqlite3_uint64 u64; typedef unsigned char u8; | < < < | 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | #include <stdio.h> #include <assert.h> #include <math.h> #include "sqlite3.h" typedef sqlite3_int64 i64; typedef sqlite3_uint64 u64; typedef unsigned char u8; #include <ctype.h> #include <stdarg.h> #if !defined(_WIN32) && !defined(WIN32) # include <signal.h> # if !defined(__RTP__) && !defined(_WRS_KERNEL) && !defined(SQLITE_WASI) # include <pwd.h> |
| ︙ | ︙ | |||
406 407 408 409 410 411 412 |
FILE *sqlite3_fopen(const char *zFilename, const char *zMode){
FILE *fp = 0;
wchar_t *b1, *b2;
int sz1, sz2;
sz1 = (int)strlen(zFilename);
sz2 = (int)strlen(zMode);
| | | | | | | | | | > > > > > > > > > | | | | > | 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 |
FILE *sqlite3_fopen(const char *zFilename, const char *zMode){
FILE *fp = 0;
wchar_t *b1, *b2;
int sz1, sz2;
sz1 = (int)strlen(zFilename);
sz2 = (int)strlen(zMode);
b1 = sqlite3_malloc( (sz1+1)*sizeof(b1[0]) );
b2 = sqlite3_malloc( (sz2+1)*sizeof(b1[0]) );
if( b1 && b2 ){
sz1 = MultiByteToWideChar(CP_UTF8, 0, zFilename, sz1, b1, sz1);
b1[sz1] = 0;
sz2 = MultiByteToWideChar(CP_UTF8, 0, zMode, sz2, b2, sz2);
b2[sz2] = 0;
fp = _wfopen(b1, b2);
}
sqlite3_free(b1);
sqlite3_free(b2);
simBinaryOther = 0;
return fp;
}
/*
** Work-alike for the popen() routine from the standard C library.
*/
FILE *sqlite3_popen(const char *zCommand, const char *zMode){
FILE *fp = 0;
wchar_t *b1, *b2;
int sz1, sz2;
sz1 = (int)strlen(zCommand);
sz2 = (int)strlen(zMode);
b1 = sqlite3_malloc( (sz1+1)*sizeof(b1[0]) );
b2 = sqlite3_malloc( (sz2+1)*sizeof(b1[0]) );
if( b1 && b2 ){
sz1 = MultiByteToWideChar(CP_UTF8, 0, zCommand, sz1, b1, sz1);
b1[sz1] = 0;
sz2 = MultiByteToWideChar(CP_UTF8, 0, zMode, sz2, b2, sz2);
b2[sz2] = 0;
fp = _wpopen(b1, b2);
}
sqlite3_free(b1);
sqlite3_free(b2);
return fp;
}
/*
** Work-alike for fgets() from the standard C library.
*/
char *sqlite3_fgets(char *buf, int sz, FILE *in){
if( UseWtextForInput(in) ){
/* When reading from the command-prompt in Windows, it is necessary
** to use _O_WTEXT input mode to read UTF-16 characters, then translate
** that into UTF-8. Otherwise, non-ASCII characters all get translated
** into '?'.
*/
wchar_t *b1 = sqlite3_malloc( sz*sizeof(wchar_t) );
if( b1==0 ) return 0;
#ifndef SQLITE_USE_STDIO_FOR_CONSOLE
DWORD nRead = 0;
if( IsConsole(in)
&& ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), b1, sz, &nRead, 0)
){
b1[nRead] = 0;
}else
#endif
{
_setmode(_fileno(in), IsConsole(in) ? _O_WTEXT : _O_U8TEXT);
if( fgetws(b1, sz/4, in)==0 ){
sqlite3_free(b1);
return 0;
}
}
WideCharToMultiByte(CP_UTF8, 0, b1, -1, buf, sz, 0, 0);
sqlite3_free(b1);
return buf;
}else{
/* Reading from a file or other input source, just read bytes without
** any translation. */
|
| ︙ | ︙ | |||
517 518 519 520 521 522 523 |
*/
int sqlite3_fputs(const char *z, FILE *out){
if( !UseWtextForOutput(out) ){
/* Writing to a file or other destination, just write bytes without
** any translation. */
return fputs(z, out);
}else{
| | | < | > > > > > > > > > > > > > | | | | | > | 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 |
*/
int sqlite3_fputs(const char *z, FILE *out){
if( !UseWtextForOutput(out) ){
/* Writing to a file or other destination, just write bytes without
** any translation. */
return fputs(z, out);
}else{
/* One must use UTF16 in order to get unicode support when writing
** to the console on Windows.
*/
int sz = (int)strlen(z);
wchar_t *b1 = sqlite3_malloc( (sz+1)*sizeof(wchar_t) );
if( b1==0 ) return 0;
sz = MultiByteToWideChar(CP_UTF8, 0, z, sz, b1, sz);
b1[sz] = 0;
#ifndef SQLITE_STDIO_FOR_CONSOLE
DWORD nWr = 0;
if( IsConsole(out)
&& WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),b1,sz,&nWr,0)
){
/* If writing to the console, then the WriteConsoleW() is all we
** need to do. */
}else
#endif
{
/* For non-console I/O, or if SQLITE_USE_STDIO_FOR_CONSOLE is defined
** then write using the standard library. */
_setmode(_fileno(out), _O_U8TEXT);
if( UseBinaryWText(out) ){
piecemealOutput(b1, sz, out);
}else{
fputws(b1, out);
}
}
sqlite3_free(b1);
return 0;
}
}
|
| ︙ | ︙ | |||
2345 2346 2347 2348 2349 2350 2351 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ****************************************************************************** ** ** This SQLite extension implements functions that compute SHA3 hashes ** in the way described by the (U.S.) NIST FIPS 202 SHA-3 Standard. | | | 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 | ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ****************************************************************************** ** ** This SQLite extension implements functions that compute SHA3 hashes ** in the way described by the (U.S.) NIST FIPS 202 SHA-3 Standard. ** Three SQL functions are implemented: ** ** sha3(X,SIZE) ** sha3_agg(Y,SIZE) ** sha3_query(Z,SIZE) ** ** The sha3(X) function computes the SHA3 hash of the input X, or NULL if ** X is NULL. If inputs X is text, the UTF-8 rendering of that text is |
| ︙ | ︙ | |||
5068 5069 5070 5071 5072 5073 5074 |
int sqlite3_percentile_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
unsigned int i;
| | | | | 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 5103 5104 5105 |
int sqlite3_percentile_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
){
int rc = SQLITE_OK;
unsigned int i;
#ifdef SQLITE3EXT_H
SQLITE_EXTENSION_INIT2(pApi);
#else
(void)pApi; /* Unused parameter */
#endif
(void)pzErrMsg; /* Unused parameter */
for(i=0; i<sizeof(aPercentFunc)/sizeof(aPercentFunc[0]); i++){
rc = sqlite3_create_window_function(db,
aPercentFunc[i].zName,
aPercentFunc[i].nArg,
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_SELFORDER1,
|
| ︙ | ︙ | |||
6829 6830 6831 6832 6833 6834 6835 |
assert( op==SQLITE_INDEX_CONSTRAINT_OFFSET );
aIdx[4] = i;
idxNum |= 0x40;
}
continue;
}
if( pConstraint->iColumn<SERIES_COLUMN_START ){
| | > > > > > > | 6849 6850 6851 6852 6853 6854 6855 6856 6857 6858 6859 6860 6861 6862 6863 6864 6865 6866 6867 6868 6869 6870 6871 6872 6873 6874 6875 6876 6877 6878 6879 6880 6881 6882 6883 6884 6885 6886 6887 6888 6889 6890 6891 6892 6893 |
assert( op==SQLITE_INDEX_CONSTRAINT_OFFSET );
aIdx[4] = i;
idxNum |= 0x40;
}
continue;
}
if( pConstraint->iColumn<SERIES_COLUMN_START ){
if( pConstraint->iColumn==SERIES_COLUMN_VALUE && pConstraint->usable ){
switch( op ){
case SQLITE_INDEX_CONSTRAINT_EQ:
case SQLITE_INDEX_CONSTRAINT_IS: {
idxNum |= 0x0080;
idxNum &= ~0x3300;
aIdx[5] = i;
aIdx[6] = -1;
#ifndef ZERO_ARGUMENT_GENERATE_SERIES
bStartSeen = 1;
#endif
break;
}
case SQLITE_INDEX_CONSTRAINT_GE: {
if( idxNum & 0x0080 ) break;
idxNum |= 0x0100;
idxNum &= ~0x0200;
aIdx[5] = i;
#ifndef ZERO_ARGUMENT_GENERATE_SERIES
bStartSeen = 1;
#endif
break;
}
case SQLITE_INDEX_CONSTRAINT_GT: {
if( idxNum & 0x0080 ) break;
idxNum |= 0x0200;
idxNum &= ~0x0100;
aIdx[5] = i;
#ifndef ZERO_ARGUMENT_GENERATE_SERIES
bStartSeen = 1;
#endif
break;
}
case SQLITE_INDEX_CONSTRAINT_LE: {
if( idxNum & 0x0080 ) break;
idxNum |= 0x1000;
idxNum &= ~0x2000;
aIdx[6] = i;
|
| ︙ | ︙ | |||
14165 14166 14167 14168 14169 14170 14171 |
if( zType[0]=='v' || zType[1]=='r' || bVirtual ){
/* A view. Or a trigger on a view. */
if( zSql ) rc = expertSchemaSql(p->dbv, zSql, pzErrmsg);
}else{
IdxTable *pTab;
rc = idxGetTableInfo(p->db, zName, &pTab, pzErrmsg);
| | | 14191 14192 14193 14194 14195 14196 14197 14198 14199 14200 14201 14202 14203 14204 14205 |
if( zType[0]=='v' || zType[1]=='r' || bVirtual ){
/* A view. Or a trigger on a view. */
if( zSql ) rc = expertSchemaSql(p->dbv, zSql, pzErrmsg);
}else{
IdxTable *pTab;
rc = idxGetTableInfo(p->db, zName, &pTab, pzErrmsg);
if( rc==SQLITE_OK && ALWAYS(pTab!=0) ){
int i;
char *zInner = 0;
char *zOuter = 0;
pTab->pNext = p->pTable;
p->pTable = pTab;
/* The statement the vtab will pass to sqlite3_declare_vtab() */
|
| ︙ | ︙ | |||
16233 16234 16235 16236 16237 16238 16239 | ** -DSQLITE_THREADSAFE=0 -DSQLITE_ENABLE_VFSTRACE \ ** shell.c test_vfstrace.c sqlite3.c ** ** Similar compiler commands will work on different systems. The key ** invariants are (1) you must have -DSQLITE_ENABLE_VFSTRACE so that ** the shell.c source file will know to include the -vfstrace command-line ** option and (2) you must compile and link the three source files | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 16259 16260 16261 16262 16263 16264 16265 16266 16267 16268 16269 16270 16271 16272 16273 16274 16275 16276 16277 16278 16279 16280 16281 16282 16283 16284 16285 16286 16287 16288 16289 16290 16291 16292 16293 16294 16295 16296 16297 16298 16299 16300 16301 16302 16303 16304 16305 16306 16307 16308 16309 16310 16311 16312 16313 16314 16315 16316 16317 16318 16319 16320 16321 16322 16323 16324 16325 16326 16327 16328 16329 16330 16331 16332 16333 16334 16335 16336 16337 16338 16339 16340 16341 16342 16343 16344 16345 16346 16347 16348 16349 16350 16351 16352 16353 16354 16355 16356 |
** -DSQLITE_THREADSAFE=0 -DSQLITE_ENABLE_VFSTRACE \
** shell.c test_vfstrace.c sqlite3.c
**
** Similar compiler commands will work on different systems. The key
** invariants are (1) you must have -DSQLITE_ENABLE_VFSTRACE so that
** the shell.c source file will know to include the -vfstrace command-line
** option and (2) you must compile and link the three source files
** shell,c, test_vfstrace.c, and sqlite3.c.
**
** RUNTIME CONTROL OF VFSTRACE OUTPUT
**
** The application can use the "vfstrace" pragma to control which VFS
** APIs are traced. To disable all output:
**
** PRAGMA vfstrace('-all');
**
** To enable all output (which is the default setting):
**
** PRAGMA vfstrace('+all');
**
** Individual APIs can be enabled or disabled by name, with or without
** the initial "x" character. For example, to set up for tracing lock
** primatives only:
**
** PRAGMA vfstrace('-all, +Lock,Unlock,ShmLock');
**
** The argument to the vfstrace pragma ignores capitalization and any
** characters other than alphabetics, '+', and '-'.
*/
#include <stdlib.h>
#include <string.h>
/* #include "sqlite3.h" */
/*
** An instance of this structure is attached to the each trace VFS to
** provide auxiliary information.
*/
typedef struct vfstrace_info vfstrace_info;
struct vfstrace_info {
sqlite3_vfs *pRootVfs; /* The underlying real VFS */
int (*xOut)(const char*, void*); /* Send output here */
unsigned int mTrace; /* Mask of interfaces to trace */
u8 bOn; /* Tracing on/off */
void *pOutArg; /* First argument to xOut */
const char *zVfsName; /* Name of this trace-VFS */
sqlite3_vfs *pTraceVfs; /* Pointer back to the trace VFS */
};
/*
** The sqlite3_file object for the trace VFS
*/
typedef struct vfstrace_file vfstrace_file;
struct vfstrace_file {
sqlite3_file base; /* Base class. Must be first */
vfstrace_info *pInfo; /* The trace-VFS to which this file belongs */
const char *zFName; /* Base name of the file */
sqlite3_file *pReal; /* The real underlying file */
};
/*
** Bit values for vfstrace_info.mTrace.
*/
#define VTR_CLOSE 0x00000001
#define VTR_READ 0x00000002
#define VTR_WRITE 0x00000004
#define VTR_TRUNC 0x00000008
#define VTR_SYNC 0x00000010
#define VTR_FSIZE 0x00000020
#define VTR_LOCK 0x00000040
#define VTR_UNLOCK 0x00000080
#define VTR_CRL 0x00000100
#define VTR_FCTRL 0x00000200
#define VTR_SECSZ 0x00000400
#define VTR_DEVCHAR 0x00000800
#define VTR_SHMLOCK 0x00001000
#define VTR_SHMMAP 0x00002000
#define VTR_SHMBAR 0x00004000
#define VTR_SHMUNMAP 0x00008000
#define VTR_OPEN 0x00010000
#define VTR_DELETE 0x00020000
#define VTR_ACCESS 0x00040000
#define VTR_FULLPATH 0x00080000
#define VTR_DLOPEN 0x00100000
#define VTR_DLERR 0x00200000
#define VTR_DLSYM 0x00400000
#define VTR_DLCLOSE 0x00800000
#define VTR_RAND 0x01000000
#define VTR_SLEEP 0x02000000
#define VTR_CURTIME 0x04000000
#define VTR_LASTERR 0x08000000
/*
** Method declarations for vfstrace_file.
*/
static int vfstraceClose(sqlite3_file*);
static int vfstraceRead(sqlite3_file*, void*, int iAmt, sqlite3_int64 iOfst);
static int vfstraceWrite(sqlite3_file*,const void*,int iAmt, sqlite3_int64);
static int vfstraceTruncate(sqlite3_file*, sqlite3_int64 size);
|
| ︙ | ︙ | |||
16327 16328 16329 16330 16331 16332 16333 |
static void vfstrace_printf(
vfstrace_info *pInfo,
const char *zFormat,
...
){
va_list ap;
char *zMsg;
| > | | | | | > | 16407 16408 16409 16410 16411 16412 16413 16414 16415 16416 16417 16418 16419 16420 16421 16422 16423 16424 16425 16426 16427 |
static void vfstrace_printf(
vfstrace_info *pInfo,
const char *zFormat,
...
){
va_list ap;
char *zMsg;
if( pInfo->bOn ){
va_start(ap, zFormat);
zMsg = sqlite3_vmprintf(zFormat, ap);
va_end(ap);
pInfo->xOut(zMsg, pInfo->pOutArg);
sqlite3_free(zMsg);
}
}
/*
** Try to convert an error code into a symbolic name for that error code.
*/
static const char *vfstrace_errcode_name(int rc ){
const char *zVal = 0;
|
| ︙ | ︙ | |||
16429 16430 16431 16432 16433 16434 16435 16436 16437 16438 16439 16440 16441 16442 16443 16444 16445 16446 16447 16448 16449 16450 |
*/
static void strappend(char *z, int *pI, const char *zAppend){
int i = *pI;
while( zAppend[0] ){ z[i++] = *(zAppend++); }
z[i] = 0;
*pI = i;
}
/*
** Close an vfstrace-file.
*/
static int vfstraceClose(sqlite3_file *pFile){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstrace_printf(pInfo, "%s.xClose(%s)", pInfo->zVfsName, p->zFName);
rc = p->pReal->pMethods->xClose(p->pReal);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
if( rc==SQLITE_OK ){
sqlite3_free((void*)p->base.pMethods);
p->base.pMethods = 0;
}
| > > > > > > > > | 16511 16512 16513 16514 16515 16516 16517 16518 16519 16520 16521 16522 16523 16524 16525 16526 16527 16528 16529 16530 16531 16532 16533 16534 16535 16536 16537 16538 16539 16540 |
*/
static void strappend(char *z, int *pI, const char *zAppend){
int i = *pI;
while( zAppend[0] ){ z[i++] = *(zAppend++); }
z[i] = 0;
*pI = i;
}
/*
** Turn tracing output on or off according to mMask.
*/
static void vfstraceOnOff(vfstrace_info *pInfo, unsigned int mMask){
pInfo->bOn = (pInfo->mTrace & mMask)!=0;
}
/*
** Close an vfstrace-file.
*/
static int vfstraceClose(sqlite3_file *pFile){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_CLOSE);
vfstrace_printf(pInfo, "%s.xClose(%s)", pInfo->zVfsName, p->zFName);
rc = p->pReal->pMethods->xClose(p->pReal);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
if( rc==SQLITE_OK ){
sqlite3_free((void*)p->base.pMethods);
p->base.pMethods = 0;
}
|
| ︙ | ︙ | |||
16459 16460 16461 16462 16463 16464 16465 16466 16467 16468 16469 16470 16471 16472 16473 16474 16475 16476 16477 16478 16479 16480 16481 16482 16483 16484 16485 16486 16487 16488 16489 16490 16491 16492 16493 16494 16495 16496 16497 16498 16499 16500 16501 16502 16503 16504 16505 |
void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstrace_printf(pInfo, "%s.xRead(%s,n=%d,ofst=%lld)",
pInfo->zVfsName, p->zFName, iAmt, iOfst);
rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
/*
** Write data to an vfstrace-file.
*/
static int vfstraceWrite(
sqlite3_file *pFile,
const void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstrace_printf(pInfo, "%s.xWrite(%s,n=%d,ofst=%lld)",
pInfo->zVfsName, p->zFName, iAmt, iOfst);
rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
/*
** Truncate an vfstrace-file.
*/
static int vfstraceTruncate(sqlite3_file *pFile, sqlite_int64 size){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstrace_printf(pInfo, "%s.xTruncate(%s,%lld)", pInfo->zVfsName, p->zFName,
size);
rc = p->pReal->pMethods->xTruncate(p->pReal, size);
vfstrace_printf(pInfo, " -> %d\n", rc);
return rc;
}
| > > > | 16549 16550 16551 16552 16553 16554 16555 16556 16557 16558 16559 16560 16561 16562 16563 16564 16565 16566 16567 16568 16569 16570 16571 16572 16573 16574 16575 16576 16577 16578 16579 16580 16581 16582 16583 16584 16585 16586 16587 16588 16589 16590 16591 16592 16593 16594 16595 16596 16597 16598 |
void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_READ);
vfstrace_printf(pInfo, "%s.xRead(%s,n=%d,ofst=%lld)",
pInfo->zVfsName, p->zFName, iAmt, iOfst);
rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
/*
** Write data to an vfstrace-file.
*/
static int vfstraceWrite(
sqlite3_file *pFile,
const void *zBuf,
int iAmt,
sqlite_int64 iOfst
){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_WRITE);
vfstrace_printf(pInfo, "%s.xWrite(%s,n=%d,ofst=%lld)",
pInfo->zVfsName, p->zFName, iAmt, iOfst);
rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
/*
** Truncate an vfstrace-file.
*/
static int vfstraceTruncate(sqlite3_file *pFile, sqlite_int64 size){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_TRUNC);
vfstrace_printf(pInfo, "%s.xTruncate(%s,%lld)", pInfo->zVfsName, p->zFName,
size);
rc = p->pReal->pMethods->xTruncate(p->pReal, size);
vfstrace_printf(pInfo, " -> %d\n", rc);
return rc;
}
|
| ︙ | ︙ | |||
16516 16517 16518 16519 16520 16521 16522 16523 16524 16525 16526 16527 16528 16529 16530 16531 16532 16533 16534 16535 16536 16537 16538 16539 16540 16541 16542 16543 |
i = 0;
if( flags & SQLITE_SYNC_FULL ) strappend(zBuf, &i, "|FULL");
else if( flags & SQLITE_SYNC_NORMAL ) strappend(zBuf, &i, "|NORMAL");
if( flags & SQLITE_SYNC_DATAONLY ) strappend(zBuf, &i, "|DATAONLY");
if( flags & ~(SQLITE_SYNC_FULL|SQLITE_SYNC_DATAONLY) ){
sqlite3_snprintf(sizeof(zBuf)-i, &zBuf[i], "|0x%x", flags);
}
vfstrace_printf(pInfo, "%s.xSync(%s,%s)", pInfo->zVfsName, p->zFName,
&zBuf[1]);
rc = p->pReal->pMethods->xSync(p->pReal, flags);
vfstrace_printf(pInfo, " -> %d\n", rc);
return rc;
}
/*
** Return the current file-size of an vfstrace-file.
*/
static int vfstraceFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstrace_printf(pInfo, "%s.xFileSize(%s)", pInfo->zVfsName, p->zFName);
rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
vfstrace_print_errcode(pInfo, " -> %s,", rc);
vfstrace_printf(pInfo, " size=%lld\n", *pSize);
return rc;
}
| > > | 16609 16610 16611 16612 16613 16614 16615 16616 16617 16618 16619 16620 16621 16622 16623 16624 16625 16626 16627 16628 16629 16630 16631 16632 16633 16634 16635 16636 16637 16638 |
i = 0;
if( flags & SQLITE_SYNC_FULL ) strappend(zBuf, &i, "|FULL");
else if( flags & SQLITE_SYNC_NORMAL ) strappend(zBuf, &i, "|NORMAL");
if( flags & SQLITE_SYNC_DATAONLY ) strappend(zBuf, &i, "|DATAONLY");
if( flags & ~(SQLITE_SYNC_FULL|SQLITE_SYNC_DATAONLY) ){
sqlite3_snprintf(sizeof(zBuf)-i, &zBuf[i], "|0x%x", flags);
}
vfstraceOnOff(pInfo, VTR_SYNC);
vfstrace_printf(pInfo, "%s.xSync(%s,%s)", pInfo->zVfsName, p->zFName,
&zBuf[1]);
rc = p->pReal->pMethods->xSync(p->pReal, flags);
vfstrace_printf(pInfo, " -> %d\n", rc);
return rc;
}
/*
** Return the current file-size of an vfstrace-file.
*/
static int vfstraceFileSize(sqlite3_file *pFile, sqlite_int64 *pSize){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_FSIZE);
vfstrace_printf(pInfo, "%s.xFileSize(%s)", pInfo->zVfsName, p->zFName);
rc = p->pReal->pMethods->xFileSize(p->pReal, pSize);
vfstrace_print_errcode(pInfo, " -> %s,", rc);
vfstrace_printf(pInfo, " size=%lld\n", *pSize);
return rc;
}
|
| ︙ | ︙ | |||
16558 16559 16560 16561 16562 16563 16564 16565 16566 16567 16568 16569 16570 16571 16572 16573 16574 16575 16576 16577 16578 16579 16580 16581 16582 16583 16584 16585 16586 16587 16588 16589 16590 16591 16592 16593 16594 16595 16596 16597 16598 16599 16600 16601 16602 16603 16604 16605 16606 16607 16608 16609 16610 16611 16612 16613 16614 16615 16616 16617 16618 |
/*
** Lock an vfstrace-file.
*/
static int vfstraceLock(sqlite3_file *pFile, int eLock){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstrace_printf(pInfo, "%s.xLock(%s,%s)", pInfo->zVfsName, p->zFName,
lockName(eLock));
rc = p->pReal->pMethods->xLock(p->pReal, eLock);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
/*
** Unlock an vfstrace-file.
*/
static int vfstraceUnlock(sqlite3_file *pFile, int eLock){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstrace_printf(pInfo, "%s.xUnlock(%s,%s)", pInfo->zVfsName, p->zFName,
lockName(eLock));
rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
/*
** Check if another file-handle holds a RESERVED lock on an vfstrace-file.
*/
static int vfstraceCheckReservedLock(sqlite3_file *pFile, int *pResOut){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstrace_printf(pInfo, "%s.xCheckReservedLock(%s,%d)",
pInfo->zVfsName, p->zFName);
rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
vfstrace_print_errcode(pInfo, " -> %s", rc);
vfstrace_printf(pInfo, ", out=%d\n", *pResOut);
return rc;
}
/*
** File control method. For custom operations on an vfstrace-file.
*/
static int vfstraceFileControl(sqlite3_file *pFile, int op, void *pArg){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
char zBuf[100];
char zBuf2[100];
char *zOp;
char *zRVal = 0;
switch( op ){
case SQLITE_FCNTL_LOCKSTATE: zOp = "LOCKSTATE"; break;
case SQLITE_GET_LOCKPROXYFILE: zOp = "GET_LOCKPROXYFILE"; break;
case SQLITE_SET_LOCKPROXYFILE: zOp = "SET_LOCKPROXYFILE"; break;
case SQLITE_LAST_ERRNO: zOp = "LAST_ERRNO"; break;
case SQLITE_FCNTL_SIZE_HINT: {
sqlite3_snprintf(sizeof(zBuf), zBuf, "SIZE_HINT,%lld",
| > > > > | 16653 16654 16655 16656 16657 16658 16659 16660 16661 16662 16663 16664 16665 16666 16667 16668 16669 16670 16671 16672 16673 16674 16675 16676 16677 16678 16679 16680 16681 16682 16683 16684 16685 16686 16687 16688 16689 16690 16691 16692 16693 16694 16695 16696 16697 16698 16699 16700 16701 16702 16703 16704 16705 16706 16707 16708 16709 16710 16711 16712 16713 16714 16715 16716 16717 |
/*
** Lock an vfstrace-file.
*/
static int vfstraceLock(sqlite3_file *pFile, int eLock){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_LOCK);
vfstrace_printf(pInfo, "%s.xLock(%s,%s)", pInfo->zVfsName, p->zFName,
lockName(eLock));
rc = p->pReal->pMethods->xLock(p->pReal, eLock);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
/*
** Unlock an vfstrace-file.
*/
static int vfstraceUnlock(sqlite3_file *pFile, int eLock){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_UNLOCK);
vfstrace_printf(pInfo, "%s.xUnlock(%s,%s)", pInfo->zVfsName, p->zFName,
lockName(eLock));
rc = p->pReal->pMethods->xUnlock(p->pReal, eLock);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
/*
** Check if another file-handle holds a RESERVED lock on an vfstrace-file.
*/
static int vfstraceCheckReservedLock(sqlite3_file *pFile, int *pResOut){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_CRL);
vfstrace_printf(pInfo, "%s.xCheckReservedLock(%s,%d)",
pInfo->zVfsName, p->zFName);
rc = p->pReal->pMethods->xCheckReservedLock(p->pReal, pResOut);
vfstrace_print_errcode(pInfo, " -> %s", rc);
vfstrace_printf(pInfo, ", out=%d\n", *pResOut);
return rc;
}
/*
** File control method. For custom operations on an vfstrace-file.
*/
static int vfstraceFileControl(sqlite3_file *pFile, int op, void *pArg){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
char zBuf[100];
char zBuf2[100];
char *zOp;
char *zRVal = 0;
vfstraceOnOff(pInfo, VTR_FCTRL);
switch( op ){
case SQLITE_FCNTL_LOCKSTATE: zOp = "LOCKSTATE"; break;
case SQLITE_GET_LOCKPROXYFILE: zOp = "GET_LOCKPROXYFILE"; break;
case SQLITE_SET_LOCKPROXYFILE: zOp = "SET_LOCKPROXYFILE"; break;
case SQLITE_LAST_ERRNO: zOp = "LAST_ERRNO"; break;
case SQLITE_FCNTL_SIZE_HINT: {
sqlite3_snprintf(sizeof(zBuf), zBuf, "SIZE_HINT,%lld",
|
| ︙ | ︙ | |||
16633 16634 16635 16636 16637 16638 16639 16640 16641 16642 16643 16644 16645 16646 |
break;
}
case SQLITE_FCNTL_OVERWRITE: zOp = "OVERWRITE"; break;
case SQLITE_FCNTL_VFSNAME: zOp = "VFSNAME"; break;
case SQLITE_FCNTL_POWERSAFE_OVERWRITE: zOp = "POWERSAFE_OVERWRITE"; break;
case SQLITE_FCNTL_PRAGMA: {
const char *const* a = (const char*const*)pArg;
sqlite3_snprintf(sizeof(zBuf), zBuf, "PRAGMA,[%s,%s]",a[1],a[2]);
zOp = zBuf;
break;
}
case SQLITE_FCNTL_BUSYHANDLER: zOp = "BUSYHANDLER"; break;
case SQLITE_FCNTL_TEMPFILENAME: zOp = "TEMPFILENAME"; break;
case SQLITE_FCNTL_MMAP_SIZE: {
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 16732 16733 16734 16735 16736 16737 16738 16739 16740 16741 16742 16743 16744 16745 16746 16747 16748 16749 16750 16751 16752 16753 16754 16755 16756 16757 16758 16759 16760 16761 16762 16763 16764 16765 16766 16767 16768 16769 16770 16771 16772 16773 16774 16775 16776 16777 16778 16779 16780 16781 16782 16783 16784 16785 16786 16787 16788 16789 16790 16791 16792 16793 16794 16795 16796 16797 16798 16799 16800 16801 16802 16803 16804 16805 16806 16807 16808 16809 16810 16811 16812 16813 16814 16815 16816 16817 16818 |
break;
}
case SQLITE_FCNTL_OVERWRITE: zOp = "OVERWRITE"; break;
case SQLITE_FCNTL_VFSNAME: zOp = "VFSNAME"; break;
case SQLITE_FCNTL_POWERSAFE_OVERWRITE: zOp = "POWERSAFE_OVERWRITE"; break;
case SQLITE_FCNTL_PRAGMA: {
const char *const* a = (const char*const*)pArg;
if( a[1] && strcmp(a[1],"vfstrace")==0 && a[2] ){
const u8 *zArg = (const u8*)a[2];
if( zArg[0]>='0' && zArg[0]<=9 ){
pInfo->mTrace = (sqlite3_uint64)strtoll(a[2], 0, 0);
}else{
static const struct {
const char *z;
unsigned int m;
} aKw[] = {
{ "all", 0xffffffff },
{ "close", VTR_CLOSE },
{ "read", VTR_READ },
{ "write", VTR_WRITE },
{ "truncate", VTR_TRUNC },
{ "sync", VTR_SYNC },
{ "filesize", VTR_FSIZE },
{ "lock", VTR_LOCK },
{ "unlock", VTR_UNLOCK },
{ "checkreservedlock", VTR_CRL },
{ "filecontrol", VTR_FCTRL },
{ "sectorsize", VTR_SECSZ },
{ "devicecharacteristics", VTR_DEVCHAR },
{ "shmlock", VTR_SHMLOCK },
{ "shmmap", VTR_SHMMAP },
{ "shmummap", VTR_SHMUNMAP },
{ "shmbarrier", VTR_SHMBAR },
{ "open", VTR_OPEN },
{ "delete", VTR_DELETE },
{ "access", VTR_ACCESS },
{ "fullpathname", VTR_FULLPATH },
{ "dlopen", VTR_DLOPEN },
{ "dlerror", VTR_DLERR },
{ "dlsym", VTR_DLSYM },
{ "dlclose", VTR_DLCLOSE },
{ "randomness", VTR_RAND },
{ "sleep", VTR_SLEEP },
{ "currenttime", VTR_CURTIME },
{ "currenttimeint64", VTR_CURTIME },
{ "getlasterror", VTR_LASTERR },
};
int onOff = 1;
while( zArg[0] ){
int jj, n;
while( zArg[0]!=0 && zArg[0]!='-' && zArg[0]!='+'
&& !isalpha(zArg[0]) ) zArg++;
if( zArg[0]==0 ) break;
if( zArg[0]=='-' ){
onOff = 0;
zArg++;
}else if( zArg[0]=='+' ){
onOff = 1;
zArg++;
}
while( !isalpha(zArg[0]) ){
if( zArg[0]==0 ) break;
zArg++;
}
if( zArg[0]=='x' && isalpha(zArg[1]) ) zArg++;
for(n=0; isalpha(zArg[n]); n++){}
for(jj=0; jj<(int)(sizeof(aKw)/sizeof(aKw[0])); jj++){
if( sqlite3_strnicmp(aKw[jj].z,(const char*)zArg,n)==0 ){
if( onOff ){
pInfo->mTrace |= aKw[jj].m;
}else{
pInfo->mTrace &= ~aKw[jj].m;
}
break;
}
}
zArg += n;
}
}
}
sqlite3_snprintf(sizeof(zBuf), zBuf, "PRAGMA,[%s,%s]",a[1],a[2]);
zOp = zBuf;
break;
}
case SQLITE_FCNTL_BUSYHANDLER: zOp = "BUSYHANDLER"; break;
case SQLITE_FCNTL_TEMPFILENAME: zOp = "TEMPFILENAME"; break;
case SQLITE_FCNTL_MMAP_SIZE: {
|
| ︙ | ︙ | |||
16728 16729 16730 16731 16732 16733 16734 16735 16736 16737 16738 16739 16740 16741 16742 16743 16744 16745 16746 16747 16748 16749 16750 16751 16752 16753 16754 16755 16756 16757 16758 16759 16760 16761 16762 16763 16764 16765 16766 16767 16768 16769 16770 16771 |
/*
** Return the sector-size in bytes for an vfstrace-file.
*/
static int vfstraceSectorSize(sqlite3_file *pFile){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstrace_printf(pInfo, "%s.xSectorSize(%s)", pInfo->zVfsName, p->zFName);
rc = p->pReal->pMethods->xSectorSize(p->pReal);
vfstrace_printf(pInfo, " -> %d\n", rc);
return rc;
}
/*
** Return the device characteristic flags supported by an vfstrace-file.
*/
static int vfstraceDeviceCharacteristics(sqlite3_file *pFile){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstrace_printf(pInfo, "%s.xDeviceCharacteristics(%s)",
pInfo->zVfsName, p->zFName);
rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
vfstrace_printf(pInfo, " -> 0x%08x\n", rc);
return rc;
}
/*
** Shared-memory operations.
*/
static int vfstraceShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
char zLck[100];
int i = 0;
memcpy(zLck, "|0", 3);
if( flags & SQLITE_SHM_UNLOCK ) strappend(zLck, &i, "|UNLOCK");
if( flags & SQLITE_SHM_LOCK ) strappend(zLck, &i, "|LOCK");
if( flags & SQLITE_SHM_SHARED ) strappend(zLck, &i, "|SHARED");
if( flags & SQLITE_SHM_EXCLUSIVE ) strappend(zLck, &i, "|EXCLUSIVE");
if( flags & ~(0xf) ){
sqlite3_snprintf(sizeof(zLck)-i, &zLck[i], "|0x%x", flags);
}
| > > > > > > > > > > > > > > | | > > > > > > > > > | 16900 16901 16902 16903 16904 16905 16906 16907 16908 16909 16910 16911 16912 16913 16914 16915 16916 16917 16918 16919 16920 16921 16922 16923 16924 16925 16926 16927 16928 16929 16930 16931 16932 16933 16934 16935 16936 16937 16938 16939 16940 16941 16942 16943 16944 16945 16946 16947 16948 16949 16950 16951 16952 16953 16954 16955 16956 16957 16958 16959 16960 16961 16962 16963 16964 16965 16966 16967 16968 16969 16970 16971 16972 16973 16974 16975 16976 16977 16978 16979 16980 16981 16982 16983 16984 16985 16986 16987 16988 16989 16990 16991 16992 16993 16994 16995 16996 16997 16998 16999 17000 17001 17002 17003 17004 17005 |
/*
** Return the sector-size in bytes for an vfstrace-file.
*/
static int vfstraceSectorSize(sqlite3_file *pFile){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_SECSZ);
vfstrace_printf(pInfo, "%s.xSectorSize(%s)", pInfo->zVfsName, p->zFName);
rc = p->pReal->pMethods->xSectorSize(p->pReal);
vfstrace_printf(pInfo, " -> %d\n", rc);
return rc;
}
/*
** Return the device characteristic flags supported by an vfstrace-file.
*/
static int vfstraceDeviceCharacteristics(sqlite3_file *pFile){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_DEVCHAR);
vfstrace_printf(pInfo, "%s.xDeviceCharacteristics(%s)",
pInfo->zVfsName, p->zFName);
rc = p->pReal->pMethods->xDeviceCharacteristics(p->pReal);
vfstrace_printf(pInfo, " -> 0x%08x\n", rc);
return rc;
}
/*
** Shared-memory operations.
*/
static int vfstraceShmLock(sqlite3_file *pFile, int ofst, int n, int flags){
static const char *azLockName[] = {
"WRITE",
"CKPT",
"RECOVER",
"READ0",
"READ1",
"READ2",
"READ3",
"READ4",
};
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
char zLck[100];
int i = 0;
vfstraceOnOff(pInfo, VTR_SHMLOCK);
memcpy(zLck, "|0", 3);
if( flags & SQLITE_SHM_UNLOCK ) strappend(zLck, &i, "|UNLOCK");
if( flags & SQLITE_SHM_LOCK ) strappend(zLck, &i, "|LOCK");
if( flags & SQLITE_SHM_SHARED ) strappend(zLck, &i, "|SHARED");
if( flags & SQLITE_SHM_EXCLUSIVE ) strappend(zLck, &i, "|EXCLUSIVE");
if( flags & ~(0xf) ){
sqlite3_snprintf(sizeof(zLck)-i, &zLck[i], "|0x%x", flags);
}
if( ofst>=0 && ofst<(int)(sizeof(azLockName)/sizeof(azLockName[0])) ){
vfstrace_printf(pInfo, "%s.xShmLock(%s,ofst=%d(%s),n=%d,%s)",
pInfo->zVfsName, p->zFName, ofst, azLockName[ofst],
n, &zLck[1]);
}else{
vfstrace_printf(pInfo, "%s.xShmLock(%s,ofst=5d,n=%d,%s)",
pInfo->zVfsName, p->zFName, ofst,
n, &zLck[1]);
}
rc = p->pReal->pMethods->xShmLock(p->pReal, ofst, n, flags);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
static int vfstraceShmMap(
sqlite3_file *pFile,
int iRegion,
int szRegion,
int isWrite,
void volatile **pp
){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_SHMMAP);
vfstrace_printf(pInfo, "%s.xShmMap(%s,iRegion=%d,szRegion=%d,isWrite=%d,*)",
pInfo->zVfsName, p->zFName, iRegion, szRegion, isWrite);
rc = p->pReal->pMethods->xShmMap(p->pReal, iRegion, szRegion, isWrite, pp);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
static void vfstraceShmBarrier(sqlite3_file *pFile){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
vfstraceOnOff(pInfo, VTR_SHMBAR);
vfstrace_printf(pInfo, "%s.xShmBarrier(%s)\n", pInfo->zVfsName, p->zFName);
p->pReal->pMethods->xShmBarrier(p->pReal);
}
static int vfstraceShmUnmap(sqlite3_file *pFile, int delFlag){
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = p->pInfo;
int rc;
vfstraceOnOff(pInfo, VTR_SHMUNMAP);
vfstrace_printf(pInfo, "%s.xShmUnmap(%s,delFlag=%d)",
pInfo->zVfsName, p->zFName, delFlag);
rc = p->pReal->pMethods->xShmUnmap(p->pReal, delFlag);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
|
| ︙ | ︙ | |||
16824 16825 16826 16827 16828 16829 16830 16831 16832 16833 16834 16835 16836 16837 |
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
p->pInfo = pInfo;
p->zFName = zName ? fileTail(zName) : "<temp>";
p->pReal = (sqlite3_file *)&p[1];
rc = pRoot->xOpen(pRoot, zName, p->pReal, flags, pOutFlags);
vfstrace_printf(pInfo, "%s.xOpen(%s,flags=0x%x)",
pInfo->zVfsName, p->zFName, flags);
if( p->pReal->pMethods ){
sqlite3_io_methods *pNew = sqlite3_malloc( sizeof(*pNew) );
const sqlite3_io_methods *pSub = p->pReal->pMethods;
memset(pNew, 0, sizeof(*pNew));
pNew->iVersion = pSub->iVersion;
| > | 17019 17020 17021 17022 17023 17024 17025 17026 17027 17028 17029 17030 17031 17032 17033 |
vfstrace_file *p = (vfstrace_file *)pFile;
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
p->pInfo = pInfo;
p->zFName = zName ? fileTail(zName) : "<temp>";
p->pReal = (sqlite3_file *)&p[1];
rc = pRoot->xOpen(pRoot, zName, p->pReal, flags, pOutFlags);
vfstraceOnOff(pInfo, VTR_OPEN);
vfstrace_printf(pInfo, "%s.xOpen(%s,flags=0x%x)",
pInfo->zVfsName, p->zFName, flags);
if( p->pReal->pMethods ){
sqlite3_io_methods *pNew = sqlite3_malloc( sizeof(*pNew) );
const sqlite3_io_methods *pSub = p->pReal->pMethods;
memset(pNew, 0, sizeof(*pNew));
pNew->iVersion = pSub->iVersion;
|
| ︙ | ︙ | |||
16869 16870 16871 16872 16873 16874 16875 16876 16877 16878 16879 16880 16881 16882 16883 16884 16885 16886 16887 16888 16889 16890 16891 16892 16893 16894 16895 16896 16897 16898 16899 16900 16901 16902 |
** ensure the file-system modifications are synced to disk before
** returning.
*/
static int vfstraceDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
int rc;
vfstrace_printf(pInfo, "%s.xDelete(\"%s\",%d)",
pInfo->zVfsName, zPath, dirSync);
rc = pRoot->xDelete(pRoot, zPath, dirSync);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
/*
** Test for access permissions. Return true if the requested permission
** is available, or false otherwise.
*/
static int vfstraceAccess(
sqlite3_vfs *pVfs,
const char *zPath,
int flags,
int *pResOut
){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
int rc;
vfstrace_printf(pInfo, "%s.xAccess(\"%s\",%d)",
pInfo->zVfsName, zPath, flags);
rc = pRoot->xAccess(pRoot, zPath, flags, pResOut);
vfstrace_print_errcode(pInfo, " -> %s", rc);
vfstrace_printf(pInfo, ", out=%d\n", *pResOut);
return rc;
}
| > > | 17065 17066 17067 17068 17069 17070 17071 17072 17073 17074 17075 17076 17077 17078 17079 17080 17081 17082 17083 17084 17085 17086 17087 17088 17089 17090 17091 17092 17093 17094 17095 17096 17097 17098 17099 17100 |
** ensure the file-system modifications are synced to disk before
** returning.
*/
static int vfstraceDelete(sqlite3_vfs *pVfs, const char *zPath, int dirSync){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
int rc;
vfstraceOnOff(pInfo, VTR_DELETE);
vfstrace_printf(pInfo, "%s.xDelete(\"%s\",%d)",
pInfo->zVfsName, zPath, dirSync);
rc = pRoot->xDelete(pRoot, zPath, dirSync);
vfstrace_print_errcode(pInfo, " -> %s\n", rc);
return rc;
}
/*
** Test for access permissions. Return true if the requested permission
** is available, or false otherwise.
*/
static int vfstraceAccess(
sqlite3_vfs *pVfs,
const char *zPath,
int flags,
int *pResOut
){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
int rc;
vfstraceOnOff(pInfo, VTR_ACCESS);
vfstrace_printf(pInfo, "%s.xAccess(\"%s\",%d)",
pInfo->zVfsName, zPath, flags);
rc = pRoot->xAccess(pRoot, zPath, flags, pResOut);
vfstrace_print_errcode(pInfo, " -> %s", rc);
vfstrace_printf(pInfo, ", out=%d\n", *pResOut);
return rc;
}
|
| ︙ | ︙ | |||
16911 16912 16913 16914 16915 16916 16917 16918 16919 16920 16921 16922 16923 16924 16925 16926 16927 16928 16929 16930 16931 16932 16933 16934 16935 16936 16937 16938 16939 16940 16941 16942 16943 16944 16945 16946 16947 16948 16949 16950 |
const char *zPath,
int nOut,
char *zOut
){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
int rc;
vfstrace_printf(pInfo, "%s.xFullPathname(\"%s\")",
pInfo->zVfsName, zPath);
rc = pRoot->xFullPathname(pRoot, zPath, nOut, zOut);
vfstrace_print_errcode(pInfo, " -> %s", rc);
vfstrace_printf(pInfo, ", out=\"%.*s\"\n", nOut, zOut);
return rc;
}
/*
** Open the dynamic library located at zPath and return a handle.
*/
static void *vfstraceDlOpen(sqlite3_vfs *pVfs, const char *zPath){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
vfstrace_printf(pInfo, "%s.xDlOpen(\"%s\")\n", pInfo->zVfsName, zPath);
return pRoot->xDlOpen(pRoot, zPath);
}
/*
** Populate the buffer zErrMsg (size nByte bytes) with a human readable
** utf-8 string describing the most recent error encountered associated
** with dynamic libraries.
*/
static void vfstraceDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
vfstrace_printf(pInfo, "%s.xDlError(%d)", pInfo->zVfsName, nByte);
pRoot->xDlError(pRoot, nByte, zErrMsg);
vfstrace_printf(pInfo, " -> \"%s\"", zErrMsg);
}
/*
** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
| > > > | 17109 17110 17111 17112 17113 17114 17115 17116 17117 17118 17119 17120 17121 17122 17123 17124 17125 17126 17127 17128 17129 17130 17131 17132 17133 17134 17135 17136 17137 17138 17139 17140 17141 17142 17143 17144 17145 17146 17147 17148 17149 17150 17151 |
const char *zPath,
int nOut,
char *zOut
){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
int rc;
vfstraceOnOff(pInfo, VTR_FULLPATH);
vfstrace_printf(pInfo, "%s.xFullPathname(\"%s\")",
pInfo->zVfsName, zPath);
rc = pRoot->xFullPathname(pRoot, zPath, nOut, zOut);
vfstrace_print_errcode(pInfo, " -> %s", rc);
vfstrace_printf(pInfo, ", out=\"%.*s\"\n", nOut, zOut);
return rc;
}
/*
** Open the dynamic library located at zPath and return a handle.
*/
static void *vfstraceDlOpen(sqlite3_vfs *pVfs, const char *zPath){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
vfstraceOnOff(pInfo, VTR_DLOPEN);
vfstrace_printf(pInfo, "%s.xDlOpen(\"%s\")\n", pInfo->zVfsName, zPath);
return pRoot->xDlOpen(pRoot, zPath);
}
/*
** Populate the buffer zErrMsg (size nByte bytes) with a human readable
** utf-8 string describing the most recent error encountered associated
** with dynamic libraries.
*/
static void vfstraceDlError(sqlite3_vfs *pVfs, int nByte, char *zErrMsg){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
vfstraceOnOff(pInfo, VTR_DLERR);
vfstrace_printf(pInfo, "%s.xDlError(%d)", pInfo->zVfsName, nByte);
pRoot->xDlError(pRoot, nByte, zErrMsg);
vfstrace_printf(pInfo, " -> \"%s\"", zErrMsg);
}
/*
** Return a pointer to the symbol zSymbol in the dynamic library pHandle.
|
| ︙ | ︙ | |||
16958 16959 16960 16961 16962 16963 16964 16965 16966 16967 16968 16969 16970 16971 16972 16973 16974 16975 16976 16977 16978 16979 16980 16981 16982 16983 16984 16985 16986 16987 16988 16989 16990 16991 16992 16993 16994 16995 |
/*
** Close the dynamic library handle pHandle.
*/
static void vfstraceDlClose(sqlite3_vfs *pVfs, void *pHandle){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
vfstrace_printf(pInfo, "%s.xDlOpen()\n", pInfo->zVfsName);
pRoot->xDlClose(pRoot, pHandle);
}
/*
** Populate the buffer pointed to by zBufOut with nByte bytes of
** random data.
*/
static int vfstraceRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
vfstrace_printf(pInfo, "%s.xRandomness(%d)\n", pInfo->zVfsName, nByte);
return pRoot->xRandomness(pRoot, nByte, zBufOut);
}
/*
** Sleep for nMicro microseconds. Return the number of microseconds
** actually slept.
*/
static int vfstraceSleep(sqlite3_vfs *pVfs, int nMicro){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
return pRoot->xSleep(pRoot, nMicro);
}
/*
** Return the current time as a Julian Day number in *pTimeOut.
*/
static int vfstraceCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
| > > > > > > > | > > > > > | > > | | > > > > | > > | 17159 17160 17161 17162 17163 17164 17165 17166 17167 17168 17169 17170 17171 17172 17173 17174 17175 17176 17177 17178 17179 17180 17181 17182 17183 17184 17185 17186 17187 17188 17189 17190 17191 17192 17193 17194 17195 17196 17197 17198 17199 17200 17201 17202 17203 17204 17205 17206 17207 17208 17209 17210 17211 17212 17213 17214 17215 17216 17217 17218 17219 17220 17221 17222 17223 17224 17225 17226 17227 17228 17229 17230 17231 17232 17233 17234 17235 17236 17237 17238 |
/*
** Close the dynamic library handle pHandle.
*/
static void vfstraceDlClose(sqlite3_vfs *pVfs, void *pHandle){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
vfstraceOnOff(pInfo, VTR_DLCLOSE);
vfstrace_printf(pInfo, "%s.xDlOpen()\n", pInfo->zVfsName);
pRoot->xDlClose(pRoot, pHandle);
}
/*
** Populate the buffer pointed to by zBufOut with nByte bytes of
** random data.
*/
static int vfstraceRandomness(sqlite3_vfs *pVfs, int nByte, char *zBufOut){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
vfstraceOnOff(pInfo, VTR_RAND);
vfstrace_printf(pInfo, "%s.xRandomness(%d)\n", pInfo->zVfsName, nByte);
return pRoot->xRandomness(pRoot, nByte, zBufOut);
}
/*
** Sleep for nMicro microseconds. Return the number of microseconds
** actually slept.
*/
static int vfstraceSleep(sqlite3_vfs *pVfs, int nMicro){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
vfstraceOnOff(pInfo, VTR_SLEEP);
vfstrace_printf(pInfo, "%s.xSleep(%d)\n", pInfo->zVfsName, nMicro);
return pRoot->xSleep(pRoot, nMicro);
}
/*
** Return the current time as a Julian Day number in *pTimeOut.
*/
static int vfstraceCurrentTime(sqlite3_vfs *pVfs, double *pTimeOut){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
int rc;
vfstraceOnOff(pInfo, VTR_CURTIME);
vfstrace_printf(pInfo, "%s.xCurrentTime()", pInfo->zVfsName);
rc = pRoot->xCurrentTime(pRoot, pTimeOut);
vfstrace_printf(pInfo, " -> %.17g\n", *pTimeOut);
return rc;
}
static int vfstraceCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
int rc;
vfstraceOnOff(pInfo, VTR_CURTIME);
vfstrace_printf(pInfo, "%s.xCurrentTimeInt64()", pInfo->zVfsName);
rc = pRoot->xCurrentTimeInt64(pRoot, pTimeOut);
vfstrace_printf(pInfo, " -> %lld\n", *pTimeOut);
return rc;
}
/*
** Return the most recent error code and message
*/
static int vfstraceGetLastError(sqlite3_vfs *pVfs, int nErr, char *zErr){
vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
sqlite3_vfs *pRoot = pInfo->pRootVfs;
int rc;
vfstraceOnOff(pInfo, VTR_LASTERR);
vfstrace_printf(pInfo, "%s.xGetLastError(%d,zBuf)", pInfo->zVfsName, nErr);
if( nErr ) zErr[0] = 0;
rc = pRoot->xGetLastError(pRoot, nErr, zErr);
vfstrace_printf(pInfo, " -> zBuf[] = \"%s\", rc = %d\n", nErr?zErr:"", rc);
return rc;
}
/*
** Override system calls.
*/
static int vfstraceSetSystemCall(
sqlite3_vfs *pVfs,
|
| ︙ | ︙ | |||
17097 17098 17099 17100 17101 17102 17103 17104 17105 17106 17107 17108 17109 17110 |
}
}
pInfo->pRootVfs = pRoot;
pInfo->xOut = xOut;
pInfo->pOutArg = pOutArg;
pInfo->zVfsName = pNew->zName;
pInfo->pTraceVfs = pNew;
vfstrace_printf(pInfo, "%s.enabled_for(\"%s\")\n",
pInfo->zVfsName, pRoot->zName);
return sqlite3_vfs_register(pNew, makeDefault);
}
/*
** Look for the named VFS. If it is a TRACEVFS, then unregister it
| > > | 17318 17319 17320 17321 17322 17323 17324 17325 17326 17327 17328 17329 17330 17331 17332 17333 |
}
}
pInfo->pRootVfs = pRoot;
pInfo->xOut = xOut;
pInfo->pOutArg = pOutArg;
pInfo->zVfsName = pNew->zName;
pInfo->pTraceVfs = pNew;
pInfo->mTrace = 0xffffffff;
pInfo->bOn = 1;
vfstrace_printf(pInfo, "%s.enabled_for(\"%s\")\n",
pInfo->zVfsName, pRoot->zName);
return sqlite3_vfs_register(pNew, makeDefault);
}
/*
** Look for the named VFS. If it is a TRACEVFS, then unregister it
|
| ︙ | ︙ | |||
20226 20227 20228 20229 20230 20231 20232 20233 20234 20235 20236 20237 20238 20239 |
}else if( iField<pTab->nCol ){
assert( apVal[iField]==0 );
apVal[iField] = sqlite3_value_dup( pVal );
if( apVal[iField]==0 ){
recoverError(p, SQLITE_NOMEM, 0);
}
p1->nVal = iField+1;
}
p1->iPrevCell = iCell;
p1->iPrevPage = iPage;
}
}else{
recoverReset(p, pSel);
p1->pTab = 0;
| > > | 20449 20450 20451 20452 20453 20454 20455 20456 20457 20458 20459 20460 20461 20462 20463 20464 |
}else if( iField<pTab->nCol ){
assert( apVal[iField]==0 );
apVal[iField] = sqlite3_value_dup( pVal );
if( apVal[iField]==0 ){
recoverError(p, SQLITE_NOMEM, 0);
}
p1->nVal = iField+1;
}else if( pTab->nCol==0 ){
p1->nVal = pTab->nCol;
}
p1->iPrevCell = iCell;
p1->iPrevPage = iPage;
}
}else{
recoverReset(p, pSel);
p1->pTab = 0;
|
| ︙ | ︙ | |||
21933 21934 21935 21936 21937 21938 21939 |
** Limit: nAccept>=0 => char count, nAccept<0 => character
*/
const char *zSkipValidUtf8(const char *z, int nAccept, long ccm){
int ng = (nAccept<0)? -nAccept : 0;
const char *pcLimit = (nAccept>=0)? z+nAccept : 0;
assert(z!=0);
while( (pcLimit)? (z<pcLimit) : (ng-- != 0) ){
| | | | 22158 22159 22160 22161 22162 22163 22164 22165 22166 22167 22168 22169 22170 22171 22172 22173 |
** Limit: nAccept>=0 => char count, nAccept<0 => character
*/
const char *zSkipValidUtf8(const char *z, int nAccept, long ccm){
int ng = (nAccept<0)? -nAccept : 0;
const char *pcLimit = (nAccept>=0)? z+nAccept : 0;
assert(z!=0);
while( (pcLimit)? (z<pcLimit) : (ng-- != 0) ){
unsigned char c = *(u8*)z;
if( c<0x7f ){
if( ccm != 0L && c < 0x20 && ((1L<<c) & ccm) != 0 ) return z;
++z; /* ASCII */
}else if( (c & 0xC0) != 0xC0 ) return z; /* not a lead byte */
else{
const char *zt = z+1; /* Got lead byte, look at trail bytes.*/
do{
if( pcLimit && zt >= pcLimit ) return z;
|
| ︙ | ︙ | |||
22002 22003 22004 22005 22006 22007 22008 |
}
z = pcEnd;
}
sqlite3_fputs(zq, out);
}
/*
| | | | | | | 22227 22228 22229 22230 22231 22232 22233 22234 22235 22236 22237 22238 22239 22240 22241 22242 22243 22244 22245 22246 22247 22248 22249 22250 22251 22252 22253 22254 22255 22256 22257 22258 22259 22260 22261 22262 22263 22264 22265 22266 22267 22268 22269 22270 22271 22272 22273 22274 22275 22276 22277 22278 22279 22280 |
}
z = pcEnd;
}
sqlite3_fputs(zq, out);
}
/*
** Output the given string as quoted according to JSON quoting rules.
*/
static void output_json_string(FILE *out, const char *z, i64 n){
unsigned char c;
static const char *zq = "\"";
static long ctrlMask = ~0L;
static const char *zDQBS = "\"\\";
const char *pcLimit;
char ace[3] = "\\?";
char cbsSay;
if( z==0 ) z = "";
pcLimit = z + ((n<0)? strlen(z) : (size_t)n);
sqlite3_fputs(zq, out);
while( z < pcLimit ){
const char *pcDQBS = anyOfInStr(z, zDQBS, pcLimit-z);
const char *pcPast = zSkipValidUtf8(z, (int)(pcLimit-z), ctrlMask);
const char *pcEnd = (pcDQBS && pcDQBS < pcPast)? pcDQBS : pcPast;
if( pcEnd > z ){
sqlite3_fprintf(out, "%.*s", (int)(pcEnd-z), z);
z = pcEnd;
}
if( z >= pcLimit ) break;
c = (unsigned char)*(z++);
switch( c ){
case '"': case '\\':
cbsSay = (char)c;
break;
case '\b': cbsSay = 'b'; break;
case '\f': cbsSay = 'f'; break;
case '\n': cbsSay = 'n'; break;
case '\r': cbsSay = 'r'; break;
case '\t': cbsSay = 't'; break;
default: cbsSay = 0; break;
}
if( cbsSay ){
ace[1] = cbsSay;
sqlite3_fputs(ace, out);
}else if( c<=0x1f || c>=0x7f ){
sqlite3_fprintf(out, "\\u%04x", c);
}else{
ace[1] = (char)c;
sqlite3_fputs(ace+1, out);
}
}
sqlite3_fputs(zq, out);
}
|
| ︙ | ︙ | |||
24782 24783 24784 24785 24786 24787 24788 |
sqlite3_snprintf(len+100, zQ2, "%s ORDER BY rowid DESC", zQuery);
rc = sqlite3_exec(p->db, zQ2, dump_callback, p, &zErr);
if( rc ){
sqlite3_fprintf(p->out, "/****** ERROR: %s ******/\n", zErr);
}else{
rc = SQLITE_CORRUPT;
}
| < > | 25007 25008 25009 25010 25011 25012 25013 25014 25015 25016 25017 25018 25019 25020 25021 25022 25023 |
sqlite3_snprintf(len+100, zQ2, "%s ORDER BY rowid DESC", zQuery);
rc = sqlite3_exec(p->db, zQ2, dump_callback, p, &zErr);
if( rc ){
sqlite3_fprintf(p->out, "/****** ERROR: %s ******/\n", zErr);
}else{
rc = SQLITE_CORRUPT;
}
free(zQ2);
}
sqlite3_free(zErr);
return rc;
}
/*
** Text of help messages.
**
** The help text for each individual command begins with a line that starts
|
| ︙ | ︙ | |||
24847 24848 24849 24850 24851 24852 24853 24854 24855 24856 24857 24858 24859 24860 | ".connection [close] [#] Open or close an auxiliary database connection", ".crlf ?on|off? Whether or not to use \\r\\n line endings", ".databases List names and files of attached databases", ".dbconfig ?op? ?val? List or change sqlite3_db_config() options", #if SQLITE_SHELL_HAVE_RECOVER ".dbinfo ?DB? Show status information about the database", #endif ".dump ?OBJECTS? Render database content as SQL", " Options:", " --data-only Output only INSERT statements", " --newlines Allow unescaped newline characters in output", " --nosys Omit system tables (ex: \"sqlite_stat1\")", " --preserve-rowids Include ROWID values in the output", " OBJECTS is a LIKE pattern for tables, indexes, triggers or views to dump", | > | 25072 25073 25074 25075 25076 25077 25078 25079 25080 25081 25082 25083 25084 25085 25086 | ".connection [close] [#] Open or close an auxiliary database connection", ".crlf ?on|off? Whether or not to use \\r\\n line endings", ".databases List names and files of attached databases", ".dbconfig ?op? ?val? List or change sqlite3_db_config() options", #if SQLITE_SHELL_HAVE_RECOVER ".dbinfo ?DB? Show status information about the database", #endif ".dbtotxt Hex dump of the database file", ".dump ?OBJECTS? Render database content as SQL", " Options:", " --data-only Output only INSERT statements", " --newlines Allow unescaped newline characters in output", " --nosys Omit system tables (ex: \"sqlite_stat1\")", " --preserve-rowids Include ROWID values in the output", " OBJECTS is a LIKE pattern for tables, indexes, triggers or views to dump", |
| ︙ | ︙ | |||
25652 25653 25654 25655 25656 25657 25658 |
int rc = sqlite3_close(db);
if( rc ){
sqlite3_fprintf(stderr,
"Error: sqlite3_close() returns %d: %s\n", rc, sqlite3_errmsg(db));
}
}
| | > | 25878 25879 25880 25881 25882 25883 25884 25885 25886 25887 25888 25889 25890 25891 25892 25893 |
int rc = sqlite3_close(db);
if( rc ){
sqlite3_fprintf(stderr,
"Error: sqlite3_close() returns %d: %s\n", rc, sqlite3_errmsg(db));
}
}
#if (HAVE_READLINE || HAVE_EDITLINE) \
&& !defined(SQLITE_OMIT_READLINE_COMPLETION)
/*
** Readline completion callbacks
*/
static char *readline_completion_generator(const char *text, int state){
static sqlite3_stmt *pStmt = 0;
char *zRet;
if( state==0 ){
|
| ︙ | ︙ | |||
25690 25691 25692 25693 25694 25695 25696 | } #elif HAVE_LINENOISE /* ** Linenoise completion callback. Note that the 3rd argument is from ** the "msteveb" version of linenoise, not the "antirez" version. */ | | > > > | > > > > | 25917 25918 25919 25920 25921 25922 25923 25924 25925 25926 25927 25928 25929 25930 25931 25932 25933 25934 25935 25936 25937 25938 25939 25940 25941 25942 25943 25944 25945 25946 |
}
#elif HAVE_LINENOISE
/*
** Linenoise completion callback. Note that the 3rd argument is from
** the "msteveb" version of linenoise, not the "antirez" version.
*/
static void linenoise_completion(
const char *zLine,
linenoiseCompletions *lc
#if HAVE_LINENOISE==2
,void *pUserData
#endif
){
i64 nLine = strlen(zLine);
i64 i, iStart;
sqlite3_stmt *pStmt = 0;
char *zSql;
char zBuf[1000];
#if HAVE_LINENOISE==2
UNUSED_PARAMETER(pUserData);
#endif
if( nLine>(i64)sizeof(zBuf)-30 ) return;
if( zLine[0]=='.' || zLine[0]=='#') return;
for(i=nLine-1; i>=0 && (isalnum(zLine[i]) || zLine[i]=='_'); i--){}
if( i==nLine-1 ) return;
iStart = i+1;
memcpy(zBuf, zLine, iStart);
zSql = sqlite3_mprintf("SELECT DISTINCT candidate COLLATE nocase"
|
| ︙ | ︙ | |||
26388 26389 26390 26391 26392 26393 26394 | # define output_redir(SS,pfO) # define output_reset(SS) #endif /* ** Run an SQL command and return the single integer result. */ | | > > > > > | > | 26622 26623 26624 26625 26626 26627 26628 26629 26630 26631 26632 26633 26634 26635 26636 26637 26638 26639 26640 26641 26642 26643 26644 26645 26646 26647 26648 26649 |
# define output_redir(SS,pfO)
# define output_reset(SS)
#endif
/*
** Run an SQL command and return the single integer result.
*/
static int db_int(sqlite3 *db, const char *zSql, ...){
sqlite3_stmt *pStmt;
int res = 0;
char *z;
va_list ap;
va_start(ap, zSql);
z = sqlite3_vmprintf(zSql, ap);
va_end(ap);
sqlite3_prepare_v2(db, z, -1, &pStmt, 0);
if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
res = sqlite3_column_int(pStmt,0);
}
sqlite3_finalize(pStmt);
sqlite3_free(z);
return res;
}
#if SQLITE_SHELL_HAVE_RECOVER
/*
** Convert a 2-byte or 4-byte big-endian integer into a native integer
*/
|
| ︙ | ︙ | |||
26498 26499 26500 26501 26502 26503 26504 |
zSchemaTab = sqlite3_mprintf("main.sqlite_schema");
}else if( cli_strcmp(zDb,"temp")==0 ){
zSchemaTab = sqlite3_mprintf("%s", "sqlite_temp_schema");
}else{
zSchemaTab = sqlite3_mprintf("\"%w\".sqlite_schema", zDb);
}
for(i=0; i<ArraySize(aQuery); i++){
| < | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 26738 26739 26740 26741 26742 26743 26744 26745 26746 26747 26748 26749 26750 26751 26752 26753 26754 26755 26756 26757 26758 26759 26760 26761 26762 26763 26764 26765 26766 26767 26768 26769 26770 26771 26772 26773 26774 26775 26776 26777 26778 26779 26780 26781 26782 26783 26784 26785 26786 26787 26788 26789 26790 26791 26792 26793 26794 26795 26796 26797 26798 26799 26800 26801 26802 26803 26804 26805 26806 26807 26808 26809 26810 26811 26812 26813 26814 26815 26816 26817 26818 26819 26820 26821 26822 26823 26824 26825 26826 26827 26828 26829 26830 26831 26832 26833 26834 26835 26836 26837 26838 26839 26840 26841 26842 26843 26844 26845 26846 26847 26848 26849 26850 26851 26852 26853 |
zSchemaTab = sqlite3_mprintf("main.sqlite_schema");
}else if( cli_strcmp(zDb,"temp")==0 ){
zSchemaTab = sqlite3_mprintf("%s", "sqlite_temp_schema");
}else{
zSchemaTab = sqlite3_mprintf("\"%w\".sqlite_schema", zDb);
}
for(i=0; i<ArraySize(aQuery); i++){
int val = db_int(p->db, aQuery[i].zSql, zSchemaTab);
sqlite3_fprintf(p->out, "%-20s %d\n", aQuery[i].zName, val);
}
sqlite3_free(zSchemaTab);
sqlite3_file_control(p->db, zDb, SQLITE_FCNTL_DATA_VERSION, &iDataVersion);
sqlite3_fprintf(p->out, "%-20s %u\n", "data version", iDataVersion);
return 0;
}
#endif /* SQLITE_SHELL_HAVE_RECOVER */
/*
** Implementation of the ".dbtotxt" command.
**
** Return 1 on error, 2 to exit, and 0 otherwise.
*/
static int shell_dbtotxt_command(ShellState *p, int nArg, char **azArg){
sqlite3_stmt *pStmt = 0;
sqlite3_int64 nPage = 0;
int pgSz = 0;
const char *zTail;
char *zName = 0;
int rc, i, j;
unsigned char bShow[256]; /* Characters ok to display */
UNUSED_PARAMETER(nArg);
UNUSED_PARAMETER(azArg);
memset(bShow, '.', sizeof(bShow));
for(i=' '; i<='~'; i++){
if( i!='{' && i!='}' && i!='"' && i!='\\' ) bShow[i] = (unsigned char)i;
}
rc = sqlite3_prepare_v2(p->db, "PRAGMA page_size", -1, &pStmt, 0);
if( rc ) goto dbtotxt_error;
rc = 0;
if( sqlite3_step(pStmt)!=SQLITE_ROW ) goto dbtotxt_error;
pgSz = sqlite3_column_int(pStmt, 0);
sqlite3_finalize(pStmt);
pStmt = 0;
if( pgSz<512 || pgSz>65536 || (pgSz&(pgSz-1))!=0 ) goto dbtotxt_error;
rc = sqlite3_prepare_v2(p->db, "PRAGMA page_count", -1, &pStmt, 0);
if( rc ) goto dbtotxt_error;
rc = 0;
if( sqlite3_step(pStmt)!=SQLITE_ROW ) goto dbtotxt_error;
nPage = sqlite3_column_int64(pStmt, 0);
sqlite3_finalize(pStmt);
pStmt = 0;
if( nPage<1 ) goto dbtotxt_error;
rc = sqlite3_prepare_v2(p->db, "PRAGMA databases", -1, &pStmt, 0);
if( rc ) goto dbtotxt_error;
if( sqlite3_step(pStmt)!=SQLITE_ROW ){
zTail = "unk.db";
}else{
const char *zFilename = (const char*)sqlite3_column_text(pStmt, 2);
if( zFilename==0 || zFilename[0]==0 ) zFilename = "unk.db";
zTail = strrchr(zFilename, '/');
#if defined(_WIN32)
if( zTail==0 ) zTail = strrchr(zFilename, '\\');
#endif
}
zName = strdup(zTail);
shell_check_oom(zName);
sqlite3_fprintf(p->out, "| size %lld pagesize %d filename %s\n",
nPage*pgSz, pgSz, zName);
sqlite3_finalize(pStmt);
pStmt = 0;
rc = sqlite3_prepare_v2(p->db,
"SELECT pgno, data FROM sqlite_dbpage ORDER BY pgno", -1, &pStmt, 0);
if( rc ) goto dbtotxt_error;
while( sqlite3_step(pStmt)==SQLITE_ROW ){
sqlite3_int64 pgno = sqlite3_column_int64(pStmt, 0);
const u8 *aData = sqlite3_column_blob(pStmt, 1);
int seenPageLabel = 0;
for(i=0; i<pgSz; i+=16){
const u8 *aLine = aData+i;
for(j=0; j<16 && aLine[j]==0; j++){}
if( j==16 ) continue;
if( !seenPageLabel ){
sqlite3_fprintf(p->out, "| page %lld offset %lld\n", pgno, pgno*pgSz);
seenPageLabel = 1;
}
sqlite3_fprintf(p->out, "| %5d:", i);
for(j=0; j<16; j++) sqlite3_fprintf(p->out, " %02x", aLine[j]);
sqlite3_fprintf(p->out, " ");
for(j=0; j<16; j++){
unsigned char c = (unsigned char)aLine[j];
sqlite3_fprintf(p->out, "%c", bShow[c]);
}
sqlite3_fprintf(p->out, "\n");
}
}
sqlite3_finalize(pStmt);
sqlite3_fprintf(p->out, "| end %s\n", zName);
free(zName);
return 0;
dbtotxt_error:
if( rc ){
sqlite3_fprintf(stderr, "ERROR: %s\n", sqlite3_errmsg(p->db));
}
sqlite3_finalize(pStmt);
free(zName);
return 1;
}
/*
** Print the given string as an error message.
*/
static void shellEmitError(const char *zErr){
sqlite3_fprintf(stderr,"Error: %s\n", zErr);
}
|
| ︙ | ︙ | |||
28065 28066 28067 28068 28069 28070 28071 |
sqlite3_finalize(pStmt);
return 0;
}else if( *pDb==0 ){
return 0;
}else{
/* Formulate the columns spec, close the DB, zero *pDb. */
char *zColsSpec = 0;
| | | | | 28396 28397 28398 28399 28400 28401 28402 28403 28404 28405 28406 28407 28408 28409 28410 28411 28412 28413 28414 28415 28416 28417 28418 28419 28420 28421 28422 28423 28424 28425 28426 |
sqlite3_finalize(pStmt);
return 0;
}else if( *pDb==0 ){
return 0;
}else{
/* Formulate the columns spec, close the DB, zero *pDb. */
char *zColsSpec = 0;
int hasDupes = db_int(*pDb, "%s", zHasDupes);
int nDigits = (hasDupes)? db_int(*pDb, "%s", zColDigits) : 0;
if( hasDupes ){
#ifdef SHELL_COLUMN_RENAME_CLEAN
rc = sqlite3_exec(*pDb, zDedoctor, 0, 0, 0);
rc_err_oom_die(rc);
#endif
rc = sqlite3_exec(*pDb, zSetReps, 0, 0, 0);
rc_err_oom_die(rc);
rc = sqlite3_prepare_v2(*pDb, zRenameRank, -1, &pStmt, 0);
rc_err_oom_die(rc);
sqlite3_bind_int(pStmt, 1, nDigits);
rc = sqlite3_step(pStmt);
sqlite3_finalize(pStmt);
if( rc!=SQLITE_DONE ) rc_err_oom_die(SQLITE_NOMEM);
}
assert(db_int(*pDb, "%s", zHasDupes)==0); /* Consider: remove this */
rc = sqlite3_prepare_v2(*pDb, zCollectVar, -1, &pStmt, 0);
rc_err_oom_die(rc);
rc = sqlite3_step(pStmt);
if( rc==SQLITE_ROW ){
zColsSpec = sqlite3_mprintf("%s", sqlite3_column_text(pStmt, 0));
}else{
zColsSpec = 0;
|
| ︙ | ︙ | |||
28686 28687 28688 28689 28690 28691 28692 28693 28694 28695 28696 28697 28698 28699 |
if( nArg==2 ){
setOrClearFlag(p, SHFLG_Echo, azArg[1]);
}else{
eputz("Usage: .echo on|off\n");
rc = 1;
}
}else
if( c=='e' && cli_strncmp(azArg[0], "eqp", n)==0 ){
if( nArg==2 ){
p->autoEQPtest = 0;
if( p->autoEQPtrace ){
if( p->db ) sqlite3_exec(p->db, "PRAGMA vdbe_trace=OFF;", 0, 0, 0);
p->autoEQPtrace = 0;
| > > > > | 29017 29018 29019 29020 29021 29022 29023 29024 29025 29026 29027 29028 29029 29030 29031 29032 29033 29034 |
if( nArg==2 ){
setOrClearFlag(p, SHFLG_Echo, azArg[1]);
}else{
eputz("Usage: .echo on|off\n");
rc = 1;
}
}else
if( c=='d' && n>=3 && cli_strncmp(azArg[0], "dbtotxt", n)==0 ){
rc = shell_dbtotxt_command(p, nArg, azArg);
}else
if( c=='e' && cli_strncmp(azArg[0], "eqp", n)==0 ){
if( nArg==2 ){
p->autoEQPtest = 0;
if( p->autoEQPtrace ){
if( p->db ) sqlite3_exec(p->db, "PRAGMA vdbe_trace=OFF;", 0, 0, 0);
p->autoEQPtrace = 0;
|
| ︙ | ︙ | |||
29127 29128 29129 29130 29131 29132 29133 |
shell_out_of_memory();
}
/* Below, resources must be freed before exit. */
while( (nSkip--)>0 ){
while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){}
}
import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */
| | > > > > | 29462 29463 29464 29465 29466 29467 29468 29469 29470 29471 29472 29473 29474 29475 29476 29477 29478 29479 29480 |
shell_out_of_memory();
}
/* Below, resources must be freed before exit. */
while( (nSkip--)>0 ){
while( xRead(&sCtx) && sCtx.cTerm==sCtx.cColSep ){}
}
import_append_char(&sCtx, 0); /* To ensure sCtx.z is allocated */
if( sqlite3_table_column_metadata(p->db, zSchema, zTable,0,0,0,0,0,0)
&& 0==db_int(p->db, "SELECT count(*) FROM \"%w\".sqlite_schema"
" WHERE name=%Q AND type='view'",
zSchema ? zSchema : "main", zTable)
){
/* Table does not exist. Create it. */
sqlite3 *dbCols = 0;
char *zRenames = 0;
char *zColDefs;
zCreate = sqlite3_mprintf("CREATE TABLE \"%w\".\"%w\"",
zSchema ? zSchema : "main", zTable);
while( xRead(&sCtx) ){
|
| ︙ | ︙ | |||
30199 30200 30201 30202 30203 30204 30205 |
shellDatabaseError(p->db);
rc = 1;
}
close_db(pSrc);
}else
#endif /* !defined(SQLITE_SHELL_FIDDLE) */
| > | > > | 30538 30539 30540 30541 30542 30543 30544 30545 30546 30547 30548 30549 30550 30551 30552 30553 30554 30555 |
shellDatabaseError(p->db);
rc = 1;
}
close_db(pSrc);
}else
#endif /* !defined(SQLITE_SHELL_FIDDLE) */
if( c=='s' &&
(cli_strncmp(azArg[0], "scanstats", n)==0 ||
cli_strncmp(azArg[0], "scanstatus", n)==0)
){
if( nArg==2 ){
if( cli_strcmp(azArg[1], "vm")==0 ){
p->scanstatsOn = 3;
}else
if( cli_strcmp(azArg[1], "est")==0 ){
p->scanstatsOn = 2;
}else{
|
| ︙ | ︙ | |||
31254 31255 31256 31257 31258 31259 31260 31261 31262 31263 31264 31265 31266 31267 31268 |
{ 0x04000000, 1, "NullUnusedCols" },
{ 0x08000000, 1, "OnePass" },
{ 0x10000000, 1, "OrderBySubq" },
{ 0xffffffff, 0, "All" },
};
unsigned int curOpt;
unsigned int newOpt;
int ii;
sqlite3_test_control(SQLITE_TESTCTRL_GETOPT, p->db, &curOpt);
newOpt = curOpt;
for(ii=2; ii<nArg; ii++){
const char *z = azArg[ii];
int useLabel = 0;
const char *zLabel = 0;
if( (z[0]=='+'|| z[0]=='-') && !IsDigit(z[1]) ){
| > > | 31596 31597 31598 31599 31600 31601 31602 31603 31604 31605 31606 31607 31608 31609 31610 31611 31612 |
{ 0x04000000, 1, "NullUnusedCols" },
{ 0x08000000, 1, "OnePass" },
{ 0x10000000, 1, "OrderBySubq" },
{ 0xffffffff, 0, "All" },
};
unsigned int curOpt;
unsigned int newOpt;
unsigned int m;
int ii;
int nOff;
sqlite3_test_control(SQLITE_TESTCTRL_GETOPT, p->db, &curOpt);
newOpt = curOpt;
for(ii=2; ii<nArg; ii++){
const char *z = azArg[ii];
int useLabel = 0;
const char *zLabel = 0;
if( (z[0]=='+'|| z[0]=='-') && !IsDigit(z[1]) ){
|
| ︙ | ︙ | |||
31295 31296 31297 31298 31299 31300 31301 |
}else{
newOpt |= aLabel[jj].mask;
}
}
}
if( curOpt!=newOpt ){
sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,p->db,newOpt);
| < > > | | | > > | | > > < > | < | | | < > | 31639 31640 31641 31642 31643 31644 31645 31646 31647 31648 31649 31650 31651 31652 31653 31654 31655 31656 31657 31658 31659 31660 31661 31662 31663 31664 31665 31666 31667 31668 31669 31670 31671 31672 31673 31674 |
}else{
newOpt |= aLabel[jj].mask;
}
}
}
if( curOpt!=newOpt ){
sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,p->db,newOpt);
}
for(ii=nOff=0, m=1; ii<32; ii++, m <<= 1){
if( m & newOpt ) nOff++;
}
if( nOff<12 ){
sqlite3_fputs("+All", p->out);
for(ii=0; ii<ArraySize(aLabel); ii++){
if( !aLabel[ii].bDsply ) continue;
if( (newOpt & aLabel[ii].mask)!=0 ){
sqlite3_fprintf(p->out, " -%s", aLabel[ii].zLabel);
}
}
}else{
sqlite3_fputs("-All", p->out);
for(ii=0; ii<ArraySize(aLabel); ii++){
if( !aLabel[ii].bDsply ) continue;
if( (newOpt & aLabel[ii].mask)==0 ){
sqlite3_fprintf(p->out, " +%s", aLabel[ii].zLabel);
}
}
}
sqlite3_fputs("\n", p->out);
rc2 = isOk = 3;
break;
}
/* sqlite3_test_control(int, db, int) */
case SQLITE_TESTCTRL_FK_NO_ACTION:
if( nArg==3 ){
|
| ︙ | ︙ | |||
31650 31651 31652 31653 31654 31655 31656 |
for(ii=1; ii<nArg; ii++){
sqlite3_create_module(p->db, azArg[ii], 0, 0);
}
}
}else
#endif
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 31998 31999 32000 32001 32002 32003 32004 32005 32006 32007 32008 32009 32010 32011 |
for(ii=1; ii<nArg; ii++){
sqlite3_create_module(p->db, azArg[ii], 0, 0);
}
}
}else
#endif
if( c=='v' && cli_strncmp(azArg[0], "version", n)==0 ){
char *zPtrSz = sizeof(void*)==8 ? "64-bit" : "32-bit";
sqlite3_fprintf(p->out, "SQLite %s %s\n" /*extra-version-info*/,
sqlite3_libversion(), sqlite3_sourceid());
#if SQLITE_HAVE_ZLIB
sqlite3_fprintf(p->out, "zlib version %s\n", zlibVersion());
#endif
|
| ︙ | ︙ | |||
31836 31837 31838 31839 31840 31841 31842 |
*/
static QuickScanState quickscan(char *zLine, QuickScanState qss,
SCAN_TRACKER_REFTYPE pst){
char cin;
char cWait = (char)qss; /* intentional narrowing loss */
if( cWait==0 ){
PlainScan:
| < | 32121 32122 32123 32124 32125 32126 32127 32128 32129 32130 32131 32132 32133 32134 |
*/
static QuickScanState quickscan(char *zLine, QuickScanState qss,
SCAN_TRACKER_REFTYPE pst){
char cin;
char cWait = (char)qss; /* intentional narrowing loss */
if( cWait==0 ){
PlainScan:
while( (cin = *zLine++)!=0 ){
if( IsSpace(cin) )
continue;
switch (cin){
case '-':
if( *zLine!='-' )
break;
|
| ︙ | ︙ | |||
31888 31889 31890 31891 31892 31893 31894 |
while( (cin = *zLine++)!=0 ){
if( cin==cWait ){
switch( cWait ){
case '*':
if( *zLine != '/' )
continue;
++zLine;
| < < | 32172 32173 32174 32175 32176 32177 32178 32179 32180 32181 32182 32183 32184 32185 32186 32187 32188 32189 32190 32191 32192 32193 32194 32195 32196 |
while( (cin = *zLine++)!=0 ){
if( cin==cWait ){
switch( cWait ){
case '*':
if( *zLine != '/' )
continue;
++zLine;
CONTINUE_PROMPT_AWAITC(pst, 0);
qss = QSS_SETV(qss, 0);
goto PlainScan;
case '`': case '\'': case '"':
if(*zLine==cWait){
/* Swallow doubled end-delimiter.*/
++zLine;
continue;
}
deliberate_fall_through;
case ']':
CONTINUE_PROMPT_AWAITC(pst, 0);
qss = QSS_SETV(qss, 0);
goto PlainScan;
default: assert(0);
}
}
}
|
| ︙ | ︙ | |||
32088 32089 32090 32091 32092 32093 32094 |
}
if( doAutoDetectRestore(p, zSql) ) return 1;
return 0;
}
static void echo_group_input(ShellState *p, const char *zDo){
| | > > > | 32370 32371 32372 32373 32374 32375 32376 32377 32378 32379 32380 32381 32382 32383 32384 32385 32386 32387 |
}
if( doAutoDetectRestore(p, zSql) ) return 1;
return 0;
}
static void echo_group_input(ShellState *p, const char *zDo){
if( ShellHasFlag(p, SHFLG_Echo) ){
sqlite3_fprintf(p->out, "%s\n", zDo);
fflush(p->out);
}
}
#ifdef SQLITE_SHELL_FIDDLE
/*
** Alternate one_input_line() impl for wasm mode. This is not in the primary
** impl because we need the global shellState and cannot access it from that
** function without moving lots of code around (creating a larger/messier diff).
|
| ︙ | ︙ | |||
32548 32549 32550 32551 32552 32553 32554 32555 32556 32557 32558 32559 32560 32561 |
}
return argv[i];
}
static void sayAbnormalExit(void){
if( seenInterrupt ) eputz("Program interrupted.\n");
}
#ifndef SQLITE_SHELL_IS_UTF8
# if (defined(_WIN32) || defined(WIN32)) \
&& (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__)))
# define SQLITE_SHELL_IS_UTF8 (0)
# else
# define SQLITE_SHELL_IS_UTF8 (1)
| > > > > > > > > > | 32833 32834 32835 32836 32837 32838 32839 32840 32841 32842 32843 32844 32845 32846 32847 32848 32849 32850 32851 32852 32853 32854 32855 |
}
return argv[i];
}
static void sayAbnormalExit(void){
if( seenInterrupt ) eputz("Program interrupted.\n");
}
/* Routine to output from vfstrace
*/
static int vfstraceOut(const char *z, void *pArg){
ShellState *p = (ShellState*)pArg;
sqlite3_fputs(z, p->out);
fflush(p->out);
return 1;
}
#ifndef SQLITE_SHELL_IS_UTF8
# if (defined(_WIN32) || defined(WIN32)) \
&& (defined(_MSC_VER) || (defined(UNICODE) && defined(__GNUC__)))
# define SQLITE_SHELL_IS_UTF8 (0)
# else
# define SQLITE_SHELL_IS_UTF8 (1)
|
| ︙ | ︙ | |||
32785 32786 32787 32788 32789 32790 32791 |
verify_uninitialized();
switch( n ){
case 0: sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); break;
case 2: sqlite3_config(SQLITE_CONFIG_MULTITHREAD); break;
default: sqlite3_config(SQLITE_CONFIG_SERIALIZED); break;
}
}else if( cli_strcmp(z,"-vfstrace")==0 ){
| < < | 33079 33080 33081 33082 33083 33084 33085 33086 33087 33088 33089 33090 33091 33092 |
verify_uninitialized();
switch( n ){
case 0: sqlite3_config(SQLITE_CONFIG_SINGLETHREAD); break;
case 2: sqlite3_config(SQLITE_CONFIG_MULTITHREAD); break;
default: sqlite3_config(SQLITE_CONFIG_SERIALIZED); break;
}
}else if( cli_strcmp(z,"-vfstrace")==0 ){
bEnableVfstrace = 1;
#ifdef SQLITE_ENABLE_MULTIPLEX
}else if( cli_strcmp(z,"-multiplex")==0 ){
extern int sqlite3_multiplex_initialize(const char*,int);
sqlite3_multiplex_initialize(0, 1);
#endif
}else if( cli_strcmp(z,"-mmap")==0 ){
|
| ︙ | ︙ | |||
32883 32884 32885 32886 32887 32888 32889 32890 32891 32892 32893 32894 32895 32896 |
#else
sqlite3_fprintf(stderr,
"%s: Error: no database filename specified\n", Argv0);
return 1;
#endif
}
data.out = stdout;
#ifndef SQLITE_SHELL_FIDDLE
sqlite3_appendvfs_init(0,0,0);
#endif
/* Go ahead and open the database file if it already exists. If the
** file does not exist, delay opening it. This prevents empty database
** files from being created if a user mistypes the database name argument
| > > > | 33175 33176 33177 33178 33179 33180 33181 33182 33183 33184 33185 33186 33187 33188 33189 33190 33191 |
#else
sqlite3_fprintf(stderr,
"%s: Error: no database filename specified\n", Argv0);
return 1;
#endif
}
data.out = stdout;
if( bEnableVfstrace ){
vfstrace_register("trace",0,vfstraceOut, &data, 1);
}
#ifndef SQLITE_SHELL_FIDDLE
sqlite3_appendvfs_init(0,0,0);
#endif
/* Go ahead and open the database file if it already exists. If the
** file does not exist, delay opening it. This prevents empty database
** files from being created if a user mistypes the database name argument
|
| ︙ | ︙ | |||
33127 33128 33129 33130 33131 33132 33133 |
}else{
/* Run commands received from standard input
*/
if( stdin_is_interactive ){
char *zHome;
char *zHistory;
int nHistory;
| < < < < < | | | | > > | 33422 33423 33424 33425 33426 33427 33428 33429 33430 33431 33432 33433 33434 33435 33436 33437 33438 33439 33440 33441 33442 33443 33444 33445 33446 33447 33448 33449 33450 33451 33452 33453 33454 33455 33456 33457 33458 33459 33460 |
}else{
/* Run commands received from standard input
*/
if( stdin_is_interactive ){
char *zHome;
char *zHistory;
int nHistory;
sqlite3_fprintf(stdout,
"SQLite version %s %.19s\n" /*extra-version-info*/
"Enter \".help\" for usage hints.\n",
sqlite3_libversion(), sqlite3_sourceid());
if( warnInmemoryDb ){
sputz(stdout, "Connected to a ");
printBold("transient in-memory database");
sputz(stdout, ".\nUse \".open FILENAME\" to reopen on a"
" persistent database.\n");
}
zHistory = getenv("SQLITE_HISTORY");
if( zHistory ){
zHistory = strdup(zHistory);
}else if( (zHome = find_home_dir(0))!=0 ){
nHistory = strlen30(zHome) + 20;
if( (zHistory = malloc(nHistory))!=0 ){
sqlite3_snprintf(nHistory, zHistory,"%s/.sqlite_history", zHome);
}
}
if( zHistory ){ shell_read_history(zHistory); }
#if (HAVE_READLINE || HAVE_EDITLINE) && !defined(SQLITE_OMIT_READLINE_COMPLETION)
rl_attempted_completion_function = readline_completion;
#elif HAVE_LINENOISE==1
linenoiseSetCompletionCallback(linenoise_completion);
#elif HAVE_LINENOISE==2
linenoiseSetCompletionCallback(linenoise_completion, NULL);
#endif
data.in = 0;
rc = process_input(&data);
if( zHistory ){
shell_stifle_history(2000);
shell_write_history(zHistory);
|
| ︙ | ︙ |
Changes to extsrc/sqlite3.c.
1 2 | /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite | | > | > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite ** version 3.48.0. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a single translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements ** of 5% or more are commonly seen when SQLite is compiled as a single ** translation unit. ** ** This file is all you need to compile SQLite. To use SQLite in other ** programs, you need this file and the "sqlite3.h" header file that defines ** the programming interface to the SQLite library. (If you do not have ** the "sqlite3.h" header file at hand, you will find a copy embedded within ** the text of this file. Search for "Begin file sqlite3.h" to find the start ** of the embedded sqlite3.h header file.) Additional code files may be needed ** if you want a wrapper to interface SQLite with your choice of programming ** language. The code for the "sqlite3" command-line shell is also in a ** separate file. This file contains only code for the core SQLite library. ** ** The content in this amalgamation comes from Fossil check-in ** 2b17bc49655c577029919c2d409de994b0d2 with changes in files: ** ** */ #ifndef SQLITE_AMALGAMATION #define SQLITE_CORE 1 #define SQLITE_AMALGAMATION 1 #ifndef SQLITE_PRIVATE # define SQLITE_PRIVATE static #endif /************** Begin file sqliteInt.h ***************************************/ /* |
| ︙ | ︙ | |||
458 459 460 461 462 463 464 | ** been edited in any way since it was last checked in, then the last ** four hexadecimal digits of the hash may be modified. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ | | | | | 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 | ** been edited in any way since it was last checked in, then the last ** four hexadecimal digits of the hash may be modified. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.48.0" #define SQLITE_VERSION_NUMBER 3048000 #define SQLITE_SOURCE_ID "2024-12-30 21:23:53 2b17bc49655c577029919c2d409de994b0d252f8efb5da1ba0913f2c96bee552" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros |
| ︙ | ︙ | |||
964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 | ** read-only media and cannot be changed even by processes with ** elevated privileges. ** ** The SQLITE_IOCAP_BATCH_ATOMIC property means that the underlying ** filesystem supports doing multiple write operations atomically when those ** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and ** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. */ #define SQLITE_IOCAP_ATOMIC 0x00000001 #define SQLITE_IOCAP_ATOMIC512 0x00000002 #define SQLITE_IOCAP_ATOMIC1K 0x00000004 #define SQLITE_IOCAP_ATOMIC2K 0x00000008 #define SQLITE_IOCAP_ATOMIC4K 0x00000010 #define SQLITE_IOCAP_ATOMIC8K 0x00000020 #define SQLITE_IOCAP_ATOMIC16K 0x00000040 #define SQLITE_IOCAP_ATOMIC32K 0x00000080 #define SQLITE_IOCAP_ATOMIC64K 0x00000100 #define SQLITE_IOCAP_SAFE_APPEND 0x00000200 #define SQLITE_IOCAP_SEQUENTIAL 0x00000400 #define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800 #define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 #define SQLITE_IOCAP_IMMUTABLE 0x00002000 #define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000 /* ** CAPI3REF: File Locking Levels ** ** SQLite uses one of these integer values as the second ** argument to calls it makes to the xLock() and xUnlock() methods ** of an [sqlite3_io_methods] object. These values are ordered from | > > > > > > > > | 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 | ** read-only media and cannot be changed even by processes with ** elevated privileges. ** ** The SQLITE_IOCAP_BATCH_ATOMIC property means that the underlying ** filesystem supports doing multiple write operations atomically when those ** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and ** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. ** ** The SQLITE_IOCAP_SUBPAGE_READ property means that it is ok to read ** from the database file in amounts that are not a multiple of the ** page size and that do not begin at a page boundary. Without this ** property, SQLite is careful to only do full-page reads and write ** on aligned pages, with the one exception that it will do a sub-page ** read of the first page to access the database header. */ #define SQLITE_IOCAP_ATOMIC 0x00000001 #define SQLITE_IOCAP_ATOMIC512 0x00000002 #define SQLITE_IOCAP_ATOMIC1K 0x00000004 #define SQLITE_IOCAP_ATOMIC2K 0x00000008 #define SQLITE_IOCAP_ATOMIC4K 0x00000010 #define SQLITE_IOCAP_ATOMIC8K 0x00000020 #define SQLITE_IOCAP_ATOMIC16K 0x00000040 #define SQLITE_IOCAP_ATOMIC32K 0x00000080 #define SQLITE_IOCAP_ATOMIC64K 0x00000100 #define SQLITE_IOCAP_SAFE_APPEND 0x00000200 #define SQLITE_IOCAP_SEQUENTIAL 0x00000400 #define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800 #define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 #define SQLITE_IOCAP_IMMUTABLE 0x00002000 #define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000 #define SQLITE_IOCAP_SUBPAGE_READ 0x00008000 /* ** CAPI3REF: File Locking Levels ** ** SQLite uses one of these integer values as the second ** argument to calls it makes to the xLock() and xUnlock() methods ** of an [sqlite3_io_methods] object. These values are ordered from |
| ︙ | ︙ | |||
1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 | ** <li> [SQLITE_IOCAP_ATOMIC64K] ** <li> [SQLITE_IOCAP_SAFE_APPEND] ** <li> [SQLITE_IOCAP_SEQUENTIAL] ** <li> [SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN] ** <li> [SQLITE_IOCAP_POWERSAFE_OVERWRITE] ** <li> [SQLITE_IOCAP_IMMUTABLE] ** <li> [SQLITE_IOCAP_BATCH_ATOMIC] ** </ul> ** ** The SQLITE_IOCAP_ATOMIC property means that all writes of ** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values ** mean that writes of blocks that are nnn bytes in size and ** are aligned to an address which is an integer multiple of ** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means | > | 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 | ** <li> [SQLITE_IOCAP_ATOMIC64K] ** <li> [SQLITE_IOCAP_SAFE_APPEND] ** <li> [SQLITE_IOCAP_SEQUENTIAL] ** <li> [SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN] ** <li> [SQLITE_IOCAP_POWERSAFE_OVERWRITE] ** <li> [SQLITE_IOCAP_IMMUTABLE] ** <li> [SQLITE_IOCAP_BATCH_ATOMIC] ** <li> [SQLITE_IOCAP_SUBPAGE_READ] ** </ul> ** ** The SQLITE_IOCAP_ATOMIC property means that all writes of ** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values ** mean that writes of blocks that are nnn bytes in size and ** are aligned to an address which is an integer multiple of ** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means |
| ︙ | ︙ | |||
1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 | ** ** <li>[[SQLITE_FCNTL_WIN32_SET_HANDLE]] ** The [SQLITE_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging. This ** opcode causes the xFileControl method to swap the file handle with the one ** pointed to by the pArg argument. This capability is used during testing ** and only needs to be supported when SQLITE_TEST is defined. ** ** <li>[[SQLITE_FCNTL_WAL_BLOCK]] ** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might ** be advantageous to block on the next WAL lock if the lock is not immediately ** available. The WAL subsystem issues this signal during rare ** circumstances in order to fix a problem with priority inversion. ** Applications should <em>not</em> use this file-control. ** | > > > > > | 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 | ** ** <li>[[SQLITE_FCNTL_WIN32_SET_HANDLE]] ** The [SQLITE_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging. This ** opcode causes the xFileControl method to swap the file handle with the one ** pointed to by the pArg argument. This capability is used during testing ** and only needs to be supported when SQLITE_TEST is defined. ** ** <li>[[SQLITE_FCNTL_NULL_IO]] ** The [SQLITE_FCNTL_NULL_IO] opcode sets the low-level file descriptor ** or file handle for the [sqlite3_file] object such that it will no longer ** read or write to the database file. ** ** <li>[[SQLITE_FCNTL_WAL_BLOCK]] ** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might ** be advantageous to block on the next WAL lock if the lock is not immediately ** available. The WAL subsystem issues this signal during rare ** circumstances in order to fix a problem with priority inversion. ** Applications should <em>not</em> use this file-control. ** |
| ︙ | ︙ | |||
1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 | #define SQLITE_FCNTL_SIZE_LIMIT 36 #define SQLITE_FCNTL_CKPT_DONE 37 #define SQLITE_FCNTL_RESERVE_BYTES 38 #define SQLITE_FCNTL_CKPT_START 39 #define SQLITE_FCNTL_EXTERNAL_READER 40 #define SQLITE_FCNTL_CKSM_FILE 41 #define SQLITE_FCNTL_RESET_CACHE 42 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO | > | 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 | #define SQLITE_FCNTL_SIZE_LIMIT 36 #define SQLITE_FCNTL_CKPT_DONE 37 #define SQLITE_FCNTL_RESERVE_BYTES 38 #define SQLITE_FCNTL_CKPT_START 39 #define SQLITE_FCNTL_EXTERNAL_READER 40 #define SQLITE_FCNTL_CKSM_FILE 41 #define SQLITE_FCNTL_RESET_CACHE 42 #define SQLITE_FCNTL_NULL_IO 43 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| ︙ | ︙ | |||
2934 2935 2936 2937 2938 2939 2940 | ** CAPI3REF: Count The Number Of Rows Modified ** METHOD: sqlite3 ** ** ^These functions return the number of rows modified, inserted or ** deleted by the most recently completed INSERT, UPDATE or DELETE ** statement on the database connection specified by the only parameter. ** The two functions are identical except for the type of the return value | | > > > > | 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 | ** CAPI3REF: Count The Number Of Rows Modified ** METHOD: sqlite3 ** ** ^These functions return the number of rows modified, inserted or ** deleted by the most recently completed INSERT, UPDATE or DELETE ** statement on the database connection specified by the only parameter. ** The two functions are identical except for the type of the return value ** and that if the number of rows modified by the most recent INSERT, UPDATE, ** or DELETE is greater than the maximum value supported by type "int", then ** the return value of sqlite3_changes() is undefined. ^Executing any other ** type of SQL statement does not modify the value returned by these functions. ** For the purposes of this interface, a CREATE TABLE AS SELECT statement ** does not count as an INSERT, UPDATE or DELETE statement and hence the rows ** added to the new table by the CREATE TABLE AS SELECT statement are not ** counted. ** ** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are ** considered - auxiliary changes caused by [CREATE TRIGGER | triggers], ** [foreign key actions] or [REPLACE] constraint resolution are not counted. ** ** Changes to a view that are intercepted by ** [INSTEAD OF trigger | INSTEAD OF triggers] are not counted. ^The value |
| ︙ | ︙ | |||
4497 4498 4499 4500 4501 4502 4503 4504 4505 4506 4507 4508 4509 4510 4511 4512 4513 4514 4515 |
** prepared statements, regardless of whether or not they use this
** flag.
**
** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt>
** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler
** to return an error (error code SQLITE_ERROR) if the statement uses
** any virtual tables.
** </dl>
*/
#define SQLITE_PREPARE_PERSISTENT 0x01
#define SQLITE_PREPARE_NORMALIZE 0x02
#define SQLITE_PREPARE_NO_VTAB 0x04
/*
** CAPI3REF: Compiling An SQL Statement
** KEYWORDS: {SQL statement compiler}
** METHOD: sqlite3
** CONSTRUCTOR: sqlite3_stmt
**
| > > > > > > > > > > > | 4519 4520 4521 4522 4523 4524 4525 4526 4527 4528 4529 4530 4531 4532 4533 4534 4535 4536 4537 4538 4539 4540 4541 4542 4543 4544 4545 4546 4547 4548 |
** prepared statements, regardless of whether or not they use this
** flag.
**
** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt>
** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler
** to return an error (error code SQLITE_ERROR) if the statement uses
** any virtual tables.
**
** [[SQLITE_PREPARE_DONT_LOG]] <dt>SQLITE_PREPARE_DONT_LOG</dt>
** <dd>The SQLITE_PREPARE_DONT_LOG flag prevents SQL compiler
** errors from being sent to the error log defined by
** [SQLITE_CONFIG_LOG]. This can be used, for example, to do test
** compiles to see if some SQL syntax is well-formed, without generating
** messages on the global error log when it is not. If the test compile
** fails, the sqlite3_prepare_v3() call returns the same error indications
** with or without this flag; it just omits the call to [sqlite3_log()] that
** logs the error.
** </dl>
*/
#define SQLITE_PREPARE_PERSISTENT 0x01
#define SQLITE_PREPARE_NORMALIZE 0x02
#define SQLITE_PREPARE_NO_VTAB 0x04
#define SQLITE_PREPARE_DONT_LOG 0x10
/*
** CAPI3REF: Compiling An SQL Statement
** KEYWORDS: {SQL statement compiler}
** METHOD: sqlite3
** CONSTRUCTOR: sqlite3_stmt
**
|
| ︙ | ︙ | |||
11192 11193 11194 11195 11196 11197 11198 | # define SQLITE_THREADSAFE 0 # endif #endif #if 0 } /* End of the 'extern "C"' block */ #endif | | | 11225 11226 11227 11228 11229 11230 11231 11232 11233 11234 11235 11236 11237 11238 11239 | # define SQLITE_THREADSAFE 0 # endif #endif #if 0 } /* End of the 'extern "C"' block */ #endif /* #endif for SQLITE3_H will be added by mksqlite3.tcl */ /******** Begin file sqlite3rtree.h *********/ /* ** 2010 August 30 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: |
| ︙ | ︙ | |||
13443 13444 13445 13446 13447 13448 13449 | ** ** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) ** This is used to access token iToken of phrase hit iIdx within the ** current row. If iIdx is less than zero or greater than or equal to the ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, ** output variable (*ppToken) is set to point to a buffer containing the ** matching document token, and (*pnToken) to the size of that buffer in | < < | > > > > > > > > > > > > > > > > > | 13476 13477 13478 13479 13480 13481 13482 13483 13484 13485 13486 13487 13488 13489 13490 13491 13492 13493 13494 13495 13496 13497 13498 13499 13500 13501 13502 13503 13504 13505 13506 13507 13508 13509 13510 13511 | ** ** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) ** This is used to access token iToken of phrase hit iIdx within the ** current row. If iIdx is less than zero or greater than or equal to the ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, ** output variable (*ppToken) is set to point to a buffer containing the ** matching document token, and (*pnToken) to the size of that buffer in ** bytes. ** ** The output text is not a copy of the document text that was tokenized. ** It is the output of the tokenizer module. For tokendata=1 tables, this ** includes any embedded 0x00 and trailing data. ** ** This API may be slow in some cases if the token identified by parameters ** iIdx and iToken matched a prefix token in the query. In most cases, the ** first call to this API for each prefix token in the query is forced ** to scan the portion of the full-text index that matches the prefix ** token to collect the extra data required by this API. If the prefix ** token matches a large number of token instances in the document set, ** this may be a performance problem. ** ** If the user knows in advance that a query may use this API for a ** prefix token, FTS5 may be configured to collect all required data as part ** of the initial querying of the full-text index, avoiding the second scan ** entirely. This also causes prefix queries that do not use this API to ** run more slowly and use more memory. FTS5 may be configured in this way ** either on a per-table basis using the [FTS5 insttoken | 'insttoken'] ** option, or on a per-query basis using the ** [fts5_insttoken | fts5_insttoken()] user function. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. ** ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) ** If parameter iCol is less than zero, or greater than or equal to the ** number of columns in the table, SQLITE_RANGE is returned. |
| ︙ | ︙ | |||
13884 13885 13886 13887 13888 13889 13890 13891 13892 13893 13894 13895 13896 13897 | #if 0 } /* end of the 'extern "C"' block */ #endif #endif /* _FTS5_H */ /******** End of fts5.h *********/ /************** End of sqlite3.h *********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ /* ** Reuse the STATIC_LRU for mutex access to sqlite3_temp_directory. */ | > | 13932 13933 13934 13935 13936 13937 13938 13939 13940 13941 13942 13943 13944 13945 13946 | #if 0 } /* end of the 'extern "C"' block */ #endif #endif /* _FTS5_H */ /******** End of fts5.h *********/ #endif /* SQLITE3_H */ /************** End of sqlite3.h *********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ /* ** Reuse the STATIC_LRU for mutex access to sqlite3_temp_directory. */ |
| ︙ | ︙ | |||
13929 13930 13931 13932 13933 13934 13935 13936 13937 13938 13939 13940 13941 13942 | ** ** The hard limit is the ability of a 32-bit signed integer ** to count the size: 2^31-1 or 2147483647. */ #ifndef SQLITE_MAX_LENGTH # define SQLITE_MAX_LENGTH 1000000000 #endif /* ** This is the maximum number of ** ** * Columns in a table ** * Columns in an index ** * Columns in a view | > | 13978 13979 13980 13981 13982 13983 13984 13985 13986 13987 13988 13989 13990 13991 13992 | ** ** The hard limit is the ability of a 32-bit signed integer ** to count the size: 2^31-1 or 2147483647. */ #ifndef SQLITE_MAX_LENGTH # define SQLITE_MAX_LENGTH 1000000000 #endif #define SQLITE_MIN_LENGTH 30 /* Minimum value for the length limit */ /* ** This is the maximum number of ** ** * Columns in a table ** * Columns in an index ** * Columns in a view |
| ︙ | ︙ | |||
13994 13995 13996 13997 13998 13999 14000 14001 14002 | */ #ifndef SQLITE_MAX_VDBE_OP # define SQLITE_MAX_VDBE_OP 250000000 #endif /* ** The maximum number of arguments to an SQL function. */ #ifndef SQLITE_MAX_FUNCTION_ARG | > > > > | | 14044 14045 14046 14047 14048 14049 14050 14051 14052 14053 14054 14055 14056 14057 14058 14059 14060 14061 14062 14063 14064 | */ #ifndef SQLITE_MAX_VDBE_OP # define SQLITE_MAX_VDBE_OP 250000000 #endif /* ** The maximum number of arguments to an SQL function. ** ** This value has a hard upper limit of 32767 due to storage ** constraints (it needs to fit inside a i16). We keep it ** lower than that to prevent abuse. */ #ifndef SQLITE_MAX_FUNCTION_ARG # define SQLITE_MAX_FUNCTION_ARG 1000 #endif /* ** The suggested maximum number of in-memory pages to use for ** the main database table and for temporary tables. ** ** IMPLEMENTATION-OF: R-30185-15359 The default suggested cache size is -2000, |
| ︙ | ︙ | |||
15998 15999 16000 16001 16002 16003 16004 16005 16006 16007 16008 16009 16010 16011 | #define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */ #define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */ #define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */ #define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */ #define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */ #define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */ /* ** Flags that make up the mask passed to sqlite3PagerGet(). */ #define PAGER_GET_NOCONTENT 0x01 /* Do not load data from disk */ #define PAGER_GET_READONLY 0x02 /* Read-only page is acceptable */ /* | > > > > > > > > > > > > > > > > | 16052 16053 16054 16055 16056 16057 16058 16059 16060 16061 16062 16063 16064 16065 16066 16067 16068 16069 16070 16071 16072 16073 16074 16075 16076 16077 16078 16079 16080 16081 |
#define PAGER_JOURNALMODE_DELETE 0 /* Commit by deleting journal file */
#define PAGER_JOURNALMODE_PERSIST 1 /* Commit by zeroing journal header */
#define PAGER_JOURNALMODE_OFF 2 /* Journal omitted. */
#define PAGER_JOURNALMODE_TRUNCATE 3 /* Commit by truncating journal */
#define PAGER_JOURNALMODE_MEMORY 4 /* In-memory journal file */
#define PAGER_JOURNALMODE_WAL 5 /* Use write-ahead logging */
#define isWalMode(x) ((x)==PAGER_JOURNALMODE_WAL)
/*
** The argument to this macro is a file descriptor (type sqlite3_file*).
** Return 0 if it is not open, or non-zero (but not 1) if it is.
**
** This is so that expressions can be written as:
**
** if( isOpen(pPager->jfd) ){ ...
**
** instead of
**
** if( pPager->jfd->pMethods ){ ...
*/
#define isOpen(pFd) ((pFd)->pMethods!=0)
/*
** Flags that make up the mask passed to sqlite3PagerGet().
*/
#define PAGER_GET_NOCONTENT 0x01 /* Do not load data from disk */
#define PAGER_GET_READONLY 0x02 /* Read-only page is acceptable */
/*
|
| ︙ | ︙ | |||
17023 17024 17025 17026 17027 17028 17029 | /************** End of opcodes.h *********************************************/ /************** Continuing where we left off in vdbe.h ***********************/ /* ** Additional non-public SQLITE_PREPARE_* flags */ #define SQLITE_PREPARE_SAVESQL 0x80 /* Preserve SQL text */ | | | 17093 17094 17095 17096 17097 17098 17099 17100 17101 17102 17103 17104 17105 17106 17107 | /************** End of opcodes.h *********************************************/ /************** Continuing where we left off in vdbe.h ***********************/ /* ** Additional non-public SQLITE_PREPARE_* flags */ #define SQLITE_PREPARE_SAVESQL 0x80 /* Preserve SQL text */ #define SQLITE_PREPARE_MASK 0x1f /* Mask of public flags */ /* ** Prototypes for the VDBE interface. See comments on the implementation ** for a description of what each of these routines does. */ SQLITE_PRIVATE Vdbe *sqlite3VdbeCreate(Parse*); SQLITE_PRIVATE Parse *sqlite3VdbeParser(Vdbe*); |
| ︙ | ︙ | |||
17738 17739 17740 17741 17742 17743 17744 |
*/
#define SQLITE_FUNC_HASH_SZ 23
struct FuncDefHash {
FuncDef *a[SQLITE_FUNC_HASH_SZ]; /* Hash table for functions */
};
#define SQLITE_FUNC_HASH(C,L) (((C)+(L))%SQLITE_FUNC_HASH_SZ)
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | < < < | < | 17808 17809 17810 17811 17812 17813 17814 17815 17816 17817 17818 17819 17820 17821 17822 17823 17824 17825 17826 |
*/
#define SQLITE_FUNC_HASH_SZ 23
struct FuncDefHash {
FuncDef *a[SQLITE_FUNC_HASH_SZ]; /* Hash table for functions */
};
#define SQLITE_FUNC_HASH(C,L) (((C)+(L))%SQLITE_FUNC_HASH_SZ)
/*
** typedef for the authorization callback function.
*/
typedef int (*sqlite3_xauth)(void*,int,const char*,const char*,const char*,
const char*);
#ifndef SQLITE_OMIT_DEPRECATED
/* This is an extra SQLITE_TRACE macro that indicates "legacy" tracing
** in the style of sqlite3_trace()
*/
#define SQLITE_TRACE_LEGACY 0x40 /* Use the legacy xTrace */
#define SQLITE_TRACE_XPROFILE 0x80 /* Use the legacy xProfile */
|
| ︙ | ︙ | |||
17939 17940 17941 17942 17943 17944 17945 | */ sqlite3 *pBlockingConnection; /* Connection that caused SQLITE_LOCKED */ sqlite3 *pUnlockConnection; /* Connection to watch for unlock */ void *pUnlockArg; /* Argument to xUnlockNotify */ void (*xUnlockNotify)(void **, int); /* Unlock notify callback */ sqlite3 *pNextBlocked; /* Next in list of all blocked connections */ #endif | < < < | 17973 17974 17975 17976 17977 17978 17979 17980 17981 17982 17983 17984 17985 17986 | */ sqlite3 *pBlockingConnection; /* Connection that caused SQLITE_LOCKED */ sqlite3 *pUnlockConnection; /* Connection to watch for unlock */ void *pUnlockArg; /* Argument to xUnlockNotify */ void (*xUnlockNotify)(void **, int); /* Unlock notify callback */ sqlite3 *pNextBlocked; /* Next in list of all blocked connections */ #endif }; /* ** A macro to discover the encoding of a database. */ #define SCHEMA_ENC(db) ((db)->aDb[0].pSchema->enc) #define ENC(db) ((db)->enc) |
| ︙ | ︙ | |||
18100 18101 18102 18103 18104 18105 18106 |
** For per-connection application-defined functions, a pointer to this
** structure is held in the db->aHash hash table.
**
** The u.pHash field is used by the global built-ins. The u.pDestructor
** field is used by per-connection app-def functions.
*/
struct FuncDef {
| | | 18131 18132 18133 18134 18135 18136 18137 18138 18139 18140 18141 18142 18143 18144 18145 |
** For per-connection application-defined functions, a pointer to this
** structure is held in the db->aHash hash table.
**
** The u.pHash field is used by the global built-ins. The u.pDestructor
** field is used by per-connection app-def functions.
*/
struct FuncDef {
i16 nArg; /* Number of arguments. -1 means unlimited */
u32 funcFlags; /* Some combination of SQLITE_FUNC_* */
void *pUserData; /* User data parameter */
FuncDef *pNext; /* Next function with same name */
void (*xSFunc)(sqlite3_context*,int,sqlite3_value**); /* func or agg-step */
void (*xFinalize)(sqlite3_context*); /* Agg finalizer */
void (*xValue)(sqlite3_context*); /* Current agg value */
void (*xInverse)(sqlite3_context*,int,sqlite3_value**); /* inverse agg-step */
|
| ︙ | ︙ | |||
22848 22849 22850 22851 22852 22853 22854 | #endif #ifdef SQLITE_UNLINK_AFTER_CLOSE "UNLINK_AFTER_CLOSE", #endif #ifdef SQLITE_UNTESTABLE "UNTESTABLE", #endif | < < < | 22879 22880 22881 22882 22883 22884 22885 22886 22887 22888 22889 22890 22891 22892 | #endif #ifdef SQLITE_UNLINK_AFTER_CLOSE "UNLINK_AFTER_CLOSE", #endif #ifdef SQLITE_UNTESTABLE "UNTESTABLE", #endif #ifdef SQLITE_USE_ALLOCA "USE_ALLOCA", #endif #ifdef SQLITE_USE_FCNTL_TRACE "USE_FCNTL_TRACE", #endif #ifdef SQLITE_USE_URI |
| ︙ | ︙ | |||
23698 23699 23700 23701 23702 23703 23704 | FuncDef *pFunc; /* Pointer to function information */ Mem *pMem; /* Memory cell used to store aggregate context */ Vdbe *pVdbe; /* The VM that owns this context */ int iOp; /* Instruction number of OP_Function */ int isError; /* Error code returned by the function. */ u8 enc; /* Encoding to use for results */ u8 skipFlag; /* Skip accumulator loading if true */ | | | 23726 23727 23728 23729 23730 23731 23732 23733 23734 23735 23736 23737 23738 23739 23740 | FuncDef *pFunc; /* Pointer to function information */ Mem *pMem; /* Memory cell used to store aggregate context */ Vdbe *pVdbe; /* The VM that owns this context */ int iOp; /* Instruction number of OP_Function */ int isError; /* Error code returned by the function. */ u8 enc; /* Encoding to use for results */ u8 skipFlag; /* Skip accumulator loading if true */ u16 argc; /* Number of arguments */ sqlite3_value *argv[1]; /* Argument set */ }; /* A bitfield type for use inside of structures. Always follow with :N where ** N is the number of bits. */ typedef unsigned bft; /* Bit Field Type */ |
| ︙ | ︙ | |||
23845 23846 23847 23848 23849 23850 23851 23852 23853 23854 23855 23856 23857 23858 | KeyInfo keyinfo; UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */ UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */ int iNewReg; /* Register for new.* values */ int iBlobWrite; /* Value returned by preupdate_blobwrite() */ i64 iKey1; /* First key value passed to hook */ i64 iKey2; /* Second key value passed to hook */ Mem *aNew; /* Array of new.* values */ Table *pTab; /* Schema object being updated */ Index *pPk; /* PK index if pTab is WITHOUT ROWID */ sqlite3_value **apDflt; /* Array of default values, if required */ }; /* | > | 23873 23874 23875 23876 23877 23878 23879 23880 23881 23882 23883 23884 23885 23886 23887 | KeyInfo keyinfo; UnpackedRecord *pUnpacked; /* Unpacked version of aRecord[] */ UnpackedRecord *pNewUnpacked; /* Unpacked version of new.* record */ int iNewReg; /* Register for new.* values */ int iBlobWrite; /* Value returned by preupdate_blobwrite() */ i64 iKey1; /* First key value passed to hook */ i64 iKey2; /* Second key value passed to hook */ Mem oldipk; /* Memory cell holding "old" IPK value */ Mem *aNew; /* Array of new.* values */ Table *pTab; /* Schema object being updated */ Index *pPk; /* PK index if pTab is WITHOUT ROWID */ sqlite3_value **apDflt; /* Array of default values, if required */ }; /* |
| ︙ | ︙ | |||
32294 32295 32296 32297 32298 32299 32300 32301 32302 32303 32304 32305 32306 32307 |
SQLITE_PRIVATE void sqlite3RecordErrorOffsetOfExpr(sqlite3 *db, const Expr *pExpr){
while( pExpr
&& (ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) || pExpr->w.iOfst<=0)
){
pExpr = pExpr->pLeft;
}
if( pExpr==0 ) return;
db->errByteOffset = pExpr->w.iOfst;
}
/*
** Enlarge the memory allocation on a StrAccum object so that it is
** able to accept at least N more bytes of text.
**
| > | 32323 32324 32325 32326 32327 32328 32329 32330 32331 32332 32333 32334 32335 32336 32337 |
SQLITE_PRIVATE void sqlite3RecordErrorOffsetOfExpr(sqlite3 *db, const Expr *pExpr){
while( pExpr
&& (ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) || pExpr->w.iOfst<=0)
){
pExpr = pExpr->pLeft;
}
if( pExpr==0 ) return;
if( ExprHasProperty(pExpr, EP_FromDDL) ) return;
db->errByteOffset = pExpr->w.iOfst;
}
/*
** Enlarge the memory allocation on a StrAccum object so that it is
** able to accept at least N more bytes of text.
**
|
| ︙ | ︙ | |||
33023 33024 33025 33026 33027 33028 33029 |
if( pItem->fg.fromDDL ){
sqlite3_str_appendf(&x, " DDL");
}
if( pItem->fg.isCte ){
sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse);
}
if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){
| | | 33053 33054 33055 33056 33057 33058 33059 33060 33061 33062 33063 33064 33065 33066 33067 |
if( pItem->fg.fromDDL ){
sqlite3_str_appendf(&x, " DDL");
}
if( pItem->fg.isCte ){
sqlite3_str_appendf(&x, " CteUse=0x%p", pItem->u2.pCteUse);
}
if( pItem->fg.isOn || (pItem->fg.isUsing==0 && pItem->u3.pOn!=0) ){
sqlite3_str_appendf(&x, " isOn");
}
if( pItem->fg.isTabFunc ) sqlite3_str_appendf(&x, " isTabFunc");
if( pItem->fg.isCorrelated ) sqlite3_str_appendf(&x, " isCorrelated");
if( pItem->fg.isMaterialized ) sqlite3_str_appendf(&x, " isMaterialized");
if( pItem->fg.viaCoroutine ) sqlite3_str_appendf(&x, " viaCoroutine");
if( pItem->fg.notCte ) sqlite3_str_appendf(&x, " notCte");
if( pItem->fg.isNestedFrom ) sqlite3_str_appendf(&x, " isNestedFrom");
|
| ︙ | ︙ | |||
34107 34108 34109 34110 34111 34112 34113 34114 34115 34116 34117 34118 34119 34120 |
** parameters. These variants are intended to be used from a symbolic
** debugger, such as "gdb", during interactive debugging sessions.
**
** This routines are given external linkage so that they will always be
** accessible to the debugging, and to avoid warnings about unused
** functions. But these routines only exist in debugging builds, so they
** do not contaminate the interface.
*/
SQLITE_PRIVATE void sqlite3ShowExpr(const Expr *p){ sqlite3TreeViewExpr(0,p,0); }
SQLITE_PRIVATE void sqlite3ShowExprList(const ExprList *p){ sqlite3TreeViewExprList(0,p,0,0);}
SQLITE_PRIVATE void sqlite3ShowIdList(const IdList *p){ sqlite3TreeViewIdList(0,p,0,0); }
SQLITE_PRIVATE void sqlite3ShowSrcList(const SrcList *p){ sqlite3TreeViewSrcList(0,p); }
SQLITE_PRIVATE void sqlite3ShowSelect(const Select *p){ sqlite3TreeViewSelect(0,p,0); }
SQLITE_PRIVATE void sqlite3ShowWith(const With *p){ sqlite3TreeViewWith(0,p,0); }
| > > > > | 34137 34138 34139 34140 34141 34142 34143 34144 34145 34146 34147 34148 34149 34150 34151 34152 34153 34154 |
** parameters. These variants are intended to be used from a symbolic
** debugger, such as "gdb", during interactive debugging sessions.
**
** This routines are given external linkage so that they will always be
** accessible to the debugging, and to avoid warnings about unused
** functions. But these routines only exist in debugging builds, so they
** do not contaminate the interface.
**
** See Also:
**
** sqlite3ShowWhereTerm() in where.c
*/
SQLITE_PRIVATE void sqlite3ShowExpr(const Expr *p){ sqlite3TreeViewExpr(0,p,0); }
SQLITE_PRIVATE void sqlite3ShowExprList(const ExprList *p){ sqlite3TreeViewExprList(0,p,0,0);}
SQLITE_PRIVATE void sqlite3ShowIdList(const IdList *p){ sqlite3TreeViewIdList(0,p,0,0); }
SQLITE_PRIVATE void sqlite3ShowSrcList(const SrcList *p){ sqlite3TreeViewSrcList(0,p); }
SQLITE_PRIVATE void sqlite3ShowSelect(const Select *p){ sqlite3TreeViewSelect(0,p,0); }
SQLITE_PRIVATE void sqlite3ShowWith(const With *p){ sqlite3TreeViewWith(0,p,0); }
|
| ︙ | ︙ | |||
35683 35684 35685 35686 35687 35688 35689 35690 | u64 s = 0; /* significand */ int d = 0; /* adjust exponent for shifting decimal point */ int esign = 1; /* sign of exponent */ int e = 0; /* exponent */ int eValid = 1; /* True exponent is either not used or is well-formed */ int nDigit = 0; /* Number of digits processed */ int eType = 1; /* 1: pure integer, 2+: fractional -1 or less: bad UTF16 */ double rr[2]; | > < | 35717 35718 35719 35720 35721 35722 35723 35724 35725 35726 35727 35728 35729 35730 35731 35732 |
u64 s = 0; /* significand */
int d = 0; /* adjust exponent for shifting decimal point */
int esign = 1; /* sign of exponent */
int e = 0; /* exponent */
int eValid = 1; /* True exponent is either not used or is well-formed */
int nDigit = 0; /* Number of digits processed */
int eType = 1; /* 1: pure integer, 2+: fractional -1 or less: bad UTF16 */
u64 s2; /* round-tripped significand */
double rr[2];
assert( enc==SQLITE_UTF8 || enc==SQLITE_UTF16LE || enc==SQLITE_UTF16BE );
*pResult = 0.0; /* Default return value, in case of an error */
if( length==0 ) return 0;
if( enc==SQLITE_UTF8 ){
incr = 1;
|
| ︙ | ︙ | |||
35787 35788 35789 35790 35791 35792 35793 |
goto atof_return;
}
/* adjust exponent by d, and update sign */
e = (e*esign) + d;
/* Try to adjust the exponent to make it smaller */
| | | | > > | > > > > | > > > > > | 35821 35822 35823 35824 35825 35826 35827 35828 35829 35830 35831 35832 35833 35834 35835 35836 35837 35838 35839 35840 35841 35842 35843 35844 35845 35846 35847 35848 35849 35850 35851 35852 35853 35854 35855 35856 35857 35858 35859 35860 |
goto atof_return;
}
/* adjust exponent by d, and update sign */
e = (e*esign) + d;
/* Try to adjust the exponent to make it smaller */
while( e>0 && s<((LARGEST_UINT64-0x7ff)/10) ){
s *= 10;
e--;
}
while( e<0 && (s%10)==0 ){
s /= 10;
e++;
}
rr[0] = (double)s;
assert( sizeof(s2)==sizeof(rr[0]) );
#ifdef SQLITE_DEBUG
rr[1] = 18446744073709549568.0;
memcpy(&s2, &rr[1], sizeof(s2));
assert( s2==0x43efffffffffffffLL );
#endif
/* Largest double that can be safely converted to u64
** vvvvvvvvvvvvvvvvvvvvvv */
if( rr[0]<=18446744073709549568.0 ){
s2 = (u64)rr[0];
rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s);
}else{
rr[1] = 0.0;
}
assert( rr[1]<=1.0e-10*rr[0] ); /* Equal only when rr[0]==0.0 */
if( e>0 ){
while( e>=100 ){
e -= 100;
dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83);
}
while( e>=10 ){
e -= 10;
|
| ︙ | ︙ | |||
38672 38673 38674 38675 38676 38677 38678 | # define F_GETLK 5 # define F_SETLK 6 # define F_SETLKW 7 # endif # endif #else /* !SQLITE_WASI */ # ifndef HAVE_FCHMOD | | | 38717 38718 38719 38720 38721 38722 38723 38724 38725 38726 38727 38728 38729 38730 38731 | # define F_GETLK 5 # define F_SETLK 6 # define F_SETLKW 7 # endif # endif #else /* !SQLITE_WASI */ # ifndef HAVE_FCHMOD # define HAVE_FCHMOD 1 # endif #endif /* SQLITE_WASI */ #ifdef SQLITE_WASI # define osGetpid(X) (pid_t)1 #else /* Always cast the getpid() return type for compatibility with |
| ︙ | ︙ | |||
42446 42447 42448 42449 42450 42451 42452 42453 42454 42455 42456 42457 42458 42459 |
}
case SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE: {
int rc = osIoctl(pFile->h, F2FS_IOC_ABORT_VOLATILE_WRITE);
return rc ? SQLITE_IOERR_ROLLBACK_ATOMIC : SQLITE_OK;
}
#endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */
case SQLITE_FCNTL_LOCKSTATE: {
*(int*)pArg = pFile->eFileLock;
return SQLITE_OK;
}
case SQLITE_FCNTL_LAST_ERRNO: {
*(int*)pArg = pFile->lastErrno;
return SQLITE_OK;
| > > > > > | 42491 42492 42493 42494 42495 42496 42497 42498 42499 42500 42501 42502 42503 42504 42505 42506 42507 42508 42509 |
}
case SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE: {
int rc = osIoctl(pFile->h, F2FS_IOC_ABORT_VOLATILE_WRITE);
return rc ? SQLITE_IOERR_ROLLBACK_ATOMIC : SQLITE_OK;
}
#endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */
case SQLITE_FCNTL_NULL_IO: {
osClose(pFile->h);
pFile->h = -1;
return SQLITE_OK;
}
case SQLITE_FCNTL_LOCKSTATE: {
*(int*)pArg = pFile->eFileLock;
return SQLITE_OK;
}
case SQLITE_FCNTL_LAST_ERRNO: {
*(int*)pArg = pFile->lastErrno;
return SQLITE_OK;
|
| ︙ | ︙ | |||
42587 42588 42589 42590 42591 42592 42593 42594 42595 42596 42597 42598 42599 42600 |
}
#endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */
/* Set the POWERSAFE_OVERWRITE flag if requested. */
if( pFd->ctrlFlags & UNIXFILE_PSOW ){
pFd->deviceCharacteristics |= SQLITE_IOCAP_POWERSAFE_OVERWRITE;
}
pFd->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE;
}
}
#else
#include <sys/dcmd_blk.h>
#include <sys/statvfs.h>
| > | 42637 42638 42639 42640 42641 42642 42643 42644 42645 42646 42647 42648 42649 42650 42651 |
}
#endif /* __linux__ && SQLITE_ENABLE_BATCH_ATOMIC_WRITE */
/* Set the POWERSAFE_OVERWRITE flag if requested. */
if( pFd->ctrlFlags & UNIXFILE_PSOW ){
pFd->deviceCharacteristics |= SQLITE_IOCAP_POWERSAFE_OVERWRITE;
}
pFd->deviceCharacteristics |= SQLITE_IOCAP_SUBPAGE_READ;
pFd->sectorSize = SQLITE_DEFAULT_SECTOR_SIZE;
}
}
#else
#include <sys/dcmd_blk.h>
#include <sys/statvfs.h>
|
| ︙ | ︙ | |||
50326 50327 50328 50329 50330 50331 50332 50333 50334 50335 50336 50337 50338 50339 |
pFile->h = *phFile;
*phFile = hOldFile;
OSTRACE(("FCNTL oldFile=%p, newFile=%p, rc=SQLITE_OK\n",
hOldFile, pFile->h));
return SQLITE_OK;
}
#endif
case SQLITE_FCNTL_TEMPFILENAME: {
char *zTFile = 0;
int rc = winGetTempname(pFile->pVfs, &zTFile);
if( rc==SQLITE_OK ){
*(char**)pArg = zTFile;
}
OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc)));
| > > > > > | 50377 50378 50379 50380 50381 50382 50383 50384 50385 50386 50387 50388 50389 50390 50391 50392 50393 50394 50395 |
pFile->h = *phFile;
*phFile = hOldFile;
OSTRACE(("FCNTL oldFile=%p, newFile=%p, rc=SQLITE_OK\n",
hOldFile, pFile->h));
return SQLITE_OK;
}
#endif
case SQLITE_FCNTL_NULL_IO: {
(void)osCloseHandle(pFile->h);
pFile->h = NULL;
return SQLITE_OK;
}
case SQLITE_FCNTL_TEMPFILENAME: {
char *zTFile = 0;
int rc = winGetTempname(pFile->pVfs, &zTFile);
if( rc==SQLITE_OK ){
*(char**)pArg = zTFile;
}
OSTRACE(("FCNTL file=%p, rc=%s\n", pFile->h, sqlite3ErrName(rc)));
|
| ︙ | ︙ | |||
50387 50388 50389 50390 50391 50392 50393 |
}
/*
** Return a vector of device characteristics.
*/
static int winDeviceCharacteristics(sqlite3_file *id){
winFile *p = (winFile*)id;
| | | 50443 50444 50445 50446 50447 50448 50449 50450 50451 50452 50453 50454 50455 50456 50457 |
}
/*
** Return a vector of device characteristics.
*/
static int winDeviceCharacteristics(sqlite3_file *id){
winFile *p = (winFile*)id;
return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN | SQLITE_IOCAP_SUBPAGE_READ |
((p->ctrlFlags & WINFILE_PSOW)?SQLITE_IOCAP_POWERSAFE_OVERWRITE:0);
}
/*
** Windows will only let you create file view mappings
** on allocation size granularity boundaries.
** During sqlite3_os_init() we do a GetSystemInfo()
|
| ︙ | ︙ | |||
51775 51776 51777 51778 51779 51780 51781 | /* If argument zPath is a NULL pointer, this function is required to open ** a temporary file. Use this buffer to store the file name in. */ char *zTmpname = 0; /* For temporary filename, if necessary. */ int rc = SQLITE_OK; /* Function Return Code */ #if !defined(NDEBUG) || SQLITE_OS_WINCE | | | 51831 51832 51833 51834 51835 51836 51837 51838 51839 51840 51841 51842 51843 51844 51845 | /* If argument zPath is a NULL pointer, this function is required to open ** a temporary file. Use this buffer to store the file name in. */ char *zTmpname = 0; /* For temporary filename, if necessary. */ int rc = SQLITE_OK; /* Function Return Code */ #if !defined(NDEBUG) || SQLITE_OS_WINCE int eType = flags&0x0FFF00; /* Type of file to open */ #endif int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); int isCreate = (flags & SQLITE_OPEN_CREATE); int isReadonly = (flags & SQLITE_OPEN_READONLY); int isReadWrite = (flags & SQLITE_OPEN_READWRITE); |
| ︙ | ︙ | |||
57976 57977 57978 57979 57980 57981 57982 | */ #if SQLITE_MAX_MMAP_SIZE>0 # define USEFETCH(x) ((x)->bUseFetch) #else # define USEFETCH(x) 0 #endif | < < < < < < < < < < < < < < | > | | > > | | | > > > > > | 58032 58033 58034 58035 58036 58037 58038 58039 58040 58041 58042 58043 58044 58045 58046 58047 58048 58049 58050 58051 58052 58053 58054 58055 58056 58057 58058 58059 58060 58061 58062 58063 58064 58065 58066 58067 58068 58069 58070 58071 58072 |
*/
#if SQLITE_MAX_MMAP_SIZE>0
# define USEFETCH(x) ((x)->bUseFetch)
#else
# define USEFETCH(x) 0
#endif
#ifdef SQLITE_DIRECT_OVERFLOW_READ
/*
** Return true if page pgno can be read directly from the database file
** by the b-tree layer. This is the case if:
**
** (1) the database file is open
** (2) the VFS for the database is able to do unaligned sub-page reads
** (3) there are no dirty pages in the cache, and
** (4) the desired page is not currently in the wal file.
*/
SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){
assert( pPager!=0 );
assert( pPager->fd!=0 );
if( pPager->fd->pMethods==0 ) return 0; /* Case (1) */
if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0; /* Failed (3) */
#ifndef SQLITE_OMIT_WAL
if( pPager->pWal ){
u32 iRead = 0;
(void)sqlite3WalFindFrame(pPager->pWal, pgno, &iRead);
return iRead==0; /* Condition (4) */
}
#endif
assert( pPager->fd->pMethods->xDeviceCharacteristics!=0 );
if( (pPager->fd->pMethods->xDeviceCharacteristics(pPager->fd)
& SQLITE_IOCAP_SUBPAGE_READ)==0 ){
return 0; /* Case (2) */
}
return 1;
}
#endif
#ifndef SQLITE_OMIT_WAL
# define pagerUseWal(x) ((x)->pWal!=0)
#else
|
| ︙ | ︙ | |||
59267 59268 59269 59270 59271 59272 59273 |
** https://bugzilla.mozilla.org/show_bug.cgi?id=1072773
*/
rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags);
}
}
pPager->journalOff = 0;
}else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST
| | | 59317 59318 59319 59320 59321 59322 59323 59324 59325 59326 59327 59328 59329 59330 59331 |
** https://bugzilla.mozilla.org/show_bug.cgi?id=1072773
*/
rc = sqlite3OsSync(pPager->jfd, pPager->syncFlags);
}
}
pPager->journalOff = 0;
}else if( pPager->journalMode==PAGER_JOURNALMODE_PERSIST
|| (pPager->exclusiveMode && pPager->journalMode<PAGER_JOURNALMODE_WAL)
){
rc = zeroJournalHdr(pPager, hasSuper||pPager->tempFile);
pPager->journalOff = 0;
}else{
/* This branch may be executed with Pager.journalMode==MEMORY if
** a hot-journal was just rolled back. In this case the journal
** file should be closed and deleted. If this connection writes to
|
| ︙ | ︙ | |||
67977 67978 67979 67980 67981 67982 67983 |
** checkpoint process do as much work as possible. This routine might
** update values of the aReadMark[] array in the header, but if it does
** so it takes care to hold an exclusive lock on the corresponding
** WAL_READ_LOCK() while changing values.
*/
static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){
volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */
| < < < < | 68027 68028 68029 68030 68031 68032 68033 68034 68035 68036 68037 68038 68039 68040 68041 |
** checkpoint process do as much work as possible. This routine might
** update values of the aReadMark[] array in the header, but if it does
** so it takes care to hold an exclusive lock on the corresponding
** WAL_READ_LOCK() while changing values.
*/
static int walTryBeginRead(Wal *pWal, int *pChanged, int useWal, int *pCnt){
volatile WalCkptInfo *pInfo; /* Checkpoint information in wal-index */
int rc = SQLITE_OK; /* Return code */
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
int nBlockTmout = 0;
#endif
assert( pWal->readLock<0 ); /* Not currently locked */
/* useWal may only be set for read/write connections */
|
| ︙ | ︙ | |||
68087 68088 68089 68090 68091 68092 68093 |
}
}
assert( pWal->nWiData>0 );
assert( pWal->apWiData[0]!=0 );
pInfo = walCkptInfo(pWal);
SEH_INJECT_FAULT;
| > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | 68133 68134 68135 68136 68137 68138 68139 68140 68141 68142 68143 68144 68145 68146 68147 68148 68149 68150 68151 68152 68153 68154 68155 68156 68157 68158 68159 68160 68161 68162 68163 68164 68165 68166 68167 68168 68169 68170 68171 68172 68173 68174 68175 68176 68177 68178 68179 68180 68181 68182 68183 68184 68185 68186 68187 68188 68189 68190 68191 68192 68193 68194 68195 68196 68197 68198 68199 68200 68201 68202 68203 68204 68205 68206 68207 68208 68209 68210 68211 68212 68213 68214 68215 68216 68217 68218 68219 68220 68221 68222 68223 68224 68225 68226 68227 68228 68229 68230 68231 68232 68233 68234 68235 68236 68237 68238 68239 68240 68241 68242 68243 68244 68245 68246 68247 68248 68249 68250 68251 68252 68253 68254 68255 68256 68257 68258 68259 68260 68261 68262 68263 68264 68265 68266 68267 68268 68269 68270 68271 68272 68273 68274 68275 68276 68277 68278 68279 68280 68281 68282 68283 68284 68285 68286 68287 |
}
}
assert( pWal->nWiData>0 );
assert( pWal->apWiData[0]!=0 );
pInfo = walCkptInfo(pWal);
SEH_INJECT_FAULT;
{
u32 mxReadMark; /* Largest aReadMark[] value */
int mxI; /* Index of largest aReadMark[] value */
int i; /* Loop counter */
u32 mxFrame; /* Wal frame to lock to */
if( !useWal && AtomicLoad(&pInfo->nBackfill)==pWal->hdr.mxFrame
#ifdef SQLITE_ENABLE_SNAPSHOT
&& ((pWal->bGetSnapshot==0 && pWal->pSnapshot==0) || pWal->hdr.mxFrame==0)
#endif
){
/* The WAL has been completely backfilled (or it is empty).
** and can be safely ignored.
*/
rc = walLockShared(pWal, WAL_READ_LOCK(0));
walShmBarrier(pWal);
if( rc==SQLITE_OK ){
if( memcmp((void *)walIndexHdr(pWal), &pWal->hdr,sizeof(WalIndexHdr)) ){
/* It is not safe to allow the reader to continue here if frames
** may have been appended to the log before READ_LOCK(0) was obtained.
** When holding READ_LOCK(0), the reader ignores the entire log file,
** which implies that the database file contains a trustworthy
** snapshot. Since holding READ_LOCK(0) prevents a checkpoint from
** happening, this is usually correct.
**
** However, if frames have been appended to the log (or if the log
** is wrapped and written for that matter) before the READ_LOCK(0)
** is obtained, that is not necessarily true. A checkpointer may
** have started to backfill the appended frames but crashed before
** it finished. Leaving a corrupt image in the database file.
*/
walUnlockShared(pWal, WAL_READ_LOCK(0));
return WAL_RETRY;
}
pWal->readLock = 0;
return SQLITE_OK;
}else if( rc!=SQLITE_BUSY ){
return rc;
}
}
/* If we get this far, it means that the reader will want to use
** the WAL to get at content from recent commits. The job now is
** to select one of the aReadMark[] entries that is closest to
** but not exceeding pWal->hdr.mxFrame and lock that entry.
*/
mxReadMark = 0;
mxI = 0;
mxFrame = pWal->hdr.mxFrame;
#ifdef SQLITE_ENABLE_SNAPSHOT
if( pWal->pSnapshot && pWal->pSnapshot->mxFrame<mxFrame ){
mxFrame = pWal->pSnapshot->mxFrame;
}
#endif
for(i=1; i<WAL_NREADER; i++){
u32 thisMark = AtomicLoad(pInfo->aReadMark+i); SEH_INJECT_FAULT;
if( mxReadMark<=thisMark && thisMark<=mxFrame ){
assert( thisMark!=READMARK_NOT_USED );
mxReadMark = thisMark;
mxI = i;
}
}
if( (pWal->readOnly & WAL_SHM_RDONLY)==0
&& (mxReadMark<mxFrame || mxI==0)
){
for(i=1; i<WAL_NREADER; i++){
rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
if( rc==SQLITE_OK ){
AtomicStore(pInfo->aReadMark+i,mxFrame);
mxReadMark = mxFrame;
mxI = i;
walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
break;
}else if( rc!=SQLITE_BUSY ){
return rc;
}
}
}
if( mxI==0 ){
assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 );
return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT;
}
(void)walEnableBlockingMs(pWal, nBlockTmout);
rc = walLockShared(pWal, WAL_READ_LOCK(mxI));
walDisableBlocking(pWal);
if( rc ){
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
if( rc==SQLITE_BUSY_TIMEOUT ){
*pCnt |= WAL_RETRY_BLOCKED_MASK;
}
#else
assert( rc!=SQLITE_BUSY_TIMEOUT );
#endif
assert((rc&0xFF)!=SQLITE_BUSY||rc==SQLITE_BUSY||rc==SQLITE_BUSY_TIMEOUT);
return (rc&0xFF)==SQLITE_BUSY ? WAL_RETRY : rc;
}
/* Now that the read-lock has been obtained, check that neither the
** value in the aReadMark[] array or the contents of the wal-index
** header have changed.
**
** It is necessary to check that the wal-index header did not change
** between the time it was read and when the shared-lock was obtained
** on WAL_READ_LOCK(mxI) was obtained to account for the possibility
** that the log file may have been wrapped by a writer, or that frames
** that occur later in the log than pWal->hdr.mxFrame may have been
** copied into the database by a checkpointer. If either of these things
** happened, then reading the database with the current value of
** pWal->hdr.mxFrame risks reading a corrupted snapshot. So, retry
** instead.
**
** Before checking that the live wal-index header has not changed
** since it was read, set Wal.minFrame to the first frame in the wal
** file that has not yet been checkpointed. This client will not need
** to read any frames earlier than minFrame from the wal file - they
** can be safely read directly from the database file.
**
** Because a ShmBarrier() call is made between taking the copy of
** nBackfill and checking that the wal-header in shared-memory still
** matches the one cached in pWal->hdr, it is guaranteed that the
** checkpointer that set nBackfill was not working with a wal-index
** header newer than that cached in pWal->hdr. If it were, that could
** cause a problem. The checkpointer could omit to checkpoint
** a version of page X that lies before pWal->minFrame (call that version
** A) on the basis that there is a newer version (version B) of the same
** page later in the wal file. But if version B happens to like past
** frame pWal->hdr.mxFrame - then the client would incorrectly assume
** that it can read version A from the database file. However, since
** we can guarantee that the checkpointer that set nBackfill could not
** see any pages past pWal->hdr.mxFrame, this problem does not come up.
*/
pWal->minFrame = AtomicLoad(&pInfo->nBackfill)+1; SEH_INJECT_FAULT;
walShmBarrier(pWal);
if( AtomicLoad(pInfo->aReadMark+mxI)!=mxReadMark
|| memcmp((void *)walIndexHdr(pWal), &pWal->hdr, sizeof(WalIndexHdr))
){
walUnlockShared(pWal, WAL_READ_LOCK(mxI));
return WAL_RETRY;
}else{
assert( mxReadMark<=pWal->hdr.mxFrame );
pWal->readLock = (i16)mxI;
}
}
return rc;
}
#ifdef SQLITE_ENABLE_SNAPSHOT
/*
** This function does the work of sqlite3WalSnapshotRecover().
|
| ︙ | ︙ | |||
87101 87102 87103 87104 87105 87106 87107 87108 87109 87110 87111 87112 87113 87114 |
** Mem.db = db
** Mem.szMalloc = 0
**
** All other fields of Mem can safely remain uninitialized for now. They
** will be initialized before use.
*/
static void initMemArray(Mem *p, int N, sqlite3 *db, u16 flags){
if( N>0 ){
do{
p->flags = flags;
p->db = db;
p->szMalloc = 0;
#ifdef SQLITE_DEBUG
p->pScopyFrom = 0;
| > | 87153 87154 87155 87156 87157 87158 87159 87160 87161 87162 87163 87164 87165 87166 87167 |
** Mem.db = db
** Mem.szMalloc = 0
**
** All other fields of Mem can safely remain uninitialized for now. They
** will be initialized before use.
*/
static void initMemArray(Mem *p, int N, sqlite3 *db, u16 flags){
assert( db!=0 );
if( N>0 ){
do{
p->flags = flags;
p->db = db;
p->szMalloc = 0;
#ifdef SQLITE_DEBUG
p->pScopyFrom = 0;
|
| ︙ | ︙ | |||
87126 87127 87128 87129 87130 87131 87132 87133 87134 87135 87136 87137 87138 87139 |
** will be unchanged. Mem elements which had something freed will be
** set to MEM_Undefined.
*/
static void releaseMemArray(Mem *p, int N){
if( p && N ){
Mem *pEnd = &p[N];
sqlite3 *db = p->db;
if( db->pnBytesFreed ){
do{
if( p->szMalloc ) sqlite3DbFree(db, p->zMalloc);
}while( (++p)<pEnd );
return;
}
do{
| > | 87179 87180 87181 87182 87183 87184 87185 87186 87187 87188 87189 87190 87191 87192 87193 |
** will be unchanged. Mem elements which had something freed will be
** set to MEM_Undefined.
*/
static void releaseMemArray(Mem *p, int N){
if( p && N ){
Mem *pEnd = &p[N];
sqlite3 *db = p->db;
assert( db!=0 );
if( db->pnBytesFreed ){
do{
if( p->szMalloc ) sqlite3DbFree(db, p->zMalloc);
}while( (++p)<pEnd );
return;
}
do{
|
| ︙ | ︙ | |||
87606 87607 87608 87609 87610 87611 87612 87613 87614 87615 87616 87617 87618 87619 | struct ReusableSpace x; /* Reusable bulk memory */ assert( p!=0 ); assert( p->nOp>0 ); assert( pParse!=0 ); assert( p->eVdbeState==VDBE_INIT_STATE ); assert( pParse==p->pParse ); p->pVList = pParse->pVList; pParse->pVList = 0; db = p->db; assert( db->mallocFailed==0 ); nVar = pParse->nVar; nMem = pParse->nMem; nCursor = pParse->nTab; | > | 87660 87661 87662 87663 87664 87665 87666 87667 87668 87669 87670 87671 87672 87673 87674 | struct ReusableSpace x; /* Reusable bulk memory */ assert( p!=0 ); assert( p->nOp>0 ); assert( pParse!=0 ); assert( p->eVdbeState==VDBE_INIT_STATE ); assert( pParse==p->pParse ); assert( pParse->db==p->db ); p->pVList = pParse->pVList; pParse->pVList = 0; db = p->db; assert( db->mallocFailed==0 ); nVar = pParse->nVar; nMem = pParse->nMem; nCursor = pParse->nTab; |
| ︙ | ︙ | |||
90486 90487 90488 90489 90490 90491 90492 90493 90494 90495 90496 90497 90498 90499 |
db->pPreUpdate = &preupdate;
db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2);
db->pPreUpdate = 0;
sqlite3DbFree(db, preupdate.aRecord);
vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pUnpacked);
vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pNewUnpacked);
if( preupdate.aNew ){
int i;
for(i=0; i<pCsr->nField; i++){
sqlite3VdbeMemRelease(&preupdate.aNew[i]);
}
sqlite3DbNNFreeNN(db, preupdate.aNew);
}
| > | 90541 90542 90543 90544 90545 90546 90547 90548 90549 90550 90551 90552 90553 90554 90555 |
db->pPreUpdate = &preupdate;
db->xPreUpdateCallback(db->pPreUpdateArg, db, op, zDb, zTbl, iKey1, iKey2);
db->pPreUpdate = 0;
sqlite3DbFree(db, preupdate.aRecord);
vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pUnpacked);
vdbeFreeUnpacked(db, preupdate.keyinfo.nKeyField+1, preupdate.pNewUnpacked);
sqlite3VdbeMemRelease(&preupdate.oldipk);
if( preupdate.aNew ){
int i;
for(i=0; i<pCsr->nField; i++){
sqlite3VdbeMemRelease(&preupdate.aNew[i]);
}
sqlite3DbNNFreeNN(db, preupdate.aNew);
}
|
| ︙ | ︙ | |||
91842 91843 91844 91845 91846 91847 91848 | ** ** Specifically, this is called from within: ** ** sqlite3_column_int() ** sqlite3_column_int64() ** sqlite3_column_text() ** sqlite3_column_text16() | | | 91898 91899 91900 91901 91902 91903 91904 91905 91906 91907 91908 91909 91910 91911 91912 |
**
** Specifically, this is called from within:
**
** sqlite3_column_int()
** sqlite3_column_int64()
** sqlite3_column_text()
** sqlite3_column_text16()
** sqlite3_column_double()
** sqlite3_column_bytes()
** sqlite3_column_bytes16()
** sqlite3_column_blob()
*/
static void columnMallocFailure(sqlite3_stmt *pStmt)
{
/* If malloc() failed during an encoding conversion within an
|
| ︙ | ︙ | |||
92704 92705 92706 92707 92708 92709 92710 |
iIdx = sqlite3TableColumnToIndex(p->pPk, iIdx);
}
if( iIdx>=p->pCsr->nField || iIdx<0 ){
rc = SQLITE_RANGE;
goto preupdate_old_out;
}
| < < < < < < < < < < < < < < < < < < < < < < > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | 92760 92761 92762 92763 92764 92765 92766 92767 92768 92769 92770 92771 92772 92773 92774 92775 92776 92777 92778 92779 92780 92781 92782 92783 92784 92785 92786 92787 92788 92789 92790 92791 92792 92793 92794 92795 92796 92797 92798 92799 92800 92801 92802 92803 92804 92805 92806 92807 92808 92809 92810 92811 92812 92813 92814 92815 92816 92817 92818 92819 92820 92821 92822 92823 92824 92825 92826 92827 92828 92829 92830 92831 |
iIdx = sqlite3TableColumnToIndex(p->pPk, iIdx);
}
if( iIdx>=p->pCsr->nField || iIdx<0 ){
rc = SQLITE_RANGE;
goto preupdate_old_out;
}
if( iIdx==p->pTab->iPKey ){
*ppValue = pMem = &p->oldipk;
sqlite3VdbeMemSetInt64(pMem, p->iKey1);
}else{
/* If the old.* record has not yet been loaded into memory, do so now. */
if( p->pUnpacked==0 ){
u32 nRec;
u8 *aRec;
assert( p->pCsr->eCurType==CURTYPE_BTREE );
nRec = sqlite3BtreePayloadSize(p->pCsr->uc.pCursor);
aRec = sqlite3DbMallocRaw(db, nRec);
if( !aRec ) goto preupdate_old_out;
rc = sqlite3BtreePayload(p->pCsr->uc.pCursor, 0, nRec, aRec);
if( rc==SQLITE_OK ){
p->pUnpacked = vdbeUnpackRecord(&p->keyinfo, nRec, aRec);
if( !p->pUnpacked ) rc = SQLITE_NOMEM;
}
if( rc!=SQLITE_OK ){
sqlite3DbFree(db, aRec);
goto preupdate_old_out;
}
p->aRecord = aRec;
}
pMem = *ppValue = &p->pUnpacked->aMem[iIdx];
if( iIdx>=p->pUnpacked->nField ){
/* This occurs when the table has been extended using ALTER TABLE
** ADD COLUMN. The value to return is the default value of the column. */
Column *pCol = &p->pTab->aCol[iIdx];
if( pCol->iDflt>0 ){
if( p->apDflt==0 ){
int nByte = sizeof(sqlite3_value*)*p->pTab->nCol;
p->apDflt = (sqlite3_value**)sqlite3DbMallocZero(db, nByte);
if( p->apDflt==0 ) goto preupdate_old_out;
}
if( p->apDflt[iIdx]==0 ){
sqlite3_value *pVal = 0;
Expr *pDflt;
assert( p->pTab!=0 && IsOrdinaryTable(p->pTab) );
pDflt = p->pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr;
rc = sqlite3ValueFromExpr(db, pDflt, ENC(db), pCol->affinity, &pVal);
if( rc==SQLITE_OK && pVal==0 ){
rc = SQLITE_CORRUPT_BKPT;
}
p->apDflt[iIdx] = pVal;
}
*ppValue = p->apDflt[iIdx];
}else{
*ppValue = (sqlite3_value *)columnNullValue();
}
}else if( p->pTab->aCol[iIdx].affinity==SQLITE_AFF_REAL ){
if( pMem->flags & (MEM_Int|MEM_IntReal) ){
testcase( pMem->flags & MEM_Int );
testcase( pMem->flags & MEM_IntReal );
sqlite3VdbeMemRealify(pMem);
}
}
}
preupdate_old_out:
sqlite3Error(db, rc);
return sqlite3ApiExit(db, rc);
}
|
| ︙ | ︙ | |||
97911 97912 97913 97914 97915 97916 97917 97918 97919 97920 97921 97922 97923 97924 97925 97926 97927 |
pCx->pgnoRoot = SCHEMA_ROOT;
rc = sqlite3BtreeCursor(pCx->ub.pBtx, SCHEMA_ROOT, BTREE_WRCSR,
0, pCx->uc.pCursor);
pCx->isTable = 1;
}
}
pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED);
if( rc ){
assert( !sqlite3BtreeClosesWithCursor(pCx->ub.pBtx, pCx->uc.pCursor) );
sqlite3BtreeClose(pCx->ub.pBtx);
}else{
assert( sqlite3BtreeClosesWithCursor(pCx->ub.pBtx, pCx->uc.pCursor) );
}
}
}
if( rc ) goto abort_due_to_error;
pCx->nullRow = 1;
| > > | 97971 97972 97973 97974 97975 97976 97977 97978 97979 97980 97981 97982 97983 97984 97985 97986 97987 97988 97989 |
pCx->pgnoRoot = SCHEMA_ROOT;
rc = sqlite3BtreeCursor(pCx->ub.pBtx, SCHEMA_ROOT, BTREE_WRCSR,
0, pCx->uc.pCursor);
pCx->isTable = 1;
}
}
pCx->isOrdered = (pOp->p5!=BTREE_UNORDERED);
assert( p->apCsr[pOp->p1]==pCx );
if( rc ){
assert( !sqlite3BtreeClosesWithCursor(pCx->ub.pBtx, pCx->uc.pCursor) );
sqlite3BtreeClose(pCx->ub.pBtx);
p->apCsr[pOp->p1] = 0; /* Not required; helps with static analysis */
}else{
assert( sqlite3BtreeClosesWithCursor(pCx->ub.pBtx, pCx->uc.pCursor) );
}
}
}
if( rc ) goto abort_due_to_error;
pCx->nullRow = 1;
|
| ︙ | ︙ | |||
109843 109844 109845 109846 109847 109848 109849 |
p4 = sqlite3BinaryCompareCollSeq(pParse, pRight, pLeft);
}else{
p4 = sqlite3BinaryCompareCollSeq(pParse, pLeft, pRight);
}
p5 = binaryCompareP5(pLeft, pRight, jumpIfNull);
addr = sqlite3VdbeAddOp4(pParse->pVdbe, opcode, in2, dest, in1,
(void*)p4, P4_COLLSEQ);
| | | 109905 109906 109907 109908 109909 109910 109911 109912 109913 109914 109915 109916 109917 109918 109919 |
p4 = sqlite3BinaryCompareCollSeq(pParse, pRight, pLeft);
}else{
p4 = sqlite3BinaryCompareCollSeq(pParse, pLeft, pRight);
}
p5 = binaryCompareP5(pLeft, pRight, jumpIfNull);
addr = sqlite3VdbeAddOp4(pParse->pVdbe, opcode, in2, dest, in1,
(void*)p4, P4_COLLSEQ);
sqlite3VdbeChangeP5(pParse->pVdbe, (u16)p5);
return addr;
}
/*
** Return true if expression pExpr is a vector, or false otherwise.
**
** A vector is defined as any expression that results in two or more
|
| ︙ | ︙ | |||
112010 112011 112012 112013 112014 112015 112016 | ** (3) pSrc cannot be part of the left operand for a RIGHT JOIN. ** (Is there some way to relax this constraint?) ** ** (4) If pSrc is the right operand of a LEFT JOIN, then... ** (4a) pExpr must come from an ON clause.. ** (4b) and specifically the ON clause associated with the LEFT JOIN. ** | | | 112072 112073 112074 112075 112076 112077 112078 112079 112080 112081 112082 112083 112084 112085 112086 | ** (3) pSrc cannot be part of the left operand for a RIGHT JOIN. ** (Is there some way to relax this constraint?) ** ** (4) If pSrc is the right operand of a LEFT JOIN, then... ** (4a) pExpr must come from an ON clause.. ** (4b) and specifically the ON clause associated with the LEFT JOIN. ** ** (5) If pSrc is the right operand of a LEFT JOIN or the left ** operand of a RIGHT JOIN, then pExpr must be from the WHERE ** clause, not an ON clause. ** ** (6) Either: ** ** (6a) pExpr does not originate in an ON or USING clause, or ** |
| ︙ | ︙ | |||
115544 115545 115546 115547 115548 115549 115550 115551 | ** or NULL value - then the VDBE currently being prepared is configured ** to re-prepare each time a new value is bound to variable pVar. ** ** Additionally, if pExpr is a simple SQL value and the value is the ** same as that currently bound to variable pVar, non-zero is returned. ** Otherwise, if the values are not the same or if pExpr is not a simple ** SQL value, zero is returned. */ | > > > | | > > > > | < | 115606 115607 115608 115609 115610 115611 115612 115613 115614 115615 115616 115617 115618 115619 115620 115621 115622 115623 115624 115625 115626 115627 115628 115629 115630 115631 115632 115633 115634 115635 115636 115637 115638 115639 115640 115641 115642 115643 115644 115645 115646 115647 115648 115649 115650 |
** or NULL value - then the VDBE currently being prepared is configured
** to re-prepare each time a new value is bound to variable pVar.
**
** Additionally, if pExpr is a simple SQL value and the value is the
** same as that currently bound to variable pVar, non-zero is returned.
** Otherwise, if the values are not the same or if pExpr is not a simple
** SQL value, zero is returned.
**
** If the SQLITE_EnableQPSG flag is set on the database connection, then
** this routine always returns false.
*/
static SQLITE_NOINLINE int exprCompareVariable(
const Parse *pParse,
const Expr *pVar,
const Expr *pExpr
){
int res = 2;
int iVar;
sqlite3_value *pL, *pR = 0;
if( pExpr->op==TK_VARIABLE && pVar->iColumn==pExpr->iColumn ){
return 0;
}
if( (pParse->db->flags & SQLITE_EnableQPSG)!=0 ) return 2;
sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, SQLITE_AFF_BLOB, &pR);
if( pR ){
iVar = pVar->iColumn;
sqlite3VdbeSetVarmask(pParse->pVdbe, iVar);
pL = sqlite3VdbeGetBoundValue(pParse->pReprepare, iVar, SQLITE_AFF_BLOB);
if( pL ){
if( sqlite3_value_type(pL)==SQLITE_TEXT ){
sqlite3_value_text(pL); /* Make sure the encoding is UTF-8 */
}
res = sqlite3MemCompare(pL, pR, 0) ? 2 : 0;
}
sqlite3ValueFree(pR);
sqlite3ValueFree(pL);
}
return res;
}
/*
** Do a deep comparison of two expression trees. Return 0 if the two
** expressions are completely identical. Return 1 if they differ only
** by a COLLATE operator at the top level. Return 2 if there are differences
|
| ︙ | ︙ | |||
115594 115595 115596 115597 115598 115599 115600 | ** returns 2, then you do not really know for certain if the two ** expressions are the same. But if you get a 0 or 1 return, then you ** can be sure the expressions are the same. In the places where ** this routine is used, it does not hurt to get an extra 2 - that ** just might result in some slightly slower code. But returning ** an incorrect 0 or 1 could lead to a malfunction. ** | | | | < < | | | | 115662 115663 115664 115665 115666 115667 115668 115669 115670 115671 115672 115673 115674 115675 115676 115677 115678 115679 115680 115681 115682 115683 115684 115685 115686 115687 115688 115689 115690 115691 115692 |
** returns 2, then you do not really know for certain if the two
** expressions are the same. But if you get a 0 or 1 return, then you
** can be sure the expressions are the same. In the places where
** this routine is used, it does not hurt to get an extra 2 - that
** just might result in some slightly slower code. But returning
** an incorrect 0 or 1 could lead to a malfunction.
**
** If pParse is not NULL and SQLITE_EnableQPSG is off then TK_VARIABLE
** terms in pA with bindings in pParse->pReprepare can be matched against
** literals in pB. The pParse->pVdbe->expmask bitmask is updated for
** each variable referenced.
*/
SQLITE_PRIVATE int sqlite3ExprCompare(
const Parse *pParse,
const Expr *pA,
const Expr *pB,
int iTab
){
u32 combinedFlags;
if( pA==0 || pB==0 ){
return pB==pA ? 0 : 2;
}
if( pParse && pA->op==TK_VARIABLE ){
return exprCompareVariable(pParse, pA, pB);
}
combinedFlags = pA->flags | pB->flags;
if( combinedFlags & EP_IntValue ){
if( (pA->flags&pB->flags&EP_IntValue)!=0 && pA->u.iValue==pB->u.iValue ){
return 0;
}
return 2;
|
| ︙ | ︙ | |||
115806 115807 115808 115809 115810 115811 115812 115813 115814 115815 115816 115817 115818 |
case TK_BITNOT:
case TK_NOT: {
return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1);
}
}
return 0;
}
/*
** Return true if we can prove the pE2 will always be true if pE1 is
** true. Return false if we cannot complete the proof or if pE2 might
** be false. Examples:
**
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | > > | 115872 115873 115874 115875 115876 115877 115878 115879 115880 115881 115882 115883 115884 115885 115886 115887 115888 115889 115890 115891 115892 115893 115894 115895 115896 115897 115898 115899 115900 115901 115902 115903 115904 115905 115906 115907 115908 115909 115910 115911 115912 115913 115914 115915 115916 115917 115918 115919 115920 115921 115922 115923 115924 115925 115926 115927 115928 115929 115930 115931 115932 115933 115934 115935 115936 115937 115938 115939 115940 115941 115942 115943 115944 115945 115946 115947 115948 115949 115950 |
case TK_BITNOT:
case TK_NOT: {
return exprImpliesNotNull(pParse, p->pLeft, pNN, iTab, 1);
}
}
return 0;
}
/*
** Return true if the boolean value of the expression is always either
** FALSE or NULL.
*/
static int sqlite3ExprIsNotTrue(Expr *pExpr){
int v;
if( pExpr->op==TK_NULL ) return 1;
if( pExpr->op==TK_TRUEFALSE && sqlite3ExprTruthValue(pExpr)==0 ) return 1;
v = 1;
if( sqlite3ExprIsInteger(pExpr, &v, 0) && v==0 ) return 1;
return 0;
}
/*
** Return true if the expression is one of the following:
**
** CASE WHEN x THEN y END
** CASE WHEN x THEN y ELSE NULL END
** CASE WHEN x THEN y ELSE false END
** iif(x,y)
** iif(x,y,NULL)
** iif(x,y,false)
*/
static int sqlite3ExprIsIIF(sqlite3 *db, const Expr *pExpr){
ExprList *pList;
if( pExpr->op==TK_FUNCTION ){
const char *z = pExpr->u.zToken;
FuncDef *pDef;
if( (z[0]!='i' && z[0]!='I') ) return 0;
if( pExpr->x.pList==0 ) return 0;
pDef = sqlite3FindFunction(db, z, pExpr->x.pList->nExpr, ENC(db), 0);
#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
if( pDef==0 ) return 0;
#else
if( NEVER(pDef==0) ) return 0;
#endif
if( (pDef->funcFlags & SQLITE_FUNC_INLINE)==0 ) return 0;
if( SQLITE_PTR_TO_INT(pDef->pUserData)!=INLINEFUNC_iif ) return 0;
}else if( pExpr->op==TK_CASE ){
if( pExpr->pLeft!=0 ) return 0;
}else{
return 0;
}
pList = pExpr->x.pList;
assert( pList!=0 );
if( pList->nExpr==2 ) return 1;
if( pList->nExpr==3 && sqlite3ExprIsNotTrue(pList->a[2].pExpr) ) return 1;
return 0;
}
/*
** Return true if we can prove the pE2 will always be true if pE1 is
** true. Return false if we cannot complete the proof or if pE2 might
** be false. Examples:
**
** pE1: x==5 pE2: x==5 Result: true
** pE1: x>0 pE2: x==5 Result: false
** pE1: x=21 pE2: x=21 OR y=43 Result: true
** pE1: x!=123 pE2: x IS NOT NULL Result: true
** pE1: x!=?1 pE2: x IS NOT NULL Result: true
** pE1: x IS NULL pE2: x IS NOT NULL Result: false
** pE1: x IS ?2 pE2: x IS NOT NULL Result: false
** pE1: iif(x,y) pE2: x Result: true
** PE1: iif(x,y,0) pE2: x Result: true
**
** When comparing TK_COLUMN nodes between pE1 and pE2, if pE2 has
** Expr.iTable<0 then assume a table number given by iTab.
**
** If pParse is not NULL, then the values of bound variables in pE1 are
** compared against literal values in pE2 and pParse->pVdbe->expmask is
** modified to record which bound variables are referenced. If pParse
|
| ︙ | ︙ | |||
115852 115853 115854 115855 115856 115857 115858 115859 115860 115861 115862 115863 115864 115865 |
return 1;
}
if( pE2->op==TK_NOTNULL
&& exprImpliesNotNull(pParse, pE1, pE2->pLeft, iTab, 0)
){
return 1;
}
return 0;
}
/* This is a helper function to impliesNotNullRow(). In this routine,
** set pWalker->eCode to one only if *both* of the input expressions
** separately have the implies-not-null-row property.
*/
| > > > | 115970 115971 115972 115973 115974 115975 115976 115977 115978 115979 115980 115981 115982 115983 115984 115985 115986 |
return 1;
}
if( pE2->op==TK_NOTNULL
&& exprImpliesNotNull(pParse, pE1, pE2->pLeft, iTab, 0)
){
return 1;
}
if( sqlite3ExprIsIIF(pParse->db, pE1) ){
return sqlite3ExprImpliesExpr(pParse,pE1->x.pList->a[0].pExpr,pE2,iTab);
}
return 0;
}
/* This is a helper function to impliesNotNullRow(). In this routine,
** set pWalker->eCode to one only if *both* of the input expressions
** separately have the implies-not-null-row property.
*/
|
| ︙ | ︙ | |||
121263 121264 121265 121266 121267 121268 121269 |
db->mDbFlags &= ~(DBFLAG_SchemaKnownOk);
if( !REOPEN_AS_MEMDB(db) ){
rc = sqlite3Init(db, &zErrDyn);
}
sqlite3BtreeLeaveAll(db);
assert( zErrDyn==0 || rc!=SQLITE_OK );
}
| < < < < < < < < < | 121384 121385 121386 121387 121388 121389 121390 121391 121392 121393 121394 121395 121396 121397 |
db->mDbFlags &= ~(DBFLAG_SchemaKnownOk);
if( !REOPEN_AS_MEMDB(db) ){
rc = sqlite3Init(db, &zErrDyn);
}
sqlite3BtreeLeaveAll(db);
assert( zErrDyn==0 || rc!=SQLITE_OK );
}
if( rc ){
if( ALWAYS(!REOPEN_AS_MEMDB(db)) ){
int iDb = db->nDb - 1;
assert( iDb>=2 );
if( db->aDb[iDb].pBt ){
sqlite3BtreeClose(db->aDb[iDb].pBt);
db->aDb[iDb].pBt = 0;
|
| ︙ | ︙ | |||
121769 121770 121771 121772 121773 121774 121775 |
int iDb /* Index of containing database. */
){
sqlite3 *db = pParse->db; /* Database handle */
char *zDb = db->aDb[iDb].zDbSName; /* Schema name of attached database */
int rc; /* Auth callback return code */
if( db->init.busy ) return SQLITE_OK;
| | < < < < | 121881 121882 121883 121884 121885 121886 121887 121888 121889 121890 121891 121892 121893 121894 121895 |
int iDb /* Index of containing database. */
){
sqlite3 *db = pParse->db; /* Database handle */
char *zDb = db->aDb[iDb].zDbSName; /* Schema name of attached database */
int rc; /* Auth callback return code */
if( db->init.busy ) return SQLITE_OK;
rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext);
if( rc==SQLITE_DENY ){
char *z = sqlite3_mprintf("%s.%s", zTab, zCol);
if( db->nDb>2 || iDb!=0 ) z = sqlite3_mprintf("%s.%z", zDb, z);
sqlite3ErrorMsg(pParse, "access to %z is prohibited", z);
pParse->rc = SQLITE_AUTH;
}else if( rc!=SQLITE_IGNORE && rc!=SQLITE_OK ){
sqliteAuthBadReturnCode(pParse);
|
| ︙ | ︙ | |||
121880 121881 121882 121883 121884 121885 121886 | ** The following testcase() macros show that any of the 3rd through 6th ** parameters can be either NULL or a string. */ testcase( zArg1==0 ); testcase( zArg2==0 ); testcase( zArg3==0 ); testcase( pParse->zAuthContext==0 ); | | < < < < | 121988 121989 121990 121991 121992 121993 121994 121995 121996 121997 121998 121999 122000 122001 122002 |
** The following testcase() macros show that any of the 3rd through 6th
** parameters can be either NULL or a string. */
testcase( zArg1==0 );
testcase( zArg2==0 );
testcase( zArg3==0 );
testcase( pParse->zAuthContext==0 );
rc = db->xAuth(db->pAuthArg,code,zArg1,zArg2,zArg3,pParse->zAuthContext);
if( rc==SQLITE_DENY ){
sqlite3ErrorMsg(pParse, "not authorized");
pParse->rc = SQLITE_AUTH;
}else if( rc!=SQLITE_OK && rc!=SQLITE_IGNORE ){
rc = SQLITE_DENY;
sqliteAuthBadReturnCode(pParse);
}
|
| ︙ | ︙ | |||
122117 122118 122119 122120 122121 122122 122123 |
sqlite3VdbeAddOp2(v, OP_Next, pReturning->iRetCur, addrRewind+1);
VdbeCoverage(v);
sqlite3VdbeJumpHere(v, addrRewind);
}
}
sqlite3VdbeAddOp0(v, OP_Halt);
| < < < < < < < < < < < | 122221 122222 122223 122224 122225 122226 122227 122228 122229 122230 122231 122232 122233 122234 |
sqlite3VdbeAddOp2(v, OP_Next, pReturning->iRetCur, addrRewind+1);
VdbeCoverage(v);
sqlite3VdbeJumpHere(v, addrRewind);
}
}
sqlite3VdbeAddOp0(v, OP_Halt);
/* The cookie mask contains one bit for each database file open.
** (Bit 0 is for main, bit 1 is for temp, and so forth.) Bits are
** set for each database that is used. Generate code to start a
** transaction on each used database and to verify the schema cookie
** on each used database.
*/
assert( pParse->nErr>0 || sqlite3VdbeGetOp(v, 0)->opcode==OP_Init );
|
| ︙ | ︙ | |||
122256 122257 122258 122259 122260 122261 122262 | sqlite3RunParser(pParse, zSql); db->mDbFlags = savedDbFlags; sqlite3DbFree(db, zSql); memcpy(PARSE_TAIL(pParse), saveBuf, PARSE_TAIL_SZ); pParse->nested--; } | < < < < < < < < < < < < < < < < < | 122349 122350 122351 122352 122353 122354 122355 122356 122357 122358 122359 122360 122361 122362 122363 122364 122365 122366 122367 122368 122369 122370 122371 122372 122373 122374 122375 122376 122377 122378 122379 122380 |
sqlite3RunParser(pParse, zSql);
db->mDbFlags = savedDbFlags;
sqlite3DbFree(db, zSql);
memcpy(PARSE_TAIL(pParse), saveBuf, PARSE_TAIL_SZ);
pParse->nested--;
}
/*
** Locate the in-memory structure that describes a particular database
** table given the name of that table and (optionally) the name of the
** database containing the table. Return NULL if not found.
**
** If zDatabase is 0, all databases are searched for the table and the
** first matching table is returned. (No checking for duplicate table
** names is done.) The search order is TEMP first, then MAIN, then any
** auxiliary databases added using the ATTACH command.
**
** See also sqlite3LocateTable().
*/
SQLITE_PRIVATE Table *sqlite3FindTable(sqlite3 *db, const char *zName, const char *zDatabase){
Table *p = 0;
int i;
/* All mutexes are required for schema access. Make sure we hold them. */
assert( zDatabase!=0 || sqlite3BtreeHoldsAllMutexes(db) );
if( zDatabase ){
for(i=0; i<db->nDb; i++){
if( sqlite3StrICmp(zDatabase, db->aDb[i].zDbSName)==0 ) break;
}
if( i>=db->nDb ){
/* No match against the official names. But always match "main"
** to schema 0 as a legacy fallback. */
|
| ︙ | ︙ | |||
125949 125950 125951 125952 125953 125954 125955 |
}
pDb = &db->aDb[iDb];
assert( pTab!=0 );
if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0
&& db->init.busy==0
&& pTblName!=0
| < < < | 126025 126026 126027 126028 126029 126030 126031 126032 126033 126034 126035 126036 126037 126038 |
}
pDb = &db->aDb[iDb];
assert( pTab!=0 );
if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0
&& db->init.busy==0
&& pTblName!=0
){
sqlite3ErrorMsg(pParse, "table %s may not be indexed", pTab->zName);
goto exit_create_index;
}
#ifndef SQLITE_OMIT_VIEW
if( IsView(pTab) ){
sqlite3ErrorMsg(pParse, "views may not be indexed");
|
| ︙ | ︙ | |||
129659 129660 129661 129662 129663 129664 129665 |
sqlite3_value **argv
){
const unsigned char *z;
const unsigned char *z2;
int len;
int p0type;
i64 p1, p2;
| < | | 129732 129733 129734 129735 129736 129737 129738 129739 129740 129741 129742 129743 129744 129745 129746 129747 129748 129749 129750 129751 129752 129753 129754 |
sqlite3_value **argv
){
const unsigned char *z;
const unsigned char *z2;
int len;
int p0type;
i64 p1, p2;
assert( argc==3 || argc==2 );
if( sqlite3_value_type(argv[1])==SQLITE_NULL
|| (argc==3 && sqlite3_value_type(argv[2])==SQLITE_NULL)
){
return;
}
p0type = sqlite3_value_type(argv[0]);
p1 = sqlite3_value_int64(argv[1]);
if( p0type==SQLITE_BLOB ){
len = sqlite3_value_bytes(argv[0]);
z = sqlite3_value_blob(argv[0]);
if( z==0 ) return;
assert( len==sqlite3_value_bytes(argv[0]) );
}else{
z = sqlite3_value_text(argv[0]);
|
| ︙ | ︙ | |||
129693 129694 129695 129696 129697 129698 129699 |
** as substr(X,1,N) - it returns the first N characters of X. This
** is essentially a back-out of the bug-fix in check-in [5fc125d362df4b8]
** from 2009-02-02 for compatibility of applications that exploited the
** old buggy behavior. */
if( p1==0 ) p1 = 1; /* <rdar://problem/6778339> */
#endif
if( argc==3 ){
| | < < < < > > > | < > | < | | > | > | > > | | | | | 129765 129766 129767 129768 129769 129770 129771 129772 129773 129774 129775 129776 129777 129778 129779 129780 129781 129782 129783 129784 129785 129786 129787 129788 129789 129790 129791 129792 129793 129794 129795 129796 129797 129798 129799 129800 129801 129802 129803 129804 129805 129806 129807 129808 129809 129810 129811 129812 129813 129814 129815 129816 129817 129818 129819 129820 129821 129822 129823 129824 129825 129826 129827 129828 129829 129830 129831 129832 129833 129834 129835 129836 129837 129838 129839 129840 129841 129842 129843 129844 129845 129846 129847 129848 129849 129850 129851 129852 129853 129854 |
** as substr(X,1,N) - it returns the first N characters of X. This
** is essentially a back-out of the bug-fix in check-in [5fc125d362df4b8]
** from 2009-02-02 for compatibility of applications that exploited the
** old buggy behavior. */
if( p1==0 ) p1 = 1; /* <rdar://problem/6778339> */
#endif
if( argc==3 ){
p2 = sqlite3_value_int64(argv[2]);
}else{
p2 = sqlite3_context_db_handle(context)->aLimit[SQLITE_LIMIT_LENGTH];
}
if( p1<0 ){
p1 += len;
if( p1<0 ){
if( p2<0 ){
p2 = 0;
}else{
p2 += p1;
}
p1 = 0;
}
}else if( p1>0 ){
p1--;
}else if( p2>0 ){
p2--;
}
if( p2<0 ){
if( p2<-p1 ){
p2 = p1;
}else{
p2 = -p2;
}
p1 -= p2;
}
assert( p1>=0 && p2>=0 );
if( p0type!=SQLITE_BLOB ){
while( *z && p1 ){
SQLITE_SKIP_UTF8(z);
p1--;
}
for(z2=z; *z2 && p2; p2--){
SQLITE_SKIP_UTF8(z2);
}
sqlite3_result_text64(context, (char*)z, z2-z, SQLITE_TRANSIENT,
SQLITE_UTF8);
}else{
if( p1>=len ){
p1 = p2 = 0;
}else if( p2>len-p1 ){
p2 = len-p1;
assert( p2>0 );
}
sqlite3_result_blob64(context, (char*)&z[p1], (u64)p2, SQLITE_TRANSIENT);
}
}
/*
** Implementation of the round() function
*/
#ifndef SQLITE_OMIT_FLOATING_POINT
static void roundFunc(sqlite3_context *context, int argc, sqlite3_value **argv){
i64 n = 0;
double r;
char *zBuf;
assert( argc==1 || argc==2 );
if( argc==2 ){
if( SQLITE_NULL==sqlite3_value_type(argv[1]) ) return;
n = sqlite3_value_int64(argv[1]);
if( n>30 ) n = 30;
if( n<0 ) n = 0;
}
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
r = sqlite3_value_double(argv[0]);
/* If Y==0 and X will fit in a 64-bit int,
** handle the rounding directly,
** otherwise use printf.
*/
if( r<-4503599627370496.0 || r>+4503599627370496.0 ){
/* The value has no fractional part so there is nothing to round */
}else if( n==0 ){
r = (double)((sqlite_int64)(r+(r<0?-0.5:+0.5)));
}else{
zBuf = sqlite3_mprintf("%!.*f",(int)n,r);
if( zBuf==0 ){
sqlite3_result_error_nomem(context);
return;
}
sqlite3AtoF(zBuf, &r, sqlite3Strlen30(zBuf), SQLITE_UTF8);
sqlite3_free(zBuf);
}
|
| ︙ | ︙ | |||
131983 131984 131985 131986 131987 131988 131989 |
#ifdef SQLITE_SOUNDEX
FUNCTION(soundex, 1, 0, 0, soundexFunc ),
#endif
#ifndef SQLITE_OMIT_LOAD_EXTENSION
SFUNCTION(load_extension, 1, 0, 0, loadExt ),
SFUNCTION(load_extension, 2, 0, 0, loadExt ),
#endif
| < < < | 132057 132058 132059 132060 132061 132062 132063 132064 132065 132066 132067 132068 132069 132070 |
#ifdef SQLITE_SOUNDEX
FUNCTION(soundex, 1, 0, 0, soundexFunc ),
#endif
#ifndef SQLITE_OMIT_LOAD_EXTENSION
SFUNCTION(load_extension, 1, 0, 0, loadExt ),
SFUNCTION(load_extension, 2, 0, 0, loadExt ),
#endif
#ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS
DFUNCTION(sqlite_compileoption_used,1, 0, 0, compileoptionusedFunc ),
DFUNCTION(sqlite_compileoption_get, 1, 0, 0, compileoptiongetFunc ),
#endif /* SQLITE_OMIT_COMPILEOPTION_DIAGS */
INLINE_FUNC(unlikely, 1, INLINEFUNC_unlikely, SQLITE_FUNC_UNLIKELY),
INLINE_FUNC(likelihood, 2, INLINEFUNC_unlikely, SQLITE_FUNC_UNLIKELY),
INLINE_FUNC(likely, 1, INLINEFUNC_unlikely, SQLITE_FUNC_UNLIKELY),
|
| ︙ | ︙ | |||
132122 132123 132124 132125 132126 132127 132128 132129 132130 132131 132132 132133 132134 132135 132136 |
MFUNCTION(sqrt, 1, sqrt, math1Func ),
MFUNCTION(radians, 1, degToRad, math1Func ),
MFUNCTION(degrees, 1, radToDeg, math1Func ),
MFUNCTION(pi, 0, 0, piFunc ),
#endif /* SQLITE_ENABLE_MATH_FUNCTIONS */
FUNCTION(sign, 1, 0, 0, signFunc ),
INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, 0 ),
INLINE_FUNC(iif, 3, INLINEFUNC_iif, 0 ),
};
#ifndef SQLITE_OMIT_ALTERTABLE
sqlite3AlterFunctions();
#endif
sqlite3WindowFunctions();
sqlite3RegisterDateTimeFunctions();
sqlite3RegisterJsonFunctions();
| > > > | 132193 132194 132195 132196 132197 132198 132199 132200 132201 132202 132203 132204 132205 132206 132207 132208 132209 132210 |
MFUNCTION(sqrt, 1, sqrt, math1Func ),
MFUNCTION(radians, 1, degToRad, math1Func ),
MFUNCTION(degrees, 1, radToDeg, math1Func ),
MFUNCTION(pi, 0, 0, piFunc ),
#endif /* SQLITE_ENABLE_MATH_FUNCTIONS */
FUNCTION(sign, 1, 0, 0, signFunc ),
INLINE_FUNC(coalesce, -1, INLINEFUNC_coalesce, 0 ),
INLINE_FUNC(iif, 2, INLINEFUNC_iif, 0 ),
INLINE_FUNC(iif, 3, INLINEFUNC_iif, 0 ),
INLINE_FUNC(if, 2, INLINEFUNC_iif, 0 ),
INLINE_FUNC(if, 3, INLINEFUNC_iif, 0 ),
};
#ifndef SQLITE_OMIT_ALTERTABLE
sqlite3AlterFunctions();
#endif
sqlite3WindowFunctions();
sqlite3RegisterDateTimeFunctions();
sqlite3RegisterJsonFunctions();
|
| ︙ | ︙ | |||
140635 140636 140637 140638 140639 140640 140641 |
}else{
u64 mask = pPragma->iArg; /* Mask of bits to set or clear. */
if( db->autoCommit==0 ){
/* Foreign key support may not be enabled or disabled while not
** in auto-commit mode. */
mask &= ~(SQLITE_ForeignKeys);
}
| < < < < < < | 140709 140710 140711 140712 140713 140714 140715 140716 140717 140718 140719 140720 140721 140722 |
}else{
u64 mask = pPragma->iArg; /* Mask of bits to set or clear. */
if( db->autoCommit==0 ){
/* Foreign key support may not be enabled or disabled while not
** in auto-commit mode. */
mask &= ~(SQLITE_ForeignKeys);
}
if( sqlite3GetBoolean(zRight, 0) ){
if( (mask & SQLITE_WriteSchema)==0
|| (db->flags & SQLITE_Defensive)==0
){
db->flags |= mask;
}
|
| ︙ | ︙ | |||
140776 140777 140778 140779 140780 140781 140782 |
Table *pTab;
if( k==0 ){ initNCol = 0; break; }
pTab = sqliteHashData(k);
if( pTab->nCol==0 ){
char *zSql = sqlite3MPrintf(db, "SELECT*FROM\"%w\"", pTab->zName);
if( zSql ){
sqlite3_stmt *pDummy = 0;
| > | | 140844 140845 140846 140847 140848 140849 140850 140851 140852 140853 140854 140855 140856 140857 140858 140859 |
Table *pTab;
if( k==0 ){ initNCol = 0; break; }
pTab = sqliteHashData(k);
if( pTab->nCol==0 ){
char *zSql = sqlite3MPrintf(db, "SELECT*FROM\"%w\"", pTab->zName);
if( zSql ){
sqlite3_stmt *pDummy = 0;
(void)sqlite3_prepare_v3(db, zSql, -1, SQLITE_PREPARE_DONT_LOG,
&pDummy, 0);
(void)sqlite3_finalize(pDummy);
sqlite3DbFree(db, zSql);
}
if( db->mallocFailed ){
sqlite3ErrorMsg(db->pParse, "out of memory");
db->pParse->rc = SQLITE_NOMEM_BKPT;
}
|
| ︙ | ︙ | |||
141257 141258 141259 141260 141261 141262 141263 |
/* Make sure sufficient number of registers have been allocated */
sqlite3TouchRegister(pParse, 8+cnt);
sqlite3VdbeAddOp3(v, OP_Null, 0, 8, 8+cnt);
sqlite3ClearTempRegCache(pParse);
/* Do the b-tree integrity checks */
sqlite3VdbeAddOp4(v, OP_IntegrityCk, 1, cnt, 8, (char*)aRoot,P4_INTARRAY);
| | | 141326 141327 141328 141329 141330 141331 141332 141333 141334 141335 141336 141337 141338 141339 141340 |
/* Make sure sufficient number of registers have been allocated */
sqlite3TouchRegister(pParse, 8+cnt);
sqlite3VdbeAddOp3(v, OP_Null, 0, 8, 8+cnt);
sqlite3ClearTempRegCache(pParse);
/* Do the b-tree integrity checks */
sqlite3VdbeAddOp4(v, OP_IntegrityCk, 1, cnt, 8, (char*)aRoot,P4_INTARRAY);
sqlite3VdbeChangeP5(v, (u16)i);
addr = sqlite3VdbeAddOp1(v, OP_IsNull, 2); VdbeCoverage(v);
sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0,
sqlite3MPrintf(db, "*** in database %s ***\n", db->aDb[i].zDbSName),
P4_DYNAMIC);
sqlite3VdbeAddOp3(v, OP_Concat, 2, 3, 3);
integrityCheckResultRow(v);
sqlite3VdbeJumpHere(v, addr);
|
| ︙ | ︙ | |||
142877 142878 142879 142880 142881 142882 142883 |
#ifndef SQLITE_OMIT_UTF16
/* If opening the main database, set ENC(db). */
encoding = (u8)meta[BTREE_TEXT_ENCODING-1] & 3;
if( encoding==0 ) encoding = SQLITE_UTF8;
#else
encoding = SQLITE_UTF8;
#endif
| < < < < < < | < | 142946 142947 142948 142949 142950 142951 142952 142953 142954 142955 142956 142957 142958 142959 142960 |
#ifndef SQLITE_OMIT_UTF16
/* If opening the main database, set ENC(db). */
encoding = (u8)meta[BTREE_TEXT_ENCODING-1] & 3;
if( encoding==0 ) encoding = SQLITE_UTF8;
#else
encoding = SQLITE_UTF8;
#endif
sqlite3SetTextEncoding(db, encoding);
}else{
/* If opening an attached database, the encoding much match ENC(db) */
if( (meta[BTREE_TEXT_ENCODING-1] & 3)!=ENC(db) ){
sqlite3SetString(pzErrMsg, db, "attached databases must use the same"
" text encoding as main database");
rc = SQLITE_ERROR;
goto initone_error_out;
|
| ︙ | ︙ | |||
147582 147583 147584 147585 147586 147587 147588 147589 147590 147591 147592 147593 147594 |
if( db->mallocFailed ){
sqlite3ExprDelete(db, pNew);
return pExpr;
}
if( pSubst->isOuterJoin ){
ExprSetProperty(pNew, EP_CanBeNull);
}
if( ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) ){
sqlite3SetJoinExpr(pNew, pExpr->w.iJoin,
pExpr->flags & (EP_OuterON|EP_InnerON));
}
sqlite3ExprDelete(db, pExpr);
pExpr = pNew;
| > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < < < < < < < < < < | 147644 147645 147646 147647 147648 147649 147650 147651 147652 147653 147654 147655 147656 147657 147658 147659 147660 147661 147662 147663 147664 147665 147666 147667 147668 147669 147670 147671 147672 147673 147674 147675 147676 147677 147678 147679 147680 147681 147682 147683 |
if( db->mallocFailed ){
sqlite3ExprDelete(db, pNew);
return pExpr;
}
if( pSubst->isOuterJoin ){
ExprSetProperty(pNew, EP_CanBeNull);
}
if( pNew->op==TK_TRUEFALSE ){
pNew->u.iValue = sqlite3ExprTruthValue(pNew);
pNew->op = TK_INTEGER;
ExprSetProperty(pNew, EP_IntValue);
}
/* Ensure that the expression now has an implicit collation sequence,
** just as it did when it was a column of a view or sub-query. */
{
CollSeq *pNat = sqlite3ExprCollSeq(pSubst->pParse, pNew);
CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse,
pSubst->pCList->a[iColumn].pExpr
);
if( pNat!=pColl || (pNew->op!=TK_COLUMN && pNew->op!=TK_COLLATE) ){
pNew = sqlite3ExprAddCollateString(pSubst->pParse, pNew,
(pColl ? pColl->zName : "BINARY")
);
}
}
ExprClearProperty(pNew, EP_Collate);
if( ExprHasProperty(pExpr,EP_OuterON|EP_InnerON) ){
sqlite3SetJoinExpr(pNew, pExpr->w.iJoin,
pExpr->flags & (EP_OuterON|EP_InnerON));
}
sqlite3ExprDelete(db, pExpr);
pExpr = pNew;
}
}
}else{
if( pExpr->op==TK_IF_NULL_ROW && pExpr->iTable==pSubst->iTable ){
pExpr->iTable = pSubst->iNewTable;
}
pExpr->pLeft = substExpr(pSubst, pExpr->pLeft);
|
| ︙ | ︙ | |||
148344 148345 148346 148347 148348 148349 148350 148351 148352 148353 148354 148355 148356 148357 148358 148359 |
if( pSrc==0 ) break;
pParent->pSrc = pSrc;
}
/* Transfer the FROM clause terms from the subquery into the
** outer query.
*/
for(i=0; i<nSubSrc; i++){
SrcItem *pItem = &pSrc->a[i+iFrom];
assert( pItem->fg.isTabFunc==0 );
assert( pItem->fg.isSubquery
|| pItem->fg.fixedSchema
|| pItem->u4.zDatabase==0 );
if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing);
*pItem = pSubSrc->a[i];
pItem->fg.jointype |= ltorj;
| > < | 148406 148407 148408 148409 148410 148411 148412 148413 148414 148415 148416 148417 148418 148419 148420 148421 148422 148423 148424 148425 148426 148427 148428 148429 |
if( pSrc==0 ) break;
pParent->pSrc = pSrc;
}
/* Transfer the FROM clause terms from the subquery into the
** outer query.
*/
iNewParent = pSubSrc->a[0].iCursor;
for(i=0; i<nSubSrc; i++){
SrcItem *pItem = &pSrc->a[i+iFrom];
assert( pItem->fg.isTabFunc==0 );
assert( pItem->fg.isSubquery
|| pItem->fg.fixedSchema
|| pItem->u4.zDatabase==0 );
if( pItem->fg.isUsing ) sqlite3IdListDelete(db, pItem->u3.pUsing);
*pItem = pSubSrc->a[i];
pItem->fg.jointype |= ltorj;
memset(&pSubSrc->a[i], 0, sizeof(pSubSrc->a[i]));
}
pSrc->a[iFrom].fg.jointype &= JT_LTORJ;
pSrc->a[iFrom].fg.jointype |= jointype | ltorj;
/* Now begin substituting subquery result set expressions for
** references to the iParent in the outer query.
|
| ︙ | ︙ | |||
148393 148394 148395 148396 148397 148398 148399 148400 148401 148402 148403 148404 148405 148406 |
assert( pParent->pOrderBy==0 );
pParent->pOrderBy = pOrderBy;
pSub->pOrderBy = 0;
}
pWhere = pSub->pWhere;
pSub->pWhere = 0;
if( isOuterJoin>0 ){
sqlite3SetJoinExpr(pWhere, iNewParent, EP_OuterON);
}
if( pWhere ){
if( pParent->pWhere ){
pParent->pWhere = sqlite3PExpr(pParse, TK_AND, pWhere, pParent->pWhere);
}else{
pParent->pWhere = pWhere;
| > | 148455 148456 148457 148458 148459 148460 148461 148462 148463 148464 148465 148466 148467 148468 148469 |
assert( pParent->pOrderBy==0 );
pParent->pOrderBy = pOrderBy;
pSub->pOrderBy = 0;
}
pWhere = pSub->pWhere;
pSub->pWhere = 0;
if( isOuterJoin>0 ){
assert( pSubSrc->nSrc==1 );
sqlite3SetJoinExpr(pWhere, iNewParent, EP_OuterON);
}
if( pWhere ){
if( pParent->pWhere ){
pParent->pWhere = sqlite3PExpr(pParse, TK_AND, pWhere, pParent->pWhere);
}else{
pParent->pWhere = pWhere;
|
| ︙ | ︙ | |||
150496 150497 150498 150499 150500 150501 150502 |
sqlite3VdbeAddOp3(v, OP_Column, pF->iOBTab, iBaseCol+j, regSubtype);
sqlite3VdbeAddOp2(v, OP_SetSubtype, regSubtype, regAgg+j);
}
sqlite3ReleaseTempReg(pParse, regSubtype);
}
sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
| | | 150559 150560 150561 150562 150563 150564 150565 150566 150567 150568 150569 150570 150571 150572 150573 |
sqlite3VdbeAddOp3(v, OP_Column, pF->iOBTab, iBaseCol+j, regSubtype);
sqlite3VdbeAddOp2(v, OP_SetSubtype, regSubtype, regAgg+j);
}
sqlite3ReleaseTempReg(pParse, regSubtype);
}
sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, (u16)nArg);
sqlite3VdbeAddOp2(v, OP_Next, pF->iOBTab, iTop+1); VdbeCoverage(v);
sqlite3VdbeJumpHere(v, iTop);
sqlite3ReleaseTempRange(pParse, regAgg, nArg);
}
sqlite3VdbeAddOp2(v, OP_AggFinal, AggInfoFuncReg(pAggInfo,i),
pList ? pList->nExpr : 0);
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
|
| ︙ | ︙ | |||
150659 150660 150661 150662 150663 150664 150665 |
}
if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem;
sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0,
(char *)pColl, P4_COLLSEQ);
}
sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
| | | 150722 150723 150724 150725 150726 150727 150728 150729 150730 150731 150732 150733 150734 150735 150736 |
}
if( regHit==0 && pAggInfo->nAccumulator ) regHit = ++pParse->nMem;
sqlite3VdbeAddOp4(v, OP_CollSeq, regHit, 0, 0,
(char *)pColl, P4_COLLSEQ);
}
sqlite3VdbeAddOp3(v, OP_AggStep, 0, regAgg, AggInfoFuncReg(pAggInfo,i));
sqlite3VdbeAppendP4(v, pF->pFunc, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, (u16)nArg);
sqlite3ReleaseTempRange(pParse, regAgg, nArg);
}
if( addrNext ){
sqlite3VdbeResolveLabel(v, addrNext);
}
if( pParse->nErr ) return;
}
|
| ︙ | ︙ | |||
151492 151493 151494 151495 151496 151497 151498 |
TREETRACE(0x4000,pParse,p,
("After WHERE-clause push-down into subquery %d:\n", pSub->selId));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
assert( pSubq->pSelect && (pSub->selFlags & SF_PushDown)!=0 );
}else{
| | | 151555 151556 151557 151558 151559 151560 151561 151562 151563 151564 151565 151566 151567 151568 151569 |
TREETRACE(0x4000,pParse,p,
("After WHERE-clause push-down into subquery %d:\n", pSub->selId));
sqlite3TreeViewSelect(0, p, 0);
}
#endif
assert( pSubq->pSelect && (pSub->selFlags & SF_PushDown)!=0 );
}else{
TREETRACE(0x4000,pParse,p,("WHERE-clause push-down not possible\n"));
}
/* Convert unused result columns of the subquery into simple NULL
** expressions, to avoid unneeded searching and computation.
** tag-select-0440
*/
if( OptimizationEnabled(db, SQLITE_NullUnusedCols)
|
| ︙ | ︙ | |||
154053 154054 154055 154056 154057 154058 154059 |
(v, "Call: %s.%s", (p->zName?p->zName:"fkey"), onErrorText(orconf)));
/* Set the P5 operand of the OP_Program instruction to non-zero if
** recursive invocation of this trigger program is disallowed. Recursive
** invocation is disallowed if (a) the sub-program is really a trigger,
** not a foreign key action, and (b) the flag to enable recursive triggers
** is clear. */
| | | 154116 154117 154118 154119 154120 154121 154122 154123 154124 154125 154126 154127 154128 154129 154130 |
(v, "Call: %s.%s", (p->zName?p->zName:"fkey"), onErrorText(orconf)));
/* Set the P5 operand of the OP_Program instruction to non-zero if
** recursive invocation of this trigger program is disallowed. Recursive
** invocation is disallowed if (a) the sub-program is really a trigger,
** not a foreign key action, and (b) the flag to enable recursive triggers
** is clear. */
sqlite3VdbeChangeP5(v, (u16)bRecursive);
}
}
/*
** This is called to code the required FOR EACH ROW triggers for an operation
** on table pTab. The operation to code triggers for (INSERT, UPDATE or DELETE)
** is given by the op parameter. The tr_tm parameter determines whether the
|
| ︙ | ︙ | |||
158266 158267 158268 158269 158270 158271 158272 158273 158274 158275 158276 158277 158278 158279 158280 158281 158282 | u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ ); SQLITE_PRIVATE int sqlite3WhereExplainBloomFilter( const Parse *pParse, /* Parse context */ const WhereInfo *pWInfo, /* WHERE clause */ const WhereLevel *pLevel /* Bloom filter on this level */ ); #else # define sqlite3WhereExplainOneScan(u,v,w,x) 0 # define sqlite3WhereExplainBloomFilter(u,v,w) 0 #endif /* SQLITE_OMIT_EXPLAIN */ #ifdef SQLITE_ENABLE_STMT_SCANSTATUS SQLITE_PRIVATE void sqlite3WhereAddScanStatus( Vdbe *v, /* Vdbe to add scanstatus entry to */ SrcList *pSrclist, /* FROM clause pLvl reads data from */ WhereLevel *pLvl, /* Level to add scanstatus() entry for */ int addrExplain /* Address of OP_Explain (or 0) */ | > > > > > > > > | 158329 158330 158331 158332 158333 158334 158335 158336 158337 158338 158339 158340 158341 158342 158343 158344 158345 158346 158347 158348 158349 158350 158351 158352 158353 | u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ ); SQLITE_PRIVATE int sqlite3WhereExplainBloomFilter( const Parse *pParse, /* Parse context */ const WhereInfo *pWInfo, /* WHERE clause */ const WhereLevel *pLevel /* Bloom filter on this level */ ); SQLITE_PRIVATE void sqlite3WhereAddExplainText( Parse *pParse, /* Parse context */ int addr, SrcList *pTabList, /* Table list this loop refers to */ WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */ u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */ ); #else # define sqlite3WhereExplainOneScan(u,v,w,x) 0 # define sqlite3WhereExplainBloomFilter(u,v,w) 0 # define sqlite3WhereAddExplainText(u,v,w,x,y) #endif /* SQLITE_OMIT_EXPLAIN */ #ifdef SQLITE_ENABLE_STMT_SCANSTATUS SQLITE_PRIVATE void sqlite3WhereAddScanStatus( Vdbe *v, /* Vdbe to add scanstatus entry to */ SrcList *pSrclist, /* FROM clause pLvl reads data from */ WhereLevel *pLvl, /* Level to add scanstatus() entry for */ int addrExplain /* Address of OP_Explain (or 0) */ |
| ︙ | ︙ | |||
158470 158471 158472 158473 158474 158475 158476 |
if( pLoop->wsFlags&WHERE_TOP_LIMIT ){
explainAppendTerm(pStr, pIndex, pLoop->u.btree.nTop, j, i, "<");
}
sqlite3_str_append(pStr, ")", 1);
}
/*
| | < < < < | < > | > < > > < > > > > < | 158541 158542 158543 158544 158545 158546 158547 158548 158549 158550 158551 158552 158553 158554 158555 158556 158557 158558 158559 158560 158561 158562 158563 158564 158565 158566 158567 158568 158569 158570 158571 158572 158573 158574 158575 158576 158577 158578 158579 158580 158581 158582 158583 158584 158585 158586 |
if( pLoop->wsFlags&WHERE_TOP_LIMIT ){
explainAppendTerm(pStr, pIndex, pLoop->u.btree.nTop, j, i, "<");
}
sqlite3_str_append(pStr, ")", 1);
}
/*
** This function sets the P4 value of an existing OP_Explain opcode to
** text describing the loop in pLevel. If the OP_Explain opcode already has
** a P4 value, it is freed before it is overwritten.
*/
SQLITE_PRIVATE void sqlite3WhereAddExplainText(
Parse *pParse, /* Parse context */
int addr, /* Address of OP_Explain opcode */
SrcList *pTabList, /* Table list this loop refers to */
WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
){
#if !defined(SQLITE_DEBUG)
if( sqlite3ParseToplevel(pParse)->explain==2 || IS_STMT_SCANSTATUS(pParse->db) )
#endif
{
VdbeOp *pOp = sqlite3VdbeGetOp(pParse->pVdbe, addr);
SrcItem *pItem = &pTabList->a[pLevel->iFrom];
sqlite3 *db = pParse->db; /* Database handle */
int isSearch; /* True for a SEARCH. False for SCAN. */
WhereLoop *pLoop; /* The controlling WhereLoop object */
u32 flags; /* Flags that describe this loop */
#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_EXPLAIN)
char *zMsg; /* Text to add to EQP output */
#endif
StrAccum str; /* EQP output string */
char zBuf[100]; /* Initial space for EQP output string */
if( db->mallocFailed ) return;
pLoop = pLevel->pWLoop;
flags = pLoop->wsFlags;
isSearch = (flags&(WHERE_BTM_LIMIT|WHERE_TOP_LIMIT))!=0
|| ((flags&WHERE_VIRTUALTABLE)==0 && (pLoop->u.btree.nEq>0))
|| (wctrlFlags&(WHERE_ORDERBY_MIN|WHERE_ORDERBY_MAX));
sqlite3StrAccumInit(&str, db, zBuf, sizeof(zBuf), SQLITE_MAX_LENGTH);
str.printfFlags = SQLITE_PRINTF_INTERNAL;
|
| ︙ | ︙ | |||
158525 158526 158527 158528 158529 158530 158531 |
if( isSearch ){
zFmt = "PRIMARY KEY";
}
}else if( flags & WHERE_PARTIALIDX ){
zFmt = "AUTOMATIC PARTIAL COVERING INDEX";
}else if( flags & WHERE_AUTO_INDEX ){
zFmt = "AUTOMATIC COVERING INDEX";
| | | 158596 158597 158598 158599 158600 158601 158602 158603 158604 158605 158606 158607 158608 158609 158610 |
if( isSearch ){
zFmt = "PRIMARY KEY";
}
}else if( flags & WHERE_PARTIALIDX ){
zFmt = "AUTOMATIC PARTIAL COVERING INDEX";
}else if( flags & WHERE_AUTO_INDEX ){
zFmt = "AUTOMATIC COVERING INDEX";
}else if( flags & (WHERE_IDX_ONLY|WHERE_EXPRIDX) ){
zFmt = "COVERING INDEX %s";
}else{
zFmt = "INDEX %s";
}
if( zFmt ){
sqlite3_str_append(&str, " USING ", 7);
sqlite3_str_appendf(&str, zFmt, pIdx->zName);
|
| ︙ | ︙ | |||
158577 158578 158579 158580 158581 158582 158583 158584 158585 |
if( pLoop->nOut>=10 ){
sqlite3_str_appendf(&str, " (~%llu rows)",
sqlite3LogEstToInt(pLoop->nOut));
}else{
sqlite3_str_append(&str, " (~1 row)", 9);
}
#endif
zMsg = sqlite3StrAccumFinish(&str);
sqlite3ExplainBreakpoint("",zMsg);
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | > > | 158648 158649 158650 158651 158652 158653 158654 158655 158656 158657 158658 158659 158660 158661 158662 158663 158664 158665 158666 158667 158668 158669 158670 158671 158672 158673 158674 158675 158676 158677 158678 158679 158680 158681 158682 158683 158684 158685 158686 158687 158688 158689 158690 158691 158692 158693 158694 158695 158696 158697 158698 158699 158700 158701 158702 158703 158704 158705 |
if( pLoop->nOut>=10 ){
sqlite3_str_appendf(&str, " (~%llu rows)",
sqlite3LogEstToInt(pLoop->nOut));
}else{
sqlite3_str_append(&str, " (~1 row)", 9);
}
#endif
#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_EXPLAIN)
zMsg = sqlite3StrAccumFinish(&str);
sqlite3ExplainBreakpoint("",zMsg);
#endif
assert( pOp->opcode==OP_Explain );
assert( pOp->p4type==P4_DYNAMIC || pOp->p4.z==0 );
sqlite3DbFree(db, pOp->p4.z);
pOp->p4type = P4_DYNAMIC;
pOp->p4.z = sqlite3StrAccumFinish(&str);
}
}
/*
** This function is a no-op unless currently processing an EXPLAIN QUERY PLAN
** command, or if stmt_scanstatus_v2() stats are enabled, or if SQLITE_DEBUG
** was defined at compile-time. If it is not a no-op, a single OP_Explain
** opcode is added to the output to describe the table scan strategy in pLevel.
**
** If an OP_Explain opcode is added to the VM, its address is returned.
** Otherwise, if no OP_Explain is coded, zero is returned.
*/
SQLITE_PRIVATE int sqlite3WhereExplainOneScan(
Parse *pParse, /* Parse context */
SrcList *pTabList, /* Table list this loop refers to */
WhereLevel *pLevel, /* Scan to write OP_Explain opcode for */
u16 wctrlFlags /* Flags passed to sqlite3WhereBegin() */
){
int ret = 0;
#if !defined(SQLITE_DEBUG)
if( sqlite3ParseToplevel(pParse)->explain==2 || IS_STMT_SCANSTATUS(pParse->db) )
#endif
{
if( (pLevel->pWLoop->wsFlags & WHERE_MULTI_OR)==0
&& (wctrlFlags & WHERE_OR_SUBCLAUSE)==0
){
Vdbe *v = pParse->pVdbe;
int addr = sqlite3VdbeCurrentAddr(v);
ret = sqlite3VdbeAddOp3(
v, OP_Explain, addr, pParse->addrExplain, pLevel->pWLoop->rRun
);
sqlite3WhereAddExplainText(pParse, addr, pTabList, pLevel, wctrlFlags);
}
}
return ret;
}
/*
** Add a single OP_Explain opcode that describes a Bloom filter.
**
|
| ︙ | ︙ | |||
158680 158681 158682 158683 158684 158685 158686 158687 158688 |
sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iTabCur);
}
if( wsFlags & WHERE_INDEXED ){
sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iIdxCur);
}
}else{
int addr;
assert( pSrclist->a[pLvl->iFrom].fg.isSubquery );
addr = pSrclist->a[pLvl->iFrom].u4.pSubq->addrFillSub;
| > | | 158790 158791 158792 158793 158794 158795 158796 158797 158798 158799 158800 158801 158802 158803 158804 158805 158806 158807 |
sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iTabCur);
}
if( wsFlags & WHERE_INDEXED ){
sqlite3VdbeScanStatusRange(v, addrExplain, -1, pLvl->iIdxCur);
}
}else{
int addr;
VdbeOp *pOp;
assert( pSrclist->a[pLvl->iFrom].fg.isSubquery );
addr = pSrclist->a[pLvl->iFrom].u4.pSubq->addrFillSub;
pOp = sqlite3VdbeGetOp(v, addr-1);
assert( sqlite3VdbeDb(v)->mallocFailed || pOp->opcode==OP_InitCoroutine );
assert( sqlite3VdbeDb(v)->mallocFailed || pOp->p2>addr );
sqlite3VdbeScanStatusRange(v, addrExplain, addr, pOp->p2-1);
}
}
}
#endif
|
| ︙ | ︙ | |||
158935 158936 158937 158938 158939 158940 158941 158942 158943 158944 158945 158946 158947 158948 |
}
sqlite3ExprListDelete(db, pOrigRhs);
if( pOrigLhs ){
sqlite3ExprListDelete(db, pOrigLhs);
pNew->pLeft->x.pList = pLhs;
}
pSelect->pEList = pRhs;
if( pLhs && pLhs->nExpr==1 ){
/* Take care here not to generate a TK_VECTOR containing only a
** single value. Since the parser never creates such a vector, some
** of the subroutines do not handle this case. */
Expr *p = pLhs->a[0].pExpr;
pLhs->a[0].pExpr = 0;
sqlite3ExprDelete(db, pNew->pLeft);
| > | 159046 159047 159048 159049 159050 159051 159052 159053 159054 159055 159056 159057 159058 159059 159060 |
}
sqlite3ExprListDelete(db, pOrigRhs);
if( pOrigLhs ){
sqlite3ExprListDelete(db, pOrigLhs);
pNew->pLeft->x.pList = pLhs;
}
pSelect->pEList = pRhs;
pSelect->selId = ++pParse->nSelect; /* Req'd for SubrtnSig validity */
if( pLhs && pLhs->nExpr==1 ){
/* Take care here not to generate a TK_VECTOR containing only a
** single value. Since the parser never creates such a vector, some
** of the subroutines do not handle this case. */
Expr *p = pLhs->a[0].pExpr;
pLhs->a[0].pExpr = 0;
sqlite3ExprDelete(db, pNew->pLeft);
|
| ︙ | ︙ | |||
164007 164008 164009 164010 164011 164012 164013 |
testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) );
if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON)
|| pTerm->pExpr->w.iJoin != pSrc->iCursor
){
return 0;
}
if( (pSrc->fg.jointype & (JT_LEFT|JT_RIGHT))!=0
| | | 164119 164120 164121 164122 164123 164124 164125 164126 164127 164128 164129 164130 164131 164132 164133 |
testcase( ExprHasProperty(pTerm->pExpr, EP_InnerON) );
if( !ExprHasProperty(pTerm->pExpr, EP_OuterON|EP_InnerON)
|| pTerm->pExpr->w.iJoin != pSrc->iCursor
){
return 0;
}
if( (pSrc->fg.jointype & (JT_LEFT|JT_RIGHT))!=0
&& NEVER(ExprHasProperty(pTerm->pExpr, EP_InnerON))
){
return 0;
}
return 1;
}
#ifndef SQLITE_OMIT_AUTOMATIC_INDEX
|
| ︙ | ︙ | |||
165500 165501 165502 165503 165504 165505 165506 | } assert( pBuilder->nRecValid==nRecValid ); return rc; } #endif /* SQLITE_ENABLE_STAT4 */ | | | 165612 165613 165614 165615 165616 165617 165618 165619 165620 165621 165622 165623 165624 165625 165626 |
}
assert( pBuilder->nRecValid==nRecValid );
return rc;
}
#endif /* SQLITE_ENABLE_STAT4 */
#if defined(WHERETRACE_ENABLED) || defined(SQLITE_DEBUG)
/*
** Print the content of a WhereTerm object
*/
SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm){
if( pTerm==0 ){
sqlite3DebugPrintf("TERM-%-3d NULL\n", iTerm);
}else{
|
| ︙ | ︙ | |||
165543 165544 165545 165546 165547 165548 165549 165550 165551 165552 165553 165554 165555 165556 |
}
if( pTerm->iParent>=0 ){
sqlite3DebugPrintf(" iParent=%d", pTerm->iParent);
}
sqlite3DebugPrintf("\n");
sqlite3TreeViewExpr(0, pTerm->pExpr, 0);
}
}
#endif
#ifdef WHERETRACE_ENABLED
/*
** Show the complete content of a WhereClause
*/
| > > > | 165655 165656 165657 165658 165659 165660 165661 165662 165663 165664 165665 165666 165667 165668 165669 165670 165671 |
}
if( pTerm->iParent>=0 ){
sqlite3DebugPrintf(" iParent=%d", pTerm->iParent);
}
sqlite3DebugPrintf("\n");
sqlite3TreeViewExpr(0, pTerm->pExpr, 0);
}
}
SQLITE_PRIVATE void sqlite3ShowWhereTerm(WhereTerm *pTerm){
sqlite3WhereTermPrint(pTerm, 0);
}
#endif
#ifdef WHERETRACE_ENABLED
/*
** Show the complete content of a WhereClause
*/
|
| ︙ | ︙ | |||
166729 166730 166731 166732 166733 166734 166735 |
if( jointype & JT_LTORJ ) return 0;
pParse = pWC->pWInfo->pParse;
while( pWhere->op==TK_AND ){
if( !whereUsablePartialIndex(iTab,jointype,pWC,pWhere->pLeft) ) return 0;
pWhere = pWhere->pRight;
}
| < | 166844 166845 166846 166847 166848 166849 166850 166851 166852 166853 166854 166855 166856 166857 |
if( jointype & JT_LTORJ ) return 0;
pParse = pWC->pWInfo->pParse;
while( pWhere->op==TK_AND ){
if( !whereUsablePartialIndex(iTab,jointype,pWC,pWhere->pLeft) ) return 0;
pWhere = pWhere->pRight;
}
for(i=0, pTerm=pWC->a; i<pWC->nTerm; i++, pTerm++){
Expr *pExpr;
pExpr = pTerm->pExpr;
if( (!ExprHasProperty(pExpr, EP_OuterON) || pExpr->w.iJoin==iTab)
&& ((jointype & JT_OUTER)==0 || ExprHasProperty(pExpr, EP_OuterON))
&& sqlite3ExprImpliesExpr(pParse, pExpr, pWhere, iTab)
&& (pTerm->wtFlags & TERM_VNULL)==0
|
| ︙ | ︙ | |||
169390 169391 169392 169393 169394 169395 169396 |
|| pTerm->pExpr->w.iJoin!=pItem->iCursor
){
break;
}
}
if( hasRightJoin
&& ExprHasProperty(pTerm->pExpr, EP_InnerON)
| | | 169504 169505 169506 169507 169508 169509 169510 169511 169512 169513 169514 169515 169516 169517 169518 |
|| pTerm->pExpr->w.iJoin!=pItem->iCursor
){
break;
}
}
if( hasRightJoin
&& ExprHasProperty(pTerm->pExpr, EP_InnerON)
&& NEVER(pTerm->pExpr->w.iJoin==pItem->iCursor)
){
break; /* restriction (5) */
}
}
if( pTerm<pEnd ) continue;
WHERETRACE(0xffffffff,("-> omit unused FROM-clause term %c\n",pLoop->cId));
m1 = MASKBIT(i)-1;
|
| ︙ | ︙ | |||
170310 170311 170312 170313 170314 170315 170316 170317 170318 170319 170320 170321 170322 170323 |
static void sqlite3WhereOpcodeRewriteTrace(
sqlite3 *db,
int pc,
VdbeOp *pOp
){
if( (db->flags & SQLITE_VdbeAddopTrace)==0 ) return;
sqlite3VdbePrintOp(0, pc, pOp);
}
#endif
/*
** Generate the end of the WHERE loop. See comments on
** sqlite3WhereBegin() for additional information.
*/
| > | 170424 170425 170426 170427 170428 170429 170430 170431 170432 170433 170434 170435 170436 170437 170438 |
static void sqlite3WhereOpcodeRewriteTrace(
sqlite3 *db,
int pc,
VdbeOp *pOp
){
if( (db->flags & SQLITE_VdbeAddopTrace)==0 ) return;
sqlite3VdbePrintOp(0, pc, pOp);
sqlite3ShowWhereTerm(0); /* So compiler won't complain about unused func */
}
#endif
/*
** Generate the end of the WHERE loop. See comments on
** sqlite3WhereBegin() for additional information.
*/
|
| ︙ | ︙ | |||
170609 170610 170611 170612 170613 170614 170615 |
x = sqlite3StorageColumnToTable(pTab,x);
}
x = sqlite3TableColumnToIndex(pIdx, x);
if( x>=0 ){
pOp->p2 = x;
pOp->p1 = pLevel->iIdxCur;
OpcodeRewriteTrace(db, k, pOp);
| | < < < < > > > > > > > > > > > > > > > > > > | 170724 170725 170726 170727 170728 170729 170730 170731 170732 170733 170734 170735 170736 170737 170738 170739 170740 170741 170742 170743 170744 170745 170746 170747 170748 170749 170750 170751 170752 170753 170754 170755 170756 170757 170758 170759 |
x = sqlite3StorageColumnToTable(pTab,x);
}
x = sqlite3TableColumnToIndex(pIdx, x);
if( x>=0 ){
pOp->p2 = x;
pOp->p1 = pLevel->iIdxCur;
OpcodeRewriteTrace(db, k, pOp);
}else if( pLoop->wsFlags & (WHERE_IDX_ONLY|WHERE_EXPRIDX) ){
if( pLoop->wsFlags & WHERE_IDX_ONLY ){
/* An error. pLoop is supposed to be a covering index loop,
** and yet the VM code refers to a column of the table that
** is not part of the index. */
sqlite3ErrorMsg(pParse, "internal query planner error");
pParse->rc = SQLITE_INTERNAL;
}else{
/* The WHERE_EXPRIDX flag is set by the planner when it is likely
** that pLoop is a covering index loop, but it is not possible
** to be 100% sure. In this case, any OP_Explain opcode
** corresponding to this loop describes the index as a "COVERING
** INDEX". But, pOp proves that pLoop is not actually a covering
** index loop. So clear the WHERE_EXPRIDX flag and rewrite the
** text that accompanies the OP_Explain opcode, if any. */
pLoop->wsFlags &= ~WHERE_EXPRIDX;
sqlite3WhereAddExplainText(pParse,
pLevel->addrBody-1,
pTabList,
pLevel,
pWInfo->wctrlFlags
);
}
}
}else if( pOp->opcode==OP_Rowid ){
pOp->p1 = pLevel->iIdxCur;
pOp->opcode = OP_IdxRowid;
OpcodeRewriteTrace(db, k, pOp);
}else if( pOp->opcode==OP_IfNullRow ){
|
| ︙ | ︙ | |||
172324 172325 172326 172327 172328 172329 172330 172331 172332 172333 172334 172335 172336 172337 172338 172339 172340 172341 172342 172343 172344 172345 172346 172347 172348 172349 172350 172351 172352 172353 172354 172355 172356 172357 172358 172359 172360 172361 172362 172363 172364 172365 172366 172367 172368 172369 172370 172371 |
Vdbe *v = sqlite3GetVdbe(pParse);
Window *pWin;
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
FuncDef *pFunc = pWin->pWFunc;
int regArg;
int nArg = pWin->bExprArgs ? 0 : windowArgCount(pWin);
int i;
assert( bInverse==0 || pWin->eStart!=TK_UNBOUNDED );
/* All OVER clauses in the same window function aggregate step must
** be the same. */
assert( pWin==pMWin || sqlite3WindowCompare(pParse,pWin,pMWin,0)!=1 );
for(i=0; i<nArg; i++){
if( i!=1 || pFunc->zName!=nth_valueName ){
sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+i, reg+i);
}else{
sqlite3VdbeAddOp3(v, OP_Column, pMWin->iEphCsr, pWin->iArgCol+i, reg+i);
}
}
regArg = reg;
if( pMWin->regStartRowid==0
&& (pFunc->funcFlags & SQLITE_FUNC_MINMAX)
&& (pWin->eStart!=TK_UNBOUNDED)
){
int addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regArg);
VdbeCoverage(v);
if( bInverse==0 ){
sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1, 1);
sqlite3VdbeAddOp2(v, OP_SCopy, regArg, pWin->regApp);
sqlite3VdbeAddOp3(v, OP_MakeRecord, pWin->regApp, 2, pWin->regApp+2);
sqlite3VdbeAddOp2(v, OP_IdxInsert, pWin->csrApp, pWin->regApp+2);
}else{
sqlite3VdbeAddOp4Int(v, OP_SeekGE, pWin->csrApp, 0, regArg, 1);
VdbeCoverageNeverTaken(v);
sqlite3VdbeAddOp1(v, OP_Delete, pWin->csrApp);
sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
}
sqlite3VdbeJumpHere(v, addrIsNull);
}else if( pWin->regApp ){
assert( pFunc->zName==nth_valueName
|| pFunc->zName==first_valueName
);
assert( bInverse==0 || bInverse==1 );
sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1-bInverse, 1);
}else if( pFunc->xSFunc!=noopStepFunc ){
| > > > > > > > > > > > > > > < < < < < < < < < < < < < | 172453 172454 172455 172456 172457 172458 172459 172460 172461 172462 172463 172464 172465 172466 172467 172468 172469 172470 172471 172472 172473 172474 172475 172476 172477 172478 172479 172480 172481 172482 172483 172484 172485 172486 172487 172488 172489 172490 172491 172492 172493 172494 172495 172496 172497 172498 172499 172500 172501 172502 172503 172504 172505 172506 172507 172508 172509 172510 172511 172512 172513 172514 172515 172516 172517 172518 172519 172520 172521 |
Vdbe *v = sqlite3GetVdbe(pParse);
Window *pWin;
for(pWin=pMWin; pWin; pWin=pWin->pNextWin){
FuncDef *pFunc = pWin->pWFunc;
int regArg;
int nArg = pWin->bExprArgs ? 0 : windowArgCount(pWin);
int i;
int addrIf = 0;
assert( bInverse==0 || pWin->eStart!=TK_UNBOUNDED );
/* All OVER clauses in the same window function aggregate step must
** be the same. */
assert( pWin==pMWin || sqlite3WindowCompare(pParse,pWin,pMWin,0)!=1 );
for(i=0; i<nArg; i++){
if( i!=1 || pFunc->zName!=nth_valueName ){
sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+i, reg+i);
}else{
sqlite3VdbeAddOp3(v, OP_Column, pMWin->iEphCsr, pWin->iArgCol+i, reg+i);
}
}
regArg = reg;
if( pWin->pFilter ){
int regTmp;
assert( ExprUseXList(pWin->pOwner) );
assert( pWin->bExprArgs || !nArg ||nArg==pWin->pOwner->x.pList->nExpr );
assert( pWin->bExprArgs || nArg ||pWin->pOwner->x.pList==0 );
regTmp = sqlite3GetTempReg(pParse);
sqlite3VdbeAddOp3(v, OP_Column, csr, pWin->iArgCol+nArg,regTmp);
addrIf = sqlite3VdbeAddOp3(v, OP_IfNot, regTmp, 0, 1);
VdbeCoverage(v);
sqlite3ReleaseTempReg(pParse, regTmp);
}
if( pMWin->regStartRowid==0
&& (pFunc->funcFlags & SQLITE_FUNC_MINMAX)
&& (pWin->eStart!=TK_UNBOUNDED)
){
int addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regArg);
VdbeCoverage(v);
if( bInverse==0 ){
sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1, 1);
sqlite3VdbeAddOp2(v, OP_SCopy, regArg, pWin->regApp);
sqlite3VdbeAddOp3(v, OP_MakeRecord, pWin->regApp, 2, pWin->regApp+2);
sqlite3VdbeAddOp2(v, OP_IdxInsert, pWin->csrApp, pWin->regApp+2);
}else{
sqlite3VdbeAddOp4Int(v, OP_SeekGE, pWin->csrApp, 0, regArg, 1);
VdbeCoverageNeverTaken(v);
sqlite3VdbeAddOp1(v, OP_Delete, pWin->csrApp);
sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2);
}
sqlite3VdbeJumpHere(v, addrIsNull);
}else if( pWin->regApp ){
assert( pWin->pFilter==0 );
assert( pFunc->zName==nth_valueName
|| pFunc->zName==first_valueName
);
assert( bInverse==0 || bInverse==1 );
sqlite3VdbeAddOp2(v, OP_AddImm, pWin->regApp+1-bInverse, 1);
}else if( pFunc->xSFunc!=noopStepFunc ){
if( pWin->bExprArgs ){
int iOp = sqlite3VdbeCurrentAddr(v);
int iEnd;
assert( ExprUseXList(pWin->pOwner) );
nArg = pWin->pOwner->x.pList->nExpr;
regArg = sqlite3GetTempRange(pParse, nArg);
|
| ︙ | ︙ | |||
172404 172405 172406 172407 172408 172409 172410 |
assert( ExprUseXList(pWin->pOwner) );
pColl = sqlite3ExprNNCollSeq(pParse, pWin->pOwner->x.pList->a[0].pExpr);
sqlite3VdbeAddOp4(v, OP_CollSeq, 0,0,0, (const char*)pColl, P4_COLLSEQ);
}
sqlite3VdbeAddOp3(v, bInverse? OP_AggInverse : OP_AggStep,
bInverse, regArg, pWin->regAccum);
sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF);
| | < > > | 172534 172535 172536 172537 172538 172539 172540 172541 172542 172543 172544 172545 172546 172547 172548 172549 172550 172551 172552 172553 172554 |
assert( ExprUseXList(pWin->pOwner) );
pColl = sqlite3ExprNNCollSeq(pParse, pWin->pOwner->x.pList->a[0].pExpr);
sqlite3VdbeAddOp4(v, OP_CollSeq, 0,0,0, (const char*)pColl, P4_COLLSEQ);
}
sqlite3VdbeAddOp3(v, bInverse? OP_AggInverse : OP_AggStep,
bInverse, regArg, pWin->regAccum);
sqlite3VdbeAppendP4(v, pFunc, P4_FUNCDEF);
sqlite3VdbeChangeP5(v, (u16)nArg);
if( pWin->bExprArgs ){
sqlite3ReleaseTempRange(pParse, regArg, nArg);
}
}
if( addrIf ) sqlite3VdbeJumpHere(v, addrIf);
}
}
/*
** Values that may be passed as the second argument to windowCodeOp().
*/
#define WINDOW_RETURN_ROW 1
|
| ︙ | ︙ | |||
173835 173836 173837 173838 173839 173840 173841 173842 173843 173844 173845 173846 173847 173848 |
** UPDATE ON (a,b,c)
**
** Then the "b" IdList records the list "a,b,c".
*/
struct TrigEvent { int a; IdList * b; };
struct FrameBound { int eType; Expr *pExpr; };
/*
** Disable lookaside memory allocation for objects that might be
** shared across database connections.
*/
static void disableLookaside(Parse *pParse){
sqlite3 *db = pParse->db;
| > > > > > > > | 173966 173967 173968 173969 173970 173971 173972 173973 173974 173975 173976 173977 173978 173979 173980 173981 173982 173983 173984 173985 173986 |
** UPDATE ON (a,b,c)
**
** Then the "b" IdList records the list "a,b,c".
*/
struct TrigEvent { int a; IdList * b; };
struct FrameBound { int eType; Expr *pExpr; };
/*
** Generate a syntax error
*/
static void parserSyntaxError(Parse *pParse, Token *p){
sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", p);
}
/*
** Disable lookaside memory allocation for objects that might be
** shared across database connections.
*/
static void disableLookaside(Parse *pParse){
sqlite3 *db = pParse->db;
|
| ︙ | ︙ | |||
177728 177729 177730 177731 177732 177733 177734 |
{
sqlite3DropTable(pParse, yymsp[0].minor.yy203, 1, yymsp[-1].minor.yy144);
}
break;
case 84: /* cmd ::= select */
{
SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0};
| > > > | > | 177866 177867 177868 177869 177870 177871 177872 177873 177874 177875 177876 177877 177878 177879 177880 177881 177882 177883 177884 |
{
sqlite3DropTable(pParse, yymsp[0].minor.yy203, 1, yymsp[-1].minor.yy144);
}
break;
case 84: /* cmd ::= select */
{
SelectDest dest = {SRT_Output, 0, 0, 0, 0, 0, 0};
if( (pParse->db->mDbFlags & DBFLAG_EncodingFixed)!=0
|| sqlite3ReadSchema(pParse)==SQLITE_OK
){
sqlite3Select(pParse, yymsp[0].minor.yy555, &dest);
}
sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy555);
}
break;
case 85: /* select ::= WITH wqlist selectnowith */
{yymsp[-2].minor.yy555 = attachWithToSelect(pParse,yymsp[0].minor.yy555,yymsp[-1].minor.yy59);}
break;
case 86: /* select ::= WITH RECURSIVE wqlist selectnowith */
|
| ︙ | ︙ | |||
178199 178200 178201 178202 178203 178204 178205 |
}else{
/* When doing a nested parse, one can include terms in an expression
** that look like this: #1 #2 ... These terms refer to registers
** in the virtual machine. #N is the N-th register. */
Token t = yymsp[0].minor.yy0; /*A-overwrites-X*/
assert( t.n>=2 );
if( pParse->nested==0 ){
| | | 178341 178342 178343 178344 178345 178346 178347 178348 178349 178350 178351 178352 178353 178354 178355 |
}else{
/* When doing a nested parse, one can include terms in an expression
** that look like this: #1 #2 ... These terms refer to registers
** in the virtual machine. #N is the N-th register. */
Token t = yymsp[0].minor.yy0; /*A-overwrites-X*/
assert( t.n>=2 );
if( pParse->nested==0 ){
parserSyntaxError(pParse, &t);
yymsp[0].minor.yy454 = 0;
}else{
yymsp[0].minor.yy454 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
if( yymsp[0].minor.yy454 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy454->iTable);
}
}
}
|
| ︙ | ︙ | |||
179047 179048 179049 179050 179051 179052 179053 |
sqlite3ParserARG_FETCH
sqlite3ParserCTX_FETCH
#define TOKEN yyminor
/************ Begin %syntax_error code ****************************************/
UNUSED_PARAMETER(yymajor); /* Silence some compiler warnings */
if( TOKEN.z[0] ){
| | | 179189 179190 179191 179192 179193 179194 179195 179196 179197 179198 179199 179200 179201 179202 179203 |
sqlite3ParserARG_FETCH
sqlite3ParserCTX_FETCH
#define TOKEN yyminor
/************ Begin %syntax_error code ****************************************/
UNUSED_PARAMETER(yymajor); /* Silence some compiler warnings */
if( TOKEN.z[0] ){
parserSyntaxError(pParse, &TOKEN);
}else{
sqlite3ErrorMsg(pParse, "incomplete input");
}
/************ End %syntax_error code ******************************************/
sqlite3ParserARG_STORE /* Suppress warning about unused %extra_argument variable */
sqlite3ParserCTX_STORE
}
|
| ︙ | ︙ | |||
180538 180539 180540 180541 180542 180543 180544 |
if( db->mallocFailed ){
pParse->rc = SQLITE_NOMEM_BKPT;
}
if( pParse->zErrMsg || (pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE) ){
if( pParse->zErrMsg==0 ){
pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc));
}
| > | > | 180680 180681 180682 180683 180684 180685 180686 180687 180688 180689 180690 180691 180692 180693 180694 180695 180696 |
if( db->mallocFailed ){
pParse->rc = SQLITE_NOMEM_BKPT;
}
if( pParse->zErrMsg || (pParse->rc!=SQLITE_OK && pParse->rc!=SQLITE_DONE) ){
if( pParse->zErrMsg==0 ){
pParse->zErrMsg = sqlite3MPrintf(db, "%s", sqlite3ErrStr(pParse->rc));
}
if( (pParse->prepFlags & SQLITE_PREPARE_DONT_LOG)==0 ){
sqlite3_log(pParse->rc, "%s in \"%s\"", pParse->zErrMsg, pParse->zTail);
}
nErr++;
}
pParse->zTail = zSql;
#ifndef SQLITE_OMIT_VIRTUALTABLE
sqlite3_free(pParse->apVtabLock);
#endif
|
| ︙ | ︙ | |||
182511 182512 182513 182514 182515 182516 182517 | } sqlite3HashClear(&db->aModule); #endif sqlite3Error(db, SQLITE_OK); /* Deallocates any cached error strings. */ sqlite3ValueFree(db->pErr); sqlite3CloseExtensions(db); | < < < < | 182655 182656 182657 182658 182659 182660 182661 182662 182663 182664 182665 182666 182667 182668 | } sqlite3HashClear(&db->aModule); #endif sqlite3Error(db, SQLITE_OK); /* Deallocates any cached error strings. */ sqlite3ValueFree(db->pErr); sqlite3CloseExtensions(db); db->eOpenState = SQLITE_STATE_ERROR; /* The temp-database schema is allocated differently from the other schema ** objects (using sqliteMalloc() directly, instead of sqlite3BtreeSchema()). ** So it needs to be freed here. Todo: Why not roll the temp schema into ** the same sqliteMalloc() as the one that allocates the database |
| ︙ | ︙ | |||
183949 183950 183951 183952 183953 183954 183955 | #endif #if SQLITE_MAX_COMPOUND_SELECT<2 # error SQLITE_MAX_COMPOUND_SELECT must be at least 2 #endif #if SQLITE_MAX_VDBE_OP<40 # error SQLITE_MAX_VDBE_OP must be at least 40 #endif | | | | 184089 184090 184091 184092 184093 184094 184095 184096 184097 184098 184099 184100 184101 184102 184103 184104 | #endif #if SQLITE_MAX_COMPOUND_SELECT<2 # error SQLITE_MAX_COMPOUND_SELECT must be at least 2 #endif #if SQLITE_MAX_VDBE_OP<40 # error SQLITE_MAX_VDBE_OP must be at least 40 #endif #if SQLITE_MAX_FUNCTION_ARG<0 || SQLITE_MAX_FUNCTION_ARG>32767 # error SQLITE_MAX_FUNCTION_ARG must be between 0 and 32767 #endif #if SQLITE_MAX_ATTACHED<0 || SQLITE_MAX_ATTACHED>125 # error SQLITE_MAX_ATTACHED must be between 0 and 125 #endif #if SQLITE_MAX_LIKE_PATTERN_LENGTH<1 # error SQLITE_MAX_LIKE_PATTERN_LENGTH must be at least 1 #endif |
| ︙ | ︙ | |||
184017 184018 184019 184020 184021 184022 184023 |
if( limitId<0 || limitId>=SQLITE_N_LIMIT ){
return -1;
}
oldLimit = db->aLimit[limitId];
if( newLimit>=0 ){ /* IMP: R-52476-28732 */
if( newLimit>aHardLimit[limitId] ){
newLimit = aHardLimit[limitId]; /* IMP: R-51463-25634 */
| | | | 184157 184158 184159 184160 184161 184162 184163 184164 184165 184166 184167 184168 184169 184170 184171 184172 |
if( limitId<0 || limitId>=SQLITE_N_LIMIT ){
return -1;
}
oldLimit = db->aLimit[limitId];
if( newLimit>=0 ){ /* IMP: R-52476-28732 */
if( newLimit>aHardLimit[limitId] ){
newLimit = aHardLimit[limitId]; /* IMP: R-51463-25634 */
}else if( newLimit<SQLITE_MIN_LENGTH && limitId==SQLITE_LIMIT_LENGTH ){
newLimit = SQLITE_MIN_LENGTH;
}
db->aLimit[limitId] = newLimit;
}
return oldLimit; /* IMP: R-53341-35419 */
}
/*
|
| ︙ | ︙ | |||
185362 185363 185364 185365 185366 185367 185368 |
volatile int x = 0;
assert( /*side-effects-ok*/ (x = va_arg(ap,int))!=0 );
rc = x;
#if defined(SQLITE_DEBUG)
/* Invoke these debugging routines so that the compiler does not
** issue "defined but not used" warnings. */
if( x==9999 ){
| < | 185502 185503 185504 185505 185506 185507 185508 185509 185510 185511 185512 185513 185514 185515 |
volatile int x = 0;
assert( /*side-effects-ok*/ (x = va_arg(ap,int))!=0 );
rc = x;
#if defined(SQLITE_DEBUG)
/* Invoke these debugging routines so that the compiler does not
** issue "defined but not used" warnings. */
if( x==9999 ){
sqlite3ShowExpr(0);
sqlite3ShowExprList(0);
sqlite3ShowIdList(0);
sqlite3ShowSrcList(0);
sqlite3ShowWith(0);
sqlite3ShowUpsert(0);
#ifndef SQLITE_OMIT_TRIGGER
|
| ︙ | ︙ | |||
189794 189795 189796 189797 189798 189799 189800 189801 189802 189803 189804 189805 189806 189807 189808 189809 189810 189811 |
/* Never set both isSaveLeft and isExact for the same invocation. */
assert( isSaveLeft==0 || isExact==0 );
assert_fts3_nc( p!=0 && *p1!=0 && *p2!=0 );
if( *p1==POS_COLUMN ){
p1++;
p1 += fts3GetVarint32(p1, &iCol1);
}
if( *p2==POS_COLUMN ){
p2++;
p2 += fts3GetVarint32(p2, &iCol2);
}
while( 1 ){
if( iCol1==iCol2 ){
char *pSave = p;
sqlite3_int64 iPrev = 0;
sqlite3_int64 iPos1 = 0;
| > > > > > | 189933 189934 189935 189936 189937 189938 189939 189940 189941 189942 189943 189944 189945 189946 189947 189948 189949 189950 189951 189952 189953 189954 189955 |
/* Never set both isSaveLeft and isExact for the same invocation. */
assert( isSaveLeft==0 || isExact==0 );
assert_fts3_nc( p!=0 && *p1!=0 && *p2!=0 );
if( *p1==POS_COLUMN ){
p1++;
p1 += fts3GetVarint32(p1, &iCol1);
/* iCol1==0 indicates corruption. Column 0 does not have a POS_COLUMN
** entry, so this is actually end-of-doclist. */
if( iCol1==0 ) return 0;
}
if( *p2==POS_COLUMN ){
p2++;
p2 += fts3GetVarint32(p2, &iCol2);
/* As above, iCol2==0 indicates corruption. */
if( iCol2==0 ) return 0;
}
while( 1 ){
if( iCol1==iCol2 ){
char *pSave = p;
sqlite3_int64 iPrev = 0;
sqlite3_int64 iPos1 = 0;
|
| ︙ | ︙ | |||
192968 192969 192970 192971 192972 192973 192974 |
/* Allocate temporary working space. */
for(p=pExpr; p->pLeft; p=p->pLeft){
assert( p->pRight->pPhrase->doclist.nList>0 );
nTmp += p->pRight->pPhrase->doclist.nList;
}
nTmp += p->pPhrase->doclist.nList;
| | | 193112 193113 193114 193115 193116 193117 193118 193119 193120 193121 193122 193123 193124 193125 193126 |
/* Allocate temporary working space. */
for(p=pExpr; p->pLeft; p=p->pLeft){
assert( p->pRight->pPhrase->doclist.nList>0 );
nTmp += p->pRight->pPhrase->doclist.nList;
}
nTmp += p->pPhrase->doclist.nList;
aTmp = sqlite3_malloc64(nTmp*2 + FTS3_VARINT_MAX);
if( !aTmp ){
*pRc = SQLITE_NOMEM;
res = 0;
}else{
char *aPoslist = p->pPhrase->doclist.pList;
int nToken = p->pPhrase->nToken;
|
| ︙ | ︙ | |||
193619 193620 193621 193622 193623 193624 193625 |
*/
#ifdef SQLITE_DEBUG
SQLITE_PRIVATE int sqlite3Fts3Corrupt(){
return SQLITE_CORRUPT_VTAB;
}
#endif
| | | 193763 193764 193765 193766 193767 193768 193769 193770 193771 193772 193773 193774 193775 193776 193777 |
*/
#ifdef SQLITE_DEBUG
SQLITE_PRIVATE int sqlite3Fts3Corrupt(){
return SQLITE_CORRUPT_VTAB;
}
#endif
#if !defined(SQLITE_CORE)
/*
** Initialize API pointer table, if required.
*/
#ifdef _WIN32
__declspec(dllexport)
#endif
SQLITE_API int sqlite3_fts3_init(
|
| ︙ | ︙ | |||
194521 194522 194523 194524 194525 194526 194527 |
const char *zByte;
int nByte = 0, iBegin = 0, iEnd = 0, iPos = 0;
rc = pModule->xNext(pCursor, &zByte, &nByte, &iBegin, &iEnd, &iPos);
if( rc==SQLITE_OK ){
Fts3PhraseToken *pToken;
p = fts3ReallocOrFree(p, nSpace + ii*sizeof(Fts3PhraseToken));
| < < | > > > < < < | > > > > < < < < < < | > | > > | | | 194665 194666 194667 194668 194669 194670 194671 194672 194673 194674 194675 194676 194677 194678 194679 194680 194681 194682 194683 194684 194685 194686 194687 194688 194689 194690 194691 194692 194693 194694 194695 194696 194697 194698 194699 194700 194701 194702 194703 194704 194705 194706 194707 194708 194709 194710 194711 194712 194713 194714 194715 194716 194717 194718 194719 194720 194721 194722 194723 194724 194725 194726 194727 194728 194729 194730 194731 194732 194733 194734 194735 194736 194737 194738 |
const char *zByte;
int nByte = 0, iBegin = 0, iEnd = 0, iPos = 0;
rc = pModule->xNext(pCursor, &zByte, &nByte, &iBegin, &iEnd, &iPos);
if( rc==SQLITE_OK ){
Fts3PhraseToken *pToken;
p = fts3ReallocOrFree(p, nSpace + ii*sizeof(Fts3PhraseToken));
zTemp = fts3ReallocOrFree(zTemp, nTemp + nByte);
if( !zTemp || !p ){
rc = SQLITE_NOMEM;
goto getnextstring_out;
}
assert( nToken==ii );
pToken = &((Fts3Phrase *)(&p[1]))->aToken[ii];
memset(pToken, 0, sizeof(Fts3PhraseToken));
memcpy(&zTemp[nTemp], zByte, nByte);
nTemp += nByte;
pToken->n = nByte;
pToken->isPrefix = (iEnd<nInput && zInput[iEnd]=='*');
pToken->bFirst = (iBegin>0 && zInput[iBegin-1]=='^');
nToken = ii+1;
}
}
}
if( rc==SQLITE_DONE ){
int jj;
char *zBuf = 0;
p = fts3ReallocOrFree(p, nSpace + nToken*sizeof(Fts3PhraseToken) + nTemp);
if( !p ){
rc = SQLITE_NOMEM;
goto getnextstring_out;
}
memset(p, 0, (char *)&(((Fts3Phrase *)&p[1])->aToken[0])-(char *)p);
p->eType = FTSQUERY_PHRASE;
p->pPhrase = (Fts3Phrase *)&p[1];
p->pPhrase->iColumn = pParse->iDefaultCol;
p->pPhrase->nToken = nToken;
zBuf = (char *)&p->pPhrase->aToken[nToken];
assert( nTemp==0 || zTemp );
if( zTemp ){
memcpy(zBuf, zTemp, nTemp);
}
for(jj=0; jj<p->pPhrase->nToken; jj++){
p->pPhrase->aToken[jj].z = zBuf;
zBuf += p->pPhrase->aToken[jj].n;
}
rc = SQLITE_OK;
}
getnextstring_out:
if( pCursor ){
pModule->xClose(pCursor);
}
sqlite3_free(zTemp);
if( rc!=SQLITE_OK ){
sqlite3_free(p);
p = 0;
}
*ppExpr = p;
return rc;
}
/*
** The output variable *ppExpr is populated with an allocated Fts3Expr
** structure, or set to 0 if the end of the input buffer is reached.
**
** Returns an SQLite error code. SQLITE_OK if everything works, SQLITE_NOMEM
|
| ︙ | ︙ | |||
215393 215394 215395 215396 215397 215398 215399 |
#else
sqlite3_str_appendf(pOut, " %d", cell.aCoord[jj].i);
#endif
}
sqlite3_str_append(pOut, "}", 1);
}
errCode = sqlite3_str_errcode(pOut);
| < > | 215536 215537 215538 215539 215540 215541 215542 215543 215544 215545 215546 215547 215548 215549 215550 215551 |
#else
sqlite3_str_appendf(pOut, " %d", cell.aCoord[jj].i);
#endif
}
sqlite3_str_append(pOut, "}", 1);
}
errCode = sqlite3_str_errcode(pOut);
sqlite3_result_error_code(ctx, errCode);
sqlite3_result_text(ctx, sqlite3_str_finish(pOut), -1, sqlite3_free);
}
/* This routine implements an SQL function that returns the "depth" parameter
** from the front of a blob that is an r-tree node. For example:
**
** SELECT rtreedepth(data) FROM rt_node WHERE nodeno=1;
**
|
| ︙ | ︙ | |||
217910 217911 217912 217913 217914 217915 217916 |
pGeomCtx->xDestructor = xDestructor;
pGeomCtx->pContext = pContext;
return sqlite3_create_function_v2(db, zQueryFunc, -1, SQLITE_ANY,
(void *)pGeomCtx, geomCallback, 0, 0, rtreeFreeCallback
);
}
| | | 218053 218054 218055 218056 218057 218058 218059 218060 218061 218062 218063 218064 218065 218066 218067 |
pGeomCtx->xDestructor = xDestructor;
pGeomCtx->pContext = pContext;
return sqlite3_create_function_v2(db, zQueryFunc, -1, SQLITE_ANY,
(void *)pGeomCtx, geomCallback, 0, 0, rtreeFreeCallback
);
}
#ifndef SQLITE_CORE
#ifdef _WIN32
__declspec(dllexport)
#endif
SQLITE_API int sqlite3_rtree_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
|
| ︙ | ︙ | |||
218501 218502 218503 218504 218505 218506 218507 |
p->xFunc, 0, 0
);
}
return rc;
}
| | | 218644 218645 218646 218647 218648 218649 218650 218651 218652 218653 218654 218655 218656 218657 218658 |
p->xFunc, 0, 0
);
}
return rc;
}
#ifndef SQLITE_CORE
#ifdef _WIN32
__declspec(dllexport)
#endif
SQLITE_API int sqlite3_icu_init(
sqlite3 *db,
char **pzErrMsg,
const sqlite3_api_routines *pApi
|
| ︙ | ︙ | |||
226175 226176 226177 226178 226179 226180 226181 226182 226183 226184 226185 226186 226187 226188 |
if( rc==SQLITE_OK ){
const void *pData = sqlite3_value_blob(argv[3]);
if( (rc = sqlite3PagerWrite(pDbPage))==SQLITE_OK && pData ){
unsigned char *aPage = sqlite3PagerGetData(pDbPage);
memcpy(aPage, pData, szPage);
pTab->pgnoTrunc = 0;
}
}
sqlite3PagerUnref(pDbPage);
return rc;
update_fail:
sqlite3_free(pVtab->zErrMsg);
pVtab->zErrMsg = sqlite3_mprintf("%s", zErr);
| > > | 226318 226319 226320 226321 226322 226323 226324 226325 226326 226327 226328 226329 226330 226331 226332 226333 |
if( rc==SQLITE_OK ){
const void *pData = sqlite3_value_blob(argv[3]);
if( (rc = sqlite3PagerWrite(pDbPage))==SQLITE_OK && pData ){
unsigned char *aPage = sqlite3PagerGetData(pDbPage);
memcpy(aPage, pData, szPage);
pTab->pgnoTrunc = 0;
}
}else{
pTab->pgnoTrunc = 0;
}
sqlite3PagerUnref(pDbPage);
return rc;
update_fail:
sqlite3_free(pVtab->zErrMsg);
pVtab->zErrMsg = sqlite3_mprintf("%s", zErr);
|
| ︙ | ︙ | |||
226208 226209 226210 226211 226212 226213 226214 |
/* Invoke sqlite3PagerTruncate() as necessary, just prior to COMMIT
*/
static int dbpageSync(sqlite3_vtab *pVtab){
DbpageTable *pTab = (DbpageTable *)pVtab;
if( pTab->pgnoTrunc>0 ){
Btree *pBt = pTab->db->aDb[pTab->iDbTrunc].pBt;
Pager *pPager = sqlite3BtreePager(pBt);
| > > | > > | 226353 226354 226355 226356 226357 226358 226359 226360 226361 226362 226363 226364 226365 226366 226367 226368 226369 226370 226371 |
/* Invoke sqlite3PagerTruncate() as necessary, just prior to COMMIT
*/
static int dbpageSync(sqlite3_vtab *pVtab){
DbpageTable *pTab = (DbpageTable *)pVtab;
if( pTab->pgnoTrunc>0 ){
Btree *pBt = pTab->db->aDb[pTab->iDbTrunc].pBt;
Pager *pPager = sqlite3BtreePager(pBt);
sqlite3BtreeEnter(pBt);
if( pTab->pgnoTrunc<sqlite3BtreeLastPage(pBt) ){
sqlite3PagerTruncateImage(pPager, pTab->pgnoTrunc);
}
sqlite3BtreeLeave(pBt);
}
pTab->pgnoTrunc = 0;
return SQLITE_OK;
}
/* Cancel any pending truncate.
*/
|
| ︙ | ︙ | |||
232802 232803 232804 232805 232806 232807 232808 | } #endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */ /************** End of sqlite3session.c **************************************/ /************** Begin file fts5.c ********************************************/ | | > > > > > > > > > > > > > > > > > > > > > > > > > > | 232951 232952 232953 232954 232955 232956 232957 232958 232959 232960 232961 232962 232963 232964 232965 232966 232967 232968 232969 232970 232971 232972 232973 232974 232975 232976 232977 232978 232979 232980 232981 232982 232983 232984 232985 232986 232987 232988 232989 232990 232991 232992 232993 232994 232995 232996 232997 232998 232999 233000 | } #endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */ /************** End of sqlite3session.c **************************************/ /************** Begin file fts5.c ********************************************/ /* ** This, the "fts5.c" source file, is a composite file that is itself ** assembled from the following files: ** ** fts5.h ** fts5Int.h ** fts5parse.h <--- Generated from fts5parse.y by Lemon ** fts5parse.c <--- Generated from fts5parse.y by Lemon ** fts5_aux.c ** fts5_buffer.c ** fts5_config.c ** fts5_expr.c ** fts5_hash.c ** fts5_index.c ** fts5_main.c ** fts5_storage.c ** fts5_tokenize.c ** fts5_unicode2.c ** fts5_varint.c ** fts5_vocab.c */ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) #if !defined(NDEBUG) && !defined(SQLITE_DEBUG) # define NDEBUG 1 #endif #if defined(NDEBUG) && defined(SQLITE_DEBUG) # undef NDEBUG #endif #ifdef HAVE_STDINT_H /* #include <stdint.h> */ #endif #ifdef HAVE_INTTYPES_H /* #include <inttypes.h> */ #endif /* ** 2014 May 31 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. |
| ︙ | ︙ | |||
233112 233113 233114 233115 233116 233117 233118 | ** ** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) ** This is used to access token iToken of phrase hit iIdx within the ** current row. If iIdx is less than zero or greater than or equal to the ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, ** output variable (*ppToken) is set to point to a buffer containing the ** matching document token, and (*pnToken) to the size of that buffer in | < < | > > > > > > > > > > > > > > > > > | 233287 233288 233289 233290 233291 233292 233293 233294 233295 233296 233297 233298 233299 233300 233301 233302 233303 233304 233305 233306 233307 233308 233309 233310 233311 233312 233313 233314 233315 233316 233317 233318 233319 233320 233321 233322 | ** ** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) ** This is used to access token iToken of phrase hit iIdx within the ** current row. If iIdx is less than zero or greater than or equal to the ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, ** output variable (*ppToken) is set to point to a buffer containing the ** matching document token, and (*pnToken) to the size of that buffer in ** bytes. ** ** The output text is not a copy of the document text that was tokenized. ** It is the output of the tokenizer module. For tokendata=1 tables, this ** includes any embedded 0x00 and trailing data. ** ** This API may be slow in some cases if the token identified by parameters ** iIdx and iToken matched a prefix token in the query. In most cases, the ** first call to this API for each prefix token in the query is forced ** to scan the portion of the full-text index that matches the prefix ** token to collect the extra data required by this API. If the prefix ** token matches a large number of token instances in the document set, ** this may be a performance problem. ** ** If the user knows in advance that a query may use this API for a ** prefix token, FTS5 may be configured to collect all required data as part ** of the initial querying of the full-text index, avoiding the second scan ** entirely. This also causes prefix queries that do not use this API to ** run more slowly and use more memory. FTS5 may be configured in this way ** either on a per-table basis using the [FTS5 insttoken | 'insttoken'] ** option, or on a per-query basis using the ** [fts5_insttoken | fts5_insttoken()] user function. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. ** ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) ** If parameter iCol is less than zero, or greater than or equal to the ** number of columns in the table, SQLITE_RANGE is returned. |
| ︙ | ︙ | |||
233801 233802 233803 233804 233805 233806 233807 | int nAutomerge; /* 'automerge' setting */ int nCrisisMerge; /* Maximum allowed segments per level */ int nUsermerge; /* 'usermerge' setting */ int nHashSize; /* Bytes of memory for in-memory hash */ char *zRank; /* Name of rank function */ char *zRankArgs; /* Arguments to rank function */ int bSecureDelete; /* 'secure-delete' */ | | > | 233991 233992 233993 233994 233995 233996 233997 233998 233999 234000 234001 234002 234003 234004 234005 234006 | int nAutomerge; /* 'automerge' setting */ int nCrisisMerge; /* Maximum allowed segments per level */ int nUsermerge; /* 'usermerge' setting */ int nHashSize; /* Bytes of memory for in-memory hash */ char *zRank; /* Name of rank function */ char *zRankArgs; /* Arguments to rank function */ int bSecureDelete; /* 'secure-delete' */ int nDeleteMerge; /* 'deletemerge' */ int bPrefixInsttoken; /* 'prefix-insttoken' */ /* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */ char **pzErrmsg; #ifdef SQLITE_DEBUG int bPrefixIndex; /* True to use prefix-indexes */ #endif |
| ︙ | ︙ | |||
234058 234059 234060 234061 234062 234063 234064 | static void *sqlite3Fts5StructureRef(Fts5Index*); static void sqlite3Fts5StructureRelease(void*); static int sqlite3Fts5StructureTest(Fts5Index*, void*); /* ** Used by xInstToken(): */ | | > > > > > > > | 234249 234250 234251 234252 234253 234254 234255 234256 234257 234258 234259 234260 234261 234262 234263 234264 234265 234266 234267 234268 234269 234270 | static void *sqlite3Fts5StructureRef(Fts5Index*); static void sqlite3Fts5StructureRelease(void*); static int sqlite3Fts5StructureTest(Fts5Index*, void*); /* ** Used by xInstToken(): */ static int sqlite3Fts5IterToken( Fts5IndexIter *pIndexIter, const char *pToken, int nToken, i64 iRowid, int iCol, int iOff, const char **ppOut, int *pnOut ); /* ** Insert or remove data to or from the index. Each time a document is ** added to or removed from the index, this function is called one or more ** times. ** ** For an insert, it must be called once for each token in the new document. |
| ︙ | ︙ | |||
238272 238273 238274 238275 238276 238277 238278 238279 238280 238281 238282 238283 238284 238285 |
bVal = sqlite3_value_int(pVal);
}
if( bVal<0 ){
*pbBadkey = 1;
}else{
pConfig->bSecureDelete = (bVal ? 1 : 0);
}
}else{
*pbBadkey = 1;
}
return rc;
}
/*
| > > > > > > > > > > > > > | 238470 238471 238472 238473 238474 238475 238476 238477 238478 238479 238480 238481 238482 238483 238484 238485 238486 238487 238488 238489 238490 238491 238492 238493 238494 238495 238496 |
bVal = sqlite3_value_int(pVal);
}
if( bVal<0 ){
*pbBadkey = 1;
}else{
pConfig->bSecureDelete = (bVal ? 1 : 0);
}
}
else if( 0==sqlite3_stricmp(zKey, "insttoken") ){
int bVal = -1;
if( SQLITE_INTEGER==sqlite3_value_numeric_type(pVal) ){
bVal = sqlite3_value_int(pVal);
}
if( bVal<0 ){
*pbBadkey = 1;
}else{
pConfig->bPrefixInsttoken = (bVal ? 1 : 0);
}
}else{
*pbBadkey = 1;
}
return rc;
}
/*
|
| ︙ | ︙ | |||
241407 241408 241409 241410 241411 241412 241413 |
for(pT=&pExpr->apExprPhrase[i]->aTerm[0]; pT; pT=pT->pSynonym){
if( (pT->nQueryTerm==nQuery || (pT->nQueryTerm<nQuery && pT->bPrefix))
&& memcmp(pT->pTerm, pToken, pT->nQueryTerm)==0
){
int rc = sqlite3Fts5PoslistWriterAppend(
&pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff
);
| | | 241618 241619 241620 241621 241622 241623 241624 241625 241626 241627 241628 241629 241630 241631 241632 |
for(pT=&pExpr->apExprPhrase[i]->aTerm[0]; pT; pT=pT->pSynonym){
if( (pT->nQueryTerm==nQuery || (pT->nQueryTerm<nQuery && pT->bPrefix))
&& memcmp(pT->pTerm, pToken, pT->nQueryTerm)==0
){
int rc = sqlite3Fts5PoslistWriterAppend(
&pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff
);
if( rc==SQLITE_OK && (pExpr->pConfig->bTokendata || pT->bPrefix) ){
int iCol = p->iOff>>32;
int iTokOff = p->iOff & 0x7FFFFFFF;
rc = sqlite3Fts5IndexIterWriteTokendata(
pT->pIter, pToken, nToken, iRowid, iCol, iTokOff
);
}
if( rc ) return rc;
|
| ︙ | ︙ | |||
241600 241601 241602 241603 241604 241605 241606 |
return SQLITE_RANGE;
}
pPhrase = pExpr->apExprPhrase[iPhrase];
if( iToken<0 || iToken>=pPhrase->nTerm ){
return SQLITE_RANGE;
}
pTerm = &pPhrase->aTerm[iToken];
| < | | > | | | | | < | 241811 241812 241813 241814 241815 241816 241817 241818 241819 241820 241821 241822 241823 241824 241825 241826 241827 241828 241829 241830 241831 241832 |
return SQLITE_RANGE;
}
pPhrase = pExpr->apExprPhrase[iPhrase];
if( iToken<0 || iToken>=pPhrase->nTerm ){
return SQLITE_RANGE;
}
pTerm = &pPhrase->aTerm[iToken];
if( pExpr->pConfig->bTokendata || pTerm->bPrefix ){
rc = sqlite3Fts5IterToken(
pTerm->pIter, pTerm->pTerm, pTerm->nQueryTerm,
iRowid, iCol, iOff+iToken, ppOut, pnOut
);
}else{
*ppOut = pTerm->pTerm;
*pnOut = pTerm->nFullTerm;
}
return rc;
}
/*
** Clear the token mappings for all Fts5IndexIter objects mannaged by
** the expression passed as the only argument.
|
| ︙ | ︙ | |||
248423 248424 248425 248426 248427 248428 248429 248430 248431 248432 248433 248434 248435 248436 248437 248438 248439 |
fts5BufferFree(p1);
fts5BufferFree(&tmp);
memset(&out.p[out.n], 0, FTS5_DATA_ZERO_PADDING);
*p1 = out;
}
static void fts5SetupPrefixIter(
Fts5Index *p, /* Index to read from */
int bDesc, /* True for "ORDER BY rowid DESC" */
int iIdx, /* Index to scan for data */
u8 *pToken, /* Buffer containing prefix to match */
int nToken, /* Size of buffer pToken in bytes */
Fts5Colset *pColset, /* Restrict matches to these columns */
Fts5Iter **ppIter /* OUT: New iterator */
){
Fts5Structure *pStruct;
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > | | > > > > > > > > | | < | | | | | | | | | < < < < < < < < < < | < < < < < < < < < < | | < < < | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | | | | < | > | | | > > > > > | | > > > | | 248633 248634 248635 248636 248637 248638 248639 248640 248641 248642 248643 248644 248645 248646 248647 248648 248649 248650 248651 248652 248653 248654 248655 248656 248657 248658 248659 248660 248661 248662 248663 248664 248665 248666 248667 248668 248669 248670 248671 248672 248673 248674 248675 248676 248677 248678 248679 248680 248681 248682 248683 248684 248685 248686 248687 248688 248689 248690 248691 248692 248693 248694 248695 248696 248697 248698 248699 248700 248701 248702 248703 248704 248705 248706 248707 248708 248709 248710 248711 248712 248713 248714 248715 248716 248717 248718 248719 248720 248721 248722 248723 248724 248725 248726 248727 248728 248729 248730 248731 248732 248733 248734 248735 248736 248737 248738 248739 248740 248741 248742 248743 248744 248745 248746 248747 248748 248749 248750 248751 248752 248753 248754 248755 248756 248757 248758 248759 248760 248761 248762 248763 248764 248765 248766 248767 248768 248769 248770 248771 248772 248773 248774 248775 248776 248777 248778 248779 248780 248781 248782 248783 248784 248785 248786 248787 248788 248789 248790 248791 248792 248793 248794 248795 248796 248797 248798 248799 248800 248801 248802 248803 248804 248805 248806 248807 248808 248809 248810 248811 248812 248813 248814 248815 248816 248817 248818 248819 248820 248821 248822 248823 248824 248825 248826 248827 248828 248829 248830 248831 248832 248833 248834 248835 248836 248837 248838 248839 248840 248841 248842 248843 248844 248845 248846 248847 248848 248849 248850 248851 248852 248853 248854 248855 248856 248857 248858 248859 248860 248861 248862 248863 248864 248865 248866 248867 248868 248869 248870 248871 248872 248873 248874 248875 248876 248877 248878 248879 248880 248881 248882 248883 248884 248885 248886 248887 248888 248889 248890 248891 248892 248893 248894 248895 248896 248897 248898 248899 248900 248901 248902 248903 248904 248905 248906 248907 248908 248909 248910 248911 248912 248913 248914 248915 248916 248917 248918 248919 248920 248921 248922 248923 248924 248925 248926 248927 248928 248929 248930 248931 248932 248933 248934 248935 248936 248937 248938 248939 248940 248941 248942 248943 248944 248945 248946 248947 248948 248949 248950 248951 248952 248953 248954 248955 248956 248957 248958 248959 248960 248961 248962 248963 248964 248965 248966 248967 248968 248969 248970 248971 248972 248973 248974 248975 248976 248977 248978 248979 248980 248981 248982 248983 248984 248985 248986 248987 248988 248989 248990 248991 248992 248993 248994 248995 248996 248997 248998 248999 249000 249001 249002 249003 249004 249005 249006 249007 249008 249009 249010 249011 249012 249013 249014 249015 249016 249017 249018 249019 249020 249021 249022 249023 249024 249025 249026 249027 249028 249029 249030 249031 249032 249033 249034 249035 249036 249037 249038 249039 249040 249041 249042 249043 249044 249045 249046 249047 249048 249049 249050 249051 249052 249053 249054 249055 249056 249057 249058 249059 249060 249061 249062 249063 249064 249065 249066 249067 249068 249069 249070 249071 249072 249073 249074 249075 249076 249077 249078 249079 249080 249081 249082 249083 249084 249085 249086 249087 249088 249089 249090 249091 249092 249093 249094 249095 249096 249097 249098 249099 249100 249101 249102 249103 249104 249105 249106 249107 249108 249109 249110 249111 249112 249113 249114 |
fts5BufferFree(p1);
fts5BufferFree(&tmp);
memset(&out.p[out.n], 0, FTS5_DATA_ZERO_PADDING);
*p1 = out;
}
/*
** Iterate through a range of entries in the FTS index, invoking the xVisit
** callback for each of them.
**
** Parameter pToken points to an nToken buffer containing an FTS index term
** (i.e. a document term with the preceding 1 byte index identifier -
** FTS5_MAIN_PREFIX or similar). If bPrefix is true, then the call visits
** all entries for terms that have pToken/nToken as a prefix. If bPrefix
** is false, then only entries with pToken/nToken as the entire key are
** visited.
**
** If the current table is a tokendata=1 table, then if bPrefix is true then
** each index term is treated separately. However, if bPrefix is false, then
** all index terms corresponding to pToken/nToken are collapsed into a single
** term before the callback is invoked.
**
** The callback invoked for each entry visited is specified by paramter xVisit.
** Each time it is invoked, it is passed a pointer to the Fts5Index object,
** a copy of the 7th paramter to this function (pCtx) and a pointer to the
** iterator that indicates the current entry. If the current entry is the
** first with a new term (i.e. different from that of the previous entry,
** including the very first term), then the final two parameters are passed
** a pointer to the term and its size in bytes, respectively. If the current
** entry is not the first associated with its term, these two parameters
** are passed 0.
**
** If parameter pColset is not NULL, then it is used to filter entries before
** the callback is invoked.
*/
static int fts5VisitEntries(
Fts5Index *p, /* Fts5 index object */
Fts5Colset *pColset, /* Columns filter to apply, or NULL */
u8 *pToken, /* Buffer containing token */
int nToken, /* Size of buffer pToken in bytes */
int bPrefix, /* True for a prefix scan */
void (*xVisit)(Fts5Index*, void *pCtx, Fts5Iter *pIter, const u8*, int),
void *pCtx /* Passed as second argument to xVisit() */
){
const int flags = (bPrefix ? FTS5INDEX_QUERY_SCAN : 0)
| FTS5INDEX_QUERY_SKIPEMPTY
| FTS5INDEX_QUERY_NOOUTPUT;
Fts5Iter *p1 = 0; /* Iterator used to gather data from index */
int bNewTerm = 1;
Fts5Structure *pStruct = fts5StructureRead(p);
fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1);
fts5IterSetOutputCb(&p->rc, p1);
for( /* no-op */ ;
fts5MultiIterEof(p, p1)==0;
fts5MultiIterNext2(p, p1, &bNewTerm)
){
Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ];
int nNew = 0;
const u8 *pNew = 0;
p1->xSetOutputs(p1, pSeg);
if( p->rc ) break;
if( bNewTerm ){
nNew = pSeg->term.n;
pNew = pSeg->term.p;
if( nNew<nToken || memcmp(pToken, pNew, nToken) ) break;
}
xVisit(p, pCtx, p1, pNew, nNew);
}
fts5MultiIterFree(p1);
fts5StructureRelease(pStruct);
return p->rc;
}
/*
** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an
** array of these for each row it visits (so all iRowid fields are the same).
** Or, for an iterator used by an "ORDER BY rank" query, it accumulates an
** array of these for the entire query (in which case iRowid fields may take
** a variety of values).
**
** Each instance in the array indicates the iterator (and therefore term)
** associated with position iPos of rowid iRowid. This is used by the
** xInstToken() API.
**
** iRowid:
** Rowid for the current entry.
**
** iPos:
** Position of current entry within row. In the usual ((iCol<<32)+iOff)
** format (e.g. see macros FTS5_POS2COLUMN() and FTS5_POS2OFFSET()).
**
** iIter:
** If the Fts5TokenDataIter iterator that the entry is part of is
** actually an iterator (i.e. with nIter>0, not just a container for
** Fts5TokenDataMap structures), then this variable is an index into
** the apIter[] array. The corresponding term is that which the iterator
** at apIter[iIter] currently points to.
**
** Or, if the Fts5TokenDataIter iterator is just a container object
** (nIter==0), then iIter is an index into the term.p[] buffer where
** the term is stored.
**
** nByte:
** In the case where iIter is an index into term.p[], this variable
** is the size of the term in bytes. If iIter is an index into apIter[],
** this variable is unused.
*/
struct Fts5TokenDataMap {
i64 iRowid; /* Row this token is located in */
i64 iPos; /* Position of token */
int iIter; /* Iterator token was read from */
int nByte; /* Length of token in bytes (or 0) */
};
/*
** An object used to supplement Fts5Iter for tokendata=1 iterators.
**
** This object serves two purposes. The first is as a container for an array
** of Fts5TokenDataMap structures, which are used to find the token required
** when the xInstToken() API is used. This is done by the nMapAlloc, nMap and
** aMap[] variables.
*/
struct Fts5TokenDataIter {
int nMapAlloc; /* Allocated size of aMap[] in entries */
int nMap; /* Number of valid entries in aMap[] */
Fts5TokenDataMap *aMap; /* Array of (rowid+pos -> token) mappings */
/* The following are used for prefix-queries only. */
Fts5Buffer terms;
/* The following are used for other full-token tokendata queries only. */
int nIter;
int nIterAlloc;
Fts5PoslistReader *aPoslistReader;
int *aPoslistToIter;
Fts5Iter *apIter[1];
};
/*
** The two input arrays - a1[] and a2[] - are in sorted order. This function
** merges the two arrays together and writes the result to output array
** aOut[]. aOut[] is guaranteed to be large enough to hold the result.
**
** Duplicate entries are copied into the output. So the size of the output
** array is always (n1+n2) entries.
*/
static void fts5TokendataMerge(
Fts5TokenDataMap *a1, int n1, /* Input array 1 */
Fts5TokenDataMap *a2, int n2, /* Input array 2 */
Fts5TokenDataMap *aOut /* Output array */
){
int i1 = 0;
int i2 = 0;
assert( n1>=0 && n2>=0 );
while( i1<n1 || i2<n2 ){
Fts5TokenDataMap *pOut = &aOut[i1+i2];
if( i2>=n2 || (i1<n1 && (
a1[i1].iRowid<a2[i2].iRowid
|| (a1[i1].iRowid==a2[i2].iRowid && a1[i1].iPos<=a2[i2].iPos)
))){
memcpy(pOut, &a1[i1], sizeof(Fts5TokenDataMap));
i1++;
}else{
memcpy(pOut, &a2[i2], sizeof(Fts5TokenDataMap));
i2++;
}
}
}
/*
** Append a mapping to the token-map belonging to object pT.
*/
static void fts5TokendataIterAppendMap(
Fts5Index *p,
Fts5TokenDataIter *pT,
int iIter,
int nByte,
i64 iRowid,
i64 iPos
){
if( p->rc==SQLITE_OK ){
if( pT->nMap==pT->nMapAlloc ){
int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64;
int nAlloc = nNew * sizeof(Fts5TokenDataMap);
Fts5TokenDataMap *aNew;
aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nAlloc);
if( aNew==0 ){
p->rc = SQLITE_NOMEM;
return;
}
pT->aMap = aNew;
pT->nMapAlloc = nNew;
}
pT->aMap[pT->nMap].iRowid = iRowid;
pT->aMap[pT->nMap].iPos = iPos;
pT->aMap[pT->nMap].iIter = iIter;
pT->aMap[pT->nMap].nByte = nByte;
pT->nMap++;
}
}
/*
** Sort the contents of the pT->aMap[] array.
**
** The sorting algorithm requries a malloc(). If this fails, an error code
** is left in Fts5Index.rc before returning.
*/
static void fts5TokendataIterSortMap(Fts5Index *p, Fts5TokenDataIter *pT){
Fts5TokenDataMap *aTmp = 0;
int nByte = pT->nMap * sizeof(Fts5TokenDataMap);
aTmp = (Fts5TokenDataMap*)sqlite3Fts5MallocZero(&p->rc, nByte);
if( aTmp ){
Fts5TokenDataMap *a1 = pT->aMap;
Fts5TokenDataMap *a2 = aTmp;
i64 nHalf;
for(nHalf=1; nHalf<pT->nMap; nHalf=nHalf*2){
int i1;
for(i1=0; i1<pT->nMap; i1+=(nHalf*2)){
int n1 = MIN(nHalf, pT->nMap-i1);
int n2 = MIN(nHalf, pT->nMap-i1-n1);
fts5TokendataMerge(&a1[i1], n1, &a1[i1+n1], n2, &a2[i1]);
}
SWAPVAL(Fts5TokenDataMap*, a1, a2);
}
if( a1!=pT->aMap ){
memcpy(pT->aMap, a1, pT->nMap*sizeof(Fts5TokenDataMap));
}
sqlite3_free(aTmp);
#ifdef SQLITE_DEBUG
{
int ii;
for(ii=1; ii<pT->nMap; ii++){
Fts5TokenDataMap *p1 = &pT->aMap[ii-1];
Fts5TokenDataMap *p2 = &pT->aMap[ii];
assert( p1->iRowid<p2->iRowid
|| (p1->iRowid==p2->iRowid && p1->iPos<=p2->iPos)
);
}
}
#endif
}
}
/*
** Delete an Fts5TokenDataIter structure and its contents.
*/
static void fts5TokendataIterDelete(Fts5TokenDataIter *pSet){
if( pSet ){
int ii;
for(ii=0; ii<pSet->nIter; ii++){
fts5MultiIterFree(pSet->apIter[ii]);
}
fts5BufferFree(&pSet->terms);
sqlite3_free(pSet->aPoslistReader);
sqlite3_free(pSet->aMap);
sqlite3_free(pSet);
}
}
/*
** fts5VisitEntries() context object used by fts5SetupPrefixIterTokendata()
** to pass data to prefixIterSetupTokendataCb().
*/
typedef struct TokendataSetupCtx TokendataSetupCtx;
struct TokendataSetupCtx {
Fts5TokenDataIter *pT; /* Object being populated with mappings */
int iTermOff; /* Offset of current term in terms.p[] */
int nTermByte; /* Size of current term in bytes */
};
/*
** fts5VisitEntries() callback used by fts5SetupPrefixIterTokendata(). This
** callback adds an entry to the Fts5TokenDataIter.aMap[] array for each
** position in the current position-list. It doesn't matter that some of
** these may be out of order - they will be sorted later.
*/
static void prefixIterSetupTokendataCb(
Fts5Index *p,
void *pCtx,
Fts5Iter *p1,
const u8 *pNew,
int nNew
){
TokendataSetupCtx *pSetup = (TokendataSetupCtx*)pCtx;
int iPosOff = 0;
i64 iPos = 0;
if( pNew ){
pSetup->nTermByte = nNew-1;
pSetup->iTermOff = pSetup->pT->terms.n;
fts5BufferAppendBlob(&p->rc, &pSetup->pT->terms, nNew-1, pNew+1);
}
while( 0==sqlite3Fts5PoslistNext64(
p1->base.pData, p1->base.nData, &iPosOff, &iPos
) ){
fts5TokendataIterAppendMap(p,
pSetup->pT, pSetup->iTermOff, pSetup->nTermByte, p1->base.iRowid, iPos
);
}
}
/*
** Context object passed by fts5SetupPrefixIter() to fts5VisitEntries().
*/
typedef struct PrefixSetupCtx PrefixSetupCtx;
struct PrefixSetupCtx {
void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*);
void (*xAppend)(Fts5Index*, u64, Fts5Iter*, Fts5Buffer*);
i64 iLastRowid;
int nMerge;
Fts5Buffer *aBuf;
int nBuf;
Fts5Buffer doclist;
TokendataSetupCtx *pTokendata;
};
/*
** fts5VisitEntries() callback used by fts5SetupPrefixIter()
*/
static void prefixIterSetupCb(
Fts5Index *p,
void *pCtx,
Fts5Iter *p1,
const u8 *pNew,
int nNew
){
PrefixSetupCtx *pSetup = (PrefixSetupCtx*)pCtx;
const int nMerge = pSetup->nMerge;
if( p1->base.nData>0 ){
if( p1->base.iRowid<=pSetup->iLastRowid && pSetup->doclist.n>0 ){
int i;
for(i=0; p->rc==SQLITE_OK && pSetup->doclist.n; i++){
int i1 = i*nMerge;
int iStore;
assert( i1+nMerge<=pSetup->nBuf );
for(iStore=i1; iStore<i1+nMerge; iStore++){
if( pSetup->aBuf[iStore].n==0 ){
fts5BufferSwap(&pSetup->doclist, &pSetup->aBuf[iStore]);
fts5BufferZero(&pSetup->doclist);
break;
}
}
if( iStore==i1+nMerge ){
pSetup->xMerge(p, &pSetup->doclist, nMerge, &pSetup->aBuf[i1]);
for(iStore=i1; iStore<i1+nMerge; iStore++){
fts5BufferZero(&pSetup->aBuf[iStore]);
}
}
}
pSetup->iLastRowid = 0;
}
pSetup->xAppend(
p, (u64)p1->base.iRowid-(u64)pSetup->iLastRowid, p1, &pSetup->doclist
);
pSetup->iLastRowid = p1->base.iRowid;
}
if( pSetup->pTokendata ){
prefixIterSetupTokendataCb(p, (void*)pSetup->pTokendata, p1, pNew, nNew);
}
}
static void fts5SetupPrefixIter(
Fts5Index *p, /* Index to read from */
int bDesc, /* True for "ORDER BY rowid DESC" */
int iIdx, /* Index to scan for data */
u8 *pToken, /* Buffer containing prefix to match */
int nToken, /* Size of buffer pToken in bytes */
Fts5Colset *pColset, /* Restrict matches to these columns */
Fts5Iter **ppIter /* OUT: New iterator */
){
Fts5Structure *pStruct;
PrefixSetupCtx s;
TokendataSetupCtx s2;
memset(&s, 0, sizeof(s));
memset(&s2, 0, sizeof(s2));
s.nMerge = 1;
s.iLastRowid = 0;
s.nBuf = 32;
if( iIdx==0
&& p->pConfig->eDetail==FTS5_DETAIL_FULL
&& p->pConfig->bPrefixInsttoken
){
s.pTokendata = &s2;
s2.pT = (Fts5TokenDataIter*)fts5IdxMalloc(p, sizeof(*s2.pT));
}
if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
s.xMerge = fts5MergeRowidLists;
s.xAppend = fts5AppendRowid;
}else{
s.nMerge = FTS5_MERGE_NLIST-1;
s.nBuf = s.nMerge*8; /* Sufficient to merge (16^8)==(2^32) lists */
s.xMerge = fts5MergePrefixLists;
s.xAppend = fts5AppendPoslist;
}
s.aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*s.nBuf);
pStruct = fts5StructureRead(p);
assert( p->rc!=SQLITE_OK || (s.aBuf && pStruct) );
if( p->rc==SQLITE_OK ){
void *pCtx = (void*)&s;
int i;
Fts5Data *pData;
/* If iIdx is non-zero, then it is the number of a prefix-index for
** prefixes 1 character longer than the prefix being queried for. That
** index contains all the doclists required, except for the one
** corresponding to the prefix itself. That one is extracted from the
** main term index here. */
if( iIdx!=0 ){
pToken[0] = FTS5_MAIN_PREFIX;
fts5VisitEntries(p, pColset, pToken, nToken, 0, prefixIterSetupCb, pCtx);
}
pToken[0] = FTS5_MAIN_PREFIX + iIdx;
fts5VisitEntries(p, pColset, pToken, nToken, 1, prefixIterSetupCb, pCtx);
assert( (s.nBuf%s.nMerge)==0 );
for(i=0; i<s.nBuf; i+=s.nMerge){
int iFree;
if( p->rc==SQLITE_OK ){
s.xMerge(p, &s.doclist, s.nMerge, &s.aBuf[i]);
}
for(iFree=i; iFree<i+s.nMerge; iFree++){
fts5BufferFree(&s.aBuf[iFree]);
}
}
pData = fts5IdxMalloc(p, sizeof(*pData)+s.doclist.n+FTS5_DATA_ZERO_PADDING);
assert( pData!=0 || p->rc!=SQLITE_OK );
if( pData ){
pData->p = (u8*)&pData[1];
pData->nn = pData->szLeaf = s.doclist.n;
if( s.doclist.n ) memcpy(pData->p, s.doclist.p, s.doclist.n);
fts5MultiIterNew2(p, pData, bDesc, ppIter);
}
assert( (*ppIter)!=0 || p->rc!=SQLITE_OK );
if( p->rc==SQLITE_OK && s.pTokendata ){
fts5TokendataIterSortMap(p, s2.pT);
(*ppIter)->pTokenDataIter = s2.pT;
s2.pT = 0;
}
}
fts5TokendataIterDelete(s2.pT);
fts5BufferFree(&s.doclist);
fts5StructureRelease(pStruct);
sqlite3_free(s.aBuf);
}
/*
** Indicate that all subsequent calls to sqlite3Fts5IndexWrite() pertain
** to the document with rowid iRowid.
*/
|
| ︙ | ︙ | |||
248813 248814 248815 248816 248817 248818 248819 |
** Ensure the segment-iterator passed as the only argument points to EOF.
*/
static void fts5SegIterSetEOF(Fts5SegIter *pSeg){
fts5DataRelease(pSeg->pLeaf);
pSeg->pLeaf = 0;
}
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 249354 249355 249356 249357 249358 249359 249360 249361 249362 249363 249364 249365 249366 249367 |
** Ensure the segment-iterator passed as the only argument points to EOF.
*/
static void fts5SegIterSetEOF(Fts5SegIter *pSeg){
fts5DataRelease(pSeg->pLeaf);
pSeg->pLeaf = 0;
}
/*
** This function appends iterator pAppend to Fts5TokenDataIter pIn and
** returns the result.
*/
static Fts5TokenDataIter *fts5AppendTokendataIter(
Fts5Index *p, /* Index object (for error code) */
Fts5TokenDataIter *pIn, /* Current Fts5TokenDataIter struct */
|
| ︙ | ︙ | |||
248881 248882 248883 248884 248885 248886 248887 |
pRet->apIter[pRet->nIter++] = pAppend;
}
assert( pRet==0 || pRet->nIter<=pRet->nIterAlloc );
return pRet;
}
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 249390 249391 249392 249393 249394 249395 249396 249397 249398 249399 249400 249401 249402 249403 |
pRet->apIter[pRet->nIter++] = pAppend;
}
assert( pRet==0 || pRet->nIter<=pRet->nIterAlloc );
return pRet;
}
/*
** The iterator passed as the only argument must be a tokendata=1 iterator
** (pIter->pTokenDataIter!=0). This function sets the iterator output
** variables (pIter->base.*) according to the contents of the current
** row.
*/
static void fts5IterSetOutputsTokendata(Fts5Iter *pIter){
|
| ︙ | ︙ | |||
248969 248970 248971 248972 248973 248974 248975 |
pIter->base.bEof = 1;
}else{
int eDetail = pIter->pIndex->pConfig->eDetail;
pIter->base.bEof = 0;
pIter->base.iRowid = iRowid;
if( nHit==1 && eDetail==FTS5_DETAIL_FULL ){
| | | 249430 249431 249432 249433 249434 249435 249436 249437 249438 249439 249440 249441 249442 249443 249444 |
pIter->base.bEof = 1;
}else{
int eDetail = pIter->pIndex->pConfig->eDetail;
pIter->base.bEof = 0;
pIter->base.iRowid = iRowid;
if( nHit==1 && eDetail==FTS5_DETAIL_FULL ){
fts5TokendataIterAppendMap(pIter->pIndex, pT, iMin, 0, iRowid, -1);
}else
if( nHit>1 && eDetail!=FTS5_DETAIL_NONE ){
int nReader = 0;
int nByte = 0;
i64 iPrev = 0;
/* Allocate array of iterators if they are not already allocated. */
|
| ︙ | ︙ | |||
249222 249223 249224 249225 249226 249227 249228 249229 249230 249231 249232 249233 249234 249235 249236 249237 249238 249239 249240 249241 249242 |
}
}
if( p->rc==SQLITE_OK ){
pRet = fts5MultiIterAlloc(p, 0);
}
if( pRet ){
pRet->pTokenDataIter = pSet;
if( pSet ){
fts5IterSetOutputsTokendata(pRet);
}else{
pRet->base.bEof = 1;
}
}else{
fts5TokendataIterDelete(pSet);
}
fts5StructureRelease(pStruct);
fts5BufferFree(&bSeek);
return pRet;
}
| > < | 249683 249684 249685 249686 249687 249688 249689 249690 249691 249692 249693 249694 249695 249696 249697 249698 249699 249700 249701 249702 249703 249704 249705 249706 249707 249708 249709 249710 249711 |
}
}
if( p->rc==SQLITE_OK ){
pRet = fts5MultiIterAlloc(p, 0);
}
if( pRet ){
pRet->nSeg = 0;
pRet->pTokenDataIter = pSet;
if( pSet ){
fts5IterSetOutputsTokendata(pRet);
}else{
pRet->base.bEof = 1;
}
}else{
fts5TokendataIterDelete(pSet);
}
fts5StructureRelease(pStruct);
fts5BufferFree(&bSeek);
return pRet;
}
/*
** Open a new iterator to iterate though all rowid that match the
** specified token or token prefix.
*/
static int sqlite3Fts5IndexQuery(
Fts5Index *p, /* FTS index to query */
|
| ︙ | ︙ | |||
249260 249261 249262 249263 249264 249265 249266 249267 249268 249269 249270 249271 249272 249273 249274 249275 |
/* If the QUERY_SCAN flag is set, all other flags must be clear. */
assert( (flags & FTS5INDEX_QUERY_SCAN)==0 || flags==FTS5INDEX_QUERY_SCAN );
if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){
int iIdx = 0; /* Index to search */
int iPrefixIdx = 0; /* +1 prefix index */
int bTokendata = pConfig->bTokendata;
if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken);
if( flags & (FTS5INDEX_QUERY_NOTOKENDATA|FTS5INDEX_QUERY_SCAN) ){
bTokendata = 0;
}
/* Figure out which index to search and set iIdx accordingly. If this
** is a prefix query for which there is no prefix index, set iIdx to
** greater than pConfig->nPrefix to indicate that the query will be
| > > > > > > | 249721 249722 249723 249724 249725 249726 249727 249728 249729 249730 249731 249732 249733 249734 249735 249736 249737 249738 249739 249740 249741 249742 |
/* If the QUERY_SCAN flag is set, all other flags must be clear. */
assert( (flags & FTS5INDEX_QUERY_SCAN)==0 || flags==FTS5INDEX_QUERY_SCAN );
if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){
int iIdx = 0; /* Index to search */
int iPrefixIdx = 0; /* +1 prefix index */
int bTokendata = pConfig->bTokendata;
assert( buf.p!=0 );
if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken);
/* The NOTOKENDATA flag is set when each token in a tokendata=1 table
** should be treated individually, instead of merging all those with
** a common prefix into a single entry. This is used, for example, by
** queries performed as part of an integrity-check, or by the fts5vocab
** module. */
if( flags & (FTS5INDEX_QUERY_NOTOKENDATA|FTS5INDEX_QUERY_SCAN) ){
bTokendata = 0;
}
/* Figure out which index to search and set iIdx accordingly. If this
** is a prefix query for which there is no prefix index, set iIdx to
** greater than pConfig->nPrefix to indicate that the query will be
|
| ︙ | ︙ | |||
249292 249293 249294 249295 249296 249297 249298 |
int nIdxChar = pConfig->aPrefix[iIdx-1];
if( nIdxChar==nChar ) break;
if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx;
}
}
if( bTokendata && iIdx==0 ){
| | | | 249759 249760 249761 249762 249763 249764 249765 249766 249767 249768 249769 249770 249771 249772 249773 249774 249775 249776 249777 249778 249779 249780 249781 249782 249783 249784 249785 249786 |
int nIdxChar = pConfig->aPrefix[iIdx-1];
if( nIdxChar==nChar ) break;
if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx;
}
}
if( bTokendata && iIdx==0 ){
buf.p[0] = FTS5_MAIN_PREFIX;
pRet = fts5SetupTokendataIter(p, buf.p, nToken+1, pColset);
}else if( iIdx<=pConfig->nPrefix ){
/* Straight index lookup */
Fts5Structure *pStruct = fts5StructureRead(p);
buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx);
if( pStruct ){
fts5MultiIterNew(p, pStruct, flags | FTS5INDEX_QUERY_SKIPEMPTY,
pColset, buf.p, nToken+1, -1, 0, &pRet
);
fts5StructureRelease(pStruct);
}
}else{
/* Scan multiple terms in the main index for a prefix query. */
int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0;
fts5SetupPrefixIter(p, bDesc, iPrefixIdx, buf.p, nToken+1, pColset,&pRet);
if( pRet==0 ){
assert( p->rc!=SQLITE_OK );
}else{
assert( pRet->pColset==0 );
fts5IterSetOutputCb(&p->rc, pRet);
|
| ︙ | ︙ | |||
249341 249342 249343 249344 249345 249346 249347 |
*/
/*
** Move to the next matching rowid.
*/
static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
assert( pIter->pIndex->rc==SQLITE_OK );
| > | | 249808 249809 249810 249811 249812 249813 249814 249815 249816 249817 249818 249819 249820 249821 249822 249823 |
*/
/*
** Move to the next matching rowid.
*/
static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
assert( pIter->pIndex->rc==SQLITE_OK );
if( pIter->nSeg==0 ){
assert( pIter->pTokenDataIter );
fts5TokendataIterNext(pIter, 0, 0);
}else{
fts5MultiIterNext(pIter->pIndex, pIter, 0, 0);
}
return fts5IndexReturn(pIter->pIndex);
}
|
| ︙ | ︙ | |||
249378 249379 249380 249381 249382 249383 249384 |
/*
** Move to the next matching rowid that occurs at or after iMatch. The
** definition of "at or after" depends on whether this iterator iterates
** in ascending or descending rowid order.
*/
static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
| > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | | > > > > > > > > > > | 249846 249847 249848 249849 249850 249851 249852 249853 249854 249855 249856 249857 249858 249859 249860 249861 249862 249863 249864 249865 249866 249867 249868 249869 249870 249871 249872 249873 249874 249875 249876 249877 249878 249879 249880 249881 249882 249883 249884 249885 249886 249887 249888 249889 249890 249891 249892 249893 249894 249895 249896 249897 249898 249899 249900 249901 249902 249903 249904 249905 249906 249907 249908 249909 249910 249911 249912 249913 249914 249915 249916 249917 249918 249919 249920 249921 249922 249923 249924 249925 249926 249927 249928 249929 249930 249931 249932 249933 249934 249935 249936 249937 249938 249939 249940 249941 249942 249943 249944 249945 249946 249947 249948 249949 249950 249951 249952 249953 249954 249955 249956 |
/*
** Move to the next matching rowid that occurs at or after iMatch. The
** definition of "at or after" depends on whether this iterator iterates
** in ascending or descending rowid order.
*/
static int sqlite3Fts5IterNextFrom(Fts5IndexIter *pIndexIter, i64 iMatch){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
if( pIter->nSeg==0 ){
assert( pIter->pTokenDataIter );
fts5TokendataIterNext(pIter, 1, iMatch);
}else{
fts5MultiIterNextFrom(pIter->pIndex, pIter, iMatch);
}
return fts5IndexReturn(pIter->pIndex);
}
/*
** Return the current term.
*/
static const char *sqlite3Fts5IterTerm(Fts5IndexIter *pIndexIter, int *pn){
int n;
const char *z = (const char*)fts5MultiIterTerm((Fts5Iter*)pIndexIter, &n);
assert_nc( z || n<=1 );
*pn = n-1;
return (z ? &z[1] : 0);
}
/*
** pIter is a prefix query. This function populates pIter->pTokenDataIter
** with an Fts5TokenDataIter object containing mappings for all rows
** matched by the query.
*/
static int fts5SetupPrefixIterTokendata(
Fts5Iter *pIter,
const char *pToken, /* Token prefix to search for */
int nToken /* Size of pToken in bytes */
){
Fts5Index *p = pIter->pIndex;
Fts5Buffer token = {0, 0, 0};
TokendataSetupCtx ctx;
memset(&ctx, 0, sizeof(ctx));
fts5BufferGrow(&p->rc, &token, nToken+1);
assert( token.p!=0 || p->rc!=SQLITE_OK );
ctx.pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*ctx.pT));
if( p->rc==SQLITE_OK ){
/* Fill in the token prefix to search for */
token.p[0] = FTS5_MAIN_PREFIX;
memcpy(&token.p[1], pToken, nToken);
token.n = nToken+1;
fts5VisitEntries(
p, 0, token.p, token.n, 1, prefixIterSetupTokendataCb, (void*)&ctx
);
fts5TokendataIterSortMap(p, ctx.pT);
}
if( p->rc==SQLITE_OK ){
pIter->pTokenDataIter = ctx.pT;
}else{
fts5TokendataIterDelete(ctx.pT);
}
fts5BufferFree(&token);
return fts5IndexReturn(p);
}
/*
** This is used by xInstToken() to access the token at offset iOff, column
** iCol of row iRowid. The token is returned via output variables *ppOut
** and *pnOut. The iterator passed as the first argument must be a tokendata=1
** iterator (pIter->pTokenDataIter!=0).
**
** pToken/nToken:
*/
static int sqlite3Fts5IterToken(
Fts5IndexIter *pIndexIter,
const char *pToken, int nToken,
i64 iRowid,
int iCol,
int iOff,
const char **ppOut, int *pnOut
){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
Fts5TokenDataIter *pT = pIter->pTokenDataIter;
i64 iPos = (((i64)iCol)<<32) + iOff;
Fts5TokenDataMap *aMap = 0;
int i1 = 0;
int i2 = 0;
int iTest = 0;
assert( pT || (pToken && pIter->nSeg>0) );
if( pT==0 ){
int rc = fts5SetupPrefixIterTokendata(pIter, pToken, nToken);
if( rc!=SQLITE_OK ) return rc;
pT = pIter->pTokenDataIter;
}
i2 = pT->nMap;
aMap = pT->aMap;
while( i2>i1 ){
iTest = (i1 + i2) / 2;
if( aMap[iTest].iRowid<iRowid ){
i1 = iTest+1;
}else if( aMap[iTest].iRowid>iRowid ){
|
| ︙ | ︙ | |||
249441 249442 249443 249444 249445 249446 249447 |
}else{
break;
}
}
}
if( i2>i1 ){
| > | | | > > > > > | > > | | > > > > > | > > > > > > | | | | | | > | 249966 249967 249968 249969 249970 249971 249972 249973 249974 249975 249976 249977 249978 249979 249980 249981 249982 249983 249984 249985 249986 249987 249988 249989 249990 249991 249992 249993 249994 249995 249996 249997 249998 249999 250000 250001 250002 250003 250004 250005 250006 250007 250008 250009 250010 250011 250012 250013 250014 250015 250016 250017 250018 250019 250020 250021 250022 250023 250024 250025 250026 250027 250028 250029 250030 250031 250032 250033 250034 250035 250036 250037 250038 250039 250040 250041 250042 250043 250044 |
}else{
break;
}
}
}
if( i2>i1 ){
if( pIter->nSeg==0 ){
Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter];
*ppOut = (const char*)pMap->aSeg[0].term.p+1;
*pnOut = pMap->aSeg[0].term.n-1;
}else{
Fts5TokenDataMap *p = &aMap[iTest];
*ppOut = (const char*)&pT->terms.p[p->iIter];
*pnOut = aMap[iTest].nByte;
}
}
return SQLITE_OK;
}
/*
** Clear any existing entries from the token-map associated with the
** iterator passed as the only argument.
*/
static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter *pIndexIter){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
if( pIter && pIter->pTokenDataIter
&& (pIter->nSeg==0 || pIter->pIndex->pConfig->eDetail!=FTS5_DETAIL_FULL)
){
pIter->pTokenDataIter->nMap = 0;
}
}
/*
** Set a token-mapping for the iterator passed as the first argument. This
** is used in detail=column or detail=none mode when a token is requested
** using the xInstToken() API. In this case the caller tokenizers the
** current row and configures the token-mapping via multiple calls to this
** function.
*/
static int sqlite3Fts5IndexIterWriteTokendata(
Fts5IndexIter *pIndexIter,
const char *pToken, int nToken,
i64 iRowid, int iCol, int iOff
){
Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
Fts5TokenDataIter *pT = pIter->pTokenDataIter;
Fts5Index *p = pIter->pIndex;
i64 iPos = (((i64)iCol)<<32) + iOff;
assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL );
assert( pIter->pTokenDataIter || pIter->nSeg>0 );
if( pIter->nSeg>0 ){
/* This is a prefix term iterator. */
if( pT==0 ){
pT = (Fts5TokenDataIter*)sqlite3Fts5MallocZero(&p->rc, sizeof(*pT));
pIter->pTokenDataIter = pT;
}
if( pT ){
fts5TokendataIterAppendMap(p, pT, pT->terms.n, nToken, iRowid, iPos);
fts5BufferAppendBlob(&p->rc, &pT->terms, nToken, (const u8*)pToken);
}
}else{
int ii;
for(ii=0; ii<pT->nIter; ii++){
Fts5Buffer *pTerm = &pT->apIter[ii]->aSeg[0].term;
if( nToken==pTerm->n-1 && memcmp(pToken, pTerm->p+1, nToken)==0 ) break;
}
if( ii<pT->nIter ){
fts5TokendataIterAppendMap(p, pT, ii, 0, iRowid, iPos);
}
}
return fts5IndexReturn(p);
}
/*
** Close an iterator opened by an earlier call to sqlite3Fts5IndexQuery().
*/
|
| ︙ | ︙ | |||
251390 251391 251392 251393 251394 251395 251396 251397 251398 251399 251400 251401 251402 251403 |
/*
** Size of header on fts5_locale() values. And macro to access a buffer
** containing a copy of the header from an Fts5Config pointer.
*/
#define FTS5_LOCALE_HDR_SIZE ((int)sizeof( ((Fts5Global*)0)->aLocaleHdr ))
#define FTS5_LOCALE_HDR(pConfig) ((const u8*)(pConfig->pGlobal->aLocaleHdr))
/*
** Each auxiliary function registered with the FTS5 module is represented
** by an object of the following type. All such objects are stored as part
** of the Fts5Global.pAux list.
*/
struct Fts5Auxiliary {
| > | 251935 251936 251937 251938 251939 251940 251941 251942 251943 251944 251945 251946 251947 251948 251949 |
/*
** Size of header on fts5_locale() values. And macro to access a buffer
** containing a copy of the header from an Fts5Config pointer.
*/
#define FTS5_LOCALE_HDR_SIZE ((int)sizeof( ((Fts5Global*)0)->aLocaleHdr ))
#define FTS5_LOCALE_HDR(pConfig) ((const u8*)(pConfig->pGlobal->aLocaleHdr))
#define FTS5_INSTTOKEN_SUBTYPE 73
/*
** Each auxiliary function registered with the FTS5 module is represented
** by an object of the following type. All such objects are stored as part
** of the Fts5Global.pAux list.
*/
struct Fts5Auxiliary {
|
| ︙ | ︙ | |||
251929 251930 251931 251932 251933 251934 251935 251936 251937 251938 251939 251940 251941 251942 |
if( p->op==SQLITE_INDEX_CONSTRAINT_MATCH
|| (p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol>=nCol)
){
/* A MATCH operator or equivalent */
if( p->usable==0 || iCol<0 ){
/* As there exists an unusable MATCH constraint this is an
** unusable plan. Return SQLITE_CONSTRAINT. */
return SQLITE_CONSTRAINT;
}else{
if( iCol==nCol+1 ){
if( bSeenRank ) continue;
idxStr[iIdxStr++] = 'r';
bSeenRank = 1;
}else{
| > | 252475 252476 252477 252478 252479 252480 252481 252482 252483 252484 252485 252486 252487 252488 252489 |
if( p->op==SQLITE_INDEX_CONSTRAINT_MATCH
|| (p->op==SQLITE_INDEX_CONSTRAINT_EQ && iCol>=nCol)
){
/* A MATCH operator or equivalent */
if( p->usable==0 || iCol<0 ){
/* As there exists an unusable MATCH constraint this is an
** unusable plan. Return SQLITE_CONSTRAINT. */
idxStr[iIdxStr] = 0;
return SQLITE_CONSTRAINT;
}else{
if( iCol==nCol+1 ){
if( bSeenRank ) continue;
idxStr[iIdxStr++] = 'r';
bSeenRank = 1;
}else{
|
| ︙ | ︙ | |||
252714 252715 252716 252717 252718 252719 252720 252721 252722 252723 252724 252725 252726 252727 |
int bOrderByRank; /* True if ORDER BY rank */
sqlite3_value *pRank = 0; /* rank MATCH ? expression (or NULL) */
sqlite3_value *pRowidEq = 0; /* rowid = ? expression (or NULL) */
sqlite3_value *pRowidLe = 0; /* rowid <= ? expression (or NULL) */
sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */
int iCol; /* Column on LHS of MATCH operator */
char **pzErrmsg = pConfig->pzErrmsg;
int i;
int iIdxStr = 0;
Fts5Expr *pExpr = 0;
assert( pConfig->bLock==0 );
if( pCsr->ePlan ){
fts5FreeCursorComponents(pCsr);
| > | 253261 253262 253263 253264 253265 253266 253267 253268 253269 253270 253271 253272 253273 253274 253275 |
int bOrderByRank; /* True if ORDER BY rank */
sqlite3_value *pRank = 0; /* rank MATCH ? expression (or NULL) */
sqlite3_value *pRowidEq = 0; /* rowid = ? expression (or NULL) */
sqlite3_value *pRowidLe = 0; /* rowid <= ? expression (or NULL) */
sqlite3_value *pRowidGe = 0; /* rowid >= ? expression (or NULL) */
int iCol; /* Column on LHS of MATCH operator */
char **pzErrmsg = pConfig->pzErrmsg;
int bPrefixInsttoken = pConfig->bPrefixInsttoken;
int i;
int iIdxStr = 0;
Fts5Expr *pExpr = 0;
assert( pConfig->bLock==0 );
if( pCsr->ePlan ){
fts5FreeCursorComponents(pCsr);
|
| ︙ | ︙ | |||
252749 252750 252751 252752 252753 252754 252755 252756 252757 252758 252759 252760 252761 252762 |
char *zText = 0;
int bFreeAndReset = 0;
int bInternal = 0;
rc = fts5ExtractExprText(pConfig, apVal[i], &zText, &bFreeAndReset);
if( rc!=SQLITE_OK ) goto filter_out;
if( zText==0 ) zText = "";
iCol = 0;
do{
iCol = iCol*10 + (idxStr[iIdxStr]-'0');
iIdxStr++;
}while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' );
| > > > | 253297 253298 253299 253300 253301 253302 253303 253304 253305 253306 253307 253308 253309 253310 253311 253312 253313 |
char *zText = 0;
int bFreeAndReset = 0;
int bInternal = 0;
rc = fts5ExtractExprText(pConfig, apVal[i], &zText, &bFreeAndReset);
if( rc!=SQLITE_OK ) goto filter_out;
if( zText==0 ) zText = "";
if( sqlite3_value_subtype(apVal[i])==FTS5_INSTTOKEN_SUBTYPE ){
pConfig->bPrefixInsttoken = 1;
}
iCol = 0;
do{
iCol = iCol*10 + (idxStr[iIdxStr]-'0');
iIdxStr++;
}while( idxStr[iIdxStr]>='0' && idxStr[iIdxStr]<='9' );
|
| ︙ | ︙ | |||
252889 252890 252891 252892 252893 252894 252895 252896 252897 252898 252899 252900 252901 252902 |
rc = fts5NextMethod(pCursor);
}
}
filter_out:
sqlite3Fts5ExprFree(pExpr);
pConfig->pzErrmsg = pzErrmsg;
return rc;
}
/*
** This is the xEof method of the virtual table. SQLite calls this
** routine to find out if it has reached the end of a result set.
*/
| > | 253440 253441 253442 253443 253444 253445 253446 253447 253448 253449 253450 253451 253452 253453 253454 |
rc = fts5NextMethod(pCursor);
}
}
filter_out:
sqlite3Fts5ExprFree(pExpr);
pConfig->pzErrmsg = pzErrmsg;
pConfig->bPrefixInsttoken = bPrefixInsttoken;
return rc;
}
/*
** This is the xEof method of the virtual table. SQLite calls this
** routine to find out if it has reached the end of a result set.
*/
|
| ︙ | ︙ | |||
254884 254885 254886 254887 254888 254889 254890 |
static void fts5SourceIdFunc(
sqlite3_context *pCtx, /* Function call context */
int nArg, /* Number of args */
sqlite3_value **apUnused /* Function arguments */
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
| | | 255436 255437 255438 255439 255440 255441 255442 255443 255444 255445 255446 255447 255448 255449 255450 |
static void fts5SourceIdFunc(
sqlite3_context *pCtx, /* Function call context */
int nArg, /* Number of args */
sqlite3_value **apUnused /* Function arguments */
){
assert( nArg==0 );
UNUSED_PARAM2(nArg, apUnused);
sqlite3_result_text(pCtx, "fts5: 2024-12-30 21:23:53 2b17bc49655c577029919c2d409de994b0d252f8efb5da1ba0913f2c96bee552", -1, SQLITE_TRANSIENT);
}
/*
** Implementation of fts5_locale(LOCALE, TEXT) function.
**
** If parameter LOCALE is NULL, or a zero-length string, then a copy of
** TEXT is returned. Otherwise, both LOCALE and TEXT are interpreted as
|
| ︙ | ︙ | |||
254947 254948 254949 254950 254951 254952 254953 254954 254955 254956 254957 254958 254959 254960 |
(*pCsr++) = 0x00;
if( zText ) memcpy(pCsr, zText, nText);
assert( &pCsr[nText]==&pBlob[nBlob] );
sqlite3_result_blob(pCtx, pBlob, nBlob, sqlite3_free);
}
}
/*
** Return true if zName is the extension on one of the shadow tables used
** by this module.
*/
static int fts5ShadowName(const char *zName){
static const char *azName[] = {
| > > > > > > > > > > > > > > | 255499 255500 255501 255502 255503 255504 255505 255506 255507 255508 255509 255510 255511 255512 255513 255514 255515 255516 255517 255518 255519 255520 255521 255522 255523 255524 255525 255526 |
(*pCsr++) = 0x00;
if( zText ) memcpy(pCsr, zText, nText);
assert( &pCsr[nText]==&pBlob[nBlob] );
sqlite3_result_blob(pCtx, pBlob, nBlob, sqlite3_free);
}
}
/*
** Implementation of fts5_insttoken() function.
*/
static void fts5InsttokenFunc(
sqlite3_context *pCtx, /* Function call context */
int nArg, /* Number of args */
sqlite3_value **apArg /* Function arguments */
){
assert( nArg==1 );
(void)nArg;
sqlite3_result_value(pCtx, apArg[0]);
sqlite3_result_subtype(pCtx, FTS5_INSTTOKEN_SUBTYPE);
}
/*
** Return true if zName is the extension on one of the shadow tables used
** by this module.
*/
static int fts5ShadowName(const char *zName){
static const char *azName[] = {
|
| ︙ | ︙ | |||
255077 255078 255079 255080 255081 255082 255083 |
SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
p, fts5SourceIdFunc, 0, 0
);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(
db, "fts5_locale", 2,
| | > > > > > > > | 255643 255644 255645 255646 255647 255648 255649 255650 255651 255652 255653 255654 255655 255656 255657 255658 255659 255660 255661 255662 255663 255664 255665 255666 |
SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
p, fts5SourceIdFunc, 0, 0
);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(
db, "fts5_locale", 2,
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE|SQLITE_SUBTYPE,
p, fts5LocaleFunc, 0, 0
);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(
db, "fts5_insttoken", 1,
SQLITE_UTF8|SQLITE_INNOCUOUS|SQLITE_RESULT_SUBTYPE,
p, fts5InsttokenFunc, 0, 0
);
}
}
/* If SQLITE_FTS5_ENABLE_TEST_MI is defined, assume that the file
** fts5_test_mi.c is compiled and linked into the executable. And call
** its entry point to enable the matchinfo() demo. */
#ifdef SQLITE_FTS5_ENABLE_TEST_MI
|
| ︙ | ︙ | |||
258005 258006 258007 258008 258009 258010 258011 |
){
TrigramTokenizer *p = (TrigramTokenizer*)pTok;
int rc = SQLITE_OK;
char aBuf[32];
char *zOut = aBuf;
int ii;
const unsigned char *zIn = (const unsigned char*)pText;
| | | > < > > > > < | 258578 258579 258580 258581 258582 258583 258584 258585 258586 258587 258588 258589 258590 258591 258592 258593 258594 258595 258596 258597 258598 258599 258600 258601 258602 258603 258604 258605 258606 258607 258608 258609 258610 258611 258612 258613 258614 258615 258616 258617 258618 258619 258620 258621 258622 258623 258624 258625 258626 258627 258628 |
){
TrigramTokenizer *p = (TrigramTokenizer*)pTok;
int rc = SQLITE_OK;
char aBuf[32];
char *zOut = aBuf;
int ii;
const unsigned char *zIn = (const unsigned char*)pText;
const unsigned char *zEof = (zIn ? &zIn[nText] : 0);
u32 iCode = 0;
int aStart[3]; /* Input offset of each character in aBuf[] */
UNUSED_PARAM(unusedFlags);
/* Populate aBuf[] with the characters for the first trigram. */
for(ii=0; ii<3; ii++){
do {
aStart[ii] = zIn - (const unsigned char*)pText;
if( zIn>=zEof ) return SQLITE_OK;
READ_UTF8(zIn, zEof, iCode);
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam);
}while( iCode==0 );
WRITE_UTF8(zOut, iCode);
}
/* At the start of each iteration of this loop:
**
** aBuf: Contains 3 characters. The 3 characters of the next trigram.
** zOut: Points to the byte following the last character in aBuf.
** aStart[3]: Contains the byte offset in the input text corresponding
** to the start of each of the three characters in the buffer.
*/
assert( zIn<=zEof );
while( 1 ){
int iNext; /* Start of character following current tri */
const char *z1;
/* Read characters from the input up until the first non-diacritic */
do {
iNext = zIn - (const unsigned char*)pText;
if( zIn>=zEof ){
iCode = 0;
break;
}
READ_UTF8(zIn, zEof, iCode);
if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, p->iFoldParam);
}while( iCode==0 );
/* Pass the current trigram back to fts5 */
rc = xToken(pCtx, 0, aBuf, zOut-aBuf, aStart[0], iNext);
if( iCode==0 || rc!=SQLITE_OK ) break;
|
| ︙ | ︙ | |||
260075 260076 260077 260078 260079 260080 260081 | }; void *p = (void*)pGlobal; return sqlite3_create_module_v2(db, "fts5vocab", &fts5Vocab, p, 0); } | | | 260651 260652 260653 260654 260655 260656 260657 260658 260659 260660 260661 260662 260663 260664 260665 | }; void *p = (void*)pGlobal; return sqlite3_create_module_v2(db, "fts5vocab", &fts5Vocab, p, 0); } /* Here ends the fts5.c composite file. */ #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) */ /************** End of fts5.c ************************************************/ /************** Begin file stmt.c ********************************************/ /* ** 2017-05-31 ** |
| ︙ | ︙ | |||
260431 260432 260433 260434 260435 260436 260437 260438 |
}
#endif /* SQLITE_CORE */
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
/************** End of stmt.c ************************************************/
/* Return the source-id for this library */
SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
/************************** End of sqlite3.c ******************************/
| > | 261007 261008 261009 261010 261011 261012 261013 261014 261015 |
}
#endif /* SQLITE_CORE */
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
/************** End of stmt.c ************************************************/
/* Return the source-id for this library */
SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
#endif /* SQLITE_AMALGAMATION */
/************************** End of sqlite3.c ******************************/
|
Changes to extsrc/sqlite3.h.
| ︙ | ︙ | |||
142 143 144 145 146 147 148 | ** been edited in any way since it was last checked in, then the last ** four hexadecimal digits of the hash may be modified. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ | | | | | 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | ** been edited in any way since it was last checked in, then the last ** four hexadecimal digits of the hash may be modified. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.48.0" #define SQLITE_VERSION_NUMBER 3048000 #define SQLITE_SOURCE_ID "2024-12-30 21:23:53 2b17bc49655c577029919c2d409de994b0d252f8efb5da1ba0913f2c96bee552" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros |
| ︙ | ︙ | |||
648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 | ** read-only media and cannot be changed even by processes with ** elevated privileges. ** ** The SQLITE_IOCAP_BATCH_ATOMIC property means that the underlying ** filesystem supports doing multiple write operations atomically when those ** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and ** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. */ #define SQLITE_IOCAP_ATOMIC 0x00000001 #define SQLITE_IOCAP_ATOMIC512 0x00000002 #define SQLITE_IOCAP_ATOMIC1K 0x00000004 #define SQLITE_IOCAP_ATOMIC2K 0x00000008 #define SQLITE_IOCAP_ATOMIC4K 0x00000010 #define SQLITE_IOCAP_ATOMIC8K 0x00000020 #define SQLITE_IOCAP_ATOMIC16K 0x00000040 #define SQLITE_IOCAP_ATOMIC32K 0x00000080 #define SQLITE_IOCAP_ATOMIC64K 0x00000100 #define SQLITE_IOCAP_SAFE_APPEND 0x00000200 #define SQLITE_IOCAP_SEQUENTIAL 0x00000400 #define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800 #define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 #define SQLITE_IOCAP_IMMUTABLE 0x00002000 #define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000 /* ** CAPI3REF: File Locking Levels ** ** SQLite uses one of these integer values as the second ** argument to calls it makes to the xLock() and xUnlock() methods ** of an [sqlite3_io_methods] object. These values are ordered from | > > > > > > > > | 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 | ** read-only media and cannot be changed even by processes with ** elevated privileges. ** ** The SQLITE_IOCAP_BATCH_ATOMIC property means that the underlying ** filesystem supports doing multiple write operations atomically when those ** write operations are bracketed by [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE] and ** [SQLITE_FCNTL_COMMIT_ATOMIC_WRITE]. ** ** The SQLITE_IOCAP_SUBPAGE_READ property means that it is ok to read ** from the database file in amounts that are not a multiple of the ** page size and that do not begin at a page boundary. Without this ** property, SQLite is careful to only do full-page reads and write ** on aligned pages, with the one exception that it will do a sub-page ** read of the first page to access the database header. */ #define SQLITE_IOCAP_ATOMIC 0x00000001 #define SQLITE_IOCAP_ATOMIC512 0x00000002 #define SQLITE_IOCAP_ATOMIC1K 0x00000004 #define SQLITE_IOCAP_ATOMIC2K 0x00000008 #define SQLITE_IOCAP_ATOMIC4K 0x00000010 #define SQLITE_IOCAP_ATOMIC8K 0x00000020 #define SQLITE_IOCAP_ATOMIC16K 0x00000040 #define SQLITE_IOCAP_ATOMIC32K 0x00000080 #define SQLITE_IOCAP_ATOMIC64K 0x00000100 #define SQLITE_IOCAP_SAFE_APPEND 0x00000200 #define SQLITE_IOCAP_SEQUENTIAL 0x00000400 #define SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN 0x00000800 #define SQLITE_IOCAP_POWERSAFE_OVERWRITE 0x00001000 #define SQLITE_IOCAP_IMMUTABLE 0x00002000 #define SQLITE_IOCAP_BATCH_ATOMIC 0x00004000 #define SQLITE_IOCAP_SUBPAGE_READ 0x00008000 /* ** CAPI3REF: File Locking Levels ** ** SQLite uses one of these integer values as the second ** argument to calls it makes to the xLock() and xUnlock() methods ** of an [sqlite3_io_methods] object. These values are ordered from |
| ︙ | ︙ | |||
810 811 812 813 814 815 816 817 818 819 820 821 822 823 | ** <li> [SQLITE_IOCAP_ATOMIC64K] ** <li> [SQLITE_IOCAP_SAFE_APPEND] ** <li> [SQLITE_IOCAP_SEQUENTIAL] ** <li> [SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN] ** <li> [SQLITE_IOCAP_POWERSAFE_OVERWRITE] ** <li> [SQLITE_IOCAP_IMMUTABLE] ** <li> [SQLITE_IOCAP_BATCH_ATOMIC] ** </ul> ** ** The SQLITE_IOCAP_ATOMIC property means that all writes of ** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values ** mean that writes of blocks that are nnn bytes in size and ** are aligned to an address which is an integer multiple of ** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means | > | 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 | ** <li> [SQLITE_IOCAP_ATOMIC64K] ** <li> [SQLITE_IOCAP_SAFE_APPEND] ** <li> [SQLITE_IOCAP_SEQUENTIAL] ** <li> [SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN] ** <li> [SQLITE_IOCAP_POWERSAFE_OVERWRITE] ** <li> [SQLITE_IOCAP_IMMUTABLE] ** <li> [SQLITE_IOCAP_BATCH_ATOMIC] ** <li> [SQLITE_IOCAP_SUBPAGE_READ] ** </ul> ** ** The SQLITE_IOCAP_ATOMIC property means that all writes of ** any size are atomic. The SQLITE_IOCAP_ATOMICnnn values ** mean that writes of blocks that are nnn bytes in size and ** are aligned to an address which is an integer multiple of ** nnn are atomic. The SQLITE_IOCAP_SAFE_APPEND value means |
| ︙ | ︙ | |||
1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 | ** ** <li>[[SQLITE_FCNTL_WIN32_SET_HANDLE]] ** The [SQLITE_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging. This ** opcode causes the xFileControl method to swap the file handle with the one ** pointed to by the pArg argument. This capability is used during testing ** and only needs to be supported when SQLITE_TEST is defined. ** ** <li>[[SQLITE_FCNTL_WAL_BLOCK]] ** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might ** be advantageous to block on the next WAL lock if the lock is not immediately ** available. The WAL subsystem issues this signal during rare ** circumstances in order to fix a problem with priority inversion. ** Applications should <em>not</em> use this file-control. ** | > > > > > | 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 | ** ** <li>[[SQLITE_FCNTL_WIN32_SET_HANDLE]] ** The [SQLITE_FCNTL_WIN32_SET_HANDLE] opcode is used for debugging. This ** opcode causes the xFileControl method to swap the file handle with the one ** pointed to by the pArg argument. This capability is used during testing ** and only needs to be supported when SQLITE_TEST is defined. ** ** <li>[[SQLITE_FCNTL_NULL_IO]] ** The [SQLITE_FCNTL_NULL_IO] opcode sets the low-level file descriptor ** or file handle for the [sqlite3_file] object such that it will no longer ** read or write to the database file. ** ** <li>[[SQLITE_FCNTL_WAL_BLOCK]] ** The [SQLITE_FCNTL_WAL_BLOCK] is a signal to the VFS layer that it might ** be advantageous to block on the next WAL lock if the lock is not immediately ** available. The WAL subsystem issues this signal during rare ** circumstances in order to fix a problem with priority inversion. ** Applications should <em>not</em> use this file-control. ** |
| ︙ | ︙ | |||
1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 | #define SQLITE_FCNTL_SIZE_LIMIT 36 #define SQLITE_FCNTL_CKPT_DONE 37 #define SQLITE_FCNTL_RESERVE_BYTES 38 #define SQLITE_FCNTL_CKPT_START 39 #define SQLITE_FCNTL_EXTERNAL_READER 40 #define SQLITE_FCNTL_CKSM_FILE 41 #define SQLITE_FCNTL_RESET_CACHE 42 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO | > | 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 | #define SQLITE_FCNTL_SIZE_LIMIT 36 #define SQLITE_FCNTL_CKPT_DONE 37 #define SQLITE_FCNTL_RESERVE_BYTES 38 #define SQLITE_FCNTL_CKPT_START 39 #define SQLITE_FCNTL_EXTERNAL_READER 40 #define SQLITE_FCNTL_CKSM_FILE 41 #define SQLITE_FCNTL_RESET_CACHE 42 #define SQLITE_FCNTL_NULL_IO 43 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| ︙ | ︙ | |||
2618 2619 2620 2621 2622 2623 2624 | ** CAPI3REF: Count The Number Of Rows Modified ** METHOD: sqlite3 ** ** ^These functions return the number of rows modified, inserted or ** deleted by the most recently completed INSERT, UPDATE or DELETE ** statement on the database connection specified by the only parameter. ** The two functions are identical except for the type of the return value | | > > > > | 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 | ** CAPI3REF: Count The Number Of Rows Modified ** METHOD: sqlite3 ** ** ^These functions return the number of rows modified, inserted or ** deleted by the most recently completed INSERT, UPDATE or DELETE ** statement on the database connection specified by the only parameter. ** The two functions are identical except for the type of the return value ** and that if the number of rows modified by the most recent INSERT, UPDATE, ** or DELETE is greater than the maximum value supported by type "int", then ** the return value of sqlite3_changes() is undefined. ^Executing any other ** type of SQL statement does not modify the value returned by these functions. ** For the purposes of this interface, a CREATE TABLE AS SELECT statement ** does not count as an INSERT, UPDATE or DELETE statement and hence the rows ** added to the new table by the CREATE TABLE AS SELECT statement are not ** counted. ** ** ^Only changes made directly by the INSERT, UPDATE or DELETE statement are ** considered - auxiliary changes caused by [CREATE TRIGGER | triggers], ** [foreign key actions] or [REPLACE] constraint resolution are not counted. ** ** Changes to a view that are intercepted by ** [INSTEAD OF trigger | INSTEAD OF triggers] are not counted. ^The value |
| ︙ | ︙ | |||
4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 |
** prepared statements, regardless of whether or not they use this
** flag.
**
** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt>
** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler
** to return an error (error code SQLITE_ERROR) if the statement uses
** any virtual tables.
** </dl>
*/
#define SQLITE_PREPARE_PERSISTENT 0x01
#define SQLITE_PREPARE_NORMALIZE 0x02
#define SQLITE_PREPARE_NO_VTAB 0x04
/*
** CAPI3REF: Compiling An SQL Statement
** KEYWORDS: {SQL statement compiler}
** METHOD: sqlite3
** CONSTRUCTOR: sqlite3_stmt
**
| > > > > > > > > > > > | 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 4210 4211 4212 4213 4214 4215 4216 4217 4218 4219 4220 4221 4222 4223 4224 4225 4226 4227 4228 4229 |
** prepared statements, regardless of whether or not they use this
** flag.
**
** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt>
** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler
** to return an error (error code SQLITE_ERROR) if the statement uses
** any virtual tables.
**
** [[SQLITE_PREPARE_DONT_LOG]] <dt>SQLITE_PREPARE_DONT_LOG</dt>
** <dd>The SQLITE_PREPARE_DONT_LOG flag prevents SQL compiler
** errors from being sent to the error log defined by
** [SQLITE_CONFIG_LOG]. This can be used, for example, to do test
** compiles to see if some SQL syntax is well-formed, without generating
** messages on the global error log when it is not. If the test compile
** fails, the sqlite3_prepare_v3() call returns the same error indications
** with or without this flag; it just omits the call to [sqlite3_log()] that
** logs the error.
** </dl>
*/
#define SQLITE_PREPARE_PERSISTENT 0x01
#define SQLITE_PREPARE_NORMALIZE 0x02
#define SQLITE_PREPARE_NO_VTAB 0x04
#define SQLITE_PREPARE_DONT_LOG 0x10
/*
** CAPI3REF: Compiling An SQL Statement
** KEYWORDS: {SQL statement compiler}
** METHOD: sqlite3
** CONSTRUCTOR: sqlite3_stmt
**
|
| ︙ | ︙ | |||
10876 10877 10878 10879 10880 10881 10882 | # define SQLITE_THREADSAFE 0 # endif #endif #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif | | | 10906 10907 10908 10909 10910 10911 10912 10913 10914 10915 10916 10917 10918 10919 10920 | # define SQLITE_THREADSAFE 0 # endif #endif #ifdef __cplusplus } /* End of the 'extern "C"' block */ #endif /* #endif for SQLITE3_H will be added by mksqlite3.tcl */ /******** Begin file sqlite3rtree.h *********/ /* ** 2010 August 30 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: |
| ︙ | ︙ | |||
13127 13128 13129 13130 13131 13132 13133 | ** ** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) ** This is used to access token iToken of phrase hit iIdx within the ** current row. If iIdx is less than zero or greater than or equal to the ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, ** output variable (*ppToken) is set to point to a buffer containing the ** matching document token, and (*pnToken) to the size of that buffer in | < < | > > > > > > > > > > > > > > > > > | 13157 13158 13159 13160 13161 13162 13163 13164 13165 13166 13167 13168 13169 13170 13171 13172 13173 13174 13175 13176 13177 13178 13179 13180 13181 13182 13183 13184 13185 13186 13187 13188 13189 13190 13191 13192 | ** ** xInstToken(pFts5, iIdx, iToken, ppToken, pnToken) ** This is used to access token iToken of phrase hit iIdx within the ** current row. If iIdx is less than zero or greater than or equal to the ** value returned by xInstCount(), SQLITE_RANGE is returned. Otherwise, ** output variable (*ppToken) is set to point to a buffer containing the ** matching document token, and (*pnToken) to the size of that buffer in ** bytes. ** ** The output text is not a copy of the document text that was tokenized. ** It is the output of the tokenizer module. For tokendata=1 tables, this ** includes any embedded 0x00 and trailing data. ** ** This API may be slow in some cases if the token identified by parameters ** iIdx and iToken matched a prefix token in the query. In most cases, the ** first call to this API for each prefix token in the query is forced ** to scan the portion of the full-text index that matches the prefix ** token to collect the extra data required by this API. If the prefix ** token matches a large number of token instances in the document set, ** this may be a performance problem. ** ** If the user knows in advance that a query may use this API for a ** prefix token, FTS5 may be configured to collect all required data as part ** of the initial querying of the full-text index, avoiding the second scan ** entirely. This also causes prefix queries that do not use this API to ** run more slowly and use more memory. FTS5 may be configured in this way ** either on a per-table basis using the [FTS5 insttoken | 'insttoken'] ** option, or on a per-query basis using the ** [fts5_insttoken | fts5_insttoken()] user function. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. ** ** xColumnLocale(pFts5, iIdx, pzLocale, pnLocale) ** If parameter iCol is less than zero, or greater than or equal to the ** number of columns in the table, SQLITE_RANGE is returned. |
| ︙ | ︙ | |||
13568 13569 13570 13571 13572 13573 13574 | #ifdef __cplusplus } /* end of the 'extern "C"' block */ #endif #endif /* _FTS5_H */ /******** End of fts5.h *********/ | > | 13613 13614 13615 13616 13617 13618 13619 13620 | #ifdef __cplusplus } /* end of the 'extern "C"' block */ #endif #endif /* _FTS5_H */ /******** End of fts5.h *********/ #endif /* SQLITE3_H */ |
Changes to fossil.1.
|
| | | | | | | | | | > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | .TH FOSSIL "1" "Oct 2024" "https://fossil-scm.org" "User Commands" .SH NAME fossil \- Distributed Version Control System .SH SYNOPSIS .B fossil \fIhelp\fR .br .B fossil \fIhelp COMMAND\fR .br .B fossil \fICOMMAND [OPTIONS]\fR .SH DESCRIPTION Fossil is a distributed version control system (DVCS) with built-in forum, wiki, ticket tracker, CGI/HTTP interface, and HTTP server. .SH Common COMMANDs: add cherrypick grep push sync .br addremove clean help rebuild tag .br all clone info remote timeline .br amend commit init repack tree .br annotate dbstat ls revert ui .br bisect delete merge rm undo .br blame describe merge-base settings unversioned .br branch diff mv sql update .br cat extras open ssl-config version .br changes finfo patch stash xdiff .br chat gdiff pull status .SH FEATURES Features as described on the fossil home page. .HP 1. |
| ︙ | ︙ |
Changes to skins/darkmode/css.txt.
| ︙ | ︙ | |||
96 97 98 99 100 101 102 103 104 105 |
color: rgba(24,24,24,0.8);
border-radius: 0.1em;
}
.fileage tr:hover,
div.filetreeline:hover {
background-color: #333;
}
.button,
button {
color: #aaa;
| > > > | | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
color: rgba(24,24,24,0.8);
border-radius: 0.1em;
}
.fileage tr:hover,
div.filetreeline:hover {
background-color: #333;
}
div.file-change-line button {
background-color: #484848
}
.button,
button {
color: #aaa;
background-color: #484848;
border-radius: 5px;
border: 0
}
.button:hover,
button:hover {
background-color: #FF4500f0;
color: rgba(24,24,24,0.8);
|
| ︙ | ︙ |
Changes to skins/default/css.txt.
| ︙ | ︙ | |||
40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
display: none; /* don't use body-area h1 except… */
}
.artifact h1.page-title,
.dir h1.page-title,
.doc h1.page-title,
.wiki h1.page-title {
display: block; /* …for potentially long doc titles… */
}
.artifact .title > .page-title,
.dir .title > .page-title,
.doc .title > .page-title,
.wiki .title > .page-title {
display: none; /* …where we suppress the title area h1 instead */
}
| > | 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
display: none; /* don't use body-area h1 except… */
}
.artifact h1.page-title,
.dir h1.page-title,
.doc h1.page-title,
.wiki h1.page-title {
display: block; /* …for potentially long doc titles… */
color: #444;
}
.artifact .title > .page-title,
.dir .title > .page-title,
.doc .title > .page-title,
.wiki .title > .page-title {
display: none; /* …where we suppress the title area h1 instead */
}
|
| ︙ | ︙ | |||
723 724 725 726 727 728 729 |
}
.forum > .content .markdown > pre {
margin-left: 20pt; /* special case for MD in forum; need less indent */
}
/* Fossil UI uses these, but in sufficiently constrained ways that we
* don't have to be nearly as careful to avoid an overreach. */
| | | | | | | | 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 |
}
.forum > .content .markdown > pre {
margin-left: 20pt; /* special case for MD in forum; need less indent */
}
/* Fossil UI uses these, but in sufficiently constrained ways that we
* don't have to be nearly as careful to avoid an overreach. */
.doc > .content h1, .artifact .content h1, .dir .content h1, .fileedit .content h1, .wiki .content h1 { margin-left: 10pt; }
.doc > .content h2, .artifact .content h2, .dir .content h2, .fileedit .content h2, .wiki .content h2 { margin-left: 20pt; }
.doc > .content h3, .artifact .content h3, .dir .content h3, .fileedit .content h3, .wiki .content h3 { margin-left: 30pt; }
.doc > .content h4, .artifact .content h4, .dir .content h4, .fileedit .content h4, .wiki .content h4 { margin-left: 40pt; }
.doc > .content h5, .artifact .content h5, .dir .content h5, .fileedit .content h5, .wiki .content h5 { margin-left: 50pt; }
.doc > .content hr, .artifact .content hr, .dir .content hr, .fileedit .content hr, .wiki .content hr { margin-left: 10pt; }
/* Don't need to be nearly as careful with tags Fossil UI doesn't use. */
.doc dd, .artifact dd, .dir dd, .fileedit dd, .wikiedit dd { margin-left: 30pt; margin-bottom: 1em; }
.doc dl, .artifact dl, .dir dl, .fileedit dl, .wikiedit dl { margin-left: 60pt; }
.doc dt, .artifact dt, .dir dt, .fileedit dt, .wikiedit dt { margin-left: 10pt; }
/* Fossil UI doesn't use Pikchr at all (yet?) so we can be quite loose
|
| ︙ | ︙ |
Changes to src/add.c.
| ︙ | ︙ | |||
351 352 353 354 355 356 357 | ** ** The --ignore and --clean options are comma-separated lists of glob patterns ** for files to be excluded. Example: '*.o,*.obj,*.exe' If the --ignore ** option does not appear on the command line then the "ignore-glob" setting ** is used. If the --clean option does not appear on the command line then ** the "clean-glob" setting is used. ** | | | 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 | ** ** The --ignore and --clean options are comma-separated lists of glob patterns ** for files to be excluded. Example: '*.o,*.obj,*.exe' If the --ignore ** option does not appear on the command line then the "ignore-glob" setting ** is used. If the --clean option does not appear on the command line then ** the "clean-glob" setting is used. ** ** When attempting to explicitly add files on the commandline, and if those ** match "ignore-glob", a confirmation is asked first. This can be prevented ** using the -f|--force option. ** ** The --case-sensitive option determines whether or not filenames should ** be treated case sensitive or not. If the option is not given, the default ** depends on the global setting, or the operating system default, if not set. ** |
| ︙ | ︙ | |||
751 752 753 754 755 756 757 | ** all files displayed using the "extras" command) are added as ** if by the "[[add]]" command. ** ** * All files in the repository but missing from the check-out (that is, ** all files that show as MISSING with the "status" command) are ** removed as if by the "[[rm]]" command. ** | < | | 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 | ** all files displayed using the "extras" command) are added as ** if by the "[[add]]" command. ** ** * All files in the repository but missing from the check-out (that is, ** all files that show as MISSING with the "status" command) are ** removed as if by the "[[rm]]" command. ** ** Note that this command does not "commit", as that is a separate step. ** ** Files and directories whose names begin with "." are ignored unless ** the --dotfiles option is used. ** ** The --ignore option overrides the "ignore-glob" setting, as do the ** --case-sensitive option with the "case-sensitive" setting and the ** --clean option with the "clean-glob" setting. See the documentation |
| ︙ | ︙ |
Changes to src/allrepo.c.
| ︙ | ︙ | |||
48 49 50 51 52 53 54 |
static void collect_argv(Blob *pExtra, int iStart){
int i;
for(i=iStart; i<g.argc; i++){
blob_appendf(pExtra, " %s", g.argv[i]);
}
}
| < | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
static void collect_argv(Blob *pExtra, int iStart){
int i;
for(i=iStart; i<g.argc; i++){
blob_appendf(pExtra, " %s", g.argv[i]);
}
}
/*
** COMMAND: all
**
** Usage: %fossil all SUBCOMMAND ...
**
** The ~/.fossil file records the location of all repositories for a
** user. This command performs certain operations on all repositories
|
| ︙ | ︙ | |||
427 428 429 430 431 432 433 |
}else{
fossil_fatal("\"all\" subcommand should be one of: "
"add cache changes clean dbstat extras fts-config git ignore "
"info list ls pull push rebuild remote "
"server settings sync ui unset whatis");
}
verify_all_options();
| > | > > > > > > > > > > > > > > > | | < | > > > | 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 |
}else{
fossil_fatal("\"all\" subcommand should be one of: "
"add cache changes clean dbstat extras fts-config git ignore "
"info list ls pull push rebuild remote "
"server settings sync ui unset whatis");
}
verify_all_options();
db_multi_exec(
"CREATE TEMP TABLE repolist(\n"
" name TEXT, -- Filename\n"
" tag TEXT, -- Key for the GLOBAL_CONFIG table entry\n"
" inode TEXT -- Unique identifier for this file\n"
");\n"
/* The seenFile() table holds inode names for entries that have
** already been processed. */
"CREATE TEMP TABLE seenFile(x TEXT COLLATE nocase);\n"
/* The toDel() table holds the "tag" for entries that need to be
** deleted because they are redundant or no longer exist */
"CREATE TEMP TABLE toDel(x TEXT);\n"
);
sqlite3_create_function(g.db, "inode", 1, SQLITE_UTF8, 0,
file_inode_sql_func, 0, 0);
if( useCheckouts ){
db_multi_exec(
"INSERT INTO repolist "
"SELECT substr(name, 7), name, inode(substr(name,7))"
" FROM global_config"
" WHERE substr(name, 1, 6)=='ckout:'"
" ORDER BY 1"
);
}else{
db_multi_exec(
"INSERT INTO repolist "
"SELECT substr(name, 6), name, inode(substr(name,6))"
" FROM global_config"
" WHERE substr(name, 1, 5)=='repo:'"
" ORDER BY 1"
);
}
db_prepare(&q,"SELECT name, tag, inode FROM repolist ORDER BY 1");
while( db_step(&q)==SQLITE_ROW ){
int rc;
const char *zFilename = db_column_text(&q, 0);
const char *zInode = db_column_text(&q,2);
#if !USE_SEE
if( sqlite3_strglob("*.efossil", zFilename)==0 ) continue;
#endif
if( file_access(zFilename, F_OK)
|| !file_is_canonical(zFilename)
|| (useCheckouts && file_isdir(zFilename, ExtFILE)!=1)
|| db_exists("SELECT 1 FROM temp.seenFile where x=%Q", zInode)
){
db_multi_exec("INSERT INTO toDel VALUES(%Q)", db_column_text(&q, 1));
nToDel++;
continue;
}
db_multi_exec("INSERT INTO seenFile(x) VALUES(%Q)", zInode);
if( zCmd[0]=='l' ){
fossil_print("%s\n", zFilename);
continue;
}else if( showFile ){
fossil_print("%s: %s\n", useCheckouts ? "check-out" : "repository",
zFilename);
}
|
| ︙ | ︙ |
Changes to src/attach.c.
| ︙ | ︙ | |||
632 633 634 635 636 637 638 | } /* ** Output HTML to show a list of attachments. */ void attachment_list( const char *zTarget, /* Object that things are attached to */ | | > > > > > > > | 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 |
}
/*
** Output HTML to show a list of attachments.
*/
void attachment_list(
const char *zTarget, /* Object that things are attached to */
const char *zHeader, /* Header to display with attachments */
int fHorizontalRule /* Insert <hr> separator above header */
){
int cnt = 0;
Stmt q;
db_prepare(&q,
"SELECT datetime(mtime,toLocal()), filename, user,"
" (SELECT uuid FROM blob WHERE rid=attachid), src"
" FROM attachment"
" WHERE isLatest AND src!='' AND target=%Q"
" ORDER BY mtime DESC",
zTarget
);
while( db_step(&q)==SQLITE_ROW ){
const char *zDate = db_column_text(&q, 0);
const char *zFile = db_column_text(&q, 1);
const char *zUser = db_column_text(&q, 2);
const char *zUuid = db_column_text(&q, 3);
const char *zSrc = db_column_text(&q, 4);
const char *zDispUser = zUser && zUser[0] ? zUser : "anonymous";
if( cnt==0 ){
@ <section class='attachlist'>
if( fHorizontalRule ){
@ <hr>
}
@ %s(zHeader)
@ <ul>
}
cnt++;
@ <li>
@ %z(href("%R/artifact/%!S",zSrc))%h(zFile)</a>
@ [<a href="%R/attachdownload/%t(zFile)?page=%t(zTarget)&file=%t(zFile)">download</a>]
@ added by %h(zDispUser) on
hyperlink_to_date(zDate, ".");
@ [%z(href("%R/ainfo/%!S",zUuid))details</a>]
@ </li>
}
if( cnt ){
@ </ul>
@ </section>
}
db_finalize(&q);
}
/*
** COMMAND: attachment*
|
| ︙ | ︙ |
Changes to src/bisect.c.
| ︙ | ︙ | |||
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
** Append a new skip entry to the bisect log.
*/
static void bisect_append_skip(int rid){
db_multi_exec(
"UPDATE vvar SET value=value||' s%d' WHERE name='bisect-log'", rid
);
}
/*
** Create a TEMP table named "bilog" that contains the complete history
** of the current bisect.
**
** If iCurrent>0 then it is the RID of the current check-out and is included
** in the history table.
**
** If zDesc is not NULL, then it is the bid= query parameter to /timeline
** that describes a bisect. Use the information in zDesc rather than in
** the bisect-log variable.
**
** If bDetail is true, then also include information about every node
** in between the inner-most GOOD and BAD nodes.
*/
int bisect_create_bilog_table(int iCurrent, const char *zDesc, int bDetail){
char *zLog;
Blob log, id;
| > > > > > > > > > > > > > > > > < > | 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
** Append a new skip entry to the bisect log.
*/
static void bisect_append_skip(int rid){
db_multi_exec(
"UPDATE vvar SET value=value||' s%d' WHERE name='bisect-log'", rid
);
}
/*
** Append a VALUES entry to the bilog table insert
*/
static void bisect_log_append(Blob *pSql,int iSeq,const char *zStat,int iRid){
if( (iSeq%6)==3 ){
blob_append_sql(pSql, ",\n ");
}else if( iSeq>1 ){
blob_append_sql(pSql, ",");
}
if( zStat ){
blob_append_sql(pSql, "(%d,%Q,%d)", iSeq, zStat, iRid);
}else{
blob_append_sql(pSql, "(NULL,NULL,%d)", iRid);
}
}
/*
** Create a TEMP table named "bilog" that contains the complete history
** of the current bisect.
**
** If iCurrent>0 then it is the RID of the current check-out and is included
** in the history table.
**
** If zDesc is not NULL, then it is the bid= query parameter to /timeline
** that describes a bisect. Use the information in zDesc rather than in
** the bisect-log variable.
**
** If bDetail is true, then also include information about every node
** in between the inner-most GOOD and BAD nodes.
*/
int bisect_create_bilog_table(int iCurrent, const char *zDesc, int bDetail){
char *zLog;
Blob log, id;
int cnt = 0;
int lastGood = -1;
int lastBad = -1;
Blob ins = BLOB_INITIALIZER;
if( zDesc!=0 ){
blob_init(&log, 0, 0);
while( zDesc[0]=='y' || zDesc[0]=='n' || zDesc[0]=='s' ){
int i;
char c;
int rid;
|
| ︙ | ︙ | |||
251 252 253 254 255 256 257 |
db_multi_exec(
"CREATE TEMP TABLE bilog("
" rid INTEGER PRIMARY KEY," /* Sequence of events */
" stat TEXT," /* Type of occurrence */
" seq INTEGER UNIQUE" /* Check-in number */
");"
);
| | < | | < | < < | < < < | < < < < < | < < | > | 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 |
db_multi_exec(
"CREATE TEMP TABLE bilog("
" rid INTEGER PRIMARY KEY," /* Sequence of events */
" stat TEXT," /* Type of occurrence */
" seq INTEGER UNIQUE" /* Check-in number */
");"
);
blob_append_sql(&ins, "INSERT OR IGNORE INTO bilog(seq,stat,rid) VALUES");
while( blob_token(&log, &id) ){
int rid;
cnt++;
if( blob_str(&id)[0]=='s' ){
rid = atoi(blob_str(&id)+1);
bisect_log_append(&ins, cnt, "SKIP", rid);
}else{
rid = atoi(blob_str(&id));
if( rid>0 ){
bisect_log_append(&ins, cnt, "GOOD", rid);
lastGood = rid;
}else{
bisect_log_append(&ins, cnt, "BAD", rid);
lastBad = -rid;
}
}
}
if( iCurrent>0 ){
bisect_log_append(&ins, ++cnt, "CURRENT", iCurrent);
}
if( bDetail && lastGood>0 && lastBad>0 ){
PathNode *p;
p = path_shortest(lastGood, lastBad, bisect_option("direct-only"),0, 0);
while( p ){
bisect_log_append(&ins, ++cnt, 0, p->rid);
p = p->u.pTo;
}
path_reset();
}
db_exec_sql(blob_sql_text(&ins));
blob_reset(&ins);
return 1;
}
/* Return a permalink description of a bisect. Space is obtained from
** fossil_malloc() and should be freed by the caller.
**
** A bisect description consists of characters 'y' and 'n' and lowercase
|
| ︙ | ︙ | |||
541 542 543 544 545 546 547 | ** be done, for example, because VERSION does not compile correctly ** or is otherwise unsuitable to participate in this bisect. ** ** > fossil bisect vlist|ls|status ?-a|--all? ** ** List the versions in between the inner-most "bad" and "good". ** | | | > > | 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 |
** be done, for example, because VERSION does not compile correctly
** or is otherwise unsuitable to participate in this bisect.
**
** > fossil bisect vlist|ls|status ?-a|--all?
**
** List the versions in between the inner-most "bad" and "good".
**
** > fossil bisect ui ?HOST@USER:PATH?
**
** Like "fossil ui" except start on a timeline that shows only the
** check-ins that are part of the current bisect. If the optional
** fourth term is added, then information is shown for the bisect that
** occurred in the PATH directory by USER on remote machine HOST.
**
** > fossil bisect undo
**
** Undo the most recent "good", "bad", or "skip" command.
*/
void bisect_cmd(void){
int n;
|
| ︙ | ︙ | |||
717 718 719 720 721 722 723 724 725 726 727 |
}else{
usage("options ?NAME? ?VALUE?");
}
}else if( strncmp(zCmd, "reset", n)==0 ){
bisect_reset();
}else if( strcmp(zCmd, "ui")==0 ){
char *newArgv[8];
newArgv[0] = g.argv[0];
newArgv[1] = "ui";
newArgv[2] = "--page";
newArgv[3] = "timeline?bisect";
| > > | | > | > > > | 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 |
}else{
usage("options ?NAME? ?VALUE?");
}
}else if( strncmp(zCmd, "reset", n)==0 ){
bisect_reset();
}else if( strcmp(zCmd, "ui")==0 ){
char *newArgv[8];
verify_all_options();
newArgv[0] = g.argv[0];
newArgv[1] = "ui";
newArgv[2] = "--page";
newArgv[3] = "timeline?bisect";
if( g.argc==4 ){
newArgv[4] = g.argv[3];
g.argc = 5;
}else{
g.argc = 4;
}
newArgv[g.argc] = 0;
g.argv = newArgv;
cmd_webserver();
}else if( strncmp(zCmd, "vlist", n)==0
|| strncmp(zCmd, "ls", n)==0
|| strncmp(zCmd, "status", n)==0
){
int fAll = find_option("all", "a", 0)!=0;
bisect_list(!fAll);
}else if( !foundCmd ){
usage:
usage("bad|good|log|chart|next|options|reset|run|skip|status|ui|undo");
}
}
|
Changes to src/blob.c.
| ︙ | ︙ | |||
663 664 665 666 667 668 669 |
void blob_dehttpize(Blob *pBlob){
blob_materialize(pBlob);
pBlob->nUsed = dehttpize(pBlob->aData);
}
/*
** Extract N bytes from blob pFrom and use it to initialize blob pTo.
| | > | 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 |
void blob_dehttpize(Blob *pBlob){
blob_materialize(pBlob);
pBlob->nUsed = dehttpize(pBlob->aData);
}
/*
** Extract N bytes from blob pFrom and use it to initialize blob pTo.
** Return the actual number of bytes extracted. The cursor position
** is advanced by the number of bytes extracted.
**
** After this call completes, pTo will be an ephemeral blob.
*/
int blob_extract(Blob *pFrom, int N, Blob *pTo){
blob_is_init(pFrom);
assert_blob_is_reset(pTo);
if( pFrom->iCursor + N > pFrom->nUsed ){
|
| ︙ | ︙ | |||
685 686 687 688 689 690 691 692 693 694 695 696 697 698 |
pTo->nAlloc = N;
pTo->aData = &pFrom->aData[pFrom->iCursor];
pTo->iCursor = 0;
pTo->xRealloc = blobReallocStatic;
pFrom->iCursor += N;
return N;
}
/*
** Rewind the cursor on a blob back to the beginning.
*/
void blob_rewind(Blob *p){
p->iCursor = 0;
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 |
pTo->nAlloc = N;
pTo->aData = &pFrom->aData[pFrom->iCursor];
pTo->iCursor = 0;
pTo->xRealloc = blobReallocStatic;
pFrom->iCursor += N;
return N;
}
/*
** Extract N **lines** of text from blob pFrom beginning at the current
** cursor position and use that text to initialize blob pTo. Unlike the
** blob_extract() routine, the cursor position is unchanged.
**
** pTo is assumed to be uninitialized.
**
** After this call completes, pTo will be an ephemeral blob.
*/
int blob_extract_lines(Blob *pFrom, int N, Blob *pTo){
int i;
int mx;
int iStart;
int n;
const char *z;
blob_zero(pTo);
z = pFrom->aData;
i = pFrom->iCursor;
mx = pFrom->nUsed;
while( N>0 ){
while( i<mx && z[i]!='\n' ){ i++; }
if( i>=mx ) break;
i++;
N--;
}
iStart = pFrom->iCursor;
n = blob_extract(pFrom, i-pFrom->iCursor, pTo);
pFrom->iCursor = iStart;
return n;
}
/*
** Return the number of lines of text in the blob. If the last
** line is incomplete (if it does not have a \n at the end) then
** it still counts.
*/
int blob_linecount(Blob *p){
int n = 0;
int i;
for(i=0; i<p->nUsed; i++){
if( p->aData[i]=='\n' ) n++;
}
if( p->nUsed>0 && p->aData[p->nUsed-1]!='\n' ) n++;
return n;
}
/*
** Rewind the cursor on a blob back to the beginning.
*/
void blob_rewind(Blob *p){
p->iCursor = 0;
}
|
| ︙ | ︙ |
Changes to src/branch.c.
| ︙ | ︙ | |||
638 639 640 641 642 643 644 | ** -c|--closed List closed branches ** -m|--merged List branches merged into the current branch ** -M|--unmerged List branches not merged into the current branch ** -p List only private branches ** -r Reverse the sort order ** -t Show recently changed branches first ** --self List only branches where you participate | | | | 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 | ** -c|--closed List closed branches ** -m|--merged List branches merged into the current branch ** -M|--unmerged List branches not merged into the current branch ** -p List only private branches ** -r Reverse the sort order ** -t Show recently changed branches first ** --self List only branches where you participate ** --username USER List only branches where USER participates ** --users N List up to N users participating ** ** The current branch is marked with an asterisk. Private branches are ** marked with a hash sign. ** ** If GLOB is given, show only branches matching the pattern. ** ** The "lsh" variant of this subcommand shows recently changed branches, |
| ︙ | ︙ |
Changes to src/chat.c.
| ︙ | ︙ | |||
1200 1201 1202 1203 1204 1205 1206 | ** Copy chat content from the server down into the local clone, ** as a backup or archive. Setup privilege is required on the server. ** ** --all Download all chat content. Normally only ** previously undownloaded content is retrieved. ** --debug Additional debugging output ** --out DATABASE Store CHAT table in separate database file | | | 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 | ** Copy chat content from the server down into the local clone, ** as a backup or archive. Setup privilege is required on the server. ** ** --all Download all chat content. Normally only ** previously undownloaded content is retrieved. ** --debug Additional debugging output ** --out DATABASE Store CHAT table in separate database file ** DATABASE rather than adding to local clone ** --unsafe Allow the use of unencrypted http:// ** ** > fossil chat send [ARGUMENTS] ** ** This command sends a new message to the chatroom. The message ** to be sent is determined by arguments as follows: ** |
| ︙ | ︙ |
Changes to src/checkin.c.
| ︙ | ︙ | |||
565 566 567 568 569 570 571 |
flags &= ~noFlagDefs[i].mask;
}
}
/* Confirm current working directory is within check-out. */
db_must_be_within_tree();
| | | 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 |
flags &= ~noFlagDefs[i].mask;
}
}
/* Confirm current working directory is within check-out. */
db_must_be_within_tree();
/* Get check-out version. */
vid = db_lget_int("checkout", 0);
/* Relative path flag determination is done by a shared function. */
if( determine_cwd_relative_option() ){
flags |= C_RELPATH;
}
|
| ︙ | ︙ | |||
965 966 967 968 969 970 971 | } /* ** COMMAND: tree ** ** Usage: %fossil tree ?OPTIONS? ?PATHS ...? ** | | | | 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 | } /* ** COMMAND: tree ** ** Usage: %fossil tree ?OPTIONS? ?PATHS ...? ** ** List all files in the current check-out much like the "tree" ** command does. If PATHS is included, only the named files ** (or their children if directories) are shown. ** ** Options: ** -r VERSION The specific check-in to list ** -R|--repository REPO Extract info from repository REPO ** ** See also: [[ls]] |
| ︙ | ︙ | |||
1561 1562 1563 1564 1565 1566 1567 |
diffFiles[0].zName[0] = '.';
diffFiles[0].zName[1] = 0;
break;
}
diffFiles[i].nName = strlen(diffFiles[i].zName);
diffFiles[i].nUsed = 0;
}
| | | | 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 |
diffFiles[0].zName[0] = '.';
diffFiles[0].zName[1] = 0;
break;
}
diffFiles[i].nName = strlen(diffFiles[i].zName);
diffFiles[i].nUsed = 0;
}
diff_version_to_checkout(0, &DCfg, diffFiles, &prompt);
for( i=0; diffFiles[i].zName; ++i ){
fossil_free(diffFiles[i].zName);
}
fossil_free(diffFiles);
}else{
diff_version_to_checkout(0, &DCfg, 0, &prompt);
}
}
prompt_for_user_comment(pComment, &prompt);
blob_reset(&prompt);
}
/*
|
| ︙ | ︙ | |||
2316 2317 2318 2319 2320 2321 2322 | ** background color for a single check-in. Subsequent check-ins revert ** to the default color. ** ** A check-in is not permitted to fork unless the --allow-fork option ** appears. An empty check-in (i.e. with nothing changed) is not ** allowed unless the --allow-empty option appears. A check-in may not ** be older than its ancestor unless the --allow-older option appears. | | | 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 | ** background color for a single check-in. Subsequent check-ins revert ** to the default color. ** ** A check-in is not permitted to fork unless the --allow-fork option ** appears. An empty check-in (i.e. with nothing changed) is not ** allowed unless the --allow-empty option appears. A check-in may not ** be older than its ancestor unless the --allow-older option appears. ** If any files in the check-in appear to contain unresolved merge ** conflicts, the check-in will not be allowed unless the ** --allow-conflict option is present. In addition, the entire ** check-in process may be aborted if a file contains content that ** appears to be binary, Unicode text, or text with CR/LF line endings ** unless the interactive user chooses to proceed. If there is no ** interactive user or these warnings should be skipped for some other ** reason, the --no-warnings option may be used. A check-in is not |
| ︙ | ︙ | |||
2356 2357 2358 2359 2360 2361 2362 | ** --delta Use a delta manifest in the commit process ** --hash Verify file status using hashing rather ** than relying on file mtimes ** --ignore-clock-skew If a clock skew is detected, ignore it and ** behave as if the user had entered 'yes' to ** the question of whether to proceed despite ** the skew. | | | 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 | ** --delta Use a delta manifest in the commit process ** --hash Verify file status using hashing rather ** than relying on file mtimes ** --ignore-clock-skew If a clock skew is detected, ignore it and ** behave as if the user had entered 'yes' to ** the question of whether to proceed despite ** the skew. ** --ignore-oversize Do not warn the user about oversized files ** --integrate Close all merged-in branches ** -m|--comment COMMENT-TEXT Use COMMENT-TEXT as commit comment ** -M|--message-file FILE Read the commit comment from given file ** --mimetype MIMETYPE Mimetype of check-in comment ** -n|--dry-run If given, display instead of run actions ** -v|--verbose Show a diff in the commit message prompt ** --no-prompt This option disables prompting the user for |
| ︙ | ︙ | |||
2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 |
int nConflict = 0; /* Number of unresolved merge conflicts */
int abortCommit = 0; /* Abort the commit due to text format conversions */
Blob ans; /* Answer to continuation prompts */
char cReply; /* First character of ans */
int bRecheck = 0; /* Repeat fork and closed-branch checks*/
int bIgnoreSkew = 0; /* --ignore-clock-skew flag */
int mxSize;
memset(&sCiInfo, 0, sizeof(sCiInfo));
url_proxy_options();
/* --sha1sum is an undocumented alias for --hash for backwards compatiblity */
useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
noSign = find_option("nosign",0,0)!=0;
if( find_option("nosync",0,0) ) g.fNoSync = 1;
| > > | 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 |
int nConflict = 0; /* Number of unresolved merge conflicts */
int abortCommit = 0; /* Abort the commit due to text format conversions */
Blob ans; /* Answer to continuation prompts */
char cReply; /* First character of ans */
int bRecheck = 0; /* Repeat fork and closed-branch checks*/
int bIgnoreSkew = 0; /* --ignore-clock-skew flag */
int mxSize;
char *zCurBranch = 0; /* The current branch name of checkout */
char *zNewBranch = 0; /* The branch name after update */
memset(&sCiInfo, 0, sizeof(sCiInfo));
url_proxy_options();
/* --sha1sum is an undocumented alias for --hash for backwards compatiblity */
useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
noSign = find_option("nosign",0,0)!=0;
if( find_option("nosync",0,0) ) g.fNoSync = 1;
|
| ︙ | ︙ | |||
2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 |
if( privateFlag==0 && sCiInfo.zBranch==0 ) {
sCiInfo.zBranch=db_get("main-branch", 0);
}
}else{
privateParent = content_is_private(vid);
}
/* Track the "private" status */
g.markPrivate = privateFlag || privateParent;
if( privateFlag && !privateParent ){
/* Apply default branch name ("private") and color ("orange") if not
** specified otherwise on the command-line, and if the parent is not
** already private. */
if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private";
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 |
if( privateFlag==0 && sCiInfo.zBranch==0 ) {
sCiInfo.zBranch=db_get("main-branch", 0);
}
}else{
privateParent = content_is_private(vid);
}
user_select();
/*
** Check that the user exists.
*/
if( !db_exists("SELECT 1 FROM user WHERE login=%Q", g.zLogin) ){
fossil_fatal("no such user: %s", g.zLogin);
}
/*
** Detect if the branch name has changed from the parent check-in
** and prompt if necessary
**/
zCurBranch = db_text(0,
" SELECT value FROM tagxref AS tx"
" WHERE rid=(SELECT pid"
" FROM tagxref LEFT JOIN event ON srcid=objid"
" LEFT JOIN plink ON rid=cid"
" WHERE rid=%d AND tagxref.tagid=%d"
" AND srcid!=origid"
" AND tagtype=2 AND coalesce(euser,user)!=%Q)"
" AND tx.tagid=%d",
vid, TAG_BRANCH, g.zLogin, TAG_BRANCH
);
if( zCurBranch!=0 && zCurBranch[0]!=0
&& forceFlag==0
&& noPrompt==0
){
zNewBranch = branch_of_rid(vid);
fossil_warning(
"WARNING: The parent check-in [%.10s] has been moved from branch\n"
" '%s' over to branch '%s'.",
rid_to_uuid(vid), zCurBranch, zNewBranch
);
prompt_user("Commit anyway? (y/N) ", &ans);
cReply = blob_str(&ans)[0];
blob_reset(&ans);
if( cReply!='y' && cReply!='Y' ){
fossil_fatal("Abandoning commit because branch has changed");
}
fossil_free(zNewBranch);
fossil_free(zCurBranch);
zCurBranch = branch_of_rid(vid);
}
if( zCurBranch==0 ) zCurBranch = branch_of_rid(vid);
/* Track the "private" status */
g.markPrivate = privateFlag || privateParent;
if( privateFlag && !privateParent ){
/* Apply default branch name ("private") and color ("orange") if not
** specified otherwise on the command-line, and if the parent is not
** already private. */
if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private";
|
| ︙ | ︙ | |||
2558 2559 2560 2561 2562 2563 2564 | ** ** The forbid-delta-manifests setting prevents new delta manifests. ** ** If the remote repository sent an avoid-delta-manifests pragma on ** the autosync above, then also try to avoid deltas, unless the ** --delta option is specified. The remote repo will send the ** avoid-delta-manifests pragma if it has its "forbid-delta-manifests" | | | 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 |
**
** The forbid-delta-manifests setting prevents new delta manifests.
**
** If the remote repository sent an avoid-delta-manifests pragma on
** the autosync above, then also try to avoid deltas, unless the
** --delta option is specified. The remote repo will send the
** avoid-delta-manifests pragma if it has its "forbid-delta-manifests"
** setting enabled.
*/
if( !db_get_boolean("seen-delta-manifest",0)
|| db_get_boolean("forbid-delta-manifests",0)
|| g.bAvoidDeltaManifests
){
if( !forceDelta ) forceBaseline = 1;
}
|
| ︙ | ︙ | |||
2637 2638 2639 2640 2641 2642 2643 |
const char *zTo = db_column_text(&q, 1);
fossil_fatal("cannot do a partial commit of '%s' without '%s' because "
"'%s' was renamed to '%s'", zFrom, zTo, zFrom, zTo);
}
db_finalize(&q);
}
| < < < < < < < < | 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 |
const char *zTo = db_column_text(&q, 1);
fossil_fatal("cannot do a partial commit of '%s' without '%s' because "
"'%s' was renamed to '%s'", zFrom, zTo, zFrom, zTo);
}
db_finalize(&q);
}
hasChanges = unsaved_changes(useHash ? CKSIG_HASH : 0);
db_begin_transaction();
db_record_repository_filename(0);
if( hasChanges==0 && !isAMerge && !allowEmpty && !forceFlag ){
fossil_fatal("nothing has changed; use --allow-empty to override");
}
|
| ︙ | ︙ | |||
2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 |
&& (sCiInfo.zBranch==0
|| db_exists("SELECT 1 FROM tagxref"
" WHERE tagid=%d AND rid=%d AND tagtype>0"
" AND value=%Q", TAG_BRANCH, vid, sCiInfo.zBranch))
){
fossil_fatal("cannot commit against a closed leaf");
}
/* Always exit the loop on the second pass */
if( bRecheck ) break;
/* Get the check-in comment. This might involve prompting the
** user for the check-in comment, in which case we should resync
| > > > > > > > > > > > > > > > > > > > > > > | 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 |
&& (sCiInfo.zBranch==0
|| db_exists("SELECT 1 FROM tagxref"
" WHERE tagid=%d AND rid=%d AND tagtype>0"
" AND value=%Q", TAG_BRANCH, vid, sCiInfo.zBranch))
){
fossil_fatal("cannot commit against a closed leaf");
}
/* Require confirmation to continue with the check-in if the branch
** has changed and the committer did not provide the same branch
*/
zNewBranch = branch_of_rid(vid);
if( fossil_strcmp(zCurBranch, zNewBranch)!=0
&& fossil_strcmp(sCiInfo.zBranch, zNewBranch)!=0
&& forceFlag==0
&& noPrompt==0
){
fossil_warning("parent check-in [%.10s] branch changed from '%s' to '%s'",
rid_to_uuid(vid), zCurBranch, zNewBranch);
prompt_user("continue (y/N)? ", &ans);
cReply = blob_str(&ans)[0];
blob_reset(&ans);
if( cReply!='y' && cReply!='Y' ){
fossil_fatal("Abandoning commit because branch has changed");
}
fossil_free(zCurBranch);
zCurBranch = branch_of_rid(vid);
}
fossil_free(zNewBranch);
/* Always exit the loop on the second pass */
if( bRecheck ) break;
/* Get the check-in comment. This might involve prompting the
** user for the check-in comment, in which case we should resync
|
| ︙ | ︙ |
Changes to src/checkout.c.
| ︙ | ︙ | |||
169 170 171 172 173 174 175 |
** the text of the manifest and the artifact ID of the manifest.
** If the manifest setting is set, but is not a boolean value, then treat
** each character as a flag to enable writing "manifest", "manifest.uuid" or
** "manifest.tags".
*/
void manifest_to_disk(int vid){
char *zManFile;
| < < | > | 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
** the text of the manifest and the artifact ID of the manifest.
** If the manifest setting is set, but is not a boolean value, then treat
** each character as a flag to enable writing "manifest", "manifest.uuid" or
** "manifest.tags".
*/
void manifest_to_disk(int vid){
char *zManFile;
int flg;
flg = db_get_manifest_setting();
if( flg & MFESTFLG_RAW ){
Blob manifest = BLOB_INITIALIZER;
content_get(vid, &manifest);
sterilize_manifest(&manifest, CFTYPE_MANIFEST);
zManFile = mprintf("%smanifest", g.zLocalRoot);
blob_write_to_file(&manifest, zManFile);
free(zManFile);
blob_reset(&manifest);
}else{
if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest'") ){
zManFile = mprintf("%smanifest", g.zLocalRoot);
file_delete(zManFile);
free(zManFile);
}
}
|
| ︙ | ︙ | |||
205 206 207 208 209 210 211 |
if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.uuid'") ){
zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
file_delete(zManFile);
free(zManFile);
}
}
if( flg & MFESTFLG_TAGS ){
| | | 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.uuid'") ){
zManFile = mprintf("%smanifest.uuid", g.zLocalRoot);
file_delete(zManFile);
free(zManFile);
}
}
if( flg & MFESTFLG_TAGS ){
Blob taglist = BLOB_INITIALIZER;
zManFile = mprintf("%smanifest.tags", g.zLocalRoot);
get_checkin_taglist(vid, &taglist);
blob_write_to_file(&taglist, zManFile);
free(zManFile);
blob_reset(&taglist);
}else{
if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest.tags'") ){
|
| ︙ | ︙ |
Changes to src/db.c.
| ︙ | ︙ | |||
168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
int iStartLine; /* Line of zStartFile where transaction started */
int (*xAuth)(void*,int,const char*,const char*,const char*,const char*);
void *pAuthArg; /* Argument to the authorizer */
const char *zAuthName; /* Name of the authorizer */
int bProtectTriggers; /* True if protection triggers already exist */
int nProtect; /* Slots of aProtect used */
unsigned aProtect[12]; /* Saved values of protectMask */
} db = {
PROTECT_USER|PROTECT_CONFIG|PROTECT_BASELINE, /* protectMask */
0, 0, 0, 0, 0, 0, 0, {{0}}, {0}, {0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0}};
/*
** Arrange for the given file to be deleted on a failure.
*/
| > > | 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
int iStartLine; /* Line of zStartFile where transaction started */
int (*xAuth)(void*,int,const char*,const char*,const char*,const char*);
void *pAuthArg; /* Argument to the authorizer */
const char *zAuthName; /* Name of the authorizer */
int bProtectTriggers; /* True if protection triggers already exist */
int nProtect; /* Slots of aProtect used */
unsigned aProtect[12]; /* Saved values of protectMask */
int pauseDmlLog; /* Ignore pDmlLog if positive */
Blob *pDmlLog; /* Append DML statements here, of not NULL */
} db = {
PROTECT_USER|PROTECT_CONFIG|PROTECT_BASELINE, /* protectMask */
0, 0, 0, 0, 0, 0, 0, {{0}}, {0}, {0}, 0, 0, 0, 0, 0, 0, 0, 0, 0, {0}};
/*
** Arrange for the given file to be deleted on a failure.
*/
|
| ︙ | ︙ | |||
641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 |
/*
** Possible flags to db_vprepare
*/
#define DB_PREPARE_IGNORE_ERROR 0x001 /* Suppress errors */
#define DB_PREPARE_PERSISTENT 0x002 /* Stmt will stick around for a while */
#endif
/*
** Prepare a Stmt. Assume that the Stmt is previously uninitialized.
** If the input string contains multiple SQL statements, only the first
** one is processed. All statements beyond the first are silently ignored.
*/
int db_vprepare(Stmt *pStmt, int flags, const char *zFormat, va_list ap){
int rc;
int prepFlags = 0;
char *zSql;
const char *zExtra = 0;
blob_zero(&pStmt->sql);
blob_vappendf(&pStmt->sql, zFormat, ap);
va_end(ap);
zSql = blob_str(&pStmt->sql);
db.nPrepare++;
if( flags & DB_PREPARE_PERSISTENT ){
prepFlags = SQLITE_PREPARE_PERSISTENT;
}
rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, &zExtra);
if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)==0 ){
db_err("%s\n%s", sqlite3_errmsg(g.db), zSql);
}else if( zExtra && !fossil_all_whitespace(zExtra) ){
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 |
/*
** Possible flags to db_vprepare
*/
#define DB_PREPARE_IGNORE_ERROR 0x001 /* Suppress errors */
#define DB_PREPARE_PERSISTENT 0x002 /* Stmt will stick around for a while */
#endif
/*
** If zSql is a DML statement, append it db.pDmlLog.
*/
static void db_append_dml(const char *zSql){
size_t nSql;
if( db.pDmlLog==0 ) return;
if( db.pauseDmlLog ) return;
if( zSql==0 ) return;
nSql = strlen(zSql);
while( nSql>0 && fossil_isspace(zSql[0]) ){ nSql--; zSql++; }
while( nSql>0 && fossil_isspace(zSql[nSql-1]) ) nSql--;
if( nSql<6 ) return;
if( fossil_strnicmp(zSql, "SELECT", 6)==0 ) return;
if( fossil_strnicmp(zSql, "PRAGMA", 6)==0 ) return;
blob_append(db.pDmlLog, zSql, nSql);
if( zSql[nSql-1]!=';' ) blob_append_char(db.pDmlLog, ';');
blob_append_char(db.pDmlLog, '\n');
}
/*
** Set the Blob to which DML statement text should be appended. Set it
** to zero to stop appending DML statement text.
*/
void db_append_dml_to_blob(Blob *pBlob){
db.pDmlLog = pBlob;
}
/*
** Pause or unpause the DML log
*/
void db_pause_dml_log(void){ db.pauseDmlLog++; }
void db_unpause_dml_log(void){ db.pauseDmlLog--; }
/*
** Prepare a Stmt. Assume that the Stmt is previously uninitialized.
** If the input string contains multiple SQL statements, only the first
** one is processed. All statements beyond the first are silently ignored.
*/
int db_vprepare(Stmt *pStmt, int flags, const char *zFormat, va_list ap){
int rc;
int prepFlags = 0;
char *zSql;
const char *zExtra = 0;
blob_zero(&pStmt->sql);
blob_vappendf(&pStmt->sql, zFormat, ap);
va_end(ap);
zSql = blob_str(&pStmt->sql);
db.nPrepare++;
db_append_dml(zSql);
if( flags & DB_PREPARE_PERSISTENT ){
prepFlags = SQLITE_PREPARE_PERSISTENT;
}
rc = sqlite3_prepare_v3(g.db, zSql, -1, prepFlags, &pStmt->pStmt, &zExtra);
if( rc!=0 && (flags & DB_PREPARE_IGNORE_ERROR)==0 ){
db_err("%s\n%s", sqlite3_errmsg(g.db), zSql);
}else if( zExtra && !fossil_all_whitespace(zExtra) ){
|
| ︙ | ︙ | |||
1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 |
while( rc==SQLITE_OK && z[0] ){
pStmt = 0;
rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd);
if( rc ){
db_err("%s: {%s}", sqlite3_errmsg(g.db), z);
}else if( pStmt ){
db.nPrepare++;
while( sqlite3_step(pStmt)==SQLITE_ROW ){}
rc = sqlite3_finalize(pStmt);
if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z);
}
z = zEnd;
}
return rc;
| > | 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 |
while( rc==SQLITE_OK && z[0] ){
pStmt = 0;
rc = sqlite3_prepare_v2(g.db, z, -1, &pStmt, &zEnd);
if( rc ){
db_err("%s: {%s}", sqlite3_errmsg(g.db), z);
}else if( pStmt ){
db.nPrepare++;
db_append_dml(sqlite3_sql(pStmt));
while( sqlite3_step(pStmt)==SQLITE_ROW ){}
rc = sqlite3_finalize(pStmt);
if( rc ) db_err("%s: {%.*s}", sqlite3_errmsg(g.db), (int)(zEnd-z), z);
}
z = zEnd;
}
return rc;
|
| ︙ | ︙ | |||
1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 |
sqlite3_result_null(context);
}else{
sqlite3_result_int64(context, rid);
}
}
}
/*
** The toLocal() SQL function returns a string that is an argument to a
** date/time function that is appropriate for modifying the time for display.
** If UTC time display is selected, no modification occurs. If local time
** display is selected, the time is adjusted appropriately.
**
** Example usage:
**
** SELECT datetime('now',toLocal());
*/
void db_tolocal_function(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < < < | 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 |
sqlite3_result_null(context);
}else{
sqlite3_result_int64(context, rid);
}
}
}
/*
** SETTING: timeline-utc boolean default=on
**
** If the timeline-utc setting is true, then Fossil tries to understand
** and display all time values using UTC. If this setting is false, Fossil
** tries to understand and display time values using the local timezone.
**
** The word "timeline" in the name of this setting is historical.
** This setting applies to all user interfaces of Fossil,
** not just the timeline.
**
** Note that when accessing Fossil using the web interface, the localtime
** used is the localtime on the server, not on the client.
*/
/*
** Return true if Fossil is set to display times as UTC. Return false
** if it wants to display times using the local timezone.
**
** False is returned if display is set to localtime even if the localtime
** happens to be the same as UTC.
*/
int fossil_ui_utctime(void){
if( g.fTimeFormat==0 ){
if( db_get_int("timeline-utc", 1) ){
g.fTimeFormat = 1; /* UTC */
}else{
g.fTimeFormat = 2; /* Localtime */
}
}
return g.fTimeFormat==1;
}
/*
** Return true if Fossil is set to display times using the local timezone.
*/
int fossil_ui_localtime(void){
return fossil_ui_utctime()==0;
}
/*
** The toLocal() SQL function returns a string that is an argument to a
** date/time function that is appropriate for modifying the time for display.
** If UTC time display is selected, no modification occurs. If local time
** display is selected, the time is adjusted appropriately.
**
** Example usage:
**
** SELECT datetime('now',toLocal());
*/
void db_tolocal_function(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
if( fossil_ui_utctime() ){
sqlite3_result_text(context, "0 seconds", -1, SQLITE_STATIC);
}else{
sqlite3_result_text(context, "localtime", -1, SQLITE_STATIC);
}
}
/*
|
| ︙ | ︙ | |||
1354 1355 1356 1357 1358 1359 1360 |
** SELECT julianday(:user_input,fromLocal());
*/
void db_fromlocal_function(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
| | < < < < < < < | 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 |
** SELECT julianday(:user_input,fromLocal());
*/
void db_fromlocal_function(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
if( fossil_ui_utctime() ){
sqlite3_result_text(context, "0 seconds", -1, SQLITE_STATIC);
}else{
sqlite3_result_text(context, "utc", -1, SQLITE_STATIC);
}
}
/*
|
| ︙ | ︙ | |||
1522 1523 1524 1525 1526 1527 1528 |
/*
** Register the SQL functions that are useful both to the internal
** representation and to the "fossil sql" command.
*/
void db_add_aux_functions(sqlite3 *db){
sqlite3_create_collation(db, "uintnocase", SQLITE_UTF8,0,uintNocaseCollFunc);
| | > | | > | | > | | > | > | | > | | > | > > | 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 |
/*
** Register the SQL functions that are useful both to the internal
** representation and to the "fossil sql" command.
*/
void db_add_aux_functions(sqlite3 *db){
sqlite3_create_collation(db, "uintnocase", SQLITE_UTF8,0,uintNocaseCollFunc);
sqlite3_create_function(db, "checkin_mtime", 2,
SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
0, db_checkin_mtime_function, 0, 0);
sqlite3_create_function(db, "symbolic_name_to_rid", 1,
SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
0, db_sym2rid_function, 0, 0);
sqlite3_create_function(db, "symbolic_name_to_rid", 2,
SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
0, db_sym2rid_function, 0, 0);
sqlite3_create_function(db, "now", 0,
SQLITE_UTF8|SQLITE_INNOCUOUS, 0,
db_now_function, 0, 0);
sqlite3_create_function(db, "toLocal", 0,
SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
0, db_tolocal_function, 0, 0);
sqlite3_create_function(db, "fromLocal", 0,
SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
0, db_fromlocal_function, 0, 0);
sqlite3_create_function(db, "hextoblob", 1,
SQLITE_UTF8|SQLITE_DETERMINISTIC|SQLITE_INNOCUOUS,
0, db_hextoblob, 0, 0);
sqlite3_create_function(db, "capunion", 1, SQLITE_UTF8, 0,
0, capability_union_step, capability_union_finalize);
sqlite3_create_function(db, "fullcap", 1, SQLITE_UTF8, 0,
capability_fullcap, 0, 0);
sqlite3_create_function(db, "find_emailaddr", 1, SQLITE_UTF8, 0,
alert_find_emailaddr_func, 0, 0);
sqlite3_create_function(db, "display_name", 1, SQLITE_UTF8, 0,
alert_display_name_func, 0, 0);
sqlite3_create_function(db, "obscure", 1, SQLITE_UTF8, 0,
db_obscure, 0, 0);
sqlite3_create_function(db, "protected_setting", 1, SQLITE_UTF8, 0,
db_protected_setting_func, 0, 0);
sqlite3_create_function(db, "win_reserved", 1, SQLITE_UTF8, 0,
db_win_reserved_func,0,0);
sqlite3_create_function(db, "url_nouser", 1, SQLITE_UTF8, 0,
url_nouser_func,0,0);
sqlite3_create_function(db, "chat_msg_from_event", 4,
SQLITE_UTF8 | SQLITE_INNOCUOUS, 0,
chat_msg_from_event, 0, 0);
sqlite3_create_function(db, "inode", 1, SQLITE_UTF8, 0,
file_inode_sql_func,0,0);
}
#if USE_SEE
/*
** This is a pointer to the saved database encryption key string.
*/
|
| ︙ | ︙ | |||
2101 2102 2103 2104 2105 2106 2107 |
if( strcmp(blob_str(&bNameCheck), g.nameOfExe)==0 ){
extern int sqlite3_appendvfs_init(
sqlite3 *, char **, const sqlite3_api_routines *
);
sqlite3_appendvfs_init(0,0,0);
g.zVfsName = "apndvfs";
}
| | | 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 |
if( strcmp(blob_str(&bNameCheck), g.nameOfExe)==0 ){
extern int sqlite3_appendvfs_init(
sqlite3 *, char **, const sqlite3_api_routines *
);
sqlite3_appendvfs_init(0,0,0);
g.zVfsName = "apndvfs";
}
blob_reset(&bNameCheck);
rc = sqlite3_open_v2(
zDbName, &db,
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
g.zVfsName
);
if( rc!=SQLITE_OK ){
db_err("[%s]: %s", zDbName, sqlite3_errmsg(db));
|
| ︙ | ︙ | |||
2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 |
sqlite3_finalize(pStmt);
sqlite3_close(db);
return res;
}
/*
** COMMAND: test-is-repo
*/
void test_is_repo(void){
int i;
for(i=2; i<g.argc; i++){
fossil_print("%s: %s\n",
db_looks_like_a_repository(g.argv[i]) ? "yes" : " no",
g.argv[i]
);
}
}
| > > > > > | 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 |
sqlite3_finalize(pStmt);
sqlite3_close(db);
return res;
}
/*
** COMMAND: test-is-repo
** Usage: %fossil test-is-repo FILENAME...
**
** Test whether the specified files look like a SQLite database
** containing a Fossil repository schema.
*/
void test_is_repo(void){
int i;
verify_all_options();
for(i=2; i<g.argc; i++){
fossil_print("%s: %s\n",
db_looks_like_a_repository(g.argv[i]) ? "yes" : " no",
g.argv[i]
);
}
}
|
| ︙ | ︙ | |||
4078 4079 4080 4081 4082 4083 4084 | ** for the repository is created with its root at the current working ** directory, or in DIR if the "--workdir DIR" is used. If VERSION is ** specified then that version is checked out. Otherwise the most recent ** check-in on the main branch (usually "trunk") is used. ** ** REPOSITORY can be the filename for a repository that already exists on the ** local machine or it can be a URI for a remote repository. If REPOSITORY | | | 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 | ** for the repository is created with its root at the current working ** directory, or in DIR if the "--workdir DIR" is used. If VERSION is ** specified then that version is checked out. Otherwise the most recent ** check-in on the main branch (usually "trunk") is used. ** ** REPOSITORY can be the filename for a repository that already exists on the ** local machine or it can be a URI for a remote repository. If REPOSITORY ** is a URI in one of the formats recognized by the [[clone]] command, the ** remote repo is first cloned, then the clone is opened. The clone will be ** stored in the current directory, or in DIR if the "--repodir DIR" option ** is used. The name of the clone will be taken from the last term of the URI. ** For "http:" and "https:" URIs, you can append an extra term to the end of ** the URI to get any repository name you like. For example: ** ** fossil open https://fossil-scm.org/home/new-name |
| ︙ | ︙ | |||
4679 4680 4681 4682 4683 4684 4685 | ** If enabled on a client, new delta manifests are prohibited on ** commits. If enabled on a server, whenever a client attempts ** to obtain a check-in lock during auto-sync, the server will ** send the "pragma avoid-delta-manifests" statement in its reply, ** which will cause the client to avoid generating a delta ** manifest. */ | < < < < < < < | 4756 4757 4758 4759 4760 4761 4762 4763 4764 4765 4766 4767 4768 4769 | ** If enabled on a client, new delta manifests are prohibited on ** commits. If enabled on a server, whenever a client attempts ** to obtain a check-in lock during auto-sync, the server will ** send the "pragma avoid-delta-manifests" statement in its reply, ** which will cause the client to avoid generating a delta ** manifest. */ /* ** SETTING: gdiff-command width=40 default=gdiff sensitive ** The value is an external command to run when performing a graphical ** diff. If undefined, text diff will be used. */ /* ** SETTING: gmerge-command width=40 sensitive |
| ︙ | ︙ | |||
5360 5361 5362 5363 5364 5365 5366 |
fossil_print("Local database: %s\n", g.zLocalDbName);
fossil_print("Config database: %s\n", g.zConfigDbName);
}
/*
** Compute a "fingerprint" on the repository. A fingerprint is used
** to verify that that the repository has not been replaced by a clone
| | | 5430 5431 5432 5433 5434 5435 5436 5437 5438 5439 5440 5441 5442 5443 5444 |
fossil_print("Local database: %s\n", g.zLocalDbName);
fossil_print("Config database: %s\n", g.zConfigDbName);
}
/*
** Compute a "fingerprint" on the repository. A fingerprint is used
** to verify that that the repository has not been replaced by a clone
** of the same repository. More precisely, a fingerprint is used to
** verify that the mapping between SHA3 hashes and RID values is unchanged.
**
** The check-out database ("localdb") stores RID values. When associating
** a check-out database against a repository database, it is useful to verify
** the fingerprint so that we know tha the RID values in the check-out
** database still correspond to the correct entries in the BLOB table of
** the repository.
|
| ︙ | ︙ | |||
5425 5426 5427 5428 5429 5430 5431 | /* ** COMMAND: test-fingerprint ** ** Usage: %fossil test-fingerprint ?RCVID? ** ** Display the repository fingerprint using the supplied RCVID or | | | 5495 5496 5497 5498 5499 5500 5501 5502 5503 5504 5505 5506 5507 5508 5509 |
/*
** COMMAND: test-fingerprint
**
** Usage: %fossil test-fingerprint ?RCVID?
**
** Display the repository fingerprint using the supplied RCVID or
** using the latest RCVID if none is given on the command line.
** Show both the legacy and the newer version of the fingerprint,
** and the currently stored fingerprint if there is one.
*/
void test_fingerprint(void){
int rcvid = 0;
db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
if( g.argc==3 ){
|
| ︙ | ︙ |
Changes to src/default.css.
| ︙ | ︙ | |||
746 747 748 749 750 751 752 753 754 755 756 757 758 759 |
}
body.tkt div.content ol.tkt-changes > li:target > p > span {
border-bottom: 3px solid gold;
}
body.tkt div.content ol.tkt-changes > li:target > ol {
border-left: 1px solid gold;
}
span.modpending {
color: #b03800;
font-style: italic;
}
pre.th1result {
white-space: pre-wrap;
| > > > > > > > > > > | 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 |
}
body.tkt div.content ol.tkt-changes > li:target > p > span {
border-bottom: 3px solid gold;
}
body.tkt div.content ol.tkt-changes > li:target > ol {
border-left: 1px solid gold;
}
body.cpage-ckout .file-change-line,
body.cpage-info .file-change-line,
body.cpage-vdiff .file-change-line {
margin-top: 16px;
margin-bottom: 16px;
margin-right: 1em /* keep it from nudging right up against the scrollbar-reveal zone */;
display: flex;
flex-direction: row;
justify-content: space-between;
}
span.modpending {
color: #b03800;
font-style: italic;
}
pre.th1result {
white-space: pre-wrap;
|
| ︙ | ︙ | |||
1816 1817 1818 1819 1820 1821 1822 |
.settings-icon:hover {
border: 1px outset rgba(127,127,127,1);
}
body.fossil-dark-style .settings-icon {
filter: invert(100%);
}
| < < < < | 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 |
.settings-icon:hover {
border: 1px outset rgba(127,127,127,1);
}
body.fossil-dark-style .settings-icon {
filter: invert(100%);
}
body.branch .brlist > table > tbody > tr:hover:not(.selected),
body.branch .brlist > table > tbody > tr.selected {
background-color: #ffc;
}
body.branch .brlist > table > tbody td:first-child > input {
cursor: pointer;
}
|
| ︙ | ︙ |
Changes to src/delta.c.
| ︙ | ︙ | |||
223 224 225 226 227 228 229 |
** buffer is not a multiple of 4 bytes length, compute the sum that would
** have occurred if the buffer was padded with zeros to the next multiple
** of four bytes.
*/
static unsigned int checksum(const char *zIn, size_t N){
static const int byteOrderTest = 1;
const unsigned char *z = (const unsigned char *)zIn;
| < > > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 |
** buffer is not a multiple of 4 bytes length, compute the sum that would
** have occurred if the buffer was padded with zeros to the next multiple
** of four bytes.
*/
static unsigned int checksum(const char *zIn, size_t N){
static const int byteOrderTest = 1;
const unsigned char *z = (const unsigned char *)zIn;
unsigned sum = 0;
if( N>0 ){
const unsigned char *zEnd = (const unsigned char*)&zIn[N&~3];
assert( (z - (const unsigned char*)0)%4==0 ); /* Four-byte alignment */
if( 0==*(char*)&byteOrderTest ){
/* This is a big-endian machine */
while( z<zEnd ){
sum += *(unsigned*)z;
z += 4;
}
}else{
/* A little-endian machine */
#if GCC_VERSION>=4003000
while( z<zEnd ){
sum += __builtin_bswap32(*(unsigned*)z);
z += 4;
}
#elif defined(_MSC_VER) && _MSC_VER>=1300
while( z<zEnd ){
sum += _byteswap_ulong(*(unsigned*)z);
z += 4;
}
#else
unsigned sum0 = 0;
unsigned sum1 = 0;
unsigned sum2 = 0;
while(N >= 16){
sum0 += ((unsigned)z[0] + z[4] + z[8] + z[12]);
sum1 += ((unsigned)z[1] + z[5] + z[9] + z[13]);
sum2 += ((unsigned)z[2] + z[6] + z[10]+ z[14]);
sum += ((unsigned)z[3] + z[7] + z[11]+ z[15]);
z += 16;
N -= 16;
}
while(N >= 4){
sum0 += z[0];
sum1 += z[1];
sum2 += z[2];
sum += z[3];
z += 4;
N -= 4;
}
sum += (sum2 << 8) + (sum1 << 16) + (sum0 << 24);
#endif
}
switch(N&3){
case 3: sum += (z[2] << 8);
case 2: sum += (z[1] << 16);
case 1: sum += (z[0] << 24);
default: ;
}
}
return sum;
}
/*
** Create a new delta.
**
|
| ︙ | ︙ |
Changes to src/descendants.c.
| ︙ | ︙ | |||
200 201 202 203 204 205 206 |
double rLimitMtime = 0.0;
if( ridBackTo ){
rLimitMtime = db_double(0.0,
"SELECT mtime FROM event WHERE objid=%d",
ridBackTo);
}
db_multi_exec(
| | | | | | | | | | | | | | | | | | | < | 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
double rLimitMtime = 0.0;
if( ridBackTo ){
rLimitMtime = db_double(0.0,
"SELECT mtime FROM event WHERE objid=%d",
ridBackTo);
}
db_multi_exec(
"WITH RECURSIVE\n"
" parent(pid,cid,isCP) AS (\n"
" SELECT plink.pid, plink.cid, 0 AS xisCP FROM plink\n"
" UNION ALL\n"
" SELECT parentid, childid, 1 FROM cherrypick WHERE NOT isExclude\n"
" ),\n"
" ancestor(rid, mtime, isCP) AS (\n"
" SELECT %d, mtime, 0 FROM event WHERE objid=%d\n"
" UNION\n"
" SELECT parent.pid, event.mtime, parent.isCP\n"
" FROM ancestor, parent, event\n"
" WHERE parent.cid=ancestor.rid\n"
" AND event.objid=parent.pid\n"
" AND NOT ancestor.isCP\n"
" AND (event.mtime>=%.17g OR parent.pid=%d)\n"
" ORDER BY mtime DESC LIMIT %d\n"
" )\n"
"INSERT OR IGNORE INTO ok SELECT rid FROM ancestor;",
rid, rid, rLimitMtime, ridBackTo, N
);
if( ridBackTo && db_changes()>1 ){
db_multi_exec("INSERT OR IGNORE INTO ok VALUES(%d)", ridBackTo);
}
}
}
|
| ︙ | ︙ | |||
320 321 322 323 324 325 326 |
void compute_descendants(int rid, int N){
if( !N ){
N = -1;
}else if( N<0 ){
N = -N;
}
db_multi_exec(
| | | | | | | | | | 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 |
void compute_descendants(int rid, int N){
if( !N ){
N = -1;
}else if( N<0 ){
N = -N;
}
db_multi_exec(
"WITH RECURSIVE\n"
" dx(rid,mtime) AS (\n"
" SELECT %d, 0\n"
" UNION\n"
" SELECT plink.cid, plink.mtime FROM dx, plink\n"
" WHERE plink.pid=dx.rid\n"
" ORDER BY 2\n"
" )\n"
"INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d",
rid, N
);
}
/*
** COMMAND: descendants*
|
| ︙ | ︙ | |||
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 |
#if INTERFACE
/* Flag parameters to compute_uses_file() */
#define USESFILE_DELETE 0x01 /* Include the check-ins where file deleted */
#endif
/*
** Add to table zTab the record ID (rid) of every check-in that contains
** the file fid.
*/
void compute_uses_file(const char *zTab, int fid, int usesFlags){
Bag seen;
Bag pending;
| > > > > > > > > > > > > > > > | > | | < < | < < | < < > | | 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 |
#if INTERFACE
/* Flag parameters to compute_uses_file() */
#define USESFILE_DELETE 0x01 /* Include the check-ins where file deleted */
#endif
/*
** Append a new VALUES term.
*/
static void uses_file_append_term(Blob *pSql, int *pnCnt, int rid){
if( *pnCnt==0 ){
blob_append_sql(pSql, "(%d)", rid);
*pnCnt = 4;
}else if( (*pnCnt)%10==9 ){
blob_append_sql(pSql, ",\n (%d)", rid);
}else{
blob_append_sql(pSql, ",(%d)", rid);
}
++*pnCnt;
}
/*
** Add to table zTab the record ID (rid) of every check-in that contains
** the file fid.
*/
void compute_uses_file(const char *zTab, int fid, int usesFlags){
Bag seen;
Bag pending;
Blob ins = BLOB_INITIALIZER;
int nIns = 0;
Stmt q;
int rid;
bag_init(&seen);
bag_init(&pending);
blob_append_sql(&ins, "INSERT OR IGNORE INTO \"%w\" VALUES", zTab);
db_prepare(&q, "SELECT mid FROM mlink WHERE fid=%d", fid);
while( db_step(&q)==SQLITE_ROW ){
int mid = db_column_int(&q, 0);
bag_insert(&pending, mid);
bag_insert(&seen, mid);
uses_file_append_term(&ins, &nIns, mid);
}
db_finalize(&q);
db_prepare(&q, "SELECT mid FROM mlink WHERE pid=%d", fid);
while( db_step(&q)==SQLITE_ROW ){
int mid = db_column_int(&q, 0);
bag_insert(&seen, mid);
if( usesFlags & USESFILE_DELETE ){
uses_file_append_term(&ins, &nIns, mid);
}
}
db_finalize(&q);
db_prepare(&q, "SELECT cid FROM plink WHERE pid=:rid AND isprim");
while( (rid = bag_first(&pending))!=0 ){
bag_remove(&pending, rid);
db_bind_int(&q, ":rid", rid);
while( db_step(&q)==SQLITE_ROW ){
int mid = db_column_int(&q, 0);
if( bag_find(&seen, mid) ) continue;
bag_insert(&seen, mid);
bag_insert(&pending, mid);
uses_file_append_term(&ins, &nIns, mid);
}
db_reset(&q);
}
db_finalize(&q);
db_exec_sql(blob_str(&ins));
blob_reset(&ins);
bag_clear(&seen);
bag_clear(&pending);
}
|
Changes to src/diff.c.
| ︙ | ︙ | |||
48 49 50 51 52 53 54 55 56 57 58 59 60 61 | #define DIFF_JSON 0x00010000 /* JSON output */ #define DIFF_DEBUG 0x00020000 /* Debugging diff output */ #define DIFF_RAW 0x00040000 /* Raw triples - for debugging */ #define DIFF_TCL 0x00080000 /* For the --tk option */ #define DIFF_INCBINARY 0x00100000 /* The --diff-binary option */ #define DIFF_SHOW_VERS 0x00200000 /* Show compared versions */ #define DIFF_DARKMODE 0x00400000 /* Use dark mode for HTML */ /* ** Per file information that may influence output. */ #define DIFF_FILE_ADDED 0x40000000 /* Added or rename destination */ #define DIFF_FILE_DELETED 0x80000000 /* Deleted or rename source */ #define DIFF_FILE_MASK 0xc0000000 /* Used for clearing file flags */ | > | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | #define DIFF_JSON 0x00010000 /* JSON output */ #define DIFF_DEBUG 0x00020000 /* Debugging diff output */ #define DIFF_RAW 0x00040000 /* Raw triples - for debugging */ #define DIFF_TCL 0x00080000 /* For the --tk option */ #define DIFF_INCBINARY 0x00100000 /* The --diff-binary option */ #define DIFF_SHOW_VERS 0x00200000 /* Show compared versions */ #define DIFF_DARKMODE 0x00400000 /* Use dark mode for HTML */ #define DIFF_BY_TOKEN 0x01000000 /* Split on tokens, not lines */ /* ** Per file information that may influence output. */ #define DIFF_FILE_ADDED 0x40000000 /* Added or rename destination */ #define DIFF_FILE_DELETED 0x80000000 /* Deleted or rename source */ #define DIFF_FILE_MASK 0xc0000000 /* Used for clearing file flags */ |
| ︙ | ︙ | |||
317 318 319 320 321 322 323 324 325 326 327 328 329 330 |
}while( zNL[0]!='\0' && zNL[1]!='\0' );
assert( i==nLine );
/* Return results */
*pnLine = nLine;
return a;
}
/*
** Return zero if two DLine elements are identical.
*/
static int compare_dline(const DLine *pA, const DLine *pB){
if( pA->h!=pB->h ) return 1;
return memcmp(pA->z,pB->z, pA->h&LENGTH_MASK);
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 |
}while( zNL[0]!='\0' && zNL[1]!='\0' );
assert( i==nLine );
/* Return results */
*pnLine = nLine;
return a;
}
/*
** Character classes for the purpose of tokenization.
**
** 1 - alphanumeric
** 2 - whitespace
** 3 - punctuation
*/
static char aTCharClass[256] = {
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3,
3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3,
3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
};
/*
** Count the number of tokens in the given string.
*/
static int count_tokens(const unsigned char *p, int n){
int nToken = 0;
int iPrev = 0;
int i;
for(i=0; i<n; i++){
char x = aTCharClass[p[i]];
if( x!=iPrev ){
iPrev = x;
nToken++;
}
}
return nToken;
}
/*
** Return an array of DLine objects containing a pointer to the
** start of each token and a hash of that token. The lower
** bits of the hash store the length of each token.
**
** This is like break_into_lines() except that it works with tokens
** instead of lines. A token is:
**
** * A contiguous sequence of alphanumeric characters.
** * A contiguous sequence of whitespace
** * A contiguous sequence of punctuation characters.
**
** Return 0 if the file is binary or contains a line that is
** too long.
*/
static DLine *break_into_tokens(
const char *z,
int n,
int *pnToken,
u64 diffFlags
){
int nToken, i, k;
u64 h, h2;
DLine *a;
unsigned char *p = (unsigned char*)z;
nToken = count_tokens(p, n);
a = fossil_malloc( sizeof(a[0])*(nToken+1) );
memset(a, 0, sizeof(a[0])*(nToken+1));
if( n==0 ){
*pnToken = 0;
return a;
}
i = 0;
while( n>0 ){
char x = aTCharClass[*p];
h = 0xcbf29ce484222325LL;
for(k=1; k<n && aTCharClass[p[k]]==x; k++){
h ^= p[k];
h *= 0x100000001b3LL;
}
a[i].z = (char*)p;
a[i].n = k;
a[i].h = h = ((h%281474976710597LL)<<LENGTH_MASK_SZ) | k;
h2 = h % nToken;
a[i].iNext = a[h2].iHash;
a[h2].iHash = i+1;
p += k; n -= k;
i++;
};
assert( i==nToken );
/* Return results */
*pnToken = nToken;
return a;
}
/*
** Return zero if two DLine elements are identical.
*/
static int compare_dline(const DLine *pA, const DLine *pB){
if( pA->h!=pB->h ) return 1;
return memcmp(pA->z,pB->z, pA->h&LENGTH_MASK);
|
| ︙ | ︙ | |||
2460 2461 2462 2463 2464 2465 2466 | sqlite3_int64 bestScore; /* Best score so far */ sqlite3_int64 score; /* Score for current candidate LCS */ int span; /* combined width of the input sequences */ int cutoff = 4; /* Max hash chain entries to follow */ int nextCutoff = -1; /* Value of cutoff for next iteration */ span = (iE1 - iS1) + (iE2 - iS2); | | | 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 |
sqlite3_int64 bestScore; /* Best score so far */
sqlite3_int64 score; /* Score for current candidate LCS */
int span; /* combined width of the input sequences */
int cutoff = 4; /* Max hash chain entries to follow */
int nextCutoff = -1; /* Value of cutoff for next iteration */
span = (iE1 - iS1) + (iE2 - iS2);
bestScore = -9223300000*(sqlite3_int64)1000000000;
score = 0;
iSXb = iSXp = iS1;
iEXb = iEXp = iS1;
iSYb = iSYp = iS2;
iEYb = iEYp = iS2;
mid = (iE1 + iS1)/2;
do{
|
| ︙ | ︙ | |||
2995 2996 2997 2998 2999 3000 3001 |
/* Prepare the input files */
memset(&c, 0, sizeof(c));
if( (pCfg->diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){
c.xDiffer = compare_dline_ignore_allws;
}else{
c.xDiffer = compare_dline;
}
| > > > > > > | | | | > | 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 |
/* Prepare the input files */
memset(&c, 0, sizeof(c));
if( (pCfg->diffFlags & DIFF_IGNORE_ALLWS)==DIFF_IGNORE_ALLWS ){
c.xDiffer = compare_dline_ignore_allws;
}else{
c.xDiffer = compare_dline;
}
if( pCfg->diffFlags & DIFF_BY_TOKEN ){
c.aFrom = break_into_tokens(blob_str(pA_Blob), blob_size(pA_Blob),
&c.nFrom, pCfg->diffFlags);
c.aTo = break_into_tokens(blob_str(pB_Blob), blob_size(pB_Blob),
&c.nTo, pCfg->diffFlags);
}else{
c.aFrom = break_into_lines(blob_str(pA_Blob), blob_size(pA_Blob),
&c.nFrom, pCfg->diffFlags);
c.aTo = break_into_lines(blob_str(pB_Blob), blob_size(pB_Blob),
&c.nTo, pCfg->diffFlags);
}
if( c.aFrom==0 || c.aTo==0 ){
fossil_free(c.aFrom);
fossil_free(c.aTo);
if( pOut ){
diff_errmsg(pOut, DIFF_CANNOT_COMPUTE_BINARY, pCfg->diffFlags);
}
return 0;
|
| ︙ | ︙ | |||
3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 |
if( pOut ) diff_errmsg(pOut, DIFF_TOO_MANY_CHANGES, pCfg->diffFlags);
return 0;
}
}
if( (pCfg->diffFlags & DIFF_NOOPT)==0 ){
diff_optimize(&c);
}
if( pOut ){
if( pCfg->diffFlags & DIFF_NUMSTAT ){
int nDel = 0, nIns = 0, i;
for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
nDel += c.aEdit[i+1];
nIns += c.aEdit[i+2];
}
g.diffCnt[1] += nIns;
g.diffCnt[2] += nDel;
if( nIns+nDel ){
g.diffCnt[0]++;
blob_appendf(pOut, "%10d %10d", nIns, nDel);
}
| > > > > > > > > > > > > > > > > | | 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 |
if( pOut ) diff_errmsg(pOut, DIFF_TOO_MANY_CHANGES, pCfg->diffFlags);
return 0;
}
}
if( (pCfg->diffFlags & DIFF_NOOPT)==0 ){
diff_optimize(&c);
}
if( (pCfg->diffFlags & DIFF_BY_TOKEN)!=0 ){
/* Convert token counts into byte counts. */
int i;
int iA = 0;
int iB = 0;
for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
int k, sum;
for(k=0, sum=0; k<c.aEdit[i]; k++) sum += c.aFrom[iA++].n;
iB += c.aEdit[i];
c.aEdit[i] = sum;
for(k=0, sum=0; k<c.aEdit[i+1]; k++) sum += c.aFrom[iA++].n;
c.aEdit[i+1] = sum;
for(k=0, sum=0; k<c.aEdit[i+2]; k++) sum += c.aTo[iB++].n;
c.aEdit[i+2] = sum;
}
}
if( pOut ){
if( pCfg->diffFlags & DIFF_NUMSTAT ){
int nDel = 0, nIns = 0, i;
for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
nDel += c.aEdit[i+1];
nIns += c.aEdit[i+2];
}
g.diffCnt[1] += nIns;
g.diffCnt[2] += nDel;
if( nIns+nDel ){
g.diffCnt[0]++;
blob_appendf(pOut, "%10d %10d", nIns, nDel);
}
}else if( pCfg->diffFlags & (DIFF_RAW|DIFF_BY_TOKEN) ){
const int *R = c.aEdit;
unsigned int r;
for(r=0; R[r] || R[r+1] || R[r+2]; r += 3){
blob_appendf(pOut, " copy %6d delete %6d insert %6d\n",
R[r], R[r+1], R[r+2]);
}
}else if( pCfg->diffFlags & DIFF_JSON ){
|
| ︙ | ︙ | |||
3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 |
/*
** Initialize the DiffConfig object using command-line options.
**
** Process diff-related command-line options and return an appropriate
** "diffFlags" integer.
**
** --brief Show filenames only DIFF_BRIEF
** -c|--context N N lines of context. nContext
** --html Format for HTML DIFF_HTML
** --invert Invert the diff DIFF_INVERT
** -n|--linenum Show line numbers DIFF_LINENO
** --noopt Disable optimization DIFF_NOOPT
** --numstat Show change counts DIFF_NUMSTAT
** --strip-trailing-cr Strip trailing CR DIFF_STRIP_EOLCR
** --unified Unified diff. ~DIFF_SIDEBYSIDE
** -w|--ignore-all-space Ignore all whitespaces DIFF_IGNORE_ALLWS
** -W|--width N N character lines. wColumn
** -y|--side-by-side Side-by-side diff. DIFF_SIDEBYSIDE
** -Z|--ignore-trailing-space Ignore eol-whitespaces DIFF_IGNORE_EOLWS
*/
void diff_options(DiffConfig *pCfg, int isGDiff, int bUnifiedTextOnly){
u64 diffFlags = 0;
const char *z;
| > > > > > > > > > | 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 |
/*
** Initialize the DiffConfig object using command-line options.
**
** Process diff-related command-line options and return an appropriate
** "diffFlags" integer.
**
** -b|--browser Show the diff output in a web-browser
** --brief Show filenames only DIFF_BRIEF
** --by Shorthand for "--browser -y"
** -c|--context N N lines of context. nContext
** --dark Use dark mode for Tcl/Tk and HTML output
** --html Format for HTML DIFF_HTML
** -i|--internal Use built-in diff, not an external tool
** --invert Invert the diff DIFF_INVERT
** --json Output formatted as JSON
** -n|--linenum Show line numbers DIFF_LINENO
** -N|--new-file Alias for --verbose
** --noopt Disable optimization DIFF_NOOPT
** --numstat Show change counts DIFF_NUMSTAT
** --strip-trailing-cr Strip trailing CR DIFF_STRIP_EOLCR
** --tcl Tcl-formatted output used internally by --tk
** --unified Unified diff. ~DIFF_SIDEBYSIDE
** -v|--verbose Show complete text of added or deleted files
** -w|--ignore-all-space Ignore all whitespaces DIFF_IGNORE_ALLWS
** --webpage Format output as a stand-alone HTML webpage
** -W|--width N N character lines. wColumn
** -y|--side-by-side Side-by-side diff. DIFF_SIDEBYSIDE
** -Z|--ignore-trailing-space Ignore eol-whitespaces DIFF_IGNORE_EOLWS
*/
void diff_options(DiffConfig *pCfg, int isGDiff, int bUnifiedTextOnly){
u64 diffFlags = 0;
const char *z;
|
| ︙ | ︙ | |||
3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 |
diffFlags |= DIFF_TCL;
}
/* Undocumented and unsupported flags used for development
** debugging and analysis: */
if( find_option("debug",0,0)!=0 ) diffFlags |= DIFF_DEBUG;
if( find_option("raw",0,0)!=0 ) diffFlags |= DIFF_RAW;
}
if( (z = find_option("context","c",1))!=0 ){
char *zEnd;
f = (int)strtol(z, &zEnd, 10);
if( zEnd[0]==0 && errno!=ERANGE ){
pCfg->nContext = f;
diffFlags |= DIFF_CONTEXT_EX;
| > > > | 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 |
diffFlags |= DIFF_TCL;
}
/* Undocumented and unsupported flags used for development
** debugging and analysis: */
if( find_option("debug",0,0)!=0 ) diffFlags |= DIFF_DEBUG;
if( find_option("raw",0,0)!=0 ) diffFlags |= DIFF_RAW;
if( find_option("bytoken",0,0)!=0 ){
diffFlags = DIFF_RAW|DIFF_BY_TOKEN;
}
}
if( (z = find_option("context","c",1))!=0 ){
char *zEnd;
f = (int)strtol(z, &zEnd, 10);
if( zEnd[0]==0 && errno!=ERANGE ){
pCfg->nContext = f;
diffFlags |= DIFF_CONTEXT_EX;
|
| ︙ | ︙ |
Changes to src/diff.tcl.
1 2 3 4 5 |
# The "diff --tk" command outputs prepends a "set fossilcmd {...}" line
# to this file, then runs this file using "tclsh" in order to display the
# graphical diff in a separate window. A typical "set fossilcmd" line
# looks like this:
#
| | | 1 2 3 4 5 6 7 8 9 10 11 12 13 |
# The "diff --tk" command outputs prepends a "set fossilcmd {...}" line
# to this file, then runs this file using "tclsh" in order to display the
# graphical diff in a separate window. A typical "set fossilcmd" line
# looks like this:
#
# set fossilcmd {| "./fossil" diff --tcl -i -v}
#
# This header comment is stripped off by the "mkbuiltin.c" program.
#
set prog {
package require Tk
array set CFG_light {
|
| ︙ | ︙ | |||
108 109 110 111 112 113 114 |
array set widths {txt 3 ln 3 mkr 1}
set fromIndex [lsearch -glob $fossilcmd *-from]
set toIndex [lsearch -glob $fossilcmd *-to]
set branchIndex [lsearch -glob $fossilcmd *-branch]
set checkinIndex [lsearch -glob $fossilcmd *-checkin]
| > > > | > | 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
array set widths {txt 3 ln 3 mkr 1}
set fromIndex [lsearch -glob $fossilcmd *-from]
set toIndex [lsearch -glob $fossilcmd *-to]
set branchIndex [lsearch -glob $fossilcmd *-branch]
set checkinIndex [lsearch -glob $fossilcmd *-checkin]
if {[string match *?--external-baseline* $fossilcmd]} {
set fA {external baseline}
} else {
set fA {base check-in}
}
set fB {current check-out}
if {$fromIndex > -1} {set fA [lindex $fossilcmd $fromIndex+1]}
if {$toIndex > -1} {set fB [lindex $fossilcmd $toIndex+1]}
if {$branchIndex > -1} {set fA "branch point"; set fB "leaf of branch '[lindex $fossilcmd $branchIndex+1]'"}
if {$checkinIndex > -1} {set fA "primary parent"; set fB [lindex $fossilcmd $checkinIndex+1]}
|
| ︙ | ︙ |
Changes to src/diffcmd.c.
| ︙ | ︙ | |||
247 248 249 250 251 252 253 |
@ }
@ table.diff pre {
@ margin: 0 0 0 0;
@ line-height: inherit;
@ font-size: inherit;
@ }
@ td.diffln {
| | | | 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
@ }
@ table.diff pre {
@ margin: 0 0 0 0;
@ line-height: inherit;
@ font-size: inherit;
@ }
@ td.diffln {
@ width: fit-content;
@ text-align: right;
@ padding: 0 1em 0 0;
@ }
@ td.difflne {
@ padding-bottom: 0.4em;
@ }
@ td.diffsep {
@ width: fit-content;
@ padding: 0 0.3em 0 1em;
@ line-height: inherit;
@ font-size: inherit;
@ }
@ td.diffsep pre {
@ line-height: inherit;
@ font-size: inherit;
|
| ︙ | ︙ | |||
377 378 379 380 381 382 383 |
@ }
@ table.diff pre {
@ margin: 0 0 0 0;
@ line-height: inherit;
@ font-size: inherit;
@ }
@ td.diffln {
| | | | 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 |
@ }
@ table.diff pre {
@ margin: 0 0 0 0;
@ line-height: inherit;
@ font-size: inherit;
@ }
@ td.diffln {
@ width: fit-content;
@ text-align: right;
@ padding: 0 1em 0 0;
@ }
@ td.difflne {
@ padding-bottom: 0.4em;
@ }
@ td.diffsep {
@ width: fit-content;
@ padding: 0 0.3em 0 1em;
@ line-height: inherit;
@ font-size: inherit;
@ }
@ td.diffsep pre {
@ line-height: inherit;
@ font-size: inherit;
|
| ︙ | ︙ | |||
782 783 784 785 786 787 788 |
rc = memcmp(blob_buffer(&file), blob_buffer(blob), blob_size(&file))==0;
}
blob_reset(&file);
return rc;
}
/*
| | | | > | | | 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 |
rc = memcmp(blob_buffer(&file), blob_buffer(blob), blob_size(&file))==0;
}
blob_reset(&file);
return rc;
}
/*
** Run a diff between the version zFrom and files on disk in the current
** working checkout. zFrom might be NULL which means to simply show the
** difference between the edited files on disk and the check-out on which
** they are based.
**
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
** command zDiffCmd to do the diffing.
**
** When using an external diff program, zBinGlob contains the GLOB patterns
** for file names to treat as binary. If fIncludeBinary is zero, these files
** will be skipped in addition to files that may contain binary content.
*/
void diff_version_to_checkout(
const char *zFrom, /* Version to difference from */
DiffConfig *pCfg, /* Flags controlling diff output */
FileDirList *pFileDir, /* Which files to diff */
Blob *pOut /* Blob to output diff instead of stdout */
){
int vid;
Blob sql;
Stmt q;
int asNewFile; /* Treat non-existant files as empty files */
int isNumStat; /* True for --numstat */
|
| ︙ | ︙ | |||
926 927 928 929 930 931 932 |
blob_reset(&fname);
}
db_finalize(&q);
db_end_transaction(1); /* ROLLBACK */
}
/*
| | | | 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 |
blob_reset(&fname);
}
db_finalize(&q);
db_end_transaction(1); /* ROLLBACK */
}
/*
** Run a diff from the undo buffer to files on disk.
**
** Use the internal diff logic if zDiffCmd is NULL. Otherwise call the
** command zDiffCmd to do the diffing.
**
** When using an external diff program, zBinGlob contains the GLOB patterns
** for file names to treat as binary. If fIncludeBinary is zero, these files
** will be skipped in addition to files that may contain binary content.
*/
static void diff_undo_to_checkout(
DiffConfig *pCfg, /* Flags controlling diff output */
FileDirList *pFileDir /* List of files and directories to diff */
){
Stmt q;
Blob content;
db_prepare(&q, "SELECT pathname, content FROM undo");
blob_init(&content, 0, 0);
|
| ︙ | ︙ | |||
1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 |
pFromFile = manifest_file_next(pFrom,0);
pToFile = manifest_file_next(pTo,0);
}
}
manifest_destroy(pFrom);
manifest_destroy(pTo);
}
/*
** Return the name of the external diff command, or return NULL if
** no external diff command is defined.
*/
const char *diff_command_external(int guiDiff){
const char *zDefault;
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 |
pFromFile = manifest_file_next(pFrom,0);
pToFile = manifest_file_next(pTo,0);
}
}
manifest_destroy(pFrom);
manifest_destroy(pTo);
}
/*
** Compute the difference from an external tree of files to the current
** working checkout with its edits.
**
** To put it another way: Every managed file in the current working
** checkout is compared to the file with same name under zExternBase. The
** zExternBase files are on the left and the files in the current working
** directory are on the right.
*/
void diff_externbase_to_checkout(
const char *zExternBase, /* Remote tree to use as the baseline */
DiffConfig *pCfg, /* Diff settings */
FileDirList *pFileDir /* Only look at these files */
){
int vid;
Stmt q;
vid = db_lget_int("checkout",0);
if( file_isdir(zExternBase, ExtFILE)!=1 ){
fossil_fatal("\"%s\" is not a directory", zExternBase);
}
db_prepare(&q,
"SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname",
vid
);
while( db_step(&q)==SQLITE_ROW ){
const char *zFile; /* Name of file in the repository */
char *zLhs; /* Full name of left-hand side file */
char *zRhs; /* Full name of right-hand side file */
Blob rhs; /* Full text of RHS */
Blob lhs; /* Full text of LHS */
zFile = db_column_text(&q,0);
if( !file_dir_match(pFileDir, zFile) ) continue;
zLhs = mprintf("%s/%s", zExternBase, zFile);
zRhs = mprintf("%s%s", g.zLocalRoot, zFile);
if( file_size(zLhs, ExtFILE)<0 ){
blob_zero(&lhs);
}else{
blob_read_from_file(&lhs, zLhs, ExtFILE);
}
blob_read_from_file(&rhs, zRhs, ExtFILE);
if( blob_size(&lhs)!=blob_size(&rhs)
|| memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0
){
diff_print_index(zFile, pCfg, 0);
diff_file_mem(&lhs, &rhs, zFile, pCfg);
}
blob_reset(&lhs);
blob_reset(&rhs);
fossil_free(zLhs);
fossil_free(zRhs);
}
db_finalize(&q);
}
/*
** Return the name of the external diff command, or return NULL if
** no external diff command is defined.
*/
const char *diff_command_external(int guiDiff){
const char *zDefault;
|
| ︙ | ︙ | |||
1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 | ** for the diff operation. If not specified, the source check-in is the ** base check-in for the current check-out. Similarly, the "--to VERSION" ** option specifies the check-in from which the second version of the file ** or files is taken. If there is no "--to" option then the (possibly edited) ** files in the current check-out are used. The "--checkin VERSION" option ** shows the changes made by check-in VERSION relative to its primary parent. ** The "--branch BRANCHNAME" shows all the changes on the branch BRANCHNAME. ** ** The "-i" command-line option forces the use of Fossil's own internal ** diff logic rather than any external diff program that might be configured ** using the "setting" command. If no external diff program is configured, ** then the "-i" option is a no-op. The "-i" option converts "gdiff" into ** "diff". ** | > > > > | 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 | ** for the diff operation. If not specified, the source check-in is the ** base check-in for the current check-out. Similarly, the "--to VERSION" ** option specifies the check-in from which the second version of the file ** or files is taken. If there is no "--to" option then the (possibly edited) ** files in the current check-out are used. The "--checkin VERSION" option ** shows the changes made by check-in VERSION relative to its primary parent. ** The "--branch BRANCHNAME" shows all the changes on the branch BRANCHNAME. ** ** With the "--from VERSION" option, if VERSION is actually a directory name ** (not a tag or check-in hash) then the files under that directory are used ** as the baseline for the diff. ** ** The "-i" command-line option forces the use of Fossil's own internal ** diff logic rather than any external diff program that might be configured ** using the "setting" command. If no external diff program is configured, ** then the "-i" option is a no-op. The "-i" option converts "gdiff" into ** "diff". ** |
| ︙ | ︙ | |||
1254 1255 1256 1257 1258 1259 1260 | ** --command PROG External diff program. Overrides "diff-command" ** -c|--context N Show N lines of context around each change, ** with negative N meaning show all content ** --dark Use dark mode for the Tcl/Tk-based GUI and HTML ** --diff-binary BOOL Include binary files with external commands ** --exec-abs-paths Force absolute path names on external commands ** --exec-rel-paths Force relative path names on external commands | | > > | | > | | < < > > > > > > > > > > > | 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 |
** --command PROG External diff program. Overrides "diff-command"
** -c|--context N Show N lines of context around each change,
** with negative N meaning show all content
** --dark Use dark mode for the Tcl/Tk-based GUI and HTML
** --diff-binary BOOL Include binary files with external commands
** --exec-abs-paths Force absolute path names on external commands
** --exec-rel-paths Force relative path names on external commands
** -r|--from VERSION Use VERSION as the baseline for the diff, or
** if VERSION is a directory name, use files in
** that directory as the baseline.
** -w|--ignore-all-space Ignore white space when comparing lines
** -i|--internal Use internal diff logic
** --invert Invert the diff
** --json Output formatted as JSON
** -n|--linenum Show line numbers
** -N|--new-file Alias for --verbose
** --numstat Show only the number of added and deleted lines
** -y|--side-by-side Side-by-side diff
** --strip-trailing-cr Strip trailing CR
** --tcl Tcl-formatted output used internally by --tk
** --tclsh PATH Tcl/Tk shell used for --tk (default: "tclsh")
** --tk Launch a Tcl/Tk GUI for display
** --to VERSION Select VERSION as target for the diff
** --undo Use the undo buffer as the baseline
** --unified Unified diff
** -v|--verbose Output complete text of added or deleted files
** -h|--versions Show compared versions in the diff header
** --webpage Format output as a stand-alone HTML webpage
** -W|--width N Width of lines in side-by-side diff
** -Z|--ignore-trailing-space Ignore changes to end-of-line whitespace
*/
void diff_cmd(void){
int isGDiff; /* True for gdiff. False for normal diff */
const char *zFrom; /* Source version number */
const char *zTo; /* Target version number */
const char *zCheckin; /* Check-in version number */
const char *zBranch; /* Branch to diff */
int againstUndo = 0; /* Diff against files in the undo buffer */
FileDirList *pFileDir = 0; /* Restrict the diff to these files */
DiffConfig DCfg; /* Diff configuration object */
int bFromIsDir = 0; /* True if zFrom is a directory name */
if( find_option("tk",0,0)!=0 || has_option("tclsh") ){
diff_tk("diff", 2);
return;
}
isGDiff = g.argv[1][0]=='g';
zFrom = find_option("from", "r", 1);
zTo = find_option("to", 0, 1);
zCheckin = find_option("checkin", "ci", 1);
zBranch = find_option("branch", 0, 1);
againstUndo = find_option("undo",0,0)!=0;
if( againstUndo && (zFrom!=0 || zTo!=0 || zCheckin!=0 || zBranch!=0) ){
fossil_fatal("cannot use --undo together with --from, --to, --checkin,"
" or --branch");
}
if( zBranch ){
if( zTo || zFrom || zCheckin ){
fossil_fatal("cannot use --from, --to, or --checkin with --branch");
}
zTo = zBranch;
zFrom = mprintf("root:%s", zBranch);
}
if( zCheckin!=0 && (zFrom!=0 || zTo!=0) ){
fossil_fatal("cannot use --checkin together with --from or --to");
}
if( 0==zCheckin ){
if( zTo==0 || againstUndo ){
db_must_be_within_tree();
}else if( zFrom==0 ){
fossil_fatal("must use --from if --to is present");
}else{
db_find_and_open_repository(0, 0);
}
}else{
db_find_and_open_repository(0, 0);
}
determine_exec_relative_option(1);
if( zFrom!=file_tail(zFrom)
&& file_isdir(zFrom, ExtFILE)==1
&& !db_exists("SELECT 1 FROM tag WHERE tagname='sym-%q'", zFrom)
){
bFromIsDir = 1;
if( zTo ){
fossil_fatal("cannot use --to together with \"--from PATH\"");
}
}
diff_options(&DCfg, isGDiff, 0);
verify_all_options();
g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0;
if( g.argc>=3 ){
int i;
Blob fname;
pFileDir = fossil_malloc( sizeof(*pFileDir) * (g.argc-1) );
memset(pFileDir, 0, sizeof(*pFileDir) * (g.argc-1));
for(i=2; i<g.argc; i++){
file_tree_name(g.argv[i], &fname, 0, 1);
|
| ︙ | ︙ | |||
1355 1356 1357 1358 1359 1360 1361 |
" WHERE plink.cid=%d AND plink.isprim AND plink.pid=blob.rid",
ridTo);
if( zFrom==0 ){
fossil_fatal("check-in %s has no parent", zTo);
}
}
diff_begin(&DCfg);
| > > | | | | 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 |
" WHERE plink.cid=%d AND plink.isprim AND plink.pid=blob.rid",
ridTo);
if( zFrom==0 ){
fossil_fatal("check-in %s has no parent", zTo);
}
}
diff_begin(&DCfg);
if( bFromIsDir ){
diff_externbase_to_checkout(zFrom, &DCfg, pFileDir);
}else if( againstUndo ){
if( db_lget_int("undo_available",0)==0 ){
fossil_print("No undo or redo is available\n");
return;
}
diff_undo_to_checkout(&DCfg, pFileDir);
}else if( zTo==0 ){
diff_version_to_checkout(zFrom, &DCfg, pFileDir, 0);
}else{
diff_two_versions(zFrom, zTo, &DCfg, pFileDir);
}
if( pFileDir ){
int i;
for(i=0; pFileDir[i].zName; i++){
if( pFileDir[i].nUsed==0
|
| ︙ | ︙ |
Changes to src/dispatch.c.
| ︙ | ︙ | |||
1305 1306 1307 1308 1309 1310 1311 |
fossil_print("%s\n", blob_str(&txt));
blob_reset(&txt);
}
/*
** Return a pointer to the setting information array.
**
| | > > > > > > > > > > > > > > > > > > > > > > > > > | 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 |
fossil_print("%s\n", blob_str(&txt));
blob_reset(&txt);
}
/*
** Return a pointer to the setting information array.
**
** This routine provides access to the aSetting[] array which is created
** by the mkindex utility program and included with <page_index.h>.
*/
const Setting *setting_info(int *pnCount){
if( pnCount ) *pnCount = (int)(sizeof(aSetting)/sizeof(aSetting[0])) - 1;
return aSetting;
}
/*
** Return a pointer to a specific Setting entry for the setting named
** in the argument. Or return NULL if no such setting exists.
**
** The pointer returned points into the middle of the global aSetting[]
** array that is generated by mkindex. Use setting_info() to fetch the
** whole array. Use this routine to fetch a specific entry.
*/
const Setting *setting_find(const char *zName){
int iFirst = 0;
int iLast = ArraySize(aSetting)-1;
while( iFirst<=iLast ){
int iCur = (iFirst+iLast)/2;
int c = strcmp(aSetting[iCur].name, zName);
if( c<0 ){
iFirst = iCur+1;
}else if( c>0 ){
iLast = iCur-1;
}else{
return &aSetting[iCur];
}
}
return 0;
}
/*****************************************************************************
** A virtual table for accessing the information in aCommand[], and
** especially the help-text
*/
/* helptextVtab_vtab is a subclass of sqlite3_vtab which is
|
| ︙ | ︙ |
Changes to src/doc.c.
| ︙ | ︙ | |||
786 787 788 789 790 791 792 |
const char *zDefaultTitle, /* Default title */
const char *zFilename /* Name of the file being rendered */
){
Blob title;
int isPopup = P("popup")!=0;
blob_init(&title,0,0);
if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){
| | > > | 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 |
const char *zDefaultTitle, /* Default title */
const char *zFilename /* Name of the file being rendered */
){
Blob title;
int isPopup = P("popup")!=0;
blob_init(&title,0,0);
if( fossil_strcmp(zMime, "text/x-fossil-wiki")==0 ){
Blob tail = BLOB_INITIALIZER;
style_adunit_config(ADUNIT_RIGHT_OK);
if( wiki_find_title(pBody, &title, &tail) ){
if( !isPopup ) style_header("%s", blob_str(&title));
wiki_convert(&tail, 0, WIKI_BUTTONS);
}else{
if( !isPopup ) style_header("%s", zDefaultTitle);
wiki_convert(pBody, 0, WIKI_BUTTONS);
}
if( !isPopup ){
document_emit_js();
style_finish_page();
}
blob_reset(&tail);
}else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
Blob tail = BLOB_INITIALIZER;
markdown_to_html(pBody, &title, &tail);
if( !isPopup ){
if( blob_size(&title)>0 ){
style_header("%s", blob_str(&title));
}else{
style_header("%s", zDefaultTitle);
}
}
convert_href_and_output(&tail);
if( !isPopup ){
document_emit_js();
style_finish_page();
}
blob_reset(&tail);
}else if( fossil_strcmp(zMime, "text/plain")==0 ){
style_header("%s", zDefaultTitle);
@ <blockquote><pre>
@ %h(blob_str(pBody))
@ </pre></blockquote>
document_emit_js();
style_finish_page();
|
| ︙ | ︙ | |||
947 948 949 950 951 952 953 954 955 956 957 958 959 960 |
#endif
};
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
style_set_current_feature("doc");
blob_init(&title, 0, 0);
zDfltTitle = isUV ? "" : "Documentation";
db_begin_transaction();
while( rid==0 && (++nMiss)<=count(azSuffix) ){
zName = P("name");
if( isUV ){
if( zName==0 ) zName = "index.wiki";
i = 0;
| > | 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 |
#endif
};
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
style_set_current_feature("doc");
blob_init(&title, 0, 0);
blob_init(&filebody, 0, 0);
zDfltTitle = isUV ? "" : "Documentation";
db_begin_transaction();
while( rid==0 && (++nMiss)<=count(azSuffix) ){
zName = P("name");
if( isUV ){
if( zName==0 ) zName = "index.wiki";
i = 0;
|
| ︙ | ︙ | |||
1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 |
Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event"
" WHERE objid=%d AND type='ci'", vid));
}
cgi_check_for_malice();
document_render(&filebody, zMime, zDfltTitle, zName);
if( nMiss>=count(azSuffix) ) cgi_set_status(404, "Not Found");
db_end_transaction(0);
return;
/* Jump here when unable to locate the document */
doc_not_found:
db_end_transaction(0);
if( isUV && P("name")==0 ){
uvlist_page();
return;
}
cgi_set_status(404, "Not Found");
style_header("Not Found");
@ <p>Document %h(zOrigName) not found
if( fossil_strcmp(zCheckin,"ckout")!=0 ){
@ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a>
}
style_finish_page();
return;
}
/*
** The default logo.
*/
static const unsigned char aLogo[] = {
| > > > > | 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 |
Th_Store("doc_date", db_text(0, "SELECT datetime(mtime) FROM event"
" WHERE objid=%d AND type='ci'", vid));
}
cgi_check_for_malice();
document_render(&filebody, zMime, zDfltTitle, zName);
if( nMiss>=count(azSuffix) ) cgi_set_status(404, "Not Found");
db_end_transaction(0);
blob_reset(&title);
blob_reset(&filebody);
return;
/* Jump here when unable to locate the document */
doc_not_found:
db_end_transaction(0);
if( isUV && P("name")==0 ){
uvlist_page();
return;
}
cgi_set_status(404, "Not Found");
style_header("Not Found");
@ <p>Document %h(zOrigName) not found
if( fossil_strcmp(zCheckin,"ckout")!=0 ){
@ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a>
}
style_finish_page();
blob_reset(&title);
blob_reset(&filebody);
return;
}
/*
** The default logo.
*/
static const unsigned char aLogo[] = {
|
| ︙ | ︙ |
Changes to src/encode.c.
| ︙ | ︙ | |||
727 728 729 730 731 732 733 734 735 736 737 738 739 740 |
*/
void canonical16(char *z, int n){
while( *z && n-- ){
*z = zEncode[zDecode[(*z)&0x7f]&0x1f];
z++;
}
}
/*
** Decode a string encoded using "quoted-printable".
**
** (1) "=" followed by two hex digits becomes a single
** byte specified by the two digits
**
| > > > > > > > > > > > > > > > > > > | 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 |
*/
void canonical16(char *z, int n){
while( *z && n-- ){
*z = zEncode[zDecode[(*z)&0x7f]&0x1f];
z++;
}
}
/*
** Decode hexadecimal into a string and return the new string. Space to
** hold the string is obtained from fossil_malloc() and should be released
** by the caller.
**
** If the input is not hex, return NULL.
*/
char *decode16_dup(const char *zIn){
int nIn = (int)strlen(zIn);
char *zOut;
if( !validate16(zIn, nIn) ) return 0;
zOut = fossil_malloc(nIn/2+1);
decode16((const u8*)zIn, (u8*)zOut, nIn);
zOut[nIn/2] = 0;
return zOut;
}
/*
** Decode a string encoded using "quoted-printable".
**
** (1) "=" followed by two hex digits becomes a single
** byte specified by the two digits
**
|
| ︙ | ︙ |
Changes to src/event.c.
| ︙ | ︙ | |||
227 228 229 230 231 232 233 |
@ %h(blob_str(&fullbody))
@ </pre>
}
zFullId = db_text(0, "SELECT SUBSTR(tagname,7)"
" FROM tag"
" WHERE tagname GLOB 'event-%q*'",
zId);
| | | 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 |
@ %h(blob_str(&fullbody))
@ </pre>
}
zFullId = db_text(0, "SELECT SUBSTR(tagname,7)"
" FROM tag"
" WHERE tagname GLOB 'event-%q*'",
zId);
attachment_list(zFullId, "<h2>Attachments:</h2>", 1);
document_emit_js();
style_finish_page();
manifest_destroy(pTNote);
}
/*
** Add or update a new tech note to the repository. rid is id of
|
| ︙ | ︙ |
Changes to src/file.c.
| ︙ | ︙ | |||
1335 1336 1337 1338 1339 1340 1341 |
}else{
return 0;
}
}
/*
** Compute a canonical pathname for a file or directory.
| > | | | | > > > > > > > > > > > | > < | 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 |
}else{
return 0;
}
}
/*
** Compute a canonical pathname for a file or directory.
**
** * Make the name absolute if it is relative.
** * Remove redundant / characters
** * Remove all /./ path elements.
** * Convert /A/../ to just /
** * On windows, add the drive letter prefix.
**
** If the slash parameter is non-zero, the trailing slash, if any,
** is retained.
**
** See also: file_canonical_name_dup()
*/
void file_canonical_name(const char *zOrigName, Blob *pOut, int slash){
char zPwd[2000];
blob_zero(pOut);
if( file_is_absolute_path(zOrigName) ){
#if defined(_WIN32)
if( fossil_isdirsep(zOrigName[0]) ){
/* Add the drive letter to the full pathname */
file_getcwd(zPwd, sizeof(zPwd)-strlen(zOrigName));
blob_appendf(pOut, "%.2s%/", zPwd, zOrigName);
}else
#endif
{
blob_appendf(pOut, "%/", zOrigName);
}
}else{
file_getcwd(zPwd, sizeof(zPwd)-strlen(zOrigName));
if( zPwd[0]=='/' && strlen(zPwd)==1 ){
/* when on '/', don't add an extra '/' */
if( zOrigName[0]=='.' && strlen(zOrigName)==1 ){
/* '.' when on '/' mean '/' */
blob_appendf(pOut, "%/", zPwd);
}else{
|
| ︙ | ︙ | |||
1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 |
filenames_are_case_sensitive());
if( zAllow ){
g.allowSymlinks = !is_false(zAllow);
}
if( zRoot==0 ) zRoot = g.zLocalRoot==0 ? "" : g.zLocalRoot;
fossil_print("db_allow_symlinks() = %d\n", db_allow_symlinks());
fossil_print("local-root = [%s]\n", zRoot);
for(i=2; i<g.argc; i++){
char *z;
emitFileStat(g.argv[i], slashFlag, resetFlag);
z = file_canonical_name_dup(g.argv[i]);
fossil_print(" file_canonical_name = %s\n", z);
fossil_print(" file_nondir_path = ");
if( fossil_strnicmp(zRoot,z,(int)strlen(zRoot))!=0 ){
fossil_print("(--root is not a prefix of this file)\n");
}else{
int n = file_nondir_objects_on_path(zRoot, z);
fossil_print("%.*s\n", n, z);
}
fossil_free(z);
}
}
/*
** COMMAND: test-canonical-name
**
** Usage: %fossil test-canonical-name FILENAME...
| > > > > > > | 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 |
filenames_are_case_sensitive());
if( zAllow ){
g.allowSymlinks = !is_false(zAllow);
}
if( zRoot==0 ) zRoot = g.zLocalRoot==0 ? "" : g.zLocalRoot;
fossil_print("db_allow_symlinks() = %d\n", db_allow_symlinks());
fossil_print("local-root = [%s]\n", zRoot);
if( g.db==0 ) sqlite3_open(":memory:", &g.db);
sqlite3_create_function(g.db, "inode", 1, SQLITE_UTF8, 0,
file_inode_sql_func, 0, 0);
for(i=2; i<g.argc; i++){
char *z;
emitFileStat(g.argv[i], slashFlag, resetFlag);
z = file_canonical_name_dup(g.argv[i]);
fossil_print(" file_canonical_name = %s\n", z);
fossil_print(" file_nondir_path = ");
if( fossil_strnicmp(zRoot,z,(int)strlen(zRoot))!=0 ){
fossil_print("(--root is not a prefix of this file)\n");
}else{
int n = file_nondir_objects_on_path(zRoot, z);
fossil_print("%.*s\n", n, z);
}
fossil_free(z);
z = db_text(0, "SELECT inode(%Q)", g.argv[i]);
fossil_print(" file_inode_sql_func = \"%s\"\n", z);
fossil_free(z);
}
}
/*
** COMMAND: test-canonical-name
**
** Usage: %fossil test-canonical-name FILENAME...
|
| ︙ | ︙ | |||
1732 1733 1734 1735 1736 1737 1738 |
** Return TRUE if the given filename is canonical.
**
** Canonical names are full pathnames using "/" not "\" and which
** contain no "/./" or "/../" terms.
*/
int file_is_canonical(const char *z){
int i;
| | | > > | 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 |
** Return TRUE if the given filename is canonical.
**
** Canonical names are full pathnames using "/" not "\" and which
** contain no "/./" or "/../" terms.
*/
int file_is_canonical(const char *z){
int i;
if(
#if defined(_WIN32) || defined(__CYGWIN__)
!fossil_isupper(z[0]) || z[1]!=':' || !fossil_isdirsep(z[2])
#else
z[0]!='/'
#endif
) return 0;
for(i=0; z[i]; i++){
if( z[i]=='\\' ) return 0;
if( z[i]=='/' ){
if( z[i+1]=='.' ){
|
| ︙ | ︙ | |||
2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 |
/*
** Returns 1 if the given directory contains a file named .fslckout, 2
** if it contains a file named _FOSSIL_, else returns 0.
*/
int dir_has_ckout_db(const char *zDir){
int rc = 0;
char * zCkoutDb = mprintf("%//.fslckout", zDir);
if(file_isfile(zCkoutDb, ExtFILE)){
rc = 1;
}else{
fossil_free(zCkoutDb);
zCkoutDb = mprintf("%//_FOSSIL_", zDir);
if(file_isfile(zCkoutDb, ExtFILE)){
rc = 2;
}
}
fossil_free(zCkoutDb);
return rc;
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 |
/*
** Returns 1 if the given directory contains a file named .fslckout, 2
** if it contains a file named _FOSSIL_, else returns 0.
*/
int dir_has_ckout_db(const char *zDir){
int rc = 0;
i64 sz;
char * zCkoutDb = mprintf("%//.fslckout", zDir);
if(file_isfile(zCkoutDb, ExtFILE)){
rc = 1;
}else{
fossil_free(zCkoutDb);
zCkoutDb = mprintf("%//_FOSSIL_", zDir);
if(file_isfile(zCkoutDb, ExtFILE)){
rc = 2;
}
}
if( rc && ((sz = file_size(zCkoutDb, ExtFILE))<1024 || (sz%512)!=0) ){
rc = 0;
}
fossil_free(zCkoutDb);
return rc;
}
/*
** This is the implementation of inode(FILENAME) SQL function.
**
** dev_inode(FILENAME) returns a string. If FILENAME exists and is
** a regular file, then the return string is of the form:
**
** DEV/INODE
**
** Where DEV and INODE are the device number and inode number for
** the file. On Windows, the volume serial number (DEV) and file
** identifier (INODE) are used to compute the value, see comments
** on the win32_file_id() function.
**
** If FILENAME does not exist, then the return is an empty string.
**
** The value of inode() can be used to eliminate files from a list
** that have duplicates because they have differing names due to links.
**
** Code that wants to use this SQL function needs to first register
** it using a call such as the following:
**
** sqlite3_create_function(g.db, "inode", 1, SQLITE_UTF8, 0,
** file_inode_sql_func, 0, 0);
*/
void file_inode_sql_func(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *zFilename;
assert( argc==1 );
zFilename = (const char*)sqlite3_value_text(argv[0]);
if( zFilename==0 || zFilename[0]==0 || file_access(zFilename,F_OK) ){
sqlite3_result_text(context, "", 0, SQLITE_STATIC);
return;
}
#if defined(_WIN32)
{
char *zFileId = win32_file_id(zFilename);
if( zFileId ){
sqlite3_result_text(context, zFileId, -1, fossil_free);
}else{
sqlite3_result_text(context, "", 0, SQLITE_STATIC);
}
}
#else
{
struct stat buf;
int rc;
memset(&buf, 0, sizeof(buf));
rc = stat(zFilename, &buf);
if( rc ){
sqlite3_result_text(context, "", 0, SQLITE_STATIC);
}else{
sqlite3_result_text(context,
mprintf("%lld/%lld", (i64)buf.st_dev, (i64)buf.st_ino), -1,
fossil_free);
}
}
#endif
}
|
Changes to src/forum.c.
| ︙ | ︙ | |||
1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 |
forum_render_debug_options();
login_insert_csrf_secret();
@ </form>
forum_emit_js();
style_finish_page();
}
/*
** WEBPAGE: setup_forum
**
** Forum configuration and metrics.
*/
void forum_setup(void){
/* boolean config settings specific to the forum. */
| > > > > > > > > > > > > > > > | | | < < < < < < < < > > | < > < | > < < < < < < < < | > | > > > > > | > | < > > > > > > > | > | > > | | > > > | > > > | > | 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 |
forum_render_debug_options();
login_insert_csrf_secret();
@ </form>
forum_emit_js();
style_finish_page();
}
/*
** SETTING: forum-close-policy boolean default=off
** If true, forum moderators may close/re-open forum posts, and reply
** to closed posts. If false, only administrators may do so. Note that
** this only affects the forum web UI, not post-closing tags which
** arrive via the command-line or from synchronization with a remote.
*/
/*
** SETTING: forum-title width=20 default=Forum
** This is the name or "title" of the Forum for this repository. The
** default is just "Forum". But in some setups, admins might want to
** change it to "Developer Forum" or "User Forum" or whatever other name
** seems more appropriate for the particular usage.
*/
/*
** WEBPAGE: setup_forum
**
** Forum configuration and metrics.
*/
void forum_setup(void){
/* boolean config settings specific to the forum. */
static const char *azForumSettings[] = {
"forum-close-policy",
"forum-title",
};
login_check_credentials();
if( !g.perm.Setup ){
login_needed(g.anon.Setup);
return;
}
style_set_current_feature("forum");
style_header("Forum Setup");
@ <h2>Metrics</h2>
{
int nPosts = db_int(0, "SELECT COUNT(*) FROM event WHERE type='f'");
@ <p><a href='%R/forum'>Forum posts</a>:
@ <a href='%R/timeline?y=f'>%d(nPosts)</a></p>
}
@ <h2>Supervisors</h2>
{
Stmt q = empty_Stmt;
db_prepare(&q, "SELECT uid, login, cap FROM user "
"WHERE cap GLOB '*[as6]*' ORDER BY login");
@ <table class='bordered'>
@ <thead><tr><th>User</th><th>Capabilities</th></tr></thead>
@ <tbody>
while( SQLITE_ROW==db_step(&q) ){
const int iUid = db_column_int(&q, 0);
const char *zUser = db_column_text(&q, 1);
const char *zCap = db_column_text(&q, 2);
@ <tr>
@ <td><a href='%R/setup_uedit?id=%d(iUid)'>%h(zUser)</a></td>
@ <td>(%h(zCap))</td>
@ </tr>
}
db_finalize(&q);
@</tbody></table>
}
@ <h2>Moderators</h2>
if( db_int(0, "SELECT count(*) FROM user "
" WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'")==0 ){
@ <p>No non-supervisor moderators
}else{
Stmt q = empty_Stmt;
db_prepare(&q, "SELECT uid, login, cap FROM user "
"WHERE cap GLOB '*5*' AND cap NOT GLOB '*[as6]*'"
" ORDER BY login");
@ <table class='bordered'>
@ <thead><tr><th>User</th><th>Capabilities</th></tr></thead>
@ <tbody>
while( SQLITE_ROW==db_step(&q) ){
const int iUid = db_column_int(&q, 0);
const char *zUser = db_column_text(&q, 1);
const char *zCap = db_column_text(&q, 2);
@ <tr>
@ <td><a href='%R/setup_uedit?id=%d(iUid)'>%h(zUser)</a></td>
@ <td>(%h(zCap))</td>
@ </tr>
}
db_finalize(&q);
@ </tbody></table>
}
@ <h2>Settings</h2>
if( P("submit") && cgi_csrf_safe(2) ){
int i = 0;
db_begin_transaction();
for(i=0; i<ArraySize(azForumSettings); i++){
char zQP[4];
const char *z;
const Setting *pSetting = setting_find(azForumSettings[i]);
if( pSetting==0 ) continue;
zQP[0] = 'a'+i;
zQP[1] = zQP[0];
zQP[2] = 0;
z = P(zQP);
if( z==0 || z[0]==0 ) continue;
db_set(pSetting->name/*works-like:"x"*/, z, 0);
}
db_end_transaction(0);
@ <p><em>Settings saved.</em></p>
}
{
int i = 0;
@ <form action="%R/setup_forum" method="post">
login_insert_csrf_secret();
@ <table class='forum-settings-list'><tbody>
for(i=0; i<ArraySize(azForumSettings); i++){
char zQP[4];
const Setting *pSetting = setting_find(azForumSettings[i]);
if( pSetting==0 ) continue;
zQP[0] = 'a'+i;
zQP[1] = zQP[0];
zQP[2] = 0;
if( pSetting->width==0 ){
/* Boolean setting */
@ <tr><td align="right">
@ <a href='%R/help?cmd=%h(pSetting->name)'>%h(pSetting->name)</a>:
@ </td><td>
onoff_attribute("", zQP, pSetting->name/*works-like:"x"*/, 0, 0);
@ </td></tr>
}else{
/* Text value setting */
@ <tr><td align="right">
@ <a href='%R/help?cmd=%h(pSetting->name)'>%h(pSetting->name)</a>:
@ </td><td>
entry_attribute("", 25, pSetting->name, zQP/*works-like:""*/,
pSetting->def, 0);
@ </td></tr>
}
}
@ </tbody></table>
@ <input type='submit' name='submit' value='Apply changes'>
@ </form>
}
style_finish_page();
|
| ︙ | ︙ | |||
1908 1909 1910 1911 1912 1913 1914 |
srchFlags = search_restrict(SRCH_FORUM);
if( !g.perm.RdForum ){
login_needed(g.anon.RdForum);
return;
}
cgi_check_for_malice();
style_set_current_feature("forum");
| > | | 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 |
srchFlags = search_restrict(SRCH_FORUM);
if( !g.perm.RdForum ){
login_needed(g.anon.RdForum);
return;
}
cgi_check_for_malice();
style_set_current_feature("forum");
style_header("%s%s", db_get("forum-title","Forum"),
isSearch ? " Search Results" : "");
style_submenu_element("Timeline", "%R/timeline?ss=v&y=f&vfx");
if( g.perm.WrForum ){
style_submenu_element("New Thread","%R/forumnew");
}else{
/* Can't combine this with previous case using the ternary operator
* because that causes an error yelling about "non-constant format"
* with some compilers. I can't see it, since both expressions have
|
| ︙ | ︙ |
Changes to src/fossil.diff.js.
1 2 3 4 5 6 7 8 9 10 |
/**
diff-related JS APIs for fossil.
*/
"use strict";
window.fossil.onPageLoad(function(){
/**
Adds toggle checkboxes to each file entry in the diff views for
/info and similar pages.
*/
const D = window.fossil.dom;
| > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > | < > > | > > | | | > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
/**
diff-related JS APIs for fossil.
*/
"use strict";
/* Locate the UI element (if any) into which we can inject some diff-related
UI controls. */
window.fossil.onPageLoad(function(){
const potentialParents = window.fossil.page.diffControlContainers = [
/* CSS selectors for possible parents for injected diff-related UI
controls. */
/* Put the most likely pages at the end, as array.pop() is more
efficient than array.shift() (see loop below). */
/* /filedit */ 'body.cpage-fileedit #fileedit-tab-diff-buttons',
/* /wikiedit */ 'body.cpage-wikiedit #wikiedit-tab-diff-buttons',
/* /fdiff */ 'body.fdiff form div.submenu',
/* /vdiff */ 'body.vdiff form div.submenu',
/* /info, /vinfo, /ckout */ 'body.vinfo div.sectionmenu.info-changes-menu'
];
window.fossil.page.diffControlContainer = undefined;
while( potentialParents.length ){
if( (window.fossil.page.diffControlContainer
= document.querySelector(potentialParents.pop())) ){
break;
}
}
});
window.fossil.onPageLoad(function(){
/**
Adds toggle checkboxes to each file entry in the diff views for
/info and similar pages.
*/
if( !window.fossil.page.diffControlContainer ){
return;
}
const D = window.fossil.dom;
const allToggles = [/*collection of all diff-toggle checkboxes*/];
let checkedCount =
0 /* When showing more than one diff, keep track of how many
"show/hide" checkboxes are are checked so we can update the
"show/hide all" label dynamically. */;
let btnAll /* UI control to show/hide all diffs */;
/* Install a diff-toggle button for the given diff table element. */
const addToggle = function(diffElem){
const sib = diffElem.previousElementSibling,
ckbox = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0;
if(!sib) return;
const lblToggle = D.label();
D.append(lblToggle, ckbox, D.text(" show/hide "));
const wrapper = D.append(D.span(), lblToggle);
allToggles.push(ckbox);
++checkedCount;
D.append(sib, D.append(wrapper, lblToggle));
ckbox.addEventListener('change', function(){
diffElem.classList[this.checked ? 'remove' : 'add']('hidden');
if(btnAll){
checkedCount += (this.checked ? 1 : -1);
btnAll.innerText = (checkedCount < allToggles.length)
? "Show diffs" : "Hide diffs";
}
}, false);
};
if( !document.querySelector('body.fdiff') ){
/* Don't show the diff toggle button for /fdiff because it only
has a single file to show (and also a different DOM layout). */
document.querySelectorAll('table.diff').forEach(addToggle);
}
/**
Set up a "toggle all diffs" button which toggles all of the
above-installed checkboxes, but only if more than one diff is
rendered.
*/
const icm = allToggles.length>1 ? window.fossil.page.diffControlContainer : 0;
if(icm) {
btnAll = D.addClass(D.a("#", "Hide diffs"), "button");
D.append( icm, btnAll );
btnAll.addEventListener('click', function(ev){
ev.preventDefault();
ev.stopPropagation();
const show = checkedCount < allToggles.length;
for( const ckbox of allToggles ){
/* Toggle all entries to match this new state. We use click()
instead of ckbox.checked=... so that the on-change event handler
fires. */
if(ckbox.checked!==show) ckbox.click();
}
}, false);
}
});
window.fossil.onPageLoad(function(){
const F = window.fossil, D = F.dom;
const Diff = F.diff = {
e:{/*certain cached DOM elements*/},
config: {
|
| ︙ | ︙ | |||
633 634 635 636 637 638 639 |
const F = window.fossil, D = F.dom, Diff = F.diff;
/* Look for a parent element to hold the sbs-sync-scroll toggle
checkbox. This differs per page. If we don't find one, simply
elide that toggle and use whatever preference the user last
specified (defaulting to on). */
let cbSync /* scroll-sync checkbox */;
| | < < < < < < < < < < | > | < < | | | 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 |
const F = window.fossil, D = F.dom, Diff = F.diff;
/* Look for a parent element to hold the sbs-sync-scroll toggle
checkbox. This differs per page. If we don't find one, simply
elide that toggle and use whatever preference the user last
specified (defaulting to on). */
let cbSync /* scroll-sync checkbox */;
let eToggleParent = /* element to put the sync-scroll checkbox in */
document.querySelector('table.diff.splitdiff')
? window.fossil.page.diffControlContainer
: undefined;
const keySbsScroll = 'sync-diff-scroll' /* F.storage key for persistent user preference */;
if( eToggleParent ){
/* Add a checkbox to toggle sbs scroll sync. Remember that in
order to be UI-consistent in the /vdiff page we have to ensure
that the checkbox is to the LEFT of of its label. We store the
sync-scroll preference in F.storage (not a cookie) so that it
persists across page loads and different apps. */
cbSync = D.checkbox(keySbsScroll, F.storage.getBool(keySbsScroll,true));
D.append(eToggleParent, D.append(
D.addClass(D.create('span'), 'input-with-label'),
D.append(D.create('label'),
cbSync, "Scroll Sync")
));
cbSync.addEventListener('change', function(e){
F.storage.set(keySbsScroll, e.target.checked);
});
}
const useSync = cbSync ? ()=>cbSync.checked : ()=>F.storage.getBool(keySbsScroll,true);
|
| ︙ | ︙ |
Changes to src/fossil.dom.js.
| ︙ | ︙ | |||
85 86 87 88 89 90 91 |
*/
dom.label = function(forElem, text){
const rc = document.createElement('label');
if(forElem){
if(forElem instanceof HTMLElement){
forElem = this.attr(forElem, 'id');
}
| > | > | 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
*/
dom.label = function(forElem, text){
const rc = document.createElement('label');
if(forElem){
if(forElem instanceof HTMLElement){
forElem = this.attr(forElem, 'id');
}
if(forElem){
dom.attr(rc, 'for', forElem);
}
}
if(text) this.append(rc, text);
return rc;
};
/**
Returns an IMG element with an optional src
attribute value.
|
| ︙ | ︙ |
Changes to src/fossil.page.chat.js.
| ︙ | ︙ | |||
2130 2131 2132 2133 2134 2135 2136 |
}
Chat.e.inputElementWrapper.classList[
s.value ? 'add' : 'remove'
]('compact');
Chat.e.inputFields[Chat.e.inputFields.$currentIndex].focus();
});
Chat.settings.addListener('edit-ctrl-send',function(s){
| | | 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 |
}
Chat.e.inputElementWrapper.classList[
s.value ? 'add' : 'remove'
]('compact');
Chat.e.inputFields[Chat.e.inputFields.$currentIndex].focus();
});
Chat.settings.addListener('edit-ctrl-send',function(s){
const label = (s.value ? "Ctrl-" : "")+"Enter submits message.";
Chat.e.inputFields.forEach((e)=>{
const v = e.dataset.placeholder0 + " " +label;
if(e.isContentEditable) e.dataset.placeholder = v;
else D.attr(e,'placeholder',v);
});
Chat.e.btnSubmit.title = label;
});
|
| ︙ | ︙ |
Changes to src/graph.c.
| ︙ | ︙ | |||
493 494 495 496 497 498 499 | ** The tmFlags parameter is zero or more of the TIMELINE_* constants. ** Only the following are honored: ** ** TIMELINE_DISJOINT: Omit descenders ** TIMELINE_FILLGAPS: Use step-children ** TIMELINE_XMERGE: Omit off-graph merge lines */ | | > > > > | 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 |
** The tmFlags parameter is zero or more of the TIMELINE_* constants.
** Only the following are honored:
**
** TIMELINE_DISJOINT: Omit descenders
** TIMELINE_FILLGAPS: Use step-children
** TIMELINE_XMERGE: Omit off-graph merge lines
*/
void graph_finish(
GraphContext *p, /* The graph to be laid out */
Matcher *pLeftBranch, /* Compares true for left-most branch */
u32 tmFlags /* TIMELINE flags */
){
GraphRow *pRow, *pDesc, *pDup, *pLoop, *pParent;
int i, j;
u64 mask;
int hasDup = 0; /* True if one or more isDup entries */
const char *zTrunk;
u8 *aMap; /* Copy of p->aiRailMap */
int omitDescenders = (tmFlags & TIMELINE_DISJOINT)!=0;
|
| ︙ | ︙ | |||
961 962 963 964 965 966 967 |
pRoot->aiRiser[iFrom] = -1;
}
}
}
/*
** Compute the rail mapping that tries to put the branch named
| | | > | > | | < | > > | > > | > > > > | > | > > | 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 |
pRoot->aiRiser[iFrom] = -1;
}
}
}
/*
** Compute the rail mapping that tries to put the branch named
** pLeftBranch at the left margin. Other branches that merge
** with pLeftBranch are to the right with merge rails in between.
**
** aMap[X]=Y means that the X-th rail is drawn as the Y-th rail.
**
** Do not move rails around if there are timewarps, because that can
** seriously mess up the display of timewarps. Timewarps should be
** rare so this should not be a serious limitation to the algorithm.
*/
aMap = p->aiRailMap;
for(i=0; i<=p->mxRail; i++) aMap[i] = i; /* Set up a default mapping */
if( nTimewarp==0 ){
int kk;
/* Priority bits:
**
** 0x04 The preferred branch
**
** 0x02 A merge rail - a rail that contains merge lines into
** the preferred branch. Only applies if a preferred branch
** is defined. This improves the display of r=BRANCH
** options to /timeline.
**
** 0x01 A rail that merges with the preferred branch
*/
u16 aPriority[GR_MAX_RAIL];
int mxMatch = 0;
memset(aPriority, 0, (p->mxRail+1)*sizeof(aPriority[0]));
if( pLeftBranch ){
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
int iMatch = match_text(pLeftBranch, pRow->zBranch);
if( iMatch>0 ){
if( iMatch>10 ) iMatch = 10;
aPriority[pRow->iRail] |= 1<<(iMatch+1);
if( mxMatch<iMatch ) mxMatch = iMatch;
for(i=0; i<=p->mxRail; i++){
if( pRow->mergeIn[i] ) aPriority[i] |= 1;
}
if( pRow->mergeOut>=0 ) aPriority[pRow->mergeOut] |= 1;
}
}
for(i=0; i<=p->mxRail; i++){
if( p->mergeRail & BIT(i) ){
aPriority[i] |= 2;
}
}
}else{
j = 1;
aPriority[0] = 4;
mxMatch = 1;
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
if( pRow->iRail==0 ){
for(i=0; i<=p->mxRail; i++){
if( pRow->mergeIn[i] ) aPriority[i] |= 1;
}
if( pRow->mergeOut>=0 ) aPriority[pRow->mergeOut] |= 1;
}
}
}
#if 0
fprintf(stderr,"mergeRail: 0x%llx\n", p->mergeRail);
fprintf(stderr,"Priority:");
for(i=0; i<=p->mxRail; i++){
fprintf(stderr," %x.%x",
aPriority[i]/4, aPriority[i]&3);
}
fprintf(stderr,"\n");
#endif
j = 0;
for(kk=4; kk<=1<<(mxMatch+1); kk*=2){
for(i=0; i<=p->mxRail; i++){
if( aPriority[i]>=kk && aPriority[i]<kk*2 ){
aMap[i] = j++;
}
}
}
for(i=p->mxRail; i>=0; i--){
if( aPriority[i]==3 ) aMap[i] = j++;
}
for(i=0; i<=p->mxRail; i++){
if( aPriority[i]==1 || aPriority[i]==2 ) aMap[i] = j++;
}
|
| ︙ | ︙ |
Changes to src/http.c.
| ︙ | ︙ | |||
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 |
** is written into that second file instead of being written on standard
** output. Use the "--out OUTPUT" option to specify an output file for
** a GET request where there is no PAYLOAD.
**
** Options:
** --compress Use ZLIB compression on the payload
** --mimetype TYPE Mimetype of the payload
** --out FILE Store the reply in FILE
** -v Verbose output
** --xfer PAYLOAD in a Fossil xfer protocol message
*/
void test_httpmsg_command(void){
const char *zMimetype;
const char *zInFile;
const char *zOutFile;
Blob in, out;
unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
zMimetype = find_option("mimetype",0,1);
zOutFile = find_option("out","o",1);
if( find_option("verbose","v",0)!=0 ) mHttpFlags |= HTTP_VERBOSE;
if( find_option("compress",0,0)!=0 ) mHttpFlags &= ~HTTP_NOCOMPRESS;
if( find_option("xfer",0,0)!=0 ){
mHttpFlags |= HTTP_USE_LOGIN;
mHttpFlags &= ~HTTP_GENERIC;
}
verify_all_options();
if( g.argc<3 || g.argc>5 ){
usage("URL ?PAYLOAD? ?OUTPUT?");
| > > > > > > | 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 |
** is written into that second file instead of being written on standard
** output. Use the "--out OUTPUT" option to specify an output file for
** a GET request where there is no PAYLOAD.
**
** Options:
** --compress Use ZLIB compression on the payload
** --mimetype TYPE Mimetype of the payload
** --no-cert-verify Disable TLS cert verification
** --out FILE Store the reply in FILE
** -v Verbose output
** --xfer PAYLOAD in a Fossil xfer protocol message
*/
void test_httpmsg_command(void){
const char *zMimetype;
const char *zInFile;
const char *zOutFile;
Blob in, out;
unsigned int mHttpFlags = HTTP_GENERIC|HTTP_NOCOMPRESS;
zMimetype = find_option("mimetype",0,1);
zOutFile = find_option("out","o",1);
if( find_option("verbose","v",0)!=0 ) mHttpFlags |= HTTP_VERBOSE;
if( find_option("compress",0,0)!=0 ) mHttpFlags &= ~HTTP_NOCOMPRESS;
if( find_option("no-cert-verify",0,0)!=0 ){
#ifdef FOSSIL_ENABLE_SSL
ssl_disable_cert_verification();
#endif
}
if( find_option("xfer",0,0)!=0 ){
mHttpFlags |= HTTP_USE_LOGIN;
mHttpFlags &= ~HTTP_GENERIC;
}
verify_all_options();
if( g.argc<3 || g.argc>5 ){
usage("URL ?PAYLOAD? ?OUTPUT?");
|
| ︙ | ︙ |
Changes to src/http_transport.c.
| ︙ | ︙ | |||
101 102 103 104 105 106 107 |
#endif
/*
** Initialize a Blob to the name of the configured SSH command.
*/
void transport_ssh_command(Blob *p){
char *zSsh; /* The base SSH command */
| > > | > | 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
#endif
/*
** Initialize a Blob to the name of the configured SSH command.
*/
void transport_ssh_command(Blob *p){
char *zSsh; /* The base SSH command */
zSsh = g.zSshCmd;
if( zSsh==0 || zSsh[0]==0 ){
zSsh = db_get("ssh-command", zDefaultSshCmd);
}
blob_init(p, zSsh, -1);
}
/*
** SSH initialization of the transport layer
*/
int transport_ssh_open(UrlData *pUrlData){
|
| ︙ | ︙ |
Changes to src/info.c.
| ︙ | ︙ | |||
318 319 320 321 322 323 324 325 326 327 328 329 330 331 |
|TIMELINE_GRAPH
|TIMELINE_FILLGAPS
|TIMELINE_NOSCROLL
|TIMELINE_XMERGE
|TIMELINE_CHPICK,
0, 0, 0, rid, rid2, 0);
db_finalize(&q);
}
/*
** Append the difference between artifacts to the output
*/
static void append_diff(
| > | 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 |
|TIMELINE_GRAPH
|TIMELINE_FILLGAPS
|TIMELINE_NOSCROLL
|TIMELINE_XMERGE
|TIMELINE_CHPICK,
0, 0, 0, rid, rid2, 0);
db_finalize(&q);
blob_reset(&sql);
}
/*
** Append the difference between artifacts to the output
*/
static void append_diff(
|
| ︙ | ︙ | |||
370 371 372 373 374 375 376 |
const char *zName, /* Name of the file that has changed */
const char *zOld, /* blob.uuid before change. NULL for added files */
const char *zNew, /* blob.uuid after change. NULL for deletes */
const char *zOldName, /* Prior name. NULL if no name change. */
DiffConfig *pCfg, /* Flags for text_diff() or NULL to omit all */
int mperm /* executable or symlink permission for zNew */
){
| | > > > | 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 |
const char *zName, /* Name of the file that has changed */
const char *zOld, /* blob.uuid before change. NULL for added files */
const char *zNew, /* blob.uuid after change. NULL for deletes */
const char *zOldName, /* Prior name. NULL if no name change. */
DiffConfig *pCfg, /* Flags for text_diff() or NULL to omit all */
int mperm /* executable or symlink permission for zNew */
){
@ <div class='file-change-line'><span>
/* Maintenance reminder: the extra level of SPAN is for
** arranging new elements via JS. */
if( !g.perm.Hyperlink ){
if( zNew==0 ){
@ Deleted %h(zName).
}else if( zOld==0 ){
@ Added %h(zName).
}else if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
@ Name change from %h(zOldName) to %h(zName).
}else if( fossil_strcmp(zNew, zOld)==0 ){
if( mperm==PERM_EXE ){
@ %h(zName) became executable.
}else if( mperm==PERM_LNK ){
@ %h(zName) became a symlink.
}else{
@ %h(zName) became a regular file.
}
}else{
@ Changes to %h(zName).
}
@ </span></div>
if( pCfg ){
append_diff(zOld, zNew, pCfg);
}
}else{
if( zOld && zNew ){
if( fossil_strcmp(zOld, zNew)!=0 ){
if( zOldName!=0 && fossil_strcmp(zName,zOldName)!=0 ){
|
| ︙ | ︙ | |||
436 437 438 439 440 441 442 443 |
@ %h(zName)</a> version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>.
}else{
@ Added %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
@ %h(zName)</a> version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
}
if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
if( pCfg ){
append_diff(zOld, zNew, pCfg);
| > | < > > > < | 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 |
@ %h(zName)</a> version %z(href("%R/artifact/%!S",zOld))[%S(zOld)]</a>.
}else{
@ Added %z(href("%R/finfo?name=%T&m=%!S&ci=%!S",zName,zNew,zCkin))\
@ %h(zName)</a> version %z(href("%R/artifact/%!S",zNew))[%S(zNew)]</a>.
}
if( zOld && zNew && fossil_strcmp(zOld,zNew)!=0 ){
if( pCfg ){
@ </span></div>
append_diff(zOld, zNew, pCfg);
}else{
@ %z(href("%R/fdiff?v1=%!S&v2=%!S",zOld,zNew))[diff]</a>
@ </span></div>
}
}else{
@ </span></div>
}
}
}
/*
** Generate javascript to enhance HTML diffs.
*/
void append_diff_javascript(int diffType){
if( diffType==0 ) return;
|
| ︙ | ︙ | |||
600 601 602 603 604 605 606 607 608 609 610 611 612 613 |
blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
db_prepare(&q, "%s", blob_sql_text(&sql));
www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
0, 0, 0, rid, 0, 0);
db_finalize(&q);
style_finish_page();
}
/*
** WEBPAGE: vinfo
** WEBPAGE: ci
** URL: /ci/ARTIFACTID
** OR: /ci?name=ARTIFACTID
**
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 |
blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
db_prepare(&q, "%s", blob_sql_text(&sql));
www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
0, 0, 0, rid, 0, 0);
db_finalize(&q);
style_finish_page();
}
/*
** Render a web-page diff of the changes in the working check-out
*/
static void ckout_normal_diff(int vid){
int diffType; /* 0: no diff, 1: unified, 2: side-by-side */
DiffConfig DCfg,*pCfg; /* Diff details */
const char *zW; /* The "w" query parameter */
int nChng; /* Number of changes */
Stmt q;
diffType = preferred_diff_type();
pCfg = construct_diff_flags(diffType, &DCfg);
nChng = db_int(0, "SELECT count(*) FROM vfile"
" WHERE vid=%d AND (deleted OR chnged OR rid==0)", vid);
if( nChng==0 ){
@ <p>No uncommitted changes</p>
return;
}
db_prepare(&q,
/* 0 1 2 3 4 5 6 */
"SELECT pathname, deleted, chnged , rid==0, rid, islink, uuid"
" FROM vfile LEFT JOIN blob USING(rid)"
" WHERE vid=%d"
" AND (deleted OR chnged OR rid==0)"
" ORDER BY pathname /*scan*/",
vid
);
if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
}else{
DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
}
@ <div class="sectionmenu info-changes-menu">
zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
if( diffType!=1 ){
@ %z(chref("button","%R?diff=1%s",zW))Unified Diff</a>
}
if( diffType!=2 ){
@ %z(chref("button","%R?diff=2%s",zW))Side-by-Side Diff</a>
}
if( diffType!=0 ){
if( *zW ){
@ %z(chref("button","%R?diff=%d",diffType))\
@ Show Whitespace Changes</a>
}else{
@ %z(chref("button","%R?diff=%d&w",diffType))Ignore Whitespace</a>
}
}
@ </div>
while( db_step(&q)==SQLITE_ROW ){
const char *zTreename = db_column_text(&q,0);
int isDeleted = db_column_int(&q, 1);
int isChnged = db_column_int(&q,2);
int isNew = db_column_int(&q,3);
int srcid = db_column_int(&q, 4);
int isLink = db_column_int(&q, 5);
const char *zUuid = db_column_text(&q, 6);
int showDiff = 1;
DCfg.diffFlags &= (~DIFF_FILE_MASK);
@ <div class='file-change-line'><span>
if( isDeleted ){
@ DELETED %h(zTreename)
DCfg.diffFlags |= DIFF_FILE_DELETED;
showDiff = 0;
}else if( file_access(zTreename, F_OK) ){
@ MISSING %h(zTreename)
showDiff = 0;
}else if( isNew ){
@ ADDED %h(zTreename)
DCfg.diffFlags |= DIFF_FILE_ADDED;
srcid = 0;
showDiff = 0;
}else if( isChnged==3 ){
@ ADDED_BY_MERGE %h(zTreename)
DCfg.diffFlags |= DIFF_FILE_ADDED;
srcid = 0;
showDiff = 0;
}else if( isChnged==5 ){
@ ADDED_BY_INTEGRATE %h(zTreename)
DCfg.diffFlags |= DIFF_FILE_ADDED;
srcid = 0;
showDiff = 0;
}else{
@ CHANGED %h(zTreename)
}
@ </span></div>
if( showDiff && pCfg ){
Blob old, new;
if( !isLink != !file_islink(zTreename) ){
@ %s(DIFF_CANNOT_COMPUTE_SYMLINK)
continue;
}
if( srcid>0 ){
content_get(srcid, &old);
pCfg->zLeftHash = zUuid;
}else{
blob_zero(&old);
pCfg->zLeftHash = 0;
}
blob_read_from_file(&new, zTreename, ExtFILE);
text_diff(&old, &new, cgi_output_blob(), pCfg);
blob_reset(&old);
blob_reset(&new);
}
}
db_finalize(&q);
append_diff_javascript(diffType);
}
/*
** Render a web-page diff of the changes in the working check-out to
** an external reference.
*/
static void ckout_external_base_diff(int vid, const char *zExBase){
int diffType; /* 0: no diff, 1: unified, 2: side-by-side */
DiffConfig DCfg,*pCfg; /* Diff details */
const char *zW; /* The "w" query parameter */
Stmt q;
diffType = preferred_diff_type();
pCfg = construct_diff_flags(diffType, &DCfg);
db_prepare(&q,
"SELECT pathname FROM vfile WHERE vid=%d ORDER BY pathname", vid
);
if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
}else{
DCfg.diffFlags |= DIFF_LINENO | DIFF_HTML | DIFF_NOTTOOBIG;
}
@ <div class="sectionmenu info-changes-menu">
zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
if( diffType!=1 ){
@ %z(chref("button","%R?diff=1&exbase=%h%s",zExBase,zW))\
@ Unified Diff</a>
}
if( diffType!=2 ){
@ %z(chref("button","%R?diff=2&exbase=%h%s",zExBase,zW))\
@ Side-by-Side Diff</a>
}
if( diffType!=0 ){
if( *zW ){
@ %z(chref("button","%R?diff=%d&exbase=%h",diffType,zExBase))\
@ Show Whitespace Changes</a>
}else{
@ %z(chref("button","%R?diff=%d&exbase=%h&w",diffType,zExBase))\
@ Ignore Whitespace</a>
}
}
@ </div>
while( db_step(&q)==SQLITE_ROW ){
const char *zFile; /* Name of file in the repository */
char *zLhs; /* Full name of left-hand side file */
char *zRhs; /* Full name of right-hand side file */
Blob rhs; /* Full text of RHS */
Blob lhs; /* Full text of LHS */
zFile = db_column_text(&q,0);
zLhs = mprintf("%s/%s", zExBase, zFile);
zRhs = mprintf("%s%s", g.zLocalRoot, zFile);
if( file_size(zLhs, ExtFILE)<0 ){
@ <div class='file-change-line'><span>
@ Missing from external baseline: %h(zFile)
@ </span></div>
}else{
blob_read_from_file(&lhs, zLhs, ExtFILE);
blob_read_from_file(&rhs, zRhs, ExtFILE);
if( blob_size(&lhs)!=blob_size(&rhs)
|| memcmp(blob_buffer(&lhs), blob_buffer(&rhs), blob_size(&lhs))!=0
){
@ <div class='file-change-line'><span>
@ Changes to %h(zFile)
@ </span></div>
if( pCfg ){
char *zFullFN;
char *zHexFN;
zFullFN = file_canonical_name_dup(zLhs);
zHexFN = mprintf("x%H", zFullFN);
fossil_free(zFullFN);
pCfg->zLeftHash = zHexFN;
text_diff(&lhs, &rhs, cgi_output_blob(), pCfg);
pCfg->zLeftHash = 0;
fossil_free(zHexFN);
}
}
blob_reset(&lhs);
blob_reset(&rhs);
}
fossil_free(zLhs);
fossil_free(zRhs);
}
db_finalize(&q);
append_diff_javascript(diffType);
}
/*
** WEBPAGE: ckout
**
** Show information about the current checkout. This page only functions
** if the web server is run on a loopback interface (in other words, was
** started using "fossil ui" or similar) from within an open check-out.
**
** If the "exbase=PATH" query parameter is provided, then the diff shown
** uses the files in PATH as the baseline. This is the same as using
** the "--from PATH" argument to the "fossil diff" command-line. In fact,
** when using "fossil ui --from PATH", the --from argument becomes the value
** of the exbase query parameter for the start page. Note that if PATH
** is a pure hexadecimal string, it is decoded first before being used as
** the pathname. Real pathnames should contain at least one directory
** separator character.
**
** Other query parameters related to diffs are also accepted.
*/
void ckout_page(void){
int vid;
const char *zHome; /* Home directory */
int nHome;
const char *zExBase;
char *zHostname;
char *zCwd;
if( !cgi_is_loopback(g.zIpAddr) || !db_open_local(0) ){
cgi_redirectf("%R/home");
return;
}
file_chdir(g.zLocalRoot, 0);
vid = db_lget_int("checkout", 0);
db_unprotect(PROTECT_ALL);
vfile_check_signature(vid, CKSIG_ENOTFILE);
db_protect_pop();
style_set_current_feature("vinfo");
zHostname = fossil_hostname();
zCwd = file_getcwd(0,0);
zHome = fossil_getenv("HOME");
if( zHome ){
nHome = (int)strlen(zHome);
if( strncmp(zCwd, zHome, nHome)==0 && zCwd[nHome]=='/' ){
zCwd = mprintf("~%s", zCwd+nHome);
}
}else{
nHome = 0;
}
if( zHostname ){
style_header("Checkout Status: %h on %h", zCwd, zHostname);
}else{
style_header("Checkout Status: %h", zCwd);
}
render_checkin_context(vid, 0, 0, 0);
@ <hr>
zExBase = P("exbase");
if( zExBase && zExBase[0] ){
char *zPath = decode16_dup(zExBase);
char *zCBase = file_canonical_name_dup(zPath?zPath:zExBase);
if( nHome && strncmp(zCBase, zHome, nHome)==0 && zCBase[nHome]=='/' ){
@ <p>Using external baseline: ~%h(zCBase+nHome)</p>
}else{
@ <p>Using external baseline: %h(zCBase)</p>
}
ckout_external_base_diff(vid, zCBase);
fossil_free(zCBase);
fossil_free(zPath);
}else{
ckout_normal_diff(vid);
}
style_finish_page();
}
/*
** WEBPAGE: vinfo
** WEBPAGE: ci
** URL: /ci/ARTIFACTID
** OR: /ci?name=ARTIFACTID
**
|
| ︙ | ︙ | |||
626 627 628 629 630 631 632 | const char *zName; /* Name of the check-in to be displayed */ const char *zUuid; /* Hash of zName, found via blob.uuid */ const char *zParent; /* Hash of the parent check-in (if any) */ const char *zRe; /* regex parameter */ ReCompiled *pRe = 0; /* regex */ const char *zW; /* URL param for ignoring whitespace */ const char *zPage = "vinfo"; /* Page that shows diffs */ | < | 899 900 901 902 903 904 905 906 907 908 909 910 911 912 |
const char *zName; /* Name of the check-in to be displayed */
const char *zUuid; /* Hash of zName, found via blob.uuid */
const char *zParent; /* Hash of the parent check-in (if any) */
const char *zRe; /* regex parameter */
ReCompiled *pRe = 0; /* regex */
const char *zW; /* URL param for ignoring whitespace */
const char *zPage = "vinfo"; /* Page that shows diffs */
const char *zBrName; /* Branch name */
DiffConfig DCfg,*pCfg; /* Type of diff */
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
style_set_current_feature("vinfo");
zName = P("name");
|
| ︙ | ︙ | |||
896 897 898 899 900 901 902 | @ </div><div class="section accordion">Changes</div> @ <div class="accordion_panel"> @ <div class="sectionmenu info-changes-menu"> /* ^^^ .info-changes-menu is used by diff scroll sync */ pCfg = construct_diff_flags(diffType, &DCfg); DCfg.pRe = pRe; zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":""; | < < < < | | | 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 |
@ </div><div class="section accordion">Changes</div>
@ <div class="accordion_panel">
@ <div class="sectionmenu info-changes-menu">
/* ^^^ .info-changes-menu is used by diff scroll sync */
pCfg = construct_diff_flags(diffType, &DCfg);
DCfg.pRe = pRe;
zW = (DCfg.diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
if( diffType!=1 ){
@ %z(chref("button","%R/%s/%T?diff=1%s",zPage,zName,zW))\
@ Unified Diff</a>
}
if( diffType!=2 ){
@ %z(chref("button","%R/%s/%T?diff=2%s",zPage,zName,zW))\
@ Side-by-Side Diff</a>
}
if( diffType!=0 ){
if( *zW ){
@ %z(chref("button","%R/%s/%T",zPage,zName))
@ Show Whitespace Changes</a>
}else{
@ %z(chref("button","%R/%s/%T?w",zPage,zName))
|
| ︙ | ︙ | |||
1266 1267 1268 1269 1270 1271 1272 |
blob_appendf(&qp, "&w");
}
cgi_check_for_malice();
style_set_current_feature("vdiff");
if( zBranch==0 ){
style_submenu_element("Path", "%R/timeline?me=%T&you=%T", zFrom, zTo);
}
| < < < | 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 |
blob_appendf(&qp, "&w");
}
cgi_check_for_malice();
style_set_current_feature("vdiff");
if( zBranch==0 ){
style_submenu_element("Path", "%R/timeline?me=%T&you=%T", zFrom, zTo);
}
if( diffType!=2 ){
style_submenu_element("Side-by-Side Diff", "%R/vdiff?diff=2&%b%b", &qp,
&qpGlob);
}
if( diffType!=1 ) {
style_submenu_element("Unified Diff", "%R/vdiff?diff=1&%b%b", &qp, &qpGlob);
}
|
| ︙ | ︙ | |||
1666 1667 1668 1669 1670 1671 1672 |
blob_append(pDownloadName, zFilename, -1);
}
tag_private_status(rid);
}
db_finalize(&q);
if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d AND tagid=%d",
rid, TAG_CLUSTER) ){
| | | 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 |
blob_append(pDownloadName, zFilename, -1);
}
tag_private_status(rid);
}
db_finalize(&q);
if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d AND tagid=%d",
rid, TAG_CLUSTER) ){
@ Cluster %z(href("%R/info/%S",zUuid))%S(zUuid)</a>.
cnt++;
}
if( cnt==0 ){
@ Unrecognized artifact
if( pDownloadName && blob_size(pDownloadName)==0 ){
blob_appendf(pDownloadName, "%S.txt", zUuid);
}
|
| ︙ | ︙ | |||
1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 |
/*
** WEBPAGE: jchunk hidden
** URL: /jchunk/HASH?from=N&to=M
**
** Return lines of text from a file as a JSON array - one entry in the
** array for each line of text.
**
** **Warning:** This is an internal-use-only interface that is subject to
** change at any moment. External application should not use this interface
** since the application will break when this interface changes, and this
** interface will undoubtedly change.
**
** This page is intended to be used in an XHR from javascript on a
** diff page, to return unseen context to fill in additional context
** when the user clicks on the appropriate button. The response is
** always in JSON form and errors are reported as documented for
** ajax_route_error().
*/
void jchunk_page(void){
int rid = 0;
const char *zName = PD("name", "");
int iFrom = atoi(PD("from","0"));
int iTo = atoi(PD("to","0"));
int ln;
int go = 1;
const char *zSep;
Blob content;
Blob line;
Blob *pOut;
if(0){
ajax_route_error(400, "Just testing client-side error handling.");
return;
}
login_check_credentials();
cgi_check_for_malice();
if( !g.perm.Read ){
ajax_route_error(403, "Access requires Read permissions.");
return;
}
#if 1
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | | | | | | | | | | | | | | | | < < < | < | 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 |
/*
** WEBPAGE: jchunk hidden
** URL: /jchunk/HASH?from=N&to=M
**
** Return lines of text from a file as a JSON array - one entry in the
** array for each line of text.
**
** The HASH is normally a sha1 or sha3 hash that identifies an artifact
** in the BLOB table of the database. However, if HASH starts with an "x"
** and is followed by valid hexadecimal, and if we are running in a
** "fossil ui" situation (locally and with privilege), then decode the hex
** into a filename and read the file content from that name.
**
** **Warning:** This is an internal-use-only interface that is subject to
** change at any moment. External application should not use this interface
** since the application will break when this interface changes, and this
** interface will undoubtedly change.
**
** This page is intended to be used in an XHR from javascript on a
** diff page, to return unseen context to fill in additional context
** when the user clicks on the appropriate button. The response is
** always in JSON form and errors are reported as documented for
** ajax_route_error().
*/
void jchunk_page(void){
int rid = 0;
const char *zName = PD("name", "");
int nName = (int)(strlen(zName)&0x7fffffff);
int iFrom = atoi(PD("from","0"));
int iTo = atoi(PD("to","0"));
int ln;
int go = 1;
const char *zSep;
Blob content;
Blob line;
Blob *pOut;
if(0){
ajax_route_error(400, "Just testing client-side error handling.");
return;
}
login_check_credentials();
cgi_check_for_malice();
if( !g.perm.Read ){
ajax_route_error(403, "Access requires Read permissions.");
return;
}
if( iFrom<1 || iTo<iFrom ){
ajax_route_error(500, "Invalid line range from=%d, to=%d.",
iFrom, iTo);
return;
}
if( zName[0]=='x'
&& ((nName-1)&1)==0
&& validate16(&zName[1],nName-1)
&& g.perm.Admin
&& cgi_is_loopback(g.zIpAddr)
&& db_open_local(0)
){
/* Treat the HASH as a hex-encoded filename */
int n = (nName-1)/2;
char *zFN = fossil_malloc(n+1);
decode16((const u8*)&zName[1], (u8*)zFN, nName-1);
zFN[n] = 0;
if( file_size(zFN, ExtFILE)<0 ){
blob_zero(&content);
}else{
blob_read_from_file(&content, zFN, ExtFILE);
}
fossil_free(zFN);
}else{
/* Treat the HASH as an artifact hash matching BLOB.UUID */
#if 1
/* Re-enable this block once this code is integrated somewhere into
the UI. */
rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName);
if( rid==0 ){
ajax_route_error(404, "Unknown artifact: %h", zName);
return;
}
#else
/* This impl is only to simplify "manual" testing via the JS
console. */
rid = symbolic_name_to_rid(zName, "*");
if( rid==0 ){
ajax_route_error(404, "Unknown artifact: %h", zName);
return;
}else if( rid<0 ){
ajax_route_error(418, "Ambiguous artifact name: %h", zName);
return;
}
#endif
content_get(rid, &content);
}
g.isConst = 1;
cgi_set_content_type("application/json");
ln = 0;
while( go && ln<iFrom ){
go = blob_line(&content, &line);
ln++;
}
|
| ︙ | ︙ | |||
2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 | @ <div class="section">Changes</div> @ <p> ticket_output_change_artifact(pTktChng, 0, 1, 0); manifest_destroy(pTktChng); style_finish_page(); } /* ** WEBPAGE: info ** URL: info/NAME ** ** The NAME argument is any valid artifact name: an artifact hash, ** a timestamp, a tag name, etc. | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 |
@ <div class="section">Changes</div>
@ <p>
ticket_output_change_artifact(pTktChng, 0, 1, 0);
manifest_destroy(pTktChng);
style_finish_page();
}
/*
** rid is a cluster. Paint a page that contains detailed information
** about that cluster.
*/
static void cluster_info(int rid, const char *zName){
Manifest *pCluster;
int i;
Blob where = BLOB_INITIALIZER;
Blob unks = BLOB_INITIALIZER;
Stmt q;
char *zSha1Bg;
char *zSha3Bg;
int badRid = 0;
int rcvid;
int hashClr = PB("hclr");
const char *zDate;
pCluster = manifest_get(rid, CFTYPE_CLUSTER, 0);
if( pCluster==0 ){
artifact_page();
return;
}
style_header("Cluster %S", zName);
rcvid = db_int(0, "SELECT rcvid FROM blob WHERE rid=%d", rid);
if( rcvid==0 ){
zDate = 0;
}else{
zDate = db_text(0, "SELECT datetime(mtime) FROM rcvfrom WHERE rcvid=%d",
rcvid);
}
@ <p>Artifact %z(href("%R/artifact/%h",zName))%S(zName)</a> is a cluster
@ with %d(pCluster->nCChild) entries
if( g.perm.Admin ){
@ received <a href="%R/rcvfrom?rcvid=%d(rcvid)">%h(zDate)</a>:
}else{
@ received %h(zDate):
}
blob_appendf(&where,"IN(0");
for(i=0; i<pCluster->nCChild; i++){
int rid = fast_uuid_to_rid(pCluster->azCChild[i]);
if( rid ){
blob_appendf(&where,",%d", rid);
}else{
if( blob_size(&unks)>0 ) blob_append_char(&unks, ',');
badRid++;
blob_append_sql(&unks,"(%d,%Q)",-badRid,pCluster->azCChild[i]);
}
}
blob_append_char(&where,')');
describe_artifacts(blob_str(&where));
blob_reset(&where);
if( badRid>0 ){
db_multi_exec(
"WITH unks(rx,hx) AS (VALUES %s)\n"
"INSERT INTO description(rid,uuid,type,summary) "
" SELECT rx, hx, 'phantom', '' FROM unks;",
blob_sql_text(&unks)
);
}
blob_reset(&unks);
db_prepare(&q,
"SELECT rid, uuid, summary, isPrivate, type='phantom', rcvid, ref"
" FROM description ORDER BY uuid"
);
if( skin_detail_boolean("white-foreground") ){
zSha1Bg = "#714417";
zSha3Bg = "#177117";
}else{
zSha1Bg = "#ebffb0";
zSha3Bg = "#b0ffb0";
}
@ <table cellpadding="2" cellspacing="0" border="1">
if( g.perm.Admin ){
@ <tr><th>RID<th>Hash<th>Rcvid<th>Description<th>Ref<th>Remarks
}else{
@ <tr><th>RID<th>Hash<th>Description<th>Ref<th>Remarks
}
while( db_step(&q)==SQLITE_ROW ){
int rid = db_column_int(&q,0);
const char *zUuid = db_column_text(&q, 1);
const char *zDesc = db_column_text(&q, 2);
int isPriv = db_column_int(&q,3);
int isPhantom = db_column_int(&q,4);
const char *zRef = db_column_text(&q,6);
if( isPriv && !isPhantom && !g.perm.Private && !g.perm.Admin ){
/* Don't show private artifacts to users without Private (x) permission */
continue;
}
if( rid<=0 ){
@ <tr><td> </td>
}else if( hashClr ){
const char *zClr = db_column_bytes(&q,1)>40 ? zSha3Bg : zSha1Bg;
@ <tr style='background-color:%s(zClr);'><td align="right">%d(rid)</td>
}else{
@ <tr><td align="right">%d(rid)</td>
}
if( rid<=0 ){
@ <td> %S(zUuid) </td>
}else{
@ <td> %z(href("%R/info/%!S",zUuid))%S(zUuid)</a> </td>
}
if( g.perm.Admin ){
int rcvid = db_column_int(&q,5);
if( rcvid<=0 ){
@ <td>
}else{
@ <td><a href='%R/rcvfrom?rcvid=%d(rcvid)'>%d(rcvid)</a>
}
}
@ <td align="left">%h(zDesc)</td>
if( zRef && zRef[0] ){
@ <td>%z(href("%R/info/%!S",zRef))%S(zRef)</a>
}else{
@ <td>
}
if( isPriv || isPhantom ){
if( isPriv==0 ){
@ <td>phantom</td>
}else if( isPhantom==0 ){
@ <td>private</td>
}else{
@ <td>private,phantom</td>
}
}else{
@ <td>
}
@ </tr>
}
@ </table>
db_finalize(&q);
style_finish_page();
}
/*
** WEBPAGE: info
** URL: info/NAME
**
** The NAME argument is any valid artifact name: an artifact hash,
** a timestamp, a tag name, etc.
|
| ︙ | ︙ | |||
2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 |
ci_page();
}else
if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){
ci_page();
}else
if( db_exists("SELECT 1 FROM attachment WHERE attachid=%d", rid) ){
ainfo_page();
}else
{
artifact_page();
}
}
/*
| > > > > | 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 |
ci_page();
}else
if( db_exists("SELECT 1 FROM plink WHERE pid=%d", rid) ){
ci_page();
}else
if( db_exists("SELECT 1 FROM attachment WHERE attachid=%d", rid) ){
ainfo_page();
}else
if( db_exists("SELECT 1 FROM tagxref WHERE rid=%d AND tagid=%d",
rid, TAG_CLUSTER) ){
cluster_info(rid, zName);
}else
{
artifact_page();
}
}
/*
|
| ︙ | ︙ |
Changes to src/interwiki.c.
| ︙ | ︙ | |||
273 274 275 276 277 278 279 280 281 |
int n = 0;
Stmt q;
db_prepare(&q,
"SELECT substr(name,11), value->>'base'"
" FROM config WHERE name glob 'interwiki:*' AND json_valid(value)"
" ORDER BY name;"
);
while( db_step(&q)==SQLITE_ROW ){
if( n==0 ){
| > | | 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
int n = 0;
Stmt q;
db_prepare(&q,
"SELECT substr(name,11), value->>'base'"
" FROM config WHERE name glob 'interwiki:*' AND json_valid(value)"
" ORDER BY name;"
);
blob_append(out, "<blockquote>", -1);
while( db_step(&q)==SQLITE_ROW ){
if( n==0 ){
blob_appendf(out, "<table>\n");
}
blob_appendf(out,"<tr><td>%h</td><td> → </td>",
db_column_text(&q,0));
blob_appendf(out,"<td>%h</td></tr>\n",
db_column_text(&q,1));
n++;
}
|
| ︙ | ︙ |
Changes to src/login.c.
| ︙ | ︙ | |||
1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 |
** are applied if this setting is undefined or is
** an empty string.
*/
void login_restrict_robot_access(void){
const char *zReferer;
const char *zGlob;
int isMatch = 1;
if( g.zLogin!=0 ) return;
zGlob = db_get("robot-restrict",0);
if( zGlob==0 || zGlob[0]==0 ) return;
if( g.isHuman ){
zReferer = P("HTTP_REFERER");
if( zReferer && zReferer[0]!=0 ) return;
}
| > | > > > > > > > > > > > | 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 |
** are applied if this setting is undefined or is
** an empty string.
*/
void login_restrict_robot_access(void){
const char *zReferer;
const char *zGlob;
int isMatch = 1;
int nQP; /* Number of query parameters other than name= */
if( g.zLogin!=0 ) return;
zGlob = db_get("robot-restrict",0);
if( zGlob==0 || zGlob[0]==0 ) return;
if( g.isHuman ){
zReferer = P("HTTP_REFERER");
if( zReferer && zReferer[0]!=0 ) return;
}
nQP = cgi_qp_count();
if( nQP<1 ) return;
isMatch = glob_multi_match(zGlob, g.zPath);
if( !isMatch ) return;
/* Check for exceptions to the restriction on the number of query
** parameters. */
zGlob = db_get("robot-restrict-qp",0);
if( zGlob && zGlob[0] ){
char *zPath = mprintf("%s/%d", g.zPath, nQP);
isMatch = glob_multi_match(zGlob, zPath);
fossil_free(zPath);
if( isMatch ) return;
}
/* If we reach this point, it means we have a situation where we
** want to restrict the activity of a robot.
*/
g.isHuman = 0;
(void)exclude_spiders(0);
cgi_reply();
|
| ︙ | ︙ |
Changes to src/main.c.
| ︙ | ︙ | |||
1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 |
while( find_option("verbose","v",0)!=0 ) verboseFlag++;
while( find_option("vv",0,0)!=0 ) verboseFlag += 2;
/* We should be done with options.. */
verify_all_options();
fossil_version_blob(&versionInfo, verboseFlag);
fossil_print("%s", blob_str(&versionInfo));
}
/*
** WEBPAGE: version
**
** Show the version information for Fossil.
| > | 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 |
while( find_option("verbose","v",0)!=0 ) verboseFlag++;
while( find_option("vv",0,0)!=0 ) verboseFlag += 2;
/* We should be done with options.. */
verify_all_options();
fossil_version_blob(&versionInfo, verboseFlag);
fossil_print("%s", blob_str(&versionInfo));
blob_reset(&versionInfo);
}
/*
** WEBPAGE: version
**
** Show the version information for Fossil.
|
| ︙ | ︙ | |||
2038 2039 2040 2041 2042 2043 2044 |
/* Use the first element of PATH_INFO as the page name
** and deliver the appropriate page back to the user.
*/
set_base_url(0);
if( fossil_redirect_to_https_if_needed(2) ) return;
if( zPathInfo==0 || zPathInfo[0]==0
|| (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
| | > | > | > > > > | > | 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 |
/* Use the first element of PATH_INFO as the page name
** and deliver the appropriate page back to the user.
*/
set_base_url(0);
if( fossil_redirect_to_https_if_needed(2) ) return;
if( zPathInfo==0 || zPathInfo[0]==0
|| (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
/* Second special case: If the PATH_INFO is blank, issue a redirect:
** (1) to "/ckout" if g.useLocalauth and g.localOpen are both set.
** (2) to the home page identified by the "index-page" setting
** in the repository CONFIG table
** (3) to "/index" if there no "index-page" setting in CONFIG
*/
#ifdef FOSSIL_ENABLE_JSON
if(g.json.isJsonMode){
json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1);
fossil_exit(0);
}
#endif
if( g.useLocalauth && g.localOpen ){
cgi_redirectf("%R/ckout");
}else{
fossil_redirect_home() /*does not return*/;
}
}else{
zPath = mprintf("%s", zPathInfo);
}
/* Make g.zPath point to the first element of the path. Make
** g.zExtra point to everything past that point.
*/
|
| ︙ | ︙ | |||
3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 | ** /doc/ckout/... ** --create Create a new REPOSITORY if it does not already exist ** --errorlog FILE Append HTTP error messages to FILE ** --extroot DIR Document root for the /ext extension mechanism ** --files GLOBLIST Comma-separated list of glob patterns for static files ** --fossilcmd PATH The pathname of the "fossil" executable on the remote ** system when REPOSITORY is remote. ** --localauth Enable automatic login for requests from localhost ** --localhost Listen on 127.0.0.1 only (always true for "ui") ** --https Indicates that the input is coming through a reverse ** proxy that has already translated HTTPS into HTTP. ** --jsmode MODE Determine how JavaScript is delivered with pages. ** Mode can be one of: ** inline All JavaScript is inserted inline at | > | 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 | ** /doc/ckout/... ** --create Create a new REPOSITORY if it does not already exist ** --errorlog FILE Append HTTP error messages to FILE ** --extroot DIR Document root for the /ext extension mechanism ** --files GLOBLIST Comma-separated list of glob patterns for static files ** --fossilcmd PATH The pathname of the "fossil" executable on the remote ** system when REPOSITORY is remote. ** --from PATH Use PATH as the diff baseline for the /ckout page ** --localauth Enable automatic login for requests from localhost ** --localhost Listen on 127.0.0.1 only (always true for "ui") ** --https Indicates that the input is coming through a reverse ** proxy that has already translated HTTPS into HTTP. ** --jsmode MODE Determine how JavaScript is delivered with pages. ** Mode can be one of: ** inline All JavaScript is inserted inline at |
| ︙ | ︙ | |||
3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 | int fCreate = 0; /* The --create flag */ int fNoBrowser = 0; /* Do not auto-launch web-browser */ const char *zInitPage = 0; /* Start on this page. --page option */ int findServerArg = 2; /* argv index for find_server_repository() */ char *zRemote = 0; /* Remote host on which to run "fossil ui" */ const char *zJsMode; /* The --jsmode parameter */ const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */ #if USE_SEE db_setup_for_saved_encryption_key(); #endif #if defined(_WIN32) | > | 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 | int fCreate = 0; /* The --create flag */ int fNoBrowser = 0; /* Do not auto-launch web-browser */ const char *zInitPage = 0; /* Start on this page. --page option */ int findServerArg = 2; /* argv index for find_server_repository() */ char *zRemote = 0; /* Remote host on which to run "fossil ui" */ const char *zJsMode; /* The --jsmode parameter */ const char *zFossilCmd =0; /* Name of "fossil" binary on remote system */ const char *zFrom; /* Value for --from */ #if USE_SEE db_setup_for_saved_encryption_key(); #endif #if defined(_WIN32) |
| ︙ | ︙ | |||
3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 |
zTimeout = find_option("max-latency",0,1);
#endif
g.useLocalauth = find_option("localauth", 0, 0)!=0;
Th_InitTraceLog();
zPort = find_option("port", "P", 1);
isUiCmd = g.argv[1][0]=='u';
if( isUiCmd ){
zInitPage = find_option("page", "p", 1);
if( zInitPage && zInitPage[0]=='/' ) zInitPage++;
zFossilCmd = find_option("fossilcmd", 0, 1);
}
zNotFound = find_option("notfound", 0, 1);
allowRepoList = find_option("repolist",0,0)!=0;
if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1;
zAltBase = find_option("baseurl", 0, 1);
fCreate = find_option("create",0,0)!=0;
g.zReqType = "HTTP";
| > > > > > > > > | 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 |
zTimeout = find_option("max-latency",0,1);
#endif
g.useLocalauth = find_option("localauth", 0, 0)!=0;
Th_InitTraceLog();
zPort = find_option("port", "P", 1);
isUiCmd = g.argv[1][0]=='u';
if( isUiCmd ){
zFrom = find_option("from", 0, 1);
if( zFrom && zFrom==file_tail(zFrom) ){
fossil_fatal("the argument to --from must be a pathname for"
" the \"ui\" command");
}
zInitPage = find_option("page", "p", 1);
if( zInitPage && zInitPage[0]=='/' ) zInitPage++;
zFossilCmd = find_option("fossilcmd", 0, 1);
if( zFrom && zInitPage==0 ){
zInitPage = mprintf("ckout?exbase=%H", zFrom);
}
}
zNotFound = find_option("notfound", 0, 1);
allowRepoList = find_option("repolist",0,0)!=0;
if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1;
zAltBase = find_option("baseurl", 0, 1);
fCreate = find_option("create",0,0)!=0;
g.zReqType = "HTTP";
|
| ︙ | ︙ | |||
3354 3355 3356 3357 3358 3359 3360 |
** chdir to that check-out so that the current version
** gets highlighted in the timeline by default. */
const char * zDir = g.argv[2];
if(dir_has_ckout_db(zDir)){
if(0!=file_chdir(zDir, 0)){
fossil_fatal("Cannot chdir to %s", zDir);
}
| | | 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 |
** chdir to that check-out so that the current version
** gets highlighted in the timeline by default. */
const char * zDir = g.argv[2];
if(dir_has_ckout_db(zDir)){
if(0!=file_chdir(zDir, 0)){
fossil_fatal("Cannot chdir to %s", zDir);
}
findServerArg = g.argc;
fCreate = 0;
g.argv[2] = 0;
--g.argc;
}
}
if( isUiCmd && 3==g.argc
&& (zRemote = (char*)file_skip_userhost(g.argv[2]))!=0
|
| ︙ | ︙ | |||
3382 3383 3384 3385 3386 3387 3388 |
g.useLocalauth = 1;
allowRepoList = 1;
}
if( !zRemote ){
find_server_repository(findServerArg, fCreate);
}
if( zInitPage==0 ){
| < < < | < | 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 |
g.useLocalauth = 1;
allowRepoList = 1;
}
if( !zRemote ){
find_server_repository(findServerArg, fCreate);
}
if( zInitPage==0 ){
zInitPage = "";
}
if( zPort ){
if( strchr(zPort,':') ){
int i;
for(i=strlen(zPort)-1; i>=0 && zPort[i]!=':'; i--){}
if( i>0 ){
if( zPort[0]=='[' && zPort[i-1]==']' ){
|
| ︙ | ︙ |
Changes to src/main.mk.
| ︙ | ︙ | |||
97 98 99 100 101 102 103 104 105 106 107 108 109 110 | $(SRCDIR)/loadctrl.c \ $(SRCDIR)/login.c \ $(SRCDIR)/lookslike.c \ $(SRCDIR)/main.c \ $(SRCDIR)/manifest.c \ $(SRCDIR)/markdown.c \ $(SRCDIR)/markdown_html.c \ $(SRCDIR)/md5.c \ $(SRCDIR)/merge.c \ $(SRCDIR)/merge3.c \ $(SRCDIR)/moderate.c \ $(SRCDIR)/name.c \ $(SRCDIR)/patch.c \ $(SRCDIR)/path.c \ | > | 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | $(SRCDIR)/loadctrl.c \ $(SRCDIR)/login.c \ $(SRCDIR)/lookslike.c \ $(SRCDIR)/main.c \ $(SRCDIR)/manifest.c \ $(SRCDIR)/markdown.c \ $(SRCDIR)/markdown_html.c \ $(SRCDIR)/match.c \ $(SRCDIR)/md5.c \ $(SRCDIR)/merge.c \ $(SRCDIR)/merge3.c \ $(SRCDIR)/moderate.c \ $(SRCDIR)/name.c \ $(SRCDIR)/patch.c \ $(SRCDIR)/path.c \ |
| ︙ | ︙ | |||
246 247 248 249 250 251 252 253 254 255 256 257 258 259 | $(SRCDIR)/fossil.wikiedit-wysiwyg.js \ $(SRCDIR)/graph.js \ $(SRCDIR)/hbmenu.js \ $(SRCDIR)/href.js \ $(SRCDIR)/login.js \ $(SRCDIR)/markdown.md \ $(SRCDIR)/menu.js \ $(SRCDIR)/scroll.js \ $(SRCDIR)/skin.js \ $(SRCDIR)/sorttable.js \ $(SRCDIR)/sounds/0.wav \ $(SRCDIR)/sounds/1.wav \ $(SRCDIR)/sounds/2.wav \ $(SRCDIR)/sounds/3.wav \ | > | 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 | $(SRCDIR)/fossil.wikiedit-wysiwyg.js \ $(SRCDIR)/graph.js \ $(SRCDIR)/hbmenu.js \ $(SRCDIR)/href.js \ $(SRCDIR)/login.js \ $(SRCDIR)/markdown.md \ $(SRCDIR)/menu.js \ $(SRCDIR)/merge.tcl \ $(SRCDIR)/scroll.js \ $(SRCDIR)/skin.js \ $(SRCDIR)/sorttable.js \ $(SRCDIR)/sounds/0.wav \ $(SRCDIR)/sounds/1.wav \ $(SRCDIR)/sounds/2.wav \ $(SRCDIR)/sounds/3.wav \ |
| ︙ | ︙ | |||
361 362 363 364 365 366 367 368 369 370 371 372 373 374 | $(OBJDIR)/loadctrl_.c \ $(OBJDIR)/login_.c \ $(OBJDIR)/lookslike_.c \ $(OBJDIR)/main_.c \ $(OBJDIR)/manifest_.c \ $(OBJDIR)/markdown_.c \ $(OBJDIR)/markdown_html_.c \ $(OBJDIR)/md5_.c \ $(OBJDIR)/merge_.c \ $(OBJDIR)/merge3_.c \ $(OBJDIR)/moderate_.c \ $(OBJDIR)/name_.c \ $(OBJDIR)/patch_.c \ $(OBJDIR)/path_.c \ | > | 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 | $(OBJDIR)/loadctrl_.c \ $(OBJDIR)/login_.c \ $(OBJDIR)/lookslike_.c \ $(OBJDIR)/main_.c \ $(OBJDIR)/manifest_.c \ $(OBJDIR)/markdown_.c \ $(OBJDIR)/markdown_html_.c \ $(OBJDIR)/match_.c \ $(OBJDIR)/md5_.c \ $(OBJDIR)/merge_.c \ $(OBJDIR)/merge3_.c \ $(OBJDIR)/moderate_.c \ $(OBJDIR)/name_.c \ $(OBJDIR)/patch_.c \ $(OBJDIR)/path_.c \ |
| ︙ | ︙ | |||
510 511 512 513 514 515 516 517 518 519 520 521 522 523 | $(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 \ | > | 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 | $(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 \ |
| ︙ | ︙ | |||
702 703 704 705 706 707 708 | SQLITE3_OBJ. = $(SQLITE3_OBJ.0) SQLITE3_OBJ = $(SQLITE3_OBJ.$(SQLITE3_ORIGIN)) # The USE_LINENOISE variable may be undefined, set to 0, or set # to 1. If it is set to 0, then there is no need to build or link # the linenoise.o object. LINENOISE_DEF.0 = | | | 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 | SQLITE3_OBJ. = $(SQLITE3_OBJ.0) SQLITE3_OBJ = $(SQLITE3_OBJ.$(SQLITE3_ORIGIN)) # The USE_LINENOISE variable may be undefined, set to 0, or set # to 1. If it is set to 0, then there is no need to build or link # the linenoise.o object. LINENOISE_DEF.0 = LINENOISE_DEF.1 = -DHAVE_LINENOISE=2 LINENOISE_DEF. = $(LINENOISE_DEF.0) LINENOISE_OBJ.0 = LINENOISE_OBJ.1 = $(OBJDIR)/linenoise.o LINENOISE_OBJ. = $(LINENOISE_OBJ.0) # The USE_SEE variable may be undefined, 0 or 1. If undefined or 0, # in-tree SQLite is used. If 1, then sqlite3-see.c (not part of the |
| ︙ | ︙ | |||
845 846 847 848 849 850 851 852 853 854 855 856 857 858 | $(OBJDIR)/loadctrl_.c:$(OBJDIR)/loadctrl.h \ $(OBJDIR)/login_.c:$(OBJDIR)/login.h \ $(OBJDIR)/lookslike_.c:$(OBJDIR)/lookslike.h \ $(OBJDIR)/main_.c:$(OBJDIR)/main.h \ $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \ $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \ $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \ $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \ $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \ $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \ $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \ $(OBJDIR)/name_.c:$(OBJDIR)/name.h \ $(OBJDIR)/patch_.c:$(OBJDIR)/patch.h \ $(OBJDIR)/path_.c:$(OBJDIR)/path.h \ | > | 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 | $(OBJDIR)/loadctrl_.c:$(OBJDIR)/loadctrl.h \ $(OBJDIR)/login_.c:$(OBJDIR)/login.h \ $(OBJDIR)/lookslike_.c:$(OBJDIR)/lookslike.h \ $(OBJDIR)/main_.c:$(OBJDIR)/main.h \ $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \ $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \ $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \ $(OBJDIR)/match_.c:$(OBJDIR)/match.h \ $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \ $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \ $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \ $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \ $(OBJDIR)/name_.c:$(OBJDIR)/name.h \ $(OBJDIR)/patch_.c:$(OBJDIR)/patch.h \ $(OBJDIR)/path_.c:$(OBJDIR)/path.h \ |
| ︙ | ︙ | |||
1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 | $(OBJDIR)/markdown_html_.c: $(SRCDIR)/markdown_html.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/markdown_html.c >$@ $(OBJDIR)/markdown_html.o: $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/markdown_html.o -c $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h: $(OBJDIR)/headers $(OBJDIR)/md5_.c: $(SRCDIR)/md5.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/md5.c >$@ $(OBJDIR)/md5.o: $(OBJDIR)/md5_.c $(OBJDIR)/md5.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/md5.o -c $(OBJDIR)/md5_.c | > > > > > > > > | 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 | $(OBJDIR)/markdown_html_.c: $(SRCDIR)/markdown_html.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/markdown_html.c >$@ $(OBJDIR)/markdown_html.o: $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/markdown_html.o -c $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h: $(OBJDIR)/headers $(OBJDIR)/match_.c: $(SRCDIR)/match.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/match.c >$@ $(OBJDIR)/match.o: $(OBJDIR)/match_.c $(OBJDIR)/match.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/match.o -c $(OBJDIR)/match_.c $(OBJDIR)/match.h: $(OBJDIR)/headers $(OBJDIR)/md5_.c: $(SRCDIR)/md5.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/md5.c >$@ $(OBJDIR)/md5.o: $(OBJDIR)/md5_.c $(OBJDIR)/md5.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/md5.o -c $(OBJDIR)/md5_.c |
| ︙ | ︙ |
Added src/match.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 |
/*
** Copyright (c) 2007 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)
** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code to implement string comparisons using a
** variety of algorithm. The comparison algorithm can be any of:
**
** MS_EXACT The string must exactly match the pattern.
**
** MS_BRLIST The pattern is a space- and/or comma-separated
** list of strings, any one of which may match
** the input string.
**
** MS_GLOB Like BRLIST, except each component of the pattern
** is a GLOB expression.
**
** MS_LIKE Like BRLIST, except each component of the pattern
** is an SQL LIKE expression.
**
** MS_REGEXP Like BRLIST, except each component of the pattern
** is a regular expression.
**
*/
#include "config.h"
#include <string.h>
#include "match.h"
#if INTERFACE
/*
** Types of comparisons that we are able to perform:
*/
typedef enum {
MS_EXACT=1, /* Exact string comparison */
MS_GLOB=2, /* Matches against a list of GLOB patterns. */
MS_LIKE=3, /* Matches against a list of LIKE patterns. */
MS_REGEXP=4, /* Matches against a list of regular expressions. */
MS_BRLIST=5, /* Matches any element of a list */
} MatchStyle;
/*
** The following object represents a precompiled pattern to use for
** string matching.
**
** * Create an instance of this object using match_create().
** * Do comparisons using match_text().
** * Destroy using match_free() when you are done.
**
*/
struct Matcher {
MatchStyle style; /* Which algorithm to use */
int nPattern; /* How many patterns are their */
char **azPattern; /* List of patterns */
ReCompiled **aRe; /* List of compiled regular expressions */
};
#endif /*INTERFACE*/
/*
** Translate a "match style" text name into the MS_* enum value.
** Return eDflt if no match is found.
*/
MatchStyle match_style(const char *zStyle, MatchStyle eDflt){
if( zStyle==0 ) return eDflt;
if( fossil_stricmp(zStyle, "brlist")==0 ) return MS_BRLIST;
if( fossil_stricmp(zStyle, "list")==0 ) return MS_BRLIST;
if( fossil_stricmp(zStyle, "regexp")==0 ) return MS_REGEXP;
if( fossil_stricmp(zStyle, "re")==0 ) return MS_REGEXP;
if( fossil_stricmp(zStyle, "glob")==0 ) return MS_GLOB;
if( fossil_stricmp(zStyle, "like")==0 ) return MS_LIKE;
if( fossil_stricmp(zStyle, "exact")==0 ) return MS_EXACT;
return eDflt;
}
/*
** Create a new Matcher object using the pattern provided.
*/
Matcher *match_create(MatchStyle style, const char *zPat){
char cDel; /* Delimiter character */
int i; /* Loop counter */
Matcher *p; /* The new Matcher to be constructed */
char *zOne; /* One element of the pattern */
if( zPat==0 ) return 0;
p = fossil_malloc( sizeof(*p) );
memset(p, 0, sizeof(*p));
p->style = style;
if( style==MS_EXACT ){
p->nPattern = 1;
p->azPattern = fossil_malloc( sizeof(p->azPattern[0]) );
p->azPattern[0] = fossil_strdup(zPat);
return p;
}
while( 1 ){
/* Skip leading delimiters. */
for( ; fossil_isspace(*zPat) || *zPat==','; ++zPat );
/* Next non-delimiter character determines quoting. */
if( zPat[0]==0 ){
/* Terminate loop at end of string. */
break;
}else if( zPat[0]=='\'' || zPat[0]=='"' ){
/* If word is quoted, prepare to stop at end quote. */
cDel = zPat[0];
++zPat;
}else{
/* If word is not quoted, prepare to stop at delimiter. */
cDel = ',';
}
/* Find the next delimiter character or end of string. */
for( i=0; zPat[i] && zPat[i]!=cDel; ++i ){
/* If delimiter is comma, also recognize spaces as delimiters. */
if( cDel==',' && fossil_isspace(zPat[i]) ){
break;
}
/* In regexp mode, ignore delimiters following backslashes. */
if( style==MS_REGEXP && zPat[i]=='\\' && zPat[i+1] ){
++i;
}
}
/* zOne is a zero-terminated copy of the pattern, without delimiters */
zOne = fossil_strndup(zPat, i);
zPat += i;
if( zPat[0] ) zPat++;
/* Check for regular expression syntax errors. */
if( style==MS_REGEXP ){
ReCompiled *regexp;
const char *zFail = re_compile(®exp, zOne, 0);
if( zFail ){
re_free(regexp);
continue;
}
p->nPattern++;
p->aRe = fossil_realloc(p->aRe, sizeof(p->aRe)*p->nPattern);
p->aRe[p->nPattern-1] = regexp;
fossil_free(zOne);
}else{
p->nPattern++;
p->azPattern = fossil_realloc(p->azPattern, sizeof(char*)*p->nPattern);
p->azPattern[p->nPattern-1] = zOne;
}
}
return p;
}
/*
** Return non-zero (true) if the input string matches the pattern
** described by the matcher.
**
** The return value is really the 1-based index of the particular
** pattern that matched.
*/
int match_text(Matcher *p, const char *zText){
int i;
if( p==0 ){
return zText==0;
}
switch( p->style ){
case MS_BRLIST:
case MS_EXACT: {
for(i=0; i<p->nPattern; i++){
if( strcmp(p->azPattern[i], zText)==0 ) return i+1;
}
break;
}
case MS_GLOB: {
for(i=0; i<p->nPattern; i++){
if( sqlite3_strglob(p->azPattern[i], zText)==0 ) return i+1;
}
break;
}
case MS_LIKE: {
for(i=0; i<p->nPattern; i++){
if( sqlite3_strlike(p->azPattern[i], zText, 0)==0 ) return i+1;
}
break;
}
case MS_REGEXP: {
int nText = (int)strlen(zText);
for(i=0; i<p->nPattern; i++){
if( re_match(p->aRe[i], (const u8*)zText, nText) ) return i+1;
}
break;
}
}
return 0;
}
/*
** Destroy a previously allocated Matcher object.
*/
void match_free(Matcher *p){
int i;
if( p==0 ) return;
if( p->style==MS_REGEXP ){
for(i=0; i<p->nPattern; i++) re_free(p->aRe[i]);
fossil_free(p->aRe);
}else{
for(i=0; i<p->nPattern; i++) fossil_free(p->azPattern[i]);
fossil_free(p->azPattern);
}
memset(p, 0, sizeof(*p));
fossil_free(p);
}
/*
** Quote a tag string by surrounding it with double quotes and preceding
** internal double quotes and backslashes with backslashes.
*/
static const char *tagQuote(
int len, /* Maximum length of zTag, or negative for unlimited */
const char *zTag /* Tag string */
){
Blob blob = BLOB_INITIALIZER;
int i, j;
blob_zero(&blob);
blob_append(&blob, "\"", 1);
for( i=j=0; zTag[j] && (len<0 || j<len); ++j ){
if( zTag[j]=='\"' || zTag[j]=='\\' ){
if( j>i ){
blob_append(&blob, zTag+i, j-i);
}
blob_append(&blob, "\\", 1);
i = j;
}
}
if( j>i ){
blob_append(&blob, zTag+i, j-i);
}
blob_append(&blob, "\"", 1);
return blob_str(&blob);
}
/*
** Construct the SQL expression that goes into the WHERE clause of a join
** that involves the TAG table and that selects a particular tag out of
** that table.
**
** This function is adapted from glob_expr() to support the MS_EXACT, MS_GLOB,
** MS_LIKE, MS_REGEXP, and MS_BRLIST match styles.
**
** For MS_EXACT, the returned expression
** checks for integer match against the tag ID which is looked up directly by
** this function. For the other modes, the returned SQL expression performs
** string comparisons against the tag names, so it is necessary to join against
** the tag table to access the "tagname" column.
**
** Each pattern is adjusted to to start with "sym-" and be anchored at end.
**
** In MS_REGEXP mode, backslash can be used to protect delimiter characters.
** The backslashes are not removed from the regular expression.
**
** In addition to assembling and returning an SQL expression, this function
** makes an English-language description of the patterns being matched, suitable
** for display in the web interface.
**
** If any errors arise during processing, *zError is set to an error message.
** Otherwise it is set to NULL.
*/
const char *match_tag_sqlexpr(
MatchStyle matchStyle, /* Match style code */
const char *zTag, /* Tag name, match pattern, or pattern list */
const char **zDesc, /* Output expression description string */
const char **zError /* Output error string */
){
Blob expr = BLOB_INITIALIZER; /* SQL expression string assembly buffer */
Blob desc = BLOB_INITIALIZER; /* English description of match patterns */
Blob err = BLOB_INITIALIZER; /* Error text assembly buffer */
const char *zStart; /* Text at start of expression */
const char *zDelimiter; /* Text between expression terms */
const char *zEnd; /* Text at end of expression */
const char *zPrefix; /* Text before each match pattern */
const char *zSuffix; /* Text after each match pattern */
const char *zIntro; /* Text introducing pattern description */
const char *zPattern = 0; /* Previous quoted pattern */
const char *zFail = 0; /* Current failure message or NULL if okay */
const char *zOr = " or "; /* Text before final quoted pattern */
char cDel; /* Input delimiter character */
int i; /* Input match pattern length counter */
/* Optimize exact matches by looking up the ID in advance to create a simple
* numeric comparison. Bypass the remainder of this function. */
if( matchStyle==MS_EXACT ){
*zDesc = tagQuote(-1, zTag);
return mprintf("(tagid=%d)", db_int(-1,
"SELECT tagid FROM tag WHERE tagname='sym-%q'", zTag));
}
/* Decide pattern prefix and suffix strings according to match style. */
if( matchStyle==MS_GLOB ){
zStart = "(";
zDelimiter = " OR ";
zEnd = ")";
zPrefix = "tagname GLOB 'sym-";
zSuffix = "'";
zIntro = "glob pattern ";
}else if( matchStyle==MS_LIKE ){
zStart = "(";
zDelimiter = " OR ";
zEnd = ")";
zPrefix = "tagname LIKE 'sym-";
zSuffix = "'";
zIntro = "SQL LIKE pattern ";
}else if( matchStyle==MS_REGEXP ){
zStart = "(tagname REGEXP '^sym-(";
zDelimiter = "|";
zEnd = ")$')";
zPrefix = "";
zSuffix = "";
zIntro = "regular expression ";
}else/* if( matchStyle==MS_BRLIST )*/{
zStart = "tagname IN ('sym-";
zDelimiter = "','sym-";
zEnd = "')";
zPrefix = "";
zSuffix = "";
zIntro = "";
}
/* Convert the list of matches into an SQL expression and text description. */
blob_zero(&expr);
blob_zero(&desc);
blob_zero(&err);
while( 1 ){
/* Skip leading delimiters. */
for( ; fossil_isspace(*zTag) || *zTag==','; ++zTag );
/* Next non-delimiter character determines quoting. */
if( !*zTag ){
/* Terminate loop at end of string. */
break;
}else if( *zTag=='\'' || *zTag=='"' ){
/* If word is quoted, prepare to stop at end quote. */
cDel = *zTag;
++zTag;
}else{
/* If word is not quoted, prepare to stop at delimiter. */
cDel = ',';
}
/* Find the next delimiter character or end of string. */
for( i=0; zTag[i] && zTag[i]!=cDel; ++i ){
/* If delimiter is comma, also recognize spaces as delimiters. */
if( cDel==',' && fossil_isspace(zTag[i]) ){
break;
}
/* In regexp mode, ignore delimiters following backslashes. */
if( matchStyle==MS_REGEXP && zTag[i]=='\\' && zTag[i+1] ){
++i;
}
}
/* Check for regular expression syntax errors. */
if( matchStyle==MS_REGEXP ){
ReCompiled *regexp;
char *zTagDup = fossil_strndup(zTag, i);
zFail = re_compile(®exp, zTagDup, 0);
re_free(regexp);
fossil_free(zTagDup);
}
/* Process success and error results. */
if( !zFail ){
/* Incorporate the match word into the output expression. %q is used to
* protect against SQL injection attacks by replacing ' with ''. */
blob_appendf(&expr, "%s%s%#q%s", blob_size(&expr) ? zDelimiter : zStart,
zPrefix, i, zTag, zSuffix);
/* Build up the description string. */
if( !blob_size(&desc) ){
/* First tag: start with intro followed by first quoted tag. */
blob_append(&desc, zIntro, -1);
blob_append(&desc, tagQuote(i, zTag), -1);
}else{
if( zPattern ){
/* Third and subsequent tags: append comma then previous tag. */
blob_append(&desc, ", ", 2);
blob_append(&desc, zPattern, -1);
zOr = ", or ";
}
/* Second and subsequent tags: store quoted tag for next iteration. */
zPattern = tagQuote(i, zTag);
}
}else{
/* On error, skip the match word and build up the error message buffer. */
if( !blob_size(&err) ){
blob_append(&err, "Error: ", 7);
}else{
blob_append(&err, ", ", 2);
}
blob_appendf(&err, "(%s%s: %s)", zIntro, tagQuote(i, zTag), zFail);
}
/* Advance past all consumed input characters. */
zTag += i;
if( cDel!=',' && *zTag==cDel ){
++zTag;
}
}
/* Finalize and extract the pattern description. */
if( zPattern ){
blob_append(&desc, zOr, -1);
blob_append(&desc, zPattern, -1);
}
*zDesc = blob_str(&desc);
/* Finalize and extract the error text. */
*zError = blob_size(&err) ? blob_str(&err) : 0;
/* Finalize and extract the SQL expression. */
if( blob_size(&expr) ){
blob_append(&expr, zEnd, -1);
return blob_str(&expr);
}
/* If execution reaches this point, the pattern was empty. Return NULL. */
return 0;
}
|
Changes to src/merge.c.
| ︙ | ︙ | |||
18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
** This file contains code used to merge two or more branches into
** a single tree.
*/
#include "config.h"
#include "merge.h"
#include <assert.h>
/*
** Print information about a particular check-in.
*/
void print_checkin_description(int rid, int indent, const char *zLabel){
Stmt q;
db_prepare(&q,
"SELECT datetime(mtime,toLocal()),"
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 |
** This file contains code used to merge two or more branches into
** a single tree.
*/
#include "config.h"
#include "merge.h"
#include <assert.h>
/*
** Bring up a Tcl/Tk GUI to show details of the most recent merge.
*/
static void merge_info_tk(int bDark, int bAll, int nContext){
int i;
Blob script;
const char *zTempFile = 0;
char *zCmd;
const char *zTclsh;
zTclsh = find_option("tclsh",0,1);
if( zTclsh==0 ){
zTclsh = db_get("tclsh",0);
}
/* The undocumented --script FILENAME option causes the Tk script to
** be written into the FILENAME instead of being run. This is used
** for testing and debugging. */
zTempFile = find_option("script",0,1);
verify_all_options();
blob_zero(&script);
blob_appendf(&script, "set ncontext %d\n", nContext);
blob_appendf(&script, "set fossilcmd {| \"%/\" merge-info}\n",
g.nameOfExe);
blob_appendf(&script, "set filelist [list");
if( g.argc==2 ){
/* No files named on the command-line. Use every file mentioned
** in the MERGESTAT table to generate the file list. */
Stmt q;
int cnt = 0;
db_prepare(&q,
"WITH priority(op,pri) AS (VALUES('CONFLICT',0),('ERROR',0),"
"('MERGE',1),('ADDED',2),('UPDATE',2))"
"SELECT coalesce(fnr,fn), op FROM mergestat JOIN priority USING(op)"
" %s ORDER BY pri, 1",
bAll ? "" : "WHERE op IN ('MERGE','CONFLICT')" /*safe-for-%s*/
);
while( db_step(&q)==SQLITE_ROW ){
blob_appendf(&script," %s ", db_column_text(&q,1));
blob_append_tcl_literal(&script, db_column_text(&q,0),
db_column_bytes(&q,0));
cnt++;
}
db_finalize(&q);
if( cnt==0 ){
fossil_print(
"No interesting changes in this merge. Use --all to see everything\n"
);
return;
}
}else{
/* Use only files named on the command-line in the file list.
** But verify each file named is actually found in the MERGESTAT
** table first. */
for(i=2; i<g.argc; i++){
char *zFile; /* Input filename */
char *zTreename; /* Name of the file in the tree */
Blob fname; /* Filename relative to root */
char *zOp; /* Operation on this file */
zFile = mprintf("%/", g.argv[i]);
file_tree_name(zFile, &fname, 0, 1);
fossil_free(zFile);
zTreename = blob_str(&fname);
zOp = db_text(0, "SELECT op FROM mergestat WHERE fn=%Q or fnr=%Q",
zTreename, zTreename);
blob_appendf(&script, " %s ", zOp);
fossil_free(zOp);
blob_append_tcl_literal(&script, zTreename, (int)strlen(zTreename));
blob_reset(&fname);
}
}
blob_appendf(&script, "]\n");
blob_appendf(&script, "set darkmode %d\n", bDark!=0);
blob_appendf(&script, "%s", builtin_file("merge.tcl", 0));
if( zTempFile ){
blob_write_to_file(&script, zTempFile);
fossil_print("To see the merge, run: %s \"%s\"\n", zTclsh, zTempFile);
}else{
#if defined(FOSSIL_ENABLE_TCL)
Th_FossilInit(TH_INIT_DEFAULT);
if( evaluateTclWithEvents(g.interp, &g.tcl, blob_str(&script),
blob_size(&script), 1, 1, 0)==TCL_OK ){
blob_reset(&script);
return;
}
/*
* If evaluation of the Tcl script fails, the reason may be that Tk
* could not be found by the loaded Tcl, or that Tcl cannot be loaded
* dynamically (e.g. x64 Tcl with x86 Fossil). Therefore, fallback
* to using the external "tclsh", if available.
*/
#endif
zTempFile = write_blob_to_temp_file(&script);
zCmd = mprintf("%$ %$", zTclsh, zTempFile);
fossil_system(zCmd);
file_delete(zTempFile);
fossil_free(zCmd);
}
blob_reset(&script);
}
/*
** Generate a TCL list on standard output that can be fed into the
** merge.tcl script to show the details of the most recent merge
** command associated with file "zFName". zFName must be the filename
** relative to the root of the check-in - in other words a "tree name".
**
** When this routine is called, we know that the mergestat table
** exists, but we do not know if zFName is mentioned in that table.
*/
static void merge_info_tcl(const char *zFName, int nContext){
const char *zTreename;/* Name of the file in the tree */
Stmt q; /* To query the MERGESTAT table */
MergeBuilder mb; /* The merge builder object */
Blob pivot,v1,v2,out; /* Blobs for holding content */
const char *zFN; /* A filename */
int rid; /* RID value */
int sz; /* File size value */
zTreename = zFName;
db_prepare(&q,
/* 0 1 2 3 4 5 6 7 */
"SELECT fnp, ridp, fn, ridv, sz, fnm, ridm, fnr"
" FROM mergestat"
" WHERE fnp=%Q OR fnr=%Q",
zTreename, zTreename
);
if( db_step(&q)!=SQLITE_ROW ){
db_finalize(&q);
fossil_print("ERROR {don't know anything about file: %s}\n", zTreename);
return;
}
mergebuilder_init_tcl(&mb);
mb.nContext = nContext;
/* Set up the pivot */
zFN = db_column_text(&q, 0);
if( zFN==0 ){
/* No pivot because the file was added */
mb.zPivot = "(no baseline)";
blob_zero(&pivot);
}else{
mb.zPivot = mprintf("%s (baseline)", file_tail(zFN));
rid = db_column_int(&q, 1);
content_get(rid, &pivot);
}
mb.pPivot = &pivot;
/* Set up the merge-in as V2 */
zFN = db_column_text(&q, 5);
if( zFN==0 ){
/* File deleted in the merged-in branch */
mb.zV2 = "(deleted file)";
blob_zero(&v2);
}else{
mb.zV2 = mprintf("%s (merge-in)", file_tail(zFN));
rid = db_column_int(&q, 6);
content_get(rid, &v2);
}
mb.pV2 = &v2;
/* Set up the local content as V1 */
zFN = db_column_text(&q, 2);
if( zFN==0 ){
/* File added by merge */
mb.zV1 = "(no original)";
blob_zero(&v1);
}else{
mb.zV1 = mprintf("%s (local)", file_tail(zFN));
rid = db_column_int(&q, 3);
sz = db_column_int(&q, 4);
if( rid==0 && sz>0 ){
/* The origin file had been edited so we'll have to pull its
** original content out of the undo buffer */
Stmt q2;
db_prepare(&q2,
"SELECT content FROM undo"
" WHERE pathname=%Q AND octet_length(content)=%d",
zFN, sz
);
blob_zero(&v1);
if( db_step(&q2)==SQLITE_ROW ){
db_column_blob(&q2, 0, &v1);
}else{
mb.zV1 = "(local content missing)";
}
db_finalize(&q2);
}else{
/* The origin file was unchanged when the merge first occurred */
content_get(rid, &v1);
}
}
mb.pV1 = &v1;
/* Set up the output */
zFN = db_column_text(&q, 7);
if( zFN==0 ){
mb.zOut = "(Merge Result)";
}else{
mb.zOut = mprintf("%s (after merge)", file_tail(zFN));
}
blob_zero(&out);
mb.pOut = &out;
merge_three_blobs(&mb);
blob_write_to_file(&out, "-");
mb.xDestroy(&mb);
blob_reset(&pivot);
blob_reset(&v1);
blob_reset(&v2);
blob_reset(&out);
db_finalize(&q);
}
/*
** COMMAND: merge-info
**
** Usage: %fossil merge-info [OPTIONS]
**
** Display information about the most recent merge operation.
**
** Options:
** -a|--all Show all file changes that happened because of
** the merge. Normally only MERGE, CONFLICT, and ERROR
** lines are shown
** -c|--context N Show N lines of context around each change,
** with negative N meaning show all content. Only
** meaningful in combination with --tcl or --tk.
** --dark Use dark mode for the Tcl/Tk-based GUI
** --tcl FILE Generate (to stdout) a TCL list containing
** information needed to display the changes to
** FILE caused by the most recent merge. FILE must
** be a pathname relative to the root of the check-out.
** --tk Bring up a Tcl/Tk GUI that shows the changes
** associated with the most recent merge.
**
*/
void merge_info_cmd(void){
const char *zCnt;
const char *zTcl;
int bTk;
int bDark;
int bAll;
int nContext;
Stmt q;
const char *zWhere;
int cnt = 0;
db_must_be_within_tree();
zTcl = find_option("tcl", 0, 1);
bTk = find_option("tk", 0, 0)!=0;
zCnt = find_option("context", "c", 1);
bDark = find_option("dark", 0, 0)!=0;
bAll = find_option("all", "a", 0)!=0;
if( bTk==0 ){
verify_all_options();
if( g.argc>2 ){
usage("[OPTIONS]");
}
}
if( zCnt ){
nContext = atoi(zCnt);
if( nContext<0 ) nContext = 0xfffffff;
}else{
nContext = 6;
}
if( !db_table_exists("localdb","mergestat") ){
if( zTcl ){
fossil_print("ERROR {no merge data available}\n");
}else{
fossil_print("No merge data is available\n");
}
return;
}
if( bTk ){
merge_info_tk(bDark, bAll, nContext);
return;
}
if( zTcl ){
merge_info_tcl(zTcl, nContext);
return;
}
if( bAll ){
zWhere = "";
}else{
zWhere = "WHERE op IN ('MERGE','CONFLICT','ERROR')";
}
db_prepare(&q,
"WITH priority(op,pri) AS (VALUES('CONFLICT',0),('ERROR',0),"
"('MERGE',1),('ADDED',2),('UPDATE',2))"
/* 0 1 2 */
"SELECT op, coalesce(fnr,fn), msg"
" FROM mergestat JOIN priority USING(op)"
" %s"
" ORDER BY pri, coalesce(fnr,fn)",
zWhere /*safe-for-%s*/
);
while( db_step(&q)==SQLITE_ROW ){
const char *zOp = db_column_text(&q, 0);
const char *zName = db_column_text(&q, 1);
const char *zErr = db_column_text(&q, 2);
if( zErr && fossil_strcmp(zOp,"CONFLICT")!=0 ){
fossil_print("%-9s %s (%s)\n", zOp, zName, zErr);
}else{
fossil_print("%-9s %s\n", zOp, zName);
}
cnt++;
}
db_finalize(&q);
if( !bAll && cnt==0 ){
fossil_print(
"No interesting changes in this merge. Use --all to see everything.\n"
);
}
}
/*
** Erase all information about prior merges. Do this, for example, after
** a commit.
*/
void merge_info_forget(void){
db_multi_exec(
"DROP TABLE IF EXISTS localdb.mergestat;"
"DELETE FROM localdb.vvar WHERE name glob 'mergestat-*';"
);
}
/*
** Initialize the MERGESTAT table.
**
** Notes about mergestat:
**
** * ridv is a positive integer and sz is NULL if the V file contained
** no local edits prior to the merge. If the V file was modified prior
** to the merge then ridv is NULL and sz is the size of the file prior
** to merge.
**
** * fnp, ridp, fn, ridv, and sz are all NULL for a file that was
** added by merge.
*/
void merge_info_init(void){
merge_info_forget();
db_multi_exec(
"CREATE TABLE localdb.mergestat(\n"
" op TEXT, -- 'UPDATE', 'ADDED', 'MERGE', etc...\n"
" fnp TEXT, -- Name of the pivot file (P)\n"
" ridp INT, -- RID for the pivot file\n"
" fn TEXT, -- Name of origin file (V)\n"
" ridv INT, -- RID for origin file, or NULL if previously edited\n"
" sz INT, -- Size of origin file in bytes, NULL if unedited\n"
" fnm TEXT, -- Name of the file being merged in (M)\n"
" ridm INT, -- RID for the merge-in file\n"
" fnr TEXT, -- Name of the final output file, after all renaming\n"
" nc INT DEFAULT 0, -- Number of conflicts\n"
" msg TEXT -- Error message\n"
");"
);
}
/*
** Print information about a particular check-in.
*/
void print_checkin_description(int rid, int indent, const char *zLabel){
Stmt q;
db_prepare(&q,
"SELECT datetime(mtime,toLocal()),"
|
| ︙ | ︙ | |||
293 294 295 296 297 298 299 300 301 302 303 304 305 306 | ** "merge --cherrypick". ** ** Files which are renamed in the merged-in branch will be renamed in ** the current check-out. ** ** If the VERSION argument is omitted, then Fossil attempts to find ** a recent fork on the current branch to merge. ** ** If there are multiple VERSION arguments, then each VERSION is merged ** (or cherrypicked) in the order that they appear on the command-line. ** ** Options: ** --backout Do a reverse cherrypick merge against VERSION. ** In other words, back out the changes that were | > > > | 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 | ** "merge --cherrypick". ** ** Files which are renamed in the merged-in branch will be renamed in ** the current check-out. ** ** If the VERSION argument is omitted, then Fossil attempts to find ** a recent fork on the current branch to merge. ** ** Note that this command does not commit the merge, as that is a ** separate step. ** ** If there are multiple VERSION arguments, then each VERSION is merged ** (or cherrypicked) in the order that they appear on the command-line. ** ** Options: ** --backout Do a reverse cherrypick merge against VERSION. ** In other words, back out the changes that were |
| ︙ | ︙ | |||
318 319 320 321 322 323 324 | ** changes back to the nearest common ancestor. ** -f|--force Force the merge even if it would be a no-op ** --force-missing Force the merge even if there is missing content ** --integrate Merged branch will be closed when committing ** -K|--keep-merge-files On merge conflict, retain the temporary files ** used for merging, named *-baseline, *-original, ** and *-merge. | | > | 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 |
** changes back to the nearest common ancestor.
** -f|--force Force the merge even if it would be a no-op
** --force-missing Force the merge even if there is missing content
** --integrate Merged branch will be closed when committing
** -K|--keep-merge-files On merge conflict, retain the temporary files
** used for merging, named *-baseline, *-original,
** and *-merge.
** -n|--dry-run Do not actually change files on disk
** --nosync Do not auto-sync prior to merging
** --noundo Do not record changes in the undo log
** -v|--verbose Show additional details of the merge
*/
void merge_cmd(void){
int vid; /* Current version "V" */
int mid; /* Version we are merging from "M" */
int pid = 0; /* The pivot version - most recent common ancestor P */
int nid = 0; /* The name pivot version "N" */
|
| ︙ | ︙ | |||
345 346 347 348 349 350 351 352 353 354 355 356 357 358 | int keepMergeFlag; /* True if --keep-merge-files is present */ int nConflict = 0; /* Number of conflicts seen */ int nOverwrite = 0; /* Number of unmanaged files overwritten */ char vAncestor = 'p'; /* If P is an ancestor of V then 'p', else 'n' */ const char *zVersion; /* The VERSION argument */ int bMultiMerge = 0; /* True if there are two or more VERSION arguments */ int nMerge = 0; /* Number of prior merges processed */ Stmt q; /* SQL statment used for merge processing */ /* Notation: ** ** V The current check-out ** M The version being merged in | > | 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 | int keepMergeFlag; /* True if --keep-merge-files is present */ int nConflict = 0; /* Number of conflicts seen */ int nOverwrite = 0; /* Number of unmanaged files overwritten */ char vAncestor = 'p'; /* If P is an ancestor of V then 'p', else 'n' */ const char *zVersion; /* The VERSION argument */ int bMultiMerge = 0; /* True if there are two or more VERSION arguments */ int nMerge = 0; /* Number of prior merges processed */ int useUndo = 1; /* True to record changes in the undo log */ Stmt q; /* SQL statment used for merge processing */ /* Notation: ** ** V The current check-out ** M The version being merged in |
| ︙ | ︙ | |||
393 394 395 396 397 398 399 400 401 402 403 404 405 406 |
** Hints:
** * Combine --debug and --verbose for still more output.
** * The --dry-run option is also useful in combination with --debug.
*/
debugFlag = find_option("debug",0,0)!=0;
if( debugFlag && verboseFlag ) debugFlag = 2;
showVfileFlag = find_option("show-vfile",0,0)!=0;
verify_all_options();
db_must_be_within_tree();
if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
vid = db_lget_int("checkout", 0);
if( vid==0 ){
fossil_fatal("nothing is checked out");
| > > | 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 |
** Hints:
** * Combine --debug and --verbose for still more output.
** * The --dry-run option is also useful in combination with --debug.
*/
debugFlag = find_option("debug",0,0)!=0;
if( debugFlag && verboseFlag ) debugFlag = 2;
showVfileFlag = find_option("show-vfile",0,0)!=0;
useUndo = find_option("noundo",0,0)==0;
if( dryRunFlag ) useUndo = 0;
verify_all_options();
db_must_be_within_tree();
if( zBinGlob==0 ) zBinGlob = db_get("binary-glob",0);
vid = db_lget_int("checkout", 0);
if( vid==0 ){
fossil_fatal("nothing is checked out");
|
| ︙ | ︙ | |||
559 560 561 562 563 564 565 |
if( verboseFlag ){
print_checkin_description(mid, 12,
integrateFlag ? "integrate:" : "merge-from:");
print_checkin_description(pid, 12, "baseline:");
}
vfile_check_signature(vid, CKSIG_ENOTFILE);
if( nMerge==0 ) db_begin_transaction();
| | | 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 |
if( verboseFlag ){
print_checkin_description(mid, 12,
integrateFlag ? "integrate:" : "merge-from:");
print_checkin_description(pid, 12, "baseline:");
}
vfile_check_signature(vid, CKSIG_ENOTFILE);
if( nMerge==0 ) db_begin_transaction();
if( useUndo ) undo_begin();
if( load_vfile_from_rid(mid) && !forceMissingFlag ){
fossil_fatal("missing content, unable to merge");
}
if( load_vfile_from_rid(pid) && !forceMissingFlag ){
fossil_fatal("missing content, unable to merge");
}
if( zPivot ){
|
| ︙ | ︙ | |||
795 796 797 798 799 800 801 |
debug_fv_dump( debugFlag>=2 );
}
/************************************************************************
** All of the information needed to do the merge is now contained in the
** FV table. Starting here, we begin to actually carry out the merge.
**
| > > > > > | > | > < > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > | > > > > > > > > > > | 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 |
debug_fv_dump( debugFlag>=2 );
}
/************************************************************************
** All of the information needed to do the merge is now contained in the
** FV table. Starting here, we begin to actually carry out the merge.
**
** Begin by constructing the localdb.mergestat table.
*/
merge_info_init();
/*
** Find files that have changed from P->M but not P->V.
** Copy the M content over into V.
*/
db_prepare(&q,
/* 0 1 2 3 4 5 6 7 */
"SELECT idv, ridm, fn, islinkm, fnp, ridp, ridv, fnm FROM fv"
" WHERE idp>0 AND idv>0 AND idm>0"
" AND ridm!=ridp AND ridv=ridp AND NOT chnged"
);
while( db_step(&q)==SQLITE_ROW ){
int idv = db_column_int(&q, 0);
int ridm = db_column_int(&q, 1);
const char *zName = db_column_text(&q, 2);
int islinkm = db_column_int(&q, 3);
/* Copy content from idm over into idv. Overwrite idv. */
fossil_print("UPDATE %s\n", zName);
if( useUndo ) undo_save(zName);
if( !dryRunFlag ){
db_multi_exec(
"UPDATE vfile SET mtime=0, mrid=%d, chnged=%d, islink=%d,"
" mhash=CASE WHEN rid<>%d"
" THEN (SELECT uuid FROM blob WHERE blob.rid=%d) END"
" WHERE id=%d", ridm, integrateFlag?4:2, islinkm, ridm, ridm, idv
);
vfile_to_disk(0, idv, 0, 0);
}
db_multi_exec(
"INSERT INTO mergestat(op,fnp,ridp,fn,ridv,fnm,ridm,fnr)"
"VALUES('UPDATE',%Q,%d,%Q,%d,%Q,%d,%Q)",
/* fnp */ db_column_text(&q, 4),
/* ridp */ db_column_int(&q,5),
/* fn */ zName,
/* ridv */ db_column_int(&q,6),
/* fnm */ db_column_text(&q, 7),
/* ridm */ ridm,
/* fnr */ zName
);
}
db_finalize(&q);
/*
** Do a three-way merge on files that have changes on both P->M and P->V.
**
** Proceed even if the file doesn't exist on P, just like the common ancestor
** of M and V is an empty file. In this case, merge conflict marks will be
** added to the file and user will be forced to take a decision.
*/
db_prepare(&q,
/* 0 1 2 3 4 5 6 7 8 */
"SELECT ridm, idv, ridp, ridv, %z, fn, isexe, islinkv, islinkm,"
/* 9 10 11 */
" fnp, fnm, chnged"
" FROM fv"
" WHERE idv>0 AND idm>0"
" AND ridm!=ridp AND (ridv!=ridp OR chnged)",
glob_expr("fv.fn", zBinGlob)
);
while( db_step(&q)==SQLITE_ROW ){
int ridm = db_column_int(&q, 0);
int idv = db_column_int(&q, 1);
int ridp = db_column_int(&q, 2);
int ridv = db_column_int(&q, 3);
int isBinary = db_column_int(&q, 4);
const char *zName = db_column_text(&q, 5);
int isExe = db_column_int(&q, 6);
int islinkv = db_column_int(&q, 7);
int islinkm = db_column_int(&q, 8);
int chnged = db_column_int(&q, 11);
int rc;
char *zFullPath;
const char *zType = "MERGE";
Blob m, p, r;
/* Do a 3-way merge of idp->idm into idp->idv. The results go into idv. */
if( verboseFlag ){
fossil_print("MERGE %s (pivot=%d v1=%d v2=%d)\n",
zName, ridp, ridm, ridv);
}else{
fossil_print("MERGE %s\n", zName);
}
if( islinkv || islinkm ){
fossil_print("***** Cannot merge symlink %s\n", zName);
nConflict++;
db_multi_exec(
"INSERT INTO mergestat(op,fnp,ridp,fn,ridv,fnm,ridm,fnr,nc,msg)"
"VALUES('ERROR',%Q,%d,%Q,%d,%Q,%d,%Q,1,'cannot merge symlink')",
/* fnp */ db_column_text(&q, 9),
/* ridp */ ridp,
/* fn */ zName,
/* ridv */ ridv,
/* fnm */ db_column_text(&q, 10),
/* ridm */ ridm,
/* fnr */ zName
);
}else{
i64 sz;
const char *zErrMsg = 0;
int nc = 0;
if( useUndo ) undo_save(zName);
zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
sz = file_size(zFullPath, ExtFILE);
content_get(ridp, &p);
content_get(ridm, &m);
if( isBinary ){
rc = -1;
blob_zero(&r);
}else{
unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0;
if(keepMergeFlag!=0) mergeFlags |= MERGE_KEEP_FILES;
rc = merge_3way(&p, zFullPath, &m, &r, mergeFlags);
}
if( rc>=0 ){
if( !dryRunFlag ){
blob_write_to_file(&r, zFullPath);
file_setexe(zFullPath, isExe);
}
db_multi_exec("UPDATE vfile SET mtime=0 WHERE id=%d", idv);
if( rc>0 ){
fossil_print("***** %d merge conflict%s in %s\n",
rc, rc>1 ? "s" : "", zName);
nConflict++;
nc = rc;
zErrMsg = "merge conflicts";
zType = "CONFLICT";
}
}else{
fossil_print("***** Cannot merge binary file %s\n", zName);
nConflict++;
nc = 1;
zErrMsg = "cannot merge binary file";
zType = "ERROR";
}
db_multi_exec(
"INSERT INTO mergestat(op,fnp,ridp,fn,ridv,sz,fnm,ridm,fnr,nc,msg)"
"VALUES(%Q,%Q,%d,%Q,iif(%d,%d,NULL),iif(%d,%lld,NULL),%Q,%d,"
"%Q,%d,%Q)",
/* op */ zType,
/* fnp */ db_column_text(&q, 9),
/* ridp */ ridp,
/* fn */ zName,
/* ridv */ chnged==0, ridv,
/* sz */ chnged!=0, sz,
/* fnm */ db_column_text(&q, 10),
/* ridm */ ridm,
/* fnr */ zName,
/* nc */ nc,
/* msg */ zErrMsg
);
fossil_free(zFullPath);
blob_reset(&p);
blob_reset(&m);
blob_reset(&r);
}
vmerge_insert(idv, ridm);
}
db_finalize(&q);
/*
** Drop files that are in P and V but not in M
*/
db_prepare(&q,
"SELECT idv, fn, chnged, ridv FROM fv"
" WHERE idp>0 AND idv>0 AND idm=0"
);
while( db_step(&q)==SQLITE_ROW ){
int idv = db_column_int(&q, 0);
const char *zName = db_column_text(&q, 1);
int chnged = db_column_int(&q, 2);
int ridv = db_column_int(&q, 3);
int sz = -1;
const char *zErrMsg = 0;
int nc = 0;
/* Delete the file idv */
fossil_print("DELETE %s\n", zName);
if( chnged ){
char *zFullPath;
fossil_warning("WARNING: local edits lost for %s", zName);
nConflict++;
ridv = 0;
nc = 1;
zErrMsg = "local edits lost";
zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);
sz = file_size(zFullPath, ExtFILE);
fossil_free(zFullPath);
}
if( useUndo ) undo_save(zName);
db_multi_exec(
"UPDATE vfile SET deleted=1 WHERE id=%d", idv
);
if( !dryRunFlag ){
char *zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
file_delete(zFullPath);
free(zFullPath);
}
db_multi_exec(
"INSERT INTO localdb.mergestat(op,fnp,ridp,fn,ridv,sz,fnm,ridm,nc,msg)"
"VALUES('DELETE',NULL,NULL,%Q,iif(%d,%d,NULL),iif(%d,%d,NULL),"
"NULL,NULL,%d,%Q)",
/* fn */ zName,
/* ridv */ chnged==0, ridv,
/* sz */ chnged!=0, sz,
/* nc */ nc,
/* msg */ zErrMsg
);
}
db_finalize(&q);
/* For certain sets of renames (e.g. A -> B and B -> A), a file that is
** being renamed must first be moved to a temporary location to avoid
** being overwritten by another rename operation. A row is added to the
** TMPRN table for each of these temporary renames.
|
| ︙ | ︙ | |||
949 950 951 952 953 954 955 |
);
while( db_step(&q)==SQLITE_ROW ){
int idv = db_column_int(&q, 0);
const char *zOldName = db_column_text(&q, 1);
const char *zNewName = db_column_text(&q, 2);
int isExe = db_column_int(&q, 3);
fossil_print("RENAME %s -> %s\n", zOldName, zNewName);
| | | > > > > | 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 |
);
while( db_step(&q)==SQLITE_ROW ){
int idv = db_column_int(&q, 0);
const char *zOldName = db_column_text(&q, 1);
const char *zNewName = db_column_text(&q, 2);
int isExe = db_column_int(&q, 3);
fossil_print("RENAME %s -> %s\n", zOldName, zNewName);
if( useUndo ) undo_save(zOldName);
if( useUndo ) undo_save(zNewName);
db_multi_exec(
"UPDATE mergestat SET fnr=fnm WHERE fnp=%Q",
zOldName
);
db_multi_exec(
"UPDATE vfile SET pathname=NULL, origname=pathname"
" WHERE vid=%d AND pathname=%Q;"
"UPDATE vfile SET pathname=%Q, origname=coalesce(origname,pathname)"
" WHERE id=%d;",
vid, zNewName, zNewName, idv
);
|
| ︙ | ︙ | |||
1004 1005 1006 1007 1008 1009 1010 |
" WHERE pathname IS NULL"
);
/*
** Insert into V any files that are not in V or P but are in M.
*/
db_prepare(&q,
| | | 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 |
" WHERE pathname IS NULL"
);
/*
** Insert into V any files that are not in V or P but are in M.
*/
db_prepare(&q,
"SELECT idm, fnm, ridm FROM fv"
" WHERE idp=0 AND idv=0 AND idm>0"
);
while( db_step(&q)==SQLITE_ROW ){
int idm = db_column_int(&q, 0);
const char *zName;
char *zFullName;
db_multi_exec(
|
| ︙ | ︙ | |||
1037 1038 1039 1040 1041 1042 1043 1044 |
if( !dryRunFlag ) fossil_print(", original copy backed up locally");
fossil_print("\n");
nOverwrite++;
}else{
fossil_print("ADDED %s\n", zName);
}
fossil_free(zFullName);
if( !dryRunFlag ){
| > > > > > > > > < | 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 |
if( !dryRunFlag ) fossil_print(", original copy backed up locally");
fossil_print("\n");
nOverwrite++;
}else{
fossil_print("ADDED %s\n", zName);
}
fossil_free(zFullName);
db_multi_exec(
"INSERT INTO mergestat(op,fnm,ridm,fnr)"
"VALUES('ADDED',%Q,%d,%Q)",
/* fnm */ zName,
/* ridm */ db_column_int(&q,2),
/* fnr */ zName
);
if( useUndo ) undo_save(zName);
if( !dryRunFlag ){
vfile_to_disk(0, idm, 0, 0);
}
}
db_finalize(&q);
/* Report on conflicts
*/
|
| ︙ | ︙ | |||
1097 1098 1099 1100 1101 1102 1103 |
}else{
vmerge_insert(0, mid);
}
if( bMultiMerge && nConflict==0 ){
nMerge++;
goto merge_next_child;
}
| | | 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 |
}else{
vmerge_insert(0, mid);
}
if( bMultiMerge && nConflict==0 ){
nMerge++;
goto merge_next_child;
}
if( useUndo ) undo_finish();
db_end_transaction(dryRunFlag);
}
|
Added src/merge.tcl.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 |
# Show details of a 3-way merge operation. The left-most column is the
# common ancestor. The next two columns are edits of that common ancestor.
# The right-most column is the result of the merge.
#
# There is always a "fossilcmd" variable which tells the script how to
# invoke Fossil to get the information it needs. This script will
# automatically append "-c N" to tell Fossil how much context it wants.
#
# If the "filelist" global variable is defined, then it is a list of
# alternating "merge-type names" (ex: UPDATE, MERGE, CONFLICT, ERROR) and
# filenames. In that case, the initial display shows the changes for
# the first pair on the list and there is a optionmenu that allows the
# user to select other fiels on the list.
#
# There should also be a global variable named "ncontext" which is the
# number of lines of context to display. The value of this variable
# controls the "-c N" argument that is appended to fossilcmd.
#
# This header comment is stripped off by the "mkbuiltin.c" program.
#
package require Tk
array set CFG_light {
TITLE {Fossil Merge}
LN_COL_BG #dddddd
LN_COL_FG #444444
TXT_COL_BG #ffffff
TXT_COL_FG #000000
MKR_COL_BG #444444
MKR_COL_FG #dddddd
CHNG_BG #d0d070
ADD_BG #c0ffc0
RM_BG #ffc0c0
HR_FG #444444
HR_PAD_TOP 4
HR_PAD_BTM 8
FN_BG #444444
FN_FG #ffffff
FN_PAD 5
ERR_FG #ee0000
PADX 5
WIDTH 80
HEIGHT 45
LB_HEIGHT 25
}
array set CFG_dark {
TITLE {Fossil Merge}
LN_COL_BG #dddddd
LN_COL_FG #444444
TXT_COL_BG #3f3f3f
TXT_COL_FG #dcdccc
MKR_COL_BG #444444
MKR_COL_FG #dddddd
CHNG_BG #6a6a00
ADD_BG #57934c
RM_BG #ef6767
HR_FG #444444
HR_PAD_TOP 4
HR_PAD_BTM 8
FN_BG #5e5e5e
FN_FG #ffffff
FN_PAD 5
ERR_FG #ee0000
PADX 5
WIDTH 80
HEIGHT 45
LB_HEIGHT 25
}
array set CFG_arr {
0 CFG_light
1 CFG_dark
}
array set CFG [array get $CFG_arr($darkmode)]
if {![namespace exists ttk]} {
interp alias {} ::ttk::scrollbar {} ::scrollbar
interp alias {} ::ttk::menubutton {} ::menubutton
}
proc dehtml {x} {
set x [regsub -all {<[^>]*>} $x {}]
return [string map {& & < < > > ' ' " \"} $x]
}
proc cols {} {
return [list .lnA .txtA .lnB .txtB .lnC .txtC .lnD .txtD]
}
proc colType {c} {
regexp {[a-z]+} $c type
return $type
}
proc readMerge {args} {
global fossilcmd ncontext current_file
if {$ncontext=="All"} {
set cmd "$fossilcmd -c -1"
} else {
set cmd "$fossilcmd -c $ncontext"
}
if {[info exists current_file]} {
regsub {^[A-Z]+ } $current_file {} fn
append cmd " -tcl [list $fn]"
}
if {[catch {
set in [open $cmd r]
fconfigure $in -encoding utf-8
set mergetxt [read $in]
close $in
} msg]} {
tk_messageBox -message "Unable to run command: \"$cmd\""
set mergetxt {}
}
foreach c [cols] {
$c config -state normal
$c delete 1.0 end
}
set lnA 1
set lnB 1
set lnC 1
set lnD 1
foreach {A B C D} $mergetxt {
set key1 [string index $A 0]
if {$key1=="S"} {
scan [string range $A 1 end] "%d %d %d %d" nA nB nC nD
foreach x {A B C D} {
set N [set n$x]
incr ln$x $N
if {$N>0} {
.ln$x insert end ...\n hrln
.txt$x insert end [string repeat . 30]\n hrtxt
} else {
.ln$x insert end \n hrln
.txt$x insert end \n hrtxt
}
}
continue
}
set key2 [string index $B 0]
set key3 [string index $C 0]
set key4 [string index $D 0]
if {$key1=="."} {
.lnA insert end \n -
.txtA insert end \n -
} elseif {$key1=="N"} {
.nameA config -text [string range $A 1 end]
} else {
.lnA insert end $lnA\n -
incr lnA
if {$key1=="X"} {
.txtA insert end [string range $A 1 end]\n rm
} else {
.txtA insert end [string range $A 1 end]\n -
}
}
if {$key2=="."} {
.lnB insert end \n -
.txtB insert end \n -
} elseif {$key2=="N"} {
.nameB config -text [string range $B 1 end]
} else {
.lnB insert end $lnB\n -
incr lnB
if {$key4=="2"} {set tag chng} {set tag -}
if {$key2=="1"} {
.txtB insert end [string range $A 1 end]\n $tag
} elseif {$key2=="X"} {
.txtB insert end [string range $B 1 end]\n rm
} else {
.txtB insert end [string range $B 1 end]\n $tag
}
}
if {$key3=="."} {
.lnC insert end \n -
.txtC insert end \n -
} elseif {$key3=="N"} {
.nameC config -text [string range $C 1 end]
} else {
.lnC insert end $lnC\n -
incr lnC
if {$key4=="3"} {set tag add} {set tag -}
if {$key3=="1"} {
.txtC insert end [string range $A 1 end]\n $tag
} elseif {$key3=="2"} {
.txtC insert end [string range $B 1 end]\n chng
} elseif {$key3=="X"} {
.txtC insert end [string range $C 1 end]\n rm
} else {
.txtC insert end [string range $C 1 end]\n $tag
}
}
if {$key4=="."} {
.lnD insert end \n -
.txtD insert end \n -
} elseif {$key4=="N"} {
.nameD config -text [string range $D 1 end]
} else {
.lnD insert end $lnD\n -
incr lnD
if {$key4=="1"} {
.txtD insert end [string range $A 1 end]\n -
} elseif {$key4=="2"} {
.txtD insert end [string range $B 1 end]\n chng
} elseif {$key4=="3"} {
.txtD insert end [string range $C 1 end]\n add
} elseif {$key4=="X"} {
.txtD insert end [string range $D 1 end]\n rm
} else {
.txtD insert end [string range $D 1 end]\n -
}
}
}
foreach c [cols] {
set type [colType $c]
if {$type ne "txt"} {
$c config -width 6; # $widths($type)
}
$c config -state disabled
}
set mx $lnA
if {$lnB>$mx} {set mx $lnB}
if {$lnC>$mx} {set mx $lnC}
if {$lnD>$mx} {set mx $lnD}
global lnWidth
set lnWidth [string length [format +%d $mx]]
.lnA config -width $lnWidth
.lnB config -width $lnWidth
.lnC config -width $lnWidth
.lnD config -width $lnWidth
grid columnconfig . {0 2 4 6} -minsize $lnWidth
}
proc viewDiff {idx} {
.txtA yview $idx
.txtA xview moveto 0
}
proc cycleDiffs {{reverse 0}} {
if {$reverse} {
set range [.txtA tag prevrange fn @0,0 1.0]
if {$range eq ""} {
viewDiff {fn.last -1c}
} else {
viewDiff [lindex $range 0]
}
} else {
set range [.txtA tag nextrange fn {@0,0 +1c} end]
if {$range eq "" || [lindex [.txtA yview] 1] == 1} {
viewDiff fn.first
} else {
viewDiff [lindex $range 0]
}
}
}
proc xvis {col} {
set view [$col xview]
return [expr {[lindex $view 1]-[lindex $view 0]}]
}
proc scroll-x {args} {
set c .txt[expr {[xvis .txtA] < [xvis .txtB] ? "A" : "B"}]
eval $c xview $args
}
interp alias {} scroll-y {} .txtA yview
proc noop {args} {}
proc enableSync {axis} {
update idletasks
interp alias {} sync-$axis {}
rename _sync-$axis sync-$axis
}
proc disableSync {axis} {
rename sync-$axis _sync-$axis
interp alias {} sync-$axis {} noop
}
proc sync-y {first last} {
disableSync y
foreach c [cols] {
$c yview moveto $first
}
if {$first > 0 || $last < 1} {
grid .sby
.sby set $first $last
} else {
grid remove .sby
}
enableSync y
}
wm withdraw .
wm title . $CFG(TITLE)
wm iconname . $CFG(TITLE)
# Keystroke bindings for on the top-level window for navigation and
# control also fire when those same keystrokes are pressed in the
# Search entry box. Disable them, to prevent the diff screen from
# disappearing abruptly and unexpectedly when searching for "q".
#
bind . <Control-q> exit
bind . <Control-p> {catch searchPrev; break}
bind . <Control-n> {catch searchNext; break}
bind . <Escape><Escape> exit
bind . <Destroy> {after 0 exit}
bind . <Tab> {cycleDiffs; break}
bind . <<PrevWindow>> {cycleDiffs 1; break}
bind . <Control-f> {searchOnOff; break}
bind . <Control-g> {catch searchNext; break}
bind . <Return> {
event generate .bb.files <1>
event generate .bb.files <ButtonRelease-1>
break
}
foreach {key axis args} {
Up y {scroll -5 units}
k y {scroll -5 units}
Down y {scroll 5 units}
j y {scroll 5 units}
Left x {scroll -5 units}
h x {scroll -5 units}
Right x {scroll 5 units}
l x {scroll 5 units}
Prior y {scroll -1 page}
b y {scroll -1 page}
Next y {scroll 1 page}
space y {scroll 1 page}
Home y {moveto 0}
g y {moveto 0}
End y {moveto 1}
} {
bind . <$key> "scroll-$axis $args; break"
bind . <Shift-$key> continue
}
frame .bb
set useOptionMenu 1
if {[info exists filelist]} {
set current_file "[lindex $filelist 0] [lindex $filelist 1]"
if {[llength $filelist]>2} {
trace add variable current_file write readMerge
if {$tcl_platform(os)=="Darwin" || [llength $filelist]<30} {
set fnlist {}
foreach {op fn} $filelist {lappend fnlist "$op $fn"}
tk_optionMenu .bb.files current_file {*}$fnlist
} else {
set useOptionMenu 0
::ttk::menubutton .bb.files -text $current_file
if {[tk windowingsystem] eq "win32"} {
::ttk::style theme use winnative
.bb.files configure -padding {20 1 10 2}
}
toplevel .wfiles
wm withdraw .wfiles
update idletasks
wm transient .wfiles .
wm overrideredirect .wfiles 1
set ht [expr {[llength $filelist]/2}]
if {$ht>$CFG(LB_HEIGHT)} {set ht $CFG(LB_HEIGHT)}
listbox .wfiles.lb -width 0 -height $ht -activestyle none \
-yscroll {.wfiles.sb set}
set mx 1
foreach {op fn} $filelist {
set n [string length $fn]
if {$n>$mx} {set mx $n}
.wfiles.lb insert end "$op $fn"
}
.bb.files config -width $mx
::ttk::scrollbar .wfiles.sb -command {.wfiles.lb yview}
grid .wfiles.lb .wfiles.sb -sticky ns
bind .bb.files <1> {
set x [winfo rootx %W]
set y [expr {[winfo rooty %W]+[winfo height %W]}]
wm geometry .wfiles +$x+$y
wm deiconify .wfiles
focus .wfiles.lb
}
bind .wfiles <FocusOut> {wm withdraw .wfiles}
bind .wfiles <Escape> {focus .}
foreach evt {1 Return} {
bind .wfiles.lb <$evt> {
set ii [%W curselection]
set ::current_file [%W get $ii]
.bb.files config -text $::current_file
focus .
break
}
}
bind .wfiles.lb <Motion> {
%W selection clear 0 end
%W selection set @%x,%y
}
}
}
}
label .bb.ctxtag -text "Context:"
set context_choices {3 6 12 25 50 100 All}
if {$ncontext<0} {set ncontext All}
trace add variable ncontext write readMerge
if {$tcl_platform(os)=="Darwin" || $useOptionMenu} {
tk_optionMenu .bb.ctx ncontext {*}$context_choices
} else {
::ttk::menubutton .bb.ctx -text $ncontext
if {[tk windowingsystem] eq "win32"} {
::ttk::style theme use winnative
.bb.ctx configure -padding {20 1 10 2}
}
toplevel .wctx
wm withdraw .wctx
update idletasks
wm transient .wctx .
wm overrideredirect .wctx 1
listbox .wctx.lb -width 0 -height 7 -activestyle none
.wctx.lb insert end {*}$context_choices
pack .wctx.lb
bind .bb.ctx <1> {
set x [winfo rootx %W]
set y [expr {[winfo rooty %W]+[winfo height %W]}]
wm geometry .wctx +$x+$y
wm deiconify .wctx
focus .wctx.lb
}
bind .wctx <FocusOut> {wm withdraw .wctx}
bind .wctx <Escape> {focus .}
foreach evt {1 Return} {
bind .wctx.lb <$evt> {
set ::ncontext [lindex $::context_choices [%W curselection]]
.bb.ctx config -text $::ncontext
focus .
break
}
}
bind .wctx.lb <Motion> {
%W selection clear 0 end
%W selection set @%x,%y
}
}
foreach {side syncCol} {A .txtA B .txtB C .txtC D .txtD} {
set ln .ln$side
text $ln -width 6
$ln tag config - -justify right
set txt .txt$side
text $txt -width $CFG(WIDTH) -height $CFG(HEIGHT) -wrap none \
-xscroll ".sbx$side set"
catch {$txt config -tabstyle wordprocessor} ;# Required for Tk>=8.5
foreach tag {add rm chng} {
$txt tag config $tag -background $CFG([string toupper $tag]_BG)
$txt tag lower $tag
}
$txt tag config fn -background $CFG(FN_BG) -foreground $CFG(FN_FG) \
-justify center
$txt tag config err -foreground $CFG(ERR_FG)
}
text .mkr
set mxwidth [lindex [wm maxsize .] 0]
while {$CFG(WIDTH)>=40} {
set wanted [expr {([winfo reqwidth .lnA]+[winfo reqwidth .txtA])*4+30}]
if {$wanted<=$mxwidth} break
incr CFG(WIDTH) -10
.txtA config -width $CFG(WIDTH)
.txtB config -width $CFG(WIDTH)
.txtC config -width $CFG(WIDTH)
.txtD config -width $CFG(WIDTH)
}
foreach c [cols] {
set keyPrefix [string toupper [colType $c]]_COL_
if {[tk windowingsystem] eq "win32"} {$c config -font {courier 9}}
$c config -bg $CFG(${keyPrefix}BG) -fg $CFG(${keyPrefix}FG) -borderwidth 0 \
-padx $CFG(PADX) -yscroll sync-y
$c tag config hrln -spacing1 $CFG(HR_PAD_TOP) -spacing3 $CFG(HR_PAD_BTM) \
-foreground $CFG(HR_FG) -justify right
$c tag config hrtxt -spacing1 $CFG(HR_PAD_TOP) -spacing3 $CFG(HR_PAD_BTM) \
-foreground $CFG(HR_FG) -justify center
$c tag config fn -spacing1 $CFG(FN_PAD) -spacing3 $CFG(FN_PAD)
bindtags $c ". $c Text all"
bind $c <1> {focus %W}
}
label .nameA
label .nameB
label .nameC
label .nameD -text {Merge Result}
::ttk::scrollbar .sby -command {.txtA yview} -orient vertical
::ttk::scrollbar .sbxA -command {.txtA xview} -orient horizontal
::ttk::scrollbar .sbxB -command {.txtB xview} -orient horizontal
::ttk::scrollbar .sbxC -command {.txtC xview} -orient horizontal
::ttk::scrollbar .sbxD -command {.txtD xview} -orient horizontal
frame .spacer
update idletasks
proc searchOnOff {} {
if {[info exists ::search]} {
unset ::search
.txtA tag remove search 1.0 end
.txtB tag remove search 1.0 end
.txtC tag remove search 1.0 end
.txtD tag remove search 1.0 end
pack forget .bb.sframe
focus .
} else {
set ::search .txtA
if {![winfo exists .bb.sframe]} {
frame .bb.sframe
::ttk::entry .bb.sframe.e -width 10
pack .bb.sframe.e -side left -fill y -expand 1
bind .bb.sframe.e <Return> {searchNext; break}
::ttk::button .bb.sframe.nx -text \u2193 -width 1 -command searchNext
::ttk::button .bb.sframe.pv -text \u2191 -width 1 -command searchPrev
tk_optionMenu .bb.sframe.typ ::search_type \
Exact {No Case} {RegExp} {Whole Word}
.bb.sframe.typ config -width 10
set ::search_type Exact
pack .bb.sframe.nx .bb.sframe.pv .bb.sframe.typ -side left
}
pack .bb.sframe -side left
after idle {focus .bb.sframe.e}
}
}
proc searchNext {} {searchStep -forwards +1 1.0 end}
proc searchPrev {} {searchStep -backwards -1 end 1.0}
proc searchStep {direction incr start stop} {
set pattern [.bb.sframe.e get]
if {$pattern==""} return
set count 0
set w $::search
switch $w {
.txtA {set other .txtB}
.txtB {set other .txtC}
.txtC {set other .txtD}
default {set other .txtA}
}
if {[lsearch [$w mark names] search]<0} {
$w mark set search $start
}
switch $::search_type {
Exact {set st -exact}
{No Case} {set st -nocase}
{RegExp} {set st -regexp}
{Whole Word} {set st -regexp; set pattern \\y$pattern\\y}
}
set idx [$w search -count count $direction $st -- \
$pattern "search $incr chars" $stop]
if {"$idx"==""} {
set idx [$other search -count count $direction $st -- $pattern $start $stop]
if {"$idx"!=""} {
set this $w
set w $other
set other $this
} else {
set idx [$w search -count count $direction $st -- $pattern $start $stop]
}
}
$w tag remove search 1.0 end
$w mark unset search
$other tag remove search 1.0 end
$other mark unset search
if {"$idx"!=""} {
$w mark set search $idx
$w yview -pickplace $idx
$w tag add search search "$idx +$count chars"
$w tag config search -background {#fcc000}
}
set ::search $w
}
::ttk::button .bb.quit -text {Quit} -command exit
::ttk::button .bb.search -text {Search} -command searchOnOff
pack .bb.quit -side left
if {[winfo exists .bb.files]} {
pack .bb.files -side left
}
pack .bb.ctxtag .bb.ctx -side left
pack .bb.search -side left
grid rowconfigure . 1 -weight 1 -minsize [winfo reqheight .nameA]
grid rowconfigure . 2 -weight 100
readMerge
grid .bb -row 0 -columnspan 8
grid .nameA -row 1 -column 1 -sticky ew
grid .nameB -row 1 -column 3 -sticky ew
grid .nameC -row 1 -column 5 -sticky ew
grid .nameD -row 1 -column 7 -sticky ew
eval grid [cols] -row 2 -sticky nsew
grid .sby -row 2 -column 8 -sticky ns
grid .sbxA -row 3 -column 1 -sticky ew
grid .sbxB -row 3 -column 3 -sticky ew
grid .sbxC -row 3 -column 5 -sticky ew
grid .sbxD -row 3 -column 7 -sticky ew
grid columnconfigure . {0 2 4 6} \
-weight 1 -uniform a -minsize [winfo reqwidth .lnA]
grid columnconfigure . {1 3 5 7} -weight 100 -uniform b
.spacer config -height [winfo height .sbxA]
wm deiconify .
|
Changes to src/merge3.c.
| ︙ | ︙ | |||
73 74 75 76 77 78 79 80 | if( aC1[0]!=aC2[0] ) return 0; if( aC1[1]!=aC2[1] ) return 0; if( aC1[2]!=aC2[2] ) return 0; if( sameLines(pV1, pV2, aC1[2]) ) return 1; return 0; } /* | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < > | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
if( aC1[0]!=aC2[0] ) return 0;
if( aC1[1]!=aC2[1] ) return 0;
if( aC1[2]!=aC2[2] ) return 0;
if( sameLines(pV1, pV2, aC1[2]) ) return 1;
return 0;
}
/*
** Text of boundary markers for merge conflicts.
*/
static const char *const mergeMarker[] = {
/*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/
"<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<",
"####### SUGGESTED CONFLICT RESOLUTION follows ###################",
"||||||| COMMON ANCESTOR content follows |||||||||||||||||||||||||",
"======= MERGED IN content follows ===============================",
">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
};
/*
** Return true if the input blob contains any CR/LF pairs on the first
|
| ︙ | ︙ | |||
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
*/
void append_merge_mark(Blob *pOut, int iMark, int ln, int useCrLf){
ensure_line_end(pOut, useCrLf);
blob_append(pOut, mergeMarker[iMark], -1);
if( ln>0 ) blob_appendf(pOut, " (line %d)", ln);
ensure_line_end(pOut, useCrLf);
}
/*
** Do a three-way merge. Initialize pOut to contain the result.
**
** The merge is an edit against pV2. Both pV1 and pV2 have a
** common origin at pPivot. Apply the changes of pPivot ==> pV1
** to pV2.
**
** The return is 0 upon complete success. If any input file is binary,
** -1 is returned and pOut is unmodified. If there are merge
** conflicts, the merge proceeds as best as it can and the number
** of conflicts is returns
*/
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < < < < < < < < < < < < < < < < < < < < < > | | | | | > > | > > > > | < | < < < | | < | > | < < < < < | < < < | < < < | < < < | | < < < | > | < | < < | < < | < < | < | < < < < < | < | > > > > | | 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 |
*/
void append_merge_mark(Blob *pOut, int iMark, int ln, int useCrLf){
ensure_line_end(pOut, useCrLf);
blob_append(pOut, mergeMarker[iMark], -1);
if( ln>0 ) blob_appendf(pOut, " (line %d)", ln);
ensure_line_end(pOut, useCrLf);
}
#if INTERFACE
/*
** This is an abstract class for constructing a merge.
** Subclasses of this object format the merge output in different ways.
**
** To subclass, create an instance of the MergeBuilder object and fill
** in appropriate method implementations.
*/
struct MergeBuilder {
void (*xStart)(MergeBuilder*);
void (*xSame)(MergeBuilder*, unsigned int);
void (*xChngV1)(MergeBuilder*, unsigned int, unsigned int);
void (*xChngV2)(MergeBuilder*, unsigned int, unsigned int);
void (*xChngBoth)(MergeBuilder*, unsigned int, unsigned int);
void (*xConflict)(MergeBuilder*, unsigned int, unsigned int, unsigned int);
void (*xEnd)(MergeBuilder*);
void (*xDestroy)(MergeBuilder*);
const char *zPivot; /* Label or name for the pivot */
const char *zV1; /* Label or name for the V1 file */
const char *zV2; /* Label or name for the V2 file */
const char *zOut; /* Label or name for the output */
Blob *pPivot; /* The common ancestor */
Blob *pV1; /* First variant (local copy) */
Blob *pV2; /* Second variant (merged in) */
Blob *pOut; /* Write merge results here */
int useCrLf; /* Use CRLF line endings */
int nContext; /* Size of unchanged line boundaries */
unsigned int mxPivot; /* Number of lines in the pivot */
unsigned int mxV1; /* Number of lines in V1 */
unsigned int mxV2; /* Number of lines in V2 */
unsigned int lnPivot; /* Lines read from pivot */
unsigned int lnV1; /* Lines read from v1 */
unsigned int lnV2; /* Lines read from v2 */
unsigned int lnOut; /* Lines written to out */
unsigned int nConflict; /* Number of conflicts seen */
u64 diffFlags; /* Flags for difference engine */
};
#endif /* INTERFACE */
/************************* Generic MergeBuilder ******************************/
/* These are generic methods for MergeBuilder. They just output debugging
** information. But some of them are useful as base methods for other useful
** implementations of MergeBuilder.
*/
/* xStart() and xEnd() are called to generate header and fotter information
** in the output. This is a no-op in the generic implementation.
*/
static void dbgStartEnd(MergeBuilder *p){ (void)p; }
/* The next N lines of PIVOT are unchanged in both V1 and V2
*/
static void dbgSame(MergeBuilder *p, unsigned int N){
blob_appendf(p->pOut,
"COPY %u from BASELINE(%u..%u) or V1(%u..%u) or V2(%u..%u)\n",
N, p->lnPivot+1, p->lnPivot+N, p->lnV1+1, p->lnV1+N,
p->lnV2+1, p->lnV2+N);
p->lnPivot += N;
p->lnV1 += N;
p->lnV2 += N;
}
/* The next nPivot lines of the PIVOT are changed into nV1 lines by V1
*/
static void dbgChngV1(MergeBuilder *p, unsigned int nPivot, unsigned int nV1){
blob_appendf(p->pOut, "COPY %u from V1(%u..%u)\n",
nV1, p->lnV1+1, p->lnV1+nV1);
p->lnPivot += nPivot;
p->lnV2 += nPivot;
p->lnV1 += nV1;
}
/* The next nPivot lines of the PIVOT are changed into nV2 lines by V2
*/
static void dbgChngV2(MergeBuilder *p, unsigned int nPivot, unsigned int nV2){
blob_appendf(p->pOut, "COPY %u lines FROM V2(%u..%u)\n",
nV2, p->lnV2+1, p->lnV2+nV2);
p->lnPivot += nPivot;
p->lnV1 += nPivot;
p->lnV2 += nV2;
}
/* The next nPivot lines of the PIVOT are changed into nV lines from V1 and
** V2, which should be the same. In other words, the same change is found
** in both V1 and V2.
*/
static void dbgChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned int nV){
blob_appendf(p->pOut, "COPY %u lines from V1(%u..%u) or V2(%u..%u)\n",
nV, p->lnV1+1, p->lnV1+nV, p->lnV2+1, p->lnV2+nV);
p->lnPivot += nPivot;
p->lnV1 += nV;
p->lnV2 += nV;
}
/* V1 and V2 have different and overlapping changes. The next nPivot lines
** of the PIVOT are converted into nV1 lines of V1 and nV2 lines of V2.
*/
static void dbgConflict(
MergeBuilder *p,
unsigned int nPivot,
unsigned int nV1,
unsigned int nV2
){
blob_appendf(p->pOut,
"CONFLICT %u,%u,%u BASELINE(%u..%u) versus V1(%u..%u) versus V2(%u..%u)\n",
nPivot, nV1, nV2,
p->lnPivot+1, p->lnPivot+nPivot,
p->lnV1+1, p->lnV1+nV1,
p->lnV2+1, p->lnV2+nV2);
p->lnV1 += nV1;
p->lnPivot += nPivot;
p->lnV2 += nV2;
}
/* Generic destructor for the MergeBuilder object
*/
static void dbgDestroy(MergeBuilder *p){
memset(p, 0, sizeof(*p));
}
/* Generic initializer for a MergeBuilder object
*/
static void mergebuilder_init(MergeBuilder *p){
memset(p, 0, sizeof(*p));
p->xStart = dbgStartEnd;
p->xSame = dbgSame;
p->xChngV1 = dbgChngV1;
p->xChngV2 = dbgChngV2;
p->xChngBoth = dbgChngBoth;
p->xConflict = dbgConflict;
p->xEnd = dbgStartEnd;
p->xDestroy = dbgDestroy;
}
/************************* MergeBuilderToken ********************************/
/* This version of MergeBuilder actually performs a merge on file that
** are broken up into tokens instead of lines, and puts the result in pOut.
*/
static void tokenSame(MergeBuilder *p, unsigned int N){
blob_append(p->pOut, p->pPivot->aData+p->pPivot->iCursor, N);
p->pPivot->iCursor += N;
p->pV1->iCursor += N;
p->pV2->iCursor += N;
}
static void tokenChngV1(MergeBuilder *p, unsigned int nPivot, unsigned nV1){
blob_append(p->pOut, p->pV1->aData+p->pV1->iCursor, nV1);
p->pPivot->iCursor += nPivot;
p->pV1->iCursor += nV1;
p->pV2->iCursor += nPivot;
}
static void tokenChngV2(MergeBuilder *p, unsigned int nPivot, unsigned nV2){
blob_append(p->pOut, p->pV2->aData+p->pV2->iCursor, nV2);
p->pPivot->iCursor += nPivot;
p->pV1->iCursor += nPivot;
p->pV2->iCursor += nV2;
}
static void tokenChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned nV){
blob_append(p->pOut, p->pV2->aData+p->pV2->iCursor, nV);
p->pPivot->iCursor += nPivot;
p->pV1->iCursor += nV;
p->pV2->iCursor += nV;
}
static void tokenConflict(
MergeBuilder *p,
unsigned int nPivot,
unsigned int nV1,
unsigned int nV2
){
/* For a token-merge conflict, use the text from the merge-in */
blob_append(p->pOut, p->pV2->aData+p->pV2->iCursor, nV2);
p->pPivot->iCursor += nPivot;
p->pV1->iCursor += nV1;
p->pV2->iCursor += nV2;
}
static void mergebuilder_init_token(MergeBuilder *p){
mergebuilder_init(p);
p->xSame = tokenSame;
p->xChngV1 = tokenChngV1;
p->xChngV2 = tokenChngV2;
p->xChngBoth = tokenChngBoth;
p->xConflict = tokenConflict;
p->diffFlags = DIFF_BY_TOKEN;
}
/*
** Attempt to do a low-level merge on a conflict. The conflict is
** described by the first four parameters, which are the same as the
** arguments to the xConflict method of the MergeBuilder object.
** This routine attempts to resolve the conflict by looking at
** elements of the conflict region that are finer grain than complete
** lines of text.
**
** The result is written into Blob pOut. pOut is initialized by this
** routine.
*/
int merge_try_to_resolve_conflict(
MergeBuilder *pMB, /* MergeBuilder that encounter conflict */
unsigned int nPivot, /* Lines of conflict in the pivot */
unsigned int nV1, /* Lines of conflict in V1 */
unsigned int nV2, /* Lines of conflict in V2 */
Blob *pOut /* Write resolution text here */
){
int nConflict;
MergeBuilder mb;
Blob pv, v1, v2;
mergebuilder_init_token(&mb);
blob_extract_lines(pMB->pPivot, nPivot, &pv);
blob_extract_lines(pMB->pV1, nV1, &v1);
blob_extract_lines(pMB->pV2, nV2, &v2);
blob_zero(pOut);
blob_materialize(&pv);
blob_materialize(&v1);
blob_materialize(&v2);
mb.pPivot = &pv;
mb.pV1 = &v1;
mb.pV2 = &v2;
mb.pOut = pOut;
nConflict = merge_three_blobs(&mb);
blob_reset(&pv);
blob_reset(&v1);
blob_reset(&v2);
/* mb has not allocated any resources, so we do not need to invoke
** the xDestroy method. */
blob_add_final_newline(pOut);
return nConflict;
}
/************************* MergeBuilderText **********************************/
/* This version of MergeBuilder actually performs a merge on file and puts
** the result in pOut
*/
static void txtStart(MergeBuilder *p){
/* If both pV1 and pV2 start with a UTF-8 byte-order-mark (BOM),
** keep it in the output. This should be secure enough not to cause
** unintended changes to the merged file and consistent with what
** users are using in their source files.
*/
if( starts_with_utf8_bom(p->pV1, 0) && starts_with_utf8_bom(p->pV2, 0) ){
blob_append(p->pOut, (char*)get_utf8_bom(0), -1);
}
if( contains_crlf(p->pV1) && contains_crlf(p->pV2) ){
p->useCrLf = 1;
}
}
static void txtSame(MergeBuilder *p, unsigned int N){
blob_copy_lines(p->pOut, p->pPivot, N); p->lnPivot += N;
blob_copy_lines(0, p->pV1, N); p->lnV1 += N;
blob_copy_lines(0, p->pV2, N); p->lnV2 += N;
}
static void txtChngV1(MergeBuilder *p, unsigned int nPivot, unsigned int nV1){
blob_copy_lines(0, p->pPivot, nPivot); p->lnPivot += nPivot;
blob_copy_lines(0, p->pV2, nPivot); p->lnV2 += nPivot;
blob_copy_lines(p->pOut, p->pV1, nV1); p->lnV1 += nV1;
}
static void txtChngV2(MergeBuilder *p, unsigned int nPivot, unsigned int nV2){
blob_copy_lines(0, p->pPivot, nPivot); p->lnPivot += nPivot;
blob_copy_lines(0, p->pV1, nPivot); p->lnV1 += nPivot;
blob_copy_lines(p->pOut, p->pV2, nV2); p->lnV2 += nV2;
}
static void txtChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned int nV){
blob_copy_lines(0, p->pPivot, nPivot); p->lnPivot += nPivot;
blob_copy_lines(0, p->pV1, nV); p->lnV1 += nV;
blob_copy_lines(p->pOut, p->pV2, nV); p->lnV2 += nV;
}
static void txtConflict(
MergeBuilder *p,
unsigned int nPivot,
unsigned int nV1,
unsigned int nV2
){
int nRes; /* Lines in the computed conflict resolution */
Blob res; /* Text of the conflict resolution */
merge_try_to_resolve_conflict(p, nPivot, nV1, nV2, &res);
nRes = blob_linecount(&res);
append_merge_mark(p->pOut, 0, p->lnV1+1, p->useCrLf);
blob_copy_lines(p->pOut, p->pV1, nV1); p->lnV1 += nV1;
if( nRes>0 ){
append_merge_mark(p->pOut, 1, 0, p->useCrLf);
blob_copy_lines(p->pOut, &res, nRes);
}
blob_reset(&res);
append_merge_mark(p->pOut, 2, p->lnPivot+1, p->useCrLf);
blob_copy_lines(p->pOut, p->pPivot, nPivot); p->lnPivot += nPivot;
append_merge_mark(p->pOut, 3, p->lnV2+1, p->useCrLf);
blob_copy_lines(p->pOut, p->pV2, nV2); p->lnV2 += nV2;
append_merge_mark(p->pOut, 4, -1, p->useCrLf);
}
static void mergebuilder_init_text(MergeBuilder *p){
mergebuilder_init(p);
p->xStart = txtStart;
p->xSame = txtSame;
p->xChngV1 = txtChngV1;
p->xChngV2 = txtChngV2;
p->xChngBoth = txtChngBoth;
p->xConflict = txtConflict;
}
/************************* MergeBuilderTcl **********************************/
/* Generate merge output formatted for reading by a TCL script.
**
** The output consists of lines of text, each with 4 tokens. The tokens
** represent the content for one line from baseline, v1, v2, and output
** respectively. The first character of each token provides auxiliary
** information:
**
** . This line is omitted.
** N Name of the file.
** T Literal text follows that should have a \n terminator.
** R Literal text follows that needs a \r\n terminator.
** X Merge conflict.
** Z Literal text without a line terminator.
** S Skipped lines. Followed by number of lines to skip.
** 1 Text is a copy of token 1
** 2 Use data from data-token 2
** 3 Use data from data-token 3
*/
/* Write text that goes into the interior of a double-quoted string in TCL */
static void tclWriteQuotedText(Blob *pOut, const char *zIn, int nIn){
int j;
for(j=0; j<nIn; j++){
char c = zIn[j];
if( c=='\\' ){
blob_append(pOut, "\\\\", 2);
}else if( c=='"' ){
blob_append(pOut, "\\\"", 2);
}else if( c<' ' || c>0x7e ){
char z[5];
z[0] = '\\';
z[1] = "01234567"[(c>>6)&0x3];
z[2] = "01234567"[(c>>3)&0x7];
z[3] = "01234567"[c&0x7];
z[4] = 0;
blob_append(pOut, z, 4);
}else{
blob_append_char(pOut, c);
}
}
}
/* Copy one line of text from pIn and append to pOut, encoded as TCL */
static void tclLineOfText(Blob *pOut, Blob *pIn, char cType){
int i, k;
for(i=pIn->iCursor; i<pIn->nUsed && pIn->aData[i]!='\n'; i++){}
if( i==pIn->nUsed ){
k = i;
}else if( i>pIn->iCursor && pIn->aData[i-1]=='\r' ){
k = i-1;
i++;
}else{
k = i;
i++;
}
blob_append_char(pOut, '"');
blob_append_char(pOut, cType);
tclWriteQuotedText(pOut, pIn->aData+pIn->iCursor, k-pIn->iCursor);
pIn->iCursor = i;
blob_append_char(pOut, '"');
}
static void tclStart(MergeBuilder *p){
Blob *pOut = p->pOut;
blob_append(pOut, "\"N", 2);
tclWriteQuotedText(pOut, p->zPivot, (int)strlen(p->zPivot));
blob_append(pOut, "\" \"N", 4);
tclWriteQuotedText(pOut, p->zV1, (int)strlen(p->zV1));
blob_append(pOut, "\" \"N", 4);
tclWriteQuotedText(pOut, p->zV2, (int)strlen(p->zV2));
blob_append(pOut, "\" \"N", 4);
if( p->zOut ){
tclWriteQuotedText(pOut, p->zOut, (int)strlen(p->zOut));
}else{
blob_append(pOut, "(Merge Result)", -1);
}
blob_append(pOut, "\"\n", 2);
}
static void tclSame(MergeBuilder *p, unsigned int N){
int i = 0;
int nSkip;
if( p->lnPivot>=2 || p->lnV1>2 || p->lnV2>2 ){
while( i<N && i<p->nContext ){
tclLineOfText(p->pOut, p->pPivot, 'T');
blob_append(p->pOut, " 1 1 1\n", 7);
i++;
}
nSkip = N - p->nContext*2;
}else{
nSkip = N - p->nContext;
}
if( nSkip>0 ){
blob_appendf(p->pOut, "\"S%d %d %d %d\" . . .\n",
nSkip, nSkip, nSkip, nSkip);
blob_copy_lines(0, p->pPivot, nSkip);
i += nSkip;
}
p->lnPivot += N;
p->lnV1 += N;
p->lnV2 += N;
if( p->lnPivot<p->mxPivot || p->lnV1<p->mxV1 || p->lnV2<p->mxV2 ){
while( i<N ){
tclLineOfText(p->pOut, p->pPivot, 'T');
blob_append(p->pOut, " 1 1 1\n", 7);
i++;
}
}
blob_copy_lines(0, p->pV1, N);
blob_copy_lines(0, p->pV2, N);
}
static void tclChngV1(MergeBuilder *p, unsigned int nPivot, unsigned int nV1){
int i;
for(i=0; i<nPivot && i<nV1; i++){
tclLineOfText(p->pOut, p->pPivot, 'T');
blob_append_char(p->pOut, ' ');
tclLineOfText(p->pOut, p->pV1, 'T');
blob_append(p->pOut, " 1 2\n", 5);
}
while( i<nPivot ){
tclLineOfText(p->pOut, p->pPivot, 'T');
blob_append(p->pOut, " . 1 .\n", 7);
i++;
}
while( i<nV1 ){
blob_append(p->pOut, ". ", 2);
tclLineOfText(p->pOut, p->pV1, 'T');
blob_append(p->pOut, " . 2\n", 5);
i++;
}
p->lnPivot += nPivot;
p->lnV1 += nV1;
p->lnV2 += nPivot;
blob_copy_lines(0, p->pV2, nPivot);
}
static void tclChngV2(MergeBuilder *p, unsigned int nPivot, unsigned int nV2){
int i;
for(i=0; i<nPivot && i<nV2; i++){
tclLineOfText(p->pOut, p->pPivot, 'T');
blob_append(p->pOut, " 1 ", 3);
tclLineOfText(p->pOut, p->pV2, 'T');
blob_append(p->pOut, " 3\n", 3);
}
while( i<nPivot ){
tclLineOfText(p->pOut, p->pPivot, 'T');
blob_append(p->pOut, " 1 . .\n", 7);
i++;
}
while( i<nV2 ){
blob_append(p->pOut, ". . ", 4);
tclLineOfText(p->pOut, p->pV2, 'T');
blob_append(p->pOut, " 3\n", 3);
i++;
}
p->lnPivot += nPivot;
p->lnV1 += nPivot;
p->lnV2 += nV2;
blob_copy_lines(0, p->pV1, nPivot);
}
static void tclChngBoth(MergeBuilder *p, unsigned int nPivot, unsigned int nV){
int i;
for(i=0; i<nPivot && i<nV; i++){
tclLineOfText(p->pOut, p->pPivot, 'T');
blob_append_char(p->pOut, ' ');
tclLineOfText(p->pOut, p->pV1, 'T');
blob_append(p->pOut, " 2 2\n", 5);
}
while( i<nPivot ){
tclLineOfText(p->pOut, p->pPivot, 'T');
blob_append(p->pOut, " . . .\n", 7);
i++;
}
while( i<nV ){
blob_append(p->pOut, ". ", 2);
tclLineOfText(p->pOut, p->pV1, 'T');
blob_append(p->pOut, " 2 2\n", 5);
i++;
}
p->lnPivot += nPivot;
p->lnV1 += nV;
p->lnV2 += nV;
blob_copy_lines(0, p->pV2, nV);
}
static void tclConflict(
MergeBuilder *p,
unsigned int nPivot,
unsigned int nV1,
unsigned int nV2
){
int mx = nPivot;
int i;
int nRes;
Blob res;
merge_try_to_resolve_conflict(p, nPivot, nV1, nV2, &res);
nRes = blob_linecount(&res);
if( nV1>mx ) mx = nV1;
if( nV2>mx ) mx = nV2;
if( nRes>mx ) mx = nRes;
if( nRes>0 ){
blob_appendf(p->pOut, "\"S0 0 0 %d\" . . .\n", nV2+2);
}
for(i=0; i<mx; i++){
if( i<nPivot ){
tclLineOfText(p->pOut, p->pPivot, 'X');
}else{
blob_append_char(p->pOut, '.');
}
blob_append_char(p->pOut, ' ');
if( i<nV1 ){
tclLineOfText(p->pOut, p->pV1, 'X');
}else{
blob_append_char(p->pOut, '.');
}
blob_append_char(p->pOut, ' ');
if( i<nV2 ){
tclLineOfText(p->pOut, p->pV2, 'X');
}else{
blob_append_char(p->pOut, '.');
}
if( i<nRes ){
blob_append_char(p->pOut, ' ');
tclLineOfText(p->pOut, &res, 'X');
blob_append_char(p->pOut, '\n');
}else{
blob_append(p->pOut, " .\n", 3);
}
if( i==mx-1 ){
blob_appendf(p->pOut, "\"S0 0 0 %d\" . . .\n", nPivot+nV1+3);
}
}
blob_reset(&res);
p->lnPivot += nPivot;
p->lnV1 += nV1;
p->lnV2 += nV2;
}
void mergebuilder_init_tcl(MergeBuilder *p){
mergebuilder_init(p);
p->xStart = tclStart;
p->xSame = tclSame;
p->xChngV1 = tclChngV1;
p->xChngV2 = tclChngV2;
p->xChngBoth = tclChngBoth;
p->xConflict = tclConflict;
}
/*****************************************************************************/
/*
** The aC[] array contains triples of integers. Within each triple, the
** elements are:
**
** (0) The number of lines to copy
** (1) The number of lines to delete
** (2) The number of liens to insert
**
** Suppose we want to advance over sz lines of the original file. This routine
** returns true if that advance would land us on a copy operation. It
** returns false if the advance would end on a delete.
*/
static int ends_with_copy(int *aC, int sz){
while( sz>0 && (aC[0]>0 || aC[1]>0 || aC[2]>0) ){
if( aC[0]>=sz ) return 1;
sz -= aC[0];
if( aC[1]>sz ) return 0;
sz -= aC[1];
aC += 3;
}
return 1;
}
/*
** aC[] is an "edit triple" for changes from A to B. Advance through
** this triple to determine the number of lines to bypass on B in order
** to match an advance of sz lines on A.
*/
static int skip_conflict(
int *aC, /* Array of integer triples describing the edit */
int i, /* Index in aC[] of current location */
int sz, /* Lines of A that have been skipped */
unsigned int *pLn /* OUT: Lines of B to skip to keep aligment with A */
){
*pLn = 0;
while( sz>0 ){
if( aC[i]==0 && aC[i+1]==0 && aC[i+2]==0 ) break;
if( aC[i]>=sz ){
aC[i] -= sz;
*pLn += sz;
break;
}
*pLn += aC[i];
*pLn += aC[i+2];
sz -= aC[i] + aC[i+1];
i += 3;
}
return i;
}
/*
** Do a three-way merge. Initialize pOut to contain the result.
**
** The merge is an edit against pV2. Both pV1 and pV2 have a
** common origin at pPivot. Apply the changes of pPivot ==> pV1
** to pV2.
**
** The return is 0 upon complete success. If any input file is binary,
** -1 is returned and pOut is unmodified. If there are merge
** conflicts, the merge proceeds as best as it can and the number
** of conflicts is returns
*/
int merge_three_blobs(MergeBuilder *p){
int *aC1; /* Changes from pPivot to pV1 */
int *aC2; /* Changes from pPivot to pV2 */
int i1, i2; /* Index into aC1[] and aC2[] */
int nCpy, nDel, nIns; /* Number of lines to copy, delete, or insert */
int limit1, limit2; /* Sizes of aC1[] and aC2[] */
int nConflict = 0; /* Number of merge conflicts seen so far */
DiffConfig DCfg;
/* Compute the edits that occur from pPivot => pV1 (into aC1)
** and pPivot => pV2 (into aC2). Each of the aC1 and aC2 arrays is
** an array of integer triples. Within each triple, the first integer
** is the number of lines of text to copy directly from the pivot,
** the second integer is the number of lines of text to omit from the
** pivot, and the third integer is the number of lines of text that are
** inserted. The edit array ends with a triple of 0,0,0.
*/
diff_config_init(&DCfg, 0);
DCfg.diffFlags = p->diffFlags;
aC1 = text_diff(p->pPivot, p->pV1, 0, &DCfg);
aC2 = text_diff(p->pPivot, p->pV2, 0, &DCfg);
if( aC1==0 || aC2==0 ){
free(aC1);
free(aC2);
return -1;
}
blob_rewind(p->pV1); /* Rewind inputs: Needed to reconstruct output */
blob_rewind(p->pV2);
blob_rewind(p->pPivot);
/* Determine the length of the aC1[] and aC2[] change vectors */
p->mxPivot = 0;
p->mxV1 = 0;
for(i1=0; aC1[i1] || aC1[i1+1] || aC1[i1+2]; i1+=3){
p->mxPivot += aC1[i1] + aC1[i1+1];
p->mxV1 += aC1[i1] + aC1[i1+2];
}
limit1 = i1;
p->mxV2 = 0;
for(i2=0; aC2[i2] || aC2[i2+1] || aC2[i2+2]; i2+=3){
p->mxV2 += aC2[i2] + aC2[i2+2];
}
limit2 = i2;
/* Output header text and do any other required initialization */
p->xStart(p);
/* Loop over the two edit vectors and use them to compute merged text
** which is written into pOut. i1 and i2 are multiples of 3 which are
** indices into aC1[] and aC2[] to the edit triple currently being
** processed
*/
i1 = i2 = 0;
while( i1<limit1 && i2<limit2 ){
if( aC1[i1]>0 && aC2[i2]>0 ){
/* Output text that is unchanged in both V1 and V2 */
nCpy = min(aC1[i1], aC2[i2]);
p->xSame(p, nCpy);
aC1[i1] -= nCpy;
aC2[i2] -= nCpy;
}else
if( aC1[i1] >= aC2[i2+1] && aC1[i1]>0 && aC2[i2+1]+aC2[i2+2]>0 ){
/* Output edits to V2 that occurs within unchanged regions of V1 */
nDel = aC2[i2+1];
nIns = aC2[i2+2];
p->xChngV2(p, nDel, nIns);
aC1[i1] -= nDel;
i2 += 3;
}else
if( aC2[i2] >= aC1[i1+1] && aC2[i2]>0 && aC1[i1+1]+aC1[i1+2]>0 ){
/* Output edits to V1 that occur within unchanged regions of V2 */
nDel = aC1[i1+1];
nIns = aC1[i1+2];
p->xChngV1(p, nDel, nIns);
aC2[i2] -= nDel;
i1 += 3;
}else
if( sameEdit(&aC1[i1], &aC2[i2], p->pV1, p->pV2) ){
/* Output edits that are identical in both V1 and V2. */
assert( aC1[i1]==0 );
nDel = aC1[i1+1];
nIns = aC1[i1+2];
p->xChngBoth(p, nDel, nIns);
i1 += 3;
i2 += 3;
}else
{
/* We have found a region where different edits to V1 and V2 overlap.
** This is a merge conflict. Find the size of the conflict, then
** output both possible edits separated by distinctive marks.
*/
unsigned int sz = 1; /* Size of the conflict in the pivot, in lines */
unsigned int nV1, nV2; /* Size of conflict in V1 and V2, in lines */
nConflict++;
while( !ends_with_copy(&aC1[i1], sz) || !ends_with_copy(&aC2[i2], sz) ){
sz++;
}
i1 = skip_conflict(aC1, i1, sz, &nV1);
i2 = skip_conflict(aC2, i2, sz, &nV2);
p->xConflict(p, sz, nV1, nV2);
}
/* If we are finished with an edit triple, advance to the next
** triple.
*/
if( i1<limit1 && aC1[i1]==0 && aC1[i1+1]==0 && aC1[i1+2]==0 ) i1+=3;
if( i2<limit2 && aC2[i2]==0 && aC2[i2+1]==0 && aC2[i2+2]==0 ) i2+=3;
}
/* When one of the two edit vectors reaches its end, there might still
** be an insert in the other edit vector. Output this remaining
** insert.
*/
if( i1<limit1 && aC1[i1+2]>0 ){
p->xChngV1(p, 0, aC1[i1+2]);
}else if( i2<limit2 && aC2[i2+2]>0 ){
p->xChngV2(p, 0, aC2[i2+2]);
}
/* Output footer text */
p->xEnd(p);
free(aC1);
free(aC2);
return nConflict;
}
/*
** Return true if the input string contains a merge marker on a line by
** itself.
*/
int contains_merge_marker(Blob *p){
int i, j;
int len = (int)strlen(mergeMarker[0]);
const char *z = blob_buffer(p);
int n = blob_size(p) - len + 1;
assert( len==(int)strlen(mergeMarker[1]) );
assert( len==(int)strlen(mergeMarker[2]) );
assert( len==(int)strlen(mergeMarker[3]) );
assert( len==(int)strlen(mergeMarker[4]) );
assert( count(mergeMarker)==5 );
for(i=0; i<n; ){
for(j=0; j<4; j++){
if( (memcmp(&z[i], mergeMarker[j], len)==0) ){
return 1;
}
}
while( i<n && z[i]!='\n' ){ i++; }
|
| ︙ | ︙ | |||
406 407 408 409 410 411 412 413 414 415 416 | Blob file; int rc; blob_read_from_file(&file, zFullpath, ExtFILE); rc = contains_merge_marker(&file); blob_reset(&file); return rc; } /* ** COMMAND: 3-way-merge* ** | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > | > > | | > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > | | | | > > | > > | > | > | > | 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 |
Blob file;
int rc;
blob_read_from_file(&file, zFullpath, ExtFILE);
rc = contains_merge_marker(&file);
blob_reset(&file);
return rc;
}
/*
** Show merge output in a Tcl/Tk window, in response to the --tk option
** to the "merge" or "3-way-merge" command.
**
** If fossil has direct access to a Tcl interpreter (either loaded
** dynamically through stubs or linked in statically), we can use it
** directly. Otherwise:
** (1) Write the Tcl/Tk script used for rendering into a temp file.
** (2) Invoke "tclsh" on the temp file using fossil_system().
** (3) Delete the temp file.
*/
void merge_tk(const char *zSubCmd, int firstArg){
int i;
Blob script;
const char *zTempFile = 0;
char *zCmd;
const char *zTclsh;
const char *zCnt;
int bDarkMode = find_option("dark",0,0)!=0;
int nContext;
zCnt = find_option("context", "c", 1);
if( zCnt==0 ){
nContext = 6;
}else{
nContext = atoi(zCnt);
if( nContext<0 ) nContext = 0xfffffff;
}
blob_zero(&script);
blob_appendf(&script, "set ncontext %d\n", nContext);
blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl",
g.nameOfExe, zSubCmd);
find_option("tcl",0,0);
find_option("debug",0,0);
zTclsh = find_option("tclsh",0,1);
if( zTclsh==0 ){
zTclsh = db_get("tclsh",0);
}
/* The undocumented --script FILENAME option causes the Tk script to
** be written into the FILENAME instead of being run. This is used
** for testing and debugging. */
zTempFile = find_option("script",0,1);
verify_all_options();
if( (g.argc - firstArg)!=3 ){
fossil_fatal("Requires 3 filename arguments");
}
for(i=firstArg; i<g.argc; i++){
const char *z = g.argv[i];
if( sqlite3_strglob("*}*",z) ){
blob_appendf(&script, " {%/}", z);
}else{
int j;
blob_append(&script, " ", 1);
for(j=0; z[j]; j++) blob_appendf(&script, "\\%03o", (unsigned char)z[j]);
}
}
blob_appendf(&script, "}\nset darkmode %d\n", bDarkMode);
blob_appendf(&script, "%s", builtin_file("merge.tcl", 0));
if( zTempFile ){
blob_write_to_file(&script, zTempFile);
fossil_print("To see the merge, run: %s \"%s\"\n", zTclsh, zTempFile);
}else{
#if defined(FOSSIL_ENABLE_TCL)
Th_FossilInit(TH_INIT_DEFAULT);
if( evaluateTclWithEvents(g.interp, &g.tcl, blob_str(&script),
blob_size(&script), 1, 1, 0)==TCL_OK ){
blob_reset(&script);
return;
}
/*
* If evaluation of the Tcl script fails, the reason may be that Tk
* could not be found by the loaded Tcl, or that Tcl cannot be loaded
* dynamically (e.g. x64 Tcl with x86 Fossil). Therefore, fallback
* to using the external "tclsh", if available.
*/
#endif
zTempFile = write_blob_to_temp_file(&script);
zCmd = mprintf("%$ %$", zTclsh, zTempFile);
fossil_system(zCmd);
file_delete(zTempFile);
fossil_free(zCmd);
}
blob_reset(&script);
}
/*
** COMMAND: 3-way-merge*
**
** Usage: %fossil 3-way-merge BASELINE V1 V2 [MERGED]
**
** Inputs are files BASELINE, V1, and V2. The file MERGED is generated
** as output. If no MERGED file is specified, output is sent to
** stdout.
**
** BASELINE is a common ancestor of two files V1 and V2 that have diverging
** edits. The generated output file MERGED is the combination of all
** changes in both V1 and V2.
**
** This command has no effect on the Fossil repository. It is a utility
** command made available for the convenience of users. This command can
** be used, for example, to help import changes from an upstream project.
**
** Suppose an upstream project has a file named "Xup.c" which is imported
** with modifications to the local project as "Xlocal.c". Suppose further
** that the "Xbase.c" is an exact copy of the last imported "Xup.c".
** Then to import the latest "Xup.c" while preserving all the local changes:
**
** fossil 3-way-merge Xbase.c Xlocal.c Xup.c Xlocal.c
** cp Xup.c Xbase.c
** # Verify that everything still works
** fossil commit
**
*/
void merge_3way_cmd(void){
MergeBuilder s;
int nConflict;
Blob pivot, v1, v2, out;
int noWarn = 0;
const char *zCnt;
if( find_option("tk", 0, 0)!=0 ){
merge_tk("3-way-merge", 2);
return;
}
mergebuilder_init_text(&s);
if( find_option("debug", 0, 0) ){
mergebuilder_init(&s);
}
if( find_option("tcl", 0, 0) ){
mergebuilder_init_tcl(&s);
noWarn = 1;
}
zCnt = find_option("context", "c", 1);
if( zCnt ){
s.nContext = atoi(zCnt);
if( s.nContext<0 ) s.nContext = 0xfffffff;
}else{
s.nContext = 6;
}
blob_zero(&pivot); s.pPivot = &pivot;
blob_zero(&v1); s.pV1 = &v1;
blob_zero(&v2); s.pV2 = &v2;
blob_zero(&out); s.pOut = &out;
/* We should be done with options.. */
verify_all_options();
if( g.argc!=6 && g.argc!=5 ){
usage("[OPTIONS] PIVOT V1 V2 [MERGED]");
}
s.zPivot = file_tail(g.argv[2]);
s.zV1 = file_tail(g.argv[3]);
s.zV2 = file_tail(g.argv[4]);
if( blob_read_from_file(s.pPivot, g.argv[2], ExtFILE)<0 ){
fossil_fatal("cannot read %s", g.argv[2]);
}
if( blob_read_from_file(s.pV1, g.argv[3], ExtFILE)<0 ){
fossil_fatal("cannot read %s", g.argv[3]);
}
if( blob_read_from_file(s.pV2, g.argv[4], ExtFILE)<0 ){
fossil_fatal("cannot read %s", g.argv[4]);
}
nConflict = merge_three_blobs(&s);
if( g.argc==6 ){
s.zOut = file_tail(g.argv[5]);
blob_write_to_file(s.pOut, g.argv[5]);
}else{
s.zOut = "(Merge Result)";
blob_write_to_file(s.pOut, "-");
}
s.xDestroy(&s);
blob_reset(&pivot);
blob_reset(&v1);
blob_reset(&v2);
blob_reset(&out);
if( nConflict>0 && !noWarn ){
fossil_warning("WARNING: %d merge conflicts", nConflict);
}
}
/*
** aSubst is an array of string pairs. The first element of each pair is
** a string that begins with %. The second element is a replacement for that
** string.
**
|
| ︙ | ︙ | |||
503 504 505 506 507 508 509 | return blob_str(&x); } #if INTERFACE /* ** Flags to the 3-way merger */ | | | | | | | | > > > > > > > | | | | | | 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 |
return blob_str(&x);
}
#if INTERFACE
/*
** Flags to the 3-way merger
*/
#define MERGE_DRYRUN 0x0001
/*
** The MERGE_KEEP_FILES flag specifies that merge_3way() should retain
** its temporary files on error. By default they are removed after the
** merge, regardless of success or failure.
*/
#define MERGE_KEEP_FILES 0x0002
#endif
/*
** This routine is a wrapper around merge_three_blobs() with the following
** enhancements:
**
** (1) If the merge-command is defined, then use the external merging
** program specified instead of the built-in blob-merge to do the
** merging. Panic if the external merger fails.
** ** Not currently implemented **
**
** (2) If gmerge-command is defined and there are merge conflicts in
** merge_three_blobs() then invoke the external graphical merger
** to resolve the conflicts.
**
** (3) If a merge conflict occurs and gmerge-command is not defined,
** then write the pivot, original, and merge-in files to the
** filesystem.
*/
int merge_3way(
Blob *pPivot, /* Common ancestor (older) */
const char *zV1, /* Name of file for version merging into (mine) */
Blob *pV2, /* Version merging from (yours) */
Blob *pOut, /* Output written here */
unsigned mergeFlags /* Flags that control operation */
){
Blob v1; /* Content of zV1 */
int rc; /* Return code of subroutines and this routine */
const char *zGMerge; /* Name of the gmerge command */
MergeBuilder s; /* The merge state */
mergebuilder_init_text(&s);
s.pPivot = pPivot;
s.pV1 = &v1;
s.pV2 = pV2;
blob_zero(pOut);
s.pOut = pOut;
blob_read_from_file(s.pV1, zV1, ExtFILE);
rc = merge_three_blobs(&s);
zGMerge = rc<=0 ? 0 : db_get("gmerge-command", 0);
if( (mergeFlags & MERGE_DRYRUN)==0
&& ((zGMerge!=0 && zGMerge[0]!=0)
|| (rc!=0 && (mergeFlags & MERGE_KEEP_FILES)!=0)) ){
char *zPivot; /* Name of the pivot file */
char *zOrig; /* Name of the original content file */
char *zOther; /* Name of the merge file */
zPivot = file_newname(zV1, "baseline", 1);
blob_write_to_file(s.pPivot, zPivot);
zOrig = file_newname(zV1, "original", 1);
blob_write_to_file(s.pV1, zOrig);
zOther = file_newname(zV1, "merge", 1);
blob_write_to_file(s.pV2, zOther);
if( rc>0 ){
if( zGMerge && zGMerge[0] ){
char *zOut; /* Temporary output file */
char *zCmd; /* Command to invoke */
const char *azSubst[8]; /* Strings to be substituted */
zOut = file_newname(zV1, "output", 1);
azSubst[0] = "%baseline"; azSubst[1] = zPivot;
|
| ︙ | ︙ | |||
588 589 590 591 592 593 594 595 596 |
file_delete(zOther);
}
fossil_free(zPivot);
fossil_free(zOrig);
fossil_free(zOther);
}
blob_reset(&v1);
return rc;
}
| > | 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 |
file_delete(zOther);
}
fossil_free(zPivot);
fossil_free(zOrig);
fossil_free(zOther);
}
blob_reset(&v1);
s.xDestroy(&s);
return rc;
}
|
Changes to src/name.c.
| ︙ | ︙ | |||
63 64 65 66 67 68 69 |
**
** If the bVerifyNotAHash flag is true, then a check is made to see if
** the string is a hash prefix and NULL is returned if it is. If the
** bVerifyNotAHash flag is false, then the result is determined by syntax
** of the input string only, without reference to the artifact table.
*/
char *fossil_expand_datetime(const char *zIn, int bVerifyNotAHash){
| | > | > > | | | > > > > > > | > > | | > > > > > > > | 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
**
** If the bVerifyNotAHash flag is true, then a check is made to see if
** the string is a hash prefix and NULL is returned if it is. If the
** bVerifyNotAHash flag is false, then the result is determined by syntax
** of the input string only, without reference to the artifact table.
*/
char *fossil_expand_datetime(const char *zIn, int bVerifyNotAHash){
static char zEDate[24];
static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' };
int n = (int)strlen(zIn);
int i, j;
int addZulu = 0;
/* These forms are allowed:
**
** 123456789 1234 123456789 123456789
** (1) YYYYMMDD => YYYY-MM-DD
** (2) YYYYMMDDHHMM => YYYY-MM-DD HH:MM
** (3) YYYYMMDDHHMMSS => YYYY-MM-DD HH:MM:SS
**
** An optional "Z" zulu timezone designator is allowed at the end.
*/
if( n>0 && (zIn[n-1]=='Z' || zIn[n-1]=='z') ){
n--;
addZulu = 1;
}
if( n!=8 && n!=12 && n!=14 ){
return 0;
}
/* Every character must be a digit */
for(i=0; fossil_isdigit(zIn[i]); i++){}
if( i!=n && (!addZulu || i!=n+1) ) return 0;
/* Expand the date */
for(i=j=0; i<n; i++){
if( i>=4 && (i%2)==0 ){
zEDate[j++] = aPunct[i/2];
}
zEDate[j++] = zIn[i];
}
if( addZulu ){
if( j==10 ){
memcpy(&zEDate[10]," 00:00", 6);
j += 6;
}
zEDate[j++] = 'Z';
}
zEDate[j] = 0;
/* Check for reasonable date values.
** Offset references:
** YYYY-MM-DD HH:MM:SS
** 0123456789 12345678
*/
|
| ︙ | ︙ | |||
109 110 111 112 113 114 115 |
if( i>24 ) return 0;
i = atoi(zEDate+14);
if( i>60 ) return 0;
if( n==14 && atoi(zEDate+17)>60 ) return 0;
}
/* The string is not also a hash prefix */
| | | 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
if( i>24 ) return 0;
i = atoi(zEDate+14);
if( i>60 ) return 0;
if( n==14 && atoi(zEDate+17)>60 ) return 0;
}
/* The string is not also a hash prefix */
if( bVerifyNotAHash && !addZulu ){
if( db_exists("SELECT 1 FROM blob WHERE uuid GLOB '%q*'",zIn) ) return 0;
}
/* It looks like this may be a date. Return it with punctuation added. */
return zEDate;
}
|
| ︙ | ︙ | |||
451 452 453 454 455 456 457 458 459 460 461 462 463 464 |
rid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim",
ridCkout);
}else if( fossil_strcmp(zTag, "next")==0 ){
rid = db_int(0, "SELECT cid FROM plink WHERE pid=%d"
" ORDER BY isprim DESC, mtime DESC", ridCkout);
}else if( isCheckin>1 && fossil_strcmp(zTag, "ckout")==0 ){
rid = RID_CKOUT;
}
if( rid ) return rid;
}
/* Date and times */
if( memcmp(zTag, "date:", 5)==0 ){
zDate = fossil_expand_datetime(&zTag[5],0);
| > > | 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 |
rid = db_int(0, "SELECT pid FROM plink WHERE cid=%d AND isprim",
ridCkout);
}else if( fossil_strcmp(zTag, "next")==0 ){
rid = db_int(0, "SELECT cid FROM plink WHERE pid=%d"
" ORDER BY isprim DESC, mtime DESC", ridCkout);
}else if( isCheckin>1 && fossil_strcmp(zTag, "ckout")==0 ){
rid = RID_CKOUT;
assert(ridCkout>0);
g.localOpen = ridCkout;
}
if( rid ) return rid;
}
/* Date and times */
if( memcmp(zTag, "date:", 5)==0 ){
zDate = fossil_expand_datetime(&zTag[5],0);
|
| ︙ | ︙ | |||
697 698 699 700 701 702 703 704 705 706 707 708 709 710 |
fossil_error(iErrPriority, "ambiguous name: %s", zName);
return 2;
}else if( rid==0 ){
fossil_error(iErrPriority, "cannot resolve name: %s", zName);
return 1;
}else{
blob_reset(pName);
db_blob(pName, "SELECT uuid FROM blob WHERE rid=%d", rid);
return 0;
}
}
/*
** This routine is similar to name_to_uuid() except in the form it
| > > > | 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 |
fossil_error(iErrPriority, "ambiguous name: %s", zName);
return 2;
}else if( rid==0 ){
fossil_error(iErrPriority, "cannot resolve name: %s", zName);
return 1;
}else{
blob_reset(pName);
if( RID_CKOUT==rid ) {
rid = g.localOpen;
}
db_blob(pName, "SELECT uuid FROM blob WHERE rid=%d", rid);
return 0;
}
}
/*
** This routine is similar to name_to_uuid() except in the form it
|
| ︙ | ︙ | |||
1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 |
** Return a page showing all artifacts in the repository. Query parameters:
**
** n=N Show N artifacts
** s=S Start with artifact number S
** priv Show only unpublished or private artifacts
** phan Show only phantom artifacts
** hclr Color code hash types (SHA1 vs SHA3)
*/
void bloblist_page(void){
Stmt q;
int s = atoi(PD("s","0"));
int n = atoi(PD("n","5000"));
int mx = db_int(0, "SELECT max(rid) FROM blob");
int privOnly = PB("priv");
int phantomOnly = PB("phan");
int hashClr = PB("hclr");
char *zRange;
char *zSha1Bg;
char *zSha3Bg;
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
cgi_check_for_malice();
style_header("List Of Artifacts");
style_submenu_element("250 Largest", "bigbloblist");
if( g.perm.Admin ){
style_submenu_element("Artifact Log", "rcvfromlist");
}
if( !phantomOnly ){
style_submenu_element("Phantoms", "bloblist?phan");
}
if( g.perm.Private || g.perm.Admin ){
if( !privOnly ){
style_submenu_element("Private", "bloblist?priv");
}
}else{
privOnly = 0;
}
if( g.perm.Write ){
style_submenu_element("Artifact Stats", "artifact_stats");
}
| > > > > > > > > > > | > > > > > > > > > > > > | > | > > < | < < < | > | < | | | < | 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 |
** Return a page showing all artifacts in the repository. Query parameters:
**
** n=N Show N artifacts
** s=S Start with artifact number S
** priv Show only unpublished or private artifacts
** phan Show only phantom artifacts
** hclr Color code hash types (SHA1 vs SHA3)
** recent Show the most recent N artifacts
*/
void bloblist_page(void){
Stmt q;
int s = atoi(PD("s","0"));
int n = atoi(PD("n","5000"));
int mx = db_int(0, "SELECT max(rid) FROM blob");
int privOnly = PB("priv");
int phantomOnly = PB("phan");
int hashClr = PB("hclr");
int bRecent = PB("recent");
int bUnclst = PB("unclustered");
char *zRange;
char *zSha1Bg;
char *zSha3Bg;
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
cgi_check_for_malice();
style_header("List Of Artifacts");
style_submenu_element("250 Largest", "bigbloblist");
if( bRecent==0 || n!=250 ){
style_submenu_element("Recent","bloblist?n=250&recent");
}
if( bUnclst==0 ){
style_submenu_element("Unclustered","bloblist?unclustered");
}
if( g.perm.Admin ){
style_submenu_element("Artifact Log", "rcvfromlist");
}
if( !phantomOnly ){
style_submenu_element("Phantoms", "bloblist?phan");
}
style_submenu_element("Clusters","clusterlist");
if( g.perm.Private || g.perm.Admin ){
if( !privOnly ){
style_submenu_element("Private", "bloblist?priv");
}
}else{
privOnly = 0;
}
if( g.perm.Write ){
style_submenu_element("Artifact Stats", "artifact_stats");
}
if( !privOnly && !phantomOnly && mx>n && P("s")==0 && !bRecent && !bUnclst ){
int i;
@ <p>Select a range of artifacts to view:</p>
@ <ul>
for(i=1; i<=mx; i+=n){
@ <li> %z(href("%R/bloblist?s=%d&n=%d",i,n))
@ %d(i)..%d(i+n-1<mx?i+n-1:mx)</a>
}
@ <li> %z(href("%R/bloblist?n=250&recent"))250 most recent</a>
@ <li> %z(href("%R/bloblist?unclustered"))All unclustered</a>
@ </ul>
style_finish_page();
return;
}
if( phantomOnly || privOnly || mx>n ){
style_submenu_element("Index", "bloblist");
}
if( privOnly ){
@ <h2>Private Artifacts</h2>
zRange = mprintf("IN private");
}else if( phantomOnly ){
@ <h2>Phantom Artifacts</h2>
zRange = mprintf("IN phantom");
}else if( bUnclst ){
@ <h2>Unclustered Artifacts</h2>
zRange = mprintf("IN unclustered");
}else if( bRecent ){
@ <h2>%d(n) Most Recent Artifacts</h2>
zRange = mprintf(">=(SELECT rid FROM blob"
" ORDER BY rid DESC LIMIT 1 OFFSET %d)",n);
}else{
zRange = mprintf("BETWEEN %d AND %d", s, s+n-1);
}
describe_artifacts(zRange);
fossil_free(zRange);
db_prepare(&q,
/* 0 1 2 3 4 5 6 */
"SELECT rid, uuid, summary, isPrivate, type='phantom', ref, rcvid, "
" datetime(rcvfrom.mtime)"
" FROM description LEFT JOIN rcvfrom USING(rcvid)"
" ORDER BY rid %s",
((bRecent||bUnclst)?"DESC":"ASC")/*safe-for-%s*/
);
if( skin_detail_boolean("white-foreground") ){
zSha1Bg = "#714417";
zSha3Bg = "#177117";
}else{
zSha1Bg = "#ebffb0";
zSha3Bg = "#b0ffb0";
}
@ <table cellpadding="2" cellspacing="0" border="1">
@ <tr><th>RID<th>Hash<th>Received<th>Description<th>Ref<th>Remarks
while( db_step(&q)==SQLITE_ROW ){
int rid = db_column_int(&q,0);
const char *zUuid = db_column_text(&q, 1);
const char *zDesc = db_column_text(&q, 2);
int isPriv = db_column_int(&q,3);
int isPhantom = db_column_int(&q,4);
const char *zRef = db_column_text(&q,5);
const char *zDate = db_column_text(&q,7);
if( isPriv && !isPhantom && !g.perm.Private && !g.perm.Admin ){
/* Don't show private artifacts to users without Private (x) permission */
continue;
}
if( hashClr ){
const char *zClr = db_column_bytes(&q,1)>40 ? zSha3Bg : zSha1Bg;
@ <tr style='background-color:%s(zClr);'><td align="right">%d(rid)</td>
}else{
@ <tr><td align="right">%d(rid)</td>
}
@ <td> %z(href("%R/info/%!S",zUuid))%S(zUuid)</a> </td>
if( g.perm.Admin ){
int rcvid = db_column_int(&q, 6);
@ <td><a href='%R/rcvfrom?rcvid=%d(rcvid)'>%h(zDate)</a>
}else{
@ <td>%h(zDate)
}
@ <td align="left">%h(zDesc)</td>
if( zRef && zRef[0] ){
@ <td>%z(href("%R/info/%!S",zRef))%S(zRef)</a>
}else{
@ <td>
}
|
| ︙ | ︙ | |||
2161 2162 2163 2164 2165 2166 2167 |
collision_report("SELECT (SELECT uuid FROM blob WHERE rid=objid)"
" FROM event WHERE event.type='ci'"
" ORDER BY 1");
@ <h1>Hash Prefix Collisions on All Artifacts</h1>
collision_report("SELECT uuid FROM blob ORDER BY 1");
style_finish_page();
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 |
collision_report("SELECT (SELECT uuid FROM blob WHERE rid=objid)"
" FROM event WHERE event.type='ci'"
" ORDER BY 1");
@ <h1>Hash Prefix Collisions on All Artifacts</h1>
collision_report("SELECT uuid FROM blob ORDER BY 1");
style_finish_page();
}
/*
** WEBPAGE: clusterlist
**
** Show information about all cluster artifacts in the database.
*/
void clusterlist_page(void){
Stmt q;
int cnt = 1;
sqlite3_int64 szTotal = 0;
sqlite3_int64 szCTotal = 0;
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
style_header("All Cluster Artifacts");
style_submenu_element("All Artifactst", "bloblist");
if( g.perm.Admin ){
style_submenu_element("Artifact Log", "rcvfromlist");
}
style_submenu_element("Phantoms", "bloblist?phan");
if( g.perm.Write ){
style_submenu_element("Artifact Stats", "artifact_stats");
}
db_prepare(&q,
"SELECT blob.uuid, "
" blob.size, "
" octet_length(blob.content), "
" datetime(rcvfrom.mtime),"
" user.login,"
" rcvfrom.ipaddr"
" FROM tagxref JOIN blob ON tagxref.rid=blob.rid"
" LEFT JOIN rcvfrom ON blob.rcvid=rcvfrom.rcvid"
" LEFT JOIN user ON user.uid=rcvfrom.uid"
" WHERE tagxref.tagid=%d"
" ORDER BY rcvfrom.mtime, blob.uuid",
TAG_CLUSTER
);
@ <table cellpadding="2" cellspacing="0" border="1">
@ <tr><th>
@ <th>Hash
@ <th>Date Received
@ <th>Size
@ <th>Compressed Size
if( g.perm.Admin ){
@ <th>User<th>IP-Address
}
while( db_step(&q)==SQLITE_ROW ){
const char *zUuid = db_column_text(&q, 0);
sqlite3_int64 sz = db_column_int64(&q, 1);
sqlite3_int64 szC = db_column_int64(&q, 2);
const char *zDate = db_column_text(&q, 3);
const char *zUser = db_column_text(&q, 4);
const char *zIp = db_column_text(&q, 5);
szTotal += sz;
szCTotal += szC;
@ <tr><td align="right">%d(cnt++)
@ <td><a href="%R/info/%S(zUuid)">%S(zUuid)</a>
if( zDate ){
@ <td>%h(zDate)
}else{
@ <td>
}
@ <td align="right">%,lld(sz)
@ <td align="right">%,lld(szC)
if( g.perm.Admin ){
if( zUser ){
@ <td>%h(zUser)
}else{
@ <td>
}
if( zIp ){
@ <td>%h(zIp)
}else{
@ <td>
}
}
@ </tr>
}
@ </table>
db_finalize(&q);
@ <p>Total size of all clusters: %,lld(szTotal) bytes,
@ %,lld(szCTotal) bytes compressed</p>
style_finish_page();
}
|
Changes to src/patch.c.
| ︙ | ︙ | |||
79 80 81 82 83 84 85 |
*/
static void mkdeltaFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *zFile;
| | < < < < | | < < | > < | | | | 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
*/
static void mkdeltaFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *zFile;
Blob x, y, out;
int rid;
int nOut;
sqlite3_int64 sz;
rid = sqlite3_value_int(argv[0]);
if( !content_get(rid, &x) ){
sqlite3_result_error(context, "mkdelta(X,Y): no content for X", -1);
return;
}
zFile = (const char*)sqlite3_value_text(argv[1]);
if( zFile==0 ){
sqlite3_result_error(context, "mkdelta(X,Y): NULL Y argument", -1);
blob_reset(&x);
return;
}
sz = blob_read_from_file(&y, zFile, RepoFILE);
if( sz<0 ){
sqlite3_result_error(context, "mkdelta(X,Y): cannot read file Y", -1);
blob_reset(&x);
return;
}
blob_init(&out, 0, 0);
blob_resize(&out, sz+70);
if( blob_size(&x)==blob_size(&y)
&& memcmp(blob_buffer(&x), blob_buffer(&y), blob_size(&x))==0
){
blob_reset(&y);
blob_reset(&x);
sqlite3_result_blob64(context, "", 0, SQLITE_STATIC);
return;
}
nOut = delta_create(blob_buffer(&x),blob_size(&x),
blob_buffer(&y),blob_size(&y), blob_buffer(&out));
blob_resize(&out, nOut);
blob_reset(&x);
blob_reset(&y);
blob_compress(&out, &out);
sqlite3_result_blob64(context, blob_buffer(&out), blob_size(&out),
SQLITE_TRANSIENT);
blob_reset(&out);
}
/*
** Generate a binary patch file and store it into the file
** named zOut. Or if zOut is NULL, write it into out.
**
|
| ︙ | ︙ | |||
385 386 387 388 389 390 391 |
blob_init(&cmd, 0, 0);
if( unsaved_changes(0) ){
if( (mFlags & PATCH_FORCE)==0 ){
fossil_fatal("Cannot apply patch: there are unsaved changes "
"in the current check-out");
}else{
| | | 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 |
blob_init(&cmd, 0, 0);
if( unsaved_changes(0) ){
if( (mFlags & PATCH_FORCE)==0 ){
fossil_fatal("Cannot apply patch: there are unsaved changes "
"in the current check-out");
}else{
blob_appendf(&cmd, "%$ revert --noundo", g.nameOfExe);
if( mFlags & PATCH_DRYRUN ){
fossil_print("%s\n", blob_str(&cmd));
}else{
int rc = fossil_system(blob_str(&cmd));
if( rc ){
fossil_fatal("unable to revert preexisting changes: %s",
blob_str(&cmd));
|
| ︙ | ︙ | |||
427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 |
fossil_fatal("unable to update to the baseline check-out: %s",
blob_str(&cmd));
}
}
}
blob_reset(&cmd);
if( db_table_exists("patch","patchmerge") ){
db_prepare(&q,
"SELECT type, mhash, upper(type) FROM patch.patchmerge"
" WHERE type IN ('merge','cherrypick','backout','integrate')"
" AND mhash NOT GLOB '*[^a-fA-F0-9]*';"
);
while( db_step(&q)==SQLITE_ROW ){
const char *zType = db_column_text(&q,0);
blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
if( strcmp(zType,"merge")==0 ){
| > | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 |
fossil_fatal("unable to update to the baseline check-out: %s",
blob_str(&cmd));
}
}
}
blob_reset(&cmd);
if( db_table_exists("patch","patchmerge") ){
int nMerge = 0;
db_prepare(&q,
"SELECT type, mhash, upper(type) FROM patch.patchmerge"
" WHERE type IN ('merge','cherrypick','backout','integrate')"
" AND mhash NOT GLOB '*[^a-fA-F0-9]*';"
);
while( db_step(&q)==SQLITE_ROW ){
const char *zType = db_column_text(&q,0);
blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
if( strcmp(zType,"merge")==0 ){
blob_appendf(&cmd, " merge --noundo --nosync %s\n",
db_column_text(&q,1));
}else{
blob_appendf(&cmd, " merge --%s --noundo --nosync %s\n",
zType, db_column_text(&q,1));
}
nMerge++;
if( mFlags & PATCH_VERBOSE ){
fossil_print("%-10s %s\n", db_column_text(&q,2),
db_column_text(&q,0));
}
}
db_finalize(&q);
if( mFlags & PATCH_DRYRUN ){
fossil_print("%s", blob_str(&cmd));
}else{
int rc = fossil_unsafe_system(blob_str(&cmd));
if( rc ){
fossil_fatal("unable to do merges:\n%s",
blob_str(&cmd));
}
}
blob_reset(&cmd);
/* 2024-12-16 https://fossil-scm.org/home/forumpost/51a37054
** If one or more merge operations occurred in the patch and there are
** files that are marked as "chnged' in the local VFILE but which
** are not mentioned as having been modified in the patch, then
** revert those files.
*/
if( nMerge ){
int vid = db_lget_int("checkout", 0);
int nRevert = 0;
blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
blob_appendf(&cmd, " revert --noundo ");
db_prepare(&q,
"SELECT pathname FROM vfile WHERE vid=%d AND chnged "
"EXCEPT SELECT pathname FROM chng",
vid
);
while( db_step(&q)==SQLITE_ROW ){
blob_append_escaped_arg(&cmd, db_column_text(&q,0), 1);
nRevert++;
}
db_finalize(&q);
if( nRevert ){
if( mFlags & PATCH_DRYRUN ){
fossil_print("%s", blob_str(&cmd));
}else{
int rc = fossil_unsafe_system(blob_str(&cmd));
if( rc ){
fossil_fatal("unable to do reverts:\n%s",
blob_str(&cmd));
}
}
}
blob_reset(&cmd);
}
}
/* Deletions */
db_prepare(&q, "SELECT pathname FROM patch.chng"
" WHERE origname IS NULL AND delta IS NULL");
while( db_step(&q)==SQLITE_ROW ){
if( blob_size(&cmd)==0 ){
|
| ︙ | ︙ |
Changes to src/path.c.
| ︙ | ︙ | |||
540 541 542 543 544 545 546 547 548 549 550 551 552 553 |
*pnChng = i/2;
while( pAll ){
pChng = pAll;
pAll = pAll->pNext;
fossil_free(pChng);
}
}
}
/*
** COMMAND: test-name-changes
**
** Usage: %fossil test-name-changes [--debug] VERSION1 VERSION2
**
| > | 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 |
*pnChng = i/2;
while( pAll ){
pChng = pAll;
pAll = pAll->pNext;
fossil_free(pChng);
}
}
path_reset();
}
/*
** COMMAND: test-name-changes
**
** Usage: %fossil test-name-changes [--debug] VERSION1 VERSION2
**
|
| ︙ | ︙ |
Changes to src/printf.c.
| ︙ | ︙ | |||
103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
#define etROOT 24 /* String value of g.zTop: %R */
#define etJSONSTR 25 /* String encoded as a JSON string literal: %j
Use %!j to include double-quotes around it. */
#define etSHELLESC 26 /* Escape a filename for use in a shell command: %$
See blob_append_escaped_arg() for details
"%$" -> adds "./" prefix if necessary.
"%!$" -> omits the "./" prefix. */
/*
** An "etByte" is an 8-bit unsigned value.
*/
typedef unsigned char etByte;
| > | 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
#define etROOT 24 /* String value of g.zTop: %R */
#define etJSONSTR 25 /* String encoded as a JSON string literal: %j
Use %!j to include double-quotes around it. */
#define etSHELLESC 26 /* Escape a filename for use in a shell command: %$
See blob_append_escaped_arg() for details
"%$" -> adds "./" prefix if necessary.
"%!$" -> omits the "./" prefix. */
#define etHEX 27 /* Encode a string as hexadecimal */
/*
** An "etByte" is an 8-bit unsigned value.
*/
typedef unsigned char etByte;
|
| ︙ | ︙ | |||
140 141 142 143 144 145 146 | ** most frequently used conversion types first. ** ** NB: When modifying this table is it vital that you also update the fmtchr[] ** variable to match!!! */ static const char aDigits[] = "0123456789ABCDEF0123456789abcdef"; static const char aPrefix[] = "-x0\000X0"; | | | 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 |
** most frequently used conversion types first.
**
** NB: When modifying this table is it vital that you also update the fmtchr[]
** variable to match!!!
*/
static const char aDigits[] = "0123456789ABCDEF0123456789abcdef";
static const char aPrefix[] = "-x0\000X0";
static const char fmtchr[] = "dsgzqQbBWhRtTwFSjcouxXfeEGin%p/$H";
static const et_info fmtinfo[] = {
{ 'd', 10, 1, etRADIX, 0, 0 },
{ 's', 0, 4, etSTRING, 0, 0 },
{ 'g', 0, 1, etGENERIC, 30, 0 },
{ 'z', 0, 6, etDYNSTRING, 0, 0 },
{ 'q', 0, 4, etSQLESCAPE, 0, 0 },
{ 'Q', 0, 4, etSQLESCAPE2, 0, 0 },
|
| ︙ | ︙ | |||
174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
{ 'G', 0, 1, etGENERIC, 14, 0 },
{ 'i', 10, 1, etRADIX, 0, 0 },
{ 'n', 0, 0, etSIZE, 0, 0 },
{ '%', 0, 0, etPERCENT, 0, 0 },
{ 'p', 16, 0, etPOINTER, 0, 1 },
{ '/', 0, 0, etPATH, 0, 0 },
{ '$', 0, 0, etSHELLESC, 0, 0 },
{ etERROR, 0,0,0,0,0} /* Must be last */
};
#define etNINFO count(fmtinfo)
/*
** Verify that the fmtchr[] and fmtinfo[] arrays are in agreement.
**
| > | 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
{ 'G', 0, 1, etGENERIC, 14, 0 },
{ 'i', 10, 1, etRADIX, 0, 0 },
{ 'n', 0, 0, etSIZE, 0, 0 },
{ '%', 0, 0, etPERCENT, 0, 0 },
{ 'p', 16, 0, etPOINTER, 0, 1 },
{ '/', 0, 0, etPATH, 0, 0 },
{ '$', 0, 0, etSHELLESC, 0, 0 },
{ 'H', 0, 0, etHEX, 0, 0 },
{ etERROR, 0,0,0,0,0} /* Must be last */
};
#define etNINFO count(fmtinfo)
/*
** Verify that the fmtchr[] and fmtinfo[] arrays are in agreement.
**
|
| ︙ | ︙ | |||
841 842 843 844 845 846 847 848 849 850 851 852 853 854 |
break;
}
case etSHELLESC: {
char *zArg = va_arg(ap, char*);
blob_append_escaped_arg(pBlob, zArg, !flag_altform2);
length = width = 0;
break;
}
case etERROR:
buf[0] = '%';
buf[1] = c;
errorflag = 0;
idx = 1+(c!=0);
blob_append(pBlob,"%",idx);
| > > > > > > > > > > > | 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 |
break;
}
case etSHELLESC: {
char *zArg = va_arg(ap, char*);
blob_append_escaped_arg(pBlob, zArg, !flag_altform2);
length = width = 0;
break;
}
case etHEX: {
char *zArg = va_arg(ap, char*);
int szArg = (int)strlen(zArg);
int szBlob = blob_size(pBlob);
u8 *aBuf;
blob_resize(pBlob, szBlob+szArg*2+1);
aBuf = (u8*)&blob_buffer(pBlob)[szBlob];
encode16((const u8*)zArg, aBuf, szArg);
length = width = 0;
break;
}
case etERROR:
buf[0] = '%';
buf[1] = c;
errorflag = 0;
idx = 1+(c!=0);
blob_append(pBlob,"%",idx);
|
| ︙ | ︙ |
Changes to src/rebuild.c.
| ︙ | ︙ | |||
388 389 390 391 392 393 394 395 |
if (ttyOutput && !g.fQuiet) {
percent_complete(0);
}
manifest_disable_event_triggers();
rebuild_update_schema();
blob_init(&sql, 0, 0);
db_unprotect(PROTECT_ALL);
db_prepare(&q,
| > > > | | | > > > | 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 |
if (ttyOutput && !g.fQuiet) {
percent_complete(0);
}
manifest_disable_event_triggers();
rebuild_update_schema();
blob_init(&sql, 0, 0);
db_unprotect(PROTECT_ALL);
#ifndef SQLITE_PREPARE_DONT_LOG
g.dbIgnoreErrors++;
#endif
db_prepare(&q,
"SELECT name FROM pragma_table_list /*scan*/"
" WHERE schema='repository' AND type IN ('table','virtual')"
" AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user','alias',"
"'config','shun','private','reportfmt',"
"'concealed','accesslog','modreq',"
"'purgeevent','purgeitem','unversioned',"
"'subscriber','pending_alert','chat')"
" AND name NOT GLOB 'sqlite_*'"
" AND name NOT GLOB 'fx_*';"
);
while( db_step(&q)==SQLITE_ROW ){
blob_appendf(&sql, "DROP TABLE IF EXISTS \"%w\";\n", db_column_text(&q,0));
}
db_finalize(&q);
#ifndef SQLITE_PREPARE_DONT_LOG
g.dbIgnoreErrors--;
#endif
db_multi_exec("%s", blob_str(&sql)/*safe-for-%s*/);
blob_reset(&sql);
db_multi_exec("%s", zRepositorySchema2/*safe-for-%s*/);
ticket_create_table(0);
shun_artifacts();
db_multi_exec(
|
| ︙ | ︙ |
Changes to src/schema.c.
| ︙ | ︙ | |||
26 27 28 29 30 31 32 | const char zConfigSchema[] = @ -- This file contains the schema for the database that is kept in the @ -- ~/.fossil file and that stores information about the users setup. @ -- @ CREATE TABLE global_config( @ name TEXT PRIMARY KEY, @ value TEXT | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | const char zConfigSchema[] = @ -- This file contains the schema for the database that is kept in the @ -- ~/.fossil file and that stores information about the users setup. @ -- @ CREATE TABLE global_config( @ name TEXT PRIMARY KEY, @ value TEXT @ ) WITHOUT ROWID; @ @ -- Identifier for this file type. @ -- The integer is the same as 'FSLG'. @ PRAGMA application_id=252006675; ; #if INTERFACE |
| ︙ | ︙ | |||
136 137 138 139 140 141 142 | @ -- in the form of name-value pairs. @ -- @ CREATE TABLE config( @ name TEXT PRIMARY KEY NOT NULL, -- Primary name of the entry @ value CLOB, -- Content of the named parameter @ mtime DATE, -- last modified. seconds since 1970 @ CHECK( typeof(name)='text' AND length(name)>=1 ) | | | | | 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | @ -- in the form of name-value pairs. @ -- @ CREATE TABLE config( @ name TEXT PRIMARY KEY NOT NULL, -- Primary name of the entry @ value CLOB, -- Content of the named parameter @ mtime DATE, -- last modified. seconds since 1970 @ CHECK( typeof(name)='text' AND length(name)>=1 ) @ ) WITHOUT ROWID; @ @ -- Artifacts that should not be processed are identified in the @ -- "shun" table. Artifacts that are control-file forgeries or @ -- spam or artifacts whose contents violate administrative policy @ -- can be shunned in order to prevent them from contaminating @ -- the repository. @ -- @ -- Shunned artifacts do not exist in the blob table. Hence they @ -- have not artifact ID (rid) and we thus must store their full @ -- UUID. @ -- @ CREATE TABLE shun( @ uuid TEXT PRIMARY KEY,-- UUID of artifact to be shunned. Canonical form @ mtime DATE, -- When added. seconds since 1970 @ scom TEXT -- Optional text explaining why the shun occurred @ ) WITHOUT ROWID; @ @ -- Artifacts that should not be pushed are stored in the "private" @ -- table. Private artifacts are omitted from the "unclustered" and @ -- "unsent" tables. @ -- @ -- A phantom artifact (that is, an artifact with BLOB.SIZE<0 - an artifact @ -- for which we do not know the content) might also be marked as private. |
| ︙ | ︙ | |||
191 192 193 194 195 196 197 | @ -- This table contains sensitive information and should not be shared @ -- with unauthorized users. @ -- @ CREATE TABLE concealed( @ hash TEXT PRIMARY KEY, -- The SHA1 hash of content @ mtime DATE, -- Time created. Seconds since 1970 @ content TEXT -- Content intended to be concealed | | | 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 | @ -- This table contains sensitive information and should not be shared @ -- with unauthorized users. @ -- @ CREATE TABLE concealed( @ hash TEXT PRIMARY KEY, -- The SHA1 hash of content @ mtime DATE, -- Time created. Seconds since 1970 @ content TEXT -- Content intended to be concealed @ ) WITHOUT ROWID; @ @ -- The application ID helps the unix "file" command to identify the @ -- database as a fossil repository. @ PRAGMA application_id=252006673; ; /* |
| ︙ | ︙ | |||
526 527 528 529 530 531 532 | /* ** The schema for the local FOSSIL database file found at the root ** of every check-out. This database contains the complete state of ** the check-out. See also the addendum in zLocalSchemaVmerge[]. */ const char zLocalSchema[] = | | | | 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 | /* ** The schema for the local FOSSIL database file found at the root ** of every check-out. This database contains the complete state of ** the check-out. See also the addendum in zLocalSchemaVmerge[]. */ const char zLocalSchema[] = @ -- The VVAR table holds miscellanous information about the local checkout @ -- in the form of name-value pairs. This is similar to the VAR table @ -- table in the repository except that this table holds information that @ -- is specific to the local check-out. @ -- @ -- Important Variables: @ -- @ -- repository Full pathname of the repository database @ -- user-id Userid to use @ -- @ CREATE TABLE vvar( @ name TEXT PRIMARY KEY NOT NULL, -- Primary name of the entry @ value CLOB, -- Content of the named parameter @ CHECK( typeof(name)='text' AND length(name)>=1 ) @ ) WITHOUT ROWID; @ @ -- Each entry in the vfile table represents a single file in the @ -- current check-out. @ -- @ -- The file.rid field is 0 for files or folders that have been @ -- added but not yet committed. @ -- |
| ︙ | ︙ |
Changes to src/setup.c.
| ︙ | ︙ | |||
499 500 501 502 503 504 505 | @ parameters. Some robots will spend hours juggling around query parameters @ or even forging fake query parameters in an effort to discover new @ behavior or to find an SQL injection opportunity or similar. This can @ waste hours of CPU time and gigabytes of bandwidth on the server. A @ suggested value for this setting is: @ "<tt>timeline,*diff,vpatch,annotate,blame,praise,dir,tree</tt>". @ (Property: robot-restrict) | | > > > > > > > > > > > | 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 |
@ parameters. Some robots will spend hours juggling around query parameters
@ or even forging fake query parameters in an effort to discover new
@ behavior or to find an SQL injection opportunity or similar. This can
@ waste hours of CPU time and gigabytes of bandwidth on the server. A
@ suggested value for this setting is:
@ "<tt>timeline,*diff,vpatch,annotate,blame,praise,dir,tree</tt>".
@ (Property: robot-restrict)
@ <br>
textarea_attribute("", 2, 80,
"robot-restrict", "rbrestrict", "", 0);
@ <br> The following comma-separated GLOB pattern allows for exceptions
@ in the maximum number of query parameters before a request is considered
@ complex. If this GLOB pattern exists and is non-empty and if it
@ matches against the pagename followed by "/" and the number of query
@ parameters, then the request is allowed through. For example, the
@ suggested pattern of "timeline/[012]" allows the /timeline page to
@ pass with up to 2 query parameters besides "name".
@ (Property: robot-restrict-qp)
@ <br>
textarea_attribute("", 2, 80,
"robot-restrict-qp", "rbrestrictqp", "", 0);
@ <hr>
@ <p><input type="submit" name="submit" value="Apply Changes"></p>
@ </div></form>
db_end_transaction(0);
style_finish_page();
}
|
| ︙ | ︙ |
Changes to src/setupuser.c.
| ︙ | ︙ | |||
113 114 115 116 117 118 119 |
}
}
}
if( !bUnusedOnly ){
style_submenu_element("Unused", "setup_ulist?unused");
}
@ <table border=1 cellpadding=2 cellspacing=0 class='userTable sortable' \
| | | 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
}
}
}
if( !bUnusedOnly ){
style_submenu_element("Unused", "setup_ulist?unused");
}
@ <table border=1 cellpadding=2 cellspacing=0 class='userTable sortable' \
@ data-column-types='ktxKTKt' data-init-sort='4'>
@ <thead><tr>
@ <th>Login Name<th>Caps<th>Info<th>Date<th>Expire<th>Last Login\
@ <th>Alerts</tr></thead>
@ <tbody>
db_multi_exec(
"CREATE TEMP TABLE lastAccess(uname TEXT PRIMARY KEY, atime REAL)"
"WITHOUT ROWID;"
|
| ︙ | ︙ | |||
159 160 161 162 163 164 165 |
db_prepare(&s,
"SELECT uid, login, cap, info, date(user.mtime,'unixepoch'),"
" lower(login) AS sortkey, "
" CASE WHEN info LIKE '%%expires 20%%'"
" THEN substr(info,instr(lower(info),'expires')+8,10)"
" END AS exp,"
"atime,"
| | > | > | | 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 |
db_prepare(&s,
"SELECT uid, login, cap, info, date(user.mtime,'unixepoch'),"
" lower(login) AS sortkey, "
" CASE WHEN info LIKE '%%expires 20%%'"
" THEN substr(info,instr(lower(info),'expires')+8,10)"
" END AS exp,"
"atime,"
" subscriber.ssub, subscriber.subscriberId,"
" user.mtime AS sorttime"
" FROM user LEFT JOIN lastAccess ON login=uname"
" LEFT JOIN subscriber ON login=suname"
" WHERE login NOT IN ('anonymous','nobody','developer','reader') %s"
" ORDER BY sorttime DESC", zWith/*safe-for-%s*/
);
rNow = db_double(0.0, "SELECT julianday('now');");
while( db_step(&s)==SQLITE_ROW ){
int uid = db_column_int(&s, 0);
const char *zLogin = db_column_text(&s, 1);
const char *zCap = db_column_text(&s, 2);
const char *zInfo = db_column_text(&s, 3);
const char *zDate = db_column_text(&s, 4);
const char *zSortKey = db_column_text(&s,5);
const char *zExp = db_column_text(&s,6);
double rATime = db_column_double(&s,7);
char *zAge = 0;
const char *zSub;
int sid = db_column_int(&s,9);
sqlite3_int64 sorttime = db_column_int64(&s, 10);
if( rATime>0.0 ){
zAge = human_readable_age(rNow - rATime);
}
if( bUbg ){
@ <tr style='background-color: %h(user_color(zLogin));'>
}else{
@ <tr>
}
@ <td data-sortkey='%h(zSortKey)'>\
@ <a href='setup_uedit?id=%d(uid)'>%h(zLogin)</a>
@ <td>%h(zCap)
@ <td>%h(zInfo)
@ <td data-sortkey='%09llx(sorttime)'>%h(zDate?zDate:"")
@ <td>%h(zExp?zExp:"")
@ <td data-sortkey='%f(rATime)' style='white-space:nowrap'>%s(zAge?zAge:"")
if( db_column_type(&s,8)==SQLITE_NULL ){
@ <td>
}else if( (zSub = db_column_text(&s,8))==0 || zSub[0]==0 ){
@ <td><a href="%R/alerts?sid=%d(sid)"><i>off</i></a>
}else{
|
| ︙ | ︙ | |||
1014 1015 1016 1017 1018 1019 1020 |
return;
}
style_header("User %h", db_column_text(&q,1));
@ <table class="label-value">
@ <tr><th>uid:</th><td>%d(db_column_int(&q,0))
@ (<a href="%R/setup_uedit?id=%d(db_column_int(&q,0))">edit</a>)</td></tr>
@ <tr><th>login:</th><td>%h(db_column_text(&q,1))</td></tr>
| | | 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 |
return;
}
style_header("User %h", db_column_text(&q,1));
@ <table class="label-value">
@ <tr><th>uid:</th><td>%d(db_column_int(&q,0))
@ (<a href="%R/setup_uedit?id=%d(db_column_int(&q,0))">edit</a>)</td></tr>
@ <tr><th>login:</th><td>%h(db_column_text(&q,1))</td></tr>
@ <tr><th>capabilities:</th><td>%h(db_column_text(&q,2))</td></tr>
@ <tr><th valign="top">info:</th>
@ <td valign="top"><span style='white-space:pre-line;'>\
@ %h(db_column_text(&q,5))</span></td></tr>
@ <tr><th>user.mtime:</th><td>%h(db_column_text(&q,6))</td></tr>
if( db_column_type(&q,7)!=SQLITE_NULL ){
@ <tr><th>subscriberId:</th><td>%d(db_column_int(&q,7))
@ (<a href="%R/alerts?sid=%d(db_column_int(&q,7))">edit</a>)</td></tr>
|
| ︙ | ︙ |
Changes to src/sitemap.c.
| ︙ | ︙ | |||
54 55 56 57 58 59 60 |
int srchFlags;
int inSublist = 0;
int i;
int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */
int e = atoi(PD("e","0"));
const char *zExtra;
| < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
int srchFlags;
int inSublist = 0;
int i;
int isPopup = 0; /* This is an XMLHttpRequest() for /sitemap */
int e = atoi(PD("e","0"));
const char *zExtra;
login_check_credentials();
if( P("popup")!=0 ){
/* The "popup" query parameter
** then disable anti-robot defenses */
isPopup = 1;
g.perm.Hyperlink = 1;
g.jsHref = 0;
}
srchFlags = search_restrict(SRCH_ALL);
if( !isPopup ){
style_header("Site Map");
style_adunit_config(ADUNIT_RIGHT_OK);
}
@ <ul id="sitemap" class="columns" style="column-width:20em">
if( (e&1)==0 ){
@ <li>%z(href("%R/home"))Home Page</a>
}
zExtra = db_get("sitemap-extra",0);
if( zExtra && (e&2)==0 ){
int rc;
char **azExtra = 0;
int *anExtra;
int nExtra = 0;
if( isPopup ) Th_FossilInit(0);
|
| ︙ | ︙ | |||
137 138 139 140 141 142 143 |
}
}
}
Th_Free(g.interp, azExtra);
}
if( (e&1)!=0 ) goto end_of_sitemap;
| < < < < < < < < < < < > > > | 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
}
}
}
Th_Free(g.interp, azExtra);
}
if( (e&1)!=0 ) goto end_of_sitemap;
if( inSublist ){
@ </ul>
inSublist = 0;
}
@ </li>
if( cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){
@ <li>%z(href("%R/ckout"))Checkout Status</a></li>
}
if( g.perm.Read ){
const char *zEditGlob = db_get("fileedit-glob","");
@ <li>%z(href("%R/tree"))File Browser</a>
@ <ul>
@ <li>%z(href("%R/tree?type=tree&ci=trunk"))Tree-view,
@ Trunk Check-in</a></li>
@ <li>%z(href("%R/tree?type=flat"))Flat-view</a></li>
|
| ︙ | ︙ | |||
190 191 192 193 194 195 196 |
if( srchFlags ){
@ <li>%z(href("%R/search"))Search</a></li>
}
if( g.perm.Chat ){
@ <li>%z(href("%R/chat"))Chat</a></li>
}
if( g.perm.RdForum ){
| > | | 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
if( srchFlags ){
@ <li>%z(href("%R/search"))Search</a></li>
}
if( g.perm.Chat ){
@ <li>%z(href("%R/chat"))Chat</a></li>
}
if( g.perm.RdForum ){
const char *zTitle = db_get("forum-title","Forum");
@ <li>%z(href("%R/forum"))%h(zTitle)</a>
@ <ul>
@ <li>%z(href("%R/timeline?y=f"))Recent activity</a></li>
@ </ul>
@ </li>
}
if( g.perm.RdTkt ){
@ <li>%z(href("%R/reportlist"))Tickets/Bug Reports</a>
|
| ︙ | ︙ |
Changes to src/statrep.c.
| ︙ | ︙ | |||
288 289 290 291 292 293 294 |
rowClass = ++nRowNumber % 2;
nEventTotal += nCount;
nEventsPerYear += nCount;
@<tr class='row%d(rowClass)'>
@ <td>
if(includeMonth){
cgi_printf("<a href='%R/timeline?"
| | | | 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 |
rowClass = ++nRowNumber % 2;
nEventTotal += nCount;
nEventsPerYear += nCount;
@<tr class='row%d(rowClass)'>
@ <td>
if(includeMonth){
cgi_printf("<a href='%R/timeline?"
"ym=%t&y=%s",
zTimeframe,
statsReportTimelineYFlag );
/* Reminder: n=nCount is not actually correct for bymonth unless
that was the only user who caused events.
*/
if( zUserName ){
cgi_printf("&u=%t", zUserName);
}
|
| ︙ | ︙ | |||
729 730 731 732 733 734 735 |
const int nCount = db_column_int(&q,1);
int nSize = (nCount>0 && nMaxEvents>0)
? (int)(100 * nCount / nMaxEvents)
: 0;
if(!nSize) nSize = 1;
total += nCount;
cgi_printf("<tr class='row%d'>", ++rowCount % 2 );
| | | | 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 |
const int nCount = db_column_int(&q,1);
int nSize = (nCount>0 && nMaxEvents>0)
? (int)(100 * nCount / nMaxEvents)
: 0;
if(!nSize) nSize = 1;
total += nCount;
cgi_printf("<tr class='row%d'>", ++rowCount % 2 );
cgi_printf("<td><a href='%R/timeline?yw=%t%s&y=%s",
zYear, zWeek,
statsReportTimelineYFlag);
if( zUserName ){
cgi_printf("&u=%t",zUserName);
}
cgi_printf("'>%s</a></td>",zWeek);
cgi_printf("<td>%d</td>",nCount);
|
| ︙ | ︙ |
Changes to src/style.c.
| ︙ | ︙ | |||
819 820 821 822 823 824 825 826 827 828 829 830 831 832 |
headerHasBeenGenerated = 1;
sideboxUsed = 0;
if( g.perm.Debug && P("showqp") ){
@ <div class="debug">
cgi_print_all(0, 0, 0);
@ </div>
}
}
#if INTERFACE
/* Allowed parameters for style_adunit() */
#define ADUNIT_OFF 0x0001 /* Do not allow ads on this page */
#define ADUNIT_RIGHT_OK 0x0002 /* Right-side vertical ads ok here */
#endif
| > | 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 |
headerHasBeenGenerated = 1;
sideboxUsed = 0;
if( g.perm.Debug && P("showqp") ){
@ <div class="debug">
cgi_print_all(0, 0, 0);
@ </div>
}
fossil_free(zTitle);
}
#if INTERFACE
/* Allowed parameters for style_adunit() */
#define ADUNIT_OFF 0x0001 /* Do not allow ads on this page */
#define ADUNIT_RIGHT_OK 0x0002 /* Right-side vertical ads ok here */
#endif
|
| ︙ | ︙ | |||
1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 |
*/
Th_Store("baseurl", g.zBaseURL);
Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL);
Th_Store("home", g.zTop);
image_url_var("logo");
image_url_var("background");
Th_Render(blob_str(&css));
/* Tell CGI that the content returned by this page is considered cacheable */
g.isConst = 1;
}
/*
** All possible capabilities
| > | 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 |
*/
Th_Store("baseurl", g.zBaseURL);
Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL);
Th_Store("home", g.zTop);
image_url_var("logo");
image_url_var("background");
Th_Render(blob_str(&css));
blob_reset(&css);
/* Tell CGI that the content returned by this page is considered cacheable */
g.isConst = 1;
}
/*
** All possible capabilities
|
| ︙ | ︙ |
Changes to src/timeline.c.
| ︙ | ︙ | |||
189 190 191 192 193 194 195 | ** 10. Short comment to user for repeated tickets and wiki */ void www_print_timeline( Stmt *pQuery, /* Query to implement the timeline */ int tmFlags, /* Flags controlling display behavior */ const char *zThisUser, /* Suppress links to this user */ const char *zThisTag, /* Suppress links to this tag */ | | | 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
** 10. Short comment to user for repeated tickets and wiki
*/
void www_print_timeline(
Stmt *pQuery, /* Query to implement the timeline */
int tmFlags, /* Flags controlling display behavior */
const char *zThisUser, /* Suppress links to this user */
const char *zThisTag, /* Suppress links to this tag */
Matcher *pLeftBranch, /* Comparison function to use for zLeftBranch */
int selectedRid, /* Highlight the line with this RID value or zero */
int secondRid, /* Secondary highlight (or zero) */
void (*xExtra)(int) /* Routine to call on each line of display */
){
int mxWikiLen;
Blob comment;
int prevTagid = 0;
|
| ︙ | ︙ | |||
811 812 813 814 815 816 817 |
@ event%s(suppressCnt>1?"s":"") omitted.</span>
suppressCnt = 0;
}
if( pendingEndTr ){
@ </td></tr>
}
if( pGraph ){
| | | 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 |
@ event%s(suppressCnt>1?"s":"") omitted.</span>
suppressCnt = 0;
}
if( pendingEndTr ){
@ </td></tr>
}
if( pGraph ){
graph_finish(pGraph, pLeftBranch, tmFlags);
if( pGraph->nErr ){
graph_free(pGraph);
pGraph = 0;
}else{
@ <tr class="timelineBottom" id="btm-%d(iTableId)">\
@ <td></td><td></td><td></td></tr>
}
|
| ︙ | ︙ | |||
1288 1289 1290 1291 1292 1293 1294 |
*/
static void addFileGlobExclusion(
const char *zChng, /* The filename GLOB list */
Blob *pSql /* The SELECT statement under construction */
){
if( zChng==0 || zChng[0]==0 ) return;
blob_append_sql(pSql," AND event.objid IN ("
| | | > < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | | < | > > > > > > > | | < | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 |
*/
static void addFileGlobExclusion(
const char *zChng, /* The filename GLOB list */
Blob *pSql /* The SELECT statement under construction */
){
if( zChng==0 || zChng[0]==0 ) return;
blob_append_sql(pSql," AND event.objid IN ("
"SELECT mlink.mid FROM mlink, filename\n"
" WHERE mlink.fnid=filename.fnid\n"
" AND %s)",
glob_expr("filename.name", mprintf("\"%s\"", zChng)));
}
static void addFileGlobDescription(
const char *zChng, /* The filename GLOB list */
Blob *pDescription /* Result description */
){
if( zChng==0 || zChng[0]==0 ) return;
blob_appendf(pDescription, " that include changes to files matching '%h'",
zChng);
}
/*
** Similar to fossil_expand_datetime()
**
** Add missing "-" characters into a date/time. Examples:
**
** 20190419 => 2019-04-19
** 201904 => 2019-04
*/
const char *timeline_expand_datetime(const char *zIn, int *pbZulu){
static char zEDate[16];
int n = (int)strlen(zIn);
int i, j;
/* These forms are recognized:
**
** (1) YYYYMMDD
** (2) YYYYMM
** (3) YYYYWW
*/
if( n && (zIn[n-1]=='Z' || zIn[n-1]=='z') ){
n--;
if( pbZulu ) *pbZulu = 1;
}else{
if( pbZulu ) *pbZulu = 0;
}
if( n!=8 && n!=6 ) return zIn;
/* Every character must be a digit */
for(i=0; i<n && fossil_isdigit(zIn[i]); i++){}
if( i!=n ) return zIn;
/* Expand the date */
for(i=j=0; i<n; i++){
if( j==4 || j==7 ) zEDate[j++] = '-';
zEDate[j++] = zIn[i];
}
zEDate[j] = 0;
/* It looks like this may be a date. Return it with punctuation added. */
return zEDate;
}
/*
** Check to see if the argument is a date-span for the ymd= query
** parameter. A valid date-span is of the form:
**
** 0123456789 123456 <-- index
** YYYYMMDD-YYYYMMDD
**
** with an optional "Z" timeline modifier at the end. Return true if
** the input is a valid date space and false if not.
*/
static int timeline_is_datespan(const char *zDay){
size_t n = strlen(zDay);
int i, d, m;
if( n<17 || n>18 ) return 0;
if( n==18 ){
if( zDay[17]!='Z' && zDay[17]!='z' ) return 0;
n--;
}
if( zDay[8]!='-' ) return 0;
for(i=0; i<17 && (fossil_isdigit(zDay[i]) || i==8); i++){}
if( i!=17 ) return 0;
i = atoi(zDay);
d = i%100;
if( d<1 || d>31 ) return 0;
m = (i/100)%100;
if( m<1 || m>12 ) return 0;
i = atoi(zDay+9);
d = i%100;
if( d<1 || d>31 ) return 0;
m = (i/100)%100;
if( m<1 || m>12 ) return 0;
return 1;
}
/*
** Find the first check-in encountered with a particular tag
** when moving either forwards are backwards in time from a
** particular starting point (iFrom). Return the rid of that
** first check-in. If there are no check-ins in the decendent
** or ancestor set of check-in iFrom that match the tag, then
|
| ︙ | ︙ | |||
1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 |
int ans = 0;
tagId = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", zEnd);
if( tagId==0 ){
endId = symbolic_name_to_rid(zEnd, "ci");
if( endId==0 ) return 0;
}
if( bForward ){
if( tagId ){
db_prepare(&q,
"WITH RECURSIVE dx(id,mtime) AS ("
" SELECT %d, event.mtime FROM event WHERE objid=%d"
" UNION"
" SELECT plink.cid, plink.mtime"
| > | 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 |
int ans = 0;
tagId = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'", zEnd);
if( tagId==0 ){
endId = symbolic_name_to_rid(zEnd, "ci");
if( endId==0 ) return 0;
}
db_pause_dml_log();
if( bForward ){
if( tagId ){
db_prepare(&q,
"WITH RECURSIVE dx(id,mtime) AS ("
" SELECT %d, event.mtime FROM event WHERE objid=%d"
" UNION"
" SELECT plink.cid, plink.mtime"
|
| ︙ | ︙ | |||
1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 |
);
}
}
if( db_step(&q)==SQLITE_ROW ){
ans = db_column_int(&q, 0);
}
db_finalize(&q);
return ans;
}
/*
** COMMAND: test-endpoint
**
** Usage: fossil test-endpoint BASE TAG ?OPTIONS?
**
** Show the first check-in with TAG that is a descendent or ancestor
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 |
);
}
}
if( db_step(&q)==SQLITE_ROW ){
ans = db_column_int(&q, 0);
}
db_finalize(&q);
db_unpause_dml_log();
return ans;
}
/*
** Add to the (temp) table zTab, RID values for every check-in
** identifier found on the zExtra string. Check-in names can be separated
** by commas or by whitespace.
*/
static void add_extra_rids(const char *zTab, const char *zExtra){
int ii;
int rid;
int cnt;
Blob sql;
char *zX;
char *zToDel;
if( zExtra==0 ) return;
cnt = 0;
blob_init(&sql, 0, 0);
zX = zToDel = fossil_strdup(zExtra);
blob_append_sql(&sql, "INSERT OR IGNORE INTO \"%w\" VALUES", zTab);
while( zX[0] ){
char c;
if( zX[0]==',' || zX[0]==' ' ){ zX++; continue; }
for(ii=1; zX[ii] && zX[ii]!=',' && zX[ii]!=' '; ii++){}
c = zX[ii];
zX[ii] = 0;
rid = name_to_rid(zX);
if( rid>0 ){
if( (cnt%10)==4 ){
blob_append_sql(&sql,",\n ");
}else if( cnt>0 ){
blob_append_sql(&sql,",");
}
blob_append_sql(&sql, "(%d)", rid);
cnt++;
}
zX[ii] = c;
zX += ii;
}
if( cnt ) db_exec_sql(blob_sql_text(&sql));
blob_reset(&sql);
fossil_free(zToDel);
}
/*
** COMMAND: test-endpoint
**
** Usage: fossil test-endpoint BASE TAG ?OPTIONS?
**
** Show the first check-in with TAG that is a descendent or ancestor
|
| ︙ | ︙ | |||
1696 1697 1698 1699 1700 1701 1702 | /* ** WEBPAGE: timeline ** ** Query parameters: ** | | | | | | | 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 | /* ** WEBPAGE: timeline ** ** Query parameters: ** ** a=TIMEORTAG Show events after TIMEORTAG. ** b=TIMEORTAG Show events before TIMEORTAG. ** c=TIMEORTAG Show events that happen "circa" TIMEORTAG ** cf=FILEHASH Show events around the time of the first use of ** the file with FILEHASH. ** m=TIMEORTAG Highlight the event at TIMEORTAG, or the closest available ** event if TIMEORTAG is not part of the timeline. If ** the t= or r= is used, the m event is added to the timeline ** if it isn't there already. ** x=LIST Show check-ins in the comma- or space-separated LIST ** in addition to check-ins specified by other parameters. ** sel1=TIMEORTAG Highlight the check-in at TIMEORTAG if it is part of ** the timeline. Similar to m= except TIMEORTAG must ** match a check-in that is already in the timeline. ** sel2=TIMEORTAG Like sel1= but use the secondary highlight. ** n=COUNT Maximum number of events. "all" for no limit ** n1=COUNT Same as "n" but doesn't set the display-preference cookie ** Use "n1=COUNT" for a one-time display change |
| ︙ | ︙ | |||
1730 1731 1732 1733 1734 1735 1736 | ** bt=CHECKIN "Back To". Show ancenstors going back to CHECKIN ** p=CX ... from CX back to time of CHECKIN ** from=CX ... shortest path from CX back to CHECKIN ** ft=CHECKIN "Forward To": Show decendents forward to CHECKIN ** d=CX ... from CX up to the time of CHECKIN ** from=CX ... shortest path from CX up to CHECKIN ** t=TAG Show only check-ins with the given TAG | | | | > > | | > | | | > > > | | | | | | | | > > | | 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 | ** bt=CHECKIN "Back To". Show ancenstors going back to CHECKIN ** p=CX ... from CX back to time of CHECKIN ** from=CX ... shortest path from CX back to CHECKIN ** ft=CHECKIN "Forward To": Show decendents forward to CHECKIN ** d=CX ... from CX up to the time of CHECKIN ** from=CX ... shortest path from CX up to CHECKIN ** t=TAG Show only check-ins with the given TAG ** r=TAG Same as 't=TAG&rel'. Mnemonic: "Related" ** tl=TAGLIST Same as 't=TAGLIST&ms=brlist'. Mnemonic: "Tag List" ** rl=TAGLIST Same as 'r=TAGLIST&ms=brlist'. Mnemonic: "Related List" ** ml=TAGLIST Same as 'tl=TAGLIST&mionly'. Mnemonic: "Merge-in List" ** sl=TAGLIST "Sort List". Draw TAGLIST branches ordered left to right. ** rel Show related check-ins as well as those matching t=TAG ** mionly Show related parents but not related children. ** nowiki Do not show wiki associated with branch or tag ** ms=MATCHSTYLE Set tag name match algorithm. One of "exact", "glob", ** "like", or "regexp". ** u=USER Only show items associated with USER ** y=TYPE 'ci', 'w', 't', 'n', 'e', 'f', or 'all'. ** ss=VIEWSTYLE c: "Compact", v: "Verbose", m: "Modern", j: "Columnar", * x: "Classic". ** advm Use the "Advanced" or "Busy" menu design. ** ng No Graph. ** ncp Omit cherrypick merges ** nd Do not highlight the focus check-in ** nsm Omit the submenu ** nc Omit all graph colors other than highlights ** v Show details of files changed ** vfx Show complete text of forum messages ** f=CHECKIN Family (immediate parents and children) of CHECKIN ** from=CHECKIN Path through common ancestor from... ** to=CHECKIN ... to this ** to2=CHECKIN ... backup name if to= doesn't resolve ** shortest ... show only the shortest path ** rel ... also show related checkins ** bt=PRIOR ... path from CHECKIN back to PRIOR ** ft=LATER ... path from CHECKIN forward to LATER ** me=CHECKIN Most direct path from... ** you=CHECKIN ... to this ** rel ... also show related checkins ** uf=FILE_HASH Show only check-ins that contain the given file version ** All qualifying check-ins are shown unless there is ** also an n= or n1= query parameter. ** chng=GLOBLIST Show only check-ins that involve changes to a file whose ** name matches one of the comma-separate GLOBLIST ** brbg Background color determined by branch name ** ubg Background color determined by user ** deltabg Background color red for delta manifests or green ** for baseline manifests ** namechng Show only check-ins that have filename changes ** forks Show only forks and their children ** cherrypicks Show all cherrypicks ** ym=YYYYMM Show only events for the given year/month ** yw=YYYYWW Show only events for the given week of the given year ** yw=YYYYMMDD Show events for the week that includes the given day ** ymd=YYYYMMDD Show only events on the given day. The use "ymd=now" ** to see all changes for the current week. Add "z" at end ** to divide days at UTC instead of localtime days. ** Use ymd=YYYYMMDD-YYYYMMDD (with optional "z") for a range. ** year=YYYY Show only events on the given year. The use "year=0" ** to see all changes for the current year. ** days=N Show events over the previous N days ** datefmt=N Override the date format: 0=HH:MM, 1=HH:MM:SS, ** 2=YYYY-MM-DD HH:MM:SS, 3=YYMMDD HH:MM, and 4 means "off". ** bisect Show the check-ins that are in the current bisect ** oldestfirst Show events oldest first. ** showid Show RIDs ** showsql Show the SQL used to generate the report ** ** p= and d= can appear individually or together. If either p= or d= ** appear, then u=, y=, a=, and b= are ignored. ** ** If both a= and b= appear then both upper and lower bounds are honored. ** ** When multiple time-related filters are used, e.g. ym, yw, and ymd, |
| ︙ | ︙ | |||
1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 |
int secondaryRid = 0; /* Show secondary highlight */
int disableY = 0; /* Disable type selector on submenu */
int advancedMenu = 0; /* Use the advanced menu design */
char *zPlural; /* Ending for plural forms */
int showCherrypicks = 1; /* True to show cherrypick merges */
int haveParameterN; /* True if n= query parameter present */
int from_to_mode = 0; /* 0: from,to. 1: from,ft 2: from,bt */
url_initialize(&url, "timeline");
cgi_query_parameters_to_url(&url);
(void)P_NoBot("ss")
/* "ss" is processed via the udc but at least one spider likes to
** try to SQL inject via this argument, so let's catch that. */;
/* Set number of rows to display */
z = P("n");
| > > > > > > > | 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 |
int secondaryRid = 0; /* Show secondary highlight */
int disableY = 0; /* Disable type selector on submenu */
int advancedMenu = 0; /* Use the advanced menu design */
char *zPlural; /* Ending for plural forms */
int showCherrypicks = 1; /* True to show cherrypick merges */
int haveParameterN; /* True if n= query parameter present */
int from_to_mode = 0; /* 0: from,to. 1: from,ft 2: from,bt */
int showSql = PB("showsql"); /* True to show the SQL */
Blob allSql; /* Copy of all SQL text */
login_check_credentials();
url_initialize(&url, "timeline");
cgi_query_parameters_to_url(&url);
blob_init(&allSql, 0, 0);
/* The "mionly" query parameter is like "rel", but shows merge-ins only */
if( P("mionly")!=0 ) related = 2;
(void)P_NoBot("ss")
/* "ss" is processed via the udc but at least one spider likes to
** try to SQL inject via this argument, so let's catch that. */;
/* Set number of rows to display */
z = P("n");
|
| ︙ | ︙ | |||
1941 1942 1943 1944 1945 1946 1947 |
/* To view the timeline, must have permission to read project data.
*/
pd_rid = name_choice("dp","dp2",&zDPName);
if( pd_rid ){
p_rid = d_rid = pd_rid;
}
| < | 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 |
/* To view the timeline, must have permission to read project data.
*/
pd_rid = name_choice("dp","dp2",&zDPName);
if( pd_rid ){
p_rid = d_rid = pd_rid;
}
if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum)
|| (bisectLocal && !g.perm.Setup)
){
login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
return;
}
if( !bisectLocal ){
|
| ︙ | ︙ | |||
1982 1983 1984 1985 1986 1987 1988 1989 |
);
}
/* Check for tl=TAGLIST and rl=TAGLIST which are abbreviations for
** t=TAGLIST&ms=brlist and r=TAGLIST&ms=brlist repectively. */
if( zBrName==0 && zTagName==0 ){
const char *z;
if( (z = P("tl"))!=0 ){
| > | < < | | > > > > > > > > > > > > | > | | > < < | < < < < < < < | | 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 |
);
}
/* Check for tl=TAGLIST and rl=TAGLIST which are abbreviations for
** t=TAGLIST&ms=brlist and r=TAGLIST&ms=brlist repectively. */
if( zBrName==0 && zTagName==0 ){
const char *z;
const char *zPattern = 0;
if( (z = P("tl"))!=0 ){
zPattern = zTagName = z;
}else if( (z = P("rl"))!=0 ){
zPattern = zBrName = z;
if( related==0 ) related = 1;
}else if( (z = P("ml"))!=0 ){
zPattern = zBrName = z;
if( related==0 ) related = 2;
}
if( zPattern!=0 && zMatchStyle==0 ){
/* If there was no ms= query parameter, set the match style to
** "glob" if the pattern appears to contain GLOB character, or
** "brlist" if it does not. */
if( strpbrk(zPattern,"*[?") ){
zMatchStyle = "glob";
}else{
zMatchStyle = "brlist";
}
}
}
/* Convert r=TAG to t=TAG&rel in order to populate the UI style widgets. */
if( zBrName ){
cgi_delete_query_parameter("r");
cgi_set_query_parameter("t", zBrName); (void)P("t");
cgi_set_query_parameter("rel", "1");
zTagName = zBrName;
if( related==0 ) related = 1;
zType = "ci";
}
/* Ignore empty tag query strings. */
if( zTagName && !*zTagName ){
zTagName = 0;
}
/* Finish preliminary processing of tag match queries. */
matchStyle = match_style(zMatchStyle, MS_EXACT);
if( zTagName ){
zType = "ci";
if( matchStyle==MS_EXACT ){
/* For exact maching, inhibit links to the selected tag. */
zThisTag = zTagName;
Th_Store("current_checkin", zTagName);
}
/* Display a checkbox to enable/disable display of related check-ins. */
if( advancedMenu ){
style_submenu_checkbox("rel", "Related", 0, 0);
}
/* Construct the tag match expression. */
zTagSql = match_tag_sqlexpr(matchStyle, zTagName, &zMatchDesc, &zError);
}
if( zMark && zMark[0]==0 ){
if( zAfter ) zMark = zAfter;
if( zBefore ) zMark = zBefore;
if( zCirca ) zMark = zCirca;
}
|
| ︙ | ︙ | |||
2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 |
if( PB("deltabg") ){
tmFlags |= TIMELINE_DELTA;
}
if( PB("nc") ){
tmFlags &= ~(TIMELINE_DELTA|TIMELINE_BRCOLOR|TIMELINE_UCOLOR);
tmFlags |= TIMELINE_NOCOLOR;
}
if( zUses!=0 ){
int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses);
if( ufid ){
zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid);
db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)");
compute_uses_file("usesfile", ufid, 0);
zType = "ci";
disableY = 1;
if( !haveParameterN ) nEntry = 0;
}else{
zUses = 0;
}
}
if( renameOnly ){
db_multi_exec(
"CREATE TEMP TABLE rnfile(rid INTEGER PRIMARY KEY);"
"INSERT OR IGNORE INTO rnfile"
| > | | | | | | | | | | | | | | | | 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 |
if( PB("deltabg") ){
tmFlags |= TIMELINE_DELTA;
}
if( PB("nc") ){
tmFlags &= ~(TIMELINE_DELTA|TIMELINE_BRCOLOR|TIMELINE_UCOLOR);
tmFlags |= TIMELINE_NOCOLOR;
}
if( showSql ) db_append_dml_to_blob(&allSql);
if( zUses!=0 ){
int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses);
if( ufid ){
zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid);
db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)");
compute_uses_file("usesfile", ufid, 0);
zType = "ci";
disableY = 1;
if( !haveParameterN ) nEntry = 0;
}else{
zUses = 0;
}
}
if( renameOnly ){
db_multi_exec(
"CREATE TEMP TABLE rnfile(rid INTEGER PRIMARY KEY);"
"INSERT OR IGNORE INTO rnfile"
" SELECT mid FROM mlink WHERE pfnid>0 AND pfnid!=fnid;"
);
disableY = 1;
}
if( forkOnly ){
db_multi_exec(
"CREATE TEMP TABLE rnfork(rid INTEGER PRIMARY KEY);\n"
"INSERT OR IGNORE INTO rnfork(rid)\n"
" SELECT pid FROM plink\n"
" WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==\n"
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
" GROUP BY pid\n"
" HAVING count(*)>1;\n"
"INSERT OR IGNORE INTO rnfork(rid)\n"
" SELECT cid FROM plink\n"
" WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==\n"
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
" GROUP BY cid\n"
" HAVING count(*)>1;\n",
TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH
);
db_multi_exec(
"INSERT OR IGNORE INTO rnfork(rid)\n"
" SELECT cid FROM plink\n"
" WHERE pid IN rnfork\n"
" AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==\n"
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
" UNION\n"
" SELECT pid FROM plink\n"
" WHERE cid IN rnfork\n"
" AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)==\n"
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n",
TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH
);
tmFlags |= TIMELINE_UNHIDE;
zType = "ci";
disableY = 1;
}
if( bisectLocal && cgi_is_loopback(g.zIpAddr) && db_open_local(0) ){
|
| ︙ | ︙ | |||
2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 |
/* If from= and to= are present, display all nodes on a path connecting
** the two */
PathNode *p = 0;
const char *zFrom = 0;
const char *zTo = 0;
Blob ins;
int nNodeOnPath = 0;
if( from_rid && to_rid ){
if( from_to_mode==0 ){
p = path_shortest(from_rid, to_rid, noMerge, 0, 0);
}else if( from_to_mode==1 ){
p = path_shortest(from_rid, to_rid, 0, 1, 0);
}else{
p = path_shortest(to_rid, from_rid, 0, 1, 0);
}
zFrom = P("from");
zTo = zTo2 ? zTo2 : P("to");
}else{
| > > > > > > | > > > > > > > | | > > > | > > > > > > | > | | | | | | | | > > > > > > > > > > > > > | | | | > | 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 |
/* If from= and to= are present, display all nodes on a path connecting
** the two */
PathNode *p = 0;
const char *zFrom = 0;
const char *zTo = 0;
Blob ins;
int nNodeOnPath = 0;
int commonAncs = 0; /* Common ancestors of me_rid and you_rid. */
int earlierRid = 0, laterRid = 0;
if( from_rid && to_rid ){
if( from_to_mode==0 ){
p = path_shortest(from_rid, to_rid, noMerge, 0, 0);
}else if( from_to_mode==1 ){
p = path_shortest(from_rid, to_rid, 0, 1, 0);
earlierRid = commonAncs = from_rid;
laterRid = to_rid;
}else{
p = path_shortest(to_rid, from_rid, 0, 1, 0);
earlierRid = commonAncs = to_rid;
laterRid = from_rid;
}
zFrom = P("from");
zTo = zTo2 ? zTo2 : P("to");
}else{
commonAncs = path_common_ancestor(me_rid, you_rid);
if( commonAncs!=0 ){
p = path_first();
}
if( commonAncs==you_rid ){
zFrom = P("you");
zTo = P("me");
earlierRid = you_rid;
laterRid = me_rid;
}else{
zFrom = P("me");
zTo = P("you");
earlierRid = me_rid;
laterRid = you_rid;
}
}
blob_init(&ins, 0, 0);
db_multi_exec(
"CREATE TEMP TABLE IF NOT EXISTS pathnode(x INTEGER PRIMARY KEY);"
);
if( p ){
int cnt = 4;
blob_init(&ins, 0, 0);
blob_append_sql(&ins, "INSERT INTO pathnode(x) VALUES(%d)", p->rid);
p = p->u.pTo;
while( p ){
if( cnt==8 ){
blob_append_sql(&ins, ",\n (%d)", p->rid);
cnt = 0;
}else{
cnt++;
blob_append_sql(&ins, ",(%d)", p->rid);
}
p = p->u.pTo;
}
}
path_reset();
db_multi_exec("%s", blob_str(&ins)/*safe-for-%s*/);
blob_reset(&ins);
if( related ){
db_multi_exec(
"CREATE TEMP TABLE IF NOT EXISTS related(x INTEGER PRIMARY KEY);"
"INSERT OR IGNORE INTO related(x)"
" SELECT pid FROM plink WHERE cid IN pathnode AND NOT isprim;"
);
if( related==1 ){
db_multi_exec(
"INSERT OR IGNORE INTO related(x)"
" SELECT cid FROM plink WHERE pid IN pathnode;"
);
}
if( showCherrypicks ){
db_multi_exec(
"INSERT OR IGNORE INTO related(x)"
" SELECT parentid FROM cherrypick WHERE childid IN pathnode;"
);
if( related==1 ){
db_multi_exec(
"INSERT OR IGNORE INTO related(x)"
" SELECT childid FROM cherrypick WHERE parentid IN pathnode;"
);
}
}
if( earlierRid && laterRid && commonAncs==earlierRid ){
/* On a query with me=XXX, you=YYY, and rel, omit all nodes that
** are not ancestors of either XXX or YYY, as those nodes tend to
** be extraneous */
db_multi_exec(
"CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
);
compute_ancestors(laterRid, 0, 0, earlierRid);
db_multi_exec(
"DELETE FROM related WHERE x NOT IN ok;"
);
}
db_multi_exec("INSERT OR IGNORE INTO pathnode SELECT x FROM related");
}
add_extra_rids("pathnode",P("x"));
blob_append_sql(&sql, " AND event.objid IN pathnode");
if( zChng && zChng[0] ){
db_multi_exec(
"DELETE FROM pathnode\n"
" WHERE NOT EXISTS(SELECT 1 FROM mlink, filename\n"
" WHERE mlink.mid=x\n"
" AND mlink.fnid=filename.fnid\n"
" AND %s)",
glob_expr("filename.name", zChng)
);
}
tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
db_multi_exec("%s", blob_sql_text(&sql));
if( advancedMenu ){
style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0);
|
| ︙ | ︙ | |||
2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 |
if( p_rid && d_rid ){
if( p_rid!=d_rid ) p_rid = d_rid;
if( !haveParameterN ) nEntry = 10;
}
db_multi_exec(
"CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
);
zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d",
p_rid ? p_rid : d_rid);
zCiName = zDPName;
if( zCiName==0 ) zCiName = zUuid;
blob_append_sql(&sql, " AND event.objid IN ok");
nd = 0;
if( d_rid ){
| > < > > > | | < | | | | | | | | < < < < | 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 |
if( p_rid && d_rid ){
if( p_rid!=d_rid ) p_rid = d_rid;
if( !haveParameterN ) nEntry = 10;
}
db_multi_exec(
"CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
);
add_extra_rids("ok", P("x"));
zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d",
p_rid ? p_rid : d_rid);
zCiName = zDPName;
if( zCiName==0 ) zCiName = zUuid;
blob_append_sql(&sql, " AND event.objid IN ok");
nd = 0;
if( d_rid ){
double rStopTime = 9e99;
zFwdTo = P("ft");
if( zFwdTo ){
double rStartDate = db_double(0.0,
"SELECT mtime FROM event WHERE objid=%d", d_rid);
ridFwdTo = first_checkin_with_tag_after_date(zFwdTo, rStartDate);
if( ridFwdTo==0 ){
ridFwdTo = name_to_typed_rid(zBackTo,"ci");
}
if( ridFwdTo ){
if( !haveParameterN ) nEntry = 0;
rStopTime = db_double(9e99,
"SELECT mtime FROM event WHERE objid=%d", ridFwdTo);
}
}
if( rStopTime<9e99 ){
rStopTime += 5.8e-6; /* Round up by 1/2 second */
}
db_multi_exec(
"WITH RECURSIVE dx(rid,mtime) AS (\n"
" SELECT %d, 0\n"
" UNION\n"
" SELECT plink.cid, plink.mtime FROM dx, plink\n"
" WHERE plink.pid=dx.rid\n"
" AND plink.mtime<=%.*g\n"
" ORDER BY 2\n"
")\n"
"INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d",
d_rid, rStopTime<8e99 ? 17 : 2, rStopTime, nEntry<=0 ? -1 : nEntry+1
);
nd = db_int(0, "SELECT count(*)-1 FROM ok");
if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql));
if( nd>0 || p_rid==0 ){
blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
}
if( useDividers && !selectedRid ) selectedRid = d_rid;
db_multi_exec("DELETE FROM ok");
|
| ︙ | ︙ | |||
2480 2481 2482 2483 2484 2485 2486 |
blob_zero(&cond);
tmFlags |= TIMELINE_FILLGAPS;
if( zChng && *zChng ){
addFileGlobExclusion(zChng, &cond);
tmFlags |= TIMELINE_XMERGE;
}
if( zUses ){
| | | | | | > > | < | < | < | > | > > < | < < > | > | > > < | > | > > | > > > > | | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < > | | > | > > > > | | < | > | > > | > > > > > > > > > > | < > | > | > > < | < > | > | > > < | > | > > | > > > | 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 |
blob_zero(&cond);
tmFlags |= TIMELINE_FILLGAPS;
if( zChng && *zChng ){
addFileGlobExclusion(zChng, &cond);
tmFlags |= TIMELINE_XMERGE;
}
if( zUses ){
blob_append_sql(&cond, " AND event.objid IN usesfile\n");
}
if( renameOnly ){
blob_append_sql(&cond, " AND event.objid IN rnfile\n");
}
if( forkOnly ){
blob_append_sql(&cond, " AND event.objid IN rnfork\n");
}
if( cpOnly && showCherrypicks ){
db_multi_exec(
"CREATE TEMP TABLE IF NOT EXISTS cpnodes(rid INTEGER PRIMARY KEY);"
"INSERT OR IGNORE INTO cpnodes SELECT childid FROM cherrypick;"
"INSERT OR IGNORE INTO cpnodes SELECT parentid FROM cherrypick;"
);
blob_append_sql(&cond, " AND event.objid IN cpnodes\n");
}
if( bisectLocal || zBisect!=0 ){
blob_append_sql(&cond, " AND event.objid IN (SELECT rid FROM bilog)\n");
}
if( zYearMonth ){
char *zNext;
int bZulu = 0;
const char *zTZMod;
zYearMonth = timeline_expand_datetime(zYearMonth, &bZulu);
zYearMonth = mprintf("%.7s", zYearMonth);
if( db_int(0,"SELECT julianday('%q-01') IS NULL", zYearMonth) ){
zYearMonth = db_text(0, "SELECT strftime('%%Y-%%m','now');");
}
zTZMod = (bZulu==0 && fossil_ui_localtime()) ? "utc" : "+00:00";
if( db_int(0,
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
" WHERE blob.rid=event.objid"
" AND mtime>=julianday('%q-01',%Q)%s)",
zYearMonth, zTZMod, blob_sql_text(&cond))
){
zNext = db_text(0, "SELECT strftime('%%Y%%m%q','%q-01','+1 month');",
&"Z"[!bZulu], zYearMonth);
zNewerButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0));
zNewerButtonLabel = "Following month";
fossil_free(zNext);
}
if( db_int(0,
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
" WHERE blob.rid=event.objid"
" AND mtime<julianday('%q-01',%Q)%s)",
zYearMonth, zTZMod, blob_sql_text(&cond))
){
zNext = db_text(0, "SELECT strftime('%%Y%%m%q','%q-01','-1 month');",
&"Z"[!bZulu], zYearMonth);
zOlderButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0));
zOlderButtonLabel = "Previous month";
fossil_free(zNext);
}
blob_append_sql(&cond,
" AND event.mtime>=julianday('%q-01',%Q)"
" AND event.mtime<julianday('%q-01',%Q,'+1 month')\n",
zYearMonth, zTZMod, zYearMonth, zTZMod);
nEntry = -1;
/* Adjust the zYearMonth for the title */
zYearMonth = mprintf("%z-01%s", zYearMonth, &"Z"[!bZulu]);
}
else if( zYearWeek ){
char *z, *zNext;
int bZulu = 0;
const char *zTZMod;
zYearWeek = timeline_expand_datetime(zYearWeek, &bZulu);
z = db_text(0, "SELECT strftime('%%Y-%%W',%Q)", zYearWeek);
if( z && z[0] ){
zYearWeekStart = db_text(0, "SELECT date(%Q,'-6 days','weekday 1')",
zYearWeek);
zYearWeek = z;
}else{
if( strlen(zYearWeek)==7 ){
zYearWeekStart = db_text(0,
"SELECT date('%.4q-01-01','%+d days','weekday 1')",
zYearWeek, atoi(zYearWeek+5)*7-6);
}else{
zYearWeekStart = 0;
}
if( zYearWeekStart==0 || zYearWeekStart[0]==0 ){
zYearWeekStart = db_text(0,
"SELECT date('now','-6 days','weekday 1');");
zYearWeek = db_text(0,
"SELECT strftime('%%Y-%%W','now','-6 days','weekday 1')");
}
}
zTZMod = (bZulu==0 && fossil_ui_localtime()) ? "utc" : "+00:00";
if( db_int(0,
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
" WHERE blob.rid=event.objid"
" AND mtime>=julianday(%Q,%Q)%s)",
zYearWeekStart, zTZMod, blob_sql_text(&cond))
){
zNext = db_text(0, "SELECT strftime('%%Y%%W%q',%Q,'+7 day');",
&"Z"[!bZulu], zYearWeekStart);
zNewerButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0));
zNewerButtonLabel = "Following week";
fossil_free(zNext);
}
if( db_int(0,
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
" WHERE blob.rid=event.objid"
" AND mtime<julianday(%Q,%Q)%s)",
zYearWeekStart, zTZMod, blob_sql_text(&cond))
){
zNext = db_text(0, "SELECT strftime('%%Y%%W%q',%Q,'-7 days');",
&"Z"[!bZulu], zYearWeekStart);
zOlderButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0));
zOlderButtonLabel = "Previous week";
fossil_free(zNext);
}
blob_append_sql(&cond,
" AND event.mtime>=julianday(%Q,%Q)"
" AND event.mtime<julianday(%Q,%Q,'+7 days')\n",
zYearWeekStart, zTZMod, zYearWeekStart, zTZMod);
nEntry = -1;
if( fossil_ui_localtime() && bZulu ){
zYearWeekStart = mprintf("%zZ", zYearWeekStart);
}
}
else if( zDay && timeline_is_datespan(zDay) ){
char *zNext;
char *zStart, *zEnd;
int nDay;
int bZulu = 0;
const char *zTZMod;
zEnd = db_text(0, "SELECT date(%Q)",
timeline_expand_datetime(zDay+9, &bZulu));
zStart = db_text(0, "SELECT date('%.4q-%.2q-%.2q')",
zDay, zDay+4, zDay+6);
nDay = db_int(0, "SELECT julianday(%Q)-julianday(%Q)", zEnd, zStart);
if( nDay==0 ){
zDay = &zDay[9];
goto single_ymd;
}
if( nDay<0 ){
char *zTemp = zEnd;
zEnd = zStart;
zStart = zTemp;
nDay = 1 - nDay;
}else{
nDay += 1;
}
zTZMod = (bZulu==0 && fossil_ui_localtime()) ? "utc" : "+00:00";
if( nDay>0 && db_int(0,
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
" WHERE blob.rid=event.objid"
" AND mtime>=julianday(%Q,'1 day',%Q)%s)",
zEnd, zTZMod, blob_sql_text(&cond))
){
zNext = db_text(0,
"SELECT strftime('%%Y%%m%%d-',%Q,'%d days')||"
"strftime('%%Y%%m%%d%q',%Q,'%d day');",
zStart, nDay, &"Z"[!bZulu], zEnd, nDay);
zNewerButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
zNewerButtonLabel = mprintf("Following %d days", nDay);
fossil_free(zNext);
}
if( nDay>1 && db_int(0,
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
" WHERE blob.rid=event.objid"
" AND mtime<julianday(%Q,'-1 day',%Q)%s)",
zStart, zTZMod, blob_sql_text(&cond))
){
zNext = db_text(0,
"SELECT strftime('%%Y%%m%%d-',%Q,'%d days')||"
"strftime('%%Y%%m%%d%q',%Q,'%d day');",
zStart, -nDay, &"Z"[!bZulu], zEnd, -nDay);
zOlderButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
zOlderButtonLabel = mprintf("Previous %d days", nDay);
fossil_free(zNext);
}
blob_append_sql(&cond,
" AND event.mtime>=julianday(%Q,%Q)"
" AND event.mtime<julianday(%Q,%Q,'+1 day')\n",
zStart, zTZMod, zEnd, zTZMod);
nEntry = -1;
if( fossil_ui_localtime() && bZulu ){
zDay = mprintf("%d days between %zZ and %zZ", nDay, zStart, zEnd);
}else{
zDay = mprintf("%d days between %z and %z", nDay, zStart, zEnd);
}
}
else if( zDay ){
char *zNext;
int bZulu = 0;
const char *zTZMod;
single_ymd:
bZulu = 0;
zDay = timeline_expand_datetime(zDay, &bZulu);
zDay = db_text(0, "SELECT date(%Q)", zDay);
if( zDay==0 || zDay[0]==0 ){
zDay = db_text(0, "SELECT date('now')");
}
zTZMod = (bZulu==0 && fossil_ui_localtime()) ? "utc" : "+00:00";
if( db_int(0,
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
" WHERE blob.rid=event.objid"
" AND mtime>=julianday(%Q,'+1 day',%Q)%s)",
zDay, zTZMod, blob_sql_text(&cond))
){
zNext = db_text(0,"SELECT strftime('%%Y%%m%%d%q',%Q,'+1 day');",
&"Z"[!bZulu], zDay);
zNewerButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
zNewerButtonLabel = "Following day";
fossil_free(zNext);
}
if( db_int(0,
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
" WHERE blob.rid=event.objid"
" AND mtime<julianday(%Q,'-1 day',%Q)%s)",
zDay, zTZMod, blob_sql_text(&cond))
){
zNext = db_text(0,"SELECT strftime('%%Y%%m%%d%q',%Q,'-1 day');",
&"Z"[!bZulu], zDay);
zOlderButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
zOlderButtonLabel = "Previous day";
fossil_free(zNext);
}
blob_append_sql(&cond,
" AND event.mtime>=julianday(%Q,%Q)"
" AND event.mtime<julianday(%Q,%Q,'+1 day')\n",
zDay, zTZMod, zDay, zTZMod);
nEntry = -1;
if( fossil_ui_localtime() && bZulu ){
zDay = mprintf("%zZ", zDay); /* Add Z suffix to day for the title */
}
}
else if( zNDays ){
nDays = atoi(zNDays);
if( nDays<1 ) nDays = 1;
blob_append_sql(&cond, " AND event.mtime>=julianday('now','-%d days') ",
nDays);
nEntry = -1;
|
| ︙ | ︙ | |||
2660 2661 2662 2663 2664 2665 2666 |
blob_append_sql(&cond, " AND %Q=strftime('%%Y',event.mtime) ",
zYear);
nEntry = -1;
}
if( zTagSql ){
db_multi_exec(
"CREATE TEMP TABLE selected_nodes(rid INTEGER PRIMARY KEY);"
| | | > | < < < < < < < < < < < < | < < < < | | | | | | | | | | | | | | | > | > | | 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 |
blob_append_sql(&cond, " AND %Q=strftime('%%Y',event.mtime) ",
zYear);
nEntry = -1;
}
if( zTagSql ){
db_multi_exec(
"CREATE TEMP TABLE selected_nodes(rid INTEGER PRIMARY KEY);"
"INSERT OR IGNORE INTO selected_nodes\n"
" SELECT tagxref.rid FROM tagxref NATURAL JOIN tag\n"
" WHERE tagtype>0\n"
" AND %s", zTagSql/*safe-for-%s*/
);
if( zMark ){
/* If the t=release option is used with m=UUID, then also
** include the UUID check-in in the display list */
int ridMark = name_to_rid(zMark);
db_multi_exec(
"INSERT OR IGNORE INTO selected_nodes(rid) VALUES(%d)", ridMark);
}
add_extra_rids("selected_nodes",P("x"));
if( related==0 ){
blob_append_sql(&cond, " AND blob.rid IN selected_nodes");
}else{
db_multi_exec(
"CREATE TEMP TABLE related_nodes(rid INTEGER PRIMARY KEY);"
"INSERT INTO related_nodes SELECT rid FROM selected_nodes;"
);
blob_append_sql(&cond, " AND blob.rid IN related_nodes");
/* The next two blob_appendf() calls add SQL that causes check-ins that
** are not part of the branch which are parents or children of the
** branch to be included in the report. These related check-ins are
** useful in helping to visualize what has happened on a quiescent
** branch that is infrequently merged with a much more activate branch.
*/
db_multi_exec(
"INSERT OR IGNORE INTO related_nodes\n"
" SELECT pid FROM selected_nodes CROSS JOIN plink\n"
" WHERE selected_nodes.rid=plink.cid;"
);
if( related==1 ){
db_multi_exec(
"INSERT OR IGNORE INTO related_nodes\n"
" SELECT cid FROM selected_nodes CROSS JOIN plink\n"
" WHERE selected_nodes.rid=plink.pid;"
);
if( showCherrypicks ){
db_multi_exec(
"INSERT OR IGNORE INTO related_nodes\n"
" SELECT childid FROM selected_nodes CROSS JOIN cherrypick\n"
" WHERE selected_nodes.rid=cherrypick.parentid;"
);
}
}
if( showCherrypicks ){
db_multi_exec(
"INSERT OR IGNORE INTO related_nodes\n"
" SELECT parentid FROM selected_nodes CROSS JOIN cherrypick\n"
" WHERE selected_nodes.rid=cherrypick.childid;"
);
}
if( (tmFlags & TIMELINE_UNHIDE)==0 ){
db_multi_exec(
"DELETE FROM related_nodes\n"
" WHERE rid IN (SELECT related_nodes.rid\n"
" FROM related_nodes, tagxref\n"
" WHERE tagid=%d AND tagtype>0\n"
" AND tagxref.rid=related_nodes.rid)",
TAG_HIDDEN
);
}
}
}
if( (zType[0]=='w' && !g.perm.RdWiki)
|| (zType[0]=='t' && !g.perm.RdTkt)
|
| ︙ | ︙ | |||
2826 2827 2828 2829 2830 2831 2832 |
rBefore = symbolic_name_to_mtime(zBefore, &zBefore);
rAfter = symbolic_name_to_mtime(zAfter, &zAfter);
rCirca = symbolic_name_to_mtime(zCirca, &zCirca);
blob_append_sql(&sql, "%s", blob_sql_text(&cond));
if( rAfter>0.0 ){
if( rBefore>0.0 ){
blob_append_sql(&sql,
| | | | | < < < | | | 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 |
rBefore = symbolic_name_to_mtime(zBefore, &zBefore);
rAfter = symbolic_name_to_mtime(zAfter, &zAfter);
rCirca = symbolic_name_to_mtime(zCirca, &zCirca);
blob_append_sql(&sql, "%s", blob_sql_text(&cond));
if( rAfter>0.0 ){
if( rBefore>0.0 ){
blob_append_sql(&sql,
" AND event.mtime>=%.17g AND event.mtime<=%.17g\n"
" ORDER BY event.mtime ASC", rAfter-ONE_SECOND, rBefore+ONE_SECOND);
nEntry = -1;
}else{
blob_append_sql(&sql,
" AND event.mtime>=%.17g\n ORDER BY event.mtime ASC",
rAfter-ONE_SECOND);
}
zCirca = 0;
url_add_parameter(&url, "c", 0);
}else if( rBefore>0.0 ){
blob_append_sql(&sql,
" AND event.mtime<=%.17g\n ORDER BY event.mtime DESC",
rBefore+ONE_SECOND);
zCirca = 0;
url_add_parameter(&url, "c", 0);
}else if( rCirca>0.0 ){
Blob sql2;
blob_init(&sql2, blob_sql_text(&sql), -1);
blob_append_sql(&sql2,
" AND event.mtime>=%f\n ORDER BY event.mtime ASC", rCirca);
if( nEntry>0 ){
blob_append_sql(&sql2," LIMIT %d", (nEntry+1)/2);
}
db_multi_exec("%s", blob_sql_text(&sql2));
if( nEntry>0 ){
nEntry -= db_int(0,"select count(*) from timeline");
if( nEntry<=0 ) nEntry = 1;
}
blob_reset(&sql2);
blob_append_sql(&sql,
" AND event.mtime<=%f\n ORDER BY event.mtime DESC",
rCirca
);
if( zMark==0 ) zMark = zCirca;
}else{
blob_append_sql(&sql, " ORDER BY event.mtime DESC");
}
if( nEntry>0 ) blob_append_sql(&sql, " LIMIT %d", nEntry);
db_multi_exec("%s", blob_sql_text(&sql));
n = db_int(0, "SELECT count(*) FROM timeline WHERE etype!='div' /*scan*/");
zPlural = n==1 ? "" : "s";
if( zYearMonth ){
blob_appendf(&desc, "%d %s%s for the month beginning %h",
n, zEType, zPlural, zYearMonth);
}else if( zYearWeek ){
blob_appendf(&desc, "%d %s%s for week %h beginning on %h",
n, zEType, zPlural, zYearWeek, zYearWeekStart);
}else if( zDay ){
blob_appendf(&desc, "%d %s%s occurring on %h", n, zEType, zPlural, zDay);
}else if( zNDays ){
|
| ︙ | ︙ | |||
3007 3008 3009 3010 3011 3012 3013 |
if( advancedMenu ){
style_submenu_entry("t", "Tag Filter:", -8, 0);
style_submenu_multichoice("ms", count(azMatchStyles)/2,azMatchStyles,0);
}
}
blob_zero(&cond);
}
| | > | > | 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 |
if( advancedMenu ){
style_submenu_entry("t", "Tag Filter:", -8, 0);
style_submenu_multichoice("ms", count(azMatchStyles)/2,azMatchStyles,0);
}
}
blob_zero(&cond);
}
if( showSql ){
db_append_dml_to_blob(0);
@ <pre>%h(blob_str(&allSql))</pre>
blob_reset(&allSql);
}
if( search_restrict(SRCH_CKIN)!=0 ){
style_submenu_element("Search", "%R/search?y=c");
}
if( advancedMenu ){
style_submenu_element("Basic", "%s",
url_render(&url, "advm", "0", "udc", "1"));
|
| ︙ | ︙ | |||
3066 3067 3068 3069 3070 3071 3072 |
}
if( zNewerButton ){
@ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\
@ ↑</a>
}
cgi_check_for_malice();
| > > > > > > > > > > > > > > | | > > > > | 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 |
}
if( zNewerButton ){
@ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\
@ ↑</a>
}
cgi_check_for_malice();
{
Matcher *pLeftBranch;
const char *zPattern = P("sl");
if( zPattern!=0 ){
MatchStyle ms;
if( zMatchStyle!=0 ){
ms = matchStyle;
}else{
ms = strpbrk(zPattern,"*[?")!=0 ? MS_GLOB : MS_BRLIST;
}
pLeftBranch = match_create(ms,zPattern);
}else{
pLeftBranch = match_create(matchStyle, zBrName?zBrName:zTagName);
}
www_print_timeline(&q, tmFlags, zThisUser, zThisTag, pLeftBranch,
selectedRid, secondaryRid, 0);
match_free(pLeftBranch);
}
db_finalize(&q);
if( zOlderButton ){
@ %z(chref("button","%s",zOlderButton))%h(zOlderButtonLabel)\
@ ↓</a>
}
document_emit_js(/*handles pikchrs rendered above*/);
blob_reset(&sql);
blob_reset(&desc);
style_finish_page();
}
/*
** Translate a timeline entry into the printable format by
** converting every %-substitutions as follows:
**
|
| ︙ | ︙ | |||
3742 3743 3744 3745 3746 3747 3748 3749 3750 3751 3752 3753 3754 3755 3756 3757 3758 |
void thisdayinhistory_page(void){
static int aYearsAgo[] = { 1, 2, 3, 4, 5, 10, 15, 20, 30, 40, 50, 75, 100 };
const char *zToday;
char *zStartOfProject;
int i;
Stmt q;
char *z;
login_check_credentials();
if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum) ){
login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
return;
}
style_set_current_feature("timeline");
style_header("Today In History");
zToday = (char*)P("today");
if( zToday ){
| > | | 3753 3754 3755 3756 3757 3758 3759 3760 3761 3762 3763 3764 3765 3766 3767 3768 3769 3770 3771 3772 3773 3774 3775 3776 3777 3778 |
void thisdayinhistory_page(void){
static int aYearsAgo[] = { 1, 2, 3, 4, 5, 10, 15, 20, 30, 40, 50, 75, 100 };
const char *zToday;
char *zStartOfProject;
int i;
Stmt q;
char *z;
int bZulu = 0;
login_check_credentials();
if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum) ){
login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
return;
}
style_set_current_feature("timeline");
style_header("Today In History");
zToday = (char*)P("today");
if( zToday ){
zToday = timeline_expand_datetime(zToday, &bZulu);
if( !fossil_isdate(zToday) ) zToday = 0;
}
if( zToday==0 ){
zToday = db_text(0, "SELECT date('now',toLocal())");
}
@ <h1>This Day In History For %h(zToday)</h1>
z = db_text(0, "SELECT date(%Q,'-1 day')", zToday);
|
| ︙ | ︙ |
Changes to src/tkt.c.
| ︙ | ︙ | |||
779 780 781 782 783 784 785 |
Th_Render(zScript);
if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1);
zFullName = db_text(0,
"SELECT tkt_uuid FROM ticket"
" WHERE tkt_uuid GLOB '%q*'", zUuid);
if( zFullName ){
| | | 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 |
Th_Render(zScript);
if( g.thTrace ) Th_Trace("END_TKTVIEW<br>\n", -1);
zFullName = db_text(0,
"SELECT tkt_uuid FROM ticket"
" WHERE tkt_uuid GLOB '%q*'", zUuid);
if( zFullName ){
attachment_list(zFullName, "<h2>Attachments:</h2>", 1);
}
style_finish_page();
}
/*
** TH1 command: append_field FIELD STRING
|
| ︙ | ︙ |
Changes to src/update.c.
| ︙ | ︙ | |||
130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
int nConflict = 0; /* Number of merge conflicts */
int nOverwrite = 0; /* Number of unmanaged files overwritten */
int nUpdate = 0; /* Number of changes of any kind */
int bNosync = 0; /* --nosync. Omit the auto-sync */
int width; /* Width of printed comment lines */
Stmt mtimeXfer; /* Statement to transfer mtimes */
const char *zWidth; /* Width option string value */
if( !internalUpdate ){
undo_capture_command_line();
url_proxy_options();
}
zWidth = find_option("width","W",1);
if( zWidth ){
| > > > | 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
int nConflict = 0; /* Number of merge conflicts */
int nOverwrite = 0; /* Number of unmanaged files overwritten */
int nUpdate = 0; /* Number of changes of any kind */
int bNosync = 0; /* --nosync. Omit the auto-sync */
int width; /* Width of printed comment lines */
Stmt mtimeXfer; /* Statement to transfer mtimes */
const char *zWidth; /* Width option string value */
const char *zCurBrName; /* Current branch name */
const char *zNewBrName; /* New branch name */
const char *zBrChgMsg = ""; /* Message to display if branch changes */
if( !internalUpdate ){
undo_capture_command_line();
url_proxy_options();
}
zWidth = find_option("width","W",1);
if( zWidth ){
|
| ︙ | ︙ | |||
161 162 163 164 165 166 167 168 169 170 171 172 173 174 |
bNosync = find_option("nosync",0,0)!=0;
/* We should be done with options.. */
verify_all_options();
db_must_be_within_tree();
vid = db_lget_int("checkout", 0);
user_select();
if( !dryRunFlag && !internalUpdate && !bNosync ){
if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag, 1, "update") ){
fossil_fatal("update abandoned due to sync failure");
}
}
| > | 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
bNosync = find_option("nosync",0,0)!=0;
/* We should be done with options.. */
verify_all_options();
db_must_be_within_tree();
vid = db_lget_int("checkout", 0);
zCurBrName = branch_of_rid(vid);
user_select();
if( !dryRunFlag && !internalUpdate && !bNosync ){
if( autosync_loop(SYNC_PULL + SYNC_VERBOSE*verboseFlag, 1, "update") ){
fossil_fatal("update abandoned due to sync failure");
}
}
|
| ︙ | ︙ | |||
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 |
db_prepare(&mtimeXfer,
"UPDATE vfile SET mtime=(SELECT mtime FROM vfile WHERE id=:idv)"
" WHERE id=:idt"
);
assert( g.zLocalRoot!=0 );
assert( strlen(g.zLocalRoot)>0 );
assert( g.zLocalRoot[strlen(g.zLocalRoot)-1]=='/' );
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0); /* The filename from root */
int idv = db_column_int(&q, 1); /* VFILE entry for current */
int ridv = db_column_int(&q, 2); /* RecordID for current */
int idt = db_column_int(&q, 3); /* VFILE entry for target */
int ridt = db_column_int(&q, 4); /* RecordID for target */
int chnged = db_column_int(&q, 5); /* Current is edited */
const char *zNewName = db_column_text(&q,6);/* New filename */
int isexe = db_column_int(&q, 7); /* EXE perm for new file */
int islinkv = db_column_int(&q, 8); /* Is current file is a link */
int islinkt = db_column_int(&q, 9); /* Is target file is a link */
int deleted = db_column_int(&q, 10); /* Marked for deletion */
char *zFullPath; /* Full pathname of the file */
char *zFullNewPath; /* Full pathname of dest */
char nameChng; /* True if the name changed */
zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
nameChng = fossil_strcmp(zName, zNewName);
nUpdate++;
if( deleted ){
db_multi_exec("UPDATE vfile SET deleted=1 WHERE id=%d", idt);
}
if( idv>0 && ridv==0 && idt>0 && ridt>0 ){
/* Conflict. This file has been added to the current check-out
** but also exists in the target check-out. Use the current version.
*/
fossil_print("CONFLICT %s\n", zName);
nConflict++;
}else if( idt>0 && idv==0 ){
/* File added in the target. */
if( file_isfile_or_link(zFullPath) ){
/* Name of backup file with Original content */
char *zOrig = file_newname(zFullPath, "original", 1);
/* Backup previously unanaged file before to be overwritten */
file_copy(zFullPath, zOrig);
fossil_free(zOrig);
fossil_print("ADD %s - overwrites an unmanaged file", zName);
if( !dryRunFlag ) fossil_print(", original copy backed up locally");
fossil_print("\n");
nOverwrite++;
}else{
fossil_print("ADD %s\n", zName);
}
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
}else if( idt>0 && idv>0 && ridt!=ridv && (chnged==0 || deleted) ){
/* The file is unedited. Change it to the target version */
if( deleted ){
fossil_print("UPDATE %s - change to unmanaged file\n", zName);
}else{
fossil_print("UPDATE %s\n", zName);
}
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
}else if( idt>0 && idv>0 && !deleted && file_size(zFullPath, RepoFILE)<0 ){
/* The file missing from the local check-out. Restore it to the
** version that appears in the target. */
fossil_print("UPDATE %s\n", zName);
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
}else if( idt==0 && idv>0 ){
if( ridv==0 ){
/* Added in current check-out. Continue to hold the file as
** as an addition */
db_multi_exec("UPDATE vfile SET vid=%d WHERE id=%d", tid, idv);
}else if( chnged ){
/* Edited locally but deleted from the target. Do not track the
** file but keep the edited version around. */
fossil_print("CONFLICT %s - edited locally but deleted by update\n",
zName);
nConflict++;
}else{
fossil_print("REMOVE %s\n", zName);
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
if( !dryRunFlag ){
char *zDir;
file_delete(zFullPath);
zDir = file_dirname(zName);
while( zDir!=0 ){
char *zNext;
db_multi_exec("INSERT OR IGNORE INTO dir_to_delete(name)"
"VALUES(%Q)", zDir);
zNext = db_changes() ? file_dirname(zDir) : 0;
fossil_free(zDir);
zDir = zNext;
}
}
}
}else if( idt>0 && idv>0 && ridt!=ridv && chnged ){
| > > > > > > > > > > > > > > > > > < < < > > > > > > > > > > > > < < | | | > > > > > > > > > > > > > > > > > > > > > > > | | | | 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 |
db_prepare(&mtimeXfer,
"UPDATE vfile SET mtime=(SELECT mtime FROM vfile WHERE id=:idv)"
" WHERE id=:idt"
);
assert( g.zLocalRoot!=0 );
assert( strlen(g.zLocalRoot)>0 );
assert( g.zLocalRoot[strlen(g.zLocalRoot)-1]=='/' );
merge_info_init();
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0); /* The filename from root */
int idv = db_column_int(&q, 1); /* VFILE entry for current */
int ridv = db_column_int(&q, 2); /* RecordID for current */
int idt = db_column_int(&q, 3); /* VFILE entry for target */
int ridt = db_column_int(&q, 4); /* RecordID for target */
int chnged = db_column_int(&q, 5); /* Current is edited */
const char *zNewName = db_column_text(&q,6);/* New filename */
int isexe = db_column_int(&q, 7); /* EXE perm for new file */
int islinkv = db_column_int(&q, 8); /* Is current file is a link */
int islinkt = db_column_int(&q, 9); /* Is target file is a link */
int deleted = db_column_int(&q, 10); /* Marked for deletion */
char *zFullPath; /* Full pathname of the file */
char *zFullNewPath; /* Full pathname of dest */
char nameChng; /* True if the name changed */
const char *zOp = 0; /* Type of change. */
i64 sz = 0; /* Size of the file */
int nc = 0; /* Number of conflicts */
const char *zErrMsg = 0; /* Error message */
zFullPath = mprintf("%s%s", g.zLocalRoot, zName);
zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
sz = file_size(zFullNewPath, ExtFILE);
nameChng = fossil_strcmp(zName, zNewName);
nUpdate++;
if( deleted ){
db_multi_exec("UPDATE vfile SET deleted=1 WHERE id=%d", idt);
}
if( idv>0 && ridv==0 && idt>0 && ridt>0 ){
/* Conflict. This file has been added to the current check-out
** but also exists in the target check-out. Use the current version.
*/
fossil_print("CONFLICT %s\n", zName);
nConflict++;
zOp = "CONFLICT";
nc = 1;
zErrMsg = "duplicate file";
}else if( idt>0 && idv==0 ){
/* File added in the target. */
if( file_isfile_or_link(zFullPath) ){
/* Name of backup file with Original content */
char *zOrig = file_newname(zFullPath, "original", 1);
/* Backup previously unanaged file before to be overwritten */
file_copy(zFullPath, zOrig);
fossil_free(zOrig);
fossil_print("ADD %s - overwrites an unmanaged file", zName);
if( !dryRunFlag ) fossil_print(", original copy backed up locally");
fossil_print("\n");
nOverwrite++;
nc = 1;
zOp = "CONFLICT";
zErrMsg = "new file overwrites unmanaged file";
}else{
fossil_print("ADD %s\n", zName);
}
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
}else if( idt>0 && idv>0 && ridt!=ridv && (chnged==0 || deleted) ){
/* The file is unedited. Change it to the target version */
if( deleted ){
fossil_print("UPDATE %s - change to unmanaged file\n", zName);
}else{
fossil_print("UPDATE %s\n", zName);
}
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
zOp = "UPDATE";
}else if( idt>0 && idv>0 && !deleted && file_size(zFullPath, RepoFILE)<0 ){
/* The file missing from the local check-out. Restore it to the
** version that appears in the target. */
fossil_print("UPDATE %s\n", zName);
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
if( !dryRunFlag ) vfile_to_disk(0, idt, 0, 0);
zOp = "UPDATE";
}else if( idt==0 && idv>0 ){
if( ridv==0 ){
/* Added in current check-out. Continue to hold the file as
** as an addition */
db_multi_exec("UPDATE vfile SET vid=%d WHERE id=%d", tid, idv);
}else if( chnged ){
/* Edited locally but deleted from the target. Do not track the
** file but keep the edited version around. */
fossil_print("CONFLICT %s - edited locally but deleted by update\n",
zName);
zOp = "CONFLICT";
zErrMsg = "edited locally but deleted by update";
nc = 1;
nConflict++;
}else{
fossil_print("REMOVE %s\n", zName);
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
if( !dryRunFlag ){
char *zDir;
file_delete(zFullPath);
zDir = file_dirname(zName);
while( zDir!=0 ){
char *zNext;
db_multi_exec("INSERT OR IGNORE INTO dir_to_delete(name)"
"VALUES(%Q)", zDir);
zNext = db_changes() ? file_dirname(zDir) : 0;
fossil_free(zDir);
zDir = zNext;
}
}
}
}else if( idt>0 && idv>0 && ridt!=ridv && chnged ){
if( nameChng ){
fossil_print("MERGE %s -> %s\n", zName, zNewName);
}else{
fossil_print("MERGE %s\n", zName);
}
if( islinkv || islinkt ){
fossil_print("***** Cannot merge symlink %s\n", zNewName);
zOp = "CONFLICT";
nConflict++;
}else{
/* Merge the changes in the current tree into the target version */
Blob r, t, v;
int rc;
unsigned mergeFlags = dryRunFlag ? MERGE_DRYRUN : 0;
if(keepMergeFlag!=0) mergeFlags |= MERGE_KEEP_FILES;
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
content_get(ridt, &t);
content_get(ridv, &v);
rc = merge_3way(&v, zFullPath, &t, &r, mergeFlags);
if( rc>=0 ){
if( !dryRunFlag ){
blob_write_to_file(&r, zFullNewPath);
file_setexe(zFullNewPath, isexe);
}
if( rc>0 ){
nc = rc;
zOp = "CONFLICT";
zErrMsg = "merge conflicts";
fossil_print("***** %d merge conflicts in %s\n", rc, zNewName);
nConflict++;
}else{
zOp = "MERGE";
}
}else{
if( !dryRunFlag ){
if( !keepMergeFlag ){
/* Name of backup file with Original content */
char *zOrig = file_newname(zFullPath, "original", 1);
/* Backup non-mergeable binary file when --keep-merge-files is
not specified */
file_copy(zFullPath, zOrig);
fossil_free(zOrig);
}
blob_write_to_file(&t, zFullNewPath);
file_setexe(zFullNewPath, isexe);
}
fossil_print("***** Cannot merge binary file %s", zNewName);
if( !dryRunFlag ){
fossil_print(", original copy backed up locally");
}
fossil_print("\n");
nConflict++;
zOp = "ERROR";
zErrMsg = "cannot merge binary file";
nc = 1;
}
blob_reset(&v);
blob_reset(&t);
blob_reset(&r);
}
if( nameChng && !dryRunFlag ) file_delete(zFullPath);
}else{
nUpdate--;
if( chnged ){
if( verboseFlag ) fossil_print("EDITED %s\n", zName);
}else{
db_bind_int(&mtimeXfer, ":idv", idv);
db_bind_int(&mtimeXfer, ":idt", idt);
db_step(&mtimeXfer);
db_reset(&mtimeXfer);
if( verboseFlag ) fossil_print("UNCHANGED %s\n", zName);
}
}
if( zOp!=0 ){
db_multi_exec(
"INSERT INTO mergestat(op,fnp,ridp,fn,ridv,sz,fnm,ridm,fnr,nc,msg)"
"VALUES(%Q,%Q,%d,%Q,NULL,%lld,%Q,%d,%Q,%d,%Q)",
/* op */ zOp,
/* fnp */ zName,
/* ridp */ ridv,
/* fn */ zNewName,
/* sz */ sz,
/* fnm */ zName,
/* ridm */ ridt,
/* fnr */ zNewName,
/* nc */ nc,
/* msg */ zErrMsg
);
}
free(zFullPath);
free(zFullNewPath);
}
db_finalize(&q);
db_finalize(&mtimeXfer);
fossil_print("%.79c\n",'-');
zNewBrName = branch_of_rid(tid);
if( g.argc<3 && fossil_strcmp(zCurBrName, zNewBrName)!=0 ){
zBrChgMsg = mprintf(" Branch changed from %s to %s.",
zCurBrName, zNewBrName);
}
if( nUpdate==0 ){
show_common_info(tid, "checkout:", 1, 0);
fossil_print("%-13s None. Already up-to-date.%s\n", "changes:", zBrChgMsg);
}else{
fossil_print("%-13s %.40s %s\n", "updated-from:", rid_to_uuid(vid),
db_text("", "SELECT datetime(mtime) || ' UTC' FROM event "
" WHERE objid=%d", vid));
show_common_info(tid, "updated-to:", 1, 0);
fossil_print("%-13s %d file%s modified.%s\n", "changes:",
nUpdate, nUpdate>1 ? "s" : "", zBrChgMsg);
}
/* Report on conflicts
*/
if( !dryRunFlag ){
Stmt q;
int nMerge = 0;
|
| ︙ | ︙ | |||
800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 |
**
** Revert all files if no file name is provided.
**
** If a file is reverted accidentally, it can be restored using
** the "fossil undo" command.
**
** Options:
** -r|--revision VERSION Revert given FILE(s) back to given
** VERSION
**
** See also: [[redo]], [[undo]], [[checkout]], [[update]]
*/
void revert_cmd(void){
Manifest *pCoManifest; /* Manifest of current check-out */
Manifest *pRvManifest; /* Manifest of selected revert version */
ManifestFile *pCoFile; /* File within current check-out manifest */
ManifestFile *pRvFile; /* File within revert version manifest */
const char *zFile; /* Filename relative to check-out root */
const char *zRevision; /* Selected revert version, NULL if current */
Blob record = BLOB_INITIALIZER; /* Contents of each reverted file */
int i;
Stmt q;
int revertAll = 0;
int revisionOptNotSupported = 0;
undo_capture_command_line();
zRevision = find_option("revision", "r", 1);
verify_all_options();
if( g.argc<2 ){
usage("?OPTIONS? [FILE] ...");
}
if( zRevision && g.argc<3 ){
fossil_fatal("directories or the entire tree can only be reverted"
" back to current version");
}
db_must_be_within_tree();
/* Get manifests of revert version and (if different) current check-out. */
pRvManifest = historical_manifest(zRevision);
pCoManifest = zRevision ? historical_manifest(0) : 0;
db_begin_transaction();
| > > > > | > > > | 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 |
**
** Revert all files if no file name is provided.
**
** If a file is reverted accidentally, it can be restored using
** the "fossil undo" command.
**
** Options:
** --noundo Do not record changes in the undo/redo log.
** -r|--revision VERSION Revert given FILE(s) back to given
** VERSION
**
** See also: [[redo]], [[undo]], [[checkout]], [[update]]
*/
void revert_cmd(void){
Manifest *pCoManifest; /* Manifest of current check-out */
Manifest *pRvManifest; /* Manifest of selected revert version */
ManifestFile *pCoFile; /* File within current check-out manifest */
ManifestFile *pRvFile; /* File within revert version manifest */
const char *zFile; /* Filename relative to check-out root */
const char *zRevision; /* Selected revert version, NULL if current */
Blob record = BLOB_INITIALIZER; /* Contents of each reverted file */
int useUndo = 1; /* True to record changes in UNDO */
int i;
Stmt q;
int revertAll = 0;
int revisionOptNotSupported = 0;
undo_capture_command_line();
zRevision = find_option("revision", "r", 1);
useUndo = find_option("noundo", 0, 0)==0;
verify_all_options();
if( g.argc<2 ){
usage("?OPTIONS? [FILE] ...");
}
if( zRevision && g.argc<3 ){
fossil_fatal("directories or the entire tree can only be reverted"
" back to current version");
}
db_must_be_within_tree();
/* Get manifests of revert version and (if different) current check-out. */
pRvManifest = historical_manifest(zRevision);
pCoManifest = zRevision ? historical_manifest(0) : 0;
db_begin_transaction();
if( useUndo ){
undo_begin();
}else{
undo_reset();
}
db_multi_exec("CREATE TEMP TABLE torevert(name UNIQUE);");
if( g.argc>2 ){
for(i=2; i<g.argc; i++){
Blob fname;
zFile = mprintf("%/", g.argv[i]);
blob_zero(&fname);
|
| ︙ | ︙ | |||
933 934 935 936 937 938 939 |
zFull = mprintf("%/%/", g.zLocalRoot, zFile);
pRvFile = pRvManifest? manifest_file_find(pRvManifest, zFile) : 0;
if( !pRvFile ){
if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q OR origname=%Q",
zFile, zFile)==0 ){
fossil_print("UNMANAGE %s\n", zFile);
}else{
| | | 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 |
zFull = mprintf("%/%/", g.zLocalRoot, zFile);
pRvFile = pRvManifest? manifest_file_find(pRvManifest, zFile) : 0;
if( !pRvFile ){
if( db_int(0, "SELECT rid FROM vfile WHERE pathname=%Q OR origname=%Q",
zFile, zFile)==0 ){
fossil_print("UNMANAGE %s\n", zFile);
}else{
if( useUndo ) undo_save(zFile);
file_delete(zFull);
fossil_print("DELETE %s\n", zFile);
}
db_multi_exec(
"UPDATE OR REPLACE vfile"
" SET pathname=origname, origname=NULL"
" WHERE pathname=%Q AND origname!=pathname;"
|
| ︙ | ︙ | |||
960 961 962 963 964 965 966 |
rvChnged = manifest_file_mperm(pRvFile)!=rvPerm
|| fossil_strcmp(pRvFile->zUuid, pCoFile->zUuid)!=0;
}
/* Get contents of reverted-to file. */
content_get(fast_uuid_to_rid(pRvFile->zUuid), &record);
| | | 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 |
rvChnged = manifest_file_mperm(pRvFile)!=rvPerm
|| fossil_strcmp(pRvFile->zUuid, pCoFile->zUuid)!=0;
}
/* Get contents of reverted-to file. */
content_get(fast_uuid_to_rid(pRvFile->zUuid), &record);
if( useUndo ) undo_save(zFile);
if( file_size(zFull, RepoFILE)>=0
&& (rvPerm==PERM_LNK || file_islink(0))
){
file_delete(zFull);
}
if( rvPerm==PERM_LNK ){
symlink_create(blob_str(&record), zFull);
|
| ︙ | ︙ | |||
986 987 988 989 990 991 992 |
mtime, rvChnged, rvPerm==PERM_EXE, rvPerm==PERM_LNK, zFile, zFile
);
}
blob_reset(&record);
free(zFull);
}
db_finalize(&q);
| | | 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 |
mtime, rvChnged, rvPerm==PERM_EXE, rvPerm==PERM_LNK, zFile, zFile
);
}
blob_reset(&record);
free(zFull);
}
db_finalize(&q);
if( useUndo) undo_finish();
db_end_transaction(0);
/* Deallocate parsed manifest structures. */
manifest_destroy(pRvManifest);
manifest_destroy(pCoManifest);
}
|
Changes to src/wiki.c.
| ︙ | ︙ | |||
620 621 622 623 624 625 626 |
blob_init(&wiki, zBody, -1);
safe_html_context(DOCSRC_WIKI);
wiki_render_by_mimetype(&wiki, zMimetype);
blob_reset(&wiki);
}
manifest_destroy(pWiki);
if( !isPopup ){
| | | | | 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 |
blob_init(&wiki, zBody, -1);
safe_html_context(DOCSRC_WIKI);
wiki_render_by_mimetype(&wiki, zMimetype);
blob_reset(&wiki);
}
manifest_destroy(pWiki);
if( !isPopup ){
char * zLabel = mprintf("<h2><a href='%R/attachlist?page=%T'>"
"Attachments</a>:</h2>",
zPageName);
attachment_list(zPageName, zLabel, 1);
fossil_free(zLabel);
document_emit_js(/*for optional pikchr support*/);
style_finish_page();
}
}
/*
|
| ︙ | ︙ | |||
1329 1330 1331 1332 1333 1334 1335 |
/* Status bar */
CX("<div id='fossil-status-bar' "
"title='Status message area. Double-click to clear them.'>"
"Status messages will go here.</div>\n"
/* will be moved into the tab container via JS */);
| | | 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 |
/* Status bar */
CX("<div id='fossil-status-bar' "
"title='Status message area. Double-click to clear them.'>"
"Status messages will go here.</div>\n"
/* will be moved into the tab container via JS */);
CX("<div id='wikiedit-edit-status'>"
"<span class='name'></span>"
"<span class='links'></span>"
"</div>");
/* Main tab container... */
CX("<div id='wikiedit-tabs' class='tab-container'>Loading...</div>");
/* The .hidden class on the following tab elements is to help lessen
|
| ︙ | ︙ |
Changes to src/winfile.c.
| ︙ | ︙ | |||
449 450 451 452 453 454 455 456 |
}
zBuf[j] = chSep; /* Undo working buffer truncation. */
i = j;
}
fossil_free(zBuf);
return zRes;
}
#endif /* _WIN32 -- This code is for win32 only */
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 |
}
zBuf[j] = chSep; /* Undo working buffer truncation. */
i = j;
}
fossil_free(zBuf);
return zRes;
}
/* Return the unique identifier (UID) for a file, made up of the file identifier
** (equal to "inode" for Unix-style file systems) plus the volume serial number.
** Call the GetFileInformationByHandleEx() function on Windows Vista, and resort
** to the GetFileInformationByHandle() function on Windows XP. The result string
** is allocated by mprintf(), or NULL on failure.
*/
char *win32_file_id(
const char *zFileName
){
static FARPROC fnGetFileInformationByHandleEx;
static int loaded_fnGetFileInformationByHandleEx;
wchar_t *wzFileName = fossil_utf8_to_path(zFileName,0);
HANDLE hFile;
char *zFileId = 0;
hFile = CreateFileW(
wzFileName,
0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL);
if( hFile!=INVALID_HANDLE_VALUE ){
BY_HANDLE_FILE_INFORMATION fi;
struct { /* FILE_ID_INFO from <winbase.h> */
u64 VolumeSerialNumber;
unsigned char FileId[16];
} fi2;
if( !loaded_fnGetFileInformationByHandleEx ){
fnGetFileInformationByHandleEx = GetProcAddress(
GetModuleHandleA("kernel32"),"GetFileInformationByHandleEx");
loaded_fnGetFileInformationByHandleEx = 1;
}
if( fnGetFileInformationByHandleEx ){
if( fnGetFileInformationByHandleEx(
hFile,/*FileIdInfo*/0x12,&fi2,sizeof(fi2)) ){
zFileId = mprintf(
"%016llx/"
"%02x%02x%02x%02x%02x%02x%02x%02x"
"%02x%02x%02x%02x%02x%02x%02x%02x",
fi2.VolumeSerialNumber,
fi2.FileId[15], fi2.FileId[14],
fi2.FileId[13], fi2.FileId[12],
fi2.FileId[11], fi2.FileId[10],
fi2.FileId[9], fi2.FileId[8],
fi2.FileId[7], fi2.FileId[6],
fi2.FileId[5], fi2.FileId[4],
fi2.FileId[3], fi2.FileId[2],
fi2.FileId[1], fi2.FileId[0]);
}
}
if( zFileId==0 ){
if( GetFileInformationByHandle(hFile,&fi) ){
ULARGE_INTEGER FileId = {
/*.LowPart = */ fi.nFileIndexLow,
/*.HighPart = */ fi.nFileIndexHigh
};
zFileId = mprintf(
"%08x/%016llx",
fi.dwVolumeSerialNumber,(u64)FileId.QuadPart);
}
}
CloseHandle(hFile);
}
fossil_path_free(wzFileName);
return zFileId;
}
#endif /* _WIN32 -- This code is for win32 only */
|
Changes to src/xfer.c.
| ︙ | ︙ | |||
1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 |
pXfer->resync = db_column_int(&q, 1)-1;
}
}
db_finalize(&q);
if( cnt==0 ) pXfer->resync = 0;
return cnt;
}
/*
** Send an igot message for every artifact.
*/
static void send_all(Xfer *pXfer){
Stmt q;
db_prepare(&q,
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 |
pXfer->resync = db_column_int(&q, 1)-1;
}
}
db_finalize(&q);
if( cnt==0 ) pXfer->resync = 0;
return cnt;
}
/*
** Send an igot message for every cluster artifact that is not a phantom,
** is not shunned, is not private, and that is not in the UNCLUSTERED table.
** Return the number of cards sent.
*/
static int send_all_clusters(Xfer *pXfer){
Stmt q;
int cnt = 0;
const char *zExtra;
if( db_table_exists("temp","onremote") ){
zExtra = " AND NOT EXISTS(SELECT 1 FROM onremote WHERE rid=blob.rid)";
}else{
zExtra = "";
}
db_prepare(&q,
"SELECT uuid"
" FROM tagxref JOIN blob ON tagxref.rid=blob.rid AND tagxref.tagid=%d"
" WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
" AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)"
" AND NOT EXISTS(SELECT 1 FROM unclustered WHERE rid=blob.rid)"
" AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)%s",
TAG_CLUSTER, zExtra /*safe-for-%s*/
);
while( db_step(&q)==SQLITE_ROW ){
if( cnt==0 ) blob_appendf(pXfer->pOut, "# sending-clusters\n");
blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
cnt++;
}
db_finalize(&q);
if( cnt ) blob_appendf(pXfer->pOut, "# end-of-clusters\n");
return cnt;
}
/*
** Send an igot message for every artifact.
*/
static void send_all(Xfer *pXfer){
Stmt q;
db_prepare(&q,
|
| ︙ | ︙ | |||
1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 |
/* pragma req-links
**
** The client sends this message to the server to ask the server
** to tell it about alternative repositories in the reply.
*/
if( blob_eq(&xfer.aToken[1], "req-links") ){
bSendLinks = 1;
}
}else
/* Unknown message
*/
{
| > > > > > > > > > | 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 |
/* pragma req-links
**
** The client sends this message to the server to ask the server
** to tell it about alternative repositories in the reply.
*/
if( blob_eq(&xfer.aToken[1], "req-links") ){
bSendLinks = 1;
}else
/* pragma req-clusters
**
** This pragma requests that the server send igot cards for every
** cluster artifact that it knows about.
*/
if( blob_eq(&xfer.aToken[1], "req-clusters") ){
send_all_clusters(&xfer);
}
}else
/* Unknown message
*/
{
|
| ︙ | ︙ | |||
1979 1980 1981 1982 1983 1984 1985 |
){
int go = 1; /* Loop until zero */
int nCardSent = 0; /* Number of cards sent */
int nCardRcvd = 0; /* Number of cards received */
int nCycle = 0; /* Number of round trips to the server */
int size; /* Size of a config value or uvfile */
int origConfigRcvMask; /* Original value of configRcvMask */
| | | 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 |
){
int go = 1; /* Loop until zero */
int nCardSent = 0; /* Number of cards sent */
int nCardRcvd = 0; /* Number of cards received */
int nCycle = 0; /* Number of round trips to the server */
int size; /* Size of a config value or uvfile */
int origConfigRcvMask; /* Original value of configRcvMask */
int nFileRecv = 0; /* Number of files received */
int mxPhantomReq = 200; /* Max number of phantoms to request per comm */
const char *zCookie; /* Server cookie */
i64 nUncSent, nUncRcvd; /* Bytes sent and received (before compression) */
i64 nSent, nRcvd; /* Bytes sent and received (after compression) */
int cloneSeqno = 1; /* Sequence number for clones */
Blob send; /* Text we are sending to the server */
Blob recv; /* Reply we got back from the server */
|
| ︙ | ︙ | |||
2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 | const char *zOpType = 0;/* Push, Pull, Sync, Clone */ double rSkew = 0.0; /* Maximum time skew */ int uvHashSent = 0; /* The "pragma uv-hash" message has been sent */ int uvDoPush = 0; /* Generate uvfile messages to send to server */ int uvPullOnly = 0; /* 1: pull-only. 2: pull-only warning issued */ int nUvGimmeSent = 0; /* Number of uvgimme cards sent on this cycle */ int nUvFileRcvd = 0; /* Number of uvfile cards received on this cycle */ sqlite3_int64 mtime; /* Modification time on a UV file */ int autopushFailed = 0; /* Autopush following commit failed if true */ const char *zCkinLock; /* Name of check-in to lock. NULL for none */ const char *zClientId; /* A unique identifier for this check-out */ unsigned int mHttpFlags;/* Flags for the http_exchange() subsystem */ const int bOutIsTty = fossil_isatty(fossil_fileno(stdout)); | > | 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 | const char *zOpType = 0;/* Push, Pull, Sync, Clone */ double rSkew = 0.0; /* Maximum time skew */ int uvHashSent = 0; /* The "pragma uv-hash" message has been sent */ int uvDoPush = 0; /* Generate uvfile messages to send to server */ int uvPullOnly = 0; /* 1: pull-only. 2: pull-only warning issued */ int nUvGimmeSent = 0; /* Number of uvgimme cards sent on this cycle */ int nUvFileRcvd = 0; /* Number of uvfile cards received on this cycle */ int nGimmeRcvd = 0; /* Number of gimme cards recevied on the prev cycle */ sqlite3_int64 mtime; /* Modification time on a UV file */ int autopushFailed = 0; /* Autopush following commit failed if true */ const char *zCkinLock; /* Name of check-in to lock. NULL for none */ const char *zClientId; /* A unique identifier for this check-out */ unsigned int mHttpFlags;/* Flags for the http_exchange() subsystem */ const int bOutIsTty = fossil_isatty(fossil_fileno(stdout)); |
| ︙ | ︙ | |||
2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 |
/* Client sends gimme cards for phantoms
*/
if( (syncFlags & SYNC_PULL)!=0
|| ((syncFlags & SYNC_CLONE)!=0 && cloneSeqno==1)
){
request_phantoms(&xfer, mxPhantomReq);
}
if( syncFlags & SYNC_PUSH ){
send_unsent(&xfer);
nCardSent += send_unclustered(&xfer);
if( syncFlags & SYNC_PRIVATE ) send_private(&xfer);
}
/* Client sends configuration parameter requests. On a clone, delay sending
** this until the second cycle since the login card might fail on
** the first cycle.
*/
if( configRcvMask && ((syncFlags & SYNC_CLONE)==0 || nCycle>0) ){
| > > > > > > | 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 |
/* Client sends gimme cards for phantoms
*/
if( (syncFlags & SYNC_PULL)!=0
|| ((syncFlags & SYNC_CLONE)!=0 && cloneSeqno==1)
){
request_phantoms(&xfer, mxPhantomReq);
if( xfer.nGimmeSent>0 && nCycle==2 && (syncFlags & SYNC_PULL)!=0 ){
blob_appendf(&send, "pragma req-clusters\n");
}
}
if( syncFlags & SYNC_PUSH ){
send_unsent(&xfer);
nCardSent += send_unclustered(&xfer);
if( syncFlags & SYNC_PRIVATE ) send_private(&xfer);
if( nGimmeRcvd>0 && nCycle==2 ){
send_all_clusters(&xfer);
}
}
/* Client sends configuration parameter requests. On a clone, delay sending
** this until the second cycle since the login card might fail on
** the first cycle.
*/
if( configRcvMask && ((syncFlags & SYNC_CLONE)==0 || nCycle>0) ){
|
| ︙ | ︙ | |||
2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 |
if( syncFlags & SYNC_PUSH ){
blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
nCardSent++;
}
go = 0;
nUvGimmeSent = 0;
nUvFileRcvd = 0;
nPriorArtifact = nArtifactRcvd;
/* Process the reply that came back from the server */
while( blob_line(&recv, &xfer.line) ){
if( blob_buffer(&xfer.line)[0]=='#' ){
const char *zLine = blob_buffer(&xfer.line);
if( memcmp(zLine, "# timestamp ", 12)==0 ){
| > | 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 |
if( syncFlags & SYNC_PUSH ){
blob_appendf(&send, "push %s %s\n", zSCode, zPCode);
nCardSent++;
}
go = 0;
nUvGimmeSent = 0;
nUvFileRcvd = 0;
nGimmeRcvd = 0;
nPriorArtifact = nArtifactRcvd;
/* Process the reply that came back from the server */
while( blob_line(&recv, &xfer.line) ){
if( blob_buffer(&xfer.line)[0]=='#' ){
const char *zLine = blob_buffer(&xfer.line);
if( memcmp(zLine, "# timestamp ", 12)==0 ){
|
| ︙ | ︙ | |||
2447 2448 2449 2450 2451 2452 2453 |
if( blob_eq(&xfer.aToken[0], "gimme")
&& xfer.nToken==2
&& blob_is_hname(&xfer.aToken[1])
){
remote_unk(&xfer.aToken[1]);
if( syncFlags & SYNC_PUSH ){
int rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
| > | > > | 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 |
if( blob_eq(&xfer.aToken[0], "gimme")
&& xfer.nToken==2
&& blob_is_hname(&xfer.aToken[1])
){
remote_unk(&xfer.aToken[1]);
if( syncFlags & SYNC_PUSH ){
int rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
if( rid ){
send_file(&xfer, rid, &xfer.aToken[1], 0);
nGimmeRcvd++;
}
}
}else
/* igot HASH ?PRIVATEFLAG?
**
** Server announces that it has a particular file. If this is
** not a file that we have and we are pulling, then create a
|
| ︙ | ︙ |
Changes to test/merge1.test.
| ︙ | ︙ | |||
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
333 - This is a test of the merging algohm - 3333
444 - If all goes well, we will be pleased - 4444
555 - we think it well and other stuff too - 5555
}
write_file_indented t23 {
<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
111 - This is line ONE of the demo program - 1111
||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
111 - This is line one of the demo program - 1111
======= MERGED IN content follows =============================== (line 1)
111 - This is line one OF the demo program - 1111
>>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
222 - The second line program line in code - 2222
333 - This is a test of the merging algohm - 3333
444 - If all goes well, we will be pleased - 4444
555 - we think it well and other stuff too - 5555
}
write_file_indented t32 {
<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
111 - This is line one OF the demo program - 1111
||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
111 - This is line one of the demo program - 1111
======= MERGED IN content follows =============================== (line 1)
111 - This is line ONE of the demo program - 1111
>>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
222 - The second line program line in code - 2222
333 - This is a test of the merging algohm - 3333
| > > > > | 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
333 - This is a test of the merging algohm - 3333
444 - If all goes well, we will be pleased - 4444
555 - we think it well and other stuff too - 5555
}
write_file_indented t23 {
<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
111 - This is line ONE of the demo program - 1111
####### SUGGESTED CONFLICT RESOLUTION follows ###################
111 - This is line ONE OF the demo program - 1111
||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
111 - This is line one of the demo program - 1111
======= MERGED IN content follows =============================== (line 1)
111 - This is line one OF the demo program - 1111
>>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
222 - The second line program line in code - 2222
333 - This is a test of the merging algohm - 3333
444 - If all goes well, we will be pleased - 4444
555 - we think it well and other stuff too - 5555
}
write_file_indented t32 {
<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
111 - This is line one OF the demo program - 1111
####### SUGGESTED CONFLICT RESOLUTION follows ###################
111 - This is line ONE OF the demo program - 1111
||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
111 - This is line one of the demo program - 1111
======= MERGED IN content follows =============================== (line 1)
111 - This is line ONE of the demo program - 1111
>>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
222 - The second line program line in code - 2222
333 - This is a test of the merging algohm - 3333
|
| ︙ | ︙ | |||
157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
222 - The second line program line in code - 2222
333 - This is a test of the merging algohm - 3333
444 - If all goes well, we will be pleased - 4444
555 - we think it well and other stuff too - 5555
}
write_file_indented t32 {
<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
111 - This is line one of the demo program - 1111
======= MERGED IN content follows =============================== (line 1)
000 - Zero lines added to the beginning of - 0000
111 - This is line one of the demo program - 1111
>>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
222 - The second line program line in code - 2222
| > > > | 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
222 - The second line program line in code - 2222
333 - This is a test of the merging algohm - 3333
444 - If all goes well, we will be pleased - 4444
555 - we think it well and other stuff too - 5555
}
write_file_indented t32 {
<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 1)
####### SUGGESTED CONFLICT RESOLUTION follows ###################
000 - Zero lines added to the beginning of - 0000
111 - This is line one of the demo program - 1111
||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 1)
111 - This is line one of the demo program - 1111
======= MERGED IN content follows =============================== (line 1)
000 - Zero lines added to the beginning of - 0000
111 - This is line one of the demo program - 1111
>>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
222 - The second line program line in code - 2222
|
| ︙ | ︙ | |||
303 304 305 306 307 308 309 310 311 312 313 314 315 316 | efgh 2 ijkl 2 mnop 2 qrst uvwx yzAB 2 CDEF 2 GHIJ 2 ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2) efgh ijkl mnop qrst uvwx | > > > > > > > > > | 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 | efgh 2 ijkl 2 mnop 2 qrst uvwx yzAB 2 CDEF 2 GHIJ 2 ####### SUGGESTED CONFLICT RESOLUTION follows ################### efgh 2 ijkl 2 mnop 3 qrst 3 uvwx 3 yzAB 3 CDEF 2 GHIJ 2 ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2) efgh ijkl mnop qrst uvwx |
| ︙ | ︙ | |||
370 371 372 373 374 375 376 377 378 379 380 381 382 383 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 2) efgh 2 ijkl 2 mnop qrst uvwx yzAB 2 CDEF 2 GHIJ 2 ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2) efgh ijkl mnop qrst | > > > > > > > > > | 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 | <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 2) efgh 2 ijkl 2 mnop qrst uvwx yzAB 2 CDEF 2 GHIJ 2 ####### SUGGESTED CONFLICT RESOLUTION follows ################### efgh 2 ijkl 2 mnop 3 qrst 3 uvwx 3 yzAB 3 CDEF 2 GHIJ 2 ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||| (line 2) efgh ijkl mnop qrst |
| ︙ | ︙ |
Changes to test/merge3.test.
| ︙ | ︙ | |||
25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
write_file t2 [join [string trim $v1] \n]\n
write_file t3 [join [string trim $v2] \n]\n
fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args
set x [read_file t4]
regsub -all \
{<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <+ \(line \d+\)} \
$x {MINE:} x
regsub -all \
{\|\|\|\|\|\|\| COMMON ANCESTOR content follows \|+ \(line \d+\)} \
$x {COM:} x
regsub -all \
{======= MERGED IN content follows =+ \(line \d+\)} \
$x {YOURS:} x
regsub -all \
| > > > | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
write_file t2 [join [string trim $v1] \n]\n
write_file t3 [join [string trim $v2] \n]\n
fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args
set x [read_file t4]
regsub -all \
{<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <+ \(line \d+\)} \
$x {MINE:} x
regsub -all \
{####### SUGGESTED CONFLICT RESOLUTION follows #+} \
$x {BOT:} x
regsub -all \
{\|\|\|\|\|\|\| COMMON ANCESTOR content follows \|+ \(line \d+\)} \
$x {COM:} x
regsub -all \
{======= MERGED IN content follows =+ \(line \d+\)} \
$x {YOURS:} x
regsub -all \
|
| ︙ | ︙ | |||
71 72 73 74 75 76 77 |
merge-test 3 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5b 6 7 8 9
} {
1 2 3 4 5c 6 7 8 9
} {
| | | | | | | | 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
merge-test 3 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5b 6 7 8 9
} {
1 2 3 4 5c 6 7 8 9
} {
1 2 MINE: 3b 4b 5b BOT: 3b 4b 5c COM: 3 4 5 YOURS: 3 4 5c END 6 7 8 9
} -expectError
merge-test 4 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5b 6b 7 8 9
} {
1 2 3 4 5c 6 7 8 9
} {
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
} -expectError
merge-test 5 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5b 6b 7 8 9
} {
1 2 3 4 5c 6c 7c 8 9
} {
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
} -expectError
merge-test 6 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5b 6b 7 8b 9
} {
1 2 3 4 5c 6c 7c 8 9
} {
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
} -expectError
merge-test 7 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5b 6b 7 8b 9
} {
1 2 3 4 5c 6c 7c 8c 9
} {
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
} -expectError
merge-test 8 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5b 6b 7 8b 9b
} {
1 2 3 4 5c 6c 7c 8c 9
} {
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
} -expectError
merge-test 9 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5 6 7 8b 9b
} {
1 2 3 4 5c 6c 7c 8 9
|
| ︙ | ︙ | |||
144 145 146 147 148 149 150 |
merge-test 11 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5 6 7 8b 9b
} {
1 2 3b 4c 5 6c 7c 8 9
} {
| | | 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
merge-test 11 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b 4b 5 6 7 8b 9b
} {
1 2 3b 4c 5 6c 7c 8 9
} {
1 2 MINE: 3b 4b BOT: 3b 4c COM: 3 4 YOURS: 3b 4c END 5 6c 7c 8b 9b
} -expectError
merge-test 12 {
1 2 3 4 5 6 7 8 9
} {
1 2 3b4b 5 6 7 8b 9b
} {
1 2 3b4b 5 6c 7c 8 9
|
| ︙ | ︙ | |||
199 200 201 202 203 204 205 |
merge-test 24 {
1 2 3 4 5 6 7 8 9
} {
1 6 7 8 9
} {
1 2 3 4 9
} {
| | | | 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 |
merge-test 24 {
1 2 3 4 5 6 7 8 9
} {
1 6 7 8 9
} {
1 2 3 4 9
} {
1 MINE: 6 7 8 BOT: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END 9
} -expectError
merge-test 25 {
1 2 3 4 5 6 7 8 9
} {
1 7 8 9
} {
1 2 3 9
} {
1 MINE: 7 8 BOT: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END 9
} -expectError
merge-test 30 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 5 6 7 9
} {
|
| ︙ | ︙ | |||
254 255 256 257 258 259 260 |
merge-test 34 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 9
} {
1 6 7 8 9
} {
| | | | 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 |
merge-test 34 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 9
} {
1 6 7 8 9
} {
1 MINE: 2 3 4 BOT: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END 9
} -expectError
merge-test 35 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 9
} {
1 7 8 9
} {
1 MINE: 2 3 BOT: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END 9
} -expectError
merge-test 40 {
2 3 4 5 6 7 8
} {
3 4 5 6 7 8
} {
|
| ︙ | ︙ | |||
309 310 311 312 313 314 315 |
merge-test 44 {
2 3 4 5 6 7 8
} {
6 7 8
} {
2 3 4
} {
| | | | 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 |
merge-test 44 {
2 3 4 5 6 7 8
} {
6 7 8
} {
2 3 4
} {
MINE: 6 7 8 BOT: 2 3 4 COM: 2 3 4 5 6 7 8 YOURS: 2 3 4 END
} -expectError
merge-test 45 {
2 3 4 5 6 7 8
} {
7 8
} {
2 3
} {
MINE: 7 8 BOT: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END
} -expectError
merge-test 50 {
2 3 4 5 6 7 8
} {
2 3 4 5 6 7
} {
|
| ︙ | ︙ | |||
363 364 365 366 367 368 369 |
merge-test 54 {
2 3 4 5 6 7 8
} {
2 3 4
} {
6 7 8
} {
| | | | 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 |
merge-test 54 {
2 3 4 5 6 7 8
} {
2 3 4
} {
6 7 8
} {
MINE: 2 3 4 BOT: 6 7 8 COM: 2 3 4 5 6 7 8 YOURS: 6 7 8 END
} -expectError
merge-test 55 {
2 3 4 5 6 7 8
} {
2 3
} {
7 8
} {
MINE: 2 3 BOT: 7 8 COM: 2 3 4 5 6 7 8 YOURS: 7 8 END
} -expectError
merge-test 60 {
1 2 3 4 5 6 7 8 9
} {
1 2b 3 4 5 6 7 8 9
} {
|
| ︙ | ︙ | |||
418 419 420 421 422 423 424 |
merge-test 64 {
1 2 3 4 5 6 7 8 9
} {
1 2b 3b 4b 5b 6 7 8 9
} {
1 2 3 4 9
} {
| | | | 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 |
merge-test 64 {
1 2 3 4 5 6 7 8 9
} {
1 2b 3b 4b 5b 6 7 8 9
} {
1 2 3 4 9
} {
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
} -expectError
merge-test 65 {
1 2 3 4 5 6 7 8 9
} {
1 2b 3b 4b 5b 6b 7 8 9
} {
1 2 3 9
} {
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
} -expectError
merge-test 70 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 5 6 7 9
} {
|
| ︙ | ︙ | |||
473 474 475 476 477 478 479 |
merge-test 74 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 9
} {
1 2b 3b 4b 5b 6 7 8 9
} {
| | | | 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 |
merge-test 74 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 9
} {
1 2b 3b 4b 5b 6 7 8 9
} {
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
} -expectError
merge-test 75 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 9
} {
1 2b 3b 4b 5b 6b 7 8 9
} {
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
} -expectError
merge-test 80 {
2 3 4 5 6 7 8
} {
2b 3 4 5 6 7 8
} {
|
| ︙ | ︙ | |||
528 529 530 531 532 533 534 |
merge-test 84 {
2 3 4 5 6 7 8
} {
2b 3b 4b 5b 6 7 8
} {
2 3 4
} {
| | | | 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 |
merge-test 84 {
2 3 4 5 6 7 8
} {
2b 3b 4b 5b 6 7 8
} {
2 3 4
} {
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
} -expectError
merge-test 85 {
2 3 4 5 6 7 8
} {
2b 3b 4b 5b 6b 7 8
} {
2 3
} {
MINE: 2b 3b 4b 5b 6b 7 8 BOT: 2 3 COM: 2 3 4 5 6 7 8 YOURS: 2 3 END
} -expectError
merge-test 90 {
2 3 4 5 6 7 8
} {
2 3 4 5 6 7
} {
|
| ︙ | ︙ | |||
583 584 585 586 587 588 589 |
merge-test 94 {
2 3 4 5 6 7 8
} {
2 3 4
} {
2b 3b 4b 5b 6 7 8
} {
| | | | 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 |
merge-test 94 {
2 3 4 5 6 7 8
} {
2 3 4
} {
2b 3b 4b 5b 6 7 8
} {
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
} -expectError
merge-test 95 {
2 3 4 5 6 7 8
} {
2 3
} {
2b 3b 4b 5b 6b 7 8
} {
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
} -expectError
merge-test 100 {
1 2 3 4 5 6 7 8 9
} {
1 2b 3 4 5 7 8 9 a b c d e
} {
|
| ︙ | ︙ | |||
629 630 631 632 633 634 635 |
merge-test 103 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 5 7 8 9b
} {
1 2 3 4 5 7 8 9b a b c d e
} {
| | | | 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 |
merge-test 103 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 5 7 8 9b
} {
1 2 3 4 5 7 8 9b a b c d e
} {
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
} -expectError
merge-test 104 {
1 2 3 4 5 6 7 8 9
} {
1 2 3 4 5 7 8 9b a b c d e
} {
1 2 3 4 5 7 8 9b
} {
1 2 3 4 5 7 8 MINE: 9b a b c d e BOT: 9b COM: 9 YOURS: 9b END
} -expectError
###############################################################################
test_cleanup
|
Changes to test/merge4.test.
| ︙ | ︙ | |||
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
write_file t1 [join [string trim $basis] \n]\n
write_file t2 [join [string trim $v1] \n]\n
write_file t3 [join [string trim $v2] \n]\n
fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args
fossil 3-way-merge t1 t3 t2 t5 {*}$fossil_args
set x [read_file t4]
regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $x {>} x
regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $x {=} x
regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $x {<} x
set x [split [string trim $x] \n]
set y [read_file t5]
regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $y {>} y
regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $y {=} y
regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $y {<} y
set y [split [string trim $y] \n]
set result1 [string trim $result1]
if {$x!=$result1} {
protOut " Expected \[$result1\]"
protOut " Got \[$x\]"
| > > | 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
write_file t1 [join [string trim $basis] \n]\n
write_file t2 [join [string trim $v1] \n]\n
write_file t3 [join [string trim $v2] \n]\n
fossil 3-way-merge t1 t2 t3 t4 {*}$fossil_args
fossil 3-way-merge t1 t3 t2 t5 {*}$fossil_args
set x [read_file t4]
regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $x {>} x
regsub -all {####### SUGGESTED CONFLICT RESOLUTION.*##} $x {#} x
regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $x {=} x
regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $x {<} x
set x [split [string trim $x] \n]
set y [read_file t5]
regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<< \(line \d+\)} $y {>} y
regsub -all {####### SUGGESTED CONFLICT RESOLUTION.*##} $y {#} y
regsub -all {\|\|\|\|\|\|\|.*======= \(line \d+\)} $y {=} y
regsub -all {>>>>>>> END MERGE CONFLICT.*>>>>} $y {<} y
set y [split [string trim $y] \n]
set result1 [string trim $result1]
if {$x!=$result1} {
protOut " Expected \[$result1\]"
protOut " Got \[$x\]"
|
| ︙ | ︙ | |||
56 57 58 59 60 61 62 |
merge-test 1000 {
1 2 3 4 5 6 7 8 9
} {
1 2b 3b 4b 5 6b 7b 8b 9
} {
1 2 3 4c 5c 6c 7 8 9
} {
| | | | | | 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
merge-test 1000 {
1 2 3 4 5 6 7 8 9
} {
1 2b 3b 4b 5 6b 7b 8b 9
} {
1 2 3 4c 5c 6c 7 8 9
} {
1 > 2b 3b 4b 5 6b 7b 8b # 2b 3b 4c 5c 6c 7b 8b = 2 3 4c 5c 6c 7 8 < 9
} {
1 > 2 3 4c 5c 6c 7 8 # 2b 3b 4b 5c 6b 7b 8b = 2b 3b 4b 5 6b 7b 8b < 9
} -expectError
merge-test 1001 {
1 2 3 4 5 6 7 8 9
} {
1 2b 3b 4 5 6 7b 8b 9
} {
1 2 3 4c 5c 6c 7 8 9
} {
1 2b 3b 4c 5c 6c 7b 8b 9
} {
1 2b 3b 4c 5c 6c 7b 8b 9
}
merge-test 1002 {
2 3 4 5 6 7 8
} {
2b 3b 4b 5 6b 7b 8b
} {
2 3 4c 5c 6c 7 8
} {
> 2b 3b 4b 5 6b 7b 8b # 2b 3b 4c 5c 6c 7b 8b = 2 3 4c 5c 6c 7 8 <
} {
> 2 3 4c 5c 6c 7 8 # 2b 3b 4b 5c 6b 7b 8b = 2b 3b 4b 5 6b 7b 8b <
} -expectError
merge-test 1003 {
2 3 4 5 6 7 8
} {
2b 3b 4 5 6 7b 8b
} {
2 3 4c 5c 6c 7 8
|
| ︙ | ︙ |
Changes to test/tester.tcl.
| ︙ | ︙ | |||
330 331 332 333 334 335 336 337 338 339 340 341 342 343 |
email-url \
empty-dirs \
encoding-glob \
exec-rel-paths \
fileedit-glob \
forbid-delta-manifests \
forum-close-policy \
gdiff-command \
gmerge-command \
hash-digits \
hooks \
http-port \
https-login \
ignore-glob \
| > | 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 |
email-url \
empty-dirs \
encoding-glob \
exec-rel-paths \
fileedit-glob \
forbid-delta-manifests \
forum-close-policy \
forum-title \
gdiff-command \
gmerge-command \
hash-digits \
hooks \
http-port \
https-login \
ignore-glob \
|
| ︙ | ︙ | |||
370 371 372 373 374 375 376 377 378 379 380 381 382 383 |
ssh-command \
ssl-ca-location \
ssl-identity \
tclsh \
th1-setup \
th1-uri-regexp \
ticket-default-report \
user-color-map \
uv-sync \
web-browser]
fossil test-th-eval "hasfeature legacyMvRm"
if {[normalize_result] eq "1"} {
| > | 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 |
ssh-command \
ssl-ca-location \
ssl-identity \
tclsh \
th1-setup \
th1-uri-regexp \
ticket-default-report \
timeline-utc \
user-color-map \
uv-sync \
web-browser]
fossil test-th-eval "hasfeature legacyMvRm"
if {[normalize_result] eq "1"} {
|
| ︙ | ︙ |
Changes to test/update.test.
| ︙ | ︙ | |||
55 56 57 58 59 60 61 |
# Make sure we are not in an open repository and initialize new repository
test_setup
###############################################################################
fossil update --verbose
test update-already-up-to-date {
| | | 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# Make sure we are not in an open repository and initialize new repository
test_setup
###############################################################################
fossil update --verbose
test update-already-up-to-date {
[regexp {^-{79}\ncheckout: .*\nchanges: +None. Already up-to-date.$} $RESULT]
}
# Remaining tests are carried out in the order update_cmd() performs checks.
#
# Common approach for tests below:
# 1. Set the testname
# 2. Set the file name, done by calling update_setup
|
| ︙ | ︙ |
Changes to tools/makemake.tcl.
| ︙ | ︙ | |||
130 131 132 133 134 135 136 137 138 139 140 141 142 143 | loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name patch path | > | 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 | loadctrl login lookslike main manifest markdown markdown_html match md5 merge merge3 moderate name patch path |
| ︙ | ︙ | |||
207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
}
# Additional resource files that get built into the executable.
# These paths are all resolved from the src/ directory, so must
# be relative to that.
set extra_files {
diff.tcl
markdown.md
wiki.wiki
*.js
default.css
style.*.css
../skins/*/*.txt
sounds/*.wav
| > | 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
}
# Additional resource files that get built into the executable.
# These paths are all resolved from the src/ directory, so must
# be relative to that.
set extra_files {
diff.tcl
merge.tcl
markdown.md
wiki.wiki
*.js
default.css
style.*.css
../skins/*/*.txt
sounds/*.wav
|
| ︙ | ︙ | |||
443 444 445 446 447 448 449 | SQLITE3_OBJ. = $(SQLITE3_OBJ.0) SQLITE3_OBJ = $(SQLITE3_OBJ.$(SQLITE3_ORIGIN)) # The USE_LINENOISE variable may be undefined, set to 0, or set # to 1. If it is set to 0, then there is no need to build or link # the linenoise.o object. LINENOISE_DEF.0 = | | | 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 | SQLITE3_OBJ. = $(SQLITE3_OBJ.0) SQLITE3_OBJ = $(SQLITE3_OBJ.$(SQLITE3_ORIGIN)) # The USE_LINENOISE variable may be undefined, set to 0, or set # to 1. If it is set to 0, then there is no need to build or link # the linenoise.o object. LINENOISE_DEF.0 = LINENOISE_DEF.1 = -DHAVE_LINENOISE=2 LINENOISE_DEF. = $(LINENOISE_DEF.0) LINENOISE_OBJ.0 = LINENOISE_OBJ.1 = $(OBJDIR)/linenoise.o LINENOISE_OBJ. = $(LINENOISE_OBJ.0) # The USE_SEE variable may be undefined, 0 or 1. If undefined or 0, # in-tree SQLite is used. If 1, then sqlite3-see.c (not part of the |
| ︙ | ︙ |
Changes to tools/man_page_command_list.tcl.
| ︙ | ︙ | |||
8 9 10 11 12 13 14 |
# The only supported command-line argument is the optional output filename.
if {[llength $argv] == 1} {
set file [lindex $argv 0]
}
# Get list of common commands.
set commands [exec fossil help]
| | | 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# The only supported command-line argument is the optional output filename.
if {[llength $argv] == 1} {
set file [lindex $argv 0]
}
# Get list of common commands.
set commands [exec fossil help]
regsub -nocase {.*?\nfrequently used commands:.*\n} $commands {} commands
regsub -nocase {\nthis is fossil version.*} $commands {} commands
regsub -all {\s+} $commands " " commands
set commands [lsort $commands]
# Compute number of rows.
set rows [expr {([llength $commands] + $columns - 1) / $columns}]
|
| ︙ | ︙ |
Changes to tools/translate.c.
| ︙ | ︙ | |||
76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
*/
static int inStr = 0;
/*
** Name of files being processed
*/
static const char *zInFile = "(stdin)";
/*
** Terminate an active cgi_printf() or free string
*/
static void end_block(FILE *out){
if( inPrint ){
zArg[nArg] = 0;
| > > > > > > > > > > > | 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 |
*/
static int inStr = 0;
/*
** Name of files being processed
*/
static const char *zInFile = "(stdin)";
/*
** The `fossil_isspace()' function copied from the Fossil source code.
** Some MSVC runtime library versions of `isspace()' break with an `assert()' if
** the input is smaller than -1 or greater than 255 in debug builds, due to sign
** extension when promoting `signed char' to `int' for non-ASCII characters. Use
** an `isspace()' replacement instead of explicit type casts to `unsigned char'.
*/
int fossil_isspace(char c){
return c==' ' || (c<='\r' && c>='\t');
}
/*
** Terminate an active cgi_printf() or free string
*/
static void end_block(FILE *out){
if( inPrint ){
zArg[nArg] = 0;
|
| ︙ | ︙ | |||
104 105 106 107 108 109 110 |
int lineNo = 0; /* Line number */
char zLine[2000]; /* A single line of input */
char zOut[4000]; /* The input line translated into appropriate output */
c1 = c2 = '-';
while( fgets(zLine, sizeof(zLine), in) ){
lineNo++;
| | | | | | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
int lineNo = 0; /* Line number */
char zLine[2000]; /* A single line of input */
char zOut[4000]; /* The input line translated into appropriate output */
c1 = c2 = '-';
while( fgets(zLine, sizeof(zLine), in) ){
lineNo++;
for(i=0; zLine[i] && fossil_isspace(zLine[i]); i++){}
if( zLine[i]!='@' ){
if( inPrint || inStr ) end_block(out);
fprintf(out,"%s",zLine);
/* 0123456789 12345 */
if( strncmp(zLine, "/* @-comment: ", 14)==0 ){
c1 = zLine[14];
c2 = zLine[15];
}
i += strlen(&zLine[i]);
while( i>0 && fossil_isspace(zLine[i-1]) ){ i--; }
lastWasEq = i>0 && zLine[i-1]=='=';
lastWasComma = i>0 && zLine[i-1]==',';
}else if( lastWasEq || lastWasComma){
/* If the last non-whitespace character before the first @ was
** an "="(var init/set) or a ","(const definition in list) then
** generate a string literal. But skip comments
** consisting of all text between c1 and c2 (default "--")
** and end of line.
*/
int indent, omitline;
char *zNewline = "\\n";
i++;
if( fossil_isspace(zLine[i]) ){ i++; }
indent = i - 2;
if( indent<0 ) indent = 0;
omitline = 0;
for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){
if( zLine[i]==c1 && (c2==' ' || zLine[i+1]==c2) ){
omitline = 1; break;
}
if( zLine[i]=='\\' && (zLine[i+1]==0 || zLine[i+1]=='\r'
|| zLine[i+1]=='\n') ){
zLine[i] = 0;
zNewline = "";
/* fprintf(stderr, "%s:%d: omit newline\n", zInFile, lineNo); */
break;
}
if( zLine[i]=='\\' || zLine[i]=='"' ){ zOut[j++] = '\\'; }
zOut[j++] = zLine[i];
}
if( zNewline[0] ) while( j>0 && fossil_isspace(zOut[j-1]) ){ j--; }
zOut[j] = 0;
if( j<=0 && omitline ){
fprintf(out,"\n");
}else{
fprintf(out,"%*s\"%s%s\"\n",indent, "", zOut, zNewline);
}
}else{
|
| ︙ | ︙ | |||
169 170 171 172 173 174 175 |
*/
const char *zNewline = "\\n";
int indent;
int nC;
int nParam;
char c;
i++;
| | | 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
*/
const char *zNewline = "\\n";
int indent;
int nC;
int nParam;
char c;
i++;
if( fossil_isspace(zLine[i]) ){ i++; }
indent = i;
for(j=0; zLine[i] && zLine[i]!='\r' && zLine[i]!='\n'; i++){
if( zLine[i]=='\\' && (!zLine[i+1] || zLine[i+1]=='\r'
|| zLine[i+1]=='\n') ){
zNewline = "";
break;
}
|
| ︙ | ︙ |
Changes to win/Makefile.dmc.
| ︙ | ︙ | |||
30 31 32 33 34 35 36 | SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_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 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 PIKCHR_OPTIONS = -DPIKCHR_TOKEN_LIMIT=10000 | | | | | 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_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 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 PIKCHR_OPTIONS = -DPIKCHR_TOKEN_LIMIT=10000 SRC = add_.c ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c chat_.c checkin_.c checkout_.c clearsign_.c clone_.c color_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c match_.c md5_.c merge_.c merge3_.c moderate_.c name_.c patch_.c path_.c piechart_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c OBJ = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\color$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\match$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\patch$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchrshow$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O RC=$(DMDIR)\bin\rcc RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ APPNAME = $(OBJDIR)\fossil$(E) all: $(APPNAME) $(APPNAME) : translate$E mkindex$E codecheck1$E headers $(OBJ) $(OBJDIR)\link cd $(OBJDIR) codecheck1$E $(SRC) $(DMDIR)\bin\link @link $(OBJDIR)\fossil.res: $B\win\fossil.rc $(RC) $(RCFLAGS) -o$@ $** $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res +echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone color comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html match md5 merge merge3 moderate name patch path piechart pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@ +echo fossil >> $@ +echo fossil >> $@ +echo $(LIBS) >> $@ +echo. >> $@ +echo fossil >> $@ translate$E: $(SRCDIR_tools)\translate.c |
| ︙ | ︙ | |||
633 634 635 636 637 638 639 640 641 642 643 644 645 646 | +translate$E $** > $@ $(OBJDIR)\markdown_html$O : markdown_html_.c markdown_html.h $(TCC) -o$@ -c markdown_html_.c markdown_html_.c : $(SRCDIR)\markdown_html.c +translate$E $** > $@ $(OBJDIR)\md5$O : md5_.c md5.h $(TCC) -o$@ -c md5_.c md5_.c : $(SRCDIR)\md5.c +translate$E $** > $@ | > > > > > > | 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 | +translate$E $** > $@ $(OBJDIR)\markdown_html$O : markdown_html_.c markdown_html.h $(TCC) -o$@ -c markdown_html_.c markdown_html_.c : $(SRCDIR)\markdown_html.c +translate$E $** > $@ $(OBJDIR)\match$O : match_.c match.h $(TCC) -o$@ -c match_.c match_.c : $(SRCDIR)\match.c +translate$E $** > $@ $(OBJDIR)\md5$O : md5_.c md5.h $(TCC) -o$@ -c md5_.c md5_.c : $(SRCDIR)\md5.c +translate$E $** > $@ |
| ︙ | ︙ | |||
1007 1008 1009 1010 1011 1012 1013 | $(OBJDIR)\zip$O : zip_.c zip.h $(TCC) -o$@ -c zip_.c zip_.c : $(SRCDIR)\zip.c +translate$E $** > $@ headers: makeheaders$E page_index.h builtin_data.h VERSION.h | | | 1013 1014 1015 1016 1017 1018 1019 1020 1021 | $(OBJDIR)\zip$O : zip_.c zip.h $(TCC) -o$@ -c zip_.c zip_.c : $(SRCDIR)\zip.c +translate$E $** > $@ headers: makeheaders$E page_index.h builtin_data.h VERSION.h +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h color_.c:color.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h interwiki_.c:interwiki.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h match_.c:match.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h patch_.c:patch.h path_.c:path.h piechart_.c:piechart.h pikchrshow_.c:pikchrshow.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR_extsrc)\pikchr.c:pikchr.h $(SRCDIR_extsrc)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR_extsrc)\cson_amalgamation.h @copy /Y nul: headers |
Changes to win/Makefile.mingw.
| ︙ | ︙ | |||
483 484 485 486 487 488 489 490 491 492 493 494 495 496 | $(SRCDIR)/loadctrl.c \ $(SRCDIR)/login.c \ $(SRCDIR)/lookslike.c \ $(SRCDIR)/main.c \ $(SRCDIR)/manifest.c \ $(SRCDIR)/markdown.c \ $(SRCDIR)/markdown_html.c \ $(SRCDIR)/md5.c \ $(SRCDIR)/merge.c \ $(SRCDIR)/merge3.c \ $(SRCDIR)/moderate.c \ $(SRCDIR)/name.c \ $(SRCDIR)/patch.c \ $(SRCDIR)/path.c \ | > | 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 | $(SRCDIR)/loadctrl.c \ $(SRCDIR)/login.c \ $(SRCDIR)/lookslike.c \ $(SRCDIR)/main.c \ $(SRCDIR)/manifest.c \ $(SRCDIR)/markdown.c \ $(SRCDIR)/markdown_html.c \ $(SRCDIR)/match.c \ $(SRCDIR)/md5.c \ $(SRCDIR)/merge.c \ $(SRCDIR)/merge3.c \ $(SRCDIR)/moderate.c \ $(SRCDIR)/name.c \ $(SRCDIR)/patch.c \ $(SRCDIR)/path.c \ |
| ︙ | ︙ | |||
632 633 634 635 636 637 638 639 640 641 642 643 644 645 | $(SRCDIR)/fossil.wikiedit-wysiwyg.js \ $(SRCDIR)/graph.js \ $(SRCDIR)/hbmenu.js \ $(SRCDIR)/href.js \ $(SRCDIR)/login.js \ $(SRCDIR)/markdown.md \ $(SRCDIR)/menu.js \ $(SRCDIR)/scroll.js \ $(SRCDIR)/skin.js \ $(SRCDIR)/sorttable.js \ $(SRCDIR)/sounds/0.wav \ $(SRCDIR)/sounds/1.wav \ $(SRCDIR)/sounds/2.wav \ $(SRCDIR)/sounds/3.wav \ | > | 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 | $(SRCDIR)/fossil.wikiedit-wysiwyg.js \ $(SRCDIR)/graph.js \ $(SRCDIR)/hbmenu.js \ $(SRCDIR)/href.js \ $(SRCDIR)/login.js \ $(SRCDIR)/markdown.md \ $(SRCDIR)/menu.js \ $(SRCDIR)/merge.tcl \ $(SRCDIR)/scroll.js \ $(SRCDIR)/skin.js \ $(SRCDIR)/sorttable.js \ $(SRCDIR)/sounds/0.wav \ $(SRCDIR)/sounds/1.wav \ $(SRCDIR)/sounds/2.wav \ $(SRCDIR)/sounds/3.wav \ |
| ︙ | ︙ | |||
747 748 749 750 751 752 753 754 755 756 757 758 759 760 | $(OBJDIR)/loadctrl_.c \ $(OBJDIR)/login_.c \ $(OBJDIR)/lookslike_.c \ $(OBJDIR)/main_.c \ $(OBJDIR)/manifest_.c \ $(OBJDIR)/markdown_.c \ $(OBJDIR)/markdown_html_.c \ $(OBJDIR)/md5_.c \ $(OBJDIR)/merge_.c \ $(OBJDIR)/merge3_.c \ $(OBJDIR)/moderate_.c \ $(OBJDIR)/name_.c \ $(OBJDIR)/patch_.c \ $(OBJDIR)/path_.c \ | > | 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 | $(OBJDIR)/loadctrl_.c \ $(OBJDIR)/login_.c \ $(OBJDIR)/lookslike_.c \ $(OBJDIR)/main_.c \ $(OBJDIR)/manifest_.c \ $(OBJDIR)/markdown_.c \ $(OBJDIR)/markdown_html_.c \ $(OBJDIR)/match_.c \ $(OBJDIR)/md5_.c \ $(OBJDIR)/merge_.c \ $(OBJDIR)/merge3_.c \ $(OBJDIR)/moderate_.c \ $(OBJDIR)/name_.c \ $(OBJDIR)/patch_.c \ $(OBJDIR)/path_.c \ |
| ︙ | ︙ | |||
896 897 898 899 900 901 902 903 904 905 906 907 908 909 | $(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 \ | > | 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 | $(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 \ |
| ︙ | ︙ | |||
1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 | $(OBJDIR)/loadctrl_.c:$(OBJDIR)/loadctrl.h \ $(OBJDIR)/login_.c:$(OBJDIR)/login.h \ $(OBJDIR)/lookslike_.c:$(OBJDIR)/lookslike.h \ $(OBJDIR)/main_.c:$(OBJDIR)/main.h \ $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \ $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \ $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \ $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \ $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \ $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \ $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \ $(OBJDIR)/name_.c:$(OBJDIR)/name.h \ $(OBJDIR)/patch_.c:$(OBJDIR)/patch.h \ $(OBJDIR)/path_.c:$(OBJDIR)/path.h \ | > | 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 | $(OBJDIR)/loadctrl_.c:$(OBJDIR)/loadctrl.h \ $(OBJDIR)/login_.c:$(OBJDIR)/login.h \ $(OBJDIR)/lookslike_.c:$(OBJDIR)/lookslike.h \ $(OBJDIR)/main_.c:$(OBJDIR)/main.h \ $(OBJDIR)/manifest_.c:$(OBJDIR)/manifest.h \ $(OBJDIR)/markdown_.c:$(OBJDIR)/markdown.h \ $(OBJDIR)/markdown_html_.c:$(OBJDIR)/markdown_html.h \ $(OBJDIR)/match_.c:$(OBJDIR)/match.h \ $(OBJDIR)/md5_.c:$(OBJDIR)/md5.h \ $(OBJDIR)/merge_.c:$(OBJDIR)/merge.h \ $(OBJDIR)/merge3_.c:$(OBJDIR)/merge3.h \ $(OBJDIR)/moderate_.c:$(OBJDIR)/moderate.h \ $(OBJDIR)/name_.c:$(OBJDIR)/name.h \ $(OBJDIR)/patch_.c:$(OBJDIR)/patch.h \ $(OBJDIR)/path_.c:$(OBJDIR)/path.h \ |
| ︙ | ︙ | |||
2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 | $(OBJDIR)/markdown_html_.c: $(SRCDIR)/markdown_html.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/markdown_html.c >$@ $(OBJDIR)/markdown_html.o: $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/markdown_html.o -c $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h: $(OBJDIR)/headers $(OBJDIR)/md5_.c: $(SRCDIR)/md5.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/md5.c >$@ $(OBJDIR)/md5.o: $(OBJDIR)/md5_.c $(OBJDIR)/md5.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/md5.o -c $(OBJDIR)/md5_.c | > > > > > > > > | 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 | $(OBJDIR)/markdown_html_.c: $(SRCDIR)/markdown_html.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/markdown_html.c >$@ $(OBJDIR)/markdown_html.o: $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/markdown_html.o -c $(OBJDIR)/markdown_html_.c $(OBJDIR)/markdown_html.h: $(OBJDIR)/headers $(OBJDIR)/match_.c: $(SRCDIR)/match.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/match.c >$@ $(OBJDIR)/match.o: $(OBJDIR)/match_.c $(OBJDIR)/match.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/match.o -c $(OBJDIR)/match_.c $(OBJDIR)/match.h: $(OBJDIR)/headers $(OBJDIR)/md5_.c: $(SRCDIR)/md5.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/md5.c >$@ $(OBJDIR)/md5.o: $(OBJDIR)/md5_.c $(OBJDIR)/md5.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/md5.o -c $(OBJDIR)/md5_.c |
| ︙ | ︙ |
Changes to win/Makefile.msc.
| ︙ | ︙ | |||
441 442 443 444 445 446 447 448 449 450 451 452 453 454 |
"$(OX)\loadctrl_.c" \
"$(OX)\login_.c" \
"$(OX)\lookslike_.c" \
"$(OX)\main_.c" \
"$(OX)\manifest_.c" \
"$(OX)\markdown_.c" \
"$(OX)\markdown_html_.c" \
"$(OX)\md5_.c" \
"$(OX)\merge_.c" \
"$(OX)\merge3_.c" \
"$(OX)\moderate_.c" \
"$(OX)\name_.c" \
"$(OX)\patch_.c" \
"$(OX)\path_.c" \
| > | 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 |
"$(OX)\loadctrl_.c" \
"$(OX)\login_.c" \
"$(OX)\lookslike_.c" \
"$(OX)\main_.c" \
"$(OX)\manifest_.c" \
"$(OX)\markdown_.c" \
"$(OX)\markdown_html_.c" \
"$(OX)\match_.c" \
"$(OX)\md5_.c" \
"$(OX)\merge_.c" \
"$(OX)\merge3_.c" \
"$(OX)\moderate_.c" \
"$(OX)\name_.c" \
"$(OX)\patch_.c" \
"$(OX)\path_.c" \
|
| ︙ | ︙ | |||
590 591 592 593 594 595 596 597 598 599 600 601 602 603 |
"$(SRCDIR)\fossil.wikiedit-wysiwyg.js" \
"$(SRCDIR)\graph.js" \
"$(SRCDIR)\hbmenu.js" \
"$(SRCDIR)\href.js" \
"$(SRCDIR)\login.js" \
"$(SRCDIR)\markdown.md" \
"$(SRCDIR)\menu.js" \
"$(SRCDIR)\scroll.js" \
"$(SRCDIR)\skin.js" \
"$(SRCDIR)\sorttable.js" \
"$(SRCDIR)\sounds\0.wav" \
"$(SRCDIR)\sounds\1.wav" \
"$(SRCDIR)\sounds\2.wav" \
"$(SRCDIR)\sounds\3.wav" \
| > | 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 |
"$(SRCDIR)\fossil.wikiedit-wysiwyg.js" \
"$(SRCDIR)\graph.js" \
"$(SRCDIR)\hbmenu.js" \
"$(SRCDIR)\href.js" \
"$(SRCDIR)\login.js" \
"$(SRCDIR)\markdown.md" \
"$(SRCDIR)\menu.js" \
"$(SRCDIR)\merge.tcl" \
"$(SRCDIR)\scroll.js" \
"$(SRCDIR)\skin.js" \
"$(SRCDIR)\sorttable.js" \
"$(SRCDIR)\sounds\0.wav" \
"$(SRCDIR)\sounds\1.wav" \
"$(SRCDIR)\sounds\2.wav" \
"$(SRCDIR)\sounds\3.wav" \
|
| ︙ | ︙ | |||
705 706 707 708 709 710 711 712 713 714 715 716 717 718 |
"$(OX)\loadctrl$O" \
"$(OX)\login$O" \
"$(OX)\lookslike$O" \
"$(OX)\main$O" \
"$(OX)\manifest$O" \
"$(OX)\markdown$O" \
"$(OX)\markdown_html$O" \
"$(OX)\md5$O" \
"$(OX)\merge$O" \
"$(OX)\merge3$O" \
"$(OX)\moderate$O" \
"$(OX)\name$O" \
"$(OX)\patch$O" \
"$(OX)\path$O" \
| > | 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 |
"$(OX)\loadctrl$O" \
"$(OX)\login$O" \
"$(OX)\lookslike$O" \
"$(OX)\main$O" \
"$(OX)\manifest$O" \
"$(OX)\markdown$O" \
"$(OX)\markdown_html$O" \
"$(OX)\match$O" \
"$(OX)\md5$O" \
"$(OX)\merge$O" \
"$(OX)\merge3$O" \
"$(OX)\moderate$O" \
"$(OX)\name$O" \
"$(OX)\patch$O" \
"$(OX)\path$O" \
|
| ︙ | ︙ | |||
954 955 956 957 958 959 960 961 962 963 964 965 966 967 | echo "$(OX)\loadctrl.obj" >> $@ echo "$(OX)\login.obj" >> $@ echo "$(OX)\lookslike.obj" >> $@ echo "$(OX)\main.obj" >> $@ echo "$(OX)\manifest.obj" >> $@ echo "$(OX)\markdown.obj" >> $@ echo "$(OX)\markdown_html.obj" >> $@ echo "$(OX)\md5.obj" >> $@ echo "$(OX)\merge.obj" >> $@ echo "$(OX)\merge3.obj" >> $@ echo "$(OX)\moderate.obj" >> $@ echo "$(OX)\name.obj" >> $@ echo "$(OX)\patch.obj" >> $@ echo "$(OX)\path.obj" >> $@ | > | 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 | echo "$(OX)\loadctrl.obj" >> $@ echo "$(OX)\login.obj" >> $@ echo "$(OX)\lookslike.obj" >> $@ echo "$(OX)\main.obj" >> $@ echo "$(OX)\manifest.obj" >> $@ echo "$(OX)\markdown.obj" >> $@ echo "$(OX)\markdown_html.obj" >> $@ echo "$(OX)\match.obj" >> $@ echo "$(OX)\md5.obj" >> $@ echo "$(OX)\merge.obj" >> $@ echo "$(OX)\merge3.obj" >> $@ echo "$(OX)\moderate.obj" >> $@ echo "$(OX)\name.obj" >> $@ echo "$(OX)\patch.obj" >> $@ echo "$(OX)\path.obj" >> $@ |
| ︙ | ︙ | |||
1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 | echo "$(SRCDIR)\fossil.wikiedit-wysiwyg.js" >> $@ echo "$(SRCDIR)\graph.js" >> $@ echo "$(SRCDIR)\hbmenu.js" >> $@ echo "$(SRCDIR)\href.js" >> $@ echo "$(SRCDIR)\login.js" >> $@ echo "$(SRCDIR)\markdown.md" >> $@ echo "$(SRCDIR)\menu.js" >> $@ echo "$(SRCDIR)\scroll.js" >> $@ echo "$(SRCDIR)\skin.js" >> $@ echo "$(SRCDIR)\sorttable.js" >> $@ echo "$(SRCDIR)\sounds/0.wav" >> $@ echo "$(SRCDIR)\sounds/1.wav" >> $@ echo "$(SRCDIR)\sounds/2.wav" >> $@ echo "$(SRCDIR)\sounds/3.wav" >> $@ | > | 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 | echo "$(SRCDIR)\fossil.wikiedit-wysiwyg.js" >> $@ echo "$(SRCDIR)\graph.js" >> $@ echo "$(SRCDIR)\hbmenu.js" >> $@ echo "$(SRCDIR)\href.js" >> $@ echo "$(SRCDIR)\login.js" >> $@ echo "$(SRCDIR)\markdown.md" >> $@ echo "$(SRCDIR)\menu.js" >> $@ echo "$(SRCDIR)\merge.tcl" >> $@ echo "$(SRCDIR)\scroll.js" >> $@ echo "$(SRCDIR)\skin.js" >> $@ echo "$(SRCDIR)\sorttable.js" >> $@ echo "$(SRCDIR)\sounds/0.wav" >> $@ echo "$(SRCDIR)\sounds/1.wav" >> $@ echo "$(SRCDIR)\sounds/2.wav" >> $@ echo "$(SRCDIR)\sounds/3.wav" >> $@ |
| ︙ | ︙ | |||
1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 | "$(OBJDIR)\translate$E" $** > $@ "$(OX)\markdown_html$O" : "$(OX)\markdown_html_.c" "$(OX)\markdown_html.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\markdown_html_.c" "$(OX)\markdown_html_.c" : "$(SRCDIR)\markdown_html.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\md5$O" : "$(OX)\md5_.c" "$(OX)\md5.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\md5_.c" "$(OX)\md5_.c" : "$(SRCDIR)\md5.c" "$(OBJDIR)\translate$E" $** > $@ | > > > > > > | 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 | "$(OBJDIR)\translate$E" $** > $@ "$(OX)\markdown_html$O" : "$(OX)\markdown_html_.c" "$(OX)\markdown_html.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\markdown_html_.c" "$(OX)\markdown_html_.c" : "$(SRCDIR)\markdown_html.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\match$O" : "$(OX)\match_.c" "$(OX)\match.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\match_.c" "$(OX)\match_.c" : "$(SRCDIR)\match.c" "$(OBJDIR)\translate$E" $** > $@ "$(OX)\md5$O" : "$(OX)\md5_.c" "$(OX)\md5.h" $(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\md5_.c" "$(OX)\md5_.c" : "$(SRCDIR)\md5.c" "$(OBJDIR)\translate$E" $** > $@ |
| ︙ | ︙ | |||
2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 | "$(OX)\loadctrl_.c":"$(OX)\loadctrl.h" \ "$(OX)\login_.c":"$(OX)\login.h" \ "$(OX)\lookslike_.c":"$(OX)\lookslike.h" \ "$(OX)\main_.c":"$(OX)\main.h" \ "$(OX)\manifest_.c":"$(OX)\manifest.h" \ "$(OX)\markdown_.c":"$(OX)\markdown.h" \ "$(OX)\markdown_html_.c":"$(OX)\markdown_html.h" \ "$(OX)\md5_.c":"$(OX)\md5.h" \ "$(OX)\merge_.c":"$(OX)\merge.h" \ "$(OX)\merge3_.c":"$(OX)\merge3.h" \ "$(OX)\moderate_.c":"$(OX)\moderate.h" \ "$(OX)\name_.c":"$(OX)\name.h" \ "$(OX)\patch_.c":"$(OX)\patch.h" \ "$(OX)\path_.c":"$(OX)\path.h" \ | > | 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 | "$(OX)\loadctrl_.c":"$(OX)\loadctrl.h" \ "$(OX)\login_.c":"$(OX)\login.h" \ "$(OX)\lookslike_.c":"$(OX)\lookslike.h" \ "$(OX)\main_.c":"$(OX)\main.h" \ "$(OX)\manifest_.c":"$(OX)\manifest.h" \ "$(OX)\markdown_.c":"$(OX)\markdown.h" \ "$(OX)\markdown_html_.c":"$(OX)\markdown_html.h" \ "$(OX)\match_.c":"$(OX)\match.h" \ "$(OX)\md5_.c":"$(OX)\md5.h" \ "$(OX)\merge_.c":"$(OX)\merge.h" \ "$(OX)\merge3_.c":"$(OX)\merge3.h" \ "$(OX)\moderate_.c":"$(OX)\moderate.h" \ "$(OX)\name_.c":"$(OX)\name.h" \ "$(OX)\patch_.c":"$(OX)\patch.h" \ "$(OX)\path_.c":"$(OX)\path.h" \ |
| ︙ | ︙ |
Changes to www/alerts.md.
| ︙ | ︙ | |||
309 310 311 312 313 314 315 | <a id="advanced"></a> ## Advanced Email Setups Fossil offers several methods of sending email: 1. Pipe the email message text into a command. | | | 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 | <a id="advanced"></a> ## Advanced Email Setups Fossil offers several methods of sending email: 1. Pipe the email message text into a command. 2. Store email messages as entries in an SQLite database. 3. Store email messages as individual files in a directory. 4. Send emails to an SMTP relay. 5. Send emails directly to the recipients via SMTP. This wide range of options allows Fossil to talk to pretty much any SMTP setup. |
| ︙ | ︙ | |||
388 389 390 391 392 393 394 | The self-hosting Fossil repository at <https://fossil-scm.org/> currently uses this method rather than [the pipe method](#pipe) because it is running inside of a restrictive [chroot jail][cj] which is unable to hand off messages to the local MTA directly. When you configure a Fossil server this way, it adds outgoing email | | | 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 | The self-hosting Fossil repository at <https://fossil-scm.org/> currently uses this method rather than [the pipe method](#pipe) because it is running inside of a restrictive [chroot jail][cj] which is unable to hand off messages to the local MTA directly. When you configure a Fossil server this way, it adds outgoing email messages to an SQLite database file. A separate daemon process can then extract those messages for further disposition. Fossil includes a copy of [the daemon](/file/tools/email-sender.tcl) used on `fossil-scm.org`: it is just a short Tcl script that continuously monitors this database for new messages and hands any that it finds off to a local MTA using the same [pipe to MTA protocol](#pipe) as above. |
| ︙ | ︙ |
Changes to www/caps/ref.html.
| ︙ | ︙ | |||
250 251 252 253 254 255 256 |
<th>TktFmt</th>
<td>
Create new ticket report formats. Note that although this allows
the user to provide SQL code to be run in the server’s context,
and this capability is given to the untrusted “anonymous” user
category by default, this is a safe capability to give to users
because it is internally restricted to read-only queries on the
| | | 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 |
<th>TktFmt</th>
<td>
Create new ticket report formats. Note that although this allows
the user to provide SQL code to be run in the server’s context,
and this capability is given to the untrusted “anonymous” user
category by default, this is a safe capability to give to users
because it is internally restricted to read-only queries on the
tickets table only. (This restriction is done with an SQLite
authorization hook, not by any method so weak as SQL text
filtering.) Mnemonic: new <b>t</b>icket report.
</td>
</tr>
<tr id="u">
<th>u</th>
|
| ︙ | ︙ |
Changes to www/changes.wiki.
1 2 | <title>Change Log</title> | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
<title>Change Log</title>
<h2 id='v2_26'>Changes for version 2.26 (pending)</h2>
* Enhanced the --from option on "[/help?cmd=diff|fossil diff]" so that
it optionally accepts a directory name as its argument, and uses files
under that directory as the baseline for the diff.
* Added the [/help?cmd=/ckout|/ckout web page] to provide information
about pending changes in a working check-out
* The [/help?cmd=ui|fossil ui] command defaults to using the
[/help?cmd=/ckout|/ckout page] as its start page. Or, if the
"--from PATH" option is present, the default start page becomes
"/ckout?exbase=PATH".
* Added the [/help?cmd=merge-info|fossil merge-info] command and especially
the --tk option to that command, to provide analysis of the most recent
merge or update operation.
* Issue a warning if a user tries to commit on a check-in where the
branch has been changed.
* When a merge conflict occurs, a new section is added to the conflict
text that shows Fossil's suggested resolution to the conflict.
* Add the "Hide diffs/Show diffs" toggle to web-UI diff pages that show
diffs of multiple files.
* Enhancements to the [/help?cmd=/timeline|/timeline page]:
<ol type="a">
<li> Added the "ml=" ("Merge-in List") query parameter that works
like "rl=" ("Related List") but adds "mionly" style related
check-ins instead of the full "rel" style.
<li> For "tl=", "rl=", and "ml=", the order of the branches in the
graph now tries to match the order of the branches named in
the list.
<li> The "ms=" ("Match Style") query parameter is honored for
"tl=", "rl=", and "ml=".
<li> New query parameter "sl=BRANCHLIST" ("Sort List") strives to
put branches in the specified order in the graph. This
overrides any "tl=" or similar ordering.
<li> In the various "from=","to=" query formats, if the one of the
end points is an ancestor of the other, then the "rel" modifier
omits check-ins that are not ancestors of the newer endpoint.
<li> For "tl=" and similar query parameters, if the pattern contains
GLOB characters, then the matching style ("ms=") is set to GLOB
automatically and the "ms=" query parameter can be omitted.
</ol>
* Added the [/help?cmd=/clusterlist|/clusterlist page] for analysis
and debugging
* Fix a bug in [/help?cmd=patch|fossil patch create] that causes
[/help?cmd=revert|fossil revert] operations that happened on individual
files after a [/help?cmd=merge|fossil merge] to be omitted from the
patch.
<h2 id='v2_25'>Changes for version 2.25 (2024-11-06)</h2>
* The "[/help?cmd=ui|fossil ui /]" command now works even for repositories
that have non-ASCII filenames
* Add the [/help?cmd=tree|fossil tree] command.
* On case-insensitive filesystems, store files using the filesystem's
preferred case rather than the case typed in by the user.
* Change the name "fossil cherry-pick" command to "fossil cherrypick",
which is more familiar to Git users. Retain the legacy name for
compatibility.
* Add new query parameters to the [/help?cmd=/timeline|/timeline page]:
d2=, p2=, and dp2=.
* Add options to the [/help?cmd=tag|fossil tag] command that will list tag values.
* Add the -b|--brief option to the [/help?cmd=status|fossil status] command.
* Add ability to upload unversioned files via the [/help?cmd=/uvlist|/uvlist page].
* Add history search to the [/help?cmd=/chat|/chat page].
* Add Unix socket support to the [/help?cmd=server|server command].
* On Windows, use the root certificates managed by the operating system
(requires OpenSSL 3.2.0 or greater).
* Take into account zero-width and double-width unicode characters when
formatting the command-line timeline.
* Update the built-in SQLite to version 3.47.0. Precompiled binaries are
linked against OpenSSL 3.4.0.
* Numerous minor fixes and additions.
<h2 id='v2_24'>Changes for version 2.24 (2024-04-23)</h2>
* Apache change work-around → As part of a security fix, the Apache webserver
mod_cgi module has stopped relaying the Content-Length field of the HTTP
|
| ︙ | ︙ |
Changes to www/fossil-is-not-relational.md.
| ︙ | ︙ | |||
129 130 131 132 133 134 135 | any given artifact into the data store, the timestamp of each such change, the inheritance tree of checkins, and many other pieces of metadata. - Raw file content of versioned files. These data are external to artifacts, which refer to them by their hashes. How they are stored is not the concern of the data model, but (spoiler alert!) Fossil | | | 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | any given artifact into the data store, the timestamp of each such change, the inheritance tree of checkins, and many other pieces of metadata. - Raw file content of versioned files. These data are external to artifacts, which refer to them by their hashes. How they are stored is not the concern of the data model, but (spoiler alert!) Fossil stores them in an SQLite database, one record per distinct hash, in its `blob` table (which we will cover more very soon). Non-SCM-relevant state includes: - Fossil's list of users and their metadata (permissions, email address, etc.). Artifacts themselves reference users only by their user names. Artifacts neither care whether, nor guarantee that, user |
| ︙ | ︙ |
Changes to www/fossil-v-git.wiki.
| ︙ | ︙ | |||
594 595 596 597 598 599 600 | <h3 id="checkouts">2.6 One vs. Many Check-outs per Repository</h3> Because Git commingles the repository data with the initial checkout of that repository, the default mode of operation in Git is to stick to that single work/repo tree, even when that's a shortsighted way of working. | | | 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 | <h3 id="checkouts">2.6 One vs. Many Check-outs per Repository</h3> Because Git commingles the repository data with the initial checkout of that repository, the default mode of operation in Git is to stick to that single work/repo tree, even when that's a shortsighted way of working. Fossil doesn't work that way. A Fossil repository is an SQLite database file which is normally stored outside the working checkout directory. You can [/help?cmd=open | open] a Fossil repository any number of times into any number of working directories. A common usage pattern is to have one working directory per active working branch, so that switching branches is done with a <tt>cd</tt> command rather than by checking out the branches successively in a single working directory. |
| ︙ | ︙ |
Changes to www/gitusers.md.
| ︙ | ︙ | |||
54 55 56 57 58 59 60 | underneath your working directory. This difference shows up in several separate places when it comes to moving from Git to Fossil. #### <a id="cwork" name="scw"></a> Checkout Workflows | | | 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | underneath your working directory. This difference shows up in several separate places when it comes to moving from Git to Fossil. #### <a id="cwork" name="scw"></a> Checkout Workflows A Fossil repository is an SQLite database storing the entire history of a project. It is not normally stored inside the working tree. A Fossil working tree — [also called a check-out](./glossary.md#check-out) — is a directory that contains a snapshot of your project that you are currently working on, extracted for you from the repository database file by the `fossil` program. There are ways to |
| ︙ | ︙ | |||
146 147 148 149 150 151 152 | destructive!) This Fossil command does not remove the managed files, and unless you give the `--force` option, it won’t let you close the check-out with uncommitted changes to those managed files. The `close` command also refuses to run without `--force` when you have certain other precious per-checkout data that Fossil stores in the | | | 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | destructive!) This Fossil command does not remove the managed files, and unless you give the `--force` option, it won’t let you close the check-out with uncommitted changes to those managed files. The `close` command also refuses to run without `--force` when you have certain other precious per-checkout data that Fossil stores in the `.fslckout` file at the root of a check-out directory. This is an SQLite database that keeps track of local state such as what version you have checked out, the contents of the [stash] for that working directory, the [undo] buffers, per-checkout [settings][set], and so forth. The stash and undo buffers are considered precious uncommitted changes, so you have to force Fossil to discard these as part of closing the check-out. |
| ︙ | ︙ |
Changes to www/glossary.md.
| ︙ | ︙ | |||
346 347 348 349 350 351 352 |
(Contrast the Cygwin and WSL Fossil binaries, which use POSIX file
naming rules.)
* In the same way that one cannot extract files from a zip archive
without having a copy of that zip file, one cannot make check-outs
without access to the repository file or a clone thereof.
| | | 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 |
(Contrast the Cygwin and WSL Fossil binaries, which use POSIX file
naming rules.)
* In the same way that one cannot extract files from a zip archive
without having a copy of that zip file, one cannot make check-outs
without access to the repository file or a clone thereof.
* Because a Fossil repository is an SQLite database file, the same
rules for avoiding data corruption apply to it. In particular, it is
[nearly a hard requirement][h2cflp] that the repository clone be on
the same machine as the one where you make check-outs and the
subsequent check-ins.
That said, the relative locations of the repo and the check-out
within the local file system are arbitrary. The repository may be
|
| ︙ | ︙ |
Changes to www/index.wiki.
| ︙ | ︙ | |||
82 83 84 85 86 87 88 |
atomic even if interrupted by a power loss or system crash.
Automatic [./selfcheck.wiki | self-checks] verify that all aspects of
the repository are consistent prior to each commit.
8. <b>Free and Open-Source</b> — [../COPYRIGHT-BSD2.txt|2-clause BSD license].
<hr>
| | | | | | 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
atomic even if interrupted by a power loss or system crash.
Automatic [./selfcheck.wiki | self-checks] verify that all aspects of
the repository are consistent prior to each commit.
8. <b>Free and Open-Source</b> — [../COPYRIGHT-BSD2.txt|2-clause BSD license].
<hr>
<h3>Latest Release: 2.25 ([/timeline?c=version-2.25|2024-11-06])</h3>
* [/uv/download.html|Download]
* [./changes.wiki#v2_25|Change Summary]
* [/timeline?p=version-2.25&bt=version-2.24&y=ci|Check-ins in version 2.25]
* [/timeline?df=version-2.25&y=ci|Check-ins derived from the 2.25 release]
* [/timeline?t=release|Timeline of all past releases]
<hr>
<h3>Quick Start</h3>
1. [/uv/download.html|Download] or install using a package manager or
[./build.wiki|compile from sources].
|
| ︙ | ︙ |
Changes to www/qandc.wiki.
| ︙ | ︙ | |||
140 141 142 143 144 145 146 | is not the point. Fossil has several key features that Trac lacks and that I need: most notably the fact that fossil supports disconnected operation. As for bloat: Fossil is a single self-contained executable. You do not need any other packages (diff, patch, merge, cvs, svn, rcs, git, python, perl, tcl, apache, | | | 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | is not the point. Fossil has several key features that Trac lacks and that I need: most notably the fact that fossil supports disconnected operation. As for bloat: Fossil is a single self-contained executable. You do not need any other packages (diff, patch, merge, cvs, svn, rcs, git, python, perl, tcl, apache, SQLite, and so forth) in order to run fossil. Fossil runs just fine in a chroot jail all by itself. And the self-contained fossil executable is much less than 1MB in size. (Update 2015-01-12: Fossil has grown in the years since the previous sentence was written but is still much less than 2MB according to "size" when compiled using -Os on x64 Linux.) Fossil is the very opposite of bloat. </div> |
Changes to www/ssl-server.md.
| ︙ | ︙ | |||
10 11 12 13 14 15 16 | proxy that handled the SSL/TLS decryption/encryption and passed cleartext down to Fossil. [0]: ./ssl.wiki [1]: /timeline?c=b05cb4a0e15d0712&y=ci&n=13 Beginning in [late December 2021](/timeline?c=f6263bb64195b07f&y=a&n=13), | | | < < < < | | | < < < | | | > | | | > > | | > > > | > > > | > > | > | > > > > > > > > | < < < > > > > | > > | < | > > > > | > > > > > > > > | > > > < | > > > < < < > | > | | | | | > > | | | | > > > > > > > > | > > > > > > | > | > > > > > > | > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > < < < | > | | | | < < | > | | > | > > | > > | > > | > | | | > > | 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 |
proxy that handled the SSL/TLS decryption/encryption and passed cleartext
down to Fossil.
[0]: ./ssl.wiki
[1]: /timeline?c=b05cb4a0e15d0712&y=ci&n=13
Beginning in [late December 2021](/timeline?c=f6263bb64195b07f&y=a&n=13),
Fossil servers are now able to converse directly over TLS. Commands like
* "[fossil server](/help?cmd=server)"
* "[fossil ui](/help?cmd=ui)", and
* "[fossil http](/help?cmd=http)"
may now handle the encryption natively when suitably configured, without
requiring a third-party proxy layer.
## <a id="usage"></a>Usage
To put any of the Fossil server commands into SSL/TLS mode, simply
add the "`--cert`" command-line option:
fossil ui --cert unsafe-builtin
Here, we are passing the magic name "unsafe-builtin" to cause Fossil to
use a [hard-coded self-signed cert][hcssc] rather than one obtained from
a recognized [Certificate Authority][CA], or "CA".
As the name implies, this self-signed cert is _not secure_ and should
only be used for testing. Your web browser is likely to complain
bitterly about it and will refuse to display the pages using the
"unsafe-builtin" cert until you placate it. The complexity of the
ceremony demanded depends on how paranoid your browser’s creators have
decided to be. It may require as little as clicking a single big "I know
the risks" type of button, or it may require a sequence be several
clicks designed to discourage the “yes, yes, just let me do the thing”
crowd lest they run themselves into trouble by disregarding well-meant
warnings.
Our purpose here is to show you an alternate path that will avoid the
issue entirely, not weigh in on which browser handles self-signed
certificates best.
[CA]: https://en.wikipedia.org/wiki/Certificate_authority
[hcssc]: /info/c2a7b14c3f541edb96?ln=89-116
## <a id="about"></a>About Certs
The X.509 certificate system used by browsers to secure TLS connections
is based on asymmetric public-key cryptography. The methods for
obtaining one vary widely, with a resulting tradeoff we may summarize as
trustworthiness versus convenience, the latter characteristic falling as
the former rises.(^No strict correlation exists. CAs have invented
highly inconvenient certification schemes that offer little additional
real-world trustworthiness. Extreme cases along this axis may be fairly
characterized as [security theater][st]. We focus in this document on
well-balanced trade-offs between decreasing convenience and useful
levels of trustworthiness gained thereby.)
The self-signed method demonstrated above offers approximately zero
trustworthiness, though not zero _value_ since it does still provide
connection encryption.
More trustworthy methods are necessarily less convenient. One such is to
send your public key and the name of the domain you want to protect to a
recognized CA, which then performs one or more tests to convince itself
that the requester is in control of that domain. If the CA’s tests all
pass, it produces an X.509 certificate bound to that domain, which
includes assorted other information under the CA’s digital signature
attesting to the validity of the document’s contents. The result is sent
back to the requester, which may then use it to transitively attest to
these tests’ success: presuming one cannot fake the type of signature
used, the document must have been signed by the trusted, recognized CA.
There is one element of the assorted information included with a
certificate that is neither supplied by the requester nor rubber-stamped
on it in passing by the CA. It also generates a one-time key pair and
stores the public half in the certificate. The cryptosystem this keypair
is intended to work with varies both by the CA and by time, as older
systems become obsolete. Details aside, the CA then puts this matching
private half of the key in a separate file, often encrypted under a
separate cryptosystem for security.
SSL/TLS servers need both resulting halves to make these attestations,
but they send only the public half to the client when establishing the
connection. The client then makes its own checks to determine whether it
trusts the attestations being made.
A properly written and administered server never releases the private
key to anyone. Ideally, it goes directly from the CA to the requesting
server and never moves from there; then when it expires, the server
deletes it permanently.
[st]: https://en.wikipedia.org/wiki/Security_theater
## <a id="startup"></a>How To Tell Fossil About Your Cert And Private Key
As we saw [above](#usage),
if you do not have your own cert and private key, you can ask Fossil
to use "unsafe-builtin", which is a self-signed cert that is built into
Fossil. This is wildly insecure, since the private key is not really private;
it is [in plain sight][hcssc] in the Fossil
source tree for anybody to read. <b>Never add the private key that is
built into Fossil to your OS's trust store</b> as doing so will severely
compromise your computer.[^ssattack] This built-in cert is only useful for testing.
If you want actual security, you will need to come up with your own private
key and cert.
Fossil wants to read certs and public keys in the
[PEM format](https://en.wikipedia.org/wiki/Privacy-Enhanced_Mail).
PEM is a pure ASCII text format. The private key consists of text
like this:
-----BEGIN PRIVATE KEY-----
*base-64 encoding of the private key*
-----END PRIVATE KEY-----
Similarly, a PEM-encoded cert will look like this:
-----BEGIN CERTIFICATE-----
*base-64 encoding of the certificate*
-----END CERTIFICATE-----
In both formats, text outside of the delimiters is ignored. That means
that if you have a PEM-formatted private key and a separate PEM-formatted
certificate, you can concatenate the two into a single file, and the
individual components will still be easily accessible.
### <a id="cat"></a>Separate or Concatenated?
Given a single concatenated file that holds both your private key and your
cert, you can hand it off to the "[fossil server](/help?cmd=server)"
command using the `--cert` option, like this:
fossil server --port 443 --cert mycert.pem /home/www/myproject.fossil
The command above is sufficient to run a fully-encrypted web site for
the "myproject.fossil" Fossil repository. This command must be run as
root, since it wants to listen on TCP port 443, and only root processes are
allowed to do that. This is safe, however, since before reading any
information off of the wire, Fossil will [put itself inside a chroot
jail](./chroot.md) at `/home/www` and drop all root privileges.
This method of combining your cert and private key into a single big PEM
file carries risks, one of which is that the system administrator must
make both halves readable by the user running the Fossil server. Given
the chroot jail feature, a more secure scheme separates the halves so
that only root can read the private half, which then means that when
Fossil drops its root privileges, it becomes unable to access the
private key on disk. Fossil’s `server` feature includes the `--pkey`
option to allow for that use case:
fossil server --port 443 --cert fullchain.pem --pkey privkey.pem /home/www/myproject.fossil
[^ssattack]: ^How, you ask? Because the keys are known, they can be used
to provide signed certificates for **any** other domain. One foolish
enough to tell their OS’s TLS mechanisms to trust the signing
certificate is implicitly handing over all TLS encryption controls
to any attacker that knows they did this. Don’t do it.
### <a id="chain"></a>Chains and Links
The file name “`fullchain.pem`” used above is a reference to a term of
art within this world of TLS protocols and their associated X.509
certificates. Within the simplistic scheme originally envisioned by the
creators of SSL — the predecessor to TLS — we were all expected to agree
on a single set of CA root authorities, and we would all agree to get
our certificates from one of them. The real world is more complicated:
* The closest we have to universal acceptance of CAs is via the
[CA/Browser Forum][CAB], and even within its select membership there
is continual argument over which roots are trustworthy. (Hashing
that out is arguably this group’s key purpose.)
* CAB’s decision regarding trustworthiness may not match that of any
given system’s administrator. There are solid, defensible reasons to
prune back the stock CA root set included with your browser, then to
augment it with ones CAB _doesn’t_ trust.
* TLS isn’t limited to use between web browsers and public Internet
sites. Several common use cases preclude use of the process CAB
envisions, with servers able to contact Internet-based CA roots as
part of proving their identity. Different use cases demand different
CA root authority stores.
The most common of these divergent cases are servers behind strict
firewalls and edge devices that never interact with the public
Internet. This class ranges from cheap home IoT devices to the
internal equipment managed by IT for a massive global corporation.
Your private Fossil server is liable to fall into that last category.
This may then require that you generate a more complicated “chain” of
certificates for Fossil to use here, without which the client may not be
able to get back to a CA root it trusts. This is true regardless of
whether that client is another copy of Fossil or a web browser
traversing Fossil’s web UI, though that fact complicates matters by
allowing for multiple classes of client, each of which may have their
own rules for modifying the stock certificate scheme.
This is distressingly common, in fact: Fossil links to OpenSSL to
provide its TLS support, but there is a good chance that your browser
uses another TLS implementation entirely. They may or may not agree on a
single CA root store.
How you accommodate all this complexity varies by the CA and other
details. As but one example, Firefox’s “View Certificate” feature offers
_two_ ways to download a given web site’s certificate: the cert alone or
the “chain” leading back to the root. Depending on the use case, the
standalone certificate might suffice, or you might need some type of
cert chain. Complicating this is that the last link in the chain may be
left off when it is for a mutually trusted CA root, implicitly
completing the chain.
[CAB]: https://en.wikipedia.org/wiki/CA/Browser_Forum
## <a id="acme"></a>The ACME Protocol
The [ACME Protocol][2] simplifies all this by automating the process of
proving to a recognized public CA that you are in control of a given
website. Without this proof, no valid CA will issue a cert for that
domain, as that allows fraudulent impersonation.
The primary implementation of ACME is [certbot], a product of the Let’s
Encrypt organization.
Here is, in a nutshell, what certbot will do to obtain your cert:
1. It sends your "signing request" (the document that contains
your public key and your domain name) to the CA.
2. After receiving the signing request, the CA needs to verify that
you control the domain of the cert. One of several methods certbot has
for accomplishing this is to create a secret token and place it at
a well-known location, then tell the CA about it over ACME.
3. The CA then tries pulling that token, which if successful proves
that the requester is able to create arbitrary data on the server,
implicitly proving control over that server. This must be done
over the unencrypted HTTP protocol since TLS isn’t working yet.
4. If satisfied by this proof of control, the CA then creates the
keypair described above and bakes the public half into the
certificate it signs. It then sends this and the private half of
the key back to certbot.
5. Certbot stores these halves separately for the reasons sketched
out above.
6. It then deletes the secret one-time-use token it used to prove
domain control. ACME’s design precludes replay attacks.
In order for all of this to happen, certbot needs to be able to create
a subdirectory named ".well-known", within a directory you specify,
then populate that subdirectory with a token file of some kind. To support
this, the "[fossil server](/help?cmd=server)" and
"[fossil http](/help?cmd=http)" commands have the --acme option.
When specified, Fossil sees a URL where the path
begins with ".well-known", then instead of doing its normal processing, it
looks for a file with that pathname and returns it to the client. If
the "server" or "http" command is referencing a single Fossil repository,
then the ".well-known" sub-directory should be in the same directory as
the repository file. If the "server" or "http" command are run against
a directory full of Fossil repositories, then the ".well-known" sub-directory
should be in that top-level directory.
Thus, to set up a project website, you should first run Fossil in ordinary
unencrypted HTTP mode like this:
fossil server --port 80 --acme /home/www/myproject.fossil
Then you create your public/private key pair and run certbot, giving it
a --webroot of /home/www. Certbot will create the sub-directory
named "/home/www/.well-known" and put token files there, which the CA
will verify. Then certbot will store your new cert in a particular file.
Once certbot has obtained your cert, you may either pass the two halves
to Fossil separately using the `--pkey` and `--cert` options described
above, or you may concatenate them and pass that via `--cert` alone.
[2]: https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment
[certbot]: https://certbot.eff.org
|
Changes to www/sync.wiki.
| ︙ | ︙ | |||
787 788 789 790 791 792 793 794 795 796 797 798 799 800 | <li><b>ci-unlock</b> <i>CLIENT-ID</i> A client sends the "ci-unlock" pragma to the server after a successful commit. This instructs the server to release any lock on any check-in previously held by that client. The ci-unlock pragma helps to avoid false-positive lock warnings that might arise if a check-in is aborted and then restarted on a branch. </ol> <h3 id="comment">3.12 Comment Cards</h3> Any card that begins with "#" (ASCII 0x23) is a comment card and is silently ignored. | > > > > > > > > | 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 | <li><b>ci-unlock</b> <i>CLIENT-ID</i> A client sends the "ci-unlock" pragma to the server after a successful commit. This instructs the server to release any lock on any check-in previously held by that client. The ci-unlock pragma helps to avoid false-positive lock warnings that might arise if a check-in is aborted and then restarted on a branch. <li><b>req-clusters</b> A client sends the "req-clusters" pragma to the server to ask the server to reply with "igot" cards for every [./fileformat.wiki#cluster|cluster artifact] that it holds. The client typically does this when it thinks that it might be attempting to pull a long chain of cluster artifacts. Sending the artifacts all at once can dramatically reduce the number of round trip messages needed to complete the synchronization. </ol> <h3 id="comment">3.12 Comment Cards</h3> Any card that begins with "#" (ASCII 0x23) is a comment card and is silently ignored. |
| ︙ | ︙ |