Changes On Branch 2318c419c9e2c3c1
Not logged in

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

Changes In Branch fileedit-ajaxify Through [2318c419c9] Excluding Merge-Ins

This is equivalent to a diff from 8d4ce834ed to 2318c419c9

2020-05-12
08:54
Double-clicking the status message bar now clears the message (useful when a long HTML-format error comes in via ajax). Removed the unsightly 'new and experimental' banner. check-in: ff7ad7125f user: stephan tags: fileedit-ajaxify
08:28
After committing, reload the leaves/files list. Added a reload button to the leaves/files widget. Several style improvements. check-in: 2318c419c9 user: stephan tags: fileedit-ajaxify
2020-05-11
13:03
Replaced the /fileedit_AJAX_ROUTE calls with /fileedit?ajax=ROUTE_NAME to eliminate the dispatch-time and help-system bloat. check-in: 9f26fd1eee user: stephan tags: fileedit-ajaxify
2020-05-05
04:06
Initial work on ajaxifying /fileedit. Fetching content, preview, and diffs are ajax'd, but save is not yet. check-in: 8edf9dbfc2 user: stephan tags: fileedit-ajaxify
2020-05-04
23:26
Moved some generic fileedit code to style.c. Refactored /fileedit to not require JS to update version info, making this impl pure no-JS. Now to ajaxify it... Closed-Leaf check-in: 8d4ce834ed user: stephan tags: checkin-without-checkout
20:16
Added /fileedit links to /finfo and /artifact. check-in: fe925e7d6d user: stephan tags: checkin-without-checkout

Changes to src/allrepo.c.
126
127
128
129
130
131
132
133

134
135
136
137
138
139
140
126
127
128
129
130
131
132

133
134
135
136
137
138
139
140







-
+







**
**    rebuild     Rebuild on all repositories.  The command line options
**                supported by the rebuild command itself, if any are
**                present, are passed along verbatim.  The --force and
**                --randomize options are not supported.
**
**    sync        Run a "sync" on all repositories.  Only the --verbose
**                option is supported.
**                and --unversioned options are supported.
**
**    setting     Run the "setting", "set", or "unset" commands on all
**    set         repositories.  These command are particularly useful in
**    unset       conjunction with the "max-loadavg" setting which cannot
**                otherwise be set globally.
**
**    server      Run the "ui" or "server" commands on all repositories.
Changes to src/browse.c.
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
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







+
+

+




-
-
-
-
-
-















+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  char *zUuid = 0;
  Blob dirname;
  Manifest *pM = 0;
  const char *zSubdirLink;
  int linkTrunk = 1;
  int linkTip = 1;
  HQuery sURI;
  int isSymbolicCI = 0;   /* ci= is symbolic name, not a hash prefix */
  char *zHeader = 0;

  if( zCI && strlen(zCI)==0 ){ zCI = 0; }
  if( strcmp(PD("type","flat"),"tree")==0 ){ page_tree(); return; }
  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
  style_header("File List");
  style_adunit_config(ADUNIT_RIGHT_OK);
  sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
                          pathelementFunc, 0, 0);
  url_initialize(&sURI, "dir");
  cgi_query_parameters_to_url(&sURI);

  /* If the name= parameter is an empty string, make it a NULL pointer */
  if( zD && strlen(zD)==0 ){ zD = 0; }

  /* If a specific check-in is requested, fetch and parse it.  If the
  ** specific check-in does not exist, clear zCI.  zCI==0 will cause all
  ** files from all check-ins to be displayed.
  */
  if( zCI ){
    pM = manifest_get_by_name(zCI, &rid);
    if( pM ){
      int trunkRid = symbolic_name_to_rid("tag:trunk", "ci");
      linkTrunk = trunkRid && rid != trunkRid;
      linkTip = rid != symbolic_name_to_rid("tip", "ci");
      zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
      isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI))!=0);
    }else{
      zCI = 0;
    }
  }

  assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) );
  if( isSymbolicCI ) {
    zHeader = mprintf("%s at %s", (zD ? zD : "Files"), zCI);
  }else if( zUuid && strlen(zUuid) ){
    zHeader = mprintf("%s at [%S]", (zD ? zD : "Files"), zUuid);
  }else{
    zHeader = mprintf("%s", (zD ? zD : "All Files"));
  }
  style_header("%s", zHeader);
  fossil_free(zHeader);
  style_adunit_config(ADUNIT_RIGHT_OK);
  sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
                          pathelementFunc, 0, 0);
  url_initialize(&sURI, "dir");
  cgi_query_parameters_to_url(&sURI);

  /* Compute the title of the page */
  blob_zero(&dirname);
  if( zD ){
    blob_append(&dirname, "in directory ", -1);
    hyperlinked_path(zD, &dirname, zCI, "dir", "");
    zPrefix = mprintf("%s/", zD);
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
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







-
+










-
+







    style_submenu_element("Trunk", "%s",
                          url_render(&sURI, "ci", "trunk", 0, 0));
  }
  if( linkTip ){
    style_submenu_element("Tip", "%s", url_render(&sURI, "ci", "tip", 0, 0));
  }
  if( zCI ){
    @ <h2>Files of check-in [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>]
    @ <h2>Files at check-in [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>]
    @ %s(blob_str(&dirname))
    if( zD ){
      @ &nbsp;&nbsp;%z(href("%R/timeline?chng=%T/*", zD))[history]</a>
    }
    @ </h2>
    zSubdirLink = mprintf("%R/dir?ci=%!S&name=%T", zUuid, zPrefix);
    if( nD==0 ){
      style_submenu_element("File Ages", "%R/fileage?name=%!S", zUuid);
    }
  }else{
    @ <h2>The union of all files from all check-ins
    @ <h2>All files known in the repository
    @ %s(blob_str(&dirname))
    if( zD ){
      @ &nbsp;&nbsp;%z(href("%R/timeline?chng=%T/*", zD))[history]</a>
    }
    @ </h2>
    zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
  }
280
281
282
283
284
285
286
287
288

289
290
291
292
293
294
295
294
295
296
297
298
299
300


301
302
303
304
305
306
307
308







-
-
+







    zFN = db_column_text(&q, 0);
    if( zFN[0]=='/' ){
      zFN++;
      @ <li class="dir">%z(href("%s%T",zSubdirLink,zFN))%h(zFN)</a></li>
    }else{
      const char *zLink;
      if( zCI ){
        const char *zUuid = db_column_text(&q, 1);
        zLink = href("%R/artifact/%!S",zUuid);
        zLink = href("%R/file?name=%T%T&ci=%!S",zPrefix,zFN,zCI);
      }else{
        zLink = href("%R/finfo?name=%T%T",zPrefix,zFN);
      }
      @ <li class="%z(fileext_class(zFN))">%z(zLink)%h(zFN)</a></li>
    }
  }
  db_finalize(&q);
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
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







+
+

+











-


-







  FileTreeNode *p;         /* One line of the tree */
  FileTree sTree;          /* The complete tree of files */
  HQuery sURI;             /* Hyperlink */
  int startExpanded;       /* True to start out with the tree expanded */
  int showDirOnly;         /* Show directories only.  Omit files */
  int nDir = 0;            /* Number of directories. Used for ID attributes */
  char *zProjectName = db_get("project-name", 0);
  int isSymbolicCI = 0;    /* ci= is a symbolic name, not a hash prefix */
  char *zHeader = 0;

  if( zCI && strlen(zCI)==0 ){ zCI = 0; }
  if( strcmp(PD("type","flat"),"flat")==0 ){ page_dir(); return; }
  memset(&sTree, 0, sizeof(sTree));
  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
  sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
                          pathelementFunc, 0, 0);
  url_initialize(&sURI, "tree");
  cgi_query_parameters_to_url(&sURI);
  if( PB("nofiles") ){
    showDirOnly = 1;
    style_header("Folder Hierarchy");
  }else{
    showDirOnly = 0;
    style_header("File Tree");
  }
  style_adunit_config(ADUNIT_RIGHT_OK);
  if( PB("expand") ){
    startExpanded = 1;
  }else{
    startExpanded = 0;
  }
658
659
660
661
662
663
664

665
666
667
668
669
670
671
672














673
674
675
676
677
678
679
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







+








+
+
+
+
+
+
+
+
+
+
+
+
+
+







      int trunkRid = symbolic_name_to_rid("tag:trunk", "ci");
      linkTrunk = trunkRid && rid != trunkRid;
      linkTip = rid != symbolic_name_to_rid("tip", "ci");
      zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
      rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
      zNow = db_text("", "SELECT datetime(mtime,toLocal())"
                         " FROM event WHERE objid=%d", rid);
      isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI)) != 0);
    }else{
      zCI = 0;
    }
  }
  if( zCI==0 ){
    rNow = db_double(0.0, "SELECT max(mtime) FROM event");
    zNow = db_text("", "SELECT datetime(max(mtime),toLocal()) FROM event");
  }

  assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) );
  if( isSymbolicCI ) {
    zHeader = mprintf("%s at %s",
      (zD ? zD : (showDirOnly ? "Folder Hierarchy" : "Tree-View")), zCI);
  }else if( zUuid && strlen(zUuid) ){
    zHeader = mprintf("%s at [%S]",
      (zD ? zD : (showDirOnly ? "Folder Hierarchy" : "Tree-View")), zUuid);
  }else{
    zHeader = mprintf("%s",
      (zD ? zD : (showDirOnly ?"All Folders Hierarchy":"All Files Tree-View")));
  }
  style_header("%s", zHeader);
  fossil_free(zHeader);

  /* Compute the title of the page */
  blob_zero(&dirname);
  if( zD ){
    blob_append(&dirname, "within directory ", -1);
    hyperlinked_path(zD, &dirname, zCI, "tree", zREx);
    if( zRE ) blob_appendf(&dirname, " matching \"%s\"", zRE);
756
757
758
759
760
761
762
763

764
765
766
767
768
769
770
785
786
787
788
789
790
791

792
793
794
795
796
797
798
799







-
+







  }else{
    zObjType = "Files";
  }

  style_submenu_checkbox("nofiles", "Folders Only", 0, 0);

  if( zCI ){
    @ <h2>%s(zObjType) from
    @ <h2>%s(zObjType) at check-in
    if( sqlite3_strnicmp(zCI, zUuid, (int)strlen(zCI))!=0 ){
      @ "%h(zCI)"
    }
    @ [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>] %s(blob_str(&dirname))
  }else{
    int n = db_int(0, "SELECT count(*) FROM plink");
    @ <h2>%s(zObjType) from all %d(n) check-ins %s(blob_str(&dirname))
822
823
824
825
826
827
828
829

830
831
832
833
834
835
836
851
852
853
854
855
856
857

858
859
860
861
862
863
864
865







-
+







        @ <ul id="dir%d(nDir)" class="collapsed">
      }
      nDir++;
    }else if( !showDirOnly ){
      const char *zFileClass = fileext_class(p->zName);
      char *zLink;
      if( zCI ){
        zLink = href("%R/artifact/%!S",p->zUuid);
        zLink = href("%R/file?name=%T&ci=%!S",p->zFullName,zCI);
      }else{
        zLink = href("%R/finfo?name=%T",p->zFullName);
      }
      @ <li class="%z(zFileClass)%s(zLastClass)"><div class="filetreeline">
      @ %z(zLink)%h(p->zName)</a>
      if( p->mtime>0 ){
        char *zAge = human_readable_age(rNow - p->mtime);
Changes to src/builtin.c.
53
54
55
56
57
58
59



60
61
62
63
64

65

66





67
68
69
70
71
72
73
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







+
+
+




-
+

+
-
+
+
+
+
+







}
const char *builtin_text(const char *zFilename){
  return (char*)builtin_file(zFilename, 0);
}

/*
** COMMAND: test-builtin-list
**
** If -verbose is used, it outputs a line at the end
** with the total item count and size.
**
** List the names and sizes of all built-in resources.
*/
void test_builtin_list(void){
  int i;
  int i, size = 0;;
  for(i=0; i<count(aBuiltinFiles); i++){
    const int n = aBuiltinFiles[i].nByte;
    fossil_print("%-30s %6d\n", aBuiltinFiles[i].zName,aBuiltinFiles[i].nByte);
    fossil_print("%-30s %6d\n", aBuiltinFiles[i].zName,n);
    size += n;
  }
  if(find_option("verbose","v",0)!=0){
    fossil_print("%d entries totaling %d bytes\n", i, size);
  }
}

/*
** WEBPAGE: test-builtin-files
**
** Show all built-in text files.
Changes to src/cgi.c.
183
184
185
186
187
188
189
190
191


192
193
194
195
196
197
198
183
184
185
186
187
188
189


190
191
192
193
194
195
196
197
198







-
-
+
+







  cgi_combine_header_and_body();
  return blob_buffer(&cgiContent[0]);
}

/*
** Additional information used to form the HTTP reply
*/
static const char *zContentType = "text/html";     /* Content type of the reply */
static const char *zReplyStatus = "OK";            /* Reply status description */
static const char *zContentType = "text/html";   /* Content type of the reply */
static const char *zReplyStatus = "OK";          /* Reply status description */
static int iReplyStatus = 200;               /* Reply status code */
static Blob extraHeader = BLOB_INITIALIZER;  /* Extra header text */
static int rangeStart = 0;                   /* Start of Range: */
static int rangeEnd = 0;                     /* End of Range: plus 1 */

/*
** Set the reply content type
302
303
304
305
306
307
308
309

310
311
312
313
314
315
316
302
303
304
305
306
307
308

309
310
311
312
313
314
315
316







-
+







    assert( rangeEnd==0 );
    fprintf(g.httpOut, "Status: %d %s\r\n", iReplyStatus, zReplyStatus);
  }
  if( g.isConst ){
    /* isConst means that the reply is guaranteed to be invariant, even
    ** after configuration changes and/or Fossil binary recompiles. */
    fprintf(g.httpOut, "Cache-Control: max-age=31536000\r\n");
  }else if( etag_tag()!=0 ){
  }else if( etag_tag()[0]!=0 ){
    fprintf(g.httpOut, "ETag: %s\r\n", etag_tag());
    fprintf(g.httpOut, "Cache-Control: max-age=%d\r\n", etag_maxage());
  }else{
    fprintf(g.httpOut, "Cache-control: no-cache\r\n");
  }
  if( etag_mtime()>0 ){
    fprintf(g.httpOut, "Last-Modified: %s\r\n",
338
339
340
341
342
343
344

345
346
347
348
349





350
351
352
353
354
355
356
357
358
338
339
340
341
342
343
344
345





346
347
348
349
350
351

352
353
354
355
356
357
358







+
-
-
-
-
-
+
+
+
+
+

-







  ** These headers are probably best added by the web server hosting fossil as
  ** a CGI script.
  */

  /* Content intended for logged in users should only be cached in
  ** the browser, not some shared location.
  */
  if( iReplyStatus!=304 ) {
  fprintf(g.httpOut, "Content-Type: %s; charset=utf-8\r\n", zContentType);
  if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){
    cgi_combine_header_and_body();
    blob_compress(&cgiContent[0], &cgiContent[0]);
  }
    fprintf(g.httpOut, "Content-Type: %s; charset=utf-8\r\n", zContentType);
    if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){
      cgi_combine_header_and_body();
      blob_compress(&cgiContent[0], &cgiContent[0]);
    }

  if( iReplyStatus!=304 ) {
    if( is_gzippable() && iReplyStatus!=206 ){
      int i;
      gzip_begin(0);
      for( i=0; i<2; i++ ){
        int size = blob_size(&cgiContent[i]);
        if( size>0 ) gzip_step(blob_buffer(&cgiContent[i]), size);
        blob_reset(&cgiContent[i]);
368
369
370
371
372
373
374
375


376
377
378
379
380
381
382
368
369
370
371
372
373
374

375
376
377
378
379
380
381
382
383







-
+
+







      total_size = rangeEnd - rangeStart; 
    }
    fprintf(g.httpOut, "Content-Length: %d\r\n", total_size);
  }else{
    total_size = 0;
  }
  fprintf(g.httpOut, "\r\n");
  if( total_size>0 && iReplyStatus != 304
  if( total_size>0
   && iReplyStatus!=304
   && fossil_strcmp(P("REQUEST_METHOD"),"HEAD")!=0
  ){
    int i, size;
    for(i=0; i<2; i++){
      size = blob_size(&cgiContent[i]);
      if( size<=rangeStart ){
        rangeStart -= size;
Changes to src/comformat.c.
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
17
18
19
20
21
22
23






24
25
26
27
28
29
30







-
-
-
-
-
-







**
** This file contains code used to format and print comments or other
** text on a TTY.
*/
#include "config.h"
#include "comformat.h"
#include <assert.h>
#ifdef _WIN32
# include <windows.h>
#else
# include <termios.h>
# include <sys/ioctl.h>
#endif

#if INTERFACE
#define COMMENT_PRINT_NONE       ((u32)0x00000000) /* No flags = non-legacy. */
#define COMMENT_PRINT_LEGACY     ((u32)0x00000001) /* Use legacy algorithm. */
#define COMMENT_PRINT_TRIM_CRLF  ((u32)0x00000002) /* Trim leading CR/LF. */
#define COMMENT_PRINT_TRIM_SPACE ((u32)0x00000004) /* Trim leading/trailing. */
#define COMMENT_PRINT_WORD_BREAK ((u32)0x00000008) /* Break lines on words. */
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
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







-
+
-
-
+
-
-
-
+

-
+
-
-
-
-
-
+
+

-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
+







** returned to indicate the terminal line width is using the hard-coded
** legacy default value.
*/
static int comment_set_maxchars(
  int indent,
  int *pMaxChars
){
#if defined(_WIN32)
  struct TerminalSize ts;
  CONSOLE_SCREEN_BUFFER_INFO csbi;
  memset(&csbi, 0, sizeof(CONSOLE_SCREEN_BUFFER_INFO));
  if ( !terminal_get_size(&ts) ){
  if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) ){
    *pMaxChars = csbi.srWindow.Right - csbi.srWindow.Left - indent;
    return 1;
    return 0;
  }
  return 0;

#elif defined(TIOCGWINSZ)
  struct winsize w;
  memset(&w, 0, sizeof(struct winsize));
  if( ioctl(0, TIOCGWINSZ, &w)!=-1 ){
    *pMaxChars = w.ws_col - indent;
  if( ts.nColumns ){
    *pMaxChars = ts.nColumns - indent;
    return 1;
  }
  return 0;
#else
  /*
  ** Fallback to using more-or-less the "legacy semantics" of hard-coding
  ** the maximum line length to a value reasonable for the vast majority
  ** of supported systems.
  */
  *pMaxChars = COMMENT_LEGACY_LINE_LENGTH - indent;
  return -1;
  }else{
    /*
    ** Fallback to using more-or-less the "legacy semantics" of hard-coding
    ** the maximum line length to a value reasonable for the vast majority
    ** of supported systems.
    */
    *pMaxChars = COMMENT_LEGACY_LINE_LENGTH - indent;
    return -1;
#endif
  }
}

/*
** This function checks the current line being printed against the original
** comment text.  Upon matching, it updates the provided character and line
** counts, if applicable.  The caller needs to emit a new line, if desired.
*/
Changes to src/default_css.txt.
859
860
861
862
863
864
865
















































































866

867
868

869
870
871


872
873
874

875
876

877
878
879
880
881



882
883

884
885
886

887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906

907
908
909
910
911
912
913
914


























915



































916












917











918

919
920
921
























922


923













924
925
926
927
928
929
930

931
932
933
934
935
936

937
938
939
940
941
942
943
944
945
946
947
948

949
950
951

952
953
954
955


956
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945

946
947
948
949
950


951
952
953
954
955
956
957

958
959
960
961


962
963
964
965

966
967
968

969
970
971



972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992



993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054

1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081


1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+


+

-
-
+
+



+

-
+



-
-
+
+
+

-
+


-
+


-
-
-















+





-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+

+

-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+






-
+





-
+











-
+


-
+


-
-
+
+

//   vertical-align: top;
// }
// #setup_skinedit_css_defaults > tbody > tr > td:nth-of-type(2) > div {
//   max-width: 30em;
//   overflow: auto;
// }
// .fileedit-XXX => /fileedit page
.hidden {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}
//.hidden {
//  display: none;
//}
#fossil-status-bar {
  display: block;
  font-family: monospace;
  border-width: 1px;
  border-style: inset;
  border-color: inherit;
  min-height: 1.5em;
  font-size: 1.2em;
  padding: 0.2em;
  margin: 0.25em 0;
  flex: 0 0 auto;
}
#fossil-status-bar.error {
  color: darkred;
  background: yellow;
}
//////////////////////////////////
// Styles for fossil.tabs.js:
.tab-container {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: stretch;
}
.tab-container > #fossil-status-bar {
  margin-top: 0;
}
.tab-container > .tabs {
  padding: 0.25em;
  margin: 0;
  display: flex;
  flex-direction: column;
  border-width: 1px;
  border-style: outset;
  border-color: inherit;
}
.tab-container > .tabs > .tab-panel {
  align-self: stretch;
  flex: 10 1 auto;
  display: block;
}
.tab-container > .tab-bar {
  display: flex;
  flex-direction: row;
  flex: 1 10 auto;
  align-self: stretch;
  flex-wrap: wrap;
}
.tab-container > .tab-bar > .tab-button {
  display: inline-block;
  border-radius: 0.5em 0.5em 0 0;
  margin: 0 0.1em;
  padding: 0.25em 0.75em;
  align-self: baseline;
  border-color: inherit;
  border-width: 1px;
  border-bottom: none;
  border-top-style: inset;
  border-left-style: inset;
  border-right-style: inset;
  cursor: pointer;
  opacity: 0.6;
}
.tab-container > .tab-bar > .tab-button.selected {
  text-decoration: underline;
  opacity: 1.0;
  border-top-style: outset;
  border-left-style: outset;
  border-right-style: outset;
}
//////////////////////////////////
// Styles for /fileedit:
form.fileedit textarea {
textarea.fileedit {
  font-family: monospace;
  width: 100%;
  flex: 10 1 auto;
}
form.fileedit fieldset {
  margin: 0.5em 0 0 0;
body.fileedit fieldset {
  margin: 0.5em 0 0.5em 0;
  border-radius: 0.5em;
  border-color: inherit;
  border-width: 1px;
  font-size: 85%;
}
form.fileedit fieldset > legend {
body.fileedit fieldset > legend {
  margin: 0 0 0 1em;
  padding: 0 0.5em 0 0.5em;
}
form.fileedit fieldset > div {
  margin: 0 0.25em 0.25em 0.25em;
body.fileedit fieldset > div {
  margin: 0 0.25em 0 0.25em;
  padding: 0;
}
form.fileedit fieldset > div > .input-with-label {
body.fileedit fieldset > div > .input-with-label {
  margin: 0.25em 0.5em;
}
form.fileedit fieldset > div > button {
body.fileedit fieldset > div > button {
  margin: 0.25em 0.5em;
}
form.fileedit input:invalid {
  border-left: 0.2em dashed red;
}
.fileedit-hint {
  font-size: 80%;
  opacity: 0.75;
}
.fileedit-error-report {
  background: yellow;
  color: darkred;
  margin: 1em 0;
  padding: 0.5em;
  border-radius: 0.5em;
}
code.fileedit-manifest {
  display: block;
  height: 16em;
  overflow: auto;
  white-space: pre;
}
div.fileedit-preview {
  margin: 0;
  padding: 0;
}
.fileedit-preview > div:first-child {
  margin: 1em 0 0 0;
  border-bottom: 1px dashed;
#fileedit-tab-diff-wrapper {
  margin: 0;
  padding: 0;
  overflow: auto;
}
#fileedit-tab-preview-wrapper {
  overflow: auto;
}
.fileedit-options.commit-message > div {
  display: flex;
  flex-direction: column;
  justify-content: stretch;
  font-family: monospace;
}
.fileedit-options.commit-message > div > * {
  margin: 0.25em;
}
#fileedit-commit-button-wrapper {
  margin: 0.25em;
}
.tab-container > .tabs > .tab-panel > .fileedit-options {
  margin-top: 0;
  border: none;
  border-radius: 0;
  border-bottom-width: 1px;
  border-bottom-style: dotted;
}
.tab-container > .tabs > .tab-panel > .fileedit-options > button {
  vertical-align: middle;
  margin: 0.5em;
}
.tab-container > .tabs > .tab-panel > .fileedit-options > input {
  vertical-align: middle;
  margin: 0.5em;
}
.tab-container > .tabs > .tab-panel > .fileedit-options > .input-with-label {
  vertical-align: middle;
  margin: 0.5em;
}
////////////////////////////////////////////////////////////////////
// Styles developed for /fileedit but which have wider
// applicability:
.flex-container {
    display: flex;
}
.flex-container.flex-row {
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
}
.fileedit-options.flex-container.flex-row {
  align-items: first baseline;
}
.flex-container .flex-grow {
  flex-grow: 10;
  flex-shrink: 0;
}
.flex-container .flex-shrink {
  flex-grow: 0;
  flex-shrink: 10;
}
div.fileedit-diff {
.fileedit-options > div > * {
  margin: 0.25em;
}
#fileedit-file-selector {
  display: flex;
  flex-direction: column;
  align-content: flex-start;
  border-color: inherit;
  border-width: 1px;
  border-style: inset;
  border-radius: 0.5em;
  padding: 0 0.25em;
  margin: 0;
  min-height: 12em;
}
#fileedit-file-selector select {
  margin: 0 0 0.5em 0;
  height: initial;
  font-family: monospace;
}
#fileedit-file-selector select:focus {
  border: none;
}
#fileedit-file-selector > div {
  padding: 0;
  margin: 0;
}
.fileedit-diff > div:first-child {
  border-bottom: 1px dashed;
#fileedit-file-selector > div > * {
  margin: 0.25em 0.5em 0.25em 0;
}
.flex-container.flex-row.stretch {
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: stretch;
  margin: 0;
}
.flex-container.flex-column {
  flex-direction: column;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
}
.flex-container.flex-column.stretch {
  align-items: stretch;
  margin: 0;
}
.flex-container.child-gap-small > * {
  margin: 0.25em;
}
.font-size-100 {
  font-size: 100%;
}
.font-size-125 {
  font-size: 125%;

}
.font-size-150 {
  font-size: 150%;
}
.font-size-175 {
  font-size: 175%;
}
.font-size-200 {
  font-size: 200%;
}
//////////////////////////////////////////////////////////////////
// .input-with-label is intended to be a wrapper element which
// contains a SPAN label and an INPUT control.
.input-with-label {
  border: 1px inset #808080;
  border-radius: 0.5em;
  padding: 0.25em 0.4em;
  margin: 0 0.5em;
  display: inline-block;
  cursor: pointer;
  cursor: default;
}
.input-with-label > * {
  vertical-align: middle;
}
.input-with-label > input {
    margin: 0;
  margin: 0;
}
.input-with-label > select {
  margin: 0;
}
.input-with-label > input[type=text] {
  margin: 0;
}
.input-with-label > textarea {
  margin: 0;
}
.input-with-label > input[type=checkbox] {
    vertical-align: sub;
  vertical-align: sub;
}
.input-with-label > input[type=radio] {
    vertical-align: sub;
  vertical-align: sub;
}
.input-with-label > span {
    margin: 0 0.25em 0 0.25em;
    vertical-align: middle;
  margin: 0 0.25em 0 0.25em;
  vertical-align: middle;
}
Changes to src/diff.c.
1824
1825
1826
1827
1828
1829
1830






1831
1832
1833
1834




















1835
1836
1837
1838
1839
1840
1841
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839

1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866







+
+
+
+
+
+



-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  if( n==0 && (diffFlags & DIFF_CONTEXT_EX)==0 ) n = 5;
  return n;
}

/*
** Extract the width of columns for side-by-side diff.  Supply an
** appropriate default if no width is given.
**
** Calculate the default automatically, based on terminal's current width:
**   term-width = 2*diff-col + diff-marker + 1
**   diff-col = lineno + lmargin + text-width + rmargin
**
**   text-width = (term-width - diff-marker - 1)/2 - lineno - lmargin - rmargin
*/
int diff_width(u64 diffFlags){
  int w = (diffFlags & DIFF_WIDTH_MASK)/(DIFF_CONTEXT_MASK+1);
  if( w==0 ) w = 80;
  if( w==0 ){
    static struct {
      unsigned int lineno, lmargin, text, rmargin, marker;
    } sbsW = { 5, 2, 0, 0, 3 };
    const unsigned int wMin = 24, wMax = 132;
    unsigned int tw = terminal_get_width(80);
    unsigned int twMin =
      (wMin + sbsW.lineno + sbsW.lmargin + sbsW.rmargin)*2 + sbsW.marker + 1;
    unsigned int twMax =
      (wMax + sbsW.lineno + sbsW.lmargin + sbsW.rmargin)*2 + sbsW.marker + 1;

    if( tw<twMin ){
      tw = twMin;
    }else if( tw>twMax ){
      tw = twMax;
    }
    sbsW.text =
      (tw - sbsW.marker - 1)/2 - sbsW.lineno - sbsW.lmargin - sbsW.rmargin;
    w = sbsW.text;
  }
  return w;
}

/*
** Append the error message to pOut.
*/
void diff_errmsg(Blob *pOut, const char *msg, int diffFlags){
Changes to src/etag.c.
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
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







+





+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+





-







-
-
-
-
+
+
+
+
















-
+












+
+
+
+
+
+
+
+
+
+
+
+







/*
** Things to monitor
*/
#define ETAG_CONFIG   0x01 /* Output depends on the CONFIG table */
#define ETAG_DATA     0x02 /* Output depends on the EVENT table */
#define ETAG_COOKIE   0x04 /* Output depends on a display cookie value */
#define ETAG_HASH     0x08 /* Output depends on a hash */
#define ETAG_QUERY    0x10 /* Output depends on PATH_INFO and QUERY_STRING */
#endif

static char zETag[33];      /* The generated ETag */
static int iMaxAge = 0;     /* The max-age parameter in the reply */
static sqlite3_int64 iEtagMtime = 0;  /* Last-Modified time */

/*
** Return a hash that changes every time the Fossil source code is
** rebuilt.
**
** The current implementation is a hash of MANIFEST_UUID, __DATE__, and
** __TIME__.  But this might change in the future if we think of a better
** way to compute an identifier that changes with each build.
*/
const char *fossil_exe_id(void){
  static char zExecId[33];
  if( zExecId[0]==0 ){
    Blob x;
    blob_init(&x, MANIFEST_UUID "," __DATE__ "," __TIME__, -1);
    md5sum_blob(&x, &x);
    memcpy(zExecId, x.aData, 32);
  }
  return zExecId;
}

/*
** Generate an ETag
*/
void etag_check(unsigned eFlags, const char *zHash){
  sqlite3_int64 mtime;
  const char *zIfNoneMatch;
  char zBuf[50];
  assert( zETag[0]==0 );  /* Only call this routine once! */

  iMaxAge = 86400;
  md5sum_init();

  /* Always include the mtime of the executable as part of the hash */
  mtime = file_mtime(g.nameOfExe, ExtFILE);
  sqlite3_snprintf(sizeof(zBuf),zBuf,"mtime: %lld\n", mtime);
  md5sum_step_text(zBuf, -1);
  /* 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;
  }else if( eFlags & ETAG_DATA ){
    int iKey = db_int(0, "SELECT max(rcvid) FROM rcvfrom");
    sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",iKey);
    md5sum_step_text("data: ", -1);
    md5sum_step_text(zBuf, -1);
    md5sum_step_text("\n", 1);
    iMaxAge = 60;
  }else if( eFlags & ETAG_CONFIG ){
    int iKey = db_int(0, "SELECT value FROM config WHERE name='cfgcnt'");
    sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",iKey);
    md5sum_step_text("data: ", -1);
    md5sum_step_text("config: ", -1);
    md5sum_step_text(zBuf, -1);
    md5sum_step_text("\n", 1);
    iMaxAge = 3600;
  }

  /* Include the display cookie */
  if( eFlags & ETAG_COOKIE ){
    md5sum_step_text("display-cookie: ", -1);
    md5sum_step_text(PD(DISPLAY_SETTINGS_COOKIE,""), -1);
    md5sum_step_text("\n", 1);
    iMaxAge = 0;
  }

  /* Output depends on PATH_INFO and QUERY_STRING */
  if( eFlags & ETAG_QUERY ){
    const char *zQS = P("QUERY_STRING");
    md5sum_step_text("query: ", -1);
    md5sum_step_text(PD("PATH_INFO",""), -1);
    if( zQS ){
      md5sum_step_text("?", 1);
      md5sum_step_text(zQS, -1);
    }
    md5sum_step_text("\n",1);
  }

  /* Generate the ETag */
  memcpy(zETag, md5sum_finish(0), 33);

  /* Check to see if the generated ETag matches If-None-Match and
  ** generate a 304 reply if it does. */
  zIfNoneMatch = P("HTTP_IF_NONE_MATCH");
Changes to src/fileedit.c.
26
27
28
29
30
31
32
33

34

35
36
37
38
39
40
41
26
27
28
29
30
31
32

33
34
35
36
37
38
39
40
41
42







-
+

+







** State for the "mini-checkin" infrastructure, which enables the
** ability to commit changes to a single file without a checkout
** db, e.g. for use via an HTTP request.
**
** Use CheckinMiniInfo_init() to cleanly initialize one to a known
** valid/empty default state.
**
** Memory for all non-const (char *) members is owned by the
** Memory for all non-const pointer members is owned by the
** CheckinMiniInfo instance and is freed by CheckinMiniInfo_cleanup().
** Similarly, each instance owns any memory for its Blob members.
*/
struct CheckinMiniInfo {
  Manifest * pParent;  /* parent checkin. Memory is owned by this
                          object. */
  char *zParentUuid;   /* Full UUID of pParent */
  char *zFilename;     /* Name of single file to commit. Must be
                          relative to the top of the repo. */
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
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







-
+
+




















-
-
-
-




-
+
+



-
+
+







  fossil_free(p->zCommentMimetype);
  fossil_free(p->zUser);
  CheckinMiniInfo_init(p);
}

/*
** Internal helper which returns an F-card perms string suitable for
** writing into a manifest.
** writing as-is into a manifest. If it's not empty, it includes a
** leading space to separate it from the F-card's hash field.
*/
static const char * mfile_permint_mstring(int perm){
  switch(perm){
    case PERM_EXE: return " x";
    case PERM_LNK: return " l";
    default: return "";
  }
}

/*
** Given a ManifestFile permission string (or NULL), it returns one of
** PERM_REG, PERM_EXE, or PERM_LNK.
*/
static int mfile_permstr_int(const char *zPerm){
  if(!zPerm || !*zPerm) return PERM_REG;
  else if(strstr(zPerm,"x")) return PERM_EXE;
  else if(strstr(zPerm,"l")) return PERM_LNK;
  else return PERM_REG/*???*/;
}

static const char * mfile_perm_mstring(const ManifestFile * p){
  return mfile_permint_mstring(manifest_file_mperm(p));
}

/*
** Internal helper for checkin_mini() and friends. Appends an F-card
** for p to pOut.
*/
static void checkin_mini_append_fcard(Blob *pOut, const ManifestFile *p){
static void checkin_mini_append_fcard(Blob *pOut,
                                      const ManifestFile *p){
  if(p->zUuid){
    assert(*p->zUuid);
    blob_appendf(pOut, "F %F %s%s", p->zName,
                 p->zUuid, mfile_perm_mstring(p));
                 p->zUuid,
                 mfile_permint_mstring(manifest_file_mperm(p)));
    if(p->zPrior){
      assert(*p->zPrior);
      blob_appendf(pOut, " %F\n", p->zPrior);
    }else{
      blob_append(pOut, "\n", 1);
    }
  }else{
741
742
743
744
745
746
747
748

749
750
751
752
753
754
755
741
742
743
744
745
746
747

748
749
750
751
752
753
754
755







-
+







**   --wet-run                 Disables the default dry-run mode.
**
** Example:
**
** %fossil test-ci-mini -R REPO -m ... -r foo --as src/myfile.c myfile.c
**
*/
void test_ci_mini_cmd(){
void test_ci_mini_cmd(void){
  CheckinMiniInfo cimi;       /* checkin state */
  int newRid = 0;                /* RID of new version */
  const char * zFilename;        /* argv[2] */
  const char * zComment;         /* -m comment */
  const char * zCommentFile;     /* -M FILE */
  const char * zAsFilename;      /* --as filename */
  const char * zRevision;        /* --revision|-r [=trunk|checkout] */
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
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010

1011
1012
1013


1014
1015
1016
1017
1018
1019
1020
1021

1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045

1046
1047
1048
1049
1050
1051
1052
1053
1054




1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095

1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
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
938
939
940


941

942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973

974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991




992
993
994
995
996

997
998
999
1000
1001
1002
1003







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-

-




+


-
+
+







-
+




















-
-

-
+









+
+
+
+


















-


















-
-
-
-
+




-







    if(0==zGlobs) return 0;
    pGlobs = glob_create(zGlobs);
    fossil_free(zGlobs);
  }
  return glob_match(pGlobs, zFilename);
}

/*
** Emits a script tag which defines window.fossil.fetch(), which works
** similarly (not identically) to the not-quite-ubiquitous global
** fetch().
**
** JS usages:
**
** fossilFetch( URI, onLoadCallback );
**
** fossilFetch( URI, optionsObject );
**
** Noting that URI must be relative to the top of the repository and
** must not start with a slash (if it does, it is stripped). It gets
** %R/ prepended to it.
**
** The optionsObject may be an onload callback or an object with any
** of these properties:
**
** - onload: callback(responseData) (default = output response to
**   console).
**
** - onerror: callback(XHR onload event) (default = console output)
**
** - method: 'POST' | 'GET' (default = 'GET')
**
** - payload: anything acceptable by XHR2.send(ARG) (DOMString,
**   Document, FormData, Blob, File, ArrayBuffer), or a plain object
**   or array, either of which gets JSON.stringify()'d. If set then
**   the method is automatically set to 'POST'. If an object/array is
**   converted to JSON, the content-type is set to 'application/json'.
**   By default XHR2 will set the content type based on the payload
**   type.
**
** - contentType: Optional request content type when POSTing. Ignored
**   if the method is not 'POST'.
**
** - responseType: optional string. One of ("text", "arraybuffer",
**   "blob", or "document") (as specified by XHR2). Default = "text".
**
** - urlParams: string|object. If a string, it is assumed to be a
**   URI-encoded list of params in the form "key1=val1&key2=val2...",
**   with NO leading '?'.  If it is an object, all of its properties
**   get converted to that form. Either way, the parameters get
**   appended to the URL.
**
*/
void fileedit_emit_script_fetch(){
  style_emit_script_tag(0);
  CX("fossil.fetch = function(path,opt){\n");
  CX("  if('/'===path[0]) path = path.substr(1);\n");
  CX("  if(!opt){\n");
  CX("    opt = {onload:(r)=>console.debug('response:',r)};\n");
  CX("  }else if('function'===typeof opt){\n");
  CX("    opt={onload:opt,\n");
  CX("         onerror:(e)=>console.error('ajax error:',e)};\n");
  CX("  }\n");
  CX("  let payload = opt.payload;\n");
  CX("  if(payload){\n");
  CX("    opt.method = 'POST';\n");
  CX("    if(!(payload instanceof FormData)\n");
  CX("       && !(payload instanceof Document)\n");
  CX("       && !(payload instanceof Blob)\n");
  CX("       && !(payload instanceof File)\n");
  CX("       && !(payload instanceof ArrayBuffer)){\n");
  CX("      if('object'===typeof payload || payload instanceof Array){\n");
  CX("        payload = JSON.stringify(payload);\n");
  CX("        opt.contentType = 'application/json';\n");
  CX("      }\n");
  CX("    }\n");
  CX("  }\n");
  CX("  const url=['%R/'+path], x=new XMLHttpRequest();\n");
  CX("  if(opt.urlParams){\n");
  CX("    url.push('?');\n");
  CX("    if('string'===typeof opt.urlParams){\n");
  CX("      url.push(opt.urlParams);\n");
  CX("    }else{/*assume object*/\n");
  CX("      let k, i = 0;\n");
  CX("      for( k in opt.urlParams ){\n");
  CX("        if(i++) url.push('&');\n");
  CX("        url.push(k,'=',encodeURIComponent(opt.urlParams[k]));\n");
  CX("      }\n");
  CX("    }\n");
  CX("  }\n");
  CX("  if('POST'===opt.method && 'string'===typeof opt.contentType){\n");
  CX("    x.setRequestHeader('Content-Type',opt.contentType);\n");
  CX("  }\n");
  CX("  x.open(opt.method||'GET', url.join(''), true);\n");
  CX("  x.responseType=opt.responseType||'text';\n");
  CX("  if(opt.onload){\n");
  CX("    x.onload = function(e){\n");
  CX("      if(200!==this.status){\n");
  CX("        if(opt.onerror) opt.onerror(e);\n");
  CX("        return;\n");
  CX("      }\n");
  CX("      opt.onload(this.response);\n");
  CX("    }\n");
  CX("  }\n");
  CX("  if(payload) x.send(payload);\n");
  CX("  else x.send();\n");
  CX("};\n");
  style_emit_script_tag(1);
};


enum fileedit_render_preview_flags {
FE_PREVIEW_LINE_NUMBERS = 1
};
enum fileedit_render_modes {
/* GUESS must be 0. All others have unspecified values. */
FE_RENDER_GUESS = 0,
FE_RENDER_PLAIN_TEXT,
FE_RENDER_HTML,
FE_RENDER_HTML_IFRAME,
FE_RENDER_HTML_INLINE,
FE_RENDER_WIKI
};

static int fileedit_render_mode_for_mimetype(const char * zMimetype){
  int rc = FE_RENDER_PLAIN_TEXT;
  if( zMimetype ){
    if( fossil_strcmp(zMimetype, "text/html")==0 ){
      rc = FE_RENDER_HTML;
      rc = FE_RENDER_HTML_IFRAME;
    }else if( fossil_strcmp(zMimetype, "text/x-fossil-wiki")==0
              || fossil_strcmp(zMimetype, "text/x-markdown")==0 ){
      rc = FE_RENDER_WIKI;
    }
  }
  return rc;
}

/*
** Performs the PREVIEW mode for /filepage.
*/
static void fileedit_render_preview(Blob * pContent,
                                    const char *zFilename,
                                    int flags, int renderMode,
                                    int nIframeHeightEm){
  const char * zMime;
  zMime = mimetype_from_name(zFilename);
  if(FE_RENDER_GUESS==renderMode){
    renderMode = fileedit_render_mode_for_mimetype(zMime);
  }
  CX("<div class='fileedit-preview'>");
  CX("<div>Preview</div>");
  switch(renderMode){
    case FE_RENDER_HTML:{
    case FE_RENDER_HTML_IFRAME:{
      char * z64 = encode64(blob_str(pContent), blob_size(pContent));
      CX("<iframe width='100%%' frameborder='0' "
         "marginwidth='0' style='height:%dem' "
         "marginheight='0' sandbox='allow-same-origin' "
         "id='ifm1' src='data:text/html;base64,%z'"
         "></iframe>",
         nIframeHeightEm ? nIframeHeightEm : 40,
         z64);
      break;
    }
    case FE_RENDER_HTML_INLINE:{
      CX("%b",pContent);
      break;
    }
    case FE_RENDER_WIKI:
      wiki_render_by_mimetype(pContent, zMime);
      break;
    default:{
      const char *zExt = strrchr(zFilename,'.');
      const char *zContent = blob_str(pContent);
      if(FE_PREVIEW_LINE_NUMBERS & flags){
        output_text_with_line_numbers(zContent, "on");
      }else if(zExt && zExt[1]){
        CX("<pre><code class='language-%s'>%h</code></pre>",
           zExt+1, zContent);
      }else{
        CX("<pre>%h</pre>", zExt+1, zContent);
      }
      break;
    }
  }
  CX("</div><!--.fileedit-preview-->\n");
}

/*
** Renders diffs for the /fileedit page. pContent is the
** locally-edited content.  frid is the RID of the file's blob entry
** from which pContent is based. zManifestUuid is the checkin version
** to which RID belongs - it is purely informational, for labeling the
** diff view. isSbs is true for side-by-side diffs, false for unified.
*/
static void fileedit_render_diff(Blob * pContent, int frid,
                                 const char * zManifestUuid,
                                 int isSbs){
  Blob orig = empty_blob;
  Blob out = empty_blob;
  u64 diffFlags = DIFF_HTML | DIFF_NOTTOOBIG | DIFF_STRIP_EOLCR
    | (isSbs ? DIFF_SIDEBYSIDE : DIFF_LINENO);
  content_get(frid, &orig);
  text_diff(&orig, pContent, &out, 0, diffFlags);
  CX("<div class='fileedit-diff'>");
  CX("<div>Diff <code>[%S]</code> &rarr; Local Edits</div>",
     zManifestUuid);
  if(isSbs){
  if(isSbs || blob_size(&out)==0){
    CX("%b",&out);
  }else{
    CX("<pre class='udiff'>%b</pre>",&out);
  }
  CX("</div><!--.fileedit-diff-->\n");
  blob_reset(&orig);
  blob_reset(&out);
}

/*
** Given a repo-relative filename and a manifest RID, returns the UUID
** of the corresponding file entry.  Returns NULL if no match is
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
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
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220



1221
1222
1223
1224
1225
1226
1227
1228

1229
1230

1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245

1246
1247
1248
1249
1250
1251

1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263

1264
1265

1266
1267
1268
1269
1270
1271
1272
1273
1274
1275

1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305




1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323

1324
1325
1326
1327
1328








1329
1330

1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351

1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380







1381
1382

1383
1384
1385
1386
1387

1388
1389
1390
1391




1392
1393

1394
1395
1396





1397
1398


1399
1400
1401
1402
1403
1404







1405
1406
1407



1408
1409

1410
1411




1412
1413

1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426












1427
1428
1429
1430


1431
1432
1433
1434




1435
1436
1437
1438
1439
1440
1441



1442
1443
1444
1445
1446

















1447
1448

1449
1450
1451
1452
1453
1454
1455

1456
1457

1458
1459
1460


1461
1462
1463




1464
1465
1466


1467
1468
1469
1470
1471










1472
1473

1474
1475



1476
1477
1478
1479
1480
1481


1482
1483

1484
1485




1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499





















1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
















































1519
1520
1521
1522




1523
1524
1525
1526
1527
1528
















1529
1530
1531


1532
1533
1534
1535
1536
1537
1538
1539
1540
1541















1542
1543
1544



1545
1546
1547
1548
1549
1550







































1551
1552
1553



1554
1555




1556
1557
1558
1559
1560
1561
1562
1563
1564
1565














1566
1567
1568
1569
1570
1571
1572












1573
1574
1575











1576
1577





1578
1579

1580



1581


1582
1583
1584

1585
1586

1587
1588
1589
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
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
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
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592



1593
1594
1595
1596
1597
1598
1599
1600

1601






1602

1603
1604

1605


1606
1607




1608
1609

1610


1611
1612
1613
1614
1615
1616
1617


1618
1619


1620
1621







1622
1623
1624
1625
1626
1627
1628
1629
1630
1631




1632




1633
1634



1635

1636
1637
1638
1639
1640
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
1668
1669
1670
1671
1672


1673





















1674



1675



















1676
1677
1678



1679
1680
1681
1682
1683
1684
1685
1686

1687





1688




1689
1690
1691
1692


1693



1694
1695
1696
1697
1698


1699
1700






1701
1702
1703
1704
1705
1706
1707



1708
1709
1710


1711


1712
1713
1714
1715


1716













1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728




1729
1730




1731
1732
1733
1734







1735
1736
1737





1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754


1755







1756


1757



1758
1759



1760
1761
1762
1763



1764
1765





1766
1767
1768
1769
1770
1771
1772
1773
1774
1775


1776


1777
1778
1779
1780
1781
1782
1783
1784

1785
1786
1787
1788
1789


1790
1791
1792
1793














1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814



















1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862




1863
1864
1865
1866






1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882



1883
1884










1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899



1900
1901
1902






1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941



1942
1943
1944


1945
1946
1947
1948










1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962







1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974



1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993

1994
1995
1996
1997
1998

1999
2000
2001
2002

2003
2004

2005

2006
2007







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+









-
-
-
+
+
+





-
+
-
-
-
-
-
-
+
-


-
+
-
-


-
-
-
-


-

-
-







-
-
+
+
-
-
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
-
-
-
-
+
+
-
-
-
+
-







-
-
-
-
-
-
-
-
-
-
-

-
-
-
+
+
+

-
-
-
-
-
-
-
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
-
-
-
-
-
-

-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-



-
-
-
-
+
+
+
+
-
-
-
-
-
-
-

-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-



-
-
-
+
+
+
+
+
+
+

-
+
-
-
-
-
-
+
-
-
-
-
+
+
+
+
-
-
+
-
-
-
+
+
+
+
+
-
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
+
-
-
+
+
+
+
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
-
-
-
-
-
-
-
+
-
-
+
-
-
-
+
+
-
-
-
+
+
+
+
-
-
-
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
+
-
-
+
+
+





-
+
+


+
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+


+
+
+
+
+

-
+

+
+
+
-
+
+


-
+

-
+
-


    if(pFilePerm){
      *pFilePerm = mfile_permstr_int(db_column_text(&stmt, 1));
    }
  }
  db_finalize(&stmt);
  return zFileUuid;
}

/*
** Helper for /fileedit_xyz routes. Clears the CGI content buffer,
** sets an error status code, and queues up a JSON response in the
** form of an object:
**
** {error: formatted message}
**
** After calling this, the caller should immediately return.
 */
static void fileedit_ajax_error(int httpCode, const char * zFmt, ...){
  Blob msg = empty_blob;
  Blob content = empty_blob;
  va_list vargs;
  va_start(vargs,zFmt);
  blob_vappendf(&msg, zFmt, vargs);
  va_end(vargs);
  blob_appendf(&content,"{\"error\":\"%j\"}", blob_str(&msg));
  blob_reset(&msg);
  cgi_set_content(&content);
  cgi_set_status(httpCode, "Error");
  cgi_set_content_type("application/json");
}

/*
** Performs bootstrapping common to the /fileedit_xyz AJAX routes.
** Returns 0 if bootstrapping fails (wrong permissions), in which
** case it has reported the error and the route should immediately
** return. Returns true on success.
*/
static int fileedit_ajax_boostrap(void){
  login_check_credentials();
  if( !g.perm.Write ){
    fileedit_ajax_error(403,"Write permissions required.");
    return 0;
  }

  return 1;
}
/*
** Returns true if the current user is allowed to edit the given
** filename, as determined by fileedit_is_editable(), else false,
** in which case it queues up an error response and the caller
** must return immediately.
*/
static int fileedit_ajax_check_filename(const char * zFilename){
  if(0==fileedit_is_editable(zFilename)){
    fileedit_ajax_error(403, "File is disallowed by the "
                        "fileedit-glob setting.");
    return 0;
  }
  return 1;
}


/*
** If zFn is not NULL, it is assigned the value of the first one of
** the "filename" or "fn" CGI parameters which is set.
**
** If zCi is not NULL, it is assigned the value of the first one of
** the "checkin" or "ci" CGI parameters which is set.
**
** If a parameter is not NULL, it will be assigned NULL if the
** corresponding parameter is not set.
**
** Returns the number of non-NULL values it assigns to arguments. Thus
** if passed (&x, NULL), it returns 1 if it assigns non-NULL to *x and
** 0 if it assigns NULL to *x.
*/
static int fileedit_get_fnci_args( const char **zFn, const char **zCi ){
  int rc = 0;
  if(zCi!=0){
    *zCi = PD("checkin",P("ci"));
    if( *zCi ) ++rc;
  }
  if(zFn!=0){
    *zFn = PD("filename",P("fn"));
    if (*zFn) ++rc;
  }
  return rc;
}

/*
** Passed the values of the "checkin" and "filename" request
** properties, this function verifies that they are valid and
** populates:
**
** - *zRevUuid = the fully-expanded value of zRev (owned by the
**    caller). zRevUuid may be NULL.
**
** - *vid = the RID of zRevUuid. May not be NULL.
**
** - *frid = the RID of zFilename's blob content. May not be NULL
**   unless zFilename is also NULL. If BOTH of zFilename and frid are
**   NULL then no confirmation is done on the filename argument - only
**   zRev is checked.
**
** Returns 0 if the given file is not in the given checkin or if
** fileedit_ajax_check_filename() fails, else returns true.  If it
** returns false, it queues up an error response and the caller must
** return immediately.
*/
static int fileedit_ajax_setup_filerev(const char * zRev,
                                       char ** zRevUuid,
                                       int * vid,
                                       const char * zFilename,
                                       int * frid){
  char * zFileUuid;    /* file UUID */
  const int checkFile = zFilename!=0 || frid!=0;
  
  if(checkFile && !fileedit_ajax_check_filename(zFilename)){
    return 0;
  }
  *vid = symbolic_name_to_rid(zRev, "ci");
  if(0==*vid){
    fileedit_ajax_error(404,"Cannot resolve name as a checkin: %s",
                        zRev);
    return 0;
  }else if(*vid<0){
    fileedit_ajax_error(400,"Checkin name is ambiguous: %s",
                        zRev);
    return 0;
  }
  if(checkFile){
    zFileUuid = fileedit_file_uuid(zFilename, *vid, 0);
    if(zFileUuid==0){
      fileedit_ajax_error(404,"Checkin does not contain file.");
      return 0;
    }
  }
  if(zRevUuid!=0){
    *zRevUuid = rid_to_uuid(*vid);
  }
  if(checkFile){
    assert(zFileUuid!=0);
    if(frid!=0){
      *frid = fast_uuid_to_rid(zFileUuid);
    }
    fossil_free(zFileUuid);
  }
  return 1;
}

/*
** AJAX route /fileedit?ajax=content
**
** Query parameters:
**
** filename=FILENAME
** checkin=CHECKIN_NAME
**
** User must have Write access to use this page.
**
** Responds with the raw content of the given page. On error it
** produces a JSON response as documented for fileedit_ajax_error().
*/
static void fileedit_ajax_content(void){
  const char * zFilename = 0;
  const char * zRev = 0;
  int vid, frid;
  Blob content = empty_blob;
  const char * zMime;

  fileedit_get_fnci_args( &zFilename, &zRev );
  if(!fileedit_ajax_boostrap()
     || !fileedit_ajax_setup_filerev(zRev, 0, &vid,
                                     zFilename, &frid)){
    return;
  }
  zMime = mimetype_from_name(zFilename);
  content_get(frid, &content);
  if(0==zMime){
    if(looks_like_binary(&content)){
      zMime = "application/octet-stream";
    }else{
      zMime = "text/plain";
    }
  }
  cgi_set_content_type(zMime);
  cgi_set_content(&content);
}

/*
** AJAX route /fileedit?ajax=preview
**
** Required query parameters:
**
** filename=FILENAME
** content=text
**
** Optional query parameters:
**
** render_mode=integer (FE_RENDER_xxx) (default=FE_RENDER_GUESS)
**
** ln=0 or 1 to disable/enable line number mode in
** FE_RENDER_PLAIN_TEXT mode.
**
** iframe_height=integer (default=40) Height, in EMs of HTML preview
** iframe.
**
** User must have Write access to use this page.
**
** Responds with the HTML content of the preview. On error it produces
** a JSON response as documented for fileedit_ajax_error().
*/
static void fileedit_ajax_preview(void){
  const char * zFilename = 0;
  const char * zContent = P("content");
  int renderMode = atoi(PD("render_mode","0"));
  int ln = atoi(PD("ln","0"));
  int iframeHeight = atoi(PD("iframe_height","40"));
  Blob content = empty_blob;

  fileedit_get_fnci_args( &zFilename, 0 );
  if(!fileedit_ajax_boostrap()
     || !fileedit_ajax_check_filename(zFilename)){
    return;
  }
  cgi_set_content_type("text/html");
  blob_init(&content, zContent, -1);
  fileedit_render_preview(&content, zFilename,
                          ln ? FE_PREVIEW_LINE_NUMBERS : 0,
                          renderMode, iframeHeight);
}

/*
** AJAX route /fileedit?ajax=diff
**
** Required query parameters:
**
** filename=FILENAME
** content=text
** checkin=checkin version
**
** Optional parameters:
**
** sbs=integer (1=side-by-side or 0=unified, default=0)
**
** User must have Write access to use this page.
**
** Responds with the HTML content of the diff. On error it produces a
** JSON response as documented for fileedit_ajax_error().
*/
static void fileedit_ajax_diff(void){
  /*
  ** Reminder: we only need the filename to perform valdiation
  ** against fileedit_is_editable(), else this route could be
  ** abused to get diffs against content disallowed by the
  ** whitelist.
  */
  const char * zFilename = 0;
  const char * zRev = 0;
  const char * zContent = P("content");
  char * zRevUuid = 0;
  int isSbs = atoi(PD("sbs","0"));
  int vid, frid;
  Blob content = empty_blob;

  fileedit_get_fnci_args( &zFilename, &zRev );
  if(!fileedit_ajax_boostrap()
     || !fileedit_ajax_setup_filerev(zRev, &zRevUuid, &vid,
                                     zFilename, &frid)){
    return;
  }
  if(!zContent){
    zContent = "";
  }
  cgi_set_content_type("text/html");
  blob_init(&content, zContent, -1);
  fileedit_render_diff(&content, frid, zRevUuid, isSbs);
  fossil_free(zRevUuid);
  blob_reset(&content);
}

/*
** Sets up and validates most, but not all, of p's checkin-related
** state from the CGI environment. Returns 0 on success or a suggested
** HTTP result code on error, in which case a message will have been
** written to pErr.
**
** It always fails if it cannot completely resolve the 'file' and 'r'
** parameters, including verifying that the refer to a real
** file/version combination and editable by the current user. All
** others are optional (at this level, anyway, but upstream code might
** require them).
**
** Intended to be used only by /filepage and /filepage_commit.
*/
static int fileedit_setup_cimi_from_p(CheckinMiniInfo * p, Blob * pErr){
  char * zFileUuid = 0;          /* UUID of file content */
  const char * zFlag;            /* generic flag */
  int rc = 0, vid = 0, frid = 0; /* result code, checkin/file rids */ 

#define fail(EXPR) blob_appendf EXPR; goto end_fail
  zFlag = PD("filename",P("fn"));
  if(zFlag==0 || !*zFlag){
    rc = 400;
    fail((pErr,"Missing required 'filename' parameter."));
  }
  p->zFilename = mprintf("%s",zFlag);

  if(0==fileedit_is_editable(p->zFilename)){
    rc = 403;
    fail((pErr,"Filename [%h] is disallowed "
          "by the [fileedit-glob] repository "
          "setting.",
          p->zFilename));
  }

  zFlag = PD("checkin",P("ci"));
  if(!zFlag){
    rc = 400;
    fail((pErr,"Missing required 'checkin' parameter."));
  }
  vid = symbolic_name_to_rid(zFlag, "ci");
  if(0==vid){
    rc = 404;
    fail((pErr,"Could not resolve checkin version."));
  }else if(vid<0){
    rc = 400;
    fail((pErr,"Checkin name is ambiguous."));
  }
  p->zParentUuid = rid_to_uuid(vid)/*fully expand it*/;

  zFileUuid = fileedit_file_uuid(p->zFilename, vid, &p->filePerm);
  if(!zFileUuid){
    rc = 404;
    fail((pErr,"Checkin [%S] does not contain file: "
          "[%h]", p->zParentUuid, p->zFilename));
  }else if(PERM_LNK==p->filePerm){
    rc = 400;
    fail((pErr,"Editing symlinks is not permitted."));
  }

  /* Find the repo-side file entry or fail... */
  frid = fast_uuid_to_rid(zFileUuid);
  assert(frid);

  /* Read file content from submit request or repo... */
  zFlag = P("content");
  if(zFlag==0){
    content_get(frid, &p->fileContent);
  }else{
    blob_init(&p->fileContent,zFlag,-1);
  }
  if(looks_like_binary(&p->fileContent)){
    rc = 400;
    fail((pErr,"File appears to be binary. Cannot edit: "
          "[%h]",p->zFilename));
  }

  zFlag = PT("comment");
  if(zFlag!=0 && *zFlag!=0){
    blob_append(&p->comment, zFlag, -1);
  }
  zFlag = P("comment_mimetype");
  if(zFlag){
    p->zCommentMimetype = mprintf("%s",zFlag);
    zFlag = 0;
  }
#define p_int(K) atoi(PD(K,"0"))
  if(p_int("dry_run")!=0){
    p->flags |= CIMINI_DRY_RUN;
  }
  if(p_int("allow_fork")!=0){
    p->flags |= CIMINI_ALLOW_FORK;
  }
  if(p_int("allow_older")!=0){
    p->flags |= CIMINI_ALLOW_OLDER;
  }
  if(p_int("exec_bit")!=0){
    p->filePerm = PERM_EXE;
  }
  if(p_int("allow_merge_conflict")!=0){
    p->flags |= CIMINI_ALLOW_MERGE_MARKER;
  }
  if(p_int("prefer_delta")!=0){
    p->flags |= CIMINI_PREFER_DELTA;
  }

  /* EOL conversion policy... */
  switch(p_int("eol")){
    case 1: p->flags |= CIMINI_CONVERT_EOL_UNIX; break;
    case 2: p->flags |= CIMINI_CONVERT_EOL_WINDOWS; break;
    default: p->flags |= CIMINI_CONVERT_EOL_INHERIT; break;
  }
#undef p_int
  /*
  ** TODO?: date-override date selection field. Maybe use
  ** an input[type=datetime-local].
  */
  p->zUser = mprintf("%s",g.zLogin);
  return 0;
end_fail:
#undef fail
  fossil_free(zFileUuid);
  return rc ? rc : 500;
}

/*
** 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
** of operation depending on its arguments:
**
** 'leaves': just fetch a list of open leaf versions, in this
** format:
**
** [
**   {checkin: UUID, branch: branchName, timestamp: string}
** ]
**
** The entries are ordered newest first.
**
** 'checkin=CHECKIN_NAME': fetch the current list of is-editable files
** for the current user and given checkin name:
**
** {
**   checkin: UUID,
**   editableFiles: [ filename1, ... filenameN ] // sorted by name
** }
**
** On error it produces a JSON response as documented for
** fileedit_ajax_error().
*/
static void fileedit_ajax_filelist(void){
  const char * zCi = PD("checkin",P("ci"));
  Blob sql = empty_blob;
  Stmt q = empty_Stmt;
  int i = 0;

  if(!fileedit_ajax_boostrap()){
    return;
  }
  cgi_set_content_type("application/json");
  if(zCi!=0){
    char * zCiFull = 0;
    int vid = 0;
    if(0==fileedit_ajax_setup_filerev(zCi, &zCiFull, &vid, 0, 0)){
      /* Error already reported */
      return;
    }
    CX("{\"checkin\":\"%j\","
       "\"editableFiles\":[", zCiFull);
    blob_append_sql(&sql, "SELECT filename FROM files_of_checkin(%Q) "
                    "ORDER BY filename %s",
                    zCiFull, filename_collation());
    db_prepare_blob(&q, &sql);
    while( SQLITE_ROW==db_step(&q) ){
      const char * zFilename = db_column_text(&q, 0);
      if(fileedit_is_editable(zFilename)){
        if(i++){
          CX(",");
        }
        CX("\"%j\"", zFilename);
      }
    }
    db_finalize(&q);
    CX("]}");
  }else if(P("leaves")!=0){
    blob_append(&sql, timeline_query_for_tty(), -1);
    blob_append_sql(&sql, " AND blob.rid IN (SElECT rid FROM leaf "
                    "WHERE NOT EXISTS("
                    "SELECT 1 from tagxref WHERE tagid=%d AND "
                    "tagtype>0 AND rid=leaf.rid"
                    ")) "
                    "ORDER BY mtime DESC", TAG_CLOSED);
    db_prepare_blob(&q, &sql);
    CX("[");
    while( SQLITE_ROW==db_step(&q) ){
      if(i++){
        CX(",");
      }
      CX("{");
      CX("\"checkin\":\"%j\",", db_column_text(&q, 1));
      CX("\"timestamp\":\"%j\",", db_column_text(&q, 2));
      CX("\"branch\":\"%j\"", db_column_text(&q, 7));
      CX("}");
    }
    CX("]");
    db_finalize(&q);
  }else{
    fileedit_ajax_error(500, "Unhandled URL argument.");
  }
}

/*
** AJAX route /fileedit?ajax=commit
**
** Required query parameters:
** 
** filename=FILENAME
** checkin=Parent checkin UUID
** content=text
** comment=text
**
** Optional query parameters:
**
** comment_mimetype=text
** dry_run=int (1 or 0)
** 
**
** User must have Write access to use this page.
**
** Responds with JSON:
**
** {
**  uuid: newUUID,
**  manifest: text of manifest,
**  dryRun: bool
** }
**
** On error it produces a JSON response as documented for
** fileedit_ajax_error().
*/
static void fileedit_ajax_commit(void){
  Blob err = empty_blob;      /* Error messages */
  Blob manifest = empty_blob; /* raw new manifest */
  CheckinMiniInfo cimi;       /* checkin state */
  int rc;                     /* generic result code */
  int newVid = 0;             /* new version's RID */
  char * zNewUuid = 0;        /* newVid's UUID */

  if(!fileedit_ajax_boostrap()){
    return;
  }
  db_begin_transaction();
  CheckinMiniInfo_init(&cimi);
  rc = fileedit_setup_cimi_from_p(&cimi, &err);
  if(0!=rc){
    fileedit_ajax_error(rc,"%b",&err);
    goto end_cleanup;
  }
  if(blob_size(&cimi.comment)==0){
    fileedit_ajax_error(400,"Empty checkin comment is not permitted.");
    goto end_cleanup;
  }
  cimi.pMfOut = &manifest;
  checkin_mini(&cimi, &newVid, &err);
  if(blob_size(&err)){
    fileedit_ajax_error(500,"%b",&err);
    goto end_cleanup;
  }
  assert(newVid>0);
  zNewUuid = rid_to_uuid(newVid);
  cgi_set_content_type("application/json");
  CX("{");
  CX("\"uuid\":\"%j\",", zNewUuid);
  CX("\"dryRun\": %s,",
     (CIMINI_DRY_RUN & cimi.flags) ? "true" : "false");
  CX("\"manifest\": \"%j\"", blob_str(&manifest));
  CX("}");
  db_end_transaction(0/*noting that dry-run mode will have already
                      ** set this to rollback mode. */);
end_cleanup:
  fossil_free(zNewUuid);
  blob_reset(&err);
  blob_reset(&manifest);
  CheckinMiniInfo_cleanup(&cimi);
}

/*
** WEBPAGE: fileedit
**
** EXPERIMENTAL and subject to change and removal at any time. The goal
** is to allow online edits of files.
**
** Query parameters:
**
**    file=FILENAME    Repo-relative path to the file.
**    r=VERSION        Checkin version, using any unambiguous
**                     supported symbolic version name.
**    filename=FILENAME    Repo-relative path to the file.
**    checkin=VERSION      Checkin version, using any unambiguous
**                         supported symbolic version name.
**
** All other parameters are for internal use only, submitted via the
** form-submission process, and may change with any given revision of
** this code.
*/
void fileedit_page(){
void fileedit_page(void){
  enum submit_modes {
  SUBMIT_NONE = 0, SUBMIT_SAVE, SUBMIT_PREVIEW,
  SUBMIT_DIFF_SBS, SUBMIT_DIFF_UNIFIED,
  SUBMIT_end /* sentinel for range validation */
  };
  const char * zFilename = PD("file",P("name"));
  const char * zFilename = 0;          /* filename. We'll accept 'name'
                                        /* filename. We'll accept 'name'
                                           because that param is handled
                                           specially by the core. */
  const char * zRev = P("r");           /* checkin version */
  const char * zRev = 0;                /* checkin version */
  const char * zContent = P("content"); /* file content */
  const char * zComment = P("comment"); /* checkin comment */
  const char * zFileMime = 0;           /* File mime type guess */
  CheckinMiniInfo cimi;                 /* Checkin state */
  int submitMode = SUBMIT_NONE;         /* See mapping below */
  int vid, newVid = 0;                  /* checkin rid */
  int frid = 0;                         /* File content rid */
  int previewLn = P("preview_ln")!=0;   /* Line number mode */
  int previewHtmlHeight = 0;            /* iframe height (EMs) */
  int previewRenderMode = FE_RENDER_GUESS; /* preview mode */
  char * zFileUuid = 0;                 /* File content UUID */
  Blob err = empty_blob;                /* Error report */
  Blob submitResult = empty_blob;       /* Error report */
  const char * zFlagCheck = 0;          /* Temp url flag holder */
  Blob endScript = empty_blob;          /* Script code to run at the
                                           end. This content will be
                                           combined into a single JS
                                           function call, thus each
                                           entry must end with a
                                           semicolon. */
  Stmt stmt = empty_Stmt;
  const int loadMode = 0;               /* See next comment block */
  /* loadMode: How to populate the TEXTAREA:
  const char *zAjax = P("ajax");

  **
  ** 0: HTML encode: despite my personal reservations regarding HTML
  if(0!=zAjax){
    if(0==strcmp("content",zAjax)){
  ** escaping, this seems to be the only reliable approach
  ** until/unless we completely AJAXify this page.
  **
  ** 1: JSON mode: JSON-izes the file content and injects it, via JS,
  ** into the editor TEXTAREA. This works wonderfully until the input
  ** file contains an raw <SCRIPT> tag, at which points the HTML
  ** parser chokes on it.
      fileedit_ajax_content();
    }else if(0==strcmp("preview",zAjax)){
      fileedit_ajax_preview();
    }else if(0==strcmp("filelist",zAjax)){
      fileedit_ajax_filelist();
    }else if(0==strcmp("diff",zAjax)){
      fileedit_ajax_diff();
    }else if(0==strcmp("commit",zAjax)){
      fileedit_ajax_commit();
    }else{
  **
  ** 2: AJAX mode: can only load content from the db, not preview/dry-run
  ** content. Unless this page is refactored to work solely over AJAX
  ** (which is a potential TODO), this method is only useful on the
      fileedit_ajax_error(500, "Unhandled 'ajax' value.");
  ** initial hit to this page, where the file is loaded.
  **
  ** loadMode is not generally configurable: change it only for
  ** testing/development purposes.
    }
    return;
  */
#define fail(EXPR) blob_appendf EXPR; goto end_footer

  }
  assert(loadMode==0 || loadMode==1 || loadMode==2);
  login_check_credentials();
  if( !g.perm.Write ){
    login_needed(g.anon.Write);
    return;
  }
  db_begin_transaction();
  CheckinMiniInfo_init(&cimi);
  submitMode = atoi(PD("submit","0"));
  if(submitMode < SUBMIT_NONE || submitMode >= SUBMIT_end){
    submitMode = 0;
  }
  zFlagCheck = P("comment_mimetype");
  if(zFlagCheck){
    cimi.zCommentMimetype = mprintf("%s",zFlagCheck);
    zFlagCheck = 0;
  }
  cimi.zUser = mprintf("%s",g.zLogin);

  style_header("File Editor");
  /* As of this point, don't use return or fossil_fatal(), use
  ** fail((&err,...))  instead so that we can be sure to do any
  ** cleanup and end the transaction cleanly.
  /* As of this point, don't use return or fossil_fatal(). Write any
  ** error in (&err) and goto end_footer instead so that we can be
  ** sure to do any cleanup and end the transaction cleanly.
  */
  if(!zRev || !*zRev || !zFilename || !*zFilename){
    fail((&err,"Missing required URL parameters: "
          "file=FILE and r=CHECKIN"));
  }
  if(0==fileedit_is_editable(zFilename)){
    fail((&err,"Filename <code>%h</code> is disallowed "
          "by the <code>fileedit-glob</code> repository "
  if(fileedit_setup_cimi_from_p(&cimi, &err)==0){
          "setting.",
          zFilename));
    zFilename = cimi.zFilename;
  }
  vid = symbolic_name_to_rid(zRev, "ci");
  if(0==vid){
    fail((&err,"Could not resolve checkin version."));
  }
  cimi.zFilename = mprintf("%s",zFilename);
  zFileMime = mimetype_from_name(zFilename);

  /* Find the repo-side file entry or fail... */
  cimi.zParentUuid = rid_to_uuid(vid);
  zFileUuid = fileedit_file_uuid(zFilename, vid, &cimi.filePerm);
  if(!zFileUuid){
    fail((&err,"Checkin [%S] does not contain file: "
          "<code>%h</code>",
          cimi.zParentUuid, zFilename));
    zRev = cimi.zParentUuid;
  }
  else if(PERM_LNK==cimi.filePerm){
    fail((&err,"Editing symlinks is not permitted."));
  }
  frid = fast_uuid_to_rid(zFileUuid);
  assert(frid);
    assert(zRev);

  /* Read file content from submit request or repo... */
  if(zContent==0){
    content_get(frid, &cimi.fileContent);
    zContent = blob_size(&cimi.fileContent)
      ? blob_str(&cimi.fileContent) : NULL;
  }else{
    blob_init(&cimi.fileContent,zContent,-1);
  }
  if(looks_like_binary(&cimi.fileContent)){
    fail((&err,"File appears to be binary. Cannot edit: "
          "<code>%h</code>",zFilename));
    assert(zFilename);
  }

    zFileMime = mimetype_from_name(cimi.zFilename);
  /*
  ** TODO?: date-override date selection field. Maybe use
  ** an input[type=datetime-local].
  */
  if(SUBMIT_NONE==submitMode || P("dry_run")!=0){
    cimi.flags |= CIMINI_DRY_RUN;
  }
  if(P("allow_fork")!=0){
    cimi.flags |= CIMINI_ALLOW_FORK;
  }

  if(P("allow_older")!=0){
    cimi.flags |= CIMINI_ALLOW_OLDER;
  }
  if(P("exec_bit")!=0){
    cimi.filePerm = PERM_EXE;
  }
  if(P("allow_merge_conflict")!=0){
    cimi.flags |= CIMINI_ALLOW_MERGE_MARKER;
  }
  if(P("prefer_delta")!=0){
    cimi.flags |= CIMINI_PREFER_DELTA;
  }
  /* EOL conversion policy... */
  {
    const int eolMode = submitMode==SUBMIT_NONE
      ? 0 : atoi(PD("eol","0"));
    switch(eolMode){
      case 1: cimi.flags |= CIMINI_CONVERT_EOL_UNIX; break;
      case 2: cimi.flags |= CIMINI_CONVERT_EOL_WINDOWS; break;
      default: cimi.flags |= CIMINI_CONVERT_EOL_INHERIT; break;
    }
  }
  
  /********************************************************************
  ** All errors which "could" have happened up to this point are of a
  ** degree which keep us from rendering the rest of the page, and
  ** thus fail() has already skipped to the end of the page to render
  ** the errors. Any up-coming errors, barring malloc failure or
  ** similar, are not "that" fatal. We can/should continue rendering
  ** the page, then output the error message at the end.
  ** thus have already caused us to skipped to the end of the page to
  ** render the errors. Any up-coming errors, barring malloc failure
  ** or similar, are not "that" fatal. We can/should continue
  ** rendering the page, then output the error message at the end.
  **
  ** Because we cannot intercept the output of the PREVIEW and DIFF
  ** rendering, we have to delay the "real work" for those modes until
  ** after the rest of the page has been rendered. In the case of
  ** SAVE, we can capture all of the output, and thus can perform that
  ** work before rendering, which is important so that we have the
  ** proper version information when rendering the rest of the page.
  ********************************************************************/
#undef fail
  while(SUBMIT_SAVE==submitMode){
    Blob manifest = empty_blob;
    /*cimi.flags |= CIMINI_STRONGLY_PREFER_DELTA;*/
    if(zComment && *zComment){
      blob_append(&cimi.comment, zComment, -1);
    }else{
      blob_append(&err,"Empty checkin comment is not permitted.",-1);
      break;
    }

    cimi.pMfOut = &manifest;
    checkin_mini(&cimi, &newVid, &err);
    if(newVid!=0){
      char * zNewUuid = rid_to_uuid(newVid);
      blob_appendf(&submitResult,
  {
    /* The CSS for this page lives in a common file but much of it we
    ** don't want inadvertently being used by other pages. We don't
    ** 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_emit_script_tag(0,0);
                   "<h3>Manifest%s: %S</h3><pre>"
                   "<code class='fileedit-manifest'>%h</code>"
    CX("document.body.classList.add('fileedit');\n");
                   "</pre>",
                   (cimi.flags & CIMINI_DRY_RUN) ? " (dry run)" : "",
                   zNewUuid, blob_str(&manifest));
      if(CIMINI_DRY_RUN & cimi.flags){
        fossil_free(zNewUuid);
      }else{
        /* Update cimi version info... */
        assert(cimi.pParent);
        assert(cimi.zParentUuid);
        fossil_free(zFileUuid);
        zFileUuid = fileedit_file_uuid(cimi.zFilename, newVid, 0);
        manifest_destroy(cimi.pParent);
        cimi.pParent = 0;
        fossil_free(cimi.zParentUuid);
        cimi.zParentUuid = zNewUuid;
        zComment = 0;
        cimi.flags |= CIMINI_DRY_RUN /* for sanity's sake */;
      }
    }
    /* On error, the error message is in the err blob and will
    ** be emitted at the end. */
    style_emit_script_tag(1,0);
    cimi.pMfOut = 0;
    blob_reset(&manifest);
    break;
  }

  CX("<h1>Editing:</h1>");
  CX("<p class='fileedit-hint'>");
  CX("File: "
     "[<a id='finfo-link' href='%R/finfo?name=%T&m=%!S'>info</a>] "
     "<code>%h</code><br>",
     zFilename, zFileUuid, zFilename);
  CX("Checkin Version: "
     "[<a id='r-link' href='%R/info/%!S'>info</a>] "
     "<code id='r-label'>%s</code><br>",
     cimi.zParentUuid, cimi.zParentUuid);
  CX("Permalink: <code>"
     "<a id='permalink' href='%R/fileedit?file=%T&r=%!S'>"
     "/fileedit?file=%T&r=%!S</a></code><br>"
     "(Clicking the permalink will reload the page and discard "
     "all edits!)",
     zFilename, cimi.zParentUuid,
     zFilename, cimi.zParentUuid);
  CX("</p>");
  CX("<p>This page is <em>NEW AND EXPERIMENTAL</em>. "
     "USE AT YOUR OWN RISK, preferably on a test "
     "repo.</p>\n");
  
  CX("<form action='%R/fileedit#options' method='POST' "
     "class='fileedit'>\n");

  /* Status bar */
  CX("<div id='fossil-status-bar'>Async. status messages will go "
     "here.</div>\n"/* will be moved into the tab container via JS */);

  /* Main tab container... */
  CX("<div id='fileedit-tabs' class='tab-container'></div>");

  /******* Hidden fields *******/
  /***** File/version info tab *****/
  CX("<input type='hidden' name='r' value='%s'>",
     cimi.zParentUuid);
  CX("<input type='hidden' name='file' value='%T'>",
     zFilename);

  {
  /******* Content *******/
  CX("<h3>File Content</h3>\n");
  CX("<textarea name='content' id='fileedit-content' "
     "rows='20' cols='80'>");
    CX("<div id='fileedit-tab-version' "
       "data-tab-parent='fileedit-tabs' "
       "data-tab-label='File Info &amp; Selection'"
       ">");
  if(0==loadMode){
    CX("%h",blob_str(&cimi.fileContent));
    CX("File: "
  }else{
    CX("Loading...");
    /* Performed via JS later on */
       "<code id='finfo-file-name'>" "???" "</code><br>");
    CX("Checkin Version: "
       "[<a id='timeline-link' href='#'>/timeline</a>] "
       "[<a id='r-link' href='#'>/info</a>] "
       /* %R/info/%!S */
  }
  CX("</textarea>\n");
       "<code id='r-label'>" "???" "</code><br>"
       );
  /******* Flags/options *******/
  CX("<fieldset class='fileedit-options' id='options'>"
     "<legend>Options</legend><div>"
     /* Chrome does not sanely lay out multiple
     ** fieldset children after the <legend>, so
     ** a containing div is necessary. */);
    CX("Permalink: <code>"
       "<a id='permalink' href='#'>" "???" "</a></code><br>"
       "(Clicking the permalink will reload the page and discard "
       "all edits!)");

    CX("<h1>Select a file to edit:</h1>");
    CX("<div id='fileedit-file-selector'></div>");
  style_labeled_checkbox("dry_run", "Dry-run?", "1",
                         "In dry-run mode, the Save button performs "
                         "all work needed for saving but then rolls "
    CX("</div>"/*#fileedit-tab-version*/);
  }
  
                         "back the transaction, and thus does not "
                         "really save.",
  /******* Content tab *******/
                         cimi.flags & CIMINI_DRY_RUN);
  style_labeled_checkbox("allow_fork", "Allow fork?", "1",
  {
    CX("<div id='fileedit-tab-content' "
       "data-tab-parent='fileedit-tabs' "
       "data-tab-label='File Content'"
                         "Allow saving to create a fork?",
                         cimi.flags & CIMINI_ALLOW_FORK);
       ">");
  style_labeled_checkbox("allow_older", "Allow older?", "1",
                         "Allow saving against a parent version "
                         "which has a newer timestamp?",
                         cimi.flags & CIMINI_ALLOW_OLDER);
  style_labeled_checkbox("exec_bit", "Executable?", "1",
                         "Set the executable bit?",
                         PERM_EXE==cimi.filePerm);
  style_labeled_checkbox("allow_merge_conflict",
                         "Allow merge conflict markers?", "1",
                         "Allow saving even if the content contains "
                         "what appear to be fossil merge conflict "
                         "markers?",
                         cimi.flags & CIMINI_ALLOW_MERGE_MARKER);
    CX("<div class='fileedit-options "
       "flex-container flex-row child-gap-small'>");
    if(1){
      /* Discard/reload button. Leave this out until we have a
      ** nice way of offering confirmation, e.g. like the old
      ** jQuery.confirmer plugin which required a 2nd click of the
      ** button within X seconds to confirm. Right now it's simply
      ** to easy to tap by accident. */
      CX("<button class='fileedit-content-reload confirmer' "
         "title='Reload the file from the server, discarding "
         "any local edits. To help avoid accidental loss of "
         "edits, it requires confirmation (a second click) within "
  style_labeled_checkbox("prefer_delta",
                         "Prefer delta manifest?", "1",
                         "Will create a delta manifest, instead of "
                         "baseline, if conditions are favorable to do "
         "a few seconds or it will not reload.'"
         ">Discard &amp; Reload</button>");
                         "so. This option is only a suggestion.",
                         cimi.flags & CIMINI_PREFER_DELTA);
  style_select_list_int("eol", "EOL Style",
                        "EOL conversion policy, noting that "
    }
    style_select_list_int("select-font-size",
                          "editor_font_size", "Editor font size",
                          NULL/*tooltip*/,
                        "form-processing may implicitly change the "
                        "line endings of the input.",
                        (cimi.flags & CIMINI_CONVERT_EOL_UNIX)
                        ? 1 : (cimi.flags & CIMINI_CONVERT_EOL_WINDOWS
                               ? 2 : 0),
                        "Inherit", 0,
                        "Unix", 1,
                          100,
                          "100%", 100, "125%", 125,
                          "150%", 150, "175%", 175,
                        "Windows", 2,
                        NULL);

  CX("</div></fieldset>") /* end of checkboxes */;

                          "200%", 200, NULL);
    CX("</div>");
    CX("<div class='flex-container flex-row'>");
    CX("<textarea name='content' id='fileedit-content-editor' "
       "class='fileedit' "
       "rows='20' cols='80'>");
    CX("Loading...");
    CX("</textarea>");
    CX("</div>"/*textarea wrapper*/);
    CX("</div>"/*#tab-file-content*/);
  }

  /****** Preview tab ******/
  {
    CX("<div id='fileedit-tab-preview' "
       "data-tab-parent='fileedit-tabs' "
       "data-tab-label='Preview'"
  /******* Comment *******/
  CX("<a id='comment'></a>");
       ">");
  CX("<fieldset><legend>Commit message</legend><div>");
  CX("<textarea name='comment' rows='3' cols='80'>");
  /* ^^^ adding the 'required' attribute means we cannot even submit
  ** for PREVIEW mode if it's empty :/. */
  if(zComment && *zComment){
    CX("%h", zComment);
  }

  CX("</textarea>\n");
  CX("<div class='fileedit-hint'>Comments use the Fossil wiki markup "
    CX("<div class='fileedit-options flex-container flex-row'>");
     "syntax.</div>\n"/*TODO: select for fossil/md/plain text*/);
  CX("</div></fieldset>\n");

    CX("<button id='btn-preview-refresh' "
       "data-f-preview-from='fileedit-content-editor' "
  /******* Buttons *******/
  CX("<a id='buttons'></a>");
  CX("<fieldset class='fileedit-options'>"
       /* ^^^ text source elem ID*/
       "data-f-preview-via='_postPreview' "
       /* ^^^ fossil.page[methodName](content, callback) */
       "data-f-preview-to='fileedit-tab-preview-wrapper' "
     "<legend>Tell the server to...</legend><div>");
  CX("<button type='submit' name='submit' value='%d'>"
     "Commit</button>", SUBMIT_SAVE);
       /* ^^^ dest elem ID */
       ">Refresh</button>");
  CX("<button type='submit' name='submit' value='%d'>"
     "Preview</button>", SUBMIT_PREVIEW);
  {
    /* Preview rendering mode selection... */
    previewRenderMode = atoi(PD("preview_render_mode","0"));
    /* Toggle auto-update of preview when the Preview tab is selected. */
    style_labeled_checkbox("cb-preview-autoupdate",
                           NULL,
                           "Auto-refresh?",
                           "1", 1,
                           "If on, the preview will automatically "
                           "refresh when this tab is selected.");

    /* Default preview rendering mode selection... */
    previewRenderMode = zFileMime
    if(0==previewRenderMode){
      previewRenderMode = fileedit_render_mode_for_mimetype(zFileMime);
      ? fileedit_render_mode_for_mimetype(zFileMime)
    }
    style_select_list_int("preview_render_mode",
      : FE_RENDER_GUESS;
    style_select_list_int("select-preview-mode",
                          "preview_render_mode",
                          "Preview Mode",
                          "Preview mode format.",
                          previewRenderMode,
                          "Guess", FE_RENDER_GUESS,
                          "Wiki/Markdown", FE_RENDER_WIKI,
                          "HTML (iframe)", FE_RENDER_HTML,
                          "HTML (iframe)", FE_RENDER_HTML_IFRAME,
                          "HTML (inline)", FE_RENDER_HTML_INLINE,
                          "Plain Text", FE_RENDER_PLAIN_TEXT,
                          NULL);
    /*
    if(FE_RENDER_HTML==previewRenderMode){
      /* HTML preview mode iframe height... */
    ** Set up a JS-side mapping of the FE_RENDER_xyz values. This is
    ** used for dynamically toggling certain UI components on and off.
    */
    blob_appendf(&endScript, "fossil.page.previewModes={"
      if(submitMode==SUBMIT_PREVIEW){
        previewHtmlHeight = atoi(PD("preview_html_ems","0"));
      }else{
        previewHtmlHeight = 40;
      }
      /* Allow selection of HTML preview iframe height */
      style_select_list_int("preview_html_ems",
                            "Preview IFrame Height (EMs)",
                            "Height (in EMs) of the iframe used for "
                            "HTML preview",
                            previewHtmlHeight,
                            "", 20, "", 40,
                            "", 60, "", 80,
                            "", 100, NULL);
                 "guess: %d, %d: 'guess', wiki: %d, %d: 'wiki',"
                 "htmlIframe: %d, %d: 'htmlIframe', "
                 "htmlInline: %d, %d: 'htmlInline', "
                 "text: %d, %d: 'text'"
                 "};\n",
                 FE_RENDER_GUESS, FE_RENDER_GUESS,
                 FE_RENDER_WIKI, FE_RENDER_WIKI,
                 FE_RENDER_HTML_IFRAME, FE_RENDER_HTML_IFRAME,
                 FE_RENDER_HTML_INLINE, FE_RENDER_HTML_INLINE,
                 FE_RENDER_PLAIN_TEXT, FE_RENDER_PLAIN_TEXT);
    /* Allow selection of HTML preview iframe height */
    previewHtmlHeight = 40;
    style_select_list_int("select-preview-html-ems",
                          "preview_html_ems",
                          "HTML Preview IFrame Height (EMs)",
                          "Height (in EMs) of the iframe used for "
                          "HTML preview",
                          previewHtmlHeight,
                          "", 20, "", 40,
                          "", 60, "", 80,
                          "", 100, NULL);
    }
    else if(FE_RENDER_PLAIN_TEXT==previewRenderMode){
      style_labeled_checkbox("preview_ln",
                             "Add line numbers to plain-text previews?",
                             "1",
                             "If on, plain-text files (only) will get "
                             "line numbers added to the preview.",
                             previewLn);
    }
  }
  CX("<button type='submit' name='submit' value='%d'>"
     "Diff (SBS)</button>", SUBMIT_DIFF_SBS);
  CX("<button type='submit' name='submit' value='%d'>"
     "Diff (Unified)</button>", SUBMIT_DIFF_UNIFIED);
  CX("</div></fieldset>");

  /******* End of form *******/    
  CX("</form>\n");

    /* Selection of line numbers for text preview */
    style_labeled_checkbox("cb-line-numbers",
                           "preview_ln",
                           "Add line numbers to plain-text previews?",
                           "1", P("preview_ln")!=0,
                           "If on, plain-text files (only) will get "
                           "line numbers added to the preview.");
    CX("</div>"/*.fileedit-options*/);
    CX("<div id='fileedit-tab-preview-wrapper'></div>");
    CX("</div>"/*#fileedit-tab-preview*/);
  }

  /****** Diff tab ******/
  {
    CX("<div id='fileedit-tab-diff' "
       "data-tab-parent='fileedit-tabs' "
       "data-tab-label='Diff'"
       ">");

    CX("<div class='fileedit-options flex-container flex-row' "
       "id='fileedit-tab-diff-buttons'>"
       "<button class='sbs'>Side-by-side</button>"
       "<button class='unified'>Unified</button>"
       "</div>");
    CX("<div id='fileedit-tab-diff-wrapper'>"
       "Diffs will be shown here."
       "</div>");
    CX("</div>"/*#fileedit-tab-diff*/);
  }

  /****** Commit ******/
  CX("<div id='fileedit-tab-commit' "
     "data-tab-parent='fileedit-tabs' "
     "data-tab-select='1' "
     "data-tab-label='Commit'"
     ">");

  {
    /******* Commit flags/options *******/
    CX("<div class='fileedit-options flex-container flex-row'>");
    style_labeled_checkbox("cb-dry-run",
                           "dry_run", "Dry-run?", "1", 1,
                           "In dry-run mode, the Save button performs "
                           "all work needed for saving but then rolls "
                           "back the transaction, and thus does not "
                           "really save.");
    style_labeled_checkbox("cb-allow-fork",
                           "allow_fork", "Allow fork?", "1",
  /*
  ** We cannot intercept the output for PREVIEW
  ** and DIFF modes, and therefore have to render those
  ** last.
                           cimi.flags & CIMINI_ALLOW_FORK,
                           "Allow saving to create a fork?");
    style_labeled_checkbox("cb-allow-older",
                           "allow_older", "Allow older?", "1",
  */
  if(SUBMIT_PREVIEW==submitMode){
    int pflags = 0;
    if(previewLn) pflags |= FE_PREVIEW_LINE_NUMBERS;
    fileedit_render_preview(&cimi.fileContent, cimi.zFilename, pflags,
                            previewRenderMode, previewHtmlHeight);
                           cimi.flags & CIMINI_ALLOW_OLDER,
                           "Allow saving against a parent version "
                           "which has a newer timestamp?");
    style_labeled_checkbox("cb-exec-bit",
                           "exec_bit", "Executable?", "1",
                           PERM_EXE==cimi.filePerm,
                           "Set the executable bit?");
    style_labeled_checkbox("cb-allow-merge-conflict",
                           "allow_merge_conflict",
                           "Allow merge conflict markers?", "1",
                           cimi.flags & CIMINI_ALLOW_MERGE_MARKER,
                           "Allow saving even if the content contains "
                           "what appear to be fossil merge conflict "
                           "markers?");
    style_labeled_checkbox("cb-prefer-delta",
                           "prefer_delta",
  }else if(SUBMIT_DIFF_SBS==submitMode
           || SUBMIT_DIFF_UNIFIED==submitMode){
    fileedit_render_diff(&cimi.fileContent, frid, cimi.zParentUuid,
                           "Prefer delta manifest?", "1",
                           cimi.flags & CIMINI_PREFER_DELTA,
                         SUBMIT_DIFF_SBS==submitMode);
  }

  /* Dynamically populate the editor... */
  fileedit_emit_script_fetch();
  if(1==loadMode || (2==loadMode && submitMode>SUBMIT_NONE)){
    char const * zQuoted = 0;
    if(blob_size(&cimi.fileContent)>0){
      db_prepare(&stmt, "SELECT json_quote(%B)", &cimi.fileContent);
      db_step(&stmt);
                           "Will create a delta manifest, instead of "
                           "baseline, if conditions are favorable to "
                           "do so. This option is only a suggestion.");
    style_select_list_int("select-eol-style",
                          "eol", "EOL Style",
                          "EOL conversion policy, noting that "
                          "webpage-side processing may implicitly change "
                          "the line endings of the input.",
                          (cimi.flags & CIMINI_CONVERT_EOL_UNIX)
                          ? 1 : (cimi.flags & CIMINI_CONVERT_EOL_WINDOWS
                                 ? 2 : 0),
                          "Inherit", 0,
                          "Unix", 1,
                          "Windows", 2,
                          NULL);
      zQuoted = db_column_text(&stmt,0);
    }
    blob_appendf(&endScript,
    CX("</div>"/*checkboxes*/);
  }

                 "/* populate editor form */\n"
                 "document.getElementById('fileedit-content')"
                 ".value=%s;", zQuoted ? zQuoted : "'';\n");
    if(stmt.pStmt){
      db_finalize(&stmt);
    }
  { /******* Commit comment, button, and result manifest *******/
    CX("<fieldset class='fileedit-options commit-message'>"
       "<legend>Message (required)</legend><div>\n");
    /* We have two comment input fields, defaulting to single-line
    ** mode. JS code sets up the ability to toggle between single-
    ** and multi-line modes. */
    CX("<input type='text' name='comment' "
       "id='fileedit-comment'></input>");
    CX("<textarea name='commentBig' class='hidden' "
       "rows='5' id='fileedit-comment-big'></textarea>\n");
    { /* comment options... */
      CX("<div class='flex-container flex-column child-gap-small'>");
      CX("<button id='comment-toggle' "
         "title='Toggle between single- and multi-line comment mode, "
         "noting that switching from multi- to single-line will cause "
         "newlines to get stripped.'"
         ">Toggle single-/multi-line</button> ");
      if(0){
        /* Manifests support an N-card (comment mime type) but it has
        ** yet to be honored where comments are rendered, so we don't
        ** currently offer it as an option here:
        ** https://fossil-scm.org/forum/forumpost/662da045a1
        **
        ** If/when it's ever implemented, simply enable this block and
        ** adjust the container's layout accordingly (as of this
        ** writing, that means changing the CSS class from
        ** 'flex-container flex-column' to 'flex-container flex-row').
        */
        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");
      }
  }else if(2==loadMode){
    assert(submitMode==SUBMIT_NONE);
    blob_appendf(&endScript,
      CX("<div class='fileedit-hint flex-container flex-row'>"
         "(Warning: switching from multi- to single-line mode will "
         "strip out all newlines!)</div>");
                 "window.fossil.fetch('raw/%s',{"
                 "onload: (r)=>document.getElementById('fileedit-content')"
    }
    CX("</div></fieldset>\n"/*commit comment options*/);
    CX("<div class='flex-container flex-column' "
       "id='fileedit-commit-button-wrapper'>"
                 ".value=r,"
                 "onerror:()=>document.getElementById('fileedit-content')"
                 ".value="
                 "'Error loading content'"
                 "});\n", zFileUuid);
  }

end_footer:
  zContent = 0;
  fossil_free(zFileUuid);
       "<button id='fileedit-btn-commit'>Commit</button>"
       "</div>\n");
    CX("<div id='fileedit-manifest'></div>\n"
       /* Manifest gets rendered here after a commit. */);
  }

  CX("</div>"/*#fileedit-tab-commit*/);

  {
    /* Dynamically populate the editor or display a warning
    ** about having no file loaded... */
    blob_appendf(&endScript,
                 "window.addEventListener('load',");
    if(zRev && zFilename){
  if(stmt.pStmt){
    db_finalize(&stmt);
  }
  if(blob_size(&err)){
    CX("<div class='fileedit-error-report'>%s</div>",
       blob_str(&err));
  }else if(blob_size(&submitResult)){
      assert(0==blob_size(&err));
      blob_appendf(&endScript,
                   "()=>fossil.page.loadFile(\"%j\",'%j')",
                   zFilename, cimi.zParentUuid);
    }else{
      blob_appendf(&endScript,"function(){");
      if(blob_size(&err)>0){
        blob_appendf(&endScript,
                     "fossil.error(\"%j\");\n"
                     "fossil.page.tabs.switchToTab(0);\n",
                     blob_str(&err));
      }else{
    CX("%b",&submitResult);
  }
  blob_reset(&submitResult);
        blob_appendf(&endScript,
                     "fossil.error('No file/version selected.')");
      }
      blob_appendf(&endScript,"}");
    }
    blob_appendf(&endScript,", false);\n");
  }

  if(stmt.pStmt){
    db_finalize(&stmt);
  }
  blob_reset(&err);
  CheckinMiniInfo_cleanup(&cimi);
  style_emit_script_fossil_bootstrap(0);
  style_emit_script_fetch(0);
  style_emit_script_tabs(0);
  style_emit_script_confirmer(0);
  style_emit_script_builtin(0, "fossil.page.fileedit.js");
  if(blob_size(&endScript)>0){
    style_emit_script_tag(0);
    style_emit_script_tag(0,0);
    CX("(function(){\n");
    CX("try{\n%b\n}"
       "catch(e){"
       "fossil.error(e);\n"
    CX("try{\n%b\n}catch(e){console.error('Exception:',e)}\n",
       "console.error('Exception:',e);\n"
       "}\n",
       &endScript);
    CX("})();");
    style_emit_script_tag(1);
    style_emit_script_tag(1,0);
  }
  db_end_transaction(0/*noting that dry-run mode will have already
  db_end_transaction(0);
                      ** set this to rollback mode. */);
  style_footer();
}
Changes to src/finfo.c.
197
198
199
200
201
202
203
204

205
206
207
208
209
210
211
197
198
199
200
201
202
203

204
205
206
207
208
209
210
211







-
+







        "   AND event.objid=ci.rid"
        " ORDER BY event.mtime DESC LIMIT %d OFFSET %d",
        TAG_BRANCH, zFilename, filename_collation(),
        iLimit, iOffset
    );
    blob_zero(&line);
    if( iBrief ){
      fossil_print("History of %s\n", blob_str(&fname));
      fossil_print("History for %s\n", blob_str(&fname));
    }
    while( db_step(&q)==SQLITE_ROW ){
      const char *zFileUuid = db_column_text(&q, 0);
      const char *zCiUuid = db_column_text(&q,1);
      const char *zDate = db_column_text(&q, 2);
      const char *zCom = db_column_text(&q, 3);
      const char *zUser = db_column_text(&q, 4);
294
295
296
297
298
299
300
301

302
303
304
305
306
307
308
294
295
296
297
298
299
300

301
302
303
304
305
306
307
308







-
+







** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in
** year-month-day form, it may be truncated, and it may also name a
** timezone offset from UTC as "-HH:MM" (westward) or "+HH:MM"
** (eastward). Either no timezone suffix or "Z" means UTC.
*/
void finfo_page(void){
  Stmt q;
  const char *zFilename;
  const char *zFilename = PD("name","");
  char zPrevDate[20];
  const char *zA;
  const char *zB;
  int n;
  int baseCheckin;
  int origCheckin = 0;
  int fnid;
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
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







+
+
-
+
+
+
+


















-

-







  int tmFlags = 0;            /* Viewing mode */
  const char *zStyle;         /* Viewing mode name */
  const char *zMark;          /* Mark this version of the file */
  int selRid = 0;             /* RID of the marked file version */

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename);
  if( fnid==0 ){
  style_header("File History");
    style_header("No such file");
  }else{
    style_header("History for %s", zFilename);
  }
  login_anonymous_available();
  tmFlags = timeline_ss_submenu();
  if( tmFlags & TIMELINE_COLUMNAR ){
    zStyle = "Columnar";
  }else if( tmFlags & TIMELINE_COMPACT ){
    zStyle = "Compact";
  }else if( tmFlags & TIMELINE_VERBOSE ){
    zStyle = "Verbose";
  }else if( tmFlags & TIMELINE_CLASSIC ){
    zStyle = "Classic";
  }else{
    zStyle = "Modern";
  }
  url_initialize(&url, "finfo");
  if( brBg ) url_add_parameter(&url, "brbg", 0);
  if( uBg ) url_add_parameter(&url, "ubg", 0);
  baseCheckin = name_to_rid_www("ci");
  zPrevDate[0] = 0;
  zFilename = PD("name","");
  cookie_render();
  fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename);
  if( fnid==0 ){
    @ No such file: %h(zFilename)
    style_footer();
    return;
  }
  if( g.perm.Admin ){
    style_submenu_element("MLink Table", "%R/mlink?name=%t", zFilename);
447
448
449
450
451
452
453
454

455
456
457
458
459
460
461
450
451
452
453
454
455
456

457
458
459
460
461
462
463
464







-
+







    if( origCheckin ){
      zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", origCheckin);
      zLink = href("%R/info/%!S", zUuid);
      blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid);
      fossil_free(zUuid);
    }
  }else{
    blob_appendf(&title, "History of ");
    blob_appendf(&title, "History for ");
    hyperlinked_path(zFilename, &title, 0, "tree", "");
    if( fShowId ) blob_appendf(&title, " (%d)", fnid);
  }
  if( uBg ){
    blob_append(&title, " (color-coded by user)", -1);
  }
  @ <h2>%b(&title)</h2>
522
523
524
525
526
527
528
529

530
531
532
533
534
535
536
525
526
527
528
529
530
531

532
533
534
535
536
537
538
539







-
+







    zTime[5] = 0;
    if( frid==selRid ){
      @ <tr class='timelineSelected'>
    }else{
      @ <tr>
    }
    @ <td class="timelineTime">\
    @ %z(href("%R/artifact/%!S",zUuid))%s(zTime)</a></td>
    @ %z(href("%R/file?name=%T&ci=%!S",zFilename,zCkin))%s(zTime)</a></td>
    @ <td class="timelineGraph"><div id="m%d(gidx)" class="tl-nodemark"></div>
    @ </td>
    if( zBgClr && zBgClr[0] ){
      @ <td class="timeline%s(zStyle)Cell" id='mc%d(gidx)'>
    }else{
      @ <td class="timeline%s(zStyle)Cell">
    }
559
560
561
562
563
564
565
566

567
568
569
570
571
572
573
562
563
564
565
566
567
568

569
570
571
572
573
574
575
576







-
+







    }
    if( tmFlags & TIMELINE_COMPACT ){
      cgi_printf("<span class='clutter' id='detail-%d'>",frid);
    }
    cgi_printf("<span class='timeline%sDetail'>", zStyle);
    if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ) cgi_printf("(");
    if( zUuid && (tmFlags & TIMELINE_VERBOSE)==0 ){
      @ file:&nbsp;%z(href("%R/artifact/%!S",zUuid))[%S(zUuid)]</a>
      @ file:&nbsp;%z(href("%R/file?name=%T&ci=%!S",zFilename,zCkin))[%S(zUuid)]</a>
      if( fShowId ){
        int srcId = delta_source_rid(frid);
        if( srcId>0 ){
          @ id:&nbsp;%d(frid)&larr;%d(srcId)
        }else{
          @ id:&nbsp;%d(frid)
        }
620
621
622
623
624
625
626
627

628
629
630
631
632
633
634
623
624
625
626
627
628
629

630
631
632
633
634
635
636
637







-
+







      @ %z(href("%R/blame?filename=%h&checkin=%s",z,zCkin))
      @ [blame]</a>
      @ %z(href("%R/timeline?n=all&uf=%!S",zUuid))[check-ins&nbsp;using]</a>
      if( fpid>0 ){
        @ %z(href("%R/fdiff?v1=%!S&v2=%!S",zPUuid,zUuid))[diff]</a>
      }
      if( fileedit_is_editable(zFilename) ){
        @ %z(href("%R/fileedit?file=%T&r=%!S",zFilename,zCkin))[edit]</a>
        @ %z(href("%R/fileedit?filename=%T&checkin=%!S",zFilename,zCkin))[edit]</a>
      }
      @ </span></span>
    }
    if( fDebug & FINFO_DEBUG_MLINK ){
      int ii;
      char *zAncLink;
      @ <br />fid=%d(frid) pid=%d(fpid) mid=%d(fmid)
Added src/fossil.bootstrap.js.

















































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
"use strict";
(function(global){
  /* Bootstrapping bits for the global.fossil object. Must be
     loaded after style.c:style_emit_script_tag() has initialized
     that object.
  */

  const F = global.fossil;

  /**
     Returns the current time in something approximating
     ISO-8601 format.
  */
  const timestring = function f(){
    if(!f.rx1){
      f.rx1 = /\.\d+Z$/;
    }
    const d = new Date();
    return d.toISOString().replace(f.rx1,'').split('T').join(' ');
  };

  /*
  ** By default fossil.message() sends its arguments console.debug(). If
  ** fossil.message.targetElement is set, it is assumed to be a DOM
  ** element, its innerText gets assigned to the concatenation of all
  ** arguments (with a space between each), and the CSS 'error' class is
  ** removed from the object. Pass it a falsy value to clear the target
  ** element.
  **
  ** Returns this object.
  */
  F.message = function f(msg){
    const args = Array.prototype.slice.call(arguments,0);
    const tgt = f.targetElement;
    args.unshift(timestring(),'UTC:');
    if(tgt){
      tgt.classList.remove('error');
      tgt.innerText = args.join(' ');
    }
    else{
      args.unshift('Fossil status:');
      console.debug.apply(console,args);
    }
    return this;
  };
  /*
  ** Set default message.targetElement to #fossil-status-bar, if found.
  */
  F.message.targetElement =
    document.querySelector('#fossil-status-bar');
  /*
  ** By default fossil.error() sends its first argument to
  ** console.error(). If fossil.message.targetElement (yes,
  ** fossil.message) is set, it adds the 'error' CSS class to
  ** that element and sets its content as defined for message().
  **
  ** Returns this object.
  */
  F.error = function f(msg){
    const args = Array.prototype.slice.call(arguments,0);
    const tgt = F.message.targetElement;
    args.unshift(timestring(),'UTC:');
    if(tgt){
      tgt.classList.add('error');
      tgt.innerText = args.join(' ');
    }
    else{
      args.unshift('Fossil error:');
      console.error.apply(console,args);
    }
    return this;
  };

  /**
     For each property in the given object, its key/value are encoded
     for use as URL parameters and the combined string is
     returned. e.g. {a:1,b:2} encodes to "a=1&b=2".

     If the 2nd argument is an array, each encoded element is appended
     to that array and tgtArray is returned. The above object would be
     appended as ['a','=','1','&','b','=','2']. This form is used for
     building up parameter lists before join('')ing the array to create
     the result string.

     If passed a truthy 3rd argument, it does not really encode each
     component - it simply concatenates them together.
  */
  F.encodeUrlArgs = function(obj,tgtArray,fakeEncode){
    if(!obj) return '';
    const a = (tgtArray instanceof Array) ? tgtArray : [],
          enc = fakeEncode ? (x)=>x : encodeURIComponent;
    let k, i = 0;
    for( k in obj ){
      if(i++) a.push('&');
      a.push(enc(k),'=',enc(obj[k]));
    }
    return a===tgtArray ? a : a.join('');
  };
  /**
     repoUrl( repoRelativePath [,urlParams] )

     Creates a URL by prepending this.rootPath to the given path
     (which must be relative from the top of the site, without a
     leading slash). If urlParams is a string, it must be
     paramters encoded in the form "key=val&key2=val2...", WITHOUT
     a leading '?'. If it's an object, all of its properties get
     appended to the URL in that form.
  */
  F.repoUrl = function(path,urlParams){
    if(!urlParams) return this.rootPath+path;
    const url=[this.rootPath,path];
    url.push('?');
    if('string'===typeof urlParams) url.push(urlParams);
    else if('object'===typeof urlParams){
      this.encodeUrlArgs(urlParams, url);
    }
    return url.join('');
  };

  /**
     Returns true if v appears to be a plain object.
  */
  F.isObject = function(v){
    return v &&
      (v instanceof Object) &&
      ('[object Object]' === Object.prototype.toString.apply(v) );
  };

  /**
     For each object argument, this function combines their properties,
     using a last-one-wins policy, and returns a new object with the
     combined properties. If passed a single object, it effectively
     shallowly clones that object.
  */
  F.mergeLastWins = function(){
    var k, o, i;
    const n = arguments.length, rc={};
    for(i = 0; i < n; ++i){
      if(!F.isObject(o = arguments[i])) continue;
      for( k in o ){
        if(o.hasOwnProperty(k)) rc[k] = o[k];
      }
    }
    return rc;
  };

  /**
     Expects to be passed as hash code as its first argument. It
     returns a "shortened" form of hash, with a length which depends
     on the 2nd argument: truthy = fossil.config.hashDigitsUrl, falsy
     = fossil.config.hashDigits. Both of those values are derived from
     the 'hash-digits' repo-level config setting or the
     FOSSIL_HASH_DIGITS_URL/FOSSIL_HASH_DIGITS compile-time options.

     If its first arugment is a non-string, that value is returned
     as-is.
  */
  F.hashDigits = function(hash,forUrl){
    return ('string'==typeof hash ? hash.substr(
      0, F.config[forUrl ? 'hashDigitsUrl' : 'hashDigits']
    ) : hash);
  };

  /**
     Sets up pseudo-automatic content preview handling between a
     source element (typically a TEXTAREA) and a target rendering
     element (typically a DIV). The selector argument must be one of:

     - A single DOM element
     - A collection of DOM elements with a forEach method.
     - A CSS selector

     Each element in the collection must have the following data
     attributes:

     - data-f-preview-from: the DOM element id of the text source
       element. It must support .value to get the content.

     - data-f-preview-to: the DOM element id of the target "previewer"
       element.

     - data-f-preview-via: the name of a method (see below).

     - OPTIONAL data-f-preview-as-text: a numeric value. Explained below.

     Each element gets a click handler added to it which does the
     following:

     1) Reads the content from its data-f-preview-from element.

     2) Passes the content to
     methodNamespace[f-data-post-via](content,callback). f-data-post-via
     is responsible for submitting the preview HTTP request, including
     any parameters the request might require. When the response
     arrives, it must pass the content of the response to its 2nd
     argument, an auto-generated callback installed by this mechanism
     which...

     3) Assigns the response text to the data-f-preview-to element. If
     data-f-preview-as-text is '0' (the default) then the content
     is assigned to the target element's innerHTML property, else
     it is assigned to the element's textContent property.


     The methodNamespace (2nd argument) defaults to fossil.page, and
     data-f-preview-via must be a single method name, not a
     property-access-style string. e.g. "myPreview" is legal but
     "foo.myPreview" is not (unless, of course, the method is actually
     named "foo.myPreview" (which is legal but would be
     unconventional)).

     An example...

     First an input button:

     <button id='test-preview-connector'
       data-f-preview-from='fileedit-content-editor' // elem ID
       data-f-preview-via='myPreview' // method name
       data-f-preview-to='fileedit-tab-preview-wrapper' // elem ID
     >Preview update</button>

     And a sample data-f-preview-via method:

     fossil.page.myPreview = function(content,callback){
       const fd = new FormData();
       fd.append('foo', ...);
       fossil.fetch('preview_forumpost',{
         payload: fd,
         onload: callback,
         onerror: (e)=>{ // only if app-specific handling is needed
           fossil.fetch.onerror(e); // default impl
           ... any app-specific error reporting ...
         }
       });
     };

     Then connect the parts with:

     fossil.connectPagePreviewers('#test-preview-connector');

     Note that the data-f-preview-from, data-f-preview-via, and
     data-f-preview-to selector are not resolved until the button is
     actually clicked, so they need not exist in the DOM at the
     instant when the connection is set up, so long as they can be
     resolved when the preview-refreshing element is clicked.
  */
  F.connectPagePreviewers = function f(selector,methodNamespace){
    if('string'===typeof selector){
      selector = document.querySelectorAll(selector);
    }else if(!selector.forEach){
      selector = [selector];
    }
    if(!methodNamespace){
      methodNamespace = F.page;
    }
    selector.forEach(function(e){
      e.addEventListener(
        'click', function(r){
          const eTo = document.querySelector('#'+e.dataset.fPreviewTo),
                eFrom = document.querySelector('#'+e.dataset.fPreviewFrom),
                asText = +(e.dataset.fPreviewAsText || 0);
          eTo.textContent = "Fetching preview...";
          methodNamespace[e.dataset.fPreviewVia](
            eFrom.value,
            (r)=>eTo[asText ? 'textContent' : 'innerHTML'] = r||''
          );
        }, false
      );
    });
    return this;
  };

})(window);
Added src/fossil.confirmer.js.































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
"use strict";
/**************************************************************
Confirmer is a utility which provides an alternative to confirmation
dialog boxes and "check this checkbox to confirm action" widgets. It
acts by modifying a button to require two clicks within a certain
time, with the second click acting as a confirmation of the first. If
the second click does not come within a specified timeout then the
action is not confirmed.

Usage:

fossil.confirmer(domElement, options);

Usually:

fossil.confirmer(element, {
  onconfirm: function(){
    // this === the element.
    // Do whatever the element would normally do when
    // clicked.
  }
});

Options:

  .initialText = initial text of the element. Defaults to the result
  of the element's .value (for INPUT tags) or innerHTML (for
  everything else). After the timeout/tick count expires, or if the
  user confirms the operation, the element's text is re-set to this
  value.

  .confirmText = text to show when in "confirm mode".
  Default=("Confirm: "+initialText), or something similar.

  .timeout = Number of milliseconds to wait for confirmation.
  Default=3000. Alternately, use a combination of .ticks and
  .ticktime.

  .onconfirm = function to call when clicked in confirm mode. Default
  = undefined. The function's "this" is the the DOM element to which
  the countdown applies.

  .ontimeout = function to call when confirm is not issued. Default =
  undefined. The function's "this" is the DOM element to which the
  countdown applies.

  .onactivate = function to call when item is clicked, but only if the
  item is not currently in countdown mode. This is called (and must
  return) before the countdown starts. The function's "this" is the
  DOM element to which the countdown applies. This can be used, e.g.,
  to change the element's text or CSS classes.

  .classInitial = optional CSS class string (default='') which is
  added to the element during its "initial" state (the state it is in
  when it is not waiting on a timeout). When the target is activated
  (waiting on a timeout) this class is removed.  In the case of a
  timeout, this class is added *before* the .ontimeout handler is
  called.

  .classWaiting = optional CSS class string (default='') which is
  added to the target when it is waiting on a timeout. When the target
  leaves timeout-wait mode, this class is removed.  When timeout-wait
  mode is entered, this class is added *before* the .onactivate
  handler is called.

  .ticktime = a number of ms to wait per tick (see the next item).
  Default = 1000.

  .ticks = a number of "ticks" to wait, as an alternative to .timeout.
  When this mode is active, the ontick callback will be triggered
  immediately before each tick, including the first one. If both
  .ticks and .timeout are set, only one will be used, but which one is
  unspecified. If passed a ticks value with a truncated integer value
  of 0 or less, it will throw an exception (e.g. that also applies if
  it's passed 0.5).

  .ontick = when using .ticks, this callback is passed the current
  tick number before each tick, and its "this" is the target
  element. On each subsequent call, the tick count will be reduced by
  1, and it is passed 0 after the final tick expires or when the
  action has been confirmed, immediately before the onconfirm or
  ontimeout callback. The intention of the callback is to update the
  label of the target element. If .ticks is set but .ontick is not
  then a default implementation is used which updates the element with
  the .confirmText, prepending a countdown to it.

  .debug = boolean. If truthy, it sends some debug output to the dev
  console to track what it's doing.

Various notes:

- To change the default option values, modify the
  fossil.confirmer.defaultOpts object.

- Exceptions triggered via the callbacks are caught and emitted to the
  dev console if the debug option is enabled, but are otherwise
  ignored.

- Due to the nature of multi-threaded code, it is potentially possible
  that confirmation and timeout actions BOTH happen if the user
  triggers the associated action at "just the right millisecond"
  before the timeout is triggered.

TODO: add an invert option which activates if the timeout is reached
and "times out" if the element is clicked again. e.g. a button which
says "Saving..." and cancels the op if it's clicked again, else it
saves after X time/ticks.

Terse Change history:

- 20200507:
  - Add a tick-based countdown in order to more easily support
    updating the target element with the countdown.

- 20200506:
  - Ported from jQuery to plain JS.

- 20181112:
  - extended to support certain INPUT elements.
  - made default opts configurable.

- 20070717: initial jQuery-based impl.
*/
(function(F/*the fossil object*/){
  F.confirmer = function f(elem,opt){
    const dbg = opt.debug
          ? function(){console.debug.apply(console,arguments)}
          : function(){};
    dbg("confirmer opt =",opt);
    if(!f.Holder){
      f.isInput = (e)=>/^(input|textarea)$/i.test(e.nodeName);
      f.Holder = function(target,opt){
        const self = this;
        this.target = target;
        this.opt = opt;
        this.timerID = undefined;
        this.state = this.states.initial;
        const isInput = f.isInput(target);
        const updateText = function(msg){
          if(isInput) target.value = msg;
          else target.innerHTML = msg;
        }
        updateText(this.opt.initialText);
        if(this.opt.ticks && !this.opt.ontick){
          this.opt.ontick = function(tick){
            updateText("("+tick+") "+self.opt.confirmText);
          };
        }
        this.setClasses(false);
        this.doTimeout = function() {
          if(this.timerID){
            clearTimeout( this.timerID );
            delete this.timerID;
          }
          if( this.state != this.states.waiting ) {
            // it was already confirmed
            return;
          }
          this.setClasses( false );
          this.state = this.states.initial;
          dbg("Timeout triggered.");
          if( this.opt.ontick ){
            try{this.opt.ontick.call(this.target, 0)}
            catch(e){dbg("ontick EXCEPTION:",e)}
          }
          if( this.opt.ontimeout ) {
            try{this.opt.ontimeout.call(this.target)}
            catch(e){dbg("ontimeout EXCEPTION:",e)}
          }
          updateText(this.opt.initialText);
        };
        target.addEventListener(
          'click', function(){
            switch( self.state ) {
            case( self.states.waiting ):
              /* Cancel the wait on confirmation */
              if( undefined !== self.timerID ){
                clearTimeout( self.timerID );
                delete self.timerID;
              }
              self.state = self.states.initial;
              self.setClasses( false );
              dbg("Confirmed");
              if( self.opt.ontick ){
                try{self.opt.ontick.call(self.target,0)}
                catch(e){dbg("ontick EXCEPTION:",e)}
              }
              if( self.opt.onconfirm ){
                try{self.opt.onconfirm.call(self.target)}
                catch(e){dbg("onconfirm EXCEPTION:",e)}
              }
              updateText(self.opt.initialText);
              break;
            case( self.states.initial ):
              /* Enter the waiting-on-confirmation state... */
              if(self.opt.ticks) self.opt.currentTick = self.opt.ticks;
              self.setClasses( true );
              self.state = self.states.waiting;
              updateText( self.opt.confirmText );
              if( self.opt.onactivate ) self.opt.onactivate.call( self.target );
              if( self.opt.ontick ) self.opt.ontick.call(self.target, self.opt.currentTick);
              if(self.opt.timeout){
                dbg("Waiting "+self.opt.timeout+"ms on confirmation...");
                self.timerID =
                  setTimeout(()=>self.doTimeout(),self.opt.timeout );
              }else if(self.opt.ticks){
                dbg("Waiting on confirmation for "+self.opt.ticks
                    +" ticks of "+self.opt.ticktime+"ms each...");
                self.timerID =
                  setInterval(function(){
                    if(0===--self.opt.currentTick) self.doTimeout();
                    else{
                      try{self.opt.ontick.call(self.target,
                                               self.opt.currentTick)}
                      catch(e){dbg("ontick EXCEPTION:",e)}
                    }
                  },self.opt.ticktime);
              }
              break;
            default: // can't happen.
              break;
            }
          }, false
        );
      };
      f.Holder.prototype = {
        states:{initial: 0, waiting: 1},
        setClasses: function(activated) {
          if(activated) {
            if( this.opt.classWaiting ) {
              this.target.classList.add( this.opt.classWaiting );
            }
            if( this.opt.classInitial ) {
              this.target.classList.remove( this.opt.classInitial );
            }
          }else{
            if( this.opt.classInitial ) {
              this.target.classList.add( this.opt.classInitial );
            }
            if( this.opt.classWaiting ) {
              this.target.classList.remove( this.opt.classWaiting );
            }
          }
        }
      };
    }/*static init*/
    opt = F.mergeLastWins(f.defaultOpts,{
      initialText: (
        f.isInput(elem) ? elem.value : elem.innerHTML
      ) || "PLEASE SET .initialText"
    },opt);
    if(!opt.confirmText){
      opt.confirmText = "Confirm: "+opt.initialText;
    }
    if(opt.ticks){
      delete opt.timeout;
      opt.ticks = 0 | opt.ticks /* ensure it's an integer */;
      if(opt.ticks<=0){
        throw new Error("ticks must be >0");
      }
      if(opt.ticktime <= 0) opt.ticktime = 1000;
    }else{
      delete opt.ontick;
      delete opt.ticks;
    }
    new f.Holder(elem,opt);
    return this;
  };
  /**
     The default options for initConfirmer(). Tweak them to set the
     defaults. A couple of them (initialText and confirmText) are
     dynamically-generated, and can't reasonably be set in the
     defaults.
  */
  F.confirmer.defaultOpts = {
    timeout:3000,
    ticks: undefined,
    ticktime: 998/*not *quite* 1000*/,
    onconfirm: undefined,
    ontimeout: undefined,
    onactivate: undefined,
    classInitial: '',
    classWaiting: '',
    debug: false
  };

})(window.fossil);
Added src/fossil.dom.js.

















































































































































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
"use strict";
(function(F/*fossil object*/){
  /**
     A collection of HTML DOM utilities to simplify, a bit, using the
     DOM API. It is focused on manipulation of the DOM, but one of its
     core mantras is "No innerHTML." Using innerHTML in this code, in
     particular assigning to it, is absolutely verboten.
  */
  const argsToArray = (a)=>Array.prototype.slice.call(a,0);
  const isArray = (v)=>v instanceof Array;

  const dom = {
    create: function(elemType){
      return document.createElement(elemType);
    },
    createElemFactory: function(eType){
      return function(){
        return document.createElement(eType);
      };
    },
    remove: function(e){
      if(e.forEach){
        e.forEach(
          (x)=>x.parentNode.removeChild(x)
        );
      }else{
        e.parentNode.removeChild(e);
      }
      return e;
    },
    /**
       Removes all child DOM elements from the given element
       and returns that element.

       If e has a forEach method (is an array or DOM element
       collection), this function instead clears each element
       in the collection and returns e.
    */
    clearElement: function f(e){
      if(e.forEach){
        e.forEach((x)=>f(x));
        return e;
      }
      while(e.firstChild) e.removeChild(e.firstChild);
      return e;
    },
  }/* dom object */;

  /**
     Returns the result of splitting the given str on
     a run of spaces of (\s*,\s*).
  */
  dom.splitClassList = function f(str){
    if(!f.rx){
      f.rx = /(\s+|\s*,\s*)/;
    }
    return str ? str.split(f.rx) : [str];
  };
  
  dom.div = dom.createElemFactory('div');
  dom.p = dom.createElemFactory('p');
  dom.header = dom.createElemFactory('header');
  dom.footer = dom.createElemFactory('footer');
  dom.section = dom.createElemFactory('section');
  dom.span = dom.createElemFactory('span');
  dom.strong = dom.createElemFactory('strong');
  dom.em = dom.createElemFactory('em');
  dom.img = function(src){
    const e = dom.create('img');
    if(src) e.setAttribute('src',src);
    return e;
  };
  /**
     Creates and returns a new anchor element with the given
     optional href and label. If label===true then href is used
     as the label.
  */
  dom.a = function(href,label){
    const e = dom.create('a');
    if(href) e.setAttribute('href',href);
    if(label) e.appendChild(dom.text(true===label ? href : label));
    return e;
  };
  dom.hr = dom.createElemFactory('hr');
  dom.br = dom.createElemFactory('br');
  dom.text = (t)=>document.createTextNode(t||'');
  dom.button = function(label){
    const b = this.create('button');
    if(label) b.appendChild(this.text(label));
    return b;
  };
  dom.select = dom.createElemFactory('select');
  /**
     Returns an OPTION element with the given value and label
     text (which defaults to the value).

     May be called as (value), (selectElement), (selectElement,
     value), (value, label) or (selectElement, value,
     label). The latter appends the new element to the given
     SELECT element.

     If the value has the undefined value then it is NOT
     assigned as the option element's value.
  */
  dom.option = function(value,label){
    const a = arguments;
    var sel;
    if(1==a.length){
      if(a[0] instanceof HTMLElement){
        sel = a[0];
      }else{
        value = a[0];
      }
    }else if(2==a.length){
      if(a[0] instanceof HTMLElement){
        sel = a[0];
        value = a[1];
      }else{
        value = a[0];
        label = a[1];
      }
    }
    else if(3===a.length){
      sel = a[0];
      value = a[1];
      label = a[2];
    }
    const o = this.create('option');
    if(undefined !== value){
      o.value = value;
      this.append(o, this.text(label || value));
    }
    if(sel) this.append(sel, o);
    return o;
  };
  dom.h = function(level){
    return this.create('h'+level);
  };
  dom.ul = dom.createElemFactory('ul');
  /**
     Creates and returns a new LI element, appending it to the
     given parent argument if it is provided.
  */
  dom.li = function(parent){
    const li = this.create('li');
    if(parent) parent.appendChild(li);
    return li;
  };

  /**
     Returns a function which creates a new DOM element of the
     given type and accepts an optional parent DOM element
     argument. If the function's argument is truthy, the new
     child element is appended to the given parent element.
     Returns the new child element.
  */
  dom.createElemFactoryWithOptionalParent = function(childType){
    return function(parent){
      const e = this.create(childType);
      if(parent) parent.appendChild(e);
      return e;
    };
  };
  
  dom.table = dom.createElemFactory('table');
  dom.thead = dom.createElemFactoryWithOptionalParent('thead');
  dom.tbody = dom.createElemFactoryWithOptionalParent('tbody');
  dom.tfoot = dom.createElemFactoryWithOptionalParent('tfoot');
  dom.tr = dom.createElemFactoryWithOptionalParent('tr');
  dom.td = dom.createElemFactoryWithOptionalParent('td');
  dom.th = dom.createElemFactoryWithOptionalParent('th');

  
  /**
     Creates and returns a FIELDSET element, optionaly with a
     LEGEND element added to it.
  */
  dom.fieldset = function(legendText){
    const fs = this.create('fieldset');
    if(legendText){
      this.append(
        fs,
        this.append(
          this.create('legend'),
          legendText
        )
      );
    }
    return fs;
  };

  /**
     Appends each argument after the first to the first argument
     (a DOM node) and returns the first argument.

     - If an argument is a string or number, it is transformed
     into a text node.

     - If an argument is an array or has a forEach member, this
     function appends each element in that list to the target
     by calling its forEach() method to pass it (recursively)
     to this function.

     - Else the argument assumed to be of a type legal
     to pass to parent.appendChild().
  */
  dom.append = function f(parent/*,...*/){
    const a = argsToArray(arguments);
    a.shift();
    for(let i in a) {
      var e = a[i];
      if(isArray(e) || e.forEach){
        e.forEach((x)=>f.call(this, parent,e));
        continue;
      }
      if('string'===typeof e || 'number'===typeof e) e = this.text(e);
      parent.appendChild(e);
    }
    return parent;
  };

  dom.input = function(type){
    return this.attr(this.create('input'), 'type', type);
  };
  
  /**
     Internal impl for addClass(), removeClass().
  */
  const domAddRemoveClass = function f(action,e){
    if(!f.rxSPlus){
      f.rxSPlus = /\s+/;
      f.applyAction = function(e,a,v){
        if(!e || !v
           /*silently skip empty strings/flasy
             values, for user convenience*/) return;
        else if(e.forEach){
          e.forEach((E)=>E.classList[a](v));
        }else{
          e.classList[a](v);
        }
      };
    }
    var i = 2, n = arguments.length;
    for( ; i < n; ++i ){
      let c = arguments[i];
      if(!c) continue;
      else if(isArray(c) ||
              ('string'===typeof c
               && c.indexOf(' ')>=0
               && (c = c.split(f.rxSPlus)))
              || c.forEach
             ){
        c.forEach((k)=>k ? f.applyAction(e, action, k) : false);
        // ^^^ we could arguably call f(action,e,k) to recursively
        // apply constructs like ['foo bar'] or [['foo'],['bar baz']].
      }else if(c){
        f.applyAction(e, action, c);
      }
    }
    return e;
  };

  /**
     Adds one or more CSS classes to one or more DOM elements.

     The first argument is a target DOM element or a list type of such elements
     which has a forEach() method.  Each argument
     after the first may be a string or array of strings. Each
     string may contain spaces, in which case it is treated as a
     list of CSS classes.

     Returns e.
  */
  dom.addClass = function(e,c){
    const a = argsToArray(arguments);
    a.unshift('add');
    return domAddRemoveClass.apply(this, a);
  };
  /**
     The 'remove' counterpart of the addClass() method, taking
     the same arguments and returning the same thing.
  */
  dom.removeClass = function(e,c){
    const a = argsToArray(arguments);
    a.unshift('remove');
    return domAddRemoveClass.apply(this, a);
  };

  dom.hasClass = function(e,c){
    return (e && e.classList) ? e.classList.contains(c) : false;
  };

  /**
     Each argument after the first may be a single DOM element
     or a container of them with a forEach() method. All such
     elements are appended, in the given order, to the dest
     element.

     Returns dest.
  */
  dom.moveTo = function(dest,e){
    const n = arguments.length;
    var i = 1;
    for( ; i < n; ++i ){
      e = arguments[i];
      if(e.forEach){
        e.forEach((x)=>dest.appendChild(x));
      }else{
        dest.appendChild(e);
      }
    }
    return dest;
  };
  /**
     Each argument after the first may be a single DOM element
     or a container of them with a forEach() method. For each
     DOM element argument, all children of that DOM element
     are moved to dest (via appendChild()). For each list argument,
     each entry in the list is assumed to be a DOM element and is
     appended to dest.

     dest may be an Array, in which case each child is pushed
     into the array and removed from its current parent element.

     All children are appended in the given order.

     Returns dest.
  */
  dom.moveChildrenTo = function f(dest,e){
    if(!f.mv){
      f.mv = function(d,v){
        if(d instanceof Array){
          d.push(v);
          if(v.parentNode) v.parentNode.removeChild(v);
        }
        else d.appendChild(v);
      };
    }
    const n = arguments.length;
    var i = 1;
    for( ; i < n; ++i ){
      e = arguments[i];
      if(!e){
        console.warn("Achtung: dom.moveChildrenTo() passed a falsy value at argment",i,"of",
                     arguments,arguments[i]);
        continue;
      }
      if(e.forEach){
        e.forEach((x)=>f.mv(dest, x));
      }else{
        while(e.firstChild){
          f.mv(dest, e.firstChild);
        }
      }
    }
    return dest;
  };

  /**
     Adds each argument (DOM Elements) after the first to the
     DOM immediately before the first argument (in the order
     provided), then removes the first argument from the DOM.
     Returns void.

     If any argument beyond the first has a forEach method, that
     method is used to recursively insert the collection's
     contents before removing the first argument from the DOM.
  */
  dom.replaceNode = function f(old,nu){
    var i = 1, n = arguments.length;
    ++f.counter;
    try {
      for( ; i < n; ++i ){
        const e = arguments[i];
        if(e.forEach){
          e.forEach((x)=>f.call(this,old,e));
          continue;
        }
        old.parentNode.insertBefore(e, old);
      }
    }
    finally{
      --f.counter;
    }
    if(!f.counter){
      old.parentNode.removeChild(old);
    }
  };
  dom.replaceNode.counter = 0;        
  /**
     Two args == getter: (e,key), returns value

     Three == setter: (e,key,val), returns e. If val===null
     or val===undefined then the attribute is removed. If (e)
     has a forEach method then this routine is applied to each
     element of that collection via that method.           
  */
  dom.attr = function f(e){
    if(2===arguments.length) return e.getAttribute(arguments[1]);
    if(e.forEach){
      e.forEach((x)=>f(x,arguments[1],arguments[2]));
      return e;
    }            
    const key = arguments[1], val = arguments[2];
    if(null===val || undefined===val){
      e.removeAttribute(key);
    }else{
      e.setAttribute(key,val);
    }
    return e;
  };

  const enableDisable = function f(enable){
    var i = 1, n = arguments.length;
    for( ; i < n; ++i ){
      let e = arguments[i];
      if(e.forEach){
        e.forEach((x)=>f(enable,x));
        return e;
      }
      e.disabled = !enable;
    }
    return arguments[1];
  };

  /**
     Enables (by removing the "disabled" attribute) each element
     (HTML DOM element or a collection with a forEach method)
     and returns the first argument.
  */
  dom.enable = function(e){
    const args = argsToArray(arguments);
    args.unshift(true);
    return enableDisable.apply(this,args);
  };
  /**
     Disables (by setting the "disabled" attribute) each element
     (HTML DOM element or a collection with a forEach method)
     and returns the first argument.
  */
  dom.disable = function(e){
    const args = argsToArray(arguments);
    args.unshift(false);
    return enableDisable.apply(this,args);
  };

  /**
     A proxy for document.querySelector() which throws if
     selection x is not found. It may optionally be passed an
     "origin" object as its 2nd argument, which restricts the
     search to that branch of the tree.
  */
  dom.selectOne = function(x,origin){
    var src = origin || document,
        e = src.querySelector(x);
    if(!e){
      e = new Error("Cannot find DOM element: "+x);
      console.error(e, src);
      throw e;
    }
    return e;
  };

  return F.dom = dom;
})(window.fossil);
Added src/fossil.fetch.js.












































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
"use strict";
/**
   Requires that window.fossil has already been set up.

   window.fossil.fetch() is an HTTP request/response mini-framework
   similar (but not identical) to the not-quite-ubiquitous
   window.fetch().

   JS usages:

   fossil.fetch( URI [, onLoadCallback] );

   fossil.fetch( URI [, optionsObject = {}] );

   Noting that URI must be relative to the top of the repository and
   should not start with a slash (if it does, it is stripped). It gets
   the equivalent of "%R/" prepended to it.

   The optionsObject may be an onload callback or an object with any
   of these properties:

   - onload: callback(responseData) (default = output response to
   the console).

   - onerror: callback(XHR onload event | exception)
   (default = event or exception to the console).

   - method: 'POST' | 'GET' (default = 'GET'). CASE SENSITIVE!

   - payload: anything acceptable by XHR2.send(ARG) (DOMString,
   Document, FormData, Blob, File, ArrayBuffer), or a plain object or
   array, either of which gets JSON.stringify()'d. If payload is set
   then the method is automatically set to 'POST'. If an object/array
   is converted to JSON, the contentType option is automatically set
   to 'application/json'. By default XHR2 will set the content type
   based on the payload type.

   - contentType: Optional request content type when POSTing. Ignored
   if the method is not 'POST'.

   - responseType: optional string. One of ("text", "arraybuffer",
   "blob", or "document") (as specified by XHR2). Default = "text".
   As an extension, it supports "json", which tells it that the
   response is expected to be text and that it should be JSON.parse()d
   before passing it on to the onload() callback.

   - urlParams: string|object. If a string, it is assumed to be a
   URI-encoded list of params in the form "key1=val1&key2=val2...",
   with NO leading '?'.  If it is an object, all of its properties get
   converted to that form. Either way, the parameters get appended to
   the URL before submitting the request.

   When an options object does not provide onload() or onerror()
   handlers of its own, this function falls back to
   fossil.fetch.onload() and fossil.fetch.onerror() as defaults. The
   default implementations route the data through the dev console and
   (for onerror()) through fossil.error(). Individual pages may
   overwrite those members to provide default implementations suitable
   for the page's use.

   Returns this object, noting that the XHR request is asynchronous,
   and still in transit (or has yet to be sent) when that happens.
*/
window.fossil.fetch = function f(uri,opt){
  const F = fossil;
  if(!f.onerror){
    f.onerror = function(e/*event or exception*/){
      console.error("Ajax error:",e);
      if(e instanceof Error){
        F.error('Exception:',e);
      }
      else if(e.originalTarget && e.originalTarget.responseType==='text'){
        const txt = e.originalTarget.responseText;
        try{
          /* The convention from the /filepage_xyz routes is to
             return error responses in JSON form if possible:
             {error: "..."}
          */
          const j = JSON.parse(txt);
          console.error("Error JSON:",j);
          if(j.error){ F.error(j.error) };
        }catch(e){/* Try harder */
          F.error(txt)
        }
      }
    };
    f.onload = (r)=>console.debug('ajax response:',r);
  }
  if('/'===uri[0]) uri = uri.substr(1);
  if(!opt) opt = {};
  else if('function'===typeof opt) opt={onload:opt};
  if(!opt.onload) opt.onload = f.onload;
  if(!opt.onerror) opt.onerror = f.onerror;
  let payload = opt.payload, jsonResponse = false;
  if(undefined!==payload){
    opt.method = 'POST';
    if(!(payload instanceof FormData)
       && !(payload instanceof Document)
       && !(payload instanceof Blob)
       && !(payload instanceof File)
       && !(payload instanceof ArrayBuffer)
       && ('object'===typeof payload
           || payload instanceof Array)){
      payload = JSON.stringify(payload);
      opt.contentType = 'application/json';
    }
  }
  const url=[F.repoUrl(uri,opt.urlParams)],
        x=new XMLHttpRequest();
  if('POST'===opt.method && 'string'===typeof opt.contentType){
    x.setRequestHeader('Content-Type',opt.contentType);
  }
  x.open(opt.method||'GET', url.join(''), true);
  if('json'===opt.responseType){
    /* 'json' is an extension to the supported XHR.responseType
       list. We use it as a flag to tell us to JSON.parse()
       the response. */
    jsonResponse = true;
    x.responseType = 'text';
  }else{
    x.responseType = opt.responseType||'text';
  }
  if(opt.onload){
    x.onload = function(e){
      if(200!==this.status){
        if(opt.onerror) opt.onerror(e);
        return;
      }
      try{
        opt.onload((jsonResponse && this.response)
                   ? JSON.parse(this.response) : this.response);
      }catch(e){
        if(opt.onerror) opt.onerror(e);
      }
    }
  }
  if(undefined!==payload) x.send(payload);
  else x.send();
  return this;
};
Added src/fossil.page.fileedit.js.




































































































































































































































































































































































































































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
(function(F/*the fossil object*/){
  "use strict";
  /**
     Code for the /filepage app. Requires that the fossil JS
     bootstrapping is complete and fossil.fetch() has been installed.
  */
  const E = (s)=>document.querySelector(s),
        D = F.dom,
        P = F.page;

  /**
     Widget for the checkin/file selection list.
  */
  P.fileSelector = {
    e:{
      container: E('#fileedit-file-selector')
    },
    finfo: {},
    cache: {
      checkins: undefined,
      files:{}
    },
    /**
       Fetches the list of leaf checkins from the server and updates
       the UI with that list.
    */
    loadLeaves: function(){
      D.append(D.clearElement(
        this.e.ciListLabel,
        this.e.selectCi,
        this.e.selectFiles
      ),"Loading leaves...");
      D.disable(this.e.btnLoadFile, this.e.selectFiles, this.e.selectCi); 
      const self = this;
      F.fetch('fileedit',{
        urlParams:'ajax=filelist&leaves',
        responseType: 'json',
        onload: function(list){
          D.append(D.clearElement(self.e.ciListLabel),"Open leaves:");
          self.cache.checkins = list;
          D.clearElement(D.enable(self.e.selectCi));
          let loadThisOne;
          list.forEach(function(o,n){
            if(!n) loadThisOne = o;
            D.option(self.e.selectCi, o.checkin,
                     o.timestamp+' ['+o.branch+']: '
                     +F.hashDigits(o.checkin));
          });
          self.loadFiles(loadThisOne ? loadThisOne.checkin : false);
        }
      });
    },
    /**
       Loads the file list for the given checkin UUID. It uses a
       cached copy on subsequent calls for the same UUID. If passed a
       falsy value, it instead clears and disables the file selection
       list.
    */
    loadFiles: function(ciUuid){
      delete this.finfo.filename;
      this.finfo.checkin = ciUuid;
      const selFiles = this.e.selectFiles;
      if(!ciUuid){
        D.clearElement(D.disable(selFiles, this.e.btnLoadFile));
        return this;
      }
      const onload = (response)=>{
        D.clearElement(D.enable(selFiles, this.e.btnLoadFile));
        D.append(
          D.clearElement(this.e.fileListLabel),
          "Editable files for ",
          D.a(F.repoUrl('timeline',{
            c: ciUuid
          }), F.hashDigits(ciUuid)),
          ':'
        );
        this.cache.files[response.checkin] = response;
        response.editableFiles.forEach(function(fn){
          D.option(selFiles, fn);
        });
      };
      const got = this.cache.files[ciUuid];
      if(got){
        onload(got);
        return this;
      }
      D.disable(selFiles,this.e.btnLoadFile);
      D.clearElement(selFiles);
      D.append(D.clearElement(this.e.fileListLabel),
               "Loading files for "+F.hashDigits(ciUuid)+"...");
      F.fetch('fileedit',{
        urlParams:{ajax:'filelist', checkin: ciUuid},
        responseType: 'json',
        onload
      });
      return this;
    },
    /**
       Initializes the checkin/file selector widget. Must only be
       called once.
    */
    init: function(){
      const selCi = this.e.selectCi = D.select(),
            selFiles = this.e.selectFiles
            = D.addClass(D.select(), 'file-list'),
            btnLoad = this.e.btnLoadFile =
            D.addClass(D.button("Load file"), "flex-shrink"),
            filesLabel = this.e.fileListLabel =
            D.addClass(D.div(),'flex-shrink','file-list-label'),
            ciLabelWrapper = D.addClass(
              D.div(), 'flex-container','flex-row', 'flex-shrink',
              'stretch'
            ),
            btnReload = D.addClass(
              D.button('Reload'), 'flex-shrink'
            ),
            ciLabel = this.e.ciListLabel =
            D.addClass(D.span(),'flex-shrink','checkin-list-label')
      ;
      D.attr(selCi, 'title',"The list of opened leaves.");
      D.attr(selFiles, 'title',
             "The list of editable files for the selected checkin.");
      D.attr(btnLoad, 'title',
             "Load the selected file into the editor.");
      D.disable(selCi, selFiles, btnLoad);
      D.attr(selFiles, 'size', 10);
      D.append(
        this.e.container,
        D.append(ciLabelWrapper,
                 btnReload, ciLabel),
        selCi,
        filesLabel,
        selFiles,
        btnLoad
      );
      this.loadLeaves();
      selCi.addEventListener(
        'change', (e)=>this.loadFiles(e.target.value), false
      );
      btnLoad.addEventListener(
        'click', (e)=>{
          this.finfo.filename = selFiles.value;
          if(this.finfo.filename){
            P.loadFile(this.finfo.filename, this.finfo.checkin);
          }
        }, false
      );
      btnReload.addEventListener(
        'click', (e)=>this.loadLeaves(), false
      );
      delete this.init;
    }
  };

  window.addEventListener("load", function() {
    P.tabs = new fossil.TabManager('#fileedit-tabs');
    P.e = {
      taEditor: E('#fileedit-content-editor'),
      taCommentSmall: E('#fileedit-comment'),
      taCommentBig: E('#fileedit-comment-big'),
      ajaxContentTarget: E('#ajax-target'),
      btnCommit: E("#fileedit-btn-commit"),
      btnReload: E("#fileedit-tab-content > .fileedit-options > "
                   +"button.fileedit-content-reload"),
      selectPreviewModeWrap: E('#select-preview-mode'),
      selectHtmlEmsWrap: E('#select-preview-html-ems'),
      selectEolWrap:  E('#select-preview-html-ems'),
      cbLineNumbersWrap: E('#cb-line-numbers'),
      cbAutoPreview: E('#cb-preview-autoupdate > input[type=checkbox]'),
      tabs:{
        content: E('#fileedit-tab-content'),
        preview: E('#fileedit-tab-preview'),
        diff: E('#fileedit-tab-diff'),
        commit: E('#fileedit-tab-commit')
      }
    };
    P.fileSelector.init();
    /* Figure out which comment editor to show by default and
       hide the other one. By default we take the one which does
       not have the 'hidden' CSS class. If neither do, we default
       to single-line mode. */
    if(D.hasClass(P.e.taCommentSmall, 'hidden')){
      P.e.taComment = P.e.taCommentBig;
    }else if(D.hasClass(P.e.taCommentBig,'hidden')){
      P.e.taComment = P.e.taCommentSmall;
    }else{
      P.e.taComment = P.e.taCommentSmall;
      D.addClass(P.e.taCommentBig, 'hidden');
    }
    D.removeClass(P.e.taComment, 'hidden');

    P.tabs.e.container.insertBefore(
      /* Move the status bar between the tab buttons and
         tab panels. Seems to be the best fit in terms of
         functionality and visibility. */
      E('#fossil-status-bar'), P.tabs.e.tabs
    );

    P.tabs.addEventListener(
      /* Set up auto-refresh of the preview tab... */
      'before-switch-to', function(ev){
        if(ev.detail===P.e.tabs.preview
           && P.e.cbAutoPreview.checked){
          P.preview();
        }
      }
    );

    F.connectPagePreviewers(
      P.e.tabs.preview.querySelector(
        '#btn-preview-refresh'
      )
    );

    const diffButtons = E('#fileedit-tab-diff-buttons');
    diffButtons.querySelector('button.sbs').addEventListener(
      "click",(e)=>P.diff(true), false
    );
    diffButtons.querySelector('button.unified').addEventListener(
      "click",(e)=>P.diff(false), false
    );
    P.e.btnCommit.addEventListener(
      "click",(e)=>P.commit(), false
    );
    F.confirmer(P.e.btnReload, {
      confirmText: "Really reload, losing edits?",
      onconfirm: (e)=>P.loadFile(),
      ticks: 3
    });
    E('#comment-toggle').addEventListener(
      "click",(e)=>P.toggleCommentMode(), false
    );

    /**
       Cosmetic: jump through some hoops to enable/disable
       certain preview options depending on the current
       preview mode...
    */
    const selectPreviewMode =
          P.e.selectPreviewModeWrap.querySelector('select');
    selectPreviewMode.addEventListener(
      "change", function(e){
        const mode = e.target.value,
              name = P.previewModes[mode],
              hide = [], unhide = [];
        if('guess'===name){
          unhide.push(P.e.cbLineNumbersWrap,
                      P.e.selectHtmlEmsWrap);
        }else{
          if('text'===name) unhide.push(P.e.cbLineNumbersWrap);
          else hide.push(P.e.cbLineNumbersWrap);
          if('htmlIframe'===name) unhide.push(P.e.selectHtmlEmsWrap);
          else hide.push(P.e.selectHtmlEmsWrap);
        }
        hide.forEach((e)=>e.classList.add('hidden'));
        unhide.forEach((e)=>e.classList.remove('hidden'));
      }, false
    );
    selectPreviewMode.dispatchEvent(
      // Force UI update
      new Event('change',{target:selectPreviewMode})
    );
    const selectFontSize = E('select[name=editor_font_size]');
    if(selectFontSize){
      selectFontSize.addEventListener(
        "change",function(e){
          const ed = P.e.taEditor;
          ed.className = ed.className.replace(
              /\bfont-size-\d+/g, '' );
          ed.classList.add('font-size-'+e.target.value);
        }, false
      );
      selectFontSize.dispatchEvent(
        // Force UI update
        new Event('change',{target:selectFontSize})
      );
    }
  }, false)/*onload event handler*/;

  /**
     Getter (if called with no args) or setter (if passed an arg) for
     the current file content. We use a function, rather than direct
     access so that clients can hypothetically swap out this method
     from their skin in order to facilitate plugging-in of fancy
     3rd-party editor widgets.

     The setter form returns this object.
  */
  P.value = function(){
    if(0===arguments.length){
      return this.e.taEditor.value;
    }else{
      this.e.taEditor.value = arguments[0];
      return this;
    }
  };

  /**
     Toggles between single- and multi-line comment
     mode.
  */
  P.toggleCommentMode = function(){
    var s, h, c = this.e.taComment.value;
    if(this.e.taComment === this.e.taCommentSmall){
      s = this.e.taCommentBig;
      h = this.e.taCommentSmall;
    }else{
      s = this.e.taCommentSmall;
      h = this.e.taCommentBig;
      /*
        Doing (input[type=text].value = textarea.value) unfortunately
        strips all newlines. To compensate we'll replace each EOL with
        a space. Not ideal. If we were to instead escape them as \n,
        and do the reverse when toggling again, then they would get
        committed as escaped newlines if the user did not first switch
        back to multi-line mode. We cannot blindly unescape the
        newlines, in the off chance that the user actually enters \n
        in the comment.
      */
      c = c.replace(/\r?\n/g,' ');
    }
    s.value = c;
    this.e.taComment = s;
    D.addClass(h, 'hidden');
    D.removeClass(s, 'hidden');
    console.debug(s,h);
  };
  
  /**
     updateVersion() updates the filename and version in various UI
     elements...

     Returns this object.
  */
  P.updateVersion = function(file,rev){
    this.finfo = {filename:file,checkin:rev};
    const E = (s)=>document.querySelector(s),
          euc = encodeURIComponent,
          rHuman = F.hashDigits(rev),
          rUrl = F.hashDigits(rev,true);
    D.append(
      D.clearElement(E('#r-label')),
      rHuman
    );
    var e;
    e = E('#timeline-link');
    D.attr(e, 'href',F.repoUrl('timeline',{c:rUrl}));
    e = E('#finfo-file-name');
    D.append(
      D.clearElement(e),
      D.a(F.repoUrl('finfo',{name:file, m:rUrl}), file)
    );
    e = E('#r-link');
    D.attr(e, 'href', F.repoUrl('info/'+rUrl));
    e = E('#r-label');
    D.append(D.clearElement(e),rHuman);
    const purlArgs = F.encodeUrlArgs({
      filename: this.finfo.filename,
      checkin: rUrl
    },false,true);
    const purl = F.repoUrl('fileedit',purlArgs);
    e = E('#permalink');
    D.attr(D.append(D.clearElement(e),'?'+purlArgs),'href', purl);
    return this;
  };

  const affirmHasFile = function(){
    if(!P.finfo) F.error("No file is loaded.");
    return !!P.finfo;
  };

  /**
     loadFile() loads (file,checkinVersion) and updates the relevant
     UI elements to reflect the loaded state.

     Returns this object, noting that the load is async.
  */
  P.loadFile = function(file,rev){
    if(0===arguments.length){
      if(!affirmHasFile()) return this;
      file = this.finfo.filename;
      rev = this.finfo.checkin;
    }
    delete this.finfo;
    const self = this;
    F.message("Loading content...");
    F.fetch('fileedit',{
      urlParams: {
        ajax: 'content',
        filename:file,
        checkin:rev
      },
      onload:(r)=>{
        F.message('Loaded content.');
        self.value(r);
        self.updateVersion(file,rev);
        self.tabs.switchToTab(self.e.tabs.content);
      }
    });
    return this;
  };

  /**
     Fetches the page preview based on the contents and settings of
     this page's input fields, and updates the UI with with the
     preview.

     Returns this object, noting that the operation is async.
  */
  P.preview = function f(switchToTab){
    if(!affirmHasFile()) return this;
    if(!f.target){
      f.target = this.e.tabs.preview.querySelector(
        '#fileedit-tab-preview-wrapper'
      );
    }
    const self = this;
    const updateView = function(c){
      D.clearElement(f.target);
      if('string'===typeof c) f.target.innerHTML = c;
      if(switchToTab) self.tabs.switchToTab(self.e.tabs.preview);
    };
    return this._postPreview(this.value(), updateView);
  };

  /**
     Callback for use with F.connectPagePreviewers()
  */
  P._postPreview = function(content,callback){
    if(!affirmHasFile()) return this;
    if(!content){
      callback(content);
      return this;
    }
    const fd = new FormData();
    fd.append('render_mode',E('select[name=preview_render_mode]').value);
    fd.append('filename',this.finfo.filename);
    fd.append('ln',E('[name=preview_ln]').checked ? 1 : 0);
    fd.append('iframe_height', E('[name=preview_html_ems]').value);
    fd.append('content',content || '');
    F.message(
      "Fetching preview..."
    ).fetch('fileedit',{
      urlParams: {ajax: 'preview'},
      payload: fd,
      onload: (r)=>{
        callback(r);
        F.message('Updated preview.');
      },
      onerror: (e)=>{
        fossil.fetch.onerror(e);
        callback("Error fetching preview: "+e);
      }
    });
    return this;
  };

  
  /**
     Fetches the content diff based on the contents and settings of this
     page's input fields, and updates the UI with the diff view.

     Returns this object, noting that the operation is async.
  */
  P.diff = function f(sbs){
    if(!affirmHasFile()) return this;
    const content = this.value(),
          self = this;
    if(!f.target){
      f.target = this.e.tabs.diff.querySelector(
        '#fileedit-tab-diff-wrapper'
      );
    }
    const fd = new FormData();
    fd.append('filename',this.finfo.filename);
    fd.append('checkin', this.finfo.checkin);
    fd.append('sbs', sbs ? 1 : 0);
    fd.append('content',content);
    F.message(
      "Fetching diff..."
    ).fetch('fileedit',{
      urlParams: {ajax: 'diff'},
      payload: fd,
      onload: function(c){
        f.target.innerHTML = [
          "<div>Diff <code>[",
          self.finfo.checkin,
          "]</code> &rarr; Local Edits</div>",
          c||'No changes.'
        ].join('');
        F.message('Updated diff.');
        self.tabs.switchToTab(self.e.tabs.diff);
      }
    });
    return this;
  };

  /**
     Performs an async commit based on the form contents and updates
     the UI.

     Returns this object.
  */
  P.commit = function f(){
    if(!affirmHasFile()) return this;
    const self = this;
    const content = this.value(),
          target = document.querySelector('#fileedit-manifest'),
          cbDryRun = E('[name=dry_run]'),
          isDryRun = cbDryRun.checked,
          filename = this.finfo.filename;
    if(!f.updateView){
      f.updateView = function(c){
        target.innerHTML = [
          "<h3>Manifest",
          (c.dryRun?" (dry run)":""),
          ": ", F.hashDigits(c.uuid),"</h3>",
          "<code class='fileedit-manifest'>",
          c.manifest,
          "</code></pre>"
        ].join('');
        const msg = [
          'Committed',
          c.dryRun ? '(dry run)' : '',
          '[', F.hashDigits(c.uuid) ,'].'
        ];
        if(!c.dryRun){
          msg.push('Re-activating dry-run mode.');
          self.e.taComment.value = '';
          cbDryRun.checked = true;
          self.updateVersion(filename, c.uuid);
          self.fileSelector.loadLeaves();
        }
        F.message.apply(fossil, msg);
        self.tabs.switchToTab(self.e.tabs.commit);
      };
    }
    if(!content){
      f.updateView('');
      return this;
    }
    const fd = new FormData();
    fd.append('filename',filename);
    fd.append('checkin', this.finfo.checkin);
    fd.append('content',content);
    fd.append('dry_run',isDryRun ? 1 : 0);
    /* Text fields or select lists... */
    ['comment_mimetype',
     'comment'
    ].forEach(function(name){
      var e = E('[name='+name+']');
      if(e) fd.append(name,e.value);
    });
    /* Checkboxes: */
    ['allow_fork',
     'allow_older',
     'exec_bit',
     'allow_merge_conflict',
     'prefer_delta'
    ].forEach(function(name){
      var e = E('[name='+name+']');
      if(e){
        if(e.checked) fd.append(name, 1);
      }else{
        console.error("Missing checkbox? name =",name);
      }
    });
    F.message(
      "Checking in..."
    ).fetch('fileedit',{
      urlParams: {ajax: 'commit'},
      payload: fd,
      responseType: 'json',
      onload: f.updateView
    });
    return this;
  };

  
})(window.fossil);
Added src/fossil.tabs.js.








































































































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
"use strict";
(function(F/*fossil object*/){
  const E = (s)=>document.querySelector(s),
        EA = (s)=>document.querySelectorAll(s),
        D = F.dom;

  /**
     Creates a TabManager. If passed an argument, it is
     passed to init().
  */
  const TabManager = function(domElem){
    this.e = {};
    if(domElem) this.init(domElem);
  };

  /**
     Internal helper to normalize a method argument
     to a tab element.
  */
  const tabArg = function(arg,tabMgr){
    if('string'===typeof arg) arg = E(arg);
    else if(tabMgr && 'number'===typeof arg && arg>=0){
      arg = tabMgr.e.tabs.childNodes[arg];
    }
    return arg;
  };

  const setVisible = function(e,yes){
    D[yes ? 'removeClass' : 'addClass'](e, 'hidden');
  };

  TabManager.prototype = {
    /**
       Initializes the tabs associated with the given tab container
       (DOM element or selector for a single element). This must be
       called once before using any other member functions of a given
       instance, noting that the constructor will call this if it is
       passed an argument.       

       The tab container must have an 'id' attribute. This function
       looks through the DOM for all elements which have
       data-tab-parent=thatId. For each one it creates a button to
       switch to that tab and moves the element into this.e.tabs.

       The label for each tab is set by the data-tab-label attribute
       of each element, defaulting to something not terribly useful.

       When it's done, it auto-selects the first tab unless a tab has
       a truthy numeric value in its data-tab-select attribute, in
       which case the last tab to have such a property is selected.

       This method must only be called once per instance. TabManagers
       may be nested but must not share any tabs instances.

       Returns this object.

       DOM elements of potential interest to users:

       this.e.container = the outermost container element.

       this.e.tabBar = the button bar. Each "button" (whether it's a
       buttor not is unspecified) has a class of .tab-button.

       this.e.tabs = the parent for all of the tab elements.

       It is legal, within reason, to manipulate these a bit, in
       particular this.e.container, e.g. by adding more children to
       it. Do not remove elements from the tabs or tabBar, however, or
       the tab state may get sorely out of sync.

       CSS classes: the container element has whatever class(es) the
       client sets on. this.e.tabBar gets the 'tab-bar' class and
       this.e.tabs gets the 'tabs' class. It's hypothetically possible
       to move the tabs to either side or the bottom using only CSS,
       but it's never been tested.
    */
    init: function(container){
      container = tabArg(container);
      const cID = container.getAttribute('id');
      if(!cID){
        throw new Error("Tab container element is missing 'id' attribute.");
      }
      const c = this.e.container = container;
      this.e.tabBar = D.addClass(D.div(),'tab-bar');
      this.e.tabs = D.addClass(D.div(),'tabs');
      D.append(c, this.e.tabBar, this.e.tabs);
      let selectIndex = 0;
      EA('[data-tab-parent='+cID+']').forEach((c,n)=>{
        if(+c.dataset.tabSelect) selectIndex=n;
        this.addTab(c);
      });
      return this.switchToTab(selectIndex);
    },

    /**
       For the given tab element, unique selector string, or integer
       (0-based tab number), returns the button associated with that
       tab, or undefined if the argument does not match any current
       tab.
    */
    getButtonForTab: function(tab){
      tab = tabArg(tab,this);
      var i = -1;
      this.e.tabs.childNodes.forEach(function(e,n){
        if(e===tab) i = n;
      });
      return i>=0 ? this.e.tabBar.childNodes[i] : undefined;
    },
    /**
       Adds the given DOM element or unique selector as the next
       tab in the tab container, adding a button to switch to
       the tab. Returns this object.
    */
    addTab: function f(tab){
      if(!f.click){
        f.click = function(e){
         e.target.$manager.switchToTab(e.target.$tab);
        };
      }
      tab = tabArg(tab);
      tab.remove();
      D.append(this.e.tabs, D.addClass(tab,'tab-panel'));
      const lbl = tab.dataset.tabLabel || 'Tab #'+(this.e.tabs.childNodes.length-1);
      const btn = D.addClass(D.append(D.span(), lbl), 'tab-button');
      D.append(this.e.tabBar,btn);
      btn.$manager = this;
      btn.$tab = tab;
      btn.addEventListener('click', f.click, false);
      return this;
    },

    /**
       Internal. Fires a new CustomEvent to all listeners which have
       registered via this.addEventListener().
     */
    _dispatchEvent: function(name, detail){
      try{
        this.e.container.dispatchEvent(
          new CustomEvent(name, {detail: detail})
        );
      }catch(e){
        /* ignore */
      }
      return this;
    },

    /**
       Registers an event listener for this object's custom events.
       The callback gets a CustomEvent object with a 'detail'
       propertly holding any tab-related state for the event. The events
       are:

       - 'before-switch-to' is emitted immediately before a new tab is
       switched to.  detail = the tab element.

       - 'after-switch-to' is emitted immediately after a new tab is
       switched to.  detail = the tab element.

       Any exceptions thrown by listeners are caught and ignored, to
       avoid that they knock the tab state out of sync.

       Returns this object.
    */
    addEventListener: function(eventName, callback){
      this.e.container.addEventListener(eventName, callback, false);
      return this;
    },

    /**
       If the given DOM element, unique selector, or integer (0-based
       tab number) is one of this object's tabs, the UI makes that tab
       the currently-visible one. Returns this object.
    */
    switchToTab: function(tab){
      tab = tabArg(tab,this);
      const self = this;
      this.e.tabs.childNodes.forEach((e,ndx)=>{
        const btn = this.e.tabBar.childNodes[ndx];
        if(e===tab){
          if(D.hasClass(e,'selected')){
            return;
          }
          self._dispatchEvent('before-switch-to',tab);
          setVisible(e, true);
          D.addClass(btn,'selected');
          self._dispatchEvent('after-switch-to',tab);
        }else{
          if(D.hasClass(e,'selected')){
            return;
          }
          setVisible(e, false);
          D.removeClass(btn,'selected');
        }
      });
      return this;
    }
  };

  F.TabManager = TabManager;
})(window.fossil);
Changes to src/info.c.
1338
1339
1340
1341
1342
1343
1344
1345

1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365

1366

1367

1368
1369
1370
1371
1372
1373
1374
1338
1339
1340
1341
1342
1343
1344

1345
1346
1347
1348
1349
1350












1351
1352

1353
1354
1355

1356
1357
1358
1359
1360
1361
1362
1363







-
+





-
-
-
-
-
-
-
-
-
-
-
-


-
+

+
-
+







#define OBJTYPE_EXE        0x0100
#define OBJTYPE_FORUM      0x0200

/*
** Possible flags for the second parameter to
** object_description()
*/
#define OBJDESC_DETAIL      0x0001   /* more detail */
#define OBJDESC_DETAIL      0x0001   /* Show more detail */
#define OBJDESC_BASE        0x0002   /* Set <base> using this object */
#endif

/*
** Write a description of an object to the www reply.
**
** If the object is a file then mention:
**
**     * It's artifact ID
**     * All its filenames
**     * The check-in it was part of, with times and users
**
** If the object is a manifest, then mention:
**
**     * It's artifact ID
**     * date of check-in
**     * Comment & user
*/
int object_description(
  int rid,                 /* The artifact ID */
  int rid,                 /* The artifact ID for the object to describe */
  u32 objdescFlags,        /* Flags to control display */
  const char *zFileName,   /* For file objects, use this name.  Can be NULL */
  Blob *pDownloadName      /* Fill with an appropriate download name */
  Blob *pDownloadName      /* Fill with a good download name.  Can be NULL */
){
  Stmt q;
  int cnt = 0;
  int nWiki = 0;
  int objType = 0;
  char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
  int showDetail = (objdescFlags & OBJDESC_DETAIL)!=0;
1399
1400
1401
1402
1403
1404
1405

1406
1407
1408
1409
1410
1411
1412
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402







+







    const char *zCom = db_column_text(&q, 2);
    const char *zUser = db_column_text(&q, 3);
    const char *zVers = db_column_text(&q, 4);
    int mPerm = db_column_int(&q, 5);
    const char *zBr = db_column_text(&q, 6);
    int szFile = db_column_int(&q,7);
    int sameFilename = prevName!=0 && fossil_strcmp(zName,prevName)==0;
    if( zFileName && fossil_strcmp(zName,zFileName)!=0 ) continue;
    if( sameFilename && !showDetail ){
      if( cnt==1 ){
        @ %z(href("%R/whatis/%!S",zUuid))[more...]</a>
      }
      cnt++;
      continue;
    }
1455
1456
1457
1458
1459
1460
1461
1462

1463
1464
1465
1466
1467
1468
1469
1445
1446
1447
1448
1449
1450
1451

1452
1453
1454
1455
1456
1457
1458
1459







-
+







    if( g.perm.Hyperlink ){
      @ %z(href("%R/annotate?filename=%T&checkin=%!S",zName,zVers))
      @ [annotate]</a>
      @ %z(href("%R/blame?filename=%T&checkin=%!S",zName,zVers))
      @ [blame]</a>
      @ %z(href("%R/timeline?n=all&uf=%!S",zUuid))[check-ins&nbsp;using]</a>
      if( fileedit_is_editable(zName) ){
        @ %z(href("%R/fileedit?file=%T&r=%!S",zName,zVers))[edit]</a>
        @ %z(href("%R/fileedit?filename=%T&checkin=%!S",zName,zVers))[edit]</a>
      }
    }
    cnt++;
    if( pDownloadName && blob_size(pDownloadName)==0 ){
      blob_append(pDownloadName, zName, -1);
    }
  }
1748
1749
1750
1751
1752
1753
1754
1755

1756
1757

1758
1759
1760
1761
1762
1763
1764
1738
1739
1740
1741
1742
1743
1744

1745
1746

1747
1748
1749
1750
1751
1752
1753
1754







-
+

-
+







  if( P("smhdr")!=0 ){
    @ <h2>Differences From Artifact
    @ %z(href("%R/artifact/%!S",zV1))[%S(zV1)]</a> To
    @ %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>.</h2>
  }else{
    @ <h2>Differences From
    @ Artifact %z(href("%R/artifact/%!S",zV1))[%S(zV1)]</a>:</h2>
    object_description(v1, objdescFlags, 0);
    object_description(v1, objdescFlags,0, 0);
    @ <h2>To Artifact %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>:</h2>
    object_description(v2, objdescFlags, 0);
    object_description(v2, objdescFlags,0, 0);
  }
  if( pRe ){
    @ <b>Only differences that match regular expression "%h(zRe)"
    @ are shown.</b>
  }
  @ <hr />
  append_diff(zV1, zV2, diffFlags, pRe);
1938
1939
1940
1941
1942
1943
1944
1945

1946
1947
1948
1949
1950
1951
1952
1928
1929
1930
1931
1932
1933
1934

1935
1936
1937
1938
1939
1940
1941
1942







-
+







  if( g.perm.Setup ){
    @  (%d(rid)):</h2>
  }else{
    @ :</h2>
  }
  blob_zero(&downloadName);
  if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL;
  object_description(rid, objdescFlags, &downloadName);
  object_description(rid, objdescFlags, 0, &downloadName);
  style_submenu_element("Download", "%s/raw/%T?name=%s",
        g.zTop, blob_str(&downloadName), zUuid);
  @ <hr />
  content_get(rid, &content);
  @ <blockquote><pre>
  hexdump(&content);
  @ </pre></blockquote>
2128
2129
2130
2131
2132
2133
2134
2135

2136
2137
2138
2139
2140

2141




2142


2143













2144


2145
2146
2147


2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163







2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180











2181
2182



2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
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
2118
2119
2120
2121
2122
2123
2124

2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
2150
2151
2152
2153

2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211


2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222


2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245

2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260

2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289

2290

2291
2292
2293
2294
2295
2296
2297
2298
2299







-
+





+

+
+
+
+

+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
+



+
+
















+
+
+
+
+
+
+

















+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+








-
-











+

+
+
+
+
+
+
+
+
+
+
-
+
+













-
+














+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
+
+







  Blob content;
  const char *zMime;
  Blob downloadName;
  int renderAsWiki = 0;
  int renderAsHtml = 0;
  int objType;
  int asText;
  const char *zUuid;
  const char *zUuid = 0;
  u32 objdescFlags = OBJDESC_BASE;
  int descOnly = fossil_strcmp(g.zPath,"whatis")==0;
  int isFile = fossil_strcmp(g.zPath,"file")==0;
  const char *zLn = P("ln");
  const char *zName = P("name");
  const char *zCI = P("ci");
  HQuery url;
  Blob dirname;
  char *zCIUuid = 0;
  int isSymbolicCI = 0;  /* ci= exists and is a symbolic name, not a hash */
  char *zHeader = 0;

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  url_initialize(&url, g.zPath);
  if( zCI && strlen(zCI)==0 ){ zCI = 0; }
  if( zCI ){
    blob_zero(&dirname);
    hyperlinked_path(zName, &dirname, zCI, "dir", "");
    blob_reset(&dirname);

    if( name_to_uuid2(zCI, "ci", &zCIUuid) ){
      isSymbolicCI = (sqlite3_strnicmp(zCIUuid, zCI, strlen(zCI)) != 0);
    }
  }
  if( isFile && zName ) {
    rid = artifact_from_ci_and_filename(0, "name");
  }else{
  rid = artifact_from_ci_and_filename(&url, 0);
    rid = artifact_from_ci_and_filename(&url, 0);
  }
  if( rid==0 ){
    url_add_parameter(&url, "name", zName);
    if( isFile ){
      int isUnknownAtCI = 0;

      /* Do a top-level directory listing in /file mode if no argument
      ** specified */
      if( zName==0 || zName[0]==0 ){
        if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
        page_tree();
        return;
      }
      /* Look for a single file with the given name */
      rid = db_int(0,
         "SELECT fid FROM filename, mlink, event"
         " WHERE name=%Q"
         "   AND mlink.fnid=filename.fnid"
         "   AND event.objid=mlink.mid"
         " ORDER BY event.mtime DESC LIMIT 1",
         zName
      );
      /* If found only by the name, then the file is unknown in the check-in.
      ** Possibly, the file was renamed/deleted.
      */
      if( rid && zCIUuid ){
        rid = 0;
        isUnknownAtCI = 1;
      }
      /* If no file called NAME exists, instead look for a directory
      ** with that name, and do a directory listing */
      if( rid==0 ){
        int nName = (int)strlen(zName);
        if( nName && zName[nName-1]=='/' ) nName--;
        if( db_exists(
           "SELECT 1 FROM filename"
           " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';",
           nName, zName, nName+1, nName, zName
        ) ){
          if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
          page_tree();
          return;
        }
      }
      /* If no file or directory called NAME: issue an error */
      if( rid==0 ){
        if( isUnknownAtCI ){
          if( isSymbolicCI ){
            zHeader = mprintf("No such file at %s", zCI);
          }else{
            zHeader = mprintf("No such file at [%S]", zCIUuid);
          }
          style_header("%s", zHeader);
          fossil_free(zHeader);
          @ File %z(href("%R/finfo?name=%T",zName))%h(zName)</a> is not known
          @ at check-in [%z(href("/info/%!S",zCIUuid))%S(zCIUuid)</a>].
        }else{
        style_header("No such file");
        @ File '%h(zName)' does not exist in this repository.
          style_header("No such file");
          @ File '%h(zName)' is not known in this repository.
        }
        style_footer();
        return;
      }
    }else{
      rid = name_to_rid_www("name");
    }
  }

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  if( rid==0 ){
    style_header("No such artifact");
    @ Artifact '%h(zName)' does not exist in this repository.
    style_footer();
    return;
  }
  if( descOnly || P("verbose")!=0 ){
    url_add_parameter(&url, "verbose", "1");
    objdescFlags |= OBJDESC_DETAIL;
  }
  zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);

  if( isFile ){
    if( zCI ){
      const char *zPath;
      Blob path;
      blob_zero(&path);
      hyperlinked_path(zName, &path, zCI, "dir", "");
      zPath = blob_str(&path);
      @ <h2>File %s(zPath) at check-in [%z(href("/info/%!S",zCIUuid))%S(zCIUuid)</a>]
      @ </h2>
      blob_reset(&path);
    }else{
    @ <h2>Latest version of file '%h(zName)':</h2>
      @ <h2>Latest version of file '%h(zName)':</h2>
    }
    style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
  }else{
    @ <h2>Artifact
    style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);
    if( g.perm.Setup ){
      @  (%d(rid)):</h2>
    }else{
      @ :</h2>
    }
  }
  blob_zero(&downloadName);
  asText = P("txt")!=0;
  if( asText ) objdescFlags &= ~OBJDESC_BASE;
  objType = object_description(rid, objdescFlags, &downloadName);
  objType = object_description(rid, objdescFlags, (isFile ? zName : 0),&downloadName);
  if( !descOnly && P("download")!=0 ){
    cgi_redirectf("%R/raw/%T?name=%s", blob_str(&downloadName),
          db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid));
    /*NOTREACHED*/
  }
  if( g.perm.Admin ){
    const char *zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
    if( db_exists("SELECT 1 FROM shun WHERE uuid=%Q", zUuid) ){
      style_submenu_element("Unshun", "%s/shun?accept=%s&sub=1#accshun",
            g.zTop, zUuid);
    }else{
      style_submenu_element("Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid);
    }
  }

  if( isFile ){
    if( isSymbolicCI ){
      zHeader = mprintf("%s at %s", file_tail(zName), zCI);
    }else if( zCI ){
      zHeader = mprintf("%s at [%S]", file_tail(zName), zCIUuid);
    }else{
      zHeader = mprintf("%s", file_tail(zName));
    }
  }else if( descOnly ){
    zHeader = mprintf("Artifact Description [%S]", zUuid);
  }else{
    zHeader = mprintf("Artifact [%S]", zUuid);
  }
  style_header("%s", isFile ? "File Content" :
  style_header("%s", zHeader);
                     descOnly ? "Artifact Description" : "Artifact Content");
  fossil_free(zCIUuid);
  fossil_free(zHeader);
  if( g.perm.Admin ){
    Stmt q;
    db_prepare(&q,
      "SELECT coalesce(user.login,rcvfrom.uid),"
      "       datetime(rcvfrom.mtime,toLocal()), rcvfrom.ipaddr"
      "  FROM blob, rcvfrom LEFT JOIN user ON user.uid=rcvfrom.uid"
      " WHERE blob.rid=%d"
Changes to src/login.c.
1734
1735
1736
1737
1738
1739
1740
1741

1742
1743
1744
1745
1746
1747
1748
1734
1735
1736
1737
1738
1739
1740

1741
1742
1743
1744
1745
1746
1747
1748







-
+







  @   <td><input type="password" name="cp" value="%h(zConfirm)" size="30"></td>
  @ </tr>
  if( iErrLine==5 ){
    @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
  }
  @ <tr>
  @   <td class="form_label" align="right">Captcha:</td>
  @   <td><input type="text" name="captcha" size="30"\
  @   <td><input type="text" name="captcha" \
  @ value="%h(captchaIsCorrect?zDecoded:"")" size="30">
  captcha_speakit_button(uSeed, "Speak the captcha text");
  @   </td>
  @ </tr>
  if( iErrLine==6 ){
    @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
  }
Changes to src/main.mk.
130
131
132
133
134
135
136

137
138
139
140
141
142
143
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144







+







  $(SRCDIR)/stash.c \
  $(SRCDIR)/stat.c \
  $(SRCDIR)/statrep.c \
  $(SRCDIR)/style.c \
  $(SRCDIR)/sync.c \
  $(SRCDIR)/tag.c \
  $(SRCDIR)/tar.c \
  $(SRCDIR)/terminal.c \
  $(SRCDIR)/th_main.c \
  $(SRCDIR)/timeline.c \
  $(SRCDIR)/tkt.c \
  $(SRCDIR)/tktsetup.c \
  $(SRCDIR)/undo.c \
  $(SRCDIR)/unicode.c \
  $(SRCDIR)/unversioned.c \
216
217
218
219
220
221
222






223
224
225
226
227
228
229
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236







+
+
+
+
+
+







  $(SRCDIR)/../skins/xekri/footer.txt \
  $(SRCDIR)/../skins/xekri/header.txt \
  $(SRCDIR)/accordion.js \
  $(SRCDIR)/ci_edit.js \
  $(SRCDIR)/copybtn.js \
  $(SRCDIR)/diff.tcl \
  $(SRCDIR)/forum.js \
  $(SRCDIR)/fossil.bootstrap.js \
  $(SRCDIR)/fossil.confirmer.js \
  $(SRCDIR)/fossil.dom.js \
  $(SRCDIR)/fossil.fetch.js \
  $(SRCDIR)/fossil.page.fileedit.js \
  $(SRCDIR)/fossil.tabs.js \
  $(SRCDIR)/graph.js \
  $(SRCDIR)/href.js \
  $(SRCDIR)/login.js \
  $(SRCDIR)/markdown.md \
  $(SRCDIR)/menu.js \
  $(SRCDIR)/sbsdiff.js \
  $(SRCDIR)/scroll.js \
364
365
366
367
368
369
370

371
372
373
374
375
376
377
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385







+







  $(OBJDIR)/stash_.c \
  $(OBJDIR)/stat_.c \
  $(OBJDIR)/statrep_.c \
  $(OBJDIR)/style_.c \
  $(OBJDIR)/sync_.c \
  $(OBJDIR)/tag_.c \
  $(OBJDIR)/tar_.c \
  $(OBJDIR)/terminal_.c \
  $(OBJDIR)/th_main_.c \
  $(OBJDIR)/timeline_.c \
  $(OBJDIR)/tkt_.c \
  $(OBJDIR)/tktsetup_.c \
  $(OBJDIR)/undo_.c \
  $(OBJDIR)/unicode_.c \
  $(OBJDIR)/unversioned_.c \
507
508
509
510
511
512
513

514
515
516
517
518
519
520
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529







+







 $(OBJDIR)/stash.o \
 $(OBJDIR)/stat.o \
 $(OBJDIR)/statrep.o \
 $(OBJDIR)/style.o \
 $(OBJDIR)/sync.o \
 $(OBJDIR)/tag.o \
 $(OBJDIR)/tar.o \
 $(OBJDIR)/terminal.o \
 $(OBJDIR)/th_main.o \
 $(OBJDIR)/timeline.o \
 $(OBJDIR)/tkt.o \
 $(OBJDIR)/tktsetup.o \
 $(OBJDIR)/undo.o \
 $(OBJDIR)/unicode.o \
 $(OBJDIR)/unversioned.o \
845
846
847
848
849
850
851

852
853
854
855
856
857
858
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868







+







	$(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \
	$(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \
	$(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \
	$(OBJDIR)/style_.c:$(OBJDIR)/style.h \
	$(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \
	$(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \
	$(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \
	$(OBJDIR)/terminal_.c:$(OBJDIR)/terminal.h \
	$(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \
	$(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
	$(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
	$(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
	$(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
	$(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \
	$(OBJDIR)/unversioned_.c:$(OBJDIR)/unversioned.h \
1818
1819
1820
1821
1822
1823
1824








1825
1826
1827
1828
1829
1830
1831
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849







+
+
+
+
+
+
+
+







$(OBJDIR)/tar_.c:	$(SRCDIR)/tar.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/tar.c >$@

$(OBJDIR)/tar.o:	$(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c

$(OBJDIR)/tar.h:	$(OBJDIR)/headers

$(OBJDIR)/terminal_.c:	$(SRCDIR)/terminal.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/terminal.c >$@

$(OBJDIR)/terminal.o:	$(OBJDIR)/terminal_.c $(OBJDIR)/terminal.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/terminal.o -c $(OBJDIR)/terminal_.c

$(OBJDIR)/terminal.h:	$(OBJDIR)/headers

$(OBJDIR)/th_main_.c:	$(SRCDIR)/th_main.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/th_main.c >$@

$(OBJDIR)/th_main.o:	$(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/th_main.o -c $(OBJDIR)/th_main_.c

Changes to src/makemake.tcl.
140
141
142
143
144
145
146

147
148
149
150
151
152
153
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154







+







  stash
  stat
  statrep
  style
  sync
  tag
  tar
  terminal
  th_main
  timeline
  tkt
  tktsetup
  undo
  unicode
  unversioned
Changes to src/merge3.c.
137
138
139
140
141
142
143
144

145
146
147
148
149
150
151
137
138
139
140
141
142
143

144
145
146
147
148
149
150
151







-
+








/*
** Text of boundary markers for merge conflicts.
*/
static const char *const mergeMarker[] = {
 /*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/
  "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<\n",
  "======= COMMON ANCESTOR content follows ============================\n",
  "||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||||||\n",
  "======= MERGED IN content follows ==================================\n",
  ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
};


/*
** Do a three-way merge.  Initialize pOut to contain the result.
Changes to src/name.c.
612
613
614
615
616
617
618
619

620
621
622
623
624
625
626
612
613
614
615
616
617
618

619
620
621
622
623
624
625
626







-
+







  canonical16(z, strlen(z));
  db_prepare(&q, "SELECT uuid, rid FROM blob WHERE uuid GLOB '%q*'", z);
  while( db_step(&q)==SQLITE_ROW ){
    const char *zUuid = db_column_text(&q, 0);
    int rid = db_column_int(&q, 1);
    @ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)">
    @ %s(zUuid)</a> -
    object_description(rid, 0, 0);
    object_description(rid, 0, 0, 0);
    @ </p></li>
  }
  db_finalize(&q);
  db_prepare(&q,
    "   SELECT tkt_rid, tkt_uuid, title"
    "     FROM ticket, ticketchng"
    "    WHERE ticket.tkt_id = ticketchng.tkt_id"
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
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







-
+















-
+







    @ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)">
    @ %s(zUuid)</a> -
    @ <ul></ul>
    @ Ticket
    hyperlink_to_uuid(zUuid);
    @ - %h(zTitle).
    @ <ul><li>
    object_description(rid, 0, 0);
    object_description(rid, 0, 0, 0);
    @ </li></ul>
    @ </p></li>
  }
  db_finalize(&q);
  db_prepare(&q,
    "SELECT rid, uuid FROM"
    "  (SELECT tagxref.rid AS rid, substr(tagname, 7) AS uuid"
    "     FROM tagxref, tag WHERE tagxref.tagid = tag.tagid"
    "      AND tagname GLOB 'event-%q*') GROUP BY uuid", z);
  while( db_step(&q)==SQLITE_ROW ){
    int rid = db_column_int(&q, 0);
    const char* zUuid = db_column_text(&q, 1);
    @ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)">
    @ %s(zUuid)</a> -
    @ <ul><li>
    object_description(rid, 0, 0);
    object_description(rid, 0, 0, 0);
    @ </li></ul>
    @ </p></li>
  }
  @ </ol>
  db_finalize(&q);
  style_footer();
}
Changes to src/setup.c.
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
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







+




+
+
+
+
+
+
+
+



-
-
-
-
-
-
-
+
+
+
-
+







      } else {
        @ <br />
      }
    }
  }
  @ <br /><input type="submit"  name="submit" value="Apply Changes" />
  @ </td><td style="width:50px;"></td><td valign="top">
  @ <table>
  for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
    if( pSet->width>0 && !pSet->forceTextArea ){
      int hasVersionableValue = pSet->versionable &&
          (db_get_versioned(pSet->name, NULL)!=0);
      @ <tr><td>
      @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
      if( pSet->versionable ){
        @  (v)
      } else {
        @
      }
      @</td><td>
      entry_attribute("", /*pSet->width*/ 25, pSet->name,
                      pSet->var!=0 ? pSet->var : pSet->name,
                      (char*)pSet->def, hasVersionableValue);
      @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
      if( pSet->versionable ){
        @  (v)<br />
      } else {
        @ <br />
      }
    }
      @</td></tr>
    }
  }
  }
  @</table>
  @ </td><td style="width:50px;"></td><td valign="top">
  for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
    if( pSet->width>0 && pSet->forceTextArea ){
      int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0;
      @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a>
      if( pSet->versionable ){
        @  (v)<br />
Changes to src/shell.c.
4328
4329
4330
4331
4332
4333
4334

4335
4336
4337
4338
4339
4340
4341
4328
4329
4330
4331
4332
4333
4334
4335
4336
4337
4338
4339
4340
4341
4342







+







  void *notUsed,
  int nKey1, const void *pKey1,
  int nKey2, const void *pKey2
){
  const unsigned char *zA = (const unsigned char*)pKey1;
  const unsigned char *zB = (const unsigned char*)pKey2;
  int i=0, j=0, x;
  (void)notUsed;
  while( i<nKey1 && j<nKey2 ){
    x = zA[i] - zB[j];
    if( isdigit(zA[i]) ){
      int k;
      if( !isdigit(zB[j]) ) return x;
      while( i<nKey1 && zA[i]=='0' ){ i++; }
      while( j<nKey2 && zB[j]=='0' ){ j++; }
11814
11815
11816
11817
11818
11819
11820

11821
11822
11823
11824
11825
11826
11827
11815
11816
11817
11818
11819
11820
11821
11822
11823
11824
11825
11826
11827
11828
11829







+







        zEQP = sqlite3_mprintf("EXPLAIN QUERY PLAN %s", zStmtSql);
        rc = sqlite3_prepare_v2(db, zEQP, -1, &pExplain, 0);
        if( rc==SQLITE_OK ){
          while( sqlite3_step(pExplain)==SQLITE_ROW ){
            const char *zEQPLine = (const char*)sqlite3_column_text(pExplain,3);
            int iEqpId = sqlite3_column_int(pExplain, 0);
            int iParentId = sqlite3_column_int(pExplain, 1);
            if( zEQPLine==0 ) zEQPLine = "";
            if( zEQPLine[0]=='-' ) eqp_render(pArg);
            eqp_append(pArg, iEqpId, iParentId, zEQPLine);
          }
          eqp_render(pArg);
        }
        sqlite3_finalize(pExplain);
        sqlite3_free(zEQP);
13723
13724
13725
13726
13727
13728
13729
13730
13731
13732
13733
13734

13735
13736
13737
13738
13739
13740
13741
13742
13725
13726
13727
13728
13729
13730
13731





13732

13733
13734
13735
13736
13737
13738
13739







-
-
-
-
-
+
-







  unsigned char aHdr[100];
  open_db(p, 0);
  if( p->db==0 ) return 1;
  rc = sqlite3_prepare_v2(p->db,
             "SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1",
             -1, &pStmt, 0);
  if( rc ){
    if( !sqlite3_compileoption_used("ENABLE_DBPAGE_VTAB") ){
      utf8_printf(stderr, "the \".dbinfo\" command requires the "
                          "-DSQLITE_ENABLE_DBPAGE_VTAB compile-time options\n");
    }else{
      utf8_printf(stderr, "error: %s\n", sqlite3_errmsg(p->db));
    utf8_printf(stderr, "error: %s\n", sqlite3_errmsg(p->db));
    }
    sqlite3_finalize(pStmt);
    return 1;
  }
  sqlite3_bind_text(pStmt, 1, zDb, -1, SQLITE_STATIC);
  if( sqlite3_step(pStmt)==SQLITE_ROW
   && sqlite3_column_bytes(pStmt,0)>100
  ){
17002
17003
17004
17005
17006
17007
17008
17009

17010
17011
17012
17013
17014
17015
17016
16999
17000
17001
17002
17003
17004
17005

17006
17007
17008
17009
17010
17011
17012
17013







-
+







   || (c=='e' && n==5 && strcmp(azArg[0],"excel")==0)
  ){
    const char *zFile = 0;
    int bTxtMode = 0;
    int i;
    int eMode = 0;
    int bBOM = 0;
    int bOnce;
    int bOnce = 0;  /* 0: .output, 1: .once, 2: .excel */

    if( c=='e' ){
      eMode = 'x';
      bOnce = 2;
    }else if( strncmp(azArg[0],"once",n)==0 ){
      bOnce = 1;
    }
Changes to src/sqlite3.c.
214
215
216
217
218
219
220



221
222
223
224
225
226
227
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230







+
+
+







#endif
#if SQLITE_ENABLE_ATOMIC_WRITE
  "ENABLE_ATOMIC_WRITE",
#endif
#if SQLITE_ENABLE_BATCH_ATOMIC_WRITE
  "ENABLE_BATCH_ATOMIC_WRITE",
#endif
#if SQLITE_ENABLE_BYTECODE_VTAB
  "ENABLE_BYTECODE_VTAB",
#endif
#if SQLITE_ENABLE_CEROD
  "ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD),
#endif
#if SQLITE_ENABLE_COLUMN_METADATA
  "ENABLE_COLUMN_METADATA",
#endif
#if SQLITE_ENABLE_COLUMN_USED_MASK
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
535
536
537
538
539
540
541



542
543
544
545
546
547
548







-
-
-







#endif
#if SQLITE_OMIT_BETWEEN_OPTIMIZATION
  "OMIT_BETWEEN_OPTIMIZATION",
#endif
#if SQLITE_OMIT_BLOB_LITERAL
  "OMIT_BLOB_LITERAL",
#endif
#if SQLITE_OMIT_BTREECOUNT
  "OMIT_BTREECOUNT",
#endif
#if SQLITE_OMIT_CAST
  "OMIT_CAST",
#endif
#if SQLITE_OMIT_CHECK
  "OMIT_CHECK",
#endif
#if SQLITE_OMIT_COMPLETE
1160
1161
1162
1163
1164
1165
1166
1167

1168
1169
1170
1171
1172
1173
1174
1160
1161
1162
1163
1164
1165
1166

1167
1168
1169
1170
1171
1172
1173
1174







-
+







**
** See also: [sqlite3_libversion()],
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION        "3.32.0"
#define SQLITE_VERSION_NUMBER 3032000
#define SQLITE_SOURCE_ID      "2020-04-20 17:35:32 2fc80ef16ce5878311ab88a0c64631813572ffbb71f75363b4619c9667e0926b"
#define SQLITE_SOURCE_ID      "2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f9654ff"

/*
** 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
1334
1335
1336
1337
1338
1339
1340




1341
1342
1343
1344
1345
1346
1347
1348
1349
1350











1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345









1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356










1357
1358
1359
1360
1361
1362
1363







+
+
+
+

-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-







**
** ^The sqlite3_close() and sqlite3_close_v2() routines are destructors
** for the [sqlite3] object.
** ^Calls to sqlite3_close() and sqlite3_close_v2() return [SQLITE_OK] if
** the [sqlite3] object is successfully destroyed and all associated
** resources are deallocated.
**
** Ideally, applications should [sqlite3_finalize | finalize] all
** [prepared statements], [sqlite3_blob_close | close] all [BLOB handles], and 
** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated
** with the [sqlite3] object prior to attempting to close the object.
** ^If the database connection is associated with unfinalized prepared
** statements or unfinished sqlite3_backup objects then sqlite3_close()
** will leave the database connection open and return [SQLITE_BUSY].
** ^If sqlite3_close_v2() is called with unfinalized prepared statements
** and/or unfinished sqlite3_backups, then the database connection becomes
** an unusable "zombie" which will automatically be deallocated when the
** last prepared statement is finalized or the last sqlite3_backup is
** finished.  The sqlite3_close_v2() interface is intended for use with
** host languages that are garbage collected, and where the order in which
** destructors are called is arbitrary.
** statements, BLOB handlers, and/or unfinished sqlite3_backup objects then
** sqlite3_close() will leave the database connection open and return
** [SQLITE_BUSY]. ^If sqlite3_close_v2() is called with unfinalized prepared
** statements, unclosed BLOB handlers, and/or unfinished sqlite3_backups,
** it returns [SQLITE_OK] regardless, but instead of deallocating the database
** connection immediately, it marks the database connection as an unusable
** "zombie" and makes arrangements to automatically deallocate the database
** connection after all prepared statements are finalized, all BLOB handles
** are closed, and all backups have finished. The sqlite3_close_v2() interface
** is intended for use with host languages that are garbage collected, and
** where the order in which destructors are called is arbitrary.
**
** Applications should [sqlite3_finalize | finalize] all [prepared statements],
** [sqlite3_blob_close | close] all [BLOB handles], and 
** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated
** with the [sqlite3] object prior to attempting to close the object.  ^If
** sqlite3_close_v2() is called on a [database connection] that still has
** outstanding [prepared statements], [BLOB handles], and/or
** [sqlite3_backup] objects then it returns [SQLITE_OK] and the deallocation
** of resources is deferred until all [prepared statements], [BLOB handles],
** and [sqlite3_backup] objects are also destroyed.
**
** ^If an [sqlite3] object is destroyed while a transaction is open,
** the transaction is automatically rolled back.
**
** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)]
** must be either a NULL
** pointer or an [sqlite3] object pointer obtained
1542
1543
1544
1545
1546
1547
1548

1549
1550
1551
1552

1553
1554
1555
1556
1557
1558
1559
1560

1561
1562
1563
1564
1565
1566
1567
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566







+




+








+







#define SQLITE_IOERR_GETTEMPPATH       (SQLITE_IOERR | (25<<8))
#define SQLITE_IOERR_CONVPATH          (SQLITE_IOERR | (26<<8))
#define SQLITE_IOERR_VNODE             (SQLITE_IOERR | (27<<8))
#define SQLITE_IOERR_AUTH              (SQLITE_IOERR | (28<<8))
#define SQLITE_IOERR_BEGIN_ATOMIC      (SQLITE_IOERR | (29<<8))
#define SQLITE_IOERR_COMMIT_ATOMIC     (SQLITE_IOERR | (30<<8))
#define SQLITE_IOERR_ROLLBACK_ATOMIC   (SQLITE_IOERR | (31<<8))
#define SQLITE_IOERR_DATA              (SQLITE_IOERR | (32<<8))
#define SQLITE_LOCKED_SHAREDCACHE      (SQLITE_LOCKED |  (1<<8))
#define SQLITE_LOCKED_VTAB             (SQLITE_LOCKED |  (2<<8))
#define SQLITE_BUSY_RECOVERY           (SQLITE_BUSY   |  (1<<8))
#define SQLITE_BUSY_SNAPSHOT           (SQLITE_BUSY   |  (2<<8))
#define SQLITE_BUSY_TIMEOUT            (SQLITE_BUSY   |  (3<<8))
#define SQLITE_CANTOPEN_NOTEMPDIR      (SQLITE_CANTOPEN | (1<<8))
#define SQLITE_CANTOPEN_ISDIR          (SQLITE_CANTOPEN | (2<<8))
#define SQLITE_CANTOPEN_FULLPATH       (SQLITE_CANTOPEN | (3<<8))
#define SQLITE_CANTOPEN_CONVPATH       (SQLITE_CANTOPEN | (4<<8))
#define SQLITE_CANTOPEN_DIRTYWAL       (SQLITE_CANTOPEN | (5<<8)) /* Not Used */
#define SQLITE_CANTOPEN_SYMLINK        (SQLITE_CANTOPEN | (6<<8))
#define SQLITE_CORRUPT_VTAB            (SQLITE_CORRUPT | (1<<8))
#define SQLITE_CORRUPT_SEQUENCE        (SQLITE_CORRUPT | (2<<8))
#define SQLITE_CORRUPT_INDEX           (SQLITE_CORRUPT | (3<<8))
#define SQLITE_READONLY_RECOVERY       (SQLITE_READONLY | (1<<8))
#define SQLITE_READONLY_CANTLOCK       (SQLITE_READONLY | (2<<8))
#define SQLITE_READONLY_ROLLBACK       (SQLITE_READONLY | (3<<8))
#define SQLITE_READONLY_DBMOVED        (SQLITE_READONLY | (4<<8))
#define SQLITE_READONLY_CANTINIT       (SQLITE_READONLY | (5<<8))
#define SQLITE_READONLY_DIRECTORY      (SQLITE_READONLY | (6<<8))
#define SQLITE_ABORT_ROLLBACK          (SQLITE_ABORT | (2<<8))
2148
2149
2150
2151
2152
2153
2154





2155
2156
2157
2158
2159
2160
2161
2147
2148
2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
2164
2165







+
+
+
+
+







** omits changes made by other database connections.  The
** [PRAGMA data_version] command provides a mechanism to detect changes to
** a single attached database that occur due to other database connections,
** but omits changes implemented by the database connection on which it is
** called.  This file control is the only mechanism to detect changes that
** happen either internally or externally and that are associated with
** a particular attached database.
**
** <li>[[SQLITE_FCNTL_CKPT_START]]
** The [SQLITE_FCNTL_CKPT_START] opcode is invoked from within a checkpoint
** in wal mode before the client starts to copy pages from the wal
** file to the database file.
**
** <li>[[SQLITE_FCNTL_CKPT_DONE]]
** The [SQLITE_FCNTL_CKPT_DONE] opcode is invoked from within a checkpoint
** in wal mode after the client has finished copying pages from the wal
** file to the database file, but before the *-shm file is updated to
** record the fact that the pages have been checkpointed.
** </ul>
2193
2194
2195
2196
2197
2198
2199

2200
2201
2202
2203
2204
2205
2206
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211







+







#define SQLITE_FCNTL_COMMIT_ATOMIC_WRITE    32
#define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE  33
#define SQLITE_FCNTL_LOCK_TIMEOUT           34
#define SQLITE_FCNTL_DATA_VERSION           35
#define SQLITE_FCNTL_SIZE_LIMIT             36
#define SQLITE_FCNTL_CKPT_DONE              37
#define SQLITE_FCNTL_RESERVE_BYTES          38
#define SQLITE_FCNTL_CKPT_START             39

/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE      SQLITE_FCNTL_GET_LOCKPROXYFILE
#define SQLITE_SET_LOCKPROXYFILE      SQLITE_FCNTL_SET_LOCKPROXYFILE
#define SQLITE_LAST_ERRNO             SQLITE_FCNTL_LAST_ERRNO


4571
4572
4573
4574
4575
4576
4577




4578
4579









4580
4581
4582
4583
4584
4585
4586
4576
4577
4578
4579
4580
4581
4582
4583
4584
4585
4586


4587
4588
4589
4590
4591
4592
4593
4594
4595
4596
4597
4598
4599
4600
4601
4602







+
+
+
+
-
-
+
+
+
+
+
+
+
+
+







/*
** CAPI3REF: Obtain Values For URI Parameters
**
** These are utility routines, useful to [VFS|custom VFS implementations],
** that check if a database file was a URI that contained a specific query 
** parameter, and if so obtains the value of that query parameter.
**
** The first parameter to these interfaces (hereafter referred to
** as F) must be one of:
** <ul>
** <li> A database filename pointer created by the SQLite core and
** If F is the database filename pointer passed into the xOpen() method of 
** a VFS implementation or it is the return value of [sqlite3_db_filename()]
** passed into the xOpen() method of a VFS implemention, or
** <li> A filename obtained from [sqlite3_db_filename()], or
** <li> A new filename constructed using [sqlite3_create_filename()].
** </ul>
** If the F parameter is not one of the above, then the behavior is
** undefined and probably undesirable.  Older versions of SQLite were
** more tolerant of invalid F parameters than newer versions.
**
** If F is a suitable filename (as described in the previous paragraph)
** and if P is the name of the query parameter, then
** sqlite3_uri_parameter(F,P) returns the value of the P
** parameter if it exists or a NULL pointer if P does not appear as a 
** query parameter on F.  If P is a query parameter of F and it
** has no explicit value, then sqlite3_uri_parameter(F,P) returns
** a pointer to an empty string.
**
4655
4656
4657
4658
4659
4660
4661



















4662
4663
4664
4665
4666
4667
4668
4671
4672
4673
4674
4675
4676
4677
4678
4679
4680
4681
4682
4683
4684
4685
4686
4687
4688
4689
4690
4691
4692
4693
4694
4695
4696
4697
4698
4699
4700
4701
4702
4703







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







** return value from [sqlite3_db_filename()], then the result is
** undefined and is likely a memory access violation.
*/
SQLITE_API const char *sqlite3_filename_database(const char*);
SQLITE_API const char *sqlite3_filename_journal(const char*);
SQLITE_API const char *sqlite3_filename_wal(const char*);

/*
** CAPI3REF:  Database File Corresponding To A Journal
**
** ^If X is the name of a rollback or WAL-mode journal file that is
** passed into the xOpen method of [sqlite3_vfs], then 
** sqlite3_database_file_object(X) returns a pointer to the [sqlite3_file]
** object that represents the main database file.
**
** This routine is intended for use in custom [VFS] implementations
** only.  It is not a general-purpose interface.
** The argument sqlite3_file_object(X) must be a filename pointer that
** has been passed into [sqlite3_vfs].xOpen method where the 
** flags parameter to xOpen contains one of the bits
** [SQLITE_OPEN_MAIN_JOURNAL] or [SQLITE_OPEN_WAL].  Any other use
** of this routine results in undefined and probably undesirable
** behavior.
*/
SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*);

/*
** CAPI3REF: Create and Destroy VFS Filenames
**
** These interfces are provided for use by [VFS shim] implementations and
** are not useful outside of that context.
**
** The sqlite3_create_filename(D,J,W,N,P) allocates memory to hold a version of
4689
4690
4691
4692
4693
4694
4695
4696

4697
4698
4699
4700
4701
4702
4703
4724
4725
4726
4727
4728
4729
4730

4731
4732
4733
4734
4735
4736
4737
4738







-
+







** pointer if N is zero.  None of the 2*N pointers in the P array may be
** NULL pointers and key pointers should not be empty strings.
** None of the D, J, or W parameters to sqlite3_create_filename(D,J,W,N,P) may
** be NULL pointers, though they can be empty strings.
**
** The sqlite3_free_filename(Y) routine releases a memory allocation
** previously obtained from sqlite3_create_filename().  Invoking
** sqlite3_free_filename(Y) is a NULL pointer is a harmless no-op.
** sqlite3_free_filename(Y) where Y is a NULL pointer is a harmless no-op.
**
** If the Y parameter to sqlite3_free_filename(Y) is anything other
** than a NULL pointer or a pointer previously acquired from
** sqlite3_create_filename(), then bad things such as heap
** corruption or segfaults may occur. The value Y should be 
** used again after sqlite3_free_filename(Y) has been called.  This means
** that if the [sqlite3_vfs.xOpen()] method of a VFS has been called using Y,
14500
14501
14502
14503
14504
14505
14506
14507
14508
14509
14510
14511
14512
14513
14514
14535
14536
14537
14538
14539
14540
14541

14542
14543
14544
14545
14546
14547
14548







-







** callback is currently invoked only from within pager.c.
*/
typedef struct BusyHandler BusyHandler;
struct BusyHandler {
  int (*xBusyHandler)(void *,int);  /* The busy callback */
  void *pBusyArg;                   /* First arg to busy callback */
  int nBusy;                        /* Incremented with each busy call */
  u8 bExtraFileArg;                 /* Include sqlite3_file as callback arg */
};

/*
** Name of the master database table.  The master database table
** is a special table that holds the names and attributes of all
** user tables and indices.
*/
15020
15021
15022
15023
15024
15025
15026
15027
15028
15029
15030
15031
15032
15033
15034
15035
15036
15054
15055
15056
15057
15058
15059
15060

15061

15062
15063
15064
15065
15066
15067
15068







-

-







SQLITE_PRIVATE int sqlite3HeaderSizeBtree(void);

#ifndef NDEBUG
SQLITE_PRIVATE int sqlite3BtreeCursorIsValid(BtCursor*);
#endif
SQLITE_PRIVATE int sqlite3BtreeCursorIsValidNN(BtCursor*);

#ifndef SQLITE_OMIT_BTREECOUNT
SQLITE_PRIVATE int sqlite3BtreeCount(sqlite3*, BtCursor*, i64*);
#endif

#ifdef SQLITE_TEST
SQLITE_PRIVATE int sqlite3BtreeCursorInfo(BtCursor*, int*, int);
SQLITE_PRIVATE void sqlite3BtreeCursorList(Btree*);
#endif

#ifndef SQLITE_OMIT_WAL
15597
15598
15599
15600
15601
15602
15603



15604
15605
15606
15607
15608
15609
15610
15629
15630
15631
15632
15633
15634
15635
15636
15637
15638
15639
15640
15641
15642
15643
15644
15645







+
+
+







typedef int (*RecordCompare)(int,const void*,UnpackedRecord*);
SQLITE_PRIVATE RecordCompare sqlite3VdbeFindCompare(UnpackedRecord*);

SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *);
SQLITE_PRIVATE int sqlite3VdbeHasSubProgram(Vdbe*);

SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*);
#ifdef SQLITE_ENABLE_BYTECODE_VTAB
SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*);
#endif

/* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on
** each VDBE opcode.
**
** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op
** comments in VDBE programs that show key decision points in the code
** generator.
15882
15883
15884
15885
15886
15887
15888
15889
15890


15891
15892
15893
15894
15895








15896
15897
15898
15899
15900
15901
15902
15917
15918
15919
15920
15921
15922
15923


15924
15925
15926
15927
15928
15929
15930
15931
15932
15933
15934
15935
15936
15937
15938
15939
15940
15941
15942
15943
15944
15945







-
-
+
+





+
+
+
+
+
+
+
+







#ifndef SQLITE_OMIT_WAL
SQLITE_PRIVATE   int sqlite3PagerCheckpoint(Pager *pPager, sqlite3*, int, int*, int*);
SQLITE_PRIVATE   int sqlite3PagerWalSupported(Pager *pPager);
SQLITE_PRIVATE   int sqlite3PagerWalCallback(Pager *pPager);
SQLITE_PRIVATE   int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
SQLITE_PRIVATE   int sqlite3PagerCloseWal(Pager *pPager, sqlite3*);
# ifdef SQLITE_ENABLE_SNAPSHOT
SQLITE_PRIVATE   int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot);
SQLITE_PRIVATE   int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot);
SQLITE_PRIVATE   int sqlite3PagerSnapshotGet(Pager*, sqlite3_snapshot **ppSnapshot);
SQLITE_PRIVATE   int sqlite3PagerSnapshotOpen(Pager*, sqlite3_snapshot *pSnapshot);
SQLITE_PRIVATE   int sqlite3PagerSnapshotRecover(Pager *pPager);
SQLITE_PRIVATE   int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot);
SQLITE_PRIVATE   void sqlite3PagerSnapshotUnlock(Pager *pPager);
# endif
#endif

#if !defined(SQLITE_OMIT_WAL) && defined(SQLITE_ENABLE_SETLK_TIMEOUT)
SQLITE_PRIVATE   int sqlite3PagerWalWriteLock(Pager*, int);
SQLITE_PRIVATE   void sqlite3PagerWalDb(Pager*, sqlite3*);
#else
# define sqlite3PagerWalWriteLock(y,z) SQLITE_OK
# define sqlite3PagerWalDb(x,y)
#endif

#ifdef SQLITE_DIRECT_OVERFLOW_READ
SQLITE_PRIVATE   int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno);
#endif

#ifdef SQLITE_ENABLE_ZIPVFS
SQLITE_PRIVATE   int sqlite3PagerWalFramesize(Pager *pPager);
15915
15916
15917
15918
15919
15920
15921
15922
15923
15924
15925
15926
15927
15928
15929
15930
15931
15932
15933
15958
15959
15960
15961
15962
15963
15964





15965
15966
15967
15968
15969
15970
15971







-
-
-
-
-







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 *);
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
SQLITE_PRIVATE void sqlite3PagerResetLockTimeout(Pager *pPager);
#else
# define sqlite3PagerResetLockTimeout(X)
#endif

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

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

/* Functions to support testing and debugging. */
16853
16854
16855
16856
16857
16858
16859

16860
16861
16862
16863
16864
16865
16866
16891
16892
16893
16894
16895
16896
16897
16898
16899
16900
16901
16902
16903
16904
16905







+







  VTable *pDisconnect;          /* Disconnect these in next sqlite3_prepare() */
#endif
  Hash aFunc;                   /* Hash table of connection functions */
  Hash aCollSeq;                /* All collating sequences */
  BusyHandler busyHandler;      /* Busy callback */
  Db aDbStatic[2];              /* Static space for the 2 default backends */
  Savepoint *pSavepoint;        /* List of active savepoints */
  int nAnalysisLimit;           /* Number of index rows to ANALYZE */
  int busyTimeout;              /* Busy handler timeout, in msec */
  int nSavepoint;               /* Number of non-transaction savepoints */
  int nStatement;               /* Number of nested statement-transactions  */
  i64 nDeferredCons;            /* Net deferred constraints this transaction. */
  i64 nDeferredImmCons;         /* Net deferred immediate constraints */
  int *pnBytesFreed;            /* If not NULL, increment this in DbFree() */
#ifdef SQLITE_ENABLE_UNLOCK_NOTIFY
19898
19899
19900
19901
19902
19903
19904
19905

19906
19907
19908
19909
19910
19911
19912
19937
19938
19939
19940
19941
19942
19943

19944
19945
19946
19947
19948
19949
19950
19951







-
+







SQLITE_PRIVATE void *sqlite3RenameTokenMap(Parse*, void*, Token*);
SQLITE_PRIVATE void sqlite3RenameTokenRemap(Parse*, void *pTo, void *pFrom);
SQLITE_PRIVATE void sqlite3RenameExprUnmap(Parse*, Expr*);
SQLITE_PRIVATE void sqlite3RenameExprlistUnmap(Parse*, ExprList*);
SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*);
SQLITE_PRIVATE char sqlite3AffinityType(const char*, Column*);
SQLITE_PRIVATE void sqlite3Analyze(Parse*, Token*, Token*);
SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler*, sqlite3_file*);
SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler*);
SQLITE_PRIVATE int sqlite3FindDb(sqlite3*, Token*);
SQLITE_PRIVATE int sqlite3FindDbName(sqlite3 *, const char *);
SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3*,int iDB);
SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3*,Index*);
SQLITE_PRIVATE void sqlite3DefaultRowEst(Index*);
SQLITE_PRIVATE void sqlite3RegisterLikeFunctions(sqlite3*, int);
SQLITE_PRIVATE int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*);
20634
20635
20636
20637
20638
20639
20640
20641


20642
20643
20644
20645
20646
20647
20648
20673
20674
20675
20676
20677
20678
20679

20680
20681
20682
20683
20684
20685
20686
20687
20688







-
+
+







#endif

/*
** VDBE_DISPLAY_P4 is true or false depending on whether or not the
** "explain" P4 display logic is enabled.
*/
#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \
     || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
     || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) \
     || defined(SQLITE_ENABLE_BYTECODE_VTAB)
# define VDBE_DISPLAY_P4 1
#else
# define VDBE_DISPLAY_P4 0
#endif

/*
** SQL is translated into a sequence of instructions to be
21099
21100
21101
21102
21103
21104
21105







21106

21107
21108
21109
21110
21111
21112
21113
21139
21140
21141
21142
21143
21144
21145
21146
21147
21148
21149
21150
21151
21152

21153
21154
21155
21156
21157
21158
21159
21160







+
+
+
+
+
+
+
-
+







SQLITE_PRIVATE u32 sqlite3VdbeSerialGet(const unsigned char*, u32, Mem*);
SQLITE_PRIVATE void sqlite3VdbeDeleteAuxData(sqlite3*, AuxData**, int, int);

int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *);
SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(sqlite3*,VdbeCursor*,UnpackedRecord*,int*);
SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3*, BtCursor*, i64*);
SQLITE_PRIVATE int sqlite3VdbeExec(Vdbe*);
#if !defined(SQLITE_OMIT_EXPLAIN) || defined(SQLITE_ENABLE_BYTECODE_VTAB)
SQLITE_PRIVATE int sqlite3VdbeNextOpcode(Vdbe*,Mem*,int,int*,int*,Op**);
SQLITE_PRIVATE char *sqlite3VdbeDisplayP4(sqlite3*,Op*);
#endif
#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS)
SQLITE_PRIVATE char *sqlite3VdbeDisplayComment(sqlite3*,const Op*,const char*);
#endif
#ifndef SQLITE_OMIT_EXPLAIN
#if !defined(SQLITE_OMIT_EXPLAIN)
SQLITE_PRIVATE int sqlite3VdbeList(Vdbe*);
#endif
SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe*);
SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *, int);
SQLITE_PRIVATE int sqlite3VdbeMemTooBig(Mem*);
SQLITE_PRIVATE int sqlite3VdbeMemCopy(Mem*, const Mem*);
SQLITE_PRIVATE void sqlite3VdbeMemShallowCopy(Mem*, const Mem*, int);
21141
21142
21143
21144
21145
21146
21147
21148

21149
21150
21151
21152
21153
21154
21155
21188
21189
21190
21191
21192
21193
21194

21195
21196
21197
21198
21199
21200
21201
21202







-
+







SQLITE_PRIVATE int sqlite3VdbeMemFromBtree(BtCursor*,u32,u32,Mem*);
SQLITE_PRIVATE int sqlite3VdbeMemFromBtreeZeroOffset(BtCursor*,u32,Mem*);
SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p);
SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
#ifndef SQLITE_OMIT_WINDOWFUNC
SQLITE_PRIVATE int sqlite3VdbeMemAggValue(Mem*, Mem*, FuncDef*);
#endif
#ifndef SQLITE_OMIT_EXPLAIN
#if !defined(SQLITE_OMIT_EXPLAIN) || defined(SQLITE_ENABLE_BYTECODE_VTAB)
SQLITE_PRIVATE const char *sqlite3OpcodeName(int);
#endif
SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int n);
SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int);
#ifdef SQLITE_DEBUG
SQLITE_PRIVATE int sqlite3VdbeFrameIsValid(VdbeFrame*);
27314
27315
27316
27317
27318
27319
27320
27321

27322
27323
27324
27325
27326
27327
27328
27361
27362
27363
27364
27365
27366
27367

27368
27369
27370
27371
27372
27373
27374
27375







-
+







    return priorLimit;
  }
  if( mem0.hardLimit>0 && (n>mem0.hardLimit || n==0) ){
    n = mem0.hardLimit;
  }
  mem0.alarmThreshold = n;
  nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
  mem0.nearlyFull = (n>0 && n<=nUsed);
  AtomicStore(&mem0.nearlyFull, n>0 && n<=nUsed);
  sqlite3_mutex_leave(mem0.mutex);
  excess = sqlite3_memory_used() - n;
  if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff));
  return priorLimit;
}
SQLITE_API void sqlite3_soft_heap_limit(int n){
  if( n<0 ) n = 0;
27382
27383
27384
27385
27386
27387
27388
27389

27390
27391
27392
27393
27394
27395
27396
27429
27430
27431
27432
27433
27434
27435

27436
27437
27438
27439
27440
27441
27442
27443







-
+








/*
** Return true if the heap is currently under memory pressure - in other
** words if the amount of heap used is close to the limit set by
** sqlite3_soft_heap_limit().
*/
SQLITE_PRIVATE int sqlite3HeapNearlyFull(void){
  return mem0.nearlyFull;
  return AtomicLoad(&mem0.nearlyFull);
}

/*
** Deinitialize the memory allocation subsystem.
*/
SQLITE_PRIVATE void sqlite3MallocEnd(void){
  if( sqlite3GlobalConfig.m.xShutdown ){
27446
27447
27448
27449
27450
27451
27452
27453

27454
27455
27456
27457
27458
27459
27460
27461
27462
27463

27464
27465
27466
27467
27468
27469
27470
27493
27494
27495
27496
27497
27498
27499

27500
27501
27502
27503
27504
27505
27506
27507
27508
27509

27510
27511
27512
27513
27514
27515
27516
27517







-
+









-
+







  ** following xRoundup() call. */
  nFull = sqlite3GlobalConfig.m.xRoundup(n);

  sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, n);
  if( mem0.alarmThreshold>0 ){
    sqlite3_int64 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
    if( nUsed >= mem0.alarmThreshold - nFull ){
      mem0.nearlyFull = 1;
      AtomicStore(&mem0.nearlyFull, 1);
      sqlite3MallocAlarm(nFull);
      if( mem0.hardLimit ){
        nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
        if( nUsed >= mem0.hardLimit - nFull ){
          *pp = 0;
          return;
        }
      }
    }else{
      mem0.nearlyFull = 0;
      AtomicStore(&mem0.nearlyFull, 0);
    }
  }
  p = sqlite3GlobalConfig.m.xMalloc(nFull);
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
  if( p==0 && mem0.alarmThreshold>0 ){
    sqlite3MallocAlarm(nFull);
    p = sqlite3GlobalConfig.m.xMalloc(nFull);
27685
27686
27687
27688
27689
27690
27691

27692
27693
27694
27695

27696
27697
27698
27699
27700
27701
27702
27732
27733
27734
27735
27736
27737
27738
27739
27740
27741
27742
27743
27744
27745
27746
27747
27748
27749
27750
27751







+




+







    sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, (int)nBytes);
    nDiff = nNew - nOld;
    if( nDiff>0 && sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >= 
          mem0.alarmThreshold-nDiff ){
      sqlite3MallocAlarm(nDiff);
    }
    pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
    if( pNew==0 && mem0.alarmThreshold>0 ){
      sqlite3MallocAlarm((int)nBytes);
      pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
    }
#endif
    if( pNew ){
      nNew = sqlite3MallocSize(pNew);
      sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nNew-nOld);
    }
    sqlite3_mutex_leave(mem0.mutex);
  }else{
    pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
34926
34927
34928
34929
34930
34931
34932

34933
34934

34935
34936
34937
34938
34939
34940
34941
34942

34943
34944
34945
34946
34947
34948
34949
34975
34976
34977
34978
34979
34980
34981
34982
34983

34984
34985
34986
34987
34988
34989
34990
34991

34992
34993
34994
34995
34996
34997
34998
34999







+

-
+







-
+







# define osSetPosixAdvisoryLock(h,x,t) osFcntl(h,F_SETLK,x)
#else
static int osSetPosixAdvisoryLock(
  int h,                /* The file descriptor on which to take the lock */
  struct flock *pLock,  /* The description of the lock */
  unixFile *pFile       /* Structure holding timeout value */
){
  int tm = pFile->iBusyTimeout;
  int rc = osFcntl(h,F_SETLK,pLock);
  while( rc<0 && pFile->iBusyTimeout>0 ){
  while( rc<0 && tm>0 ){
    /* On systems that support some kind of blocking file lock with a timeout,
    ** make appropriate changes here to invoke that blocking file lock.  On
    ** generic posix, however, there is no such API.  So we simply try the
    ** lock once every millisecond until either the timeout expires, or until
    ** the lock is obtained. */
    usleep(1000);
    rc = osFcntl(h,F_SETLK,pLock);
    pFile->iBusyTimeout--;
    tm--;
  }
  return rc;
}
#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */


/*
37677
37678
37679
37680
37681
37682
37683

37684
37685
37686
37687
37688
37689
37690








37691
37692
37693
37694
37695
37696
37697
37727
37728
37729
37730
37731
37732
37733
37734
37735
37736
37737
37738
37739


37740
37741
37742
37743
37744
37745
37746
37747
37748
37749
37750
37751
37752
37753
37754







+





-
-
+
+
+
+
+
+
+
+







  /* 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;
    rc = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile);
    rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY;
    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);
38180
38181
38182
38183
38184
38185
38186
38187
38188


38189
38190
38191

38192
38193
38194
38195
38196
38197
38198
38199
38200
38201
38202






38203
38204
38205
38206
38207
38208
38209
38237
38238
38239
38240
38241
38242
38243


38244
38245
38246
38247

38248
38249

38250
38251
38252
38253
38254




38255
38256
38257
38258
38259
38260
38261
38262
38263
38264
38265
38266
38267







-
-
+
+


-
+

-





-
-
-
-
+
+
+
+
+
+







       || flags==(SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE)
       || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED)
       || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
  assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 );
  assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 );
  assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 );

  /* Check that, if this to be a blocking lock, that locks have been
  ** obtained in the following order.
  /* Check that, if this to be a blocking lock, no locks that occur later
  ** in the following list than the lock being obtained are already held:
  **
  **   1. Checkpointer lock (ofst==1).
  **   2. Recover lock (ofst==2).
  **   2. Write lock (ofst==0).
  **   3. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK).
  **   4. Write lock (ofst==0).
  **
  ** 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.  */
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  assert( pDbFd->iBusyTimeout==0 
       || (flags & SQLITE_SHM_UNLOCK) || ofst==0
       || ((p->exclMask|p->sharedMask)&~((1<<ofst)-2))==0
  );
  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);
  if( flags & SQLITE_SHM_UNLOCK ){
    u16 allMask = 0; /* Mask of locks held by siblings */
51503
51504
51505
51506
51507
51508
51509





51510
51511
51512
51513
51514
51515
51516
51561
51562
51563
51564
51565
51566
51567
51568
51569
51570
51571
51572
51573
51574
51575
51576
51577
51578
51579







+
+
+
+
+







** stored in each frame (i.e. the db page-size when the WAL was created).
*/
SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal);
#endif

/* Return the sqlite3_file object for the WAL file */
SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal);

#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
SQLITE_PRIVATE int sqlite3WalWriteLock(Wal *pWal, int bLock);
SQLITE_PRIVATE void sqlite3WalDb(Wal *pWal, sqlite3 *db);
#endif

#endif /* ifndef SQLITE_OMIT_WAL */
#endif /* SQLITE_WAL_H */

/************** End of wal.h *************************************************/
/************** Continuing where we left off in pager.c **********************/

54024
54025
54026
54027
54028
54029
54030



54031
54032
54033

54034
54035
54036
54037
54038
54039
54040
54087
54088
54089
54090
54091
54092
54093
54094
54095
54096
54097
54098

54099
54100
54101
54102
54103
54104
54105
54106







+
+
+


-
+







    if( rc!=SQLITE_OK ){
      goto delmaster_out;
    }
    if( exists ){
      /* One of the journals pointed to by the master journal exists.
      ** Open it and check if it points at the master journal. If
      ** so, return without deleting the master journal file.
      ** NB:  zJournal is really a MAIN_JOURNAL.  But call it a 
      ** MASTER_JOURNAL here so that the VFS will not send the zJournal
      ** name into sqlite3_database_file_object().
      */
      int c;
      int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL);
      int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MASTER_JOURNAL);
      rc = sqlite3OsOpen(pVfs, zJournal, pJournal, flags, 0);
      if( rc!=SQLITE_OK ){
        goto delmaster_out;
      }

      rc = readMasterJournal(pJournal, zMasterPtr, nMasterPtr);
      sqlite3OsClose(pJournal);
56230
56231
56232
56233
56234
56235
56236

56237
56238
56239
56240
56241
56242
56243
56296
56297
56298
56299
56300
56301
56302
56303
56304
56305
56306
56307
56308
56309
56310







+







  ** file name. The layout in memory is as follows:
  **
  **     Pager object                    (sizeof(Pager) bytes)
  **     PCache object                   (sqlite3PcacheSize() bytes)
  **     Database file handle            (pVfs->szOsFile bytes)
  **     Sub-journal file handle         (journalFileSize bytes)
  **     Main journal file handle        (journalFileSize bytes)
  **     Ptr back to the Pager           (sizeof(Pager*) bytes)
  **     \0\0\0\0 database prefix        (4 bytes)
  **     Database file name              (nPathname+1 bytes)
  **     URI query parameters            (nUriByte bytes)
  **     Journal filename                (nPathname+8+1 bytes)
  **     WAL filename                    (nPathname+4+1 bytes)
  **     \0\0\0 terminator               (3 bytes)
  **
56269
56270
56271
56272
56273
56274
56275

56276
56277
56278
56279
56280
56281
56282
56283
56284
56285
56286
56287
56288
56289
56290
56291
56292
56293
56294
56295

56296
56297
56298
56299
56300
56301
56302
56336
56337
56338
56339
56340
56341
56342
56343
56344
56345
56346
56347
56348
56349
56350
56351
56352
56353
56354
56355
56356
56357
56358
56359
56360
56361
56362
56363
56364
56365
56366
56367
56368
56369
56370
56371







+




















+







  ** changes here, be sure to change it there as well.
  */
  pPtr = (u8 *)sqlite3MallocZero(
    ROUND8(sizeof(*pPager)) +            /* Pager structure */
    ROUND8(pcacheSize) +                 /* PCache object */
    ROUND8(pVfs->szOsFile) +             /* The main db file */
    journalFileSize * 2 +                /* The two journal files */
    sizeof(pPager) +                     /* Space to hold a pointer */
    4 +                                  /* Database prefix */
    nPathname + 1 +                      /* database filename */
    nUriByte +                           /* query parameters */
    nPathname + 8 + 1 +                  /* Journal filename */
#ifndef SQLITE_OMIT_WAL
    nPathname + 4 + 1 +                  /* WAL filename */
#endif
    3                                    /* Terminator */
  );
  assert( EIGHT_BYTE_ALIGNMENT(SQLITE_INT_TO_PTR(journalFileSize)) );
  if( !pPtr ){
    sqlite3DbFree(0, zPathname);
    return SQLITE_NOMEM_BKPT;
  }
  pPager = (Pager*)pPtr;                  pPtr += ROUND8(sizeof(*pPager));
  pPager->pPCache = (PCache*)pPtr;        pPtr += ROUND8(pcacheSize);
  pPager->fd = (sqlite3_file*)pPtr;       pPtr += ROUND8(pVfs->szOsFile);
  pPager->sjfd = (sqlite3_file*)pPtr;     pPtr += journalFileSize;
  pPager->jfd =  (sqlite3_file*)pPtr;     pPtr += journalFileSize;
  assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) );
  memcpy(pPtr, &pPager, sizeof(pPager));  pPtr += sizeof(pPager);

  /* Fill in the Pager.zFilename and pPager.zQueryParam fields */
                                          pPtr += 4;  /* Skip zero prefix */
  pPager->zFilename = (char*)pPtr;
  if( nPathname>0 ){
    memcpy(pPtr, zPathname, nPathname);   pPtr += nPathname + 1;
    if( zUri ){
56489
56490
56491
56492
56493
56494
56495













56496
56497
56498
56499
56500
56501
56502
56558
56559
56560
56561
56562
56563
56564
56565
56566
56567
56568
56569
56570
56571
56572
56573
56574
56575
56576
56577
56578
56579
56580
56581
56582
56583
56584







+
+
+
+
+
+
+
+
+
+
+
+
+







  /* memset(pPager->aHash, 0, sizeof(pPager->aHash)); */
  /* pPager->szMmap = SQLITE_DEFAULT_MMAP_SIZE // will be set by btree.c */

  *ppPager = pPager;
  return SQLITE_OK;
}

/*
** Return the sqlite3_file for the main database given the name
** of the corresonding WAL or Journal name as passed into
** xOpen.
*/
SQLITE_API sqlite3_file *sqlite3_database_file_object(const char *zName){
  Pager *pPager;
  while( zName[-1]!=0 || zName[-2]!=0 || zName[-3]!=0 || zName[-4]!=0 ){
    zName--;
  }
  pPager = *(Pager**)(zName - 4 - sizeof(Pager*));
  return pPager->fd;
}


/*
** This function is called after transitioning from PAGER_UNLOCK to
** PAGER_SHARED state. It tests if there is a hot journal present in
** the file-system for the given pager. A hot journal is one that 
** needs to be played back. According to this function, a hot-journal
57174
57175
57176
57177
57178
57179
57180
57181
57182
57183
57184
57185
57186
57187
57188
57256
57257
57258
57259
57260
57261
57262

57263
57264
57265
57266
57267
57268
57269







-







}
SQLITE_PRIVATE void sqlite3PagerUnrefPageOne(DbPage *pPg){
  Pager *pPager;
  assert( pPg!=0 );
  assert( pPg->pgno==1 );
  assert( (pPg->flags & PGHDR_MMAP)==0 ); /* Page1 is never memory mapped */
  pPager = pPg->pPager;
  sqlite3PagerResetLockTimeout(pPager);
  sqlite3PcacheRelease(pPg);
  pagerUnlockIfUnused(pPager);
}

/*
** This function is called at the start of every write transaction.
** There must already be a RESERVED or EXCLUSIVE lock on the database 
58467
58468
58469
58470
58471
58472
58473
58474
58475
58476
58477
58478
58479
58480
58481
58482
58483
58484
58485
58486
58487
58488
58489
58490
58548
58549
58550
58551
58552
58553
58554










58555
58556
58557
58558
58559
58560
58561







-
-
-
-
-
-
-
-
-
-







** with the pager.  This might return NULL if the file has
** not yet been opened.
*/
SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager *pPager){
  return pPager->fd;
}

#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
/*
** Reset the lock timeout for pager.
*/
SQLITE_PRIVATE void sqlite3PagerResetLockTimeout(Pager *pPager){
  int x = 0;
  sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCK_TIMEOUT, &x);
}
#endif

/*
** Return the file handle for the journal file (if it exists).
** This will be either the rollback journal or the WAL file.
*/
SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager *pPager){
#if SQLITE_OMIT_WAL
  return pPager->jfd;
58890
58891
58892
58893
58894
58895
58896
58897
58898
58899
58900
58901
58902
58903
58904
58961
58962
58963
58964
58965
58966
58967

58968
58969
58970
58971
58972
58973
58974







-







  if( pPager->pWal ){
    rc = sqlite3WalCheckpoint(pPager->pWal, db, eMode,
        (eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler),
        pPager->pBusyHandlerArg,
        pPager->walSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace,
        pnLog, pnCkpt
    );
    sqlite3PagerResetLockTimeout(pPager);
  }
  return rc;
}

SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager){
  return sqlite3WalCallback(pPager->pWal);
}
59055
59056
59057
59058
59059
59060
59061











59062














59063
59064
59065
59066
59067
59068
59069
59070
59071
59072
59073
59074
59075
59076
59077
59078
59079
59080
59081
59082




59083
59084
59085
59086
59087
59088
59089
59125
59126
59127
59128
59129
59130
59131
59132
59133
59134
59135
59136
59137
59138
59139
59140
59141
59142

59143
59144
59145
59146
59147
59148
59149
59150
59151
59152
59153
59154
59155
59156
59157
59158
59159
59160
59161
59162
59163
59164
59165
59166
59167
59168
59169
59170
59171
59172
59173
59174
59175

59176
59177
59178
59179
59180
59181
59182
59183
59184
59185
59186







+
+
+
+
+
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+



















-
+
+
+
+







      pagerFixMaplimit(pPager);
      if( rc && !pPager->exclusiveMode ) pagerUnlockDb(pPager, SHARED_LOCK);
    }
  }
  return rc;
}

#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
/*
** If pager pPager is a wal-mode database not in exclusive locking mode,
** invoke the sqlite3WalWriteLock() function on the associated Wal object 
** with the same db and bLock parameters as were passed to this function.
** Return an SQLite error code if an error occurs, or SQLITE_OK otherwise.
*/
SQLITE_PRIVATE int sqlite3PagerWalWriteLock(Pager *pPager, int bLock){
  int rc = SQLITE_OK;
  if( pagerUseWal(pPager) && pPager->exclusiveMode==0 ){
    rc = sqlite3WalWriteLock(pPager->pWal, bLock);

  }
  return rc;
}

/*
** Set the database handle used by the wal layer to determine if 
** blocking locks are required.
*/
SQLITE_PRIVATE void sqlite3PagerWalDb(Pager *pPager, sqlite3 *db){
  if( pagerUseWal(pPager) ){
    sqlite3WalDb(pPager->pWal, db);
  }
}
#endif

#ifdef SQLITE_ENABLE_SNAPSHOT
/*
** If this is a WAL database, obtain a snapshot handle for the snapshot
** currently open. Otherwise, return an error.
*/
SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot){
  int rc = SQLITE_ERROR;
  if( pPager->pWal ){
    rc = sqlite3WalSnapshotGet(pPager->pWal, ppSnapshot);
  }
  return rc;
}

/*
** If this is a WAL database, store a pointer to pSnapshot. Next time a
** read transaction is opened, attempt to read from the snapshot it 
** identifies. If this is not a WAL database, return an error.
*/
SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot){
SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(
  Pager *pPager, 
  sqlite3_snapshot *pSnapshot
){
  int rc = SQLITE_OK;
  if( pPager->pWal ){
    sqlite3WalSnapshotOpen(pPager->pWal, pSnapshot);
  }else{
    rc = SQLITE_ERROR;
  }
  return rc;
59620
59621
59622
59623
59624
59625
59626



59627
59628
59629
59630
59631
59632
59633
59717
59718
59719
59720
59721
59722
59723
59724
59725
59726
59727
59728
59729
59730
59731
59732
59733







+
+
+







  u32 nCkpt;                 /* Checkpoint sequence counter in the wal-header */
#ifdef SQLITE_DEBUG
  u8 lockError;              /* True if a locking error has occurred */
#endif
#ifdef SQLITE_ENABLE_SNAPSHOT
  WalIndexHdr *pSnapshot;    /* Start transaction here if not NULL */
#endif
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  sqlite3 *db;
#endif
};

/*
** Candidate values for Wal.exclusiveMode.
*/
#define WAL_NORMAL_MODE     0
#define WAL_EXCLUSIVE_MODE  1     
59993
59994
59995
59996
59997
59998
59999
60000

60001
60002
60003
60004
60005
60006
60007
60008
60009
60010
60011
60012
60013
60014
60015
60016

60017
60018
60019
60020
60021
60022
60023
60093
60094
60095
60096
60097
60098
60099

60100
60101
60102
60103
60104
60105
60106
60107
60108
60109
60110
60111
60112
60113
60114
60115

60116
60117
60118
60119
60120
60121
60122
60123







-
+















-
+







static int walLockShared(Wal *pWal, int lockIdx){
  int rc;
  if( pWal->exclusiveMode ) return SQLITE_OK;
  rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1,
                        SQLITE_SHM_LOCK | SQLITE_SHM_SHARED);
  WALTRACE(("WAL%p: acquire SHARED-%s %s\n", pWal,
            walLockName(lockIdx), rc ? "failed" : "ok"));
  VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && rc!=SQLITE_BUSY); )
  VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); )
  return rc;
}
static void walUnlockShared(Wal *pWal, int lockIdx){
  if( pWal->exclusiveMode ) return;
  (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1,
                         SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED);
  WALTRACE(("WAL%p: release SHARED-%s\n", pWal, walLockName(lockIdx)));
}
static int walLockExclusive(Wal *pWal, int lockIdx, int n){
  int rc;
  if( pWal->exclusiveMode ) return SQLITE_OK;
  rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, n,
                        SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE);
  WALTRACE(("WAL%p: acquire EXCLUSIVE-%s cnt=%d %s\n", pWal,
            walLockName(lockIdx), n, rc ? "failed" : "ok"));
  VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && rc!=SQLITE_BUSY); )
  VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); )
  return rc;
}
static void walUnlockExclusive(Wal *pWal, int lockIdx, int n){
  if( pWal->exclusiveMode ) return;
  (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, n,
                         SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE);
  WALTRACE(("WAL%p: release EXCLUSIVE-%s cnt=%d\n", pWal,
60281
60282
60283
60284
60285
60286
60287
60288
60289
60290
60291
60292
60293
60294
60295
60296
60297
60298
60299
60381
60382
60383
60384
60385
60386
60387





60388
60389
60390
60391
60392
60393
60394







-
-
-
-
-







*/
static int walIndexRecover(Wal *pWal){
  int rc;                         /* Return Code */
  i64 nSize;                      /* Size of log file */
  u32 aFrameCksum[2] = {0, 0};
  int iLock;                      /* Lock offset to lock for checkpoint */

#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  int tmout = 0;
  sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout);
#endif

  /* Obtain an exclusive lock on all byte in the locking range not already
  ** locked by the caller. The caller is guaranteed to have locked the
  ** WAL_WRITE_LOCK byte, and may have also locked the WAL_CKPT_LOCK byte.
  ** If successful, the same bytes that are locked here are unlocked before
  ** this function returns.
  */
  assert( pWal->ckptLock==1 || pWal->ckptLock==0 );
60833
60834
60835
60836
60837
60838
60839



















































































60840
60841
60842
60843
60844
60845
60846
60847
60848
60849
60850
60851
60852
60853
60854
60855
60856
60857






60858
60859
60860
60861
60862
60863
60864
60928
60929
60930
60931
60932
60933
60934
60935
60936
60937
60938
60939
60940
60941
60942
60943
60944
60945
60946
60947
60948
60949
60950
60951
60952
60953
60954
60955
60956
60957
60958
60959
60960
60961
60962
60963
60964
60965
60966
60967
60968
60969
60970
60971
60972
60973
60974
60975
60976
60977
60978
60979
60980
60981
60982
60983
60984
60985
60986
60987
60988
60989
60990
60991
60992
60993
60994
60995
60996
60997
60998
60999
61000
61001
61002
61003
61004
61005
61006
61007
61008
61009
61010
61011
61012
61013
61014
61015
61016
61017
61018
61019
61020
61021
61022
61023
61024
61025
61026
61027
61028
61029
61030
61031
61032
61033
61034
61035
61036
61037
61038
61039
61040
61041
61042
61043
61044
61045
61046
61047
61048







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


















+
+
+
+
+
+







  if( rc!=SQLITE_OK ){
    walIteratorFree(p);
    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.
*/
static void walDisableBlocking(Wal *pWal){
  int tmout = 0;
  sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout);
}

/*
** If parameter bLock is true, attempt to enable blocking locks, take
** the WRITER lock, and then disable blocking locks. If blocking locks
** cannot be enabled, no attempt to obtain the WRITER lock is made. Return 
** an SQLite error code if an error occurs, or SQLITE_OK otherwise. It is not
** an error if blocking locks can not be enabled.
**
** If the bLock parameter is false and the WRITER lock is held, release it.
*/
SQLITE_PRIVATE int sqlite3WalWriteLock(Wal *pWal, int bLock){
  int rc = SQLITE_OK;
  assert( pWal->readLock<0 || bLock==0 );
  if( bLock ){
    assert( pWal->db );
    if( walEnableBlocking(pWal) ){
      rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1);
      if( rc==SQLITE_OK ){
        pWal->writeLock = 1;
      }
      walDisableBlocking(pWal);
    }
  }else if( pWal->writeLock ){
    walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
    pWal->writeLock = 0;
  }
  return rc;
}

/*
** 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
** busy-handler function. Invoke it and retry the lock until either the
** lock is successfully obtained or the busy-handler returns 0.
*/
static int walBusyLock(
  Wal *pWal,                      /* WAL connection */
  int (*xBusy)(void*),            /* Function to call when busy */
  void *pBusyArg,                 /* Context argument for xBusyHandler */
  int lockIdx,                    /* Offset of first byte to lock */
  int n                           /* Number of bytes to lock */
){
  int rc;
  do {
    rc = walLockExclusive(pWal, lockIdx, n);
  }while( xBusy && rc==SQLITE_BUSY && xBusy(pBusyArg) );
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  if( rc==SQLITE_BUSY_TIMEOUT ){
    walDisableBlocking(pWal);
    rc = SQLITE_BUSY;
  }
#endif
  return rc;
}

/*
** The cache of the wal-index header must be valid to call this function.
** Return the page-size in bytes used by the database.
*/
61021
61022
61023
61024
61025
61026
61027

61028
61029
61030
61031
61032
61033
61034
61205
61206
61207
61208
61209
61210
61211
61212
61213
61214
61215
61216
61217
61218
61219







+








      /* If the database may grow as a result of this checkpoint, hint
      ** about the eventual size of the db file to the VFS layer.
      */
      if( rc==SQLITE_OK ){
        i64 nReq = ((i64)mxPage * szPage);
        i64 nSize;                    /* Current size of database file */
        sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_START, 0);
        rc = sqlite3OsFileSize(pWal->pDbFd, &nSize);
        if( rc==SQLITE_OK && nSize<nReq ){
          sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq);
        }
      }


61048
61049
61050
61051
61052
61053
61054

61055
61056
61057
61058
61059
61060
61061
61062
61063
61064
61065
61066
61067
61068
61069
61070
61071
61072
61073
61074
61075
61076
61233
61234
61235
61236
61237
61238
61239
61240
61241
61242
61243
61244
61245
61246
61247
61248
61249
61250
61251




61252
61253
61254
61255
61256
61257
61258







+











-
-
-
-







        rc = sqlite3OsRead(pWal->pWalFd, zBuf, szPage, iOffset);
        if( rc!=SQLITE_OK ) break;
        iOffset = (iDbpage-1)*(i64)szPage;
        testcase( IS_BIG_INT(iOffset) );
        rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset);
        if( rc!=SQLITE_OK ) break;
      }
      sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_DONE, 0);

      /* If work was actually accomplished... */
      if( rc==SQLITE_OK ){
        if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){
          i64 szDb = pWal->hdr.nPage*(i64)szPage;
          testcase( IS_BIG_INT(szDb) );
          rc = sqlite3OsTruncate(pWal->pDbFd, szDb);
          if( rc==SQLITE_OK ){
            rc = sqlite3OsSync(pWal->pDbFd, CKPT_SYNC_FLAGS(sync_flags));
          }
        }
        if( rc==SQLITE_OK ){
          rc = sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_DONE, 0);
          if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
        }
        if( rc==SQLITE_OK ){
          pInfo->nBackfill = mxSafeFrame;
        }
      }

      /* Release the reader lock held while backfilling */
      walUnlockExclusive(pWal, WAL_READ_LOCK(0), 1);
61332
61333
61334
61335
61336
61337
61338
61339
61340
61341
61342
61343
61344
61345


61346
61347
61348
61349
61350
61351
61352
61353
61354
61355
61356
61357
61358
61359
61360


















61361
61362
61363
61364
61365
61366
61367
61514
61515
61516
61517
61518
61519
61520

61521
61522
61523
61524
61525
61526
61527
61528















61529
61530
61531
61532
61533
61534
61535
61536
61537
61538
61539
61540
61541
61542
61543
61544
61545
61546
61547
61548
61549
61550
61551
61552
61553







-






+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  ** being modified by another thread or process.
  */
  badHdr = (page0 ? walIndexTryHdr(pWal, pChanged) : 1);

  /* If the first attempt failed, it might have been due to a race
  ** with a writer.  So get a WRITE lock and try again.
  */
  assert( badHdr==0 || pWal->writeLock==0 );
  if( badHdr ){
    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;
    }else if( 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.
          */
          rc = walIndexRecover(pWal);
          *pChanged = 1;
        }
      }
      pWal->writeLock = 0;
      walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
      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);
        }
      }
    }
  }

  /* If the header is read successfully, check the version number to make
  ** sure the wal-index was not constructed with some future format that
  ** this version of SQLite cannot understand.
  */
61905
61906
61907
61908
61909
61910
61911
61912
61913


61914
61915
61916
61917
61918

61919
61920
61921
61922















61923
61924

61925
61926
61927




61928
61929
61930
61931
61932
61933
61934
61935
61936
61937
61938
61939
61940
61941
61942
61943
61944
61945
61946
61947
61948
61949
61950
61951
61952
61953
61954
62091
62092
62093
62094
62095
62096
62097


62098
62099

62100
62101
62102
62103
62104




62105
62106
62107
62108
62109
62110
62111
62112
62113
62114
62115
62116
62117
62118
62119
62120

62121



62122
62123
62124
62125
62126
62127
62128
62129
62130
62131
62132
62133
62134
62135










62136
62137
62138
62139
62140
62141
62142







-
-
+
+
-




+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+
-
-
-
+
+
+
+










-
-
-
-
-
-
-
-
-
-







** transaction, then *pChanged is set to 1 before returning.  The
** Pager layer will use this to know that its cache is stale and
** needs to be flushed.
*/
SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
  int rc;                         /* Return code */
  int cnt = 0;                    /* Number of TryBeginRead attempts */
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  int tmout = 0;

  assert( pWal->ckptLock==0 );
#endif

#ifdef SQLITE_ENABLE_SNAPSHOT
  int bChanged = 0;
  WalIndexHdr *pSnapshot = pWal->pSnapshot;
  if( pSnapshot ){
  if( pSnapshot && memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){
    bChanged = 1;
  }
#endif
    if( memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){
      bChanged = 1;
    }

    /* It is possible that there is a checkpointer thread running 
    ** concurrent with this code. If this is the case, it may be that the
    ** checkpointer has already determined that it will checkpoint 
    ** snapshot X, where X is later in the wal file than pSnapshot, but 
    ** has not yet set the pInfo->nBackfillAttempted variable to indicate 
    ** its intent. To avoid the race condition this leads to, ensure that
    ** there is no checkpointer process by taking a shared CKPT lock 
    ** before checking pInfo->nBackfillAttempted.  */
    (void)walEnableBlocking(pWal);
    rc = walLockShared(pWal, WAL_CKPT_LOCK);
    walDisableBlocking(pWal);

#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
    if( rc!=SQLITE_OK ){
  /* Disable blocking locks. They are not useful when trying to open a
  ** read-transaction, and blocking may cause deadlock anyway. */
  sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout);
      return rc;
    }
    pWal->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_SETLK_TIMEOUT
  /* If they were disabled earlier and the read-transaction has been
  ** successfully opened, re-enable blocking locks. This is because the
  ** connection may attempt to upgrade to a write-transaction, which does
  ** benefit from using blocking locks.  */
  if( rc==SQLITE_OK ){
    sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout);
  }
#endif

#ifdef SQLITE_ENABLE_SNAPSHOT
  if( rc==SQLITE_OK ){
    if( pSnapshot && memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){
      /* At this point the client has a lock on an aReadMark[] slot holding
      ** a value equal to or smaller than pSnapshot->mxFrame, but pWal->hdr
      ** is populated with the wal-index header corresponding to the head
      ** of the wal file. Verify that pSnapshot is still valid before
61962
61963
61964
61965
61966
61967
61968
61969
61970
61971
61972
61973
61974
61975
61976
61977
61978
61979
61980
61981
61982
61983
61984
61985
61986
61987
61988
61989
61990
61991
61992
61993
61994
61995
61996
61997
61998















61999





62000

62001
62002

62003
62004
62005
62006
62007
62008
62009
62010







62011
62012
62013
62014
62015
62016
62017
62150
62151
62152
62153
62154
62155
62156






























62157
62158
62159
62160
62161
62162
62163
62164
62165
62166
62167
62168
62169
62170
62171
62172
62173
62174
62175
62176
62177

62178


62179


62180
62181
62182
62183
62184
62185
62186
62187
62188
62189
62190
62191
62192
62193
62194
62195
62196
62197
62198
62199







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
-
+
-
-
+
-
-






+
+
+
+
+
+
+







      **         checkpoint need not have completed for this to cause problems.
      */
      volatile WalCkptInfo *pInfo = walCkptInfo(pWal);

      assert( pWal->readLock>0 || pWal->hdr.mxFrame==0 );
      assert( pInfo->aReadMark[pWal->readLock]<=pSnapshot->mxFrame );

      /* It is possible that there is a checkpointer thread running 
      ** concurrent with this code. If this is the case, it may be that the
      ** checkpointer has already determined that it will checkpoint 
      ** snapshot X, where X is later in the wal file than pSnapshot, but 
      ** has not yet set the pInfo->nBackfillAttempted variable to indicate 
      ** its intent. To avoid the race condition this leads to, ensure that
      ** there is no checkpointer process by taking a shared CKPT lock 
      ** before checking pInfo->nBackfillAttempted.  
      **
      ** TODO: Does the aReadMark[] lock prevent a checkpointer from doing
      **       this already?
      */
      rc = walLockShared(pWal, WAL_CKPT_LOCK);

      if( rc==SQLITE_OK ){
        /* Check that the wal file has not been wrapped. Assuming that it has
        ** not, also check that no checkpointer has attempted to checkpoint any
        ** frames beyond pSnapshot->mxFrame. If either of these conditions are
        ** true, return SQLITE_ERROR_SNAPSHOT. Otherwise, overwrite pWal->hdr
        ** with *pSnapshot and set *pChanged as appropriate for opening the
        ** snapshot.  */
        if( !memcmp(pSnapshot->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))
         && pSnapshot->mxFrame>=pInfo->nBackfillAttempted
        ){
          assert( pWal->readLock>0 );
          memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr));
          *pChanged = bChanged;
        }else{
          rc = SQLITE_ERROR_SNAPSHOT;
        }
      /* Check that the wal file has not been wrapped. Assuming that it has
      ** not, also check that no checkpointer has attempted to checkpoint any
      ** frames beyond pSnapshot->mxFrame. If either of these conditions are
      ** true, return SQLITE_ERROR_SNAPSHOT. Otherwise, overwrite pWal->hdr
      ** with *pSnapshot and set *pChanged as appropriate for opening the
      ** snapshot.  */
      if( !memcmp(pSnapshot->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))
       && pSnapshot->mxFrame>=pInfo->nBackfillAttempted
      ){
        assert( pWal->readLock>0 );
        memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr));
        *pChanged = bChanged;
      }else{
        rc = SQLITE_ERROR_SNAPSHOT;
      }

      /* A client using a non-current snapshot may not ignore any frames
      ** from the start of the wal file. This is because, for a system
      ** where (minFrame < iSnapshot < maxFrame), a checkpointer may
      ** have omitted to checkpoint a frame earlier than minFrame in 
      ** the file because there exists a frame after iSnapshot that
        /* Release the shared CKPT lock obtained above. */
      ** is the same database page.  */
        walUnlockShared(pWal, WAL_CKPT_LOCK);
        pWal->minFrame = 1;
      pWal->minFrame = 1;
      }


      if( rc!=SQLITE_OK ){
        sqlite3WalEndReadTransaction(pWal);
      }
    }
  }

  /* Release the shared CKPT lock obtained above. */
  if( pWal->ckptLock ){
    assert( pSnapshot );
    walUnlockShared(pWal, WAL_CKPT_LOCK);
    pWal->ckptLock = 0;
  }
#endif
  return rc;
}

/*
** Finish with a read transaction.  All this does is release the
** read-lock.
62173
62174
62175
62176
62177
62178
62179










62180
62181
62182
62183
62184
62185
62186
62355
62356
62357
62358
62359
62360
62361
62362
62363
62364
62365
62366
62367
62368
62369
62370
62371
62372
62373
62374
62375
62376
62377
62378







+
+
+
+
+
+
+
+
+
+







** thread to write as doing so would cause a fork.  So this routine
** returns SQLITE_BUSY in that case and no write transaction is started.
**
** There can only be a single writer active at a time.
*/
SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){
  int rc;

#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  /* If the write-lock is already held, then it was obtained before the
  ** read-transaction was even opened, making this call a no-op.
  ** Return early. */
  if( pWal->writeLock ){
    assert( !memcmp(&pWal->hdr,(void *)walIndexHdr(pWal),sizeof(WalIndexHdr)) );
    return SQLITE_OK;
  }
#endif

  /* Cannot start a write transaction without first holding a read
  ** transaction. */
  assert( pWal->readLock>=0 );
  assert( pWal->writeLock==0 && pWal->iReCksum==0 );

  if( pWal->readOnly ){
62749
62750
62751
62752
62753
62754
62755
62756





62757
62758







62759
62760
62761
62762
62763
62764
62765
62766
62767
62768
62769



62770
62771

62772
62773
62774
62775
62776
62777
62778
62779
62780
62781
62782
62783
62784
62785
62786
62787
62788
62789
62790
62791
62792






















62793
62794

62795

62796
62797
62798
62799
62800
62801
62802
62941
62942
62943
62944
62945
62946
62947
62948
62949
62950
62951
62952
62953
62954

62955
62956
62957
62958
62959
62960
62961
62962










62963
62964
62965


62966
62967




















62968
62969
62970
62971
62972
62973
62974
62975
62976
62977
62978
62979
62980
62981
62982
62983
62984
62985
62986
62987
62988
62989
62990
62991
62992
62993
62994
62995
62996
62997
62998
62999
63000
63001








+
+
+
+
+

-
+
+
+
+
+
+
+

-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
+

-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+


+

+








  /* 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. */
  ** "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,
  ** it will not be invoked in this case.
  */
  rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
  if( rc ){
    /* 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,
    ** it will not be invoked in this case.
    */
    testcase( rc==SQLITE_BUSY );
    testcase( xBusy!=0 );
    return rc;
  testcase( rc==SQLITE_BUSY );
  testcase( rc!=SQLITE_OK && xBusy2!=0 );
  if( rc==SQLITE_OK ){
  }
  pWal->ckptLock = 1;
    pWal->ckptLock = 1;

  /* IMPLEMENTATION-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART and
  ** TRUNCATE modes also obtain the exclusive "writer" lock on the database
  ** file.
  **
  ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained
  ** immediately, and a busy-handler is configured, it is invoked and the
  ** writer lock retried until either the busy-handler returns 0 or the
  ** lock is successfully obtained.
  */
  if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
    rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_WRITE_LOCK, 1);
    if( rc==SQLITE_OK ){
      pWal->writeLock = 1;
    }else if( rc==SQLITE_BUSY ){
      eMode2 = SQLITE_CHECKPOINT_PASSIVE;
      xBusy2 = 0;
      rc = SQLITE_OK;
    }
  }

    /* IMPLEMENTATION-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART and
    ** TRUNCATE modes also obtain the exclusive "writer" lock on the database
    ** file.
    **
    ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained
    ** immediately, and a busy-handler is configured, it is invoked and the
    ** writer lock retried until either the busy-handler returns 0 or the
    ** lock is successfully obtained.
    */
    if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
      rc = walBusyLock(pWal, xBusy2, pBusyArg, WAL_WRITE_LOCK, 1);
      if( rc==SQLITE_OK ){
        pWal->writeLock = 1;
      }else if( rc==SQLITE_BUSY ){
        eMode2 = SQLITE_CHECKPOINT_PASSIVE;
        xBusy2 = 0;
        rc = SQLITE_OK;
      }
    }
  }


  /* Read the wal-index header. */
  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 ){
62819
62820
62821
62822
62823
62824
62825



62826
62827
62828

62829
62830



62831



62832
62833
62834
62835
62836
62837
62838
63018
63019
63020
63021
63022
63023
63024
63025
63026
63027
63028
63029
63030
63031


63032
63033
63034
63035
63036
63037
63038
63039
63040
63041
63042
63043
63044
63045







+
+
+



+
-
-
+
+
+

+
+
+







    ** performed, then the pager-cache associated with pWal is now
    ** out of date. So zero the cached wal-index header to ensure that
    ** next time the pager opens a snapshot on this database it knows that
    ** the cache needs to be reset.
    */
    memset(&pWal->hdr, 0, sizeof(WalIndexHdr));
  }

  walDisableBlocking(pWal);
  sqlite3WalDb(pWal, 0);

  /* Release the locks. */
  sqlite3WalEndWriteTransaction(pWal);
  if( pWal->ckptLock ){
  walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
  pWal->ckptLock = 0;
    walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
    pWal->ckptLock = 0;
  }
  WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok"));
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY;
#endif
  return (rc==SQLITE_OK && eMode!=eMode2 ? SQLITE_BUSY : rc);
}

/* Return the value to pass to a sqlite3_wal_hook callback, the
** number of frames in the WAL at the point of the last commit since
** sqlite3WalCallback() was called.  If no commits have occurred since
** the last call, then return 0.
62941
62942
62943
62944
62945
62946
62947
62948




62949
62950
62951
62952
62953
62954
62955
63148
63149
63150
63151
63152
63153
63154

63155
63156
63157
63158
63159
63160
63161
63162
63163
63164
63165







-
+
+
+
+







  }

  return rc;
}

/* Try to open on pSnapshot when the next read-transaction starts
*/
SQLITE_PRIVATE void sqlite3WalSnapshotOpen(Wal *pWal, sqlite3_snapshot *pSnapshot){
SQLITE_PRIVATE void sqlite3WalSnapshotOpen(
  Wal *pWal, 
  sqlite3_snapshot *pSnapshot
){
  pWal->pSnapshot = (WalIndexHdr*)pSnapshot;
}

/* 
** Return a +ve value if snapshot p1 is newer than p2. A -ve value if
** p1 is older than p2 and zero if p1 and p2 are the same snapshot.
*/
63460
63461
63462
63463
63464
63465
63466
63467

63468
63469
63470
63471
63472
63473
63474
63670
63671
63672
63673
63674
63675
63676

63677
63678
63679
63680
63681
63682
63683
63684







-
+







#ifndef SQLITE_OMIT_AUTOVACUUM
  u8 autoVacuum;        /* True if auto-vacuum is enabled */
  u8 incrVacuum;        /* True if incr-vacuum is enabled */
  u8 bDoTruncate;       /* True to truncate db on commit */
#endif
  u8 inTransaction;     /* Transaction state */
  u8 max1bytePayload;   /* Maximum first byte of cell for a 1-byte payload */
  u8 nReserveWanted;    /* 1 more than desired number of extra bytes per page */
  u8 nReserveWanted;    /* Desired number of extra bytes per page */
  u16 btsFlags;         /* Boolean parameters.  See BTS_* macros below */
  u16 maxLocal;         /* Maximum local payload in non-LEAFDATA tables */
  u16 minLocal;         /* Minimum local payload in non-LEAFDATA tables */
  u16 maxLeaf;          /* Maximum local payload in a LEAFDATA table */
  u16 minLeaf;          /* Minimum local payload in a LEAFDATA table */
  u32 pageSize;         /* Total number of bytes on a page */
  u32 usableSize;       /* Number of usable bytes on each page */
66354
66355
66356
66357
66358
66359
66360
66361

66362
66363
66364
66365
66366
66367
66368
66369
66564
66565
66566
66567
66568
66569
66570

66571

66572
66573
66574
66575
66576
66577
66578







-
+
-







/*
** Invoke the busy handler for a btree.
*/
static int btreeInvokeBusyHandler(void *pArg){
  BtShared *pBt = (BtShared*)pArg;
  assert( pBt->db );
  assert( sqlite3_mutex_held(pBt->db->mutex) );
  return sqlite3InvokeBusyHandler(&pBt->db->busyHandler,
  return sqlite3InvokeBusyHandler(&pBt->db->busyHandler);
                                  sqlite3PagerFile(pBt->pPager));
}

/*
** Open a database file.
** 
** zFilename is the name of the database file.  If zFilename is NULL
** then an ephemeral database is created.  The ephemeral database might
66906
66907
66908
66909
66910
66911
66912

66913
66914

66915
66916
66917

66918


66919
66920
66921
66922
66923
66924
66925
66926
66927
66928
66929
66930
66931
66932
67115
67116
67117
67118
67119
67120
67121
67122
67123

67124
67125


67126

67127
67128
67129
67130
67131
67132



67133
67134
67135
67136
67137
67138
67139







+

-
+

-
-
+
-
+
+




-
-
-







** bytes per page is left unchanged.
**
** If the iFix!=0 then the BTS_PAGESIZE_FIXED flag is set so that the page size
** and autovacuum mode can no longer be changed.
*/
SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){
  int rc = SQLITE_OK;
  int x;
  BtShared *pBt = p->pBt;
  assert( nReserve>=-1 && nReserve<=254 );
  assert( nReserve>=0 && nReserve<=255 );
  sqlite3BtreeEnter(p);
  if( nReserve>=0 ){
    pBt->nReserveWanted = nReserve + 1;
  pBt->nReserveWanted = nReserve;
  }
  x = pBt->pageSize - pBt->usableSize;
  if( nReserve<x ) nReserve = x;
  if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){
    sqlite3BtreeLeave(p);
    return SQLITE_READONLY;
  }
  if( nReserve<0 ){
    nReserve = pBt->pageSize - pBt->usableSize;
  }
  assert( nReserve>=0 && nReserve<=255 );
  if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE &&
        ((pageSize-1)&pageSize)==0 ){
    assert( (pageSize & 7)==0 );
    assert( !pBt->pCursor );
    pBt->pageSize = (u32)pageSize;
    freeTempSpace(pBt);
66963
66964
66965
66966
66967
66968
66969




66970
66971
66972

66973
66974
66975


66976
66977

66978
66979
66980
66981
66982
66983
66984
67170
67171
67172
67173
67174
67175
67176
67177
67178
67179
67180
67181
67182

67183
67184


67185
67186
67187

67188
67189
67190
67191
67192
67193
67194
67195







+
+
+
+


-
+

-
-
+
+

-
+







  return n;
}

/*
** Return the number of bytes of space at the end of every page that
** are intentually left unused.  This is the "reserved" space that is
** sometimes used by extensions.
**
** The value returned is the larger of the current reserve size and
** the latest reserve size requested by SQLITE_FILECTRL_RESERVE_BYTES.
** The amount of reserve can only grow - never shrink.
*/
SQLITE_PRIVATE int sqlite3BtreeGetRequestedReserve(Btree *p){
  int n;
  int n1, n2;
  sqlite3BtreeEnter(p);
  n = ((int)p->pBt->nReserveWanted) - 1;
  if( n<0 ) n = sqlite3BtreeGetReserveNoMutex(p);
  n1 = (int)p->pBt->nReserveWanted;
  n2 = sqlite3BtreeGetReserveNoMutex(p);
  sqlite3BtreeLeave(p);
  return n;
  return n1>n2 ? n1 : n2;
}


/*
** Set the maximum page count for a database if mxPage is positive.
** No changes are made if mxPage is 0 or negative.
** Regardless of the value of mxPage, return the maximum page count.
67420
67421
67422
67423
67424
67425
67426

67427
67428
67429
67430
67431
67432
67433
67434
67435
67436
67437
67438
67439
67440
67441
67442

67443
67444
67445
67446
67447
67448
67449
67631
67632
67633
67634
67635
67636
67637
67638
67639
67640
67641
67642
67643
67644
67645
67646
67647
67648
67649
67650
67651
67652
67653

67654
67655
67656
67657
67658
67659
67660
67661







+















-
+







** One or the other of the two processes must give way or there can be
** no progress.  By returning SQLITE_BUSY and not invoking the busy callback
** when A already has a read lock, we encourage A to give up and let B
** proceed.
*/
SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){
  BtShared *pBt = p->pBt;
  Pager *pPager = pBt->pPager;
  int rc = SQLITE_OK;

  sqlite3BtreeEnter(p);
  btreeIntegrity(p);

  /* If the btree is already in a write-transaction, or it
  ** is already in a read-transaction and a read-transaction
  ** is requested, this is a no-op.
  */
  if( p->inTrans==TRANS_WRITE || (p->inTrans==TRANS_READ && !wrflag) ){
    goto trans_begun;
  }
  assert( pBt->inTransaction==TRANS_WRITE || IfNotOmitAV(pBt->bDoTruncate)==0 );

  if( (p->db->flags & SQLITE_ResetDatabase) 
   && sqlite3PagerIsreadonly(pBt->pPager)==0 
   && sqlite3PagerIsreadonly(pPager)==0 
  ){
    pBt->btsFlags &= ~BTS_READ_ONLY;
  }

  /* Write transactions are not possible on a read-only database */
  if( (pBt->btsFlags & BTS_READ_ONLY)!=0 && wrflag ){
    rc = SQLITE_READONLY;
67483
67484
67485
67486
67487
67488
67489












67490
67491
67492
67493
67494
67495
67496
67497
67498
67499
67500
67501
67502
67503

67504
67505
67506
67507
67508
67509
67510
67511
67512
67513
67514
67515

67516
67517
67518
67519
67520




67521
67522
67523
67524
67525
67526
67527
67695
67696
67697
67698
67699
67700
67701
67702
67703
67704
67705
67706
67707
67708
67709
67710
67711
67712
67713
67714
67715
67716
67717
67718
67719
67720
67721
67722
67723
67724
67725
67726

67727
67728
67729
67730
67731
67732
67733
67734
67735
67736
67737
67738
67739
67740
67741
67742
67743
67744

67745
67746
67747
67748
67749
67750
67751
67752
67753
67754
67755







+
+
+
+
+
+
+
+
+
+
+
+













-
+












+




-
+
+
+
+







  ** on page 1, the transaction cannot be opened. */
  rc = querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK);
  if( SQLITE_OK!=rc ) goto trans_begun;

  pBt->btsFlags &= ~BTS_INITIALLY_EMPTY;
  if( pBt->nPage==0 ) pBt->btsFlags |= BTS_INITIALLY_EMPTY;
  do {
    sqlite3PagerWalDb(pPager, p->db);

#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
    /* If transitioning from no transaction directly to a write transaction,
    ** block for the WRITER lock first if possible. */
    if( pBt->pPage1==0 && wrflag ){
      assert( pBt->inTransaction==TRANS_NONE );
      rc = sqlite3PagerWalWriteLock(pPager, 1);
      if( rc!=SQLITE_BUSY && rc!=SQLITE_OK ) break;
    }
#endif

    /* Call lockBtree() until either pBt->pPage1 is populated or
    ** lockBtree() returns something other than SQLITE_OK. lockBtree()
    ** may return SQLITE_OK but leave pBt->pPage1 set to 0 if after
    ** reading page 1 it discovers that the page-size of the database 
    ** file is not pBt->pageSize. In this case lockBtree() will update
    ** pBt->pageSize to the page-size of the file on disk.
    */
    while( pBt->pPage1==0 && SQLITE_OK==(rc = lockBtree(pBt)) );

    if( rc==SQLITE_OK && wrflag ){
      if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){
        rc = SQLITE_READONLY;
      }else{
        rc = sqlite3PagerBegin(pBt->pPager,wrflag>1,sqlite3TempInMemory(p->db));
        rc = sqlite3PagerBegin(pPager, wrflag>1, sqlite3TempInMemory(p->db));
        if( rc==SQLITE_OK ){
          rc = newDatabase(pBt);
        }else if( rc==SQLITE_BUSY_SNAPSHOT && pBt->inTransaction==TRANS_NONE ){
          /* if there was no transaction opened when this function was
          ** called and SQLITE_BUSY_SNAPSHOT is returned, change the error
          ** code to SQLITE_BUSY. */
          rc = SQLITE_BUSY;
        }
      }
    }
  
    if( rc!=SQLITE_OK ){
      (void)sqlite3PagerWalWriteLock(pPager, 0);
      unlockBtreeIfUnused(pBt);
    }
  }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&
          btreeInvokeBusyHandler(pBt) );
  sqlite3PagerResetLockTimeout(pBt->pPager);
  sqlite3PagerWalDb(pPager, 0);
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY;
#endif

  if( rc==SQLITE_OK ){
    if( p->inTrans==TRANS_NONE ){
      pBt->nTransaction++;
#ifndef SQLITE_OMIT_SHARED_CACHE
      if( p->sharable ){
        assert( p->lock.pBtree==p && p->lock.iTable==1 );
67565
67566
67567
67568
67569
67570
67571
67572

67573
67574
67575
67576
67577
67578
67579
67793
67794
67795
67796
67797
67798
67799

67800
67801
67802
67803
67804
67805
67806
67807







-
+







      *pSchemaVersion = get4byte(&pBt->pPage1->aData[40]);
    }
    if( wrflag ){
      /* This call makes sure that the pager has the correct number of
      ** open savepoints. If the second parameter is greater than 0 and
      ** the sub-journal is not already open, then it will be opened here.
      */
      rc = sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint);
      rc = sqlite3PagerOpenSavepoint(pPager, p->db->nSavepoint);
    }
  }

  btreeIntegrity(p);
  sqlite3BtreeLeave(p);
  return rc;
}
71201
71202
71203
71204
71205
71206
71207
71208

71209
71210
71211
71212
71213
71214
71215
71429
71430
71431
71432
71433
71434
71435

71436
71437
71438
71439
71440
71441
71442
71443







-
+







  memcpy(pTmp, aData, pPg->pBt->usableSize);
#endif

  /* Remove cells from the start and end of the page */
  assert( nCell>=0 );
  if( iOld<iNew ){
    int nShift = pageFreeArray(pPg, iOld, iNew-iOld, pCArray);
    if( nShift>nCell ) return SQLITE_CORRUPT_BKPT;
    if( NEVER(nShift>nCell) ) return SQLITE_CORRUPT_BKPT;
    memmove(pPg->aCellIdx, &pPg->aCellIdx[nShift*2], nCell*2);
    nCell -= nShift;
  }
  if( iNewEnd < iOldEnd ){
    int nTail = pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray);
    assert( nCell>=nTail );
    nCell -= nTail;
73558
73559
73560
73561
73562
73563
73564
73565
73566
73567
73568
73569
73570
73571
73572
73786
73787
73788
73789
73790
73791
73792

73793
73794
73795
73796
73797
73798
73799







-







    }
#endif
  }
  sqlite3BtreeLeave(p);
  return rc;
}

#ifndef SQLITE_OMIT_BTREECOUNT
/*
** The first argument, pCur, is a cursor opened on some b-tree. Count the
** number of entries in the b-tree and write the result to *pnEntry.
**
** SQLITE_OK is returned if the operation is successfully executed. 
** Otherwise, if an error is encountered (i.e. an IO error or database
** corruption) an SQLite error code is returned.
73631
73632
73633
73634
73635
73636
73637
73638
73639
73640
73641
73642
73643
73644
73645
73858
73859
73860
73861
73862
73863
73864

73865
73866
73867
73868
73869
73870
73871







-







      rc = moveToChild(pCur, get4byte(findCell(pPage, iIdx)));
    }
  }

  /* An error has occurred. Return an error code. */
  return rc;
}
#endif

/*
** Return the pager associated with a BTree.  This routine is used for
** testing and debugging only.
*/
SQLITE_PRIVATE Pager *sqlite3BtreePager(Btree *p){
  return p->pBt->pPager;
74682
74683
74684
74685
74686
74687
74688
74689

74690
74691
74692
74693
74694
74695
74696
74908
74909
74910
74911
74912
74913
74914

74915
74916
74917
74918
74919
74920
74921
74922







-
+








/*
** Attempt to set the page size of the destination to match the page size
** of the source.
*/
static int setDestPgsz(sqlite3_backup *p){
  int rc;
  rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),-1,0);
  rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),0,0);
  return rc;
}

/*
** Check that there is no open read-transaction on the b-tree passed as the
** second argument. If there is not, return SQLITE_OK. Otherwise, if there
** is an open read-transaction, return SQLITE_ERROR and leave an error 
78707
78708
78709
78710
78711
78712
78713
78714


78715
78716

78717
78718
78719
78720
78721
78722
78723
78724
78725
78726
78727

78728
78729
78730
78731
78732
78733
78734
78933
78934
78935
78936
78937
78938
78939

78940
78941
78942

78943


78944
78945
78946
78947
78948
78949
78950

78951
78952
78953
78954
78955
78956
78957
78958
78959







-
+
+

-
+
-
-







-

+







** Some translation occurs:
**
**       "PX"      ->  "r[X]"
**       "PX@PY"   ->  "r[X..X+Y-1]"  or "r[x]" if y is 0 or 1
**       "PX@PY+1" ->  "r[X..X+Y]"    or "r[x]" if y is 0
**       "PY..PY"  ->  "r[X..Y]"      or "r[x]" if y<=x
*/
static int displayComment(
SQLITE_PRIVATE char *sqlite3VdbeDisplayComment(
  sqlite3 *db,       /* Optional - Oom error reporting only */
  const Op *pOp,     /* The opcode to be commented */
  const char *zP4,   /* Previously obtained value for P4 */
  const char *zP4    /* Previously obtained value for P4 */
  char *zTemp,       /* Write result here */
  int nTemp          /* Space available in zTemp[] */
){
  const char *zOpName;
  const char *zSynopsis;
  int nOpName;
  int ii;
  char zAlt[50];
  StrAccum x;
  sqlite3StrAccumInit(&x, 0, zTemp, nTemp, 0);

  sqlite3StrAccumInit(&x, 0, 0, 0, SQLITE_MAX_LENGTH);
  zOpName = sqlite3OpcodeName(pOp->opcode);
  nOpName = sqlite3Strlen30(zOpName);
  if( zOpName[nOpName+1] ){
    int seenCom = 0;
    char c;
    zSynopsis = zOpName += nOpName + 1;
    if( strncmp(zSynopsis,"IF ",3)==0 ){
78787
78788
78789
78790
78791
78792
78793



78794

78795
78796
78797

78798
78799
78800
78801
78802
78803
78804
79012
79013
79014
79015
79016
79017
79018
79019
79020
79021

79022

79023

79024
79025
79026
79027
79028
79029
79030
79031







+
+
+
-
+
-

-
+







    }
    if( !seenCom && pOp->zComment ){
      sqlite3_str_appendf(&x, "; %s", pOp->zComment);
    }
  }else if( pOp->zComment ){
    sqlite3_str_appendall(&x, pOp->zComment);
  }
  if( (x.accError & SQLITE_NOMEM)!=0 && db!=0 ){
    sqlite3OomFault(db);
  }
  sqlite3StrAccumFinish(&x);
  return sqlite3StrAccumFinish(&x);
  return x.nChar;
}
#endif /* SQLITE_DEBUG */
#endif /* SQLITE_ENABLE_EXPLAIN_COMMENTS */

#if VDBE_DISPLAY_P4 && defined(SQLITE_ENABLE_CURSOR_HINTS)
/*
** Translate the P4.pExpr value for an OP_CursorHint opcode into text
** that can be displayed in the P4 column of EXPLAIN output.
*/
static void displayP4Expr(StrAccum *p, Expr *pExpr){
78871
78872
78873
78874
78875
78876
78877
78878
78879


78880
78881
78882


78883
78884
78885
78886
78887
78888
78889
79098
79099
79100
79101
79102
79103
79104


79105
79106
79107


79108
79109
79110
79111
79112
79113
79114
79115
79116







-
-
+
+

-
-
+
+









#if VDBE_DISPLAY_P4
/*
** Compute a string that describes the P4 parameter for an opcode.
** Use zTemp for any required temporary buffer space.
*/
static char *displayP4(Op *pOp, char *zTemp, int nTemp){
  char *zP4 = zTemp;
SQLITE_PRIVATE char *sqlite3VdbeDisplayP4(sqlite3 *db, Op *pOp){
  char *zP4 = 0;
  StrAccum x;
  assert( nTemp>=20 );
  sqlite3StrAccumInit(&x, 0, zTemp, nTemp, 0);

  sqlite3StrAccumInit(&x, 0, 0, 0, SQLITE_MAX_LENGTH);
  switch( pOp->p4type ){
    case P4_KEYINFO: {
      int j;
      KeyInfo *pKeyInfo = pOp->p4.pKeyInfo;
      assert( pKeyInfo->aSortFlags!=0 );
      sqlite3_str_appendf(&x, "k(%d", pKeyInfo->nKeyField);
      for(j=0; j<pKeyInfo->nKeyField; j++){
78959
78960
78961
78962
78963
78964
78965
78966

78967
78968
78969
78970
78971
78972
78973

78974
78975
78976
78977
78978
78979
78980
78981
78982

78983
78984
78985
78986
78987
78988
78989
78990
78991





78992
78993

78994
78995
78996
78997
78998
78999
79000
79001
79002
79186
79187
79188
79189
79190
79191
79192

79193
79194

79195
79196
79197
79198

79199
79200
79201
79202
79203

79204
79205
79206

79207
79208
79209
79210
79211





79212
79213
79214
79215
79216
79217

79218


79219
79220
79221
79222
79223
79224
79225







-
+

-




-
+




-



-
+




-
-
-
-
-
+
+
+
+
+

-
+
-
-







#endif
    case P4_INTARRAY: {
      int i;
      int *ai = pOp->p4.ai;
      int n = ai[0];   /* The first element of an INTARRAY is always the
                       ** count of the number of elements to follow */
      for(i=1; i<=n; i++){
        sqlite3_str_appendf(&x, ",%d", ai[i]);
        sqlite3_str_appendf(&x, "%c%d", (i==1 ? '[' : ','), ai[i]);
      }
      zTemp[0] = '[';
      sqlite3_str_append(&x, "]", 1);
      break;
    }
    case P4_SUBPROGRAM: {
      sqlite3_str_appendf(&x, "program");
      zP4 = "program";
      break;
    }
    case P4_DYNBLOB:
    case P4_ADVANCE: {
      zTemp[0] = 0;
      break;
    }
    case P4_TABLE: {
      sqlite3_str_appendf(&x, "%s", pOp->p4.pTab->zName);
      zP4 = pOp->p4.pTab->zName;
      break;
    }
    default: {
      zP4 = pOp->p4.z;
      if( zP4==0 ){
        zP4 = zTemp;
        zTemp[0] = 0;
      }
    }
    }
  }
  if( zP4 ) sqlite3_str_appendall(&x, zP4);
  if( (x.accError & SQLITE_NOMEM)!=0 ){
    sqlite3OomFault(db);
  }
  sqlite3StrAccumFinish(&x);
  return sqlite3StrAccumFinish(&x);
  assert( zP4!=0 );
  return zP4;
}
#endif /* VDBE_DISPLAY_P4 */

/*
** Declare to the Vdbe that the BTree object at db->aDb[i] is used.
**
** The prepared statements need to know in advance the complete set of
79078
79079
79080
79081
79082
79083
79084
79085
79086


79087
79088

79089

79090
79091

79092
79093

79094
79095
79096
79097
79098
79099
79100



79101
79102


79103
79104
79105
79106
79107
79108
79109
79301
79302
79303
79304
79305
79306
79307


79308
79309
79310
79311
79312

79313
79314

79315
79316

79317
79318
79319
79320
79321
79322


79323
79324
79325
79326
79327
79328
79329
79330
79331
79332
79333
79334
79335
79336







-
-
+
+


+
-
+

-
+

-
+





-
-
+
+
+


+
+








#if defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
/*
** Print a single opcode.  This routine is used for debugging only.
*/
SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE *pOut, int pc, VdbeOp *pOp){
  char *zP4;
  char zPtr[50];
  char zCom[100];
  char *zCom;
  sqlite3 dummyDb;
  static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-13s %.2X %s\n";
  if( pOut==0 ) pOut = stdout;
  dummyDb.mallocFailed = 1;
  zP4 = displayP4(pOp, zPtr, sizeof(zPtr));
  zP4 = sqlite3VdbeDisplayP4(&dummyDb, pOp);
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
  displayComment(pOp, zP4, zCom, sizeof(zCom));
  zCom = sqlite3VdbeDisplayComment(0, pOp, zP4);
#else
  zCom[0] = 0;
  zCom = 0;
#endif
  /* NB:  The sqlite3OpcodeName() function is implemented by code created
  ** by the mkopcodeh.awk and mkopcodec.awk scripts which extract the
  ** information from the vdbe.c source text */
  fprintf(pOut, zFormat1, pc, 
      sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, zP4, pOp->p5,
      zCom
      sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, 
      zP4 ? zP4 : "", pOp->p5,
      zCom ? zCom : ""
  );
  fflush(pOut);
  sqlite3_free(zP4);
  sqlite3_free(zCom);
}
#endif

/*
** Initialize an array of N Mem element.
*/
static void initMemArray(Mem *p, int N, sqlite3 *db, u16 flags){
79186
79187
79188
79189
79190
79191
79192



















































































































79193
79194
79195
79196
79197
79198
79199
79413
79414
79415
79416
79417
79418
79419
79420
79421
79422
79423
79424
79425
79426
79427
79428
79429
79430
79431
79432
79433
79434
79435
79436
79437
79438
79439
79440
79441
79442
79443
79444
79445
79446
79447
79448
79449
79450
79451
79452
79453
79454
79455
79456
79457
79458
79459
79460
79461
79462
79463
79464
79465
79466
79467
79468
79469
79470
79471
79472
79473
79474
79475
79476
79477
79478
79479
79480
79481
79482
79483
79484
79485
79486
79487
79488
79489
79490
79491
79492
79493
79494
79495
79496
79497
79498
79499
79500
79501
79502
79503
79504
79505
79506
79507
79508
79509
79510
79511
79512
79513
79514
79515
79516
79517
79518
79519
79520
79521
79522
79523
79524
79525
79526
79527
79528
79529
79530
79531
79532
79533
79534
79535
79536
79537
79538
79539
79540
79541







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







SQLITE_PRIVATE void sqlite3VdbeFrameMemDel(void *pArg){
  VdbeFrame *pFrame = (VdbeFrame*)pArg;
  assert( sqlite3VdbeFrameIsValid(pFrame) );
  pFrame->pParent = pFrame->v->pDelFrame;
  pFrame->v->pDelFrame = pFrame;
}

#if defined(SQLITE_ENABLE_BYTECODE_VTAB) || !defined(SQLITE_OMIT_EXPLAIN)
/*
** Locate the next opcode to be displayed in EXPLAIN or EXPLAIN
** QUERY PLAN output.
**
** Return SQLITE_ROW on success.  Return SQLITE_DONE if there are no
** more opcodes to be displayed.
*/
SQLITE_PRIVATE int sqlite3VdbeNextOpcode(
  Vdbe *p,         /* The statement being explained */
  Mem *pSub,       /* Storage for keeping track of subprogram nesting */
  int eMode,       /* 0: normal.  1: EQP.  2:  TablesUsed */
  int *piPc,       /* IN/OUT: Current rowid.  Overwritten with next rowid */
  int *piAddr,     /* OUT: Write index into (*paOp)[] here */
  Op **paOp        /* OUT: Write the opcode array here */
){
  int nRow;                            /* Stop when row count reaches this */
  int nSub = 0;                        /* Number of sub-vdbes seen so far */
  SubProgram **apSub = 0;              /* Array of sub-vdbes */
  int i;                               /* Next instruction address */
  int rc = SQLITE_OK;                  /* Result code */
  Op *aOp = 0;                         /* Opcode array */
  int iPc;                             /* Rowid.  Copy of value in *piPc */

  /* When the number of output rows reaches nRow, that means the
  ** listing has finished and sqlite3_step() should return SQLITE_DONE.
  ** nRow is the sum of the number of rows in the main program, plus
  ** the sum of the number of rows in all trigger subprograms encountered
  ** so far.  The nRow value will increase as new trigger subprograms are
  ** encountered, but p->pc will eventually catch up to nRow.
  */
  nRow = p->nOp;
  if( pSub!=0 ){
    if( pSub->flags&MEM_Blob ){
      /* pSub is initiallly NULL.  It is initialized to a BLOB by
      ** the P4_SUBPROGRAM processing logic below */
      nSub = pSub->n/sizeof(Vdbe*);
      apSub = (SubProgram **)pSub->z;
    }
    for(i=0; i<nSub; i++){
      nRow += apSub[i]->nOp;
    }
  }
  iPc = *piPc;
  while(1){  /* Loop exits via break */
    i = iPc++;
    if( i>=nRow ){
      p->rc = SQLITE_OK;
      rc = SQLITE_DONE;
      break;
    }
    if( i<p->nOp ){
      /* The rowid is small enough that we are still in the
      ** main program. */
      aOp = p->aOp;
    }else{
      /* We are currently listing subprograms.  Figure out which one and
      ** pick up the appropriate opcode. */
      int j;
      i -= p->nOp;
      assert( apSub!=0 );
      assert( nSub>0 );
      for(j=0; i>=apSub[j]->nOp; j++){
        i -= apSub[j]->nOp;
        assert( i<apSub[j]->nOp || j+1<nSub );
      }
      aOp = apSub[j]->aOp;
    }

    /* When an OP_Program opcode is encounter (the only opcode that has
    ** a P4_SUBPROGRAM argument), expand the size of the array of subprograms
    ** kept in p->aMem[9].z to hold the new program - assuming this subprogram
    ** has not already been seen.
    */
    if( pSub!=0 && aOp[i].p4type==P4_SUBPROGRAM ){
      int nByte = (nSub+1)*sizeof(SubProgram*);
      int j;
      for(j=0; j<nSub; j++){
        if( apSub[j]==aOp[i].p4.pProgram ) break;
      }
      if( j==nSub ){
        p->rc = sqlite3VdbeMemGrow(pSub, nByte, nSub!=0);
        if( p->rc!=SQLITE_OK ){
          rc = SQLITE_ERROR;
          break;
        }
        apSub = (SubProgram **)pSub->z;
        apSub[nSub++] = aOp[i].p4.pProgram;
        MemSetTypeFlag(pSub, MEM_Blob);
        pSub->n = nSub*sizeof(SubProgram*);
        nRow += aOp[i].p4.pProgram->nOp;
      }
    }
    if( eMode==0 ) break;
#ifdef SQLITE_ENABLE_BYTECODE_VTAB
    if( eMode==2 ){
      Op *pOp = aOp + i;
      if( pOp->opcode==OP_OpenRead ) break;
      if( pOp->opcode==OP_OpenWrite && (pOp->p5 & OPFLAG_P2ISREG)==0 ) break;
      if( pOp->opcode==OP_ReopenIdx ) break;      
    }else
#endif
    {
      assert( eMode==1 );
      if( aOp[i].opcode==OP_Explain ) break;
      if( aOp[i].opcode==OP_Init && iPc>1 ) break;
    }
  }
  *piPc = iPc;
  *piAddr = i;
  *paOp = aOp;
  return rc;
}
#endif /* SQLITE_ENABLE_BYTECODE_VTAB || !SQLITE_OMIT_EXPLAIN */


/*
** Delete a VdbeFrame object and its contents. VdbeFrame objects are
** allocated by the OP_Program opcode in sqlite3VdbeExec().
*/
SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame *p){
  int i;
79226
79227
79228
79229
79230
79231
79232
79233
79234
79235
79236
79237
79238
79239
79240
79241

79242

79243
79244
79245
79246
79247
79248
79249
79250
79251
79252
79253
79254
79255
79256
79257
79258
79259
79260
79261
79262
79263
79264
79265
79266
79267
79268
79269
79270
79271
79272
79273
79274
79275
79276
79277
79278
79279
79280
79281
79282
79283
79284
79285
79286
79287
79288
79289
79290
79291
79292
79293
79294
79295
79296
79297
79298
79299

79300
79301
79302
79303
79304

79305
79306
79307
79308
79309

79310
79311
79312


79313
79314
79315
79316
79317
79318
79319
79320
79321
79322
79323
79324
79325

79326
79327
79328
79329

79330
79331
79332
79333
79334
79335
79336
79337
79338
79339
79340
79341
79342

79343
79344
79345
79346
79347
79348
79349


79350
79351
79352
79353

79354
79355
79356
79357
79358
79359
79360
79361

79362
79363
79364
79365

79366
79367
79368
79369

79370
79371
79372
79373

79374
79375
79376
79377
79378
79379
79380
79381
79382
79383
79384
79385
79386



79387
79388
79389


79390
79391

79392
79393
79394
79395
79396
79397
79398
79399
79400


79401

79402

79403
79404

79405
79406
79407
79408
79409
79410

79411
79412
79413
79414
79415
79416
79417











79418
79419
79420
79421
79422
79423
79424
79568
79569
79570
79571
79572
79573
79574



79575
79576
79577
79578
79579
79580
79581

79582
79583
79584
79585
79586
79587
79588
79589
79590
79591
79592
79593
79594
79595
79596
79597
79598
79599
79600
79601








79602
79603
79604
79605
79606
79607
79608























79609





79610





79611



79612
79613













79614




79615












79616
79617
79618
79619
79620
79621
79622


79623
79624




79625








79626




79627




79628




79629









79630



79631
79632
79633



79634
79635


79636









79637
79638
79639
79640

79641


79642
79643



79644

79645
79646






79647
79648
79649
79650
79651
79652
79653
79654
79655
79656
79657
79658
79659
79660
79661
79662
79663
79664







-
-
-






+
-
+



















-
-
-
-
-
-
-
-







-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
+
-
-
-
-
-
+
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-

+





-
-
+
+
-
-
-
-
+
-
-
-
-
-
-
-
-
+
-
-
-
-
+
-
-
-
-
+
-
-
-
-
+
-
-
-
-
-
-
-
-
-

-
-
-
+
+
+
-
-
-
+
+
-
-
+
-
-
-
-
-
-
-
-
-
+
+

+
-
+
-
-
+

-
-
-

-
+

-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+







**
** When p->explain==1, first the main program is listed, then each of
** the trigger subprograms are listed one by one.
*/
SQLITE_PRIVATE int sqlite3VdbeList(
  Vdbe *p                   /* The VDBE */
){
  int nRow;                            /* Stop when row count reaches this */
  int nSub = 0;                        /* Number of sub-vdbes seen so far */
  SubProgram **apSub = 0;              /* Array of sub-vdbes */
  Mem *pSub = 0;                       /* Memory cell hold array of subprogs */
  sqlite3 *db = p->db;                 /* The database connection */
  int i;                               /* Loop counter */
  int rc = SQLITE_OK;                  /* Return code */
  Mem *pMem = &p->aMem[1];             /* First Mem of result set */
  int bListSubprogs = (p->explain==1 || (db->flags & SQLITE_TriggerEQP)!=0);
  Op *aOp;                             /* Array of opcodes */
  Op *pOp = 0;
  Op *pOp;                             /* Current opcode */

  assert( p->explain );
  assert( p->magic==VDBE_MAGIC_RUN );
  assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || p->rc==SQLITE_NOMEM );

  /* Even though this opcode does not use dynamic strings for
  ** the result, result columns may become dynamic if the user calls
  ** sqlite3_column_text16(), causing a translation to UTF-16 encoding.
  */
  releaseMemArray(pMem, 8);
  p->pResultSet = 0;

  if( p->rc==SQLITE_NOMEM ){
    /* This happens if a malloc() inside a call to sqlite3_column_text() or
    ** sqlite3_column_text16() failed.  */
    sqlite3OomFault(db);
    return SQLITE_ERROR;
  }

  /* When the number of output rows reaches nRow, that means the
  ** listing has finished and sqlite3_step() should return SQLITE_DONE.
  ** nRow is the sum of the number of rows in the main program, plus
  ** the sum of the number of rows in all trigger subprograms encountered
  ** so far.  The nRow value will increase as new trigger subprograms are
  ** encountered, but p->pc will eventually catch up to nRow.
  */
  nRow = p->nOp;
  if( bListSubprogs ){
    /* The first 8 memory cells are used for the result set.  So we will
    ** commandeer the 9th cell to use as storage for an array of pointers
    ** to trigger subprograms.  The VDBE is guaranteed to have at least 9
    ** cells.  */
    assert( p->nMem>9 );
    pSub = &p->aMem[9];
    if( pSub->flags&MEM_Blob ){
      /* On the first call to sqlite3_step(), pSub will hold a NULL.  It is
      ** initialized to a BLOB by the P4_SUBPROGRAM processing logic below */
      nSub = pSub->n/sizeof(Vdbe*);
      apSub = (SubProgram **)pSub->z;
    }
    for(i=0; i<nSub; i++){
      nRow += apSub[i]->nOp;
    }
  }

  while(1){  /* Loop exits via break */
    i = p->pc++;
    if( i>=nRow ){
      p->rc = SQLITE_OK;
      rc = SQLITE_DONE;
      break;
    }
    if( i<p->nOp ){
      /* The output line number is small enough that we are still in the
      ** main program. */
      pOp = &p->aOp[i];
    }else{
  }else{
      /* We are currently listing subprograms.  Figure out which one and
      ** pick up the appropriate opcode. */
      int j;
      i -= p->nOp;
      assert( apSub!=0 );
    pSub = 0;
      assert( nSub>0 );
      for(j=0; i>=apSub[j]->nOp; j++){
        i -= apSub[j]->nOp;
        assert( i<apSub[j]->nOp || j+1<nSub );
      }
  }
      pOp = &apSub[j]->aOp[i];
    }


  /* Figure out which opcode is next to display */
    /* When an OP_Program opcode is encounter (the only opcode that has
    ** a P4_SUBPROGRAM argument), expand the size of the array of subprograms
    ** kept in p->aMem[9].z to hold the new program - assuming this subprogram
    ** has not already been seen.
    */
    if( bListSubprogs && pOp->p4type==P4_SUBPROGRAM ){
      int nByte = (nSub+1)*sizeof(SubProgram*);
      int j;
      for(j=0; j<nSub; j++){
        if( apSub[j]==pOp->p4.pProgram ) break;
      }
      if( j==nSub ){
        p->rc = sqlite3VdbeMemGrow(pSub, nByte, nSub!=0);
  rc = sqlite3VdbeNextOpcode(p, pSub, p->explain==2, &p->pc, &i, &aOp);
        if( p->rc!=SQLITE_OK ){
          rc = SQLITE_ERROR;
          break;
        }

        apSub = (SubProgram **)pSub->z;
        apSub[nSub++] = pOp->p4.pProgram;
        pSub->flags |= MEM_Blob;
        pSub->n = nSub*sizeof(SubProgram*);
        nRow += pOp->p4.pProgram->nOp;
      }
    }
    if( p->explain<2 ) break;
    if( pOp->opcode==OP_Explain ) break;
    if( pOp->opcode==OP_Init && p->pc>1 ) break;
  }

  if( rc==SQLITE_OK ){
    pOp = aOp + i;
    if( AtomicLoad(&db->u1.isInterrupted) ){
      p->rc = SQLITE_INTERRUPT;
      rc = SQLITE_ERROR;
      sqlite3VdbeError(p, sqlite3ErrStr(p->rc));
    }else{
      char *zP4;
      if( p->explain==1 ){
      char *zP4 = sqlite3VdbeDisplayP4(db, pOp);
      if( p->explain==2 ){
        pMem->flags = MEM_Int;
        pMem->u.i = i;                                /* Program counter */
        pMem++;
    
        sqlite3VdbeMemSetInt64(pMem, pOp->p1);
        pMem->flags = MEM_Static|MEM_Str|MEM_Term;
        pMem->z = (char*)sqlite3OpcodeName(pOp->opcode); /* Opcode */
        assert( pMem->z!=0 );
        pMem->n = sqlite3Strlen30(pMem->z);
        pMem->enc = SQLITE_UTF8;
        pMem++;
      }

        sqlite3VdbeMemSetInt64(pMem+1, pOp->p2);
      pMem->flags = MEM_Int;
      pMem->u.i = pOp->p1;                          /* P1 */
      pMem++;

        sqlite3VdbeMemSetInt64(pMem+2, pOp->p3);
      pMem->flags = MEM_Int;
      pMem->u.i = pOp->p2;                          /* P2 */
      pMem++;

        sqlite3VdbeMemSetStr(pMem+3, zP4, -1, SQLITE_UTF8, sqlite3_free);     
      pMem->flags = MEM_Int;
      pMem->u.i = pOp->p3;                          /* P3 */
      pMem++;

        p->nResColumn = 4;
      if( sqlite3VdbeMemClearAndResize(pMem, 100) ){ /* P4 */
        assert( p->db->mallocFailed );
        return SQLITE_ERROR;
      }
      pMem->flags = MEM_Str|MEM_Term;
      zP4 = displayP4(pOp, pMem->z, pMem->szMalloc);
      if( zP4!=pMem->z ){
        pMem->n = 0;
        sqlite3VdbeMemSetStr(pMem, zP4, -1, SQLITE_UTF8, 0);
      }else{
        assert( pMem->z!=0 );
        pMem->n = sqlite3Strlen30(pMem->z);
        pMem->enc = SQLITE_UTF8;
        sqlite3VdbeMemSetInt64(pMem+0, i);
        sqlite3VdbeMemSetStr(pMem+1, (char*)sqlite3OpcodeName(pOp->opcode),
                             -1, SQLITE_UTF8, SQLITE_STATIC);
      }
      pMem++;

        sqlite3VdbeMemSetInt64(pMem+2, pOp->p1);
        sqlite3VdbeMemSetInt64(pMem+3, pOp->p2);
      if( p->explain==1 ){
        if( sqlite3VdbeMemClearAndResize(pMem, 4) ){
        sqlite3VdbeMemSetInt64(pMem+4, pOp->p3);
          assert( p->db->mallocFailed );
          return SQLITE_ERROR;
        }
        pMem->flags = MEM_Str|MEM_Term;
        pMem->n = 2;
        sqlite3_snprintf(3, pMem->z, "%.2x", pOp->p5);   /* P5 */
        pMem->enc = SQLITE_UTF8;
        pMem++;
    
        /* pMem+5 for p4 is done last */
        sqlite3VdbeMemSetInt64(pMem+6, pOp->p5);
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
        {
        if( sqlite3VdbeMemClearAndResize(pMem, 500) ){
          char *zCom = sqlite3VdbeDisplayComment(db, pOp, zP4);
          assert( p->db->mallocFailed );
          return SQLITE_ERROR;
          sqlite3VdbeMemSetStr(pMem+7, zCom, -1, SQLITE_UTF8, sqlite3_free);
        }
        pMem->flags = MEM_Str|MEM_Term;
        pMem->n = displayComment(pOp, zP4, pMem->z, 500);
        pMem->enc = SQLITE_UTF8;
#else
        pMem->flags = MEM_Null;                       /* Comment */
        sqlite3VdbeMemSetNull(pMem+7);
#endif
      }

      p->nResColumn = 8 - 4*(p->explain-1);
      p->pResultSet = &p->aMem[1];
      p->rc = SQLITE_OK;
      rc = SQLITE_ROW;
        sqlite3VdbeMemSetStr(pMem+5, zP4, -1, SQLITE_UTF8, sqlite3_free);
        p->nResColumn = 8;
      }
      p->pResultSet = pMem;
      if( db->mallocFailed ){
        p->rc = SQLITE_NOMEM;
        rc = SQLITE_ERROR;
      }else{
        p->rc = SQLITE_OK;
        rc = SQLITE_ROW;
      }
    }
  }
  return rc;
}
#endif /* SQLITE_OMIT_EXPLAIN */

#ifdef SQLITE_DEBUG
79980
79981
79982
79983
79984
79985
79986
79987

79988

79989
79990
79991
79992
79993
79994
79995
80220
80221
80222
80223
80224
80225
80226

80227
80228
80229
80230
80231
80232
80233
80234
80235
80236







-
+

+







    i64 offset = 0;
    int res;
    int retryCount = 0;
    int nMainFile;

    /* Select a master journal file name */
    nMainFile = sqlite3Strlen30(zMainFile);
    zMaster = sqlite3MPrintf(db, "%s-mjXXXXXX9XXz%c%c", zMainFile, 0, 0);
    zMaster = sqlite3MPrintf(db, "%.4c%s%.16c", 0,zMainFile,0);
    if( zMaster==0 ) return SQLITE_NOMEM_BKPT;
    zMaster += 4;
    do {
      u32 iRandom;
      if( retryCount ){
        if( retryCount>100 ){
          sqlite3_log(SQLITE_FULL, "MJ delete: %s", zMaster);
          sqlite3OsDelete(pVfs, zMaster, 0);
          break;
80011
80012
80013
80014
80015
80016
80017
80018

80019
80020
80021
80022
80023
80024
80025
80252
80253
80254
80255
80256
80257
80258

80259
80260
80261
80262
80263
80264
80265
80266







-
+







      /* Open the master journal. */
      rc = sqlite3OsOpenMalloc(pVfs, zMaster, &pMaster, 
          SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|
          SQLITE_OPEN_EXCLUSIVE|SQLITE_OPEN_MASTER_JOURNAL, 0
      );
    }
    if( rc!=SQLITE_OK ){
      sqlite3DbFree(db, zMaster);
      sqlite3DbFree(db, zMaster-4);
      return rc;
    }
 
    /* Write the name of each database file in the transaction into the new
    ** master journal file. If an error occurs at this point close
    ** and delete the master journal file. All the individual journal files
    ** still have 'null' as the master journal pointer, so they will roll
80034
80035
80036
80037
80038
80039
80040
80041

80042
80043
80044
80045
80046
80047
80048
80049
80050
80051
80052
80053
80054
80055

80056
80057
80058
80059
80060
80061
80062
80275
80276
80277
80278
80279
80280
80281

80282
80283
80284
80285
80286
80287
80288
80289
80290
80291
80292
80293
80294
80295

80296
80297
80298
80299
80300
80301
80302
80303







-
+













-
+







        }
        assert( zFile[0]!=0 );
        rc = sqlite3OsWrite(pMaster, zFile, sqlite3Strlen30(zFile)+1, offset);
        offset += sqlite3Strlen30(zFile)+1;
        if( rc!=SQLITE_OK ){
          sqlite3OsCloseFree(pMaster);
          sqlite3OsDelete(pVfs, zMaster, 0);
          sqlite3DbFree(db, zMaster);
          sqlite3DbFree(db, zMaster-4);
          return rc;
        }
      }
    }

    /* Sync the master journal file. If the IOCAP_SEQUENTIAL device
    ** flag is set this is not required.
    */
    if( 0==(sqlite3OsDeviceCharacteristics(pMaster)&SQLITE_IOCAP_SEQUENTIAL)
     && SQLITE_OK!=(rc = sqlite3OsSync(pMaster, SQLITE_SYNC_NORMAL))
    ){
      sqlite3OsCloseFree(pMaster);
      sqlite3OsDelete(pVfs, zMaster, 0);
      sqlite3DbFree(db, zMaster);
      sqlite3DbFree(db, zMaster-4);
      return rc;
    }

    /* Sync all the db files involved in the transaction. The same call
    ** sets the master journal pointer in each individual journal. If
    ** an error occurs here, do not delete the master journal file.
    **
80071
80072
80073
80074
80075
80076
80077
80078

80079
80080
80081
80082
80083
80084
80085
80086
80087

80088
80089
80090
80091
80092
80093
80094
80312
80313
80314
80315
80316
80317
80318

80319
80320
80321
80322
80323
80324
80325
80326
80327

80328
80329
80330
80331
80332
80333
80334
80335







-
+








-
+







      if( pBt ){
        rc = sqlite3BtreeCommitPhaseOne(pBt, zMaster);
      }
    }
    sqlite3OsCloseFree(pMaster);
    assert( rc!=SQLITE_BUSY );
    if( rc!=SQLITE_OK ){
      sqlite3DbFree(db, zMaster);
      sqlite3DbFree(db, zMaster-4);
      return rc;
    }

    /* Delete the master journal file. This commits the transaction. After
    ** doing this the directory is synced again before any individual
    ** transaction files are deleted.
    */
    rc = sqlite3OsDelete(pVfs, zMaster, 1);
    sqlite3DbFree(db, zMaster);
    sqlite3DbFree(db, zMaster-4);
    zMaster = 0;
    if( rc ){
      return rc;
    }

    /* All files and directories have already been synced, so the following
    ** calls to sqlite3BtreeCommitPhaseTwo() are only closing files and
87996
87997
87998
87999
88000
88001
88002
88003

88004
88005
88006
88007





88008
88009
88010
88011
88012
88013
88014
88015
88016



88017
88018
88019




88020
88021
88022
88023
88024
88025
88026
88027
88028
88029
88030
88031
88237
88238
88239
88240
88241
88242
88243

88244
88245
88246
88247

88248
88249
88250
88251
88252
88253

88254
88255
88256
88257
88258
88259
88260
88261
88262
88263



88264
88265
88266
88267
88268
88269
88270
88271

88272
88273
88274
88275
88276
88277
88278







-
+



-
+
+
+
+
+

-







+
+
+
-
-
-
+
+
+
+




-







  assert( nByte==(int)(zPayload - (u8*)pOut->z) );

  assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
  REGISTER_TRACE(pOp->p3, pOut);
  break;
}

/* Opcode: Count P1 P2 * * *
/* Opcode: Count P1 P2 p3 * *
** Synopsis: r[P2]=count()
**
** Store the number of entries (an integer value) in the table or index 
** opened by cursor P1 in register P2
** opened by cursor P1 in register P2.
**
** If P3==0, then an exact count is obtained, which involves visiting
** every btree page of the table.  But if P3 is non-zero, an estimate
** is returned based on the current cursor position.  
*/
#ifndef SQLITE_OMIT_BTREECOUNT
case OP_Count: {         /* out2 */
  i64 nEntry;
  BtCursor *pCrsr;

  assert( p->apCsr[pOp->p1]->eCurType==CURTYPE_BTREE );
  pCrsr = p->apCsr[pOp->p1]->uc.pCursor;
  assert( pCrsr );
  if( pOp->p3 ){
    nEntry = sqlite3BtreeRowCountEst(pCrsr);
  }else{
  nEntry = 0;  /* Not needed.  Only used to silence a warning. */
  rc = sqlite3BtreeCount(db, pCrsr, &nEntry);
  if( rc ) goto abort_due_to_error;
    nEntry = 0;  /* Not needed.  Only used to silence a warning. */
    rc = sqlite3BtreeCount(db, pCrsr, &nEntry);
    if( rc ) goto abort_due_to_error;
  }
  pOut = out2Prerelease(p, pOp);
  pOut->u.i = nEntry;
  goto check_for_interrupt;
}
#endif

/* Opcode: Savepoint P1 * * P4 *
**
** Open, release or rollback the savepoint named by parameter P4, depending
** on the value of P1. To open a new savepoint set P1==0 (SAVEPOINT_BEGIN).
** To release (commit) an existing savepoint set P1==1 (SAVEPOINT_RELEASE).
** To rollback an existing savepoint set P1==2 (SAVEPOINT_ROLLBACK).
90455
90456
90457
90458
90459
90460
90461
90462

90463
90464
90465
90466
90467







90468
90469
90470
90471
90472
90473
90474
90475
90476
90477
90478
90479
90480
90481
90482
90483
90484
90485
90486
90487
90488
90489
90490
90491
90492
90493



90494
90495
90496
90497
90498
90499
90500
90702
90703
90704
90705
90706
90707
90708

90709
90710
90711
90712
90713
90714
90715
90716
90717
90718
90719
90720
90721
90722
90723
90724
90725
90726
90727
90728
90729
90730
90731
90732
90733
90734
90735
90736
90737

90738
90739
90740
90741
90742
90743
90744
90745
90746
90747
90748
90749
90750
90751
90752
90753
90754
90755
90756







-
+





+
+
+
+
+
+
+
















-









+
+
+







  rc = ExpandBlob(pIn2);
  if( rc ) goto abort_due_to_error;
  rc = sqlite3VdbeSorterWrite(pC, pIn2);
  if( rc) goto abort_due_to_error;
  break;
}

/* Opcode: IdxDelete P1 P2 P3 * *
/* Opcode: IdxDelete P1 P2 P3 * P5
** Synopsis: key=r[P2@P3]
**
** The content of P3 registers starting at register P2 form
** an unpacked index key. This opcode removes that entry from the 
** index opened by cursor P1.
**
** If P5 is not zero, then raise an SQLITE_CORRUPT_INDEX error
** if no matching index entry is found.  This happens when running
** an UPDATE or DELETE statement and the index entry to be updated
** or deleted is not found.  For some uses of IdxDelete
** (example:  the EXCEPT operator) it does not matter that no matching
** entry is found.  For those cases, P5 is zero.
*/
case OP_IdxDelete: {
  VdbeCursor *pC;
  BtCursor *pCrsr;
  int res;
  UnpackedRecord r;

  assert( pOp->p3>0 );
  assert( pOp->p2>0 && pOp->p2+pOp->p3<=(p->nMem+1 - p->nCursor)+1 );
  assert( pOp->p1>=0 && pOp->p1<p->nCursor );
  pC = p->apCsr[pOp->p1];
  assert( pC!=0 );
  assert( pC->eCurType==CURTYPE_BTREE );
  sqlite3VdbeIncrWriteCounter(p, pC);
  pCrsr = pC->uc.pCursor;
  assert( pCrsr!=0 );
  assert( pOp->p5==0 );
  r.pKeyInfo = pC->pKeyInfo;
  r.nField = (u16)pOp->p3;
  r.default_rc = 0;
  r.aMem = &aMem[pOp->p2];
  rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res);
  if( rc ) goto abort_due_to_error;
  if( res==0 ){
    rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE);
    if( rc ) goto abort_due_to_error;
  }else if( pOp->p5 ){
    rc = SQLITE_CORRUPT_INDEX;
    goto abort_due_to_error;
  }
  assert( pC->deferredMoveto==0 );
  pC->cacheStatus = CACHE_STALE;
  pC->seekResult = 0;
  break;
}

96091
96092
96093
96094
96095
96096
96097









































































































































































































































































































































































































































96098
96099
96100
96101
96102
96103
96104
96347
96348
96349
96350
96351
96352
96353
96354
96355
96356
96357
96358
96359
96360
96361
96362
96363
96364
96365
96366
96367
96368
96369
96370
96371
96372
96373
96374
96375
96376
96377
96378
96379
96380
96381
96382
96383
96384
96385
96386
96387
96388
96389
96390
96391
96392
96393
96394
96395
96396
96397
96398
96399
96400
96401
96402
96403
96404
96405
96406
96407
96408
96409
96410
96411
96412
96413
96414
96415
96416
96417
96418
96419
96420
96421
96422
96423
96424
96425
96426
96427
96428
96429
96430
96431
96432
96433
96434
96435
96436
96437
96438
96439
96440
96441
96442
96443
96444
96445
96446
96447
96448
96449
96450
96451
96452
96453
96454
96455
96456
96457
96458
96459
96460
96461
96462
96463
96464
96465
96466
96467
96468
96469
96470
96471
96472
96473
96474
96475
96476
96477
96478
96479
96480
96481
96482
96483
96484
96485
96486
96487
96488
96489
96490
96491
96492
96493
96494
96495
96496
96497
96498
96499
96500
96501
96502
96503
96504
96505
96506
96507
96508
96509
96510
96511
96512
96513
96514
96515
96516
96517
96518
96519
96520
96521
96522
96523
96524
96525
96526
96527
96528
96529
96530
96531
96532
96533
96534
96535
96536
96537
96538
96539
96540
96541
96542
96543
96544
96545
96546
96547
96548
96549
96550
96551
96552
96553
96554
96555
96556
96557
96558
96559
96560
96561
96562
96563
96564
96565
96566
96567
96568
96569
96570
96571
96572
96573
96574
96575
96576
96577
96578
96579
96580
96581
96582
96583
96584
96585
96586
96587
96588
96589
96590
96591
96592
96593
96594
96595
96596
96597
96598
96599
96600
96601
96602
96603
96604
96605
96606
96607
96608
96609
96610
96611
96612
96613
96614
96615
96616
96617
96618
96619
96620
96621
96622
96623
96624
96625
96626
96627
96628
96629
96630
96631
96632
96633
96634
96635
96636
96637
96638
96639
96640
96641
96642
96643
96644
96645
96646
96647
96648
96649
96650
96651
96652
96653
96654
96655
96656
96657
96658
96659
96660
96661
96662
96663
96664
96665
96666
96667
96668
96669
96670
96671
96672
96673
96674
96675
96676
96677
96678
96679
96680
96681
96682
96683
96684
96685
96686
96687
96688
96689
96690
96691
96692
96693
96694
96695
96696
96697
96698
96699
96700
96701
96702
96703
96704
96705
96706
96707
96708
96709
96710
96711
96712
96713
96714
96715
96716
96717
96718
96719
96720
96721
96722
96723
96724
96725
96726
96727
96728
96729
96730
96731
96732
96733
96734
96735
96736
96737
96738
96739
96740
96741
96742
96743
96744
96745
96746
96747
96748
96749
96750
96751
96752
96753
96754
96755
96756
96757
96758
96759
96760
96761
96762
96763
96764
96765
96766
96767
96768
96769
96770
96771
96772
96773
96774
96775
96776
96777
96778
96779
96780
96781
96782
96783
96784
96785







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  }

  *pRes = sqlite3VdbeRecordCompare(pVal->n, pVal->z, r2);
  return SQLITE_OK;
}

/************** End of vdbesort.c ********************************************/
/************** Begin file vdbevtab.c ****************************************/
/*
** 2020-03-23
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
**    May you do good and not evil.
**    May you find forgiveness for yourself and forgive others.
**    May you share freely, never taking more than you give.
**
*************************************************************************
**
** This file implements virtual-tables for examining the bytecode content
** of a prepared statement.
*/
#ifdef SQLITE_ENABLE_BYTECODE_VTAB
/* #include "sqliteInt.h" */
/* #include "vdbeInt.h" */

/* An instance of the bytecode() table-valued function.
*/
typedef struct bytecodevtab bytecodevtab;
struct bytecodevtab {
  sqlite3_vtab base;     /* Base class - must be first */
  sqlite3 *db;           /* Database connection */
  int bTablesUsed;       /* 2 for tables_used().  0 for bytecode(). */
};

/* A cursor for scanning through the bytecode
*/
typedef struct bytecodevtab_cursor bytecodevtab_cursor;
struct bytecodevtab_cursor {
  sqlite3_vtab_cursor base;  /* Base class - must be first */
  sqlite3_stmt *pStmt;       /* The statement whose bytecode is displayed */
  int iRowid;                /* The rowid of the output table */
  int iAddr;                 /* Address */
  int needFinalize;          /* Cursors owns pStmt and must finalize it */
  int showSubprograms;       /* Provide a listing of subprograms */
  Op *aOp;                   /* Operand array */
  char *zP4;                 /* Rendered P4 value */
  const char *zType;         /* tables_used.type */
  const char *zSchema;       /* tables_used.schema */
  const char *zName;         /* tables_used.name */
  Mem sub;                   /* Subprograms */
};

/*
** Create a new bytecode() table-valued function.
*/
static int bytecodevtabConnect(
  sqlite3 *db,
  void *pAux,
  int argc, const char *const*argv,
  sqlite3_vtab **ppVtab,
  char **pzErr
){
  bytecodevtab *pNew;
  int rc;
  int isTabUsed = pAux!=0;
  const char *azSchema[2] = {
    /* bytecode() schema */
    "CREATE TABLE x("
      "addr INT,"
      "opcode TEXT,"
      "p1 INT,"
      "p2 INT,"
      "p3 INT,"
      "p4 TEXT,"
      "p5 INT,"
      "comment TEXT,"
      "subprog TEXT," 
      "stmt HIDDEN"
    ");",

    /* Tables_used() schema */
    "CREATE TABLE x("
      "type TEXT,"
      "schema TEXT,"
      "name TEXT,"
      "wr INT,"
      "subprog TEXT," 
      "stmt HIDDEN"
   ");"
  };

  rc = sqlite3_declare_vtab(db, azSchema[isTabUsed]);
  if( rc==SQLITE_OK ){
    pNew = sqlite3_malloc( sizeof(*pNew) );
    *ppVtab = (sqlite3_vtab*)pNew;
    if( pNew==0 ) return SQLITE_NOMEM;
    memset(pNew, 0, sizeof(*pNew));
    pNew->db = db;
    pNew->bTablesUsed = isTabUsed*2;
  }
  return rc;
}

/*
** This method is the destructor for bytecodevtab objects.
*/
static int bytecodevtabDisconnect(sqlite3_vtab *pVtab){
  bytecodevtab *p = (bytecodevtab*)pVtab;
  sqlite3_free(p);
  return SQLITE_OK;
}

/*
** Constructor for a new bytecodevtab_cursor object.
*/
static int bytecodevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
  bytecodevtab *pVTab = (bytecodevtab*)p;
  bytecodevtab_cursor *pCur;
  pCur = sqlite3_malloc( sizeof(*pCur) );
  if( pCur==0 ) return SQLITE_NOMEM;
  memset(pCur, 0, sizeof(*pCur));
  sqlite3VdbeMemInit(&pCur->sub, pVTab->db, 1);
  *ppCursor = &pCur->base;
  return SQLITE_OK;
}

/*
** Clear all internal content from a bytecodevtab cursor.
*/
static void bytecodevtabCursorClear(bytecodevtab_cursor *pCur){
  sqlite3_free(pCur->zP4);
  pCur->zP4 = 0;
  sqlite3VdbeMemRelease(&pCur->sub);
  sqlite3VdbeMemSetNull(&pCur->sub);
  if( pCur->needFinalize ){
    sqlite3_finalize(pCur->pStmt);
  }
  pCur->pStmt = 0;
  pCur->needFinalize = 0;
  pCur->zType = 0;
  pCur->zSchema = 0;
  pCur->zName = 0;
}

/*
** Destructor for a bytecodevtab_cursor.
*/
static int bytecodevtabClose(sqlite3_vtab_cursor *cur){
  bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
  bytecodevtabCursorClear(pCur);
  sqlite3_free(pCur);
  return SQLITE_OK;
}


/*
** Advance a bytecodevtab_cursor to its next row of output.
*/
static int bytecodevtabNext(sqlite3_vtab_cursor *cur){
  bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
  bytecodevtab *pTab = (bytecodevtab*)cur->pVtab;
  int rc;
  if( pCur->zP4 ){
    sqlite3_free(pCur->zP4);
    pCur->zP4 = 0;
  }
  if( pCur->zName ){
    pCur->zName = 0;
    pCur->zType = 0;
    pCur->zSchema = 0;
  }
  rc = sqlite3VdbeNextOpcode(
           (Vdbe*)pCur->pStmt, 
           pCur->showSubprograms ? &pCur->sub : 0,
           pTab->bTablesUsed,
           &pCur->iRowid,
           &pCur->iAddr,
           &pCur->aOp);
  if( rc!=SQLITE_OK ){
    sqlite3VdbeMemSetNull(&pCur->sub);
    pCur->aOp = 0;
  }
  return SQLITE_OK;
}

/*
** Return TRUE if the cursor has been moved off of the last
** row of output.
*/
static int bytecodevtabEof(sqlite3_vtab_cursor *cur){
  bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
  return pCur->aOp==0;
}

/*
** Return values of columns for the row at which the bytecodevtab_cursor
** is currently pointing.
*/
static int bytecodevtabColumn(
  sqlite3_vtab_cursor *cur,   /* The cursor */
  sqlite3_context *ctx,       /* First argument to sqlite3_result_...() */
  int i                       /* Which column to return */
){
  bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
  bytecodevtab *pVTab = (bytecodevtab*)cur->pVtab;
  Op *pOp = pCur->aOp + pCur->iAddr;
  if( pVTab->bTablesUsed ){
    if( i==4 ){
      i = 8;
    }else{
      if( i<=2 && pCur->zType==0 ){
        Schema *pSchema;
        HashElem *k;
        int iDb = pOp->p3;
        int iRoot = pOp->p2;
        sqlite3 *db = pVTab->db;
        pSchema = db->aDb[iDb].pSchema;
        pCur->zSchema = db->aDb[iDb].zDbSName;
        for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){
          Table *pTab = (Table*)sqliteHashData(k);
          if( !IsVirtual(pTab) && pTab->tnum==iRoot ){
            pCur->zName = pTab->zName;
            pCur->zType = "table";
            break;
          }
        }
        if( pCur->zName==0 ){
          for(k=sqliteHashFirst(&pSchema->idxHash); k; k=sqliteHashNext(k)){
            Index *pIdx = (Index*)sqliteHashData(k);
            if( pIdx->tnum==iRoot ){
              pCur->zName = pIdx->zName;
              pCur->zType = "index";
            }
          }
        }
      }
      i += 10;
    }
  }
  switch( i ){
    case 0:   /* addr */
      sqlite3_result_int(ctx, pCur->iAddr);
      break;
    case 1:   /* opcode */
      sqlite3_result_text(ctx, (char*)sqlite3OpcodeName(pOp->opcode),
                          -1, SQLITE_STATIC);
      break;
    case 2:   /* p1 */
      sqlite3_result_int(ctx, pOp->p1);
      break;
    case 3:   /* p2 */
      sqlite3_result_int(ctx, pOp->p2);
      break;
    case 4:   /* p3 */
      sqlite3_result_int(ctx, pOp->p3);
      break;
    case 5:   /* p4 */
    case 7:   /* comment */
      if( pCur->zP4==0 ){
        pCur->zP4 = sqlite3VdbeDisplayP4(pVTab->db, pOp);
      }
      if( i==5 ){
        sqlite3_result_text(ctx, pCur->zP4, -1, SQLITE_STATIC);
      }else{
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
        char *zCom = sqlite3VdbeDisplayComment(pVTab->db, pOp, pCur->zP4);
        sqlite3_result_text(ctx, zCom, -1, sqlite3_free);
#endif
      }
      break;
    case 6:     /* p5 */
      sqlite3_result_int(ctx, pOp->p5);
      break;
    case 8: {   /* subprog */
      Op *aOp = pCur->aOp;
      assert( aOp[0].opcode==OP_Init );
      assert( aOp[0].p4.z==0 || strncmp(aOp[0].p4.z,"-" "- ",3)==0 );
      if( pCur->iRowid==pCur->iAddr+1 ){
        break;  /* Result is NULL for the main program */
      }else if( aOp[0].p4.z!=0 ){
         sqlite3_result_text(ctx, aOp[0].p4.z+3, -1, SQLITE_STATIC);
      }else{
         sqlite3_result_text(ctx, "(FK)", 4, SQLITE_STATIC);
      }
      break;
    }
    case 10:  /* tables_used.type */
      sqlite3_result_text(ctx, pCur->zType, -1, SQLITE_STATIC);
      break;
    case 11:  /* tables_used.schema */
      sqlite3_result_text(ctx, pCur->zSchema, -1, SQLITE_STATIC);
      break;
    case 12:  /* tables_used.name */
      sqlite3_result_text(ctx, pCur->zName, -1, SQLITE_STATIC);
      break;
    case 13:  /* tables_used.wr */
      sqlite3_result_int(ctx, pOp->opcode==OP_OpenWrite);
      break;
  }
  return SQLITE_OK;
}

/*
** Return the rowid for the current row.  In this implementation, the
** rowid is the same as the output value.
*/
static int bytecodevtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
  bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
  *pRowid = pCur->iRowid;
  return SQLITE_OK;
}

/*
** Initialize a cursor.
**
**    idxNum==0     means show all subprograms
**    idxNum==1     means show only the main bytecode and omit subprograms.
*/
static int bytecodevtabFilter(
  sqlite3_vtab_cursor *pVtabCursor, 
  int idxNum, const char *idxStr,
  int argc, sqlite3_value **argv
){
  bytecodevtab_cursor *pCur = (bytecodevtab_cursor *)pVtabCursor;
  bytecodevtab *pVTab = (bytecodevtab *)pVtabCursor->pVtab;
  int rc = SQLITE_OK;

  bytecodevtabCursorClear(pCur);
  pCur->iRowid = 0;
  pCur->iAddr = 0;
  pCur->showSubprograms = idxNum==0;
  assert( argc==1 );
  if( sqlite3_value_type(argv[0])==SQLITE_TEXT ){
    const char *zSql = (const char*)sqlite3_value_text(argv[0]);
    if( zSql==0 ){
      rc = SQLITE_NOMEM;
    }else{
      rc = sqlite3_prepare_v2(pVTab->db, zSql, -1, &pCur->pStmt, 0);
      pCur->needFinalize = 1;
    }
  }else{
    pCur->pStmt = (sqlite3_stmt*)sqlite3_value_pointer(argv[0],"stmt-pointer");
  }
  if( pCur->pStmt==0 ){
    pVTab->base.zErrMsg = sqlite3_mprintf(
       "argument to %s() is not a valid SQL statement",
       pVTab->bTablesUsed ? "tables_used" : "bytecode"
    );
    rc = SQLITE_ERROR;
  }else{
    bytecodevtabNext(pVtabCursor);
  }
  return rc;
}

/*
** We must have a single stmt=? constraint that will be passed through
** into the xFilter method.  If there is no valid stmt=? constraint,
** then return an SQLITE_CONSTRAINT error.
*/
static int bytecodevtabBestIndex(
  sqlite3_vtab *tab,
  sqlite3_index_info *pIdxInfo
){
  int i;
  int rc = SQLITE_CONSTRAINT;
  struct sqlite3_index_constraint *p;
  bytecodevtab *pVTab = (bytecodevtab*)tab;
  int iBaseCol = pVTab->bTablesUsed ? 4 : 8;
  pIdxInfo->estimatedCost = (double)100;
  pIdxInfo->estimatedRows = 100;
  pIdxInfo->idxNum = 0;
  for(i=0, p=pIdxInfo->aConstraint; i<pIdxInfo->nConstraint; i++, p++){
    if( p->usable==0 ) continue;
    if( p->op==SQLITE_INDEX_CONSTRAINT_EQ && p->iColumn==iBaseCol+1 ){
      rc = SQLITE_OK;
      pIdxInfo->aConstraintUsage[i].omit = 1;
      pIdxInfo->aConstraintUsage[i].argvIndex = 1;
    }
    if( p->op==SQLITE_INDEX_CONSTRAINT_ISNULL && p->iColumn==iBaseCol ){
      pIdxInfo->aConstraintUsage[i].omit = 1;
      pIdxInfo->idxNum = 1;
    }
  }
  return rc;
}

/*
** This following structure defines all the methods for the 
** virtual table.
*/
static sqlite3_module bytecodevtabModule = {
  /* iVersion    */ 0,
  /* xCreate     */ 0,
  /* xConnect    */ bytecodevtabConnect,
  /* xBestIndex  */ bytecodevtabBestIndex,
  /* xDisconnect */ bytecodevtabDisconnect,
  /* xDestroy    */ 0,
  /* xOpen       */ bytecodevtabOpen,
  /* xClose      */ bytecodevtabClose,
  /* xFilter     */ bytecodevtabFilter,
  /* xNext       */ bytecodevtabNext,
  /* xEof        */ bytecodevtabEof,
  /* xColumn     */ bytecodevtabColumn,
  /* xRowid      */ bytecodevtabRowid,
  /* xUpdate     */ 0,
  /* xBegin      */ 0,
  /* xSync       */ 0,
  /* xCommit     */ 0,
  /* xRollback   */ 0,
  /* xFindMethod */ 0,
  /* xRename     */ 0,
  /* xSavepoint  */ 0,
  /* xRelease    */ 0,
  /* xRollbackTo */ 0,
  /* xShadowName */ 0
};


SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3 *db){
  int rc;
  rc = sqlite3_create_module(db, "bytecode", &bytecodevtabModule, 0);
  if( rc==SQLITE_OK ){
    rc = sqlite3_create_module(db, "tables_used", &bytecodevtabModule, &db);
  }
  return rc;
}
#endif /* SQLITE_ENABLE_BYTECODE_VTAB */

/************** End of vdbevtab.c ********************************************/
/************** Begin file memjournal.c **************************************/
/*
** 2008 October 7
**
** The author disclaims copyright to this source code.  In place of
** a legal notice, here is a blessing:
**
98737
98738
98739
98740
98741
98742
98743
98744

98745
98746
98747
98748
98749
98750
98751
99418
99419
99420
99421
99422
99423
99424

99425
99426
99427
99428
99429
99430
99431
99432







-
+







** SELECT * FROM t1 WHERE a;
** SELECT a AS b FROM t1 WHERE b;
** SELECT * FROM t1 WHERE (select a from t1);
*/
SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr){
  int op;
  while( ExprHasProperty(pExpr, EP_Skip) ){
    assert( pExpr->op==TK_COLLATE );
    assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW );
    pExpr = pExpr->pLeft;
    assert( pExpr!=0 );
  }
  op = pExpr->op;
  if( op==TK_SELECT ){
    assert( pExpr->flags&EP_xIsSelect );
    return sqlite3ExprAffinity(pExpr->x.pSelect->pEList->a[0].pExpr);
98804
98805
98806
98807
98808
98809
98810
98811

98812
98813
98814
98815
98816
98817
98818
98819
98820
98821
98822
98823
98824
98825
98826
98827
98828
98829
98830

98831
98832
98833
98834
98835
98836
98837
99485
99486
99487
99488
99489
99490
99491

99492
99493
99494
99495
99496
99497
99498
99499
99500
99501
99502
99503
99504
99505
99506
99507
99508
99509
99510

99511
99512
99513
99514
99515
99516
99517
99518







-
+


















-
+







}

/*
** Skip over any TK_COLLATE operators.
*/
SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr *pExpr){
  while( pExpr && ExprHasProperty(pExpr, EP_Skip) ){
    assert( pExpr->op==TK_COLLATE );
    assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW );
    pExpr = pExpr->pLeft;
  }   
  return pExpr;
}

/*
** Skip over any TK_COLLATE operators and/or any unlikely()
** or likelihood() or likely() functions at the root of an
** expression.
*/
SQLITE_PRIVATE Expr *sqlite3ExprSkipCollateAndLikely(Expr *pExpr){
  while( pExpr && ExprHasProperty(pExpr, EP_Skip|EP_Unlikely) ){
    if( ExprHasProperty(pExpr, EP_Unlikely) ){
      assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
      assert( pExpr->x.pList->nExpr>0 );
      assert( pExpr->op==TK_FUNCTION );
      pExpr = pExpr->x.pList->a[0].pExpr;
    }else{
      assert( pExpr->op==TK_COLLATE );
      assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW );
      pExpr = pExpr->pLeft;
    }
  }   
  return pExpr;
}

/*
103174
103175
103176
103177
103178
103179
103180
103181

103182
103183
103184
103185
103186
103187
103188
103189
103190
103191
103192
103193
103194
103195
103196



103197
103198
103199
103200
103201
103202
103203
103855
103856
103857
103858
103859
103860
103861

103862
103863
103864
103865
103866
103867
103868
103869
103870
103871
103872
103873
103874
103875


103876
103877
103878
103879
103880
103881
103882
103883
103884
103885







-
+













-
-
+
+
+







#ifndef SQLITE_OMIT_TRIGGER
    case TK_RAISE: {
      assert( pExpr->affExpr==OE_Rollback 
           || pExpr->affExpr==OE_Abort
           || pExpr->affExpr==OE_Fail
           || pExpr->affExpr==OE_Ignore
      );
      if( !pParse->pTriggerTab ){
      if( !pParse->pTriggerTab && !pParse->nested ){
        sqlite3ErrorMsg(pParse,
                       "RAISE() may only be used within a trigger-program");
        return 0;
      }
      if( pExpr->affExpr==OE_Abort ){
        sqlite3MayAbort(pParse);
      }
      assert( !ExprHasProperty(pExpr, EP_IntValue) );
      if( pExpr->affExpr==OE_Ignore ){
        sqlite3VdbeAddOp4(
            v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0);
        VdbeCoverage(v);
      }else{
        sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_TRIGGER,
                              pExpr->affExpr, pExpr->u.zToken, 0, 0);
        sqlite3HaltConstraint(pParse,
             pParse->pTriggerTab ? SQLITE_CONSTRAINT_TRIGGER : SQLITE_ERROR,
             pExpr->affExpr, pExpr->u.zToken, 0, 0);
      }

      break;
    }
#endif
  }
  sqlite3ReleaseTempReg(pParse, regFree1);
104944
104945
104946
104947
104948
104949
104950
















104951
104952
104953
104954
104955
104956
104957
105626
105627
105628
105629
105630
105631
105632
105633
105634
105635
105636
105637
105638
105639
105640
105641
105642
105643
105644
105645
105646
105647
105648
105649
105650
105651
105652
105653
105654
105655







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







  renameTestSchema(pParse, zDb, iDb==1);

exit_rename_table:
  sqlite3SrcListDelete(db, pSrc);
  sqlite3DbFree(db, zName);
  db->mDbFlags = savedDbFlags;
}

/*
** Write code that will raise an error if the table described by
** zDb and zTab is not empty.
*/
static void sqlite3ErrorIfNotEmpty(
  Parse *pParse,        /* Parsing context */
  const char *zDb,      /* Schema holding the table */
  const char *zTab,     /* Table to check for empty */
  const char *zErr      /* Error message text */
){
  sqlite3NestedParse(pParse,
     "SELECT raise(ABORT,%Q) FROM \"%w\".\"%w\"",
     zErr, zDb, zTab
  );
}

/*
** This function is called after an "ALTER TABLE ... ADD" statement
** has been parsed. Argument pColDef contains the text of the new
** column definition.
**
** The Table structure pParse->pNewTable was extended to include
104997
104998
104999
105000
105001
105002
105003
105004


105005
105006
105007
105008
105009
105010
105011
105012
105013
105014
105015
105016
105017

105018
105019
105020
105021
105022

105023
105024
105025

105026
105027
105028
105029
105030
105031
105032
105033
105034
105035
105036
105037
105038
105039

105040

105041
105042
105043
105044
105045
105046

105047
105048
105049
105050
105051
105052
105053
105054
105695
105696
105697
105698
105699
105700
105701

105702
105703
105704
105705
105706
105707
105708
105709
105710
105711
105712
105713
105714
105715

105716
105717

105718
105719

105720
105721

105722
105723
105724
105725
105726
105727
105728
105729
105730
105731
105732
105733
105734
105735
105736
105737
105738

105739

105740
105741
105742
105743

105744

105745
105746
105747
105748
105749
105750
105751







-
+
+












-
+

-


-
+

-

+














+
-
+
-




-
+
-







  ** column must not be NULL.
  */
  if( pCol->colFlags & COLFLAG_PRIMKEY ){
    sqlite3ErrorMsg(pParse, "Cannot add a PRIMARY KEY column");
    return;
  }
  if( pNew->pIndex ){
    sqlite3ErrorMsg(pParse, "Cannot add a UNIQUE column");
    sqlite3ErrorMsg(pParse,
         "Cannot add a UNIQUE column");
    return;
  }
  if( (pCol->colFlags & COLFLAG_GENERATED)==0 ){
    /* If the default value for the new column was specified with a 
    ** literal NULL, then set pDflt to 0. This simplifies checking
    ** for an SQL NULL default below.
    */
    assert( pDflt==0 || pDflt->op==TK_SPAN );
    if( pDflt && pDflt->pLeft->op==TK_NULL ){
      pDflt = 0;
    }
    if( (db->flags&SQLITE_ForeignKeys) && pNew->pFKey && pDflt ){
      sqlite3ErrorMsg(pParse, 
      sqlite3ErrorIfNotEmpty(pParse, zDb, zTab,
          "Cannot add a REFERENCES column with non-NULL default value");
      return;
    }
    if( pCol->notNull && !pDflt ){
      sqlite3ErrorMsg(pParse, 
      sqlite3ErrorIfNotEmpty(pParse, zDb, zTab,
          "Cannot add a NOT NULL column with default value NULL");
      return;
    }


    /* Ensure the default expression is something that sqlite3ValueFromExpr()
    ** can handle (i.e. not CURRENT_TIME etc.)
    */
    if( pDflt ){
      sqlite3_value *pVal = 0;
      int rc;
      rc = sqlite3ValueFromExpr(db, pDflt, SQLITE_UTF8, SQLITE_AFF_BLOB, &pVal);
      assert( rc==SQLITE_OK || rc==SQLITE_NOMEM );
      if( rc!=SQLITE_OK ){
        assert( db->mallocFailed == 1 );
        return;
      }
      if( !pVal ){
        sqlite3ErrorIfNotEmpty(pParse, zDb, zTab,
        sqlite3ErrorMsg(pParse,"Cannot add a column with non-constant default");
           "Cannot add a column with non-constant default");
        return;
      }
      sqlite3ValueFree(pVal);
    }
  }else if( pCol->colFlags & COLFLAG_STORED ){
    sqlite3ErrorMsg(pParse, "cannot add a STORED column");
    sqlite3ErrorIfNotEmpty(pParse, zDb, zTab, "cannot add a STORED column");
    return;
  }


  /* Modify the CREATE TABLE statement. */
  zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n);
  if( zCol ){
    char *zEnd = &zCol[pColDef->n-1];
106603
106604
106605
106606
106607
106608
106609





106610
106611
106612
106613
106614
106615
106616
106617
106618
106619
106620
106621

106622
106623

106624
106625
106626
106627
106628
106629
106630
106631
106632
106633
106634
106635
106636
106637
106638
106639
106640
106641
106642
106643
106644
106645
106646
106647
106648
106649
106650
106651
106652
106653
106654
106655
106656
106657
106658

106659
106660
106661
106662
106663
106664
106665
107300
107301
107302
107303
107304
107305
107306
107307
107308
107309
107310
107311
107312
107313
107314
107315
107316
107317
107318
107319
107320
107321
107322
107323
107324
107325

107326
107327
107328
107329
107330
107331
107332
107333
107334
107335
107336
107337
107338
107339
107340
107341

107342
107343
107344
107345
107346
107347
107348
107349
107350
107351
107352
107353
107354
107355
107356
107357
107358
107359

107360
107361
107362
107363
107364
107365
107366
107367







+
+
+
+
+












+

-
+















-


















-
+







  };
  int i;
  sqlite3 *db = pParse->db;
  Db *pDb;
  Vdbe *v = sqlite3GetVdbe(pParse);
  int aRoot[ArraySize(aTable)];
  u8 aCreateTbl[ArraySize(aTable)];
#ifdef SQLITE_ENABLE_STAT4
  const int nToOpen = OptimizationEnabled(db,SQLITE_Stat4) ? 2 : 1;
#else
  const int nToOpen = 1;
#endif

  if( v==0 ) return;
  assert( sqlite3BtreeHoldsAllMutexes(db) );
  assert( sqlite3VdbeDb(v)==db );
  pDb = &db->aDb[iDb];

  /* Create new statistic tables if they do not exist, or clear them
  ** if they do already exist.
  */
  for(i=0; i<ArraySize(aTable); i++){
    const char *zTab = aTable[i].zName;
    Table *pStat;
    aCreateTbl[i] = 0;
    if( (pStat = sqlite3FindTable(db, zTab, pDb->zDbSName))==0 ){
      if( aTable[i].zCols ){
      if( i<nToOpen ){
        /* The sqlite_statN table does not exist. Create it. Note that a 
        ** side-effect of the CREATE TABLE statement is to leave the rootpage 
        ** of the new table in register pParse->regRoot. This is important 
        ** because the OpenWrite opcode below will be needing it. */
        sqlite3NestedParse(pParse,
            "CREATE TABLE %Q.%s(%s)", pDb->zDbSName, zTab, aTable[i].zCols
        );
        aRoot[i] = pParse->regRoot;
        aCreateTbl[i] = OPFLAG_P2ISREG;
      }
    }else{
      /* The table already exists. If zWhere is not NULL, delete all entries 
      ** associated with the table zWhere. If zWhere is NULL, delete the
      ** entire contents of the table. */
      aRoot[i] = pStat->tnum;
      aCreateTbl[i] = 0;
      sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab);
      if( zWhere ){
        sqlite3NestedParse(pParse,
           "DELETE FROM %Q.%s WHERE %s=%Q",
           pDb->zDbSName, zTab, zWhereType, zWhere
        );
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
      }else if( db->xPreUpdateCallback ){
        sqlite3NestedParse(pParse, "DELETE FROM %Q.%s", pDb->zDbSName, zTab);
#endif
      }else{
        /* The sqlite_stat[134] table already exists.  Delete all rows. */
        sqlite3VdbeAddOp2(v, OP_Clear, aRoot[i], iDb);
      }
    }
  }

  /* Open the sqlite_stat[134] tables for writing. */
  for(i=0; aTable[i].zCols; i++){
  for(i=0; i<nToOpen; i++){
    assert( i<ArraySize(aTable) );
    sqlite3VdbeAddOp4Int(v, OP_OpenWrite, iStatCur+i, aRoot[i], iDb, 3);
    sqlite3VdbeChangeP5(v, aCreateTbl[i]);
    VdbeComment((v, aTable[i].zName));
  }
}

106690
106691
106692
106693
106694
106695
106696

106697


106698
106699

106700
106701
106702
106703
106704
106705
106706
107392
107393
107394
107395
107396
107397
107398
107399

107400
107401
107402
107403
107404
107405
107406
107407
107408
107409
107410
107411







+
-
+
+


+







  u8 isPSample;                   /* True if a periodic sample */
  int iCol;                       /* If !isPSample, the reason for inclusion */
  u32 iHash;                      /* Tiebreaker hash */
#endif
};                                                    
struct StatAccum {
  sqlite3 *db;              /* Database connection, for malloc() */
  tRowcnt nEst;             /* Estimated number of rows */
  tRowcnt nRow;             /* Number of rows in the entire table */
  tRowcnt nRow;             /* Number of rows visited so far */
  int nLimit;               /* Analysis row-scan limit */
  int nCol;                 /* Number of columns in index + pk/rowid */
  int nKeyCol;              /* Number of index columns w/o the pk/rowid */
  u8 nSkipAhead;            /* Number of times of skip-ahead */
  StatSample current;       /* Current row as a StatSample */
#ifdef SQLITE_ENABLE_STAT4
  tRowcnt nPSample;         /* How often to do a periodic sample */
  int mxSample;             /* Maximum number of samples to accumulate */
  u32 iPrn;                 /* Pseudo-random number used for sampling */
  StatSample *aBest;        /* Array of nCol best samples */
  int iMin;                 /* Index in a[] of entry with minimum score */
106772
106773
106774
106775
106776
106777
106778

106779
106780
106781
106782





106783
106784
106785
106786
106787
106788

106789
106790
106791
106792


106793
106794
106795
106796
106797
106798
106799
106800
106801
106802
106803
106804
106805
106806
106807
106808
106809
106810
106811
106812
106813
106814
106815
106816
106817
106818
106819
106820

106821

106822

106823
106824
106825
106826
106827
106828
106829
106830
106831
106832
106833
106834
106835
106836
106837

106838

106839
106840
106841




106842
106843
106844
106845
106846
106847
106848
106849
106850
106851

106852

106853
106854

106855
106856
106857
106858
106859


106860
106861
106862
106863
106864
106865

106866
106867
106868
106869
106870
106871
106872
107477
107478
107479
107480
107481
107482
107483
107484




107485
107486
107487
107488
107489
107490
107491
107492
107493
107494

107495
107496
107497
107498

107499
107500
107501
107502
107503
107504
107505


107506
107507
107508
107509
107510
107511
107512
107513
107514
107515
107516
107517
107518
107519
107520
107521
107522
107523
107524
107525

107526
107527
107528

107529
107530
107531
107532
107533
107534
107535
107536
107537
107538
107539
107540
107541
107542
107543

107544
107545
107546



107547
107548
107549
107550
107551

107552
107553
107554
107555
107556
107557
107558
107559
107560
107561
107562
107563
107564
107565
107566
107567
107568
107569

107570
107571
107572
107573
107574
107575


107576
107577
107578
107579
107580
107581
107582
107583







+
-
-
-
-
+
+
+
+
+





-
+



-
+
+





-
-




















-
+

+
-
+














-
+

+
-
-
-
+
+
+
+

-








+

+


+




-
+
+




-
-
+








/*
** Reclaim all memory of a StatAccum structure.
*/
static void statAccumDestructor(void *pOld){
  StatAccum *p = (StatAccum*)pOld;
#ifdef SQLITE_ENABLE_STAT4
  if( p->mxSample ){
  int i;
  for(i=0; i<p->nCol; i++) sampleClear(p->db, p->aBest+i);
  for(i=0; i<p->mxSample; i++) sampleClear(p->db, p->a+i);
  sampleClear(p->db, &p->current);
    int i;
    for(i=0; i<p->nCol; i++) sampleClear(p->db, p->aBest+i);
    for(i=0; i<p->mxSample; i++) sampleClear(p->db, p->a+i);
    sampleClear(p->db, &p->current);
  }
#endif
  sqlite3DbFree(p->db, p);
}

/*
** Implementation of the stat_init(N,K,C) SQL function. The three parameters
** Implementation of the stat_init(N,K,C,L) SQL function. The four parameters
** are:
**     N:    The number of columns in the index including the rowid/pk (note 1)
**     K:    The number of columns in the index excluding the rowid/pk.
**     C:    The number of rows in the index (note 2)
**     C:    Estimated number of rows in the index
**     L:    A limit on the number of rows to scan, or 0 for no-limit 
**
** Note 1:  In the special case of the covering index that implements a
** WITHOUT ROWID table, N is the number of PRIMARY KEY columns, not the
** total number of columns in the table.
**
** Note 2:  C is only used for STAT4.
**
** For indexes on ordinary rowid tables, N==K+1.  But for indexes on
** WITHOUT ROWID tables, N=K+P where P is the number of columns in the
** PRIMARY KEY of the table.  The covering index that implements the
** original WITHOUT ROWID table as N==K as a special case.
**
** This routine allocates the StatAccum object in heap memory. The return 
** value is a pointer to the StatAccum object.  The datatype of the
** return value is BLOB, but it is really just a pointer to the StatAccum
** object.
*/
static void statInit(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  StatAccum *p;
  int nCol;                       /* Number of columns in index being sampled */
  int nKeyCol;                    /* Number of key columns */
  int nColUp;                     /* nCol rounded up for alignment */
  int n;                          /* Bytes of space to allocate */
  sqlite3 *db;                    /* Database connection */
  sqlite3 *db = sqlite3_context_db_handle(context);   /* Database connection */
#ifdef SQLITE_ENABLE_STAT4
  /* Maximum number of samples.  0 if STAT4 data is not collected */
  int mxSample = SQLITE_STAT4_SAMPLES;
  int mxSample = OptimizationEnabled(db,SQLITE_Stat4) ?SQLITE_STAT4_SAMPLES :0;
#endif

  /* Decode the three function arguments */
  UNUSED_PARAMETER(argc);
  nCol = sqlite3_value_int(argv[0]);
  assert( nCol>0 );
  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 */
    + sizeof(tRowcnt)*nColUp;                 /* StatAccum.anDLt */
#ifdef SQLITE_ENABLE_STAT4
  if( mxSample ){
    + sizeof(tRowcnt)*nColUp                  /* StatAccum.anLt */
    + sizeof(StatSample)*(nCol+mxSample)      /* StatAccum.aBest[], a[] */
    + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample)
    n += sizeof(tRowcnt)*nColUp                  /* StatAccum.anLt */
      + sizeof(StatSample)*(nCol+mxSample)       /* StatAccum.aBest[], a[] */
      + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample);
  }
#endif
  ;
  db = sqlite3_context_db_handle(context);
  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->mxSample = mxSample;
    p->nPSample = (tRowcnt)(sqlite3_value_int64(argv[2])/(mxSample/3+1) + 1);
    p->nPSample = (tRowcnt)(p->nEst/(mxSample/3+1) + 1);
    p->current.anLt = &p->current.anEq[nColUp];
    p->iPrn = 0x689e962d*(u32)nCol ^ 0xd0944565*(u32)sqlite3_value_int(argv[2]);
  
    /* Set up the StatAccum.a[] and aBest[] arrays */
    p->a = (struct StatSample*)&p->current.anLt[nColUp];
    p->aBest = &p->a[mxSample];
    pSpace = (u8*)(&p->a[mxSample+nCol]);
106886
106887
106888
106889
106890
106891
106892
106893

106894
106895
106896
106897
106898
106899
106900
107597
107598
107599
107600
107601
107602
107603

107604
107605
107606
107607
107608
107609
107610
107611







-
+







  /* Return a pointer to the allocated object to the caller.  Note that
  ** only the pointer (the 2nd parameter) matters.  The size of the object
  ** (given by the 3rd parameter) is never used and can be any positive
  ** value. */
  sqlite3_result_blob(context, p, sizeof(*p), statAccumDestructor);
}
static const FuncDef statInitFuncdef = {
  2+IsStat4,       /* nArg */
  4,               /* nArg */
  SQLITE_UTF8,     /* funcFlags */
  0,               /* pUserData */
  0,               /* pNext */
  statInit,        /* xSFunc */
  0,               /* xFinalize */
  0, 0,            /* xValue, xInverse */
  "stat_init",     /* zName */
107090
107091
107092
107093
107094
107095
107096
107097
107098
107099
107100







107101
107102
107103
107104
107105
107106
107107
107801
107802
107803
107804
107805
107806
107807




107808
107809
107810
107811
107812
107813
107814
107815
107816
107817
107818
107819
107820
107821







-
-
-
-
+
+
+
+
+
+
+







** Arguments:
**
**    P     Pointer to the StatAccum object created by stat_init()
**    C     Index of left-most column to differ from previous row
**    R     Rowid for the current row.  Might be a key record for
**          WITHOUT ROWID tables.
**
** This SQL function always returns NULL.  It's purpose it to accumulate
** statistical data and/or samples in the StatAccum object about the
** index being analyzed.  The stat_get() SQL function will later be used to
** extract relevant information for constructing the sqlite_statN tables.
** The purpose of this routine is to collect statistical data and/or
** samples from the index being analyzed into the StatAccum object.
** The stat_get() SQL function will be used afterwards to
** retrieve the information gathered.
**
** This SQL function usually returns NULL, but might return an integer
** if it wants the byte-code to do special processing.
**
** The R parameter is only used for STAT4
*/
static void statPush(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
107119
107120
107121
107122
107123
107124
107125
107126

107127
107128
107129
107130
107131
107132
107133
107134
107135
107136
107137

107138
107139
107140
107141

107142
107143


107144
107145
107146
107147
107148
107149
107150







107151
107152
107153
107154
107155

107156
107157
107158
107159
107160
107161
107162
107163
107164
107165
107166
107167
107168
107169
107170
107171
107172

107173



107174



107175
107176
107177
107178
107179
107180
107181
107833
107834
107835
107836
107837
107838
107839

107840
107841
107842
107843
107844
107845
107846
107847
107848
107849
107850

107851
107852
107853
107854
107855
107856
107857
107858
107859
107860







107861
107862
107863
107864
107865
107866
107867

107868



107869

107870
107871
107872
107873
107874
107875
107876
107877
107878
107879
107880
107881
107882
107883
107884

107885
107886
107887
107888
107889

107890
107891
107892
107893
107894
107895
107896
107897
107898
107899







-
+










-
+




+


+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-

-
-
-
+
-















-
+

+
+
+
-
+
+
+








  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
    samplePushPrevious(p, iChng);
    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
      p->current.anLt[i] += p->current.anEq[i];
      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;
  if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){
    sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2]));
  }else{
    sampleSetRowid(p->db, &p->current, sqlite3_value_bytes(argv[2]),
                                       sqlite3_value_blob(argv[2]));
  }
  p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345;
    if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){
      sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2]));
    }else{
      sampleSetRowid(p->db, &p->current, sqlite3_value_bytes(argv[2]),
                                         sqlite3_value_blob(argv[2]));
    }
    p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345;
#endif

#ifdef SQLITE_ENABLE_STAT4
  {
    tRowcnt nLt = p->current.anLt[p->nCol-1];
    nLt = p->current.anLt[p->nCol-1];

    /* Check if this is to be a periodic sample. If so, add it. */
    if( (nLt/p->nPSample)!=(nLt+1)/p->nPSample ){
      p->current.isPSample = 1;
      p->current.iCol = 0;
      sampleInsert(p, &p->current, p->nCol-1);
      p->current.isPSample = 0;
    }

    /* Update the aBest[] array. */
    for(i=0; i<(p->nCol-1); i++){
      p->current.iCol = i;
      if( i>=iChng || sampleIsBetterPost(p, &p->current, &p->aBest[i]) ){
        sampleCopy(p, &p->aBest[i], &p->current);
      }
    }
  }
  }else
#endif
  if( p->nLimit && p->nRow>(tRowcnt)p->nLimit*(p->nSkipAhead+1) ){
    p->nSkipAhead++;
    sqlite3_result_int(context, p->current.anDLt[0]>0);
}
  }
}

static const FuncDef statPushFuncdef = {
  2+IsStat4,       /* nArg */
  SQLITE_UTF8,     /* funcFlags */
  0,               /* pUserData */
  0,               /* pNext */
  statPush,        /* xSFunc */
  0,               /* xFinalize */
107219
107220
107221
107222
107223
107224
107225

107226
107227
107228
107229
107230
107231
107232
107937
107938
107939
107940
107941
107942
107943
107944
107945
107946
107947
107948
107949
107950
107951







+







  /* STAT4 has a parameter on this routine. */
  int eCall = sqlite3_value_int(argv[1]);
  assert( argc==2 );
  assert( eCall==STAT_GET_STAT1 || eCall==STAT_GET_NEQ 
       || eCall==STAT_GET_ROWID || eCall==STAT_GET_NLT
       || eCall==STAT_GET_NDLT 
  );
  assert( eCall==STAT_GET_STAT1 || p->mxSample );
  if( eCall==STAT_GET_STAT1 )
#else
  assert( argc==1 );
#endif
  {
    /* Return the value to store in the "stat" column of the sqlite_stat1
    ** table for this index.
107254
107255
107256
107257
107258
107259
107260
107261


107262
107263
107264
107265
107266
107267
107268
107973
107974
107975
107976
107977
107978
107979

107980
107981
107982
107983
107984
107985
107986
107987
107988







-
+
+








    char *zRet = sqlite3MallocZero( (p->nKeyCol+1)*25 );
    if( zRet==0 ){
      sqlite3_result_error_nomem(context);
      return;
    }

    sqlite3_snprintf(24, zRet, "%llu", (u64)p->nRow);
    sqlite3_snprintf(24, zRet, "%llu", 
        p->nSkipAhead ? (u64)p->nEst : (u64)p->nRow);
    z = zRet + sqlite3Strlen30(zRet);
    for(i=0; i<p->nKeyCol; i++){
      u64 nDistinct = p->current.anDLt[i] + 1;
      u64 iVal = (p->nRow + nDistinct - 1) / nDistinct;
      sqlite3_snprintf(24, z, " %llu", iVal);
      z += sqlite3Strlen30(z);
      assert( p->current.anEq[i] );
107330
107331
107332
107333
107334
107335
107336
107337

107338
107339

107340
107341
107342
107343
107344
107345
107346


107347
107348
107349
107350
107351
107352
107353
108050
108051
108052
108053
108054
108055
108056

108057
108058

108059
108060
108061
108062
108063
108064


108065
108066
108067
108068
108069
108070
108071
108072
108073







-
+

-
+





-
-
+
+







  statGet,         /* xSFunc */
  0,               /* xFinalize */
  0, 0,            /* xValue, xInverse */
  "stat_get",      /* zName */
  {0}
};

static void callStatGet(Parse *pParse, int regStat4, int iParam, int regOut){
static void callStatGet(Parse *pParse, int regStat, int iParam, int regOut){
#ifdef SQLITE_ENABLE_STAT4
  sqlite3VdbeAddOp2(pParse->pVdbe, OP_Integer, iParam, regStat4+1);
  sqlite3VdbeAddOp2(pParse->pVdbe, OP_Integer, iParam, regStat+1);
#elif SQLITE_DEBUG
  assert( iParam==STAT_GET_STAT1 );
#else
  UNUSED_PARAMETER( iParam );
#endif
  assert( regOut!=regStat4 && regOut!=regStat4+1 );
  sqlite3VdbeAddFunctionCall(pParse, 0, regStat4, regOut, 1+IsStat4,
  assert( regOut!=regStat && regOut!=regStat+1 );
  sqlite3VdbeAddFunctionCall(pParse, 0, regStat, regOut, 1+IsStat4,
                             &statGetFuncdef, 0);
}

/*
** Generate code to do an analysis of all indices associated with
** a single table.
*/
107365
107366
107367
107368
107369
107370
107371
107372

107373
107374
107375
107376
107377

107378
107379
107380
107381
107382
107383
107384
108085
108086
108087
108088
108089
108090
108091

108092
108093

108094

108095
108096
108097
108098
108099
108100
108101
108102
108103







-
+

-

-

+







  int iTabCur;                 /* Table cursor */
  Vdbe *v;                     /* The virtual machine being built up */
  int i;                       /* Loop counter */
  int jZeroRows = -1;          /* Jump from here if number of rows is zero */
  int iDb;                     /* Index of database containing pTab */
  u8 needTableCnt = 1;         /* True to count the table */
  int regNewRowid = iMem++;    /* Rowid for the inserted record */
  int regStat4 = iMem++;       /* Register to hold StatAccum object */
  int regStat = iMem++;        /* Register to hold StatAccum object */
  int regChng = iMem++;        /* Index of changed index field */
#ifdef SQLITE_ENABLE_STAT4
  int regRowid = iMem++;       /* Rowid argument passed to stat_push() */
#endif
  int regTemp = iMem++;        /* Temporary use register */
  int regTemp2 = iMem++;       /* Second temporary use register */
  int regTabname = iMem++;     /* Register containing table name */
  int regIdxname = iMem++;     /* Register containing index name */
  int regStat1 = iMem++;       /* Value for the stat column of sqlite_stat1 */
  int regPrev = iMem;          /* MUST BE LAST (see below) */
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
  Table *pStat1 = 0; 
#endif
107498
107499
107500
107501
107502
107503
107504
107505

107506
107507
107508
107509



107510

107511




107512

107513
107514
107515







107516
107517
107518
107519
107520
107521
107522
107523
107524
107525
107526
107527
107528
107529
107530
107531
107532
107533
107534
108217
108218
108219
108220
108221
108222
108223

108224



108225
108226
108227
108228
108229
108230

108231
108232
108233
108234
108235
108236



108237
108238
108239
108240
108241
108242
108243
108244
108245
108246
108247
108248
108249
108250
108251
108252
108253


108254
108255
108256
108257
108258
108259
108260







-
+
-
-
-

+
+
+

+
-
+
+
+
+

+
-
-
-
+
+
+
+
+
+
+










-
-







    VdbeComment((v, "%s", pIdx->zName));

    /* Invoke the stat_init() function. The arguments are:
    ** 
    **    (1) the number of columns in the index including the rowid
    **        (or for a WITHOUT ROWID table, the number of PK columns),
    **    (2) the number of columns in the key without the rowid/pk
    **    (3) the number of rows in the index,
    **    (3) estimated number of rows in the index,
    **
    **
    ** The third argument is only used for STAT4
    */
    sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat+1);
    assert( regRowid==regStat+2 );
    sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regRowid);
#ifdef SQLITE_ENABLE_STAT4
    if( OptimizationEnabled(db, SQLITE_Stat4) ){
    sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat4+3);
      sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regTemp);
      addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
      VdbeCoverage(v);
    }else
#endif
    {
    sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat4+1);
    sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regStat4+2);
    sqlite3VdbeAddFunctionCall(pParse, 0, regStat4+1, regStat4, 2+IsStat4,
      addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
      VdbeCoverage(v);
      sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, 1);
    }
    assert( regTemp2==regStat+4 );
    sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2);
    sqlite3VdbeAddFunctionCall(pParse, 0, regStat+1, regStat, 4,
                               &statInitFuncdef, 0);

    /* Implementation of the following:
    **
    **   Rewind csr
    **   if eof(csr) goto end_of_scan;
    **   regChng = 0
    **   goto next_push_0;
    **
    */
    addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
    VdbeCoverage(v);
    sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng);
    addrNextRow = sqlite3VdbeCurrentAddr(v);

    if( nColTest>0 ){
      int endDistinctTest = sqlite3VdbeMakeLabel(pParse);
      int *aGotoChng;               /* Array of jump instruction addresses */
      aGotoChng = sqlite3DbMallocRawNN(db, sizeof(int)*nColTest);
107553
107554
107555
107556
107557
107558
107559

107560
107561
107562
107563
107564
107565
107566
107567
107568
107569
107570
107571
107572
107573
107574
107575
107576
107577
107578
107579

107580
107581
107582
107583
107584
107585
107586
107587
107588
107589
107590
107591
107592

107593
107594
107595
107596
107597
107598
107599
107600
107601
107602
107603
107604
107605
107606
107607
107608

















107609
107610
107611
107612
107613
107614



















107615
107616

107617
107618
107619
107620
107621
107622
107623
107624
107625
107626
107627
107628

107629
107630
107631
107632
107633
107634
107635
107636
107637
107638
107639
107640
107641
107642

107643
107644
107645
107646
107647



107648
107649
107650
107651
107652
107653
107654
108279
108280
108281
108282
108283
108284
108285
108286
108287
108288
108289
108290
108291
108292
108293
108294
108295
108296
108297
108298
108299
108300
108301
108302
108303
108304
108305
108306
108307
108308
108309
108310
108311
108312
108313
108314
108315
108316
108317
108318
108319
108320
108321
















108322
108323
108324
108325
108326
108327
108328
108329
108330
108331
108332
108333
108334
108335
108336
108337
108338
108339





108340
108341
108342
108343
108344
108345
108346
108347
108348
108349
108350
108351
108352
108353
108354
108355
108356
108357
108358
108359

108360
108361
108362
108363
108364
108365
108366
108367
108368
108369
108370
108371

108372
108373
108374
108375
108376
108377
108378
108379
108380
108381
108382
108383
108384
108385

108386
108387
108388



108389
108390
108391
108392
108393
108394
108395
108396
108397
108398







+




















+













+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+











-
+













-
+


-
-
-
+
+
+







        sqlite3VdbeAddOp2(v, OP_NotNull, regPrev, endDistinctTest);
        VdbeCoverage(v);
      }
      for(i=0; i<nColTest; i++){
        char *pColl = (char*)sqlite3LocateCollSeq(pParse, pIdx->azColl[i]);
        sqlite3VdbeAddOp2(v, OP_Integer, i, regChng);
        sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regTemp);
        VdbeComment((v, "%s.column(%d)", pIdx->zName, i));
        aGotoChng[i] = 
        sqlite3VdbeAddOp4(v, OP_Ne, regTemp, 0, regPrev+i, pColl, P4_COLLSEQ);
        sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
        VdbeCoverage(v);
      }
      sqlite3VdbeAddOp2(v, OP_Integer, nColTest, regChng);
      sqlite3VdbeGoto(v, endDistinctTest);
  
  
      /*
      **  chng_addr_0:
      **   regPrev(0) = idx(0)
      **  chng_addr_1:
      **   regPrev(1) = idx(1)
      **  ...
      */
      sqlite3VdbeJumpHere(v, addrNextRow-1);
      for(i=0; i<nColTest; i++){
        sqlite3VdbeJumpHere(v, aGotoChng[i]);
        sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regPrev+i);
        VdbeComment((v, "%s.column(%d)", pIdx->zName, i));
      }
      sqlite3VdbeResolveLabel(v, endDistinctTest);
      sqlite3DbFree(db, aGotoChng);
    }
  
    /*
    **  chng_addr_N:
    **   regRowid = idx(rowid)            // STAT4 only
    **   stat_push(P, regChng, regRowid)  // 3rd parameter STAT4 only
    **   Next csr
    **   if !eof(csr) goto next_row;
    */
#ifdef SQLITE_ENABLE_STAT4
    if( OptimizationEnabled(db, SQLITE_Stat4) ){
    assert( regRowid==(regStat4+2) );
    if( HasRowid(pTab) ){
      sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid);
    }else{
      Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
      int j, k, regKey;
      regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol);
      for(j=0; j<pPk->nKeyCol; j++){
        k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[j]);
        assert( k>=0 && k<pIdx->nColumn );
        sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j);
        VdbeComment((v, "%s", pTab->aCol[pPk->aiColumn[j]].zName));
      }
      sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid);
      sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol);
    }
      assert( regRowid==(regStat+2) );
      if( HasRowid(pTab) ){
        sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid);
      }else{
        Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
        int j, k, regKey;
        regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol);
        for(j=0; j<pPk->nKeyCol; j++){
          k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[j]);
          assert( k>=0 && k<pIdx->nColumn );
          sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j);
          VdbeComment((v, "%s.column(%d)", pIdx->zName, i));
        }
        sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid);
        sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol);
      }
    }
#endif
    assert( regChng==(regStat4+1) );
    sqlite3VdbeAddFunctionCall(pParse, 1, regStat4, regTemp, 2+IsStat4,
                               &statPushFuncdef, 0);
    sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);

    assert( regChng==(regStat+1) );
    {
      sqlite3VdbeAddFunctionCall(pParse, 1, regStat, regTemp, 2+IsStat4,
                                 &statPushFuncdef, 0);
      if( db->nAnalysisLimit ){
        int j1, j2, j3;
        j1 = sqlite3VdbeAddOp1(v, OP_IsNull, regTemp); VdbeCoverage(v);
        j2 = sqlite3VdbeAddOp1(v, OP_If, regTemp); VdbeCoverage(v);
        j3 = sqlite3VdbeAddOp4Int(v, OP_SeekGT, iIdxCur, 0, regPrev, 1);
        VdbeCoverage(v);
        sqlite3VdbeJumpHere(v, j1);
        sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
        sqlite3VdbeJumpHere(v, j2);
        sqlite3VdbeJumpHere(v, j3);
      }else{
        sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
      }
    }

    /* Add the entry to the stat1 table. */
    callStatGet(pParse, regStat4, STAT_GET_STAT1, regStat1);
    callStatGet(pParse, regStat, STAT_GET_STAT1, regStat1);
    assert( "BBB"[0]==SQLITE_AFF_TEXT );
    sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0);
    sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
    sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid);
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
    sqlite3VdbeChangeP4(v, -1, (char*)pStat1, P4_TABLE);
#endif
    sqlite3VdbeChangeP5(v, OPFLAG_APPEND);

    /* Add the entries to the stat4 table. */
#ifdef SQLITE_ENABLE_STAT4
    {
    if( OptimizationEnabled(db, SQLITE_Stat4) && db->nAnalysisLimit==0 ){
      int regEq = regStat1;
      int regLt = regStat1+1;
      int regDLt = regStat1+2;
      int regSample = regStat1+3;
      int regCol = regStat1+4;
      int regSampleRowid = regCol + nCol;
      int addrNext;
      int addrIsNull;
      u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound;

      pParse->nMem = MAX(pParse->nMem, regCol+nCol);

      addrNext = sqlite3VdbeCurrentAddr(v);
      callStatGet(pParse, regStat4, STAT_GET_ROWID, regSampleRowid);
      callStatGet(pParse, regStat, STAT_GET_ROWID, regSampleRowid);
      addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regSampleRowid);
      VdbeCoverage(v);
      callStatGet(pParse, regStat4, STAT_GET_NEQ, regEq);
      callStatGet(pParse, regStat4, STAT_GET_NLT, regLt);
      callStatGet(pParse, regStat4, STAT_GET_NDLT, regDLt);
      callStatGet(pParse, regStat, STAT_GET_NEQ, regEq);
      callStatGet(pParse, regStat, STAT_GET_NLT, regLt);
      callStatGet(pParse, regStat, STAT_GET_NDLT, regDLt);
      sqlite3VdbeAddOp4Int(v, seekOp, iTabCur, addrNext, regSampleRowid, 0);
      VdbeCoverage(v);
      for(i=0; i<nCol; i++){
        sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iTabCur, i, regCol+i);
      }
      sqlite3VdbeAddOp3(v, OP_MakeRecord, regCol, nCol, regSample);
      sqlite3VdbeAddOp3(v, OP_MakeRecord, regTabname, 6, regTemp);
113816
113817
113818
113819
113820
113821
113822
113823

113824
113825
113826
113827
113828
113829
113830
114560
114561
114562
114563
114564
114565
114566

114567
114568
114569
114570
114571
114572
114573
114574







-
+







      sqlite3ErrorMsg(pParse, "unable to open a temporary database "
        "file for storing temporary tables");
      pParse->rc = rc;
      return 1;
    }
    db->aDb[1].pBt = pBt;
    assert( db->aDb[1].pSchema );
    if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, -1, 0) ){
    if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, 0, 0) ){
      sqlite3OomFault(db);
      return 1;
    }
  }
  return 0;
}

113927
113928
113929
113930
113931
113932
113933
113934

113935
113936
113937
113938
113939
113940
113941
114671
114672
114673
114674
114675
114676
114677

114678
114679
114680
114681
114682
114683
114684
114685







-
+







  int errCode,      /* extended error code */
  int onError,      /* Constraint type */
  char *p4,         /* Error message */
  i8 p4type,        /* P4_STATIC or P4_TRANSIENT */
  u8 p5Errmsg       /* P5_ErrMsg type */
){
  Vdbe *v = sqlite3GetVdbe(pParse);
  assert( (errCode&0xff)==SQLITE_CONSTRAINT );
  assert( (errCode&0xff)==SQLITE_CONSTRAINT || pParse->nested );
  if( onError==OE_Abort ){
    sqlite3MayAbort(pParse);
  }
  sqlite3VdbeAddOp4(v, OP_Halt, errCode, onError, 0, p4, p4type);
  sqlite3VdbeChangeP5(v, p5Errmsg);
}

115640
115641
115642
115643
115644
115645
115646

115647
115648
115649
115650
115651
115652
115653
116384
116385
116386
116387
116388
116389
116390
116391
116392
116393
116394
116395
116396
116397
116398







+







    if( pIdx==pPk ) continue;
    if( iIdxCur+i==iIdxNoSeek ) continue;
    VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName));
    r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1,
        &iPartIdxLabel, pPrior, r1);
    sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1,
        pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn);
    sqlite3VdbeChangeP5(v, 1);  /* Cause IdxDelete to error if no entry found */
    sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel);
    pPrior = pIdx;
  }
}

/*
** Generate code that will assemble an index key and stores it in register
122627
122628
122629
122630
122631
122632
122633

122634
122635
122636
122637
122638
122639
122640
123372
123373
123374
123375
123376
123377
123378
123379
123380
123381
123382
123383
123384
123385
123386







+







  const char *(*filename_database)(const char*);
  const char *(*filename_journal)(const char*);
  const char *(*filename_wal)(const char*);
  /* Version 3.32.0 and later */
  char *(*create_filename)(const char*,const char*,const char*,
                           int,const char**);
  void (*free_filename)(char*);
  sqlite3_file *(*database_file_object)(const char*);
};

/*
** This is the function signature used for all extension entry points.  It
** is also defined in the file "loadext.c".
*/
typedef int (*sqlite3_loadext_entry)(
122930
122931
122932
122933
122934
122935
122936

122937
122938
122939
122940
122941
122942
122943
123676
123677
123678
123679
123680
123681
123682
123683
123684
123685
123686
123687
123688
123689
123690







+







#define sqlite3_uri_key                sqlite3_api->uri_key
#define sqlite3_filename_database      sqlite3_api->filename_database
#define sqlite3_filename_journal       sqlite3_api->filename_journal
#define sqlite3_filename_wal           sqlite3_api->filename_wal
/* Version 3.32.0 and later */
#define sqlite3_create_filename        sqlite3_api->create_filename
#define sqlite3_free_filename          sqlite3_api->free_filename
#define sqlite3_database_file_object   sqlite3_api->database_file_object
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */

#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
  /* This case when the file really is being compiled as a loadable 
  ** extension */
# define SQLITE_EXTENSION_INIT1     const sqlite3_api_routines *sqlite3_api=0;
# define SQLITE_EXTENSION_INIT2(v)  sqlite3_api=v;
123411
123412
123413
123414
123415
123416
123417

123418








123419
123420
123421
123422
123423
123424
123425
124158
124159
124160
124161
124162
124163
124164
124165
124166
124167
124168
124169
124170
124171
124172
124173
124174
124175
124176
124177
124178
124179
124180
124181







+

+
+
+
+
+
+
+
+







  sqlite3_uri_key,
  sqlite3_filename_database,
  sqlite3_filename_journal,
  sqlite3_filename_wal,
  /* Version 3.32.0 and later */
  sqlite3_create_filename,
  sqlite3_free_filename,
  sqlite3_database_file_object,
};

/* True if x is the directory separator character
*/
#if SQLITE_OS_WIN
# define DirSep(X)  ((X)=='/'||(X)=='\\')
#else
# define DirSep(X)  ((X)=='/')
#endif

/*
** Attempt to load an SQLite extension library contained in the file
** zFile.  The entry point is zProc.  zProc may be 0 in which case a
** default entry point name (sqlite3_extension_init) is used.  Use
** of the default name is recommended.
**
123514
123515
123516
123517
123518
123519
123520
123521

123522
123523
123524
123525
123526
123527
123528
124270
124271
124272
124273
124274
124275
124276

124277
124278
124279
124280
124281
124282
124283
124284







-
+







    int ncFile = sqlite3Strlen30(zFile);
    zAltEntry = sqlite3_malloc64(ncFile+30);
    if( zAltEntry==0 ){
      sqlite3OsDlClose(pVfs, handle);
      return SQLITE_NOMEM_BKPT;
    }
    memcpy(zAltEntry, "sqlite3_", 8);
    for(iFile=ncFile-1; iFile>=0 && zFile[iFile]!='/'; iFile--){}
    for(iFile=ncFile-1; iFile>=0 && !DirSep(zFile[iFile]); iFile--){}
    iFile++;
    if( sqlite3_strnicmp(zFile+iFile, "lib", 3)==0 ) iFile += 3;
    for(iEntry=8; (c = zFile[iFile])!=0 && c!='.'; iFile++){
      if( sqlite3Isalpha(c) ){
        zAltEntry[iEntry++] = (char)sqlite3UpperToLower[(unsigned)c];
      }
    }
123818
123819
123820
123821
123822
123823
123824

123825
123826
123827
123828
123829
123830
123831
123832
123833
123834
123835
123836
123837
123838
123839
123840
123841
123842
123843
123844
123845
123846
123847
123848
123849
123850
123851
123852
123853
123854
123855
123856
123857
123858
123859
123860
123861
123862
123863
123864
123865
123866
123867











































123868
123869
123870
123871
123872
123873
123874
124574
124575
124576
124577
124578
124579
124580
124581











































124582
124583
124584
124585
124586
124587
124588
124589
124590
124591
124592
124593
124594
124595
124596
124597
124598
124599
124600
124601
124602
124603
124604
124605
124606
124607
124608
124609
124610
124611
124612
124613
124614
124615
124616
124617
124618
124619
124620
124621
124622
124623
124624
124625
124626
124627
124628
124629
124630
124631







+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







** This file is automatically generated by the script at
** ../tool/mkpragmatab.tcl.  To update the set of pragmas, edit
** that script and rerun it.
*/

/* The various pragma types */
#define PragTyp_ACTIVATE_EXTENSIONS            0
#define PragTyp_ANALYSIS_LIMIT                 1
#define PragTyp_HEADER_VALUE                   1
#define PragTyp_AUTO_VACUUM                    2
#define PragTyp_FLAG                           3
#define PragTyp_BUSY_TIMEOUT                   4
#define PragTyp_CACHE_SIZE                     5
#define PragTyp_CACHE_SPILL                    6
#define PragTyp_CASE_SENSITIVE_LIKE            7
#define PragTyp_COLLATION_LIST                 8
#define PragTyp_COMPILE_OPTIONS                9
#define PragTyp_DATA_STORE_DIRECTORY          10
#define PragTyp_DATABASE_LIST                 11
#define PragTyp_DEFAULT_CACHE_SIZE            12
#define PragTyp_ENCODING                      13
#define PragTyp_FOREIGN_KEY_CHECK             14
#define PragTyp_FOREIGN_KEY_LIST              15
#define PragTyp_FUNCTION_LIST                 16
#define PragTyp_HARD_HEAP_LIMIT               17
#define PragTyp_INCREMENTAL_VACUUM            18
#define PragTyp_INDEX_INFO                    19
#define PragTyp_INDEX_LIST                    20
#define PragTyp_INTEGRITY_CHECK               21
#define PragTyp_JOURNAL_MODE                  22
#define PragTyp_JOURNAL_SIZE_LIMIT            23
#define PragTyp_LOCK_PROXY_FILE               24
#define PragTyp_LOCKING_MODE                  25
#define PragTyp_PAGE_COUNT                    26
#define PragTyp_MMAP_SIZE                     27
#define PragTyp_MODULE_LIST                   28
#define PragTyp_OPTIMIZE                      29
#define PragTyp_PAGE_SIZE                     30
#define PragTyp_PRAGMA_LIST                   31
#define PragTyp_SECURE_DELETE                 32
#define PragTyp_SHRINK_MEMORY                 33
#define PragTyp_SOFT_HEAP_LIMIT               34
#define PragTyp_SYNCHRONOUS                   35
#define PragTyp_TABLE_INFO                    36
#define PragTyp_TEMP_STORE                    37
#define PragTyp_TEMP_STORE_DIRECTORY          38
#define PragTyp_THREADS                       39
#define PragTyp_WAL_AUTOCHECKPOINT            40
#define PragTyp_WAL_CHECKPOINT                41
#define PragTyp_LOCK_STATUS                   42
#define PragTyp_STATS                         43
#define PragTyp_HEADER_VALUE                   2
#define PragTyp_AUTO_VACUUM                    3
#define PragTyp_FLAG                           4
#define PragTyp_BUSY_TIMEOUT                   5
#define PragTyp_CACHE_SIZE                     6
#define PragTyp_CACHE_SPILL                    7
#define PragTyp_CASE_SENSITIVE_LIKE            8
#define PragTyp_COLLATION_LIST                 9
#define PragTyp_COMPILE_OPTIONS               10
#define PragTyp_DATA_STORE_DIRECTORY          11
#define PragTyp_DATABASE_LIST                 12
#define PragTyp_DEFAULT_CACHE_SIZE            13
#define PragTyp_ENCODING                      14
#define PragTyp_FOREIGN_KEY_CHECK             15
#define PragTyp_FOREIGN_KEY_LIST              16
#define PragTyp_FUNCTION_LIST                 17
#define PragTyp_HARD_HEAP_LIMIT               18
#define PragTyp_INCREMENTAL_VACUUM            19
#define PragTyp_INDEX_INFO                    20
#define PragTyp_INDEX_LIST                    21
#define PragTyp_INTEGRITY_CHECK               22
#define PragTyp_JOURNAL_MODE                  23
#define PragTyp_JOURNAL_SIZE_LIMIT            24
#define PragTyp_LOCK_PROXY_FILE               25
#define PragTyp_LOCKING_MODE                  26
#define PragTyp_PAGE_COUNT                    27
#define PragTyp_MMAP_SIZE                     28
#define PragTyp_MODULE_LIST                   29
#define PragTyp_OPTIMIZE                      30
#define PragTyp_PAGE_SIZE                     31
#define PragTyp_PRAGMA_LIST                   32
#define PragTyp_SECURE_DELETE                 33
#define PragTyp_SHRINK_MEMORY                 34
#define PragTyp_SOFT_HEAP_LIMIT               35
#define PragTyp_SYNCHRONOUS                   36
#define PragTyp_TABLE_INFO                    37
#define PragTyp_TEMP_STORE                    38
#define PragTyp_TEMP_STORE_DIRECTORY          39
#define PragTyp_THREADS                       40
#define PragTyp_WAL_AUTOCHECKPOINT            41
#define PragTyp_WAL_CHECKPOINT                42
#define PragTyp_LOCK_STATUS                   43
#define PragTyp_STATS                         44

/* Property flags associated with various pragma. */
#define PragFlg_NeedSchema 0x01 /* Force schema load before running */
#define PragFlg_NoColumns  0x02 /* OP_ResultRow called with zero columns */
#define PragFlg_NoColumns1 0x04 /* zero columns if RHS argument is present */
#define PragFlg_ReadOnly   0x08 /* Read-only HEADER_VALUE */
#define PragFlg_Result0    0x10 /* Acts as query when no argument */
123951
123952
123953
123954
123955
123956
123957





123958
123959
123960
123961
123962
123963
123964
124708
124709
124710
124711
124712
124713
124714
124715
124716
124717
124718
124719
124720
124721
124722
124723
124724
124725
124726







+
+
+
+
+







#if defined(SQLITE_ENABLE_CEROD)
 {/* zName:     */ "activate_extensions",
  /* ePragTyp:  */ PragTyp_ACTIVATE_EXTENSIONS,
  /* ePragFlg:  */ 0,
  /* ColNames:  */ 0, 0,
  /* iArg:      */ 0 },
#endif
 {/* zName:     */ "analysis_limit",
  /* ePragTyp:  */ PragTyp_ANALYSIS_LIMIT,
  /* ePragFlg:  */ PragFlg_Result0,
  /* ColNames:  */ 0, 0,
  /* iArg:      */ 0 },
#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
 {/* zName:     */ "application_id",
  /* ePragTyp:  */ PragTyp_HEADER_VALUE,
  /* ePragFlg:  */ PragFlg_NoColumns1|PragFlg_Result0,
  /* ColNames:  */ 0, 0,
  /* iArg:      */ BTREE_APPLICATION_ID },
#endif
124451
124452
124453
124454
124455
124456
124457
124458

124459
124460
124461
124462
124463
124464
124465
125213
125214
125215
125216
125217
125218
125219

125220
125221
125222
125223
125224
125225
125226
125227







-
+







 {/* zName:     */ "writable_schema",
  /* ePragTyp:  */ PragTyp_FLAG,
  /* ePragFlg:  */ PragFlg_Result0|PragFlg_NoColumns1,
  /* ColNames:  */ 0, 0,
  /* iArg:      */ SQLITE_WriteSchema|SQLITE_NoSchemaError },
#endif
};
/* Number of pragmas: 66 on by default, 76 total. */
/* Number of pragmas: 67 on by default, 77 total. */

/************** End of pragma.h **********************************************/
/************** Continuing where we left off in pragma.c *********************/

/*
** Interpret the given string as a safety level.  Return 0 for OFF,
** 1 for ON or NORMAL, 2 for FULL, and 3 for EXTRA.  Return 1 for an empty or 
124981
124982
124983
124984
124985
124986
124987
124988

124989
124990
124991
124992
124993
124994
124995
125743
125744
125745
125746
125747
125748
125749

125750
125751
125752
125753
125754
125755
125756
125757







-
+







      int size = ALWAYS(pBt) ? sqlite3BtreeGetPageSize(pBt) : 0;
      returnSingleInt(v, size);
    }else{
      /* Malloc may fail when setting the page-size, as there is an internal
      ** buffer that the pager module resizes using sqlite3_realloc().
      */
      db->nextPagesize = sqlite3Atoi(zRight);
      if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize,-1,0) ){
      if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize,0,0) ){
        sqlite3OomFault(db);
      }
    }
    break;
  }

  /*
126155
126156
126157
126158
126159
126160
126161
126162
126163
126164
126165
126166
126167
126168
126169
126170
126171
126172
126173
126174
126175
126176
126177
126178
126179
126180
126181
126182
126183
126917
126918
126919
126920
126921
126922
126923

126924
126925
126926
126927
126928
126929
126930
126931
126932
126933
126934
126935
126936

126937
126938
126939
126940
126941
126942
126943







-













-







            }
            sqlite3VdbeJumpHere(v, jmp4);
            sqlite3ResolvePartIdxLabel(pParse, jmp3);
          }
        }
        sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v);
        sqlite3VdbeJumpHere(v, loopTop-1);
#ifndef SQLITE_OMIT_BTREECOUNT
        if( !isQuick ){
          sqlite3VdbeLoadString(v, 2, "wrong # of entries in index ");
          for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
            if( pPk==pIdx ) continue;
            sqlite3VdbeAddOp2(v, OP_Count, iIdxCur+j, 3);
            addr = sqlite3VdbeAddOp3(v, OP_Eq, 8+j, 0, 3); VdbeCoverage(v);
            sqlite3VdbeChangeP5(v, SQLITE_NOTNULL);
            sqlite3VdbeLoadString(v, 4, pIdx->zName);
            sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3);
            integrityCheckResultRow(v);
            sqlite3VdbeJumpHere(v, addr);
          }
        }
#endif /* SQLITE_OMIT_BTREECOUNT */
      } 
    }
    {
      static const int iLn = VDBE_OFFSET_LINENO(2);
      static const VdbeOpList endCode[] = {
        { OP_AddImm,      1, 0,        0},    /* 0 */
        { OP_IfNotZero,   1, 4,        0},    /* 1 */
126603
126604
126605
126606
126607
126608
126609



















126610
126611
126612
126613
126614
126615
126616
127363
127364
127365
127366
127367
127368
127369
127370
127371
127372
127373
127374
127375
127376
127377
127378
127379
127380
127381
127382
127383
127384
127385
127386
127387
127388
127389
127390
127391
127392
127393
127394
127395







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







     && N>=0
    ){
      sqlite3_limit(db, SQLITE_LIMIT_WORKER_THREADS, (int)(N&0x7fffffff));
    }
    returnSingleInt(v, sqlite3_limit(db, SQLITE_LIMIT_WORKER_THREADS, -1));
    break;
  }

  /*
  **   PRAGMA analysis_limit
  **   PRAGMA analysis_limit = N
  **
  ** Configure the maximum number of rows that ANALYZE will examine
  ** in each index that it looks at.  Return the new limit.
  */
  case PragTyp_ANALYSIS_LIMIT: {
    sqlite3_int64 N;
    if( zRight
     && sqlite3DecOrHexToI64(zRight, &N)==SQLITE_OK
     && N>=0
    ){
      db->nAnalysisLimit = (int)(N&0x7fffffff);
    }
    returnSingleInt(v, db->nAnalysisLimit);
    break;
  }

#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
  /*
  ** Report the current state of file logs for all databases
  */
  case PragTyp_LOCK_STATUS: {
    static const char *const azLockName[] = {
131402
131403
131404
131405
131406
131407
131408

131409
131410
131411
131412
131413
131414
131415
132181
132182
132183
132184
132185
132186
132187
132188
132189
132190
132191
132192
132193
132194
132195







+







      }else{
        sqlite3 *db = pSubst->pParse->db;
        if( pSubst->isLeftJoin && pCopy->op!=TK_COLUMN ){
          memset(&ifNullRow, 0, sizeof(ifNullRow));
          ifNullRow.op = TK_IF_NULL_ROW;
          ifNullRow.pLeft = pCopy;
          ifNullRow.iTable = pSubst->iNewTable;
          ifNullRow.flags = EP_Skip;
          pCopy = &ifNullRow;
        }
        testcase( ExprHasProperty(pCopy, EP_Subquery) );
        pNew = sqlite3ExprDup(db, pCopy, 0);
        if( pNew && pSubst->isLeftJoin ){
          ExprSetProperty(pNew, EP_CanBeNull);
        }
134566
134567
134568
134569
134570
134571
134572
134573
134574
134575
134576
134577
134578
134579
134580
135346
135347
135348
135349
135350
135351
135352

135353
135354
135355
135356
135357
135358
135359







-







      resetAccumulator(pParse, &sAggInfo);
      sqlite3VdbeAddOp2(v, OP_Integer, 0, iUseFlag);
      VdbeComment((v, "indicate accumulator empty"));
      sqlite3VdbeAddOp1(v, OP_Return, regReset);
     
    } /* endif pGroupBy.  Begin aggregate queries without GROUP BY: */
    else {
#ifndef SQLITE_OMIT_BTREECOUNT
      Table *pTab;
      if( (pTab = isSimpleCount(p, &sAggInfo))!=0 ){
        /* If isSimpleCount() returns a pointer to a Table structure, then
        ** the SQL statement is of the form:
        **
        **   SELECT count(*) FROM <tbl>
        **
134602
134603
134604
134605
134606
134607
134608

134609
134610
134611
134612
134613
134614
134615








134616
134617
134618
134619
134620
134621
134622
134623
134624
134625
134626
134627
134628
134629
134630
134631

134632
134633
134634
134635
134636
134637
134638
134639
134640
135381
135382
135383
135384
135385
135386
135387
135388







135389
135390
135391
135392
135393
135394
135395
135396
135397
135398
135399
135400
135401
135402
135403
135404
135405
135406
135407
135408
135409
135410
135411

135412


135413
135414
135415
135416
135417
135418
135419







+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+















-
+
-
-







        **
        ** (2013-10-03) Do not count the entries in a partial index.
        **
        ** In practice the KeyInfo structure will not be used. It is only 
        ** passed to keep OP_OpenRead happy.
        */
        if( !HasRowid(pTab) ) pBest = sqlite3PrimaryKeyIndex(pTab);
        if( !p->pSrc->a[0].fg.notIndexed ){
        for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
          if( pIdx->bUnordered==0
           && pIdx->szIdxRow<pTab->szTabRow
           && pIdx->pPartIdxWhere==0
           && (!pBest || pIdx->szIdxRow<pBest->szIdxRow)
          ){
            pBest = pIdx;
          for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
            if( pIdx->bUnordered==0
             && pIdx->szIdxRow<pTab->szTabRow
             && pIdx->pPartIdxWhere==0
             && (!pBest || pIdx->szIdxRow<pBest->szIdxRow)
            ){
              pBest = pIdx;
            }
          }
        }
        if( pBest ){
          iRoot = pBest->tnum;
          pKeyInfo = sqlite3KeyInfoOfIndex(pParse, pBest);
        }

        /* Open a read-only cursor, execute the OP_Count, close the cursor. */
        sqlite3VdbeAddOp4Int(v, OP_OpenRead, iCsr, iRoot, iDb, 1);
        if( pKeyInfo ){
          sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO);
        }
        sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem);
        sqlite3VdbeAddOp1(v, OP_Close, iCsr);
        explainSimpleCount(pParse, pTab, pBest);
      }else
      }else{
#endif /* SQLITE_OMIT_BTREECOUNT */
      {
        int regAcc = 0;           /* "populate accumulators" flag */

        /* If there are accumulator registers but no min() or max() functions
        ** without FILTER clauses, allocate register regAcc. Register regAcc
        ** will contain 0 the first time the inner loop runs, and 1 thereafter.
        ** The code generated by updateAccumulator() uses this to ensure
        ** that the accumulator registers are (a) updated only once if
137850
137851
137852
137853
137854
137855
137856
137857

137858
137859
137860
137861
137862
137863
137864
138629
138630
138631
138632
138633
138634
138635

138636
138637
138638
138639
138640
138641
138642
138643







-
+







  /* Restore the original value of db->flags */
  db->init.iDb = 0;
  db->mDbFlags = saved_mDbFlags;
  db->flags = saved_flags;
  db->nChange = saved_nChange;
  db->nTotalChange = saved_nTotalChange;
  db->mTrace = saved_mTrace;
  sqlite3BtreeSetPageSize(pMain, -1, -1, 1);
  sqlite3BtreeSetPageSize(pMain, -1, 0, 1);

  /* Currently there is an SQL level transaction open on the vacuum
  ** database. No locks are held on any other files (since the main file
  ** was committed at the btree level). So it safe to end the transaction
  ** by manually setting the autoCommit flag to true and detaching the
  ** vacuum database. The vacuum_db journal file is deleted when the pager
  ** is closed by the DETACH.
159253
159254
159255
159256
159257
159258
159259

























159260
159261
159262
159263
159264
159265















159266
159267

159268























159269
159270
159271
159272
159273
159274
159275
160032
160033
160034
160035
160036
160037
160038
160039
160040
160041
160042
160043
160044
160045
160046
160047
160048
160049
160050
160051
160052
160053
160054
160055
160056
160057
160058
160059
160060
160061
160062
160063
160064
160065
160066
160067
160068
160069
160070
160071
160072
160073
160074
160075
160076
160077
160078
160079
160080
160081
160082
160083
160084
160085

160086
160087
160088
160089
160090
160091
160092
160093
160094
160095
160096
160097
160098
160099
160100
160101
160102
160103
160104
160105
160106
160107
160108
160109
160110
160111
160112
160113
160114
160115
160116
160117







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+






+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







}  /* extern "C" */
#endif  /* __cplusplus */


/************** End of sqliteicu.h *******************************************/
/************** Continuing where we left off in main.c ***********************/
#endif

/*
** This is an extension initializer that is a no-op and always
** succeeds, except that it fails if the fault-simulation is set
** to 500.
*/
static int sqlite3TestExtInit(sqlite3 *db){
  (void)db;
  return sqlite3FaultSim(500);
}


/*
** Forward declarations of external module initializer functions
** for modules that need them.
*/
#ifdef SQLITE_ENABLE_FTS1
SQLITE_PRIVATE int sqlite3Fts1Init(sqlite3*);
#endif
#ifdef SQLITE_ENABLE_FTS2
SQLITE_PRIVATE int sqlite3Fts2Init(sqlite3*);
#endif
#ifdef SQLITE_ENABLE_FTS5
SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3*);
#endif
#ifdef SQLITE_ENABLE_JSON1
SQLITE_PRIVATE int sqlite3Json1Init(sqlite3*);
#endif
#ifdef SQLITE_ENABLE_STMTVTAB
SQLITE_PRIVATE int sqlite3StmtVtabInit(sqlite3*);
#endif

/*
** An array of pointers to extension initializer functions for
** built-in extensions.
*/
static int (*const sqlite3BuiltinExtensions[])(sqlite3*) = {
#ifdef SQLITE_ENABLE_FTS1
  sqlite3Fts1Init,
#endif
#ifdef SQLITE_ENABLE_FTS2
  sqlite3Fts2Init,
#endif
#ifdef SQLITE_ENABLE_FTS3
  sqlite3Fts3Init,
#endif
#ifdef SQLITE_ENABLE_FTS5
SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3*);
  sqlite3Fts5Init,
#endif
#if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS)
  sqlite3IcuInit,
#endif
#ifdef SQLITE_ENABLE_RTREE
  sqlite3RtreeInit,
#endif
#ifdef SQLITE_ENABLE_DBPAGE_VTAB
  sqlite3DbpageRegister,
#endif
#ifdef SQLITE_ENABLE_DBSTAT_VTAB
  sqlite3DbstatRegister,
#endif
  sqlite3TestExtInit,
#ifdef SQLITE_ENABLE_JSON1
  sqlite3Json1Init,
#endif
#ifdef SQLITE_ENABLE_STMTVTAB
  sqlite3StmtVtabInit,
#endif
#ifdef SQLITE_ENABLE_BYTECODE_VTAB
  sqlite3VdbeBytecodeVtabInit,
#endif
};

#ifndef SQLITE_AMALGAMATION
/* IMPLEMENTATION-OF: R-46656-45156 The sqlite3_version[] string constant
** contains the text of SQLITE_VERSION macro. 
*/
SQLITE_API const char sqlite3_version[] = SQLITE_VERSION;
#endif
160779
160780
160781
160782
160783
160784
160785
160786

160787
160788
160789
160790
160791
160792
160793
160794
160795
160796
160797
160798
160799
160800
160801
160802
160803
160804
160805
160806
160807
160808
160809
160810
160811
160812
160813
160814
160815
160816
160817
160818
160819
160820
160821
160822
160823
160824
160825
160826
160827
160828
160829
160830
160831
160832
160833
160834
160835
160836
160837
160838
160839
160840
160841
160842
160843
160844
160845
160846
160847
160848
160849
160850
160851
160852
160853
160854
160855
160856
160857
160858
160859
160860
160861
160862
160863

160864
160865
160866
160867
160868
160869
160870
160871
160872
160873
160874

160875
160876
160877
160878
160879
160880
160881
160882
161621
161622
161623
161624
161625
161626
161627

161628

161629
161630
161631
161632
161633
161634
161635
161636
161637
161638
161639
161640
161641

























161642
161643
161644
161645
161646
161647
161648
161649
161650
161651
161652
161653
161654
161655
161656
161657
161658
161659
161660

161661
161662
161663
161664
161665
161666
161667
161668
161669
161670
161671
161672
161673
161674
161675
161676
161677

161678
161679
161680









161681

161682
161683
161684
161685
161686
161687
161688







-
+
-













-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-



















-

















-
+


-
-
-
-
-
-
-
-
-
+
-







** argument.
**
** Return non-zero to retry the lock.  Return zero to stop trying
** and cause SQLite to return SQLITE_BUSY.
*/
static int sqliteDefaultBusyCallback(
  void *ptr,               /* Database connection */
  int count,               /* Number of times table has been busy */
  int count                /* Number of times table has been busy */
  sqlite3_file *pFile      /* The file on which the lock occurred */
){
#if SQLITE_OS_WIN || HAVE_USLEEP
  /* This case is for systems that have support for sleeping for fractions of
  ** a second.  Examples:  All windows systems, unix systems with usleep() */
  static const u8 delays[] =
     { 1, 2, 5, 10, 15, 20, 25, 25,  25,  50,  50, 100 };
  static const u8 totals[] =
     { 0, 1, 3,  8, 18, 33, 53, 78, 103, 128, 178, 228 };
# define NDELAY ArraySize(delays)
  sqlite3 *db = (sqlite3 *)ptr;
  int tmout = db->busyTimeout;
  int delay, prior;

#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
  if( sqlite3OsFileControl(pFile,SQLITE_FCNTL_LOCK_TIMEOUT,&tmout)==SQLITE_OK ){
    if( count ){
      /* If this is the second or later invocation of the busy-handler,
      ** but tmout==0, then code in wal.c must have disabled the blocking
      ** lock before the SQLITE_BUSY error was hit. In this case, no delay
      ** occurred while waiting for the lock, so fall through to the xSleep()
      ** code below to delay a while before retrying the lock.  
      **
      ** Alternatively, if tmout!=0, then SQLite has already waited 
      ** sqlite3.busyTimeout ms for a lock. In this case, return 0 to 
      ** indicate that the lock should not be retried and the SQLITE_BUSY 
      ** error returned to the application.  */
      if( tmout ){
        tmout = 0;
        sqlite3OsFileControl(pFile, SQLITE_FCNTL_LOCK_TIMEOUT, &tmout);
        return 0;
      }
    }else{
      return 1;
    }
  }
#else
  UNUSED_PARAMETER(pFile);
#endif
  assert( count>=0 );
  if( count < NDELAY ){
    delay = delays[count];
    prior = totals[count];
  }else{
    delay = delays[NDELAY-1];
    prior = totals[NDELAY-1] + delay*(count-(NDELAY-1));
  }
  if( prior + delay > tmout ){
    delay = tmout - prior;
    if( delay<=0 ) return 0;
  }
  sqlite3OsSleep(db->pVfs, delay*1000);
  return 1;
#else
  /* This case for unix systems that lack usleep() support.  Sleeping
  ** must be done in increments of whole seconds */
  sqlite3 *db = (sqlite3 *)ptr;
  int tmout = ((sqlite3 *)ptr)->busyTimeout;
  UNUSED_PARAMETER(pFile);
  if( (count+1)*1000 > tmout ){
    return 0;
  }
  sqlite3OsSleep(db->pVfs, 1000000);
  return 1;
#endif
}

/*
** Invoke the given busy handler.
**
** This routine is called when an operation failed to acquire a
** lock on VFS file pFile.
**
** If this routine returns non-zero, the lock is retried.  If it
** returns 0, the operation aborts with an SQLITE_BUSY error.
*/
SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler *p, sqlite3_file *pFile){
SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler *p){
  int rc;
  if( p->xBusyHandler==0 || p->nBusy<0 ) return 0;
  if( p->bExtraFileArg ){
    /* Add an extra parameter with the pFile pointer to the end of the
    ** callback argument list */
    int (*xTra)(void*,int,sqlite3_file*);
    xTra = (int(*)(void*,int,sqlite3_file*))p->xBusyHandler;
    rc = xTra(p->pBusyArg, p->nBusy, pFile);
  }else{
    /* Legacy style busy handler callback */
    rc = p->xBusyHandler(p->pBusyArg, p->nBusy);
  rc = p->xBusyHandler(p->pBusyArg, p->nBusy);
  }
  if( rc==0 ){
    p->nBusy = -1;
  }else{
    p->nBusy++;
  }
  return rc; 
}
160893
160894
160895
160896
160897
160898
160899
160900
160901
160902
160903
160904
160905
160906
160907
161699
161700
161701
161702
161703
161704
161705

161706
161707
161708
161709
161710
161711
161712







-







#ifdef SQLITE_ENABLE_API_ARMOR
  if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
#endif
  sqlite3_mutex_enter(db->mutex);
  db->busyHandler.xBusyHandler = xBusy;
  db->busyHandler.pBusyArg = pArg;
  db->busyHandler.nBusy = 0;
  db->busyHandler.bExtraFileArg = 0;
  db->busyTimeout = 0;
  sqlite3_mutex_leave(db->mutex);
  return SQLITE_OK;
}

#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
/*
160944
160945
160946
160947
160948
160949
160950
160951
160952
160953
160954
160955
160956
160957
160958
161749
161750
161751
161752
161753
161754
161755

161756
161757
161758
161759
161760
161761
161762







-







#ifdef SQLITE_ENABLE_API_ARMOR
  if( !sqlite3SafetyCheckOk(db) ) return SQLITE_MISUSE_BKPT;
#endif
  if( ms>0 ){
    sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback,
                             (void*)db);
    db->busyTimeout = ms;
    db->busyHandler.bExtraFileArg = 1;
  }else{
    sqlite3_busy_handler(db, 0, 0);
  }
  return SQLITE_OK;
}

/*
162270
162271
162272
162273
162274
162275
162276

162277
162278
162279
162280
162281
162282
162283
163074
163075
163076
163077
163078
163079
163080
163081
163082
163083
163084
163085
163086
163087
163088







+







  const char *zVfs       /* Name of the VFS to use */
){
  sqlite3 *db;                    /* Store allocated handle here */
  int rc;                         /* Return code */
  int isThreadsafe;               /* True for threadsafe connections */
  char *zOpen = 0;                /* Filename argument to pass to BtreeOpen() */
  char *zErrMsg = 0;              /* Error message from sqlite3ParseUri() */
  int i;                          /* Loop counter */

#ifdef SQLITE_ENABLE_API_ARMOR
  if( ppDb==0 ) return SQLITE_MISUSE_BKPT;
#endif
  *ppDb = 0;
#ifndef SQLITE_OMIT_AUTOINIT
  rc = sqlite3_initialize();
162418
162419
162420
162421
162422
162423
162424



162425
162426
162427
162428
162429
162430
162431
163223
163224
163225
163226
163227
163228
163229
163230
163231
163232
163233
163234
163235
163236
163237
163238
163239







+
+
+







#endif
#if defined(SQLITE_ENABLE_QPSG)
                 | SQLITE_EnableQPSG
#endif
#if defined(SQLITE_DEFAULT_DEFENSIVE)
                 | SQLITE_Defensive
#endif
#if defined(SQLITE_DEFAULT_LEGACY_ALTER_TABLE)
                 | SQLITE_LegacyAlter
#endif
      ;
  sqlite3HashInit(&db->aCollSeq);
#ifndef SQLITE_OMIT_VIRTUALTABLE
  sqlite3HashInit(&db->aModule);
#endif

  /* Add the default collation sequence BINARY. BINARY works for both UTF-8
162510
162511
162512
162513
162514
162515
162516
162517

162518
162519

162520
162521
162522


162523
162524
162525
162526
162527
162528
162529
162530
162531
162532
162533
162534
162535
162536
162537
162538
162539
162540
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
162566
162567
162568
162569
162570
162571
162572
162573
162574
162575
162576
162577
162578
162579
162580
162581
162582
162583
162584
162585
162586
162587
162588
162589
162590
162591
162592
162593
162594
162595
162596
162597
162598
162599
163318
163319
163320
163321
163322
163323
163324

163325


163326



163327
163328
163329

163330
163331
163332
163333
163334
163335
163336
163337
163338
163339
163340
163341
























































163342
163343
163344
163345
163346
163347
163348







-
+
-
-
+
-
-
-
+
+

-












-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







  ** database schema yet. This is delayed until the first time the database
  ** is accessed.
  */
  sqlite3Error(db, SQLITE_OK);
  sqlite3RegisterPerConnectionBuiltinFunctions(db);
  rc = sqlite3_errcode(db);

#ifdef SQLITE_ENABLE_FTS5

  /* Register any built-in FTS5 module before loading the automatic
  ** extensions. This allows automatic extensions to register FTS5 
  /* Load compiled-in extensions */
  ** tokenizers and auxiliary functions.  */
  if( !db->mallocFailed && rc==SQLITE_OK ){
    rc = sqlite3Fts5Init(db);
  for(i=0; rc==SQLITE_OK && i<ArraySize(sqlite3BuiltinExtensions); i++){
    rc = sqlite3BuiltinExtensions[i](db);
  }
#endif

  /* Load automatic extensions - extensions that have been registered
  ** using the sqlite3_automatic_extension() API.
  */
  if( rc==SQLITE_OK ){
    sqlite3AutoLoadExtensions(db);
    rc = sqlite3_errcode(db);
    if( rc!=SQLITE_OK ){
      goto opendb_out;
    }
  }

#ifdef SQLITE_ENABLE_FTS1
  if( !db->mallocFailed ){
    extern int sqlite3Fts1Init(sqlite3*);
    rc = sqlite3Fts1Init(db);
  }
#endif

#ifdef SQLITE_ENABLE_FTS2
  if( !db->mallocFailed && rc==SQLITE_OK ){
    extern int sqlite3Fts2Init(sqlite3*);
    rc = sqlite3Fts2Init(db);
  }
#endif

#ifdef SQLITE_ENABLE_FTS3 /* automatically defined by SQLITE_ENABLE_FTS4 */
  if( !db->mallocFailed && rc==SQLITE_OK ){
    rc = sqlite3Fts3Init(db);
  }
#endif

#if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS)
  if( !db->mallocFailed && rc==SQLITE_OK ){
    rc = sqlite3IcuInit(db);
  }
#endif

#ifdef SQLITE_ENABLE_RTREE
  if( !db->mallocFailed && rc==SQLITE_OK){
    rc = sqlite3RtreeInit(db);
  }
#endif

#ifdef SQLITE_ENABLE_DBPAGE_VTAB
  if( !db->mallocFailed && rc==SQLITE_OK){
    rc = sqlite3DbpageRegister(db);
  }
#endif

#ifdef SQLITE_ENABLE_DBSTAT_VTAB
  if( !db->mallocFailed && rc==SQLITE_OK){
    rc = sqlite3DbstatRegister(db);
  }
#endif

#ifdef SQLITE_ENABLE_JSON1
  if( !db->mallocFailed && rc==SQLITE_OK){
    rc = sqlite3Json1Init(db);
  }
#endif

#ifdef SQLITE_ENABLE_STMTVTAB
  if( !db->mallocFailed && rc==SQLITE_OK){
    rc = sqlite3StmtVtabInit(db);
  }
#endif

#ifdef SQLITE_ENABLE_INTERNAL_FUNCTIONS
  /* Testing use only!!! The -DSQLITE_ENABLE_INTERNAL_FUNCTIONS=1 compile-time
  ** option gives access to internal functions by default.  
  ** Testing use only!!! */
  db->mDbFlags |= DBFLAG_InternalFunc;
#endif

163074
163075
163076
163077
163078
163079
163080
163081

163082
163083
163084
163085
163086
163087
163088
163823
163824
163825
163826
163827
163828
163829

163830
163831
163832
163833
163834
163835
163836
163837







-
+







      rc = SQLITE_OK;
    }else if( op==SQLITE_FCNTL_DATA_VERSION ){
      *(unsigned int*)pArg = sqlite3PagerDataVersion(pPager);
      rc = SQLITE_OK;
    }else if( op==SQLITE_FCNTL_RESERVE_BYTES ){
      int iNew = *(int*)pArg;
      *(int*)pArg = sqlite3BtreeGetRequestedReserve(pBtree);
      if( iNew>=0 && iNew<=254 ){
      if( iNew>=0 && iNew<=255 ){
        sqlite3BtreeSetPageSize(pBtree, 0, iNew, 0);
      }
      rc = SQLITE_OK;
    }else{
      rc = sqlite3OsFileControl(fd, op, pArg);
    }
    sqlite3BtreeLeave(pBtree);
165355
165356
165357
165358
165359
165360
165361

165362
165363
165364
165365
165366
165367
165368
166104
166105
166106
166107
166108
166109
166110
166111
166112
166113
166114
166115
166116
166117
166118







+







SQLITE_PRIVATE int sqlite3Fts3VarintLen(sqlite3_uint64);
SQLITE_PRIVATE void sqlite3Fts3Dequote(char *);
SQLITE_PRIVATE void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,int*,u8*);
SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *);
SQLITE_PRIVATE int sqlite3Fts3FirstFilter(sqlite3_int64, char *, int, char *);
SQLITE_PRIVATE void sqlite3Fts3CreateStatTable(int*, Fts3Table*);
SQLITE_PRIVATE int sqlite3Fts3EvalTestDeferred(Fts3Cursor *pCsr, int *pRc);
SQLITE_PRIVATE int sqlite3Fts3ReadInt(const char *z, int *pnOut);

/* fts3_tokenizer.c */
SQLITE_PRIVATE const char *sqlite3Fts3NextToken(const char *, int *);
SQLITE_PRIVATE int sqlite3Fts3InitHashTable(sqlite3 *, Fts3Hash *, const char *);
SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(Fts3Hash *pHash, const char *, 
    sqlite3_tokenizer **, char **
);
166086
166087
166088
166089
166090
166091
166092
















166093
166094
166095
166096
166097
166098
166099
166100
166101
166102
166103
166104
166105
166106
166107
166108
166109
166110
166111

166112
166113
166114
166115



166116
166117



166118
166119
166120
166121

166122
166123
166124
166125
166126
166127
166128
166836
166837
166838
166839
166840
166841
166842
166843
166844
166845
166846
166847
166848
166849
166850
166851
166852
166853
166854
166855
166856
166857
166858
166859
166860
166861
166862
166863
166864
166865
166866
166867
166868
166869
166870
166871
166872
166873
166874

166875

166876




166877
166878
166879


166880
166881
166882
166883

166884

166885
166886
166887
166888
166889
166890
166891
166892







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
















-

-
+
-
-
-
-
+
+
+
-
-
+
+
+

-

-
+







  }
  if( p->zLanguageid ){
    fts3Appendf(pRc, &zRet, ", ?");
  }
  sqlite3_free(zFree);
  return zRet;
}

/*
** Buffer z contains a positive integer value encoded as utf-8 text.
** Decode this value and store it in *pnOut, returning the number of bytes
** consumed. If an overflow error occurs return a negative value.
*/
SQLITE_PRIVATE int sqlite3Fts3ReadInt(const char *z, int *pnOut){
  u64 iVal = 0;
  int i;
  for(i=0; z[i]>='0' && z[i]<='9'; i++){
    iVal = iVal*10 + (z[i] - '0');
    if( iVal>0x7FFFFFFF ) return -1;
  }
  *pnOut = (int)iVal;
  return i;
}

/*
** This function interprets the string at (*pp) as a non-negative integer
** value. It reads the integer and sets *pnOut to the value read, then 
** sets *pp to point to the byte immediately following the last byte of
** the integer value.
**
** Only decimal digits ('0'..'9') may be part of an integer value. 
**
** If *pp does not being with a decimal digit SQLITE_ERROR is returned and
** the output value undefined. Otherwise SQLITE_OK is returned.
**
** This function is used when parsing the "prefix=" FTS4 parameter.
*/
static int fts3GobbleInt(const char **pp, int *pnOut){
  const int MAX_NPREFIX = 10000000;
  const char *p;                  /* Iterator pointer */
  int nInt = 0;                   /* Output value */

  int nByte;
  for(p=*pp; p[0]>='0' && p[0]<='9'; p++){
    nInt = nInt * 10 + (p[0] - '0');
    if( nInt>MAX_NPREFIX ){
      nInt = 0;
  nByte = sqlite3Fts3ReadInt(*pp, &nInt);
  if( nInt>MAX_NPREFIX ){
    nInt = 0;
      break;
    }
  }
  if( nByte==0 ){
    return SQLITE_ERROR;
  }
  if( p==*pp ) return SQLITE_ERROR;
  *pnOut = nInt;
  *pp = p;
  *pp += nByte;
  return SQLITE_OK;
}

/*
** This function is called to allocate an array of Fts3Index structures
** representing the indexes maintained by the current FTS table. FTS tables
** always maintain the main "terms" index, but may also maintain one or
167296
167297
167298
167299
167300
167301
167302

167303


167304
167305
167306
167307
167308
167309
167310
168060
168061
168062
168063
168064
168065
168066
168067

168068
168069
168070
168071
168072
168073
168074
168075
168076







+
-
+
+







** the next position.
*/
static void fts3ReadNextPos(
  char **pp,                    /* IN/OUT: Pointer into position-list buffer */
  sqlite3_int64 *pi             /* IN/OUT: Value read from position-list */
){
  if( (**pp)&0xFE ){
    int iVal;
    fts3GetDeltaVarint(pp, pi);
    *pp += fts3GetVarint32((*pp), &iVal);
    *pi += iVal;
    *pi -= 2;
  }else{
    *pi = POSITION_LIST_END;
  }
}

/*
172196
172197
172198
172199
172200
172201
172202
172203
172204
172205

172206
172207
172208
172209
172210
172211
172212
172213
172962
172963
172964
172965
172966
172967
172968



172969

172970
172971
172972
172973
172974
172975
172976







-
-
-
+
-







      int nKey = pKey->n;
      char cNext;

      /* If this is a "NEAR" keyword, check for an explicit nearness. */
      if( pKey->eType==FTSQUERY_NEAR ){
        assert( nKey==4 );
        if( zInput[4]=='/' && zInput[5]>='0' && zInput[5]<='9' ){
          nNear = 0;
          for(nKey=5; zInput[nKey]>='0' && zInput[nKey]<='9'; nKey++){
            nNear = nNear * 10 + (zInput[nKey] - '0');
          nKey += 1+sqlite3Fts3ReadInt(&zInput[nKey+1], &nNear);
          }
        }
      }

      /* At this point this is probably a keyword. But for that to be true,
      ** the next byte must contain either whitespace, an open or close
      ** parenthesis, a quote character, or EOF. 
      */
178382
178383
178384
178385
178386
178387
178388
178389

178390
178391
178392
178393

178394
178395
178396
178397
178398
178399
178400
178401
178402
178403

178404
178405
178406
178407
178408
178409
178410
179145
179146
179147
179148
179149
179150
179151

179152
179153
179154
179155

179156
179157
179158
179159
179160
179161
179162
179163
179164
179165

179166
179167
179168
179169
179170
179171
179172
179173







-
+



-
+









-
+







  i64 *piEndBlock,
  i64 *pnByte
){
  const unsigned char *zText = sqlite3_column_text(pStmt, iCol);
  if( zText ){
    int i;
    int iMul = 1;
    i64 iVal = 0;
    u64 iVal = 0;
    for(i=0; zText[i]>='0' && zText[i]<='9'; i++){
      iVal = iVal*10 + (zText[i] - '0');
    }
    *piEndBlock = iVal;
    *piEndBlock = (i64)iVal;
    while( zText[i]==' ' ) i++;
    iVal = 0;
    if( zText[i]=='-' ){
      i++;
      iMul = -1;
    }
    for(/* no-op */; zText[i]>='0' && zText[i]<='9'; i++){
      iVal = iVal*10 + (zText[i] - '0');
    }
    *pnByte = (iVal * (i64)iMul);
    *pnByte = ((i64)iVal * (i64)iMul);
  }
}


/*
** A segment of size nByte bytes has just been written to absolute level
** iAbsLevel. Promote any segments that should be promoted as a result.
223899
223900
223901
223902
223903
223904
223905
223906

223907
223908
223909
223910
223911
223912
223913
224662
224663
224664
224665
224666
224667
224668

224669
224670
224671
224672
224673
224674
224675
224676







-
+







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: 2020-03-03 20:04:29 bd94d7d052734460904c687756231f8aa243a2252f07f742dd1e437aa940f536", -1, SQLITE_TRANSIENT);
  sqlite3_result_text(pCtx, "fts5: 2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f9654ff", -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){
228550
228551
228552
228553
228554
228555
228556

228557

228558
228559
228560
228561
228562
228563
228564
229313
229314
229315
229316
229317
229318
229319
229320

229321
229322
229323
229324
229325
229326
229327
229328







+
-
+







      sqlite3_result_int(ctx, sqlite3_stmt_readonly(pCur->pStmt));
      break;
    }
    case STMT_COLUMN_BUSY: {
      sqlite3_result_int(ctx, sqlite3_stmt_busy(pCur->pStmt));
      break;
    }
    default: {
    case STMT_COLUMN_MEM: {
      assert( i==STMT_COLUMN_MEM );
      i = SQLITE_STMTSTATUS_MEMUSED + 
            STMT_COLUMN_NSCAN - SQLITE_STMTSTATUS_FULLSCAN_STEP;
      /* Fall thru */
    }
    case STMT_COLUMN_NSCAN:
    case STMT_COLUMN_NSORT:
    case STMT_COLUMN_NAIDX:
228681
228682
228683
228684
228685
228686
228687
228688

228689
228690

228691
228692
228693
228694
229445
229446
229447
229448
229449
229450
229451

229452
229453

229454
229455
229456
229457
229458







-
+

-
+




#endif
  return rc;
}
#endif /* SQLITE_CORE */
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */

/************** End of stmt.c ************************************************/
#if __LINE__!=228688
#if __LINE__!=229452
#undef SQLITE_SOURCE_ID
#define SQLITE_SOURCE_ID      "2020-04-20 17:35:32 2fc80ef16ce5878311ab88a0c64631813572ffbb71f75363b4619c9667e0alt2"
#define SQLITE_SOURCE_ID      "2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f96alt2"
#endif
/* Return the source-id for this library */
SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
/************************** End of sqlite3.c ******************************/
Changes to src/sqlite3.h.
121
122
123
124
125
126
127
128

129
130
131
132
133
134
135
121
122
123
124
125
126
127

128
129
130
131
132
133
134
135







-
+







**
** See also: [sqlite3_libversion()],
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION        "3.32.0"
#define SQLITE_VERSION_NUMBER 3032000
#define SQLITE_SOURCE_ID      "2020-04-20 17:35:32 2fc80ef16ce5878311ab88a0c64631813572ffbb71f75363b4619c9667e0926b"
#define SQLITE_SOURCE_ID      "2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f9654ff"

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







+
+
+
+

-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-







**
** ^The sqlite3_close() and sqlite3_close_v2() routines are destructors
** for the [sqlite3] object.
** ^Calls to sqlite3_close() and sqlite3_close_v2() return [SQLITE_OK] if
** the [sqlite3] object is successfully destroyed and all associated
** resources are deallocated.
**
** Ideally, applications should [sqlite3_finalize | finalize] all
** [prepared statements], [sqlite3_blob_close | close] all [BLOB handles], and 
** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated
** with the [sqlite3] object prior to attempting to close the object.
** ^If the database connection is associated with unfinalized prepared
** statements or unfinished sqlite3_backup objects then sqlite3_close()
** will leave the database connection open and return [SQLITE_BUSY].
** ^If sqlite3_close_v2() is called with unfinalized prepared statements
** and/or unfinished sqlite3_backups, then the database connection becomes
** an unusable "zombie" which will automatically be deallocated when the
** last prepared statement is finalized or the last sqlite3_backup is
** finished.  The sqlite3_close_v2() interface is intended for use with
** host languages that are garbage collected, and where the order in which
** destructors are called is arbitrary.
** statements, BLOB handlers, and/or unfinished sqlite3_backup objects then
** sqlite3_close() will leave the database connection open and return
** [SQLITE_BUSY]. ^If sqlite3_close_v2() is called with unfinalized prepared
** statements, unclosed BLOB handlers, and/or unfinished sqlite3_backups,
** it returns [SQLITE_OK] regardless, but instead of deallocating the database
** connection immediately, it marks the database connection as an unusable
** "zombie" and makes arrangements to automatically deallocate the database
** connection after all prepared statements are finalized, all BLOB handles
** are closed, and all backups have finished. The sqlite3_close_v2() interface
** is intended for use with host languages that are garbage collected, and
** where the order in which destructors are called is arbitrary.
**
** Applications should [sqlite3_finalize | finalize] all [prepared statements],
** [sqlite3_blob_close | close] all [BLOB handles], and 
** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated
** with the [sqlite3] object prior to attempting to close the object.  ^If
** sqlite3_close_v2() is called on a [database connection] that still has
** outstanding [prepared statements], [BLOB handles], and/or
** [sqlite3_backup] objects then it returns [SQLITE_OK] and the deallocation
** of resources is deferred until all [prepared statements], [BLOB handles],
** and [sqlite3_backup] objects are also destroyed.
**
** ^If an [sqlite3] object is destroyed while a transaction is open,
** the transaction is automatically rolled back.
**
** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)]
** must be either a NULL
** pointer or an [sqlite3] object pointer obtained
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
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







+




+








+







#define SQLITE_IOERR_GETTEMPPATH       (SQLITE_IOERR | (25<<8))
#define SQLITE_IOERR_CONVPATH          (SQLITE_IOERR | (26<<8))
#define SQLITE_IOERR_VNODE             (SQLITE_IOERR | (27<<8))
#define SQLITE_IOERR_AUTH              (SQLITE_IOERR | (28<<8))
#define SQLITE_IOERR_BEGIN_ATOMIC      (SQLITE_IOERR | (29<<8))
#define SQLITE_IOERR_COMMIT_ATOMIC     (SQLITE_IOERR | (30<<8))
#define SQLITE_IOERR_ROLLBACK_ATOMIC   (SQLITE_IOERR | (31<<8))
#define SQLITE_IOERR_DATA              (SQLITE_IOERR | (32<<8))
#define SQLITE_LOCKED_SHAREDCACHE      (SQLITE_LOCKED |  (1<<8))
#define SQLITE_LOCKED_VTAB             (SQLITE_LOCKED |  (2<<8))
#define SQLITE_BUSY_RECOVERY           (SQLITE_BUSY   |  (1<<8))
#define SQLITE_BUSY_SNAPSHOT           (SQLITE_BUSY   |  (2<<8))
#define SQLITE_BUSY_TIMEOUT            (SQLITE_BUSY   |  (3<<8))
#define SQLITE_CANTOPEN_NOTEMPDIR      (SQLITE_CANTOPEN | (1<<8))
#define SQLITE_CANTOPEN_ISDIR          (SQLITE_CANTOPEN | (2<<8))
#define SQLITE_CANTOPEN_FULLPATH       (SQLITE_CANTOPEN | (3<<8))
#define SQLITE_CANTOPEN_CONVPATH       (SQLITE_CANTOPEN | (4<<8))
#define SQLITE_CANTOPEN_DIRTYWAL       (SQLITE_CANTOPEN | (5<<8)) /* Not Used */
#define SQLITE_CANTOPEN_SYMLINK        (SQLITE_CANTOPEN | (6<<8))
#define SQLITE_CORRUPT_VTAB            (SQLITE_CORRUPT | (1<<8))
#define SQLITE_CORRUPT_SEQUENCE        (SQLITE_CORRUPT | (2<<8))
#define SQLITE_CORRUPT_INDEX           (SQLITE_CORRUPT | (3<<8))
#define SQLITE_READONLY_RECOVERY       (SQLITE_READONLY | (1<<8))
#define SQLITE_READONLY_CANTLOCK       (SQLITE_READONLY | (2<<8))
#define SQLITE_READONLY_ROLLBACK       (SQLITE_READONLY | (3<<8))
#define SQLITE_READONLY_DBMOVED        (SQLITE_READONLY | (4<<8))
#define SQLITE_READONLY_CANTINIT       (SQLITE_READONLY | (5<<8))
#define SQLITE_READONLY_DIRECTORY      (SQLITE_READONLY | (6<<8))
#define SQLITE_ABORT_ROLLBACK          (SQLITE_ABORT | (2<<8))
1109
1110
1111
1112
1113
1114
1115





1116
1117
1118
1119
1120
1121
1122
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126







+
+
+
+
+







** omits changes made by other database connections.  The
** [PRAGMA data_version] command provides a mechanism to detect changes to
** a single attached database that occur due to other database connections,
** but omits changes implemented by the database connection on which it is
** called.  This file control is the only mechanism to detect changes that
** happen either internally or externally and that are associated with
** a particular attached database.
**
** <li>[[SQLITE_FCNTL_CKPT_START]]
** The [SQLITE_FCNTL_CKPT_START] opcode is invoked from within a checkpoint
** in wal mode before the client starts to copy pages from the wal
** file to the database file.
**
** <li>[[SQLITE_FCNTL_CKPT_DONE]]
** The [SQLITE_FCNTL_CKPT_DONE] opcode is invoked from within a checkpoint
** in wal mode after the client has finished copying pages from the wal
** file to the database file, but before the *-shm file is updated to
** record the fact that the pages have been checkpointed.
** </ul>
1154
1155
1156
1157
1158
1159
1160

1161
1162
1163
1164
1165
1166
1167
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172







+







#define SQLITE_FCNTL_COMMIT_ATOMIC_WRITE    32
#define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE  33
#define SQLITE_FCNTL_LOCK_TIMEOUT           34
#define SQLITE_FCNTL_DATA_VERSION           35
#define SQLITE_FCNTL_SIZE_LIMIT             36
#define SQLITE_FCNTL_CKPT_DONE              37
#define SQLITE_FCNTL_RESERVE_BYTES          38
#define SQLITE_FCNTL_CKPT_START             39

/* deprecated names */
#define SQLITE_GET_LOCKPROXYFILE      SQLITE_FCNTL_GET_LOCKPROXYFILE
#define SQLITE_SET_LOCKPROXYFILE      SQLITE_FCNTL_SET_LOCKPROXYFILE
#define SQLITE_LAST_ERRNO             SQLITE_FCNTL_LAST_ERRNO


3532
3533
3534
3535
3536
3537
3538




3539
3540









3541
3542
3543
3544
3545
3546
3547
3537
3538
3539
3540
3541
3542
3543
3544
3545
3546
3547


3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563







+
+
+
+
-
-
+
+
+
+
+
+
+
+
+







/*
** CAPI3REF: Obtain Values For URI Parameters
**
** These are utility routines, useful to [VFS|custom VFS implementations],
** that check if a database file was a URI that contained a specific query 
** parameter, and if so obtains the value of that query parameter.
**
** The first parameter to these interfaces (hereafter referred to
** as F) must be one of:
** <ul>
** <li> A database filename pointer created by the SQLite core and
** If F is the database filename pointer passed into the xOpen() method of 
** a VFS implementation or it is the return value of [sqlite3_db_filename()]
** passed into the xOpen() method of a VFS implemention, or
** <li> A filename obtained from [sqlite3_db_filename()], or
** <li> A new filename constructed using [sqlite3_create_filename()].
** </ul>
** If the F parameter is not one of the above, then the behavior is
** undefined and probably undesirable.  Older versions of SQLite were
** more tolerant of invalid F parameters than newer versions.
**
** If F is a suitable filename (as described in the previous paragraph)
** and if P is the name of the query parameter, then
** sqlite3_uri_parameter(F,P) returns the value of the P
** parameter if it exists or a NULL pointer if P does not appear as a 
** query parameter on F.  If P is a query parameter of F and it
** has no explicit value, then sqlite3_uri_parameter(F,P) returns
** a pointer to an empty string.
**
3616
3617
3618
3619
3620
3621
3622



















3623
3624
3625
3626
3627
3628
3629
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







** return value from [sqlite3_db_filename()], then the result is
** undefined and is likely a memory access violation.
*/
SQLITE_API const char *sqlite3_filename_database(const char*);
SQLITE_API const char *sqlite3_filename_journal(const char*);
SQLITE_API const char *sqlite3_filename_wal(const char*);

/*
** CAPI3REF:  Database File Corresponding To A Journal
**
** ^If X is the name of a rollback or WAL-mode journal file that is
** passed into the xOpen method of [sqlite3_vfs], then 
** sqlite3_database_file_object(X) returns a pointer to the [sqlite3_file]
** object that represents the main database file.
**
** This routine is intended for use in custom [VFS] implementations
** only.  It is not a general-purpose interface.
** The argument sqlite3_file_object(X) must be a filename pointer that
** has been passed into [sqlite3_vfs].xOpen method where the 
** flags parameter to xOpen contains one of the bits
** [SQLITE_OPEN_MAIN_JOURNAL] or [SQLITE_OPEN_WAL].  Any other use
** of this routine results in undefined and probably undesirable
** behavior.
*/
SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*);

/*
** CAPI3REF: Create and Destroy VFS Filenames
**
** These interfces are provided for use by [VFS shim] implementations and
** are not useful outside of that context.
**
** The sqlite3_create_filename(D,J,W,N,P) allocates memory to hold a version of
3650
3651
3652
3653
3654
3655
3656
3657

3658
3659
3660
3661
3662
3663
3664
3685
3686
3687
3688
3689
3690
3691

3692
3693
3694
3695
3696
3697
3698
3699







-
+







** pointer if N is zero.  None of the 2*N pointers in the P array may be
** NULL pointers and key pointers should not be empty strings.
** None of the D, J, or W parameters to sqlite3_create_filename(D,J,W,N,P) may
** be NULL pointers, though they can be empty strings.
**
** The sqlite3_free_filename(Y) routine releases a memory allocation
** previously obtained from sqlite3_create_filename().  Invoking
** sqlite3_free_filename(Y) is a NULL pointer is a harmless no-op.
** sqlite3_free_filename(Y) where Y is a NULL pointer is a harmless no-op.
**
** If the Y parameter to sqlite3_free_filename(Y) is anything other
** than a NULL pointer or a pointer previously acquired from
** sqlite3_create_filename(), then bad things such as heap
** corruption or segfaults may occur. The value Y should be 
** used again after sqlite3_free_filename(Y) has been called.  This means
** that if the [sqlite3_vfs.xOpen()] method of a VFS has been called using Y,
Changes to src/style.c.
705
706
707
708
709
710
711
712

713
714
715
716
717
718
719
705
706
707
708
709
710
711

712
713
714
715
716
717
718
719







-
+







  needCopyBtnJs = 1;
}

/*
** Generate code to load a single javascript file
*/
void style_load_one_js_file(const char *zFile){
  @ <script src='%R/builtin/%s(zFile)?id=%S(MANIFEST_UUID)'></script>
  @ <script src='%R/builtin/%s(zFile)?id=%S(fossil_exe_id())'></script>
}

/*
** All extra JS files to load.
*/
static const char *azJsToLoad[4];
static int nJsToLoad = 0;
1257
1258
1259
1260
1261
1262
1263

1264
1265
1266
1267
1268
1269
1270
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271







+







    @ capabilities = %s(find_capabilities(zCap))<br />
    if( zCap[0] ){
      @ anonymous-adds = %s(find_anon_capabilities(zCap))<br />
    }
    @ g.zRepositoryName = %h(g.zRepositoryName)<br />
    @ load_average() = %f(load_average())<br />
    @ cgi_csrf_safe(0) = %d(cgi_csrf_safe(0))<br />
    @ fossil_exe_id() = %h(fossil_exe_id())<br />
    @ <hr />
    P("HTTP_USER_AGENT");
    cgi_print_all(showAll, 0);
    if( showAll && blob_size(&g.httpHeader)>0 ){
      @ <hr />
      @ <pre>
      @ %h(blob_str(&g.httpHeader))
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307







1308
1309
1310
1311

1312
1313
1314
1315

1316
1317


1318
1319
1320
1321
1322
1323
1324
1325
1326





1327
1328
1329



1330
1331





1332
1333

1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351



1352



1353
1354
1355
1356
1357
1358
1359
1360
1361

1362
1363
1364

1365
1366
1367
1368

1369
1370
1371
1372
1373
1374
1375


1376
1377
1378
1379
1380

1381
1382



1383
1384
1385
1386
1387
1388
1389
1296
1297
1298
1299
1300
1301
1302






1303
1304
1305
1306
1307
1308
1309
1310
1311
1312

1313
1314
1315
1316

1317
1318

1319
1320
1321
1322
1323
1324
1325




1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336


1337
1338
1339
1340
1341
1342

1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364

1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375

1376
1377
1378

1379
1380
1381
1382

1383
1384
1385
1386
1387
1388
1389

1390
1391
1392
1393
1394
1395

1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408







-
-
-
-
-
-
+
+
+
+
+
+
+



-
+



-
+

-
+
+





-
-
-
-
+
+
+
+
+



+
+
+
-
-
+
+
+
+
+

-
+


















+
+
+
-
+
+
+








-
+


-
+



-
+






-
+
+




-
+


+
+
+







}

#if INTERFACE
# define webpage_assert(T) if(!(T)){webpage_assert_page(__FILE__,__LINE__,#T);}
#endif

/*
** Outputs a labeled checkbox element. zFieldName is the form element
** name. zLabel is the label for the checkbox. zValue is the optional
** value for the checkbox. zTip is an optional tooltip, which gets set
** as the "title" attribute of the outermost element. If isChecked is
** true, the checkbox gets the "checked" attribute set, else it is
** not.
** Outputs a labeled checkbox element. zWrapperId is an optional ID
** value for the containing element (see below). zFieldName is the
** form element name. zLabel is the label for the checkbox. zValue is
** the optional value for the checkbox. zTip is an optional tooltip,
** which gets set as the "title" attribute of the outermost
** element. If isChecked is true, the checkbox gets the "checked"
** attribute set, else it is not.
**
** Resulting structure:
**
** <div class='input-with-label' title={{zTip}}>
** <span class='input-with-label' title={{zTip}} id={{zWrapperId}}>
**   <input type='checkbox' name={{zFieldName}} value={{zValue}}
**          {{isChecked ? " checked : ""}}/>
**   <span>{{zLabel}}</span>
** </div>
** </span>
**
** zFieldName, zLabel, and zValue are required. zTip is optional.
** zLabel, and zValue are required. zFieldName, zWrapperId, and zTip
** are may be NULL or empty.
**
** Be sure that the input-with-label CSS class is defined sensibly, in
** particular, having its display:inline-block is useful for alignment
** purposes.
*/
void style_labeled_checkbox(const char *zFieldName, const char * zLabel,
                            const char * zValue, const char * zTip,
                            int isChecked){
  CX("<div class='input-with-label'");
void style_labeled_checkbox(const char * zWrapperId,
                            const char *zFieldName, const char * zLabel,
                            const char * zValue, int isChecked,
                            const char * zTip){
  CX("<span class='input-with-label'");
  if(zTip && *zTip){
    CX(" title='%h'", zTip);
  }
  if(zWrapperId && *zWrapperId){
    CX(" id='%s'",zWrapperId);
  }
  CX("><input type='checkbox' name='%s' value='%T'%s/>",
     zFieldName,
  CX("><input type='checkbox' ");
  if(zFieldName && *zFieldName){
    CX("name='%s' ",zFieldName);
  }
  CX("value='%T'%s/>",
     zValue ? zValue : "", isChecked ? " checked" : "");
  CX("<span>%h</span></div>", zLabel);
  CX("<span>%h</span></span>", zLabel);
}

/*
** Outputs a SELECT list from a compile-time list of integers.
** The vargs must be a list of (const char *, int) pairs, terminated
** with a single NULL. Each pair is interpreted as...
**
** If the (const char *) is NULL, it is the end of the list, else
** a new OPTION entry is created. If the string is empty, the
** label and value of the OPTION is the integer part of the pair.
** If the string is not empty, it becomes the label and the integer
** the value. If that value == selectedValue then that OPTION
** element gets the 'selected' attribute.
**
** Note that the pairs are not in (int, const char *) order because
** there is no well-known integer value which we can definitively use
** as a list terminator.
**
** zWrapperId is an optional ID value for the containing element (see
** below).
**
** zFieldName is the value of the form element's name attribute.
** zFieldName is the value of the form element's name attribute. Note
** that fossil prefers underscores over '-' for separators in form
** element names.
**
** zLabel is an optional string to use as a "label" for the element
** (see below).
**
** zTooltip is an optional value for the SELECT's title attribute.
**
** The structure of the emitted HTML is:
**
** <div class='input-with-label' title={{zToolTip}}>
** <span class='input-with-label' title={{zToolTip}} id={{zWrapperId}}>
**   <span>{{zLabel}}</span>
**   <select>...</select>
** </div>
** </span>
**
** Example:
**
** style_select_list_int("my_field", "Grapes",
** 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 *zFieldName, const char * zLabel,
void style_select_list_int(const char * zWrapperId,
                           const char *zFieldName, const char * zLabel,
                           const char * zToolTip, int selectedVal,
                           ... ){
  va_list vargs;
  va_start(vargs,selectedVal);
  CX("<div class='input-with-label'");
  CX("<span class='input-with-label'");
  if(zToolTip && *zToolTip){
    CX(" title='%h'",zToolTip);
  }
  if(zWrapperId && *zWrapperId){
    CX(" id='%s'",zWrapperId);
  }
  CX(">");
  if(zLabel && *zLabel){
    CX("<span>%h</span>", zLabel);
  }
  CX("<select name='%s'>",zFieldName);
  while(1){
1399
1400
1401
1402
1403
1404
1405









































1406

















1407

1408


1409
1410
1411
1412
1413






1414
1415
1416
1417




1418
1419



1420
1421

1422
1423
1424
1425
1426
1427
1428
1429






























































1430
1431
1432
1433






























































































1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483

1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498




1499
1500
1501
1502


1503
1504
1505
1506

1507
1508







1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
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
1668







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+

+
+





+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
-
+
+
+

-
+

-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+




+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
      CX("%s", zOption);
    }else{
      CX("%d",v);
    }
    CX("</option>\n");
  }
  CX("</select>\n");
  CX("</span>\n");
  va_end(vargs);
}

/*
** The C-string counterpart of style_select_list_int(), this variant
** differs only in that its variadic arguments are C-strings in pairs
** of (optionLabel, optionValue). If a given optionLabel is an empty
** string, the corresponding optionValue is used as its label. If any
** given value matches zSelectedVal, that option gets preselected. If
** no options match zSelectedVal then the first entry is selected by
** default.
**
** Any of (zWrapperId, zTooltip, zSelectedVal) may be NULL or empty.
**
** Example:
**
** style_select_list_str("my-grapes", "my_grapes", "Grapes",
**                      "Select the number of grapes",
**                       P("my_field"),
**                       "1", "One", "2", "Two", "", "3",
**                       NULL);
*/
void style_select_list_str(const char * zWrapperId,
                           const char *zFieldName, const char * zLabel,
                           const char * zToolTip, char const * zSelectedVal,
                           ... ){
  va_list vargs;

  va_start(vargs,zSelectedVal);
  if(!zSelectedVal){
    zSelectedVal = __FILE__/*some string we'll never match*/;
  }
  CX("<span class='input-with-label'");
  if(zToolTip && *zToolTip){
    CX(" title='%h'",zToolTip);
  }
  if(zWrapperId && *zWrapperId){
    CX(" id='%s'",zWrapperId);
  }
  CX(">");
  if(zLabel && *zLabel){
    CX("<span>%h</span>", zLabel);
  }
  CX("<select name='%s'>",zFieldName);
  while(1){
    const char * zLabel = va_arg(vargs,char *);
    const char * zVal;
    if(NULL==zLabel){
      break;
    }
    zVal = va_arg(vargs,char *);
    CX("<option value='%T'%s>",
       zVal, 0==fossil_strcmp(zVal, zSelectedVal) ? " selected" : "");
    if(*zLabel){
      CX("%s", zLabel);
    }else{
      CX("%h",zVal);
    }
    CX("</div>\n");
    CX("</option>\n");
  }
  CX("</select>\n");
  CX("</span>\n");
  va_end(vargs);
}


/*
** The first time this is called, it emits code to install and
** bootstrap the window.fossil object, using the built-in file
** fossil.bootstrap.js (not to be confused with bootstrap.js).
**
** Subsequent calls are no-ops.
**
** If passed 0, it emits a script opener tag with this session's
** nonce. If passed non-0 it emits a script closing tag. The very
** first time it is called, it emits some bootstrapping JS code
** immediately after the script opener. Specifically, it defines
** If passed a true value, it emits the contents directly to the page
** output, else it emits a script tag with a src=builtin/... to load
** the script. It always outputs a small pre-bootstrap element in its
** own script tag to initialize parts which need C-runtime-level
** window.fossil if it's not already defined, and may set some
** properties on it.
** information, before loading the main fossil.bootstrap.js either
** inline or via a <script src=...>, as specified by the first
** argument.
*/
void style_emit_script_tag(int phase){
void style_emit_script_fossil_bootstrap(int asInline){
  static int once = 0;
  if(0==phase){
    CX("<script nonce='%s'>", style_nonce());
      if(0==once){
        once = 1;
        CX("\nif(!window.fossil) window.fossil={};\n");
        CX("window.fossil.version = %Q;\n", get_version());
      }
  if(0==once++){
    /* Set up the generic/app-agnostic parts of window.fossil
    ** which require C-level state... */
    style_emit_script_tag(0,0);
    CX("(function(){\n"
       "if(!window.fossil) window.fossil={};\n"
       "window.fossil.version = \"%j\";\n"
    /* fossil.rootPath is the top-most CGI/server path,
    ** including a trailing slash. */
       "window.fossil.rootPath = \"%j\"+'/';\n",
       get_version(), g.zTop);
    /* fossil.config = {...various config-level options...} */
    CX("window.fossil.config = {"
       "hashDigits: %d, hashDigitsUrl: %d"
       "};\n", hash_digits(0), hash_digits(1));
    /*
    ** fossil.page holds info about the current page. This is also
    ** where the current page "should" store any of its own
    ** page-specific state, and it is reserved for that purpose.
    */
    CX("window.fossil.page = {"
       "page:\"%T\""
       "};\n", g.zPath);
    CX("})();\n");
    /* The remaining fossil object bootstrap code is not dependent on
    ** C-runtime state... */
    if(asInline){
      CX("%s\n", builtin_text("fossil.bootstrap.js"));
    }
    style_emit_script_tag(1,0);
    if(asInline==0){
      style_emit_script_builtin(0, "fossil.bootstrap.js");
    }
  }
}

/*
** If passed 0 as its first argument, it emits a script opener tag
** with this request's nonce. If passed non-0 it emits a script
** closing tag. Mnemonic for remembering the order in which to pass 0
** or 1 as the first argument to this function: 0 comes before 1.
**
** If passed 0 as its first argument and a non-NULL/non-empty zSrc,
** then it instead emits:
**
** <script src='%R/{{zSrc}}'></script>
**
** zSrc is always assumed to be a repository-relative path without
** a leading slash, and has %R/ prepended to it.
**
** Meaning that no follow-up call to pass a non-0 first argument
** to close the tag. zSrc is ignored if the first argument is not
** 0.
**
*/
void style_emit_script_tag(int isCloser, const char * zSrc){
  if(0==isCloser){
    if(zSrc!=0 && zSrc[0]!=0){
      CX("<script src='%R/%T'></script>\n", zSrc);
    }else{
      CX("<script nonce='%s'>", style_nonce());
    }
  }else{
    CX("</script>\n");
  }
}

/*
** Emits a script tag which uses content from a builtin script file.
**
** If asInline is true, it is emitted directly as an opening tag, the
** content of the zName builtin file, and a closing tag.
**
** If it is false, a script tag loading it via
** src=builtin/{{zName}}?cache=XYZ is emitted, where XYZ is a
** build-time-dependent cache-buster value.
*/
void style_emit_script_builtin(int asInline, char const * zName){
  if(asInline){
    style_emit_script_tag(0,0);
    CX("%s", builtin_text(zName));
    style_emit_script_tag(1,0);
  }else{
    char * zFullName = mprintf("builtin/%s",zName);
    const char * zHash = fossil_exe_id();
    CX("<script src='%R/%T?cache=%.8s'></script>\n",
       zFullName, zHash);
    fossil_free(zFullName);
  }
}

/*
** The first time this is called it emits the JS code from the
** built-in file fossil.fossil.js. Subsequent calls are no-ops.
**
** If passed a true value, it emits the contents directly
** to the page output, else it emits a script tag with a
** src=builtin/... to load the script.
**
** Note that this code relies on that loaded via
** style_emit_script_fossil_bootstrap() but it does not call that
** routine.
*/
void style_emit_script_fetch(int asInline){
  static int once = 0;
  if(0==once++){
    style_emit_script_builtin(asInline, "fossil.fetch.js");
  }
}

/*
** The first time this is called it emits the JS code from the
** built-in file fossil.dom.js. Subsequent calls are no-ops.
**
** If passed a true value, it emits the contents directly
** to the page output, else it emits a script tag with a
** src=builtin/... to load the script.
**
** Note that this code relies on that loaded via
** style_emit_script_fossil_bootstrap(), but it does not call that
** routine.
*/
void style_emit_script_dom(int asInline){
  static int once = 0;
  if(0==once++){
    style_emit_script_builtin(asInline, "fossil.dom.js");
  }
}

/*
** The first time this is called, it calls style_emit_script_dom(),
** passing it the given asInline value, and emits the JS code from the
** built-in file fossil.tabs.js. Subsequent calls are no-ops.
**
** If passed a true value, it emits the contents directly
** to the page output, else it emits a script tag with a
** src=builtin/... to load the script.
*/
void style_emit_script_tabs(int asInline){
  static int once = 0;
  if(0==once++){
    style_emit_script_dom(asInline);
    style_emit_script_builtin(asInline, "fossil.tabs.js");
  }
}

/*
** The first time this is called it emits the JS code from the
** built-in file fossil.confirmer.js. Subsequent calls are no-ops.
**
** If passed a true value, it emits the contents directly
** to the page output, else it emits a script tag with a
** src=builtin/... to load the script.
*/
void style_emit_script_confirmer(int asInline){
  static int once = 0;
  if(0==once++){
    style_emit_script_builtin(asInline, "fossil.confirmer.js");
  }
}
Added src/terminal.c.


































































































































1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
/*
** Copyright (c) 2020 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)

** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code used to query terminal info
*/

#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



#if INTERFACE
/*
** Terminal size defined in terms of columns and lines.
*/
struct TerminalSize {
  unsigned int nColumns;         /* Number of characters on a single line */
  unsigned int nLines;           /* Number of lines */
};
#endif


/* Get the current terminal size by calling a system service.
**
** Return 1 on success. This sets the size parameters to the values retured by
** the system call, when such is supported; set the size to zero otherwise.
** Return 0 on the system service call failure.
**
** Under Linux/bash the size info is also available from env $LINES, $COLUMNS.
** Or it can be queried using tput `echo -e "lines\ncols"|tput -S`.
** Technically, this info could be cached, but then we'd need to handle
** SIGWINCH signal to requery the terminal on resize event.
*/
int terminal_get_size(TerminalSize *t){
  memset(t, 0, sizeof(*t));

#if defined(TIOCGSIZE)
  {
    struct ttysize ts;
    if( ioctl(STDIN_FILENO, TIOCGSIZE, &ts)!=-1 ){
      t->nColumns = ts.ts_cols;
      t->nLines = ts.ts_lines;
      return 1;
    }
    return 0;
  }
#elif defined(TIOCGWINSZ)
  {
    struct winsize ws;
    if( ioctl(STDIN_FILENO, TIOCGWINSZ, &ws)!=-1 ){
      t->nColumns = ws.ws_col;
      t->nLines = ws.ws_row;
      return 1;
    }
    return 0;
  }
#elif defined(_WIN32)
  {
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) ){
      t->nColumns = csbi.srWindow.Right - csbi.srWindow.Left + 1;
      t->nLines = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
      return 1;
    }
    return 0;
  }
#else
  return 1;
#endif
}

/*
** Return the terminal's current width in columns when available, otherwise
** return the specified default value.
*/
unsigned int terminal_get_width(unsigned int nDefault){
  TerminalSize ts;
  if( terminal_get_size(&ts) ){
    return ts.nColumns;
  }
  return nDefault;
}

/*
** Return the terminal's current height in lines when available, otherwise
** return the specified default value.
*/
unsigned int terminal_get_height(unsigned int nDefault){
  TerminalSize ts;
  if( terminal_get_size(&ts) ){
    return ts.nLines;
  }
  return nDefault;
}

/*
** COMMAND: test-terminal-size
**
** Show the size of the terminal window from which the command is launched
** as two integers, the width in charaters and the height in lines.
**
** If the size cannot be determined, two zeros are shown.
*/
void test_terminal_size_cmd(void){
  TerminalSize ts;
  terminal_get_size(&ts);
  fossil_print("%d %d\n", ts.nColumns, ts.nLines);
}
Changes to src/timeline.c.
1310
1311
1312
1313
1314
1315
1316
1317

1318
1319
1320
1321
1322
1323
1324
1310
1311
1312
1313
1314
1315
1316

1317
1318
1319
1320
1321
1322
1323
1324







-
+







  const char *zChng,        /* The filename GLOB list */
  Blob *pSql                /* The SELECT statement under construction */
){
  if( zChng==0 || zChng[0]==0 ) return;
  blob_append_sql(pSql," AND event.objid IN ("
      "SELECT mlink.mid FROM mlink, filename"
      " WHERE mlink.fnid=filename.fnid AND %s)",
      glob_expr("filename.name", zChng));
      glob_expr("filename.name", mprintf("\"%s\"", zChng)));
}
static void addFileGlobDescription(
  const char *zChng,        /* The filename GLOB list */
  Blob *pDescription        /* Result description */
){
  if( zChng==0 || zChng[0]==0 ) return;
  blob_appendf(pDescription, " that include changes to files matching '%h'",
1740
1741
1742
1743
1744
1745
1746

1747
1748
1749
1750
1751
1752
1753
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754







+







  login_check_credentials();
  if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum)
   || (bisectLocal && !g.perm.Setup)
  ){
    login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
    return;
  }
  etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA, 0);
  cookie_read_parameter("y","y");
  zType = P("y");
  if( zType==0 ){
    zType = g.perm.Read ? "ci" : "all";
    cgi_set_parameter("y", zType);
  }
  if( zType[0]=='a' || zType[0]=='c' ){
Changes to src/unversioned.c.
522
523
524
525
526
527
528

529
530
531
532
533
534
535
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536







+







  int n = 0;
  const char *zOrderBy = "name";
  int showDel = 0;
  char zSzName[100];

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  etag_check(ETAG_DATA,0);
  style_header("Unversioned Files");
  if( !db_table_exists("repository","unversioned") ){
    @ No unversioned files on this server
    style_footer();
    return;
  }
  if( PB("byage") ) zOrderBy = "mtime DESC";
634
635
636
637
638
639
640

641
642
643
644
645
646
647
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649







+







  Stmt q;
  char *zSep = "[";
  Blob json;

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  cgi_set_content_type("text/json");
  etag_check(ETAG_DATA,0);
  if( !db_table_exists("repository","unversioned") ){
    blob_init(&json, "[]", -1);
    cgi_set_content(&json);
    return;
  }
  blob_init(&json, 0, 0);
  db_prepare(&q,
Changes to src/url.c.
375
376
377
378
379
380
381

382
383
384

385
386
387
388
389
390
391
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393







+



+







**                         WARNING: this option opens you up to
**                         forged-DNS and man-in-the-middle attacks!
*/
void url_proxy_options(void){
  zProxyOpt = find_option("proxy", 0, 1);
  if( find_option("nosync",0,0) ) g.fNoSync = 1;
  if( find_option("ipv4",0,0) ) g.fIPv4 = 1;
#ifdef FOSSIL_ENABLE_SSL
  if( find_option("accept-any-cert",0,0) ){
    ssl_disable_cert_verification();
  }
#endif /* FOSSIL_ENABLE_SSL */
}

/*
** If the "proxy" setting is defined, then change the URL settings
** (initialized by a prior call to url_parse()) so that the HTTP
** header will be appropriate for the proxy and so that the TCP/IP
** connection will be opened to the proxy rather than to the server.
Added test/subdir with spaces/filename with spaces.txt.


1
2
+
+
This file has a name that contains spaces.  It is used to help verify
that fossil can handle filenames that contain spaces.
Added tools/fossil-diff-log.








































































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/usr/bin/env perl 
# Fossil emulation of the "git log --patch / -p" feature: emit a stream
# of diffs from one version to the next for each file named on the
# command line.
#
# LIMITATIONS: It does not assume "all files" if you give no args, and
# it cannot take a directory to mean "all files under this parent".
#
# PREREQUISITES: This script needs several CPAN modules to run properly.
# There are multiple methods to install them:
#
#    sudo dnf install perl-File-Which perl-IO-Interactive
#    sudo apt install libfile-which-perl libio-interactive-perl
#    sudo cpanm File::Which IO::Interactive
#    ...etc...

use strict;
use warnings;

use Carp;
use File::Which;
use IO::Interactive qw(is_interactive);

die "usage: $0 <files...>\n\n" unless @ARGV;

my $out;
if (is_interactive()) {
	my $pager = $ENV{PAGER} || which('less') || which('more');
	open $out, '|-', $pager or croak "Cannot pipe to $pager: $!";
}
else {
	$out = *STDOUT;
}

open my $bcmd, '-|', 'fossil branch current'
		or die "Cannot get branch: $!\n";
my $cbranch = <$bcmd>;
chomp $cbranch;
close $bcmd;

for my $file (@ARGV) {
	my $lastckid;
	open my $finfo, '-|', "fossil finfo --brief --limit 0 '$file'"
			or die "Failed to get file info: $!\n";
	my @filines = <$finfo>;
	close $finfo;
	
	for my $line (@filines) {
		my ($currckid, $date, $user, $branch, @cwords) = split ' ', $line;
		next unless $branch eq $cbranch;
		if (defined $lastckid and defined $branch) {
            my $comment = join ' ', @cwords;
			open my $diff, '-|', 'fossil', 'diff', $file,
					'--from', $currckid,
					'--to',   $lastckid,
					or die "Failed to diff $currckid -> $lastckid: $!\n";
			my @dl = <$diff>;
			close $diff;
			my $patch = join '', @dl;

			print $out <<"OUT"
Checkin ID $currckid to $branch by $user on $date
Comment: $comment

$patch

OUT
		}

		$lastckid = $currckid;
	}
}
Changes to win/Makefile.dmc.
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
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







-
+

-
+


















-
+







TCC    = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL)
LIBS   = $(DMDIR)\extra\lib\ zlib wsock32 advapi32 dnsapi

SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0

SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen

SRC   = add_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c
SRC   = add_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c

OBJ   = $(OBJDIR)\add$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
OBJ   = $(OBJDIR)\add$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O


RC=$(DMDIR)\bin\rcc
RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__

APPNAME = $(OBJDIR)\fossil$(E)

all: $(APPNAME)

$(APPNAME) : translate$E mkindex$E codecheck1$E headers  $(OBJ) $(OBJDIR)\link
	cd $(OBJDIR)
	codecheck1$E $(SRC)
	$(DMDIR)\bin\link @link

$(OBJDIR)\fossil.res:	$B\win\fossil.rc
	$(RC) $(RCFLAGS) -o$@ $**

$(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
	+echo add alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
	+echo add alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
	+echo fossil >> $@
	+echo fossil >> $@
	+echo $(LIBS) >> $@
	+echo. >> $@
	+echo fossil >> $@

translate$E: $(SRCDIR)\translate.c
834
835
836
837
838
839
840






841
842
843
844
845
846
847
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853







+
+
+
+
+
+







	+translate$E $** > $@

$(OBJDIR)\tar$O : tar_.c tar.h
	$(TCC) -o$@ -c tar_.c

tar_.c : $(SRCDIR)\tar.c
	+translate$E $** > $@

$(OBJDIR)\terminal$O : terminal_.c terminal.h
	$(TCC) -o$@ -c terminal_.c

terminal_.c : $(SRCDIR)\terminal.c
	+translate$E $** > $@

$(OBJDIR)\th_main$O : th_main_.c th_main.h
	$(TCC) -o$@ -c th_main_.c

th_main_.c : $(SRCDIR)\th_main.c
	+translate$E $** > $@

974
975
976
977
978
979
980
981

982
980
981
982
983
984
985
986

987
988







-
+

$(OBJDIR)\zip$O : zip_.c zip.h
	$(TCC) -o$@ -c zip_.c

zip_.c : $(SRCDIR)\zip.c
	+translate$E $** > $@

headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h
	 +makeheaders$E add_.c:add.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
	 +makeheaders$E add_.c:add.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
	@copy /Y nul: headers
Changes to win/Makefile.mingw.
552
553
554
555
556
557
558

559
560
561
562
563
564
565
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566







+







  $(SRCDIR)/stash.c \
  $(SRCDIR)/stat.c \
  $(SRCDIR)/statrep.c \
  $(SRCDIR)/style.c \
  $(SRCDIR)/sync.c \
  $(SRCDIR)/tag.c \
  $(SRCDIR)/tar.c \
  $(SRCDIR)/terminal.c \
  $(SRCDIR)/th_main.c \
  $(SRCDIR)/timeline.c \
  $(SRCDIR)/tkt.c \
  $(SRCDIR)/tktsetup.c \
  $(SRCDIR)/undo.c \
  $(SRCDIR)/unicode.c \
  $(SRCDIR)/unversioned.c \
638
639
640
641
642
643
644






645
646
647
648
649
650
651
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658







+
+
+
+
+
+







  $(SRCDIR)/../skins/xekri/footer.txt \
  $(SRCDIR)/../skins/xekri/header.txt \
  $(SRCDIR)/accordion.js \
  $(SRCDIR)/ci_edit.js \
  $(SRCDIR)/copybtn.js \
  $(SRCDIR)/diff.tcl \
  $(SRCDIR)/forum.js \
  $(SRCDIR)/fossil.bootstrap.js \
  $(SRCDIR)/fossil.confirmer.js \
  $(SRCDIR)/fossil.dom.js \
  $(SRCDIR)/fossil.fetch.js \
  $(SRCDIR)/fossil.page.fileedit.js \
  $(SRCDIR)/fossil.tabs.js \
  $(SRCDIR)/graph.js \
  $(SRCDIR)/href.js \
  $(SRCDIR)/login.js \
  $(SRCDIR)/markdown.md \
  $(SRCDIR)/menu.js \
  $(SRCDIR)/sbsdiff.js \
  $(SRCDIR)/scroll.js \
786
787
788
789
790
791
792

793
794
795
796
797
798
799
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807







+







  $(OBJDIR)/stash_.c \
  $(OBJDIR)/stat_.c \
  $(OBJDIR)/statrep_.c \
  $(OBJDIR)/style_.c \
  $(OBJDIR)/sync_.c \
  $(OBJDIR)/tag_.c \
  $(OBJDIR)/tar_.c \
  $(OBJDIR)/terminal_.c \
  $(OBJDIR)/th_main_.c \
  $(OBJDIR)/timeline_.c \
  $(OBJDIR)/tkt_.c \
  $(OBJDIR)/tktsetup_.c \
  $(OBJDIR)/undo_.c \
  $(OBJDIR)/unicode_.c \
  $(OBJDIR)/unversioned_.c \
929
930
931
932
933
934
935

936
937
938
939
940
941
942
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951







+







 $(OBJDIR)/stash.o \
 $(OBJDIR)/stat.o \
 $(OBJDIR)/statrep.o \
 $(OBJDIR)/style.o \
 $(OBJDIR)/sync.o \
 $(OBJDIR)/tag.o \
 $(OBJDIR)/tar.o \
 $(OBJDIR)/terminal.o \
 $(OBJDIR)/th_main.o \
 $(OBJDIR)/timeline.o \
 $(OBJDIR)/tkt.o \
 $(OBJDIR)/tktsetup.o \
 $(OBJDIR)/undo.o \
 $(OBJDIR)/unicode.o \
 $(OBJDIR)/unversioned.o \
1292
1293
1294
1295
1296
1297
1298

1299
1300
1301
1302
1303
1304
1305
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315







+







		$(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \
		$(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \
		$(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \
		$(OBJDIR)/style_.c:$(OBJDIR)/style.h \
		$(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \
		$(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \
		$(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \
		$(OBJDIR)/terminal_.c:$(OBJDIR)/terminal.h \
		$(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \
		$(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
		$(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
		$(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
		$(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
		$(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \
		$(OBJDIR)/unversioned_.c:$(OBJDIR)/unversioned.h \
2267
2268
2269
2270
2271
2272
2273








2274
2275
2276
2277
2278
2279
2280
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298







+
+
+
+
+
+
+
+







$(OBJDIR)/tar_.c:	$(SRCDIR)/tar.c $(TRANSLATE)
	$(TRANSLATE) $(SRCDIR)/tar.c >$@

$(OBJDIR)/tar.o:	$(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c

$(OBJDIR)/tar.h:	$(OBJDIR)/headers

$(OBJDIR)/terminal_.c:	$(SRCDIR)/terminal.c $(TRANSLATE)
	$(TRANSLATE) $(SRCDIR)/terminal.c >$@

$(OBJDIR)/terminal.o:	$(OBJDIR)/terminal_.c $(OBJDIR)/terminal.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/terminal.o -c $(OBJDIR)/terminal_.c

$(OBJDIR)/terminal.h:	$(OBJDIR)/headers

$(OBJDIR)/th_main_.c:	$(SRCDIR)/th_main.c $(TRANSLATE)
	$(TRANSLATE) $(SRCDIR)/th_main.c >$@

$(OBJDIR)/th_main.o:	$(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/th_main.o -c $(OBJDIR)/th_main_.c

Changes to win/Makefile.mingw.mistachkin.
551
552
553
554
555
556
557

558
559
560
561
562
563
564
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565







+







  $(SRCDIR)/stash.c \
  $(SRCDIR)/stat.c \
  $(SRCDIR)/statrep.c \
  $(SRCDIR)/style.c \
  $(SRCDIR)/sync.c \
  $(SRCDIR)/tag.c \
  $(SRCDIR)/tar.c \
  $(SRCDIR)/terminal.c \
  $(SRCDIR)/th_main.c \
  $(SRCDIR)/timeline.c \
  $(SRCDIR)/tkt.c \
  $(SRCDIR)/tktsetup.c \
  $(SRCDIR)/undo.c \
  $(SRCDIR)/unicode.c \
  $(SRCDIR)/unversioned.c \
784
785
786
787
788
789
790

791
792
793
794
795
796
797
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799







+







  $(OBJDIR)/stash_.c \
  $(OBJDIR)/stat_.c \
  $(OBJDIR)/statrep_.c \
  $(OBJDIR)/style_.c \
  $(OBJDIR)/sync_.c \
  $(OBJDIR)/tag_.c \
  $(OBJDIR)/tar_.c \
  $(OBJDIR)/terminal_.c \
  $(OBJDIR)/th_main_.c \
  $(OBJDIR)/timeline_.c \
  $(OBJDIR)/tkt_.c \
  $(OBJDIR)/tktsetup_.c \
  $(OBJDIR)/undo_.c \
  $(OBJDIR)/unicode_.c \
  $(OBJDIR)/unversioned_.c \
926
927
928
929
930
931
932

933
934
935
936
937
938
939
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942







+







 $(OBJDIR)/stash.o \
 $(OBJDIR)/stat.o \
 $(OBJDIR)/statrep.o \
 $(OBJDIR)/style.o \
 $(OBJDIR)/sync.o \
 $(OBJDIR)/tag.o \
 $(OBJDIR)/tar.o \
 $(OBJDIR)/terminal.o \
 $(OBJDIR)/th_main.o \
 $(OBJDIR)/timeline.o \
 $(OBJDIR)/tkt.o \
 $(OBJDIR)/tktsetup.o \
 $(OBJDIR)/undo.o \
 $(OBJDIR)/unicode.o \
 $(OBJDIR)/unversioned.o \
1288
1289
1290
1291
1292
1293
1294

1295
1296
1297
1298
1299
1300
1301
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305







+







		$(OBJDIR)/stash_.c:$(OBJDIR)/stash.h \
		$(OBJDIR)/stat_.c:$(OBJDIR)/stat.h \
		$(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \
		$(OBJDIR)/style_.c:$(OBJDIR)/style.h \
		$(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \
		$(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \
		$(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \
		$(OBJDIR)/terminal_.c:$(OBJDIR)/terminal.h \
		$(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \
		$(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
		$(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
		$(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
		$(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
		$(OBJDIR)/unicode_.c:$(OBJDIR)/unicode.h \
		$(OBJDIR)/unversioned_.c:$(OBJDIR)/unversioned.h \
2255
2256
2257
2258
2259
2260
2261








2262
2263
2264
2265
2266
2267
2268
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280







+
+
+
+
+
+
+
+







$(OBJDIR)/tar_.c:	$(SRCDIR)/tar.c $(TRANSLATE)
	$(TRANSLATE) $(SRCDIR)/tar.c >$@

$(OBJDIR)/tar.o:	$(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c

$(OBJDIR)/tar.h:	$(OBJDIR)/headers

$(OBJDIR)/terminal_.c:	$(SRCDIR)/terminal.c $(TRANSLATE)
	$(TRANSLATE) $(SRCDIR)/terminal.c >$@

$(OBJDIR)/terminal.o:	$(OBJDIR)/terminal_.c $(OBJDIR)/terminal.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/terminal.o -c $(OBJDIR)/terminal_.c

$(OBJDIR)/terminal.h:	$(OBJDIR)/headers

$(OBJDIR)/th_main_.c:	$(SRCDIR)/th_main.c $(TRANSLATE)
	$(TRANSLATE) $(SRCDIR)/th_main.c >$@

$(OBJDIR)/th_main.o:	$(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/th_main.o -c $(OBJDIR)/th_main_.c

Changes to win/Makefile.msc.
460
461
462
463
464
465
466

467
468
469
470
471
472
473
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474







+







        stash_.c \
        stat_.c \
        statrep_.c \
        style_.c \
        sync_.c \
        tag_.c \
        tar_.c \
        terminal_.c \
        th_main_.c \
        timeline_.c \
        tkt_.c \
        tktsetup_.c \
        undo_.c \
        unicode_.c \
        unversioned_.c \
545
546
547
548
549
550
551






552
553
554
555
556
557
558
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565







+
+
+
+
+
+







        $(SRCDIR)\..\skins\xekri\footer.txt \
        $(SRCDIR)\..\skins\xekri\header.txt \
        $(SRCDIR)\accordion.js \
        $(SRCDIR)\ci_edit.js \
        $(SRCDIR)\copybtn.js \
        $(SRCDIR)\diff.tcl \
        $(SRCDIR)\forum.js \
        $(SRCDIR)\fossil.bootstrap.js \
        $(SRCDIR)\fossil.confirmer.js \
        $(SRCDIR)\fossil.dom.js \
        $(SRCDIR)\fossil.fetch.js \
        $(SRCDIR)\fossil.page.fileedit.js \
        $(SRCDIR)\fossil.tabs.js \
        $(SRCDIR)\graph.js \
        $(SRCDIR)\href.js \
        $(SRCDIR)\login.js \
        $(SRCDIR)\markdown.md \
        $(SRCDIR)\menu.js \
        $(SRCDIR)\sbsdiff.js \
        $(SRCDIR)\scroll.js \
695
696
697
698
699
700
701

702
703
704
705
706
707
708
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716







+







        $(OX)\stash$O \
        $(OX)\stat$O \
        $(OX)\statrep$O \
        $(OX)\style$O \
        $(OX)\sync$O \
        $(OX)\tag$O \
        $(OX)\tar$O \
        $(OX)\terminal$O \
        $(OX)\th$O \
        $(OX)\th_lang$O \
        $(OX)\th_main$O \
        $(OX)\th_tcl$O \
        $(OX)\timeline$O \
        $(OX)\tkt$O \
        $(OX)\tktsetup$O \
900
901
902
903
904
905
906

907
908
909
910
911
912
913
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922







+







	echo $(OX)\stash.obj >> $@
	echo $(OX)\stat.obj >> $@
	echo $(OX)\statrep.obj >> $@
	echo $(OX)\style.obj >> $@
	echo $(OX)\sync.obj >> $@
	echo $(OX)\tag.obj >> $@
	echo $(OX)\tar.obj >> $@
	echo $(OX)\terminal.obj >> $@
	echo $(OX)\th.obj >> $@
	echo $(OX)\th_lang.obj >> $@
	echo $(OX)\th_main.obj >> $@
	echo $(OX)\th_tcl.obj >> $@
	echo $(OX)\timeline.obj >> $@
	echo $(OX)\tkt.obj >> $@
	echo $(OX)\tktsetup.obj >> $@
1751
1752
1753
1754
1755
1756
1757






1758
1759
1760
1761
1762
1763
1764
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779







+
+
+
+
+
+







	translate$E $** > $@

$(OX)\tar$O : tar_.c tar.h
	$(TCC) /Fo$@ -c tar_.c

tar_.c : $(SRCDIR)\tar.c
	translate$E $** > $@

$(OX)\terminal$O : terminal_.c terminal.h
	$(TCC) /Fo$@ -c terminal_.c

terminal_.c : $(SRCDIR)\terminal.c
	translate$E $** > $@

$(OX)\th_main$O : th_main_.c th_main.h
	$(TCC) /Fo$@ -c th_main_.c

th_main_.c : $(SRCDIR)\th_main.c
	translate$E $** > $@

2012
2013
2014
2015
2016
2017
2018

2019
2020
2021
2022
2023
2024
2025
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041







+







			stash_.c:stash.h \
			stat_.c:stat.h \
			statrep_.c:statrep.h \
			style_.c:style.h \
			sync_.c:sync.h \
			tag_.c:tag.h \
			tar_.c:tar.h \
			terminal_.c:terminal.h \
			th_main_.c:th_main.h \
			timeline_.c:timeline.h \
			tkt_.c:tkt.h \
			tktsetup_.c:tktsetup.h \
			undo_.c:undo.h \
			unicode_.c:unicode.h \
			unversioned_.c:unversioned.h \
Changes to www/changes.wiki.
53
54
55
56
57
58
59


60
61
62
63
64
65
66
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68







+
+







     database it opens has been tampered with by an adversary and takes
     extra precautions to ensure that such tampering is harmless.
  *  Security: Fossil now puts the Content-Security-Policy in the
     HTTP reply header, in addition to also leaving it in the
     HTML &lt;head&gt; section, so that it is always available, even
     if a custom skin overrides the HTML &lt;head&gt; and omits
     the CSP in the process.
  *  Output of the [/help?cmd=diff|fossil diff -y] command automatically
     adjusts according to the terminal width.
  *  The Content-Security-Policy is now set using the
     [/help?cmd=default-csp|default-csp setting].
  *  Merge conflicts caused via the [/help?cmd=merge|merge] and
     [/help?cmd=update|update] commands no longer leave temporary
     files behind unless the new <tt>--keep-merge-files</tt> flag
     is used.
  *  The [/help?cmd=/artifact_stats|/artifact_stats page] is now accessible