Fossil

Check-in [02cdfa5e08]
Login

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: 02cdfa5e0866368cbb2d5bf881b544fbcf924c6ad76d20f42c91f62ae266eb7b
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
Unified Diff Ignore Whitespace Patch
Changes to BUILD.txt.
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 generates 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







|







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
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 aren't pinning to a more stable version of Alpine 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 basic Alpine Linux 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
### Alpine environment 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 ] ;                                             \







|







|











|





|
|
|







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.25
|
1
2.26
Changes to extsrc/shell.c.
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include <stdio.h>
#include <assert.h>
#include <math.h>
#include "sqlite3.h"
typedef sqlite3_int64 i64;
typedef sqlite3_uint64 u64;
typedef unsigned char u8;
#if SQLITE_USER_AUTHENTICATION
# include "sqlite3userauth.h"
#endif
#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>







<
<
<







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
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
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 = malloc( (sz1+1)*sizeof(b1[0]) );
  b2 = 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);
  }
  free(b1);
  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 = malloc( (sz1+1)*sizeof(b1[0]) );
  b2 = 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);
  }
  free(b1);
  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 = malloc( sz*sizeof(wchar_t) );
    if( b1==0 ) return 0;









    _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. */







|
|







|
|















|
|







|
|













|

>
>
>
>
>
>
>
>
>
|
|
|
|
>







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
524
525
526
527
528
529
530
531
532













533
534
535
536
537

538
539
540
541
542
543
544
*/
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{
    /* When writing to the command-prompt in Windows, it is necessary
    ** to use O_U8TEXT to render Unicode U+0080 and greater.  Go ahead
    ** use O_U8TEXT for everything in text mode.
    */
    int sz = (int)strlen(z);
    wchar_t *b1 = malloc( (sz+1)*sizeof(wchar_t) );
    if( b1==0 ) return 0;
    sz = MultiByteToWideChar(CP_UTF8, 0, z, sz, b1, sz);
    b1[sz] = 0;













    _setmode(_fileno(out), _O_U8TEXT);
    if( UseBinaryWText(out) ){
      piecemealOutput(b1, sz, out);
    }else{
      fputws(b1, out);

    }
    sqlite3_free(b1);
    return 0;
  }
}









|
|
<


|



>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
|
|
>







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
2352
2353
2354
2355
2356
2357
2358
2359
**    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.
** Two 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







|







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
5075
5076
5077
5078
5079
5080
5081
5082
5083
5084
5085
int sqlite3_percentile_init(
  sqlite3 *db, 
  char **pzErrMsg, 
  const sqlite3_api_routines *pApi
){
  int rc = SQLITE_OK;
  unsigned int i;
#if defined(SQLITE3_H) || defined(SQLITE_STATIC_PERCENTILE)
  (void)pApi;      /* Unused parameter */
#else
  SQLITE_EXTENSION_INIT2(pApi);
#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,







|
|

|







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
6836
6837
6838
6839
6840
6841
6842
6843

6844

6845
6846
6847
6848
6849
6850
6851

6852

6853
6854
6855
6856
6857
6858
6859

6860

6861
6862
6863
6864
6865
6866
6867
        assert( op==SQLITE_INDEX_CONSTRAINT_OFFSET );
        aIdx[4] = i;
        idxNum |= 0x40;
      }
      continue;
    }
    if( pConstraint->iColumn<SERIES_COLUMN_START ){
      if( pConstraint->iColumn==SERIES_COLUMN_VALUE ){
        switch( op ){
          case SQLITE_INDEX_CONSTRAINT_EQ:
          case SQLITE_INDEX_CONSTRAINT_IS: {
            idxNum |=  0x0080;
            idxNum &= ~0x3300;
            aIdx[5] = i;
            aIdx[6] = -1;

            bStartSeen = 1;

            break;
          }
          case SQLITE_INDEX_CONSTRAINT_GE: {
            if( idxNum & 0x0080 ) break;
            idxNum |=  0x0100;
            idxNum &= ~0x0200;
            aIdx[5] = i;

            bStartSeen = 1;

            break;
          }
          case SQLITE_INDEX_CONSTRAINT_GT: {
            if( idxNum & 0x0080 ) break;
            idxNum |=  0x0200;
            idxNum &= ~0x0100;
            aIdx[5] = i;

            bStartSeen = 1;

            break;
          }
          case SQLITE_INDEX_CONSTRAINT_LE: {
            if( idxNum & 0x0080 ) break;
            idxNum |=  0x1000;
            idxNum &= ~0x2000;
            aIdx[6] = i;







|







>

>







>

>







>

>







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
14172
14173
14174
14175
14176
14177
14178
14179

    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 ){
        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() */







|







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
16240




















16241
16242
16243
16244
16245
16246
16247
16248
16249
16250
16251
16252
16253


16254
16255
16256
16257
16258
16259
16260
16261
16262
16263
16264
16265
16266
16267
16268
16269
































16270
16271
16272
16273
16274
16275
16276
**         -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.  




















*/
#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 */


  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 */
};

































/*
** 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);







|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>













>
>
















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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

16334
16335
16336
16337
16338

16339
16340
16341
16342
16343
16344
16345
static void vfstrace_printf(
  vfstrace_info *pInfo,
  const char *zFormat,
  ...
){
  va_list ap;
  char *zMsg;

  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;







>
|
|
|
|
|
>







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

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
/*
** 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);
  }

  vfstrace_printf(pInfo, "%s.xShmLock(%s,ofst=%d,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;

  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;

  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;

  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;
}








>













>











>
>
>
>
>
>
>
>
>
>





>








>
|
|
>
>
>
>
>
>














>









>







>







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



16996


16997
16998
16999
17000



17001


17002
17003
17004
17005
17006
17007
17008
17009




17010


17011
17012
17013
17014
17015
17016
17017

/*
** 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;



  return pRoot->xCurrentTime(pRoot, pTimeOut);


}
static int vfstraceCurrentTimeInt64(sqlite3_vfs *pVfs, sqlite3_int64 *pTimeOut){
  vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
  sqlite3_vfs *pRoot = pInfo->pRootVfs;



  return pRoot->xCurrentTimeInt64(pRoot, pTimeOut);


}

/*
** Return th3 most recent error code and message
*/
static int vfstraceGetLastError(sqlite3_vfs *pVfs, int iErr, char *zErr){
  vfstrace_info *pInfo = (vfstrace_info*)pVfs->pAppData;
  sqlite3_vfs *pRoot = pInfo->pRootVfs;




  return pRoot->xGetLastError(pRoot, iErr, zErr);


}

/*
** Override system calls.
*/
static int vfstraceSetSystemCall(
  sqlite3_vfs *pVfs,







>











>











>
>









>
>
>
|
>
>




>
>
>
|
>
>



|

|


>
>
>
>
|
>
>







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
21940
21941
21942
21943
21944
21945
21946
21947
21948
** 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) ){
    char c = *z;
    if( (c & 0x80) == 0 ){
      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;







|
|







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
22009
22010
22011
22012
22013
22014
22015
22016
22017
22018
22019
22020
22021
22022
22023
22024
22025
22026
22027
22028
22029
22030
22031
22032
22033
22034
22035
22036
22037
22038
22039
22040
22041
22042
22043
22044
22045
22046
22047
22048
22049
22050
22051
22052
22053
22054
22055
    }
    z = pcEnd;
  }
  sqlite3_fputs(zq, out);
}

/*
** Output the given string as a quoted according to JSON quoting rules.
*/
static void output_json_string(FILE *out, const char *z, i64 n){
  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 = *(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 ){
      sqlite3_fprintf(out, "u%04x", c);
    }else{
      ace[1] = (char)c;
      sqlite3_fputs(ace+1, out);
    }
  }
  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
24789
24790
24791

24792
24793
24794
24795
24796
24797
24798
    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;
    }
    sqlite3_free(zErr);
    free(zQ2);
  }

  return rc;
}

/*
** Text of help messages.
**
** The help text for each individual command begins with a line that starts







<


>







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
25659

25660
25661
25662
25663
25664
25665
25666
  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

/*
** Readline completion callbacks
*/
static char *readline_completion_generator(const char *text, int state){
  static sqlite3_stmt *pStmt = 0;
  char *zRet;
  if( state==0 ){







|
>







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
25697



25698


25699
25700
25701
25702
25703
25704

25705

25706
25707
25708
25709
25710
25711
25712
}

#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,



                                 void *pUserData){


  i64 nLine = strlen(zLine);
  i64 i, iStart;
  sqlite3_stmt *pStmt = 0;
  char *zSql;
  char zBuf[1000];


  UNUSED_PARAMETER(pUserData);

  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"







|
>
>
>
|
>
>






>

>







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
26395
26396
26397





26398
26399
26400
26401
26402

26403
26404
26405
26406
26407
26408
26409
# 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;





  sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0);
  if( pStmt && sqlite3_step(pStmt)==SQLITE_ROW ){
    res = sqlite3_column_int(pStmt,0);
  }
  sqlite3_finalize(pStmt);

  return res;
}

#if SQLITE_SHELL_HAVE_RECOVER
/*
** Convert a 2-byte or 4-byte big-endian integer into a native integer
*/







|


>
>
>
>
>
|




>







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
26505
26506
26507
26508
26509
26510
26511
26512
26513
26514
26515





























































































26516
26517
26518
26519
26520
26521
26522
    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++){
    char *zSql = sqlite3_mprintf(aQuery[i].zSql, zSchemaTab);
    int val = db_int(p->db, zSql);
    sqlite3_free(zSql);
    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 */






























































































/*
** Print the given string as an error message.
*/
static void shellEmitError(const char *zErr){
  sqlite3_fprintf(stderr,"Error: %s\n", zErr);
}







<
|
<








>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
28072
28073
28074
28075
28076
28077
28078
28079
28080
28081
28082
28083
28084
28085
28086
28087
28088
28089
28090
28091
28092
28093
28094
28095
    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, zHasDupes);
    int nDigits = (hasDupes)? db_int(*pDb, 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, 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;







|
|














|







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
29134




29135
29136
29137
29138
29139
29140
29141
      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) ){




      /* 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) ){







|
>
>
>
>







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

30206


30207
30208
30209
30210
30211
30212
30213
      shellDatabaseError(p->db);
      rc = 1;
    }
    close_db(pSrc);
  }else
#endif /* !defined(SQLITE_SHELL_FIDDLE) */


  if( c=='s' && cli_strncmp(azArg[0], "scanstats", 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{







>
|
>
>







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
31302


31303
31304
31305
31306


31307
31308


31309
31310

31311
31312
31313
31314
31315
31316
31317
31318
31319

31320
31321
31322
31323
31324
31325
31326
              }else{
                newOpt |= aLabel[jj].mask;
              }
            }
          }
          if( curOpt!=newOpt ){
            sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,p->db,newOpt);
          }else if( nArg<3 ){


            curOpt = ~newOpt;
          }
          if( newOpt==0 ){
            sqlite3_fputs("+All\n", p->out);


          }else if( newOpt==0xffffffff ){
            sqlite3_fputs("-All\n", p->out);


          }else{
            int jj;

            for(jj=0; jj<ArraySize(aLabel); jj++){
              unsigned int m = aLabel[jj].mask;
              if( !aLabel[jj].bDsply  ) continue;
              if( (curOpt&m)!=(newOpt&m) ){
                sqlite3_fprintf(p->out, "%c%s\n", (newOpt & m)==0 ? '+' : '-',
                      aLabel[jj].zLabel);
              }
            }
          }

          rc2 = isOk = 3;
          break;
        }

        /* sqlite3_test_control(int, db, int) */
        case SQLITE_TESTCTRL_FK_NO_ACTION:
          if( nArg==3 ){







<
>
>
|

|
|
>
>
|
|
>
>

<
>
|
<
|
|
|
<



>







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
31657
31658
31659
31660
31661
31662
31663
31664
31665
31666
31667
31668
31669
31670
31671
31672
31673
31674
31675
31676
31677
31678
31679
31680
31681
31682
31683
31684
31685
31686
31687
31688
31689
31690
31691
31692
31693
31694
31695
31696
31697
31698
31699
31700
31701
31702
31703
31704
31705
31706
31707
31708
31709
31710
31711
31712
31713
31714
31715
31716
31717
31718
31719
31720
31721
31722
31723
31724
31725
31726
      for(ii=1; ii<nArg; ii++){
        sqlite3_create_module(p->db, azArg[ii], 0, 0);
      }
    }
  }else
#endif

#if SQLITE_USER_AUTHENTICATION
  if( c=='u' && cli_strncmp(azArg[0], "user", n)==0 ){
    if( nArg<2 ){
      eputz("Usage: .user SUBCOMMAND ...\n");
      rc = 1;
      goto meta_command_exit;
    }
    open_db(p, 0);
    if( cli_strcmp(azArg[1],"login")==0 ){
      if( nArg!=4 ){
        eputz("Usage: .user login USER PASSWORD\n");
        rc = 1;
        goto meta_command_exit;
      }
      rc = sqlite3_user_authenticate(p->db, azArg[2], azArg[3],
                                     strlen30(azArg[3]));
      if( rc ){
        sqlite3_fprintf(stderr,"Authentication failed for user %s\n", azArg[2]);
        rc = 1;
      }
    }else if( cli_strcmp(azArg[1],"add")==0 ){
      if( nArg!=5 ){
        eputz("Usage: .user add USER PASSWORD ISADMIN\n");
        rc = 1;
        goto meta_command_exit;
      }
      rc = sqlite3_user_add(p->db, azArg[2], azArg[3], strlen30(azArg[3]),
                            booleanValue(azArg[4]));
      if( rc ){
        sqlite3_fprintf(stderr,"User-Add failed: %d\n", rc);
        rc = 1;
      }
    }else if( cli_strcmp(azArg[1],"edit")==0 ){
      if( nArg!=5 ){
        eputz("Usage: .user edit USER PASSWORD ISADMIN\n");
        rc = 1;
        goto meta_command_exit;
      }
      rc = sqlite3_user_change(p->db, azArg[2], azArg[3], strlen30(azArg[3]),
                              booleanValue(azArg[4]));
      if( rc ){
        sqlite3_fprintf(stderr,"User-Edit failed: %d\n", rc);
        rc = 1;
      }
    }else if( cli_strcmp(azArg[1],"delete")==0 ){
      if( nArg!=3 ){
        eputz("Usage: .user delete USER\n");
        rc = 1;
        goto meta_command_exit;
      }
      rc = sqlite3_user_delete(p->db, azArg[2]);
      if( rc ){
        sqlite3_fprintf(stderr,"User-Delete failed: %d\n", rc);
        rc = 1;
      }
    }else{
      eputz("Usage: .user login|add|edit|delete ...\n");
      rc = 1;
      goto meta_command_exit;
    }
  }else
