Fossil

Check-in [c454059a18]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Update with all changes from trunk.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | clone-resume
Files: files | file ages | folders
SHA3-256: c454059a18bd2c3564b6ec7f42b317e5e392233fd002b19cec72f267c2b261b4
User & Date: andybradford 2024-02-03 16:55:40.013
Context
2024-02-06
14:19
Bring in fixes for SSH from trunk. check-in: c55d1b7b26 user: andybradford tags: clone-resume
2024-02-03
16:55
Update with all changes from trunk. check-in: c454059a18 user: andybradford tags: clone-resume
2024-02-02
22:22
Fix overlong lines and remove trailing whitespace from non-external C files. check-in: 275da70f8c user: danield tags: trunk
2023-12-17
05:49
When resuming a clone, delete the previous setup user so it can be reset. This also handles the case where a new --admin-user is passed in. check-in: b86d4da5a2 user: andybradford tags: clone-resume
Changes
Unified Diff Ignore Whitespace Patch
Changes to Dockerfile.
79
80
81
82
83
84
85
86
87
88
89
90

## ---------------------------------------------------------------------
## RUN!
## ---------------------------------------------------------------------

ENV PATH "/bin"
EXPOSE 8080/tcp
USER fossil
ENTRYPOINT [ "fossil", "server", "museum/repo.fossil" ]
CMD [                       \
    "--create",             \
    "--jsmode", "bundled",  \
    "--user",   "admin" ]








|



|
>
79
80
81
82
83
84
85
86
87
88
89
90
91
## ---------------------------------------------------------------------
## RUN!
## ---------------------------------------------------------------------

ENV PATH "/bin"
EXPOSE 8080/tcp
USER fossil
ENTRYPOINT [ "fossil", "server" ]
CMD [                       \
    "--create",             \
    "--jsmode", "bundled",  \
    "--user",   "admin",    \
    "museum/repo.fossil" ]
Changes to auto.def.
453
454
455
456
457
458
459


460
461
462
463
464
465
466
            }
        }
    }
    if {$found} {
        define FOSSIL_ENABLE_SSL
        define-append EXTRA_CFLAGS $cflags
        define-append EXTRA_LDFLAGS $ldflags


        if {[info exists ssllibs]} {
            define-append LIBS $ssllibs
        } else {
            define-append LIBS -lssl -lcrypto
        }
        if {[info exists ::zlib_lib]} {
            define-append LIBS $::zlib_lib







>
>







453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
            }
        }
    }
    if {$found} {
        define FOSSIL_ENABLE_SSL
        define-append EXTRA_CFLAGS $cflags
        define-append EXTRA_LDFLAGS $ldflags
        define-append CFLAGS $cflags
        define-append LDFLAGS $ldflags
        if {[info exists ssllibs]} {
            define-append LIBS $ssllibs
        } else {
            define-append LIBS -lssl -lcrypto
        }
        if {[info exists ::zlib_lib]} {
            define-append LIBS $::zlib_lib
653
654
655
656
657
658
659

660
661
662
663
664
665
666
    }
    set version $tclconfig(TCL_VERSION)$tclconfig(TCL_PATCH_LEVEL)
    msg-result "Found Tcl $version at $tclconfig(TCL_PREFIX)"
    if {!$tclprivatestubs} {
        define-append LIBS $libs
    }
    define-append EXTRA_CFLAGS $cflags

    if {[info exists zlibpath] && $zlibpath eq "tree"} {
      #
      # NOTE: When using zlib in the source tree, prevent Tcl from
      #       pulling in the system one.
      #
      set tclconfig(TCL_LD_FLAGS) [string map [list -lz ""] \
          $tclconfig(TCL_LD_FLAGS)]







>







655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
    }
    set version $tclconfig(TCL_VERSION)$tclconfig(TCL_PATCH_LEVEL)
    msg-result "Found Tcl $version at $tclconfig(TCL_PREFIX)"
    if {!$tclprivatestubs} {
        define-append LIBS $libs
    }
    define-append EXTRA_CFLAGS $cflags
    define-append CFLAGS $cflags
    if {[info exists zlibpath] && $zlibpath eq "tree"} {
      #
      # NOTE: When using zlib in the source tree, prevent Tcl from
      #       pulling in the system one.
      #
      set tclconfig(TCL_LD_FLAGS) [string map [list -lz ""] \
          $tclconfig(TCL_LD_FLAGS)]
Changes to extsrc/pikchr.c.
5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
  int iErrCol;          /* Column of the error token on its line */
  int iStart;           /* Start position of the error context */
  int iEnd;             /* End position of the error context */
  int iLineno;          /* Line number of the error */
  int iFirstLineno;     /* Line number of start of error context */
  int i;                /* Loop counter */
  int iBump = 0;        /* Bump the location of the error cursor */
  char zLineno[20];     /* Buffer in which to generate line numbers */

  iErrPt = (int)(pErr->z - p->sIn.z);
  if( iErrPt>=(int)p->sIn.n ){
    iErrPt = p->sIn.n-1;
    iBump = 1;
  }else{
    while( iErrPt>0 && (p->sIn.z[iErrPt]=='\n' || p->sIn.z[iErrPt]=='\r') ){







|







5106
5107
5108
5109
5110
5111
5112
5113
5114
5115
5116
5117
5118
5119
5120
  int iErrCol;          /* Column of the error token on its line */
  int iStart;           /* Start position of the error context */
  int iEnd;             /* End position of the error context */
  int iLineno;          /* Line number of the error */
  int iFirstLineno;     /* Line number of start of error context */
  int i;                /* Loop counter */
  int iBump = 0;        /* Bump the location of the error cursor */
  char zLineno[24];     /* Buffer in which to generate line numbers */

  iErrPt = (int)(pErr->z - p->sIn.z);
  if( iErrPt>=(int)p->sIn.n ){
    iErrPt = p->sIn.n-1;
    iBump = 1;
  }else{
    while( iErrPt>0 && (p->sIn.z[iErrPt]=='\n' || p->sIn.z[iErrPt]=='\r') ){
Changes to extsrc/shell.c.
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
** This code may change in tandem with other project code as needed.
**
** When this .h file and its companion .c are directly incorporated into
** a source conglomeration (such as shell.c), the preprocessor symbol
** CIO_WIN_WC_XLATE is defined as 0 or 1, reflecting whether console I/O
** translation for Windows is effected for the build.
*/

#ifndef SQLITE_INTERNAL_LINKAGE
# define SQLITE_INTERNAL_LINKAGE extern /* external to translation unit */
# include <stdio.h>
#else
# define SHELL_NO_SYSINC /* Better yet, modify mkshellc.tcl for this. */
#endif








|







292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
** This code may change in tandem with other project code as needed.
**
** When this .h file and its companion .c are directly incorporated into
** a source conglomeration (such as shell.c), the preprocessor symbol
** CIO_WIN_WC_XLATE is defined as 0 or 1, reflecting whether console I/O
** translation for Windows is effected for the build.
*/
#define HAVE_CONSOLE_IO_H 1
#ifndef SQLITE_INTERNAL_LINKAGE
# define SQLITE_INTERNAL_LINKAGE extern /* external to translation unit */
# include <stdio.h>
#else
# define SHELL_NO_SYSINC /* Better yet, modify mkshellc.tcl for this. */
#endif

430
431
432
433
434
435
436
437
438

439
440
441
442
443
444
445
** accepted char or character sequence is limited by nAccept.
**
** Returns the number of accepted char values.
*/
#ifdef CONSIO_SPUTB
SQLITE_INTERNAL_LINKAGE int
fPutbUtf8(FILE *pfOut, const char *cBuf, int nAccept);
#endif
/* Like fPutbUtf8 except stream is always the designated output. */

SQLITE_INTERNAL_LINKAGE int
oPutbUtf8(const char *cBuf, int nAccept);
/* Like fPutbUtf8 except stream is always the designated error. */
#ifdef CONSIO_EPUTB
SQLITE_INTERNAL_LINKAGE int
ePutbUtf8(const char *cBuf, int nAccept);
#endif







<

>







430
431
432
433
434
435
436

437
438
439
440
441
442
443
444
445
** accepted char or character sequence is limited by nAccept.
**
** Returns the number of accepted char values.
*/
#ifdef CONSIO_SPUTB
SQLITE_INTERNAL_LINKAGE int
fPutbUtf8(FILE *pfOut, const char *cBuf, int nAccept);

/* Like fPutbUtf8 except stream is always the designated output. */
#endif
SQLITE_INTERNAL_LINKAGE int
oPutbUtf8(const char *cBuf, int nAccept);
/* Like fPutbUtf8 except stream is always the designated error. */
#ifdef CONSIO_EPUTB
SQLITE_INTERNAL_LINKAGE int
ePutbUtf8(const char *cBuf, int nAccept);
#endif
571
572
573
574
575
576
577
578
579



580
581
582
583
584
585
586

#ifndef SHELL_NO_SYSINC
# include <stdarg.h>
# include <string.h>
# include <stdlib.h>
# include <limits.h>
# include <assert.h>
# include "console_io.h"
/* # include "sqlite3.h" */



#endif

#ifndef SQLITE_CIO_NO_TRANSLATE
# if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT
#  ifndef SHELL_NO_SYSINC
#   include <io.h>
#   include <fcntl.h>







<

>
>
>







571
572
573
574
575
576
577

578
579
580
581
582
583
584
585
586
587
588

#ifndef SHELL_NO_SYSINC
# include <stdarg.h>
# include <string.h>
# include <stdlib.h>
# include <limits.h>
# include <assert.h>

/* # include "sqlite3.h" */
#endif
#ifndef HAVE_CONSOLE_IO_H
# include "console_io.h"
#endif

#ifndef SQLITE_CIO_NO_TRANSLATE
# if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT
#  ifndef SHELL_NO_SYSINC
#   include <io.h>
#   include <fcntl.h>
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
    }
  }
  return z;
}
#endif /*!(defined(SQLITE_CIO_NO_UTF8SCAN)&&defined(SQLITE_CIO_NO_TRANSLATE))*/

#ifndef SQLITE_CIO_NO_TRANSLATE

#ifdef CONSIO_SPUTB
SQLITE_INTERNAL_LINKAGE int
fPutbUtf8(FILE *pfO, const char *cBuf, int nAccept){
  assert(pfO!=0);
# if CIO_WIN_WC_XLATE
  PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
  PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO);
  if( pstReachesConsole(ppst) ){
    int rv;
    maybeSetupAsConsole(ppst, 1);
    rv = conZstrEmit(ppst, cBuf, nAccept);
    if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst);
    return rv;
  }else {
# endif
    return (int)fwrite(cBuf, 1, nAccept, pfO);
# if CIO_WIN_WC_XLATE
  }
# endif
}
#endif /* defined(CONSIO_SPUTB) */

SQLITE_INTERNAL_LINKAGE int
oPutbUtf8(const char *cBuf, int nAccept){
  FILE *pfOut;
  PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
# if CIO_WIN_WC_XLATE
  PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut);







<
|



|









|

|

|

|







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
    }
  }
  return z;
}
#endif /*!(defined(SQLITE_CIO_NO_UTF8SCAN)&&defined(SQLITE_CIO_NO_TRANSLATE))*/

#ifndef SQLITE_CIO_NO_TRANSLATE

# ifdef CONSIO_SPUTB
SQLITE_INTERNAL_LINKAGE int
fPutbUtf8(FILE *pfO, const char *cBuf, int nAccept){
  assert(pfO!=0);
#  if CIO_WIN_WC_XLATE
  PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
  PerStreamTags *ppst = getEmitStreamInfo(0, &pst, &pfO);
  if( pstReachesConsole(ppst) ){
    int rv;
    maybeSetupAsConsole(ppst, 1);
    rv = conZstrEmit(ppst, cBuf, nAccept);
    if( 0 == isKnownWritable(ppst->pf) ) restoreConsoleArb(ppst);
    return rv;
  }else {
#  endif
    return (int)fwrite(cBuf, 1, nAccept, pfO);
#  if CIO_WIN_WC_XLATE
  }
#  endif
}
# endif

SQLITE_INTERNAL_LINKAGE int
oPutbUtf8(const char *cBuf, int nAccept){
  FILE *pfOut;
  PerStreamTags pst = PST_INITIALIZER; /* for unknown streams */
# if CIO_WIN_WC_XLATE
  PerStreamTags *ppst = getEmitStreamInfo(1, &pst, &pfOut);
1228
1229
1230
1231
1232
1233
1234

1235
1236
1237
1238
1239
1240
1241
#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */

#undef SHELL_INVALID_FILE_PTR

/************************* End ../ext/consio/console_io.c ********************/

#ifndef SQLITE_SHELL_FIDDLE

/* From here onward, fgets() is redirected to the console_io library. */
# define fgets(b,n,f) fGetsUtf8(b,n,f)
/*
 * Define macros for emitting output text in various ways:
 *  sputz(s, z)      => emit 0-terminated string z to given stream s
 *  sputf(s, f, ...) => emit varargs per format f to given stream s
 *  oputz(z)         => emit 0-terminated string z to default stream







>







1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
#endif /* !defined(SQLITE_CIO_NO_TRANSLATE) */

#undef SHELL_INVALID_FILE_PTR

/************************* End ../ext/consio/console_io.c ********************/

#ifndef SQLITE_SHELL_FIDDLE

/* From here onward, fgets() is redirected to the console_io library. */
# define fgets(b,n,f) fGetsUtf8(b,n,f)
/*
 * Define macros for emitting output text in various ways:
 *  sputz(s, z)      => emit 0-terminated string z to given stream s
 *  sputf(s, f, ...) => emit varargs per format f to given stream s
 *  oputz(z)         => emit 0-terminated string z to default stream
1252
1253
1254
1255
1256
1257
1258

1259
1260
1261
1262
1263
1264
1265
# define sputz(s,z) fPutsUtf8(z,s)
# define sputf fPrintfUtf8
# define oputz(z) oPutsUtf8(z)
# define oputf oPrintfUtf8
# define eputz(z) ePutsUtf8(z)
# define eputf ePrintfUtf8
# define oputb(buf,na) oPutbUtf8(buf,na)

#else
/* For Fiddle, all console handling and emit redirection is omitted. */
# define sputz(fp,z) fputs(z,fp)
# define sputf(fp,fmt, ...) fprintf(fp,fmt,__VA_ARGS__)
# define oputz(z) fputs(z,stdout)
# define oputf(fmt, ...) printf(fmt,__VA_ARGS__)
# define eputz(z) fputs(z,stderr)







>







1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
# define sputz(s,z) fPutsUtf8(z,s)
# define sputf fPrintfUtf8
# define oputz(z) oPutsUtf8(z)
# define oputf oPrintfUtf8
# define eputz(z) ePutsUtf8(z)
# define eputf ePrintfUtf8
# define oputb(buf,na) oPutbUtf8(buf,na)

#else
/* For Fiddle, all console handling and emit redirection is omitted. */
# define sputz(fp,z) fputs(z,fp)
# define sputf(fp,fmt, ...) fprintf(fp,fmt,__VA_ARGS__)
# define oputz(z) fputs(z,stdout)
# define oputf(fmt, ...) printf(fmt,__VA_ARGS__)
# define eputz(z) fputs(z,stderr)
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
** Print the timing results.
*/
static void endTimer(void){
  if( enableTimer ){
    sqlite3_int64 iEnd = timeOfDay();
    struct rusage sEnd;
    getrusage(RUSAGE_SELF, &sEnd);
    oputf("Run Time: real %.3f user %f sys %f\n",
          (iEnd - iBegin)*0.001,
          timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
          timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
  }
}

#define BEGIN_TIMER beginTimer()







|







1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
** Print the timing results.
*/
static void endTimer(void){
  if( enableTimer ){
    sqlite3_int64 iEnd = timeOfDay();
    struct rusage sEnd;
    getrusage(RUSAGE_SELF, &sEnd);
    sputf(stdout, "Run Time: real %.3f user %f sys %f\n",
          (iEnd - iBegin)*0.001,
          timeDiff(&sBegin.ru_utime, &sEnd.ru_utime),
          timeDiff(&sBegin.ru_stime, &sEnd.ru_stime));
  }
}

#define BEGIN_TIMER beginTimer()
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
** Print the timing results.
*/
static void endTimer(void){
  if( enableTimer && getProcessTimesAddr){
    FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd;
    sqlite3_int64 ftWallEnd = timeOfDay();
    getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd);
    oputf("Run Time: real %.3f user %f sys %f\n",
          (ftWallEnd - ftWallBegin)*0.001,
          timeDiff(&ftUserBegin, &ftUserEnd),
          timeDiff(&ftKernelBegin, &ftKernelEnd));
  }
}

#define BEGIN_TIMER beginTimer()







|







1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
** Print the timing results.
*/
static void endTimer(void){
  if( enableTimer && getProcessTimesAddr){
    FILETIME ftCreation, ftExit, ftKernelEnd, ftUserEnd;
    sqlite3_int64 ftWallEnd = timeOfDay();
    getProcessTimesAddr(hProcess,&ftCreation,&ftExit,&ftKernelEnd,&ftUserEnd);
    sputf(stdout, "Run Time: real %.3f user %f sys %f\n",
          (ftWallEnd - ftWallBegin)*0.001,
          timeDiff(&ftUserBegin, &ftUserEnd),
          timeDiff(&ftKernelBegin, &ftKernelEnd));
  }
}

#define BEGIN_TIMER beginTimer()
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
/*
** Return open FILE * if zFile exists, can be opened for read
** and is an ordinary file or a character stream source.
** Otherwise return 0.
*/
static FILE * openChrSource(const char *zFile){
#if defined(_WIN32) || defined(WIN32)
  struct _stat x = {0};
# define STAT_CHR_SRC(mode) ((mode & (_S_IFCHR|_S_IFIFO|_S_IFREG))!=0)
  /* On Windows, open first, then check the stream nature. This order
  ** is necessary because _stat() and sibs, when checking a named pipe,
  ** effectively break the pipe as its supplier sees it. */
  FILE *rv = fopen(zFile, "rb");
  if( rv==0 ) return 0;
  if( _fstat(_fileno(rv), &x) != 0
      || !STAT_CHR_SRC(x.st_mode)){
    fclose(rv);
    rv = 0;
  }
  return rv;
#else
  struct stat x = {0};







|






|







1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
/*
** Return open FILE * if zFile exists, can be opened for read
** and is an ordinary file or a character stream source.
** Otherwise return 0.
*/
static FILE * openChrSource(const char *zFile){
#if defined(_WIN32) || defined(WIN32)
  struct __stat64 x = {0};
# define STAT_CHR_SRC(mode) ((mode & (_S_IFCHR|_S_IFIFO|_S_IFREG))!=0)
  /* On Windows, open first, then check the stream nature. This order
  ** is necessary because _stat() and sibs, when checking a named pipe,
  ** effectively break the pipe as its supplier sees it. */
  FILE *rv = fopen(zFile, "rb");
  if( rv==0 ) return 0;
  if( _fstat64(_fileno(rv), &x) != 0
      || !STAT_CHR_SRC(x.st_mode)){
    fclose(rv);
    rv = 0;
  }
  return rv;
#else
  struct stat x = {0};
14823
14824
14825
14826
14827
14828
14829

14830
14831
14832
14833
14834
14835
14836
          iOff += nPointer;
    
          /* Load the "byte of payload including overflow" field */
          if( bNextPage || iOff>pCsr->nPage ){
            bNextPage = 1;
          }else{
            iOff += dbdataGetVarintU32(&pCsr->aPage[iOff], &nPayload);

          }
    
          /* If this is a leaf intkey cell, load the rowid */
          if( bHasRowid && !bNextPage && iOff<pCsr->nPage ){
            iOff += dbdataGetVarint(&pCsr->aPage[iOff], &pCsr->iIntkey);
          }
    







>







14826
14827
14828
14829
14830
14831
14832
14833
14834
14835
14836
14837
14838
14839
14840
          iOff += nPointer;
    
          /* Load the "byte of payload including overflow" field */
          if( bNextPage || iOff>pCsr->nPage ){
            bNextPage = 1;
          }else{
            iOff += dbdataGetVarintU32(&pCsr->aPage[iOff], &nPayload);
            if( nPayload>0x7fffff00 ) nPayload &= 0x3fff;
          }
    
          /* If this is a leaf intkey cell, load the rowid */
          if( bHasRowid && !bNextPage && iOff<pCsr->nPage ){
            iOff += dbdataGetVarint(&pCsr->aPage[iOff], &pCsr->iIntkey);
          }
    
18139
18140
18141
18142
18143
18144
18145

18146
18147
18148
18149
18150
18151
18152
  u8 scanstatsOn;        /* True to display scan stats before each finalize */
  u8 openMode;           /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */
  u8 doXdgOpen;          /* Invoke start/open/xdg-open in output_reset() */
  u8 nEqpLevel;          /* Depth of the EQP output graph */
  u8 eTraceType;         /* SHELL_TRACE_* value for type of trace */
  u8 bSafeMode;          /* True to prohibit unsafe operations */
  u8 bSafeModePersist;   /* The long-term value of bSafeMode */

  ColModeOpts cmOpts;    /* Option values affecting columnar mode output */
  unsigned statsOn;      /* True to display memory stats before each finalize */
  unsigned mEqpLines;    /* Mask of vertical lines in the EQP output graph */
  int inputNesting;      /* Track nesting level of .read and other redirects */
  int outCount;          /* Revert to stdout when reaching zero */
  int cnt;               /* Number of records displayed so far */
  int lineno;            /* Line number of last line read from in */







>







18143
18144
18145
18146
18147
18148
18149
18150
18151
18152
18153
18154
18155
18156
18157
  u8 scanstatsOn;        /* True to display scan stats before each finalize */
  u8 openMode;           /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */
  u8 doXdgOpen;          /* Invoke start/open/xdg-open in output_reset() */
  u8 nEqpLevel;          /* Depth of the EQP output graph */
  u8 eTraceType;         /* SHELL_TRACE_* value for type of trace */
  u8 bSafeMode;          /* True to prohibit unsafe operations */
  u8 bSafeModePersist;   /* The long-term value of bSafeMode */
  u8 eRestoreState;      /* See comments above doAutoDetectRestore() */
  ColModeOpts cmOpts;    /* Option values affecting columnar mode output */
  unsigned statsOn;      /* True to display memory stats before each finalize */
  unsigned mEqpLines;    /* Mask of vertical lines in the EQP output graph */
  int inputNesting;      /* Track nesting level of .read and other redirects */
  int outCount;          /* Revert to stdout when reaching zero */
  int cnt;               /* Number of records displayed so far */
  int lineno;            /* Line number of last line read from in */
22166
22167
22168
22169
22170
22171
22172
22173
22174
22175
22176
22177
22178
22179
22180
22181
22182
22183
22184
22185
22186
22187
22188
22189

22190
22191
22192
22193
22194
22195
22196
      case SHELL_OPEN_UNSPEC:
      case SHELL_OPEN_NORMAL: {
        sqlite3_open_v2(zDbFilename, &p->db,
           SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, 0);
        break;
      }
    }
    globalDb = p->db;
    if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
      eputf("Error: unable to open database \"%s\": %s\n",
            zDbFilename, sqlite3_errmsg(p->db));
      if( (openFlags & OPEN_DB_KEEPALIVE)==0 ){
        exit(1);
      }
      sqlite3_close(p->db);
      sqlite3_open(":memory:", &p->db);
      if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
        eputz("Also: unable to open substitute in-memory database.\n");
        exit(1);
      }else{
        eputf("Notice: using substitute in-memory database instead of \"%s\"\n",
              zDbFilename);
      }
    }

    sqlite3_db_config(p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, (int)0, (int*)0);

    /* Reflect the use or absence of --unsafe-testing invocation. */
    {
      int testmode_on = ShellHasFlag(p,SHFLG_TestingMode);
      sqlite3_db_config(p->db, SQLITE_DBCONFIG_TRUSTED_SCHEMA, testmode_on,0);
      sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, !testmode_on,0);







<
















>







22171
22172
22173
22174
22175
22176
22177

22178
22179
22180
22181
22182
22183
22184
22185
22186
22187
22188
22189
22190
22191
22192
22193
22194
22195
22196
22197
22198
22199
22200
22201
      case SHELL_OPEN_UNSPEC:
      case SHELL_OPEN_NORMAL: {
        sqlite3_open_v2(zDbFilename, &p->db,
           SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|p->openFlags, 0);
        break;
      }
    }

    if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
      eputf("Error: unable to open database \"%s\": %s\n",
            zDbFilename, sqlite3_errmsg(p->db));
      if( (openFlags & OPEN_DB_KEEPALIVE)==0 ){
        exit(1);
      }
      sqlite3_close(p->db);
      sqlite3_open(":memory:", &p->db);
      if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
        eputz("Also: unable to open substitute in-memory database.\n");
        exit(1);
      }else{
        eputf("Notice: using substitute in-memory database instead of \"%s\"\n",
              zDbFilename);
      }
    }
    globalDb = p->db;
    sqlite3_db_config(p->db, SQLITE_DBCONFIG_STMT_SCANSTATUS, (int)0, (int*)0);

    /* Reflect the use or absence of --unsafe-testing invocation. */
    {
      int testmode_on = ShellHasFlag(p,SHFLG_TestingMode);
      sqlite3_db_config(p->db, SQLITE_DBCONFIG_TRUSTED_SCHEMA, testmode_on,0);
      sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, !testmode_on,0);
23578
23579
23580
23581
23582
23583
23584
23585
23586
23587
23588
23589
23590
23591
23592
23593
23594
23595
23596
23597
23598
23599
23600
23601
23602
23603
23604
23605
23606
23607
23608
23609
23610
23611
23612
23613
23614
23615
23616
 usage:
  eputf("Usage %s sub-command ?switches...?\n", azArg[0]);
  eputz("Where sub-commands are:\n");
  eputz("    fkey-indexes\n");
  return SQLITE_ERROR;
}

#if !defined SQLITE_OMIT_VIRTUALTABLE
static void shellPrepare(
  sqlite3 *db,
  int *pRc,
  const char *zSql,
  sqlite3_stmt **ppStmt
){
  *ppStmt = 0;
  if( *pRc==SQLITE_OK ){
    int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0);
    if( rc!=SQLITE_OK ){
      eputf("sql error: %s (%d)\n", sqlite3_errmsg(db), sqlite3_errcode(db));
      *pRc = rc;
    }
  }
}

/*
** Create a prepared statement using printf-style arguments for the SQL.
**
** This routine is could be marked "static".  But it is not always used,
** depending on compile-time options.  By omitting the "static", we avoid
** nuisance compiler warnings about "defined but not used".
*/
void shellPreparePrintf(
  sqlite3 *db,
  int *pRc,
  sqlite3_stmt **ppStmt,
  const char *zFmt,
  ...
){
  *ppStmt = 0;







<


















<
<
<
<

|







23583
23584
23585
23586
23587
23588
23589

23590
23591
23592
23593
23594
23595
23596
23597
23598
23599
23600
23601
23602
23603
23604
23605
23606
23607




23608
23609
23610
23611
23612
23613
23614
23615
23616
 usage:
  eputf("Usage %s sub-command ?switches...?\n", azArg[0]);
  eputz("Where sub-commands are:\n");
  eputz("    fkey-indexes\n");
  return SQLITE_ERROR;
}


static void shellPrepare(
  sqlite3 *db,
  int *pRc,
  const char *zSql,
  sqlite3_stmt **ppStmt
){
  *ppStmt = 0;
  if( *pRc==SQLITE_OK ){
    int rc = sqlite3_prepare_v2(db, zSql, -1, ppStmt, 0);
    if( rc!=SQLITE_OK ){
      eputf("sql error: %s (%d)\n", sqlite3_errmsg(db), sqlite3_errcode(db));
      *pRc = rc;
    }
  }
}

/*
** Create a prepared statement using printf-style arguments for the SQL.




*/
static void shellPreparePrintf(
  sqlite3 *db,
  int *pRc,
  sqlite3_stmt **ppStmt,
  const char *zFmt,
  ...
){
  *ppStmt = 0;
23625
23626
23627
23628
23629
23630
23631

23632
23633
23634
23635
23636
23637
23638
23639
23640
23641
23642
23643
23644
23645
23646
23647
23648
23649
23650
23651
23652
23653

23654
23655
23656
23657
23658
23659
23660
    }else{
      shellPrepare(db, pRc, z, ppStmt);
      sqlite3_free(z);
    }
  }
}


/* Finalize the prepared statement created using shellPreparePrintf().
**
** This routine is could be marked "static".  But it is not always used,
** depending on compile-time options.  By omitting the "static", we avoid
** nuisance compiler warnings about "defined but not used".
*/
void shellFinalize(
  int *pRc,
  sqlite3_stmt *pStmt
){
  if( pStmt ){
    sqlite3 *db = sqlite3_db_handle(pStmt);
    int rc = sqlite3_finalize(pStmt);
    if( *pRc==SQLITE_OK ){
      if( rc!=SQLITE_OK ){
        eputf("SQL error: %s\n", sqlite3_errmsg(db));
      }
      *pRc = rc;
    }
  }
}


/* Reset the prepared statement created using shellPreparePrintf().
**
** This routine is could be marked "static".  But it is not always used,
** depending on compile-time options.  By omitting the "static", we avoid
** nuisance compiler warnings about "defined but not used".
*/
void shellReset(







>
|
<
<
<
<

|















>







23625
23626
23627
23628
23629
23630
23631
23632
23633




23634
23635
23636
23637
23638
23639
23640
23641
23642
23643
23644
23645
23646
23647
23648
23649
23650
23651
23652
23653
23654
23655
23656
23657
23658
    }else{
      shellPrepare(db, pRc, z, ppStmt);
      sqlite3_free(z);
    }
  }
}

/* 
** Finalize the prepared statement created using shellPreparePrintf().




*/
static void shellFinalize(
  int *pRc,
  sqlite3_stmt *pStmt
){
  if( pStmt ){
    sqlite3 *db = sqlite3_db_handle(pStmt);
    int rc = sqlite3_finalize(pStmt);
    if( *pRc==SQLITE_OK ){
      if( rc!=SQLITE_OK ){
        eputf("SQL error: %s\n", sqlite3_errmsg(db));
      }
      *pRc = rc;
    }
  }
}

#if !defined SQLITE_OMIT_VIRTUALTABLE
/* Reset the prepared statement created using shellPreparePrintf().
**
** This routine is could be marked "static".  But it is not always used,
** depending on compile-time options.  By omitting the "static", we avoid
** nuisance compiler warnings about "defined but not used".
*/
void shellReset(
24712
24713
24714
24715
24716
24717
24718
























24719
24720
24721
24722
24723
24724
24725
    }
    sqlite3_finalize(pStmt);
    sqlite3_close(*pDb);
    *pDb = 0;
    return zColsSpec;
  }
}

























/*
** If an input line begins with "." then invoke this routine to
** process that line.
**
** Return 1 on error, 2 to exit, and 0 otherwise.
*/







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







24710
24711
24712
24713
24714
24715
24716
24717
24718
24719
24720
24721
24722
24723
24724
24725
24726
24727
24728
24729
24730
24731
24732
24733
24734
24735
24736
24737
24738
24739
24740
24741
24742
24743
24744
24745
24746
24747
    }
    sqlite3_finalize(pStmt);
    sqlite3_close(*pDb);
    *pDb = 0;
    return zColsSpec;
  }
}

/*
** Check if the sqlite_schema table contains one or more virtual tables. If
** parameter zLike is not NULL, then it is an SQL expression that the
** sqlite_schema row must also match. If one or more such rows are found,
** print the following warning to the output:
**
** WARNING: Script requires that SQLITE_DBCONFIG_DEFENSIVE be disabled
*/
static int outputDumpWarning(ShellState *p, const char *zLike){
  int rc = SQLITE_OK;
  sqlite3_stmt *pStmt = 0;
  shellPreparePrintf(p->db, &rc, &pStmt,
    "SELECT 1 FROM sqlite_schema o WHERE "
    "sql LIKE 'CREATE VIRTUAL TABLE%%' AND %s", zLike ? zLike : "true"
  );
  if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
    oputz("/* WARNING: "
          "Script requires that SQLITE_DBCONFIG_DEFENSIVE be disabled */\n"
    );
  }
  shellFinalize(&rc, pStmt);
  return rc;
}

/*
** If an input line begins with "." then invoke this routine to
** process that line.
**
** Return 1 on error, 2 to exit, and 0 otherwise.
*/
25175
25176
25177
25178
25179
25180
25181

25182
25183
25184
25185
25186
25187
25188
          zLike = zExpr;
        }
      }
    }

    open_db(p, 0);


    if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){
      /* When playing back a "dump", the content might appear in an order
      ** which causes immediate foreign key constraints to be violated.
      ** So disable foreign-key constraint enforcement to prevent problems. */
      oputz("PRAGMA foreign_keys=OFF;\n");
      oputz("BEGIN TRANSACTION;\n");
    }







>







25197
25198
25199
25200
25201
25202
25203
25204
25205
25206
25207
25208
25209
25210
25211
          zLike = zExpr;
        }
      }
    }

    open_db(p, 0);

    outputDumpWarning(p, zLike);
    if( (p->shellFlgs & SHFLG_DumpDataOnly)==0 ){
      /* When playing back a "dump", the content might appear in an order
      ** which causes immediate foreign key constraints to be violated.
      ** So disable foreign-key constraint enforcement to prevent problems. */
      oputz("PRAGMA foreign_keys=OFF;\n");
      oputz("BEGIN TRANSACTION;\n");
    }
27603
27604
27605
27606
27607
27608
27609

27610
27611
27612
27613
27614
27615
27616
  /*{"bitvec_test",        SQLITE_TESTCTRL_BITVEC_TEST, 1,  ""              },*/
    {"byteorder",          SQLITE_TESTCTRL_BYTEORDER, 0,  ""                },
    {"extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,0,"BOOLEAN"  },
  /*{"fault_install",      SQLITE_TESTCTRL_FAULT_INSTALL, 1,""              },*/
    {"fk_no_action",       SQLITE_TESTCTRL_FK_NO_ACTION, 0, "BOOLEAN"       },
    {"imposter",         SQLITE_TESTCTRL_IMPOSTER,1,"SCHEMA ON/OFF ROOTPAGE"},
    {"internal_functions", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS,0,""          },

    {"localtime_fault",    SQLITE_TESTCTRL_LOCALTIME_FAULT,0,"BOOLEAN"      },
    {"never_corrupt",      SQLITE_TESTCTRL_NEVER_CORRUPT,1, "BOOLEAN"       },
    {"optimizations",      SQLITE_TESTCTRL_OPTIMIZATIONS,0,"DISABLE-MASK"   },
#ifdef YYCOVERAGE
    {"parser_coverage",    SQLITE_TESTCTRL_PARSER_COVERAGE,0,""             },
#endif
    {"pending_byte",       SQLITE_TESTCTRL_PENDING_BYTE,0, "OFFSET  "       },







>







27626
27627
27628
27629
27630
27631
27632
27633
27634
27635
27636
27637
27638
27639
27640
  /*{"bitvec_test",        SQLITE_TESTCTRL_BITVEC_TEST, 1,  ""              },*/
    {"byteorder",          SQLITE_TESTCTRL_BYTEORDER, 0,  ""                },
    {"extra_schema_checks",SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS,0,"BOOLEAN"  },
  /*{"fault_install",      SQLITE_TESTCTRL_FAULT_INSTALL, 1,""              },*/
    {"fk_no_action",       SQLITE_TESTCTRL_FK_NO_ACTION, 0, "BOOLEAN"       },
    {"imposter",         SQLITE_TESTCTRL_IMPOSTER,1,"SCHEMA ON/OFF ROOTPAGE"},
    {"internal_functions", SQLITE_TESTCTRL_INTERNAL_FUNCTIONS,0,""          },
    {"json_selfcheck",     SQLITE_TESTCTRL_JSON_SELFCHECK ,0,"BOOLEAN"      },
    {"localtime_fault",    SQLITE_TESTCTRL_LOCALTIME_FAULT,0,"BOOLEAN"      },
    {"never_corrupt",      SQLITE_TESTCTRL_NEVER_CORRUPT,1, "BOOLEAN"       },
    {"optimizations",      SQLITE_TESTCTRL_OPTIMIZATIONS,0,"DISABLE-MASK"   },
#ifdef YYCOVERAGE
    {"parser_coverage",    SQLITE_TESTCTRL_PARSER_COVERAGE,0,""             },
#endif
    {"pending_byte",       SQLITE_TESTCTRL_PENDING_BYTE,0, "OFFSET  "       },
27821
27822
27823
27824
27825
27826
27827










27828
27829
27830
27831
27832
27833
27834
        case SQLITE_TESTCTRL_SORTER_MMAP:
          if( nArg==3 ){
            int opt = (unsigned int)integerValue(azArg[2]);
            rc2 = sqlite3_test_control(testctrl, p->db, opt);
            isOk = 3;
          }
          break;










      }
    }
    if( isOk==0 && iCtrl>=0 ){
      oputf("Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage);
      rc = 1;
    }else if( isOk==1 ){
      oputf("%d\n", rc2);







>
>
>
>
>
>
>
>
>
>







27845
27846
27847
27848
27849
27850
27851
27852
27853
27854
27855
27856
27857
27858
27859
27860
27861
27862
27863
27864
27865
27866
27867
27868
        case SQLITE_TESTCTRL_SORTER_MMAP:
          if( nArg==3 ){
            int opt = (unsigned int)integerValue(azArg[2]);
            rc2 = sqlite3_test_control(testctrl, p->db, opt);
            isOk = 3;
          }
          break;
        case SQLITE_TESTCTRL_JSON_SELFCHECK:
          if( nArg==2 ){
            rc2 = -1;
            isOk = 1;
          }else{
            rc2 = booleanValue(azArg[2]);
            isOk = 3;
          }
          sqlite3_test_control(testctrl, &rc2);
          break;
      }
    }
    if( isOk==0 && iCtrl>=0 ){
      oputf("Usage: .testctrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage);
      rc = 1;
    }else if( isOk==1 ){
      oputf("%d\n", rc2);
28226
28227
28228
28229
28230
28231
28232


















































































28233
28234
28235
28236
28237
28238
28239
  if( zSql==0 ) return 1;
  zSql[nSql] = ';';
  zSql[nSql+1] = 0;
  rc = sqlite3_complete(zSql);
  zSql[nSql] = 0;
  return rc;
}



















































































/*
** Run a single line of SQL.  Return the number of errors.
*/
static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){
  int rc;
  char *zErrMsg = 0;







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







28260
28261
28262
28263
28264
28265
28266
28267
28268
28269
28270
28271
28272
28273
28274
28275
28276
28277
28278
28279
28280
28281
28282
28283
28284
28285
28286
28287
28288
28289
28290
28291
28292
28293
28294
28295
28296
28297
28298
28299
28300
28301
28302
28303
28304
28305
28306
28307
28308
28309
28310
28311
28312
28313
28314
28315
28316
28317
28318
28319
28320
28321
28322
28323
28324
28325
28326
28327
28328
28329
28330
28331
28332
28333
28334
28335
28336
28337
28338
28339
28340
28341
28342
28343
28344
28345
28346
28347
28348
28349
28350
28351
28352
28353
28354
28355
  if( zSql==0 ) return 1;
  zSql[nSql] = ';';
  zSql[nSql+1] = 0;
  rc = sqlite3_complete(zSql);
  zSql[nSql] = 0;
  return rc;
}

/*
** This function is called after processing each line of SQL in the
** runOneSqlLine() function. Its purpose is to detect scenarios where
** defensive mode should be automatically turned off. Specifically, when
**
**   1. The first line of input is "PRAGMA foreign_keys=OFF;",
**   2. The second line of input is "BEGIN TRANSACTION;",
**   3. The database is empty, and
**   4. The shell is not running in --safe mode.
** 
** The implementation uses the ShellState.eRestoreState to maintain state:
**
**    0: Have not seen any SQL.
**    1: Have seen "PRAGMA foreign_keys=OFF;".
**    2-6: Currently running .dump transaction. If the "2" bit is set,
**         disable DEFENSIVE when done. If "4" is set, disable DQS_DDL.
**    7: Nothing left to do. This function becomes a no-op.
*/
static int doAutoDetectRestore(ShellState *p, const char *zSql){
  int rc = SQLITE_OK;

  if( p->eRestoreState<7 ){
    switch( p->eRestoreState ){
      case 0: {
        const char *zExpect = "PRAGMA foreign_keys=OFF;";
        assert( strlen(zExpect)==24 );
        if( p->bSafeMode==0 && memcmp(zSql, zExpect, 25)==0 ){
          p->eRestoreState = 1;
        }else{
          p->eRestoreState = 7;
        }
        break;
      };
  
      case 1: {
        int bIsDump = 0;
        const char *zExpect = "BEGIN TRANSACTION;";
        assert( strlen(zExpect)==18 );
        if( memcmp(zSql, zExpect, 19)==0 ){
          /* Now check if the database is empty. */
          const char *zQuery = "SELECT 1 FROM sqlite_schema LIMIT 1";
          sqlite3_stmt *pStmt = 0;
  
          bIsDump = 1;
          shellPrepare(p->db, &rc, zQuery, &pStmt);
          if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
            bIsDump = 0;
          }
          shellFinalize(&rc, pStmt);
        }
        if( bIsDump && rc==SQLITE_OK ){
          int bDefense = 0;
          int bDqsDdl = 0;
          sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, -1, &bDefense);
          sqlite3_db_config(p->db, SQLITE_DBCONFIG_DQS_DDL, -1, &bDqsDdl);
          sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, 0, 0);
          sqlite3_db_config(p->db, SQLITE_DBCONFIG_DQS_DDL, 1, 0);
          p->eRestoreState = (bDefense ? 2 : 0) + (bDqsDdl ? 4 : 0);
        }else{
          p->eRestoreState = 7;
        }
        break;
      }
  
      default: {
        if( sqlite3_get_autocommit(p->db) ){
          if( (p->eRestoreState & 2) ){
            sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, 1, 0);
          }
          if( (p->eRestoreState & 4) ){
            sqlite3_db_config(p->db, SQLITE_DBCONFIG_DQS_DDL, 0, 0);
          }
          p->eRestoreState = 7;
        }
        break;
      }
    }
  }

  return rc;
}

/*
** Run a single line of SQL.  Return the number of errors.
*/
static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){
  int rc;
  char *zErrMsg = 0;
28274
28275
28276
28277
28278
28279
28280


28281
28282
28283
28284
28285
28286
28287
  }else if( ShellHasFlag(p, SHFLG_CountChanges) ){
    char zLineBuf[2000];
    sqlite3_snprintf(sizeof(zLineBuf), zLineBuf,
            "changes: %lld   total_changes: %lld",
            sqlite3_changes64(p->db), sqlite3_total_changes64(p->db));
    oputf("%s\n", zLineBuf);
  }


  return 0;
}

static void echo_group_input(ShellState *p, const char *zDo){
  if( ShellHasFlag(p, SHFLG_Echo) ) oputf("%s\n", zDo);
}








>
>







28390
28391
28392
28393
28394
28395
28396
28397
28398
28399
28400
28401
28402
28403
28404
28405
  }else if( ShellHasFlag(p, SHFLG_CountChanges) ){
    char zLineBuf[2000];
    sqlite3_snprintf(sizeof(zLineBuf), zLineBuf,
            "changes: %lld   total_changes: %lld",
            sqlite3_changes64(p->db), sqlite3_total_changes64(p->db));
    oputf("%s\n", zLineBuf);
  }

  if( doAutoDetectRestore(p, zSql) ) return 1;
  return 0;
}

static void echo_group_input(ShellState *p, const char *zDo){
  if( ShellHasFlag(p, SHFLG_Echo) ) oputf("%s\n", zDo);
}

28707
28708
28709
28710
28711
28712
28713
28714
28715
28716
28717
28718
28719
28720
28721
28722
28723
28724
28725
28726
28727
28728
  HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
  CONSOLE_SCREEN_BUFFER_INFO defaultScreenInfo;
  GetConsoleScreenBufferInfo(out, &defaultScreenInfo);
  SetConsoleTextAttribute(out,
         FOREGROUND_RED|FOREGROUND_INTENSITY
  );
#endif
  oputz(zText);
#if !SQLITE_OS_WINRT
  SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes);
#endif
}
#else
static void printBold(const char *zText){
  oputf("\033[1m%s\033[0m", zText);
}
#endif

/*
** Get the argument to an --option.  Throw an error and die if no argument
** is available.
*/







|






|







28825
28826
28827
28828
28829
28830
28831
28832
28833
28834
28835
28836
28837
28838
28839
28840
28841
28842
28843
28844
28845
28846
  HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
  CONSOLE_SCREEN_BUFFER_INFO defaultScreenInfo;
  GetConsoleScreenBufferInfo(out, &defaultScreenInfo);
  SetConsoleTextAttribute(out,
         FOREGROUND_RED|FOREGROUND_INTENSITY
  );
#endif
  sputz(stdout, zText);
#if !SQLITE_OS_WINRT
  SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes);
#endif
}
#else
static void printBold(const char *zText){
  sputf(stdout, "\033[1m%s\033[0m", zText);
}
#endif

/*
** Get the argument to an --option.  Throw an error and die if no argument
** is available.
*/
28908
28909
28910
28911
28912
28913
28914
28915
28916
28917
28918
28919
28920
28921
28922
28923
28924
28925
     || cli_strcmp(z,"-newline")==0
     || cli_strcmp(z,"-cmd")==0
    ){
      (void)cmdline_option_value(argc, argv, ++i);
    }else if( cli_strcmp(z,"-init")==0 ){
      zInitFile = cmdline_option_value(argc, argv, ++i);
    }else if( cli_strcmp(z,"-interactive")==0 ){
      /* Need to check for interactive override here to so that it can
      ** affect console setup (for Windows only) and testing thereof.
      */
      stdin_is_interactive = 1;
    }else if( cli_strcmp(z,"-batch")==0 ){
      /* Need to check for batch mode here to so we can avoid printing
      ** informational messages (like from process_sqliterc) before
      ** we do the actual processing of arguments later in a second pass.
      */
      stdin_is_interactive = 0;
    }else if( cli_strcmp(z,"-utf8")==0 ){







<
<
<
<







29026
29027
29028
29029
29030
29031
29032




29033
29034
29035
29036
29037
29038
29039
     || cli_strcmp(z,"-newline")==0
     || cli_strcmp(z,"-cmd")==0
    ){
      (void)cmdline_option_value(argc, argv, ++i);
    }else if( cli_strcmp(z,"-init")==0 ){
      zInitFile = cmdline_option_value(argc, argv, ++i);
    }else if( cli_strcmp(z,"-interactive")==0 ){




    }else if( cli_strcmp(z,"-batch")==0 ){
      /* Need to check for batch mode here to so we can avoid printing
      ** informational messages (like from process_sqliterc) before
      ** we do the actual processing of arguments later in a second pass.
      */
      stdin_is_interactive = 0;
    }else if( cli_strcmp(z,"-utf8")==0 ){
29181
29182
29183
29184
29185
29186
29187
29188
29189
29190
29191
29192



29193
29194
29195
29196
29197
29198
29199
      ** prior to sending the SQL into SQLite.  Useful for injecting
      ** crazy bytes in the middle of SQL statements for testing and debugging.
      */
      ShellSetFlag(&data, SHFLG_Backslash);
    }else if( cli_strcmp(z,"-bail")==0 ){
      /* No-op.  The bail_on_error flag should already be set. */
    }else if( cli_strcmp(z,"-version")==0 ){
      oputf("%s %s (%d-bit)\n", sqlite3_libversion(), sqlite3_sourceid(),
            8*(int)sizeof(char*));
      return 0;
    }else if( cli_strcmp(z,"-interactive")==0 ){
      /* already handled */



    }else if( cli_strcmp(z,"-batch")==0 ){
      /* already handled */
    }else if( cli_strcmp(z,"-utf8")==0 ){
      /* already handled */
    }else if( cli_strcmp(z,"-no-utf8")==0 ){
      /* already handled */
    }else if( cli_strcmp(z,"-heap")==0 ){







|
|


|
>
>
>







29295
29296
29297
29298
29299
29300
29301
29302
29303
29304
29305
29306
29307
29308
29309
29310
29311
29312
29313
29314
29315
29316
      ** prior to sending the SQL into SQLite.  Useful for injecting
      ** crazy bytes in the middle of SQL statements for testing and debugging.
      */
      ShellSetFlag(&data, SHFLG_Backslash);
    }else if( cli_strcmp(z,"-bail")==0 ){
      /* No-op.  The bail_on_error flag should already be set. */
    }else if( cli_strcmp(z,"-version")==0 ){
      sputf(stdout, "%s %s (%d-bit)\n",
            sqlite3_libversion(), sqlite3_sourceid(), 8*(int)sizeof(char*));
      return 0;
    }else if( cli_strcmp(z,"-interactive")==0 ){
      /* Need to check for interactive override here to so that it can
      ** affect console setup (for Windows only) and testing thereof.
      */
      stdin_is_interactive = 1;
    }else if( cli_strcmp(z,"-batch")==0 ){
      /* already handled */
    }else if( cli_strcmp(z,"-utf8")==0 ){
      /* already handled */
    }else if( cli_strcmp(z,"-no-utf8")==0 ){
      /* already handled */
    }else if( cli_strcmp(z,"-heap")==0 ){
29314
29315
29316
29317
29318
29319
29320
29321
29322
29323
29324
29325
29326
29327
29328
29329
29330
29331
29332
29333
29334
      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
      oputf("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 ){
        oputz("Connected to a ");
        printBold("transient in-memory database");
        oputz(".\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;







|



|

|







29431
29432
29433
29434
29435
29436
29437
29438
29439
29440
29441
29442
29443
29444
29445
29446
29447
29448
29449
29450
29451
      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
      sputf(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;
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
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
** version 3.44.2.  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
** ebead0e7230cd33bcec9f95d2183069565b9.
*/
#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
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
** version 3.45.1.  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
** e876e51a0ed5c5b3126f52e532044363a014.
*/
#define SQLITE_CORE 1
#define SQLITE_AMALGAMATION 1
#ifndef SQLITE_PRIVATE
# define SQLITE_PRIVATE static
#endif
/************** Begin file sqliteInt.h ***************************************/
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
** 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.44.2"
#define SQLITE_VERSION_NUMBER 3044002
#define SQLITE_SOURCE_ID      "2023-11-24 11:41:44 ebead0e7230cd33bcec9f95d2183069565b9e709bf745c9b5db65cc0cbf92c0f"

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







|
|
|







455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
** 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.45.1"
#define SQLITE_VERSION_NUMBER 3045001
#define SQLITE_SOURCE_ID      "2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a"

/*
** 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
4263
4264
4265
4266
4267
4268
4269
4270

4271
4272
4273
4274
4275
4276
4277
4278

4279
4280
4281
4282
4283
4284
4285
** <li> sqlite3_extended_errcode()
** <li> sqlite3_errmsg()
** <li> sqlite3_errmsg16()
** <li> sqlite3_error_offset()
** </ul>
**
** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
** text that describes the error, as either UTF-8 or UTF-16 respectively.

** (See how SQLite handles [invalid UTF] for exceptions to this rule.)
** ^(Memory to hold the error message string is managed internally.
** The application does not need to worry about freeing the result.
** However, the error string might be overwritten or deallocated by
** subsequent calls to other SQLite interface functions.)^
**
** ^The sqlite3_errstr() interface returns the English-language text
** that describes the [result code], as UTF-8.

** ^(Memory to hold the error message string is managed internally
** and must not be freed by the application)^.
**
** ^If the most recent error references a specific token in the input
** SQL, the sqlite3_error_offset() interface returns the byte offset
** of the start of that token.  ^The byte offset returned by
** sqlite3_error_offset() assumes that the input SQL is UTF8.







|
>






|
|
>







4263
4264
4265
4266
4267
4268
4269
4270
4271
4272
4273
4274
4275
4276
4277
4278
4279
4280
4281
4282
4283
4284
4285
4286
4287
** <li> sqlite3_extended_errcode()
** <li> sqlite3_errmsg()
** <li> sqlite3_errmsg16()
** <li> sqlite3_error_offset()
** </ul>
**
** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
** text that describes the error, as either UTF-8 or UTF-16 respectively,
** or NULL if no error message is available.
** (See how SQLite handles [invalid UTF] for exceptions to this rule.)
** ^(Memory to hold the error message string is managed internally.
** The application does not need to worry about freeing the result.
** However, the error string might be overwritten or deallocated by
** subsequent calls to other SQLite interface functions.)^
**
** ^The sqlite3_errstr(E) interface returns the English-language text
** that describes the [result code] E, as UTF-8, or NULL if E is not an
** result code for which a text error message is available.
** ^(Memory to hold the error message string is managed internally
** and must not be freed by the application)^.
**
** ^If the most recent error references a specific token in the input
** SQL, the sqlite3_error_offset() interface returns the byte offset
** of the start of that token.  ^The byte offset returned by
** sqlite3_error_offset() assumes that the input SQL is UTF8.
8346
8347
8348
8349
8350
8351
8352
8353
8354
8355


8356
8357
8358
8359
8360
8361
8362
** In such cases, the
** mutex must be exited an equal number of times before another thread
** can enter.)^  If the same thread tries to enter any mutex other
** than an SQLITE_MUTEX_RECURSIVE more than once, the behavior is undefined.
**
** ^(Some systems (for example, Windows 95) do not support the operation
** implemented by sqlite3_mutex_try().  On those systems, sqlite3_mutex_try()
** will always return SQLITE_BUSY. The SQLite core only ever uses
** sqlite3_mutex_try() as an optimization so this is acceptable
** behavior.)^


**
** ^The sqlite3_mutex_leave() routine exits a mutex that was
** previously entered by the same thread.   The behavior
** is undefined if the mutex is not currently entered by the
** calling thread or is not currently allocated.
**
** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(),







|
|
|
>
>







8348
8349
8350
8351
8352
8353
8354
8355
8356
8357
8358
8359
8360
8361
8362
8363
8364
8365
8366
** In such cases, the
** mutex must be exited an equal number of times before another thread
** can enter.)^  If the same thread tries to enter any mutex other
** than an SQLITE_MUTEX_RECURSIVE more than once, the behavior is undefined.
**
** ^(Some systems (for example, Windows 95) do not support the operation
** implemented by sqlite3_mutex_try().  On those systems, sqlite3_mutex_try()
** will always return SQLITE_BUSY. In most cases the SQLite core only uses
** sqlite3_mutex_try() as an optimization, so this is acceptable
** behavior. The exceptions are unix builds that set the
** SQLITE_ENABLE_SETLK_TIMEOUT build option. In that case a working
** sqlite3_mutex_try() is required.)^
**
** ^The sqlite3_mutex_leave() routine exits a mutex that was
** previously entered by the same thread.   The behavior
** is undefined if the mutex is not currently entered by the
** calling thread or is not currently allocated.
**
** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(),
8607
8608
8609
8610
8611
8612
8613

8614
8615
8616
8617
8618
8619
8620
#define SQLITE_TESTCTRL_BITVEC_TEST              8
#define SQLITE_TESTCTRL_FAULT_INSTALL            9
#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS     10
#define SQLITE_TESTCTRL_PENDING_BYTE            11
#define SQLITE_TESTCTRL_ASSERT                  12
#define SQLITE_TESTCTRL_ALWAYS                  13
#define SQLITE_TESTCTRL_RESERVE                 14  /* NOT USED */

#define SQLITE_TESTCTRL_OPTIMIZATIONS           15
#define SQLITE_TESTCTRL_ISKEYWORD               16  /* NOT USED */
#define SQLITE_TESTCTRL_SCRATCHMALLOC           17  /* NOT USED */
#define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS      17
#define SQLITE_TESTCTRL_LOCALTIME_FAULT         18
#define SQLITE_TESTCTRL_EXPLAIN_STMT            19  /* NOT USED */
#define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD    19







>







8611
8612
8613
8614
8615
8616
8617
8618
8619
8620
8621
8622
8623
8624
8625
#define SQLITE_TESTCTRL_BITVEC_TEST              8
#define SQLITE_TESTCTRL_FAULT_INSTALL            9
#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS     10
#define SQLITE_TESTCTRL_PENDING_BYTE            11
#define SQLITE_TESTCTRL_ASSERT                  12
#define SQLITE_TESTCTRL_ALWAYS                  13
#define SQLITE_TESTCTRL_RESERVE                 14  /* NOT USED */
#define SQLITE_TESTCTRL_JSON_SELFCHECK          14
#define SQLITE_TESTCTRL_OPTIMIZATIONS           15
#define SQLITE_TESTCTRL_ISKEYWORD               16  /* NOT USED */
#define SQLITE_TESTCTRL_SCRATCHMALLOC           17  /* NOT USED */
#define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS      17
#define SQLITE_TESTCTRL_LOCALTIME_FAULT         18
#define SQLITE_TESTCTRL_EXPLAIN_STMT            19  /* NOT USED */
#define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD    19
13120
13121
13122
13123
13124
13125
13126



13127
13128
13129
13130
13131
13132
13133
13134
13135
13136
13137


13138
13139
13140
13141
13142
13143
13144
13145
13146
13147
13148
13149
13150
13151
13152
13153
13154
13155

13156
13157
13158
13159
13160
13161
13162
13163
13164
13165
13166
13167
**   an OOM condition or IO error), an appropriate SQLite error code is
**   returned.
**
**   This function may be quite inefficient if used with an FTS5 table
**   created with the "columnsize=0" option.
**
** xColumnText:



**   This function attempts to retrieve the text of column iCol of the
**   current document. If successful, (*pz) is set to point to a buffer
**   containing the text in utf-8 encoding, (*pn) is set to the size in bytes
**   (not characters) of the buffer and SQLITE_OK is returned. Otherwise,
**   if an error occurs, an SQLite error code is returned and the final values
**   of (*pz) and (*pn) are undefined.
**
** xPhraseCount:
**   Returns the number of phrases in the current query expression.
**
** xPhraseSize:


**   Returns the number of tokens in phrase iPhrase of the query. Phrases
**   are numbered starting from zero.
**
** xInstCount:
**   Set *pnInst to the total number of occurrences of all phrases within
**   the query within the current row. Return SQLITE_OK if successful, or
**   an error code (i.e. SQLITE_NOMEM) if an error occurs.
**
**   This API can be quite slow if used with an FTS5 table created with the
**   "detail=none" or "detail=column" option. If the FTS5 table is created
**   with either "detail=none" or "detail=column" and "content=" option
**   (i.e. if it is a contentless table), then this API always returns 0.
**
** xInst:
**   Query for the details of phrase match iIdx within the current row.
**   Phrase matches are numbered starting from zero, so the iIdx argument
**   should be greater than or equal to zero and smaller than the value
**   output by xInstCount().

**
**   Usually, output parameter *piPhrase is set to the phrase number, *piCol
**   to the column in which it occurs and *piOff the token offset of the
**   first token of the phrase. Returns SQLITE_OK if successful, or an error
**   code (i.e. SQLITE_NOMEM) if an error occurs.
**
**   This API can be quite slow if used with an FTS5 table created with the
**   "detail=none" or "detail=column" option.
**
** xRowid:
**   Returns the rowid of the current row.
**







>
>
>
|
|









>
>
|
|















|
>

|

|
|







13125
13126
13127
13128
13129
13130
13131
13132
13133
13134
13135
13136
13137
13138
13139
13140
13141
13142
13143
13144
13145
13146
13147
13148
13149
13150
13151
13152
13153
13154
13155
13156
13157
13158
13159
13160
13161
13162
13163
13164
13165
13166
13167
13168
13169
13170
13171
13172
13173
13174
13175
13176
13177
13178
**   an OOM condition or IO error), an appropriate SQLite error code is
**   returned.
**
**   This function may be quite inefficient if used with an FTS5 table
**   created with the "columnsize=0" option.
**
** xColumnText:
**   If parameter iCol is less than zero, or greater than or equal to the
**   number of columns in the table, SQLITE_RANGE is returned.
**
**   Otherwise, this function attempts to retrieve the text of column iCol of
**   the current document. If successful, (*pz) is set to point to a buffer
**   containing the text in utf-8 encoding, (*pn) is set to the size in bytes
**   (not characters) of the buffer and SQLITE_OK is returned. Otherwise,
**   if an error occurs, an SQLite error code is returned and the final values
**   of (*pz) and (*pn) are undefined.
**
** xPhraseCount:
**   Returns the number of phrases in the current query expression.
**
** xPhraseSize:
**   If parameter iCol is less than zero, or greater than or equal to the
**   number of phrases in the current query, as returned by xPhraseCount,
**   0 is returned. Otherwise, this function returns the number of tokens in
**   phrase iPhrase of the query. Phrases are numbered starting from zero.
**
** xInstCount:
**   Set *pnInst to the total number of occurrences of all phrases within
**   the query within the current row. Return SQLITE_OK if successful, or
**   an error code (i.e. SQLITE_NOMEM) if an error occurs.
**
**   This API can be quite slow if used with an FTS5 table created with the
**   "detail=none" or "detail=column" option. If the FTS5 table is created
**   with either "detail=none" or "detail=column" and "content=" option
**   (i.e. if it is a contentless table), then this API always returns 0.
**
** xInst:
**   Query for the details of phrase match iIdx within the current row.
**   Phrase matches are numbered starting from zero, so the iIdx argument
**   should be greater than or equal to zero and smaller than the value
**   output by xInstCount(). If iIdx is less than zero or greater than
**   or equal to the value returned by xInstCount(), SQLITE_RANGE is returned.
**
**   Otherwise, output parameter *piPhrase is set to the phrase number, *piCol
**   to the column in which it occurs and *piOff the token offset of the
**   first token of the phrase. SQLITE_OK is returned if successful, or an
**   error code (i.e. SQLITE_NOMEM) if an error occurs.
**
**   This API can be quite slow if used with an FTS5 table created with the
**   "detail=none" or "detail=column" option.
**
** xRowid:
**   Returns the rowid of the current row.
**
13178
13179
13180
13181
13182
13183
13184




13185
13186
13187
13188
13189
13190
13191
**   current query is executed. Any column filter that applies to
**   phrase iPhrase of the current query is included in $p. For each
**   row visited, the callback function passed as the fourth argument
**   is invoked. The context and API objects passed to the callback
**   function may be used to access the properties of each matched row.
**   Invoking Api.xUserData() returns a copy of the pointer passed as
**   the third argument to pUserData.




**
**   If the callback function returns any value other than SQLITE_OK, the
**   query is abandoned and the xQueryPhrase function returns immediately.
**   If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK.
**   Otherwise, the error code is propagated upwards.
**
**   If the query runs to completion without incident, SQLITE_OK is returned.







>
>
>
>







13189
13190
13191
13192
13193
13194
13195
13196
13197
13198
13199
13200
13201
13202
13203
13204
13205
13206
**   current query is executed. Any column filter that applies to
**   phrase iPhrase of the current query is included in $p. For each
**   row visited, the callback function passed as the fourth argument
**   is invoked. The context and API objects passed to the callback
**   function may be used to access the properties of each matched row.
**   Invoking Api.xUserData() returns a copy of the pointer passed as
**   the third argument to pUserData.
**
**   If parameter iPhrase is less than zero, or greater than or equal to
**   the number of phrases in the query, as returned by xPhraseCount(),
**   this function returns SQLITE_RANGE.
**
**   If the callback function returns any value other than SQLITE_OK, the
**   query is abandoned and the xQueryPhrase function returns immediately.
**   If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK.
**   Otherwise, the error code is propagated upwards.
**
**   If the query runs to completion without incident, SQLITE_OK is returned.
13293
13294
13295
13296
13297
13298
13299

































13300
13301
13302
13303
13304
13305
13306
13307
13308
13309
**   xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext
**   (or xInst/xInstCount). The chief advantage of this API is that it is
**   significantly more efficient than those alternatives when used with
**   "detail=column" tables.
**
** xPhraseNextColumn()
**   See xPhraseFirstColumn above.

































*/
struct Fts5ExtensionApi {
  int iVersion;                   /* Currently always set to 2 */

  void *(*xUserData)(Fts5Context*);

  int (*xColumnCount)(Fts5Context*);
  int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow);
  int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken);








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


|







13308
13309
13310
13311
13312
13313
13314
13315
13316
13317
13318
13319
13320
13321
13322
13323
13324
13325
13326
13327
13328
13329
13330
13331
13332
13333
13334
13335
13336
13337
13338
13339
13340
13341
13342
13343
13344
13345
13346
13347
13348
13349
13350
13351
13352
13353
13354
13355
13356
13357
**   xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext
**   (or xInst/xInstCount). The chief advantage of this API is that it is
**   significantly more efficient than those alternatives when used with
**   "detail=column" tables.
**
** xPhraseNextColumn()
**   See xPhraseFirstColumn above.
**
** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken)
**   This is used to access token iToken of phrase iPhrase of the current
**   query. Before returning, output parameter *ppToken is set to point
**   to a buffer containing the requested token, and *pnToken to the
**   size of this buffer in bytes.
**
**   If iPhrase or iToken are less than zero, or if iPhrase is greater than
**   or equal to the number of phrases in the query as reported by
**   xPhraseCount(), or if iToken is equal to or greater than the number of
**   tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken
     are both zeroed.
**
**   The output text is not a copy of the query text that specified the
**   token. It is the output of the tokenizer module. For tokendata=1
**   tables, this includes any embedded 0x00 and trailing data.
**
** 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.
*/
struct Fts5ExtensionApi {
  int iVersion;                   /* Currently always set to 3 */

  void *(*xUserData)(Fts5Context*);

  int (*xColumnCount)(Fts5Context*);
  int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow);
  int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken);

13330
13331
13332
13333
13334
13335
13336







13337
13338
13339
13340
13341
13342
13343
  void *(*xGetAuxdata)(Fts5Context*, int bClear);

  int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*);
  void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff);

  int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
  void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);







};

/*
** CUSTOM AUXILIARY FUNCTIONS
*************************************************************************/

/*************************************************************************







>
>
>
>
>
>
>







13378
13379
13380
13381
13382
13383
13384
13385
13386
13387
13388
13389
13390
13391
13392
13393
13394
13395
13396
13397
13398
  void *(*xGetAuxdata)(Fts5Context*, int bClear);

  int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*);
  void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff);

  int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
  void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);

  /* Below this point are iVersion>=3 only */
  int (*xQueryToken)(Fts5Context*,
      int iPhrase, int iToken,
      const char **ppToken, int *pnToken
  );
  int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*);
};

/*
** CUSTOM AUXILIARY FUNCTIONS
*************************************************************************/

/*************************************************************************
13816
13817
13818
13819
13820
13821
13822
13823
13824
13825
13826
13827
13828
13829
13830
** Maximum number of pages in one database file.
**
** This is really just the default value for the max_page_count pragma.
** This value can be lowered (or raised) at run-time using that the
** max_page_count macro.
*/
#ifndef SQLITE_MAX_PAGE_COUNT
# define SQLITE_MAX_PAGE_COUNT 1073741823
#endif

/*
** Maximum length (in bytes) of the pattern in a LIKE or GLOB
** operator.
*/
#ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH







|







13871
13872
13873
13874
13875
13876
13877
13878
13879
13880
13881
13882
13883
13884
13885
** Maximum number of pages in one database file.
**
** This is really just the default value for the max_page_count pragma.
** This value can be lowered (or raised) at run-time using that the
** max_page_count macro.
*/
#ifndef SQLITE_MAX_PAGE_COUNT
# define SQLITE_MAX_PAGE_COUNT 0xfffffffe /* 4294967294 */
#endif

/*
** Maximum length (in bytes) of the pattern in a LIKE or GLOB
** operator.
*/
#ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH
13954
13955
13956
13957
13958
13959
13960













13961
13962
13963
13964
13965
13966
13967
** SEH support if the -DSQLITE_OMIT_SEH option is given.
*/
#if defined(_MSC_VER) && !defined(SQLITE_OMIT_SEH)
# define SQLITE_USE_SEH 1
#else
# undef SQLITE_USE_SEH
#endif














/*
** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2.
** 0 means mutexes are permanently disable and the library is never
** threadsafe.  1 means the library is serialized which is the highest
** level of threadsafety.  2 means the library is multithreaded - multiple
** threads can use SQLite as long as no two threads try to use the same







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







14009
14010
14011
14012
14013
14014
14015
14016
14017
14018
14019
14020
14021
14022
14023
14024
14025
14026
14027
14028
14029
14030
14031
14032
14033
14034
14035
** SEH support if the -DSQLITE_OMIT_SEH option is given.
*/
#if defined(_MSC_VER) && !defined(SQLITE_OMIT_SEH)
# define SQLITE_USE_SEH 1
#else
# undef SQLITE_USE_SEH
#endif

/*
** Enable SQLITE_DIRECT_OVERFLOW_READ, unless the build explicitly
** disables it using -DSQLITE_DIRECT_OVERFLOW_READ=0
*/
#if defined(SQLITE_DIRECT_OVERFLOW_READ) && SQLITE_DIRECT_OVERFLOW_READ+1==1
  /* Disable if -DSQLITE_DIRECT_OVERFLOW_READ=0 */
# undef SQLITE_DIRECT_OVERFLOW_READ
#else
  /* In all other cases, enable */
# define SQLITE_DIRECT_OVERFLOW_READ 1
#endif


/*
** The SQLITE_THREADSAFE macro must be defined as 0, 1, or 2.
** 0 means mutexes are permanently disable and the library is never
** threadsafe.  1 means the library is serialized which is the highest
** level of threadsafety.  2 means the library is multithreaded - multiple
** threads can use SQLite as long as no two threads try to use the same
15837
15838
15839
15840
15841
15842
15843
15844
15845
15846
15847
15848
15849
15850
15851
SQLITE_PRIVATE const char *sqlite3PagerFilename(const Pager*, int);
SQLITE_PRIVATE sqlite3_vfs *sqlite3PagerVfs(Pager*);
SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*);
SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager*);
SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*);
SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*);
SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*);
SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *);
SQLITE_PRIVATE void sqlite3PagerClearCache(Pager*);
SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *);

/* Functions used to truncate the database file. */
SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno);

SQLITE_PRIVATE void sqlite3PagerRekey(DbPage*, Pgno, u16);







|







15905
15906
15907
15908
15909
15910
15911
15912
15913
15914
15915
15916
15917
15918
15919
SQLITE_PRIVATE const char *sqlite3PagerFilename(const Pager*, int);
SQLITE_PRIVATE sqlite3_vfs *sqlite3PagerVfs(Pager*);
SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager*);
SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager*);
SQLITE_PRIVATE const char *sqlite3PagerJournalname(Pager*);
SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*);
SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*);
SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, u64*);
SQLITE_PRIVATE void sqlite3PagerClearCache(Pager*);
SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *);

/* Functions used to truncate the database file. */
SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno);

SQLITE_PRIVATE void sqlite3PagerRekey(DbPage*, Pgno, u16);
16424
16425
16426
16427
16428
16429
16430

16431
16432
16433
16434
16435
16436
16437
#define P4_EXPR       (-9) /* P4 is a pointer to an Expr tree */
#define P4_MEM        (-10) /* P4 is a pointer to a Mem*    structure */
#define P4_VTAB       (-11) /* P4 is a pointer to an sqlite3_vtab structure */
#define P4_REAL       (-12) /* P4 is a 64-bit floating point value */
#define P4_INT64      (-13) /* P4 is a 64-bit signed integer */
#define P4_INTARRAY   (-14) /* P4 is a vector of 32-bit integers */
#define P4_FUNCCTX    (-15) /* P4 is a pointer to an sqlite3_context object */


/* Error message codes for OP_Halt */
#define P5_ConstraintNotNull 1
#define P5_ConstraintUnique  2
#define P5_ConstraintCheck   3
#define P5_ConstraintFK      4








>







16492
16493
16494
16495
16496
16497
16498
16499
16500
16501
16502
16503
16504
16505
16506
#define P4_EXPR       (-9) /* P4 is a pointer to an Expr tree */
#define P4_MEM        (-10) /* P4 is a pointer to a Mem*    structure */
#define P4_VTAB       (-11) /* P4 is a pointer to an sqlite3_vtab structure */
#define P4_REAL       (-12) /* P4 is a 64-bit floating point value */
#define P4_INT64      (-13) /* P4 is a 64-bit signed integer */
#define P4_INTARRAY   (-14) /* P4 is a vector of 32-bit integers */
#define P4_FUNCCTX    (-15) /* P4 is a pointer to an sqlite3_context object */
#define P4_TABLEREF   (-16) /* Like P4_TABLE, but reference counted */

/* Error message codes for OP_Halt */
#define P5_ConstraintNotNull 1
#define P5_ConstraintUnique  2
#define P5_ConstraintCheck   3
#define P5_ConstraintFK      4

16646
16647
16648
16649
16650
16651
16652


16653
16654
16655
16656
16657
16658
16659
16660
16661
16662
16663
16664
16665
16666
#define OP_VCheck        174
#define OP_VInitIn       175 /* synopsis: r[P2]=ValueList(P1,P3)           */
#define OP_VColumn       176 /* synopsis: r[P3]=vcolumn(P2)                */
#define OP_VRename       177
#define OP_Pagecount     178
#define OP_MaxPgcnt      179
#define OP_ClrSubtype    180 /* synopsis: r[P1].subtype = 0                */


#define OP_FilterAdd     181 /* synopsis: filter(P1) += key(P3@P4)         */
#define OP_Trace         182
#define OP_CursorHint    183
#define OP_ReleaseReg    184 /* synopsis: release r[P1@P2] mask P3         */
#define OP_Noop          185
#define OP_Explain       186
#define OP_Abortable     187

/* Properties such as "out2" or "jump" that are specified in
** comments following the "case" for each opcode in the vdbe.c
** are encoded into bitvectors as follows:
*/
#define OPFLG_JUMP        0x01  /* jump:  P2 holds jmp target */
#define OPFLG_IN1         0x02  /* in1:   P1 is an input */







>
>
|
|
|
|
|
|
|







16715
16716
16717
16718
16719
16720
16721
16722
16723
16724
16725
16726
16727
16728
16729
16730
16731
16732
16733
16734
16735
16736
16737
#define OP_VCheck        174
#define OP_VInitIn       175 /* synopsis: r[P2]=ValueList(P1,P3)           */
#define OP_VColumn       176 /* synopsis: r[P3]=vcolumn(P2)                */
#define OP_VRename       177
#define OP_Pagecount     178
#define OP_MaxPgcnt      179
#define OP_ClrSubtype    180 /* synopsis: r[P1].subtype = 0                */
#define OP_GetSubtype    181 /* synopsis: r[P2] = r[P1].subtype            */
#define OP_SetSubtype    182 /* synopsis: r[P2].subtype = r[P1]            */
#define OP_FilterAdd     183 /* synopsis: filter(P1) += key(P3@P4)         */
#define OP_Trace         184
#define OP_CursorHint    185
#define OP_ReleaseReg    186 /* synopsis: release r[P1@P2] mask P3         */
#define OP_Noop          187
#define OP_Explain       188
#define OP_Abortable     189

/* Properties such as "out2" or "jump" that are specified in
** comments following the "case" for each opcode in the vdbe.c
** are encoded into bitvectors as follows:
*/
#define OPFLG_JUMP        0x01  /* jump:  P2 holds jmp target */
#define OPFLG_IN1         0x02  /* in1:   P1 is an input */
16688
16689
16690
16691
16692
16693
16694
16695
16696
16697
16698
16699
16700
16701
16702
16703
/* 120 */ 0x00, 0x00, 0x40, 0x00, 0x40, 0x40, 0x10, 0x10,\
/* 128 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x50,\
/* 136 */ 0x00, 0x40, 0x04, 0x04, 0x00, 0x40, 0x50, 0x40,\
/* 144 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\
/* 152 */ 0x00, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x04,\
/* 160 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x50,\
/* 176 */ 0x40, 0x00, 0x10, 0x10, 0x02, 0x00, 0x00, 0x00,\
/* 184 */ 0x00, 0x00, 0x00, 0x00,}

/* The resolve3P2Values() routine is able to run faster if it knows
** the value of the largest JUMP opcode.  The smaller the maximum
** JUMP opcode the better, so the mkopcodeh.tcl script that
** generated this include file strives to group all JUMP opcodes
** together near the beginning of the list.
*/







|
|







16759
16760
16761
16762
16763
16764
16765
16766
16767
16768
16769
16770
16771
16772
16773
16774
/* 120 */ 0x00, 0x00, 0x40, 0x00, 0x40, 0x40, 0x10, 0x10,\
/* 128 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x50,\
/* 136 */ 0x00, 0x40, 0x04, 0x04, 0x00, 0x40, 0x50, 0x40,\
/* 144 */ 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\
/* 152 */ 0x00, 0x10, 0x00, 0x00, 0x06, 0x10, 0x00, 0x04,\
/* 160 */ 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
/* 168 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x10, 0x50,\
/* 176 */ 0x40, 0x00, 0x10, 0x10, 0x02, 0x12, 0x12, 0x00,\
/* 184 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,}

/* The resolve3P2Values() routine is able to run faster if it knows
** the value of the largest JUMP opcode.  The smaller the maximum
** JUMP opcode the better, so the mkopcodeh.tcl script that
** generated this include file strives to group all JUMP opcodes
** together near the beginning of the list.
*/
17950
17951
17952
17953
17954
17955
17956
17957
17958
17959
17960
17961
17962
17963
17964
17965
17966
17967
17968
   SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
#define SFUNCTION(zName, nArg, iArg, bNC, xFunc) \
  {nArg, SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_DIRECTONLY|SQLITE_FUNC_UNSAFE, \
   SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
#define MFUNCTION(zName, nArg, xPtr, xFunc) \
  {nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \
   xPtr, 0, xFunc, 0, 0, 0, #zName, {0} }
#define JFUNCTION(zName, nArg, bUseCache, bWS, bRS, iArg, xFunc) \
  {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|SQLITE_FUNC_CONSTANT|\
   SQLITE_UTF8|((bUseCache)*SQLITE_FUNC_RUNONLY)|\
   ((bRS)*SQLITE_SUBTYPE)|((bWS)*SQLITE_RESULT_SUBTYPE), \
   SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
#define INLINE_FUNC(zName, nArg, iArg, mFlags) \
  {nArg, SQLITE_FUNC_BUILTIN|\
   SQLITE_UTF8|SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \
   SQLITE_INT_TO_PTR(iArg), 0, noopFunc, 0, 0, 0, #zName, {0} }
#define TEST_FUNC(zName, nArg, iArg, mFlags) \
  {nArg, SQLITE_FUNC_BUILTIN|\
         SQLITE_UTF8|SQLITE_FUNC_INTERNAL|SQLITE_FUNC_TEST| \







|



|







18021
18022
18023
18024
18025
18026
18027
18028
18029
18030
18031
18032
18033
18034
18035
18036
18037
18038
18039
   SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
#define SFUNCTION(zName, nArg, iArg, bNC, xFunc) \
  {nArg, SQLITE_FUNC_BUILTIN|SQLITE_UTF8|SQLITE_DIRECTONLY|SQLITE_FUNC_UNSAFE, \
   SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
#define MFUNCTION(zName, nArg, xPtr, xFunc) \
  {nArg, SQLITE_FUNC_BUILTIN|SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \
   xPtr, 0, xFunc, 0, 0, 0, #zName, {0} }
#define JFUNCTION(zName, nArg, bUseCache, bWS, bRS, bJsonB, iArg, xFunc) \
  {nArg, SQLITE_FUNC_BUILTIN|SQLITE_DETERMINISTIC|SQLITE_FUNC_CONSTANT|\
   SQLITE_UTF8|((bUseCache)*SQLITE_FUNC_RUNONLY)|\
   ((bRS)*SQLITE_SUBTYPE)|((bWS)*SQLITE_RESULT_SUBTYPE), \
   SQLITE_INT_TO_PTR(iArg|((bJsonB)*JSON_BLOB)),0,xFunc,0, 0, 0, #zName, {0} }
#define INLINE_FUNC(zName, nArg, iArg, mFlags) \
  {nArg, SQLITE_FUNC_BUILTIN|\
   SQLITE_UTF8|SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \
   SQLITE_INT_TO_PTR(iArg), 0, noopFunc, 0, 0, 0, #zName, {0} }
#define TEST_FUNC(zName, nArg, iArg, mFlags) \
  {nArg, SQLITE_FUNC_BUILTIN|\
         SQLITE_UTF8|SQLITE_FUNC_INTERNAL|SQLITE_FUNC_TEST| \
18589
18590
18591
18592
18593
18594
18595

18596
18597
18598
18599
18600
18601
18602
  unsigned idxType:2;      /* 0:Normal 1:UNIQUE, 2:PRIMARY KEY, 3:IPK */
  unsigned bUnordered:1;   /* Use this index for == or IN queries only */
  unsigned uniqNotNull:1;  /* True if UNIQUE and NOT NULL for all columns */
  unsigned isResized:1;    /* True if resizeIndexObject() has been called */
  unsigned isCovering:1;   /* True if this is a covering index */
  unsigned noSkipScan:1;   /* Do not try to use skip-scan if true */
  unsigned hasStat1:1;     /* aiRowLogEst values come from sqlite_stat1 */

  unsigned bNoQuery:1;     /* Do not use this index to optimize queries */
  unsigned bAscKeyBug:1;   /* True if the bba7b69f9849b5bf bug applies */
  unsigned bHasVCol:1;     /* Index references one or more VIRTUAL columns */
  unsigned bHasExpr:1;     /* Index contains an expression, either a literal
                           ** expression, or a reference to a VIRTUAL column */
#ifdef SQLITE_ENABLE_STAT4
  int nSample;             /* Number of elements in aSample[] */







>







18660
18661
18662
18663
18664
18665
18666
18667
18668
18669
18670
18671
18672
18673
18674
  unsigned idxType:2;      /* 0:Normal 1:UNIQUE, 2:PRIMARY KEY, 3:IPK */
  unsigned bUnordered:1;   /* Use this index for == or IN queries only */
  unsigned uniqNotNull:1;  /* True if UNIQUE and NOT NULL for all columns */
  unsigned isResized:1;    /* True if resizeIndexObject() has been called */
  unsigned isCovering:1;   /* True if this is a covering index */
  unsigned noSkipScan:1;   /* Do not try to use skip-scan if true */
  unsigned hasStat1:1;     /* aiRowLogEst values come from sqlite_stat1 */
  unsigned bLowQual:1;     /* sqlite_stat1 says this is a low-quality index */
  unsigned bNoQuery:1;     /* Do not use this index to optimize queries */
  unsigned bAscKeyBug:1;   /* True if the bba7b69f9849b5bf bug applies */
  unsigned bHasVCol:1;     /* Index references one or more VIRTUAL columns */
  unsigned bHasExpr:1;     /* Index contains an expression, either a literal
                           ** expression, or a reference to a VIRTUAL column */
#ifdef SQLITE_ENABLE_STAT4
  int nSample;             /* Number of elements in aSample[] */
18702
18703
18704
18705
18706
18707
18708

18709
18710
18711
18712
18713
18714
18715
    Expr *pFExpr;            /* Expression encoding the function */
    FuncDef *pFunc;          /* The aggregate function implementation */
    int iDistinct;           /* Ephemeral table used to enforce DISTINCT */
    int iDistAddr;           /* Address of OP_OpenEphemeral */
    int iOBTab;              /* Ephemeral table to implement ORDER BY */
    u8 bOBPayload;           /* iOBTab has payload columns separate from key */
    u8 bOBUnique;            /* Enforce uniqueness on iOBTab keys */

  } *aFunc;
  int nFunc;              /* Number of entries in aFunc[] */
  u32 selId;              /* Select to which this AggInfo belongs */
#ifdef SQLITE_DEBUG
  Select *pSelect;        /* SELECT statement that this AggInfo supports */
#endif
};







>







18774
18775
18776
18777
18778
18779
18780
18781
18782
18783
18784
18785
18786
18787
18788
    Expr *pFExpr;            /* Expression encoding the function */
    FuncDef *pFunc;          /* The aggregate function implementation */
    int iDistinct;           /* Ephemeral table used to enforce DISTINCT */
    int iDistAddr;           /* Address of OP_OpenEphemeral */
    int iOBTab;              /* Ephemeral table to implement ORDER BY */
    u8 bOBPayload;           /* iOBTab has payload columns separate from key */
    u8 bOBUnique;            /* Enforce uniqueness on iOBTab keys */
    u8 bUseSubtype;          /* Transfer subtype info through sorter */
  } *aFunc;
  int nFunc;              /* Number of entries in aFunc[] */
  u32 selId;              /* Select to which this AggInfo belongs */
#ifdef SQLITE_DEBUG
  Select *pSelect;        /* SELECT statement that this AggInfo supports */
#endif
};
19235
19236
19237
19238
19239
19240
19241

19242
19243
19244
19245
19246
19247
19248
    Upsert *pUpsert;     /* ON CONFLICT clause information from an upsert */
    int iBaseReg;        /* For TK_REGISTER when parsing RETURNING */
  } uNC;
  NameContext *pNext;  /* Next outer name context.  NULL for outermost */
  int nRef;            /* Number of names resolved by this context */
  int nNcErr;          /* Number of errors encountered while resolving names */
  int ncFlags;         /* Zero or more NC_* flags defined below */

  Select *pWinSelect;  /* SELECT statement for any window functions */
};

/*
** Allowed values for the NameContext, ncFlags field.
**
** Value constraints (all checked via assert()):







>







19308
19309
19310
19311
19312
19313
19314
19315
19316
19317
19318
19319
19320
19321
19322
    Upsert *pUpsert;     /* ON CONFLICT clause information from an upsert */
    int iBaseReg;        /* For TK_REGISTER when parsing RETURNING */
  } uNC;
  NameContext *pNext;  /* Next outer name context.  NULL for outermost */
  int nRef;            /* Number of names resolved by this context */
  int nNcErr;          /* Number of errors encountered while resolving names */
  int ncFlags;         /* Zero or more NC_* flags defined below */
  u32 nNestedSelect;   /* Number of nested selects using this NC */
  Select *pWinSelect;  /* SELECT statement for any window functions */
};

/*
** Allowed values for the NameContext, ncFlags field.
**
** Value constraints (all checked via assert()):
19951
19952
19953
19954
19955
19956
19957



19958
19959
19960
19961
19962
19963
19964
**       when the reference count reaches zero.
**
**   2.  Use sqlite3RCStrUnref() to free an RCStr string rather than
**       sqlite3_free()
**
**   3.  Make a (read-only) copy of a read-only RCStr string using
**       sqlite3RCStrRef().



*/
struct RCStr {
  u64 nRCRef;            /* Number of references */
  /* Total structure size should be a multiple of 8 bytes for alignment */
};

/*







>
>
>







20025
20026
20027
20028
20029
20030
20031
20032
20033
20034
20035
20036
20037
20038
20039
20040
20041
**       when the reference count reaches zero.
**
**   2.  Use sqlite3RCStrUnref() to free an RCStr string rather than
**       sqlite3_free()
**
**   3.  Make a (read-only) copy of a read-only RCStr string using
**       sqlite3RCStrRef().
**
** "String" is in the name, but an RCStr object can also be used to hold
** binary data.
*/
struct RCStr {
  u64 nRCRef;            /* Number of references */
  /* Total structure size should be a multiple of 8 bytes for alignment */
};

/*
20009
20010
20011
20012
20013
20014
20015



20016
20017
20018
20019
20020
20021
20022
  u8 bCoreMutex;                    /* True to enable core mutexing */
  u8 bFullMutex;                    /* True to enable full mutexing */
  u8 bOpenUri;                      /* True to interpret filenames as URIs */
  u8 bUseCis;                       /* Use covering indices for full-scans */
  u8 bSmallMalloc;                  /* Avoid large memory allocations if true */
  u8 bExtraSchemaChecks;            /* Verify type,name,tbl_name in schema */
  u8 bUseLongDouble;                /* Make use of long double */



  int mxStrlen;                     /* Maximum string length */
  int neverCorrupt;                 /* Database is always well-formed */
  int szLookaside;                  /* Default lookaside buffer size */
  int nLookaside;                   /* Default lookaside buffer count */
  int nStmtSpill;                   /* Stmt-journal spill-to-disk threshold */
  sqlite3_mem_methods m;            /* Low-level memory allocation interface */
  sqlite3_mutex_methods mutex;      /* Low-level mutex interface */







>
>
>







20086
20087
20088
20089
20090
20091
20092
20093
20094
20095
20096
20097
20098
20099
20100
20101
20102
  u8 bCoreMutex;                    /* True to enable core mutexing */
  u8 bFullMutex;                    /* True to enable full mutexing */
  u8 bOpenUri;                      /* True to interpret filenames as URIs */
  u8 bUseCis;                       /* Use covering indices for full-scans */
  u8 bSmallMalloc;                  /* Avoid large memory allocations if true */
  u8 bExtraSchemaChecks;            /* Verify type,name,tbl_name in schema */
  u8 bUseLongDouble;                /* Make use of long double */
#ifdef SQLITE_DEBUG
  u8 bJsonSelfcheck;                /* Double-check JSON parsing */
#endif
  int mxStrlen;                     /* Maximum string length */
  int neverCorrupt;                 /* Database is always well-formed */
  int szLookaside;                  /* Default lookaside buffer size */
  int nLookaside;                   /* Default lookaside buffer count */
  int nStmtSpill;                   /* Stmt-journal spill-to-disk threshold */
  sqlite3_mem_methods m;            /* Low-level memory allocation interface */
  sqlite3_mutex_methods mutex;      /* Low-level mutex interface */
20635
20636
20637
20638
20639
20640
20641

20642
20643
20644
20645
20646
20647
20648
20649
20650

20651
20652
20653
20654
20655
20656
20657
SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr*);
SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse*,ExprList*, const Token*, int);
SQLITE_PRIVATE void sqlite3ExprAddFunctionOrderBy(Parse*,Expr*,ExprList*);
SQLITE_PRIVATE void sqlite3ExprOrderByAggregateError(Parse*,Expr*);
SQLITE_PRIVATE void sqlite3ExprFunctionUsable(Parse*,const Expr*,const FuncDef*);
SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse*, Expr*, u32);
SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3*, Expr*);

SQLITE_PRIVATE void sqlite3ExprDeferredDelete(Parse*, Expr*);
SQLITE_PRIVATE void sqlite3ExprUnmapAndDelete(Parse*, Expr*);
SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*);
SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*);
SQLITE_PRIVATE Select *sqlite3ExprListToValues(Parse*, int, ExprList*);
SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList*,int,int);
SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,const Token*,int);
SQLITE_PRIVATE void sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*);
SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3*, ExprList*);

SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList*);
SQLITE_PRIVATE int sqlite3IndexHasDuplicateRootPage(Index*);
SQLITE_PRIVATE int sqlite3Init(sqlite3*, char**);
SQLITE_PRIVATE int sqlite3InitCallback(void*, int, char**, char**);
SQLITE_PRIVATE int sqlite3InitOne(sqlite3*, int, char**, u32);
SQLITE_PRIVATE void sqlite3Pragma(Parse*,Token*,Token*,Token*,int);
#ifndef SQLITE_OMIT_VIRTUALTABLE







>









>







20715
20716
20717
20718
20719
20720
20721
20722
20723
20724
20725
20726
20727
20728
20729
20730
20731
20732
20733
20734
20735
20736
20737
20738
20739
SQLITE_PRIVATE Expr *sqlite3ExprSimplifiedAndOr(Expr*);
SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse*,ExprList*, const Token*, int);
SQLITE_PRIVATE void sqlite3ExprAddFunctionOrderBy(Parse*,Expr*,ExprList*);
SQLITE_PRIVATE void sqlite3ExprOrderByAggregateError(Parse*,Expr*);
SQLITE_PRIVATE void sqlite3ExprFunctionUsable(Parse*,const Expr*,const FuncDef*);
SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse*, Expr*, u32);
SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3*, Expr*);
SQLITE_PRIVATE void sqlite3ExprDeleteGeneric(sqlite3*,void*);
SQLITE_PRIVATE void sqlite3ExprDeferredDelete(Parse*, Expr*);
SQLITE_PRIVATE void sqlite3ExprUnmapAndDelete(Parse*, Expr*);
SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*);
SQLITE_PRIVATE ExprList *sqlite3ExprListAppendVector(Parse*,ExprList*,IdList*,Expr*);
SQLITE_PRIVATE Select *sqlite3ExprListToValues(Parse*, int, ExprList*);
SQLITE_PRIVATE void sqlite3ExprListSetSortOrder(ExprList*,int,int);
SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,const Token*,int);
SQLITE_PRIVATE void sqlite3ExprListSetSpan(Parse*,ExprList*,const char*,const char*);
SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3*, ExprList*);
SQLITE_PRIVATE void sqlite3ExprListDeleteGeneric(sqlite3*,void*);
SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList*);
SQLITE_PRIVATE int sqlite3IndexHasDuplicateRootPage(Index*);
SQLITE_PRIVATE int sqlite3Init(sqlite3*, char**);
SQLITE_PRIVATE int sqlite3InitCallback(void*, int, char**, char**);
SQLITE_PRIVATE int sqlite3InitOne(sqlite3*, int, char**, u32);
SQLITE_PRIVATE void sqlite3Pragma(Parse*,Token*,Token*,Token*,int);
#ifndef SQLITE_OMIT_VIRTUALTABLE
20734
20735
20736
20737
20738
20739
20740

20741
20742
20743
20744
20745
20746
20747

#if SQLITE_MAX_ATTACHED>30
SQLITE_PRIVATE   int sqlite3DbMaskAllZero(yDbMask);
#endif
SQLITE_PRIVATE void sqlite3DropTable(Parse*, SrcList*, int, int);
SQLITE_PRIVATE void sqlite3CodeDropTable(Parse*, Table*, int, int);
SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3*, Table*);

SQLITE_PRIVATE void sqlite3FreeIndex(sqlite3*, Index*);
#ifndef SQLITE_OMIT_AUTOINCREMENT
SQLITE_PRIVATE   void sqlite3AutoincrementBegin(Parse *pParse);
SQLITE_PRIVATE   void sqlite3AutoincrementEnd(Parse *pParse);
#else
# define sqlite3AutoincrementBegin(X)
# define sqlite3AutoincrementEnd(X)







>







20816
20817
20818
20819
20820
20821
20822
20823
20824
20825
20826
20827
20828
20829
20830

#if SQLITE_MAX_ATTACHED>30
SQLITE_PRIVATE   int sqlite3DbMaskAllZero(yDbMask);
#endif
SQLITE_PRIVATE void sqlite3DropTable(Parse*, SrcList*, int, int);
SQLITE_PRIVATE void sqlite3CodeDropTable(Parse*, Table*, int, int);
SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3*, Table*);
SQLITE_PRIVATE void sqlite3DeleteTableGeneric(sqlite3*, void*);
SQLITE_PRIVATE void sqlite3FreeIndex(sqlite3*, Index*);
#ifndef SQLITE_OMIT_AUTOINCREMENT
SQLITE_PRIVATE   void sqlite3AutoincrementBegin(Parse *pParse);
SQLITE_PRIVATE   void sqlite3AutoincrementEnd(Parse *pParse);
#else
# define sqlite3AutoincrementBegin(X)
# define sqlite3AutoincrementEnd(X)
20770
20771
20772
20773
20774
20775
20776

20777
20778
20779
20780
20781
20782
20783
SQLITE_PRIVATE void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
                          Expr*, int, int, u8);
SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int);
SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*);
SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
                         Expr*,ExprList*,u32,Expr*);
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*);

SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse*, SrcList*);
SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, Trigger*);
SQLITE_PRIVATE void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int);
#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY)
SQLITE_PRIVATE Expr *sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,char*);
#endif
SQLITE_PRIVATE void sqlite3CodeChangeCount(Vdbe*,int,const char*);







>







20853
20854
20855
20856
20857
20858
20859
20860
20861
20862
20863
20864
20865
20866
20867
SQLITE_PRIVATE void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*,
                          Expr*, int, int, u8);
SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int);
SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*);
SQLITE_PRIVATE Select *sqlite3SelectNew(Parse*,ExprList*,SrcList*,Expr*,ExprList*,
                         Expr*,ExprList*,u32,Expr*);
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3*, Select*);
SQLITE_PRIVATE void sqlite3SelectDeleteGeneric(sqlite3*,void*);
SQLITE_PRIVATE Table *sqlite3SrcListLookup(Parse*, SrcList*);
SQLITE_PRIVATE int sqlite3IsReadOnly(Parse*, Table*, Trigger*);
SQLITE_PRIVATE void sqlite3OpenTable(Parse*, int iCur, int iDb, Table*, int);
#if defined(SQLITE_ENABLE_UPDATE_DELETE_LIMIT) && !defined(SQLITE_OMIT_SUBQUERY)
SQLITE_PRIVATE Expr *sqlite3LimitWhere(Parse*,SrcList*,Expr*,ExprList*,Expr*,char*);
#endif
SQLITE_PRIVATE void sqlite3CodeChangeCount(Vdbe*,int,const char*);
20996
20997
20998
20999
21000
21001
21002

21003
21004
21005
21006
21007
21008
21009
SQLITE_PRIVATE int sqlite3GetUInt32(const char*, u32*);
SQLITE_PRIVATE int sqlite3Atoi(const char*);
#ifndef SQLITE_OMIT_UTF16
SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar);
#endif
SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte);
SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8**);

SQLITE_PRIVATE LogEst sqlite3LogEst(u64);
SQLITE_PRIVATE LogEst sqlite3LogEstAdd(LogEst,LogEst);
SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double);
SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst);
SQLITE_PRIVATE VList *sqlite3VListAdd(sqlite3*,VList*,const char*,int,int);
SQLITE_PRIVATE const char *sqlite3VListNumToName(VList*,int);
SQLITE_PRIVATE int sqlite3VListNameToNum(VList*,const char*,int);







>







21080
21081
21082
21083
21084
21085
21086
21087
21088
21089
21090
21091
21092
21093
21094
SQLITE_PRIVATE int sqlite3GetUInt32(const char*, u32*);
SQLITE_PRIVATE int sqlite3Atoi(const char*);
#ifndef SQLITE_OMIT_UTF16
SQLITE_PRIVATE int sqlite3Utf16ByteLen(const void *pData, int nChar);
#endif
SQLITE_PRIVATE int sqlite3Utf8CharLen(const char *pData, int nByte);
SQLITE_PRIVATE u32 sqlite3Utf8Read(const u8**);
SQLITE_PRIVATE int sqlite3Utf8ReadLimited(const u8*, int, u32*);
SQLITE_PRIVATE LogEst sqlite3LogEst(u64);
SQLITE_PRIVATE LogEst sqlite3LogEstAdd(LogEst,LogEst);
SQLITE_PRIVATE LogEst sqlite3LogEstFromDouble(double);
SQLITE_PRIVATE u64 sqlite3LogEstToInt(LogEst);
SQLITE_PRIVATE VList *sqlite3VListAdd(sqlite3*,VList*,const char*,int,int);
SQLITE_PRIVATE const char *sqlite3VListNumToName(VList*,int);
SQLITE_PRIVATE int sqlite3VListNameToNum(VList*,const char*,int);
21342
21343
21344
21345
21346
21347
21348

21349
21350
21351
21352
21353
21354
21355
SQLITE_PRIVATE   int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int);
#endif
#ifndef SQLITE_OMIT_CTE
SQLITE_PRIVATE   Cte *sqlite3CteNew(Parse*,Token*,ExprList*,Select*,u8);
SQLITE_PRIVATE   void sqlite3CteDelete(sqlite3*,Cte*);
SQLITE_PRIVATE   With *sqlite3WithAdd(Parse*,With*,Cte*);
SQLITE_PRIVATE   void sqlite3WithDelete(sqlite3*,With*);

SQLITE_PRIVATE   With *sqlite3WithPush(Parse*, With*, u8);
#else
# define sqlite3CteNew(P,T,E,S)   ((void*)0)
# define sqlite3CteDelete(D,C)
# define sqlite3CteWithAdd(P,W,C) ((void*)0)
# define sqlite3WithDelete(x,y)
# define sqlite3WithPush(x,y,z) ((void*)0)







>







21427
21428
21429
21430
21431
21432
21433
21434
21435
21436
21437
21438
21439
21440
21441
SQLITE_PRIVATE   int sqlite3WalDefaultHook(void*,sqlite3*,const char*,int);
#endif
#ifndef SQLITE_OMIT_CTE
SQLITE_PRIVATE   Cte *sqlite3CteNew(Parse*,Token*,ExprList*,Select*,u8);
SQLITE_PRIVATE   void sqlite3CteDelete(sqlite3*,Cte*);
SQLITE_PRIVATE   With *sqlite3WithAdd(Parse*,With*,Cte*);
SQLITE_PRIVATE   void sqlite3WithDelete(sqlite3*,With*);
SQLITE_PRIVATE   void sqlite3WithDeleteGeneric(sqlite3*,void*);
SQLITE_PRIVATE   With *sqlite3WithPush(Parse*, With*, u8);
#else
# define sqlite3CteNew(P,T,E,S)   ((void*)0)
# define sqlite3CteDelete(D,C)
# define sqlite3CteWithAdd(P,W,C) ((void*)0)
# define sqlite3WithDelete(x,y)
# define sqlite3WithPush(x,y,z) ((void*)0)
22719
22720
22721
22722
22723
22724
22725



22726
22727
22728
22729
22730
22731
22732
   1,                         /* bCoreMutex */
   SQLITE_THREADSAFE==1,      /* bFullMutex */
   SQLITE_USE_URI,            /* bOpenUri */
   SQLITE_ALLOW_COVERING_INDEX_SCAN,   /* bUseCis */
   0,                         /* bSmallMalloc */
   1,                         /* bExtraSchemaChecks */
   sizeof(LONGDOUBLE_TYPE)>8, /* bUseLongDouble */



   0x7ffffffe,                /* mxStrlen */
   0,                         /* neverCorrupt */
   SQLITE_DEFAULT_LOOKASIDE,  /* szLookaside, nLookaside */
   SQLITE_STMTJRNL_SPILL,     /* nStmtSpill */
   {0,0,0,0,0,0,0,0},         /* m */
   {0,0,0,0,0,0,0,0,0},       /* mutex */
   {0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */







>
>
>







22805
22806
22807
22808
22809
22810
22811
22812
22813
22814
22815
22816
22817
22818
22819
22820
22821
   1,                         /* bCoreMutex */
   SQLITE_THREADSAFE==1,      /* bFullMutex */
   SQLITE_USE_URI,            /* bOpenUri */
   SQLITE_ALLOW_COVERING_INDEX_SCAN,   /* bUseCis */
   0,                         /* bSmallMalloc */
   1,                         /* bExtraSchemaChecks */
   sizeof(LONGDOUBLE_TYPE)>8, /* bUseLongDouble */
#ifdef SQLITE_DEBUG
   0,                         /* bJsonSelfcheck */
#endif
   0x7ffffffe,                /* mxStrlen */
   0,                         /* neverCorrupt */
   SQLITE_DEFAULT_LOOKASIDE,  /* szLookaside, nLookaside */
   SQLITE_STMTJRNL_SPILL,     /* nStmtSpill */
   {0,0,0,0,0,0,0,0},         /* m */
   {0,0,0,0,0,0,0,0,0},       /* mutex */
   {0,0,0,0,0,0,0,0,0,0,0,0,0},/* pcache2 */
23971
23972
23973
23974
23975
23976
23977
23978
23979
23980
23981
23982
23983
23984
23985
23986
23987
23988
23989
23990
23991
23992
23993
23994
23995
23996
23997
23998
    case SQLITE_DBSTATUS_CACHE_SPILL:
      op = SQLITE_DBSTATUS_CACHE_WRITE+1;
      /* no break */ deliberate_fall_through
    case SQLITE_DBSTATUS_CACHE_HIT:
    case SQLITE_DBSTATUS_CACHE_MISS:
    case SQLITE_DBSTATUS_CACHE_WRITE:{
      int i;
      int nRet = 0;
      assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 );
      assert( SQLITE_DBSTATUS_CACHE_WRITE==SQLITE_DBSTATUS_CACHE_HIT+2 );

      for(i=0; i<db->nDb; i++){
        if( db->aDb[i].pBt ){
          Pager *pPager = sqlite3BtreePager(db->aDb[i].pBt);
          sqlite3PagerCacheStat(pPager, op, resetFlag, &nRet);
        }
      }
      *pHighwater = 0; /* IMP: R-42420-56072 */
                       /* IMP: R-54100-20147 */
                       /* IMP: R-29431-39229 */
      *pCurrent = nRet;
      break;
    }

    /* Set *pCurrent to non-zero if there are unresolved deferred foreign
    ** key constraints.  Set *pCurrent to zero if all foreign key constraints
    ** have been satisfied.  The *pHighwater is always set to zero.
    */







|












|







24060
24061
24062
24063
24064
24065
24066
24067
24068
24069
24070
24071
24072
24073
24074
24075
24076
24077
24078
24079
24080
24081
24082
24083
24084
24085
24086
24087
    case SQLITE_DBSTATUS_CACHE_SPILL:
      op = SQLITE_DBSTATUS_CACHE_WRITE+1;
      /* no break */ deliberate_fall_through
    case SQLITE_DBSTATUS_CACHE_HIT:
    case SQLITE_DBSTATUS_CACHE_MISS:
    case SQLITE_DBSTATUS_CACHE_WRITE:{
      int i;
      u64 nRet = 0;
      assert( SQLITE_DBSTATUS_CACHE_MISS==SQLITE_DBSTATUS_CACHE_HIT+1 );
      assert( SQLITE_DBSTATUS_CACHE_WRITE==SQLITE_DBSTATUS_CACHE_HIT+2 );

      for(i=0; i<db->nDb; i++){
        if( db->aDb[i].pBt ){
          Pager *pPager = sqlite3BtreePager(db->aDb[i].pBt);
          sqlite3PagerCacheStat(pPager, op, resetFlag, &nRet);
        }
      }
      *pHighwater = 0; /* IMP: R-42420-56072 */
                       /* IMP: R-54100-20147 */
                       /* IMP: R-29431-39229 */
      *pCurrent = (int)nRet & 0x7fffffff;
      break;
    }

    /* Set *pCurrent to non-zero if there are unresolved deferred foreign
    ** key constraints.  Set *pCurrent to zero if all foreign key constraints
    ** have been satisfied.  The *pHighwater is always set to zero.
    */
25053
25054
25055
25056
25057
25058
25059






25060
25061
25062
25063
25064
25065
25066
  for(i=1; i<argc; i++){
    z = sqlite3_value_text(argv[i]);
    n = sqlite3_value_bytes(argv[i]);
    if( z==0 || parseModifier(context, (char*)z, n, p, i) ) return 1;
  }
  computeJD(p);
  if( p->isError || !validJulianDay(p->iJD) ) return 1;






  return 0;
}


/*
** The following routines implement the various date and time functions
** of SQLite.







>
>
>
>
>
>







25142
25143
25144
25145
25146
25147
25148
25149
25150
25151
25152
25153
25154
25155
25156
25157
25158
25159
25160
25161
  for(i=1; i<argc; i++){
    z = sqlite3_value_text(argv[i]);
    n = sqlite3_value_bytes(argv[i]);
    if( z==0 || parseModifier(context, (char*)z, n, p, i) ) return 1;
  }
  computeJD(p);
  if( p->isError || !validJulianDay(p->iJD) ) return 1;
  if( argc==1 && p->validYMD && p->D>28 ){
    /* Make sure a YYYY-MM-DD is normalized.
    ** Example: 2023-02-31 -> 2023-03-03 */
    assert( p->validJD );
    p->validYMD = 0;
  }
  return 0;
}


/*
** The following routines implement the various date and time functions
** of SQLite.
32081
32082
32083
32084
32085
32086
32087
32088
32089
32090
32091
32092
32093
32094
32095
  va_start(ap,zFormat);
  sqlite3_str_vappendf(p, zFormat, ap);
  va_end(ap);
}


/*****************************************************************************
** Reference counted string storage
*****************************************************************************/

/*
** Increase the reference count of the string by one.
**
** The input parameter is returned.
*/







|







32176
32177
32178
32179
32180
32181
32182
32183
32184
32185
32186
32187
32188
32189
32190
  va_start(ap,zFormat);
  sqlite3_str_vappendf(p, zFormat, ap);
  va_end(ap);
}


/*****************************************************************************
** Reference counted string/blob storage
*****************************************************************************/

/*
** Increase the reference count of the string by one.
**
** The input parameter is returned.
*/
32933
32934
32935
32936
32937
32938
32939
32940
32941
32942
32943
32944
32945
32946
32947
    case TK_BETWEEN: {
      const Expr *pX, *pY, *pZ;
      pX = pExpr->pLeft;
      assert( ExprUseXList(pExpr) );
      assert( pExpr->x.pList->nExpr==2 );
      pY = pExpr->x.pList->a[0].pExpr;
      pZ = pExpr->x.pList->a[1].pExpr;
      sqlite3TreeViewLine(pView, "BETWEEN");
      sqlite3TreeViewExpr(pView, pX, 1);
      sqlite3TreeViewExpr(pView, pY, 1);
      sqlite3TreeViewExpr(pView, pZ, 0);
      break;
    }
    case TK_TRIGGER: {
      /* If the opcode is TK_TRIGGER, then the expression is a reference







|







33028
33029
33030
33031
33032
33033
33034
33035
33036
33037
33038
33039
33040
33041
33042
    case TK_BETWEEN: {
      const Expr *pX, *pY, *pZ;
      pX = pExpr->pLeft;
      assert( ExprUseXList(pExpr) );
      assert( pExpr->x.pList->nExpr==2 );
      pY = pExpr->x.pList->a[0].pExpr;
      pZ = pExpr->x.pList->a[1].pExpr;
      sqlite3TreeViewLine(pView, "BETWEEN%s", zFlgs);
      sqlite3TreeViewExpr(pView, pX, 1);
      sqlite3TreeViewExpr(pView, pY, 1);
      sqlite3TreeViewExpr(pView, pZ, 0);
      break;
    }
    case TK_TRIGGER: {
      /* If the opcode is TK_TRIGGER, then the expression is a reference
34068
34069
34070
34071
34072
34073
34074



























34075




34076
34077
34078
34079
34080
34081
34082
    if( c<0x80
        || (c&0xFFFFF800)==0xD800
        || (c&0xFFFFFFFE)==0xFFFE ){  c = 0xFFFD; }
  }
  return c;
}



































/*
** If the TRANSLATE_TRACE macro is defined, the value of each Mem is
** printed on stderr on the way into and out of sqlite3VdbeMemTranslate().
*/
/* #define TRANSLATE_TRACE 1 */







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







34163
34164
34165
34166
34167
34168
34169
34170
34171
34172
34173
34174
34175
34176
34177
34178
34179
34180
34181
34182
34183
34184
34185
34186
34187
34188
34189
34190
34191
34192
34193
34194
34195
34196
34197
34198
34199
34200
34201
34202
34203
34204
34205
34206
34207
34208
    if( c<0x80
        || (c&0xFFFFF800)==0xD800
        || (c&0xFFFFFFFE)==0xFFFE ){  c = 0xFFFD; }
  }
  return c;
}

/*
** Read a single UTF8 character out of buffer z[], but reading no
** more than n characters from the buffer.  z[] is not zero-terminated.
**
** Return the number of bytes used to construct the character.
**
** Invalid UTF8 might generate a strange result.  No effort is made
** to detect invalid UTF8.
**
** At most 4 bytes will be read out of z[].  The return value will always
** be between 1 and 4.
*/
SQLITE_PRIVATE int sqlite3Utf8ReadLimited(
  const u8 *z,
  int n,
  u32 *piOut
){
  u32 c;
  int i = 1;
  assert( n>0 );
  c = z[0];
  if( c>=0xc0 ){
    c = sqlite3Utf8Trans1[c-0xc0];
    if( n>4 ) n = 4;
    while( i<n && (z[i] & 0xc0)==0x80 ){
      c = (c<<6) + (0x3f & z[i]);
      i++;
    }
  }
  *piOut = c;
  return i;
}


/*
** If the TRANSLATE_TRACE macro is defined, the value of each Mem is
** printed on stderr on the way into and out of sqlite3VdbeMemTranslate().
*/
/* #define TRANSLATE_TRACE 1 */
34583
34584
34585
34586
34587
34588
34589
34590
34591
34592
34593
34594
34595
34596
34597

/*
** Load the sqlite3.iSysErrno field if that is an appropriate thing
** to do based on the SQLite error code in rc.
*/
SQLITE_PRIVATE void sqlite3SystemError(sqlite3 *db, int rc){
  if( rc==SQLITE_IOERR_NOMEM ) return;
#ifdef SQLITE_USE_SEH
  if( rc==SQLITE_IOERR_IN_PAGE ){
    int ii;
    int iErr;
    sqlite3BtreeEnterAll(db);
    for(ii=0; ii<db->nDb; ii++){
      if( db->aDb[ii].pBt ){
        iErr = sqlite3PagerWalSystemErrno(sqlite3BtreePager(db->aDb[ii].pBt));







|







34709
34710
34711
34712
34713
34714
34715
34716
34717
34718
34719
34720
34721
34722
34723

/*
** Load the sqlite3.iSysErrno field if that is an appropriate thing
** to do based on the SQLite error code in rc.
*/
SQLITE_PRIVATE void sqlite3SystemError(sqlite3 *db, int rc){
  if( rc==SQLITE_IOERR_NOMEM ) return;
#if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL)
  if( rc==SQLITE_IOERR_IN_PAGE ){
    int ii;
    int iErr;
    sqlite3BtreeEnterAll(db);
    for(ii=0; ii<db->nDb; ii++){
      if( db->aDb[ii].pBt ){
        iErr = sqlite3PagerWalSystemErrno(sqlite3BtreePager(db->aDb[ii].pBt));
36837
36838
36839
36840
36841
36842
36843


36844
36845
36846
36847
36848
36849
36850
36851
36852
36853
36854
36855
36856
36857
    /* 174 */ "VCheck"           OpHelp(""),
    /* 175 */ "VInitIn"          OpHelp("r[P2]=ValueList(P1,P3)"),
    /* 176 */ "VColumn"          OpHelp("r[P3]=vcolumn(P2)"),
    /* 177 */ "VRename"          OpHelp(""),
    /* 178 */ "Pagecount"        OpHelp(""),
    /* 179 */ "MaxPgcnt"         OpHelp(""),
    /* 180 */ "ClrSubtype"       OpHelp("r[P1].subtype = 0"),


    /* 181 */ "FilterAdd"        OpHelp("filter(P1) += key(P3@P4)"),
    /* 182 */ "Trace"            OpHelp(""),
    /* 183 */ "CursorHint"       OpHelp(""),
    /* 184 */ "ReleaseReg"       OpHelp("release r[P1@P2] mask P3"),
    /* 185 */ "Noop"             OpHelp(""),
    /* 186 */ "Explain"          OpHelp(""),
    /* 187 */ "Abortable"        OpHelp(""),
  };
  return azName[i];
}
#endif

/************** End of opcodes.c *********************************************/
/************** Begin file os_kv.c *******************************************/







>
>
|
|
|
|
|
|
|







36963
36964
36965
36966
36967
36968
36969
36970
36971
36972
36973
36974
36975
36976
36977
36978
36979
36980
36981
36982
36983
36984
36985
    /* 174 */ "VCheck"           OpHelp(""),
    /* 175 */ "VInitIn"          OpHelp("r[P2]=ValueList(P1,P3)"),
    /* 176 */ "VColumn"          OpHelp("r[P3]=vcolumn(P2)"),
    /* 177 */ "VRename"          OpHelp(""),
    /* 178 */ "Pagecount"        OpHelp(""),
    /* 179 */ "MaxPgcnt"         OpHelp(""),
    /* 180 */ "ClrSubtype"       OpHelp("r[P1].subtype = 0"),
    /* 181 */ "GetSubtype"       OpHelp("r[P2] = r[P1].subtype"),
    /* 182 */ "SetSubtype"       OpHelp("r[P2].subtype = r[P1]"),
    /* 183 */ "FilterAdd"        OpHelp("filter(P1) += key(P3@P4)"),
    /* 184 */ "Trace"            OpHelp(""),
    /* 185 */ "CursorHint"       OpHelp(""),
    /* 186 */ "ReleaseReg"       OpHelp("release r[P1@P2] mask P3"),
    /* 187 */ "Noop"             OpHelp(""),
    /* 188 */ "Explain"          OpHelp(""),
    /* 189 */ "Abortable"        OpHelp(""),
  };
  return azName[i];
}
#endif

/************** End of opcodes.c *********************************************/
/************** Begin file os_kv.c *******************************************/
41889
41890
41891
41892
41893
41894
41895

41896





41897
41898
41899
41900
41901
41902
41903
    case SQLITE_FCNTL_HAS_MOVED: {
      *(int*)pArg = fileHasMoved(pFile);
      return SQLITE_OK;
    }
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
    case SQLITE_FCNTL_LOCK_TIMEOUT: {
      int iOld = pFile->iBusyTimeout;

      pFile->iBusyTimeout = *(int*)pArg;





      *(int*)pArg = iOld;
      return SQLITE_OK;
    }
#endif
#if SQLITE_MAX_MMAP_SIZE>0
    case SQLITE_FCNTL_MMAP_SIZE: {
      i64 newLimit = *(i64*)pArg;







>

>
>
>
>
>







42017
42018
42019
42020
42021
42022
42023
42024
42025
42026
42027
42028
42029
42030
42031
42032
42033
42034
42035
42036
42037
    case SQLITE_FCNTL_HAS_MOVED: {
      *(int*)pArg = fileHasMoved(pFile);
      return SQLITE_OK;
    }
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
    case SQLITE_FCNTL_LOCK_TIMEOUT: {
      int iOld = pFile->iBusyTimeout;
#if SQLITE_ENABLE_SETLK_TIMEOUT==1
      pFile->iBusyTimeout = *(int*)pArg;
#elif SQLITE_ENABLE_SETLK_TIMEOUT==2
      pFile->iBusyTimeout = !!(*(int*)pArg);
#else
# error "SQLITE_ENABLE_SETLK_TIMEOUT must be set to 1 or 2"
#endif
      *(int*)pArg = iOld;
      return SQLITE_OK;
    }
#endif
#if SQLITE_MAX_MMAP_SIZE>0
    case SQLITE_FCNTL_MMAP_SIZE: {
      i64 newLimit = *(i64*)pArg;
42142
42143
42144
42145
42146
42147
42148



















42149
42150
42151
42152
42153
42154
42155
42156
42157
42158
42159
42160
42161



42162
42163
42164
42165
42166
42167
42168
42169
42170
42171
42172
**
**      hShm
**      zFilename
**
** Either unixShmNode.pShmMutex must be held or unixShmNode.nRef==0 and
** unixMutexHeld() is true when reading or writing any other field
** in this structure.



















*/
struct unixShmNode {
  unixInodeInfo *pInode;     /* unixInodeInfo that owns this SHM node */
  sqlite3_mutex *pShmMutex;  /* Mutex to access this object */
  char *zFilename;           /* Name of the mmapped file */
  int hShm;                  /* Open file descriptor */
  int szRegion;              /* Size of shared-memory regions */
  u16 nRegion;               /* Size of array apRegion */
  u8 isReadonly;             /* True if read-only */
  u8 isUnlocked;             /* True if no DMS lock held */
  char **apRegion;           /* Array of mapped shared-memory regions */
  int nRef;                  /* Number of unixShm objects pointing to this */
  unixShm *pFirst;           /* All unixShm objects pointing to this */



  int aLock[SQLITE_SHM_NLOCK];  /* # shared locks on slot, -1==excl lock */
#ifdef SQLITE_DEBUG
  u8 exclMask;               /* Mask of exclusive locks held */
  u8 sharedMask;             /* Mask of shared locks held */
  u8 nextShmId;              /* Next available unixShm.id value */
#endif
};

/*
** Structure used internally by this VFS to record the state of an
** open shared memory connection.







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













>
>
>


<
<







42276
42277
42278
42279
42280
42281
42282
42283
42284
42285
42286
42287
42288
42289
42290
42291
42292
42293
42294
42295
42296
42297
42298
42299
42300
42301
42302
42303
42304
42305
42306
42307
42308
42309
42310
42311
42312
42313
42314
42315
42316
42317
42318
42319


42320
42321
42322
42323
42324
42325
42326
**
**      hShm
**      zFilename
**
** Either unixShmNode.pShmMutex must be held or unixShmNode.nRef==0 and
** unixMutexHeld() is true when reading or writing any other field
** in this structure.
**
** aLock[SQLITE_SHM_NLOCK]:
**   This array records the various locks held by clients on each of the
**   SQLITE_SHM_NLOCK slots. If the aLock[] entry is set to 0, then no
**   locks are held by the process on this slot. If it is set to -1, then
**   some client holds an EXCLUSIVE lock on the locking slot. If the aLock[]
**   value is set to a positive value, then it is the number of shared
**   locks currently held on the slot.
**
** aMutex[SQLITE_SHM_NLOCK]:
**   Normally, when SQLITE_ENABLE_SETLK_TIMEOUT is not defined, mutex
**   pShmMutex is used to protect the aLock[] array and the right to
**   call fcntl() on unixShmNode.hShm to obtain or release locks.
**
**   If SQLITE_ENABLE_SETLK_TIMEOUT is defined though, we use an array
**   of mutexes - one for each locking slot. To read or write locking
**   slot aLock[iSlot], the caller must hold the corresponding mutex
**   aMutex[iSlot]. Similarly, to call fcntl() to obtain or release a
**   lock corresponding to slot iSlot, mutex aMutex[iSlot] must be held.
*/
struct unixShmNode {
  unixInodeInfo *pInode;     /* unixInodeInfo that owns this SHM node */
  sqlite3_mutex *pShmMutex;  /* Mutex to access this object */
  char *zFilename;           /* Name of the mmapped file */
  int hShm;                  /* Open file descriptor */
  int szRegion;              /* Size of shared-memory regions */
  u16 nRegion;               /* Size of array apRegion */
  u8 isReadonly;             /* True if read-only */
  u8 isUnlocked;             /* True if no DMS lock held */
  char **apRegion;           /* Array of mapped shared-memory regions */
  int nRef;                  /* Number of unixShm objects pointing to this */
  unixShm *pFirst;           /* All unixShm objects pointing to this */
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  sqlite3_mutex *aMutex[SQLITE_SHM_NLOCK];
#endif
  int aLock[SQLITE_SHM_NLOCK];  /* # shared locks on slot, -1==excl lock */
#ifdef SQLITE_DEBUG


  u8 nextShmId;              /* Next available unixShm.id value */
#endif
};

/*
** Structure used internally by this VFS to record the state of an
** open shared memory connection.
42241
42242
42243
42244
42245
42246
42247
42248
42249









42250








42251


42252
42253
42254
42255
42256
42257

42258
42259
42260
42261
42262
42263
42264
42265
42266
42267
42268
42269
42270
42271
42272
42273
42274
42275
42276
42277
42278
42279
42280
42281
42282
42283
42284
42285
42286
42287
42288
42289
42290
42291
42292
42293
42294
42295
42296
42297
42298
42299
42300
42301
42302
42303
42304
42305
42306
42307
42308
42309
42310
42311
42312
42313
42314
  int ofst,              /* First byte of the locking range */
  int n                  /* Number of bytes to lock */
){
  unixShmNode *pShmNode; /* Apply locks to this open shared-memory segment */
  struct flock f;        /* The posix advisory locking structure */
  int rc = SQLITE_OK;    /* Result code form fcntl() */

  /* Access to the unixShmNode object is serialized by the caller */
  pShmNode = pFile->pInode->pShmNode;









  assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) );








  assert( pShmNode->nRef>0 || unixMutexHeld() );



  /* Shared locks never span more than one byte */
  assert( n==1 || lockType!=F_RDLCK );

  /* Locks are within range */
  assert( n>=1 && n<=SQLITE_SHM_NLOCK );


  if( pShmNode->hShm>=0 ){
    int res;
    /* Initialize the locking parameters */
    f.l_type = lockType;
    f.l_whence = SEEK_SET;
    f.l_start = ofst;
    f.l_len = n;
    res = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile);
    if( res==-1 ){
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
      rc = (pFile->iBusyTimeout ? SQLITE_BUSY_TIMEOUT : SQLITE_BUSY);
#else
      rc = SQLITE_BUSY;
#endif
    }
  }

  /* Update the global lock state and do debug tracing */
#ifdef SQLITE_DEBUG
  { u16 mask;
  OSTRACE(("SHM-LOCK "));
  mask = ofst>31 ? 0xffff : (1<<(ofst+n)) - (1<<ofst);
  if( rc==SQLITE_OK ){
    if( lockType==F_UNLCK ){
      OSTRACE(("unlock %d ok", ofst));
      pShmNode->exclMask &= ~mask;
      pShmNode->sharedMask &= ~mask;
    }else if( lockType==F_RDLCK ){
      OSTRACE(("read-lock %d ok", ofst));
      pShmNode->exclMask &= ~mask;
      pShmNode->sharedMask |= mask;
    }else{
      assert( lockType==F_WRLCK );
      OSTRACE(("write-lock %d ok", ofst));
      pShmNode->exclMask |= mask;
      pShmNode->sharedMask &= ~mask;
    }
  }else{
    if( lockType==F_UNLCK ){
      OSTRACE(("unlock %d failed", ofst));
    }else if( lockType==F_RDLCK ){
      OSTRACE(("read-lock failed"));
    }else{
      assert( lockType==F_WRLCK );
      OSTRACE(("write-lock %d failed", ofst));
    }
  }
  OSTRACE((" - afterwards %03x,%03x\n",
           pShmNode->sharedMask, pShmNode->exclMask));
  }
#endif

  return rc;
}

/*







<

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






>










|







|

<

<


|
<
<

|
<
<


|
<
<



|

|


|

<
<
<







42395
42396
42397
42398
42399
42400
42401

42402
42403
42404
42405
42406
42407
42408
42409
42410
42411
42412
42413
42414
42415
42416
42417
42418
42419
42420
42421
42422
42423
42424
42425
42426
42427
42428
42429
42430
42431
42432
42433
42434
42435
42436
42437
42438
42439
42440
42441
42442
42443
42444
42445
42446
42447
42448
42449
42450

42451

42452
42453
42454


42455
42456


42457
42458
42459


42460
42461
42462
42463
42464
42465
42466
42467
42468
42469



42470
42471
42472
42473
42474
42475
42476
  int ofst,              /* First byte of the locking range */
  int n                  /* Number of bytes to lock */
){
  unixShmNode *pShmNode; /* Apply locks to this open shared-memory segment */
  struct flock f;        /* The posix advisory locking structure */
  int rc = SQLITE_OK;    /* Result code form fcntl() */


  pShmNode = pFile->pInode->pShmNode;

  /* Assert that the parameters are within expected range and that the
  ** correct mutex or mutexes are held. */
  assert( pShmNode->nRef>=0 );
  assert( (ofst==UNIX_SHM_DMS && n==1)
       || (ofst>=UNIX_SHM_BASE && ofst+n<=(UNIX_SHM_BASE+SQLITE_SHM_NLOCK))
  );
  if( ofst==UNIX_SHM_DMS ){
    assert( pShmNode->nRef>0 || unixMutexHeld() );
    assert( pShmNode->nRef==0 || sqlite3_mutex_held(pShmNode->pShmMutex) );
  }else{
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
    int ii;
    for(ii=ofst-UNIX_SHM_BASE; ii<ofst-UNIX_SHM_BASE+n; ii++){
      assert( sqlite3_mutex_held(pShmNode->aMutex[ii]) );
    }
#else
    assert( sqlite3_mutex_held(pShmNode->pShmMutex) );
    assert( pShmNode->nRef>0 );
#endif
  }

  /* Shared locks never span more than one byte */
  assert( n==1 || lockType!=F_RDLCK );

  /* Locks are within range */
  assert( n>=1 && n<=SQLITE_SHM_NLOCK );
  assert( ofst>=UNIX_SHM_BASE && ofst<=(UNIX_SHM_DMS+SQLITE_SHM_NLOCK) );

  if( pShmNode->hShm>=0 ){
    int res;
    /* Initialize the locking parameters */
    f.l_type = lockType;
    f.l_whence = SEEK_SET;
    f.l_start = ofst;
    f.l_len = n;
    res = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile);
    if( res==-1 ){
#if defined(SQLITE_ENABLE_SETLK_TIMEOUT) && SQLITE_ENABLE_SETLK_TIMEOUT==1
      rc = (pFile->iBusyTimeout ? SQLITE_BUSY_TIMEOUT : SQLITE_BUSY);
#else
      rc = SQLITE_BUSY;
#endif
    }
  }

  /* Do debug tracing */
#ifdef SQLITE_DEBUG

  OSTRACE(("SHM-LOCK "));

  if( rc==SQLITE_OK ){
    if( lockType==F_UNLCK ){
      OSTRACE(("unlock %d..%d ok\n", ofst, ofst+n-1));


    }else if( lockType==F_RDLCK ){
      OSTRACE(("read-lock %d..%d ok\n", ofst, ofst+n-1));


    }else{
      assert( lockType==F_WRLCK );
      OSTRACE(("write-lock %d..%d ok\n", ofst, ofst+n-1));


    }
  }else{
    if( lockType==F_UNLCK ){
      OSTRACE(("unlock %d..%d failed\n", ofst, ofst+n-1));
    }else if( lockType==F_RDLCK ){
      OSTRACE(("read-lock %d..%d failed\n", ofst, ofst+n-1));
    }else{
      assert( lockType==F_WRLCK );
      OSTRACE(("write-lock %d..%d failed\n", ofst, ofst+n-1));
    }



  }
#endif

  return rc;
}

/*
42338
42339
42340
42341
42342
42343
42344





42345
42346
42347
42348
42349
42350
42351
  unixShmNode *p = pFd->pInode->pShmNode;
  assert( unixMutexHeld() );
  if( p && ALWAYS(p->nRef==0) ){
    int nShmPerMap = unixShmRegionPerMap();
    int i;
    assert( p->pInode==pFd->pInode );
    sqlite3_mutex_free(p->pShmMutex);





    for(i=0; i<p->nRegion; i+=nShmPerMap){
      if( p->hShm>=0 ){
        osMunmap(p->apRegion[i], p->szRegion);
      }else{
        sqlite3_free(p->apRegion[i]);
      }
    }







>
>
>
>
>







42500
42501
42502
42503
42504
42505
42506
42507
42508
42509
42510
42511
42512
42513
42514
42515
42516
42517
42518
  unixShmNode *p = pFd->pInode->pShmNode;
  assert( unixMutexHeld() );
  if( p && ALWAYS(p->nRef==0) ){
    int nShmPerMap = unixShmRegionPerMap();
    int i;
    assert( p->pInode==pFd->pInode );
    sqlite3_mutex_free(p->pShmMutex);
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
    for(i=0; i<SQLITE_SHM_NLOCK; i++){
      sqlite3_mutex_free(p->aMutex[i]);
    }
#endif
    for(i=0; i<p->nRegion; i+=nShmPerMap){
      if( p->hShm>=0 ){
        osMunmap(p->apRegion[i], p->szRegion);
      }else{
        sqlite3_free(p->apRegion[i]);
      }
    }
42397
42398
42399
42400
42401
42402
42403










42404



42405
42406
42407
42408
42409
42410
42411
  if( osFcntl(pShmNode->hShm, F_GETLK, &lock)!=0 ) {
    rc = SQLITE_IOERR_LOCK;
  }else if( lock.l_type==F_UNLCK ){
    if( pShmNode->isReadonly ){
      pShmNode->isUnlocked = 1;
      rc = SQLITE_READONLY_CANTINIT;
    }else{










      rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1);



      /* The first connection to attach must truncate the -shm file.  We
      ** truncate to 3 bytes (an arbitrary small number, less than the
      ** -shm header size) rather than 0 as a system debugging aid, to
      ** help detect if a -shm file truncation is legitimate or is the work
      ** or a rogue process. */
      if( rc==SQLITE_OK && robust_ftruncate(pShmNode->hShm, 3) ){
        rc = unixLogError(SQLITE_IOERR_SHMOPEN,"ftruncate",pShmNode->zFilename);







>
>
>
>
>
>
>
>
>
>

>
>
>







42564
42565
42566
42567
42568
42569
42570
42571
42572
42573
42574
42575
42576
42577
42578
42579
42580
42581
42582
42583
42584
42585
42586
42587
42588
42589
42590
42591
  if( osFcntl(pShmNode->hShm, F_GETLK, &lock)!=0 ) {
    rc = SQLITE_IOERR_LOCK;
  }else if( lock.l_type==F_UNLCK ){
    if( pShmNode->isReadonly ){
      pShmNode->isUnlocked = 1;
      rc = SQLITE_READONLY_CANTINIT;
    }else{
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
      /* Do not use a blocking lock here. If the lock cannot be obtained
      ** immediately, it means some other connection is truncating the
      ** *-shm file. And after it has done so, it will not release its
      ** lock, but only downgrade it to a shared lock. So no point in
      ** blocking here. The call below to obtain the shared DMS lock may
      ** use a blocking lock. */
      int iSaveTimeout = pDbFd->iBusyTimeout;
      pDbFd->iBusyTimeout = 0;
#endif
      rc = unixShmSystemLock(pDbFd, F_WRLCK, UNIX_SHM_DMS, 1);
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
      pDbFd->iBusyTimeout = iSaveTimeout;
#endif
      /* The first connection to attach must truncate the -shm file.  We
      ** truncate to 3 bytes (an arbitrary small number, less than the
      ** -shm header size) rather than 0 as a system debugging aid, to
      ** help detect if a -shm file truncation is legitimate or is the work
      ** or a rogue process. */
      if( rc==SQLITE_OK && robust_ftruncate(pShmNode->hShm, 3) ){
        rc = unixLogError(SQLITE_IOERR_SHMOPEN,"ftruncate",pShmNode->zFilename);
42518
42519
42520
42521
42522
42523
42524












42525
42526
42527
42528
42529
42530
42531
    pShmNode->pInode = pDbFd->pInode;
    if( sqlite3GlobalConfig.bCoreMutex ){
      pShmNode->pShmMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
      if( pShmNode->pShmMutex==0 ){
        rc = SQLITE_NOMEM_BKPT;
        goto shm_open_err;
      }












    }

    if( pInode->bProcessLock==0 ){
      if( 0==sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
        pShmNode->hShm = robust_open(zShm, O_RDWR|O_CREAT|O_NOFOLLOW,
                                     (sStat.st_mode&0777));
      }







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







42698
42699
42700
42701
42702
42703
42704
42705
42706
42707
42708
42709
42710
42711
42712
42713
42714
42715
42716
42717
42718
42719
42720
42721
42722
42723
    pShmNode->pInode = pDbFd->pInode;
    if( sqlite3GlobalConfig.bCoreMutex ){
      pShmNode->pShmMutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
      if( pShmNode->pShmMutex==0 ){
        rc = SQLITE_NOMEM_BKPT;
        goto shm_open_err;
      }
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
      {
        int ii;
        for(ii=0; ii<SQLITE_SHM_NLOCK; ii++){
          pShmNode->aMutex[ii] = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST);
          if( pShmNode->aMutex[ii]==0 ){
            rc = SQLITE_NOMEM_BKPT;
            goto shm_open_err;
          }
        }
      }
#endif
    }

    if( pInode->bProcessLock==0 ){
      if( 0==sqlite3_uri_boolean(pDbFd->zPath, "readonly_shm", 0) ){
        pShmNode->hShm = robust_open(zShm, O_RDWR|O_CREAT|O_NOFOLLOW,
                                     (sStat.st_mode&0777));
      }
42739
42740
42741
42742
42743
42744
42745



42746
42747
42748
42749
42750
42751
42752
42753
42754
42755
42756
42757
42758
42759
42760
42761
42762
42763
42764
42765

42766
42767
42768
42769
42770
42771
42772
42773
42774
42775
42776
42777
42778
42779
42780
42781
42782
42783
42784
42785
42786
42787
42788
42789
42790
42791
42792
42793
42794
** held by each client. Return true if it does, or false otherwise. This
** is to be used in an assert(). e.g.
**
**     assert( assertLockingArrayOk(pShmNode) );
*/
#ifdef SQLITE_DEBUG
static int assertLockingArrayOk(unixShmNode *pShmNode){



  unixShm *pX;
  int aLock[SQLITE_SHM_NLOCK];
  assert( sqlite3_mutex_held(pShmNode->pShmMutex) );

  memset(aLock, 0, sizeof(aLock));
  for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
    int i;
    for(i=0; i<SQLITE_SHM_NLOCK; i++){
      if( pX->exclMask & (1<<i) ){
        assert( aLock[i]==0 );
        aLock[i] = -1;
      }else if( pX->sharedMask & (1<<i) ){
        assert( aLock[i]>=0 );
        aLock[i]++;
      }
    }
  }

  assert( 0==memcmp(pShmNode->aLock, aLock, sizeof(aLock)) );
  return (memcmp(pShmNode->aLock, aLock, sizeof(aLock))==0);

}
#endif

/*
** Change the lock state for a shared-memory segment.
**
** Note that the relationship between SHAREd and EXCLUSIVE locks is a little
** different here than in posix.  In xShmLock(), one can go from unlocked
** to shared and back or from unlocked to exclusive and back.  But one may
** not go from shared to exclusive or from exclusive to shared.
*/
static int unixShmLock(
  sqlite3_file *fd,          /* Database file holding the shared memory */
  int ofst,                  /* First lock to acquire or release */
  int n,                     /* Number of locks to acquire or release */
  int flags                  /* What to do with the lock */
){
  unixFile *pDbFd = (unixFile*)fd;      /* Connection holding shared memory */
  unixShm *p;                           /* The shared memory being locked */
  unixShmNode *pShmNode;                /* The underlying file iNode */
  int rc = SQLITE_OK;                   /* Result code */
  u16 mask;                             /* Mask of locks to take or release */
  int *aLock;

  p = pDbFd->pShm;
  if( p==0 ) return SQLITE_IOERR_SHMLOCK;
  pShmNode = p->pShmNode;
  if( NEVER(pShmNode==0) ) return SQLITE_IOERR_SHMLOCK;
  aLock = pShmNode->aLock;







>
>
>


<

















>






|














|







42931
42932
42933
42934
42935
42936
42937
42938
42939
42940
42941
42942

42943
42944
42945
42946
42947
42948
42949
42950
42951
42952
42953
42954
42955
42956
42957
42958
42959
42960
42961
42962
42963
42964
42965
42966
42967
42968
42969
42970
42971
42972
42973
42974
42975
42976
42977
42978
42979
42980
42981
42982
42983
42984
42985
42986
42987
42988
42989
** held by each client. Return true if it does, or false otherwise. This
** is to be used in an assert(). e.g.
**
**     assert( assertLockingArrayOk(pShmNode) );
*/
#ifdef SQLITE_DEBUG
static int assertLockingArrayOk(unixShmNode *pShmNode){
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  return 1;
#else
  unixShm *pX;
  int aLock[SQLITE_SHM_NLOCK];


  memset(aLock, 0, sizeof(aLock));
  for(pX=pShmNode->pFirst; pX; pX=pX->pNext){
    int i;
    for(i=0; i<SQLITE_SHM_NLOCK; i++){
      if( pX->exclMask & (1<<i) ){
        assert( aLock[i]==0 );
        aLock[i] = -1;
      }else if( pX->sharedMask & (1<<i) ){
        assert( aLock[i]>=0 );
        aLock[i]++;
      }
    }
  }

  assert( 0==memcmp(pShmNode->aLock, aLock, sizeof(aLock)) );
  return (memcmp(pShmNode->aLock, aLock, sizeof(aLock))==0);
#endif
}
#endif

/*
** Change the lock state for a shared-memory segment.
**
** Note that the relationship between SHARED and EXCLUSIVE locks is a little
** different here than in posix.  In xShmLock(), one can go from unlocked
** to shared and back or from unlocked to exclusive and back.  But one may
** not go from shared to exclusive or from exclusive to shared.
*/
static int unixShmLock(
  sqlite3_file *fd,          /* Database file holding the shared memory */
  int ofst,                  /* First lock to acquire or release */
  int n,                     /* Number of locks to acquire or release */
  int flags                  /* What to do with the lock */
){
  unixFile *pDbFd = (unixFile*)fd;      /* Connection holding shared memory */
  unixShm *p;                           /* The shared memory being locked */
  unixShmNode *pShmNode;                /* The underlying file iNode */
  int rc = SQLITE_OK;                   /* Result code */
  u16 mask = (1<<(ofst+n)) - (1<<ofst); /* Mask of locks to take or release */
  int *aLock;

  p = pDbFd->pShm;
  if( p==0 ) return SQLITE_IOERR_SHMLOCK;
  pShmNode = p->pShmNode;
  if( NEVER(pShmNode==0) ) return SQLITE_IOERR_SHMLOCK;
  aLock = pShmNode->aLock;
42815
42816
42817
42818
42819
42820
42821


42822
42823
42824
42825
42826
42827

42828
42829










42830





42831


























42832

42833

42834
42835
42836

42837



42838






42839
42840
42841


42842
42843
42844
42845
42846
42847
42848
42849
42850
42851
42852
42853
42854
42855
42856
42857
42858
42859
42860
42861

42862
42863
42864
42865

42866
42867
42868
42869
42870
42871
42872
42873
42874
42875



42876



42877
42878
42879
42880
42881
42882
42883
42884
42885
42886
42887
42888
42889
42890
42891
42892
42893
42894
42895
42896
42897
42898
42899
42900
42901
42902









42903



42904
42905
42906
42907
42908
42909
42910
  ** In other words, if this is a blocking lock, none of the locks that
  ** occur later in the above list than the lock being obtained may be
  ** held.
  **
  ** It is not permitted to block on the RECOVER lock.
  */
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT


  assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
         (ofst!=2)                                   /* not RECOVER */
      && (ofst!=1 || (p->exclMask|p->sharedMask)==0)
      && (ofst!=0 || (p->exclMask|p->sharedMask)<3)
      && (ofst<3  || (p->exclMask|p->sharedMask)<(1<<ofst))
  ));

#endif











  mask = (1<<(ofst+n)) - (1<<ofst);





  assert( n>1 || mask==(1<<ofst) );


























  sqlite3_mutex_enter(pShmNode->pShmMutex);

  assert( assertLockingArrayOk(pShmNode) );

  if( flags & SQLITE_SHM_UNLOCK ){
    if( (p->exclMask|p->sharedMask) & mask ){
      int ii;

      int bUnlock = 1;










      for(ii=ofst; ii<ofst+n; ii++){
        if( aLock[ii]>((p->sharedMask & (1<<ii)) ? 1 : 0) ){
          bUnlock = 0;


        }
      }

      if( bUnlock ){
        rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n);
        if( rc==SQLITE_OK ){
          memset(&aLock[ofst], 0, sizeof(int)*n);
        }
      }else if( ALWAYS(p->sharedMask & (1<<ofst)) ){
        assert( n==1 && aLock[ofst]>1 );
        aLock[ofst]--;
      }

      /* Undo the local locks */
      if( rc==SQLITE_OK ){
        p->exclMask &= ~mask;
        p->sharedMask &= ~mask;
      }
    }
  }else if( flags & SQLITE_SHM_SHARED ){

    assert( n==1 );
    assert( (p->exclMask & (1<<ofst))==0 );
    if( (p->sharedMask & mask)==0 ){
      if( aLock[ofst]<0 ){

        rc = SQLITE_BUSY;
      }else if( aLock[ofst]==0 ){
        rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n);
      }

      /* Get the local shared locks */
      if( rc==SQLITE_OK ){
        p->sharedMask |= mask;
        aLock[ofst]++;
      }



    }



  }else{
    /* Make sure no sibling connections hold locks that will block this
    ** lock.  If any do, return SQLITE_BUSY right away.  */
    int ii;
    for(ii=ofst; ii<ofst+n; ii++){
      assert( (p->sharedMask & mask)==0 );
      if( ALWAYS((p->exclMask & (1<<ii))==0) && aLock[ii] ){
        rc = SQLITE_BUSY;
        break;
      }
    }

    /* Get the exclusive locks at the system level. Then if successful
    ** also update the in-memory values. */
    if( rc==SQLITE_OK ){
      rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n);
      if( rc==SQLITE_OK ){
        assert( (p->sharedMask & mask)==0 );
        p->exclMask |= mask;
        for(ii=ofst; ii<ofst+n; ii++){
          aLock[ii] = -1;
        }
      }
    }
  }
  assert( assertLockingArrayOk(pShmNode) );









  sqlite3_mutex_leave(pShmNode->pShmMutex);



  OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n",
           p->id, osGetpid(0), p->sharedMask, p->exclMask));
  return rc;
}

/*
** Implement a memory barrier or memory fence on shared memory.







>
>
|
|
|
|
|
|
>


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

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

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

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

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







43010
43011
43012
43013
43014
43015
43016
43017
43018
43019
43020
43021
43022
43023
43024
43025
43026
43027
43028
43029
43030
43031
43032
43033
43034
43035
43036
43037
43038
43039
43040
43041
43042
43043
43044
43045
43046
43047
43048
43049
43050
43051
43052
43053
43054
43055
43056
43057
43058
43059
43060
43061
43062
43063
43064
43065
43066
43067
43068
43069
43070
43071
43072
43073
43074
43075


43076
43077
43078
43079
43080
43081
43082
43083
43084
43085
43086
43087
43088
43089
43090
43091
43092
43093
43094
43095
43096
43097
43098
43099

43100






43101

43102
43103
43104
43105
43106


43107
43108
43109
43110
43111
43112
43113
43114
43115
43116
43117
43118
43119
43120
43121
43122
43123
43124
43125
43126
43127
43128

43129

43130
43131
43132
43133
43134
43135
43136
43137
43138
43139
43140

43141
43142
43143
43144
43145
43146
43147
43148
43149
43150
43151
43152
43153
43154
43155
43156
43157
43158
43159
43160
43161
43162
43163
43164
43165
43166
43167
43168
  ** In other words, if this is a blocking lock, none of the locks that
  ** occur later in the above list than the lock being obtained may be
  ** held.
  **
  ** It is not permitted to block on the RECOVER lock.
  */
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  {
    u16 lockMask = (p->exclMask|p->sharedMask);
    assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
          (ofst!=2)                                   /* not RECOVER */
       && (ofst!=1 || lockMask==0 || lockMask==2)
       && (ofst!=0 || lockMask<3)
       && (ofst<3  || lockMask<(1<<ofst))
    ));
  }
#endif

  /* Check if there is any work to do. There are three cases:
  **
  **    a) An unlock operation where there are locks to unlock,
  **    b) An shared lock where the requested lock is not already held
  **    c) An exclusive lock where the requested lock is not already held
  **
  ** The SQLite core never requests an exclusive lock that it already holds.
  ** This is assert()ed below.
  */
  assert( flags!=(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK)
       || 0==(p->exclMask & mask)
  );
  if( ((flags & SQLITE_SHM_UNLOCK) && ((p->exclMask|p->sharedMask) & mask))
   || (flags==(SQLITE_SHM_SHARED|SQLITE_SHM_LOCK) && 0==(p->sharedMask & mask))
   || (flags==(SQLITE_SHM_EXCLUSIVE|SQLITE_SHM_LOCK))
  ){

    /* Take the required mutexes. In SETLK_TIMEOUT mode (blocking locks), if
    ** this is an attempt on an exclusive lock use sqlite3_mutex_try(). If any
    ** other thread is holding this mutex, then it is either holding or about
    ** to hold a lock exclusive to the one being requested, and we may
    ** therefore return SQLITE_BUSY to the caller.
    **
    ** Doing this prevents some deadlock scenarios. For example, thread 1 may
    ** be a checkpointer blocked waiting on the WRITER lock. And thread 2
    ** may be a normal SQL client upgrading to a write transaction. In this
    ** case thread 2 does a non-blocking request for the WRITER lock. But -
    ** if it were to use sqlite3_mutex_enter() then it would effectively
    ** become a (doomed) blocking request, as thread 2 would block until thread
    ** 1 obtained WRITER and released the mutex. Since thread 2 already holds
    ** a lock on a read-locking slot at this point, this breaks the
    ** anti-deadlock rules (see above).  */
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
    int iMutex;
    for(iMutex=ofst; iMutex<ofst+n; iMutex++){
      if( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) ){
        rc = sqlite3_mutex_try(pShmNode->aMutex[iMutex]);
        if( rc!=SQLITE_OK ) goto leave_shmnode_mutexes;
      }else{
        sqlite3_mutex_enter(pShmNode->aMutex[iMutex]);
      }
    }
#else
    sqlite3_mutex_enter(pShmNode->pShmMutex);
#endif

    if( ALWAYS(rc==SQLITE_OK) ){
      if( flags & SQLITE_SHM_UNLOCK ){


        /* Case (a) - unlock.  */
        int bUnlock = 1;
        assert( (p->exclMask & p->sharedMask)==0 );
        assert( !(flags & SQLITE_SHM_EXCLUSIVE) || (p->exclMask & mask)==mask );
        assert( !(flags & SQLITE_SHM_SHARED) || (p->sharedMask & mask)==mask );

        /* If this is a SHARED lock being unlocked, it is possible that other
        ** clients within this process are holding the same SHARED lock. In
        ** this case, set bUnlock to 0 so that the posix lock is not removed
        ** from the file-descriptor below.  */
        if( flags & SQLITE_SHM_SHARED ){
          assert( n==1 );
          assert( aLock[ofst]>=1 );
          if( aLock[ofst]>1 ){
            bUnlock = 0;
            aLock[ofst]--;
            p->sharedMask &= ~mask;
          }
        }

        if( bUnlock ){
          rc = unixShmSystemLock(pDbFd, F_UNLCK, ofst+UNIX_SHM_BASE, n);
          if( rc==SQLITE_OK ){
            memset(&aLock[ofst], 0, sizeof(int)*n);

            p->sharedMask &= ~mask;






            p->exclMask &= ~mask;

          }
        }
      }else if( flags & SQLITE_SHM_SHARED ){
        /* Case (b) - a shared lock.  */



        if( aLock[ofst]<0 ){
          /* An exclusive lock is held by some other connection. BUSY. */
          rc = SQLITE_BUSY;
        }else if( aLock[ofst]==0 ){
          rc = unixShmSystemLock(pDbFd, F_RDLCK, ofst+UNIX_SHM_BASE, n);
        }

        /* Get the local shared locks */
        if( rc==SQLITE_OK ){
          p->sharedMask |= mask;
          aLock[ofst]++;
        }
      }else{
        /* Case (c) - an exclusive lock.  */
        int ii;

        assert( flags==(SQLITE_SHM_LOCK|SQLITE_SHM_EXCLUSIVE) );
        assert( (p->sharedMask & mask)==0 );
        assert( (p->exclMask & mask)==0 );

        /* Make sure no sibling connections hold locks that will block this
        ** lock.  If any do, return SQLITE_BUSY right away.  */

        for(ii=ofst; ii<ofst+n; ii++){

          if( aLock[ii] ){
            rc = SQLITE_BUSY;
            break;
          }
        }

        /* Get the exclusive locks at the system level. Then if successful
        ** also update the in-memory values. */
        if( rc==SQLITE_OK ){
          rc = unixShmSystemLock(pDbFd, F_WRLCK, ofst+UNIX_SHM_BASE, n);
          if( rc==SQLITE_OK ){

            p->exclMask |= mask;
            for(ii=ofst; ii<ofst+n; ii++){
              aLock[ii] = -1;
            }
          }
        }
      }
      assert( assertLockingArrayOk(pShmNode) );
    }

    /* Drop the mutexes acquired above. */
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  leave_shmnode_mutexes:
    for(iMutex--; iMutex>=ofst; iMutex--){
      sqlite3_mutex_leave(pShmNode->aMutex[iMutex]);
    }
#else
    sqlite3_mutex_leave(pShmNode->pShmMutex);
#endif
  }

  OSTRACE(("SHM-LOCK shmid-%d, pid-%d got %03x,%03x\n",
           p->id, osGetpid(0), p->sharedMask, p->exclMask));
  return rc;
}

/*
** Implement a memory barrier or memory fence on shared memory.
43146
43147
43148
43149
43150
43151
43152





43153
43154
43155
43156
43157
43158
43159
43160
43161
43162
43163
43164
#if SQLITE_MAX_MMAP_SIZE>0
  unixFile *pFd = (unixFile *)fd;   /* The underlying database file */
#endif
  *pp = 0;

#if SQLITE_MAX_MMAP_SIZE>0
  if( pFd->mmapSizeMax>0 ){





    if( pFd->pMapRegion==0 ){
      int rc = unixMapfile(pFd, -1);
      if( rc!=SQLITE_OK ) return rc;
    }
    if( pFd->mmapSize >= iOff+nAmt ){
      *pp = &((u8 *)pFd->pMapRegion)[iOff];
      pFd->nFetchOut++;
    }
  }
#endif
  return SQLITE_OK;
}







>
>
>
>
>




|







43404
43405
43406
43407
43408
43409
43410
43411
43412
43413
43414
43415
43416
43417
43418
43419
43420
43421
43422
43423
43424
43425
43426
43427
#if SQLITE_MAX_MMAP_SIZE>0
  unixFile *pFd = (unixFile *)fd;   /* The underlying database file */
#endif
  *pp = 0;

#if SQLITE_MAX_MMAP_SIZE>0
  if( pFd->mmapSizeMax>0 ){
    /* Ensure that there is always at least a 256 byte buffer of addressable
    ** memory following the returned page. If the database is corrupt,
    ** SQLite may overread the page slightly (in practice only a few bytes,
    ** but 256 is safe, round, number).  */
    const int nEofBuffer = 256;
    if( pFd->pMapRegion==0 ){
      int rc = unixMapfile(pFd, -1);
      if( rc!=SQLITE_OK ) return rc;
    }
    if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){
      *pp = &((u8 *)pFd->pMapRegion)[iOff];
      pFd->nFetchOut++;
    }
  }
#endif
  return SQLITE_OK;
}
50503
50504
50505
50506
50507
50508
50509





50510
50511
50512
50513
50514
50515
50516
50517
50518
50519
50520
50521
50522
50523
50524
50525
  *pp = 0;

  OSTRACE(("FETCH pid=%lu, pFile=%p, offset=%lld, amount=%d, pp=%p\n",
           osGetCurrentProcessId(), fd, iOff, nAmt, pp));

#if SQLITE_MAX_MMAP_SIZE>0
  if( pFd->mmapSizeMax>0 ){





    if( pFd->pMapRegion==0 ){
      int rc = winMapfile(pFd, -1);
      if( rc!=SQLITE_OK ){
        OSTRACE(("FETCH pid=%lu, pFile=%p, rc=%s\n",
                 osGetCurrentProcessId(), pFd, sqlite3ErrName(rc)));
        return rc;
      }
    }
    if( pFd->mmapSize >= iOff+nAmt ){
      assert( pFd->pMapRegion!=0 );
      *pp = &((u8 *)pFd->pMapRegion)[iOff];
      pFd->nFetchOut++;
    }
  }
#endif








>
>
>
>
>








|







50766
50767
50768
50769
50770
50771
50772
50773
50774
50775
50776
50777
50778
50779
50780
50781
50782
50783
50784
50785
50786
50787
50788
50789
50790
50791
50792
50793
  *pp = 0;

  OSTRACE(("FETCH pid=%lu, pFile=%p, offset=%lld, amount=%d, pp=%p\n",
           osGetCurrentProcessId(), fd, iOff, nAmt, pp));

#if SQLITE_MAX_MMAP_SIZE>0
  if( pFd->mmapSizeMax>0 ){
    /* Ensure that there is always at least a 256 byte buffer of addressable
    ** memory following the returned page. If the database is corrupt,
    ** SQLite may overread the page slightly (in practice only a few bytes,
    ** but 256 is safe, round, number).  */
    const int nEofBuffer = 256;
    if( pFd->pMapRegion==0 ){
      int rc = winMapfile(pFd, -1);
      if( rc!=SQLITE_OK ){
        OSTRACE(("FETCH pid=%lu, pFile=%p, rc=%s\n",
                 osGetCurrentProcessId(), pFd, sqlite3ErrName(rc)));
        return rc;
      }
    }
    if( pFd->mmapSize >= (iOff+nAmt+nEofBuffer) ){
      assert( pFd->pMapRegion!=0 );
      *pp = &((u8 *)pFd->pMapRegion)[iOff];
      pFd->nFetchOut++;
    }
  }
#endif

57114
57115
57116
57117
57118
57119
57120
57121
57122
57123
57124
57125
57126
57127
57128
  Pgno lckPgno;               /* Page number for the locking page */
  i64 pageSize;               /* Number of bytes in a page */
  i64 journalSizeLimit;       /* Size limit for persistent journal files */
  char *zFilename;            /* Name of the database file */
  char *zJournal;             /* Name of the journal file */
  int (*xBusyHandler)(void*); /* Function to call when busy */
  void *pBusyHandlerArg;      /* Context argument for xBusyHandler */
  int aStat[4];               /* Total cache hits, misses, writes, spills */
#ifdef SQLITE_TEST
  int nRead;                  /* Database pages read */
#endif
  void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */
  int (*xGet)(Pager*,Pgno,DbPage**,int); /* Routine to fetch a patch */
  char *pTmpSpace;            /* Pager.pageSize bytes of space for tmp use */
  PCache *pPCache;            /* Pointer to page cache object */







|







57382
57383
57384
57385
57386
57387
57388
57389
57390
57391
57392
57393
57394
57395
57396
  Pgno lckPgno;               /* Page number for the locking page */
  i64 pageSize;               /* Number of bytes in a page */
  i64 journalSizeLimit;       /* Size limit for persistent journal files */
  char *zFilename;            /* Name of the database file */
  char *zJournal;             /* Name of the journal file */
  int (*xBusyHandler)(void*); /* Function to call when busy */
  void *pBusyHandlerArg;      /* Context argument for xBusyHandler */
  u32 aStat[4];               /* Total cache hits, misses, writes, spills */
#ifdef SQLITE_TEST
  int nRead;                  /* Database pages read */
#endif
  void (*xReiniter)(DbPage*); /* Call this routine when reloading pages */
  int (*xGet)(Pager*,Pgno,DbPage**,int); /* Routine to fetch a patch */
  char *pTmpSpace;            /* Pager.pageSize bytes of space for tmp use */
  PCache *pPCache;            /* Pointer to page cache object */
57244
57245
57246
57247
57248
57249
57250
57251
57252
57253
57254
57255
57256
57257
57258
57259
57260
*/
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;
    int rc;
    rc = sqlite3WalFindFrame(pPager->pWal, pgno, &iRead);
    return (rc==SQLITE_OK && iRead==0);
  }
#endif
  return 1;
}
#endif

#ifndef SQLITE_OMIT_WAL







<
|
|







57512
57513
57514
57515
57516
57517
57518

57519
57520
57521
57522
57523
57524
57525
57526
57527
*/
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
63258
63259
63260
63261
63262
63263
63264
63265
63266
63267
63268
63269
63270
63271
63272
63273
63274
63275
63276
63277
63278
63279
63280
63281
63282
63283
63284
63285
63286
63287
63288
63289
63290
63291
63292
  static int a[11];
  a[0] = sqlite3PcacheRefCount(pPager->pPCache);
  a[1] = sqlite3PcachePagecount(pPager->pPCache);
  a[2] = sqlite3PcacheGetCachesize(pPager->pPCache);
  a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize;
  a[4] = pPager->eState;
  a[5] = pPager->errCode;
  a[6] = pPager->aStat[PAGER_STAT_HIT];
  a[7] = pPager->aStat[PAGER_STAT_MISS];
  a[8] = 0;  /* Used to be pPager->nOvfl */
  a[9] = pPager->nRead;
  a[10] = pPager->aStat[PAGER_STAT_WRITE];
  return a;
}
#endif

/*
** Parameter eStat must be one of SQLITE_DBSTATUS_CACHE_HIT, _MISS, _WRITE,
** or _WRITE+1.  The SQLITE_DBSTATUS_CACHE_WRITE+1 case is a translation
** of SQLITE_DBSTATUS_CACHE_SPILL.  The _SPILL case is not contiguous because
** it was added later.
**
** Before returning, *pnVal is incremented by the
** current cache hit or miss count, according to the value of eStat. If the
** reset parameter is non-zero, the cache hit or miss count is zeroed before
** returning.
*/
SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, int *pnVal){

  assert( eStat==SQLITE_DBSTATUS_CACHE_HIT
       || eStat==SQLITE_DBSTATUS_CACHE_MISS
       || eStat==SQLITE_DBSTATUS_CACHE_WRITE
       || eStat==SQLITE_DBSTATUS_CACHE_WRITE+1
  );








|
|


|















|







63525
63526
63527
63528
63529
63530
63531
63532
63533
63534
63535
63536
63537
63538
63539
63540
63541
63542
63543
63544
63545
63546
63547
63548
63549
63550
63551
63552
63553
63554
63555
63556
63557
63558
63559
  static int a[11];
  a[0] = sqlite3PcacheRefCount(pPager->pPCache);
  a[1] = sqlite3PcachePagecount(pPager->pPCache);
  a[2] = sqlite3PcacheGetCachesize(pPager->pPCache);
  a[3] = pPager->eState==PAGER_OPEN ? -1 : (int) pPager->dbSize;
  a[4] = pPager->eState;
  a[5] = pPager->errCode;
  a[6] = (int)pPager->aStat[PAGER_STAT_HIT] & 0x7fffffff;
  a[7] = (int)pPager->aStat[PAGER_STAT_MISS] & 0x7fffffff;
  a[8] = 0;  /* Used to be pPager->nOvfl */
  a[9] = pPager->nRead;
  a[10] = (int)pPager->aStat[PAGER_STAT_WRITE] & 0x7fffffff;
  return a;
}
#endif

/*
** Parameter eStat must be one of SQLITE_DBSTATUS_CACHE_HIT, _MISS, _WRITE,
** or _WRITE+1.  The SQLITE_DBSTATUS_CACHE_WRITE+1 case is a translation
** of SQLITE_DBSTATUS_CACHE_SPILL.  The _SPILL case is not contiguous because
** it was added later.
**
** Before returning, *pnVal is incremented by the
** current cache hit or miss count, according to the value of eStat. If the
** reset parameter is non-zero, the cache hit or miss count is zeroed before
** returning.
*/
SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *pPager, int eStat, int reset, u64 *pnVal){

  assert( eStat==SQLITE_DBSTATUS_CACHE_HIT
       || eStat==SQLITE_DBSTATUS_CACHE_MISS
       || eStat==SQLITE_DBSTATUS_CACHE_WRITE
       || eStat==SQLITE_DBSTATUS_CACHE_WRITE+1
  );

64218
64219
64220
64221
64222
64223
64224
64225
64226
64227
64228
64229
64230
64231
64232
*/
SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){
  assert( pPager->eState>=PAGER_READER );
  return sqlite3WalFramesize(pPager->pWal);
}
#endif

#ifdef SQLITE_USE_SEH
SQLITE_PRIVATE int sqlite3PagerWalSystemErrno(Pager *pPager){
  return sqlite3WalSystemErrno(pPager->pWal);
}
#endif

#endif /* SQLITE_OMIT_DISKIO */








|







64485
64486
64487
64488
64489
64490
64491
64492
64493
64494
64495
64496
64497
64498
64499
*/
SQLITE_PRIVATE int sqlite3PagerWalFramesize(Pager *pPager){
  assert( pPager->eState>=PAGER_READER );
  return sqlite3WalFramesize(pPager->pWal);
}
#endif

#if defined(SQLITE_USE_SEH) && !defined(SQLITE_OMIT_WAL)
SQLITE_PRIVATE int sqlite3PagerWalSystemErrno(Pager *pPager){
  return sqlite3WalSystemErrno(pPager->pWal);
}
#endif

#endif /* SQLITE_OMIT_DISKIO */

66234
66235
66236
66237
66238
66239
66240













66241
66242
66243
66244
66245
66246
66247
66248
66249
66250
66251
66252
66253
66254
66255
66256
66257
66258
66259
66260
66261
66262
66263
    p = 0;
  }
  *pp = p;
  return rc;
}

#ifdef SQLITE_ENABLE_SETLK_TIMEOUT













/*
** Attempt to enable blocking locks. Blocking locks are enabled only if (a)
** they are supported by the VFS, and (b) the database handle is configured
** with a busy-timeout. Return 1 if blocking locks are successfully enabled,
** or 0 otherwise.
*/
static int walEnableBlocking(Wal *pWal){
  int res = 0;
  if( pWal->db ){
    int tmout = pWal->db->busyTimeout;
    if( tmout ){
      int rc;
      rc = sqlite3OsFileControl(
          pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout
      );
      res = (rc==SQLITE_OK);
    }
  }
  return res;
}

/*
** Disable blocking locks.







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











<
<
<
<
|







66501
66502
66503
66504
66505
66506
66507
66508
66509
66510
66511
66512
66513
66514
66515
66516
66517
66518
66519
66520
66521
66522
66523
66524
66525
66526
66527
66528
66529
66530
66531




66532
66533
66534
66535
66536
66537
66538
66539
    p = 0;
  }
  *pp = p;
  return rc;
}

#ifdef SQLITE_ENABLE_SETLK_TIMEOUT


/*
** Attempt to enable blocking locks that block for nMs ms. Return 1 if
** blocking locks are successfully enabled, or 0 otherwise.
*/
static int walEnableBlockingMs(Wal *pWal, int nMs){
  int rc = sqlite3OsFileControl(
      pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&nMs
  );
  return (rc==SQLITE_OK);
}

/*
** Attempt to enable blocking locks. Blocking locks are enabled only if (a)
** they are supported by the VFS, and (b) the database handle is configured
** with a busy-timeout. Return 1 if blocking locks are successfully enabled,
** or 0 otherwise.
*/
static int walEnableBlocking(Wal *pWal){
  int res = 0;
  if( pWal->db ){
    int tmout = pWal->db->busyTimeout;
    if( tmout ){




      res = walEnableBlockingMs(pWal, tmout);
    }
  }
  return res;
}

/*
** Disable blocking locks.
66298
66299
66300
66301
66302
66303
66304
66305
66306
66307
66308
66309
66310
66311
66312
66313
66314
66315
66316
66317
66318
66319
66320
66321
66322
66323
66324
66325
/*
** Set the database handle used to determine if blocking locks are required.
*/
SQLITE_PRIVATE void sqlite3WalDb(Wal *pWal, sqlite3 *db){
  pWal->db = db;
}

/*
** Take an exclusive WRITE lock. Blocking if so configured.
*/
static int walLockWriter(Wal *pWal){
  int rc;
  walEnableBlocking(pWal);
  rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1);
  walDisableBlocking(pWal);
  return rc;
}
#else
# define walEnableBlocking(x) 0
# define walDisableBlocking(x)
# define walLockWriter(pWal) walLockExclusive((pWal), WAL_WRITE_LOCK, 1)
# define sqlite3WalDb(pWal, db)
#endif   /* ifdef SQLITE_ENABLE_SETLK_TIMEOUT */


/*
** Attempt to obtain the exclusive WAL lock defined by parameters lockIdx and
** n. If the attempt fails and parameter xBusy is not NULL, then it is a







<
<
<
<
<
<
<
<
<
<



|







66574
66575
66576
66577
66578
66579
66580










66581
66582
66583
66584
66585
66586
66587
66588
66589
66590
66591
/*
** Set the database handle used to determine if blocking locks are required.
*/
SQLITE_PRIVATE void sqlite3WalDb(Wal *pWal, sqlite3 *db){
  pWal->db = db;
}











#else
# define walEnableBlocking(x) 0
# define walDisableBlocking(x)
# define walEnableBlockingMs(pWal, ms) 0
# define sqlite3WalDb(pWal, db)
#endif   /* ifdef SQLITE_ENABLE_SETLK_TIMEOUT */


/*
** Attempt to obtain the exclusive WAL lock defined by parameters lockIdx and
** n. If the attempt fails and parameter xBusy is not NULL, then it is a
66912
66913
66914
66915
66916
66917
66918
66919


66920
66921
66922
66923
66924
66925
66926
66927

66928
66929
66930
66931
66932
66933
66934
    if( pWal->bShmUnreliable==0 && (pWal->readOnly & WAL_SHM_RDONLY) ){
      if( SQLITE_OK==(rc = walLockShared(pWal, WAL_WRITE_LOCK)) ){
        walUnlockShared(pWal, WAL_WRITE_LOCK);
        rc = SQLITE_READONLY_RECOVERY;
      }
    }else{
      int bWriteLock = pWal->writeLock;
      if( bWriteLock || SQLITE_OK==(rc = walLockWriter(pWal)) ){


        pWal->writeLock = 1;
        if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){
          badHdr = walIndexTryHdr(pWal, pChanged);
          if( badHdr ){
            /* If the wal-index header is still malformed even while holding
            ** a WRITE lock, it can only mean that the header is corrupted and
            ** needs to be reconstructed.  So run recovery to do exactly that.
            */

            rc = walIndexRecover(pWal);
            *pChanged = 1;
          }
        }
        if( bWriteLock==0 ){
          pWal->writeLock = 0;
          walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);







|
>
>







|
>







67178
67179
67180
67181
67182
67183
67184
67185
67186
67187
67188
67189
67190
67191
67192
67193
67194
67195
67196
67197
67198
67199
67200
67201
67202
67203
    if( pWal->bShmUnreliable==0 && (pWal->readOnly & WAL_SHM_RDONLY) ){
      if( SQLITE_OK==(rc = walLockShared(pWal, WAL_WRITE_LOCK)) ){
        walUnlockShared(pWal, WAL_WRITE_LOCK);
        rc = SQLITE_READONLY_RECOVERY;
      }
    }else{
      int bWriteLock = pWal->writeLock;
      if( bWriteLock
       || SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1))
      ){
        pWal->writeLock = 1;
        if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){
          badHdr = walIndexTryHdr(pWal, pChanged);
          if( badHdr ){
            /* If the wal-index header is still malformed even while holding
            ** a WRITE lock, it can only mean that the header is corrupted and
            ** needs to be reconstructed.  So run recovery to do exactly that.
            ** Disable blocking locks first.  */
            walDisableBlocking(pWal);
            rc = walIndexRecover(pWal);
            *pChanged = 1;
          }
        }
        if( bWriteLock==0 ){
          pWal->writeLock = 0;
          walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
67130
67131
67132
67133
67134
67135
67136































67137
67138
67139
67140
67141
67142
67143
    pWal->bShmUnreliable = 0;
    sqlite3WalEndReadTransaction(pWal);
    *pChanged = 1;
  }
  return rc;
}
































/*
** Attempt to start a read transaction.  This might fail due to a race or
** other transient condition.  When that happens, it returns WAL_RETRY to
** indicate to the caller that it is safe to retry immediately.
**
** On success return SQLITE_OK.  On a permanent failure (such an
** I/O error or an SQLITE_BUSY because another process is running







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







67399
67400
67401
67402
67403
67404
67405
67406
67407
67408
67409
67410
67411
67412
67413
67414
67415
67416
67417
67418
67419
67420
67421
67422
67423
67424
67425
67426
67427
67428
67429
67430
67431
67432
67433
67434
67435
67436
67437
67438
67439
67440
67441
67442
67443
    pWal->bShmUnreliable = 0;
    sqlite3WalEndReadTransaction(pWal);
    *pChanged = 1;
  }
  return rc;
}

/*
** The final argument passed to walTryBeginRead() is of type (int*). The
** caller should invoke walTryBeginRead as follows:
**
**   int cnt = 0;
**   do {
**     rc = walTryBeginRead(..., &cnt);
**   }while( rc==WAL_RETRY );
**
** The final value of "cnt" is of no use to the caller. It is used by
** the implementation of walTryBeginRead() as follows:
**
**   + Each time walTryBeginRead() is called, it is incremented. Once
**     it reaches WAL_RETRY_PROTOCOL_LIMIT - indicating that walTryBeginRead()
**     has many times been invoked and failed with WAL_RETRY - walTryBeginRead()
**     returns SQLITE_PROTOCOL.
**
**   + If SQLITE_ENABLE_SETLK_TIMEOUT is defined and walTryBeginRead() failed
**     because a blocking lock timed out (SQLITE_BUSY_TIMEOUT from the OS
**     layer), the WAL_RETRY_BLOCKED_MASK bit is set in "cnt". In this case
**     the next invocation of walTryBeginRead() may omit an expected call to
**     sqlite3OsSleep(). There has already been a delay when the previous call
**     waited on a lock.
*/
#define WAL_RETRY_PROTOCOL_LIMIT 100
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
# define WAL_RETRY_BLOCKED_MASK    0x10000000
#else
# define WAL_RETRY_BLOCKED_MASK    0
#endif

/*
** Attempt to start a read transaction.  This might fail due to a race or
** other transient condition.  When that happens, it returns WAL_RETRY to
** indicate to the caller that it is safe to retry immediately.
**
** On success return SQLITE_OK.  On a permanent failure (such an
** I/O error or an SQLITE_BUSY because another process is running
67180
67181
67182
67183
67184
67185
67186
67187
67188
67189
67190
67191
67192
67193



67194
67195
67196
67197
67198
67199
67200
** This routine uses the nBackfill and aReadMark[] fields of the header
** to select a particular WAL_READ_LOCK() that strives to let the
** 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 cnt){
  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 */




  assert( pWal->readLock<0 );     /* Not currently locked */

  /* useWal may only be set for read/write connections */
  assert( (pWal->readOnly & WAL_SHM_RDONLY)==0 || useWal==0 );

  /* Take steps to avoid spinning forever if there is a protocol error.







|






>
>
>







67480
67481
67482
67483
67484
67485
67486
67487
67488
67489
67490
67491
67492
67493
67494
67495
67496
67497
67498
67499
67500
67501
67502
67503
** This routine uses the nBackfill and aReadMark[] fields of the header
** to select a particular WAL_READ_LOCK() that strives to let the
** 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 */
  assert( (pWal->readOnly & WAL_SHM_RDONLY)==0 || useWal==0 );

  /* Take steps to avoid spinning forever if there is a protocol error.
67210
67211
67212
67213
67214
67215
67216

67217
67218

67219
67220
67221
67222
67223

















67224

67225
67226
67227
67228
67229
67230
67231







67232
67233
67234
67235
67236
67237
67238
  ** After 5 RETRYs, we begin calling sqlite3OsSleep().  The first few
  ** calls to sqlite3OsSleep() have a delay of 1 microsecond.  Really this
  ** is more of a scheduler yield than an actual delay.  But on the 10th
  ** an subsequent retries, the delays start becoming longer and longer,
  ** so that on the 100th (and last) RETRY we delay for 323 milliseconds.
  ** The total delay time before giving up is less than 10 seconds.
  */

  if( cnt>5 ){
    int nDelay = 1;                      /* Pause time in microseconds */

    if( cnt>100 ){
      VVA_ONLY( pWal->lockError = 1; )
      return SQLITE_PROTOCOL;
    }
    if( cnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39;

















    sqlite3OsSleep(pWal->pVfs, nDelay);

  }

  if( !useWal ){
    assert( rc==SQLITE_OK );
    if( pWal->bShmUnreliable==0 ){
      rc = walIndexReadHdr(pWal, pChanged);
    }







    if( rc==SQLITE_BUSY ){
      /* If there is not a recovery running in another thread or process
      ** then convert BUSY errors to WAL_RETRY.  If recovery is known to
      ** be running, convert BUSY to BUSY_RECOVERY.  There is a race here
      ** which might cause WAL_RETRY to be returned even if BUSY_RECOVERY
      ** would be technically correct.  But the race is benign since with
      ** WAL_RETRY this routine will be called again and will probably be







>
|

>
|



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

>







>
>
>
>
>
>
>







67513
67514
67515
67516
67517
67518
67519
67520
67521
67522
67523
67524
67525
67526
67527
67528
67529
67530
67531
67532
67533
67534
67535
67536
67537
67538
67539
67540
67541
67542
67543
67544
67545
67546
67547
67548
67549
67550
67551
67552
67553
67554
67555
67556
67557
67558
67559
67560
67561
67562
67563
67564
67565
67566
67567
67568
  ** After 5 RETRYs, we begin calling sqlite3OsSleep().  The first few
  ** calls to sqlite3OsSleep() have a delay of 1 microsecond.  Really this
  ** is more of a scheduler yield than an actual delay.  But on the 10th
  ** an subsequent retries, the delays start becoming longer and longer,
  ** so that on the 100th (and last) RETRY we delay for 323 milliseconds.
  ** The total delay time before giving up is less than 10 seconds.
  */
  (*pCnt)++;
  if( *pCnt>5 ){
    int nDelay = 1;                      /* Pause time in microseconds */
    int cnt = (*pCnt & ~WAL_RETRY_BLOCKED_MASK);
    if( cnt>WAL_RETRY_PROTOCOL_LIMIT ){
      VVA_ONLY( pWal->lockError = 1; )
      return SQLITE_PROTOCOL;
    }
    if( *pCnt>=10 ) nDelay = (cnt-9)*(cnt-9)*39;
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
    /* In SQLITE_ENABLE_SETLK_TIMEOUT builds, configure the file-descriptor
    ** to block for locks for approximately nDelay us. This affects three
    ** locks: (a) the shared lock taken on the DMS slot in os_unix.c (if
    ** using os_unix.c), (b) the WRITER lock taken in walIndexReadHdr() if the
    ** first attempted read fails, and (c) the shared lock taken on the
    ** read-mark.
    **
    ** If the previous call failed due to an SQLITE_BUSY_TIMEOUT error,
    ** then sleep for the minimum of 1us. The previous call already provided
    ** an extra delay while it was blocking on the lock.
    */
    nBlockTmout = (nDelay+998) / 1000;
    if( !useWal && walEnableBlockingMs(pWal, nBlockTmout) ){
      if( *pCnt & WAL_RETRY_BLOCKED_MASK ) nDelay = 1;
    }
#endif
    sqlite3OsSleep(pWal->pVfs, nDelay);
    *pCnt &= ~WAL_RETRY_BLOCKED_MASK;
  }

  if( !useWal ){
    assert( rc==SQLITE_OK );
    if( pWal->bShmUnreliable==0 ){
      rc = walIndexReadHdr(pWal, pChanged);
    }
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
    walDisableBlocking(pWal);
    if( rc==SQLITE_BUSY_TIMEOUT ){
      rc = SQLITE_BUSY;
      *pCnt |= WAL_RETRY_BLOCKED_MASK;
    }
#endif
    if( rc==SQLITE_BUSY ){
      /* If there is not a recovery running in another thread or process
      ** then convert BUSY errors to WAL_RETRY.  If recovery is known to
      ** be running, convert BUSY to BUSY_RECOVERY.  There is a race here
      ** which might cause WAL_RETRY to be returned even if BUSY_RECOVERY
      ** would be technically correct.  But the race is benign since with
      ** WAL_RETRY this routine will be called again and will probably be
67339
67340
67341
67342
67343
67344
67345

67346

67347








67348
67349
67350
67351
67352
67353
67354
67355
    }
  }
  if( mxI==0 ){
    assert( rc==SQLITE_BUSY || (pWal->readOnly & WAL_SHM_RDONLY)!=0 );
    return rc==SQLITE_BUSY ? WAL_RETRY : SQLITE_READONLY_CANTINIT;
  }


  rc = walLockShared(pWal, WAL_READ_LOCK(mxI));

  if( rc ){








    return rc==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







>

>

>
>
>
>
>
>
>
>
|







67669
67670
67671
67672
67673
67674
67675
67676
67677
67678
67679
67680
67681
67682
67683
67684
67685
67686
67687
67688
67689
67690
67691
67692
67693
67694
67695
    }
  }
  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
67529
67530
67531
67532
67533
67534
67535
67536
67537
67538
67539
67540
67541
67542
67543
      return rc;
    }
    ckptLock = 1;
  }
#endif

  do{
    rc = walTryBeginRead(pWal, pChanged, 0, ++cnt);
  }while( rc==WAL_RETRY );
  testcase( (rc&0xff)==SQLITE_BUSY );
  testcase( (rc&0xff)==SQLITE_IOERR );
  testcase( rc==SQLITE_PROTOCOL );
  testcase( rc==SQLITE_OK );

#ifdef SQLITE_ENABLE_SNAPSHOT







|







67869
67870
67871
67872
67873
67874
67875
67876
67877
67878
67879
67880
67881
67882
67883
      return rc;
    }
    ckptLock = 1;
  }
#endif

  do{
    rc = walTryBeginRead(pWal, pChanged, 0, &cnt);
  }while( rc==WAL_RETRY );
  testcase( (rc&0xff)==SQLITE_BUSY );
  testcase( (rc&0xff)==SQLITE_IOERR );
  testcase( rc==SQLITE_PROTOCOL );
  testcase( rc==SQLITE_OK );

#ifdef SQLITE_ENABLE_SNAPSHOT
67710
67711
67712
67713
67714
67715
67716

67717
67718
67719
67720
67721
67722
67723
    while( (iH = AtomicLoad(&sLoc.aHash[iKey]))!=0 ){
      u32 iFrame = iH + sLoc.iZero;
      if( iFrame<=iLast && iFrame>=pWal->minFrame && sLoc.aPgno[iH-1]==pgno ){
        assert( iFrame>iRead || CORRUPT_DB );
        iRead = iFrame;
      }
      if( (nCollide--)==0 ){

        return SQLITE_CORRUPT_BKPT;
      }
      iKey = walNextHash(iKey);
    }
    if( iRead ) break;
  }








>







68050
68051
68052
68053
68054
68055
68056
68057
68058
68059
68060
68061
68062
68063
68064
    while( (iH = AtomicLoad(&sLoc.aHash[iKey]))!=0 ){
      u32 iFrame = iH + sLoc.iZero;
      if( iFrame<=iLast && iFrame>=pWal->minFrame && sLoc.aPgno[iH-1]==pgno ){
        assert( iFrame>iRead || CORRUPT_DB );
        iRead = iFrame;
      }
      if( (nCollide--)==0 ){
        *piRead = 0;
        return SQLITE_CORRUPT_BKPT;
      }
      iKey = walNextHash(iKey);
    }
    if( iRead ) break;
  }

68013
68014
68015
68016
68017
68018
68019
68020
68021
68022
68023
68024
68025
68026
68027
      }
    }
    walUnlockShared(pWal, WAL_READ_LOCK(0));
    pWal->readLock = -1;
    cnt = 0;
    do{
      int notUsed;
      rc = walTryBeginRead(pWal, &notUsed, 1, ++cnt);
    }while( rc==WAL_RETRY );
    assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */
    testcase( (rc&0xff)==SQLITE_IOERR );
    testcase( rc==SQLITE_PROTOCOL );
    testcase( rc==SQLITE_OK );
  }
  return rc;







|







68354
68355
68356
68357
68358
68359
68360
68361
68362
68363
68364
68365
68366
68367
68368
      }
    }
    walUnlockShared(pWal, WAL_READ_LOCK(0));
    pWal->readLock = -1;
    cnt = 0;
    do{
      int notUsed;
      rc = walTryBeginRead(pWal, &notUsed, 1, &cnt);
    }while( rc==WAL_RETRY );
    assert( (rc&0xff)!=SQLITE_BUSY ); /* BUSY not possible when useWal==1 */
    testcase( (rc&0xff)==SQLITE_IOERR );
    testcase( rc==SQLITE_PROTOCOL );
    testcase( rc==SQLITE_OK );
  }
  return rc;
68434
68435
68436
68437
68438
68439
68440
68441
68442
68443
68444
68445
68446
68447
68448
68449
68450
68451
  /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked
  ** in the SQLITE_CHECKPOINT_PASSIVE mode. */
  assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 );

  if( pWal->readOnly ) return SQLITE_READONLY;
  WALTRACE(("WAL%p: checkpoint begins\n", pWal));

  /* Enable blocking locks, if possible. If blocking locks are successfully
  ** enabled, set xBusy2=0 so that the busy-handler is never invoked. */
  sqlite3WalDb(pWal, db);
  (void)walEnableBlocking(pWal);

  /* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive
  ** "checkpoint" lock on the database file.
  ** EVIDENCE-OF: R-10421-19736 If any other process is running a
  ** checkpoint operation at the same time, the lock cannot be obtained and
  ** SQLITE_BUSY is returned.
  ** EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured,







|
<

|







68775
68776
68777
68778
68779
68780
68781
68782

68783
68784
68785
68786
68787
68788
68789
68790
68791
  /* EVIDENCE-OF: R-62920-47450 The busy-handler callback is never invoked
  ** in the SQLITE_CHECKPOINT_PASSIVE mode. */
  assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 );

  if( pWal->readOnly ) return SQLITE_READONLY;
  WALTRACE(("WAL%p: checkpoint begins\n", pWal));

  /* Enable blocking locks, if possible. */

  sqlite3WalDb(pWal, db);
  if( xBusy2 ) (void)walEnableBlocking(pWal);

  /* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive
  ** "checkpoint" lock on the database file.
  ** EVIDENCE-OF: R-10421-19736 If any other process is running a
  ** checkpoint operation at the same time, the lock cannot be obtained and
  ** SQLITE_BUSY is returned.
  ** EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured,
68478
68479
68480
68481
68482
68483
68484





68485
68486
68487
68488
68489
68490
68491
68492
68493
68494
    }
  }


  /* Read the wal-index header. */
  SEH_TRY {
    if( rc==SQLITE_OK ){





      walDisableBlocking(pWal);
      rc = walIndexReadHdr(pWal, &isChanged);
      (void)walEnableBlocking(pWal);
      if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){
        sqlite3OsUnfetch(pWal->pDbFd, 0, 0);
      }
    }

    /* Copy data from the log to the database file. */
    if( rc==SQLITE_OK ){







>
>
>
>
>


|







68818
68819
68820
68821
68822
68823
68824
68825
68826
68827
68828
68829
68830
68831
68832
68833
68834
68835
68836
68837
68838
68839
    }
  }


  /* Read the wal-index header. */
  SEH_TRY {
    if( rc==SQLITE_OK ){
      /* For a passive checkpoint, do not re-enable blocking locks after
      ** reading the wal-index header. A passive checkpoint should not block
      ** or invoke the busy handler. The only lock such a checkpoint may
      ** attempt to obtain is a lock on a read-slot, and it should give up
      ** immediately and do a partial checkpoint if it cannot obtain it. */
      walDisableBlocking(pWal);
      rc = walIndexReadHdr(pWal, &isChanged);
      if( eMode2!=SQLITE_CHECKPOINT_PASSIVE ) (void)walEnableBlocking(pWal);
      if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){
        sqlite3OsUnfetch(pWal->pDbFd, 0, 0);
      }
    }

    /* Copy data from the log to the database file. */
    if( rc==SQLITE_OK ){
68817
68818
68819
68820
68821
68822
68823
68824
68825
68826
68827
68828
68829
68830
68831
**     18       1     File format write version
**     19       1     File format read version
**     20       1     Bytes of unused space at the end of each page
**     21       1     Max embedded payload fraction (must be 64)
**     22       1     Min embedded payload fraction (must be 32)
**     23       1     Min leaf payload fraction (must be 32)
**     24       4     File change counter
**     28       4     Reserved for future use
**     32       4     First freelist page
**     36       4     Number of freelist pages in the file
**     40      60     15 4-byte meta values passed to higher layers
**
**     40       4     Schema cookie
**     44       4     File format of schema layer
**     48       4     Size of page cache







|







69162
69163
69164
69165
69166
69167
69168
69169
69170
69171
69172
69173
69174
69175
69176
**     18       1     File format write version
**     19       1     File format read version
**     20       1     Bytes of unused space at the end of each page
**     21       1     Max embedded payload fraction (must be 64)
**     22       1     Min embedded payload fraction (must be 32)
**     23       1     Min leaf payload fraction (must be 32)
**     24       4     File change counter
**     28       4     The size of the database in pages
**     32       4     First freelist page
**     36       4     Number of freelist pages in the file
**     40      60     15 4-byte meta values passed to higher layers
**
**     40       4     Schema cookie
**     44       4     File format of schema layer
**     48       4     Size of page cache
74944
74945
74946
74947
74948
74949
74950
74951
74952
74953
74954
74955
74956
74957
74958
        ){
          sqlite3_file *fd = sqlite3PagerFile(pBt->pPager);
          u8 aSave[4];
          u8 *aWrite = &pBuf[-4];
          assert( aWrite>=pBufStart );                         /* due to (6) */
          memcpy(aSave, aWrite, 4);
          rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1));
          if( rc && nextPage>pBt->nPage ) rc = SQLITE_CORRUPT_BKPT;
          nextPage = get4byte(aWrite);
          memcpy(aWrite, aSave, 4);
        }else
#endif

        {
          DbPage *pDbPage;







<







75289
75290
75291
75292
75293
75294
75295

75296
75297
75298
75299
75300
75301
75302
        ){
          sqlite3_file *fd = sqlite3PagerFile(pBt->pPager);
          u8 aSave[4];
          u8 *aWrite = &pBuf[-4];
          assert( aWrite>=pBufStart );                         /* due to (6) */
          memcpy(aSave, aWrite, 4);
          rc = sqlite3OsRead(fd, aWrite, a+4, (i64)pBt->pageSize*(nextPage-1));

          nextPage = get4byte(aWrite);
          memcpy(aWrite, aSave, 4);
        }else
#endif

        {
          DbPage *pDbPage;
76064
76065
76066
76067
76068
76069
76070

76071


76072
76073
76074
76075
76076
76077
76078
    if( CURSOR_SKIPNEXT==pCur->eState ){
      pCur->eState = CURSOR_VALID;
      if( pCur->skipNext<0 ) return SQLITE_OK;
    }
  }

  pPage = pCur->pPage;

  assert( pPage->isInit );


  if( !pPage->leaf ){
    int idx = pCur->ix;
    rc = moveToChild(pCur, get4byte(findCell(pPage, idx)));
    if( rc ) return rc;
    rc = moveToRightmost(pCur);
  }else{
    while( pCur->ix==0 ){







>
|
>
>







76408
76409
76410
76411
76412
76413
76414
76415
76416
76417
76418
76419
76420
76421
76422
76423
76424
76425
    if( CURSOR_SKIPNEXT==pCur->eState ){
      pCur->eState = CURSOR_VALID;
      if( pCur->skipNext<0 ) return SQLITE_OK;
    }
  }

  pPage = pCur->pPage;
  if( sqlite3FaultSim(412) ) pPage->isInit = 0;
  if( !pPage->isInit ){
    return SQLITE_CORRUPT_BKPT;
  }
  if( !pPage->leaf ){
    int idx = pCur->ix;
    rc = moveToChild(pCur, get4byte(findCell(pPage, idx)));
    if( rc ) return rc;
    rc = moveToRightmost(pCur);
  }else{
    while( pCur->ix==0 ){
85368
85369
85370
85371
85372
85373
85374




85375
85376
85377
85378
85379
85380
85381
      }
      break;
    }
    case P4_VTAB : {
      if( db->pnBytesFreed==0 ) sqlite3VtabUnlock((VTable *)p4);
      break;
    }




  }
}

/*
** Free the space allocated for aOp and any p4 values allocated for the
** opcodes contained within. If aOp is not NULL it is assumed to contain
** nOp entries.







>
>
>
>







85715
85716
85717
85718
85719
85720
85721
85722
85723
85724
85725
85726
85727
85728
85729
85730
85731
85732
      }
      break;
    }
    case P4_VTAB : {
      if( db->pnBytesFreed==0 ) sqlite3VtabUnlock((VTable *)p4);
      break;
    }
    case P4_TABLEREF: {
      if( db->pnBytesFreed==0 ) sqlite3DeleteTable(db, (Table*)p4);
      break;
    }
  }
}

/*
** Free the space allocated for aOp and any p4 values allocated for the
** opcodes contained within. If aOp is not NULL it is assumed to contain
** nOp entries.
85495
85496
85497
85498
85499
85500
85501
85502
85503
85504
85505
85506
85507
85508
85509
static void SQLITE_NOINLINE vdbeChangeP4Full(
  Vdbe *p,
  Op *pOp,
  const char *zP4,
  int n
){
  if( pOp->p4type ){
    freeP4(p->db, pOp->p4type, pOp->p4.p);
    pOp->p4type = 0;
    pOp->p4.p = 0;
  }
  if( n<0 ){
    sqlite3VdbeChangeP4(p, (int)(pOp - p->aOp), zP4, n);
  }else{
    if( n==0 ) n = sqlite3Strlen30(zP4);







|







85846
85847
85848
85849
85850
85851
85852
85853
85854
85855
85856
85857
85858
85859
85860
static void SQLITE_NOINLINE vdbeChangeP4Full(
  Vdbe *p,
  Op *pOp,
  const char *zP4,
  int n
){
  if( pOp->p4type ){
    assert( pOp->p4type > P4_FREE_IF_LE );
    pOp->p4type = 0;
    pOp->p4.p = 0;
  }
  if( n<0 ){
    sqlite3VdbeChangeP4(p, (int)(pOp - p->aOp), zP4, n);
  }else{
    if( n==0 ) n = sqlite3Strlen30(zP4);
89618
89619
89620
89621
89622
89623
89624
89625








89626
89627
89628
89629
89630
89631
89632
** Set all the parameters in the compiled SQL statement to NULL.
*/
SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt *pStmt){
  int i;
  int rc = SQLITE_OK;
  Vdbe *p = (Vdbe*)pStmt;
#if SQLITE_THREADSAFE
  sqlite3_mutex *mutex = ((Vdbe*)pStmt)->db->mutex;








#endif
  sqlite3_mutex_enter(mutex);
  for(i=0; i<p->nVar; i++){
    sqlite3VdbeMemRelease(&p->aVar[i]);
    p->aVar[i].flags = MEM_Null;
  }
  assert( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 || p->expmask==0 );







|
>
>
>
>
>
>
>
>







89969
89970
89971
89972
89973
89974
89975
89976
89977
89978
89979
89980
89981
89982
89983
89984
89985
89986
89987
89988
89989
89990
89991
** Set all the parameters in the compiled SQL statement to NULL.
*/
SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt *pStmt){
  int i;
  int rc = SQLITE_OK;
  Vdbe *p = (Vdbe*)pStmt;
#if SQLITE_THREADSAFE
  sqlite3_mutex *mutex;
#endif
#ifdef SQLITE_ENABLE_API_ARMOR
  if( pStmt==0 ){
    return SQLITE_MISUSE_BKPT;
  }
#endif
#if SQLITE_THREADSAFE
  mutex = p->db->mutex;
#endif
  sqlite3_mutex_enter(mutex);
  for(i=0; i<p->nVar; i++){
    sqlite3VdbeMemRelease(&p->aVar[i]);
    p->aVar[i].flags = MEM_Null;
  }
  assert( (p->prepFlags & SQLITE_PREPARE_SAVESQL)!=0 || p->expmask==0 );
90408
90409
90410
90411
90412
90413
90414
90415
90416
90417
90418
90419
90420
90421
90422
90423
90424
/*
** Extract the user data from a sqlite3_context structure and return a
** pointer to it.
*/
SQLITE_API void *sqlite3_user_data(sqlite3_context *p){
#ifdef SQLITE_ENABLE_API_ARMOR
  if( p==0 ) return 0;
#else
  assert( p && p->pFunc );
#endif
  return p->pFunc->pUserData;
}

/*
** Extract the user data from a sqlite3_context structure and return a
** pointer to it.
**







|

<







90767
90768
90769
90770
90771
90772
90773
90774
90775

90776
90777
90778
90779
90780
90781
90782
/*
** Extract the user data from a sqlite3_context structure and return a
** pointer to it.
*/
SQLITE_API void *sqlite3_user_data(sqlite3_context *p){
#ifdef SQLITE_ENABLE_API_ARMOR
  if( p==0 ) return 0;
#endif
  assert( p && p->pFunc );

  return p->pFunc->pUserData;
}

/*
** Extract the user data from a sqlite3_context structure and return a
** pointer to it.
**
94229
94230
94231
94232
94233
94234
94235
94236
94237
94238
94239
94240
94241
94242
94243
**
** To force any register to be an integer, just add 0.
*/
case OP_AddImm: {            /* in1 */
  pIn1 = &aMem[pOp->p1];
  memAboutToChange(p, pIn1);
  sqlite3VdbeMemIntegerify(pIn1);
  pIn1->u.i += pOp->p2;
  break;
}

/* Opcode: MustBeInt P1 P2 * * *
**
** Force the value in register P1 to be an integer.  If the value
** in P1 is not an integer and cannot be converted into an integer







|







94587
94588
94589
94590
94591
94592
94593
94594
94595
94596
94597
94598
94599
94600
94601
**
** To force any register to be an integer, just add 0.
*/
case OP_AddImm: {            /* in1 */
  pIn1 = &aMem[pOp->p1];
  memAboutToChange(p, pIn1);
  sqlite3VdbeMemIntegerify(pIn1);
  *(u64*)&pIn1->u.i += (u64)pOp->p2;
  break;
}

/* Opcode: MustBeInt P1 P2 * * *
**
** Force the value in register P1 to be an integer.  If the value
** in P1 is not an integer and cannot be converted into an integer
100375
100376
100377
100378
100379
100380
100381
100382
100383
100384

100385
100386
100387
100388
100389
100390
100391
100392
100393
100394
100395
100396
100397
100398
100399
100400
100401
100402
100403
100404
100405
100406
  Table *pTab;
  sqlite3_vtab *pVtab;
  const sqlite3_module *pModule;
  char *zErr = 0;

  pOut = &aMem[pOp->p2];
  sqlite3VdbeMemSetNull(pOut);  /* Innocent until proven guilty */
  assert( pOp->p4type==P4_TABLE );
  pTab = pOp->p4.pTab;
  assert( pTab!=0 );

  assert( IsVirtual(pTab) );
  if( pTab->u.vtab.p==0 ) break;
  pVtab = pTab->u.vtab.p->pVtab;
  assert( pVtab!=0 );
  pModule = pVtab->pModule;
  assert( pModule!=0 );
  assert( pModule->iVersion>=4 );
  assert( pModule->xIntegrity!=0 );
  pTab->nTabRef++;
  sqlite3VtabLock(pTab->u.vtab.p);
  assert( pOp->p1>=0 && pOp->p1<db->nDb );
  rc = pModule->xIntegrity(pVtab, db->aDb[pOp->p1].zDbSName, pTab->zName,
                           pOp->p3, &zErr);
  sqlite3VtabUnlock(pTab->u.vtab.p);
  sqlite3DeleteTable(db, pTab);
  if( rc ){
    sqlite3_free(zErr);
    goto abort_due_to_error;
  }
  if( zErr ){
    sqlite3VdbeMemSetStr(pOut, zErr, -1, SQLITE_UTF8, sqlite3_free);
  }







|


>








<





<







100733
100734
100735
100736
100737
100738
100739
100740
100741
100742
100743
100744
100745
100746
100747
100748
100749
100750
100751

100752
100753
100754
100755
100756

100757
100758
100759
100760
100761
100762
100763
  Table *pTab;
  sqlite3_vtab *pVtab;
  const sqlite3_module *pModule;
  char *zErr = 0;

  pOut = &aMem[pOp->p2];
  sqlite3VdbeMemSetNull(pOut);  /* Innocent until proven guilty */
  assert( pOp->p4type==P4_TABLEREF );
  pTab = pOp->p4.pTab;
  assert( pTab!=0 );
  assert( pTab->nTabRef>0 );
  assert( IsVirtual(pTab) );
  if( pTab->u.vtab.p==0 ) break;
  pVtab = pTab->u.vtab.p->pVtab;
  assert( pVtab!=0 );
  pModule = pVtab->pModule;
  assert( pModule!=0 );
  assert( pModule->iVersion>=4 );
  assert( pModule->xIntegrity!=0 );

  sqlite3VtabLock(pTab->u.vtab.p);
  assert( pOp->p1>=0 && pOp->p1<db->nDb );
  rc = pModule->xIntegrity(pVtab, db->aDb[pOp->p1].zDbSName, pTab->zName,
                           pOp->p3, &zErr);
  sqlite3VtabUnlock(pTab->u.vtab.p);

  if( rc ){
    sqlite3_free(zErr);
    goto abort_due_to_error;
  }
  if( zErr ){
    sqlite3VdbeMemSetStr(pOut, zErr, -1, SQLITE_UTF8, sqlite3_free);
  }
100517
100518
100519
100520
100521
100522
100523

100524
100525
100526
100527
100528
100529
100530
100531
100532
100533
100534
100535
100536
100537
100538
100539
100540



100541
100542
100543
100544
100545
100546
100547
** unused by OP_VColumn.
*/
case OP_VColumn: {           /* ncycle */
  sqlite3_vtab *pVtab;
  const sqlite3_module *pModule;
  Mem *pDest;
  sqlite3_context sContext;


  VdbeCursor *pCur = p->apCsr[pOp->p1];
  assert( pCur!=0 );
  assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
  pDest = &aMem[pOp->p3];
  memAboutToChange(p, pDest);
  if( pCur->nullRow ){
    sqlite3VdbeMemSetNull(pDest);
    break;
  }
  assert( pCur->eCurType==CURTYPE_VTAB );
  pVtab = pCur->uc.pVCur->pVtab;
  pModule = pVtab->pModule;
  assert( pModule->xColumn );
  memset(&sContext, 0, sizeof(sContext));
  sContext.pOut = pDest;
  sContext.enc = encoding;



  assert( pOp->p5==OPFLAG_NOCHNG || pOp->p5==0 );
  if( pOp->p5 & OPFLAG_NOCHNG ){
    sqlite3VdbeMemSetNull(pDest);
    pDest->flags = MEM_Null|MEM_Zero;
    pDest->u.nZero = 0;
  }else{
    MemSetTypeFlag(pDest, MEM_Null);







>

















>
>
>







100874
100875
100876
100877
100878
100879
100880
100881
100882
100883
100884
100885
100886
100887
100888
100889
100890
100891
100892
100893
100894
100895
100896
100897
100898
100899
100900
100901
100902
100903
100904
100905
100906
100907
100908
** unused by OP_VColumn.
*/
case OP_VColumn: {           /* ncycle */
  sqlite3_vtab *pVtab;
  const sqlite3_module *pModule;
  Mem *pDest;
  sqlite3_context sContext;
  FuncDef nullFunc;

  VdbeCursor *pCur = p->apCsr[pOp->p1];
  assert( pCur!=0 );
  assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
  pDest = &aMem[pOp->p3];
  memAboutToChange(p, pDest);
  if( pCur->nullRow ){
    sqlite3VdbeMemSetNull(pDest);
    break;
  }
  assert( pCur->eCurType==CURTYPE_VTAB );
  pVtab = pCur->uc.pVCur->pVtab;
  pModule = pVtab->pModule;
  assert( pModule->xColumn );
  memset(&sContext, 0, sizeof(sContext));
  sContext.pOut = pDest;
  sContext.enc = encoding;
  nullFunc.pUserData = 0;
  nullFunc.funcFlags = SQLITE_RESULT_SUBTYPE;
  sContext.pFunc = &nullFunc;
  assert( pOp->p5==OPFLAG_NOCHNG || pOp->p5==0 );
  if( pOp->p5 & OPFLAG_NOCHNG ){
    sqlite3VdbeMemSetNull(pDest);
    pDest->flags = MEM_Null|MEM_Zero;
    pDest->u.nZero = 0;
  }else{
    MemSetTypeFlag(pDest, MEM_Null);
100865
100866
100867
100868
100869
100870
100871




































100872
100873
100874
100875
100876
100877
100878
** Clear the subtype from register P1.
*/
case OP_ClrSubtype: {   /* in1 */
  pIn1 = &aMem[pOp->p1];
  pIn1->flags &= ~MEM_Subtype;
  break;
}





































/* Opcode: FilterAdd P1 * P3 P4 *
** Synopsis: filter(P1) += key(P3@P4)
**
** Compute a hash on the P4 registers starting with r[P3] and
** add that hash to the bloom filter contained in r[P1].
*/







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







101226
101227
101228
101229
101230
101231
101232
101233
101234
101235
101236
101237
101238
101239
101240
101241
101242
101243
101244
101245
101246
101247
101248
101249
101250
101251
101252
101253
101254
101255
101256
101257
101258
101259
101260
101261
101262
101263
101264
101265
101266
101267
101268
101269
101270
101271
101272
101273
101274
101275
** Clear the subtype from register P1.
*/
case OP_ClrSubtype: {   /* in1 */
  pIn1 = &aMem[pOp->p1];
  pIn1->flags &= ~MEM_Subtype;
  break;
}

/* Opcode: GetSubtype P1 P2 * * *
** Synopsis:  r[P2] = r[P1].subtype
**
** Extract the subtype value from register P1 and write that subtype
** into register P2.  If P1 has no subtype, then P1 gets a NULL.
*/
case OP_GetSubtype: {   /* in1 out2 */
  pIn1 = &aMem[pOp->p1];
  pOut = &aMem[pOp->p2];
  if( pIn1->flags & MEM_Subtype ){
    sqlite3VdbeMemSetInt64(pOut, pIn1->eSubtype);
  }else{
    sqlite3VdbeMemSetNull(pOut);
  }
  break;
}

/* Opcode: SetSubtype P1 P2 * * *
** Synopsis:  r[P2].subtype = r[P1]
**
** Set the subtype value of register P2 to the integer from register P1.
** If P1 is NULL, clear the subtype from p2.
*/
case OP_SetSubtype: {   /* in1 out2 */
  pIn1 = &aMem[pOp->p1];
  pOut = &aMem[pOp->p2];
  if( pIn1->flags & MEM_Null ){
    pOut->flags &= ~MEM_Subtype;
  }else{
    assert( pIn1->flags & MEM_Int );
    pOut->flags |= MEM_Subtype;
    pOut->eSubtype = (u8)(pIn1->u.i & 0xff);
  }
  break;
}

/* Opcode: FilterAdd P1 * P3 P4 *
** Synopsis: filter(P1) += key(P3@P4)
**
** Compute a hash on the P4 registers starting with r[P3] and
** add that hash to the bloom filter contained in r[P1].
*/
105914
105915
105916
105917
105918
105919
105920

105921
105922
105923
105924
105925
105926
105927
  int n;
  Table *pExTab;

  n = pExpr->iColumn;
  assert( ExprUseYTab(pExpr) );
  pExTab = pExpr->y.pTab;
  assert( pExTab!=0 );

  if( (pExTab->tabFlags & TF_HasGenerated)!=0
   && (pExTab->aCol[n].colFlags & COLFLAG_GENERATED)!=0
  ){
    testcase( pExTab->nCol==BMS-1 );
    testcase( pExTab->nCol==BMS );
    return pExTab->nCol>=BMS ? ALLBITS : MASKBIT(pExTab->nCol)-1;
  }else{







>







106311
106312
106313
106314
106315
106316
106317
106318
106319
106320
106321
106322
106323
106324
106325
  int n;
  Table *pExTab;

  n = pExpr->iColumn;
  assert( ExprUseYTab(pExpr) );
  pExTab = pExpr->y.pTab;
  assert( pExTab!=0 );
  assert( n < pExTab->nCol );
  if( (pExTab->tabFlags & TF_HasGenerated)!=0
   && (pExTab->aCol[n].colFlags & COLFLAG_GENERATED)!=0
  ){
    testcase( pExTab->nCol==BMS-1 );
    testcase( pExTab->nCol==BMS );
    return pExTab->nCol>=BMS ? ALLBITS : MASKBIT(pExTab->nCol)-1;
  }else{
106490
106491
106492
106493
106494
106495
106496

106497
106498
106499
106500
106501
106502
106503
      sqlite3ErrorMsg(pParse, "%s: %s.%s", zErr, zTab, zCol);
    }else{
      sqlite3ErrorMsg(pParse, "%s: %s", zErr, zCol);
    }
    sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr);
    pParse->checkSchema = 1;
    pTopNC->nNcErr++;

  }
  assert( pFJMatch==0 );

  /* Remove all substructure from pExpr */
  if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){
    sqlite3ExprDelete(db, pExpr->pLeft);
    pExpr->pLeft = 0;







>







106888
106889
106890
106891
106892
106893
106894
106895
106896
106897
106898
106899
106900
106901
106902
      sqlite3ErrorMsg(pParse, "%s: %s.%s", zErr, zTab, zCol);
    }else{
      sqlite3ErrorMsg(pParse, "%s: %s", zErr, zCol);
    }
    sqlite3RecordErrorOffsetOfExpr(pParse->db, pExpr);
    pParse->checkSchema = 1;
    pTopNC->nNcErr++;
    eNewExprOp = TK_NULL;
  }
  assert( pFJMatch==0 );

  /* Remove all substructure from pExpr */
  if( !ExprHasProperty(pExpr,(EP_TokenOnly|EP_Leaf)) ){
    sqlite3ExprDelete(db, pExpr->pLeft);
    pExpr->pLeft = 0;
106516
106517
106518
106519
106520
106521
106522
106523
106524
106525
106526
106527
106528
106529
106530
  ** if the mask contains extra set bits.  However, it is important to
  ** avoid setting bits beyond the maximum column number of the table.
  ** (See ticket [b92e5e8ec2cdbaa1]).
  **
  ** If a generated column is referenced, set bits for every column
  ** of the table.
  */
  if( pExpr->iColumn>=0 && pMatch!=0 ){
    pMatch->colUsed |= sqlite3ExprColUsed(pExpr);
  }

  pExpr->op = eNewExprOp;
lookupname_end:
  if( cnt==1 ){
    assert( pNC!=0 );







|







106915
106916
106917
106918
106919
106920
106921
106922
106923
106924
106925
106926
106927
106928
106929
  ** if the mask contains extra set bits.  However, it is important to
  ** avoid setting bits beyond the maximum column number of the table.
  ** (See ticket [b92e5e8ec2cdbaa1]).
  **
  ** If a generated column is referenced, set bits for every column
  ** of the table.
  */
  if( pExpr->iColumn>=0 && cnt==1 && pMatch!=0 ){
    pMatch->colUsed |= sqlite3ExprColUsed(pExpr);
  }

  pExpr->op = eNewExprOp;
lookupname_end:
  if( cnt==1 ){
    assert( pNC!=0 );
106981
106982
106983
106984
106985
106986
106987
106988
106989
106990
106991
106992

106993
106994
106995
106996
106997
106998
106999
            sqlite3WalkExpr(pWalker, pExpr->y.pWin->pFilter);
          }
#endif
          pNC2 = pNC;
          while( pNC2
              && sqlite3ReferencesSrcList(pParse, pExpr, pNC2->pSrcList)==0
          ){
            pExpr->op2++;
            pNC2 = pNC2->pNext;
          }
          assert( pDef!=0 || IN_RENAME_OBJECT );
          if( pNC2 && pDef ){

            assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg );
            assert( SQLITE_FUNC_ANYORDER==NC_OrderAgg );
            testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 );
            testcase( (pDef->funcFlags & SQLITE_FUNC_ANYORDER)!=0 );
            pNC2->ncFlags |= NC_HasAgg
              | ((pDef->funcFlags^SQLITE_FUNC_ANYORDER)
                  & (SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER));







|




>







107380
107381
107382
107383
107384
107385
107386
107387
107388
107389
107390
107391
107392
107393
107394
107395
107396
107397
107398
107399
            sqlite3WalkExpr(pWalker, pExpr->y.pWin->pFilter);
          }
#endif
          pNC2 = pNC;
          while( pNC2
              && sqlite3ReferencesSrcList(pParse, pExpr, pNC2->pSrcList)==0
          ){
            pExpr->op2 += (1 + pNC2->nNestedSelect);
            pNC2 = pNC2->pNext;
          }
          assert( pDef!=0 || IN_RENAME_OBJECT );
          if( pNC2 && pDef ){
            pExpr->op2 += pNC2->nNestedSelect;
            assert( SQLITE_FUNC_MINMAX==NC_MinMaxAgg );
            assert( SQLITE_FUNC_ANYORDER==NC_OrderAgg );
            testcase( (pDef->funcFlags & SQLITE_FUNC_MINMAX)!=0 );
            testcase( (pDef->funcFlags & SQLITE_FUNC_ANYORDER)!=0 );
            pNC2->ncFlags |= NC_HasAgg
              | ((pDef->funcFlags^SQLITE_FUNC_ANYORDER)
                  & (SQLITE_FUNC_MINMAX|SQLITE_FUNC_ANYORDER));
107544
107545
107546
107547
107548
107549
107550

107551
107552
107553
107554
107555
107556
107557
      assert( pSub->pPrior && pSub->pOrderBy==0 );
      pSub->pOrderBy = p->pOrderBy;
      p->pOrderBy = 0;
    }

    /* Recursively resolve names in all subqueries in the FROM clause
    */

    for(i=0; i<p->pSrc->nSrc; i++){
      SrcItem *pItem = &p->pSrc->a[i];
      if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){
        int nRef = pOuterNC ? pOuterNC->nRef : 0;
        const char *zSavedContext = pParse->zAuthContext;

        if( pItem->zName ) pParse->zAuthContext = pItem->zName;







>







107944
107945
107946
107947
107948
107949
107950
107951
107952
107953
107954
107955
107956
107957
107958
      assert( pSub->pPrior && pSub->pOrderBy==0 );
      pSub->pOrderBy = p->pOrderBy;
      p->pOrderBy = 0;
    }

    /* Recursively resolve names in all subqueries in the FROM clause
    */
    if( pOuterNC ) pOuterNC->nNestedSelect++;
    for(i=0; i<p->pSrc->nSrc; i++){
      SrcItem *pItem = &p->pSrc->a[i];
      if( pItem->pSelect && (pItem->pSelect->selFlags & SF_Resolved)==0 ){
        int nRef = pOuterNC ? pOuterNC->nRef : 0;
        const char *zSavedContext = pParse->zAuthContext;

        if( pItem->zName ) pParse->zAuthContext = pItem->zName;
107567
107568
107569
107570
107571
107572
107573



107574
107575
107576
107577
107578
107579
107580
        ** the refcount on all contexts between the current one and the
        ** context containing the column when it resolves a name. */
        if( pOuterNC ){
          assert( pItem->fg.isCorrelated==0 && pOuterNC->nRef>=nRef );
          pItem->fg.isCorrelated = (pOuterNC->nRef>nRef);
        }
      }



    }

    /* Set up the local name-context to pass to sqlite3ResolveExprNames() to
    ** resolve the result-set expression list.
    */
    sNC.ncFlags = NC_AllowAgg|NC_AllowWin;
    sNC.pSrcList = p->pSrc;







>
>
>







107968
107969
107970
107971
107972
107973
107974
107975
107976
107977
107978
107979
107980
107981
107982
107983
107984
        ** the refcount on all contexts between the current one and the
        ** context containing the column when it resolves a name. */
        if( pOuterNC ){
          assert( pItem->fg.isCorrelated==0 && pOuterNC->nRef>=nRef );
          pItem->fg.isCorrelated = (pOuterNC->nRef>nRef);
        }
      }
    }
    if( pOuterNC && ALWAYS(pOuterNC->nNestedSelect>0) ){
      pOuterNC->nNestedSelect--;
    }

    /* Set up the local name-context to pass to sqlite3ResolveExprNames() to
    ** resolve the result-set expression list.
    */
    sNC.ncFlags = NC_AllowAgg|NC_AllowWin;
    sNC.pSrcList = p->pSrc;
109155
109156
109157
109158
109159
109160
109161
109162
109163
109164
109165
109166
109167
109168
109169
109170
109171
    return;
  }
  assert( pExpr->op==TK_FUNCTION );
  assert( pExpr->pLeft==0 );
  assert( ExprUseXList(pExpr) );
  if( pExpr->x.pList==0 || NEVER(pExpr->x.pList->nExpr==0) ){
    /* Ignore ORDER BY on zero-argument aggregates */
    sqlite3ParserAddCleanup(pParse,
        (void(*)(sqlite3*,void*))sqlite3ExprListDelete,
        pOrderBy);
    return;
  }
  if( IsWindowFunc(pExpr) ){
    sqlite3ExprOrderByAggregateError(pParse, pExpr);
    sqlite3ExprListDelete(db, pOrderBy);
    return;
  }







|
<
<







109559
109560
109561
109562
109563
109564
109565
109566


109567
109568
109569
109570
109571
109572
109573
    return;
  }
  assert( pExpr->op==TK_FUNCTION );
  assert( pExpr->pLeft==0 );
  assert( ExprUseXList(pExpr) );
  if( pExpr->x.pList==0 || NEVER(pExpr->x.pList->nExpr==0) ){
    /* Ignore ORDER BY on zero-argument aggregates */
    sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pOrderBy);


    return;
  }
  if( IsWindowFunc(pExpr) ){
    sqlite3ExprOrderByAggregateError(pParse, pExpr);
    sqlite3ExprListDelete(db, pOrderBy);
    return;
  }
109338
109339
109340
109341
109342
109343
109344



109345
109346
109347
109348
109349
109350
109351
  if( !ExprHasProperty(p, EP_Static) ){
    sqlite3DbNNFreeNN(db, p);
  }
}
SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3 *db, Expr *p){
  if( p ) sqlite3ExprDeleteNN(db, p);
}




/*
** Clear both elements of an OnOrUsing object
*/
SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3 *db, OnOrUsing *p){
  if( p==0 ){
    /* Nothing to clear */







>
>
>







109740
109741
109742
109743
109744
109745
109746
109747
109748
109749
109750
109751
109752
109753
109754
109755
109756
  if( !ExprHasProperty(p, EP_Static) ){
    sqlite3DbNNFreeNN(db, p);
  }
}
SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3 *db, Expr *p){
  if( p ) sqlite3ExprDeleteNN(db, p);
}
SQLITE_PRIVATE void sqlite3ExprDeleteGeneric(sqlite3 *db, void *p){
  if( ALWAYS(p) ) sqlite3ExprDeleteNN(db, (Expr*)p);
}

/*
** Clear both elements of an OnOrUsing object
*/
SQLITE_PRIVATE void sqlite3ClearOnOrUsing(sqlite3 *db, OnOrUsing *p){
  if( p==0 ){
    /* Nothing to clear */
109363
109364
109365
109366
109367
109368
109369
109370
109371
109372
109373
109374
109375
109376
109377
109378
109379
**
** The pExpr might be deleted immediately on an OOM error.
**
** The deferred delete is (currently) implemented by adding the
** pExpr to the pParse->pConstExpr list with a register number of 0.
*/
SQLITE_PRIVATE void sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){
  sqlite3ParserAddCleanup(pParse,
    (void(*)(sqlite3*,void*))sqlite3ExprDelete,
    pExpr);
}

/* Invoke sqlite3RenameExprUnmap() and sqlite3ExprDelete() on the
** expression.
*/
SQLITE_PRIVATE void sqlite3ExprUnmapAndDelete(Parse *pParse, Expr *p){
  if( p ){







|
<
<







109768
109769
109770
109771
109772
109773
109774
109775


109776
109777
109778
109779
109780
109781
109782
**
** The pExpr might be deleted immediately on an OOM error.
**
** The deferred delete is (currently) implemented by adding the
** pExpr to the pParse->pConstExpr list with a register number of 0.
*/
SQLITE_PRIVATE void sqlite3ExprDeferredDelete(Parse *pParse, Expr *pExpr){
  sqlite3ParserAddCleanup(pParse, sqlite3ExprDeleteGeneric, pExpr);


}

/* Invoke sqlite3RenameExprUnmap() and sqlite3ExprDelete() on the
** expression.
*/
SQLITE_PRIVATE void sqlite3ExprUnmapAndDelete(Parse *pParse, Expr *p){
  if( p ){
110171
110172
110173
110174
110175
110176
110177



110178
110179
110180
110181
110182
110183
110184
    pItem++;
  }while( --i>0 );
  sqlite3DbNNFreeNN(db, pList);
}
SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){
  if( pList ) exprListDeleteNN(db, pList);
}




/*
** Return the bitwise-OR of all Expr.flags fields in the given
** ExprList.
*/
SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList *pList){
  int i;







>
>
>







110574
110575
110576
110577
110578
110579
110580
110581
110582
110583
110584
110585
110586
110587
110588
110589
110590
    pItem++;
  }while( --i>0 );
  sqlite3DbNNFreeNN(db, pList);
}
SQLITE_PRIVATE void sqlite3ExprListDelete(sqlite3 *db, ExprList *pList){
  if( pList ) exprListDeleteNN(db, pList);
}
SQLITE_PRIVATE void sqlite3ExprListDeleteGeneric(sqlite3 *db, void *pList){
  if( ALWAYS(pList) ) exprListDeleteNN(db, (ExprList*)pList);
}

/*
** Return the bitwise-OR of all Expr.flags fields in the given
** ExprList.
*/
SQLITE_PRIVATE u32 sqlite3ExprListFlags(const ExprList *pList){
  int i;
110670
110671
110672
110673
110674
110675
110676
110677
110678
110679

110680
110681
110682
110683
110684
110685
110686
    case TK_STRING:
    case TK_FLOAT:
    case TK_BLOB:
      return 0;
    case TK_COLUMN:
      assert( ExprUseYTab(p) );
      return ExprHasProperty(p, EP_CanBeNull) ||
             p->y.pTab==0 ||  /* Reference to column of index on expression */
             (p->iColumn>=0
              && p->y.pTab->aCol!=0 /* Possible due to prior error */

              && p->y.pTab->aCol[p->iColumn].notNull==0);
    default:
      return 1;
  }
}

/*







|


>







111076
111077
111078
111079
111080
111081
111082
111083
111084
111085
111086
111087
111088
111089
111090
111091
111092
111093
    case TK_STRING:
    case TK_FLOAT:
    case TK_BLOB:
      return 0;
    case TK_COLUMN:
      assert( ExprUseYTab(p) );
      return ExprHasProperty(p, EP_CanBeNull) ||
             NEVER(p->y.pTab==0) ||  /* Reference to column of index on expr */
             (p->iColumn>=0
              && p->y.pTab->aCol!=0 /* Possible due to prior error */
              && ALWAYS(p->iColumn<p->y.pTab->nCol)
              && p->y.pTab->aCol[p->iColumn].notNull==0);
    default:
      return 1;
  }
}

/*
113254
113255
113256
113257
113258
113259
113260


113261
113262
113263
113264
113265
113266
113267
113268
113269
  assert( pExpr==0 || !ExprHasVVAProperty(pExpr,EP_Immutable) );
  assert( target>0 && target<=pParse->nMem );
  assert( pParse->pVdbe!=0 || pParse->db->mallocFailed );
  if( pParse->pVdbe==0 ) return;
  inReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
  if( inReg!=target ){
    u8 op;


    if( ALWAYS(pExpr)
     && (ExprHasProperty(pExpr,EP_Subquery) || pExpr->op==TK_REGISTER)
    ){
      op = OP_Copy;
    }else{
      op = OP_SCopy;
    }
    sqlite3VdbeAddOp2(pParse->pVdbe, op, inReg, target);
  }







>
>
|
|







113661
113662
113663
113664
113665
113666
113667
113668
113669
113670
113671
113672
113673
113674
113675
113676
113677
113678
  assert( pExpr==0 || !ExprHasVVAProperty(pExpr,EP_Immutable) );
  assert( target>0 && target<=pParse->nMem );
  assert( pParse->pVdbe!=0 || pParse->db->mallocFailed );
  if( pParse->pVdbe==0 ) return;
  inReg = sqlite3ExprCodeTarget(pParse, pExpr, target);
  if( inReg!=target ){
    u8 op;
    Expr *pX = sqlite3ExprSkipCollateAndLikely(pExpr);
    testcase( pX!=pExpr );
    if( ALWAYS(pX)
     && (ExprHasProperty(pX,EP_Subquery) || pX->op==TK_REGISTER)
    ){
      op = OP_Copy;
    }else{
      op = OP_SCopy;
    }
    sqlite3VdbeAddOp2(pParse->pVdbe, op, inReg, target);
  }
114701
114702
114703
114704
114705
114706
114707

114708
114709
114710
114711
114712
114713
114714
114715
114716
114717
114718
114719
114720
114721
        } /* end loop over pSrcList */
      }
      return WRC_Continue;
    }
    case TK_AGG_FUNCTION: {
      if( (pNC->ncFlags & NC_InAggFunc)==0
       && pWalker->walkerDepth==pExpr->op2

      ){
        /* Check to see if pExpr is a duplicate of another aggregate
        ** function that is already in the pAggInfo structure
        */
        struct AggInfo_func *pItem = pAggInfo->aFunc;
        for(i=0; i<pAggInfo->nFunc; i++, pItem++){
          if( pItem->pFExpr==pExpr ) break;
          if( sqlite3ExprCompare(0, pItem->pFExpr, pExpr, -1)==0 ){
            break;
          }
        }
        if( i>=pAggInfo->nFunc ){
          /* pExpr is original.  Make a new entry in pAggInfo->aFunc[]
          */







>






|







115110
115111
115112
115113
115114
115115
115116
115117
115118
115119
115120
115121
115122
115123
115124
115125
115126
115127
115128
115129
115130
115131
        } /* end loop over pSrcList */
      }
      return WRC_Continue;
    }
    case TK_AGG_FUNCTION: {
      if( (pNC->ncFlags & NC_InAggFunc)==0
       && pWalker->walkerDepth==pExpr->op2
       && pExpr->pAggInfo==0
      ){
        /* Check to see if pExpr is a duplicate of another aggregate
        ** function that is already in the pAggInfo structure
        */
        struct AggInfo_func *pItem = pAggInfo->aFunc;
        for(i=0; i<pAggInfo->nFunc; i++, pItem++){
          if( NEVER(pItem->pFExpr==pExpr) ) break;
          if( sqlite3ExprCompare(0, pItem->pFExpr, pExpr, -1)==0 ){
            break;
          }
        }
        if( i>=pAggInfo->nFunc ){
          /* pExpr is original.  Make a new entry in pAggInfo->aFunc[]
          */
114750
114751
114752
114753
114754
114755
114756


114757
114758
114759
114760
114761
114762
114763
                               pExpr->x.pList->a[0].pExpr,0)==0
              ){
                pItem->bOBPayload = 0;
                pItem->bOBUnique = ExprHasProperty(pExpr, EP_Distinct);
              }else{
                pItem->bOBPayload = 1;
              }


            }else{
              pItem->iOBTab = -1;
            }
            if( ExprHasProperty(pExpr, EP_Distinct) && !pItem->bOBUnique ){
              pItem->iDistinct = pParse->nTab++;
            }else{
              pItem->iDistinct = -1;







>
>







115160
115161
115162
115163
115164
115165
115166
115167
115168
115169
115170
115171
115172
115173
115174
115175
                               pExpr->x.pList->a[0].pExpr,0)==0
              ){
                pItem->bOBPayload = 0;
                pItem->bOBUnique = ExprHasProperty(pExpr, EP_Distinct);
              }else{
                pItem->bOBPayload = 1;
              }
              pItem->bUseSubtype =
                    (pItem->pFunc->funcFlags & SQLITE_SUBTYPE)!=0;
            }else{
              pItem->iOBTab = -1;
            }
            if( ExprHasProperty(pExpr, EP_Distinct) && !pItem->bOBUnique ){
              pItem->iDistinct = pParse->nTab++;
            }else{
              pItem->iDistinct = -1;
117516
117517
117518
117519
117520
117521
117522
117523
117524
117525

117526
117527
117528
117529
117530
117531
117532
** Three SQL functions - stat_init(), stat_push(), and stat_get() -
** share an instance of the following structure to hold their state
** information.
*/
typedef struct StatAccum StatAccum;
typedef struct StatSample StatSample;
struct StatSample {
  tRowcnt *anEq;                  /* sqlite_stat4.nEq */
  tRowcnt *anDLt;                 /* sqlite_stat4.nDLt */
#ifdef SQLITE_ENABLE_STAT4

  tRowcnt *anLt;                  /* sqlite_stat4.nLt */
  union {
    i64 iRowid;                     /* Rowid in main table of the key */
    u8 *aRowid;                     /* Key for WITHOUT ROWID tables */
  } u;
  u32 nRowid;                     /* Sizeof aRowid[] */
  u8 isPSample;                   /* True if a periodic sample */







<


>







117928
117929
117930
117931
117932
117933
117934

117935
117936
117937
117938
117939
117940
117941
117942
117943
117944
** Three SQL functions - stat_init(), stat_push(), and stat_get() -
** share an instance of the following structure to hold their state
** information.
*/
typedef struct StatAccum StatAccum;
typedef struct StatSample StatSample;
struct StatSample {

  tRowcnt *anDLt;                 /* sqlite_stat4.nDLt */
#ifdef SQLITE_ENABLE_STAT4
  tRowcnt *anEq;                  /* sqlite_stat4.nEq */
  tRowcnt *anLt;                  /* sqlite_stat4.nLt */
  union {
    i64 iRowid;                     /* Rowid in main table of the key */
    u8 *aRowid;                     /* Key for WITHOUT ROWID tables */
  } u;
  u32 nRowid;                     /* Sizeof aRowid[] */
  u8 isPSample;                   /* True if a periodic sample */
117676
117677
117678
117679
117680
117681
117682
117683
117684
117685

117686
117687
117688
117689
117690
117691
117692
117693
117694
117695
117696
117697
117698
117699
117700
117701
117702
117703
117704
117705
117706
117707
117708

117709
117710
117711
117712
117713
117714
117715
  nColUp = sizeof(tRowcnt)<8 ? (nCol+1)&~1 : nCol;
  nKeyCol = sqlite3_value_int(argv[1]);
  assert( nKeyCol<=nCol );
  assert( nKeyCol>0 );

  /* Allocate the space required for the StatAccum object */
  n = sizeof(*p)
    + sizeof(tRowcnt)*nColUp                  /* StatAccum.anEq */
    + sizeof(tRowcnt)*nColUp;                 /* StatAccum.anDLt */
#ifdef SQLITE_ENABLE_STAT4

  if( mxSample ){
    n += sizeof(tRowcnt)*nColUp                  /* StatAccum.anLt */
      + sizeof(StatSample)*(nCol+mxSample)       /* StatAccum.aBest[], a[] */
      + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample);
  }
#endif
  p = sqlite3DbMallocZero(db, n);
  if( p==0 ){
    sqlite3_result_error_nomem(context);
    return;
  }

  p->db = db;
  p->nEst = sqlite3_value_int64(argv[2]);
  p->nRow = 0;
  p->nLimit = sqlite3_value_int64(argv[3]);
  p->nCol = nCol;
  p->nKeyCol = nKeyCol;
  p->nSkipAhead = 0;
  p->current.anDLt = (tRowcnt*)&p[1];
  p->current.anEq = &p->current.anDLt[nColUp];

#ifdef SQLITE_ENABLE_STAT4

  p->mxSample = p->nLimit==0 ? mxSample : 0;
  if( mxSample ){
    u8 *pSpace;                     /* Allocated space not yet assigned */
    int i;                          /* Used to iterate through p->aSample[] */

    p->iGet = -1;
    p->nPSample = (tRowcnt)(p->nEst/(mxSample/3+1) + 1);







<
|

>




















<


>







118088
118089
118090
118091
118092
118093
118094

118095
118096
118097
118098
118099
118100
118101
118102
118103
118104
118105
118106
118107
118108
118109
118110
118111
118112
118113
118114
118115
118116
118117

118118
118119
118120
118121
118122
118123
118124
118125
118126
118127
  nColUp = sizeof(tRowcnt)<8 ? (nCol+1)&~1 : nCol;
  nKeyCol = sqlite3_value_int(argv[1]);
  assert( nKeyCol<=nCol );
  assert( nKeyCol>0 );

  /* Allocate the space required for the StatAccum object */
  n = sizeof(*p)

    + sizeof(tRowcnt)*nColUp;                    /* StatAccum.anDLt */
#ifdef SQLITE_ENABLE_STAT4
  n += sizeof(tRowcnt)*nColUp;                   /* StatAccum.anEq */
  if( mxSample ){
    n += sizeof(tRowcnt)*nColUp                  /* StatAccum.anLt */
      + sizeof(StatSample)*(nCol+mxSample)       /* StatAccum.aBest[], a[] */
      + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample);
  }
#endif
  p = sqlite3DbMallocZero(db, n);
  if( p==0 ){
    sqlite3_result_error_nomem(context);
    return;
  }

  p->db = db;
  p->nEst = sqlite3_value_int64(argv[2]);
  p->nRow = 0;
  p->nLimit = sqlite3_value_int64(argv[3]);
  p->nCol = nCol;
  p->nKeyCol = nKeyCol;
  p->nSkipAhead = 0;
  p->current.anDLt = (tRowcnt*)&p[1];


#ifdef SQLITE_ENABLE_STAT4
  p->current.anEq = &p->current.anDLt[nColUp];
  p->mxSample = p->nLimit==0 ? mxSample : 0;
  if( mxSample ){
    u8 *pSpace;                     /* Allocated space not yet assigned */
    int i;                          /* Used to iterate through p->aSample[] */

    p->iGet = -1;
    p->nPSample = (tRowcnt)(p->nEst/(mxSample/3+1) + 1);
117968
117969
117970
117971
117972
117973
117974

117975

117976
117977
117978
117979
117980
117981
117982
117983

117984
117985
117986

117987
117988
117989
117990
117991
117992

117993
117994
117995
117996
117997
117998
117999
  UNUSED_PARAMETER( argc );
  UNUSED_PARAMETER( context );
  assert( p->nCol>0 );
  assert( iChng<p->nCol );

  if( p->nRow==0 ){
    /* This is the first call to this function. Do initialization. */

    for(i=0; i<p->nCol; i++) p->current.anEq[i] = 1;

  }else{
    /* Second and subsequent calls get processed here */
#ifdef SQLITE_ENABLE_STAT4
    if( p->mxSample ) samplePushPrevious(p, iChng);
#endif

    /* Update anDLt[], anLt[] and anEq[] to reflect the values that apply
    ** to the current row of the index. */

    for(i=0; i<iChng; i++){
      p->current.anEq[i]++;
    }

    for(i=iChng; i<p->nCol; i++){
      p->current.anDLt[i]++;
#ifdef SQLITE_ENABLE_STAT4
      if( p->mxSample ) p->current.anLt[i] += p->current.anEq[i];
#endif
      p->current.anEq[i] = 1;

    }
  }

  p->nRow++;
#ifdef SQLITE_ENABLE_STAT4
  if( p->mxSample ){
    tRowcnt nLt;







>

>








>



>




<

>







118380
118381
118382
118383
118384
118385
118386
118387
118388
118389
118390
118391
118392
118393
118394
118395
118396
118397
118398
118399
118400
118401
118402
118403
118404
118405
118406

118407
118408
118409
118410
118411
118412
118413
118414
118415
  UNUSED_PARAMETER( argc );
  UNUSED_PARAMETER( context );
  assert( p->nCol>0 );
  assert( iChng<p->nCol );

  if( p->nRow==0 ){
    /* This is the first call to this function. Do initialization. */
#ifdef SQLITE_ENABLE_STAT4
    for(i=0; i<p->nCol; i++) p->current.anEq[i] = 1;
#endif
  }else{
    /* Second and subsequent calls get processed here */
#ifdef SQLITE_ENABLE_STAT4
    if( p->mxSample ) samplePushPrevious(p, iChng);
#endif

    /* Update anDLt[], anLt[] and anEq[] to reflect the values that apply
    ** to the current row of the index. */
#ifdef SQLITE_ENABLE_STAT4
    for(i=0; i<iChng; i++){
      p->current.anEq[i]++;
    }
#endif
    for(i=iChng; i<p->nCol; i++){
      p->current.anDLt[i]++;
#ifdef SQLITE_ENABLE_STAT4
      if( p->mxSample ) p->current.anLt[i] += p->current.anEq[i];

      p->current.anEq[i] = 1;
#endif
    }
  }

  p->nRow++;
#ifdef SQLITE_ENABLE_STAT4
  if( p->mxSample ){
    tRowcnt nLt;
118119
118120
118121
118122
118123
118124
118125

118126

118127
118128
118129
118130
118131
118132
118133
    sqlite3_str_appendf(&sStat, "%llu",
        p->nSkipAhead ? (u64)p->nEst : (u64)p->nRow);
    for(i=0; i<p->nKeyCol; i++){
      u64 nDistinct = p->current.anDLt[i] + 1;
      u64 iVal = (p->nRow + nDistinct - 1) / nDistinct;
      if( iVal==2 && p->nRow*10 <= nDistinct*11 ) iVal = 1;
      sqlite3_str_appendf(&sStat, " %llu", iVal);

      assert( p->current.anEq[i] );

    }
    sqlite3ResultStrAccum(context, &sStat);
  }
#ifdef SQLITE_ENABLE_STAT4
  else if( eCall==STAT_GET_ROWID ){
    if( p->iGet<0 ){
      samplePushPrevious(p, 0);







>

>







118535
118536
118537
118538
118539
118540
118541
118542
118543
118544
118545
118546
118547
118548
118549
118550
118551
    sqlite3_str_appendf(&sStat, "%llu",
        p->nSkipAhead ? (u64)p->nEst : (u64)p->nRow);
    for(i=0; i<p->nKeyCol; i++){
      u64 nDistinct = p->current.anDLt[i] + 1;
      u64 iVal = (p->nRow + nDistinct - 1) / nDistinct;
      if( iVal==2 && p->nRow*10 <= nDistinct*11 ) iVal = 1;
      sqlite3_str_appendf(&sStat, " %llu", iVal);
#ifdef SQLITE_ENABLE_STAT4
      assert( p->current.anEq[i] );
#endif
    }
    sqlite3ResultStrAccum(context, &sStat);
  }
#ifdef SQLITE_ENABLE_STAT4
  else if( eCall==STAT_GET_ROWID ){
    if( p->iGet<0 ){
      samplePushPrevious(p, 0);
118808
118809
118810
118811
118812
118813
118814










118815
118816
118817
118818
118819
118820
118821
      else if( sqlite3_strglob("costmult=[0-9]*",z)==0 ){
        pIndex->pTable->costMult = sqlite3LogEst(sqlite3Atoi(z+9));
      }
#endif
      while( z[0]!=0 && z[0]!=' ' ) z++;
      while( z[0]==' ' ) z++;
    }










  }
}

/*
** This callback is invoked once for each index when reading the
** sqlite_stat1 table.
**







>
>
>
>
>
>
>
>
>
>







119226
119227
119228
119229
119230
119231
119232
119233
119234
119235
119236
119237
119238
119239
119240
119241
119242
119243
119244
119245
119246
119247
119248
119249
      else if( sqlite3_strglob("costmult=[0-9]*",z)==0 ){
        pIndex->pTable->costMult = sqlite3LogEst(sqlite3Atoi(z+9));
      }
#endif
      while( z[0]!=0 && z[0]!=' ' ) z++;
      while( z[0]==' ' ) z++;
    }

    /* Set the bLowQual flag if the peak number of rows obtained
    ** from a full equality match is so large that a full table scan
    ** seems likely to be faster than using the index.
    */
    if( aLog[0] > 66              /* Index has more than 100 rows */
     && aLog[0] <= aLog[nOut-1]   /* And only a single value seen */
    ){
      pIndex->bLowQual = 1;
    }
  }
}

/*
** This callback is invoked once for each index when reading the
** sqlite_stat1 table.
**
120854
120855
120856
120857
120858
120859
120860
120861
120862
120863
120864
120865
120866
120867
120868
/*
** Return the expression associated with a column.  The expression might be
** the DEFAULT clause or the AS clause of a generated column.
** Return NULL if the column has no associated expression.
*/
SQLITE_PRIVATE Expr *sqlite3ColumnExpr(Table *pTab, Column *pCol){
  if( pCol->iDflt==0 ) return 0;
  if( NEVER(!IsOrdinaryTable(pTab)) ) return 0;
  if( NEVER(pTab->u.tab.pDfltList==0) ) return 0;
  if( NEVER(pTab->u.tab.pDfltList->nExpr<pCol->iDflt) ) return 0;
  return pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr;
}

/*
** Set the collating sequence name for a column.







|







121282
121283
121284
121285
121286
121287
121288
121289
121290
121291
121292
121293
121294
121295
121296
/*
** Return the expression associated with a column.  The expression might be
** the DEFAULT clause or the AS clause of a generated column.
** Return NULL if the column has no associated expression.
*/
SQLITE_PRIVATE Expr *sqlite3ColumnExpr(Table *pTab, Column *pCol){
  if( pCol->iDflt==0 ) return 0;
  if( !IsOrdinaryTable(pTab) ) return 0;
  if( NEVER(pTab->u.tab.pDfltList==0) ) return 0;
  if( NEVER(pTab->u.tab.pDfltList->nExpr<pCol->iDflt) ) return 0;
  return pTab->u.tab.pDfltList->a[pCol->iDflt-1].pExpr;
}

/*
** Set the collating sequence name for a column.
121006
121007
121008
121009
121010
121011
121012



121013
121014
121015
121016
121017
121018
121019
}
SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
  /* Do not delete the table until the reference count reaches zero. */
  assert( db!=0 );
  if( !pTable ) return;
  if( db->pnBytesFreed==0 && (--pTable->nTabRef)>0 ) return;
  deleteTable(db, pTable);



}


/*
** Unlink the given table from the hash tables and the delete the
** table structure with all its indices and foreign keys.
*/







>
>
>







121434
121435
121436
121437
121438
121439
121440
121441
121442
121443
121444
121445
121446
121447
121448
121449
121450
}
SQLITE_PRIVATE void sqlite3DeleteTable(sqlite3 *db, Table *pTable){
  /* Do not delete the table until the reference count reaches zero. */
  assert( db!=0 );
  if( !pTable ) return;
  if( db->pnBytesFreed==0 && (--pTable->nTabRef)>0 ) return;
  deleteTable(db, pTable);
}
SQLITE_PRIVATE void sqlite3DeleteTableGeneric(sqlite3 *db, void *pTable){
  sqlite3DeleteTable(db, (Table*)pTable);
}


/*
** Unlink the given table from the hash tables and the delete the
** table structure with all its indices and foreign keys.
*/
121544
121545
121546
121547
121548
121549
121550
121551

121552
121553
121554
121555
121556
121557
121558
  }
}
#endif

/*
** Clean up the data structures associated with the RETURNING clause.
*/
static void sqlite3DeleteReturning(sqlite3 *db, Returning *pRet){

  Hash *pHash;
  pHash = &(db->aDb[1].pSchema->trigHash);
  sqlite3HashInsert(pHash, pRet->zName, 0);
  sqlite3ExprListDelete(db, pRet->pReturnEL);
  sqlite3DbFree(db, pRet);
}








|
>







121975
121976
121977
121978
121979
121980
121981
121982
121983
121984
121985
121986
121987
121988
121989
121990
  }
}
#endif

/*
** Clean up the data structures associated with the RETURNING clause.
*/
static void sqlite3DeleteReturning(sqlite3 *db, void *pArg){
  Returning *pRet = (Returning*)pArg;
  Hash *pHash;
  pHash = &(db->aDb[1].pSchema->trigHash);
  sqlite3HashInsert(pHash, pRet->zName, 0);
  sqlite3ExprListDelete(db, pRet->pReturnEL);
  sqlite3DbFree(db, pRet);
}

121586
121587
121588
121589
121590
121591
121592
121593
121594
121595
121596
121597
121598
121599
121600
121601
  if( pRet==0 ){
    sqlite3ExprListDelete(db, pList);
    return;
  }
  pParse->u1.pReturning = pRet;
  pRet->pParse = pParse;
  pRet->pReturnEL = pList;
  sqlite3ParserAddCleanup(pParse,
     (void(*)(sqlite3*,void*))sqlite3DeleteReturning, pRet);
  testcase( pParse->earlyCleanup );
  if( db->mallocFailed ) return;
  sqlite3_snprintf(sizeof(pRet->zName), pRet->zName,
                   "sqlite_returning_%p", pParse);
  pRet->retTrig.zName = pRet->zName;
  pRet->retTrig.op = TK_RETURNING;
  pRet->retTrig.tr_tm = TRIGGER_AFTER;







|
<







122018
122019
122020
122021
122022
122023
122024
122025

122026
122027
122028
122029
122030
122031
122032
  if( pRet==0 ){
    sqlite3ExprListDelete(db, pList);
    return;
  }
  pParse->u1.pReturning = pRet;
  pRet->pParse = pParse;
  pRet->pReturnEL = pList;
  sqlite3ParserAddCleanup(pParse, sqlite3DeleteReturning, pRet);

  testcase( pParse->earlyCleanup );
  if( db->mallocFailed ) return;
  sqlite3_snprintf(sizeof(pRet->zName), pRet->zName,
                   "sqlite_returning_%p", pParse);
  pRet->retTrig.zName = pRet->zName;
  pRet->retTrig.op = TK_RETURNING;
  pRet->retTrig.tr_tm = TRIGGER_AFTER;
121786
121787
121788
121789
121790
121791
121792

121793
121794
121795
121796
121797
121798
121799
121800
SQLITE_PRIVATE char sqlite3AffinityType(const char *zIn, Column *pCol){
  u32 h = 0;
  char aff = SQLITE_AFF_NUMERIC;
  const char *zChar = 0;

  assert( zIn!=0 );
  while( zIn[0] ){

    h = (h<<8) + sqlite3UpperToLower[(*zIn)&0xff];
    zIn++;
    if( h==(('c'<<24)+('h'<<16)+('a'<<8)+'r') ){             /* CHAR */
      aff = SQLITE_AFF_TEXT;
      zChar = zIn;
    }else if( h==(('c'<<24)+('l'<<16)+('o'<<8)+'b') ){       /* CLOB */
      aff = SQLITE_AFF_TEXT;
    }else if( h==(('t'<<24)+('e'<<16)+('x'<<8)+'t') ){       /* TEXT */







>
|







122217
122218
122219
122220
122221
122222
122223
122224
122225
122226
122227
122228
122229
122230
122231
122232
SQLITE_PRIVATE char sqlite3AffinityType(const char *zIn, Column *pCol){
  u32 h = 0;
  char aff = SQLITE_AFF_NUMERIC;
  const char *zChar = 0;

  assert( zIn!=0 );
  while( zIn[0] ){
    u8 x = *(u8*)zIn;
    h = (h<<8) + sqlite3UpperToLower[x];
    zIn++;
    if( h==(('c'<<24)+('h'<<16)+('a'<<8)+'r') ){             /* CHAR */
      aff = SQLITE_AFF_TEXT;
      zChar = zIn;
    }else if( h==(('c'<<24)+('l'<<16)+('o'<<8)+'b') ){       /* CLOB */
      aff = SQLITE_AFF_TEXT;
    }else if( h==(('t'<<24)+('e'<<16)+('x'<<8)+'t') ){       /* TEXT */
125651
125652
125653
125654
125655
125656
125657
125658
125659
125660
125661
125662
125663
125664
125665
125666
125667

125668
125669
125670
125671
125672
125673
125674
    }
    sqlite3DbFree(db, zColl);
  }
  iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pObjName);
  if( iDb<0 ) return;
  z = sqlite3NameFromToken(db, pObjName);
  if( z==0 ) return;
  zDb = db->aDb[iDb].zDbSName;
  pTab = sqlite3FindTable(db, z, zDb);
  if( pTab ){
    reindexTable(pParse, pTab, 0);
    sqlite3DbFree(db, z);
    return;
  }
  pIndex = sqlite3FindIndex(db, z, zDb);
  sqlite3DbFree(db, z);
  if( pIndex ){

    sqlite3BeginWriteOperation(pParse, 0, iDb);
    sqlite3RefillIndex(pParse, pIndex, -1);
    return;
  }
  sqlite3ErrorMsg(pParse, "unable to identify the object to be reindexed");
}
#endif







|









>







126083
126084
126085
126086
126087
126088
126089
126090
126091
126092
126093
126094
126095
126096
126097
126098
126099
126100
126101
126102
126103
126104
126105
126106
126107
    }
    sqlite3DbFree(db, zColl);
  }
  iDb = sqlite3TwoPartName(pParse, pName1, pName2, &pObjName);
  if( iDb<0 ) return;
  z = sqlite3NameFromToken(db, pObjName);
  if( z==0 ) return;
  zDb = pName2->n ? db->aDb[iDb].zDbSName : 0;
  pTab = sqlite3FindTable(db, z, zDb);
  if( pTab ){
    reindexTable(pParse, pTab, 0);
    sqlite3DbFree(db, z);
    return;
  }
  pIndex = sqlite3FindIndex(db, z, zDb);
  sqlite3DbFree(db, z);
  if( pIndex ){
    iDb = sqlite3SchemaToIndex(db, pIndex->pTable->pSchema);
    sqlite3BeginWriteOperation(pParse, 0, iDb);
    sqlite3RefillIndex(pParse, pIndex, -1);
    return;
  }
  sqlite3ErrorMsg(pParse, "unable to identify the object to be reindexed");
}
#endif
125825
125826
125827
125828
125829
125830
125831



125832
125833
125834
125835
125836
125837
125838
  if( pWith ){
    int i;
    for(i=0; i<pWith->nCte; i++){
      cteClear(db, &pWith->a[i]);
    }
    sqlite3DbFree(db, pWith);
  }



}
#endif /* !defined(SQLITE_OMIT_CTE) */

/************** End of build.c ***********************************************/
/************** Begin file callback.c ****************************************/
/*
** 2005 May 23







>
>
>







126258
126259
126260
126261
126262
126263
126264
126265
126266
126267
126268
126269
126270
126271
126272
126273
126274
  if( pWith ){
    int i;
    for(i=0; i<pWith->nCte; i++){
      cteClear(db, &pWith->a[i]);
    }
    sqlite3DbFree(db, pWith);
  }
}
SQLITE_PRIVATE void sqlite3WithDeleteGeneric(sqlite3 *db, void *pWith){
  sqlite3WithDelete(db, (With*)pWith);
}
#endif /* !defined(SQLITE_OMIT_CTE) */

/************** End of build.c ***********************************************/
/************** Begin file callback.c ****************************************/
/*
** 2005 May 23
139051
139052
139053
139054
139055
139056
139057

139058
139059
139060
139061
139062
139063
139064
139065
          if( pTab->u.vtab.p==0 ) continue;
          pVTab = pTab->u.vtab.p->pVtab;
          if( NEVER(pVTab==0) ) continue;
          if( NEVER(pVTab->pModule==0) ) continue;
          if( pVTab->pModule->iVersion<4 ) continue;
          if( pVTab->pModule->xIntegrity==0 ) continue;
          sqlite3VdbeAddOp3(v, OP_VCheck, i, 3, isQuick);

          sqlite3VdbeAppendP4(v, pTab, P4_TABLE);
          a1 = sqlite3VdbeAddOp1(v, OP_IsNull, 3); VdbeCoverage(v);
          integrityCheckResultRow(v);
          sqlite3VdbeJumpHere(v, a1);
#endif
          continue;
        }
        if( isQuick || HasRowid(pTab) ){







>
|







139487
139488
139489
139490
139491
139492
139493
139494
139495
139496
139497
139498
139499
139500
139501
139502
          if( pTab->u.vtab.p==0 ) continue;
          pVTab = pTab->u.vtab.p->pVtab;
          if( NEVER(pVTab==0) ) continue;
          if( NEVER(pVTab->pModule==0) ) continue;
          if( pVTab->pModule->iVersion<4 ) continue;
          if( pVTab->pModule->xIntegrity==0 ) continue;
          sqlite3VdbeAddOp3(v, OP_VCheck, i, 3, isQuick);
          pTab->nTabRef++;
          sqlite3VdbeAppendP4(v, pTab, P4_TABLEREF);
          a1 = sqlite3VdbeAddOp1(v, OP_IsNull, 3); VdbeCoverage(v);
          integrityCheckResultRow(v);
          sqlite3VdbeJumpHere(v, a1);
#endif
          continue;
        }
        if( isQuick || HasRowid(pTab) ){
141078
141079
141080
141081
141082
141083
141084

141085
141086
141087
141088
141089
141090
141091
  }while( (rc==SQLITE_ERROR_RETRY && (cnt++)<SQLITE_MAX_PREPARE_RETRY)
       || (rc==SQLITE_SCHEMA && (sqlite3ResetOneSchema(db,-1), cnt++)==0) );
  sqlite3BtreeLeaveAll(db);
  rc = sqlite3ApiExit(db, rc);
  assert( (rc&db->errMask)==rc );
  db->busyHandler.nBusy = 0;
  sqlite3_mutex_leave(db->mutex);

  return rc;
}


/*
** Rerun the compilation of a statement after a schema change.
**







>







141515
141516
141517
141518
141519
141520
141521
141522
141523
141524
141525
141526
141527
141528
141529
  }while( (rc==SQLITE_ERROR_RETRY && (cnt++)<SQLITE_MAX_PREPARE_RETRY)
       || (rc==SQLITE_SCHEMA && (sqlite3ResetOneSchema(db,-1), cnt++)==0) );
  sqlite3BtreeLeaveAll(db);
  rc = sqlite3ApiExit(db, rc);
  assert( (rc&db->errMask)==rc );
  db->busyHandler.nBusy = 0;
  sqlite3_mutex_leave(db->mutex);
  assert( rc==SQLITE_OK || (*ppStmt)==0 );
  return rc;
}


/*
** Rerun the compilation of a statement after a schema change.
**
141474
141475
141476
141477
141478
141479
141480



141481
141482
141483
141484
141485
141486
141487


/*
** Delete the given Select structure and all of its substructures.
*/
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3 *db, Select *p){
  if( OK_IF_ALWAYS_TRUE(p) ) clearSelect(db, p, 1);



}

/*
** Return a pointer to the right-most SELECT statement in a compound.
*/
static Select *findRightmost(Select *p){
  while( p->pNext ) p = p->pNext;







>
>
>







141912
141913
141914
141915
141916
141917
141918
141919
141920
141921
141922
141923
141924
141925
141926
141927
141928


/*
** Delete the given Select structure and all of its substructures.
*/
SQLITE_PRIVATE void sqlite3SelectDelete(sqlite3 *db, Select *p){
  if( OK_IF_ALWAYS_TRUE(p) ) clearSelect(db, p, 1);
}
SQLITE_PRIVATE void sqlite3SelectDeleteGeneric(sqlite3 *db, void *p){
  if( ALWAYS(p) ) clearSelect(db, (Select*)p, 1);
}

/*
** Return a pointer to the right-most SELECT statement in a compound.
*/
static Select *findRightmost(Select *p){
  while( p->pNext ) p = p->pNext;
144495
144496
144497
144498
144499
144500
144501
144502
144503
144504
144505
144506
144507
144508
144509
144510
144511
    sqlite3KeyInfoUnref(pKeyInfo);
  }

multi_select_end:
  pDest->iSdst = dest.iSdst;
  pDest->nSdst = dest.nSdst;
  if( pDelete ){
    sqlite3ParserAddCleanup(pParse,
        (void(*)(sqlite3*,void*))sqlite3SelectDelete,
        pDelete);
  }
  return rc;
}
#endif /* SQLITE_OMIT_COMPOUND_SELECT */

/*
** Error message for when two or more terms of a compound select have different







|
<
<







144936
144937
144938
144939
144940
144941
144942
144943


144944
144945
144946
144947
144948
144949
144950
    sqlite3KeyInfoUnref(pKeyInfo);
  }

multi_select_end:
  pDest->iSdst = dest.iSdst;
  pDest->nSdst = dest.nSdst;
  if( pDelete ){
    sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pDelete);


  }
  return rc;
}
#endif /* SQLITE_OMIT_COMPOUND_SELECT */

/*
** Error message for when two or more terms of a compound select have different
145048
145049
145050
145051
145052
145053
145054
145055
145056
145057
145058
145059
145060
145061
145062
145063
  /* Jump to the this point in order to terminate the query.
  */
  sqlite3VdbeResolveLabel(v, labelEnd);

  /* Make arrangements to free the 2nd and subsequent arms of the compound
  ** after the parse has finished */
  if( pSplit->pPrior ){
    sqlite3ParserAddCleanup(pParse,
       (void(*)(sqlite3*,void*))sqlite3SelectDelete, pSplit->pPrior);
  }
  pSplit->pPrior = pPrior;
  pPrior->pNext = pSplit;
  sqlite3ExprListDelete(db, pPrior->pOrderBy);
  pPrior->pOrderBy = 0;

  /*** TBD:  Insert subroutine calls to close cursors on incomplete







|
<







145487
145488
145489
145490
145491
145492
145493
145494

145495
145496
145497
145498
145499
145500
145501
  /* Jump to the this point in order to terminate the query.
  */
  sqlite3VdbeResolveLabel(v, labelEnd);

  /* Make arrangements to free the 2nd and subsequent arms of the compound
  ** after the parse has finished */
  if( pSplit->pPrior ){
    sqlite3ParserAddCleanup(pParse, sqlite3SelectDeleteGeneric, pSplit->pPrior);

  }
  pSplit->pPrior = pPrior;
  pPrior->pNext = pSplit;
  sqlite3ExprListDelete(db, pPrior->pOrderBy);
  pPrior->pOrderBy = 0;

  /*** TBD:  Insert subroutine calls to close cursors on incomplete
145870
145871
145872
145873
145874
145875
145876
145877
145878
145879
145880
145881
145882
145883
145884
145885
145886
  **
  ** pSubitem->pTab is always non-NULL by test restrictions and tests above.
  */
  if( ALWAYS(pSubitem->pTab!=0) ){
    Table *pTabToDel = pSubitem->pTab;
    if( pTabToDel->nTabRef==1 ){
      Parse *pToplevel = sqlite3ParseToplevel(pParse);
      sqlite3ParserAddCleanup(pToplevel,
         (void(*)(sqlite3*,void*))sqlite3DeleteTable,
         pTabToDel);
      testcase( pToplevel->earlyCleanup );
    }else{
      pTabToDel->nTabRef--;
    }
    pSubitem->pTab = 0;
  }








|
<
<







146308
146309
146310
146311
146312
146313
146314
146315


146316
146317
146318
146319
146320
146321
146322
  **
  ** pSubitem->pTab is always non-NULL by test restrictions and tests above.
  */
  if( ALWAYS(pSubitem->pTab!=0) ){
    Table *pTabToDel = pSubitem->pTab;
    if( pTabToDel->nTabRef==1 ){
      Parse *pToplevel = sqlite3ParseToplevel(pParse);
      sqlite3ParserAddCleanup(pToplevel, sqlite3DeleteTableGeneric, pTabToDel);


      testcase( pToplevel->earlyCleanup );
    }else{
      pTabToDel->nTabRef--;
    }
    pSubitem->pTab = 0;
  }

146919
146920
146921
146922
146923
146924
146925
146926
146927
146928
146929
146930
146931
146932
146933
146934
**
** If bFree is true, do not continue to use the pWith pointer after
** calling this routine,  Instead, use only the return value.
*/
SQLITE_PRIVATE With *sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){
  if( pWith ){
    if( bFree ){
      pWith = (With*)sqlite3ParserAddCleanup(pParse,
                      (void(*)(sqlite3*,void*))sqlite3WithDelete,
                      pWith);
      if( pWith==0 ) return 0;
    }
    if( pParse->nErr==0 ){
      assert( pParse->pWith!=pWith );
      pWith->pOuter = pParse->pWith;
      pParse->pWith = pWith;







|
<







147355
147356
147357
147358
147359
147360
147361
147362

147363
147364
147365
147366
147367
147368
147369
**
** If bFree is true, do not continue to use the pWith pointer after
** calling this routine,  Instead, use only the return value.
*/
SQLITE_PRIVATE With *sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){
  if( pWith ){
    if( bFree ){
      pWith = (With*)sqlite3ParserAddCleanup(pParse, sqlite3WithDeleteGeneric,

                      pWith);
      if( pWith==0 ) return 0;
    }
    if( pParse->nErr==0 ){
      assert( pParse->pWith!=pWith );
      pWith->pOuter = pParse->pWith;
      pParse->pWith = pWith;
147953
147954
147955
147956
147957
147958
147959

147960
147961
147962
147963
147964
147965
147966
147967
147968



147969
147970
147971
147972
147973
147974
147975
    if( pFunc->iOBTab>=0 ){
      ExprList *pOBList;
      KeyInfo *pKeyInfo;
      int nExtra = 0;
      assert( pFunc->pFExpr->pLeft!=0 );
      assert( pFunc->pFExpr->pLeft->op==TK_ORDER );
      assert( ExprUseXList(pFunc->pFExpr->pLeft) );

      pOBList = pFunc->pFExpr->pLeft->x.pList;
      if( !pFunc->bOBUnique ){
        nExtra++;  /* One extra column for the OP_Sequence */
      }
      if( pFunc->bOBPayload ){
        /* extra columns for the function arguments */
        assert( ExprUseXList(pFunc->pFExpr) );
        nExtra += pFunc->pFExpr->x.pList->nExpr;
      }



      pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOBList, 0, nExtra);
      if( !pFunc->bOBUnique && pParse->nErr==0 ){
        pKeyInfo->nKeyField++;
      }
      sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
            pFunc->iOBTab, pOBList->nExpr+nExtra, 0,
            (char*)pKeyInfo, P4_KEYINFO);







>









>
>
>







148388
148389
148390
148391
148392
148393
148394
148395
148396
148397
148398
148399
148400
148401
148402
148403
148404
148405
148406
148407
148408
148409
148410
148411
148412
148413
148414
    if( pFunc->iOBTab>=0 ){
      ExprList *pOBList;
      KeyInfo *pKeyInfo;
      int nExtra = 0;
      assert( pFunc->pFExpr->pLeft!=0 );
      assert( pFunc->pFExpr->pLeft->op==TK_ORDER );
      assert( ExprUseXList(pFunc->pFExpr->pLeft) );
      assert( pFunc->pFunc!=0 );
      pOBList = pFunc->pFExpr->pLeft->x.pList;
      if( !pFunc->bOBUnique ){
        nExtra++;  /* One extra column for the OP_Sequence */
      }
      if( pFunc->bOBPayload ){
        /* extra columns for the function arguments */
        assert( ExprUseXList(pFunc->pFExpr) );
        nExtra += pFunc->pFExpr->x.pList->nExpr;
      }
      if( pFunc->bUseSubtype ){
        nExtra += pFunc->pFExpr->x.pList->nExpr;
      }
      pKeyInfo = sqlite3KeyInfoFromExprList(pParse, pOBList, 0, nExtra);
      if( !pFunc->bOBUnique && pParse->nErr==0 ){
        pKeyInfo->nKeyField++;
      }
      sqlite3VdbeAddOp4(v, OP_OpenEphemeral,
            pFunc->iOBTab, pOBList->nExpr+nExtra, 0,
            (char*)pKeyInfo, P4_KEYINFO);
147988
147989
147990
147991
147992
147993
147994
147995
147996
147997
147998
147999
148000
148001
148002
148003
148004

148005
148006
148007
148008
148009
148010
148011
148012
148013
148014
148015
148016
148017
148018
148019
148020









148021
148022
148023
148024
148025
148026
148027
  int i;
  struct AggInfo_func *pF;
  for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){
    ExprList *pList;
    assert( ExprUseXList(pF->pFExpr) );
    pList = pF->pFExpr->x.pList;
    if( pF->iOBTab>=0 ){
      /* For an ORDER BY aggregate, calls to OP_AggStep where deferred and
      ** all content was stored in emphermal table pF->iOBTab.  Extract that
      ** content now (in ORDER BY order) and make all calls to OP_AggStep
      ** before doing the OP_AggFinal call. */
      int iTop;        /* Start of loop for extracting columns */
      int nArg;        /* Number of columns to extract */
      int nKey;        /* Key columns to be skipped */
      int regAgg;      /* Extract into this array */
      int j;           /* Loop counter */


      nArg = pList->nExpr;
      regAgg = sqlite3GetTempRange(pParse, nArg);

      if( pF->bOBPayload==0 ){
        nKey = 0;
      }else{
        assert( pF->pFExpr->pLeft!=0 );
        assert( ExprUseXList(pF->pFExpr->pLeft) );
        assert( pF->pFExpr->pLeft->x.pList!=0 );
        nKey = pF->pFExpr->pLeft->x.pList->nExpr;
        if( ALWAYS(!pF->bOBUnique) ) nKey++;
      }
      iTop = sqlite3VdbeAddOp1(v, OP_Rewind, pF->iOBTab); VdbeCoverage(v);
      for(j=nArg-1; j>=0; j--){
        sqlite3VdbeAddOp3(v, OP_Column, pF->iOBTab, nKey+j, regAgg+j);
      }









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







|
|
|







>
















>
>
>
>
>
>
>
>
>







148427
148428
148429
148430
148431
148432
148433
148434
148435
148436
148437
148438
148439
148440
148441
148442
148443
148444
148445
148446
148447
148448
148449
148450
148451
148452
148453
148454
148455
148456
148457
148458
148459
148460
148461
148462
148463
148464
148465
148466
148467
148468
148469
148470
148471
148472
148473
148474
148475
148476
  int i;
  struct AggInfo_func *pF;
  for(i=0, pF=pAggInfo->aFunc; i<pAggInfo->nFunc; i++, pF++){
    ExprList *pList;
    assert( ExprUseXList(pF->pFExpr) );
    pList = pF->pFExpr->x.pList;
    if( pF->iOBTab>=0 ){
      /* For an ORDER BY aggregate, calls to OP_AggStep were deferred.  Inputs
      ** were stored in emphermal table pF->iOBTab.  Here, we extract those
      ** inputs (in ORDER BY order) and make all calls to OP_AggStep
      ** before doing the OP_AggFinal call. */
      int iTop;        /* Start of loop for extracting columns */
      int nArg;        /* Number of columns to extract */
      int nKey;        /* Key columns to be skipped */
      int regAgg;      /* Extract into this array */
      int j;           /* Loop counter */

      assert( pF->pFunc!=0 );
      nArg = pList->nExpr;
      regAgg = sqlite3GetTempRange(pParse, nArg);

      if( pF->bOBPayload==0 ){
        nKey = 0;
      }else{
        assert( pF->pFExpr->pLeft!=0 );
        assert( ExprUseXList(pF->pFExpr->pLeft) );
        assert( pF->pFExpr->pLeft->x.pList!=0 );
        nKey = pF->pFExpr->pLeft->x.pList->nExpr;
        if( ALWAYS(!pF->bOBUnique) ) nKey++;
      }
      iTop = sqlite3VdbeAddOp1(v, OP_Rewind, pF->iOBTab); VdbeCoverage(v);
      for(j=nArg-1; j>=0; j--){
        sqlite3VdbeAddOp3(v, OP_Column, pF->iOBTab, nKey+j, regAgg+j);
      }
      if( pF->bUseSubtype ){
        int regSubtype = sqlite3GetTempReg(pParse);
        int iBaseCol = nKey + nArg + (pF->bOBPayload==0 && pF->bOBUnique==0);
        for(j=nArg-1; j>=0; j--){
          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);
    }
148068
148069
148070
148071
148072
148073
148074

148075
148076
148077
148078
148079
148080
148081
    int addrNext = 0;
    int regAgg;
    int regAggSz = 0;
    int regDistinct = 0;
    ExprList *pList;
    assert( ExprUseXList(pF->pFExpr) );
    assert( !IsWindowFunc(pF->pFExpr) );

    pList = pF->pFExpr->x.pList;
    if( ExprHasProperty(pF->pFExpr, EP_WinFunc) ){
      Expr *pFilter = pF->pFExpr->y.pWin->pFilter;
      if( pAggInfo->nAccumulator
       && (pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)
       && regAcc
      ){







>







148517
148518
148519
148520
148521
148522
148523
148524
148525
148526
148527
148528
148529
148530
148531
    int addrNext = 0;
    int regAgg;
    int regAggSz = 0;
    int regDistinct = 0;
    ExprList *pList;
    assert( ExprUseXList(pF->pFExpr) );
    assert( !IsWindowFunc(pF->pFExpr) );
    assert( pF->pFunc!=0 );
    pList = pF->pFExpr->x.pList;
    if( ExprHasProperty(pF->pFExpr, EP_WinFunc) ){
      Expr *pFilter = pF->pFExpr->y.pWin->pFilter;
      if( pAggInfo->nAccumulator
       && (pF->pFunc->funcFlags & SQLITE_FUNC_NEEDCOLL)
       && regAcc
      ){
148111
148112
148113
148114
148115
148116
148117



148118
148119
148120
148121
148122
148123
148124
148125
148126
148127
148128
148129
148130








148131
148132
148133
148134
148135
148136
148137
      assert( pOBList->nExpr>0 );
      regAggSz = pOBList->nExpr;
      if( !pF->bOBUnique ){
        regAggSz++;   /* One register for OP_Sequence */
      }
      if( pF->bOBPayload ){
        regAggSz += nArg;



      }
      regAggSz++;  /* One extra register to hold result of MakeRecord */
      regAgg = sqlite3GetTempRange(pParse, regAggSz);
      regDistinct = regAgg;
      sqlite3ExprCodeExprList(pParse, pOBList, regAgg, 0, SQLITE_ECEL_DUP);
      jj = pOBList->nExpr;
      if( !pF->bOBUnique ){
        sqlite3VdbeAddOp2(v, OP_Sequence, pF->iOBTab, regAgg+jj);
        jj++;
      }
      if( pF->bOBPayload ){
        regDistinct = regAgg+jj;
        sqlite3ExprCodeExprList(pParse, pList, regDistinct, 0, SQLITE_ECEL_DUP);








      }
    }else if( pList ){
      nArg = pList->nExpr;
      regAgg = sqlite3GetTempRange(pParse, nArg);
      regDistinct = regAgg;
      sqlite3ExprCodeExprList(pParse, pList, regAgg, 0, SQLITE_ECEL_DUP);
    }else{







>
>
>













>
>
>
>
>
>
>
>







148561
148562
148563
148564
148565
148566
148567
148568
148569
148570
148571
148572
148573
148574
148575
148576
148577
148578
148579
148580
148581
148582
148583
148584
148585
148586
148587
148588
148589
148590
148591
148592
148593
148594
148595
148596
148597
148598
      assert( pOBList->nExpr>0 );
      regAggSz = pOBList->nExpr;
      if( !pF->bOBUnique ){
        regAggSz++;   /* One register for OP_Sequence */
      }
      if( pF->bOBPayload ){
        regAggSz += nArg;
      }
      if( pF->bUseSubtype ){
        regAggSz += nArg;
      }
      regAggSz++;  /* One extra register to hold result of MakeRecord */
      regAgg = sqlite3GetTempRange(pParse, regAggSz);
      regDistinct = regAgg;
      sqlite3ExprCodeExprList(pParse, pOBList, regAgg, 0, SQLITE_ECEL_DUP);
      jj = pOBList->nExpr;
      if( !pF->bOBUnique ){
        sqlite3VdbeAddOp2(v, OP_Sequence, pF->iOBTab, regAgg+jj);
        jj++;
      }
      if( pF->bOBPayload ){
        regDistinct = regAgg+jj;
        sqlite3ExprCodeExprList(pParse, pList, regDistinct, 0, SQLITE_ECEL_DUP);
        jj += nArg;
      }
      if( pF->bUseSubtype ){
        int kk;
        int regBase = pF->bOBPayload ? regDistinct : regAgg;
        for(kk=0; kk<nArg; kk++, jj++){
          sqlite3VdbeAddOp2(v, OP_GetSubtype, regBase+kk, regAgg+jj);
        }
      }
    }else if( pList ){
      nArg = pList->nExpr;
      regAgg = sqlite3GetTempRange(pParse, nArg);
      regDistinct = regAgg;
      sqlite3ExprCodeExprList(pParse, pList, regAgg, 0, SQLITE_ECEL_DUP);
    }else{
148328
148329
148330
148331
148332
148333
148334
148335

148336
148337
148338
148339
148340
148341
148342
  }
  return 0;
}

/*
** Deallocate a single AggInfo object
*/
static void agginfoFree(sqlite3 *db, AggInfo *p){

  sqlite3DbFree(db, p->aCol);
  sqlite3DbFree(db, p->aFunc);
  sqlite3DbFreeNN(db, p);
}

/*
** Attempt to transform a query of the form







|
>







148789
148790
148791
148792
148793
148794
148795
148796
148797
148798
148799
148800
148801
148802
148803
148804
  }
  return 0;
}

/*
** Deallocate a single AggInfo object
*/
static void agginfoFree(sqlite3 *db, void *pArg){
  AggInfo *p = (AggInfo*)pArg;
  sqlite3DbFree(db, p->aCol);
  sqlite3DbFree(db, p->aFunc);
  sqlite3DbFreeNN(db, p);
}

/*
** Attempt to transform a query of the form
148402
148403
148404
148405
148406
148407
148408
148409
148410
148411
148412
148413
148414
148415
148416
    Expr *pTerm;
    pPrior = pSub->pPrior;
    pSub->pPrior = 0;
    pSub->pNext = 0;
    pSub->selFlags |= SF_Aggregate;
    pSub->selFlags &= ~SF_Compound;
    pSub->nSelectRow = 0;
    sqlite3ExprListDelete(db, pSub->pEList);
    pTerm = pPrior ? sqlite3ExprDup(db, pCount, 0) : pCount;
    pSub->pEList = sqlite3ExprListAppend(pParse, 0, pTerm);
    pTerm = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
    sqlite3PExprAddSelect(pParse, pTerm, pSub);
    if( pExpr==0 ){
      pExpr = pTerm;
    }else{







|







148864
148865
148866
148867
148868
148869
148870
148871
148872
148873
148874
148875
148876
148877
148878
    Expr *pTerm;
    pPrior = pSub->pPrior;
    pSub->pPrior = 0;
    pSub->pNext = 0;
    pSub->selFlags |= SF_Aggregate;
    pSub->selFlags &= ~SF_Compound;
    pSub->nSelectRow = 0;
    sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric, pSub->pEList);
    pTerm = pPrior ? sqlite3ExprDup(db, pCount, 0) : pCount;
    pSub->pEList = sqlite3ExprListAppend(pParse, 0, pTerm);
    pTerm = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
    sqlite3PExprAddSelect(pParse, pTerm, pSub);
    if( pExpr==0 ){
      pExpr = pTerm;
    }else{
148582
148583
148584
148585
148586
148587
148588
148589
148590
148591
148592
148593
148594
148595
148596
148597
148598
    if( p->pOrderBy ){
#if TREETRACE_ENABLED
      TREETRACE(0x800,pParse,p, ("dropping superfluous ORDER BY:\n"));
      if( sqlite3TreeTrace & 0x800 ){
        sqlite3TreeViewExprList(0, p->pOrderBy, 0, "ORDERBY");
      }
#endif
      sqlite3ParserAddCleanup(pParse,
        (void(*)(sqlite3*,void*))sqlite3ExprListDelete,
        p->pOrderBy);
      testcase( pParse->earlyCleanup );
      p->pOrderBy = 0;
    }
    p->selFlags &= ~SF_Distinct;
    p->selFlags |= SF_NoopOrderBy;
  }
  sqlite3SelectPrep(pParse, p, 0);







|
<
|







149044
149045
149046
149047
149048
149049
149050
149051

149052
149053
149054
149055
149056
149057
149058
149059
    if( p->pOrderBy ){
#if TREETRACE_ENABLED
      TREETRACE(0x800,pParse,p, ("dropping superfluous ORDER BY:\n"));
      if( sqlite3TreeTrace & 0x800 ){
        sqlite3TreeViewExprList(0, p->pOrderBy, 0, "ORDERBY");
      }
#endif
      sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric,

                              p->pOrderBy);
      testcase( pParse->earlyCleanup );
      p->pOrderBy = 0;
    }
    p->selFlags &= ~SF_Distinct;
    p->selFlags |= SF_NoopOrderBy;
  }
  sqlite3SelectPrep(pParse, p, 0);
148776
148777
148778
148779
148780
148781
148782
148783
148784
148785
148786
148787
148788
148789
148790
148791
148792
     && pSub->pLimit==0                           /* Condition (1) */
     && (pSub->selFlags & SF_OrderByReqd)==0      /* Condition (2) */
     && (p->selFlags & SF_OrderByReqd)==0         /* Condition (3) and (4) */
     && OptimizationEnabled(db, SQLITE_OmitOrderBy)
    ){
      TREETRACE(0x800,pParse,p,
                ("omit superfluous ORDER BY on %r FROM-clause subquery\n",i+1));
      sqlite3ParserAddCleanup(pParse,
         (void(*)(sqlite3*,void*))sqlite3ExprListDelete,
         pSub->pOrderBy);
      pSub->pOrderBy = 0;
    }

    /* If the outer query contains a "complex" result set (that is,
    ** if the result set of the outer query uses functions or subqueries)
    ** and if the subquery contains an ORDER BY clause and if
    ** it will be implemented as a co-routine, then do not flatten.  This







|
<
|







149237
149238
149239
149240
149241
149242
149243
149244

149245
149246
149247
149248
149249
149250
149251
149252
     && pSub->pLimit==0                           /* Condition (1) */
     && (pSub->selFlags & SF_OrderByReqd)==0      /* Condition (2) */
     && (p->selFlags & SF_OrderByReqd)==0         /* Condition (3) and (4) */
     && OptimizationEnabled(db, SQLITE_OmitOrderBy)
    ){
      TREETRACE(0x800,pParse,p,
                ("omit superfluous ORDER BY on %r FROM-clause subquery\n",i+1));
      sqlite3ParserAddCleanup(pParse, sqlite3ExprListDeleteGeneric,

                              pSub->pOrderBy);
      pSub->pOrderBy = 0;
    }

    /* If the outer query contains a "complex" result set (that is,
    ** if the result set of the outer query uses functions or subqueries)
    ** and if the subquery contains an ORDER BY clause and if
    ** it will be implemented as a co-routine, then do not flatten.  This
149307
149308
149309
149310
149311
149312
149313
149314
149315
149316
149317
149318
149319
149320
149321
149322

    /* Convert TK_COLUMN nodes into TK_AGG_COLUMN and make entries in
    ** sAggInfo for all TK_AGG_FUNCTION nodes in expressions of the
    ** SELECT statement.
    */
    pAggInfo = sqlite3DbMallocZero(db, sizeof(*pAggInfo) );
    if( pAggInfo ){
      sqlite3ParserAddCleanup(pParse,
          (void(*)(sqlite3*,void*))agginfoFree, pAggInfo);
      testcase( pParse->earlyCleanup );
    }
    if( db->mallocFailed ){
      goto select_end;
    }
    pAggInfo->selId = p->selId;
#ifdef SQLITE_DEBUG







|
<







149767
149768
149769
149770
149771
149772
149773
149774

149775
149776
149777
149778
149779
149780
149781

    /* Convert TK_COLUMN nodes into TK_AGG_COLUMN and make entries in
    ** sAggInfo for all TK_AGG_FUNCTION nodes in expressions of the
    ** SELECT statement.
    */
    pAggInfo = sqlite3DbMallocZero(db, sizeof(*pAggInfo) );
    if( pAggInfo ){
      sqlite3ParserAddCleanup(pParse, agginfoFree, pAggInfo);

      testcase( pParse->earlyCleanup );
    }
    if( db->mallocFailed ){
      goto select_end;
    }
    pAggInfo->selId = p->selId;
#ifdef SQLITE_DEBUG
153957
153958
153959
153960
153961
153962
153963
153964
153965
153966
153967
153968
153969
153970
153971
  VTable *p = db->pDisconnect;

  assert( sqlite3BtreeHoldsAllMutexes(db) );
  assert( sqlite3_mutex_held(db->mutex) );

  if( p ){
    db->pDisconnect = 0;
    sqlite3ExpirePreparedStatements(db, 0);
    do {
      VTable *pNext = p->pNext;
      sqlite3VtabUnlock(p);
      p = pNext;
    }while( p );
  }
}







<







154416
154417
154418
154419
154420
154421
154422

154423
154424
154425
154426
154427
154428
154429
  VTable *p = db->pDisconnect;

  assert( sqlite3BtreeHoldsAllMutexes(db) );
  assert( sqlite3_mutex_held(db->mutex) );

  if( p ){
    db->pDisconnect = 0;

    do {
      VTable *pNext = p->pNext;
      sqlite3VtabUnlock(p);
      p = pNext;
    }while( p );
  }
}
155523
155524
155525
155526
155527
155528
155529
155530
155531
155532
155533
155534
155535
155536
155537
**
** where.c:
*/
SQLITE_PRIVATE Bitmask sqlite3WhereGetMask(WhereMaskSet*,int);
#ifdef WHERETRACE_ENABLED
SQLITE_PRIVATE void sqlite3WhereClausePrint(WhereClause *pWC);
SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm);
SQLITE_PRIVATE void sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC);
#endif
SQLITE_PRIVATE WhereTerm *sqlite3WhereFindTerm(
  WhereClause *pWC,     /* The WHERE clause to be searched */
  int iCur,             /* Cursor number of LHS */
  int iColumn,          /* Column number of LHS */
  Bitmask notReady,     /* RHS must not overlap with this mask */
  u32 op,               /* Mask of WO_xx values describing operator */







|







155981
155982
155983
155984
155985
155986
155987
155988
155989
155990
155991
155992
155993
155994
155995
**
** where.c:
*/
SQLITE_PRIVATE Bitmask sqlite3WhereGetMask(WhereMaskSet*,int);
#ifdef WHERETRACE_ENABLED
SQLITE_PRIVATE void sqlite3WhereClausePrint(WhereClause *pWC);
SQLITE_PRIVATE void sqlite3WhereTermPrint(WhereTerm *pTerm, int iTerm);
SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC);
#endif
SQLITE_PRIVATE WhereTerm *sqlite3WhereFindTerm(
  WhereClause *pWC,     /* The WHERE clause to be searched */
  int iCur,             /* Cursor number of LHS */
  int iColumn,          /* Column number of LHS */
  Bitmask notReady,     /* RHS must not overlap with this mask */
  u32 op,               /* Mask of WO_xx values describing operator */
160985
160986
160987
160988
160989
160990
160991





160992
160993
160994
160995
160996
160997





160998
160999
161000
161001
161002
161003
161004
  Vdbe *v = pParse->pVdbe;
  VdbeOp *pOp = sqlite3VdbeGetOp(v, iStart);
  int iEnd = sqlite3VdbeCurrentAddr(v);
  if( pParse->db->mallocFailed ) return;
  for(; iStart<iEnd; iStart++, pOp++){
    if( pOp->p1!=iTabCur ) continue;
    if( pOp->opcode==OP_Column ){





      pOp->opcode = OP_Copy;
      pOp->p1 = pOp->p2 + iRegister;
      pOp->p2 = pOp->p3;
      pOp->p3 = 0;
      pOp->p5 = 2;  /* Cause the MEM_Subtype flag to be cleared */
    }else if( pOp->opcode==OP_Rowid ){





      pOp->opcode = OP_Sequence;
      pOp->p1 = iAutoidxCur;
#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
      if( iAutoidxCur==0 ){
        pOp->opcode = OP_Null;
        pOp->p3 = 0;
      }







>
>
>
>
>






>
>
>
>
>







161443
161444
161445
161446
161447
161448
161449
161450
161451
161452
161453
161454
161455
161456
161457
161458
161459
161460
161461
161462
161463
161464
161465
161466
161467
161468
161469
161470
161471
161472
  Vdbe *v = pParse->pVdbe;
  VdbeOp *pOp = sqlite3VdbeGetOp(v, iStart);
  int iEnd = sqlite3VdbeCurrentAddr(v);
  if( pParse->db->mallocFailed ) return;
  for(; iStart<iEnd; iStart++, pOp++){
    if( pOp->p1!=iTabCur ) continue;
    if( pOp->opcode==OP_Column ){
#ifdef SQLITE_DEBUG
      if( pParse->db->flags & SQLITE_VdbeAddopTrace ){
        printf("TRANSLATE OP_Column to OP_Copy at %d\n", iStart);
      }
#endif
      pOp->opcode = OP_Copy;
      pOp->p1 = pOp->p2 + iRegister;
      pOp->p2 = pOp->p3;
      pOp->p3 = 0;
      pOp->p5 = 2;  /* Cause the MEM_Subtype flag to be cleared */
    }else if( pOp->opcode==OP_Rowid ){
#ifdef SQLITE_DEBUG
      if( pParse->db->flags & SQLITE_VdbeAddopTrace ){
        printf("TRANSLATE OP_Rowid to OP_Sequence at %d\n", iStart);
      }
#endif
      pOp->opcode = OP_Sequence;
      pOp->p1 = iAutoidxCur;
#ifdef SQLITE_ALLOW_ROWID_IN_VIEW
      if( iAutoidxCur==0 ){
        pOp->opcode = OP_Null;
        pOp->p3 = 0;
      }
162317
162318
162319
162320
162321
162322
162323
162324

162325
162326
162327
162328
162329
162330
162331
      if( rc==SQLITE_OK ){
        if( iUpper>iLower ){
          nNew = sqlite3LogEst(iUpper - iLower);
          /* TUNING:  If both iUpper and iLower are derived from the same
          ** sample, then assume they are 4x more selective.  This brings
          ** the estimated selectivity more in line with what it would be
          ** if estimated without the use of STAT4 tables. */
          if( iLwrIdx==iUprIdx ) nNew -= 20;  assert( 20==sqlite3LogEst(4) );

        }else{
          nNew = 10;        assert( 10==sqlite3LogEst(2) );
        }
        if( nNew<nOut ){
          nOut = nNew;
        }
        WHERETRACE(0x20, ("STAT4 range scan: %u..%u  est=%d\n",







|
>







162785
162786
162787
162788
162789
162790
162791
162792
162793
162794
162795
162796
162797
162798
162799
162800
      if( rc==SQLITE_OK ){
        if( iUpper>iLower ){
          nNew = sqlite3LogEst(iUpper - iLower);
          /* TUNING:  If both iUpper and iLower are derived from the same
          ** sample, then assume they are 4x more selective.  This brings
          ** the estimated selectivity more in line with what it would be
          ** if estimated without the use of STAT4 tables. */
          if( iLwrIdx==iUprIdx ){ nNew -= 20; }
          assert( 20==sqlite3LogEst(4) );
        }else{
          nNew = 10;        assert( 10==sqlite3LogEst(2) );
        }
        if( nNew<nOut ){
          nOut = nNew;
        }
        WHERETRACE(0x20, ("STAT4 range scan: %u..%u  est=%d\n",
162541
162542
162543
162544
162545
162546
162547












162548
162549

162550
162551
162552
162553
162554
162555
162556
162557
162558




162559
162560
162561
162562
162563
162564
162565
  }
}
#endif

#ifdef WHERETRACE_ENABLED
/*
** Print a WhereLoop object for debugging purposes












*/
SQLITE_PRIVATE void sqlite3WhereLoopPrint(WhereLoop *p, WhereClause *pWC){

  WhereInfo *pWInfo = pWC->pWInfo;
  int nb = 1+(pWInfo->pTabList->nSrc+3)/4;
  SrcItem *pItem = pWInfo->pTabList->a + p->iTab;
  Table *pTab = pItem->pTab;
  Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1;
  sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId,
                     p->iTab, nb, p->maskSelf, nb, p->prereq & mAll);
  sqlite3DebugPrintf(" %12s",
                     pItem->zAlias ? pItem->zAlias : pTab->zName);




  if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){
    const char *zName;
    if( p->u.btree.pIndex && (zName = p->u.btree.pIndex->zName)!=0 ){
      if( strncmp(zName, "sqlite_autoindex_", 17)==0 ){
        int i = sqlite3Strlen30(zName) - 1;
        while( zName[i]!='_' ) i--;
        zName += i;







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

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







163010
163011
163012
163013
163014
163015
163016
163017
163018
163019
163020
163021
163022
163023
163024
163025
163026
163027
163028
163029
163030
163031
163032
163033
163034
163035
163036
163037
163038
163039
163040
163041
163042
163043
163044
163045
163046
163047
163048
163049
163050
163051
  }
}
#endif

#ifdef WHERETRACE_ENABLED
/*
** Print a WhereLoop object for debugging purposes
**
** Format example:
**
**     .--- Position in WHERE clause           rSetup, rRun, nOut ---.
**     |                                                             |
**     |  .--- selfMask                       nTerm ------.          |
**     |  |                                               |          |
**     |  |   .-- prereq    Idx          wsFlags----.     |          |
**     |  |   |             Name                    |     |          |
**     |  |   |           __|__        nEq ---.  ___|__   |        __|__
**     | / \ / \         /     \              | /      \ / \      /     \
**     1.002.001         t2.t2xy              2 f 010241 N 2 cost 0,56,31
*/
SQLITE_PRIVATE void sqlite3WhereLoopPrint(const WhereLoop *p, const WhereClause *pWC){
  if( pWC ){
    WhereInfo *pWInfo = pWC->pWInfo;
    int nb = 1+(pWInfo->pTabList->nSrc+3)/4;
    SrcItem *pItem = pWInfo->pTabList->a + p->iTab;
    Table *pTab = pItem->pTab;
    Bitmask mAll = (((Bitmask)1)<<(nb*4)) - 1;
    sqlite3DebugPrintf("%c%2d.%0*llx.%0*llx", p->cId,
                       p->iTab, nb, p->maskSelf, nb, p->prereq & mAll);
    sqlite3DebugPrintf(" %12s",
                       pItem->zAlias ? pItem->zAlias : pTab->zName);
  }else{
    sqlite3DebugPrintf("%c%2d.%03llx.%03llx %c%d",
         p->cId, p->iTab, p->maskSelf, p->prereq & 0xfff, p->cId, p->iTab);
  }
  if( (p->wsFlags & WHERE_VIRTUALTABLE)==0 ){
    const char *zName;
    if( p->u.btree.pIndex && (zName = p->u.btree.pIndex->zName)!=0 ){
      if( strncmp(zName, "sqlite_autoindex_", 17)==0 ){
        int i = sqlite3Strlen30(zName) - 1;
        while( zName[i]!='_' ) i--;
        zName += i;
162587
162588
162589
162590
162591
162592
162593









162594
162595
162596
162597
162598
162599
162600
  sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut);
  if( p->nLTerm && (sqlite3WhereTrace & 0x4000)!=0 ){
    int i;
    for(i=0; i<p->nLTerm; i++){
      sqlite3WhereTermPrint(p->aLTerm[i], i);
    }
  }









}
#endif

/*
** Convert bulk memory into a valid WhereLoop that can be passed
** to whereLoopClear harmlessly.
*/







>
>
>
>
>
>
>
>
>







163073
163074
163075
163076
163077
163078
163079
163080
163081
163082
163083
163084
163085
163086
163087
163088
163089
163090
163091
163092
163093
163094
163095
  sqlite3DebugPrintf(" cost %d,%d,%d\n", p->rSetup, p->rRun, p->nOut);
  if( p->nLTerm && (sqlite3WhereTrace & 0x4000)!=0 ){
    int i;
    for(i=0; i<p->nLTerm; i++){
      sqlite3WhereTermPrint(p->aLTerm[i], i);
    }
  }
}
SQLITE_PRIVATE void sqlite3ShowWhereLoop(const WhereLoop *p){
  if( p ) sqlite3WhereLoopPrint(p, 0);
}
SQLITE_PRIVATE void sqlite3ShowWhereLoopList(const WhereLoop *p){
  while( p ){
    sqlite3ShowWhereLoop(p);
    p = p->pNextLoop;
  }
}
#endif

/*
** Convert bulk memory into a valid WhereLoop that can be passed
** to whereLoopClear harmlessly.
*/
162700
162701
162702
162703
162704
162705
162706
162707



162708












162709
162710
162711
162712
162713
162714
162715
162716
162717
162718
162719
162720
162721
162722
162723
162724
162725
162726
162727
162728
162729









162730
162731
162732
162733
162734
162735
162736
162737
162738
162739
162740
162741
162742
162743
162744
162745
162746
162747
162748
162749
162750
162751
162752
162753
    sqlite3DbNNFreeNN(db, pWInfo->pMemToFree);
    pWInfo->pMemToFree = pNext;
  }
  sqlite3DbNNFreeNN(db, pWInfo);
}

/*
** Return TRUE if all of the following are true:



**












**   (1)  X has the same or lower cost, or returns the same or fewer rows,
**        than Y.
**   (2)  X uses fewer WHERE clause terms than Y
**   (3)  Every WHERE clause term used by X is also used by Y
**   (4)  X skips at least as many columns as Y
**   (5)  If X is a covering index, than Y is too
**
** Conditions (2) and (3) mean that X is a "proper subset" of Y.
** If X is a proper subset of Y then Y is a better choice and ought
** to have a lower cost.  This routine returns TRUE when that cost
** relationship is inverted and needs to be adjusted.  Constraint (4)
** was added because if X uses skip-scan less than Y it still might
** deserve a lower cost even if it is a proper subset of Y.  Constraint (5)
** was added because a covering index probably deserves to have a lower cost
** than a non-covering index even if it is a proper subset.
*/
static int whereLoopCheaperProperSubset(
  const WhereLoop *pX,       /* First WhereLoop to compare */
  const WhereLoop *pY        /* Compare against this WhereLoop */
){
  int i, j;









  if( pX->nLTerm-pX->nSkip >= pY->nLTerm-pY->nSkip ){
    return 0; /* X is not a subset of Y */
  }
  if( pX->rRun>pY->rRun && pX->nOut>pY->nOut ) return 0;
  if( pY->nSkip > pX->nSkip ) return 0;
  for(i=pX->nLTerm-1; i>=0; i--){
    if( pX->aLTerm[i]==0 ) continue;
    for(j=pY->nLTerm-1; j>=0; j--){
      if( pY->aLTerm[j]==pX->aLTerm[i] ) break;
    }
    if( j<0 ) return 0;  /* X not a subset of Y since term X[i] not used by Y */
  }
  if( (pX->wsFlags&WHERE_IDX_ONLY)!=0
   && (pY->wsFlags&WHERE_IDX_ONLY)==0 ){
    return 0;  /* Constraint (5) */
  }
  return 1;  /* All conditions meet */
}

/*
** Try to adjust the cost and number of output rows of WhereLoop pTemplate
** upwards or downwards so that:
**
**   (1) pTemplate costs less than any other WhereLoops that are a proper







|
>
>
>

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






>
>
>
>
>
>
>
>
>

|

<
|





|



|

|







163195
163196
163197
163198
163199
163200
163201
163202
163203
163204
163205
163206
163207
163208
163209
163210
163211
163212
163213
163214
163215
163216
163217
163218
163219
163220
163221
163222
163223
163224









163225
163226
163227
163228
163229
163230
163231
163232
163233
163234
163235
163236
163237
163238
163239
163240
163241
163242

163243
163244
163245
163246
163247
163248
163249
163250
163251
163252
163253
163254
163255
163256
163257
163258
163259
163260
163261
163262
    sqlite3DbNNFreeNN(db, pWInfo->pMemToFree);
    pWInfo->pMemToFree = pNext;
  }
  sqlite3DbNNFreeNN(db, pWInfo);
}

/*
** Return TRUE if X is a proper subset of Y but is of equal or less cost.
** In other words, return true if all constraints of X are also part of Y
** and Y has additional constraints that might speed the search that X lacks
** but the cost of running X is not more than the cost of running Y.
**
** In other words, return true if the cost relationwship between X and Y
** is inverted and needs to be adjusted.
**
** Case 1:
**
**   (1a)  X and Y use the same index.
**   (1b)  X has fewer == terms than Y
**   (1c)  Neither X nor Y use skip-scan
**   (1d)  X does not have a a greater cost than Y
**
** Case 2:
**
**   (2a)  X has the same or lower cost, or returns the same or fewer rows,
**         than Y.
**   (2b)  X uses fewer WHERE clause terms than Y
**   (2c)  Every WHERE clause term used by X is also used by Y
**   (2d)  X skips at least as many columns as Y
**   (2e)  If X is a covering index, than Y is too









*/
static int whereLoopCheaperProperSubset(
  const WhereLoop *pX,       /* First WhereLoop to compare */
  const WhereLoop *pY        /* Compare against this WhereLoop */
){
  int i, j;
  if( pX->rRun>pY->rRun && pX->nOut>pY->nOut ) return 0; /* (1d) and (2a) */
  assert( (pX->wsFlags & WHERE_VIRTUALTABLE)==0 );
  assert( (pY->wsFlags & WHERE_VIRTUALTABLE)==0 );
  if( pX->u.btree.nEq < pY->u.btree.nEq                  /* (1b) */
   && pX->u.btree.pIndex==pY->u.btree.pIndex             /* (1a) */
   && pX->nSkip==0 && pY->nSkip==0                       /* (1c) */
  ){
    return 1;  /* Case 1 is true */
  }
  if( pX->nLTerm-pX->nSkip >= pY->nLTerm-pY->nSkip ){
    return 0;                                            /* (2b) */
  }

  if( pY->nSkip > pX->nSkip ) return 0;                  /* (2d) */
  for(i=pX->nLTerm-1; i>=0; i--){
    if( pX->aLTerm[i]==0 ) continue;
    for(j=pY->nLTerm-1; j>=0; j--){
      if( pY->aLTerm[j]==pX->aLTerm[i] ) break;
    }
    if( j<0 ) return 0;                                  /* (2c) */
  }
  if( (pX->wsFlags&WHERE_IDX_ONLY)!=0
   && (pY->wsFlags&WHERE_IDX_ONLY)==0 ){
    return 0;                                            /* (2e) */
  }
  return 1;  /* Case 2 is true */
}

/*
** Try to adjust the cost and number of output rows of WhereLoop pTemplate
** upwards or downwards so that:
**
**   (1) pTemplate costs less than any other WhereLoops that are a proper
163229
163230
163231
163232
163233
163234
163235

163236


163237
163238
163239
163240
163241
163242
163243
  assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 );
  if( pNew->wsFlags & WHERE_BTM_LIMIT ){
    opMask = WO_LT|WO_LE;
  }else{
    assert( pNew->u.btree.nBtm==0 );
    opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS;
  }

  if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);



  assert( pNew->u.btree.nEq<pProbe->nColumn );
  assert( pNew->u.btree.nEq<pProbe->nKeyCol
       || pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY );

  saved_nEq = pNew->u.btree.nEq;
  saved_nBtm = pNew->u.btree.nBtm;







>
|
>
>







163738
163739
163740
163741
163742
163743
163744
163745
163746
163747
163748
163749
163750
163751
163752
163753
163754
163755
  assert( (pNew->wsFlags & WHERE_TOP_LIMIT)==0 );
  if( pNew->wsFlags & WHERE_BTM_LIMIT ){
    opMask = WO_LT|WO_LE;
  }else{
    assert( pNew->u.btree.nBtm==0 );
    opMask = WO_EQ|WO_IN|WO_GT|WO_GE|WO_LT|WO_LE|WO_ISNULL|WO_IS;
  }
  if( pProbe->bUnordered || pProbe->bLowQual ){
    if( pProbe->bUnordered ) opMask &= ~(WO_GT|WO_GE|WO_LT|WO_LE);
    if( pProbe->bLowQual )   opMask &= ~(WO_EQ|WO_IN|WO_IS);
  }

  assert( pNew->u.btree.nEq<pProbe->nColumn );
  assert( pNew->u.btree.nEq<pProbe->nKeyCol
       || pProbe->idxType!=SQLITE_IDXTYPE_PRIMARYKEY );

  saved_nEq = pNew->u.btree.nEq;
  saved_nBtm = pNew->u.btree.nBtm;
166309
166310
166311
166312
166313
166314
166315
166316



166317
166318
166319
166320
166321
166322
166323

  /* Variable initialization */
  db = pParse->db;
  memset(&sWLB, 0, sizeof(sWLB));

  /* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */
  testcase( pOrderBy && pOrderBy->nExpr==BMS-1 );
  if( pOrderBy && pOrderBy->nExpr>=BMS ) pOrderBy = 0;




  /* The number of tables in the FROM clause is limited by the number of
  ** bits in a Bitmask
  */
  testcase( pTabList->nSrc==BMS );
  if( pTabList->nSrc>BMS ){
    sqlite3ErrorMsg(pParse, "at most %d tables in a join", BMS);







|
>
>
>







166821
166822
166823
166824
166825
166826
166827
166828
166829
166830
166831
166832
166833
166834
166835
166836
166837
166838

  /* Variable initialization */
  db = pParse->db;
  memset(&sWLB, 0, sizeof(sWLB));

  /* An ORDER/GROUP BY clause of more than 63 terms cannot be optimized */
  testcase( pOrderBy && pOrderBy->nExpr==BMS-1 );
  if( pOrderBy && pOrderBy->nExpr>=BMS ){
    pOrderBy = 0;
    wctrlFlags &= ~WHERE_WANT_DISTINCT;
  }

  /* The number of tables in the FROM clause is limited by the number of
  ** bits in a Bitmask
  */
  testcase( pTabList->nSrc==BMS );
  if( pTabList->nSrc>BMS ){
    sqlite3ErrorMsg(pParse, "at most %d tables in a join", BMS);
166334
166335
166336
166337
166338
166339
166340
166341



166342
166343
166344
166345
166346
166347
166348
  /* Allocate and initialize the WhereInfo structure that will become the
  ** return value. A single allocation is used to store the WhereInfo
  ** struct, the contents of WhereInfo.a[], the WhereClause structure
  ** and the WhereMaskSet structure. Since WhereClause contains an 8-byte
  ** field (type Bitmask) it must be aligned on an 8-byte boundary on
  ** some architectures. Hence the ROUND8() below.
  */
  nByteWInfo = ROUND8P(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel));



  pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop));
  if( db->mallocFailed ){
    sqlite3DbFree(db, pWInfo);
    pWInfo = 0;
    goto whereBeginError;
  }
  pWInfo->pParse = pParse;







|
>
>
>







166849
166850
166851
166852
166853
166854
166855
166856
166857
166858
166859
166860
166861
166862
166863
166864
166865
166866
  /* Allocate and initialize the WhereInfo structure that will become the
  ** return value. A single allocation is used to store the WhereInfo
  ** struct, the contents of WhereInfo.a[], the WhereClause structure
  ** and the WhereMaskSet structure. Since WhereClause contains an 8-byte
  ** field (type Bitmask) it must be aligned on an 8-byte boundary on
  ** some architectures. Hence the ROUND8() below.
  */
  nByteWInfo = ROUND8P(sizeof(WhereInfo));
  if( nTabList>1 ){
    nByteWInfo = ROUND8P(nByteWInfo + (nTabList-1)*sizeof(WhereLevel));
  }
  pWInfo = sqlite3DbMallocRawNN(db, nByteWInfo + sizeof(WhereLoop));
  if( db->mallocFailed ){
    sqlite3DbFree(db, pWInfo);
    pWInfo = 0;
    goto whereBeginError;
  }
  pWInfo->pParse = pParse;
166896
166897
166898
166899
166900
166901
166902





166903
166904
166905
166906
166907
166908
166909

  /* Jump here if malloc fails */
whereBeginError:
  if( pWInfo ){
    pParse->nQueryLoop = pWInfo->savedNQueryLoop;
    whereInfoFree(db, pWInfo);
  }





  return 0;
}

/*
** Part of sqlite3WhereEnd() will rewrite opcodes to reference the
** index rather than the main table.  In SQLITE_DEBUG mode, we want
** to trace those changes if PRAGMA vdbe_addoptrace=on.  This routine







>
>
>
>
>







167414
167415
167416
167417
167418
167419
167420
167421
167422
167423
167424
167425
167426
167427
167428
167429
167430
167431
167432

  /* Jump here if malloc fails */
whereBeginError:
  if( pWInfo ){
    pParse->nQueryLoop = pWInfo->savedNQueryLoop;
    whereInfoFree(db, pWInfo);
  }
#ifdef WHERETRACE_ENABLED
  /* Prevent harmless compiler warnings about debugging routines
  ** being declared but never used */
  sqlite3ShowWhereLoopList(0);
#endif /* WHERETRACE_ENABLED */
  return 0;
}

/*
** Part of sqlite3WhereEnd() will rewrite opcodes to reference the
** index rather than the main table.  In SQLITE_DEBUG mode, we want
** to trace those changes if PRAGMA vdbe_addoptrace=on.  This routine
182232
182233
182234
182235
182236
182237
182238






















182239
182240
182241
182242
182243
182244
182245
        *piValue = Tuning(-id);
      }else{
        rc = SQLITE_NOTFOUND;
      }
      break;
    }
#endif






















  }
  va_end(ap);
#endif /* SQLITE_UNTESTABLE */
  return rc;
}

/*







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







182755
182756
182757
182758
182759
182760
182761
182762
182763
182764
182765
182766
182767
182768
182769
182770
182771
182772
182773
182774
182775
182776
182777
182778
182779
182780
182781
182782
182783
182784
182785
182786
182787
182788
182789
182790
        *piValue = Tuning(-id);
      }else{
        rc = SQLITE_NOTFOUND;
      }
      break;
    }
#endif

    /* sqlite3_test_control(SQLITE_TESTCTRL_JSON_SELFCHECK, &onOff);
    **
    ** Activate or deactivate validation of JSONB that is generated from
    ** text.  Off by default, as the validation is slow.  Validation is
    ** only available if compiled using SQLITE_DEBUG.
    **
    ** If onOff is initially 1, then turn it on.  If onOff is initially
    ** off, turn it off.  If onOff is initially -1, then change onOff
    ** to be the current setting.
    */
    case SQLITE_TESTCTRL_JSON_SELFCHECK: {
#if defined(SQLITE_DEBUG) && !defined(SQLITE_OMIT_WSD)
      int *pOnOff = va_arg(ap, int*);
      if( *pOnOff<0 ){
        *pOnOff = sqlite3Config.bJsonSelfcheck;
      }else{
        sqlite3Config.bJsonSelfcheck = (u8)((*pOnOff)&0xff);
      }
#endif
      break;
    }
  }
  va_end(ap);
#endif /* SQLITE_UNTESTABLE */
  return rc;
}

/*
184215
184216
184217
184218
184219
184220
184221


184222
184223
184224
184225
184226
184227
184228
#ifndef SQLITE_DISABLE_FTS3_UNICODE
SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int, int);
SQLITE_PRIVATE int sqlite3FtsUnicodeIsalnum(int);
SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int);
#endif

SQLITE_PRIVATE int sqlite3Fts3ExprIterate(Fts3Expr*, int (*x)(Fts3Expr*,int,void*), void*);



#endif /* !SQLITE_CORE || SQLITE_ENABLE_FTS3 */
#endif /* _FTSINT_H */

/************** End of fts3Int.h *********************************************/
/************** Continuing where we left off in fts3.c ***********************/
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)







>
>







184760
184761
184762
184763
184764
184765
184766
184767
184768
184769
184770
184771
184772
184773
184774
184775
#ifndef SQLITE_DISABLE_FTS3_UNICODE
SQLITE_PRIVATE int sqlite3FtsUnicodeFold(int, int);
SQLITE_PRIVATE int sqlite3FtsUnicodeIsalnum(int);
SQLITE_PRIVATE int sqlite3FtsUnicodeIsdiacritic(int);
#endif

SQLITE_PRIVATE int sqlite3Fts3ExprIterate(Fts3Expr*, int (*x)(Fts3Expr*,int,void*), void*);

SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk);

#endif /* !SQLITE_CORE || SQLITE_ENABLE_FTS3 */
#endif /* _FTSINT_H */

/************** End of fts3Int.h *********************************************/
/************** Continuing where we left off in fts3.c ***********************/
#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3)
187938
187939
187940
187941
187942
187943
187944
187945
187946
187947
187948
187949
187950
187951
187952
187953
187954
187955
187956
187957
187958
187959
187960
187961
187962
187963
187964
187965
187966
187967
187968
187969
187970
187971
187972
187973
187974



187975
187976
187977
187978
187979
187980
187981
187982
187983
  return 0;
}

/*
** Implementation of the xIntegrity() method on the FTS3/FTS4 virtual
** table.
*/
static int fts3Integrity(
  sqlite3_vtab *pVtab,      /* The virtual table to be checked */
  const char *zSchema,      /* Name of schema in which pVtab lives */
  const char *zTabname,     /* Name of the pVTab table */
  int isQuick,              /* True if this is a quick_check */
  char **pzErr              /* Write error message here */
){
  Fts3Table *p = (Fts3Table*)pVtab;
  char *zSql;
  int rc;
  char *zErr = 0;

  assert( pzErr!=0 );
  assert( *pzErr==0 );
  UNUSED_PARAMETER(isQuick);
  zSql = sqlite3_mprintf(
            "INSERT INTO \"%w\".\"%w\"(\"%w\") VALUES('integrity-check');",
            zSchema, zTabname, zTabname);
  if( zSql==0 ){
    return SQLITE_NOMEM;
  }
  rc = sqlite3_exec(p->db, zSql, 0, 0, &zErr);
  sqlite3_free(zSql);
  if( (rc&0xff)==SQLITE_CORRUPT ){
    *pzErr = sqlite3_mprintf("malformed inverted index for FTS%d table %s.%s",
                p->bFts4 ? 4 : 3, zSchema, zTabname);
  }else if( rc!=SQLITE_OK ){
    *pzErr = sqlite3_mprintf("unable to validate the inverted index for"
                             " FTS%d table %s.%s: %s",
                p->bFts4 ? 4 : 3, zSchema, zTabname, zErr);



  }
  sqlite3_free(zErr);
  return SQLITE_OK;
}



static const sqlite3_module fts3Module = {
  /* iVersion      */ 4,







|







<

|

<
<

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


|
>
>
>

|







188485
188486
188487
188488
188489
188490
188491
188492
188493
188494
188495
188496
188497
188498
188499

188500
188501
188502


188503
188504







188505


188506
188507
188508
188509
188510
188511
188512
188513
188514
188515
188516
188517
188518
188519
188520
188521
  return 0;
}

/*
** Implementation of the xIntegrity() method on the FTS3/FTS4 virtual
** table.
*/
static int fts3IntegrityMethod(
  sqlite3_vtab *pVtab,      /* The virtual table to be checked */
  const char *zSchema,      /* Name of schema in which pVtab lives */
  const char *zTabname,     /* Name of the pVTab table */
  int isQuick,              /* True if this is a quick_check */
  char **pzErr              /* Write error message here */
){
  Fts3Table *p = (Fts3Table*)pVtab;

  int rc;
  int bOk = 0;



  UNUSED_PARAMETER(isQuick);
  rc = sqlite3Fts3IntegrityCheck(p, &bOk);







  assert( rc!=SQLITE_CORRUPT_VTAB || bOk==0 );


  if( rc!=SQLITE_OK && rc!=SQLITE_CORRUPT_VTAB ){
    *pzErr = sqlite3_mprintf("unable to validate the inverted index for"
                             " FTS%d table %s.%s: %s",
                p->bFts4 ? 4 : 3, zSchema, zTabname, sqlite3_errstr(rc));
  }else if( bOk==0 ){
    *pzErr = sqlite3_mprintf("malformed inverted index for FTS%d table %s.%s",
                p->bFts4 ? 4 : 3, zSchema, zTabname);
  }
  sqlite3Fts3SegmentsClose(p);
  return SQLITE_OK;
}



static const sqlite3_module fts3Module = {
  /* iVersion      */ 4,
188000
188001
188002
188003
188004
188005
188006
188007
188008
188009
188010
188011
188012
188013
188014
  /* xRollback     */ fts3RollbackMethod,
  /* xFindFunction */ fts3FindFunctionMethod,
  /* xRename */       fts3RenameMethod,
  /* xSavepoint    */ fts3SavepointMethod,
  /* xRelease      */ fts3ReleaseMethod,
  /* xRollbackTo   */ fts3RollbackToMethod,
  /* xShadowName   */ fts3ShadowName,
  /* xIntegrity    */ fts3Integrity,
};

/*
** This function is registered as the module destructor (called when an
** FTS3 enabled database connection is closed). It frees the memory
** allocated for the tokenizer hash table.
*/







|







188538
188539
188540
188541
188542
188543
188544
188545
188546
188547
188548
188549
188550
188551
188552
  /* xRollback     */ fts3RollbackMethod,
  /* xFindFunction */ fts3FindFunctionMethod,
  /* xRename */       fts3RenameMethod,
  /* xSavepoint    */ fts3SavepointMethod,
  /* xRelease      */ fts3ReleaseMethod,
  /* xRollbackTo   */ fts3RollbackToMethod,
  /* xShadowName   */ fts3ShadowName,
  /* xIntegrity    */ fts3IntegrityMethod,
};

/*
** This function is registered as the module destructor (called when an
** FTS3 enabled database connection is closed). It frees the memory
** allocated for the tokenizer hash table.
*/
199554
199555
199556
199557
199558
199559
199560
199561
199562
199563
199564
199565
199566
199567
199568
** content table. If no error occurs and the contents do match, set *pbOk
** to true and return SQLITE_OK. Or if the contents do not match, set *pbOk
** to false before returning.
**
** If an error occurs (e.g. an OOM or IO error), return an SQLite error
** code. The final value of *pbOk is undefined in this case.
*/
static int fts3IntegrityCheck(Fts3Table *p, int *pbOk){
  int rc = SQLITE_OK;             /* Return code */
  u64 cksum1 = 0;                 /* Checksum based on FTS index contents */
  u64 cksum2 = 0;                 /* Checksum based on %_content contents */
  sqlite3_stmt *pAllLangid = 0;   /* Statement to return all language-ids */

  /* This block calculates the checksum according to the FTS index. */
  rc = fts3SqlStmt(p, SQL_SELECT_ALL_LANGID, &pAllLangid, 0);







|







200092
200093
200094
200095
200096
200097
200098
200099
200100
200101
200102
200103
200104
200105
200106
** content table. If no error occurs and the contents do match, set *pbOk
** to true and return SQLITE_OK. Or if the contents do not match, set *pbOk
** to false before returning.
**
** If an error occurs (e.g. an OOM or IO error), return an SQLite error
** code. The final value of *pbOk is undefined in this case.
*/
SQLITE_PRIVATE int sqlite3Fts3IntegrityCheck(Fts3Table *p, int *pbOk){
  int rc = SQLITE_OK;             /* Return code */
  u64 cksum1 = 0;                 /* Checksum based on FTS index contents */
  u64 cksum2 = 0;                 /* Checksum based on %_content contents */
  sqlite3_stmt *pAllLangid = 0;   /* Statement to return all language-ids */

  /* This block calculates the checksum according to the FTS index. */
  rc = fts3SqlStmt(p, SQL_SELECT_ALL_LANGID, &pAllLangid, 0);
199632
199633
199634
199635
199636
199637
199638
199639
199640
199641
199642
199643
199644
199645
199646
        }
      }
    }

    sqlite3_finalize(pStmt);
  }

  *pbOk = (cksum1==cksum2);
  return rc;
}

/*
** Run the integrity-check. If no error occurs and the current contents of
** the FTS index are correct, return SQLITE_OK. Or, if the contents of the
** FTS index are incorrect, return SQLITE_CORRUPT_VTAB.







|







200170
200171
200172
200173
200174
200175
200176
200177
200178
200179
200180
200181
200182
200183
200184
        }
      }
    }

    sqlite3_finalize(pStmt);
  }

  *pbOk = (rc==SQLITE_OK && cksum1==cksum2);
  return rc;
}

/*
** Run the integrity-check. If no error occurs and the current contents of
** the FTS index are correct, return SQLITE_OK. Or, if the contents of the
** FTS index are incorrect, return SQLITE_CORRUPT_VTAB.
199672
199673
199674
199675
199676
199677
199678
199679
199680
199681
199682
199683
199684
199685
199686
** passed.
*/
static int fts3DoIntegrityCheck(
  Fts3Table *p                    /* FTS3 table handle */
){
  int rc;
  int bOk = 0;
  rc = fts3IntegrityCheck(p, &bOk);
  if( rc==SQLITE_OK && bOk==0 ) rc = FTS_CORRUPT_VTAB;
  return rc;
}

/*
** Handle a 'special' INSERT of the form:
**







|







200210
200211
200212
200213
200214
200215
200216
200217
200218
200219
200220
200221
200222
200223
200224
** passed.
*/
static int fts3DoIntegrityCheck(
  Fts3Table *p                    /* FTS3 table handle */
){
  int rc;
  int bOk = 0;
  rc = sqlite3Fts3IntegrityCheck(p, &bOk);
  if( rc==SQLITE_OK && bOk==0 ) rc = FTS_CORRUPT_VTAB;
  return rc;
}

/*
** Handle a 'special' INSERT of the form:
**
202646
202647
202648
202649
202650
202651
202652
202653
202654
202655
202656
202657
202658


















202659









202660













































202661





















202662



202663
202664
202665
202666

























202667
202668
202669
202670
202671
202672
202673
202674
202675
202676
202677
202678
202679
202680
202681
202682
202683
202684
202685
202686
202687
202688
202689
202690
202691
202692
202693






202694
202695


202696
202697
202698
202699
202700
202701
202702
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
******************************************************************************
**
** This SQLite JSON functions.
**
** This file began as an extension in ext/misc/json1.c in 2015.  That
** extension proved so useful that it has now been moved into the core.
**
** For the time being, all JSON is stored as pure text.  (We might add


















** a JSONB type in the future which stores a binary encoding of JSON in









** a BLOB, but there is no support for JSONB in the current implementation.













































** This implementation parses JSON text at 250 MB/s, so it is hard to see





















** how JSONB might improve on that.)



*/
#ifndef SQLITE_OMIT_JSON
/* #include "sqliteInt.h" */


























/*
** Growing our own isspace() routine this way is twice as fast as
** the library isspace() function, resulting in a 7% overall performance
** increase for the parser.  (Ubuntu14.10 gcc 4.8.4 x64 with -Os).
*/
static const char jsonIsSpace[] = {
  0, 0, 0, 0, 0, 0, 0, 0,  0, 1, 1, 0, 0, 1, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  1, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,

  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
};
#define fast_isspace(x) (jsonIsSpace[(unsigned char)x])

/*






** Characters that are special to JSON.  Control charaters,
** '"' and '\\'.


*/
static const char jsonIsOk[256] = {
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  1, 1, 0, 1, 1, 1, 1, 0,  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,







|




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




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



|




















|


>
>
>
>
>
>
|
|
>
>







203184
203185
203186
203187
203188
203189
203190
203191
203192
203193
203194
203195
203196
203197
203198
203199
203200
203201
203202
203203
203204
203205
203206
203207
203208
203209
203210
203211
203212
203213
203214
203215
203216
203217
203218
203219
203220
203221
203222
203223
203224
203225
203226
203227
203228
203229
203230
203231
203232
203233
203234
203235
203236
203237
203238
203239
203240
203241
203242
203243
203244
203245
203246
203247
203248
203249
203250
203251
203252
203253
203254
203255
203256
203257
203258
203259
203260
203261
203262
203263
203264
203265
203266
203267
203268
203269
203270
203271
203272
203273
203274
203275
203276
203277
203278
203279
203280
203281
203282
203283
203284
203285
203286
203287
203288
203289
203290
203291
203292
203293
203294
203295
203296
203297
203298
203299
203300
203301
203302
203303
203304
203305
203306
203307
203308
203309
203310
203311
203312
203313
203314
203315
203316
203317
203318
203319
203320
203321
203322
203323
203324
203325
203326
203327
203328
203329
203330
203331
203332
203333
203334
203335
203336
203337
203338
203339
203340
203341
203342
203343
203344
203345
203346
203347
203348
203349
203350
203351
203352
203353
203354
203355
203356
203357
203358
203359
203360
203361
203362
203363
203364
203365
203366
203367
203368
203369
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
******************************************************************************
**
** SQLite JSON functions.
**
** This file began as an extension in ext/misc/json1.c in 2015.  That
** extension proved so useful that it has now been moved into the core.
**
** The original design stored all JSON as pure text, canonical RFC-8259.
** Support for JSON-5 extensions was added with version 3.42.0 (2023-05-16).
** All generated JSON text still conforms strictly to RFC-8259, but text
** with JSON-5 extensions is accepted as input.
**
** Beginning with version 3.45.0 (circa 2024-01-01), these routines also
** accept BLOB values that have JSON encoded using a binary representation
** called "JSONB".  The name JSONB comes from PostgreSQL, however the on-disk
** format SQLite JSONB is completely different and incompatible with
** PostgreSQL JSONB.
**
** Decoding and interpreting JSONB is still O(N) where N is the size of
** the input, the same as text JSON.  However, the constant of proportionality
** for JSONB is much smaller due to faster parsing.  The size of each
** element in JSONB is encoded in its header, so there is no need to search
** for delimiters using persnickety syntax rules.  JSONB seems to be about
** 3x faster than text JSON as a result.  JSONB is also tends to be slightly
** smaller than text JSON, by 5% or 10%, but there are corner cases where
** JSONB can be slightly larger.  So you are not far mistaken to say that
** a JSONB blob is the same size as the equivalent RFC-8259 text.
**
**
** THE JSONB ENCODING:
**
** Every JSON element is encoded in JSONB as a header and a payload.
** The header is between 1 and 9 bytes in size.  The payload is zero
** or more bytes.
**
** The lower 4 bits of the first byte of the header determines the
** element type:
**
**    0:   NULL
**    1:   TRUE
**    2:   FALSE
**    3:   INT        -- RFC-8259 integer literal
**    4:   INT5       -- JSON5 integer literal
**    5:   FLOAT      -- RFC-8259 floating point literal
**    6:   FLOAT5     -- JSON5 floating point literal
**    7:   TEXT       -- Text literal acceptable to both SQL and JSON
**    8:   TEXTJ      -- Text containing RFC-8259 escapes
**    9:   TEXT5      -- Text containing JSON5 and/or RFC-8259 escapes
**   10:   TEXTRAW    -- Text containing unescaped syntax characters
**   11:   ARRAY
**   12:   OBJECT
**
** The other three possible values (13-15) are reserved for future
** enhancements.
**
** The upper 4 bits of the first byte determine the size of the header
** and sometimes also the size of the payload.  If X is the first byte
** of the element and if X>>4 is between 0 and 11, then the payload
** will be that many bytes in size and the header is exactly one byte
** in size.  Other four values for X>>4 (12-15) indicate that the header
** is more than one byte in size and that the payload size is determined
** by the remainder of the header, interpreted as a unsigned big-endian
** integer.
**
**   Value of X>>4         Size integer        Total header size
**   -------------     --------------------    -----------------
**        12           1 byte (0-255)                2
**        13           2 byte (0-65535)              3
**        14           4 byte (0-4294967295)         5
**        15           8 byte (0-1.8e19)             9
**
** The payload size need not be expressed in its minimal form.  For example,
** if the payload size is 10, the size can be expressed in any of 5 different
** ways: (1) (X>>4)==10, (2) (X>>4)==12 following by on 0x0a byte,
** (3) (X>>4)==13 followed by 0x00 and 0x0a, (4) (X>>4)==14 followed by
** 0x00 0x00 0x00 0x0a, or (5) (X>>4)==15 followed by 7 bytes of 0x00 and
** a single byte of 0x0a.  The shorter forms are preferred, of course, but
** sometimes when generating JSONB, the payload size is not known in advance
** and it is convenient to reserve sufficient header space to cover the
** largest possible payload size and then come back later and patch up
** the size when it becomes known, resulting in a non-minimal encoding.
**
** The value (X>>4)==15 is not actually used in the current implementation
** (as SQLite is currently unable handle BLOBs larger than about 2GB)
** but is included in the design to allow for future enhancements.
**
** The payload follows the header.  NULL, TRUE, and FALSE have no payload and
** their payload size must always be zero.  The payload for INT, INT5,
** FLOAT, FLOAT5, TEXT, TEXTJ, TEXT5, and TEXTROW is text.  Note that the
** "..." or '...' delimiters are omitted from the various text encodings.
** The payload for ARRAY and OBJECT is a list of additional elements that
** are the content for the array or object.  The payload for an OBJECT
** must be an even number of elements.  The first element of each pair is
** the label and must be of type TEXT, TEXTJ, TEXT5, or TEXTRAW.
**
** A valid JSONB blob consists of a single element, as described above.
** Usually this will be an ARRAY or OBJECT element which has many more
** elements as its content.  But the overall blob is just a single element.
**
** Input validation for JSONB blobs simply checks that the element type
** code is between 0 and 12 and that the total size of the element
** (header plus payload) is the same as the size of the BLOB.  If those
** checks are true, the BLOB is assumed to be JSONB and processing continues.
** Errors are only raised if some other miscoding is discovered during
** processing.
**
** Additional information can be found in the doc/jsonb.md file of the
** canonical SQLite source tree.
*/
#ifndef SQLITE_OMIT_JSON
/* #include "sqliteInt.h" */

/* JSONB element types
*/
#define JSONB_NULL     0   /* "null" */
#define JSONB_TRUE     1   /* "true" */
#define JSONB_FALSE    2   /* "false" */
#define JSONB_INT      3   /* integer acceptable to JSON and SQL */
#define JSONB_INT5     4   /* integer in 0x000 notation */
#define JSONB_FLOAT    5   /* float acceptable to JSON and SQL */
#define JSONB_FLOAT5   6   /* float with JSON5 extensions */
#define JSONB_TEXT     7   /* Text compatible with both JSON and SQL */
#define JSONB_TEXTJ    8   /* Text with JSON escapes */
#define JSONB_TEXT5    9   /* Text with JSON-5 escape */
#define JSONB_TEXTRAW 10   /* SQL text that needs escaping for JSON */
#define JSONB_ARRAY   11   /* An array */
#define JSONB_OBJECT  12   /* An object */

/* Human-readable names for the JSONB values.  The index for each
** string must correspond to the JSONB_* integer above.
*/
static const char * const jsonbType[] = {
  "null", "true", "false", "integer", "integer",
  "real", "real", "text",  "text",    "text",
  "text", "array", "object", "", "", "", ""
};

/*
** Growing our own isspace() routine this way is twice as fast as
** the library isspace() function, resulting in a 7% overall performance
** increase for the text-JSON parser.  (Ubuntu14.10 gcc 4.8.4 x64 with -Os).
*/
static const char jsonIsSpace[] = {
  0, 0, 0, 0, 0, 0, 0, 0,  0, 1, 1, 0, 0, 1, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  1, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,

  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
};
#define jsonIsspace(x) (jsonIsSpace[(unsigned char)x])

/*
** The set of all space characters recognized by jsonIsspace().
** Useful as the second argument to strspn().
*/
static const char jsonSpaces[] = "\011\012\015\040";

/*
** Characters that are special to JSON.  Control characters,
** '"' and '\\' and '\''.  Actually, '\'' is not special to
** canonical JSON, but it is special in JSON-5, so we include
** it in the set of special characters.
*/
static const char jsonIsOk[256] = {
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0,  0, 0, 0, 0, 0, 0, 0, 0,
  1, 1, 0, 1, 1, 1, 1, 0,  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,
202710
202711
202712
202713
202714
202715
202716
202717
202718
202719
202720
202721
202722
202723
202724
202725
202726
202727


























202728




202729
202730
202731
202732




202733
202734
202735
202736
202737
202738
202739
202740
202741
202742
202743
202744
202745
202746
202747
202748
202749
202750
202751
202752
202753
202754
202755
202756
202757
202758
202759
202760
202761
202762
202763
202764
202765


202766
202767
202768
202769
202770
202771
202772
202773
202774
202775
202776
202777
202778
202779
202780
202781
202782
202783
202784
202785
202786
202787
202788
202789
202790
202791
202792
202793
202794
202795
202796
202797
202798
202799
202800
202801
202802
202803
202804
202805
202806
202807
202808
202809
202810
202811
202812
202813

202814
202815
202816
202817
202818
202819
202820
202821
202822
202823
202824
202825
202826
202827
202828
202829


202830
202831
202832
202833
202834
202835
202836
202837
202838
202839

202840
202841
202842
202843
202844
202845
202846
202847
202848
202849
202850
202851
202852
202853
202854






202855
202856
202857
202858
202859
202860
202861

202862



202863






202864





























































































































202865
202866
202867
202868

202869
202870
202871
202872
202873
202874
202875
202876
202877
202878
202879
202880
202881
202882
202883
202884
202885
202886
202887
202888
202889
202890
202891
202892
202893
202894
202895
202896
202897
202898
202899
202900
202901
202902
202903
202904
202905
202906
202907
202908
202909
202910
202911
202912
202913
202914
202915
202916
202917
202918
202919
202920
202921
202922
202923
202924
202925
202926
202927
202928
202929
202930
202931
202932
202933
202934
202935
202936
202937
202938
202939
202940
202941
202942
202943
202944
202945
202946
202947
202948
202949
202950
202951
202952
202953
202954
202955
202956
202957
202958
202959
202960
202961
202962
202963
202964
202965
202966
202967
202968
202969
202970
202971
202972
202973
202974
202975
202976
202977
202978
202979
202980
202981
202982
202983
202984
202985








202986


202987
202988
202989
202990
202991
202992
202993
202994
202995
202996
202997
202998
202999
203000
203001
203002
203003
203004
203005
203006
203007
203008
203009
203010
203011
203012
203013
203014
203015
203016
203017
203018



203019
203020
203021



203022
203023
203024

203025









203026



















203027











203028
203029
203030
203031
203032
203033
203034
203035
203036
203037
203038
203039
203040
203041
203042
203043
203044
203045
203046
203047
203048
203049
203050
203051
203052
203053
203054
203055
203056
203057
203058


203059
203060
203061
203062
203063
203064
203065
203066
203067
203068
203069
203070
203071
203072
203073
203074
203075
203076
203077
203078
203079
203080
203081
203082
203083
203084
203085
203086
203087
203088
203089
203090
203091
203092
203093
203094
203095
203096
203097
203098
203099
203100
203101
203102
203103
203104
203105
203106
203107
203108
203109
203110
203111
203112
203113
203114
203115
203116
203117
203118
203119
203120
203121
203122
203123
203124
203125
203126
203127
203128
203129
203130
203131
203132
203133
203134
203135
203136
203137
203138
203139
203140
203141
203142
203143
203144
203145
203146
203147
203148
203149
203150
203151
203152
203153
203154
203155
203156
203157
203158
203159
203160
203161
203162
203163
203164
203165
203166
203167
203168
203169
203170
203171
203172
203173
203174
203175
203176
203177
203178
203179
203180
203181
203182
203183
203184
203185
203186
203187
203188
203189
203190
203191
203192
203193
203194
203195
203196
203197
203198
203199
203200
203201
203202
203203
203204
203205
  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
};


#if !defined(SQLITE_DEBUG) && !defined(SQLITE_COVERAGE_TEST)
#  define VVA(X)
#else
#  define VVA(X) X
#endif

/* Objects */
typedef struct JsonString JsonString;
typedef struct JsonNode JsonNode;
typedef struct JsonParse JsonParse;


























typedef struct JsonCleanup JsonCleanup;





/* An instance of this object represents a JSON string
** under construction.  Really, this is a generic string accumulator
** that can be and is used to create strings other than JSON.




*/
struct JsonString {
  sqlite3_context *pCtx;   /* Function context - put error messages here */
  char *zBuf;              /* Append JSON content here */
  u64 nAlloc;              /* Bytes of storage available in zBuf[] */
  u64 nUsed;               /* Bytes of zBuf[] currently used */
  u8 bStatic;              /* True if zBuf is static space */
  u8 bErr;                 /* True if an error has been encountered */
  char zSpace[100];        /* Initial static space */
};

/* A deferred cleanup task.  A list of JsonCleanup objects might be
** run when the JsonParse object is destroyed.
*/
struct JsonCleanup {
  JsonCleanup *pJCNext;    /* Next in a list */
  void (*xOp)(void*);      /* Routine to run */
  void *pArg;              /* Argument to xOp() */
};

/* JSON type values
*/
#define JSON_SUBST    0    /* Special edit node.  Uses u.iPrev */
#define JSON_NULL     1
#define JSON_TRUE     2
#define JSON_FALSE    3
#define JSON_INT      4
#define JSON_REAL     5
#define JSON_STRING   6
#define JSON_ARRAY    7
#define JSON_OBJECT   8

/* The "subtype" set for JSON values */


#define JSON_SUBTYPE  74    /* Ascii for "J" */

/*
** Names of the various JSON types:
*/
static const char * const jsonType[] = {
  "subst",
  "null", "true", "false", "integer", "real", "text", "array", "object"
};

/* Bit values for the JsonNode.jnFlag field
*/
#define JNODE_RAW     0x01  /* Content is raw, not JSON encoded */
#define JNODE_ESCAPE  0x02  /* Content is text with \ escapes */
#define JNODE_REMOVE  0x04  /* Do not output */
#define JNODE_REPLACE 0x08  /* Target of a JSON_SUBST node */
#define JNODE_APPEND  0x10  /* More ARRAY/OBJECT entries at u.iAppend */
#define JNODE_LABEL   0x20  /* Is a label of an object */
#define JNODE_JSON5   0x40  /* Node contains JSON5 enhancements */


/* A single node of parsed JSON.  An array of these nodes describes
** a parse of JSON + edits.
**
** Use the json_parse() SQL function (available when compiled with
** -DSQLITE_DEBUG) to see a dump of complete JsonParse objects, including
** a complete listing and decoding of the array of JsonNodes.
*/
struct JsonNode {
  u8 eType;              /* One of the JSON_ type values */
  u8 jnFlags;            /* JNODE flags */
  u8 eU;                 /* Which union element to use */
  u32 n;                 /* Bytes of content for INT, REAL or STRING
                         ** Number of sub-nodes for ARRAY and OBJECT
                         ** Node that SUBST applies to */
  union {
    const char *zJContent; /* 1: Content for INT, REAL, and STRING */
    u32 iAppend;           /* 2: More terms for ARRAY and OBJECT */
    u32 iKey;              /* 3: Key for ARRAY objects in json_tree() */
    u32 iPrev;             /* 4: Previous SUBST node, or 0 */
  } u;
};


/* A parsed and possibly edited JSON string.  Lifecycle:
**
**   1.  JSON comes in and is parsed into an array aNode[].  The original
**       JSON text is stored in zJson.

**
**   2.  Zero or more changes are made (via json_remove() or json_replace()
**       or similar) to the aNode[] array.
**
**   3.  A new, edited and mimified JSON string is generated from aNode
**       and stored in zAlt.  The JsonParse object always owns zAlt.
**
** Step 1 always happens.  Step 2 and 3 may or may not happen, depending
** on the operation.
**
** aNode[].u.zJContent entries typically point into zJson.  Hence zJson
** must remain valid for the lifespan of the parse.  For edits,
** aNode[].u.zJContent might point to malloced space other than zJson.
** Entries in pClup are responsible for freeing that extra malloced space.
**
** When walking the parse tree in aNode[], edits are ignored if useMod is


** false.
*/
struct JsonParse {
  u32 nNode;         /* Number of slots of aNode[] used */
  u32 nAlloc;        /* Number of slots of aNode[] allocated */
  JsonNode *aNode;   /* Array of nodes containing the parse */
  char *zJson;       /* Original JSON string (before edits) */
  char *zAlt;        /* Revised and/or mimified JSON */
  u32 *aUp;          /* Index of parent of each node */
  JsonCleanup *pClup;/* Cleanup operations prior to freeing this object */

  u16 iDepth;        /* Nesting depth */
  u8 nErr;           /* Number of errors seen */
  u8 oom;            /* Set to true if out of memory */
  u8 bJsonIsRCStr;   /* True if zJson is an RCStr */
  u8 hasNonstd;      /* True if input uses non-standard features like JSON5 */
  u8 useMod;         /* Actually use the edits contain inside aNode */
  u8 hasMod;         /* aNode contains edits from the original zJson */
  u32 nJPRef;        /* Number of references to this object */
  int nJson;         /* Length of the zJson string in bytes */
  int nAlt;          /* Length of alternative JSON string zAlt, in bytes */
  u32 iErr;          /* Error location in zJson[] */
  u32 iSubst;        /* Last JSON_SUBST entry in aNode[] */
  u32 iHold;         /* Age of this entry in the cache for LRU replacement */
};







/*
** Maximum nesting depth of JSON for this implementation.
**
** This limit is needed to avoid a stack overflow in the recursive
** descent parser.  A depth of 1000 is far deeper than any sane JSON
** should go.  Historical note: This limit was 2000 prior to version 3.42.0
*/

#define JSON_MAX_DEPTH  1000










/**************************************************************************





























































































































** Utility routines for dealing with JsonString objects
**************************************************************************/

/* Set the JsonString object to an empty string

*/
static void jsonZero(JsonString *p){
  p->zBuf = p->zSpace;
  p->nAlloc = sizeof(p->zSpace);
  p->nUsed = 0;
  p->bStatic = 1;
}

/* Initialize the JsonString object
*/
static void jsonInit(JsonString *p, sqlite3_context *pCtx){
  p->pCtx = pCtx;
  p->bErr = 0;
  jsonZero(p);
}

/* Free all allocated memory and reset the JsonString object back to its
** initial state.
*/
static void jsonReset(JsonString *p){
  if( !p->bStatic ) sqlite3RCStrUnref(p->zBuf);
  jsonZero(p);
}

/* Report an out-of-memory (OOM) condition
*/
static void jsonOom(JsonString *p){
  p->bErr = 1;
  sqlite3_result_error_nomem(p->pCtx);
  jsonReset(p);
}

/* Enlarge pJson->zBuf so that it can hold at least N more bytes.
** Return zero on success.  Return non-zero on an OOM error
*/
static int jsonGrow(JsonString *p, u32 N){
  u64 nTotal = N<p->nAlloc ? p->nAlloc*2 : p->nAlloc+N+10;
  char *zNew;
  if( p->bStatic ){
    if( p->bErr ) return 1;
    zNew = sqlite3RCStrNew(nTotal);
    if( zNew==0 ){
      jsonOom(p);
      return SQLITE_NOMEM;
    }
    memcpy(zNew, p->zBuf, (size_t)p->nUsed);
    p->zBuf = zNew;
    p->bStatic = 0;
  }else{
    p->zBuf = sqlite3RCStrResize(p->zBuf, nTotal);
    if( p->zBuf==0 ){
      p->bErr = 1;
      jsonZero(p);
      return SQLITE_NOMEM;
    }
  }
  p->nAlloc = nTotal;
  return SQLITE_OK;
}

/* Append N bytes from zIn onto the end of the JsonString string.
*/
static SQLITE_NOINLINE void jsonAppendExpand(
  JsonString *p,
  const char *zIn,
  u32 N
){
  assert( N>0 );
  if( jsonGrow(p,N) ) return;
  memcpy(p->zBuf+p->nUsed, zIn, N);
  p->nUsed += N;
}
static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){
  if( N==0 ) return;
  if( N+p->nUsed >= p->nAlloc ){
    jsonAppendExpand(p,zIn,N);
  }else{
    memcpy(p->zBuf+p->nUsed, zIn, N);
    p->nUsed += N;
  }
}
static void jsonAppendRawNZ(JsonString *p, const char *zIn, u32 N){
  assert( N>0 );
  if( N+p->nUsed >= p->nAlloc ){
    jsonAppendExpand(p,zIn,N);
  }else{
    memcpy(p->zBuf+p->nUsed, zIn, N);
    p->nUsed += N;
  }
}


/* Append formatted text (not to exceed N bytes) to the JsonString.
*/
static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){
  va_list ap;
  if( (p->nUsed + N >= p->nAlloc) && jsonGrow(p, N) ) return;
  va_start(ap, zFormat);
  sqlite3_vsnprintf(N, p->zBuf+p->nUsed, zFormat, ap);
  va_end(ap);
  p->nUsed += (int)strlen(p->zBuf+p->nUsed);
}

/* Append a single character
*/
static SQLITE_NOINLINE void jsonAppendCharExpand(JsonString *p, char c){
  if( jsonGrow(p,1) ) return;
  p->zBuf[p->nUsed++] = c;
}
static void jsonAppendChar(JsonString *p, char c){
  if( p->nUsed>=p->nAlloc ){
    jsonAppendCharExpand(p,c);
  }else{
    p->zBuf[p->nUsed++] = c;
  }
}









/* Try to force the string to be a zero-terminated RCStr string.


**
** Return true on success.  Return false if an OOM prevents this
** from happening.
*/
static int jsonForceRCStr(JsonString *p){
  jsonAppendChar(p, 0);
  if( p->bErr ) return 0;
  p->nUsed--;
  if( p->bStatic==0 ) return 1;
  p->nAlloc = 0;
  p->nUsed++;
  jsonGrow(p, p->nUsed);
  p->nUsed--;
  return p->bStatic==0;
}


/* Append a comma separator to the output buffer, if the previous
** character is not '[' or '{'.
*/
static void jsonAppendSeparator(JsonString *p){
  char c;
  if( p->nUsed==0 ) return;
  c = p->zBuf[p->nUsed-1];
  if( c=='[' || c=='{' ) return;
  jsonAppendChar(p, ',');
}

/* Append the N-byte string in zIn to the end of the JsonString string
** under construction.  Enclose the string in "..." and escape
** any double-quotes or backslash characters contained within the
** string.



*/
static void jsonAppendString(JsonString *p, const char *zIn, u32 N){
  u32 i;



  if( zIn==0 || ((N+p->nUsed+2 >= p->nAlloc) && jsonGrow(p,N+2)!=0) ) return;
  p->zBuf[p->nUsed++] = '"';
  for(i=0; i<N; i++){

    unsigned char c = ((unsigned const char*)zIn)[i];









    if( jsonIsOk[c] ){



















      p->zBuf[p->nUsed++] = c;











    }else if( c=='"' || c=='\\' ){
      json_simple_escape:
      if( (p->nUsed+N+3-i > p->nAlloc) && jsonGrow(p,N+3-i)!=0 ) return;
      p->zBuf[p->nUsed++] = '\\';
      p->zBuf[p->nUsed++] = c;
    }else if( c=='\'' ){
      p->zBuf[p->nUsed++] = c;
    }else{
      static const char aSpecial[] = {
         0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0,   0,   0,   0, 0,   0,   0, 0, 0
      };
      assert( sizeof(aSpecial)==32 );
      assert( aSpecial['\b']=='b' );
      assert( aSpecial['\f']=='f' );
      assert( aSpecial['\n']=='n' );
      assert( aSpecial['\r']=='r' );
      assert( aSpecial['\t']=='t' );
      assert( c>=0 && c<sizeof(aSpecial) );
      if( aSpecial[c] ){
        c = aSpecial[c];
        goto json_simple_escape;
      }
      if( (p->nUsed+N+7+i > p->nAlloc) && jsonGrow(p,N+7-i)!=0 ) return;
      p->zBuf[p->nUsed++] = '\\';
      p->zBuf[p->nUsed++] = 'u';
      p->zBuf[p->nUsed++] = '0';
      p->zBuf[p->nUsed++] = '0';
      p->zBuf[p->nUsed++] = "0123456789abcdef"[c>>4];
      p->zBuf[p->nUsed++] = "0123456789abcdef"[c&0xf];
    }


  }
  p->zBuf[p->nUsed++] = '"';
  assert( p->nUsed<p->nAlloc );
}

/*
** The zIn[0..N] string is a JSON5 string literal.  Append to p a translation
** of the string literal that standard JSON and that omits all JSON5
** features.
*/
static void jsonAppendNormalizedString(JsonString *p, const char *zIn, u32 N){
  u32 i;
  jsonAppendChar(p, '"');
  zIn++;
  N -= 2;
  while( N>0 ){
    for(i=0; i<N && zIn[i]!='\\' && zIn[i]!='"'; i++){}
    if( i>0 ){
      jsonAppendRawNZ(p, zIn, i);
      zIn += i;
      N -= i;
      if( N==0 ) break;
    }
    if( zIn[0]=='"' ){
      jsonAppendRawNZ(p, "\\\"", 2);
      zIn++;
      N--;
      continue;
    }
    assert( zIn[0]=='\\' );
    switch( (u8)zIn[1] ){
      case '\'':
        jsonAppendChar(p, '\'');
        break;
      case 'v':
        jsonAppendRawNZ(p, "\\u0009", 6);
        break;
      case 'x':
        jsonAppendRawNZ(p, "\\u00", 4);
        jsonAppendRawNZ(p, &zIn[2], 2);
        zIn += 2;
        N -= 2;
        break;
      case '0':
        jsonAppendRawNZ(p, "\\u0000", 6);
        break;
      case '\r':
        if( zIn[2]=='\n' ){
          zIn++;
          N--;
        }
        break;
      case '\n':
        break;
      case 0xe2:
        assert( N>=4 );
        assert( 0x80==(u8)zIn[2] );
        assert( 0xa8==(u8)zIn[3] || 0xa9==(u8)zIn[3] );
        zIn += 2;
        N -= 2;
        break;
      default:
        jsonAppendRawNZ(p, zIn, 2);
        break;
    }
    zIn += 2;
    N -= 2;
  }
  jsonAppendChar(p, '"');
}

/*
** The zIn[0..N] string is a JSON5 integer literal.  Append to p a translation
** of the string literal that standard JSON and that omits all JSON5
** features.
*/
static void jsonAppendNormalizedInt(JsonString *p, const char *zIn, u32 N){
  if( zIn[0]=='+' ){
    zIn++;
    N--;
  }else if( zIn[0]=='-' ){
    jsonAppendChar(p, '-');
    zIn++;
    N--;
  }
  if( zIn[0]=='0' && (zIn[1]=='x' || zIn[1]=='X') ){
    sqlite3_int64 i = 0;
    int rc = sqlite3DecOrHexToI64(zIn, &i);
    if( rc<=1 ){
      jsonPrintf(100,p,"%lld",i);
    }else{
      assert( rc==2 );
      jsonAppendRawNZ(p, "9.0e999", 7);
    }
    return;
  }
  assert( N>0 );
  jsonAppendRawNZ(p, zIn, N);
}

/*
** The zIn[0..N] string is a JSON5 real literal.  Append to p a translation
** of the string literal that standard JSON and that omits all JSON5
** features.
*/
static void jsonAppendNormalizedReal(JsonString *p, const char *zIn, u32 N){
  u32 i;
  if( zIn[0]=='+' ){
    zIn++;
    N--;
  }else if( zIn[0]=='-' ){
    jsonAppendChar(p, '-');
    zIn++;
    N--;
  }
  if( zIn[0]=='.' ){
    jsonAppendChar(p, '0');
  }
  for(i=0; i<N; i++){
    if( zIn[i]=='.' && (i+1==N || !sqlite3Isdigit(zIn[i+1])) ){
      i++;
      jsonAppendRaw(p, zIn, i);
      zIn += i;
      N -= i;
      jsonAppendChar(p, '0');
      break;
    }
  }
  if( N>0 ){
    jsonAppendRawNZ(p, zIn, N);
  }
}



/*
** Append a function parameter value to the JSON string under
** construction.
*/
static void jsonAppendValue(
  JsonString *p,                 /* Append to this JSON string */
  sqlite3_value *pValue          /* Value to append */
){
  switch( sqlite3_value_type(pValue) ){
    case SQLITE_NULL: {
      jsonAppendRawNZ(p, "null", 4);
      break;







<
<
<
<
<
<
<

|
|

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




>
>
>
>







|



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

|
>
>



<
<
<
<
<
<
|
|

<
<
|
|
<
|
|
|

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

|
<

|
|
>

<
|

|
|

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


|
|
|
|
|
|
|
>





|
|
|
|
<
|
|
|


>
>
>
>
>
>







>
|
>
>
>

>
>
>
>
>
>

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



|
>

|








|

|
|





|

|




|
|
|
|





|



|


|








|
|









|





|






|








|











|









|










>
>
>
>
>
>
>
>
|
>
>




|

<
<
<
<
<
|
<
|

<













|
|

>
>
>


|
>
>
>
|

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

|




















|







>
>






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

|







203377
203378
203379
203380
203381
203382
203383







203384
203385
203386
203387
203388
203389
203390
203391
203392
203393
203394
203395
203396
203397
203398
203399
203400
203401
203402
203403
203404
203405
203406
203407
203408
203409
203410
203411
203412
203413
203414
203415
203416
203417
203418
203419
203420
203421
203422
203423
203424
203425
203426
203427
203428
203429
203430
203431
203432
203433
203434
203435
203436
203437








203438



203439
203440
203441





203442
203443
203444
203445
203446
203447
203448






203449
203450
203451


203452
203453

203454
203455
203456
203457





















203458
203459

203460
203461
203462
203463
203464

203465
203466
203467
203468
203469









203470
203471
203472
203473
203474
203475
203476
203477
203478
203479
203480
203481
203482
203483
203484
203485
203486
203487
203488
203489
203490
203491

203492
203493
203494
203495
203496
203497
203498
203499
203500
203501
203502
203503
203504
203505
203506
203507
203508
203509
203510
203511
203512
203513
203514
203515
203516
203517
203518
203519
203520
203521
203522
203523
203524
203525
203526
203527
203528
203529
203530
203531
203532
203533
203534
203535
203536
203537
203538
203539
203540
203541
203542
203543
203544
203545
203546
203547
203548
203549
203550
203551
203552
203553
203554
203555
203556
203557
203558
203559
203560
203561
203562
203563
203564
203565
203566
203567
203568
203569
203570
203571
203572
203573
203574
203575
203576
203577
203578
203579
203580
203581
203582
203583
203584
203585
203586
203587
203588
203589
203590
203591
203592
203593
203594
203595
203596
203597
203598
203599
203600
203601
203602
203603
203604
203605
203606
203607
203608
203609
203610
203611
203612
203613
203614
203615
203616
203617
203618
203619
203620
203621
203622
203623
203624
203625
203626
203627
203628
203629
203630
203631
203632
203633
203634
203635
203636
203637
203638
203639
203640
203641
203642
203643
203644
203645
203646
203647
203648
203649
203650
203651
203652
203653
203654
203655
203656
203657
203658
203659
203660
203661
203662
203663
203664
203665
203666
203667
203668
203669
203670
203671
203672
203673
203674
203675
203676
203677
203678
203679
203680
203681
203682
203683
203684
203685
203686
203687
203688
203689
203690
203691
203692
203693
203694
203695
203696
203697
203698
203699
203700
203701
203702
203703
203704
203705
203706
203707
203708
203709
203710
203711
203712
203713
203714
203715
203716
203717
203718
203719
203720
203721
203722
203723
203724
203725
203726
203727
203728
203729
203730
203731
203732
203733
203734
203735
203736
203737
203738
203739
203740
203741
203742
203743
203744
203745
203746
203747
203748
203749
203750
203751
203752
203753
203754
203755
203756
203757
203758
203759
203760
203761
203762
203763
203764
203765
203766
203767
203768
203769
203770
203771
203772
203773
203774
203775
203776
203777
203778
203779
203780
203781
203782
203783
203784
203785
203786





203787

203788
203789

203790
203791
203792
203793
203794
203795
203796
203797
203798
203799
203800
203801
203802
203803
203804
203805
203806
203807
203808
203809
203810
203811
203812
203813
203814
203815
203816

203817
203818
203819
203820
203821
203822
203823
203824
203825
203826
203827
203828
203829
203830
203831
203832
203833
203834
203835
203836
203837
203838
203839
203840
203841
203842
203843
203844
203845
203846
203847
203848
203849
203850
203851
203852
203853
203854
203855
203856
203857
203858
203859
203860
203861
203862
203863
203864
203865
203866
203867
203868
203869
203870
203871
203872
203873
203874
203875
203876
203877
203878
203879
203880
203881
203882
203883
203884
203885
203886
203887
203888
203889
203890
203891
203892
203893
203894
203895
203896
203897
203898
































































203899


































































203900
203901
203902
203903
203904
203905
203906
203907
203908
203909
  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
};








/* Objects */
typedef struct JsonCache JsonCache;
typedef struct JsonString JsonString;
typedef struct JsonParse JsonParse;

/*
** Magic number used for the JSON parse cache in sqlite3_get_auxdata()
*/
#define JSON_CACHE_ID    (-429938)  /* Cache entry */
#define JSON_CACHE_SIZE  4          /* Max number of cache entries */

/*
** jsonUnescapeOneChar() returns this invalid code point if it encounters
** a syntax error.
*/
#define JSON_INVALID_CHAR 0x99999

/* A cache mapping JSON text into JSONB blobs.
**
** Each cache entry is a JsonParse object with the following restrictions:
**
**    *   The bReadOnly flag must be set
**
**    *   The aBlob[] array must be owned by the JsonParse object.  In other
**        words, nBlobAlloc must be non-zero.
**
**    *   eEdit and delta must be zero.
**
**    *   zJson must be an RCStr.  In other words bJsonIsRCStr must be true.
*/
struct JsonCache {
  sqlite3 *db;                    /* Database connection */
  int nUsed;                      /* Number of active entries in the cache */
  JsonParse *a[JSON_CACHE_SIZE];  /* One line for each cache entry */
};

/* An instance of this object represents a JSON string
** under construction.  Really, this is a generic string accumulator
** that can be and is used to create strings other than JSON.
**
** If the generated string is longer than will fit into the zSpace[] buffer,
** then it will be an RCStr string.  This aids with caching of large
** JSON strings.
*/
struct JsonString {
  sqlite3_context *pCtx;   /* Function context - put error messages here */
  char *zBuf;              /* Append JSON content here */
  u64 nAlloc;              /* Bytes of storage available in zBuf[] */
  u64 nUsed;               /* Bytes of zBuf[] currently used */
  u8 bStatic;              /* True if zBuf is static space */
  u8 eErr;                 /* True if an error has been encountered */
  char zSpace[100];        /* Initial static space */
};









/* Allowed values for JsonString.eErr */



#define JSTRING_OOM         0x01   /* Out of memory */
#define JSTRING_MALFORMED   0x02   /* Malformed JSONB */
#define JSTRING_ERR         0x04   /* Error already sent to sqlite3_result */






/* The "subtype" set for text JSON values passed through using
** sqlite3_result_subtype() and sqlite3_value_subtype().
*/
#define JSON_SUBTYPE  74    /* Ascii for "J" */

/*






** Bit values for the flags passed into various SQL function implementations
** via the sqlite3_user_data() value.
*/


#define JSON_JSON      0x01        /* Result is always JSON */
#define JSON_SQL       0x02        /* Result is always SQL */

#define JSON_ABPATH    0x03        /* Allow abbreviated JSON path specs */
#define JSON_ISSET     0x04        /* json_set(), not json_insert() */
#define JSON_BLOB      0x08        /* Use the BLOB output format */























/* A parsed JSON value.  Lifecycle:

**
**   1.  JSON comes in and is parsed into a JSONB value in aBlob.  The
**       original text is stored in zJson.  This step is skipped if the
**       input is JSONB instead of text JSON.
**

**   2.  The aBlob[] array is searched using the JSON path notation, if needed.
**
**   3.  Zero or more changes are made to aBlob[] (via json_remove() or
**       json_replace() or json_patch() or similar).
**









**   4.  New JSON text is generated from the aBlob[] for output.  This step
**       is skipped if the function is one of the jsonb_* functions that
**       returns JSONB instead of text JSON.
*/
struct JsonParse {
  u8 *aBlob;         /* JSONB representation of JSON value */
  u32 nBlob;         /* Bytes of aBlob[] actually used */
  u32 nBlobAlloc;    /* Bytes allocated to aBlob[].  0 if aBlob is external */
  char *zJson;       /* Json text used for parsing */
  sqlite3 *db;       /* The database connection to which this object belongs */
  int nJson;         /* Length of the zJson string in bytes */
  u32 nJPRef;        /* Number of references to this object */
  u32 iErr;          /* Error location in zJson[] */
  u16 iDepth;        /* Nesting depth */
  u8 nErr;           /* Number of errors seen */
  u8 oom;            /* Set to true if out of memory */
  u8 bJsonIsRCStr;   /* True if zJson is an RCStr */
  u8 hasNonstd;      /* True if input uses non-standard features like JSON5 */
  u8 bReadOnly;      /* Do not modify. */
  /* Search and edit information.  See jsonLookupStep() */
  u8 eEdit;          /* Edit operation to apply */
  int delta;         /* Size change due to the edit */

  u32 nIns;          /* Number of bytes to insert */
  u32 iLabel;        /* Location of label if search landed on an object value */
  u8 *aIns;          /* Content to be inserted */
};

/* Allowed values for JsonParse.eEdit */
#define JEDIT_DEL   1   /* Delete if exists */
#define JEDIT_REPL  2   /* Overwrite if exists */
#define JEDIT_INS   3   /* Insert if not exists */
#define JEDIT_SET   4   /* Insert or overwrite */

/*
** Maximum nesting depth of JSON for this implementation.
**
** This limit is needed to avoid a stack overflow in the recursive
** descent parser.  A depth of 1000 is far deeper than any sane JSON
** should go.  Historical note: This limit was 2000 prior to version 3.42.0
*/
#ifndef SQLITE_JSON_MAX_DEPTH
# define JSON_MAX_DEPTH  1000
#else
# define JSON_MAX_DEPTH SQLITE_JSON_MAX_DEPTH
#endif

/*
** Allowed values for the flgs argument to jsonParseFuncArg();
*/
#define JSON_EDITABLE  0x01   /* Generate a writable JsonParse object */
#define JSON_KEEPERROR 0x02   /* Return non-NULL even if there is an error */

/**************************************************************************
** Forward references
**************************************************************************/
static void jsonReturnStringAsBlob(JsonString*);
static int jsonFuncArgMightBeBinary(sqlite3_value *pJson);
static u32 jsonTranslateBlobToText(const JsonParse*,u32,JsonString*);
static void jsonReturnParse(sqlite3_context*,JsonParse*);
static JsonParse *jsonParseFuncArg(sqlite3_context*,sqlite3_value*,u32);
static void jsonParseFree(JsonParse*);
static u32 jsonbPayloadSize(const JsonParse*, u32, u32*);
static u32 jsonUnescapeOneChar(const char*, u32, u32*);

/**************************************************************************
** Utility routines for dealing with JsonCache objects
**************************************************************************/

/*
** Free a JsonCache object.
*/
static void jsonCacheDelete(JsonCache *p){
  int i;
  for(i=0; i<p->nUsed; i++){
    jsonParseFree(p->a[i]);
  }
  sqlite3DbFree(p->db, p);
}
static void jsonCacheDeleteGeneric(void *p){
  jsonCacheDelete((JsonCache*)p);
}

/*
** Insert a new entry into the cache.  If the cache is full, expel
** the least recently used entry.  Return SQLITE_OK on success or a
** result code otherwise.
**
** Cache entries are stored in age order, oldest first.
*/
static int jsonCacheInsert(
  sqlite3_context *ctx,   /* The SQL statement context holding the cache */
  JsonParse *pParse       /* The parse object to be added to the cache */
){
  JsonCache *p;

  assert( pParse->zJson!=0 );
  assert( pParse->bJsonIsRCStr );
  assert( pParse->delta==0 );
  p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID);
  if( p==0 ){
    sqlite3 *db = sqlite3_context_db_handle(ctx);
    p = sqlite3DbMallocZero(db, sizeof(*p));
    if( p==0 ) return SQLITE_NOMEM;
    p->db = db;
    sqlite3_set_auxdata(ctx, JSON_CACHE_ID, p, jsonCacheDeleteGeneric);
    p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID);
    if( p==0 ) return SQLITE_NOMEM;
  }
  if( p->nUsed >= JSON_CACHE_SIZE ){
    jsonParseFree(p->a[0]);
    memmove(p->a, &p->a[1], (JSON_CACHE_SIZE-1)*sizeof(p->a[0]));
    p->nUsed = JSON_CACHE_SIZE-1;
  }
  assert( pParse->nBlobAlloc>0 );
  pParse->eEdit = 0;
  pParse->nJPRef++;
  pParse->bReadOnly = 1;
  p->a[p->nUsed] = pParse;
  p->nUsed++;
  return SQLITE_OK;
}

/*
** Search for a cached translation the json text supplied by pArg.  Return
** the JsonParse object if found.  Return NULL if not found.
**
** When a match if found, the matching entry is moved to become the
** most-recently used entry if it isn't so already.
**
** The JsonParse object returned still belongs to the Cache and might
** be deleted at any moment.  If the caller whants the JsonParse to
** linger, it needs to increment the nPJRef reference counter.
*/
static JsonParse *jsonCacheSearch(
  sqlite3_context *ctx,    /* The SQL statement context holding the cache */
  sqlite3_value *pArg      /* Function argument containing SQL text */
){
  JsonCache *p;
  int i;
  const char *zJson;
  int nJson;

  if( sqlite3_value_type(pArg)!=SQLITE_TEXT ){
    return 0;
  }
  zJson = (const char*)sqlite3_value_text(pArg);
  if( zJson==0 ) return 0;
  nJson = sqlite3_value_bytes(pArg);

  p = sqlite3_get_auxdata(ctx, JSON_CACHE_ID);
  if( p==0 ){
    return 0;
  }
  for(i=0; i<p->nUsed; i++){
    if( p->a[i]->zJson==zJson ) break;
  }
  if( i>=p->nUsed ){
    for(i=0; i<p->nUsed; i++){
      if( p->a[i]->nJson!=nJson ) continue;
      if( memcmp(p->a[i]->zJson, zJson, nJson)==0 ) break;
    }
  }
  if( i<p->nUsed ){
    if( i<p->nUsed-1 ){
      /* Make the matching entry the most recently used entry */
      JsonParse *tmp = p->a[i];
      memmove(&p->a[i], &p->a[i+1], (p->nUsed-i-1)*sizeof(tmp));
      p->a[p->nUsed-1] = tmp;
      i = p->nUsed - 1;
    }
    assert( p->a[i]->delta==0 );
    return p->a[i];
  }else{
    return 0;
  }
}

/**************************************************************************
** Utility routines for dealing with JsonString objects
**************************************************************************/

/* Turn uninitialized bulk memory into a valid JsonString object
** holding a zero-length string.
*/
static void jsonStringZero(JsonString *p){
  p->zBuf = p->zSpace;
  p->nAlloc = sizeof(p->zSpace);
  p->nUsed = 0;
  p->bStatic = 1;
}

/* Initialize the JsonString object
*/
static void jsonStringInit(JsonString *p, sqlite3_context *pCtx){
  p->pCtx = pCtx;
  p->eErr = 0;
  jsonStringZero(p);
}

/* Free all allocated memory and reset the JsonString object back to its
** initial state.
*/
static void jsonStringReset(JsonString *p){
  if( !p->bStatic ) sqlite3RCStrUnref(p->zBuf);
  jsonStringZero(p);
}

/* Report an out-of-memory (OOM) condition
*/
static void jsonStringOom(JsonString *p){
  p->eErr |= JSTRING_OOM;
  if( p->pCtx ) sqlite3_result_error_nomem(p->pCtx);
  jsonStringReset(p);
}

/* Enlarge pJson->zBuf so that it can hold at least N more bytes.
** Return zero on success.  Return non-zero on an OOM error
*/
static int jsonStringGrow(JsonString *p, u32 N){
  u64 nTotal = N<p->nAlloc ? p->nAlloc*2 : p->nAlloc+N+10;
  char *zNew;
  if( p->bStatic ){
    if( p->eErr ) return 1;
    zNew = sqlite3RCStrNew(nTotal);
    if( zNew==0 ){
      jsonStringOom(p);
      return SQLITE_NOMEM;
    }
    memcpy(zNew, p->zBuf, (size_t)p->nUsed);
    p->zBuf = zNew;
    p->bStatic = 0;
  }else{
    p->zBuf = sqlite3RCStrResize(p->zBuf, nTotal);
    if( p->zBuf==0 ){
      p->eErr |= JSTRING_OOM;
      jsonStringZero(p);
      return SQLITE_NOMEM;
    }
  }
  p->nAlloc = nTotal;
  return SQLITE_OK;
}

/* Append N bytes from zIn onto the end of the JsonString string.
*/
static SQLITE_NOINLINE void jsonStringExpandAndAppend(
  JsonString *p,
  const char *zIn,
  u32 N
){
  assert( N>0 );
  if( jsonStringGrow(p,N) ) return;
  memcpy(p->zBuf+p->nUsed, zIn, N);
  p->nUsed += N;
}
static void jsonAppendRaw(JsonString *p, const char *zIn, u32 N){
  if( N==0 ) return;
  if( N+p->nUsed >= p->nAlloc ){
    jsonStringExpandAndAppend(p,zIn,N);
  }else{
    memcpy(p->zBuf+p->nUsed, zIn, N);
    p->nUsed += N;
  }
}
static void jsonAppendRawNZ(JsonString *p, const char *zIn, u32 N){
  assert( N>0 );
  if( N+p->nUsed >= p->nAlloc ){
    jsonStringExpandAndAppend(p,zIn,N);
  }else{
    memcpy(p->zBuf+p->nUsed, zIn, N);
    p->nUsed += N;
  }
}


/* Append formatted text (not to exceed N bytes) to the JsonString.
*/
static void jsonPrintf(int N, JsonString *p, const char *zFormat, ...){
  va_list ap;
  if( (p->nUsed + N >= p->nAlloc) && jsonStringGrow(p, N) ) return;
  va_start(ap, zFormat);
  sqlite3_vsnprintf(N, p->zBuf+p->nUsed, zFormat, ap);
  va_end(ap);
  p->nUsed += (int)strlen(p->zBuf+p->nUsed);
}

/* Append a single character
*/
static SQLITE_NOINLINE void jsonAppendCharExpand(JsonString *p, char c){
  if( jsonStringGrow(p,1) ) return;
  p->zBuf[p->nUsed++] = c;
}
static void jsonAppendChar(JsonString *p, char c){
  if( p->nUsed>=p->nAlloc ){
    jsonAppendCharExpand(p,c);
  }else{
    p->zBuf[p->nUsed++] = c;
  }
}

/* Remove a single character from the end of the string
*/
static void jsonStringTrimOneChar(JsonString *p){
  if( p->eErr==0 ){
    assert( p->nUsed>0 );
    p->nUsed--;
  }
}


/* Make sure there is a zero terminator on p->zBuf[]
**
** Return true on success.  Return false if an OOM prevents this
** from happening.
*/
static int jsonStringTerminate(JsonString *p){
  jsonAppendChar(p, 0);





  jsonStringTrimOneChar(p);

  return p->eErr==0;
}


/* Append a comma separator to the output buffer, if the previous
** character is not '[' or '{'.
*/
static void jsonAppendSeparator(JsonString *p){
  char c;
  if( p->nUsed==0 ) return;
  c = p->zBuf[p->nUsed-1];
  if( c=='[' || c=='{' ) return;
  jsonAppendChar(p, ',');
}

/* Append the N-byte string in zIn to the end of the JsonString string
** under construction.  Enclose the string in double-quotes ("...") and
** escape any double-quotes or backslash characters contained within the
** string.
**
** This routine is a high-runner.  There is a measurable performance
** increase associated with unwinding the jsonIsOk[] loop.
*/
static void jsonAppendString(JsonString *p, const char *zIn, u32 N){
  u32 k;
  u8 c;
  const u8 *z = (const u8*)zIn;
  if( z==0 ) return;
  if( (N+p->nUsed+2 >= p->nAlloc) && jsonStringGrow(p,N+2)!=0 ) return;
  p->zBuf[p->nUsed++] = '"';

  while( 1 /*exit-by-break*/ ){
    k = 0;
    /* The following while() is the 4-way unwound equivalent of
    **
    **     while( k<N && jsonIsOk[z[k]] ){ k++; }
    */
    while( 1 /* Exit by break */ ){
      if( k+3>=N ){
        while( k<N && jsonIsOk[z[k]] ){ k++; }
        break;
      }
      if( !jsonIsOk[z[k]] ){
        break;
      }
      if( !jsonIsOk[z[k+1]] ){
        k += 1;
        break;
      }
      if( !jsonIsOk[z[k+2]] ){
        k += 2;
        break;
      }
      if( !jsonIsOk[z[k+3]] ){
        k += 3;
        break;
      }else{
        k += 4;
      }
    }
    if( k>=N ){
      if( k>0 ){
        memcpy(&p->zBuf[p->nUsed], z, k);
        p->nUsed += k;
      }
      break;
    }
    if( k>0 ){
      memcpy(&p->zBuf[p->nUsed], z, k);
      p->nUsed += k;
      z += k;
      N -= k;
    }
    c = z[0];
    if( c=='"' || c=='\\' ){
      json_simple_escape:
      if( (p->nUsed+N+3 > p->nAlloc) && jsonStringGrow(p,N+3)!=0 ) return;
      p->zBuf[p->nUsed++] = '\\';
      p->zBuf[p->nUsed++] = c;
    }else if( c=='\'' ){
      p->zBuf[p->nUsed++] = c;
    }else{
      static const char aSpecial[] = {
         0, 0, 0, 0, 0, 0, 0, 0, 'b', 't', 'n', 0, 'f', 'r', 0, 0,
         0, 0, 0, 0, 0, 0, 0, 0,   0,   0,   0, 0,   0,   0, 0, 0
      };
      assert( sizeof(aSpecial)==32 );
      assert( aSpecial['\b']=='b' );
      assert( aSpecial['\f']=='f' );
      assert( aSpecial['\n']=='n' );
      assert( aSpecial['\r']=='r' );
      assert( aSpecial['\t']=='t' );
      assert( c>=0 && c<sizeof(aSpecial) );
      if( aSpecial[c] ){
        c = aSpecial[c];
        goto json_simple_escape;
      }
      if( (p->nUsed+N+7 > p->nAlloc) && jsonStringGrow(p,N+7)!=0 ) return;
      p->zBuf[p->nUsed++] = '\\';
      p->zBuf[p->nUsed++] = 'u';
      p->zBuf[p->nUsed++] = '0';
      p->zBuf[p->nUsed++] = '0';
      p->zBuf[p->nUsed++] = "0123456789abcdef"[c>>4];
      p->zBuf[p->nUsed++] = "0123456789abcdef"[c&0xf];
    }
    z++;
    N--;
  }
  p->zBuf[p->nUsed++] = '"';
  assert( p->nUsed<p->nAlloc );
}

/*
































































** Append an sqlite3_value (such as a function parameter) to the JSON


































































** string under construction in p.
*/
static void jsonAppendSqlValue(
  JsonString *p,                 /* Append to this JSON string */
  sqlite3_value *pValue          /* Value to append */
){
  switch( sqlite3_value_type(pValue) ){
    case SQLITE_NULL: {
      jsonAppendRawNZ(p, "null", 4);
      break;
203221
203222
203223
203224
203225
203226
203227






203228
203229
203230
203231
203232
203233
203234
203235
203236
203237
203238
203239
203240
203241




203242
203243






203244



203245
203246
203247
203248


203249









203250
203251
203252
203253

203254
203255
203256


203257
203258
203259
203260
203261
203262
203263
203264
203265
203266
203267
203268
203269
203270
203271
203272
203273
203274
203275
203276
203277
203278
203279
203280
203281
203282
203283
203284
203285
203286
203287
203288
203289
203290
203291
203292
203293
203294
203295
203296
203297
203298
203299
203300
203301
203302

203303
203304
203305

203306
203307

203308
203309
203310
203311
203312
203313
203314
203315
203316
203317
203318
203319

203320
203321
203322
203323
203324
203325
203326
203327
203328
203329
203330
203331
203332
203333
203334
203335
203336
203337
203338
203339
203340
203341
203342
203343
203344
203345
203346
203347
203348
203349
203350
203351
203352
203353
203354
203355
203356
203357
203358
203359
203360
203361
203362
203363
203364
203365
203366
203367
203368
203369
203370
203371
203372
203373
203374
203375
203376
203377
203378
203379
203380
203381
203382
203383
203384
203385
203386
203387
203388
203389
203390
203391
203392
203393
203394
203395
203396
203397
203398
203399
203400
203401
203402
203403
203404
203405
203406
203407
203408
203409
203410
203411
203412
203413
203414
203415
203416
203417
203418
203419
203420
203421
203422
203423
203424
203425
203426
203427
203428
203429
203430
203431
203432
203433
203434
203435
203436
203437
203438
203439
203440
203441
203442
203443
203444
203445
203446
203447
203448
203449
203450
203451
203452
203453
203454
203455
203456
203457
203458
203459
203460
203461
203462
203463
203464
203465
203466
203467
203468
203469
203470
203471
203472
203473
203474
203475
203476
203477
203478
203479
203480
203481
203482
203483
203484
203485
203486
203487
203488
203489
203490
203491
203492
203493
203494
203495
203496
203497
203498
203499
203500

203501
203502
203503
203504
203505

203506
203507
203508
203509
203510
203511

203512
203513
203514
203515
203516
203517
203518
203519
203520
203521
203522
203523
203524
203525
203526
203527
203528
203529
203530
203531
203532
203533
203534
203535
203536
203537
203538
203539
203540
203541
203542
203543
203544
203545
203546
203547
203548
203549
203550
203551
203552
203553
203554
203555
203556
203557
203558
203559
203560
203561
203562
203563
203564
203565
203566
203567
203568
203569
203570
203571
203572
203573
203574
203575
203576
203577
203578
203579
203580
203581
203582
203583
203584
203585
203586
203587
203588
203589
203590
203591
203592
203593
203594
203595
203596
203597
203598
203599
203600
203601
203602
203603
203604
203605
203606
203607
203608
203609
203610
203611
203612
203613
203614
203615
203616
203617
203618
203619
203620
203621
203622
203623
203624
203625
203626
203627
203628
203629
203630
203631
203632
203633
203634
203635
203636
203637
203638
203639
203640
203641
203642
203643
203644
203645
203646
203647
203648
203649
203650
203651
203652
203653
203654
203655
203656
203657
203658
203659
203660
203661
203662
203663
203664
203665
203666
203667
203668
203669
203670
203671
203672
203673
203674
203675
203676
203677
203678
203679
203680
203681
203682
203683
203684
203685
203686
203687
203688
203689
203690
203691
203692
203693
203694
203695
203696
203697
203698
203699
203700
203701
203702
203703
203704
203705
203706
203707
203708
203709
203710
203711
203712
203713
203714
203715
203716
203717
203718
203719
203720
203721
203722
203723
203724
203725
203726
203727
203728
203729
203730
203731
203732
203733
203734
203735
203736
203737
203738
203739
203740
203741
203742
203743
203744
203745
203746
203747
203748
203749
203750
203751
203752
203753
203754
203755
203756
203757
203758
203759
203760
203761
203762
203763
203764
203765
203766
203767
203768
203769
203770
203771
203772
203773
203774
203775
203776
203777
203778
203779
203780
203781
203782
203783
203784
203785
203786
203787
203788
203789
203790
203791
203792
203793
203794
203795
203796
203797
203798
203799
203800
203801
203802
203803
203804
203805
203806
203807
203808
203809
203810
203811
203812
203813
203814
203815
        jsonAppendRaw(p, z, n);
      }else{
        jsonAppendString(p, z, n);
      }
      break;
    }
    default: {






      if( p->bErr==0 ){
        sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1);
        p->bErr = 2;
        jsonReset(p);
      }
      break;
    }
  }
}


/* Make the JSON in p the result of the SQL function.
**
** The JSON string is reset.




*/
static void jsonResult(JsonString *p){






  if( p->bErr==0 ){



    if( p->bStatic ){
      sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed,
                            SQLITE_TRANSIENT, SQLITE_UTF8);
    }else if( jsonForceRCStr(p) ){


      sqlite3RCStrRef(p->zBuf);









      sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed,
                            sqlite3RCStrUnref,
                            SQLITE_UTF8);
    }

  }
  if( p->bErr==1 ){
    sqlite3_result_error_nomem(p->pCtx);


  }
  jsonReset(p);
}

/**************************************************************************
** Utility routines for dealing with JsonNode and JsonParse objects
**************************************************************************/

/*
** Return the number of consecutive JsonNode slots need to represent
** the parsed JSON at pNode.  The minimum answer is 1.  For ARRAY and
** OBJECT types, the number might be larger.
**
** Appended elements are not counted.  The value returned is the number
** by which the JsonNode counter should increment in order to go to the
** next peer value.
*/
static u32 jsonNodeSize(JsonNode *pNode){
  return pNode->eType>=JSON_ARRAY ? pNode->n+1 : 1;
}

/*
** Reclaim all memory allocated by a JsonParse object.  But do not
** delete the JsonParse object itself.
*/
static void jsonParseReset(JsonParse *pParse){
  while( pParse->pClup ){
    JsonCleanup *pTask = pParse->pClup;
    pParse->pClup = pTask->pJCNext;
    pTask->xOp(pTask->pArg);
    sqlite3_free(pTask);
  }
  assert( pParse->nJPRef<=1 );
  if( pParse->aNode ){
    sqlite3_free(pParse->aNode);
    pParse->aNode = 0;
  }
  pParse->nNode = 0;
  pParse->nAlloc = 0;
  if( pParse->aUp ){
    sqlite3_free(pParse->aUp);
    pParse->aUp = 0;
  }
  if( pParse->bJsonIsRCStr ){
    sqlite3RCStrUnref(pParse->zJson);
    pParse->zJson = 0;

    pParse->bJsonIsRCStr = 0;
  }
  if( pParse->zAlt ){

    sqlite3RCStrUnref(pParse->zAlt);
    pParse->zAlt = 0;

  }
}

/*
** Free a JsonParse object that was obtained from sqlite3_malloc().
**
** Note that destroying JsonParse might call sqlite3RCStrUnref() to
** destroy the zJson value.  The RCStr object might recursively invoke
** JsonParse to destroy this pParse object again.  Take care to ensure
** that this recursive destructor sequence terminates harmlessly.
*/
static void jsonParseFree(JsonParse *pParse){

  if( pParse->nJPRef>1 ){
    pParse->nJPRef--;
  }else{
    jsonParseReset(pParse);
    sqlite3_free(pParse);
  }
}

/*
** Add a cleanup task to the JsonParse object.
**
** If an OOM occurs, the cleanup operation happens immediately
** and this function returns SQLITE_NOMEM.
*/
static int jsonParseAddCleanup(
  JsonParse *pParse,          /* Add the cleanup task to this parser */
  void(*xOp)(void*),          /* The cleanup task */
  void *pArg                  /* Argument to the cleanup */
){
  JsonCleanup *pTask = sqlite3_malloc64( sizeof(*pTask) );
  if( pTask==0 ){
    pParse->oom = 1;
    xOp(pArg);
    return SQLITE_ERROR;
  }
  pTask->pJCNext = pParse->pClup;
  pParse->pClup = pTask;
  pTask->xOp = xOp;
  pTask->pArg = pArg;
  return SQLITE_OK;
}

/*
** Convert the JsonNode pNode into a pure JSON string and
** append to pOut.  Subsubstructure is also included.  Return
** the number of JsonNode objects that are encoded.
*/
static void jsonRenderNode(
  JsonParse *pParse,             /* the complete parse of the JSON */
  JsonNode *pNode,               /* The node to render */
  JsonString *pOut               /* Write JSON here */
){
  assert( pNode!=0 );
  while( (pNode->jnFlags & JNODE_REPLACE)!=0 && pParse->useMod ){
    u32 idx = (u32)(pNode - pParse->aNode);
    u32 i = pParse->iSubst;
    while( 1 /*exit-by-break*/ ){
      assert( i<pParse->nNode );
      assert( pParse->aNode[i].eType==JSON_SUBST );
      assert( pParse->aNode[i].eU==4 );
      assert( pParse->aNode[i].u.iPrev<i );
      if( pParse->aNode[i].n==idx ){
        pNode = &pParse->aNode[i+1];
        break;
      }
      i = pParse->aNode[i].u.iPrev;
    }
  }
  switch( pNode->eType ){
    default: {
      assert( pNode->eType==JSON_NULL );
      jsonAppendRawNZ(pOut, "null", 4);
      break;
    }
    case JSON_TRUE: {
      jsonAppendRawNZ(pOut, "true", 4);
      break;
    }
    case JSON_FALSE: {
      jsonAppendRawNZ(pOut, "false", 5);
      break;
    }
    case JSON_STRING: {
      assert( pNode->eU==1 );
      if( pNode->jnFlags & JNODE_RAW ){
        if( pNode->jnFlags & JNODE_LABEL ){
          jsonAppendChar(pOut, '"');
          jsonAppendRaw(pOut, pNode->u.zJContent, pNode->n);
          jsonAppendChar(pOut, '"');
        }else{
          jsonAppendString(pOut, pNode->u.zJContent, pNode->n);
        }
      }else if( pNode->jnFlags & JNODE_JSON5 ){
        jsonAppendNormalizedString(pOut, pNode->u.zJContent, pNode->n);
      }else{
        assert( pNode->n>0 );
        jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n);
      }
      break;
    }
    case JSON_REAL: {
      assert( pNode->eU==1 );
      if( pNode->jnFlags & JNODE_JSON5 ){
        jsonAppendNormalizedReal(pOut, pNode->u.zJContent, pNode->n);
      }else{
        assert( pNode->n>0 );
        jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n);
      }
      break;
    }
    case JSON_INT: {
      assert( pNode->eU==1 );
      if( pNode->jnFlags & JNODE_JSON5 ){
        jsonAppendNormalizedInt(pOut, pNode->u.zJContent, pNode->n);
      }else{
        assert( pNode->n>0 );
        jsonAppendRawNZ(pOut, pNode->u.zJContent, pNode->n);
      }
      break;
    }
    case JSON_ARRAY: {
      u32 j = 1;
      jsonAppendChar(pOut, '[');
      for(;;){
        while( j<=pNode->n ){
          if( (pNode[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){
            jsonAppendSeparator(pOut);
            jsonRenderNode(pParse, &pNode[j], pOut);
          }
          j += jsonNodeSize(&pNode[j]);
        }
        if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
        if( pParse->useMod==0 ) break;
        assert( pNode->eU==2 );
        pNode = &pParse->aNode[pNode->u.iAppend];
        j = 1;
      }
      jsonAppendChar(pOut, ']');
      break;
    }
    case JSON_OBJECT: {
      u32 j = 1;
      jsonAppendChar(pOut, '{');
      for(;;){
        while( j<=pNode->n ){
          if( (pNode[j+1].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ){
            jsonAppendSeparator(pOut);
            jsonRenderNode(pParse, &pNode[j], pOut);
            jsonAppendChar(pOut, ':');
            jsonRenderNode(pParse, &pNode[j+1], pOut);
          }
          j += 1 + jsonNodeSize(&pNode[j+1]);
        }
        if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
        if( pParse->useMod==0 ) break;
        assert( pNode->eU==2 );
        pNode = &pParse->aNode[pNode->u.iAppend];
        j = 1;
      }
      jsonAppendChar(pOut, '}');
      break;
    }
  }
}

/*
** Return a JsonNode and all its descendants as a JSON string.
*/
static void jsonReturnJson(
  JsonParse *pParse,          /* The complete JSON */
  JsonNode *pNode,            /* Node to return */
  sqlite3_context *pCtx,      /* Return value for this function */
  int bGenerateAlt,           /* Also store the rendered text in zAlt */
  int omitSubtype             /* Do not call sqlite3_result_subtype() */
){
  JsonString s;
  if( pParse->oom ){
    sqlite3_result_error_nomem(pCtx);
    return;
  }
  if( pParse->nErr==0 ){
    jsonInit(&s, pCtx);
    jsonRenderNode(pParse, pNode, &s);
    if( bGenerateAlt && pParse->zAlt==0 && jsonForceRCStr(&s) ){
      pParse->zAlt = sqlite3RCStrRef(s.zBuf);
      pParse->nAlt = s.nUsed;
    }
    jsonResult(&s);
    if( !omitSubtype ) sqlite3_result_subtype(pCtx, JSON_SUBTYPE);
  }
}


/*
** Translate a single byte of Hex into an integer.
** This routine only works if h really is a valid hexadecimal
** character:  0..9a..fA..F

*/
static u8 jsonHexToInt(int h){
  assert( (h>='0' && h<='9') ||  (h>='a' && h<='f') ||  (h>='A' && h<='F') );
#ifdef SQLITE_EBCDIC
  h += 9*(1&~(h>>4));
#else

  h += 9*(1&(h>>6));
#endif
  return (u8)(h & 0xf);
}

/*
** Convert a 4-byte hex string into an integer
*/
static u32 jsonHexToInt4(const char *z){
  u32 v;
  assert( sqlite3Isxdigit(z[0]) );
  assert( sqlite3Isxdigit(z[1]) );
  assert( sqlite3Isxdigit(z[2]) );
  assert( sqlite3Isxdigit(z[3]) );
  v = (jsonHexToInt(z[0])<<12)
    + (jsonHexToInt(z[1])<<8)
    + (jsonHexToInt(z[2])<<4)
    + jsonHexToInt(z[3]);
  return v;
}

/*
** Make the JsonNode the return value of the function.
*/
static void jsonReturn(
  JsonParse *pParse,          /* Complete JSON parse tree */
  JsonNode *pNode,            /* Node to return */
  sqlite3_context *pCtx,      /* Return value for this function */
  int omitSubtype             /* Do not call sqlite3_result_subtype() */
){
  switch( pNode->eType ){
    default: {
      assert( pNode->eType==JSON_NULL );
      sqlite3_result_null(pCtx);
      break;
    }
    case JSON_TRUE: {
      sqlite3_result_int(pCtx, 1);
      break;
    }
    case JSON_FALSE: {
      sqlite3_result_int(pCtx, 0);
      break;
    }
    case JSON_INT: {
      sqlite3_int64 i = 0;
      int rc;
      int bNeg = 0;
      const char *z;

      assert( pNode->eU==1 );
      z = pNode->u.zJContent;
      if( z[0]=='-' ){ z++; bNeg = 1; }
      else if( z[0]=='+' ){ z++; }
      rc = sqlite3DecOrHexToI64(z, &i);
      if( rc<=1 ){
        sqlite3_result_int64(pCtx, bNeg ? -i : i);
      }else if( rc==3 && bNeg ){
        sqlite3_result_int64(pCtx, SMALLEST_INT64);
      }else{
        goto to_double;
      }
      break;
    }
    case JSON_REAL: {
      double r;
      const char *z;
      assert( pNode->eU==1 );
    to_double:
      z = pNode->u.zJContent;
      sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8);
      sqlite3_result_double(pCtx, r);
      break;
    }
    case JSON_STRING: {
      if( pNode->jnFlags & JNODE_RAW ){
        assert( pNode->eU==1 );
        sqlite3_result_text(pCtx, pNode->u.zJContent, pNode->n,
                            SQLITE_TRANSIENT);
      }else if( (pNode->jnFlags & JNODE_ESCAPE)==0 ){
        /* JSON formatted without any backslash-escapes */
        assert( pNode->eU==1 );
        sqlite3_result_text(pCtx, pNode->u.zJContent+1, pNode->n-2,
                            SQLITE_TRANSIENT);
      }else{
        /* Translate JSON formatted string into raw text */
        u32 i;
        u32 n = pNode->n;
        const char *z;
        char *zOut;
        u32 j;
        u32 nOut = n;
        assert( pNode->eU==1 );
        z = pNode->u.zJContent;
        zOut = sqlite3_malloc( nOut+1 );
        if( zOut==0 ){
          sqlite3_result_error_nomem(pCtx);
          break;
        }
        for(i=1, j=0; i<n-1; i++){
          char c = z[i];
          if( c=='\\' ){
            c = z[++i];
            if( c=='u' ){
              u32 v = jsonHexToInt4(z+i+1);
              i += 4;
              if( v==0 ) break;
              if( v<=0x7f ){
                zOut[j++] = (char)v;
              }else if( v<=0x7ff ){
                zOut[j++] = (char)(0xc0 | (v>>6));
                zOut[j++] = 0x80 | (v&0x3f);
              }else{
                u32 vlo;
                if( (v&0xfc00)==0xd800
                  && i<n-6
                  && z[i+1]=='\\'
                  && z[i+2]=='u'
                  && ((vlo = jsonHexToInt4(z+i+3))&0xfc00)==0xdc00
                ){
                  /* We have a surrogate pair */
                  v = ((v&0x3ff)<<10) + (vlo&0x3ff) + 0x10000;
                  i += 6;
                  zOut[j++] = 0xf0 | (v>>18);
                  zOut[j++] = 0x80 | ((v>>12)&0x3f);
                  zOut[j++] = 0x80 | ((v>>6)&0x3f);
                  zOut[j++] = 0x80 | (v&0x3f);
                }else{
                  zOut[j++] = 0xe0 | (v>>12);
                  zOut[j++] = 0x80 | ((v>>6)&0x3f);
                  zOut[j++] = 0x80 | (v&0x3f);
                }
              }
              continue;
            }else if( c=='b' ){
              c = '\b';
            }else if( c=='f' ){
              c = '\f';
            }else if( c=='n' ){
              c = '\n';
            }else if( c=='r' ){
              c = '\r';
            }else if( c=='t' ){
              c = '\t';
            }else if( c=='v' ){
              c = '\v';
            }else if( c=='\'' || c=='"' || c=='/' || c=='\\' ){
              /* pass through unchanged */
            }else if( c=='0' ){
              c = 0;
            }else if( c=='x' ){
              c = (jsonHexToInt(z[i+1])<<4) | jsonHexToInt(z[i+2]);
              i += 2;
            }else if( c=='\r' && z[i+1]=='\n' ){
              i++;
              continue;
            }else if( 0xe2==(u8)c ){
              assert( 0x80==(u8)z[i+1] );
              assert( 0xa8==(u8)z[i+2] || 0xa9==(u8)z[i+2] );
              i += 2;
              continue;
            }else{
              continue;
            }
          } /* end if( c=='\\' ) */
          zOut[j++] = c;
        } /* end for() */
        zOut[j] = 0;
        sqlite3_result_text(pCtx, zOut, j, sqlite3_free);
      }
      break;
    }
    case JSON_ARRAY:
    case JSON_OBJECT: {
      jsonReturnJson(pParse, pNode, pCtx, 0, omitSubtype);
      break;
    }
  }
}

/* Forward reference */
static int jsonParseAddNode(JsonParse*,u32,u32,const char*);

/*
** A macro to hint to the compiler that a function should not be
** inlined.
*/
#if defined(__GNUC__)
#  define JSON_NOINLINE  __attribute__((noinline))
#elif defined(_MSC_VER) && _MSC_VER>=1310
#  define JSON_NOINLINE  __declspec(noinline)
#else
#  define JSON_NOINLINE
#endif


/*
** Add a single node to pParse->aNode after first expanding the
** size of the aNode array.  Return the index of the new node.
**
** If an OOM error occurs, set pParse->oom and return -1.
*/
static JSON_NOINLINE int jsonParseAddNodeExpand(
  JsonParse *pParse,        /* Append the node to this object */
  u32 eType,                /* Node type */
  u32 n,                    /* Content size or sub-node count */
  const char *zContent      /* Content */
){
  u32 nNew;
  JsonNode *pNew;
  assert( pParse->nNode>=pParse->nAlloc );
  if( pParse->oom ) return -1;
  nNew = pParse->nAlloc*2 + 10;
  pNew = sqlite3_realloc64(pParse->aNode, sizeof(JsonNode)*nNew);
  if( pNew==0 ){
    pParse->oom = 1;
    return -1;
  }
  pParse->nAlloc = sqlite3_msize(pNew)/sizeof(JsonNode);
  pParse->aNode = pNew;
  assert( pParse->nNode<pParse->nAlloc );
  return jsonParseAddNode(pParse, eType, n, zContent);
}

/*
** Create a new JsonNode instance based on the arguments and append that
** instance to the JsonParse.  Return the index in pParse->aNode[] of the
** new node, or -1 if a memory allocation fails.
*/
static int jsonParseAddNode(
  JsonParse *pParse,        /* Append the node to this object */
  u32 eType,                /* Node type */
  u32 n,                    /* Content size or sub-node count */
  const char *zContent      /* Content */
){
  JsonNode *p;
  assert( pParse->aNode!=0 || pParse->nNode>=pParse->nAlloc );
  if( pParse->nNode>=pParse->nAlloc ){
    return jsonParseAddNodeExpand(pParse, eType, n, zContent);
  }
  assert( pParse->aNode!=0 );
  p = &pParse->aNode[pParse->nNode];
  assert( p!=0 );
  p->eType = (u8)(eType & 0xff);
  p->jnFlags = (u8)(eType >> 8);
  VVA( p->eU = zContent ? 1 : 0 );
  p->n = n;
  p->u.zJContent = zContent;
  return pParse->nNode++;
}

/*
** Add an array of new nodes to the current pParse->aNode array.
** Return the index of the first node added.
**
** If an OOM error occurs, set pParse->oom.
*/
static void jsonParseAddNodeArray(
  JsonParse *pParse,        /* Append the node to this object */
  JsonNode *aNode,          /* Array of nodes to add */
  u32 nNode                 /* Number of elements in aNew */
){
  assert( aNode!=0 );
  assert( nNode>=1 );
  if( pParse->nNode + nNode > pParse->nAlloc ){
    u32 nNew = pParse->nNode + nNode;
    JsonNode *aNew = sqlite3_realloc64(pParse->aNode, nNew*sizeof(JsonNode));
    if( aNew==0 ){
      pParse->oom = 1;
      return;
    }
    pParse->nAlloc = sqlite3_msize(aNew)/sizeof(JsonNode);
    pParse->aNode = aNew;
  }
  memcpy(&pParse->aNode[pParse->nNode], aNode, nNode*sizeof(JsonNode));
  pParse->nNode += nNode;
}

/*
** Add a new JSON_SUBST node.  The node immediately following
** this new node will be the substitute content for iNode.
*/
static int jsonParseAddSubstNode(
  JsonParse *pParse,       /* Add the JSON_SUBST here */
  u32 iNode                /* References this node */
){
  int idx = jsonParseAddNode(pParse, JSON_SUBST, iNode, 0);
  if( pParse->oom ) return -1;
  pParse->aNode[iNode].jnFlags |= JNODE_REPLACE;
  pParse->aNode[idx].eU = 4;
  pParse->aNode[idx].u.iPrev = pParse->iSubst;
  pParse->iSubst = idx;
  pParse->hasMod = 1;
  pParse->useMod = 1;
  return idx;
}

/*
** Return true if z[] begins with 2 (or more) hexadecimal digits
*/
static int jsonIs2Hex(const char *z){
  return sqlite3Isxdigit(z[0]) && sqlite3Isxdigit(z[1]);
}








>
>
>
>
>
>
|

|
|






|
|

|
>
>
>
>

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


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


|
>
|
|

>
>

|



|


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





<
<
<
<
<
<

<
<
<
<
<
<
<
<
<
<



>


|
>
|
|
>




|
|
<
<
<
<


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



|
|
>


<
|
|
|
>
|









<
<
<
<







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







203925
203926
203927
203928
203929
203930
203931
203932
203933
203934
203935
203936
203937
203938
203939
203940
203941
203942
203943
203944
203945
203946
203947
203948
203949
203950
203951
203952
203953
203954
203955
203956
203957
203958
203959
203960
203961
203962
203963
203964
203965
203966
203967
203968
203969
203970
203971
203972
203973
203974
203975
203976
203977
203978
203979
203980
203981
203982
203983
203984
203985
203986
203987
203988
203989
203990
203991
203992
203993
203994
203995
203996
203997
203998
203999
204000
204001













204002
204003
204004
204005
204006






204007










204008
204009
204010
204011
204012
204013
204014
204015
204016
204017
204018
204019
204020
204021
204022
204023
204024




204025
204026
204027
204028
204029
204030
204031
204032
204033
204034
204035

























































































204036
























































204037











204038














204039
204040
204041
204042
204043
204044
204045
204046
204047

204048
204049
204050
204051
204052
204053
204054
204055
204056
204057
204058
204059
204060
204061




204062
204063
204064
204065
204066
204067
204068




















































































































































































































































































204069
204070
204071
204072
204073
204074
204075
        jsonAppendRaw(p, z, n);
      }else{
        jsonAppendString(p, z, n);
      }
      break;
    }
    default: {
      if( jsonFuncArgMightBeBinary(pValue) ){
        JsonParse px;
        memset(&px, 0, sizeof(px));
        px.aBlob = (u8*)sqlite3_value_blob(pValue);
        px.nBlob = sqlite3_value_bytes(pValue);
        jsonTranslateBlobToText(&px, 0, p);
      }else if( p->eErr==0 ){
        sqlite3_result_error(p->pCtx, "JSON cannot hold BLOB values", -1);
        p->eErr = JSTRING_ERR;
        jsonStringReset(p);
      }
      break;
    }
  }
}

/* Make the text in p (which is probably a generated JSON text string)
** the result of the SQL function.
**
** The JsonString is reset.
**
** If pParse and ctx are both non-NULL, then the SQL string in p is
** loaded into the zJson field of the pParse object as a RCStr and the
** pParse is added to the cache.
*/
static void jsonReturnString(
  JsonString *p,            /* String to return */
  JsonParse *pParse,        /* JSONB source or NULL */
  sqlite3_context *ctx      /* Where to cache */
){
  assert( (pParse!=0)==(ctx!=0) );
  assert( ctx==0 || ctx==p->pCtx );
  if( p->eErr==0 ){
    int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(p->pCtx));
    if( flags & JSON_BLOB ){
      jsonReturnStringAsBlob(p);
    }else if( p->bStatic ){
      sqlite3_result_text64(p->pCtx, p->zBuf, p->nUsed,
                            SQLITE_TRANSIENT, SQLITE_UTF8);
    }else if( jsonStringTerminate(p) ){
      if( pParse && pParse->bJsonIsRCStr==0 && pParse->nBlobAlloc>0 ){
        int rc;
        pParse->zJson = sqlite3RCStrRef(p->zBuf);
        pParse->nJson = p->nUsed;
        pParse->bJsonIsRCStr = 1;
        rc = jsonCacheInsert(ctx, pParse);
        if( rc==SQLITE_NOMEM ){
          sqlite3_result_error_nomem(ctx);
          jsonStringReset(p);
          return;
        }
      }
      sqlite3_result_text64(p->pCtx, sqlite3RCStrRef(p->zBuf), p->nUsed,
                            sqlite3RCStrUnref,
                            SQLITE_UTF8);
    }else{
      sqlite3_result_error_nomem(p->pCtx);
    }
  }else if( p->eErr & JSTRING_OOM ){
    sqlite3_result_error_nomem(p->pCtx);
  }else if( p->eErr & JSTRING_MALFORMED ){
    sqlite3_result_error(p->pCtx, "malformed JSON", -1);
  }
  jsonStringReset(p);
}

/**************************************************************************
** Utility routines for dealing with JsonParse objects
**************************************************************************/














/*
** Reclaim all memory allocated by a JsonParse object.  But do not
** delete the JsonParse object itself.
*/
static void jsonParseReset(JsonParse *pParse){






  assert( pParse->nJPRef<=1 );










  if( pParse->bJsonIsRCStr ){
    sqlite3RCStrUnref(pParse->zJson);
    pParse->zJson = 0;
    pParse->nJson = 0;
    pParse->bJsonIsRCStr = 0;
  }
  if( pParse->nBlobAlloc ){
    sqlite3DbFree(pParse->db, pParse->aBlob);
    pParse->aBlob = 0;
    pParse->nBlob = 0;
    pParse->nBlobAlloc = 0;
  }
}

/*
** Decrement the reference count on the JsonParse object.  When the
** count reaches zero, free the object.




*/
static void jsonParseFree(JsonParse *pParse){
  if( pParse ){
    if( pParse->nJPRef>1 ){
      pParse->nJPRef--;
    }else{
      jsonParseReset(pParse);
      sqlite3DbFree(pParse->db, pParse);
    }
  }
}


















































































































































/**************************************************************************











** Utility routines for the JSON text parser














**************************************************************************/

/*
** Translate a single byte of Hex into an integer.
** This routine only gives a correct answer if h really is a valid hexadecimal
** character:  0..9a..fA..F.  But unlike sqlite3HexToInt(), it does not
** assert() if the digit is not hex.
*/
static u8 jsonHexToInt(int h){

#ifdef SQLITE_ASCII
  h += 9*(1&(h>>6));
#endif
#ifdef SQLITE_EBCDIC
  h += 9*(1&~(h>>4));
#endif
  return (u8)(h & 0xf);
}

/*
** Convert a 4-byte hex string into an integer
*/
static u32 jsonHexToInt4(const char *z){
  u32 v;




  v = (jsonHexToInt(z[0])<<12)
    + (jsonHexToInt(z[1])<<8)
    + (jsonHexToInt(z[2])<<4)
    + jsonHexToInt(z[3]);
  return v;
}





















































































































































































































































































/*
** Return true if z[] begins with 2 (or more) hexadecimal digits
*/
static int jsonIs2Hex(const char *z){
  return sqlite3Isxdigit(z[0]) && sqlite3Isxdigit(z[1]);
}

203955
203956
203957
203958
203959
203960
203961
203962
203963
203964















































































































203965











































203966





















203967


























































































































































203968





































































































203969
203970
203971



203972

203973
203974
203975
203976
203977
203978
203979
203980
203981
203982
203983
203984
203985
203986
203987
203988
203989
203990
203991
203992

203993
203994
203995
203996
203997
203998

203999
204000
204001
204002

204003
204004
204005
204006
204007
204008

204009
204010
204011
204012
204013
204014
204015
204016
204017

204018
204019
204020
204021
204022
204023
204024
204025
204026
204027
204028
204029
204030
204031
204032
204033
204034
204035
204036
204037

204038
204039
204040
204041
204042
204043
204044
204045
204046
204047
204048
204049
204050
204051
204052
204053
204054
204055
204056
204057
204058
204059
204060
204061
204062
204063
204064
204065
204066
204067
204068
204069
204070
204071
204072
204073
204074
204075
204076
204077
204078
204079
204080
204081
204082
204083
204084
204085
204086
204087
204088
204089

204090

204091
204092
204093
204094
204095
204096
204097
204098
204099
204100
204101
204102
204103
204104
204105
204106
204107
204108
204109
204110
204111
204112
204113
204114
204115
204116
204117
204118
204119
204120
204121
204122
204123
204124
204125
204126
204127
204128
204129
204130
204131
204132
204133
204134
204135
204136
204137
204138
204139
204140
204141
204142
204143
204144
204145
204146
204147
204148
204149
204150

204151









204152
204153
204154
204155
204156
204157
204158
204159
204160
204161
204162
204163
204164
204165
204166
204167
204168
204169
204170
204171
204172
204173
204174
204175
204176
204177
204178


204179

204180
204181
204182
204183
204184
204185
204186
204187
204188
204189
204190
204191
204192
204193
204194
204195
204196
204197
204198
204199
204200
204201
204202
204203
204204
204205
204206
204207
204208
204209
204210
204211
204212
204213
204214
204215
204216
204217
204218
204219
204220
204221
204222
204223
204224
204225
204226
204227
204228
204229
204230
204231
204232
204233
204234
204235
204236
204237
204238
204239
204240
204241
204242
204243
204244
204245
204246
204247
204248
204249
204250
204251
204252
204253
204254
204255
204256
204257
204258
204259
204260
204261
204262
204263
204264
204265
204266
204267
204268
204269
204270
204271
204272
204273
204274
204275
204276
204277
204278
204279
204280
204281
204282
204283
204284

204285
204286
204287
204288
204289
204290
204291
204292
204293
204294
204295
204296
204297
204298
204299
204300
204301
204302
204303
204304
204305
204306
204307
204308
204309
204310
204311
204312
204313
204314
204315
204316
204317
204318
204319
204320
204321
204322
204323
204324
204325
204326
204327
204328
204329
204330
204331
204332
204333
204334
204335




204336
204337
204338
204339
204340
204341
204342
204343
  char c2;
  char n;
  char eType;
  char nRepl;
  char *zMatch;
  char *zRepl;
} aNanInfName[] = {
  { 'i', 'I', 3, JSON_REAL, 7, "inf", "9.0e999" },
  { 'i', 'I', 8, JSON_REAL, 7, "infinity", "9.0e999" },
  { 'n', 'N', 3, JSON_NULL, 4, "NaN", "null" },















































































































  { 'q', 'Q', 4, JSON_NULL, 4, "QNaN", "null" },











































  { 's', 'S', 4, JSON_NULL, 4, "SNaN", "null" },





















};
































































































































































































































































/*
** Parse a single JSON value which begins at pParse->zJson[i].  Return the
** index of the first character past the end of the value parsed.



**

** Special return values:
**
**      0    End of input
**     -1    Syntax error
**     -2    '}' seen
**     -3    ']' seen
**     -4    ',' seen
**     -5    ':' seen
*/
static int jsonParseValue(JsonParse *pParse, u32 i){
  char c;
  u32 j;
  int iThis;
  int x;
  JsonNode *pNode;
  const char *z = pParse->zJson;
json_parse_restart:
  switch( (u8)z[i] ){
  case '{': {
    /* Parse object */

    iThis = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
    if( iThis<0 ) return -1;
    if( ++pParse->iDepth > JSON_MAX_DEPTH ){
      pParse->iErr = i;
      return -1;
    }

    for(j=i+1;;j++){
      u32 nNode = pParse->nNode;
      x = jsonParseValue(pParse, j);
      if( x<=0 ){

        if( x==(-2) ){
          j = pParse->iErr;
          if( pParse->nNode!=(u32)iThis+1 ) pParse->hasNonstd = 1;
          break;
        }
        j += json5Whitespace(&z[j]);

        if( sqlite3JsonId1(z[j])
         || (z[j]=='\\' && z[j+1]=='u' && jsonIs4Hex(&z[j+2]))
        ){
          int k = j+1;
          while( (sqlite3JsonId2(z[k]) && json5Whitespace(&z[k])==0)
            || (z[k]=='\\' && z[k+1]=='u' && jsonIs4Hex(&z[k+2]))
          ){
            k++;
          }

          jsonParseAddNode(pParse, JSON_STRING | (JNODE_RAW<<8), k-j, &z[j]);
          pParse->hasNonstd = 1;
          x = k;
        }else{
          if( x!=-1 ) pParse->iErr = j;
          return -1;
        }
      }
      if( pParse->oom ) return -1;
      pNode = &pParse->aNode[nNode];
      if( pNode->eType!=JSON_STRING ){
        pParse->iErr = j;
        return -1;
      }
      pNode->jnFlags |= JNODE_LABEL;
      j = x;
      if( z[j]==':' ){
        j++;
      }else{
        if( fast_isspace(z[j]) ){

          do{ j++; }while( fast_isspace(z[j]) );
          if( z[j]==':' ){
            j++;
            goto parse_object_value;
          }
        }
        x = jsonParseValue(pParse, j);
        if( x!=(-5) ){
          if( x!=(-1) ) pParse->iErr = j;
          return -1;
        }
        j = pParse->iErr+1;
      }
    parse_object_value:
      x = jsonParseValue(pParse, j);
      if( x<=0 ){
        if( x!=(-1) ) pParse->iErr = j;
        return -1;
      }
      j = x;
      if( z[j]==',' ){
        continue;
      }else if( z[j]=='}' ){
        break;
      }else{
        if( fast_isspace(z[j]) ){
          do{ j++; }while( fast_isspace(z[j]) );
          if( z[j]==',' ){
            continue;
          }else if( z[j]=='}' ){
            break;
          }
        }
        x = jsonParseValue(pParse, j);
        if( x==(-4) ){
          j = pParse->iErr;
          continue;
        }
        if( x==(-2) ){
          j = pParse->iErr;
          break;
        }
      }
      pParse->iErr = j;
      return -1;
    }
    pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
    pParse->iDepth--;
    return j+1;
  }
  case '[': {
    /* Parse array */

    iThis = jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);

    if( iThis<0 ) return -1;
    if( ++pParse->iDepth > JSON_MAX_DEPTH ){
      pParse->iErr = i;
      return -1;
    }
    memset(&pParse->aNode[iThis].u, 0, sizeof(pParse->aNode[iThis].u));
    for(j=i+1;;j++){
      x = jsonParseValue(pParse, j);
      if( x<=0 ){
        if( x==(-3) ){
          j = pParse->iErr;
          if( pParse->nNode!=(u32)iThis+1 ) pParse->hasNonstd = 1;
          break;
        }
        if( x!=(-1) ) pParse->iErr = j;
        return -1;
      }
      j = x;
      if( z[j]==',' ){
        continue;
      }else if( z[j]==']' ){
        break;
      }else{
        if( fast_isspace(z[j]) ){
          do{ j++; }while( fast_isspace(z[j]) );
          if( z[j]==',' ){
            continue;
          }else if( z[j]==']' ){
            break;
          }
        }
        x = jsonParseValue(pParse, j);
        if( x==(-4) ){
          j = pParse->iErr;
          continue;
        }
        if( x==(-3) ){
          j = pParse->iErr;
          break;
        }
      }
      pParse->iErr = j;
      return -1;
    }
    pParse->aNode[iThis].n = pParse->nNode - (u32)iThis - 1;
    pParse->iDepth--;
    return j+1;
  }
  case '\'': {
    u8 jnFlags;
    char cDelim;
    pParse->hasNonstd = 1;
    jnFlags = JNODE_JSON5;
    goto parse_string;
  case '"':
    /* Parse string */
    jnFlags = 0;
  parse_string:
    cDelim = z[i];
    for(j=i+1; 1; j++){

      if( jsonIsOk[(unsigned char)z[j]] ) continue;









      c = z[j];
      if( c==cDelim ){
        break;
      }else if( c=='\\' ){
        c = z[++j];
        if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f'
           || c=='n' || c=='r' || c=='t'
           || (c=='u' && jsonIs4Hex(&z[j+1])) ){
          jnFlags |= JNODE_ESCAPE;
        }else if( c=='\'' || c=='0' || c=='v' || c=='\n'
           || (0xe2==(u8)c && 0x80==(u8)z[j+1]
                && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2]))
           || (c=='x' && jsonIs2Hex(&z[j+1])) ){
          jnFlags |= (JNODE_ESCAPE|JNODE_JSON5);
          pParse->hasNonstd = 1;
        }else if( c=='\r' ){
          if( z[j+1]=='\n' ) j++;
          jnFlags |= (JNODE_ESCAPE|JNODE_JSON5);
          pParse->hasNonstd = 1;
        }else{
          pParse->iErr = j;
          return -1;
        }
      }else if( c<=0x1f ){
        /* Control characters are not allowed in strings */
        pParse->iErr = j;
        return -1;


      }

    }
    jsonParseAddNode(pParse, JSON_STRING | (jnFlags<<8), j+1-i, &z[i]);
    return j+1;
  }
  case 't': {
    if( strncmp(z+i,"true",4)==0 && !sqlite3Isalnum(z[i+4]) ){
      jsonParseAddNode(pParse, JSON_TRUE, 0, 0);
      return i+4;
    }
    pParse->iErr = i;
    return -1;
  }
  case 'f': {
    if( strncmp(z+i,"false",5)==0 && !sqlite3Isalnum(z[i+5]) ){
      jsonParseAddNode(pParse, JSON_FALSE, 0, 0);
      return i+5;
    }
    pParse->iErr = i;
    return -1;
  }
  case '+': {
    u8 seenDP, seenE, jnFlags;
    pParse->hasNonstd = 1;
    jnFlags = JNODE_JSON5;
    goto parse_number;
  case '.':
    if( sqlite3Isdigit(z[i+1]) ){
      pParse->hasNonstd = 1;
      jnFlags = JNODE_JSON5;
      seenE = 0;
      seenDP = JSON_REAL;
      goto parse_number_2;
    }
    pParse->iErr = i;
    return -1;
  case '-':
  case '0':
  case '1':
  case '2':
  case '3':
  case '4':
  case '5':
  case '6':
  case '7':
  case '8':
  case '9':
    /* Parse number */
    jnFlags = 0;
  parse_number:
    seenDP = JSON_INT;
    seenE = 0;
    assert( '-' < '0' );
    assert( '+' < '0' );
    assert( '.' < '0' );
    c = z[i];

    if( c<='0' ){
      if( c=='0' ){
        if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){
          assert( seenDP==JSON_INT );
          pParse->hasNonstd = 1;
          jnFlags |= JNODE_JSON5;
          for(j=i+3; sqlite3Isxdigit(z[j]); j++){}
          goto parse_number_finish;
        }else if( sqlite3Isdigit(z[i+1]) ){
          pParse->iErr = i+1;
          return -1;
        }
      }else{
        if( !sqlite3Isdigit(z[i+1]) ){
          /* JSON5 allows for "+Infinity" and "-Infinity" using exactly
          ** that case.  SQLite also allows these in any case and it allows
          ** "+inf" and "-inf". */
          if( (z[i+1]=='I' || z[i+1]=='i')
           && sqlite3StrNICmp(&z[i+1], "inf",3)==0
          ){
            pParse->hasNonstd = 1;
            if( z[i]=='-' ){
              jsonParseAddNode(pParse, JSON_REAL, 8, "-9.0e999");
            }else{
              jsonParseAddNode(pParse, JSON_REAL, 7, "9.0e999");
            }
            return i + (sqlite3StrNICmp(&z[i+4],"inity",5)==0 ? 9 : 4);
          }
          if( z[i+1]=='.' ){
            pParse->hasNonstd = 1;
            jnFlags |= JNODE_JSON5;
            goto parse_number_2;
          }
          pParse->iErr = i;
          return -1;
        }
        if( z[i+1]=='0' ){
          if( sqlite3Isdigit(z[i+2]) ){
            pParse->iErr = i+1;
            return -1;
          }else if( (z[i+2]=='x' || z[i+2]=='X') && sqlite3Isxdigit(z[i+3]) ){
            pParse->hasNonstd = 1;
            jnFlags |= JNODE_JSON5;
            for(j=i+4; sqlite3Isxdigit(z[j]); j++){}
            goto parse_number_finish;
          }
        }
      }
    }

  parse_number_2:
    for(j=i+1;; j++){
      c = z[j];
      if( sqlite3Isdigit(c) ) continue;
      if( c=='.' ){
        if( seenDP==JSON_REAL ){
          pParse->iErr = j;
          return -1;
        }
        seenDP = JSON_REAL;
        continue;
      }
      if( c=='e' || c=='E' ){
        if( z[j-1]<'0' ){
          if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){
            pParse->hasNonstd = 1;
            jnFlags |= JNODE_JSON5;
          }else{
            pParse->iErr = j;
            return -1;
          }
        }
        if( seenE ){
          pParse->iErr = j;
          return -1;
        }
        seenDP = JSON_REAL;
        seenE = 1;
        c = z[j+1];
        if( c=='+' || c=='-' ){
          j++;
          c = z[j+1];
        }
        if( c<'0' || c>'9' ){
          pParse->iErr = j;
          return -1;
        }
        continue;
      }
      break;
    }
    if( z[j-1]<'0' ){
      if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){
        pParse->hasNonstd = 1;
        jnFlags |= JNODE_JSON5;
      }else{
        pParse->iErr = j;
        return -1;
      }
    }
  parse_number_finish:




    jsonParseAddNode(pParse, seenDP | (jnFlags<<8), j - i, &z[i]);
    return j;
  }
  case '}': {
    pParse->iErr = i;
    return -2;  /* End of {...} */
  }
  case ']': {







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

|
<
>
>
>

>
|


|
|
|
|
|

|


|

|





>
|
<




>

|
|

>


|



>

|



|



>
|








|
|



<




|
>
|





|







|










|
|






|












|





>
|
>
|




<

|



|











|
|






|












|




|


|



|


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








|




|



|









>
>

>

|




|







|






|

|




|

<
















|

<









|

|
















|

|





|











|






>





|



|






|









|

















|






>
>
>
>
|







204215
204216
204217
204218
204219
204220
204221
204222
204223
204224
204225
204226
204227
204228
204229
204230
204231
204232
204233
204234
204235
204236
204237
204238
204239
204240
204241
204242
204243
204244
204245
204246
204247
204248
204249
204250
204251
204252
204253
204254
204255
204256
204257
204258
204259
204260
204261
204262
204263
204264
204265
204266
204267
204268
204269
204270
204271
204272
204273
204274
204275
204276
204277
204278
204279
204280
204281
204282
204283
204284
204285
204286
204287
204288
204289
204290
204291
204292
204293
204294
204295
204296
204297
204298
204299
204300
204301
204302
204303
204304
204305
204306
204307
204308
204309
204310
204311
204312
204313
204314
204315
204316
204317
204318
204319
204320
204321
204322
204323
204324
204325
204326
204327
204328
204329
204330
204331
204332
204333
204334
204335
204336
204337
204338
204339
204340
204341
204342
204343
204344
204345
204346
204347
204348
204349
204350
204351
204352
204353
204354
204355
204356
204357
204358
204359
204360
204361
204362
204363
204364
204365
204366
204367
204368
204369
204370
204371
204372
204373
204374
204375
204376
204377
204378
204379
204380
204381
204382
204383
204384
204385
204386
204387
204388
204389
204390
204391
204392
204393
204394
204395
204396
204397
204398
204399
204400
204401
204402
204403
204404
204405
204406
204407
204408
204409
204410
204411
204412
204413
204414
204415
204416
204417
204418
204419
204420
204421
204422
204423
204424
204425
204426
204427
204428
204429
204430
204431
204432
204433
204434
204435
204436
204437
204438
204439
204440
204441
204442
204443
204444
204445
204446
204447
204448
204449
204450
204451
204452
204453
204454
204455
204456
204457
204458
204459
204460
204461
204462
204463
204464
204465
204466
204467
204468
204469
204470
204471
204472
204473
204474
204475
204476
204477
204478
204479
204480
204481
204482
204483
204484
204485
204486
204487
204488
204489
204490
204491
204492
204493
204494
204495
204496
204497
204498
204499
204500
204501
204502
204503
204504
204505
204506
204507
204508
204509
204510
204511
204512
204513
204514
204515
204516
204517
204518
204519
204520
204521
204522
204523
204524
204525
204526
204527
204528
204529
204530
204531
204532
204533
204534
204535
204536
204537
204538
204539
204540
204541
204542
204543
204544
204545
204546
204547
204548
204549
204550
204551
204552
204553
204554
204555
204556
204557
204558
204559
204560
204561
204562
204563
204564
204565
204566
204567
204568
204569
204570
204571
204572
204573
204574
204575
204576
204577
204578
204579
204580
204581
204582
204583
204584
204585
204586
204587
204588
204589
204590
204591
204592
204593
204594
204595
204596
204597
204598
204599
204600
204601
204602
204603
204604
204605
204606
204607
204608
204609
204610
204611
204612
204613
204614
204615
204616
204617
204618
204619
204620
204621
204622
204623
204624
204625
204626
204627
204628
204629
204630
204631
204632
204633
204634
204635
204636
204637
204638
204639
204640
204641
204642
204643
204644
204645
204646
204647
204648
204649
204650
204651
204652
204653
204654
204655
204656
204657
204658
204659
204660

204661
204662
204663
204664
204665
204666
204667
204668
204669
204670
204671
204672
204673
204674
204675
204676
204677
204678
204679
204680
204681
204682
204683
204684
204685
204686
204687

204688
204689
204690
204691
204692
204693
204694
204695
204696
204697
204698
204699
204700
204701
204702
204703
204704
204705
204706
204707
204708
204709
204710
204711
204712
204713
204714
204715
204716
204717
204718
204719
204720
204721
204722
204723
204724
204725
204726
204727
204728

204729
204730
204731
204732
204733
204734
204735
204736
204737
204738
204739
204740
204741
204742
204743
204744
204745
204746
204747
204748
204749
204750
204751
204752
204753
204754
204755
204756
204757
204758
204759
204760
204761
204762
204763
204764
204765
204766
204767
204768
204769
204770
204771
204772
204773
204774
204775
204776
204777
204778
204779
204780
204781
204782
204783
204784
204785
204786
204787
204788
204789
204790
204791
204792
204793
204794

204795
204796
204797
204798
204799
204800
204801
204802
204803
204804
204805
204806
204807
204808
204809
204810
204811
204812
204813
204814
204815
204816
204817
204818
204819
204820
204821
204822
204823
204824
204825
204826
204827
204828
204829
204830
204831
204832
204833
204834
204835
204836
204837
204838
204839
204840
204841
204842
204843
204844
204845
204846
204847
204848
204849
204850
204851
204852
204853
204854
204855
204856
204857
204858
204859
204860
204861
204862
204863
204864
204865
204866
204867
204868
204869
204870
204871
204872
204873
204874
204875
204876
204877
204878
204879
204880
204881
204882
204883
204884
204885
204886
204887
204888
204889
204890
204891
204892
204893
204894
204895
204896
204897
204898
204899
204900
204901
204902
204903
204904
204905
204906
204907
204908
204909
204910
204911
204912
204913
204914
204915
204916
204917
204918
204919
204920

204921
204922
204923
204924
204925
204926
204927
204928
204929
204930
204931
204932
204933
204934
204935
204936
204937
204938

204939
204940
204941
204942
204943
204944
204945
204946
204947
204948
204949
204950
204951
204952
204953
204954
204955
204956
204957
204958
204959
204960
204961
204962
204963
204964
204965
204966
204967
204968
204969
204970
204971
204972
204973
204974
204975
204976
204977
204978
204979
204980
204981
204982
204983
204984
204985
204986
204987
204988
204989
204990
204991
204992
204993
204994
204995
204996
204997
204998
204999
205000
205001
205002
205003
205004
205005
205006
205007
205008
205009
205010
205011
205012
205013
205014
205015
205016
205017
205018
205019
205020
205021
205022
205023
205024
205025
205026
205027
205028
205029
205030
205031
205032
205033
205034
205035
205036
205037
205038
205039
205040
205041
205042
205043
205044
205045
205046
205047
205048
205049
205050
205051
205052
205053
205054
205055
205056
205057
  char c2;
  char n;
  char eType;
  char nRepl;
  char *zMatch;
  char *zRepl;
} aNanInfName[] = {
  { 'i', 'I', 3, JSONB_FLOAT, 7, "inf", "9.0e999" },
  { 'i', 'I', 8, JSONB_FLOAT, 7, "infinity", "9.0e999" },
  { 'n', 'N', 3, JSONB_NULL, 4, "NaN", "null" },
  { 'q', 'Q', 4, JSONB_NULL, 4, "QNaN", "null" },
  { 's', 'S', 4, JSONB_NULL, 4, "SNaN", "null" },
};


/*
** Report the wrong number of arguments for json_insert(), json_replace()
** or json_set().
*/
static void jsonWrongNumArgs(
  sqlite3_context *pCtx,
  const char *zFuncName
){
  char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments",
                               zFuncName);
  sqlite3_result_error(pCtx, zMsg, -1);
  sqlite3_free(zMsg);
}

/****************************************************************************
** Utility routines for dealing with the binary BLOB representation of JSON
****************************************************************************/

/*
** Expand pParse->aBlob so that it holds at least N bytes.
**
** Return the number of errors.
*/
static int jsonBlobExpand(JsonParse *pParse, u32 N){
  u8 *aNew;
  u32 t;
  assert( N>pParse->nBlobAlloc );
  if( pParse->nBlobAlloc==0 ){
    t = 100;
  }else{
    t = pParse->nBlobAlloc*2;
  }
  if( t<N ) t = N+100;
  aNew = sqlite3DbRealloc(pParse->db, pParse->aBlob, t);
  if( aNew==0 ){ pParse->oom = 1; return 1; }
  pParse->aBlob = aNew;
  pParse->nBlobAlloc = t;
  return 0;
}

/*
** If pParse->aBlob is not previously editable (because it is taken
** from sqlite3_value_blob(), as indicated by the fact that
** pParse->nBlobAlloc==0 and pParse->nBlob>0) then make it editable
** by making a copy into space obtained from malloc.
**
** Return true on success.  Return false on OOM.
*/
static int jsonBlobMakeEditable(JsonParse *pParse, u32 nExtra){
  u8 *aOld;
  u32 nSize;
  assert( !pParse->bReadOnly );
  if( pParse->oom ) return 0;
  if( pParse->nBlobAlloc>0 ) return 1;
  aOld = pParse->aBlob;
  nSize = pParse->nBlob + nExtra;
  pParse->aBlob = 0;
  if( jsonBlobExpand(pParse, nSize) ){
    return 0;
  }
  assert( pParse->nBlobAlloc >= pParse->nBlob + nExtra );
  memcpy(pParse->aBlob, aOld, pParse->nBlob);
  return 1;
}

/* Expand pParse->aBlob and append one bytes.
*/
static SQLITE_NOINLINE void jsonBlobExpandAndAppendOneByte(
  JsonParse *pParse,
  u8 c
){
  jsonBlobExpand(pParse, pParse->nBlob+1);
  if( pParse->oom==0 ){
    assert( pParse->nBlob+1<=pParse->nBlobAlloc );
    pParse->aBlob[pParse->nBlob++] = c;
  }
}

/* Append a single character.
*/
static void jsonBlobAppendOneByte(JsonParse *pParse, u8 c){
  if( pParse->nBlob >= pParse->nBlobAlloc ){
    jsonBlobExpandAndAppendOneByte(pParse, c);
  }else{
    pParse->aBlob[pParse->nBlob++] = c;
  }
}

/* Slow version of jsonBlobAppendNode() that first resizes the
** pParse->aBlob structure.
*/
static void jsonBlobAppendNode(JsonParse*,u8,u32,const void*);
static SQLITE_NOINLINE void jsonBlobExpandAndAppendNode(
  JsonParse *pParse,
  u8 eType,
  u32 szPayload,
  const void *aPayload
){
  if( jsonBlobExpand(pParse, pParse->nBlob+szPayload+9) ) return;
  jsonBlobAppendNode(pParse, eType, szPayload, aPayload);
}


/* Append an node type byte together with the payload size and
** possibly also the payload.
**
** If aPayload is not NULL, then it is a pointer to the payload which
** is also appended.  If aPayload is NULL, the pParse->aBlob[] array
** is resized (if necessary) so that it is big enough to hold the
** payload, but the payload is not appended and pParse->nBlob is left
** pointing to where the first byte of payload will eventually be.
*/
static void jsonBlobAppendNode(
  JsonParse *pParse,          /* The JsonParse object under construction */
  u8 eType,                   /* Node type.  One of JSONB_* */
  u32 szPayload,              /* Number of bytes of payload */
  const void *aPayload        /* The payload.  Might be NULL */
){
  u8 *a;
  if( pParse->nBlob+szPayload+9 > pParse->nBlobAlloc ){
    jsonBlobExpandAndAppendNode(pParse,eType,szPayload,aPayload);
    return;
  }
  assert( pParse->aBlob!=0 );
  a = &pParse->aBlob[pParse->nBlob];
  if( szPayload<=11 ){
    a[0] = eType | (szPayload<<4);
    pParse->nBlob += 1;
  }else if( szPayload<=0xff ){
    a[0] = eType | 0xc0;
    a[1] = szPayload & 0xff;
    pParse->nBlob += 2;
  }else if( szPayload<=0xffff ){
    a[0] = eType | 0xd0;
    a[1] = (szPayload >> 8) & 0xff;
    a[2] = szPayload & 0xff;
    pParse->nBlob += 3;
  }else{
    a[0] = eType | 0xe0;
    a[1] = (szPayload >> 24) & 0xff;
    a[2] = (szPayload >> 16) & 0xff;
    a[3] = (szPayload >> 8) & 0xff;
    a[4] = szPayload & 0xff;
    pParse->nBlob += 5;
  }
  if( aPayload ){
    pParse->nBlob += szPayload;
    memcpy(&pParse->aBlob[pParse->nBlob-szPayload], aPayload, szPayload);
  }
}

/* Change the payload size for the node at index i to be szPayload.
*/
static int jsonBlobChangePayloadSize(
  JsonParse *pParse,
  u32 i,
  u32 szPayload
){
  u8 *a;
  u8 szType;
  u8 nExtra;
  u8 nNeeded;
  int delta;
  if( pParse->oom ) return 0;
  a = &pParse->aBlob[i];
  szType = a[0]>>4;
  if( szType<=11 ){
    nExtra = 0;
  }else if( szType==12 ){
    nExtra = 1;
  }else if( szType==13 ){
    nExtra = 2;
  }else{
    nExtra = 4;
  }
  if( szPayload<=11 ){
    nNeeded = 0;
  }else if( szPayload<=0xff ){
    nNeeded = 1;
  }else if( szPayload<=0xffff ){
    nNeeded = 2;
  }else{
    nNeeded = 4;
  }
  delta = nNeeded - nExtra;
  if( delta ){
    u32 newSize = pParse->nBlob + delta;
    if( delta>0 ){
      if( newSize>pParse->nBlobAlloc && jsonBlobExpand(pParse, newSize) ){
        return 0;  /* OOM error.  Error state recorded in pParse->oom. */
      }
      a = &pParse->aBlob[i];
      memmove(&a[1+delta], &a[1], pParse->nBlob - (i+1));
    }else{
      memmove(&a[1], &a[1-delta], pParse->nBlob - (i+1-delta));
    }
    pParse->nBlob = newSize;
  }
  if( nNeeded==0 ){
    a[0] = (a[0] & 0x0f) | (szPayload<<4);
  }else if( nNeeded==1 ){
    a[0] = (a[0] & 0x0f) | 0xc0;
    a[1] = szPayload & 0xff;
  }else if( nNeeded==2 ){
    a[0] = (a[0] & 0x0f) | 0xd0;
    a[1] = (szPayload >> 8) & 0xff;
    a[2] = szPayload & 0xff;
  }else{
    a[0] = (a[0] & 0x0f) | 0xe0;
    a[1] = (szPayload >> 24) & 0xff;
    a[2] = (szPayload >> 16) & 0xff;
    a[3] = (szPayload >> 8) & 0xff;
    a[4] = szPayload & 0xff;
  }
  return delta;
}

/*
** If z[0] is 'u' and is followed by exactly 4 hexadecimal character,
** then set *pOp to JSONB_TEXTJ and return true.  If not, do not make
** any changes to *pOp and return false.
*/
static int jsonIs4HexB(const char *z, int *pOp){
  if( z[0]!='u' ) return 0;
  if( !jsonIs4Hex(&z[1]) ) return 0;
  *pOp = JSONB_TEXTJ;
  return 1;
}

/*
** Check a single element of the JSONB in pParse for validity.
**
** The element to be checked starts at offset i and must end at on the
** last byte before iEnd.
**
** Return 0 if everything is correct.  Return the 1-based byte offset of the
** error if a problem is detected.  (In other words, if the error is at offset
** 0, return 1).
*/
static u32 jsonbValidityCheck(
  const JsonParse *pParse,    /* Input JSONB.  Only aBlob and nBlob are used */
  u32 i,                      /* Start of element as pParse->aBlob[i] */
  u32 iEnd,                   /* One more than the last byte of the element */
  u32 iDepth                  /* Current nesting depth */
){
  u32 n, sz, j, k;
  const u8 *z;
  u8 x;
  if( iDepth>JSON_MAX_DEPTH ) return i+1;
  sz = 0;
  n = jsonbPayloadSize(pParse, i, &sz);
  if( NEVER(n==0) ) return i+1;          /* Checked by caller */
  if( NEVER(i+n+sz!=iEnd) ) return i+1;  /* Checked by caller */
  z = pParse->aBlob;
  x = z[i] & 0x0f;
  switch( x ){
    case JSONB_NULL:
    case JSONB_TRUE:
    case JSONB_FALSE: {
      return n+sz==1 ? 0 : i+1;
    }
    case JSONB_INT: {
      if( sz<1 ) return i+1;
      j = i+n;
      if( z[j]=='-' ){
        j++;
        if( sz<2 ) return i+1;
      }
      k = i+n+sz;
      while( j<k ){
        if( sqlite3Isdigit(z[j]) ){
          j++;
        }else{
          return j+1;
        }
      }
      return 0;
    }
    case JSONB_INT5: {
      if( sz<3 ) return i+1;
      j = i+n;
      if( z[j]=='-' ){
        if( sz<4 ) return i+1;
        j++;
      }
      if( z[j]!='0' ) return i+1;
      if( z[j+1]!='x' && z[j+1]!='X' ) return j+2;
      j += 2;
      k = i+n+sz;
      while( j<k ){
        if( sqlite3Isxdigit(z[j]) ){
          j++;
        }else{
          return j+1;
        }
      }
      return 0;
    }
    case JSONB_FLOAT:
    case JSONB_FLOAT5: {
      u8 seen = 0;   /* 0: initial.  1: '.' seen  2: 'e' seen */
      if( sz<2 ) return i+1;
      j = i+n;
      k = j+sz;
      if( z[j]=='-' ){
        j++;
        if( sz<3 ) return i+1;
      }
      if( z[j]=='.' ){
        if( x==JSONB_FLOAT ) return j+1;
        if( !sqlite3Isdigit(z[j+1]) ) return j+1;
        j += 2;
        seen = 1;
      }else if( z[j]=='0' && x==JSONB_FLOAT ){
        if( j+3>k ) return j+1;
        if( z[j+1]!='.' && z[j+1]!='e' && z[j+1]!='E' ) return j+1;
        j++;
      }
      for(; j<k; j++){
        if( sqlite3Isdigit(z[j]) ) continue;
        if( z[j]=='.' ){
          if( seen>0 ) return j+1;
          if( x==JSONB_FLOAT && (j==k-1 || !sqlite3Isdigit(z[j+1])) ){
            return j+1;
          }
          seen = 1;
          continue;
        }
        if( z[j]=='e' || z[j]=='E' ){
          if( seen==2 ) return j+1;
          if( j==k-1 ) return j+1;
          if( z[j+1]=='+' || z[j+1]=='-' ){
            j++;
            if( j==k-1 ) return j+1;
          }
          seen = 2;
          continue;
        }
        return j+1;
      }
      if( seen==0 ) return i+1;
      return 0;
    }
    case JSONB_TEXT: {
      j = i+n;
      k = j+sz;
      while( j<k ){
        if( !jsonIsOk[z[j]] && z[j]!='\'' ) return j+1;
        j++;
      }
      return 0;
    }
    case JSONB_TEXTJ:
    case JSONB_TEXT5: {
      j = i+n;
      k = j+sz;
      while( j<k ){
        if( !jsonIsOk[z[j]] && z[j]!='\'' ){
          if( z[j]=='"' ){
            if( x==JSONB_TEXTJ ) return j+1;
          }else if( z[j]!='\\' || j+1>=k ){
            return j+1;
          }else if( strchr("\"\\/bfnrt",z[j+1])!=0 ){
            j++;
          }else if( z[j+1]=='u' ){
            if( j+5>=k ) return j+1;
            if( !jsonIs4Hex((const char*)&z[j+2]) ) return j+1;
            j++;
          }else if( x!=JSONB_TEXT5 ){
            return j+1;
          }else{
            u32 c = 0;
            u32 szC = jsonUnescapeOneChar((const char*)&z[j], k-j, &c);
            if( c==JSON_INVALID_CHAR ) return j+1;
            j += szC - 1;
          }
        }
        j++;
      }
      return 0;
    }
    case JSONB_TEXTRAW: {
      return 0;
    }
    case JSONB_ARRAY: {
      u32 sub;
      j = i+n;
      k = j+sz;
      while( j<k ){
        sz = 0;
        n = jsonbPayloadSize(pParse, j, &sz);
        if( n==0 ) return j+1;
        if( j+n+sz>k ) return j+1;
        sub = jsonbValidityCheck(pParse, j, j+n+sz, iDepth+1);
        if( sub ) return sub;
        j += n + sz;
      }
      assert( j==k );
      return 0;
    }
    case JSONB_OBJECT: {
      u32 cnt = 0;
      u32 sub;
      j = i+n;
      k = j+sz;
      while( j<k ){
        sz = 0;
        n = jsonbPayloadSize(pParse, j, &sz);
        if( n==0 ) return j+1;
        if( j+n+sz>k ) return j+1;
        if( (cnt & 1)==0 ){
          x = z[j] & 0x0f;
          if( x<JSONB_TEXT || x>JSONB_TEXTRAW ) return j+1;
        }
        sub = jsonbValidityCheck(pParse, j, j+n+sz, iDepth+1);
        if( sub ) return sub;
        cnt++;
        j += n + sz;
      }
      assert( j==k );
      if( (cnt & 1)!=0 ) return j+1;
      return 0;
    }
    default: {
      return i+1;
    }
  }
}

/*
** Translate a single element of JSON text at pParse->zJson[i] into

** its equivalent binary JSONB representation.  Append the translation into
** pParse->aBlob[] beginning at pParse->nBlob.  The size of
** pParse->aBlob[] is increased as necessary.
**
** Return the index of the first character past the end of the element parsed,
** or one of the following special result codes:
**
**      0    End of input
**     -1    Syntax error or OOM
**     -2    '}' seen   \
**     -3    ']' seen    \___  For these returns, pParse->iErr is set to
**     -4    ',' seen    /     the index in zJson[] of the seen character
**     -5    ':' seen   /
*/
static int jsonTranslateTextToBlob(JsonParse *pParse, u32 i){
  char c;
  u32 j;
  u32 iThis, iStart;
  int x;
  u8 t;
  const char *z = pParse->zJson;
json_parse_restart:
  switch( (u8)z[i] ){
  case '{': {
    /* Parse object */
    iThis = pParse->nBlob;
    jsonBlobAppendNode(pParse, JSONB_OBJECT, pParse->nJson-i, 0);

    if( ++pParse->iDepth > JSON_MAX_DEPTH ){
      pParse->iErr = i;
      return -1;
    }
    iStart = pParse->nBlob;
    for(j=i+1;;j++){
      u32 iBlob = pParse->nBlob;
      x = jsonTranslateTextToBlob(pParse, j);
      if( x<=0 ){
        int op;
        if( x==(-2) ){
          j = pParse->iErr;
          if( pParse->nBlob!=(u32)iStart ) pParse->hasNonstd = 1;
          break;
        }
        j += json5Whitespace(&z[j]);
        op = JSONB_TEXT;
        if( sqlite3JsonId1(z[j])
         || (z[j]=='\\' && jsonIs4HexB(&z[j+1], &op))
        ){
          int k = j+1;
          while( (sqlite3JsonId2(z[k]) && json5Whitespace(&z[k])==0)
            || (z[k]=='\\' && jsonIs4HexB(&z[k+1], &op))
          ){
            k++;
          }
          assert( iBlob==pParse->nBlob );
          jsonBlobAppendNode(pParse, op, k-j, &z[j]);
          pParse->hasNonstd = 1;
          x = k;
        }else{
          if( x!=-1 ) pParse->iErr = j;
          return -1;
        }
      }
      if( pParse->oom ) return -1;
      t = pParse->aBlob[iBlob] & 0x0f;
      if( t<JSONB_TEXT || t>JSONB_TEXTRAW ){
        pParse->iErr = j;
        return -1;
      }

      j = x;
      if( z[j]==':' ){
        j++;
      }else{
        if( jsonIsspace(z[j]) ){
          /* strspn() is not helpful here */
          do{ j++; }while( jsonIsspace(z[j]) );
          if( z[j]==':' ){
            j++;
            goto parse_object_value;
          }
        }
        x = jsonTranslateTextToBlob(pParse, j);
        if( x!=(-5) ){
          if( x!=(-1) ) pParse->iErr = j;
          return -1;
        }
        j = pParse->iErr+1;
      }
    parse_object_value:
      x = jsonTranslateTextToBlob(pParse, j);
      if( x<=0 ){
        if( x!=(-1) ) pParse->iErr = j;
        return -1;
      }
      j = x;
      if( z[j]==',' ){
        continue;
      }else if( z[j]=='}' ){
        break;
      }else{
        if( jsonIsspace(z[j]) ){
          j += 1 + (u32)strspn(&z[j+1], jsonSpaces);
          if( z[j]==',' ){
            continue;
          }else if( z[j]=='}' ){
            break;
          }
        }
        x = jsonTranslateTextToBlob(pParse, j);
        if( x==(-4) ){
          j = pParse->iErr;
          continue;
        }
        if( x==(-2) ){
          j = pParse->iErr;
          break;
        }
      }
      pParse->iErr = j;
      return -1;
    }
    jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart);
    pParse->iDepth--;
    return j+1;
  }
  case '[': {
    /* Parse array */
    iThis = pParse->nBlob;
    jsonBlobAppendNode(pParse, JSONB_ARRAY, pParse->nJson - i, 0);
    iStart = pParse->nBlob;
    if( pParse->oom ) return -1;
    if( ++pParse->iDepth > JSON_MAX_DEPTH ){
      pParse->iErr = i;
      return -1;
    }

    for(j=i+1;;j++){
      x = jsonTranslateTextToBlob(pParse, j);
      if( x<=0 ){
        if( x==(-3) ){
          j = pParse->iErr;
          if( pParse->nBlob!=iStart ) pParse->hasNonstd = 1;
          break;
        }
        if( x!=(-1) ) pParse->iErr = j;
        return -1;
      }
      j = x;
      if( z[j]==',' ){
        continue;
      }else if( z[j]==']' ){
        break;
      }else{
        if( jsonIsspace(z[j]) ){
          j += 1 + (u32)strspn(&z[j+1], jsonSpaces);
          if( z[j]==',' ){
            continue;
          }else if( z[j]==']' ){
            break;
          }
        }
        x = jsonTranslateTextToBlob(pParse, j);
        if( x==(-4) ){
          j = pParse->iErr;
          continue;
        }
        if( x==(-3) ){
          j = pParse->iErr;
          break;
        }
      }
      pParse->iErr = j;
      return -1;
    }
    jsonBlobChangePayloadSize(pParse, iThis, pParse->nBlob - iStart);
    pParse->iDepth--;
    return j+1;
  }
  case '\'': {
    u8 opcode;
    char cDelim;
    pParse->hasNonstd = 1;
    opcode = JSONB_TEXT;
    goto parse_string;
  case '"':
    /* Parse string */
    opcode = JSONB_TEXT;
  parse_string:
    cDelim = z[i];
    j = i+1;
    while( 1 /*exit-by-break*/ ){
      if( jsonIsOk[(u8)z[j]] ){
        if( !jsonIsOk[(u8)z[j+1]] ){
          j += 1;
        }else if( !jsonIsOk[(u8)z[j+2]] ){
          j += 2;
        }else{
          j += 3;
          continue;
        }
      }
      c = z[j];
      if( c==cDelim ){
        break;
      }else if( c=='\\' ){
        c = z[++j];
        if( c=='"' || c=='\\' || c=='/' || c=='b' || c=='f'
           || c=='n' || c=='r' || c=='t'
           || (c=='u' && jsonIs4Hex(&z[j+1])) ){
          if( opcode==JSONB_TEXT ) opcode = JSONB_TEXTJ;
        }else if( c=='\'' || c=='0' || c=='v' || c=='\n'
           || (0xe2==(u8)c && 0x80==(u8)z[j+1]
                && (0xa8==(u8)z[j+2] || 0xa9==(u8)z[j+2]))
           || (c=='x' && jsonIs2Hex(&z[j+1])) ){
          opcode = JSONB_TEXT5;
          pParse->hasNonstd = 1;
        }else if( c=='\r' ){
          if( z[j+1]=='\n' ) j++;
          opcode = JSONB_TEXT5;
          pParse->hasNonstd = 1;
        }else{
          pParse->iErr = j;
          return -1;
        }
      }else if( c<=0x1f ){
        /* Control characters are not allowed in strings */
        pParse->iErr = j;
        return -1;
      }else if( c=='"' ){
        opcode = JSONB_TEXT5;
      }
      j++;
    }
    jsonBlobAppendNode(pParse, opcode, j-1-i, &z[i+1]);
    return j+1;
  }
  case 't': {
    if( strncmp(z+i,"true",4)==0 && !sqlite3Isalnum(z[i+4]) ){
      jsonBlobAppendOneByte(pParse, JSONB_TRUE);
      return i+4;
    }
    pParse->iErr = i;
    return -1;
  }
  case 'f': {
    if( strncmp(z+i,"false",5)==0 && !sqlite3Isalnum(z[i+5]) ){
      jsonBlobAppendOneByte(pParse, JSONB_FALSE);
      return i+5;
    }
    pParse->iErr = i;
    return -1;
  }
  case '+': {
    u8 seenE;
    pParse->hasNonstd = 1;
    t = 0x00;            /* Bit 0x01:  JSON5.   Bit 0x02:  FLOAT */
    goto parse_number;
  case '.':
    if( sqlite3Isdigit(z[i+1]) ){
      pParse->hasNonstd = 1;
      t = 0x03;          /* Bit 0x01:  JSON5.   Bit 0x02:  FLOAT */
      seenE = 0;

      goto parse_number_2;
    }
    pParse->iErr = i;
    return -1;
  case '-':
  case '0':
  case '1':
  case '2':
  case '3':
  case '4':
  case '5':
  case '6':
  case '7':
  case '8':
  case '9':
    /* Parse number */
    t = 0x00;            /* Bit 0x01:  JSON5.   Bit 0x02:  FLOAT */
  parse_number:

    seenE = 0;
    assert( '-' < '0' );
    assert( '+' < '0' );
    assert( '.' < '0' );
    c = z[i];

    if( c<='0' ){
      if( c=='0' ){
        if( (z[i+1]=='x' || z[i+1]=='X') && sqlite3Isxdigit(z[i+2]) ){
          assert( t==0x00 );
          pParse->hasNonstd = 1;
          t = 0x01;
          for(j=i+3; sqlite3Isxdigit(z[j]); j++){}
          goto parse_number_finish;
        }else if( sqlite3Isdigit(z[i+1]) ){
          pParse->iErr = i+1;
          return -1;
        }
      }else{
        if( !sqlite3Isdigit(z[i+1]) ){
          /* JSON5 allows for "+Infinity" and "-Infinity" using exactly
          ** that case.  SQLite also allows these in any case and it allows
          ** "+inf" and "-inf". */
          if( (z[i+1]=='I' || z[i+1]=='i')
           && sqlite3StrNICmp(&z[i+1], "inf",3)==0
          ){
            pParse->hasNonstd = 1;
            if( z[i]=='-' ){
              jsonBlobAppendNode(pParse, JSONB_FLOAT, 6, "-9e999");
            }else{
              jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999");
            }
            return i + (sqlite3StrNICmp(&z[i+4],"inity",5)==0 ? 9 : 4);
          }
          if( z[i+1]=='.' ){
            pParse->hasNonstd = 1;
            t |= 0x01;
            goto parse_number_2;
          }
          pParse->iErr = i;
          return -1;
        }
        if( z[i+1]=='0' ){
          if( sqlite3Isdigit(z[i+2]) ){
            pParse->iErr = i+1;
            return -1;
          }else if( (z[i+2]=='x' || z[i+2]=='X') && sqlite3Isxdigit(z[i+3]) ){
            pParse->hasNonstd = 1;
            t |= 0x01;
            for(j=i+4; sqlite3Isxdigit(z[j]); j++){}
            goto parse_number_finish;
          }
        }
      }
    }

  parse_number_2:
    for(j=i+1;; j++){
      c = z[j];
      if( sqlite3Isdigit(c) ) continue;
      if( c=='.' ){
        if( (t & 0x02)!=0 ){
          pParse->iErr = j;
          return -1;
        }
        t |= 0x02;
        continue;
      }
      if( c=='e' || c=='E' ){
        if( z[j-1]<'0' ){
          if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){
            pParse->hasNonstd = 1;
            t |= 0x01;
          }else{
            pParse->iErr = j;
            return -1;
          }
        }
        if( seenE ){
          pParse->iErr = j;
          return -1;
        }
        t |= 0x02;
        seenE = 1;
        c = z[j+1];
        if( c=='+' || c=='-' ){
          j++;
          c = z[j+1];
        }
        if( c<'0' || c>'9' ){
          pParse->iErr = j;
          return -1;
        }
        continue;
      }
      break;
    }
    if( z[j-1]<'0' ){
      if( ALWAYS(z[j-1]=='.') && ALWAYS(j-2>=i) && sqlite3Isdigit(z[j-2]) ){
        pParse->hasNonstd = 1;
        t |= 0x01;
      }else{
        pParse->iErr = j;
        return -1;
      }
    }
  parse_number_finish:
    assert( JSONB_INT+0x01==JSONB_INT5 );
    assert( JSONB_FLOAT+0x01==JSONB_FLOAT5 );
    assert( JSONB_INT+0x02==JSONB_FLOAT );
    if( z[i]=='+' ) i++;
    jsonBlobAppendNode(pParse, JSONB_INT+t, j-i, &z[i]);
    return j;
  }
  case '}': {
    pParse->iErr = i;
    return -2;  /* End of {...} */
  }
  case ']': {
204355
204356
204357
204358
204359
204360
204361
204362
204363
204364
204365
204366
204367
204368
204369
204370
204371
  case 0: {
    return 0;   /* End of file */
  }
  case 0x09:
  case 0x0a:
  case 0x0d:
  case 0x20: {
    do{
      i++;
    }while( fast_isspace(z[i]) );
    goto json_parse_restart;
  }
  case 0x0b:
  case 0x0c:
  case '/':
  case 0xc2:
  case 0xe1:







|
<
<







205069
205070
205071
205072
205073
205074
205075
205076


205077
205078
205079
205080
205081
205082
205083
  case 0: {
    return 0;   /* End of file */
  }
  case 0x09:
  case 0x0a:
  case 0x0d:
  case 0x20: {
    i += 1 + (u32)strspn(&z[i+1], jsonSpaces);


    goto json_parse_restart;
  }
  case 0x0b:
  case 0x0c:
  case '/':
  case 0xc2:
  case 0xe1:
204379
204380
204381
204382
204383
204384
204385
204386
204387
204388
204389
204390
204391
204392
204393
204394
204395
204396
204397
204398
204399
204400
204401
204402
204403



204404
204405
204406
204407
204408
204409
204410
204411

204412
204413
204414
204415
204416
204417
204418
204419
204420
204421
204422
204423
204424
204425
204426
204427
204428
204429

204430




204431
204432
204433
204434

204435
204436
204437
204438
204439
204440
204441
204442
204443
204444
204445
204446
204447
204448
204449
204450
204451
204452
204453
204454




















204455

204456
204457




204458
204459
204460

204461





204462
204463


204464
204465
204466


204467





204468
204469


204470
204471








204472
204473





204474
204475



204476



204477



204478
204479
204480
204481

204482



204483



204484


204485

204486



204487
204488
204489
204490

204491




204492
204493

204494
204495
204496





















204497
204498

204499
204500











204501
204502






204503
204504
204505
204506
204507
204508
204509
204510





204511
204512
204513



204514
204515
204516

204517

204518
204519
204520
204521
204522

204523
204524
204525
204526
204527
204528

204529
204530
204531

































204532
204533
204534
204535















204536





204537
204538

204539
204540

204541

204542





204543
204544
204545

204546





204547
204548
204549
204550



204551











204552








204553
204554
204555

204556
204557
204558
204559
204560





204561







204562


204563



204564





204565




204566




204567
204568
204569
204570
204571
204572
204573

204574
204575
204576










204577

204578





204579
204580





















204581
204582
204583
204584
204585
204586
204587
204588





204589
204590
204591
204592
204593
204594
204595
204596
204597

204598











204599


204600
204601




204602
204603
204604

204605
204606
204607
204608



204609
204610
204611
204612
204613
204614
204615
204616
204617
204618
204619
204620
204621
204622
204623
204624
204625


204626
204627




204628
204629
204630

204631

204632






204633


























204634
204635

204636
204637


204638



204639
204640

204641
204642

204643


204644






204645
204646
204647
































204648
204649
204650


























204651


204652
204653
204654
204655
204656
204657
204658
204659
204660
204661
204662
204663

204664
204665
204666

204667










204668


































204669
204670




204671


204672
204673
204674
204675




204676
204677
204678
204679

204680



204681


























204682




204683


204684
204685
204686

204687




204688
204689
204690

204691
204692
204693
204694
204695

204696
204697
204698
204699
204700
204701
204702
204703
204704
204705
204706
204707
204708

204709
204710
204711
204712
204713
204714
204715
204716
204717



204718
204719
204720










204721





204722


204723
204724

204725
204726
204727
204728
204729
204730
204731
204732
204733

204734
204735

204736
204737
204738

204739

204740
204741



204742


204743
204744
204745

204746
204747

204748
204749

204750



204751
204752
204753



204754
204755
204756
204757
204758
204759
204760
204761

204762

















204763





204764

204765

204766
204767
204768
204769
204770

204771







204772











204773









204774
204775
204776
204777
204778
204779
204780
204781



204782
204783











204784
204785


204786

204787

204788
204789

204790






204791
204792
204793

204794
204795




204796
204797



204798







204799
204800
204801

204802



204803
204804
204805








204806
204807
204808

204809
204810
204811



204812

204813




204814
204815
204816





204817












204818



204819
204820
204821
204822
204823
204824




204825

204826
204827









204828








204829
204830



204831






204832
204833


204834
204835
204836


204837
204838
































204839
204840






204841






204842
204843


204844



204845
204846
204847
204848
204849
204850








204851
204852
204853
204854



204855
204856
204857
204858
204859








204860


204861

204862

204863


204864



















204865


204866



204867
204868


204869
204870
204871










204872











204873
204874
204875
204876
204877
204878
204879
204880
204881
204882
204883
204884
204885


204886
204887
204888
204889

204890




204891









204892






204893
204894

204895










204896



204897

204898

204899


204900



204901








204902
204903
204904

204905
204906
204907


204908







204909






204910

204911


204912




204913
204914
204915
















204916









204917






204918
204919

204920
204921
204922
204923
204924
204925
204926
204927
204928
204929











204930
204931
204932
204933
204934


204935












204936

204937




204938
204939

204940
204941

204942
204943
204944

204945

204946





204947
204948













204949

















204950
204951
204952
204953
204954
204955
204956

204957

204958
204959
204960
204961





204962


204963











204964
204965
204966
204967
204968
204969

204970
204971
204972

204973
204974
204975
204976
204977
204978
204979
204980





204981
204982
204983
204984
204985
204986
204987
204988
204989
204990
204991

















204992





204993
204994
204995
204996




204997
204998
204999
205000



205001



















205002

205003





205004



205005
205006
205007
205008
205009
205010
205011
205012
205013
205014
205015
205016
205017
205018
205019
205020

205021
205022

205023
205024
205025
205026
205027
205028
205029
205030
205031
205032
205033
205034
205035
205036
205037
205038

205039

205040
205041
205042
205043
205044
205045
205046
205047
205048
205049
205050
205051
205052
205053
205054
205055
205056
205057
205058
205059
205060
205061
205062
205063
205064
205065
205066
205067
205068
205069
205070
205071
205072
205073
205074
205075
205076
205077
205078
205079
205080
205081
205082
205083
205084
205085
205086
205087
205088
205089
205090
205091
205092
205093
205094
205095
205096
205097
205098
205099
205100
205101
205102
205103
205104
205105
205106
205107
205108
205109
205110
205111
205112
205113
205114
205115
205116
205117
205118
205119
205120
205121
205122
205123
205124
205125
205126
205127
205128
205129
205130

205131




205132

205133
205134
205135
205136
205137
205138
205139
205140
205141
205142
205143
205144
205145


205146
205147

205148
205149
205150
205151
205152
205153
205154
205155
205156


205157






205158
205159
205160
205161
205162
205163
205164
      goto json_parse_restart;
    }
    pParse->iErr = i;
    return -1;
  }
  case 'n': {
    if( strncmp(z+i,"null",4)==0 && !sqlite3Isalnum(z[i+4]) ){
      jsonParseAddNode(pParse, JSON_NULL, 0, 0);
      return i+4;
    }
    /* fall-through into the default case that checks for NaN */
  }
  default: {
    u32 k;
    int nn;
    c = z[i];
    for(k=0; k<sizeof(aNanInfName)/sizeof(aNanInfName[0]); k++){
      if( c!=aNanInfName[k].c1 && c!=aNanInfName[k].c2 ) continue;
      nn = aNanInfName[k].n;
      if( sqlite3StrNICmp(&z[i], aNanInfName[k].zMatch, nn)!=0 ){
        continue;
      }
      if( sqlite3Isalnum(z[i+nn]) ) continue;
      jsonParseAddNode(pParse, aNanInfName[k].eType,
          aNanInfName[k].nRepl, aNanInfName[k].zRepl);



      pParse->hasNonstd = 1;
      return i + nn;
    }
    pParse->iErr = i;
    return -1;  /* Syntax error */
  }
  } /* End switch(z[i]) */
}


/*
** Parse a complete JSON string.  Return 0 on success or non-zero if there
** are any errors.  If an error occurs, free all memory held by pParse,
** but not pParse itself.
**
** pParse must be initialized to an empty parse object prior to calling
** this routine.
*/
static int jsonParse(
  JsonParse *pParse,           /* Initialize and fill this JsonParse object */
  sqlite3_context *pCtx        /* Report errors here */
){
  int i;
  const char *zJson = pParse->zJson;
  i = jsonParseValue(pParse, 0);
  if( pParse->oom ) i = -1;
  if( i>0 ){

    assert( pParse->iDepth==0 );




    while( fast_isspace(zJson[i]) ) i++;
    if( zJson[i] ){
      i += json5Whitespace(&zJson[i]);
      if( zJson[i] ){

        jsonParseReset(pParse);
        return 1;
      }
      pParse->hasNonstd = 1;
    }
  }
  if( i<=0 ){
    if( pCtx!=0 ){
      if( pParse->oom ){
        sqlite3_result_error_nomem(pCtx);
      }else{
        sqlite3_result_error(pCtx, "malformed JSON", -1);
      }
    }
    jsonParseReset(pParse);
    return 1;
  }
  return 0;
}























/* Mark node i of pParse as being a child of iParent.  Call recursively
** to fill in all the descendants of node i.




*/
static void jsonParseFillInParentage(JsonParse *pParse, u32 i, u32 iParent){
  JsonNode *pNode = &pParse->aNode[i];

  u32 j;





  pParse->aUp[i] = iParent;
  switch( pNode->eType ){


    case JSON_ARRAY: {
      for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j)){
        jsonParseFillInParentage(pParse, i+j, i);


      }





      break;
    }


    case JSON_OBJECT: {
      for(j=1; j<=pNode->n; j += jsonNodeSize(pNode+j+1)+1){








        pParse->aUp[i+j] = i;
        jsonParseFillInParentage(pParse, i+j+1, i);





      }
      break;



    }



    default: {



      break;
    }
  }
}





/*



** Compute the parentage of all nodes in a completed parse.


*/

static int jsonParseFindParents(JsonParse *pParse){



  u32 *aUp;
  assert( pParse->aUp==0 );
  aUp = pParse->aUp = sqlite3_malloc64( sizeof(u32)*pParse->nNode );
  if( aUp==0 ){

    pParse->oom = 1;




    return SQLITE_NOMEM;
  }

  jsonParseFillInParentage(pParse, 0, 0);
  return SQLITE_OK;
}






















/*

** Magic number used for the JSON parse cache in sqlite3_get_auxdata()
*/











#define JSON_CACHE_ID  (-429938)  /* First cache entry */
#define JSON_CACHE_SZ  4          /* Max number of cache entries */







/*
** Obtain a complete parse of the JSON found in the pJson argument
**
** Use the sqlite3_get_auxdata() cache to find a preexisting parse
** if it is available.  If the cache is not available or if it
** is no longer valid, parse the JSON again and return the new parse.
** Also register the new parse so that it will be available for





** future sqlite3_get_auxdata() calls.
**
** If an error occurs and pErrCtx!=0 then report the error on pErrCtx



** and return NULL.
**
** The returned pointer (if it is not NULL) is owned by the cache in

** most cases, not the caller.  The caller does NOT need to invoke

** jsonParseFree(), in most cases.
**
** Except, if an error occurs and pErrCtx==0 then return the JsonParse
** object with JsonParse.nErr non-zero and the caller will own the JsonParse
** object.  In that case, it will be the responsibility of the caller to

** invoke jsonParseFree().  To summarize:
**
**   pErrCtx!=0 || p->nErr==0      ==>   Return value p is owned by the
**                                       cache.  Call does not need to
**                                       free it.
**

**   pErrCtx==0 && p->nErr!=0      ==>   Return value is owned by the caller
**                                       and so the caller must free it.
*/

































static JsonParse *jsonParseCached(
  sqlite3_context *pCtx,         /* Context to use for cache search */
  sqlite3_value *pJson,          /* Function param containing JSON text */
  sqlite3_context *pErrCtx,      /* Write parse errors here if not NULL */















  int bUnedited                  /* No prior edits allowed */





){
  char *zJson = (char*)sqlite3_value_text(pJson);

  int nJson = sqlite3_value_bytes(pJson);
  JsonParse *p;

  JsonParse *pMatch = 0;

  int iKey;





  int iMinKey = 0;
  u32 iMinHold = 0xffffffff;
  u32 iMaxHold = 0;

  int bJsonRCStr;






  if( zJson==0 ) return 0;
  for(iKey=0; iKey<JSON_CACHE_SZ; iKey++){
    p = (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iKey);



    if( p==0 ){











      iMinKey = iKey;








      break;
    }
    if( pMatch==0

     && p->nJson==nJson
     && (p->hasMod==0 || bUnedited==0)
     && (p->zJson==zJson || memcmp(p->zJson,zJson,nJson)==0)
    ){
      p->nErr = 0;





      p->useMod = 0;







      pMatch = p;


    }else



    if( pMatch==0





     && p->zAlt!=0




     && bUnedited==0




     && p->nAlt==nJson
     && memcmp(p->zAlt, zJson, nJson)==0
    ){
      p->nErr = 0;
      p->useMod = 1;
      pMatch = p;
    }else if( p->iHold<iMinHold ){

      iMinHold = p->iHold;
      iMinKey = iKey;
    }










    if( p->iHold>iMaxHold ){

      iMaxHold = p->iHold;





    }
  }





















  if( pMatch ){
    /* The input JSON text was found in the cache.  Use the preexisting
    ** parse of this JSON */
    pMatch->nErr = 0;
    pMatch->iHold = iMaxHold+1;
    assert( pMatch->nJPRef>0 ); /* pMatch is owned by the cache */
    return pMatch;
  }






  /* The input JSON was not found anywhere in the cache.  We will need
  ** to parse it ourselves and generate a new JsonParse object.
  */
  bJsonRCStr = sqlite3ValueIsOfClass(pJson,sqlite3RCStrUnref);
  p = sqlite3_malloc64( sizeof(*p) + (bJsonRCStr ? 0 : nJson+1) );
  if( p==0 ){
    sqlite3_result_error_nomem(pCtx);
    return 0;

  }











  memset(p, 0, sizeof(*p));


  if( bJsonRCStr ){
    p->zJson = sqlite3RCStrRef(zJson);




    p->bJsonIsRCStr = 1;
  }else{
    p->zJson = (char*)&p[1];

    memcpy(p->zJson, zJson, nJson+1);
  }
  p->nJPRef = 1;
  if( jsonParse(p, pErrCtx) ){



    if( pErrCtx==0 ){
      p->nErr = 1;
      assert( p->nJPRef==1 ); /* Caller will own the new JsonParse object p */
      return p;
    }
    jsonParseFree(p);
    return 0;
  }
  p->nJson = nJson;
  p->iHold = iMaxHold+1;
  /* Transfer ownership of the new JsonParse to the cache */
  sqlite3_set_auxdata(pCtx, JSON_CACHE_ID+iMinKey, p,
                      (void(*)(void*))jsonParseFree);
  return (JsonParse*)sqlite3_get_auxdata(pCtx, JSON_CACHE_ID+iMinKey);
}

/*


** Compare the OBJECT label at pNode against zKey,nKey.  Return true on
** a match.




*/
static int jsonLabelCompare(const JsonNode *pNode, const char *zKey, u32 nKey){
  assert( pNode->eU==1 );

  if( pNode->jnFlags & JNODE_RAW ){

    if( pNode->n!=nKey ) return 0;






    return strncmp(pNode->u.zJContent, zKey, nKey)==0;


























  }else{
    if( pNode->n!=nKey+2 ) return 0;

    return strncmp(pNode->u.zJContent+1, zKey, nKey)==0;
  }


}



static int jsonSameLabel(const JsonNode *p1, const JsonNode *p2){
  if( p1->jnFlags & JNODE_RAW ){

    return jsonLabelCompare(p2, p1->u.zJContent, p1->n);
  }else if( p2->jnFlags & JNODE_RAW ){

    return jsonLabelCompare(p1, p2->u.zJContent, p2->n);


  }else{






    return p1->n==p2->n && strncmp(p1->u.zJContent,p2->u.zJContent,p1->n)==0;
  }
}

































/* forward declaration */
static JsonNode *jsonLookupAppend(JsonParse*,const char*,int*,const char**);





























/*
** Search along zPath to find the node specified.  Return a pointer
** to that node, or NULL if zPath is malformed or if there is no such
** node.
**
** If pApnd!=0, then try to append new nodes to complete zPath if it is
** possible to do so and if no existing node corresponds to zPath.  If
** new nodes are appended *pApnd is set to 1.
*/
static JsonNode *jsonLookupStep(
  JsonParse *pParse,      /* The JSON to search */
  u32 iRoot,              /* Begin the search at this node */

  const char *zPath,      /* The path to search */
  int *pApnd,             /* Append nodes to complete path if not NULL */
  const char **pzErr      /* Make *pzErr point to any syntax error in zPath */

){










  u32 i, j, nKey;


































  const char *zKey;
  JsonNode *pRoot;




  if( pParse->oom ) return 0;


  pRoot = &pParse->aNode[iRoot];
  if( pRoot->jnFlags & (JNODE_REPLACE|JNODE_REMOVE) && pParse->useMod ){
    while( (pRoot->jnFlags & JNODE_REPLACE)!=0 ){
      u32 idx = (u32)(pRoot - pParse->aNode);




      i = pParse->iSubst;
      while( 1 /*exit-by-break*/ ){
        assert( i<pParse->nNode );
        assert( pParse->aNode[i].eType==JSON_SUBST );

        assert( pParse->aNode[i].eU==4 );



        assert( pParse->aNode[i].u.iPrev<i );


























        if( pParse->aNode[i].n==idx ){




          pRoot = &pParse->aNode[i+1];


          iRoot = i+1;
          break;
        }

        i = pParse->aNode[i].u.iPrev;




      }
    }
    if( pRoot->jnFlags & JNODE_REMOVE ){

      return 0;
    }
  }
  if( zPath[0]==0 ) return pRoot;
  if( zPath[0]=='.' ){

    if( pRoot->eType!=JSON_OBJECT ) return 0;
    zPath++;
    if( zPath[0]=='"' ){
      zKey = zPath + 1;
      for(i=1; zPath[i] && zPath[i]!='"'; i++){}
      nKey = i-1;
      if( zPath[i] ){
        i++;
      }else{
        *pzErr = zPath;
        return 0;
      }
      testcase( nKey==0 );

    }else{
      zKey = zPath;
      for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){}
      nKey = i;
      if( nKey==0 ){
        *pzErr = zPath;
        return 0;
      }
    }



    j = 1;
    for(;;){
      while( j<=pRoot->n ){










        if( jsonLabelCompare(pRoot+j, zKey, nKey) ){





          return jsonLookupStep(pParse, iRoot+j+1, &zPath[i], pApnd, pzErr);


        }
        j++;

        j += jsonNodeSize(&pRoot[j]);
      }
      if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;
      if( pParse->useMod==0 ) break;
      assert( pRoot->eU==2 );
      iRoot = pRoot->u.iAppend;
      pRoot = &pParse->aNode[iRoot];
      j = 1;
    }

    if( pApnd ){
      u32 iStart, iLabel;

      JsonNode *pNode;
      assert( pParse->useMod );
      iStart = jsonParseAddNode(pParse, JSON_OBJECT, 2, 0);

      iLabel = jsonParseAddNode(pParse, JSON_STRING, nKey, zKey);

      zPath += i;
      pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);



      if( pParse->oom ) return 0;


      if( pNode ){
        pRoot = &pParse->aNode[iRoot];
        assert( pRoot->eU==0 );

        pRoot->u.iAppend = iStart;
        pRoot->jnFlags |= JNODE_APPEND;

        VVA( pRoot->eU = 2 );
        pParse->aNode[iLabel].jnFlags |= JNODE_RAW;

      }



      return pNode;
    }
  }else if( zPath[0]=='[' ){



    i = 0;
    j = 1;
    while( sqlite3Isdigit(zPath[j]) ){
      i = i*10 + zPath[j] - '0';
      j++;
    }
    if( j<2 || zPath[j]!=']' ){
      if( zPath[1]=='#' ){

        JsonNode *pBase = pRoot;

















        int iBase = iRoot;





        if( pRoot->eType!=JSON_ARRAY ) return 0;

        for(;;){

          while( j<=pBase->n ){
            if( (pBase[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i++;
            j += jsonNodeSize(&pBase[j]);
          }
          if( (pBase->jnFlags & JNODE_APPEND)==0 ) break;

          if( pParse->useMod==0 ) break;







          assert( pBase->eU==2 );











          iBase = pBase->u.iAppend;









          pBase = &pParse->aNode[iBase];
          j = 1;
        }
        j = 2;
        if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){
          unsigned int x = 0;
          j = 3;
          do{



            x = x*10 + zPath[j] - '0';
            j++;











          }while( sqlite3Isdigit(zPath[j]) );
          if( x>i ) return 0;


          i -= x;

        }

        if( zPath[j]!=']' ){
          *pzErr = zPath;

          return 0;






        }
      }else{
        *pzErr = zPath;

        return 0;
      }




    }
    if( pRoot->eType!=JSON_ARRAY ) return 0;



    zPath += j + 1;







    j = 1;
    for(;;){
      while( j<=pRoot->n

         && (i>0 || ((pRoot[j].jnFlags & JNODE_REMOVE)!=0 && pParse->useMod))



      ){
        if( (pRoot[j].jnFlags & JNODE_REMOVE)==0 || pParse->useMod==0 ) i--;
        j += jsonNodeSize(&pRoot[j]);








      }
      if( i==0 && j<=pRoot->n ) break;
      if( (pRoot->jnFlags & JNODE_APPEND)==0 ) break;

      if( pParse->useMod==0 ) break;
      assert( pRoot->eU==2 );
      iRoot = pRoot->u.iAppend;



      pRoot = &pParse->aNode[iRoot];

      j = 1;




    }
    if( j<=pRoot->n ){
      return jsonLookupStep(pParse, iRoot+j, zPath, pApnd, pzErr);





    }












    if( i==0 && pApnd ){



      u32 iStart;
      JsonNode *pNode;
      assert( pParse->useMod );
      iStart = jsonParseAddNode(pParse, JSON_ARRAY, 1, 0);
      pNode = jsonLookupAppend(pParse, zPath, pApnd, pzErr);
      if( pParse->oom ) return 0;




      if( pNode ){

        pRoot = &pParse->aNode[iRoot];
        assert( pRoot->eU==0 );









        pRoot->u.iAppend = iStart;








        pRoot->jnFlags |= JNODE_APPEND;
        VVA( pRoot->eU = 2 );



      }






      return pNode;
    }


  }else{
    *pzErr = zPath;
  }


  return 0;
}

































/*






** Append content to pParse that will complete zPath.  Return a pointer






** to the inserted node, or return NULL if the append fails.
*/


static JsonNode *jsonLookupAppend(



  JsonParse *pParse,     /* Append content to the JSON parse */
  const char *zPath,     /* Description of content to append */
  int *pApnd,            /* Set this flag to 1 */
  const char **pzErr     /* Make this point to any syntax error */
){
  *pApnd = 1;








  if( zPath[0]==0 ){
    jsonParseAddNode(pParse, JSON_NULL, 0, 0);
    return pParse->oom ? 0 : &pParse->aNode[pParse->nNode-1];
  }



  if( zPath[0]=='.' ){
    jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);
  }else if( strncmp(zPath,"[0]",3)==0 ){
    jsonParseAddNode(pParse, JSON_ARRAY, 0, 0);
  }else{








    return 0;


  }

  if( pParse->oom ) return 0;

  return jsonLookupStep(pParse, pParse->nNode-1, zPath, pApnd, pzErr);


}






















/*



** Return the text of a syntax error message on a JSON path.  Space is
** obtained from sqlite3_malloc().


*/
static char *jsonPathSyntaxError(const char *zErr){
  return sqlite3_mprintf("JSON path error near '%q'", zErr);










}












/*
** Do a node lookup using zPath.  Return a pointer to the node on success.
** Return NULL if not found or if there is an error.
**
** On an error, write an error message into pCtx and increment the
** pParse->nErr counter.
**
** If pApnd!=NULL then try to append missing nodes and set *pApnd = 1 if
** nodes are appended.
*/
static JsonNode *jsonLookup(
  JsonParse *pParse,      /* The JSON to search */


  const char *zPath,      /* The path to search */
  int *pApnd,             /* Append nodes to complete path if not NULL */
  sqlite3_context *pCtx   /* Report errors here, if not NULL */
){

  const char *zErr = 0;




  JsonNode *pNode = 0;









  char *zMsg;







  if( zPath==0 ) return 0;

  if( zPath[0]!='$' ){










    zErr = zPath;



    goto lookup_err;

  }

  zPath++;


  pNode = jsonLookupStep(pParse, 0, zPath, pApnd, &zErr);



  if( zErr==0 ) return pNode;









lookup_err:
  pParse->nErr++;

  assert( zErr!=0 && pCtx!=0 );
  zMsg = jsonPathSyntaxError(zErr);
  if( zMsg ){


    sqlite3_result_error(pCtx, zMsg, -1);







    sqlite3_free(zMsg);






  }else{

    sqlite3_result_error_nomem(pCtx);


  }




  return 0;
}



























/*






** Report the wrong number of arguments for json_insert(), json_replace()
** or json_set().

*/
static void jsonWrongNumArgs(
  sqlite3_context *pCtx,
  const char *zFuncName
){
  char *zMsg = sqlite3_mprintf("json_%s() needs an odd number of arguments",
                               zFuncName);
  sqlite3_result_error(pCtx, zMsg, -1);
  sqlite3_free(zMsg);
}












/*
** Mark all NULL entries in the Object passed in as JNODE_REMOVE.
*/
static void jsonRemoveAllNulls(JsonNode *pNode){


  int i, n;












  assert( pNode->eType==JSON_OBJECT );

  n = pNode->n;




  for(i=2; i<=n; i += jsonNodeSize(&pNode[i])+1){
    switch( pNode[i].eType ){

      case JSON_NULL:
        pNode[i].jnFlags |= JNODE_REMOVE;

        break;
      case JSON_OBJECT:
        jsonRemoveAllNulls(&pNode[i]);

        break;

    }





  }
}
































/****************************************************************************
** SQL functions used for testing and debugging
****************************************************************************/

#if SQLITE_DEBUG
/*

** Print N node entries.

*/
static void jsonDebugPrintNodeEntries(
  JsonNode *aNode,  /* First node entry to print */
  int N             /* Number of node entries to print */





){


  int i;











  for(i=0; i<N; i++){
    const char *zType;
    if( aNode[i].jnFlags & JNODE_LABEL ){
      zType = "label";
    }else{
      zType = jsonType[aNode[i].eType];

    }
    printf("node %4u: %-7s n=%-5d", i, zType, aNode[i].n);
    if( (aNode[i].jnFlags & ~JNODE_LABEL)!=0 ){

      u8 f = aNode[i].jnFlags;
      if( f & JNODE_RAW )     printf(" RAW");
      if( f & JNODE_ESCAPE )  printf(" ESCAPE");
      if( f & JNODE_REMOVE )  printf(" REMOVE");
      if( f & JNODE_REPLACE ) printf(" REPLACE");
      if( f & JNODE_APPEND )  printf(" APPEND");
      if( f & JNODE_JSON5 )   printf(" JSON5");
    }





    switch( aNode[i].eU ){
      case 1:  printf(" zJContent=[%.*s]\n",
                      aNode[i].n, aNode[i].u.zJContent);           break;
      case 2:  printf(" iAppend=%u\n", aNode[i].u.iAppend);        break;
      case 3:  printf(" iKey=%u\n", aNode[i].u.iKey);              break;
      case 4:  printf(" iPrev=%u\n", aNode[i].u.iPrev);            break;
      default: printf("\n");
    }
  }
}
#endif /* SQLITE_DEBUG */
























#if 0  /* 1 for debugging.  0 normally.  Requires -DSQLITE_DEBUG too */
static void jsonDebugPrintParse(JsonParse *p){
  jsonDebugPrintNodeEntries(p->aNode, p->nNode);




}
static void jsonDebugPrintNode(JsonNode *pNode){
  jsonDebugPrintNodeEntries(pNode, jsonNodeSize(pNode));
}



#else



















   /* The usual case */

# define jsonDebugPrintNode(X)





# define jsonDebugPrintParse(X)



#endif

#ifdef SQLITE_DEBUG
/*
** SQL function:   json_parse(JSON)
**
** Parse JSON using jsonParseCached().  Then print a dump of that
** parse on standard output.  Return the mimified JSON result, just
** like the json() function.
*/
static void jsonParseFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  JsonParse *p;        /* The parse */


  assert( argc==1 );

  p = jsonParseCached(ctx, argv[0], ctx, 0);
  if( p==0 ) return;
  printf("nNode     = %u\n", p->nNode);
  printf("nAlloc    = %u\n", p->nAlloc);
  printf("nJson     = %d\n", p->nJson);
  printf("nAlt      = %d\n", p->nAlt);
  printf("nErr      = %u\n", p->nErr);
  printf("oom       = %u\n", p->oom);
  printf("hasNonstd = %u\n", p->hasNonstd);
  printf("useMod    = %u\n", p->useMod);
  printf("hasMod    = %u\n", p->hasMod);
  printf("nJPRef    = %u\n", p->nJPRef);
  printf("iSubst    = %u\n", p->iSubst);
  printf("iHold     = %u\n", p->iHold);
  jsonDebugPrintNodeEntries(p->aNode, p->nNode);
  jsonReturnJson(p, p->aNode, ctx, 1, 0);

}


/*
** The json_test1(JSON) function return true (1) if the input is JSON
** text generated by another json function.  It returns (0) if the input
** is not known to be JSON.
*/
static void jsonTest1Func(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  UNUSED_PARAMETER(argc);
  sqlite3_result_int(ctx, sqlite3_value_subtype(argv[0])==JSON_SUBTYPE);
}
#endif /* SQLITE_DEBUG */

/****************************************************************************
** Scalar SQL function implementations
****************************************************************************/

/*
** Implementation of the json_QUOTE(VALUE) function.  Return a JSON value
** corresponding to the SQL value input.  Mostly this means putting
** double-quotes around strings and returning the unquoted string "null"
** when given a NULL input.
*/
static void jsonQuoteFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  JsonString jx;
  UNUSED_PARAMETER(argc);

  jsonInit(&jx, ctx);
  jsonAppendValue(&jx, argv[0]);
  jsonResult(&jx);
  sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}

/*
** Implementation of the json_array(VALUE,...) function.  Return a JSON
** array that contains all values given in arguments.  Or if any argument
** is a BLOB, throw an error.
*/
static void jsonArrayFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  int i;
  JsonString jx;

  jsonInit(&jx, ctx);
  jsonAppendChar(&jx, '[');
  for(i=0; i<argc; i++){
    jsonAppendSeparator(&jx);
    jsonAppendValue(&jx, argv[i]);
  }
  jsonAppendChar(&jx, ']');
  jsonResult(&jx);
  sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}


/*
** json_array_length(JSON)
** json_array_length(JSON, PATH)
**
** Return the number of elements in the top-level JSON array.
** Return 0 if the input is not a well-formed JSON array.
*/
static void jsonArrayLengthFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  JsonParse *p;          /* The parse */
  sqlite3_int64 n = 0;
  u32 i;
  JsonNode *pNode;

  p = jsonParseCached(ctx, argv[0], ctx, 0);
  if( p==0 ) return;
  assert( p->nNode );
  if( argc==2 ){
    const char *zPath = (const char*)sqlite3_value_text(argv[1]);
    pNode = jsonLookup(p, zPath, 0, ctx);
  }else{
    pNode = p->aNode;
  }

  if( pNode==0 ){




    return;

  }
  if( pNode->eType==JSON_ARRAY ){
    while( 1 /*exit-by-break*/ ){
      i = 1;
      while( i<=pNode->n ){
        if( (pNode[i].jnFlags & JNODE_REMOVE)==0 ) n++;
        i += jsonNodeSize(&pNode[i]);
      }
      if( (pNode->jnFlags & JNODE_APPEND)==0 ) break;
      if( p->useMod==0 ) break;
      assert( pNode->eU==2 );
      pNode = &p->aNode[pNode->u.iAppend];
    }


  }
  sqlite3_result_int64(ctx, n);

}

/*
** Bit values for the flags passed into jsonExtractFunc() or
** jsonSetFunc() via the user-data value.
*/
#define JSON_JSON      0x01        /* Result is always JSON */
#define JSON_SQL       0x02        /* Result is always SQL */
#define JSON_ABPATH    0x03        /* Allow abbreviated JSON path specs */


#define JSON_ISSET     0x04        /* json_set(), not json_insert() */







/*
** json_extract(JSON, PATH, ...)
** "->"(JSON,PATH)
** "->>"(JSON,PATH)
**
** Return the element described by PATH.  Return NULL if that PATH element







|















|
|
>
>
>








>









|





|


>

>
>
>
>
|



>




















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

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

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

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


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

<
|

<
<
<
<
<
|



>
>
|
<
>
>
>
>

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

|
<
|
<
<
<
<

|
|
|
>
|
|
<
>

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

>
|
>
>
>
>


<
>
|
|
<
<

>
|








<
|


>





<
|


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

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


>
>
>
|
|
|
|
|

|

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

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

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

<
<
>
>
>
>
>

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

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

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

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

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

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







>
|
>

|
<
<
>
>
>
>
>

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

<
|
>
|
|
<
<
<
<
<

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





|
|
<







>

|
>
|

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








|












|
|
|
















|



|


|


<














|

|

|

<


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

|
>


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







205091
205092
205093
205094
205095
205096
205097
205098
205099
205100
205101
205102
205103
205104
205105
205106
205107
205108
205109
205110
205111
205112
205113
205114
205115
205116
205117
205118
205119
205120
205121
205122
205123
205124
205125
205126
205127
205128
205129
205130
205131
205132
205133
205134
205135
205136
205137
205138
205139
205140
205141
205142
205143
205144
205145
205146
205147
205148
205149
205150
205151
205152
205153
205154
205155
205156
205157
205158
205159
205160
205161
205162
205163
205164
205165
205166
205167
205168
205169
205170
205171
205172
205173
205174
205175
205176
205177
205178
205179
205180
205181
205182
205183
205184
205185
205186
205187
205188
205189
205190
205191
205192
205193
205194
205195
205196
205197
205198
205199

205200
205201
205202
205203
205204
205205

205206
205207
205208
205209
205210
205211
205212
205213
205214
205215
205216
205217

205218
205219
205220
205221
205222
205223
205224
205225
205226
205227
205228
205229
205230
205231
205232
205233
205234
205235
205236
205237
205238
205239
205240
205241
205242
205243
205244
205245
205246
205247
205248

205249
205250
205251
205252
205253
205254
205255
205256
205257
205258
205259
205260
205261
205262
205263
205264
205265
205266
205267
205268
205269
205270
205271
205272
205273
205274
205275
205276
205277
205278
205279
205280
205281
205282
205283
205284
205285
205286
205287
205288
205289
205290
205291
205292
205293
205294
205295
205296
205297
205298
205299
205300
205301
205302
205303
205304
205305
205306
205307
205308
205309
205310
205311
205312
205313
205314
205315
205316
205317
205318
205319

205320
205321

205322
205323
205324
205325
205326
205327
205328
205329
205330
205331
205332
205333

205334
205335
205336
205337
205338
205339
205340







205341
205342
205343
205344
205345
205346


205347
205348
205349
205350


205351
205352
205353
205354
205355
205356
205357

205358
205359
205360
205361
205362
205363

205364
205365
205366

205367
205368
205369
205370
205371
205372
205373
205374
205375
205376
205377
205378
205379
205380
205381
205382
205383
205384
205385
205386
205387
205388
205389
205390
205391
205392
205393
205394
205395
205396
205397
205398
205399
205400


205401
205402
205403
205404
205405
205406
205407
205408
205409
205410
205411
205412
205413
205414
205415
205416
205417
205418
205419
205420
205421
205422
205423

205424
205425
205426
205427
205428
205429
205430
205431
205432
205433
205434
205435
205436

205437
205438
205439
205440
205441
205442
205443
205444
205445

205446

205447
205448
205449
205450
205451
205452
205453
205454
205455
205456
205457
205458
205459
205460
205461
205462
205463
205464
205465
205466
205467
205468
205469
205470
205471
205472
205473
205474
205475



205476
205477
205478
205479
205480
205481
205482
205483
205484
205485
205486
205487
205488
205489
205490
205491
205492
205493
205494
205495
205496
205497
205498
205499
205500
205501
205502
205503
205504
205505
205506
205507
205508
205509
205510
205511
205512
205513
205514

205515
205516
205517

205518
205519

205520
205521
205522
205523
205524
205525
205526
205527
205528
205529
205530
205531
205532
205533
205534
205535
205536
205537
205538
205539
205540
205541
205542
205543
205544
205545
205546
205547
205548
205549
205550
205551
205552
205553
205554
205555
205556
205557
205558
205559
205560
205561
205562

205563
205564


205565
205566
205567
205568
205569
205570
205571
205572





205573


205574
205575
205576
205577
205578
205579
205580
205581
205582
205583
205584
205585
205586
205587
205588
205589
205590
205591
205592
205593
205594
205595
205596
205597
205598
205599
205600
205601

205602
205603
205604
205605
205606
205607

205608
205609

205610
205611





205612
205613
205614
205615
205616
205617
205618

205619
205620
205621
205622
205623
205624
205625
205626
205627
205628
205629
205630
205631
205632
205633
205634
205635
205636
205637
205638
205639
205640
205641
205642
205643
205644
205645
205646
205647
205648
205649
205650
205651
205652
205653
205654
205655
205656
205657
205658
205659
205660
205661
205662
205663
205664
205665
205666
205667
205668
205669
205670
205671
205672
205673
205674
205675
205676
205677
205678
205679
205680
205681
205682
205683
205684
205685
205686
205687
205688
205689
205690
205691
205692
205693
205694
205695
205696
205697
205698
205699
205700
205701
205702
205703
205704
205705
205706
205707
205708
205709
205710
205711
205712
205713
205714
205715
205716
205717
205718
205719
205720
205721
205722
205723
205724
205725


205726
205727
205728
205729
205730
205731
205732
205733
205734
205735
205736
205737
205738
205739
205740
205741
205742
205743
205744
205745
205746
205747
205748
205749
205750
205751
205752
205753
205754
205755
205756

205757




205758
205759
205760
205761
205762
205763
205764

205765
205766
205767
205768
205769
205770
205771
205772
205773
205774
205775
205776
205777
205778
205779
205780
205781
205782
205783
205784
205785
205786
205787
205788
205789
205790
205791
205792
205793
205794
205795
205796
205797
205798
205799
205800
205801
205802
205803
205804
205805
205806
205807
205808
205809
205810
205811
205812

205813
205814
205815
205816
205817
205818
205819
205820
205821

205822
205823
205824
205825
205826
205827

205828
205829
205830
205831
205832
205833
205834
205835
205836
205837
205838
205839
205840
205841
205842
205843
205844
205845
205846
205847
205848
205849
205850
205851
205852
205853
205854
205855
205856
205857
205858
205859
205860
205861
205862
205863
205864
205865
205866
205867
205868
205869
205870

205871
205872
205873
205874
205875
205876
205877
205878
205879

205880
205881
205882


205883
205884
205885
205886
205887
205888
205889
205890
205891
205892
205893

205894
205895
205896
205897
205898
205899
205900
205901
205902

205903
205904
205905
205906
205907
205908
205909

205910
205911
205912
205913
205914
205915
205916
205917
205918
205919
205920
205921
205922
205923
205924
205925
205926
205927
205928
205929
205930
205931
205932
205933


205934



205935
205936
205937
205938
205939
205940
205941
205942
205943
205944
205945
205946
205947
205948
205949
205950
205951
205952
205953
205954
205955
205956
205957
205958
205959

205960
205961
205962
205963
205964
205965
205966
205967
205968
205969
205970
205971
205972
205973
205974
205975
205976
205977
205978
205979
205980
205981
205982
205983
205984
205985
205986
205987
205988
205989
205990
205991
205992
205993
205994
205995
205996
205997
205998
205999
206000
206001
206002
206003
206004
206005
206006
206007
206008

206009
206010
206011
206012
206013
206014
206015
206016
206017
206018
206019
206020
206021
206022
206023
206024
206025
206026
206027
206028
206029
206030
206031
206032
206033
206034
206035
206036
206037
206038
206039
206040
206041
206042
206043
206044
206045
206046
206047
206048
206049
206050
206051
206052
206053
206054
206055
206056
206057
206058
206059
206060
206061
206062
206063
206064
206065
206066
206067
206068
206069
206070
206071
206072
206073
206074
206075
206076
206077
206078

206079
206080
206081
206082
206083
206084
206085
206086
206087
206088
206089
206090
206091
206092
206093
206094
206095
206096
206097
206098
206099
206100
206101
206102
206103
206104
206105
206106
206107
206108
206109
206110


206111
206112
206113
206114
206115
206116


206117
206118
206119
206120
206121
206122
206123
206124
206125
206126

206127
206128
206129
206130
206131
206132
206133
206134
206135
206136
206137
206138
206139
206140
206141


206142
206143
206144
206145
206146
206147
206148
206149
206150
206151
206152
206153
206154
206155
206156
206157
206158
206159
206160
206161
206162
206163
206164
206165
206166
206167
206168
206169
206170
206171
206172
206173
206174
206175
206176
206177
206178
206179
206180
206181
206182
206183
206184
206185
206186
206187
206188
206189
206190
206191
206192
206193
206194
206195
206196

206197
206198
206199
206200
206201
206202
206203
206204
206205
206206
206207
206208
206209
206210
206211

206212
206213
206214
206215
206216
206217
206218
206219
206220
206221
206222
206223
206224
206225
206226
206227
206228
206229
206230
206231
206232
206233
206234
206235
206236
206237
206238
206239
206240
206241
206242
206243
206244
206245
206246
206247
206248
206249

206250
206251
206252
206253
206254
206255
206256
206257
206258
206259
206260
206261
206262
206263

206264
206265
206266
206267
206268
206269
206270




206271
206272
206273
206274
206275
206276
206277
206278
206279
206280
206281

206282
206283
206284
206285
206286
206287
206288
206289
206290
206291
206292
206293
206294
206295
206296
206297
206298
206299
206300
206301
206302
206303
206304
206305
206306
206307
206308
206309
206310
206311
206312
206313
206314
206315
206316
206317
206318
206319
206320
206321
206322
206323
206324
206325
206326
206327
206328
206329
206330
206331
206332
206333
206334
206335
206336
206337
206338
206339
206340
206341
206342
206343
206344
206345
206346
206347
206348
206349
206350
206351
206352
206353
206354
206355
206356
206357
206358
206359
206360
206361
206362
206363
206364
206365







206366
206367

206368
206369
206370
206371
206372

206373
206374
206375
206376
206377
206378
206379
206380
206381
206382
206383
206384
206385
206386
206387
206388
206389
206390
206391
206392
206393
206394
206395
206396
206397
206398
206399
206400
206401
206402
206403
206404
206405
206406
206407
206408
206409
206410
206411
206412
206413
206414
206415
206416
206417
206418
206419
206420
206421
206422
206423
206424
206425
206426
206427
206428
206429
206430
206431
206432
206433
206434
206435
206436

206437
206438
206439
206440
206441
206442
206443
206444
206445
206446
206447
206448
206449
206450
206451
206452
206453
206454
206455
206456
206457
206458
206459
206460
206461
206462
206463
206464
206465
206466
206467
206468
206469
206470
206471
206472
206473
206474
206475
206476
206477
206478
206479
206480
206481
206482
206483
206484
206485
206486
206487
206488
206489
206490
206491
206492
206493
206494
206495
206496
206497
206498
206499
206500
206501
206502
206503
206504
206505

206506
206507








206508
206509
206510
206511
206512
206513
206514
206515
206516
206517
206518
206519
206520




206521
206522
206523
206524
206525
206526
206527
206528
206529
206530
206531
206532
206533
206534
206535
206536
206537
206538
206539
206540
206541
206542
206543

206544
206545

206546
206547
206548
206549
206550
206551
206552
206553
206554
206555
206556
206557
206558
206559
206560
206561
206562
206563
206564
206565
206566
206567
206568
206569
206570
206571
206572
206573
206574
206575
206576
206577
206578
206579
206580
206581
206582
206583
206584
206585
206586
206587
206588
206589
206590
206591
206592
206593
206594
206595
206596
206597
206598
206599
206600
206601
206602
206603


206604
206605
206606
206607
206608
206609
206610
206611
206612
206613
206614
206615
206616
206617
206618
206619
206620
206621
206622
206623
206624





206625
206626

206627
206628
206629
206630





206631
206632
206633
206634
206635
206636
206637
206638




206639
206640
206641
206642
206643
206644
206645
206646
206647
206648
206649
206650
206651
206652
206653
206654
206655
206656
206657
206658
206659
206660
206661
206662
206663
206664
206665
206666
206667



206668
206669
206670
206671
206672


206673
206674
206675
206676
206677
206678
206679
206680
206681
206682
206683
206684
206685
206686
206687
206688
206689
206690
206691
206692
206693
206694
206695
206696
206697
206698
206699
206700
206701
206702
206703
206704
206705
206706
206707
206708
206709
206710
206711
206712
206713
206714
206715
206716

206717
206718
206719
206720
206721
206722
206723
206724
206725
206726
206727
206728
206729


206730









206731

206732
206733
206734
206735





206736






206737
206738
206739
206740
206741
206742
206743
206744
206745
206746
206747
206748
206749
206750
206751
206752
206753
206754
206755
206756
206757
206758
206759
206760
206761
206762
206763
206764
206765
206766
206767
206768
206769
206770
206771
206772
206773
206774
206775
206776
206777
206778
206779
206780
206781
206782
206783
206784
206785
206786

206787
206788
206789
206790
206791
206792
206793
206794
206795
206796
206797
206798
206799
206800
206801
206802
206803
206804
206805
206806

206807
206808
206809
206810
206811
206812
206813
206814
206815
206816
206817
206818
206819
206820
206821


206822


206823
206824


206825
206826
206827
206828
206829
206830
206831
206832
206833
206834
206835

206836


206837
206838
206839
206840
206841
206842
206843
206844
206845
206846
206847
206848
206849
206850
206851
206852
206853
206854
      goto json_parse_restart;
    }
    pParse->iErr = i;
    return -1;
  }
  case 'n': {
    if( strncmp(z+i,"null",4)==0 && !sqlite3Isalnum(z[i+4]) ){
      jsonBlobAppendOneByte(pParse, JSONB_NULL);
      return i+4;
    }
    /* fall-through into the default case that checks for NaN */
  }
  default: {
    u32 k;
    int nn;
    c = z[i];
    for(k=0; k<sizeof(aNanInfName)/sizeof(aNanInfName[0]); k++){
      if( c!=aNanInfName[k].c1 && c!=aNanInfName[k].c2 ) continue;
      nn = aNanInfName[k].n;
      if( sqlite3StrNICmp(&z[i], aNanInfName[k].zMatch, nn)!=0 ){
        continue;
      }
      if( sqlite3Isalnum(z[i+nn]) ) continue;
      if( aNanInfName[k].eType==JSONB_FLOAT ){
        jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999");
      }else{
        jsonBlobAppendOneByte(pParse, JSONB_NULL);
      }
      pParse->hasNonstd = 1;
      return i + nn;
    }
    pParse->iErr = i;
    return -1;  /* Syntax error */
  }
  } /* End switch(z[i]) */
}


/*
** Parse a complete JSON string.  Return 0 on success or non-zero if there
** are any errors.  If an error occurs, free all memory held by pParse,
** but not pParse itself.
**
** pParse must be initialized to an empty parse object prior to calling
** this routine.
*/
static int jsonConvertTextToBlob(
  JsonParse *pParse,           /* Initialize and fill this JsonParse object */
  sqlite3_context *pCtx        /* Report errors here */
){
  int i;
  const char *zJson = pParse->zJson;
  i = jsonTranslateTextToBlob(pParse, 0);
  if( pParse->oom ) i = -1;
  if( i>0 ){
#ifdef SQLITE_DEBUG
    assert( pParse->iDepth==0 );
    if( sqlite3Config.bJsonSelfcheck ){
      assert( jsonbValidityCheck(pParse, 0, pParse->nBlob, 0)==0 );
    }
#endif
    while( jsonIsspace(zJson[i]) ) i++;
    if( zJson[i] ){
      i += json5Whitespace(&zJson[i]);
      if( zJson[i] ){
        if( pCtx ) sqlite3_result_error(pCtx, "malformed JSON", -1);
        jsonParseReset(pParse);
        return 1;
      }
      pParse->hasNonstd = 1;
    }
  }
  if( i<=0 ){
    if( pCtx!=0 ){
      if( pParse->oom ){
        sqlite3_result_error_nomem(pCtx);
      }else{
        sqlite3_result_error(pCtx, "malformed JSON", -1);
      }
    }
    jsonParseReset(pParse);
    return 1;
  }
  return 0;
}

/*
** The input string pStr is a well-formed JSON text string.  Convert
** this into the JSONB format and make it the return value of the
** SQL function.
*/
static void jsonReturnStringAsBlob(JsonString *pStr){
  JsonParse px;
  memset(&px, 0, sizeof(px));
  jsonStringTerminate(pStr);
  px.zJson = pStr->zBuf;
  px.nJson = pStr->nUsed;
  px.db = sqlite3_context_db_handle(pStr->pCtx);
  (void)jsonTranslateTextToBlob(&px, 0);
  if( px.oom ){
    sqlite3DbFree(px.db, px.aBlob);
    sqlite3_result_error_nomem(pStr->pCtx);
  }else{
    assert( px.nBlobAlloc>0 );
    assert( !px.bReadOnly );
    sqlite3_result_blob(pStr->pCtx, px.aBlob, px.nBlob, SQLITE_DYNAMIC);
  }
}


/* The byte at index i is a node type-code.  This routine
** determines the payload size for that node and writes that
** payload size in to *pSz.  It returns the offset from i to the
** beginning of the payload.  Return 0 on error.
*/
static u32 jsonbPayloadSize(const JsonParse *pParse, u32 i, u32 *pSz){

  u8 x;
  u32 sz;
  u32 n;
  if( NEVER(i>pParse->nBlob) ){
    *pSz = 0;
    return 0;
  }
  x = pParse->aBlob[i]>>4;
  if( x<=11 ){
    sz = x;
    n = 1;
  }else if( x==12 ){

    if( i+1>=pParse->nBlob ){
      *pSz = 0;
      return 0;
    }
    sz = pParse->aBlob[i+1];
    n = 2;
  }else if( x==13 ){
    if( i+2>=pParse->nBlob ){
      *pSz = 0;
      return 0;
    }
    sz = (pParse->aBlob[i+1]<<8) + pParse->aBlob[i+2];
    n = 3;
  }else if( x==14 ){
    if( i+4>=pParse->nBlob ){
      *pSz = 0;
      return 0;
    }
    sz = ((u32)pParse->aBlob[i+1]<<24) + (pParse->aBlob[i+2]<<16) +
         (pParse->aBlob[i+3]<<8) + pParse->aBlob[i+4];
    n = 5;
  }else{
    if( i+8>=pParse->nBlob
     || pParse->aBlob[i+1]!=0
     || pParse->aBlob[i+2]!=0
     || pParse->aBlob[i+3]!=0
     || pParse->aBlob[i+4]!=0
    ){
      *pSz = 0;
      return 0;
    }

    sz = (pParse->aBlob[i+5]<<24) + (pParse->aBlob[i+6]<<16) +
         (pParse->aBlob[i+7]<<8) + pParse->aBlob[i+8];
    n = 9;
  }
  if( (i64)i+sz+n > pParse->nBlob
   && (i64)i+sz+n > pParse->nBlob-pParse->delta
  ){
    sz = 0;
    n = 0;
  }
  *pSz = sz;
  return n;
}


/*
** Translate the binary JSONB representation of JSON beginning at
** pParse->aBlob[i] into a JSON text string.  Append the JSON
** text onto the end of pOut.  Return the index in pParse->aBlob[]
** of the first byte past the end of the element that is translated.
**
** If an error is detected in the BLOB input, the pOut->eErr flag
** might get set to JSTRING_MALFORMED.  But not all BLOB input errors
** are detected.  So a malformed JSONB input might either result
** in an error, or in incorrect JSON.
**
** The pOut->eErr JSTRING_OOM flag is set on a OOM.
*/
static u32 jsonTranslateBlobToText(
  const JsonParse *pParse,       /* the complete parse of the JSON */
  u32 i,                         /* Start rendering at this index */
  JsonString *pOut               /* Write JSON here */
){
  u32 sz, n, j, iEnd;

  n = jsonbPayloadSize(pParse, i, &sz);
  if( n==0 ){
    pOut->eErr |= JSTRING_MALFORMED;
    return pParse->nBlob+1;
  }
  switch( pParse->aBlob[i] & 0x0f ){
    case JSONB_NULL: {
      jsonAppendRawNZ(pOut, "null", 4);
      return i+1;
    }
    case JSONB_TRUE: {
      jsonAppendRawNZ(pOut, "true", 4);
      return i+1;
    }
    case JSONB_FALSE: {
      jsonAppendRawNZ(pOut, "false", 5);
      return i+1;
    }
    case JSONB_INT:
    case JSONB_FLOAT: {
      if( sz==0 ) goto malformed_jsonb;
      jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz);
      break;
    }
    case JSONB_INT5: {  /* Integer literal in hexadecimal notation */
      u32 k = 2;
      sqlite3_uint64 u = 0;
      const char *zIn = (const char*)&pParse->aBlob[i+n];
      int bOverflow = 0;
      if( sz==0 ) goto malformed_jsonb;
      if( zIn[0]=='-' ){
        jsonAppendChar(pOut, '-');
        k++;
      }else if( zIn[0]=='+' ){
        k++;
      }

      for(; k<sz; k++){
        if( !sqlite3Isxdigit(zIn[k]) ){

          pOut->eErr |= JSTRING_MALFORMED;
          break;
        }else if( (u>>60)!=0 ){
          bOverflow = 1;
        }else{
          u = u*16 + sqlite3HexToInt(zIn[k]);
        }
      }
      jsonPrintf(100,pOut,bOverflow?"9.0e999":"%llu", u);
      break;
    }
    case JSONB_FLOAT5: { /* Float literal missing digits beside "." */

      u32 k = 0;
      const char *zIn = (const char*)&pParse->aBlob[i+n];
      if( sz==0 ) goto malformed_jsonb;
      if( zIn[0]=='-' ){
        jsonAppendChar(pOut, '-');
        k++;
      }







      if( zIn[k]=='.' ){
        jsonAppendChar(pOut, '0');
      }
      for(; k<sz; k++){
        jsonAppendChar(pOut, zIn[k]);
        if( zIn[k]=='.' && (k+1==sz || !sqlite3Isdigit(zIn[k+1])) ){


          jsonAppendChar(pOut, '0');
        }
      }
      break;


    }
    case JSONB_TEXT:
    case JSONB_TEXTJ: {
      jsonAppendChar(pOut, '"');
      jsonAppendRaw(pOut, (const char*)&pParse->aBlob[i+n], sz);
      jsonAppendChar(pOut, '"');
      break;

    }
    case JSONB_TEXT5: {
      const char *zIn;
      u32 k;
      u32 sz2 = sz;
      zIn = (const char*)&pParse->aBlob[i+n];

      jsonAppendChar(pOut, '"');
      while( sz2>0 ){
        for(k=0; k<sz2 && zIn[k]!='\\' && zIn[k]!='"'; k++){}

        if( k>0 ){
          jsonAppendRawNZ(pOut, zIn, k);
          if( k>=sz2 ){
            break;
          }
          zIn += k;
          sz2 -= k;
        }
        if( zIn[0]=='"' ){
          jsonAppendRawNZ(pOut, "\\\"", 2);
          zIn++;
          sz2--;
          continue;
        }
        assert( zIn[0]=='\\' );
        assert( sz2>=1 );
        if( sz2<2 ){
          pOut->eErr |= JSTRING_MALFORMED;
          break;
        }
        switch( (u8)zIn[1] ){
          case '\'':
            jsonAppendChar(pOut, '\'');
            break;
          case 'v':
            jsonAppendRawNZ(pOut, "\\u0009", 6);
            break;
          case 'x':
            if( sz2<4 ){
              pOut->eErr |= JSTRING_MALFORMED;
              sz2 = 2;
              break;
            }
            jsonAppendRawNZ(pOut, "\\u00", 4);


            jsonAppendRawNZ(pOut, &zIn[2], 2);
            zIn += 2;
            sz2 -= 2;
            break;
          case '0':
            jsonAppendRawNZ(pOut, "\\u0000", 6);
            break;
          case '\r':
            if( sz2>2 && zIn[2]=='\n' ){
              zIn++;
              sz2--;
            }
            break;
          case '\n':
            break;
          case 0xe2:
            /* '\' followed by either U+2028 or U+2029 is ignored as
            ** whitespace.  Not that in UTF8, U+2028 is 0xe2 0x80 0x29.
            ** U+2029 is the same except for the last byte */
            if( sz2<4
             || 0x80!=(u8)zIn[2]
             || (0xa8!=(u8)zIn[3] && 0xa9!=(u8)zIn[3])
            ){

              pOut->eErr |= JSTRING_MALFORMED;
              sz2 = 2;
              break;
            }
            zIn += 2;
            sz2 -= 2;
            break;
          default:
            jsonAppendRawNZ(pOut, zIn, 2);
            break;
        }
        assert( sz2>=2 );
        zIn += 2;

        sz2 -= 2;
      }
      jsonAppendChar(pOut, '"');
      break;
    }
    case JSONB_TEXTRAW: {
      jsonAppendString(pOut, (const char*)&pParse->aBlob[i+n], sz);
      break;
    }

    case JSONB_ARRAY: {

      jsonAppendChar(pOut, '[');
      j = i+n;
      iEnd = j+sz;
      while( j<iEnd && pOut->eErr==0 ){
        j = jsonTranslateBlobToText(pParse, j, pOut);
        jsonAppendChar(pOut, ',');
      }
      if( j>iEnd ) pOut->eErr |= JSTRING_MALFORMED;
      if( sz>0 ) jsonStringTrimOneChar(pOut);
      jsonAppendChar(pOut, ']');
      break;
    }
    case JSONB_OBJECT: {
      int x = 0;
      jsonAppendChar(pOut, '{');
      j = i+n;
      iEnd = j+sz;
      while( j<iEnd && pOut->eErr==0 ){
        j = jsonTranslateBlobToText(pParse, j, pOut);
        jsonAppendChar(pOut, (x++ & 1) ? ',' : ':');
      }
      if( (x & 1)!=0 || j>iEnd ) pOut->eErr |= JSTRING_MALFORMED;
      if( sz>0 ) jsonStringTrimOneChar(pOut);
      jsonAppendChar(pOut, '}');
      break;
    }

    default: {
      malformed_jsonb:



      pOut->eErr |= JSTRING_MALFORMED;
      break;
    }
  }
  return i+n+sz;
}

/* Return true if the input pJson
**
** For performance reasons, this routine does not do a detailed check of the
** input BLOB to ensure that it is well-formed.  Hence, false positives are
** possible.  False negatives should never occur, however.
*/
static int jsonFuncArgMightBeBinary(sqlite3_value *pJson){
  u32 sz, n;
  const u8 *aBlob;
  int nBlob;
  JsonParse s;
  if( sqlite3_value_type(pJson)!=SQLITE_BLOB ) return 0;
  aBlob = sqlite3_value_blob(pJson);
  nBlob = sqlite3_value_bytes(pJson);
  if( nBlob<1 ) return 0;
  if( NEVER(aBlob==0) || (aBlob[0] & 0x0f)>JSONB_OBJECT ) return 0;
  memset(&s, 0, sizeof(s));
  s.aBlob = (u8*)aBlob;
  s.nBlob = nBlob;
  n = jsonbPayloadSize(&s, 0, &sz);
  if( n==0 ) return 0;
  if( sz+n!=(u32)nBlob ) return 0;
  if( (aBlob[0] & 0x0f)<=JSONB_FALSE && sz>0 ) return 0;
  return sz+n==(u32)nBlob;
}

/*
** Given that a JSONB_ARRAY object starts at offset i, return
** the number of entries in that array.
*/
static u32 jsonbArrayCount(JsonParse *pParse, u32 iRoot){
  u32 n, sz, i, iEnd;

  u32 k = 0;
  n = jsonbPayloadSize(pParse, iRoot, &sz);
  iEnd = iRoot+n+sz;

  for(i=iRoot+n; n>0 && i<iEnd; i+=sz+n, k++){
    n = jsonbPayloadSize(pParse, i, &sz);

  }
  return k;
}

/*
** Edit the payload size of the element at iRoot by the amount in
** pParse->delta.
*/
static void jsonAfterEditSizeAdjust(JsonParse *pParse, u32 iRoot){
  u32 sz = 0;
  u32 nBlob;
  assert( pParse->delta!=0 );
  assert( pParse->nBlobAlloc >= pParse->nBlob );
  nBlob = pParse->nBlob;
  pParse->nBlob = pParse->nBlobAlloc;
  (void)jsonbPayloadSize(pParse, iRoot, &sz);
  pParse->nBlob = nBlob;
  sz += pParse->delta;
  pParse->delta += jsonBlobChangePayloadSize(pParse, iRoot, sz);
}

/*
** Modify the JSONB blob at pParse->aBlob by removing nDel bytes of
** content beginning at iDel, and replacing them with nIns bytes of
** content given by aIns.
**
** nDel may be zero, in which case no bytes are removed.  But iDel is
** still important as new bytes will be insert beginning at iDel.
**
** aIns may be zero, in which case space is created to hold nIns bytes
** beginning at iDel, but that space is uninitialized.
**
** Set pParse->oom if an OOM occurs.
*/
static void jsonBlobEdit(
  JsonParse *pParse,     /* The JSONB to be modified is in pParse->aBlob */
  u32 iDel,              /* First byte to be removed */
  u32 nDel,              /* Number of bytes to remove */
  const u8 *aIns,        /* Content to insert */
  u32 nIns               /* Bytes of content to insert */
){
  i64 d = (i64)nIns - (i64)nDel;
  if( d!=0 ){

    if( pParse->nBlob + d > pParse->nBlobAlloc ){
      jsonBlobExpand(pParse, pParse->nBlob+d);


      if( pParse->oom ) return;
    }
    memmove(&pParse->aBlob[iDel+nIns],
            &pParse->aBlob[iDel+nDel],
            pParse->nBlob - (iDel+nDel));
    pParse->nBlob += d;
    pParse->delta += d;
  }





  if( nIns && aIns ) memcpy(&pParse->aBlob[iDel], aIns, nIns);


}

/*
** Return the number of escaped newlines to be ignored.
** An escaped newline is a one of the following byte sequences:
**
**    0x5c 0x0a
**    0x5c 0x0d
**    0x5c 0x0d 0x0a
**    0x5c 0xe2 0x80 0xa8
**    0x5c 0xe2 0x80 0xa9
*/
static u32 jsonBytesToBypass(const char *z, u32 n){
  u32 i = 0;
  while( i+1<n ){
    if( z[i]!='\\' ) return i;
    if( z[i+1]=='\n' ){
      i += 2;
      continue;
    }
    if( z[i+1]=='\r' ){
      if( i+2<n && z[i+2]=='\n' ){
        i += 3;
      }else{
        i += 2;
      }
      continue;
    }

    if( 0xe2==(u8)z[i+1]
     && i+3<n
     && 0x80==(u8)z[i+2]
     && (0xa8==(u8)z[i+3] || 0xa9==(u8)z[i+3])
    ){
      i += 4;

      continue;
    }

    break;
  }





  return i;
}

/*
** Input z[0..n] defines JSON escape sequence including the leading '\\'.
** Decode that escape sequence into a single character.  Write that
** character into *piOut.  Return the number of bytes in the escape sequence.

**
** If there is a syntax error of some kind (for example too few characters
** after the '\\' to complete the encoding) then *piOut is set to
** JSON_INVALID_CHAR.
*/
static u32 jsonUnescapeOneChar(const char *z, u32 n, u32 *piOut){
  assert( n>0 );
  assert( z[0]=='\\' );
  if( n<2 ){
    *piOut = JSON_INVALID_CHAR;
    return n;
  }
  switch( (u8)z[1] ){
    case 'u': {
      u32 v, vlo;
      if( n<6 ){
        *piOut = JSON_INVALID_CHAR;
        return n;
      }
      v = jsonHexToInt4(&z[2]);
      if( (v & 0xfc00)==0xd800
       && n>=12
       && z[6]=='\\'
       && z[7]=='u'
       && ((vlo = jsonHexToInt4(&z[8]))&0xfc00)==0xdc00
      ){
        *piOut = ((v&0x3ff)<<10) + (vlo&0x3ff) + 0x10000;
        return 12;
      }else{
        *piOut = v;
        return 6;
      }
    }
    case 'b': {   *piOut = '\b';  return 2; }
    case 'f': {   *piOut = '\f';  return 2; }
    case 'n': {   *piOut = '\n';  return 2; }
    case 'r': {   *piOut = '\r';  return 2; }
    case 't': {   *piOut = '\t';  return 2; }
    case 'v': {   *piOut = '\v';  return 2; }
    case '0': {   *piOut = 0;     return 2; }
    case '\'':
    case '"':
    case '/':
    case '\\':{   *piOut = z[1];  return 2; }
    case 'x': {
      if( n<4 ){
        *piOut = JSON_INVALID_CHAR;
        return n;
      }
      *piOut = (jsonHexToInt(z[2])<<4) | jsonHexToInt(z[3]);
      return 4;
    }
    case 0xe2:
    case '\r':
    case '\n': {
      u32 nSkip = jsonBytesToBypass(z, n);
      if( nSkip==0 ){
        *piOut = JSON_INVALID_CHAR;
        return n;
      }else if( nSkip==n ){
        *piOut = 0;
        return n;
      }else if( z[nSkip]=='\\' ){
        return nSkip + jsonUnescapeOneChar(&z[nSkip], n-nSkip, piOut);
      }else{
        int sz = sqlite3Utf8ReadLimited((u8*)&z[nSkip], n-nSkip, piOut);
        return nSkip + sz;
      }
    }
    default: {
      *piOut = JSON_INVALID_CHAR;
      return 2;
    }
  }
}


/*
** Compare two object labels.  Return 1 if they are equal and
** 0 if they differ.
**
** In this version, we know that one or the other or both of the
** two comparands contains an escape sequence.
*/
static SQLITE_NOINLINE int jsonLabelCompareEscaped(
  const char *zLeft,          /* The left label */
  u32 nLeft,                  /* Size of the left label in bytes */
  int rawLeft,                /* True if zLeft contains no escapes */
  const char *zRight,         /* The right label */
  u32 nRight,                 /* Size of the right label in bytes */
  int rawRight                /* True if zRight is escape-free */
){
  u32 cLeft, cRight;
  assert( rawLeft==0 || rawRight==0 );
  while( 1 /*exit-by-return*/ ){
    if( nLeft==0 ){
      cLeft = 0;
    }else if( rawLeft || zLeft[0]!='\\' ){
      cLeft = ((u8*)zLeft)[0];
      if( cLeft>=0xc0 ){
        int sz = sqlite3Utf8ReadLimited((u8*)zLeft, nLeft, &cLeft);
        zLeft += sz;
        nLeft -= sz;
      }else{
        zLeft++;
        nLeft--;
      }


    }else{
      u32 n = jsonUnescapeOneChar(zLeft, nLeft, &cLeft);
      zLeft += n;
      assert( n<=nLeft );
      nLeft -= n;
    }
    if( nRight==0 ){
      cRight = 0;
    }else if( rawRight || zRight[0]!='\\' ){
      cRight = ((u8*)zRight)[0];
      if( cRight>=0xc0 ){
        int sz = sqlite3Utf8ReadLimited((u8*)zRight, nRight, &cRight);
        zRight += sz;
        nRight -= sz;
      }else{
        zRight++;
        nRight--;
      }
    }else{
      u32 n = jsonUnescapeOneChar(zRight, nRight, &cRight);
      zRight += n;
      assert( n<=nRight );
      nRight -= n;
    }
    if( cLeft!=cRight ) return 0;
    if( cLeft==0 ) return 1;
  }
}

/*
** Compare two object labels.  Return 1 if they are equal and

** 0 if they differ.  Return -1 if an OOM occurs.




*/
static int jsonLabelCompare(
  const char *zLeft,          /* The left label */
  u32 nLeft,                  /* Size of the left label in bytes */
  int rawLeft,                /* True if zLeft contains no escapes */
  const char *zRight,         /* The right label */
  u32 nRight,                 /* Size of the right label in bytes */

  int rawRight                /* True if zRight is escape-free */
){
  if( rawLeft && rawRight ){
    /* Simpliest case:  Neither label contains escapes.  A simple
    ** memcmp() is sufficient. */
    if( nLeft!=nRight ) return 0;
    return memcmp(zLeft, zRight, nLeft)==0;
  }else{
    return jsonLabelCompareEscaped(zLeft, nLeft, rawLeft,
                                   zRight, nRight, rawRight);
  }
}

/*
** Error returns from jsonLookupStep()
*/
#define JSON_LOOKUP_ERROR      0xffffffff
#define JSON_LOOKUP_NOTFOUND   0xfffffffe
#define JSON_LOOKUP_PATHERROR  0xfffffffd
#define JSON_LOOKUP_ISERROR(x) ((x)>=JSON_LOOKUP_PATHERROR)

/* Forward declaration */
static u32 jsonLookupStep(JsonParse*,u32,const char*,u32);


/* This helper routine for jsonLookupStep() populates pIns with
** binary data that is to be inserted into pParse.
**
** In the common case, pIns just points to pParse->aIns and pParse->nIns.
** But if the zPath of the original edit operation includes path elements
** that go deeper, additional substructure must be created.
**
** For example:
**
**     json_insert('{}', '$.a.b.c', 123);
**
** The search stops at '$.a'  But additional substructure must be
** created for the ".b.c" part of the patch so that the final result
** is:  {"a":{"b":{"c"::123}}}.  This routine populates pIns with
** the binary equivalent of {"b":{"c":123}} so that it can be inserted.
**
** The caller is responsible for resetting pIns when it has finished
** using the substructure.
*/
static u32 jsonCreateEditSubstructure(
  JsonParse *pParse,  /* The original JSONB that is being edited */
  JsonParse *pIns,    /* Populate this with the blob data to insert */
  const char *zTail   /* Tail of the path that determins substructure */

){
  static const u8 emptyObject[] = { JSONB_ARRAY, JSONB_OBJECT };
  int rc;
  memset(pIns, 0, sizeof(*pIns));
  pIns->db = pParse->db;
  if( zTail[0]==0 ){
    /* No substructure.  Just insert what is given in pParse. */
    pIns->aBlob = pParse->aIns;
    pIns->nBlob = pParse->nIns;

    rc = 0;
  }else{
    /* Construct the binary substructure */
    pIns->nBlob = 1;
    pIns->aBlob = (u8*)&emptyObject[zTail[0]=='.'];
    pIns->eEdit = pParse->eEdit;

    pIns->nIns = pParse->nIns;
    pIns->aIns = pParse->aIns;
    rc = jsonLookupStep(pIns, 0, zTail, 0);
    pParse->oom |= pIns->oom;
  }
  return rc;  /* Error code only */
}

/*
** Search along zPath to find the Json element specified.  Return an
** index into pParse->aBlob[] for the start of that element's value.
**
** If the value found by this routine is the value half of label/value pair
** within an object, then set pPath->iLabel to the start of the corresponding
** label, before returning.
**
** Return one of the JSON_LOOKUP error codes if problems are seen.
**
** This routine will also modify the blob.  If pParse->eEdit is one of
** JEDIT_DEL, JEDIT_REPL, JEDIT_INS, or JEDIT_SET, then changes might be
** made to the selected value.  If an edit is performed, then the return
** value does not necessarily point to the select element.  If an edit
** is performed, the return value is only useful for detecting error
** conditions.
*/
static u32 jsonLookupStep(
  JsonParse *pParse,      /* The JSON to search */
  u32 iRoot,              /* Begin the search at this element of aBlob[] */
  const char *zPath,      /* The path to search */
  u32 iLabel              /* Label if iRoot is a value of in an object */
){
  u32 i, j, k, nKey, sz, n, iEnd, rc;
  const char *zKey;
  u8 x;

  if( zPath[0]==0 ){
    if( pParse->eEdit && jsonBlobMakeEditable(pParse, pParse->nIns) ){
      n = jsonbPayloadSize(pParse, iRoot, &sz);
      sz += n;
      if( pParse->eEdit==JEDIT_DEL ){
        if( iLabel>0 ){
          sz += iRoot - iLabel;
          iRoot = iLabel;

        }
        jsonBlobEdit(pParse, iRoot, sz, 0, 0);
      }else if( pParse->eEdit==JEDIT_INS ){
        /* Already exists, so json_insert() is a no-op */
      }else{
        /* json_set() or json_replace() */
        jsonBlobEdit(pParse, iRoot, sz, pParse->aIns, pParse->nIns);
      }
    }

    pParse->iLabel = iLabel;
    return iRoot;
  }


  if( zPath[0]=='.' ){
    int rawKey = 1;
    x = pParse->aBlob[iRoot];
    zPath++;
    if( zPath[0]=='"' ){
      zKey = zPath + 1;
      for(i=1; zPath[i] && zPath[i]!='"'; i++){}
      nKey = i-1;
      if( zPath[i] ){
        i++;
      }else{

        return JSON_LOOKUP_PATHERROR;
      }
      testcase( nKey==0 );
      rawKey = memchr(zKey, '\\', nKey)==0;
    }else{
      zKey = zPath;
      for(i=0; zPath[i] && zPath[i]!='.' && zPath[i]!='['; i++){}
      nKey = i;
      if( nKey==0 ){

        return JSON_LOOKUP_PATHERROR;
      }
    }
    if( (x & 0x0f)!=JSONB_OBJECT ) return JSON_LOOKUP_NOTFOUND;
    n = jsonbPayloadSize(pParse, iRoot, &sz);
    j = iRoot + n;  /* j is the index of a label */
    iEnd = j+sz;

    while( j<iEnd ){
      int rawLabel;
      const char *zLabel;
      x = pParse->aBlob[j] & 0x0f;
      if( x<JSONB_TEXT || x>JSONB_TEXTRAW ) return JSON_LOOKUP_ERROR;
      n = jsonbPayloadSize(pParse, j, &sz);
      if( n==0 ) return JSON_LOOKUP_ERROR;
      k = j+n;  /* k is the index of the label text */
      if( k+sz>=iEnd ) return JSON_LOOKUP_ERROR;
      zLabel = (const char*)&pParse->aBlob[k];
      rawLabel = x==JSONB_TEXT || x==JSONB_TEXTRAW;
      if( jsonLabelCompare(zKey, nKey, rawKey, zLabel, sz, rawLabel) ){
        u32 v = k+sz;  /* v is the index of the value */
        if( ((pParse->aBlob[v])&0x0f)>JSONB_OBJECT ) return JSON_LOOKUP_ERROR;
        n = jsonbPayloadSize(pParse, v, &sz);
        if( n==0 || v+n+sz>iEnd ) return JSON_LOOKUP_ERROR;
        assert( j>0 );
        rc = jsonLookupStep(pParse, v, &zPath[i], j);
        if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot);
        return rc;
      }
      j = k+sz;
      if( ((pParse->aBlob[j])&0x0f)>JSONB_OBJECT ) return JSON_LOOKUP_ERROR;
      n = jsonbPayloadSize(pParse, j, &sz);


      if( n==0 ) return JSON_LOOKUP_ERROR;



      j += n+sz;
    }
    if( j>iEnd ) return JSON_LOOKUP_ERROR;
    if( pParse->eEdit>=JEDIT_INS ){
      u32 nIns;          /* Total bytes to insert (label+value) */
      JsonParse v;       /* BLOB encoding of the value to be inserted */
      JsonParse ix;      /* Header of the label to be inserted */
      testcase( pParse->eEdit==JEDIT_INS );
      testcase( pParse->eEdit==JEDIT_SET );
      memset(&ix, 0, sizeof(ix));
      ix.db = pParse->db;
      jsonBlobAppendNode(&ix, rawKey?JSONB_TEXTRAW:JSONB_TEXT5, nKey, 0);
      pParse->oom |= ix.oom;
      rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i]);
      if( !JSON_LOOKUP_ISERROR(rc)
       && jsonBlobMakeEditable(pParse, ix.nBlob+nKey+v.nBlob)
      ){
        assert( !pParse->oom );
        nIns = ix.nBlob + nKey + v.nBlob;
        jsonBlobEdit(pParse, j, 0, 0, nIns);
        if( !pParse->oom ){
          assert( pParse->aBlob!=0 ); /* Because pParse->oom!=0 */
          assert( ix.aBlob!=0 );      /* Because pPasre->oom!=0 */
          memcpy(&pParse->aBlob[j], ix.aBlob, ix.nBlob);
          k = j + ix.nBlob;

          memcpy(&pParse->aBlob[k], zKey, nKey);
          k += nKey;
          memcpy(&pParse->aBlob[k], v.aBlob, v.nBlob);
          if( ALWAYS(pParse->delta) ) jsonAfterEditSizeAdjust(pParse, iRoot);
        }
      }
      jsonParseReset(&v);
      jsonParseReset(&ix);
      return rc;
    }
  }else if( zPath[0]=='[' ){
    x = pParse->aBlob[iRoot] & 0x0f;
    if( x!=JSONB_ARRAY )  return JSON_LOOKUP_NOTFOUND;
    n = jsonbPayloadSize(pParse, iRoot, &sz);
    k = 0;
    i = 1;
    while( sqlite3Isdigit(zPath[i]) ){
      k = k*10 + zPath[i] - '0';
      i++;
    }
    if( i<2 || zPath[i]!=']' ){
      if( zPath[1]=='#' ){
        k = jsonbArrayCount(pParse, iRoot);
        i = 2;
        if( zPath[2]=='-' && sqlite3Isdigit(zPath[3]) ){
          unsigned int nn = 0;
          i = 3;
          do{
            nn = nn*10 + zPath[i] - '0';
            i++;
          }while( sqlite3Isdigit(zPath[i]) );
          if( nn>k ) return JSON_LOOKUP_NOTFOUND;
          k -= nn;
        }
        if( zPath[i]!=']' ){
          return JSON_LOOKUP_PATHERROR;
        }
      }else{
        return JSON_LOOKUP_PATHERROR;
      }
    }
    j = iRoot+n;
    iEnd = j+sz;
    while( j<iEnd ){
      if( k==0 ){
        rc = jsonLookupStep(pParse, j, &zPath[i+1], 0);
        if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot);
        return rc;
      }

      k--;
      n = jsonbPayloadSize(pParse, j, &sz);
      if( n==0 ) return JSON_LOOKUP_ERROR;
      j += n+sz;
    }
    if( j>iEnd ) return JSON_LOOKUP_ERROR;
    if( k>0 ) return JSON_LOOKUP_NOTFOUND;
    if( pParse->eEdit>=JEDIT_INS ){
      JsonParse v;
      testcase( pParse->eEdit==JEDIT_INS );
      testcase( pParse->eEdit==JEDIT_SET );
      rc = jsonCreateEditSubstructure(pParse, &v, &zPath[i+1]);
      if( !JSON_LOOKUP_ISERROR(rc)
       && jsonBlobMakeEditable(pParse, v.nBlob)
      ){
        assert( !pParse->oom );
        jsonBlobEdit(pParse, j, 0, v.aBlob, v.nBlob);
      }
      jsonParseReset(&v);
      if( pParse->delta ) jsonAfterEditSizeAdjust(pParse, iRoot);
      return rc;
    }
  }else{
    return JSON_LOOKUP_PATHERROR;
  }
  return JSON_LOOKUP_NOTFOUND;
}

/*
** Convert a JSON BLOB into text and make that text the return value
** of an SQL function.
*/
static void jsonReturnTextJsonFromBlob(
  sqlite3_context *ctx,
  const u8 *aBlob,
  u32 nBlob
){
  JsonParse x;
  JsonString s;

  if( NEVER(aBlob==0) ) return;
  memset(&x, 0, sizeof(x));
  x.aBlob = (u8*)aBlob;
  x.nBlob = nBlob;
  jsonStringInit(&s, ctx);
  jsonTranslateBlobToText(&x, 0, &s);
  jsonReturnString(&s, 0, 0);
}


/*
** Return the value of the BLOB node at index i.
**
** If the value is a primitive, return it as an SQL value.
** If the value is an array or object, return it as either
** JSON text or the BLOB encoding, depending on the JSON_B flag
** on the userdata.
*/
static void jsonReturnFromBlob(
  JsonParse *pParse,          /* Complete JSON parse tree */
  u32 i,                      /* Index of the node */
  sqlite3_context *pCtx,      /* Return value for this function */
  int textOnly                /* return text JSON.  Disregard user-data */
){
  u32 n, sz;
  int rc;
  sqlite3 *db = sqlite3_context_db_handle(pCtx);

  n = jsonbPayloadSize(pParse, i, &sz);
  if( n==0 ){

    sqlite3_result_error(pCtx, "malformed JSON", -1);
    return;
  }
  switch( pParse->aBlob[i] & 0x0f ){
    case JSONB_NULL: {
      if( sz ) goto returnfromblob_malformed;
      sqlite3_result_null(pCtx);
      break;
    }
    case JSONB_TRUE: {
      if( sz ) goto returnfromblob_malformed;
      sqlite3_result_int(pCtx, 1);
      break;
    }
    case JSONB_FALSE: {
      if( sz ) goto returnfromblob_malformed;
      sqlite3_result_int(pCtx, 0);
      break;
    }
    case JSONB_INT5:
    case JSONB_INT: {
      sqlite3_int64 iRes = 0;
      char *z;
      int bNeg = 0;
      char x;
      if( sz==0 ) goto returnfromblob_malformed;
      x = (char)pParse->aBlob[i+n];
      if( x=='-' ){
        if( sz<2 ) goto returnfromblob_malformed;
        n++;
        sz--;
        bNeg = 1;


      }
      z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz);
      if( z==0 ) goto returnfromblob_oom;
      rc = sqlite3DecOrHexToI64(z, &iRes);
      sqlite3DbFree(db, z);
      if( rc==0 ){


        sqlite3_result_int64(pCtx, bNeg ? -iRes : iRes);
      }else if( rc==3 && bNeg ){
        sqlite3_result_int64(pCtx, SMALLEST_INT64);
      }else if( rc==1 ){
        goto returnfromblob_malformed;
      }else{
        if( bNeg ){ n--; sz++; }
        goto to_double;
      }
      break;

    }
    case JSONB_FLOAT5:
    case JSONB_FLOAT: {
      double r;
      char *z;
      if( sz==0 ) goto returnfromblob_malformed;
    to_double:
      z = sqlite3DbStrNDup(db, (const char*)&pParse->aBlob[i+n], (int)sz);
      if( z==0 ) goto returnfromblob_oom;
      rc = sqlite3AtoF(z, &r, sqlite3Strlen30(z), SQLITE_UTF8);
      sqlite3DbFree(db, z);
      if( rc<=0 ) goto returnfromblob_malformed;
      sqlite3_result_double(pCtx, r);
      break;
    }


    case JSONB_TEXTRAW:
    case JSONB_TEXT: {
      sqlite3_result_text(pCtx, (char*)&pParse->aBlob[i+n], sz,
                          SQLITE_TRANSIENT);
      break;
    }
    case JSONB_TEXT5:
    case JSONB_TEXTJ: {
      /* Translate JSON formatted string into raw text */
      u32 iIn, iOut;
      const char *z;
      char *zOut;
      u32 nOut = sz;
      z = (const char*)&pParse->aBlob[i+n];
      zOut = sqlite3DbMallocRaw(db, nOut+1);
      if( zOut==0 ) goto returnfromblob_oom;
      for(iIn=iOut=0; iIn<sz; iIn++){
        char c = z[iIn];
        if( c=='\\' ){
          u32 v;
          u32 szEscape = jsonUnescapeOneChar(&z[iIn], sz-iIn, &v);
          if( v<=0x7f ){
            zOut[iOut++] = (char)v;
          }else if( v<=0x7ff ){
            assert( szEscape>=2 );
            zOut[iOut++] = (char)(0xc0 | (v>>6));
            zOut[iOut++] = 0x80 | (v&0x3f);
          }else if( v<0x10000 ){
            assert( szEscape>=3 );
            zOut[iOut++] = 0xe0 | (v>>12);
            zOut[iOut++] = 0x80 | ((v>>6)&0x3f);
            zOut[iOut++] = 0x80 | (v&0x3f);
          }else if( v==JSON_INVALID_CHAR ){
            /* Silently ignore illegal unicode */
          }else{
            assert( szEscape>=4 );
            zOut[iOut++] = 0xf0 | (v>>18);
            zOut[iOut++] = 0x80 | ((v>>12)&0x3f);
            zOut[iOut++] = 0x80 | ((v>>6)&0x3f);
            zOut[iOut++] = 0x80 | (v&0x3f);
          }
          iIn += szEscape - 1;
        }else{
          zOut[iOut++] = c;
        }
      } /* end for() */
      assert( iOut<=nOut );
      zOut[iOut] = 0;
      sqlite3_result_text(pCtx, zOut, iOut, SQLITE_DYNAMIC);
      break;
    }
    case JSONB_ARRAY:
    case JSONB_OBJECT: {
      int flags = textOnly ? 0 : SQLITE_PTR_TO_INT(sqlite3_user_data(pCtx));
      if( flags & JSON_BLOB ){

        sqlite3_result_blob(pCtx, &pParse->aBlob[i], sz+n, SQLITE_TRANSIENT);
      }else{
        jsonReturnTextJsonFromBlob(pCtx, &pParse->aBlob[i], sz+n);
      }
      break;
    }
    default: {
      goto returnfromblob_malformed;
    }
  }
  return;

returnfromblob_oom:
  sqlite3_result_error_nomem(pCtx);
  return;


returnfromblob_malformed:
  sqlite3_result_error(pCtx, "malformed JSON", -1);
  return;
}

/*
** pArg is a function argument that might be an SQL value or a JSON
** value.  Figure out what it is and encode it as a JSONB blob.
** Return the results in pParse.
**
** pParse is uninitialized upon entry.  This routine will handle the
** initialization of pParse.  The result will be contained in
** pParse->aBlob and pParse->nBlob.  pParse->aBlob might be dynamically
** allocated (if pParse->nBlobAlloc is greater than zero) in which case
** the caller is responsible for freeing the space allocated to pParse->aBlob
** when it has finished with it.  Or pParse->aBlob might be a static string
** or a value obtained from sqlite3_value_blob(pArg).
**
** If the argument is a BLOB that is clearly not a JSONB, then this
** function might set an error message in ctx and return non-zero.
** It might also set an error message and return non-zero on an OOM error.
*/
static int jsonFunctionArgToBlob(
  sqlite3_context *ctx,
  sqlite3_value *pArg,
  JsonParse *pParse
){
  int eType = sqlite3_value_type(pArg);
  static u8 aNull[] = { 0x00 };
  memset(pParse, 0, sizeof(pParse[0]));
  pParse->db = sqlite3_context_db_handle(ctx);
  switch( eType ){
    default: {
      pParse->aBlob = aNull;
      pParse->nBlob = 1;
      return 0;
    }

    case SQLITE_BLOB: {
      if( jsonFuncArgMightBeBinary(pArg) ){
        pParse->aBlob = (u8*)sqlite3_value_blob(pArg);
        pParse->nBlob = sqlite3_value_bytes(pArg);
      }else{
        sqlite3_result_error(ctx, "JSON cannot hold BLOB values", -1);
        return 1;
      }
      break;
    }
    case SQLITE_TEXT: {
      const char *zJson = (const char*)sqlite3_value_text(pArg);
      int nJson = sqlite3_value_bytes(pArg);
      if( zJson==0 ) return 1;

      if( sqlite3_value_subtype(pArg)==JSON_SUBTYPE ){
        pParse->zJson = (char*)zJson;
        pParse->nJson = nJson;
        if( jsonConvertTextToBlob(pParse, ctx) ){
          sqlite3_result_error(ctx, "malformed JSON", -1);
          sqlite3DbFree(pParse->db, pParse->aBlob);
          memset(pParse, 0, sizeof(pParse[0]));




          return 1;
        }
      }else{
        jsonBlobAppendNode(pParse, JSONB_TEXTRAW, nJson, zJson);
      }
      break;
    }
    case SQLITE_FLOAT: {
      double r = sqlite3_value_double(pArg);
      if( NEVER(sqlite3IsNaN(r)) ){
        jsonBlobAppendNode(pParse, JSONB_NULL, 0, 0);

      }else{
        int n = sqlite3_value_bytes(pArg);
        const char *z = (const char*)sqlite3_value_text(pArg);
        if( z==0 ) return 1;
        if( z[0]=='I' ){
          jsonBlobAppendNode(pParse, JSONB_FLOAT, 5, "9e999");
        }else if( z[0]=='-' && z[1]=='I' ){
          jsonBlobAppendNode(pParse, JSONB_FLOAT, 6, "-9e999");
        }else{
          jsonBlobAppendNode(pParse, JSONB_FLOAT, n, z);
        }
      }
      break;
    }
    case SQLITE_INTEGER: {
      int n = sqlite3_value_bytes(pArg);
      const char *z = (const char*)sqlite3_value_text(pArg);
      if( z==0 ) return 1;
      jsonBlobAppendNode(pParse, JSONB_INT, n, z);
      break;
    }
  }
  if( pParse->oom ){
    sqlite3_result_error_nomem(ctx);
    return 1;
  }else{
    return 0;
  }
}

/*
** Generate a bad path error.
**
** If ctx is not NULL then push the error message into ctx and return NULL.
** If ctx is NULL, then return the text of the error message.
*/
static char *jsonBadPathError(
  sqlite3_context *ctx,     /* The function call containing the error */
  const char *zPath         /* The path with the problem */
){
  char *zMsg = sqlite3_mprintf("bad JSON path: %Q", zPath);
  if( ctx==0 ) return zMsg;
  if( zMsg ){
    sqlite3_result_error(ctx, zMsg, -1);
    sqlite3_free(zMsg);
  }else{
    sqlite3_result_error_nomem(ctx);
  }
  return 0;
}

/* argv[0] is a BLOB that seems likely to be a JSONB.  Subsequent
** arguments come in parse where each pair contains a JSON path and
** content to insert or set at that patch.  Do the updates
** and return the result.
**
** The specific operation is determined by eEdit, which can be one
** of JEDIT_INS, JEDIT_REPL, or JEDIT_SET.
*/
static void jsonInsertIntoBlob(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv,
  int eEdit                /* JEDIT_INS, JEDIT_REPL, or JEDIT_SET */
){
  int i;
  u32 rc = 0;
  const char *zPath = 0;
  int flgs;
  JsonParse *p;
  JsonParse ax;

  assert( (argc&1)==1 );
  flgs = argc==1 ? 0 : JSON_EDITABLE;
  p = jsonParseFuncArg(ctx, argv[0], flgs);
  if( p==0 ) return;
  for(i=1; i<argc-1; i+=2){
    if( sqlite3_value_type(argv[i])==SQLITE_NULL ) continue;
    zPath = (const char*)sqlite3_value_text(argv[i]);
    if( zPath==0 ){
      sqlite3_result_error_nomem(ctx);
      jsonParseFree(p);
      return;
    }







    if( zPath[0]!='$' ) goto jsonInsertIntoBlob_patherror;
    if( jsonFunctionArgToBlob(ctx, argv[i+1], &ax) ){

      jsonParseReset(&ax);
      jsonParseFree(p);
      return;
    }
    if( zPath[1]==0 ){

      if( eEdit==JEDIT_REPL || eEdit==JEDIT_SET ){
        jsonBlobEdit(p, 0, p->nBlob, ax.aBlob, ax.nBlob);
      }
      rc = 0;
   }else{
      p->eEdit = eEdit;
      p->nIns = ax.nBlob;
      p->aIns = ax.aBlob;
      p->delta = 0;
      rc = jsonLookupStep(p, 0, zPath+1, 0);
    }
    jsonParseReset(&ax);
    if( rc==JSON_LOOKUP_NOTFOUND ) continue;
    if( JSON_LOOKUP_ISERROR(rc) ) goto jsonInsertIntoBlob_patherror;
  }
  jsonReturnParse(ctx, p);
  jsonParseFree(p);
  return;

jsonInsertIntoBlob_patherror:
  jsonParseFree(p);
  if( rc==JSON_LOOKUP_ERROR ){
    sqlite3_result_error(ctx, "malformed JSON", -1);
  }else{
    jsonBadPathError(ctx, zPath);
  }
  return;
}

/*
** If pArg is a blob that seems like a JSONB blob, then initialize
** p to point to that JSONB and return TRUE.  If pArg does not seem like
** a JSONB blob, then return FALSE;
**
** This routine is only called if it is already known that pArg is a
** blob.  The only open question is whether or not the blob appears
** to be a JSONB blob.
*/
static int jsonArgIsJsonb(sqlite3_value *pArg, JsonParse *p){
  u32 n, sz = 0;
  p->aBlob = (u8*)sqlite3_value_blob(pArg);
  p->nBlob = (u32)sqlite3_value_bytes(pArg);
  if( p->nBlob==0 ){
    p->aBlob = 0;
    return 0;
  }
  if( NEVER(p->aBlob==0) ){
    return 0;
  }
  if( (p->aBlob[0] & 0x0f)<=JSONB_OBJECT
   && (n = jsonbPayloadSize(p, 0, &sz))>0
   && sz+n==p->nBlob
   && ((p->aBlob[0] & 0x0f)>JSONB_FALSE || sz==0)
  ){
    return 1;
  }
  p->aBlob = 0;
  p->nBlob = 0;
  return 0;
}

/*
** Generate a JsonParse object, containing valid JSONB in aBlob and nBlob,
** from the SQL function argument pArg.  Return a pointer to the new

** JsonParse object.
**
** Ownership of the new JsonParse object is passed to the caller.  The
** caller should invoke jsonParseFree() on the return value when it
** has finished using it.
**
** If any errors are detected, an appropriate error messages is set
** using sqlite3_result_error() or the equivalent and this routine
** returns NULL.  This routine also returns NULL if the pArg argument
** is an SQL NULL value, but no error message is set in that case.  This
** is so that SQL functions that are given NULL arguments will return
** a NULL value.
*/
static JsonParse *jsonParseFuncArg(
  sqlite3_context *ctx,
  sqlite3_value *pArg,
  u32 flgs
){
  int eType;                   /* Datatype of pArg */
  JsonParse *p = 0;            /* Value to be returned */
  JsonParse *pFromCache = 0;   /* Value taken from cache */
  sqlite3 *db;                 /* The database connection */

  assert( ctx!=0 );
  eType = sqlite3_value_type(pArg);
  if( eType==SQLITE_NULL ){
    return 0;
  }
  pFromCache = jsonCacheSearch(ctx, pArg);
  if( pFromCache ){
    pFromCache->nJPRef++;
    if( (flgs & JSON_EDITABLE)==0 ){
      return pFromCache;
    }
  }
  db = sqlite3_context_db_handle(ctx);
rebuild_from_cache:
  p = sqlite3DbMallocZero(db, sizeof(*p));
  if( p==0 ) goto json_pfa_oom;
  memset(p, 0, sizeof(*p));
  p->db = db;
  p->nJPRef = 1;
  if( pFromCache!=0 ){
    u32 nBlob = pFromCache->nBlob;
    p->aBlob = sqlite3DbMallocRaw(db, nBlob);
    if( p->aBlob==0 ) goto json_pfa_oom;
    memcpy(p->aBlob, pFromCache->aBlob, nBlob);
    p->nBlobAlloc = p->nBlob = nBlob;
    p->hasNonstd = pFromCache->hasNonstd;
    jsonParseFree(pFromCache);
    return p;
  }
  if( eType==SQLITE_BLOB ){
    if( jsonArgIsJsonb(pArg,p) ){
      if( (flgs & JSON_EDITABLE)!=0 && jsonBlobMakeEditable(p, 0)==0 ){
        goto json_pfa_oom;
      }
      return p;
    }
    /* If the blob is not valid JSONB, fall through into trying to cast
    ** the blob into text which is then interpreted as JSON.  (tag-20240123-a)
    **
    ** This goes against all historical documentation about how the SQLite
    ** JSON functions were suppose to work.  From the beginning, blob was
    ** reserved for expansion and a blob value should have raised an error.
    ** But it did not, due to a bug.  And many applications came to depend
    ** upon this buggy behavior, espeically when using the CLI and reading
    ** JSON text using readfile(), which returns a blob.  For this reason
    ** we will continue to support the bug moving forward.

    ** See for example https://sqlite.org/forum/forumpost/012136abd5292b8d
    */








  }
  p->zJson = (char*)sqlite3_value_text(pArg);
  p->nJson = sqlite3_value_bytes(pArg);
  if( p->nJson==0 ) goto json_pfa_malformed;
  if( NEVER(p->zJson==0) ) goto json_pfa_oom;
  if( jsonConvertTextToBlob(p, (flgs & JSON_KEEPERROR) ? 0 : ctx) ){
    if( flgs & JSON_KEEPERROR ){
      p->nErr = 1;
      return p;
    }else{
      jsonParseFree(p);
      return 0;
    }




  }else{
    int isRCStr = sqlite3ValueIsOfClass(pArg, sqlite3RCStrUnref);
    int rc;
    if( !isRCStr ){
      char *zNew = sqlite3RCStrNew( p->nJson );
      if( zNew==0 ) goto json_pfa_oom;
      memcpy(zNew, p->zJson, p->nJson);
      p->zJson = zNew;
      p->zJson[p->nJson] = 0;
    }else{
      sqlite3RCStrRef(p->zJson);
    }
    p->bJsonIsRCStr = 1;
    rc = jsonCacheInsert(ctx, p);
    if( rc==SQLITE_NOMEM ) goto json_pfa_oom;
    if( flgs & JSON_EDITABLE ){
      pFromCache = p;
      p = 0;
      goto rebuild_from_cache;
    }
  }
  return p;


json_pfa_malformed:
  if( flgs & JSON_KEEPERROR ){

    p->nErr = 1;
    return p;
  }else{
    jsonParseFree(p);
    sqlite3_result_error(ctx, "malformed JSON", -1);
    return 0;
  }

json_pfa_oom:
  jsonParseFree(pFromCache);
  jsonParseFree(p);
  sqlite3_result_error_nomem(ctx);
  return 0;
}

/*
** Make the return value of a JSON function either the raw JSONB blob
** or make it JSON text, depending on whether the JSON_BLOB flag is
** set on the function.
*/
static void jsonReturnParse(
  sqlite3_context *ctx,
  JsonParse *p
){
  int flgs;
  if( p->oom ){
    sqlite3_result_error_nomem(ctx);
    return;
  }
  flgs = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
  if( flgs & JSON_BLOB ){
    if( p->nBlobAlloc>0 && !p->bReadOnly ){
      sqlite3_result_blob(ctx, p->aBlob, p->nBlob, SQLITE_DYNAMIC);
      p->nBlobAlloc = 0;
    }else{
      sqlite3_result_blob(ctx, p->aBlob, p->nBlob, SQLITE_TRANSIENT);
    }
  }else{
    JsonString s;
    jsonStringInit(&s, ctx);
    p->delta = 0;
    jsonTranslateBlobToText(p, 0, &s);
    jsonReturnString(&s, p, ctx);
    sqlite3_result_subtype(ctx, JSON_SUBTYPE);
  }
}

/****************************************************************************
** SQL functions used for testing and debugging
****************************************************************************/

#if SQLITE_DEBUG
/*
** Decode JSONB bytes in aBlob[] starting at iStart through but not
** including iEnd.  Indent the
** content by nIndent spaces.
*/
static void jsonDebugPrintBlob(


  JsonParse *pParse, /* JSON content */
  u32 iStart,        /* Start rendering here */
  u32 iEnd,          /* Do not render this byte or any byte after this one */
  int nIndent,       /* Indent by this many spaces */
  sqlite3_str *pOut  /* Generate output into this sqlite3_str object */
){
  while( iStart<iEnd ){
    u32 i, n, nn, sz = 0;
    int showContent = 1;
    u8 x = pParse->aBlob[iStart] & 0x0f;
    u32 savedNBlob = pParse->nBlob;
    sqlite3_str_appendf(pOut, "%5d:%*s", iStart, nIndent, "");
    if( pParse->nBlobAlloc>pParse->nBlob ){
      pParse->nBlob = pParse->nBlobAlloc;
    }
    nn = n = jsonbPayloadSize(pParse, iStart, &sz);
    if( nn==0 ) nn = 1;
    if( sz>0 && x<JSONB_ARRAY ){
      nn += sz;
    }
    for(i=0; i<nn; i++){





      sqlite3_str_appendf(pOut, " %02x", pParse->aBlob[iStart+i]);
    }

    if( n==0 ){
      sqlite3_str_appendf(pOut, "   ERROR invalid node size\n");
      iStart = n==0 ? iStart+1 : iEnd;
      continue;





    }
    pParse->nBlob = savedNBlob;
    if( iStart+n+sz>iEnd ){
      iEnd = iStart+n+sz;
      if( iEnd>pParse->nBlob ){
        if( pParse->nBlobAlloc>0 && iEnd>pParse->nBlobAlloc ){
          iEnd = pParse->nBlobAlloc;
        }else{




          iEnd = pParse->nBlob;
        }
      }
    }
    sqlite3_str_appendall(pOut,"  <-- ");
    switch( x ){
      case JSONB_NULL:     sqlite3_str_appendall(pOut,"null"); break;
      case JSONB_TRUE:     sqlite3_str_appendall(pOut,"true"); break;
      case JSONB_FALSE:    sqlite3_str_appendall(pOut,"false"); break;
      case JSONB_INT:      sqlite3_str_appendall(pOut,"int"); break;
      case JSONB_INT5:     sqlite3_str_appendall(pOut,"int5"); break;
      case JSONB_FLOAT:    sqlite3_str_appendall(pOut,"float"); break;
      case JSONB_FLOAT5:   sqlite3_str_appendall(pOut,"float5"); break;
      case JSONB_TEXT:     sqlite3_str_appendall(pOut,"text"); break;
      case JSONB_TEXTJ:    sqlite3_str_appendall(pOut,"textj"); break;
      case JSONB_TEXT5:    sqlite3_str_appendall(pOut,"text5"); break;
      case JSONB_TEXTRAW:  sqlite3_str_appendall(pOut,"textraw"); break;
      case JSONB_ARRAY: {
        sqlite3_str_appendf(pOut,"array, %u bytes\n", sz);
        jsonDebugPrintBlob(pParse, iStart+n, iStart+n+sz, nIndent+2, pOut);
        showContent = 0;
        break;
      }
      case JSONB_OBJECT: {
        sqlite3_str_appendf(pOut, "object, %u bytes\n", sz);
        jsonDebugPrintBlob(pParse, iStart+n, iStart+n+sz, nIndent+2, pOut);
        showContent = 0;
        break;
      }



      default: {
        sqlite3_str_appendall(pOut, "ERROR: unknown node type\n");
        showContent = 0;
        break;
      }


    }
    if( showContent ){
      if( sz==0 && x<=JSONB_FALSE ){
        sqlite3_str_append(pOut, "\n", 1);
      }else{
        u32 i;
        sqlite3_str_appendall(pOut, ": \"");
        for(i=iStart+n; i<iStart+n+sz; i++){
          u8 c = pParse->aBlob[i];
          if( c<0x20 || c>=0x7f ) c = '.';
          sqlite3_str_append(pOut, (char*)&c, 1);
        }
        sqlite3_str_append(pOut, "\"\n", 2);
      }
    }
    iStart += n + sz;
  }
}
static void jsonShowParse(JsonParse *pParse){
  sqlite3_str out;
  char zBuf[1000];
  if( pParse==0 ){
    printf("NULL pointer\n");
    return;
  }else{
    printf("nBlobAlloc = %u\n", pParse->nBlobAlloc);
    printf("nBlob = %u\n", pParse->nBlob);
    printf("delta = %d\n", pParse->delta);
    if( pParse->nBlob==0 ) return;
    printf("content (bytes 0..%u):\n", pParse->nBlob-1);
  }
  sqlite3StrAccumInit(&out, 0, zBuf, sizeof(zBuf), 1000000);
  jsonDebugPrintBlob(pParse, 0, pParse->nBlob, 0, &out);
  printf("%s", sqlite3_str_value(&out));
  sqlite3_str_reset(&out);
}
#endif /* SQLITE_DEBUG */

#ifdef SQLITE_DEBUG
/*
** SQL function:   json_parse(JSON)
**
** Parse JSON using jsonParseFuncArg().  Return text that is a
** human-readable dump of the binary JSONB for the input parameter.

*/
static void jsonParseFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  JsonParse *p;        /* The parse */
  sqlite3_str out;

  assert( argc>=1 );
  sqlite3StrAccumInit(&out, 0, 0, 0, 1000000);
  p = jsonParseFuncArg(ctx, argv[0], 0);
  if( p==0 ) return;


  if( argc==1 ){









    jsonDebugPrintBlob(p, 0, p->nBlob, 0, &out);

    sqlite3_result_text64(ctx, out.zText, out.nChar, SQLITE_DYNAMIC, SQLITE_UTF8);
  }else{
    jsonShowParse(p);
  }





  jsonParseFree(p);






}
#endif /* SQLITE_DEBUG */

/****************************************************************************
** Scalar SQL function implementations
****************************************************************************/

/*
** Implementation of the json_quote(VALUE) function.  Return a JSON value
** corresponding to the SQL value input.  Mostly this means putting
** double-quotes around strings and returning the unquoted string "null"
** when given a NULL input.
*/
static void jsonQuoteFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  JsonString jx;
  UNUSED_PARAMETER(argc);

  jsonStringInit(&jx, ctx);
  jsonAppendSqlValue(&jx, argv[0]);
  jsonReturnString(&jx, 0, 0);
  sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}

/*
** Implementation of the json_array(VALUE,...) function.  Return a JSON
** array that contains all values given in arguments.  Or if any argument
** is a BLOB, throw an error.
*/
static void jsonArrayFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  int i;
  JsonString jx;

  jsonStringInit(&jx, ctx);
  jsonAppendChar(&jx, '[');
  for(i=0; i<argc; i++){
    jsonAppendSeparator(&jx);
    jsonAppendSqlValue(&jx, argv[i]);
  }
  jsonAppendChar(&jx, ']');
  jsonReturnString(&jx, 0, 0);
  sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}


/*
** json_array_length(JSON)
** json_array_length(JSON, PATH)
**
** Return the number of elements in the top-level JSON array.
** Return 0 if the input is not a well-formed JSON array.
*/
static void jsonArrayLengthFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  JsonParse *p;          /* The parse */
  sqlite3_int64 cnt = 0;
  u32 i;
  u8 eErr = 0;

  p = jsonParseFuncArg(ctx, argv[0], 0);
  if( p==0 ) return;

  if( argc==2 ){
    const char *zPath = (const char*)sqlite3_value_text(argv[1]);
    if( zPath==0 ){
      jsonParseFree(p);
      return;
    }
    i = jsonLookupStep(p, 0, zPath[0]=='$' ? zPath+1 : "@", 0);
    if( JSON_LOOKUP_ISERROR(i) ){
      if( i==JSON_LOOKUP_NOTFOUND ){
        /* no-op */
      }else if( i==JSON_LOOKUP_PATHERROR ){
        jsonBadPathError(ctx, zPath);
      }else{
        sqlite3_result_error(ctx, "malformed JSON", -1);
      }


      eErr = 1;


      i = 0;
    }


  }else{
    i = 0;
  }
  if( (p->aBlob[i] & 0x0f)==JSONB_ARRAY ){
    cnt = jsonbArrayCount(p, i);
  }
  if( !eErr ) sqlite3_result_int64(ctx, cnt);
  jsonParseFree(p);
}

/* True if the string is all digits */

static int jsonAllDigits(const char *z, int n){


  int i;
  for(i=0; i<n && sqlite3Isdigit(z[i]); i++){}
  return i==n;
}

/* True if the string is all alphanumerics and underscores */
static int jsonAllAlphanum(const char *z, int n){
  int i;
  for(i=0; i<n && (sqlite3Isalnum(z[i]) || z[i]=='_'); i++){}
  return i==n;
}

/*
** json_extract(JSON, PATH, ...)
** "->"(JSON,PATH)
** "->>"(JSON,PATH)
**
** Return the element described by PATH.  Return NULL if that PATH element
205177
205178
205179
205180
205181
205182
205183
205184
205185
205186
205187

205188
205189
205190
205191
205192


205193



205194
205195


205196



205197
205198
205199
205200
205201
205202
205203
205204
205205
205206
205207
205208
205209
205210
205211





205212
205213
205214
205215
205216

205217
205218
205219
205220

205221

205222
205223


205224



205225
205226




205227
205228
205229
205230
205231

205232



205233
205234
205235
205236
205237
205238
205239
205240
205241
205242
205243


205244

205245
205246

205247
205248
205249
205250
205251
205252

205253
205254


205255


205256
205257







205258

205259
205260





205261
205262







205263

205264
205265
205266



205267

205268
205269
205270
205271











205272


205273



205274

205275
205276
205277














205278
205279
205280



205281





205282
205283
205284
205285




205286
205287
205288
205289
205290
205291

205292
205293
205294
205295
205296
205297
205298
205299
205300
205301






205302

205303




205304
205305








205306
205307
205308
205309

205310




205311


205312















205313


205314








205315
205316








205317



205318
205319
205320





205321
205322


205323
205324
205325


205326
205327

205328
205329
205330
205331
205332
205333
205334
205335
205336
205337
205338
205339
205340
205341
205342
205343

205344
205345
205346
205347
205348
205349
205350
205351
205352
205353
205354
205355

205356
205357
205358
205359
205360



205361
205362
205363
205364
205365
205366
205367
** compatibility with PG.
*/
static void jsonExtractFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  JsonParse *p;          /* The parse */
  JsonNode *pNode;
  const char *zPath;
  int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));

  JsonString jx;

  if( argc<2 ) return;
  p = jsonParseCached(ctx, argv[0], ctx, 0);
  if( p==0 ) return;


  if( argc==2 ){



    /* With a single PATH argument */
    zPath = (const char*)sqlite3_value_text(argv[1]);


    if( zPath==0 ) return;



    if( flags & JSON_ABPATH ){
      if( zPath[0]!='$' || (zPath[1]!='.' && zPath[1]!='[' && zPath[1]!=0) ){
        /* The -> and ->> operators accept abbreviated PATH arguments.  This
        ** is mostly for compatibility with PostgreSQL, but also for
        ** convenience.
        **
        **     NUMBER   ==>  $[NUMBER]     // PG compatible
        **     LABEL    ==>  $.LABEL       // PG compatible
        **     [NUMBER] ==>  $[NUMBER]     // Not PG.  Purely for convenience
        */
        jsonInit(&jx, ctx);
        if( sqlite3Isdigit(zPath[0]) ){
          jsonAppendRawNZ(&jx, "$[", 2);
          jsonAppendRaw(&jx, zPath, (int)strlen(zPath));
          jsonAppendRawNZ(&jx, "]", 2);





        }else{
          jsonAppendRawNZ(&jx, "$.", 1 + (zPath[0]!='['));
          jsonAppendRaw(&jx, zPath, (int)strlen(zPath));
          jsonAppendChar(&jx, 0);
        }

        pNode = jx.bErr ? 0 : jsonLookup(p, jx.zBuf, 0, ctx);
        jsonReset(&jx);
      }else{
        pNode = jsonLookup(p, zPath, 0, ctx);

      }

      if( pNode ){
        if( flags & JSON_JSON ){


          jsonReturnJson(p, pNode, ctx, 0, 0);



        }else{
          jsonReturn(p, pNode, ctx, 1);




        }
      }
    }else{
      pNode = jsonLookup(p, zPath, 0, ctx);
      if( p->nErr==0 && pNode ) jsonReturn(p, pNode, ctx, 0);

    }



  }else{
    /* Two or more PATH arguments results in a JSON array with each
    ** element of the array being the value selected by one of the PATHs */
    int i;
    jsonInit(&jx, ctx);
    jsonAppendChar(&jx, '[');
    for(i=1; i<argc; i++){
      zPath = (const char*)sqlite3_value_text(argv[i]);
      pNode = jsonLookup(p, zPath, 0, ctx);
      if( p->nErr ) break;
      jsonAppendSeparator(&jx);


      if( pNode ){

        jsonRenderNode(p, pNode, &jx);
      }else{

        jsonAppendRawNZ(&jx, "null", 4);
      }
    }
    if( i==argc ){
      jsonAppendChar(&jx, ']');
      jsonResult(&jx);

      sqlite3_result_subtype(ctx, JSON_SUBTYPE);
    }


    jsonReset(&jx);


  }
}









/* This is the RFC 7396 MergePatch algorithm.
*/





static JsonNode *jsonMergePatch(
  JsonParse *pParse,   /* The JSON parser that contains the TARGET */







  u32 iTarget,         /* Node of the TARGET in pParse */

  JsonNode *pPatch     /* The PATCH */
){
  u32 i, j;



  u32 iRoot;

  JsonNode *pTarget;
  if( pPatch->eType!=JSON_OBJECT ){
    return pPatch;
  }











  assert( iTarget<pParse->nNode );


  pTarget = &pParse->aNode[iTarget];



  assert( (pPatch->jnFlags & JNODE_APPEND)==0 );

  if( pTarget->eType!=JSON_OBJECT ){
    jsonRemoveAllNulls(pPatch);
    return pPatch;














  }
  iRoot = iTarget;
  for(i=1; i<pPatch->n; i += jsonNodeSize(&pPatch[i+1])+1){



    u32 nKey;





    const char *zKey;
    assert( pPatch[i].eType==JSON_STRING );
    assert( pPatch[i].jnFlags & JNODE_LABEL );
    assert( pPatch[i].eU==1 );




    nKey = pPatch[i].n;
    zKey = pPatch[i].u.zJContent;
    for(j=1; j<pTarget->n; j += jsonNodeSize(&pTarget[j+1])+1 ){
      assert( pTarget[j].eType==JSON_STRING );
      assert( pTarget[j].jnFlags & JNODE_LABEL );
      if( jsonSameLabel(&pPatch[i], &pTarget[j]) ){

        if( pTarget[j+1].jnFlags & (JNODE_REMOVE|JNODE_REPLACE) ) break;
        if( pPatch[i+1].eType==JSON_NULL ){
          pTarget[j+1].jnFlags |= JNODE_REMOVE;
        }else{
          JsonNode *pNew = jsonMergePatch(pParse, iTarget+j+1, &pPatch[i+1]);
          if( pNew==0 ) return 0;
          if( pNew!=&pParse->aNode[iTarget+j+1] ){
            jsonParseAddSubstNode(pParse, iTarget+j+1);
            jsonParseAddNodeArray(pParse, pNew, jsonNodeSize(pNew));
          }






          pTarget = &pParse->aNode[iTarget];

        }




        break;
      }








    }
    if( j>=pTarget->n && pPatch[i+1].eType!=JSON_NULL ){
      int iStart;
      JsonNode *pApnd;

      u32 nApnd;




      iStart = jsonParseAddNode(pParse, JSON_OBJECT, 0, 0);


      jsonParseAddNode(pParse, JSON_STRING, nKey, zKey);















      pApnd = &pPatch[i+1];


      if( pApnd->eType==JSON_OBJECT ) jsonRemoveAllNulls(pApnd);








      nApnd = jsonNodeSize(pApnd);
      jsonParseAddNodeArray(pParse, pApnd, jsonNodeSize(pApnd));








      if( pParse->oom ) return 0;



      pParse->aNode[iStart].n = 1+nApnd;
      pParse->aNode[iRoot].jnFlags |= JNODE_APPEND;
      pParse->aNode[iRoot].u.iAppend = iStart;





      VVA( pParse->aNode[iRoot].eU = 2 );
      iRoot = iStart;


      pTarget = &pParse->aNode[iTarget];
    }
  }


  return pTarget;
}


/*
** Implementation of the json_mergepatch(JSON1,JSON2) function.  Return a JSON
** object that is the result of running the RFC 7396 MergePatch() algorithm
** on the two arguments.
*/
static void jsonPatchFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  JsonParse *pX;     /* The JSON that is being patched */
  JsonParse *pY;     /* The patch */
  JsonNode *pResult;   /* The result of the merge */

  UNUSED_PARAMETER(argc);

  pX = jsonParseCached(ctx, argv[0], ctx, 1);
  if( pX==0 ) return;
  assert( pX->hasMod==0 );
  pX->hasMod = 1;
  pY = jsonParseCached(ctx, argv[1], ctx, 1);
  if( pY==0 ) return;
  pX->useMod = 1;
  pY->useMod = 1;
  pResult = jsonMergePatch(pX, 0, pY->aNode);
  assert( pResult!=0 || pX->oom );
  if( pResult && pX->oom==0 ){
    jsonDebugPrintParse(pX);

    jsonDebugPrintNode(pResult);
    jsonReturnJson(pX, pResult, ctx, 0, 0);
  }else{
    sqlite3_result_error_nomem(ctx);
  }



}


/*
** Implementation of the json_object(NAME,VALUE,...) function.  Return a JSON
** object that contains all name/value given in arguments.  Or if any name
** is not a string or if any value is a BLOB, throw an error.







|
<
<
|
>
|


|

>
>
|
>
>
>

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

>
>
|
>
>
>

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


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

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

>











|
|
|


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







206867
206868
206869
206870
206871
206872
206873
206874


206875
206876
206877
206878
206879
206880
206881
206882
206883
206884
206885
206886
206887
206888
206889
206890
206891
206892
206893
206894
206895
206896

206897
206898
206899
206900
206901
206902
206903
206904
206905
206906
206907
206908
206909
206910
206911
206912
206913
206914
206915
206916
206917
206918
206919
206920
206921
206922
206923
206924
206925
206926
206927
206928
206929
206930
206931
206932
206933
206934
206935
206936
206937
206938
206939
206940
206941
206942
206943
206944
206945

206946
206947
206948
206949
206950
206951









206952
206953
206954
206955
206956
206957
206958
206959
206960
206961
206962
206963
206964
206965
206966
206967
206968
206969
206970
206971
206972
206973
206974
206975
206976
206977
206978
206979
206980
206981
206982
206983
206984
206985
206986
206987
206988
206989
206990
206991
206992

206993
206994
206995
206996
206997
206998
206999
207000
207001
207002

207003
207004
207005
207006
207007
207008
207009
207010
207011

207012
207013
207014
207015
207016
207017
207018
207019
207020
207021
207022
207023
207024
207025
207026
207027
207028
207029
207030
207031
207032
207033
207034
207035
207036
207037
207038
207039
207040
207041
207042
207043
207044
207045
207046
207047
207048
207049


207050
207051
207052
207053
207054
207055
207056
207057
207058
207059
207060
207061
207062
207063
207064
207065
207066
207067
207068
207069
207070
207071
207072
207073
207074
207075
207076

207077

207078
207079

207080
207081
207082
207083
207084
207085
207086
207087
207088
207089
207090
207091
207092
207093
207094
207095
207096
207097
207098
207099
207100
207101
207102
207103
207104

207105

207106
207107
207108
207109
207110
207111
207112
207113
207114
207115
207116
207117
207118
207119
207120
207121
207122
207123
207124
207125
207126
207127
207128
207129
207130
207131
207132
207133
207134
207135
207136
207137
207138
207139
207140
207141
207142
207143

207144
207145
207146
207147
207148
207149
207150
207151
207152
207153
207154
207155
207156


207157
207158
207159
207160
207161
207162
207163
207164
207165
207166
207167
207168
207169
207170
207171
207172
207173
207174
207175
207176
207177
207178
207179
207180
207181
207182
207183
207184
207185
207186
207187
207188
207189
207190
207191
207192


207193
207194


207195

207196
207197
207198
207199

207200
207201
207202
207203
207204
207205
207206
207207
207208
207209
207210
207211
207212
** compatibility with PG.
*/
static void jsonExtractFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  JsonParse *p = 0;      /* The parse */


  int flags;             /* Flags associated with the function */
  int i;                 /* Loop counter */
  JsonString jx;         /* String for array result */

  if( argc<2 ) return;
  p = jsonParseFuncArg(ctx, argv[0], 0);
  if( p==0 ) return;
  flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
  jsonStringInit(&jx, ctx);
  if( argc>2 ){
    jsonAppendChar(&jx, '[');
  }
  for(i=1; i<argc; i++){
    /* With a single PATH argument */
    const char *zPath = (const char*)sqlite3_value_text(argv[i]);
    int nPath;
    u32 j;
    if( zPath==0 ) goto json_extract_error;
    nPath = sqlite3Strlen30(zPath);
    if( zPath[0]=='$' ){
      j = jsonLookupStep(p, 0, zPath+1, 0);
    }else if( (flags & JSON_ABPATH) ){

      /* The -> and ->> operators accept abbreviated PATH arguments.  This
      ** is mostly for compatibility with PostgreSQL, but also for
      ** convenience.
      **
      **     NUMBER   ==>  $[NUMBER]     // PG compatible
      **     LABEL    ==>  $.LABEL       // PG compatible
      **     [NUMBER] ==>  $[NUMBER]     // Not PG.  Purely for convenience
      */
      jsonStringInit(&jx, ctx);
      if( jsonAllDigits(zPath, nPath) ){
        jsonAppendRawNZ(&jx, "[", 1);
        jsonAppendRaw(&jx, zPath, nPath);
        jsonAppendRawNZ(&jx, "]", 2);
      }else if( jsonAllAlphanum(zPath, nPath) ){
        jsonAppendRawNZ(&jx, ".", 1);
        jsonAppendRaw(&jx, zPath, nPath);
      }else if( zPath[0]=='[' && nPath>=3 && zPath[nPath-1]==']' ){
        jsonAppendRaw(&jx, zPath, nPath);
      }else{
        jsonAppendRawNZ(&jx, ".\"", 2);
        jsonAppendRaw(&jx, zPath, nPath);
        jsonAppendRawNZ(&jx, "\"", 1);
      }
      jsonStringTerminate(&jx);
      j = jsonLookupStep(p, 0, jx.zBuf, 0);
      jsonStringReset(&jx);
    }else{
      jsonBadPathError(ctx, zPath);
      goto json_extract_error;
    }
    if( j<p->nBlob ){
      if( argc==2 ){
        if( flags & JSON_JSON ){
          jsonStringInit(&jx, ctx);
          jsonTranslateBlobToText(p, j, &jx);
          jsonReturnString(&jx, 0, 0);
          jsonStringReset(&jx);
          assert( (flags & JSON_BLOB)==0 );
          sqlite3_result_subtype(ctx, JSON_SUBTYPE);
        }else{
          jsonReturnFromBlob(p, j, ctx, 0);
          if( (flags & (JSON_SQL|JSON_BLOB))==0
           && (p->aBlob[j]&0x0f)>=JSONB_ARRAY
          ){
            sqlite3_result_subtype(ctx, JSON_SUBTYPE);
          }
        }
      }else{
        jsonAppendSeparator(&jx);

        jsonTranslateBlobToText(p, j, &jx);
      }
    }else if( j==JSON_LOOKUP_NOTFOUND ){
      if( argc==2 ){
        goto json_extract_error;  /* Return NULL if not found */
      }else{









        jsonAppendSeparator(&jx);
        jsonAppendRawNZ(&jx, "null", 4);
      }
    }else if( j==JSON_LOOKUP_ERROR ){
      sqlite3_result_error(ctx, "malformed JSON", -1);
      goto json_extract_error;
    }else{
      jsonBadPathError(ctx, zPath);
      goto json_extract_error;
    }
  }
  if( argc>2 ){
    jsonAppendChar(&jx, ']');
    jsonReturnString(&jx, 0, 0);
    if( (flags & JSON_BLOB)==0 ){
      sqlite3_result_subtype(ctx, JSON_SUBTYPE);
    }
  }
json_extract_error:
  jsonStringReset(&jx);
  jsonParseFree(p);
  return;
}

/*
** Return codes for jsonMergePatch()
*/
#define JSON_MERGE_OK          0     /* Success */
#define JSON_MERGE_BADTARGET   1     /* Malformed TARGET blob */
#define JSON_MERGE_BADPATCH    2     /* Malformed PATCH blob */
#define JSON_MERGE_OOM         3     /* Out-of-memory condition */

/*
** RFC-7396 MergePatch for two JSONB blobs.
**
** pTarget is the target. pPatch is the patch.  The target is updated
** in place.  The patch is read-only.
**
** The original RFC-7396 algorithm is this:
**
**   define MergePatch(Target, Patch):

**     if Patch is an Object:
**       if Target is not an Object:
**         Target = {} # Ignore the contents and set it to an empty Object
**     for each Name/Value pair in Patch:
**         if Value is null:
**           if Name exists in Target:
**             remove the Name/Value pair from Target
**         else:
**           Target[Name] = MergePatch(Target[Name], Value)
**       return Target

**     else:
**       return Patch
**
** Here is an equivalent algorithm restructured to show the actual
** implementation:
**
** 01   define MergePatch(Target, Patch):
** 02      if Patch is not an Object:
** 03         return Patch

** 04      else: // if Patch is an Object
** 05         if Target is not an Object:
** 06            Target = {}
** 07      for each Name/Value pair in Patch:
** 08         if Name exists in Target:
** 09            if Value is null:
** 10               remove the Name/Value pair from Target
** 11            else
** 12               Target[name] = MergePatch(Target[Name], Value)
** 13         else if Value is not NULL:
** 14            if Value is not an Object:
** 15               Target[name] = Value
** 16            else:
** 17               Target[name] = MergePatch('{}',value)
** 18      return Target
**  |
**  ^---- Line numbers referenced in comments in the implementation
*/
static int jsonMergePatch(
  JsonParse *pTarget,      /* The JSON parser that contains the TARGET */
  u32 iTarget,             /* Index of TARGET in pTarget->aBlob[] */
  const JsonParse *pPatch, /* The PATCH */
  u32 iPatch               /* Index of PATCH in pPatch->aBlob[] */
){
  u8 x;             /* Type of a single node */
  u32 n, sz=0;      /* Return values from jsonbPayloadSize() */
  u32 iTCursor;     /* Cursor position while scanning the target object */
  u32 iTStart;      /* First label in the target object */
  u32 iTEndBE;      /* Original first byte past end of target, before edit */
  u32 iTEnd;        /* Current first byte past end of target */
  u8 eTLabel;       /* Node type of the target label */
  u32 iTLabel = 0;  /* Index of the label */
  u32 nTLabel = 0;  /* Header size in bytes for the target label */
  u32 szTLabel = 0; /* Size of the target label payload */
  u32 iTValue = 0;  /* Index of the target value */
  u32 nTValue = 0;  /* Header size of the target value */
  u32 szTValue = 0; /* Payload size for the target value */



  u32 iPCursor;     /* Cursor position while scanning the patch */
  u32 iPEnd;        /* First byte past the end of the patch */
  u8 ePLabel;       /* Node type of the patch label */
  u32 iPLabel;      /* Start of patch label */
  u32 nPLabel;      /* Size of header on the patch label */
  u32 szPLabel;     /* Payload size of the patch label */
  u32 iPValue;      /* Start of patch value */
  u32 nPValue;      /* Header size for the patch value */
  u32 szPValue;     /* Payload size of the patch value */

  assert( iTarget>=0 && iTarget<pTarget->nBlob );
  assert( iPatch>=0 && iPatch<pPatch->nBlob );
  x = pPatch->aBlob[iPatch] & 0x0f;
  if( x!=JSONB_OBJECT ){  /* Algorithm line 02 */
    u32 szPatch;        /* Total size of the patch, header+payload */
    u32 szTarget;       /* Total size of the target, header+payload */
    n = jsonbPayloadSize(pPatch, iPatch, &sz);
    szPatch = n+sz;
    sz = 0;
    n = jsonbPayloadSize(pTarget, iTarget, &sz);
    szTarget = n+sz;
    jsonBlobEdit(pTarget, iTarget, szTarget, pPatch->aBlob+iPatch, szPatch);
    return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK;  /* Line 03 */
  }
  x = pTarget->aBlob[iTarget] & 0x0f;
  if( x!=JSONB_OBJECT ){  /* Algorithm line 05 */
    n = jsonbPayloadSize(pTarget, iTarget, &sz);

    jsonBlobEdit(pTarget, iTarget+n, sz, 0, 0);

    x = pTarget->aBlob[iTarget];
    pTarget->aBlob[iTarget] = (x & 0xf0) | JSONB_OBJECT;

  }
  n = jsonbPayloadSize(pPatch, iPatch, &sz);
  if( NEVER(n==0) ) return JSON_MERGE_BADPATCH;
  iPCursor = iPatch+n;
  iPEnd = iPCursor+sz;
  n = jsonbPayloadSize(pTarget, iTarget, &sz);
  if( NEVER(n==0) ) return JSON_MERGE_BADTARGET;
  iTStart = iTarget+n;
  iTEndBE = iTStart+sz;

  while( iPCursor<iPEnd ){  /* Algorithm line 07 */
    iPLabel = iPCursor;
    ePLabel = pPatch->aBlob[iPCursor] & 0x0f;
    if( ePLabel<JSONB_TEXT || ePLabel>JSONB_TEXTRAW ){
      return JSON_MERGE_BADPATCH;
    }
    nPLabel = jsonbPayloadSize(pPatch, iPCursor, &szPLabel);
    if( nPLabel==0 ) return JSON_MERGE_BADPATCH;
    iPValue = iPCursor + nPLabel + szPLabel;
    if( iPValue>=iPEnd ) return JSON_MERGE_BADPATCH;
    nPValue = jsonbPayloadSize(pPatch, iPValue, &szPValue);
    if( nPValue==0 ) return JSON_MERGE_BADPATCH;
    iPCursor = iPValue + nPValue + szPValue;
    if( iPCursor>iPEnd ) return JSON_MERGE_BADPATCH;


    iTCursor = iTStart;

    iTEnd = iTEndBE + pTarget->delta;
    while( iTCursor<iTEnd ){
      int isEqual;   /* true if the patch and target labels match */
      iTLabel = iTCursor;
      eTLabel = pTarget->aBlob[iTCursor] & 0x0f;
      if( eTLabel<JSONB_TEXT || eTLabel>JSONB_TEXTRAW ){
        return JSON_MERGE_BADTARGET;
      }
      nTLabel = jsonbPayloadSize(pTarget, iTCursor, &szTLabel);
      if( nTLabel==0 ) return JSON_MERGE_BADTARGET;
      iTValue = iTLabel + nTLabel + szTLabel;
      if( iTValue>=iTEnd ) return JSON_MERGE_BADTARGET;
      nTValue = jsonbPayloadSize(pTarget, iTValue, &szTValue);
      if( nTValue==0 ) return JSON_MERGE_BADTARGET;
      if( iTValue + nTValue + szTValue > iTEnd ) return JSON_MERGE_BADTARGET;
      isEqual = jsonLabelCompare(
                   (const char*)&pPatch->aBlob[iPLabel+nPLabel],
                   szPLabel,
                   (ePLabel==JSONB_TEXT || ePLabel==JSONB_TEXTRAW),
                   (const char*)&pTarget->aBlob[iTLabel+nTLabel],
                   szTLabel,
                   (eTLabel==JSONB_TEXT || eTLabel==JSONB_TEXTRAW));
      if( isEqual ) break;
      iTCursor = iTValue + nTValue + szTValue;
    }
    x = pPatch->aBlob[iPValue] & 0x0f;
    if( iTCursor<iTEnd ){
      /* A match was found.  Algorithm line 08 */
      if( x==0 ){
        /* Patch value is NULL.  Algorithm line 09 */
        jsonBlobEdit(pTarget, iTLabel, nTLabel+szTLabel+nTValue+szTValue, 0,0);
        /*  vvvvvv----- No OOM on a delete-only edit */
        if( NEVER(pTarget->oom) ) return JSON_MERGE_OOM;
      }else{
        /* Algorithm line 12 */
        int rc, savedDelta = pTarget->delta;
        pTarget->delta = 0;
        rc = jsonMergePatch(pTarget, iTValue, pPatch, iPValue);

        if( rc ) return rc;
        pTarget->delta += savedDelta;
      }
    }else if( x>0 ){  /* Algorithm line 13 */
      /* No match and patch value is not NULL */
      u32 szNew = szPLabel+nPLabel;
      if( (pPatch->aBlob[iPValue] & 0x0f)!=JSONB_OBJECT ){  /* Line 14 */
        jsonBlobEdit(pTarget, iTEnd, 0, 0, szPValue+nPValue+szNew);
        if( pTarget->oom ) return JSON_MERGE_OOM;
        memcpy(&pTarget->aBlob[iTEnd], &pPatch->aBlob[iPLabel], szNew);
        memcpy(&pTarget->aBlob[iTEnd+szNew],
               &pPatch->aBlob[iPValue], szPValue+nPValue);
      }else{


        int rc, savedDelta;
        jsonBlobEdit(pTarget, iTEnd, 0, 0, szNew+1);
        if( pTarget->oom ) return JSON_MERGE_OOM;
        memcpy(&pTarget->aBlob[iTEnd], &pPatch->aBlob[iPLabel], szNew);
        pTarget->aBlob[iTEnd+szNew] = 0x00;
        savedDelta = pTarget->delta;
        pTarget->delta = 0;
        rc = jsonMergePatch(pTarget, iTEnd+szNew,pPatch,iPValue);
        if( rc ) return rc;
        pTarget->delta += savedDelta;
      }
    }
  }
  if( pTarget->delta ) jsonAfterEditSizeAdjust(pTarget, iTarget);
  return pTarget->oom ? JSON_MERGE_OOM : JSON_MERGE_OK;
}


/*
** Implementation of the json_mergepatch(JSON1,JSON2) function.  Return a JSON
** object that is the result of running the RFC 7396 MergePatch() algorithm
** on the two arguments.
*/
static void jsonPatchFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  JsonParse *pTarget;    /* The TARGET */
  JsonParse *pPatch;     /* The PATCH */
  int rc;                /* Result code */

  UNUSED_PARAMETER(argc);
  assert( argc==2 );
  pTarget = jsonParseFuncArg(ctx, argv[0], JSON_EDITABLE);
  if( pTarget==0 ) return;


  pPatch = jsonParseFuncArg(ctx, argv[1], 0);
  if( pPatch ){


    rc = jsonMergePatch(pTarget, 0, pPatch, 0);

    if( rc==JSON_MERGE_OK ){
      jsonReturnParse(ctx, pTarget);
    }else if( rc==JSON_MERGE_OOM ){
      sqlite3_result_error_nomem(ctx);

    }else{
      sqlite3_result_error(ctx, "malformed JSON", -1);
    }
    jsonParseFree(pPatch);
  }
  jsonParseFree(pTarget);
}


/*
** Implementation of the json_object(NAME,VALUE,...) function.  Return a JSON
** object that contains all name/value given in arguments.  Or if any name
** is not a string or if any value is a BLOB, throw an error.
205377
205378
205379
205380
205381
205382
205383
205384
205385
205386
205387
205388
205389
205390
205391
205392
205393
205394
205395
205396
205397
205398
205399
205400
205401
205402
205403
205404
205405
205406
205407
205408
205409
205410
205411
205412
205413
205414
205415
205416
205417
205418

205419
205420
205421
205422
205423
205424
205425
205426
205427
205428
205429
205430
205431
205432
205433
205434
205435
205436
205437


205438
205439
205440
205441
205442
205443
205444
205445
205446
205447
205448
205449
205450
205451
205452
205453
205454
205455
205456
205457
205458
205459
205460
205461
205462
205463
205464
205465
205466
205467
205468
205469
205470
205471
205472
205473
205474
205475
205476
205477
205478
205479
205480
205481
205482
205483
205484
205485
205486
205487
205488
205489
205490
205491
205492
205493
205494
205495
205496
205497
205498
205499
205500
205501
205502
205503
205504
205505
205506
205507
205508
205509
205510
205511
205512
205513
205514
205515
205516
205517
205518
205519
205520
205521
205522
205523
205524
205525
205526
205527
205528
205529
205530
205531
205532
205533
205534
205535
205536
205537
205538
205539
205540
205541
205542
205543
205544
205545
205546
205547
205548
205549
205550
205551
205552
205553
205554
205555
205556
205557
205558
205559
205560
205561
205562
205563
205564
205565
205566
205567
205568
205569
205570
205571
205572
205573
205574
205575
205576
205577
205578
205579
205580
205581
205582
205583
205584
205585
205586
205587
205588
205589
205590
205591
205592
205593
205594
205595
205596
205597
205598
205599
205600
205601
205602
205603
205604
205605
205606
205607
205608
205609
205610
205611
205612
205613
205614
205615
205616
205617
205618
205619
205620
205621
205622
205623
205624
205625
205626
205627
205628
205629
205630
205631
205632
205633
205634
205635
205636
205637
205638
205639
205640
205641
205642
205643





205644





205645
205646

205647
205648
205649
205650






205651
205652
205653
205654

205655











205656


































205657





205658
205659
205660
205661
205662
205663
205664


205665








205666

205667
205668
205669
205670
205671
205672



























205673


205674
205675





205676
205677
205678

205679
205680


205681
205682
205683
205684
205685
205686
205687
205688
205689
205690
205691

205692
205693
205694
205695
205696

205697
205698
205699
205700
205701
205702
205703
205704
205705
205706
205707
205708
205709
205710
205711
205712
205713
205714
205715

205716


205717

205718
205719
205720
205721
205722

205723


205724



205725
205726

205727
205728

205729
205730
205731






205732
205733
205734

205735
205736
205737
205738
205739
205740
205741
205742
205743
205744
205745
205746
205747
205748
205749
205750
205751
205752
205753
205754
205755
205756
205757
205758
205759
205760
205761
205762
205763
205764
205765
205766
205767
205768

205769
205770

205771




205772
205773




205774
205775
205776
205777
205778
205779
205780
205781
205782
205783
205784
205785
205786
205787
205788
  u32 n;

  if( argc&1 ){
    sqlite3_result_error(ctx, "json_object() requires an even number "
                                  "of arguments", -1);
    return;
  }
  jsonInit(&jx, ctx);
  jsonAppendChar(&jx, '{');
  for(i=0; i<argc; i+=2){
    if( sqlite3_value_type(argv[i])!=SQLITE_TEXT ){
      sqlite3_result_error(ctx, "json_object() labels must be TEXT", -1);
      jsonReset(&jx);
      return;
    }
    jsonAppendSeparator(&jx);
    z = (const char*)sqlite3_value_text(argv[i]);
    n = (u32)sqlite3_value_bytes(argv[i]);
    jsonAppendString(&jx, z, n);
    jsonAppendChar(&jx, ':');
    jsonAppendValue(&jx, argv[i+1]);
  }
  jsonAppendChar(&jx, '}');
  jsonResult(&jx);
  sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}


/*
** json_remove(JSON, PATH, ...)
**
** Remove the named elements from JSON and return the result.  malformed
** JSON or PATH arguments result in an error.
*/
static void jsonRemoveFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  JsonParse *pParse;          /* The parse */
  JsonNode *pNode;
  const char *zPath;

  u32 i;

  if( argc<1 ) return;
  pParse = jsonParseCached(ctx, argv[0], ctx, argc>1);
  if( pParse==0 ) return;
  for(i=1; i<(u32)argc; i++){
    zPath = (const char*)sqlite3_value_text(argv[i]);
    if( zPath==0 ) goto remove_done;
    pNode = jsonLookup(pParse, zPath, 0, ctx);
    if( pParse->nErr ) goto remove_done;
    if( pNode ){
      pNode->jnFlags |= JNODE_REMOVE;
      pParse->hasMod = 1;
      pParse->useMod = 1;
    }
  }
  if( (pParse->aNode[0].jnFlags & JNODE_REMOVE)==0 ){
    jsonReturnJson(pParse, pParse->aNode, ctx, 1, 0);
  }


remove_done:
  jsonDebugPrintParse(p);
}

/*
** Substitute the value at iNode with the pValue parameter.
*/
static void jsonReplaceNode(
  sqlite3_context *pCtx,
  JsonParse *p,
  int iNode,
  sqlite3_value *pValue
){
  int idx = jsonParseAddSubstNode(p, iNode);
  if( idx<=0 ){
    assert( p->oom );
    return;
  }
  switch( sqlite3_value_type(pValue) ){
    case SQLITE_NULL: {
      jsonParseAddNode(p, JSON_NULL, 0, 0);
      break;
    }
    case SQLITE_FLOAT: {
      char *z = sqlite3_mprintf("%!0.15g", sqlite3_value_double(pValue));
      int n;
      if( z==0 ){
        p->oom = 1;
        break;
      }
      n = sqlite3Strlen30(z);
      jsonParseAddNode(p, JSON_REAL, n, z);
      jsonParseAddCleanup(p, sqlite3_free, z);
      break;
    }
    case SQLITE_INTEGER: {
      char *z = sqlite3_mprintf("%lld", sqlite3_value_int64(pValue));
      int n;
      if( z==0 ){
        p->oom = 1;
        break;
      }
      n = sqlite3Strlen30(z);
      jsonParseAddNode(p, JSON_INT, n, z);
      jsonParseAddCleanup(p, sqlite3_free, z);

      break;
    }
    case SQLITE_TEXT: {
      const char *z = (const char*)sqlite3_value_text(pValue);
      u32 n = (u32)sqlite3_value_bytes(pValue);
      if( z==0 ){
         p->oom = 1;
         break;
      }
      if( sqlite3_value_subtype(pValue)!=JSON_SUBTYPE ){
        char *zCopy = sqlite3_malloc64( n+1 );
        int k;
        if( zCopy ){
          memcpy(zCopy, z, n);
          zCopy[n] = 0;
          jsonParseAddCleanup(p, sqlite3_free, zCopy);
        }else{
          p->oom = 1;
          sqlite3_result_error_nomem(pCtx);
        }
        k = jsonParseAddNode(p, JSON_STRING, n, zCopy);
        assert( k>0 || p->oom );
        if( p->oom==0 ) p->aNode[k].jnFlags |= JNODE_RAW;
      }else{
        JsonParse *pPatch = jsonParseCached(pCtx, pValue, pCtx, 1);
        if( pPatch==0 ){
          p->oom = 1;
          break;
        }
        jsonParseAddNodeArray(p, pPatch->aNode, pPatch->nNode);
        /* The nodes copied out of pPatch and into p likely contain
        ** u.zJContent pointers into pPatch->zJson.  So preserve the
        ** content of pPatch until p is destroyed. */
        assert( pPatch->nJPRef>=1 );
        pPatch->nJPRef++;
        jsonParseAddCleanup(p, (void(*)(void*))jsonParseFree, pPatch);
      }
      break;
    }
    default: {
      jsonParseAddNode(p, JSON_NULL, 0, 0);
      sqlite3_result_error(pCtx, "JSON cannot hold BLOB values", -1);
      p->nErr++;
      break;
    }
  }
}

/*
** json_replace(JSON, PATH, VALUE, ...)
**
** Replace the value at PATH with VALUE.  If PATH does not already exist,
** this routine is a no-op.  If JSON or PATH is malformed, throw an error.
*/
static void jsonReplaceFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  JsonParse *pParse;          /* The parse */
  JsonNode *pNode;
  const char *zPath;
  u32 i;

  if( argc<1 ) return;
  if( (argc&1)==0 ) {
    jsonWrongNumArgs(ctx, "replace");
    return;
  }
  pParse = jsonParseCached(ctx, argv[0], ctx, argc>1);
  if( pParse==0 ) return;
  pParse->nJPRef++;
  for(i=1; i<(u32)argc; i+=2){
    zPath = (const char*)sqlite3_value_text(argv[i]);
    pParse->useMod = 1;
    pNode = jsonLookup(pParse, zPath, 0, ctx);
    if( pParse->nErr ) goto replace_err;
    if( pNode ){
      jsonReplaceNode(ctx, pParse, (u32)(pNode - pParse->aNode), argv[i+1]);
    }
  }
  jsonReturnJson(pParse, pParse->aNode, ctx, 1, 0);
replace_err:
  jsonDebugPrintParse(pParse);
  jsonParseFree(pParse);
}


/*
** json_set(JSON, PATH, VALUE, ...)
**
** Set the value at PATH to VALUE.  Create the PATH if it does not already
** exist.  Overwrite existing values that do exist.
** If JSON or PATH is malformed, throw an error.
**
** json_insert(JSON, PATH, VALUE, ...)
**
** Create PATH and initialize it to VALUE.  If PATH already exists, this
** routine is a no-op.  If JSON or PATH is malformed, throw an error.
*/
static void jsonSetFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  JsonParse *pParse;       /* The parse */
  JsonNode *pNode;
  const char *zPath;
  u32 i;
  int bApnd;
  int bIsSet = sqlite3_user_data(ctx)!=0;

  if( argc<1 ) return;
  if( (argc&1)==0 ) {
    jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert");
    return;
  }
  pParse = jsonParseCached(ctx, argv[0], ctx, argc>1);
  if( pParse==0 ) return;
  pParse->nJPRef++;
  for(i=1; i<(u32)argc; i+=2){
    zPath = (const char*)sqlite3_value_text(argv[i]);
    bApnd = 0;
    pParse->useMod = 1;
    pNode = jsonLookup(pParse, zPath, &bApnd, ctx);
    if( pParse->oom ){
      sqlite3_result_error_nomem(ctx);
      goto jsonSetDone;
    }else if( pParse->nErr ){
      goto jsonSetDone;
    }else if( pNode && (bApnd || bIsSet) ){
      jsonReplaceNode(ctx, pParse, (u32)(pNode - pParse->aNode), argv[i+1]);
    }
  }
  jsonDebugPrintParse(pParse);
  jsonReturnJson(pParse, pParse->aNode, ctx, 1, 0);
jsonSetDone:
  jsonParseFree(pParse);
}

/*
** json_type(JSON)
** json_type(JSON, PATH)
**
** Return the top-level "type" of a JSON string.  json_type() raises an
** error if either the JSON or PATH inputs are not well-formed.
*/
static void jsonTypeFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  JsonParse *p;          /* The parse */
  const char *zPath;
  JsonNode *pNode;

  p = jsonParseCached(ctx, argv[0], ctx, 0);
  if( p==0 ) return;
  if( argc==2 ){
    zPath = (const char*)sqlite3_value_text(argv[1]);





    pNode = jsonLookup(p, zPath, 0, ctx);





  }else{
    pNode = p->aNode;

  }
  if( pNode ){
    sqlite3_result_text(ctx, jsonType[pNode->eType], -1, SQLITE_STATIC);
  }






}

/*
** json_valid(JSON)

**











** Return 1 if JSON is a well-formed canonical JSON string according


































** to RFC-7159. Return 0 otherwise.





*/
static void jsonValidFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  JsonParse *p;          /* The parse */


  UNUSED_PARAMETER(argc);








  if( sqlite3_value_type(argv[0])==SQLITE_NULL ){

#ifdef SQLITE_LEGACY_JSON_VALID
    /* Incorrect legacy behavior was to return FALSE for a NULL input */
    sqlite3_result_int(ctx, 0);
#endif
    return;
  }



























  p = jsonParseCached(ctx, argv[0], 0, 0);


  if( p==0 || p->oom ){
    sqlite3_result_error_nomem(ctx);





    sqlite3_free(p);
  }else{
    sqlite3_result_int(ctx, p->nErr==0 && (p->hasNonstd==0 || p->useMod));

    if( p->nErr ) jsonParseFree(p);
  }


}

/*
** json_error_position(JSON)
**
** If the argument is not an interpretable JSON string, then return the 1-based
** character position at which the parser first recognized that the input
** was in error.  The left-most character is 1.  If the string is valid
** JSON, then return 0.
**
** Note that json_valid() is only true for strictly conforming canonical JSON.

** But this routine returns zero if the input contains extension.  Thus:
**
** (1) If the input X is strictly conforming canonical JSON:
**
**         json_valid(X) returns true

**         json_error_position(X) returns 0
**
** (2) If the input X is JSON but it includes extension (such as JSON5) that
**     are not part of RFC-8259:
**
**         json_valid(X) returns false
**         json_error_position(X) return 0
**
** (3) If the input X cannot be interpreted as JSON even taking extensions
**     into account:
**
**         json_valid(X) return false
**         json_error_position(X) returns 1 or more
*/
static void jsonErrorFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){

  JsonParse *p;          /* The parse */


  UNUSED_PARAMETER(argc);

  if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
  p = jsonParseCached(ctx, argv[0], 0, 0);
  if( p==0 || p->oom ){
    sqlite3_result_error_nomem(ctx);
    sqlite3_free(p);

  }else if( p->nErr==0 ){


    sqlite3_result_int(ctx, 0);



  }else{
    int n = 1;

    u32 i;
    const char *z = (const char*)sqlite3_value_text(argv[0]);

    for(i=0; i<p->iErr && ALWAYS(z[i]); i++){
      if( (z[i]&0xc0)!=0x80 ) n++;
    }






    sqlite3_result_int(ctx, n);
    jsonParseFree(p);
  }

}


/****************************************************************************
** Aggregate SQL function implementations
****************************************************************************/
/*
** json_group_array(VALUE)
**
** Return a JSON array composed of all values in the aggregate.
*/
static void jsonArrayStep(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  JsonString *pStr;
  UNUSED_PARAMETER(argc);
  pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
  if( pStr ){
    if( pStr->zBuf==0 ){
      jsonInit(pStr, ctx);
      jsonAppendChar(pStr, '[');
    }else if( pStr->nUsed>1 ){
      jsonAppendChar(pStr, ',');
    }
    pStr->pCtx = ctx;
    jsonAppendValue(pStr, argv[0]);
  }
}
static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){
  JsonString *pStr;
  pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
  if( pStr ){

    pStr->pCtx = ctx;
    jsonAppendChar(pStr, ']');

    if( pStr->bErr ){




      if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
      assert( pStr->bStatic );




    }else if( isFinal ){
      sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed,
                          pStr->bStatic ? SQLITE_TRANSIENT :
                              sqlite3RCStrUnref);
      pStr->bStatic = 1;
    }else{
      sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
      pStr->nUsed--;
    }
  }else{
    sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC);
  }
  sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
static void jsonArrayValue(sqlite3_context *ctx){







|




|




|


|


|















|
<
|
>
|


|
|
|

|
<
|
<
<
<
<

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













<
<
<
<
<





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




















|
<
<
<
|
|






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















|
|

|



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




>

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







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

|
|

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





|
<
<
<

<
>
|
<
|

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






>
|
>
>

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



















|





|






>


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







|







207222
207223
207224
207225
207226
207227
207228
207229
207230
207231
207232
207233
207234
207235
207236
207237
207238
207239
207240
207241
207242
207243
207244
207245
207246
207247
207248
207249
207250
207251
207252
207253
207254
207255
207256
207257
207258
207259
207260
207261

207262
207263
207264
207265
207266
207267
207268
207269
207270
207271

207272




207273

207274
207275
207276
207277
207278
207279

207280
207281






207282


207283
207284





207285




207286
207287




207288









207289



207290

207291





207292
207293










207294
207295



207296


207297
207298
207299




207300

207301
207302


207303


207304


207305
207306
207307
207308
207309
207310
207311
207312
207313
207314
207315
207316
207317





207318
207319
207320
207321
207322
207323















207324
207325
207326
207327
207328
207329
207330
207331
207332
207333
207334
207335
207336
207337
207338
207339
207340
207341
207342
207343
207344



207345
207346
207347
207348
207349
207350
207351
207352
207353




















207354
207355
207356
207357
207358
207359
207360
207361
207362
207363
207364
207365
207366
207367
207368
207369
207370
207371
207372
207373
207374
207375
207376
207377
207378
207379
207380
207381
207382
207383
207384
207385
207386
207387

207388
207389

207390
207391
207392
207393
207394
207395
207396
207397
207398
207399
207400
207401
207402
207403
207404
207405
207406
207407
207408
207409
207410
207411
207412
207413
207414
207415
207416
207417
207418
207419
207420
207421
207422
207423
207424
207425
207426
207427
207428
207429
207430
207431
207432
207433
207434
207435
207436
207437
207438
207439
207440
207441
207442
207443
207444
207445
207446
207447
207448
207449
207450
207451
207452
207453
207454
207455
207456
207457
207458
207459
207460
207461
207462
207463
207464
207465
207466
207467
207468
207469
207470
207471
207472
207473
207474
207475
207476
207477
207478
207479
207480
207481
207482
207483
207484
207485
207486
207487
207488
207489
207490
207491
207492
207493
207494
207495
207496
207497
207498
207499
207500
207501
207502
207503
207504
207505
207506
207507
207508
207509
207510
207511
207512
207513
207514
207515
207516
207517
207518
207519
207520
207521
207522
207523
207524
207525
207526
207527
207528
207529
207530
207531
207532



207533

207534
207535

207536
207537

207538
207539

207540





207541




207542
207543
207544
207545
207546
207547
207548
207549
207550
207551
207552
207553
207554

207555
207556
207557
207558
207559
207560
207561
207562
207563
207564
207565
207566

207567
207568

207569
207570
207571
207572
207573
207574
207575
207576
207577
207578
207579

207580
207581
207582
207583
207584
207585
207586
207587
207588
207589
207590
207591
207592
207593
207594
207595
207596
207597
207598
207599
207600
207601
207602
207603
207604
207605
207606
207607
207608
207609
207610
207611
207612
207613
207614
207615
207616
207617
207618
207619
207620
207621
207622
207623
207624
207625
207626
207627
207628
207629
207630
207631
207632
207633
207634
207635
207636
207637
207638
207639
207640
207641
207642
207643
207644
207645
  u32 n;

  if( argc&1 ){
    sqlite3_result_error(ctx, "json_object() requires an even number "
                                  "of arguments", -1);
    return;
  }
  jsonStringInit(&jx, ctx);
  jsonAppendChar(&jx, '{');
  for(i=0; i<argc; i+=2){
    if( sqlite3_value_type(argv[i])!=SQLITE_TEXT ){
      sqlite3_result_error(ctx, "json_object() labels must be TEXT", -1);
      jsonStringReset(&jx);
      return;
    }
    jsonAppendSeparator(&jx);
    z = (const char*)sqlite3_value_text(argv[i]);
    n = sqlite3_value_bytes(argv[i]);
    jsonAppendString(&jx, z, n);
    jsonAppendChar(&jx, ':');
    jsonAppendSqlValue(&jx, argv[i+1]);
  }
  jsonAppendChar(&jx, '}');
  jsonReturnString(&jx, 0, 0);
  sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}


/*
** json_remove(JSON, PATH, ...)
**
** Remove the named elements from JSON and return the result.  malformed
** JSON or PATH arguments result in an error.
*/
static void jsonRemoveFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  JsonParse *p;          /* The parse */

  const char *zPath = 0; /* Path of element to be removed */
  int i;                 /* Loop counter */
  u32 rc;                /* Subroutine return code */

  if( argc<1 ) return;
  p = jsonParseFuncArg(ctx, argv[0], argc>1 ? JSON_EDITABLE : 0);
  if( p==0 ) return;
  for(i=1; i<argc; i++){
    zPath = (const char*)sqlite3_value_text(argv[i]);
    if( zPath==0 ){

      goto json_remove_done;




    }

    if( zPath[0]!='$' ){
      goto json_remove_patherror;
    }
    if( zPath[1]==0 ){
      /* json_remove(j,'$') returns NULL */
      goto json_remove_done;

    }
    p->eEdit = JEDIT_DEL;






    p->delta = 0;


    rc = jsonLookupStep(p, 0, zPath+1, 0);
    if( JSON_LOOKUP_ISERROR(rc) ){





      if( rc==JSON_LOOKUP_NOTFOUND ){




        continue;  /* No-op */
      }else if( rc==JSON_LOOKUP_PATHERROR ){




        jsonBadPathError(ctx, zPath);









      }else{



        sqlite3_result_error(ctx, "malformed JSON", -1);

      }





      goto json_remove_done;
    }










  }
  jsonReturnParse(ctx, p);



  jsonParseFree(p);


  return;

json_remove_patherror:




  jsonBadPathError(ctx, zPath);


json_remove_done:


  jsonParseFree(p);


  return;


}

/*
** json_replace(JSON, PATH, VALUE, ...)
**
** Replace the value at PATH with VALUE.  If PATH does not already exist,
** this routine is a no-op.  If JSON or PATH is malformed, throw an error.
*/
static void jsonReplaceFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){





  if( argc<1 ) return;
  if( (argc&1)==0 ) {
    jsonWrongNumArgs(ctx, "replace");
    return;
  }
  jsonInsertIntoBlob(ctx, argc, argv, JEDIT_REPL);















}


/*
** json_set(JSON, PATH, VALUE, ...)
**
** Set the value at PATH to VALUE.  Create the PATH if it does not already
** exist.  Overwrite existing values that do exist.
** If JSON or PATH is malformed, throw an error.
**
** json_insert(JSON, PATH, VALUE, ...)
**
** Create PATH and initialize it to VALUE.  If PATH already exists, this
** routine is a no-op.  If JSON or PATH is malformed, throw an error.
*/
static void jsonSetFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){




  int flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
  int bIsSet = (flags&JSON_ISSET)!=0;

  if( argc<1 ) return;
  if( (argc&1)==0 ) {
    jsonWrongNumArgs(ctx, bIsSet ? "set" : "insert");
    return;
  }
  jsonInsertIntoBlob(ctx, argc, argv, bIsSet ? JEDIT_SET : JEDIT_INS);




















}

/*
** json_type(JSON)
** json_type(JSON, PATH)
**
** Return the top-level "type" of a JSON string.  json_type() raises an
** error if either the JSON or PATH inputs are not well-formed.
*/
static void jsonTypeFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  JsonParse *p;          /* The parse */
  const char *zPath = 0;
  u32 i;

  p = jsonParseFuncArg(ctx, argv[0], 0);
  if( p==0 ) return;
  if( argc==2 ){
    zPath = (const char*)sqlite3_value_text(argv[1]);
    if( zPath==0 ) goto json_type_done;
    if( zPath[0]!='$' ){
      jsonBadPathError(ctx, zPath);
      goto json_type_done;
    }
    i = jsonLookupStep(p, 0, zPath+1, 0);
    if( JSON_LOOKUP_ISERROR(i) ){
      if( i==JSON_LOOKUP_NOTFOUND ){
        /* no-op */
      }else if( i==JSON_LOOKUP_PATHERROR ){
        jsonBadPathError(ctx, zPath);
      }else{

        sqlite3_result_error(ctx, "malformed JSON", -1);
      }

      goto json_type_done;
    }
  }else{
    i = 0;
  }
  sqlite3_result_text(ctx, jsonbType[p->aBlob[i]&0x0f], -1, SQLITE_STATIC);
json_type_done:
  jsonParseFree(p);
}

/*
** json_valid(JSON)
** json_valid(JSON, FLAGS)
**
** Check the JSON argument to see if it is well-formed.  The FLAGS argument
** encodes the various constraints on what is meant by "well-formed":
**
**     0x01      Canonical RFC-8259 JSON text
**     0x02      JSON text with optional JSON-5 extensions
**     0x04      Superficially appears to be JSONB
**     0x08      Strictly well-formed JSONB
**
** If the FLAGS argument is omitted, it defaults to 1.  Useful values for
** FLAGS include:
**
**    1          Strict canonical JSON text
**    2          JSON text perhaps with JSON-5 extensions
**    4          Superficially appears to be JSONB
**    5          Canonical JSON text or superficial JSONB
**    6          JSON-5 text or superficial JSONB
**    8          Strict JSONB
**    9          Canonical JSON text or strict JSONB
**    10         JSON-5 text or strict JSONB
**
** Other flag combinations are redundant.  For example, every canonical
** JSON text is also well-formed JSON-5 text, so FLAG values 2 and 3
** are the same.  Similarly, any input that passes a strict JSONB validation
** will also pass the superficial validation so 12 through 15 are the same
** as 8 through 11 respectively.
**
** This routine runs in linear time to validate text and when doing strict
** JSONB validation.  Superficial JSONB validation is constant time,
** assuming the BLOB is already in memory.  The performance advantage
** of superficial JSONB validation is why that option is provided.
** Application developers can choose to do fast superficial validation or
** slower strict validation, according to their specific needs.
**
** Only the lower four bits of the FLAGS argument are currently used.
** Higher bits are reserved for future expansion.   To facilitate
** compatibility, the current implementation raises an error if any bit
** in FLAGS is set other than the lower four bits.
**
** The original circa 2015 implementation of the JSON routines in
** SQLite only supported canonical RFC-8259 JSON text and the json_valid()
** function only accepted one argument.  That is why the default value
** for the FLAGS argument is 1, since FLAGS=1 causes this routine to only
** recognize canonical RFC-8259 JSON text as valid.  The extra FLAGS
** argument was added when the JSON routines were extended to support
** JSON5-like extensions and binary JSONB stored in BLOBs.
**
** Return Values:
**
**   *   Raise an error if FLAGS is outside the range of 1 to 15.
**   *   Return NULL if the input is NULL
**   *   Return 1 if the input is well-formed.
**   *   Return 0 if the input is not well-formed.
*/
static void jsonValidFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  JsonParse *p;          /* The parse */
  u8 flags = 1;
  u8 res = 0;
  if( argc==2 ){
    i64 f = sqlite3_value_int64(argv[1]);
    if( f<1 || f>15 ){
      sqlite3_result_error(ctx, "FLAGS parameter to json_valid() must be"
                                " between 1 and 15", -1);
      return;
    }
    flags = f & 0x0f;
  }
  switch( sqlite3_value_type(argv[0]) ){
    case SQLITE_NULL: {
#ifdef SQLITE_LEGACY_JSON_VALID
      /* Incorrect legacy behavior was to return FALSE for a NULL input */
      sqlite3_result_int(ctx, 0);
#endif
      return;
    }
    case SQLITE_BLOB: {
      if( jsonFuncArgMightBeBinary(argv[0]) ){
        if( flags & 0x04 ){
          /* Superficial checking only - accomplished by the
          ** jsonFuncArgMightBeBinary() call above. */
          res = 1;
        }else if( flags & 0x08 ){
          /* Strict checking.  Check by translating BLOB->TEXT->BLOB.  If
          ** no errors occur, call that a "strict check". */
          JsonParse px;
          u32 iErr;
          memset(&px, 0, sizeof(px));
          px.aBlob = (u8*)sqlite3_value_blob(argv[0]);
          px.nBlob = sqlite3_value_bytes(argv[0]);
          iErr = jsonbValidityCheck(&px, 0, px.nBlob, 1);
          res = iErr==0;
        }
        break;
      }
      /* Fall through into interpreting the input as text.  See note
      ** above at tag-20240123-a. */
      /* no break */ deliberate_fall_through
    }
    default: {
      JsonParse px;
      if( (flags & 0x3)==0 ) break;
      memset(&px, 0, sizeof(px));

      p = jsonParseFuncArg(ctx, argv[0], JSON_KEEPERROR);
      if( p ){
        if( p->oom ){
          sqlite3_result_error_nomem(ctx);
        }else if( p->nErr ){
          /* no-op */
        }else if( (flags & 0x02)!=0 || p->hasNonstd==0 ){
          res = 1;
        }
        jsonParseFree(p);
      }else{
        sqlite3_result_error_nomem(ctx);
      }
      break;
    }
  }
  sqlite3_result_int(ctx, res);
}

/*
** json_error_position(JSON)
**
** If the argument is NULL, return NULL



**

** If the argument is BLOB, do a full validity check and return non-zero
** if the check fails.  The return value is the approximate 1-based offset

** to the byte of the element that contains the first error.
**

** Otherwise interpret the argument is TEXT (even if it is numeric) and
** return the 1-based character position for where the parser first recognized

** that the input was not valid JSON, or return 0 if the input text looks





** ok.  JSON-5 extensions are accepted.




*/
static void jsonErrorFunc(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  i64 iErrPos = 0;       /* Error position to be returned */
  JsonParse s;

  assert( argc==1 );
  UNUSED_PARAMETER(argc);
  memset(&s, 0, sizeof(s));
  s.db = sqlite3_context_db_handle(ctx);

  if( jsonFuncArgMightBeBinary(argv[0]) ){
    s.aBlob = (u8*)sqlite3_value_blob(argv[0]);
    s.nBlob = sqlite3_value_bytes(argv[0]);
    iErrPos = (i64)jsonbValidityCheck(&s, 0, s.nBlob, 1);
  }else{
    s.zJson = (char*)sqlite3_value_text(argv[0]);
    if( s.zJson==0 ) return;  /* NULL input or OOM */
    s.nJson = sqlite3_value_bytes(argv[0]);
    if( jsonConvertTextToBlob(&s,0) ){
      if( s.oom ){
        iErrPos = -1;
      }else{

        /* Convert byte-offset s.iErr into a character offset */
        u32 k;

        assert( s.zJson!=0 );  /* Because s.oom is false */
        for(k=0; k<s.iErr && ALWAYS(s.zJson[k]); k++){
          if( (s.zJson[k] & 0xc0)!=0x80 ) iErrPos++;
        }
        iErrPos++;
      }
    }
  }
  jsonParseReset(&s);
  if( iErrPos<0 ){
    sqlite3_result_error_nomem(ctx);

  }else{
    sqlite3_result_int64(ctx, iErrPos);
  }
}

/****************************************************************************
** Aggregate SQL function implementations
****************************************************************************/
/*
** json_group_array(VALUE)
**
** Return a JSON array composed of all values in the aggregate.
*/
static void jsonArrayStep(
  sqlite3_context *ctx,
  int argc,
  sqlite3_value **argv
){
  JsonString *pStr;
  UNUSED_PARAMETER(argc);
  pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
  if( pStr ){
    if( pStr->zBuf==0 ){
      jsonStringInit(pStr, ctx);
      jsonAppendChar(pStr, '[');
    }else if( pStr->nUsed>1 ){
      jsonAppendChar(pStr, ',');
    }
    pStr->pCtx = ctx;
    jsonAppendSqlValue(pStr, argv[0]);
  }
}
static void jsonArrayCompute(sqlite3_context *ctx, int isFinal){
  JsonString *pStr;
  pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
  if( pStr ){
    int flags;
    pStr->pCtx = ctx;
    jsonAppendChar(pStr, ']');
    flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
    if( pStr->eErr ){
      jsonReturnString(pStr, 0, 0);
      return;
    }else if( flags & JSON_BLOB ){
      jsonReturnStringAsBlob(pStr);
      if( isFinal ){
        if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf);
      }else{
        jsonStringTrimOneChar(pStr);
      }
      return;
    }else if( isFinal ){
      sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed,
                          pStr->bStatic ? SQLITE_TRANSIENT :
                              sqlite3RCStrUnref);
      pStr->bStatic = 1;
    }else{
      sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
      jsonStringTrimOneChar(pStr);
    }
  }else{
    sqlite3_result_text(ctx, "[]", 2, SQLITE_STATIC);
  }
  sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
static void jsonArrayValue(sqlite3_context *ctx){
205855
205856
205857
205858
205859
205860
205861
205862
205863
205864
205865
205866
205867
205868
205869
205870
205871
205872
205873
205874
205875
205876
205877
205878

205879


205880




205881
205882




205883
205884
205885
205886
205887
205888
205889
205890
205891
205892
205893
205894
205895
205896
205897
205898
205899
205900
205901
205902
205903
205904
205905
205906
205907
205908
205909









205910
205911
205912
205913
205914
205915
205916

205917
205918
205919


205920

205921
205922






205923
205924
205925
205926
205927
205928
205929
205930
205931
205932
205933
205934
205935
205936
205937
205938
205939
  JsonString *pStr;
  const char *z;
  u32 n;
  UNUSED_PARAMETER(argc);
  pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
  if( pStr ){
    if( pStr->zBuf==0 ){
      jsonInit(pStr, ctx);
      jsonAppendChar(pStr, '{');
    }else if( pStr->nUsed>1 ){
      jsonAppendChar(pStr, ',');
    }
    pStr->pCtx = ctx;
    z = (const char*)sqlite3_value_text(argv[0]);
    n = (u32)sqlite3_value_bytes(argv[0]);
    jsonAppendString(pStr, z, n);
    jsonAppendChar(pStr, ':');
    jsonAppendValue(pStr, argv[1]);
  }
}
static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){
  JsonString *pStr;
  pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
  if( pStr ){

    jsonAppendChar(pStr, '}');


    if( pStr->bErr ){




      if( pStr->bErr==1 ) sqlite3_result_error_nomem(ctx);
      assert( pStr->bStatic );




    }else if( isFinal ){
      sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed,
                          pStr->bStatic ? SQLITE_TRANSIENT :
                          sqlite3RCStrUnref);
      pStr->bStatic = 1;
    }else{
      sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
      pStr->nUsed--;
    }
  }else{
    sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC);
  }
  sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
static void jsonObjectValue(sqlite3_context *ctx){
  jsonObjectCompute(ctx, 0);
}
static void jsonObjectFinal(sqlite3_context *ctx){
  jsonObjectCompute(ctx, 1);
}



#ifndef SQLITE_OMIT_VIRTUALTABLE
/****************************************************************************
** The json_each virtual table
****************************************************************************/









typedef struct JsonEachCursor JsonEachCursor;
struct JsonEachCursor {
  sqlite3_vtab_cursor base;  /* Base class - must be first */
  u32 iRowid;                /* The rowid */
  u32 iBegin;                /* The first node of the scan */
  u32 i;                     /* Index in sParse.aNode[] of current row */
  u32 iEnd;                  /* EOF when i equals or exceeds this value */

  u8 eType;                  /* Type of top-level element */
  u8 bRecursive;             /* True for json_tree().  False for json_each() */
  char *zJson;               /* Input JSON */


  char *zRoot;               /* Path by which to filter zJson */

  JsonParse sParse;          /* Parse of the input JSON */
};







/* Constructor for the json_each virtual table */
static int jsonEachConnect(
  sqlite3 *db,
  void *pAux,
  int argc, const char *const*argv,
  sqlite3_vtab **ppVtab,
  char **pzErr
){
  sqlite3_vtab *pNew;
  int rc;

/* Column numbers */
#define JEACH_KEY     0
#define JEACH_VALUE   1
#define JEACH_TYPE    2
#define JEACH_ATOM    3







|






|


|






>

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







|



















>
>
>
>
>
>
>
>
>




<
|

>
|

|
>
>
|
>


>
>
>
>
>
>









|







207712
207713
207714
207715
207716
207717
207718
207719
207720
207721
207722
207723
207724
207725
207726
207727
207728
207729
207730
207731
207732
207733
207734
207735
207736
207737
207738
207739
207740
207741
207742
207743
207744
207745
207746
207747
207748
207749
207750
207751
207752
207753
207754
207755
207756
207757
207758
207759
207760
207761
207762
207763
207764
207765
207766
207767
207768
207769
207770
207771
207772
207773
207774
207775
207776
207777
207778
207779
207780
207781
207782
207783
207784
207785
207786
207787
207788
207789
207790

207791
207792
207793
207794
207795
207796
207797
207798
207799
207800
207801
207802
207803
207804
207805
207806
207807
207808
207809
207810
207811
207812
207813
207814
207815
207816
207817
207818
207819
207820
207821
207822
207823
207824
207825
  JsonString *pStr;
  const char *z;
  u32 n;
  UNUSED_PARAMETER(argc);
  pStr = (JsonString*)sqlite3_aggregate_context(ctx, sizeof(*pStr));
  if( pStr ){
    if( pStr->zBuf==0 ){
      jsonStringInit(pStr, ctx);
      jsonAppendChar(pStr, '{');
    }else if( pStr->nUsed>1 ){
      jsonAppendChar(pStr, ',');
    }
    pStr->pCtx = ctx;
    z = (const char*)sqlite3_value_text(argv[0]);
    n = sqlite3Strlen30(z);
    jsonAppendString(pStr, z, n);
    jsonAppendChar(pStr, ':');
    jsonAppendSqlValue(pStr, argv[1]);
  }
}
static void jsonObjectCompute(sqlite3_context *ctx, int isFinal){
  JsonString *pStr;
  pStr = (JsonString*)sqlite3_aggregate_context(ctx, 0);
  if( pStr ){
    int flags;
    jsonAppendChar(pStr, '}');
    pStr->pCtx = ctx;
    flags = SQLITE_PTR_TO_INT(sqlite3_user_data(ctx));
    if( pStr->eErr ){
      jsonReturnString(pStr, 0, 0);
      return;
    }else if( flags & JSON_BLOB ){
      jsonReturnStringAsBlob(pStr);
      if( isFinal ){
        if( !pStr->bStatic ) sqlite3RCStrUnref(pStr->zBuf);
      }else{
        jsonStringTrimOneChar(pStr);
      }
      return;
    }else if( isFinal ){
      sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed,
                          pStr->bStatic ? SQLITE_TRANSIENT :
                          sqlite3RCStrUnref);
      pStr->bStatic = 1;
    }else{
      sqlite3_result_text(ctx, pStr->zBuf, (int)pStr->nUsed, SQLITE_TRANSIENT);
      jsonStringTrimOneChar(pStr);
    }
  }else{
    sqlite3_result_text(ctx, "{}", 2, SQLITE_STATIC);
  }
  sqlite3_result_subtype(ctx, JSON_SUBTYPE);
}
static void jsonObjectValue(sqlite3_context *ctx){
  jsonObjectCompute(ctx, 0);
}
static void jsonObjectFinal(sqlite3_context *ctx){
  jsonObjectCompute(ctx, 1);
}



#ifndef SQLITE_OMIT_VIRTUALTABLE
/****************************************************************************
** The json_each virtual table
****************************************************************************/
typedef struct JsonParent JsonParent;
struct JsonParent {
  u32 iHead;                 /* Start of object or array */
  u32 iValue;                /* Start of the value */
  u32 iEnd;                  /* First byte past the end */
  u32 nPath;                 /* Length of path */
  i64 iKey;                  /* Key for JSONB_ARRAY */
};

typedef struct JsonEachCursor JsonEachCursor;
struct JsonEachCursor {
  sqlite3_vtab_cursor base;  /* Base class - must be first */
  u32 iRowid;                /* The rowid */

  u32 i;                     /* Index in sParse.aBlob[] of current row */
  u32 iEnd;                  /* EOF when i equals or exceeds this value */
  u32 nRoot;                 /* Size of the root path in bytes */
  u8 eType;                  /* Type of the container for element i */
  u8 bRecursive;             /* True for json_tree().  False for json_each() */
  u32 nParent;               /* Current nesting depth */
  u32 nParentAlloc;          /* Space allocated for aParent[] */
  JsonParent *aParent;       /* Parent elements of i */
  sqlite3 *db;               /* Database connection */
  JsonString path;           /* Current path */
  JsonParse sParse;          /* Parse of the input JSON */
};
typedef struct JsonEachConnection JsonEachConnection;
struct JsonEachConnection {
  sqlite3_vtab base;         /* Base class - must be first */
  sqlite3 *db;               /* Database connection */
};


/* Constructor for the json_each virtual table */
static int jsonEachConnect(
  sqlite3 *db,
  void *pAux,
  int argc, const char *const*argv,
  sqlite3_vtab **ppVtab,
  char **pzErr
){
  JsonEachConnection *pNew;
  int rc;

/* Column numbers */
#define JEACH_KEY     0
#define JEACH_VALUE   1
#define JEACH_TYPE    2
#define JEACH_ATOM    3
205951
205952
205953
205954
205955
205956
205957

205958
205959
205960
205961

205962
205963
205964
205965
205966
205967

205968
205969
205970
205971
205972
205973

205974
205975
205976
205977
205978
205979

205980
205981
205982
205983
205984
205985
205986
205987
205988
205989
205990
205991
205992
205993
205994
205995
205996
205997
205998


205999
206000



206001
206002
206003
206004
206005
206006
206007
206008
206009
206010
206011

206012
206013
206014
206015
206016
206017
206018
206019
206020
206021
















































206022
206023
206024

206025
206026
206027
206028


206029
206030
206031
206032
206033

206034
206035

206036

206037
206038
206039
206040
206041
206042
206043
206044
206045
206046
206047
206048
206049
206050
206051

206052
206053
206054


206055
206056

206057
206058
206059

206060
206061

206062


206063
206064
206065
206066
206067
206068
206069
206070
206071
206072
206073
206074
206075
206076
206077
206078
206079
206080
206081
206082
206083
206084
206085
206086
206087
206088
206089





206090
206091


206092


206093
206094
206095
206096


206097
206098
206099


206100
206101


206102


206103
206104
206105


206106
206107
206108
206109
206110
206111
206112
206113
206114
206115
206116
206117
206118
206119


206120
206121
206122
206123
206124
206125
206126
206127
206128
206129
206130
206131
206132

206133
206134
206135
206136
206137
206138
206139

206140



206141
206142

206143






206144
206145
206146
206147
206148
206149
206150
206151
206152
206153
206154

206155
206156
206157
206158
206159
206160
206161

206162
206163
206164
206165
206166
206167
206168
206169
206170
206171
206172
206173
206174
206175
206176
206177
206178
206179
206180
206181
206182
206183
206184
206185
206186
206187

206188
206189
206190
206191
206192
206193
206194
206195
206196
206197
206198
206199
206200

206201
206202
206203
206204
206205
206206
206207
206208
206209
206210
206211
206212
206213



206214
206215

206216
206217
206218
206219
206220
206221
206222
  UNUSED_PARAMETER(argv);
  UNUSED_PARAMETER(argc);
  UNUSED_PARAMETER(pAux);
  rc = sqlite3_declare_vtab(db,
     "CREATE TABLE x(key,value,type,atom,id,parent,fullkey,path,"
                    "json HIDDEN,root HIDDEN)");
  if( rc==SQLITE_OK ){

    pNew = *ppVtab = sqlite3_malloc( sizeof(*pNew) );
    if( pNew==0 ) return SQLITE_NOMEM;
    memset(pNew, 0, sizeof(*pNew));
    sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);

  }
  return rc;
}

/* destructor for json_each virtual table */
static int jsonEachDisconnect(sqlite3_vtab *pVtab){

  sqlite3_free(pVtab);
  return SQLITE_OK;
}

/* constructor for a JsonEachCursor object for json_each(). */
static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){

  JsonEachCursor *pCur;

  UNUSED_PARAMETER(p);
  pCur = sqlite3_malloc( sizeof(*pCur) );
  if( pCur==0 ) return SQLITE_NOMEM;
  memset(pCur, 0, sizeof(*pCur));

  *ppCursor = &pCur->base;
  return SQLITE_OK;
}

/* constructor for a JsonEachCursor object for json_tree(). */
static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
  int rc = jsonEachOpenEach(p, ppCursor);
  if( rc==SQLITE_OK ){
    JsonEachCursor *pCur = (JsonEachCursor*)*ppCursor;
    pCur->bRecursive = 1;
  }
  return rc;
}

/* Reset a JsonEachCursor back to its original state.  Free any memory
** held. */
static void jsonEachCursorReset(JsonEachCursor *p){
  sqlite3_free(p->zRoot);
  jsonParseReset(&p->sParse);


  p->iRowid = 0;
  p->i = 0;



  p->iEnd = 0;
  p->eType = 0;
  p->zJson = 0;
  p->zRoot = 0;
}

/* Destructor for a jsonEachCursor object */
static int jsonEachClose(sqlite3_vtab_cursor *cur){
  JsonEachCursor *p = (JsonEachCursor*)cur;
  jsonEachCursorReset(p);
  sqlite3_free(cur);

  return SQLITE_OK;
}

/* Return TRUE if the jsonEachCursor object has been advanced off the end
** of the JSON object */
static int jsonEachEof(sqlite3_vtab_cursor *cur){
  JsonEachCursor *p = (JsonEachCursor*)cur;
  return p->i >= p->iEnd;
}

















































/* Advance the cursor to the next element for json_tree() */
static int jsonEachNext(sqlite3_vtab_cursor *cur){
  JsonEachCursor *p = (JsonEachCursor*)cur;

  if( p->bRecursive ){
    if( p->sParse.aNode[p->i].jnFlags & JNODE_LABEL ) p->i++;
    p->i++;
    p->iRowid++;


    if( p->i<p->iEnd ){
      u32 iUp = p->sParse.aUp[p->i];
      JsonNode *pUp = &p->sParse.aNode[iUp];
      p->eType = pUp->eType;
      if( pUp->eType==JSON_ARRAY ){

        assert( pUp->eU==0 || pUp->eU==3 );
        testcase( pUp->eU==3 );

        VVA( pUp->eU = 3 );

        if( iUp==p->i-1 ){
          pUp->u.iKey = 0;
        }else{
          pUp->u.iKey++;
        }
      }
    }
  }else{
    switch( p->eType ){
      case JSON_ARRAY: {
        p->i += jsonNodeSize(&p->sParse.aNode[p->i]);
        p->iRowid++;
        break;
      }
      case JSON_OBJECT: {

        p->i += 1 + jsonNodeSize(&p->sParse.aNode[p->i+1]);
        p->iRowid++;
        break;


      }
      default: {

        p->i = p->iEnd;
        break;
      }

    }
  }

  return SQLITE_OK;


}

/* Append an object label to the JSON Path being constructed
** in pStr.
*/
static void jsonAppendObjectPathElement(
  JsonString *pStr,
  JsonNode *pNode
){
  int jj, nn;
  const char *z;
  assert( pNode->eType==JSON_STRING );
  assert( pNode->jnFlags & JNODE_LABEL );
  assert( pNode->eU==1 );
  z = pNode->u.zJContent;
  nn = pNode->n;
  if( (pNode->jnFlags & JNODE_RAW)==0 ){
    assert( nn>=2 );
    assert( z[0]=='"' || z[0]=='\'' );
    assert( z[nn-1]=='"' || z[0]=='\'' );
    if( nn>2 && sqlite3Isalpha(z[1]) ){
      for(jj=2; jj<nn-1 && sqlite3Isalnum(z[jj]); jj++){}
      if( jj==nn-1 ){
        z++;
        nn -= 2;
      }
    }





  }
  jsonPrintf(nn+2, pStr, ".%.*s", nn, z);


}



/* Append the name of the path for element i to pStr
*/
static void jsonEachComputePath(


  JsonEachCursor *p,       /* The cursor */
  JsonString *pStr,        /* Write the path here */
  u32 i                    /* Path to this element */


){
  JsonNode *pNode, *pUp;


  u32 iUp;


  if( i==0 ){
    jsonAppendChar(pStr, '$');
    return;


  }
  iUp = p->sParse.aUp[i];
  jsonEachComputePath(p, pStr, iUp);
  pNode = &p->sParse.aNode[i];
  pUp = &p->sParse.aNode[iUp];
  if( pUp->eType==JSON_ARRAY ){
    assert( pUp->eU==3 || (pUp->eU==0 && pUp->u.iKey==0) );
    testcase( pUp->eU==0 );
    jsonPrintf(30, pStr, "[%d]", pUp->u.iKey);
  }else{
    assert( pUp->eType==JSON_OBJECT );
    if( (pNode->jnFlags & JNODE_LABEL)==0 ) pNode--;
    jsonAppendObjectPathElement(pStr, pNode);
  }


}

/* Return the value of a column */
static int jsonEachColumn(
  sqlite3_vtab_cursor *cur,   /* The cursor */
  sqlite3_context *ctx,       /* First argument to sqlite3_result_...() */
  int i                       /* Which column to return */
){
  JsonEachCursor *p = (JsonEachCursor*)cur;
  JsonNode *pThis = &p->sParse.aNode[p->i];
  switch( i ){
    case JEACH_KEY: {
      if( p->i==0 ) break;

      if( p->eType==JSON_OBJECT ){
        jsonReturn(&p->sParse, pThis, ctx, 0);
      }else if( p->eType==JSON_ARRAY ){
        u32 iKey;
        if( p->bRecursive ){
          if( p->iRowid==0 ) break;
          assert( p->sParse.aNode[p->sParse.aUp[p->i]].eU==3 );

          iKey = p->sParse.aNode[p->sParse.aUp[p->i]].u.iKey;



        }else{
          iKey = p->iRowid;

        }






        sqlite3_result_int64(ctx, (sqlite3_int64)iKey);
      }
      break;
    }
    case JEACH_VALUE: {
      if( pThis->jnFlags & JNODE_LABEL ) pThis++;
      jsonReturn(&p->sParse, pThis, ctx, 0);
      break;
    }
    case JEACH_TYPE: {
      if( pThis->jnFlags & JNODE_LABEL ) pThis++;

      sqlite3_result_text(ctx, jsonType[pThis->eType], -1, SQLITE_STATIC);
      break;
    }
    case JEACH_ATOM: {
      if( pThis->jnFlags & JNODE_LABEL ) pThis++;
      if( pThis->eType>=JSON_ARRAY ) break;
      jsonReturn(&p->sParse, pThis, ctx, 0);

      break;
    }
    case JEACH_ID: {
      sqlite3_result_int64(ctx,
         (sqlite3_int64)p->i + ((pThis->jnFlags & JNODE_LABEL)!=0));
      break;
    }
    case JEACH_PARENT: {
      if( p->i>p->iBegin && p->bRecursive ){
        sqlite3_result_int64(ctx, (sqlite3_int64)p->sParse.aUp[p->i]);
      }
      break;
    }
    case JEACH_FULLKEY: {
      JsonString x;
      jsonInit(&x, ctx);
      if( p->bRecursive ){
        jsonEachComputePath(p, &x, p->i);
      }else{
        if( p->zRoot ){
          jsonAppendRaw(&x, p->zRoot, (int)strlen(p->zRoot));
        }else{
          jsonAppendChar(&x, '$');
        }
        if( p->eType==JSON_ARRAY ){
          jsonPrintf(30, &x, "[%d]", p->iRowid);

        }else if( p->eType==JSON_OBJECT ){
          jsonAppendObjectPathElement(&x, pThis);
        }
      }
      jsonResult(&x);
      break;
    }
    case JEACH_PATH: {
      if( p->bRecursive ){
        JsonString x;
        jsonInit(&x, ctx);
        jsonEachComputePath(p, &x, p->sParse.aUp[p->i]);
        jsonResult(&x);

        break;
      }
      /* For json_each() path and root are the same so fall through
      ** into the root case */
      /* no break */ deliberate_fall_through
    }
    default: {
      const char *zRoot = p->zRoot;
      if( zRoot==0 ) zRoot = "$";
      sqlite3_result_text(ctx, zRoot, -1, SQLITE_STATIC);
      break;
    }
    case JEACH_JSON: {



      assert( i==JEACH_JSON );
      sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC);

      break;
    }
  }
  return SQLITE_OK;
}

/* Return the current rowid value */







>
|

<

>






>
|





>



|

|
>

















<

>
>


>
>
>


<
<






|
>










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



>

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

<
>
|
<
|
>

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


>
>
>
>
>

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






|


<
|

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

<
>

>
>
>
>
>
>
|




|
|



|
>
|



|
|
|
>



|
<



|
|




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



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

<
<
|



>
>
>
|
|
>







207837
207838
207839
207840
207841
207842
207843
207844
207845
207846

207847
207848
207849
207850
207851
207852
207853
207854
207855
207856
207857
207858
207859
207860
207861
207862
207863
207864
207865
207866
207867
207868
207869
207870
207871
207872
207873
207874
207875
207876
207877
207878
207879
207880
207881
207882
207883
207884
207885
207886

207887
207888
207889
207890
207891
207892
207893
207894
207895
207896


207897
207898
207899
207900
207901
207902
207903
207904
207905
207906
207907
207908
207909
207910
207911
207912
207913
207914
207915
207916
207917
207918
207919
207920
207921
207922
207923
207924
207925
207926
207927
207928
207929
207930
207931
207932
207933
207934
207935
207936
207937
207938
207939
207940
207941
207942
207943
207944
207945
207946
207947
207948
207949
207950
207951
207952
207953
207954
207955
207956
207957
207958
207959
207960
207961
207962
207963
207964
207965
207966
207967
207968


207969
207970
207971
207972
207973

207974
207975
207976
207977
207978
207979
207980
207981
207982

207983
207984


207985



207986
207987

207988
207989
207990
207991
207992
207993
207994
207995

207996
207997

207998
207999
208000

208001
208002
208003
208004
208005
208006








208007
208008


208009
208010

208011






208012
208013
208014
208015
208016
208017
208018
208019
208020

208021
208022
208023
208024
208025
208026


208027
208028
208029
208030

208031
208032
208033
208034

208035
208036
208037
208038
208039
208040
208041
208042
208043
208044
208045












208046
208047
208048
208049
208050
208051
208052
208053
208054
208055
208056
208057

208058
208059
208060
208061
208062
208063
208064

208065
208066
208067
208068
208069
208070
208071
208072
208073

208074
208075
208076
208077
208078
208079
208080
208081
208082
208083
208084
208085
208086
208087
208088
208089
208090
208091
208092
208093
208094
208095
208096
208097
208098
208099
208100
208101
208102
208103
208104
208105

208106
208107
208108
208109
208110
208111
208112
208113
208114





208115


208116


208117
208118
208119




208120
208121
208122



208123
208124
208125
208126
208127




208128


208129
208130
208131
208132
208133
208134
208135
208136
208137
208138
208139
208140
208141
208142
208143
208144
208145
  UNUSED_PARAMETER(argv);
  UNUSED_PARAMETER(argc);
  UNUSED_PARAMETER(pAux);
  rc = sqlite3_declare_vtab(db,
     "CREATE TABLE x(key,value,type,atom,id,parent,fullkey,path,"
                    "json HIDDEN,root HIDDEN)");
  if( rc==SQLITE_OK ){
    pNew = (JsonEachConnection*)sqlite3DbMallocZero(db, sizeof(*pNew));
    *ppVtab = (sqlite3_vtab*)pNew;
    if( pNew==0 ) return SQLITE_NOMEM;

    sqlite3_vtab_config(db, SQLITE_VTAB_INNOCUOUS);
    pNew->db = db;
  }
  return rc;
}

/* destructor for json_each virtual table */
static int jsonEachDisconnect(sqlite3_vtab *pVtab){
  JsonEachConnection *p = (JsonEachConnection*)pVtab;
  sqlite3DbFree(p->db, pVtab);
  return SQLITE_OK;
}

/* constructor for a JsonEachCursor object for json_each(). */
static int jsonEachOpenEach(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
  JsonEachConnection *pVtab = (JsonEachConnection*)p;
  JsonEachCursor *pCur;

  UNUSED_PARAMETER(p);
  pCur = sqlite3DbMallocZero(pVtab->db, sizeof(*pCur));
  if( pCur==0 ) return SQLITE_NOMEM;
  pCur->db = pVtab->db;
  jsonStringZero(&pCur->path);
  *ppCursor = &pCur->base;
  return SQLITE_OK;
}

/* constructor for a JsonEachCursor object for json_tree(). */
static int jsonEachOpenTree(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
  int rc = jsonEachOpenEach(p, ppCursor);
  if( rc==SQLITE_OK ){
    JsonEachCursor *pCur = (JsonEachCursor*)*ppCursor;
    pCur->bRecursive = 1;
  }
  return rc;
}

/* Reset a JsonEachCursor back to its original state.  Free any memory
** held. */
static void jsonEachCursorReset(JsonEachCursor *p){

  jsonParseReset(&p->sParse);
  jsonStringReset(&p->path);
  sqlite3DbFree(p->db, p->aParent);
  p->iRowid = 0;
  p->i = 0;
  p->aParent = 0;
  p->nParent = 0;
  p->nParentAlloc = 0;
  p->iEnd = 0;
  p->eType = 0;


}

/* Destructor for a jsonEachCursor object */
static int jsonEachClose(sqlite3_vtab_cursor *cur){
  JsonEachCursor *p = (JsonEachCursor*)cur;
  jsonEachCursorReset(p);

  sqlite3DbFree(p->db, cur);
  return SQLITE_OK;
}

/* Return TRUE if the jsonEachCursor object has been advanced off the end
** of the JSON object */
static int jsonEachEof(sqlite3_vtab_cursor *cur){
  JsonEachCursor *p = (JsonEachCursor*)cur;
  return p->i >= p->iEnd;
}

/*
** If the cursor is currently pointing at the label of a object entry,
** then return the index of the value.  For all other cases, return the
** current pointer position, which is the value.
*/
static int jsonSkipLabel(JsonEachCursor *p){
  if( p->eType==JSONB_OBJECT ){
    u32 sz = 0;
    u32 n = jsonbPayloadSize(&p->sParse, p->i, &sz);
    return p->i + n + sz;
  }else{
    return p->i;
  }
}

/*
** Append the path name for the current element.
*/
static void jsonAppendPathName(JsonEachCursor *p){
  assert( p->nParent>0 );
  assert( p->eType==JSONB_ARRAY || p->eType==JSONB_OBJECT );
  if( p->eType==JSONB_ARRAY ){
    jsonPrintf(30, &p->path, "[%lld]", p->aParent[p->nParent-1].iKey);
  }else{
    u32 n, sz = 0, k, i;
    const char *z;
    int needQuote = 0;
    n = jsonbPayloadSize(&p->sParse, p->i, &sz);
    k = p->i + n;
    z = (const char*)&p->sParse.aBlob[k];
    if( sz==0 || !sqlite3Isalpha(z[0]) ){
      needQuote = 1;
    }else{
      for(i=0; i<sz; i++){
        if( !sqlite3Isalnum(z[i]) ){
          needQuote = 1;
          break;
        }
      }
    }
    if( needQuote ){
      jsonPrintf(sz+4,&p->path,".\"%.*s\"", sz, z);
    }else{
      jsonPrintf(sz+2,&p->path,".%.*s", sz, z);
    }
  }
}

/* Advance the cursor to the next element for json_tree() */
static int jsonEachNext(sqlite3_vtab_cursor *cur){
  JsonEachCursor *p = (JsonEachCursor*)cur;
  int rc = SQLITE_OK;
  if( p->bRecursive ){
    u8 x;


    u8 levelChange = 0;
    u32 n, sz = 0;
    u32 i = jsonSkipLabel(p);
    x = p->sParse.aBlob[i] & 0x0f;
    n = jsonbPayloadSize(&p->sParse, i, &sz);

    if( x==JSONB_OBJECT || x==JSONB_ARRAY ){
      JsonParent *pParent;
      if( p->nParent>=p->nParentAlloc ){
        JsonParent *pNew;
        u64 nNew;
        nNew = p->nParentAlloc*2 + 3;
        pNew = sqlite3DbRealloc(p->db, p->aParent, sizeof(JsonParent)*nNew);
        if( pNew==0 ) return SQLITE_NOMEM;
        p->nParentAlloc = (u32)nNew;

        p->aParent = pNew;
      }


      levelChange = 1;



      pParent = &p->aParent[p->nParent];
      pParent->iHead = p->i;

      pParent->iValue = i;
      pParent->iEnd = i + n + sz;
      pParent->iKey = -1;
      pParent->nPath = (u32)p->path.nUsed;
      if( p->eType && p->nParent ){
        jsonAppendPathName(p);
        if( p->path.eErr ) rc = SQLITE_NOMEM;
      }

      p->nParent++;
      p->i = i + n;

    }else{
      p->i = i + n + sz;
    }

    while( p->nParent>0 && p->i >= p->aParent[p->nParent-1].iEnd ){
      p->nParent--;
      p->path.nUsed = p->aParent[p->nParent].nPath;
      levelChange = 1;
    }
    if( levelChange ){








      if( p->nParent>0 ){
        JsonParent *pParent = &p->aParent[p->nParent-1];


        u32 iVal = pParent->iValue;
        p->eType = p->sParse.aBlob[iVal] & 0x0f;

      }else{






        p->eType = 0;
      }
    }
  }else{
    u32 n, sz = 0;
    u32 i = jsonSkipLabel(p);
    n = jsonbPayloadSize(&p->sParse, i, &sz);
    p->i = i + n + sz;
  }

  if( p->eType==JSONB_ARRAY && p->nParent ){
    p->aParent[p->nParent-1].iKey++;
  }
  p->iRowid++;
  return rc;
}



/* Length of the path for rowid==0 in bRecursive mode.
*/
static int jsonEachPathLength(JsonEachCursor *p){

  u32 n = p->path.nUsed;
  char *z = p->path.zBuf;
  if( p->iRowid==0 && p->bRecursive && n>=2 ){
    while( n>1 ){

      n--;
      if( z[n]=='[' || z[n]=='.' ){
        u32 x, sz = 0;
        char cSaved = z[n];
        z[n] = 0;
        assert( p->sParse.eEdit==0 );
        x = jsonLookupStep(&p->sParse, 0, z+1, 0);
        z[n] = cSaved;
        if( JSON_LOOKUP_ISERROR(x) ) continue;
        if( x + jsonbPayloadSize(&p->sParse, x, &sz) == p->i ) break;
      }












    }
  }
  return n;
}

/* Return the value of a column */
static int jsonEachColumn(
  sqlite3_vtab_cursor *cur,   /* The cursor */
  sqlite3_context *ctx,       /* First argument to sqlite3_result_...() */
  int iColumn                 /* Which column to return */
){
  JsonEachCursor *p = (JsonEachCursor*)cur;

  switch( iColumn ){
    case JEACH_KEY: {
      if( p->nParent==0 ){
        u32 n, j;
        if( p->nRoot==1 ) break;
        j = jsonEachPathLength(p);
        n = p->nRoot - j;

        if( n==0 ){
          break;
        }else if( p->path.zBuf[j]=='[' ){
          i64 x;
          sqlite3Atoi64(&p->path.zBuf[j+1], &x, n-1, SQLITE_UTF8);
          sqlite3_result_int64(ctx, x);
        }else if( p->path.zBuf[j+1]=='"' ){
          sqlite3_result_text(ctx, &p->path.zBuf[j+2], n-3, SQLITE_TRANSIENT);
        }else{

          sqlite3_result_text(ctx, &p->path.zBuf[j+1], n-1, SQLITE_TRANSIENT);
        }
        break;
      }
      if( p->eType==JSONB_OBJECT ){
        jsonReturnFromBlob(&p->sParse, p->i, ctx, 1);
      }else{
        assert( p->eType==JSONB_ARRAY );
        sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iKey);
      }
      break;
    }
    case JEACH_VALUE: {
      u32 i = jsonSkipLabel(p);
      jsonReturnFromBlob(&p->sParse, i, ctx, 1);
      break;
    }
    case JEACH_TYPE: {
      u32 i = jsonSkipLabel(p);
      u8 eType = p->sParse.aBlob[i] & 0x0f;
      sqlite3_result_text(ctx, jsonbType[eType], -1, SQLITE_STATIC);
      break;
    }
    case JEACH_ATOM: {
      u32 i = jsonSkipLabel(p);
      if( (p->sParse.aBlob[i] & 0x0f)<JSONB_ARRAY ){
        jsonReturnFromBlob(&p->sParse, i, ctx, 1);
      }
      break;
    }
    case JEACH_ID: {
      sqlite3_result_int64(ctx, (sqlite3_int64)p->i);

      break;
    }
    case JEACH_PARENT: {
      if( p->nParent>0 && p->bRecursive ){
        sqlite3_result_int64(ctx, p->aParent[p->nParent-1].iHead);
      }
      break;
    }
    case JEACH_FULLKEY: {





      u64 nBase = p->path.nUsed;


      if( p->nParent ) jsonAppendPathName(p);


      sqlite3_result_text64(ctx, p->path.zBuf, p->path.nUsed,
                            SQLITE_TRANSIENT, SQLITE_UTF8);
      p->path.nUsed = nBase;




      break;
    }
    case JEACH_PATH: {



      u32 n = jsonEachPathLength(p);
      sqlite3_result_text64(ctx, p->path.zBuf, n,
                            SQLITE_TRANSIENT, SQLITE_UTF8);
      break;
    }




    default: {


      sqlite3_result_text(ctx, p->path.zBuf, p->nRoot, SQLITE_STATIC);
      break;
    }
    case JEACH_JSON: {
      if( p->sParse.zJson==0 ){
        sqlite3_result_blob(ctx, p->sParse.aBlob, p->sParse.nBlob,
                            SQLITE_STATIC);
      }else{
        sqlite3_result_text(ctx, p->sParse.zJson, -1, SQLITE_STATIC);
      }
      break;
    }
  }
  return SQLITE_OK;
}

/* Return the current rowid value */
206299
206300
206301
206302
206303
206304
206305
206306
206307
206308
206309
206310
206311
206312
206313
206314
206315
206316
206317


206318
206319
206320

206321
206322
206323
206324

206325
206326
206327
206328
206329
206330
206331
206332
206333
206334
206335
206336
206337
206338
206339
206340
206341


206342
206343
206344
206345
206346
206347
206348
206349
206350






206351


206352
206353






206354
206355
206356
206357
206358
206359
206360
206361
206362



206363
206364

206365
206366
206367
206368
206369
206370
206371
206372
206373
206374
206375
206376
206377

206378
206379



206380
206381

206382











206383

206384




206385
206386
206387
206388
206389
206390
206391
206392
/* Start a search on a new JSON string */
static int jsonEachFilter(
  sqlite3_vtab_cursor *cur,
  int idxNum, const char *idxStr,
  int argc, sqlite3_value **argv
){
  JsonEachCursor *p = (JsonEachCursor*)cur;
  const char *z;
  const char *zRoot = 0;
  sqlite3_int64 n;

  UNUSED_PARAMETER(idxStr);
  UNUSED_PARAMETER(argc);
  jsonEachCursorReset(p);
  if( idxNum==0 ) return SQLITE_OK;
  z = (const char*)sqlite3_value_text(argv[0]);
  if( z==0 ) return SQLITE_OK;
  memset(&p->sParse, 0, sizeof(p->sParse));
  p->sParse.nJPRef = 1;


  if( sqlite3ValueIsOfClass(argv[0], sqlite3RCStrUnref) ){
    p->sParse.zJson = sqlite3RCStrRef((char*)z);
  }else{

    n = sqlite3_value_bytes(argv[0]);
    p->sParse.zJson = sqlite3RCStrNew( n+1 );
    if( p->sParse.zJson==0 ) return SQLITE_NOMEM;
    memcpy(p->sParse.zJson, z, (size_t)n+1);

  }
  p->sParse.bJsonIsRCStr = 1;
  p->zJson = p->sParse.zJson;
  if( jsonParse(&p->sParse, 0) ){
    int rc = SQLITE_NOMEM;
    if( p->sParse.oom==0 ){
      sqlite3_free(cur->pVtab->zErrMsg);
      cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON");
      if( cur->pVtab->zErrMsg ) rc = SQLITE_ERROR;
    }
    jsonEachCursorReset(p);
    return rc;
  }else if( p->bRecursive && jsonParseFindParents(&p->sParse) ){
    jsonEachCursorReset(p);
    return SQLITE_NOMEM;
  }else{
    JsonNode *pNode = 0;


    if( idxNum==3 ){
      const char *zErr = 0;
      zRoot = (const char*)sqlite3_value_text(argv[1]);
      if( zRoot==0 ) return SQLITE_OK;
      n = sqlite3_value_bytes(argv[1]);
      p->zRoot = sqlite3_malloc64( n+1 );
      if( p->zRoot==0 ) return SQLITE_NOMEM;
      memcpy(p->zRoot, zRoot, (size_t)n+1);
      if( zRoot[0]!='$' ){






        zErr = zRoot;


      }else{
        pNode = jsonLookupStep(&p->sParse, 0, p->zRoot+1, 0, &zErr);






      }
      if( zErr ){
        sqlite3_free(cur->pVtab->zErrMsg);
        cur->pVtab->zErrMsg = jsonPathSyntaxError(zErr);
        jsonEachCursorReset(p);
        return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
      }else if( pNode==0 ){
        return SQLITE_OK;
      }



    }else{
      pNode = p->sParse.aNode;

    }
    p->iBegin = p->i = (int)(pNode - p->sParse.aNode);
    p->eType = pNode->eType;
    if( p->eType>=JSON_ARRAY ){
      assert( pNode->eU==0 );
      VVA( pNode->eU = 3 );
      pNode->u.iKey = 0;
      p->iEnd = p->i + pNode->n + 1;
      if( p->bRecursive ){
        p->eType = p->sParse.aNode[p->sParse.aUp[p->i]].eType;
        if( p->i>0 && (p->sParse.aNode[p->i-1].jnFlags & JNODE_LABEL)!=0 ){
          p->i--;
        }

      }else{
        p->i++;



      }
    }else{

      p->iEnd = p->i+1;











    }

  }




  return SQLITE_OK;
}

/* The methods of the json_each virtual table */
static sqlite3_module jsonEachModule = {
  0,                         /* iVersion */
  0,                         /* xCreate */
  jsonEachConnect,           /* xConnect */







<

|





<
<


>
>
|
|

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

|


<
<

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







208222
208223
208224
208225
208226
208227
208228

208229
208230
208231
208232
208233
208234
208235


208236
208237
208238
208239
208240
208241
208242
208243
208244

208245
208246
208247
208248


208249

208250


208251
208252
208253






208254
208255
208256

208257
208258




208259
208260
208261
208262
208263
208264
208265
208266
208267
208268
208269
208270
208271
208272
208273
208274
208275
208276
208277

208278
208279
208280
208281


208282
208283
208284
208285
208286
208287
208288
208289











208290
208291
208292
208293
208294
208295
208296
208297
208298
208299
208300
208301
208302
208303
208304
208305
208306
208307
208308
208309
208310
208311
208312
208313
208314
208315
208316
208317
208318
208319
208320
208321
208322
208323
208324
208325
208326
/* Start a search on a new JSON string */
static int jsonEachFilter(
  sqlite3_vtab_cursor *cur,
  int idxNum, const char *idxStr,
  int argc, sqlite3_value **argv
){
  JsonEachCursor *p = (JsonEachCursor*)cur;

  const char *zRoot = 0;
  u32 i, n, sz;

  UNUSED_PARAMETER(idxStr);
  UNUSED_PARAMETER(argc);
  jsonEachCursorReset(p);
  if( idxNum==0 ) return SQLITE_OK;


  memset(&p->sParse, 0, sizeof(p->sParse));
  p->sParse.nJPRef = 1;
  p->sParse.db = p->db;
  if( jsonFuncArgMightBeBinary(argv[0]) ){
    p->sParse.nBlob = sqlite3_value_bytes(argv[0]);
    p->sParse.aBlob = (u8*)sqlite3_value_blob(argv[0]);
  }else{
    p->sParse.zJson = (char*)sqlite3_value_text(argv[0]);
    p->sParse.nJson = sqlite3_value_bytes(argv[0]);

    if( p->sParse.zJson==0 ){
      p->i = p->iEnd = 0;
      return SQLITE_OK;
    }


    if( jsonConvertTextToBlob(&p->sParse, 0) ){

      if( p->sParse.oom ){


        return SQLITE_NOMEM;
      }
      goto json_each_malformed_input;






    }
  }
  if( idxNum==3 ){

    zRoot = (const char*)sqlite3_value_text(argv[1]);
    if( zRoot==0 ) return SQLITE_OK;




    if( zRoot[0]!='$' ){
      sqlite3_free(cur->pVtab->zErrMsg);
      cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot);
      jsonEachCursorReset(p);
      return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
    }
    p->nRoot = sqlite3Strlen30(zRoot);
    if( zRoot[1]==0 ){
      i = p->i = 0;
      p->eType = 0;
    }else{
      i = jsonLookupStep(&p->sParse, 0, zRoot+1, 0);
      if( JSON_LOOKUP_ISERROR(i) ){
        if( i==JSON_LOOKUP_NOTFOUND ){
          p->i = 0;
          p->eType = 0;
          p->iEnd = 0;
          return SQLITE_OK;
        }

        sqlite3_free(cur->pVtab->zErrMsg);
        cur->pVtab->zErrMsg = jsonBadPathError(0, zRoot);
        jsonEachCursorReset(p);
        return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;


      }
      if( p->sParse.iLabel ){
        p->i = p->sParse.iLabel;
        p->eType = JSONB_OBJECT;
      }else{
        p->i = i;
        p->eType = JSONB_ARRAY;
      }











    }
    jsonAppendRaw(&p->path, zRoot, p->nRoot);
  }else{
    i = p->i = 0;
    p->eType = 0;
    p->nRoot = 1;
    jsonAppendRaw(&p->path, "$", 1);
  }
  p->nParent = 0;
  n = jsonbPayloadSize(&p->sParse, i, &sz);
  p->iEnd = i+n+sz;
  if( (p->sParse.aBlob[i] & 0x0f)>=JSONB_ARRAY && !p->bRecursive ){
    p->i = i + n;
    p->eType = p->sParse.aBlob[i] & 0x0f;
    p->aParent = sqlite3DbMallocZero(p->db, sizeof(JsonParent));
    if( p->aParent==0 ) return SQLITE_NOMEM;
    p->nParent = 1;
    p->nParentAlloc = 1;
    p->aParent[0].iKey = 0;
    p->aParent[0].iEnd = p->iEnd;
    p->aParent[0].iHead = p->i;
    p->aParent[0].iValue = i;
  }
  return SQLITE_OK;

json_each_malformed_input:
  sqlite3_free(cur->pVtab->zErrMsg);
  cur->pVtab->zErrMsg = sqlite3_mprintf("malformed JSON");
  jsonEachCursorReset(p);
  return cur->pVtab->zErrMsg ? SQLITE_ERROR : SQLITE_NOMEM;
}

/* The methods of the json_each virtual table */
static sqlite3_module jsonEachModule = {
  0,                         /* iVersion */
  0,                         /* xCreate */
  jsonEachConnect,           /* xConnect */
206447
206448
206449
206450
206451
206452
206453
206454
206455
206456
206457
206458
206459
206460
206461

206462

206463
206464
206465
206466

206467
206468
206469

206470

206471

206472
206473

206474

206475

206476
206477
206478

206479
206480
206481
206482
206483
206484
206485
206486



206487



206488
206489
206490
206491
206492
206493
206494

/*
** Register JSON functions.
*/
SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void){
#ifndef SQLITE_OMIT_JSON
  static FuncDef aJsonFunc[] = {
    /*                     calls sqlite3_result_subtype()                    */
    /*                                  |                                    */
    /*              Uses cache ______   |   __ calls sqlite3_value_subtype() */
    /*                               |  |  |                                 */
    /*          Num args _________   |  |  |   ___ Flags                     */
    /*                            |  |  |  |  |                              */
    /*                            |  |  |  |  |                              */
    JFUNCTION(json,               1, 1, 1, 0, 0,          jsonRemoveFunc),

    JFUNCTION(json_array,        -1, 0, 1, 1, 0,          jsonArrayFunc),

    JFUNCTION(json_array_length,  1, 1, 0, 0, 0,          jsonArrayLengthFunc),
    JFUNCTION(json_array_length,  2, 1, 0, 0, 0,          jsonArrayLengthFunc),
    JFUNCTION(json_error_position,1, 1, 0, 0, 0,          jsonErrorFunc),
    JFUNCTION(json_extract,      -1, 1, 1, 0, 0,          jsonExtractFunc),

    JFUNCTION(->,                 2, 1, 1, 0, JSON_JSON,  jsonExtractFunc),
    JFUNCTION(->>,                2, 1, 0, 0, JSON_SQL,   jsonExtractFunc),
    JFUNCTION(json_insert,       -1, 1, 1, 1, 0,          jsonSetFunc),

    JFUNCTION(json_object,       -1, 0, 1, 1, 0,          jsonObjectFunc),

    JFUNCTION(json_patch,         2, 1, 1, 0, 0,          jsonPatchFunc),

    JFUNCTION(json_quote,         1, 0, 1, 1, 0,          jsonQuoteFunc),
    JFUNCTION(json_remove,       -1, 1, 1, 0, 0,          jsonRemoveFunc),

    JFUNCTION(json_replace,      -1, 1, 1, 1, 0,          jsonReplaceFunc),

    JFUNCTION(json_set,          -1, 1, 1, 1, JSON_ISSET, jsonSetFunc),

    JFUNCTION(json_type,          1, 1, 0, 0, 0,          jsonTypeFunc),
    JFUNCTION(json_type,          2, 1, 0, 0, 0,          jsonTypeFunc),
    JFUNCTION(json_valid,         1, 1, 0, 0, 0,          jsonValidFunc),

#ifdef SQLITE_DEBUG
    JFUNCTION(json_parse,         1, 1, 1, 0, 0,          jsonParseFunc),
    JFUNCTION(json_test1,         1, 1, 0, 1, 0,          jsonTest1Func),
#endif
    WAGGREGATE(json_group_array,  1, 0, 0,
       jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse,
       SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|
       SQLITE_DETERMINISTIC),



    WAGGREGATE(json_group_object, 2, 0, 0,



       jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse,
       SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|
       SQLITE_DETERMINISTIC)
  };
  sqlite3InsertBuiltinFuncs(aJsonFunc, ArraySize(aJsonFunc));
#endif
}







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





>
>
>

>
>
>







208381
208382
208383
208384
208385
208386
208387
208388
208389
208390
208391
208392
208393

208394
208395
208396
208397
208398
208399
208400
208401
208402
208403
208404
208405
208406
208407
208408
208409
208410
208411
208412
208413
208414
208415
208416
208417
208418
208419
208420
208421
208422
208423

208424
208425
208426
208427
208428
208429
208430
208431
208432
208433
208434
208435
208436
208437
208438
208439
208440
208441
208442

/*
** Register JSON functions.
*/
SQLITE_PRIVATE void sqlite3RegisterJsonFunctions(void){
#ifndef SQLITE_OMIT_JSON
  static FuncDef aJsonFunc[] = {
    /*   sqlite3_result_subtype() ----,  ,--- sqlite3_value_subtype()       */
    /*                                |  |                                  */
    /*             Uses cache ------, |  | ,---- Returns JSONB              */
    /*                              | |  | |                                */
    /*     Number of arguments ---, | |  | | ,--- Flags                     */
    /*                            | | |  | | |                              */

    JFUNCTION(json,               1,1,1, 0,0,0,          jsonRemoveFunc),
    JFUNCTION(jsonb,              1,1,0, 0,1,0,          jsonRemoveFunc),
    JFUNCTION(json_array,        -1,0,1, 1,0,0,          jsonArrayFunc),
    JFUNCTION(jsonb_array,       -1,0,1, 1,1,0,          jsonArrayFunc),
    JFUNCTION(json_array_length,  1,1,0, 0,0,0,          jsonArrayLengthFunc),
    JFUNCTION(json_array_length,  2,1,0, 0,0,0,          jsonArrayLengthFunc),
    JFUNCTION(json_error_position,1,1,0, 0,0,0,          jsonErrorFunc),
    JFUNCTION(json_extract,      -1,1,1, 0,0,0,          jsonExtractFunc),
    JFUNCTION(jsonb_extract,     -1,1,0, 0,1,0,          jsonExtractFunc),
    JFUNCTION(->,                 2,1,1, 0,0,JSON_JSON,  jsonExtractFunc),
    JFUNCTION(->>,                2,1,0, 0,0,JSON_SQL,   jsonExtractFunc),
    JFUNCTION(json_insert,       -1,1,1, 1,0,0,          jsonSetFunc),
    JFUNCTION(jsonb_insert,      -1,1,0, 1,1,0,          jsonSetFunc),
    JFUNCTION(json_object,       -1,0,1, 1,0,0,          jsonObjectFunc),
    JFUNCTION(jsonb_object,      -1,0,1, 1,1,0,          jsonObjectFunc),
    JFUNCTION(json_patch,         2,1,1, 0,0,0,          jsonPatchFunc),
    JFUNCTION(jsonb_patch,        2,1,0, 0,1,0,          jsonPatchFunc),
    JFUNCTION(json_quote,         1,0,1, 1,0,0,          jsonQuoteFunc),
    JFUNCTION(json_remove,       -1,1,1, 0,0,0,          jsonRemoveFunc),
    JFUNCTION(jsonb_remove,      -1,1,0, 0,1,0,          jsonRemoveFunc),
    JFUNCTION(json_replace,      -1,1,1, 1,0,0,          jsonReplaceFunc),
    JFUNCTION(jsonb_replace,     -1,1,0, 1,1,0,          jsonReplaceFunc),
    JFUNCTION(json_set,          -1,1,1, 1,0,JSON_ISSET, jsonSetFunc),
    JFUNCTION(jsonb_set,         -1,1,0, 1,1,JSON_ISSET, jsonSetFunc),
    JFUNCTION(json_type,          1,1,0, 0,0,0,          jsonTypeFunc),
    JFUNCTION(json_type,          2,1,0, 0,0,0,          jsonTypeFunc),
    JFUNCTION(json_valid,         1,1,0, 0,0,0,          jsonValidFunc),
    JFUNCTION(json_valid,         2,1,0, 0,0,0,          jsonValidFunc),
#if SQLITE_DEBUG
    JFUNCTION(json_parse,         1,1,0, 0,0,0,          jsonParseFunc),

#endif
    WAGGREGATE(json_group_array,  1, 0, 0,
       jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse,
       SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|
       SQLITE_DETERMINISTIC),
    WAGGREGATE(jsonb_group_array, 1, JSON_BLOB, 0,
       jsonArrayStep, jsonArrayFinal, jsonArrayValue, jsonGroupInverse,
       SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC),
    WAGGREGATE(json_group_object, 2, 0, 0,
       jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse,
       SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|SQLITE_DETERMINISTIC),
    WAGGREGATE(jsonb_group_object,2, JSON_BLOB, 0,
       jsonObjectStep, jsonObjectFinal, jsonObjectValue, jsonGroupInverse,
       SQLITE_SUBTYPE|SQLITE_RESULT_SUBTYPE|SQLITE_UTF8|
       SQLITE_DETERMINISTIC)
  };
  sqlite3InsertBuiltinFuncs(aJsonFunc, ArraySize(aJsonFunc));
#endif
}
207231
207232
207233
207234
207235
207236
207237
207238
207239
207240
207241
207242
207243
207244
207245
  int rc = SQLITE_OK;
  RtreeNode *pNode = 0;

  /* Check if the requested node is already in the hash table. If so,
  ** increase its reference count and return it.
  */
  if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){
    if( pParent && pParent!=pNode->pParent ){
      RTREE_IS_CORRUPT(pRtree);
      return SQLITE_CORRUPT_VTAB;
    }
    pNode->nRef++;
    *ppNode = pNode;
    return SQLITE_OK;
  }







|







209179
209180
209181
209182
209183
209184
209185
209186
209187
209188
209189
209190
209191
209192
209193
  int rc = SQLITE_OK;
  RtreeNode *pNode = 0;

  /* Check if the requested node is already in the hash table. If so,
  ** increase its reference count and return it.
  */
  if( (pNode = nodeHashLookup(pRtree, iNode))!=0 ){
    if( pParent && ALWAYS(pParent!=pNode->pParent) ){
      RTREE_IS_CORRUPT(pRtree);
      return SQLITE_CORRUPT_VTAB;
    }
    pNode->nRef++;
    *ppNode = pNode;
    return SQLITE_OK;
  }
209966
209967
209968
209969
209970
209971
209972
209973
209974
209975
209976
209977
209978
209979
209980
    if( zSql ){
      rc = sqlite3_prepare_v3(db, zSql, -1, f, appStmt[i], 0);
    }else{
      rc = SQLITE_NOMEM;
    }
    sqlite3_free(zSql);
  }
  if( pRtree->nAux ){
    pRtree->zReadAuxSql = sqlite3_mprintf(
       "SELECT * FROM \"%w\".\"%w_rowid\" WHERE rowid=?1",
       zDb, zPrefix);
    if( pRtree->zReadAuxSql==0 ){
      rc = SQLITE_NOMEM;
    }else{
      sqlite3_str *p = sqlite3_str_new(db);







|







211914
211915
211916
211917
211918
211919
211920
211921
211922
211923
211924
211925
211926
211927
211928
    if( zSql ){
      rc = sqlite3_prepare_v3(db, zSql, -1, f, appStmt[i], 0);
    }else{
      rc = SQLITE_NOMEM;
    }
    sqlite3_free(zSql);
  }
  if( pRtree->nAux && rc!=SQLITE_NOMEM ){
    pRtree->zReadAuxSql = sqlite3_mprintf(
       "SELECT * FROM \"%w\".\"%w_rowid\" WHERE rowid=?1",
       zDb, zPrefix);
    if( pRtree->zReadAuxSql==0 ){
      rc = SQLITE_NOMEM;
    }else{
      sqlite3_str *p = sqlite3_str_new(db);
210655
210656
210657
210658
210659
210660
210661
210662
210663
210664
210665
210666
210667
210668
210669
210670
210671
210672
210673
210674
210675
210676
210677
  /* Initialize the context object */
  memset(&check, 0, sizeof(check));
  check.db = db;
  check.zDb = zDb;
  check.zTab = zTab;

  /* Find the number of auxiliary columns */
  if( check.rc==SQLITE_OK ){
    pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.'%q_rowid'", zDb, zTab);
    if( pStmt ){
      nAux = sqlite3_column_count(pStmt) - 2;
      sqlite3_finalize(pStmt);
    }else
    if( check.rc!=SQLITE_NOMEM ){
      check.rc = SQLITE_OK;
    }
  }

  /* Find number of dimensions in the rtree table. */
  pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.%Q", zDb, zTab);
  if( pStmt ){
    int rc;
    check.nDim = (sqlite3_column_count(pStmt) - 1 - nAux) / 2;







<
|
|
|
|
|
|
|
<







212603
212604
212605
212606
212607
212608
212609

212610
212611
212612
212613
212614
212615
212616

212617
212618
212619
212620
212621
212622
212623
  /* Initialize the context object */
  memset(&check, 0, sizeof(check));
  check.db = db;
  check.zDb = zDb;
  check.zTab = zTab;

  /* Find the number of auxiliary columns */

  pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.'%q_rowid'", zDb, zTab);
  if( pStmt ){
    nAux = sqlite3_column_count(pStmt) - 2;
    sqlite3_finalize(pStmt);
  }else
  if( check.rc!=SQLITE_NOMEM ){
    check.rc = SQLITE_OK;

  }

  /* Find number of dimensions in the rtree table. */
  pStmt = rtreeCheckPrepare(&check, "SELECT * FROM %Q.%Q", zDb, zTab);
  if( pStmt ){
    int rc;
    check.nDim = (sqlite3_column_count(pStmt) - 1 - nAux) / 2;
210718
210719
210720
210721
210722
210723
210724

210725
210726
210727
210728
210729
210730
210731
  UNUSED_PARAMETER(zSchema);
  UNUSED_PARAMETER(zName);
  UNUSED_PARAMETER(isQuick);
  rc = rtreeCheckTable(pRtree->db, pRtree->zDb, pRtree->zName, pzErr);
  if( rc==SQLITE_OK && *pzErr ){
    *pzErr = sqlite3_mprintf("In RTree %s.%s:\n%z",
                 pRtree->zDb, pRtree->zName, *pzErr);

  }
  return rc;
}

/*
** Usage:
**







>







212664
212665
212666
212667
212668
212669
212670
212671
212672
212673
212674
212675
212676
212677
212678
  UNUSED_PARAMETER(zSchema);
  UNUSED_PARAMETER(zName);
  UNUSED_PARAMETER(isQuick);
  rc = rtreeCheckTable(pRtree->db, pRtree->zDb, pRtree->zName, pzErr);
  if( rc==SQLITE_OK && *pzErr ){
    *pzErr = sqlite3_mprintf("In RTree %s.%s:\n%z",
                 pRtree->zDb, pRtree->zName, *pzErr);
    if( (*pzErr)==0 ) rc = SQLITE_NOMEM;
  }
  return rc;
}

/*
** Usage:
**
223388
223389
223390
223391
223392
223393
223394
223395
223396
223397
223398
223399
223400
223401
223402
223403
223404
  sqlite3_mutex_leave(sqlite3_db_mutex(db));
  sqlite3ValueFree(pSession->pZeroBlob);

  /* Delete all attached table objects. And the contents of their
  ** associated hash-tables. */
  sessionDeleteTable(pSession, pSession->pTable);

  /* Assert that all allocations have been freed and then free the
  ** session object itself. */
  // assert( pSession->nMalloc==0 );
  sqlite3_free(pSession);
}

/*
** Set a table filter on a Session Object.
*/
SQLITE_API void sqlite3session_table_filter(







<
|
<







225335
225336
225337
225338
225339
225340
225341

225342

225343
225344
225345
225346
225347
225348
225349
  sqlite3_mutex_leave(sqlite3_db_mutex(db));
  sqlite3ValueFree(pSession->pZeroBlob);

  /* Delete all attached table objects. And the contents of their
  ** associated hash-tables. */
  sessionDeleteTable(pSession, pSession->pTable);


  /* Free the session object. */

  sqlite3_free(pSession);
}

/*
** Set a table filter on a Session Object.
*/
SQLITE_API void sqlite3session_table_filter(
227603
227604
227605
227606
227607
227608
227609



227610
227611
227612
227613
227614
227615
227616
227617
227618
227619
227620


227621
227622
227623
227624
227625
227626
227627
227628
227629
227630
227631
227632
227633
227634
227635
227636
227637
227638

227639
227640
227641
227642
227643
227644
227645
227646
227647
227648
227649
227650
**   an OOM condition or IO error), an appropriate SQLite error code is
**   returned.
**
**   This function may be quite inefficient if used with an FTS5 table
**   created with the "columnsize=0" option.
**
** xColumnText:



**   This function attempts to retrieve the text of column iCol of the
**   current document. If successful, (*pz) is set to point to a buffer
**   containing the text in utf-8 encoding, (*pn) is set to the size in bytes
**   (not characters) of the buffer and SQLITE_OK is returned. Otherwise,
**   if an error occurs, an SQLite error code is returned and the final values
**   of (*pz) and (*pn) are undefined.
**
** xPhraseCount:
**   Returns the number of phrases in the current query expression.
**
** xPhraseSize:


**   Returns the number of tokens in phrase iPhrase of the query. Phrases
**   are numbered starting from zero.
**
** xInstCount:
**   Set *pnInst to the total number of occurrences of all phrases within
**   the query within the current row. Return SQLITE_OK if successful, or
**   an error code (i.e. SQLITE_NOMEM) if an error occurs.
**
**   This API can be quite slow if used with an FTS5 table created with the
**   "detail=none" or "detail=column" option. If the FTS5 table is created
**   with either "detail=none" or "detail=column" and "content=" option
**   (i.e. if it is a contentless table), then this API always returns 0.
**
** xInst:
**   Query for the details of phrase match iIdx within the current row.
**   Phrase matches are numbered starting from zero, so the iIdx argument
**   should be greater than or equal to zero and smaller than the value
**   output by xInstCount().

**
**   Usually, output parameter *piPhrase is set to the phrase number, *piCol
**   to the column in which it occurs and *piOff the token offset of the
**   first token of the phrase. Returns SQLITE_OK if successful, or an error
**   code (i.e. SQLITE_NOMEM) if an error occurs.
**
**   This API can be quite slow if used with an FTS5 table created with the
**   "detail=none" or "detail=column" option.
**
** xRowid:
**   Returns the rowid of the current row.
**







>
>
>
|
|









>
>
|
|















|
>

|

|
|







229548
229549
229550
229551
229552
229553
229554
229555
229556
229557
229558
229559
229560
229561
229562
229563
229564
229565
229566
229567
229568
229569
229570
229571
229572
229573
229574
229575
229576
229577
229578
229579
229580
229581
229582
229583
229584
229585
229586
229587
229588
229589
229590
229591
229592
229593
229594
229595
229596
229597
229598
229599
229600
229601
**   an OOM condition or IO error), an appropriate SQLite error code is
**   returned.
**
**   This function may be quite inefficient if used with an FTS5 table
**   created with the "columnsize=0" option.
**
** xColumnText:
**   If parameter iCol is less than zero, or greater than or equal to the
**   number of columns in the table, SQLITE_RANGE is returned.
**
**   Otherwise, this function attempts to retrieve the text of column iCol of
**   the current document. If successful, (*pz) is set to point to a buffer
**   containing the text in utf-8 encoding, (*pn) is set to the size in bytes
**   (not characters) of the buffer and SQLITE_OK is returned. Otherwise,
**   if an error occurs, an SQLite error code is returned and the final values
**   of (*pz) and (*pn) are undefined.
**
** xPhraseCount:
**   Returns the number of phrases in the current query expression.
**
** xPhraseSize:
**   If parameter iCol is less than zero, or greater than or equal to the
**   number of phrases in the current query, as returned by xPhraseCount,
**   0 is returned. Otherwise, this function returns the number of tokens in
**   phrase iPhrase of the query. Phrases are numbered starting from zero.
**
** xInstCount:
**   Set *pnInst to the total number of occurrences of all phrases within
**   the query within the current row. Return SQLITE_OK if successful, or
**   an error code (i.e. SQLITE_NOMEM) if an error occurs.
**
**   This API can be quite slow if used with an FTS5 table created with the
**   "detail=none" or "detail=column" option. If the FTS5 table is created
**   with either "detail=none" or "detail=column" and "content=" option
**   (i.e. if it is a contentless table), then this API always returns 0.
**
** xInst:
**   Query for the details of phrase match iIdx within the current row.
**   Phrase matches are numbered starting from zero, so the iIdx argument
**   should be greater than or equal to zero and smaller than the value
**   output by xInstCount(). If iIdx is less than zero or greater than
**   or equal to the value returned by xInstCount(), SQLITE_RANGE is returned.
**
**   Otherwise, output parameter *piPhrase is set to the phrase number, *piCol
**   to the column in which it occurs and *piOff the token offset of the
**   first token of the phrase. SQLITE_OK is returned if successful, or an
**   error code (i.e. SQLITE_NOMEM) if an error occurs.
**
**   This API can be quite slow if used with an FTS5 table created with the
**   "detail=none" or "detail=column" option.
**
** xRowid:
**   Returns the rowid of the current row.
**
227661
227662
227663
227664
227665
227666
227667




227668
227669
227670
227671
227672
227673
227674
**   current query is executed. Any column filter that applies to
**   phrase iPhrase of the current query is included in $p. For each
**   row visited, the callback function passed as the fourth argument
**   is invoked. The context and API objects passed to the callback
**   function may be used to access the properties of each matched row.
**   Invoking Api.xUserData() returns a copy of the pointer passed as
**   the third argument to pUserData.




**
**   If the callback function returns any value other than SQLITE_OK, the
**   query is abandoned and the xQueryPhrase function returns immediately.
**   If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK.
**   Otherwise, the error code is propagated upwards.
**
**   If the query runs to completion without incident, SQLITE_OK is returned.







>
>
>
>







229612
229613
229614
229615
229616
229617
229618
229619
229620
229621
229622
229623
229624
229625
229626
229627
229628
229629
**   current query is executed. Any column filter that applies to
**   phrase iPhrase of the current query is included in $p. For each
**   row visited, the callback function passed as the fourth argument
**   is invoked. The context and API objects passed to the callback
**   function may be used to access the properties of each matched row.
**   Invoking Api.xUserData() returns a copy of the pointer passed as
**   the third argument to pUserData.
**
**   If parameter iPhrase is less than zero, or greater than or equal to
**   the number of phrases in the query, as returned by xPhraseCount(),
**   this function returns SQLITE_RANGE.
**
**   If the callback function returns any value other than SQLITE_OK, the
**   query is abandoned and the xQueryPhrase function returns immediately.
**   If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK.
**   Otherwise, the error code is propagated upwards.
**
**   If the query runs to completion without incident, SQLITE_OK is returned.
227776
227777
227778
227779
227780
227781
227782

































227783
227784
227785
227786
227787
227788
227789
227790
227791
227792
**   xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext
**   (or xInst/xInstCount). The chief advantage of this API is that it is
**   significantly more efficient than those alternatives when used with
**   "detail=column" tables.
**
** xPhraseNextColumn()
**   See xPhraseFirstColumn above.

































*/
struct Fts5ExtensionApi {
  int iVersion;                   /* Currently always set to 2 */

  void *(*xUserData)(Fts5Context*);

  int (*xColumnCount)(Fts5Context*);
  int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow);
  int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken);








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


|







229731
229732
229733
229734
229735
229736
229737
229738
229739
229740
229741
229742
229743
229744
229745
229746
229747
229748
229749
229750
229751
229752
229753
229754
229755
229756
229757
229758
229759
229760
229761
229762
229763
229764
229765
229766
229767
229768
229769
229770
229771
229772
229773
229774
229775
229776
229777
229778
229779
229780
**   xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext
**   (or xInst/xInstCount). The chief advantage of this API is that it is
**   significantly more efficient than those alternatives when used with
**   "detail=column" tables.
**
** xPhraseNextColumn()
**   See xPhraseFirstColumn above.
**
** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken)
**   This is used to access token iToken of phrase iPhrase of the current
**   query. Before returning, output parameter *ppToken is set to point
**   to a buffer containing the requested token, and *pnToken to the
**   size of this buffer in bytes.
**
**   If iPhrase or iToken are less than zero, or if iPhrase is greater than
**   or equal to the number of phrases in the query as reported by
**   xPhraseCount(), or if iToken is equal to or greater than the number of
**   tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken
     are both zeroed.
**
**   The output text is not a copy of the query text that specified the
**   token. It is the output of the tokenizer module. For tokendata=1
**   tables, this includes any embedded 0x00 and trailing data.
**
** 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.
*/
struct Fts5ExtensionApi {
  int iVersion;                   /* Currently always set to 3 */

  void *(*xUserData)(Fts5Context*);

  int (*xColumnCount)(Fts5Context*);
  int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow);
  int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken);

227813
227814
227815
227816
227817
227818
227819







227820
227821
227822
227823
227824
227825
227826
  void *(*xGetAuxdata)(Fts5Context*, int bClear);

  int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*);
  void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff);

  int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
  void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);







};

/*
** CUSTOM AUXILIARY FUNCTIONS
*************************************************************************/

/*************************************************************************







>
>
>
>
>
>
>







229801
229802
229803
229804
229805
229806
229807
229808
229809
229810
229811
229812
229813
229814
229815
229816
229817
229818
229819
229820
229821
  void *(*xGetAuxdata)(Fts5Context*, int bClear);

  int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*);
  void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff);

  int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
  void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);

  /* Below this point are iVersion>=3 only */
  int (*xQueryToken)(Fts5Context*,
      int iPhrase, int iToken,
      const char **ppToken, int *pnToken
  );
  int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*);
};

/*
** CUSTOM AUXILIARY FUNCTIONS
*************************************************************************/

/*************************************************************************
228287
228288
228289
228290
228291
228292
228293

228294
228295
228296
228297
228298
228299
228300
  int nPrefix;                    /* Number of prefix indexes */
  int *aPrefix;                   /* Sizes in bytes of nPrefix prefix indexes */
  int eContent;                   /* An FTS5_CONTENT value */
  int bContentlessDelete;         /* "contentless_delete=" option (dflt==0) */
  char *zContent;                 /* content table */
  char *zContentRowid;            /* "content_rowid=" option value */
  int bColumnsize;                /* "columnsize=" option value (dflt==1) */

  int eDetail;                    /* FTS5_DETAIL_XXX value */
  char *zContentExprlist;
  Fts5Tokenizer *pTok;
  fts5_tokenizer *pTokApi;
  int bLock;                      /* True when table is preparing statement */
  int ePattern;                   /* FTS_PATTERN_XXX constant */








>







230282
230283
230284
230285
230286
230287
230288
230289
230290
230291
230292
230293
230294
230295
230296
  int nPrefix;                    /* Number of prefix indexes */
  int *aPrefix;                   /* Sizes in bytes of nPrefix prefix indexes */
  int eContent;                   /* An FTS5_CONTENT value */
  int bContentlessDelete;         /* "contentless_delete=" option (dflt==0) */
  char *zContent;                 /* content table */
  char *zContentRowid;            /* "content_rowid=" option value */
  int bColumnsize;                /* "columnsize=" option value (dflt==1) */
  int bTokendata;                 /* "tokendata=" option value (dflt==0) */
  int eDetail;                    /* FTS5_DETAIL_XXX value */
  char *zContentExprlist;
  Fts5Tokenizer *pTok;
  fts5_tokenizer *pTokApi;
  int bLock;                      /* True when table is preparing statement */
  int ePattern;                   /* FTS_PATTERN_XXX constant */

228475
228476
228477
228478
228479
228480
228481
228482
228483
228484
228485
228486
228487
228488
228489
228490
228491
228492


228493
228494
228495
228496
228497
228498
228499
};

#define sqlite3Fts5IterEof(x) ((x)->bEof)

/*
** Values used as part of the flags argument passed to IndexQuery().
*/
#define FTS5INDEX_QUERY_PREFIX     0x0001   /* Prefix query */
#define FTS5INDEX_QUERY_DESC       0x0002   /* Docs in descending rowid order */
#define FTS5INDEX_QUERY_TEST_NOIDX 0x0004   /* Do not use prefix index */
#define FTS5INDEX_QUERY_SCAN       0x0008   /* Scan query (fts5vocab) */

/* The following are used internally by the fts5_index.c module. They are
** defined here only to make it easier to avoid clashes with the flags
** above. */
#define FTS5INDEX_QUERY_SKIPEMPTY  0x0010
#define FTS5INDEX_QUERY_NOOUTPUT   0x0020
#define FTS5INDEX_QUERY_SKIPHASH   0x0040



/*
** Create/destroy an Fts5Index object.
*/
static int sqlite3Fts5IndexOpen(Fts5Config *pConfig, int bCreate, Fts5Index**, char**);
static int sqlite3Fts5IndexClose(Fts5Index *p);








|
|
|
|




|
|
|
>
>







230471
230472
230473
230474
230475
230476
230477
230478
230479
230480
230481
230482
230483
230484
230485
230486
230487
230488
230489
230490
230491
230492
230493
230494
230495
230496
230497
};

#define sqlite3Fts5IterEof(x) ((x)->bEof)

/*
** Values used as part of the flags argument passed to IndexQuery().
*/
#define FTS5INDEX_QUERY_PREFIX      0x0001  /* Prefix query */
#define FTS5INDEX_QUERY_DESC        0x0002  /* Docs in descending rowid order */
#define FTS5INDEX_QUERY_TEST_NOIDX  0x0004  /* Do not use prefix index */
#define FTS5INDEX_QUERY_SCAN        0x0008  /* Scan query (fts5vocab) */

/* The following are used internally by the fts5_index.c module. They are
** defined here only to make it easier to avoid clashes with the flags
** above. */
#define FTS5INDEX_QUERY_SKIPEMPTY   0x0010
#define FTS5INDEX_QUERY_NOOUTPUT    0x0020
#define FTS5INDEX_QUERY_SKIPHASH    0x0040
#define FTS5INDEX_QUERY_NOTOKENDATA 0x0080
#define FTS5INDEX_QUERY_SCANONETERM 0x0100

/*
** Create/destroy an Fts5Index object.
*/
static int sqlite3Fts5IndexOpen(Fts5Config *pConfig, int bCreate, Fts5Index**, char**);
static int sqlite3Fts5IndexClose(Fts5Index *p);

228554
228555
228556
228557
228558
228559
228560




228561
228562
228563
228564
228565
228566
228567
*/
static const char *sqlite3Fts5IterTerm(Fts5IndexIter*, int*);
static int sqlite3Fts5IterNextScan(Fts5IndexIter*);
static void *sqlite3Fts5StructureRef(Fts5Index*);
static void sqlite3Fts5StructureRelease(void*);
static int sqlite3Fts5StructureTest(Fts5Index*, void*);






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







>
>
>
>







230552
230553
230554
230555
230556
230557
230558
230559
230560
230561
230562
230563
230564
230565
230566
230567
230568
230569
*/
static const char *sqlite3Fts5IterTerm(Fts5IndexIter*, int*);
static int sqlite3Fts5IterNextScan(Fts5IndexIter*);
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.
228630
228631
228632
228633
228634
228635
228636







228637
228638
228639
228640
228641
228642
228643
static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge);
static int sqlite3Fts5IndexReset(Fts5Index *p);

static int sqlite3Fts5IndexLoadConfig(Fts5Index *p);

static int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin);
static int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid);








/*
** End of interface to code in fts5_index.c.
**************************************************************************/

/**************************************************************************
** Interface to code in fts5_varint.c.







>
>
>
>
>
>
>







230632
230633
230634
230635
230636
230637
230638
230639
230640
230641
230642
230643
230644
230645
230646
230647
230648
230649
230650
230651
230652
static int sqlite3Fts5IndexMerge(Fts5Index *p, int nMerge);
static int sqlite3Fts5IndexReset(Fts5Index *p);

static int sqlite3Fts5IndexLoadConfig(Fts5Index *p);

static int sqlite3Fts5IndexGetOrigin(Fts5Index *p, i64 *piOrigin);
static int sqlite3Fts5IndexContentlessDelete(Fts5Index *p, i64 iOrigin, i64 iRowid);

static void sqlite3Fts5IndexIterClearTokendata(Fts5IndexIter*);

/* Used to populate hash tables for xInstToken in detail=none/column mode. */
static int sqlite3Fts5IndexIterWriteTokendata(
    Fts5IndexIter*, const char*, int, i64 iRowid, int iCol, int iOff
);

/*
** End of interface to code in fts5_index.c.
**************************************************************************/

/**************************************************************************
** Interface to code in fts5_varint.c.
228736
228737
228738
228739
228740
228741
228742

228743
228744
228745
228746
228747
228748
228749
  Fts5Hash*,                      /* Hash table to query */
  const char *pTerm, int nTerm    /* Query prefix */
);
static void sqlite3Fts5HashScanNext(Fts5Hash*);
static int sqlite3Fts5HashScanEof(Fts5Hash*);
static void sqlite3Fts5HashScanEntry(Fts5Hash *,
  const char **pzTerm,            /* OUT: term (nul-terminated) */

  const u8 **ppDoclist,           /* OUT: pointer to doclist */
  int *pnDoclist                  /* OUT: size of doclist in bytes */
);



/*







>







230745
230746
230747
230748
230749
230750
230751
230752
230753
230754
230755
230756
230757
230758
230759
  Fts5Hash*,                      /* Hash table to query */
  const char *pTerm, int nTerm    /* Query prefix */
);
static void sqlite3Fts5HashScanNext(Fts5Hash*);
static int sqlite3Fts5HashScanEof(Fts5Hash*);
static void sqlite3Fts5HashScanEntry(Fts5Hash *,
  const char **pzTerm,            /* OUT: term (nul-terminated) */
  int *pnTerm,                    /* OUT: Size of term in bytes */
  const u8 **ppDoclist,           /* OUT: pointer to doclist */
  int *pnDoclist                  /* OUT: size of doclist in bytes */
);



/*
228861
228862
228863
228864
228865
228866
228867




228868
228869
228870
228871
228872
228873
228874
    Fts5Config*, Fts5Expr*, Fts5PoslistPopulator*, int, const char*, int
);
static void sqlite3Fts5ExprCheckPoslists(Fts5Expr*, i64);

static int sqlite3Fts5ExprClonePhrase(Fts5Expr*, int, Fts5Expr**);

static int sqlite3Fts5ExprPhraseCollist(Fts5Expr *, int, const u8 **, int *);





/*******************************************
** The fts5_expr.c API above this point is used by the other hand-written
** C code in this module. The interfaces below this point are called by
** the parser code in fts5parse.y.  */

static void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...);







>
>
>
>







230871
230872
230873
230874
230875
230876
230877
230878
230879
230880
230881
230882
230883
230884
230885
230886
230887
230888
    Fts5Config*, Fts5Expr*, Fts5PoslistPopulator*, int, const char*, int
);
static void sqlite3Fts5ExprCheckPoslists(Fts5Expr*, i64);

static int sqlite3Fts5ExprClonePhrase(Fts5Expr*, int, Fts5Expr**);

static int sqlite3Fts5ExprPhraseCollist(Fts5Expr *, int, const u8 **, int *);

static int sqlite3Fts5ExprQueryToken(Fts5Expr*, int, int, const char**, int*);
static int sqlite3Fts5ExprInstToken(Fts5Expr*, i64, int, int, int, int, const char**, int*);
static void sqlite3Fts5ExprClearTokens(Fts5Expr*);

/*******************************************
** The fts5_expr.c API above this point is used by the other hand-written
** C code in this module. The interfaces below this point are called by
** the parser code in fts5parse.y.  */

static void sqlite3Fts5ParseError(Fts5Parse *pParse, const char *zFmt, ...);
230677
230678
230679
230680
230681
230682
230683








230684
230685
230686
230687
230688
230689
230690

    if( rc==SQLITE_OK ){
      rc = fts5CInstIterNext(&p->iter);
    }
  }

  if( iPos==p->iRangeEnd ){








    fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
    p->iOff = iEndOff;
  }

  return rc;
}








>
>
>
>
>
>
>
>







232691
232692
232693
232694
232695
232696
232697
232698
232699
232700
232701
232702
232703
232704
232705
232706
232707
232708
232709
232710
232711
232712

    if( rc==SQLITE_OK ){
      rc = fts5CInstIterNext(&p->iter);
    }
  }

  if( iPos==p->iRangeEnd ){
    if( p->bOpen ){
      if( p->iter.iStart>=0 && iPos>=p->iter.iStart ){
        fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
        p->iOff = iEndOff;
      }
      fts5HighlightAppend(&rc, p, p->zClose, -1);
      p->bOpen = 0;
    }
    fts5HighlightAppend(&rc, p, &p->zIn[p->iOff], iEndOff - p->iOff);
    p->iOff = iEndOff;
  }

  return rc;
}

230710
230711
230712
230713
230714
230715
230716
230717


230718
230719
230720
230721
230722
230723
230724
230725

  iCol = sqlite3_value_int(apVal[0]);
  memset(&ctx, 0, sizeof(HighlightContext));
  ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]);
  ctx.zClose = (const char*)sqlite3_value_text(apVal[2]);
  ctx.iRangeEnd = -1;
  rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn);



  if( ctx.zIn ){
    if( rc==SQLITE_OK ){
      rc = fts5CInstIterInit(pApi, pFts, iCol, &ctx.iter);
    }

    if( rc==SQLITE_OK ){
      rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
    }







|
>
>
|







232732
232733
232734
232735
232736
232737
232738
232739
232740
232741
232742
232743
232744
232745
232746
232747
232748
232749

  iCol = sqlite3_value_int(apVal[0]);
  memset(&ctx, 0, sizeof(HighlightContext));
  ctx.zOpen = (const char*)sqlite3_value_text(apVal[1]);
  ctx.zClose = (const char*)sqlite3_value_text(apVal[2]);
  ctx.iRangeEnd = -1;
  rc = pApi->xColumnText(pFts, iCol, &ctx.zIn, &ctx.nIn);
  if( rc==SQLITE_RANGE ){
    sqlite3_result_text(pCtx, "", -1, SQLITE_STATIC);
    rc = SQLITE_OK;
  }else if( ctx.zIn ){
    if( rc==SQLITE_OK ){
      rc = fts5CInstIterInit(pApi, pFts, iCol, &ctx.iter);
    }

    if( rc==SQLITE_OK ){
      rc = pApi->xTokenize(pFts, ctx.zIn, ctx.nIn, (void*)&ctx,fts5HighlightCb);
    }
231278
231279
231280
231281
231282
231283
231284

231285
231286
231287
231288
231289
231290
231291
  int *pRc,
  Fts5Buffer *pBuf,
  u32 nData,
  const u8 *pData
){
  if( nData ){
    if( fts5BufferGrow(pRc, pBuf, nData) ) return;

    memcpy(&pBuf->p[pBuf->n], pData, nData);
    pBuf->n += nData;
  }
}

/*
** Append the nul-terminated string zStr to the buffer pBuf. This function







>







233302
233303
233304
233305
233306
233307
233308
233309
233310
233311
233312
233313
233314
233315
233316
  int *pRc,
  Fts5Buffer *pBuf,
  u32 nData,
  const u8 *pData
){
  if( nData ){
    if( fts5BufferGrow(pRc, pBuf, nData) ) return;
    assert( pBuf->p!=0 );
    memcpy(&pBuf->p[pBuf->n], pData, nData);
    pBuf->n += nData;
  }
}

/*
** Append the nul-terminated string zStr to the buffer pBuf. This function
231379
231380
231381
231382
231383
231384
231385

231386
231387
231388
231389
231390
231391
231392

231393
231394
231395
231396
231397
231398
231399

static int sqlite3Fts5PoslistNext64(
  const u8 *a, int n,             /* Buffer containing poslist */
  int *pi,                        /* IN/OUT: Offset within a[] */
  i64 *piOff                      /* IN/OUT: Current offset */
){
  int i = *pi;

  if( i>=n ){
    /* EOF */
    *piOff = -1;
    return 1;
  }else{
    i64 iOff = *piOff;
    u32 iVal;

    fts5FastGetVarint32(a, i, iVal);
    if( iVal<=1 ){
      if( iVal==0 ){
        *pi = i;
        return 0;
      }
      fts5FastGetVarint32(a, i, iVal);







>







>







233404
233405
233406
233407
233408
233409
233410
233411
233412
233413
233414
233415
233416
233417
233418
233419
233420
233421
233422
233423
233424
233425
233426

static int sqlite3Fts5PoslistNext64(
  const u8 *a, int n,             /* Buffer containing poslist */
  int *pi,                        /* IN/OUT: Offset within a[] */
  i64 *piOff                      /* IN/OUT: Current offset */
){
  int i = *pi;
  assert( a!=0 || i==0 );
  if( i>=n ){
    /* EOF */
    *piOff = -1;
    return 1;
  }else{
    i64 iOff = *piOff;
    u32 iVal;
    assert( a!=0 );
    fts5FastGetVarint32(a, i, iVal);
    if( iVal<=1 ){
      if( iVal==0 ){
        *pi = i;
        return 0;
      }
      fts5FastGetVarint32(a, i, iVal);
232016
232017
232018
232019
232020
232021
232022










232023
232024
232025
232026
232027
232028
232029
    };

    if( (rc = fts5ConfigSetEnum(aDetail, zArg, &pConfig->eDetail)) ){
      *pzErr = sqlite3_mprintf("malformed detail=... directive");
    }
    return rc;
  }











  *pzErr = sqlite3_mprintf("unrecognized option: \"%.*s\"", nCmd, zCmd);
  return SQLITE_ERROR;
}

/*
** Allocate an instance of the default tokenizer ("simple") at







>
>
>
>
>
>
>
>
>
>







234043
234044
234045
234046
234047
234048
234049
234050
234051
234052
234053
234054
234055
234056
234057
234058
234059
234060
234061
234062
234063
234064
234065
234066
    };

    if( (rc = fts5ConfigSetEnum(aDetail, zArg, &pConfig->eDetail)) ){
      *pzErr = sqlite3_mprintf("malformed detail=... directive");
    }
    return rc;
  }

  if( sqlite3_strnicmp("tokendata", zCmd, nCmd)==0 ){
    if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1]!='\0' ){
      *pzErr = sqlite3_mprintf("malformed tokendata=... directive");
      rc = SQLITE_ERROR;
    }else{
      pConfig->bTokendata = (zArg[0]=='1');
    }
    return rc;
  }

  *pzErr = sqlite3_mprintf("unrecognized option: \"%.*s\"", nCmd, zCmd);
  return SQLITE_ERROR;
}

/*
** Allocate an instance of the default tokenizer ("simple") at
232750
232751
232752
232753
232754
232755
232756
232757


232758
232759
232760
232761
232762
232763
232764
/*
** An instance of the following structure represents a single search term
** or term prefix.
*/
struct Fts5ExprTerm {
  u8 bPrefix;                     /* True for a prefix term */
  u8 bFirst;                      /* True if token must be first in column */
  char *zTerm;                    /* nul-terminated term */


  Fts5IndexIter *pIter;           /* Iterator for this term */
  Fts5ExprTerm *pSynonym;         /* Pointer to first in list of synonyms */
};

/*
** A phrase. One or more terms that must appear in a contiguous sequence
** within a document for it to match.







|
>
>







234787
234788
234789
234790
234791
234792
234793
234794
234795
234796
234797
234798
234799
234800
234801
234802
234803
/*
** An instance of the following structure represents a single search term
** or term prefix.
*/
struct Fts5ExprTerm {
  u8 bPrefix;                     /* True for a prefix term */
  u8 bFirst;                      /* True if token must be first in column */
  char *pTerm;                    /* Term data */
  int nQueryTerm;                 /* Effective size of term in bytes */
  int nFullTerm;                  /* Size of term in bytes incl. tokendata */
  Fts5IndexIter *pIter;           /* Iterator for this term */
  Fts5ExprTerm *pSynonym;         /* Pointer to first in list of synonyms */
};

/*
** A phrase. One or more terms that must appear in a contiguous sequence
** within a document for it to match.
233617
233618
233619
233620
233621
233622
233623
233624
233625
233626
233627
233628
233629
233630
233631
        for(p=pTerm; p; p=p->pSynonym){
          int rc;
          if( p->pIter ){
            sqlite3Fts5IterClose(p->pIter);
            p->pIter = 0;
          }
          rc = sqlite3Fts5IndexQuery(
              pExpr->pIndex, p->zTerm, (int)strlen(p->zTerm),
              (pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) |
              (pExpr->bDesc ? FTS5INDEX_QUERY_DESC : 0),
              pNear->pColset,
              &p->pIter
          );
          assert( (rc==SQLITE_OK)==(p->pIter!=0) );
          if( rc!=SQLITE_OK ) return rc;







|







235656
235657
235658
235659
235660
235661
235662
235663
235664
235665
235666
235667
235668
235669
235670
        for(p=pTerm; p; p=p->pSynonym){
          int rc;
          if( p->pIter ){
            sqlite3Fts5IterClose(p->pIter);
            p->pIter = 0;
          }
          rc = sqlite3Fts5IndexQuery(
              pExpr->pIndex, p->pTerm, p->nQueryTerm,
              (pTerm->bPrefix ? FTS5INDEX_QUERY_PREFIX : 0) |
              (pExpr->bDesc ? FTS5INDEX_QUERY_DESC : 0),
              pNear->pColset,
              &p->pIter
          );
          assert( (rc==SQLITE_OK)==(p->pIter!=0) );
          if( rc!=SQLITE_OK ) return rc;
234254
234255
234256
234257
234258
234259
234260
234261
234262
234263
234264
234265
234266
234267
234268
static void fts5ExprPhraseFree(Fts5ExprPhrase *pPhrase){
  if( pPhrase ){
    int i;
    for(i=0; i<pPhrase->nTerm; i++){
      Fts5ExprTerm *pSyn;
      Fts5ExprTerm *pNext;
      Fts5ExprTerm *pTerm = &pPhrase->aTerm[i];
      sqlite3_free(pTerm->zTerm);
      sqlite3Fts5IterClose(pTerm->pIter);
      for(pSyn=pTerm->pSynonym; pSyn; pSyn=pNext){
        pNext = pSyn->pSynonym;
        sqlite3Fts5IterClose(pSyn->pIter);
        fts5BufferFree((Fts5Buffer*)&pSyn[1]);
        sqlite3_free(pSyn);
      }







|







236293
236294
236295
236296
236297
236298
236299
236300
236301
236302
236303
236304
236305
236306
236307
static void fts5ExprPhraseFree(Fts5ExprPhrase *pPhrase){
  if( pPhrase ){
    int i;
    for(i=0; i<pPhrase->nTerm; i++){
      Fts5ExprTerm *pSyn;
      Fts5ExprTerm *pNext;
      Fts5ExprTerm *pTerm = &pPhrase->aTerm[i];
      sqlite3_free(pTerm->pTerm);
      sqlite3Fts5IterClose(pTerm->pIter);
      for(pSyn=pTerm->pSynonym; pSyn; pSyn=pNext){
        pNext = pSyn->pSynonym;
        sqlite3Fts5IterClose(pSyn->pIter);
        fts5BufferFree((Fts5Buffer*)&pSyn[1]);
        sqlite3_free(pSyn);
      }
234352
234353
234354
234355
234356
234357
234358

234359
234360
234361
234362
234363
234364
234365
  }
  return pRet;
}

typedef struct TokenCtx TokenCtx;
struct TokenCtx {
  Fts5ExprPhrase *pPhrase;

  int rc;
};

/*
** Callback for tokenizing terms used by ParseTerm().
*/
static int fts5ParseTokenize(







>







236391
236392
236393
236394
236395
236396
236397
236398
236399
236400
236401
236402
236403
236404
236405
  }
  return pRet;
}

typedef struct TokenCtx TokenCtx;
struct TokenCtx {
  Fts5ExprPhrase *pPhrase;
  Fts5Config *pConfig;
  int rc;
};

/*
** Callback for tokenizing terms used by ParseTerm().
*/
static int fts5ParseTokenize(
234385
234386
234387
234388
234389
234390
234391
234392




234393
234394
234395
234396
234397
234398
234399
234400
    Fts5ExprTerm *pSyn;
    sqlite3_int64 nByte = sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer) + nToken+1;
    pSyn = (Fts5ExprTerm*)sqlite3_malloc64(nByte);
    if( pSyn==0 ){
      rc = SQLITE_NOMEM;
    }else{
      memset(pSyn, 0, (size_t)nByte);
      pSyn->zTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer);




      memcpy(pSyn->zTerm, pToken, nToken);
      pSyn->pSynonym = pPhrase->aTerm[pPhrase->nTerm-1].pSynonym;
      pPhrase->aTerm[pPhrase->nTerm-1].pSynonym = pSyn;
    }
  }else{
    Fts5ExprTerm *pTerm;
    if( pPhrase==0 || (pPhrase->nTerm % SZALLOC)==0 ){
      Fts5ExprPhrase *pNew;







|
>
>
>
>
|







236425
236426
236427
236428
236429
236430
236431
236432
236433
236434
236435
236436
236437
236438
236439
236440
236441
236442
236443
236444
    Fts5ExprTerm *pSyn;
    sqlite3_int64 nByte = sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer) + nToken+1;
    pSyn = (Fts5ExprTerm*)sqlite3_malloc64(nByte);
    if( pSyn==0 ){
      rc = SQLITE_NOMEM;
    }else{
      memset(pSyn, 0, (size_t)nByte);
      pSyn->pTerm = ((char*)pSyn) + sizeof(Fts5ExprTerm) + sizeof(Fts5Buffer);
      pSyn->nFullTerm = pSyn->nQueryTerm = nToken;
      if( pCtx->pConfig->bTokendata ){
        pSyn->nQueryTerm = (int)strlen(pSyn->pTerm);
      }
      memcpy(pSyn->pTerm, pToken, nToken);
      pSyn->pSynonym = pPhrase->aTerm[pPhrase->nTerm-1].pSynonym;
      pPhrase->aTerm[pPhrase->nTerm-1].pSynonym = pSyn;
    }
  }else{
    Fts5ExprTerm *pTerm;
    if( pPhrase==0 || (pPhrase->nTerm % SZALLOC)==0 ){
      Fts5ExprPhrase *pNew;
234411
234412
234413
234414
234415
234416
234417
234418




234419
234420
234421
234422
234423
234424
234425
        pNew->nTerm = nNew - SZALLOC;
      }
    }

    if( rc==SQLITE_OK ){
      pTerm = &pPhrase->aTerm[pPhrase->nTerm++];
      memset(pTerm, 0, sizeof(Fts5ExprTerm));
      pTerm->zTerm = sqlite3Fts5Strndup(&rc, pToken, nToken);




    }
  }

  pCtx->rc = rc;
  return rc;
}








|
>
>
>
>







236455
236456
236457
236458
236459
236460
236461
236462
236463
236464
236465
236466
236467
236468
236469
236470
236471
236472
236473
        pNew->nTerm = nNew - SZALLOC;
      }
    }

    if( rc==SQLITE_OK ){
      pTerm = &pPhrase->aTerm[pPhrase->nTerm++];
      memset(pTerm, 0, sizeof(Fts5ExprTerm));
      pTerm->pTerm = sqlite3Fts5Strndup(&rc, pToken, nToken);
      pTerm->nFullTerm = pTerm->nQueryTerm = nToken;
      if( pCtx->pConfig->bTokendata && rc==SQLITE_OK ){
        pTerm->nQueryTerm = (int)strlen(pTerm->pTerm);
      }
    }
  }

  pCtx->rc = rc;
  return rc;
}

234478
234479
234480
234481
234482
234483
234484

234485
234486
234487
234488
234489
234490
234491
  Fts5Config *pConfig = pParse->pConfig;
  TokenCtx sCtx;                  /* Context object passed to callback */
  int rc;                         /* Tokenize return code */
  char *z = 0;

  memset(&sCtx, 0, sizeof(TokenCtx));
  sCtx.pPhrase = pAppend;


  rc = fts5ParseStringFromToken(pToken, &z);
  if( rc==SQLITE_OK ){
    int flags = FTS5_TOKENIZE_QUERY | (bPrefix ? FTS5_TOKENIZE_PREFIX : 0);
    int n;
    sqlite3Fts5Dequote(z);
    n = (int)strlen(z);







>







236526
236527
236528
236529
236530
236531
236532
236533
236534
236535
236536
236537
236538
236539
236540
  Fts5Config *pConfig = pParse->pConfig;
  TokenCtx sCtx;                  /* Context object passed to callback */
  int rc;                         /* Tokenize return code */
  char *z = 0;

  memset(&sCtx, 0, sizeof(TokenCtx));
  sCtx.pPhrase = pAppend;
  sCtx.pConfig = pConfig;

  rc = fts5ParseStringFromToken(pToken, &z);
  if( rc==SQLITE_OK ){
    int flags = FTS5_TOKENIZE_QUERY | (bPrefix ? FTS5_TOKENIZE_PREFIX : 0);
    int n;
    sqlite3Fts5Dequote(z);
    n = (int)strlen(z);
234525
234526
234527
234528
234529
234530
234531
234532
234533
234534
234535


234536
234537

234538
234539
234540
234541
234542
234543
234544
234545
234546
234547
234548
234549
234550
234551
234552
234553
234554
234555
234556
234557
234558
234559
234560
234561
234562
234563

234564
234565

234566
234567
234568
234569
234570
234571
234572
234573
234574
234575
234576
234577
234578
234579
234580
234581
234582
234583

234584
234585
234586
234587
234588
234589
234590
*/
static int sqlite3Fts5ExprClonePhrase(
  Fts5Expr *pExpr,
  int iPhrase,
  Fts5Expr **ppNew
){
  int rc = SQLITE_OK;             /* Return code */
  Fts5ExprPhrase *pOrig;          /* The phrase extracted from pExpr */
  Fts5Expr *pNew = 0;             /* Expression to return via *ppNew */
  TokenCtx sCtx = {0,0};          /* Context object for fts5ParseTokenize */



  pOrig = pExpr->apExprPhrase[iPhrase];
  pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr));

  if( rc==SQLITE_OK ){
    pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc,
        sizeof(Fts5ExprPhrase*));
  }
  if( rc==SQLITE_OK ){
    pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc,
        sizeof(Fts5ExprNode));
  }
  if( rc==SQLITE_OK ){
    pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc,
        sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*));
  }
  if( rc==SQLITE_OK ){
    Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset;
    if( pColsetOrig ){
      sqlite3_int64 nByte;
      Fts5Colset *pColset;
      nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int);
      pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte);
      if( pColset ){
        memcpy(pColset, pColsetOrig, (size_t)nByte);
      }
      pNew->pRoot->pNear->pColset = pColset;
    }
  }


  if( pOrig->nTerm ){
    int i;                          /* Used to iterate through phrase terms */

    for(i=0; rc==SQLITE_OK && i<pOrig->nTerm; i++){
      int tflags = 0;
      Fts5ExprTerm *p;
      for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){
        const char *zTerm = p->zTerm;
        rc = fts5ParseTokenize((void*)&sCtx, tflags, zTerm, (int)strlen(zTerm),
            0, 0);
        tflags = FTS5_TOKEN_COLOCATED;
      }
      if( rc==SQLITE_OK ){
        sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix;
        sCtx.pPhrase->aTerm[i].bFirst = pOrig->aTerm[i].bFirst;
      }
    }
  }else{
    /* This happens when parsing a token or quoted phrase that contains
    ** no token characters at all. (e.g ... MATCH '""'). */
    sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase));

  }

  if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){
    /* All the allocations succeeded. Put the expression object together. */
    pNew->pIndex = pExpr->pIndex;
    pNew->pConfig = pExpr->pConfig;
    pNew->nPhrase = 1;







|

|
|
>
>
|
|
>












|













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







236574
236575
236576
236577
236578
236579
236580
236581
236582
236583
236584
236585
236586
236587
236588
236589
236590
236591
236592
236593
236594
236595
236596
236597
236598
236599
236600
236601
236602
236603
236604
236605
236606
236607
236608
236609
236610
236611
236612
236613
236614
236615
236616
236617
236618
236619
236620
236621
236622
236623

236624

236625
236626
236627
236628
236629
236630
236631
236632
236633
236634
236635
236636
236637
236638
236639
236640
236641
236642
236643
*/
static int sqlite3Fts5ExprClonePhrase(
  Fts5Expr *pExpr,
  int iPhrase,
  Fts5Expr **ppNew
){
  int rc = SQLITE_OK;             /* Return code */
  Fts5ExprPhrase *pOrig = 0;      /* The phrase extracted from pExpr */
  Fts5Expr *pNew = 0;             /* Expression to return via *ppNew */
  TokenCtx sCtx = {0,0,0};        /* Context object for fts5ParseTokenize */
  if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){
    rc = SQLITE_RANGE;
  }else{
    pOrig = pExpr->apExprPhrase[iPhrase];
    pNew = (Fts5Expr*)sqlite3Fts5MallocZero(&rc, sizeof(Fts5Expr));
  }
  if( rc==SQLITE_OK ){
    pNew->apExprPhrase = (Fts5ExprPhrase**)sqlite3Fts5MallocZero(&rc,
        sizeof(Fts5ExprPhrase*));
  }
  if( rc==SQLITE_OK ){
    pNew->pRoot = (Fts5ExprNode*)sqlite3Fts5MallocZero(&rc,
        sizeof(Fts5ExprNode));
  }
  if( rc==SQLITE_OK ){
    pNew->pRoot->pNear = (Fts5ExprNearset*)sqlite3Fts5MallocZero(&rc,
        sizeof(Fts5ExprNearset) + sizeof(Fts5ExprPhrase*));
  }
  if( rc==SQLITE_OK && ALWAYS(pOrig!=0) ){
    Fts5Colset *pColsetOrig = pOrig->pNode->pNear->pColset;
    if( pColsetOrig ){
      sqlite3_int64 nByte;
      Fts5Colset *pColset;
      nByte = sizeof(Fts5Colset) + (pColsetOrig->nCol-1) * sizeof(int);
      pColset = (Fts5Colset*)sqlite3Fts5MallocZero(&rc, nByte);
      if( pColset ){
        memcpy(pColset, pColsetOrig, (size_t)nByte);
      }
      pNew->pRoot->pNear->pColset = pColset;
    }
  }

  if( rc==SQLITE_OK ){
    if( pOrig->nTerm ){
      int i;                          /* Used to iterate through phrase terms */
      sCtx.pConfig = pExpr->pConfig;
      for(i=0; rc==SQLITE_OK && i<pOrig->nTerm; i++){
        int tflags = 0;
        Fts5ExprTerm *p;
        for(p=&pOrig->aTerm[i]; p && rc==SQLITE_OK; p=p->pSynonym){

          rc = fts5ParseTokenize((void*)&sCtx,tflags,p->pTerm,p->nFullTerm,0,0);

          tflags = FTS5_TOKEN_COLOCATED;
        }
        if( rc==SQLITE_OK ){
          sCtx.pPhrase->aTerm[i].bPrefix = pOrig->aTerm[i].bPrefix;
          sCtx.pPhrase->aTerm[i].bFirst = pOrig->aTerm[i].bFirst;
        }
      }
    }else{
      /* This happens when parsing a token or quoted phrase that contains
      ** no token characters at all. (e.g ... MATCH '""'). */
      sCtx.pPhrase = sqlite3Fts5MallocZero(&rc, sizeof(Fts5ExprPhrase));
    }
  }

  if( rc==SQLITE_OK && ALWAYS(sCtx.pPhrase) ){
    /* All the allocations succeeded. Put the expression object together. */
    pNew->pIndex = pExpr->pIndex;
    pNew->pConfig = pExpr->pConfig;
    pNew->nPhrase = 1;
234946
234947
234948
234949
234950
234951
234952


234953
234954
234955
234956
234957


234958
234959
234960
234961
234962
234963
234964
      Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero(
          &pParse->rc, sizeof(Fts5ExprPhrase)
      );
      if( pPhrase ){
        if( parseGrowPhraseArray(pParse) ){
          fts5ExprPhraseFree(pPhrase);
        }else{


          pParse->apPhrase[pParse->nPhrase++] = pPhrase;
          pPhrase->nTerm = 1;
          pPhrase->aTerm[0].zTerm = sqlite3Fts5Strndup(
              &pParse->rc, pNear->apPhrase[0]->aTerm[ii].zTerm, -1
          );


          pRet->apChild[ii] = sqlite3Fts5ParseNode(pParse, FTS5_STRING,
              0, 0, sqlite3Fts5ParseNearset(pParse, 0, pPhrase)
          );
        }
      }
    }








>
>


|
<
<
>
>







236999
237000
237001
237002
237003
237004
237005
237006
237007
237008
237009
237010


237011
237012
237013
237014
237015
237016
237017
237018
237019
      Fts5ExprPhrase *pPhrase = (Fts5ExprPhrase*)sqlite3Fts5MallocZero(
          &pParse->rc, sizeof(Fts5ExprPhrase)
      );
      if( pPhrase ){
        if( parseGrowPhraseArray(pParse) ){
          fts5ExprPhraseFree(pPhrase);
        }else{
          Fts5ExprTerm *p = &pNear->apPhrase[0]->aTerm[ii];
          Fts5ExprTerm *pTo = &pPhrase->aTerm[0];
          pParse->apPhrase[pParse->nPhrase++] = pPhrase;
          pPhrase->nTerm = 1;
          pTo->pTerm = sqlite3Fts5Strndup(&pParse->rc, p->pTerm, p->nFullTerm);


          pTo->nQueryTerm = p->nQueryTerm;
          pTo->nFullTerm = p->nFullTerm;
          pRet->apChild[ii] = sqlite3Fts5ParseNode(pParse, FTS5_STRING,
              0, 0, sqlite3Fts5ParseNearset(pParse, 0, pPhrase)
          );
        }
      }
    }

235135
235136
235137
235138
235139
235140
235141
235142
235143
235144
235145
235146
235147
235148
235149

235150
235151
235152
235153
235154
235155
235156
235157
235158
static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){
  sqlite3_int64 nByte = 0;
  Fts5ExprTerm *p;
  char *zQuoted;

  /* Determine the maximum amount of space required. */
  for(p=pTerm; p; p=p->pSynonym){
    nByte += (int)strlen(pTerm->zTerm) * 2 + 3 + 2;
  }
  zQuoted = sqlite3_malloc64(nByte);

  if( zQuoted ){
    int i = 0;
    for(p=pTerm; p; p=p->pSynonym){
      char *zIn = p->zTerm;

      zQuoted[i++] = '"';
      while( *zIn ){
        if( *zIn=='"' ) zQuoted[i++] = '"';
        zQuoted[i++] = *zIn++;
      }
      zQuoted[i++] = '"';
      if( p->pSynonym ) zQuoted[i++] = '|';
    }
    if( pTerm->bPrefix ){







|






|
>

|







237190
237191
237192
237193
237194
237195
237196
237197
237198
237199
237200
237201
237202
237203
237204
237205
237206
237207
237208
237209
237210
237211
237212
237213
237214
static char *fts5ExprTermPrint(Fts5ExprTerm *pTerm){
  sqlite3_int64 nByte = 0;
  Fts5ExprTerm *p;
  char *zQuoted;

  /* Determine the maximum amount of space required. */
  for(p=pTerm; p; p=p->pSynonym){
    nByte += pTerm->nQueryTerm * 2 + 3 + 2;
  }
  zQuoted = sqlite3_malloc64(nByte);

  if( zQuoted ){
    int i = 0;
    for(p=pTerm; p; p=p->pSynonym){
      char *zIn = p->pTerm;
      char *zEnd = &zIn[p->nQueryTerm];
      zQuoted[i++] = '"';
      while( zIn<zEnd ){
        if( *zIn=='"' ) zQuoted[i++] = '"';
        zQuoted[i++] = *zIn++;
      }
      zQuoted[i++] = '"';
      if( p->pSynonym ) zQuoted[i++] = '|';
    }
    if( pTerm->bPrefix ){
235222
235223
235224
235225
235226
235227
235228
235229
235230


235231
235232
235233
235234
235235
235236
235237
    if( zRet==0 ) return 0;

    for(i=0; i<pNear->nPhrase; i++){
      Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];

      zRet = fts5PrintfAppend(zRet, " {");
      for(iTerm=0; zRet && iTerm<pPhrase->nTerm; iTerm++){
        char *zTerm = pPhrase->aTerm[iTerm].zTerm;
        zRet = fts5PrintfAppend(zRet, "%s%s", iTerm==0?"":" ", zTerm);


        if( pPhrase->aTerm[iTerm].bPrefix ){
          zRet = fts5PrintfAppend(zRet, "*");
        }
      }

      if( zRet ) zRet = fts5PrintfAppend(zRet, "}");
      if( zRet==0 ) return 0;







|
|
>
>







237278
237279
237280
237281
237282
237283
237284
237285
237286
237287
237288
237289
237290
237291
237292
237293
237294
237295
    if( zRet==0 ) return 0;

    for(i=0; i<pNear->nPhrase; i++){
      Fts5ExprPhrase *pPhrase = pNear->apPhrase[i];

      zRet = fts5PrintfAppend(zRet, " {");
      for(iTerm=0; zRet && iTerm<pPhrase->nTerm; iTerm++){
        Fts5ExprTerm *p = &pPhrase->aTerm[iTerm];
        zRet = fts5PrintfAppend(zRet, "%s%.*s", iTerm==0?"":" ",
            p->nQueryTerm, p->pTerm
        );
        if( pPhrase->aTerm[iTerm].bPrefix ){
          zRet = fts5PrintfAppend(zRet, "*");
        }
      }

      if( zRet ) zRet = fts5PrintfAppend(zRet, "}");
      if( zRet==0 ) return 0;
235623
235624
235625
235626
235627
235628
235629











235630
235631
235632
235633
235634
235635
235636
235637
235638
235639
235640
235641


235642
235643
235644
235645



235646
235647
235648
235649
235650
235651
235652
235653
235654
235655
235656
235657







235658
235659
235660
235661
235662
235663
235664
static int fts5ExprColsetTest(Fts5Colset *pColset, int iCol){
  int i;
  for(i=0; i<pColset->nCol; i++){
    if( pColset->aiCol[i]==iCol ) return 1;
  }
  return 0;
}












static int fts5ExprPopulatePoslistsCb(
  void *pCtx,                /* Copy of 2nd argument to xTokenize() */
  int tflags,                /* Mask of FTS5_TOKEN_* flags */
  const char *pToken,        /* Pointer to buffer containing token */
  int nToken,                /* Size of token in bytes */
  int iUnused1,              /* Byte offset of token within input text */
  int iUnused2               /* Byte offset of end of token within input text */
){
  Fts5ExprCtx *p = (Fts5ExprCtx*)pCtx;
  Fts5Expr *pExpr = p->pExpr;
  int i;



  UNUSED_PARAM2(iUnused1, iUnused2);

  if( nToken>FTS5_MAX_TOKEN_SIZE ) nToken = FTS5_MAX_TOKEN_SIZE;



  if( (tflags & FTS5_TOKEN_COLOCATED)==0 ) p->iOff++;
  for(i=0; i<pExpr->nPhrase; i++){
    Fts5ExprTerm *pTerm;
    if( p->aPopulator[i].bOk==0 ) continue;
    for(pTerm=&pExpr->apExprPhrase[i]->aTerm[0]; pTerm; pTerm=pTerm->pSynonym){
      int nTerm = (int)strlen(pTerm->zTerm);
      if( (nTerm==nToken || (nTerm<nToken && pTerm->bPrefix))
       && memcmp(pTerm->zTerm, pToken, nTerm)==0
      ){
        int rc = sqlite3Fts5PoslistWriterAppend(
            &pExpr->apExprPhrase[i]->poslist, &p->aPopulator[i].writer, p->iOff
        );







        if( rc ) return rc;
        break;
      }
    }
  }
  return SQLITE_OK;
}







>
>
>
>
>
>
>
>
>
>
>












>
>



|
>
>
>


|

|
<
|
|




>
>
>
>
>
>
>







237681
237682
237683
237684
237685
237686
237687
237688
237689
237690
237691
237692
237693
237694
237695
237696
237697
237698
237699
237700
237701
237702
237703
237704
237705
237706
237707
237708
237709
237710
237711
237712
237713
237714
237715
237716
237717
237718
237719
237720
237721
237722
237723
237724

237725
237726
237727
237728
237729
237730
237731
237732
237733
237734
237735
237736
237737
237738
237739
237740
237741
237742
237743
237744
static int fts5ExprColsetTest(Fts5Colset *pColset, int iCol){
  int i;
  for(i=0; i<pColset->nCol; i++){
    if( pColset->aiCol[i]==iCol ) return 1;
  }
  return 0;
}

/*
** pToken is a buffer nToken bytes in size that may or may not contain
** an embedded 0x00 byte. If it does, return the number of bytes in
** the buffer before the 0x00. If it does not, return nToken.
*/
static int fts5QueryTerm(const char *pToken, int nToken){
  int ii;
  for(ii=0; ii<nToken && pToken[ii]; ii++){}
  return ii;
}

static int fts5ExprPopulatePoslistsCb(
  void *pCtx,                /* Copy of 2nd argument to xTokenize() */
  int tflags,                /* Mask of FTS5_TOKEN_* flags */
  const char *pToken,        /* Pointer to buffer containing token */
  int nToken,                /* Size of token in bytes */
  int iUnused1,              /* Byte offset of token within input text */
  int iUnused2               /* Byte offset of end of token within input text */
){
  Fts5ExprCtx *p = (Fts5ExprCtx*)pCtx;
  Fts5Expr *pExpr = p->pExpr;
  int i;
  int nQuery = nToken;
  i64 iRowid = pExpr->pRoot->iRowid;

  UNUSED_PARAM2(iUnused1, iUnused2);

  if( nQuery>FTS5_MAX_TOKEN_SIZE ) nQuery = FTS5_MAX_TOKEN_SIZE;
  if( pExpr->pConfig->bTokendata ){
    nQuery = fts5QueryTerm(pToken, nQuery);
  }
  if( (tflags & FTS5_TOKEN_COLOCATED)==0 ) p->iOff++;
  for(i=0; i<pExpr->nPhrase; i++){
    Fts5ExprTerm *pT;
    if( p->aPopulator[i].bOk==0 ) continue;
    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;
        break;
      }
    }
  }
  return SQLITE_OK;
}
235785
235786
235787
235788
235789
235790
235791













































































235792
235793
235794
235795
235796
235797
235798
  }else{
    *ppCollist = 0;
    *pnCollist = 0;
  }

  return rc;
}














































































/*
** 2014 August 11
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**







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







237865
237866
237867
237868
237869
237870
237871
237872
237873
237874
237875
237876
237877
237878
237879
237880
237881
237882
237883
237884
237885
237886
237887
237888
237889
237890
237891
237892
237893
237894
237895
237896
237897
237898
237899
237900
237901
237902
237903
237904
237905
237906
237907
237908
237909
237910
237911
237912
237913
237914
237915
237916
237917
237918
237919
237920
237921
237922
237923
237924
237925
237926
237927
237928
237929
237930
237931
237932
237933
237934
237935
237936
237937
237938
237939
237940
237941
237942
237943
237944
237945
237946
237947
237948
237949
237950
237951
237952
237953
237954
237955
  }else{
    *ppCollist = 0;
    *pnCollist = 0;
  }

  return rc;
}

/*
** Does the work of the fts5_api.xQueryToken() API method.
*/
static int sqlite3Fts5ExprQueryToken(
  Fts5Expr *pExpr,
  int iPhrase,
  int iToken,
  const char **ppOut,
  int *pnOut
){
  Fts5ExprPhrase *pPhrase = 0;

  if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){
    return SQLITE_RANGE;
  }
  pPhrase = pExpr->apExprPhrase[iPhrase];
  if( iToken<0 || iToken>=pPhrase->nTerm ){
    return SQLITE_RANGE;
  }

  *ppOut = pPhrase->aTerm[iToken].pTerm;
  *pnOut = pPhrase->aTerm[iToken].nFullTerm;
  return SQLITE_OK;
}

/*
** Does the work of the fts5_api.xInstToken() API method.
*/
static int sqlite3Fts5ExprInstToken(
  Fts5Expr *pExpr,
  i64 iRowid,
  int iPhrase,
  int iCol,
  int iOff,
  int iToken,
  const char **ppOut,
  int *pnOut
){
  Fts5ExprPhrase *pPhrase = 0;
  Fts5ExprTerm *pTerm = 0;
  int rc = SQLITE_OK;

  if( iPhrase<0 || iPhrase>=pExpr->nPhrase ){
    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.
*/
static void sqlite3Fts5ExprClearTokens(Fts5Expr *pExpr){
  int ii;
  for(ii=0; ii<pExpr->nPhrase; ii++){
    Fts5ExprTerm *pT;
    for(pT=&pExpr->apExprPhrase[ii]->aTerm[0]; pT; pT=pT->pSynonym){
      sqlite3Fts5IndexIterClearTokendata(pT->pIter);
    }
  }
}

/*
** 2014 August 11
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
235824
235825
235826
235827
235828
235829
235830
235831
235832
235833
235834





235835
235836
235837
235838
235839
235840
235841
  int nSlot;                      /* Size of aSlot[] array */
  Fts5HashEntry *pScan;           /* Current ordered scan item */
  Fts5HashEntry **aSlot;          /* Array of hash slots */
};

/*
** Each entry in the hash table is represented by an object of the
** following type. Each object, its key (a nul-terminated string) and
** its current data are stored in a single memory allocation. The
** key immediately follows the object in memory. The position list
** data immediately follows the key data in memory.





**
** The data that follows the key is in a similar, but not identical format
** to the doclist data stored in the database. It is:
**
**   * Rowid, as a varint
**   * Position list, without 0x00 terminator.
**   * Size of previous position list and rowid, as a 4 byte







|
|
|
|
>
>
>
>
>







237981
237982
237983
237984
237985
237986
237987
237988
237989
237990
237991
237992
237993
237994
237995
237996
237997
237998
237999
238000
238001
238002
238003
  int nSlot;                      /* Size of aSlot[] array */
  Fts5HashEntry *pScan;           /* Current ordered scan item */
  Fts5HashEntry **aSlot;          /* Array of hash slots */
};

/*
** Each entry in the hash table is represented by an object of the
** following type. Each object, its key, and its current data are stored
** in a single memory allocation. The key immediately follows the object
** in memory. The position list data immediately follows the key data
** in memory.
**
** The key is Fts5HashEntry.nKey bytes in size. It consists of a single
** byte identifying the index (either the main term index or a prefix-index),
** followed by the term data. For example: "0token". There is no
** nul-terminator - in this case nKey=6.
**
** The data that follows the key is in a similar, but not identical format
** to the doclist data stored in the database. It is:
**
**   * Rowid, as a varint
**   * Position list, without 0x00 terminator.
**   * Size of previous position list and rowid, as a 4 byte
235962
235963
235964
235965
235966
235967
235968
235969
235970
235971
235972
235973
235974
235975
235976
235977
  memset(apNew, 0, nNew*sizeof(Fts5HashEntry*));

  for(i=0; i<pHash->nSlot; i++){
    while( apOld[i] ){
      unsigned int iHash;
      Fts5HashEntry *p = apOld[i];
      apOld[i] = p->pHashNext;
      iHash = fts5HashKey(nNew, (u8*)fts5EntryKey(p),
                          (int)strlen(fts5EntryKey(p)));
      p->pHashNext = apNew[iHash];
      apNew[iHash] = p;
    }
  }

  sqlite3_free(apOld);
  pHash->nSlot = nNew;







|
<







238124
238125
238126
238127
238128
238129
238130
238131

238132
238133
238134
238135
238136
238137
238138
  memset(apNew, 0, nNew*sizeof(Fts5HashEntry*));

  for(i=0; i<pHash->nSlot; i++){
    while( apOld[i] ){
      unsigned int iHash;
      Fts5HashEntry *p = apOld[i];
      apOld[i] = p->pHashNext;
      iHash = fts5HashKey(nNew, (u8*)fts5EntryKey(p), p->nKey);

      p->pHashNext = apNew[iHash];
      apNew[iHash] = p;
    }
  }

  sqlite3_free(apOld);
  pHash->nSlot = nNew;
236047
236048
236049
236050
236051
236052
236053
236054
236055
236056
236057
236058
236059
236060
236061
  bNew = (pHash->eDetail==FTS5_DETAIL_FULL);

  /* Attempt to locate an existing hash entry */
  iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken);
  for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){
    char *zKey = fts5EntryKey(p);
    if( zKey[0]==bByte
     && p->nKey==nToken
     && memcmp(&zKey[1], pToken, nToken)==0
    ){
      break;
    }
  }

  /* If an existing hash entry cannot be found, create a new one. */







|







238208
238209
238210
238211
238212
238213
238214
238215
238216
238217
238218
238219
238220
238221
238222
  bNew = (pHash->eDetail==FTS5_DETAIL_FULL);

  /* Attempt to locate an existing hash entry */
  iHash = fts5HashKey2(pHash->nSlot, (u8)bByte, (const u8*)pToken, nToken);
  for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){
    char *zKey = fts5EntryKey(p);
    if( zKey[0]==bByte
     && p->nKey==nToken+1
     && memcmp(&zKey[1], pToken, nToken)==0
    ){
      break;
    }
  }

  /* If an existing hash entry cannot be found, create a new one. */
236077
236078
236079
236080
236081
236082
236083
236084
236085
236086
236087
236088
236089
236090
236091
236092
236093
    if( !p ) return SQLITE_NOMEM;
    memset(p, 0, sizeof(Fts5HashEntry));
    p->nAlloc = (int)nByte;
    zKey = fts5EntryKey(p);
    zKey[0] = bByte;
    memcpy(&zKey[1], pToken, nToken);
    assert( iHash==fts5HashKey(pHash->nSlot, (u8*)zKey, nToken+1) );
    p->nKey = nToken;
    zKey[nToken+1] = '\0';
    p->nData = nToken+1 + 1 + sizeof(Fts5HashEntry);
    p->pHashNext = pHash->aSlot[iHash];
    pHash->aSlot[iHash] = p;
    pHash->nEntry++;

    /* Add the first rowid field to the hash-entry */
    p->nData += sqlite3Fts5PutVarint(&((u8*)p)[p->nData], iRowid);
    p->iRowid = iRowid;







|

|







238238
238239
238240
238241
238242
238243
238244
238245
238246
238247
238248
238249
238250
238251
238252
238253
238254
    if( !p ) return SQLITE_NOMEM;
    memset(p, 0, sizeof(Fts5HashEntry));
    p->nAlloc = (int)nByte;
    zKey = fts5EntryKey(p);
    zKey[0] = bByte;
    memcpy(&zKey[1], pToken, nToken);
    assert( iHash==fts5HashKey(pHash->nSlot, (u8*)zKey, nToken+1) );
    p->nKey = nToken+1;
    zKey[nToken+1] = '\0';
    p->nData = nToken+1 + sizeof(Fts5HashEntry);
    p->pHashNext = pHash->aSlot[iHash];
    pHash->aSlot[iHash] = p;
    pHash->nEntry++;

    /* Add the first rowid field to the hash-entry */
    p->nData += sqlite3Fts5PutVarint(&((u8*)p)[p->nData], iRowid);
    p->iRowid = iRowid;
236196
236197
236198
236199
236200
236201
236202
236203
236204
236205

236206



236207

236208

236209
236210
236211
236212
236213
236214
236215
    if( p1==0 ){
      *ppOut = p2;
      p2 = 0;
    }else if( p2==0 ){
      *ppOut = p1;
      p1 = 0;
    }else{
      int i = 0;
      char *zKey1 = fts5EntryKey(p1);
      char *zKey2 = fts5EntryKey(p2);

      while( zKey1[i]==zKey2[i] ) i++;





      if( ((u8)zKey1[i])>((u8)zKey2[i]) ){

        /* p2 is smaller */
        *ppOut = p2;
        ppOut = &p2->pScanNext;
        p2 = p2->pScanNext;
      }else{
        /* p1 is smaller */
        *ppOut = p1;







<


>
|
>
>
>
|
>
|
>







238357
238358
238359
238360
238361
238362
238363

238364
238365
238366
238367
238368
238369
238370
238371
238372
238373
238374
238375
238376
238377
238378
238379
238380
238381
    if( p1==0 ){
      *ppOut = p2;
      p2 = 0;
    }else if( p2==0 ){
      *ppOut = p1;
      p1 = 0;
    }else{

      char *zKey1 = fts5EntryKey(p1);
      char *zKey2 = fts5EntryKey(p2);
      int nMin = MIN(p1->nKey, p2->nKey);

      int cmp = memcmp(zKey1, zKey2, nMin);
      if( cmp==0 ){
        cmp = p1->nKey - p2->nKey;
      }
      assert( cmp!=0 );

      if( cmp>0 ){
        /* p2 is smaller */
        *ppOut = p2;
        ppOut = &p2->pScanNext;
        p2 = p2->pScanNext;
      }else{
        /* p1 is smaller */
        *ppOut = p1;
236243
236244
236245
236246
236247
236248
236249
236250
236251
236252
236253
236254
236255
236256
236257
  if( !ap ) return SQLITE_NOMEM;
  memset(ap, 0, sizeof(Fts5HashEntry*) * nMergeSlot);

  for(iSlot=0; iSlot<pHash->nSlot; iSlot++){
    Fts5HashEntry *pIter;
    for(pIter=pHash->aSlot[iSlot]; pIter; pIter=pIter->pHashNext){
      if( pTerm==0
       || (pIter->nKey+1>=nTerm && 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm))
      ){
        Fts5HashEntry *pEntry = pIter;
        pEntry->pScanNext = 0;
        for(i=0; ap[i]; i++){
          pEntry = fts5HashEntryMerge(pEntry, ap[i]);
          ap[i] = 0;
        }







|







238409
238410
238411
238412
238413
238414
238415
238416
238417
238418
238419
238420
238421
238422
238423
  if( !ap ) return SQLITE_NOMEM;
  memset(ap, 0, sizeof(Fts5HashEntry*) * nMergeSlot);

  for(iSlot=0; iSlot<pHash->nSlot; iSlot++){
    Fts5HashEntry *pIter;
    for(pIter=pHash->aSlot[iSlot]; pIter; pIter=pIter->pHashNext){
      if( pTerm==0
       || (pIter->nKey>=nTerm && 0==memcmp(fts5EntryKey(pIter), pTerm, nTerm))
      ){
        Fts5HashEntry *pEntry = pIter;
        pEntry->pScanNext = 0;
        for(i=0; ap[i]; i++){
          pEntry = fts5HashEntryMerge(pEntry, ap[i]);
          ap[i] = 0;
        }
236282
236283
236284
236285
236286
236287
236288
236289
236290
236291
236292
236293
236294
236295
236296
236297
236298
236299
236300
236301
){
  unsigned int iHash = fts5HashKey(pHash->nSlot, (const u8*)pTerm, nTerm);
  char *zKey = 0;
  Fts5HashEntry *p;

  for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){
    zKey = fts5EntryKey(p);
    assert( p->nKey+1==(int)strlen(zKey) );
    if( nTerm==p->nKey+1 && memcmp(zKey, pTerm, nTerm)==0 ) break;
  }

  if( p ){
    int nHashPre = sizeof(Fts5HashEntry) + nTerm + 1;
    int nList = p->nData - nHashPre;
    u8 *pRet = (u8*)(*ppOut = sqlite3_malloc64(nPre + nList + 10));
    if( pRet ){
      Fts5HashEntry *pFaux = (Fts5HashEntry*)&pRet[nPre-nHashPre];
      memcpy(&pRet[nPre], &((u8*)p)[nHashPre], nList);
      nList += fts5HashAddPoslistSize(pHash, p, pFaux);
      *pnDoclist = nList;







<
|



|







238448
238449
238450
238451
238452
238453
238454

238455
238456
238457
238458
238459
238460
238461
238462
238463
238464
238465
238466
){
  unsigned int iHash = fts5HashKey(pHash->nSlot, (const u8*)pTerm, nTerm);
  char *zKey = 0;
  Fts5HashEntry *p;

  for(p=pHash->aSlot[iHash]; p; p=p->pHashNext){
    zKey = fts5EntryKey(p);

    if( nTerm==p->nKey && memcmp(zKey, pTerm, nTerm)==0 ) break;
  }

  if( p ){
    int nHashPre = sizeof(Fts5HashEntry) + nTerm;
    int nList = p->nData - nHashPre;
    u8 *pRet = (u8*)(*ppOut = sqlite3_malloc64(nPre + nList + 10));
    if( pRet ){
      Fts5HashEntry *pFaux = (Fts5HashEntry*)&pRet[nPre-nHashPre];
      memcpy(&pRet[nPre], &((u8*)p)[nHashPre], nList);
      nList += fts5HashAddPoslistSize(pHash, p, pFaux);
      *pnDoclist = nList;
236348
236349
236350
236351
236352
236353
236354

236355
236356
236357
236358
236359
236360
236361
236362
236363

236364
236365
236366
236367

236368
236369
236370
236371
236372
236373
236374
static int sqlite3Fts5HashScanEof(Fts5Hash *p){
  return (p->pScan==0);
}

static void sqlite3Fts5HashScanEntry(
  Fts5Hash *pHash,
  const char **pzTerm,            /* OUT: term (nul-terminated) */

  const u8 **ppDoclist,           /* OUT: pointer to doclist */
  int *pnDoclist                  /* OUT: size of doclist in bytes */
){
  Fts5HashEntry *p;
  if( (p = pHash->pScan) ){
    char *zKey = fts5EntryKey(p);
    int nTerm = (int)strlen(zKey);
    fts5HashAddPoslistSize(pHash, p, 0);
    *pzTerm = zKey;

    *ppDoclist = (const u8*)&zKey[nTerm+1];
    *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm + 1);
  }else{
    *pzTerm = 0;

    *ppDoclist = 0;
    *pnDoclist = 0;
  }
}

/*
** 2014 May 31







>






|


>
|
|


>







238513
238514
238515
238516
238517
238518
238519
238520
238521
238522
238523
238524
238525
238526
238527
238528
238529
238530
238531
238532
238533
238534
238535
238536
238537
238538
238539
238540
238541
238542
static int sqlite3Fts5HashScanEof(Fts5Hash *p){
  return (p->pScan==0);
}

static void sqlite3Fts5HashScanEntry(
  Fts5Hash *pHash,
  const char **pzTerm,            /* OUT: term (nul-terminated) */
  int *pnTerm,                    /* OUT: Size of term in bytes */
  const u8 **ppDoclist,           /* OUT: pointer to doclist */
  int *pnDoclist                  /* OUT: size of doclist in bytes */
){
  Fts5HashEntry *p;
  if( (p = pHash->pScan) ){
    char *zKey = fts5EntryKey(p);
    int nTerm = p->nKey;
    fts5HashAddPoslistSize(pHash, p, 0);
    *pzTerm = zKey;
    *pnTerm = nTerm;
    *ppDoclist = (const u8*)&zKey[nTerm];
    *pnDoclist = p->nData - (sizeof(Fts5HashEntry) + nTerm);
  }else{
    *pzTerm = 0;
    *pnTerm = 0;
    *ppDoclist = 0;
    *pnDoclist = 0;
  }
}

/*
** 2014 May 31
236691
236692
236693
236694
236695
236696
236697



236698
236699
236700
236701
236702
236703
236704
typedef struct Fts5PageWriter Fts5PageWriter;
typedef struct Fts5SegIter Fts5SegIter;
typedef struct Fts5DoclistIter Fts5DoclistIter;
typedef struct Fts5SegWriter Fts5SegWriter;
typedef struct Fts5Structure Fts5Structure;
typedef struct Fts5StructureLevel Fts5StructureLevel;
typedef struct Fts5StructureSegment Fts5StructureSegment;




struct Fts5Data {
  u8 *p;                          /* Pointer to buffer containing record */
  int nn;                         /* Size of record in bytes */
  int szLeaf;                     /* Size of leaf without page-index */
};








>
>
>







238859
238860
238861
238862
238863
238864
238865
238866
238867
238868
238869
238870
238871
238872
238873
238874
238875
typedef struct Fts5PageWriter Fts5PageWriter;
typedef struct Fts5SegIter Fts5SegIter;
typedef struct Fts5DoclistIter Fts5DoclistIter;
typedef struct Fts5SegWriter Fts5SegWriter;
typedef struct Fts5Structure Fts5Structure;
typedef struct Fts5StructureLevel Fts5StructureLevel;
typedef struct Fts5StructureSegment Fts5StructureSegment;
typedef struct Fts5TokenDataIter Fts5TokenDataIter;
typedef struct Fts5TokenDataMap Fts5TokenDataMap;
typedef struct Fts5TombstoneArray Fts5TombstoneArray;

struct Fts5Data {
  u8 *p;                          /* Pointer to buffer containing record */
  int nn;                         /* Size of record in bytes */
  int szLeaf;                     /* Size of leaf without page-index */
};

236725
236726
236727
236728
236729
236730
236731

236732
236733
236734
236735
236736
236737
236738
236739

236740
236741
236742
236743
236744
236745
236746
  i64 iWriteRowid;                /* Rowid for current doc being written */
  int bDelete;                    /* Current write is a delete */
  int nContentlessDelete;         /* Number of contentless delete ops */
  int nPendingRow;                /* Number of INSERT in hash table */

  /* Error state. */
  int rc;                         /* Current error code */


  /* State used by the fts5DataXXX() functions. */
  sqlite3_blob *pReader;          /* RO incr-blob open on %_data table */
  sqlite3_stmt *pWriter;          /* "INSERT ... %_data VALUES(?,?)" */
  sqlite3_stmt *pDeleter;         /* "DELETE FROM %_data ... id>=? AND id<=?" */
  sqlite3_stmt *pIdxWriter;       /* "INSERT ... %_idx VALUES(?,?,?,?)" */
  sqlite3_stmt *pIdxDeleter;      /* "DELETE FROM %_idx WHERE segid=?" */
  sqlite3_stmt *pIdxSelect;

  int nRead;                      /* Total number of blocks read */

  sqlite3_stmt *pDeleteFromIdx;

  sqlite3_stmt *pDataVersion;
  i64 iStructVersion;             /* data_version when pStruct read */
  Fts5Structure *pStruct;         /* Current db structure (or NULL) */







>








>







238896
238897
238898
238899
238900
238901
238902
238903
238904
238905
238906
238907
238908
238909
238910
238911
238912
238913
238914
238915
238916
238917
238918
238919
  i64 iWriteRowid;                /* Rowid for current doc being written */
  int bDelete;                    /* Current write is a delete */
  int nContentlessDelete;         /* Number of contentless delete ops */
  int nPendingRow;                /* Number of INSERT in hash table */

  /* Error state. */
  int rc;                         /* Current error code */
  int flushRc;

  /* State used by the fts5DataXXX() functions. */
  sqlite3_blob *pReader;          /* RO incr-blob open on %_data table */
  sqlite3_stmt *pWriter;          /* "INSERT ... %_data VALUES(?,?)" */
  sqlite3_stmt *pDeleter;         /* "DELETE FROM %_data ... id>=? AND id<=?" */
  sqlite3_stmt *pIdxWriter;       /* "INSERT ... %_idx VALUES(?,?,?,?)" */
  sqlite3_stmt *pIdxDeleter;      /* "DELETE FROM %_idx WHERE segid=?" */
  sqlite3_stmt *pIdxSelect;
  sqlite3_stmt *pIdxNextSelect;
  int nRead;                      /* Total number of blocks read */

  sqlite3_stmt *pDeleteFromIdx;

  sqlite3_stmt *pDataVersion;
  i64 iStructVersion;             /* data_version when pStruct read */
  Fts5Structure *pStruct;         /* Current db structure (or NULL) */
236886
236887
236888
236889
236890
236891
236892
236893
236894
236895
236896
236897
236898
236899
236900
236901
struct Fts5SegIter {
  Fts5StructureSegment *pSeg;     /* Segment to iterate through */
  int flags;                      /* Mask of configuration flags */
  int iLeafPgno;                  /* Current leaf page number */
  Fts5Data *pLeaf;                /* Current leaf data */
  Fts5Data *pNextLeaf;            /* Leaf page (iLeafPgno+1) */
  i64 iLeafOffset;                /* Byte offset within current leaf */
  Fts5Data **apTombstone;         /* Array of tombstone pages */
  int nTombstone;

  /* Next method */
  void (*xNext)(Fts5Index*, Fts5SegIter*, int*);

  /* The page and offset from which the current term was read. The offset
  ** is the offset of the first rowid in the current doclist.  */
  int iTermLeafPgno;







|
<







239059
239060
239061
239062
239063
239064
239065
239066

239067
239068
239069
239070
239071
239072
239073
struct Fts5SegIter {
  Fts5StructureSegment *pSeg;     /* Segment to iterate through */
  int flags;                      /* Mask of configuration flags */
  int iLeafPgno;                  /* Current leaf page number */
  Fts5Data *pLeaf;                /* Current leaf data */
  Fts5Data *pNextLeaf;            /* Leaf page (iLeafPgno+1) */
  i64 iLeafOffset;                /* Byte offset within current leaf */
  Fts5TombstoneArray *pTombArray; /* Array of tombstone pages */


  /* Next method */
  void (*xNext)(Fts5Index*, Fts5SegIter*, int*);

  /* The page and offset from which the current term was read. The offset
  ** is the offset of the first rowid in the current doclist.  */
  int iTermLeafPgno;
236913
236914
236915
236916
236917
236918
236919









236920
236921
236922
236923
236924
236925
236926

  /* Variables populated based on current entry. */
  Fts5Buffer term;                /* Current term */
  i64 iRowid;                     /* Current rowid */
  int nPos;                       /* Number of bytes in current position list */
  u8 bDel;                        /* True if the delete flag is set */
};










/*
** Argument is a pointer to an Fts5Data structure that contains a
** leaf page.
*/
#define ASSERT_SZLEAF_OK(x) assert( \
    (x)->szLeaf==(x)->nn || (x)->szLeaf==fts5GetU16(&(x)->p[2]) \







>
>
>
>
>
>
>
>
>







239085
239086
239087
239088
239089
239090
239091
239092
239093
239094
239095
239096
239097
239098
239099
239100
239101
239102
239103
239104
239105
239106
239107

  /* Variables populated based on current entry. */
  Fts5Buffer term;                /* Current term */
  i64 iRowid;                     /* Current rowid */
  int nPos;                       /* Number of bytes in current position list */
  u8 bDel;                        /* True if the delete flag is set */
};

/*
** Array of tombstone pages. Reference counted.
*/
struct Fts5TombstoneArray {
  int nRef;                       /* Number of pointers to this object */
  int nTombstone;
  Fts5Data *apTombstone[1];       /* Array of tombstone pages */
};

/*
** Argument is a pointer to an Fts5Data structure that contains a
** leaf page.
*/
#define ASSERT_SZLEAF_OK(x) assert( \
    (x)->szLeaf==(x)->nn || (x)->szLeaf==fts5GetU16(&(x)->p[2]) \
236958
236959
236960
236961
236962
236963
236964






236965
236966
236967

236968
236969
236970
236971
236972
236973
236974
236975
236976
236977
236978
236979
236980
236981
236982
236983
236984
236985
236986
236987
236988
236989
236990
236991
**
** aFirst[1] contains the index in aSeg[] of the iterator that points to
** the smallest key overall. aFirst[0] is unused.
**
** poslist:
**   Used by sqlite3Fts5IterPoslist() when the poslist needs to be buffered.
**   There is no way to tell if this is populated or not.






*/
struct Fts5Iter {
  Fts5IndexIter base;             /* Base class containing output vars */


  Fts5Index *pIndex;              /* Index that owns this iterator */
  Fts5Buffer poslist;             /* Buffer containing current poslist */
  Fts5Colset *pColset;            /* Restrict matches to these columns */

  /* Invoked to set output variables. */
  void (*xSetOutputs)(Fts5Iter*, Fts5SegIter*);

  int nSeg;                       /* Size of aSeg[] array */
  int bRev;                       /* True to iterate in reverse order */
  u8 bSkipEmpty;                  /* True to skip deleted entries */

  i64 iSwitchRowid;               /* Firstest rowid of other than aFirst[1] */
  Fts5CResult *aFirst;            /* Current merge state (see above) */
  Fts5SegIter aSeg[1];            /* Array of segment iterators */
};


/*
** An instance of the following type is used to iterate through the contents
** of a doclist-index record.
**
** pData:
**   Record containing the doclist-index data.







>
>
>
>
>
>



>
















<







239139
239140
239141
239142
239143
239144
239145
239146
239147
239148
239149
239150
239151
239152
239153
239154
239155
239156
239157
239158
239159
239160
239161
239162
239163
239164
239165
239166
239167
239168
239169
239170
239171

239172
239173
239174
239175
239176
239177
239178
**
** aFirst[1] contains the index in aSeg[] of the iterator that points to
** the smallest key overall. aFirst[0] is unused.
**
** poslist:
**   Used by sqlite3Fts5IterPoslist() when the poslist needs to be buffered.
**   There is no way to tell if this is populated or not.
**
** pColset:
**   If not NULL, points to an object containing a set of column indices.
**   Only matches that occur in one of these columns will be returned.
**   The Fts5Iter does not own the Fts5Colset object, and so it is not
**   freed when the iterator is closed - it is owned by the upper layer.
*/
struct Fts5Iter {
  Fts5IndexIter base;             /* Base class containing output vars */
  Fts5TokenDataIter *pTokenDataIter;

  Fts5Index *pIndex;              /* Index that owns this iterator */
  Fts5Buffer poslist;             /* Buffer containing current poslist */
  Fts5Colset *pColset;            /* Restrict matches to these columns */

  /* Invoked to set output variables. */
  void (*xSetOutputs)(Fts5Iter*, Fts5SegIter*);

  int nSeg;                       /* Size of aSeg[] array */
  int bRev;                       /* True to iterate in reverse order */
  u8 bSkipEmpty;                  /* True to skip deleted entries */

  i64 iSwitchRowid;               /* Firstest rowid of other than aFirst[1] */
  Fts5CResult *aFirst;            /* Current merge state (see above) */
  Fts5SegIter aSeg[1];            /* Array of segment iterators */
};


/*
** An instance of the following type is used to iterate through the contents
** of a doclist-index record.
**
** pData:
**   Record containing the doclist-index data.
237896
237897
237898
237899
237900
237901
237902
237903
237904
237905
237906
237907
237908
237909
237910
237911
237912
  }else{
    int iOff;
    for(iOff=pLvl->iOff; iOff<pData->nn; iOff++){
      if( pData->p[iOff] ) break;
    }

    if( iOff<pData->nn ){
      i64 iVal;
      pLvl->iLeafPgno += (iOff - pLvl->iOff) + 1;
      iOff += fts5GetVarint(&pData->p[iOff], (u64*)&iVal);
      pLvl->iRowid += iVal;
      pLvl->iOff = iOff;
    }else{
      pLvl->bEof = 1;
    }
  }








|

|







240083
240084
240085
240086
240087
240088
240089
240090
240091
240092
240093
240094
240095
240096
240097
240098
240099
  }else{
    int iOff;
    for(iOff=pLvl->iOff; iOff<pData->nn; iOff++){
      if( pData->p[iOff] ) break;
    }

    if( iOff<pData->nn ){
      u64 iVal;
      pLvl->iLeafPgno += (iOff - pLvl->iOff) + 1;
      iOff += fts5GetVarint(&pData->p[iOff], &iVal);
      pLvl->iRowid += iVal;
      pLvl->iOff = iOff;
    }else{
      pLvl->bEof = 1;
    }
  }

238277
238278
238279
238280
238281
238282
238283
238284
238285
238286
238287
238288
238289
238290
238291

238292
238293
238294
238295


238296
238297
238298
238299
238300
238301
238302
    pIter->xNext = fts5SegIterNext_None;
  }else{
    pIter->xNext = fts5SegIterNext;
  }
}

/*
** Allocate a tombstone hash page array (pIter->apTombstone) for the
** iterator passed as the second argument. If an OOM error occurs, leave
** an error in the Fts5Index object.
*/
static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){
  const int nTomb = pIter->pSeg->nPgTombstone;
  if( nTomb>0 ){
    Fts5Data **apTomb = 0;

    apTomb = (Fts5Data**)sqlite3Fts5MallocZero(&p->rc, sizeof(Fts5Data)*nTomb);
    if( apTomb ){
      pIter->apTombstone = apTomb;
      pIter->nTombstone = nTomb;


    }
  }
}

/*
** Initialize the iterator object pIter to iterate through the entries in
** segment pSeg. The iterator is left pointing to the first entry when







|
|
|




|
>
|
|
<
|
>
>







240464
240465
240466
240467
240468
240469
240470
240471
240472
240473
240474
240475
240476
240477
240478
240479
240480
240481

240482
240483
240484
240485
240486
240487
240488
240489
240490
240491
    pIter->xNext = fts5SegIterNext_None;
  }else{
    pIter->xNext = fts5SegIterNext;
  }
}

/*
** Allocate a tombstone hash page array object (pIter->pTombArray) for
** the iterator passed as the second argument. If an OOM error occurs,
** leave an error in the Fts5Index object.
*/
static void fts5SegIterAllocTombstone(Fts5Index *p, Fts5SegIter *pIter){
  const int nTomb = pIter->pSeg->nPgTombstone;
  if( nTomb>0 ){
    int nByte = nTomb * sizeof(Fts5Data*) + sizeof(Fts5TombstoneArray);
    Fts5TombstoneArray *pNew;
    pNew = (Fts5TombstoneArray*)sqlite3Fts5MallocZero(&p->rc, nByte);
    if( pNew ){

      pNew->nTombstone = nTomb;
      pNew->nRef = 1;
      pIter->pTombArray = pNew;
    }
  }
}

/*
** Initialize the iterator object pIter to iterate through the entries in
** segment pSeg. The iterator is left pointing to the first entry when
238545
238546
238547
238548
238549
238550
238551

238552
238553
238554
238555
238556
238557
238558
238559
238560
238561
238562
238563
238564
238565
238566
238567
        iOff += fts5GetVarint32(&pIter->pLeaf->p[iOff], nKeep);
      }
      pIter->iLeafOffset = iOff;
      fts5SegIterLoadTerm(p, pIter, nKeep);
    }else{
      const u8 *pList = 0;
      const char *zTerm = 0;

      int nList;
      sqlite3Fts5HashScanNext(p->pHash);
      sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList);
      if( pList==0 ) goto next_none_eof;
      pIter->pLeaf->p = (u8*)pList;
      pIter->pLeaf->nn = nList;
      pIter->pLeaf->szLeaf = nList;
      pIter->iEndofDoclist = nList;
      sqlite3Fts5BufferSet(&p->rc,&pIter->term, (int)strlen(zTerm), (u8*)zTerm);
      pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid);
    }

    if( pbNewTerm ) *pbNewTerm = 1;
  }else{
    goto next_none_eof;
  }







>


|





|







240734
240735
240736
240737
240738
240739
240740
240741
240742
240743
240744
240745
240746
240747
240748
240749
240750
240751
240752
240753
240754
240755
240756
240757
        iOff += fts5GetVarint32(&pIter->pLeaf->p[iOff], nKeep);
      }
      pIter->iLeafOffset = iOff;
      fts5SegIterLoadTerm(p, pIter, nKeep);
    }else{
      const u8 *pList = 0;
      const char *zTerm = 0;
      int nTerm = 0;
      int nList;
      sqlite3Fts5HashScanNext(p->pHash);
      sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &nTerm, &pList, &nList);
      if( pList==0 ) goto next_none_eof;
      pIter->pLeaf->p = (u8*)pList;
      pIter->pLeaf->nn = nList;
      pIter->pLeaf->szLeaf = nList;
      pIter->iEndofDoclist = nList;
      sqlite3Fts5BufferSet(&p->rc,&pIter->term, nTerm, (u8*)zTerm);
      pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid);
    }

    if( pbNewTerm ) *pbNewTerm = 1;
  }else{
    goto next_none_eof;
  }
238619
238620
238621
238622
238623
238624
238625

238626
238627
238628
238629
238630
238631
238632
238633
238634
238635
238636
238637
238638
238639
238640
238641
238642
238643
238644
238645
238646
238647
238648
      assert_nc( iDelta>0 );
    }
    pIter->iLeafOffset = iOff;

  }else if( pIter->pSeg==0 ){
    const u8 *pList = 0;
    const char *zTerm = 0;

    int nList = 0;
    assert( (pIter->flags & FTS5_SEGITER_ONETERM) || pbNewTerm );
    if( 0==(pIter->flags & FTS5_SEGITER_ONETERM) ){
      sqlite3Fts5HashScanNext(p->pHash);
      sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &pList, &nList);
    }
    if( pList==0 ){
      fts5DataRelease(pIter->pLeaf);
      pIter->pLeaf = 0;
    }else{
      pIter->pLeaf->p = (u8*)pList;
      pIter->pLeaf->nn = nList;
      pIter->pLeaf->szLeaf = nList;
      pIter->iEndofDoclist = nList+1;
      sqlite3Fts5BufferSet(&p->rc, &pIter->term, (int)strlen(zTerm),
          (u8*)zTerm);
      pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid);
      *pbNewTerm = 1;
    }
  }else{
    iOff = 0;
    /* Next entry is not on the current page */
    while( iOff==0 ){







>




|









|
<







240809
240810
240811
240812
240813
240814
240815
240816
240817
240818
240819
240820
240821
240822
240823
240824
240825
240826
240827
240828
240829
240830
240831

240832
240833
240834
240835
240836
240837
240838
      assert_nc( iDelta>0 );
    }
    pIter->iLeafOffset = iOff;

  }else if( pIter->pSeg==0 ){
    const u8 *pList = 0;
    const char *zTerm = 0;
    int nTerm = 0;
    int nList = 0;
    assert( (pIter->flags & FTS5_SEGITER_ONETERM) || pbNewTerm );
    if( 0==(pIter->flags & FTS5_SEGITER_ONETERM) ){
      sqlite3Fts5HashScanNext(p->pHash);
      sqlite3Fts5HashScanEntry(p->pHash, &zTerm, &nTerm, &pList, &nList);
    }
    if( pList==0 ){
      fts5DataRelease(pIter->pLeaf);
      pIter->pLeaf = 0;
    }else{
      pIter->pLeaf->p = (u8*)pList;
      pIter->pLeaf->nn = nList;
      pIter->pLeaf->szLeaf = nList;
      pIter->iEndofDoclist = nList+1;
      sqlite3Fts5BufferSet(&p->rc, &pIter->term, nTerm, (u8*)zTerm);

      pIter->iLeafOffset = fts5GetVarint(pList, (u64*)&pIter->iRowid);
      *pbNewTerm = 1;
    }
  }else{
    iOff = 0;
    /* Next entry is not on the current page */
    while( iOff==0 ){
239020
239021
239022
239023
239024
239025
239026
239027
239028
239029
239030
239031
239032
239033
239034
239035
239036
239037
239038
239039
239040
239041
239042

239043

239044
239045
239046
239047
239048
239049
239050
239051
239052
239053
239054
239055
239056
239057
239058
239059









































































239060
239061
239062
239063
239064
239065
239066
  pIter->iLeafPgno = iPg - 1;
  fts5SegIterNextPage(p, pIter);

  if( pIter->pLeaf ){
    fts5LeafSeek(p, bGe, pIter, pTerm, nTerm);
  }

  if( p->rc==SQLITE_OK && bGe==0 ){
    pIter->flags |= FTS5_SEGITER_ONETERM;
    if( pIter->pLeaf ){
      if( flags & FTS5INDEX_QUERY_DESC ){
        pIter->flags |= FTS5_SEGITER_REVERSE;
      }
      if( bDlidx ){
        fts5SegIterLoadDlidx(p, pIter);
      }
      if( flags & FTS5INDEX_QUERY_DESC ){
        fts5SegIterReverse(p, pIter);
      }
    }
  }

  fts5SegIterSetNext(p, pIter);

  fts5SegIterAllocTombstone(p, pIter);


  /* Either:
  **
  **   1) an error has occurred, or
  **   2) the iterator points to EOF, or
  **   3) the iterator points to an entry with term (pTerm/nTerm), or
  **   4) the FTS5INDEX_QUERY_SCAN flag was set and the iterator points
  **      to an entry with a term greater than or equal to (pTerm/nTerm).
  */
  assert_nc( p->rc!=SQLITE_OK                                       /* 1 */
   || pIter->pLeaf==0                                               /* 2 */
   || fts5BufferCompareBlob(&pIter->term, pTerm, nTerm)==0          /* 3 */
   || (bGe && fts5BufferCompareBlob(&pIter->term, pTerm, nTerm)>0)  /* 4 */
  );
}










































































/*
** Initialize the object pIter to point to term pTerm/nTerm within the
** in-memory hash table. If there is no such term in the hash-table, the
** iterator is set to EOF.
**
** If an error occurs, Fts5Index.rc is set to an appropriate error code. If
** an error has already occurred when this function is called, it is a no-op.







|















>
|
>
















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







241210
241211
241212
241213
241214
241215
241216
241217
241218
241219
241220
241221
241222
241223
241224
241225
241226
241227
241228
241229
241230
241231
241232
241233
241234
241235
241236
241237
241238
241239
241240
241241
241242
241243
241244
241245
241246
241247
241248
241249
241250
241251
241252
241253
241254
241255
241256
241257
241258
241259
241260
241261
241262
241263
241264
241265
241266
241267
241268
241269
241270
241271
241272
241273
241274
241275
241276
241277
241278
241279
241280
241281
241282
241283
241284
241285
241286
241287
241288
241289
241290
241291
241292
241293
241294
241295
241296
241297
241298
241299
241300
241301
241302
241303
241304
241305
241306
241307
241308
241309
241310
241311
241312
241313
241314
241315
241316
241317
241318
241319
241320
241321
241322
241323
241324
241325
241326
241327
241328
241329
241330
241331
  pIter->iLeafPgno = iPg - 1;
  fts5SegIterNextPage(p, pIter);

  if( pIter->pLeaf ){
    fts5LeafSeek(p, bGe, pIter, pTerm, nTerm);
  }

  if( p->rc==SQLITE_OK && (bGe==0 || (flags & FTS5INDEX_QUERY_SCANONETERM)) ){
    pIter->flags |= FTS5_SEGITER_ONETERM;
    if( pIter->pLeaf ){
      if( flags & FTS5INDEX_QUERY_DESC ){
        pIter->flags |= FTS5_SEGITER_REVERSE;
      }
      if( bDlidx ){
        fts5SegIterLoadDlidx(p, pIter);
      }
      if( flags & FTS5INDEX_QUERY_DESC ){
        fts5SegIterReverse(p, pIter);
      }
    }
  }

  fts5SegIterSetNext(p, pIter);
  if( 0==(flags & FTS5INDEX_QUERY_SCANONETERM) ){
    fts5SegIterAllocTombstone(p, pIter);
  }

  /* Either:
  **
  **   1) an error has occurred, or
  **   2) the iterator points to EOF, or
  **   3) the iterator points to an entry with term (pTerm/nTerm), or
  **   4) the FTS5INDEX_QUERY_SCAN flag was set and the iterator points
  **      to an entry with a term greater than or equal to (pTerm/nTerm).
  */
  assert_nc( p->rc!=SQLITE_OK                                       /* 1 */
   || pIter->pLeaf==0                                               /* 2 */
   || fts5BufferCompareBlob(&pIter->term, pTerm, nTerm)==0          /* 3 */
   || (bGe && fts5BufferCompareBlob(&pIter->term, pTerm, nTerm)>0)  /* 4 */
  );
}


/*
** SQL used by fts5SegIterNextInit() to find the page to open.
*/
static sqlite3_stmt *fts5IdxNextStmt(Fts5Index *p){
  if( p->pIdxNextSelect==0 ){
    Fts5Config *pConfig = p->pConfig;
    fts5IndexPrepareStmt(p, &p->pIdxNextSelect, sqlite3_mprintf(
          "SELECT pgno FROM '%q'.'%q_idx' WHERE "
          "segid=? AND term>? ORDER BY term ASC LIMIT 1",
          pConfig->zDb, pConfig->zName
    ));

  }
  return p->pIdxNextSelect;
}

/*
** This is similar to fts5SegIterSeekInit(), except that it initializes
** the segment iterator to point to the first term following the page
** with pToken/nToken on it.
*/
static void fts5SegIterNextInit(
  Fts5Index *p,
  const char *pTerm, int nTerm,
  Fts5StructureSegment *pSeg,     /* Description of segment */
  Fts5SegIter *pIter              /* Object to populate */
){
  int iPg = -1;                   /* Page of segment to open */
  int bDlidx = 0;
  sqlite3_stmt *pSel = 0;         /* SELECT to find iPg */

  pSel = fts5IdxNextStmt(p);
  if( pSel ){
    assert( p->rc==SQLITE_OK );
    sqlite3_bind_int(pSel, 1, pSeg->iSegid);
    sqlite3_bind_blob(pSel, 2, pTerm, nTerm, SQLITE_STATIC);

    if( sqlite3_step(pSel)==SQLITE_ROW ){
      i64 val = sqlite3_column_int64(pSel, 0);
      iPg = (int)(val>>1);
      bDlidx = (val & 0x0001);
    }
    p->rc = sqlite3_reset(pSel);
    sqlite3_bind_null(pSel, 2);
    if( p->rc ) return;
  }

  memset(pIter, 0, sizeof(*pIter));
  pIter->pSeg = pSeg;
  pIter->flags |= FTS5_SEGITER_ONETERM;
  if( iPg>=0 ){
    pIter->iLeafPgno = iPg - 1;
    fts5SegIterNextPage(p, pIter);
    fts5SegIterSetNext(p, pIter);
  }
  if( pIter->pLeaf ){
    const u8 *a = pIter->pLeaf->p;
    int iTermOff = 0;

    pIter->iPgidxOff = pIter->pLeaf->szLeaf;
    pIter->iPgidxOff += fts5GetVarint32(&a[pIter->iPgidxOff], iTermOff);
    pIter->iLeafOffset = iTermOff;
    fts5SegIterLoadTerm(p, pIter, 0);
    fts5SegIterLoadNPos(p, pIter);
    if( bDlidx ) fts5SegIterLoadDlidx(p, pIter);

    assert( p->rc!=SQLITE_OK ||
        fts5BufferCompareBlob(&pIter->term, (const u8*)pTerm, nTerm)>0
    );
  }
}

/*
** Initialize the object pIter to point to term pTerm/nTerm within the
** in-memory hash table. If there is no such term in the hash-table, the
** iterator is set to EOF.
**
** If an error occurs, Fts5Index.rc is set to an appropriate error code. If
** an error has already occurred when this function is called, it is a no-op.
239079
239080
239081
239082
239083
239084
239085
239086
239087
239088
239089
239090
239091
239092
239093
239094
  assert( p->pHash );
  assert( p->rc==SQLITE_OK );

  if( pTerm==0 || (flags & FTS5INDEX_QUERY_SCAN) ){
    const u8 *pList = 0;

    p->rc = sqlite3Fts5HashScanInit(p->pHash, (const char*)pTerm, nTerm);
    sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &pList, &nList);
    n = (z ? (int)strlen((const char*)z) : 0);
    if( pList ){
      pLeaf = fts5IdxMalloc(p, sizeof(Fts5Data));
      if( pLeaf ){
        pLeaf->p = (u8*)pList;
      }
    }








|
<







241344
241345
241346
241347
241348
241349
241350
241351

241352
241353
241354
241355
241356
241357
241358
  assert( p->pHash );
  assert( p->rc==SQLITE_OK );

  if( pTerm==0 || (flags & FTS5INDEX_QUERY_SCAN) ){
    const u8 *pList = 0;

    p->rc = sqlite3Fts5HashScanInit(p->pHash, (const char*)pTerm, nTerm);
    sqlite3Fts5HashScanEntry(p->pHash, (const char**)&z, &n, &pList, &nList);

    if( pList ){
      pLeaf = fts5IdxMalloc(p, sizeof(Fts5Data));
      if( pLeaf ){
        pLeaf->p = (u8*)pList;
      }
    }

239138
239139
239140
239141
239142
239143
239144

















239145
239146
239147
239148
239149
239150
239151
239152
239153
239154
239155
239156
239157
239158
239159
239160
    int ii;
    for(ii=0; ii<n; ii++){
      fts5DataRelease(ap[ii]);
    }
    sqlite3_free(ap);
  }
}


















/*
** Zero the iterator passed as the only argument.
*/
static void fts5SegIterClear(Fts5SegIter *pIter){
  fts5BufferFree(&pIter->term);
  fts5DataRelease(pIter->pLeaf);
  fts5DataRelease(pIter->pNextLeaf);
  fts5IndexFreeArray(pIter->apTombstone, pIter->nTombstone);
  fts5DlidxIterFree(pIter->pDlidx);
  sqlite3_free(pIter->aRowidOffset);
  memset(pIter, 0, sizeof(Fts5SegIter));
}

#ifdef SQLITE_DEBUG








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








|







241402
241403
241404
241405
241406
241407
241408
241409
241410
241411
241412
241413
241414
241415
241416
241417
241418
241419
241420
241421
241422
241423
241424
241425
241426
241427
241428
241429
241430
241431
241432
241433
241434
241435
241436
241437
241438
241439
241440
241441
    int ii;
    for(ii=0; ii<n; ii++){
      fts5DataRelease(ap[ii]);
    }
    sqlite3_free(ap);
  }
}

/*
** Decrement the ref-count of the object passed as the only argument. If it
** reaches 0, free it and its contents.
*/
static void fts5TombstoneArrayDelete(Fts5TombstoneArray *p){
  if( p ){
    p->nRef--;
    if( p->nRef<=0 ){
      int ii;
      for(ii=0; ii<p->nTombstone; ii++){
        fts5DataRelease(p->apTombstone[ii]);
      }
      sqlite3_free(p);
    }
  }
}

/*
** Zero the iterator passed as the only argument.
*/
static void fts5SegIterClear(Fts5SegIter *pIter){
  fts5BufferFree(&pIter->term);
  fts5DataRelease(pIter->pLeaf);
  fts5DataRelease(pIter->pNextLeaf);
  fts5TombstoneArrayDelete(pIter->pTombArray);
  fts5DlidxIterFree(pIter->pDlidx);
  sqlite3_free(pIter->aRowidOffset);
  memset(pIter, 0, sizeof(Fts5SegIter));
}

#ifdef SQLITE_DEBUG

239391
239392
239393
239394
239395
239396
239397
239398
239399
239400
239401
239402
239403
239404
239405
    if( pIter->pLeaf==0 ) break;
    if( bRev==0 && pIter->iRowid>=iMatch ) break;
    if( bRev!=0 && pIter->iRowid<=iMatch ) break;
    bMove = 1;
  }while( p->rc==SQLITE_OK );
}


/*
** Free the iterator object passed as the second argument.
*/
static void fts5MultiIterFree(Fts5Iter *pIter){
  if( pIter ){
    int i;
    for(i=0; i<pIter->nSeg; i++){







<







241672
241673
241674
241675
241676
241677
241678

241679
241680
241681
241682
241683
241684
241685
    if( pIter->pLeaf==0 ) break;
    if( bRev==0 && pIter->iRowid>=iMatch ) break;
    if( bRev!=0 && pIter->iRowid<=iMatch ) break;
    bMove = 1;
  }while( p->rc==SQLITE_OK );
}


/*
** Free the iterator object passed as the second argument.
*/
static void fts5MultiIterFree(Fts5Iter *pIter){
  if( pIter ){
    int i;
    for(i=0; i<pIter->nSeg; i++){
239536
239537
239538
239539
239540
239541
239542

239543
239544
239545
239546
239547
239548
239549
239550
239551
239552
239553
239554
239555
239556
239557
239558
239559
239560
239561
239562
239563
239564
239565
239566
239567
** Return true if the iterator passed as the only argument points
** to an segment entry for which there is a tombstone. Return false
** if there is no tombstone or if the iterator is already at EOF.
*/
static int fts5MultiIterIsDeleted(Fts5Iter *pIter){
  int iFirst = pIter->aFirst[1].iFirst;
  Fts5SegIter *pSeg = &pIter->aSeg[iFirst];


  if( pSeg->pLeaf && pSeg->nTombstone ){
    /* Figure out which page the rowid might be present on. */
    int iPg = ((u64)pSeg->iRowid) % pSeg->nTombstone;
    assert( iPg>=0 );

    /* If tombstone hash page iPg has not yet been loaded from the
    ** database, load it now. */
    if( pSeg->apTombstone[iPg]==0 ){
      pSeg->apTombstone[iPg] = fts5DataRead(pIter->pIndex,
          FTS5_TOMBSTONE_ROWID(pSeg->pSeg->iSegid, iPg)
      );
      if( pSeg->apTombstone[iPg]==0 ) return 0;
    }

    return fts5IndexTombstoneQuery(
        pSeg->apTombstone[iPg],
        pSeg->nTombstone,
        pSeg->iRowid
    );
  }

  return 0;
}








>

|

|




|
|


|



|
|







241816
241817
241818
241819
241820
241821
241822
241823
241824
241825
241826
241827
241828
241829
241830
241831
241832
241833
241834
241835
241836
241837
241838
241839
241840
241841
241842
241843
241844
241845
241846
241847
241848
** Return true if the iterator passed as the only argument points
** to an segment entry for which there is a tombstone. Return false
** if there is no tombstone or if the iterator is already at EOF.
*/
static int fts5MultiIterIsDeleted(Fts5Iter *pIter){
  int iFirst = pIter->aFirst[1].iFirst;
  Fts5SegIter *pSeg = &pIter->aSeg[iFirst];
  Fts5TombstoneArray *pArray = pSeg->pTombArray;

  if( pSeg->pLeaf && pArray ){
    /* Figure out which page the rowid might be present on. */
    int iPg = ((u64)pSeg->iRowid) % pArray->nTombstone;
    assert( iPg>=0 );

    /* If tombstone hash page iPg has not yet been loaded from the
    ** database, load it now. */
    if( pArray->apTombstone[iPg]==0 ){
      pArray->apTombstone[iPg] = fts5DataRead(pIter->pIndex,
          FTS5_TOMBSTONE_ROWID(pSeg->pSeg->iSegid, iPg)
      );
      if( pArray->apTombstone[iPg]==0 ) return 0;
    }

    return fts5IndexTombstoneQuery(
        pArray->apTombstone[iPg],
        pArray->nTombstone,
        pSeg->iRowid
    );
  }

  return 0;
}

240092
240093
240094
240095
240096
240097
240098


























240099
240100
240101
240102
240103
240104
240105
      }else{
        pIter->xSetOutputs = fts5IterSetOutputs_Col;
      }
    }
  }
}




























/*
** Allocate a new Fts5Iter object.
**
** The new object will be used to iterate through data in structure pStruct.
** If iLevel is -ve, then all data in all segments is merged. Or, if iLevel
** is zero or greater, data from the first nSegment segments on level iLevel







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







242373
242374
242375
242376
242377
242378
242379
242380
242381
242382
242383
242384
242385
242386
242387
242388
242389
242390
242391
242392
242393
242394
242395
242396
242397
242398
242399
242400
242401
242402
242403
242404
242405
242406
242407
242408
242409
242410
242411
242412
      }else{
        pIter->xSetOutputs = fts5IterSetOutputs_Col;
      }
    }
  }
}

/*
** All the component segment-iterators of pIter have been set up. This
** functions finishes setup for iterator pIter itself.
*/
static void fts5MultiIterFinishSetup(Fts5Index *p, Fts5Iter *pIter){
  int iIter;
  for(iIter=pIter->nSeg-1; iIter>0; iIter--){
    int iEq;
    if( (iEq = fts5MultiIterDoCompare(pIter, iIter)) ){
      Fts5SegIter *pSeg = &pIter->aSeg[iEq];
      if( p->rc==SQLITE_OK ) pSeg->xNext(p, pSeg, 0);
      fts5MultiIterAdvanced(p, pIter, iEq, iIter);
    }
  }
  fts5MultiIterSetEof(pIter);
  fts5AssertMultiIterSetup(p, pIter);

  if( (pIter->bSkipEmpty && fts5MultiIterIsEmpty(p, pIter))
   || fts5MultiIterIsDeleted(pIter)
  ){
    fts5MultiIterNext(p, pIter, 0, 0);
  }else if( pIter->base.bEof==0 ){
    Fts5SegIter *pSeg = &pIter->aSeg[pIter->aFirst[1].iFirst];
    pIter->xSetOutputs(pIter, pSeg);
  }
}

/*
** Allocate a new Fts5Iter object.
**
** The new object will be used to iterate through data in structure pStruct.
** If iLevel is -ve, then all data in all segments is merged. Or, if iLevel
** is zero or greater, data from the first nSegment segments on level iLevel
240173
240174
240175
240176
240177
240178
240179
240180
240181
240182
240183
240184
240185
240186
240187
240188
240189
240190
240191
240192
240193
240194
240195
240196
240197
240198
240199
240200
240201
240202
240203
240204
240205
240206
240207
240208
240209
240210
240211
      for(iSeg=nSeg-1; iSeg>=0; iSeg--){
        fts5SegIterInit(p, &pLvl->aSeg[iSeg], &pNew->aSeg[iIter++]);
      }
    }
    assert( iIter==nSeg );
  }

  /* If the above was successful, each component iterators now points
  ** to the first entry in its segment. In this case initialize the
  ** aFirst[] array. Or, if an error has occurred, free the iterator
  ** object and set the output variable to NULL.  */
  if( p->rc==SQLITE_OK ){
    for(iIter=pNew->nSeg-1; iIter>0; iIter--){
      int iEq;
      if( (iEq = fts5MultiIterDoCompare(pNew, iIter)) ){
        Fts5SegIter *pSeg = &pNew->aSeg[iEq];
        if( p->rc==SQLITE_OK ) pSeg->xNext(p, pSeg, 0);
        fts5MultiIterAdvanced(p, pNew, iEq, iIter);
      }
    }
    fts5MultiIterSetEof(pNew);
    fts5AssertMultiIterSetup(p, pNew);

    if( (pNew->bSkipEmpty && fts5MultiIterIsEmpty(p, pNew))
     || fts5MultiIterIsDeleted(pNew)
    ){
      fts5MultiIterNext(p, pNew, 0, 0);
    }else if( pNew->base.bEof==0 ){
      Fts5SegIter *pSeg = &pNew->aSeg[pNew->aFirst[1].iFirst];
      pNew->xSetOutputs(pNew, pSeg);
    }

  }else{
    fts5MultiIterFree(pNew);
    *ppOut = 0;
  }

fts5MultiIterNew_post_check:
  assert( (*ppOut)!=0 || p->rc!=SQLITE_OK );







|




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







242480
242481
242482
242483
242484
242485
242486
242487
242488
242489
242490
242491








242492











242493
242494
242495
242496
242497
242498
242499
      for(iSeg=nSeg-1; iSeg>=0; iSeg--){
        fts5SegIterInit(p, &pLvl->aSeg[iSeg], &pNew->aSeg[iIter++]);
      }
    }
    assert( iIter==nSeg );
  }

  /* If the above was successful, each component iterator now points
  ** to the first entry in its segment. In this case initialize the
  ** aFirst[] array. Or, if an error has occurred, free the iterator
  ** object and set the output variable to NULL.  */
  if( p->rc==SQLITE_OK ){








    fts5MultiIterFinishSetup(p, pNew);











  }else{
    fts5MultiIterFree(pNew);
    *ppOut = 0;
  }

fts5MultiIterNew_post_check:
  assert( (*ppOut)!=0 || p->rc!=SQLITE_OK );
240222
240223
240224
240225
240226
240227
240228
240229
240230
240231
240232
240233
240234
240235
240236
  int bDesc,                      /* True for descending rowid order */
  Fts5Iter **ppOut                /* New object */
){
  Fts5Iter *pNew;
  pNew = fts5MultiIterAlloc(p, 2);
  if( pNew ){
    Fts5SegIter *pIter = &pNew->aSeg[1];

    pIter->flags = FTS5_SEGITER_ONETERM;
    if( pData->szLeaf>0 ){
      pIter->pLeaf = pData;
      pIter->iLeafOffset = fts5GetVarint(pData->p, (u64*)&pIter->iRowid);
      pIter->iEndofDoclist = pData->nn;
      pNew->aFirst[1].iFirst = 1;
      if( bDesc ){







<







242510
242511
242512
242513
242514
242515
242516

242517
242518
242519
242520
242521
242522
242523
  int bDesc,                      /* True for descending rowid order */
  Fts5Iter **ppOut                /* New object */
){
  Fts5Iter *pNew;
  pNew = fts5MultiIterAlloc(p, 2);
  if( pNew ){
    Fts5SegIter *pIter = &pNew->aSeg[1];

    pIter->flags = FTS5_SEGITER_ONETERM;
    if( pData->szLeaf>0 ){
      pIter->pLeaf = pData;
      pIter->iLeafOffset = fts5GetVarint(pData->p, (u64*)&pIter->iRowid);
      pIter->iEndofDoclist = pData->nn;
      pNew->aFirst[1].iFirst = 1;
      if( bDesc ){
240370
240371
240372
240373
240374
240375
240376

240377
240378
240379
240380
240381
240382
240383
*/
static void fts5IndexDiscardData(Fts5Index *p){
  assert( p->pHash || p->nPendingData==0 );
  if( p->pHash ){
    sqlite3Fts5HashClear(p->pHash);
    p->nPendingData = 0;
    p->nPendingRow = 0;

  }
  p->nContentlessDelete = 0;
}

/*
** Return the size of the prefix, in bytes, that buffer
** (pNew/<length-unknown>) shares with buffer (pOld/nOld).







>







242657
242658
242659
242660
242661
242662
242663
242664
242665
242666
242667
242668
242669
242670
242671
*/
static void fts5IndexDiscardData(Fts5Index *p){
  assert( p->pHash || p->nPendingData==0 );
  if( p->pHash ){
    sqlite3Fts5HashClear(p->pHash);
    p->nPendingData = 0;
    p->nPendingRow = 0;
    p->flushRc = SQLITE_OK;
  }
  p->nContentlessDelete = 0;
}

/*
** Return the size of the prefix, in bytes, that buffer
** (pNew/<length-unknown>) shares with buffer (pOld/nOld).
240585
240586
240587
240588
240589
240590
240591
240592
240593
240594
240595
240596
240597
240598
240599
      pDlidx->bPrevValid = 0;
      pDlidx->pgno++;
    }else{
      bDone = 1;
    }

    if( pDlidx->bPrevValid ){
      iVal = iRowid - pDlidx->iPrev;
    }else{
      i64 iPgno = (i==0 ? pWriter->writer.pgno : pDlidx[-1].pgno);
      assert( pDlidx->buf.n==0 );
      sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx->buf, !bDone);
      sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx->buf, iPgno);
      iVal = iRowid;
    }







|







242873
242874
242875
242876
242877
242878
242879
242880
242881
242882
242883
242884
242885
242886
242887
      pDlidx->bPrevValid = 0;
      pDlidx->pgno++;
    }else{
      bDone = 1;
    }

    if( pDlidx->bPrevValid ){
      iVal = (u64)iRowid - (u64)pDlidx->iPrev;
    }else{
      i64 iPgno = (i==0 ? pWriter->writer.pgno : pDlidx[-1].pgno);
      assert( pDlidx->buf.n==0 );
      sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx->buf, !bDone);
      sqlite3Fts5BufferAppendVarint(&p->rc, &pDlidx->buf, iPgno);
      iVal = iRowid;
    }
241708
241709
241710
241711
241712
241713
241714

241715
241716
241717
241718
241719
241720
241721
241722
241723
241724
241725
** mode. It edits the segments within the database described by argument
** pStruct to remove the entries for term zTerm, rowid iRowid.
*/
static void fts5FlushSecureDelete(
  Fts5Index *p,
  Fts5Structure *pStruct,
  const char *zTerm,

  i64 iRowid
){
  const int f = FTS5INDEX_QUERY_SKIPHASH;
  int nTerm = (int)strlen(zTerm);
  Fts5Iter *pIter = 0;            /* Used to find term instance */

  fts5MultiIterNew(p, pStruct, f, 0, (const u8*)zTerm, nTerm, -1, 0, &pIter);
  if( fts5MultiIterEof(p, pIter)==0 ){
    i64 iThis = fts5MultiIterRowid(pIter);
    if( iThis<iRowid ){
      fts5MultiIterNextFrom(p, pIter, iRowid);







>



<







243996
243997
243998
243999
244000
244001
244002
244003
244004
244005
244006

244007
244008
244009
244010
244011
244012
244013
** mode. It edits the segments within the database described by argument
** pStruct to remove the entries for term zTerm, rowid iRowid.
*/
static void fts5FlushSecureDelete(
  Fts5Index *p,
  Fts5Structure *pStruct,
  const char *zTerm,
  int nTerm,
  i64 iRowid
){
  const int f = FTS5INDEX_QUERY_SKIPHASH;

  Fts5Iter *pIter = 0;            /* Used to find term instance */

  fts5MultiIterNew(p, pStruct, f, 0, (const u8*)zTerm, nTerm, -1, 0, &pIter);
  if( fts5MultiIterEof(p, pIter)==0 ){
    i64 iThis = fts5MultiIterRowid(pIter);
    if( iThis<iRowid ){
      fts5MultiIterNextFrom(p, pIter, iRowid);
241785
241786
241787
241788
241789
241790
241791
241792
241793
241794
241795
241796
241797
241798
241799
241800
      while( p->rc==SQLITE_OK && 0==sqlite3Fts5HashScanEof(pHash) ){
        const char *zTerm;        /* Buffer containing term */
        int nTerm;                /* Size of zTerm in bytes */
        const u8 *pDoclist;       /* Pointer to doclist for this term */
        int nDoclist;             /* Size of doclist in bytes */

        /* Get the term and doclist for this entry. */
        sqlite3Fts5HashScanEntry(pHash, &zTerm, &pDoclist, &nDoclist);
        nTerm = (int)strlen(zTerm);
        if( bSecureDelete==0 ){
          fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm);
          if( p->rc!=SQLITE_OK ) break;
          assert( writer.bFirstRowidInPage==0 );
        }

        if( !bSecureDelete && pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){







|
<







244073
244074
244075
244076
244077
244078
244079
244080

244081
244082
244083
244084
244085
244086
244087
      while( p->rc==SQLITE_OK && 0==sqlite3Fts5HashScanEof(pHash) ){
        const char *zTerm;        /* Buffer containing term */
        int nTerm;                /* Size of zTerm in bytes */
        const u8 *pDoclist;       /* Pointer to doclist for this term */
        int nDoclist;             /* Size of doclist in bytes */

        /* Get the term and doclist for this entry. */
        sqlite3Fts5HashScanEntry(pHash, &zTerm, &nTerm, &pDoclist, &nDoclist);

        if( bSecureDelete==0 ){
          fts5WriteAppendTerm(p, &writer, nTerm, (const u8*)zTerm);
          if( p->rc!=SQLITE_OK ) break;
          assert( writer.bFirstRowidInPage==0 );
        }

        if( !bSecureDelete && pgsz>=(pBuf->n + pPgidx->n + nDoclist + 1) ){
241816
241817
241818
241819
241820
241821
241822
241823
241824
241825
241826
241827
241828
241829
241830
241831
241832
241833
241834
241835
241836
241837
241838
241839
241840

            /* If in secure delete mode, and if this entry in the poslist is
            ** in fact a delete, then edit the existing segments directly
            ** using fts5FlushSecureDelete().  */
            if( bSecureDelete ){
              if( eDetail==FTS5_DETAIL_NONE ){
                if( iOff<nDoclist && pDoclist[iOff]==0x00 ){
                  fts5FlushSecureDelete(p, pStruct, zTerm, iRowid);
                  iOff++;
                  if( iOff<nDoclist && pDoclist[iOff]==0x00 ){
                    iOff++;
                    nDoclist = 0;
                  }else{
                    continue;
                  }
                }
              }else if( (pDoclist[iOff] & 0x01) ){
                fts5FlushSecureDelete(p, pStruct, zTerm, iRowid);
                if( p->rc!=SQLITE_OK || pDoclist[iOff]==0x01 ){
                  iOff++;
                  continue;
                }
              }
            }








|









|







244103
244104
244105
244106
244107
244108
244109
244110
244111
244112
244113
244114
244115
244116
244117
244118
244119
244120
244121
244122
244123
244124
244125
244126
244127

            /* If in secure delete mode, and if this entry in the poslist is
            ** in fact a delete, then edit the existing segments directly
            ** using fts5FlushSecureDelete().  */
            if( bSecureDelete ){
              if( eDetail==FTS5_DETAIL_NONE ){
                if( iOff<nDoclist && pDoclist[iOff]==0x00 ){
                  fts5FlushSecureDelete(p, pStruct, zTerm, nTerm, iRowid);
                  iOff++;
                  if( iOff<nDoclist && pDoclist[iOff]==0x00 ){
                    iOff++;
                    nDoclist = 0;
                  }else{
                    continue;
                  }
                }
              }else if( (pDoclist[iOff] & 0x01) ){
                fts5FlushSecureDelete(p, pStruct, zTerm, nTerm, iRowid);
                if( p->rc!=SQLITE_OK || pDoclist[iOff]==0x01 ){
                  iOff++;
                  continue;
                }
              }
            }

241952
241953
241954
241955
241956
241957
241958




241959
241960
241961
241962
241963
241964
241965
241966


241967
241968
241969
241970
241971
241972
241973
}

/*
** Flush any data stored in the in-memory hash tables to the database.
*/
static void fts5IndexFlush(Fts5Index *p){
  /* Unless it is empty, flush the hash table to disk */




  if( p->nPendingData || p->nContentlessDelete ){
    assert( p->pHash );
    fts5FlushOneHash(p);
    if( p->rc==SQLITE_OK ){
      sqlite3Fts5HashClear(p->pHash);
      p->nPendingData = 0;
      p->nPendingRow = 0;
      p->nContentlessDelete = 0;


    }
  }
}

static Fts5Structure *fts5IndexOptimizeStruct(
  Fts5Index *p,
  Fts5Structure *pStruct







>
>
>
>








>
>







244239
244240
244241
244242
244243
244244
244245
244246
244247
244248
244249
244250
244251
244252
244253
244254
244255
244256
244257
244258
244259
244260
244261
244262
244263
244264
244265
244266
}

/*
** Flush any data stored in the in-memory hash tables to the database.
*/
static void fts5IndexFlush(Fts5Index *p){
  /* Unless it is empty, flush the hash table to disk */
  if( p->flushRc ){
    p->rc = p->flushRc;
    return;
  }
  if( p->nPendingData || p->nContentlessDelete ){
    assert( p->pHash );
    fts5FlushOneHash(p);
    if( p->rc==SQLITE_OK ){
      sqlite3Fts5HashClear(p->pHash);
      p->nPendingData = 0;
      p->nPendingRow = 0;
      p->nContentlessDelete = 0;
    }else if( p->nPendingData || p->nContentlessDelete ){
      p->flushRc = p->rc;
    }
  }
}

static Fts5Structure *fts5IndexOptimizeStruct(
  Fts5Index *p,
  Fts5Structure *pStruct
242446
242447
242448
242449
242450
242451
242452
242453
242454
242455
242456
242457
242458
242459
242460
242461
242462
242463
242464
242465
242466
242467
242468
242469
242470
242471
242472
242473

242474
242475
242476
242477
242478
242479
242480
242481
242482
242483
242484
242485
242486






242487
242488
242489
242490
242491
242492
242493
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);


  if( aBuf && pStruct ){
    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!=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(;







|




















>

|











>
>
>
>
>
>







244739
244740
244741
244742
244743
244744
244745
244746
244747
244748
244749
244750
244751
244752
244753
244754
244755
244756
244757
244758
244759
244760
244761
244762
244763
244764
244765
244766
244767
244768
244769
244770
244771
244772
244773
244774
244775
244776
244777
244778
244779
244780
244781
244782
244783
244784
244785
244786
244787
244788
244789
244790
244791
244792
244793
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(;
242503
242504
242505
242506
242507
242508
242509

242510
242511
242512
242513
242514
242515
242516
242517
242518
242519
242520
242521
242522
242523
242524
242525
242526
242527
242528
242529
242530
242531
242532
      }
      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 ){







>















<







244803
244804
244805
244806
244807
244808
244809
244810
244811
244812
244813
244814
244815
244816
244817
244818
244819
244820
244821
244822
244823
244824
244825

244826
244827
244828
244829
244830
244831
244832
      }
      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 ){
242557
242558
242559
242560
242561
242562
242563
242564
242565
242566
242567
242568
242569
242570
242571
      }
      for(iFree=i; iFree<i+nMerge; iFree++){
        fts5BufferFree(&aBuf[iFree]);
      }
    }
    fts5MultiIterFree(p1);

    pData = fts5IdxMalloc(p, sizeof(Fts5Data)+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);







|







244857
244858
244859
244860
244861
244862
244863
244864
244865
244866
244867
244868
244869
244870
244871
      }
      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);
242700
242701
242702
242703
242704
242705
242706

242707
242708
242709
242710
242711
242712
242713
    assert( p->pReader==0 );
    fts5StructureInvalidate(p);
    sqlite3_finalize(p->pWriter);
    sqlite3_finalize(p->pDeleter);
    sqlite3_finalize(p->pIdxWriter);
    sqlite3_finalize(p->pIdxDeleter);
    sqlite3_finalize(p->pIdxSelect);

    sqlite3_finalize(p->pDataVersion);
    sqlite3_finalize(p->pDeleteFromIdx);
    sqlite3Fts5HashFree(p->pHash);
    sqlite3_free(p->zDataTbl);
    sqlite3_free(p);
  }
  return rc;







>







245000
245001
245002
245003
245004
245005
245006
245007
245008
245009
245010
245011
245012
245013
245014
    assert( p->pReader==0 );
    fts5StructureInvalidate(p);
    sqlite3_finalize(p->pWriter);
    sqlite3_finalize(p->pDeleter);
    sqlite3_finalize(p->pIdxWriter);
    sqlite3_finalize(p->pIdxDeleter);
    sqlite3_finalize(p->pIdxSelect);
    sqlite3_finalize(p->pIdxNextSelect);
    sqlite3_finalize(p->pDataVersion);
    sqlite3_finalize(p->pDeleteFromIdx);
    sqlite3Fts5HashFree(p->pHash);
    sqlite3_free(p->zDataTbl);
    sqlite3_free(p);
  }
  return rc;
242794
242795
242796
242797
242798
242799
242800
































































































































































































































































































































































































































































242801
242802
242803
242804
242805
242806
242807
          nByte
      );
    }
  }

  return rc;
}

































































































































































































































































































































































































































































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







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







245095
245096
245097
245098
245099
245100
245101
245102
245103
245104
245105
245106
245107
245108
245109
245110
245111
245112
245113
245114
245115
245116
245117
245118
245119
245120
245121
245122
245123
245124
245125
245126
245127
245128
245129
245130
245131
245132
245133
245134
245135
245136
245137
245138
245139
245140
245141
245142
245143
245144
245145
245146
245147
245148
245149
245150
245151
245152
245153
245154
245155
245156
245157
245158
245159
245160
245161
245162
245163
245164
245165
245166
245167
245168
245169
245170
245171
245172
245173
245174
245175
245176
245177
245178
245179
245180
245181
245182
245183
245184
245185
245186
245187
245188
245189
245190
245191
245192
245193
245194
245195
245196
245197
245198
245199
245200
245201
245202
245203
245204
245205
245206
245207
245208
245209
245210
245211
245212
245213
245214
245215
245216
245217
245218
245219
245220
245221
245222
245223
245224
245225
245226
245227
245228
245229
245230
245231
245232
245233
245234
245235
245236
245237
245238
245239
245240
245241
245242
245243
245244
245245
245246
245247
245248
245249
245250
245251
245252
245253
245254
245255
245256
245257
245258
245259
245260
245261
245262
245263
245264
245265
245266
245267
245268
245269
245270
245271
245272
245273
245274
245275
245276
245277
245278
245279
245280
245281
245282
245283
245284
245285
245286
245287
245288
245289
245290
245291
245292
245293
245294
245295
245296
245297
245298
245299
245300
245301
245302
245303
245304
245305
245306
245307
245308
245309
245310
245311
245312
245313
245314
245315
245316
245317
245318
245319
245320
245321
245322
245323
245324
245325
245326
245327
245328
245329
245330
245331
245332
245333
245334
245335
245336
245337
245338
245339
245340
245341
245342
245343
245344
245345
245346
245347
245348
245349
245350
245351
245352
245353
245354
245355
245356
245357
245358
245359
245360
245361
245362
245363
245364
245365
245366
245367
245368
245369
245370
245371
245372
245373
245374
245375
245376
245377
245378
245379
245380
245381
245382
245383
245384
245385
245386
245387
245388
245389
245390
245391
245392
245393
245394
245395
245396
245397
245398
245399
245400
245401
245402
245403
245404
245405
245406
245407
245408
245409
245410
245411
245412
245413
245414
245415
245416
245417
245418
245419
245420
245421
245422
245423
245424
245425
245426
245427
245428
245429
245430
245431
245432
245433
245434
245435
245436
245437
245438
245439
245440
245441
245442
245443
245444
245445
245446
245447
245448
245449
245450
245451
245452
245453
245454
245455
245456
245457
245458
245459
245460
245461
245462
245463
245464
245465
245466
245467
245468
245469
245470
245471
245472
245473
245474
245475
245476
245477
245478
245479
245480
245481
245482
245483
245484
245485
245486
245487
245488
245489
245490
245491
245492
245493
245494
245495
245496
245497
245498
245499
245500
245501
245502
245503
245504
245505
245506
245507
245508
245509
245510
245511
245512
245513
245514
245515
245516
245517
245518
245519
245520
245521
245522
245523
245524
245525
245526
245527
245528
245529
245530
245531
245532
245533
245534
245535
245536
245537
245538
245539
245540
245541
245542
245543
245544
245545
245546
245547
245548
245549
245550
245551
245552
245553
245554
245555
245556
          nByte
      );
    }
  }

  return rc;
}

/*
** pToken points to a buffer of size nToken bytes containing a search
** term, including the index number at the start, used on a tokendata=1
** table. This function returns true if the term in buffer pBuf matches
** token pToken/nToken.
*/
static int fts5IsTokendataPrefix(
  Fts5Buffer *pBuf,
  const u8 *pToken,
  int nToken
){
  return (
      pBuf->n>=nToken
   && 0==memcmp(pBuf->p, pToken, nToken)
   && (pBuf->n==nToken || pBuf->p[nToken]==0x00)
  );
}

/*
** 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 */
  Fts5Iter *pAppend               /* Append this iterator */
){
  Fts5TokenDataIter *pRet = pIn;

  if( p->rc==SQLITE_OK ){
    if( pIn==0 || pIn->nIter==pIn->nIterAlloc ){
      int nAlloc = pIn ? pIn->nIterAlloc*2 : 16;
      int nByte = nAlloc * sizeof(Fts5Iter*) + sizeof(Fts5TokenDataIter);
      Fts5TokenDataIter *pNew = (Fts5TokenDataIter*)sqlite3_realloc(pIn, nByte);

      if( pNew==0 ){
        p->rc = SQLITE_NOMEM;
      }else{
        if( pIn==0 ) memset(pNew, 0, nByte);
        pRet = pNew;
        pNew->nIterAlloc = nAlloc;
      }
    }
  }
  if( p->rc ){
    sqlite3Fts5IterClose((Fts5IndexIter*)pAppend);
  }else{
    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){
  int ii;
  int nHit = 0;
  i64 iRowid = SMALLEST_INT64;
  int iMin = 0;

  Fts5TokenDataIter *pT = pIter->pTokenDataIter;

  pIter->base.nData = 0;
  pIter->base.pData = 0;

  for(ii=0; ii<pT->nIter; ii++){
    Fts5Iter *p = pT->apIter[ii];
    if( p->base.bEof==0 ){
      if( nHit==0 || p->base.iRowid<iRowid ){
        iRowid = p->base.iRowid;
        nHit = 1;
        pIter->base.pData = p->base.pData;
        pIter->base.nData = p->base.nData;
        iMin = ii;
      }else if( p->base.iRowid==iRowid ){
        nHit++;
      }
    }
  }

  if( nHit==0 ){
    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. */
      if( pT->aPoslistReader==0 ){
        pT->aPoslistReader = (Fts5PoslistReader*)sqlite3Fts5MallocZero(
            &pIter->pIndex->rc,
            pT->nIter * (sizeof(Fts5PoslistReader) + sizeof(int))
        );
        if( pT->aPoslistReader==0 ) return;
        pT->aPoslistToIter = (int*)&pT->aPoslistReader[pT->nIter];
      }

      /* Populate an iterator for each poslist that will be merged */
      for(ii=0; ii<pT->nIter; ii++){
        Fts5Iter *p = pT->apIter[ii];
        if( iRowid==p->base.iRowid ){
          pT->aPoslistToIter[nReader] = ii;
          sqlite3Fts5PoslistReaderInit(
              p->base.pData, p->base.nData, &pT->aPoslistReader[nReader++]
          );
          nByte += p->base.nData;
        }
      }

      /* Ensure the output buffer is large enough */
      if( fts5BufferGrow(&pIter->pIndex->rc, &pIter->poslist, nByte+nHit*10) ){
        return;
      }

      /* Ensure the token-mapping is large enough */
      if( eDetail==FTS5_DETAIL_FULL && pT->nMapAlloc<(pT->nMap + nByte) ){
        int nNew = (pT->nMapAlloc + nByte) * 2;
        Fts5TokenDataMap *aNew = (Fts5TokenDataMap*)sqlite3_realloc(
            pT->aMap, nNew*sizeof(Fts5TokenDataMap)
        );
        if( aNew==0 ){
          pIter->pIndex->rc = SQLITE_NOMEM;
          return;
        }
        pT->aMap = aNew;
        pT->nMapAlloc = nNew;
      }

      pIter->poslist.n = 0;

      while( 1 ){
        i64 iMinPos = LARGEST_INT64;

        /* Find smallest position */
        iMin = 0;
        for(ii=0; ii<nReader; ii++){
          Fts5PoslistReader *pReader = &pT->aPoslistReader[ii];
          if( pReader->bEof==0 ){
            if( pReader->iPos<iMinPos ){
              iMinPos = pReader->iPos;
              iMin = ii;
            }
          }
        }

        /* If all readers were at EOF, break out of the loop. */
        if( iMinPos==LARGEST_INT64 ) break;

        sqlite3Fts5PoslistSafeAppend(&pIter->poslist, &iPrev, iMinPos);
        sqlite3Fts5PoslistReaderNext(&pT->aPoslistReader[iMin]);

        if( eDetail==FTS5_DETAIL_FULL ){
          pT->aMap[pT->nMap].iPos = iMinPos;
          pT->aMap[pT->nMap].iIter = pT->aPoslistToIter[iMin];
          pT->aMap[pT->nMap].iRowid = iRowid;
          pT->nMap++;
        }
      }

      pIter->base.pData = pIter->poslist.p;
      pIter->base.nData = pIter->poslist.n;
    }
  }
}

/*
** The iterator passed as the only argument must be a tokendata=1 iterator
** (pIter->pTokenDataIter!=0). This function advances the iterator. If
** argument bFrom is false, then the iterator is advanced to the next
** entry. Or, if bFrom is true, it is advanced to the first entry with
** a rowid of iFrom or greater.
*/
static void fts5TokendataIterNext(Fts5Iter *pIter, int bFrom, i64 iFrom){
  int ii;
  Fts5TokenDataIter *pT = pIter->pTokenDataIter;

  for(ii=0; ii<pT->nIter; ii++){
    Fts5Iter *p = pT->apIter[ii];
    if( p->base.bEof==0
     && (p->base.iRowid==pIter->base.iRowid || (bFrom && p->base.iRowid<iFrom))
    ){
      fts5MultiIterNext(p->pIndex, p, bFrom, iFrom);
      while( bFrom && p->base.bEof==0
          && p->base.iRowid<iFrom
          && p->pIndex->rc==SQLITE_OK
      ){
        fts5MultiIterNext(p->pIndex, p, 0, 0);
      }
    }
  }

  fts5IterSetOutputsTokendata(pIter);
}

/*
** If the segment-iterator passed as the first argument is at EOF, then
** set pIter->term to a copy of buffer pTerm.
*/
static void fts5TokendataSetTermIfEof(Fts5Iter *pIter, Fts5Buffer *pTerm){
  if( pIter && pIter->aSeg[0].pLeaf==0 ){
    fts5BufferSet(&pIter->pIndex->rc, &pIter->aSeg[0].term, pTerm->n, pTerm->p);
  }
}

/*
** This function sets up an iterator to use for a non-prefix query on a
** tokendata=1 table.
*/
static Fts5Iter *fts5SetupTokendataIter(
  Fts5Index *p,                   /* FTS index to query */
  const u8 *pToken,               /* Buffer containing query term */
  int nToken,                     /* Size of buffer pToken in bytes */
  Fts5Colset *pColset             /* Colset to filter on */
){
  Fts5Iter *pRet = 0;
  Fts5TokenDataIter *pSet = 0;
  Fts5Structure *pStruct = 0;
  const int flags = FTS5INDEX_QUERY_SCANONETERM | FTS5INDEX_QUERY_SCAN;

  Fts5Buffer bSeek = {0, 0, 0};
  Fts5Buffer *pSmall = 0;

  fts5IndexFlush(p);
  pStruct = fts5StructureRead(p);

  while( p->rc==SQLITE_OK ){
    Fts5Iter *pPrev = pSet ? pSet->apIter[pSet->nIter-1] : 0;
    Fts5Iter *pNew = 0;
    Fts5SegIter *pNewIter = 0;
    Fts5SegIter *pPrevIter = 0;

    int iLvl, iSeg, ii;

    pNew = fts5MultiIterAlloc(p, pStruct->nSegment);
    if( pSmall ){
      fts5BufferSet(&p->rc, &bSeek, pSmall->n, pSmall->p);
      fts5BufferAppendBlob(&p->rc, &bSeek, 1, (const u8*)"\0");
    }else{
      fts5BufferSet(&p->rc, &bSeek, nToken, pToken);
    }
    if( p->rc ){
      sqlite3Fts5IterClose((Fts5IndexIter*)pNew);
      break;
    }

    pNewIter = &pNew->aSeg[0];
    pPrevIter = (pPrev ? &pPrev->aSeg[0] : 0);
    for(iLvl=0; iLvl<pStruct->nLevel; iLvl++){
      for(iSeg=pStruct->aLevel[iLvl].nSeg-1; iSeg>=0; iSeg--){
        Fts5StructureSegment *pSeg = &pStruct->aLevel[iLvl].aSeg[iSeg];
        int bDone = 0;

        if( pPrevIter ){
          if( fts5BufferCompare(pSmall, &pPrevIter->term) ){
            memcpy(pNewIter, pPrevIter, sizeof(Fts5SegIter));
            memset(pPrevIter, 0, sizeof(Fts5SegIter));
            bDone = 1;
          }else if( pPrevIter->iEndofDoclist>pPrevIter->pLeaf->szLeaf ){
            fts5SegIterNextInit(p,(const char*)bSeek.p,bSeek.n-1,pSeg,pNewIter);
            bDone = 1;
          }
        }

        if( bDone==0 ){
          fts5SegIterSeekInit(p, bSeek.p, bSeek.n, flags, pSeg, pNewIter);
        }

        if( pPrevIter ){
          if( pPrevIter->pTombArray ){
            pNewIter->pTombArray = pPrevIter->pTombArray;
            pNewIter->pTombArray->nRef++;
          }
        }else{
          fts5SegIterAllocTombstone(p, pNewIter);
        }

        pNewIter++;
        if( pPrevIter ) pPrevIter++;
        if( p->rc ) break;
      }
    }
    fts5TokendataSetTermIfEof(pPrev, pSmall);

    pNew->bSkipEmpty = 1;
    pNew->pColset = pColset;
    fts5IterSetOutputCb(&p->rc, pNew);

    /* Loop through all segments in the new iterator. Find the smallest
    ** term that any segment-iterator points to. Iterator pNew will be
    ** used for this term. Also, set any iterator that points to a term that
    ** does not match pToken/nToken to point to EOF */
    pSmall = 0;
    for(ii=0; ii<pNew->nSeg; ii++){
      Fts5SegIter *pII = &pNew->aSeg[ii];
      if( 0==fts5IsTokendataPrefix(&pII->term, pToken, nToken) ){
        fts5SegIterSetEOF(pII);
      }
      if( pII->pLeaf && (!pSmall || fts5BufferCompare(pSmall, &pII->term)>0) ){
        pSmall = &pII->term;
      }
    }

    /* If pSmall is still NULL at this point, then the new iterator does
    ** not point to any terms that match the query. So delete it and break
    ** out of the loop - all required iterators have been collected.  */
    if( pSmall==0 ){
      sqlite3Fts5IterClose((Fts5IndexIter*)pNew);
      break;
    }

    /* Append this iterator to the set and continue. */
    pSet = fts5AppendTokendataIter(p, pSet, pNew);
  }

  if( p->rc==SQLITE_OK && pSet ){
    int ii;
    for(ii=0; ii<pSet->nIter; ii++){
      Fts5Iter *pIter = pSet->apIter[ii];
      int iSeg;
      for(iSeg=0; iSeg<pIter->nSeg; iSeg++){
        pIter->aSeg[iSeg].flags |= FTS5_SEGITER_ONETERM;
      }
      fts5MultiIterFinishSetup(p, pIter);
    }
  }

  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 */
242816
242817
242818
242819
242820
242821
242822

242823




242824
242825
242826
242827
242828
242829
242830

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

    if( nToken>0 ) memcpy(&buf.p[1], pToken, nToken);





    /* 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
    ** satisfied by scanning multiple terms in the main index.
    **
    ** If the QUERY_TEST_NOIDX flag was specified, then this must be a







>

>
>
>
>







245565
245566
245567
245568
245569
245570
245571
245572
245573
245574
245575
245576
245577
245578
245579
245580
245581
245582
245583
245584

  /* 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
    ** satisfied by scanning multiple terms in the main index.
    **
    ** If the QUERY_TEST_NOIDX flag was specified, then this must be a
242843
242844
242845
242846
242847
242848
242849



242850
242851
242852
242853
242854
242855
242856
242857
      for(iIdx=1; iIdx<=pConfig->nPrefix; iIdx++){
        int nIdxChar = pConfig->aPrefix[iIdx-1];
        if( nIdxChar==nChar ) break;
        if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx;
      }
    }




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







>
>
>
|







245597
245598
245599
245600
245601
245602
245603
245604
245605
245606
245607
245608
245609
245610
245611
245612
245613
245614
      for(iIdx=1; iIdx<=pConfig->nPrefix; iIdx++){
        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
        );
242890
242891
242892
242893
242894
242895
242896



242897

242898
242899
242900
242901
242902
242903
242904
*/
/*
** Move to the next matching rowid.
*/
static int sqlite3Fts5IterNext(Fts5IndexIter *pIndexIter){
  Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
  assert( pIter->pIndex->rc==SQLITE_OK );



  fts5MultiIterNext(pIter->pIndex, pIter, 0, 0);

  return fts5IndexReturn(pIter->pIndex);
}

/*
** Move to the next matching term/rowid. Used by the fts5vocab module.
*/
static int sqlite3Fts5IterNextScan(Fts5IndexIter *pIndexIter){







>
>
>
|
>







245647
245648
245649
245650
245651
245652
245653
245654
245655
245656
245657
245658
245659
245660
245661
245662
245663
245664
245665
*/
/*
** 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);
}

/*
** Move to the next matching term/rowid. Used by the fts5vocab module.
*/
static int sqlite3Fts5IterNextScan(Fts5IndexIter *pIndexIter){
242923
242924
242925
242926
242927
242928
242929



242930

242931
242932
242933
242934
242935
242936
242937
242938
242939
242940
242941
242942
242943
242944
242945





























































































242946
242947
242948
242949
242950
242951

242952
242953
242954
242955
242956
242957
242958
/*
** 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;



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

/*





























































































** Close an iterator opened by an earlier call to sqlite3Fts5IndexQuery().
*/
static void sqlite3Fts5IterClose(Fts5IndexIter *pIndexIter){
  if( pIndexIter ){
    Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
    Fts5Index *pIndex = pIter->pIndex;

    fts5MultiIterFree(pIter);
    sqlite3Fts5IndexCloseReader(pIndex);
  }
}

/*
** Read and decode the "averages" record from the database.







>
>
>
|
>















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






>







245684
245685
245686
245687
245688
245689
245690
245691
245692
245693
245694
245695
245696
245697
245698
245699
245700
245701
245702
245703
245704
245705
245706
245707
245708
245709
245710
245711
245712
245713
245714
245715
245716
245717
245718
245719
245720
245721
245722
245723
245724
245725
245726
245727
245728
245729
245730
245731
245732
245733
245734
245735
245736
245737
245738
245739
245740
245741
245742
245743
245744
245745
245746
245747
245748
245749
245750
245751
245752
245753
245754
245755
245756
245757
245758
245759
245760
245761
245762
245763
245764
245765
245766
245767
245768
245769
245770
245771
245772
245773
245774
245775
245776
245777
245778
245779
245780
245781
245782
245783
245784
245785
245786
245787
245788
245789
245790
245791
245792
245793
245794
245795
245796
245797
245798
245799
245800
245801
245802
245803
245804
245805
245806
245807
245808
245809
245810
245811
245812
245813
245814
245815
245816
245817
/*
** 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 ){
      i2 = iTest;
    }else{
      if( aMap[iTest].iPos<iPos ){
        if( aMap[iTest].iPos<0 ){
          break;
        }
        i1 = iTest+1;
      }else if( aMap[iTest].iPos>iPos ){
        i2 = iTest;
      }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().
*/
static void sqlite3Fts5IterClose(Fts5IndexIter *pIndexIter){
  if( pIndexIter ){
    Fts5Iter *pIter = (Fts5Iter*)pIndexIter;
    Fts5Index *pIndex = pIter->pIndex;
    fts5TokendataIterDelete(pIter->pTokenDataIter);
    fts5MultiIterFree(pIter);
    sqlite3Fts5IndexCloseReader(pIndex);
  }
}

/*
** Read and decode the "averages" record from the database.
243452
243453
243454
243455
243456
243457
243458
243459


243460
243461
243462
243463
243464
243465
243466
  int n,                          /* Size of index key in bytes */
  int flags,                      /* Flags for Fts5IndexQuery */
  u64 *pCksum                     /* IN/OUT: Checksum value */
){
  int eDetail = p->pConfig->eDetail;
  u64 cksum = *pCksum;
  Fts5IndexIter *pIter = 0;
  int rc = sqlite3Fts5IndexQuery(p, z, n, flags, 0, &pIter);



  while( rc==SQLITE_OK && ALWAYS(pIter!=0) && 0==sqlite3Fts5IterEof(pIter) ){
    i64 rowid = pIter->iRowid;

    if( eDetail==FTS5_DETAIL_NONE ){
      cksum ^= sqlite3Fts5IndexEntryCksum(rowid, 0, 0, iIdx, z, n);
    }else{







|
>
>







246311
246312
246313
246314
246315
246316
246317
246318
246319
246320
246321
246322
246323
246324
246325
246326
246327
  int n,                          /* Size of index key in bytes */
  int flags,                      /* Flags for Fts5IndexQuery */
  u64 *pCksum                     /* IN/OUT: Checksum value */
){
  int eDetail = p->pConfig->eDetail;
  u64 cksum = *pCksum;
  Fts5IndexIter *pIter = 0;
  int rc = sqlite3Fts5IndexQuery(
      p, z, n, (flags | FTS5INDEX_QUERY_NOTOKENDATA), 0, &pIter
  );

  while( rc==SQLITE_OK && ALWAYS(pIter!=0) && 0==sqlite3Fts5IterEof(pIter) ){
    i64 rowid = pIter->iRowid;

    if( eDetail==FTS5_DETAIL_NONE ){
      cksum ^= sqlite3Fts5IndexEntryCksum(rowid, 0, 0, iIdx, z, n);
    }else{
243619
243620
243621
243622
243623
243624
243625
243626
243627
243628
243629
243630
243631
243632
243633
243634
243635
243636
243637
243638
243639
243640
243641
243642
      if( i>=iNoRowid && 0!=fts5LeafFirstRowidOff(pLeaf) ) p->rc = FTS5_CORRUPT;
    }
    fts5DataRelease(pLeaf);
  }
}

static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
  int iTermOff = 0;
  int ii;

  Fts5Buffer buf1 = {0,0,0};
  Fts5Buffer buf2 = {0,0,0};

  ii = pLeaf->szLeaf;
  while( ii<pLeaf->nn && p->rc==SQLITE_OK ){
    int res;
    int iOff;
    int nIncr;

    ii += fts5GetVarint32(&pLeaf->p[ii], nIncr);
    iTermOff += nIncr;
    iOff = iTermOff;

    if( iOff>=pLeaf->szLeaf ){







|








|







246480
246481
246482
246483
246484
246485
246486
246487
246488
246489
246490
246491
246492
246493
246494
246495
246496
246497
246498
246499
246500
246501
246502
246503
      if( i>=iNoRowid && 0!=fts5LeafFirstRowidOff(pLeaf) ) p->rc = FTS5_CORRUPT;
    }
    fts5DataRelease(pLeaf);
  }
}

static void fts5IntegrityCheckPgidx(Fts5Index *p, Fts5Data *pLeaf){
  i64 iTermOff = 0;
  int ii;

  Fts5Buffer buf1 = {0,0,0};
  Fts5Buffer buf2 = {0,0,0};

  ii = pLeaf->szLeaf;
  while( ii<pLeaf->nn && p->rc==SQLITE_OK ){
    int res;
    i64 iOff;
    int nIncr;

    ii += fts5GetVarint32(&pLeaf->p[ii], nIncr);
    iTermOff += nIncr;
    iOff = iTermOff;

    if( iOff>=pLeaf->szLeaf ){
244149
244150
244151
244152
244153
244154
244155


















244156
244157
244158
244159
244160
244161
244162
      }
    }

    sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " %lld%s", iRowid, zApp);
  }
}
#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */



















#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
/*
** The implementation of user-defined scalar function fts5_decode().
*/
static void fts5DecodeFunction(
  sqlite3_context *pCtx,          /* Function call context */







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







247010
247011
247012
247013
247014
247015
247016
247017
247018
247019
247020
247021
247022
247023
247024
247025
247026
247027
247028
247029
247030
247031
247032
247033
247034
247035
247036
247037
247038
247039
247040
247041
      }
    }

    sqlite3Fts5BufferAppendPrintf(pRc, pBuf, " %lld%s", iRowid, zApp);
  }
}
#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */

#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
static void fts5BufferAppendTerm(int *pRc, Fts5Buffer *pBuf, Fts5Buffer *pTerm){
  int ii;
  fts5BufferGrow(pRc, pBuf, pTerm->n*2 + 1);
  if( *pRc==SQLITE_OK ){
    for(ii=0; ii<pTerm->n; ii++){
      if( pTerm->p[ii]==0x00 ){
        pBuf->p[pBuf->n++] = '\\';
        pBuf->p[pBuf->n++] = '0';
      }else{
        pBuf->p[pBuf->n++] = pTerm->p[ii];
      }
    }
    pBuf->p[pBuf->n] = 0x00;
  }
}
#endif /* SQLITE_TEST || SQLITE_FTS5_DEBUG */

#if defined(SQLITE_TEST) || defined(SQLITE_FTS5_DEBUG)
/*
** The implementation of user-defined scalar function fts5_decode().
*/
static void fts5DecodeFunction(
  sqlite3_context *pCtx,          /* Function call context */
244257
244258
244259
244260
244261
244262
244263
244264
244265
244266
244267
244268
244269
244270
244271
244272
244273
    while( iOff<szLeaf && rc==SQLITE_OK ){
      int nAppend;

      /* Read the term data for the next term*/
      iOff += fts5GetVarint32(&a[iOff], nAppend);
      term.n = nKeep;
      fts5BufferAppendBlob(&rc, &term, nAppend, &a[iOff]);
      sqlite3Fts5BufferAppendPrintf(
          &rc, &s, " term=%.*s", term.n, (const char*)term.p
      );
      iOff += nAppend;

      /* Figure out where the doclist for this term ends */
      if( iPgidxOff<n ){
        int nIncr;
        iPgidxOff += fts5GetVarint32(&a[iPgidxOff], nIncr);
        iTermOff += nIncr;







|
|
<







247136
247137
247138
247139
247140
247141
247142
247143
247144

247145
247146
247147
247148
247149
247150
247151
    while( iOff<szLeaf && rc==SQLITE_OK ){
      int nAppend;

      /* Read the term data for the next term*/
      iOff += fts5GetVarint32(&a[iOff], nAppend);
      term.n = nKeep;
      fts5BufferAppendBlob(&rc, &term, nAppend, &a[iOff]);
      sqlite3Fts5BufferAppendPrintf(&rc, &s, " term=");
      fts5BufferAppendTerm(&rc, &s, &term);

      iOff += nAppend;

      /* Figure out where the doclist for this term ends */
      if( iPgidxOff<n ){
        int nIncr;
        iPgidxOff += fts5GetVarint32(&a[iPgidxOff], nIncr);
        iTermOff += nIncr;
244367
244368
244369
244370
244371
244372
244373
244374
244375
244376
244377
244378
244379
244380
244381
244382
244383
      if( iOff+nByte>n ){
        rc = FTS5_CORRUPT;
        break;
      }
      fts5BufferAppendBlob(&rc, &term, nByte, &a[iOff]);
      iOff += nByte;

      sqlite3Fts5BufferAppendPrintf(
          &rc, &s, " term=%.*s", term.n, (const char*)term.p
      );
      iOff += fts5DecodeDoclist(&rc, &s, &a[iOff], iEnd-iOff);
    }

    fts5BufferFree(&term);
  }

 decode_out:







|
|
<







247245
247246
247247
247248
247249
247250
247251
247252
247253

247254
247255
247256
247257
247258
247259
247260
      if( iOff+nByte>n ){
        rc = FTS5_CORRUPT;
        break;
      }
      fts5BufferAppendBlob(&rc, &term, nByte, &a[iOff]);
      iOff += nByte;

      sqlite3Fts5BufferAppendPrintf(&rc, &s, " term=");
      fts5BufferAppendTerm(&rc, &s, &term);

      iOff += fts5DecodeDoclist(&rc, &s, &a[iOff], iEnd-iOff);
    }

    fts5BufferFree(&term);
  }

 decode_out:
244844
244845
244846
244847
244848
244849
244850
244851
244852
244853
244854
244855
244856
244857
244858

struct Fts5FullTable {
  Fts5Table p;                    /* Public class members from fts5Int.h */
  Fts5Storage *pStorage;          /* Document store */
  Fts5Global *pGlobal;            /* Global (connection wide) data */
  Fts5Cursor *pSortCsr;           /* Sort data from this cursor */
  int iSavepoint;                 /* Successful xSavepoint()+1 */
  int bInSavepoint;
#ifdef SQLITE_DEBUG
  struct Fts5TransactionState ts;
#endif
};

struct Fts5MatchPhrase {
  Fts5Buffer *pPoslist;           /* Pointer to current poslist */







|







247721
247722
247723
247724
247725
247726
247727
247728
247729
247730
247731
247732
247733
247734
247735

struct Fts5FullTable {
  Fts5Table p;                    /* Public class members from fts5Int.h */
  Fts5Storage *pStorage;          /* Document store */
  Fts5Global *pGlobal;            /* Global (connection wide) data */
  Fts5Cursor *pSortCsr;           /* Sort data from this cursor */
  int iSavepoint;                 /* Successful xSavepoint()+1 */

#ifdef SQLITE_DEBUG
  struct Fts5TransactionState ts;
#endif
};

struct Fts5MatchPhrase {
  Fts5Buffer *pPoslist;           /* Pointer to current poslist */
245382
245383
245384
245385
245386
245387
245388
245389



245390
245391
245392
245393
245394
245395
245396
245397
245398
245399
245400
245401
          bSeenGt = 1;
        }
      }
    }
  }
  idxStr[iIdxStr] = '\0';

  /* Set idxFlags flags for the ORDER BY clause */



  if( pInfo->nOrderBy==1 ){
    int iSort = pInfo->aOrderBy[0].iColumn;
    if( iSort==(pConfig->nCol+1) && bSeenMatch ){
      idxFlags |= FTS5_BI_ORDER_RANK;
    }else if( iSort==-1 ){
      idxFlags |= FTS5_BI_ORDER_ROWID;
    }
    if( BitFlagTest(idxFlags, FTS5_BI_ORDER_RANK|FTS5_BI_ORDER_ROWID) ){
      pInfo->orderByConsumed = 1;
      if( pInfo->aOrderBy[0].desc ){
        idxFlags |= FTS5_BI_ORDER_DESC;
      }







|
>
>
>




|







248259
248260
248261
248262
248263
248264
248265
248266
248267
248268
248269
248270
248271
248272
248273
248274
248275
248276
248277
248278
248279
248280
248281
          bSeenGt = 1;
        }
      }
    }
  }
  idxStr[iIdxStr] = '\0';

  /* Set idxFlags flags for the ORDER BY clause
  **
  ** Note that tokendata=1 tables cannot currently handle "ORDER BY rowid DESC".
  */
  if( pInfo->nOrderBy==1 ){
    int iSort = pInfo->aOrderBy[0].iColumn;
    if( iSort==(pConfig->nCol+1) && bSeenMatch ){
      idxFlags |= FTS5_BI_ORDER_RANK;
    }else if( iSort==-1 && (!pInfo->aOrderBy[0].desc || !pConfig->bTokendata) ){
      idxFlags |= FTS5_BI_ORDER_ROWID;
    }
    if( BitFlagTest(idxFlags, FTS5_BI_ORDER_RANK|FTS5_BI_ORDER_ROWID) ){
      pInfo->orderByConsumed = 1;
      if( pInfo->aOrderBy[0].desc ){
        idxFlags |= FTS5_BI_ORDER_DESC;
      }
245638
245639
245640
245641
245642
245643
245644










245645
245646
245647
245648
245649
245650
245651
  Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
  int rc;

  assert( (pCsr->ePlan<3)==
          (pCsr->ePlan==FTS5_PLAN_MATCH || pCsr->ePlan==FTS5_PLAN_SOURCE)
  );
  assert( !CsrFlagTest(pCsr, FTS5CSR_EOF) );











  if( pCsr->ePlan<3 ){
    int bSkip = 0;
    if( (rc = fts5CursorReseek(pCsr, &bSkip)) || bSkip ) return rc;
    rc = sqlite3Fts5ExprNext(pCsr->pExpr, pCsr->iLastRowid);
    CsrFlagSet(pCsr, sqlite3Fts5ExprEof(pCsr->pExpr));
    fts5CsrNewrow(pCsr);







>
>
>
>
>
>
>
>
>
>







248518
248519
248520
248521
248522
248523
248524
248525
248526
248527
248528
248529
248530
248531
248532
248533
248534
248535
248536
248537
248538
248539
248540
248541
  Fts5Cursor *pCsr = (Fts5Cursor*)pCursor;
  int rc;

  assert( (pCsr->ePlan<3)==
          (pCsr->ePlan==FTS5_PLAN_MATCH || pCsr->ePlan==FTS5_PLAN_SOURCE)
  );
  assert( !CsrFlagTest(pCsr, FTS5CSR_EOF) );

  /* If this cursor uses FTS5_PLAN_MATCH and this is a tokendata=1 table,
  ** clear any token mappings accumulated at the fts5_index.c level. In
  ** other cases, specifically FTS5_PLAN_SOURCE and FTS5_PLAN_SORTED_MATCH,
  ** we need to retain the mappings for the entire query.  */
  if( pCsr->ePlan==FTS5_PLAN_MATCH
   && ((Fts5Table*)pCursor->pVtab)->pConfig->bTokendata
  ){
    sqlite3Fts5ExprClearTokens(pCsr->pExpr);
  }

  if( pCsr->ePlan<3 ){
    int bSkip = 0;
    if( (rc = fts5CursorReseek(pCsr, &bSkip)) || bSkip ) return rc;
    rc = sqlite3Fts5ExprNext(pCsr->pExpr, pCsr->iLastRowid);
    CsrFlagSet(pCsr, sqlite3Fts5ExprEof(pCsr->pExpr));
    fts5CsrNewrow(pCsr);
246299
246300
246301
246302
246303
246304
246305


246306

246307
246308
246309
246310
246311
246312
246313
#ifdef SQLITE_DEBUG
  }else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){
    pConfig->bPrefixIndex = sqlite3_value_int(pVal);
#endif
  }else if( 0==sqlite3_stricmp("flush", zCmd) ){
    rc = sqlite3Fts5FlushToDisk(&pTab->p);
  }else{


    rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);

    if( rc==SQLITE_OK ){
      rc = sqlite3Fts5ConfigSetValue(pTab->p.pConfig, zCmd, pVal, &bError);
    }
    if( rc==SQLITE_OK ){
      if( bError ){
        rc = SQLITE_ERROR;
      }else{







>
>
|
>







249189
249190
249191
249192
249193
249194
249195
249196
249197
249198
249199
249200
249201
249202
249203
249204
249205
249206
#ifdef SQLITE_DEBUG
  }else if( 0==sqlite3_stricmp("prefix-index", zCmd) ){
    pConfig->bPrefixIndex = sqlite3_value_int(pVal);
#endif
  }else if( 0==sqlite3_stricmp("flush", zCmd) ){
    rc = sqlite3Fts5FlushToDisk(&pTab->p);
  }else{
    rc = sqlite3Fts5FlushToDisk(&pTab->p);
    if( rc==SQLITE_OK ){
      rc = sqlite3Fts5IndexLoadConfig(pTab->p.pIndex);
    }
    if( rc==SQLITE_OK ){
      rc = sqlite3Fts5ConfigSetValue(pTab->p.pConfig, zCmd, pVal, &bError);
    }
    if( rc==SQLITE_OK ){
      if( bError ){
        rc = SQLITE_ERROR;
      }else{
246624
246625
246626
246627
246628
246629
246630



246631
246632
246633
246634
246635
246636
246637
246638
  Fts5Context *pCtx,
  int iCol,
  const char **pz,
  int *pn
){
  int rc = SQLITE_OK;
  Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;



  if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab))
   || pCsr->ePlan==FTS5_PLAN_SPECIAL
  ){
    *pz = 0;
    *pn = 0;
  }else{
    rc = fts5SeekCursor(pCsr, 0);
    if( rc==SQLITE_OK ){







>
>
>
|







249517
249518
249519
249520
249521
249522
249523
249524
249525
249526
249527
249528
249529
249530
249531
249532
249533
249534
  Fts5Context *pCtx,
  int iCol,
  const char **pz,
  int *pn
){
  int rc = SQLITE_OK;
  Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
  Fts5Table *pTab = (Fts5Table*)(pCsr->base.pVtab);
  if( iCol<0 || iCol>=pTab->pConfig->nCol ){
    rc = SQLITE_RANGE;
  }else if( fts5IsContentless((Fts5FullTable*)(pCsr->base.pVtab))
   || pCsr->ePlan==FTS5_PLAN_SPECIAL
  ){
    *pz = 0;
    *pn = 0;
  }else{
    rc = fts5SeekCursor(pCsr, 0);
    if( rc==SQLITE_OK ){
246649
246650
246651
246652
246653
246654
246655


246656
246657
246658
246659
246660
246661
246662
246663
246664
  const u8 **pa,
  int *pn
){
  Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig;
  int rc = SQLITE_OK;
  int bLive = (pCsr->pSorter==0);



  if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){

    if( pConfig->eDetail!=FTS5_DETAIL_FULL ){
      Fts5PoslistPopulator *aPopulator;
      int i;
      aPopulator = sqlite3Fts5ExprClearPoslists(pCsr->pExpr, bLive);
      if( aPopulator==0 ) rc = SQLITE_NOMEM;
      for(i=0; i<pConfig->nCol && rc==SQLITE_OK; i++){
        int n; const char *z;







>
>
|
<







249545
249546
249547
249548
249549
249550
249551
249552
249553
249554

249555
249556
249557
249558
249559
249560
249561
  const u8 **pa,
  int *pn
){
  Fts5Config *pConfig = ((Fts5Table*)(pCsr->base.pVtab))->pConfig;
  int rc = SQLITE_OK;
  int bLive = (pCsr->pSorter==0);

  if( iPhrase<0 || iPhrase>=sqlite3Fts5ExprPhraseCount(pCsr->pExpr) ){
    rc = SQLITE_RANGE;
  }else if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_POSLIST) ){

    if( pConfig->eDetail!=FTS5_DETAIL_FULL ){
      Fts5PoslistPopulator *aPopulator;
      int i;
      aPopulator = sqlite3Fts5ExprClearPoslists(pCsr->pExpr, bLive);
      if( aPopulator==0 ) rc = SQLITE_NOMEM;
      for(i=0; i<pConfig->nCol && rc==SQLITE_OK; i++){
        int n; const char *z;
246674
246675
246676
246677
246678
246679
246680

246681
246682
246683
246684
246685
246686
246687
246688





246689
246690
246691
246692
246693
246694
246695
      if( pCsr->pSorter ){
        sqlite3Fts5ExprCheckPoslists(pCsr->pExpr, pCsr->pSorter->iRowid);
      }
    }
    CsrFlagClear(pCsr, FTS5CSR_REQUIRE_POSLIST);
  }


  if( pCsr->pSorter && pConfig->eDetail==FTS5_DETAIL_FULL ){
    Fts5Sorter *pSorter = pCsr->pSorter;
    int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]);
    *pn = pSorter->aIdx[iPhrase] - i1;
    *pa = &pSorter->aPoslist[i1];
  }else{
    *pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa);
  }






  return rc;
}

/*
** Ensure that the Fts5Cursor.nInstCount and aInst[] variables are populated
** correctly for the current view. Return SQLITE_OK if successful, or an







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







249571
249572
249573
249574
249575
249576
249577
249578
249579
249580
249581
249582
249583
249584
249585
249586
249587
249588
249589
249590
249591
249592
249593
249594
249595
249596
249597
249598
      if( pCsr->pSorter ){
        sqlite3Fts5ExprCheckPoslists(pCsr->pExpr, pCsr->pSorter->iRowid);
      }
    }
    CsrFlagClear(pCsr, FTS5CSR_REQUIRE_POSLIST);
  }

  if( rc==SQLITE_OK ){
    if( pCsr->pSorter && pConfig->eDetail==FTS5_DETAIL_FULL ){
      Fts5Sorter *pSorter = pCsr->pSorter;
      int i1 = (iPhrase==0 ? 0 : pSorter->aIdx[iPhrase-1]);
      *pn = pSorter->aIdx[iPhrase] - i1;
      *pa = &pSorter->aPoslist[i1];
    }else{
      *pn = sqlite3Fts5ExprPoslist(pCsr->pExpr, iPhrase, pa);
    }
  }else{
    *pa = 0;
    *pn = 0;
  }


  return rc;
}

/*
** Ensure that the Fts5Cursor.nInstCount and aInst[] variables are populated
** correctly for the current view. Return SQLITE_OK if successful, or an
246789
246790
246791
246792
246793
246794
246795
246796
246797
246798
246799
246800
246801
246802
246803
246804
246805
246806
246807
246808
  Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
  int rc = SQLITE_OK;
  if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0
   || SQLITE_OK==(rc = fts5CacheInstArray(pCsr))
  ){
    if( iIdx<0 || iIdx>=pCsr->nInstCount ){
      rc = SQLITE_RANGE;
#if 0
    }else if( fts5IsOffsetless((Fts5Table*)pCsr->base.pVtab) ){
      *piPhrase = pCsr->aInst[iIdx*3];
      *piCol = pCsr->aInst[iIdx*3 + 2];
      *piOff = -1;
#endif
    }else{
      *piPhrase = pCsr->aInst[iIdx*3];
      *piCol = pCsr->aInst[iIdx*3 + 1];
      *piOff = pCsr->aInst[iIdx*3 + 2];
    }
  }
  return rc;







<
<
<
<
<
<







249692
249693
249694
249695
249696
249697
249698






249699
249700
249701
249702
249703
249704
249705
  Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
  int rc = SQLITE_OK;
  if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0
   || SQLITE_OK==(rc = fts5CacheInstArray(pCsr))
  ){
    if( iIdx<0 || iIdx>=pCsr->nInstCount ){
      rc = SQLITE_RANGE;






    }else{
      *piPhrase = pCsr->aInst[iIdx*3];
      *piCol = pCsr->aInst[iIdx*3 + 1];
      *piOff = pCsr->aInst[iIdx*3 + 2];
    }
  }
  return rc;
247049
247050
247051
247052
247053
247054
247055











































247056
247057
247058
247059
247060
247061
247062
247063
247064
247065
247066
247067
247068
247069
247070
247071
247072
247073
247074
247075
247076
247077
247078
247079
247080
247081


247082
247083
247084
247085
247086
247087
247088
      }
    }
  }

  return rc;
}













































static int fts5ApiQueryPhrase(Fts5Context*, int, void*,
    int(*)(const Fts5ExtensionApi*, Fts5Context*, void*)
);

static const Fts5ExtensionApi sFts5Api = {
  2,                            /* iVersion */
  fts5ApiUserData,
  fts5ApiColumnCount,
  fts5ApiRowCount,
  fts5ApiColumnTotalSize,
  fts5ApiTokenize,
  fts5ApiPhraseCount,
  fts5ApiPhraseSize,
  fts5ApiInstCount,
  fts5ApiInst,
  fts5ApiRowid,
  fts5ApiColumnText,
  fts5ApiColumnSize,
  fts5ApiQueryPhrase,
  fts5ApiSetAuxdata,
  fts5ApiGetAuxdata,
  fts5ApiPhraseFirst,
  fts5ApiPhraseNext,
  fts5ApiPhraseFirstColumn,
  fts5ApiPhraseNextColumn,


};

/*
** Implementation of API function xQueryPhrase().
*/
static int fts5ApiQueryPhrase(
  Fts5Context *pCtx,







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






|



















>
>







249946
249947
249948
249949
249950
249951
249952
249953
249954
249955
249956
249957
249958
249959
249960
249961
249962
249963
249964
249965
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
      }
    }
  }

  return rc;
}

/*
** xQueryToken() API implemenetation.
*/
static int fts5ApiQueryToken(
  Fts5Context* pCtx,
  int iPhrase,
  int iToken,
  const char **ppOut,
  int *pnOut
){
  Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
  return sqlite3Fts5ExprQueryToken(pCsr->pExpr, iPhrase, iToken, ppOut, pnOut);
}

/*
** xInstToken() API implemenetation.
*/
static int fts5ApiInstToken(
  Fts5Context *pCtx,
  int iIdx,
  int iToken,
  const char **ppOut, int *pnOut
){
  Fts5Cursor *pCsr = (Fts5Cursor*)pCtx;
  int rc = SQLITE_OK;
  if( CsrFlagTest(pCsr, FTS5CSR_REQUIRE_INST)==0
   || SQLITE_OK==(rc = fts5CacheInstArray(pCsr))
  ){
    if( iIdx<0 || iIdx>=pCsr->nInstCount ){
      rc = SQLITE_RANGE;
    }else{
      int iPhrase = pCsr->aInst[iIdx*3];
      int iCol = pCsr->aInst[iIdx*3 + 1];
      int iOff = pCsr->aInst[iIdx*3 + 2];
      i64 iRowid = fts5CursorRowid(pCsr);
      rc = sqlite3Fts5ExprInstToken(
          pCsr->pExpr, iRowid, iPhrase, iCol, iOff, iToken, ppOut, pnOut
      );
    }
  }
  return rc;
}


static int fts5ApiQueryPhrase(Fts5Context*, int, void*,
    int(*)(const Fts5ExtensionApi*, Fts5Context*, void*)
);

static const Fts5ExtensionApi sFts5Api = {
  3,                            /* iVersion */
  fts5ApiUserData,
  fts5ApiColumnCount,
  fts5ApiRowCount,
  fts5ApiColumnTotalSize,
  fts5ApiTokenize,
  fts5ApiPhraseCount,
  fts5ApiPhraseSize,
  fts5ApiInstCount,
  fts5ApiInst,
  fts5ApiRowid,
  fts5ApiColumnText,
  fts5ApiColumnSize,
  fts5ApiQueryPhrase,
  fts5ApiSetAuxdata,
  fts5ApiGetAuxdata,
  fts5ApiPhraseFirst,
  fts5ApiPhraseNext,
  fts5ApiPhraseFirstColumn,
  fts5ApiPhraseNextColumn,
  fts5ApiQueryToken,
  fts5ApiInstToken
};

/*
** Implementation of API function xQueryPhrase().
*/
static int fts5ApiQueryPhrase(
  Fts5Context *pCtx,
247341
247342
247343
247344
247345
247346
247347
247348
247349
247350
247351
247352
247353
247354
247355
247356
247357
247358
247359
247360
247361
247362
247363
247364
247365
247366
247367
247368
247369
247370
247371
247372
247373
247374
247375
247376
247377
247378
247379
247380
247381
247382
247383
247384
247385
247386
247387
247388
247389
247390
247391
247392
247393
*/
static int fts5RenameMethod(
  sqlite3_vtab *pVtab,            /* Virtual table handle */
  const char *zName               /* New name of table */
){
  int rc;
  Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
  pTab->bInSavepoint = 1;
  rc = sqlite3Fts5StorageRename(pTab->pStorage, zName);
  pTab->bInSavepoint = 0;
  return rc;
}

static int sqlite3Fts5FlushToDisk(Fts5Table *pTab){
  fts5TripCursors((Fts5FullTable*)pTab);
  return sqlite3Fts5StorageSync(((Fts5FullTable*)pTab)->pStorage);
}

/*
** The xSavepoint() method.
**
** Flush the contents of the pending-terms table to disk.
*/
static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
  Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
  int rc = SQLITE_OK;
  char *zSql = 0;
  fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint);

  if( pTab->bInSavepoint==0 ){
    zSql = sqlite3_mprintf("INSERT INTO %Q.%Q(%Q) VALUES('flush')",
        pTab->p.pConfig->zDb, pTab->p.pConfig->zName, pTab->p.pConfig->zName
    );
    if( zSql ){
      pTab->bInSavepoint = 1;
      rc = sqlite3_exec(pTab->p.pConfig->db, zSql, 0, 0, 0);
      pTab->bInSavepoint = 0;
      sqlite3_free(zSql);
    }else{
      rc = SQLITE_NOMEM;
    }
    if( rc==SQLITE_OK ){
      pTab->iSavepoint = iSavepoint+1;
    }
  }

  return rc;
}

/*
** The xRelease() method.
**
** This is a no-op.







<

<
















|

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







250283
250284
250285
250286
250287
250288
250289

250290

250291
250292
250293
250294
250295
250296
250297
250298
250299
250300
250301
250302
250303
250304
250305
250306
250307
250308
250309












250310
250311
250312


250313
250314
250315
250316
250317
250318
250319
*/
static int fts5RenameMethod(
  sqlite3_vtab *pVtab,            /* Virtual table handle */
  const char *zName               /* New name of table */
){
  int rc;
  Fts5FullTable *pTab = (Fts5FullTable*)pVtab;

  rc = sqlite3Fts5StorageRename(pTab->pStorage, zName);

  return rc;
}

static int sqlite3Fts5FlushToDisk(Fts5Table *pTab){
  fts5TripCursors((Fts5FullTable*)pTab);
  return sqlite3Fts5StorageSync(((Fts5FullTable*)pTab)->pStorage);
}

/*
** The xSavepoint() method.
**
** Flush the contents of the pending-terms table to disk.
*/
static int fts5SavepointMethod(sqlite3_vtab *pVtab, int iSavepoint){
  Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
  int rc = SQLITE_OK;

  fts5CheckTransactionState(pTab, FTS5_SAVEPOINT, iSavepoint);
  rc = sqlite3Fts5FlushToDisk((Fts5Table*)pVtab);












  if( rc==SQLITE_OK ){
    pTab->iSavepoint = iSavepoint+1;
  }


  return rc;
}

/*
** The xRelease() method.
**
** This is a no-op.
247411
247412
247413
247414
247415
247416
247417
247418
247419

247420
247421
247422
247423
247424
247425
247426
** Discard the contents of the pending terms table.
*/
static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
  Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
  int rc = SQLITE_OK;
  fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint);
  fts5TripCursors(pTab);
  pTab->p.pConfig->pgsz = 0;
  if( (iSavepoint+1)<=pTab->iSavepoint ){

    rc = sqlite3Fts5StorageRollback(pTab->pStorage);
  }
  return rc;
}

/*
** Register a new auxiliary function with global context pGlobal.







<

>







250337
250338
250339
250340
250341
250342
250343

250344
250345
250346
250347
250348
250349
250350
250351
250352
** Discard the contents of the pending terms table.
*/
static int fts5RollbackToMethod(sqlite3_vtab *pVtab, int iSavepoint){
  Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
  int rc = SQLITE_OK;
  fts5CheckTransactionState(pTab, FTS5_ROLLBACKTO, iSavepoint);
  fts5TripCursors(pTab);

  if( (iSavepoint+1)<=pTab->iSavepoint ){
    pTab->p.pConfig->pgsz = 0;
    rc = sqlite3Fts5StorageRollback(pTab->pStorage);
  }
  return rc;
}

/*
** Register a new auxiliary function with global context pGlobal.
247617
247618
247619
247620
247621
247622
247623
247624
247625
247626
247627
247628
247629
247630
247631
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: 2023-11-24 11:41:44 ebead0e7230cd33bcec9f95d2183069565b9e709bf745c9b5db65cc0cbf92c0f", -1, SQLITE_TRANSIENT);
}

/*
** Return true if zName is the extension on one of the shadow tables used
** by this module.
*/
static int fts5ShadowName(const char *zName){







|







250543
250544
250545
250546
250547
250548
250549
250550
250551
250552
250553
250554
250555
250556
250557
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-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a", -1, SQLITE_TRANSIENT);
}

/*
** Return true if zName is the extension on one of the shadow tables used
** by this module.
*/
static int fts5ShadowName(const char *zName){
247640
247641
247642
247643
247644
247645
247646
247647
247648
247649
247650
247651
247652
247653
247654
247655
247656
247657
247658

247659
247660
247661
247662
247663
247664
247665
247666
247667
247668
247669
247670
247671
247672
247673
247674

247675
247676
247677
247678
247679
247680
247681
247682
}

/*
** Run an integrity check on the FTS5 data structures.  Return a string
** if anything is found amiss.  Return a NULL pointer if everything is
** OK.
*/
static int fts5Integrity(
  sqlite3_vtab *pVtab,    /* the FTS5 virtual table to check */
  const char *zSchema,    /* Name of schema in which this table lives */
  const char *zTabname,   /* Name of the table itself */
  int isQuick,            /* True if this is a quick-check */
  char **pzErr            /* Write error message here */
){
  Fts5FullTable *pTab = (Fts5FullTable*)pVtab;
  Fts5Config *pConfig = pTab->p.pConfig;
  char *zSql;
  char *zErr = 0;
  int rc;

  assert( pzErr!=0 && *pzErr==0 );
  UNUSED_PARAM(isQuick);
  zSql = sqlite3_mprintf(
            "INSERT INTO \"%w\".\"%w\"(\"%w\") VALUES('integrity-check');",
            zSchema, zTabname, pConfig->zName);
  if( zSql==0 ) return SQLITE_NOMEM;
  rc = sqlite3_exec(pConfig->db, zSql, 0, 0, &zErr);
  sqlite3_free(zSql);
  if( (rc&0xff)==SQLITE_CORRUPT ){
    *pzErr = sqlite3_mprintf("malformed inverted index for FTS5 table %s.%s",
                zSchema, zTabname);
  }else if( rc!=SQLITE_OK ){
    *pzErr = sqlite3_mprintf("unable to validate the inverted index for"
                             " FTS5 table %s.%s: %s",
                zSchema, zTabname, zErr);
  }

  sqlite3_free(zErr);
  return SQLITE_OK;
}

static int fts5Init(sqlite3 *db){
  static const sqlite3_module fts5Mod = {
    /* iVersion      */ 4,
    /* xCreate       */ fts5CreateMethod,







|







<
<
<

>


|
<
<
<
<
<






|

>
|







250566
250567
250568
250569
250570
250571
250572
250573
250574
250575
250576
250577
250578
250579
250580



250581
250582
250583
250584
250585





250586
250587
250588
250589
250590
250591
250592
250593
250594
250595
250596
250597
250598
250599
250600
250601
250602
}

/*
** Run an integrity check on the FTS5 data structures.  Return a string
** if anything is found amiss.  Return a NULL pointer if everything is
** OK.
*/
static int fts5IntegrityMethod(
  sqlite3_vtab *pVtab,    /* the FTS5 virtual table to check */
  const char *zSchema,    /* Name of schema in which this table lives */
  const char *zTabname,   /* Name of the table itself */
  int isQuick,            /* True if this is a quick-check */
  char **pzErr            /* Write error message here */
){
  Fts5FullTable *pTab = (Fts5FullTable*)pVtab;



  int rc;

  assert( pzErr!=0 && *pzErr==0 );
  UNUSED_PARAM(isQuick);
  rc = sqlite3Fts5StorageIntegrity(pTab->pStorage, 0);





  if( (rc&0xff)==SQLITE_CORRUPT ){
    *pzErr = sqlite3_mprintf("malformed inverted index for FTS5 table %s.%s",
                zSchema, zTabname);
  }else if( rc!=SQLITE_OK ){
    *pzErr = sqlite3_mprintf("unable to validate the inverted index for"
                             " FTS5 table %s.%s: %s",
                zSchema, zTabname, sqlite3_errstr(rc));
  }
  sqlite3Fts5IndexCloseReader(pTab->p.pIndex);

  return SQLITE_OK;
}

static int fts5Init(sqlite3 *db){
  static const sqlite3_module fts5Mod = {
    /* iVersion      */ 4,
    /* xCreate       */ fts5CreateMethod,
247698
247699
247700
247701
247702
247703
247704
247705
247706
247707
247708
247709
247710
247711
247712
    /* xRollback     */ fts5RollbackMethod,
    /* xFindFunction */ fts5FindFunctionMethod,
    /* xRename       */ fts5RenameMethod,
    /* xSavepoint    */ fts5SavepointMethod,
    /* xRelease      */ fts5ReleaseMethod,
    /* xRollbackTo   */ fts5RollbackToMethod,
    /* xShadowName   */ fts5ShadowName,
    /* xIntegrity    */ fts5Integrity
  };

  int rc;
  Fts5Global *pGlobal = 0;

  pGlobal = (Fts5Global*)sqlite3_malloc(sizeof(Fts5Global));
  if( pGlobal==0 ){







|







250618
250619
250620
250621
250622
250623
250624
250625
250626
250627
250628
250629
250630
250631
250632
    /* xRollback     */ fts5RollbackMethod,
    /* xFindFunction */ fts5FindFunctionMethod,
    /* xRename       */ fts5RenameMethod,
    /* xSavepoint    */ fts5SavepointMethod,
    /* xRelease      */ fts5ReleaseMethod,
    /* xRollbackTo   */ fts5RollbackToMethod,
    /* xShadowName   */ fts5ShadowName,
    /* xIntegrity    */ fts5IntegrityMethod
  };

  int rc;
  Fts5Global *pGlobal = 0;

  pGlobal = (Fts5Global*)sqlite3_malloc(sizeof(Fts5Global));
  if( pGlobal==0 ){
248464
248465
248466
248467
248468
248469
248470
248471
248472
248473
248474
248475
248476
248477
248478
  ctx.pStorage = p;
  rc = sqlite3Fts5StorageDeleteAll(p);
  if( rc==SQLITE_OK ){
    rc = fts5StorageLoadTotals(p, 1);
  }

  if( rc==SQLITE_OK ){
    rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, 0);
  }

  while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pScan) ){
    i64 iRowid = sqlite3_column_int64(pScan, 0);

    sqlite3Fts5BufferZero(&buf);
    rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid);







|







251384
251385
251386
251387
251388
251389
251390
251391
251392
251393
251394
251395
251396
251397
251398
  ctx.pStorage = p;
  rc = sqlite3Fts5StorageDeleteAll(p);
  if( rc==SQLITE_OK ){
    rc = fts5StorageLoadTotals(p, 1);
  }

  if( rc==SQLITE_OK ){
    rc = fts5StorageGetStmt(p, FTS5_STMT_SCAN, &pScan, pConfig->pzErrmsg);
  }

  while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(pScan) ){
    i64 iRowid = sqlite3_column_int64(pScan, 0);

    sqlite3Fts5BufferZero(&buf);
    rc = sqlite3Fts5IndexBeginWrite(p->pIndex, 0, iRowid);
249250
249251
249252
249253
249254
249255
249256






249257
249258
249259
249260
249261
249262
249263
    *zOut++ = 0x80 + (unsigned char)((c>>12) & 0x3F);  \
    *zOut++ = 0x80 + (unsigned char)((c>>6) & 0x3F);   \
    *zOut++ = 0x80 + (unsigned char)(c & 0x3F);        \
  }                                                    \
}

#endif /* ifndef SQLITE_AMALGAMATION */







typedef struct Unicode61Tokenizer Unicode61Tokenizer;
struct Unicode61Tokenizer {
  unsigned char aTokenChar[128];  /* ASCII range token characters */
  char *aFold;                    /* Buffer to fold text into */
  int nFold;                      /* Size of aFold[] in bytes */
  int eRemoveDiacritic;           /* True if remove_diacritics=1 is set */







>
>
>
>
>
>







252170
252171
252172
252173
252174
252175
252176
252177
252178
252179
252180
252181
252182
252183
252184
252185
252186
252187
252188
252189
    *zOut++ = 0x80 + (unsigned char)((c>>12) & 0x3F);  \
    *zOut++ = 0x80 + (unsigned char)((c>>6) & 0x3F);   \
    *zOut++ = 0x80 + (unsigned char)(c & 0x3F);        \
  }                                                    \
}

#endif /* ifndef SQLITE_AMALGAMATION */

#define FTS5_SKIP_UTF8(zIn) {                               \
  if( ((unsigned char)(*(zIn++)))>=0xc0 ){                              \
    while( (((unsigned char)*zIn) & 0xc0)==0x80 ){ zIn++; }             \
  }                                                    \
}

typedef struct Unicode61Tokenizer Unicode61Tokenizer;
struct Unicode61Tokenizer {
  unsigned char aTokenChar[128];  /* ASCII range token characters */
  char *aFold;                    /* Buffer to fold text into */
  int nFold;                      /* Size of aFold[] in bytes */
  int eRemoveDiacritic;           /* True if remove_diacritics=1 is set */
250286
250287
250288
250289
250290
250291
250292

250293
250294
250295
250296
250297
250298
250299

/**************************************************************************
** Start of trigram implementation.
*/
typedef struct TrigramTokenizer TrigramTokenizer;
struct TrigramTokenizer {
  int bFold;                      /* True to fold to lower-case */

};

/*
** Free a trigram tokenizer.
*/
static void fts5TriDelete(Fts5Tokenizer *p){
  sqlite3_free(p);







>







253212
253213
253214
253215
253216
253217
253218
253219
253220
253221
253222
253223
253224
253225
253226

/**************************************************************************
** Start of trigram implementation.
*/
typedef struct TrigramTokenizer TrigramTokenizer;
struct TrigramTokenizer {
  int bFold;                      /* True to fold to lower-case */
  int iFoldParam;                 /* Parameter to pass to Fts5UnicodeFold() */
};

/*
** Free a trigram tokenizer.
*/
static void fts5TriDelete(Fts5Tokenizer *p){
  sqlite3_free(p);
250312
250313
250314
250315
250316
250317
250318

250319
250320
250321
250322
250323
250324
250325
250326






250327
250328
250329
250330





250331
250332
250333
250334
250335
250336
250337
  TrigramTokenizer *pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew));
  UNUSED_PARAM(pUnused);
  if( pNew==0 ){
    rc = SQLITE_NOMEM;
  }else{
    int i;
    pNew->bFold = 1;

    for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
      const char *zArg = azArg[i+1];
      if( 0==sqlite3_stricmp(azArg[i], "case_sensitive") ){
        if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1] ){
          rc = SQLITE_ERROR;
        }else{
          pNew->bFold = (zArg[0]=='0');
        }






      }else{
        rc = SQLITE_ERROR;
      }
    }





    if( rc!=SQLITE_OK ){
      fts5TriDelete((Fts5Tokenizer*)pNew);
      pNew = 0;
    }
  }
  *ppOut = (Fts5Tokenizer*)pNew;
  return rc;







>








>
>
>
>
>
>




>
>
>
>
>







253239
253240
253241
253242
253243
253244
253245
253246
253247
253248
253249
253250
253251
253252
253253
253254
253255
253256
253257
253258
253259
253260
253261
253262
253263
253264
253265
253266
253267
253268
253269
253270
253271
253272
253273
253274
253275
253276
  TrigramTokenizer *pNew = (TrigramTokenizer*)sqlite3_malloc(sizeof(*pNew));
  UNUSED_PARAM(pUnused);
  if( pNew==0 ){
    rc = SQLITE_NOMEM;
  }else{
    int i;
    pNew->bFold = 1;
    pNew->iFoldParam = 0;
    for(i=0; rc==SQLITE_OK && i<nArg; i+=2){
      const char *zArg = azArg[i+1];
      if( 0==sqlite3_stricmp(azArg[i], "case_sensitive") ){
        if( (zArg[0]!='0' && zArg[0]!='1') || zArg[1] ){
          rc = SQLITE_ERROR;
        }else{
          pNew->bFold = (zArg[0]=='0');
        }
      }else if( 0==sqlite3_stricmp(azArg[i], "remove_diacritics") ){
        if( (zArg[0]!='0' && zArg[0]!='1' && zArg[0]!='2') || zArg[1] ){
          rc = SQLITE_ERROR;
        }else{
          pNew->iFoldParam = (zArg[0]!='0') ? 2 : 0;
        }
      }else{
        rc = SQLITE_ERROR;
      }
    }

    if( pNew->iFoldParam!=0 && pNew->bFold==0 ){
      rc = SQLITE_ERROR;
    }

    if( rc!=SQLITE_OK ){
      fts5TriDelete((Fts5Tokenizer*)pNew);
      pNew = 0;
    }
  }
  *ppOut = (Fts5Tokenizer*)pNew;
  return rc;
250346
250347
250348
250349
250350
250351
250352


250353
250354
250355

250356
250357




















250358

250359
250360
250361
250362


250363
250364
250365
250366
250367
250368
250369


250370
250371
250372
250373
250374
250375

250376
250377
250378
250379
250380
250381
250382
250383

250384
250385

250386
250387
250388
250389
250390
250391
250392
250393
  int unusedFlags,
  const char *pText, int nText,
  int (*xToken)(void*, int, const char*, int, int, int)
){
  TrigramTokenizer *p = (TrigramTokenizer*)pTok;
  int rc = SQLITE_OK;
  char aBuf[32];


  const unsigned char *zIn = (const unsigned char*)pText;
  const unsigned char *zEof = &zIn[nText];
  u32 iCode;


  UNUSED_PARAM(unusedFlags);




















  while( 1 ){

    char *zOut = aBuf;
    int iStart = zIn - (const unsigned char*)pText;
    const unsigned char *zNext;



    READ_UTF8(zIn, zEof, iCode);
    if( iCode==0 ) break;
    zNext = zIn;
    if( zIn<zEof ){
      if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
      WRITE_UTF8(zOut, iCode);
      READ_UTF8(zIn, zEof, iCode);


      if( iCode==0 ) break;
    }else{
      break;
    }
    if( zIn<zEof ){
      if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);

      WRITE_UTF8(zOut, iCode);
      READ_UTF8(zIn, zEof, iCode);
      if( iCode==0 ) break;
      if( p->bFold ) iCode = sqlite3Fts5UnicodeFold(iCode, 0);
      WRITE_UTF8(zOut, iCode);
    }else{
      break;
    }

    rc = xToken(pCtx, 0, aBuf, zOut-aBuf, iStart, iStart + zOut-aBuf);
    if( rc!=SQLITE_OK ) break;

    zIn = zNext;
  }

  return rc;
}

/*
** Argument xCreate is a pointer to a constructor function for a tokenizer.







>
>



>


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

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







253285
253286
253287
253288
253289
253290
253291
253292
253293
253294
253295
253296
253297
253298
253299
253300
253301
253302
253303
253304
253305
253306
253307
253308
253309
253310
253311
253312
253313
253314
253315
253316
253317
253318
253319
253320
253321
253322
253323

253324
253325
253326
253327
253328


253329
253330
253331
253332
253333
253334


253335
253336
253337
253338
253339
253340

253341
253342


253343
253344
253345

253346
253347
253348
253349
253350
253351
253352
253353
253354
  int unusedFlags,
  const char *pText, int nText,
  int (*xToken)(void*, int, const char*, int, int, int)
){
  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;



    /* Remove the first character from buffer aBuf[]. Append the character
    ** with codepoint iCode.  */
    z1 = aBuf;
    FTS5_SKIP_UTF8(z1);
    memmove(aBuf, z1, zOut - z1);

    zOut -= (z1 - aBuf);
    WRITE_UTF8(zOut, iCode);



    /* Update the aStart[] array */
    aStart[0] = aStart[1];

    aStart[1] = aStart[2];
    aStart[2] = iNext;
  }

  return rc;
}

/*
** Argument xCreate is a pointer to a constructor function for a tokenizer.
250402
250403
250404
250405
250406
250407
250408

250409

250410
250411
250412
250413
250414
250415
250416
*/
static int sqlite3Fts5TokenizerPattern(
    int (*xCreate)(void*, const char**, int, Fts5Tokenizer**),
    Fts5Tokenizer *pTok
){
  if( xCreate==fts5TriCreate ){
    TrigramTokenizer *p = (TrigramTokenizer*)pTok;

    return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB;

  }
  return FTS5_PATTERN_NONE;
}

/*
** Register all built-in tokenizers with FTS5.
*/







>
|
>







253363
253364
253365
253366
253367
253368
253369
253370
253371
253372
253373
253374
253375
253376
253377
253378
253379
*/
static int sqlite3Fts5TokenizerPattern(
    int (*xCreate)(void*, const char**, int, Fts5Tokenizer**),
    Fts5Tokenizer *pTok
){
  if( xCreate==fts5TriCreate ){
    TrigramTokenizer *p = (TrigramTokenizer*)pTok;
    if( p->iFoldParam==0 ){
      return p->bFold ? FTS5_PATTERN_LIKE : FTS5_PATTERN_GLOB;
    }
  }
  return FTS5_PATTERN_NONE;
}

/*
** Register all built-in tokenizers with FTS5.
*/
252191
252192
252193
252194
252195
252196
252197
252198
252199
252200
252201
252202
252203
252204
252205
  if( idxNum & FTS5_VOCAB_TERM_EQ ) pEq = apVal[iVal++];
  if( idxNum & FTS5_VOCAB_TERM_GE ) pGe = apVal[iVal++];
  if( idxNum & FTS5_VOCAB_TERM_LE ) pLe = apVal[iVal++];

  if( pEq ){
    zTerm = (const char *)sqlite3_value_text(pEq);
    nTerm = sqlite3_value_bytes(pEq);
    f = 0;
  }else{
    if( pGe ){
      zTerm = (const char *)sqlite3_value_text(pGe);
      nTerm = sqlite3_value_bytes(pGe);
    }
    if( pLe ){
      const char *zCopy = (const char *)sqlite3_value_text(pLe);







|







255154
255155
255156
255157
255158
255159
255160
255161
255162
255163
255164
255165
255166
255167
255168
  if( idxNum & FTS5_VOCAB_TERM_EQ ) pEq = apVal[iVal++];
  if( idxNum & FTS5_VOCAB_TERM_GE ) pGe = apVal[iVal++];
  if( idxNum & FTS5_VOCAB_TERM_LE ) pLe = apVal[iVal++];

  if( pEq ){
    zTerm = (const char *)sqlite3_value_text(pEq);
    nTerm = sqlite3_value_bytes(pEq);
    f = FTS5INDEX_QUERY_NOTOKENDATA;
  }else{
    if( pGe ){
      zTerm = (const char *)sqlite3_value_text(pGe);
      nTerm = sqlite3_value_bytes(pGe);
    }
    if( pLe ){
      const char *zCopy = (const char *)sqlite3_value_text(pLe);
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.44.2"
#define SQLITE_VERSION_NUMBER 3044002
#define SQLITE_SOURCE_ID      "2023-11-24 11:41:44 ebead0e7230cd33bcec9f95d2183069565b9e709bf745c9b5db65cc0cbf92c0f"

/*
** 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.45.1"
#define SQLITE_VERSION_NUMBER 3045001
#define SQLITE_SOURCE_ID      "2024-01-30 16:01:20 e876e51a0ed5c5b3126f52e532044363a014bc594cfefa87ffb5b82257cc467a"

/*
** 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
3950
3951
3952
3953
3954
3955
3956
3957

3958
3959
3960
3961
3962
3963
3964
3965

3966
3967
3968
3969
3970
3971
3972
** <li> sqlite3_extended_errcode()
** <li> sqlite3_errmsg()
** <li> sqlite3_errmsg16()
** <li> sqlite3_error_offset()
** </ul>
**
** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
** text that describes the error, as either UTF-8 or UTF-16 respectively.

** (See how SQLite handles [invalid UTF] for exceptions to this rule.)
** ^(Memory to hold the error message string is managed internally.
** The application does not need to worry about freeing the result.
** However, the error string might be overwritten or deallocated by
** subsequent calls to other SQLite interface functions.)^
**
** ^The sqlite3_errstr() interface returns the English-language text
** that describes the [result code], as UTF-8.

** ^(Memory to hold the error message string is managed internally
** and must not be freed by the application)^.
**
** ^If the most recent error references a specific token in the input
** SQL, the sqlite3_error_offset() interface returns the byte offset
** of the start of that token.  ^The byte offset returned by
** sqlite3_error_offset() assumes that the input SQL is UTF8.







|
>






|
|
>







3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
3970
3971
3972
3973
3974
** <li> sqlite3_extended_errcode()
** <li> sqlite3_errmsg()
** <li> sqlite3_errmsg16()
** <li> sqlite3_error_offset()
** </ul>
**
** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language
** text that describes the error, as either UTF-8 or UTF-16 respectively,
** or NULL if no error message is available.
** (See how SQLite handles [invalid UTF] for exceptions to this rule.)
** ^(Memory to hold the error message string is managed internally.
** The application does not need to worry about freeing the result.
** However, the error string might be overwritten or deallocated by
** subsequent calls to other SQLite interface functions.)^
**
** ^The sqlite3_errstr(E) interface returns the English-language text
** that describes the [result code] E, as UTF-8, or NULL if E is not an
** result code for which a text error message is available.
** ^(Memory to hold the error message string is managed internally
** and must not be freed by the application)^.
**
** ^If the most recent error references a specific token in the input
** SQL, the sqlite3_error_offset() interface returns the byte offset
** of the start of that token.  ^The byte offset returned by
** sqlite3_error_offset() assumes that the input SQL is UTF8.
8033
8034
8035
8036
8037
8038
8039
8040
8041
8042


8043
8044
8045
8046
8047
8048
8049
** In such cases, the
** mutex must be exited an equal number of times before another thread
** can enter.)^  If the same thread tries to enter any mutex other
** than an SQLITE_MUTEX_RECURSIVE more than once, the behavior is undefined.
**
** ^(Some systems (for example, Windows 95) do not support the operation
** implemented by sqlite3_mutex_try().  On those systems, sqlite3_mutex_try()
** will always return SQLITE_BUSY. The SQLite core only ever uses
** sqlite3_mutex_try() as an optimization so this is acceptable
** behavior.)^


**
** ^The sqlite3_mutex_leave() routine exits a mutex that was
** previously entered by the same thread.   The behavior
** is undefined if the mutex is not currently entered by the
** calling thread or is not currently allocated.
**
** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(),







|
|
|
>
>







8035
8036
8037
8038
8039
8040
8041
8042
8043
8044
8045
8046
8047
8048
8049
8050
8051
8052
8053
** In such cases, the
** mutex must be exited an equal number of times before another thread
** can enter.)^  If the same thread tries to enter any mutex other
** than an SQLITE_MUTEX_RECURSIVE more than once, the behavior is undefined.
**
** ^(Some systems (for example, Windows 95) do not support the operation
** implemented by sqlite3_mutex_try().  On those systems, sqlite3_mutex_try()
** will always return SQLITE_BUSY. In most cases the SQLite core only uses
** sqlite3_mutex_try() as an optimization, so this is acceptable
** behavior. The exceptions are unix builds that set the
** SQLITE_ENABLE_SETLK_TIMEOUT build option. In that case a working
** sqlite3_mutex_try() is required.)^
**
** ^The sqlite3_mutex_leave() routine exits a mutex that was
** previously entered by the same thread.   The behavior
** is undefined if the mutex is not currently entered by the
** calling thread or is not currently allocated.
**
** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(),
8294
8295
8296
8297
8298
8299
8300

8301
8302
8303
8304
8305
8306
8307
#define SQLITE_TESTCTRL_BITVEC_TEST              8
#define SQLITE_TESTCTRL_FAULT_INSTALL            9
#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS     10
#define SQLITE_TESTCTRL_PENDING_BYTE            11
#define SQLITE_TESTCTRL_ASSERT                  12
#define SQLITE_TESTCTRL_ALWAYS                  13
#define SQLITE_TESTCTRL_RESERVE                 14  /* NOT USED */

#define SQLITE_TESTCTRL_OPTIMIZATIONS           15
#define SQLITE_TESTCTRL_ISKEYWORD               16  /* NOT USED */
#define SQLITE_TESTCTRL_SCRATCHMALLOC           17  /* NOT USED */
#define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS      17
#define SQLITE_TESTCTRL_LOCALTIME_FAULT         18
#define SQLITE_TESTCTRL_EXPLAIN_STMT            19  /* NOT USED */
#define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD    19







>







8298
8299
8300
8301
8302
8303
8304
8305
8306
8307
8308
8309
8310
8311
8312
#define SQLITE_TESTCTRL_BITVEC_TEST              8
#define SQLITE_TESTCTRL_FAULT_INSTALL            9
#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS     10
#define SQLITE_TESTCTRL_PENDING_BYTE            11
#define SQLITE_TESTCTRL_ASSERT                  12
#define SQLITE_TESTCTRL_ALWAYS                  13
#define SQLITE_TESTCTRL_RESERVE                 14  /* NOT USED */
#define SQLITE_TESTCTRL_JSON_SELFCHECK          14
#define SQLITE_TESTCTRL_OPTIMIZATIONS           15
#define SQLITE_TESTCTRL_ISKEYWORD               16  /* NOT USED */
#define SQLITE_TESTCTRL_SCRATCHMALLOC           17  /* NOT USED */
#define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS      17
#define SQLITE_TESTCTRL_LOCALTIME_FAULT         18
#define SQLITE_TESTCTRL_EXPLAIN_STMT            19  /* NOT USED */
#define SQLITE_TESTCTRL_ONCE_RESET_THRESHOLD    19
12807
12808
12809
12810
12811
12812
12813



12814
12815
12816
12817
12818
12819
12820
12821
12822
12823
12824


12825
12826
12827
12828
12829
12830
12831
12832
12833
12834
12835
12836
12837
12838
12839
12840
12841
12842

12843
12844
12845
12846
12847
12848
12849
12850
12851
12852
12853
12854
**   an OOM condition or IO error), an appropriate SQLite error code is
**   returned.
**
**   This function may be quite inefficient if used with an FTS5 table
**   created with the "columnsize=0" option.
**
** xColumnText:



**   This function attempts to retrieve the text of column iCol of the
**   current document. If successful, (*pz) is set to point to a buffer
**   containing the text in utf-8 encoding, (*pn) is set to the size in bytes
**   (not characters) of the buffer and SQLITE_OK is returned. Otherwise,
**   if an error occurs, an SQLite error code is returned and the final values
**   of (*pz) and (*pn) are undefined.
**
** xPhraseCount:
**   Returns the number of phrases in the current query expression.
**
** xPhraseSize:


**   Returns the number of tokens in phrase iPhrase of the query. Phrases
**   are numbered starting from zero.
**
** xInstCount:
**   Set *pnInst to the total number of occurrences of all phrases within
**   the query within the current row. Return SQLITE_OK if successful, or
**   an error code (i.e. SQLITE_NOMEM) if an error occurs.
**
**   This API can be quite slow if used with an FTS5 table created with the
**   "detail=none" or "detail=column" option. If the FTS5 table is created
**   with either "detail=none" or "detail=column" and "content=" option
**   (i.e. if it is a contentless table), then this API always returns 0.
**
** xInst:
**   Query for the details of phrase match iIdx within the current row.
**   Phrase matches are numbered starting from zero, so the iIdx argument
**   should be greater than or equal to zero and smaller than the value
**   output by xInstCount().

**
**   Usually, output parameter *piPhrase is set to the phrase number, *piCol
**   to the column in which it occurs and *piOff the token offset of the
**   first token of the phrase. Returns SQLITE_OK if successful, or an error
**   code (i.e. SQLITE_NOMEM) if an error occurs.
**
**   This API can be quite slow if used with an FTS5 table created with the
**   "detail=none" or "detail=column" option.
**
** xRowid:
**   Returns the rowid of the current row.
**







>
>
>
|
|









>
>
|
|















|
>

|

|
|







12812
12813
12814
12815
12816
12817
12818
12819
12820
12821
12822
12823
12824
12825
12826
12827
12828
12829
12830
12831
12832
12833
12834
12835
12836
12837
12838
12839
12840
12841
12842
12843
12844
12845
12846
12847
12848
12849
12850
12851
12852
12853
12854
12855
12856
12857
12858
12859
12860
12861
12862
12863
12864
12865
**   an OOM condition or IO error), an appropriate SQLite error code is
**   returned.
**
**   This function may be quite inefficient if used with an FTS5 table
**   created with the "columnsize=0" option.
**
** xColumnText:
**   If parameter iCol is less than zero, or greater than or equal to the
**   number of columns in the table, SQLITE_RANGE is returned.
**
**   Otherwise, this function attempts to retrieve the text of column iCol of
**   the current document. If successful, (*pz) is set to point to a buffer
**   containing the text in utf-8 encoding, (*pn) is set to the size in bytes
**   (not characters) of the buffer and SQLITE_OK is returned. Otherwise,
**   if an error occurs, an SQLite error code is returned and the final values
**   of (*pz) and (*pn) are undefined.
**
** xPhraseCount:
**   Returns the number of phrases in the current query expression.
**
** xPhraseSize:
**   If parameter iCol is less than zero, or greater than or equal to the
**   number of phrases in the current query, as returned by xPhraseCount,
**   0 is returned. Otherwise, this function returns the number of tokens in
**   phrase iPhrase of the query. Phrases are numbered starting from zero.
**
** xInstCount:
**   Set *pnInst to the total number of occurrences of all phrases within
**   the query within the current row. Return SQLITE_OK if successful, or
**   an error code (i.e. SQLITE_NOMEM) if an error occurs.
**
**   This API can be quite slow if used with an FTS5 table created with the
**   "detail=none" or "detail=column" option. If the FTS5 table is created
**   with either "detail=none" or "detail=column" and "content=" option
**   (i.e. if it is a contentless table), then this API always returns 0.
**
** xInst:
**   Query for the details of phrase match iIdx within the current row.
**   Phrase matches are numbered starting from zero, so the iIdx argument
**   should be greater than or equal to zero and smaller than the value
**   output by xInstCount(). If iIdx is less than zero or greater than
**   or equal to the value returned by xInstCount(), SQLITE_RANGE is returned.
**
**   Otherwise, output parameter *piPhrase is set to the phrase number, *piCol
**   to the column in which it occurs and *piOff the token offset of the
**   first token of the phrase. SQLITE_OK is returned if successful, or an
**   error code (i.e. SQLITE_NOMEM) if an error occurs.
**
**   This API can be quite slow if used with an FTS5 table created with the
**   "detail=none" or "detail=column" option.
**
** xRowid:
**   Returns the rowid of the current row.
**
12865
12866
12867
12868
12869
12870
12871




12872
12873
12874
12875
12876
12877
12878
**   current query is executed. Any column filter that applies to
**   phrase iPhrase of the current query is included in $p. For each
**   row visited, the callback function passed as the fourth argument
**   is invoked. The context and API objects passed to the callback
**   function may be used to access the properties of each matched row.
**   Invoking Api.xUserData() returns a copy of the pointer passed as
**   the third argument to pUserData.




**
**   If the callback function returns any value other than SQLITE_OK, the
**   query is abandoned and the xQueryPhrase function returns immediately.
**   If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK.
**   Otherwise, the error code is propagated upwards.
**
**   If the query runs to completion without incident, SQLITE_OK is returned.







>
>
>
>







12876
12877
12878
12879
12880
12881
12882
12883
12884
12885
12886
12887
12888
12889
12890
12891
12892
12893
**   current query is executed. Any column filter that applies to
**   phrase iPhrase of the current query is included in $p. For each
**   row visited, the callback function passed as the fourth argument
**   is invoked. The context and API objects passed to the callback
**   function may be used to access the properties of each matched row.
**   Invoking Api.xUserData() returns a copy of the pointer passed as
**   the third argument to pUserData.
**
**   If parameter iPhrase is less than zero, or greater than or equal to
**   the number of phrases in the query, as returned by xPhraseCount(),
**   this function returns SQLITE_RANGE.
**
**   If the callback function returns any value other than SQLITE_OK, the
**   query is abandoned and the xQueryPhrase function returns immediately.
**   If the returned value is SQLITE_DONE, xQueryPhrase returns SQLITE_OK.
**   Otherwise, the error code is propagated upwards.
**
**   If the query runs to completion without incident, SQLITE_OK is returned.
12980
12981
12982
12983
12984
12985
12986

































12987
12988
12989
12990
12991
12992
12993
12994
12995
12996
**   xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext
**   (or xInst/xInstCount). The chief advantage of this API is that it is
**   significantly more efficient than those alternatives when used with
**   "detail=column" tables.
**
** xPhraseNextColumn()
**   See xPhraseFirstColumn above.

































*/
struct Fts5ExtensionApi {
  int iVersion;                   /* Currently always set to 2 */

  void *(*xUserData)(Fts5Context*);

  int (*xColumnCount)(Fts5Context*);
  int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow);
  int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken);








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


|







12995
12996
12997
12998
12999
13000
13001
13002
13003
13004
13005
13006
13007
13008
13009
13010
13011
13012
13013
13014
13015
13016
13017
13018
13019
13020
13021
13022
13023
13024
13025
13026
13027
13028
13029
13030
13031
13032
13033
13034
13035
13036
13037
13038
13039
13040
13041
13042
13043
13044
**   xPhraseFirstColumn() may also be obtained using xPhraseFirst/xPhraseNext
**   (or xInst/xInstCount). The chief advantage of this API is that it is
**   significantly more efficient than those alternatives when used with
**   "detail=column" tables.
**
** xPhraseNextColumn()
**   See xPhraseFirstColumn above.
**
** xQueryToken(pFts5, iPhrase, iToken, ppToken, pnToken)
**   This is used to access token iToken of phrase iPhrase of the current
**   query. Before returning, output parameter *ppToken is set to point
**   to a buffer containing the requested token, and *pnToken to the
**   size of this buffer in bytes.
**
**   If iPhrase or iToken are less than zero, or if iPhrase is greater than
**   or equal to the number of phrases in the query as reported by
**   xPhraseCount(), or if iToken is equal to or greater than the number of
**   tokens in the phrase, SQLITE_RANGE is returned and *ppToken and *pnToken
     are both zeroed.
**
**   The output text is not a copy of the query text that specified the
**   token. It is the output of the tokenizer module. For tokendata=1
**   tables, this includes any embedded 0x00 and trailing data.
**
** 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.
*/
struct Fts5ExtensionApi {
  int iVersion;                   /* Currently always set to 3 */

  void *(*xUserData)(Fts5Context*);

  int (*xColumnCount)(Fts5Context*);
  int (*xRowCount)(Fts5Context*, sqlite3_int64 *pnRow);
  int (*xColumnTotalSize)(Fts5Context*, int iCol, sqlite3_int64 *pnToken);

13017
13018
13019
13020
13021
13022
13023







13024
13025
13026
13027
13028
13029
13030
  void *(*xGetAuxdata)(Fts5Context*, int bClear);

  int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*);
  void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff);

  int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
  void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);







};

/*
** CUSTOM AUXILIARY FUNCTIONS
*************************************************************************/

/*************************************************************************







>
>
>
>
>
>
>







13065
13066
13067
13068
13069
13070
13071
13072
13073
13074
13075
13076
13077
13078
13079
13080
13081
13082
13083
13084
13085
  void *(*xGetAuxdata)(Fts5Context*, int bClear);

  int (*xPhraseFirst)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*, int*);
  void (*xPhraseNext)(Fts5Context*, Fts5PhraseIter*, int *piCol, int *piOff);

  int (*xPhraseFirstColumn)(Fts5Context*, int iPhrase, Fts5PhraseIter*, int*);
  void (*xPhraseNextColumn)(Fts5Context*, Fts5PhraseIter*, int *piCol);

  /* Below this point are iVersion>=3 only */
  int (*xQueryToken)(Fts5Context*,
      int iPhrase, int iToken,
      const char **ppToken, int *pnToken
  );
  int (*xInstToken)(Fts5Context*, int iIdx, int iToken, const char**, int*);
};

/*
** CUSTOM AUXILIARY FUNCTIONS
*************************************************************************/

/*************************************************************************
Changes to skins/default/css.txt.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/* Overall page style */

body {
  margin: 0 auto;
  background-color: white;
  font-family: sans-serif;
  font-size: 14pt;
     -moz-text-size-adjust: none;
      -mx-text-size-adjust: none;
  -webkit-text-size-adjust: none;
}

a {
  color: #4183C4;
  text-decoration: none;
}
a:hover {







<
<
<







1
2
3
4
5
6
7



8
9
10
11
12
13
14
/* Overall page style */

body {
  margin: 0 auto;
  background-color: white;
  font-family: sans-serif;
  font-size: 14pt;



}

a {
  color: #4183C4;
  text-decoration: none;
}
a:hover {
Changes to src/ajax.c.
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
*/
void ajax_route_dispatcher(void){
  const char * zName = P("name");
  AjaxRoute routeName = {0,0,0,0};
  const AjaxRoute * pRoute = 0;
  const AjaxRoute routes[] = {
  /* Keep these sorted by zName (for bsearch()) */
  {"preview-text", ajax_route_preview_text, 1, 1}









  };

  if(zName==0 || zName[0]==0){
    ajax_route_error(400,"Missing required [route] 'name' parameter.");
    return;
  }
  routeName.zName = zName;
  pRoute = (const AjaxRoute *)bsearch(&routeName, routes,
                                      count(routes), sizeof routes[0],
                                      cmp_ajax_route_name);
  if(pRoute==0){
    ajax_route_error(404,"Ajax route not found.");
    return;
  }else if(0==ajax_route_bootstrap(pRoute->bWriteMode, pRoute->bPost)){
    return;
  }
  pRoute->xCallback();  
}







|
>
>
>
>
>
>
>
>
>
















|

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
*/
void ajax_route_dispatcher(void){
  const char * zName = P("name");
  AjaxRoute routeName = {0,0,0,0};
  const AjaxRoute * pRoute = 0;
  const AjaxRoute routes[] = {
  /* Keep these sorted by zName (for bsearch()) */
  {"preview-text", ajax_route_preview_text, 0, 1
   /* Note that this does not require write permissions in the repo.
   ** It should arguably require write permissions but doing means
   ** that /chat does not work without checkin permissions:
   **
   ** https://fossil-scm.org/forum/forumpost/ed4a762b3a557898
   **
   ** This particular route is used by /fileedit and /chat, whereas
   ** /wikiedit uses a simpler wiki-specific route.
   */ }
  };

  if(zName==0 || zName[0]==0){
    ajax_route_error(400,"Missing required [route] 'name' parameter.");
    return;
  }
  routeName.zName = zName;
  pRoute = (const AjaxRoute *)bsearch(&routeName, routes,
                                      count(routes), sizeof routes[0],
                                      cmp_ajax_route_name);
  if(pRoute==0){
    ajax_route_error(404,"Ajax route not found.");
    return;
  }else if(0==ajax_route_bootstrap(pRoute->bWriteMode, pRoute->bPost)){
    return;
  }
  pRoute->xCallback();
}
Changes to src/alerts.c.
45
46
47
48
49
50
51

52
53
54
55
56
57
58
@ -- to the USER entry.
@ --
@ -- The ssub field is a string where each character indicates a particular
@ -- type of event to subscribe to.  Choices:
@ --     a - Announcements
@ --     c - Check-ins
@ --     f - Forum posts

@ --     n - New forum threads
@ --     r - Replies to my own forum posts
@ --     t - Ticket changes
@ --     w - Wiki changes
@ --     x - Edits to forum posts
@ -- Probably different codes will be added in the future.  In the future
@ -- we might also add a separate table that allows subscribing to email







>







45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
@ -- to the USER entry.
@ --
@ -- The ssub field is a string where each character indicates a particular
@ -- type of event to subscribe to.  Choices:
@ --     a - Announcements
@ --     c - Check-ins
@ --     f - Forum posts
@ --     k - ** Special: Unsubscribed using /oneclickunsub
@ --     n - New forum threads
@ --     r - Replies to my own forum posts
@ --     t - Ticket changes
@ --     w - Wiki changes
@ --     x - Edits to forum posts
@ -- Probably different codes will be added in the future.  In the future
@ -- we might also add a separate table that allows subscribing to email
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
@ --
@ CREATE TABLE repository.pending_alert(
@   eventid TEXT PRIMARY KEY,         -- Object that changed
@   sentSep BOOLEAN DEFAULT false,    -- individual alert sent
@   sentDigest BOOLEAN DEFAULT false, -- digest alert sent
@   sentMod BOOLEAN DEFAULT false     -- pending moderation alert sent
@ ) WITHOUT ROWID;
@ 
@ -- Obsolete table.  No longer used.
@ DROP TABLE IF EXISTS repository.alert_bounce;
;

/*
** Return true if the email notification tables exist.
*/







|







85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
@ --
@ CREATE TABLE repository.pending_alert(
@   eventid TEXT PRIMARY KEY,         -- Object that changed
@   sentSep BOOLEAN DEFAULT false,    -- individual alert sent
@   sentDigest BOOLEAN DEFAULT false, -- digest alert sent
@   sentMod BOOLEAN DEFAULT false     -- pending moderation alert sent
@ ) WITHOUT ROWID;
@
@ -- Obsolete table.  No longer used.
@ DROP TABLE IF EXISTS repository.alert_bounce;
;

/*
** Return true if the email notification tables exist.
*/
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
*/
void email_header_to(Blob *pMsg, int *pnTo, char ***pazTo){
  int nTo = 0;
  char **azTo = 0;
  Blob v;
  char *z, *zAddr;
  int i;
  
  email_header_value(pMsg, "to", &v);
  z = blob_str(&v);
  for(i=0; z[i]; i++){
    if( z[i]=='<' && (zAddr = email_copy_addr(&z[i+1],'>'))!=0 ){
      azTo = fossil_realloc(azTo, sizeof(azTo[0])*(nTo+1) );
      azTo[nTo++] = zAddr;
    }
  }
  *pnTo = nTo;
  *pazTo = azTo;
}

/*
** Free a list of To addresses obtained from a prior call to 
** email_header_to()
*/
void email_header_to_free(int nTo, char **azTo){
  int i;
  for(i=0; i<nTo; i++) fossil_free(azTo[i]);
  fossil_free(azTo);
}







|













|







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
*/
void email_header_to(Blob *pMsg, int *pnTo, char ***pazTo){
  int nTo = 0;
  char **azTo = 0;
  Blob v;
  char *z, *zAddr;
  int i;

  email_header_value(pMsg, "to", &v);
  z = blob_str(&v);
  for(i=0; z[i]; i++){
    if( z[i]=='<' && (zAddr = email_copy_addr(&z[i+1],'>'))!=0 ){
      azTo = fossil_realloc(azTo, sizeof(azTo[0])*(nTo+1) );
      azTo[nTo++] = zAddr;
    }
  }
  *pnTo = nTo;
  *pazTo = azTo;
}

/*
** Free a list of To addresses obtained from a prior call to
** email_header_to()
*/
void email_header_to_free(int nTo, char **azTo){
  int i;
  for(i=0; i<nTo; i++) fossil_free(azTo[i]);
  fossil_free(azTo);
}
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
**     From:
**     Date:
**     Message-Id:
**     Content-Type:
**     Content-Transfer-Encoding:
**     MIME-Version:
**     Sender:
**     
** The caller maintains ownership of the input Blobs.  This routine will
** read the Blobs and send them onward to the email system, but it will
** not free them.
**
** The Message-Id: field is added if there is not already a Message-Id
** in the pHdr parameter.
**
** If the zFromName argument is not NULL, then it should be a human-readable
** name or handle for the sender.  In that case, "From:" becomes a made-up
** email address based on a hash of zFromName and the domain of email-self,
** and an additional "Sender:" field is inserted with the email-self
** address.  Downstream software might use the Sender header to set
** the envelope-from address of the email.  If zFromName is a NULL pointer, 
** then the "From:" is set to the email-self value and Sender is
** omitted.
*/
void alert_send(
  AlertSender *p,           /* Emailer context */
  Blob *pHdr,               /* Email header (incomplete) */
  Blob *pBody,              /* Email body */







|












|







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
**     From:
**     Date:
**     Message-Id:
**     Content-Type:
**     Content-Transfer-Encoding:
**     MIME-Version:
**     Sender:
**
** The caller maintains ownership of the input Blobs.  This routine will
** read the Blobs and send them onward to the email system, but it will
** not free them.
**
** The Message-Id: field is added if there is not already a Message-Id
** in the pHdr parameter.
**
** If the zFromName argument is not NULL, then it should be a human-readable
** name or handle for the sender.  In that case, "From:" becomes a made-up
** email address based on a hash of zFromName and the domain of email-self,
** and an additional "Sender:" field is inserted with the email-self
** address.  Downstream software might use the Sender header to set
** the envelope-from address of the email.  If zFromName is a NULL pointer,
** then the "From:" is set to the email-self value and Sender is
** omitted.
*/
void alert_send(
  AlertSender *p,           /* Emailer context */
  Blob *pHdr,               /* Email header (incomplete) */
  Blob *pBody,              /* Email body */
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
** the basename for hyperlinks included in email alert text.
** Omit the trailing "/".  If the repository is not intended to be
** a long-running server and will not be sending email notifications,
** then leave this setting blank.
*/
/*
** SETTING: email-admin               width=40
** This is the email address for the human administrator for the system. 
** Abuse and trouble reports and password reset requests are send here.
*/
/*
** SETTING: email-subname             width=16
** This is a short name used to identifies the repository in the Subject:
** line of email alerts. Traditionally this name is included in square
** brackets. Examples: "[fossil-src]", "[sqlite-src]".







|







1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
** the basename for hyperlinks included in email alert text.
** Omit the trailing "/".  If the repository is not intended to be
** a long-running server and will not be sending email notifications,
** then leave this setting blank.
*/
/*
** SETTING: email-admin               width=40
** This is the email address for the human administrator for the system.
** Abuse and trouble reports and password reset requests are send here.
*/
/*
** SETTING: email-subname             width=16
** This is a short name used to identifies the repository in the Subject:
** line of email alerts. Traditionally this name is included in square
** brackets. Examples: "[fossil-src]", "[sqlite-src]".
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
** a subscription is less than email-renew-cutoff, then now new emails
** are sent to the subscriber.
**
** email-renew-warning is the time (in days since 1970-01-01) when the
** last batch of "your subscription is about to expire" emails were
** sent out.
**
** email-renew-cutoff is normally 7 days behind email-renew-warning.  
*/
/*
** SETTING: email-send-method         width=5 default=off sensitive
** Determine the method used to send email.  Allowed values are
** "off", "relay", "pipe", "dir", "db", and "stdout".  The "off" value
** means no email is ever sent.  The "relay" value means emails are sent
** to an Mail Sending Agent using SMTP located at email-send-relayhost.
** The "pipe" value means email messages are piped into a command 
** determined by the email-send-command setting. The "dir" value means
** emails are written to individual files in a directory determined
** by the email-send-dir setting.  The "db" value means that emails
** are added to an SQLite database named by the* email-send-db setting.
** The "stdout" value writes email text to standard output, for debugging.
*/
/*







|







|







1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
** a subscription is less than email-renew-cutoff, then now new emails
** are sent to the subscriber.
**
** email-renew-warning is the time (in days since 1970-01-01) when the
** last batch of "your subscription is about to expire" emails were
** sent out.
**
** email-renew-cutoff is normally 7 days behind email-renew-warning.
*/
/*
** SETTING: email-send-method         width=5 default=off sensitive
** Determine the method used to send email.  Allowed values are
** "off", "relay", "pipe", "dir", "db", and "stdout".  The "off" value
** means no email is ever sent.  The "relay" value means emails are sent
** to an Mail Sending Agent using SMTP located at email-send-relayhost.
** The "pipe" value means email messages are piped into a command
** determined by the email-send-command setting. The "dir" value means
** emails are written to individual files in a directory determined
** by the email-send-dir setting.  The "db" value means that emails
** are added to an SQLite database named by the* email-send-db setting.
** The "stdout" value writes email text to standard output, for debugging.
*/
/*
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
** SMTP server configured as a Mail Submission Agent listening on the
** designated host and port and all times.
*/


/*
** COMMAND: alerts*
** 
** Usage: %fossil alerts SUBCOMMAND ARGS...
**
** Subcommands:
**
**    pending                 Show all pending alerts.  Useful for debugging.
**
**    reset                   Hard reset of all email notification tables







|







1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
** SMTP server configured as a Mail Submission Agent listening on the
** designated host and port and all times.
*/


/*
** COMMAND: alerts*
**
** Usage: %fossil alerts SUBCOMMAND ARGS...
**
** Subcommands:
**
**    pending                 Show all pending alerts.  Useful for debugging.
**
**    reset                   Hard reset of all email notification tables
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
  /* If we reach this point, all is well */
  return 1;
}

/*
** Text of email message sent in order to confirm a subscription.
*/
static const char zConfirmMsg[] = 
@ Someone has signed you up for email alerts on the Fossil repository
@ at %s.
@
@ To confirm your subscription and begin receiving alerts, click on
@ the following hyperlink:
@
@    %s/alerts/%s







|







1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
  /* If we reach this point, all is well */
  return 1;
}

/*
** Text of email message sent in order to confirm a subscription.
*/
static const char zConfirmMsg[] =
@ Someone has signed you up for email alerts on the Fossil repository
@ at %s.
@
@ To confirm your subscription and begin receiving alerts, click on
@ the following hyperlink:
@
@    %s/alerts/%s
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
}

/*
** Either shutdown or completely delete a subscription entry given
** by the hex value zName.  Then paint a webpage that explains that
** the entry has been removed.
*/
static void alert_unsubscribe(int sid){
  const char *zEmail = 0;
  const char *zLogin = 0;
  int uid = 0;
  Stmt q;
  db_prepare(&q, "SELECT semail, suname FROM subscriber"
                 " WHERE subscriberId=%d", sid);
  if( db_step(&q)==SQLITE_ROW ){
    zEmail = db_column_text(&q, 0);
    zLogin = db_column_text(&q, 1);
    uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin);
  }
  style_set_current_feature("alerts");
  if( zEmail==0 ){
    style_header("Unsubscribe Fail");
    @ <p>Unable to locate a subscriber with the requested key</p>
  }else{
    


    db_multi_exec(
      "DELETE FROM subscriber WHERE subscriberId=%d", sid
    );








    style_header("Unsubscribed");
    @ <p>The "%h(zEmail)" email address has been unsubscribed from all
    @ notifications.  All subscription records for "%h(zEmail)" have
    @ been purged.  No further emails will be sent to "%h(zEmail)".</p>
    if( uid && g.perm.Admin ){
       @ <p>You may also want to
       @ <a href="%R/setup_uedit?id=%d(uid)">edit or delete







|
















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







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
}

/*
** Either shutdown or completely delete a subscription entry given
** by the hex value zName.  Then paint a webpage that explains that
** the entry has been removed.
*/
static void alert_unsubscribe(int sid, int bTotal){
  const char *zEmail = 0;
  const char *zLogin = 0;
  int uid = 0;
  Stmt q;
  db_prepare(&q, "SELECT semail, suname FROM subscriber"
                 " WHERE subscriberId=%d", sid);
  if( db_step(&q)==SQLITE_ROW ){
    zEmail = db_column_text(&q, 0);
    zLogin = db_column_text(&q, 1);
    uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin);
  }
  style_set_current_feature("alerts");
  if( zEmail==0 ){
    style_header("Unsubscribe Fail");
    @ <p>Unable to locate a subscriber with the requested key</p>
  }else{
    db_unprotect(PROTECT_READONLY);
    if( bTotal ){
      /* Completely delete the subscriber */
      db_multi_exec(
        "DELETE FROM subscriber WHERE subscriberId=%d", sid
      );
    }else{
      /* Keep the subscriber, but turn off all notifications */
      db_multi_exec(
        "UPDATE subscriber SET ssub='k', mtime=now() WHERE subscriberId=%d",
        sid
      );
    }
    db_protect_pop();
    style_header("Unsubscribed");
    @ <p>The "%h(zEmail)" email address has been unsubscribed from all
    @ notifications.  All subscription records for "%h(zEmail)" have
    @ been purged.  No further emails will be sent to "%h(zEmail)".</p>
    if( uid && g.perm.Admin ){
       @ <p>You may also want to
       @ <a href="%R/setup_uedit?id=%d(uid)">edit or delete
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
**         email and clicks on the link in the email.  When a
**         compilete subscriberCode is seen on the name= query parameter,
**         that constitutes verification of the email address.
**
**    *    The sid= query parameter contains an integer subscriberId.
**         This only works for the administrator.  It allows the
**         administrator to edit any subscription.
**         
**    *    The user is logged into an account other than "nobody" or
**         "anonymous".  In that case the notification settings
**         associated with that account can be edited without needing
**         to know the subscriber code.
**
**    *    The name= query parameter contains a 32-digit prefix of
**         subscriber code.  (Subscriber codes are normally 64 hex digits







|







1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
**         email and clicks on the link in the email.  When a
**         compilete subscriberCode is seen on the name= query parameter,
**         that constitutes verification of the email address.
**
**    *    The sid= query parameter contains an integer subscriberId.
**         This only works for the administrator.  It allows the
**         administrator to edit any subscription.
**
**    *    The user is logged into an account other than "nobody" or
**         "anonymous".  In that case the notification settings
**         associated with that account can be edited without needing
**         to know the subscriber code.
**
**    *    The name= query parameter contains a 32-digit prefix of
**         subscriber code.  (Subscriber codes are normally 64 hex digits
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
  }
  if( P("delete")!=0 && cgi_csrf_safe(2) ){
    if( !PB("dodelete") ){
      eErr = 9;
      zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to"
                     " unsubscribe");
    }else{
      alert_unsubscribe(sid);
      db_commit_transaction();
      return; 
    }
  }
  style_set_current_feature("alerts");
  style_header("Update Subscription");
  db_prepare(&q,
    "SELECT"
    "  semail,"                       /* 0 */







|

|







1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
  }
  if( P("delete")!=0 && cgi_csrf_safe(2) ){
    if( !PB("dodelete") ){
      eErr = 9;
      zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to"
                     " unsubscribe");
    }else{
      alert_unsubscribe(sid, 1);
      db_commit_transaction();
      return;
    }
  }
  style_set_current_feature("alerts");
  style_header("Update Subscription");
  db_prepare(&q,
    "SELECT"
    "  semail,"                       /* 0 */
2087
2088
2089
2090
2091
2092
2093




2094
2095
2096
2097
2098
2099
2100
    @  Ticket changes</label><br>
  }
  if( g.perm.RdWiki ){
    @  <label><input type="checkbox" name="sw" %s(sw?"checked":"")>\
    @  Wiki</label>
  }
  @ </td></tr>




  @ <tr>
  @  <td class="form_label">Delivery:</td>
  @  <td><select size="1" name="sdigest">
  @     <option value="0" %s(sdigest?"":"selected")>Individual Emails</option>
  @     <option value="1" %s(sdigest?"selected":"")>Daily Digest</option>
  @     </select></td>
  @ </tr>







>
>
>
>







2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
    @  Ticket changes</label><br>
  }
  if( g.perm.RdWiki ){
    @  <label><input type="checkbox" name="sw" %s(sw?"checked":"")>\
    @  Wiki</label>
  }
  @ </td></tr>
  if( strchr(ssub,'k')!=0 ){
    @ <tr><td></td><td>&nbsp;&uarr;&nbsp;
    @ Note: User did a one-click unsubscribe</td></tr>
  }
  @ <tr>
  @  <td class="form_label">Delivery:</td>
  @  <td><select size="1" name="sdigest">
  @     <option value="0" %s(sdigest?"":"selected")>Individual Emails</option>
  @     <option value="1" %s(sdigest?"selected":"")>Daily Digest</option>
  @     </select></td>
  @ </tr>
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
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
  style_finish_page();
}


/* This is the message that gets sent to describe how to change
** or modify a subscription
*/
static const char zUnsubMsg[] = 
@ To changes your subscription settings at %s visit this link:
@
@    %s/alerts/%s
@
@ To completely unsubscribe from %s, visit the following link:
@
@    %s/unsubscribe/%s
;

/*
** WEBPAGE: unsubscribe

**
** Users visit this page to be delisted from email alerts.
**
** If a valid subscriber code is supplied in the name= query parameter,
** then that subscriber is delisted.
**
** Otherwise, If the users is logged in, then they are redirected
** to the /alerts page where they have an unsubscribe button.
**
** Non-logged-in users with no name= query parameter are invited to enter
** an email address to which will be sent the unsubscribe link that
** contains the correct subscriber code.



*/
void unsubscribe_page(void){
  const char *zName = P("name");
  char *zErr = 0;
  int eErr = 0;
  unsigned int uSeed = 0;
  const char *zDecoded;
  char *zCaptcha = 0;
  int dx;
  int bSubmit;
  const char *zEAddr;
  char *zCode = 0;
  int sid = 0;

  if( zName==0 ) zName = P("scode");

  /* If a valid subscriber code is supplied, then either present the user
  ** with a comformation, or if already confirmed, unsubscribe immediately.
  */
  if( zName 
   && (sid = db_int(0, "SELECT subscriberId FROM subscriber"
                       " WHERE subscriberCode=hextoblob(%Q)", zName))!=0
  ){
    char *zUnsubName = mprintf("confirm%04x", sid);
    if( P(zUnsubName)!=0 ){
      alert_unsubscribe(sid);


    }else if( P("manage")!=0 ){
      cgi_redirectf("%R/alerts/%s", zName);
    }else{
      style_header("Unsubscribe");
      form_begin(0, "%R/unsubscribe");
      @ <input type="hidden" name="scode" value="%h(zName)">
      @ <table border="0" cellpadding="10" width="100%%">







|











>












>
>
>

















|

|





|
>
>







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


/* This is the message that gets sent to describe how to change
** or modify a subscription
*/
static const char zUnsubMsg[] =
@ To changes your subscription settings at %s visit this link:
@
@    %s/alerts/%s
@
@ To completely unsubscribe from %s, visit the following link:
@
@    %s/unsubscribe/%s
;

/*
** WEBPAGE: unsubscribe
** WEBPAGE: oneclickunsub
**
** Users visit this page to be delisted from email alerts.
**
** If a valid subscriber code is supplied in the name= query parameter,
** then that subscriber is delisted.
**
** Otherwise, If the users is logged in, then they are redirected
** to the /alerts page where they have an unsubscribe button.
**
** Non-logged-in users with no name= query parameter are invited to enter
** an email address to which will be sent the unsubscribe link that
** contains the correct subscriber code.
**
** The /unsubscribe page requires comfirmation.  The /oneclickunsub
** page unsubscribes immediately without any need to confirm.
*/
void unsubscribe_page(void){
  const char *zName = P("name");
  char *zErr = 0;
  int eErr = 0;
  unsigned int uSeed = 0;
  const char *zDecoded;
  char *zCaptcha = 0;
  int dx;
  int bSubmit;
  const char *zEAddr;
  char *zCode = 0;
  int sid = 0;

  if( zName==0 ) zName = P("scode");

  /* If a valid subscriber code is supplied, then either present the user
  ** with a confirmation, or if already confirmed, unsubscribe immediately.
  */
  if( zName
   && (sid = db_int(0, "SELECT subscriberId FROM subscriber"
                       " WHERE subscriberCode=hextoblob(%Q)", zName))!=0
  ){
    char *zUnsubName = mprintf("confirm%04x", sid);
    if( P(zUnsubName)!=0 ){
      alert_unsubscribe(sid, 1);
    }else if( sqlite3_strglob("*oneclick*",g.zPath)==0 ){
      alert_unsubscribe(sid, 0);
    }else if( P("manage")!=0 ){
      cgi_redirectf("%R/alerts/%s", zName);
    }else{
      style_header("Unsubscribe");
      form_begin(0, "%R/unsubscribe");
      @ <input type="hidden" name="scode" value="%h(zName)">
      @ <table border="0" cellpadding="10" width="100%%">
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
    }else{
      @ <p>An email has been sent to "%h(zEAddr)" that explains how to
      @ unsubscribe and/or modify your subscription settings</p>
    }
    alert_sender_free(pSender);
    style_finish_page();
    return;
  }  

  /* Non-logged-in users have to enter an email address to which is
  ** sent a message containing the unsubscribe link.
  */
  style_header("Unsubscribe Request");
  @ <p>Fill out the form below to request an email message that will
  @ explain how to unsubscribe and/or change your subscription settings.</p>







|







2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
    }else{
      @ <p>An email has been sent to "%h(zEAddr)" that explains how to
      @ unsubscribe and/or modify your subscription settings</p>
    }
    alert_sender_free(pSender);
    style_finish_page();
    return;
  }

  /* Non-logged-in users have to enter an email address to which is
  ** sent a message containing the unsubscribe link.
  */
  style_header("Unsubscribe Request");
  @ <p>Fill out the form below to request an email message that will
  @ explain how to unsubscribe and/or change your subscription settings.</p>
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
}

/*
** Compute a string that is appropriate for the EmailEvent.zPriors field
** for a particular forum post.
**
** This string is an encode list of sender names and rids for all ancestors
** of the fpdi post - the post that fpid answer, the post that that parent 
** post answers, and so forth back up to the root post. Duplicates sender
** names are omitted.
**
** The EmailEvent.zPriors field is used to screen events for people who
** only want to see replies to their own posts or to specific posts.
*/
static char *alert_compute_priors(int fpid){







|







2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
}

/*
** Compute a string that is appropriate for the EmailEvent.zPriors field
** for a particular forum post.
**
** This string is an encode list of sender names and rids for all ancestors
** of the fpdi post - the post that fpid answer, the post that that parent
** post answers, and so forth back up to the root post. Duplicates sender
** names are omitted.
**
** The EmailEvent.zPriors field is used to screen events for people who
** only want to see replies to their own posts or to specific posts.
*/
static char *alert_compute_priors(int fpid){
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
    zUuid = db_column_text(&q, 1);
    zTitle = db_column_text(&q, 3);
    if( p->needMod ){
      blob_appendf(&p->hdr, "Subject: %s Pending Moderation: %s\r\n",
                   zSub, zTitle);
    }else{
      blob_appendf(&p->hdr, "Subject: %s %s\r\n", zSub, zTitle);
      blob_appendf(&p->hdr, "Message-Id: <%.32s@%s>\r\n", 
                   zUuid, alert_hostname(zFrom));
      zIrt = db_column_text(&q, 4);
      if( zIrt && zIrt[0] ){
        blob_appendf(&p->hdr, "In-Reply-To: <%.32s@%s>\r\n",
                     zIrt, alert_hostname(zFrom));
      }
    }







|







2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
    zUuid = db_column_text(&q, 1);
    zTitle = db_column_text(&q, 3);
    if( p->needMod ){
      blob_appendf(&p->hdr, "Subject: %s Pending Moderation: %s\r\n",
                   zSub, zTitle);
    }else{
      blob_appendf(&p->hdr, "Subject: %s %s\r\n", zSub, zTitle);
      blob_appendf(&p->hdr, "Message-Id: <%.32s@%s>\r\n",
                   zUuid, alert_hostname(zFrom));
      zIrt = db_column_text(&q, 4);
      if( zIrt && zIrt[0] ){
        blob_appendf(&p->hdr, "In-Reply-To: <%.32s@%s>\r\n",
                     zIrt, alert_hostname(zFrom));
      }
    }
3087
3088
3089
3090
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102

3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
     " ssub,"                 /* 2 */
     " fullcap(user.cap),"    /* 3 */
     " suname"                /* 4 */
     " FROM subscriber LEFT JOIN user ON (login=suname)"
     " WHERE sverified"
     "   AND NOT sdonotcall"
     "   AND sdigest IS %s"
     "   AND coalesce(subscriber.lastContact,subscriber.mtime)>=%d",
     zDigest/*safe-for-%s*/,
     db_get_int("email-renew-cutoff",0)
  );
  while( db_step(&q)==SQLITE_ROW ){
    const char *zCode = db_column_text(&q, 0);
    const char *zSub = db_column_text(&q, 2);
    const char *zEmail = db_column_text(&q, 1);
    const char *zCap = db_column_text(&q, 3);

    int nHit = 0;
    for(p=pEvents; p; p=p->pNext){
      if( strchr(zSub,p->type)==0 ){
        if( p->type!='f' ) continue;
        if( strchr(zSub,'n')!=0 && (p->zPriors==0 || p->zPriors[0]==0) ){
          /* New post: accepted */
        }else if( strchr(zSub,'r')!=0
               && alert_in_priors(db_column_text(&q,4), p->zPriors) ){
          /* A follow-up to a post written by the user: accept */
        }else{
          continue;
        }
      }
      if( p->needMod ){
        /* For events that require moderator approval, only send an alert







|








>






|
|







3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
3122
3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
     " ssub,"                 /* 2 */
     " fullcap(user.cap),"    /* 3 */
     " suname"                /* 4 */
     " FROM subscriber LEFT JOIN user ON (login=suname)"
     " WHERE sverified"
     "   AND NOT sdonotcall"
     "   AND sdigest IS %s"
     "   AND coalesce(subscriber.lastContact*86400,subscriber.mtime)>=%d",
     zDigest/*safe-for-%s*/,
     db_get_int("email-renew-cutoff",0)
  );
  while( db_step(&q)==SQLITE_ROW ){
    const char *zCode = db_column_text(&q, 0);
    const char *zSub = db_column_text(&q, 2);
    const char *zEmail = db_column_text(&q, 1);
    const char *zCap = db_column_text(&q, 3);
    const char *zUser = db_column_text(&q, 4);
    int nHit = 0;
    for(p=pEvents; p; p=p->pNext){
      if( strchr(zSub,p->type)==0 ){
        if( p->type!='f' ) continue;
        if( strchr(zSub,'n')!=0 && (p->zPriors==0 || p->zPriors[0]==0) ){
          /* New post: accepted */
        }else if( strchr(zSub,'r')!=0 && zUser!=0
               && alert_in_priors(zUser, p->zPriors) ){
          /* A follow-up to a post written by the user: accept */
        }else{
          continue;
        }
      }
      if( p->needMod ){
        /* For events that require moderator approval, only send an alert
3146
3147
3148
3149
3150
3151
3152




3153
3154
3155
3156
3157
3158
3159
      if( blob_size(&p->hdr)>0 ){
        /* This alert should be sent as a separate email */
        Blob fhdr, fbody;
        blob_init(&fhdr, 0, 0);
        blob_appendf(&fhdr, "To: <%s>\r\n", zEmail);
        blob_append(&fhdr, blob_buffer(&p->hdr), blob_size(&p->hdr));
        blob_init(&fbody, blob_buffer(&p->txt), blob_size(&p->txt));




        blob_appendf(&fbody, "\n-- \nUnsubscribe: %s/unsubscribe/%s\n",
           zUrl, zCode);
        /* blob_appendf(&fbody, "Subscription settings: %s/alerts/%s\n",
        **   zUrl, zCode); */
        alert_send(pSender,&fhdr,&fbody,p->zFromName);
        nSent++;
        blob_reset(&fhdr);







>
>
>
>







3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
      if( blob_size(&p->hdr)>0 ){
        /* This alert should be sent as a separate email */
        Blob fhdr, fbody;
        blob_init(&fhdr, 0, 0);
        blob_appendf(&fhdr, "To: <%s>\r\n", zEmail);
        blob_append(&fhdr, blob_buffer(&p->hdr), blob_size(&p->hdr));
        blob_init(&fbody, blob_buffer(&p->txt), blob_size(&p->txt));
        blob_appendf(&fhdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n",
                     zUrl, zCode);
        blob_appendf(&fhdr,
                   "List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
        blob_appendf(&fbody, "\n-- \nUnsubscribe: %s/unsubscribe/%s\n",
           zUrl, zCode);
        /* blob_appendf(&fbody, "Subscription settings: %s/alerts/%s\n",
        **   zUrl, zCode); */
        alert_send(pSender,&fhdr,&fbody,p->zFromName);
        nSent++;
        blob_reset(&fhdr);
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
        }
        nHit++;
        blob_append(&body, "\n", 1);
        blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt));
      }
    }
    if( nHit==0 ) continue;
    blob_appendf(&hdr, "List-Unsubscribe: <%s/unsubscribe/%s>\r\n",
         zUrl, zCode);
    blob_appendf(&hdr, "List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
    blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n",
         zUrl, zCode);
    alert_send(pSender,&hdr,&body,0);
    nSent++;
    blob_truncate(&hdr, 0);







|







3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
        }
        nHit++;
        blob_append(&body, "\n", 1);
        blob_append(&body, blob_buffer(&p->txt), blob_size(&p->txt));
      }
    }
    if( nHit==0 ) continue;
    blob_appendf(&hdr, "List-Unsubscribe: <%s/oneclickunsub/%s>\r\n",
         zUrl, zCode);
    blob_appendf(&hdr, "List-Unsubscribe-Post: List-Unsubscribe=One-Click\r\n");
    blob_appendf(&body,"\n-- \nSubscription info: %s/alerts/%s\n",
         zUrl, zCode);
    alert_send(pSender,&hdr,&body,0);
    nSent++;
    blob_truncate(&hdr, 0);
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
  ** alerts that have been completely sent.
  */
  db_multi_exec("DELETE FROM pending_alert WHERE sentDigest AND sentSep;");

  /* Send renewal messages to subscribers whose subscriptions are about
  ** to expire.  Only do this if:
  **
  **  (1)  email-renew-interval is 14 or greater (or in other words if 
  **       subscription expiration is enabled).
  **
  **  (2)  The SENDALERT_RENEWAL flag is set
  */
send_alert_expiration_warnings:
  if( (flags & SENDALERT_RENEWAL)!=0
   && (iInterval = db_get_int("email-renew-interval",0))>=14







|







3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
  ** alerts that have been completely sent.
  */
  db_multi_exec("DELETE FROM pending_alert WHERE sentDigest AND sentSep;");

  /* Send renewal messages to subscribers whose subscriptions are about
  ** to expire.  Only do this if:
  **
  **  (1)  email-renew-interval is 14 or greater (or in other words if
  **       subscription expiration is enabled).
  **
  **  (2)  The SENDALERT_RENEWAL flag is set
  */
send_alert_expiration_warnings:
  if( (flags & SENDALERT_RENEWAL)!=0
   && (iInterval = db_get_int("email-renew-interval",0))>=14
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
         "   AND length(sdigest)>0",
         iNewWarn, iOldWarn
      );
      while( db_step(&q)==SQLITE_ROW ){
        Blob hdr, body;
        blob_init(&hdr, 0, 0);
        blob_init(&body, 0, 0);
        alert_renewal_msg(&hdr, &body, 
           db_column_text(&q,0),
           db_column_int(&q,1),
           db_column_text(&q,2),
           db_column_text(&q,3),
           zRepoName, zUrl);
        alert_send(pSender,&hdr,&body,0);
        blob_reset(&hdr);







|







3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
         "   AND length(sdigest)>0",
         iNewWarn, iOldWarn
      );
      while( db_step(&q)==SQLITE_ROW ){
        Blob hdr, body;
        blob_init(&hdr, 0, 0);
        blob_init(&body, 0, 0);
        alert_renewal_msg(&hdr, &body,
           db_column_text(&q,0),
           db_column_int(&q,1),
           db_column_text(&q,2),
           db_column_text(&q,3),
           zRepoName, zUrl);
        alert_send(pSender,&hdr,&body,0);
        blob_reset(&hdr);
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
  style_set_current_feature("alerts");
  if( zAdminEmail==0 || zAdminEmail[0]==0 ){
    style_header("Outbound Email Disabled");
    @ <p>Outbound email is disabled on this repository
    style_finish_page();
    return;
  }
  if( P("submit")!=0 
   && P("subject")!=0
   && P("msg")!=0
   && P("from")!=0
   && cgi_csrf_safe(2)
   && captcha_is_correct(0)
  ){
    Blob hdr, body;







|







3320
3321
3322
3323
3324
3325
3326
3327
3328
3329
3330
3331
3332
3333
3334
  style_set_current_feature("alerts");
  if( zAdminEmail==0 || zAdminEmail[0]==0 ){
    style_header("Outbound Email Disabled");
    @ <p>Outbound email is disabled on this repository
    style_finish_page();
    return;
  }
  if( P("submit")!=0
   && P("subject")!=0
   && P("msg")!=0
   && P("from")!=0
   && cgi_csrf_safe(2)
   && captcha_is_correct(0)
  ){
    Blob hdr, body;
Changes to src/allrepo.c.
29
30
31
32
33
34
35
36


37
38
39
40
41
42
43
44
*/
static void collect_argument(Blob *pExtra,const char *zArg,const char *zShort){
  const char *z = find_option(zArg, zShort, 0);
  if( z!=0 ){
    blob_appendf(pExtra, " %s", z);
  }
}
static void collect_argument_value(Blob *pExtra, const char *zArg){


  const char *zValue = find_option(zArg, 0, 1);
  if( zValue ){
    if( zValue[0] ){
      blob_appendf(pExtra, " --%s %$", zArg, zValue);
    }else{
      blob_appendf(pExtra, " --%s \"\"", zArg);
    }
  }







|
>
>
|







29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
*/
static void collect_argument(Blob *pExtra,const char *zArg,const char *zShort){
  const char *z = find_option(zArg, zShort, 0);
  if( z!=0 ){
    blob_appendf(pExtra, " %s", z);
  }
}
static void collect_argument_value(
    Blob *pExtra, const char *zArg, const char *zShort
){
  const char *zValue = find_option(zArg, zShort, 1);
  if( zValue ){
    if( zValue[0] ){
      blob_appendf(pExtra, " --%s %$", zArg, zValue);
    }else{
      blob_appendf(pExtra, " --%s \"\"", zArg);
    }
  }
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
**
**    ui          Run the "ui" command on all repositories.  Like "server"
**                but bind to the loopback TCP address only, enable
**                the --localauth option and automatically launch a
**                web-browser
**
**    whatis      Run the "whatis" command on all repositories.  Only
**                show output for repositories that have a match.           
**
**
** In addition, the following maintenance operations are supported:
**
**    add         Add all the repositories named to the set of repositories
**                tracked by Fossil.  Normally Fossil is able to keep up with
**                this list by itself, but sometimes it can benefit from this







|







132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
**
**    ui          Run the "ui" command on all repositories.  Like "server"
**                but bind to the loopback TCP address only, enable
**                the --localauth option and automatically launch a
**                web-browser
**
**    whatis      Run the "whatis" command on all repositories.  Only
**                show output for repositories that have a match.
**
**
** In addition, the following maintenance operations are supported:
**
**    add         Add all the repositories named to the set of repositories
**                tracked by Fossil.  Normally Fossil is able to keep up with
**                this list by itself, but sometimes it can benefit from this
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
    if( file_isdir(zDest, ExtFILE)!=1 ){
      fossil_fatal("argument to \"fossil all backup\" must be a directory");
    }
    blob_appendf(&extra, " %$", zDest);
  }else if( fossil_strcmp(zCmd, "clean")==0 ){
    zCmd = "clean --chdir";
    collect_argument(&extra, "allckouts",0);
    collect_argument_value(&extra, "case-sensitive");
    collect_argument_value(&extra, "clean");
    collect_argument(&extra, "dirsonly",0);
    collect_argument(&extra, "disable-undo",0);
    collect_argument(&extra, "dotfiles",0);
    collect_argument(&extra, "emptydirs",0);
    collect_argument(&extra, "force","f");
    collect_argument_value(&extra, "ignore");
    collect_argument_value(&extra, "keep");
    collect_argument(&extra, "no-prompt",0);
    collect_argument(&extra, "temp",0);
    collect_argument(&extra, "verbose","v");
    collect_argument(&extra, "whatif",0);
    useCheckouts = 1;
  }else if( fossil_strcmp(zCmd, "config")==0 ){
    zCmd = "config -R";







|
|





|
|







210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
    if( file_isdir(zDest, ExtFILE)!=1 ){
      fossil_fatal("argument to \"fossil all backup\" must be a directory");
    }
    blob_appendf(&extra, " %$", zDest);
  }else if( fossil_strcmp(zCmd, "clean")==0 ){
    zCmd = "clean --chdir";
    collect_argument(&extra, "allckouts",0);
    collect_argument_value(&extra, "case-sensitive", 0);
    collect_argument_value(&extra, "clean", 0);
    collect_argument(&extra, "dirsonly",0);
    collect_argument(&extra, "disable-undo",0);
    collect_argument(&extra, "dotfiles",0);
    collect_argument(&extra, "emptydirs",0);
    collect_argument(&extra, "force","f");
    collect_argument_value(&extra, "ignore", 0);
    collect_argument_value(&extra, "keep", 0);
    collect_argument(&extra, "no-prompt",0);
    collect_argument(&extra, "temp",0);
    collect_argument(&extra, "verbose","v");
    collect_argument(&extra, "whatif",0);
    useCheckouts = 1;
  }else if( fossil_strcmp(zCmd, "config")==0 ){
    zCmd = "config -R";
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
  }else if( fossil_strcmp(zCmd, "extras")==0 ){
    if( showFile ){
      zCmd = "extras --chdir";
    }else{
      zCmd = "extras --header --chdir";
    }
    collect_argument(&extra, "abs-paths",0);
    collect_argument_value(&extra, "case-sensitive");
    collect_argument(&extra, "dotfiles",0);
    collect_argument_value(&extra, "ignore");
    collect_argument(&extra, "rel-paths",0);
    useCheckouts = 1;
    stopOnError = 0;
    quiet = 1;
  }else if( fossil_strcmp(zCmd, "git")==0 ){
    if( g.argc<4 ){
      usage("git (export|status)");







|

|







247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
  }else if( fossil_strcmp(zCmd, "extras")==0 ){
    if( showFile ){
      zCmd = "extras --chdir";
    }else{
      zCmd = "extras --header --chdir";
    }
    collect_argument(&extra, "abs-paths",0);
    collect_argument_value(&extra, "case-sensitive", 0);
    collect_argument(&extra, "dotfiles",0);
    collect_argument_value(&extra, "ignore", 0);
    collect_argument(&extra, "rel-paths",0);
    useCheckouts = 1;
    stopOnError = 0;
    quiet = 1;
  }else if( fossil_strcmp(zCmd, "git")==0 ){
    if( g.argc<4 ){
      usage("git (export|status)");
274
275
276
277
278
279
280

281
282
283
284
285
286
287
288
289
290
291
292
293
294
    collect_argument(&extra, "verbose","v");
  }else if( fossil_strcmp(zCmd, "pull")==0 ){
    zCmd = "pull -autourl -R";
    collect_argument(&extra, "verbose","v");
    collect_argument(&extra, "share-links",0);
  }else if( fossil_strcmp(zCmd, "rebuild")==0 ){
    zCmd = "rebuild";

    collect_argument(&extra, "cluster",0);
    collect_argument(&extra, "compress",0);
    collect_argument(&extra, "compress-only",0);
    collect_argument(&extra, "noverify",0);
    collect_argument_value(&extra, "pagesize");
    collect_argument(&extra, "vacuum",0);
    collect_argument(&extra, "deanalyze",0);
    collect_argument(&extra, "analyze",0);
    collect_argument(&extra, "wal",0);
    collect_argument(&extra, "stats",0);
    collect_argument(&extra, "index",0);
    collect_argument(&extra, "noindex",0);
    collect_argument(&extra, "ifneeded", 0);
  }else if( fossil_strcmp(zCmd, "remote")==0 ){







>




|

|







276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
    collect_argument(&extra, "verbose","v");
  }else if( fossil_strcmp(zCmd, "pull")==0 ){
    zCmd = "pull -autourl -R";
    collect_argument(&extra, "verbose","v");
    collect_argument(&extra, "share-links",0);
  }else if( fossil_strcmp(zCmd, "rebuild")==0 ){
    zCmd = "rebuild";
    collect_argument(&extra, "analyze",0);
    collect_argument(&extra, "cluster",0);
    collect_argument(&extra, "compress",0);
    collect_argument(&extra, "compress-only",0);
    collect_argument(&extra, "noverify",0);
    collect_argument_value(&extra, "pagesize", 0);
    collect_argument(&extra, "vacuum",0);
    collect_argument(&extra, "deanalyze",0); /* Deprecated */
    collect_argument(&extra, "analyze",0);
    collect_argument(&extra, "wal",0);
    collect_argument(&extra, "stats",0);
    collect_argument(&extra, "index",0);
    collect_argument(&extra, "noindex",0);
    collect_argument(&extra, "ifneeded", 0);
  }else if( fossil_strcmp(zCmd, "remote")==0 ){
412
413
414
415
416
417
418


419
420
421
422
423
424
425
  }else if( fossil_strcmp(zCmd, "cache")==0 ){
    zCmd = "cache -R";
    showLabel = 1;
    collect_argv(&extra, 3);
  }else if( fossil_strcmp(zCmd, "whatis")==0 ){
    zCmd = "whatis -q -R";
    quiet = 1;


    collect_argv(&extra, 3);
  }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 setting sync ui unset whatis");
  }







>
>







415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
  }else if( fossil_strcmp(zCmd, "cache")==0 ){
    zCmd = "cache -R";
    showLabel = 1;
    collect_argv(&extra, 3);
  }else if( fossil_strcmp(zCmd, "whatis")==0 ){
    zCmd = "whatis -q -R";
    quiet = 1;
    collect_argument(&extra, "file", "f");
    collect_argument_value(&extra, "type", 0);
    collect_argv(&extra, 3);
  }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 setting sync ui unset whatis");
  }
Changes to src/attach.c.
748
749
750
751
752
753
754
755

756
757
758
759
760
761
762
      if( (pWiki = manifest_get(rid, CFTYPE_EVENT, 0))!=0 ){
        zBody = pWiki->zWiki;
      }
      if( zBody==0 ){
        fossil_fatal("technote [%s] not found",zETime);
      }
      zTarget = db_text(0,
        "SELECT substr(tagname,7) FROM tag WHERE tagid=(SELECT tagid FROM event WHERE objid='%d')",

        rid
      );
      zFile = g.argv[3];
    }
    blob_read_from_file(&content, zFile, ExtFILE);
    user_select();
    attach_commit(







|
>







748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
      if( (pWiki = manifest_get(rid, CFTYPE_EVENT, 0))!=0 ){
        zBody = pWiki->zWiki;
      }
      if( zBody==0 ){
        fossil_fatal("technote [%s] not found",zETime);
      }
      zTarget = db_text(0,
        "SELECT substr(tagname,7) FROM tag "
        "  WHERE tagid=(SELECT tagid FROM event WHERE objid='%d')",
        rid
      );
      zFile = g.argv[3];
    }
    blob_read_from_file(&content, zFile, ExtFILE);
    user_select();
    attach_commit(
Changes to src/backlink.c.
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
  void *opaque
){
  Backlink *p = (Backlink*)opaque;
  char *zTarget = blob_buffer(target);
  int nTarget = blob_size(target);

  backlink_create(p, zTarget, nTarget);
  return 1;    
}

/* No-op routines for the rendering callbacks that we do not need */
static void mkdn_noop_prolog(Blob *b, void *v){ return; }
static void (*mkdn_noop_epilog)(Blob*, void*) = mkdn_noop_prolog;
static void mkdn_noop_footnotes(Blob *b1, const Blob *b2, void *v){ return; }
static void mkdn_noop_blockcode(Blob *b1, Blob *b2, void *v){ return; }







|







247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
  void *opaque
){
  Backlink *p = (Backlink*)opaque;
  char *zTarget = blob_buffer(target);
  int nTarget = blob_size(target);

  backlink_create(p, zTarget, nTarget);
  return 1;
}

/* No-op routines for the rendering callbacks that we do not need */
static void mkdn_noop_prolog(Blob *b, void *v){ return; }
static void (*mkdn_noop_epilog)(Blob*, void*) = mkdn_noop_prolog;
static void mkdn_noop_footnotes(Blob *b1, const Blob *b2, void *v){ return; }
static void mkdn_noop_blockcode(Blob *b1, Blob *b2, void *v){ return; }
Changes to src/backoffice.c.
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
** we cannot prove that the process is dead, return true.
*/
static int backofficeProcessExists(sqlite3_uint64 pid){
#if defined(_WIN32)
  return pid>0 && backofficeWin32ProcessExists((DWORD)pid)!=0;
#else
  return pid>0 && kill((pid_t)pid, 0)==0;
#endif 
}

/*
** Check to see if the process identified by pid has finished.  If
** we cannot prove that the process is still running, return true.
*/
static int backofficeProcessDone(sqlite3_uint64 pid){
#if defined(_WIN32)
  return pid<=0 || backofficeWin32ProcessExists((DWORD)pid)==0;
#else
  return pid<=0 || kill((pid_t)pid, 0)!=0;
#endif 
}

/*
** Return a process id number for the current process
*/
static sqlite3_uint64 backofficeProcessId(void){
  return (sqlite3_uint64)GETPID();







|











|







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
** we cannot prove that the process is dead, return true.
*/
static int backofficeProcessExists(sqlite3_uint64 pid){
#if defined(_WIN32)
  return pid>0 && backofficeWin32ProcessExists((DWORD)pid)!=0;
#else
  return pid>0 && kill((pid_t)pid, 0)==0;
#endif
}

/*
** Check to see if the process identified by pid has finished.  If
** we cannot prove that the process is still running, return true.
*/
static int backofficeProcessDone(sqlite3_uint64 pid){
#if defined(_WIN32)
  return pid<=0 || backofficeWin32ProcessExists((DWORD)pid)==0;
#else
  return pid<=0 || kill((pid_t)pid, 0)!=0;
#endif
}

/*
** Return a process id number for the current process
*/
static sqlite3_uint64 backofficeProcessId(void){
  return (sqlite3_uint64)GETPID();
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
** This might be done by a cron job or similar to make sure backoffice
** processing happens periodically.  Or, the --poll option can be used
** to run this command as a daemon that will periodically invoke backoffice
** on a collection of repositories.
**
** If only a single repository is named and --poll is omitted, then the
** backoffice work is done in-process.  But if there are multiple repositories
** or if --poll is used, a separate sub-process is started for each poll of 
** each repository.
**
** Standard options:
**
**    --debug                 Show what this command is doing
**
**    --logfile FILE          Append a log of backoffice actions onto FILE







|







673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
** This might be done by a cron job or similar to make sure backoffice
** processing happens periodically.  Or, the --poll option can be used
** to run this command as a daemon that will periodically invoke backoffice
** on a collection of repositories.
**
** If only a single repository is named and --poll is omitted, then the
** backoffice work is done in-process.  But if there are multiple repositories
** or if --poll is used, a separate sub-process is started for each poll of
** each repository.
**
** Standard options:
**
**    --debug                 Show what this command is doing
**
**    --logfile FILE          Append a log of backoffice actions onto FILE
Changes to src/blob.c.
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
      z[--j] = z[i];
    }
  }
}

/*
** ASCII (for reference):
**    x0  x1  x2  x3  x4  x5  x6  x7  x8  x9  xa  xb  xc  xd  xe  xf 
** 0x ^`  ^a  ^b  ^c  ^d  ^e  ^f  ^g  \b  \t  \n  ()  \f  \r  ^n  ^o 
** 1x ^p  ^q  ^r  ^s  ^t  ^u  ^v  ^w  ^x  ^y  ^z  ^{  ^|  ^}  ^~  ^ 
** 2x ()  !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /  
** 3x 0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?  
** 4x @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O  
** 5x P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _  
** 6x `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o  
** 7x p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~   ^_ 
*/

/*
** Meanings for bytes in a filename:
**
**    0      Ordinary character.  No encoding required
**    1      Needs to be escaped







|
|
|
|
|
|
|
|
|







1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
      z[--j] = z[i];
    }
  }
}

/*
** ASCII (for reference):
**    x0  x1  x2  x3  x4  x5  x6  x7  x8  x9  xa  xb  xc  xd  xe  xf
** 0x ^`  ^a  ^b  ^c  ^d  ^e  ^f  ^g  \b  \t  \n  ()  \f  \r  ^n  ^o
** 1x ^p  ^q  ^r  ^s  ^t  ^u  ^v  ^w  ^x  ^y  ^z  ^{  ^|  ^}  ^~  ^
** 2x ()  !   "   #   $   %   &   '   (   )   *   +   ,   -   .   /
** 3x 0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ?
** 4x @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O
** 5x P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _
** 6x `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o
** 7x p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~   ^_
*/

/*
** Meanings for bytes in a filename:
**
**    0      Ordinary character.  No encoding required
**    1      Needs to be escaped
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
          blob_token(pBlob, &bad);
          fossil_fatal("the [%s] argument to the \"%s\" command contains "
                       "an illegal UTF-8 character",
                       zIn, blob_str(&bad));
        }
        i += x-2;
      }
    } 
  }

  /* Separate from the previous argument by a space */
  if( n>0 && !fossil_isspace(z[n-1]) ){
    blob_append_char(pBlob, ' ');
  }








|







1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
          blob_token(pBlob, &bad);
          fossil_fatal("the [%s] argument to the \"%s\" command contains "
                       "an illegal UTF-8 character",
                       zIn, blob_str(&bad));
        }
        i += x-2;
      }
    }
  }

  /* Separate from the previous argument by a space */
  if( n>0 && !fossil_isspace(z[n-1]) ){
    blob_append_char(pBlob, ' ');
  }

1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
      }
#ifdef _WIN32
      if( zBuf[0]=='-' && zArg[0]=='.' && zArg[1]=='\\' ) zArg += 2;
#else
      if( zBuf[0]=='-' && zArg[0]=='.' && zArg[1]=='/' ) zArg += 2;
#endif
      if( strcmp(zBuf, zArg)!=0 ){
        fossil_fatal("argument disagree: \"%s\" (%s) versus \"%s\"", 
                     zBuf, g.argv[i-1], zArg);
      }
      continue;
    }else if( fossil_strcmp(zArg, "--fuzz")==0 && i+1<g.argc ){
      int n = atoi(g.argv[++i]);
      int j;
      for(j=0; j<n; j++){







|







1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
      }
#ifdef _WIN32
      if( zBuf[0]=='-' && zArg[0]=='.' && zArg[1]=='\\' ) zArg += 2;
#else
      if( zBuf[0]=='-' && zArg[0]=='.' && zArg[1]=='/' ) zArg += 2;
#endif
      if( strcmp(zBuf, zArg)!=0 ){
        fossil_fatal("argument disagree: \"%s\" (%s) versus \"%s\"",
                     zBuf, g.argv[i-1], zArg);
      }
      continue;
    }else if( fossil_strcmp(zArg, "--fuzz")==0 && i+1<g.argc ){
      int n = atoi(g.argv[++i]);
      int j;
      for(j=0; j<n; j++){
Changes to src/branch.c.
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
  const char *zUser
){
  Blob sql;
  blob_init(&sql, 0, 0);
  brlist_create_temp_table();
  /* Ignore nLimitMRU if no chronological sort requested. */
  if( (brFlags & BRL_ORDERBY_MTIME)==0 ) nLimitMRU = 0;
  /* Undocumented: invert negative values for nLimitMRU, so that command-line
  ** arguments similar to `head -5' with "option numbers" are possible. */
  if( nLimitMRU<0 ) nLimitMRU = -nLimitMRU;
  /* OUTER QUERY */
  blob_append_sql(&sql,"SELECT name, isprivate, mergeto,");
  if( brFlags & BRL_LIST_USERS ){
    blob_append_sql(&sql,
      " (SELECT group_concat(user) FROM ("
      "     SELECT DISTINCT * FROM ("
      "         SELECT coalesce(euser,user) AS user"







|
<
|







304
305
306
307
308
309
310
311

312
313
314
315
316
317
318
319
  const char *zUser
){
  Blob sql;
  blob_init(&sql, 0, 0);
  brlist_create_temp_table();
  /* Ignore nLimitMRU if no chronological sort requested. */
  if( (brFlags & BRL_ORDERBY_MTIME)==0 ) nLimitMRU = 0;
  /* Negative values for nLimitMRU also mean "no limit". */

  if( nLimitMRU<0 ) nLimitMRU = 0;
  /* OUTER QUERY */
  blob_append_sql(&sql,"SELECT name, isprivate, mergeto,");
  if( brFlags & BRL_LIST_USERS ){
    blob_append_sql(&sql,
      " (SELECT group_concat(user) FROM ("
      "     SELECT DISTINCT * FROM ("
      "         SELECT coalesce(euser,user) AS user"
339
340
341
342
343
344
345
346

347
348
349
350
351
352
353
      blob_append_sql(&sql,
        "SELECT name, isprivate, mtime, mergeto FROM tmp_brlist WHERE 1"
      );
      break;
    }
    case BRL_OPEN_ONLY: {
      blob_append_sql(&sql,
        "SELECT name, isprivate, mtime, mergeto FROM tmp_brlist WHERE NOT isclosed"

      );
      break;
    }
  }
  if( brFlags & BRL_PRIVATE ) blob_append_sql(&sql, " AND isprivate");
  if( brFlags & BRL_MERGED ) blob_append_sql(&sql, " AND mergeto IS NOT NULL");
  if( zBrNameGlob ) blob_append_sql(&sql, " AND (name GLOB %Q)", zBrNameGlob);







|
>







338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
      blob_append_sql(&sql,
        "SELECT name, isprivate, mtime, mergeto FROM tmp_brlist WHERE 1"
      );
      break;
    }
    case BRL_OPEN_ONLY: {
      blob_append_sql(&sql,
        "SELECT name, isprivate, mtime, mergeto FROM tmp_brlist "
        "  WHERE NOT isclosed"
      );
      break;
    }
  }
  if( brFlags & BRL_PRIVATE ) blob_append_sql(&sql, " AND isprivate");
  if( brFlags & BRL_MERGED ) blob_append_sql(&sql, " AND mergeto IS NOT NULL");
  if( zBrNameGlob ) blob_append_sql(&sql, " AND (name GLOB %Q)", zBrNameGlob);
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
      int isPriv = db_column_int(&q, 1)==1;
      const char *zMergeTo = db_column_text(&q, 2);
      int isCur = zCurrent!=0 && fossil_strcmp(zCurrent,zBr)==0;
      const char *zUsers = db_column_text(&q, 3);
      if( (brFlags & BRL_MERGED) && fossil_strcmp(zCurrent,zMergeTo)!=0 ){
        continue;
      }
      if( (brFlags & BRL_UNMERGED) && (fossil_strcmp(zCurrent,zMergeTo)==0 
          || isCur) ){
        continue;
      }
      blob_appendf(&txt, "%s%s%s",
        ( (brFlags & BRL_PRIVATE) ? " " : ( isPriv ? "#" : " ") ),
        (isCur ? "* " : "  "), zBr);
      if( nUsers ){







|







771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
      int isPriv = db_column_int(&q, 1)==1;
      const char *zMergeTo = db_column_text(&q, 2);
      int isCur = zCurrent!=0 && fossil_strcmp(zCurrent,zBr)==0;
      const char *zUsers = db_column_text(&q, 3);
      if( (brFlags & BRL_MERGED) && fossil_strcmp(zCurrent,zMergeTo)!=0 ){
        continue;
      }
      if( (brFlags & BRL_UNMERGED) && (fossil_strcmp(zCurrent,zMergeTo)==0
          || isCur) ){
        continue;
      }
      blob_appendf(&txt, "%s%s%s",
        ( (brFlags & BRL_PRIVATE) ? " " : ( isPriv ? "#" : " ") ),
        (isCur ? "* " : "  "), zBr);
      if( nUsers ){
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
      }
    }
    if( zBgClr && zBgClr[0] && show_colors ){
      @ <tr style="background-color:%s(zBgClr)">
    }else{
      @ <tr>
    }
    @ <td>%z(href("%R/timeline?r=%T",zBranch))%h(zBranch)</a><input 
    @  type="checkbox" disabled="disabled"/></td>
    @ <td data-sortkey="%016llx(iMtime)">%s(zAge)</td>
    @ <td>%d(nCkin)</td>
    fossil_free(zAge);
    @ <td>%s(isClosed?"closed":"")</td>
    if( zMergeTo ){
      @ <td>merged into







|







885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
      }
    }
    if( zBgClr && zBgClr[0] && show_colors ){
      @ <tr style="background-color:%s(zBgClr)">
    }else{
      @ <tr>
    }
    @ <td>%z(href("%R/timeline?r=%T",zBranch))%h(zBranch)</a><input
    @  type="checkbox" disabled="disabled"/></td>
    @ <td data-sortkey="%016llx(iMtime)">%s(zAge)</td>
    @ <td>%d(nCkin)</td>
    fossil_free(zAge);
    @ <td>%s(isClosed?"closed":"")</td>
    if( zMergeTo ){
      @ <td>merged into
Changes to src/browse.c.
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370

  /* Generate a multi-column table listing the contents of zD[]
  ** directory.
  */
  mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/");
  if( mxLen<12 ) mxLen = 12;
  mxLen += (mxLen+9)/10;
  db_prepare(&q, 
     "SELECT x, u FROM localfiles ORDER BY x COLLATE uintnocase /*scan*/");
  @ <div class="columns files" style="columns: %d(mxLen)ex auto">
  @ <ul class="browser">
  while( db_step(&q)==SQLITE_ROW ){
    const char *zFN;
    zFN = db_column_text(&q, 0);
    if( zFN[0]=='/' ){







|







356
357
358
359
360
361
362
363
364
365
366
367
368
369
370

  /* Generate a multi-column table listing the contents of zD[]
  ** directory.
  */
  mxLen = db_int(12, "SELECT max(length(x)) FROM localfiles /*scan*/");
  if( mxLen<12 ) mxLen = 12;
  mxLen += (mxLen+9)/10;
  db_prepare(&q,
     "SELECT x, u FROM localfiles ORDER BY x COLLATE uintnocase /*scan*/");
  @ <div class="columns files" style="columns: %d(mxLen)ex auto">
  @ <ul class="browser">
  while( db_step(&q)==SQLITE_ROW ){
    const char *zFN;
    zFN = db_column_text(&q, 0);
    if( zFN[0]=='/' ){
469
470
471
472
473
474
475
476

477
478
479
480
481
482
483
  FileTreeNode *pSibling;   /* Next element in the same subdirectory */
  FileTreeNode *pChild;     /* List of child nodes */
  FileTreeNode *pLastChild; /* Last child on the pChild list */
  char *zName;              /* Name of this entry.  The "tail" */
  char *zFullName;          /* Full pathname of this entry */
  char *zUuid;              /* Artifact hash of this file.  May be NULL. */
  double mtime;             /* Modification time for this entry */
  double sortBy;            /* Either mtime or size, depending on desired sort order */

  int iSize;                /* Size for this entry */
  unsigned nFullName;       /* Length of zFullName */
  unsigned iLevel;          /* Levels of parent directories */
};

/*
** A complete file hierarchy







|
>







469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
  FileTreeNode *pSibling;   /* Next element in the same subdirectory */
  FileTreeNode *pChild;     /* List of child nodes */
  FileTreeNode *pLastChild; /* Last child on the pChild list */
  char *zName;              /* Name of this entry.  The "tail" */
  char *zFullName;          /* Full pathname of this entry */
  char *zUuid;              /* Artifact hash of this file.  May be NULL. */
  double mtime;             /* Modification time for this entry */
  double sortBy;            /* Either mtime or size, depending on desired
                               sort order */
  int iSize;                /* Size for this entry */
  unsigned nFullName;       /* Length of zFullName */
  unsigned iLevel;          /* Levels of parent directories */
};

/*
** A complete file hierarchy
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
  const char *zUuid,       /* Hash of the file.  Might be NULL. */
  double mtime,            /* Modification time for this entry */
  int size,                /* Size for this entry */
  int sortOrder            /* 0: filename, 1: mtime, 2: size */
){
  int i;
  FileTreeNode *pParent;   /* Parent (directory) of the next node to insert */
  
  /* Make pParent point to the most recent ancestor of zPath, or
  ** NULL if there are no prior entires that are a container for zPath.
  */
  pParent = pTree->pLast;
  while( pParent!=0 &&
      ( strncmp(pParent->zFullName, zPath, pParent->nFullName)!=0
        || zPath[pParent->nFullName]!='/' )







|







507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
  const char *zUuid,       /* Hash of the file.  Might be NULL. */
  double mtime,            /* Modification time for this entry */
  int size,                /* Size for this entry */
  int sortOrder            /* 0: filename, 1: mtime, 2: size */
){
  int i;
  FileTreeNode *pParent;   /* Parent (directory) of the next node to insert */

  /* Make pParent point to the most recent ancestor of zPath, or
  ** NULL if there are no prior entires that are a container for zPath.
  */
  pParent = pTree->pLast;
  while( pParent!=0 &&
      ( strncmp(pParent->zFullName, zPath, pParent->nFullName)!=0
        || zPath[pParent->nFullName]!='/' )
Changes to src/builtin.c.
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
  builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur;
  return pCur->iRowid>count(aBuiltinFiles);
}

/*
** This method is called to "rewind" the builtinVtab_cursor object back
** to the first row of output.  This method is always called at least
** once prior to any call to builtinVtabColumn() or builtinVtabRowid() or 
** builtinVtabEof().
*/
static int builtinVtabFilter(
  sqlite3_vtab_cursor *pVtabCursor, 
  int idxNum, const char *idxStr,
  int argc, sqlite3_value **argv
){
  builtinVtab_cursor *pCur = (builtinVtab_cursor *)pVtabCursor;
  pCur->iRowid = 1;
  return SQLITE_OK;
}







|



|







519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
  builtinVtab_cursor *pCur = (builtinVtab_cursor*)cur;
  return pCur->iRowid>count(aBuiltinFiles);
}

/*
** This method is called to "rewind" the builtinVtab_cursor object back
** to the first row of output.  This method is always called at least
** once prior to any call to builtinVtabColumn() or builtinVtabRowid() or
** builtinVtabEof().
*/
static int builtinVtabFilter(
  sqlite3_vtab_cursor *pVtabCursor,
  int idxNum, const char *idxStr,
  int argc, sqlite3_value **argv
){
  builtinVtab_cursor *pCur = (builtinVtab_cursor *)pVtabCursor;
  pCur->iRowid = 1;
  return SQLITE_OK;
}
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
){
  pIdxInfo->estimatedCost = (double)count(aBuiltinFiles);
  pIdxInfo->estimatedRows = count(aBuiltinFiles);
  return SQLITE_OK;
}

/*
** This following structure defines all the methods for the 
** virtual table.
*/
static sqlite3_module builtinVtabModule = {
  /* iVersion    */ 0,
  /* xCreate     */ 0,  /* The builtin vtab is eponymous and read-only */
  /* xConnect    */ builtinVtabConnect,
  /* xBestIndex  */ builtinVtabBestIndex,







|







548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
){
  pIdxInfo->estimatedCost = (double)count(aBuiltinFiles);
  pIdxInfo->estimatedRows = count(aBuiltinFiles);
  return SQLITE_OK;
}

/*
** This following structure defines all the methods for the
** virtual table.
*/
static sqlite3_module builtinVtabModule = {
  /* iVersion    */ 0,
  /* xCreate     */ 0,  /* The builtin vtab is eponymous and read-only */
  /* xConnect    */ builtinVtabConnect,
  /* xBestIndex  */ builtinVtabBestIndex,
575
576
577
578
579
580
581
582

583
584
585
586
587
588
589
  /* xCommit     */ 0,
  /* xRollback   */ 0,
  /* xFindMethod */ 0,
  /* xRename     */ 0,
  /* xSavepoint  */ 0,
  /* xRelease    */ 0,
  /* xRollbackTo */ 0,
  /* xShadowName */ 0

};


/*
** Register the builtin virtual table
*/
int builtin_vtab_register(sqlite3 *db){







|
>







575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
  /* xCommit     */ 0,
  /* xRollback   */ 0,
  /* xFindMethod */ 0,
  /* xRename     */ 0,
  /* xSavepoint  */ 0,
  /* xRelease    */ 0,
  /* xRollbackTo */ 0,
  /* xShadowName */ 0,
  /* xIntegrity  */ 0
};


/*
** Register the builtin virtual table
*/
int builtin_vtab_register(sqlite3 *db){
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
** per-page basis. In this case, all arguments are ignored!
**
** This function has an internal mapping of the dependencies for each
** of the known fossil.XYZ.js modules and ensures that the
** dependencies also get queued (recursively) and that each module is
** queued only once.
**
** If passed a name which is not a base fossil module name then it 
** will fail fatally!
**
** DO NOT use this for loading fossil.page.*.js: use
** builtin_request_js() for those.
**
** If the current JS delivery mode is *not* JS_BUNDLED then this
** function queues up a request for each given module and its known







|







814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
** per-page basis. In this case, all arguments are ignored!
**
** This function has an internal mapping of the dependencies for each
** of the known fossil.XYZ.js modules and ensures that the
** dependencies also get queued (recursively) and that each module is
** queued only once.
**
** If passed a name which is not a base fossil module name then it
** will fail fatally!
**
** DO NOT use this for loading fossil.page.*.js: use
** builtin_request_js() for those.
**
** If the current JS delivery mode is *not* JS_BUNDLED then this
** function queues up a request for each given module and its known
Changes to src/cache.c.
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
      sqlite3_exec(db, "DELETE FROM cache; DELETE FROM blob; VACUUM;",0,0,0);
      sqlite3_close(db);
      fossil_print("cache cleared\n");
    }else{
      fossil_print("nothing to clear; cache does not exist\n");
    }
  }else if( strncmp(zCmd, "list", nCmd)==0
        ||  strncmp(zCmd, "ls", nCmd)==0 
        ||  strncmp(zCmd, "status", nCmd)==0
  ){
    db = cacheOpen(0);
    if( db==0 ){
      fossil_print("cache does not exist\n");
    }else{
      int nEntry = 0;







|







311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
      sqlite3_exec(db, "DELETE FROM cache; DELETE FROM blob; VACUUM;",0,0,0);
      sqlite3_close(db);
      fossil_print("cache cleared\n");
    }else{
      fossil_print("nothing to clear; cache does not exist\n");
    }
  }else if( strncmp(zCmd, "list", nCmd)==0
        ||  strncmp(zCmd, "ls", nCmd)==0
        ||  strncmp(zCmd, "status", nCmd)==0
  ){
    db = cacheOpen(0);
    if( db==0 ){
      fossil_print("cache does not exist\n");
    }else{
      int nEntry = 0;
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
        @ hit-count: %d(sqlite3_column_int(pStmt,2))
        @ last-access: %s(sqlite3_column_text(pStmt,3)) \
        if( zHash ){
          @ %z(href("%R/timeline?c=%S",zHash))check-in</a>\
          fossil_free(zHash);
        }
        @ </p></li>
        
      }
      sqlite3_finalize(pStmt);
      @ </ol>
    }
    zDbName = cacheName();
    bigSizeName(sizeof(zBuf), zBuf, file_size(zDbName, ExtFILE));
    @ <p>







|







430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
        @ hit-count: %d(sqlite3_column_int(pStmt,2))
        @ last-access: %s(sqlite3_column_text(pStmt,3)) \
        if( zHash ){
          @ %z(href("%R/timeline?c=%S",zHash))check-in</a>\
          fossil_free(zHash);
        }
        @ </p></li>

      }
      sqlite3_finalize(pStmt);
      @ </ol>
    }
    zDbName = cacheName();
    bigSizeName(sizeof(zBuf), zBuf, file_size(zDbName, ExtFILE));
    @ <p>
Changes to src/capabilities.c.
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
  @ <th>Unversioned Content</th></tr>
  while( db_step(&q)==SQLITE_ROW ){
    const char *zId = db_column_text(&q, 0);
    const char *zCap = db_column_text(&q, 1);
    int n = db_column_int(&q, 3);
    int eType;
    static const char *const azType[] = { "off", "read", "write" };
    static const char *const azClass[] = 
        { "capsumOff", "capsumRead", "capsumWrite" };

    if( n==0 ) continue;

    /* Code */
    if( db_column_int(&q,2)<10 ){
      @ <tr><th align="right"><tt>"%h(zId)"</tt></th>







|







399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
  @ <th>Unversioned Content</th></tr>
  while( db_step(&q)==SQLITE_ROW ){
    const char *zId = db_column_text(&q, 0);
    const char *zCap = db_column_text(&q, 1);
    int n = db_column_int(&q, 3);
    int eType;
    static const char *const azType[] = { "off", "read", "write" };
    static const char *const azClass[] =
        { "capsumOff", "capsumRead", "capsumWrite" };

    if( n==0 ) continue;

    /* Code */
    if( db_column_int(&q,2)<10 ){
      @ <tr><th align="right"><tt>"%h(zId)"</tt></th>
Changes to src/cgi.c.
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
** So, even though the name of this file implies that it only deals with
** CGI, in fact, the code in this file is used to interpret webpage requests
** received by a variety of means, and to generate well-formatted replies
** to those requests.
**
** The code in this file abstracts the web-request so that downstream
** modules that generate the body of the reply (based on the requested page)
** do not need to know if the request is coming from CGI, direct HTTP, 
** SCGI, or some other means.
**
** This module gathers information about web page request into a key/value
** store.  Keys and values come from:
**
**    *  Query parameters
**    *  POST parameter







|







35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
** So, even though the name of this file implies that it only deals with
** CGI, in fact, the code in this file is used to interpret webpage requests
** received by a variety of means, and to generate well-formatted replies
** to those requests.
**
** The code in this file abstracts the web-request so that downstream
** modules that generate the body of the reply (based on the requested page)
** do not need to know if the request is coming from CGI, direct HTTP,
** SCGI, or some other means.
**
** This module gathers information about web page request into a key/value
** store.  Keys and values come from:
**
**    *  Query parameters
**    *  POST parameter
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
  if( iReplyStatus<=0 ){
    iReplyStatus = 200;
    zReplyStatus = "OK";
  }

  if( g.fullHttpReply ){
    if( rangeEnd>0
     && iReplyStatus==200 
     && fossil_strcmp(P("REQUEST_METHOD"),"GET")==0
    ){
      iReplyStatus = 206;
      zReplyStatus = "Partial Content";
    }
    blob_appendf(&hdr, "HTTP/1.0 %d %s\r\n", iReplyStatus, zReplyStatus);
    blob_appendf(&hdr, "Date: %s\r\n", cgi_rfc822_datestamp(time(0)));







|







479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
  if( iReplyStatus<=0 ){
    iReplyStatus = 200;
    zReplyStatus = "OK";
  }

  if( g.fullHttpReply ){
    if( rangeEnd>0
     && iReplyStatus==200
     && fossil_strcmp(P("REQUEST_METHOD"),"GET")==0
    ){
      iReplyStatus = 206;
      zReplyStatus = "Partial Content";
    }
    blob_appendf(&hdr, "HTTP/1.0 %d %s\r\n", iReplyStatus, zReplyStatus);
    blob_appendf(&hdr, "Date: %s\r\n", cgi_rfc822_datestamp(time(0)));
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
      blob_appendf(&hdr, "Content-Encoding: gzip\r\n");
      blob_appendf(&hdr, "Vary: Accept-Encoding\r\n");
    }
    total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]);
    if( iReplyStatus==206 ){
      blob_appendf(&hdr, "Content-Range: bytes %d-%d/%d\r\n",
              rangeStart, rangeEnd-1, total_size);
      total_size = rangeEnd - rangeStart; 
    }
    blob_appendf(&hdr, "Content-Length: %d\r\n", total_size);
  }else{
    total_size = 0;
  }
  blob_appendf(&hdr, "\r\n");
  cgi_fwrite(blob_buffer(&hdr), blob_size(&hdr));







|







560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
      blob_appendf(&hdr, "Content-Encoding: gzip\r\n");
      blob_appendf(&hdr, "Vary: Accept-Encoding\r\n");
    }
    total_size = blob_size(&cgiContent[0]) + blob_size(&cgiContent[1]);
    if( iReplyStatus==206 ){
      blob_appendf(&hdr, "Content-Range: bytes %d-%d/%d\r\n",
              rangeStart, rangeEnd-1, total_size);
      total_size = rangeEnd - rangeStart;
    }
    blob_appendf(&hdr, "Content-Length: %d\r\n", total_size);
  }else{
    total_size = 0;
  }
  blob_appendf(&hdr, "\r\n");
  cgi_fwrite(blob_buffer(&hdr), blob_size(&hdr));
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
**                          /                              \
**    https://fossil-scm.org/forum/info/12736b30c072551a?t=c
**    \___/   \____________/\____/\____________________/ \_/
**      |           |          |             |            |
**      |       HTTP_HOST      |        PATH_INFO     QUERY_STRING
**      |                      |
**    REQUEST_SCHEMA         SCRIPT_NAME
**               
*/
void cgi_init(void){
  char *z;
  const char *zType;
  char *zSemi;
  int len;
  const char *zRequestUri = cgi_parameter("REQUEST_URI",0);







|







1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
**                          /                              \
**    https://fossil-scm.org/forum/info/12736b30c072551a?t=c
**    \___/   \____________/\____/\____________________/ \_/
**      |           |          |             |            |
**      |       HTTP_HOST      |        PATH_INFO     QUERY_STRING
**      |                      |
**    REQUEST_SCHEMA         SCRIPT_NAME
**
*/
void cgi_init(void){
  char *z;
  const char *zType;
  char *zSemi;
  int len;
  const char *zRequestUri = cgi_parameter("REQUEST_URI",0);
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
    zScriptName = fossil_strndup(zRequestUri,(int)(z-zRequestUri));
    cgi_set_parameter("SCRIPT_NAME", zScriptName);
  }

#ifdef _WIN32
  /* The Microsoft IIS web server does not define REQUEST_URI, instead it uses
  ** PATH_INFO for virtually the same purpose.  Define REQUEST_URI the same as
  ** PATH_INFO and redefine PATH_INFO with SCRIPT_NAME removed from the 
  ** beginning. */
  if( zServerSoftware && strstr(zServerSoftware, "Microsoft-IIS") ){
    int i, j;
    cgi_set_parameter("REQUEST_URI", zPathInfo);
    for(i=0; zPathInfo[i]==zScriptName[i] && zPathInfo[i]; i++){}
    for(j=i; zPathInfo[j] && zPathInfo[j]!='?'; j++){}
    zPathInfo = fossil_strndup(zPathInfo+i, j-i);







|







1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
    zScriptName = fossil_strndup(zRequestUri,(int)(z-zRequestUri));
    cgi_set_parameter("SCRIPT_NAME", zScriptName);
  }

#ifdef _WIN32
  /* The Microsoft IIS web server does not define REQUEST_URI, instead it uses
  ** PATH_INFO for virtually the same purpose.  Define REQUEST_URI the same as
  ** PATH_INFO and redefine PATH_INFO with SCRIPT_NAME removed from the
  ** beginning. */
  if( zServerSoftware && strstr(zServerSoftware, "Microsoft-IIS") ){
    int i, j;
    cgi_set_parameter("REQUEST_URI", zPathInfo);
    for(i=0; zPathInfo[i]==zScriptName[i] && zPathInfo[i]; i++){}
    for(j=i; zPathInfo[j] && zPathInfo[j]!='?'; j++){}
    zPathInfo = fossil_strndup(zPathInfo+i, j-i);
Changes to src/chat.c.
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
**    *   Chat content lives in a single repository.  It is never synced.
**        Content expires and is deleted after a set interval (a week or so).
**
** Notification is accomplished using the "hanging GET" or "long poll" design
** in which a GET request is issued but the server does not send a reply until
** new content arrives.  Newer Web Sockets and Server Sent Event protocols are
** more elegant, but are not compatible with CGI, and would thus complicate
** configuration.  
*/
#include "config.h"
#include <assert.h>
#include "chat.h"

/*
** Outputs JS code to initialize a list of chat alert audio files for







|







32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
**    *   Chat content lives in a single repository.  It is never synced.
**        Content expires and is deleted after a set interval (a week or so).
**
** Notification is accomplished using the "hanging GET" or "long poll" design
** in which a GET request is issued but the server does not send a reply until
** new content arrives.  Newer Web Sockets and Server Sent Event protocols are
** more elegant, but are not compatible with CGI, and would thus complicate
** configuration.
*/
#include "config.h"
#include <assert.h>
#include "chat.h"

/*
** Outputs JS code to initialize a list of chat alert audio files for
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
                         " ORDER BY msgid LIMIT 1");
   if( rAge>mxDays ){
     msgid = db_int(0, "SELECT msgid FROM chat"
                       " ORDER BY msgid DESC LIMIT 1 OFFSET %d", mxCnt);
     if( msgid>0 ){
       Stmt s;
       db_multi_exec("PRAGMA secure_delete=ON;");
       db_prepare(&s, 
             "DELETE FROM chat WHERE mtime<julianday('now')-:mxage"
             " AND msgid<%d", msgid);
       db_bind_double(&s, ":mxage", mxDays);
       db_step(&s);
       db_finalize(&s);
     }
   }







|







317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
                         " ORDER BY msgid LIMIT 1");
   if( rAge>mxDays ){
     msgid = db_int(0, "SELECT msgid FROM chat"
                       " ORDER BY msgid DESC LIMIT 1 OFFSET %d", mxCnt);
     if( msgid>0 ){
       Stmt s;
       db_multi_exec("PRAGMA secure_delete=ON;");
       db_prepare(&s,
             "DELETE FROM chat WHERE mtime<julianday('now')-:mxage"
             " AND msgid<%d", msgid);
       db_bind_double(&s, ":mxage", mxDays);
       db_step(&s);
       db_finalize(&s);
     }
   }
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
      }
      sqlite3_sleep(iDelay); nDelay--;
    }
  } /* Exit by "break" */
  db_finalize(&q1);
  blob_append(&json, "\n]}", 3);
  cgi_set_content(&json);
  return;      
}

/*
** WEBPAGE: chat-fetch-one hidden loadavg-exempt
**
** /chat-fetch-one/N
**







|







691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
      }
      sqlite3_sleep(iDelay); nDelay--;
    }
  } /* Exit by "break" */
  db_finalize(&q1);
  blob_append(&json, "\n]}", 3);
  cgi_set_content(&json);
  return;
}

/*
** WEBPAGE: chat-fetch-one hidden loadavg-exempt
**
** /chat-fetch-one/N
**
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
  if( !g.perm.Chat ) {
    chat_emit_permissions_error(0);
    return;
  }
  zChatUser = db_get("chat-timeline-user",0);
  chat_create_tables();
  cgi_set_content_type("application/json");
  db_prepare(&q, 
    "SELECT datetime(mtime), xfrom, xmsg, octet_length(file),"
    "       fname, fmime, lmtime"
    "  FROM chat WHERE msgid=%d AND mdel IS NULL",
    msgid);
  if(SQLITE_ROW==db_step(&q)){
    const char *zDate = db_column_text(&q, 0);
    const char *zFrom = db_column_text(&q, 1);







|







724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
  if( !g.perm.Chat ) {
    chat_emit_permissions_error(0);
    return;
  }
  zChatUser = db_get("chat-timeline-user",0);
  chat_create_tables();
  cgi_set_content_type("application/json");
  db_prepare(&q,
    "SELECT datetime(mtime), xfrom, xmsg, octet_length(file),"
    "       fname, fmime, lmtime"
    "  FROM chat WHERE msgid=%d AND mdel IS NULL",
    msgid);
  if(SQLITE_ROW==db_step(&q)){
    const char *zDate = db_column_text(&q, 0);
    const char *zFrom = db_column_text(&q, 1);
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
      fossil_free(zMsg);
    }
    if( nByte==0 ){
      blob_appendf(&json, "\"fsize\":0");
    }else{
      blob_appendf(&json, "\"fsize\":%d,\"fname\":%!j,\"fmime\":%!j",
                   nByte, zFName, zFMime);
    }    
    blob_append(&json,"}",1);
    cgi_set_content(&json);
  }else{
    ajax_route_error(404,"Chat message #%d not found.", msgid);
  }
  db_finalize(&q);
}







|







767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
      fossil_free(zMsg);
    }
    if( nByte==0 ){
      blob_appendf(&json, "\"fsize\":0");
    }else{
      blob_appendf(&json, "\"fsize\":%d,\"fname\":%!j,\"fmime\":%!j",
                   nByte, zFName, zFMime);
    }
    blob_append(&json,"}",1);
    cgi_set_content(&json);
  }else{
    ajax_route_error(404,"Chat message #%d not found.", msgid);
  }
  db_finalize(&q);
}
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
  sqlite3_value **argv
){
  const char *zType = (const char*)sqlite3_value_text(argv[0]);
  int rid = sqlite3_value_int(argv[1]);
  const char *zUser = (const char*)sqlite3_value_text(argv[2]);
  const char *zMsg = (const char*)sqlite3_value_text(argv[3]);
  char *zRes = 0;
  
  if( zType==0 || zUser==0 || zMsg==0 ) return;
  if( zType[0]=='c' ){
    /* Check-ins */
    char *zBranch;
    char *zUuid;

    zBranch = db_text(0,







|







955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
  sqlite3_value **argv
){
  const char *zType = (const char*)sqlite3_value_text(argv[0]);
  int rid = sqlite3_value_int(argv[1]);
  const char *zUser = (const char*)sqlite3_value_text(argv[2]);
  const char *zMsg = (const char*)sqlite3_value_text(argv[3]);
  char *zRes = 0;

  if( zType==0 || zUser==0 || zMsg==0 ) return;
  if( zType[0]=='c' ){
    /* Check-ins */
    char *zBranch;
    char *zUuid;

    zBranch = db_text(0,
Changes to src/checkin.c.
1363
1364
1365
1366
1367
1368
1369


1370
1371
1372
1373
1374






1375
1376







1377
1378
1379
1380
1381
1382
1383
        "#\n%.78c\n"
        "# The following diff is excluded from the commit message:\n#\n",
        '#'
    );
    diff_options(&DCfg, 0, 1);
    DCfg.diffFlags |= DIFF_VERBOSE;
    if( g.aCommitFile ){


      FileDirList *diffFiles;
      int i;
      for(i=0; g.aCommitFile[i]!=0; ++i){}
      diffFiles = fossil_malloc_zero((i+1) * sizeof(*diffFiles));
      for(i=0; g.aCommitFile[i]!=0; ++i){






        diffFiles[i].zName  = db_text(0,
         "SELECT pathname FROM vfile WHERE id=%d", g.aCommitFile[i]);







        if( fossil_strcmp(diffFiles[i].zName, "." )==0 ){
          diffFiles[0].zName[0] = '.';
          diffFiles[0].zName[1] = 0;
          break;
        }
        diffFiles[i].nName = strlen(diffFiles[i].zName);
        diffFiles[i].nUsed = 0;







>
>





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







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
        "#\n%.78c\n"
        "# The following diff is excluded from the commit message:\n#\n",
        '#'
    );
    diff_options(&DCfg, 0, 1);
    DCfg.diffFlags |= DIFF_VERBOSE;
    if( g.aCommitFile ){
      Stmt q;
      Blob sql = BLOB_INITIALIZER;
      FileDirList *diffFiles;
      int i;
      for(i=0; g.aCommitFile[i]!=0; ++i){}
      diffFiles = fossil_malloc_zero((i+1) * sizeof(*diffFiles));
      for(i=0; g.aCommitFile[i]!=0; ++i){
        blob_append_sql(&sql,
                        "SELECT pathname, deleted, rid WHERE id=%d",
                        g.aCommitFile[i]);
        db_prepare(&q, "%s", blob_sql_text(&sql));
        blob_reset(&sql);
        assert( db_step(&q)==SQLITE_ROW );
        diffFiles[i].zName = fossil_strdup(db_column_text(&q, 0));

        DCfg.diffFlags &= (~DIFF_FILE_MASK);
        if( db_column_int(&q, 1) ){
          DCfg.diffFlags |= DIFF_FILE_DELETED;
        }else if( db_column_int(&q, 2)==0 ){
          DCfg.diffFlags |= DIFF_FILE_ADDED;
        }
        db_finalize(&q);
        if( fossil_strcmp(diffFiles[i].zName, "." )==0 ){
          diffFiles[0].zName[0] = '.';
          diffFiles[0].zName[1] = 0;
          break;
        }
        diffFiles[i].nName = strlen(diffFiles[i].zName);
        diffFiles[i].nUsed = 0;
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
                     "use --override-lock",
                     g.ckinLockFail);
      }else{
        fossil_fatal("Would fork.  \"update\" first or use --branch or "
                     "--allow-fork.");
      }
    }
  
    /*
    ** Do not allow a commit against a closed leaf unless the commit
    ** ends up on a different branch.
    */
    if(
        /* parent check-in has the "closed" tag... */
       leaf_is_closed(vid)
        /* ... and the new check-in has no --branch option or the --branch
        ** option does not actually change the branch */
     && (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
    ** to renew the check-in lock and repeat the checks for conflicts.
    */
    if( zComment ){
      blob_zero(&comment);
      blob_append(&comment, zComment, -1);







|




















|







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
                     "use --override-lock",
                     g.ckinLockFail);
      }else{
        fossil_fatal("Would fork.  \"update\" first or use --branch or "
                     "--allow-fork.");
      }
    }

    /*
    ** Do not allow a commit against a closed leaf unless the commit
    ** ends up on a different branch.
    */
    if(
        /* parent check-in has the "closed" tag... */
       leaf_is_closed(vid)
        /* ... and the new check-in has no --branch option or the --branch
        ** option does not actually change the branch */
     && (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
    ** to renew the check-in lock and repeat the checks for conflicts.
    */
    if( zComment ){
      blob_zero(&comment);
      blob_append(&comment, zComment, -1);
Changes to src/clone.c.
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
        g.argv[2]);
    }
    zRepo = mprintf("./%s.fossil", zBase);
    if( zWorkDir==0 ){
      zWorkDir = mprintf("./%s", zBase);
    }
    fossil_free(zBase);
  }  
  if( -1 != file_size(zRepo, ExtFILE) ){
    db_open_repository(zRepo);
    nResumeSeqno = db_get_int("aux-clone-seqno",0);
    db_close(0);
    if( !nResumeSeqno ){
      fossil_fatal("file already exists: %s", zRepo);
    }







|







202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
        g.argv[2]);
    }
    zRepo = mprintf("./%s.fossil", zBase);
    if( zWorkDir==0 ){
      zWorkDir = mprintf("./%s", zBase);
    }
    fossil_free(zBase);
  }
  if( -1 != file_size(zRepo, ExtFILE) ){
    db_open_repository(zRepo);
    nResumeSeqno = db_get_int("aux-clone-seqno",0);
    db_close(0);
    if( !nResumeSeqno ){
      fossil_fatal("file already exists: %s", zRepo);
    }
Changes to src/comformat.c.
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
      if( maxChars<useChars ){
        zBuf[iBuf++] = ' ';
        break;
      }
    }else if( wordBreak && fossil_isspace(c) ){
      int distUTF8;
      int nextIndex = comment_next_space(zLine, index, &distUTF8);
      if( nextIndex<=0 || distUTF8>maxChars ){
        break;
      }
      charCnt++;
    }else{
      charCnt++;
    }
    assert( c!='\n' || charCnt==0 );







|







272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
      if( maxChars<useChars ){
        zBuf[iBuf++] = ' ';
        break;
      }
    }else if( wordBreak && fossil_isspace(c) ){
      int distUTF8;
      int nextIndex = comment_next_space(zLine, index, &distUTF8);
      if( nextIndex<=0 || distUTF8>=maxChars ){
        break;
      }
      charCnt++;
    }else{
      charCnt++;
    }
    assert( c!='\n' || charCnt==0 );
Changes to src/cookies.c.
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
    zDel = mprintf("del%s",zName);
    if( P(zDel)!=0 ){
      cgi_set_cookie(zName, "", 0, -1);
      cgi_redirect("cookies");
    }
    nCookie++;
    @ <li><p><b>%h(zName)</b>: %h(zValue)
    @ <input type="submit" name="%h(zDel)" value="Delete"> 
    if( fossil_strcmp(zName, DISPLAY_SETTINGS_COOKIE)==0  && cookies.nParam>0 ){
      int j;
      @ <ul>
      for(j=0; j<cookies.nParam; j++){
        @ <li>%h(cookies.aParam[j].zPName): "%h(cookies.aParam[j].zPValue)"
      }
      @ </ul>







|







237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
    zDel = mprintf("del%s",zName);
    if( P(zDel)!=0 ){
      cgi_set_cookie(zName, "", 0, -1);
      cgi_redirect("cookies");
    }
    nCookie++;
    @ <li><p><b>%h(zName)</b>: %h(zValue)
    @ <input type="submit" name="%h(zDel)" value="Delete">
    if( fossil_strcmp(zName, DISPLAY_SETTINGS_COOKIE)==0  && cookies.nParam>0 ){
      int j;
      @ <ul>
      for(j=0; j<cookies.nParam; j++){
        @ <li>%h(cookies.aParam[j].zPName): "%h(cookies.aParam[j].zPValue)"
      }
      @ </ul>
Changes to src/db.c.
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
  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, };

/*
** Arrange for the given file to be deleted on a failure.
*/
void db_delete_on_failure(const char *zFilename){
  assert( db.nDeleteOnFail<count(db.azDeleteOnFail) );
  if( zFilename==0 ) return;







|







170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
  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.
*/
void db_delete_on_failure(const char *zFilename){
  assert( db.nDeleteOnFail<count(db.azDeleteOnFail) );
  if( zFilename==0 ) return;
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
** be compromised by an attack.
*/
void db_protect_only(unsigned flags){
  if( db.nProtect>=count(db.aProtect)-2 ){
    fossil_panic("too many db_protect() calls");
  }
  db.aProtect[db.nProtect++] = db.protectMask;
  if( (flags & PROTECT_SENSITIVE)!=0 
   && db.bProtectTriggers==0
   && g.repositoryOpen
  ){
    /* Create the triggers needed to protect sensitive settings from
    ** being created or modified the first time that PROTECT_SENSITIVE
    ** is enabled.  Deleting a sensitive setting is harmless, so there
    ** is not trigger to block deletes.  After being created once, the







|







455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
** be compromised by an attack.
*/
void db_protect_only(unsigned flags){
  if( db.nProtect>=count(db.aProtect)-2 ){
    fossil_panic("too many db_protect() calls");
  }
  db.aProtect[db.nProtect++] = db.protectMask;
  if( (flags & PROTECT_SENSITIVE)!=0
   && db.bProtectTriggers==0
   && g.repositoryOpen
  ){
    /* Create the triggers needed to protect sensitive settings from
    ** being created or modified the first time that PROTECT_SENSITIVE
    ** is enabled.  Deleting a sensitive setting is harmless, so there
    ** is not trigger to block deletes.  After being created once, the
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
  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.
*/
static char *zSavedKey = 0;







|

|







1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
  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.
*/
static char *zSavedKey = 0;
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
      db_multi_exec("ALTER TABLE undo ADD COLUMN isLink BOOLEAN DEFAULT 0");
    }
    if( db_local_table_exists_but_lacks_column("undo_vfile", "islink") ){
      db_multi_exec("ALTER TABLE undo_vfile ADD COLUMN islink BOOL DEFAULT 0");
    }
  }

  /* The design of the check-out database changed on 2019-01-19, adding the mhash
  ** column to vfile and vmerge and changing the UNIQUE index on vmerge into
  ** a PRIMARY KEY that includes the new mhash column.  However, we must have
  ** the repository database at hand in order to do the migration, so that
  ** step is deferred. */
  return 1;
}








|







2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
      db_multi_exec("ALTER TABLE undo ADD COLUMN isLink BOOLEAN DEFAULT 0");
    }
    if( db_local_table_exists_but_lacks_column("undo_vfile", "islink") ){
      db_multi_exec("ALTER TABLE undo_vfile ADD COLUMN islink BOOL DEFAULT 0");
    }
  }

  /* The design of the check-out database changed on 2019-01-19 adding the mhash
  ** column to vfile and vmerge and changing the UNIQUE index on vmerge into
  ** a PRIMARY KEY that includes the new mhash column.  However, we must have
  ** the repository database at hand in order to do the migration, so that
  ** step is deferred. */
  return 1;
}

2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
  sqlite3_stmt *pStmt = 0;

  sz = file_size(zDbName, ExtFILE);
  if( sz<16834 ) return 0;
  db = db_open(zDbName);
  if( !db ) return 0;
  if( !g.zVfsName && sz%512 ) return 0;
  rc = sqlite3_prepare_v2(db, 
       "SELECT count(*) FROM sqlite_schema"
       " WHERE name COLLATE nocase IN"
       "('blob','delta','rcvfrom','user','config','mlink','plink');",
       -1, &pStmt, 0);
  if( rc ) goto is_repo_end;
  rc = sqlite3_step(pStmt);
  if( rc!=SQLITE_ROW ) goto is_repo_end;







|







2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
  sqlite3_stmt *pStmt = 0;

  sz = file_size(zDbName, ExtFILE);
  if( sz<16834 ) return 0;
  db = db_open(zDbName);
  if( !db ) return 0;
  if( !g.zVfsName && sz%512 ) return 0;
  rc = sqlite3_prepare_v2(db,
       "SELECT count(*) FROM sqlite_schema"
       " WHERE name COLLATE nocase IN"
       "('blob','delta','rcvfrom','user','config','mlink','plink');",
       -1, &pStmt, 0);
  if( rc ) goto is_repo_end;
  rc = sqlite3_step(pStmt);
  if( rc!=SQLITE_ROW ) goto is_repo_end;
3714
3715
3716
3717
3718
3719
3720
3721

3722
3723
3724
3725
3726
3727
3728
      z = fossil_strdup(pSetting->def);
    }else{
      z = fossil_strdup(zDefault);
    }
  }
  return z;
}
char *db_get_mtime(const char *zName, const char *zFormat, const char *zDefault){

  char *z = 0;
  if( g.repositoryOpen ){
    z = db_text(0, "SELECT mtime FROM config WHERE name=%Q", zName);
  }
  if( z==0 ){
    z = fossil_strdup(zDefault);
  }else if( zFormat!=0 ){







|
>







3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
      z = fossil_strdup(pSetting->def);
    }else{
      z = fossil_strdup(zDefault);
    }
  }
  return z;
}
char *db_get_mtime(const char *zName, const char *zFormat,
                   const char *zDefault){
  char *z = 0;
  if( g.repositoryOpen ){
    z = db_text(0, "SELECT mtime FROM config WHERE name=%Q", zName);
  }
  if( z==0 ){
    z = fossil_strdup(zDefault);
  }else if( zFormat!=0 ){
4019
4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
    if( !g.localOpen ) return;
    zName = db_repository_filename();
  }
  file_canonical_name(zName, &full, 0);
  (void)filename_collation();  /* Initialize before connection swap */
  db_swap_connections();
  zRepoSetting = mprintf("repo:%q", blob_str(&full));
  
  db_unprotect(PROTECT_CONFIG);
  db_multi_exec(
     "DELETE FROM global_config WHERE name %s = %Q;",
     filename_collation(), zRepoSetting
  );
  db_multi_exec(
     "INSERT OR IGNORE INTO global_config(name,value)"







|







4020
4021
4022
4023
4024
4025
4026
4027
4028
4029
4030
4031
4032
4033
4034
    if( !g.localOpen ) return;
    zName = db_repository_filename();
  }
  file_canonical_name(zName, &full, 0);
  (void)filename_collation();  /* Initialize before connection swap */
  db_swap_connections();
  zRepoSetting = mprintf("repo:%q", blob_str(&full));

  db_unprotect(PROTECT_CONFIG);
  db_multi_exec(
     "DELETE FROM global_config WHERE name %s = %Q;",
     filename_collation(), zRepoSetting
  );
  db_multi_exec(
     "INSERT OR IGNORE INTO global_config(name,value)"
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122
** "new-name.fossil".
**
** Options:
**   --empty           Initialize check-out as being empty, but still connected
**                     with the local repository. If you commit this check-out,
**                     it will become a new "initial" commit in the repository.
**   -f|--force        Continue with the open even if the working directory is
**                     not empty
**   --force-missing   Force opening a repository with missing content
**   -k|--keep         Only modify the manifest file(s)
**   --nested          Allow opening a repository inside an opened check-out
**   --nosync          Do not auto-sync the repository prior to opening even
**                     if the autosync setting is on.
**   --repodir DIR     If REPOSITORY is a URI that will be cloned, store
**                     the clone in DIR rather than in "."
**   --setmtime        Set timestamps of all files to match their SCM-side
**                     times (the timestamp of the last check-in which modified
**                     them).
**   --sync            Auto-sync prior to opening even if the autosync setting
**                     is off
**   --verbose         If passed a URI then this flag is passed on to the clone
**                     operation, otherwise it has no effect
**   --workdir DIR     Use DIR as the working directory instead of ".". The DIR
**                     directory is created if it does not exist.
**
** See also: [[close]], [[clone]]
*/







|










<
<







4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
4113
4114


4115
4116
4117
4118
4119
4120
4121
** "new-name.fossil".
**
** Options:
**   --empty           Initialize check-out as being empty, but still connected
**                     with the local repository. If you commit this check-out,
**                     it will become a new "initial" commit in the repository.
**   -f|--force        Continue with the open even if the working directory is
**                     not empty, or if auto-sync fails.
**   --force-missing   Force opening a repository with missing content
**   -k|--keep         Only modify the manifest file(s)
**   --nested          Allow opening a repository inside an opened check-out
**   --nosync          Do not auto-sync the repository prior to opening even
**                     if the autosync setting is on.
**   --repodir DIR     If REPOSITORY is a URI that will be cloned, store
**                     the clone in DIR rather than in "."
**   --setmtime        Set timestamps of all files to match their SCM-side
**                     times (the timestamp of the last check-in which modified
**                     them).


**   --verbose         If passed a URI then this flag is passed on to the clone
**                     operation, otherwise it has no effect
**   --workdir DIR     Use DIR as the working directory instead of ".". The DIR
**                     directory is created if it does not exist.
**
** See also: [[close]], [[clone]]
*/
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
4199
  if( keepFlag==0
   && bForce==0
   && (nLocal = file_directory_size(".", 0, 1))>0
   && (nLocal>1 || isUri || !file_in_cwd(zRepo))
  ){
    fossil_fatal("directory %s is not empty\n"
                 "use the -f (--force) option to override\n"
                 "or the -k (--keep) option to keep local files unchanged", 
                 file_getcwd(0,0));
  }

  if( db_open_local_v2(0, allowNested) ){
    fossil_fatal("there is already an open tree at %s", g.zLocalRoot);
  }








|







4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
4197
4198
  if( keepFlag==0
   && bForce==0
   && (nLocal = file_directory_size(".", 0, 1))>0
   && (nLocal>1 || isUri || !file_in_cwd(zRepo))
  ){
    fossil_fatal("directory %s is not empty\n"
                 "use the -f (--force) option to override\n"
                 "or the -k (--keep) option to keep local files unchanged",
                 file_getcwd(0,0));
  }

  if( db_open_local_v2(0, allowNested) ){
    fossil_fatal("there is already an open tree at %s", g.zLocalRoot);
  }

4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
4408
**
** When the admin-log setting is enabled, configuration changes are recorded
** in the "admin_log" table of the repository.
*/
/*
** SETTING: allow-symlinks  boolean default=off sensitive
**
** When allow-symlinks is OFF, Fossil does not see symbolic links 
** (a.k.a "symlinks") on disk as a separate class of object.  Instead Fossil
** sees the object that the symlink points to.  Fossil will only manage files
** and directories, not symlinks.  When a symlink is added to a repository,
** the object that the symlink points to is added, not the symlink itself.
**
** When allow-symlinks is ON, Fossil sees symlinks on disk as a separate
** object class that is distinct from files and directories.  When a symlink







|







4393
4394
4395
4396
4397
4398
4399
4400
4401
4402
4403
4404
4405
4406
4407
**
** When the admin-log setting is enabled, configuration changes are recorded
** in the "admin_log" table of the repository.
*/
/*
** SETTING: allow-symlinks  boolean default=off sensitive
**
** When allow-symlinks is OFF, Fossil does not see symbolic links
** (a.k.a "symlinks") on disk as a separate class of object.  Instead Fossil
** sees the object that the symlink points to.  Fossil will only manage files
** and directories, not symlinks.  When a symlink is added to a repository,
** the object that the symlink points to is added, not the symlink itself.
**
** When allow-symlinks is ON, Fossil sees symlinks on disk as a separate
** object class that is distinct from files and directories.  When a symlink
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
4466
** When the auto-hyperlink setting is 1, the javascript that runs to set
** the href= attributes of hyperlinks delays by this many milliseconds
** after the page load.  Suggested values:  50 to 200.
*/
/*
** SETTING: auto-hyperlink-mouseover  boolean default=off
**
** When the auto-hyperlink setting is 1 and this setting is on, the 
** javascript that runs to set the href= attributes of hyperlinks waits
** until either a mousedown or mousemove event is seen.  This helps
** to distinguish real users from robots. For maximum robot defense,
** the recommended setting is ON.
*/
/*
** SETTING: auto-shun       boolean default=on







|







4451
4452
4453
4454
4455
4456
4457
4458
4459
4460
4461
4462
4463
4464
4465
** When the auto-hyperlink setting is 1, the javascript that runs to set
** the href= attributes of hyperlinks delays by this many milliseconds
** after the page load.  Suggested values:  50 to 200.
*/
/*
** SETTING: auto-hyperlink-mouseover  boolean default=off
**
** When the auto-hyperlink setting is 1 and this setting is on, the
** javascript that runs to set the href= attributes of hyperlinks waits
** until either a mousedown or mousemove event is seen.  This helps
** to distinguish real users from robots. For maximum robot defense,
** the recommended setting is ON.
*/
/*
** SETTING: auto-shun       boolean default=on
4487
4488
4489
4490
4491
4492
4493



4494
4495
4496
4497
4498
4499
4500
**    off,commit=pullonly    Do not autosync, except do a pull before each
**                           "commit", presumably to avoid undesirable
**                           forks.
**
** The syntax is a comma-separated list of VALUE and COMMAND=VALUE entries.
** A plain VALUE entry is the default that is used if no COMMAND matches.
** Otherwise, the VALUE of the matching command is used.



*/
/*
** SETTING: autosync-tries  width=16 default=1
** If autosync is enabled setting this to a value greater
** than zero will cause autosync to try no more than this
** number of attempts if there is a sync failure.
*/







>
>
>







4486
4487
4488
4489
4490
4491
4492
4493
4494
4495
4496
4497
4498
4499
4500
4501
4502
**    off,commit=pullonly    Do not autosync, except do a pull before each
**                           "commit", presumably to avoid undesirable
**                           forks.
**
** The syntax is a comma-separated list of VALUE and COMMAND=VALUE entries.
** A plain VALUE entry is the default that is used if no COMMAND matches.
** Otherwise, the VALUE of the matching command is used.
**
** The "all" value is special in that it applies to the "sync" command in
** addition to "commit", "merge", "open", and "update".
*/
/*
** SETTING: autosync-tries  width=16 default=1
** If autosync is enabled setting this to a value greater
** than zero will cause autosync to try no more than this
** number of attempts if there is a sync failure.
*/
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
** Note that /fileedit cannot edit binary files, so the list should not
** contain any globs for, e.g., images or PDFs.
*/
/*
** SETTING: forbid-delta-manifests    boolean default=off
** 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







|







4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
** Note that /fileedit cannot edit binary files, so the list should not
** contain any globs for, e.g., images or PDFs.
*/
/*
** SETTING: forbid-delta-manifests    boolean default=off
** 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
5014
5015
5016
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5027
5028
** Defaults to "start" on windows, "open" on Mac,
** and "firefox" on Unix.
*/
/*
** SETTING: large-file-size     width=10 default=200000000
** Fossil considers any file whose size is greater than this value
** to be a "large file".  Fossil might issue warnings if you try to
** "add" or "commit" a "large file".  Set this value to 0 or less 
** to disable all such warnings.
*/

/*
** Look up a control setting by its name.  Return a pointer to the Setting
** object, or NULL if there is no such setting.
**







|







5016
5017
5018
5019
5020
5021
5022
5023
5024
5025
5026
5027
5028
5029
5030
** Defaults to "start" on windows, "open" on Mac,
** and "firefox" on Unix.
*/
/*
** SETTING: large-file-size     width=10 default=200000000
** Fossil considers any file whose size is greater than this value
** to be a "large file".  Fossil might issue warnings if you try to
** "add" or "commit" a "large file".  Set this value to 0 or less
** to disable all such warnings.
*/

/*
** Look up a control setting by its name.  Return a pointer to the Setting
** object, or NULL if there is no such setting.
**
5239
5240
5241
5242
5243
5244
5245
5246
5247
5248
5249
5250
5251
5252
5253
** optimization.  FILENAME can also be the configuration database file
** (~/.fossil or ~/.config/fossil.db) or a local .fslckout or _FOSSIL_ file.
**
** The purpose of this command is for testing the WITHOUT ROWID capabilities
** of SQLite.  There is no big advantage to using WITHOUT ROWID in Fossil.
**
** Options:
**    -n|--dry-run  	No changes.  Just print what would happen.
*/
void test_without_rowid(void){
  int i, j;
  Stmt q;
  Blob allSql;
  int dryRun = find_option("dry-run", "n", 0)!=0;
  for(i=2; i<g.argc; i++){







|







5241
5242
5243
5244
5245
5246
5247
5248
5249
5250
5251
5252
5253
5254
5255
** optimization.  FILENAME can also be the configuration database file
** (~/.fossil or ~/.config/fossil.db) or a local .fslckout or _FOSSIL_ file.
**
** The purpose of this command is for testing the WITHOUT ROWID capabilities
** of SQLite.  There is no big advantage to using WITHOUT ROWID in Fossil.
**
** Options:
**    -n|--dry-run     No changes.  Just print what would happen.
*/
void test_without_rowid(void){
  int i, j;
  Stmt q;
  Blob allSql;
  int dryRun = find_option("dry-run", "n", 0)!=0;
  for(i=2; i<g.argc; i++){
Changes to src/deltafunc.c.
484
485
486
487
488
489
490
491

492
493
494
495
496
497
498
  /* xCommit     */ 0,
  /* xRollback   */ 0,
  /* xFindMethod */ 0,
  /* xRename     */ 0,
  /* xSavepoint  */ 0,
  /* xRelease    */ 0,
  /* xRollbackTo */ 0,
  /* xShadowName */ 0

};

/*
** Invoke this routine to register the various delta functions.
*/
int deltafunc_init(sqlite3 *db){
  int rc = SQLITE_OK;







|
>







484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
  /* xCommit     */ 0,
  /* xRollback   */ 0,
  /* xFindMethod */ 0,
  /* xRename     */ 0,
  /* xSavepoint  */ 0,
  /* xRelease    */ 0,
  /* xRollbackTo */ 0,
  /* xShadowName */ 0,
  /* xIntegrity  */ 0
};

/*
** Invoke this routine to register the various delta functions.
*/
int deltafunc_init(sqlite3 *db){
  int rc = SQLITE_OK;
Changes to src/diff.c.
46
47
48
49
50
51
52








53
54
55
56
57
58
59
#define DIFF_BROWSER           0x00008000 /* The --browser option */
#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 */









/*
** These error messages are shared in multiple locations.  They are defined
** here for consistency.
*/
#define DIFF_CANNOT_COMPUTE_BINARY \
    "cannot compute difference between binary files\n"







>
>
>
>
>
>
>
>







46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#define DIFF_BROWSER           0x00008000 /* The --browser option */
#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 */

/*
** These error messages are shared in multiple locations.  They are defined
** here for consistency.
*/
#define DIFF_CANNOT_COMPUTE_BINARY \
    "cannot compute difference between binary files\n"
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
** Conceptually, this object is as an encoding of the command-line options
** for the "fossil diff" command.  That is not a precise description, though,
** because not all diff operations are started from the command-line.  But
** the idea is sound.
**
** Information encoded by this object includes but is not limited to:
**
**    *   The desired output format (unified vs. side-by-side, 
**        TCL, JSON, HTML vs. plain-text).
**
**    *   Number of lines of context surrounding each difference block
**
**    *   Width of output columns for text side-by-side diffop          
*/
struct DiffConfig {
  u64 diffFlags;           /* Diff flags */
  int nContext;            /* Number of lines of context */
  int wColumn;             /* Column width in -y mode */
  u32 nFile;               /* Number of files diffed so far */
  const char *zDiffCmd;    /* External diff command to use instead of builtin */







|




|







88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
** Conceptually, this object is as an encoding of the command-line options
** for the "fossil diff" command.  That is not a precise description, though,
** because not all diff operations are started from the command-line.  But
** the idea is sound.
**
** Information encoded by this object includes but is not limited to:
**
**    *   The desired output format (unified vs. side-by-side,
**        TCL, JSON, HTML vs. plain-text).
**
**    *   Number of lines of context surrounding each difference block
**
**    *   Width of output columns for text side-by-side diffop
*/
struct DiffConfig {
  u64 diffFlags;           /* Diff flags */
  int nContext;            /* Number of lines of context */
  int wColumn;             /* Column width in -y mode */
  u32 nFile;               /* Number of files diffed so far */
  const char *zDiffCmd;    /* External diff command to use instead of builtin */
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
  A = p->aFrom;
  B = p->aTo;
  R = p->aEdit;
  mxr = p->nEdit;
  while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
  for(r=0; r<mxr; r += 3*nr){
    /* Figure out how many triples to show in a single block */
    for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<nContext*2; nr++){}
    /* printf("r=%d nr=%d\n", r, nr); */

    /* For the current block comprising nr triples, figure out
    ** how many lines of A and B are to be displayed
    */
    if( R[r]>nContext ){
      na = nb = nContext;







|







428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
  A = p->aFrom;
  B = p->aTo;
  R = p->aEdit;
  mxr = p->nEdit;
  while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }
  for(r=0; r<mxr; r += 3*nr){
    /* Figure out how many triples to show in a single block */
    for(nr=1; 3*nr<mxr && R[r+nr*3]>0 && R[r+nr*3]<(int)nContext*2; nr++){}
    /* printf("r=%d nr=%d\n", r, nr); */

    /* For the current block comprising nr triples, figure out
    ** how many lines of A and B are to be displayed
    */
    if( R[r]>nContext ){
      na = nb = nContext;
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
/*
** This is an abstract superclass for an object that accepts difference
** lines and formats them for display.  Subclasses of this object format
** the diff output in different ways.
**
** To subclass, create an instance of the DiffBuilder object and fill
** in appropriate method implementations.
*/ 
typedef struct DiffBuilder DiffBuilder;
struct DiffBuilder {
  void (*xSkip)(DiffBuilder*, unsigned int, int);
  void (*xCommon)(DiffBuilder*,const DLine*);
  void (*xInsert)(DiffBuilder*,const DLine*);
  void (*xDelete)(DiffBuilder*,const DLine*);
  void (*xReplace)(DiffBuilder*,const DLine*,const DLine*);







|







913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
/*
** This is an abstract superclass for an object that accepts difference
** lines and formats them for display.  Subclasses of this object format
** the diff output in different ways.
**
** To subclass, create an instance of the DiffBuilder object and fill
** in appropriate method implementations.
*/
typedef struct DiffBuilder DiffBuilder;
struct DiffBuilder {
  void (*xSkip)(DiffBuilder*, unsigned int, int);
  void (*xCommon)(DiffBuilder*,const DLine*);
  void (*xInsert)(DiffBuilder*,const DLine*);
  void (*xDelete)(DiffBuilder*,const DLine*);
  void (*xReplace)(DiffBuilder*,const DLine*,const DLine*);
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
    blob_append_char(p->pOut, ' ');
    blob_append_tcl_literal(p->pOut, pX->z + x, chng.a[i].iStart1 - x);
    x = chng.a[i].iStart1;
    blob_append_char(p->pOut, ' ');
    blob_append_tcl_literal(p->pOut, pX->z + x, chng.a[i].iLen1);
    x += chng.a[i].iLen1;
    blob_append_char(p->pOut, ' ');
    blob_append_tcl_literal(p->pOut, 
                         pY->z + chng.a[i].iStart2, chng.a[i].iLen2);
  }
  if( x<pX->n ){
    blob_append_char(p->pOut, ' ');
    blob_append_tcl_literal(p->pOut, pX->z + x, pX->n - x);
  }
  blob_append_char(p->pOut, '\n');







|







1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
    blob_append_char(p->pOut, ' ');
    blob_append_tcl_literal(p->pOut, pX->z + x, chng.a[i].iStart1 - x);
    x = chng.a[i].iStart1;
    blob_append_char(p->pOut, ' ');
    blob_append_tcl_literal(p->pOut, pX->z + x, chng.a[i].iLen1);
    x += chng.a[i].iLen1;
    blob_append_char(p->pOut, ' ');
    blob_append_tcl_literal(p->pOut,
                         pY->z + chng.a[i].iStart2, chng.a[i].iLen2);
  }
  if( x<pX->n ){
    blob_append_char(p->pOut, ' ');
    blob_append_tcl_literal(p->pOut, pX->z + x, pX->n - x);
  }
  blob_append_char(p->pOut, '\n');
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
    }
    blob_append_json_literal(p->pOut, pX->z + x, chng.a[i].iStart1 - x);
    x = chng.a[i].iStart1;
    blob_append_char(p->pOut, ',');
    blob_append_json_literal(p->pOut, pX->z + x, chng.a[i].iLen1);
    x += chng.a[i].iLen1;
    blob_append_char(p->pOut, ',');
    blob_append_json_literal(p->pOut, 
                         pY->z + chng.a[i].iStart2, chng.a[i].iLen2);
  }
  blob_append_char(p->pOut, ',');
  blob_append_json_literal(p->pOut, pX->z + x, pX->n - x);
  blob_append(p->pOut, "],\n",3);
}
static void dfjsonEnd(DiffBuilder *p){







|







1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
    }
    blob_append_json_literal(p->pOut, pX->z + x, chng.a[i].iStart1 - x);
    x = chng.a[i].iStart1;
    blob_append_char(p->pOut, ',');
    blob_append_json_literal(p->pOut, pX->z + x, chng.a[i].iLen1);
    x += chng.a[i].iLen1;
    blob_append_char(p->pOut, ',');
    blob_append_json_literal(p->pOut,
                         pY->z + chng.a[i].iStart2, chng.a[i].iLen2);
  }
  blob_append_char(p->pOut, ',');
  blob_append_json_literal(p->pOut, pX->z + x, pX->n - x);
  blob_append(p->pOut, "],\n",3);
}
static void dfjsonEnd(DiffBuilder *p){
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
  /* "+" marks for the separator on inserted lines */
  for(i=0; i<p->nPending; i++) blob_append(&p->aCol[1], "+\n", 2);

  /* Text of the inserted lines */
  blob_append(&p->aCol[2], "<ins>", 5);
  blob_append_xfer(&p->aCol[2], &p->aCol[4]);
  blob_append(&p->aCol[2], "</ins>", 6);
  
  p->nPending = 0;
}
static void dfunifiedFinishRow(DiffBuilder *p){
  dfunifiedFinishDelete(p);
  dfunifiedFinishInsert(p);
  if( blob_size(&p->aCol[0])==0 ) return;
  blob_append(p->pOut, "</pre></td><td class=\"diffln difflnr\"><pre>\n", -1);







|







1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
  /* "+" marks for the separator on inserted lines */
  for(i=0; i<p->nPending; i++) blob_append(&p->aCol[1], "+\n", 2);

  /* Text of the inserted lines */
  blob_append(&p->aCol[2], "<ins>", 5);
  blob_append_xfer(&p->aCol[2], &p->aCol[4]);
  blob_append(&p->aCol[2], "</ins>", 6);

  p->nPending = 0;
}
static void dfunifiedFinishRow(DiffBuilder *p){
  dfunifiedFinishDelete(p);
  dfunifiedFinishInsert(p);
  if( blob_size(&p->aCol[0])==0 ) return;
  blob_append(p->pOut, "</pre></td><td class=\"diffln difflnr\"><pre>\n", -1);
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
    aBig = aRight;
    nBig = nRight;
  }
  iDivBig = nBig/2;
  iDivSmall = nSmall/2;

  if( pCfg->diffFlags & DIFF_DEBUG ){
    fossil_print("  Divide at [%.*s]\n", 
                 aBig[iDivBig].n, aBig[iDivBig].z);
  }

  bestScore = 10000;
  for(i=0; i<nSmall; i++){
    score = match_dline(aBig+iDivBig, aSmall+i) + abs(i-nSmall/2)*2;
    if( score<bestScore ){







|







2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
    aBig = aRight;
    nBig = nRight;
  }
  iDivBig = nBig/2;
  iDivSmall = nSmall/2;

  if( pCfg->diffFlags & DIFF_DEBUG ){
    fossil_print("  Divide at [%.*s]\n",
                 aBig[iDivBig].n, aBig[iDivBig].z);
  }

  bestScore = 10000;
  for(i=0; i<nSmall; i++){
    score = match_dline(aBig+iDivBig, aSmall+i) + abs(i-nSmall/2)*2;
    if( score<bestScore ){
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
  B = p->aTo;
  R = p->aEdit;
  mxr = p->nEdit;
  while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }

  for(r=0; r<mxr; r += 3*nr){
    /* Figure out how many triples to show in a single block */
    for(nr=1; R[r+nr*3]>0 && R[r+nr*3]<(int)nContext*2; nr++){}

    /* If there is a regex, skip this block (generate no diff output)
    ** if the regex matches or does not match both insert and delete.
    ** Only display the block if one side matches but the other side does
    ** not.
    */
    if( pCfg->pRe ){







|







2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
  B = p->aTo;
  R = p->aEdit;
  mxr = p->nEdit;
  while( mxr>2 && R[mxr-1]==0 && R[mxr-2]==0 ){ mxr -= 3; }

  for(r=0; r<mxr; r += 3*nr){
    /* Figure out how many triples to show in a single block */
    for(nr=1; 3*nr<mxr && R[r+nr*3]>0 && R[r+nr*3]<(int)nContext*2; nr++){}

    /* If there is a regex, skip this block (generate no diff output)
    ** if the regex matches or does not match both insert and delete.
    ** Only display the block if one side matches but the other side does
    ** not.
    */
    if( pCfg->pRe ){
3158
3159
3160
3161
3162
3163
3164

3165
3166
3167
3168
3169
3170
3171
  if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
    pCfg->wColumn = f;
  }
  if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO;
  if( find_option("noopt",0,0)!=0 ) diffFlags |= DIFF_NOOPT;
  if( find_option("numstat",0,0)!=0 ) diffFlags |= DIFF_NUMSTAT;
  if( find_option("versions","h",0)!=0 ) diffFlags |= DIFF_SHOW_VERS;

  if( find_option("invert",0,0)!=0 ) diffFlags |= DIFF_INVERT;
  if( find_option("brief",0,0)!=0 ) diffFlags |= DIFF_BRIEF;
  if( find_option("internal","i",0)==0
   && (diffFlags & (DIFF_HTML|DIFF_TCL|DIFF_DEBUG|DIFF_JSON))==0
  ){
    pCfg->zDiffCmd = find_option("command", 0, 1);
    if( pCfg->zDiffCmd==0 ) pCfg->zDiffCmd = diff_command_external(isGDiff);







>







3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
  if( (z = find_option("width","W",1))!=0 && (f = atoi(z))>0 ){
    pCfg->wColumn = f;
  }
  if( find_option("linenum","n",0)!=0 ) diffFlags |= DIFF_LINENO;
  if( find_option("noopt",0,0)!=0 ) diffFlags |= DIFF_NOOPT;
  if( find_option("numstat",0,0)!=0 ) diffFlags |= DIFF_NUMSTAT;
  if( find_option("versions","h",0)!=0 ) diffFlags |= DIFF_SHOW_VERS;
  if( find_option("dark",0,0)!=0 ) diffFlags |= DIFF_DARKMODE;
  if( find_option("invert",0,0)!=0 ) diffFlags |= DIFF_INVERT;
  if( find_option("brief",0,0)!=0 ) diffFlags |= DIFF_BRIEF;
  if( find_option("internal","i",0)==0
   && (diffFlags & (DIFF_HTML|DIFF_TCL|DIFF_DEBUG|DIFF_JSON))==0
  ){
    pCfg->zDiffCmd = find_option("command", 0, 1);
    if( pCfg->zDiffCmd==0 ) pCfg->zDiffCmd = diff_command_external(isGDiff);
3482
3483
3484
3485
3486
3487
3488
3489

3490
3491
3492
3493
3494
3495
3496
    }
    p->nVers++;
    cnt++;
  }

  if( p->nVers==0 ){
    if( zRevision ){
      fossil_fatal("file %s does not exist in check-in %s", zFilename, zRevision);

    }else{
      fossil_fatal("no history for file: %s", zFilename);
    }
  }

  db_finalize(&q);
  db_end_transaction(0);







|
>







3491
3492
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
    }
    p->nVers++;
    cnt++;
  }

  if( p->nVers==0 ){
    if( zRevision ){
      fossil_fatal("file %s does not exist in check-in %s",
                   zFilename, zRevision);
    }else{
      fossil_fatal("no history for file: %s", zFilename);
    }
  }

  db_finalize(&q);
  db_end_transaction(0);
Changes to src/diff.tcl.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 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 {
  TITLE      {Fossil Diff}
  LN_COL_BG  #dddddd
  LN_COL_FG  #444444
  TXT_COL_BG #ffffff
  TXT_COL_FG #000000
  MKR_COL_BG #444444
  MKR_COL_FG #dddddd












|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 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 {
  TITLE      {Fossil Diff}
  LN_COL_BG  #dddddd
  LN_COL_FG  #444444
  TXT_COL_BG #ffffff
  TXT_COL_FG #000000
  MKR_COL_BG #444444
  MKR_COL_FG #dddddd
30
31
32
33
34
35
36































37
38
39
40
41
42
43
  ERR_FG     #ee0000
  PADX       5
  WIDTH      80
  HEIGHT     45
  LB_HEIGHT  25
}
































if {![namespace exists ttk]} {
  interp alias {} ::ttk::scrollbar {} ::scrollbar
  interp alias {} ::ttk::menubutton {} ::menubutton
}

proc dehtml {x} {
  set x [regsub -all {<[^>]*>} $x {}]







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







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
  ERR_FG     #ee0000
  PADX       5
  WIDTH      80
  HEIGHT     45
  LB_HEIGHT  25
}

array set CFG_dark {
  TITLE      {Fossil Diff}
  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    #6a6afc
  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 {}]
Changes to src/diffcmd.c.
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
  }
  return 0;
}

/*
** Print details about the compared versions - possibly the working directory
** or the undo buffer.  For check-ins, show hash and commit time.
** 
** This is intended primarily to go into the "header garbage" that is ignored
** by patch(1).
**
** zFrom and zTo are interpreted as symbolic version names, unless they
** start with '(', in which case they are printed directly.
*/
void diff_print_versions(const char *zFrom, const char *zTo, DiffConfig *pCfg){







|







111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
  }
  return 0;
}

/*
** Print details about the compared versions - possibly the working directory
** or the undo buffer.  For check-ins, show hash and commit time.
**
** This is intended primarily to go into the "header garbage" that is ignored
** by patch(1).
**
** zFrom and zTo are interpreted as symbolic version names, unless they
** start with '(', in which case they are printed directly.
*/
void diff_print_versions(const char *zFrom, const char *zTo, DiffConfig *pCfg){
159
160
161
162
163
164
165



166
167
168
169
170
171
172
void diff_print_filenames(
  const char *zLeft,      /* Name of the left file */
  const char *zRight,     /* Name of the right file */
  DiffConfig *pCfg,       /* Diff configuration */
  Blob *pOut              /* Write to this blob, or stdout of this is NULL */
){
  u64 diffFlags = pCfg->diffFlags;



  if( diffFlags & (DIFF_BRIEF|DIFF_RAW) ){
    /* no-op */
  }else if( diffFlags & DIFF_DEBUG ){
    blob_appendf(pOut, "FILE-LEFT   %s\nFILE-RIGHT  %s\n", zLeft, zRight);
  }else if( diffFlags & DIFF_WEBPAGE ){
    if( fossil_strcmp(zLeft,zRight)==0 ){
      blob_appendf(pOut,"<h1>%h</h1>\n", zLeft);







>
>
>







159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
void diff_print_filenames(
  const char *zLeft,      /* Name of the left file */
  const char *zRight,     /* Name of the right file */
  DiffConfig *pCfg,       /* Diff configuration */
  Blob *pOut              /* Write to this blob, or stdout of this is NULL */
){
  u64 diffFlags = pCfg->diffFlags;
  /* Standardize on /dev/null, regardless of platform. */
  if( pCfg->diffFlags & DIFF_FILE_ADDED ) zLeft = "/dev/null";
  if( pCfg->diffFlags & DIFF_FILE_DELETED ) zRight = "/dev/null";
  if( diffFlags & (DIFF_BRIEF|DIFF_RAW) ){
    /* no-op */
  }else if( diffFlags & DIFF_DEBUG ){
    blob_appendf(pOut, "FILE-LEFT   %s\nFILE-RIGHT  %s\n", zLeft, zRight);
  }else if( diffFlags & DIFF_WEBPAGE ){
    if( fossil_strcmp(zLeft,zRight)==0 ){
      blob_appendf(pOut,"<h1>%h</h1>\n", zLeft);
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
  }else{
    blob_appendf(pOut, "--- %s\n+++ %s\n", zLeft, zRight);
  }
}


/*
** Default header text for diff with --webpage
*/
static const char zWebpageHdr[] = 
@ <!DOCTYPE html>
@ <html>
@ <head>
@ <meta charset="UTF-8">
@ <style>
@ body {
@    background-color: white;







|

|







214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
  }else{
    blob_appendf(pOut, "--- %s\n+++ %s\n", zLeft, zRight);
  }
}


/*
** Default header texts for diff with --webpage
*/
static const char zWebpageHdr[] =
@ <!DOCTYPE html>
@ <html>
@ <head>
@ <meta charset="UTF-8">
@ <style>
@ body {
@    background-color: white;
308
309
310
311
312
313
314





























315
316
317
318
319










































































































320
321
322
323
324
325
326
327
@   font-weight: bold;
@ }
@ td.difftxt ins > ins.edit {
@   background-color: #c0c0ff;
@   text-decoration: none;
@   font-weight: bold;
@ }





























@ 
@ </style>
@ </head>
@ <body>
;










































































































const char zWebpageEnd[] = 
@ </body>
@ </html>
;

/*
** State variables used by the --browser option for diff.  These must
** be static variables, not elements of DiffConfig, since they are







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




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







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
@   font-weight: bold;
@ }
@ td.difftxt ins > ins.edit {
@   background-color: #c0c0ff;
@   text-decoration: none;
@   font-weight: bold;
@ }
@ @media (prefers-color-scheme: dark) {
@   body {
@     background-color: #353535;
@     color: #ffffff;
@   }
@   td.diffln ins {
@     background-color: #559855;
@     color: #000000;
@   }
@   td.diffln del {
@     background-color: #cc5555;
@     color: #000000;
@   }
@   td.difftxt del {
@     background-color: #f9cfcf;
@     color: #000000;
@   }
@     td.difftxt del > del {
@     background-color: #cc5555;
@     color: #000000;
@   }
@   td.difftxt ins {
@     background-color: #a2dbb2;
@     color: #000000;
@   }
@   td.difftxt ins > ins {
@     background-color: #559855;
@   }
@ }
@
@ </style>
@ </head>
@ <body>
;
static const char zWebpageHdrDark[] =
@ <!DOCTYPE html>
@ <html>
@ <head>
@ <meta charset="UTF-8">
@ <style>
@ body {
@    background-color: #353535;
@    color: #ffffff;
@ }
@ h1 {
@   font-size: 150%;
@ }
@
@ table.diff {
@   width: 100%;
@   border-spacing: 0;
@   border: 1px solid black;
@   line-height: inherit;
@   font-size: inherit;
@ }
@ table.diff td {
@   vertical-align: top;
@   line-height: inherit;
@   font-size: inherit;
@ }
@ 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;
@ }
@ td.difftxt pre {
@   overflow-x: auto;
@ }
@ td.diffln ins {
@   background-color: #559855;
@   color: #000000;
@   text-decoration: none;
@   line-height: inherit;
@   font-size: inherit;
@ }
@ td.diffln del {
@   background-color: #cc5555;
@   color: #000000;
@   text-decoration: none;
@   line-height: inherit;
@   font-size: inherit;
@ }
@ td.difftxt del {
@   background-color: #f9cfcf;
@   color: #000000;
@   text-decoration: none;
@   line-height: inherit;
@   font-size: inherit;
@ }
@ td.difftxt del > del {
@   background-color: #cc5555;
@   color: #000000;
@   text-decoration: none;
@   font-weight: bold;
@ }
@ td.difftxt del > del.edit {
@   background-color: #c0c0ff;
@   text-decoration: none;
@   font-weight: bold;
@ }
@ td.difftxt ins {
@   background-color: #a2dbb2;
@   color: #000000;
@   text-decoration: none;
@   line-height: inherit;
@   font-size: inherit;
@ }
@ td.difftxt ins > ins {
@   background-color: #559855;
@   text-decoration: none;
@   font-weight: bold;
@ }
@ td.difftxt ins > ins.edit {
@   background-color: #c0c0ff;
@   text-decoration: none;
@   font-weight: bold;
@ }
@
@ </style>
@ </head>
@ <body>
;
const char zWebpageEnd[] =
@ </body>
@ </html>
;

/*
** State variables used by the --browser option for diff.  These must
** be static variables, not elements of DiffConfig, since they are
376
377
378
379
380
381
382

383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
#ifndef _WIN32
    signal(SIGINT, diff_www_interrupt);
#else
    SetConsoleCtrlHandler(diff_console_ctrl_handler, TRUE);
#endif
  }
  if( (pCfg->diffFlags & DIFF_WEBPAGE)!=0 ){

    fossil_print("%s",zWebpageHdr);
    fflush(stdout);
  }
}

/* Do any final output required by a diff and complete the diff
** process.
**
** For --browser and --webpage, output any javascript required by 
** the diff.  (Currently JS is only needed for side-by-side diffs).
**
** For --browser, close the connection to the temporary file, then
** launch a web browser to view the file.  After a delay
** of FOSSIL_BROWSER_DIFF_DELAY milliseconds, delete the temp file.
*/
void diff_end(DiffConfig *pCfg, int nErr){







>
|







|







514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
#ifndef _WIN32
    signal(SIGINT, diff_www_interrupt);
#else
    SetConsoleCtrlHandler(diff_console_ctrl_handler, TRUE);
#endif
  }
  if( (pCfg->diffFlags & DIFF_WEBPAGE)!=0 ){
    fossil_print("%s",(pCfg->diffFlags & DIFF_DARKMODE)!=0 ? zWebpageHdrDark :
                                                             zWebpageHdr);
    fflush(stdout);
  }
}

/* Do any final output required by a diff and complete the diff
** process.
**
** For --browser and --webpage, output any javascript required by
** the diff.  (Currently JS is only needed for side-by-side diffs).
**
** For --browser, close the connection to the temporary file, then
** launch a web browser to view the file.  After a delay
** of FOSSIL_BROWSER_DIFF_DELAY milliseconds, delete the temp file.
*/
void diff_end(DiffConfig *pCfg, int nErr){
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
  if( pCfg->zDiffCmd==0 ){
    Blob out;                 /* Diff output text */
    Blob file2;               /* Content of zFile2 */
    const char *zName2;       /* Name of zFile2 for display */

    /* Read content of zFile2 into memory */
    blob_zero(&file2);
    if( file_size(zFile2, ExtFILE)<0 ){
      zName2 = NULL_DEVICE;
    }else{
      blob_read_from_file(&file2, zFile2, ExtFILE);
      zName2 = zName;
    }

    /* Compute and output the differences */







|







575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
  if( pCfg->zDiffCmd==0 ){
    Blob out;                 /* Diff output text */
    Blob file2;               /* Content of zFile2 */
    const char *zName2;       /* Name of zFile2 for display */

    /* Read content of zFile2 into memory */
    blob_zero(&file2);
    if( pCfg->diffFlags & DIFF_FILE_DELETED || file_size(zFile2, ExtFILE)<0 ){
      zName2 = NULL_DEVICE;
    }else{
      blob_read_from_file(&file2, zFile2, ExtFILE);
      zName2 = zName;
    }

    /* Compute and output the differences */
467
468
469
470
471
472
473

474
475
476
477
478
479
480
    }

    /* Release memory resources */
    blob_reset(&file2);
  }else{
    Blob nameFile1;    /* Name of temporary file to old pFile1 content */
    Blob cmd;          /* Text of command to run */


    if( (pCfg->diffFlags & DIFF_INCBINARY)==0 ){
      Blob file2;
      if( looks_like_binary(pFile1) ){
        fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
        return;
      }







>







606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
    }

    /* Release memory resources */
    blob_reset(&file2);
  }else{
    Blob nameFile1;    /* Name of temporary file to old pFile1 content */
    Blob cmd;          /* Text of command to run */
    int useTempfile = 1;

    if( (pCfg->diffFlags & DIFF_INCBINARY)==0 ){
      Blob file2;
      if( looks_like_binary(pFile1) ){
        fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
        return;
      }
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
      }
      blob_reset(&file2);
    }

    /* Construct a temporary file to hold pFile1 based on the name of
    ** zFile2 */
    file_tempname(&nameFile1, zFile2, "orig");









    blob_write_to_file(pFile1, blob_str(&nameFile1));

    /* Construct the external diff command */
    blob_zero(&cmd);
    blob_append(&cmd, pCfg->zDiffCmd, -1);
    if( pCfg->diffFlags & DIFF_INVERT ){
      blob_append_escaped_arg(&cmd, zFile2, 1);
      blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1);
    }else{
      blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1);
      blob_append_escaped_arg(&cmd, zFile2, 1);
    }

    /* Run the external diff command */
    fossil_system(blob_str(&cmd));

    /* Delete the temporary file and clean up memory used */
    file_delete(blob_str(&nameFile1));
    blob_reset(&nameFile1);
    blob_reset(&cmd);
  }
}

/*
** Show the difference between two files, both in memory.







>
>
>
>
>
>
>
>
>
|
















|







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

    /* Construct a temporary file to hold pFile1 based on the name of
    ** zFile2 */
    file_tempname(&nameFile1, zFile2, "orig");
#if !defined(_WIN32)
    /* On Unix, use /dev/null for added or deleted files. */
    if( pCfg->diffFlags & DIFF_FILE_ADDED ){
      blob_init(&nameFile1, NULL_DEVICE, -1);
      useTempfile = 0;
    }else if( pCfg->diffFlags & DIFF_FILE_DELETED ){
      zFile2 = NULL_DEVICE;
    }
#endif
    if( useTempfile ) blob_write_to_file(pFile1, blob_str(&nameFile1));

    /* Construct the external diff command */
    blob_zero(&cmd);
    blob_append(&cmd, pCfg->zDiffCmd, -1);
    if( pCfg->diffFlags & DIFF_INVERT ){
      blob_append_escaped_arg(&cmd, zFile2, 1);
      blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1);
    }else{
      blob_append_escaped_arg(&cmd, blob_str(&nameFile1), 1);
      blob_append_escaped_arg(&cmd, zFile2, 1);
    }

    /* Run the external diff command */
    fossil_system(blob_str(&cmd));

    /* Delete the temporary file and clean up memory used */
    if( useTempfile ) file_delete(blob_str(&nameFile1));
    blob_reset(&nameFile1);
    blob_reset(&cmd);
  }
}

/*
** Show the difference between two files, both in memory.
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

    /* Release memory resources */
    blob_reset(&out);
  }else{
    Blob cmd;
    Blob temp1;
    Blob temp2;



    if( (pCfg->diffFlags & DIFF_INCBINARY)==0 ){
      if( looks_like_binary(pFile1) || looks_like_binary(pFile2) ){
        fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
        return;
      }
      if( pCfg->zBinGlob ){
        Glob *pBinary = glob_create(pCfg->zBinGlob);
        if( glob_match(pBinary, zName) ){
          fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
          glob_free(pBinary);
          return;
        }
        glob_free(pBinary);
      }
    }

    /* Construct a temporary file names */
    file_tempname(&temp1, zName, "before");
    file_tempname(&temp2, zName, "after");










    blob_write_to_file(pFile1, blob_str(&temp1));
    blob_write_to_file(pFile2, blob_str(&temp2));

    /* Construct the external diff command */
    blob_zero(&cmd);
    blob_append(&cmd, pCfg->zDiffCmd, -1);
    blob_append_escaped_arg(&cmd, blob_str(&temp1), 1);
    blob_append_escaped_arg(&cmd, blob_str(&temp2), 1);

    /* Run the external diff command */
    fossil_system(blob_str(&cmd));

    /* Delete the temporary file and clean up memory used */
    file_delete(blob_str(&temp1));
    file_delete(blob_str(&temp2));

    blob_reset(&temp1);
    blob_reset(&temp2);
    blob_reset(&cmd);
  }
}








>
>

















|


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











|
|







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

    /* Release memory resources */
    blob_reset(&out);
  }else{
    Blob cmd;
    Blob temp1;
    Blob temp2;
    int useTempfile1 = 1;
    int useTempfile2 = 1;

    if( (pCfg->diffFlags & DIFF_INCBINARY)==0 ){
      if( looks_like_binary(pFile1) || looks_like_binary(pFile2) ){
        fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
        return;
      }
      if( pCfg->zBinGlob ){
        Glob *pBinary = glob_create(pCfg->zBinGlob);
        if( glob_match(pBinary, zName) ){
          fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
          glob_free(pBinary);
          return;
        }
        glob_free(pBinary);
      }
    }

    /* Construct temporary file names */
    file_tempname(&temp1, zName, "before");
    file_tempname(&temp2, zName, "after");
#if !defined(_WIN32)
    /* On Unix, use /dev/null for added or deleted files. */
    if( pCfg->diffFlags & DIFF_FILE_ADDED ){
      useTempfile1 = 0;
      blob_init(&temp1, NULL_DEVICE, -1);
    }else if( pCfg->diffFlags & DIFF_FILE_DELETED ){
      useTempfile2 = 0;
      blob_init(&temp2, NULL_DEVICE, -1);
    }
#endif
    if( useTempfile1 ) blob_write_to_file(pFile1, blob_str(&temp1));
    if( useTempfile2 ) blob_write_to_file(pFile2, blob_str(&temp2));

    /* Construct the external diff command */
    blob_zero(&cmd);
    blob_append(&cmd, pCfg->zDiffCmd, -1);
    blob_append_escaped_arg(&cmd, blob_str(&temp1), 1);
    blob_append_escaped_arg(&cmd, blob_str(&temp2), 1);

    /* Run the external diff command */
    fossil_system(blob_str(&cmd));

    /* Delete the temporary file and clean up memory used */
    if( useTempfile1 ) file_delete(blob_str(&temp1));
    if( useTempfile2 ) file_delete(blob_str(&temp2));

    blob_reset(&temp1);
    blob_reset(&temp2);
    blob_reset(&cmd);
  }
}

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
      blob_zero(&fname);
      file_relative_name(zPathname, &fname, 1);
    }else{
      blob_set(&fname, g.zLocalRoot);
      blob_append(&fname, zPathname, -1);
    }
    zFullName = blob_str(&fname);

    if( isDeleted ){
      if( !isNumStat ){ fossil_print("DELETED  %s\n", zPathname); }

      if( !asNewFile ){ showDiff = 0; zFullName = NULL_DEVICE; }
    }else if( file_access(zFullName, F_OK) ){
      if( !isNumStat ){ fossil_print("MISSING  %s\n", zPathname); }
      if( !asNewFile ){ showDiff = 0; }
    }else if( isNew ){
      if( !isNumStat ){ fossil_print("ADDED    %s\n", zPathname); }

      srcid = 0;
      if( !asNewFile ){ showDiff = 0; }
    }else if( isChnged==3 ){
      if( !isNumStat ){ fossil_print("ADDED_BY_MERGE %s\n", zPathname); }

      srcid = 0;
      if( !asNewFile ){ showDiff = 0; }
    }else if( isChnged==5 ){
      if( !isNumStat ){ fossil_print("ADDED_BY_INTEGRATE %s\n", zPathname); }

      srcid = 0;
      if( !asNewFile ){ showDiff = 0; }
    }
    if( showDiff ){
      Blob content;
      if( !isLink != !file_islink(zFullName) ){
        diff_print_index(zPathname, pCfg, 0);
        diff_print_filenames(zPathname, zPathname, pCfg, 0);
        fossil_print("%s",DIFF_CANNOT_COMPUTE_SYMLINK);
        continue;
      }
      if( srcid>0 ){
        content_get(srcid, &content);
      }else{
        blob_zero(&content);
      }


      if( isChnged==0 || !file_same_as_blob(&content, zFullName) ){

        diff_print_index(zPathname, pCfg, pOut);
        diff_file(&content, zFullName, zPathname, pCfg, pOut);
      }
      blob_reset(&content);
    }
    blob_reset(&fname);
  }







>


>






>




>




>
















>
>
|
>







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
      blob_zero(&fname);
      file_relative_name(zPathname, &fname, 1);
    }else{
      blob_set(&fname, g.zLocalRoot);
      blob_append(&fname, zPathname, -1);
    }
    zFullName = blob_str(&fname);
    pCfg->diffFlags &= (~DIFF_FILE_MASK);
    if( isDeleted ){
      if( !isNumStat ){ fossil_print("DELETED  %s\n", zPathname); }
      pCfg->diffFlags |= DIFF_FILE_DELETED;
      if( !asNewFile ){ showDiff = 0; zFullName = NULL_DEVICE; }
    }else if( file_access(zFullName, F_OK) ){
      if( !isNumStat ){ fossil_print("MISSING  %s\n", zPathname); }
      if( !asNewFile ){ showDiff = 0; }
    }else if( isNew ){
      if( !isNumStat ){ fossil_print("ADDED    %s\n", zPathname); }
      pCfg->diffFlags |= DIFF_FILE_ADDED;
      srcid = 0;
      if( !asNewFile ){ showDiff = 0; }
    }else if( isChnged==3 ){
      if( !isNumStat ){ fossil_print("ADDED_BY_MERGE %s\n", zPathname); }
      pCfg->diffFlags |= DIFF_FILE_ADDED;
      srcid = 0;
      if( !asNewFile ){ showDiff = 0; }
    }else if( isChnged==5 ){
      if( !isNumStat ){ fossil_print("ADDED_BY_INTEGRATE %s\n", zPathname); }
      pCfg->diffFlags |= DIFF_FILE_ADDED;
      srcid = 0;
      if( !asNewFile ){ showDiff = 0; }
    }
    if( showDiff ){
      Blob content;
      if( !isLink != !file_islink(zFullName) ){
        diff_print_index(zPathname, pCfg, 0);
        diff_print_filenames(zPathname, zPathname, pCfg, 0);
        fossil_print("%s",DIFF_CANNOT_COMPUTE_SYMLINK);
        continue;
      }
      if( srcid>0 ){
        content_get(srcid, &content);
      }else{
        blob_zero(&content);
      }
      if( isChnged==0
       || pCfg->diffFlags & DIFF_FILE_DELETED
       || !file_same_as_blob(&content, zFullName)
      ){
        diff_print_index(zPathname, pCfg, pOut);
        diff_file(&content, zFullName, zPathname, pCfg, pOut);
      }
      blob_reset(&content);
    }
    blob_reset(&fname);
  }
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
){
  Stmt q;
  Blob content;
  db_prepare(&q, "SELECT pathname, content FROM undo");
  blob_init(&content, 0, 0);
  if( (pCfg->diffFlags & DIFF_SHOW_VERS)!=0 ){
    diff_print_versions("(undo)", "(workdir)", pCfg);
  }  
  while( db_step(&q)==SQLITE_ROW ){
    char *zFullName;
    const char *zFile = (const char*)db_column_text(&q, 0);
    if( !file_dir_match(pFileDir, zFile) ) continue;
    zFullName = mprintf("%s%s", g.zLocalRoot, zFile);
    db_column_blob(&q, 1, &content);
    diff_file(&content, zFullName, zFile, pCfg, 0);







|







945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
){
  Stmt q;
  Blob content;
  db_prepare(&q, "SELECT pathname, content FROM undo");
  blob_init(&content, 0, 0);
  if( (pCfg->diffFlags & DIFF_SHOW_VERS)!=0 ){
    diff_print_versions("(undo)", "(workdir)", pCfg);
  }
  while( db_step(&q)==SQLITE_ROW ){
    char *zFullName;
    const char *zFile = (const char*)db_column_text(&q, 0);
    if( !file_dir_match(pFileDir, zFile) ) continue;
    zFullName = mprintf("%s%s", g.zLocalRoot, zFile);
    db_column_blob(&q, 1, &content);
    diff_file(&content, zFullName, zFile, pCfg, 0);
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
  manifest_file_rewind(pFrom);
  pFromFile = manifest_file_next(pFrom,0);
  pTo = manifest_get_by_name(zTo, 0);
  manifest_file_rewind(pTo);
  pToFile = manifest_file_next(pTo,0);
  if( (pCfg->diffFlags & DIFF_SHOW_VERS)!=0 ){
    diff_print_versions(zFrom, zTo, pCfg);
  }    
  while( pFromFile || pToFile ){
    int cmp;
    if( pFromFile==0 ){
      cmp = +1;
    }else if( pToFile==0 ){
      cmp = -1;
    }else{
      cmp = fossil_strcmp(pFromFile->zName, pToFile->zName);
    }

    if( cmp<0 ){
      if( file_dir_match(pFileDir, pFromFile->zName) ){
        if( (pCfg->diffFlags & (DIFF_NUMSTAT|DIFF_HTML))==0 ){
          fossil_print("DELETED %s\n", pFromFile->zName);
        }

        if( asNewFlag ){
          diff_manifest_entry(pFromFile, 0, pCfg);
        }
      }
      pFromFile = manifest_file_next(pFrom,0);
    }else if( cmp>0 ){
      if( file_dir_match(pFileDir, pToFile->zName) ){
        if( (pCfg->diffFlags &
             (DIFF_NUMSTAT|DIFF_HTML|DIFF_TCL|DIFF_JSON))==0 ){
          fossil_print("ADDED   %s\n", pToFile->zName);
        }

        if( asNewFlag ){
          diff_manifest_entry(0, pToFile, pCfg);
        }
      }
      pToFile = manifest_file_next(pTo,0);
    }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
      /* No changes */







|









>





>











>







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
  manifest_file_rewind(pFrom);
  pFromFile = manifest_file_next(pFrom,0);
  pTo = manifest_get_by_name(zTo, 0);
  manifest_file_rewind(pTo);
  pToFile = manifest_file_next(pTo,0);
  if( (pCfg->diffFlags & DIFF_SHOW_VERS)!=0 ){
    diff_print_versions(zFrom, zTo, pCfg);
  }
  while( pFromFile || pToFile ){
    int cmp;
    if( pFromFile==0 ){
      cmp = +1;
    }else if( pToFile==0 ){
      cmp = -1;
    }else{
      cmp = fossil_strcmp(pFromFile->zName, pToFile->zName);
    }
    pCfg->diffFlags &= (~DIFF_FILE_MASK);
    if( cmp<0 ){
      if( file_dir_match(pFileDir, pFromFile->zName) ){
        if( (pCfg->diffFlags & (DIFF_NUMSTAT|DIFF_HTML))==0 ){
          fossil_print("DELETED %s\n", pFromFile->zName);
        }
        pCfg->diffFlags |= DIFF_FILE_DELETED;
        if( asNewFlag ){
          diff_manifest_entry(pFromFile, 0, pCfg);
        }
      }
      pFromFile = manifest_file_next(pFrom,0);
    }else if( cmp>0 ){
      if( file_dir_match(pFileDir, pToFile->zName) ){
        if( (pCfg->diffFlags &
             (DIFF_NUMSTAT|DIFF_HTML|DIFF_TCL|DIFF_JSON))==0 ){
          fossil_print("ADDED   %s\n", pToFile->zName);
        }
        pCfg->diffFlags |= DIFF_FILE_ADDED;
        if( asNewFlag ){
          diff_manifest_entry(0, pToFile, pCfg);
        }
      }
      pToFile = manifest_file_next(pTo,0);
    }else if( fossil_strcmp(pFromFile->zUuid, pToFile->zUuid)==0 ){
      /* No changes */
954
955
956
957
958
959
960

961
962
963
964
965
966
967
*/
void diff_tk(const char *zSubCmd, int firstArg){
  int i;
  Blob script;
  const char *zTempFile = 0;
  char *zCmd;
  const char *zTclsh;

  blob_zero(&script);
  blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl -i -v",
               g.nameOfExe, zSubCmd);
  find_option("tcl",0,0);
  find_option("html",0,0);
  find_option("side-by-side","y",0);
  find_option("internal","i",0);







>







1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
*/
void diff_tk(const char *zSubCmd, int firstArg){
  int i;
  Blob script;
  const char *zTempFile = 0;
  char *zCmd;
  const char *zTclsh;
  int bDarkMode = find_option("dark",0,0)!=0;
  blob_zero(&script);
  blob_appendf(&script, "set fossilcmd {| \"%/\" %s -tcl -i -v",
               g.nameOfExe, zSubCmd);
  find_option("tcl",0,0);
  find_option("html",0,0);
  find_option("side-by-side","y",0);
  find_option("internal","i",0);
980
981
982
983
984
985
986

987
988
989
990
991
992
993
994
      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, "}\n%s", builtin_file("diff.tcl", 0));
  if( zTempFile ){
    blob_write_to_file(&script, zTempFile);
    fossil_print("To see diff, 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),







>
|







1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
      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("diff.tcl", 0));
  if( zTempFile ){
    blob_write_to_file(&script, zTempFile);
    fossil_print("To see diff, 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),
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
** out.  Or if the FILE arguments are omitted, show all unsaved changes
** currently in the working check-out.
**
** The default output format is a "unified patch" (the same as the
** output of "diff -u" on most unix systems).  Many alternative formats
** are available.  A few of the more useful alternatives:
**
**    --tk              Pop up a TCL/TK-based GUI to show the diff
**    --by              Show a side-by-side diff in the default web browser
**    -b                Show a linear diff in the default web browser
**    -y                Show a text side-by-side diff
**    --webpage         Format output as HTML
**    --webpage -y      HTML output in the side-by-side format
**
** The "--from VERSION" option is used to specify the source check-in







|







1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
** out.  Or if the FILE arguments are omitted, show all unsaved changes
** currently in the working check-out.
**
** The default output format is a "unified patch" (the same as the
** output of "diff -u" on most unix systems).  Many alternative formats
** are available.  A few of the more useful alternatives:
**
**    --tk              Pop up a Tcl/Tk-based GUI to show the diff
**    --by              Show a side-by-side diff in the default web browser
**    -b                Show a linear diff in the default web browser
**    -y                Show a text side-by-side diff
**    --webpage         Format output as HTML
**    --webpage -y      HTML output in the side-by-side format
**
** The "--from VERSION" option is used to specify the source check-in
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
**                               as binary
**   --branch BRANCH             Show diff of all changes on BRANCH
**   --brief                     Show filenames only
**   -b|--browser                Show the diff output in a web-browser
**   --by                        Shorthand for "--browser -y"
**   -ci|--checkin VERSION       Show diff of all changes in VERSION
**   --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

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

**   --json                      Output formatted as JSON

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







|
|
>






>

>




|
|







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
**                               as binary
**   --branch BRANCH             Show diff of all changes on BRANCH
**   --brief                     Show filenames only
**   -b|--browser                Show the diff output in a web-browser
**   --by                        Shorthand for "--browser -y"
**   -ci|--checkin VERSION       Show diff of all changes in VERSION
**   --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
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
      }
      fossil_free(pFileDir[i].zName);
    }
    fossil_free(pFileDir);
  }
  diff_end(&DCfg, 0);
  if ( DCfg.diffFlags & DIFF_NUMSTAT ){
    fossil_print("%10d %10d TOTAL over %d changed files\n", 
                 g.diffCnt[1], g.diffCnt[2], g.diffCnt[0]);
  }
}

/*
** WEBPAGE: vpatch
** URL: /vpatch?from=FROM&to=TO







|







1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
      }
      fossil_free(pFileDir[i].zName);
    }
    fossil_free(pFileDir);
  }
  diff_end(&DCfg, 0);
  if ( DCfg.diffFlags & DIFF_NUMSTAT ){
    fossil_print("%10d %10d TOTAL over %d changed files\n",
                 g.diffCnt[1], g.diffCnt[2], g.diffCnt[0]);
  }
}

/*
** WEBPAGE: vpatch
** URL: /vpatch?from=FROM&to=TO
Changes to src/dispatch.c.
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
        aIndent[iLevel] = nIndent;
        azEnd[iLevel] = zEndUL;
        if( wantP ){
          blob_append(pHtml,"<p>", 3);
          wantP = 0;
        }
        blob_append(pHtml, "<ul>\n", 5);
      }else if( isDT 
             || zHelp[nIndent]=='-'
             || hasGap(zHelp+nIndent,i-nIndent) ){
        iLevel++;
        aIndent[iLevel] = nIndent;
        azEnd[iLevel] = zEndDL;
        wantP = 0;
        blob_append(pHtml, "<blockquote><dl>\n", -1);







|







451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
        aIndent[iLevel] = nIndent;
        azEnd[iLevel] = zEndUL;
        if( wantP ){
          blob_append(pHtml,"<p>", 3);
          wantP = 0;
        }
        blob_append(pHtml, "<ul>\n", 5);
      }else if( isDT
             || zHelp[nIndent]=='-'
             || hasGap(zHelp+nIndent,i-nIndent) ){
        iLevel++;
        aIndent[iLevel] = nIndent;
        azEnd[iLevel] = zEndDL;
        wantP = 0;
        blob_append(pHtml, "<blockquote><dl>\n", -1);
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
    if( c=='[' && (x = help_is_link(zHelp+i, 100000))!=0 ){
      if( i>0 ) blob_append(pText, zHelp, i);
      zHelp += i+2;
      blob_append(pText, zHelp, x-3);
      zHelp += x-1;
      i = -1;
      continue;
    }     
  }
  if( i>0 ){
    blob_append(pText, zHelp, i);
  }      
}

/*
** Display help for all commands based on provided flags.
*/
static void display_all_help(int mask, int useHtml, int rawOut){
  int i;







|



|







545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
    if( c=='[' && (x = help_is_link(zHelp+i, 100000))!=0 ){
      if( i>0 ) blob_append(pText, zHelp, i);
      zHelp += i+2;
      blob_append(pText, zHelp, x-3);
      zHelp += x-1;
      i = -1;
      continue;
    }
  }
  if( i>0 ){
    blob_append(pText, zHelp, i);
  }
}

/*
** Display help for all commands based on provided flags.
*/
static void display_all_help(int mask, int useHtml, int rawOut){
  int i;
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
**
** Show help text for commands and pages.  Useful for proof-reading.
** Defaults to just the CLI commands.  Specify --www to see only the
** web pages, or --everything to see both commands and pages.
**
** Options:
**    -a|--aliases      Show aliases
**    -e|--everything   Show all commands and pages.  Omit aliases to 
**                      avoid duplicates.
**    -h|--html         Transform output to HTML
**    -o|--options      Show global options
**    -r|--raw          No output formatting
**    -s|--settings     Show settings
**    -t|--test         Include test- commands
**    -w|--www          Show WWW pages







|







633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
**
** Show help text for commands and pages.  Useful for proof-reading.
** Defaults to just the CLI commands.  Specify --www to see only the
** web pages, or --everything to see both commands and pages.
**
** Options:
**    -a|--aliases      Show aliases
**    -e|--everything   Show all commands and pages.  Omit aliases to
**                      avoid duplicates.
**    -h|--html         Transform output to HTML
**    -o|--options      Show global options
**    -r|--raw          No output formatting
**    -s|--settings     Show settings
**    -t|--test         Include test- commands
**    -w|--www          Show WWW pages
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
              CMDFLAG_ALIAS | CMDFLAG_SETTING | CMDFLAG_TEST;
  }
  if( find_option("settings","s",0) ){
    mask = CMDFLAG_SETTING;
  }
  if( find_option("aliases","a",0) ){
    mask = CMDFLAG_ALIAS;
  }  
  if( find_option("test","t",0) ){
    mask |= CMDFLAG_TEST;
  }
  display_all_help(mask, useHtml, rawOut);
}

/*







|







659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
              CMDFLAG_ALIAS | CMDFLAG_SETTING | CMDFLAG_TEST;
  }
  if( find_option("settings","s",0) ){
    mask = CMDFLAG_SETTING;
  }
  if( find_option("aliases","a",0) ){
    mask = CMDFLAG_ALIAS;
  }
  if( find_option("test","t",0) ){
    mask |= CMDFLAG_TEST;
  }
  display_all_help(mask, useHtml, rawOut);
}

/*
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
    iLast = FOSSIL_FIRST_CMD-1;
  }else{
    iFirst = FOSSIL_FIRST_CMD;
    iLast = MX_COMMAND-1;
  }

  while( n<nArray ){
    bestScore = mxScore;    
    for(i=iFirst; i<=iLast; i++){
      m = edit_distance(zIn, aCommand[i].zName);
      if( m<mnScore ) continue;
      if( m==mnScore ){
        azArray[n++] = aCommand[i].zName;
        if( n>=nArray ) return n;
       }else if( m<bestScore ){







|







766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
    iLast = FOSSIL_FIRST_CMD-1;
  }else{
    iFirst = FOSSIL_FIRST_CMD;
    iLast = MX_COMMAND-1;
  }

  while( n<nArray ){
    bestScore = mxScore;
    for(i=iFirst; i<=iLast; i++){
      m = edit_distance(zIn, aCommand[i].zName);
      if( m<mnScore ) continue;
      if( m==mnScore ){
        azArray[n++] = aCommand[i].zName;
        if( n>=nArray ) return n;
       }else if( m<bestScore ){
895
896
897
898
899
900
901
902

903
904
905
906
907
908
909
      @ <li><a href="%R/help?cmd=%s(z)">%s(zBoldOn)%s(z)%s(zBoldOff)</a>
      /* Output aliases */
      if( occHelp[aCommand[i].iHelp] > 1 ){
        int j;
        int aliases[MX_HELP_DUP], nAliases=0;
        for(j=0; j<occHelp[aCommand[i].iHelp]; j++){
          if( bktHelp[aCommand[i].iHelp][j] != i ){
            if( aCommand[bktHelp[aCommand[i].iHelp][j]].eCmdFlags & CMDFLAG_ALIAS ){

              aliases[nAliases++] = bktHelp[aCommand[i].iHelp][j];
            }
          }
        }
        if( nAliases>0 ){
          int k;
          @(\







|
>







895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
      @ <li><a href="%R/help?cmd=%s(z)">%s(zBoldOn)%s(z)%s(zBoldOff)</a>
      /* Output aliases */
      if( occHelp[aCommand[i].iHelp] > 1 ){
        int j;
        int aliases[MX_HELP_DUP], nAliases=0;
        for(j=0; j<occHelp[aCommand[i].iHelp]; j++){
          if( bktHelp[aCommand[i].iHelp][j] != i ){
            if( aCommand[bktHelp[aCommand[i].iHelp][j]].eCmdFlags
                & CMDFLAG_ALIAS ){
              aliases[nAliases++] = bktHelp[aCommand[i].iHelp][j];
            }
          }
        }
        if( nAliases>0 ){
          int k;
          @(\
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
  style_set_current_feature("test");
  style_header("All Help Text");
  @ <dl>
  /* Fill in help string buckets */
  for(i=0; i<MX_COMMAND; i++){
    if(aCommand[i].eCmdFlags & CMDFLAG_HIDDEN) continue;
    bktHelp[aCommand[i].iHelp][occHelp[aCommand[i].iHelp]++] = i;
  }  
  for(i=0; i<MX_COMMAND; i++){
    const char *zDesc;
    unsigned int e = aCommand[i].eCmdFlags;
    if( e & CMDFLAG_1ST_TIER ){
      zDesc = "1st tier command";
    }else if( e & CMDFLAG_2ND_TIER ){
      zDesc = "2nd tier command";







|







986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
  style_set_current_feature("test");
  style_header("All Help Text");
  @ <dl>
  /* Fill in help string buckets */
  for(i=0; i<MX_COMMAND; i++){
    if(aCommand[i].eCmdFlags & CMDFLAG_HIDDEN) continue;
    bktHelp[aCommand[i].iHelp][occHelp[aCommand[i].iHelp]++] = i;
  }
  for(i=0; i<MX_COMMAND; i++){
    const char *zDesc;
    unsigned int e = aCommand[i].eCmdFlags;
    if( e & CMDFLAG_1ST_TIER ){
      zDesc = "1st tier command";
    }else if( e & CMDFLAG_2ND_TIER ){
      zDesc = "2nd tier command";
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
        }else if( e & CMDFLAG_WEBPAGE ){
          if( e & CMDFLAG_RAWCONTENT ){
            zDesc = "raw-content web page";
          }else{
            zDesc = "web page";
          }
        }
        
        @ <dt><big><b>%s(aCommand[bktHelp[aCommand[i].iHelp][j]].zName)</b>
        @</big> (%s(zDesc))</dt>
      }
      @ <p><dd>
      help_to_html(aCommand[i].zHelp, cgi_output_blob());
      @ </dd><p>
      occHelp[aCommand[i].iHelp] = 0;







|







1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
        }else if( e & CMDFLAG_WEBPAGE ){
          if( e & CMDFLAG_RAWCONTENT ){
            zDesc = "raw-content web page";
          }else{
            zDesc = "web page";
          }
        }

        @ <dt><big><b>%s(aCommand[bktHelp[aCommand[i].iHelp][j]].zName)</b>
        @</big> (%s(zDesc))</dt>
      }
      @ <p><dd>
      help_to_html(aCommand[i].zHelp, cgi_output_blob());
      @ </dd><p>
      occHelp[aCommand[i].iHelp] = 0;
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137

/*
** Documentation on universal command-line options.
*/
/* @-comment: # */
static const char zOptions[] =
@ Command-line options common to all commands:
@ 
@   --args FILENAME         Read additional arguments and options from FILENAME
@   --case-sensitive BOOL   Set case sensitivity for file names
@   --cgitrace              Active CGI tracing
@   --chdir PATH            Change to PATH before performing any operations 
@   --comfmtflags VALUE     Set comment formatting flags to VALUE
@   --comment-format VALUE  Alias for --comfmtflags
@   --errorlog FILENAME     Log errors to FILENAME 
@   --help                  Show help on the command rather than running it
@   --httptrace             Trace outbound HTTP requests
@   --localtime             Display times using the local timezone
@   --nocgi                 Do not act as CGI
@   --no-th-hook            Do not run TH1 hooks
@   --quiet                 Reduce the amount of output
@   --sqlstats              Show SQL usage statistics when done







|



|


|







1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138

/*
** Documentation on universal command-line options.
*/
/* @-comment: # */
static const char zOptions[] =
@ Command-line options common to all commands:
@
@   --args FILENAME         Read additional arguments and options from FILENAME
@   --case-sensitive BOOL   Set case sensitivity for file names
@   --cgitrace              Active CGI tracing
@   --chdir PATH            Change to PATH before performing any operations
@   --comfmtflags VALUE     Set comment formatting flags to VALUE
@   --comment-format VALUE  Alias for --comfmtflags
@   --errorlog FILENAME     Log errors to FILENAME
@   --help                  Show help on the command rather than running it
@   --httptrace             Trace outbound HTTP requests
@   --localtime             Display times using the local timezone
@   --nocgi                 Do not act as CGI
@   --no-th-hook            Do not run TH1 hooks
@   --quiet                 Reduce the amount of output
@   --sqlstats              Show SQL usage statistics when done
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
  helptextVtab_cursor *pCur = (helptextVtab_cursor*)cur;
  return pCur->iRowid>=MX_COMMAND;
}

/*
** This method is called to "rewind" the helptextVtab_cursor object back
** to the first row of output.  This method is always called at least
** once prior to any call to helptextVtabColumn() or helptextVtabRowid() or 
** helptextVtabEof().
*/
static int helptextVtabFilter(
  sqlite3_vtab_cursor *pVtabCursor, 
  int idxNum, const char *idxStr,
  int argc, sqlite3_value **argv
){
  helptextVtab_cursor *pCur = (helptextVtab_cursor *)pVtabCursor;
  pCur->iRowid = 1;
  return SQLITE_OK;
}







|



|







1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
  helptextVtab_cursor *pCur = (helptextVtab_cursor*)cur;
  return pCur->iRowid>=MX_COMMAND;
}

/*
** This method is called to "rewind" the helptextVtab_cursor object back
** to the first row of output.  This method is always called at least
** once prior to any call to helptextVtabColumn() or helptextVtabRowid() or
** helptextVtabEof().
*/
static int helptextVtabFilter(
  sqlite3_vtab_cursor *pVtabCursor,
  int idxNum, const char *idxStr,
  int argc, sqlite3_value **argv
){
  helptextVtab_cursor *pCur = (helptextVtab_cursor *)pVtabCursor;
  pCur->iRowid = 1;
  return SQLITE_OK;
}
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
){
  pIdxInfo->estimatedCost = (double)MX_COMMAND;
  pIdxInfo->estimatedRows = MX_COMMAND;
  return SQLITE_OK;
}

/*
** This following structure defines all the methods for the 
** virtual table.
*/
static sqlite3_module helptextVtabModule = {
  /* iVersion    */ 0,
  /* xCreate     */ 0,  /* Helptext is eponymous and read-only */
  /* xConnect    */ helptextVtabConnect,
  /* xBestIndex  */ helptextVtabBestIndex,







|







1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
){
  pIdxInfo->estimatedCost = (double)MX_COMMAND;
  pIdxInfo->estimatedRows = MX_COMMAND;
  return SQLITE_OK;
}

/*
** This following structure defines all the methods for the
** virtual table.
*/
static sqlite3_module helptextVtabModule = {
  /* iVersion    */ 0,
  /* xCreate     */ 0,  /* Helptext is eponymous and read-only */
  /* xConnect    */ helptextVtabConnect,
  /* xBestIndex  */ helptextVtabBestIndex,
1541
1542
1543
1544
1545
1546
1547
1548

1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
  /* xCommit     */ 0,
  /* xRollback   */ 0,
  /* xFindMethod */ 0,
  /* xRename     */ 0,
  /* xSavepoint  */ 0,
  /* xRelease    */ 0,
  /* xRollbackTo */ 0,
  /* xShadowName */ 0

};


/*
** Register the helptext virtual table
*/
int helptext_vtab_register(sqlite3 *db){
  int rc = sqlite3_create_module(db, "helptext", &helptextVtabModule, 0);
  return rc;
}
/* End of the helptext virtual table
******************************************************************************/







|
>












1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
  /* xCommit     */ 0,
  /* xRollback   */ 0,
  /* xFindMethod */ 0,
  /* xRename     */ 0,
  /* xSavepoint  */ 0,
  /* xRelease    */ 0,
  /* xRollbackTo */ 0,
  /* xShadowName */ 0,
  /* xIntegrity  */ 0
};


/*
** Register the helptext virtual table
*/
int helptext_vtab_register(sqlite3 *db){
  int rc = sqlite3_create_module(db, "helptext", &helptextVtabModule, 0);
  return rc;
}
/* End of the helptext virtual table
******************************************************************************/
Changes to src/doc.c.
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
  static char * zList = 0;
  static char const * zEnd = 0;
  static int once = 0;
  char * z;
  int tokenizerState /* 0=expecting a key, 1=skip next token,
                     ** 2=accept next token */;
  if(once==0){
    once = 1; 
    zList = db_get("mimetypes",0);
    if(zList==0){
      return 0;
    }
    /* Transform zList to simplify the main loop:
       replace non-newline spaces with NUL bytes. */
    zEnd = zList + strlen(zList);







|







339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
  static char * zList = 0;
  static char const * zEnd = 0;
  static int once = 0;
  char * z;
  int tokenizerState /* 0=expecting a key, 1=skip next token,
                     ** 2=accept next token */;
  if(once==0){
    once = 1;
    zList = db_get("mimetypes",0);
    if(zList==0){
      return 0;
    }
    /* Transform zList to simplify the main loop:
       replace non-newline spaces with NUL bytes. */
    zEnd = zList + strlen(zList);
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
** Transfer content to the output.  During the transfer, when text of
** the following form is seen:
**
**       href="$ROOT/..."
**       action="$ROOT/..."
**       href=".../doc/$CURRENT/..."
**
** Convert $ROOT to the root URI of the repository, and $CURRENT to the 
** version number of the /doc/ document currently being displayed (if any).
** Allow ' in place of " and any case for href or action.  
**
** Efforts are made to limit this translation to cases where the text is
** fully contained with an HTML markup element.
*/
void convert_href_and_output(Blob *pIn){
  int i, base;
  int n = blob_size(pIn);







|

|







727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
** Transfer content to the output.  During the transfer, when text of
** the following form is seen:
**
**       href="$ROOT/..."
**       action="$ROOT/..."
**       href=".../doc/$CURRENT/..."
**
** Convert $ROOT to the root URI of the repository, and $CURRENT to the
** version number of the /doc/ document currently being displayed (if any).
** Allow ' in place of " and any case for href or action.
**
** Efforts are made to limit this translation to cases where the text is
** fully contained with an HTML markup element.
*/
void convert_href_and_output(Blob *pIn){
  int i, base;
  int n = blob_size(pIn);
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
**
** The intended use case here is to supply an icon for the "fossil ui"
** command.  For a permanent website, the recommended process is for
** the admin to set up a project-specific icon and reference that icon
** in the HTML header using a line like:
**
**   <link rel="icon" href="URL-FOR-YOUR-ICON" type="MIMETYPE"/>
** 
*/
void favicon_page(void){
  Blob icon;
  char *zMime;

  etag_check(ETAG_CONFIG, 0);
  zMime = db_get("icon-mimetype", "image/gif");







|







1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
**
** The intended use case here is to supply an icon for the "fossil ui"
** command.  For a permanent website, the recommended process is for
** the admin to set up a project-specific icon and reference that icon
** in the HTML header using a line like:
**
**   <link rel="icon" href="URL-FOR-YOUR-ICON" type="MIMETYPE"/>
**
*/
void favicon_page(void){
  Blob icon;
  char *zMime;

  etag_check(ETAG_CONFIG, 0);
  zMime = db_get("icon-mimetype", "image/gif");
Changes to src/etag.c.
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
  char zBuf[50];
  assert( zETag[0]==0 );  /* Only call this routine once! */

  if( etagCancelled ) return;

  /* By default, ETagged URLs never expire since the ETag will change
   * when the content changes.  Approximate this policy as 10 years. */
  iMaxAge = 10 * 365 * 24 * 60 * 60;   
  md5sum_init();

  /* Always include the executable ID as part of the hash */
  md5sum_step_text("exe-id: ", -1);
  md5sum_step_text(fossil_exe_id(), -1);
  md5sum_step_text("\n", 1);
  
  if( (eFlags & ETAG_HASH)!=0 && zHash ){
    md5sum_step_text("hash: ", -1);
    md5sum_step_text(zHash, -1);
    md5sum_step_text("\n", 1);
    iMaxAge = 0;
  }
  if( eFlags & ETAG_DATA ){







|






|







98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
  char zBuf[50];
  assert( zETag[0]==0 );  /* Only call this routine once! */

  if( etagCancelled ) return;

  /* By default, ETagged URLs never expire since the ETag will change
   * when the content changes.  Approximate this policy as 10 years. */
  iMaxAge = 10 * 365 * 24 * 60 * 60;
  md5sum_init();

  /* Always include the executable ID as part of the hash */
  md5sum_step_text("exe-id: ", -1);
  md5sum_step_text(fossil_exe_id(), -1);
  md5sum_step_text("\n", 1);

  if( (eFlags & ETAG_HASH)!=0 && zHash ){
    md5sum_step_text("hash: ", -1);
    md5sum_step_text(zHash, -1);
    md5sum_step_text("\n", 1);
    iMaxAge = 0;
  }
  if( eFlags & ETAG_DATA ){
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222

  /* Check to see the If-Modified-Since constraint is satisfied */
  zIfModifiedSince = P("HTTP_IF_MODIFIED_SINCE");
  if( zIfModifiedSince==0 ) return;
  x = cgi_rfc822_parsedate(zIfModifiedSince);
  if( x<mtime ) return;

#if 0  
  /* If the Fossil executable is more recent than If-Modified-Since,
  ** go ahead and regenerate the resource. */
  if( file_mtime(g.nameOfExe, ExtFILE)>x ) return;
#endif

  /* If we reach this point, it means that the resource has not changed
  ** and that we should generate a 304 Not Modified reply */







|







208
209
210
211
212
213
214
215
216
217
218
219
220
221
222

  /* Check to see the If-Modified-Since constraint is satisfied */
  zIfModifiedSince = P("HTTP_IF_MODIFIED_SINCE");
  if( zIfModifiedSince==0 ) return;
  x = cgi_rfc822_parsedate(zIfModifiedSince);
  if( x<mtime ) return;

#if 0
  /* If the Fossil executable is more recent than If-Modified-Since,
  ** go ahead and regenerate the resource. */
  if( file_mtime(g.nameOfExe, ExtFILE)>x ) return;
#endif

  /* If we reach this point, it means that the resource has not changed
  ** and that we should generate a 304 Not Modified reply */
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
/* Return the last-modified time in seconds since 1970.  Or return 0 if
** there is no last-modified time.
*/
sqlite3_int64 etag_mtime(void){
  return iEtagMtime;
}

/* 
** COMMAND: test-etag
**
** Usage:  fossil test-etag -key KEY-NUMBER  -hash HASH
**
** Generate an etag given a KEY-NUMBER and/or a HASH.
**
** KEY-NUMBER is some combination of:







|







242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
/* Return the last-modified time in seconds since 1970.  Or return 0 if
** there is no last-modified time.
*/
sqlite3_int64 etag_mtime(void){
  return iEtagMtime;
}

/*
** COMMAND: test-etag
**
** Usage:  fossil test-etag -key KEY-NUMBER  -hash HASH
**
** Generate an etag given a KEY-NUMBER and/or a HASH.
**
** KEY-NUMBER is some combination of:
Changes to src/export.c.
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
      }while( (rid = bag_next(vers, rid))!=0 );
    }
  }
}

/* This is the original header command (and hence documentation) for
** the "fossil export" command:
** 
** Usage: %fossil export --git ?OPTIONS? ?REPOSITORY?
**
** Write an export of all check-ins to standard output.  The export is
** written in the git-fast-export file format assuming the --git option is
** provided.  The git-fast-export format is currently the only VCS
** interchange format supported, though other formats may be added in
** the future.







|







447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
      }while( (rid = bag_next(vers, rid))!=0 );
    }
  }
}

/* This is the original header command (and hence documentation) for
** the "fossil export" command:
**
** Usage: %fossil export --git ?OPTIONS? ?REPOSITORY?
**
** Write an export of all check-ins to standard output.  The export is
** written in the git-fast-export file format assuming the --git option is
** provided.  The git-fast-export format is currently the only VCS
** interchange format supported, though other formats may be added in
** the future.
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
  db_bind_int(&sIns, ":isfile", isFile!=0);
  db_step(&sIns);
  db_reset(&sIns);
  return mprintf(":%d", db_last_insert_rowid());
}

/* This is the SHA3-256 hash of an empty file */
static const char zEmptySha3[] = 
  "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a";

/*
** Export a single file named by zUuid.
**
** Return 0 on success and non-zero on any failure.
**







|







1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
  db_bind_int(&sIns, ":isfile", isFile!=0);
  db_step(&sIns);
  db_reset(&sIns);
  return mprintf(":%d", db_last_insert_rowid());
}

/* This is the SHA3-256 hash of an empty file */
static const char zEmptySha3[] =
  "a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a";

/*
** Export a single file named by zUuid.
**
** Return 0 on success and non-zero on any failure.
**
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
  }else{
    rc = content_get(rid, &data);
    if( rc==0 ){
      if( bPhantomOk ){
        blob_init(&data, 0, 0);
        gitmirror_message(VERB_EXTRA, "missing file: %s\n", zUuid);
        zUuid = zEmptySha3;
      }else{      
        return 1;
      }
    }
  }
  zMark = gitmirror_find_mark(zUuid, 1, 1);
  if( zMark[0]==':' ){
    fprintf(xCmd, "blob\nmark %s\ndata %d\n", zMark, blob_size(&data));







|







1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
  }else{
    rc = content_get(rid, &data);
    if( rc==0 ){
      if( bPhantomOk ){
        blob_init(&data, 0, 0);
        gitmirror_message(VERB_EXTRA, "missing file: %s\n", zUuid);
        zUuid = zEmptySha3;
      }else{
        return 1;
      }
    }
  }
  zMark = gitmirror_find_mark(zUuid, 1, 1);
  if( zMark[0]==':' ){
    fprintf(xCmd, "blob\nmark %s\ndata %d\n", zMark, blob_size(&data));
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
    int i;
    zCmd = "git symbolic-ref --short HEAD";
    gitmirror_message(VERB_NORMAL, "%s\n", zCmd);
    xCmd = popen(zCmd, "r");
    if( xCmd==0 ){
      fossil_fatal("git command failed: %s", zCmd);
    }
    
    z = fgets(zLine, sizeof(zLine), xCmd);
    pclose(xCmd);
    if( z==0 ){
      fossil_fatal("no output from \"%s\"", zCmd);
    }
    for(i=0; z[i] && !fossil_isspace(z[i]); i++){}
    z[i] = 0;
    zMainBr = fossil_strdup(z);
  }
  return zMainBr;
} 

/*
** Implementation of the "fossil git export" command.
*/
void gitmirror_export_command(void){
  const char *zLimit;             /* Text of the --limit flag */
  int nLimit = 0x7fffffff;        /* Numeric value of the --limit flag */







|










|







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
    int i;
    zCmd = "git symbolic-ref --short HEAD";
    gitmirror_message(VERB_NORMAL, "%s\n", zCmd);
    xCmd = popen(zCmd, "r");
    if( xCmd==0 ){
      fossil_fatal("git command failed: %s", zCmd);
    }

    z = fgets(zLine, sizeof(zLine), xCmd);
    pclose(xCmd);
    if( z==0 ){
      fossil_fatal("no output from \"%s\"", zCmd);
    }
    for(i=0; z[i] && !fossil_isspace(z[i]); i++){}
    z[i] = 0;
    zMainBr = fossil_strdup(z);
  }
  return zMainBr;
}

/*
** Implementation of the "fossil git export" command.
*/
void gitmirror_export_command(void){
  const char *zLimit;             /* Text of the --limit flag */
  int nLimit = 0x7fffffff;        /* Numeric value of the --limit flag */
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
  /* Make sure GIT has been initialized */
  z = mprintf("%s/.git", zMirror);
  if( !file_isdir(z, ExtFILE) ){
    zMainBr = gitmirror_init(zMirror, zMainBr);
    bNeedRepack = 1;
  }
  fossil_free(z);
  
  /* Make sure the .mirror_state subdirectory exists */
  z = mprintf("%s/.mirror_state", zMirror);
  rc = file_mkdir(z, ExtFILE, 0);
  if( rc ) fossil_fatal("cannot create directory \"%s\"", z);
  fossil_free(z);

  /* Attach the .mirror_state/db database */







|







1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
  /* Make sure GIT has been initialized */
  z = mprintf("%s/.git", zMirror);
  if( !file_isdir(z, ExtFILE) ){
    zMainBr = gitmirror_init(zMirror, zMainBr);
    bNeedRepack = 1;
  }
  fossil_free(z);

  /* Make sure the .mirror_state subdirectory exists */
  z = mprintf("%s/.mirror_state", zMirror);
  rc = file_mkdir(z, ExtFILE, 0);
  if( rc ) fossil_fatal("cannot create directory \"%s\"", z);
  fossil_free(z);

  /* Attach the .mirror_state/db database */
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
  char *zSql;
  int bQuiet = 0;
  int bByAll = 0;   /* Undocumented option meaning this command was invoked
                    ** from "fossil all" and should modify output accordingly */

  db_find_and_open_repository(0, 0);
  bQuiet = find_option("quiet","q",0)!=0;
  bByAll = find_option("by-all",0,0)!=0; 
  verify_all_options();
  zMirror = db_get("last-git-export-repo", 0);
  if( zMirror==0 ){
    if( bQuiet ) return;
    if( bByAll ) return;
    fossil_print("Git mirror:  none\n");
    return;







|







1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
  char *zSql;
  int bQuiet = 0;
  int bByAll = 0;   /* Undocumented option meaning this command was invoked
                    ** from "fossil all" and should modify output accordingly */

  db_find_and_open_repository(0, 0);
  bQuiet = find_option("quiet","q",0)!=0;
  bByAll = find_option("by-all",0,0)!=0;
  verify_all_options();
  zMirror = db_get("last-git-export-repo", 0);
  if( zMirror==0 ){
    if( bQuiet ) return;
    if( bByAll ) return;
    fossil_print("Git mirror:  none\n");
    return;
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
**                             mapped into this name.  "master" is used if
**                             this option is omitted.
**         -q|--quiet          Reduce output. Repeat for even less output.
**         -v|--verbose        More output
**
** > fossil git import MIRROR
**
**       TBD...   
**
** > fossil git status
**
**       Show the status of the current Git mirror, if there is one.
**
**         -q|--quiet         No output if there is nothing to report
*/







|







1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
**                             mapped into this name.  "master" is used if
**                             this option is omitted.
**         -q|--quiet          Reduce output. Repeat for even less output.
**         -v|--verbose        More output
**
** > fossil git import MIRROR
**
**       TBD...
**
** > fossil git status
**
**       Show the status of the current Git mirror, if there is one.
**
**         -q|--quiet         No output if there is nothing to report
*/
Changes to src/file.c.
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257

/*
** Return non-NULL if zFilename contains pathname elements that
** are reserved on Windows.  The returned string is the disallowed
** path element.
*/
const char *file_is_win_reserved(const char *zPath){
  static const char *const azRes[] = { "CON", "PRN", "AUX", "NUL", "COM", "LPT" };
  static char zReturn[5];
  int i;
  while( zPath[0] ){
    for(i=0; i<count(azRes); i++){
      if( sqlite3_strnicmp(zPath, azRes[i], 3)==0
       && ((i>=4 && fossil_isdigit(zPath[3])
                 && (zPath[4]=='/' || zPath[4]=='.' || zPath[4]==0))







|







2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257

/*
** Return non-NULL if zFilename contains pathname elements that
** are reserved on Windows.  The returned string is the disallowed
** path element.
*/
const char *file_is_win_reserved(const char *zPath){
  static const char *const azRes[] = { "CON","PRN","AUX","NUL","COM","LPT" };
  static char zReturn[5];
  int i;
  while( zPath[0] ){
    for(i=0; i<count(azRes); i++){
      if( sqlite3_strnicmp(zPath, azRes[i], 3)==0
       && ((i>=4 && fossil_isdigit(zPath[3])
                 && (zPath[4]=='/' || zPath[4]=='.' || zPath[4]==0))
Changes to src/fileedit.c.
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
** pCI's ownership is not modified.
**
** This function validates pCI's state and fails if any validation
** fails.
**
** On error, returns false (0) and, if pErr is not NULL, writes a
** diagnostic message there.
** 
** Returns true on success. If pRid is not NULL, the RID of the
** resulting manifest is written to *pRid.
**
** The check-in process is largely influenced by pCI->flags, and that
** must be populated before calling this. See the fossil_cimini_flags
** enum for the docs for each flag.
*/







|







434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
** pCI's ownership is not modified.
**
** This function validates pCI's state and fails if any validation
** fails.
**
** On error, returns false (0) and, if pErr is not NULL, writes a
** diagnostic message there.
**
** Returns true on success. If pRid is not NULL, the RID of the
** resulting manifest is written to *pRid.
**
** The check-in process is largely influenced by pCI->flags, and that
** must be populated before calling this. See the fossil_cimini_flags
** enum for the docs for each flag.
*/
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
     && blob_size(&pCI->fileContent)>0
     ){
    /* Convert to the requested EOL style. Note that this inherently
    ** runs a risk of breaking content, e.g. string literals which
    ** contain embedded newlines. Note that HTML5 specifies that
    ** form-submitted TEXTAREA content gets normalized to CRLF-style:
    **
    ** https://html.spec.whatwg.org/multipage/form-elements.html#the-textarea-element
    */
    const int pseudoBinary = LOOK_LONG | LOOK_NUL;
    const int lookFlags = LOOK_CRLF | LOOK_LONE_LF | pseudoBinary;
    const int lookNew = looks_like_utf8( &pCI->fileContent, lookFlags );
    if(!(pseudoBinary & lookNew)){
      int rehash = 0;
      /*fossil_print("lookNew=%08x\n",lookNew);*/







|







571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
     && blob_size(&pCI->fileContent)>0
     ){
    /* Convert to the requested EOL style. Note that this inherently
    ** runs a risk of breaking content, e.g. string literals which
    ** contain embedded newlines. Note that HTML5 specifies that
    ** form-submitted TEXTAREA content gets normalized to CRLF-style:
    **
    ** https://html.spec.whatwg.org/#the-textarea-element
    */
    const int pseudoBinary = LOOK_LONG | LOOK_NUL;
    const int lookFlags = LOOK_CRLF | LOOK_LONE_LF | pseudoBinary;
    const int lookNew = looks_like_utf8( &pCI->fileContent, lookFlags );
    if(!(pseudoBinary & lookNew)){
      int rehash = 0;
      /*fossil_print("lookNew=%08x\n",lookNew);*/
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
                                       char ** zRevUuid,
                                       int * pVid,
                                       const char * zFilename,
                                       int * frid){
  char * zFileUuid = 0;             /* file content UUID */
  const int checkFile = zFilename!=0 || frid!=0;
  int vid = 0;
  
  if(checkFile && !fileedit_ajax_check_filename(zFilename)){
    return 0;
  }
  vid = symbolic_name_to_rid(zRev, "ci");
  if(0==vid){
    ajax_route_error(404,"Cannot resolve name as a check-in: %s",
                     zRev);







|







979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
                                       char ** zRevUuid,
                                       int * pVid,
                                       const char * zFilename,
                                       int * frid){
  char * zFileUuid = 0;             /* file content UUID */
  const int checkFile = zFilename!=0 || frid!=0;
  int vid = 0;

  if(checkFile && !fileedit_ajax_check_filename(zFilename)){
    return 0;
  }
  vid = symbolic_name_to_rid(zRev, "ci");
  if(0==vid){
    ajax_route_error(404,"Cannot resolve name as a check-in: %s",
                     zRev);
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
**
** Intended to be used only by /filepage and /filepage_commit.
*/
static int fileedit_setup_cimi_from_p(CheckinMiniInfo * p, Blob * pErr,
                                      int * bIsMissingArg){
  char * zFileUuid = 0;          /* UUID of file content */
  const char * zFlag;            /* generic flag */
  int rc = 0, vid = 0, frid = 0; /* result code, check-in/file rids */ 

#define fail(EXPR) blob_appendf EXPR; goto end_fail
  zFlag = PD("filename",P("fn"));
  if(zFlag==0 || !*zFlag){
    rc = 400;
    if(bIsMissingArg){
      *bIsMissingArg = 1;







|







1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
**
** Intended to be used only by /filepage and /filepage_commit.
*/
static int fileedit_setup_cimi_from_p(CheckinMiniInfo * p, Blob * pErr,
                                      int * bIsMissingArg){
  char * zFileUuid = 0;          /* UUID of file content */
  const char * zFlag;            /* generic flag */
  int rc = 0, vid = 0, frid = 0; /* result code, check-in/file rids */

#define fail(EXPR) blob_appendf EXPR; goto end_fail
  zFlag = PD("filename",P("fn"));
  if(zFlag==0 || !*zFlag){
    rc = 400;
    if(bIsMissingArg){
      *bIsMissingArg = 1;
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
      if(i++){
        CX(",");
      }
      CX("%!j", zFilename);
    }
  }
  db_finalize(&q);
  CX("]}");  
}

/*
** AJAX route /fileedit?ajax=filelist
**
** Fetches a JSON-format list of leaves and/or filenames for use in
** creating a file selection list in /fileedit. It has different modes







|







1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
      if(i++){
        CX(",");
      }
      CX("%!j", zFilename);
    }
  }
  db_finalize(&q);
  CX("]}");
}

/*
** AJAX route /fileedit?ajax=filelist
**
** Fetches a JSON-format list of leaves and/or filenames for use in
** creating a file selection list in /fileedit. It has different modes
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
  }
}

/*
** AJAX route /fileedit?ajax=commit
**
** Required query parameters:
** 
** filename=FILENAME
** checkin=Parent check-in UUID
** content=text
** comment=non-empty text
**
** Optional query parameters:
**
** comment_mimetype=text (NOT currently honored)
**
** dry_run=int (1 or 0)
**
** include_manifest=int (1 or 0), whether to include
** the generated manifest in the response.
** 
**
** User must have Write permissions to use this page.
**
** Responds with JSON (with some state repeated
** from the input in order to avoid certain race conditions
** client-side):
**







|













|







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

/*
** AJAX route /fileedit?ajax=commit
**
** Required query parameters:
**
** filename=FILENAME
** checkin=Parent check-in UUID
** content=text
** comment=non-empty text
**
** Optional query parameters:
**
** comment_mimetype=text (NOT currently honored)
**
** dry_run=int (1 or 0)
**
** include_manifest=int (1 or 0), whether to include
** the generated manifest in the response.
**
**
** User must have Write permissions to use this page.
**
** Responds with JSON (with some state repeated
** from the input in order to avoid certain race conditions
** client-side):
**
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
  ** use of the name parameter.
  **
  ** Which additional parameters are used by each distinct ajax route
  ** is an internal implementation detail and may change with any
  ** given build of this code. An unknown "name" value triggers an
  ** error, as documented for ajax_route_error().
  */
  
  /* Allow no access to this page without check-in privilege */
  login_check_credentials();
  if( !g.perm.Write ){
    if(zAjax!=0){
      ajax_route_error(403, "Write permissions required.");
    }else{
      login_needed(g.anon.Write);







|







1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
  ** use of the name parameter.
  **
  ** Which additional parameters are used by each distinct ajax route
  ** is an internal implementation detail and may change with any
  ** given build of this code. An unknown "name" value triggers an
  ** error, as documented for ajax_route_error().
  */

  /* Allow no access to this page without check-in privilege */
  login_check_credentials();
  if( !g.perm.Write ){
    if(zAjax!=0){
      ajax_route_error(403, "Write permissions required.");
    }else{
      login_needed(g.anon.Write);
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
  ** have a common, page-specific container we can filter our CSS
  ** selectors, but we do have the BODY, which we can decorate with
  ** whatever CSS we wish...
  */
  style_script_begin(__FILE__,__LINE__);
  CX("document.body.classList.add('fileedit');\n");
  style_script_end();
  
  /* 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='fileedit-edit-status'>"







|







1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
  ** have a common, page-specific container we can filter our CSS
  ** selectors, but we do have the BODY, which we can decorate with
  ** whatever CSS we wish...
  */
  style_script_begin(__FILE__,__LINE__);
  CX("document.body.classList.add('fileedit');\n");
  style_script_end();

  /* 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='fileedit-edit-status'>"
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
       "data-tab-parent='fileedit-tabs' "
       "data-tab-label='File Selection' "
       "class='hidden'"
       ">");
    CX("<div id='fileedit-file-selector'></div>");
    CX("</div>"/*#fileedit-tab-fileselect*/);
  }
  
  /******* Content tab *******/
  {
    CX("<div id='fileedit-tab-content' "
       "data-tab-parent='fileedit-tabs' "
       "data-tab-label='File Content' "
       "class='hidden'"
       ">");

    CX("<div class='flex-container flex-row child-gap-small'>");
    CX("<div class='input-with-label'>"
       "<button class='fileedit-content-reload confirmer' "
       ">Discard &amp; Reload</button>"
       "<div class='help-buttonlet'>"
       "Reload the file from the server, discarding "
       "any local edits. To help avoid accidental loss of "
       "edits, it requires confirmation (a second click) within "
       "a few seconds or it will not reload."
       "</div>"
       "</div>");
    style_select_list_int("select-font-size",
                          "editor_font_size", "Editor font size",
                          NULL/*tooltip*/,
                          100,
                          "100%", 100, "125%", 125,
                          "150%", 150, "175%", 175,
                          "200%", 200, NULL);

    CX("</div>");
    CX("<div class='flex-container flex-column stretch'>");
    CX("<textarea name='content' id='fileedit-content-editor' "
       "class='fileedit' rows='25'>");
    CX("</textarea>");
    CX("</div>"/*textarea wrapper*/);
    CX("</div>"/*#tab-file-content*/);







|







>
|

















>







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
       "data-tab-parent='fileedit-tabs' "
       "data-tab-label='File Selection' "
       "class='hidden'"
       ">");
    CX("<div id='fileedit-file-selector'></div>");
    CX("</div>"/*#fileedit-tab-fileselect*/);
  }

  /******* Content tab *******/
  {
    CX("<div id='fileedit-tab-content' "
       "data-tab-parent='fileedit-tabs' "
       "data-tab-label='File Content' "
       "class='hidden'"
       ">");
    CX("<div class='fileedit-options flex-container "
       "flex-row child-gap-small'>");
    CX("<div class='input-with-label'>"
       "<button class='fileedit-content-reload confirmer' "
       ">Discard &amp; Reload</button>"
       "<div class='help-buttonlet'>"
       "Reload the file from the server, discarding "
       "any local edits. To help avoid accidental loss of "
       "edits, it requires confirmation (a second click) within "
       "a few seconds or it will not reload."
       "</div>"
       "</div>");
    style_select_list_int("select-font-size",
                          "editor_font_size", "Editor font size",
                          NULL/*tooltip*/,
                          100,
                          "100%", 100, "125%", 125,
                          "150%", 150, "175%", 175,
                          "200%", 200, NULL);
    wikiedit_emit_toggle_preview();
    CX("</div>");
    CX("<div class='flex-container flex-column stretch'>");
    CX("<textarea name='content' id='fileedit-content-editor' "
       "class='fileedit' rows='25'>");
    CX("</textarea>");
    CX("</div>"/*textarea wrapper*/);
    CX("</div>"/*#tab-file-content*/);
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
        */
        style_select_list_str("comment-mimetype", "comment_mimetype",
                              "Comment style:",
                              "Specify how fossil will interpret the "
                              "comment string.",
                              NULL,
                              "Fossil", "text/x-fossil-wiki",
                              "Markdown", "text/x-markdown", 
                              "Plain text", "text/plain",
                              NULL);
        CX("</div>\n");
      }
      CX("<div class='fileedit-hint flex-container flex-row'>"
         "(Warning: switching from multi- to single-line mode will "
         "strip out all newlines!)</div>");







|







1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
        */
        style_select_list_str("comment-mimetype", "comment_mimetype",
                              "Comment style:",
                              "Specify how fossil will interpret the "
                              "comment string.",
                              NULL,
                              "Fossil", "text/x-fossil-wiki",
                              "Markdown", "text/x-markdown",
                              "Plain text", "text/plain",
                              NULL);
        CX("</div>\n");
      }
      CX("<div class='fileedit-hint flex-container flex-row'>"
         "(Warning: switching from multi- to single-line mode will "
         "strip out all newlines!)</div>");
Changes to src/foci.c.
266
267
268
269
270
271
272
273

274
275
276
277
    0,                            /* xCommit */
    0,                            /* xRollback */
    0,                            /* xFindFunction */
    0,                            /* xRename */
    0,                            /* xSavepoint */
    0,                            /* xRelease */
    0,                            /* xRollbackTo */
    0                             /* xShadowName */

  };
  sqlite3_create_module(db, "files_of_checkin", &foci_module, 0);
  return SQLITE_OK;
}







|
>




266
267
268
269
270
271
272
273
274
275
276
277
278
    0,                            /* xCommit */
    0,                            /* xRollback */
    0,                            /* xFindFunction */
    0,                            /* xRename */
    0,                            /* xSavepoint */
    0,                            /* xRelease */
    0,                            /* xRollbackTo */
    0,                            /* xShadowName */
    0                             /* xIntegrity  */
  };
  sqlite3_create_module(db, "files_of_checkin", &foci_module, 0);
  return SQLITE_OK;
}
Changes to src/fossil.page.chat.js.
1
2
3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
/**
   This file contains the client-side implementation of fossil's /chat
   application. 
*/
window.fossil.onPageLoad(function(){
  const F = window.fossil, D = F.dom;
  const E1 = function(selector){
    const e = document.querySelector(selector);
    if(!e) throw new Error("missing required DOM element: "+selector);
    return e;
  };

  /**
     Returns true if e is entirely within the bounds of the window's viewport.
  */
  const isEntirelyInViewport = function(e) {
    const rect = e.getBoundingClientRect();
    return (
      rect.top >= 0 &&


|








>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
   This file contains the client-side implementation of fossil's /chat
   application.
*/
window.fossil.onPageLoad(function(){
  const F = window.fossil, D = F.dom;
  const E1 = function(selector){
    const e = document.querySelector(selector);
    if(!e) throw new Error("missing required DOM element: "+selector);
    return e;
  };

  /**
     Returns true if e is entirely within the bounds of the window's viewport.
  */
  const isEntirelyInViewport = function(e) {
    const rect = e.getBoundingClientRect();
    return (
      rect.top >= 0 &&
394
395
396
397
398
399
400





401
402
403
404
405
406
407
             ctrl-enter both send them. */
          "edit-ctrl-send": false,
          /* When on, the edit field starts as a single line and
             expands as the user types, and the relevant buttons are
             laid out in a compact form. When off, the edit field and
             buttons are larger. */
          "edit-compact-mode": true,





          /* When on, sets the font-family on messages and the edit
             field to monospace. */
          "monospace-messages": false,
          /* When on, non-chat UI elements (page header/footer) are
             hidden */
          "chat-only-mode": false,
          /* When set to a URI, it is assumed to be an audio file,







>
>
>
>
>







395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
             ctrl-enter both send them. */
          "edit-ctrl-send": false,
          /* When on, the edit field starts as a single line and
             expands as the user types, and the relevant buttons are
             laid out in a compact form. When off, the edit field and
             buttons are larger. */
          "edit-compact-mode": true,
          /* See notes for this setting in fossil.page.wikiedit.js.
             Both /wikiedit and /fileedit share this persistent config
             option under the same storage key. */
          "edit-shift-enter-preview":
            F.storage.getBool('edit-shift-enter-preview', true),
          /* When on, sets the font-family on messages and the edit
             field to monospace. */
          "monospace-messages": false,
          /* When on, non-chat UI elements (page header/footer) are
             hidden */
          "chat-only-mode": false,
          /* When set to a URI, it is assumed to be an audio file,
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
      /* Shift-enter will run preview mode UNLESS preview mode is
         active AND the input field is empty, in which case it will
         switch back to message view. */
      if(Chat.e.currentView===Chat.e.viewPreview && !text){
        Chat.setCurrentView(Chat.e.viewMessages);
      }else if(!text){
        f.$toggleCompact(compactMode);
      }else{
        Chat.e.btnPreview.click();
      }
      return false;
    }
    if(ev.ctrlKey && !text && !BlobXferState.blob){
      /* Ctrl-enter on empty input field(s) toggles Enter/Ctrl-enter mode */
      ev.preventDefault();
      ev.stopPropagation();
      f.$toggleCtrl(ctrlMode);
      return false;
    }
    if(!ctrlMode && ev.ctrlKey && text){
      //console.debug("!ctrlMode && ev.ctrlKey && text.");
      /* Ctrl-enter in Enter-sends mode SHOULD, with this logic add a
         newline, but that is not happening, for unknown reasons
         (possibly related to this element being a conteneditable DIV
         instead of a textarea). Forcibly appending a newline do the
         input area does not work, also for unknown reasons, and would
         only be suitable when we're at the end of the input.

         Strangely, this approach DOES work for shift-enter, but we
         need shift-enter as a hotkey for preview mode.
      */
      //return;
      // return here "should" cause newline to be added, but that doesn't work
    }
    if((!ctrlMode && !ev.ctrlKey) || (ev.ctrlKey/* && ctrlMode*/)){
      /* Ship it! */
      ev.preventDefault();
      ev.stopPropagation();
      Chat.submitMessage();
      return false;
    }
  };  
  Chat.e.inputFields.forEach(
    (e)=>e.addEventListener('keydown', inputWidgetKeydown, false)
  );
  Chat.e.btnSubmit.addEventListener('click',(e)=>{
    e.preventDefault();
    Chat.submitMessage();
    return false;







|















|

















|







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
      /* Shift-enter will run preview mode UNLESS preview mode is
         active AND the input field is empty, in which case it will
         switch back to message view. */
      if(Chat.e.currentView===Chat.e.viewPreview && !text){
        Chat.setCurrentView(Chat.e.viewMessages);
      }else if(!text){
        f.$toggleCompact(compactMode);
      }else if(Chat.settings.getBool('edit-shift-enter-preview', true)){
        Chat.e.btnPreview.click();
      }
      return false;
    }
    if(ev.ctrlKey && !text && !BlobXferState.blob){
      /* Ctrl-enter on empty input field(s) toggles Enter/Ctrl-enter mode */
      ev.preventDefault();
      ev.stopPropagation();
      f.$toggleCtrl(ctrlMode);
      return false;
    }
    if(!ctrlMode && ev.ctrlKey && text){
      //console.debug("!ctrlMode && ev.ctrlKey && text.");
      /* Ctrl-enter in Enter-sends mode SHOULD, with this logic add a
         newline, but that is not happening, for unknown reasons
         (possibly related to this element being a contenteditable DIV
         instead of a textarea). Forcibly appending a newline do the
         input area does not work, also for unknown reasons, and would
         only be suitable when we're at the end of the input.

         Strangely, this approach DOES work for shift-enter, but we
         need shift-enter as a hotkey for preview mode.
      */
      //return;
      // return here "should" cause newline to be added, but that doesn't work
    }
    if((!ctrlMode && !ev.ctrlKey) || (ev.ctrlKey/* && ctrlMode*/)){
      /* Ship it! */
      ev.preventDefault();
      ev.stopPropagation();
      Chat.submitMessage();
      return false;
    }
  };
  Chat.e.inputFields.forEach(
    (e)=>e.addEventListener('keydown', inputWidgetKeydown, false)
  );
  Chat.e.btnSubmit.addEventListener('click',(e)=>{
    e.preventDefault();
    Chat.submitMessage();
    return false;
1665
1666
1667
1668
1669
1670
1671







1672
1673
1674
1675
1676
1677
1678
        boolValue: 'edit-widget-x',
        hint: [
          "When enabled, chat input uses a so-called 'contenteditable' ",
          "field. Though generally more comfortable and modern than ",
          "plain-text input fields, browser-specific quirks and bugs ",
          "may lead to frustration. Ideal for mobile devices."
        ].join('')







      }]
    },{
      label: "Appearance Options...",
      children:[{
        label: "Left-align my posts",
        hint: "Default alignment of your own messages is selected "
          + "based window width/height ratio.",







>
>
>
>
>
>
>







1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
        boolValue: 'edit-widget-x',
        hint: [
          "When enabled, chat input uses a so-called 'contenteditable' ",
          "field. Though generally more comfortable and modern than ",
          "plain-text input fields, browser-specific quirks and bugs ",
          "may lead to frustration. Ideal for mobile devices."
        ].join('')
      },{
        label: "Shift-enter to preview",
        hint: ["Use shift-enter to preview being-edited messages. ",
               "This is normally desirable but some software-mode ",
               "keyboards misinteract with this, in which cases it can be ",
               "disabled."],
        boolValue: 'edit-shift-enter-preview'
      }]
    },{
      label: "Appearance Options...",
      children:[{
        label: "Left-align my posts",
        hint: "Default alignment of your own messages is selected "
          + "based window width/height ratio.",
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
          D.enable(elemsToEnable);
        }
      });
      return false;
    };
    btnPreview.addEventListener('click', submit, false);
  })()/*message preview setup*/;
  
  /** Callback for poll() to inject new content into the page.  jx ==
      the response from /chat-poll. If atEnd is true, the message is
      appended to the end of the chat list (for loading older
      messages), else the beginning (the default). */
  const newcontent = function f(jx,atEnd){
    if(!f.processPost){
      /** Processes chat message m, placing it either the start (if atEnd







|







1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
          D.enable(elemsToEnable);
        }
      });
      return false;
    };
    btnPreview.addEventListener('click', submit, false);
  })()/*message preview setup*/;

  /** Callback for poll() to inject new content into the page.  jx ==
      the response from /chat-poll. If atEnd is true, the message is
      appended to the end of the chat list (for loading older
      messages), else the beginning (the default). */
  const newcontent = function f(jx,atEnd){
    if(!f.processPost){
      /** Processes chat message m, placing it either the start (if atEnd
Changes to src/fossil.page.fileedit.js.
68
69
70
71
72
73
74
75






76
77
78
79
80
81
82
     );
  */
  const E = (s)=>document.querySelector(s),
        D = F.dom,
        P = F.page;

  P.config = {
    defaultMaxStashSize: 7






  };

  /**
     $stash is an internal-use-only object for managing "stashed"
     local edits, to help avoid that users accidentally lose content
     by switching tabs or following links or some such. The basic
     theory of operation is...







|
>
>
>
>
>
>







68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
     );
  */
  const E = (s)=>document.querySelector(s),
        D = F.dom,
        P = F.page;

  P.config = {
    defaultMaxStashSize: 7,
    /**
       See notes for this setting in fossil.page.wikiedit.js. Both
       /wikiedit and /fileedit share this persistent config option
       under the same storage key.
    */
    shiftEnterPreview: F.storage.getBool('edit-shift-enter-preview', true)
  };

  /**
     $stash is an internal-use-only object for managing "stashed"
     local edits, to help avoid that users accidentally lose content
     by switching tabs or following links or some such. The basic
     theory of operation is...
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
        opt._finfo = finfo;
        if(0===f.compare(currentFinfo, finfo)){
          D.attr(opt, 'selected', true);
        }
      });
    }
  }/*P.stashWidget*/;
  
  /**
     Internal workaround to select the current preview mode
     and fire a change event if the value actually changes
     or if forceEvent is truthy.
  */
  P.selectPreviewMode = function(modeValue, forceEvent){
    const s = this.e.selectPreviewMode;







|







574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
        opt._finfo = finfo;
        if(0===f.compare(currentFinfo, finfo)){
          D.attr(opt, 'selected', true);
        }
      });
    }
  }/*P.stashWidget*/;

  /**
     Internal workaround to select the current preview mode
     and fire a change event if the value actually changes
     or if forceEvent is truthy.
  */
  P.selectPreviewMode = function(modeValue, forceEvent){
    const s = this.e.selectPreviewMode;
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
        }
      }
    );
    ////////////////////////////////////////////////////////////
    // Trigger preview on Ctrl-Enter. This only works on the built-in
    // editor widget, not a client-provided one.
    P.e.taEditor.addEventListener('keydown',function(ev){
      if(ev.shiftKey && 13 === ev.keyCode){
        ev.preventDefault();
        ev.stopPropagation();
        P.e.taEditor.blur(/*force change event, if needed*/);
        P.tabs.switchToTab(P.e.tabs.preview);
        if(!P.e.cbAutoPreview.checked){/* If NOT in auto-preview mode, trigger an update. */
          P.preview();
        }







|







728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
        }
      }
    );
    ////////////////////////////////////////////////////////////
    // Trigger preview on Ctrl-Enter. This only works on the built-in
    // editor widget, not a client-provided one.
    P.e.taEditor.addEventListener('keydown',function(ev){
      if(P.config.shiftEnterPreview && ev.shiftKey && 13===ev.keyCode){
        ev.preventDefault();
        ev.stopPropagation();
        P.e.taEditor.blur(/*force change event, if needed*/);
        P.tabs.switchToTab(P.e.tabs.preview);
        if(!P.e.cbAutoPreview.checked){/* If NOT in auto-preview mode, trigger an update. */
          P.preview();
        }
843
844
845
846
847
848
849







850
851
852
853
854
855
856
      }
    );

    P.fileSelectWidget.init();
    P.stashWidget.init(
      P.e.tabs.content.lastElementChild
    );







  }/*F.onPageLoad()*/);

  /**
     Getter (if called with no args) or setter (if passed an arg) for
     the current file content.

     The setter form sets the content, dispatches a







>
>
>
>
>
>
>







849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
      }
    );

    P.fileSelectWidget.init();
    P.stashWidget.init(
      P.e.tabs.content.lastElementChild
    );

    const cbEditPreview = E('#edit-shift-enter-preview');
    cbEditPreview.addEventListener('change', function(e){
      F.storage.set('edit-shift-enter-preview',
                    P.config.shiftEnterPreview = e.target.checked);
    }, false);
    cbEditPreview.checked = P.config.shiftEnterPreview;
  }/*F.onPageLoad()*/);

  /**
     Getter (if called with no args) or setter (if passed an arg) for
     the current file content.

     The setter form sets the content, dispatches a
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
    const target = this.e.previewTarget;
    D.clearElement(target);
    if('string'===typeof c) D.parseHtml(target,c);
    if(F.pikchr){
      F.pikchr.addSrcView(target.querySelectorAll('svg.pikchr'));
    }
  };
  
  /**
     Callback for use with F.connectPagePreviewers()
  */
  P._postPreview = function(content,callback){
    if(!affirmHasFile()) return this;
    if(!content){
      callback(content);







|







1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
    const target = this.e.previewTarget;
    D.clearElement(target);
    if('string'===typeof c) D.parseHtml(target,c);
    if(F.pikchr){
      F.pikchr.addSrcView(target.querySelectorAll('svg.pikchr'));
    }
  };

  /**
     Callback for use with F.connectPagePreviewers()
  */
  P._postPreview = function(content,callback){
    if(!affirmHasFile()) return this;
    if(!content){
      callback(content);
Changes to src/fossil.page.whistory.js.
1
2
3
4
5
6
7
8
9
10
11
12
13
14



15
16
17
18
19
20
21
/* This script adds interactivity for wiki-history webpages.
 *
 * The main code is within the 'on-click' handler of the "diff" links.
 * Instead of standard redirection it fills-in two hidden inputs with
 * the appropriate values and submits the corresponding form.
 * A special care should be taken if some intermediate edits are hidden.
 *
 * For the sake of compatibility with ascetic browsers the code tries
 * to avoid modern API and ECMAScript constructs. This makes it less
 * readable and may be reconsidered in the future.
*/
window.addEventListener( 'load', function() {

document.getElementById("wh-form").method = "GET";




var wh_id  = document.getElementById("wh-id" );
var wh_pid = document.getElementById("wh-pid");
var wh_cleaner = document.getElementById("wh-cleaner");
var wh_collapser = document.getElementById("wh-collapser");

var wh_radios   = [];  // user-visible controls for baseline selection













|
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/* This script adds interactivity for wiki-history webpages.
 *
 * The main code is within the 'on-click' handler of the "diff" links.
 * Instead of standard redirection it fills-in two hidden inputs with
 * the appropriate values and submits the corresponding form.
 * A special care should be taken if some intermediate edits are hidden.
 *
 * For the sake of compatibility with ascetic browsers the code tries
 * to avoid modern API and ECMAScript constructs. This makes it less
 * readable and may be reconsidered in the future.
*/
window.addEventListener( 'load', function() {

var form = document.getElementById("wh-form");
form.method = "GET";
var csrf = form.querySelector("input[name='csrf']");
if( csrf ) form.removeChild( csrf );

var wh_id  = document.getElementById("wh-id" );
var wh_pid = document.getElementById("wh-pid");
var wh_cleaner = document.getElementById("wh-cleaner");
var wh_collapser = document.getElementById("wh-collapser");

var wh_radios   = [];  // user-visible controls for baseline selection
Changes to src/fossil.page.wikiedit.js.
73
74
75
76
77
78
79







80






81
82
83
84
85
86
87
    useConfirmerButtons:{
    /* If true during fossil.page setup, certain buttons will use a
       "confirmer" step, else they will not. The confirmer topic has
       been the source of much contention in the forum. */
      save: false,
      reload: true,
      discardStash: true







    }






  };

  /**
     $stash is an internal-use-only object for managing "stashed"
     local edits, to help avoid that users accidentally lose content
     by switching tabs or following links or some such. The basic
     theory of operation is...







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







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
    useConfirmerButtons:{
    /* If true during fossil.page setup, certain buttons will use a
       "confirmer" step, else they will not. The confirmer topic has
       been the source of much contention in the forum. */
      save: false,
      reload: true,
      discardStash: true
    },
    /**
       If true, a keyboard combo of shift-enter (from the editor)
       toggles between preview and edit modes.  This is normally
       desired but at least one software keyboard is known to
       misinteract with this, treating an Enter after
       automatically-capitalized letters as a shift-enter:

       https://fossil-scm.org/forum/forumpost/dbd5b68366147ce8

       Maintenance note: /fileedit also uses this same key for the
       same purpose.
    */
    shiftEnterPreview: F.storage.getBool('edit-shift-enter-preview', true)
  };

  /**
     $stash is an internal-use-only object for managing "stashed"
     local edits, to help avoid that users accidentally lose content
     by switching tabs or following links or some such. The basic
     theory of operation is...
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
            opt.dataset.isDeleted = true;
          }
          self._refreshStashMarks(opt);
        });
      D.enable(sel);
      if(P.winfo) sel.value = P.winfo.name;
    },
    
    /** Loads the page list and populates the selection list. */
    loadList: function callee(){
      if(!callee.onload){
        const self = this;
        callee.onload = function(list){
          self.cache.pageList = list;
          self._rebuildList();







|







465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
            opt.dataset.isDeleted = true;
          }
          self._refreshStashMarks(opt);
        });
      D.enable(sel);
      if(P.winfo) sel.value = P.winfo.name;
    },

    /** Loads the page list and populates the selection list. */
    loadList: function callee(){
      if(!callee.onload){
        const self = this;
        callee.onload = function(list){
          self.cache.pageList = list;
          self._rebuildList();
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
      }, false);

      D.append(
        parentElem,
        D.append(D.addClass(D.div(), 'fieldset-wrapper'),
                 fsFilter, fsNewPage, fsLegend)
      );
      
      D.append(parentElem, btn);
      btn.addEventListener('click', ()=>this.loadList(), false);
      this.loadList();
      const onSelect = (e)=>P.loadPage(e.target.value);
      sel.addEventListener('change', onSelect, false);
      sel.addEventListener('dblclick', onSelect, false);
      F.page.addEventListener('wiki-stash-updated', ()=>{







|







662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
      }, false);

      D.append(
        parentElem,
        D.append(D.addClass(D.div(), 'fieldset-wrapper'),
                 fsFilter, fsNewPage, fsLegend)
      );

      D.append(parentElem, btn);
      btn.addEventListener('click', ()=>this.loadList(), false);
      this.loadList();
      const onSelect = (e)=>P.loadPage(e.target.value);
      sel.addEventListener('change', onSelect, false);
      sel.addEventListener('dblclick', onSelect, false);
      F.page.addEventListener('wiki-stash-updated', ()=>{
672
673
674
675
676
677
678







679
680
681
682
683
684
685
686
687
          if(page.isEmpty) opt.dataset.isDeleted = true;
          else delete opt.dataset.isDeleted;
          self._refreshStashMarks(opt);
        }else if('sandbox'!==page.type){
          F.error("BUG: internal mis-handling of page object: missing OPTION for page "+page.name);
        }
      });







      delete this.init;
    }
  };

  /**
     Widget for listing and selecting $stash entries.
  */
  P.stashWidget = {
    e:{/*DOM element(s)*/},







>
>
>
>
>
>
>

|







685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
          if(page.isEmpty) opt.dataset.isDeleted = true;
          else delete opt.dataset.isDeleted;
          self._refreshStashMarks(opt);
        }else if('sandbox'!==page.type){
          F.error("BUG: internal mis-handling of page object: missing OPTION for page "+page.name);
        }
      });

      const cbEditPreview = E('#edit-shift-enter-preview');
      cbEditPreview.addEventListener('change', function(e){
        F.storage.set('edit-shift-enter-preview',
                      P.config.shiftEnterPreview = e.target.checked);
      }, false);
      cbEditPreview.checked = P.config.shiftEnterPreview;
      delete this.init;
    }/*init()*/
  };

  /**
     Widget for listing and selecting $stash entries.
  */
  P.stashWidget = {
    e:{/*DOM element(s)*/},
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
        }
      }
    );
    ////////////////////////////////////////////////////////////
    // Trigger preview on Ctrl-Enter. This only works on the built-in
    // editor widget, not a client-provided one.
    P.e.taEditor.addEventListener('keydown',function(ev){
      if(ev.shiftKey && 13 === ev.keyCode){
        ev.preventDefault();
        ev.stopPropagation();
        P.e.taEditor.blur(/*force change event, if needed*/);
        P.tabs.switchToTab(P.e.tabs.preview);
        if(!P.e.cbAutoPreview.checked){/* If NOT in auto-preview mode, trigger an update. */
          P.preview();
        }







|







932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
        }
      }
    );
    ////////////////////////////////////////////////////////////
    // Trigger preview on Ctrl-Enter. This only works on the built-in
    // editor widget, not a client-provided one.
    P.e.taEditor.addEventListener('keydown',function(ev){
      if(P.config.shiftEnterPreview && ev.shiftKey && 13===ev.keyCode){
        ev.preventDefault();
        ev.stopPropagation();
        P.e.taEditor.blur(/*force change event, if needed*/);
        P.tabs.switchToTab(P.e.tabs.preview);
        if(!P.e.cbAutoPreview.checked){/* If NOT in auto-preview mode, trigger an update. */
          P.preview();
        }
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
    const target = this.e.previewTarget;
    D.clearElement(target);
    if('string'===typeof c) D.parseHtml(target,c);
    if(F.pikchr){
      F.pikchr.addSrcView(target.querySelectorAll('svg.pikchr'));
    }
  };
  
  /**
     Callback for use with F.connectPagePreviewers()
  */
  P._postPreview = function(content,callback){
    if(!affirmPageLoaded()) return this;
    if(!content){
      callback(content);







|







1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
    const target = this.e.previewTarget;
    D.clearElement(target);
    if('string'===typeof c) D.parseHtml(target,c);
    if(F.pikchr){
      F.pikchr.addSrcView(target.querySelectorAll('svg.pikchr'));
    }
  };

  /**
     Callback for use with F.connectPagePreviewers()
  */
  P._postPreview = function(content,callback){
    if(!affirmPageLoaded()) return this;
    if(!content){
      callback(content);
Changes to src/graph.c.
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
    dist = i - iNearto;
    if( dist<0 ) dist = -dist;
    if( dist<iBestDist ){
      iBestDist = dist;
      iBest = i;
    }
  }
  
  /* If no match, consider all possible rails */
  if( iBestDist>1000 ){
    for(i=0; i<=p->mxRail+1; i++){
      int dist;
      if( inUseMask & BIT(i) ) continue;
      if( iNearto<=0 ){
        iBest = i;







|







309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
    dist = i - iNearto;
    if( dist<0 ) dist = -dist;
    if( dist<iBestDist ){
      iBestDist = dist;
      iBest = i;
    }
  }

  /* If no match, consider all possible rails */
  if( iBestDist>1000 ){
    for(i=0; i<=p->mxRail+1; i++){
      int dist;
      if( inUseMask & BIT(i) ) continue;
      if( iNearto<=0 ){
        iBest = i;
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
  ** the aParent[] array.
  */
  if( (tmFlags & (TIMELINE_DISJOINT|TIMELINE_XMERGE))!=0 ){
    for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
      for(i=1; i<pRow->nParent; i++){
        GraphRow *pParent = hashFind(p, pRow->aParent[i]);
        if( pParent==0 ){
          memmove(pRow->aParent+i, pRow->aParent+i+1, 
                  sizeof(pRow->aParent[0])*(pRow->nParent-i-1));
          pRow->nParent--;
          if( i<pRow->nNonCherrypick ){
            pRow->nNonCherrypick--;
          }else{
            pRow->nCherrypick--;
          }
          i--;
        }
      }
    }
  }
 
  /* Put the deepest (earliest) merge parent first in the list.
  ** An off-screen merge parent is considered deepest.
  */
  for(pRow=p->pFirst; pRow; pRow=pRow->pNext ){
    if( pRow->nParent<=1 ) continue;
    for(i=1; i<pRow->nParent; i++){
      GraphRow *pParent = hashFind(p, pRow->aParent[i]);







|












|







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
  ** the aParent[] array.
  */
  if( (tmFlags & (TIMELINE_DISJOINT|TIMELINE_XMERGE))!=0 ){
    for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
      for(i=1; i<pRow->nParent; i++){
        GraphRow *pParent = hashFind(p, pRow->aParent[i]);
        if( pParent==0 ){
          memmove(pRow->aParent+i, pRow->aParent+i+1,
                  sizeof(pRow->aParent[0])*(pRow->nParent-i-1));
          pRow->nParent--;
          if( i<pRow->nNonCherrypick ){
            pRow->nNonCherrypick--;
          }else{
            pRow->nCherrypick--;
          }
          i--;
        }
      }
    }
  }

  /* Put the deepest (earliest) merge parent first in the list.
  ** An off-screen merge parent is considered deepest.
  */
  for(pRow=p->pFirst; pRow; pRow=pRow->pNext ){
    if( pRow->nParent<=1 ) continue;
    for(i=1; i<pRow->nParent; i++){
      GraphRow *pParent = hashFind(p, pRow->aParent[i]);
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
        /* The parent branch from which this branch emerges is on the
        ** same rail as pRow.  Do not shift as that would stack a child
        ** branch directly above its parent. */
        continue;
      }

      /* All clear.  Make the translation
      */      
      for(pLoop=pRow; pLoop && pLoop->idx<=pBottom->idx; pLoop=pLoop->pNext){
        if( pLoop->iRail==iFrom ){
          pLoop->iRail = iTo;
          pLoop->aiRiser[iTo] = pLoop->aiRiser[iFrom];
          pLoop->aiRiser[iFrom] = -1;
        }
      }







|







938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
        /* The parent branch from which this branch emerges is on the
        ** same rail as pRow.  Do not shift as that would stack a child
        ** branch directly above its parent. */
        continue;
      }

      /* All clear.  Make the translation
      */
      for(pLoop=pRow; pLoop && pLoop->idx<=pBottom->idx; pLoop=pLoop->pNext){
        if( pLoop->iRail==iFrom ){
          pLoop->iRail = iTo;
          pLoop->aiRiser[iTo] = pLoop->aiRiser[iFrom];
          pLoop->aiRiser[iFrom] = -1;
        }
      }
Changes to src/graph.js.
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
function hideGraphTooltip(){ /* Hide the tooltip */
  document.removeEventListener('keydown',onKeyDown,/* useCapture == */true);
  stopCloseTimer();
  tooltipObj.style.display = "none";
  tooltipInfo.ixActive = -1;
  tooltipInfo.idNodeActive = 0;
}
document.body.onunload = hideGraphTooltip
function stopDwellTimer(){
  if(tooltipInfo.idTimer!=0){
    clearTimeout(tooltipInfo.idTimer);
    tooltipInfo.idTimer = 0;
  }
}
function resumeCloseTimer(){







|







132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
function hideGraphTooltip(){ /* Hide the tooltip */
  document.removeEventListener('keydown',onKeyDown,/* useCapture == */true);
  stopCloseTimer();
  tooltipObj.style.display = "none";
  tooltipInfo.ixActive = -1;
  tooltipInfo.idNodeActive = 0;
}
window.onpagehide = hideGraphTooltip;
function stopDwellTimer(){
  if(tooltipInfo.idTimer!=0){
    clearTimeout(tooltipInfo.idTimer);
    tooltipInfo.idTimer = 0;
  }
}
function resumeCloseTimer(){
Changes to src/http.c.
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
  ** sha1_shared_secret()), not the original password.  So convert the
  ** password to its SHA1 encoding if it isn't already a SHA1 hash.
  **
  ** We assume that a hexadecimal string of exactly 40 characters is a
  ** SHA1 hash, not an original password.  If a user has a password which
  ** just happens to be a 40-character hex string, then this routine won't
  ** be able to distinguish it from a hash, the translation will not be
  ** performed, and the sync won't work.  
  */
  if( zPw && zPw[0] && (strlen(zPw)!=40 || !validate16(zPw,40)) ){
    const char *zProjectCode = 0;
    if( g.url.flags & URL_USE_PARENT ){
      zProjectCode = db_get("parent-project-code", 0);
    }else{
      zProjectCode = db_get("project-code", 0);







|







96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
  ** sha1_shared_secret()), not the original password.  So convert the
  ** password to its SHA1 encoding if it isn't already a SHA1 hash.
  **
  ** We assume that a hexadecimal string of exactly 40 characters is a
  ** SHA1 hash, not an original password.  If a user has a password which
  ** just happens to be a 40-character hex string, then this routine won't
  ** be able to distinguish it from a hash, the translation will not be
  ** performed, and the sync won't work.
  */
  if( zPw && zPw[0] && (strlen(zPw)!=40 || !validate16(zPw,40)) ){
    const char *zProjectCode = 0;
    if( g.url.flags & URL_USE_PARENT ){
      zProjectCode = db_get("parent-project-code", 0);
    }else{
      zProjectCode = db_get("project-code", 0);
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
  blob_write_to_file(pSend, zUplink);
  if( g.fHttpTrace ){
    fossil_print("RUN %s\n", zCmd);
  }
  rc = fossil_system(zCmd);
  if( rc ){
    fossil_warning("Transport command failed: %s\n", zCmd);
  }    
  fossil_free(zCmd);
  file_delete(zUplink);
  if( file_size(zDownlink, ExtFILE)<0 ){
    blob_zero(pReply);
  }else{
    blob_read_from_file(pReply, zDownlink, ExtFILE);
    file_delete(zDownlink);
  }
  return rc; 
}

/*
** Sign the content in pSend, compress it, and send it to the server
** via HTTP or HTTPS.  Get a reply, uncompress the reply, and store the reply
** in pRecv.  pRecv is assumed to be uninitialized when
** this routine is called - this routine will initialize it.







|








|







256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
  blob_write_to_file(pSend, zUplink);
  if( g.fHttpTrace ){
    fossil_print("RUN %s\n", zCmd);
  }
  rc = fossil_system(zCmd);
  if( rc ){
    fossil_warning("Transport command failed: %s\n", zCmd);
  }
  fossil_free(zCmd);
  file_delete(zUplink);
  if( file_size(zDownlink, ExtFILE)<0 ){
    blob_zero(pReply);
  }else{
    blob_read_from_file(pReply, zDownlink, ExtFILE);
    file_delete(zDownlink);
  }
  return rc;
}

/*
** Sign the content in pSend, compress it, and send it to the server
** via HTTP or HTTPS.  Get a reply, uncompress the reply, and store the reply
** in pRecv.  pRecv is assumed to be uninitialized when
** this routine is called - this routine will initialize it.
Changes to src/http_ssl.c.
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
} sException;
static int sslNoCertVerify = 0;  /* Do not verify SSL certs */


/* This is a self-signed cert in the PEM format that can be used when
** no other certs are available.
*/
static const char sslSelfCert[] = 
"-----BEGIN CERTIFICATE-----\n"
"MIIDMTCCAhkCFGrDmuJkkzWERP/ITBvzwwI2lv0TMA0GCSqGSIb3DQEBCwUAMFQx\n"
"CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOQzESMBAGA1UEBwwJQ2hhcmxvdHRlMRMw\n"
"EQYDVQQKDApGb3NzaWwtU0NNMQ8wDQYDVQQDDAZGb3NzaWwwIBcNMjExMjI3MTEz\n"
"MTU2WhgPMjEyMTEyMjcxMTMxNTZaMFQxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJO\n"
"QzESMBAGA1UEBwwJQ2hhcmxvdHRlMRMwEQYDVQQKDApGb3NzaWwtU0NNMQ8wDQYD\n"
"VQQDDAZGb3NzaWwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCCbTU2\n"







|







57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
} sException;
static int sslNoCertVerify = 0;  /* Do not verify SSL certs */


/* This is a self-signed cert in the PEM format that can be used when
** no other certs are available.
*/
static const char sslSelfCert[] =
"-----BEGIN CERTIFICATE-----\n"
"MIIDMTCCAhkCFGrDmuJkkzWERP/ITBvzwwI2lv0TMA0GCSqGSIb3DQEBCwUAMFQx\n"
"CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJOQzESMBAGA1UEBwwJQ2hhcmxvdHRlMRMw\n"
"EQYDVQQKDApGb3NzaWwtU0NNMQ8wDQYDVQQDDAZGb3NzaWwwIBcNMjExMjI3MTEz\n"
"MTU2WhgPMjEyMTEyMjcxMTMxNTZaMFQxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJO\n"
"QzESMBAGA1UEBwwJQ2hhcmxvdHRlMRMwEQYDVQQKDApGb3NzaWwtU0NNMQ8wDQYD\n"
"VQQDDAZGb3NzaWwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCCbTU2\n"
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
"G6wxc4kN9dLK+5S29q3nzl24/qzXoF8P9Re5KBCbrwaHgy+OEEceq5jkmfGFxXjw\n"
"pvVCNry5uAhH5NqbXZampUWqiWtM4eTaIPo7Y2mDA1uWhuWtO6F9PsnFJlQHCnwy\n"
"s/TsrXk=\n"
"-----END CERTIFICATE-----\n";

/* This is the private-key corresponding to the cert above
*/
static const char sslSelfPKey[] = 
"-----BEGIN PRIVATE KEY-----\n"
"MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCCbTU26GRQHQqL\n"
"q7vyZ0OxpAxmgfAKCxt6eIz+jBi2ZM/CB5vVXWVh2+SkSiWEA3UZiUqXxZlzmS/C\n"
"glZdiwLLDJML8B4OiV72oivFH/vJ7+cbvh1dTxnYiHuww7GfQngPrLfefiIYPDk1\n"
"GTUJHBQ7Ue477F7F8vKuHdVgwktF/JDM6M60aSqlo2D/oysirrb+dlurTlv0rjsY\n"
"Ofq6bLAajoL3qi/vek6DNssoywbge4PfbTgS9g7Gcgncbcet5pvaS12JavhFcd4J\n"
"U4Ity49Hl9S/C2MfZ1tE53xVggRwKz4FPj65M5uymTdcxtjKXtCxIE1kKxJxXQh7\n"







|







81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
"G6wxc4kN9dLK+5S29q3nzl24/qzXoF8P9Re5KBCbrwaHgy+OEEceq5jkmfGFxXjw\n"
"pvVCNry5uAhH5NqbXZampUWqiWtM4eTaIPo7Y2mDA1uWhuWtO6F9PsnFJlQHCnwy\n"
"s/TsrXk=\n"
"-----END CERTIFICATE-----\n";

/* This is the private-key corresponding to the cert above
*/
static const char sslSelfPKey[] =
"-----BEGIN PRIVATE KEY-----\n"
"MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCCbTU26GRQHQqL\n"
"q7vyZ0OxpAxmgfAKCxt6eIz+jBi2ZM/CB5vVXWVh2+SkSiWEA3UZiUqXxZlzmS/C\n"
"glZdiwLLDJML8B4OiV72oivFH/vJ7+cbvh1dTxnYiHuww7GfQngPrLfefiIYPDk1\n"
"GTUJHBQ7Ue477F7F8vKuHdVgwktF/JDM6M60aSqlo2D/oysirrb+dlurTlv0rjsY\n"
"Ofq6bLAajoL3qi/vek6DNssoywbge4PfbTgS9g7Gcgncbcet5pvaS12JavhFcd4J\n"
"U4Ity49Hl9S/C2MfZ1tE53xVggRwKz4FPj65M5uymTdcxtjKXtCxIE1kKxJxXQh7\n"
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
    "or the ssl-identity setting.");
  return 0; /* no cert available */
}

/*
** Convert an OpenSSL ASN1_TIME to an ISO8601 timestamp.
**
** Per RFC 5280, ASN1 timestamps in X.509 certificates must 
** be in UTC (Zulu timezone) with no fractional seconds.
**
** If showUtc==1, add " UTC" at the end of the returned string. This is
** not ISO8601-compliant, but makes the displayed value more user-friendly.
*/
static const char *ssl_asn1time_to_iso8601(ASN1_TIME *asn1_time,
                                           int showUtc){







|







204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
    "or the ssl-identity setting.");
  return 0; /* no cert available */
}

/*
** Convert an OpenSSL ASN1_TIME to an ISO8601 timestamp.
**
** Per RFC 5280, ASN1 timestamps in X.509 certificates must
** be in UTC (Zulu timezone) with no fractional seconds.
**
** If showUtc==1, add " UTC" at the end of the returned string. This is
** not ISO8601-compliant, but makes the displayed value more user-friendly.
*/
static const char *ssl_asn1time_to_iso8601(ASN1_TIME *asn1_time,
                                           int showUtc){
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
** Invoke this routine to disable SSL cert verification.  After
** this call is made, any SSL cert that the server provides will
** be accepted.  Communication will still be encrypted, but the
** client has no way of knowing whether it is talking to the
** real server or a man-in-the-middle imposter.
*/
void ssl_disable_cert_verification(void){
  sslNoCertVerify = 1;  
}

/*
** Open an SSL connection as a client that is to connect to the server
** identified by pUrlData.
**
*  The identify of the server is determined as follows:







|







410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
** Invoke this routine to disable SSL cert verification.  After
** this call is made, any SSL cert that the server provides will
** be accepted.  Communication will still be encrypted, but the
** client has no way of knowing whether it is talking to the
** real server or a man-in-the-middle imposter.
*/
void ssl_disable_cert_verification(void){
  sslNoCertVerify = 1;
}

/*
** Open an SSL connection as a client that is to connect to the server
** identified by pUrlData.
**
*  The identify of the server is determined as follows:
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
      X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 0, XN_FLAG_ONELINE);
      BIO_printf(mem, "\n  notBefore: %s",
                 ssl_asn1time_to_iso8601(X509_get_notBefore(cert), 1));
      BIO_printf(mem, "\n  notAfter:  %s",
                 ssl_asn1time_to_iso8601(X509_get_notAfter(cert), 1));
      BIO_printf(mem, "\n  sha256:    %s", zHash);
      desclen = BIO_get_mem_data(mem, &desc);
  
      prompt = mprintf("Unable to verify SSL cert from %s\n%.*s\n"
          "accept this cert and continue (y/N/fingerprint)? ",
          pUrlData->name, desclen, desc);
      BIO_free(mem);
  
      prompt_user(prompt, &ans);
      free(prompt);
      cReply = blob_str(&ans)[0];
      if( cReply!='y' && cReply!='Y'
       && fossil_stricmp(blob_str(&ans),zHash)!=0
      ){
        X509_free(cert);







|




|







563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
      X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 0, XN_FLAG_ONELINE);
      BIO_printf(mem, "\n  notBefore: %s",
                 ssl_asn1time_to_iso8601(X509_get_notBefore(cert), 1));
      BIO_printf(mem, "\n  notAfter:  %s",
                 ssl_asn1time_to_iso8601(X509_get_notAfter(cert), 1));
      BIO_printf(mem, "\n  sha256:    %s", zHash);
      desclen = BIO_get_mem_data(mem, &desc);

      prompt = mprintf("Unable to verify SSL cert from %s\n%.*s\n"
          "accept this cert and continue (y/N/fingerprint)? ",
          pUrlData->name, desclen, desc);
      BIO_free(mem);

      prompt_user(prompt, &ans);
      free(prompt);
      cReply = blob_str(&ans)[0];
      if( cReply!='y' && cReply!='Y'
       && fossil_stricmp(blob_str(&ans),zHash)!=0
      ){
        X509_free(cert);
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196

/*
** Return the OpenSSL version number being used.  Space to hold
** this name is obtained from fossil_malloc() and should be
** freed by the caller.
*/
char *fossil_openssl_version(void){
#if defined(FOSSIL_ENABLE_SSL) 
  return mprintf("%s (0x%09x)\n",
         SSLeay_version(SSLEAY_VERSION), OPENSSL_VERSION_NUMBER);
#else
  return mprintf("none");
#endif
}







|






1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196

/*
** Return the OpenSSL version number being used.  Space to hold
** this name is obtained from fossil_malloc() and should be
** freed by the caller.
*/
char *fossil_openssl_version(void){
#if defined(FOSSIL_ENABLE_SSL)
  return mprintf("%s (0x%09x)\n",
         SSLeay_version(SSLEAY_VERSION), OPENSSL_VERSION_NUMBER);
#else
  return mprintf("none");
#endif
}
Changes to src/http_transport.c.
133
134
135
136
137
138
139

140
141
142
143
144
145
146
  }else{
    blob_append_escaped_arg(&zCmd, pUrlData->name, 0);
  }
  if( !is_safe_fossil_command(pUrlData->fossil) ){
    fossil_fatal("the ssh:// URL is asking to run an unsafe command [%s] on "
                 "the server.", pUrlData->fossil);
  }

  blob_append_escaped_arg(&zCmd, pUrlData->fossil, 1);
  blob_append(&zCmd, " test-http", 10);
  if( pUrlData->path && pUrlData->path[0] ){
    blob_append_escaped_arg(&zCmd, pUrlData->path, 1);
  }else{
    fossil_fatal("ssh:// URI does not specify a path to the repository");
  }







>







133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
  }else{
    blob_append_escaped_arg(&zCmd, pUrlData->name, 0);
  }
  if( !is_safe_fossil_command(pUrlData->fossil) ){
    fossil_fatal("the ssh:// URL is asking to run an unsafe command [%s] on "
                 "the server.", pUrlData->fossil);
  }
  blob_append_escaped_arg(&zCmd, "PATH=$HOME/bin:$PATH", 1);
  blob_append_escaped_arg(&zCmd, pUrlData->fossil, 1);
  blob_append(&zCmd, " test-http", 10);
  if( pUrlData->path && pUrlData->path[0] ){
    blob_append_escaped_arg(&zCmd, pUrlData->path, 1);
  }else{
    fossil_fatal("ssh:// URI does not specify a path to the repository");
  }
Changes to src/import.c.
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
      gg.fromLoaded = 1;
    }else
    if( strncmp(zLine, "N ", 2)==0 ){
      /* No-op */
    }else
    if( strncmp(zLine, "property branch-nick ", 21)==0 ){
      /* Breezy uses this property to store the branch name.
      ** It has two values. Integer branch number, then the 
      ** user-readable branch name. */
      z = &zLine[21];
      next_token(&z);
      fossil_free(gg.zBranch);
      gg.zBranch = fossil_strdup(next_token(&z));
    }else
    if( strncmp(zLine, "property rebase-of ", 19)==0 ){







|







815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
      gg.fromLoaded = 1;
    }else
    if( strncmp(zLine, "N ", 2)==0 ){
      /* No-op */
    }else
    if( strncmp(zLine, "property branch-nick ", 21)==0 ){
      /* Breezy uses this property to store the branch name.
      ** It has two values. Integer branch number, then the
      ** user-readable branch name. */
      z = &zLine[21];
      next_token(&z);
      fossil_free(gg.zBranch);
      gg.zBranch = fossil_strdup(next_token(&z));
    }else
    if( strncmp(zLine, "property rebase-of ", 19)==0 ){
Changes to src/info.c.
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
      ) ){
        if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
        page_tree();
        return;
      }
      /* No directory found, look for an historic version of the file
      ** that was subsequently deleted. */
      db_prepare(&q, 
        "SELECT fid, uuid FROM mlink, filename, event, blob"
        " WHERE filename.name=%Q"
        "   AND mlink.fnid=filename.fnid AND mlink.fid>0"
        "   AND event.objid=mlink.mid"
        "   AND blob.rid=mlink.mid"
        " ORDER BY event.mtime DESC",
        zName







|







2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
      ) ){
        if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
        page_tree();
        return;
      }
      /* No directory found, look for an historic version of the file
      ** that was subsequently deleted. */
      db_prepare(&q,
        "SELECT fid, uuid FROM mlink, filename, event, blob"
        " WHERE filename.name=%Q"
        "   AND mlink.fnid=filename.fnid AND mlink.fid>0"
        "   AND event.objid=mlink.mid"
        "   AND blob.rid=mlink.mid"
        " ORDER BY event.mtime DESC",
        zName
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
      }
    }
    if( strcmp(zModAction,"approve")==0 ){
      moderation_approve('t', rid);
    }
  }
  zTktTitle = db_table_has_column("repository", "ticket", "title" )
      ? db_text("(No title)", 
                "SELECT title FROM ticket WHERE tkt_uuid=%Q", zTktName)
      : 0;
  style_set_current_feature("tinfo");
  style_header("Ticket Change Details");
  style_submenu_element("Raw", "%R/artifact/%s", zUuid);
  style_submenu_element("History", "%R/tkthistory/%s#%S", zTktName,zUuid);
  style_submenu_element("Page", "%R/tktview/%t", zTktName);







|







2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
      }
    }
    if( strcmp(zModAction,"approve")==0 ){
      moderation_approve('t', rid);
    }
  }
  zTktTitle = db_table_has_column("repository", "ticket", "title" )
      ? db_text("(No title)",
                "SELECT title FROM ticket WHERE tkt_uuid=%Q", zTktName)
      : 0;
  style_set_current_feature("tinfo");
  style_header("Ticket Change Details");
  style_submenu_element("Raw", "%R/artifact/%s", zUuid);
  style_submenu_element("History", "%R/tkthistory/%s#%S", zTktName,zUuid);
  style_submenu_element("Page", "%R/tktview/%t", zTktName);
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
  Blob ctrl;
  Blob comment;
  char *zNow;
  int nTags, nCancels;
  int i;
  Stmt q;

  if( g.argc==3 ) usage(AMEND_USAGE_STMT);
  fEditComment = find_option("edit-comment","e",0)!=0;
  zNewComment = find_option("comment","m",1);
  zComFile = find_option("message-file","M",1);
  zNewBranch = find_option("branch",0,1);
  zNewColor = find_option("bgcolor",0,1);
  zNewBrColor = find_option("branchcolor",0,1);
  if( zNewBrColor ){







<







3545
3546
3547
3548
3549
3550
3551

3552
3553
3554
3555
3556
3557
3558
  Blob ctrl;
  Blob comment;
  char *zNow;
  int nTags, nCancels;
  int i;
  Stmt q;


  fEditComment = find_option("edit-comment","e",0)!=0;
  zNewComment = find_option("comment","m",1);
  zComFile = find_option("message-file","M",1);
  zNewBranch = find_option("branch",0,1);
  zNewColor = find_option("bgcolor",0,1);
  zNewBrColor = find_option("branchcolor",0,1);
  if( zNewBrColor ){
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
3845
** If no VERSION is provided, describe the currently checked-out version.
**
** If VERSION and the found ancestor refer to the same commit, the last two
** components are omitted, unless --long is provided.  When no fitting tagged
** ancestor is found, show only the short hash of VERSION.
**
** Options:
**    --digits           Display so many hex digits of the hash 
**                       (default: the larger of 6 and the 'hash-digit' setting)
**    -d|--dirty         Show whether there are changes to be committed
**    --long             Always show all three components
**    --match GLOB       Consider only non-propagating tags matching GLOB
*/
void describe_cmd(void){
  const char *zName;







|







3830
3831
3832
3833
3834
3835
3836
3837
3838
3839
3840
3841
3842
3843
3844
** If no VERSION is provided, describe the currently checked-out version.
**
** If VERSION and the found ancestor refer to the same commit, the last two
** components are omitted, unless --long is provided.  When no fitting tagged
** ancestor is found, show only the short hash of VERSION.
**
** Options:
**    --digits           Display so many hex digits of the hash
**                       (default: the larger of 6 and the 'hash-digit' setting)
**    -d|--dirty         Show whether there are changes to be committed
**    --long             Always show all three components
**    --match GLOB       Consider only non-propagating tags matching GLOB
*/
void describe_cmd(void){
  const char *zName;
Changes to src/interwiki.c.
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
**
**    {
**      "base":  Base URL for the remote site.
**      "hash":  Append this to "base" for Hash targets.
**      "wiki":  Append this to "base" for Wiki targets.
**    }
**
** If the remote wiki is Fossil, then the correct value for "hash" 
** is "/info/" and the correct value for "wiki" is "/wiki?name=".
** If (for example) Wikipedia is the remote, then "hash" should be
** omitted and the correct value for "wiki" is "/wiki/".  
**
** PageName is link name of the target wiki.  Several different forms
** of PageName are recognized.
**
**    Path       If PageName is empty or begins with a "/" character, then
**               it is a pathname that is appended to "base".
**







|


|







43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
**
**    {
**      "base":  Base URL for the remote site.
**      "hash":  Append this to "base" for Hash targets.
**      "wiki":  Append this to "base" for Wiki targets.
**    }
**
** If the remote wiki is Fossil, then the correct value for "hash"
** is "/info/" and the correct value for "wiki" is "/wiki?name=".
** If (for example) Wikipedia is the remote, then "hash" should be
** omitted and the correct value for "wiki" is "/wiki/".
**
** PageName is link name of the target wiki.  Several different forms
** of PageName are recognized.
**
**    Path       If PageName is empty or begins with a "/" character, then
**               it is a pathname that is appended to "base".
**
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
  static Stmt q;
  for(i=0; fossil_isalnum(zTarget[i]); i++){}
  if( zTarget[i]!=':' ) return 0;
  nCode = i;
  if( nCode==4 && strncmp(zTarget,"wiki",4)==0 ) return 0;
  zPage = zTarget + nCode + 1;
  nPage = (int)strlen(zPage);
  db_static_prepare(&q, 
     "SELECT value->>'base', value->>'hash', value->>'wiki'"
     " FROM config WHERE name=lower($name) AND json_valid(value)"
  );
  zName = mprintf("interwiki:%.*s", nCode, zTarget);
  db_bind_text(&q, "$name", zName);
  while( db_step(&q)==SQLITE_ROW ){
    const char *zBase = db_column_text(&q,0);







|







80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
  static Stmt q;
  for(i=0; fossil_isalnum(zTarget[i]); i++){}
  if( zTarget[i]!=':' ) return 0;
  nCode = i;
  if( nCode==4 && strncmp(zTarget,"wiki",4)==0 ) return 0;
  zPage = zTarget + nCode + 1;
  nPage = (int)strlen(zPage);
  db_static_prepare(&q,
     "SELECT value->>'base', value->>'hash', value->>'wiki'"
     " FROM config WHERE name=lower($name) AND json_valid(value)"
  );
  zName = mprintf("interwiki:%.*s", nCode, zTarget);
  db_bind_text(&q, "$name", zName);
  while( db_step(&q)==SQLITE_ROW ){
    const char *zBase = db_column_text(&q,0);
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
    verify_all_options();
    if( g.argc<4 ) usage("delete ID ...");
    db_begin_write();
    db_unprotect(PROTECT_CONFIG);
    for(i=3; i<g.argc; i++){
      const char *zName = g.argv[i];
      db_multi_exec(
        "DELETE FROM config WHERE name='interwiki:%q'", 
        zName
      );
    }
    setup_incr_cfgcnt();
    db_protect_pop();
    db_commit_transaction();
  }else







|







220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
    verify_all_options();
    if( g.argc<4 ) usage("delete ID ...");
    db_begin_write();
    db_unprotect(PROTECT_CONFIG);
    for(i=3; i<g.argc; i++){
      const char *zName = g.argv[i];
      db_multi_exec(
        "DELETE FROM config WHERE name='interwiki:%q'",
        zName
      );
    }
    setup_incr_cfgcnt();
    db_protect_pop();
    db_commit_transaction();
  }else
Changes to src/json.c.
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
** The JSON API's public interface is documented at:
**
** https://fossil-scm.org/fossil/doc/trunk/www/json-api/index.md
**
** Notes for hackers...
**
** Here's how command/page dispatching works: json_page_top() (in HTTP mode) or
** json_cmd_top() (in CLI mode) catch the "json" path/command. Those functions then
** dispatch to a JSON-mode-specific command/page handler with the type fossil_json_f().

** See the API docs for that typedef (below) for the semantics of the callbacks.
**
**
*/
#include "VERSION.h"
#include "config.h"
#include "json.h"
#include <assert.h>
#include <time.h>

#if INTERFACE
#include "json_detail.h" /* workaround for apparent enum limitation in makeheaders */

#endif

const FossilJsonKeys_ FossilJsonKeys = {
  "anonymousSeed" /*anonymousSeed*/,
  "authToken"  /*authToken*/,
  "COMMAND_PATH" /*commandPath*/,
  "mtime" /*mtime*/,







|
|
>











|
>







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
** The JSON API's public interface is documented at:
**
** https://fossil-scm.org/fossil/doc/trunk/www/json-api/index.md
**
** Notes for hackers...
**
** Here's how command/page dispatching works: json_page_top() (in HTTP mode) or
** json_cmd_top() (in CLI mode) catch the "json" path/command. Those functions
** then dispatch to a JSON-mode-specific command/page handler with the type
** fossil_json_f().
** See the API docs for that typedef (below) for the semantics of the callbacks.
**
**
*/
#include "VERSION.h"
#include "config.h"
#include "json.h"
#include <assert.h>
#include <time.h>

#if INTERFACE
#include "json_detail.h" /* workaround for apparent enum limitation
                            in makeheaders */
#endif

const FossilJsonKeys_ FossilJsonKeys = {
  "anonymousSeed" /*anonymousSeed*/,
  "authToken"  /*authToken*/,
  "COMMAND_PATH" /*commandPath*/,
  "mtime" /*mtime*/,
174
175
176
177
178
179
180
181

182
183
184
185
186
187
188
  return 0;
}

/*
** Convenience wrapper around cson_output() which appends the output
** to pDest. pOpt may be NULL, in which case g.json.outOpt will be used.
*/
int cson_output_Blob( cson_value const * pVal, Blob * pDest, cson_output_opt const * pOpt ){

  return cson_output( pVal, cson_data_dest_Blob,
                      pDest, pOpt ? pOpt : &g.json.outOpt );
}

/*
** Convenience wrapper around cson_parse() which reads its input
** from pSrc. pSrc is rewound before parsing.







|
>







176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
  return 0;
}

/*
** Convenience wrapper around cson_output() which appends the output
** to pDest. pOpt may be NULL, in which case g.json.outOpt will be used.
*/
int cson_output_Blob( cson_value const * pVal, Blob * pDest,
                      cson_output_opt const * pOpt ){
  return cson_output( pVal, cson_data_dest_Blob,
                      pDest, pOpt ? pOpt : &g.json.outOpt );
}

/*
** Convenience wrapper around cson_parse() which reads its input
** from pSrc. pSrc is rewound before parsing.
705
706
707
708
709
710
711
712

713
714
715
716
717
718
719
         login_cookie_name(), there is(?) a potential(?) login hijacking
         window here. We may need to change the JSON auth token to be in
         the form: login_cookie_name()=...

         Then again, the hardened cookie value helps ensure that
         only a proper key/value match is valid.
      */
      cgi_replace_parameter( login_cookie_name(), cson_value_get_cstr(g.json.authToken) );

    }else if( g.isHTTP ){
      /* try fossil's conventional cookie. */
      /* Reminder: chicken/egg scenario regarding db access in CLI
         mode because login_cookie_name() needs the db. CLI
         mode does not use any authentication, so we don't need
         to support it here.
      */







|
>







708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
         login_cookie_name(), there is(?) a potential(?) login hijacking
         window here. We may need to change the JSON auth token to be in
         the form: login_cookie_name()=...

         Then again, the hardened cookie value helps ensure that
         only a proper key/value match is valid.
      */
      cgi_replace_parameter( login_cookie_name(),
                             cson_value_get_cstr(g.json.authToken) );
    }else if( g.isHTTP ){
      /* try fossil's conventional cookie. */
      /* Reminder: chicken/egg scenario regarding db access in CLI
         mode because login_cookie_name() needs the db. CLI
         mode does not use any authentication, so we don't need
         to support it here.
      */
902
903
904
905
906
907
908

909
910
911
912
913
914
915
916
        assert( head != p );
        zPart = (char*)fossil_malloc(len+1);
        memcpy(zPart, head, len);
        zPart[len] = 0;
        if(doDeHttp){
          dehttpize(zPart);
        }

        if( *zPart ){ /* should only fail if someone manages to url-encoded a NUL byte */
          part = cson_value_new_string(zPart, strlen(zPart));
          if( 0 != cson_array_append( target, part ) ){
            cson_value_free(part);
            rc = -rc;
            break;
          }
        }else{







>
|







906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
        assert( head != p );
        zPart = (char*)fossil_malloc(len+1);
        memcpy(zPart, head, len);
        zPart[len] = 0;
        if(doDeHttp){
          dehttpize(zPart);
        }
        if( *zPart ){
          /* should only fail if someone manages to url-encoded a NUL byte */
          part = cson_value_new_string(zPart, strlen(zPart));
          if( 0 != cson_array_append( target, part ) ){
            cson_value_free(part);
            rc = -rc;
            break;
          }
        }else{
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
    break;
  }

  /* g.json.reqPayload exists only to simplify some of our access to
     the request payload. We currently only use this in the context of
     Object payloads, not Arrays, strings, etc.
  */
  g.json.reqPayload.v = cson_object_get( g.json.post.o, FossilJsonKeys.payload );
  if( g.json.reqPayload.v ){
    g.json.reqPayload.o = cson_value_get_object( g.json.reqPayload.v )
        /* g.json.reqPayload.o may legally be NULL, which means only that
           g.json.reqPayload.v is-not-a Object.
        */;
  }








|







1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
    break;
  }

  /* g.json.reqPayload exists only to simplify some of our access to
     the request payload. We currently only use this in the context of
     Object payloads, not Arrays, strings, etc.
  */
  g.json.reqPayload.v = cson_object_get( g.json.post.o,FossilJsonKeys.payload );
  if( g.json.reqPayload.v ){
    g.json.reqPayload.o = cson_value_get_object( g.json.reqPayload.v )
        /* g.json.reqPayload.o may legally be NULL, which means only that
           g.json.reqPayload.v is-not-a Object.
        */;
  }

1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
  }


  if(!g.json.jsonp){
    g.json.jsonp = json_find_option_cstr("jsonp",NULL,NULL);
  }
  if(!g.isHTTP){
    g.json.errorDetailParanoia = 0 /*disable error code dumb-down for CLI mode*/;
  }

  {/* set up JSON output formatting options. */
    int indent = -1;
    indent = json_find_option_int("indent",NULL,"I",-1);
    g.json.outOpt.indentation = (0>indent)
      ? (g.isHTTP ? 0 : 1)







|







1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
  }


  if(!g.json.jsonp){
    g.json.jsonp = json_find_option_cstr("jsonp",NULL,NULL);
  }
  if(!g.isHTTP){
    g.json.errorDetailParanoia = 0;/*disable error code dumb-down for CLI mode*/
  }

  {/* set up JSON output formatting options. */
    int indent = -1;
    indent = json_find_option_int("indent",NULL,"I",-1);
    g.json.outOpt.indentation = (0>indent)
      ? (g.isHTTP ? 0 : 1)
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
** Note that CLI options are not included in the command path. Use
** find_option() to get those.
**
*/
char const * json_command_arg(unsigned short ndx){
  cson_array * ar = g.json.cmd.a;
  assert((NULL!=ar) && "Internal error. Was json_bootstrap_late() called?");
  assert((g.argc>1) && "Internal error - we never should have gotten this far.");
  if( g.json.cmd.offset < 0 ){
    /* first-time setup. */
    short i = 0;
#define NEXT cson_string_cstr(          \
                 cson_value_get_string( \
                   cson_array_get(ar,i) \
                   ))







|







1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
** Note that CLI options are not included in the command path. Use
** find_option() to get those.
**
*/
char const * json_command_arg(unsigned short ndx){
  cson_array * ar = g.json.cmd.a;
  assert((NULL!=ar) && "Internal error. Was json_bootstrap_late() called?");
  assert((g.argc>1) &&"Internal error - we never should have gotten this far.");
  if( g.json.cmd.offset < 0 ){
    /* first-time setup. */
    short i = 0;
#define NEXT cson_string_cstr(          \
                 cson_value_get_string( \
                   cson_array_get(ar,i) \
                   ))
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
    }
  }
#undef NEXT
  if(g.json.cmd.offset < 0){
    return NULL;
  }else{
    ndx = g.json.cmd.offset + ndx;
    return cson_string_cstr(cson_value_get_string(cson_array_get( ar, g.json.cmd.offset + ndx )));

  }
}

/* Returns the C-string form of json_auth_token(), or NULL
** if json_auth_token() returns NULL.
*/
char const * json_auth_token_cstr(){
  return cson_value_get_cstr( json_auth_token() );
}

/*
** Returns the JsonPageDef with the given name, or NULL if no match is
** found.
**
** head must be a pointer to an array of JsonPageDefs in which the
** last entry has a NULL name.
*/
JsonPageDef const * json_handler_for_name( char const * name, JsonPageDef const * head ){

  JsonPageDef const * pageDef = head;
  assert( head != NULL );
  if(name && *name) for( ; pageDef->name; ++pageDef ){
    if( 0 == strcmp(name, pageDef->name) ){
      return pageDef;
    }
  }







|
>

















|
>







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
    }
  }
#undef NEXT
  if(g.json.cmd.offset < 0){
    return NULL;
  }else{
    ndx = g.json.cmd.offset + ndx;
    return cson_string_cstr(cson_value_get_string(
                              cson_array_get( ar, g.json.cmd.offset + ndx )));
  }
}

/* Returns the C-string form of json_auth_token(), or NULL
** if json_auth_token() returns NULL.
*/
char const * json_auth_token_cstr(){
  return cson_value_get_cstr( json_auth_token() );
}

/*
** Returns the JsonPageDef with the given name, or NULL if no match is
** found.
**
** head must be a pointer to an array of JsonPageDefs in which the
** last entry has a NULL name.
*/
JsonPageDef const * json_handler_for_name( char const * name,
                                           JsonPageDef const * head ){
  JsonPageDef const * pageDef = head;
  assert( head != NULL );
  if(name && *name) for( ; pageDef->name; ++pageDef ){
    if( 0 == strcmp(name, pageDef->name) ){
      return pageDef;
    }
  }
1290
1291
1292
1293
1294
1295
1296
1297

1298
1299
1300

1301
1302
1303
1304
1305
1306
1307
*/
static cson_value * json_response_command_path(){
  if(!g.json.cmd.a){
    return NULL;
  }else{
    cson_value * rc = NULL;
    Blob path = empty_blob;
    unsigned int aLen = g.json.dispatchDepth+1; /*cson_array_length_get(g.json.cmd.a);*/

    unsigned int i = 1;
    for( ; i < aLen; ++i ){
      char const * part = cson_string_cstr(cson_value_get_string(cson_array_get(g.json.cmd.a, i)));

      if(!part){
#if 1
          fossil_warning("Iterating further than expected in %s.",
                       __FILE__);
#endif
          break;
      }







|
>


|
>







1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
*/
static cson_value * json_response_command_path(){
  if(!g.json.cmd.a){
    return NULL;
  }else{
    cson_value * rc = NULL;
    Blob path = empty_blob;
    unsigned int aLen = g.json.dispatchDepth+1;
                                 /*cson_array_length_get(g.json.cmd.a);*/
    unsigned int i = 1;
    for( ; i < aLen; ++i ){
      char const * part = cson_string_cstr(cson_value_get_string(
                                             cson_array_get(g.json.cmd.a, i)));
      if(!part){
#if 1
          fossil_warning("Iterating further than expected in %s.",
                       __FILE__);
#endif
          break;
      }
1327
1328
1329
1330
1331
1332
1333
1334

1335
1336
1337
1338
1339
1340
1341
*/
cson_value * json_g_to_json(){
  cson_object * o = NULL;
  cson_object * pay = NULL;
  pay = o = cson_new_object();

#define INT(OBJ,K) cson_object_set(o, #K, json_new_int(OBJ.K))
#define CSTR(OBJ,K) cson_object_set(o, #K, OBJ.K ? json_new_string(OBJ.K) : cson_value_null())

#define VAL(K,V) cson_object_set(o, #K, (V) ? (V) : cson_value_null())
  VAL(capabilities, json_cap_value());
  INT(g, argc);
  INT(g, isConst);
  CSTR(g, zConfigDbName);
  INT(g, repositoryOpen);
  INT(g, localOpen);







|
>







1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
*/
cson_value * json_g_to_json(){
  cson_object * o = NULL;
  cson_object * pay = NULL;
  pay = o = cson_new_object();

#define INT(OBJ,K) cson_object_set(o, #K, json_new_int(OBJ.K))
#define CSTR(OBJ,K) cson_object_set(o, #K, OBJ.K ? json_new_string(OBJ.K) \
                                                 : cson_value_null())
#define VAL(K,V) cson_object_set(o, #K, (V) ? (V) : cson_value_null())
  VAL(capabilities, json_cap_value());
  INT(g, argc);
  INT(g, isConst);
  CSTR(g, zConfigDbName);
  INT(g, repositoryOpen);
  INT(g, localOpen);
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821

1822
1823
1824
1825
1826
1827
1828
    cson_string * kDesc;
    cson_array_reserve( list, 35 );
    kRC = cson_new_string("resultCode",10);
    kSymbol = cson_new_string("cSymbol",7);
    kNumber = cson_new_string("number",6);
    kDesc = cson_new_string("description",11);
#define C(K) obj = cson_new_object(); \
    cson_object_set_s(obj, kRC, json_new_string(json_rc_cstr(FSL_JSON_E_##K)) ); \
    cson_object_set_s(obj, kSymbol, json_new_string("FSL_JSON_E_"#K) );             \
    cson_object_set_s(obj, kNumber, cson_value_new_integer(FSL_JSON_E_##K) );        \
    cson_object_set_s(obj, kDesc, json_new_string(json_err_cstr(FSL_JSON_E_##K))); \

    cson_array_append( list, cson_object_value(obj) ); obj = NULL;

    C(GENERIC);
    C(INVALID_REQUEST);
    C(UNKNOWN_COMMAND);
    C(UNKNOWN);
    C(TIMEOUT);







|
|
|
|
>







1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
    cson_string * kDesc;
    cson_array_reserve( list, 35 );
    kRC = cson_new_string("resultCode",10);
    kSymbol = cson_new_string("cSymbol",7);
    kNumber = cson_new_string("number",6);
    kDesc = cson_new_string("description",11);
#define C(K) obj = cson_new_object(); \
    cson_object_set_s(obj, kRC,json_new_string(json_rc_cstr(FSL_JSON_E_##K))); \
    cson_object_set_s(obj, kSymbol, json_new_string("FSL_JSON_E_"#K) );        \
    cson_object_set_s(obj, kNumber, cson_value_new_integer(FSL_JSON_E_##K) );  \
    cson_object_set_s(obj, kDesc, \
                      json_new_string(json_err_cstr(FSL_JSON_E_##K))); \
    cson_array_append( list, cson_object_value(obj) ); obj = NULL;

    C(GENERIC);
    C(INVALID_REQUEST);
    C(UNKNOWN_COMMAND);
    C(UNKNOWN);
    C(TIMEOUT);
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
  if( !g.perm.Read ){
    json_set_err(FSL_JSON_E_DENIED,
                 "Requires 'o' permissions.");
    return NULL;
  }
  full = json_find_option_bool("full",NULL,"f",
              json_find_option_bool("verbose",NULL,"v",0));
#define SETBUF(O,K) cson_object_set(O, K, cson_value_new_string(zBuf, strlen(zBuf)));


  jv = cson_value_new_object();
  jo = cson_value_get_object(jv);

  zTmp = db_get("project-name",NULL);
  cson_object_set(jo, "projectName", json_new_string(zTmp));
  fossil_free(zTmp);
  zTmp = db_get("project-description",NULL);
  cson_object_set(jo, "projectDescription", json_new_string(zTmp));
  fossil_free(zTmp);
  zTmp = NULL;
  fsize = file_size(g.zRepositoryName, ExtFILE);
  cson_object_set(jo, "repositorySize", 
                  cson_value_new_integer((cson_int_t)fsize));

  if(full){
    n = db_int(0, "SELECT count(*) FROM blob");
    m = db_int(0, "SELECT count(*) FROM delta");
    cson_object_set(jo, "blobCount", cson_value_new_integer((cson_int_t)n));
    cson_object_set(jo, "deltaCount", cson_value_new_integer((cson_int_t)m));







|
>












|







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
  if( !g.perm.Read ){
    json_set_err(FSL_JSON_E_DENIED,
                 "Requires 'o' permissions.");
    return NULL;
  }
  full = json_find_option_bool("full",NULL,"f",
              json_find_option_bool("verbose",NULL,"v",0));
#define SETBUF(O,K) cson_object_set(O, K, \
                                    cson_value_new_string(zBuf, strlen(zBuf)));

  jv = cson_value_new_object();
  jo = cson_value_get_object(jv);

  zTmp = db_get("project-name",NULL);
  cson_object_set(jo, "projectName", json_new_string(zTmp));
  fossil_free(zTmp);
  zTmp = db_get("project-description",NULL);
  cson_object_set(jo, "projectDescription", json_new_string(zTmp));
  fossil_free(zTmp);
  zTmp = NULL;
  fsize = file_size(g.zRepositoryName, ExtFILE);
  cson_object_set(jo, "repositorySize",
                  cson_value_new_integer((cson_int_t)fsize));

  if(full){
    n = db_int(0, "SELECT count(*) FROM blob");
    m = db_int(0, "SELECT count(*) FROM delta");
    cson_object_set(jo, "blobCount", cson_value_new_integer((cson_int_t)n));
    cson_object_set(jo, "deltaCount", cson_value_new_integer((cson_int_t)m));
2066
2067
2068
2069
2070
2071
2072

2073
2074
2075
2076
2077
2078
2079
2080
2081

2082

2083

2084
2085
2086

2087

2088
2089
2090
2091
2092
2093
2094
  }/*full*/
  n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
                " + 0.99");
  cson_object_set(jo, "ageDays", cson_value_new_integer((cson_int_t)n));
  cson_object_set(jo, "ageYears", cson_value_new_double(n/365.2425));
  sqlite3_snprintf(BufLen, zBuf, db_get("project-code",""));
  SETBUF(jo, "projectCode");

  cson_object_set(jo, "compiler", cson_value_new_string(COMPILER_NAME, strlen(COMPILER_NAME)));

  jv2 = cson_value_new_object();
  jo2 = cson_value_get_object(jv2);
  cson_object_set(jo, "sqlite", jv2);
  sqlite3_snprintf(BufLen, zBuf, "%.19s [%.10s] (%s)",
                   sqlite3_sourceid(), &sqlite3_sourceid()[20], sqlite3_libversion());
  SETBUF(jo2, "version");
  cson_object_set(jo2, "pageCount", cson_value_new_integer((cson_int_t)db_int(0, "PRAGMA repository.page_count")));

  cson_object_set(jo2, "pageSize", cson_value_new_integer((cson_int_t)db_int(0, "PRAGMA repository.page_size")));

  cson_object_set(jo2, "freeList", cson_value_new_integer((cson_int_t)db_int(0, "PRAGMA repository.freelist_count")));

  sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0, "PRAGMA repository.encoding"));
  SETBUF(jo2, "encoding");
  sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0, "PRAGMA repository.journal_mode"));

  cson_object_set(jo2, "journalMode", *zBuf ? cson_value_new_string(zBuf, strlen(zBuf)) : cson_value_null());

  return jv;
#undef SETBUF
}











>
|




|
|

|
>
|
>
|
>
|

|
>
|
>







2078
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
  }/*full*/
  n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
                " + 0.99");
  cson_object_set(jo, "ageDays", cson_value_new_integer((cson_int_t)n));
  cson_object_set(jo, "ageYears", cson_value_new_double(n/365.2425));
  sqlite3_snprintf(BufLen, zBuf, db_get("project-code",""));
  SETBUF(jo, "projectCode");
  cson_object_set(jo, "compiler",
                  cson_value_new_string(COMPILER_NAME, strlen(COMPILER_NAME)));

  jv2 = cson_value_new_object();
  jo2 = cson_value_get_object(jv2);
  cson_object_set(jo, "sqlite", jv2);
  sqlite3_snprintf(BufLen, zBuf, "%.19s [%.10s] (%s)", sqlite3_sourceid(),
                   &sqlite3_sourceid()[20], sqlite3_libversion());
  SETBUF(jo2, "version");
  cson_object_set(jo2, "pageCount", cson_value_new_integer(
                  (cson_int_t)db_int(0, "PRAGMA repository.page_count")));
  cson_object_set(jo2, "pageSize", cson_value_new_integer(
                  (cson_int_t)db_int(0, "PRAGMA repository.page_size")));
  cson_object_set(jo2, "freeList", cson_value_new_integer(
                  (cson_int_t)db_int(0, "PRAGMA repository.freelist_count")));
  sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0,"PRAGMA repository.encoding"));
  SETBUF(jo2, "encoding");
  sqlite3_snprintf(BufLen, zBuf, "%s",
                   db_text(0, "PRAGMA repository.journal_mode"));
  cson_object_set(jo2, "journalMode", *zBuf ?
                 cson_value_new_string(zBuf, strlen(zBuf)) : cson_value_null());
  return jv;
#undef SETBUF
}




2235
2236
2237
2238
2239
2240
2241
2242

2243
2244
2245
2246
2247
2248
2249
cson_value * json_page_status(void);

/*
** Mapping of names to JSON pages/commands.  Each name is a subpath of
** /json (in CGI mode) or a subcommand of the json command in CLI mode
*/
static const JsonPageDef JsonPageDefs[] = {
/* please keep alphabetically sorted (case-insensitive) for maintenance reasons. */

{"anonymousPassword", json_page_anon_password, 0},
{"artifact", json_page_artifact, 0},
{"branch", json_page_branch,0},
{"cap", json_page_cap, 0},
{"config", json_page_config, 0 },
{"diff", json_page_diff, 0},
{"dir", json_page_dir, 0},







|
>







2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
cson_value * json_page_status(void);

/*
** Mapping of names to JSON pages/commands.  Each name is a subpath of
** /json (in CGI mode) or a subcommand of the json command in CLI mode
*/
static const JsonPageDef JsonPageDefs[] = {
/* please keep alphabetically sorted (case-insensitive)
   for maintenance reasons. */
{"anonymousPassword", json_page_anon_password, 0},
{"artifact", json_page_artifact, 0},
{"branch", json_page_branch,0},
{"cap", json_page_cap, 0},
{"config", json_page_config, 0 },
{"diff", json_page_diff, 0},
{"dir", json_page_dir, 0},
Changes to src/json_artifact.c.
209
210
211
212
213
214
215
216

217
218
219
220
221
222
223
}

/*
** Sub-impl of /json/artifact for check-ins.
*/
static cson_value * json_artifact_ci( cson_object * zParent, int rid ){
  if(!g.perm.Read){
    json_set_err( FSL_JSON_E_DENIED, "Viewing check-ins requires 'o' privileges." );

    return NULL;
  }else{
    cson_value * artV = json_artifact_for_ci(rid, 1);
    cson_object * art = cson_value_get_object(artV);
    if(art){
      cson_object_merge( zParent, art, CSON_MERGE_REPLACE );
      cson_free_object(art);







|
>







209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
}

/*
** Sub-impl of /json/artifact for check-ins.
*/
static cson_value * json_artifact_ci( cson_object * zParent, int rid ){
  if(!g.perm.Read){
    json_set_err( FSL_JSON_E_DENIED,
                  "Viewing check-ins requires 'o' privileges." );
    return NULL;
  }else{
    cson_value * artV = json_artifact_for_ci(rid, 1);
    cson_object * art = cson_value_get_object(artV);
    if(art){
      cson_object_merge( zParent, art, CSON_MERGE_REPLACE );
      cson_free_object(art);
248
249
250
251
252
253
254
255

256
257
258
259
260
261
262
263
264
265
266
267
** if either the includeContent (HTTP) or -content|-c boolean flags
** (CLI) are set.
*/
static int json_artifact_get_content_format_flag(void){
  enum { MagicValue = -9 };
  int contentFormat = json_wiki_get_content_format_flag(MagicValue);
  if(MagicValue == contentFormat){
    contentFormat = json_find_option_bool("includeContent","content","c",0) /* deprecated */ ? -1 : 0;

  }
  return contentFormat;
}

extern int json_wiki_get_content_format_flag( int defaultValue ) /* json_wiki.c */;

cson_value * json_artifact_wiki(cson_object * zParent, int rid){
  if( ! g.perm.RdWiki ){
    json_set_err(FSL_JSON_E_DENIED,
                 "Requires 'j' privileges.");
    return NULL;
  }else{







|
>




|







249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
** if either the includeContent (HTTP) or -content|-c boolean flags
** (CLI) are set.
*/
static int json_artifact_get_content_format_flag(void){
  enum { MagicValue = -9 };
  int contentFormat = json_wiki_get_content_format_flag(MagicValue);
  if(MagicValue == contentFormat){
    contentFormat = json_find_option_bool("includeContent",
                                     "content","c",0) /* deprecated */ ? -1 : 0;
  }
  return contentFormat;
}

extern int json_wiki_get_content_format_flag(int defaultValue) /* json_wiki.c*/;

cson_value * json_artifact_wiki(cson_object * zParent, int rid){
  if( ! g.perm.RdWiki ){
    json_set_err(FSL_JSON_E_DENIED,
                 "Requires 'j' privileges.");
    return NULL;
  }else{
378
379
380
381
382
383
384
385

386
387
388
389
390
391
392
393
394
395
396
397
398
399
    );
  /* TODO: add a "state" flag for the file in each check-in,
     e.g. "modified", "new", "deleted".
   */
  checkin_arr = cson_new_array();
  cson_object_set(pay, "checkins", cson_array_value(checkin_arr));
  while( (SQLITE_ROW==db_step(&q) ) ){
    cson_object * row = cson_value_get_object(cson_sqlite3_row_to_object(q.pStmt));

    /* FIXME: move this isNew/isDel stuff into an SQL CASE statement. */
    char const isNew = cson_value_get_bool(cson_object_get(row,"isNew"));
    char const isDel = cson_value_get_bool(cson_object_get(row,"isDel"));
    cson_object_set(row, "isNew", NULL);
    cson_object_set(row, "isDel", NULL);
    cson_object_set(row, "state",
                    json_new_string(json_artifact_status_to_string(isNew, isDel)));
    cson_array_append( checkin_arr, cson_object_value(row) );
  }
  db_finalize(&q);
  return cson_object_value(pay);
}

/*







|
>





|
|







380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
    );
  /* TODO: add a "state" flag for the file in each check-in,
     e.g. "modified", "new", "deleted".
   */
  checkin_arr = cson_new_array();
  cson_object_set(pay, "checkins", cson_array_value(checkin_arr));
  while( (SQLITE_ROW==db_step(&q) ) ){
    cson_object * row = cson_value_get_object(
                           cson_sqlite3_row_to_object(q.pStmt));
    /* FIXME: move this isNew/isDel stuff into an SQL CASE statement. */
    char const isNew = cson_value_get_bool(cson_object_get(row,"isNew"));
    char const isDel = cson_value_get_bool(cson_object_get(row,"isDel"));
    cson_object_set(row, "isNew", NULL);
    cson_object_set(row, "isDel", NULL);
    cson_object_set(row, "state", json_new_string(
                            json_artifact_status_to_string(isNew, isDel)));
    cson_array_append( checkin_arr, cson_object_value(row) );
  }
  db_finalize(&q);
  return cson_object_value(pay);
}

/*
Changes to src/json_branch.c.
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
  Manifest *pParent;     /* Parsed parent manifest */
  Blob mcksum;           /* Self-checksum on the manifest */
  int bAutoColor = 0;    /* Value of "--bgcolor" is "auto" */

  if( fossil_strncmp(zColor, "auto", 4)==0 ) {
    bAutoColor = 1;
    zColor = 0;
  } 
  /* fossil branch new name */
  if( zBranch==0 || zBranch[0]==0 ){
    zOpt->rcErrMsg = "Branch name may not be null/empty.";
    return FSL_JSON_E_INVALID_ARGS;
  }
  if( db_exists(
        "SELECT 1 FROM tagxref"







|







200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
  Manifest *pParent;     /* Parsed parent manifest */
  Blob mcksum;           /* Self-checksum on the manifest */
  int bAutoColor = 0;    /* Value of "--bgcolor" is "auto" */

  if( fossil_strncmp(zColor, "auto", 4)==0 ) {
    bAutoColor = 1;
    zColor = 0;
  }
  /* fossil branch new name */
  if( zBranch==0 || zBranch[0]==0 ){
    zOpt->rcErrMsg = "Branch name may not be null/empty.";
    return FSL_JSON_E_INVALID_ARGS;
  }
  if( db_exists(
        "SELECT 1 FROM tagxref"
333
334
335
336
337
338
339
340

341
342
343
344
345
346
347
  }

  if(!opt.zName){
    opt.zName = json_command_arg(g.json.dispatchDepth+1);
  }

  if(!opt.zName){
    json_set_err(FSL_JSON_E_MISSING_ARGS, "'name' parameter was not specified." );

    return NULL;
  }

  opt.zColor = json_find_option_cstr("bgColor","bgcolor",NULL);
  opt.zBasis = json_find_option_cstr("basis",NULL,NULL);
  if(!opt.zBasis && !g.isHTTP){
    opt.zBasis = json_command_arg(g.json.dispatchDepth+2);







|
>







333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
  }

  if(!opt.zName){
    opt.zName = json_command_arg(g.json.dispatchDepth+1);
  }

  if(!opt.zName){
    json_set_err(FSL_JSON_E_MISSING_ARGS,
                 "'name' parameter was not specified." );
    return NULL;
  }

  opt.zColor = json_find_option_cstr("bgColor","bgcolor",NULL);
  opt.zBasis = json_find_option_cstr("basis",NULL,NULL);
  if(!opt.zBasis && !g.isHTTP){
    opt.zBasis = json_command_arg(g.json.dispatchDepth+2);
Changes to src/json_config.c.
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
  }
  for(i=0; i<nSetting; ++i){
    const Setting *pSet = &aSetting[i];
    cson_object * jSet;
    cson_value * pVal = 0, * pSrc = 0;
    jSet = cson_new_object();
    cson_object_set(pay, pSet->name, cson_object_value(jSet));
    cson_object_set(jSet, "versionable", cson_value_new_bool(pSet->versionable));
    cson_object_set(jSet, "sensitive", cson_value_new_bool(pSet->sensitive));
    cson_object_set(jSet, "defaultValue", (pSet->def && pSet->def[0])
                    ? json_new_string(pSet->def)
                    : cson_value_null());
    if( 0==pSet->sensitive || 0!=g.perm.Setup ){
      if( pSet->versionable ){
        /* Check to see if this is overridden by a versionable







|







255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
  }
  for(i=0; i<nSetting; ++i){
    const Setting *pSet = &aSetting[i];
    cson_object * jSet;
    cson_value * pVal = 0, * pSrc = 0;
    jSet = cson_new_object();
    cson_object_set(pay, pSet->name, cson_object_value(jSet));
    cson_object_set(jSet, "versionable",cson_value_new_bool(pSet->versionable));
    cson_object_set(jSet, "sensitive", cson_value_new_bool(pSet->sensitive));
    cson_object_set(jSet, "defaultValue", (pSet->def && pSet->def[0])
                    ? json_new_string(pSet->def)
                    : cson_value_null());
    if( 0==pSet->sensitive || 0!=g.perm.Setup ){
      if( pSet->versionable ){
        /* Check to see if this is overridden by a versionable
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
          Blob versionedPathname;
          blob_zero(&versionedPathname);
          blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
                       g.zLocalRoot, pSet->name);
          if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
            Blob content;
            blob_zero(&content);
            blob_read_from_file(&content, blob_str(&versionedPathname), ExtFILE);
            pSrc = json_new_string("versioned");
            pVal = json_new_string(blob_str(&content));
            blob_reset(&content);
          }
          blob_reset(&versionedPathname);
        }
      }







|







290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
          Blob versionedPathname;
          blob_zero(&versionedPathname);
          blob_appendf(&versionedPathname, "%s.fossil-settings/%s",
                       g.zLocalRoot, pSet->name);
          if( file_size(blob_str(&versionedPathname), ExtFILE)>=0 ){
            Blob content;
            blob_zero(&content);
            blob_read_from_file(&content, blob_str(&versionedPathname),ExtFILE);
            pSrc = json_new_string("versioned");
            pVal = json_new_string(blob_str(&content));
            blob_reset(&content);
          }
          blob_reset(&versionedPathname);
        }
      }
Changes to src/json_finfo.c.
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
  Blob sql = empty_blob;
  Stmt q = empty_Stmt;
  char const * zAfter = NULL;
  char const * zBefore = NULL;
  int limit = -1;
  int currentRow = 0;
  char const * zCheckin = NULL;
  char sort = -1;
  if(!g.perm.Read){
    json_set_err(FSL_JSON_E_DENIED,"Requires 'o' privileges.");
    return NULL;
  }

  json_warn( FSL_JSON_W_UNKNOWN, "Achtung: the output of the finfo command is up for change.");

  /* For the "name" argument we have to jump through some hoops to make sure that we don't
     get the fossil-internally-assigned "name" option.
  */
  zFilename = json_find_option_cstr2("name",NULL,NULL, g.json.dispatchDepth+1);
  if(!zFilename || !*zFilename){
    json_set_err(FSL_JSON_E_MISSING_ARGS, "Missing 'name' parameter.");
    return NULL;
  }

  if(0==db_int(0,"SELECT 1 FROM filename WHERE name=%Q",zFilename)){
    json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, "File entry not found.");
    return NULL;
  }

  zBefore = json_find_option_cstr("before",NULL,"b");
  zAfter = json_find_option_cstr("after",NULL,"a");
  limit = json_find_option_int("limit",NULL,"n", -1);
  zCheckin = json_find_option_cstr("checkin",NULL,"ci");

  blob_append_sql(&sql,
/*0*/   "SELECT b.uuid,"
/*1*/   "   ci.uuid,"
/*2*/   "   (SELECT uuid FROM blob WHERE rid=mlink.fid),"  /* Current file uuid */
/*3*/   "   cast(strftime('%%s',event.mtime) AS INTEGER),"
/*4*/   "   coalesce(event.euser, event.user),"
/*5*/   "   coalesce(event.ecomment, event.comment),"
/*6*/   " (SELECT uuid FROM blob WHERE rid=mlink.pid),"  /* Parent file uuid */
/*7*/   "   event.bgcolor,"
/*8*/   " b.size,"
/*9*/   " (mlink.pid==0) AS isNew,"







|




>
|

|
|




















|







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
  Blob sql = empty_blob;
  Stmt q = empty_Stmt;
  char const * zAfter = NULL;
  char const * zBefore = NULL;
  int limit = -1;
  int currentRow = 0;
  char const * zCheckin = NULL;
  signed char sort = -1;
  if(!g.perm.Read){
    json_set_err(FSL_JSON_E_DENIED,"Requires 'o' privileges.");
    return NULL;
  }
  json_warn( FSL_JSON_W_UNKNOWN,
             "Achtung: the output of the finfo command is up for change.");

  /* For the "name" argument we have to jump through some hoops to make sure
     that we don't get the fossil-internally-assigned "name" option.
  */
  zFilename = json_find_option_cstr2("name",NULL,NULL, g.json.dispatchDepth+1);
  if(!zFilename || !*zFilename){
    json_set_err(FSL_JSON_E_MISSING_ARGS, "Missing 'name' parameter.");
    return NULL;
  }

  if(0==db_int(0,"SELECT 1 FROM filename WHERE name=%Q",zFilename)){
    json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, "File entry not found.");
    return NULL;
  }

  zBefore = json_find_option_cstr("before",NULL,"b");
  zAfter = json_find_option_cstr("after",NULL,"a");
  limit = json_find_option_int("limit",NULL,"n", -1);
  zCheckin = json_find_option_cstr("checkin",NULL,"ci");

  blob_append_sql(&sql,
/*0*/   "SELECT b.uuid,"
/*1*/   "   ci.uuid,"
/*2*/   "   (SELECT uuid FROM blob WHERE rid=mlink.fid),"/* Current file uuid */
/*3*/   "   cast(strftime('%%s',event.mtime) AS INTEGER),"
/*4*/   "   coalesce(event.euser, event.user),"
/*5*/   "   coalesce(event.ecomment, event.comment),"
/*6*/   " (SELECT uuid FROM blob WHERE rid=mlink.pid),"  /* Parent file uuid */
/*7*/   "   event.bgcolor,"
/*8*/   " b.size,"
/*9*/   " (mlink.pid==0) AS isNew,"
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
               );

  if( zCheckin && *zCheckin ){
    char * zU = NULL;
    int rc = name_to_uuid2( zCheckin, "ci", &zU );
    /*printf("zCheckin=[%s], zU=[%s]", zCheckin, zU);*/
    if(rc<=0){
      json_set_err((rc<0) ? FSL_JSON_E_AMBIGUOUS_UUID : FSL_JSON_E_RESOURCE_NOT_FOUND,

                   "Check-in hash %s.", (rc<0) ? "is ambiguous" : "not found");
      blob_reset(&sql);
      return NULL;
    }
    blob_append_sql(&sql, " AND ci.uuid='%q'", zU);
    free(zU);
  }else{
    if( zAfter && *zAfter ){
      blob_append_sql(&sql, " AND event.mtime>=julianday('%q')", zAfter);
      sort = 1;
    }else if( zBefore && *zBefore ){
      blob_append_sql(&sql, " AND event.mtime<=julianday('%q')", zBefore);
    }
  }

  blob_append_sql(&sql," ORDER BY event.mtime %s /*sort*/", (sort>0?"ASC":"DESC"));

  /*printf("SQL=\n%s\n",blob_str(&sql));*/
  db_prepare(&q, "%s", blob_sql_text(&sql));
  blob_reset(&sql);

  pay = cson_new_object();
  cson_object_set(pay, "name", json_new_string(zFilename));
  if( limit > 0 ){
    cson_object_set(pay, "limit", json_new_int(limit));
  }
  checkins = cson_new_array();
  cson_object_set(pay, "checkins", cson_array_value(checkins));
  while( db_step(&q)==SQLITE_ROW ){
    cson_object * row = cson_new_object();
    int const isNew = db_column_int(&q,9);
    int const isDel = db_column_int(&q,10);
    cson_array_append( checkins, cson_object_value(row) );
    cson_object_set(row, "checkin", json_new_string( db_column_text(&q,1) ));
    cson_object_set(row, "uuid", json_new_string( db_column_text(&q,2) ));
    /*cson_object_set(row, "parentArtifact", json_new_string( db_column_text(&q,6) ));*/

    cson_object_set(row, "timestamp", json_new_int( db_column_int64(&q,3) ));
    cson_object_set(row, "user", json_new_string( db_column_text(&q,4) ));
    cson_object_set(row, "comment", json_new_string( db_column_text(&q,5) ));
    /*cson_object_set(row, "bgColor", json_new_string( db_column_text(&q,7) ));*/

    cson_object_set(row, "size", json_new_int( db_column_int64(&q,8) ));
    cson_object_set(row, "state",
                    json_new_string(json_artifact_status_to_string(isNew,isDel)));
    if( (0 < limit) && (++currentRow >= limit) ){
      break;
    }
  }
  db_finalize(&q);

  return pay ? cson_object_value(pay) : NULL;
}



#endif /* FOSSIL_ENABLE_JSON */







|
>















|
>


















|
>



|
>

|
|












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

  if( zCheckin && *zCheckin ){
    char * zU = NULL;
    int rc = name_to_uuid2( zCheckin, "ci", &zU );
    /*printf("zCheckin=[%s], zU=[%s]", zCheckin, zU);*/
    if(rc<=0){
      json_set_err((rc<0) ? FSL_JSON_E_AMBIGUOUS_UUID :
                            FSL_JSON_E_RESOURCE_NOT_FOUND,
                   "Check-in hash %s.", (rc<0) ? "is ambiguous" : "not found");
      blob_reset(&sql);
      return NULL;
    }
    blob_append_sql(&sql, " AND ci.uuid='%q'", zU);
    free(zU);
  }else{
    if( zAfter && *zAfter ){
      blob_append_sql(&sql, " AND event.mtime>=julianday('%q')", zAfter);
      sort = 1;
    }else if( zBefore && *zBefore ){
      blob_append_sql(&sql, " AND event.mtime<=julianday('%q')", zBefore);
    }
  }

  blob_append_sql(&sql," ORDER BY event.mtime %s /*sort*/",
                        (sort>0 ? "ASC" : "DESC"));
  /*printf("SQL=\n%s\n",blob_str(&sql));*/
  db_prepare(&q, "%s", blob_sql_text(&sql));
  blob_reset(&sql);

  pay = cson_new_object();
  cson_object_set(pay, "name", json_new_string(zFilename));
  if( limit > 0 ){
    cson_object_set(pay, "limit", json_new_int(limit));
  }
  checkins = cson_new_array();
  cson_object_set(pay, "checkins", cson_array_value(checkins));
  while( db_step(&q)==SQLITE_ROW ){
    cson_object * row = cson_new_object();
    int const isNew = db_column_int(&q,9);
    int const isDel = db_column_int(&q,10);
    cson_array_append( checkins, cson_object_value(row) );
    cson_object_set(row, "checkin", json_new_string( db_column_text(&q,1) ));
    cson_object_set(row, "uuid", json_new_string( db_column_text(&q,2) ));
    /*cson_object_set(row, "parentArtifact",
                      json_new_string( db_column_text(&q,6) ));*/
    cson_object_set(row, "timestamp", json_new_int( db_column_int64(&q,3) ));
    cson_object_set(row, "user", json_new_string( db_column_text(&q,4) ));
    cson_object_set(row, "comment", json_new_string( db_column_text(&q,5) ));
    /*cson_object_set(row, "bgColor",
                      json_new_string( db_column_text(&q,7) ));*/
    cson_object_set(row, "size", json_new_int( db_column_int64(&q,8) ));
    cson_object_set(row, "state", json_new_string(
                            json_artifact_status_to_string(isNew, isDel)));
    if( (0 < limit) && (++currentRow >= limit) ){
      break;
    }
  }
  db_finalize(&q);

  return pay ? cson_object_value(pay) : NULL;
}



#endif /* FOSSIL_ENABLE_JSON */
Changes to src/json_login.c.
153
154
155
156
157
158
159
160

161
162

163
164
165
166
167
168
169
    }
    payload = cson_value_new_object();
    po = cson_value_get_object(payload);
    cson_object_set(po, "authToken", json_new_string(cookie));
    free(cookie);
    cson_object_set(po, "name", json_new_string(name));
    cap = db_text(NULL, "SELECT cap FROM user WHERE login=%Q", name);
    cson_object_set(po, "capabilities", cap ? json_new_string(cap) : cson_value_null() );

    free(cap);
    cson_object_set(po, "loginCookieName", json_new_string( login_cookie_name() ) );

    /* TODO: add loginExpiryTime to the payload. To do this properly
       we "should" add an ([unsigned] int *) to
       login_set_user_cookie() and login_set_anon_cookie(), to which
       the expiry time is assigned. (Remember that JSON doesn't do
       unsigned int.)

       For non-anonymous users we could also simply query the







|
>

|
>







153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
    }
    payload = cson_value_new_object();
    po = cson_value_get_object(payload);
    cson_object_set(po, "authToken", json_new_string(cookie));
    free(cookie);
    cson_object_set(po, "name", json_new_string(name));
    cap = db_text(NULL, "SELECT cap FROM user WHERE login=%Q", name);
    cson_object_set(po, "capabilities",
                    cap ? json_new_string(cap) : cson_value_null() );
    free(cap);
    cson_object_set(po, "loginCookieName",
                    json_new_string( login_cookie_name() ) );
    /* TODO: add loginExpiryTime to the payload. To do this properly
       we "should" add an ([unsigned] int *) to
       login_set_user_cookie() and login_set_anon_cookie(), to which
       the expiry time is assigned. (Remember that JSON doesn't do
       unsigned int.)

       For non-anonymous users we could also simply query the
Changes to src/json_tag.c.
115
116
117
118
119
120
121
122

123
124
125
126
127
128
129
  cson_object_set(pay, "raw", cson_value_new_bool(fRaw));
  {
    Blob uu = empty_blob;
    int rc;
    blob_append(&uu, zName, -1);
    rc = name_to_uuid(&uu, 9, "*");
    if(0!=rc){
      json_set_err(FSL_JSON_E_UNKNOWN,"Could not convert name back to artifact hash!");

      blob_reset(&uu);
      goto error;
    }
    cson_object_set(pay, "appliedTo", json_new_string(blob_buffer(&uu)));
    blob_reset(&uu);
  }








|
>







115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
  cson_object_set(pay, "raw", cson_value_new_bool(fRaw));
  {
    Blob uu = empty_blob;
    int rc;
    blob_append(&uu, zName, -1);
    rc = name_to_uuid(&uu, 9, "*");
    if(0!=rc){
      json_set_err(FSL_JSON_E_UNKNOWN,
                   "Could not convert name back to artifact hash!");
      blob_reset(&uu);
      goto error;
    }
    cson_object_set(pay, "appliedTo", json_new_string(blob_buffer(&uu)));
    blob_reset(&uu);
  }

Changes to src/json_timeline.c.
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
**
** If payload is not NULL then on success its "tag" or "branch"
** property is set to the tag/branch name found in the request.
**
** Only one of "tag" or "branch" modes will work at a time, and if
** both are specified, which one takes precedence is unspecified.
*/
static char json_timeline_add_tag_branch_clause(Blob *pSql,
                                                cson_object * pPayload){
  char const * zTag = NULL;
  char const * zBranch = NULL;
  char const * zMiOnly = NULL;
  char const * zUnhide = NULL;
  int tagid = 0;
  if(! g.perm.Read ){
    return 0;







|
|







141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
**
** If payload is not NULL then on success its "tag" or "branch"
** property is set to the tag/branch name found in the request.
**
** Only one of "tag" or "branch" modes will work at a time, and if
** both are specified, which one takes precedence is unspecified.
*/
static signed char json_timeline_add_tag_branch_clause(Blob *pSql,
                                                       cson_object * pPayload){
  char const * zTag = NULL;
  char const * zBranch = NULL;
  char const * zMiOnly = NULL;
  char const * zUnhide = NULL;
  int tagid = 0;
  if(! g.perm.Read ){
    return 0;
167
168
169
170
171
172
173
174

175
176
177
178
179
180
181
182
183

184
185
186
187
188
189
190
  zUnhide = json_find_option_cstr("unhide",NULL,NULL);
  tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'",
                 zTag);
  if(tagid<=0){
    return -1;
  }
  if(pPayload){
    cson_object_set( pPayload, zBranch ? "branch" : "tag", json_new_string(zTag) );

  }
  blob_appendf(pSql,
               " AND ("
               " EXISTS(SELECT 1 FROM tagxref"
               "        WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)",
               tagid);
  if(!zUnhide){
    blob_appendf(pSql,
               " AND NOT EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=blob.rid"

               "    WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)",
               TAG_HIDDEN);
  }
  if(zBranch){
    /* from "r" flag code in page_timeline().*/
    blob_appendf(pSql,
                 " OR EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=cid"







|
>








|
>







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
  zUnhide = json_find_option_cstr("unhide",NULL,NULL);
  tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'",
                 zTag);
  if(tagid<=0){
    return -1;
  }
  if(pPayload){
    cson_object_set( pPayload, zBranch ? "branch" : "tag",
                     json_new_string(zTag) );
  }
  blob_appendf(pSql,
               " AND ("
               " EXISTS(SELECT 1 FROM tagxref"
               "        WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)",
               tagid);
  if(!zUnhide){
    blob_appendf(pSql,
               " AND NOT EXISTS(SELECT 1 FROM plink "
               "    JOIN tagxref ON rid=blob.rid"
               "    WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)",
               TAG_HIDDEN);
  }
  if(zBranch){
    /* from "r" flag code in page_timeline().*/
    blob_appendf(pSql,
                 " OR EXISTS(SELECT 1 FROM plink JOIN tagxref ON rid=cid"
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
** of the "after" ("a") or "before" ("b") environment parameters.
** This function gives "after" precedence over "before", and only
** applies one of them.
**
** Returns -1 if it adds a "before" clause, 1 if it adds
** an "after" clause, and 0 if adds only an order-by clause.
*/
static char json_timeline_add_time_clause(Blob *pSql){
  char const * zAfter = NULL;
  char const * zBefore = NULL;
  int rc = 0;
  zAfter = json_find_option_cstr("after",NULL,"a");
  zBefore = zAfter ? NULL : json_find_option_cstr("before",NULL,"b");

  if(zAfter&&*zAfter){







|







220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
** of the "after" ("a") or "before" ("b") environment parameters.
** This function gives "after" precedence over "before", and only
** applies one of them.
**
** Returns -1 if it adds a "before" clause, 1 if it adds
** an "after" clause, and 0 if adds only an order-by clause.
*/
static signed char json_timeline_add_time_clause(Blob *pSql){
  char const * zAfter = NULL;
  char const * zBefore = NULL;
  int rc = 0;
  zAfter = json_find_option_cstr("after",NULL,"a");
  zBefore = zAfter ? NULL : json_find_option_cstr("before",NULL,"b");

  if(zAfter&&*zAfter){
350
351
352
353
354
355
356
357
358
359

360
361
362
363
364
365
366
    cson_object_set(row, "uuid", json_new_string(db_column_text(&q,3)));
    if(!isNew && (flags & json_get_changed_files_ELIDE_PARENT)){
      cson_object_set(row, "parent", json_new_string(db_column_text(&q,4)));
    }
    cson_object_set(row, "size", json_new_int(db_column_int(&q,5)));

    cson_object_set(row, "state",
                    json_new_string(json_artifact_status_to_string(isNew,isDel)));
    zDownload = mprintf("/raw/%s?name=%s",
                        /* reminder: g.zBaseURL is of course not set for CLI mode. */

                        db_column_text(&q,2),
                        db_column_text(&q,3));
    cson_object_set(row, "downloadPath", json_new_string(zDownload));
    free(zDownload);
  }
  db_finalize(&q);
  return rowsV;







|

|
>







352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
    cson_object_set(row, "uuid", json_new_string(db_column_text(&q,3)));
    if(!isNew && (flags & json_get_changed_files_ELIDE_PARENT)){
      cson_object_set(row, "parent", json_new_string(db_column_text(&q,4)));
    }
    cson_object_set(row, "size", json_new_int(db_column_int(&q,5)));

    cson_object_set(row, "state",
                  json_new_string(json_artifact_status_to_string(isNew,isDel)));
    zDownload = mprintf("/raw/%s?name=%s",
                        /* reminder: g.zBaseURL is of course not set
                           for CLI mode. */
                        db_column_text(&q,2),
                        db_column_text(&q,3));
    cson_object_set(row, "downloadPath", json_new_string(zDownload));
    free(zDownload);
  }
  db_finalize(&q);
  return rowsV;
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
    int const rid = db_column_int(&q,0);
    cson_value * rowV = json_artifact_for_ci(rid, verboseFlag);
    cson_object * row = cson_value_get_object(rowV);
    if(!row){
      if( !warnRowToJsonFailed ){
        warnRowToJsonFailed = 1;
        json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED,
                   "Could not convert at least one timeline result row to JSON." );
      }
      continue;
    }
    cson_array_append(list, rowV);
  }
#undef SET
  goto ok;







|







506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
    int const rid = db_column_int(&q,0);
    cson_value * rowV = json_artifact_for_ci(rid, verboseFlag);
    cson_object * row = cson_value_get_object(rowV);
    if(!row){
      if( !warnRowToJsonFailed ){
        warnRowToJsonFailed = 1;
        json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED,
                "Could not convert at least one timeline result row to JSON." );
      }
      continue;
    }
    cson_array_append(list, rowV);
  }
#undef SET
  goto ok;
546
547
548
549
550
551
552
553

554
555
556
557
558
559
560
561


562
563
564
565
566
567
568
  if(check){
    json_set_err(check, "Query initialization failed.");
    goto error;
  }

#if 0
  /* only for testing! */
  cson_object_set(pay, "timelineSql", cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))));

#endif
  db_multi_exec("%s", blob_buffer(&sql) /*safe-for-%s*/);
  blob_reset(&sql);
  db_prepare(&q, "SELECT"
             /* For events, the name is generally more useful than
                the uuid, but the uuid is unambiguous and can be used
                with commands like 'artifact'. */
             " substr((SELECT tagname FROM tag AS tn WHERE tn.tagid=json_timeline.tagId AND tagname LIKE 'event-%%'),7) AS name,"


             " uuid as uuid,"
             " mtime AS timestamp,"
             " comment AS comment, "
             " user AS user,"
             " eventType AS eventType"
             " FROM json_timeline"
             " ORDER BY rowid");







|
>







|
>
>







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
  if(check){
    json_set_err(check, "Query initialization failed.");
    goto error;
  }

#if 0
  /* only for testing! */
  cson_object_set(pay, "timelineSql", cson_value_new_string(blob_buffer(&sql),
                  strlen(blob_buffer(&sql))));
#endif
  db_multi_exec("%s", blob_buffer(&sql) /*safe-for-%s*/);
  blob_reset(&sql);
  db_prepare(&q, "SELECT"
             /* For events, the name is generally more useful than
                the uuid, but the uuid is unambiguous and can be used
                with commands like 'artifact'. */
             " substr((SELECT tagname FROM tag AS tn "
             "   WHERE tn.tagid=json_timeline.tagId "
             "   AND tagname LIKE 'event-%%'),7) AS name,"
             " uuid as uuid,"
             " mtime AS timestamp,"
             " comment AS comment, "
             " user AS user,"
             " eventType AS eventType"
             " FROM json_timeline"
             " ORDER BY rowid");
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
  cson_value * payV = NULL;
  cson_object * pay = NULL;
  cson_array * list = NULL;
  int check = 0;
  Stmt q = empty_Stmt;
  Blob sql = empty_blob;
  if( !g.perm.RdWiki && !g.perm.Read ){
    json_set_err( FSL_JSON_E_DENIED, "Wiki timeline requires 'o' or 'j' access.");

    return NULL;
  }
  payV = cson_value_new_object();
  pay = cson_value_get_object(payV);
  check = json_timeline_setup_sql( "w", &sql, pay );
  if(check){
    json_set_err(check, "Query initialization failed.");
    goto error;
  }

#if 0
  /* only for testing! */
  cson_object_set(pay, "timelineSql", cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))));

#endif
  db_multi_exec("%s", blob_buffer(&sql) /*safe-for-%s*/);
  blob_reset(&sql);
  db_prepare(&q, "SELECT"
             " uuid AS uuid,"
             " mtime AS timestamp,"
#if 0







|
>












|
>







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
  cson_value * payV = NULL;
  cson_object * pay = NULL;
  cson_array * list = NULL;
  int check = 0;
  Stmt q = empty_Stmt;
  Blob sql = empty_blob;
  if( !g.perm.RdWiki && !g.perm.Read ){
    json_set_err( FSL_JSON_E_DENIED,
                  "Wiki timeline requires 'o' or 'j' access.");
    return NULL;
  }
  payV = cson_value_new_object();
  pay = cson_value_get_object(payV);
  check = json_timeline_setup_sql( "w", &sql, pay );
  if(check){
    json_set_err(check, "Query initialization failed.");
    goto error;
  }

#if 0
  /* only for testing! */
  cson_object_set(pay, "timelineSql", cson_value_new_string(blob_buffer(&sql),
                  strlen(blob_buffer(&sql))));
#endif
  db_multi_exec("%s", blob_buffer(&sql) /*safe-for-%s*/);
  blob_reset(&sql);
  db_prepare(&q, "SELECT"
             " uuid AS uuid,"
             " mtime AS timestamp,"
#if 0
652
653
654
655
656
657
658
659

660
661
662
663
664
665
666
  cson_value * tmp = NULL;
  cson_value * listV = NULL;
  cson_array * list = NULL;
  int check = 0;
  Stmt q = empty_Stmt;
  Blob sql = empty_blob;
  if( !g.perm.RdTkt && !g.perm.Read ){
    json_set_err(FSL_JSON_E_DENIED, "Ticket timeline requires 'o' or 'r' access.");

    return NULL;
  }
  payV = cson_value_new_object();
  pay = cson_value_get_object(payV);
  check = json_timeline_setup_sql( "t", &sql, pay );
  if(check){
    json_set_err(check, "Query initialization failed.");







|
>







660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
  cson_value * tmp = NULL;
  cson_value * listV = NULL;
  cson_array * list = NULL;
  int check = 0;
  Stmt q = empty_Stmt;
  Blob sql = empty_blob;
  if( !g.perm.RdTkt && !g.perm.Read ){
    json_set_err(FSL_JSON_E_DENIED,
                 "Ticket timeline requires 'o' or 'r' access.");
    return NULL;
  }
  payV = cson_value_new_object();
  pay = cson_value_get_object(payV);
  check = json_timeline_setup_sql( "t", &sql, pay );
  if(check){
    json_set_err(check, "Query initialization failed.");
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
    }

    rowV = cson_sqlite3_row_to_object(q.pStmt);
    row = cson_value_get_object(rowV);
    if(!row){
      manifest_destroy(pMan);
      json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED,
                 "Could not convert at least one timeline result row to JSON." );
      continue;
    }
    /* FIXME: certainly there's a more efficient way for use to get
       the ticket UUIDs?
    */
    cson_object_set(row,"ticketUuid",json_new_string(pMan->zTicketUuid));
    manifest_destroy(pMan);







|







733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
    }

    rowV = cson_sqlite3_row_to_object(q.pStmt);
    row = cson_value_get_object(rowV);
    if(!row){
      manifest_destroy(pMan);
      json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED,
                "Could not convert at least one timeline result row to JSON." );
      continue;
    }
    /* FIXME: certainly there's a more efficient way for use to get
       the ticket UUIDs?
    */
    cson_object_set(row,"ticketUuid",json_new_string(pMan->zTicketUuid));
    manifest_destroy(pMan);
Changes to src/json_user.c.
168
169
170
171
172
173
174
175

176
177
178
179
180
181
182
** Requires either Admin, Setup, or Password access. Non-admin/setup
** users can only change their own information. Non-setup users may
** not modify the 's' permission. Admin users without setup
** permissions may not edit any other user who has the 's' permission.
**
*/
int json_user_update_from_json( cson_object * pUser ){
#define CSTR(X) cson_string_cstr(cson_value_get_string( cson_object_get(pUser, X ) ))

  char const * zName = CSTR("name");
  char const * zNameNew = zName;
  char * zNameFree = NULL;
  char const * zInfo = CSTR("info");
  char const * zCap = CSTR("capabilities");
  char const * zPW = CSTR("password");
  cson_value const * forceLogout = cson_object_get(pUser, "forceLogout");







|
>







168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
** Requires either Admin, Setup, or Password access. Non-admin/setup
** users can only change their own information. Non-setup users may
** not modify the 's' permission. Admin users without setup
** permissions may not edit any other user who has the 's' permission.
**
*/
int json_user_update_from_json( cson_object * pUser ){
#define CSTR(X) cson_string_cstr(cson_value_get_string( cson_object_get(pUser, \
                                                                        X ) ))
  char const * zName = CSTR("name");
  char const * zNameNew = zName;
  char * zNameFree = NULL;
  char const * zInfo = CSTR("info");
  char const * zCap = CSTR("capabilities");
  char const * zPW = CSTR("password");
  cson_value const * forceLogout = cson_object_get(pUser, "forceLogout");
Changes to src/json_wiki.c.
161
162
163
164
165
166
167
168

169
170
171
172
173
174
175
}

/*
** Searches for the latest version of a wiki page with the given
** name. If found it behaves like json_get_wiki_page_by_rid(theRid,
** contentFormat), else it returns NULL.
*/
cson_value * json_get_wiki_page_by_name(char const * zPageName, int contentFormat){

  int rid;
  rid = db_int(0,
               "SELECT x.rid FROM tag t, tagxref x, blob b"
               " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q' "
               " AND b.rid=x.rid"
               " ORDER BY x.mtime DESC LIMIT 1",
               zPageName







|
>







161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
}

/*
** Searches for the latest version of a wiki page with the given
** name. If found it behaves like json_get_wiki_page_by_rid(theRid,
** contentFormat), else it returns NULL.
*/
cson_value * json_get_wiki_page_by_name(char const * zPageName,
                                        int contentFormat){
  int rid;
  rid = db_int(0,
               "SELECT x.rid FROM tag t, tagxref x, blob b"
               " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q' "
               " AND b.rid=x.rid"
               " ORDER BY x.mtime DESC LIMIT 1",
               zPageName
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
  }
  zPageName = json_find_option_cstr2("name",NULL,"n",g.json.dispatchDepth+1);

  zSymName = json_find_option_cstr("uuid",NULL,"u");

  if((!zPageName||!*zPageName) && (!zSymName || !*zSymName)){
    json_set_err(FSL_JSON_E_MISSING_ARGS,
                 "At least one of the 'name' or 'uuid' arguments must be provided.");
    return NULL;
  }

  /* TODO: see if we have a page named zPageName. If not, try to resolve
     zPageName as a UUID.
  */








|







258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
  }
  zPageName = json_find_option_cstr2("name",NULL,"n",g.json.dispatchDepth+1);

  zSymName = json_find_option_cstr("uuid",NULL,"u");

  if((!zPageName||!*zPageName) && (!zSymName || !*zSymName)){
    json_set_err(FSL_JSON_E_MISSING_ARGS,
            "At least one of the 'name' or 'uuid' arguments must be provided.");
    return NULL;
  }

  /* TODO: see if we have a page named zPageName. If not, try to resolve
     zPageName as a UUID.
  */

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
    zMime = cson_value_get_cstr(cson_object_get(g.json.reqPayload.o,
                                                "mimetype"));
  }else{
    sContent = cson_value_get_string(g.json.reqPayload.v);
  }
  if(!sContent) {
    json_set_err(FSL_JSON_E_MISSING_ARGS,
                 "The 'payload' property must be either a string containing the "
                 "Fossil wiki code to preview or an object with body + mimetype "
                 "properties.");
    return NULL;
  }
  zContent = cson_string_cstr(sContent);
  blob_append( &contentOrig, zContent, (int)cson_string_length_bytes(sContent) );
  zMime = wiki_filter_mimetypes(zMime);
  if( 0==fossil_strcmp(zMime, "text/x-markdown") ){
    markdown_to_html(&contentOrig, 0, &contentHtml);
  }else if( 0==fossil_strcmp(zMime, "text/plain") ){
    blob_append(&contentHtml, "<pre class='textPlain'>", -1);
    blob_append(&contentHtml, blob_str(&contentOrig), blob_size(&contentOrig));
    blob_append(&contentHtml, "</pre>", -1);
  }else{
    wiki_convert( &contentOrig, &contentHtml, 0 );
  }
  blob_reset( &contentOrig );
  pay = cson_value_new_string( blob_str(&contentHtml), (unsigned int)blob_size(&contentHtml));

  blob_reset( &contentHtml );
  return pay;
}


/*
** Internal impl of /wiki/save and /wiki/create. If createMode is 0







|
|
|



|











|
>







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
    zMime = cson_value_get_cstr(cson_object_get(g.json.reqPayload.o,
                                                "mimetype"));
  }else{
    sContent = cson_value_get_string(g.json.reqPayload.v);
  }
  if(!sContent) {
    json_set_err(FSL_JSON_E_MISSING_ARGS,
                "The 'payload' property must be either a string containing the "
                "Fossil wiki code to preview or an object with body + mimetype "
                "properties.");
    return NULL;
  }
  zContent = cson_string_cstr(sContent);
  blob_append( &contentOrig, zContent, (int)cson_string_length_bytes(sContent));
  zMime = wiki_filter_mimetypes(zMime);
  if( 0==fossil_strcmp(zMime, "text/x-markdown") ){
    markdown_to_html(&contentOrig, 0, &contentHtml);
  }else if( 0==fossil_strcmp(zMime, "text/plain") ){
    blob_append(&contentHtml, "<pre class='textPlain'>", -1);
    blob_append(&contentHtml, blob_str(&contentOrig), blob_size(&contentOrig));
    blob_append(&contentHtml, "</pre>", -1);
  }else{
    wiki_convert( &contentOrig, &contentHtml, 0 );
  }
  blob_reset( &contentOrig );
  pay = cson_value_new_string( blob_str(&contentHtml),
                               (unsigned int)blob_size(&contentHtml));
  blob_reset( &contentHtml );
  return pay;
}


/*
** Internal impl of /wiki/save and /wiki/create. If createMode is 0
344
345
346
347
348
349
350
351

352
353
354
355
356
357
358
                                             char allowCreateIfNotExists){
  Blob content = empty_blob;  /* wiki  page content */
  cson_value * nameV;         /* wiki page name */
  char const * zPageName;     /* cstr form of page name */
  cson_value * contentV;      /* passed-in content */
  cson_value * emptyContent = NULL;  /* placeholder for empty content. */
  cson_value * payV = NULL;   /* payload/return value */
  cson_string const * jstr = NULL;  /* temp for cson_value-to-cson_string conversions. */

  char const * zMimeType = 0;
  unsigned int contentLen = 0;
  int rid;
  if( (createMode && !g.perm.NewWiki)
      || (!createMode && !g.perm.WrWiki)){
    json_set_err(FSL_JSON_E_DENIED,
                 "Requires '%c' permissions.",







|
>







346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
                                             char allowCreateIfNotExists){
  Blob content = empty_blob;  /* wiki  page content */
  cson_value * nameV;         /* wiki page name */
  char const * zPageName;     /* cstr form of page name */
  cson_value * contentV;      /* passed-in content */
  cson_value * emptyContent = NULL;  /* placeholder for empty content. */
  cson_value * payV = NULL;   /* payload/return value */
  cson_string const * jstr = NULL;  /* temp for cson_value-to-cson_string
                                       conversions. */
  char const * zMimeType = 0;
  unsigned int contentLen = 0;
  int rid;
  if( (createMode && !g.perm.NewWiki)
      || (!createMode && !g.perm.WrWiki)){
    json_set_err(FSL_JSON_E_DENIED,
                 "Requires '%c' permissions.",
Changes to src/login.c.
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <time.h>

/*
** Compute an appropriate Anti-CSRF token into g.zCsrfToken[].
*/
static void login_create_csrf_secret(const char *zSeed){
  unsigned char zResult[20];
  int i;

  sha1sum_binary(zSeed, zResult);
  for(i=0; i<sizeof(g.zCsrfToken)-1; i++){
    g.zCsrfToken[i] = "abcdefghijklmnopqrstuvwxyz"
                      "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                      "0123456789-/"[zResult[i]%64];
  }







|







52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#include <time.h>

/*
** Compute an appropriate Anti-CSRF token into g.zCsrfToken[].
*/
static void login_create_csrf_secret(const char *zSeed){
  unsigned char zResult[20];
  unsigned int i;

  sha1sum_binary(zSeed, zResult);
  for(i=0; i<sizeof(g.zCsrfToken)-1; i++){
    g.zCsrfToken[i] = "abcdefghijklmnopqrstuvwxyz"
                      "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
                      "0123456789-/"[zResult[i]%64];
  }
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
      const char *zLogin = db_column_text(&q,0);
      if( (uid = login_search_uid(&zLogin, zPasswd) ) != 0 ){
        *pzUsername = fossil_strdup(zLogin);
        break;
      }
    }
    db_finalize(&q);
  }    
  free(zSha1Pw);
  return uid;
}

/*
** Generates a login cookie value for a non-anonymous user.
**







|







252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
      const char *zLogin = db_column_text(&q,0);
      if( (uid = login_search_uid(&zLogin, zPasswd) ) != 0 ){
        *pzUsername = fossil_strdup(zLogin);
        break;
      }
    }
    db_finalize(&q);
  }
  free(zSha1Pw);
  return uid;
}

/*
** Generates a login cookie value for a non-anonymous user.
**
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
    }else{
      zAnonPw = 0;
    }
    @ <table class="login_out">
    if( P("HTTPS")==0 ){
      @ <tr><td class="form_label">Warning:</td>
      @ <td><span class='securityWarning'>
      @ Login information, including the password, 
      @ will be sent in the clear over an unencrypted connection.
      if( !g.sslNotAvailable ){
        @ Consider logging in at
        @ <a href='%s(g.zHttpsURL)'>%h(g.zHttpsURL)</a> instead.
      }
      @ </span></td></tr>
    }







|







773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
    }else{
      zAnonPw = 0;
    }
    @ <table class="login_out">
    if( P("HTTPS")==0 ){
      @ <tr><td class="form_label">Warning:</td>
      @ <td><span class='securityWarning'>
      @ Login information, including the password,
      @ will be sent in the clear over an unencrypted connection.
      if( !g.sslNotAvailable ){
        @ Consider logging in at
        @ <a href='%s(g.zHttpsURL)'>%h(g.zHttpsURL)</a> instead.
      }
      @ </span></td></tr>
    }
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
      @ </tr>
    }
    @ </table>
    if( zAnonPw && !noAnon ){
      const char *zDecoded = captcha_decode(uSeed);
      int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
      char *zCaptcha = captcha_render(zDecoded);
  
      @ <p><input type="hidden" name="cs" value="%u(uSeed)">
      @ Visitors may enter <b>anonymous</b> as the user-ID with
      @ the 8-character hexadecimal password shown below:</p>
      @ <div class="captcha"><table class="captcha"><tr><td>\
      @ <pre class="captcha">
      @ %h(zCaptcha)
      @ </pre></td></tr></table>







|







822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
      @ </tr>
    }
    @ </table>
    if( zAnonPw && !noAnon ){
      const char *zDecoded = captcha_decode(uSeed);
      int bAutoCaptcha = db_get_boolean("auto-captcha", 0);
      char *zCaptcha = captcha_render(zDecoded);

      @ <p><input type="hidden" name="cs" value="%u(uSeed)">
      @ Visitors may enter <b>anonymous</b> as the user-ID with
      @ the 8-character hexadecimal password shown below:</p>
      @ <div class="captcha"><table class="captcha"><tr><td>\
      @ <pre class="captcha">
      @ %h(zCaptcha)
      @ </pre></td></tr></table>
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
  uid = login_resetpw_suffix_is_valid(zName);
  if( uid==0 ){
    @ <p><span class="loginError">
    @ This password-reset URL is invalid, probably because it has expired.
    @ Password-reset URLs have a short lifespan.
    @ </span></p>
    style_finish_page();
    sleep(1);  /* Introduce a small delay on an invalid suffix as an 
               ** extra defense against search attacks */
    return;
  }
  fossil_redirect_to_https_if_needed(1);
  login_set_uid(uid, 0);
  if( g.perm.Setup || g.perm.Admin || !g.perm.Password || g.zLogin==0 ){
    @ <p><span class="loginError">







|







1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
  uid = login_resetpw_suffix_is_valid(zName);
  if( uid==0 ){
    @ <p><span class="loginError">
    @ This password-reset URL is invalid, probably because it has expired.
    @ Password-reset URLs have a short lifespan.
    @ </span></p>
    style_finish_page();
    sleep(1);  /* Introduce a small delay on an invalid suffix as an
               ** extra defense against search attacks */
    return;
  }
  fossil_redirect_to_https_if_needed(1);
  login_set_uid(uid, 0);
  if( g.perm.Setup || g.perm.Admin || !g.perm.Password || g.zLogin==0 ){
    @ <p><span class="loginError">
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
    pStmt = 0;
    rc = sqlite3_prepare_v2(pOther, zSQL, -1, &pStmt, 0);
    if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
      db_unprotect(PROTECT_USER);
      db_multi_exec(
        "UPDATE user SET cookie=%Q, cexpire=%.17g"
        " WHERE login=%Q",
        zHash, 
        sqlite3_column_double(pStmt, 0), zLogin
      );
      db_protect_pop();
      nXfer++;
    }
    sqlite3_finalize(pStmt);
  }







|







1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
    pStmt = 0;
    rc = sqlite3_prepare_v2(pOther, zSQL, -1, &pStmt, 0);
    if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
      db_unprotect(PROTECT_USER);
      db_multi_exec(
        "UPDATE user SET cookie=%Q, cexpire=%.17g"
        " WHERE login=%Q",
        zHash,
        sqlite3_column_double(pStmt, 0), zLogin
      );
      db_protect_pop();
      nXfer++;
    }
    sqlite3_finalize(pStmt);
  }
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
      case 'a':   p->Admin = p->RdTkt = p->WrTkt = p->Zip =
                             p->RdWiki = p->WrWiki = p->NewWiki =
                             p->ApndWiki = p->Hyperlink = p->Clone =
                             p->NewTkt = p->Password = p->RdAddr =
                             p->TktFmt = p->Attach = p->ApndTkt =
                             p->ModWiki = p->ModTkt =
                             p->RdForum = p->WrForum = p->ModForum =
                             p->WrTForum = p->AdminForum = p->Chat = 
                             p->EmailAlert = p->Announce = p->Debug = 1;
                             /* Fall thru into Read/Write */
      case 'i':   p->Read = p->Write = 1;                      break;
      case 'o':   p->Read = 1;                                 break;
      case 'z':   p->Zip = 1;                                  break;

      case 'h':   p->Hyperlink = 1;                            break;







|







1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
      case 'a':   p->Admin = p->RdTkt = p->WrTkt = p->Zip =
                             p->RdWiki = p->WrWiki = p->NewWiki =
                             p->ApndWiki = p->Hyperlink = p->Clone =
                             p->NewTkt = p->Password = p->RdAddr =
                             p->TktFmt = p->Attach = p->ApndTkt =
                             p->ModWiki = p->ModTkt =
                             p->RdForum = p->WrForum = p->ModForum =
                             p->WrTForum = p->AdminForum = p->Chat =
                             p->EmailAlert = p->Announce = p->Debug = 1;
                             /* Fall thru into Read/Write */
      case 'i':   p->Read = p->Write = 1;                      break;
      case 'o':   p->Read = 1;                                 break;
      case 'z':   p->Zip = 1;                                  break;

      case 'h':   p->Hyperlink = 1;                            break;
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
*/
void login_insert_csrf_secret(void){
  @ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)">
}

/*
** Check to see if the candidate username zUserID is already used.
** Return 1 if it is already in use.  Return 0 if the name is 
** available for a self-registeration.
*/
static int login_self_choosen_userid_already_exists(const char *zUserID){
  int rc = db_exists(
    "SELECT 1 FROM user WHERE login=%Q "
    "UNION ALL "
    "SELECT 1 FROM event WHERE user=%Q OR euser=%Q",







|







1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
*/
void login_insert_csrf_secret(void){
  @ <input type="hidden" name="csrf" value="%s(g.zCsrfToken)">
}

/*
** Check to see if the candidate username zUserID is already used.
** Return 1 if it is already in use.  Return 0 if the name is
** available for a self-registeration.
*/
static int login_self_choosen_userid_already_exists(const char *zUserID){
  int rc = db_exists(
    "SELECT 1 FROM user WHERE login=%Q "
    "UNION ALL "
    "SELECT 1 FROM event WHERE user=%Q OR euser=%Q",
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
** searches for a user or subscriber that has that email address.  If the
** email address is used no-where in the system, return 0.  If the email
** address is assigned to a particular user return the UID for that user.
** If the email address is used, but not by a particular user, return -1.
*/
static int email_address_in_use(const char *zEMail){
  int uid;
  uid = db_int(0, 
    "SELECT uid FROM user"
    " WHERE info LIKE '%%<%q>%%'", zEMail);
  if( uid>0 ){
    if( db_exists("SELECT 1 FROM user WHERE uid=%d AND ("
                  "   cap GLOB '*[as]*' OR"
                  "   find_emailaddr(info)<>%Q COLLATE nocase)",
                  uid, zEMail) ){







|







1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
** searches for a user or subscriber that has that email address.  If the
** email address is used no-where in the system, return 0.  If the email
** address is assigned to a particular user return the UID for that user.
** If the email address is used, but not by a particular user, return -1.
*/
static int email_address_in_use(const char *zEMail){
  int uid;
  uid = db_int(0,
    "SELECT uid FROM user"
    " WHERE info LIKE '%%<%q>%%'", zEMail);
  if( uid>0 ){
    if( db_exists("SELECT 1 FROM user WHERE uid=%d AND ("
                  "   cap GLOB '*[as]*' OR"
                  "   find_emailaddr(info)<>%Q COLLATE nocase)",
                  uid, zEMail) ){
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
  }
  return uid;
}

/*
** COMMAND: test-email-used
** Usage:  fossil test-email-used EMAIL ...
** 
** Given a list of email addresses, show the UID and LOGIN associated
** with each one.
*/
void test_email_used(void){
  int i;
  db_find_and_open_repository(0, 0);
  verify_all_options();







|







1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
  }
  return uid;
}

/*
** COMMAND: test-email-used
** Usage:  fossil test-email-used EMAIL ...
**
** Given a list of email addresses, show the UID and LOGIN associated
** with each one.
*/
void test_email_used(void){
  int i;
  db_find_and_open_repository(0, 0);
  verify_all_options();
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
    }else{
      char *zLogin = db_text(0, "SELECT login FROM user WHERE uid=%d", uid);
      fossil_print("%s:  UID %d (%s)\n", zEMail, uid, zLogin);
      fossil_free(zLogin);
    }
  }
}
    

/*
** Check an email address and confirm that it is valid for self-registration.
** The email address is known already to be well-formed.  Return true
** if the email address is on the allowed list.
**
** The default behavior is that any valid email address is accepted.







|







1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
    }else{
      char *zLogin = db_text(0, "SELECT login FROM user WHERE uid=%d", uid);
      fossil_print("%s:  UID %d (%s)\n", zEMail, uid, zLogin);
      fossil_free(zLogin);
    }
  }
}


/*
** Check an email address and confirm that it is valid for self-registration.
** The email address is known already to be well-formed.  Return true
** if the email address is on the allowed list.
**
** The default behavior is that any valid email address is accepted.
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
    zErr = "Incorrect CAPTCHA";
  }else if( strlen(zUserID)<6 ){
    iErrLine = 1;
    zErr = "User ID too short. Must be at least 6 characters.";
  }else if( sqlite3_strglob("*[^-a-zA-Z0-9_.]*",zUserID)==0 ){
    iErrLine = 1;
    zErr = "User ID may not contain spaces or special characters.";
  }else if( sqlite3_strlike("anonymous%", zUserID, 0)==0 
         || sqlite3_strlike("nobody%", zUserID, 0)==0
         || sqlite3_strlike("reader%", zUserID, 0)==0
         || sqlite3_strlike("developer%", zUserID, 0)==0
  ){
    iErrLine = 1;
    zErr = "This User ID is reserved. Choose something different.";
  }else if( zDName[0]==0 ){







|







1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
    zErr = "Incorrect CAPTCHA";
  }else if( strlen(zUserID)<6 ){
    iErrLine = 1;
    zErr = "User ID too short. Must be at least 6 characters.";
  }else if( sqlite3_strglob("*[^-a-zA-Z0-9_.]*",zUserID)==0 ){
    iErrLine = 1;
    zErr = "User ID may not contain spaces or special characters.";
  }else if( sqlite3_strlike("anonymous%", zUserID, 0)==0
         || sqlite3_strlike("nobody%", zUserID, 0)==0
         || sqlite3_strlike("reader%", zUserID, 0)==0
         || sqlite3_strlike("developer%", zUserID, 0)==0
  ){
    iErrLine = 1;
    zErr = "This User ID is reserved. Choose something different.";
  }else if( zDName[0]==0 ){
Changes to src/lookslike.c.
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
  const WCHAR_T *z = (WCHAR_T *)blob_buffer(pContent);
  unsigned int n = blob_size(pContent);
  int j, c, flags = LOOK_NONE;  /* Assume UTF-16 text, prove otherwise */

  if( n%sizeof(WCHAR_T) ){
    flags |= LOOK_ODD;  /* Odd number of bytes -> binary (UTF-8?) */
  }
  if( n<sizeof(WCHAR_T) ) return flags;  /* Zero or One byte -> binary (UTF-8?) */
  c = *z;
  if( bReverse ){
    c = UTF16_SWAP(c);
  }
  if( c==0 ){
    flags |= LOOK_NUL;  /* NUL character in a file -> binary */
  }else if( c=='\r' ){







|







268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
  const WCHAR_T *z = (WCHAR_T *)blob_buffer(pContent);
  unsigned int n = blob_size(pContent);
  int j, c, flags = LOOK_NONE;  /* Assume UTF-16 text, prove otherwise */

  if( n%sizeof(WCHAR_T) ){
    flags |= LOOK_ODD;  /* Odd number of bytes -> binary (UTF-8?) */
  }
  if( n<sizeof(WCHAR_T) ) return flags;/* Zero or One byte -> binary (UTF-8?) */
  c = *z;
  if( bReverse ){
    c = UTF16_SWAP(c);
  }
  if( c==0 ){
    flags |= LOOK_NUL;  /* NUL character in a file -> binary */
  }else if( c=='\r' ){
Changes to src/main.c.
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
      zNewArgv[0] = g.argv[0];
      zNewArgv[1] = "ui";
      zNewArgv[2] = g.argv[1];
      zNewArgv[3] = 0;
      g.argc = 3;
      g.argv = zNewArgv;
#endif
    }   
    zCmdName = g.argv[1];
  }
#ifndef _WIN32
  /* There is a bug in stunnel4 in which it sometimes starts up client
  ** processes without first opening file descriptor 2 (standard error).
  ** If this happens, and a subsequent open() of a database returns file
  ** descriptor 2, and then an assert() fires and writes on fd 2, that







|







850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
      zNewArgv[0] = g.argv[0];
      zNewArgv[1] = "ui";
      zNewArgv[2] = g.argv[1];
      zNewArgv[3] = 0;
      g.argc = 3;
      g.argv = zNewArgv;
#endif
    }
    zCmdName = g.argv[1];
  }
#ifndef _WIN32
  /* There is a bug in stunnel4 in which it sometimes starts up client
  ** processes without first opening file descriptor 2 (standard error).
  ** If this happens, and a subsequent open() of a database returns file
  ** descriptor 2, and then an assert() fires and writes on fd 2, that
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
      /* Remove trailing ":443" from the HOST, if any */
      if( i>4 && z[i-1]=='3' && z[i-2]=='4' && z[i-3]=='4' && z[i-4]==':' ){
        i -= 4;
      }
    }else{
      /* Remove trailing ":80" from the HOST */
      if( i>3 && z[i-1]=='0' && z[i-2]=='8' && z[i-3]==':' ) i -= 3;
    }    
    if( i && z[i-1]=='.' ) i--;
    z[i] = 0;
    zCur = PD("SCRIPT_NAME","/");
    i = strlen(zCur);
    while( i>0 && zCur[i-1]=='/' ) i--;
    if( fossil_stricmp(zMode,"on")==0 ){
      g.zBaseURL = mprintf("https://%s%.*s", z, i, zCur);







|







1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
      /* Remove trailing ":443" from the HOST, if any */
      if( i>4 && z[i-1]=='3' && z[i-2]=='4' && z[i-3]=='4' && z[i-4]==':' ){
        i -= 4;
      }
    }else{
      /* Remove trailing ":80" from the HOST */
      if( i>3 && z[i-1]=='0' && z[i-2]=='8' && z[i-3]==':' ) i -= 3;
    }
    if( i && z[i-1]=='.' ) i--;
    z[i] = 0;
    zCur = PD("SCRIPT_NAME","/");
    i = strlen(zCur);
    while( i>0 && zCur[i-1]=='/' ) i--;
    if( fossil_stricmp(zMode,"on")==0 ){
      g.zBaseURL = mprintf("https://%s%.*s", z, i, zCur);
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
  if( db_get_int("redirect-to-https",0)<iLevel ) return 0;
  if( P("HTTPS")!=0 ) return 0;
  return 1;
}

/*
** Redirect to the equivalent HTTPS request if the current connection is
** insecure and if the redirect-to-https flag greater than or equal to 
** iLevel.  iLevel is 1 for /login pages and 2 for every other page.
*/
int fossil_redirect_to_https_if_needed(int iLevel){
  if( fossil_wants_https(iLevel) ){
    const char *zQS = P("QUERY_STRING");
    char *zURL;
    if( zQS==0 || zQS[0]==0 ){







|







1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
  if( db_get_int("redirect-to-https",0)<iLevel ) return 0;
  if( P("HTTPS")!=0 ) return 0;
  return 1;
}

/*
** Redirect to the equivalent HTTPS request if the current connection is
** insecure and if the redirect-to-https flag greater than or equal to
** iLevel.  iLevel is 1 for /login pages and 2 for every other page.
*/
int fossil_redirect_to_https_if_needed(int iLevel){
  if( fossil_wants_https(iLevel) ){
    const char *zQS = P("QUERY_STRING");
    char *zURL;
    if( zQS==0 || zQS[0]==0 ){
1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
    zPathInfo += 7;
    g.nExtraURL += 7;
    cgi_replace_parameter("PATH_INFO", zPathInfo);
    cgi_replace_parameter("SCRIPT_NAME", zNewScript);
    etag_cancel();
  }

  /* If the content type is application/x-fossil or 
  ** application/x-fossil-debug, then a sync/push/pull/clone is
  ** desired, so default the PATH_INFO to /xfer
  */
  if( g.zContentType &&
      strncmp(g.zContentType, "application/x-fossil", 20)==0 ){
    /* Special case:  If the content mimetype shows that it is "fossil sync"
    ** payload, then pretend that the PATH_INFO is /xfer so that we always







|







1967
1968
1969
1970
1971
1972
1973
1974
1975
1976
1977
1978
1979
1980
1981
    zPathInfo += 7;
    g.nExtraURL += 7;
    cgi_replace_parameter("PATH_INFO", zPathInfo);
    cgi_replace_parameter("SCRIPT_NAME", zNewScript);
    etag_cancel();
  }

  /* If the content type is application/x-fossil or
  ** application/x-fossil-debug, then a sync/push/pull/clone is
  ** desired, so default the PATH_INFO to /xfer
  */
  if( g.zContentType &&
      strncmp(g.zContentType, "application/x-fossil", 20)==0 ){
    /* Special case:  If the content mimetype shows that it is "fossil sync"
    ** payload, then pretend that the PATH_INFO is /xfer so that we always
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
**   --nocompress        Do not compress HTTP replies
**   --nodelay           Omit backoffice processing if it would delay
**                       process exit
**   --nojail            Drop root privilege but do not enter the chroot jail
**   --nossl             Do not do http: to https: redirects, regardless of
**                       the redirect-to-https setting.
**   --notfound URL      Use URL as the "HTTP 404, object not found" page
**   --out FILE          Write the HTTP reply to FILE instead of to 
**                       standard output
**   --pkey FILE         Read the private key used for TLS from FILE
**   --repolist          If REPOSITORY is directory, URL "/" lists all repos
**   --scgi              Interpret input as SCGI rather than HTTP
**   --skin LABEL        Use override skin LABEL. Use an empty string ("")
**                       to force use of the current local skin config.
**   --th-trace          Trace TH1 execution (for debugging purposes)







|







2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
**   --nocompress        Do not compress HTTP replies
**   --nodelay           Omit backoffice processing if it would delay
**                       process exit
**   --nojail            Drop root privilege but do not enter the chroot jail
**   --nossl             Do not do http: to https: redirects, regardless of
**                       the redirect-to-https setting.
**   --notfound URL      Use URL as the "HTTP 404, object not found" page
**   --out FILE          Write the HTTP reply to FILE instead of to
**                       standard output
**   --pkey FILE         Read the private key used for TLS from FILE
**   --repolist          If REPOSITORY is directory, URL "/" lists all repos
**   --scgi              Interpret input as SCGI rather than HTTP
**   --skin LABEL        Use override skin LABEL. Use an empty string ("")
**                       to force use of the current local skin config.
**   --th-trace          Trace TH1 execution (for debugging purposes)
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
  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)
  const char *zStopperFile;    /* Name of file used to terminate server */







|







3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
  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)
  const char *zStopperFile;    /* Name of file used to terminate server */
3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
    ** tunnel from the local machine to the remote. */
    FILE *sshIn;
    Blob ssh;
    char zLine[1000];
    blob_init(&ssh, 0, 0);
    transport_ssh_command(&ssh);
    db_close_config();
    blob_appendf(&ssh, 
       " -t -L 127.0.0.1:%d:127.0.0.1:%d %!$",
       iPort, iPort, zRemote
    );
    if( zFossilCmd==0 ){
      blob_appendf(&ssh, " %$ fossil", "PATH=$HOME/bin:$PATH");
    }else{
      blob_appendf(&ssh, " %$", zFossilCmd);







|







3345
3346
3347
3348
3349
3350
3351
3352
3353
3354
3355
3356
3357
3358
3359
    ** tunnel from the local machine to the remote. */
    FILE *sshIn;
    Blob ssh;
    char zLine[1000];
    blob_init(&ssh, 0, 0);
    transport_ssh_command(&ssh);
    db_close_config();
    blob_appendf(&ssh,
       " -t -L 127.0.0.1:%d:127.0.0.1:%d %!$",
       iPort, iPort, zRemote
    );
    if( zFossilCmd==0 ){
      blob_appendf(&ssh, " %$ fossil", "PATH=$HOME/bin:$PATH");
    }else{
      blob_appendf(&ssh, " %$", zFossilCmd);
Changes to src/manifest.c.
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
  ** control artifact.  Make a copy, and run it through the official
  ** artifact parser.  This is the slow path, but it is rarely taken.
  */
  blob_init(&copy, 0, 0);
  blob_init(&errmsg, 0, 0);
  blob_append(&copy, zIn, nIn);
  pManifest = manifest_parse(&copy, 0, &errmsg);
  iRes = pManifest!=0;  
  manifest_destroy(pManifest);
  blob_reset(&errmsg);
  return iRes;
}

/*
** COMMAND: test-parse-manifest







|







1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
  ** control artifact.  Make a copy, and run it through the official
  ** artifact parser.  This is the slow path, but it is rarely taken.
  */
  blob_init(&copy, 0, 0);
  blob_init(&errmsg, 0, 0);
  blob_append(&copy, zIn, nIn);
  pManifest = manifest_parse(&copy, 0, &errmsg);
  iRes = pManifest!=0;
  manifest_destroy(pManifest);
  blob_reset(&errmsg);
  return iRes;
}

/*
** COMMAND: test-parse-manifest
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
                     id, blob_str(&err));
        nErr++;
      }else if( !isWF && p!=0 ){
        fossil_print("%d ERROR: manifest_is_well_formed() reported false "
                     "but manifest_parse() found nothing wrong.\n", id);
        nErr++;
      }
    }else{            
      p = manifest_get(id, CFTYPE_ANY, &err);
      if( p==0 ){
        fossil_print("%d ERROR: %s\n", id, blob_str(&err));
        nErr++;
      }
    }
    blob_reset(&err);







|







1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
                     id, blob_str(&err));
        nErr++;
      }else if( !isWF && p!=0 ){
        fossil_print("%d ERROR: manifest_is_well_formed() reported false "
                     "but manifest_parse() found nothing wrong.\n", id);
        nErr++;
      }
    }else{
      p = manifest_get(id, CFTYPE_ANY, &err);
      if( p==0 ){
        fossil_print("%d ERROR: %s\n", id, blob_str(&err));
        nErr++;
      }
    }
    blob_reset(&err);
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
** Activate EVENT triggers if they do not already exist.
*/
void manifest_create_event_triggers(void){
  if( manifest_event_triggers_are_enabled ){
    return;  /* Triggers already exists.  No-op. */
  }
  alert_create_trigger();
  manifest_event_triggers_are_enabled = 1;  
}

/*
** Disable manifest event triggers.  Drop them if they exist, but mark
** them has having been created so that they won't be recreated.  This
** is used during "rebuild" to prevent triggers from firing then.
*/







|







2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
** Activate EVENT triggers if they do not already exist.
*/
void manifest_create_event_triggers(void){
  if( manifest_event_triggers_are_enabled ){
    return;  /* Triggers already exists.  No-op. */
  }
  alert_create_trigger();
  manifest_event_triggers_are_enabled = 1;
}

/*
** Disable manifest event triggers.  Drop them if they exist, but mark
** them has having been created so that they won't be recreated.  This
** is used during "rebuild" to prevent triggers from firing then.
*/
Changes to src/markdown.c.
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
  void (*paragraph)(struct Blob *ob, struct Blob *text, void *opaque);
  void (*table)(struct Blob *ob, struct Blob *head_row, struct Blob *rows,
              void *opaque);
  void (*table_cell)(struct Blob *ob, struct Blob *text, int flags,
              void *opaque);
  void (*table_row)(struct Blob *ob, struct Blob *cells, int flags,
              void *opaque);
  void (*footnote_item)(struct Blob *ob, const struct Blob *text, 
              int index, int nUsed, void *opaque);

  /* span level callbacks - NULL or return 0 prints the span verbatim */
  int (*autolink)(struct Blob *ob, struct Blob *link,
          enum mkd_autolink type, void *opaque);
  int (*codespan)(struct Blob *ob, struct Blob *text, int nSep, void *opaque);
  int (*double_emphasis)(struct Blob *ob, struct Blob *text,







|







62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
  void (*paragraph)(struct Blob *ob, struct Blob *text, void *opaque);
  void (*table)(struct Blob *ob, struct Blob *head_row, struct Blob *rows,
              void *opaque);
  void (*table_cell)(struct Blob *ob, struct Blob *text, int flags,
              void *opaque);
  void (*table_row)(struct Blob *ob, struct Blob *cells, int flags,
              void *opaque);
  void (*footnote_item)(struct Blob *ob, const struct Blob *text,
              int index, int nUsed, void *opaque);

  /* span level callbacks - NULL or return 0 prints the span verbatim */
  int (*autolink)(struct Blob *ob, struct Blob *link,
          enum mkd_autolink type, void *opaque);
  int (*codespan)(struct Blob *ob, struct Blob *text, int nSep, void *opaque);
  int (*double_emphasis)(struct Blob *ob, struct Blob *text,
380
381
382
383
384
385
386

387
388
389
390
391
392
393
394


/* release the given working buffer back to the cache */
static void release_work_buffer(struct render *rndr, struct Blob *buf){
  if( !buf ) return;
  rndr->iDepth--;
  blob_reset(buf);

  if( rndr->nBlobCache < (int)(sizeof(rndr->aBlobCache)/sizeof(rndr->aBlobCache[0])) ){
    rndr->aBlobCache[rndr->nBlobCache++] = buf;
  }else{
    fossil_free(buf);
  }
}









>
|







380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395


/* release the given working buffer back to the cache */
static void release_work_buffer(struct render *rndr, struct Blob *buf){
  if( !buf ) return;
  rndr->iDepth--;
  blob_reset(buf);
  if( rndr->nBlobCache <
        (int)(sizeof(rndr->aBlobCache)/sizeof(rndr->aBlobCache[0])) ){
    rndr->aBlobCache[rndr->nBlobCache++] = buf;
  }else{
    fossil_free(buf);
  }
}


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
/* parse_blockquote -- handles parsing of a blockquote fragment */
static size_t parse_blockquote(
  struct Blob *ob,
  struct render *rndr,
  char *data,
  size_t size
){
  size_t beg, end = 0, pre, work_size = 0;
  char *work_data = 0;
  struct Blob *out = new_work_buffer(rndr);

























  beg = 0;
  while( beg<size ){
    for(end=beg+1; end<size && data[end-1]!='\n'; end++);
    pre = prefix_quote(data+beg, end-beg);
    if( pre ){
      beg += pre; /* skipping prefix */
    }else if( is_empty(data+beg, end-beg)
     && (end>=size

         || (prefix_quote(data+end, size-end)==0
             && !is_empty(data+end, size-end)))
    ){
      /* empty line followed by non-quote line */
      break;
    }
    if( beg<end ){ /* copy into the in-place working buffer */
      if( !work_data ){







|


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









>
|







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
1661
1662
1663
1664
1665
1666
1667
/* parse_blockquote -- handles parsing of a blockquote fragment */
static size_t parse_blockquote(
  struct Blob *ob,
  struct render *rndr,
  char *data,
  size_t size
){
  size_t beg, end = 0, pre, work_size = 0, nb, endFence = 0;
  char *work_data = 0;
  struct Blob *out = new_work_buffer(rndr);

  /* Check to see if this is a quote of a fenced code block, because
  ** if it is, then blank lines do not terminated the quoted text.  Ex:
  **
  **    > ~~~~
  **    First line
  **
  **    Line after blank
  **    ~~~~
  **
  **  If this is a quoted fenced block, then set endFence to be the
  **  offset of the end of the fenced block.
  */
  pre = prefix_quote(data,size);
  pre += is_empty(data+pre,size-pre);
  nb = prefix_fencedcode(data+pre,size-pre);
  if( nb ){
    size_t i = 0;
    char delim = data[pre];
    for(end=pre+nb; end<size && i<nb; end++){
      if( data[end]==delim ) i++; else i = 0;
    }
    if( i>=nb ) endFence = end;
  }

  beg = 0;
  while( beg<size ){
    for(end=beg+1; end<size && data[end-1]!='\n'; end++);
    pre = prefix_quote(data+beg, end-beg);
    if( pre ){
      beg += pre; /* skipping prefix */
    }else if( is_empty(data+beg, end-beg)
     && (end>=size
         || (end>endFence
             && prefix_quote(data+end, size-end)==0
             && !is_empty(data+end, size-end)))
    ){
      /* empty line followed by non-quote line */
      break;
    }
    if( beg<end ){ /* copy into the in-place working buffer */
      if( !work_data ){
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
      beg += parse_blockcode(ob, rndr, txt_data, end);
    }else if( prefix_uli(txt_data, end) ){
      beg += parse_list(ob, rndr, txt_data, end, 0);
    }else if( prefix_oli(txt_data, end) ){
      beg += parse_list(ob, rndr, txt_data, end, MKD_LIST_ORDERED);
    }else if( has_table && is_tableline(txt_data, end) ){
      beg += parse_table(ob, rndr, txt_data, end);
    }else if( prefix_fencedcode(txt_data, end) 
             && (i = char_codespan(ob, rndr, txt_data, 0, end))!=0
    ){
      beg += i;
    }else{
      beg += parse_paragraph(ob, rndr, txt_data, end);
    }
  }







|







2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
      beg += parse_blockcode(ob, rndr, txt_data, end);
    }else if( prefix_uli(txt_data, end) ){
      beg += parse_list(ob, rndr, txt_data, end, 0);
    }else if( prefix_oli(txt_data, end) ){
      beg += parse_list(ob, rndr, txt_data, end, MKD_LIST_ORDERED);
    }else if( has_table && is_tableline(txt_data, end) ){
      beg += parse_table(ob, rndr, txt_data, end);
    }else if( prefix_fencedcode(txt_data, end)
             && (i = char_codespan(ob, rndr, txt_data, 0, end))!=0
    ){
      beg += i;
    }else{
      beg += parse_paragraph(ob, rndr, txt_data, end);
    }
  }
Changes to src/markdown_html.c.
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
){
  char *zLink = blob_buffer(link);
  char *zTitle = title!=0 && blob_size(title)>0 ? blob_str(title) : 0;
  char zClose[20];

  if( zLink==0 || zLink[0]==0 ){
    zClose[0] = 0;
  }else{  
    static const int flags = 
       WIKI_NOBADLINKS |
       WIKI_MARKDOWNLINKS
    ;
    wiki_resolve_hyperlink(ob, flags, zLink, zClose, sizeof(zClose), 0, zTitle);
  }
  if( blob_size(content)==0 ){
    if( link ) blob_appendb(ob, link);







|
|







778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
){
  char *zLink = blob_buffer(link);
  char *zTitle = title!=0 && blob_size(title)>0 ? blob_str(title) : 0;
  char zClose[20];

  if( zLink==0 || zLink[0]==0 ){
    zClose[0] = 0;
  }else{
    static const int flags =
       WIKI_NOBADLINKS |
       WIKI_MARKDOWNLINKS
    ;
    wiki_resolve_hyperlink(ob, flags, zLink, zClose, sizeof(zClose), 0, zTitle);
  }
  if( blob_size(content)==0 ){
    if( link ) blob_appendb(ob, link);
Changes to src/merge.c.
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*
** Add an entry to the FV table for all files renamed between
** version N and the version specified by vid.
*/
static void add_renames(
  const char *zFnCol, /* The FV column for the filename in vid */
  int vid,            /* The desired version's- RID */
  int nid,            /* The	check-in rid for the name pivot */
  int revOK,          /* OK to move backwards (child->parent) if true */
  const char *zDebug  /* Generate trace output if not NULL */
){
  int nChng;  /* Number of file name changes */
  int *aChng; /* An array of file name changes */
  int i;      /* Loop counter */
  find_filename_changes(nid, vid, revOK, &nChng, &aChng, zDebug);







|







134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*
** Add an entry to the FV table for all files renamed between
** version N and the version specified by vid.
*/
static void add_renames(
  const char *zFnCol, /* The FV column for the filename in vid */
  int vid,            /* The desired version's- RID */
  int nid,            /* The check-in rid for the name pivot */
  int revOK,          /* OK to move backwards (child->parent) if true */
  const char *zDebug  /* Generate trace output if not NULL */
){
  int nChng;  /* Number of file name changes */
  int *aChng; /* An array of file name changes */
  int i;      /* Loop counter */
  find_filename_changes(nid, vid, revOK, &nChng, &aChng, zDebug);
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
*/
void test_show_vfile_cmd(void){
  if( g.argc!=2 ){
    fossil_fatal("unknown arguments to the %s command\n", g.argv[1]);
  }
  verify_all_options();
  db_must_be_within_tree();
  debug_show_vfile();  
}


/*
** COMMAND: merge
** COMMAND: cherry-pick
**







|







266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
*/
void test_show_vfile_cmd(void){
  if( g.argc!=2 ){
    fossil_fatal("unknown arguments to the %s command\n", g.argv[1]);
  }
  verify_all_options();
  db_must_be_within_tree();
  debug_show_vfile();
}


/*
** COMMAND: merge
** COMMAND: cherry-pick
**
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387

  /* Undocumented --debug and --show-vfile options:
  **
  ** When included on the command-line, --debug causes lots of state
  ** information to be displayed.  This option is undocumented as it
  ** might change or be eliminated in future releases.
  **
  ** The --show-vfile flag does a dump of the VFILE table for reference. 
  **
  ** 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;







|







373
374
375
376
377
378
379
380
381
382
383
384
385
386
387

  /* Undocumented --debug and --show-vfile options:
  **
  ** When included on the command-line, --debug causes lots of state
  ** information to be displayed.  This option is undocumented as it
  ** might change or be eliminated in future releases.
  **
  ** The --show-vfile flag does a dump of the VFILE table for reference.
  **
  ** 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;
Changes to src/merge3.c.
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
  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);







|







209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
  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);
Changes to src/name.c.
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
    return start_of_branch(rid, 0);
  }

  /* start:BR -> The first check-in on branch named BR */
  if( strncmp(zTag, "start:", 6)==0 ){
    rid = symbolic_name_to_rid(zTag+6, zType);
    return start_of_branch(rid, 1);
  }  
  
  /* merge-in:BR -> Most recent merge-in for the branch named BR */
  if( strncmp(zTag, "merge-in:", 9)==0 ){
    rid = symbolic_name_to_rid(zTag+9, zType);
    return start_of_branch(rid, 2);
  }

  /* symbolic-name ":" date-time */
  nTag = strlen(zTag);
  for(i=0; i<nTag-8 && zTag[i]!=':'; i++){}
  if( zTag[i]==':' 
   && (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0)!=0)
  ){
    char *zDate = mprintf("%s", &zTag[i+1]);
    char *zTagBase = mprintf("%.*s", i, zTag);
    char *zXDate;
    int nDate = strlen(zDate);
    if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){







|
|









|







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
    return start_of_branch(rid, 0);
  }

  /* start:BR -> The first check-in on branch named BR */
  if( strncmp(zTag, "start:", 6)==0 ){
    rid = symbolic_name_to_rid(zTag+6, zType);
    return start_of_branch(rid, 1);
  }

  /* merge-in:BR -> Most recent merge-in for the branch named BR */
  if( strncmp(zTag, "merge-in:", 9)==0 ){
    rid = symbolic_name_to_rid(zTag+9, zType);
    return start_of_branch(rid, 2);
  }

  /* symbolic-name ":" date-time */
  nTag = strlen(zTag);
  for(i=0; i<nTag-8 && zTag[i]!=':'; i++){}
  if( zTag[i]==':'
   && (fossil_isdate(&zTag[i+1]) || fossil_expand_datetime(&zTag[i+1],0)!=0)
  ){
    char *zDate = mprintf("%s", &zTag[i+1]);
    char *zTagBase = mprintf("%.*s", i, zTag);
    char *zXDate;
    int nDate = strlen(zDate);
    if( sqlite3_strnicmp(&zDate[nDate-3],"utc",3)==0 ){
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
    "       coalesce(euser,user), coalesce(ecomment,comment)"
    "  FROM mlink, filename, blob, event"
    " WHERE mlink.fid=%d"
    "   AND filename.fnid=mlink.fnid"
    "   AND event.objid=mlink.mid"
    "   AND blob.rid=mlink.mid"
    " ORDER BY event.mtime %s /*sort*/",
    rid, 
    (flags & WHATIS_BRIEF) ? "LIMIT 1" : "DESC");
  while( db_step(&q)==SQLITE_ROW ){
    if( flags & WHATIS_BRIEF ){
      fossil_print("mtime:      %s\n", db_column_text(&q,2));
    }
    fossil_print("file:       %s\n", db_column_text(&q,0));
    fossil_print("            part of [%S] by %s on %s\n",







|







1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
    "       coalesce(euser,user), coalesce(ecomment,comment)"
    "  FROM mlink, filename, blob, event"
    " WHERE mlink.fid=%d"
    "   AND filename.fnid=mlink.fnid"
    "   AND event.objid=mlink.mid"
    "   AND blob.rid=mlink.mid"
    " ORDER BY event.mtime %s /*sort*/",
    rid,
    (flags & WHATIS_BRIEF) ? "LIMIT 1" : "DESC");
  while( db_step(&q)==SQLITE_ROW ){
    if( flags & WHATIS_BRIEF ){
      fossil_print("mtime:      %s\n", db_column_text(&q,2));
    }
    fossil_print("file:       %s\n", db_column_text(&q,0));
    fossil_print("            part of [%S] by %s on %s\n",
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
1206
*/
void whatis_artifact(
    const char *zName,    /* Symbolic name or full hash */
    const char *zFileName,/* Optional: original filename (in file mode) */
    const char *zType,    /* Artifact type filter */
    int mFlags            /* WHATIS_* flags */
){
  const char* zNameTitle = "name:";
  int rid = symbolic_name_to_rid(zName, zType);
  if( zFileName ){
    fossil_print("%-12s%s\n", zNameTitle, zFileName);
    zNameTitle = "hash:";
  }
  if( rid<0 ){
    Stmt q;
    int cnt = 0;
    if( mFlags & WHATIS_REPO ){
      fossil_print("\nrepository: %s\n", g.zRepositoryName);
    }



    fossil_print("%-12s%s (ambiguous)\n", zNameTitle, zName);
    db_prepare(&q,
        "SELECT rid FROM blob WHERE uuid>=lower(%Q) AND uuid<(lower(%Q)||'z')",
        zName, zName
        );
    while( db_step(&q)==SQLITE_ROW ){
      if( cnt++ ) fossil_print("%12s---- meaning #%d ----\n", " ", cnt);
      whatis_rid(db_column_int(&q, 0), mFlags);
    }
    db_finalize(&q);
  }else if( rid==0 ){
    if( (mFlags & WHATIS_OMIT_UNK)==0 ){
                 /* 0123456789 12 */



      fossil_print("unknown:    %s\n", zName);
    }
  }else{
    if( mFlags & WHATIS_REPO ){
      fossil_print("\nrepository: %s\n", g.zRepositoryName);
    }



    fossil_print("%-12s%s\n", zNameTitle, zName);
    whatis_rid(rid, mFlags);
  }
}

/*
** COMMAND: whatis*
**







<

<
<
<
<






>
>
>
|












>
>
>






>
>
>
|







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
1206
1207
1208
1209
1210
*/
void whatis_artifact(
    const char *zName,    /* Symbolic name or full hash */
    const char *zFileName,/* Optional: original filename (in file mode) */
    const char *zType,    /* Artifact type filter */
    int mFlags            /* WHATIS_* flags */
){

  int rid = symbolic_name_to_rid(zName, zType);




  if( rid<0 ){
    Stmt q;
    int cnt = 0;
    if( mFlags & WHATIS_REPO ){
      fossil_print("\nrepository: %s\n", g.zRepositoryName);
    }
    if( zFileName ){
      fossil_print("%-12s%s\n", "name:", zFileName);
    }
    fossil_print("%-12s%s (ambiguous)\n", "hash:", zName);
    db_prepare(&q,
        "SELECT rid FROM blob WHERE uuid>=lower(%Q) AND uuid<(lower(%Q)||'z')",
        zName, zName
        );
    while( db_step(&q)==SQLITE_ROW ){
      if( cnt++ ) fossil_print("%12s---- meaning #%d ----\n", " ", cnt);
      whatis_rid(db_column_int(&q, 0), mFlags);
    }
    db_finalize(&q);
  }else if( rid==0 ){
    if( (mFlags & WHATIS_OMIT_UNK)==0 ){
                 /* 0123456789 12 */
      if( zFileName ){
        fossil_print("%-12s%s\n", "name:", zFileName);
      }
      fossil_print("unknown:    %s\n", zName);
    }
  }else{
    if( mFlags & WHATIS_REPO ){
      fossil_print("\nrepository: %s\n", g.zRepositoryName);
    }
    if( zFileName ){
      zName = zFileName;
    }
    fossil_print("%-12s%s\n", "name:", zName);
    whatis_rid(rid, mFlags);
  }
}

/*
** COMMAND: whatis*
**
Changes to src/patch.c.
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
}

/*
** mkdelta(X,Y)
**
** X is an numeric artifact id.  Y is a filename.
**
** Compute a compressed delta that carries X into Y.  Or return 
** and zero-length blob if X is equal to Y.
*/
static void mkdeltaFunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){







|







69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
}

/*
** mkdelta(X,Y)
**
** X is an numeric artifact id.  Y is a filename.
**
** Compute a compressed delta that carries X into Y.  Or return
** and zero-length blob if X is equal to Y.
*/
static void mkdeltaFunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
    "PRAGMA patch.page_size=512;\n"
    "CREATE TABLE patch.chng(\n"
    "  pathname TEXT,\n" /* Filename */
    "  origname TEXT,\n" /* Name before rename.  NULL if not renamed */
    "  hash TEXT,\n"     /* Baseline hash.  NULL for new files. */
    "  isexe BOOL,\n"    /* True if executable */
    "  islink BOOL,\n"   /* True if is a symbolic link */
    "  delta BLOB\n"     /* compressed delta. NULL if deleted. 
                         **    length 0 if unchanged */
    ");"
    "CREATE TABLE patch.cfg(\n"
    "  key TEXT,\n"
    "  value ANY\n"
    ");"
  );







|







160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
    "PRAGMA patch.page_size=512;\n"
    "CREATE TABLE patch.chng(\n"
    "  pathname TEXT,\n" /* Filename */
    "  origname TEXT,\n" /* Name before rename.  NULL if not renamed */
    "  hash TEXT,\n"     /* Baseline hash.  NULL for new files. */
    "  isexe BOOL,\n"    /* True if executable */
    "  islink BOOL,\n"   /* True if is a symbolic link */
    "  delta BLOB\n"     /* compressed delta. NULL if deleted.
                         **    length 0 if unchanged */
    ");"
    "CREATE TABLE patch.cfg(\n"
    "  key TEXT,\n"
    "  value ANY\n"
    ");"
  );
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
    ";", vid, g.zLocalRoot, g.zRepositoryName, g.zLogin);
  z = fossil_hostname();
  if( z ){
    db_multi_exec(
       "INSERT INTO patch.cfg(key,value)VALUES('hostname',%Q)", z);
    fossil_free(z);
  }
  
  /* New files */
  db_multi_exec(
    "INSERT INTO patch.chng(pathname,hash,isexe,islink,delta)"
    "  SELECT pathname, NULL, isexe, islink,"
    "         compress(read_co_file(%Q||pathname))"
    "    FROM vfile WHERE rid==0;",
    g.zLocalRoot







|







194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
    ";", vid, g.zLocalRoot, g.zRepositoryName, g.zLogin);
  z = fossil_hostname();
  if( z ){
    db_multi_exec(
       "INSERT INTO patch.cfg(key,value)VALUES('hostname',%Q)", z);
    fossil_free(z);
  }

  /* New files */
  db_multi_exec(
    "INSERT INTO patch.chng(pathname,hash,isexe,islink,delta)"
    "  SELECT pathname, NULL, isexe, islink,"
    "         compress(read_co_file(%Q||pathname))"
    "    FROM vfile WHERE rid==0;",
    g.zLocalRoot
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
      fossil_fatal("out of memory");
    }
#ifdef _WIN32
    fflush(out);
    _setmode(_fileno(out), _O_BINARY);
#endif
    fwrite(pData, sz, 1, out);
    sqlite3_free(pData); 
    fflush(out);
  }
}

/*
** Attempt to load and validate a patchfile identified by the first
** argument.







|







247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
      fossil_fatal("out of memory");
    }
#ifdef _WIN32
    fflush(out);
    _setmode(_fileno(out), _O_BINARY);
#endif
    fwrite(pData, sz, 1, out);
    sqlite3_free(pData);
    fflush(out);
  }
}

/*
** Attempt to load and validate a patchfile identified by the first
** argument.
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
}

/*
** Show a summary of the content of a patch on standard output
*/
void patch_view(unsigned mFlags){
  Stmt q;
  db_prepare(&q, 
    "WITH nmap(nkey,nm) AS (VALUES"
       "('baseline','BASELINE'),"
       "('project-name','PROJECT-NAME'))"
    "SELECT nm, value FROM nmap, patch.cfg WHERE nkey=key;"
  );
  while( db_step(&q)==SQLITE_ROW ){
    fossil_print("%-12s %s\n", db_column_text(&q,0), db_column_text(&q,1));
  }
  db_finalize(&q);
  if( mFlags & PATCH_VERBOSE ){
    db_prepare(&q, 
      "WITH nmap(nkey,nm,isDate) AS (VALUES"
         "('project-code','PROJECT-CODE',0),"
         "('date','TIMESTAMP',1),"
         "('user','USER',0),"
         "('hostname','HOSTNAME',0),"
         "('ckout','CHECKOUT',0),"
         "('repo','REPOSITORY',0))"







|










|







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
}

/*
** Show a summary of the content of a patch on standard output
*/
void patch_view(unsigned mFlags){
  Stmt q;
  db_prepare(&q,
    "WITH nmap(nkey,nm) AS (VALUES"
       "('baseline','BASELINE'),"
       "('project-name','PROJECT-NAME'))"
    "SELECT nm, value FROM nmap, patch.cfg WHERE nkey=key;"
  );
  while( db_step(&q)==SQLITE_ROW ){
    fossil_print("%-12s %s\n", db_column_text(&q,0), db_column_text(&q,1));
  }
  db_finalize(&q);
  if( mFlags & PATCH_VERBOSE ){
    db_prepare(&q,
      "WITH nmap(nkey,nm,isDate) AS (VALUES"
         "('project-code','PROJECT-CODE',0),"
         "('date','TIMESTAMP',1),"
         "('user','USER',0),"
         "('hostname','HOSTNAME',0),"
         "('ckout','CHECKOUT',0),"
         "('repo','REPOSITORY',0))"
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
      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{







|







431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
      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{
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
    }else{
      blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
      blob_appendf(&cmd, " add %$\n", zPathname);
      if( mFlags & PATCH_VERBOSE ){
        fossil_print("%-10s %s\n", "NEW", zPathname);
      }
    }
    if( (mFlags & PATCH_DRYRUN)==0 ){   
      if( isLink ){
        symlink_create(blob_str(&data), zPathname);
      }else{
        blob_write_to_file(&data, zPathname);
      }
      file_setexe(zPathname, isExe);
      blob_reset(&data);







|







559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
    }else{
      blob_append_escaped_arg(&cmd, g.nameOfExe, 1);
      blob_appendf(&cmd, " add %$\n", zPathname);
      if( mFlags & PATCH_VERBOSE ){
        fossil_print("%-10s %s\n", "NEW", zPathname);
      }
    }
    if( (mFlags & PATCH_DRYRUN)==0 ){
      if( isLink ){
        symlink_create(blob_str(&data), zPathname);
      }else{
        blob_write_to_file(&data, zPathname);
      }
      file_setexe(zPathname, isExe);
      blob_reset(&data);
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
    blob_appendf(&cmd, " -T");
    blob_append_escaped_arg(&cmd, zRemote, 0);
    blob_init(&remote, 0, 0);
    if( zFossilCmd==0 ){
      blob_append_escaped_arg(&cmd, "PATH=$HOME/bin:$PATH", 0);
      zFossilCmd = "fossil";
    }
    blob_appendf(&remote, "%$ patch %s%s --dir64 %z -", 
                 zFossilCmd, zRemoteCmd, zForce, encode64(zDir, -1));
    blob_append_escaped_arg(&cmd, blob_str(&remote), 0);
    blob_reset(&remote);
  }
  fossil_print("%s\n", blob_str(&cmd));
  fflush(stdout);
  f = popen(blob_str(&cmd), zRW);







|







695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
    blob_appendf(&cmd, " -T");
    blob_append_escaped_arg(&cmd, zRemote, 0);
    blob_init(&remote, 0, 0);
    if( zFossilCmd==0 ){
      blob_append_escaped_arg(&cmd, "PATH=$HOME/bin:$PATH", 0);
      zFossilCmd = "fossil";
    }
    blob_appendf(&remote, "%$ patch %s%s --dir64 %z -",
                 zFossilCmd, zRemoteCmd, zForce, encode64(zDir, -1));
    blob_append_escaped_arg(&cmd, blob_str(&remote), 0);
    blob_reset(&remote);
  }
  fossil_print("%s\n", blob_str(&cmd));
  fflush(stdout);
  f = popen(blob_str(&cmd), zRW);
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
     " FROM patch.chng"
     " ORDER BY pathname"
  );
  while( db_step(&q)==SQLITE_ROW ){
    int rid;
    const char *zName;
    Blob a, b;
 
    if( db_column_type(&q,0)!=SQLITE_INTEGER
     && db_column_type(&q,4)==SQLITE_TEXT
    ){
      char *zUuid = fossil_strdup(db_column_text(&q,4));
      char *zName = fossil_strdup(db_column_text(&q,1));
      if( mFlags & PATCH_FORCE ){
        fossil_print("ERROR cannot find base artifact %S for file \"%s\"\n",







|







775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
     " FROM patch.chng"
     " ORDER BY pathname"
  );
  while( db_step(&q)==SQLITE_ROW ){
    int rid;
    const char *zName;
    Blob a, b;

    if( db_column_type(&q,0)!=SQLITE_INTEGER
     && db_column_type(&q,4)==SQLITE_TEXT
    ){
      char *zUuid = fossil_strdup(db_column_text(&q,4));
      char *zName = fossil_strdup(db_column_text(&q,1));
      if( mFlags & PATCH_FORCE ){
        fossil_print("ERROR cannot find base artifact %S for file \"%s\"\n",
797
798
799
800
801
802
803

804
805

806
807
808
809
810
811
812

813
814
815
816
817
818
819
        fossil_fatal("base artifact %S for file \"%s\" not found",
                     zUuid, zName);
      }
    }
    zName = db_column_text(&q, 1);
    rid = db_column_int(&q, 0);


    if( db_column_type(&q,3)==SQLITE_NULL ){
      if( !bWebpage ) fossil_print("DELETE %s\n", zName);

      diff_print_index(zName, pCfg, 0);
      content_get(rid, &a);
      diff_file_mem(&a, &empty, zName, pCfg);
    }else if( rid==0 ){
      db_ephemeral_blob(&q, 3, &a);
      blob_uncompress(&a, &a);
      if( !bWebpage ) fossil_print("ADDED %s\n", zName);

      diff_print_index(zName, pCfg, 0);
      diff_file_mem(&empty, &a, zName, pCfg);
      blob_reset(&a);
    }else if( db_column_bytes(&q, 3)>0 ){
      Blob delta;
      db_ephemeral_blob(&q, 3, &delta);
      blob_uncompress(&delta, &delta);







>


>







>







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
        fossil_fatal("base artifact %S for file \"%s\" not found",
                     zUuid, zName);
      }
    }
    zName = db_column_text(&q, 1);
    rid = db_column_int(&q, 0);

    pCfg->diffFlags &= (~DIFF_FILE_MASK);
    if( db_column_type(&q,3)==SQLITE_NULL ){
      if( !bWebpage ) fossil_print("DELETE %s\n", zName);
      pCfg->diffFlags |= DIFF_FILE_DELETED;
      diff_print_index(zName, pCfg, 0);
      content_get(rid, &a);
      diff_file_mem(&a, &empty, zName, pCfg);
    }else if( rid==0 ){
      db_ephemeral_blob(&q, 3, &a);
      blob_uncompress(&a, &a);
      if( !bWebpage ) fossil_print("ADDED %s\n", zName);
      pCfg->diffFlags |= DIFF_FILE_ADDED;
      diff_print_index(zName, pCfg, 0);
      diff_file_mem(&empty, &a, zName, pCfg);
      blob_reset(&a);
    }else if( db_column_bytes(&q, 3)>0 ){
      Blob delta;
      db_ephemeral_blob(&q, 3, &delta);
      blob_uncompress(&delta, &delta);
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
**
**       Command-line options:
**
**           -f|--force         Apply the patch even though there are unsaved
**                              changes in the current check-out.  Unsaved
**                              changes will be reverted and then the patch is
**                              applied.
**           --fossilcmd EXE    Name of the "fossil" executable on the remote  
**           -n|--dry-run       Do nothing, but print what would have happened
**           -v|--verbose       Extra output explaining what happens
**
**
** > fossil patch pull REMOTE-CHECKOUT
**
**       Like "fossil patch push" except that the transfer is from remote







|







897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
**
**       Command-line options:
**
**           -f|--force         Apply the patch even though there are unsaved
**                              changes in the current check-out.  Unsaved
**                              changes will be reverted and then the patch is
**                              applied.
**           --fossilcmd EXE    Name of the "fossil" executable on the remote
**           -n|--dry-run       Do nothing, but print what would have happened
**           -v|--verbose       Extra output explaining what happens
**
**
** > fossil patch pull REMOTE-CHECKOUT
**
**       Like "fossil patch push" except that the transfer is from remote
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
    unsigned flags = 0;
    const char *zFossilCmd = find_option("fossilcmd",0,1);
    if( find_option("dry-run","n",0) )  flags |= PATCH_DRYRUN;
    if( find_option("verbose","v",0) )  flags |= PATCH_VERBOSE;
    if( find_option("force","f",0) )    flags |= PATCH_FORCE;
    db_must_be_within_tree();
    verify_all_options();
    pIn = patch_remote_command(flags & (~PATCH_FORCE), 
                 "pull", "create", zFossilCmd, "r");
    if( pIn ){
      patch_attach(0, pIn);
      pclose(pIn);
      patch_apply(flags);
    }
  }else







|







974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
    unsigned flags = 0;
    const char *zFossilCmd = find_option("fossilcmd",0,1);
    if( find_option("dry-run","n",0) )  flags |= PATCH_DRYRUN;
    if( find_option("verbose","v",0) )  flags |= PATCH_VERBOSE;
    if( find_option("force","f",0) )    flags |= PATCH_FORCE;
    db_must_be_within_tree();
    verify_all_options();
    pIn = patch_remote_command(flags & (~PATCH_FORCE),
                 "pull", "create", zFossilCmd, "r");
    if( pIn ){
      patch_attach(0, pIn);
      pclose(pIn);
      patch_apply(flags);
    }
  }else
1009
1010
1011
1012
1013
1014
1015
1016
1017
    zIn = g.argv[3];
    if( fossil_strcmp(zIn, "-")==0 ) zIn = 0;
    patch_attach(zIn, stdin);
    patch_view(flags);
  }else
  {
    goto patch_usage;
  } 
}







|

1012
1013
1014
1015
1016
1017
1018
1019
1020
    zIn = g.argv[3];
    if( fossil_strcmp(zIn, "-")==0 ) zIn = 0;
    patch_attach(zIn, stdin);
    patch_view(flags);
  }else
  {
    goto patch_usage;
  }
}
Changes to src/pikchrshow.c.
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
         ) & pikFlags){
    pikFlags |= PIKCHR_PROCESS_DIV;
  }
  if(!(PIKCHR_PROCESS_TH1 & pikFlags)
     /* If any TH1_xxx flags are set, set TH1 */
     && (PIKCHR_PROCESS_TH1_NOSVG & pikFlags || thFlags!=0)){
    pikFlags |= PIKCHR_PROCESS_TH1;
  }  
  if(zNonce){
    blob_appendf(pOut, "%s\n", zNonce);
  }
  if(PIKCHR_PROCESS_TH1 & pikFlags){
    Blob out = empty_blob;
    isErr = Th_RenderToBlob(zIn, &out, thFlags)
      ? 1 : 0;







|







133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
         ) & pikFlags){
    pikFlags |= PIKCHR_PROCESS_DIV;
  }
  if(!(PIKCHR_PROCESS_TH1 & pikFlags)
     /* If any TH1_xxx flags are set, set TH1 */
     && (PIKCHR_PROCESS_TH1_NOSVG & pikFlags || thFlags!=0)){
    pikFlags |= PIKCHR_PROCESS_TH1;
  }
  if(zNonce){
    blob_appendf(pOut, "%s\n", zNonce);
  }
  if(PIKCHR_PROCESS_TH1 & pikFlags){
    Blob out = empty_blob;
    isErr = Th_RenderToBlob(zIn, &out, thFlags)
      ? 1 : 0;
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
**
**    -div-source  Set the 'source' CSS class on the div, which tells
**                 CSS to hide the SVG and reveal the source by default.
**
**    -src       Store the input pikchr's source code in the output as
**               a separate element adjacent to the SVG one. Implied
**               by -div-source.
**                
**
**    -th        Process the input using TH1 before passing it to pikchr
**
**    -th-novar  Disable $var and $<var> TH1 processing. Use this if the
**               pikchr script uses '$' for its own purposes and that
**               causes issues. This only affects parsing of '$' outside
**               of TH1 script blocks. Code in such blocks is unaffected.







|







544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
**
**    -div-source  Set the 'source' CSS class on the div, which tells
**                 CSS to hide the SVG and reveal the source by default.
**
**    -src       Store the input pikchr's source code in the output as
**               a separate element adjacent to the SVG one. Implied
**               by -div-source.
**
**
**    -th        Process the input using TH1 before passing it to pikchr
**
**    -th-novar  Disable $var and $<var> TH1 processing. Use this if the
**               pikchr script uses '$' for its own purposes and that
**               causes issues. This only affects parsing of '$' outside
**               of TH1 script blocks. Code in such blocks is unaffected.
Changes to src/rebuild.c.
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
** executable in a way that changes the database schema.
**
** Options:
**   --analyze         Run ANALYZE on the database after rebuilding
**   --cluster         Compute clusters for unclustered artifacts
**   --compress        Strive to make the database as small as possible
**   --compress-only   Skip the rebuilding step. Do --compress only
**   --deanalyze       Remove ANALYZE tables from the database
**   --force           Force the rebuild to complete even if errors are seen
**   --ifneeded        Only do the rebuild if it would change the schema version
**   --index           Always add in the full-text search index
**   --noverify        Skip the verification of changes to the BLOB table
**   --noindex         Always omit the full-text search index
**   --pagesize N      Set the database pagesize to N. (512..65536 and power of 2)
**   --quiet           Only show output if there are errors
**   --stats           Show artifact statistics after rebuilding
**   --vacuum          Run VACUUM on the database after rebuilding
**   --wal             Set Write-Ahead-Log journalling mode on the database
*/
void rebuild_database(void){
  int forceFlag;







<





|







654
655
656
657
658
659
660

661
662
663
664
665
666
667
668
669
670
671
672
673
** executable in a way that changes the database schema.
**
** Options:
**   --analyze         Run ANALYZE on the database after rebuilding
**   --cluster         Compute clusters for unclustered artifacts
**   --compress        Strive to make the database as small as possible
**   --compress-only   Skip the rebuilding step. Do --compress only

**   --force           Force the rebuild to complete even if errors are seen
**   --ifneeded        Only do the rebuild if it would change the schema version
**   --index           Always add in the full-text search index
**   --noverify        Skip the verification of changes to the BLOB table
**   --noindex         Always omit the full-text search index
**   --pagesize N      Set the database pagesize to N (512..65536, power of 2)
**   --quiet           Only show output if there are errors
**   --stats           Show artifact statistics after rebuilding
**   --vacuum          Run VACUUM on the database after rebuilding
**   --wal             Set Write-Ahead-Log journalling mode on the database
*/
void rebuild_database(void){
  int forceFlag;
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
  int optIfNeeded;
  int compressOnlyFlag;

  omitVerify = find_option("noverify",0,0)!=0;
  forceFlag = find_option("force","f",0)!=0;
  doClustering = find_option("cluster", 0, 0)!=0;
  runVacuum = find_option("vacuum",0,0)!=0;
  runDeanalyze = find_option("deanalyze",0,0)!=0;
  runAnalyze = find_option("analyze",0,0)!=0;
  runCompress = find_option("compress",0,0)!=0;
  zPagesize = find_option("pagesize",0,1);
  showStats = find_option("stats",0,0)!=0;
  optIndex = find_option("index",0,0)!=0;
  optNoIndex = find_option("noindex",0,0)!=0;
  optIfNeeded = find_option("ifneeded",0,0)!=0;







|







688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
  int optIfNeeded;
  int compressOnlyFlag;

  omitVerify = find_option("noverify",0,0)!=0;
  forceFlag = find_option("force","f",0)!=0;
  doClustering = find_option("cluster", 0, 0)!=0;
  runVacuum = find_option("vacuum",0,0)!=0;
  runDeanalyze = find_option("deanalyze",0,0)!=0; /* Deprecated */
  runAnalyze = find_option("analyze",0,0)!=0;
  runCompress = find_option("compress",0,0)!=0;
  zPagesize = find_option("pagesize",0,1);
  showStats = find_option("stats",0,0)!=0;
  optIndex = find_option("index",0,0)!=0;
  optNoIndex = find_option("noindex",0,0)!=0;
  optIfNeeded = find_option("ifneeded",0,0)!=0;
1394
1395
1396
1397
1398
1399
1400
1401

1402
1403
1404
1405
1406
1407
1408
  */
  verify_cancel();

  db_end_transaction(0);
  fossil_print("project-id: %s\n", db_get("project-code", 0));
  fossil_print("server-id: %s\n", db_get("server-code", 0));
  zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
  fossil_print("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword);

  hash_user_password(g.zLogin);
}

/*
** COMMAND: deconstruct*
**
** Usage %fossil deconstruct ?OPTIONS? DESTINATION







|
>







1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
  */
  verify_cancel();

  db_end_transaction(0);
  fossil_print("project-id: %s\n", db_get("project-code", 0));
  fossil_print("server-id: %s\n", db_get("server-code", 0));
  zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
  fossil_print("admin-user: %s (initial password is \"%s\")\n", g.zLogin,
               zPassword);
  hash_user_password(g.zLogin);
}

/*
** COMMAND: deconstruct*
**
** Usage %fossil deconstruct ?OPTIONS? DESTINATION
Changes to src/report.c.
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
  char *zClrKey;
  char *zDesc;
  char *zMimetype;
  int tabs;
  Stmt q;
  char *zErr1 = 0;
  char *zErr2 = 0;
  
  login_check_credentials();
  if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; }
  report_update_reportfmt_table();
  rn = report_number();
  tabs = P("tablist")!=0;
  db_prepare(&q,
    "SELECT title, sqlcode, owner, cols, rn, jx->>'desc', jx->>'descmt'"







|







1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
  char *zClrKey;
  char *zDesc;
  char *zMimetype;
  int tabs;
  Stmt q;
  char *zErr1 = 0;
  char *zErr2 = 0;

  login_check_credentials();
  if( !g.perm.RdTkt ){ login_needed(g.anon.RdTkt); return; }
  report_update_reportfmt_table();
  rn = report_number();
  tabs = P("tablist")!=0;
  db_prepare(&q,
    "SELECT title, sqlcode, owner, cols, rn, jx->>'desc', jx->>'descmt'"
1366
1367
1368
1369
1370
1371
1372
1373

1374
1375
1376
1377
1378
1379
1380
  Stmt q;
  char *zSql;
  char *zErr1 = 0;
  char *zErr2 = 0;
  int count = 0;
  int rn;

  if( !zRep || !strcmp(zRep,zFullTicketRptRn) || !strcmp(zRep,zFullTicketRptTitle) ){

    zSql = "SELECT * FROM ticket";
  }else{
    rn = atoi(zRep);
    if( rn ){
      db_prepare(&q,
       "SELECT sqlcode FROM reportfmt WHERE rn=%d", rn);
    }else{







|
>







1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
  Stmt q;
  char *zSql;
  char *zErr1 = 0;
  char *zErr2 = 0;
  int count = 0;
  int rn;

  if( !zRep || !strcmp(zRep,zFullTicketRptRn)
      || !strcmp(zRep,zFullTicketRptTitle) ){
    zSql = "SELECT * FROM ticket";
  }else{
    rn = atoi(zRep);
    if( rn ){
      db_prepare(&q,
       "SELECT sqlcode FROM reportfmt WHERE rn=%d", rn);
    }else{
Changes to src/rss.c.
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156

  blob_append_sql( &bSQL, " ORDER BY event.mtime DESC" );

  cgi_set_content_type("application/rss+xml");

  zProjectName = db_get("project-name", 0);
  if( zProjectName==0 ){
    zFreeProjectName = zProjectName = mprintf("Fossil source repository for: %s",
      g.zBaseURL);
  }
  zProjectDescr = db_get("project-description", 0);
  if( zProjectDescr==0 ){
    zProjectDescr = zProjectName;
  }

  zPubDate = cgi_rfc822_datestamp(time(NULL));







|
|







141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156

  blob_append_sql( &bSQL, " ORDER BY event.mtime DESC" );

  cgi_set_content_type("application/rss+xml");

  zProjectName = db_get("project-name", 0);
  if( zProjectName==0 ){
    zFreeProjectName = zProjectName =
      mprintf("Fossil source repository for: %s", g.zBaseURL);
  }
  zProjectDescr = db_get("project-description", 0);
  if( zProjectDescr==0 ){
    zProjectDescr = zProjectName;
  }

  zPubDate = cgi_rfc822_datestamp(time(NULL));
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
**                   The default is "URL-PLACEHOLDER" (without quotes).
*/
void cmd_timeline_rss(void){
  Stmt q;
  int nLine=0;
  char *zPubDate, *zProjectName, *zProjectDescr, *zFreeProjectName=0;
  Blob bSQL;
  const char *zType = find_option("type","y",1); /* Type of events.  All if NULL */
  const char *zTicketUuid = find_option("tkt",NULL,1);
  const char *zTag = find_option("tag",NULL,1);
  const char *zFilename = find_option("name",NULL,1);
  const char *zWiki = find_option("wiki",NULL,1);
  const char *zLimit = find_option("limit", "n",1);
  const char *zBaseURL = find_option("url", NULL, 1);
  int nLimit = atoi( (zLimit && *zLimit) ? zLimit : "20" );







|







256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
**                   The default is "URL-PLACEHOLDER" (without quotes).
*/
void cmd_timeline_rss(void){
  Stmt q;
  int nLine=0;
  char *zPubDate, *zProjectName, *zProjectDescr, *zFreeProjectName=0;
  Blob bSQL;
  const char *zType = find_option("type","y",1); /* Type of events;All if NULL*/
  const char *zTicketUuid = find_option("tkt",NULL,1);
  const char *zTag = find_option("tag",NULL,1);
  const char *zFilename = find_option("name",NULL,1);
  const char *zWiki = find_option("wiki",NULL,1);
  const char *zLimit = find_option("limit", "n",1);
  const char *zBaseURL = find_option("url", NULL, 1);
  int nLimit = atoi( (zLimit && *zLimit) ? zLimit : "20" );
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
  }else if( nTagId!=0 ){
    blob_append_sql(&bSQL, " AND (EXISTS(SELECT 1 FROM tagxref"
      " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid))", nTagId);
  }

  if( zFilename ){
    blob_append_sql(&bSQL,
      " AND (SELECT mlink.fnid FROM mlink WHERE event.objid=mlink.mid) IN (SELECT fnid FROM filename WHERE name=%Q %s)",

        zFilename, filename_collation()
    );
  }

  blob_append( &bSQL, " ORDER BY event.mtime DESC", -1 );

  zProjectName = db_get("project-name", 0);
  if( zProjectName==0 ){
    zFreeProjectName = zProjectName = mprintf("Fossil source repository for: %s",
      zBaseURL);
  }
  zProjectDescr = db_get("project-description", 0);
  if( zProjectDescr==0 ){
    zProjectDescr = zProjectName;
  }

  zPubDate = cgi_rfc822_datestamp(time(NULL));

  fossil_print("<?xml version=\"1.0\"?>");
  fossil_print("<rss xmlns:dc=\"http://purl.org/dc/elements/1.1/\" version=\"2.0\">");

  fossil_print("<channel>\n");
  fossil_print("<title>%h</title>\n", zProjectName);
  fossil_print("<link>%s</link>\n", zBaseURL);
  fossil_print("<description>%h</description>\n", zProjectDescr);
  fossil_print("<pubDate>%s</pubDate>\n", zPubDate);
  fossil_print("<generator>Fossil version %s %s</generator>\n",
               MANIFEST_VERSION, MANIFEST_DATE);







|
>








|
|









|
>







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
  }else if( nTagId!=0 ){
    blob_append_sql(&bSQL, " AND (EXISTS(SELECT 1 FROM tagxref"
      " WHERE tagid=%d AND tagtype>0 AND rid=blob.rid))", nTagId);
  }

  if( zFilename ){
    blob_append_sql(&bSQL,
      " AND (SELECT mlink.fnid FROM mlink WHERE event.objid=mlink.mid) "
      " IN (SELECT fnid FROM filename WHERE name=%Q %s)",
        zFilename, filename_collation()
    );
  }

  blob_append( &bSQL, " ORDER BY event.mtime DESC", -1 );

  zProjectName = db_get("project-name", 0);
  if( zProjectName==0 ){
    zFreeProjectName = zProjectName =
      mprintf("Fossil source repository for: %s", zBaseURL);
  }
  zProjectDescr = db_get("project-description", 0);
  if( zProjectDescr==0 ){
    zProjectDescr = zProjectName;
  }

  zPubDate = cgi_rfc822_datestamp(time(NULL));

  fossil_print("<?xml version=\"1.0\"?>");
  fossil_print("<rss xmlns:dc=\"http://purl.org/dc/elements/1.1/\" "
               "  version=\"2.0\">");
  fossil_print("<channel>\n");
  fossil_print("<title>%h</title>\n", zProjectName);
  fossil_print("<link>%s</link>\n", zBaseURL);
  fossil_print("<description>%h</description>\n", zProjectDescr);
  fossil_print("<pubDate>%s</pubDate>\n", zPubDate);
  fossil_print("<generator>Fossil version %s %s</generator>\n",
               MANIFEST_VERSION, MANIFEST_DATE);
Changes to src/search.c.
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
** option can be used to output all matches, regardless of their search
** score.  The -limit option can be used to limit the number of entries
** returned.  The -width option can be used to set the output width used
** when printing matches.
**
** Options:
**     -a|--all          Output all matches, not just best matches


**     -n|--limit N      Limit output to N matches


**     -W|--width WIDTH  Set display width to WIDTH columns, 0 for
**                       unlimited. Defaults the terminal's width.
*/
void search_cmd(void){
  Blob pattern;
  int i;
  Blob sql = empty_blob;
  Stmt q;
  int iBest;
  char fAll = NULL != find_option("all", "a", 0); /* If set, do not lop
                                                     off the end of the
                                                     results. */
  const char *zLimit = find_option("limit","n",1);
  const char *zWidth = find_option("width","W",1);


  int nLimit = zLimit ? atoi(zLimit) : -1000;   /* Max number of matching
                                                   lines/entries to list */
  int width;


  if( zWidth ){
    width = atoi(zWidth);
    if( (width!=0) && (width<=20) ){
      fossil_fatal("-W|--width value must be >20 or 0");
    }
  }else{
    width = -1;
  }

  db_find_and_open_repository(0, 0);
  if( g.argc<3 ) return;
  blob_init(&pattern, g.argv[2], -1);
  for(i=3; i<g.argc; i++){
    blob_appendf(&pattern, " %s", g.argv[i]);
  }








































































  (void)search_init(blob_str(&pattern),"*","*","...",SRCHFLG_STATIC);
  blob_reset(&pattern);
  search_sql_setup(g.db);

  db_multi_exec(
     "CREATE TEMP TABLE srch(rid,uuid,date,comment,x);"
     "CREATE INDEX srch_idx1 ON srch(x);"
     "INSERT INTO srch(rid,uuid,date,comment,x)"
     "   SELECT blob.rid, uuid, datetime(event.mtime,toLocal()),"
     "          coalesce(ecomment,comment),"
     "          search_score()"
     "     FROM event, blob"
     "    WHERE blob.rid=event.objid"
     "      AND search_match(coalesce(ecomment,comment));"
  );
  iBest = db_int(0, "SELECT max(x) FROM srch");
  blob_append(&sql,
              "SELECT rid, uuid, date, comment, 0, 0 FROM srch "
              "WHERE 1 ", -1);
  if(!fAll){
    blob_append_sql(&sql,"AND x>%d ", iBest/3);
  }
  blob_append(&sql, "ORDER BY x DESC, date DESC ", -1);
  db_prepare(&q, "%s", blob_sql_text(&sql));
  blob_reset(&sql);
  print_timeline(&q, nLimit, width, 0, 0);
  db_finalize(&q);

}

#if INTERFACE
/* What to search for */
#define SRCH_CKIN     0x0001    /* Search over check-in comments */
#define SRCH_DOC      0x0002    /* Search over embedded documents */
#define SRCH_TKT      0x0004    /* Search over tickets */







>
>

>
>









|
<
<


>
>
|
<

>
>















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

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







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
** option can be used to output all matches, regardless of their search
** score.  The -limit option can be used to limit the number of entries
** returned.  The -width option can be used to set the output width used
** when printing matches.
**
** Options:
**     -a|--all          Output all matches, not just best matches
**     --debug           Show additional debug content on --fts search
**     --fts             Use the full-text search mechanism (testing only)
**     -n|--limit N      Limit output to N matches
**     --scope SCOPE     Scope of search.  Valid for --fts only.  One or
**                       more of: all, c, d, e, f, t, w.  Defaults to all.
**     -W|--width WIDTH  Set display width to WIDTH columns, 0 for
**                       unlimited. Defaults the terminal's width.
*/
void search_cmd(void){
  Blob pattern;
  int i;
  Blob sql = empty_blob;
  Stmt q;
  int iBest;
  char fAll = NULL != find_option("all", "a", 0);


  const char *zLimit = find_option("limit","n",1);
  const char *zWidth = find_option("width","W",1);
  const char *zScope = find_option("scope",0,1);
  int bDebug = find_option("debug",0,0)!=0;
  int nLimit = zLimit ? atoi(zLimit) : -1000;

  int width;
  int bFts = find_option("fts",0,0)!=0;

  if( zWidth ){
    width = atoi(zWidth);
    if( (width!=0) && (width<=20) ){
      fossil_fatal("-W|--width value must be >20 or 0");
    }
  }else{
    width = -1;
  }

  db_find_and_open_repository(0, 0);
  if( g.argc<3 ) return;
  blob_init(&pattern, g.argv[2], -1);
  for(i=3; i<g.argc; i++){
    blob_appendf(&pattern, " %s", g.argv[i]);
  }
  if( bFts ){
    /* Search using FTS */
    Blob com;
    Blob snip;
    const char *zPattern = blob_str(&pattern);
    int srchFlags;
    unsigned int j;
    if( zScope==0 ){
      srchFlags = SRCH_ALL;
    }else{
      srchFlags = 0;
      for(i=0; zScope[i]; i++){
        switch( zScope[i] ){
          case 'a':  srchFlags = SRCH_ALL;  break;
          case 'c':  srchFlags |= SRCH_CKIN;     break;
          case 'd':  srchFlags |= SRCH_DOC;      break;
          case 'e':  srchFlags |= SRCH_TECHNOTE; break;
          case 'f':  srchFlags |= SRCH_FORUM;    break;
          case 't':  srchFlags |= SRCH_TKT;      break;
          case 'w':  srchFlags |= SRCH_WIKI;     break;
        }
      }
    }
    search_sql_setup(g.db);
    add_content_sql_commands(g.db);
    db_multi_exec(
      "CREATE TEMP TABLE x(label,url,score,id,date,snip);"
    );
    if( !search_index_exists() ){
      search_fullscan(zPattern, srchFlags);  /* Full-scan search */
    }else{
      search_update_index(srchFlags);        /* Update the index */
      search_indexed(zPattern, srchFlags);   /* Indexed search */
    }
    db_prepare(&q, "SELECT snip, label, score, id, date"
                   "  FROM x"
                   " ORDER BY score DESC, date DESC;");
    blob_init(&com, 0, 0);
    blob_init(&snip, 0, 0);
    if( width<0 ) width = 80;
    while( db_step(&q)==SQLITE_ROW ){
      const char *zSnippet = db_column_text(&q, 0);
      const char *zLabel = db_column_text(&q, 1);
      const char *zDate = db_column_text(&q, 4);
      const char *zScore = db_column_text(&q, 2);
      const char *zId = db_column_text(&q, 3);
      blob_appendf(&snip, "%s", zSnippet);
      for(j=0; j<snip.nUsed; j++){
        if( snip.aData[j]=='\n' ){
          if( j>0 && snip.aData[j-1]=='\r' ) snip.aData[j-1] = ' ';
          snip.aData[j] = ' ';
        }
      }
      blob_appendf(&com, "%s\n%s\n%s", zLabel, blob_str(&snip), zDate);
      if( bDebug ){
        blob_appendf(&com," score: %s id: %s", zScore, zId);
      }
      comment_print(blob_str(&com), 0, 5, width,
            COMMENT_PRINT_TRIM_CRLF |
            COMMENT_PRINT_WORD_BREAK |
            COMMENT_PRINT_TRIM_SPACE);
      blob_reset(&com);
      blob_reset(&snip);
      if( nLimit>=1 ){
        nLimit--;
        if( nLimit==0 ) break;
      }
    }
    db_finalize(&q);
    blob_reset(&pattern);
  }else{
    /* Legacy timeline search (the default) */
    (void)search_init(blob_str(&pattern),"*","*","...",SRCHFLG_STATIC);
    blob_reset(&pattern);
    search_sql_setup(g.db);

    db_multi_exec(
       "CREATE TEMP TABLE srch(rid,uuid,date,comment,x);"
       "CREATE INDEX srch_idx1 ON srch(x);"
       "INSERT INTO srch(rid,uuid,date,comment,x)"
       "   SELECT blob.rid, uuid, datetime(event.mtime,toLocal()),"
       "          coalesce(ecomment,comment),"
       "          search_score()"
       "     FROM event, blob"
       "    WHERE blob.rid=event.objid"
       "      AND search_match(coalesce(ecomment,comment));"
    );
    iBest = db_int(0, "SELECT max(x) FROM srch");
    blob_append(&sql,
                "SELECT rid, uuid, date, comment, 0, 0 FROM srch "
                "WHERE 1 ", -1);
    if(!fAll){
      blob_append_sql(&sql,"AND x>%d ", iBest/3);
    }
    blob_append(&sql, "ORDER BY x DESC, date DESC ", -1);
    db_prepare(&q, "%s", blob_sql_text(&sql));
    blob_reset(&sql);
    print_timeline(&q, nLimit, width, 0, 0);
    db_finalize(&q);
  }
}

#if INTERFACE
/* What to search for */
#define SRCH_CKIN     0x0001    /* Search over check-in comments */
#define SRCH_DOC      0x0002    /* Search over embedded documents */
#define SRCH_TKT      0x0004    /* Search over tickets */
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
** snip:   A snippet for the match
**
** And the srchFlags parameter has been validated.  This routine
** fills the X table with search results using a full-scan search.
**
** The companion indexed search routine is search_indexed().
*/
static void search_fullscan(
  const char *zPattern,       /* The query pattern */
  unsigned int srchFlags      /* What to search over */
){
  search_init(zPattern, "<mark>", "</mark>", " ... ",
          SRCHFLG_STATIC|SRCHFLG_HTML);
  if( (srchFlags & SRCH_DOC)!=0 ){
    char *zDocGlob = db_get("doc-glob","");







|







782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
** snip:   A snippet for the match
**
** And the srchFlags parameter has been validated.  This routine
** fills the X table with search results using a full-scan search.
**
** The companion indexed search routine is search_indexed().
*/
LOCAL void search_fullscan(
  const char *zPattern,       /* The query pattern */
  unsigned int srchFlags      /* What to search over */
){
  search_init(zPattern, "<mark>", "</mark>", " ... ",
          SRCHFLG_STATIC|SRCHFLG_HTML);
  if( (srchFlags & SRCH_DOC)!=0 ){
    char *zDocGlob = db_get("doc-glob","");
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
** snip:   A snippet for the match
**
** And the srchFlags parameter has been validated.  This routine
** fills the X table with search results using FTS indexed search.
**
** The companion full-scan search routine is search_fullscan().
*/
static void search_indexed(
  const char *zPattern,       /* The query pattern */
  unsigned int srchFlags      /* What to search over */
){
  Blob sql;
  char *zPat = mprintf("%s",zPattern);
  int i;
  static const char *zSnippetCall;







|







990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
** snip:   A snippet for the match
**
** And the srchFlags parameter has been validated.  This routine
** fills the X table with search results using FTS indexed search.
**
** The companion full-scan search routine is search_fullscan().
*/
LOCAL void search_indexed(
  const char *zPattern,       /* The query pattern */
  unsigned int srchFlags      /* What to search over */
){
  Blob sql;
  char *zPat = mprintf("%s",zPattern);
  int i;
  static const char *zSnippetCall;
1255
1256
1257
1258
1259
1260
1261
1262

1263
1264



1265


1266
1267
1268
1269
1270
1271
1272
  if( zMimetype==0 ) zMimetype = "text/plain";
  if( fossil_strcmp(zMimetype,"text/x-fossil-wiki")==0 ){
    if( blob_size(&title) ){
      wiki_convert(pIn, &html, 0);
    }else{
      Blob tail;
      blob_init(&tail, 0, 0);
      wiki_find_title(pIn, &title, &tail);

      wiki_convert(&tail, &html, 0);
      blob_reset(&tail);



    }


  }else if( fossil_strcmp(zMimetype,"text/x-markdown")==0 ){
    markdown_to_html(pIn, blob_size(&title) ? NULL : &title, &html);
  }else if( fossil_strcmp(zMimetype,"text/html")==0 ){
    if( blob_size(&title)==0 ) doc_is_embedded_html(pIn, &title);
    pHtml = pIn;
  }
  blob_appendf(pOut, "%s\n", blob_str(&title));







|
>
|
|
>
>
>
|
>
>







1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
  if( zMimetype==0 ) zMimetype = "text/plain";
  if( fossil_strcmp(zMimetype,"text/x-fossil-wiki")==0 ){
    if( blob_size(&title) ){
      wiki_convert(pIn, &html, 0);
    }else{
      Blob tail;
      blob_init(&tail, 0, 0);
      if( wiki_find_title(pIn, &title, &tail) ){
        blob_appendf(pOut, "%s\n", blob_str(&title));
        wiki_convert(&tail, &html, 0);
        blob_reset(&tail);
      }else{
        blob_append(pOut, "\n", 1);
        wiki_convert(pIn, &html, 0);
      }
    }
    html_to_plaintext(blob_str(&html), pOut);
  }else if( fossil_strcmp(zMimetype,"text/x-markdown")==0 ){
    markdown_to_html(pIn, blob_size(&title) ? NULL : &title, &html);
  }else if( fossil_strcmp(zMimetype,"text/html")==0 ){
    if( blob_size(&title)==0 ) doc_is_embedded_html(pIn, &title);
    pHtml = pIn;
  }
  blob_appendf(pOut, "%s\n", blob_str(&title));
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308

  return rc;
}


/*
** Argument f should be a flag accepted by matchinfo() (a valid character
** in the string passed as the second argument). If it is not, -1 is 
** returned. Otherwise, if f is a valid matchinfo flag, the value returned
** is the number of 32-bit integers added to the output array if the
** table has nCol columns and the query nPhrase phrases.
*/
static int fts5MatchinfoFlagsize(int nCol, int nPhrase, char f){
  int ret = -1;
  switch( f ){







|







2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392

  return rc;
}


/*
** Argument f should be a flag accepted by matchinfo() (a valid character
** in the string passed as the second argument). If it is not, -1 is
** returned. Otherwise, if f is a valid matchinfo flag, the value returned
** is the number of 32-bit integers added to the output array if the
** table has nCol columns and the query nPhrase phrases.
*/
static int fts5MatchinfoFlagsize(int nCol, int nPhrase, char f){
  int ret = -1;
  switch( f ){
Changes to src/security_audit.c.
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
  }

  /* Anonymous users probably should not be allowed act as moderators
  ** for wiki or tickets.
  */
  if( hasAnyCap(zAnonCap, "lq5") ){
    @ <li><p><b>WARNING:</b>
    @ Anonymous users can act as moderators for wiki, tickets, or 
    @ forum posts. This defeats the whole purpose of moderation.
    @ Fix this by removing the "Mod-Wiki", "Mod-Tkt", and "Mod-Forum"
    @ privileges (<a href="%R/setup_ucap_list">capabilities</a> "fq5")
    @ from users "anonymous" and "nobody"
    @ on the <a href="setup_ulist">User Configuration</a> page.
  }

  /* Check to see if any TH1 scripts are configured to run on a sync
  */
  if( db_exists("SELECT 1 FROM config WHERE name GLOB 'xfer-*-script'"
                " AND length(value)>0") ){
    @ <li><p><b>WARNING:</b>
    @ TH1 scripts might be configured to run on any sync, push, pull, or
    @ clone operation.  See the the <a href="%R/xfersetup">/xfersetup</a>
    @ page for more information.  These TH1 scripts are a potential
    @ security concern and so should be carefully audited by a human.
  } 

  /* The strict-manifest-syntax setting should be on. */
  if( db_get_boolean("strict-manifest-syntax",1)==0 ){
    @ <li><p><b>WARNING:</b>
    @ The "strict-manifest-syntax"  flag is off.  This is a security
    @ risk.  Turn this setting on (its default) to protect the users
    @ of this repository.







|
















|







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
  }

  /* Anonymous users probably should not be allowed act as moderators
  ** for wiki or tickets.
  */
  if( hasAnyCap(zAnonCap, "lq5") ){
    @ <li><p><b>WARNING:</b>
    @ Anonymous users can act as moderators for wiki, tickets, or
    @ forum posts. This defeats the whole purpose of moderation.
    @ Fix this by removing the "Mod-Wiki", "Mod-Tkt", and "Mod-Forum"
    @ privileges (<a href="%R/setup_ucap_list">capabilities</a> "fq5")
    @ from users "anonymous" and "nobody"
    @ on the <a href="setup_ulist">User Configuration</a> page.
  }

  /* Check to see if any TH1 scripts are configured to run on a sync
  */
  if( db_exists("SELECT 1 FROM config WHERE name GLOB 'xfer-*-script'"
                " AND length(value)>0") ){
    @ <li><p><b>WARNING:</b>
    @ TH1 scripts might be configured to run on any sync, push, pull, or
    @ clone operation.  See the the <a href="%R/xfersetup">/xfersetup</a>
    @ page for more information.  These TH1 scripts are a potential
    @ security concern and so should be carefully audited by a human.
  }

  /* The strict-manifest-syntax setting should be on. */
  if( db_get_boolean("strict-manifest-syntax",1)==0 ){
    @ <li><p><b>WARNING:</b>
    @ The "strict-manifest-syntax"  flag is off.  This is a security
    @ risk.  Turn this setting on (its default) to protect the users
    @ of this repository.
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
  }else {
    double r = atof(db_get("max-loadavg", 0));
    if( r<=0.0 ){
      @ <li><p>
      @ Load average limiting is turned off.  This can cause the server
      @ to bog down if many requests for expensive services (such as
      @ large diffs or tarballs) arrive at about the same time.
      @ To fix this, set the 
      @ <a href='%R/setup_access#slal'>"Server Load Average Limit"</a> on the
      @ <a href='%R/setup_access'>Access Control</a> page to the approximate
      @ the number of available cores on your server, or maybe just a little
      @ less.
    }else if( r>=8.0 ){
      @ <li><p>
      @ The <a href='%R/setup_access#slal'>"Server Load Average Limit"</a> on







|







580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
  }else {
    double r = atof(db_get("max-loadavg", 0));
    if( r<=0.0 ){
      @ <li><p>
      @ Load average limiting is turned off.  This can cause the server
      @ to bog down if many requests for expensive services (such as
      @ large diffs or tarballs) arrive at about the same time.
      @ To fix this, set the
      @ <a href='%R/setup_access#slal'>"Server Load Average Limit"</a> on the
      @ <a href='%R/setup_access'>Access Control</a> page to the approximate
      @ the number of available cores on your server, or maybe just a little
      @ less.
    }else if( r>=8.0 ){
      @ <li><p>
      @ The <a href='%R/setup_access#slal'>"Server Load Average Limit"</a> on
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
    @ <li><p>
    @ The server error log is disabled.
    @ To set up an error log,
    if( fossil_strcmp(g.zCmdName, "cgi")==0 ){
      @ make an entry like "errorlog: <i>FILENAME</i>" in the
      @ CGI script at %h(P("SCRIPT_FILENAME")).
    }else{
      @ add the "--errorlog <i>FILENAME</i>" option to the 
      @ "%h(g.argv[0]) %h(g.zCmdName)" command that launched this server.
    }
  }else{
    FILE *pTest = fossil_fopen(g.zErrlog,"a");
    if( pTest==0 ){
      @ <li><p>
      @ <b>Error:</b>







|







602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
    @ <li><p>
    @ The server error log is disabled.
    @ To set up an error log,
    if( fossil_strcmp(g.zCmdName, "cgi")==0 ){
      @ make an entry like "errorlog: <i>FILENAME</i>" in the
      @ CGI script at %h(P("SCRIPT_FILENAME")).
    }else{
      @ add the "--errorlog <i>FILENAME</i>" option to the
      @ "%h(g.argv[0]) %h(g.zCmdName)" command that launched this server.
    }
  }else{
    FILE *pTest = fossil_fopen(g.zErrlog,"a");
    if( pTest==0 ){
      @ <li><p>
      @ <b>Error:</b>
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
    @ <li><p> CGI Extensions are enabled with a document root
    @ at <a href='%R/extfilelist'>%h(g.zExtRoot)</a> holding
    @ %d(nCgi) CGIs and %d(nFile-nCgi) static content and data files.
  }

  if( fileedit_glob()!=0 ){
    @ <li><p><a href='%R/fileedit'>Online File Editing</a> is enabled
    @ for this repository.  Clear the 
    @ <a href='%R/setup_settings'>"fileedit-glob" setting</a> to
    @ disable online editing.</p>
  }

  @ <li><p> User capability summary:
  capability_summary();


  azCSP = parse_content_security_policy();
  if( azCSP==0 ){
    @ <li><p> WARNING: No Content Security Policy (CSP) is specified in the
    @ header. Though not required, a strong CSP is recommended. Fossil will
    @ automatically insert an appropriate CSP if you let it generate the
    @ HTML <tt>&lt;head&gt;</tt> element by omitting <tt>&lt;body&gt;</tt>
    @ from the header configuration in your customized skin.
    @ 
  }else{
    int ii;
    @ <li><p> Content Security Policy:
    @ <ol type="a">
    for(ii=0; azCSP[ii]; ii++){
      @ <li>%h(azCSP[ii])
    }







|















|







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
    @ <li><p> CGI Extensions are enabled with a document root
    @ at <a href='%R/extfilelist'>%h(g.zExtRoot)</a> holding
    @ %d(nCgi) CGIs and %d(nFile-nCgi) static content and data files.
  }

  if( fileedit_glob()!=0 ){
    @ <li><p><a href='%R/fileedit'>Online File Editing</a> is enabled
    @ for this repository.  Clear the
    @ <a href='%R/setup_settings'>"fileedit-glob" setting</a> to
    @ disable online editing.</p>
  }

  @ <li><p> User capability summary:
  capability_summary();


  azCSP = parse_content_security_policy();
  if( azCSP==0 ){
    @ <li><p> WARNING: No Content Security Policy (CSP) is specified in the
    @ header. Though not required, a strong CSP is recommended. Fossil will
    @ automatically insert an appropriate CSP if you let it generate the
    @ HTML <tt>&lt;head&gt;</tt> element by omitting <tt>&lt;body&gt;</tt>
    @ from the header configuration in your customized skin.
    @
  }else{
    int ii;
    @ <li><p> Content Security Policy:
    @ <ol type="a">
    for(ii=0; azCSP[ii]; ii++){
      @ <li>%h(azCSP[ii])
    }
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
    @ <li><p>
    @ If the server is running as CGI, then create a line in the CGI file
    @ like this:
    @ <blockquote><pre>
    @ errorlog: <i>FILENAME</i>
    @ </pre></blockquote>
    @ <li><p>
    @ If the server is running using one of 
    @ the "fossil http" or "fossil server" commands then add
    @ a command-line option "--errorlog <i>FILENAME</i>" to that
    @ command.
    @ </ol>
    style_finish_page();
    return;
  }







|







785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
    @ <li><p>
    @ If the server is running as CGI, then create a line in the CGI file
    @ like this:
    @ <blockquote><pre>
    @ errorlog: <i>FILENAME</i>
    @ </pre></blockquote>
    @ <li><p>
    @ If the server is running using one of
    @ the "fossil http" or "fossil server" commands then add
    @ a command-line option "--errorlog <i>FILENAME</i>" to that
    @ command.
    @ </ol>
    style_finish_page();
    return;
  }
Changes to src/setup.c.
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
  @ for users who are not logged in. (Property: "require-captcha")</p>

  @ <hr>
  entry_attribute("Public pages", 30, "public-pages",
                  "pubpage", "", 0);
  @ <p>A comma-separated list of glob patterns for pages that are accessible
  @ without needing a login and using the privileges given by the
  @ "Default privileges" setting below. 
  @
  @ <p>Example use case: Set this field to "/doc/trunk/www/*" and set
  @ the "Default privileges" to include the "o" privilege
  @ to give anonymous users read-only permission to the
  @ latest version of the embedded documentation in the www/ folder without
  @ allowing them to see the rest of the source code.
  @ (Property: "public-pages")







|







586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
  @ for users who are not logged in. (Property: "require-captcha")</p>

  @ <hr>
  entry_attribute("Public pages", 30, "public-pages",
                  "pubpage", "", 0);
  @ <p>A comma-separated list of glob patterns for pages that are accessible
  @ without needing a login and using the privileges given by the
  @ "Default privileges" setting below.
  @
  @ <p>Example use case: Set this field to "/doc/trunk/www/*" and set
  @ the "Default privileges" to include the "o" privilege
  @ to give anonymous users read-only permission to the
  @ latest version of the embedded documentation in the www/ folder without
  @ allowing them to see the rest of the source code.
  @ (Property: "public-pages")
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
  @ choices (such as the hamburger button) to the menu that are not shown
  @ on this list. (Property: mainmenu)
  @ <p>
  if(P("resetMenu")!=0){
    db_unset("mainmenu", 0);
    cgi_delete_parameter("mmenu");
  }
  textarea_attribute("Main Menu", 12, 80, 
      "mainmenu", "mmenu", style_default_mainmenu(), 0);
  @ </p>
  @ <p><input type='checkbox' id='cbResetMenu' name='resetMenu' value='1'>
  @ <label for='cbResetMenu'>Reset menu to default value</label>
  @ </p>
  @ <hr>
  @ <p>Extra links to appear on the <a href="%R/sitemap">/sitemap</a> page,







|







1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
  @ choices (such as the hamburger button) to the menu that are not shown
  @ on this list. (Property: mainmenu)
  @ <p>
  if(P("resetMenu")!=0){
    db_unset("mainmenu", 0);
    cgi_delete_parameter("mmenu");
  }
  textarea_attribute("Main Menu", 12, 80,
      "mainmenu", "mmenu", style_default_mainmenu(), 0);
  @ </p>
  @ <p><input type='checkbox' id='cbResetMenu' name='resetMenu' value='1'>
  @ <label for='cbResetMenu'>Reset menu to default value</label>
  @ </p>
  @ <hr>
  @ <p>Extra links to appear on the <a href="%R/sitemap">/sitemap</a> page,
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
  @      If capexpr evaluates to true, then the entry is shown.  If not,
  @      the entry is omitted.  "*" is always true.
  @ </ol>
  @
  @ <p>The default value is blank, meaning no added entries.
  @ (Property: sitemap-extra)
  @ <p>
  textarea_attribute("Custom Sitemap Entries", 8, 80, 
      "sitemap-extra", "smextra", "", 0);
  @ <hr>
  @ <p><input type="submit"  name="submit" value="Apply Changes"></p>
  @ </div></form>
  db_end_transaction(0);
  style_finish_page();
}







|







1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
  @      If capexpr evaluates to true, then the entry is shown.  If not,
  @      the entry is omitted.  "*" is always true.
  @ </ol>
  @
  @ <p>The default value is blank, meaning no added entries.
  @ (Property: sitemap-extra)
  @ <p>
  textarea_attribute("Custom Sitemap Entries", 8, 80,
      "sitemap-extra", "smextra", "", 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.
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
  @ subscript suffix
  @ indicates the privileges of <span class="usertype">anonymous</span> that
  @ are inherited by all logged-in users.
  @ </p></li>
  @
  @ <li><p>
  @ The "<span class="ueditInheritDeveloper"><sub>D</sub></span>"
  @ subscript suffix indicates the privileges of 
  @ <span class="usertype">developer</span> that
  @ are inherited by all users with the
  @ <span class="capability">Developer</span> privilege.
  @ </p></li>
  @
  @ <li><p>
  @ The "<span class="ueditInheritReader"><sub>R</sub></span>" subscript suffix







|







808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
  @ subscript suffix
  @ indicates the privileges of <span class="usertype">anonymous</span> that
  @ are inherited by all logged-in users.
  @ </p></li>
  @
  @ <li><p>
  @ The "<span class="ueditInheritDeveloper"><sub>D</sub></span>"
  @ subscript suffix indicates the privileges of
  @ <span class="usertype">developer</span> that
  @ are inherited by all users with the
  @ <span class="capability">Developer</span> privilege.
  @ </p></li>
  @
  @ <li><p>
  @ The "<span class="ueditInheritReader"><sub>R</sub></span>" subscript suffix
Changes to src/sha1.c.
30
31
32
33
34
35
36
37

38
39
40
41
42
43
44
**
** Downloaded on 2017-03-01 then repackaged to work with Fossil
** and makeheaders.
*/
#if FOSSIL_HARDENED_SHA1

#if INTERFACE
typedef void(*collision_block_callback)(uint64_t, const uint32_t*, const uint32_t*, const uint32_t*, const uint32_t*);

struct SHA1_CTX {
  uint64_t total;
  uint32_t ihv[5];
  unsigned char buffer[64];
  int bigendian;
  int found_collision;
  int safe_hash;







|
>







30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
**
** Downloaded on 2017-03-01 then repackaged to work with Fossil
** and makeheaders.
*/
#if FOSSIL_HARDENED_SHA1

#if INTERFACE
typedef void(*collision_block_callback)(uint64_t, const uint32_t*,
                            const uint32_t*, const uint32_t*, const uint32_t*);
struct SHA1_CTX {
  uint64_t total;
  uint32_t ihv[5];
  unsigned char buffer[64];
  int bigendian;
  int found_collision;
  int safe_hash;
Changes to src/sha1hard.c.
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
void sha1_message_expansion(uint32_t W[80]);
void sha1_compression(uint32_t ihv[5], const uint32_t m[16]);
void sha1_compression_W(uint32_t ihv[5], const uint32_t W[80]);
void sha1_compression_states(uint32_t ihv[5], const uint32_t W[80], uint32_t states[80][5]);
extern sha1_recompression_type sha1_recompression_step[80];
typedef void(*collision_block_callback)(uint64_t, const uint32_t*, const uint32_t*, const uint32_t*, const uint32_t*);
typedef struct {
	uint64_t total;
	uint32_t ihv[5];
	unsigned char buffer[64];
	int bigendian;
	int found_collision;
	int safe_hash;
	int detect_coll;
	int ubc_check;
	int reduced_round_coll;
	collision_block_callback callback;

	uint32_t ihv1[5];
	uint32_t ihv2[5];
	uint32_t m1[80];
	uint32_t m2[80];
	uint32_t states[80][5];
} SHA1_CTX;

/******************** File: lib/ubc_check.c **************************/
/***
* Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow <danshu@microsoft.com>
* Distributed under the MIT Software License.
* See accompanying file LICENSE.txt or copy at







|
|
|
|
|
|
|
|
|
|

|
|
|
|
|







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
void sha1_message_expansion(uint32_t W[80]);
void sha1_compression(uint32_t ihv[5], const uint32_t m[16]);
void sha1_compression_W(uint32_t ihv[5], const uint32_t W[80]);
void sha1_compression_states(uint32_t ihv[5], const uint32_t W[80], uint32_t states[80][5]);
extern sha1_recompression_type sha1_recompression_step[80];
typedef void(*collision_block_callback)(uint64_t, const uint32_t*, const uint32_t*, const uint32_t*, const uint32_t*);
typedef struct {
  uint64_t total;
  uint32_t ihv[5];
  unsigned char buffer[64];
  int bigendian;
  int found_collision;
  int safe_hash;
  int detect_coll;
  int ubc_check;
  int reduced_round_coll;
  collision_block_callback callback;

  uint32_t ihv1[5];
  uint32_t ihv2[5];
  uint32_t m1[80];
  uint32_t m2[80];
  uint32_t states[80][5];
} SHA1_CTX;

/******************** File: lib/ubc_check.c **************************/
/***
* Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow <danshu@microsoft.com>
* Distributed under the MIT Software License.
* See accompanying file LICENSE.txt or copy at
Changes to src/sha3.c.
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
static void SHA3Update(
  SHA3Context *p,
  const unsigned char *aData,
  unsigned int nData
){
  unsigned int i = 0;
#if SHA3_BYTEORDER==1234
  if( (p->nLoaded % 8)==0 && ((aData - (const unsigned char*)0)&7)==0 ){
    for(; i+7<nData; i+=8){
      p->u.s[p->nLoaded/8] ^= *(u64*)&aData[i];
      p->nLoaded += 8;
      if( p->nLoaded>=p->nRate ){
        KeccakF1600Step(p);
        p->nLoaded = 0;
      }







|







414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
static void SHA3Update(
  SHA3Context *p,
  const unsigned char *aData,
  unsigned int nData
){
  unsigned int i = 0;
#if SHA3_BYTEORDER==1234
  if( (p->nLoaded % 8)==0 && (((intptr_t)aData)&7)==0 ){
    for(; i+7<nData; i+=8){
      p->u.s[p->nLoaded/8] ^= *(u64*)&aData[i];
      p->nLoaded += 8;
      if( p->nLoaded>=p->nRate ){
        KeccakF1600Step(p);
        p->nLoaded = 0;
      }
Changes to src/shun.c.
45
46
47
48
49
50
51

52
53
54
55
56
57
58
void shun_page(void){
  Stmt q;
  int cnt = 0;
  const char *zUuid = P("uuid");
  const char *zShun = P("shun");
  const char *zAccept = P("accept");
  const char *zRcvid = P("rcvid");

  int nRcvid = 0;
  int numRows = 3;
  char *zCanonical = 0;

  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);







>







45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
void shun_page(void){
  Stmt q;
  int cnt = 0;
  const char *zUuid = P("uuid");
  const char *zShun = P("shun");
  const char *zAccept = P("accept");
  const char *zRcvid = P("rcvid");
  int reviewList = P("review")!=0;
  int nRcvid = 0;
  int numRows = 3;
  char *zCanonical = 0;

  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
      }
      i++;
    }
    zCanonical[j+1] = zCanonical[j] = 0;
    p = zCanonical;
    while( *p ){
      int nUuid = strlen(p);
      if( !hname_validate(p, nUuid) ){
        @ <p class="generalError">Error: Bad artifact IDs.</p>
        fossil_free(zCanonical);
        zCanonical = 0;
        break;
      }else{
        canonical16(p, nUuid);
        p += nUuid+1;







|







84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
      }
      i++;
    }
    zCanonical[j+1] = zCanonical[j] = 0;
    p = zCanonical;
    while( *p ){
      int nUuid = strlen(p);
      if( !(reviewList || hname_validate(p, nUuid)) ){
        @ <p class="generalError">Error: Bad artifact IDs.</p>
        fossil_free(zCanonical);
        zCanonical = 0;
        break;
      }else{
        canonical16(p, nUuid);
        p += nUuid+1;
153
154
155
156
157
158
159





















































160
161
162
163
164
165
166
    for( p = zUuid ; *p ; p += strlen(p)+1 ){
      @ <a href="%R/artifact/%s(p)">%s(p)</a><br>
    }
    @ have been shunned.  They will no longer be pushed.
    @ They will be removed from the repository the next time the repository
    @ is rebuilt using the <b>fossil rebuild</b> command-line</p>
  }





















































  if( zRcvid ){
    nRcvid = atoi(zRcvid);
    numRows = db_int(0, "SELECT min(count(), 10) FROM blob WHERE rcvid=%d",
                     nRcvid);
  }
  @ <p>A shunned artifact will not be pushed nor accepted in a pull and the
  @ artifact content will be purged from the repository the next time the







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







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
    for( p = zUuid ; *p ; p += strlen(p)+1 ){
      @ <a href="%R/artifact/%s(p)">%s(p)</a><br>
    }
    @ have been shunned.  They will no longer be pushed.
    @ They will be removed from the repository the next time the repository
    @ is rebuilt using the <b>fossil rebuild</b> command-line</p>
  }
  if( zUuid && reviewList ){
    const char *p;
    int nTotal = 0;
    int nOk = 0;
    @ <table class="shun-review"><tbody><tr><td>
    for( p = zUuid ; *p ; p += strlen(p)+1 ){
      int rid = symbolic_name_to_rid(p, 0);
      nTotal++;
      if( rid < 0 ){
        @ Ambiguous<br>
      }else if( rid == 0 ){
        if( !hname_validate(p, strlen(p)) ){
          @ Bad artifact<br>
        }else if(db_int(0, "SELECT 1 FROM shun WHERE uuid=%Q", p)){
          @ Already shunned<br>
        }else{
          @ Unknown<br>
        }
      }else{
        char *zCmpUuid = db_text(0,
            "SELECT uuid"
            "  FROM blob, rcvfrom"
            " WHERE rid=%d"
            "   AND rcvfrom.rcvid=blob.rcvid",
            rid);
        if( fossil_strcmp(p, zCmpUuid)==0 ){
          nOk++;
          @ OK</br>
        }else{
          @ Abbreviated<br>
        }
      }
    }
    @ </td><td>
    for( p = zUuid ; *p ; p += strlen(p)+1 ){
      int rid = symbolic_name_to_rid(p, 0);
      if( rid > 0 ){
        @ <a href="%R/artifact/%s(p)">%s(p)</a><br>
      }else{
        @ %s(p)<br>
      }
    }
    @ </td></tr></tbody></table>
    @ <p class="shunned">
    if( nOk < nTotal){
      @ <b>Warning:</b> Not all artifacts
    }else if( nTotal==1 ){
      @ The artifact is present and
    }else{
      @ All %i(nOk) artifacts are present and
    }
    @ can be shunned with its hash above.</p>
  }
  if( zRcvid ){
    nRcvid = atoi(zRcvid);
    numRows = db_int(0, "SELECT min(count(), 10) FROM blob WHERE rcvid=%d",
                     nRcvid);
  }
  @ <p>A shunned artifact will not be pushed nor accepted in a pull and the
  @ artifact content will be purged from the repository the next time the
194
195
196
197
198
199
200





201
202
203

204
205
206
207
208
209
210
    }else if( nRcvid ){
      db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid);
      while( db_step(&q)==SQLITE_ROW ){
        @ %s(db_column_text(&q, 0))
      }
      db_finalize(&q);
    }





  }
  @ </textarea>
  @ <input type="submit" name="add" value="Shun">

  @ </div></form>
  @ </blockquote>
  @
  @ <a name="delshun"></a>
  @ <p>Enter the UUIDs of previously shunned artifacts to cause them to be
  @ accepted again in the repository.  The artifacts content is not
  @ restored because the content is unknown.  The only change is that







>
>
>
>
>



>







248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
    }else if( nRcvid ){
      db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid);
      while( db_step(&q)==SQLITE_ROW ){
        @ %s(db_column_text(&q, 0))
      }
      db_finalize(&q);
    }
  }else if( zUuid && reviewList ){
    const char *p;
    for( p = zUuid ; *p ; p += strlen(p)+1 ){
      @ %s(p)
    }
  }
  @ </textarea>
  @ <input type="submit" name="add" value="Shun">
  @ <input type="submit" name="review" value="Review">
  @ </div></form>
  @ </blockquote>
  @
  @ <a name="delshun"></a>
  @ <p>Enter the UUIDs of previously shunned artifacts to cause them to be
  @ accepted again in the repository.  The artifacts content is not
  @ restored because the content is unknown.  The only change is that
Changes to src/sitemap.c.
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
    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++){







|







79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
    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++){
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
    }
    @ <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,







|







150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
    }
    @ <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,
Changes to src/skins.c.
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
    DiffConfig DCfg;
    construct_diff_flags(1, &DCfg);
    DCfg.diffFlags |= DIFF_STRIP_EOLCR;
    if( P("sbsdiff")!=0 ) DCfg.diffFlags |= DIFF_SIDEBYSIDE;
    blob_init(&to, zContent, -1);
    blob_init(&from, skin_file_content(zBasis, zFile), -1);
    blob_zero(&out);
    DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG; 
    if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
      text_diff(&from, &to, &out, &DCfg);
      @ %s(blob_str(&out))
    }else{
      DCfg.diffFlags |= DIFF_LINENO;
      text_diff(&from, &to, &out, &DCfg);
      @ <pre class="udiff">







|







883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
    DiffConfig DCfg;
    construct_diff_flags(1, &DCfg);
    DCfg.diffFlags |= DIFF_STRIP_EOLCR;
    if( P("sbsdiff")!=0 ) DCfg.diffFlags |= DIFF_SIDEBYSIDE;
    blob_init(&to, zContent, -1);
    blob_init(&from, skin_file_content(zBasis, zFile), -1);
    blob_zero(&out);
    DCfg.diffFlags |= DIFF_HTML | DIFF_NOTTOOBIG;
    if( DCfg.diffFlags & DIFF_SIDEBYSIDE ){
      text_diff(&from, &to, &out, &DCfg);
      @ %s(blob_str(&out))
    }else{
      DCfg.diffFlags |= DIFF_LINENO;
      text_diff(&from, &to, &out, &DCfg);
      @ <pre class="udiff">
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
  }else if( pAltSkin ){
    char *zPattern = mprintf("*/skn_%s", pAltSkin->zLabel);
    if( sqlite3_strglob(zPattern, zBase)==0 ){
      nBase -= strlen(zPattern)-1;
      zBase[nBase] = 0;
    }
    fossil_free(zPattern);
  } 
  login_check_credentials();
  style_header("Skins");
  if( iDraftSkin || nSkinRank<=1 ){
    @ <p class="warning">Warning:
    if( iDraftSkin>0 ){
      @ you are using a draft skin,
    }else{







|







1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
  }else if( pAltSkin ){
    char *zPattern = mprintf("*/skn_%s", pAltSkin->zLabel);
    if( sqlite3_strglob(zPattern, zBase)==0 ){
      nBase -= strlen(zPattern)-1;
      zBase[nBase] = 0;
    }
    fossil_free(zPattern);
  }
  login_check_credentials();
  style_header("Skins");
  if( iDraftSkin || nSkinRank<=1 ){
    @ <p class="warning">Warning:
    if( iDraftSkin>0 ){
      @ you are using a draft skin,
    }else{
Changes to src/smtp.c.
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
**
** Implementation of SMTP (Simple Mail Transport Protocol) according
** to RFC 5321.
*/
#include "config.h"
#include "smtp.h"
#include <assert.h>
#if (HAVE_DN_EXPAND || HAVE___NS_NAME_UNCOMPRESS || HAVE_NS_NAME_UNCOMPRESS) && \
    (HAVE_NS_PARSERR || HAVE___NS_PARSERR) && !defined(FOSSIL_OMIT_DNS)
#  include <sys/types.h>
#  include <netinet/in.h>
#  if defined(HAVE_BIND_RESOLV_H)
#    include <bind/resolv.h>
#    include <bind/arpa/nameser_compat.h>
#  else
#    include <arpa/nameser.h>







|
|







17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
**
** Implementation of SMTP (Simple Mail Transport Protocol) according
** to RFC 5321.
*/
#include "config.h"
#include "smtp.h"
#include <assert.h>
#if (HAVE_DN_EXPAND || HAVE___NS_NAME_UNCOMPRESS || HAVE_NS_NAME_UNCOMPRESS) \
     && (HAVE_NS_PARSERR || HAVE___NS_PARSERR) && !defined(FOSSIL_OMIT_DNS)
#  include <sys/types.h>
#  include <netinet/in.h>
#  if defined(HAVE_BIND_RESOLV_H)
#    include <bind/resolv.h>
#    include <bind/arpa/nameser_compat.h>
#  else
#    include <arpa/nameser.h>
Changes to src/sqlcmd.c.
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
**    files_of_checkin(X)       A table-valued function that returns info on
**                              all files contained in check-in X.  Example:
**
**                                  SELECT * FROM files_of_checkin('trunk');
**
**    helptext                  A virtual table with one row for each command,
**                              webpage, and setting together with the built-in
**                              help text.  
**
**    now()                     Return the number of seconds since 1970.
**
**    obscure(T)                Obfuscate the text password T so that its
**                              original value is not readily visible.  Fossil
**                              uses this same algorithm when storing passwords
**                              of remote URLs.







|







382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
**    files_of_checkin(X)       A table-valued function that returns info on
**                              all files contained in check-in X.  Example:
**
**                                  SELECT * FROM files_of_checkin('trunk');
**
**    helptext                  A virtual table with one row for each command,
**                              webpage, and setting together with the built-in
**                              help text.
**
**    now()                     Return the number of seconds since 1970.
**
**    obscure(T)                Obfuscate the text password T so that its
**                              original value is not readily visible.  Fossil
**                              uses this same algorithm when storing passwords
**                              of remote URLs.
Changes to src/stash.c.
425
426
427
428
429
430
431

432
433
434

435
436
437
438

439
440
441
442
443
444
445
    int rid = db_column_int(&q, 0);
    int isRemoved = db_column_int(&q, 1);
    int isLink = db_column_int(&q, 3);
    const char *zOrig = db_column_text(&q, 4);
    const char *zNew = db_column_text(&q, 5);
    char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
    Blob a, b;

    if( rid==0 ){
      db_ephemeral_blob(&q, 6, &a);
      if( !bWebpage ) fossil_print("ADDED %s\n", zNew);

      diff_print_index(zNew, pCfg, 0);
      diff_file_mem(&empty, &a, zNew, pCfg);
    }else if( isRemoved ){
      if( !bWebpage) fossil_print("DELETE %s\n", zOrig);

      diff_print_index(zNew, pCfg, 0);
      if( fBaseline ){
        content_get(rid, &a);
        diff_file_mem(&a, &empty, zOrig, pCfg);
      }
    }else{
      Blob delta;







>



>




>







425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
    int rid = db_column_int(&q, 0);
    int isRemoved = db_column_int(&q, 1);
    int isLink = db_column_int(&q, 3);
    const char *zOrig = db_column_text(&q, 4);
    const char *zNew = db_column_text(&q, 5);
    char *zOPath = mprintf("%s%s", g.zLocalRoot, zOrig);
    Blob a, b;
    pCfg->diffFlags &= (~DIFF_FILE_MASK);
    if( rid==0 ){
      db_ephemeral_blob(&q, 6, &a);
      if( !bWebpage ) fossil_print("ADDED %s\n", zNew);
      pCfg->diffFlags |= DIFF_FILE_ADDED;
      diff_print_index(zNew, pCfg, 0);
      diff_file_mem(&empty, &a, zNew, pCfg);
    }else if( isRemoved ){
      if( !bWebpage) fossil_print("DELETE %s\n", zOrig);
      pCfg->diffFlags |= DIFF_FILE_DELETED;
      diff_print_index(zNew, pCfg, 0);
      if( fBaseline ){
        content_get(rid, &a);
        diff_file_mem(&a, &empty, zOrig, pCfg);
      }
    }else{
      Blob delta;
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
  stash_tables_exist_and_current();
  if( g.argc<=2 ){
    zCmd = "save";
  }else{
    zCmd = g.argv[2];
  }
  nCmd = strlen(zCmd);
  if( memcmp(zCmd, "save", nCmd)==0 ){
    if( unsaved_changes(0)==0 ){
      fossil_fatal("nothing to stash");
    }
    stashid = stash_create();
    undo_disable();
    if( g.argc>=2 ){
      int nFile = db_int(0, "SELECT count(*) FROM stashfile WHERE stashid=%d",







|







570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
  stash_tables_exist_and_current();
  if( g.argc<=2 ){
    zCmd = "save";
  }else{
    zCmd = g.argv[2];
  }
  nCmd = strlen(zCmd);
  if( strncmp(zCmd, "save", nCmd)==0 ){
    if( unsaved_changes(0)==0 ){
      fossil_fatal("nothing to stash");
    }
    stashid = stash_create();
    undo_disable();
    if( g.argc>=2 ){
      int nFile = db_int(0, "SELECT count(*) FROM stashfile WHERE stashid=%d",
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
    ** we have a copy of the changes before deleting them. */
    db_commit_transaction();
    g.argv[1] = "revert";
    revert_cmd();
    fossil_print("stash %d saved\n", stashid);
    return;
  }else
  if( memcmp(zCmd, "snapshot", nCmd)==0 ){
    stash_create();
  }else
  if( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){
    Stmt q, q2;
    int n = 0, width;
    int verboseFlag = find_option("verbose","v",0)!=0;
    const char *zWidth = find_option("width","W",1);

    if( zWidth ){
      width = atoi(zWidth);







|


|







601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
    ** we have a copy of the changes before deleting them. */
    db_commit_transaction();
    g.argv[1] = "revert";
    revert_cmd();
    fossil_print("stash %d saved\n", stashid);
    return;
  }else
  if( strncmp(zCmd, "snapshot", nCmd)==0 ){
    stash_create();
  }else
  if( strncmp(zCmd, "list", nCmd)==0 || strncmp(zCmd, "ls", nCmd)==0 ){
    Stmt q, q2;
    int n = 0, width;
    int verboseFlag = find_option("verbose","v",0)!=0;
    const char *zWidth = find_option("width","W",1);

    if( zWidth ){
      width = atoi(zWidth);
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
        db_reset(&q2);
      }
    }
    db_finalize(&q);
    if( verboseFlag ) db_finalize(&q2);
    if( n==0 ) fossil_print("empty stash\n");
  }else
  if( memcmp(zCmd, "drop", nCmd)==0 || memcmp(zCmd, "rm", nCmd)==0 ){
    int allFlag = find_option("all", "a", 0)!=0;
    if( allFlag ){
      Blob ans;
      char cReply;
      prompt_user("This action is not undoable.  Continue (y/N)? ", &ans);
      cReply = blob_str(&ans)[0];
      if( cReply=='y' || cReply=='Y' ){







|







668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
        db_reset(&q2);
      }
    }
    db_finalize(&q);
    if( verboseFlag ) db_finalize(&q2);
    if( n==0 ) fossil_print("empty stash\n");
  }else
  if( strncmp(zCmd, "drop", nCmd)==0 || strncmp(zCmd, "rm", nCmd)==0 ){
    int allFlag = find_option("all", "a", 0)!=0;
    if( allFlag ){
      Blob ans;
      char cReply;
      prompt_user("This action is not undoable.  Continue (y/N)? ", &ans);
      cReply = blob_str(&ans)[0];
      if( cReply=='y' || cReply=='Y' ){
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
    }else{
      undo_begin();
      undo_save_stash(0);
      stash_drop(stashid);
      undo_finish();
    }
  }else
  if( memcmp(zCmd, "pop", nCmd)==0 ||  memcmp(zCmd, "apply", nCmd)==0 ){
    char *zCom = 0, *zDate = 0, *zHash = 0;
    int popped = *zCmd=='p';
    if( popped ){
      if( g.argc>3 ) usage("pop");
      stashid = stash_get_id(0);
    }else{
      if( g.argc>4 ) usage("apply STASHID");







|







694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
    }else{
      undo_begin();
      undo_save_stash(0);
      stash_drop(stashid);
      undo_finish();
    }
  }else
  if( strncmp(zCmd, "pop", nCmd)==0 ||  strncmp(zCmd, "apply", nCmd)==0 ){
    char *zCom = 0, *zDate = 0, *zHash = 0;
    int popped = *zCmd=='p';
    if( popped ){
      if( g.argc>3 ) usage("pop");
      stashid = stash_get_id(0);
    }else{
      if( g.argc>4 ) usage("apply STASHID");
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
    }
    fossil_free(zCom);
    fossil_free(zDate);
    fossil_free(zHash);
    undo_finish();
    if( popped ) stash_drop(stashid);
  }else
  if( memcmp(zCmd, "goto", nCmd)==0 ){
    int nConflict;
    int vid;
    if( g.argc>4 ) usage("apply STASHID");
    stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
    undo_begin();
    vid = db_int(0, "SELECT blob.rid FROM stash,blob"
                    " WHERE stashid=%d AND blob.uuid=stash.hash", stashid);
    nConflict = update_to(vid);
    stash_apply(stashid, nConflict);
    db_multi_exec("UPDATE vfile SET mtime=0 WHERE pathname IN "
                  "(SELECT origname FROM stashfile WHERE stashid=%d)",
                  stashid);
    undo_finish();
  }else
  if( memcmp(zCmd, "diff", nCmd)==0
   || memcmp(zCmd, "gdiff", nCmd)==0
   || memcmp(zCmd, "show", nCmd)==0
   || memcmp(zCmd, "gshow", nCmd)==0
   || memcmp(zCmd, "cat", nCmd)==0
   || memcmp(zCmd, "gcat", nCmd)==0
  ){
    int fBaseline = 0;
    DiffConfig DCfg;

    if( strstr(zCmd,"show")!=0 || strstr(zCmd,"cat")!=0 ){
      fBaseline = 1;
    }
    if( find_option("tk",0,0)!=0 ){
      db_close(0);
      diff_tk(fBaseline ? "stash show" : "stash diff", 3);
      return;
    }
    diff_options(&DCfg, zCmd[0]=='g', 0);
    stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
    stash_diff(stashid, fBaseline, &DCfg);
  }else
  if( memcmp(zCmd, "help", nCmd)==0 ){
    g.argv[1] = "help";
    g.argv[2] = "stash";
    g.argc = 3;
    help_cmd();
  }else
  {
    usage("SUBCOMMAND ARGS...");
  }
  db_end_transaction(0);
}







|














|
|
|
|
|
|
















|










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
    }
    fossil_free(zCom);
    fossil_free(zDate);
    fossil_free(zHash);
    undo_finish();
    if( popped ) stash_drop(stashid);
  }else
  if( strncmp(zCmd, "goto", nCmd)==0 ){
    int nConflict;
    int vid;
    if( g.argc>4 ) usage("apply STASHID");
    stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
    undo_begin();
    vid = db_int(0, "SELECT blob.rid FROM stash,blob"
                    " WHERE stashid=%d AND blob.uuid=stash.hash", stashid);
    nConflict = update_to(vid);
    stash_apply(stashid, nConflict);
    db_multi_exec("UPDATE vfile SET mtime=0 WHERE pathname IN "
                  "(SELECT origname FROM stashfile WHERE stashid=%d)",
                  stashid);
    undo_finish();
  }else
  if( strncmp(zCmd, "diff", nCmd)==0
   || strncmp(zCmd, "gdiff", nCmd)==0
   || strncmp(zCmd, "show", nCmd)==0
   || strncmp(zCmd, "gshow", nCmd)==0
   || strncmp(zCmd, "cat", nCmd)==0
   || strncmp(zCmd, "gcat", nCmd)==0
  ){
    int fBaseline = 0;
    DiffConfig DCfg;

    if( strstr(zCmd,"show")!=0 || strstr(zCmd,"cat")!=0 ){
      fBaseline = 1;
    }
    if( find_option("tk",0,0)!=0 ){
      db_close(0);
      diff_tk(fBaseline ? "stash show" : "stash diff", 3);
      return;
    }
    diff_options(&DCfg, zCmd[0]=='g', 0);
    stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
    stash_diff(stashid, fBaseline, &DCfg);
  }else
  if( strncmp(zCmd, "help", nCmd)==0 ){
    g.argv[1] = "help";
    g.argv[2] = "stash";
    g.argc = 3;
    help_cmd();
  }else
  {
    usage("SUBCOMMAND ARGS...");
  }
  db_end_transaction(0);
}
Changes to src/stat.c.
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
    }else{
      @ <tr><td width='100%%'>%h(db_column_text(&q,0))</td>
      @ <td><nobr>%h(db_column_text(&q,1))</nobr></td></tr>
    }
    cnt++;
  }
  db_finalize(&q);
  
  if( nOmitted ){
    @ <tr><td><a href="urllist?all"><i>Show %d(nOmitted) more...</i></a>
  }
  if( cnt ){
    @ </table>
    total += cnt;
  }







|







555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
    }else{
      @ <tr><td width='100%%'>%h(db_column_text(&q,0))</td>
      @ <td><nobr>%h(db_column_text(&q,1))</nobr></td></tr>
    }
    cnt++;
  }
  db_finalize(&q);

  if( nOmitted ){
    @ <tr><td><a href="urllist?all"><i>Show %d(nOmitted) more...</i></a>
  }
  if( cnt ){
    @ </table>
    total += cnt;
  }
712
713
714
715
716
717
718
















719
720
721
722
723
724
725
*/
void repo_schema_page(void){
  Stmt q;
  Blob sql;
  const char *zArg = P("n");
  login_check_credentials();
  if( !g.perm.Admin ){ login_needed(0); return; }

















  style_set_current_feature("stat");
  style_header("Repository Schema");
  style_adunit_config(ADUNIT_RIGHT_OK);
  style_submenu_element("Stat", "stat");
  style_submenu_element("URLs", "urllist");
  if( sqlite3_compileoption_used("ENABLE_DBSTAT_VTAB") ){







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







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
*/
void repo_schema_page(void){
  Stmt q;
  Blob sql;
  const char *zArg = P("n");
  login_check_credentials();
  if( !g.perm.Admin ){ login_needed(0); return; }

  if( zArg!=0
   && db_table_exists("repository",zArg)
   && cgi_csrf_safe(1)
  ){
    if( P("analyze")!=0 ){
      db_multi_exec("ANALYZE \"%w\"", zArg);
    }else if( P("analyze200")!=0 ){
      db_multi_exec("PRAGMA analysis_limit=200; ANALYZE \"%w\"", zArg);
    }else if( P("deanalyze")!=0 ){
      db_unprotect(PROTECT_ALL);
      db_multi_exec("DELETE FROM repository.sqlite_stat1"
                    " WHERE tbl LIKE %Q", zArg);
      db_protect_pop();
    }
  }

  style_set_current_feature("stat");
  style_header("Repository Schema");
  style_adunit_config(ADUNIT_RIGHT_OK);
  style_submenu_element("Stat", "stat");
  style_submenu_element("URLs", "urllist");
  if( sqlite3_compileoption_used("ENABLE_DBSTAT_VTAB") ){
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
      }
      @ </pre>
      db_finalize(&q);
    }else{
      style_submenu_element("Stat1","repo_stat1");
    }
  }







  style_finish_page();
}

/*
** WEBPAGE: repo_stat1
**
** Show the sqlite_stat1 table for the repository schema
*/
void repo_stat1_page(void){

  login_check_credentials();
  if( !g.perm.Admin ){ login_needed(0); return; }











  style_set_current_feature("stat");
  style_header("Repository STAT1 Table");
  style_adunit_config(ADUNIT_RIGHT_OK);
  style_submenu_element("Stat", "stat");
  style_submenu_element("Schema", "repo_schema");

  if( db_table_exists("repository","sqlite_stat1") ){
    Stmt q;
    db_prepare(&q,
      "SELECT tbl, idx, stat FROM repository.sqlite_stat1"
      " ORDER BY tbl, idx");




    @ <pre>

    while( db_step(&q)==SQLITE_ROW ){
      const char *zTab = db_column_text(&q,0);
      const char *zIdx = db_column_text(&q,1);
      const char *zStat = db_column_text(&q,2);
      char *zUrl = href("%R/repo_schema?n=%t",zTab);




      @ INSERT INTO sqlite_stat1 VALUES('%z(zUrl)%h(zTab)</a>','%h(zIdx)','%h(zStat)');
    }




    @ </pre>

    db_finalize(&q);
  }










  style_finish_page();
}

/*
** WEBPAGE: repo-tabsize
**
** Show relative sizes of tables in the repository database.







>
>
>
>
>
>
>









>


>

>
>
>
>
>
>
>
>
>





>





>
>
>
>
|
>





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


>
>
>
>
>
>
>
>
>
>







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
      }
      @ </pre>
      db_finalize(&q);
    }else{
      style_submenu_element("Stat1","repo_stat1");
    }
  }
  @ <hr><form method="POST">
  @ <input type="submit" name="analyze" value="Run ANALYZE"><br />
  @ <input type="submit" name="analyze200"\
  @  value="Run ANALYZE with limit=200"><br />
  @ <input type="submit" name="deanalyze" value="De-ANALYZE">
  @ </form>

  style_finish_page();
}

/*
** WEBPAGE: repo_stat1
**
** Show the sqlite_stat1 table for the repository schema
*/
void repo_stat1_page(void){
  int bTabular;
  login_check_credentials();
  if( !g.perm.Admin ){ login_needed(0); return; }
  bTabular = PB("tabular");

  if( P("analyze")!=0 && cgi_csrf_safe(1) ){
    db_multi_exec("ANALYZE");
  }else if( P("analyze200")!=0 && cgi_csrf_safe(1) ){
    db_multi_exec("PRAGMA analysis_limit=200; ANALYZE;");
  }else if( P("deanalyze")!=0 && cgi_csrf_safe(1) ){
    db_unprotect(PROTECT_ALL);
    db_multi_exec("DELETE FROM repository.sqlite_stat1;");
    db_protect_pop();
  }
  style_set_current_feature("stat");
  style_header("Repository STAT1 Table");
  style_adunit_config(ADUNIT_RIGHT_OK);
  style_submenu_element("Stat", "stat");
  style_submenu_element("Schema", "repo_schema");
  style_submenu_checkbox("tabular", "Tabular", 0, 0);
  if( db_table_exists("repository","sqlite_stat1") ){
    Stmt q;
    db_prepare(&q,
      "SELECT tbl, idx, stat FROM repository.sqlite_stat1"
      " ORDER BY tbl, idx");
    if( bTabular ){
      @ <table border="1" cellpadding="0" cellspacing="0">
      @ <tr><th>Table<th>Index<th>Stat
    }else{
      @ <pre>
    }
    while( db_step(&q)==SQLITE_ROW ){
      const char *zTab = db_column_text(&q,0);
      const char *zIdx = db_column_text(&q,1);
      const char *zStat = db_column_text(&q,2);
      char *zUrl = href("%R/repo_schema?n=%t",zTab);
      if( bTabular ){
        @ <tr><td>%z(zUrl)%h(zTab)</a><td>%h(zIdx)<td>%h(zStat)
      }else{
        @ INSERT INTO sqlite_stat1 \
        @ VALUES('%z(zUrl)%h(zTab)</a>','%h(zIdx)','%h(zStat)');
      }
    }
    if( bTabular ){
      @ </table>
    }else{
      @ </pre>
    }
    db_finalize(&q);
  }
  @ <p><form method="POST">
  if( bTabular ){
    @ <input type="hidden" name="tabular" value="1">
  }
  @ <input type="submit" name="analyze" value="Run ANALYZE"><br />
  @ <input type="submit" name="analyze200"\
  @  value="Run ANALYZE with limit=200"><br>
  @ <input type="submit" name="deanalyze"\
  @  value="De-ANALYZE">
  @ </form>
  style_finish_page();
}

/*
** WEBPAGE: repo-tabsize
**
** Show relative sizes of tables in the repository database.
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
935
936
937

/*
** Gather statistics on artifact types, counts, and sizes.
**
** Only populate the artstat.atype field if the bWithTypes parameter is true.
*/
void gather_artifact_stats(int bWithTypes){
  static const char zSql[] = 
    @ CREATE TEMP TABLE artstat(
    @   id INTEGER PRIMARY KEY,   -- Corresponds to BLOB.RID
    @   atype TEXT,               -- 'data', 'manifest', 'tag', 'wiki', etc.
    @   isDelta BOOLEAN,          -- true if stored as a delta
    @   szExp,                    -- expanded, uncompressed size
    @   szCmpr                    -- size as stored on disk
    @ );
    @ INSERT INTO artstat(id,atype,isDelta,szExp,szCmpr)
    @    SELECT blob.rid, NULL,
    @           delta.rid IS NOT NULL,
    @           size, octet_length(content)
    @      FROM blob LEFT JOIN delta ON blob.rid=delta.rid
    @     WHERE content IS NOT NULL;
  ;
  static const char zSql2[] = 
    @ UPDATE artstat SET atype='file'
    @  WHERE +id IN (SELECT fid FROM mlink);
    @ UPDATE artstat SET atype='manifest'
    @  WHERE id IN (SELECT objid FROM event WHERE type='ci') AND atype IS NULL;
    @ UPDATE artstat SET atype='forum'
    @  WHERE id IN (SELECT objid FROM event WHERE type='f') AND atype IS NULL;
    @ UPDATE artstat SET atype='cluster'
    @  WHERE atype IS NULL 
    @    AND id IN (SELECT rid FROM tagxref
    @                WHERE tagid=(SELECT tagid FROM tag
    @                              WHERE tagname='cluster'));
    @ UPDATE artstat SET atype='ticket'
    @  WHERE atype IS NULL 
    @    AND id IN (SELECT rid FROM tagxref
    @                WHERE tagid IN (SELECT tagid FROM tag
    @                              WHERE tagname GLOB 'tkt-*'));
    @ UPDATE artstat SET atype='wiki'
    @  WHERE atype IS NULL 
    @    AND id IN (SELECT rid FROM tagxref
    @                WHERE tagid IN (SELECT tagid FROM tag
    @                              WHERE tagname GLOB 'wiki-*'));
    @ UPDATE artstat SET atype='technote'
    @  WHERE atype IS NULL 
    @    AND id IN (SELECT rid FROM tagxref
    @                WHERE tagid IN (SELECT tagid FROM tag
    @                              WHERE tagname GLOB 'event-*'));
    @ UPDATE artstat SET atype='attachment'
    @  WHERE atype IS NULL 
    @    AND id IN (SELECT attachid FROM attachment UNION 
    @               SELECT blob.rid FROM attachment JOIN blob ON uuid=src);
    @ UPDATE artstat SET atype='tag'
    @  WHERE atype IS NULL 
    @    AND id IN (SELECT srcid FROM tagxref);
    @ UPDATE artstat SET atype='tag'
    @  WHERE atype IS NULL 
    @    AND id IN (SELECT objid FROM event WHERE type='g');
    @ UPDATE artstat SET atype='unused' WHERE atype IS NULL;
  ;
  db_multi_exec("%s", zSql/*safe-for-%s*/);
  if( bWithTypes ){
    db_multi_exec("%s", zSql2/*safe-for-%s*/);
  }







|














|







|




|




|




|




|
|


|


|







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

/*
** Gather statistics on artifact types, counts, and sizes.
**
** Only populate the artstat.atype field if the bWithTypes parameter is true.
*/
void gather_artifact_stats(int bWithTypes){
  static const char zSql[] =
    @ CREATE TEMP TABLE artstat(
    @   id INTEGER PRIMARY KEY,   -- Corresponds to BLOB.RID
    @   atype TEXT,               -- 'data', 'manifest', 'tag', 'wiki', etc.
    @   isDelta BOOLEAN,          -- true if stored as a delta
    @   szExp,                    -- expanded, uncompressed size
    @   szCmpr                    -- size as stored on disk
    @ );
    @ INSERT INTO artstat(id,atype,isDelta,szExp,szCmpr)
    @    SELECT blob.rid, NULL,
    @           delta.rid IS NOT NULL,
    @           size, octet_length(content)
    @      FROM blob LEFT JOIN delta ON blob.rid=delta.rid
    @     WHERE content IS NOT NULL;
  ;
  static const char zSql2[] =
    @ UPDATE artstat SET atype='file'
    @  WHERE +id IN (SELECT fid FROM mlink);
    @ UPDATE artstat SET atype='manifest'
    @  WHERE id IN (SELECT objid FROM event WHERE type='ci') AND atype IS NULL;
    @ UPDATE artstat SET atype='forum'
    @  WHERE id IN (SELECT objid FROM event WHERE type='f') AND atype IS NULL;
    @ UPDATE artstat SET atype='cluster'
    @  WHERE atype IS NULL
    @    AND id IN (SELECT rid FROM tagxref
    @                WHERE tagid=(SELECT tagid FROM tag
    @                              WHERE tagname='cluster'));
    @ UPDATE artstat SET atype='ticket'
    @  WHERE atype IS NULL
    @    AND id IN (SELECT rid FROM tagxref
    @                WHERE tagid IN (SELECT tagid FROM tag
    @                              WHERE tagname GLOB 'tkt-*'));
    @ UPDATE artstat SET atype='wiki'
    @  WHERE atype IS NULL
    @    AND id IN (SELECT rid FROM tagxref
    @                WHERE tagid IN (SELECT tagid FROM tag
    @                              WHERE tagname GLOB 'wiki-*'));
    @ UPDATE artstat SET atype='technote'
    @  WHERE atype IS NULL
    @    AND id IN (SELECT rid FROM tagxref
    @                WHERE tagid IN (SELECT tagid FROM tag
    @                              WHERE tagname GLOB 'event-*'));
    @ UPDATE artstat SET atype='attachment'
    @  WHERE atype IS NULL
    @    AND id IN (SELECT attachid FROM attachment UNION
    @               SELECT blob.rid FROM attachment JOIN blob ON uuid=src);
    @ UPDATE artstat SET atype='tag'
    @  WHERE atype IS NULL
    @    AND id IN (SELECT srcid FROM tagxref);
    @ UPDATE artstat SET atype='tag'
    @  WHERE atype IS NULL
    @    AND id IN (SELECT objid FROM event WHERE type='g');
    @ UPDATE artstat SET atype='unused' WHERE atype IS NULL;
  ;
  db_multi_exec("%s", zSql/*safe-for-%s*/);
  if( bWithTypes ){
    db_multi_exec("%s", zSql2/*safe-for-%s*/);
  }
Changes to src/statrep.c.
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
    const char *zNot = rc=='n' ? "NOT" : "";
    statsReportTimelineYFlag = "ci";
    db_multi_exec(
      "CREATE TEMP VIEW v_reports AS "
      "SELECT * FROM event WHERE type='ci' AND %s"
      " AND objid %s IN (SELECT cid FROM plink WHERE NOT isprim)",
      zTimeSpan/*safe-for-%s*/, zNot/*safe-for-%s*/
    );        
  }
  return statsReportType = rc;
}

/*
** Returns a string suitable (for a given value of suitable) for
** use in a label with the header of the /reports pages, dependent







|







128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
    const char *zNot = rc=='n' ? "NOT" : "";
    statsReportTimelineYFlag = "ci";
    db_multi_exec(
      "CREATE TEMP VIEW v_reports AS "
      "SELECT * FROM event WHERE type='ci' AND %s"
      " AND objid %s IN (SELECT cid FROM plink WHERE NOT isprim)",
      zTimeSpan/*safe-for-%s*/, zNot/*safe-for-%s*/
    );
  }
  return statsReportType = rc;
}

/*
** Returns a string suitable (for a given value of suitable) for
** use in a label with the header of the /reports pages, dependent
Changes to src/style.c.
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
  ** or after any updates to the CSS files
  */
  blob_appendf(&url, "?id=%x", skin_id("css"));
  if( P("once")!=0 && P("skin")!=0 ){
    blob_appendf(&url, "&skin=%s&once", skin_in_use());
  }

  /* Generate the CSS URL variable */  
  Th_Store("stylesheet_url", blob_str(&url));
  blob_reset(&url);
}

/*
** Create a TH1 variable containing the URL for the specified image.
** The resulting variable name will be of the form $[zImageName]_image_url.
** The value will be a URL that includes an id= query parameter that
** changes if the underlying resource changes or if a different skin
** is selected.
*/
static void image_url_var(const char *zImageName){
  char *zVarName;   /* Name of the new TH1 variable */
  char *zResource;  /* Name of CONFIG entry holding content */
  char *zUrl;       /* The URL */

  zResource = mprintf("%s-image", zImageName);
  zUrl = mprintf("%R/%s?id=%x", zImageName, skin_id(zResource));
  free(zResource);
  zVarName = mprintf("%s_image_url", zImageName);  
  Th_Store(zVarName, zUrl);
  free(zVarName);
  free(zUrl);
}

/*
** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js







|



















|







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
  ** or after any updates to the CSS files
  */
  blob_appendf(&url, "?id=%x", skin_id("css"));
  if( P("once")!=0 && P("skin")!=0 ){
    blob_appendf(&url, "&skin=%s&once", skin_in_use());
  }

  /* Generate the CSS URL variable */
  Th_Store("stylesheet_url", blob_str(&url));
  blob_reset(&url);
}

/*
** Create a TH1 variable containing the URL for the specified image.
** The resulting variable name will be of the form $[zImageName]_image_url.
** The value will be a URL that includes an id= query parameter that
** changes if the underlying resource changes or if a different skin
** is selected.
*/
static void image_url_var(const char *zImageName){
  char *zVarName;   /* Name of the new TH1 variable */
  char *zResource;  /* Name of CONFIG entry holding content */
  char *zUrl;       /* The URL */

  zResource = mprintf("%s-image", zImageName);
  zUrl = mprintf("%R/%s?id=%x", zImageName, skin_id(zResource));
  free(zResource);
  zVarName = mprintf("%s_image_url", zImageName);
  Th_Store(zVarName, zUrl);
  free(zVarName);
  free(zUrl);
}

/*
** Output TEXT with a click-to-copy button next to it. Loads the copybtn.js
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
** The text '$nonce' is replaced by style_nonce() if and whereever it
** occurs in the input string.
**
** The string returned is obtained from fossil_malloc() and
** should be released by the caller.
*/
char *style_csp(int toHeader){
  static const char zBackupCSP[] = 
   "default-src 'self' data:; "
   "script-src 'self' 'nonce-$nonce'; "
   "style-src 'self' 'unsafe-inline'; "
   "img-src * data:";
  const char *zFormat;
  Blob csp;
  char *zNonce;







|







595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
** The text '$nonce' is replaced by style_nonce() if and whereever it
** occurs in the input string.
**
** The string returned is obtained from fossil_malloc() and
** should be released by the caller.
*/
char *style_csp(int toHeader){
  static const char zBackupCSP[] =
   "default-src 'self' data:; "
   "script-src 'self' 'nonce-$nonce'; "
   "style-src 'self' 'unsafe-inline'; "
   "img-src * data:";
  const char *zFormat;
  Blob csp;
  char *zNonce;
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
  return zCsp;
}

/*
** Disable content security policy for the current page.
** WARNING:  Do not do this lightly!
**
** This routine must be called before the CSP is sued by 
** style_header().
*/
void style_disable_csp(void){
  disableCSP = 1;
}

/*
** Default HTML page header text through <body>.  If the repository-specific
** header template lacks a <body> tag, then all of the following is
** prepended.
*/
static const char zDfltHeader[] = 
@ <html>
@ <head>
@ <meta charset="UTF-8">
@ <base href="$baseurl/$current_page">
@ <meta http-equiv="Content-Security-Policy" content="$default_csp">
@ <meta name="viewport" content="width=device-width, initial-scale=1.0">
@ <title>$<project_name>: $<title></title>







|











|







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

/*
** Disable content security policy for the current page.
** WARNING:  Do not do this lightly!
**
** This routine must be called before the CSP is sued by
** style_header().
*/
void style_disable_csp(void){
  disableCSP = 1;
}

/*
** Default HTML page header text through <body>.  If the repository-specific
** header template lacks a <body> tag, then all of the following is
** prepended.
*/
static const char zDfltHeader[] =
@ <html>
@ <head>
@ <meta charset="UTF-8">
@ <base href="$baseurl/$current_page">
@ <meta http-equiv="Content-Security-Policy" content="$default_csp">
@ <meta name="viewport" content="width=device-width, initial-scale=1.0">
@ <title>$<project_name>: $<title></title>
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
const char *get_default_header(){
  return zDfltHeader;
}

/*
** The default TCL list that defines the main menu.
*/
static const char zDfltMainMenu[] = 
@ Home      /home        *              {}
@ Timeline  /timeline    {o r j}        {}
@ Files     /dir?ci=tip  oh             desktoponly
@ Branches  /brlist      o              wideonly
@ Tags      /taglist     o              wideonly
@ Forum     /forum       {@2 3 4 5 6}   wideonly
@ Chat      /chat        C              wideonly







|







668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
const char *get_default_header(){
  return zDfltHeader;
}

/*
** The default TCL list that defines the main menu.
*/
static const char zDfltMainMenu[] =
@ Home      /home        *              {}
@ Timeline  /timeline    {o r j}        {}
@ Files     /dir?ci=tip  oh             desktoponly
@ Branches  /brlist      o              wideonly
@ Tags      /taglist     o              wideonly
@ Forum     /forum       {@2 3 4 5 6}   wideonly
@ Chat      /chat        C              wideonly
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
  if( !login_is_nobody() ){
    Th_Store("login", g.zLogin);
  }
  Th_MaybeStore("current_feature", feature_from_page_path(local_zCurrentPage) );
  if( g.ftntsIssues[0] || g.ftntsIssues[1] ||
      g.ftntsIssues[2] || g.ftntsIssues[3] ){
    char buf[80];
    sqlite3_snprintf(sizeof(buf),buf,"%i %i %i %i",g.ftntsIssues[0],g.ftntsIssues[1],
                                  g.ftntsIssues[2],g.ftntsIssues[3]);
    Th_Store("footnotes_issues_counters", buf);
  }
}

/*
** Draw the header.
*/







|
|







793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
  if( !login_is_nobody() ){
    Th_Store("login", g.zLogin);
  }
  Th_MaybeStore("current_feature", feature_from_page_path(local_zCurrentPage) );
  if( g.ftntsIssues[0] || g.ftntsIssues[1] ||
      g.ftntsIssues[2] || g.ftntsIssues[3] ){
    char buf[80];
    sqlite3_snprintf(sizeof(buf), buf, "%i %i %i %i", g.ftntsIssues[0],
                     g.ftntsIssues[1], g.ftntsIssues[2], g.ftntsIssues[3]);
    Th_Store("footnotes_issues_counters", buf);
  }
}

/*
** Draw the header.
*/
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
**    *   $basename
**    *   $secureurl
**    *   $home
**    *   $logo
**    *   $background
**
** The output from TH1 becomes the style sheet.  Fossil always reports
** that the style sheet is cacheable.  
*/
void page_style_css(void){
  Blob css = empty_blob;
  int i;
  const char * zDefaults;
  const char *zSkin;








|







1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
**    *   $basename
**    *   $secureurl
**    *   $home
**    *   $logo
**    *   $background
**
** The output from TH1 becomes the style sheet.  Fossil always reports
** that the style sheet is cacheable.
*/
void page_style_css(void){
  Blob css = empty_blob;
  int i;
  const char * zDefaults;
  const char *zSkin;

1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
  /* Tell CGI that the content returned by this page is considered cacheable */
  g.isConst = 1;
}

/*
** All possible capabilities
*/
static const char allCap[] = 
  "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKL";

/*
** Compute the current login capabilities
*/
static char *find_capabilities(char *zCap){
  int i, j;







|







1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
  /* Tell CGI that the content returned by this page is considered cacheable */
  g.isConst = 1;
}

/*
** All possible capabilities
*/
static const char allCap[] =
  "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKL";

/*
** Compute the current login capabilities
*/
static char *find_capabilities(char *zCap){
  int i, j;
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
         break;
      }
      default: {
         @ CSRF safety = unsafe<br>
         break;
      }
    }
    
    @ fossil_exe_id() = %h(fossil_exe_id())<br>
    if( g.perm.Admin ){
      int k;
      for(k=0; g.argvOrig[k]; k++){
        Blob t;
        blob_init(&t, 0, 0);
        blob_append_escaped_arg(&t, g.argvOrig[k], 0);







|







1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
         break;
      }
      default: {
         @ CSRF safety = unsafe<br>
         break;
      }
    }

    @ fossil_exe_id() = %h(fossil_exe_id())<br>
    if( g.perm.Admin ){
      int k;
      for(k=0; g.argvOrig[k]; k++){
        Blob t;
        blob_init(&t, 0, 0);
        blob_append_escaped_arg(&t, g.argvOrig[k], 0);
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
** Example:
**
** style_select_list_int("my-grapes", "my_grapes", "Grapes",
**                      "Select the number of grapes",
**                       atoi(PD("my_field","0")),
**                       "", 1, "2", 2, "Three", 3,
**                       NULL);
** 
*/
void style_select_list_int(const char * zWrapperId,
                           const char *zFieldName, const char * zLabel,
                           const char * zToolTip, int selectedVal,
                           ... ){
  char * zLabelID = style_next_input_id();
  va_list vargs;







|







1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
** Example:
**
** style_select_list_int("my-grapes", "my_grapes", "Grapes",
**                      "Select the number of grapes",
**                       atoi(PD("my_field","0")),
**                       "", 1, "2", 2, "Three", 3,
**                       NULL);
**
*/
void style_select_list_int(const char * zWrapperId,
                           const char *zFieldName, const char * zLabel,
                           const char * zToolTip, int selectedVal,
                           ... ){
  char * zLabelID = style_next_input_id();
  va_list vargs;
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
    if( z[0]=='/' || z[0]=='\\' ){
      zOrigin = z+1;
    }
  }
  CX("<script nonce='%s'>/* %s:%d */\n", style_nonce(), zOrigin, iLine);
}

/* Generate the closing </script> tag 
*/
void style_script_end(void){
  CX("</script>\n");
}

/*
** Emits a NOSCRIPT tag with an error message stating that JS is







|







1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
    if( z[0]=='/' || z[0]=='\\' ){
      zOrigin = z+1;
    }
  }
  CX("<script nonce='%s'>/* %s:%d */\n", style_nonce(), zOrigin, iLine);
}

/* Generate the closing </script> tag
*/
void style_script_end(void){
  CX("</script>\n");
}

/*
** Emits a NOSCRIPT tag with an error message stating that JS is
Changes to src/style.fileedit.css.
74
75
76
77
78
79
80



81
82
83
84
85
86
87
  overflow: auto;
}
body.fileedit #fileedit-tab-preview-wrapper > pre {
  margin: 0;
}
body.fileedit #fileedit-tab-fileselect > h1 {
  margin: 0;



}
body.fileedit .fileedit-options.commit-message > div {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  font-family: monospace;
}







>
>
>







74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
  overflow: auto;
}
body.fileedit #fileedit-tab-preview-wrapper > pre {
  margin: 0;
}
body.fileedit #fileedit-tab-fileselect > h1 {
  margin: 0;
}
body.fileedit .fileedit-options > div > * {
  margin: 0.25em;
}
body.fileedit .fileedit-options.commit-message > div {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  font-family: monospace;
}
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
  margin: 0.5em;
}
body.fileedit .tab-container > .tabs > .tab-panel > .fileedit-options > input {
  vertical-align: middle;
  margin: 0.5em;
}
body.fileedit .tab-container > .tabs > .tab-panel > .fileedit-options > .input-with-label {
  vertical-align: middle;
  margin: 0.5em;
}
body.fileedit .fileedit-options > div > * {
  margin: 0.25em;
}
body.fileedit .fileedit-options.flex-container.flex-row {
  align-items: first baseline;
}







<
|







106
107
108
109
110
111
112

113
114
115
116
117
118
119
120
  margin: 0.5em;
}
body.fileedit .tab-container > .tabs > .tab-panel > .fileedit-options > input {
  vertical-align: middle;
  margin: 0.5em;
}
body.fileedit .tab-container > .tabs > .tab-panel > .fileedit-options > .input-with-label {

  margin: 0 0.5em 0.25em 0.5em;
}
body.fileedit .fileedit-options > div > * {
  margin: 0.25em;
}
body.fileedit .fileedit-options.flex-container.flex-row {
  align-items: first baseline;
}
Changes to src/style.wikiedit.css.
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
  margin: 0.5em;
}
body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options > input {
  vertical-align: middle;
  margin: 0.5em;
}
body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options > .input-with-label {
  vertical-align: middle;
  margin: 0 0.5em 0.25em 0.5em;
}
body.wikiedit label {
  display: inline; /* some skins set label display to block! */
}
body.wikiedit .wikiedit-options > div > * {
  margin: 0.25em;







<







41
42
43
44
45
46
47

48
49
50
51
52
53
54
  margin: 0.5em;
}
body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options > input {
  vertical-align: middle;
  margin: 0.5em;
}
body.wikiedit .tab-container > .tabs > .tab-panel > .wikiedit-options > .input-with-label {

  margin: 0 0.5em 0.25em 0.5em;
}
body.wikiedit label {
  display: inline; /* some skins set label display to block! */
}
body.wikiedit .wikiedit-options > div > * {
  margin: 0.25em;
Changes to src/sync.c.
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
  int configSync = 0;       /* configuration changes transferred */
  if( g.fNoSync ){
    return 0;
  }
  zAutosync = db_get_for_subsystem("autosync", zSubsys);
  if( zAutosync==0 ) zAutosync = "on";  /* defend against misconfig */
  if( is_false(zAutosync) ) return 0;
  if( db_get_boolean("dont-push",0) 
   || sqlite3_strglob("*pull*", zAutosync)==0
  ){
    flags &= ~SYNC_CKIN_LOCK;
    if( flags & SYNC_PUSH ) return 0;
  }
  if( find_option("verbose","v",0)!=0 ) flags |= SYNC_VERBOSE;
  url_parse(0, URL_REMEMBER|URL_USE_CONFIG);







|







126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
  int configSync = 0;       /* configuration changes transferred */
  if( g.fNoSync ){
    return 0;
  }
  zAutosync = db_get_for_subsystem("autosync", zSubsys);
  if( zAutosync==0 ) zAutosync = "on";  /* defend against misconfig */
  if( is_false(zAutosync) ) return 0;
  if( db_get_boolean("dont-push",0)
   || sqlite3_strglob("*pull*", zAutosync)==0
  ){
    flags &= ~SYNC_CKIN_LOCK;
    if( flags & SYNC_PUSH ) return 0;
  }
  if( find_option("verbose","v",0)!=0 ) flags |= SYNC_VERBOSE;
  url_parse(0, URL_REMEMBER|URL_USE_CONFIG);
301
302
303
304
305
306
307






308
309
310
311
312
313
314
  if( g.url.protocol==0 ){
    if( urlOptional ) fossil_exit(0);
    usage("URL");
  }
  user_select();
  url_enable_proxy("via proxy: ");
  *pConfigFlags |= configSync;






}


/*
** COMMAND: pull
**
** Usage: %fossil pull ?URL? ?options?







>
>
>
>
>
>







301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
  if( g.url.protocol==0 ){
    if( urlOptional ) fossil_exit(0);
    usage("URL");
  }
  user_select();
  url_enable_proxy("via proxy: ");
  *pConfigFlags |= configSync;
  if( (*pSyncFlags & SYNC_ALLURL)==0 && zUrl==0 ){
    const char *zAutosync = db_get_for_subsystem("autosync", "sync");
    if( sqlite3_strglob("*all*", zAutosync)==0 ){
      *pSyncFlags |= SYNC_ALLURL;
    }
  }
}


/*
** COMMAND: pull
**
** Usage: %fossil pull ?URL? ?options?
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
**
** > fossil remote list|ls
**
**     Show all remote repository URLs.
**
** > fossil remote off
**
**     Forget the default URL. This disables autosync. 
**
**     This is a convenient way to enter "airplane mode".  To enter
**     airplane mode, first save the current default URL, then turn the
**     default off.  Perhaps like this:
**
**         fossil remote add main default
**         fossil remote off







|







531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
**
** > fossil remote list|ls
**
**     Show all remote repository URLs.
**
** > fossil remote off
**
**     Forget the default URL. This disables autosync.
**
**     This is a convenient way to enter "airplane mode".  To enter
**     airplane mode, first save the current default URL, then turn the
**     default off.  Perhaps like this:
**
**         fossil remote add main default
**         fossil remote off
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
  **
  ** The last-sync-url is called "default" for the display list.
  **
  ** The last-sync-url might be duplicated into one of the sync-url:NAME
  ** entries.  Thus, when doing a "fossil sync --all" or an autosync with
  ** autosync=all, each sync-url:NAME entry is checked to see if it is the
  ** same as last-sync-url and if it is then that entry is skipped.
  */ 

  if( g.argc==2 ){
    /* "fossil remote" with no arguments:  Show the last sync URL. */
    zUrl = db_get("last-sync-url", 0);
    if( zUrl==0 ){
      fossil_print("off\n");
    }else{







|







593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
  **
  ** The last-sync-url is called "default" for the display list.
  **
  ** The last-sync-url might be duplicated into one of the sync-url:NAME
  ** entries.  Thus, when doing a "fossil sync --all" or an autosync with
  ** autosync=all, each sync-url:NAME entry is checked to see if it is the
  ** same as last-sync-url and if it is then that entry is skipped.
  */

  if( g.argc==2 ){
    /* "fossil remote" with no arguments:  Show the last sync URL. */
    zUrl = db_get("last-sync-url", 0);
    if( zUrl==0 ){
      fossil_print("off\n");
    }else{
Changes to src/tag.c.
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
    const char *zTagPrefix = find_option("prefix","",1);
    int nTagType = fRaw ? -1 : 0;

    if( zTagType!=0 ){
      int l = strlen(zTagType);
      if( strncmp(zTagType,"cancel",l)==0 ){
        nTagType = 0;
      }else if( strncmp(zTagType,"singleton",l)==0 ){ 
        nTagType = 1;
      }else if( strncmp(zTagType,"propagated",l)==0 ){ 
        nTagType = 2;
      }else{
        fossil_fatal("unrecognized tag type");
      }
    }
    if( g.argc==3 ){
      const int nTagPrefix = zTagPrefix ? (int)strlen(zTagPrefix) : 0;







|

|







638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
    const char *zTagPrefix = find_option("prefix","",1);
    int nTagType = fRaw ? -1 : 0;

    if( zTagType!=0 ){
      int l = strlen(zTagType);
      if( strncmp(zTagType,"cancel",l)==0 ){
        nTagType = 0;
      }else if( strncmp(zTagType,"singleton",l)==0 ){
        nTagType = 1;
      }else if( strncmp(zTagType,"propagated",l)==0 ){
        nTagType = 2;
      }else{
        fossil_fatal("unrecognized tag type");
      }
    }
    if( g.argc==3 ){
      const int nTagPrefix = zTagPrefix ? (int)strlen(zTagPrefix) : 0;
Changes to src/tar.c.
242
243
244
245
246
247
248
249

250
251
252
253
254
255
256
    n /= 10;
  }
  /* adding the length extended the length field? */
  if(blen > next10){
    blen++;
  }
  /* build the string */
  blob_appendf(&tball.pax, "%d %s=%*.*s\n", blen, zField, nValue, nValue, zValue);

  /* this _must_ be right */
  if((int)blob_size(&tball.pax) != blen){
    fossil_panic("internal error: PAX tar header has bad length");
  }
}









|
>







242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
    n /= 10;
  }
  /* adding the length extended the length field? */
  if(blen > next10){
    blen++;
  }
  /* build the string */
  blob_appendf(&tball.pax, "%d %s=%*.*s\n",
               blen, zField, nValue, nValue, zValue);
  /* this _must_ be right */
  if((int)blob_size(&tball.pax) != blen){
    fossil_panic("internal error: PAX tar header has bad length");
  }
}


Changes to src/terminal.c.
20
21
22
23
24
25
26



27
28
29
30
31
32
33

#include "config.h"
#include "terminal.h"
#include <assert.h>
#ifdef _WIN32
# include <windows.h>
#else



#include <sys/ioctl.h>
#include <stdio.h>
#include <unistd.h>
#endif










>
>
>







20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

#include "config.h"
#include "terminal.h"
#include <assert.h>
#ifdef _WIN32
# include <windows.h>
#else
#ifdef __EXTENSIONS__
#include <termio.h>
#endif
#include <sys/ioctl.h>
#include <stdio.h>
#include <unistd.h>
#endif



Changes to src/th.c.
2868
2869
2870
2871
2872
2873
2874

2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892

/*
** Set the result of the interpreter to the th1 representation of
** the integer iVal and return TH_OK.
*/
int Th_SetResultInt(Th_Interp *interp, int iVal){
  int isNegative = 0;

  char zBuf[32];
  char *z = &zBuf[32];

  if( iVal<0 ){
    isNegative = 1;
    iVal = iVal * -1;
  }
  *(--z) = '\0';
  *(--z) = (char)(48+((unsigned)iVal%10));
  while( (iVal = ((unsigned)iVal/10))>0 ){
    *(--z) = (char)(48+((unsigned)iVal%10));
    assert(z>zBuf);
  }
  if( isNegative ){
    *(--z) = '-';
  }

  return Th_SetResult(interp, z, -1);







>





|


|
|
|







2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893

/*
** Set the result of the interpreter to the th1 representation of
** the integer iVal and return TH_OK.
*/
int Th_SetResultInt(Th_Interp *interp, int iVal){
  int isNegative = 0;
  unsigned int uVal = iVal;
  char zBuf[32];
  char *z = &zBuf[32];

  if( iVal<0 ){
    isNegative = 1;
    uVal = iVal * -1;
  }
  *(--z) = '\0';
  *(--z) = (char)(48+(uVal%10));
  while( (uVal = (uVal/10))>0 ){
    *(--z) = (char)(48+(uVal%10));
    assert(z>zBuf);
  }
  if( isNegative ){
    *(--z) = '-';
  }

  return Th_SetResult(interp, z, -1);
Changes to src/th_main.c.
29
30
31
32
33
34
35
36

37
38
39
40
41
42
43
*/
#define TH_INIT_NONE        ((u32)0x00000000) /* No flags. */
#define TH_INIT_NEED_CONFIG ((u32)0x00000001) /* Open configuration first? */
#define TH_INIT_FORCE_TCL   ((u32)0x00000002) /* Force Tcl to be enabled? */
#define TH_INIT_FORCE_RESET ((u32)0x00000004) /* Force TH1 commands re-added? */
#define TH_INIT_FORCE_SETUP ((u32)0x00000008) /* Force eval of setup script? */
#define TH_INIT_NO_REPO     ((u32)0x00000010) /* Skip opening repository. */
#define TH_INIT_NO_ENCODE   ((u32)0x00000020) /* Do not html-encode sendText() output. */

#define TH_INIT_MASK        ((u32)0x0000003F) /* All possible init flags. */

/*
** Useful and/or "well-known" combinations of flag values.
*/
#define TH_INIT_DEFAULT     (TH_INIT_NONE)      /* Default flags. */
#define TH_INIT_HOOK        (TH_INIT_NEED_CONFIG | TH_INIT_FORCE_SETUP)







|
>







29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
*/
#define TH_INIT_NONE        ((u32)0x00000000) /* No flags. */
#define TH_INIT_NEED_CONFIG ((u32)0x00000001) /* Open configuration first? */
#define TH_INIT_FORCE_TCL   ((u32)0x00000002) /* Force Tcl to be enabled? */
#define TH_INIT_FORCE_RESET ((u32)0x00000004) /* Force TH1 commands re-added? */
#define TH_INIT_FORCE_SETUP ((u32)0x00000008) /* Force eval of setup script? */
#define TH_INIT_NO_REPO     ((u32)0x00000010) /* Skip opening repository. */
#define TH_INIT_NO_ENCODE   ((u32)0x00000020) /* Do not html-encode sendText()*/
                                              /* output. */
#define TH_INIT_MASK        ((u32)0x0000003F) /* All possible init flags. */

/*
** Useful and/or "well-known" combinations of flag values.
*/
#define TH_INIT_DEFAULT     (TH_INIT_NONE)      /* Default flags. */
#define TH_INIT_HOOK        (TH_INIT_NEED_CONFIG | TH_INIT_FORCE_SETUP)
Changes to src/th_tcl.c.
1162
1163
1164
1165
1166
1167
1168
1169

1170
1171
1172
1173
1174
1175
1176

1177
1178
1179
1180
1181
1182
1183
    Tcl_DeleteInterp(tclInterp); /* TODO: Redundant? */
    tclInterp = 0;
    return TH_ERROR;
  }
  tclContext->interp = tclInterp;
  if( Tcl_Init(tclInterp)!=TCL_OK ){
    Th_ErrorMessage(interp,
        "Tcl initialization error:", Tcl_GetString(Tcl_GetObjResult(tclInterp)), -1);

    Tcl_DeleteInterp(tclInterp);
    tclContext->interp = tclInterp = 0;
    return TH_ERROR;
  }
  if( setTclArguments(tclInterp, argc, argv)!=TCL_OK ){
    Th_ErrorMessage(interp,
        "Tcl error setting arguments:", Tcl_GetString(Tcl_GetObjResult(tclInterp)), -1);

    Tcl_DeleteInterp(tclInterp);
    tclContext->interp = tclInterp = 0;
    return TH_ERROR;
  }
  /*
  ** Determine (and cache) if an objProc can be called directly for a Tcl
  ** command invoked via the tclInvoke TH1 command.







|
>






|
>







1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
    Tcl_DeleteInterp(tclInterp); /* TODO: Redundant? */
    tclInterp = 0;
    return TH_ERROR;
  }
  tclContext->interp = tclInterp;
  if( Tcl_Init(tclInterp)!=TCL_OK ){
    Th_ErrorMessage(interp,
        "Tcl initialization error:",
        Tcl_GetString(Tcl_GetObjResult(tclInterp)), -1);
    Tcl_DeleteInterp(tclInterp);
    tclContext->interp = tclInterp = 0;
    return TH_ERROR;
  }
  if( setTclArguments(tclInterp, argc, argv)!=TCL_OK ){
    Th_ErrorMessage(interp,
        "Tcl error setting arguments:",
        Tcl_GetString(Tcl_GetObjResult(tclInterp)), -1);
    Tcl_DeleteInterp(tclInterp);
    tclContext->interp = tclInterp = 0;
    return TH_ERROR;
  }
  /*
  ** Determine (and cache) if an objProc can be called directly for a Tcl
  ** command invoked via the tclInvoke TH1 command.
1192
1193
1194
1195
1196
1197
1198
1199

1200
1201
1202
1203
1204
1205
1206
  Tcl_CallWhenDeleted(tclInterp, Th1DeleteProc, interp);
  Tcl_CreateObjCommand(tclInterp, "th1Eval", Th1EvalObjCmd, interp, NULL);
  Tcl_CreateObjCommand(tclInterp, "th1Expr", Th1ExprObjCmd, interp, NULL);
  /* If necessary, evaluate the custom Tcl setup script. */
  setup = tclContext->setup;
  if( setup && Tcl_EvalEx(tclInterp, setup, -1, 0)!=TCL_OK ){
    Th_ErrorMessage(interp,
        "Tcl setup script error:", Tcl_GetString(Tcl_GetObjResult(tclInterp)), -1);

    Tcl_DeleteInterp(tclInterp);
    tclContext->interp = tclInterp = 0;
    return TH_ERROR;
  }
  return TH_OK;
}








|
>







1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
  Tcl_CallWhenDeleted(tclInterp, Th1DeleteProc, interp);
  Tcl_CreateObjCommand(tclInterp, "th1Eval", Th1EvalObjCmd, interp, NULL);
  Tcl_CreateObjCommand(tclInterp, "th1Expr", Th1ExprObjCmd, interp, NULL);
  /* If necessary, evaluate the custom Tcl setup script. */
  setup = tclContext->setup;
  if( setup && Tcl_EvalEx(tclInterp, setup, -1, 0)!=TCL_OK ){
    Th_ErrorMessage(interp,
        "Tcl setup script error:",
        Tcl_GetString(Tcl_GetObjResult(tclInterp)), -1);
    Tcl_DeleteInterp(tclInterp);
    tclContext->interp = tclInterp = 0;
    return TH_ERROR;
  }
  return TH_OK;
}

Changes to src/timeline.c.
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

#define TIMELINE_FMT_ONELINE \
    "%h %c"
#define TIMELINE_FMT_MEDIUM \
    "Commit:   %h%nDate:     %d%nAuthor:   %a%nComment:  %c"
#define TIMELINE_FMT_FULL \
    "Commit:   %H%nDate:     %d%nAuthor:   %a%nComment:  %c%n"\
    "Branch:   %b%nTags:     %t%nPhase:    %p"                             
/*
** Add an appropriate tag to the output if "rid" is unpublished (private)
*/
#define UNPUB_TAG "<em>(unpublished)</em>"
void tag_private_status(int rid){
  if( content_is_private(rid) ){
    cgi_printf(" %s", UNPUB_TAG);







|







39
40
41
42
43
44
45
46
47
48
49
50
51
52
53

#define TIMELINE_FMT_ONELINE \
    "%h %c"
#define TIMELINE_FMT_MEDIUM \
    "Commit:   %h%nDate:     %d%nAuthor:   %a%nComment:  %c"
#define TIMELINE_FMT_FULL \
    "Commit:   %H%nDate:     %d%nAuthor:   %a%nComment:  %c%n"\
    "Branch:   %b%nTags:     %t%nPhase:    %p"
/*
** Add an appropriate tag to the output if "rid" is unpublished (private)
*/
#define UNPUB_TAG "<em>(unpublished)</em>"
void tag_private_status(int rid){
  if( content_is_private(rid) ){
    cgi_printf(" %s", UNPUB_TAG);
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
  db_bind_int(&q, "$rid", rid);
  res = db_step(&q)==SQLITE_ROW;
  db_reset(&q);
  return res;
}

/*
** Return the text of the unformatted 
** forum post given by the RID in the argument.
*/
static void forum_post_content_function(
 sqlite3_context *context,
 int argc,
 sqlite3_value **argv
){







|







151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
  db_bind_int(&q, "$rid", rid);
  res = db_step(&q)==SQLITE_ROW;
  db_reset(&q);
  return res;
}

/*
** Return the text of the unformatted
** forum post given by the RID in the argument.
*/
static void forum_post_content_function(
 sqlite3_context *context,
 int argc,
 sqlite3_value **argv
){
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
        int isClosed = 0;
        if( is_ticket(zTktid, &isClosed) && isClosed ){
          zExtraClass = " tktTlClosed";
        }else{
          zExtraClass = " tktTlOpen";
        }
        fossil_free(zTktid);
      }   
    }
    if( zType[0]=='e' && tagid ){
      if( bTimestampLinksToInfo ){
        char *zId;
        zId = db_text(0, "SELECT substr(tagname, 7) FROM tag WHERE tagid=%d",
                          tagid);
        zDateLink = href("%R/technote/%s",zId);







|







364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
        int isClosed = 0;
        if( is_ticket(zTktid, &isClosed) && isClosed ){
          zExtraClass = " tktTlClosed";
        }else{
          zExtraClass = " tktTlOpen";
        }
        fossil_free(zTktid);
      }
    }
    if( zType[0]=='e' && tagid ){
      if( bTimestampLinksToInfo ){
        char *zId;
        zId = db_text(0, "SELECT substr(tagname, 7) FROM tag WHERE tagid=%d",
                          tagid);
        zDateLink = href("%R/technote/%s",zId);
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
        cgi_printf(" tags:&nbsp;%h", zTagList);
      }
    }

    if( tmFlags & TIMELINE_SHOWRID ){
      int srcId = delta_source_rid(rid);
      if( srcId ){
        cgi_printf(" id:&nbsp;%z%d&larr;%d</a>", 
                   href("%R/deltachain/%d",rid), rid, srcId);
      }else{
        cgi_printf(" id:&nbsp;%z%d</a>",
                   href("%R/deltachain/%d",rid), rid);
      }
    }
    tag_private_status(rid);







|







674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
        cgi_printf(" tags:&nbsp;%h", zTagList);
      }
    }

    if( tmFlags & TIMELINE_SHOWRID ){
      int srcId = delta_source_rid(rid);
      if( srcId ){
        cgi_printf(" id:&nbsp;%z%d&larr;%d</a>",
                   href("%R/deltachain/%d",rid), rid, srcId);
      }else{
        cgi_printf(" id:&nbsp;%z%d</a>",
                   href("%R/deltachain/%d",rid), rid);
      }
    }
    tag_private_status(rid);
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
    }

    blob_appendf(&desc, " of %z%h</a>",
                   href("%R/info?name=%h", zCiName), zCiName);
    if( ridBackTo ){
      if( np==0 ){
        blob_reset(&desc);
        blob_appendf(&desc, 
                    "Check-in %z%h</a> only (%z%h</a> is not an ancestor)",
                     href("%R/info?name=%h",zCiName), zCiName,
                     href("%R/info?name=%h",zBackTo), zBackTo);
      }else{
        blob_appendf(&desc, " back to %z%h</a>",
                     href("%R/info?name=%h",zBackTo), zBackTo);
        if( ridFwdTo && zFwdTo ){
          blob_appendf(&desc, " and up to %z%h</a>",
                     href("%R/info?name=%h",zFwdTo), zFwdTo);
        }
      }
    }else if( ridFwdTo ){
      if( nd==0 ){
        blob_reset(&desc);
        blob_appendf(&desc, 
                    "Check-in %z%h</a> only (%z%h</a> is not an descendant)",
                     href("%R/info?name=%h",zCiName), zCiName,
                     href("%R/info?name=%h",zFwdTo), zFwdTo);
      }else{
        blob_appendf(&desc, " up to %z%h</a>",
                     href("%R/info?name=%h",zFwdTo), zFwdTo);
      }







|














|







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
    }

    blob_appendf(&desc, " of %z%h</a>",
                   href("%R/info?name=%h", zCiName), zCiName);
    if( ridBackTo ){
      if( np==0 ){
        blob_reset(&desc);
        blob_appendf(&desc,
                    "Check-in %z%h</a> only (%z%h</a> is not an ancestor)",
                     href("%R/info?name=%h",zCiName), zCiName,
                     href("%R/info?name=%h",zBackTo), zBackTo);
      }else{
        blob_appendf(&desc, " back to %z%h</a>",
                     href("%R/info?name=%h",zBackTo), zBackTo);
        if( ridFwdTo && zFwdTo ){
          blob_appendf(&desc, " and up to %z%h</a>",
                     href("%R/info?name=%h",zFwdTo), zFwdTo);
        }
      }
    }else if( ridFwdTo ){
      if( nd==0 ){
        blob_reset(&desc);
        blob_appendf(&desc,
                    "Check-in %z%h</a> only (%z%h</a> is not an descendant)",
                     href("%R/info?name=%h",zCiName), zCiName,
                     href("%R/info?name=%h",zFwdTo), zFwdTo);
      }else{
        blob_appendf(&desc, " up to %z%h</a>",
                     href("%R/info?name=%h",zFwdTo), zFwdTo);
      }
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
**    6.  mtime
**    7.  branch
**    8.  event-type: 'ci', 'w', 't', 'f', and so forth.
**    9.  comment
**   10.  user
**   11.  tags
*/
void print_timeline(Stmt *q, int nLimit, int width, const char *zFormat, int verboseFlag){

  int nAbsLimit = (nLimit >= 0) ? nLimit : -nLimit;
  int nLine = 0;
  int nEntry = 0;
  char zPrevDate[20];
  const char *zCurrentUuid = 0;
  int fchngQueryInit = 0;     /* True if fchngQuery is initialized */
  Stmt fchngQuery;            /* Query for file changes on check-ins */
  int rc;
  /* True: separate entries with a newline after file listing */

  int bVerboseNL = (zFormat && (fossil_strcmp(zFormat, TIMELINE_FMT_ONELINE)!=0));
  /* True: separate entries with a newline even with no file listing */
  int bNoVerboseNL = (zFormat && (fossil_strcmp(zFormat, TIMELINE_FMT_MEDIUM)==0 ||

                      fossil_strcmp(zFormat, TIMELINE_FMT_FULL)==0));

  zPrevDate[0] = 0;
  if( g.localOpen ){
    int rid = db_lget_int("checkout", 0);
    zCurrentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
  }








|
>









>
|

|
>
|







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
**    6.  mtime
**    7.  branch
**    8.  event-type: 'ci', 'w', 't', 'f', and so forth.
**    9.  comment
**   10.  user
**   11.  tags
*/
void print_timeline(Stmt *q, int nLimit, int width, const char *zFormat,
                    int verboseFlag){
  int nAbsLimit = (nLimit >= 0) ? nLimit : -nLimit;
  int nLine = 0;
  int nEntry = 0;
  char zPrevDate[20];
  const char *zCurrentUuid = 0;
  int fchngQueryInit = 0;     /* True if fchngQuery is initialized */
  Stmt fchngQuery;            /* Query for file changes on check-ins */
  int rc;
  /* True: separate entries with a newline after file listing */
  int bVerboseNL = (zFormat &&
                    (fossil_strcmp(zFormat, TIMELINE_FMT_ONELINE)!=0));
  /* True: separate entries with a newline even with no file listing */
  int bNoVerboseNL = (zFormat &&
                      (fossil_strcmp(zFormat, TIMELINE_FMT_MEDIUM)==0 ||
                       fossil_strcmp(zFormat, TIMELINE_FMT_FULL)==0));

  zPrevDate[0] = 0;
  if( g.localOpen ){
    int rid = db_lget_int("checkout", 0);
    zCurrentUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
  }

3120
3121
3122
3123
3124
3125
3126
3127
3128

3129
3130
3131
3132
3133
3134
3135

    if( zFormat ){
      char *zEntry;
      int nEntryLine = 0;
      if( nChild==0 ){
        sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*LEAF* ");
      }
      zEntry = timeline_entry_subst(zFormat, &nEntryLine, zId, zDate, zUserShort,
                                    zComShort, zBranch, zTags, zPrefix);

      nLine += nEntryLine;
      fossil_print("%s\n", zEntry);
      fossil_free(zEntry);
    }
    else{
      /* record another X lines */
      nLine += comment_print(zFree, zCom, 9, width, get_comment_format());







|
|
>







3123
3124
3125
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139

    if( zFormat ){
      char *zEntry;
      int nEntryLine = 0;
      if( nChild==0 ){
        sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*LEAF* ");
      }
      zEntry = timeline_entry_subst(zFormat, &nEntryLine, zId, zDate,
                                    zUserShort, zComShort, zBranch, zTags,
                                    zPrefix);
      nLine += nEntryLine;
      fossil_print("%s\n", zEntry);
      fossil_free(zEntry);
    }
    else{
      /* record another X lines */
      nLine += comment_print(zFree, zCom, 9, width, get_comment_format());
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
        nLine++; /* record another line */
      }
      db_reset(&fchngQuery);
      if( bVerboseNL ) fossil_print("\n");
    }else{
      if( bNoVerboseNL ) fossil_print("\n");
    }
    
    nEntry++; /* record another complete entry */
  }
  if( rc==SQLITE_DONE ){
    /* Did the underlying query actually have all entries? */
    if( nAbsLimit==0 ){
      fossil_print("+++ end of timeline (%d) +++\n", nEntry);
    }else{







|







3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
        nLine++; /* record another line */
      }
      db_reset(&fchngQuery);
      if( bVerboseNL ) fossil_print("\n");
    }else{
      if( bNoVerboseNL ) fossil_print("\n");
    }

    nEntry++; /* record another complete entry */
  }
  if( rc==SQLITE_DONE ){
    /* Did the underlying query actually have all entries? */
    if( nAbsLimit==0 ){
      fossil_print("+++ end of timeline (%d) +++\n", nEntry);
    }else{
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
    @   event.type
    @   , coalesce(ecomment,comment) AS comment0
    @   , coalesce(euser,user,'?') AS user0
    @   , (SELECT case when length(x)>0 then x else '' end
    @         FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
    @         FROM tag, tagxref
    @         WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
    @          AND tagxref.rid=blob.rid AND tagxref.tagtype>0)) AS tags    
    @ FROM tag CROSS JOIN event CROSS JOIN blob
    @      LEFT JOIN tagxref ON tagxref.tagid=tag.tagid
    @   AND tagxref.tagtype>0
    @   AND tagxref.rid=blob.rid
    @ WHERE blob.rid=event.objid
    @   AND tag.tagname='branch'
  ;







|







3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
    @   event.type
    @   , coalesce(ecomment,comment) AS comment0
    @   , coalesce(euser,user,'?') AS user0
    @   , (SELECT case when length(x)>0 then x else '' end
    @         FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
    @         FROM tag, tagxref
    @         WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
    @          AND tagxref.rid=blob.rid AND tagxref.tagtype>0)) AS tags
    @ FROM tag CROSS JOIN event CROSS JOIN blob
    @      LEFT JOIN tagxref ON tagxref.tagid=tag.tagid
    @   AND tagxref.tagtype>0
    @   AND tagxref.rid=blob.rid
    @ WHERE blob.rid=event.objid
    @   AND tag.tagname='branch'
  ;
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
** means UTC.
**
**
** Options:
**   -b|--branch BRANCH   Show only items on the branch named BRANCH
**   -c|--current-branch  Show only items on the current branch
**   -F|--format          Entry format. Values "oneline", "medium", and "full"
**                        get mapped to the full options below. Otherwise a 
**                        string which can contain these placeholders:
**                            %n  newline
**                            %%  a raw %
**                            %H  commit hash
**                            %h  abbreviated commit hash
**                            %a  author name
**                            %d  date
**                            %c  comment (NL, TAB replaced by space, LF deleted)
**                            %b  branch
**                            %t  tags
**                            %p  phase: zero or more of *CURRENT*, *MERGE*,
**                                      *FORK*, *UNPUBLISHED*, *LEAF*, *BRANCH*
**   --oneline            Show only short hash and comment for each entry
**   --medium             Medium-verbose entry formatting
**   --full               Extra verbose entry formatting







|







|







3274
3275
3276
3277
3278
3279
3280
3281
3282
3283
3284
3285
3286
3287
3288
3289
3290
3291
3292
3293
3294
3295
3296
** means UTC.
**
**
** Options:
**   -b|--branch BRANCH   Show only items on the branch named BRANCH
**   -c|--current-branch  Show only items on the current branch
**   -F|--format          Entry format. Values "oneline", "medium", and "full"
**                        get mapped to the full options below. Otherwise a
**                        string which can contain these placeholders:
**                            %n  newline
**                            %%  a raw %
**                            %H  commit hash
**                            %h  abbreviated commit hash
**                            %a  author name
**                            %d  date
**                            %c  comment (NL, TAB replaced by space, LF erased)
**                            %b  branch
**                            %t  tags
**                            %p  phase: zero or more of *CURRENT*, *MERGE*,
**                                      *FORK*, *UNPUBLISHED*, *LEAF*, *BRANCH*
**   --oneline            Show only short hash and comment for each entry
**   --medium             Medium-verbose entry formatting
**   --full               Extra verbose entry formatting
Changes to src/tkt.c.
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
    case SQLITE_CREATE_VIEW:
    case SQLITE_CREATE_TABLE: {
      if( sqlite3_stricmp(z2,"main")!=0
       && sqlite3_stricmp(z2,"repository")!=0
      ){
        goto ticket_schema_error;
      }
      if( sqlite3_strnicmp(z0,"ticket",6)!=0 
       && sqlite3_strnicmp(z0,"fx_",3)!=0
      ){
        goto ticket_schema_error;
      }
      break;
    }
    case SQLITE_DROP_INDEX:







|







555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
    case SQLITE_CREATE_VIEW:
    case SQLITE_CREATE_TABLE: {
      if( sqlite3_stricmp(z2,"main")!=0
       && sqlite3_stricmp(z2,"repository")!=0
      ){
        goto ticket_schema_error;
      }
      if( sqlite3_strnicmp(z0,"ticket",6)!=0
       && sqlite3_strnicmp(z0,"fx_",3)!=0
      ){
        goto ticket_schema_error;
      }
      break;
    }
    case SQLITE_DROP_INDEX:
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
}

/*
** WEBPAGE: tkttimeline
** URL: /tkttimeline/TICKETUUID
**
** Show the change history for a single ticket in timeline format.
** 
** Query parameters:
**
**     y=ci          Show only check-ins associated with the ticket
*/
void tkttimeline_page(void){
  char *zTitle;
  const char *zUuid;







|







1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
}

/*
** WEBPAGE: tkttimeline
** URL: /tkttimeline/TICKETUUID
**
** Show the change history for a single ticket in timeline format.
**
** Query parameters:
**
**     y=ci          Show only check-ins associated with the ticket
*/
void tkttimeline_page(void){
  char *zTitle;
  const char *zUuid;
Changes to src/unicode.c.
238
239
240
241
242
243
244
245

246
247
248
249
250
251
252
      iLo = iTest+1;
    }else{
      iHi = iTest-1;
    }
  }
  assert( key>=aDia[iRes] );
  if( bComplex==0 && (aChar[iRes] & 0x80) ) return c;
  return (c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : ((int)aChar[iRes] & 0x7F);

}


/*
** Return true if the argument interpreted as a unicode codepoint
** is a diacritical modifier character.
*/







|
>







238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
      iLo = iTest+1;
    }else{
      iHi = iTest-1;
    }
  }
  assert( key>=aDia[iRes] );
  if( bComplex==0 && (aChar[iRes] & 0x80) ) return c;
  return (c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c :
                                                     ((int)aChar[iRes] & 0x7F);
}


/*
** Return true if the argument interpreted as a unicode codepoint
** is a diacritical modifier character.
*/
Changes to src/unversioned.c.
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
  nCmd = (int)strlen(zCmd);
  if( zMtime==0 ){
    mtime = time(0);
  }else{
    mtime = db_int(0, "SELECT strftime('%%s',%Q)", zMtime);
    if( mtime<=0 ) fossil_fatal("bad timestamp: %Q", zMtime);
  }
  if( memcmp(zCmd, "add", nCmd)==0 ){
    const char *zError = 0;
    const char *zIn;
    const char *zAs;
    Blob file;
    int i;

    zAs = find_option("as",0,1);







|







306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
  nCmd = (int)strlen(zCmd);
  if( zMtime==0 ){
    mtime = time(0);
  }else{
    mtime = db_int(0, "SELECT strftime('%%s',%Q)", zMtime);
    if( mtime<=0 ) fossil_fatal("bad timestamp: %Q", zMtime);
  }
  if( strncmp(zCmd, "add", nCmd)==0 ){
    const char *zError = 0;
    const char *zIn;
    const char *zAs;
    Blob file;
    int i;

    zAs = find_option("as",0,1);
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
      }
      blob_init(&file,0,0);
      blob_read_from_file(&file, g.argv[i], ExtFILE);
      unversioned_write(zIn, &file, mtime);
      blob_reset(&file);
    }
    db_end_transaction(0);
  }else if( memcmp(zCmd, "cat", nCmd)==0 ){
    int i;
    verify_all_options();
    db_begin_transaction();
    for(i=3; i<g.argc; i++){
      Blob content;
      if( unversioned_content(g.argv[i], &content)!=0 ){
        blob_write_to_file(&content, "-");
      }
      blob_reset(&content);
    }
    db_end_transaction(0);
  }else if( memcmp(zCmd, "edit", nCmd)==0 ){
    const char *zEditor;    /* Name of the text-editor command */
    const char *zTFile;     /* Temporary file */
    const char *zUVFile;    /* Name of the unversioned file */
    char *zCmd;             /* Command to run the text editor */
    Blob content;           /* Content of the unversioned file */

    verify_all_options();







|











|







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
      }
      blob_init(&file,0,0);
      blob_read_from_file(&file, g.argv[i], ExtFILE);
      unversioned_write(zIn, &file, mtime);
      blob_reset(&file);
    }
    db_end_transaction(0);
  }else if( strncmp(zCmd, "cat", nCmd)==0 ){
    int i;
    verify_all_options();
    db_begin_transaction();
    for(i=3; i<g.argc; i++){
      Blob content;
      if( unversioned_content(g.argv[i], &content)!=0 ){
        blob_write_to_file(&content, "-");
      }
      blob_reset(&content);
    }
    db_end_transaction(0);
  }else if( strncmp(zCmd, "edit", nCmd)==0 ){
    const char *zEditor;    /* Name of the text-editor command */
    const char *zTFile;     /* Temporary file */
    const char *zUVFile;    /* Name of the unversioned file */
    char *zCmd;             /* Command to run the text editor */
    Blob content;           /* Content of the unversioned file */

    verify_all_options();
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
    blob_to_lf_only(&content);
#endif
    file_delete(zTFile);
    if( zMtime==0 ) mtime = time(0);
    unversioned_write(zUVFile, &content, mtime);
    db_end_transaction(0);
    blob_reset(&content);
  }else if( memcmp(zCmd, "export", nCmd)==0 ){
    Blob content;
    verify_all_options();
    if( g.argc!=5 ) usage("export UVFILE OUTPUT");
    if( unversioned_content(g.argv[3], &content)==0 ){
      fossil_fatal("no such uv-file: %Q", g.argv[3]);
    }
    blob_write_to_file(&content, g.argv[4]);
    blob_reset(&content);
  }else if( memcmp(zCmd, "hash", nCmd)==0 ){  /* undocumented */
    /* Show the hash value used during uv sync */
    int debugFlag = find_option("debug",0,0)!=0;
    fossil_print("%s\n", unversioned_content_hash(debugFlag));
  }else if( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){
    Stmt q;
    int allFlag = find_option("all","a",0)!=0;
    int longFlag = find_option("l",0,0)!=0 || (nCmd>1 && zCmd[1]=='i');
    char *zPattern = sqlite3_mprintf("true");
    const char *zGlob;
    zGlob = find_option("glob",0,1);
    if( zGlob ){







|








|



|







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
    blob_to_lf_only(&content);
#endif
    file_delete(zTFile);
    if( zMtime==0 ) mtime = time(0);
    unversioned_write(zUVFile, &content, mtime);
    db_end_transaction(0);
    blob_reset(&content);
  }else if( strncmp(zCmd, "export", nCmd)==0 ){
    Blob content;
    verify_all_options();
    if( g.argc!=5 ) usage("export UVFILE OUTPUT");
    if( unversioned_content(g.argv[3], &content)==0 ){
      fossil_fatal("no such uv-file: %Q", g.argv[3]);
    }
    blob_write_to_file(&content, g.argv[4]);
    blob_reset(&content);
  }else if( strncmp(zCmd, "hash", nCmd)==0 ){  /* undocumented */
    /* Show the hash value used during uv sync */
    int debugFlag = find_option("debug",0,0)!=0;
    fossil_print("%s\n", unversioned_content_hash(debugFlag));
  }else if( strncmp(zCmd, "list", nCmd)==0 || strncmp(zCmd, "ls", nCmd)==0 ){
    Stmt q;
    int allFlag = find_option("all","a",0)!=0;
    int longFlag = find_option("l",0,0)!=0 || (nCmd>1 && zCmd[1]=='i');
    char *zPattern = sqlite3_mprintf("true");
    const char *zGlob;
    zGlob = find_option("glob",0,1);
    if( zGlob ){
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
           db_column_text(&q,4),
           zNoContent
        );
      }
    }
    db_finalize(&q);
    sqlite3_free(zPattern);
  }else if( memcmp(zCmd, "revert", nCmd)==0 ){
    unsigned syncFlags =
        unversioned_sync_flags(SYNC_UNVERSIONED|SYNC_UV_REVERT);
    g.argv[1] = "sync";
    g.argv[2] = "--uv-noop";
    sync_unversioned(syncFlags);
  }else if( memcmp(zCmd, "remove", nCmd)==0 || memcmp(zCmd, "rm", nCmd)==0
         || memcmp(zCmd, "delete", nCmd)==0 ){
    int i;
    const char *zGlob;
    db_begin_transaction();
    while( (zGlob = find_option("glob",0,1))!=0 ){
      db_multi_exec(
        "UPDATE unversioned"
        "   SET hash=NULL, content=NULL, mtime=%lld, sz=0 WHERE name GLOB %Q",







|





|
|







462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
           db_column_text(&q,4),
           zNoContent
        );
      }
    }
    db_finalize(&q);
    sqlite3_free(zPattern);
  }else if( strncmp(zCmd, "revert", nCmd)==0 ){
    unsigned syncFlags =
        unversioned_sync_flags(SYNC_UNVERSIONED|SYNC_UV_REVERT);
    g.argv[1] = "sync";
    g.argv[2] = "--uv-noop";
    sync_unversioned(syncFlags);
  }else if( strncmp(zCmd, "remove", nCmd)==0 || strncmp(zCmd, "rm", nCmd)==0
         || strncmp(zCmd, "delete", nCmd)==0 ){
    int i;
    const char *zGlob;
    db_begin_transaction();
    while( (zGlob = find_option("glob",0,1))!=0 ){
      db_multi_exec(
        "UPDATE unversioned"
        "   SET hash=NULL, content=NULL, mtime=%lld, sz=0 WHERE name GLOB %Q",
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
        "UPDATE unversioned"
        "   SET hash=NULL, content=NULL, mtime=%lld, sz=0 WHERE name=%Q",
        mtime, g.argv[i]
      );
    }
    db_unset("uv-hash", 0);
    db_end_transaction(0);
  }else if( memcmp(zCmd,"sync",nCmd)==0 ){
    unsigned syncFlags = unversioned_sync_flags(SYNC_UNVERSIONED);
    g.argv[1] = "sync";
    g.argv[2] = "--uv-noop";
    sync_unversioned(syncFlags);
  }else if( memcmp(zCmd, "touch", nCmd)==0 ){
    int i;
    verify_all_options();
    db_begin_transaction();
    for(i=3; i<g.argc; i++){
      db_multi_exec(
        "UPDATE unversioned SET mtime=%lld WHERE name=%Q",
        mtime, g.argv[i]







|




|







497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
        "UPDATE unversioned"
        "   SET hash=NULL, content=NULL, mtime=%lld, sz=0 WHERE name=%Q",
        mtime, g.argv[i]
      );
    }
    db_unset("uv-hash", 0);
    db_end_transaction(0);
  }else if( strncmp(zCmd,"sync",nCmd)==0 ){
    unsigned syncFlags = unversioned_sync_flags(SYNC_UNVERSIONED);
    g.argv[1] = "sync";
    g.argv[2] = "--uv-noop";
    sync_unversioned(syncFlags);
  }else if( strncmp(zCmd, "touch", nCmd)==0 ){
    int i;
    verify_all_options();
    db_begin_transaction();
    for(i=3; i<g.argc; i++){
      db_multi_exec(
        "UPDATE unversioned SET mtime=%lld WHERE name=%Q",
        mtime, g.argv[i]
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
  );
  iNow = db_int64(0, "SELECT strftime('%%s','now');");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zName = db_column_text(&q, 0);
    sqlite3_int64 mtime = db_column_int(&q, 1);
    const char *zHash = db_column_text(&q, 2);
    int isDeleted = zHash==0;

    int fullSize = db_column_int(&q, 3);
    char *zAge = human_readable_age((iNow - mtime)/86400.0);
    const char *zLogin = db_column_text(&q, 4);
    int rcvid = db_column_int(&q,5);


    if( zLogin==0 ) zLogin = "";
    if( (n++)==0 ){
      style_table_sorter();
      @ <div class="uvlist">
      @ <table cellpadding="2" cellspacing="0" border="1" class='sortable' \
      @  data-column-types='tkKttn' data-init-sort='1'>
      @ <thead><tr>
      @   <th> Name
      @   <th> Age
      @   <th> Size
      @   <th> User
      @   <th> Hash

      if( g.perm.Admin ){
        @ <th> rcvid
      }
      @ </tr></thead>
      @ <tbody>
    }
    @ <tr>







>




>
>












>







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
  );
  iNow = db_int64(0, "SELECT strftime('%%s','now');");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zName = db_column_text(&q, 0);
    sqlite3_int64 mtime = db_column_int(&q, 1);
    const char *zHash = db_column_text(&q, 2);
    int isDeleted = zHash==0;
    const char *zAlgo;
    int fullSize = db_column_int(&q, 3);
    char *zAge = human_readable_age((iNow - mtime)/86400.0);
    const char *zLogin = db_column_text(&q, 4);
    int rcvid = db_column_int(&q,5);
    if( isDeleted ) zAlgo = "deleted";
    else zAlgo = hname_alg(strlen(zHash));
    if( zLogin==0 ) zLogin = "";
    if( (n++)==0 ){
      style_table_sorter();
      @ <div class="uvlist">
      @ <table cellpadding="2" cellspacing="0" border="1" class='sortable' \
      @  data-column-types='tkKttn' data-init-sort='1'>
      @ <thead><tr>
      @   <th> Name
      @   <th> Age
      @   <th> Size
      @   <th> User
      @   <th> Hash
      @   <th> Algo
      if( g.perm.Admin ){
        @ <th> rcvid
      }
      @ </tr></thead>
      @ <tbody>
    }
    @ <tr>
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
      iTotalSz += fullSize;
      cnt++;
      @ <td> <a href='%R/uv/%T(zName)'>%h(zName)</a> </td>
    }
    @ <td data-sortkey='%016llx(-mtime)'> %s(zAge) </td>
    @ <td data-sortkey='%08x(fullSize)'> %s(zSzName) </td>
    @ <td> %h(zLogin) </td>
    @ <td> %h(zHash) </td>

    if( g.perm.Admin ){
      if( rcvid ){
        @ <td> <a href="%R/rcvfrom?rcvid=%d(rcvid)">%d(rcvid)</a>
      }else{
        @ <td>
      }
    }
    @ </tr>
    fossil_free(zAge);
  }
  db_finalize(&q);
  if( n ){
    approxSizeName(sizeof(zSzName), zSzName, iTotalSz);
    @ </tbody>
    @ <tfoot><tr><td><b>Total for %d(cnt) files</b><td><td>%s(zSzName)
    @ <td><td>
    if( g.perm.Admin ){
      @ <td>
    }

    @ </tfoot>
    @ </table></div>
  }else{
    @ No unversioned files on this server.
  }
  style_finish_page();
}







|
>



















>







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
      iTotalSz += fullSize;
      cnt++;
      @ <td> <a href='%R/uv/%T(zName)'>%h(zName)</a> </td>
    }
    @ <td data-sortkey='%016llx(-mtime)'> %s(zAge) </td>
    @ <td data-sortkey='%08x(fullSize)'> %s(zSzName) </td>
    @ <td> %h(zLogin) </td>
    @ <td><code> %h(zHash) </code></td>
    @ <td> %s(zAlgo) </td>
    if( g.perm.Admin ){
      if( rcvid ){
        @ <td> <a href="%R/rcvfrom?rcvid=%d(rcvid)">%d(rcvid)</a>
      }else{
        @ <td>
      }
    }
    @ </tr>
    fossil_free(zAge);
  }
  db_finalize(&q);
  if( n ){
    approxSizeName(sizeof(zSzName), zSzName, iTotalSz);
    @ </tbody>
    @ <tfoot><tr><td><b>Total for %d(cnt) files</b><td><td>%s(zSzName)
    @ <td><td>
    if( g.perm.Admin ){
      @ <td>
    }
    @ <td>
    @ </tfoot>
    @ </table></div>
  }else{
    @ No unversioned files on this server.
  }
  style_finish_page();
}
Changes to src/update.c.
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
  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" : "");
  }








|







565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
  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" : "");
  }

Changes to src/url.c.
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
**      path        Path name for HTTP or HTTPS.
**      user        Userid.
**      passwd      Password.
**      hostname    HOST:PORT or just HOST if port is the default.
**      canonical   The URL in canonical form, omitting the password
**
** If URL_USECONFIG is set and zUrl is NULL or "default", then parse the
** URL stored in last-sync-url and last-sync-pw of the CONFIG table.  Or if 
** URL_USE_PARENT is also set, then use parent-project-url and
** parent-project-pw from the CONFIG table instead of last-sync-url
** and last-sync-pw.
**
** If URL_USE_CONFIG is set and zUrl is a symbolic name, then look up
** the URL in sync-url:%Q and sync-pw:%Q elements of the CONFIG table where
** %Q is the symbolic name.







|







87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
**      path        Path name for HTTP or HTTPS.
**      user        Userid.
**      passwd      Password.
**      hostname    HOST:PORT or just HOST if port is the default.
**      canonical   The URL in canonical form, omitting the password
**
** If URL_USECONFIG is set and zUrl is NULL or "default", then parse the
** URL stored in last-sync-url and last-sync-pw of the CONFIG table.  Or if
** URL_USE_PARENT is also set, then use parent-project-url and
** parent-project-pw from the CONFIG table instead of last-sync-url
** and last-sync-pw.
**
** If URL_USE_CONFIG is set and zUrl is a symbolic name, then look up
** the URL in sync-url:%Q and sync-pw:%Q elements of the CONFIG table where
** %Q is the symbolic name.
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
    free(zFile);
    zFile = 0;
    pUrlData->protocol = "file";
    pUrlData->path = mprintf("");
    pUrlData->name = mprintf("%b", &cfile);
    pUrlData->canonical = mprintf("file://%T", pUrlData->name);
    blob_reset(&cfile);
  }else if( pUrlData->user!=0 && pUrlData->passwd==0 
         && (urlFlags & URL_PROMPT_PW)!=0 ){
    url_prompt_for_password_local(pUrlData);
  }else if( pUrlData->user!=0 && ( urlFlags & URL_ASK_REMEMBER_PW ) ){
    if( isatty(fileno(stdin)) && ( urlFlags & URL_REMEMBER_PW )==0 ){
      if( save_password_prompt(pUrlData->passwd) ){
        pUrlData->flags = urlFlags |= URL_REMEMBER_PW;
      }else{







|







315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
    free(zFile);
    zFile = 0;
    pUrlData->protocol = "file";
    pUrlData->path = mprintf("");
    pUrlData->name = mprintf("%b", &cfile);
    pUrlData->canonical = mprintf("file://%T", pUrlData->name);
    blob_reset(&cfile);
  }else if( pUrlData->user!=0 && pUrlData->passwd==0
         && (urlFlags & URL_PROMPT_PW)!=0 ){
    url_prompt_for_password_local(pUrlData);
  }else if( pUrlData->user!=0 && ( urlFlags & URL_ASK_REMEMBER_PW ) ){
    if( isatty(fileno(stdin)) && ( urlFlags & URL_REMEMBER_PW )==0 ){
      if( save_password_prompt(pUrlData->passwd) ){
        pUrlData->flags = urlFlags |= URL_REMEMBER_PW;
      }else{
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
** Given a URL for a remote repository clone point, try to come up with a
** reasonable basename of a local clone of that repository.
**
**    *  If the URL has a path, use the tail of the path, with any suffix
**       elided.
**
**    *  If the URL is just a domain name, without a path, then use the
**       first element of the domain name, except skip over "www." if 
**       present and if there is a ".com" or ".org" or similar suffix.
**
** The string returned is obtained from fossil_malloc().  NULL might be
** returned if there is an error.
*/
char *url_to_repo_basename(const char *zUrl){
  const char *zTail = 0;







|







789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
** Given a URL for a remote repository clone point, try to come up with a
** reasonable basename of a local clone of that repository.
**
**    *  If the URL has a path, use the tail of the path, with any suffix
**       elided.
**
**    *  If the URL is just a domain name, without a path, then use the
**       first element of the domain name, except skip over "www." if
**       present and if there is a ".com" or ".org" or similar suffix.
**
** The string returned is obtained from fossil_malloc().  NULL might be
** returned if there is an error.
*/
char *url_to_repo_basename(const char *zUrl){
  const char *zTail = 0;
Changes to src/user.c.
397
398
399
400
401
402
403
404

405
406
407
408
409
410
411
      }
      if( g.localOpen ){
        db_lset("default-user", g.argv[3]);
      }else{
        db_set("default-user", g.argv[3], 0);
      }
    }
  }else if(( n>=2 && strncmp(g.argv[2],"list",n)==0 ) || ( n>=2 && strncmp(g.argv[2],"ls",n)==0 )){

    Stmt q;
    db_prepare(&q, "SELECT login, info FROM user ORDER BY login");
    while( db_step(&q)==SQLITE_ROW ){
      fossil_print("%-12s %s\n", db_column_text(&q, 0), db_column_text(&q, 1));
    }
    db_finalize(&q);
  }else if( n>=2 && strncmp(g.argv[2],"password",2)==0 ){







|
>







397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
      }
      if( g.localOpen ){
        db_lset("default-user", g.argv[3]);
      }else{
        db_set("default-user", g.argv[3], 0);
      }
    }
  }else if(( n>=2 && strncmp(g.argv[2],"list",n)==0 ) ||
           ( n>=2 && strncmp(g.argv[2],"ls",n)==0 )){
    Stmt q;
    db_prepare(&q, "SELECT login, info FROM user ORDER BY login");
    while( db_step(&q)==SQLITE_ROW ){
      fossil_print("%-12s %s\n", db_column_text(&q, 0), db_column_text(&q, 1));
    }
    db_finalize(&q);
  }else if( n>=2 && strncmp(g.argv[2],"password",2)==0 ){
Changes to src/vfile.c.
408
409
410
411
412
413
414

415
416
417
418
419
420
421
422
     "original",
     "output",
  };
  int i, j, n;

  if( sqlite3_strglob("ci-comment-????????????.txt", zName)==0 ) return 1;
  for(; zName[0]!=0; zName++){

    if( zName[0]=='/' && sqlite3_strglob("/ci-comment-????????????.txt", zName)==0 ){
      return 1;
    }
    if( zName[0]!='-' ) continue;
    for(i=0; i<count(azTemp); i++){
      n = (int)strlen(azTemp[i]);
      if( memcmp(azTemp[i], zName+1, n) ) continue;
      if( zName[n+1]==0 ) return 1;







>
|







408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
     "original",
     "output",
  };
  int i, j, n;

  if( sqlite3_strglob("ci-comment-????????????.txt", zName)==0 ) return 1;
  for(; zName[0]!=0; zName++){
    if( zName[0]=='/'
        && sqlite3_strglob("/ci-comment-????????????.txt", zName)==0 ){
      return 1;
    }
    if( zName[0]!='-' ) continue;
    for(i=0; i<count(azTemp); i++){
      n = (int)strlen(azTemp[i]);
      if( memcmp(azTemp[i], zName+1, n) ) continue;
      if( zName[n+1]==0 ) return 1;
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
          md5sum_step_text(" 0\n", -1);
          continue;
        }
        fseek(in, 0L, SEEK_END);
        sqlite3_snprintf(sizeof(zBuf), zBuf, " %ld\n", ftell(in));
        fseek(in, 0L, SEEK_SET);
        md5sum_step_text(zBuf, -1);
        /*printf("%s %s %s",md5sum_current_state(),zName,zBuf); fflush(stdout);*/
        for(;;){
          int n;
          n = fread(zBuf, 1, sizeof(zBuf), in);
          if( n<=0 ) break;
          md5sum_step_text(zBuf, n);
        }
        fclose(in);







|







753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
          md5sum_step_text(" 0\n", -1);
          continue;
        }
        fseek(in, 0L, SEEK_END);
        sqlite3_snprintf(sizeof(zBuf), zBuf, " %ld\n", ftell(in));
        fseek(in, 0L, SEEK_SET);
        md5sum_step_text(zBuf, -1);
        /*printf("%s %s %s",md5sum_current_state(),zName,zBuf);fflush(stdout);*/
        for(;;){
          int n;
          n = fread(zBuf, 1, sizeof(zBuf), in);
          if( n<=0 ) break;
          md5sum_step_text(zBuf, n);
        }
        fclose(in);
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059

  /* Add RID values for merged-in files */
  db_multi_exec(
    "INSERT OR IGNORE INTO idMap(oldrid, newrid)"
    " SELECT vfile.mrid, blob.rid FROM vfile, blob"
    "  WHERE blob.uuid=vfile.mhash;"
  );
  
  if( dryRun ){
    Stmt q;
    db_prepare(&q, "SELECT oldrid, newrid, blob.uuid"
                   "  FROM idMap, blob WHERE blob.rid=idMap.newrid");
    while( db_step(&q)==SQLITE_ROW ){
      fossil_print("%8d -> %8d  %.25s\n", 
         db_column_int(&q,0),
         db_column_int(&q,1),
         db_column_text(&q,2));
    }
    db_finalize(&q);
  }








|





|







1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060

  /* Add RID values for merged-in files */
  db_multi_exec(
    "INSERT OR IGNORE INTO idMap(oldrid, newrid)"
    " SELECT vfile.mrid, blob.rid FROM vfile, blob"
    "  WHERE blob.uuid=vfile.mhash;"
  );

  if( dryRun ){
    Stmt q;
    db_prepare(&q, "SELECT oldrid, newrid, blob.uuid"
                   "  FROM idMap, blob WHERE blob.rid=idMap.newrid");
    while( db_step(&q)==SQLITE_ROW ){
      fossil_print("%8d -> %8d  %.25s\n",
         db_column_int(&q,0),
         db_column_int(&q,1),
         db_column_text(&q,2));
    }
    db_finalize(&q);
  }

1067
1068
1069
1070
1071
1072
1073
1074







1075
1076
1077
1078
1079
1080
1081
     "  UNION SELECT %d"
     ")"
     "SELECT group_concat(x,' ') FROM allrid"
     " WHERE x<>0 AND x NOT IN (SELECT oldrid FROM idMap);",
     oldVid
  );
  if( zUnresolved[0] ){
    fossil_fatal("Unresolved RID values: %s\n", zUnresolved);







  }

  /* Make the changes to the VFILE and VMERGE tables */
  if( !dryRun ){
    db_multi_exec(
      "UPDATE vfile"
      "   SET rid=(SELECT newrid FROM idMap WHERE oldrid=vfile.rid)"







|
>
>
>
>
>
>
>







1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
     "  UNION SELECT %d"
     ")"
     "SELECT group_concat(x,' ') FROM allrid"
     " WHERE x<>0 AND x NOT IN (SELECT oldrid FROM idMap);",
     oldVid
  );
  if( zUnresolved[0] ){
    fossil_fatal("Unresolved RID values: %s\n"
        "\n"
        "Local check-out database is out of sync with repository file:\n"
        "\n"
        "    %s\n"
        "\n"
        "Has the repository file been replaced?\n",
        zUnresolved, db_repository_filename());
  }

  /* Make the changes to the VFILE and VMERGE tables */
  if( !dryRun ){
    db_multi_exec(
      "UPDATE vfile"
      "   SET rid=(SELECT newrid FROM idMap WHERE oldrid=vfile.rid)"
Changes to src/wiki.c.
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
}
int wiki_tagid2(const char *zPrefix, const char *zPageName){
  return db_int(0, "SELECT tagid FROM tag WHERE tagname='wiki-%q/%q'",
                zPrefix, zPageName);
}

/*
** Return the RID of the next or previous version of a wiki page.  
** Return 0 if rid is the last/first version.
*/
int wiki_next(int tagid, double mtime){
  return db_int(0,
     "SELECT srcid FROM tagxref"
     " WHERE tagid=%d AND mtime>%.16g"
     " ORDER BY mtime ASC LIMIT 1",







|







83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
}
int wiki_tagid2(const char *zPrefix, const char *zPageName){
  return db_int(0, "SELECT tagid FROM tag WHERE tagname='wiki-%q/%q'",
                zPrefix, zPageName);
}

/*
** Return the RID of the next or previous version of a wiki page.
** Return 0 if rid is the last/first version.
*/
int wiki_next(int tagid, double mtime){
  return db_int(0,
     "SELECT srcid FROM tagxref"
     " WHERE tagid=%d AND mtime>%.16g"
     " ORDER BY mtime ASC LIMIT 1",
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
/*
** Figure out what type of wiki page we are dealing with.
*/
int wiki_page_type(const char *zPageName){
  if( db_get_boolean("wiki-about",1)==0 ){
    return WIKITYPE_NORMAL;
  }else
  if( sqlite3_strglob("checkin/*", zPageName)==0 
   && db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8)
  ){
    return WIKITYPE_CHECKIN;
  }else
  if( sqlite3_strglob("branch/*", zPageName)==0 ){
    return WIKITYPE_BRANCH;
  }else







|







411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
/*
** Figure out what type of wiki page we are dealing with.
*/
int wiki_page_type(const char *zPageName){
  if( db_get_boolean("wiki-about",1)==0 ){
    return WIKITYPE_NORMAL;
  }else
  if( sqlite3_strglob("checkin/*", zPageName)==0
   && db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8)
  ){
    return WIKITYPE_CHECKIN;
  }else
  if( sqlite3_strglob("branch/*", zPageName)==0 ){
    return WIKITYPE_BRANCH;
  }else
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459

/*
** Add an appropriate style_header() for either the /wiki or /wikiedit page
** for zPageName.  zExtra is an empty string for /wiki but has the text
** "Edit: " for /wikiedit.
**
** If the page is /wiki and the page is one of the special times (check-in,
** branch, or tag) and the "p" query parameter is omitted, then do a 
** redirect to the display of the check-in, branch, or tag rather than
** continuing to the plain wiki display.
*/
static int wiki_page_header(
  int eType,                /* Page type.  Might be WIKITYPE_UNKNOWN */
  const char *zPageName,    /* Name of the page */
  const char *zExtra        /* Extra prefix text on the page header */







|







445
446
447
448
449
450
451
452
453
454
455
456
457
458
459

/*
** Add an appropriate style_header() for either the /wiki or /wikiedit page
** for zPageName.  zExtra is an empty string for /wiki but has the text
** "Edit: " for /wikiedit.
**
** If the page is /wiki and the page is one of the special times (check-in,
** branch, or tag) and the "p" query parameter is omitted, then do a
** redirect to the display of the check-in, branch, or tag rather than
** continuing to the plain wiki display.
*/
static int wiki_page_header(
  int eType,                /* Page type.  Might be WIKITYPE_UNKNOWN */
  const char *zPageName,    /* Name of the page */
  const char *zExtra        /* Extra prefix text on the page header */
467
468
469
470
471
472
473
474

475
476
477
478
479
480
481
    }
    case WIKITYPE_CHECKIN: {
      zPageName += 8;
      if( zExtra[0]==0 && !P("p") ){
        cgi_redirectf("%R/info/%s",zPageName);
      }else{
        style_header("Notes About Check-in %S", zPageName);
        style_submenu_element("Check-in Timeline","%R/timeline?f=%s", zPageName);

        style_submenu_element("Check-in Info","%R/info/%s", zPageName);
      }
      break;
    }
    case WIKITYPE_BRANCH: {
      zPageName += 7;
      if( zExtra[0]==0 && !P("p") ){







|
>







467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
    }
    case WIKITYPE_CHECKIN: {
      zPageName += 8;
      if( zExtra[0]==0 && !P("p") ){
        cgi_redirectf("%R/info/%s",zPageName);
      }else{
        style_header("Notes About Check-in %S", zPageName);
        style_submenu_element("Check-in Timeline","%R/timeline?f=%s",
                              zPageName);
        style_submenu_element("Check-in Info","%R/info/%s", zPageName);
      }
      break;
    }
    case WIKITYPE_BRANCH: {
      zPageName += 7;
      if( zExtra[0]==0 && !P("p") ){
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
** Note that the sandbox is a special case: it is a pseudo-page with
** no rid and the /wikiajax API does not allow anyone to actually save
** a sandbox page, but it is reported as writable here (with rid 0).
*/
static int wiki_ajax_can_write(const char *zPageName, int * pRid){
  int rid = 0;
  const char * zErr = 0;
 
  if(pRid) *pRid = 0;
  if(!zPageName || !*zPageName
     || !wiki_name_is_wellformed((unsigned const char *)zPageName)){
    zErr = "Invalid page name.";
  }else if(is_sandbox(zPageName)){
    return 1;
  }else{







|







742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
** Note that the sandbox is a special case: it is a pseudo-page with
** no rid and the /wikiajax API does not allow anyone to actually save
** a sandbox page, but it is reported as writable here (with rid 0).
*/
static int wiki_ajax_can_write(const char *zPageName, int * pRid){
  int rid = 0;
  const char * zErr = 0;

  if(pRid) *pRid = 0;
  if(!zPageName || !*zPageName
     || !wiki_name_is_wellformed((unsigned const char *)zPageName)){
    zErr = "Invalid page name.";
  }else if(is_sandbox(zPageName)){
    return 1;
  }else{
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
    }else if(!rid && !g.perm.NewWiki){
      zErr = "Requires new-wiki permissions.";
    }else{
      zErr = "Cannot happen! Please report this as a bug.";
    }
  }
  ajax_route_error(403, "%s", zErr);
  return 0;  
}


/*
** Emits an array of attachment info records for the given wiki page
** artifact.
**







|







765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
    }else if(!rid && !g.perm.NewWiki){
      zErr = "Requires new-wiki permissions.";
    }else{
      zErr = "Cannot happen! Please report this as a bug.";
    }
  }
  ajax_route_error(403, "%s", zErr);
  return 0;
}


/*
** Emits an array of attachment info records for the given wiki page
** artifact.
**
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
**
** Responds with JSON. On error, an object in the form documented by
** ajax_route_error(). On success, an object in the form documented
** for wiki_ajax_emit_page_object().
*/
static void wiki_ajax_route_fetch(void){
  const char * zPageName = P("page");
  
  if( zPageName==0 || zPageName[0]==0 ){
    ajax_route_error(400,"Missing page name.");
    return;
  }
  cgi_set_content_type("application/json");
  wiki_ajax_emit_page_object(zPageName, 1);
}







|







1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
**
** Responds with JSON. On error, an object in the form documented by
** ajax_route_error(). On success, an object in the form documented
** for wiki_ajax_emit_page_object().
*/
static void wiki_ajax_route_fetch(void){
  const char * zPageName = P("page");

  if( zPageName==0 || zPageName[0]==0 ){
    ajax_route_error(400,"Missing page name.");
    return;
  }
  cgi_set_content_type("application/json");
  wiki_ajax_emit_page_object(zPageName, 1);
}
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
}

/*
** WEBPAGE: wikiajax hidden
**
** An internal dispatcher for wiki AJAX operations. Not for direct
** client use. All routes defined by this interface are app-internal,
** subject to change 
*/
void wiki_ajax_page(void){
  const char * zName = P("name");
  AjaxRoute routeName = {0,0,0,0};
  const AjaxRoute * pRoute = 0;
  const AjaxRoute routes[] = {
  /* Keep these sorted by zName (for bsearch()) */







|







1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
}

/*
** WEBPAGE: wikiajax hidden
**
** An internal dispatcher for wiki AJAX operations. Not for direct
** client use. All routes defined by this interface are app-internal,
** subject to change
*/
void wiki_ajax_page(void){
  const char * zName = P("name");
  AjaxRoute routeName = {0,0,0,0};
  const AjaxRoute * pRoute = 0;
  const AjaxRoute routes[] = {
  /* Keep these sorted by zName (for bsearch()) */
1245
1246
1247
1248
1249
1250
1251
















1252
1253
1254
1255
1256
1257
1258
                     "CSRF violation (make sure sending of HTTP "
                     "Referer headers is enabled for XHR "
                     "connections).");
    return;
  }
  pRoute->xCallback();
}

















/*
** WEBPAGE: wikiedit
** URL: /wikedit?name=PAGENAME
**
** The main front-end for the Ajax-based wiki editor app. Passing
** in the name of an unknown page will trigger the creation







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







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
                     "CSRF violation (make sure sending of HTTP "
                     "Referer headers is enabled for XHR "
                     "connections).");
    return;
  }
  pRoute->xCallback();
}

/*
** Emits a preview-toggle option widget for /wikiedit and /fileedit.
*/
void wikiedit_emit_toggle_preview(void){
  CX("<div class='input-with-label'>"
     "<input type='checkbox' id='edit-shift-enter-preview' "
     "></input><label for='edit-shift-enter-preview'>"
     "Shift-enter previews</label>"
     "<div class='help-buttonlet'>"
     "When enabled, shift-enter switches between preview and edit modes. "
     "Some software-based keyboards misinteract with this, so it can be "
     "disabled when needed."
     "</div>"
     "</div>");
}

/*
** WEBPAGE: wikiedit
** URL: /wikedit?name=PAGENAME
**
** The main front-end for the Ajax-based wiki editor app. Passing
** in the name of an unknown page will trigger the creation
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
     "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
     the FOUC effect of the tabs before JS re-assembles them. */

  /******* Page list *******/
  {
    CX("<div id='wikiedit-tab-pages' "
       "data-tab-parent='wikiedit-tabs' "
       "data-tab-label='Wiki Page List' "
       "class='hidden'"
       ">");
    CX("<div>Loading wiki pages list...</div>");
    CX("</div>"/*#wikiedit-tab-pages*/);
  }
  
  /******* Content tab *******/
  {
    CX("<div id='wikiedit-tab-content' "
       "data-tab-parent='wikiedit-tabs' "
       "data-tab-label='Editor' "
       "class='hidden'"
       ">");







|















|







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
     "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
     the FOUC effect of the tabs before JS re-assembles them. */

  /******* Page list *******/
  {
    CX("<div id='wikiedit-tab-pages' "
       "data-tab-parent='wikiedit-tabs' "
       "data-tab-label='Wiki Page List' "
       "class='hidden'"
       ">");
    CX("<div>Loading wiki pages list...</div>");
    CX("</div>"/*#wikiedit-tab-pages*/);
  }

  /******* Content tab *******/
  {
    CX("<div id='wikiedit-tab-content' "
       "data-tab-parent='wikiedit-tabs' "
       "data-tab-label='Editor' "
       "class='hidden'"
       ">");
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
       "<div class='help-buttonlet'>"
       "Reload the file from the server, discarding "
       "any local edits. To help avoid accidental loss of "
       "edits, it requires confirmation (a second click) within "
       "a few seconds or it will not reload."
       "</div>"
       "</div>");

    CX("</div>");
    CX("<div class='flex-container flex-column stretch'>");
    CX("<textarea name='content' id='wikiedit-content-editor' "
       "class='wikiedit' rows='25'>");
    CX("</textarea>");
    CX("</div>"/*textarea wrapper*/);
    CX("</div>"/*#tab-file-content*/);







|







1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
       "<div class='help-buttonlet'>"
       "Reload the file from the server, discarding "
       "any local edits. To help avoid accidental loss of "
       "edits, it requires confirmation (a second click) within "
       "a few seconds or it will not reload."
       "</div>"
       "</div>");
    wikiedit_emit_toggle_preview();
    CX("</div>");
    CX("<div class='flex-container flex-column stretch'>");
    CX("<textarea name='content' id='wikiedit-content-editor' "
       "class='wikiedit' rows='25'>");
    CX("</textarea>");
    CX("</div>"/*textarea wrapper*/);
    CX("</div>"/*#tab-file-content*/);
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
**    wsort         Sort names by this label
**    wrid          rid of the most recent version of the page
**    wmtime        time most recent version was created
**    wcnt          Number of versions of this wiki page
**
** The wrid value is zero for deleted wiki pages.
*/
static const char listAllWikiPages[] = 
@ SELECT
@   substr(tag.tagname, 6) AS wname,
@   lower(substr(tag.tagname, 6)) AS sortname,
@   tagxref.value+0 AS wrid,
@   max(tagxref.mtime) AS wmtime,
@   count(*) AS wcnt
@ FROM







|







1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
**    wsort         Sort names by this label
**    wrid          rid of the most recent version of the page
**    wmtime        time most recent version was created
**    wcnt          Number of versions of this wiki page
**
** The wrid value is zero for deleted wiki pages.
*/
static const char listAllWikiPages[] =
@ SELECT
@   substr(tag.tagname, 6) AS wname,
@   lower(substr(tag.tagname, 6)) AS sortname,
@   tagxref.value+0 AS wrid,
@   max(tagxref.mtime) AS wmtime,
@   count(*) AS wcnt
@ FROM
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( !rid ) {
      /*
      ** At present, technote tags are prefixed with 'sym-', which shouldn't
      ** be the case, so we check for both with and without the prefix until
      ** such time as tags have the errant prefix dropped.
      */
      rid = db_int(0, "SELECT e.objid"
		      "  FROM event e, tag t, tagxref tx"
		      " WHERE e.type='e'"
		      "   AND e.tagid IS NOT NULL"
		      "   AND e.objid IN"
                      "       (SELECT rid FROM tagxref"
                      "         WHERE tagid=(SELECT tagid FROM tag"
                      "                       WHERE tagname GLOB '%q'))"
		      "    OR e.objid IN"
                      "       (SELECT rid FROM tagxref"
                      "         WHERE tagid=(SELECT tagid FROM tag"
                      "                       WHERE tagname GLOB 'sym-%q'))"
		      "   ORDER BY e.mtime DESC LIMIT 1",
		   zETime, zETime);
  }
  return rid;
}

/*
** COMMAND: wiki*
**







|
|
|
|



|



|
|







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
  if( !rid ) {
      /*
      ** At present, technote tags are prefixed with 'sym-', which shouldn't
      ** be the case, so we check for both with and without the prefix until
      ** such time as tags have the errant prefix dropped.
      */
      rid = db_int(0, "SELECT e.objid"
          "  FROM event e, tag t, tagxref tx"
          " WHERE e.type='e'"
          "   AND e.tagid IS NOT NULL"
          "   AND e.objid IN"
                      "       (SELECT rid FROM tagxref"
                      "         WHERE tagid=(SELECT tagid FROM tag"
                      "                       WHERE tagname GLOB '%q'))"
          "    OR e.objid IN"
                      "       (SELECT rid FROM tagxref"
                      "         WHERE tagid=(SELECT tagid FROM tag"
                      "                       WHERE tagname GLOB 'sym-%q'))"
          "   ORDER BY e.mtime DESC LIMIT 1",
       zETime, zETime);
  }
  return rid;
}

/*
** COMMAND: wiki*
**
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
    }
    while( db_step(&q)==SQLITE_ROW ){
      const char *zName = db_column_text(&q, 0);
      const int wrid = db_column_int(&q, 2);
      if(!showAll && !wrid){
        continue;
      }
      if( !showCkBr && 
          (sqlite3_strglob("checkin/*", zName)==0 ||
           sqlite3_strglob("branch/*", zName)==0) ){
        continue;
      }
      if( showIds ){
        const char *zUuid = db_column_text(&q, 1);
        fossil_print("%s ",zUuid);







|







2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
    }
    while( db_step(&q)==SQLITE_ROW ){
      const char *zName = db_column_text(&q, 0);
      const int wrid = db_column_int(&q, 2);
      if(!showAll && !wrid){
        continue;
      }
      if( !showCkBr &&
          (sqlite3_strglob("checkin/*", zName)==0 ||
           sqlite3_strglob("branch/*", zName)==0) ){
        continue;
      }
      if( showIds ){
        const char *zUuid = db_column_text(&q, 1);
        fossil_print("%s ",zUuid);
Changes to src/wikiformat.c.
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
      iMatchCnt = 1;
    }else if( n==1 && zStart[0]=='=' && iMatchCnt==1 ){
      iMatchCnt = 2;
    }else if( iMatchCnt==2 ){
      if( (zStart[0]=='"' || zStart[0]=='\'') && zStart[n-1]==zStart[0] ){
        zStart++;
        n -= 2;
      } 
      *pLen = n;
      return zStart;
    }else{
      iMatchCnt = 0;
    }
  }
  return 0;







|







2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
      iMatchCnt = 1;
    }else if( n==1 && zStart[0]=='=' && iMatchCnt==1 ){
      iMatchCnt = 2;
    }else if( iMatchCnt==2 ){
      if( (zStart[0]=='"' || zStart[0]=='\'') && zStart[n-1]==zStart[0] ){
        zStart++;
        n -= 2;
      }
      *pLen = n;
      return zStart;
    }else{
      iMatchCnt = 0;
    }
  }
  return 0;
Changes to src/winhttp.c.
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
    fossil_panic("unable to get path to the temporary directory.");
  }
  /* Use a subdirectory for temp files (can then be excluded from virus scan) */
  zTempSubDirPath = mprintf("%s%s\\",fossil_path_to_utf8(zTmpPath),zTempSubDir);
  if ( !file_mkdir(zTempSubDirPath, ExtFILE, 0) ||
        file_isdir(zTempSubDirPath, ExtFILE)==1 ){
    wcscpy(zTmpPath, fossil_utf8_to_path(zTempSubDirPath, 1));
  }  
  if( g.fHttpTrace ){
    zTempPrefix = mprintf("httptrace");
  }else{
    zTempPrefix = mprintf("%sfossil_server_P%d",
                          fossil_unicode_to_utf8(zTmpPath), iPort);
  }
  fossil_print("Temporary files: %s*\n", zTempPrefix);







|







666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
    fossil_panic("unable to get path to the temporary directory.");
  }
  /* Use a subdirectory for temp files (can then be excluded from virus scan) */
  zTempSubDirPath = mprintf("%s%s\\",fossil_path_to_utf8(zTmpPath),zTempSubDir);
  if ( !file_mkdir(zTempSubDirPath, ExtFILE, 0) ||
        file_isdir(zTempSubDirPath, ExtFILE)==1 ){
    wcscpy(zTmpPath, fossil_utf8_to_path(zTempSubDirPath, 1));
  }
  if( g.fHttpTrace ){
    zTempPrefix = mprintf("httptrace");
  }else{
    zTempPrefix = mprintf("%sfossil_server_P%d",
                          fossil_unicode_to_utf8(zTmpPath), iPort);
  }
  fossil_print("Temporary files: %s*\n", zTempPrefix);
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
    if( !hScm ) winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
    hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName),
                        SERVICE_ALL_ACCESS);
    if( !hSvc ) winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
    QueryServiceStatus(hSvc, &sstat);
    if( sstat.dwCurrentState!=SERVICE_RUNNING ){
      fossil_print("Starting service '%s'", zSvcName);
      if( sstat.dwCurrentState!=SERVICE_START_PENDING ){ 
        if( !StartServiceW(hSvc, 0, NULL) ){ 
          winhttp_fatal("start", zSvcName, win32_get_last_errmsg()); 
        } 
        QueryServiceStatus(hSvc, &sstat); 
      } 
      while( sstat.dwCurrentState==SERVICE_START_PENDING ||
             sstat.dwCurrentState==SERVICE_STOPPED ){
        Sleep(100);
        fossil_print(".");
        QueryServiceStatus(hSvc, &sstat);
      }
      if( sstat.dwCurrentState==SERVICE_RUNNING ){







|
|
|
|
|
|







1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
    if( !hScm ) winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
    hSvc = OpenServiceW(hScm, fossil_utf8_to_unicode(zSvcName),
                        SERVICE_ALL_ACCESS);
    if( !hSvc ) winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
    QueryServiceStatus(hSvc, &sstat);
    if( sstat.dwCurrentState!=SERVICE_RUNNING ){
      fossil_print("Starting service '%s'", zSvcName);
      if( sstat.dwCurrentState!=SERVICE_START_PENDING ){
        if( !StartServiceW(hSvc, 0, NULL) ){
          winhttp_fatal("start", zSvcName, win32_get_last_errmsg());
        }
        QueryServiceStatus(hSvc, &sstat);
      }
      while( sstat.dwCurrentState==SERVICE_START_PENDING ||
             sstat.dwCurrentState==SERVICE_STOPPED ){
        Sleep(100);
        fossil_print(".");
        QueryServiceStatus(hSvc, &sstat);
      }
      if( sstat.dwCurrentState==SERVICE_RUNNING ){
Changes to src/xfer.c.
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
    }
  }else{
    nullContent = 1;
  }

  /* The isWriter flag must be true in order to land the new file */
  if( !isWriter ){
    blob_appendf(&pXfer->err, "Write permissions for unversioned files missing");
    goto end_accept_unversioned_file;
  }

  /* Make sure we have a valid g.rcvid marker */
  content_rcvid_init(0);

  /* Check to see if current content really should be overwritten.  Ideally,







|







356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
    }
  }else{
    nullContent = 1;
  }

  /* The isWriter flag must be true in order to land the new file */
  if( !isWriter ){
    blob_appendf(&pXfer->err,"Write permissions for unversioned files missing");
    goto end_accept_unversioned_file;
  }

  /* Make sure we have a valid g.rcvid marker */
  content_rcvid_init(0);

  /* Check to see if current content really should be overwritten.  Ideally,
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
        xfer.nextIsPrivate = 1;
      }
    }else


    /*    pragma NAME VALUE...
    **
    ** The client issue pragmas to try to influence the behavior of the
    ** server.  These are requests only.  Unknown pragmas are silently
    ** ignored.
    */
    if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){

      /*   pragma send-private
      **







|







1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
        xfer.nextIsPrivate = 1;
      }
    }else


    /*    pragma NAME VALUE...
    **
    ** The client issues pragmas to try to influence the behavior of the
    ** server.  These are requests only.  Unknown pragmas are silently
    ** ignored.
    */
    if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){

      /*   pragma send-private
      **
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
      const char *zArg = db_column_text(&q, 1);
      i64 iMtime = db_column_int64(&q, 2);
      memset(&x, 0, sizeof(x));
      url_parse_local(zUrl, URL_OMIT_USER, &x);
      if( x.name!=0 && sqlite3_strlike("%localhost%", x.name, 0)!=0 ){
        @ pragma link %F(x.canonical) %F(zArg) %lld(iMtime)
      }
      url_unparse(&x);      
    }
    db_finalize(&q);
  }

  /* Send the server timestamp last, in case prior processing happened
  ** to use up a significant fraction of our time window.
  */







|







1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
      const char *zArg = db_column_text(&q, 1);
      i64 iMtime = db_column_int64(&q, 2);
      memset(&x, 0, sizeof(x));
      url_parse_local(zUrl, URL_OMIT_USER, &x);
      if( x.name!=0 && sqlite3_strlike("%localhost%", x.name, 0)!=0 ){
        @ pragma link %F(x.canonical) %F(zArg) %lld(iMtime)
      }
      url_unparse(&x);
    }
    db_finalize(&q);
  }

  /* Send the server timestamp last, in case prior processing happened
  ** to use up a significant fraction of our time window.
  */
1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
**
** Usage: %fossil test-xfer ?OPTIONS? XFERFILE
**
** Pass the sync-protocol input file XFERFILE into the server-side sync
** protocol handler.  Generate a reply on standard output.
**
** This command was original created to help debug the server side of
** sync messages.  The XFERFILE is the uncompressed content of an 
** "xfer" HTTP request from client to server.  This command interprets
** that message and generates the content of an HTTP reply (without any
** encoding and without the HTTP reply headers) and writes that reply
** on standard output.
**
** One possible usages scenario is to capture some XFERFILE examples
** using a command like:







|







1878
1879
1880
1881
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
**
** Usage: %fossil test-xfer ?OPTIONS? XFERFILE
**
** Pass the sync-protocol input file XFERFILE into the server-side sync
** protocol handler.  Generate a reply on standard output.
**
** This command was original created to help debug the server side of
** sync messages.  The XFERFILE is the uncompressed content of an
** "xfer" HTTP request from client to server.  This command interprets
** that message and generates the content of an HTTP reply (without any
** encoding and without the HTTP reply headers) and writes that reply
** on standard output.
**
** One possible usages scenario is to capture some XFERFILE examples
** using a command like:
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
        if( iStatus>=4 && uvPullOnly==1 ){
          fossil_warning(
            "Warning: uv-pull-only                                       \n"
            "         Unable to push unversioned content because you lack\n"
            "         sufficient permission on the server\n"
          );
          uvPullOnly = 2;
        }          
        if( iStatus<=3 || uvPullOnly ){
          db_multi_exec("DELETE FROM uv_tosend WHERE name=%Q", zName);
        }else if( iStatus==4 ){
          db_multi_exec("UPDATE uv_tosend SET mtimeOnly=1 WHERE name=%Q",zName);
        }else if( iStatus==5 ){
          db_multi_exec("REPLACE INTO uv_tosend(name,mtimeOnly) VALUES(%Q,0)",
                        zName);







|







2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
        if( iStatus>=4 && uvPullOnly==1 ){
          fossil_warning(
            "Warning: uv-pull-only                                       \n"
            "         Unable to push unversioned content because you lack\n"
            "         sufficient permission on the server\n"
          );
          uvPullOnly = 2;
        }
        if( iStatus<=3 || uvPullOnly ){
          db_multi_exec("DELETE FROM uv_tosend WHERE name=%Q", zName);
        }else if( iStatus==4 ){
          db_multi_exec("UPDATE uv_tosend SET mtimeOnly=1 WHERE name=%Q",zName);
        }else if( iStatus==5 ){
          db_multi_exec("REPLACE INTO uv_tosend(name,mtimeOnly) VALUES(%Q,0)",
                        zName);
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
      ** The server can send pragmas to try to convey meta-information to
      ** the client.  These are informational only.  Unknown pragmas are
      ** silently ignored.
      */
      if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){
        /*   pragma server-version VERSION ?DATE? ?TIME?
        **
        ** The servger announces to the server what version of Fossil it
        ** is running.  The DATE and TIME are a pure numeric ISO8601 time
        ** for the specific check-in of the client.
        */
        if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "server-version") ){
          xfer.remoteVersion = atoi(blob_str(&xfer.aToken[2]));
          if( xfer.nToken>=5 ){
            xfer.remoteDate = atoi(blob_str(&xfer.aToken[3]));
            xfer.remoteTime = atoi(blob_str(&xfer.aToken[4]));
          }
        }

        /*   pragma uv-pull-only
        **   pragma uv-push-ok
        **
        ** If the server is unwill to accept new unversioned content (because
        ** this client lacks the necessary permissions) then it sends a
        ** "uv-pull-only" pragma so that the client will know not to waste
        ** bandwidth trying to upload unversioned content.  If the server
        ** does accept new unversioned content, it sends "uv-push-ok".
        */
        else if( syncFlags & SYNC_UNVERSIONED ){
          if( blob_eq(&xfer.aToken[1], "uv-pull-only") ){







|














|







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
      ** The server can send pragmas to try to convey meta-information to
      ** the client.  These are informational only.  Unknown pragmas are
      ** silently ignored.
      */
      if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){
        /*   pragma server-version VERSION ?DATE? ?TIME?
        **
        ** The server announces to the server what version of Fossil it
        ** is running.  The DATE and TIME are a pure numeric ISO8601 time
        ** for the specific check-in of the client.
        */
        if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "server-version") ){
          xfer.remoteVersion = atoi(blob_str(&xfer.aToken[2]));
          if( xfer.nToken>=5 ){
            xfer.remoteDate = atoi(blob_str(&xfer.aToken[3]));
            xfer.remoteTime = atoi(blob_str(&xfer.aToken[4]));
          }
        }

        /*   pragma uv-pull-only
        **   pragma uv-push-ok
        **
        ** If the server is unwilling to accept new unversioned content (because
        ** this client lacks the necessary permissions) then it sends a
        ** "uv-pull-only" pragma so that the client will know not to waste
        ** bandwidth trying to upload unversioned content.  If the server
        ** does accept new unversioned content, it sends "uv-push-ok".
        */
        else if( syncFlags & SYNC_UNVERSIONED ){
          if( blob_eq(&xfer.aToken[1], "uv-pull-only") ){
Changes to src/zip.c.
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
  return 512;
}
static int archiveDeviceCharacteristics(sqlite3_file *pFile){
  return 0;
}

static int archiveOpen(
  sqlite3_vfs *pVfs, const char *zName, 
  sqlite3_file *pFile, int flags, int *pOutFlags
){
  static struct sqlite3_io_methods methods = {
    1,             /* iVersion */
    archiveClose,
    archiveRead,
    archiveWrite,







|







136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
  return 512;
}
static int archiveDeviceCharacteristics(sqlite3_file *pFile){
  return 0;
}

static int archiveOpen(
  sqlite3_vfs *pVfs, const char *zName,
  sqlite3_file *pFile, int flags, int *pOutFlags
){
  static struct sqlite3_io_methods methods = {
    1,             /* iVersion */
    archiveClose,
    archiveRead,
    archiveWrite,
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
** Append a single file to a growing ZIP archive.
**
** pFile is the file to be appended.  zName is the name
** that the file should be saved as.
*/
static void zip_add_file_to_zip(
  Archive *p,
  const char *zName, 
  const Blob *pFile, 
  int mPerm
){
  z_stream stream;
  int nameLen;
  int toOut = 0;
  int iStart;
  unsigned long iCRC = 0;







|
|







245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
** Append a single file to a growing ZIP archive.
**
** pFile is the file to be appended.  zName is the name
** that the file should be saved as.
*/
static void zip_add_file_to_zip(
  Archive *p,
  const char *zName,
  const Blob *pFile,
  int mPerm
){
  z_stream stream;
  int nameLen;
  int toOut = 0;
  int iStart;
  unsigned long iCRC = 0;
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
  put16(&zExTime[2], 5);
  blob_append(&toc, zExTime, 9);
  nEntry++;
}

static void zip_add_file_to_sqlar(
  Archive *p,
  const char *zName, 
  const Blob *pFile, 
  int mPerm
){
  int nName = (int)strlen(zName);

  if( p->db==0 ){
    assert( p->vfs.zName==0 );
    p->vfs.zName = (const char*)mprintf("archivevfs%p", (void*)p);
    p->vfs.iVersion = 1;
    p->vfs.szOsFile = sizeof(ArchiveFile);
    p->vfs.mxPathname = 512;
    p->vfs.pAppData = (void*)p->pBlob;
    p->vfs.xOpen = archiveOpen;
    p->vfs.xDelete = archiveDelete;
    p->vfs.xAccess = archiveAccess;
    p->vfs.xFullPathname = archiveFullPathname;
    p->vfs.xRandomness = archiveRandomness;
    p->vfs.xSleep = archiveSleep;
    p->vfs.xCurrentTime = archiveCurrentTime;
    p->vfs.xGetLastError = archiveGetLastError;
    sqlite3_vfs_register(&p->vfs, 0);
    sqlite3_open_v2("file:xyz.db", &p->db, 
        SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE, p->vfs.zName
    );
    assert( p->db );
    blob_zero(&p->tmp);
    sqlite3_exec(p->db, 
        "PRAGMA page_size=512;"
        "PRAGMA journal_mode = off;"
        "PRAGMA cache_spill = off;"
        "BEGIN;"
        "CREATE TABLE sqlar("
          "name TEXT PRIMARY KEY,  -- name of the file\n"
          "mode INT,               -- access permissions\n"
          "mtime INT,              -- last modification time\n"
          "sz INT,                 -- original file size\n"
          "data BLOB               -- compressed content\n"
        ");", 0, 0, 0
    );
    sqlite3_prepare(p->db, 
        "INSERT INTO sqlar VALUES(?, ?, ?, ?, ?)", -1, 
        &p->pInsert, 0
    );
    assert( p->pInsert );

    sqlite3_bind_int64(p->pInsert, 3, unixTime);
    blob_zero(p->pBlob);
  }







|
|




















|




|












|
|







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
  put16(&zExTime[2], 5);
  blob_append(&toc, zExTime, 9);
  nEntry++;
}

static void zip_add_file_to_sqlar(
  Archive *p,
  const char *zName,
  const Blob *pFile,
  int mPerm
){
  int nName = (int)strlen(zName);

  if( p->db==0 ){
    assert( p->vfs.zName==0 );
    p->vfs.zName = (const char*)mprintf("archivevfs%p", (void*)p);
    p->vfs.iVersion = 1;
    p->vfs.szOsFile = sizeof(ArchiveFile);
    p->vfs.mxPathname = 512;
    p->vfs.pAppData = (void*)p->pBlob;
    p->vfs.xOpen = archiveOpen;
    p->vfs.xDelete = archiveDelete;
    p->vfs.xAccess = archiveAccess;
    p->vfs.xFullPathname = archiveFullPathname;
    p->vfs.xRandomness = archiveRandomness;
    p->vfs.xSleep = archiveSleep;
    p->vfs.xCurrentTime = archiveCurrentTime;
    p->vfs.xGetLastError = archiveGetLastError;
    sqlite3_vfs_register(&p->vfs, 0);
    sqlite3_open_v2("file:xyz.db", &p->db,
        SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE, p->vfs.zName
    );
    assert( p->db );
    blob_zero(&p->tmp);
    sqlite3_exec(p->db,
        "PRAGMA page_size=512;"
        "PRAGMA journal_mode = off;"
        "PRAGMA cache_spill = off;"
        "BEGIN;"
        "CREATE TABLE sqlar("
          "name TEXT PRIMARY KEY,  -- name of the file\n"
          "mode INT,               -- access permissions\n"
          "mtime INT,              -- last modification time\n"
          "sz INT,                 -- original file size\n"
          "data BLOB               -- compressed content\n"
        ");", 0, 0, 0
    );
    sqlite3_prepare(p->db,
        "INSERT INTO sqlar VALUES(?, ?, ?, ?, ?)", -1,
        &p->pInsert, 0
    );
    assert( p->pInsert );

    sqlite3_bind_int64(p->pInsert, 3, unixTime);
    blob_zero(p->pBlob);
  }
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
    sqlite3_bind_int(p->pInsert, 4, 0);
    sqlite3_bind_null(p->pInsert, 5);
  }else{
    sqlite3_bind_text(p->pInsert, 1, zName, nName, SQLITE_STATIC);
    if( mPerm==PERM_LNK ){
      sqlite3_bind_int(p->pInsert, 2, 0120755);
      sqlite3_bind_int(p->pInsert, 4, -1);
      sqlite3_bind_text(p->pInsert, 5, 
          blob_buffer(pFile), blob_size(pFile), SQLITE_STATIC
      );
    }else{
      unsigned int nIn = blob_size(pFile);
      unsigned long int nOut = nIn;
      sqlite3_bind_int(p->pInsert, 2, mPerm==PERM_EXE ? 0100755 : 0100644);
      sqlite3_bind_int(p->pInsert, 4, nIn);
      zip_blob_minsize(&p->tmp, nIn);
      compress( (unsigned char*)
          blob_buffer(&p->tmp), &nOut, (unsigned char*)blob_buffer(pFile), nIn
      );
      if( nOut>=(unsigned long)nIn ){
        sqlite3_bind_blob(p->pInsert, 5, 
            blob_buffer(pFile), blob_size(pFile), SQLITE_STATIC
        );
      }else{
        sqlite3_bind_blob(p->pInsert, 5, 
            blob_buffer(&p->tmp), nOut, SQLITE_STATIC
        );
      }
    }
  }

  sqlite3_step(p->pInsert);
  sqlite3_reset(p->pInsert);
}

static void zip_add_file(
  Archive *p,
  const char *zName, 
  const Blob *pFile, 
  int mPerm
){
  if( p->eType==ARCHIVE_ZIP ){
    zip_add_file_to_zip(p, zName, pFile, mPerm);
  }else{
    zip_add_file_to_sqlar(p, zName, pFile, mPerm);
  }







|












|



|












|
|







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
    sqlite3_bind_int(p->pInsert, 4, 0);
    sqlite3_bind_null(p->pInsert, 5);
  }else{
    sqlite3_bind_text(p->pInsert, 1, zName, nName, SQLITE_STATIC);
    if( mPerm==PERM_LNK ){
      sqlite3_bind_int(p->pInsert, 2, 0120755);
      sqlite3_bind_int(p->pInsert, 4, -1);
      sqlite3_bind_text(p->pInsert, 5,
          blob_buffer(pFile), blob_size(pFile), SQLITE_STATIC
      );
    }else{
      unsigned int nIn = blob_size(pFile);
      unsigned long int nOut = nIn;
      sqlite3_bind_int(p->pInsert, 2, mPerm==PERM_EXE ? 0100755 : 0100644);
      sqlite3_bind_int(p->pInsert, 4, nIn);
      zip_blob_minsize(&p->tmp, nIn);
      compress( (unsigned char*)
          blob_buffer(&p->tmp), &nOut, (unsigned char*)blob_buffer(pFile), nIn
      );
      if( nOut>=(unsigned long)nIn ){
        sqlite3_bind_blob(p->pInsert, 5,
            blob_buffer(pFile), blob_size(pFile), SQLITE_STATIC
        );
      }else{
        sqlite3_bind_blob(p->pInsert, 5,
            blob_buffer(&p->tmp), nOut, SQLITE_STATIC
        );
      }
    }
  }

  sqlite3_step(p->pInsert);
  sqlite3_reset(p->pInsert);
}

static void zip_add_file(
  Archive *p,
  const char *zName,
  const Blob *pFile,
  int mPerm
){
  if( p->eType==ARCHIVE_ZIP ){
    zip_add_file_to_zip(p, zName, pFile, mPerm);
  }else{
    zip_add_file_to_sqlar(p, zName, pFile, mPerm);
  }
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
          " || substr(blob.uuid, 1, 10)"
       "  FROM event, blob"
       " WHERE event.objid=%d"
       "   AND blob.rid=%d",
       db_get("project-name", "unnamed"), rid, rid
    );
  }
  zip_of_checkin(eType, rid, zOut ? &zip : 0, 
                 zName, pInclude, pExclude, listFlag);
  glob_free(pInclude);
  glob_free(pExclude);
  if( zOut ){
    blob_write_to_file(&zip, zOut);
    blob_reset(&zip);
  }







|







784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
          " || substr(blob.uuid, 1, 10)"
       "  FROM event, blob"
       " WHERE event.objid=%d"
       "   AND blob.rid=%d",
       db_get("project-name", "unnamed"), rid, rid
    );
  }
  zip_of_checkin(eType, rid, zOut ? &zip : 0,
                 zName, pInclude, pExclude, listFlag);
  glob_free(pInclude);
  glob_free(pExclude);
  if( zOut ){
    blob_write_to_file(&zip, zOut);
    blob_reset(&zip);
  }
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
  zInclude = P("in");
  if( zInclude ) pInclude = glob_create(zInclude);
  zExclude = P("ex");
  if( zExclude ) pExclude = glob_create(zExclude);
  if( zInclude==0 && zExclude==0 ){
    etag_check_for_invariant_name(z);
  }
  if( eType==ARCHIVE_ZIP 
   && nName>4
   && fossil_strcmp(&zName[nName-4], ".zip")==0
  ){
    /* Special case:  Remove the ".zip" suffix.  */
    nName -= 4;
    zName[nName] = 0;
  }else if( eType==ARCHIVE_SQLAR 
   && nName>6
   && fossil_strcmp(&zName[nName-6], ".sqlar")==0
  ){
    /* Special case:  Remove the ".sqlar" suffix.  */
    nName -= 6;
    zName[nName] = 0;
  }else{







|






|







945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
  zInclude = P("in");
  if( zInclude ) pInclude = glob_create(zInclude);
  zExclude = P("ex");
  if( zExclude ) pExclude = glob_create(zExclude);
  if( zInclude==0 && zExclude==0 ){
    etag_check_for_invariant_name(z);
  }
  if( eType==ARCHIVE_ZIP
   && nName>4
   && fossil_strcmp(&zName[nName-4], ".zip")==0
  ){
    /* Special case:  Remove the ".zip" suffix.  */
    nName -= 4;
    zName[nName] = 0;
  }else if( eType==ARCHIVE_SQLAR
   && nName>6
   && fossil_strcmp(&zName[nName-6], ".sqlar")==0
  ){
    /* Special case:  Remove the ".sqlar" suffix.  */
    nName -= 6;
    zName[nName] = 0;
  }else{
Changes to test/amend.test.
304
305
306
307
308
309
310
311
312
313



314
315
316
317
318
319
320
  set t5exp "*"
  foreach tag $tagt {
    lappend tags -tag $tag
    lappend cancels -cancel $tag
  }
  foreach res $result {
    append t1exp ", $res"
    append t2exp "sym-$res*"
    append t3exp "Add*tag*\"$res\".*"
    append t5exp "Cancel*tag*\"$res\".*"



  }
  eval fossil amend $HASH $tags
  test amend-tag-$tc.1 {[string match "*hash:*$HASH*tags:*$t1exp*" $RESULT]}
  fossil tag ls --raw $HASH
  test amend-tag-$tc.2 {[string match $t2exp $RESULT]}
  fossil timeline -n 1
  test amend-tag-$tc.3 {[string match $t3exp $RESULT]}







<


>
>
>







304
305
306
307
308
309
310

311
312
313
314
315
316
317
318
319
320
321
322
  set t5exp "*"
  foreach tag $tagt {
    lappend tags -tag $tag
    lappend cancels -cancel $tag
  }
  foreach res $result {
    append t1exp ", $res"

    append t3exp "Add*tag*\"$res\".*"
    append t5exp "Cancel*tag*\"$res\".*"
  }
  foreach res [lsort -nocase $result] {
    append t2exp "sym-$res*"
  }
  eval fossil amend $HASH $tags
  test amend-tag-$tc.1 {[string match "*hash:*$HASH*tags:*$t1exp*" $RESULT]}
  fossil tag ls --raw $HASH
  test amend-tag-$tc.2 {[string match $t2exp $RESULT]}
  fossil timeline -n 1
  test amend-tag-$tc.3 {[string match $t3exp $RESULT]}
Changes to test/commit-warning.test.
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
# of source files that MUST NEVER BE TEXT.
#
test_block_in_checkout pre-commit-warnings-fossil-1 {
  fossil test-commit-warning --no-settings
} {
  test pre-commit-warnings-fossil-1 {[normalize_result] eq \
      [subst -nocommands -novariables [string trim {
1\tart/branching.odp\tbinary data
1\tart/concept1.dia\tbinary data
1\tart/concept2.dia\tbinary data
1\tcompat/zlib/contrib/blast/test.pk\tbinary data
1\tcompat/zlib/contrib/dotzlib/DotZLib.build\tCR/LF line endings
1\tcompat/zlib/contrib/dotzlib/DotZLib.chm\tbinary data
1\tcompat/zlib/contrib/dotzlib/DotZLib.sln\tCR/LF line endings
1\tcompat/zlib/contrib/dotzlib/DotZLib/AssemblyInfo.cs\tCR/LF line endings
1\tcompat/zlib/contrib/dotzlib/DotZLib/ChecksumImpl.cs\tinvalid UTF-8
1\tcompat/zlib/contrib/dotzlib/DotZLib/CircularBuffer.cs\tinvalid UTF-8
1\tcompat/zlib/contrib/dotzlib/DotZLib/CodecBase.cs\tinvalid UTF-8
1\tcompat/zlib/contrib/dotzlib/DotZLib/Deflater.cs\tinvalid UTF-8
1\tcompat/zlib/contrib/dotzlib/DotZLib/DotZLib.cs\tinvalid UTF-8
1\tcompat/zlib/contrib/dotzlib/DotZLib/DotZLib.csproj\tCR/LF line endings
1\tcompat/zlib/contrib/dotzlib/DotZLib/GZipStream.cs\tinvalid UTF-8
1\tcompat/zlib/contrib/dotzlib/DotZLib/Inflater.cs\tinvalid UTF-8
1\tcompat/zlib/contrib/dotzlib/DotZLib/UnitTests.cs\tCR/LF line endings
1\tcompat/zlib/contrib/dotzlib/LICENSE_1_0.txt\tCR/LF line endings
1\tcompat/zlib/contrib/dotzlib/readme.txt\tCR/LF line endings
1\tcompat/zlib/contrib/gcc_gvmat64/gvmat64.S\tCR/LF line endings
1\tcompat/zlib/contrib/masmx64/bld_ml64.bat\tCR/LF line endings
1\tcompat/zlib/contrib/masmx64/gvmat64.asm\tCR/LF line endings
1\tcompat/zlib/contrib/masmx64/inffas8664.c\tCR/LF line endings
1\tcompat/zlib/contrib/masmx64/inffasx64.asm\tCR/LF line endings
1\tcompat/zlib/contrib/masmx64/readme.txt\tCR/LF line endings
1\tcompat/zlib/contrib/masmx86/bld_ml32.bat\tCR/LF line endings
1\tcompat/zlib/contrib/masmx86/inffas32.asm\tCR/LF line endings
1\tcompat/zlib/contrib/masmx86/match686.asm\tCR/LF line endings
1\tcompat/zlib/contrib/masmx86/readme.txt\tCR/LF line endings
1\tcompat/zlib/contrib/puff/zeros.raw\tbinary data
1\tcompat/zlib/contrib/testzlib/testzlib.c\tCR/LF line endings
1\tcompat/zlib/contrib/testzlib/testzlib.txt\tCR/LF line endings
1\tcompat/zlib/contrib/vstudio/readme.txt\tCR/LF line endings
1\tcompat/zlib/contrib/vstudio/vc10/miniunz.vcxproj\tCR/LF line endings
1\tcompat/zlib/contrib/vstudio/vc10/miniunz.vcxproj.filters\tCR/LF line endings
1\tcompat/zlib/contrib/vstudio/vc10/minizip.vcxproj\tCR/LF line endings







<
<
<

















<
<
<
<
<
<
<
<
<







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
# of source files that MUST NEVER BE TEXT.
#
test_block_in_checkout pre-commit-warnings-fossil-1 {
  fossil test-commit-warning --no-settings
} {
  test pre-commit-warnings-fossil-1 {[normalize_result] eq \
      [subst -nocommands -novariables [string trim {



1\tcompat/zlib/contrib/blast/test.pk\tbinary data
1\tcompat/zlib/contrib/dotzlib/DotZLib.build\tCR/LF line endings
1\tcompat/zlib/contrib/dotzlib/DotZLib.chm\tbinary data
1\tcompat/zlib/contrib/dotzlib/DotZLib.sln\tCR/LF line endings
1\tcompat/zlib/contrib/dotzlib/DotZLib/AssemblyInfo.cs\tCR/LF line endings
1\tcompat/zlib/contrib/dotzlib/DotZLib/ChecksumImpl.cs\tinvalid UTF-8
1\tcompat/zlib/contrib/dotzlib/DotZLib/CircularBuffer.cs\tinvalid UTF-8
1\tcompat/zlib/contrib/dotzlib/DotZLib/CodecBase.cs\tinvalid UTF-8
1\tcompat/zlib/contrib/dotzlib/DotZLib/Deflater.cs\tinvalid UTF-8
1\tcompat/zlib/contrib/dotzlib/DotZLib/DotZLib.cs\tinvalid UTF-8
1\tcompat/zlib/contrib/dotzlib/DotZLib/DotZLib.csproj\tCR/LF line endings
1\tcompat/zlib/contrib/dotzlib/DotZLib/GZipStream.cs\tinvalid UTF-8
1\tcompat/zlib/contrib/dotzlib/DotZLib/Inflater.cs\tinvalid UTF-8
1\tcompat/zlib/contrib/dotzlib/DotZLib/UnitTests.cs\tCR/LF line endings
1\tcompat/zlib/contrib/dotzlib/LICENSE_1_0.txt\tCR/LF line endings
1\tcompat/zlib/contrib/dotzlib/readme.txt\tCR/LF line endings
1\tcompat/zlib/contrib/gcc_gvmat64/gvmat64.S\tCR/LF line endings









1\tcompat/zlib/contrib/puff/zeros.raw\tbinary data
1\tcompat/zlib/contrib/testzlib/testzlib.c\tCR/LF line endings
1\tcompat/zlib/contrib/testzlib/testzlib.txt\tCR/LF line endings
1\tcompat/zlib/contrib/vstudio/readme.txt\tCR/LF line endings
1\tcompat/zlib/contrib/vstudio/vc10/miniunz.vcxproj\tCR/LF line endings
1\tcompat/zlib/contrib/vstudio/vc10/miniunz.vcxproj.filters\tCR/LF line endings
1\tcompat/zlib/contrib/vstudio/vc10/minizip.vcxproj\tCR/LF line endings
241
242
243
244
245
246
247

248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
1\tcompat/zlib/contrib/vstudio/vc9/zlibstat.vcproj\tCR/LF line endings
1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.def\tCR/LF line endings
1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.sln\tCR/LF line endings
1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.vcproj\tCR/LF line endings
1\tcompat/zlib/win32/zlib.def\tCR/LF line endings
1\tcompat/zlib/zlib.3.pdf\tbinary data
1\tcompat/zlib/zlib.map\tCR/LF line endings

1\tskins/blitz/arrow_project.png\tbinary data
1\tskins/blitz/dir.png\tbinary data
1\tskins/blitz/file.png\tbinary data
1\tskins/blitz/fossil_100.png\tbinary data
1\tskins/blitz/fossil_80_reversed_darkcyan.png\tbinary data
1\tskins/blitz/fossil_80_reversed_darkcyan_text.png\tbinary data
1\tskins/blitz/rss_20.png\tbinary data
1\tskins/bootstrap/css.txt\tlong lines
1\tsrc/alerts/bflat2.wav\tbinary data
1\tsrc/alerts/bflat3.wav\tbinary data
1\tsrc/alerts/bloop.wav\tbinary data
1\tsrc/alerts/plunk.wav\tbinary data
1\tsrc/sounds/0.wav\tbinary data
1\tsrc/sounds/1.wav\tbinary data
1\tsrc/sounds/2.wav\tbinary data







>







<







229
230
231
232
233
234
235
236
237
238
239
240
241
242
243

244
245
246
247
248
249
250
1\tcompat/zlib/contrib/vstudio/vc9/zlibstat.vcproj\tCR/LF line endings
1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.def\tCR/LF line endings
1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.sln\tCR/LF line endings
1\tcompat/zlib/contrib/vstudio/vc9/zlibvc.vcproj\tCR/LF line endings
1\tcompat/zlib/win32/zlib.def\tCR/LF line endings
1\tcompat/zlib/zlib.3.pdf\tbinary data
1\tcompat/zlib/zlib.map\tCR/LF line endings
1\textsrc/pikchr.wasm\tbinary data
1\tskins/blitz/arrow_project.png\tbinary data
1\tskins/blitz/dir.png\tbinary data
1\tskins/blitz/file.png\tbinary data
1\tskins/blitz/fossil_100.png\tbinary data
1\tskins/blitz/fossil_80_reversed_darkcyan.png\tbinary data
1\tskins/blitz/fossil_80_reversed_darkcyan_text.png\tbinary data
1\tskins/blitz/rss_20.png\tbinary data

1\tsrc/alerts/bflat2.wav\tbinary data
1\tsrc/alerts/bflat3.wav\tbinary data
1\tsrc/alerts/bloop.wav\tbinary data
1\tsrc/alerts/plunk.wav\tbinary data
1\tsrc/sounds/0.wav\tbinary data
1\tsrc/sounds/1.wav\tbinary data
1\tsrc/sounds/2.wav\tbinary data
Changes to test/delta1.test.
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# Use test script files as the basis for this test.
#
# For each test, copy the file intact to "./t1".  Make
# some random changes in "./t2".  Then call test-delta on the
# two files to make sure that deltas between these two files
# work properly.
#
set filelist [glob $testdir/*]
foreach f $filelist {
  if {[file isdir $f]} continue
  set base [file root [file tail $f]]
  set f1 [read_file $f]
  write_file t1 $f1
  for {set i 0} {$i<100} {incr i} {
    write_file t2 [random_changes $f1 1 1 0 0.1]







|







23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# Use test script files as the basis for this test.
#
# For each test, copy the file intact to "./t1".  Make
# some random changes in "./t2".  Then call test-delta on the
# two files to make sure that deltas between these two files
# work properly.
#
set filelist [lsort [glob $testdir/*]]
foreach f $filelist {
  if {[file isdir $f]} continue
  set base [file root [file tail $f]]
  set f1 [read_file $f]
  write_file t1 $f1
  for {set i 0} {$i<100} {incr i} {
    write_file t2 [random_changes $f1 1 1 0 0.1]
Changes to test/diff.test.
106
107
108
109
110
111
112














































113
114
115
116
fossil diff file5.dat

test diff-file5-1 {[normalize_result] eq {Index: file5.dat
==================================================================
--- file5.dat
+++ file5.dat
cannot compute difference between binary files}}















































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

test_cleanup







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




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
fossil diff file5.dat

test diff-file5-1 {[normalize_result] eq {Index: file5.dat
==================================================================
--- file5.dat
+++ file5.dat
cannot compute difference between binary files}}

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

write_file file6a.dat "{\n \"abc\": {\n  \"def\": false,\n  \"ghi\": false\n }\n}\n"
write_file file6b.dat "{\n \"abc\": {\n  \"def\": false,\n  \"ghi\": false\n },\n \"jkl\": {\n  \"mno\": {\n   \"pqr\": false\n  }\n }\n}\n"
fossil xdiff -y -W 16 file6a.dat file6b.dat
test diff-file-6-1 {[normalize_result] eq {========== file6a.dat ===== versus ===== file6b.dat =====
     1 {                       1 {
     2  "abc": {               2  "abc": {
     3   "def": false,         3   "def": false,
     4   "ghi": false          4   "ghi": false
                        >      5  },
                        >      6  "jkl": {
                        >      7   "mno": {
                        >      8    "pqr": false
                        >      9   }
     5  }                     10  }
     6 }                      11 }}}

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

fossil rm file1.dat
fossil diff -v file1.dat

test diff-deleted-file-1 {[normalize_result] eq {DELETED  file1.dat
Index: file1.dat
==================================================================
--- file1.dat
+++ /dev/null
@@ -1,1 +0,0 @@
-test file 1 (one line no term).}}

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

write_file file6.dat "test file 6 (one line no term)."
fossil add file6.dat

fossil diff -v file6.dat

test diff-added-file-1 {[normalize_result] eq {ADDED    file6.dat
Index: file6.dat
==================================================================
--- /dev/null
+++ file6.dat
@@ -0,0 +1,1 @@
+test file 6 (one line no term).}}

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

test_cleanup
Changes to test/fake-editor.tcl.
47
48
49
50
51
52
53





54
55
56
57
58
59
60
  close $channel
  return ""
}

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

set fileName [lindex $argv 0]






if {[file exists $fileName]} {
  set data [readFile $fileName]
} else {
  set data ""
}








>
>
>
>
>







47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
  close $channel
  return ""
}

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

set fileName [lindex $argv 0]

if {[regexp {^CYGWIN} $::tcl_platform(os)]} {
  # Under Cygwin, we get a Windows path but must access using the unix path.
  set fileName [exec cygpath --unix $fileName]
}

if {[file exists $fileName]} {
  set data [readFile $fileName]
} else {
  set data ""
}

Changes to test/json.test.
175
176
177
178
179
180
181








182
183
184
185
186
187
188
proc test_json_payload {testname okfields badfields} {
  test_dict_keys $testname [dict get $::JR payload] $okfields $badfields
}

#### VERSION AKA HAI

# The JSON API generally assumes we have a respository, so let it have one.








test_setup

# Stop backoffice from running during this test as it can cause hangs.
fossil settings backoffice-disable 1

# Check for basic envelope fields in the result with an error
fossil_json -expectError







>
>
>
>
>
>
>
>







175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
proc test_json_payload {testname okfields badfields} {
  test_dict_keys $testname [dict get $::JR payload] $okfields $badfields
}

#### VERSION AKA HAI

# The JSON API generally assumes we have a respository, so let it have one.

# Set FOSSIL_USER to ensure consistent results in "json user list"
set _fossil_user ""
if [info exists env(FOSSIL_USER)] {
  set _fossil_user $env(FOSSIL_USER)
}
set ::env(FOSSIL_USER) "JSON-TEST-USER"

test_setup

# Stop backoffice from running during this test as it can cause hangs.
fossil settings backoffice-disable 1

# Check for basic envelope fields in the result with an error
fossil_json -expectError
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
test_json_payload json-login-a {authToken name capabilities loginCookieName} {}
set AuthAnon [dict get $JR payload]
proc test_hascaps {testname need caps} {
  foreach n [split $need {}] {
    test $testname-$n {[string first $n $caps] >= 0}
  }
}
test_hascaps json-login-c "hmnc" [dict get $AuthAnon capabilities]

fossil user new U1 User-1 Uone
fossil user capabilities U1 s
write_file u1 {
{
  "command":"login",
  "payload":{







|







282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
test_json_payload json-login-a {authToken name capabilities loginCookieName} {}
set AuthAnon [dict get $JR payload]
proc test_hascaps {testname need caps} {
  foreach n [split $need {}] {
    test $testname-$n {[string first $n $caps] >= 0}
  }
}
test_hascaps json-login-c "hz" [dict get $AuthAnon capabilities]

fossil user new U1 User-1 Uone
fossil user capabilities U1 s
write_file u1 {
{
  "command":"login",
  "payload":{
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






#     Fossil repository db file could not be found.
fossil close
fossil_json HAI -expectError
test json-RC-4102-CLI-exit {$CODE != 0}
test_json_envelope json-RC-4102-CLI-exit {fossil timestamp command procTimeUs \
procTimeMs resultCode resultText} {payload}
test json-RC-4102 {[dict get $JR resultCode] eq "FOSSIL-4102"}
fossil open .rep.fossil

# FOSSIL-4103 FSL_JSON_E_DB_NOT_VALID
#     Fossil repository db file is not valid.
write_file nope.fossil {
This is not a fossil repo. It ought to be a SQLite db with a well-known schema,
but it is actually just a block of text.
}
fossil_json HAI -R nope.fossil -expectError
test json-RC-4103-CLI-exit {$CODE != 0}
if { $JR ne "" } {
  test_json_envelope json-RC-4103-CLI {fossil timestamp command procTimeUs \
    procTimeMs resultCode resultText} {payload}
  test json-RC-4103 {[dict get $JR resultCode] eq "FOSSIL-4103"}
} else {
  test json-RC-4103 0 knownBug
}

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

test_cleanup













<




















>
>
>
>
>
>
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
#     Fossil repository db file could not be found.
fossil close
fossil_json HAI -expectError
test json-RC-4102-CLI-exit {$CODE != 0}
test_json_envelope json-RC-4102-CLI-exit {fossil timestamp command procTimeUs \
procTimeMs resultCode resultText} {payload}
test json-RC-4102 {[dict get $JR resultCode] eq "FOSSIL-4102"}


# FOSSIL-4103 FSL_JSON_E_DB_NOT_VALID
#     Fossil repository db file is not valid.
write_file nope.fossil {
This is not a fossil repo. It ought to be a SQLite db with a well-known schema,
but it is actually just a block of text.
}
fossil_json HAI -R nope.fossil -expectError
test json-RC-4103-CLI-exit {$CODE != 0}
if { $JR ne "" } {
  test_json_envelope json-RC-4103-CLI {fossil timestamp command procTimeUs \
    procTimeMs resultCode resultText} {payload}
  test json-RC-4103 {[dict get $JR resultCode] eq "FOSSIL-4103"}
} else {
  test json-RC-4103 0 knownBug
}

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

test_cleanup

if { $_fossil_user eq "" } {
  unset ::env(FOSSIL_USER)
} else {
  set ::env(FOSSIL_USER) $_fossil_user
}
Changes to test/merge1.test.
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
  111 - This is line one OF the demo program - 1111
  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 t23 {
  <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<
  111 - This is line ONE of the demo program - 1111
  ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||||||
  111 - This is line one of the demo program - 1111
  ======= MERGED IN content follows ==================================
  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 <<<<<<<<<<<<<<<
  111 - This is line one OF the demo program - 1111
  ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||||||
  111 - This is line one of the demo program - 1111
  ======= MERGED IN content follows ==================================
  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
}
fossil 3-way-merge t1 t3 t2 a32
test merge1-2.1 {[same_file t32 a32]}
fossil 3-way-merge t1 t2 t3 a23
test merge1-2.2 {[same_file t23 a23]}

write_file_indented t1 {
  111 - This is line one of the demo program - 1111
  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







|

|

|

|






|

|

|

|





|

|







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
  111 - This is line one OF the demo program - 1111
  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 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
  444 - If all goes well, we will be pleased - 4444
  555 - we think it well and other stuff too - 5555
}
fossil 3-way-merge t1 t3 t2 a32 -expectError
test merge1-2.1 {[same_file t32 a32]}
fossil 3-way-merge t1 t2 t3 a23 -expectError
test merge1-2.2 {[same_file t23 a23]}

write_file_indented t1 {
  111 - This is line one of the demo program - 1111
  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
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
write_file_indented t3 {
  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 <<<<<<<<<<<<<<<
  ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||||||
  111 - This is line one of the demo program - 1111
  ======= MERGED IN content follows ==================================
  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
  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 <<<<<<<<<<<<<<<
  000 - Zero lines added to the beginning of - 0000
  111 - This is line one of the demo program - 1111
  ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||||||
  111 - This is line one of the demo program - 1111
  ======= MERGED IN content follows ==================================
  >>>>>>> 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
}
fossil 3-way-merge t1 t3 t2 a32
test merge1-4.1 {[same_file t32 a32]}
fossil 3-way-merge t1 t2 t3 a23
test merge1-4.2 {[same_file t23 a23]}

write_file_indented t1 {
  111 - This is line one of the demo program - 1111
  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







|
|

|


|






|


|

|
|





|

|







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
write_file_indented t3 {
  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
  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)
  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)
  >>>>>>> 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
}
fossil 3-way-merge t1 t3 t2 a32 -expectError
test merge1-4.1 {[same_file t32 a32]}
fossil 3-way-merge t1 t2 t3 a23 -expectError
test merge1-4.2 {[same_file t23 a23]}

write_file_indented t1 {
  111 - This is line one of the demo program - 1111
  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
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
  KLMN
  OPQR
  STUV
  XYZ.
}
write_file_indented t23 {
  abcd
  <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<
  efgh 2
  ijkl 2
  mnop 2
  qrst
  uvwx
  yzAB 2
  CDEF 2
  GHIJ 2
  ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||||||
  efgh
  ijkl
  mnop
  qrst
  uvwx
  yzAB
  CDEF
  GHIJ
  ======= MERGED IN content follows ==================================
  efgh
  ijkl
  mnop 3
  qrst 3
  uvwx 3
  yzAB 3
  CDEF
  GHIJ
  >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  KLMN
  OPQR
  STUV
  XYZ.
}
fossil 3-way-merge t1 t2 t3 a23
test merge1-7.1 {[same_file t23 a23]}

write_file_indented t2 {
  abcd
  efgh 2
  ijkl 2
  mnop 







|








|








|








|





|







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
  KLMN
  OPQR
  STUV
  XYZ.
}
write_file_indented t23 {
  abcd
  <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<< (line 2)
  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
  yzAB
  CDEF
  GHIJ
  ======= MERGED IN content follows =============================== (line 2)
  efgh
  ijkl
  mnop 3
  qrst 3
  uvwx 3
  yzAB 3
  CDEF
  GHIJ
  >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  KLMN
  OPQR
  STUV
  XYZ.
}
fossil 3-way-merge t1 t2 t3 a23 -expectError
test merge1-7.1 {[same_file t23 a23]}

write_file_indented t2 {
  abcd
  efgh 2
  ijkl 2
  mnop 
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
  KLMN
  OPQR
  STUV
  XYZ.
}
write_file_indented t23 {
  abcd
  <<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<
  efgh 2
  ijkl 2
  mnop 
  qrst
  uvwx
  yzAB 2
  CDEF 2
  GHIJ 2
  ||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||||||
  efgh
  ijkl
  mnop
  qrst
  uvwx
  yzAB
  CDEF
  GHIJ
  ======= MERGED IN content follows ==================================
  efgh
  ijkl
  mnop 3
  qrst 3
  uvwx 3
  yzAB 3
  CDEF
  GHIJ
  >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  KLMN
  OPQR
  STUV
  XYZ.
}
fossil 3-way-merge t1 t2 t3 a23
test merge1-7.2 {[same_file t23 a23]}

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

test_cleanup







|








|








|








|





|





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
  KLMN
  OPQR
  STUV
  XYZ.
}
write_file_indented t23 {
  abcd
  <<<<<<< 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
  uvwx
  yzAB
  CDEF
  GHIJ
  ======= MERGED IN content follows =============================== (line 2)
  efgh
  ijkl
  mnop 3
  qrst 3
  uvwx 3
  yzAB 3
  CDEF
  GHIJ
  >>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
  KLMN
  OPQR
  STUV
  XYZ.
}
fossil 3-way-merge t1 t2 t3 a23 -expectError
test merge1-7.2 {[same_file t23 a23]}

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

test_cleanup
Changes to test/merge2.test.
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
############################################################################
#
# Tests of the delta mechanism.
#

test_setup ""

set filelist [glob $testdir/*]
foreach f $filelist {
  if {[file isdir $f]} continue
  set base [file root [file tail $f]]
  if {[string match "utf16*" $base]} continue
  set f1 [read_file $f]
  write_file t1 $f1
  for {set i 0} {$i<100} {incr i} {







|







16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
############################################################################
#
# Tests of the delta mechanism.
#

test_setup ""

set filelist [lsort [glob $testdir/*]]
foreach f $filelist {
  if {[file isdir $f]} continue
  set base [file root [file tail $f]]
  if {[string match "utf16*" $base]} continue
  set f1 [read_file $f]
  write_file t1 $f1
  for {set i 0} {$i<100} {incr i} {
Changes to test/merge3.test.
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
############################################################################
#
# Tests of the 3-way merge
#

test_setup ""

proc merge-test {testid basis v1 v2 result} {
  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
  set x [read_file t4]

  regsub -all {<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <+} $x \
              {MINE:} x

  regsub -all {\|\|\|\|\|\|\| COMMON ANCESTOR content follows \|+} $x {COM:} x


  regsub -all {======= MERGED IN content follows =+} $x {YOURS:} x


  regsub -all {>>>>>>> END MERGE CONFLICT >+} $x {END} x

  set x [split [string trim $x] \n]
  set result [string trim $result]
  if {$x!=$result} {
    protOut "  Expected \[$result\]"
    protOut "       Got \[$x\]"
    test merge3-$testid 0
  } else {







|



|

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







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
############################################################################
#
# Tests of the 3-way merge
#

test_setup ""

proc merge-test {testid basis v1 v2 result {fossil_args ""}} {
  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
  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 \
    {>>>>>>> END MERGE CONFLICT >+} \
    $x {END} x
  set x [split [string trim $x] \n]
  set result [string trim $result]
  if {$x!=$result} {
    protOut "  Expected \[$result\]"
    protOut "       Got \[$x\]"
    test merge3-$testid 0
  } else {
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
  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
}
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
}
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
}
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
}
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
}
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
}
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
} {







|








|








|








|








|








|







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
  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
} {
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
  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
}
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
} {







|







145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
  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
} {
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
  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
}
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
}

merge-test 30 {
  1 2 3 4 5 6 7 8 9
} {
  1 2 3 4 5 6 7 9
} {
  1 3 4 5 6 7 8 9







|








|







200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
  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
} {
  1 3 4 5 6 7 8 9
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
  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
}
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
}

merge-test 40 {
  2 3 4 5 6 7 8
} {
  3 4 5 6 7 8
} {
  2 3 4 5 6 7







|








|







255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
  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
} {
  2 3 4 5 6 7
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
  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
}
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
}

merge-test 50 {
  2 3 4 5 6 7 8
} {
  2 3 4 5 6 7
} {
  3 4 5 6 7 8







|








|







310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
  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
} {
  3 4 5 6 7 8
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
  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
}
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
}

merge-test 60 {
  1 2 3 4 5 6 7 8 9
} {
  1 2b 3 4 5 6 7 8 9
} {
  1 2 3 4 5 6 7 9







|








|







364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
  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
} {
  1 2 3 4 5 6 7 9
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
  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
}
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
}

merge-test 70 {
  1 2 3 4 5 6 7 8 9
} {
  1 2 3 4 5 6 7 9
} {
  1 2b 3 4 5 6 7 8 9







|








|







419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
  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
} {
  1 2b 3 4 5 6 7 8 9
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
  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
}
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
}

merge-test 80 {
  2 3 4 5 6 7 8
} {
  2b 3 4 5 6 7 8
} {
  2 3 4 5 6 7







|








|







474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
  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
} {
  2 3 4 5 6 7
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
  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
}
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
}

merge-test 90 {
  2 3 4 5 6 7 8
} {
  2 3 4 5 6 7
} {
  2b 3 4 5 6 7 8







|








|







529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
  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
} {
  2b 3 4 5 6 7 8
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
  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
}
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
}

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
} {
  1 2b 3 4 5 7 8 9 a b c d e







|








|







584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
  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
} {
  1 2b 3 4 5 7 8 9 a b c d e
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
  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
}
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
}

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

test_cleanup







|








|




630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
  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
Changes to test/merge4.test.
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
############################################################################
#
# Tests of the 3-way merge
#

test_setup ""

proc merge-test {testid basis v1 v2 result1 result2} {
  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 3-way-merge t1 t3 t2 t5
  set x [read_file t4]
  regsub -all {<<<<<<< BEGIN MERGE CONFLICT.*<<} $x {>} x
  regsub -all {\|\|\|\|\|\|\|.*=======} $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.*<<} $y {>} y
  regsub -all {\|\|\|\|\|\|\|.*=======} $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\]"
    test merge4-$testid 0







|



|
|

|
|



|
|







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
############################################################################
#
# Tests of the 3-way merge
#

test_setup ""

proc merge-test {testid basis v1 v2 result1 result2 {fossil_args ""}} {
  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\]"
    test merge4-$testid 0
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
  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
}
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
} {







|







59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
  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
} {
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
  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 <
}
merge-test 1003 {
  2 3 4 5 6 7 8
} {
  2b 3b 4 5 6 7b 8b
} {
  2 3 4c 5c 6c 7 8
} {







|







81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
  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
} {
Changes to test/merge5.test.
14
15
16
17
18
19
20

21

22
23
24
25
26
27
28
#   http://www.hwaci.com/drh/
#
############################################################################
#
# Tests of the "merge" command
#


puts "Skipping Merge5 tests"

protOut {
fossil sqlite3 --no-repository reacts badly to SQL dumped from
repositories created from fossil older than version 2.0.
}
test merge5-sqlite3-issue false knownBug
test_cleanup_then_return








>
|
>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#   http://www.hwaci.com/drh/
#
############################################################################
#
# Tests of the "merge" command
#

if {! $::QUIET} {
  puts "Skipping Merge5 tests"
}
protOut {
fossil sqlite3 --no-repository reacts badly to SQL dumped from
repositories created from fossil older than version 2.0.
}
test merge5-sqlite3-issue false knownBug
test_cleanup_then_return

Changes to test/merge_renames.test.
260
261
262
263
264
265
266
267
268

269
270
271
272
273
274
275

276
277
278
279
280
281
282
283
fossil update trunk
write_file f1 "f1.2"
fossil add f1
fossil commit -b b2 -m "add f1"

fossil update trunk
fossil merge b1
fossil merge b2
test_status_list merge_renames-8-1 $RESULT {

  WARNING: no common ancestor for f1
}

fossil revert
fossil merge --integrate b1
fossil merge b2
test_status_list merge_renames-8-2 $RESULT {

  WARNING: no common ancestor for f1
}

#############################################
#  Test 9                                   #
#  Merging a delete/rename/add combination  #
#############################################








|

>
|




|

>
|







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
fossil update trunk
write_file f1 "f1.2"
fossil add f1
fossil commit -b b2 -m "add f1"

fossil update trunk
fossil merge b1
fossil merge b2 -expectError
test_status_list merge_renames-8-1 $RESULT {
  MERGE f1
  WARNING: 1 merge conflicts
}

fossil revert
fossil merge --integrate b1
fossil merge b2 -expectError
test_status_list merge_renames-8-2 $RESULT {
  MERGE f1
  WARNING: 1 merge conflicts
}

#############################################
#  Test 9                                   #
#  Merging a delete/rename/add combination  #
#############################################

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
  ADDED f1
}
test_status_list merge_renames-9-1 $RESULT $expectedMerge
fossil changes
test_status_list merge_renames-9-2 $RESULT "
  MERGED_WITH [commit_id b]
  ADDED_BY_MERGE f1
  RENAMED f2
  DELETED f2 (overwritten by rename)
"
test_file_contents merge_renames-9-3 f1 "f1.1"
test_file_contents merge_renames-9-4 f2 "f1"

# Undo and ensure a dry run merge results in no changes
fossil undo
test_status_list merge_renames-9-5 $RESULT {
  UNDO f1
  UNDO f2
}
fossil merge -n b
test_status_list merge_renames-9-6 $RESULT "
  $expectedMerge
  REMINDER: this was a dry run - no files were actually changed.
"
test merge_renames-9-7 {[fossil changes] eq ""}

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







|
|










|







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
  ADDED f1
}
test_status_list merge_renames-9-1 $RESULT $expectedMerge
fossil changes
test_status_list merge_renames-9-2 $RESULT "
  MERGED_WITH [commit_id b]
  ADDED_BY_MERGE f1
  RENAMED f1  ->  f2
  DELETED f2  ->  f2 (overwritten by rename)
"
test_file_contents merge_renames-9-3 f1 "f1.1"
test_file_contents merge_renames-9-4 f2 "f1"

# Undo and ensure a dry run merge results in no changes
fossil undo
test_status_list merge_renames-9-5 $RESULT {
  UNDO f1
  UNDO f2
}
fossil merge -n b -expectError
test_status_list merge_renames-9-6 $RESULT "
  $expectedMerge
  REMINDER: this was a dry run - no files were actually changed.
"
test merge_renames-9-7 {[fossil changes] eq ""}

###################################################################
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
test_status_list merge_renames-10-4 $RESULT {
  RENAME f1 -> f2
  RENAME f2 -> f1
}
test_file_contents merge_renames-10-5 f1 "f1"
test_file_contents merge_renames-10-6 f2 "f2"
test_status_list merge_renames-10-7 [fossil changes] "
  RENAMED f1
  RENAMED f2
  BACKOUT [commit_id trunk]
"
fossil commit -m "swap back" ;# V

fossil merge b
test_status_list merge_renames-10-8 $RESULT {
  UPDATE f1







|
|







368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
test_status_list merge_renames-10-4 $RESULT {
  RENAME f1 -> f2
  RENAME f2 -> f1
}
test_file_contents merge_renames-10-5 f1 "f1"
test_file_contents merge_renames-10-6 f2 "f2"
test_status_list merge_renames-10-7 [fossil changes] "
  RENAMED f1  ->  f2
  RENAMED f2  ->  f1
  BACKOUT [commit_id trunk]
"
fossil commit -m "swap back" ;# V

fossil merge b
test_status_list merge_renames-10-8 $RESULT {
  UPDATE f1
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
  ADD f2
}
fossil merge trunk
fossil commit -m "merge trunk" --tag c4
fossil mv --hard f2 f2n
test_status_list merge_renames-13-3 $RESULT "
  RENAME f2 f2n
  MOVED_FILE $repoDir/f2
"
fossil commit -m "renamed f2->f2n" --tag c5

fossil update trunk
fossil merge b
test_status_list merge_renames-13-4 $RESULT {ADDED f2n}
fossil commit -m "merge f2n" --tag m1 --tag c6







|







495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
  ADD f2
}
fossil merge trunk
fossil commit -m "merge trunk" --tag c4
fossil mv --hard f2 f2n
test_status_list merge_renames-13-3 $RESULT "
  RENAME f2 f2n
  MOVED_FILE [file normalize $repoDir]/f2
"
fossil commit -m "renamed f2->f2n" --tag c5

fossil update trunk
fossil merge b
test_status_list merge_renames-13-4 $RESULT {ADDED f2n}
fossil commit -m "merge f2n" --tag m1 --tag c6
Changes to test/merge_warn.test.
38
39
40
41
42
43
44
45
46

47
48
49
50
51
52
53
54
55
56
57
58
59
write_file f4 "f4"
fossil add f4
fossil commit -m "add f4"

fossil update trunk
write_file f1 "f1.1"
write_file f3 "f3.1"
fossil merge --integrate mrg
test_status_list merge_warn-1 $RESULT {

  WARNING: no common ancestor for f2
  DELETE f1
  WARNING: local edits lost for f1
  ADDED f3 (overwrites an unmanaged file)
  WARNING: 1 merge conflicts
  WARNING: 1 unmanaged files were overwritten
}
test merge_warn-2 {
  [string first "ignoring --integrate: mrg is not a leaf" $RESULT]>=0
}

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








|

>
|

|
|
|
<







38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

53
54
55
56
57
58
59
write_file f4 "f4"
fossil add f4
fossil commit -m "add f4"

fossil update trunk
write_file f1 "f1.1"
write_file f3 "f3.1"
fossil merge --integrate mrg -expectError
test_status_list merge_warn-1 $RESULT {
  WARNING: 1 unmanaged files were overwritten
  WARNING: 2 merge conflicts
  DELETE f1
  MERGE f2
  ADDED f3 (overwrites an unmanaged file), original copy backed up locally
  WARNING: local edits lost for f1

}
test merge_warn-2 {
  [string first "ignoring --integrate: mrg is not a leaf" $RESULT]>=0
}

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

Changes to test/revert.test.
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
# Test with a single filename argument
#
revert-test 1-2 f0 {
  UNMANAGE f0
} -changes {
  DELETED f1
  EDITED f2
  RENAMED f3n
} -addremove {
  ADDED f0
} -exists {f0 f2 f3n} -notexists f3

revert-test 1-3 f1 {
  REVERT   f1
} -changes {
  ADDED f0
  EDITED f2
  RENAMED f3n
} -exists {f0 f1 f2 f3n} -notexists f3

revert-test 1-4 f2 {
  REVERT   f2
} -changes {
  ADDED f0
  DELETED f1
  RENAMED f3n
} -exists {f0 f2 f3n} -notexists {f1 f3}

# Both files involved in a rename are reverted regardless of which filename
# is used as an argument to 'fossil revert'
#
revert-test 1-5 f3 {
  REVERT   f3







|









|







|







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
# Test with a single filename argument
#
revert-test 1-2 f0 {
  UNMANAGE f0
} -changes {
  DELETED f1
  EDITED f2
  RENAMED f3  ->  f3n
} -addremove {
  ADDED f0
} -exists {f0 f2 f3n} -notexists f3

revert-test 1-3 f1 {
  REVERT   f1
} -changes {
  ADDED f0
  EDITED f2
  RENAMED f3  ->  f3n
} -exists {f0 f1 f2 f3n} -notexists f3

revert-test 1-4 f2 {
  REVERT   f2
} -changes {
  ADDED f0
  DELETED f1
  RENAMED f3  ->  f3n
} -exists {f0 f2 f3n} -notexists {f1 f3}

# Both files involved in a rename are reverted regardless of which filename
# is used as an argument to 'fossil revert'
#
revert-test 1-5 f3 {
  REVERT   f3
Added test/rewrite-test-output.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
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
#!/usr/bin/env tclsh

# Script to anonymise test results for comparison.
# - Replaces hashes, pids and similar with fixed strings
# - Rewrites temporary paths to standardise them in output

# Pick up options
set EXTRA 0
set i [lsearch $argv -extra]
while { $i >= 0 } {
  incr EXTRA
  set argv [lreplace $argv $i $i]
  set i [lsearch $argv -extra]
}

# With no arguments or "-", use stdin.
set fname "-"
if { [llength $argv] > 0 } {
  set fname [lindex $argv 0]
}

# Any -options, or an empty first argument, is an error.
if { [llength $argv] > 1 || [regexp {^-.+} $fname] } {
  puts stderr "Error: argument error"
  puts stderr "usage: \[-extra\] [file tail $argv0] ?FILE"
  puts stderr "       Rewrite test output to ease comparison of outputs."
  puts stderr "       With -extra, more output is rewritten as is summaries"
  puts stderr "       to make diff(1) mor euseful across runs and platforms."
  exit 1
} elseif { $fname ne "-" && ! [file exists $fname] } {
  puts stderr "File does not exist: '$fname'"
  exit 1
}

proc common_rewrites { line testname } {
  # Normalise the fossil commands with path as just fossil
  regsub {^(?:[A-Z]:)?/.*?/fossil(?:\.exe)? } $line {fossil } line
  if {[string match "Usage: *" $line]} {
    regsub {^(Usage: )/.*?/fossil(?:\.exe)? } $line {\1fossil } line
    regsub {^(Usage: )[A-Z]:\\.*?\\fossil(?:\.exe)? } $line {\1fossil } line
  }

  # Accept 40 and 64 byte hashes as such
  regsub -all {[[:<:]][0-9a-f]{40}[[:>:]]} $line HASH line
  regsub -all {[[:<:]][0-9a-f]{64}[[:>:]]} $line HASH line

  # Date and time
  regsub -all {[[:<:]]\d{4}-\d\d-\d\d \d\d:\d\d:\d\d[[:>:]]} $line {YYYY-mm-dd HH:MM:SS} line
  if { [lsearch -exact {"amend" "wiki"} $testname] >= 0 } {
    # With embedded T and milliseconds
    regsub { \d{4}-\d\d-\d\dT\d\d:\d\d:\d\d\.\d{3}$} $line { YYYY-mm-ddTHH:MM:SS.NNN} line
  }
  if { [lsearch -exact {"amend" "th1-hooks" "wiki"} $testname] >= 0 } {
    regsub {[[:<:]]\d{4}-\d\d-\d\d[[:>:]]} $line {YYYY-mm-dd} line
  }

  # Timelines have HH:MM:SS [HASH], but don't mess with the zero'ed version.
  regsub {^(?!00:00:00 \[0000000000\])\d\d:\d\d:\d\d \[[0-9a-f]{10}\] } $line {HH:MM:SS [HASH] } line

  # Temporary directories
  regsub -all {(?:[A-Z]:)?/.*?/repo_\d+/\d+_\d+} $line {/TMP/repo_PID/SEC_SEQ} line
  # Home directories only seem present with .fossil or _fossil. Simplify to .fossil.
  regsub -all {(?:[A-Z]:)?/.*?/home_\d+/[._]fossil[[:>:]]} $line {/TMP/home_PID/.fossil} line

  # Users in output
  regsub { (\(user: )[^\)]*\)$} $line { \1USER)} line

  return $line
}

#
# tests/tests_unix/tests_windows contain tuples of
#
# 1. A regular expression to match current line
# 2. A substitution for the current line
#
# Some common patterns applicable to multiples tests are appended below.
#
# The common_rewrites procedure is run first, so use e.g. HASH as needed.
#

dict set tests "amend" {
  {^(fossil artifact) [0-9a-f]{10}}
      {\1 HASH}
  {^U [^ ]+$}
      {U USER}
  {^Z [0-9a-f]{32}$}
      {Z CHECKSUM}
  {^(ed -s \./ci-comment-).*?(\.txt)$}
      {\1UNIQ\2}
  {^(fossil amend HASH -date \{?)\d\d/\d\d/\d{4}}
      {\1dd/mm/YYYY}
  {^(fossil amend HASH -date \{.* )\d{4}(\})$}
      {\1YYYY\2}
  {^(fossil amend HASH -date \{.* )\d\d:}
      {\1HH:}
  {^(fossil amend HASH -date \{)[A-Z][a-z]{2} [A-Z][a-z]{2} [ 0-9]\d }
      {\1Day Mon dd }
  {(\] Edit \[)[0-9a-f]{16}.[0-9a-f]{10}(\]: )}
      {\1HASH1|HASH2\2}
  {(\] Edit \[.*?&dp=)[0-9a-f]{16}}
      {\1dp=HASH}
}

dict set tests "cmdline" {
  {^(fossil test-echo --args) .*/}
      {\1 /TMP/}
  {^(g\.nameOfExe =) \[[^\]]+[/\\]fossil(?:\.exe)?\]$}
      {\1 [/PATH/FOSSILCMD]}
  {^(argv\[0\] =) \[[^\]]+[/\\]fossil(?:\.exe)?\]$}
      {\1 [/PATH/FOSSILCMD]}
}

dict set tests "contains-selector" {
  {^(fossil test-contains-selector) .*?/(compare-selector.css )}
      {\1 /TMP/\2}
}

dict set tests "json" {
  {^(Content-Length) \d+$}
      {\1 LENGTH}
  {^(Cookie: fossil-)[0-9a-f]{16}(\=HASH%2F)\d+\.\d+(%2Fanonymous)$}
      {\1CODE\2NOW\3}
  {^(GET /json/cap\?authToken\=HASH)/\d+\.\d+/(anonymous )}
      {\1/NOW/\2}
  {^(Cookie: fossil-)[0-9a-f]{16}\=[0-9A-F]{50}%2F[0-9a-f]{16}%2F(.*)$}
      {\1CODE=SHA1%2FCODE%2F\2}
  {("authToken":").+?(")}
      {\1AUTHTOKEN\2}
  {("averageArtifactSize":)\d+()}
      {\1SIZE\2}
  {("compiler":").+?(")}
      {\1COMPILER\2}
  {("loginCookieName":").+?(")}
      {\1COOKIE\2}
  {("manifestVersion":"\[)[0-9a-f]{10}(\]")}
      {\1HASH\2}
  {("manifestYear":")\d{4}(")}
      {\1YYYY\2}
  {("name":").+?(")}
      {\1NAME\2}
  {("password":")[0-9a-f]+(")}
      {\1PASSWORD\2}
  {("projectCode":")[0-9a-f]{40}(")}
      {\1HASH\2}
  {("procTimeMs":)\d+}
      {\1MSEC}
  {("procTimeUs":)\d+}
      {\1USEC}
  {("releaseVersion":")\d+\.\d+(")}
      {\1VERSION\2}
  {("releaseVersionNumber":")\d+(")}
      {\1VERSION_NUMBER\2}
  {("timestamp":)\d+}
      {\1SEC}
  {("seed":)\d+()}
      {\1SEED\2}
  {("uid":)\d+()}
      {\1UID\2}
  {("uncompressedArtifactSize":)\d+()}
      {\1SIZE\2}
  {("user":").+?(")}
      {\1USER\2}
  {("version":"YYYY-mm-dd HH:MM:SS )\[[0-9a-f]{10}\] \(\d+\.\d+\.\d+\)"}
      {\1[HASH] (major.minor.patch)}
  {^(Date:) [A-Z][a-z]{2}, \d\d? [A-Z][a-z]{2} \d{4} \d\d:\d\d:\d\d [-+]\d{4}$}
      {\1 Day, dd Mon YYYY HH:MM:SS TZ}
}

dict set tests "merge_renames" {
  {^(size: {7})\d+( bytes)$}
      {\1N\2}
  {^(type: {7}Check-in by ).+?( on YYYY-mm-dd HH:MM:SS)$}
      {\1USER\2}
}

dict set tests "set-manifest" {
  {^(project-code: )[0-9a-f]{40}$}
      {\1HASH} line
}

dict set tests "stash" {
  {^(---|\+\+\+) NUL$}
      {\1 /dev/null}
  {(^    1: \[)[0-9a-f]{14}(\] on YYYY-mm-dd HH:MM:SS)$}
      {\1HASH\2}
  {(^    1: \[)[0-9a-f]{14}(\] from YYYY-mm-dd HH:MM:SS)$}
      {\1HASH\2}
}

dict set tests "th1" {
  {^(fossil test-th-source) (?:[A-Z]:)?.*?/(th1-)\d+([.]th1)$}
      {\1 /TMP/\2PID\3}
  {^(?:[A-Z]:)?[/\\].*?[/\\]fossil(?:\.exe)?$}
      {/PATH/FOSSILCMD}
  {[[:<:]](Content-Security-Policy[[:>:]].*'nonce-)[0-9a-f]{48}(';)}
      {\1NONCE\2}
  {^(<link rel="stylesheet" href="/style.css\?id=)[0-9a-f]+(" type="text/css">)$}
      {\1ID\2}
  {^\d+\.\d{3}(s by)$}
      {N.MMM\1}
  {^(Fossil) \d+\.\d+ \[[0-9a-f]{10}\] (YYYY-mm-dd HH:MM:SS)$}
      {\1 N.M [HASH] \2}
  {^(<script nonce=")[0-9a-f]{48}(">/\* style\.c:)\d+}
      {\1NONCE\2LINENO}
}

dict set tests "th1-docs" {
  {^(check-ins:    ).*}
      {\1COUNT}
  {^(local-root:   ).*}
      {\1/PATH/}
  {^(repository:   ).*}
      {\1/PATH/REPO}
  {^(comment:      ).*}
      {\1/COMMENT/}
  {^(tags:         ).*}
      {\1/TAGS/}
  {(--ipaddr 127\.0\.0\.1) .*? (--localauth)}
      {\1 REPO \2}
}

dict set tests "th1-hooks" {
  {^(?:/[^:]*/fossil|[A-Z]:\\[^:]*\\fossil\.exe): (unknown command:|use \"help\")}
      {fossil: \1}
  {^(project-code: )[0-9a-f]{40}$}
      {\1HASH}
}

dict set tests "th1-tcl" {
  {^(fossil test-th-render --open-config) \{?.*?[/\\]test[/\\]([^/\\]*?)\}?$}
      {\1 /CHECKOUT/test/\2}
  {^(fossil)(?:\.exe)?( 3 \{test-th-render --open-config )(?:\{[A-Z]:)?[/\\].*?[/\\]test[/\\](th1-tcl9.txt\})\}?$}
      {\1\2/CHECKOUT/test/\3}
  {^\d{10}$}
      {SEC}
}

dict set tests "unversioned" {
  {^(fossil user new uvtester.*) \d+$}
      {\1 PASSWORD}
  {^(fossil .*http://uvtester:)\d+(@localhost:)\d+}
      {\1PASSWORD\2PORT}
  {^(Pull from http://uvtester@localhost:)\d+}
      {\1PORT}
  {^(ERROR \(1\): Usage:) .*?[/\\]fossil(?:\.exe)? (unversioned)}
      {\1 /PATH/fossil \2}
  {^(Started Fossil server, pid \")\d+(\", port \")\d+}
      {\1PID\2PORT}
  {^(Now in client directory \")(?:[A-Z]:)?/.*?/uvtest_\d+_\d+\"}
      {\1/TMP/uvtest_SEC_SEQ}
  {^(Stopped Fossil server, pid \")\d+(\", using argument \")(?:\d+|[^\"]*\.stopper)(\")}
      {\1PID\2PID_OR_SCRIPT\3}
  {^(This is unversioned file #4\.) \d+ \d+}
      {\1 PID SEC}
  {^(This is unversioned file #4\. PID SEC) \d+ \d+}
      {\1 PID SEC}
  {^[0-9a-f]{12}( YYYY-mm-dd HH:MM:SS *)(\d+)( *)\2( unversioned4.txt)$}
      {HASH        \1SZ\3SZ\4}
  {^[0-9a-f]{40}$}
      {\1HASH}
  {^((?:Clone|Pull)? done, wire bytes sent: )\d+(  received: )\d+(  remote: )(?:127\.0.0\.1|::1)$}
      {\1SENT\2RECV\3LOCALIP}
  {^(project-id: )[0-9a-f]{40}$}
      {\1HASH}
  {^(server-id:  )[0-9a-f]{40}$}
      {\1HASH}
  {^(admin-user: uvtester \(password is ").*("\))$}
      {\1PASSWORD\2}
  {^(repository:   ).*?/uvtest_\d+_\d+/(uvrepo.fossil)$}
      {\1/TMP/uvtest_SEC_SEQ/\2}
  {^(local-root:   ).*?/uvtest_\d+_\d+/$}
      {\1/TMP/uvtest_SEC_SEQ/}
  {^(project-code: )[0-9a-f]{40}$}
      {\1HASH}
}

dict set tests "utf" {
  {^(fossil test-looks-like-utf) (?:[A-Z]:)?/.*?/([^/\\]*?)\}?$}
      {\1 /TMP/test/\2}
  {^(File ")(?:[A-Z]:)?/.*?/(utf-check-\d+-\d+-\d+-\d+.jnk" has \d+ bytes\.)$}
      {\1/TMP/\2}
}

dict set tests "wiki" {
  {^(fossil (?:attachment|wiki) .*--technote )[0-9a-f]{21}$}
      {\1HASH}
  {^(fossil (?:attachment|wiki) .* (?:a13|f15|fa) --technote )[0-9a-f]+$}
      {\1ID}
  {^[0-9a-f]{40}( YYYY-mm-dd HH:MM:SS)}
      {HASH\1}
  {(\] Add attachment \[/artifact/)[0-9a-f]{16}(|)}
      {\1HASH\2}
  { (to tech note \[/technote/)[0-9a-f]{16}\|[0-9a-f]{10}(\] \(user:)}
      {\1HASH1|HASH2\2}
  {^(ambiguous tech note id: )[0-9a-f]+$}
      {\1ID}
  {^(Attached fa to tech note )[0-9a-f]{21}(?:[0-9a-f]{19})?\.$}
      {\1HASH.}
  {^(Date:) [A-Z][a-z]{2}, \d\d? [A-Z][a-z]{2} \d{4} \d\d:\d\d:\d\d [-+]\d{4}$}
      {\1 Day, dd Mon YYYY HH:MM:SS TZ}
  {(Content-Security-Policy.*'nonce-)[0-9a-f]{48}(';)}
      {\1NONCE\2}
  {^(<link rel="stylesheet" href="/style.css\?id=)[0-9a-f]+(" type="text/css">)$}
      {\1ID\2}
  {^(added by )[^ ]*( on)$}
      {\1USER\2}
  {^(<script nonce=['\"])[0-9a-f]{48}(['\"]>/\* [a-z]+\.c:)\d+}
      {\1NONCE\2LINENO}
  {^(<script nonce=['\"])[0-9a-f]{48}(['\"]>)$}
      {\1NONCE\2}
  {^(projectCode: ")[0-9a-f]{40}(",)$}
      {\1HASH\2}
  {^\d+\.\d+(s by)$}
      {N.SUB\1}
  {^(window\.fossil.version = ")\d+\.\d+ \[[0-9a-f]{10}\] (YYYY-mm-dd HH:MM:SS(?: UTC";)?)$}
      {\1N.M [HASH] \2}
  {^(Fossil) \d+\.\d+ \[[0-9a-f]{10}\]( YYYY-mm-dd HH:MM:SS)$}
      {\1 N.M [HASH]\2}
  {^(type:       Wiki-edit by ).+?( on YYYY-mm-dd HH:MM:SS)$$}
      {\1USER\2}
  {^(size:       )\d+( bytes)$}
      {\1N\2}
  {^U [^ ]+$}
      {U USER}
  {^Z [0-9a-f]{32}$}
      {Z CHECKSUM}
}

#
# Some pattersn are used in multiple groups
#

set testnames {"th1" "th1-docs" "th1-hooks"}
set pat {^((?:ERROR \(1\): )?/[*]{5} Subprocess) \d+ (exit)}
set sub {\1 PID \2}
foreach testname $testnames {
  dict lappend tests $testname $pat $sub
}

set testnames {"th1-docs" "th1-hooks"}
set pat {(?:[A-Z]:)?/.*?/(test-http-(?:in|out))-\d+-\d+-\d+(\.txt)}
set sub {/TMP/\1-PID-SEQ-SEC\2}
foreach testname $testnames {
  dict lappend tests $testname $pat $sub
}

set testnames {"json" "th1" "wiki"}
set pat {^(Content-Length:) \d+$}
set sub {\1 LENGTH}
foreach testname $testnames {
  dict lappend tests $testname $pat $sub
}

set testnames {"th1" "wiki"}
set pat {^\d+\.\d+(s by)$}
set sub {N.SUB\1}
foreach testname $testnames {
  dict lappend tests $testname $pat $sub
}

#
# Main
#

if { $fname eq "-" } {
  set fd stdin
} else {
  set fd [open $fname r]
}

# Platforms we detect
set UNKOWN_PLATFORM 0
set UNIX 1
set WINDOWS 2
set CYGWIN 3

# One specific wiki test creates repetitive output of varying length
set wiki_f13_cmd1 "fossil wiki create {timestamp of 2399999} f13 --technote 2399999"
set wiki_f13_cmd2 "fossil wiki list --technote --show-technote-ids"
set wiki_f13_cmd3 "fossil wiki export a13 --technote ID"
set collecting_f3 0
set collecting_f3_verbose 0

# Collected lines for summaries in --extra mode
set amend_ed_lines [list]
set amend_ed_failed 0
set symlinks_lines [list]
set symlinks_failed 0
set test_simplify_name_lines [list]
set test_simplify_name_failed 0

# State information s we progress
set check_json_empty_line 0
set lineno 0
set platform $UNKOWN_PLATFORM
set prev_line ""
set testname ""

while { [gets $fd line] >= 0 } {   
  incr lineno

  if { $lineno == 1 } {
    if { [string index $line 0] in {"\UFFEF" "\UFEFF"} } {
      set line [string range $line 1 end]
    }
  }

  # Remove RESULT status while matching (inserted again in output).
  # If collecting lines of output, include $result_prefix as needed.
  regexp {^(RESULT \([01]\): )?(.*)} $line match result_prefix line

  if { [regsub {^\*{5} ([^ ]+) \*{6}$} $line {\1} new_testname] } {
    # Pick up test name for special handling below
    set testname "$new_testname"
  } elseif { [regexp {^\*{5} End of } $line] } {
    # Test done.  Handle --extra before resetting.
    if { $EXTRA } {
      if { $testname eq "symlinks" } {
        if { $symlinks_failed } {
          foreach l $symlinks_lines {
            puts "$l"
          }
        } else {
          puts "All symlinks tests OK (not run on Windows)"
        }
      }
      regsub {(: )\d+( errors so far)} $line {\1N\2} line
    }
    set testname ""
  } elseif { $testname ne "" } {
    if { $platform == $UNKOWN_PLATFORM } {
      if { [regexp {^[A-Z]:/.*?/fossil\.exe } $line] } {
        set platform $WINDOWS
      } elseif { [regexp {^/.*?/fossil\.exe } $line] } {
        # No drive, but still .exe - must be CYGWIN
        set platform $CYGWIN
      } elseif { [regexp {^/.*?/fossil } $line] } {
        set platform $UNIX
      }
    }

    # Do common and per testname rewrites
    set line [common_rewrites $line $testname]
    if { [dict exists $tests $testname] } {
      foreach {pat sub} [dict get $tests $testname] {
        regsub $pat $line $sub line
      }
    }

    # On Windows, HTTP headers may get printed with an extra newline
    if { $testname eq "json" } {
      if { $check_json_empty_line == 1 } {
        if { "$result_prefix$line" eq "" } {
          set check_json_empty_line 2
          continue
        }
        set check_json_empty_line 0
      } elseif { [regexp {^(?:$|GET |POST |[A-Z][A-Za-z]*(?:-[A-Z][A-Za-z]*)*: )} $line] } {
        set check_json_empty_line 1
      } else {
        if { $check_json_empty_line == 2 } {
          # The empty line we skipped was meant to be followed by a new
          # HTTP header or empty line, but it was not.
          puts ""
        }
        set check_json_empty_line 0
      }
    }

    # Summarise repetitive output of varying length for f13 in wiki test
    if { $testname eq "wiki" } {
      if { $collecting_f3 == 2 } {
        if { $collecting_f3_verbose == 1 && [regexp {^HASH } $line] } {
          incr collecting_f3_verbose
        } elseif { $line eq $wiki_f13_cmd3 } {
          incr collecting_f3
          puts "\[...\]"
        } else {
          continue
        }
      } elseif { $collecting_f3 == 1 } {
        if { $line eq $wiki_f13_cmd2 } {
          incr collecting_f3
        } elseif { $collecting_f3_verbose == 0 } {
          incr collecting_f3_verbose
        }
      } elseif { $line eq $wiki_f13_cmd1 } {
        incr collecting_f3
      }
    }

    if { $EXTRA } {
      if { $line eq "ERROR (0): " && $platform == $WINDOWS } {
        if { [string match "fossil http --in *" $prev_line] } {
          continue
        }
      }
      if { $testname eq "amend" } {
        # The amend-comment-5.N tests are not run on Windows
        if { $line eq "fossil amend {} -close" } {
          if { $amend_ed_failed } {
            foreach l $amend_ed_lines {
              puts "$l"
            }
          } else {
            puts "All amend tests based on ed -s OK (not run on Windows)"
          }
          set amend_ed_lines [list]
        } elseif { [llength $amend_ed_lines] } {
          if { [regexp {^test amend-comment-5\.\d+ (.*)} $line match status] } {
            lappend amend_ed_lines "$result_prefix$line"
            if { $status ne "OK" } {
              incr amend_ed_failed
            }
            continue
          } elseif { [string range $line 0 4] eq "test " } {
            # Handle change in tests by simply emitting what we got
            foreach l $amend_ed_lines {
              puts "$l"
            }
            set amend_ed_lines [list]
          } else {
            lappend amend_ed_lines "$result_prefix$line"
            continue
          }
        } elseif { $line eq "fossil settings editor {ed -s}" } {
          lappend amend_ed_lines "$result_prefix$line"
          continue
        }
      } elseif { $testname eq "cmdline" } {
        if { [regexp {^(fossil test-echo) (.*)} $line match test args] } {
          if { ($platform == $UNIX && $args in {"*" "*.*"})
               || ($platform == $WINDOWS && $args eq "--args /TMP/fossil-cmd-line-101.txt")
               || ($platform == $CYGWIN && $args in {"*" "*.*"}) } {
            set line "$test ARG_FOR_PLATFORM"
          }
        }
      } elseif { $testname eq "commit-warning" } {
        if { [regexp {^(micro-smile|pale facepalm) .*} $line match desc] } {
          set line "$desc PLATFORM_SPECIFIC_BYTES"
        }
      } elseif { $testname eq "file1" } {
        # test-simplify-name with question marks is specific to Windows
        # They all immediately preceed "fossil test-relative-name --chdir . ."
        if { $line eq "fossil test-relative-name --chdir . ." } {
          if { $test_simplify_name_failed } {
            foreach l $test_simplify_name_lines {
              puts "$l"
            }
          } else {
            puts "ALL Windows specific test-relative-name tests OK (if on Windows)"
          }
          set test_simplify_name_lines [list]
        } elseif { [regexp {^fossil test-simplify-name .*([/\\])\?\1} $line] } {
          lappend test_simplify_name_lines $line
          continue
        } elseif { [llength $test_simplify_name_lines] } {
          if { [regexp {^test simplify-name-\d+ (.*)} $line match status] } {
            if { $status ne "OK" } {
              incr test_simplify_name_failed
            }
          }
          lappend test_simplify_name_lines "$result_prefix$line"
          continue
        }
      } elseif { $testname eq "settings-repo" } {
        if { [regexp {^fossil test-th-eval (?:--open-config )?\{setting case-sensitive\}$} $prev_line] } {
          if { ($platform == $UNIX && $line eq "on")
               || ($platform == $WINDOWS && $line eq "off")
               || ($platform == $CYGWIN && $line eq "off")
               } {
            set line "EXPECTED_FOR_PLATFORM"
          }
        }
      } elseif { $testname eq "symlinks" } {
        # Collect all lines and post-process at the end
        lappend symlinks_lines "$result_prefix$line"
        if { [regexp {^test symlinks-[^ ]* (.*)} $line match status] } {
          if { $status ne "OK" } {
            #TODO: incr symlinks_failed
          }
        }
        continue
      } elseif { $testname in {"th1" "th1-docs" "th1-hooks"} } {
        # Special case that spans a couple of tests
        # "Subprocess PID exit(0)" is sent on stderr on Unix. On Windows, there is no output
        if { [regexp {^(ERROR \(1\): )?/\*{5} Subprocess PID exit\(0\) \*{5}/$} $line match prefix] } {
          if { $prefix eq "" } {
            continue
          } elseif { $prefix eq "ERROR (1): " } {
            set line "RESULT (0): "
          }
        } elseif { $testname eq "th1" } {
          if { [regexp {^fossil test-th-eval --vfs ([^ ]+) \{globalState vfs\}$} $line match vfs] } {
            if { ($platform == $UNIX && $vfs == "unix-dotfile")
                 || ($platform == $WINDOWS && $vfs == "win32-longpath")
                 || ($platform == $CYGWIN && $vfs == "win32-longpath") } {
              regsub $vfs $line {EXEPECTED_VFS} line
            }
          } elseif { $prev_line eq "fossil test-th-eval --vfs EXEPECTED_VFS {globalState vfs}" } {
            # Replace $vfs from previous line
            regsub "^$vfs\$" $line {EXEPECTED_VFS} line
          } elseif { $prev_line eq "fossil test-th-eval {set tcl_platform(platform)}" } {
            if { $platform == $UNIX } {
              regsub {^unix$} $line {EXPECTED_PLATFORM} line
            } elseif { $platform == $WINDOWS } {
              regsub {^windows$} $line {EXPECTED_PLATFORM} line
            } elseif { $platform == $CYGWIN } {
              regsub {^unix$} $line {EXPECTED_PLATFORM} line
            }
          } elseif { [string match "fossil test-th-eval --th-trace *" $prev_line] } {
            if { ($result_prefix eq "RESULT (1): " && $line eq "")
                 || ($result_prefix eq "" && $line eq "ERROR (0): ") } {
              set result_prefix ""
              set line "RESULT (0): / ERROR (1): "
            }
          }
        } elseif { $testname eq "th1-docs" } {
          # In th1-docs, the fossil check-out is exposed in various states.
          regsub {(^project-code:) CE59BB9F186226D80E49D1FA2DB29F935CCA0333} $line {\1 HASH} line
          if { [regexp {^merged-from:  HASH YYYY-mm-dd HH:MM:SS UTC$} $line] } {
            continue
          }
        }
      }
    }
  } elseif { $EXTRA } {
    # Fix up summaries to be generic and easy to diff(1)
    if { [regsub {(^\*{5} (Final|Ignored) results: )\d+} $line {\1N} line] } {
      regsub {\d+} $line {N} line
    } elseif { [regexp {^(\*{5} (?:Considered failure|Ignored failure|Skipped test))s: (.*)} $line match desc vals] } {
      if { $vals ne ""} {
        foreach val [split $vals " "] {
          puts "$desc: $val"
        }
        continue
      }
    }
  }

  # Not exactly correct if we continue'd, but OK for the purpose
  set prev_line "$result_prefix$line"
  puts "$prev_line"
}
Changes to test/set-manifest.test.
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
test_setup

#### Verify classic behavior of the manifest setting

# Setting is off by default, and there are no extra files.
fossil settings manifest
test "set-manifest-1" {[regexp {^manifest *$} $RESULT]}
set filelist [glob -nocomplain manifest*]
test "set-manifest-1-n" {[llength $filelist] == 0}

# Classic behavior: TRUE value creates manifest and manifest.uuid
set truths [list true on 1]
foreach v $truths {
  fossil settings manifest $v
  test "set-manifest-2-$v" {$RESULT eq ""}
  fossil settings manifest
  test "set-manifest-2-$v-a" {[regexp "^manifest\\s+\\(local\\)\\s+$v\\s*$" $RESULT]}
  set filelist [glob manifest*]
  test "set-manifest-2-$v-n" {[llength $filelist] == 2}
  foreach f $filelist {
    test "set-manifest-2-$v-f-$f" {[file isfile $f]}
  }
}

# ... and manifest.uuid is the checkout's hash







|









|







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
test_setup

#### Verify classic behavior of the manifest setting

# Setting is off by default, and there are no extra files.
fossil settings manifest
test "set-manifest-1" {[regexp {^manifest *$} $RESULT]}
set filelist [lsort [glob -nocomplain manifest*]]
test "set-manifest-1-n" {[llength $filelist] == 0}

# Classic behavior: TRUE value creates manifest and manifest.uuid
set truths [list true on 1]
foreach v $truths {
  fossil settings manifest $v
  test "set-manifest-2-$v" {$RESULT eq ""}
  fossil settings manifest
  test "set-manifest-2-$v-a" {[regexp "^manifest\\s+\\(local\\)\\s+$v\\s*$" $RESULT]}
  set filelist [lsort [glob manifest*]]
  test "set-manifest-2-$v-n" {[llength $filelist] == 2}
  foreach f $filelist {
    test "set-manifest-2-$v-f-$f" {[file isfile $f]}
  }
}

# ... and manifest.uuid is the checkout's hash
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
# Classic behavior: FALSE value removes manifest and manifest.uuid
set falses [list false off 0]
foreach v $falses {
  fossil settings manifest $v
  test "set-manifest-3-$v" {$RESULT eq ""}
  fossil settings manifest
  test "set-manifest-3-$v-a" {[regexp "^manifest\\s+\\(local\\)\\s+$v\\s*$" $RESULT]}
  set filelist [glob -nocomplain manifest*]
  test "set-manifest-3-$v-n" {[llength $filelist] == 0}
}


# Classic behavior: unset removes manifest and manifest.uuid
fossil unset manifest
test "set-manifest-4" {$RESULT eq ""}
fossil settings manifest
test "set-manifest-4-a" {[regexp {^manifest *$} $RESULT]}
set filelist [glob -nocomplain manifest*]
test "set-manifest-4-n" {[llength $filelist] == 0}


##### Tags Manifest feature extends the manifest setting

# Manifest Tags: use letters r, u, and t to select each of manifest,
# manifest.uuid, and manifest.tags files.
set truths [list r u t ru ut rt rut]
foreach v $truths {
  fossil settings manifest $v
  test "set-manifest-5-$v" {$RESULT eq ""}
  fossil settings manifest
  test "set-manifest-5-$v-a" {[regexp "^manifest\\s+\\(local\\)\\s+$v\\s*$" $RESULT]}
  set filelist [glob manifest*]
  test "set-manifest-5-$v-n" {[llength $filelist] == [string length $v]}
  foreach f $filelist {
    test "set-manifest-5-$v-f-$f" {[file isfile $f]}
  }
}

# Quick check for tags applied in trunk







|









|













|







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
# Classic behavior: FALSE value removes manifest and manifest.uuid
set falses [list false off 0]
foreach v $falses {
  fossil settings manifest $v
  test "set-manifest-3-$v" {$RESULT eq ""}
  fossil settings manifest
  test "set-manifest-3-$v-a" {[regexp "^manifest\\s+\\(local\\)\\s+$v\\s*$" $RESULT]}
  set filelist [lsort [glob -nocomplain manifest*]]
  test "set-manifest-3-$v-n" {[llength $filelist] == 0}
}


# Classic behavior: unset removes manifest and manifest.uuid
fossil unset manifest
test "set-manifest-4" {$RESULT eq ""}
fossil settings manifest
test "set-manifest-4-a" {[regexp {^manifest *$} $RESULT]}
set filelist [lsort [glob -nocomplain manifest*]]
test "set-manifest-4-n" {[llength $filelist] == 0}


##### Tags Manifest feature extends the manifest setting

# Manifest Tags: use letters r, u, and t to select each of manifest,
# manifest.uuid, and manifest.tags files.
set truths [list r u t ru ut rt rut]
foreach v $truths {
  fossil settings manifest $v
  test "set-manifest-5-$v" {$RESULT eq ""}
  fossil settings manifest
  test "set-manifest-5-$v-a" {[regexp "^manifest\\s+\\(local\\)\\s+$v\\s*$" $RESULT]}
  set filelist [lsort [glob manifest*]]
  test "set-manifest-5-$v-n" {[llength $filelist] == [string length $v]}
  foreach f $filelist {
    test "set-manifest-5-$v-f-$f" {[file isfile $f]}
  }
}

# Quick check for tags applied in trunk
Changes to test/settings-repo.test.
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
set all_settings [get_all_settings]

foreach name $all_settings {
  #
  # HACK: Make 100% sure that there are no non-default setting values
  #       present anywhere.
  #



  fossil unset $name --exact --global

  fossil unset $name --exact

  #
  # NOTE: Query for the hard-coded default value of this setting and
  #       save it.
  #
  fossil test-th-eval "setting $name"
  set defaults($name) [normalize_result]
}

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

fossil settings bad-setting some_value

test settings-set-bad-local {
  [normalize_result] eq "no such setting: bad-setting"
}

fossil settings bad-setting some_value --global

test settings-set-bad-global {
  [normalize_result] eq "no such setting: bad-setting"
}

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

fossil unset bad-setting

test settings-unset-bad-local {
  [normalize_result] eq "no such setting: bad-setting"
}

fossil unset bad-setting --global

test settings-unset-bad-global {
  [normalize_result] eq "no such setting: bad-setting"
}

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

fossil settings ssl some_value

test settings-set-ambiguous-local {
  [normalize_result] eq
  "ambiguous setting \"ssl\" - might be: ssl-ca-location ssl-identity"
}

fossil settings ssl some_value --global

test settings-set-ambiguous-global {
  [normalize_result] eq
  "ambiguous setting \"ssl\" - might be: ssl-ca-location ssl-identity"
}

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

fossil unset ssl

test settings-unset-ambiguous-local {
  [normalize_result] eq
  "ambiguous setting \"ssl\" - might be: ssl-ca-location ssl-identity"
}

fossil unset ssl --global

test settings-unset-ambiguous-global {
  [normalize_result] eq
  "ambiguous setting \"ssl\" - might be: ssl-ca-location ssl-identity"
}

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







>
>
>
|
>












|





|







|





|







|






|








|






|







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
set all_settings [get_all_settings]

foreach name $all_settings {
  #
  # HACK: Make 100% sure that there are no non-default setting values
  #       present anywhere.
  #
  if {$name eq "manifest"} {
    fossil unset $name --exact --global -expectError
  } else {
    fossil unset $name --exact --global
  }
  fossil unset $name --exact

  #
  # NOTE: Query for the hard-coded default value of this setting and
  #       save it.
  #
  fossil test-th-eval "setting $name"
  set defaults($name) [normalize_result]
}

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

fossil settings bad-setting some_value -expectError

test settings-set-bad-local {
  [normalize_result] eq "no such setting: bad-setting"
}

fossil settings bad-setting some_value --global -expectError

test settings-set-bad-global {
  [normalize_result] eq "no such setting: bad-setting"
}

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

fossil unset bad-setting -expectError

test settings-unset-bad-local {
  [normalize_result] eq "no such setting: bad-setting"
}

fossil unset bad-setting --global -expectError

test settings-unset-bad-global {
  [normalize_result] eq "no such setting: bad-setting"
}

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

fossil settings ssl some_value -expectError

test settings-set-ambiguous-local {
  [normalize_result] eq
  "ambiguous setting \"ssl\" - might be: ssl-ca-location ssl-identity"
}

fossil settings ssl some_value --global -expectError

test settings-set-ambiguous-global {
  [normalize_result] eq
  "ambiguous setting \"ssl\" - might be: ssl-ca-location ssl-identity"
}

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

fossil unset ssl -expectError

test settings-unset-ambiguous-local {
  [normalize_result] eq
  "ambiguous setting \"ssl\" - might be: ssl-ca-location ssl-identity"
}

fossil unset ssl --global -expectError

test settings-unset-ambiguous-global {
  [normalize_result] eq
  "ambiguous setting \"ssl\" - might be: ssl-ca-location ssl-identity"
}

###############################################################################
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
    [regexp -- [string map [list %name% $name] $pattern(5)] $data]
  }

  fossil test-th-eval --open-config "setting $name"
  set data [normalize_result]

  test settings-set-check2-versionable-$name {
    $data eq $value
  }

  file delete $fileName

  fossil settings $name --exact
  set data [normalize_result]








|







244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
    [regexp -- [string map [list %name% $name] $pattern(5)] $data]
  }

  fossil test-th-eval --open-config "setting $name"
  set data [normalize_result]

  test settings-set-check2-versionable-$name {
    $data eq ""
  }

  file delete $fileName

  fossil settings $name --exact
  set data [normalize_result]

Changes to test/settings.test.
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
  set data [normalize_result]

  test settings-query-local-$name {
    [regexp -- [string map [list %name% $name] $pattern(1)] $data] ||
    [regexp -- [string map [list %name% $name] $pattern(2)] $data]
  }




  fossil settings $name --exact --global

  set data [normalize_result]

  if {$name eq "manifest"} {
    test settings-query-global-$name {
      $data eq "cannot set 'manifest' globally"
    }
  } else {
    test settings-query-global-$name {
      [regexp -- [string map [list %name% $name] $pattern(1)] $data] ||
      [regexp -- [string map [list %name% $name] $pattern(2)] $data]
    }
  }
}

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

fossil settings bad-setting

test settings-query-bad-local {
  [normalize_result] eq "no such setting: bad-setting"
}

fossil settings bad-setting --global

test settings-query-bad-global {
  [normalize_result] eq "no such setting: bad-setting"
}

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

test_cleanup







>
>
>
|
>
















|





|








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
  set data [normalize_result]

  test settings-query-local-$name {
    [regexp -- [string map [list %name% $name] $pattern(1)] $data] ||
    [regexp -- [string map [list %name% $name] $pattern(2)] $data]
  }

  if {$name eq "manifest"} {
    fossil settings $name --exact --global -expectError
  } else {
    fossil settings $name --exact --global
  }
  set data [normalize_result]

  if {$name eq "manifest"} {
    test settings-query-global-$name {
      $data eq "cannot set 'manifest' globally"
    }
  } else {
    test settings-query-global-$name {
      [regexp -- [string map [list %name% $name] $pattern(1)] $data] ||
      [regexp -- [string map [list %name% $name] $pattern(2)] $data]
    }
  }
}

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

fossil settings bad-setting -expectError

test settings-query-bad-local {
  [normalize_result] eq "no such setting: bad-setting"
}

fossil settings bad-setting --global -expectError

test settings-query-bad-global {
  [normalize_result] eq "no such setting: bad-setting"
}

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

test_cleanup
Changes to test/stash.test.
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
test stash-1-list-1 {[regexp {^1: \[[0-9a-z]+\] on } [first_data_line]]}
test stash-1-list-2 {[regexp {^\s+stash 1\s*$} [second_data_line]]}

set diff_stash_1 {DELETE f1
Index: f1
==================================================================
--- f1
+++ f1
@@ -1,1 +0,0 @@
-f1

CHANGED f2
--- f2
+++ f2
@@ -1,1 +1,1 @@
-f2
+f2.1

CHANGED f3n
--- f3n
+++ f3n

ADDED f0
Index: f0
==================================================================
--- f0
+++ f0
@@ -0,0 +1,1 @@
+f0}

########
# fossil stash show|cat ?STASHID? ?DIFF-OPTIONS?
# fossil stash [g]diff ?STASHID? ?DIFF-OPTIONS?







|

















|







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
test stash-1-list-1 {[regexp {^1: \[[0-9a-z]+\] on } [first_data_line]]}
test stash-1-list-2 {[regexp {^\s+stash 1\s*$} [second_data_line]]}

set diff_stash_1 {DELETE f1
Index: f1
==================================================================
--- f1
+++ /dev/null
@@ -1,1 +0,0 @@
-f1

CHANGED f2
--- f2
+++ f2
@@ -1,1 +1,1 @@
-f2
+f2.1

CHANGED f3n
--- f3n
+++ f3n

ADDED f0
Index: f0
==================================================================
--- /dev/null
+++ f0
@@ -0,0 +1,1 @@
+f0}

########
# fossil stash show|cat ?STASHID? ?DIFF-OPTIONS?
# fossil stash [g]diff ?STASHID? ?DIFF-OPTIONS?
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
  UPDATE f2
  UPDATE f3n
  ADDED  f0
} -changes {
  ADDED      f0
  MISSING    f1
  EDITED     f2
  RENAMED    f3n
} -addremove {
  DELETED  f1
} -exists {f0 f2 f3n} -notexists {f1 f3}

# Confirm there is no longer a stash saved
fossil stash list
test stash-2-list {[first_data_line] eq "empty stash"}


# Test stashed mv without touching the file system
# Issue reported by email to fossil-users
#   from Warren Young, dated Tue, 9 Feb 2016 01:22:54 -0700
#   with checkin [b8c7af5bd9] plus a local patch on CentOS 5
#   64 bit intel, 8-byte pointer, 4-byte integer
# Stashed renamed file said:
# fossil: ./src/delta.c:231: checksum: Assertion '...' failed.
# Should be triggered by this stash-WY-1 test.
fossil checkout --force c1
fossil clean
fossil mv --soft f1 f1new
stash-test WY-1 {save -m "Reported 2016-02-09"} {
  REVERT   f1
  DELETE   f1new
} -changes {
} -addremove {
} -exists {f1 f2 f3} -notexists {f1new} -knownbugs {-code -result}
# TODO: add tests that verify the saved stash is sensible. Possibly
# by applying it and checking results. But until the SQLITE_CONSTRAINT







|




















|







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
  UPDATE f2
  UPDATE f3n
  ADDED  f0
} -changes {
  ADDED      f0
  MISSING    f1
  EDITED     f2
  RENAMED    f3  ->  f3n
} -addremove {
  DELETED  f1
} -exists {f0 f2 f3n} -notexists {f1 f3}

# Confirm there is no longer a stash saved
fossil stash list
test stash-2-list {[first_data_line] eq "empty stash"}


# Test stashed mv without touching the file system
# Issue reported by email to fossil-users
#   from Warren Young, dated Tue, 9 Feb 2016 01:22:54 -0700
#   with checkin [b8c7af5bd9] plus a local patch on CentOS 5
#   64 bit intel, 8-byte pointer, 4-byte integer
# Stashed renamed file said:
# fossil: ./src/delta.c:231: checksum: Assertion '...' failed.
# Should be triggered by this stash-WY-1 test.
fossil checkout --force c1
fossil clean
fossil mv --soft f1 f1new
stash-test WY-1 {-expectError save -m "Reported 2016-02-09"} {
  REVERT   f1
  DELETE   f1new
} -changes {
} -addremove {
} -exists {f1 f2 f3} -notexists {f1new} -knownbugs {-code -result}
# TODO: add tests that verify the saved stash is sensible. Possibly
# by applying it and checking results. But until the SQLITE_CONSTRAINT
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
  ADDED f3
} -exists {f1 f2 f3} -notexists {}
#fossil status
fossil stash show
test stash-3-1-show {[normalize_result] eq {ADDED f3
Index: f3
==================================================================
--- f3
+++ f3
@@ -0,0 +1,1 @@
+f3}}
stash-test 3-1-pop {pop} {
  ADDED f3
} -changes {
  ADDED f3







|







263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
  ADDED f3
} -exists {f1 f2 f3} -notexists {}
#fossil status
fossil stash show
test stash-3-1-show {[normalize_result] eq {ADDED f3
Index: f3
==================================================================
--- /dev/null
+++ f3
@@ -0,0 +1,1 @@
+f3}}
stash-test 3-1-pop {pop} {
  ADDED f3
} -changes {
  ADDED f3
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
fossil commit -m "baseline"

fossil mv --hard f2 f2n
test_result_state stash-3-2-mv "mv --hard f2 f2n" [concat {
  RENAME f2 f2n
  MOVED_FILE} [file normalize f2] {
}] -changes {
  RENAMED f2n
} -addremove {
} -exists {f1 f2n} -notexists {f2}

stash-test 3-2 {save -m f2n} {
  REVERT f2
  DELETE f2n
} -exists {f1 f2} -notexists {f2n} -knownbugs {-result}
fossil stash show
test stash-3-2-show-1 {![regexp {\sf1} $RESULT]} knownBug
test stash-3-2-show-2 {[regexp {\sf2n} $RESULT]}
stash-test 3-2-pop {pop} {
  UPDATE f1
  UPDATE f2n
} -changes {
  RENAMED    f2n
} -addremove {
} -exists {f1 f2n} -notexists {f2}



########
# fossil stash snapshot ?-m|--comment COMMENT? ?FILES...?







|














|







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
fossil commit -m "baseline"

fossil mv --hard f2 f2n
test_result_state stash-3-2-mv "mv --hard f2 f2n" [concat {
  RENAME f2 f2n
  MOVED_FILE} [file normalize f2] {
}] -changes {
  RENAMED f2  ->  f2n
} -addremove {
} -exists {f1 f2n} -notexists {f2}

stash-test 3-2 {save -m f2n} {
  REVERT f2
  DELETE f2n
} -exists {f1 f2} -notexists {f2n} -knownbugs {-result}
fossil stash show
test stash-3-2-show-1 {![regexp {\sf1} $RESULT]} knownBug
test stash-3-2-show-2 {[regexp {\sf2n} $RESULT]}
stash-test 3-2-pop {pop} {
  UPDATE f1
  UPDATE f2n
} -changes {
  RENAMED    f2  ->  f2n
} -addremove {
} -exists {f1 f2n} -notexists {f2}



########
# fossil stash snapshot ?-m|--comment COMMENT? ?FILES...?
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
file rename -force f3 f3n
fossil mv f3 f3n
stash-test 4-3 {snapshot -m "snap 3"} {
} -changes {
  ADDED      f0
  DELETED    f1
  EDITED     f2
  RENAMED    f3n
} -addremove {
} -exists {f0 f2 f3n} -notexists {f1 f3}
fossil stash diff
test stash-4-3-diff-CODE {!$::CODE} knownBug
fossil stash show
test stash-4-3-show-1 {[regexp {DELETE f1} $RESULT]}
test stash-4-3-show-2 {[regexp {CHANGED f2} $RESULT]}







|







366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
file rename -force f3 f3n
fossil mv f3 f3n
stash-test 4-3 {snapshot -m "snap 3"} {
} -changes {
  ADDED      f0
  DELETED    f1
  EDITED     f2
  RENAMED    f3  ->  f3n
} -addremove {
} -exists {f0 f2 f3n} -notexists {f1 f3}
fossil stash diff
test stash-4-3-diff-CODE {!$::CODE} knownBug
fossil stash show
test stash-4-3-show-1 {[regexp {DELETE f1} $RESULT]}
test stash-4-3-show-2 {[regexp {CHANGED f2} $RESULT]}
Changes to test/symlinks.test.
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
set path [file dirname [info script]]

if {$is_windows} {
  puts "Symlinks are not supported on Windows."
  test_cleanup_then_return
}

fossil test-th-eval --open-config "setting allow-symlinks"

if {![string is true -strict [normalize_result]]} {
  puts "Symlinks are not enabled."
  test_cleanup_then_return
}

require_no_open_checkout

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

test_setup; set rootDir [file normalize [pwd]]




fossil test-th-eval --open-config {repository}
set repository [normalize_result]

if {[string length $repository] == 0} {
  puts "Detection of the open repository file failed."
  test_cleanup_then_return







<
<
<
<
<
<
<





>
>
>







21
22
23
24
25
26
27







28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
set path [file dirname [info script]]

if {$is_windows} {
  puts "Symlinks are not supported on Windows."
  test_cleanup_then_return
}








require_no_open_checkout

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

test_setup; set rootDir [file normalize [pwd]]

# Using tempHomePath, allow-symlinks will always be off at this point.
fossil set allow-symlinks on

fossil test-th-eval --open-config {repository}
set repository [normalize_result]

if {[string length $repository] == 0} {
  puts "Detection of the open repository file failed."
  test_cleanup_then_return
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

test symlinks-dir-1 {[file exists [file join $rootDir subdirA f1.txt]] eq 1}
test symlinks-dir-2 {[file exists [file join $rootDir symdirA f1.txt]] eq 1}
test symlinks-dir-3 {[file exists [file join $rootDir subdirA f2.txt]] eq 1}
test symlinks-dir-4 {[file exists [file join $rootDir symdirA f2.txt]] eq 1}

fossil add [file join $rootDir symdirA f1.txt]



fossil commit -m "c1"




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

fossil ls
test symlinks-dir-5 {[normalize_result] eq "symdirA/f1.txt"}

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

fossil extras
test symlinks-dir-6 {[normalize_result] eq \
"subdirA/f1.txt\nsubdirA/f2.txt\nsymdirA/f2.txt"}

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

fossil close
file delete [file join $rootDir subdirA f1.txt]

test symlinks-dir-7 {[file exists [file join $rootDir subdirA f1.txt]] eq 0}
test symlinks-dir-8 {[file exists [file join $rootDir symdirA f1.txt]] eq 0}
test symlinks-dir-9 {[file exists [file join $rootDir subdirA f2.txt]] eq 1}
test symlinks-dir-10 {[file exists [file join $rootDir symdirA f2.txt]] eq 1}

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

fossil open $repository
set code [catch {file readlink [file join $rootDir symdirA]} result]

test symlinks-dir-11 {$code == 0}
test symlinks-dir-12 {$result eq [file join $rootDir subdirA]}
test symlinks-dir-13 {[file exists [file join $rootDir subdirA f1.txt]] eq 1}
test symlinks-dir-14 {[file exists [file join $rootDir symdirA f1.txt]] eq 1}
test symlinks-dir-15 {[file exists [file join $rootDir subdirA f2.txt]] eq 1}
test symlinks-dir-16 {[file exists [file join $rootDir symdirA f2.txt]] eq 1}

###############################################################################
#
# TODO: Add tests for symbolic links as files here, including tests with the
#       "allow-symlinks" setting on and off.
#
###############################################################################

test_cleanup







>
>
>
|
>
>
>




|





|













|




|
|











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

test symlinks-dir-1 {[file exists [file join $rootDir subdirA f1.txt]] eq 1}
test symlinks-dir-2 {[file exists [file join $rootDir symdirA f1.txt]] eq 1}
test symlinks-dir-3 {[file exists [file join $rootDir subdirA f2.txt]] eq 1}
test symlinks-dir-4 {[file exists [file join $rootDir symdirA f2.txt]] eq 1}

fossil add [file join $rootDir symdirA f1.txt]
test symlinks-skip-dir-traversal {[normalize_result] eq \
"SKIP   symdirA/f1.txt"}

fossil commit -m "c1" -expectError

test symlinks-empty-commit {[normalize_result] eq \
"nothing has changed; use --allow-empty to override"}

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

fossil ls
test symlinks-dir-5 {[normalize_result] eq ""}

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

fossil extras
test symlinks-dir-6 {[normalize_result] eq \
"subdirA/f1.txt\nsubdirA/f2.txt\nsymdirA"}

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

fossil close
file delete [file join $rootDir subdirA f1.txt]

test symlinks-dir-7 {[file exists [file join $rootDir subdirA f1.txt]] eq 0}
test symlinks-dir-8 {[file exists [file join $rootDir symdirA f1.txt]] eq 0}
test symlinks-dir-9 {[file exists [file join $rootDir subdirA f2.txt]] eq 1}
test symlinks-dir-10 {[file exists [file join $rootDir symdirA f2.txt]] eq 1}

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

fossil open --force $repository
set code [catch {file readlink [file join $rootDir symdirA]} result]

test symlinks-dir-11 {$code == 0}
test symlinks-dir-12 {$result eq [file join $rootDir subdirA]}
test symlinks-dir-13 {[file exists [file join $rootDir subdirA f1.txt]] eq 0}
test symlinks-dir-14 {[file exists [file join $rootDir symdirA f1.txt]] eq 0}
test symlinks-dir-15 {[file exists [file join $rootDir subdirA f2.txt]] eq 1}
test symlinks-dir-16 {[file exists [file join $rootDir symdirA f2.txt]] eq 1}

###############################################################################
#
# TODO: Add tests for symbolic links as files here, including tests with the
#       "allow-symlinks" setting on and off.
#
###############################################################################

test_cleanup
Changes to test/tester.tcl.
18
19
20
21
22
23
24





25
26
27
28
29
30
31
32
33
34

35
36
37
38
39
40
41
# This is the main test script.  To run a regression test, do this:
#
#     tclsh ../test/tester.tcl ../bld/fossil
#
# Where ../test/tester.tcl is the name of this file and ../bld/fossil
# is the name of the executable to be tested.
#






# We use some things introduced in 8.6 such as lmap.  auto.def should
# have found us a suitable Tcl installation.
package require Tcl 8.6

set testfiledir [file normalize [file dirname [info script]]]
set testrundir [pwd]
set testdir [file normalize [file dirname $argv0]]
set fossilexe [file normalize [lindex $argv 0]]
set is_windows [expr {$::tcl_platform(platform) eq "windows"}]


if {$::is_windows} {
  if {[string length [file extension $fossilexe]] == 0} {
    append fossilexe .exe
  }
  set outside_fossil_repo [expr ![file exists "$::testfiledir\\..\\_FOSSIL_"]]
} else {







>
>
>
>
>










>







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
# This is the main test script.  To run a regression test, do this:
#
#     tclsh ../test/tester.tcl ../bld/fossil
#
# Where ../test/tester.tcl is the name of this file and ../bld/fossil
# is the name of the executable to be tested.
#
# To run a subset of tests (i.e. only one or more of the test/*.test
# scripts), append the script base names as arguments:
#
#     tclsh ../test/tester.tcl ../bld/fossil <script-basename>...
#

# We use some things introduced in 8.6 such as lmap.  auto.def should
# have found us a suitable Tcl installation.
package require Tcl 8.6

set testfiledir [file normalize [file dirname [info script]]]
set testrundir [pwd]
set testdir [file normalize [file dirname $argv0]]
set fossilexe [file normalize [lindex $argv 0]]
set is_windows [expr {$::tcl_platform(platform) eq "windows"}]
set is_cygwin [regexp {^CYGWIN} $::tcl_platform(os)]

if {$::is_windows} {
  if {[string length [file extension $fossilexe]] == 0} {
    append fossilexe .exe
  }
  set outside_fossil_repo [expr ![file exists "$::testfiledir\\..\\_FOSSIL_"]]
} else {
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
  #
  set result [list \
      access-log \
      admin-log \
      allow-symlinks \
      auto-captcha \
      auto-hyperlink \


      auto-shun \
      autosync \
      autosync-tries \
      backoffice-disable \
      backoffice-logfile \
      backoffice-nodelay \
      binary-glob \
      case-sensitive \
      chat-alert-sound \
      chat-initial-history \
      chat-inline-images \
      chat-keep-count \
      chat-keep-days \
      chat-poll-timeout \

      clean-glob \
      clearsign \
      comment-format \
      crlf-glob \
      crnl-glob \
      default-csp \
      default-perms \
      diff-binary \
      diff-command \

      dont-push \
      dotfiles \
      editor \
      email-admin \

      email-renew-interval \
      email-self \
      email-send-command \
      email-send-db \
      email-send-dir \
      email-send-method \
      email-send-relayhost \
      email-subname \
      email-url \
      empty-dirs \
      encoding-glob \
      exec-rel-paths \
      fileedit-glob \
      forbid-delta-manifests \

      gdiff-command \
      gmerge-command \
      hash-digits \
      hooks \
      http-port \
      https-login \
      ignore-glob \
      keep-glob \

      localauth \
      lock-timeout \
      main-branch \
      mainmenu \
      manifest \
      max-cache-entry \
      max-loadavg \
      max-upload \
      mimetypes \
      mtime-changes \

      pgp-command \
      preferred-diff-type \
      proxy \
      redirect-to-https \
      relative-paths \
      repo-cksum \
      repolist-skin \
      safe-html \

      self-register \
      sitemap-extra \
      ssh-command \
      ssl-ca-location \
      ssl-identity \
      tclsh \
      th1-setup \







>
>














>









>




>














>








>










>








>







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
  #
  set result [list \
      access-log \
      admin-log \
      allow-symlinks \
      auto-captcha \
      auto-hyperlink \
      auto-hyperlink-delay \
      auto-hyperlink-mouseover \
      auto-shun \
      autosync \
      autosync-tries \
      backoffice-disable \
      backoffice-logfile \
      backoffice-nodelay \
      binary-glob \
      case-sensitive \
      chat-alert-sound \
      chat-initial-history \
      chat-inline-images \
      chat-keep-count \
      chat-keep-days \
      chat-poll-timeout \
      chat-timeline-user \
      clean-glob \
      clearsign \
      comment-format \
      crlf-glob \
      crnl-glob \
      default-csp \
      default-perms \
      diff-binary \
      diff-command \
      dont-commit \
      dont-push \
      dotfiles \
      editor \
      email-admin \
      email-listid \
      email-renew-interval \
      email-self \
      email-send-command \
      email-send-db \
      email-send-dir \
      email-send-method \
      email-send-relayhost \
      email-subname \
      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 \
      keep-glob \
      large-file-size \
      localauth \
      lock-timeout \
      main-branch \
      mainmenu \
      manifest \
      max-cache-entry \
      max-loadavg \
      max-upload \
      mimetypes \
      mtime-changes \
      mv-rm-files \
      pgp-command \
      preferred-diff-type \
      proxy \
      redirect-to-https \
      relative-paths \
      repo-cksum \
      repolist-skin \
      safe-html \
      self-pw-reset \
      self-register \
      sitemap-extra \
      ssh-command \
      ssl-ca-location \
      ssl-identity \
      tclsh \
      th1-setup \
431
432
433
434
435
436
437


438
439
440
441
442
443
444
proc require_no_open_checkout {} {
  if {[info exists ::env(FOSSIL_TEST_DANGEROUS_IGNORE_OPEN_CHECKOUT)] && \
      $::env(FOSSIL_TEST_DANGEROUS_IGNORE_OPEN_CHECKOUT) eq "YES_DO_IT"} {
    return
  }
  catch {exec $::fossilexe info} res
  if {[regexp {local-root:} $res]} {


    set projectName <unknown>
    set localRoot <unknown>
    regexp -line -- {^project-name: (.*)$} $res dummy projectName
    set projectName [string trim $projectName]
    regexp -line -- {^local-root: (.*)$} $res dummy localRoot
    set localRoot [string trim $localRoot]
    error "Detected an open checkout of project \"$projectName\",\







>
>







446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
proc require_no_open_checkout {} {
  if {[info exists ::env(FOSSIL_TEST_DANGEROUS_IGNORE_OPEN_CHECKOUT)] && \
      $::env(FOSSIL_TEST_DANGEROUS_IGNORE_OPEN_CHECKOUT) eq "YES_DO_IT"} {
    return
  }
  catch {exec $::fossilexe info} res
  if {[regexp {local-root:} $res]} {
    global skipped_tests testfile
    lappend skipped_tests $testfile
    set projectName <unknown>
    set localRoot <unknown>
    regexp -line -- {^project-name: (.*)$} $res dummy projectName
    set projectName [string trim $projectName]
    regexp -line -- {^local-root: (.*)$} $res dummy localRoot
    set localRoot [string trim $localRoot]
    error "Detected an open checkout of project \"$projectName\",\
468
469
470
471
472
473
474


475
476
477
478
479
480




481
482
483
484
485
486
487
    }
    after [expr {$try * 100}]
  }
  error "Could not delete \"$path\", error: $error"
}

proc test_cleanup_then_return {} {


  uplevel 1 [list test_cleanup]
  return -code return
}

proc test_cleanup {} {
  if {$::KEEP} {return}; # All cleanup disabled?




  if {![info exists ::tempRepoPath]} {return}
  if {![file exists $::tempRepoPath]} {return}
  if {![file isdirectory $::tempRepoPath]} {return}
  set tempPathEnd [expr {[string length $::tempPath] - 1}]
  if {[string length $::tempPath] == 0 || \
      [string range $::tempRepoPath 0 $tempPathEnd] ne $::tempPath} {
    error "Temporary repository path has wrong parent during cleanup."







>
>





|
>
>
>
>







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
    }
    after [expr {$try * 100}]
  }
  error "Could not delete \"$path\", error: $error"
}

proc test_cleanup_then_return {} {
  global skipped_tests testfile
  lappend skipped_tests $testfile
  uplevel 1 [list test_cleanup]
  return -code return
}

proc test_cleanup {} {
  if {$::KEEP} {
      # To avoid errors with require_no_open_checkout, cd out of here.
      if {[info exists ::tempSavedPwd]} {cd $::tempSavedPwd; unset ::tempSavedPwd}
      return
  }
  if {![info exists ::tempRepoPath]} {return}
  if {![file exists $::tempRepoPath]} {return}
  if {![file isdirectory $::tempRepoPath]} {return}
  set tempPathEnd [expr {[string length $::tempPath] - 1}]
  if {[string length $::tempPath] == 0 || \
      [string range $::tempRepoPath 0 $tempPathEnd] ne $::tempPath} {
    error "Temporary repository path has wrong parent during cleanup."
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
  # Finally, attempt to gracefully delete the temporary home directory,
  # unless forbidden by external forces.
  if {![info exists ::tempKeepHome]} {delete_temporary_home}
}

proc delete_temporary_home {} {
  if {$::KEEP} {return}; # All cleanup disabled?
  if {$::is_windows} {
    robust_delete [file join $::tempHomePath _fossil]
  } else {
    robust_delete [file join $::tempHomePath .fossil]
  }
  robust_delete $::tempHomePath
}








|







525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
  # Finally, attempt to gracefully delete the temporary home directory,
  # unless forbidden by external forces.
  if {![info exists ::tempKeepHome]} {delete_temporary_home}
}

proc delete_temporary_home {} {
  if {$::KEEP} {return}; # All cleanup disabled?
  if {$::is_windows || $::is_cygwin} {
    robust_delete [file join $::tempHomePath _fossil]
  } else {
    robust_delete [file join $::tempHomePath .fossil]
  }
  robust_delete $::tempHomePath
}

829
830
831
832
833
834
835

836
837
838
839
840
841
842
      lappend bad_test $name
      if {$::HALT} {exit 1}
    }
  }
}
set bad_test {}
set ignored_test {}


# Return a random string N characters long.
#
set vocabulary 01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
append vocabulary "       ()*^!.eeeeeeeeaaaaattiioo   "
set nvocabulary [string length $vocabulary]
proc rand_str {N} {







>







852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
      lappend bad_test $name
      if {$::HALT} {exit 1}
    }
  }
}
set bad_test {}
set ignored_test {}
set skipped_tests {}

# Return a random string N characters long.
#
set vocabulary 01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
append vocabulary "       ()*^!.eeeeeeeeaaaaattiioo   "
set nvocabulary [string length $vocabulary]
proc rand_str {N} {
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
  set inFileName [file join $::tempPath [appendArgs test-http-in- $suffix]]
  set outFileName [file join $::tempPath [appendArgs test-http-out- $suffix]]
  set data [subst [read_file $dataFileName]]

  write_file $inFileName $data

  fossil http --in $inFileName --out $outFileName --ipaddr 127.0.0.1 \
      $repository --localauth --th-trace

  set result [expr {[file exists $outFileName] ? [read_file $outFileName] : ""}]

  if {1} {
    catch {file delete $inFileName}
    catch {file delete $outFileName}
  }







|







1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
  set inFileName [file join $::tempPath [appendArgs test-http-in- $suffix]]
  set outFileName [file join $::tempPath [appendArgs test-http-out- $suffix]]
  set data [subst [read_file $dataFileName]]

  write_file $inFileName $data

  fossil http --in $inFileName --out $outFileName --ipaddr 127.0.0.1 \
      $repository --localauth --th-trace -expectError

  set result [expr {[file exists $outFileName] ? [read_file $outFileName] : ""}]

  if {1} {
    catch {file delete $inFileName}
    catch {file delete $outFileName}
  }
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
1104
1105
1106
1107
1108
1109
1110
1111
1112







} error] != 0} {
  error "Could not write file \"$tempFile\" in directory \"$tempPath\",\
please set TEMP variable in environment, error: $error"
}

set tempHomePath [file join $tempPath home_[pid]]










if {[catch {
  file mkdir $tempHomePath
} error] != 0} {
  error "Could not make directory \"$tempHomePath\",\
please set TEMP variable in environment, error: $error"
}


protInit $fossilexe
set ::tempKeepHome 1





foreach testfile $argv {
  protOut "***** $testfile ******"
  if { [catch {source $testdir/$testfile.test} testerror testopts] } {
    test test-framework-$testfile 0
    protOut "!!!!! $testfile: $testerror"
    protOutDict $testopts"
  } else {
    test test-framework-$testfile 1
  }
  protOut "***** End of $testfile: [llength $bad_test] errors so far ******"
}

unset ::tempKeepHome; delete_temporary_home




set nErr [llength $bad_test]
if {$nErr>0 || !$::QUIET} {
  protOut "***** Final results: $nErr errors out of $test_count tests" 1
}
if {$nErr>0} {
  protOut "***** Considered failures: $bad_test" 1
}
set nErr [llength $ignored_test]
if {$nErr>0 || !$::QUIET} {
  protOut "***** Ignored results: $nErr ignored errors out of $test_count tests" 1
}
if {$nErr>0} {
  protOut "***** Ignored failures: $ignored_test" 1
}














>
>
>
>
>
>
>
>
>










>
>
>
>
>











>

>
>
>
>














>
>
>
>
>
>
>
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
1158
1159
1160
1161
1162
} error] != 0} {
  error "Could not write file \"$tempFile\" in directory \"$tempPath\",\
please set TEMP variable in environment, error: $error"
}

set tempHomePath [file join $tempPath home_[pid]]

# Close stdin to avoid errors on wrapped text for narrow terminals.
# Closing stdin means that terminal detection returns 0 width, in turn
# causing the relvant strings to be printed on a single line.
# However, closing stdin makes file descriptor 0 avaailable on some systems
# and/or TCL implementations, which triggers fossil to complain about opening
# databases using fd 0. Avoid this by opening the script, consuming fd 0.
close stdin
set possibly_fd0 [open [info script] r]

if {[catch {
  file mkdir $tempHomePath
} error] != 0} {
  error "Could not make directory \"$tempHomePath\",\
please set TEMP variable in environment, error: $error"
}


protInit $fossilexe
set ::tempKeepHome 1

# Start in tempHomePath to help avoid errors with require_no_open_checkout
set startPwd [pwd]
cd $tempHomePath

foreach testfile $argv {
  protOut "***** $testfile ******"
  if { [catch {source $testdir/$testfile.test} testerror testopts] } {
    test test-framework-$testfile 0
    protOut "!!!!! $testfile: $testerror"
    protOutDict $testopts"
  } else {
    test test-framework-$testfile 1
  }
  protOut "***** End of $testfile: [llength $bad_test] errors so far ******"
}
cd $startPwd
unset ::tempKeepHome; delete_temporary_home

# Clean up the file descriptor
close $possibly_fd0

set nErr [llength $bad_test]
if {$nErr>0 || !$::QUIET} {
  protOut "***** Final results: $nErr errors out of $test_count tests" 1
}
if {$nErr>0} {
  protOut "***** Considered failures: $bad_test" 1
}
set nErr [llength $ignored_test]
if {$nErr>0 || !$::QUIET} {
  protOut "***** Ignored results: $nErr ignored errors out of $test_count tests" 1
}
if {$nErr>0} {
  protOut "***** Ignored failures: $ignored_test" 1
}
set nSkipped [llength $skipped_tests]
if {$nSkipped>0} {
  protOut "***** Skipped tests: $skipped_tests" 1
}
if {$bad_test>0} {
  exit 1
}
Changes to test/th1-docs.test.
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
fossil test-th-eval "hasfeature tcl"

if {[normalize_result] ne "1"} {
  puts "Fossil was not compiled with Tcl support."
  test_cleanup_then_return
}

if {$::outside_fossil_repo} {
  puts "Skipping th1-docs-* tests: not in Fossil repo checkout."
  test_cleanup_then_return
} elseif ($::dirty_ckout) {
  puts "Skipping th1-docs-* tests: uncommitted changes in Fossil checkout."
  test_cleanup_then_return
}

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

test_setup ""

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

set env(TH1_ENABLE_DOCS) 1; # TH1 docs must be enabled for this test.
set env(TH1_ENABLE_TCL) 1; # Tcl integration must be enabled for this test.

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

run_in_checkout {
  set data [fossil info]
}

regexp -line -- {^repository:   (.*)$} $data dummy repository

if {[string length $repository] == 0 || ![file exists $repository]} {
  error "unable to locate repository"
}

set dataFileName [file join $::testdir th1-docs-input.txt]










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

run_in_checkout {
  set RESULT [test_fossil_http \
      $repository $dataFileName /doc/trunk/test/fileStat.th1]
}

test th1-docs-1a {[regexp {<title>Fossil: test/fileStat.th1</title>} $RESULT]}
test th1-docs-1b {[regexp {>\[[0-9a-f]{40,64}\]<} $RESULT]}
test th1-docs-1c {[regexp { contains \d+ files\.} $RESULT]}

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

test_cleanup







<
<
<
<
<
<
<
<


|








<
|
<








>
>
>
>
>
>
>
>
>



<
|
|
|
<
|






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
fossil test-th-eval "hasfeature tcl"

if {[normalize_result] ne "1"} {
  puts "Fossil was not compiled with Tcl support."
  test_cleanup_then_return
}









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

test_setup

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

set env(TH1_ENABLE_DOCS) 1; # TH1 docs must be enabled for this test.
set env(TH1_ENABLE_TCL) 1; # Tcl integration must be enabled for this test.

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


set data [fossil info]


regexp -line -- {^repository:   (.*)$} $data dummy repository

if {[string length $repository] == 0 || ![file exists $repository]} {
  error "unable to locate repository"
}

set dataFileName [file join $::testdir th1-docs-input.txt]
set origFileStat [file join $::testdir fileStat.th1]

if {![file exists $origFileStat]} {
  error "unable to locate [$origFileStat]"
}

file copy $origFileStat fileStat.th1
fossil add fileStat.th1
fossil commit -m "Add fileStat.th1"

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


set RESULT [test_fossil_http \
  $repository $dataFileName /doc/trunk/fileStat.th1]


test th1-docs-1a {[regexp {<title>Unnamed Fossil Project: fileStat.th1</title>} $RESULT]}
test th1-docs-1b {[regexp {>\[[0-9a-f]{40,64}\]<} $RESULT]}
test th1-docs-1c {[regexp { contains \d+ files\.} $RESULT]}

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

test_cleanup
Changes to test/th1-hooks.test.
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
test th1-cmd-hooks-1b {[normalize_result] eq \
{<h1><b>command_hook timeline</b></h1>
+++ some stuff here +++
<h1><b>command_hook timeline command_notify timeline</b></h1>}}

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

fossil timeline custom3; # NOTE: Bad "WHEN" argument.

test th1-cmd-hooks-1c {[normalize_result] eq \
{<h1><b>command_hook timeline</b></h1>
unknown check-in or invalid date: custom3}}

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








|







144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
test th1-cmd-hooks-1b {[normalize_result] eq \
{<h1><b>command_hook timeline</b></h1>
+++ some stuff here +++
<h1><b>command_hook timeline command_notify timeline</b></h1>}}

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

fossil timeline custom3 -expectError; # NOTE: Bad "WHEN" argument.

test th1-cmd-hooks-1c {[normalize_result] eq \
{<h1><b>command_hook timeline</b></h1>
unknown check-in or invalid date: custom3}}

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

194
195
196
197
198
199
200
201
202
203
204
205
206
207
208

fossil test3
test th1-custom-cmd-3a {[string trim $RESULT] eq \
    {<h1><b>command_hook test3</b></h1>}}

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

fossil test4

test th1-custom-cmd-4a {[first_data_line] eq \
    {<h1><b>command_hook test4</b></h1>}}

test th1-custom-cmd-4b {[regexp -- \
    {: unknown command: test4$} [second_data_line]]}








|







194
195
196
197
198
199
200
201
202
203
204
205
206
207
208

fossil test3
test th1-custom-cmd-3a {[string trim $RESULT] eq \
    {<h1><b>command_hook test3</b></h1>}}

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

fossil test4 -expectError

test th1-custom-cmd-4a {[first_data_line] eq \
    {<h1><b>command_hook test4</b></h1>}}

test th1-custom-cmd-4b {[regexp -- \
    {: unknown command: test4$} [second_data_line]]}

Changes to test/th1-tcl.test.
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
}

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

fossil test-th-render --open-config \
    [file nativename [file join $path th1-tcl3.txt]]

test th1-tcl-3 {$RESULT eq {<hr /><p class="thmainError">ERROR:\
invalid command name &quot;bad_command&quot;</p>}}

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

fossil test-th-render --open-config \
    [file nativename [file join $path th1-tcl4.txt]]

test th1-tcl-4 {$RESULT eq {<hr /><p class="thmainError">ERROR:\
divide by zero</p>}}

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

fossil test-th-render --open-config \
    [file nativename [file join $path th1-tcl5.txt]]

test th1-tcl-5 {$RESULT eq {<hr /><p class="thmainError">ERROR:\
Tcl command not found: bad_command</p>} || $RESULT eq {<hr /><p\
class="thmainError">ERROR: invalid command name &quot;bad_command&quot;</p>}}

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

fossil test-th-render --open-config \
    [file nativename [file join $path th1-tcl6.txt]]

test th1-tcl-6 {$RESULT eq {<hr /><p class="thmainError">ERROR:\
no such command:  bad_command</p>}}

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

fossil test-th-render --open-config \
    [file nativename [file join $path th1-tcl7.txt]]

test th1-tcl-7 {$RESULT eq {<hr /><p class="thmainError">ERROR:\
syntax error in expression: &quot;2**0&quot;</p>}}

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

fossil test-th-render --open-config \
    [file nativename [file join $path th1-tcl8.txt]]

test th1-tcl-8 {$RESULT eq {<hr /><p class="thmainError">ERROR:\
cannot invoke Tcl command: tailcall</p>} || $RESULT eq {<hr /><p\
class="thmainError">ERROR: tailcall can only be called from a proc or\
lambda</p>} || $RESULT eq {<hr /><p class="thmainError">ERROR: This test\
requires Tcl 8.6 or higher.</p>}}

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

fossil test-th-render --open-config \
    [file nativename [file join $path th1-tcl9.txt]]








test th1-tcl-9 {[string trim $RESULT] eq [list [file tail $fossilexe] 3 \
[list test-th-render --open-config [file nativename [file join $path \
th1-tcl9.txt]]]]}

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

fossil test-th-eval "tclMakeSafe a"
test th1-tcl-10 {[normalize_result] eq \







|







|







|
|







|







|







|
|

|







>
>
>
>
>
>
>
|







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
}

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

fossil test-th-render --open-config \
    [file nativename [file join $path th1-tcl3.txt]]

test th1-tcl-3 {$RESULT eq {<hr><p class="thmainError">ERROR:\
invalid command name &quot;bad_command&quot;</p>}}

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

fossil test-th-render --open-config \
    [file nativename [file join $path th1-tcl4.txt]]

test th1-tcl-4 {$RESULT eq {<hr><p class="thmainError">ERROR:\
divide by zero</p>}}

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

fossil test-th-render --open-config \
    [file nativename [file join $path th1-tcl5.txt]]

test th1-tcl-5 {$RESULT eq {<hr><p class="thmainError">ERROR:\
Tcl command not found: bad_command</p>} || $RESULT eq {<hr><p\
class="thmainError">ERROR: invalid command name &quot;bad_command&quot;</p>}}

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

fossil test-th-render --open-config \
    [file nativename [file join $path th1-tcl6.txt]]

test th1-tcl-6 {$RESULT eq {<hr><p class="thmainError">ERROR:\
no such command:  bad_command</p>}}

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

fossil test-th-render --open-config \
    [file nativename [file join $path th1-tcl7.txt]]

test th1-tcl-7 {$RESULT eq {<hr><p class="thmainError">ERROR:\
syntax error in expression: &quot;2**0&quot;</p>}}

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

fossil test-th-render --open-config \
    [file nativename [file join $path th1-tcl8.txt]]

test th1-tcl-8 {$RESULT eq {<hr><p class="thmainError">ERROR:\
cannot invoke Tcl command: tailcall</p>} || $RESULT eq {<hr><p\
class="thmainError">ERROR: tailcall can only be called from a proc or\
lambda</p>} || $RESULT eq {<hr><p class="thmainError">ERROR: This test\
requires Tcl 8.6 or higher.</p>}}

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

fossil test-th-render --open-config \
    [file nativename [file join $path th1-tcl9.txt]]

# Under cygwin, the printed name with Usage: strips the extension
if { $::is_cygwin && [file extension $fossilexe] eq ".exe" } {
  set fossilexeref [string range $fossilexe 0 end-4]
} else {
  set fossilexeref $fossilexe
}

test th1-tcl-9 {[string trim $RESULT] eq [list [file tail $fossilexeref] 3 \
[list test-th-render --open-config [file nativename [file join $path \
th1-tcl9.txt]]]]}

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

fossil test-th-eval "tclMakeSafe a"
test th1-tcl-10 {[normalize_result] eq \
Changes to test/th1.test.
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
###############################################################################

fossil test-th-eval "trace {}"
test th1-trace-1 {$RESULT eq {}}

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

fossil test-th-eval --th-trace "trace {}"
set normalized_result [normalize_result]

regsub -- {\n/\*\*\*\*\* Subprocess \d+ exit\(\d+\) \*\*\*\*\*/} \
    $normalized_result {} normalized_result

if {$th1Hooks} {
  test th1-trace-2 {$normalized_result eq \
{------------------ BEGIN TRACE LOG ------------------
th1-init 0x0 => 0x0<br />

------------------- END TRACE LOG -------------------}}
} else {
  test th1-trace-2 {$normalized_result eq \
      {------------------ BEGIN TRACE LOG ------------------
th1-init 0x0 => 0x0<br />
th1-setup {} => TH_OK<br />

------------------- END TRACE LOG -------------------}}
}

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

fossil test-th-eval "trace {this is a trace message.}"
test th1-trace-3 {$RESULT eq {}}

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

fossil test-th-eval --th-trace "trace {this is a trace message.}"
set normalized_result [normalize_result]

regsub -- {\n/\*\*\*\*\* Subprocess \d+ exit\(\d+\) \*\*\*\*\*/} \
    $normalized_result {} normalized_result

if {$th1Hooks} {
  test th1-trace-4 {$normalized_result eq \
      {------------------ BEGIN TRACE LOG ------------------
th1-init 0x0 => 0x0<br />
this is a trace message.
------------------- END TRACE LOG -------------------}}
} else {
  test th1-trace-4 {$normalized_result eq \
      {------------------ BEGIN TRACE LOG ------------------
th1-init 0x0 => 0x0<br />
th1-setup {} => TH_OK<br />
this is a trace message.
------------------- END TRACE LOG -------------------}}
}

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

fossil test-th-eval "defHeader {Page Title Here}"
test th1-defHeader-1 {$RESULT eq \
    {TH_ERROR: wrong # args: should be "defHeader"}}

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

fossil test-th-eval "defHeader"
test th1-defHeader-2 {[string match *<body> [normalize_result]] || \
    [string match "*<body class=\"\$current_feature\">" [normalize_result]]}



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

fossil test-th-eval "styleHeader {Page Title Here}"
test th1-header-1 {$RESULT eq {TH_ERROR: repository unavailable}}

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







|








|





|
|











|








|





|
|














|
>
>







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

fossil test-th-eval "trace {}"
test th1-trace-1 {$RESULT eq {}}

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

fossil test-th-eval --th-trace "trace {}" -expectError
set normalized_result [normalize_result]

regsub -- {\n/\*\*\*\*\* Subprocess \d+ exit\(\d+\) \*\*\*\*\*/} \
    $normalized_result {} normalized_result

if {$th1Hooks} {
  test th1-trace-2 {$normalized_result eq \
{------------------ BEGIN TRACE LOG ------------------
th1-init 0x0 => 0x0<br>

------------------- END TRACE LOG -------------------}}
} else {
  test th1-trace-2 {$normalized_result eq \
      {------------------ BEGIN TRACE LOG ------------------
th1-init 0x0 => 0x0<br>
th1-setup {} => TH_OK<br>

------------------- END TRACE LOG -------------------}}
}

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

fossil test-th-eval "trace {this is a trace message.}"
test th1-trace-3 {$RESULT eq {}}

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

fossil test-th-eval --th-trace "trace {this is a trace message.}" -expectError
set normalized_result [normalize_result]

regsub -- {\n/\*\*\*\*\* Subprocess \d+ exit\(\d+\) \*\*\*\*\*/} \
    $normalized_result {} normalized_result

if {$th1Hooks} {
  test th1-trace-4 {$normalized_result eq \
      {------------------ BEGIN TRACE LOG ------------------
th1-init 0x0 => 0x0<br>
this is a trace message.
------------------- END TRACE LOG -------------------}}
} else {
  test th1-trace-4 {$normalized_result eq \
      {------------------ BEGIN TRACE LOG ------------------
th1-init 0x0 => 0x0<br>
th1-setup {} => TH_OK<br>
this is a trace message.
------------------- END TRACE LOG -------------------}}
}

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

fossil test-th-eval "defHeader {Page Title Here}"
test th1-defHeader-1 {$RESULT eq \
    {TH_ERROR: wrong # args: should be "defHeader"}}

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

fossil test-th-eval "defHeader"
test th1-defHeader-2 {[string match *<body> [normalize_result]] || \
    [string match "*<body class=\"\$current_feature\
                    rpage-\$requested_page\
                    cpage-\$canonical_page\">" [normalize_result]]}

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

fossil test-th-eval "styleHeader {Page Title Here}"
test th1-header-1 {$RESULT eq {TH_ERROR: repository unavailable}}

###############################################################################
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
###############################################################################

fossil test-th-eval "globalState vfs"
test th1-globalState-14 {[string length $RESULT] == 0}

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

if {$is_windows} {
  set altVfs win32-longpath
} else {
  set altVfs unix-dotfile
}

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








|







1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
###############################################################################

fossil test-th-eval "globalState vfs"
test th1-globalState-14 {[string length $RESULT] == 0}

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

if {$is_windows || $is_cygwin} {
  set altVfs win32-longpath
} else {
  set altVfs unix-dotfile
}

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

1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
set sorted_result [lsort $RESULT]
protOut "Sorted: $sorted_result"
set base_commands {anoncap anycap array artifact break breakpoint \
      builtin_request_js capexpr captureTh1 catch cgiHeaderLine checkout \
      combobox continue copybtn date decorate defHeader dir enable_htmlify \
      enable_output encode64 error expr for foreach getParameter glob_match \
      globalState hascap hasfeature html htmlize http httpize if info \
      insertCsrf lappend lindex linecount list llength lsearch markdown \
      nonce proc puts query randhex redirect regexp reinitialize rename \
      render repository return searchable set setParameter setting stime \
      string styleFooter styleHeader styleScript tclReady trace unset \
      unversioned uplevel upvar utime verifyCsrf verifyLogin wiki}
set tcl_commands {tclEval tclExpr tclInvoke tclIsSafe tclMakeSafe}
if {$th1Tcl} {
  test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands $tcl_commands"]}
} else {
  test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands"]}
}







|
|
|
|







1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
set sorted_result [lsort $RESULT]
protOut "Sorted: $sorted_result"
set base_commands {anoncap anycap array artifact break breakpoint \
      builtin_request_js capexpr captureTh1 catch cgiHeaderLine checkout \
      combobox continue copybtn date decorate defHeader dir enable_htmlify \
      enable_output encode64 error expr for foreach getParameter glob_match \
      globalState hascap hasfeature html htmlize http httpize if info \
      insertCsrf lappend lindex linecount list llength lsearch markdown nonce \
      proc puts query randhex redirect regexp reinitialize rename render \
      repository return searchable set setParameter setting stime string \
      styleFooter styleHeader styleScript submenu tclReady trace unset \
      unversioned uplevel upvar utime verifyCsrf verifyLogin wiki}
set tcl_commands {tclEval tclExpr tclInvoke tclIsSafe tclMakeSafe}
if {$th1Tcl} {
  test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands $tcl_commands"]}
} else {
  test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands"]}
}
Changes to test/unversioned.test.
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
  puts "The \"sha1\" package is not available."
  test_cleanup_then_return
}

require_no_open_checkout

test_setup; set rootDir [file normalize [pwd]]




fossil test-th-eval --open-config {repository}
set repository [normalize_result]

if {[string length $repository] == 0} {
  puts "Detection of the open repository file failed."
  test_cleanup_then_return
}

write_file unversioned1.txt "This is unversioned file #1."
write_file unversioned2.txt " This is unversioned file #2. "
write_file "unversioned space.txt" "\nThis is unversioned file #3.\n"
write_file unversioned4.txt "This is unversioned file #4."
write_file unversioned5.txt "This is unversioned file #5."

set env(VISUAL) [appendArgs \
    [info nameofexecutable] " " [file join $path fake-editor.tcl]]

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








fossil unversioned
test unversioned-1 {[normalize_result] eq \
[string map [list %fossil% [file nativename $fossilexe]] {Usage: %fossil%\
unversioned add|cat|edit|export|list|revert|remove|sync|touch}]}

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

fossil unversioned list
test unversioned-2 {[normalize_result] eq {}}








>
>
>




















>
>
>
>
>
>
>
|

|







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
  puts "The \"sha1\" package is not available."
  test_cleanup_then_return
}

require_no_open_checkout

test_setup; set rootDir [file normalize [pwd]]

# Avoid delays from the backoffice.
fossil set backoffice-disable 1

fossil test-th-eval --open-config {repository}
set repository [normalize_result]

if {[string length $repository] == 0} {
  puts "Detection of the open repository file failed."
  test_cleanup_then_return
}

write_file unversioned1.txt "This is unversioned file #1."
write_file unversioned2.txt " This is unversioned file #2. "
write_file "unversioned space.txt" "\nThis is unversioned file #3.\n"
write_file unversioned4.txt "This is unversioned file #4."
write_file unversioned5.txt "This is unversioned file #5."

set env(VISUAL) [appendArgs \
    [info nameofexecutable] " " [file join $path fake-editor.tcl]]

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

# Under cygwin, the printed name with Usage: strips the extension
if { $::is_cygwin && [file extension $fossilexe] eq ".exe" } {
  set fossilexeref [string range $fossilexe 0 end-4]
} else {
  set fossilexeref $fossilexe
}

fossil unversioned -expectError
test unversioned-1 {[normalize_result] eq \
[string map [list %fossil% [file nativename $fossilexeref]] {Usage: %fossil%\
unversioned add|cat|edit|export|list|revert|remove|sync|touch}]}

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

fossil unversioned list
test unversioned-2 {[normalize_result] eq {}}

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

fossil user new uvtester "Unversioned Test User" $password
fossil user capabilities uvtester oy

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

foreach {pid port outTmpFile} [test_start_server $repository stopArg] {}

puts [appendArgs "Started Fossil server, pid \"" $pid \" ", port \"" $port \".]

set remote [appendArgs http://uvtester: $password @localhost: $port /]

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

set clientDir [file join $tempPath [appendArgs \
    uvtest_ [string trim [clock seconds] -] _ [getSeqNo]]]

set savedPwd [pwd]
file mkdir $clientDir; cd $clientDir

puts [appendArgs "Now in client directory \"" [pwd] \".]

write_file unversioned-client1.txt "This is unversioned client file #1."

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

fossil_maybe_answer y clone $remote uvrepo.fossil
fossil open -f uvrepo.fossil

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

fossil unversioned list
test unversioned-45 {[normalize_result] eq {}}

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

fossil_maybe_answer y unversioned sync $remote
test unversioned-46 {[regexp \
{Round-trips: 1   Artifacts sent: 0  received: 0
Round-trips: 1   Artifacts sent: 0  received: 0
Round-trips: 2   Artifacts sent: 0  received: 0
Round-trips: 2   Artifacts sent: 0  received: 2
\n? done, sent: \d+  received: \d+  ip: (?:127\.0\.0\.1|::1)} \
[normalize_result]]}

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

fossil unversioned ls
test unversioned-47 {[normalize_result] eq {unversioned2.txt
unversioned5.txt}}







>
|
>









>
|
>




|















|







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

fossil user new uvtester "Unversioned Test User" $password
fossil user capabilities uvtester oy

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

foreach {pid port outTmpFile} [test_start_server $repository stopArg] {}
if {! $::QUIET} {
  puts [appendArgs "Started Fossil server, pid \"" $pid \" ", port \"" $port \".]
}
set remote [appendArgs http://uvtester: $password @localhost: $port /]

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

set clientDir [file join $tempPath [appendArgs \
    uvtest_ [string trim [clock seconds] -] _ [getSeqNo]]]

set savedPwd [pwd]
file mkdir $clientDir; cd $clientDir
if {! $::QUIET} {
  puts [appendArgs "Now in client directory \"" [pwd] \".]
}
write_file unversioned-client1.txt "This is unversioned client file #1."

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

fossil clone --save-http-password $remote uvrepo.fossil
fossil open -f uvrepo.fossil

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

fossil unversioned list
test unversioned-45 {[normalize_result] eq {}}

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

fossil_maybe_answer y unversioned sync $remote
test unversioned-46 {[regexp \
{Round-trips: 1   Artifacts sent: 0  received: 0
Round-trips: 1   Artifacts sent: 0  received: 0
Round-trips: 2   Artifacts sent: 0  received: 0
Round-trips: 2   Artifacts sent: 0  received: 2
\n? done, wire bytes sent: \d+  received: \d+  remote: (?:127\.0\.0\.1|::1)} \
[normalize_result]]}

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

fossil unversioned ls
test unversioned-47 {[normalize_result] eq {unversioned2.txt
unversioned5.txt}}
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401

fossil_maybe_answer y unversioned revert $remote
test unversioned-52 {[regexp \
{Round-trips: 1   Artifacts sent: 0  received: 0
Round-trips: 1   Artifacts sent: 0  received: 0
Round-trips: 2   Artifacts sent: 0  received: 0
Round-trips: 2   Artifacts sent: 0  received: 2
\n? done, sent: \d+  received: \d+  ip: (?:127\.0\.0\.1|::1)} \
[normalize_result]]}

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

fossil unversioned list
test unversioned-53 {[regexp \
{^[0-9a-f]{12} 2016-10-01 00:00:00       30       30\







|







401
402
403
404
405
406
407
408
409
410
411
412
413
414
415

fossil_maybe_answer y unversioned revert $remote
test unversioned-52 {[regexp \
{Round-trips: 1   Artifacts sent: 0  received: 0
Round-trips: 1   Artifacts sent: 0  received: 0
Round-trips: 2   Artifacts sent: 0  received: 0
Round-trips: 2   Artifacts sent: 0  received: 2
\n? done, wire bytes sent: \d+  received: \d+  remote: (?:127\.0\.0\.1|::1)} \
[normalize_result]]}

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

fossil unversioned list
test unversioned-53 {[regexp \
{^[0-9a-f]{12} 2016-10-01 00:00:00       30       30\
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

fossil_maybe_answer y unversioned sync $remote
test unversioned-55 {[regexp \
{Round-trips: 1   Artifacts sent: 0  received: 0
Round-trips: 1   Artifacts sent: 0  received: 0
Round-trips: 2   Artifacts sent: 1  received: 0
Round-trips: 2   Artifacts sent: 1  received: 0
\n? done, sent: \d+  received: \d+  ip: (?:127\.0\.0\.1|::1)} \
[normalize_result]]}

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

fossil close
test unversioned-56 {[normalize_result] eq {}}

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

cd $savedPwd; unset savedPwd
file delete -force $clientDir

puts [appendArgs "Now in server directory \"" [pwd] \".]


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

set stopped [test_stop_server $stopArg $pid $outTmpFile]


puts [appendArgs \
    [expr {$stopped ? "Stopped" : "Could not stop"}] \
    " Fossil server, pid \"" $pid "\", using argument \"" \
    $stopArg \".]


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

fossil unversioned list
test unversioned-57 {[regexp \
{^[0-9a-f]{12} \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}       35       35\
unversioned-client1\.txt







|











>
|
>





>
|



>







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

fossil_maybe_answer y unversioned sync $remote
test unversioned-55 {[regexp \
{Round-trips: 1   Artifacts sent: 0  received: 0
Round-trips: 1   Artifacts sent: 0  received: 0
Round-trips: 2   Artifacts sent: 1  received: 0
Round-trips: 2   Artifacts sent: 1  received: 0
\n? done, wire bytes sent: \d+  received: \d+  remote: (?:127\.0\.0\.1|::1)} \
[normalize_result]]}

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

fossil close
test unversioned-56 {[normalize_result] eq {}}

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

cd $savedPwd; unset savedPwd
file delete -force $clientDir
if {! $::QUIET} {
  puts [appendArgs "Now in server directory \"" [pwd] \".]
}

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

set stopped [test_stop_server $stopArg $pid $outTmpFile]

if {! $::QUIET} {
  puts [appendArgs \
    [expr {$stopped ? "Stopped" : "Could not stop"}] \
    " Fossil server, pid \"" $pid "\", using argument \"" \
    $stopArg \".]
}

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

fossil unversioned list
test unversioned-57 {[regexp \
{^[0-9a-f]{12} \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}       35       35\
unversioned-client1\.txt
Added test/update.test.














































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
#
# Copyright (c) 2024 Preben Guldnerg <preben@guldberg.org>
#
# 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/
#
############################################################################
#
# Tests for the "update" command.
#

# Track number of tests we have set up in test_update_setup. This helps ensure
# that generated files are ordered in `fossil update --verbose` mode.
set UPDATE_TEST 0

proc test_update_setup {desc} {
    global UPDATE_TEST
    incr UPDATE_TEST
    fossil revert
    fossil update
    return [format "test-%02u-%s.txt" $UPDATE_TEST $desc]
}

# The output is in file name order, so massage $RESULT to remove initial UNCHANGED
# files. Only do this if we have the expected branch information.
proc test_update {testname message changes {fossil_args ""}} {
    fossil update --verbose {*}$fossil_args
    if { [regsub {\n-{79}\nupdated-from: [0-9a-z]{40} .*} $::RESULT {} test_result ] } {
        regsub {^(?:UNCHANGED [-a-z0-9.]+\n)*} $test_result {} test_result
    } else {
        set test_result $::RESULT
    }
    test "update-message-$testname" {$message == $test_result}
    fossil changes
    test "update-changes-$testname" {$changes == $::RESULT}
}

# Use a sequence number for file content that is not important for the test.
set UPDATE_SEQ_NO 0
proc write_seq_to_file {fname} {
    global UPDATE_SEQ_NO
    incr UPDATE_SEQ_NO
    write_file $fname "$UPDATE_SEQ_NO\n"
}

# 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
# 3. Set message and changes, the expected message message and subsequent changes
# 3. Optionally set up and commit a common base for the next steps
# 4. Commit a change to the repository (new tip)
# 5. Update to the previous version
# 6. Make changes
# 7. Call test_update to attempt and update to tip


set testname "conflict-standard"
set fname [test_update_setup $testname]
set message "CONFLICT $fname"
set changes "EDITED     $fname"
write_seq_to_file $fname
fossil add $fname
fossil commit -m "Add $fname"
fossil up previous
write_seq_to_file $fname
fossil add $fname
test_update $testname $message $changes -expectError

set testname "add-overwrites"
set fname [test_update_setup $testname]
set message "ADD $fname - overwrites an unmanaged file, original copy backed up locally"
set changes ""
write_seq_to_file $fname
fossil add $fname
fossil commit -m "Add $fname"
fossil up previous
write_seq_to_file $fname
test_update $testname $message $changes -expectError

set testname "add-standard"
set fname [test_update_setup $testname]
set message "ADD $fname"
set changes ""
write_seq_to_file $fname
fossil add $fname
fossil commit -m "Add $fname"
fossil up previous
test_update $testname $message $changes

set testname "update-change"
set fname [test_update_setup $testname]
set message "UPDATE $fname - change to unmanaged file"
set changes "DELETED    $fname"
write_seq_to_file $fname
fossil add $fname
fossil commit -m "Add $fname"
write_seq_to_file $fname
fossil commit -m "Update $fname"
fossil up previous
fossil rm --hard $fname
test_update $testname $message $changes

set testname "update-standard"
set fname [test_update_setup $testname]
set message "UPDATE $fname"
set changes ""
write_seq_to_file $fname
fossil add $fname
fossil commit -m "Add $fname"
write_seq_to_file $fname
fossil commit -m "Update $testname"
fossil up previous
test_update $testname $message $changes

set testname "update-missing"
set fname [test_update_setup $testname]
set message "UPDATE $fname"
set changes ""
write_seq_to_file $fname
fossil add $fname
fossil commit -m "Add $fname"
write_seq_to_file $fname
fossil commit -m "Update $fname"
fossil up previous
file delete $fname
test_update $testname $message $changes

set testname "conflict-deleted"
set fname [test_update_setup $testname]
set message "CONFLICT $fname - edited locally but deleted by update"
set changes ""
write_seq_to_file $fname
fossil add $fname
fossil commit -m "Add $fname"
fossil rm --hard $fname
fossil commit -m "Remove $fname"
fossil up previous
file delete $fname
test_update $testname $message $changes -expectError

set testname "remove"
set fname [test_update_setup $testname]
set message "REMOVE $fname"
set changes ""
write_seq_to_file $fname
fossil add $fname
fossil commit -m "Add $fname"
fossil rm --hard $fname
fossil commit -m "Remove $fname"
fossil up previous
test_update $testname $message $changes

set testname "merge-renamed"
set fname [test_update_setup $testname]
set message "MERGE $fname -> $fname.renamed"
set changes "EDITED     $fname.renamed"
write_file $fname "center\n"
fossil add $fname
fossil commit -m "Add $fname"
write_file $fname "top\ncenter\n"
fossil mv --hard $fname "$fname.renamed"
fossil commit -m "Update and rename $fname"
fossil up previous
write_file $fname "center\nbelow\n"
test_update $testname $message $changes

set testname "merge-standard"
set fname [test_update_setup $testname]
set message "MERGE $fname"
set changes "EDITED     $fname"
write_file $fname "center\n"
fossil add $fname
fossil commit -m "Add $fname"
write_file $fname "top\ncenter\n"
fossil commit -m "Update $fname"
fossil up previous
write_file $fname "center\nbelow\n"
test_update $testname $message $changes

# TODO: test for "Cannot merge symlink" would be platform dependent

set testname "merge-conflict"
set fname [test_update_setup $testname]
set message "MERGE $fname\n***** 1 merge conflicts in $fname"
set changes "CONFLICT   $fname"
write_seq_to_file $fname
fossil add $fname
fossil commit -m "Add $fname"
write_seq_to_file $fname
fossil commit -m "Update $fname"
fossil up previous
write_seq_to_file $fname
test_update $testname $message $changes -expectError

# TODO: test for "Cannot merge binary file"?

set testname "edited"
set fname [test_update_setup $testname]
set message "EDITED $fname\nADD $fname.other"
set changes "EDITED     $fname"
write_seq_to_file $fname
fossil add $fname
fossil commit -m "Add $fname"
write_seq_to_file "$fname.other"
fossil add $fname.other
fossil commit -m "Add $fname.other"
fossil up previous
write_seq_to_file $fname
test_update $testname $message $changes

set testname "unchanged"
set fname [test_update_setup $testname]
set message "ADD $fname\nUNCHANGED $fname.unchanged"
set changes ""
write_seq_to_file "$fname.unchanged"
fossil add "$fname.unchanged"
fossil commit -m "Add $fname.unchanged"
write_seq_to_file "$fname"
fossil add "$fname"
fossil commit -m "Add $fname"
fossil up previous
test_update $testname $message $changes

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

test_cleanup
Changes to tools/codecheck1.c.
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
        if( (acType[i]=='s' || acType[i]=='z' || acType[i]=='b') ){
          const char *zExpr = azArg[fmtArg+i];
          if( never_safe(zExpr) ){
            printf("%s:%d: Argument %d to %.*s() is not safe for"
                   " a query parameter\n",
               zFilename, lnFCall, i+fmtArg, szFName, zFCall);
             nErr++;
   
          }else if( (fmtFlags & FMT_SQL)!=0 && !is_sql_safe(zExpr) ){
            printf("%s:%d: Argument %d to %.*s() not safe for SQL\n",
               zFilename, lnFCall, i+fmtArg, szFName, zFCall);
             nErr++;
          }
        }
      }







|







602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
        if( (acType[i]=='s' || acType[i]=='z' || acType[i]=='b') ){
          const char *zExpr = azArg[fmtArg+i];
          if( never_safe(zExpr) ){
            printf("%s:%d: Argument %d to %.*s() is not safe for"
                   " a query parameter\n",
               zFilename, lnFCall, i+fmtArg, szFName, zFCall);
             nErr++;

          }else if( (fmtFlags & FMT_SQL)!=0 && !is_sql_safe(zExpr) ){
            printf("%s:%d: Argument %d to %.*s() not safe for SQL\n",
               zFilename, lnFCall, i+fmtArg, szFName, zFCall);
             nErr++;
          }
        }
      }
Changes to tools/makeheaders.c.
36
37
38
39
40
41
42
43

44
45
46
47
48
49
50
#include <stdlib.h>
#include <ctype.h>
#include <memory.h>
#include <sys/stat.h>
#include <assert.h>
#include <string.h>

#if defined( __MINGW32__) ||  defined(__DMC__) || defined(_MSC_VER) || defined(__POCC__)

#  ifndef WIN32
#    define WIN32
#  endif
#else
# include <unistd.h>
#endif








|
>







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <stdlib.h>
#include <ctype.h>
#include <memory.h>
#include <sys/stat.h>
#include <assert.h>
#include <string.h>

#if defined( __MINGW32__) || defined(__DMC__)   || \
    defined(_MSC_VER)     || defined(__POCC__)
#  ifndef WIN32
#    define WIN32
#  endif
#else
# include <unistd.h>
#endif

2224
2225
2226
2227
2228
2229
2230

2231
2232
2233
2234
2235
2236
2237
2238
    if (pToken->zText[pToken->nText-1] == '\r') { nArg--; }
    if( nArg==9 && strncmp(zArg,"INTERFACE",9)==0 ){
      PushIfMacro(0,0,0,pToken->nLine,PS_Interface);
    }else if( nArg==16 && strncmp(zArg,"EXPORT_INTERFACE",16)==0 ){
      PushIfMacro(0,0,0,pToken->nLine,PS_Export);
    }else if( nArg==15 && strncmp(zArg,"LOCAL_INTERFACE",15)==0 ){
      PushIfMacro(0,0,0,pToken->nLine,PS_Local);

    }else if( nArg==15 && strncmp(zArg,"MAKEHEADERS_STOPLOCAL_INTERFACE",15)==0 ){
      PushIfMacro(0,0,0,pToken->nLine,PS_Local);
    }else{
      PushIfMacro(0,zArg,nArg,pToken->nLine,0);
    }
  }else if( nCmd==5 && strncmp(zCmd,"ifdef",5)==0 ){
    /*
    ** Push an #ifdef.







>
|







2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
    if (pToken->zText[pToken->nText-1] == '\r') { nArg--; }
    if( nArg==9 && strncmp(zArg,"INTERFACE",9)==0 ){
      PushIfMacro(0,0,0,pToken->nLine,PS_Interface);
    }else if( nArg==16 && strncmp(zArg,"EXPORT_INTERFACE",16)==0 ){
      PushIfMacro(0,0,0,pToken->nLine,PS_Export);
    }else if( nArg==15 && strncmp(zArg,"LOCAL_INTERFACE",15)==0 ){
      PushIfMacro(0,0,0,pToken->nLine,PS_Local);
    }else if( nArg==15 &&
              strncmp(zArg,"MAKEHEADERS_STOPLOCAL_INTERFACE",15)==0 ){
      PushIfMacro(0,0,0,pToken->nLine,PS_Local);
    }else{
      PushIfMacro(0,zArg,nArg,pToken->nLine,0);
    }
  }else if( nCmd==5 && strncmp(zCmd,"ifdef",5)==0 ){
    /*
    ** Push an #ifdef.
Changes to tools/mkindex.c.
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
** legacy commands.  Test commands are unsupported commands used for testing
** and analysis only.
**
** Commands are 1st-tier by default.  If the command name begins with
** "test-" or if the command name has a "test" argument, then it becomes
** a test command.  If the command name has a "2nd-tier" argument or ends
** with a "*" character, it is second tier.  If the command name has an "alias"
** argument or ends with a "#" character, it is an alias: another name  
** (a one-to-one replacement) for a command.  Examples:
**
**        COMMAND:  abcde*
**        COMMAND:  fghij        2nd-tier
**        COMMAND:  mnopq#
**        COMMAND:  rstuv        alias
**        COMMAND:  test-xyzzy







|







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
** legacy commands.  Test commands are unsupported commands used for testing
** and analysis only.
**
** Commands are 1st-tier by default.  If the command name begins with
** "test-" or if the command name has a "test" argument, then it becomes
** a test command.  If the command name has a "2nd-tier" argument or ends
** with a "*" character, it is second tier.  If the command name has an "alias"
** argument or ends with a "#" character, it is an alias: another name
** (a one-to-one replacement) for a command.  Examples:
**
**        COMMAND:  abcde*
**        COMMAND:  fghij        2nd-tier
**        COMMAND:  mnopq#
**        COMMAND:  rstuv        alias
**        COMMAND:  test-xyzzy
Changes to tools/skintxt2config.c.
1
2
3
4
5
6
7
8
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 
/* vim: set ts=2 et sw=2 tw=80: */
/*
** Copyright (c) 2021 Stephan Beal (https://wanderinghorse.net/home/stephan/)
**
** 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".)
|







1
2
3
4
5
6
7
8
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/*
** Copyright (c) 2021 Stephan Beal (https://wanderinghorse.net/home/stephan/)
**
** 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".)
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
  end:
  fclose(f);
  if(rc){
    free(zMem);
  }else{
    *zContent = zMem;
    *nContent = fpos;
  }    
  return rc;
}

/*
** Expects zFilename to be one of the conventional skin filename
** parts. This routine converts it to config format and emits it to
** App.ostr.







|







100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
  end:
  fclose(f);
  if(rc){
    free(zMem);
  }else{
    *zContent = zMem;
    *nContent = fpos;
  }
  return rc;
}

/*
** Expects zFilename to be one of the conventional skin filename
** parts. This routine converts it to config format and emits it to
** App.ostr.
Changes to tools/sqlcompattest.c.
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
#error "Must set -DMINIMUM_SQLITE_VERSION=nn.nn.nn in auto.def"
#endif

#define QUOTE(VAL) #VAL
#define STR(MACRO_VAL) QUOTE(MACRO_VAL)

  char zMinimumVersionNumber[8]="nn.nn.nn";
  strncpy((char *)&zMinimumVersionNumber,STR(MINIMUM_SQLITE_VERSION),sizeof(zMinimumVersionNumber));


  long major, minor, release, version;
  sscanf(zMinimumVersionNumber, "%li.%li.%li", &major, &minor, &release);
  version=(major*1000000)+(minor*1000)+release;

  int i;
  static const char *zRequiredOpts[] = {
    "ENABLE_FTS4",        /* Required for repository search */
    "ENABLE_DBSTAT_VTAB", /* Required by /repo-tabsize page */
  };

  /* Check minimum SQLite version number */
  if( sqlite3_libversion_number()<version ){
    printf("found system SQLite version %s but need %s or later, consider removing --disable-internal-sqlite\n",

            sqlite3_libversion(),STR(MINIMUM_SQLITE_VERSION));
    return 1;
  }

  for(i=0; i<sizeof(zRequiredOpts)/sizeof(zRequiredOpts[0]); i++){
    if( !sqlite3_compileoption_used(zRequiredOpts[i]) ){
      printf("system SQLite library omits required build option -DSQLITE_%s\n",







|
>













|
>







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
#error "Must set -DMINIMUM_SQLITE_VERSION=nn.nn.nn in auto.def"
#endif

#define QUOTE(VAL) #VAL
#define STR(MACRO_VAL) QUOTE(MACRO_VAL)

  char zMinimumVersionNumber[8]="nn.nn.nn";
  strncpy((char *)&zMinimumVersionNumber,STR(MINIMUM_SQLITE_VERSION),
          sizeof(zMinimumVersionNumber));

  long major, minor, release, version;
  sscanf(zMinimumVersionNumber, "%li.%li.%li", &major, &minor, &release);
  version=(major*1000000)+(minor*1000)+release;

  int i;
  static const char *zRequiredOpts[] = {
    "ENABLE_FTS4",        /* Required for repository search */
    "ENABLE_DBSTAT_VTAB", /* Required by /repo-tabsize page */
  };

  /* Check minimum SQLite version number */
  if( sqlite3_libversion_number()<version ){
    printf("found system SQLite version %s but need %s or later, "
           "consider removing --disable-internal-sqlite\n",
            sqlite3_libversion(),STR(MINIMUM_SQLITE_VERSION));
    return 1;
  }

  for(i=0; i<sizeof(zRequiredOpts)/sizeof(zRequiredOpts[0]); i++){
    if( !sqlite3_compileoption_used(zRequiredOpts[i]) ){
      printf("system SQLite library omits required build option -DSQLITE_%s\n",
Changes to www/changes.wiki.
1
2
3
4
5







6
7
8
9
10
11
12
<title>Change Log</title>

<h2 id='v2_24'>Changes for version 2.24 (pending)</h2>

  *  Add the x= query paramater to the [/help?cmd=/timeline|/timeline page].








<h2 id='v2_23'>Changes for version 2.23 (2023-11-01)</h2>

  *  Add ability to "close" forum threads, such that unprivileged users
     may no longer respond to them. Only administrators can close
     threads or respond to them by default, and the
     [/help?cmd=forum-close-policy|forum-close-policy setting] can be





>
>
>
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<title>Change Log</title>

<h2 id='v2_24'>Changes for version 2.24 (pending)</h2>

  *  Add the x= query paramater to the [/help?cmd=/timeline|/timeline page].
  *  Moved the /museum/repo.fossil file referenced from the Dockerfile from
     the ENTRYPOINT to the CMD part to allow use of --repolist mode.
  *  The /uvlist page now shows the hash algorithm used so that
     outsiders don't have to guess it from the hash length when
     double-checking hashes of downloaded files on their end.
  *  The hash itself is now shown in a fixed-width font on the /uvlist
     page, suiting this tabular display.

<h2 id='v2_23'>Changes for version 2.23 (2023-11-01)</h2>

  *  Add ability to "close" forum threads, such that unprivileged users
     may no longer respond to them. Only administrators can close
     threads or respond to them by default, and the
     [/help?cmd=forum-close-policy|forum-close-policy setting] can be
Changes to www/customskin.md.
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

## <a id="vars"></a>TH1 Variables

Before expanding the TH1 within the header and footer, Fossil first
initializes a number of TH1 variables to values that depend on
repository settings and the specific page being generated.

   *   **project_name** - The project_name variable is filled with the
       name of the project as configured under the Admin/Configuration
       menu.

   *   **project_description** - The project_description variable is
       filled with the description of the project as configured under
       the Admin/Configuration menu.

   *   **title** - The title variable holds the title of the page being
       generated.

       The title variable is special in that it is deleted after
       the header script runs and before the footer script.  This is
       necessary to avoid a conflict with a variable by the same name used
       in my ticket-screen scripts.

   *   **baseurl** - The root of the URL namespace for this server.

   *   **secureurl** - The same as $baseurl except that if the scheme is
                       "http:" it is changed to "https:"

   *   **home** - The $baseurl without the scheme and hostname.  For example,
       if the $baseurl is "http://projectX.com/cgi-bin/fossil" then the
       $home will be just "/cgi-bin/fossil".

   *   **index_page** - The landing page URI as
       specified by the Admin/Configuration setup page.

   *   **current_page** - The name of the page currently being processed,
       without the leading "/" and without query parameters.
       Examples:  "timeline", "doc/trunk/README.txt", "wiki".

   *   **csrf_token** - A token used to prevent cross-site request forgery.

   *   **default_csp** - [Fossil’s default CSP](./defcsp.md) unless
       [overridden by custom TH1 code](./defcsp.md#th1). Useful within
       the skin for inserting the CSP into a `<meta>` tag within [a
       custom `<head>` element](#headfoot).

   *   **nonce** - The value of the cryptographic nonce for the request
       being processed.

   *   **release_version** - The release version of Fossil.  Ex: "1.31"

   *   **manifest_version** - A prefix on the check-in hash of the
       specific version of fossil that is running.  Ex: "\[47bb6432a1\]"

   *   **manifest_date** - The date of the source-code check-in for the
       version of fossil that is running.

   *   **compiler_name** - The name and version of the compiler used to
       build the fossil executable.

   *   **login** - This variable only exists if the user has logged in.
       The value is the username of the user.

   *   **stylesheet_url** - A URL for the internal style-sheet maintained
       by Fossil.

   *   **log\_image\_url** - A URL for the logo image for this project, as
       configured on the Admin/Logo page.

   *   **background\_image\_url** - A URL for a background image for this
       project, as configured on the Admin/Logo page.

All of the above are variables in the sense that either the header or the
footer is free to change or erase them.  But they should probably be treated
as constants.  New predefined values are likely to be added in future
releases of Fossil.








|



|



|







|

|


|



|


|



|

|




|


|

|


|


|


|


|


|


|







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

## <a id="vars"></a>TH1 Variables

Before expanding the TH1 within the header and footer, Fossil first
initializes a number of TH1 variables to values that depend on
repository settings and the specific page being generated.

   *   **`project_name`** - The project_name variable is filled with the
       name of the project as configured under the Admin/Configuration
       menu.

   *   **`project_description`** - The project_description variable is
       filled with the description of the project as configured under
       the Admin/Configuration menu.

   *   **`title`** - The title variable holds the title of the page being
       generated.

       The title variable is special in that it is deleted after
       the header script runs and before the footer script.  This is
       necessary to avoid a conflict with a variable by the same name used
       in my ticket-screen scripts.

   *   **`baseurl`** - The root of the URL namespace for this server.

   *   **`secureurl`** - The same as $baseurl except that if the scheme is
                       "http:" it is changed to "https:"

   *   **`home`** - The $baseurl without the scheme and hostname.  For example,
       if the $baseurl is "http://projectX.com/cgi-bin/fossil" then the
       $home will be just "/cgi-bin/fossil".

   *   **`index_page`** - The landing page URI as
       specified by the Admin/Configuration setup page.

   *   **`current_page`** - The name of the page currently being processed,
       without the leading "/" and without query parameters.
       Examples:  "timeline", "doc/trunk/README.txt", "wiki".

   *   **`csrf_token`** - A token used to prevent cross-site request forgery.

   *   **`default_csp`** - [Fossil’s default CSP](./defcsp.md) unless
       [overridden by custom TH1 code](./defcsp.md#th1). Useful within
       the skin for inserting the CSP into a `<meta>` tag within [a
       custom `<head>` element](#headfoot).

   *   **`nonce`** - The value of the cryptographic nonce for the request
       being processed.

   *   **`release_version`** - The release version of Fossil.  Ex: "1.31"

   *   **`manifest_version`** - A prefix on the check-in hash of the
       specific version of fossil that is running.  Ex: "\[47bb6432a1\]"

   *   **`manifest_date`** - The date of the source-code check-in for the
       version of fossil that is running.

   *   **`compiler_name`** - The name and version of the compiler used to
       build the fossil executable.

   *   **`login`** - This variable only exists if the user has logged in.
       The value is the username of the user.

   *   **`stylesheet_url`** - A URL for the internal style-sheet maintained
       by Fossil.

   *   **`logo_image_url`** - A URL for the logo image for this project, as
       configured on the Admin/Logo page.

   *   **`background_image_url`** - A URL for a background image for this
       project, as configured on the Admin/Logo page.

All of the above are variables in the sense that either the header or the
footer is free to change or erase them.  But they should probably be treated
as constants.  New predefined values are likely to be added in future
releases of Fossil.

Changes to www/tech_overview.wiki.
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
file that people are normally referring to when they say
"a Fossil repository".  The checkout database is found in the working
checkout for a project and contains state information that is unique
to that working checkout.

Fossil does not always use all three database files.  The web interface,
for example, typically only uses the repository database.  And the
[/help/all | fossil settings] command only opens the configuration database
when the --global option is used.  But other commands use all three
databases at once.  For example, the [/help/status | fossil status]
command will first locate the checkout database, then use the checkout
database to find the repository database, then open the configuration
database.  Whenever multiple databases are used at the same time,
they are all opened on the same SQLite database connection using
SQLite's [http://www.sqlite.org/lang_attach.html | ATTACH] command.







|







51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
file that people are normally referring to when they say
"a Fossil repository".  The checkout database is found in the working
checkout for a project and contains state information that is unique
to that working checkout.

Fossil does not always use all three database files.  The web interface,
for example, typically only uses the repository database.  And the
[/help/settings | fossil settings] command only opens the configuration database
when the --global option is used.  But other commands use all three
databases at once.  For example, the [/help/status | fossil status]
command will first locate the checkout database, then use the checkout
database to find the repository database, then open the configuration
database.  Whenever multiple databases are used at the same time,
they are all opened on the same SQLite database connection using
SQLite's [http://www.sqlite.org/lang_attach.html | ATTACH] command.
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175

  *  %FOSSIL_HOME%/_fossil
  *  %LOCALAPPDATA%/_fossil
  *  %APPDATA%/_fossil
  *  %USERPROFILES%/_fossil
  *  %HOMEDRIVE%%HOMEPATH%/_fossil

The second case is the one that usually determines the name  Note that the
FOSSIL_HOME environment variable can always be set to determine the 
location of the configuration database.  Note also that the configuration
database file itself is called ".fossil" or "fossil.db" on unix but
"_fossil" on windows.

The [/help?cmd=info|fossil info] command will show the location of
the configuration database on a line that starts with "config-db:".







|







161
162
163
164
165
166
167
168
169
170
171
172
173
174
175

  *  %FOSSIL_HOME%/_fossil
  *  %LOCALAPPDATA%/_fossil
  *  %APPDATA%/_fossil
  *  %USERPROFILES%/_fossil
  *  %HOMEDRIVE%%HOMEPATH%/_fossil

The second case is the one that usually determines the name. Note that the
FOSSIL_HOME environment variable can always be set to determine the 
location of the configuration database.  Note also that the configuration
database file itself is called ".fossil" or "fossil.db" on unix but
"_fossil" on windows.

The [/help?cmd=info|fossil info] command will show the location of
the configuration database on a line that starts with "config-db:".
Changes to www/unvers.wiki.
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

<blockquote><pre>
CREATE TABLE unversioned(
  uvid INTEGER PRIMARY KEY AUTOINCREMENT,  -- unique ID for this file
  name TEXT UNIQUE,       -- Name of the file
  rcvid INTEGER,          -- From whence this file was received
  mtime DATETIME,         -- Last change (seconds since 1970)
  hash TEXT,              -- SHA1 hash of uncompressed content
  sz INTEGER,             -- Size of uncompressed content
  encoding INT,           -- 0: plaintext  1: zlib compressed
  content BLOB            -- File content
);
</pre></blockquote>

Fossil does not create the table ahead of need.







|







76
77
78
79
80
81
82
83
84
85
86
87
88
89
90

<blockquote><pre>
CREATE TABLE unversioned(
  uvid INTEGER PRIMARY KEY AUTOINCREMENT,  -- unique ID for this file
  name TEXT UNIQUE,       -- Name of the file
  rcvid INTEGER,          -- From whence this file was received
  mtime DATETIME,         -- Last change (seconds since 1970)
  hash TEXT,              -- SHA1 or SHA3-256 hash of uncompressed content
  sz INTEGER,             -- Size of uncompressed content
  encoding INT,           -- 0: plaintext  1: zlib compressed
  content BLOB            -- File content
);
</pre></blockquote>

Fossil does not create the table ahead of need.
104
105
106
107
108
109
110
111
112
113
114
to "diff" your local version against the remote and send only the
changes. We point this out because one use-case for unversioned content
is to send large, frequently-changing files. Appreciate the consequences
before making each change.

There are two bandwidth-saving measures in "<tt>fossil uv sync</tt>".
The first is the regular HTTP payload compression step, done on all
syncs. The second is that Fossil sends SHA1 hash exchanges to determine
when it can avoid sending duplicate content over the wire unnecessarily.
See the [./sync.wiki|synchronization protocol documentation] for further
information.







|



104
105
106
107
108
109
110
111
112
113
114
to "diff" your local version against the remote and send only the
changes. We point this out because one use-case for unversioned content
is to send large, frequently-changing files. Appreciate the consequences
before making each change.

There are two bandwidth-saving measures in "<tt>fossil uv sync</tt>".
The first is the regular HTTP payload compression step, done on all
syncs. The second is that Fossil sends hash exchanges to determine
when it can avoid sending duplicate content over the wire unnecessarily.
See the [./sync.wiki|synchronization protocol documentation] for further
information.