#endif /* SQLITE_USER_AUTHENTICATION */

  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







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







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
31843
31844
31845
31846
31847
31848
31849
31850
*/
static QuickScanState quickscan(char *zLine, QuickScanState qss,
                                SCAN_TRACKER_REFTYPE pst){
  char cin;
  char cWait = (char)qss; /* intentional narrowing loss */
  if( cWait==0 ){
  PlainScan:
    assert( cWait==0 );
    while( (cin = *zLine++)!=0 ){
      if( IsSpace(cin) )
        continue;
      switch (cin){
      case '-':
        if( *zLine!='-' )
          break;







<







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
31895
31896
31897
31898
31899
31900
31901
31902
31903
31904
31905
31906
31907
31908
31909
31910
31911
31912
31913
31914
    while( (cin = *zLine++)!=0 ){
      if( cin==cWait ){
        switch( cWait ){
        case '*':
          if( *zLine != '/' )
            continue;
          ++zLine;
          cWait = 0;
          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 ']':
          cWait = 0;
          CONTINUE_PROMPT_AWAITC(pst, 0);
          qss = QSS_SETV(qss, 0);
          goto PlainScan;
        default: assert(0);
        }
      }
    }







<











<







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
32095



32096
32097
32098
32099
32100
32101
32102
  }

  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);



}

#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).







|
>
>
>







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
32792
32793
32794
32795
32796
32797
32798
32799
32800
      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 ){
      vfstrace_register("trace",0,(int(*)(const char*,void*))sqlite3_fputs,
                        stderr,1);
      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 ){







<
<







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
33134
33135
33136
33137
33138
33139
33140
33141
33142
33143
33144
33145
33146
33147
33148
33149
33150
33151
33152
33153
33154
33155
33156
33157
33158
33159
33160
33161


33162
33163
33164
33165
33166
33167
33168
  }else{
    /* Run commands received from standard input
    */
    if( stdin_is_interactive ){
      char *zHome;
      char *zHistory;
      int nHistory;
#if CIO_WIN_WC_XLATE
# define SHELL_CIO_CHAR_SET (stdout_is_console? " (UTF-16 console I/O)" : "")
#else
# define SHELL_CIO_CHAR_SET ""
#endif
      sqlite3_fprintf(stdout,
            "SQLite version %s %.19s%s\n" /*extra-version-info*/
            "Enter \".help\" for usage hints.\n",
            sqlite3_libversion(), sqlite3_sourceid(), SHELL_CIO_CHAR_SET);
      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
      rl_attempted_completion_function = readline_completion;
#elif HAVE_LINENOISE


      linenoiseSetCompletionCallback(linenoise_completion, NULL);
#endif
      data.in = 0;
      rc = process_input(&data);
      if( zHistory ){
        shell_stifle_history(2000);
        shell_write_history(zHistory);







<
<
<
<
<

|

|
















|

|
>
>







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
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
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
** version 3.47.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

** 03a9703e27c44437c39363d0baf82db4ebc9.

*/

#define SQLITE_CORE 1
#define SQLITE_AMALGAMATION 1
#ifndef SQLITE_PRIVATE
# define SQLITE_PRIVATE static
#endif
/************** Begin file sqliteInt.h ***************************************/
/*


|

















>
|
>

>







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
465
466
467
468
469
470
471
472
473
474
** 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.47.0"
#define SQLITE_VERSION_NUMBER 3047000
#define SQLITE_SOURCE_ID      "2024-10-21 16:30:22 03a9703e27c44437c39363d0baf82db4ebc94538a0f28411c85dda156f82636e"

/*
** 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







|
|
|







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
2941
2942
2943
2944




2945
2946
2947
2948
2949
2950
2951
** 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.




**
** ^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







|



>
>
>
>







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
11199
11200
11201
11202
11203
11204
11205
11206
#  define SQLITE_THREADSAFE 0
# endif
#endif

#if 0
}  /* End of the 'extern "C"' block */
#endif
#endif /* SQLITE3_H */

/******** 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:







|







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
13450
13451
13452
13453
13454
13455
13456

















13457
13458
13459
13460
13461
13462
13463
**
** 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. This API is not available if the specified token matches a
**   prefix query term. In that case both output variables are always set
**   to 0.
**
**   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 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.







<
<
|




>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
14003
14004
14005
14006
14007
14008
14009
14010
*/
#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
# define SQLITE_MAX_FUNCTION_ARG 127
#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,







>
>
>
>


|







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
17030
17031
17032
17033
17034
17035
17036
17037
/************** 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     0x0f  /* 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*);







|







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
17745
17746
17747
17748
17749
17750
17751
17752
17753
17754
17755
17756
17757
17758
17759
17760
17761
17762
17763
17764
17765
17766
17767
17768
17769
17770
17771
17772
17773
17774
17775
17776
17777
17778
17779
17780
17781
17782
17783
17784
17785
17786
17787
17788
17789
17790
17791
17792
*/
#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)

#if defined(SQLITE_USER_AUTHENTICATION)
# warning  "The SQLITE_USER_AUTHENTICATION extension is deprecated. \
 See ext/userauth/user-auth.txt for details."
#endif
#ifdef SQLITE_USER_AUTHENTICATION
/*
** Information held in the "sqlite3" database connection object and used
** to manage user authentication.
*/
typedef struct sqlite3_userauth sqlite3_userauth;
struct sqlite3_userauth {
  u8 authLevel;                 /* Current authentication level */
  int nAuthPW;                  /* Size of the zAuthPW in bytes */
  char *zAuthPW;                /* Password used to authenticate */
  char *zAuthUser;              /* User name used to authenticate */
};

/* Allowed values for sqlite3_userauth.authLevel */
#define UAUTH_Unknown     0     /* Authentication not yet checked */
#define UAUTH_Fail        1     /* User authentication failed */
#define UAUTH_User        2     /* Authenticated as a normal user */
#define UAUTH_Admin       3     /* Authenticated as an administrator */

/* Functions used only by user authorization logic */
SQLITE_PRIVATE int sqlite3UserAuthTable(const char*);
SQLITE_PRIVATE int sqlite3UserAuthCheckLogin(sqlite3*,const char*,u8*);
SQLITE_PRIVATE void sqlite3UserAuthInit(sqlite3*);
SQLITE_PRIVATE void sqlite3CryptFunc(sqlite3_context*,int,sqlite3_value**);

#endif /* SQLITE_USER_AUTHENTICATION */

/*
** typedef for the authorization callback function.
*/
#ifdef SQLITE_USER_AUTHENTICATION
  typedef int (*sqlite3_xauth)(void*,int,const char*,const char*,const char*,
                               const char*, const char*);
#else
  typedef int (*sqlite3_xauth)(void*,int,const char*,const char*,const char*,
                               const char*);
#endif

#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 */







<
<
<
<
<

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<


<
|
<
<
<
|
<







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
17946
17947
17948
17949
17950
17951
17952
17953
17954
17955
  */
  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
#ifdef SQLITE_USER_AUTHENTICATION
  sqlite3_userauth auth;        /* User authentication information */
#endif
};

/*
** A macro to discover the encoding of a database.
*/
#define SCHEMA_ENC(db) ((db)->aDb[0].pSchema->enc)
#define ENC(db)        ((db)->enc)







<
<
<







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
18107
18108
18109
18110
18111
18112
18113
18114
** 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 {
  i8 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 */







|







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
22855
22856
22857
22858
22859
22860
22861
22862
22863
22864
#endif
#ifdef SQLITE_UNLINK_AFTER_CLOSE
  "UNLINK_AFTER_CLOSE",
#endif
#ifdef SQLITE_UNTESTABLE
  "UNTESTABLE",
#endif
#ifdef SQLITE_USER_AUTHENTICATION
  "USER_AUTHENTICATION",
#endif
#ifdef SQLITE_USE_ALLOCA
  "USE_ALLOCA",
#endif
#ifdef SQLITE_USE_FCNTL_TRACE
  "USE_FCNTL_TRACE",
#endif
#ifdef SQLITE_USE_URI







<
<
<







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
23705
23706
23707
23708
23709
23710
23711
23712
  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 */
  u8 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 */







|







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
33030
33031
33032
33033
33034
33035
33036
33037
    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, " ON");
    }
    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");







|







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
35691
35692
35693
35694
35695
35696
35697
35698
  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];
  u64 s2;

  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;







>

<







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
35794
35795
35796
35797
35798
35799
35800
35801
35802
35803
35804
35805


35806
35807




35808





35809
35810
35811
35812
35813
35814
35815
    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/10) ){
    s *= 10;
    e--;
  }
  while( e<0 && (s%10)==0 ){
    s /= 10;
    e++;
  }

  rr[0] = (double)s;
  s2 = (u64)rr[0];
#if defined(_MSC_VER) && _MSC_VER<1700


  if( s2==0x8000000000000000LL ){ s2 = 2*(u64)(0.5*rr[0]); }
#endif




  rr[1] = s>=s2 ? (double)(s - s2) : -(double)(s2 - s);





  if( e>0 ){
    while( e>=100  ){
      e -= 100;
      dekkerMul2(rr, 1.0e+100, -1.5902891109759918046e+83);
    }
    while( e>=10   ){
      e -= 10;







|









|
|
>
>
|

>
>
>
>
|
>
>
>
>
>







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
38679
38680
38681
38682
38683
38684
38685
38686
#   define F_GETLK 5
#   define F_SETLK 6
#   define F_SETLKW 7
#  endif
# endif
#else /* !SQLITE_WASI */
# ifndef HAVE_FCHMOD
#  define HAVE_FCHMOD
# endif
#endif /* SQLITE_WASI */

#ifdef SQLITE_WASI
# define osGetpid(X) (pid_t)1
#else
/* Always cast the getpid() return type for compatibility with







|







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
50394
50395
50396
50397
50398
50399
50400
50401
}

/*
** Return a vector of device characteristics.
*/
static int winDeviceCharacteristics(sqlite3_file *id){
  winFile *p = (winFile*)id;
  return SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN |
         ((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()







|







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
51782
51783
51784
51785
51786
51787
51788
51789
  /* 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&0xFFFFFF00;  /* 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);







|







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
57983
57984
57985
57986
57987
57988
57989
57990
57991
57992
57993
57994
57995
57996
57997
57998
57999
58000
58001
58002

58003
58004
58005
58006


58007
58008
58009
58010
58011
58012
58013
58014
58015





58016
58017
58018
58019
58020
58021
58022
*/
#if SQLITE_MAX_MMAP_SIZE>0
# define USEFETCH(x) ((x)->bUseFetch)
#else
# define USEFETCH(x) 0
#endif

/*
** 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)

#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:
**
**   * the database file is open,

**   * there are no dirty pages in the cache, and
**   * the desired page is not currently in the wal file.
*/
SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno){


  if( pPager->fd->pMethods==0 ) return 0;
  if( sqlite3PCacheIsDirty(pPager->pPCache) ) return 0;
#ifndef SQLITE_OMIT_WAL
  if( pPager->pWal ){
    u32 iRead = 0;
    (void)sqlite3WalFindFrame(pPager->pWal, pgno, &iRead);
    return iRead==0;
  }
#endif





  return 1;
}
#endif

#ifndef SQLITE_OMIT_WAL
# define pagerUseWal(x) ((x)->pWal!=0)
#else







<
<
<
<
<
<
<
<
<
<
<
<
<
<





|
>
|
|


>
>
|
|




|


>
>
>
>
>







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
59274
59275
59276
59277
59278
59279
59280
59281
          ** 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







|







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
67984
67985
67986
67987
67988
67989
67990
67991
67992
67993
67994
67995
** 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 */
  u32 mxReadMark;                 /* Largest aReadMark[] value */
  int mxI;                        /* Index of largest aReadMark[] value */
  int i;                          /* Loop counter */
  int rc = SQLITE_OK;             /* Return code  */
  u32 mxFrame;                    /* Wal frame to lock to */
#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 */







<
<
<

<







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





68094
68095
68096
68097
68098
68099
68100
68101
68102
68103
68104
68105
68106
68107
68108
68109
68110
68111
68112
68113
68114
68115
68116
68117
68118
68119
68120
68121
68122
68123
68124
68125
68126
68127
68128
68129
68130
68131
68132
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
    }
  }

  assert( pWal->nWiData>0 );
  assert( pWal->apWiData[0]!=0 );
  pInfo = walCkptInfo(pWal);
  SEH_INJECT_FAULT;





  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().







>
>
>
>
>
|

|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|

|
|
|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|

|
|
|
|

|
|
|

|

|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>







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
91849
91850
91851
91852
91853
91854
91855
91856
**
** Specifically, this is called from within:
**
**     sqlite3_column_int()
**     sqlite3_column_int64()
**     sqlite3_column_text()
**     sqlite3_column_text16()
**     sqlite3_column_real()
**     sqlite3_column_bytes()
**     sqlite3_column_bytes16()
**     sqlite3_column_blob()
*/
static void columnMallocFailure(sqlite3_stmt *pStmt)
{
  /* If malloc() failed during an encoding conversion within an







|







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
92711
92712
92713
92714
92715
92716
92717
92718
92719
92720
92721
92722
92723
92724
92725
92726
92727
92728
92729
92730
92731
92732
92733

92734
























92735
92736
92737
92738
92739
92740
92741
92742
92743
92744
92745
92746
92747
92748
92749
92750
92751
92752
92753
92754
92755
92756
92757
92758
92759
92760
92761
92762
92763
92764

92765
92766
92767
92768
92769
92770
92771
    iIdx = sqlite3TableColumnToIndex(p->pPk, iIdx);
  }
  if( iIdx>=p->pCsr->nField || iIdx<0 ){
    rc = SQLITE_RANGE;
    goto preupdate_old_out;
  }

  /* 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->pTab->iPKey ){

    sqlite3VdbeMemSetInt64(pMem, p->iKey1);
























  }else 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);
}







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>







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
109850
109851
109852
109853
109854
109855
109856
109857
    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, (u8)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







|







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
112017
112018
112019
112020
112021
112022
112023
112024
**   (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 not 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
**







|







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
115552
115553
115554
115555
115556
115557
115558
115559
115560




115561
115562
115563
115564
115565
115566
115567
115568
115569
115570
115571
115572
115573
115574
115575
115576
115577
115578
115579
115580
115581
115582
** 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.



*/
static int exprCompareVariable(
  const Parse *pParse,
  const Expr *pVar,
  const Expr *pExpr
){
  int res = 0;
  int iVar;
  sqlite3_value *pL, *pR = 0;





  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 =  0==sqlite3MemCompare(pL, pR, 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







>
>
>

|




|



>
>
>
>









|




<







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
115601
115602
115603
115604
115605
115606
115607
115608
115609
115610
115611
115612
115613
115614
115615
115616
115617
115618
115619
115620
115621
115622
115623
115624
115625
115626
** 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 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.
** If pParse is NULL (the normal case) then any TK_VARIABLE term in
** Argument pParse should normally be NULL. If it is not NULL and pA or
** pB causes a return value of 2.
*/
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 && exprCompareVariable(pParse, pA, pB) ){
    return 0;
  }
  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;







|
|
|
<
<
|











|
|







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
115819
115820
115821
115822
115823
115824
115825


115826
115827
115828
115829
115830
115831
115832
    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:
**
**     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


**
** 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









>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>




|
|
|
|
|
|
|
>
>







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
121270
121271
121272
121273
121274
121275
121276
121277
121278
121279
121280
121281
121282
121283
121284
121285
    db->mDbFlags &= ~(DBFLAG_SchemaKnownOk);
    if( !REOPEN_AS_MEMDB(db) ){
      rc = sqlite3Init(db, &zErrDyn);
    }
    sqlite3BtreeLeaveAll(db);
    assert( zErrDyn==0 || rc!=SQLITE_OK );
  }
#ifdef SQLITE_USER_AUTHENTICATION
  if( rc==SQLITE_OK && !REOPEN_AS_MEMDB(db) ){
    u8 newAuth = 0;
    rc = sqlite3UserAuthCheckLogin(db, zName, &newAuth);
    if( newAuth<db->auth.authLevel ){
      rc = SQLITE_AUTH_USER;
    }
  }
#endif
  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;







<
<
<
<
<
<
<
<
<







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
121776
121777
121778
121779
121780
121781
121782
121783
121784
121785
121786
121787
  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
#ifdef SQLITE_USER_AUTHENTICATION
                 ,db->auth.zAuthUser
#endif
                );
  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);







|
<
<
<
<







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
121887
121888
121889
121890
121891
121892
121893
121894
121895
121896
121897
121898
  ** 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
#ifdef SQLITE_USER_AUTHENTICATION
                 ,db->auth.zAuthUser
#endif
                );
  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);
  }







|
<
<
<
<







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
122124
122125
122126
122127
122128
122129
122130
122131
122132
122133
122134
122135
122136
122137
122138
122139
122140
122141
        sqlite3VdbeAddOp2(v, OP_Next, pReturning->iRetCur, addrRewind+1);
        VdbeCoverage(v);
        sqlite3VdbeJumpHere(v, addrRewind);
      }
    }
    sqlite3VdbeAddOp0(v, OP_Halt);

#if SQLITE_USER_AUTHENTICATION && !defined(SQLITE_OMIT_SHARED_CACHE)
    if( pParse->nTableLock>0 && db->init.busy==0 ){
      sqlite3UserAuthInit(db);
      if( db->auth.authLevel<UAUTH_User ){
        sqlite3ErrorMsg(pParse, "user not authenticated");
        pParse->rc = SQLITE_AUTH_USER;
        return;
      }
    }
#endif

    /* 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 );







<
<
<
<
<
<
<
<
<
<
<







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
122263
122264
122265
122266
122267
122268
122269
122270
122271
122272
122273
122274
122275
122276
122277
122278
122279
122280
122281
122282
122283
122284
122285
122286
122287
122288
122289
122290
122291
122292
122293
122294
122295
122296
122297
122298
122299
122300
122301
122302
122303
122304
  sqlite3RunParser(pParse, zSql);
  db->mDbFlags = savedDbFlags;
  sqlite3DbFree(db, zSql);
  memcpy(PARSE_TAIL(pParse), saveBuf, PARSE_TAIL_SZ);
  pParse->nested--;
}

#if SQLITE_USER_AUTHENTICATION
/*
** Return TRUE if zTable is the name of the system table that stores the
** list of users and their access credentials.
*/
SQLITE_PRIVATE int sqlite3UserAuthTable(const char *zTable){
  return sqlite3_stricmp(zTable, "sqlite_user")==0;
}
#endif

/*
** 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 SQLITE_USER_AUTHENTICATION
  /* Only the admin user is allowed to know that the sqlite_user table
  ** exists */
  if( db->auth.authLevel<UAUTH_Admin && sqlite3UserAuthTable(zName)!=0 ){
    return 0;
  }
#endif
  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. */







<
<
<
<
<
<
<
<
<
<


















<
<
<
<
<
<
<







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
125956
125957
125958
125959
125960
125961
125962
125963
125964
125965
  }
  pDb = &db->aDb[iDb];

  assert( pTab!=0 );
  if( sqlite3StrNICmp(pTab->zName, "sqlite_", 7)==0
       && db->init.busy==0
       && pTblName!=0
#if SQLITE_USER_AUTHENTICATION
       && sqlite3UserAuthTable(pTab->zName)==0
#endif
  ){
    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");







<
<
<







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
129666
129667
129668
129669
129670
129671
129672
129673
129674
129675
129676
129677
129678
129679
129680
129681
129682
  sqlite3_value **argv
){
  const unsigned char *z;
  const unsigned char *z2;
  int len;
  int p0type;
  i64 p1, p2;
  int negP2 = 0;

  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_int(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]);







<








|







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
129700
129701
129702
129703
129704
129705
129706
129707
129708
129709
129710



129711
129712

129713
129714
129715
129716
129717
129718
129719
129720
129721
129722
129723

129724
129725

129726
129727
129728
129729
129730
129731
129732
129733
129734
129735
129736
129737
129738
129739


129740
129741
129742
129743
129744
129745
129746
129747
129748
129749
129750
129751
129752
129753
129754
129755
129756
129757
129758
129759
129760
129761
129762
129763
129764
129765
129766
129767
129768
129769
129770
129771
129772
129773
129774
129775
129776
129777
129778
129779
129780
  ** 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_int(argv[2]);
    if( p2<0 ){
      p2 = -p2;
      negP2 = 1;
    }
  }else{
    p2 = sqlite3_context_db_handle(context)->aLimit[SQLITE_LIMIT_LENGTH];
  }
  if( p1<0 ){
    p1 += len;
    if( p1<0 ){



      p2 += p1;
      if( p2<0 ) p2 = 0;

      p1 = 0;
    }
  }else if( p1>0 ){
    p1--;
  }else if( p2>0 ){
    p2--;
  }
  if( negP2 ){
    p1 -= p2;
    if( p1<0 ){
      p2 += p1;

      p1 = 0;
    }

  }
  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+p2>len ){


      p2 = len-p1;
      if( p2<0 ) 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){
  int 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_int(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",n,r);
    if( zBuf==0 ){
      sqlite3_result_error_nomem(context);
      return;
    }
    sqlite3AtoF(zBuf, &r, sqlite3Strlen30(zBuf), SQLITE_UTF8);
    sqlite3_free(zBuf);
  }







|
<
<
<
<






>
>
>
|
<
>







|
<
|
|
>
|

>













|
>
>

|










|





|














|







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
131990
131991
131992
131993
131994
131995
131996
131997
131998
131999
#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
#if SQLITE_USER_AUTHENTICATION
    FUNCTION(sqlite_crypt,       2, 0, 0, sqlite3CryptFunc ),
#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),







<
<
<







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
140642
140643
140644
140645
140646
140647
140648
140649
140650
140651
140652
140653
140654
    }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 SQLITE_USER_AUTHENTICATION
      if( db->auth.authLevel==UAUTH_User ){
        /* Do not allow non-admin users to modify the schema arbitrarily */
        mask &= ~(SQLITE_WriteSchema);
      }
#endif

      if( sqlite3GetBoolean(zRight, 0) ){
        if( (mask & SQLITE_WriteSchema)==0
         || (db->flags & SQLITE_Defensive)==0
        ){
          db->flags |= mask;
        }







<
<
<
<
<
<







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

140783
140784
140785
140786
140787
140788
140789
140790
          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(db, zSql, -1, &pDummy, 0);
              (void)sqlite3_finalize(pDummy);
              sqlite3DbFree(db, zSql);
            }
            if( db->mallocFailed ){
              sqlite3ErrorMsg(db->pParse, "out of memory");
              db->pParse->rc = SQLITE_NOMEM_BKPT;
            }







>
|







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
141264
141265
141266
141267
141268
141269
141270
141271
      /* 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, (u8)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);







|







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
142884
142885
142886
142887
142888
142889
142890
142891
142892
142893
142894
142895
142896
142897
142898
#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
      if( db->nVdbeActive>0 && encoding!=ENC(db)
       && (db->mDbFlags & DBFLAG_Vacuum)==0
      ){
        rc = SQLITE_LOCKED;
        goto initone_error_out;
      }else{
        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;







<
<
<
<
<
<
|
<







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
147595
147596
147597
147598
147599
147600
147601
147602
147603
147604
147605
147606
147607
147608
147609
147610
147611
147612
147613
147614
147615
147616
147617
147618
147619
147620
147621
        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;
        if( pExpr->op==TK_TRUEFALSE ){
          pExpr->u.iValue = sqlite3ExprTruthValue(pExpr);
          pExpr->op = TK_INTEGER;
          ExprSetProperty(pExpr, 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, pExpr);
          CollSeq *pColl = sqlite3ExprCollSeq(pSubst->pParse,
                pSubst->pCList->a[iColumn].pExpr
          );
          if( pNat!=pColl || (pExpr->op!=TK_COLUMN && pExpr->op!=TK_COLLATE) ){
            pExpr = sqlite3ExprAddCollateString(pSubst->pParse, pExpr,
                (pColl ? pColl->zName : "BINARY")
            );
          }
        }
        ExprClearProperty(pExpr, EP_Collate);
      }
    }
  }else{
    if( pExpr->op==TK_IF_NULL_ROW && pExpr->iTable==pSubst->iTable ){
      pExpr->iTable = pSubst->iNewTable;
    }
    pExpr->pLeft = substExpr(pSubst, pExpr->pLeft);







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>






<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







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
148360
148361
148362
148363
148364
148365
148366
148367
      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;
      iNewParent = pSubSrc->a[i].iCursor;
      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.







>









<







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
150503
150504
150505
150506
150507
150508
150509
150510
          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, (u8)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);







|







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
150666
150667
150668
150669
150670
150671
150672
150673
        }
        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, (u8)nArg);
      sqlite3ReleaseTempRange(pParse, regAgg, nArg);
    }
    if( addrNext ){
      sqlite3VdbeResolveLabel(v, addrNext);
    }
    if( pParse->nErr ) return;
  }







|







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
151499
151500
151501
151502
151503
151504
151505
151506
        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-lcause 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)







|







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
154060
154061
154062
154063
154064
154065
154066
154067
        (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, (u8)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







|







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
158477
158478
158479
158480
158481
158482
158483

158484
158485
158486

158487
158488
158489
158490
158491
158492
158493
158494
158495


158496
158497
158498
158499
158500
158501

158502

158503
158504
158505


158506
158507
158508
158509
158510
158511
158512
158513
158514
158515
  if( pLoop->wsFlags&WHERE_TOP_LIMIT ){
    explainAppendTerm(pStr, pIndex, pLoop->u.btree.nTop, j, i, "<");
  }
  sqlite3_str_append(pStr, ")", 1);
}

/*
** 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
  {


    SrcItem *pItem = &pTabList->a[pLevel->iFrom];
    Vdbe *v = pParse->pVdbe;      /* VM being constructed */
    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 */

    char *zMsg;                   /* Text to add to EQP output */

    StrAccum str;                 /* EQP output string */
    char zBuf[100];               /* Initial space for EQP output string */



    pLoop = pLevel->pWLoop;
    flags = pLoop->wsFlags;
    if( (flags&WHERE_MULTI_OR) || (wctrlFlags&WHERE_OR_SUBCLAUSE) ) return 0;

    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;







|
<
<
<
<
|
<
>

|

>




<




>
>

<




>

>



>
>


<







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
158532
158533
158534
158535
158536
158537
158538
158539
        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 ){
        zFmt = "COVERING INDEX %s";
      }else{
        zFmt = "INDEX %s";
      }
      if( zFmt ){
        sqlite3_str_append(&str, " USING ", 7);
        sqlite3_str_appendf(&str, zFmt, pIdx->zName);







|







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




































158586
158587
158588


158589
158590
158591
158592
158593
158594
158595
    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);




































    ret = sqlite3VdbeAddOp4(v, OP_Explain, sqlite3VdbeCurrentAddr(v),
                            pParse->addrExplain, pLoop->rRun,
                            zMsg, P4_DYNAMIC);


  }
  return ret;
}

/*
** Add a single OP_Explain opcode that describes a Bloom filter.
**







>


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
|
>
>







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
158689
158690
158691
158692
158693
158694
158695
158696
        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;
      VdbeOp *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







>


|







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
164014
164015
164016
164017
164018
164019
164020
164021
  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
   && ExprHasProperty(pTerm->pExpr, EP_InnerON)
  ){
    return 0;
  }
  return 1;
}

#ifndef SQLITE_OMIT_AUTOMATIC_INDEX







|







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
165507
165508
165509
165510
165511
165512
165513
165514
  }
  assert( pBuilder->nRecValid==nRecValid );
  return rc;
}
#endif /* SQLITE_ENABLE_STAT4 */


#ifdef WHERETRACE_ENABLED
/*
** 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{







|







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
166736
166737
166738
166739
166740
166741
166742
166743

  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;
  }
  if( pParse->db->flags & SQLITE_EnableQPSG ) pParse = 0;
  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







<







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
169397
169398
169399
169400
169401
169402
169403
169404
         || pTerm->pExpr->w.iJoin!=pItem->iCursor
        ){
          break;
        }
      }
      if( hasRightJoin
       && ExprHasProperty(pTerm->pExpr, EP_InnerON)
       && 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;







|







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
170616
170617
170618
170619
170620
170621



170622
170623















170624
170625
170626
170627
170628
170629
170630
            x = sqlite3StorageColumnToTable(pTab,x);
          }
          x = sqlite3TableColumnToIndex(pIdx, x);
          if( x>=0 ){
            pOp->p2 = x;
            pOp->p1 = pLevel->iIdxCur;
            OpcodeRewriteTrace(db, k, pOp);
          }else{
            /* Unable to translate the table reference into an index
            ** reference.  Verify that this is harmless - that the
            ** table being referenced really is open.
            */
            if( pLoop->wsFlags & WHERE_IDX_ONLY ){



              sqlite3ErrorMsg(pParse, "internal query planner error");
              pParse->rc = SQLITE_INTERNAL;















            }
          }
        }else if( pOp->opcode==OP_Rowid ){
          pOp->p1 = pLevel->iIdxCur;
          pOp->opcode = OP_IdxRowid;
          OpcodeRewriteTrace(db, k, pOp);
        }else if( pOp->opcode==OP_IfNullRow ){







|
<
<
<
<

>
>
>


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
172372
172373
172374
172375
172376
172377
172378
172379
172380
172381
172382
172383
172384
172385
172386
172387
172388
172389
172390
172391
  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 ){
      int addrIf = 0;
      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( pWin->bExprArgs ){
        int iOp = sqlite3VdbeCurrentAddr(v);
        int iEnd;

        assert( ExprUseXList(pWin->pOwner) );
        nArg = pWin->pOwner->x.pList->nExpr;
        regArg = sqlite3GetTempRange(pParse, nArg);







>















>
>
>
>
>
>
>
>
>
>
>
>




















>






<
<
<
<
<
<
<
<
<
<
<
<
<







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
172411
172412
172413
172414
172415
172416


172417
172418
172419
172420
172421
172422
172423
        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, (u8)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







|



<

>
>







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



177735

177736
177737
177738
177739
177740
177741
177742
{
  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};



  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 */







>
>
>
|
>







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
178206
178207
178208
178209
178210
178211
178212
178213
  }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 ){
      sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &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);
    }
  }
}







|







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
179054
179055
179056
179057
179058
179059
179060
179061
  sqlite3ParserARG_FETCH
  sqlite3ParserCTX_FETCH
#define TOKEN yyminor
/************ Begin %syntax_error code ****************************************/

  UNUSED_PARAMETER(yymajor);  /* Silence some compiler warnings */
  if( TOKEN.z[0] ){
    sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &TOKEN);
  }else{
    sqlite3ErrorMsg(pParse, "incomplete input");
  }
/************ End %syntax_error code ******************************************/
  sqlite3ParserARG_STORE /* Suppress warning about unused %extra_argument variable */
  sqlite3ParserCTX_STORE
}







|







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

180545

180546
180547
180548
180549
180550
180551
180552
  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));
    }

    sqlite3_log(pParse->rc, "%s in \"%s\"", pParse->zErrMsg, pParse->zTail);

    nErr++;
  }
  pParse->zTail = zSql;
#ifndef SQLITE_OMIT_VIRTUALTABLE
  sqlite3_free(pParse->apVtabLock);
#endif








>
|
>







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
182518
182519
182520
182521
182522
182523
182524
182525
182526
182527
182528
  }
  sqlite3HashClear(&db->aModule);
#endif

  sqlite3Error(db, SQLITE_OK); /* Deallocates any cached error strings. */
  sqlite3ValueFree(db->pErr);
  sqlite3CloseExtensions(db);
#if SQLITE_USER_AUTHENTICATION
  sqlite3_free(db->auth.zAuthUser);
  sqlite3_free(db->auth.zAuthPW);
#endif

  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







<
<
<
<







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
183956
183957
183958
183959
183960
183961
183962
183963
183964
#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>127
# error SQLITE_MAX_FUNCTION_ARG must be between 0 and 127
#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







|
|







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
184024
184025
184026
184027
184028
184029
184030
184031
184032
  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<1 && limitId==SQLITE_LIMIT_LENGTH ){
      newLimit = 1;
    }
    db->aLimit[limitId] = newLimit;
  }
  return oldLimit;                     /* IMP: R-53341-35419 */
}

/*







|
|







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
185369
185370
185371
185372
185373
185374
185375
185376
      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);
        sqlite3ShowExpr(0);
        sqlite3ShowExprList(0);
        sqlite3ShowIdList(0);
        sqlite3ShowSrcList(0);
        sqlite3ShowWith(0);
        sqlite3ShowUpsert(0);
#ifndef SQLITE_OMIT_TRIGGER







<







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
192975
192976
192977
192978
192979
192980
192981
192982

    /* 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);
    if( !aTmp ){
      *pRc = SQLITE_NOMEM;
      res = 0;
    }else{
      char *aPoslist = p->pPhrase->doclist.pList;
      int nToken = p->pPhrase->nToken;








|







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
193626
193627
193628
193629
193630
193631
193632
193633
*/
#ifdef SQLITE_DEBUG
SQLITE_PRIVATE int sqlite3Fts3Corrupt(){
  return SQLITE_CORRUPT_VTAB;
}
#endif

#if !SQLITE_CORE
/*
** Initialize API pointer table, if required.
*/
#ifdef _WIN32
__declspec(dllexport)
#endif
SQLITE_API int sqlite3_fts3_init(







|







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
194528
194529
194530
194531



194532
194533
194534
194535
194536
194537
194538
194539
194540
194541
194542
194543
194544
194545
194546
194547
194548
194549
194550
194551
194552
194553
194554
194555
194556



194557
194558
194559
194560
194561
194562
194563

194564
194565
194566
194567
194568
194569
194570
194571
194572
194573
194574
194575
194576
194577
194578
194579
194580
194581
194582
194583
194584
194585

194586


194587
194588
194589
194590
194591
194592
194593
194594
194595
      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));
        if( !p ) goto no_mem;

        zTemp = fts3ReallocOrFree(zTemp, nTemp + nByte);
        if( !zTemp ) goto no_mem;




        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;
      }
    }

    pModule->xClose(pCursor);
    pCursor = 0;
  }

  if( rc==SQLITE_DONE ){
    int jj;
    char *zBuf = 0;

    p = fts3ReallocOrFree(p, nSpace + nToken*sizeof(Fts3PhraseToken) + nTemp);
    if( !p ) goto no_mem;



    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];

    if( zTemp ){
      memcpy(zBuf, zTemp, nTemp);
      sqlite3_free(zTemp);
    }else{
      assert( nTemp==0 );
    }

    for(jj=0; jj<p->pPhrase->nToken; jj++){
      p->pPhrase->aToken[jj].z = zBuf;
      zBuf += p->pPhrase->aToken[jj].n;
    }
    rc = SQLITE_OK;
  }

  *ppExpr = p;
  return rc;
no_mem:

  if( pCursor ){
    pModule->xClose(pCursor);
  }
  sqlite3_free(zTemp);

  sqlite3_free(p);


  *ppExpr = 0;
  return SQLITE_NOMEM;
}

/*
** 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







<
<

|
>
>
>














<
<
<







|
>
>
>







>


<
<
<









<
<
<
|




>
|
>
>
|
|







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
215400
215401

215402
215403
215404
215405
215406
215407
215408
#else
      sqlite3_str_appendf(pOut, " %d", cell.aCoord[jj].i);
#endif
    }
    sqlite3_str_append(pOut, "}", 1);
  }
  errCode = sqlite3_str_errcode(pOut);
  sqlite3_result_text(ctx, sqlite3_str_finish(pOut), -1, sqlite3_free);
  sqlite3_result_error_code(ctx, errCode);

}

/* 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;
**







<

>







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
217917
217918
217919
217920
217921
217922
217923
217924
  pGeomCtx->xDestructor = xDestructor;
  pGeomCtx->pContext = pContext;
  return sqlite3_create_function_v2(db, zQueryFunc, -1, SQLITE_ANY,
      (void *)pGeomCtx, geomCallback, 0, 0, rtreeFreeCallback
  );
}

#if !SQLITE_CORE
#ifdef _WIN32
__declspec(dllexport)
#endif
SQLITE_API int sqlite3_rtree_init(
  sqlite3 *db,
  char **pzErrMsg,
  const sqlite3_api_routines *pApi







|







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
218508
218509
218510
218511
218512
218513
218514
218515
        p->xFunc, 0, 0
    );
  }

  return rc;
}

#if !SQLITE_CORE
#ifdef _WIN32
__declspec(dllexport)
#endif
SQLITE_API int sqlite3_icu_init(
  sqlite3 *db,
  char **pzErrMsg,
  const sqlite3_api_routines *pApi







|







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


226215


226216
226217
226218
226219
226220
226221
226222
/* 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);


    sqlite3PagerTruncateImage(pPager, pTab->pgnoTrunc);


  }
  pTab->pgnoTrunc = 0;
  return SQLITE_OK;
}

/* Cancel any pending truncate.
*/







>
>
|
>
>







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
232809




















232810
232811
232812
232813
232814
232815
232816
232817
232818






232819
232820
232821
232822
232823
232824
232825
}

#endif /* SQLITE_ENABLE_SESSION && SQLITE_ENABLE_PREUPDATE_HOOK */

/************** End of sqlite3session.c **************************************/
/************** Begin file fts5.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







/*
** 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.







|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>









>
>
>
>
>
>







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
233119
233120
233121
233122
233123
233124
233125

















233126
233127
233128
233129
233130
233131
233132
**
** 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. This API is not available if the specified token matches a
**   prefix query term. In that case both output variables are always set
**   to 0.
**
**   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 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.







<
<
|




>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
233808

233809
233810
233811
233812
233813
233814
233815
  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' */


  /* If non-NULL, points to sqlite3_vtab.base.zErrmsg. Often NULL. */
  char **pzErrmsg;

#ifdef SQLITE_DEBUG
  int bPrefixIndex;               /* True to use prefix-indexes */
#endif







|
>







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
234065







234066
234067
234068
234069
234070
234071
234072
static void *sqlite3Fts5StructureRef(Fts5Index*);
static void sqlite3Fts5StructureRelease(void*);
static int sqlite3Fts5StructureTest(Fts5Index*, void*);

/*
** Used by xInstToken():
*/
static int sqlite3Fts5IterToken(Fts5IndexIter*, i64, int, int, const char**, int*);








/*
** 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.







|
>
>
>
>
>
>
>







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
241414
241415
241416
241417
241418
241419
241420
241421
    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;







|







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
241607
241608
241609

241610
241611
241612
241613
241614
241615
241616
241617
241618
241619
241620
241621
241622
    return SQLITE_RANGE;
  }
  pPhrase = pExpr->apExprPhrase[iPhrase];
  if( iToken<0 || iToken>=pPhrase->nTerm ){
    return SQLITE_RANGE;
  }
  pTerm = &pPhrase->aTerm[iToken];
  if( pTerm->bPrefix==0 ){
    if( pExpr->pConfig->bTokendata ){
      rc = sqlite3Fts5IterToken(

          pTerm->pIter, 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.







<
|
|
>
|
|
|
|
|
<







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


248440


248441
248442








248443
248444
248445
248446
248447
248448
248449
248450
248451
248452
248453
248454
248455
248456
248457
248458
248459
248460
248461
248462
248463
248464
248465
248466
248467
248468
248469
248470
248471
248472
248473
248474
248475
248476
248477
248478
248479
248480
248481
248482
248483
248484
248485
248486
248487
248488
248489
248490
248491
248492
248493
248494
248495
248496
248497
248498
248499
248500
248501
248502
248503
248504
248505
248506
248507
248508
248509
248510
248511
248512
248513
248514
248515
248516
248517
248518
248519
248520
248521
248522
248523
248524
248525
248526
248527
248528
248529
248530
248531
248532
248533
248534
248535
248536
248537
248538
248539
248540
248541
248542
248543
248544
248545
248546
248547
248548
248549
248550
248551
248552
248553
248554
248555

248556
248557
248558
248559
248560
248561
248562





248563
248564



248565
248566
248567
248568
248569
248570
248571
248572
248573

  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;


  Fts5Buffer *aBuf;


  int nBuf = 32;
  int nMerge = 1;









  void (*xMerge)(Fts5Index*, Fts5Buffer*, int, Fts5Buffer*);
  void (*xAppend)(Fts5Index*, u64, Fts5Iter*, Fts5Buffer*);
  if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
    xMerge = fts5MergeRowidLists;
    xAppend = fts5AppendRowid;
  }else{
    nMerge = FTS5_MERGE_NLIST-1;
    nBuf = nMerge*8;   /* Sufficient to merge (16^8)==(2^32) lists */
    xMerge = fts5MergePrefixLists;
    xAppend = fts5AppendPoslist;
  }

  aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf);
  pStruct = fts5StructureRead(p);
  assert( p->rc!=SQLITE_OK || (aBuf && pStruct) );

  if( p->rc==SQLITE_OK ){
    const int flags = FTS5INDEX_QUERY_SCAN
                    | FTS5INDEX_QUERY_SKIPEMPTY
                    | FTS5INDEX_QUERY_NOOUTPUT;
    int i;
    i64 iLastRowid = 0;
    Fts5Iter *p1 = 0;     /* Iterator used to gather data from index */
    Fts5Data *pData;
    Fts5Buffer doclist;
    int bNewTerm = 1;

    memset(&doclist, 0, sizeof(doclist));

    /* 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 ){
      int dummy = 0;
      const int f2 = FTS5INDEX_QUERY_SKIPEMPTY|FTS5INDEX_QUERY_NOOUTPUT;
      pToken[0] = FTS5_MAIN_PREFIX;
      fts5MultiIterNew(p, pStruct, f2, pColset, pToken, nToken, -1, 0, &p1);
      fts5IterSetOutputCb(&p->rc, p1);
      for(;
        fts5MultiIterEof(p, p1)==0;
        fts5MultiIterNext2(p, p1, &dummy)
      ){
        Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ];
        p1->xSetOutputs(p1, pSeg);
        if( p1->base.nData ){
          xAppend(p, (u64)p1->base.iRowid-(u64)iLastRowid, p1, &doclist);
          iLastRowid = p1->base.iRowid;
        }
      }
      fts5MultiIterFree(p1);
    }

    pToken[0] = FTS5_MAIN_PREFIX + iIdx;
    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 nTerm = pSeg->term.n;
      const u8 *pTerm = pSeg->term.p;
      p1->xSetOutputs(p1, pSeg);

      assert_nc( memcmp(pToken, pTerm, MIN(nToken, nTerm))<=0 );
      if( bNewTerm ){
        if( nTerm<nToken || memcmp(pToken, pTerm, nToken) ) break;
      }

      if( p1->base.nData==0 ) continue;
      if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){
        for(i=0; p->rc==SQLITE_OK && doclist.n; i++){
          int i1 = i*nMerge;
          int iStore;
          assert( i1+nMerge<=nBuf );
          for(iStore=i1; iStore<i1+nMerge; iStore++){
            if( aBuf[iStore].n==0 ){
              fts5BufferSwap(&doclist, &aBuf[iStore]);
              fts5BufferZero(&doclist);
              break;
            }
          }
          if( iStore==i1+nMerge ){
            xMerge(p, &doclist, nMerge, &aBuf[i1]);
            for(iStore=i1; iStore<i1+nMerge; iStore++){
              fts5BufferZero(&aBuf[iStore]);
            }
          }
        }
        iLastRowid = 0;
      }

      xAppend(p, (u64)p1->base.iRowid-(u64)iLastRowid, p1, &doclist);
      iLastRowid = p1->base.iRowid;
    }

    assert( (nBuf%nMerge)==0 );
    for(i=0; i<nBuf; i+=nMerge){
      int iFree;
      if( p->rc==SQLITE_OK ){
        xMerge(p, &doclist, nMerge, &aBuf[i]);
      }
      for(iFree=i; iFree<i+nMerge; iFree++){
        fts5BufferFree(&aBuf[iFree]);
      }
    }
    fts5MultiIterFree(p1);

    pData = fts5IdxMalloc(p, sizeof(*pData)+doclist.n+FTS5_DATA_ZERO_PADDING);

    if( pData ){
      pData->p = (u8*)&pData[1];
      pData->nn = pData->szLeaf = doclist.n;
      if( doclist.n ) memcpy(pData->p, doclist.p, doclist.n);
      fts5MultiIterNew2(p, pData, bDesc, ppIter);
    }
    fts5BufferFree(&doclist);





  }




  fts5StructureRelease(pStruct);
  sqlite3_free(aBuf);
}


/*
** Indicate that all subsequent calls to sqlite3Fts5IndexWrite() pertain
** to the document with rowid iRowid.
*/







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>










>
>
|
>
>
|
|
>
>
>
>
>
>
>
>
|
|
<

|
|

|
|
|
|


|

|


|
<
<

<
<

<
<
<
<







<
<

|
<
<
<
<
<
<
<
<
<
<
|
|
<
<
<

|
<

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
|


|

|
|


<

|
>


|
|


|
>
>
>
>
>
|
|
>
>
>

|







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
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
** Ensure the segment-iterator passed as the only argument points to EOF.
*/
static void fts5SegIterSetEOF(Fts5SegIter *pSeg){
  fts5DataRelease(pSeg->pLeaf);
  pSeg->pLeaf = 0;
}

/*
** Usually, a tokendata=1 iterator (struct Fts5TokenDataIter) accumulates an
** array of these for each row it visits. Or, for an iterator used by an
** "ORDER BY rank" query, it accumulates an array of these for the entire
** query.
**
** 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.
*/
struct Fts5TokenDataMap {
  i64 iRowid;                     /* Row this token is located in */
  i64 iPos;                       /* Position of token */
  int iIter;                      /* Iterator token was read from */
};

/*
** An object used to supplement Fts5Iter for tokendata=1 iterators.
*/
struct Fts5TokenDataIter {
  int nIter;
  int nIterAlloc;

  int nMap;
  int nMapAlloc;
  Fts5TokenDataMap *aMap;

  Fts5PoslistReader *aPoslistReader;
  int *aPoslistToIter;
  Fts5Iter *apIter[1];
};

/*
** 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 */







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







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
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
    pRet->apIter[pRet->nIter++] = pAppend;
  }
  assert( pRet==0 || pRet->nIter<=pRet->nIterAlloc );

  return pRet;
}

/*
** 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]);
    }
    sqlite3_free(pSet->aPoslistReader);
    sqlite3_free(pSet->aMap);
    sqlite3_free(pSet);
  }
}

/*
** Append a mapping to the token-map belonging to object pT.
*/
static void fts5TokendataIterAppendMap(
  Fts5Index *p,
  Fts5TokenDataIter *pT,
  int iIter,
  i64 iRowid,
  i64 iPos
){
  if( p->rc==SQLITE_OK ){
    if( pT->nMap==pT->nMapAlloc ){
      int nNew = pT->nMapAlloc ? pT->nMapAlloc*2 : 64;
      int nByte = nNew * sizeof(Fts5TokenDataMap);
      Fts5TokenDataMap *aNew;

      aNew = (Fts5TokenDataMap*)sqlite3_realloc(pT->aMap, nByte);
      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->nMap++;
  }
}

/*
** 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){







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







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
248976
248977
248978
248979
248980
248981
248982
248983
    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, 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. */







|







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
249243
249244
249245
249246
249247
249248
249249
249250
    }
  }

  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;
}


/*
** 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 */







>














<







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
249299
249300
249301
249302
249303
249304
249305
249306
249307
249308
249309
249310
249311
249312
249313
249314
249315
249316
249317
249318
249319
        int nIdxChar = pConfig->aPrefix[iIdx-1];
        if( nIdxChar==nChar ) break;
        if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx;
      }
    }

    if( bTokendata && iIdx==0 ){
      buf.p[0] = '0';
      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 */
      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);







|












|







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

249348
249349
249350
249351
249352
249353
249354
249355
*/
/*
** Move to the next matching rowid.
*/
static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){
  Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
  assert( pIter->pIndex->rc==SQLITE_OK );

  if( pIter->pTokenDataIter ){
    fts5TokendataIterNext(pIter, 0, 0);
  }else{
    fts5MultiIterNext(pIter->pIndex, pIter, 0, 0);
  }
  return fts5IndexReturn(pIter->pIndex);
}








>
|







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

249385
249386
249387
249388
249389
249390
249391
249392
249393
249394
249395
249396
249397
249398
249399
249400
249401
249402












































249403
249404
249405
249406
249407
249408


249409
249410
249411

249412
249413
249414
249415
249416
249417
249418
249419
249420
249421
249422
249423
249424










249425
249426
249427
249428
249429
249430
249431
/*
** 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->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);
}













































/*
** 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).


*/
static int sqlite3Fts5IterToken(
  Fts5IndexIter *pIndexIter,

  i64 iRowid,
  int iCol,
  int iOff,
  const char **ppOut, int *pnOut
){
  Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
  Fts5TokenDataIter *pT = pIter->pTokenDataIter;
  Fts5TokenDataMap *aMap = pT->aMap;
  i64 iPos = (((i64)iCol)<<32) + iOff;

  int i1 = 0;
  int i2 = pT->nMap;
  int iTest = 0;











  while( i2>i1 ){
    iTest = (i1 + i2) / 2;

    if( aMap[iTest].iRowid<iRowid ){
      i1 = iTest+1;
    }else if( aMap[iTest].iRowid>iRowid ){







>
|

















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>






>
>



>







<

|

|

>
>
>
>
>
>
>
>
>
>







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

249448
249449
249450





249451
249452
249453
249454
249455
249456
249457
249458
249459
249460
249461
249462


249463
249464
249465
249466
249467
249468
249469
249470
249471
249472
249473
249474
249475
249476
249477
249478
249479
249480
249481
249482
249483
249484
249485





249486






249487
249488
249489
249490
249491
249492

249493
249494
249495
249496
249497
249498
249499
      }else{
        break;
      }
    }
  }

  if( i2>i1 ){

    Fts5Iter *pMap = pT->apIter[aMap[iTest].iIter];
    *ppOut = (const char*)pMap->aSeg[0].term.p+1;
    *pnOut = pMap->aSeg[0].term.n-1;





  }

  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->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;
  int ii;

  assert( p->pConfig->eDetail!=FTS5_DETAIL_FULL );
  assert( pIter->pTokenDataIter );












  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, iRowid, (((i64)iCol)<<32) + iOff);

  }
  return fts5IndexReturn(p);
}

/*
** Close an iterator opened by an earlier call to sqlite3Fts5IndexQuery().
*/







>
|
|
|
>
>
>
>
>











|
>
>



















|


|
>
>
>
>
>
|
>
>
>
>
>
>
|
|
|
|
|
|
>







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
254891
254892
254893
254894
254895
254896
254897
254898
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-10-21 16:30:22 03a9703e27c44437c39363d0baf82db4ebc94538a0f28411c85dda156f82636e", -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







|







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
255084
255085
255086







255087
255088
255089
255090
255091
255092
255093
          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,
          p, fts5LocaleFunc, 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







|


>
>
>
>
>
>
>







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
258012
258013
258014
258015
258016
258017
258018
258019
258020
258021

258022
258023
258024
258025
258026
258027
258028
258029
258030
258031
258032
258033
258034
258035
258036
258037
258038
258039
258040
258041
258042
258043




258044
258045
258046
258047
258048
258049
258050
258051
258052
){
  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[nText];
  u32 iCode;
  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;

      READ_UTF8(zIn, zEof, iCode);
      if( iCode==0 ) return SQLITE_OK;
      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;




      READ_UTF8(zIn, zEof, iCode);
      if( iCode==0 ) break;
      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;








|
|








>

<




















>
>
>
>

<







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
260082
260083
260084
260085
260086
260087
260088
260089
  };
  void *p = (void*)pGlobal;

  return sqlite3_create_module_v2(db, "fts5vocab", &fts5Vocab, p, 0);
}



#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS5) */

/************** End of fts5.c ************************************************/
/************** Begin file stmt.c ********************************************/
/*
** 2017-05-31
**







|







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
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.47.0"
#define SQLITE_VERSION_NUMBER 3047000
#define SQLITE_SOURCE_ID      "2024-10-21 16:30:22 03a9703e27c44437c39363d0baf82db4ebc94538a0f28411c85dda156f82636e"

/*
** 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







|
|
|







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
2625
2626
2627
2628




2629
2630
2631
2632
2633
2634
2635
** 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.




**
** ^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







|



>
>
>
>







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
10883
10884
10885
10886
10887
10888
10889
10890
#  define SQLITE_THREADSAFE 0
# endif
#endif

#ifdef __cplusplus
}  /* End of the 'extern "C"' block */
#endif
#endif /* SQLITE3_H */

/******** 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:







|







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
13134
13135
13136
13137
13138
13139
13140

















13141
13142
13143
13144
13145
13146
13147
**
** 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. This API is not available if the specified token matches a
**   prefix query term. In that case both output variables are always set
**   to 0.
**
**   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 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.







<
<
|




>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
.TH FOSSIL "1" "July 2021" "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          cat          diff         ls           revert       timeline
.br
addremove    changes      extras       merge        rm           ui
.br
all          chat         finfo        mv           settings     undo
.br
amend        clean        gdiff        open         sql          unversioned
.br
annotate     clone        grep         pull         stash        update
.br
bisect       commit       help         push         status       version
.br
blame        dbstat       info         rebuild      sync
.br
branch       delete       init         remote       tag
.br






.SH FEATURES

Features as described on the fossil home page.

.HP
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
106
107
108
109
110
111
112
113
  color: rgba(24,24,24,0.8);
  border-radius: 0.1em;
}
.fileage tr:hover,
div.filetreeline:hover {
  background-color: #333;
}



.button,
button {
  color: #aaa;
  background-color: #444;
  border-radius: 5px;
  border: 0
}
.button:hover,
button:hover {
  background-color: #FF4500f0;
  color: rgba(24,24,24,0.8);







>
>
>



|







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
730
731
732
733
734
735
736
737
738
739
740
741
742
  }
  .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 h1, .dir h1, .fileedit h1, .wiki h1 { margin-left: 10pt; }
  .doc > .content h2, .artifact h2, .dir h2, .fileedit h2, .wiki h2 { margin-left: 20pt; }
  .doc > .content h3, .artifact h3, .dir h3, .fileedit h3, .wiki h3 { margin-left: 30pt; }
  .doc > .content h4, .artifact h4, .dir h4, .fileedit h4, .wiki h4 { margin-left: 40pt; }
  .doc > .content h5, .artifact h5, .dir h5, .fileedit h5, .wiki h5 { margin-left: 50pt; }
  .doc > .content hr, .artifact hr, .dir hr, .fileedit hr, .wiki 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







|
|
|
|
|
|







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
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.
**
** If files are attempted to be added explicitly on the command line which
** 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.
**







|







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
758
759
760
761
762
763
764
765
766
**     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.
**
** The command does not "[[commit]]".  You must run the "[[commit]]" separately
** as 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







<
|







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
55
56
57
58
59
60
61
62
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







<







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

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
  }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(name,tag);");















  if( useCheckouts ){
    db_multi_exec(
       "INSERT INTO repolist "
       "SELECT DISTINCT substr(name, 7), name COLLATE nocase"
       "  FROM global_config"
       " WHERE substr(name, 1, 6)=='ckout:'"
       " ORDER BY 1"
    );
  }else{
    db_multi_exec(
       "INSERT INTO repolist "
       "SELECT DISTINCT substr(name, 6), name COLLATE nocase"
       "  FROM global_config"
       " WHERE substr(name, 1, 5)=='repo:'"
       " ORDER BY 1"
    );
  }
  db_multi_exec("CREATE TEMP TABLE toDel(x TEXT)");
  db_prepare(&q, "SELECT name, tag FROM repolist ORDER BY 1");
  while( db_step(&q)==SQLITE_ROW ){
    int rc;
    const char *zFilename = db_column_text(&q, 0);

#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_multi_exec("INSERT INTO toDel VALUES(%Q)", db_column_text(&q, 1));
      nToDel++;
      continue;
    }

    if( zCmd[0]=='l' ){
      fossil_print("%s\n", zFilename);
      continue;
    }else if( showFile ){
      fossil_print("%s: %s\n", useCheckouts ? "check-out" : "repository",
                   zFilename);
    }







>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



|







|





<
|



>






>





>







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
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
}

/*
** 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 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 ){




      @ %s(zHeader)

    }
    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>

  }
  db_finalize(&q);

}

/*
** COMMAND: attachment*







|
>



















>
>
>
>

>












>







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
220
221
222
223

224
225
226
227
228
229
230
** 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;
  Stmt q;
  int cnt = 0;
  int lastGood = -1;
  int lastBad = -1;


  if( zDesc!=0 ){
    blob_init(&log, 0, 0);
    while( zDesc[0]=='y' || zDesc[0]=='n' || zDesc[0]=='s' ){
      int i;
      char c;
      int rid;







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


















<



>







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
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
  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 */
     ");"
  );
  db_prepare(&q, "INSERT OR IGNORE INTO bilog(seq,stat,rid)"
                 " VALUES(:seq,:stat,:rid)");
  while( blob_token(&log, &id) ){
    int rid;
    db_bind_int(&q, ":seq", ++cnt);
    if( blob_str(&id)[0]=='s' ){
      rid = atoi(blob_str(&id)+1);
      db_bind_text(&q, ":stat", "SKIP");
      db_bind_int(&q, ":rid", rid);
    }else{
      rid = atoi(blob_str(&id));
      if( rid>0 ){
        db_bind_text(&q, ":stat","GOOD");
        db_bind_int(&q, ":rid", rid);
        lastGood = rid;
      }else{
        db_bind_text(&q, ":stat", "BAD");
        db_bind_int(&q, ":rid", -rid);
        lastBad = -rid;
      }
    }
    db_step(&q);
    db_reset(&q);
  }
  if( iCurrent>0 ){
    db_bind_int(&q, ":seq", ++cnt);
    db_bind_text(&q, ":stat", "CURRENT");
    db_bind_int(&q, ":rid", iCurrent);
    db_step(&q);
    db_reset(&q);
  }
  if( bDetail && lastGood>0 && lastBad>0 ){
    PathNode *p;
    p = path_shortest(lastGood, lastBad, bisect_option("direct-only"),0, 0);
    while( p ){
      db_bind_null(&q, ":seq");
      db_bind_null(&q, ":stat");
      db_bind_int(&q, ":rid", p->rid);
      db_step(&q);
      db_reset(&q);
      p = p->u.pTo;
    }
    path_reset();
  }
  db_finalize(&q);

  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







|
<


|


|
<



|
<


<
|



<
<


<
|
<
<
<





<
<
|
<
<




|
>







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
548
549
550
551


552
553
554
555
556
557
558
**       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
**
**       Like "fossil ui" except start on a timeline that shows only the
**       check-ins that are part of the current bisect.


**
** > fossil bisect undo
**
**       Undo the most recent "good", "bad", or "skip" command.
*/
void bisect_cmd(void){
  int n;







|


|
>
>







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

728
729

730



731
732
733
734
735
736
737
738
739
740
741
742
    }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";

    newArgv[4] = 0;
    g.argv = newArgv;

    g.argc = 4;



    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");
  }
}







>




>
|
|
>
|
>
>
>












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
670

671
672
673
674
675
676
677
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.

**
** 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 ){







|
>







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
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 participate
**          --users N        List up to N users partipiating
**
**        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,







|
|







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
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 that 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:
**







|







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
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. l*/
  vid = db_lget_int("checkout", 0);

  /* Relative path flag determination is done by a shared function. */
  if( determine_cwd_relative_option() ){
    flags |= C_RELPATH;
  }








|







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
972
973
974
975
976
977
978
979
980
}

/*
** COMMAND: tree
**
** Usage: %fossil tree ?OPTIONS? ?PATHS ...?
**
** List all files in the current check-out in after the fashion of the
** "tree" command.  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]]







|
|







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
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_against_disk(0, &DCfg, diffFiles, &prompt);
      for( i=0; diffFiles[i].zName; ++i ){
        fossil_free(diffFiles[i].zName);
      }
      fossil_free(diffFiles);
    }else{
      diff_against_disk(0, &DCfg, 0, &prompt);
    }
  }
  prompt_for_user_comment(pComment, &prompt);
  blob_reset(&prompt);
}

/*







|





|







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
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 of 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







|







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
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 warning 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







|







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
2565
2566
2567
2568
2569
2570
2571
2572
  **
  ** 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 is enabled.
  */
  if( !db_get_boolean("seen-delta-manifest",0)
   || db_get_boolean("forbid-delta-manifests",0)
   || g.bAvoidDeltaManifests
  ){
    if( !forceDelta ) forceBaseline = 1;
  }







|







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
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
      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);
  }

  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);
  }

  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");
  }








<
<
<
<
<
<
<
<







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
176
177
178
179
180
181
182
183
184
185
186
187
188

189
190
191
192
193
194
195
** 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;
  Blob manifest;
  Blob taglist;
  int flg;

  flg = db_get_manifest_setting();

  if( flg & MFESTFLG_RAW ){
    blob_zero(&manifest);
    content_get(vid, &manifest);
    sterilize_manifest(&manifest, CFTYPE_MANIFEST);
    zManFile = mprintf("%smanifest", g.zLocalRoot);
    blob_write_to_file(&manifest, zManFile);
    free(zManFile);

  }else{
    if( !db_exists("SELECT 1 FROM vfile WHERE pathname='manifest'") ){
      zManFile = mprintf("%smanifest", g.zLocalRoot);
      file_delete(zManFile);
      free(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
212
213
214
215
216
217
218
219
    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_zero(&taglist);
    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'") ){







|







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
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
      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
){
  if( g.fTimeFormat==0 ){
    if( db_get_int("timeline-utc", 1) ){
      g.fTimeFormat = 1;
    }else{
      g.fTimeFormat = 2;
    }
  }
  if( g.fTimeFormat==1 ){
    sqlite3_result_text(context, "0 seconds", -1, SQLITE_STATIC);
  }else{
    sqlite3_result_text(context, "localtime", -1, SQLITE_STATIC);
  }
}

/*







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>















|
<
<
<
<
<
<
<







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
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
**         SELECT julianday(:user_input,fromLocal());
*/
void db_fromlocal_function(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  if( g.fTimeFormat==0 ){
    if( db_get_int("timeline-utc", 1) ){
      g.fTimeFormat = 1;
    }else{
      g.fTimeFormat = 2;
    }
  }
  if( g.fTimeFormat==1 ){
    sqlite3_result_text(context, "0 seconds", -1, SQLITE_STATIC);
  }else{
    sqlite3_result_text(context, "utc", -1, SQLITE_STATIC);
  }
}

/*







|
<
<
<
<
<
<
<







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
1529

1530
1531

1532
1533

1534
1535

1536
1537

1538
1539

1540
1541

1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561


1562
1563
1564
1565
1566
1567
1568

/*
** 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, 0,

                          db_checkin_mtime_function, 0, 0);
  sqlite3_create_function(db, "symbolic_name_to_rid", 1, SQLITE_UTF8, 0,

                          db_sym2rid_function, 0, 0);
  sqlite3_create_function(db, "symbolic_name_to_rid", 2, SQLITE_UTF8, 0,

                          db_sym2rid_function, 0, 0);
  sqlite3_create_function(db, "now", 0, SQLITE_UTF8, 0,

                          db_now_function, 0, 0);
  sqlite3_create_function(db, "toLocal", 0, SQLITE_UTF8, 0,

                          db_tolocal_function, 0, 0);
  sqlite3_create_function(db, "fromLocal", 0, SQLITE_UTF8, 0,

                          db_fromlocal_function, 0, 0);
  sqlite3_create_function(db, "hextoblob", 1, SQLITE_UTF8, 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);



}

#if USE_SEE
/*
** This is a pointer to the saved database encryption key string.
*/







|
>
|
|
>
|
|
>
|
|
>

|
>
|
|
>
|
|
>
|



















>
>







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
2108
2109
2110
2111
2112
2113
2114
2115
  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_zero(&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));







|







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
4085
4086
4087
4088
4089
4090
4091
4092
** 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, then
** 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







|







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
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
** 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: 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: 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







<
<
<
<
<
<
<







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
5367
5368
5369
5370
5371
5372
5373
5374
  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 are 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.







|







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
5432
5433
5434
5435
5436
5437
5438
5439

/*
** COMMAND: test-fingerprint
**
** Usage: %fossil test-fingerprint ?RCVID?
**
** Display the repository fingerprint using the supplied RCVID or
** using the latest RCVID if not 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 ){







|







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
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
.settings-icon:hover {
  border: 1px outset rgba(127,127,127,1);
}
body.fossil-dark-style .settings-icon {
  filter: invert(100%);
}

input[type="checkbox"].diff-toggle {
  float: right;
}

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;
}







<
<
<
<







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
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
** 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;
  const unsigned char *zEnd = (const unsigned char*)&zIn[N&~3];
  unsigned sum = 0;


  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.
**







<

>
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>







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
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
    double rLimitMtime = 0.0;
    if( ridBackTo ){
      rLimitMtime = db_double(0.0,
         "SELECT mtime FROM event WHERE objid=%d",
         ridBackTo);
    }
    db_multi_exec(
      "WITH RECURSIVE "
      "  parent(pid,cid,isCP) AS ("
      "    SELECT plink.pid, plink.cid, 0 AS xisCP FROM plink"
      "    UNION ALL"
      "    SELECT parentid, childid, 1 FROM cherrypick WHERE NOT isExclude"
      "  ),"
      "  ancestor(rid, mtime, isCP) AS ("
      "    SELECT %d, mtime, 0 FROM event WHERE objid=%d "
      "    UNION "
      "    SELECT parent.pid, event.mtime, parent.isCP"
      "      FROM ancestor, parent, event"
      "     WHERE parent.cid=ancestor.rid"
      "       AND event.objid=parent.pid"
      "       AND NOT ancestor.isCP"
      "       AND (event.mtime>=%.17g OR parent.pid=%d)"
      "     ORDER BY mtime DESC LIMIT %d"
      "  )"
      "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);
    }
  }
}







|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<







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
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
void compute_descendants(int rid, int N){
  if( !N ){
     N = -1;
  }else if( N<0 ){
     N = -N;
  }
  db_multi_exec(
    "WITH RECURSIVE"
    "  dx(rid,mtime) AS ("
    "     SELECT %d, 0"
    "     UNION"
    "     SELECT plink.cid, plink.mtime FROM dx, plink"
    "      WHERE plink.pid=dx.rid"
    "      ORDER BY 2"
    "  )"
    "INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d",
    rid, N
  );
}

/*
** COMMAND: descendants*







|
|
|
|
|
|
|
|







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
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

#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;
  Stmt ins;

  Stmt q;
  int rid;

  bag_init(&seen);
  bag_init(&pending);
  db_prepare(&ins, "INSERT OR IGNORE INTO \"%w\" VALUES(:rid)", 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);
    db_bind_int(&ins, ":rid", mid);
    db_step(&ins);
    db_reset(&ins);
  }
  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 ){
      db_bind_int(&ins, ":rid", mid);
      db_step(&ins);
      db_reset(&ins);
    }
  }
  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);
      db_bind_int(&ins, ":rid", mid);
      db_step(&ins);
      db_reset(&ins);
    }
    db_reset(&q);
  }
  db_finalize(&q);

  db_finalize(&ins);
  bag_clear(&seen);
  bag_clear(&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
2467
2468
2469
2470
2471
2472
2473
2474
  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 = -10000;
  score = 0;
  iSXb = iSXp = iS1;
  iEXb = iEXp = iS1;
  iSYb = iSYp = iS2;
  iEYb = iEYp = iS2;
  mid = (iE1 + iS1)/2;
  do{







|







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






3002
3003
3004
3005

3006
3007
3008
3009
3010
3011
3012
  /* 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;
  }






  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;







>
>
>
>
>
>
|
|
|
|
>







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
3054
3055
3056
3057
3058
3059
3060
3061
      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);
      }
    }else if( pCfg->diffFlags & DIFF_RAW ){
      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 ){







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>














|







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
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 --html -y -i -v}
#
# This header comment is stripped off by the "mkbuiltin.c" program.
#
set prog {
package require Tk

array set CFG_light {





|







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



115

116
117
118
119
120
121
122
  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]



  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]}
  
  







>
>
>
|
>







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
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: 1px;
@   text-align: right;
@   padding: 0 1em 0 0;
@ }
@ td.difflne {
@   padding-bottom: 0.4em;
@ }
@ td.diffsep {
@   width: 1px;
@   padding: 0 0.3em 0 1em;
@   line-height: inherit;
@   font-size: inherit;
@ }
@ td.diffsep pre {
@   line-height: inherit;
@   font-size: inherit;







|







|







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
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: 1px;
@   text-align: right;
@   padding: 0 1em 0 0;
@ }
@ td.difflne {
@   padding-bottom: 0.4em;
@ }
@ td.diffsep {
@   width: 1px;
@   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
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
789
790
791

792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
    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.  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_against_disk(
  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 */








|
|
|
>








|



|







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
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
    blob_reset(&fname);
  }
  db_finalize(&q);
  db_end_transaction(1);  /* ROLLBACK */
}

/*
** Run a diff between the undo buffer and 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_against_undo(
  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);







|








|







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
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
**   --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           Select VERSION as source for the diff


**   -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-formated 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                      Diff against the "undo" buffer
**   --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 */


  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");
  }
  g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0;
  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);
  }
  diff_options(&DCfg, isGDiff, 0);
  determine_exec_relative_option(1);










  verify_all_options();

  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);







|
>
>









|



|
















>











|










|


<











<

>
>
>
>
>
>
>
>
>
>

>







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


1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
      " 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( againstUndo ){
    if( db_lget_int("undo_available",0)==0 ){
      fossil_print("No undo or redo is available\n");
      return;
    }
    diff_against_undo(&DCfg, pFileDir);
  }else if( zTo==0 ){
    diff_against_disk(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







>
>
|




|

|







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
1312
1313
1314
1315
1316
1317
1318

























1319
1320
1321
1322
1323
1324
1325
  fossil_print("%s\n", blob_str(&txt));
  blob_reset(&txt);
}

/*
** Return a pointer to the setting information array.
**
** This routine provides access to the aSetting2[] 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;
}


























/*****************************************************************************
** A virtual table for accessing the information in aCommand[], and
** especially the help-text
*/

/* helptextVtab_vtab is a subclass of sqlite3_vtab which is







|






>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
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
  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;
    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();
    }

  }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();
    }

  }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();







|












>















>







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
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, "<hr><h2>Attachments:</h2><ul>");
  document_emit_js();
  style_finish_page();
  manifest_destroy(pTNote);
}

/*
** Add or update a new tech note to the repository.  rid is id of







|







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

1342
1343
1344
1345


1346
1347
1348
1349
1350
1351

1352
1353








1354

1355
1356
1357
1358
1359
1360
1361
1362
1363
  }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 /


** 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){

  blob_zero(pOut);
  if( file_is_absolute_path(zOrigName) ){








    blob_appendf(pOut, "%/", zOrigName);

  }else{
    char zPwd[2000];
    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{







>
|
|
|
|
>
>






>


>
>
>
>
>
>
>
>
|
>

<







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
1739
1740
1741


1742
1743
1744
1745
1746
1747
1748
** 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( z[0]!='/'
#if defined(_WIN32) || defined(__CYGWIN__)
    && (!fossil_isupper(z[0]) || z[1]!=':' || z[2]!='/')


#endif
  ) return 0;

  for(i=0; z[i]; i++){
    if( z[i]=='\\' ) return 0;
    if( z[i]=='/' ){
      if( z[i+1]=='.' ){







|

|
>
>







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
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
  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. */
  const char * zSettingsBool[] = {
  "forum-close-policy",
  NULL /* sentinel entry */
  };

  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>
  @ <p>Users with capabilities 's', 'a', or '6'.</p>
  {
    Stmt q = empty_Stmt;
    int nRows = 0;
    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);
      ++nRows;
      @ <tr>
      @ <td><a href='%R/setup_uedit?id=%d(iUid)'>%h(zUser)</a></td>
      @ <td>(%h(zCap))</td>
      @ </tr>
    }
    db_finalize(&q);
    @</tbody></table>
    if( 0==nRows ){
      @ No supervisors
    }else{
      @ %d(nRows) supervisor(s)
    }
  }

  @ <h2>Moderators</h2>


  @ <p>Users with capability '5'.</p>
  {

    Stmt q = empty_Stmt;
    int nRows = 0;
    db_prepare(&q, "SELECT uid, login, cap FROM user "
               "WHERE cap GLOB '*5*' 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);
      ++nRows;
      @ <tr>
      @ <td><a href='%R/setup_uedit?id=%d(iUid)'>%h(zUser)</a></td>
      @ <td>(%h(zCap))</td>
      @ </tr>
    }
    db_finalize(&q);
    @ </tbody></table>
    if( 0==nRows ){
      @ No non-supervisor moderators
    }else{
      @ %d(nRows) moderator(s)
    }
  }

  @ <h2>Settings</h2>
  @ <p>Configuration settings specific to the forum.</p>
  if( P("submit") && cgi_csrf_safe(2) ){
    int i = 0;
    const char *zSetting;
    db_begin_transaction();
    while( (zSetting = zSettingsBool[i++]) ){

      const char *z = P(zSetting);





      if( !z || !z[0] ) z = "off";

      db_set(zSetting/*works-like:"x"*/, z, 0);
    }
    db_end_transaction(0);
    @ <p><em>Settings saved.</em></p>
  }
  {
    int i = 0;
    const char *zSetting;
    @ <form action="%R/setup_forum" method="post">
    login_insert_csrf_secret();
    @ <table class='forum-settings-list'><tbody>







    while( (zSetting = zSettingsBool[i++]) ){

      @ <tr><td>


      onoff_attribute("", zSetting, zSetting/*works-like:"x"*/, 0, 0);
      @ </td><td>



      @ <a href='%R/help?cmd=%h(zSetting)'>%h(zSetting)</a>



      @ </td></tr>

    }
    @ </tbody></table>
    @ <input type='submit' name='submit' value='Apply changes'>
    @ </form>
  }

  style_finish_page();







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







|
|
|


















<


<









<







<
<
<
<
<



>
>
|
<
>

<

|
>







<







<
<
<
<
<



<


<

|
>
|
>
>
>
>
>
|
>
|






<



>
>
>
>
>
>
>
|
>
|
>
>
|
|
>
>
>
|
>
>
>
|
>







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

1915
1916
1917
1918
1919
1920
1921
1922
  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", isSearch ? "Forum Search Results" : "Forum" );
  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







>
|







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
11






12
13
14
15
16


17


18
19
20





21
22



23






















24
25
26
27
28
29
30
/**
   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;
  const isFdiff = !!document.querySelector('body.fdiff');






  const addToggle = function(diffElem){
    const sib = diffElem.previousElementSibling,
          btn = sib ? D.addClass(D.checkbox(true), 'diff-toggle') : 0;
    if(!sib) return;
    if(isFdiff) sib.parentElement.insertBefore(


                  D.append(D.div(),btn),sib.nextElementSibling);


    else D.append(sib,btn);
    btn.addEventListener('click', function(){
      diffElem.classList.toggle('hidden');





    }, false);
  };



  document.querySelectorAll('table.diff').forEach(addToggle);






















});

window.fossil.onPageLoad(function(){
  const F = window.fossil, D = F.dom;
  const Diff = F.diff = {
    e:{/*certain cached DOM elements*/},
    config: {




>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>





>
>
>

|
>
>
>
>
>
>


|

<
>
>
|
>
>
|
|
|
>
>
>
>
>


>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
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
  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 */;
  const potentialParents = [ /* possible parents for the checkbox */
    /* 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 */ 'body.vinfo div.sectionmenu.info-changes-menu'
  ];
  while( potentialParents.length ){
    if( (eToggleParent = document.querySelector(potentialParents.pop())) ){

      break;
    }
  }
  const keySbsScroll = 'sync-diff-scroll' /* F.storage key */;
  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, "Sync side-by-side scrolling")
    ));
    cbSync.addEventListener('change', function(e){
      F.storage.set(keySbsScroll, e.target.checked);
    });
  }
  const useSync = cbSync ? ()=>cbSync.checked : ()=>F.storage.getBool(keySbsScroll,true);








|
<
<
<
<
<
<
<
<
<
<
|
>
|
<
<
|










|







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

92

93
94
95
96
97
98
99
  */
  dom.label = function(forElem, text){
    const rc = document.createElement('label');
    if(forElem){
      if(forElem instanceof HTMLElement){
        forElem = this.attr(forElem, 'id');
      }

      dom.attr(rc, 'for', forElem);

    }
    if(text) this.append(rc, text);
    return rc;
  };
  /**
     Returns an IMG element with an optional src
     attribute value.







>
|
>







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
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;
    });







|







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
500




501
502
503
504
505
506
507
** 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, const char *zLeftBranch, u32 tmFlags){




  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;







|
>
>
>
>







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
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
        pRoot->aiRiser[iFrom] = -1;
      }
    }
  }

  /*
  ** Compute the rail mapping that tries to put the branch named
  ** zLeftBranch at the left margin.  Other branches that merge
  ** with zLeftBranch 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 ){

    /* 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
    */
    u8 aPriority[GR_MAX_RAIL];

    memset(aPriority, 0, p->mxRail+1);
    if( zLeftBranch ){
      char *zLeft = persistBranchName(p, zLeftBranch);
      for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
        if( pRow->zBranch==zLeft ){


          aPriority[pRow->iRail] |= 4;

          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;

      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," %d", aPriority[i]);



    fprintf(stderr,"\n");
#endif

    j = 0;

    for(i=0; i<=p->mxRail; i++){

      if( aPriority[i]>=4 ) 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++;
    }







|
|










>











|
>
|
|
<

|
>
>
|
>














>













|
>
>
>




>
|
>
|
>
>







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


108

109
110
111
112
113
114
115
#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 = db_get("ssh-command", zDefaultSshCmd);

  blob_init(p, zSsh, -1);
}

/*
** SSH initialization of the transport layer
*/
int transport_ssh_open(UrlData *pUrlData){







>
>
|
>







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
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
  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 */
){
  @ <p>


  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).
    }

    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 ){







|
>
>


















>







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
444
445
446

447


448
449
450
451
452
453
454
455
456
457
      @ %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);
      }else{ 
        @ &nbsp;&nbsp;
        @ %z(href("%R/fdiff?v1=%!S&v2=%!S",zOld,zNew))[diff]</a>

      }


    }
  }
  @ </p>
}

/*
** Generate javascript to enhance HTML diffs.
*/
void append_diff_javascript(int diffType){
  if( diffType==0 ) return;







>

|
<

>

>
>


<







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&nbsp;Diff</a>
  }
  if( diffType!=2 ){
    @ %z(chref("button","%R?diff=2%s",zW))Side-by-Side&nbsp;Diff</a>
  }
  if( diffType!=0 ){
    if( *zW ){
      @ %z(chref("button","%R?diff=%d",diffType))\
      @ Show&nbsp;Whitespace&nbsp;Changes</a>
    }else{
      @ %z(chref("button","%R?diff=%d&w",diffType))Ignore&nbsp;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&nbsp;Diff</a>
  }
  if( diffType!=2 ){
    @ %z(chref("button","%R?diff=2&exbase=%h%s",zExBase,zW))\
    @ Side-by-Side&nbsp;Diff</a>
  }
  if( diffType!=0 ){
    if( *zW ){
      @ %z(chref("button","%R?diff=%d&exbase=%h",diffType,zExBase))\
      @ Show&nbsp;Whitespace&nbsp;Changes</a>
    }else{
      @ %z(chref("button","%R?diff=%d&exbase=%h&w",diffType,zExBase))\
      @ Ignore&nbsp;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
633
634
635
636
637
638
639
640
  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 *zPageHide = "ci"; /* Page that hides 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");







<







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
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
  @ </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!=0 ){
    @ %z(chref("button","%R/%s/%T?diff=0",zPageHide,zName))\
    @ Hide&nbsp;Diffs</a>
  }
  if( diffType!=1 ){
    @ %z(chref("button","%R/%s/%T?diff=1%s",zPage,zName,zW))\
    @ Unified&nbsp;Diffs</a>
  }
  if( diffType!=2 ){
    @ %z(chref("button","%R/%s/%T?diff=2%s",zPage,zName,zW))\
    @ Side-by-Side&nbsp;Diffs</a>
  }
  if( diffType!=0 ){
    if( *zW ){
      @ %z(chref("button","%R/%s/%T",zPage,zName))
      @ Show&nbsp;Whitespace&nbsp;Changes</a>
    }else{
      @ %z(chref("button","%R/%s/%T?w",zPage,zName))







<
<
<
<


|



|







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&nbsp;Diff</a>
  }
  if( diffType!=2 ){
    @ %z(chref("button","%R/%s/%T?diff=2%s",zPage,zName,zW))\
    @ Side-by-Side&nbsp;Diff</a>
  }
  if( diffType!=0 ){
    if( *zW ){
      @ %z(chref("button","%R/%s/%T",zPage,zName))
      @ Show&nbsp;Whitespace&nbsp;Changes</a>
    }else{
      @ %z(chref("button","%R/%s/%T?w",zPage,zName))
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
    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!=0 ){
    style_submenu_element("Hide Diff", "%R/vdiff?diff=0&%b%b", &qp, &qpGlob);
  }
  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);
  }







<
<
<







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
1673
1674
1675
1676
1677
1678
1679
1680
      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
    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
      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
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

/*
** 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
  /* 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
  if( iFrom<1 || iTo<iFrom ){
    ajax_route_error(500, "Invalid line range from=%d, to=%d.",
                     iFrom, iTo);
    return;
  }
  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++;
  }







>
>
>
>
>
>















>




















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|

<
<
<
|

<







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>&nbsp;</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>&nbsp;%S(zUuid)&nbsp;</td>
    }else{
      @ <td>&nbsp;%z(href("%R/info/%!S",zUuid))%S(zUuid)</a>&nbsp;</td>
    }
    if( g.perm.Admin ){
      int rcvid = db_column_int(&q,5);
      if( rcvid<=0 ){
        @ <td>&nbsp;
      }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>&nbsp;
    }
    if( isPriv || isPhantom ){
      if( isPriv==0 ){
        @ <td>phantom</td>
      }else if( isPhantom==0 ){
        @ <td>private</td>
      }else{
        @ <td>private,phantom</td>
      }
    }else{
      @ <td>&nbsp;
    }
    @ </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
282
283
284
285
286
287
288
289
  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 ){
      blob_appendf(out, "<blockquote><table>\n");
    }
    blob_appendf(out,"<tr><td>%h</td><td>&nbsp;&rarr;&nbsp;</td>",
       db_column_text(&q,0));
    blob_appendf(out,"<td>%h</td></tr>\n",
       db_column_text(&q,1));
    n++;
  }







>


|







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>&nbsp;&rarr;&nbsp;</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
1314

1315
1316










1317
1318
1319
1320
1321
1322
1323
**                      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;
  }
  if( cgi_qp_count()<1 ) return;

  isMatch = glob_multi_match(zGlob, g.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();







>







|
>


>
>
>
>
>
>
>
>
>
>







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
2045

2046

2047

2048
2049
2050
2051
2052
2053



2054

2055
2056
2057
2058
2059
2060
2061
  /* 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 to

    ** the home page identified by the "index-page" setting in the repository

    ** CONFIG table, to "/index" if there no "index-page" setting. */

#ifdef FOSSIL_ENABLE_JSON
    if(g.json.isJsonMode){
      json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1);
      fossil_exit(0);
    }
#endif



    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.
  */







|
>
|
>
|
>






>
>
>
|
>







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
3361
3362
3363
3364
3365
3366
3367
3368
    ** 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 = 99;
      fCreate = 0;
      g.argv[2] = 0;
      --g.argc;
    }
  }
  if( isUiCmd && 3==g.argc
   && (zRemote = (char*)file_skip_userhost(g.argv[2]))!=0







|







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
3389
3390
3391
3392
3393
3394
3395
3396
3397
3398
3399
3400
    g.useLocalauth = 1;
    allowRepoList = 1;
  }
  if( !zRemote ){
    find_server_repository(findServerArg, fCreate);
  }
  if( zInitPage==0 ){
    if( isUiCmd && g.localOpen ){
      zInitPage = "timeline?c=current";
    }else{
      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]==']' ){







<
<
<
|
<







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
709
710
711
712
713
714
715
716
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
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







|







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(&regexp, 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(&regexp, 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
325
326

327
328
329
330
331
332
333
**                           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            If given, display instead of run actions
**   --nosync                Do not auto-sync prior to merging

**   -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" */







|

>







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
566
567
568
569
570
571
572
573
  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( !dryRunFlag ) 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 ){







|







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





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
900
901
902
903
904
905
906
907
908
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
    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.
  **





  ** First, find files that have changed from P->M but not P->V.
  ** Copy the M content over into V.
  */
  db_prepare(&q,

    "SELECT idv, ridm, fn, islinkm 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( !dryRunFlag ){
      undo_save(zName);
      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_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,

    "SELECT ridm, idv, ridp, ridv, %s, fn, isexe, islinkv, islinkm 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 rc;
    char *zFullPath;

    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++;











    }else{




      if( !dryRunFlag ) undo_save(zName);
      zFullPath = mprintf("%s/%s", g.zLocalRoot, zName);

      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++;



        }
      }else{
        fossil_print("***** Cannot merge binary file %s\n", zName);
        nConflict++;



      }

















      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 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);




    /* Delete the file idv */
    fossil_print("DELETE %s\n", zName);
    if( chnged ){

      fossil_warning("WARNING: local edits lost for %s", zName);
      nConflict++;






    }
    if( !dryRunFlag ) 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_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.







>
>
>
>
>
|



>
|










>

<








>
>
>
>
>
>
>
>
>
>
>











>
|
>
>
>














>


>











>
>
>
>
>
>
>
>
>
>
>

>
>
>
>
|

>




















>
>
>




>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>












|






>
>
>
>



>


>
>
>
>
>
>

|








>
>
>
>
>
>
>
>
>
>







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
956
957




958
959
960
961
962
963
964
  );
  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( !dryRunFlag ) undo_save(zOldName);
    if( !dryRunFlag ) undo_save(zNewName);




    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
    );







|
|
>
>
>
>







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
1011
1012
1013
1014
1015
1016
1017
1018
    " 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 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(







|







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
1045
1046
1047
1048
1049
1050
1051
1052
      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 ){
      undo_save(zName);
      vfile_to_disk(0, idm, 0, 0);
    }
  }
  db_finalize(&q);

  /* Report on conflicts
  */







>
>
>
>
>
>
>
>

<







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
1104
1105
1106
1107
  }else{
    vmerge_insert(0, mid);
  }
  if( bMultiMerge && nConflict==0 ){
    nMerge++;
    goto merge_next_child;
  }
  if( !dryRunFlag ) undo_finish();

  db_end_transaction(dryRunFlag);
}







|



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 {&amp; & &lt; < &gt; > &#39; ' &quot; \"} $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
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
  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;
}

/*
** 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_at_CPY(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;
}

/*
** pSrc contains an edited file where aC[] describes the edit.  Part of
** pSrc has already been output.  This routine outputs additional lines
** of pSrc - lines that correspond to the next sz lines of the original
** unedited file.
**
** Note that sz counts the number of lines of text in the original file.
** But text is output from the edited file.  So the number of lines transfer
** to pOut might be different from sz.  Fewer lines appear in pOut if there
** are deletes.  More lines appear if there are inserts.
**
** The aC[] array is updated and the new index into aC[] is returned.
*/
static int output_one_side(
  Blob *pOut,     /* Write to this blob */
  Blob *pSrc,     /* The edited file that is to be copied to pOut */
  int *aC,        /* Array of integer triples describing the edit */
  int i,          /* Index in aC[] of current location in pSrc */
  int sz,         /* Number of lines in unedited source to output */
  int *pLn        /* Line number counter */
){
  while( sz>0 ){
    if( aC[i]==0 && aC[i+1]==0 && aC[i+2]==0 ) break;
    if( aC[i]>=sz ){
      blob_copy_lines(pOut, pSrc, sz);  *pLn += sz;
      aC[i] -= sz;
      break;
    }
    blob_copy_lines(pOut, pSrc, aC[i]);      *pLn += aC[i];
    blob_copy_lines(pOut, pSrc, aC[i+2]);    *pLn += aC[i+2];
    sz -= aC[i] + aC[i+1];
    i += 3;
  }
  return i;
}

/*
** 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 <<<<<<<<<<<<",

  "||||||| 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








<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<





>







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
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
*/
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
*/
static int blob_merge(Blob *pPivot, Blob *pV1, Blob *pV2, Blob *pOut){
  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 */
  int useCrLf = 0;
  int ln1, ln2, lnPivot; /* Line numbers for all files */
  DiffConfig DCfg;

  blob_zero(pOut);         /* Merge results stored in pOut */

  /* 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(pV1, 0) && starts_with_utf8_bom(pV2, 0) ){
    blob_append(pOut, (char*)get_utf8_bom(0), -1);
  }

  /* Check once to see if both pV1 and pV2 contains CR/LF endings.
  ** If true, CR/LF pair will be used later to append the
  ** boundary markers for merge conflicts.
  */
  if( contains_crlf(pV1) && contains_crlf(pV2) ){
    useCrLf = 1;
  }

  /* 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);

  aC1 = text_diff(pPivot, pV1, 0, &DCfg);
  aC2 = text_diff(pPivot, pV2, 0, &DCfg);
  if( aC1==0 || aC2==0 ){
    free(aC1);
    free(aC2);
    return -1;
  }

  blob_rewind(pV1);        /* Rewind inputs:  Needed to reconstruct output */
  blob_rewind(pV2);
  blob_rewind(pPivot);

  /* Determine the length of the aC1[] and aC2[] change vectors */


  for(i1=0; aC1[i1] || aC1[i1+1] || aC1[i1+2]; i1+=3){}



  limit1 = i1;

  for(i2=0; aC2[i2] || aC2[i2+1] || aC2[i2+2]; i2+=3){}
  limit2 = i2;

  DEBUG(
    for(i1=0; i1<limit1; i1+=3){
      printf("c1: %4d %4d %4d\n", aC1[i1], aC1[i1+1], aC1[i1+2]);
    }
    for(i2=0; i2<limit2; i2+=3){
      printf("c2: %4d %4d %4d\n", aC2[i2], aC2[i2+1], aC2[i2+2]);
    }

  )

  /* 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;
  ln1 = ln2 = lnPivot = 1;
  while( i1<limit1 && i2<limit2 ){
    DEBUG( printf("%d: %2d %2d %2d   %d: %2d %2d %2d\n",
           i1/3, aC1[i1], aC1[i1+1], aC1[i1+2],
           i2/3, aC2[i2], aC2[i2+1], aC2[i2+2]); )

    if( aC1[i1]>0 && aC2[i2]>0 ){
      /* Output text that is unchanged in both V1 and V2 */
      nCpy = min(aC1[i1], aC2[i2]);
      DEBUG( printf("COPY %d\n", nCpy); )
      blob_copy_lines(pOut, pPivot, nCpy); lnPivot += nCpy;
      blob_copy_lines(0, pV1, nCpy);       ln1 += nCpy;
      blob_copy_lines(0, pV2, nCpy);       ln2 += 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];
      DEBUG( printf("EDIT -%d+%d left\n", nDel, nIns); )
      blob_copy_lines(0, pPivot, nDel);    lnPivot += nDel;
      blob_copy_lines(0, pV1, nDel);       ln1 += nDel;
      blob_copy_lines(pOut, pV2, nIns);    ln2 += 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];
      DEBUG( printf("EDIT -%d+%d right\n", nDel, nIns); )
      blob_copy_lines(0, pPivot, nDel);    lnPivot += nDel;
      blob_copy_lines(0, pV2, nDel);       ln2 += nDel;
      blob_copy_lines(pOut, pV1, nIns);    ln1 += nIns;
      aC2[i2] -= nDel;
      i1 += 3;
    }else
    if( sameEdit(&aC1[i1], &aC2[i2], pV1, pV2) ){
      /* Output edits that are identical in both V1 and V2. */
      assert( aC1[i1]==0 );
      nDel = aC1[i1+1];
      nIns = aC1[i1+2];
      DEBUG( printf("EDIT -%d+%d both\n", nDel, nIns); )
      blob_copy_lines(0, pPivot, nDel);    lnPivot += nDel;
      blob_copy_lines(pOut, pV1, nIns);    ln1 += nIns;
      blob_copy_lines(0, pV2, nIns);       ln2 += 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.
      */
      int sz = 1;    /* Size of the conflict in lines */

      nConflict++;
      while( !ends_at_CPY(&aC1[i1], sz) || !ends_at_CPY(&aC2[i2], sz) ){
        sz++;
      }
      DEBUG( printf("CONFLICT %d\n", sz); )

      append_merge_mark(pOut, 0, ln1, useCrLf);
      i1 = output_one_side(pOut, pV1, aC1, i1, sz, &ln1);

      append_merge_mark(pOut, 1, lnPivot, useCrLf);
      blob_copy_lines(pOut, pPivot, sz);   lnPivot += sz;

      append_merge_mark(pOut, 2, ln2, useCrLf);
      i2 = output_one_side(pOut, pV2, aC2, i2, sz, &ln2);

      append_merge_mark(pOut, 3, -1, useCrLf);
   }

    /* 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.
  */
  DEBUG( printf("%d: %2d %2d %2d   %d: %2d %2d %2d\n",
         i1/3, aC1[i1], aC1[i1+1], aC1[i1+2],
         i2/3, aC2[i2], aC2[i2+1], aC2[i2+2]); )
  if( i1<limit1 && aC1[i1+2]>0 ){
    DEBUG( printf("INSERT +%d left\n", aC1[i1+2]); )
    blob_copy_lines(pOut, pV1, aC1[i1+2]);
  }else if( i2<limit2 && aC2[i2+2]>0 ){
    DEBUG( printf("INSERT +%d right\n", aC2[i2+2]); )
    blob_copy_lines(pOut, pV2, aC2[i2+2]);
  }




  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( count(mergeMarker)==4 );
  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++; }







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>













|






<
<


<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<









>
|
|






|
|
|


>
>
|
>
>
>

>
|
<
|
<
<
<
|
|
<
|
>
|







<

<
<
<
<



|
<
<
<







|
<
<
<







|
<
<
<



|




|
<
<
<








|
>

|


<
|
<
<
|
<
<
|
<
<
|
<
|
<











<
<
<

<
|

<
|

>
>
>


















>
|







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
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
  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*
**
** Usage: %fossil 3-way-merge BASELINE V1 V2 MERGED
**
** Inputs are files BASELINE, V1, and V2.  The file MERGED is generated
** as output.

**
** 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 delta_3waymerge_cmd(void){


  Blob pivot, v1, v2, merged;
  int nConflict;


























  /* We should be done with options.. */
  verify_all_options();

  if( g.argc!=6 ){
    usage("PIVOT V1 V2 MERGED");
  }



  if( blob_read_from_file(&pivot, g.argv[2], ExtFILE)<0 ){
    fossil_fatal("cannot read %s", g.argv[2]);
  }
  if( blob_read_from_file(&v1, g.argv[3], ExtFILE)<0 ){
    fossil_fatal("cannot read %s", g.argv[3]);
  }
  if( blob_read_from_file(&v2, g.argv[4], ExtFILE)<0 ){
    fossil_fatal("cannot read %s", g.argv[4]);
  }
  nConflict = blob_merge(&pivot, &v1, &v2, &merged);


  if( blob_write_to_file(&merged, g.argv[5])<(int)blob_size(&merged) ){


    fossil_fatal("cannot write %s", g.argv[4]);
  }

  blob_reset(&pivot);
  blob_reset(&v1);
  blob_reset(&v2);
  blob_reset(&merged);

  if( nConflict>0 ) 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.
**









>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


|


|
>




















|
>
>
|
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>




|
|

>
>
>
|


|


|


|
>
>
|
>
>
|

>



|
>
|
>







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
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
  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 blob_merge() 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
**        blob_merge() 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 */








  blob_read_from_file(&v1, zV1, ExtFILE);
  rc = blob_merge(pPivot, &v1, pV2, pOut);
  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(pPivot, zPivot);
    zOrig = file_newname(zV1, "original", 1);
    blob_write_to_file(&v1, zOrig);
    zOther = file_newname(zV1, "merge", 1);
    blob_write_to_file(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;







|





|




|








|
|












|
|

>

>
>
>
>
>
>
|
|









|

|

|







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
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
**
** 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[20];
  static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' };
  int n = (int)strlen(zIn);
  int i, j;


  /* Only three forms allowed:


  **   (1)  YYYYMMDD
  **   (2)  YYYYMMDDHHMM
  **   (3)  YYYYMMDDHHMMSS


  */




  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 ) return 0;

  /* Expand the date */
  for(i=j=0; zIn[i]; i++){
    if( i>=4 && (i%2)==0 ){
      zEDate[j++] = aPunct[i/2];
    }
    zEDate[j++] = zIn[i];
  }







  zEDate[j] = 0;

  /* Check for reasonable date values.
  ** Offset references:
  **    YYYY-MM-DD HH:MM:SS
  **    0123456789 12345678
  */







|



>

|
>
>
|
|
|
>
>

>
>
>
>
|
>
>



|


|





>
>
>
>
>
>
>







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
116
117
118
119
120
121
122
123
    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 ){
    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;
}








|







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
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
** 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");
  }
  if( !privOnly && !phantomOnly && mx>n && P("s")==0 ){
    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>
    }


    @ </ul>
    style_finish_page();
    return;
  }
  if( phantomOnly || privOnly || mx>n ){
    style_submenu_element("Index", "bloblist");
  }
  if( privOnly ){

    zRange = mprintf("IN private");
  }else if( phantomOnly ){

    zRange = mprintf("IN phantom");







  }else{
    zRange = mprintf("BETWEEN %d AND %d", s, s+n-1);
  }
  describe_artifacts(zRange);
  fossil_free(zRange);
  db_prepare(&q,

    "SELECT rid, uuid, summary, isPrivate, type='phantom', rcvid, ref"

    "  FROM description ORDER BY rid"


  );
  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( 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>&nbsp;%z(href("%R/info/%!S",zUuid))%S(zUuid)</a>&nbsp;</td>
    if( g.perm.Admin ){
      int rcvid = db_column_int(&q,5);
      if( rcvid<=0 ){
        @ <td>&nbsp;
      }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>&nbsp;
    }







>









>
>









>
>
>
>
>
>






>










|







>
>








>


>

>
>
>
>
>
>
>






>
|
>
|
>
>









<
|
<
<
<






|
>












|
<
|
|
|
<







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>&nbsp;%z(href("%R/info/%!S",zUuid))%S(zUuid)</a>&nbsp;</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>&nbsp;
    }
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>&nbsp;
  @ <th>Hash
  @ <th>Date&nbsp;Received
  @ <th>Size
  @ <th>Compressed&nbsp;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>&nbsp;
    }
    @ <td align="right">%,lld(sz)
    @ <td align="right">%,lld(szC)
    if( g.perm.Admin ){
      if( zUser ){
        @ <td>%h(zUser)
      }else{
        @ <td>&nbsp;
      }
      if( zIp ){
        @ <td>%h(zIp)
      }else{
        @ <td>&nbsp;
      }
    }
    @ </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
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
*/
static void mkdeltaFunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  const char *zFile;
  Blob x, y;
  int rid;
  char *aOut;
  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;
  }
  aOut = sqlite3_malloc64(sz+70);
  if( aOut==0 ){
    sqlite3_result_error_nomem(context);
    blob_reset(&y);
    blob_reset(&x);
    return;
  }
  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), aOut);

  blob_reset(&x);
  blob_reset(&y);
  blob_init(&x, aOut, nOut);
  blob_compress(&x, &x);
  sqlite3_result_blob64(context, blob_buffer(&x), blob_size(&x),
                        SQLITE_TRANSIENT);
  blob_reset(&x);
}


/*
** Generate a binary patch file and store it into the file
** named zOut.  Or if zOut is NULL, write it into out.
**







|

<




















<
<
<
|
|
<
<









|
>


<
|
|

|







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
392
393
394
395
396
397
398
399

  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", 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));







|







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
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
        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 ){
        blob_appendf(&cmd, " merge %s\n", db_column_text(&q,1));

      }else{
        blob_appendf(&cmd, " merge --%s %s\n", zType, db_column_text(&q,1));

      }

      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);



































  }

  /* 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 ){







>









|
>

|
>

>
















>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
147
148
149
150
151
152
153
154
** 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/$";
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 },







|







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
396
397
398
399
400
401
402
403
404
405
406
407
408
409



410
411
412
413
414
415
416
  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,
     "SELECT name FROM sqlite_schema /*scan*/"
     " WHERE type='table'"
     " 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);



  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(







>
>
>

|
|






|





>
>
>







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
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
@ );
@
@ -- Identifier for this file type.
@ -- The integer is the same as 'FSLG'.
@ PRAGMA application_id=252006675;
;

#if INTERFACE







|







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
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 )
@ );
@
@ -- 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 UNIQUE,          -- UUID of artifact to be shunned. Canonical form
@   mtime DATE,           -- When added.  seconds since 1970
@   scom TEXT             -- Optional text explaining why the shun occurred
@ );
@
@ -- 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.







|












|


|







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
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
@ );
@
@ -- The application ID helps the unix "file" command to identify the
@ -- database as a fossil repository.
@ PRAGMA application_id=252006673;
;

/*







|







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
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 database
@ -- 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 )
@ );
@
@ -- 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.
@ --







|













|







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
506
507
508











509
510
511
512
513
514
515
  @ 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)
  @ <p>
  textarea_attribute("", 2, 80,
      "robot-restrict", "rbrestrict", "", 0);












  @ <hr>
  @ <p><input type="submit"  name="submit" value="Apply Changes"></p>
  @ </div></form>
  db_end_transaction(0);
  style_finish_page();
}







|


>
>
>
>
>
>
>
>
>
>
>







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
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='ktxTTKt' data-init-sort='2'>
  @ <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;"







|







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
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
  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"

     "  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 sortkey", 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);

    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>%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{







|
>



|














>












|







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
1021
1022
1023
1024
1025
1026
1027
1028
    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))</th></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>







|







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
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
  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;

#if 0  /* Removed 2021-01-26 */
  const struct {
    const char *zTitle;
    const char *zProperty;
  } aExtra[] = {
    { "Documentation",  "sitemap-docidx" },
    { "Download",       "sitemap-download" },
    { "License",        "sitemap-license" },
    { "Contact",        "sitemap-contact" },
  };
#endif

  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>
  }

#if 0  /* Removed 2021-01-26  */
  for(i=0; i<sizeof(aExtra)/sizeof(aExtra[0]); i++){
    char *z = db_get(aExtra[i].zProperty,0);
    if( z==0 || z[0]==0 ) continue;
    if( !inSublist ){
      @ <ul>
      inSublist = 1;
    }
    if( z[0]=='/' ){
      @ <li>%z(href("%R%s",z))%s(aExtra[i].zTitle)</a></li>
    }else{
      @ <li>%z(href("%s",z))%s(aExtra[i].zTitle)</a></li>
    }
  }
#endif

  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);







<
<
<
<
<
<
<
<
<
<
<
<



















<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<







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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159



160
161
162
163
164
165
166
        }
      }
    }
    Th_Free(g.interp, azExtra);
  }
  if( (e&1)!=0 ) goto end_of_sitemap;

#if 0  /* Removed on 2021-02-11.  Make a sitemap-extra entry if you */
       /* really want this */
  if( srchFlags & SRCH_DOC ){
    if( !inSublist ){
      @ <ul>
      inSublist = 1;
    }
    @ <li>%z(href("%R/docsrch"))Documentation Search</a></li>
  }
#endif

  if( inSublist ){
    @ </ul>
    inSublist = 0;
  }
  @ </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>







<
<
<
<
<
<
<
<
<
<
<





>
>
>







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

197
198
199
200
201
202
203
204
  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 ){

    @ <li>%z(href("%R/forum"))Forum</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>







>
|







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
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&n=%d&y=%s",
                 zTimeframe, nCount,
                 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);
      }







|
|







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
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&n=%d&y=%s",
               zYear, zWeek, nCount,
               statsReportTimelineYFlag);
    if( zUserName ){
      cgi_printf("&u=%t",zUserName);
    }
    cgi_printf("'>%s</a></td>",zWeek);

    cgi_printf("<td>%d</td>",nCount);







|
|







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
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 */
  const char *zLeftBranch, /* Strive to put this branch on the left margin */
  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;







|







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
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, zLeftBranch, tmFlags);
    if( pGraph->nErr ){
      graph_free(pGraph);
      pGraph = 0;
    }else{
      @ <tr class="timelineBottom" id="btm-%d(iTableId)">\
      @ <td></td><td></td><td></td></tr>
    }







|







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
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
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
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
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
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549

1550
1551
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
1577
1578
*/
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"
      " WHERE mlink.fnid=filename.fnid 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);
}

/*
** Tag match expression type code.
*/
typedef enum {
  MS_EXACT,   /* Matches a single tag by exact string comparison. */
  MS_GLOB,    /* Matches tags against a list of GLOB patterns. */
  MS_LIKE,    /* Matches tags against a list of LIKE patterns. */
  MS_REGEXP,  /* Matches tags against a list of regular expressions. */
  MS_BRLIST,  /* Same as REGEXP, except the regular expression is a list
              ** of branch names */
} MatchStyle;

/*
** 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 tag match SQL expression.
**
** 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.
*/
static const char *tagMatchExpression(
  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(&regexp, 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;
}

/*
** 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){
  static char zEDate[20];
  static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' };
  int n = (int)strlen(zIn);
  int i, j;

  /* Only three forms allowed:

  **   (1)  YYYYMMDD
  **   (2)  YYYYMM
  **   (3)  YYYYWW
  */






  if( n!=8 && n!=6 ) return zIn;

  /* Every character must be a digit */
  for(i=0; fossil_isdigit(zIn[i]); i++){}
  if( i!=n ) return zIn;

  /* Expand the date */
  for(i=j=0; zIn[i]; i++){
    if( i>=4 && (i%2)==0 ){
      zEDate[j++] = aPunct[i/2];
    }
    zEDate[j++] = zIn[i];
  }
  zEDate[j] = 0;

  /* It looks like this may be a date.  Return it with punctuation added. */
  return zEDate;
}




































/*
** 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







|
|
>











<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<








|
|
<



|
>




>
>
>
>
>
>



|



|
<
|
<







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







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
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720


/*
** 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=HASHLIST      Show all check-ins in the comma-separated HASHLIST
**                    in addition to check-ins specified by t= or r=
**    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







|
|


|




|
|







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
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
**    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           Show check-ins related to TAG, equivalent to t=TAG&rel
**    tl=TAGLIST      Shorthand for t=TAGLIST&ms=brlist
**    rl=TAGLIST      Shorthand for r=TAGLIST&ms=brlist


**    rel             Show related check-ins as well as those matching t=TAG
**    mionly          Limit rel to show ancestors but not descendants
**    nowiki          Do not show wiki associated with branch or tag
**    ms=MATCHSTYLE   Set tag match style to EXACT, GLOB, LIKE, 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       Show family (immediate parents and children) of CHECKIN
**    from=CHECKIN    Path 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



**    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=YYYY-MM      Show only events for the given year/month
**    yw=YYYY-WW      Show only events for the given week of the given year
**    yw=YYYY-MM-DD   Show events for the week that includes the given day
**    ymd=YYYY-MM-DD  Show only events on the given day. The use "ymd=now"
**                    to see all changes for the current week.


**    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 text
**
** 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,







|
|
|
>
>

|

|
>



|








|
|






>
>
>

|
|

|







|
|
|
|
|
>
>








|







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
1948
1949
1950
1951
1952
1953
1954
1955

  /* 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;
  }
  login_check_credentials();
  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 ){







<







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
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
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045
    );
  }

  /* 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 ){
      zTagName = z;
      zMatchStyle = "brlist";
    }
    if( (z = P("rl"))!=0 ){
      zBrName = z;












      zMatchStyle = "brlist";

    }
  }

  /* Convert r=TAG to t=TAG&rel in order to populate the UI style widgets. */
  if( zBrName && !related ){
    cgi_delete_query_parameter("r");
    cgi_set_query_parameter("t", zBrName);  (void)P("t");
    cgi_set_query_parameter("rel", "1");
    zTagName = zBrName;
    related = 1;
    zType = "ci";
  }

  /* Ignore empty tag query strings. */
  if( zTagName && !*zTagName ){
    zTagName = 0;
  }

  /* Finish preliminary processing of tag match queries. */

  if( zTagName ){
    zType = "ci";
    /* Interpet the tag style string. */
    if( fossil_stricmp(zMatchStyle, "glob")==0 ){
      matchStyle = MS_GLOB;
    }else if( fossil_stricmp(zMatchStyle, "like")==0 ){
      matchStyle = MS_LIKE;
    }else if( fossil_stricmp(zMatchStyle, "regexp")==0 ){
      matchStyle = MS_REGEXP;
    }else if( fossil_stricmp(zMatchStyle, "brlist")==0 ){
      matchStyle = MS_BRLIST;
    }else{
      /* 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 = tagMatchExpression(matchStyle, zTagName, &zMatchDesc, &zError);
  }

  if( zMark && zMark[0]==0 ){
    if( zAfter ) zMark = zAfter;
    if( zBefore ) zMark = zBefore;
    if( zCirca ) zMark = zCirca;
  }







>

|
<
<
|
|
>
>
>
>
>
>
>
>
>
>
>
>
|
>




|




|









>


<
<
|
<
<
<
<
<
<
<











|







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
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
  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"
      "  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)=="
      "           (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
      "   GROUP BY pid"
      "   HAVING count(*)>1;\n"
      "INSERT OR IGNORE INTO rnfork(rid)"
      "  SELECT cid FROM plink\n"
      "   WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)=="
      "           (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
      "   GROUP BY cid"
      "   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"
      "     AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)=="
      "           (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
      " UNION "
      "  SELECT pid FROM plink\n"
      "   WHERE cid IN rnfork"
      "     AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)=="
      "           (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) ){







>

















|








|
|
|

|

|
|
|






|
|
|
|

|
|
|







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
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
    /* 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{
      if( path_common_ancestor(me_rid, you_rid) ){

        p = path_first();
      }






      zFrom = P("me");
      zTo = P("you");



    }
    blob_init(&ins, 0, 0);
    db_multi_exec(
      "CREATE TABLE IF NOT EXISTS temp.pathnode(x INTEGER PRIMARY KEY);"
    );
    if( p ){

      blob_init(&ins, 0, 0);
      blob_append_sql(&ins, "INSERT INTO pathnode(x) VALUES(%d)", p->rid);
      p = p->u.pTo;
      while( p ){





        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 || P("mionly") ){
      db_multi_exec(
        "CREATE TABLE IF NOT EXISTS temp.related(x INTEGER PRIMARY KEY);"
        "INSERT OR IGNORE INTO related(x)"
        "  SELECT pid FROM plink WHERE cid IN pathnode AND NOT isprim;"
      );
      if( P("mionly")==0 ){
        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( P("mionly")==0 ){
          db_multi_exec(
            "INSERT OR IGNORE INTO related(x)"
            "  SELECT childid FROM cherrypick WHERE parentid IN pathnode;"
          );
        }












      }
      db_multi_exec("INSERT OR IGNORE INTO pathnode SELECT x FROM related");
    }

    blob_append_sql(&sql, " AND event.objid IN pathnode");
    if( zChng && zChng[0] ){
      db_multi_exec(
        "DELETE FROM pathnode "
        " WHERE NOT EXISTS(SELECT 1 FROM mlink, filename"
                          " WHERE mlink.mid=x"
                          "   AND mlink.fnid=filename.fnid 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);







>
>






>
>


>
>




|
>


>
>
>
>
>
>
|
|
>
>
>



|


>




>
>
>
>
>
|
>






|

|

|

|


|





|

|


|


>
>
>
>
>
>
>
>
>
>
>
>



>



|
|
|
|
>







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
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357



2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
    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 ){
      Stmt s;
      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);
        }
      }



      db_prepare(&s,
        "WITH RECURSIVE"
        "  dx(rid,mtime) AS ("
        "     SELECT %d, 0"
        "     UNION"
        "     SELECT plink.cid, plink.mtime FROM dx, plink"
        "      WHERE plink.pid=dx.rid"
        "        AND (:stop>=8e99 OR plink.mtime<=:stop)"
        "      ORDER BY 2"
        "  )"
        "INSERT OR IGNORE INTO ok SELECT rid FROM dx LIMIT %d",
        d_rid, nEntry<=0 ? -1 : nEntry+1
      );
      db_bind_double(&s, ":stop", rStopTime);
      db_step(&s);
      db_finalize(&s);
      /* compute_descendants(d_rid, nEntry==0 ? 0 : 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");







>







<















>
>
>
|
|
<
|
|
|
|
|
|
|

|

<
<
<
<







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
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
    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 ");
    }
    if( renameOnly ){
      blob_append_sql(&cond, " AND event.objid IN rnfile ");
    }
    if( forkOnly ){
      blob_append_sql(&cond, " AND event.objid IN rnfork ");
    }
    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 ");
    }
    if( bisectLocal || zBisect!=0 ){
      blob_append_sql(&cond, " AND event.objid IN (SELECT rid FROM bilog) ");
    }
    if( zYearMonth ){
      char *zNext;


      zYearMonth = timeline_expand_datetime(zYearMonth);
      if( strlen(zYearMonth)>7 ){
        zYearMonth = mprintf("%.7s", zYearMonth);
      }
      if( db_int(0,"SELECT julianday('%q-01') IS NULL", zYearMonth) ){
        zYearMonth = db_text(0, "SELECT strftime('%%Y-%%m','now');");
      }
      zNext = db_text(0, "SELECT strftime('%%Y-%%m','%q-01','+1 month');",
                      zYearMonth);
      if( db_int(0,
          "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
          " WHERE blob.rid=event.objid AND mtime>=julianday('%q-01')%s)",

          zNext, blob_sql_text(&cond))
      ){


        zNewerButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0));
        zNewerButtonLabel = "Following month";
      }
      fossil_free(zNext);
      zNext = db_text(0, "SELECT strftime('%%Y-%%m','%q-01','-1 month');",
                      zYearMonth);

      if( db_int(0,
          "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
          " WHERE blob.rid=event.objid AND mtime<julianday('%q-01')%s)",

          zYearMonth, blob_sql_text(&cond))
      ){


        zOlderButton = fossil_strdup(url_render(&url, "ym", zNext, 0, 0));
        zOlderButtonLabel = "Previous month";
      }
      fossil_free(zNext);

      blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%m',event.mtime) ",


                      zYearMonth);
      nEntry = -1;


    }
    else if( zYearWeek ){
      char *z, *zNext;


      zYearWeek = timeline_expand_datetime(zYearWeek);
      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')");
        }
      }
      zNext = db_text(0, "SELECT date(%Q,'+7 day');", zYearWeekStart);
      if( db_int(0,
          "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
          " WHERE blob.rid=event.objid AND mtime>=julianday(%Q)%s)",

          zNext, blob_sql_text(&cond))
      ){


        zNewerButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0));
        zNewerButtonLabel = "Following week";





































      }





















      fossil_free(zNext);
      zNext = db_text(0, "SELECT date(%Q,'-7 days');", zYearWeekStart);

      if( db_int(0,
          "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
          " WHERE blob.rid=event.objid AND mtime<julianday(%Q)%s)",

          zYearWeekStart, blob_sql_text(&cond))
      ){




        zOlderButton = fossil_strdup(url_render(&url, "yw", zNext, 0, 0));
        zOlderButtonLabel = "Previous week";
      }
      fossil_free(zNext);

      blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%W',event.mtime) ",


                   zYearWeek);
      nEntry = -1;






    }
    else if( zDay ){
      char *zNext;




      zDay = timeline_expand_datetime(zDay);
      zDay = db_text(0, "SELECT date(%Q)", zDay);
      if( zDay==0 || zDay[0]==0 ){
        zDay = db_text(0, "SELECT date('now')");
      }
      zNext = db_text(0, "SELECT date(%Q,'+1 day');", zDay);

      if( db_int(0,
          "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
          " WHERE blob.rid=event.objid AND mtime>=julianday(%Q)%s)",

          zNext, blob_sql_text(&cond))
      ){


        zNewerButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
        zNewerButtonLabel = "Following day";
      }
      fossil_free(zNext);
      zNext = db_text(0, "SELECT date(%Q,'-1 day');", zDay);

      if( db_int(0,
          "SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
          " WHERE blob.rid=event.objid AND mtime<julianday(%Q)%s)",

          zDay, blob_sql_text(&cond))
      ){


        zOlderButton = fossil_strdup(url_render(&url, "ymd", zNext, 0, 0));
        zOlderButtonLabel = "Previous day";
      }
      fossil_free(zNext);

      blob_append_sql(&cond, " AND %Q=date(event.mtime) ",


                   zDay);
      nEntry = -1;



    }
    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;







|


|


|







|


|



>
>
|
<
|
<



|
<


|
>
|

>
>


<
|
<
<
>


|
>
|

>
>


<
|
>
|
>
>
|

>
>



>
>
|




















|


|
>
|

>
>


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
<
>
|

|
>
|

>
>
>
>
|
|
<
|
>
|
>
>
|

>
>
>
>
>
>



>
>
>
>
|




<
>


|
>
|

>
>


<
|
<
>


|
>
|

>
>


<
|
>
|
>
>
|

>
>
>







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
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
2741
2742
2743
2744
2745
2746
      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"
        " SELECT tagxref.rid FROM tagxref NATURAL JOIN tag"

        " WHERE %s AND tagtype>0", 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);
      }
      if( P("x")!=0 ){
        char *zX = fossil_strdup(P("x"));
        int ii;
        int ridX;
        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;
          ridX = name_to_rid(zX);
          db_multi_exec(
            "INSERT OR IGNORE INTO selected_nodes(rid) VALUES(%d)", ridX);
          zX[ii] = c;
          zX += ii;
        }
      }
      if( !related ){
        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"
          " SELECT pid FROM selected_nodes CROSS JOIN plink"
          " WHERE selected_nodes.rid=plink.cid;"
        );
        if( P("mionly")==0 ){
          db_multi_exec(
            "INSERT OR IGNORE INTO related_nodes"
            " SELECT cid FROM selected_nodes CROSS JOIN plink"
            " WHERE selected_nodes.rid=plink.pid;"
          );
          if( showCherrypicks ){
            db_multi_exec(
              "INSERT OR IGNORE INTO related_nodes"
              " SELECT childid FROM selected_nodes CROSS JOIN cherrypick"
              " WHERE selected_nodes.rid=cherrypick.parentid;"
            );
          }
        }
        if( showCherrypicks ){
          db_multi_exec(
            "INSERT OR IGNORE INTO related_nodes"
            " SELECT parentid FROM selected_nodes CROSS JOIN cherrypick"
            " WHERE selected_nodes.rid=cherrypick.childid;"
          );
        }
        if( (tmFlags & TIMELINE_UNHIDE)==0 ){
          db_multi_exec(
            "DELETE FROM related_nodes WHERE rid IN "

            " (SELECT related_nodes.rid FROM related_nodes, tagxref"

            " WHERE tagid=%d AND tagtype>0 AND tagxref.rid=related_nodes.rid)",
            TAG_HIDDEN
          );
        }
      }
    }
    if( (zType[0]=='w' && !g.perm.RdWiki)
     || (zType[0]=='t' && !g.perm.RdTkt)







|
|
>
|








<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
|














|
|
|

|

|
|
|



|
|
|





|
|
|




|
>
|
>
|







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
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
2879
2880
2881
2882
2883
2884
2885
2886
2887
    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"
           " ORDER BY event.mtime ASC", rAfter-ONE_SECOND, rBefore+ONE_SECOND);
        nEntry = -1;
      }else{
        blob_append_sql(&sql,
           " AND event.mtime>=%.17g  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 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 ORDER BY event.mtime ASC", rCirca);
      if( nEntry>0 ){
        blob_append_sql(&sql2," LIMIT %d", (nEntry+1)/2);
      }
      if( PB("showsql") ){
         @ <pre>%h(blob_sql_text(&sql2))</pre>
      }
      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 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-01",
                   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 ){







|




|






|







|



<
<
<







|












|







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
3014

3015

3016
3017
3018
3019
3020
3021
3022
      if( advancedMenu ){
        style_submenu_entry("t", "Tag Filter:", -8, 0);
        style_submenu_multichoice("ms", count(azMatchStyles)/2,azMatchStyles,0);
      }
    }
    blob_zero(&cond);
  }
  if( PB("showsql") ){

    @ <pre>%h(blob_sql_text(&sql))</pre>

  }
  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"));







|
>
|
>







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














3073
3074


3075
3076
3077
3078
3079
3080


3081
3082
3083
3084
3085
3086
3087
  }

  if( zNewerButton ){
    @ %z(chref("button","%s",zNewerButton))%h(zNewerButtonLabel)\
    @ &nbsp;&uarr;</a>
  }
  cgi_check_for_malice();














  www_print_timeline(&q, tmFlags, zThisUser, zThisTag, zBrName,
                     selectedRid, secondaryRid, 0);


  db_finalize(&q);
  if( zOlderButton ){
    @ %z(chref("button","%s",zOlderButton))%h(zOlderButtonLabel)\
    @ &nbsp;&darr;</a>
  }
  document_emit_js(/*handles pikchrs rendered above*/);


  style_finish_page();
}

/*
** Translate a timeline entry into the printable format by
** converting every %-substitutions as follows:
**







>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
|
>
>






>
>







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)\
    @ &nbsp;&uarr;</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)\
    @ &nbsp;&darr;</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
3759
3760
3761
3762
3763
3764
3765
3766
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 ){
    zToday = timeline_expand_datetime(zToday);
    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);







>










|







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
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, "<hr><h2>Attachments:</h2><ul>");
  }

  style_finish_page();
}

/*
** TH1 command: append_field FIELD STRING







|







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
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
  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 ){
      /* Merge the changes in the current tree into the target version */
      Blob r, t, v;
      int rc;
      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);

        nConflict++;
      }else{



        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 ){



            fossil_print("***** %d merge conflicts in %s\n", rc, zNewName);
            nConflict++;


          }
        }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++;



        }
      }
      if( nameChng && !dryRunFlag ) file_delete(zFullPath);
      blob_reset(&v);
      blob_reset(&t);
      blob_reset(&r);


    }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);
      }
















    }
    free(zFullPath);
    free(zFullNewPath);
  }
  db_finalize(&q);
  db_finalize(&mtimeXfer);
  fossil_print("%.79c\n",'-');





  if( nUpdate==0 ){
    show_common_info(tid, "checkout:", 1, 0);
    fossil_print("%-13s None. Already up-to-date\n", "changes:");
  }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.\n", "changes:",
                 nUpdate, nUpdate>1 ? "s" : "");
  }

  /* Report on conflicts
  */
  if( !dryRunFlag ){
    Stmt q;
    int nMerge = 0;







>















>
>
>
>



>











>
>
>












>
>
>














>






>










>
>
>



















<
<
<







>


>
>
>












>
>
>


>
>




















>
>
>

<
<
|
|
|
>
>











>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







>
>
>
>
>


|





|
|







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

843



844
845
846
847
848
849
850
**
** 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();

  undo_begin();



  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);







>













>







>
















>
|
>
>
>







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
940
941
942
943
944
945
946
947
    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{
        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;"







|







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
967
968
969
970
971
972
973
974
        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);

      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);







|







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
993
994
995
996
997
998
999
         mtime, rvChnged, rvPerm==PERM_EXE, rvPerm==PERM_LNK, zFile, zFile
      );
    }
    blob_reset(&record);
    free(zFull);
  }
  db_finalize(&q);
  undo_finish();
  db_end_transaction(0);

  /* Deallocate parsed manifest structures. */
  manifest_destroy(pRvManifest);
  manifest_destroy(pCoManifest);
}







|






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
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("<hr><h2><a href='%R/attachlist?name=%T'>"
                            "Attachments</a>:</h2><ul>",
                            zPageName);
    attachment_list(zPageName, zLabel);
    fossil_free(zLabel);
    document_emit_js(/*for optional pikchr support*/);
    style_finish_page();
  }
}

/*







|
|

|







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
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







|







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
1986
1987
1988
1989
1990
1991
1992
1993
){
  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;          /* 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 */







|







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

2454


2455
2456
2457
2458
2459
2460
2461
      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);


        }
      }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







>
|
>
>







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
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
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 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 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 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 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 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 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







|








|








|








|








|








|







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
151
152
153
154
155
156
157
158
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 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







|







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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
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 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 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
} {







|








|







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
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
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 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 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
} {







|








|







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
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
merge-test 44 {
  2 3 4 5 6 7 8
} {
  6 7 8
} {
  2 3 4
} {
  MINE: 6 7 8 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 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
} {







|








|







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
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
merge-test 54 {
  2 3 4 5 6 7 8
} {
  2 3 4
} {
  6 7 8
} {
  MINE: 2 3 4 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 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
} {







|








|







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
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
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 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 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
} {







|








|







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
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
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 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 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
} {







|








|







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
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
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 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 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
} {







|








|







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
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
merge-test 94 {
  2 3 4 5 6 7 8
} {
  2 3 4
} {
  2b 3b 4b 5b 6 7 8
} {
  MINE: 2 3 4 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 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
} {







|








|







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
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
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 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 COM: 9 YOURS: 9b END
} -expectError

###############################################################################

test_cleanup







|








|





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
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
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 = 2 3 4c 5c 6c 7 8 < 9
} {
  1 > 2 3 4c 5c 6c 7 8 = 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 = 2 3 4c 5c 6c 7 8 <
} {
  > 2 3 4c 5c 6c 7 8 = 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







|

|



















|

|







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
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







|







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
450
451
452
453
454
455
456
457
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
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







|







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
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 {.*?\ncommon 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}]








|







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
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
  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] && 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 && 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( 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 && 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{







|









|












|

















|







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
176
177
178
179
180
181
182
183
      */
      const char *zNewline = "\\n";
      int indent;
      int nC;
      int nParam;
      char c;
      i++;
      if( 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;
        }







|







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
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 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)\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 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







|

|


















|







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
1014
1015
$(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 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







|

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
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 a 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.








|







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
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 a 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.







|







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
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 a 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>







|







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















































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
<title>Change Log</title>
















































<h2 id='v2_25'>Changes for version 2.25 (pending)</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 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.


  *  Numerous minor fixes and additions.


<h2 id='v2_24'>Changes for version 2.24 (2024-04-23)</h2>

  *  Apache change work-around &rarr; As part of a security fix, the Apache webserver
     mod_cgi module has stopped relaying the Content-Length field of the HTTP


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|












>







>
>







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 &rarr; 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
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







|







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
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 a 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.








|







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
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 a 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







|







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
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 a 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.








|







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
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 a 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







|







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
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.24 ([/timeline?c=version-2.24|2024-04-23])</h3>

  *  [/uv/download.html|Download]
  *  [./changes.wiki#v2_24|Change Summary]
  *  [/timeline?p=version-2.24&bt=version-2.23&y=ci|Check-ins in version 2.24]
  *  [/timeline?df=version-2.24&y=ci|Check-ins derived from the 2.24 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].







|


|
|
|







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
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>







|







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
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

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),
this has been fixed.  Commands like

  * "[fossil server](/help?cmd=server)"
  * "[fossil ui](/help?cmd=ui)", and
  * "[fossil http](/help?cmd=http)"

now all handle server-mode SSL/TLS encryption natively.  It is now possible
to run a secure Fossil server without having to put Fossil behind an encrypting
web server or reverse proxy.  Hence, it is now possible to stand up a complete
Fossil project website on an inexpensive VPS with no added software other than
Fossil itself and something like [certbot](https://certbot.eff.org) for
obtaining a CA-signed certificate.

## 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

The --cert option is what tells Fossil to use TLS encryption.
Normally, the argument to --cert is the name of a file containing
the certificate (the "fullchain.pem" file) for the website.  In this
example, the magic name "unsafe-builtin" is used, which causes Fossil
to use a self-signed cert rather than a real cert obtained from a
[Certificate Authority](https://en.wikipedia.org/wiki/Certificate_authority)

or "CA".  As the name implies, this self-signed cert is not secure and
should only be used for testing.  Your web-browser will complain bitterly 
and will refuse to display the pages using the "unsafe-builtin" cert.


Firefox will allow you to click an "I know the risks" button and continue.
Other web browsers will stubornly refuse to display the page, under the theory



that weak encryption is worse than no encryption at all.  Continue reading



to see how to solve this.



## About Certs


Certs are based on public-key or asymmetric cryptography.  To create a cert,








you first create a new "key pair" consisting of a public key and a private key.
The public key can be freely shared with the world, but you must keep the
private key secret.  If anyone gains access to your private key then he will be
able to impersonate you and break into your system.





To obtain a cert, you send your public key and the name of the domain you


want to protect to a certificate authority.  The CA then digitally signs
the combination of those two things using their own private key and sends
the signed combination back to you.  The CA's digital signature of your




public key and domain name is the cert.









SSL/TLS servers need two things in order to prove their identity to clients:




  1.  The cert that was signed by a CA
  2.  The private key




The SSL/TLS servers send the cert to each client, so that the client
can verify it.  But the private key is kept strictly private and is never
shared with anyone.


## How To Tell Fossil About Your Cert And Private Key


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](/info/c2a7b14c3f541edb96?ln=89-116) 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.  The 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.



If you have a single 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
at /home/www and drop all root privileges.









### Keeping The Cert And Private Key In Separate Files







If you do not want to combine your cert and private key into a single

big PEM file, you can keep them separate using the --pkey option to






Fossil.









    fossil server --port 443 --cert fullchain.pem --pkey privkey.pem /home/www/myproject.fossil




































## The ACME Protocol






The [ACME Protocol][2] is used to prove to a CA that you control a
website.  CAs require proof that you control a domain before they
will issue a cert for that domain.  The usual means of dealing
with ACME is to run the separate [certbot](https://certbot.eff.org) tool.

Here is, in a nutshell, what certbot will do to obtain your cert:

  1.  Certbot 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.  To do this (or, one common
      way of doing this, at least) the CA sends a secret token back to
      certbot through a secure backchannel, and instructs certbot to make
      that token accessible on the (unencrypted, ordinary "http:") web site
      for the domain in a particular file under the ".well-known" subdirectory.


  3.  Certbot puts the token where the CA requested it, then notifies the
      CA that it is there.


  4.  The CA accesses the token to confirm that you do indeed control the


      website.  It then creates the cert and sends it back to certbot.



  5.  Certbot stores your cert and deletes the ".well-known" token.



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, and
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 the --acme option is specified and 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, then you can concatenate that
cert with your private key and run Fossil in SSL/TLS mode as shown above.


[2]: https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment








|





|
<
<
<
<
|

|


|



<
<
<
|
|
|
>
|
|
|
>
>
|
|
>
>
>
|
>
>
>
|
>
>

|

>
|
>
>
>
>
>
>
>
>
|
<
<
<
>
>
>

>
|
>
>
|
<
|
>
>
>
>
|
>
>
>
>
>
>
>
>

|
>
>
>

<
|
>
>
>

<
<
<
>

|

>
|

|
|


|




















|


>
>
|

|







|
|

>
>
>
>
>
>
>
>
|
>

>
>
>
>
>
|
>
|
>
>
>
>
>
>
|
>
>
>
>

>
>
>
>
|
>
>
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>

<
<
<
|
>


|


|
|
|
<
<
|

>
|
|
>

|
>
>
|

>
>
|
>
>


|



>
|


















|
|
>


>
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 CAs 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.