Check-in [0e31c8d258]
Not logged in

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

Overview
Comment:Merge version-2.14
Timelines: family | ancestors | descendants | both | wiki-history
Files: files | file ages | folders
SHA3-256: 0e31c8d25896220092c79840324a02a0c6d1bfed740319f58e669cea9973959b
User & Date: george 2021-01-20 19:19:22
Context
2021-02-25
14:15
Rearrange the JavaScript code as a handler of the 'load' event; this keeps the global scope clean from the supplementary variables. check-in: ec55e51c44 user: george tags: wiki-history
2021-01-20
19:19
Merge version-2.14 check-in: 0e31c8d258 user: george tags: wiki-history
15:34
Version 2.14 check-in: 487776dc45 user: drh tags: trunk, release, version-2.14
2020-12-15
20:50
Merge from trunk check-in: 7b9ca24cd2 user: george tags: wiki-history
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to .fossil-settings/binary-glob.

1
2
3
4
5
6
7

8
9
10
11
*.gif
*.ico
*.jpg
*.odp
*.dia
*.pdf
*.png

compat/zlib/contrib/blast/test.pk
compat/zlib/contrib/dotzlib/DotZLib.chm
compat/zlib/contrib/puff/zeros.raw
compat/zlib/zlib.3.pdf







>




1
2
3
4
5
6
7
8
9
10
11
12
*.gif
*.ico
*.jpg
*.odp
*.dia
*.pdf
*.png
*.wav
compat/zlib/contrib/blast/test.pk
compat/zlib/contrib/dotzlib/DotZLib.chm
compat/zlib/contrib/puff/zeros.raw
compat/zlib/zlib.3.pdf

Changes to .fossil-settings/clean-glob.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
*.a
*.lib
*.manifest
*.o
*.obj
*.pdb
*.res
Makefile
autosetup/jimsh0
autosetup/jimsh0.exe
bld/*
wbld/*
win/*.c
win/*.h
win/*.exe
win/headers
win/linkopts
autoconfig.h
config.log











|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
*.a
*.lib
*.manifest
*.o
*.obj
*.pdb
*.res
Makefile
autosetup/jimsh0
autosetup/jimsh0.exe
bld/*
msvcbld/*
win/*.c
win/*.h
win/*.exe
win/headers
win/linkopts
autoconfig.h
config.log

Changes to .fossil-settings/ignore-glob.

1
2

3
4
5
6
7


compat/openssl*
compat/tcl*

fossil
fossil.exe
win/fossil.exe
*shell-see.*
*sqlite3-see.*




>





>
>
1
2
3
4
5
6
7
8
9
10
compat/openssl*
compat/tcl*
compat/zlib*
fossil
fossil.exe
win/fossil.exe
*shell-see.*
*sqlite3-see.*
bld/*
msvcbld/*

Changes to auto.def.

568
569
570
571
572
573
574








575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590


591
592
593
594
595
596
597
598

# Other nonstandard function checks
cc-check-functions utime
cc-check-functions usleep
cc-check-functions strchrnul
cc-check-functions pledge
cc-check-functions backtrace









# Check for getloadavg(), and if it doesn't exist, define FOSSIL_OMIT_LOAD_AVERAGE
if {![cc-check-functions getloadavg]} {
  define FOSSIL_OMIT_LOAD_AVERAGE 1
  msg-result "Load average support unavailable"
}

# Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars
if {![cc-check-functions getpassphrase]} {
    # Haiku needs this
    cc-check-function-in-lib getpass bsd
}
cc-check-function-in-lib sin m

# Check for the FuseFS library
if {[opt-bool fusefs]} {


  if {[cc-check-function-in-lib fuse_mount fuse]} {
     define-append EXTRA_CFLAGS -DFOSSIL_HAVE_FUSEFS
     define FOSSIL_HAVE_FUSEFS 1
     define-append LIBS -lfuse
     msg-result "FuseFS support enabled"
  }
}








>
>
>
>
>
>
>
>
















>
>
|







568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608

# Other nonstandard function checks
cc-check-functions utime
cc-check-functions usleep
cc-check-functions strchrnul
cc-check-functions pledge
cc-check-functions backtrace

# Termux on Android adds "getpass(char *)" to unistd.h, so check this so we
# guard against including it again; use cctest as cc-check-functions and 
# cctest_function check for "getpass()" with no args and fail
if {[cctest -link 1 -includes {unistd.h} -code "getpass(0);"]} {
    define FOSSIL_HAVE_GETPASS 1
    msg-result "Found getpass() with unistd.h"
}

# Check for getloadavg(), and if it doesn't exist, define FOSSIL_OMIT_LOAD_AVERAGE
if {![cc-check-functions getloadavg]} {
  define FOSSIL_OMIT_LOAD_AVERAGE 1
  msg-result "Load average support unavailable"
}

# Check for getpassphrase() for Solaris 10 where getpass() truncates to 10 chars
if {![cc-check-functions getpassphrase]} {
    # Haiku needs this
    cc-check-function-in-lib getpass bsd
}
cc-check-function-in-lib sin m

# Check for the FuseFS library
if {[opt-bool fusefs]} {
  if {[opt-bool static]} {
     msg-result "FuseFS support disabled due to -static"
  } elseif {[cc-check-function-in-lib fuse_mount fuse]} {
     define-append EXTRA_CFLAGS -DFOSSIL_HAVE_FUSEFS
     define FOSSIL_HAVE_FUSEFS 1
     define-append LIBS -lfuse
     msg-result "FuseFS support enabled"
  }
}

606
607
608
609
610
611
612
613
614
615
616
617
618
619






620
621
622
    if {[string first "undefined" $fsan] != -1} {
        # We need to link with libubsan if we're compiling under
        # GCC with -fsanitize=undefined.
        cc-check-function-in-lib __ubsan_handle_add_overflow ubsan
    }
}

# Finally, append -ldl to make sure it's the last in the list.
# The library order matters in case of static linking.
if {[check-function-in-lib dlopen dl]} {
    # Some platforms (*BSD) have the dl functions already in libc and no libdl.
    # In such case we can link directly without -ldl.
    define-append LIBS [get-define lib_dlopen]
}







make-template Makefile.in
make-config-header autoconfig.h -auto {USE_* FOSSIL_*}







|
|





>
>
>
>
>
>



616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
    if {[string first "undefined" $fsan] != -1} {
        # We need to link with libubsan if we're compiling under
        # GCC with -fsanitize=undefined.
        cc-check-function-in-lib __ubsan_handle_add_overflow ubsan
    }
}

# Finally, append libraries that must be last. This matters more on some
# OSes than others, but is most broadly required for static linking.
if {[check-function-in-lib dlopen dl]} {
    # Some platforms (*BSD) have the dl functions already in libc and no libdl.
    # In such case we can link directly without -ldl.
    define-append LIBS [get-define lib_dlopen]
}
if {[opt-bool static]} {
    # Linux can only infer the dependency on pthread from OpenSSL when
    # doing dynamic linkage.
    define-append LIBS -lpthread
}


make-template Makefile.in
make-config-header autoconfig.h -auto {USE_* FOSSIL_*}

Changes to fossil.1.

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
\fICOMMAND [OPTIONS]\fR
.SH DESCRIPTION
Fossil is a distributed version control system (DVCS) with built-in
forum, wiki, ticket tracker, CGI/HTTP interface, and HTTP server.

.SH Common COMMANDs:

add            clean          help           push           timeline
.br
addremove      clone          import         rebuild        ui
.br
all            commit         info           remote-url     undo
.br
amend          delete         init           revert         unpublished
.br
annotate       diff           ls             rm             unversioned
.br
bisect         export         merge          settings       update
.br
blame          extras         mv             sql            version
.br
branch         finfo          open           stash
.br
bundle         fusefs         praise         status
.br
cat            gdiff          publish        sync
.br
changes        grep           pull           tag

.SH FEATURES

Features as described on the fossil home page.

.HP
1.







|

|

|

|

|

|

|

|

<
<
<
<
<







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
\fICOMMAND [OPTIONS]\fR
.SH DESCRIPTION
Fossil is a distributed version control system (DVCS) with built-in
forum, wiki, ticket tracker, CGI/HTTP interface, and HTTP server.

.SH Common COMMANDs:

add          cat          diff         ls           revert       timeline
.br
addremove    changes      extras       merge        rm           ui
.br
all          chat         finfo        mv           settings     undo
.br
amend        clean        gdiff        open         sql          unversioned
.br
annotate     clone        grep         pull         stash        update
.br
bisect       commit       help         push         status       version
.br
blame        dbstat       info         rebuild      sync
.br
branch       delete       init         remote       tag
.br






.SH FEATURES

Features as described on the fossil home page.

.HP
1.

Changes to skins/bootstrap/header.txt.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
      if(/^#/.test(x)) x = x.substr(1);
      var e = document.getElementById(x);
      if(!e) throw new Error("Expecting element with ID "+x);
      else return e;
    }
    </script>
  </head>
  <body data-spy="scroll" data-target=".sidebar">
    <div id="wrap">
      <div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
        <div class="container">
          <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
              <span class="sr-only">Toggle navigation</span>
              <span class="icon-bar"></span>







|







12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
      if(/^#/.test(x)) x = x.substr(1);
      var e = document.getElementById(x);
      if(!e) throw new Error("Expecting element with ID "+x);
      else return e;
    }
    </script>
  </head>
  <body data-spy="scroll" data-target=".sidebar" class="$current_feature">
    <div id="wrap">
      <div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
        <div class="container">
          <div class="navbar-header">
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target=".navbar-collapse">
              <span class="sr-only">Toggle navigation</span>
              <span class="icon-bar"></span>

Changes to skins/default/header.txt.

31
32
33
34
35
36
37



38
39
40
41
42
43
44
if {[hascap o]} {
  menulink  /brlist Branches wideonly
  menulink  /taglist Tags wideonly
}
if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
  menulink /forum Forum desktoponly
}



if {[hascap r]} {
  menulink /ticket Tickets wideonly
}
if {[hascap j]} {
  menulink /wiki Wiki wideonly
}
if {[hascap s]} {







>
>
>







31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
if {[hascap o]} {
  menulink  /brlist Branches wideonly
  menulink  /taglist Tags wideonly
}
if {[anycap 23456] || [anoncap 2] || [anoncap 3]} {
  menulink /forum Forum desktoponly
}
if {[hascap C]} {
  menulink /chat Chat wideonly
}
if {[hascap r]} {
  menulink /ticket Tickets wideonly
}
if {[hascap j]} {
  menulink /wiki Wiki wideonly
}
if {[hascap s]} {

Changes to skins/xekri/css.txt.

1117
1118
1119
1120
1121
1122
1123

1124
1125
1126
1127
  /* use default */
}
/* odd table row color */
tr.row1 {
  /* Use default */
}


.fossil-tooltip.help-buttonlet-content {
  background-color: #111;
  border: 1px solid rgba(255,255,255,0.5);
}







>




1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
  /* use default */
}
/* odd table row color */
tr.row1 {
  /* Use default */
}

.fossil-PopupWidget,
.fossil-tooltip.help-buttonlet-content {
  background-color: #111;
  border: 1px solid rgba(255,255,255,0.5);
}

Changes to src/alerts.c.

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
  if( !alert_tables_exist() ){
    if( bOnlyIfEnabled
     && fossil_strcmp(db_get("email-send-method",0),"off")==0
    ){
      return;  /* Don't create table for disabled email */
    }
    db_exec_sql(zAlertInit);
    alert_triggers_enable();
  }else if( !db_table_has_column("repository","pending_alert","sentMod") ){
    db_multi_exec(
      "ALTER TABLE repository.pending_alert"
      " ADD COLUMN sentMod BOOLEAN DEFAULT false;"
    );
  }
}

/*
** Enable triggers that automatically populate the pending_alert
** table.
*/
void alert_triggers_enable(void){
  if( !db_table_exists("repository","pending_alert") ) return;
  db_multi_exec(
    "CREATE TRIGGER IF NOT EXISTS repository.alert_trigger1\n"


    "AFTER INSERT ON event BEGIN\n"
    "  INSERT INTO pending_alert(eventid)\n"
    "    SELECT printf('%%.1c%%d',new.type,new.objid) WHERE true\n"
    "    ON CONFLICT(eventId) DO NOTHING;\n"
    "END;"
  );
}

/*
** Disable triggers the event_pending triggers.
**
** This must be called before rebuilding the EVENT table, for example
** via the "fossil rebuild" command.
*/
void alert_triggers_disable(void){
  db_multi_exec(
    "DROP TRIGGER IF EXISTS repository.alert_trigger1;\n"
    "DROP TRIGGER IF EXISTS repository.email_trigger1;\n" // Legacy
  );
}

/*
** Return true if email alerts are active.
*/
int alert_enabled(void){
  if( !alert_tables_exist() ) return 0;
  if( fossil_strcmp(db_get("email-send-method",0),"off")==0 ) return 0;
  return 1;
}

/*
** If the subscriber table does not exist, then paint an error message
** web page and return true.
**
** If the subscriber table does exist, return 0 without doing anything.
*/
static int alert_webpages_disabled(void){
  if( alert_tables_exist() ) return 0;

  style_header("Email Alerts Are Disabled");
  @ <p>Email alerts are disabled on this server</p>
  style_finish_page("alerts");
  return 1;
}

/*
** Insert a "Subscriber List" submenu link if the current user
** is an administrator.
*/







<












|


|
>
>
|













|

|
|




















>


|







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
  if( !alert_tables_exist() ){
    if( bOnlyIfEnabled
     && fossil_strcmp(db_get("email-send-method",0),"off")==0
    ){
      return;  /* Don't create table for disabled email */
    }
    db_exec_sql(zAlertInit);

  }else if( !db_table_has_column("repository","pending_alert","sentMod") ){
    db_multi_exec(
      "ALTER TABLE repository.pending_alert"
      " ADD COLUMN sentMod BOOLEAN DEFAULT false;"
    );
  }
}

/*
** Enable triggers that automatically populate the pending_alert
** table.
*/
void alert_create_trigger(void){
  if( !db_table_exists("repository","pending_alert") ) return;
  db_multi_exec(
    "DROP TRIGGER IF EXISTS repository.alert_trigger1;\n" /* Purge legacy */
    /* "DROP TRIGGER IF EXISTS repository.email_trigger1;\n" Very old legacy */
    "CREATE TRIGGER temp.alert_trigger1\n"
    "AFTER INSERT ON repository.event BEGIN\n"
    "  INSERT INTO pending_alert(eventid)\n"
    "    SELECT printf('%%.1c%%d',new.type,new.objid) WHERE true\n"
    "    ON CONFLICT(eventId) DO NOTHING;\n"
    "END;"
  );
}

/*
** Disable triggers the event_pending triggers.
**
** This must be called before rebuilding the EVENT table, for example
** via the "fossil rebuild" command.
*/
void alert_drop_trigger(void){
  db_multi_exec(
    "DROP TRIGGER IF EXISTS temp.alert_trigger1;\n"
    "DROP TRIGGER IF EXISTS repository.alert_trigger1;\n" /* Purge legacy */
  );
}

/*
** Return true if email alerts are active.
*/
int alert_enabled(void){
  if( !alert_tables_exist() ) return 0;
  if( fossil_strcmp(db_get("email-send-method",0),"off")==0 ) return 0;
  return 1;
}

/*
** If the subscriber table does not exist, then paint an error message
** web page and return true.
**
** If the subscriber table does exist, return 0 without doing anything.
*/
static int alert_webpages_disabled(void){
  if( alert_tables_exist() ) return 0;
  style_set_current_feature("alerts");
  style_header("Email Alerts Are Disabled");
  @ <p>Email alerts are disabled on this server</p>
  style_finish_page();
  return 1;
}

/*
** Insert a "Subscriber List" submenu link if the current user
** is an administrator.
*/
214
215
216
217
218
219
220

221
222
223
224
225
226
227
    login_needed(0);
    return;
  }
  db_begin_transaction();

  alert_submenu_common();
  style_submenu_element("Send Announcement","%R/announce");

  style_header("Email Notification Setup");
  @ <h1>Status</h1>
  @ <table class="label-value">
  if( alert_enabled() ){
    stats_for_email();
  }else{
    @ <th>Disabled</th>







>







216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
    login_needed(0);
    return;
  }
  db_begin_transaction();

  alert_submenu_common();
  style_submenu_element("Send Announcement","%R/announce");
  style_set_current_feature("alerts");
  style_header("Email Notification Setup");
  @ <h1>Status</h1>
  @ <table class="label-value">
  if( alert_enabled() ){
    stats_for_email();
  }else{
    @ <th>Disabled</th>
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
  @ The default TCP port number is 25.
  @ (Property: "email-send-relayhost")</p>
  @ <hr>

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

#if 0
/*
** Encode pMsg as MIME base64 and append it to pOut
*/
static void append_base64(Blob *pOut, Blob *pMsg){







|







309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
  @ The default TCP port number is 25.
  @ (Property: "email-send-relayhost")</p>
  @ <hr>

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

#if 0
/*
** Encode pMsg as MIME base64 and append it to pOut
*/
static void append_base64(Blob *pOut, Blob *pMsg){
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
          "deleting all subscriber information.  The information will be\n"
          "unrecoverable.\n");
      prompt_user("Continue? (y/N) ", &yn);
      c = blob_str(&yn)[0];
      blob_reset(&yn);
    }
    if( c=='y' ){
      alert_triggers_disable();
      db_multi_exec(
        "DROP TABLE IF EXISTS subscriber;\n"
        "DROP TABLE IF EXISTS pending_alert;\n"
        "DROP TABLE IF EXISTS alert_bounce;\n"
        /* Legacy */
        "DROP TABLE IF EXISTS alert_pending;\n"
        "DROP TABLE IF EXISTS subscription;\n"







|







1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
          "deleting all subscriber information.  The information will be\n"
          "unrecoverable.\n");
      prompt_user("Continue? (y/N) ", &yn);
      c = blob_str(&yn)[0];
      blob_reset(&yn);
    }
    if( c=='y' ){
      alert_drop_trigger();
      db_multi_exec(
        "DROP TABLE IF EXISTS subscriber;\n"
        "DROP TABLE IF EXISTS pending_alert;\n"
        "DROP TABLE IF EXISTS alert_bounce;\n"
        /* Legacy */
        "DROP TABLE IF EXISTS alert_pending;\n"
        "DROP TABLE IF EXISTS subscription;\n"
1362
1363
1364
1365
1366
1367
1368

1369
1370
1371
1372
1373
1374
1375
      return;
    }
  }
  if( !g.perm.Admin && !db_get_boolean("anon-subscribe",1) ){
    register_page();
    return;
  }

  alert_submenu_common();
  needCaptcha = !login_is_individual();
  if( P("submit")
   && cgi_csrf_safe(1)
   && subscribe_error_check(&eErr,&zErr,needCaptcha)
  ){
    /* A validated request for a new subscription has been received. */







>







1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
      return;
    }
  }
  if( !g.perm.Admin && !db_get_boolean("anon-subscribe",1) ){
    register_page();
    return;
  }
  style_set_current_feature("alerts");
  alert_submenu_common();
  needCaptcha = !login_is_individual();
  if( P("submit")
   && cgi_csrf_safe(1)
   && subscribe_error_check(&eErr,&zErr,needCaptcha)
  ){
    /* A validated request for a new subscription has been received. */
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
        @ </pre></blockquote>
      }else{
        @ <p>An email has been sent to "%h(zEAddr)". That email contains a
        @ hyperlink that you must click to activate your
        @ subscription.</p>
      }
      alert_sender_free(pSender);
      style_finish_page("alerts");
    }
    return;
  }
  style_header("Signup For Email Alerts");
  if( P("submit")==0 ){
    /* If this is the first visit to this page (if this HTTP request did not
    ** come from a prior Submit of the form) then default all of the







|







1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
        @ </pre></blockquote>
      }else{
        @ <p>An email has been sent to "%h(zEAddr)". That email contains a
        @ hyperlink that you must click to activate your
        @ subscription.</p>
      }
      alert_sender_free(pSender);
      style_finish_page();
    }
    return;
  }
  style_header("Signup For Email Alerts");
  if( P("submit")==0 ){
    /* If this is the first visit to this page (if this HTTP request did not
    ** come from a prior Submit of the form) then default all of the
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
    @ %h(zCaptcha)
    @ </pre>
    @ Enter the 8 characters above in the "Security Code" box<br/>
    @ </td></tr></table></div>
  }
  @ </form>
  fossil_free(zErr);
  style_finish_page("alerts");
}

/*
** Either shutdown or completely delete a subscription entry given
** by the hex value zName.  Then paint a webpage that explains that
** the entry has been removed.
*/
static void alert_unsubscribe(int sid){
  const char *zEmail = 0;
  const char *zLogin = 0;
  int uid = 0;
  Stmt q;
  db_prepare(&q, "SELECT semail, suname FROM subscriber"
                 " WHERE subscriberId=%d", sid);
  if( db_step(&q)==SQLITE_ROW ){
    zEmail = db_column_text(&q, 0);
    zLogin = db_column_text(&q, 1);
    uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin);
  }

  if( zEmail==0 ){
    style_header("Unsubscribe Fail");
    @ <p>Unable to locate a subscriber with the requested key</p>
  }else{
    
    db_multi_exec(
      "DELETE FROM subscriber WHERE subscriberId=%d", sid
    );
    style_header("Unsubscribed");
    @ <p>The "%h(zEmail)" email address has been unsubscribed and the
    @ corresponding row in the subscriber table has been deleted.<p>
    if( uid && g.perm.Admin ){
       @ <p>You may also want to
       @ <a href="%R/setup_uedit?id=%d(uid)">edit or delete
       @ the corresponding user "%h(zLogin)"</a></p>
    }
  }
  db_finalize(&q);
  style_finish_page("alerts");
  return;
}

/*
** WEBPAGE: alerts
**
** Edit email alert and notification settings.







|



















>


















|







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
    @ %h(zCaptcha)
    @ </pre>
    @ Enter the 8 characters above in the "Security Code" box<br/>
    @ </td></tr></table></div>
  }
  @ </form>
  fossil_free(zErr);
  style_finish_page();
}

/*
** Either shutdown or completely delete a subscription entry given
** by the hex value zName.  Then paint a webpage that explains that
** the entry has been removed.
*/
static void alert_unsubscribe(int sid){
  const char *zEmail = 0;
  const char *zLogin = 0;
  int uid = 0;
  Stmt q;
  db_prepare(&q, "SELECT semail, suname FROM subscriber"
                 " WHERE subscriberId=%d", sid);
  if( db_step(&q)==SQLITE_ROW ){
    zEmail = db_column_text(&q, 0);
    zLogin = db_column_text(&q, 1);
    uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zLogin);
  }
  style_set_current_feature("alerts");
  if( zEmail==0 ){
    style_header("Unsubscribe Fail");
    @ <p>Unable to locate a subscriber with the requested key</p>
  }else{
    
    db_multi_exec(
      "DELETE FROM subscriber WHERE subscriberId=%d", sid
    );
    style_header("Unsubscribed");
    @ <p>The "%h(zEmail)" email address has been unsubscribed and the
    @ corresponding row in the subscriber table has been deleted.<p>
    if( uid && g.perm.Admin ){
       @ <p>You may also want to
       @ <a href="%R/setup_uedit?id=%d(uid)">edit or delete
       @ the corresponding user "%h(zLogin)"</a></p>
    }
  }
  db_finalize(&q);
  style_finish_page();
  return;
}

/*
** WEBPAGE: alerts
**
** Edit email alert and notification settings.
1735
1736
1737
1738
1739
1740
1741

1742
1743
1744
1745
1746
1747
1748
                     " unsubscribe");
    }else{
      alert_unsubscribe(sid);
      db_commit_transaction();
      return; 
    }
  }

  style_header("Update Subscription");
  db_prepare(&q,
    "SELECT"
    "  semail,"                       /* 0 */
    "  sverified,"                    /* 1 */
    "  sdonotcall,"                   /* 2 */
    "  sdigest,"                      /* 3 */







>







1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
                     " unsubscribe");
    }else{
      alert_unsubscribe(sid);
      db_commit_transaction();
      return; 
    }
  }
  style_set_current_feature("alerts");
  style_header("Update Subscription");
  db_prepare(&q,
    "SELECT"
    "  semail,"                       /* 0 */
    "  sverified,"                    /* 1 */
    "  sdonotcall,"                   /* 2 */
    "  sdigest,"                      /* 3 */
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
  @  <td><input type="submit" name="submit" value="Submit">
  @  <input type="submit" name="delete" value="Unsubscribe">
  @ </tr>
  @ </table>
  @ </form>
  fossil_free(zErr);
  db_finalize(&q);
  style_finish_page("alerts");
  db_commit_transaction();
  return;
}

/* This is the message that gets sent to describe how to change
** or modify a subscription
*/







|







1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
  @  <td><input type="submit" name="submit" value="Submit">
  @  <input type="submit" name="delete" value="Unsubscribe">
  @ </tr>
  @ </table>
  @ </form>
  fossil_free(zErr);
  db_finalize(&q);
  style_finish_page();
  db_commit_transaction();
  return;
}

/* This is the message that gets sent to describe how to change
** or modify a subscription
*/
1975
1976
1977
1978
1979
1980
1981


1982
1983
1984
1985
1986
1987
1988
  /* Logged in users are redirected to the /alerts page */
  login_check_credentials();
  if( login_is_individual() ){
    cgi_redirectf("%R/alerts");
    return;
  }



  zEAddr = PD("e","");
  dx = atoi(PD("dx","0"));
  bSubmit = P("submit")!=0 && P("e")!=0 && cgi_csrf_safe(1);
  if( bSubmit ){
    if( !captcha_is_correct(1) ){
      eErr = 2;
      zErr = mprintf("enter the security code shown below");







>
>







1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
  /* Logged in users are redirected to the /alerts page */
  login_check_credentials();
  if( login_is_individual() ){
    cgi_redirectf("%R/alerts");
    return;
  }

  style_set_current_feature("alerts");

  zEAddr = PD("e","");
  dx = atoi(PD("dx","0"));
  bSubmit = P("submit")!=0 && P("e")!=0 && cgi_csrf_safe(1);
  if( bSubmit ){
    if( !captcha_is_correct(1) ){
      eErr = 2;
      zErr = mprintf("enter the security code shown below");
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
      @ %h(pSender->zErr)
      @ </pre></blockquote>
    }else{
      @ <p>An email has been sent to "%h(zEAddr)" that explains how to
      @ unsubscribe and/or modify your subscription settings</p>
    }
    alert_sender_free(pSender);
    style_finish_page("alerts");
    return;
  }  

  /* Non-logged-in users have to enter an email address to which is
  ** sent a message containing the unsubscribe link.
  */
  style_header("Unsubscribe Request");







|







2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
      @ %h(pSender->zErr)
      @ </pre></blockquote>
    }else{
      @ <p>An email has been sent to "%h(zEAddr)" that explains how to
      @ unsubscribe and/or modify your subscription settings</p>
    }
    alert_sender_free(pSender);
    style_finish_page();
    return;
  }  

  /* Non-logged-in users have to enter an email address to which is
  ** sent a message containing the unsubscribe link.
  */
  style_header("Unsubscribe Request");
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
  @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha">
  @ %h(zCaptcha)
  @ </pre>
  @ Enter the 8 characters above in the "Security Code" box<br/>
  @ </td></tr></table></div>
  @ </form>
  fossil_free(zErr);
  style_finish_page("alerts");
}

/*
** WEBPAGE: subscribers
**
** This page, accessible to administrators only,
** shows a list of subscriber email addresses.







|







2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
  @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha">
  @ %h(zCaptcha)
  @ </pre>
  @ Enter the 8 characters above in the "Security Code" box<br/>
  @ </td></tr></table></div>
  @ </form>
  fossil_free(zErr);
  style_finish_page();
}

/*
** WEBPAGE: subscribers
**
** This page, accessible to administrators only,
** shows a list of subscriber email addresses.
2096
2097
2098
2099
2100
2101
2102

2103
2104
2105
2106
2107
2108
2109
  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }
  alert_submenu_common();
  style_submenu_element("Users","setup_ulist");

  style_header("Subscriber List");
  nTotal = db_int(0, "SELECT count(*) FROM subscriber");
  nPending = db_int(0, "SELECT count(*) FROM subscriber WHERE NOT sverified");
  if( nPending>0 && P("purge") && cgi_csrf_safe(0) ){
    int nNewPending;
    db_multi_exec(
       "DELETE FROM subscriber"







>







2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }
  alert_submenu_common();
  style_submenu_element("Users","setup_ulist");
  style_set_current_feature("alerts");
  style_header("Subscriber List");
  nTotal = db_int(0, "SELECT count(*) FROM subscriber");
  nPending = db_int(0, "SELECT count(*) FROM subscriber WHERE NOT sverified");
  if( nPending>0 && P("purge") && cgi_csrf_safe(0) ){
    int nNewPending;
    db_multi_exec(
       "DELETE FROM subscriber"
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
    @ <td data-sortkey='%010llx(iMtime)'>%z(human_readable_age(rAge))</td>
    @ <td>%h(db_column_text(&q,7))</td>
    @ </tr>
  }
  @ </tbody></table>
  db_finalize(&q);
  style_table_sorter();
  style_finish_page("alerts");
}

#if LOCAL_INTERFACE
/*
** A single event that might appear in an alert is recorded as an
** instance of the following object.
**







|







2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
    @ <td data-sortkey='%010llx(iMtime)'>%z(human_readable_age(rAge))</td>
    @ <td>%h(db_column_text(&q,7))</td>
    @ </tr>
  }
  @ </tbody></table>
  db_finalize(&q);
  style_table_sorter();
  style_finish_page();
}

#if LOCAL_INTERFACE
/*
** A single event that might appear in an alert is recorded as an
** instance of the following object.
**
2797
2798
2799
2800
2801
2802
2803

2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
void contact_admin_page(void){
  const char *zAdminEmail = db_get("email-admin",0);
  unsigned int uSeed = 0;
  const char *zDecoded;
  char *zCaptcha = 0;

  login_check_credentials();

  if( zAdminEmail==0 || zAdminEmail[0]==0 ){
    style_header("Outbound Email Disabled");
    @ <p>Outbound email is disabled on this repository
    style_finish_page("alerts");
    return;
  }
  if( P("submit")!=0 
   && P("subject")!=0
   && P("msg")!=0
   && P("from")!=0
   && cgi_csrf_safe(1)







>



|







2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
void contact_admin_page(void){
  const char *zAdminEmail = db_get("email-admin",0);
  unsigned int uSeed = 0;
  const char *zDecoded;
  char *zCaptcha = 0;

  login_check_credentials();
  style_set_current_feature("alerts");
  if( zAdminEmail==0 || zAdminEmail[0]==0 ){
    style_header("Outbound Email Disabled");
    @ <p>Outbound email is disabled on this repository
    style_finish_page();
    return;
  }
  if( P("submit")!=0 
   && P("subject")!=0
   && P("msg")!=0
   && P("from")!=0
   && cgi_csrf_safe(1)
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846

2847
2848
2849
2850
2851
2852
2853
      @ %h(pSender->zErr)
      @ </pre></blockquote>
    }else{
      @ <p>Your message has been sent to the repository administrator.
      @ Thank you for your input.</p>
    }
    alert_sender_free(pSender);
    style_finish_page("alerts");
    return;
  }
  if( captcha_needed() ){
    uSeed = captcha_seed();
    zDecoded = captcha_decode(uSeed);
    zCaptcha = captcha_render(zDecoded);
  }

  style_header("Message To Administrator");
  form_begin(0, "%R/contact_admin");
  @ <p>Enter a message to the repository administrator below:</p>
  @ <table class="subscribe">
  if( zCaptcha ){
    @ <tr>
    @  <td class="form_label">Security&nbsp;Code:</td>







|







>







2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
      @ %h(pSender->zErr)
      @ </pre></blockquote>
    }else{
      @ <p>Your message has been sent to the repository administrator.
      @ Thank you for your input.</p>
    }
    alert_sender_free(pSender);
    style_finish_page();
    return;
  }
  if( captcha_needed() ){
    uSeed = captcha_seed();
    zDecoded = captcha_decode(uSeed);
    zCaptcha = captcha_render(zDecoded);
  }
  style_set_current_feature("alerts");
  style_header("Message To Administrator");
  form_begin(0, "%R/contact_admin");
  @ <p>Enter a message to the repository administrator below:</p>
  @ <table class="subscribe">
  if( zCaptcha ){
    @ <tr>
    @  <td class="form_label">Security&nbsp;Code:</td>
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
    @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha">
    @ %h(zCaptcha)
    @ </pre>
    @ Enter the 8 characters above in the "Security Code" box<br/>
    @ </td></tr></table></div>
  }
  @ </form>
  style_finish_page("alerts");
}

/*
** Send an annoucement message described by query parameter.
** Permission to do this has already been verified.
*/
static char *alert_send_announcement(void){







|







2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
    @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha">
    @ %h(zCaptcha)
    @ </pre>
    @ Enter the 8 characters above in the "Security Code" box<br/>
    @ </td></tr></table></div>
  }
  @ </form>
  style_finish_page();
}

/*
** Send an annoucement message described by query parameter.
** Permission to do this has already been verified.
*/
static char *alert_send_announcement(void){
2969
2970
2971
2972
2973
2974
2975

2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
*/
void announce_page(void){
  login_check_credentials();
  if( !g.perm.Announce ){
    login_needed(0);
    return;
  }

  if( fossil_strcmp(P("name"),"test1")==0 ){
    /* Visit the /announce/test1 page to see the CGI variables */
    @ <p style='border: 1px solid black; padding: 1ex;'>
    cgi_print_all(0, 0);
    @ </p>
  }else if( P("submit")!=0 && cgi_csrf_safe(1) ){
    char *zErr = alert_send_announcement();
    style_header("Announcement Sent");
    if( zErr ){
      @ <h1>Internal Error</h1>
      @ <p>The following error was reported by the system:
      @ <blockquote><pre>
      @ %h(zErr)
      @ </pre></blockquote>
    }else{
      @ <p>The announcement has been sent.
      @ <a href="%h(PD("REQUEST_URI","/"))">Send another</a></p>
    }
    style_finish_page("alerts");
    return;
  } else if( !alert_enabled() ){
    style_header("Cannot Send Announcement");
    @ <p>Either you have no subscribers yet, or email alerts are not yet
    @ <a href="https://fossil-scm.org/fossil/doc/trunk/www/alerts.md">set up</a>
    @ for this repository.</p>
    return;







>


















|







2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
*/
void announce_page(void){
  login_check_credentials();
  if( !g.perm.Announce ){
    login_needed(0);
    return;
  }
  style_set_current_feature("alerts");
  if( fossil_strcmp(P("name"),"test1")==0 ){
    /* Visit the /announce/test1 page to see the CGI variables */
    @ <p style='border: 1px solid black; padding: 1ex;'>
    cgi_print_all(0, 0);
    @ </p>
  }else if( P("submit")!=0 && cgi_csrf_safe(1) ){
    char *zErr = alert_send_announcement();
    style_header("Announcement Sent");
    if( zErr ){
      @ <h1>Internal Error</h1>
      @ <p>The following error was reported by the system:
      @ <blockquote><pre>
      @ %h(zErr)
      @ </pre></blockquote>
    }else{
      @ <p>The announcement has been sent.
      @ <a href="%h(PD("REQUEST_URI","/"))">Send another</a></p>
    }
    style_finish_page();
    return;
  } else if( !alert_enabled() ){
    style_header("Cannot Send Announcement");
    @ <p>Either you have no subscribers yet, or email alerts are not yet
    @ <a href="https://fossil-scm.org/fossil/doc/trunk/www/alerts.md">set up</a>
    @ for this repository.</p>
    return;
3041
3042
3043
3044
3045
3046
3047
3048
3049
    @   <td><input type="submit" name="submit" value="Dry Run">
  }else{
    @   <td><input type="submit" name="submit" value="Send Message">
  }
  @ </tr>
  @ </table>
  @ </form>
  style_finish_page("alerts");
}







|

3053
3054
3055
3056
3057
3058
3059
3060
3061
    @   <td><input type="submit" name="submit" value="Dry Run">
  }else{
    @   <td><input type="submit" name="submit" value="Send Message">
  }
  @ </tr>
  @ </table>
  @ </form>
  style_finish_page();
}

Added src/alerts/bflat2.wav.

cannot compute difference between binary files

Added src/alerts/bflat3.wav.

cannot compute difference between binary files

Added src/alerts/bloop.wav.

cannot compute difference between binary files

Added src/alerts/mkwav.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
/*
** This C program was used to generate the "g-minor-triad.wav" file.
** A small modification generated the "b-flat.wav" file.
**
** This code is saved as an historical reference.  It is not part
** of Fossil.
*/
#include <stdio.h>
#include <math.h>
#include <stdlib.h>

/*
** Write a four-byte little-endian integer value to out.
*/
void write_int4(FILE *out, unsigned int i){
  unsigned char z[4];
  z[0] = i&0xff;
  z[1] = (i>>8)&0xff;
  z[2] = (i>>16)&0xff;
  z[3] = (i>>24)&0xff;
  fwrite(z, 4, 1, out);
}

/*
** Write out the WAV file
*/
void write_wave(
  const char *zFilename,    /* The file to write */
  unsigned int nData,       /* Bytes of data */
  unsigned char *aData      /* 8000 samples/sec, 8 bit samples */
){
  const unsigned char aWavFmt[] = {
    0x57,  0x41,  0x56, 0x45,    /* "WAVE" */
    0x66,  0x6d,  0x74, 0x20,    /* "fmt " */
    0x10,  0x00,  0x00, 0x00,    /* 16 bytes in the "fmt " section */
    0x01,  0x00,                 /* FormatTag: WAVE_FORMAT_PCM */
    0x01,  0x00,                 /* 1 channel */
    0x40,  0x1f,  0x00, 0x00,    /* 8000 samples/second */
    0x40,  0x1f,  0x00, 0x00,    /* 8000 bytes/second */
    0x01,  0x00,                 /* Block alignment */
    0x08,  0x00,                 /* bits/sample */
    0x64,  0x61,  0x74, 0x61,    /* "data" */
  };
  FILE *out = fopen(zFilename,"wb");
  if( out==0 ){
    fprintf(stderr, "cannot open \"%s\" for writing\n", zFilename);
    exit(1);
  }
  fwrite("RIFF", 4, 1, out);
  write_int4(out, nData+4+20+8);
  fwrite(aWavFmt, sizeof(aWavFmt), 1, out);
  write_int4(out, nData);
  fwrite(aData, nData, 1, out);
  fclose(out);
}

int main(int argc, char **argv){
  int i = 0;
  unsigned char aBuf[800];
# define N      sizeof(aBuf)
# define pitch1 195.9977*2   /* G */
# define pitch2 233.0819*2   /* B-flat */
# define pitch3 293.6648*2   /* D */
  while( i<N/2 ){
    double v;
    v = 99.0*sin((2*M_PI*pitch3*i)/8000);
    if( i<200 ){
      v = v*i/200.0;
    }else if( i>N-200 ){
      v = v*(N-i)/200.0;
    }
    aBuf[i] = (char)(v+99.0);
    i++;
  }
  while( i<N ){
    double v;
    v = 99.0*sin((2*M_PI*pitch1*i)/8000);
    if( i<200 ){
      v = v*i/200.0;
    }else if( i>N-200 ){
      v = v*(N-i)/200.0;
    }
    aBuf[i] = (char)(v+99.0);
    i++;
  }
  write_wave("out.wav", N, aBuf);
  return 0;
}

Added src/alerts/plunk.wav.

cannot compute difference between binary files

Changes to src/attach.c.

43
44
45
46
47
48
49

50
51
52
53
54
55
56
  const char *zTkt = P("tkt");
  const char *zTechNote = P("technote");
  Blob sql;
  Stmt q;

  if( zPage && zTkt ) zTkt = 0;
  login_check_credentials();

  blob_zero(&sql);
  blob_append_sql(&sql,
     "SELECT datetime(mtime,toLocal()), src, target, filename,"
     "       comment, user,"
     "       (SELECT uuid FROM blob WHERE rid=attachid), attachid,"
     "       (CASE WHEN 'tkt-'||target IN (SELECT tagname FROM tag)"
     "                  THEN 1"







>







43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
  const char *zTkt = P("tkt");
  const char *zTechNote = P("technote");
  Blob sql;
  Stmt q;

  if( zPage && zTkt ) zTkt = 0;
  login_check_credentials();
  style_set_current_feature("attach");
  blob_zero(&sql);
  blob_append_sql(&sql,
     "SELECT datetime(mtime,toLocal()), src, target, filename,"
     "       comment, user,"
     "       (SELECT uuid FROM blob WHERE rid=attachid), attachid,"
     "       (CASE WHEN 'tkt-'||target IN (SELECT tagname FROM tag)"
     "                  THEN 1"
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
    }
    @ by %h(zDispUser) on
    hyperlink_to_date(zDate, ".");
    free(zUrlTail);
  }
  db_finalize(&q);
  @ </ol>
  style_finish_page("attach");
  return;
}

/*
** WEBPAGE: attachdownload
** WEBPAGE: attachimage
** WEBPAGE: attachview







|







144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
    }
    @ by %h(zDispUser) on
    hyperlink_to_date(zDate, ".");
    free(zUrlTail);
  }
  db_finalize(&q);
  @ </ol>
  style_finish_page();
  return;
}

/*
** WEBPAGE: attachdownload
** WEBPAGE: attachimage
** WEBPAGE: attachview
174
175
176
177
178
179
180

181
182
183
184
185
186
187
  const char *zFile = P("file");
  const char *zTarget = 0;
  int attachid = atoi(PD("attachid","0"));
  char *zUUID;

  if( zFile==0 ) fossil_redirect_home();
  login_check_credentials();

  if( zPage ){
    if( g.perm.RdWiki==0 ){ login_needed(g.anon.RdWiki); return; }
    zTarget = zPage;
  }else if( zTkt ){
    if( g.perm.RdTkt==0 ){ login_needed(g.anon.RdTkt); return; }
    zTarget = zTkt;
  }else if( zTechNote ){







>







175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
  const char *zFile = P("file");
  const char *zTarget = 0;
  int attachid = atoi(PD("attachid","0"));
  char *zUUID;

  if( zFile==0 ) fossil_redirect_home();
  login_check_credentials();
  style_set_current_feature("attach");
  if( zPage ){
    if( g.perm.RdWiki==0 ){ login_needed(g.anon.RdWiki); return; }
    zTarget = zPage;
  }else if( zTkt ){
    if( g.perm.RdTkt==0 ){ login_needed(g.anon.RdTkt); return; }
    zTarget = zTkt;
  }else if( zTechNote ){
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
       " ORDER BY mtime DESC LIMIT 1",
       zTarget, zFile
    );
  }
  if( zUUID==0 || zUUID[0]==0 ){
    style_header("No Such Attachment");
    @ No such attachment....
    style_finish_page("attach");
    return;
  }else if( zUUID[0]=='x' ){
    style_header("Missing");
    @ Attachment has been deleted
    style_finish_page("attach");
    return;
  }else{
    g.perm.Read = 1;
    cgi_replace_parameter("name",zUUID);
    if( fossil_strcmp(g.zPath,"attachview")==0 ){
      artifact_page();
    }else{







|




|







205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
       " ORDER BY mtime DESC LIMIT 1",
       zTarget, zFile
    );
  }
  if( zUUID==0 || zUUID[0]==0 ){
    style_header("No Such Attachment");
    @ No such attachment....
    style_finish_page();
    return;
  }else if( zUUID[0]=='x' ){
    style_header("Missing");
    @ Attachment has been deleted
    style_finish_page();
    return;
  }else{
    g.perm.Read = 1;
    cgi_replace_parameter("name",zUUID);
    if( fossil_strcmp(g.zPath,"attachview")==0 ){
      artifact_page();
    }else{
384
385
386
387
388
389
390

391
392
393
394
395
396
397
  if( P("ok") && szContent>0 && (goodCaptcha = captcha_is_correct(0)) ){
    int needModerator = (zTkt!=0 && ticket_need_moderation(0)) ||
                        (zPage!=0 && wiki_need_moderation(0));
    const char *zComment = PD("comment", "");
    attach_commit(zName, zTarget, aContent, szContent, needModerator, zComment);
    cgi_redirect(zFrom);
  }

  style_header("Add Attachment");
  if( !goodCaptcha ){
    @ <p class="generalError">Error: Incorrect security code.</p>
  }
  @ <h2>Add Attachment To %s(zTargetType)</h2>
  form_begin("enctype='multipart/form-data'", "%R/attachadd");
  @ <div>







>







386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
  if( P("ok") && szContent>0 && (goodCaptcha = captcha_is_correct(0)) ){
    int needModerator = (zTkt!=0 && ticket_need_moderation(0)) ||
                        (zPage!=0 && wiki_need_moderation(0));
    const char *zComment = PD("comment", "");
    attach_commit(zName, zTarget, aContent, szContent, needModerator, zComment);
    cgi_redirect(zFrom);
  }
  style_set_current_feature("attach");
  style_header("Add Attachment");
  if( !goodCaptcha ){
    @ <p class="generalError">Error: Incorrect security code.</p>
  }
  @ <h2>Add Attachment To %s(zTargetType)</h2>
  form_begin("enctype='multipart/form-data'", "%R/attachadd");
  @ <div>
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
  }
  @ <input type="hidden" name="from" value="%h(zFrom)" />
  @ <input type="submit" name="ok" value="Add Attachment" />
  @ <input type="submit" name="cancel" value="Cancel" />
  @ </div>
  captcha_generate(0);
  @ </form>
  style_finish_page("attach");
  fossil_free(zTargetType);
}

/*
** WEBPAGE: ainfo
** URL: /ainfo?name=ARTIFACTID
**







|







411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
  }
  @ <input type="hidden" name="from" value="%h(zFrom)" />
  @ <input type="submit" name="ok" value="Add Attachment" />
  @ <input type="submit" name="cancel" value="Cancel" />
  @ </div>
  captcha_generate(0);
  @ </form>
  style_finish_page();
  fossil_free(zTargetType);
}

/*
** WEBPAGE: ainfo
** URL: /ainfo?name=ARTIFACTID
**
537
538
539
540
541
542
543

544
545
546
547
548
549
550
      }
      return;
    }
    if( strcmp(zModAction,"approve")==0 ){
      moderation_approve('a', rid);
    }
  }

  style_header("Attachment Details");
  style_submenu_element("Raw", "%R/artifact/%s", zUuid);
  if(fShowContent){
    style_submenu_element("Line Numbers", "%R/ainfo/%s%s", zUuid,
                          ((zLn&&*zLn) ? "" : "?ln=0"));
  }








>







540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
      }
      return;
    }
    if( strcmp(zModAction,"approve")==0 ){
      moderation_approve('a', rid);
    }
  }
  style_set_current_feature("attach");
  style_header("Attachment Details");
  style_submenu_element("Raw", "%R/artifact/%s", zUuid);
  if(fShowContent){
    style_submenu_element("Line Numbers", "%R/ainfo/%s%s", zUuid,
                          ((zLn&&*zLn) ? "" : "?ln=0"));
  }

620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
  }else{
    int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc);
    @ <i>(file is %d(sz) bytes of binary data)</i>
  }
  @ </blockquote>
  manifest_destroy(pAttach);
  blob_reset(&attach);
  style_finish_page("attach");
}

/*
** Output HTML to show a list of attachments.
*/
void attachment_list(
  const char *zTarget,   /* Object that things are attached to */







|







624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
  }else{
    int sz = db_int(0, "SELECT size FROM blob WHERE rid=%d", ridSrc);
    @ <i>(file is %d(sz) bytes of binary data)</i>
  }
  @ </blockquote>
  manifest_destroy(pAttach);
  blob_reset(&attach);
  style_finish_page();
}

/*
** Output HTML to show a list of attachments.
*/
void attachment_list(
  const char *zTarget,   /* Object that things are attached to */

Changes to src/backlink.c.

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

  login_check_credentials();
  if( !g.perm.Read || !g.perm.RdTkt || !g.perm.RdWiki ){
    login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
    return;
  }

  style_header("Backlink Timeline (Internal Testing Use)");
  db_multi_exec(
     "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);"
     "DELETE FROM ok;"
     "INSERT OR IGNORE INTO ok"
     " SELECT blob.rid FROM backlink, blob"
     "  WHERE blob.uuid BETWEEN backlink.target AND (backlink.target||'x')"
  );
  blob_zero(&sql);
  blob_append(&sql, timeline_query_for_www(), -1);
  blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
  db_prepare(&q, "%s", blob_sql_text(&sql));
  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
                     0, 0, 0, 0, 0, 0);
  db_finalize(&q);
  style_finish_page("test");
}

/*
** WEBPAGE: test-backlinks
**
** Show a table of all backlinks.  Admin access only.
*/
void backlink_table_page(void){
  Stmt q;
  int n;
  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(g.anon.Admin);
    return;
  }

  style_header("Backlink Table (Internal Testing Use)");
  n = db_int(0, "SELECT count(*) FROM backlink");
  @ <p>%d(n) backlink table entries:</p>
  db_prepare(&q,
    "SELECT target, srctype, srcid, datetime(mtime),"
    "  CASE srctype"
    "  WHEN 2 THEN (SELECT substr(tagname,6) FROM tag"







>















|















>







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

  login_check_credentials();
  if( !g.perm.Read || !g.perm.RdTkt || !g.perm.RdWiki ){
    login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
    return;
  }
  style_set_current_feature("test");
  style_header("Backlink Timeline (Internal Testing Use)");
  db_multi_exec(
     "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);"
     "DELETE FROM ok;"
     "INSERT OR IGNORE INTO ok"
     " SELECT blob.rid FROM backlink, blob"
     "  WHERE blob.uuid BETWEEN backlink.target AND (backlink.target||'x')"
  );
  blob_zero(&sql);
  blob_append(&sql, timeline_query_for_www(), -1);
  blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
  db_prepare(&q, "%s", blob_sql_text(&sql));
  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
                     0, 0, 0, 0, 0, 0);
  db_finalize(&q);
  style_finish_page();
}

/*
** WEBPAGE: test-backlinks
**
** Show a table of all backlinks.  Admin access only.
*/
void backlink_table_page(void){
  Stmt q;
  int n;
  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(g.anon.Admin);
    return;
  }
  style_set_current_feature("test");
  style_header("Backlink Table (Internal Testing Use)");
  n = db_int(0, "SELECT count(*) FROM backlink");
  @ <p>%d(n) backlink table entries:</p>
  db_prepare(&q,
    "SELECT target, srctype, srcid, datetime(mtime),"
    "  CASE srctype"
    "  WHEN 2 THEN (SELECT substr(tagname,6) FROM tag"
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
      }
    }
    @ <td>%h(zMtime)</tr>
  }
  @ </tbody>
  @ </table>
  db_finalize(&q);
  style_finish_page("test");
}

/*
** Remove all prior backlinks for the wiki page given.  Then
** add new backlinks for the latest version of the wiki page.
*/
void backlink_wiki_refresh(const char *zWikiTitle){







|







152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
      }
    }
    @ <td>%h(zMtime)</tr>
  }
  @ </tbody>
  @ </table>
  db_finalize(&q);
  style_finish_page();
}

/*
** Remove all prior backlinks for the wiki page given.  Then
** add new backlinks for the latest version of the wiki page.
*/
void backlink_wiki_refresh(const char *zWikiTitle){

Changes to src/backoffice.c.

661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
**
** This might be done by a cron job or similar to make sure backoffice
** processing happens periodically.  Or, the --poll option can be used
** to run this command as a daemon that will periodically invoke backoffice
** on a collection of repositories.
**
** If only a single repository is named and --poll is omitted, then the
** backoffice work is done in-process.  But if there are multiple respositories
** or if --poll is used, a separate sub-process is started for each poll of 
** each repository.
**
** Standard options:
**
**    --debug                 Show what this command is doing.
**







|







661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
**
** This might be done by a cron job or similar to make sure backoffice
** processing happens periodically.  Or, the --poll option can be used
** to run this command as a daemon that will periodically invoke backoffice
** on a collection of repositories.
**
** If only a single repository is named and --poll is omitted, then the
** backoffice work is done in-process.  But if there are multiple repositories
** or if --poll is used, a separate sub-process is started for each poll of 
** each repository.
**
** Standard options:
**
**    --debug                 Show what this command is doing.
**

Changes to src/blob.c.

363
364
365
366
367
368
369











370
371
372
373
374
375
376
  if( p->nUsed<p->nAlloc ){
    p->aData[p->nUsed] = 0;
  }else{
    blob_materialize(p);
  }
  return p->aData;
}












/*
** Return a pointer to a null-terminated string for a blob that has
** been created using blob_append_sql() and not blob_appendf().  If
** text was ever added using blob_appendf() then throw an error.
*/
char *blob_sql_text(Blob *p){







>
>
>
>
>
>
>
>
>
>
>







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
  if( p->nUsed<p->nAlloc ){
    p->aData[p->nUsed] = 0;
  }else{
    blob_materialize(p);
  }
  return p->aData;
}

/*
** Compute the string length of a Blob.  If there are embedded
** nul characters, truncate the to blob at the first nul.
*/
int blob_strlen(Blob *p){
  char *z = blob_str(p);
  if( z==0 ) return 0;
  p->nUsed = (int)strlen(p->aData);
  return p->nUsed;
}

/*
** Return a pointer to a null-terminated string for a blob that has
** been created using blob_append_sql() and not blob_appendf().  If
** text was ever added using blob_appendf() then throw an error.
*/
char *blob_sql_text(Blob *p){
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
}

/*
** COMMAND: test-escaped-arg
**
** Usage %fossil ARG ...
**
** Run each argment through blob_append_escaped_arg() and show the
** result.  Append each argument to "fossil test-echo" and run that
** using fossil_system() to verify that it really does get escaped
** correctly.
*/
void test_escaped_arg__cmd(void){
  int i;
  Blob x;







|







1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
}

/*
** COMMAND: test-escaped-arg
**
** Usage %fossil ARG ...
**
** Run each argument through blob_append_escaped_arg() and show the
** result.  Append each argument to "fossil test-echo" and run that
** using fossil_system() to verify that it really does get escaped
** correctly.
*/
void test_escaped_arg__cmd(void){
  int i;
  Blob x;

Changes to src/branch.c.

456
457
458
459
460
461
462

463
464
465
466
467
468
469
*/
static void new_brlist_page(void){
  Stmt q;
  double rNow;
  int show_colors = PB("colors");
  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }

  style_header("Branches");
  style_adunit_config(ADUNIT_RIGHT_OK);
  style_submenu_checkbox("colors", "Use Branch Colors", 0, 0);
  login_anonymous_available();

  brlist_create_temp_table();
  db_prepare(&q, "SELECT * FROM tmp_brlist ORDER BY mtime DESC");







>







456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
*/
static void new_brlist_page(void){
  Stmt q;
  double rNow;
  int show_colors = PB("colors");
  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  style_set_current_feature("branch");
  style_header("Branches");
  style_adunit_config(ADUNIT_RIGHT_OK);
  style_submenu_checkbox("colors", "Use Branch Colors", 0, 0);
  login_anonymous_available();

  brlist_create_temp_table();
  db_prepare(&q, "SELECT * FROM tmp_brlist ORDER BY mtime DESC");
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
      @ <td></td>
    }
    @ </tr>
  }
  @ </tbody></table></div>
  db_finalize(&q);
  style_table_sorter();
  style_finish_page("branch");
}

/*
** WEBPAGE: brlist
** Show a list of branches.  With no query parameters, a sortable table
** is used to show all branches.  If query parameters are present a
** fixed bullet list is shown.







|







513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
      @ <td></td>
    }
    @ </tr>
  }
  @ </tbody></table></div>
  db_finalize(&q);
  style_table_sorter();
  style_finish_page();
}

/*
** WEBPAGE: brlist
** Show a list of branches.  With no query parameters, a sortable table
** is used to show all branches.  If query parameters are present a
** fixed bullet list is shown.
554
555
556
557
558
559
560

561
562
563
564
565
566
567
  if( colorTest ){
    showClosed = 0;
    showAll = 1;
  }
  if( showAll ) brFlags = BRL_BOTH;
  if( showClosed ) brFlags = BRL_CLOSED_ONLY;


  style_header("%s", showClosed ? "Closed Branches" :
                        showAll ? "All Branches" : "Open Branches");
  style_submenu_element("Timeline", "brtimeline");
  if( showClosed ){
    style_submenu_element("All", "brlist?all");
    style_submenu_element("Open", "brlist?open");
  }else if( showAll ){







>







555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
  if( colorTest ){
    showClosed = 0;
    showAll = 1;
  }
  if( showAll ) brFlags = BRL_BOTH;
  if( showClosed ) brFlags = BRL_CLOSED_ONLY;

  style_set_current_feature("branch");
  style_header("%s", showClosed ? "Closed Branches" :
                        showAll ? "All Branches" : "Open Branches");
  style_submenu_element("Timeline", "brtimeline");
  if( showClosed ){
    style_submenu_element("All", "brlist?all");
    style_submenu_element("Open", "brlist?open");
  }else if( showAll ){
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
      @ <li>%z(href("%R/timeline?r=%T",zBr))%h(zBr)</a></li>
    }
  }
  if( cnt ){
    @ </ul>
  }
  db_finalize(&q);
  style_finish_page("branch");
}

/*
** This routine is called while for each check-in that is rendered by
** the timeline of a "brlist" page.  Add some additional hyperlinks
** to the end of the line.
*/







|







622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
      @ <li>%z(href("%R/timeline?r=%T",zBr))%h(zBr)</a></li>
    }
  }
  if( cnt ){
    @ </ul>
  }
  db_finalize(&q);
  style_finish_page();
}

/*
** This routine is called while for each check-in that is rendered by
** the timeline of a "brlist" page.  Add some additional hyperlinks
** to the end of the line.
*/
669
670
671
672
673
674
675

676
677
678
679
680
681
682
  int tmFlags;                            /* Timeline display flags */
  int fNoHidden = PB("nohidden")!=0;      /* The "nohidden" query parameter */
  int fOnlyHidden = PB("onlyhidden")!=0;  /* The "onlyhidden" query parameter */

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }


  style_header("Branches");
  style_submenu_element("List", "brlist");
  login_anonymous_available();
  timeline_ss_submenu();
  cookie_render();
  @ <h2>The initial check-in for each branch:</h2>
  blob_append(&sql, timeline_query_for_www(), -1);







>







671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
  int tmFlags;                            /* Timeline display flags */
  int fNoHidden = PB("nohidden")!=0;      /* The "nohidden" query parameter */
  int fOnlyHidden = PB("onlyhidden")!=0;  /* The "onlyhidden" query parameter */

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }

  style_set_current_feature("branch");
  style_header("Branches");
  style_submenu_element("List", "brlist");
  login_anonymous_available();
  timeline_ss_submenu();
  cookie_render();
  @ <h2>The initial check-in for each branch:</h2>
  blob_append(&sql, timeline_query_for_www(), -1);
696
697
698
699
700
701
702
703
704
  ** many descenders to (off-screen) parents. */
  tmFlags = TIMELINE_DISJOINT | TIMELINE_NOSCROLL;
  if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH;
  if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR;
  if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR;
  www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, brtimeline_extra);
  db_finalize(&q);
  style_finish_page("branch");
}







|

699
700
701
702
703
704
705
706
707
  ** many descenders to (off-screen) parents. */
  tmFlags = TIMELINE_DISJOINT | TIMELINE_NOSCROLL;
  if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH;
  if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR;
  if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR;
  www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, brtimeline_extra);
  db_finalize(&q);
  style_finish_page();
}

Changes to src/browse.c.

334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
  manifest_destroy(pM);
  @ </ul></div>

  /* If the "noreadme" query parameter is present, do not try to
  ** show the content of the README file.
  */
  if( P("noreadme")!=0 ){
    style_finish_page("dir");
    return;
  }

  /* If the directory contains a readme file, then display its content below
  ** the list of files
  */
  db_prepare(&q,







|







334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
  manifest_destroy(pM);
  @ </ul></div>

  /* If the "noreadme" query parameter is present, do not try to
  ** show the content of the README file.
  */
  if( P("noreadme")!=0 ){
    style_finish_page();
    return;
  }

  /* If the directory contains a readme file, then display its content below
  ** the list of files
  */
  db_prepare(&q,
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
        safe_html_context(DOCSRC_FILE);
        wiki_render_by_mimetype(&content, zMime);
        document_emit_js();
      }
    }
  }
  db_finalize(&q);
  style_finish_page("dir");
}

/*
** Objects used by the "tree" webpage.
*/
typedef struct FileTreeNode FileTreeNode;
typedef struct FileTree FileTree;







|







395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
        safe_html_context(DOCSRC_FILE);
        wiki_render_by_mimetype(&content, zMime);
        document_emit_js();
      }
    }
  }
  db_finalize(&q);
  style_finish_page();
}

/*
** Objects used by the "tree" webpage.
*/
typedef struct FileTreeNode FileTreeNode;
typedef struct FileTree FileTree;
784
785
786
787
788
789
790
791
792
793

794
795
796
797
798
799
800
801
802
803
      tree_add_node(&sTree, zFile, zUuid, mtime);
      nFile++;
    }
    db_finalize(&q);
  }else{
    Stmt q;
    db_prepare(&q,
      "SELECT filename.name, blob.uuid, max(event.mtime)\n"
      "  FROM filename, mlink, blob, event\n"
      " WHERE mlink.fnid=filename.fnid\n"

      "   AND event.objid=mlink.mid\n"
      "   AND blob.rid=mlink.fid\n"
      " GROUP BY 1 ORDER BY 1 COLLATE nocase");
    while( db_step(&q)==SQLITE_ROW ){
      const char *zName = db_column_text(&q, 0);
      const char *zUuid = db_column_text(&q,1);
      double mtime = db_column_double(&q,2);
      if( nD>0 && (fossil_strncmp(zName, zD, nD-1)!=0 || zName[nD-1]!='/') ){
        continue;
      }







|
|
|
>
|
|
|







784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
      tree_add_node(&sTree, zFile, zUuid, mtime);
      nFile++;
    }
    db_finalize(&q);
  }else{
    Stmt q;
    db_prepare(&q,
      "SELECT\n"
      "    (SELECT name FROM filename WHERE filename.fnid=mlink.fnid),\n"
      "    (SELECT uuid FROM blob WHERE blob.rid=mlink.fid),\n"
      "    max(event.mtime)\n"
      "  FROM mlink JOIN event ON event.objid=mlink.mid\n"
      " GROUP BY mlink.fnid\n"
      " ORDER BY 1 COLLATE nocase;");
    while( db_step(&q)==SQLITE_ROW ){
      const char *zName = db_column_text(&q, 0);
      const char *zUuid = db_column_text(&q,1);
      double mtime = db_column_double(&q,2);
      if( nD>0 && (fossil_strncmp(zName, zD, nD-1)!=0 || zName[nD-1]!='/') ){
        continue;
      }
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
        @ </ul>
      }
    }
  }
  @ </ul>
  @ </ul></div>
  builtin_request_js("tree.js");
  style_finish_page("tree");

  /* We could free memory used by sTree here if we needed to.  But
  ** the process is about to exit, so doing so would not really accomplish
  ** anything useful. */
}

/*







|







912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
        @ </ul>
      }
    }
  }
  @ </ul>
  @ </ul></div>
  builtin_request_js("tree.js");
  style_finish_page();

  /* We could free memory used by sTree here if we needed to.  But
  ** the process is about to exit, so doing so would not really accomplish
  ** anything useful. */
}

/*
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
@   pathname TEXT
@ );
@ CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;
;

static const char zComputeFileAgeRun[] =
@ WITH RECURSIVE
@   ckin(x,m) AS (SELECT objid, mtime FROM event WHERE objid=:ckin
@                 UNION
@                 SELECT plink.pid, event.mtime
@                   FROM ckin, plink, event
@                  WHERE plink.cid=ckin.x AND event.objid=plink.pid
@                  ORDER BY 2 DESC)
@ INSERT OR IGNORE INTO fileage(fnid, fid, mid, mtime, pathname)
@   SELECT filename.fnid, mlink.fid, mlink.mid, event.mtime, filename.name
@     FROM foci, filename, blob, mlink, event
@    WHERE foci.checkinID=:ckin
@      AND foci.filename GLOB :glob
@      AND filename.name=foci.filename
@      AND blob.uuid=foci.uuid







|
|
|
|
|
<







955
956
957
958
959
960
961
962
963
964
965
966

967
968
969
970
971
972
973
@   pathname TEXT
@ );
@ CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin;
;

static const char zComputeFileAgeRun[] =
@ WITH RECURSIVE
@  ckin(x) AS (VALUES(:ckin)
@              UNION
@              SELECT plink.pid
@                FROM ckin, plink
@               WHERE plink.cid=ckin.x)

@ INSERT OR IGNORE INTO fileage(fnid, fid, mid, mtime, pathname)
@   SELECT filename.fnid, mlink.fid, mlink.mid, event.mtime, filename.name
@     FROM foci, filename, blob, mlink, event
@    WHERE foci.checkinID=:ckin
@      AND foci.filename GLOB :glob
@      AND filename.name=foci.filename
@      AND blob.uuid=foci.uuid
1165
1166
1167
1168
1169
1170
1171
1172
1173
    @ </td></tr>
    @
    fossil_free(zAge);
  }
  @ </table></div>
  db_finalize(&q1);
  db_finalize(&q2);
  style_finish_page("fileage");
}







|

1165
1166
1167
1168
1169
1170
1171
1172
1173
    @ </td></tr>
    @
    fossil_free(zAge);
  }
  @ </table></div>
  db_finalize(&q1);
  db_finalize(&q2);
  style_finish_page();
}

Changes to src/builtin.c.

92
93
94
95
96
97
98

99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/*
** WEBPAGE: test-builtin-files
**
** Show all built-in text files.
*/
void test_builtin_list_page(void){
  int i;

  style_header("Built-in Text Files");
  @ <ol>
  for(i=0; i<count(aBuiltinFiles); i++){
    const char *z = aBuiltinFiles[i].zName;
    char *zUrl = href("%R/builtin?name=%T&id=%.8s&mimetype=text/plain",
           z,fossil_exe_id());
    @ <li>%z(zUrl)%h(z)</a>
  }
  @ </ol>
  style_finish_page("test");
}

/*
** COMMAND: test-builtin-get
**
** Usage: %fossil test-builtin-get NAME ?OUTPUT-FILE?
*/







>









|







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
/*
** WEBPAGE: test-builtin-files
**
** Show all built-in text files.
*/
void test_builtin_list_page(void){
  int i;
  style_set_current_feature("test");
  style_header("Built-in Text Files");
  @ <ol>
  for(i=0; i<count(aBuiltinFiles); i++){
    const char *z = aBuiltinFiles[i].zName;
    char *zUrl = href("%R/builtin?name=%T&id=%.8s&mimetype=text/plain",
           z,fossil_exe_id());
    @ <li>%z(zUrl)%h(z)</a>
  }
  @ </ol>
  style_finish_page();
}

/*
** COMMAND: test-builtin-get
**
** Usage: %fossil test-builtin-get NAME ?OUTPUT-FILE?
*/
176
177
178
179
180
181
182
183

184
185
186
187
188
189
190
191
192
193
194
195
**
** If the id= query parameter is present, then Fossil assumes that the
** result is immutable and sets a very large cache retention time (1 year).
*/
void builtin_webpage(void){
  Blob out;
  const char *zName = P("name");
  const char *zTxt = 0;

  const char *zId = P("id");
  const char *zType = P("mimetype");
  int nId;
  if( zName ) zTxt = builtin_text(zName);
  if( zTxt==0 ){
    const char *zM = P("m");
    if( zM ){
      if( zId && (nId = (int)strlen(zId))>=8
       && strncmp(zId,fossil_exe_id(),nId)==0
      ){
        g.isConst = 1;
      }







|
>



|
|







177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
**
** If the id= query parameter is present, then Fossil assumes that the
** result is immutable and sets a very large cache retention time (1 year).
*/
void builtin_webpage(void){
  Blob out;
  const char *zName = P("name");
  const char *zContent = 0;
  int nContent = 0;
  const char *zId = P("id");
  const char *zType = P("mimetype");
  int nId;
  if( zName ) zContent = (const char *)builtin_file(zName, &nContent);
  if( zContent==0 ){
    const char *zM = P("m");
    if( zM ){
      if( zId && (nId = (int)strlen(zId))>=8
       && strncmp(zId,fossil_exe_id(),nId)==0
      ){
        g.isConst = 1;
      }
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
  if( zId
   && (nId = (int)strlen(zId))>=8
   && strncmp(zId,fossil_exe_id(),nId)==0
  ){
    g.isConst = 1;
  }
  etag_check(0,0);
  blob_init(&out, zTxt, -1);
  cgi_set_content(&out);
}

/* Variables controlling the JS cache.
*/
static struct {
  int aReq[30];        /* Indexes of all requested built-in JS files */







|







214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
  if( zId
   && (nId = (int)strlen(zId))>=8
   && strncmp(zId,fossil_exe_id(),nId)==0
  ){
    g.isConst = 1;
  }
  etag_check(0,0);
  blob_init(&out, zContent, nContent);
  cgi_set_content(&out);
}

/* Variables controlling the JS cache.
*/
static struct {
  int aReq[30];        /* Indexes of all requested built-in JS files */
633
634
635
636
637
638
639




640
641
642
643
644
645
646
    /* can leak a local filesystem path:
       CX("name: %!j,", skin_in_use());*/
    CX("isDark: %s"
       "/*true if the current skin has the 'white-foreground' detail*/",
       skin_detail_boolean("white-foreground") ? "true" : "false");
    CX("}\n"/*fossil.config.skin*/);
    CX("};\n"/* fossil.config */);




    CX("if(fossil.config.skin.isDark) "
       "document.body.classList.add('fossil-dark-style');\n");
#if 0
    /* Is it safe to emit the CSRF token here? Some pages add it
    ** as a hidden form field. */
    if(g.zCsrfToken[0]!=0){
      CX("window.fossil.csrfToken = %!j;\n",







>
>
>
>







635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
    /* can leak a local filesystem path:
       CX("name: %!j,", skin_in_use());*/
    CX("isDark: %s"
       "/*true if the current skin has the 'white-foreground' detail*/",
       skin_detail_boolean("white-foreground") ? "true" : "false");
    CX("}\n"/*fossil.config.skin*/);
    CX("};\n"/* fossil.config */);
    CX("window.fossil.user = {");
    CX("name: %!j,", (g.zLogin&&*g.zLogin) ? g.zLogin : "guest");
    CX("isAdmin: %s", (g.perm.Admin || g.perm.Setup) ? "true" : "false");
    CX("};\n"/*fossil.user*/);
    CX("if(fossil.config.skin.isDark) "
       "document.body.classList.add('fossil-dark-style');\n");
#if 0
    /* Is it safe to emit the CSRF token here? Some pages add it
    ** as a hidden form field. */
    if(g.zCsrfToken[0]!=0){
      CX("window.fossil.csrfToken = %!j;\n",

Changes to src/cache.c.

351
352
353
354
355
356
357

358
359
360
361
362
363
364
void cache_page(void){
  sqlite3 *db;
  sqlite3_stmt *pStmt;
  char zBuf[100];

  login_check_credentials();
  if( !g.perm.Setup ){ login_needed(0); return; }

  style_header("Web Cache Status");
  db = cacheOpen(0);
  if( db==0 ){
    @ The web-page cache is disabled for this repository
  }else{
    char *zDbName = cacheName();
    cache_register_sizename(db);







>







351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
void cache_page(void){
  sqlite3 *db;
  sqlite3_stmt *pStmt;
  char zBuf[100];

  login_check_credentials();
  if( !g.perm.Setup ){ login_needed(0); return; }
  style_set_current_feature("cache");
  style_header("Web Cache Status");
  db = cacheOpen(0);
  if( db==0 ){
    @ The web-page cache is disabled for this repository
  }else{
    char *zDbName = cacheName();
    cache_register_sizename(db);
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
    zDbName = cacheName();
    bigSizeName(sizeof(zBuf), zBuf, file_size(zDbName, ExtFILE));
    @ <p>cache-file name: %h(zDbName)</p>
    @ <p>cache-file size: %s(zBuf)</p>
    fossil_free(zDbName);
    sqlite3_close(db);
  }
  style_finish_page("cache");
}

/*
** WEBPAGE: cacheget
**
** Usage:  /cacheget?key=KEY
**
** Download a single entry for the cache, identified by KEY.
** This page is normally a hyperlink from the /cachestat page.
** Requires Admin privilege.
*/
void cache_getpage(void){
  const char *zKey;
  Blob content;

  login_check_credentials();
  if( !g.perm.Setup ){ login_needed(0); return; }
  zKey = PD("key","");
  blob_zero(&content);
  if( cache_read(&content, zKey)==0 ){

    style_header("Cache Download Error");
    @ The cache does not contain any entry with this key: "%h(zKey)"
    style_finish_page("cache");
    return;
  }
  cgi_set_content(&content);
  cgi_set_content_type("application/x-compressed");
}







|




















>


|





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
    zDbName = cacheName();
    bigSizeName(sizeof(zBuf), zBuf, file_size(zDbName, ExtFILE));
    @ <p>cache-file name: %h(zDbName)</p>
    @ <p>cache-file size: %s(zBuf)</p>
    fossil_free(zDbName);
    sqlite3_close(db);
  }
  style_finish_page();
}

/*
** WEBPAGE: cacheget
**
** Usage:  /cacheget?key=KEY
**
** Download a single entry for the cache, identified by KEY.
** This page is normally a hyperlink from the /cachestat page.
** Requires Admin privilege.
*/
void cache_getpage(void){
  const char *zKey;
  Blob content;

  login_check_credentials();
  if( !g.perm.Setup ){ login_needed(0); return; }
  zKey = PD("key","");
  blob_zero(&content);
  if( cache_read(&content, zKey)==0 ){
    style_set_current_feature("cache");
    style_header("Cache Download Error");
    @ The cache does not contain any entry with this key: "%h(zKey)"
    style_finish_page();
    return;
  }
  cgi_set_content(&content);
  cgi_set_content_type("application/x-compressed");
}

Changes to src/capabilities.c.

302
303
304
305
306
307
308


309
310
311
312
313
314
315
    "Forum-Mod", "Moderator for forum messages" },
  { '6', CAPCLASS_FORUM|CAPCLASS_SUPER, 0,
    "Forum-Admin", "Grant capability '4' to other users" },
  { '7', CAPCLASS_ALERT, 0,
    "Alerts", "Sign up for email alerts" },
  { 'A', CAPCLASS_ALERT|CAPCLASS_SUPER, 0,
    "Announce", "Send announcements to all subscribers" },


  { 'D', CAPCLASS_OTHER, 0,
    "Debug", "Enable debugging features" },
};

/*
** Populate the aCap[].nUser values based on the current content
** of the USER table.







>
>







302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
    "Forum-Mod", "Moderator for forum messages" },
  { '6', CAPCLASS_FORUM|CAPCLASS_SUPER, 0,
    "Forum-Admin", "Grant capability '4' to other users" },
  { '7', CAPCLASS_ALERT, 0,
    "Alerts", "Sign up for email alerts" },
  { 'A', CAPCLASS_ALERT|CAPCLASS_SUPER, 0,
    "Announce", "Send announcements to all subscribers" },
  { 'C', CAPCLASS_FORUM, 0,
    "Chat",  "Read and/or writes messages in the chatroom" },
  { 'D', CAPCLASS_OTHER, 0,
    "Debug", "Enable debugging features" },
};

/*
** Populate the aCap[].nUser values based on the current content
** of the USER table.
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
    " UNION ALL"
    " SELECT 'Adminstrator', fullcap(capunion(cap)), 300, count(*) FROM user"
    " WHERE cap GLOB '*[as]*'"
    " ORDER BY 3 ASC",
    zSelfCap, hasPubPages, zSelfCap
  );
  @ <table id='capabilitySummary' cellpadding="0" cellspacing="0" border="1">
  @ <tr><th>&nbsp;<th>Code<th>Forum<th>Tickets<th>Wiki\
  @ <th>Unversioned Content</th></tr>
  while( db_step(&q)==SQLITE_ROW ){
    const char *zId = db_column_text(&q, 0);
    const char *zCap = db_column_text(&q, 1);
    int n = db_column_int(&q, 3);
    int eType;
    static const char *const azType[] = { "off", "read", "write" };







|







391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
    " UNION ALL"
    " SELECT 'Adminstrator', fullcap(capunion(cap)), 300, count(*) FROM user"
    " WHERE cap GLOB '*[as]*'"
    " ORDER BY 3 ASC",
    zSelfCap, hasPubPages, zSelfCap
  );
  @ <table id='capabilitySummary' cellpadding="0" cellspacing="0" border="1">
  @ <tr><th>&nbsp;<th>Code<th>Forum<th>Tickets<th>Wiki<th>Chat\
  @ <th>Unversioned Content</th></tr>
  while( db_step(&q)==SQLITE_ROW ){
    const char *zId = db_column_text(&q, 0);
    const char *zCap = db_column_text(&q, 1);
    int n = db_column_int(&q, 3);
    int eType;
    static const char *const azType[] = { "off", "read", "write" };
444
445
446
447
448
449
450








451
452
453
454
455
456
457
    @ <td class="%s(azClass[eType])">%s(azType[eType])</td>

    /* Wiki */
    if( sqlite3_strglob("*[asdfklm]*",zCap)==0 ){
      eType = 2;
    }else if( sqlite3_strglob("*j*",zCap)==0 ){
      eType = 1;








    }else{
      eType = 0;
    }
    @ <td class="%s(azClass[eType])">%s(azType[eType])</td>

    /* Unversioned */
    if( sqlite3_strglob("*y*",zCap)==0 ){







>
>
>
>
>
>
>
>







446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
    @ <td class="%s(azClass[eType])">%s(azType[eType])</td>

    /* Wiki */
    if( sqlite3_strglob("*[asdfklm]*",zCap)==0 ){
      eType = 2;
    }else if( sqlite3_strglob("*j*",zCap)==0 ){
      eType = 1;
    }else{
      eType = 0;
    }
    @ <td class="%s(azClass[eType])">%s(azType[eType])</td>

    /* Chat */
    if( sqlite3_strglob("*C*",zCap)==0 ){
      eType = 2;
    }else{
      eType = 0;
    }
    @ <td class="%s(azClass[eType])">%s(azType[eType])</td>

    /* Unversioned */
    if( sqlite3_strglob("*y*",zCap)==0 ){

Changes to src/captcha.c.

584
585
586
587
588
589
590

591
592
593
594
595
596
597
598
599
600
601
602
void captcha_test(void){
  const char *zPw = P("name");
  if( zPw==0 || zPw[0]==0 ){
    u64 x;
    sqlite3_randomness(sizeof(x), &x);
    zPw = mprintf("%016llx", x);
  }

  style_header("Captcha Test");
  @ <pre>
  @ %s(captcha_render(zPw))
  @ </pre>
  style_finish_page("test");
}

/*
** Check to see if the current request is coming from an agent that might
** be a spider.  If the agent is not a spider, then return 0 without doing
** anything.  But if the user agent appears to be a spider, offer
** a captcha challenge to allow the user agent to prove that it is human







>




|







584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
void captcha_test(void){
  const char *zPw = P("name");
  if( zPw==0 || zPw[0]==0 ){
    u64 x;
    sqlite3_randomness(sizeof(x), &x);
    zPw = mprintf("%016llx", x);
  }
  style_set_current_feature("test");
  style_header("Captcha Test");
  @ <pre>
  @ %s(captcha_render(zPw))
  @ </pre>
  style_finish_page();
}

/*
** Check to see if the current request is coming from an agent that might
** be a spider.  If the agent is not a spider, then return 0 without doing
** anything.  But if the user agent appears to be a spider, offer
** a captcha challenge to allow the user agent to prove that it is human
619
620
621
622
623
624
625

626
627
628
629
630
631
632
633
634
635
636
637
638
639
  if( zCookieValue && atoi(zCookieValue)==1 ) return 0;
  if( captcha_is_correct(0) ){
    cgi_set_cookie(zCookieName, "1", login_cookie_path(), 8*3600);
    return 0;
  }

  /* This appears to be a spider.  Offer the captcha */

  style_header("Verification");
  @ <form method='POST' action='%s(g.zPath)'>
  cgi_query_parameters_to_hidden();
  @ <p>Please demonstrate that you are human, not a spider or robot</p>
  captcha_generate(1);
  @ </form>
  style_finish_page("captcha");
  return 1;
}

/*
** Generate a WAV file that reads aloud the hex digits given by
** zHex.
*/







>






|







620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
  if( zCookieValue && atoi(zCookieValue)==1 ) return 0;
  if( captcha_is_correct(0) ){
    cgi_set_cookie(zCookieName, "1", login_cookie_path(), 8*3600);
    return 0;
  }

  /* This appears to be a spider.  Offer the captcha */
  style_set_current_feature("captcha");
  style_header("Verification");
  @ <form method='POST' action='%s(g.zPath)'>
  cgi_query_parameters_to_hidden();
  @ <p>Please demonstrate that you are human, not a spider or robot</p>
  captcha_generate(1);
  @ </form>
  style_finish_page();
  return 1;
}

/*
** Generate a WAV file that reads aloud the hex digits given by
** zHex.
*/

Changes to src/cgi.c.

2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
      wchar_t *wUrl = fossil_utf8_to_unicode(zBrowser+5);
      wUrl[wcslen(wUrl)-2] = 0; /* Strip terminating " &" */
      if( (size_t)ShellExecuteW(0, L"open", wUrl, 0, 0, 1)<33 ){
        fossil_warning("cannot start browser\n");
      }
    }else
#endif
    if( system(zBrowser)<0 ){
      fossil_warning("cannot start browser: %s\n", zBrowser);
    }
  }
  while( 1 ){
#if FOSSIL_MAX_CONNECTIONS>0
    while( nchildren>=FOSSIL_MAX_CONNECTIONS ){
      if( wait(0)>=0 ) nchildren--;







|







2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146
2147
2148
2149
      wchar_t *wUrl = fossil_utf8_to_unicode(zBrowser+5);
      wUrl[wcslen(wUrl)-2] = 0; /* Strip terminating " &" */
      if( (size_t)ShellExecuteW(0, L"open", wUrl, 0, 0, 1)<33 ){
        fossil_warning("cannot start browser\n");
      }
    }else
#endif
    if( fossil_system(zBrowser)<0 ){
      fossil_warning("cannot start browser: %s\n", zBrowser);
    }
  }
  while( 1 ){
#if FOSSIL_MAX_CONNECTIONS>0
    while( nchildren>=FOSSIL_MAX_CONNECTIONS ){
      if( wait(0)>=0 ) nchildren--;

Added src/chat.c.





















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
/*
** 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 implement the Fossil chatroom.
**
** Initial design goals:
**
**    *   Keep it simple.  This chatroom is not intended as a competitor
**        or replacement for IRC, Discord, Telegram, Slack, etc.  The goal
**        is zero- or near-zero-configuration, not an abundance of features.
**
**    *   Intended as a place for insiders to have ephemeral conversations
**        about a project.  This is not a public gather place.  Think
**        "boardroom", not "corner pub".
**
**    *   One chatroom per repository.
**
**    *   Chat content lives in a single repository.  It is never synced.
**        Content expires and is deleted after a set interval (a week or so).
**
** Notification is accomplished using the "hanging GET" or "long poll" design
** in which a GET request is issued but the server does not send a reply until
** new content arrives.  Newer Web Sockets and Server Sent Event protocols are
** more elegant, but are not compatible with CGI, and would thus complicate
** configuration.  
*/
#include "config.h"
#include <assert.h>
#include "chat.h"

/*
** Outputs JS code to initialize a list of chat alert audio files for
** use by the chat front-end client. A handful of builtin files
** (from alerts/\*.wav) and all unversioned files matching
** alert-sounds/\*.{mp3,ogg,wav} are included.
*/
static void chat_emit_alert_list(void){
  /*Stmt q = empty_Stmt;*/
  unsigned int i;
  const char * azBuiltins[] = {
  "builtin/alerts/plunk.wav",
  "builtin/alerts/b-flat.wav"
  };
  CX("window.fossil.config.chat.alerts = [\n");
  for(i=0; i < sizeof(azBuiltins)/sizeof(azBuiltins[0]); ++i){
    CX("%s%!j", i ? ", " : "", azBuiltins[i]);
  }
#if 0
  /*
  ** 2021-01-05 temporarily disabled until we decide whether we're
  ** going to keep configurable audio files or not. If we do, this
  ** code needs to check whether the [unversioned] table exists before
  ** querying it.
  */
  db_prepare(&q, "SELECT 'uv/'||name FROM unversioned "
             "WHERE content IS NOT NULL "
             "AND (name LIKE 'alert-sounds/%%.wav' "
             "OR name LIKE 'alert-sounds/%%.mp3' "
             "OR name LIKE 'alert-sounds/%%.ogg')");
  while(SQLITE_ROW==db_step(&q)){
    CX(", %!j", db_column_text(&q, 0));
  }
  db_finalize(&q);
#endif
  CX("\n];\n");
}

/* Settings that can be used to control chat */
/*
** SETTING: chat-initial-history    width=10 default=50
**
** If this setting has an integer value of N, then when /chat first
** starts up it initializes the screen with the N most recent chat
** messages.  If N is zero, then all chat messages are loaded.
*/
/*
** SETTING: chat-keep-count    width=10 default=50
**
** When /chat is cleaning up older messages, it will always keep
** the most recent chat-keep-count messages, even if some of those
** messages are older than the discard threshold.  If this value
** is zero, then /chat is free to delete all historic messages once
** they are old enough.
*/
/*
** SETTING: chat-keep-days    width=10 default=7
**
** The /chat subsystem will try to discard messages that are older then
** chat-keep-days.  The value of chat-keep-days can be a floating point
** number.  So, for example, if you only want to keep chat messages for
** 12 hours, set this value to 0.5.
**
** A value of 0.0 or less means that messages are retained forever.
*/
/*
** SETTING: chat-inline-images    boolean default=on
**
** Specifies whether posted images in /chat should default to being
** displayed inline or as downloadable links. Each chat user can
** change this value for their current chat session in the UI.
*/
/*
** SETTING: chat-poll-timeout    width=10 default=420
**
** On an HTTP request to /chat-poll, if there is no new content available,
** the reply is delayed waiting for new content to arrive.  (This is the
** "long poll" strategy of event delivery to the client.)  This setting
** determines approximately how long /chat-poll will delay before giving
** up and returning an empty reply.  The default value is about 7 minutes,
** which works well for Fossil behind the althttpd web server.  Other
** server environments may choose a longer or shorter delay.
**
** For maximum efficiency, it is best to choose the longest delay that
** does not cause timeouts in intermediate proxies or web server.
*/
/*
** SETTING: chat-alert-sound     width=10
**
** This is the name of the builtin sound file to use for the alert tone.
** The value must be the name of one of a builtin WAV file.
*/
/*
** WEBPAGE: chat
**
** Start up a browser-based chat session.
**
** This is the main page that humans use to access the chatroom.  Simply
** point a web-browser at /chat and the screen fills with the latest
** chat messages, and waits for new one.
**
** Other /chat-OP pages are used by XHR requests from this page to
** send new chat message, delete older messages, or poll for changes.
*/
void chat_webpage(void){
  char *zAlert;
  login_check_credentials();
  if( !g.perm.Chat ){
    login_needed(g.anon.Chat);
    return;
  }
  zAlert = mprintf("%s/builtin/%s", g.zBaseURL,
                db_get("chat-alert-sound","alerts/plunk.wav"));
  style_set_current_feature("chat");
  style_header("Chat");
  @ <form accept-encoding="utf-8" id="chat-form" autocomplete="off">
  @ <div id='chat-input-area'>
  @   <div id='chat-input-line'>
  @     <input type="text" name="msg" id="chat-input-single" \
  @      placeholder="Type message here." autocomplete="off">
  @     <textarea rows="8" id="chat-input-multi" \
  @      placeholder="Type message here. Ctrl-Enter sends it." \
  @      class="hidden"></textarea>
  @     <input type="submit" value="Send" id="chat-message-submit">
  @     <button id="chat-scroll-top" class="hidden">&uarr;</button>
  @     <button id="chat-scroll-bottom" class="hidden">&darr;</button>
  @     <span id="chat-settings-button" class="settings-icon" \
  @       aria-label="Settings..." aria-haspopup="true" ></span>
  @   </div>
  @   <div id='chat-input-file-area'>
  @     <div class='file-selection-wrapper'>
  @       <div class='help-buttonlet'>
  @        Select a file to upload, drag/drop a file into this spot,
  @        or paste an image from the clipboard if supported by
  @        your environment.
  @       </div>
  @       <input type="file" name="file" id="chat-input-file">
  @     </div>
  @     <div id="chat-drop-details"></div>
  @   </div>
  @ </div>
  @ </form>
  @ <div id='chat-messages-wrapper'>
  /* New chat messages get inserted immediately after this element */
  @ <span id='message-inject-point'></span>
  @ </div>

  builtin_fossil_js_bundle_or("popupwidget", "storage", NULL);
  /* Always in-line the javascript for the chat page */
  @ <script nonce="%h(style_nonce())">/* chat.c:%d(__LINE__) */
  /* We need an onload handler to ensure that window.fossil is
     initialized before the chat init code runs. */
  @ window.addEventListener('load', function(){
  @ document.body.classList.add('chat')
  @ /*^^^for skins which add their own BODY tag */;
  @ window.fossil.config.chat = {
  @   fromcli: %h(PB("cli")?"true":"false"),
  @   alertSound: "%h(zAlert)",
  @   initSize: %d(db_get_int("chat-initial-history",50)),
  @   imagesInline: !!%d(db_get_boolean("chat-inline-images",1))
  @ };
  chat_emit_alert_list();
  cgi_append_content(builtin_text("chat.js"),-1);
  @ }, false);
  @ </script>

  style_finish_page();
}

/* Definition of repository tables used by chat
*/
static const char zChatSchema1[] =
@ CREATE TABLE repository.chat(
@   msgid INTEGER PRIMARY KEY AUTOINCREMENT,
@   mtime JULIANDAY,       -- Time for this entry - Julianday Zulu
@   lmtime TEXT,           -- Localtime when message originally sent
@   xfrom TEXT,            -- Login of the sender
@   xmsg  TEXT,            -- Raw, unformatted text of the message
@   fname TEXT,            -- Filename of the uploaded file, or NULL
@   fmime TEXT,            -- MIMEType of the upload file, or NULL
@   mdel INT,              -- msgid of another message to delete
@   file  BLOB             -- Text of the uploaded file, or NULL
@ );
;


/*
** Make sure the repository data tables used by chat exist.  Create them
** if they do not.
*/
static void chat_create_tables(void){
  if( !db_table_exists("repository","chat") ){
    db_multi_exec(zChatSchema1/*works-like:""*/);
  }else if( !db_table_has_column("repository","chat","lmtime") ){
    if( !db_table_has_column("repository","chat","mdel") ){
      db_multi_exec("ALTER TABLE chat ADD COLUMN mdel INT");
    }
    db_multi_exec("ALTER TABLE chat ADD COLUMN lmtime TEXT");
  }
}

/*
** Delete old content from the chat table.
*/
static void chat_purge(void){
   int mxCnt = db_get_int("chat-keep-count",50);
   double mxDays = atof(db_get("chat-keep-days","7"));
   double rAge;
   int msgid;
   rAge = db_double(0.0, "SELECT julianday('now')-mtime FROM chat"
                         " ORDER BY msgid LIMIT 1");
   if( rAge>mxDays ){
     msgid = db_int(0, "SELECT msgid FROM chat"
                       " ORDER BY msgid DESC LIMIT 1 OFFSET %d", mxCnt);
     if( msgid>0 ){
       Stmt s;
       db_multi_exec("PRAGMA secure_delete=ON;");
       db_prepare(&s, 
             "DELETE FROM chat WHERE mtime<julianday('now')-:mxage"
             " AND msgid<%d", msgid);
       db_bind_double(&s, ":mxage", mxDays);
       db_step(&s);
       db_finalize(&s);
     }
   }
}

/*
** Sets the current CGI response type to application/json then emits a
** JSON-format error message object. If fAsMessageList is true then
** the object is output using the list format described for chat-poll,
** else it is emitted as a single object in that same format.
*/
static void chat_emit_permissions_error(int fAsMessageList){
  char * zTime = cgi_iso8601_datestamp();
  cgi_set_content_type("application/json");
  if(fAsMessageList){
    CX("{\"msgs\":[{");
  }else{
    CX("{");
  }
  CX("\"isError\": true, \"xfrom\": null,");
  CX("\"mtime\": %!j, \"lmtime\": %!j,", zTime, zTime);
  CX("\"xmsg\": \"Missing permissions or not logged in. "
     "Try <a href='%R/login?g=%R/chat'>logging in</a>.\"");
  if(fAsMessageList){
    CX("}]}");
  }else{
    CX("}");
  }
  fossil_free(zTime);
}

/*
** WEBPAGE: chat-send
**
** This page receives (via XHR) a new chat-message and/or a new file
** to be entered into the chat history.
**
** On success it responds with an empty response: the new message
** should be fetched via /chat-poll. On error, e.g. login expiry,
** it emits a JSON response in the same form as described for
** /chat-poll errors, but as a standalone object instead of a
** list of objects.
*/
void chat_send_webpage(void){
  int nByte;
  const char *zMsg;
  login_check_credentials();
  if( !g.perm.Chat ) {
    chat_emit_permissions_error(0);
    return;
  }
  chat_create_tables();
  nByte = atoi(PD("file:bytes","0"));
  zMsg = PD("msg","");
  db_begin_write();
  chat_purge();
  if( nByte==0 ){
    if( zMsg[0] ){
      db_multi_exec(
        "INSERT INTO chat(mtime,lmtime,xfrom,xmsg)"
        "VALUES(julianday('now'),%Q,%Q,%Q)",
        P("lmtime"), g.zLogin, zMsg
      );
    }
  }else{
    Stmt q;
    Blob b;
    db_prepare(&q,
        "INSERT INTO chat(mtime,lmtime,xfrom,xmsg,file,fname,fmime)"
        "VALUES(julianday('now'),%Q,%Q,%Q,:file,%Q,%Q)",
        P("lmtime"), g.zLogin, zMsg, PD("file:filename",""),
        PD("file:mimetype","application/octet-stream"));
    blob_init(&b, P("file"), nByte);
    db_bind_blob(&q, ":file", &b);
    db_step(&q);
    db_finalize(&q);
    blob_reset(&b);
  }
  db_commit_transaction();
}

/*
** This routine receives raw (user-entered) message text and transforms
** it into HTML that is safe to insert using innerHTML.
**
**    *   HTML in the original text is escaped.
**
**    *   Hyperlinks are identified and tagged.  Hyperlinks are:
**
**          -  Undelimited text of the form https:... or http:...
**          -  Any text enclosed within [...]
**
** Space to hold the returned string is obtained from fossil_malloc()
** and must be freed by the caller.
*/
static char *chat_format_to_html(const char *zMsg){
  char *zSafe = mprintf("%h", zMsg);
  int i, j, k;
  Blob out;
  char zClose[20];
  blob_init(&out, 0, 0);
  for(i=j=0; zSafe[i]; i++){
    if( zSafe[i]=='[' ){
      for(k=i+1; zSafe[k] && zSafe[k]!=']'; k++){}
      if( zSafe[k]==']' ){
        zSafe[k] = 0;
        if( j<i ){
          blob_append(&out, zSafe + j, i-j);
          j = i;
        }
        wiki_resolve_hyperlink(&out, WIKI_NOBADLINKS|WIKI_TARGET_BLANK,
                               zSafe+i+1, zClose, sizeof(zClose), zSafe, 0);
        zSafe[k] = ']';
        j++;
        blob_append(&out, zSafe + j, k - j);
        blob_append(&out, zClose, -1);
        i = k;
        j = k+1;
        continue;
      }
    }else if( zSafe[i]=='h' 
           && (strncmp(zSafe+i,"http:",5)==0
               || strncmp(zSafe+i,"https:",6)==0) ){
      for(k=i+1; zSafe[k] && !fossil_isspace(zSafe[k]); k++){}
      if( k>i+7 ){
        char c = zSafe[k];
        if( !fossil_isalnum(zSafe[k-1]) && zSafe[k-1]!='/' ){
          k--;
          c = zSafe[k];
        }
        if( j<i ){
          blob_append(&out, zSafe + j, i-j);
          j = i;
        }
        zSafe[k] = 0;        
        wiki_resolve_hyperlink(&out, WIKI_NOBADLINKS|WIKI_TARGET_BLANK,
                               zSafe+i, zClose, sizeof(zClose), zSafe, 0);
        zSafe[k] = c;
        blob_append(&out, zSafe + j, k - j);
        blob_append(&out, zClose, -1);
        i = j = k;
        continue;
      }
    }
  }
  if( j<i ){
    blob_append(&out, zSafe+j, j-i);
  }
  fossil_free(zSafe);
  return blob_str(&out);
}

/*
** COMMAND: test-chat-formatter
**
** Usage: %fossil test-chat-formatter STRING ...
**
** Transform each argument string into HTML that will display the
** chat message.  This is used to test the formatter and to verify
** that a malicious message text will not cause HTML or JS injection
** into the chat display in a browser.
*/
void chat_test_formatter_cmd(void){
  int i;
  char *zOut;
  db_find_and_open_repository(0,0);
  g.perm.Hyperlink = 1;
  for(i=0; i<g.argc; i++){
    zOut = chat_format_to_html(g.argv[i]);
    fossil_print("[%d]: %s\n", i, zOut);
    fossil_free(zOut);
  }
}

/*
** WEBPAGE: chat-poll
**
** The chat page generated by /chat using an XHR to this page to
** request new chat content.  A typical invocation is:
**
**     /chat-poll/N
**     /chat-poll?name=N
**
** The "name" argument should begin with an integer which is the largest
** "msgid" that the chat page currently holds. If newer content is
** available, this routine returns that content straight away. If no new
** content is available, this webpage blocks until the new content becomes
** available.  In this way, the system implements "hanging-GET" or "long-poll"
** style event notification. If no new content arrives after a delay of
** approximately chat-poll-timeout seconds (default: 420), then reply is
** sent with an empty "msg": field.
**
** If N is negative, then the return value is the N most recent messages.
** Hence a request like /chat-poll/-100 can be used to initialize a new
** chat session to just the most recent messages.
**
** Some webservers (althttpd) do not allow a term of the URL path to
** begin with "-".  Then /chat-poll/-100 cannot be used.  Instead you
** have to say "/chat-poll?name=-100".
**
** If the integer parameter "before" is passed in, it is assumed that
** the client is requesting older messages, up to (but not including)
** that message ID, in which case the next-oldest "n" messages
** (default=chat-initial-history setting, equivalent to n=0) are
** returned (negative n fetches all older entries). The client then
** needs to take care to inject them at the end of the history rather
** than the same place new messages go.
**
** If "before" is provided, "name" is ignored.
**
** The reply from this webpage is JSON that describes the new content.
** Format of the json:
**
** |    {
** |      "msgs":[
** |        {
** |           "msgid": integer // message id
** |           "mtime": text    // When sent:  YYYY-MM-DD HH:MM:SS UTC
** |           "lmtime: text    // Localtime where the message was sent from
** |           "xfrom": text    // Login name of sender
** |           "uclr":  text    // Color string associated with the user
** |           "xmsg":  text    // HTML text of the message
** |           "fsize": integer // file attachment size in bytes
** |           "fname": text    // Name of file attachment
** |           "fmime": text    // MIME-type of file attachment
** |           "mdel":  integer // message id of prior message to delete
** |        }
** |      ]
** |    }
**
** The "fname" and "fmime" fields are only present if "fsize" is greater
** than zero.  The "xmsg" field may be an empty string if "fsize" is zero.
**
** The "msgid" values will be in increasing order.
**
** The "mdel" will only exist if "xmsg" is an empty string and "fsize" is zero.
**
** The "lmtime" value might be known, in which case it is omitted.
**
** The messages are ordered oldest first unless "before" is provided, in which
** case they are sorted newest first (to facilitate the client-side UI update).
**
** As a special case, if this routine encounters an error, e.g. the user's
** permissions cannot be verified because their login cookie expired, the
** request returns a slightly modified structure:
**
** |    {
** |      "msgs":[
** |        {
** |          "isError": true,
** |          "xfrom": null,
** |          "xmsg": "error details"
** |          "mtime": as above,
** |          "ltime": same as mtime
** |        }
** |      ]
** |    }
**
** If the client gets such a response, it should display the message
** in a prominent manner and then stop polling for new messages.
*/
void chat_poll_webpage(void){
  Blob json;                  /* The json to be constructed and returned */
  sqlite3_int64 dataVersion;  /* Data version.  Used for polling. */
  const int iDelay = 1000;    /* Delay until next poll (milliseconds) */
  int nDelay;                 /* Maximum delay.*/
  int msgid = atoi(PD("name","0"));
  const int msgBefore = atoi(PD("before","0"));
  int nLimit = msgBefore>0 ? atoi(PD("n","0")) : 0;
  Blob sql = empty_blob;
  Stmt q1;
  nDelay = db_get_int("chat-poll-timeout",420);  /* Default about 7 minutes */
  login_check_credentials();
  if( !g.perm.Chat ) {
    chat_emit_permissions_error(1);
    return;
  }
  chat_create_tables();
  cgi_set_content_type("application/json");
  dataVersion = db_int64(0, "PRAGMA data_version");
  blob_append_sql(&sql,
    "SELECT msgid, datetime(mtime), xfrom, xmsg, length(file),"
    "       fname, fmime, %s, lmtime"
    "  FROM chat ",
    msgBefore>0 ? "0 as mdel" : "mdel");
  if( msgid<=0 || msgBefore>0 ){
    db_begin_write();
    chat_purge();
    db_commit_transaction();
  }
  if(msgBefore>0){
    if(0==nLimit){
      nLimit = db_get_int("chat-initial-history",50);
    }
    blob_append_sql(&sql,
      " WHERE msgid<%d"
      " ORDER BY msgid DESC "
      "LIMIT %d",
      msgBefore, nLimit>0 ? nLimit : -1
    );
  }else{
    if( msgid<0 ){
      msgid = db_int(0,
            "SELECT msgid FROM chat WHERE mdel IS NOT true"
            " ORDER BY msgid DESC LIMIT 1 OFFSET %d", -msgid);
    }
    blob_append_sql(&sql,
      " WHERE msgid>%d"
      " ORDER BY msgid",
      msgid
    );
  }
  db_prepare(&q1, "%s", blob_sql_text(&sql));
  blob_reset(&sql);
  blob_init(&json, "{\"msgs\":[\n", -1);
  while( nDelay>0 ){
    int cnt = 0;
    while( db_step(&q1)==SQLITE_ROW ){
      int id = db_column_int(&q1, 0);
      const char *zDate = db_column_text(&q1, 1);
      const char *zFrom = db_column_text(&q1, 2);
      const char *zRawMsg = db_column_text(&q1, 3);
      int nByte = db_column_int(&q1, 4);
      const char *zFName = db_column_text(&q1, 5);
      const char *zFMime = db_column_text(&q1, 6);
      int iToDel = db_column_int(&q1, 7);
      const char *zLMtime = db_column_text(&q1, 8);
      char *zMsg;
      if(cnt++){
        blob_append(&json, ",\n", 2);
      }
      blob_appendf(&json, "{\"msgid\":%d,", id);
      blob_appendf(&json, "\"mtime\":\"%.10sT%sZ\",", zDate, zDate+11);
      if( zLMtime && zLMtime[0] ){
        blob_appendf(&json, "\"lmtime\":%!j,", zLMtime);
      }
      blob_appendf(&json, "\"xfrom\":%!j,", zFrom);
      blob_appendf(&json, "\"uclr\":%!j,", hash_color(zFrom));

      zMsg = chat_format_to_html(zRawMsg ? zRawMsg : "");
      blob_appendf(&json, "\"xmsg\":%!j,", zMsg);
      fossil_free(zMsg);

      if( nByte==0 ){
        blob_appendf(&json, "\"fsize\":0");
      }else{
        blob_appendf(&json, "\"fsize\":%d,\"fname\":%!j,\"fmime\":%!j",
               nByte, zFName, zFMime);
      }

      if( iToDel ){
        blob_appendf(&json, ",\"mdel\":%d}", iToDel);
      }else{
        blob_append(&json, "}", 1);
      }
    }
    db_reset(&q1);
    if( cnt || msgBefore>0 ){
      break;
    }
    sqlite3_sleep(iDelay); nDelay--;
    while( nDelay>0 ){
      sqlite3_int64 newDataVers = db_int64(0,"PRAGMA repository.data_version");
      if( newDataVers!=dataVersion ){
        dataVersion = newDataVers;
        break;
      }
      sqlite3_sleep(iDelay); nDelay--;
    }
  } /* Exit by "break" */
  db_finalize(&q1);
  blob_append(&json, "\n]}", 3);
  cgi_set_content(&json);
  return;      
}

/*
** WEBPAGE: chat-download
**
** Download the CHAT.FILE attachment associated with a single chat
** entry.  The "name" query parameter begins with an integer that
** identifies the particular chat message. The integer may be followed
** by a / and a filename, which will indicate to the browser to use
** the indicated name when saving the file.
*/
void chat_download_webpage(void){
  int msgid;
  Blob r;
  const char *zMime;
  login_check_credentials();
  if( !g.perm.Chat ){
    style_header("Chat Not Authorized");
    @ <h1>Not Authorized</h1>
    @ <p>You do not have permission to use the chatroom on this
    @ repository.</p>
    style_finish_page();
    return;
  }
  chat_create_tables();
  msgid = atoi(PD("name","0"));
  blob_zero(&r);
  zMime = db_text(0, "SELECT fmime FROM chat wHERE msgid=%d", msgid);
  if( zMime==0 ) return;
  db_blob(&r, "SELECT file FROM chat WHERE msgid=%d", msgid);
  cgi_set_content_type(zMime);
  cgi_set_content(&r);
}


/*
** WEBPAGE: chat-delete
**
** Delete the chat entry identified by the name query parameter.
** Invoking fetch("chat-delete/"+msgid) from javascript in the client
** will delete a chat entry from the CHAT table.
**
** This routine both deletes the identified chat entry and also inserts
** a new entry with the current timestamp and with:
**
**   *  xmsg = NULL
**   *  file = NULL
**   *  mdel = The msgid of the row that was deleted
**
** This new entry will then be propagated to all listeners so that they
** will know to delete their copies of the message too.
*/
void chat_delete_webpage(void){
  int mdel;
  char *zOwner;
  login_check_credentials();
  if( !g.perm.Chat ) return;
  chat_create_tables();
  mdel = atoi(PD("name","0"));
  zOwner = db_text(0, "SELECT xfrom FROM chat WHERE msgid=%d", mdel);
  if( zOwner==0 ) return;
  if( fossil_strcmp(zOwner, g.zLogin)!=0 && !g.perm.Admin ) return;
  db_multi_exec(
    "PRAGMA secure_delete=ON;\n"
    "BEGIN;\n"
    "DELETE FROM chat WHERE msgid=%d;\n"
    "INSERT INTO chat(mtime, xfrom, mdel)"
    " VALUES(julianday('now'), %Q, %d);\n"
    "COMMIT;",
    mdel, g.zLogin, mdel
  );
}

/*
** COMMAND: chat
**
** Usage: %fossil chat [SUBCOMMAND] [--remote URL] [ARGS...]
**
** This command performs actions associated with the /chat instance
** on the default remote Fossil repository (the Fossil repository whose
** URL shows when you run the "fossil remote" command) or to the URL
** specified by the --remote option.  If there is no default remote
** Fossil repository and the --remote option is omitted, then this
** command fails with an error.
**
** When there is no SUBCOMMAND (when this command is simply "fossil chat")
** the response is to bring up a web-browser window to the chatroom
** on the default system web-browser.  You can accomplish the same by
** typing the appropriate URL into the web-browser yourself.  This
** command is merely a convenience for command-line oriented people.
**
** The following subcommands are supported:
**
** > fossil chat send [ARGUMENTS]
**
**      This command sends a new message to the chatroom.  The message
**      to be sent is determined by arguments as follows:
**
**        -f|--file FILENAME     File to attach to the message
**        -m|--message TEXT      Text of the chat message
**        --unsafe               Allow the use of unencrypted http://
**
** Additional subcommands may be added in the future.
*/
void chat_command(void){
  const char *zUrl = find_option("remote",0,1);
  int urlFlags = 0;
  int isDefaultUrl = 0;
  int i;

  db_find_and_open_repository(0,0);
  if( zUrl ){
    urlFlags = URL_PROMPT_PW;
  }else{
    zUrl = db_get("last-sync-url",0);
    if( zUrl==0 ){
      fossil_fatal("no \"remote\" repository defined");
    }else{
      isDefaultUrl = 1;
    }
  }
  url_parse(zUrl, urlFlags);
  if( g.url.isFile || g.url.isSsh ){
    fossil_fatal("chat only works for http:// and https:// URLs");
  }
  i = (int)strlen(g.url.path);
  while( i>0 && g.url.path[i-1]=='/' ) i--;
  if( g.url.port==g.url.dfltPort ){
    zUrl = mprintf(
      "%s://%T%.*T",
      g.url.protocol, g.url.name, i, g.url.path
    );
  }else{
    zUrl = mprintf(
      "%s://%T:%d%.*T",
      g.url.protocol, g.url.name, g.url.port, i, g.url.path
    );
  }
  if( g.argc==2 ){
    const char *zBrowser = fossil_web_browser();
    char *zCmd;
    verify_all_options();
    if( zBrowser==0 ) return;
#ifdef _WIN32
    zCmd = mprintf("%s %s/chat?cli &", zBrowser, zUrl);
#else
    zCmd = mprintf("%s \"%s/chat?cli\" &", zBrowser, zUrl);
#endif
    fossil_system(zCmd);
  }else if( strcmp(g.argv[2],"send")==0 ){
    const char *zFilename = find_option("file","r",1);
    const char *zMsg = find_option("message","m",1);
    int allowUnsafe = find_option("unsafe",0,0)!=0;
    const int mFlags = HTTP_GENERIC | HTTP_QUIET | HTTP_NOCOMPRESS;
    int i;
    const char *zPw;
    char *zLMTime;
    Blob up, down, fcontent;
    char zBoundary[80];
    sqlite3_uint64 r[3];
    if( zFilename==0 && zMsg==0 ){
      fossil_fatal("must have --message or --file or both");
    }
    if( !g.url.isHttps && !allowUnsafe ){
      fossil_fatal("URL \"%s\" is unencrypted. Use https:// instead", zUrl);
    }
    verify_all_options();
    if( g.argc>3 ){
      fossil_fatal("unknown extra argument: \"%s\"", g.argv[3]);
    }
    i = (int)strlen(g.url.path);
    while( i>0 && g.url.path[i-1]=='/' ) i--;
    g.url.path = mprintf("%.*s/chat-send", i, g.url.path);
    blob_init(&up, 0, 0);
    blob_init(&down, 0, 0);
    sqlite3_randomness(sizeof(r),r);
    sqlite3_snprintf(sizeof(zBoundary),zBoundary,
                     "--------%016llu%016llu%016llu", r[0], r[1], r[2]);
    blob_appendf(&up, "%s", zBoundary);
    zLMTime = db_text(0,
              "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%S','now','localtime')");
    if( zLMTime ){
      blob_appendf(&up,"\r\nContent-Disposition: form-data; name=\"lmtime\"\r\n"
                       "\r\n%z\r\n%s", zLMTime, zBoundary);
    }
    if( g.url.user && g.url.user[0] ){
      blob_appendf(&up,"\r\nContent-Disposition: form-data; name=\"resid\"\r\n"
                       "\r\n%z\r\n%s", obscure(g.url.user), zBoundary);
    }
    zPw = g.url.passwd;
    if( zPw==0 && isDefaultUrl ) zPw = unobscure(db_get("last-sync-pw", 0));
    if( zPw && zPw[0] ){
      blob_appendf(&up,"\r\nContent-Disposition: form-data; name=\"token\"\r\n"
                       "\r\n%z\r\n%s", obscure(zPw), zBoundary);
    }
    if( zMsg && zMsg[0] ){
      blob_appendf(&up,"\r\nContent-Disposition: form-data; name=\"msg\"\r\n"
                       "\r\n%s\r\n%s", zMsg, zBoundary);
    }
    if( zFilename && blob_read_from_file(&fcontent, zFilename, ExtFILE)>0 ){
      char *zFN = mprintf("%s", file_tail(zFilename));
      int i;
      const char *zMime = mimetype_from_name(zFilename);
      for(i=0; zFN[i]; i++){
        char c = zFN[i];
        if( fossil_isalnum(c) ) continue;
        if( c=='.' ) continue;
        if( c=='-' ) continue;
        zFN[i] = '_';
      }
      blob_appendf(&up,"\r\nContent-Disposition: form-data; name=\"file\";"
                       " filename=\"%s\"\r\n", zFN);
      blob_appendf(&up,"Content-Type: %s\r\n\r\n", zMime);
      blob_append(&up, fcontent.aData, fcontent.nUsed);
      blob_appendf(&up,"\r\n%s", zBoundary);
    }
    blob_append(&up,"--\r\n", 4);
    http_exchange(&up, &down, mFlags, 4, "multipart/form-data");
    blob_reset(&up);
    if( sqlite3_strglob("{\"isError\": true,*", blob_str(&down))==0 ){
      if( strstr(blob_str(&down), "not logged in")!=0 ){
        fossil_print("ERROR: username and/or password is incorrect\n");
      }else{
        fossil_print("ERROR: %s\n", blob_str(&down));
      }
      fossil_fatal("unable to send the chat message");
    }
    blob_reset(&down);
  }else if( strcmp(g.argv[2],"url")==0 ){
    /* Undocumented command.  Show the URL to access chat. */
    fossil_print("%s/chat\n", zUrl);
  }else{
    fossil_fatal("no such subcommand \"%s\".  Use --help for help", g.argv[2]);
  }
}

Added src/chat.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
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
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
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
/**
   This file contains the client-side implementation of fossil's /chat
   application. 
*/
(function(){
  const F = window.fossil, D = F.dom;
  const E1 = function(selector){
    const e = document.querySelector(selector);
    if(!e) throw new Error("missing required DOM element: "+selector);
    return e;
  };
  /**
     Returns true if e is entirely within the bounds of the window's viewport.
  */
  const isEntirelyInViewport = function(e) {
    const rect = e.getBoundingClientRect();
    return (
      rect.top >= 0 &&
      rect.left >= 0 &&
      rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
      rect.right <= (window.innerWidth || document.documentElement.clientWidth)
    );
  };

  /**
     Returns true if e's on-screen position vertically overlaps that
     of element v's. Horizontal overlap is ignored (we don't need it
     for our case).
  */
  const overlapsElemView = function(e,v) {
    const r1 = e.getBoundingClientRect(),
          r2 = v.getBoundingClientRect();
    if(r1.top<=r2.bottom && r1.top>=r2.top) return true;
    else if(r1.bottom<=r2.bottom && r1.bottom>=r2.top) return true;
    return false;
  };

  (function(){
    let dbg = document.querySelector('#debugMsg');
    if(dbg){
      /* This can inadvertently influence our flexbox layouts, so move
         it out of the way. */
      D.append(document.body,dbg);
    }
  })();
  const ForceResizeKludge = 0 ? function(){} : (function f(){
    /* Workaround for Safari mayhem regarding use of vh CSS units....
       We tried to use vh units to set the content area size for the
       chat layout, but Safari chokes on that, so we calculate that
       height here: 85% when in "normal" mode and 95% in chat-only
       mode. Larger than ~95% is too big for Firefox on Android,
       causing the input area to move off-screen. */
    if(!f.elemsToCount){
      f.elemsToCount = [
        document.querySelector('body > div.header'),
        document.querySelector('body > div.mainmenu'),
        document.querySelector('body > #hbdrop'),
        document.querySelector('body > div.footer')
      ];
      f.contentArea = E1('div.content');
    }
    const bcl = document.body.classList;
    const resized = function(){
      const wh = window.innerHeight,
            com = bcl.contains('chat-only-mode');
      var ht;
      var extra = 0;
      if(com){
        ht = wh;
      }else{
        f.elemsToCount.forEach((e)=>e ? extra += D.effectiveHeight(e) : false);
        ht = wh - extra;
      }
      f.contentArea.style.height =
        f.contentArea.style.maxHeight = [
          "calc(", (ht>=100 ? ht : 100), "px",
          " - 1em"/*fudge value*/,")"
          /* ^^^^ hypothetically not needed, but both Chrome/FF on
             Linux will force scrollbars on the body if this value is
             too small (<0.75em in my tests). */
        ].join('');
      if(false){
        console.debug("resized.",wh, extra, ht,
                      window.getComputedStyle(f.contentArea).maxHeight,
                      f.contentArea);
      }
    };
    var doit;
    window.addEventListener('resize',function(ev){
      clearTimeout(doit);
      doit = setTimeout(resized, 100);
    }, false);
    resized();
    return resized;
  })();
  fossil.FRK = ForceResizeKludge/*for debugging*/;
  const Chat = (function(){
    const cs = {
      e:{/*map of certain DOM elements.*/
        messageInjectPoint: E1('#message-inject-point'),
        pageTitle: E1('head title'),
        loadOlderToolbar: undefined /* the load-posts toolbar (dynamically created) */,
        inputWrapper: E1("#chat-input-area"),
        fileSelectWrapper: E1('#chat-input-file-area'),
        messagesWrapper: E1('#chat-messages-wrapper'),
        inputForm: E1('#chat-form'),
        btnSubmit: E1('#chat-message-submit'),
        inputSingle: E1('#chat-input-single'),
        inputMulti: E1('#chat-input-multi'),
        inputCurrent: undefined/*one of inputSingle or inputMulti*/,
        inputFile: E1('#chat-input-file'),
        contentDiv: E1('div.content'),
        btnMsgHome: E1('#chat-scroll-top'),
        btnMsgEnd: E1('#chat-scroll-bottom')
      },
      me: F.user.name,
      mxMsg: F.config.chat.initSize ? -F.config.chat.initSize : -50,
      mnMsg: undefined/*lowest message ID we've seen so far (for history loading)*/,
      pageIsActive: 'visible'===document.visibilityState,
      changesSincePageHidden: 0,
      notificationBubbleColor: 'white',
      totalMessageCount: 0, // total # of inbound messages
      //! Number of messages to load for the history buttons
      loadMessageCount: Math.abs(F.config.chat.initSize || 20),
      ajaxInflight: 0,
      /** Gets (no args) or sets (1 arg) the current input text field value,
          taking into account single- vs multi-line input. The getter returns
          a string and the setter returns this object. */
      inputValue: function(){
        const e = this.inputElement();
        if(arguments.length){
          e.value = arguments[0];
          return this;
        }
        return e.value;
      },
      /** Asks the current user input field to take focus. Returns this. */
      inputFocus: function(){
        this.inputElement().focus();
        return this;
      },
      /** Returns the current message input element. */
      inputElement: function(){
        return this.e.inputCurrent;
      },
      /** Toggles between single- and multi-line edit modes. Returns this. */
      inputToggleSingleMulti: function(){
        const old = this.e.inputCurrent;
        if(this.e.inputCurrent === this.e.inputSingle){
          this.e.inputCurrent = this.e.inputMulti;
        }else{
          this.e.inputCurrent = this.e.inputSingle;
        }
        const m = this.e.messagesWrapper,
              sTop = m.scrollTop,
              mh1 = m.clientHeight;
        D.addClass(old, 'hidden');
        D.removeClass(this.e.inputCurrent, 'hidden');
        const mh2 = m.clientHeight;
        m.scrollTo(0, sTop + (mh1-mh2));
        this.e.inputCurrent.value = old.value;
        old.value = '';
        return this;
      },
      /**
         If passed true or no arguments, switches to multi-line mode
         if currently in single-line mode. If passed false, switches
         to single-line mode if currently in multi-line mode. Returns
         this.
      */
      inputMultilineMode: function(yes){
        if(!arguments.length) yes = true;
        if(yes && this.e.inputCurrent === this.e.inputMulti) return this;
        else if(!yes && this.e.inputCurrent === this.e.inputSingle) return this;
        else return this.inputToggleSingleMulti();
      },
      /** Enables (if yes is truthy) or disables all elements in
       * this.disableDuringAjax. */
      enableAjaxComponents: function(yes){
        D[yes ? 'enable' : 'disable'](this.disableDuringAjax);
        return this;
      },
      /* Must be called before any API is used which starts ajax traffic.
         If this call represents the currently only in-flight ajax request,
         all DOM elements in this.disableDuringAjax are disabled.
         We cannot do this via a central API because (1) window.fetch()'s
         Promise-based API seemingly makes that impossible and (2) the polling
         technique holds ajax requests open for as long as possible. A call
         to this method obligates the caller to also call ajaxEnd().

         This must NOT be called for the chat-polling API except, as a
         special exception, the very first one which fetches the
         initial message list.
      */
      ajaxStart: function(){
        if(1===++this.ajaxInflight){
          this.enableAjaxComponents(false);
        }
      },
      /* Must be called after any ajax-related call for which
         ajaxStart() was called, regardless of success or failure. If
         it was the last such call (as measured by calls to
         ajaxStart() and ajaxEnd()), elements disabled by a prior call
         to ajaxStart() will be re-enabled. */
      ajaxEnd: function(){
        if(0===--this.ajaxInflight){
          this.enableAjaxComponents(true);
        }
      },
      disableDuringAjax: [
        /* List of DOM elements disable while ajax traffic is in
           transit. Must be populated before ajax starts. We do this
           to avoid various race conditions in the UI and long-running
           network requests. */
      ],
      /** Either scrolls .message-widget element eMsg into view
          immediately or, if it represents an inlined image, delays
          the scroll until the image is loaded, at which point it will
          scroll to either the newest message, if one is set or to
          eMsg (the liklihood is good, at least on initial page load,
          that the the image won't be loaded until other messages have
          been injected). */
      scheduleScrollOfMsg: function(eMsg){
        if(1===+eMsg.dataset.hasImage){
          eMsg.querySelector('img').addEventListener(
            'load', ()=>(this.e.newestMessage || eMsg).scrollIntoView(false)
          );
        }else{
          eMsg.scrollIntoView(false);
        }
        return this;
      },
      /* Injects element e as a new row in the chat, at the top of the
         list if atEnd is falsy, else at the end of the list, before
         the load-history widget. */
      injectMessageElem: function f(e, atEnd){
        const mip = atEnd ? this.e.loadOlderToolbar : this.e.messageInjectPoint,
              holder = this.e.messagesWrapper,
              prevMessage = this.e.newestMessage;
        if(atEnd){
          const fe = mip.nextElementSibling;
          if(fe) mip.parentNode.insertBefore(e, fe);
          else D.append(mip.parentNode, e);
        }else{
          D.append(holder,e);
          this.e.newestMessage = e;
        }
        if(!atEnd && !this._isBatchLoading
           && e.dataset.xfrom!==this.me
           && (prevMessage
               ? !this.messageIsInView(prevMessage)
               : false)){
          /* If a new non-history message arrives while the user is
             scrolled elsewhere, do not scroll to the latest
             message, but gently alert the user that a new message
             has arrived. */
          if(!f.btnDown){
            f.btnDown = D.button("⇣⇣⇣");
            f.btnDown.addEventListener('click',()=>this.scrollMessagesTo(1),false);
          }
          F.toast.message(f.btnDown," New message has arrived.");
        }else if(!this._isBatchLoading && e.dataset.xfrom===Chat.me){
          this.scheduleScrollOfMsg(e);
        }else if(!this._isBatchLoading){
          /* When a message from someone else arrives, we have to
             figure out whether or not to scroll it into view. Ideally
             we'd just stuff it in the UI and let the flexbox layout
             DTRT, but Safari has expressed, in no uncertain terms,
             some disappointment with that approach, so we'll
             heuristicize it: if the previous last message is in view,
             assume the user is at or near the input element and
             scroll this one into view. If that message is NOT in
             view, assume the user is up reading history somewhere and
             do NOT scroll because doing so would interrupt
             them. There are middle grounds here where the user will
             experience a slight UI jolt, but this heuristic mostly
             seems to work out okay. If there was no previous message,
             assume we don't have any messages yet and go ahead and
             scroll this message into view (noting that that scrolling
             is hypothetically a no-op in such cases).

             The wrench in these works are posts with IMG tags, as
             those images are loaded async and the element does not
             yet have enough information to know how far to scroll!
             For such cases we have to delay the scroll until the
             image loads (and we hope it does so before another
             message arrives).
          */
          if(1===+e.dataset.hasImage){
            e.querySelector('img').addEventListener('load',()=>e.scrollIntoView());
          }else if(!prevMessage || (prevMessage && isEntirelyInViewport(prevMessage))){
            e.scrollIntoView(false);
          }
        }
      },
      /** Returns true if chat-only mode is enabled. */
      isChatOnlyMode: ()=>document.body.classList.contains('chat-only-mode'),
      /**
         Enters (if passed a truthy value or no arguments) or leaves
         "chat-only" mode. That mode hides the page's header and
         footer, leaving only the chat application visible to the
         user.
      */
      chatOnlyMode: function f(yes){
        if(undefined === f.elemsToToggle){
          f.elemsToToggle = [];
          document.querySelectorAll(
            ["body > div.header",
             "body > div.mainmenu",
             "body > div.footer",
             "#debugMsg"
            ].join(',')
          ).forEach((e)=>f.elemsToToggle.push(e));
        }
        if(!arguments.length) yes = true;
        if(yes === this.isChatOnlyMode()) return this;
        if(yes){
          D.addClass(f.elemsToToggle, 'hidden');
          D.addClass(document.body, 'chat-only-mode');
          document.body.scroll(0,document.body.height);
        }else{
          D.removeClass(f.elemsToToggle, 'hidden');
          D.removeClass(document.body, 'chat-only-mode');
        }
        ForceResizeKludge();
        return this;
      },
      /** Tries to scroll the message area to...
          <0 = top of the message list, >0 = bottom of the message list,
          0 == the newest message (normally the same position as >1).
      */
      scrollMessagesTo: function(where){
        if(where<0){
          Chat.e.messagesWrapper.scrollTop = 0;
        }else if(where>0){
          Chat.e.messagesWrapper.scrollTop = Chat.e.messagesWrapper.scrollHeight;
        }else if(Chat.e.newestMessage){
          Chat.e.newestMessage.scrollIntoView(false);
        }
      },
      toggleChatOnlyMode: function(){
        return this.chatOnlyMode(!this.isChatOnlyMode());
      },
      /* Turn the message area top/bottom buttons on (yes===true), off
         (yes==false), or toggle them (no arguments). Returns this. */
      toggleNavButtons: function(yes){
        const e = [this.e.btnMsgHome, this.e.btnMsgEnd], c = 'hidden';
        if(0===arguments.length) D.toggleClass(e, c);
        else if(!arguments[0]) D.addClass(e, c);
        else D.removeClass(e, c);
      },
      messageIsInView: function(e){
        return e ? overlapsElemView(e, this.e.messagesWrapper) : false;
      },
      settings:{
        get: (k,dflt)=>F.storage.get(k,dflt),
        getBool: (k,dflt)=>F.storage.getBool(k,dflt),
        set: (k,v)=>F.storage.set(k,v),
        /* Toggles the boolean setting specified by k. Returns the
           new value.*/
        toggle: function(k){
          const v = this.getBool(k);
          this.set(k, !v);
          return !v;
        },
        defaults:{
          "images-inline": !!F.config.chat.imagesInline,
          "edit-multiline": false,
          "monospace-messages": false,
          "chat-only-mode": false,
          "audible-alert": true
        }
      },
      /** Plays a new-message notification sound IF the audible-alert
          setting is true, else this is a no-op. Returns this.
      */
      playNewMessageSound: function f(){
        if(f.uri){
          try{
            if(!f.audio) f.audio = new Audio(f.uri);
            f.audio.currentTime = 0;
            f.audio.play();
          }catch(e){
            console.error("Audio playblack failed.",e);
          }
        }
        return this;
      },
      /**
         Sets the current new-message audio alert URI (must be a
         repository-relative path which responds with an audio
         file). Pass a falsy value to disable audio alerts. Returns
         this. This setting is persistent. Returns this.
      */
      setNewMessageSound: function f(uri){
        delete this.playNewMessageSound.audio;
        this.playNewMessageSound.uri = uri;
        this.settings.set('audible-alert', !!uri);
        return this;
      }
    };
    cs.e.inputCurrent = cs.e.inputSingle;
    /* Install default settings... */
    Object.keys(cs.settings.defaults).forEach(function(k){
      const v = cs.settings.get(k,cs);
      if(cs===v) cs.settings.set(k,cs.settings.defaults[k]);
    });
    if(window.innerWidth<window.innerHeight){
      /* Alignment of 'my' messages: right alignment is conventional
         for mobile chat apps but can be difficult to read in wide
         windows (desktop/tablet landscape mode), so we default to a
         layout based on the apparent "orientation" of the window:
         tall vs wide. Can be toggled via settings popup. */
      document.body.classList.add('my-messages-right');
    }
    if(cs.settings.getBool('monospace-messages',false)){
      document.body.classList.add('monospace-messages');
    }
    cs.inputMultilineMode(cs.settings.getBool('edit-multiline',false));
    cs.chatOnlyMode(cs.settings.getBool('chat-only-mode'));
    cs.pageTitleOrig = cs.e.pageTitle.innerText;

    const qs = (e)=>document.querySelector(e);
    const argsToArray = function(args){
      return Array.prototype.slice.call(args,0);
    };
    /**
       Reports an error via console.error() and as a toast message.
       Accepts any argument types valid for fossil.toast.error().
    */
    cs.reportError = function(/*msg args*/){
      const args = argsToArray(arguments);
      console.error("chat error:",args);
      F.toast.error.apply(F.toast, args);
    };
    /**
       Reports an error in the form of a new message in the chat
       feed. All arguments are appended to the message's content area
       using fossil.dom.append(), so may be of any type supported by
       that function.
    */
    cs.reportErrorAsMessage = function(/*msg args*/){
      const args = argsToArray(arguments);
      console.error("chat error:",args);
      const d = new Date().toISOString(),
            msg = {
              isError: true,
              xfrom: null,
              msgid: -1,
              mtime: d,
              lmtime: d,
              xmsg: args
            }, mw = new this.MessageWidget(msg);
      this.injectMessageElem(mw.e.body);
      mw.scrollIntoView();
    };

    cs.getMessageElemById = function(id){
      return qs('[data-msgid="'+id+'"]');
    };

    /** Finds the last .message-widget element and returns it or
        the undefined value if none are found. */
    cs.fetchLastMessageElem = function(){
      const msgs = document.querySelectorAll('.message-widget');
      var rc;
      if(msgs.length){
        rc = this.e.newestMessage = msgs[msgs.length-1];
      }
      return rc;
    };

    /**
       LOCALLY deletes a message element by the message ID or passing
       the .message-row element. Returns true if it removes an element,
       else false.
    */
    cs.deleteMessageElem = function(id){
      var e;
      if(id instanceof HTMLElement){
        e = id;
        id = e.dataset.msgid;
      }else{
        e = this.getMessageElemById(id);
      }
      if(e && id){
        D.remove(e);
        if(e===this.e.newestMessage){
          this.fetchLastMessageElem();
        }
        F.toast.message("Deleted message "+id+".");
      }
      return !!e;
    };

    /** Given a .message-row element, this function returns whethe the
        current user may, at least hypothetically, delete the message
        globally.  A user may always delete a local copy of a
        post. The server may trump this, e.g. if the login has been
        cancelled after this page was loaded.
    */
    cs.userMayDelete = function(eMsg){
      return +eMsg.dataset.msgid>0
        && (this.me === eMsg.dataset.xfrom
            || F.user.isAdmin/*will be confirmed server-side*/);
    };

    /** Returns a new Error() object encapsulating state from the given
        fetch() response promise. */
    cs._newResponseError = function(response){
      return new Error([
        "HTTP status ", response.status,": ",response.url,": ",
        response.statusText].join(''));
    };
    
    /** Helper for reporting HTTP-level response errors via fetch().
        If response.ok then response.json() is returned, else an Error
        is thrown. */
    cs._fetchJsonOrError = function(response){
      if(response.ok) return response.json();
      else throw cs._newResponseError(response);
    };
    
    /**
       Removes the given message ID from the local chat record and, if
       the message was posted by this user OR this user in an
       admin/setup, also submits it for removal on the remote.

       id may optionally be a DOM element, in which case it must be a
       .message-row element.
    */
    cs.deleteMessage = function(id){
      var e;
      if(id instanceof HTMLElement){
        e = id;
        id = e.dataset.msgid;
      }else{
        e = this.getMessageElemById(id);
      }
      if(!(e instanceof HTMLElement)) return;
      if(this.userMayDelete(e)){
        this.ajaxStart();
        fetch("chat-delete/" + id)
          .then(function(response){
            if(!response.ok) throw cs._newResponseError(response);
            return response;
          })
          .then(()=>this.deleteMessageElem(e))
          .catch(err=>this.reportErrorAsMessage(err))
          .finally(()=>this.ajaxEnd());
      }else{
        this.deleteMessageElem(id);
      }
    };
    document.addEventListener('visibilitychange', function(ev){
      cs.pageIsActive = ('visible' === document.visibilityState);
      if(cs.pageIsActive){
        cs.e.pageTitle.innerText = cs.pageTitleOrig;
      }
    }, true);
    return cs;
  })()/*Chat initialization*/;

  /**
     Custom widget type for rendering messages (one message per
     instance). These are modelled after FIELDSET elements but we
     don't use FIELDSET because of cross-browser inconsistencies in
     features of the FIELDSET/LEGEND combination, e.g. inability to
     align legends via CSS in Firefox and clicking-related
     deficiencies in Safari.
  */
  Chat.MessageWidget = (function(){
    /**
       Constructor. If passed an argument, it is passed to
       this.setMessage() after initialization.
    */
    const cf = function(){
      this.e = {
        body: D.addClass(D.div(), 'message-widget'),
        tab: D.addClass(D.span(), 'message-widget-tab'),
        content: D.addClass(D.div(), 'message-widget-content')
      };
      D.append(this.e.body, this.e.tab, this.e.content);
      this.e.tab.setAttribute('role', 'button');
      if(arguments.length){
        this.setMessage(arguments[0]);
      }
    };
    const theTime = function(d){
      return [d.getHours(),":",
              (d.getMinutes()+100).toString().slice(1,3)
             ].join('');
    };
    cf.prototype = {
      setLabel: function(label){
        return this;
      },
      scrollIntoView: function(){
        this.e.content.scrollIntoView();
      },
      setMessage: function(m){
        const ds = this.e.body.dataset;
        ds.timestamp = m.mtime;
        ds.lmtime = m.lmtime;
        ds.msgid = m.msgid;
        ds.xfrom = m.xfrom || '';
        if(m.xfrom === Chat.me){
          D.addClass(this.e.body, 'mine');
        }
        if(m.uclr){
          this.e.content.style.backgroundColor = m.uclr;
          this.e.tab.style.backgroundColor = m.uclr;
        }
        const d = new Date(m.mtime);
        D.clearElement(this.e.tab);
        var contentTarget = this.e.content;
        if(m.xfrom){
          D.append(
            this.e.tab,
            D.text(m.xfrom," #",(m.msgid||'???'),' @ ',theTime(d))
          );
        }else{/*notification*/
          D.addClass(this.e.body, 'notification');
          if(m.isError){
            D.addClass([contentTarget, this.e.tab], 'error');
          }
          D.append(
            this.e.tab,
            D.text('notification @ ',theTime(d))
          );
        }
        if( m.xfrom && m.fsize>0 ){
          if( m.fmime
              && m.fmime.startsWith("image/")
              && Chat.settings.getBool('images-inline',true)
            ){
            contentTarget.appendChild(D.img("chat-download/" + m.msgid));
            ds.hasImage = 1;
          }else{
            const a = D.a(
              window.fossil.rootPath+
                'chat-download/' + m.msgid+'/'+encodeURIComponent(m.fname),
              // ^^^ add m.fname to URL to cause downloaded file to have that name.
              "(" + m.fname + " " + m.fsize + " bytes)"
            )
            D.attr(a,'target','_blank');
            contentTarget.appendChild(a);
          }
        }
        if(m.xmsg){
          if(m.fsize>0){
            /* We have file/image content, so need another element for
               the message text. */
            contentTarget = D.div();
            D.append(this.e.content, contentTarget);
          }
          // The m.xmsg text comes from the same server as this script and
          // is guaranteed by that server to be "safe" HTML - safe in the
          // sense that it is not possible for a malefactor to inject HTML
          // or javascript or CSS.  The m.xmsg content might contain
          // hyperlinks, but otherwise it will be markup-free.  See the
          // chat_format_to_html() routine in the server for details.
          //
          // Hence, even though innerHTML is normally frowned upon, it is
          // perfectly safe to use in this context.
          if(m.xmsg instanceof Array){
            // Used by Chat.reportErrorAsMessage()
            D.append(contentTarget, m.xmsg);
          }else{
            contentTarget.innerHTML = m.xmsg;
          }
        }
        this.e.tab.addEventListener('click', this._handleLegendClicked, false);
        return this;
      },
      /* Event handler for clicking .message-user elements to show their
         timestamps. */
      _handleLegendClicked: function f(ev){
        if(!f.popup){
          /* Timestamp popup widget */
          f.popup = new F.PopupWidget({
            cssClass: ['fossil-tooltip', 'chat-message-popup'],
            refresh:function(){
              const eMsg = this._eMsg;
              if(!eMsg) return;
              D.clearElement(this.e);
              const d = new Date(eMsg.dataset.timestamp);
              if(d.getMinutes().toString()!=="NaN"){
                // Date works, render informative timestamps
                const xfrom = eMsg.dataset.xfrom || 'server';
                D.append(this.e,
                         D.append(D.span(), localTimeString(d)," ",Chat.me," time"),
                         D.append(D.span(), iso8601ish(d)));
                if(eMsg.dataset.lmtime && xfrom!==Chat.me){
                  D.append(this.e,
                           D.append(D.span(), localTime8601(
                             new Date(eMsg.dataset.lmtime)
                           ).replace('T',' ')," ",xfrom," time"));
                }
              }else{
                // Date doesn't work, so dumb it down...
                D.append(this.e, D.append(D.span(), eMsg.dataset.timestamp," zulu"));
              }
              const toolbar = D.addClass(D.div(), 'toolbar');
              D.append(this.e, toolbar);
              const btnDeleteLocal = D.button("Delete locally");
              D.append(toolbar, btnDeleteLocal);
              const self = this;
              btnDeleteLocal.addEventListener('click', function(){
                self.hide();
                Chat.deleteMessageElem(eMsg);
              });
              if(Chat.userMayDelete(eMsg)){
                const btnDeleteGlobal = D.button("Delete globally");
                D.append(toolbar, btnDeleteGlobal);
                btnDeleteGlobal.addEventListener('click', function(){
                  self.hide();
                  Chat.deleteMessage(eMsg);
                });
              }
            }/*refresh()*/
          });
          f.popup.installHideHandlers();
          f.popup.hide = function(){
            delete this._eMsg;
            D.clearElement(this.e);
            return this.show(false);
          };
        }/*end static init*/
        const rect = ev.target.getBoundingClientRect();
        const eMsg = ev.target.parentNode/*the owning .message-widget element*/;
        f.popup._eMsg = eMsg;
        let x = rect.left, y = rect.topm;
        f.popup.show(ev.target)/*so we can get its computed size*/;
        if(eMsg.dataset.xfrom===Chat.me
           && document.body.classList.contains('my-messages-right')){
          // Shift popup to the left for right-aligned messages to avoid
          // truncation off the right edge of the page.
          const pRect = f.popup.e.getBoundingClientRect();
          x = rect.right - pRect.width;
        }
        f.popup.show(x, y);
      }/*_handleLegendClicked()*/
    };
    return cf;
  })()/*MessageWidget*/;

  const BlobXferState = (function(){/*drag/drop bits...*/
    /* State for paste and drag/drop */
    const bxs = {
      dropDetails: document.querySelector('#chat-drop-details'),
      blob: undefined,
      clear: function(){
        this.blob = undefined;
        D.clearElement(this.dropDetails);
        Chat.e.inputFile.value = "";
      }
    };
    /** Updates the paste/drop zone with details of the pasted/dropped
        data. The argument must be a Blob or Blob-like object (File) or
        it can be falsy to reset/clear that state.*/
    const updateDropZoneContent = function(blob){
      const dd = bxs.dropDetails;
      bxs.blob = blob;
      D.clearElement(dd);
      if(!blob){
        Chat.e.inputFile.value = '';
        return;
      }
      D.append(dd, "Name: ", blob.name,
               D.br(), "Size: ",blob.size);
      if(blob.type && blob.type.startsWith("image/")){
        const img = D.img();
        D.append(dd, D.br(), img);
        const reader = new FileReader();
        reader.onload = (e)=>img.setAttribute('src', e.target.result);
        reader.readAsDataURL(blob);
      }
      const btn = D.button("Cancel");
      D.append(dd, D.br(), btn);
      btn.addEventListener('click', ()=>updateDropZoneContent(), false);
    };
    Chat.e.inputFile.addEventListener('change', function(ev){
      updateDropZoneContent(this.files && this.files[0] ? this.files[0] : undefined)
    });
    /* Handle image paste from clipboard. TODO: figure out how we can
       paste non-image binary data as if it had been selected via the
       file selection element. */
    document.addEventListener('paste', function(event){
      const items = event.clipboardData.items,
            item = items[0];
      if(!item || !item.type) return;
      else if('file'===item.kind){
        updateDropZoneContent(false/*clear prev state*/);
        updateDropZoneContent(items[0].getAsFile());
      }
    }, false);
    /* Add help button for drag/drop/paste zone */
    Chat.e.inputFile.parentNode.insertBefore(
      F.helpButtonlets.create(
        Chat.e.fileSelectWrapper.querySelector('.help-buttonlet')
      ), Chat.e.inputFile
    );
    ////////////////////////////////////////////////////////////
    // File drag/drop visual notification.
    const dropHighlight = Chat.e.inputFile /* target zone */;
    const dropEvents = {
      drop: function(ev){
        D.removeClass(dropHighlight, 'dragover');
      },
      dragenter: function(ev){
        ev.preventDefault();
        ev.dataTransfer.dropEffect = "copy";
        D.addClass(dropHighlight, 'dragover');
      },
      dragleave: function(ev){
        D.removeClass(dropHighlight, 'dragover');
      },
      dragend: function(ev){
        D.removeClass(dropHighlight, 'dragover');
      }
    };
    Object.keys(dropEvents).forEach(
      (k)=>Chat.e.inputFile.addEventListener(k, dropEvents[k], true)
    );
    return bxs;
  })()/*drag/drop*/;

  const tzOffsetToString = function(off){
    const hours = Math.round(off/60), min = Math.round(off % 30);
    return ''+(hours + (min ? '.5' : ''));
  };
  const pad2 = (x)=>('0'+x).substr(-2);
  const localTime8601 = function(d){
    return [
      d.getYear()+1900, '-', pad2(d.getMonth()+1), '-', pad2(d.getDate()),
      'T', pad2(d.getHours()),':', pad2(d.getMinutes()),':',pad2(d.getSeconds())
    ].join('');
  };
    
  Chat.submitMessage = function(){
    const fd = new FormData(this.e.inputForm)
    /* ^^^^ we don't really want/need the FORM element, but when
       FormData() is default-constructed here then the server
       segfaults, and i have no clue why! */;
    const msg = this.inputValue().trim();
    if(msg) fd.set('msg',msg);
    const file = BlobXferState.blob || this.e.inputFile.files[0];
    if(file) fd.set("file", file);
    if( !msg && !file ) return;
    const self = this;
    fd.set("lmtime", localTime8601(new Date()));
    fetch("chat-send",{
      method: 'POST',
      body: fd
    }).then((x)=>{
      if(x.ok) return x.text();
      else throw Chat._newResponseError(x);
    }).then(function(txt){
        if(!txt) return/*success response*/;
        try{
          const json = JSON.parse(txt);
          self.newContent({msgs:[json]});
        }catch(e){
          self.reportError(e);
          return;
        }
      })
      .catch((e)=>this.reportErrorAsMessage(e));
    BlobXferState.clear();
    Chat.inputValue("").inputFocus();
  };

  Chat.e.inputSingle.addEventListener('keydown',function(ev){
    if(13===ev.keyCode/*ENTER*/){
      ev.preventDefault();
      ev.stopPropagation();
      Chat.submitMessage();
      return false;
    }
  }, false);
  Chat.e.inputMulti.addEventListener('keydown',function(ev){
    if(ev.ctrlKey && 13 === ev.keyCode){
      ev.preventDefault();
      ev.stopPropagation();
      Chat.submitMessage();
      return false;
    }
  }, false);
  Chat.e.btnSubmit.addEventListener('click',(e)=>{
    e.preventDefault();
    Chat.submitMessage();
    return false;
  });

  /* Returns a new TEXT node with the given text content. */
  /** Returns the local time string of Date object d, defaulting
      to the current time. */
  const localTimeString = function ff(d){
    if(!ff.pad){
      ff.pad = (x)=>(''+x).length>1 ? x : '0'+x;
    }
    d || (d = new Date());
    return [
      d.getFullYear(),'-',ff.pad(d.getMonth()+1/*sigh*/),
      '-',ff.pad(d.getDate()),
      ' ',ff.pad(d.getHours()),':',ff.pad(d.getMinutes()),
      ':',ff.pad(d.getSeconds())
    ].join('');
  };
  /* Returns an almost-ISO8601 form of Date object d. */
  const iso8601ish = function(d){
    return d.toISOString()
      .replace('T',' ').replace(/\.\d+/,'').replace('Z', ' zulu');
  };

  (function(){/*Set up #chat-settings-button */
    const settingsButton = document.querySelector('#chat-settings-button');
    var popupSize = undefined/*placement workaround*/;
    const settingsPopup = new F.PopupWidget({
      cssClass: ['fossil-tooltip', 'chat-settings-popup']
    });
    /* Settings menu entries... */
    const settingsOps = [{
      label: "Multi-line input",
      boolValue: ()=>Chat.inputElement()===Chat.e.inputMulti,
      persistentSetting: 'edit-multiline',
      callback: function(){
        Chat.inputToggleSingleMulti();
      }
    },{
      label: "Monospace message font",
      boolValue: ()=>document.body.classList.contains('monospace-messages'),
      persistentSetting: 'monospace-messages',
      callback: function(){
        document.body.classList.toggle('monospace-messages');
      }
    },{
      label: "Chat-only mode",
      boolValue: ()=>Chat.isChatOnlyMode(),
      persistentSetting: 'chat-only-mode',
      callback: function(){
        Chat.toggleChatOnlyMode();
      }
    },{
      label: "Left-align my posts",
      boolValue: ()=>!document.body.classList.contains('my-messages-right'),
      callback: function f(){
        document.body.classList.toggle('my-messages-right');
      }
    },{
      label: "Images inline",
      boolValue: ()=>Chat.settings.getBool('images-inline'),
      callback: function(){
        const v = Chat.settings.toggle('images-inline');
        F.toast.message("Image mode set to "+(v ? "inline" : "hyperlink")+".");
      }
    },{
      label: "Message home/end buttons",
      boolValue: ()=>!Chat.e.btnMsgHome.classList.contains('hidden'),
      callback: ()=>Chat.toggleNavButtons()
    }];

    /** Set up selection list of notification sounds. */
    if(true/*flip this to false to enable selection of audio files*/){
      settingsOps.push({
        label: "Audible alerts",
        boolValue: ()=>Chat.settings.getBool('audible-alert'),
        callback: function(){
          const v = Chat.settings.toggle('audible-alert');
          Chat.setNewMessageSound(v ? F.config.chat.alertSound : false);
          if(v) setTimeout(()=>Chat.playNewMessageSound(), 50);
          F.toast.message("Audio notifications "+(v ? "enabled" : "disabled")+".");
        }
      });
      Chat.setNewMessageSound(
        Chat.settings.getBool('audible-alert') ? F.config.chat.alertSound : false
      );
    }else{
      /* Disabled per chatroom discussion: selection list of audio files for
         chat notification. */
      const selectSound = settingsOps.selectSound = D.addClass(D.select(), 'menu-entry');
      D.disable(D.option(selectSound, "0", "Audible alert..."));
      D.option(selectSound, "", "(no audio)");
      F.config.chat.alerts.forEach(function(a){
        D.option(selectSound, a);
      });
      if(true===Chat.settings.getBool('audible-alert')){
        selectSound.selectedIndex = 2/*first audio file in the list*/;
      }else{
        selectSound.value = Chat.settings.get('audible-alert','');
        if(selectSound.selectedIndex<0){
          /*Missing file - removed after this setting was applied. Fall back
            to the first sound in the list. */
          selectSound.selectedIndex = 2;
        }
      }
      selectSound.addEventListener('change',function(){
        const v = this.selectedIndex>1 ? this.value : '';
        Chat.setNewMessageSound(v);
        F.toast.message("Audio notifications "+(v ? "enabled" : "disabled")+".");
        if(v) setTimeout(()=>Chat.playNewMessageSound(), 50);
        settingsPopup.hide();
      }, false);
      Chat.setNewMessageSound(selectSound.value);
    }/*audio notification config*/
    /**
       Rebuild the menu each time it's shown so that the toggles can
       show their current values.
    */
    settingsPopup.options.refresh = function(){
      D.clearElement(this.e);
      settingsOps.forEach(function(op){
        const line = D.addClass(D.span(), 'menu-entry');
        const btn = D.append(D.addClass(D.span(), 'button'), op.label);
        const callback = function(ev){
          settingsPopup.hide();
          op.callback(ev);
          if(op.persistentSetting){
            Chat.settings.set(op.persistentSetting, op.boolValue());
          }
        };
        D.append(line, btn);
        if(op.hasOwnProperty('boolValue')){
          const check = D.attr(D.checkbox(1, op.boolValue()),
                                          'aria-label', op.label);
          D.append(line, check);
        }
        D.append(settingsPopup.e, line);
        line.addEventListener('click', callback);
      });
      if(settingsOps.selectSound){
        D.append(settingsPopup.e, settingsOps.selectSound);
      }
    };
    settingsPopup.installHideHandlers(
      false, settingsOps.selectSound ? false : true,
      true)
    /** Reminder: click-to-hide interferes with "?" embedded within
        the popup, so cannot be used together with those. Enabling
        this means, however, that tapping the menu button to toggle
        the menu cannot work because tapping the menu button while the
        menu is opened will, because of the click-to-hide handler,
        hide the menu before the button gets an event saying to toggle
        it.

        Reminder: because we need a SELECT element for the audio file
        selection (since that list can be arbitrarily long), we have
        to disable tap-outside-the-popup-to-close-it via passing false
        as the 2nd argument to installHideHandlers(). If we don't,
        tapping on the select element is unreliable on desktop
        browsers and doesn't seem to work at all on mobile. */;
    D.attr(settingsButton, 'role', 'button');
    settingsButton.addEventListener('click',function(ev){
      //ev.preventDefault();
      if(settingsPopup.isShown()) settingsPopup.hide();
      else settingsPopup.show(settingsButton);
      /* Reminder: we cannot toggle the visibility from her
       */
    }, false);

    /* Find an ideal X/Y position for the popup, directly above the settings
       button, based on the size of the popup... */
    settingsPopup.show(document.body);
    popupSize = settingsPopup.e.getBoundingClientRect();
    settingsPopup.hide();
    settingsPopup.options.adjustX = function(x){
      const rect = settingsButton.getBoundingClientRect();
      return rect.right - popupSize.width;
    };
    settingsPopup.options.adjustY = function(y){
      const rect = settingsButton.getBoundingClientRect();
      return rect.top - popupSize.height -2;
    };
  })()/*#chat-settings-button setup*/;

  (function(){ /* buttons to scroll to the begin/end of the messages. */
    Chat.e.btnMsgEnd.addEventListener('click',function(ev){
      ev.preventDefault();
      Chat.scrollMessagesTo(1);
      return false;
    });
    Chat.e.btnMsgHome.addEventListener('click',function(ev){
      ev.preventDefault();
      Chat.scrollMessagesTo(-1);
      return false;
    });
  })();
  
  /** Callback for poll() to inject new content into the page.  jx ==
      the response from /chat-poll. If atEnd is true, the message is
      appended to the end of the chat list (for loading older
      messages), else the beginning (the default). */
  const newcontent = function f(jx,atEnd){
    if(!f.processPost){
      /** Processes chat message m, placing it either the start (if atEnd
          is falsy) or end (if atEnd is truthy) of the chat history. atEnd
          should only be true when loading older messages. */
      f.processPost = function(m,atEnd){
        ++Chat.totalMessageCount;
        if( m.msgid>Chat.mxMsg ) Chat.mxMsg = m.msgid;
        if( !Chat.mnMsg || m.msgid<Chat.mnMsg) Chat.mnMsg = m.msgid;
        if( m.mdel ){
          /* A record deletion notice. */
          Chat.deleteMessageElem(m.mdel);
          return;
        }
        if(!Chat._isBatchLoading /*&& Chat.me!==m.xfrom*/ && Chat.playNewMessageSound){
          Chat.playNewMessageSound();
        }
        const row = new Chat.MessageWidget(m);
        Chat.injectMessageElem(row.e.body,atEnd);
        if(m.isError){
          Chat._gotServerError = m;
        }
      }/*processPost()*/;
    }/*end static init*/
    jx.msgs.forEach((m)=>f.processPost(m,atEnd));
    if('visible'===document.visibilityState){
      if(Chat.changesSincePageHidden){
        Chat.changesSincePageHidden = 0;
        Chat.e.pageTitle.innerText = Chat.pageTitleOrig;
      }
    }else{
      Chat.changesSincePageHidden += jx.msgs.length;
      if(jx.msgs.length){
        Chat.e.pageTitle.innerText = '[*] '+Chat.pageTitleOrig;
      }
    }
  }/*newcontent()*/;
  Chat.newContent = newcontent;

  (function(){
    /** Add toolbar for loading older messages. We use a FIELDSET here
        because a fieldset is the only parent element type which can
        automatically enable/disable its children by
        enabling/disabling the parent element. */
    const loadLegend = D.legend("Load...");
    const toolbar = Chat.e.loadOlderToolbar = D.attr(
      D.fieldset(loadLegend), "id", "load-msg-toolbar"
    );
    Chat.disableDuringAjax.push(toolbar);
    /* Loads the next n oldest messages, or all previous history if n is negative. */
    const loadOldMessages = function(n){
      Chat.ajaxStart();
      Chat.e.messagesWrapper.classList.add('loading');
      Chat._isBatchLoading = true;
      var gotMessages = false;
      const scrollHt = Chat.e.messagesWrapper.scrollHeight,
            scrollTop = Chat.e.messagesWrapper.scrollTop;
      fetch("chat-poll?before="+Chat.mnMsg+"&n="+n)
        .then(Chat._fetchJsonOrError)
        .then(function(x){
          gotMessages = x.msgs.length;
          newcontent(x,true);
        })
        .catch(e=>Chat.reportErrorAsMessage(e))
        .finally(function(){
          Chat._isBatchLoading = false;
          Chat.e.messagesWrapper.classList.remove('loading');
          Chat.ajaxEnd();
          if(Chat._gotServerError){
            F.toast.error("Got an error response from the server. ",
                          "See message for details.");
            return;
          }else if(n<0/*we asked for all history*/
             || 0===gotMessages/*we found no history*/
             || (n>0 && gotMessages<n /*we got fewer history entries than requested*/)
             || (false!==gotMessages && n===0 && gotMessages<Chat.loadMessageCount
                 /*we asked for default amount and got fewer than that.*/)){
            /* We've loaded all history. Permanently disable the
               history-load toolbar and keep it from being re-enabled
               via the ajaxStart()/ajaxEnd() mechanism... */
            const div = Chat.e.loadOlderToolbar.querySelector('div');
            D.append(D.clearElement(div), "All history has been loaded.");
            D.addClass(Chat.e.loadOlderToolbar, 'all-done');
            const ndx = Chat.disableDuringAjax.indexOf(Chat.e.loadOlderToolbar);
            if(ndx>=0) Chat.disableDuringAjax.splice(ndx,1);
            Chat.e.loadOlderToolbar.disabled = true;
          }
          if(gotMessages > 0){
            F.toast.message("Loaded "+gotMessages+" older messages.");
            /* Return scroll position to where it was when the history load
               was requested, per user request */
            Chat.e.messagesWrapper.scrollTo(
              0, Chat.e.messagesWrapper.scrollHeight - scrollHt + scrollTop
            );
          }
        });
    };
    const wrapper = D.div(); /* browsers don't all properly handle >1 child in a fieldset */;
    D.append(toolbar, wrapper);
    var btn = D.button("Previous "+Chat.loadMessageCount+" messages");
    D.append(wrapper, btn);
    btn.addEventListener('click',()=>loadOldMessages(Chat.loadMessageCount));
    btn = D.button("All previous messages");
    D.append(wrapper, btn);
    btn.addEventListener('click',()=>loadOldMessages(-1));
    D.append(Chat.e.messagesWrapper, toolbar);
    toolbar.disabled = true /*will be enabled when msg load finishes */;
  })()/*end history loading widget setup*/;

  async function poll(isFirstCall){
    if(poll.running) return;
    poll.running = true;
    if(isFirstCall){
      Chat.ajaxStart();
      Chat.e.messagesWrapper.classList.add('loading');
    }
    Chat._isBatchLoading = isFirstCall;
    var p = fetch("chat-poll?name=" + Chat.mxMsg);
    p.then(Chat._fetchJsonOrError)
      .then(y=>newcontent(y))
      .catch(e=>console.error(e))
    /* ^^^ we don't use Chat.reportError(e) here b/c the polling
       fails exepectedly when it times out, but is then immediately
       resumed, and reportError() produces a loud error message. */
      .finally(function(){
        if(isFirstCall){
          Chat._isBatchLoading = false;
          Chat.ajaxEnd();
          Chat.e.messagesWrapper.classList.remove('loading');
          setTimeout(function(){
            Chat.scrollMessagesTo(1);
          }, 250);
        }
        if(Chat._gotServerError && Chat.intervalTimer){
          clearInterval(Chat.intervalTimer);
          delete Chat.intervalTimer;
        }
        poll.running=false;
      });
  }
  Chat._gotServerError = poll.running = false;
  poll(true);
  if(!Chat._gotServerError){
    Chat.intervalTimer = setInterval(poll, 1000);
  }
  if( window.fossil.config.chat.fromcli ){
    Chat.chatOnlyMode(true);
  }

  F.page.chat = Chat/* enables testing the APIs via the dev tools */;
})();

Changes to src/checkin.c.

1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
  const char *zEditor;
  char *zCmd;
  char *zFile;
  Blob reply, line;
  char *zComment;
  int i;

  zEditor = fossil_text_editor();db_get("editor", 0);
  if( zEditor==0 ){
    if( blob_size(pPrompt)>0 ){
      blob_append(pPrompt,
         "#\n"
         "# Since no default text editor is set using EDITOR or VISUAL\n"
         "# environment variables or the \"fossil set editor\" command,\n"
         "# and because no comment was specified using the \"-m\" or \"-M\"\n"







|







1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
  const char *zEditor;
  char *zCmd;
  char *zFile;
  Blob reply, line;
  char *zComment;
  int i;

  zEditor = fossil_text_editor();
  if( zEditor==0 ){
    if( blob_size(pPrompt)>0 ){
      blob_append(pPrompt,
         "#\n"
         "# Since no default text editor is set using EDITOR or VISUAL\n"
         "# environment variables or the \"fossil set editor\" command,\n"
         "# and because no comment was specified using the \"-m\" or \"-M\"\n"

Changes to src/checkout.c.

260
261
262
263
264
265
266





267
268
269
270
271
272
273
274
275
276
/*
** COMMAND: checkout*
** COMMAND: co*
**
** Usage: %fossil checkout ?VERSION | --latest? ?OPTIONS?
**    or: %fossil co ?VERSION | --latest? ?OPTIONS?
**





** Check out a version specified on the command-line.  This command
** will abort if there are edited files in the current checkout unless
** the --force option appears on the command-line.  The --keep option
** leaves files on disk unchanged, except the manifest and manifest.uuid
** files.
**
** The --latest flag can be used in place of VERSION to checkout the
** latest version in the repository.
**
** Options:







>
>
>
>
>
|
|
|







260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
/*
** COMMAND: checkout*
** COMMAND: co*
**
** Usage: %fossil checkout ?VERSION | --latest? ?OPTIONS?
**    or: %fossil co ?VERSION | --latest? ?OPTIONS?
**
** NOTE: Most people use "fossil update" instead of "fossil checkout" for
** day-to-day operations.  If you are new to Fossil and trying to learn your
** way around, it is recommended that you become familiar with the
** "fossil update" command first.
**
** This command changes the current check-out to the version specified
** as an argument.  The command aborts if there are edited files in the
** current checkout unless the --force option is used.  The --keep option
** leaves files on disk unchanged, except the manifest and manifest.uuid
** files.
**
** The --latest flag can be used in place of VERSION to checkout the
** latest version in the repository.
**
** Options:

Changes to src/clone.c.

420
421
422
423
424
425
426
427
428
  }else{
    const char *zNm = db_get("short-project-name","clone");
    @ <p>Clone the repository using this command:
    @ <blockquote><pre>
    @ fossil  clone  %s(g.zBaseURL)  %h(zNm).fossil
    @ </pre></blockquote>
  }
  style_finish_page("download");
}







|

420
421
422
423
424
425
426
427
428
  }else{
    const char *zNm = db_get("short-project-name","clone");
    @ <p>Clone the repository using this command:
    @ <blockquote><pre>
    @ fossil  clone  %s(g.zBaseURL)  %h(zNm).fossil
    @ </pre></blockquote>
  }
  style_finish_page();
}

Changes to src/cookies.c.

223
224
225
226
227
228
229
230
231
  @ "fossil_display_settings" cookie.
  @ <ul>
  @ <li>Raw cookie value: "%h(PD("fossil_display_settings",""))"
  for(i=0; i<cookies.nParam; i++){
    @ <li>%h(cookies.aParam[i].zPName): "%h(cookies.aParam[i].zPValue)"
  }
  @ </ul>
  style_finish_page("cookies");
}







|

223
224
225
226
227
228
229
230
231
  @ "fossil_display_settings" cookie.
  @ <ul>
  @ <li>Raw cookie value: "%h(PD("fossil_display_settings",""))"
  for(i=0; i<cookies.nParam; i++){
    @ <li>%h(cookies.aParam[i].zPName): "%h(cookies.aParam[i].zPValue)"
  }
  @ </ul>
  style_finish_page();
}

Changes to src/db.c.

2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
** Callback for sqlite3_trace_v2();
*/
int db_sql_trace(unsigned m, void *notUsed, void *pP, void *pX){
  sqlite3_stmt *pStmt = (sqlite3_stmt*)pP;
  char *zSql;
  int n;
  const char *zArg = (const char*)pX;
  char zEnd[40];
  if( m & SQLITE_TRACE_CLOSE ){
    /* If we are tracking closes, that means we want to clean up static
    ** prepared statements. */
    while( db.pAllStmt ){
      db_finalize(db.pAllStmt);
    }
    return 0;







|







2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
** Callback for sqlite3_trace_v2();
*/
int db_sql_trace(unsigned m, void *notUsed, void *pP, void *pX){
  sqlite3_stmt *pStmt = (sqlite3_stmt*)pP;
  char *zSql;
  int n;
  const char *zArg = (const char*)pX;
  char zEnd[100];
  if( m & SQLITE_TRACE_CLOSE ){
    /* If we are tracking closes, that means we want to clean up static
    ** prepared statements. */
    while( db.pAllStmt ){
      db_finalize(db.pAllStmt);
    }
    return 0;
3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
/*
** SETTING: lock-timeout  width=25 default=60
** This is the number of seconds that a check-in lock will be held on
** the server before the lock expires.  The default is a 60-second delay.
** Set this value to zero to disable the check-in lock mechanism.
**
** This value should be set on the server to which users auto-sync
** their work.  This setting has no affect on client repositories.  The
** check-in lock mechanism is only effective if all users are auto-syncing
** to the same server.
**
** Check-in locks are an advisory mechanism designed to help prevent
** accidental forks due to a check-in race in installations where many
** user are  committing to the same branch and auto-sync is enabled.
** As forks are harmless, there is no danger in disabling this mechanism.







|







3994
3995
3996
3997
3998
3999
4000
4001
4002
4003
4004
4005
4006
4007
4008
/*
** SETTING: lock-timeout  width=25 default=60
** This is the number of seconds that a check-in lock will be held on
** the server before the lock expires.  The default is a 60-second delay.
** Set this value to zero to disable the check-in lock mechanism.
**
** This value should be set on the server to which users auto-sync
** their work.  This setting has no effect on client repositories.  The
** check-in lock mechanism is only effective if all users are auto-syncing
** to the same server.
**
** Check-in locks are an advisory mechanism designed to help prevent
** accidental forks due to a check-in race in installations where many
** user are  committing to the same branch and auto-sync is enabled.
** As forks are harmless, there is no danger in disabling this mechanism.

Changes to src/default.css.

1259
1260
1261
1262
1263
1264
1265





1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
  position: absolute;
  display: inline-block;
  z-index: 19/*below default skin's hamburger popup*/;
  box-shadow: -0.15em 0.15em 0.2em rgba(0, 0, 0, 0.75);
  background-color: inherit;
  color: inherit;
}






.fossil-toast-message {
  /* "toast"-style popup message.
     See fossil.popupwidget:toast() */
  position: absolute;
  display: block;
  z-index: 101;
  text-align: left;
  padding: 0.15em 0.5em;
  margin: 0;
  font-size: 1em;
  border-width: 1px;
  border-style: solid;
  border-color: rgba( 127, 127, 127, 0.75 );







>
>
>
>
>
|





|







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
  position: absolute;
  display: inline-block;
  z-index: 19/*below default skin's hamburger popup*/;
  box-shadow: -0.15em 0.15em 0.2em rgba(0, 0, 0, 0.75);
  background-color: inherit;
  color: inherit;
}
.fossil-PopupWidget {
  /* This class is ALWAYS set on every fossil.PopupWidget instance, in
     addition to client/app-configured classes. It should not get any
     style - it is only used for DOM element selecting/filtering
     purposes. */
}
.fossil-toast-message {
  /* "toast"-style popup message.
     See fossil.popupwidget:toast() */
  position: absolute;
  display: block;
  z-index: 1001;
  text-align: left;
  padding: 0.15em 0.5em;
  margin: 0;
  font-size: 1em;
  border-width: 1px;
  border-style: solid;
  border-color: rgba( 127, 127, 127, 0.75 );
1459
1460
1461
1462
1463
1464
1465








































































































































































































































































































div.pikchr-wrapper.source > div.pikchr-svg {
  /* Hide image when sources are being shown. */
  position: absolute !important;
  opacity: 0 !important;
  pointer-events: none !important;
  display: none !important;
}















































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
div.pikchr-wrapper.source > div.pikchr-svg {
  /* Hide image when sources are being shown. */
  position: absolute !important;
  opacity: 0 !important;
  pointer-events: none !important;
  display: none !important;
}

/* Chat-related */
body.chat span.at-name { /* for @USERNAME references */
  text-decoration: underline;
  font-weight: bold;
}
/* A wrapper for a single single chat message (one row of the UI) */
body.chat .message-widget {
  margin-bottom: 0.75em;
  border: none;
  display: flex;
  flex-direction: column;
  border: none;
  align-items: flex-start;
}
body.chat.my-messages-right .message-widget.mine {
  /* Right-aligns a user's own chat messages, similar to how
     most mobile messaging apps do it. */
  align-items: flex-end;
}
body.chat.my-messages-right .message-widget.notification {
  /* Center-aligns a system-level notification message. */
  align-items: center;
}
/* The content area of a message. */
body.chat .message-widget-content {
  display: inline-block;
  border-radius: 0.25em;
  border: 1px solid rgba(0,0,0,0.2);
  box-shadow: 0.2em 0.2em 0.2em rgba(0, 0, 0, 0.29);
  padding: 0.25em 0.5em;
  margin-top: 0;
  min-width: 9em /*avoid unsightly "underlap" with the neighboring
                   .message-widget-tab element*/;
  white-space: pre-wrap/*needed for multi-line edits*/;
}
body.chat.monospace-messages .message-widget-content,
body.chat.monospace-messages textarea,
body.chat.monospace-messages input[type=text]{
  font-family: monospace;  
}
/* User name and timestamp (a LEGEND-like element) */
body.chat .message-widget .message-widget-tab {
  border-radius: 0.25em 0.25em 0 0;
  margin: 0 0.25em 0em 0.15em;
  padding: 0 0.5em 0.15em 0.5em;
  cursor: pointer;
  white-space: nowrap;
}
body.chat .fossil-tooltip.help-buttonlet-content {
  font-size: 80%;
}
/* The popup element for displaying message timestamps
   and deletion controls. */
body.chat .chat-message-popup {
  font-family: monospace;
  font-size: 0.8em;
  text-align: left;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  padding: 0.25em;
  z-index: 200;
}
/* Full message timestamps. */
body.chat .chat-message-popup > span { white-space: nowrap; }
/* Container for the message deletion buttons. */
body.chat .chat-message-popup > .toolbar {
  padding: 0.2em;
  margin: 0;
  border: 2px inset rgba(0,0,0,0.3);
  border-radius: 0.25em;
  display: flex;
  flex-direction: row;
  justify-content: stretch;
  flex-wrap: wrap;
}
body.chat .chat-message-popup > .toolbar > button {
  flex: 1 1 auto;
}
/* The widget for loading more/older chat messages. */
body.chat #load-msg-toolbar  {
  border-radius: 0.25em;
  padding: 0.1em 0.2em;
  margin-bottom: 1em;
}
/* .all-done is set when chat has loaded all of the available
   historical messages */
body.chat #load-msg-toolbar.all-done {
  opacity: 0.5;
}
body.chat #load-msg-toolbar > div {
  display: flex;
  flex-direction: row;
  justify-content: stretch;
  flex-wrap: wrap;
}
body.chat #load-msg-toolbar > div > button {
  flex: 1 1 auto;
}

/* An icon element intended for use as a button/menu for
   accessing app-specific settings. */
.settings-icon {
  /* Icon source: https://de.wikipedia.org/wiki/Datei:OOjs_UI_icon_settings.svg
     MIT License. */
  background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg \
xmlns='http://www.w3.org/2000/svg' width='24' height='24' \
viewBox='0, 0, 24, 24'%3e%3cg id='settings'%3e%3cpath id='gear' \
d='M3 4h3v2h-3zM12 4h9v2h-9zM8 3h2c.552 0 1 .448 1 1v2c0 .552-.448 1-1 1h-2c-.552 \
0-1-.448-1-1v-2c0-.552.448-1 1-1zM3 11h9v2h-9zM18 11h3v2h-3zM14 10h2c.552 0 1 .448 \
1 1v2c0 .552-.448 1-1 1h-2c-.552 0-1-.448-1-1v-2c0-.552.448-1 1-1zM3 18h6v2h-6zM15 \
18h6v2h-6zM11 17h2c.552 0 1 .448 1 1v2c0 .552-.448 1-1 1h-2c-.552 \
0-1-.448-1-1v-2c0-.552.448-1 1-1z'/%3e%3c/g%3e%3c/svg%3e");
  background-repeat: no-repeat;
  background-position: center;
  display: inline-block;
  min-height: 1em;
  max-height: 1em;
  min-width: 1em;
  max-width: 1em;
  margin: 0;
  padding: 0.2em/*needed to avoid image truncation*/;
  border: 1px solid rgba(0,0,0,0.0)/*avoid resize when hover style kicks in*/;
  cursor: pointer;
  border-radius: 0.25em;
}
.settings-icon:hover {
  border: 1px outset rgba(127,127,127,1);
}
body.fossil-dark-style .settings-icon {
  filter: invert(100%);
}
/* "Chat-only mode" hides the site header/footer, showing only
   the chat app. */
body.chat.chat-only-mode{}
body.chat #chat-settings-button {}
/** Popup widget for the /chat settings. */
body.chat .chat-settings-popup {
  font-size: 0.8em;
  text-align: left;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  padding: 0.25em;
  z-index: 200;
}
body.chat .chat-settings-popup > span {
  vertical-align: middle;
}
body.chat .chat-settings-popup > span.menu-entry{
  white-space: nowrap;
  cursor: pointer;
  border: 1px solid;
  border-radius: 0.25em;
  padding: 0.25em 0.5em;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
}
body.chat .chat-settings-popup > span.menu-entry:hover {
}
body.chat .chat-settings-popup > span.menu-entry > .help-buttonlet {
  vertical-align: middle;
}
body.chat .chat-settings-popup > span.menu-entry > span.button {
  margin: 0.25em 0.2em;
  padding: 0.25em;
  flex: 1 1 auto/*eliminates dead no-click zones on the right*/;
}
body.chat .chat-settings-popup > span.menu-entry > input[type=checkbox] {
  cursor: inherit;
}
body.chat .chat-settings-popup > select.menu-entry {
  flex: 1 1 auto;
  padding: 0;
  cursor: pointer;
  min-height: 2.5em;
  border-radius: 0.25em;
}
body.chat .chat-settings-popup > select.menu-entry > option {
  /*Recall that many browsers don't allow styling of OPTION
    elements, or allow only very limited styling.*/
}

/** Container for the list of /chat messages. */
body.chat #chat-messages-wrapper {
  overflow: auto;
  /*max-height: 800em*//*will be re-calc'd in JS*/;
  flex: 2 1 auto;
}
body.chat #chat-messages-wrapper.loading > * {
  /* An attempt at reducing flicker when loading lots of messages. */
  visibility: hidden;
}
body.chat div.content {
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column-reverse;
  /* ^^^^ In order to get good automatic scrolling of new messages on
     the BOTTOM in bottom-up chat mode, such that they scroll up
     instead of down, we have to use column-reverse layout, which
     changes #chat-messages-wrapper's "gravity" for purposes of
     scrolling! If we instead use flex-direction:column then each new
     message pushes #chat-input-area down further off the screen!
 */
  align-items: stretch;
}
/* Wrapper for /chat user input controls */
body.chat #chat-input-area {
  display: flex;
  flex-direction: column;
  padding: 0.5em 1em;
  border-bottom: none;
  border-top: 1px solid black;
  margin: 0.5em 1em 0 1em;
  position: initial /*sticky currently disabled due to scrolling-related issues*/;
  bottom: 0;
}
body.chat:not(.chat-only-mode) #chat-input-area{
  /* Safari user reports that 2em is necessary to keep the file selection
     widget from overlapping the page footer, whereas a margin of 0 is fine
     for FF/Chrome (and 2em is a *huge* waste of space for those). */
  margin-bottom: 0;
}

/* Widget holding the chat message input field, send button, and
   settings button. */
body.chat #chat-input-line {
  display: flex;
  flex-direction: row;
  margin-bottom: 0.25em;
  align-items: self-start;
}
body.chat #chat-input-line > input[type=submit],
body.chat #chat-input-line > #chat-settings-button,
body.chat #chat-input-line > button {
  flex: 1 5 auto;
  max-width: 6em;
  margin: 0 0.25em;
}
body.chat #chat-input-line > button {
  max-width: 4em;
}
body.chat #chat-input-line > #chat-settings-button{
  margin: 0 0 0 0.25em;
  max-width: 2em;
}
body.chat #chat-input-line > input[type=text],
body.chat #chat-input-line > textarea {
  flex: 5 1 auto;
}
/* Widget holding the file selection control and preview */
body.chat #chat-input-file-area  {
  display: flex;
  flex-direction: row;
  align-items: center;
  flex-wrap: wrap;
}
body.chat #chat-input-file-area > .file-selection-wrapper {
  align-self: flex-start;
  margin-right: 0.5em;
  flex: 0 1 auto;
  padding: 0.25em 0.25em 0.25em 0;
}
body.chat #chat-input-file-area .file-selection-wrapper > * {
  vertical-align: middle;
  margin: 0;
}
body.chat #chat-input-file {
  border:1px solid rgba(0,0,0,0);/*avoid UI shift during drop-targeting*/
  border-radius: 0.25em;
  padding: 0.25em;
}
body.chat #chat-input-file > input {
  flex: 1 0 auto;
}
/* Indicator when a drag/drop is in progress */
body.chat #chat-input-file.dragover {
  border: 1px dashed green;
}
/* Widget holding the details of a selected/dropped file/image. */
body.chat #chat-drop-details {
  flex: 0 1 auto;
  padding: 0.5em 1em;
  margin-left: 0.5em;
  white-space: pre;
  font-family: monospace;
}

body.chat #chat-drop-details img {
  max-width: 45%;
  max-height: 45%;
}

Changes to src/descendants.c.

380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
  compute_leaves(base, 0);
  db_prepare(&q,
    "%s"
    "   AND event.objid IN (SELECT rid FROM leaves)"
    " ORDER BY event.mtime DESC",
    timeline_query_for_tty()
  );
  print_timeline(&q, 0, width, 0);
  db_finalize(&q);
}

/*
** COMMAND: leaves*
**
** Usage: %fossil leaves ?OPTIONS?







|







380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
  compute_leaves(base, 0);
  db_prepare(&q,
    "%s"
    "   AND event.objid IN (SELECT rid FROM leaves)"
    " ORDER BY event.mtime DESC",
    timeline_query_for_tty()
  );
  print_timeline(&q, 0, width, 0, 0);
  db_finalize(&q);
}

/*
** COMMAND: leaves*
**
** Usage: %fossil leaves ?OPTIONS?
559
560
561
562
563
564
565

566
567
568
569
570
571
572
  if( !showClosed ){
    style_submenu_element("Closed", "%s", url_render(&url, "closed", "", 0, 0));
  }
  if( showClosed || showAll ){
    style_submenu_element("Open", "%s", url_render(&url, 0, 0, 0, 0));
  }
  url_reset(&url);

  style_header("Leaves");
  login_anonymous_available();
  timeline_ss_submenu();
  cookie_render();
#if 0
  style_sidebox_begin("Nomenclature:", "33%");
  @ <ol>







>







559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
  if( !showClosed ){
    style_submenu_element("Closed", "%s", url_render(&url, "closed", "", 0, 0));
  }
  if( showClosed || showAll ){
    style_submenu_element("Open", "%s", url_render(&url, 0, 0, 0, 0));
  }
  url_reset(&url);
  style_set_current_feature("leaves");
  style_header("Leaves");
  login_anonymous_available();
  timeline_ss_submenu();
  cookie_render();
#if 0
  style_sidebox_begin("Nomenclature:", "33%");
  @ <ol>
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
  tmFlags = TIMELINE_LEAFONLY | TIMELINE_DISJOINT | TIMELINE_NOSCROLL;
  if( fNg==0 ) tmFlags |= TIMELINE_GRAPH;
  if( fBrBg ) tmFlags |= TIMELINE_BRCOLOR;
  if( fUBg ) tmFlags |= TIMELINE_UCOLOR;
  www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0);
  db_finalize(&q);
  @ <br />
  style_finish_page("leaves");
}

#if INTERFACE
/* Flag parameters to compute_uses_file() */
#define USESFILE_DELETE   0x01  /* Include the check-ins where file deleted */

#endif







|







612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
  tmFlags = TIMELINE_LEAFONLY | TIMELINE_DISJOINT | TIMELINE_NOSCROLL;
  if( fNg==0 ) tmFlags |= TIMELINE_GRAPH;
  if( fBrBg ) tmFlags |= TIMELINE_BRCOLOR;
  if( fUBg ) tmFlags |= TIMELINE_UCOLOR;
  www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0);
  db_finalize(&q);
  @ <br />
  style_finish_page();
}

#if INTERFACE
/* Flag parameters to compute_uses_file() */
#define USESFILE_DELETE   0x01  /* Include the check-ins where file deleted */

#endif

Changes to src/diff.c.

1953
1954
1955
1956
1957
1958
1959




1960

1961
1962
1963
1964
1965
1966
1967
  if( pOut ){
    if( diffFlags & DIFF_NUMSTAT ){
      int nDel = 0, nIns = 0, i;
      for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
        nDel += c.aEdit[i+1];
        nIns += c.aEdit[i+2];
      }




      blob_appendf(pOut, "%10d %10d", nIns, nDel);

    }else if( diffFlags & DIFF_SIDEBYSIDE ){
      sbsDiff(&c, pOut, pRe, diffFlags);
    }else{
      contextDiff(&c, pOut, pRe, diffFlags);
    }
    fossil_free(c.aFrom);
    fossil_free(c.aTo);







>
>
>
>
|
>







1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
  if( pOut ){
    if( diffFlags & DIFF_NUMSTAT ){
      int nDel = 0, nIns = 0, i;
      for(i=0; c.aEdit[i] || c.aEdit[i+1] || c.aEdit[i+2]; i+=3){
        nDel += c.aEdit[i+1];
        nIns += c.aEdit[i+2];
      }
      g.diffCnt[1] += nIns;
      g.diffCnt[2] += nDel;
      if( nIns+nDel ){
        g.diffCnt[0]++;
        blob_appendf(pOut, "%10d %10d", nIns, nDel);
      }
    }else if( diffFlags & DIFF_SIDEBYSIDE ){
      sbsDiff(&c, pOut, pRe, diffFlags);
    }else{
      contextDiff(&c, pOut, pRe, diffFlags);
    }
    fossil_free(c.aFrom);
    fossil_free(c.aTo);
2446
2447
2448
2449
2450
2451
2452

2453
2454
2455
2456
2457
2458
2459
  if( ignoreWs ) annFlags |= DIFF_IGNORE_ALLWS;

  /* compute the annotation */
  annotate_file(&ann, zFilename, zRevision, zLimit, zOrigin, annFlags);
  zCI = ann.aVers[0].zMUuid;

  /* generate the web page */

  style_header("Annotation For %h", zFilename);
  if( bBlame ){
    url_initialize(&url, "blame");
  }else{
    url_initialize(&url, "annotate");
  }
  url_add_parameter(&url, "checkin", P("checkin"));







>







2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
  if( ignoreWs ) annFlags |= DIFF_IGNORE_ALLWS;

  /* compute the annotation */
  annotate_file(&ann, zFilename, zRevision, zLimit, zOrigin, annFlags);
  zCI = ann.aVers[0].zMUuid;

  /* generate the web page */
  style_set_current_feature("annotate");
  style_header("Annotation For %h", zFilename);
  if( bBlame ){
    url_initialize(&url, "blame");
  }else{
    url_initialize(&url, "annotate");
  }
  url_add_parameter(&url, "checkin", P("checkin"));
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
        sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%*s%4d:",szHash+12,"",i+1);
      }
    }
    @ %s(zPrefix) %h(z)

  }
  @ </pre>
  style_finish_page("annotate");
}

/*
** COMMAND: annotate
** COMMAND: blame
** COMMAND: praise*
**







|







2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
        sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%*s%4d:",szHash+12,"",i+1);
      }
    }
    @ %s(zPrefix) %h(z)

  }
  @ </pre>
  style_finish_page();
}

/*
** COMMAND: annotate
** COMMAND: blame
** COMMAND: praise*
**

Changes to src/diffcmd.c.

885
886
887
888
889
890
891

892
893
894
895
896
897
898
    }
    zTo = zBranch;
    zFrom = mprintf("root:%s", zBranch);
  }
  if( zCheckin!=0 && ( zFrom!=0 || zTo!=0 ) ){
    fossil_fatal("cannot use --checkin together with --from or --to");
  }

  if( zTo==0 || againstUndo ){
    db_must_be_within_tree();
  }else if( zFrom==0 ){
    fossil_fatal("must use --from if --to is present");
  }else{
    db_find_and_open_repository(0, 0);
  }







>







885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
    }
    zTo = zBranch;
    zFrom = mprintf("root:%s", zBranch);
  }
  if( zCheckin!=0 && ( zFrom!=0 || zTo!=0 ) ){
    fossil_fatal("cannot use --checkin together with --from or --to");
  }
  g.diffCnt[0] = g.diffCnt[1] = g.diffCnt[2] = 0;
  if( zTo==0 || againstUndo ){
    db_must_be_within_tree();
  }else if( zFrom==0 ){
    fossil_fatal("must use --from if --to is present");
  }else{
    db_find_and_open_repository(0, 0);
  }
956
957
958
959
960
961
962




963
964
965
966
967
968
969
      ){
        fossil_fatal("not found: '%s'", g.argv[i+2]);
      }
      fossil_free(pFileDir[i].zName);
    }
    fossil_free(pFileDir);
  }




}

/*
** WEBPAGE: vpatch
** URL: /vpatch?from=FROM&to=TO
**
** Show a patch that goes from check-in FROM to check-in TO.







>
>
>
>







957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
      ){
        fossil_fatal("not found: '%s'", g.argv[i+2]);
      }
      fossil_free(pFileDir[i].zName);
    }
    fossil_free(pFileDir);
  }
  if ( diffFlags & DIFF_NUMSTAT ){
    fossil_print("%10d %10d TOTAL over %d changed files\n", 
                 g.diffCnt[1], g.diffCnt[2], g.diffCnt[0]);
  }
}

/*
** WEBPAGE: vpatch
** URL: /vpatch?from=FROM&to=TO
**
** Show a patch that goes from check-in FROM to check-in TO.

Changes to src/dispatch.c.

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
**   *  Display lists are indented from the surrounding text.
**      Each tag begins with "-" or occur on a line that is
**      followed by two spaces and a non-space.  <dd> elements can begin
**      on the same line as long as they are separated by at least
**      two spaces.
**
**   *  Indented text is show verbatim (<pre>...</pre>)


*/
static void help_to_html(const char *zHelp, Blob *pHtml){
  int i;
  char c;
  int nIndent = 0;
  int wantP = 0;
  int wantBR = 0;
  int aIndent[10];
  const char *azEnd[10];
  int iLevel = 0;
  int isLI = 0;
  int isDT = 0;

  static const char *zEndDL = "</dl></blockquote>";
  static const char *zEndPRE = "</pre></blockquote>";
  static const char *zEndUL = "</ul>";
  static const char *zEndDD = "</dd>";

  aIndent[0] = 0;
  azEnd[0] = "";
  while( zHelp[0] ){
    i = 0;
    while( (c = zHelp[i])!=0 && c!='\n' ){
      if( c=='%' && i>2 && zHelp[i-2]==':' && strncmp(zHelp+i,"%fossil",7)==0 ){
        appendLinked(pHtml, zHelp, i);
        zHelp += i+1;
        i = 0;
        wantBR = 1;
        continue;
      }
      i++;
    }

    if( i>2 && zHelp[0]=='>' && zHelp[1]==' ' ){
      isDT = 1;
      for(nIndent=1; nIndent<i && zHelp[nIndent]==' '; nIndent++){}
    }else{










      isDT = 0;
      for(nIndent=0; nIndent<i && zHelp[nIndent]==' '; nIndent++){}





    }
    if( nIndent==i ){
      if( c==0 ) break;
      if( iLevel && azEnd[iLevel]==zEndPRE ){
        /* Skip the newline at the end of a <pre> */
      }else{
        blob_append_char(pHtml, '\n');







>
>












>



















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


>
>
>
>
>







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
**   *  Display lists are indented from the surrounding text.
**      Each tag begins with "-" or occur on a line that is
**      followed by two spaces and a non-space.  <dd> elements can begin
**      on the same line as long as they are separated by at least
**      two spaces.
**
**   *  Indented text is show verbatim (<pre>...</pre>)
**
**   *  Lines that begin with "|" at the left margin are in <pre>...</pre>
*/
static void help_to_html(const char *zHelp, Blob *pHtml){
  int i;
  char c;
  int nIndent = 0;
  int wantP = 0;
  int wantBR = 0;
  int aIndent[10];
  const char *azEnd[10];
  int iLevel = 0;
  int isLI = 0;
  int isDT = 0;
  int inPRE = 0;
  static const char *zEndDL = "</dl></blockquote>";
  static const char *zEndPRE = "</pre></blockquote>";
  static const char *zEndUL = "</ul>";
  static const char *zEndDD = "</dd>";

  aIndent[0] = 0;
  azEnd[0] = "";
  while( zHelp[0] ){
    i = 0;
    while( (c = zHelp[i])!=0 && c!='\n' ){
      if( c=='%' && i>2 && zHelp[i-2]==':' && strncmp(zHelp+i,"%fossil",7)==0 ){
        appendLinked(pHtml, zHelp, i);
        zHelp += i+1;
        i = 0;
        wantBR = 1;
        continue;
      }
      i++;
    }
    if( i>2 && (zHelp[0]=='>' || zHelp[0]=='|') && zHelp[1]==' ' ){
      if( zHelp[0]=='>' ){
        isDT = 1;
        for(nIndent=1; nIndent<i && zHelp[nIndent]==' '; nIndent++){}
      }else{
        if( !inPRE ){
          blob_append(pHtml, "<pre>\n", -1);
          inPRE = 1;
        }
      }
    }else{
      if( inPRE ){
        blob_append(pHtml, "</pre>\n", -1);
        inPRE = 0;
      }
      isDT = 0;
      for(nIndent=0; nIndent<i && zHelp[nIndent]==' '; nIndent++){}
    }
    if( inPRE ){
      blob_append(pHtml, zHelp+1, i);
      zHelp += i + 1;
      continue;
    }
    if( nIndent==i ){
      if( c==0 ) break;
      if( iLevel && azEnd[iLevel]==zEndPRE ){
        /* Skip the newline at the end of a <pre> */
      }else{
        blob_append_char(pHtml, '\n');
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
    if( c=='%' && strncmp(zHelp+i,"%fossil",7)==0 ){
      if( i>0 ) blob_append(pText, zHelp, i);
      blob_append(pText, "fossil", 6);
      zHelp += i+7;
      i = -1;
      continue;
    }
    if( c=='\n' && strncmp(zHelp+i+1,"> ",2)==0 ){
      blob_append(pText, zHelp, i+1);
      blob_append(pText, " ", 1);
      zHelp += i+2;
      i = -1;
      continue;
    }
    if( c=='[' && (x = help_is_link(zHelp+i, 100000))!=0 ){







|







508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
    if( c=='%' && strncmp(zHelp+i,"%fossil",7)==0 ){
      if( i>0 ) blob_append(pText, zHelp, i);
      blob_append(pText, "fossil", 6);
      zHelp += i+7;
      i = -1;
      continue;
    }
    if( c=='\n' && (zHelp[i+1]=='>' || zHelp[i+1]=='|') && zHelp[i+2]==' ' ){
      blob_append(pText, zHelp, i+1);
      blob_append(pText, " ", 1);
      zHelp += i+2;
      i = -1;
      continue;
    }
    if( c=='[' && (x = help_is_link(zHelp+i, 100000))!=0 ){
745
746
747
748
749
750
751

752
753
754
755
756
757
758
  const char *zCmd = P("cmd");

  if( zCmd==0 ) zCmd = P("name");
  if( zCmd && *zCmd ){
    int rc;
    const CmdOrPage *pCmd = 0;


    style_header("Help: %s", zCmd);

    style_submenu_element("Command-List", "%R/help");
    rc = dispatch_name_search(zCmd, CMDFLAG_ANY|CMDFLAG_PREFIX, &pCmd);
    if( *zCmd=='/' ){
      /* Some of the webpages require query parameters in order to work.
      ** @ <h1>The "<a href='%R%s(zCmd)'>%s(zCmd)</a>" page:</h1> */







>







764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
  const char *zCmd = P("cmd");

  if( zCmd==0 ) zCmd = P("name");
  if( zCmd && *zCmd ){
    int rc;
    const CmdOrPage *pCmd = 0;

  style_set_current_feature("tkt");
    style_header("Help: %s", zCmd);

    style_submenu_element("Command-List", "%R/help");
    rc = dispatch_name_search(zCmd, CMDFLAG_ANY|CMDFLAG_PREFIX, &pCmd);
    if( *zCmd=='/' ){
      /* Some of the webpages require query parameters in order to work.
      ** @ <h1>The "<a href='%R%s(zCmd)'>%s(zCmd)</a>" page:</h1> */
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866

867
868
869
870
871
872
873
      }else{
        @ <li>%s(z)</li>
      }
    }
    @ </ul></div>

  }
  style_finish_page("help");
}

/*
** WEBPAGE: test-all-help
**
** Show all help text on a single page.  Useful for proof-reading.
*/
void test_all_help_page(void){
  int i;
  Blob buf;
  blob_init(&buf,0,0);

  style_header("All Help Text");
  @ <dl>
  for(i=0; i<MX_COMMAND; i++){
    const char *zDesc;
    unsigned int e = aCommand[i].eCmdFlags;
    if( e & CMDFLAG_1ST_TIER ){
      zDesc = "1st tier command";







|











>







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
      }else{
        @ <li>%s(z)</li>
      }
    }
    @ </ul></div>

  }
  style_finish_page();
}

/*
** WEBPAGE: test-all-help
**
** Show all help text on a single page.  Useful for proof-reading.
*/
void test_all_help_page(void){
  int i;
  Blob buf;
  blob_init(&buf,0,0);
  style_set_current_feature("test");
  style_header("All Help Text");
  @ <dl>
  for(i=0; i<MX_COMMAND; i++){
    const char *zDesc;
    unsigned int e = aCommand[i].eCmdFlags;
    if( e & CMDFLAG_1ST_TIER ){
      zDesc = "1st tier command";
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
    @ <dt><big><b>%s(aCommand[i].zName)</b></big> (%s(zDesc))</dt>
    @ <dd>
    help_to_html(aCommand[i].zHelp, cgi_output_blob());
    @ </dd>
  }
  @ </dl>
  blob_reset(&buf);
  style_finish_page("help");
}

static void multi_column_list(const char **azWord, int nWord){
  int i, j, len;
  int mxLen = 0;
  int nCol;
  int nRow;







|







920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
    @ <dt><big><b>%s(aCommand[i].zName)</b></big> (%s(zDesc))</dt>
    @ <dd>
    help_to_html(aCommand[i].zHelp, cgi_output_blob());
    @ </dd>
  }
  @ </dl>
  blob_reset(&buf);
  style_finish_page();
}

static void multi_column_list(const char **azWord, int nWord){
  int i, j, len;
  int mxLen = 0;
  int nCol;
  int nRow;

Changes to src/doc.c.

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
  int i;
  int n;
  const unsigned char *x;

  /* A table of mimetypes based on file content prefixes
  */
  static const struct {
    const char *zPrefix;       /* The file prefix */
    const int size;            /* Length of the prefix */



    const char *zMimetype;     /* The corresponding mimetype */
  } aMime[] = {
    { "GIF87a",                  6, "image/gif"  },
    { "GIF89a",                  6, "image/gif"  },
    { "\211PNG\r\n\032\n",       8, "image/png"  },
    { "\377\332\377",            3, "image/jpeg" },
    { "\377\330\377",            3, "image/jpeg" },

  };

  if( !looks_like_binary(pBlob) ) {
    return 0;   /* Plain text */
  }
  x = (const unsigned char*)blob_buffer(pBlob);
  n = blob_size(pBlob);
  for(i=0; i<count(aMime); i++){

    if( n>=aMime[i].size && memcmp(x, aMime[i].zPrefix, aMime[i].size)==0 ){
      return aMime[i].zMimetype;



    }

  }
  return "unknown/unknown";
}

/* A table of mimetypes based on file suffixes.
** Suffixes must be in sorted order so that we can do a binary
** search to find the mime-type







|
|
>
>
>


|
|
|
|
|
>








>
|
|
>
>
>

>







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
  int i;
  int n;
  const unsigned char *x;

  /* A table of mimetypes based on file content prefixes
  */
  static const struct {
    const char *z;             /* Identifying file text */
    const unsigned char sz1;   /* Length of the prefix */
    const unsigned char of2;   /* Offset to the second segment */
    const unsigned char sz2;   /* Size of the second segment */
    const unsigned char mn;    /* Minimum size of input */
    const char *zMimetype;     /* The corresponding mimetype */
  } aMime[] = {
    { "GIF87a",                  6, 0, 0, 6,  "image/gif"  },
    { "GIF89a",                  6, 0, 0, 6,  "image/gif"  },
    { "\211PNG\r\n\032\n",       8, 0, 0, 8,  "image/png"  },
    { "\377\332\377",            3, 0, 0, 3,  "image/jpeg" },
    { "\377\330\377",            3, 0, 0, 3,  "image/jpeg" },
    { "RIFFWAVEfmt",             4, 8, 7, 15, "sound/wav"  },
  };

  if( !looks_like_binary(pBlob) ) {
    return 0;   /* Plain text */
  }
  x = (const unsigned char*)blob_buffer(pBlob);
  n = blob_size(pBlob);
  for(i=0; i<count(aMime); i++){
    if( n<aMime[i].mn ) continue;
    if( memcmp(x, aMime[i].z, aMime[i].sz1)!=0 ) continue;
    if( aMime[i].sz2
     && memcmp(x+aMime[i].of2, aMime[i].z+aMime[i].sz1, aMime[i].sz2)!=0
    ){
      continue;
    }
    return aMime[i].zMimetype;
  }
  return "unknown/unknown";
}

/* A table of mimetypes based on file suffixes.
** Suffixes must be in sorted order so that we can do a binary
** search to find the mime-type
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
       mimetype_from_name_custom(aMime[i].zSuffix)!=0){
      zFlag = "<em><strong>!</strong></em> ";
    }
    @ <tr><td>%s(zFlag)%h(aMime[i].zSuffix)<td>%h(aMime[i].zMimetype)</tr>
  }
  @ </tbody></table>
  style_table_sorter();
  style_finish_page("mimetypes");
}

/*
** Check to see if the file in the pContent blob is "embedded HTML".  Return
** true if it is, and fill pTitle with the document title.
**
** An "embedded HTML" file is HTML that lacks a header and a footer.  The







|







562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
       mimetype_from_name_custom(aMime[i].zSuffix)!=0){
      zFlag = "<em><strong>!</strong></em> ";
    }
    @ <tr><td>%s(zFlag)%h(aMime[i].zSuffix)<td>%h(aMime[i].zMimetype)</tr>
  }
  @ </tbody></table>
  style_table_sorter();
  style_finish_page();
}

/*
** Check to see if the file in the pContent blob is "embedded HTML".  Return
** true if it is, and fill pTitle with the document title.
**
** An "embedded HTML" file is HTML that lacks a header and a footer.  The
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
      style_header("%s", blob_str(&title));
      wiki_convert(&tail, 0, WIKI_BUTTONS);
    }else{
      style_header("%s", zDefaultTitle);
      wiki_convert(pBody, 0, WIKI_BUTTONS);
    }
    document_emit_js();
    style_finish_page("doc");
  }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
    Blob tail = BLOB_INITIALIZER;
    markdown_to_html(pBody, &title, &tail);
    if( blob_size(&title)>0 ){
      style_header("%s", blob_str(&title));
    }else{
      style_header("%s", zDefaultTitle);
    }
    convert_href_and_output(&tail);
    document_emit_js();
    style_finish_page("doc");
  }else if( fossil_strcmp(zMime, "text/plain")==0 ){
    style_header("%s", zDefaultTitle);
    @ <blockquote><pre>
    @ %h(blob_str(pBody))
    @ </pre></blockquote>
    document_emit_js();
    style_finish_page("doc");
  }else if( fossil_strcmp(zMime, "text/html")==0
            && doc_is_embedded_html(pBody, &title) ){
    if( blob_size(&title)==0 ) blob_append(&title,zFilename,-1);
    style_header("%s", blob_str(&title));
    convert_href_and_output(pBody);
    document_emit_js();
    style_finish_page("doc");
  }else if( fossil_strcmp(zMime, "text/x-pikchr")==0 ){
    style_adunit_config(ADUNIT_RIGHT_OK);
    style_header("%s", zDefaultTitle);
    wiki_render_by_mimetype(pBody, zMime);
    style_finish_page("doc");
#ifdef FOSSIL_ENABLE_TH1_DOCS
  }else if( Th_AreDocsEnabled() &&
            fossil_strcmp(zMime, "application/x-th1")==0 ){
    int raw = P("raw")!=0;
    if( !raw ){
      Blob tail;
      blob_zero(&tail);
      if( wiki_find_title(pBody, &title, &tail) ){
        style_header("%s", blob_str(&title));
        Th_Render(blob_str(&tail));
        blob_reset(&tail);
      }else{
        style_header("%h", zFilename);
        Th_Render(blob_str(pBody));
      }
    }else{
      Th_Render(blob_str(pBody));
    }
    if( !raw ){
      document_emit_js();
      style_finish_page("doc");
    }
#endif
  }else{
    fossil_free(style_csp(1));
    cgi_set_content_type(zMime);
    cgi_set_content(pBody);
  }







|










|






|






|




|




















|







777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
      style_header("%s", blob_str(&title));
      wiki_convert(&tail, 0, WIKI_BUTTONS);
    }else{
      style_header("%s", zDefaultTitle);
      wiki_convert(pBody, 0, WIKI_BUTTONS);
    }
    document_emit_js();
    style_finish_page();
  }else if( fossil_strcmp(zMime, "text/x-markdown")==0 ){
    Blob tail = BLOB_INITIALIZER;
    markdown_to_html(pBody, &title, &tail);
    if( blob_size(&title)>0 ){
      style_header("%s", blob_str(&title));
    }else{
      style_header("%s", zDefaultTitle);
    }
    convert_href_and_output(&tail);
    document_emit_js();
    style_finish_page();
  }else if( fossil_strcmp(zMime, "text/plain")==0 ){
    style_header("%s", zDefaultTitle);
    @ <blockquote><pre>
    @ %h(blob_str(pBody))
    @ </pre></blockquote>
    document_emit_js();
    style_finish_page();
  }else if( fossil_strcmp(zMime, "text/html")==0
            && doc_is_embedded_html(pBody, &title) ){
    if( blob_size(&title)==0 ) blob_append(&title,zFilename,-1);
    style_header("%s", blob_str(&title));
    convert_href_and_output(pBody);
    document_emit_js();
    style_finish_page();
  }else if( fossil_strcmp(zMime, "text/x-pikchr")==0 ){
    style_adunit_config(ADUNIT_RIGHT_OK);
    style_header("%s", zDefaultTitle);
    wiki_render_by_mimetype(pBody, zMime);
    style_finish_page();
#ifdef FOSSIL_ENABLE_TH1_DOCS
  }else if( Th_AreDocsEnabled() &&
            fossil_strcmp(zMime, "application/x-th1")==0 ){
    int raw = P("raw")!=0;
    if( !raw ){
      Blob tail;
      blob_zero(&tail);
      if( wiki_find_title(pBody, &title, &tail) ){
        style_header("%s", blob_str(&title));
        Th_Render(blob_str(&tail));
        blob_reset(&tail);
      }else{
        style_header("%h", zFilename);
        Th_Render(blob_str(pBody));
      }
    }else{
      Th_Render(blob_str(pBody));
    }
    if( !raw ){
      document_emit_js();
      style_finish_page();
    }
#endif
  }else{
    fossil_free(style_csp(1));
    cgi_set_content_type(zMime);
    cgi_set_content(pBody);
  }
904
905
906
907
908
909
910

911
912
913
914
915
916
917
#ifdef FOSSIL_ENABLE_TH1_DOCS
      , "index.th1"
#endif
  };

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }

  blob_init(&title, 0, 0);
  zDfltTitle = isUV ? "" : "Documentation";
  db_begin_transaction();
  while( rid==0 && (++nMiss)<=count(azSuffix) ){
    zName = P("name");
    if( isUV ){
      if( zName==0 ) zName = "index.wiki";







>







913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
#ifdef FOSSIL_ENABLE_TH1_DOCS
      , "index.th1"
#endif
  };

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  style_set_current_feature("doc");
  blob_init(&title, 0, 0);
  zDfltTitle = isUV ? "" : "Documentation";
  db_begin_transaction();
  while( rid==0 && (++nMiss)<=count(azSuffix) ){
    zName = P("name");
    if( isUV ){
      if( zName==0 ) zName = "index.wiki";
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
  }
  cgi_set_status(404, "Not Found");
  style_header("Not Found");
  @ <p>Document %h(zOrigName) not found
  if( fossil_strcmp(zCheckin,"ckout")!=0 ){
    @ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a>
  }
  style_finish_page("doc");
  return;
}

/*
** The default logo.
*/
static const unsigned char aLogo[] = {







|







1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
  }
  cgi_set_status(404, "Not Found");
  style_header("Not Found");
  @ <p>Document %h(zOrigName) not found
  if( fossil_strcmp(zCheckin,"ckout")!=0 ){
    @ in %z(href("%R/tree?ci=%T",zCheckin))%h(zCheckin)</a>
  }
  style_finish_page();
  return;
}

/*
** The default logo.
*/
static const unsigned char aLogo[] = {
1200
1201
1202
1203
1204
1205
1206
1207
1208
**
**     s=PATTERN             Search for PATTERN
*/
void doc_search_page(void){
  login_check_credentials();
  style_header("Document Search");
  search_screen(SRCH_DOC, 0);
  style_finish_page("docsrch");
}







|

1210
1211
1212
1213
1214
1215
1216
1217
1218
**
**     s=PATTERN             Search for PATTERN
*/
void doc_search_page(void){
  login_check_credentials();
  style_header("Document Search");
  search_screen(SRCH_DOC, 0);
  style_finish_page();
}

Changes to src/encode.c.

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
                                 int * nOut){
  const unsigned char *z;
  char *zOut;
  u32 c;
  int n, i, j;
  z = (const unsigned char*)zStr;
  n = 0;
  while( (c = fossil_utf8_read(&z))!=0 ){
    if( c=='\\' || c=='"' ){
      n += 2;
    }else if( c<' ' || c>=0x7f ){
      if( c=='\n' || c=='\r' ){
        n += 2;
      }else{
        n += 6;
      }
    }else{
      n++;
    }
  }
  if(fAddQuotes){
    n += 2;
  }
  zOut = fossil_malloc(n+1);
  if( zOut==0 ) return 0;
  z = (const unsigned char*)zStr;
  i = 0;
  if(fAddQuotes){
    zOut[i++] = '"';
  }
  while( (c = fossil_utf8_read(&z))!=0 ){
    if( c=='\\' || c=='"' ){
      zOut[i++] = '\\';
      zOut[i++] = c;
    }else if( c<' ' || c>=0x7f ){
      zOut[i++] = '\\';
      if( c=='\n' ){
        zOut[i++] = 'n';
      }else if( c=='\r' ){
        zOut[i++] = 'r';
      }else{
        zOut[i++] = 'u';







|


|



















|



|







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
                                 int * nOut){
  const unsigned char *z;
  char *zOut;
  u32 c;
  int n, i, j;
  z = (const unsigned char*)zStr;
  n = 0;
  while( (c = *(z++))!=0 ){
    if( c=='\\' || c=='"' ){
      n += 2;
    }else if( c<' ' ){
      if( c=='\n' || c=='\r' ){
        n += 2;
      }else{
        n += 6;
      }
    }else{
      n++;
    }
  }
  if(fAddQuotes){
    n += 2;
  }
  zOut = fossil_malloc(n+1);
  if( zOut==0 ) return 0;
  z = (const unsigned char*)zStr;
  i = 0;
  if(fAddQuotes){
    zOut[i++] = '"';
  }
  while( (c = *(z++))!=0 ){
    if( c=='\\' || c=='"' ){
      zOut[i++] = '\\';
      zOut[i++] = c;
    }else if( c<' ' ){
      zOut[i++] = '\\';
      if( c=='\n' ){
        zOut[i++] = 'n';
      }else if( c=='\r' ){
        zOut[i++] = 'r';
      }else{
        zOut[i++] = 'u';

Changes to src/event.c.

108
109
110
111
112
113
114

115
116
117
118
119
120
121
122
123
124
125
      if( db_step(&q1)==SQLITE_ROW ){
        prevRid = db_column_int(&q1, 0);
      }
      break;
    }
  }
  db_finalize(&q1);

  if( rid==0 || (specRid!=0 && specRid!=rid) ){
    style_header("No Such Tech-Note");
    @ Cannot locate a technical note called <b>%h(zId)</b>.
    style_finish_page("event");
    return;
  }
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
  zVerbose = P("v");
  if( !zVerbose ){
    zVerbose = P("verbose");
  }







>



|







108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
      if( db_step(&q1)==SQLITE_ROW ){
        prevRid = db_column_int(&q1, 0);
      }
      break;
    }
  }
  db_finalize(&q1);
  style_set_current_feature("event");
  if( rid==0 || (specRid!=0 && specRid!=rid) ){
    style_header("No Such Tech-Note");
    @ Cannot locate a technical note called <b>%h(zId)</b>.
    style_finish_page();
    return;
  }
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
  zVerbose = P("v");
  if( !zVerbose ){
    zVerbose = P("verbose");
  }
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
  }
  zFullId = db_text(0, "SELECT SUBSTR(tagname,7)"
                       "  FROM tag"
                       " WHERE tagname GLOB 'event-%q*'",
                    zId);
  attachment_list(zFullId, "<hr /><h2>Attachments:</h2><ul>");
  document_emit_js();
  style_finish_page("event");
  manifest_destroy(pTNote);
}

/*
** Add or update a new tech note to the repository.  rid is id of
** the prior version of this technote, if any.
**







|







228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
  }
  zFullId = db_text(0, "SELECT SUBSTR(tagname,7)"
                       "  FROM tag"
                       " WHERE tagname GLOB 'event-%q*'",
                    zId);
  attachment_list(zFullId, "<hr /><h2>Attachments:</h2><ul>");
  document_emit_js();
  style_finish_page();
  manifest_destroy(pTNote);
}

/*
** Add or update a new tech note to the repository.  rid is id of
** the prior version of this technote, if any.
**
413
414
415
416
417
418
419

420
421
422
423
424
425
426
  /* Need both check-in and wiki-write or wiki-create privileges in order
  ** to edit/create an event.
  */
  if( !g.perm.Write || (rid && !g.perm.WrWiki) || (!rid && !g.perm.NewWiki) ){
    login_needed(g.anon.Write && (rid ? g.anon.WrWiki : g.anon.NewWiki));
    return;
  }


  /* Figure out the color */
  if( rid ){
    zClr = db_text("", "SELECT bgcolor FROM event WHERE objid=%d", rid);
    if( zClr && zClr[0] ){
      const char * zRequestMethod = P("REQUEST_METHOD");
      if(zRequestMethod && 'G'==zRequestMethod[0]){







>







414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
  /* Need both check-in and wiki-write or wiki-create privileges in order
  ** to edit/create an event.
  */
  if( !g.perm.Write || (rid && !g.perm.WrWiki) || (!rid && !g.perm.NewWiki) ){
    login_needed(g.anon.Write && (rid ? g.anon.WrWiki : g.anon.NewWiki));
    return;
  }
  style_set_current_feature("event");

  /* Figure out the color */
  if( rid ){
    zClr = db_text("", "SELECT bgcolor FROM event WHERE objid=%d", rid);
    if( zClr && zClr[0] ){
      const char * zRequestMethod = P("REQUEST_METHOD");
      if(zRequestMethod && 'G'==zRequestMethod[0]){
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
    login_verify_csrf_secret();
    if ( !event_commit_common(rid, zId, zBody, zETime,
                              zMimetype, zComment, zTags,
                              zClrFlag[0] ? zClr : 0) ){
      style_header("Error");
      @ Internal error:  Fossil tried to make an invalid artifact for
      @ the edited technote.
      style_finish_page("event");
      return;
    }
    cgi_redirectf("%R/technote?name=%T", zId);
  }
  if( P("cancel")!=0 ){
    cgi_redirectf("%R/technote?name=%T", zId);
    return;







|







473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
    login_verify_csrf_secret();
    if ( !event_commit_common(rid, zId, zBody, zETime,
                              zMimetype, zComment, zTags,
                              zClrFlag[0] ? zClr : 0) ){
      style_header("Error");
      @ Internal error:  Fossil tried to make an invalid artifact for
      @ the edited technote.
      style_finish_page();
      return;
    }
    cgi_redirectf("%R/technote?name=%T", zId);
  }
  if( P("cancel")!=0 ){
    cgi_redirectf("%R/technote?name=%T", zId);
    return;
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
  @ <input type="submit" name="cancel" value="Cancel" />
  @ <input type="submit" name="preview" value="Preview" />
  if( P("preview") ){
    @ <input type="submit" name="submit" value="Submit" />
  }
  @ </td></tr></table>
  @ </div></form>
  style_finish_page("event");
}

/*
** Add a new tech note to the repository.  The timestamp is
** given by the zETime parameter.  rid must be zero to create
** a new page.  If no previous page with the name zPageName exists
** and isNew is false, then this routine throws an error.







|







567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
  @ <input type="submit" name="cancel" value="Cancel" />
  @ <input type="submit" name="preview" value="Preview" />
  if( P("preview") ){
    @ <input type="submit" name="submit" value="Submit" />
  }
  @ </td></tr></table>
  @ </div></form>
  style_finish_page();
}

/*
** Add a new tech note to the repository.  The timestamp is
** given by the zETime parameter.  rid must be zero to create
** a new page.  If no previous page with the name zPageName exists
** and isNew is false, then this routine throws an error.

Changes to src/export.c.

860
861
862
863
864
865
866





867
868
869
870
871
872
873
**    3     Extra details
*/
#define VERB_ERROR  1
#define VERB_NORMAL 2
#define VERB_EXTRA  3
static int gitmirror_verbosity = VERB_NORMAL;






/*
** Output routine that depends on verbosity
*/
static void gitmirror_message(int iLevel, const char *zFormat, ...){
  va_list ap;
  if( iLevel>gitmirror_verbosity ) return;
  va_start(ap, zFormat);







>
>
>
>
>







860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
**    3     Extra details
*/
#define VERB_ERROR  1
#define VERB_NORMAL 2
#define VERB_EXTRA  3
static int gitmirror_verbosity = VERB_NORMAL;

/* The main branch in the Git repository.  The "trunk" branch of
** Fossil is renamed to be this branch name.
*/
static const char *gitmirror_mainbranch = 0;

/*
** Output routine that depends on verbosity
*/
static void gitmirror_message(int iLevel, const char *zFormat, ...){
  va_list ap;
  if( iLevel>gitmirror_verbosity ) return;
  va_start(ap, zFormat);
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

  /* If some required files could not be exported, abandon the check-in
  ** export */
  if( nErr ){
    gitmirror_message(VERB_ERROR,
             "export of %s abandoned due to missing files\n", zUuid);
    *pnLimit = 0;

    return 1;
  }

  /* Figure out which branch this check-in is a member of */
  zBranch = db_text(0,
    "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=%d",
    TAG_BRANCH, rid
  );
  if( fossil_strcmp(zBranch,"trunk")==0 ){

    fossil_free(zBranch);
    zBranch = mprintf("master");
  }else if( zBranch==0 ){
    zBranch = mprintf("unknown");
  }else{
    gitmirror_sanitize_name(zBranch);
  }

  /* Export the check-in */
  fprintf(xCmd, "commit refs/heads/%s\n", zBranch);
  fossil_free(zBranch);
  zMark = gitmirror_find_mark(zUuid,0,1);
  fprintf(xCmd, "mark %s\n", zMark);
  fossil_free(zMark);
  sqlite3_snprintf(sizeof(buf), buf, "%lld",
     (sqlite3_int64)((pMan->rDate-2440587.5)*86400.0)
  );

  /*
  ** Check for 'fx_' table from previous Git import, otherwise take contact info
  ** from user table for <emailaddr> in committer field. If no emailaddr, check
  ** if username is in email form, otherwise use generic 'username@noemail.net'.
  */
  char *zTmp;
  if (db_table_exists("repository", "fx_git")) {
    zEmail = db_text(0, "SELECT email FROM fx_git WHERE user=%Q", pMan->zUser);
  } else {
    zEmail = db_text(0, "SELECT info FROM user WHERE login=%Q", pMan->zUser);
  }

  /* Some repo 'info' fields return an empty string hence the second check */
  if (zEmail == NULL || zEmail[0] == '\0') {
    /* If username is in emailaddr form, don't append '@noemail.net' */
    if (strchr(pMan->zUser, '@') == NULL) {
      zEmail = mprintf("%s@noemail.net", pMan->zUser);
    } else {
      zEmail = fossil_strdup(pMan->zUser);
    }

  } else if ((zTmp = strchr(zEmail, '<')) != NULL) {
    ++zTmp;
    char *zTmpEnd = strchr(zTmp, '>');


    *(zTmpEnd) = '\0';
    zEmail = fossil_strdup(zTmp);




  }


  fprintf(xCmd, "committer %s <%s> %s +0000\n", pMan->zUser, zEmail, buf);
  fossil_free(zEmail);
  blob_init(&comment, pMan->zComment, -1);
  if( blob_size(&comment)==0 ){
    blob_append(&comment, "(no comment)", -1);
  }
  blob_appendf(&comment, "\n\nFossilOrigin-Name: %s", zUuid);
  fprintf(xCmd, "data %d\n%s\n", blob_size(&comment), blob_str(&comment));
  blob_reset(&comment);
  iParent = -1;  /* Which ancestor is the primary parent */
  for(i=0; i<pMan->nParent; i++){
    char *zOther = gitmirror_find_mark(pMan->azParent[i],0,0);
    if( zOther==0 ) continue;
    if( iParent<0 ){
      iParent = i;







>









>

|















>





<





>

|

|




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







|







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

  /* If some required files could not be exported, abandon the check-in
  ** export */
  if( nErr ){
    gitmirror_message(VERB_ERROR,
             "export of %s abandoned due to missing files\n", zUuid);
    *pnLimit = 0;
    manifest_destroy(pMan);
    return 1;
  }

  /* Figure out which branch this check-in is a member of */
  zBranch = db_text(0,
    "SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=%d",
    TAG_BRANCH, rid
  );
  if( fossil_strcmp(zBranch,"trunk")==0 ){
    assert( gitmirror_mainbranch!=0 );
    fossil_free(zBranch);
    zBranch = mprintf("%s",gitmirror_mainbranch);
  }else if( zBranch==0 ){
    zBranch = mprintf("unknown");
  }else{
    gitmirror_sanitize_name(zBranch);
  }

  /* Export the check-in */
  fprintf(xCmd, "commit refs/heads/%s\n", zBranch);
  fossil_free(zBranch);
  zMark = gitmirror_find_mark(zUuid,0,1);
  fprintf(xCmd, "mark %s\n", zMark);
  fossil_free(zMark);
  sqlite3_snprintf(sizeof(buf), buf, "%lld",
     (sqlite3_int64)((pMan->rDate-2440587.5)*86400.0)
  );

  /*
  ** Check for 'fx_' table from previous Git import, otherwise take contact info
  ** from user table for <emailaddr> in committer field. If no emailaddr, check
  ** if username is in email form, otherwise use generic 'username@noemail.net'.
  */

  if (db_table_exists("repository", "fx_git")) {
    zEmail = db_text(0, "SELECT email FROM fx_git WHERE user=%Q", pMan->zUser);
  } else {
    zEmail = db_text(0, "SELECT info FROM user WHERE login=%Q", pMan->zUser);
  }

  /* Some repo 'info' fields return an empty string hence the second check */
  if( zEmail==0 ){
    /* If username is in emailaddr form, don't append '@noemail.net' */
    if( pMan->zUser==0 || strchr(pMan->zUser, '@')==0 ){
      zEmail = mprintf("%s@noemail.net", pMan->zUser);
    } else {
      zEmail = fossil_strdup(pMan->zUser);
    }
  }else{
    char *zTmp = strchr(zEmail, '<');
    if( zTmp ){
      char *zTmpEnd = strchr(zTmp+1, '>');
      char *zNew;
      int i;
      if( zTmpEnd ) *(zTmpEnd) = 0;
      zNew = fossil_strdup(zTmp+1);
      fossil_free(zEmail);
      zEmail = zNew;
      for(i=0; zEmail[i] && !fossil_isspace(zEmail[i]); i++){}
      zEmail[i] = 0;
    }
  }
  fprintf(xCmd, "# rid=%d\n", rid);
  fprintf(xCmd, "committer %s <%s> %s +0000\n", pMan->zUser, zEmail, buf);
  fossil_free(zEmail);
  blob_init(&comment, pMan->zComment, -1);
  if( blob_size(&comment)==0 ){
    blob_append(&comment, "(no comment)", -1);
  }
  blob_appendf(&comment, "\n\nFossilOrigin-Name: %s", zUuid);
  fprintf(xCmd, "data %d\n%s\n", blob_strlen(&comment), blob_str(&comment));
  blob_reset(&comment);
  iParent = -1;  /* Which ancestor is the primary parent */
  for(i=0; i<pMan->nParent; i++){
    char *zOther = gitmirror_find_mark(pMan->azParent[i],0,0);
    if( zOther==0 ) continue;
    if( iParent<0 ){
      iParent = i;
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
      if( strchr(zMode,'l') ) zGitMode = "120000";
    }
    zFNQuoted = gitmirror_quote_filename_if_needed(zFilename);
    fprintf(xCmd,"M %s %s %s\n", zGitMode, zMark, zFNQuoted);
    fossil_free(zFNQuoted);
  }
  db_finalize(&q);



  /* Include Fossil-generated auxiliary files in the check-in */
  if( fManifest & MFESTFLG_RAW ){
    Blob manifest;
    content_get(rid, &manifest);
    fprintf(xCmd,"M 100644 inline manifest\ndata %d\n%s\n",
      blob_size(&manifest), blob_str(&manifest));
    blob_reset(&manifest);
  }
  if( fManifest & MFESTFLG_UUID ){
    int n = (int)strlen(zUuid);
    fprintf(xCmd,"M 100644 inline manifest.uuid\ndata %d\n%s\n", n, zUuid);
  }
  if( fManifest & MFESTFLG_TAGS ){
    Blob tagslist;
    blob_init(&tagslist, 0, 0);
    get_checkin_taglist(rid, &tagslist);
    fprintf(xCmd,"M 100644 inline manifest.tags\ndata %d\n%s\n",
      blob_size(&tagslist), blob_str(&tagslist));
    blob_reset(&tagslist);
  }

  /* The check-in is finished, so decrement the counter */
  (*pnLimit)--;
  return 0;
}





































































/*
** Implementation of the "fossil git export" command.
*/
void gitmirror_export_command(void){
  const char *zLimit;             /* Text of the --limit flag */
  int nLimit = 0x7fffffff;        /* Numeric value of the --limit flag */
  int nTotal = 0;                 /* Total number of check-ins to export */
  char *zMirror;                  /* Name of the mirror */
  char *z;                        /* Generic string */
  char *zCmd;                     /* git command to run as a subprocess */
  const char *zDebug = 0;         /* Value of the --debug flag */
  const char *zAutoPush = 0;      /* Value of the --autopush flag */

  char *zPushUrl;                 /* URL to sync the mirror to */
  double rEnd;                    /* time of most recent export */
  int rc;                         /* Result code */
  int bForce;                     /* Do the export and sync even if no changes*/
  int bNeedRepack = 0;            /* True if we should run repack at the end */
  int fManifest;                  /* Current "manifest" setting */
  int bIfExists;                  /* The --if-mirrored flag */
  FILE *xCmd;                     /* Pipe to the "git fast-import" command */
  FILE *pMarks;                   /* Git mark files */
  Stmt q;                         /* Queries */
  char zLine[200];                /* One line of a mark file */

  zDebug = find_option("debug",0,1);
  db_find_and_open_repository(0, 0);
  zLimit = find_option("limit", 0, 1);
  if( zLimit ){
    nLimit = (unsigned int)atoi(zLimit);
    if( nLimit<=0 ) fossil_fatal("--limit must be positive");
  }
  zAutoPush = find_option("autopush",0,1);

  bForce = find_option("force","f",0)!=0;
  bIfExists = find_option("if-mirrored",0,0)!=0;
  gitmirror_verbosity = VERB_NORMAL;
  while( find_option("quiet","q",0)!=0 ){ gitmirror_verbosity--; }
  while( find_option("verbose","v",0)!=0 ){ gitmirror_verbosity++; }
  verify_all_options();
  if( g.argc!=4 && g.argc!=3 ){ usage("export ?MIRROR?"); }
  if( g.argc==4 ){
    Blob mirror;
    file_canonical_name(g.argv[3], &mirror, 0);
    db_set("last-git-export-repo", blob_str(&mirror), 0);
    blob_reset(&mirror);
  }
  zMirror = db_get("last-git-export-repo", 0);
  if( zMirror==0 ){
    if( bIfExists ) return;
    fossil_fatal("no Git repository specified");
  }










  /* Make sure the GIT repository directory exists */
  rc = file_mkdir(zMirror, ExtFILE, 0);
  if( rc ) fossil_fatal("cannot create directory \"%s\"", zMirror);

  /* Make sure GIT has been initialized */
  z = mprintf("%s/.git", zMirror);
  if( !file_isdir(z, ExtFILE) ){
    zCmd = mprintf("git init %$",zMirror);
    gitmirror_message(VERB_NORMAL, "%s\n", zCmd);
    rc = fossil_system(zCmd);
    if( rc ){
      fossil_fatal("cannot initialize the git repository using: \"%s\"", zCmd);
    }
    fossil_free(zCmd);
    bNeedRepack = 1;
  }
  fossil_free(z);
  
  /* Make sure the .mirror_state subdirectory exists */
  z = mprintf("%s/.mirror_state", zMirror);
  rc = file_mkdir(z, ExtFILE, 0);







>
>






|











|







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













>




















>


















>
>
>
>
>
>
>
>
>








<
|
<
<
<
<
<







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
      if( strchr(zMode,'l') ) zGitMode = "120000";
    }
    zFNQuoted = gitmirror_quote_filename_if_needed(zFilename);
    fprintf(xCmd,"M %s %s %s\n", zGitMode, zMark, zFNQuoted);
    fossil_free(zFNQuoted);
  }
  db_finalize(&q);
  manifest_destroy(pMan);
  pMan = 0;

  /* Include Fossil-generated auxiliary files in the check-in */
  if( fManifest & MFESTFLG_RAW ){
    Blob manifest;
    content_get(rid, &manifest);
    fprintf(xCmd,"M 100644 inline manifest\ndata %d\n%s\n",
      blob_strlen(&manifest), blob_str(&manifest));
    blob_reset(&manifest);
  }
  if( fManifest & MFESTFLG_UUID ){
    int n = (int)strlen(zUuid);
    fprintf(xCmd,"M 100644 inline manifest.uuid\ndata %d\n%s\n", n, zUuid);
  }
  if( fManifest & MFESTFLG_TAGS ){
    Blob tagslist;
    blob_init(&tagslist, 0, 0);
    get_checkin_taglist(rid, &tagslist);
    fprintf(xCmd,"M 100644 inline manifest.tags\ndata %d\n%s\n",
      blob_strlen(&tagslist), blob_str(&tagslist));
    blob_reset(&tagslist);
  }

  /* The check-in is finished, so decrement the counter */
  (*pnLimit)--;
  return 0;
}

/*
** Create a new Git repository at zMirror to use as the mirror.
** Try to make zMainBr be the main branch for the new repository.
**
** A side-effect of this routine is that current-working directory
** is changed to zMirror.
**
** If zMainBr is initially NULL, then the return value will be the
** name of the default branch to be used by Git.  If zMainBr is
** initially non-NULL, then the return value will be a copy of zMainBr.
*/
static char *gitmirror_init(
  const char *zMirror,
  char *zMainBr
){
  char *zCmd;
  int rc;

  /* Create a new Git repository at zMirror */
  zCmd = mprintf("git init %$", zMirror);
  gitmirror_message(VERB_NORMAL, "%s\n", zCmd);
  rc = fossil_system(zCmd);
  if( rc ){
    fossil_fatal("cannot initialize git repository using: %s\n", zCmd);
  }
  fossil_free(zCmd);

  /* Must be in the new Git repository directory for subsequent commands */
  rc = file_chdir(zMirror, 0);
  if( rc ){
    fossil_fatal("cannot change to directory \"%s\"", zMirror);
  }

  if( zMainBr ){
    /* Set the current branch to zMainBr */
    zCmd = mprintf("git symbolic-ref HEAD refs/heads/%s", zMainBr);
    gitmirror_message(VERB_NORMAL, "%s\n", zCmd);
    rc = fossil_system(zCmd);
    if( rc ){
      fossil_fatal("git command failed: %s", zCmd);
    }
    fossil_free(zCmd);
  }else{
    /* If zMainBr is not specified, then check to see what branch
    ** name Git chose for itself */
    char *z;
    char zLine[1000];
    FILE *xCmd;
    int i;
    zCmd = "git symbolic-ref --short HEAD";
    gitmirror_message(VERB_NORMAL, "%s\n", zCmd);
    xCmd = popen(zCmd, "r");
    if( xCmd==0 ){
      fossil_fatal("git command failed: %s", zCmd);
    }
    
    z = fgets(zLine, sizeof(zLine), xCmd);
    pclose(xCmd);
    if( z==0 ){
      fossil_fatal("no output from \"%s\"", zCmd);
    }
    for(i=0; z[i] && !fossil_isspace(z[i]); i++){}
    z[i] = 0;
    zMainBr = fossil_strdup(z);
  }
  return zMainBr;
} 

/*
** Implementation of the "fossil git export" command.
*/
void gitmirror_export_command(void){
  const char *zLimit;             /* Text of the --limit flag */
  int nLimit = 0x7fffffff;        /* Numeric value of the --limit flag */
  int nTotal = 0;                 /* Total number of check-ins to export */
  char *zMirror;                  /* Name of the mirror */
  char *z;                        /* Generic string */
  char *zCmd;                     /* git command to run as a subprocess */
  const char *zDebug = 0;         /* Value of the --debug flag */
  const char *zAutoPush = 0;      /* Value of the --autopush flag */
  char *zMainBr = 0;              /* Value of the --mainbranch flag */
  char *zPushUrl;                 /* URL to sync the mirror to */
  double rEnd;                    /* time of most recent export */
  int rc;                         /* Result code */
  int bForce;                     /* Do the export and sync even if no changes*/
  int bNeedRepack = 0;            /* True if we should run repack at the end */
  int fManifest;                  /* Current "manifest" setting */
  int bIfExists;                  /* The --if-mirrored flag */
  FILE *xCmd;                     /* Pipe to the "git fast-import" command */
  FILE *pMarks;                   /* Git mark files */
  Stmt q;                         /* Queries */
  char zLine[200];                /* One line of a mark file */

  zDebug = find_option("debug",0,1);
  db_find_and_open_repository(0, 0);
  zLimit = find_option("limit", 0, 1);
  if( zLimit ){
    nLimit = (unsigned int)atoi(zLimit);
    if( nLimit<=0 ) fossil_fatal("--limit must be positive");
  }
  zAutoPush = find_option("autopush",0,1);
  zMainBr = (char*)find_option("mainbranch",0,1);
  bForce = find_option("force","f",0)!=0;
  bIfExists = find_option("if-mirrored",0,0)!=0;
  gitmirror_verbosity = VERB_NORMAL;
  while( find_option("quiet","q",0)!=0 ){ gitmirror_verbosity--; }
  while( find_option("verbose","v",0)!=0 ){ gitmirror_verbosity++; }
  verify_all_options();
  if( g.argc!=4 && g.argc!=3 ){ usage("export ?MIRROR?"); }
  if( g.argc==4 ){
    Blob mirror;
    file_canonical_name(g.argv[3], &mirror, 0);
    db_set("last-git-export-repo", blob_str(&mirror), 0);
    blob_reset(&mirror);
  }
  zMirror = db_get("last-git-export-repo", 0);
  if( zMirror==0 ){
    if( bIfExists ) return;
    fossil_fatal("no Git repository specified");
  }

  if( zMainBr ){
    z = fossil_strdup(zMainBr);
    gitmirror_sanitize_name(z);
    if( strcmp(z, zMainBr) ){
      fossil_fatal("\"%s\" is not a legal branch name for Git", zMainBr);
    }
    fossil_free(z);
  }

  /* Make sure the GIT repository directory exists */
  rc = file_mkdir(zMirror, ExtFILE, 0);
  if( rc ) fossil_fatal("cannot create directory \"%s\"", zMirror);

  /* Make sure GIT has been initialized */
  z = mprintf("%s/.git", zMirror);
  if( !file_isdir(z, ExtFILE) ){

    zMainBr = gitmirror_init(zMirror, zMainBr);





    bNeedRepack = 1;
  }
  fossil_free(z);
  
  /* Make sure the .mirror_state subdirectory exists */
  z = mprintf("%s/.mirror_state", zMirror);
  rc = file_mkdir(z, ExtFILE, 0);
1403
1404
1405
1406
1407
1408
1409















1410
1411
1412
1413
1414
1415
1416
      db_multi_exec(
         "REPLACE INTO mirror.mconfig(key,value)"
         "VALUES('autopush',%Q)",
         zAutoPush
      );
    }
  }
















  /* See if there is any work to be done.  Exit early if not, before starting
  ** the "git fast-import" command. */
  if( !bForce
   && !db_exists("SELECT 1 FROM event WHERE type IN ('ci','t')"
                 " AND mtime>coalesce((SELECT value FROM mconfig"
                                        " WHERE key='start'),0.0)")







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







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
      db_multi_exec(
         "REPLACE INTO mirror.mconfig(key,value)"
         "VALUES('autopush',%Q)",
         zAutoPush
      );
    }
  }

  /* Change the mainbranch setting if the --mainbranch flag is present */
  if( zMainBr && zMainBr[0] ){
    db_multi_exec(
       "REPLACE INTO mirror.mconfig(key,value)"
       "VALUES('mainbranch',%Q)",
       zMainBr
    );
    gitmirror_mainbranch = fossil_strdup(zMainBr);
  }else{
    /* Recover the saved name of the main branch */
    gitmirror_mainbranch = db_text("master",
                "SELECT value FROM mconfig WHERE key='mainbranch'");
  }


  /* See if there is any work to be done.  Exit early if not, before starting
  ** the "git fast-import" command. */
  if( !bForce
   && !db_exists("SELECT 1 FROM event WHERE type IN ('ci','t')"
                 " AND mtime>coalesce((SELECT value FROM mconfig"
                                        " WHERE key='start'),0.0)")
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
              " --quiet --done");
    gitmirror_message(VERB_NORMAL, "%s\n", zCmd);
#ifdef _WIN32
    xCmd = popen(zCmd, "wb");
#else
    xCmd = popen(zCmd, "w");
#endif
    if( zCmd==0 ){
      fossil_fatal("cannot start the \"git fast-import\" command");
    }
    fossil_free(zCmd);
  }

  /* Run the export */
  rEnd = 0.0;







|







1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
              " --quiet --done");
    gitmirror_message(VERB_NORMAL, "%s\n", zCmd);
#ifdef _WIN32
    xCmd = popen(zCmd, "wb");
#else
    xCmd = popen(zCmd, "w");
#endif
    if( xCmd==0 ){
      fossil_fatal("cannot start the \"git fast-import\" command");
    }
    fossil_free(zCmd);
  }

  /* Run the export */
  rEnd = 0.0;
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
  );
  while( db_step(&q)==SQLITE_ROW ){
    char *zBrname = fossil_strdup(db_column_text(&q,0));
    const char *zObj = db_column_text(&q,2);
    char *zRefCmd;
    if( fossil_strcmp(zBrname,"trunk")==0 ){
      fossil_free(zBrname);
      zBrname = fossil_strdup("master");
    }else{
      gitmirror_sanitize_name(zBrname);
    }
    zRefCmd = mprintf("git update-ref \"refs/heads/%s\" %$", zBrname, zObj);
    fossil_free(zBrname);
    gitmirror_message(VERB_NORMAL, "%s\n", zRefCmd);
    fossil_system(zRefCmd);







|







1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
  );
  while( db_step(&q)==SQLITE_ROW ){
    char *zBrname = fossil_strdup(db_column_text(&q,0));
    const char *zObj = db_column_text(&q,2);
    char *zRefCmd;
    if( fossil_strcmp(zBrname,"trunk")==0 ){
      fossil_free(zBrname);
      zBrname = fossil_strdup(gitmirror_mainbranch);
    }else{
      gitmirror_sanitize_name(zBrname);
    }
    zRefCmd = mprintf("git update-ref \"refs/heads/%s\" %$", zBrname, zObj);
    fossil_free(zBrname);
    gitmirror_message(VERB_NORMAL, "%s\n", zRefCmd);
    fossil_system(zRefCmd);
1649
1650
1651
1652
1653
1654
1655

1656
1657
1658
1659
1660
1661
1662


1663
1664
1665
1666
1667
1668
1669
  z = db_text(0, "SELECT value FROM mconfig WHERE key='autopush'");
  if( z==0 ){
    fossil_print("Autopush:    off\n");
  }else{
    UrlData url;
    url_parse_local(z, 0, &url);
    fossil_print("Autopush:    %s\n", url.canonical);

  }
  n = db_int(0,
    "SELECT count(*) FROM event"
    " WHERE type='ci'"
    "   AND mtime>coalesce((SELECT value FROM mconfig"
                          "  WHERE key='start'),0.0)"
  );


  if( n==0 ){
    fossil_print("Status:      up-to-date\n");
  }else{
    fossil_print("Status:      %d check-in%s awaiting export\n",
                 n, n==1 ? "" : "s");
  }
  n = db_int(0, "SELECT count(*) FROM mmark WHERE isfile");







>







>
>







1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
  z = db_text(0, "SELECT value FROM mconfig WHERE key='autopush'");
  if( z==0 ){
    fossil_print("Autopush:    off\n");
  }else{
    UrlData url;
    url_parse_local(z, 0, &url);
    fossil_print("Autopush:    %s\n", url.canonical);
    fossil_free(z);
  }
  n = db_int(0,
    "SELECT count(*) FROM event"
    " WHERE type='ci'"
    "   AND mtime>coalesce((SELECT value FROM mconfig"
                          "  WHERE key='start'),0.0)"
  );
  z = db_text("master", "SELECT value FROM mconfig WHERE key='mainbranch'");
  fossil_print("Main-Branch: %s\n",z);
  if( n==0 ){
    fossil_print("Status:      up-to-date\n");
  }else{
    fossil_print("Status:      %d check-in%s awaiting export\n",
                 n, n==1 ? "" : "s");
  }
  n = db_int(0, "SELECT count(*) FROM mmark WHERE isfile");
1704
1705
1706
1707
1708
1709
1710




1711
1712
1713
1714
1715
1716
1717
**                             auto-push mechanism is disabled
**         --debug FILE        Write fast-export text to FILE rather than
**                             piping it into "git fast-import".
**         --force|-f          Do the export even if nothing has changed
**         --if-mirrored       No-op if the mirror does not already exist.
**         --limit N           Add no more than N new check-ins to MIRROR.
**                             Useful for debugging




**         --quiet|-q          Reduce output. Repeat for even less output.
**         --verbose|-v        More output.
**
** > fossil git import MIRROR
**
**       TBD...   
**







>
>
>
>







1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
**                             auto-push mechanism is disabled
**         --debug FILE        Write fast-export text to FILE rather than
**                             piping it into "git fast-import".
**         --force|-f          Do the export even if nothing has changed
**         --if-mirrored       No-op if the mirror does not already exist.
**         --limit N           Add no more than N new check-ins to MIRROR.
**                             Useful for debugging
**         --mainbranch NAME   Use NAME as the name of the main branch in Git.
**                             The "trunk" branch of the Fossil repository is
**                             mapped into this name.  "master" is used if
**                             this option is omitted.
**         --quiet|-q          Reduce output. Repeat for even less output.
**         --verbose|-v        More output.
**
** > fossil git import MIRROR
**
**       TBD...   
**

Changes to src/extcgi.c.

141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
** Relay an HTTP request to secondary CGI after first checking the
** login credentials and setting auxiliary environment variables
** so that the secondary CGI can be aware of the credentials and
** capabilities of the Fossil user.
**
** The /ext page is only functional if the "extroot: DIR" setting is
** found in the CGI script that launched Fossil, or if the "--extroot DIR"
** flag is present when Fossil is lauched using the "server", "ui", or
** "http" commands.  DIR must be an absolute pathname (relative to the
** chroot jail) of the root of the file hierarchy that implements the CGI
** functionality.  Executable files are CGI.  Non-executable files are
** static content.
**
** The path after the /ext is the path to the CGI script or static file
** relative to DIR. For security, this path may not contain characters







|







141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
** Relay an HTTP request to secondary CGI after first checking the
** login credentials and setting auxiliary environment variables
** so that the secondary CGI can be aware of the credentials and
** capabilities of the Fossil user.
**
** The /ext page is only functional if the "extroot: DIR" setting is
** found in the CGI script that launched Fossil, or if the "--extroot DIR"
** flag is present when Fossil is launched using the "server", "ui", or
** "http" commands.  DIR must be an absolute pathname (relative to the
** chroot jail) of the root of the file hierarchy that implements the CGI
** functionality.  Executable files are CGI.  Non-executable files are
** static content.
**
** The path after the /ext is the path to the CGI script or static file
** relative to DIR. For security, this path may not contain characters
393
394
395
396
397
398
399

400
401
402
403
404
405
406
  Stmt q;
  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }
  ext_files();

  style_header("CGI Extension Filelist");
  @ <table border="0" cellspacing="0" cellpadding="3">
  @ <tbody>
  db_prepare(&q, "SELECT pathname, isexe FROM sfile"
                 " ORDER BY pathname");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zName = db_column_text(&q,0);







>







393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
  Stmt q;
  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }
  ext_files();
  style_set_current_feature("extcgi");
  style_header("CGI Extension Filelist");
  @ <table border="0" cellspacing="0" cellpadding="3">
  @ <tbody>
  db_prepare(&q, "SELECT pathname, isexe FROM sfile"
                 " ORDER BY pathname");
  while( db_step(&q)==SQLITE_ROW ){
    const char *zName = db_column_text(&q,0);
418
419
420
421
422
423
424
425
426
      }
    }
    @ </tr>
  }
  db_finalize(&q);
  @ </tbody>
  @ </table>
  style_finish_page("extcgi");
}







|

419
420
421
422
423
424
425
426
427
      }
    }
    @ </tr>
  }
  db_finalize(&q);
  @ </tbody>
  @ </table>
  style_finish_page();
}

Changes to src/fileedit.c.

1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
         "<code>fileedit-glob</code> repository setting</a>\n"
         "must be set to a comma- and/or newine-delimited list of glob\n"
         "values matching files which may be edited online."
         "</p>\n");
    }else{
      CX("<p>Online editing is disabled for this repository.</p>\n");
    }
    style_finish_page("fileedit");
    return;
  }

  /* Dispatch AJAX methods based tail of the request URI.
  ** The AJAX parts do their own permissions/CSRF check and
  ** fail with a JSON-format response if needed.
  */







|







1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
         "<code>fileedit-glob</code> repository setting</a>\n"
         "must be set to a comma- and/or newine-delimited list of glob\n"
         "values matching files which may be edited online."
         "</p>\n");
    }else{
      CX("<p>Online editing is disabled for this repository.</p>\n");
    }
    style_finish_page();
    return;
  }

  /* Dispatch AJAX methods based tail of the request URI.
  ** The AJAX parts do their own permissions/CSRF check and
  ** fail with a JSON-format response if needed.
  */
2059
2060
2061
2062
2063
2064
2065
2066
2067
       "}\n");
    CX("})();")/*anonymous function*/;
    style_script_end();
  }
  blob_reset(&err);
  CheckinMiniInfo_cleanup(&cimi);
  db_end_transaction(0);
  style_finish_page("fileedit");
}







|

2059
2060
2061
2062
2063
2064
2065
2066
2067
       "}\n");
    CX("})();")/*anonymous function*/;
    style_script_end();
  }
  blob_reset(&err);
  CheckinMiniInfo_cleanup(&cimi);
  db_end_transaction(0);
  style_finish_page();
}

Changes to src/finfo.c.

365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
  if( brBg ) url_add_parameter(&url, "brbg", 0);
  if( uBg ) url_add_parameter(&url, "ubg", 0);
  ridFrom = name_to_rid_www("from");
  zPrevDate[0] = 0;
  cookie_render();
  if( fnid==0 ){
    @ No such file: %h(zFilename)
    style_finish_page("finfo");
    return;
  }
  if( g.perm.Admin ){
    style_submenu_element("MLink Table", "%R/mlink?name=%t", zFilename);
  }
  if( ridFrom ){
    if( P("to")!=0 ){







|







365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
  if( brBg ) url_add_parameter(&url, "brbg", 0);
  if( uBg ) url_add_parameter(&url, "ubg", 0);
  ridFrom = name_to_rid_www("from");
  zPrevDate[0] = 0;
  cookie_render();
  if( fnid==0 ){
    @ No such file: %h(zFilename)
    style_finish_page();
    return;
  }
  if( g.perm.Admin ){
    style_submenu_element("MLink Table", "%R/mlink?name=%t", zFilename);
  }
  if( ridFrom ){
    if( P("to")!=0 ){
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
    if( g.perm.Hyperlink && zUuid ){
      const char *z = zFName;
      @ <span id='links-%d(frid)'><span class='timelineExtraLinks'>
      @ %z(href("%R/annotate?filename=%h&checkin=%s",z,zCkin))
      @ [annotate]</a>
      @ %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(zFName) ){
        @ %z(href("%R/fileedit?filename=%T&checkin=%!S",zFName,zCkin))\
        @ [edit]</a>
      }







|







703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
    if( g.perm.Hyperlink && zUuid ){
      const char *z = zFName;
      @ <span id='links-%d(frid)'><span class='timelineExtraLinks'>
      @ %z(href("%R/annotate?filename=%h&checkin=%s",z,zCkin))
      @ [annotate]</a>
      @ %z(href("%R/blame?filename=%h&checkin=%s",z,zCkin))
      @ [blame]</a>
      @ %z(href("%R/timeline?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(zFName) ){
        @ %z(href("%R/fileedit?filename=%T&checkin=%!S",zFName,zCkin))\
        @ [edit]</a>
      }
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
    }else{
      @ <tr class="timelineBottom" id="btm-%d(iTableId)">\
      @ <td></td><td></td><td></td></tr>
    }
  }
  @ </table>
  timeline_output_graph_javascript(pGraph, TIMELINE_FILEDIFF, iTableId);
  style_finish_page("finfo");
}

/*
** WEBPAGE: mlink
** URL: /mlink?name=FILENAME
** URL: /mlink?ci=NAME
**







|







752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
    }else{
      @ <tr class="timelineBottom" id="btm-%d(iTableId)">\
      @ <td></td><td></td><td></td></tr>
    }
  }
  @ </table>
  timeline_output_graph_javascript(pGraph, TIMELINE_FILEDIFF, iTableId);
  style_finish_page();
}

/*
** WEBPAGE: mlink
** URL: /mlink?name=FILENAME
** URL: /mlink?ci=NAME
**
777
778
779
780
781
782
783

784
785
786
787
788
789
790
void mlink_page(void){
  const char *zFName = P("name");
  const char *zCI = P("ci");
  Stmt q;

  login_check_credentials();
  if( !g.perm.Admin ){ login_needed(g.anon.Admin); return; }

  style_header("MLINK Table");
  if( zFName==0 && zCI==0 ){
    @ <span class='generalError'>
    @ Requires either a name= or ci= query parameter
    @ </span>
  }else if( zFName ){
    int fnid = db_int(0,"SELECT fnid FROM filename WHERE name=%Q",zFName);







>







777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
void mlink_page(void){
  const char *zFName = P("name");
  const char *zCI = P("ci");
  Stmt q;

  login_check_credentials();
  if( !g.perm.Admin ){ login_needed(g.anon.Admin); return; }
  style_set_current_feature("finfo");
  style_header("MLINK Table");
  if( zFName==0 && zCI==0 ){
    @ <span class='generalError'>
    @ Requires either a name= or ci= query parameter
    @ </span>
  }else if( zFName ){
    int fnid = db_int(0,"SELECT fnid FROM filename WHERE name=%Q",zFName);
932
933
934
935
936
937
938
939
940
      @ </tr>
    }
    db_finalize(&q);
    @ </tbody>
    @ </table>
    @ </div>
  }
  style_finish_page("finfo");
}







|

933
934
935
936
937
938
939
940
941
      @ </tr>
    }
    db_finalize(&q);
    @ </tbody>
    @ </table>
    @ </div>
  }
  style_finish_page();
}

Changes to src/forum.c.

760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
** Query parameters:
**
**   name=X        REQUIRED.  The hash of the post to display.
**   t=a           Automatic display mode, i.e. hierarchical for
**                 desktop and chronological for mobile.  This is the
**                 default if the "t" query parameter is omitted.
**   t=c           Show posts in the order they were written.
**   t=h           Show posts usin hierarchical indenting.
**   t=s           Show only the post specified by "name=X".
**   t=r           Alias for "t=c&unf&hist".
**   t=y           Alias for "t=s&unf&hist".
**   raw           Alias for "t=s&unf".  Additionally, omit the border
**                 around the post, and ignore "t" and "hist".
**   unf           Show the original, unformatted source text.
**   hist          Show edit history in addition to current posts.







|







760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
** Query parameters:
**
**   name=X        REQUIRED.  The hash of the post to display.
**   t=a           Automatic display mode, i.e. hierarchical for
**                 desktop and chronological for mobile.  This is the
**                 default if the "t" query parameter is omitted.
**   t=c           Show posts in the order they were written.
**   t=h           Show posts using hierarchical indenting.
**   t=s           Show only the post specified by "name=X".
**   t=r           Alias for "t=c&unf&hist".
**   t=y           Alias for "t=s&unf&hist".
**   raw           Alias for "t=s&unf".  Additionally, omit the border
**                 around the post, and ignore "t" and "hist".
**   unf           Show the original, unformatted source text.
**   hist          Show edit history in addition to current posts.
860
861
862
863
864
865
866

867
868
869
870
871
872
873
    "SELECT"
    " substr(event.comment,instr(event.comment,':')+2)"
    " FROM forumpost, event"
    " WHERE event.objid=forumpost.fpid"
    "   AND forumpost.fpid=%d;",
    fpid
  );

  style_header("%s%s", zThreadTitle, *zThreadTitle ? "" : "Forum");
  fossil_free(zThreadTitle);
  if( mode!=FD_CHRONO ){
    style_submenu_element("Chronological", "%R/%s/%s?t=c%s%s", g.zPath, zName,
        bUnf ? "&unf" : "", bHist ? "&hist" : "");
  }
  if( mode!=FD_HIER ){







>







860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
    "SELECT"
    " substr(event.comment,instr(event.comment,':')+2)"
    " FROM forumpost, event"
    " WHERE event.objid=forumpost.fpid"
    "   AND forumpost.fpid=%d;",
    fpid
  );
  style_set_current_feature("forum");
  style_header("%s%s", zThreadTitle, *zThreadTitle ? "" : "Forum");
  fossil_free(zThreadTitle);
  if( mode!=FD_CHRONO ){
    style_submenu_element("Chronological", "%R/%s/%s?t=c%s%s", g.zPath, zName,
        bUnf ? "&unf" : "", bHist ? "&hist" : "");
  }
  if( mode!=FD_HIER ){
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
  forum_display_thread(froot, fpid, mode, bUnf, bHist);

  /* Emit Forum Javascript. */
  builtin_request_js("forum.js");
  forum_emit_js();

  /* Emit the page style. */
  style_finish_page("forum");
}

/*
** Return true if a forum post should be moderated.
*/
static int forum_need_moderation(void){
  if( P("domod") ) return 1;







|







882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
  forum_display_thread(froot, fpid, mode, bUnf, bHist);

  /* Emit Forum Javascript. */
  builtin_request_js("forum.js");
  forum_emit_js();

  /* Emit the page style. */
  style_finish_page();
}

/*
** Return true if a forum post should be moderated.
*/
static int forum_need_moderation(void){
  if( P("domod") ) return 1;
1052
1053
1054
1055
1056
1057
1058

1059
1060
1061
1062
1063
1064
1065
    if( isEdit ){
      forumedit_page();
    }else{
      forumnew_page();
    }
    return;
  }

  style_header("%h As Anonymous?", isEdit ? "Reply" : "Post");
  @ <p>You are not logged in.
  @ <p><table border="0" cellpadding="10">
  @ <tr><td>
  @ <form action="%s(zGoto)" method="POST">
  @ <input type="submit" value="Remain Anonymous">
  @ </form>







>







1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
    if( isEdit ){
      forumedit_page();
    }else{
      forumnew_page();
    }
    return;
  }
  style_set_current_feature("forum");
  style_header("%h As Anonymous?", isEdit ? "Reply" : "Post");
  @ <p>You are not logged in.
  @ <p><table border="0" cellpadding="10">
  @ <tr><td>
  @ <form action="%s(zGoto)" method="POST">
  @ <input type="submit" value="Remain Anonymous">
  @ </form>
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
  @ <input type="hidden" name="g" value="%s(zGoto)">
  @ <input type="hidden" name="noanon" value="1">
  @ <input type="submit" value="Login">
  @ </form>
  @ <td>Log into an existing account
  @ </table>
  forum_emit_js();
  style_finish_page("forum");
  fossil_free(zGoto);
}

/*
** Write the "From: USER" line on the webpage.
*/
static void forum_from_line(void){







|







1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
  @ <input type="hidden" name="g" value="%s(zGoto)">
  @ <input type="hidden" name="noanon" value="1">
  @ <input type="submit" value="Login">
  @ </form>
  @ <td>Log into an existing account
  @ </table>
  forum_emit_js();
  style_finish_page();
  fossil_free(zGoto);
}

/*
** Write the "From: USER" line on the webpage.
*/
static void forum_from_line(void){
1113
1114
1115
1116
1117
1118
1119

1120
1121
1122
1123
1124
1125
1126
  if( P("submit") && cgi_csrf_safe(1) ){
    if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent) ) return;
  }
  if( P("preview") && !whitespace_only(zContent) ){
    @ <h1>Preview:</h1>
    forum_render(zTitle, zMimetype, zContent, "forumEdit", 1);
  }

  style_header("New Forum Thread");
  @ <form action="%R/forume1" method="POST">
  @ <h1>New Thread:</h1>
  forum_from_line();
  forum_post_widget(zTitle, zMimetype, zContent);
  @ <input type="submit" name="preview" value="Preview">
  if( P("preview") && !whitespace_only(zContent) ){







>







1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
  if( P("submit") && cgi_csrf_safe(1) ){
    if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent) ) return;
  }
  if( P("preview") && !whitespace_only(zContent) ){
    @ <h1>Preview:</h1>
    forum_render(zTitle, zMimetype, zContent, "forumEdit", 1);
  }
  style_set_current_feature("forum");
  style_header("New Forum Thread");
  @ <form action="%R/forume1" method="POST">
  @ <h1>New Thread:</h1>
  forum_from_line();
  forum_post_widget(zTitle, zMimetype, zContent);
  @ <input type="submit" name="preview" value="Preview">
  if( P("preview") && !whitespace_only(zContent) ){
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
    @ Require moderator approval</label>
    @ <br><label><input type="checkbox" name="showqp" %s(PCK("showqp"))> \
    @ Show query parameters</label>
    @ </div>
  }
  @ </form>
  forum_emit_js();
  style_finish_page("forum");
}

/*
** WEBPAGE: forume2
**
** Edit an existing forum message.
** Query parameters:







|







1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
    @ Require moderator approval</label>
    @ <br><label><input type="checkbox" name="showqp" %s(PCK("showqp"))> \
    @ Show query parameters</label>
    @ </div>
  }
  @ </form>
  forum_emit_js();
  style_finish_page();
}

/*
** WEBPAGE: forume2
**
** Edit an existing forum message.
** Query parameters:
1212
1213
1214
1215
1216
1217
1218

1219
1220
1221
1222
1223
1224
1225
        cgi_redirectf("%R/forumpost/%S",zParent);
      }else{
        cgi_redirectf("%R/forum");
      }
      return;
    }
  }

  isDelete = P("nullout")!=0;
  if( P("submit")
   && isCsrfSafe
   && (zContent = PDT("content",""))!=0
   && (!whitespace_only(zContent) || isDelete)
  ){
    int done = 1;







>







1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
        cgi_redirectf("%R/forumpost/%S",zParent);
      }else{
        cgi_redirectf("%R/forum");
      }
      return;
    }
  }
  style_set_current_feature("forum");
  isDelete = P("nullout")!=0;
  if( P("submit")
   && isCsrfSafe
   && (zContent = PDT("content",""))!=0
   && (!whitespace_only(zContent) || isDelete)
  ){
    int done = 1;
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
    @ Require moderator approval</label>
    @ <br><label><input type="checkbox" name="showqp" %s(PCK("showqp"))> \
    @ Show query parameters</label>
    @ </div>
  }
  @ </form>
  forum_emit_js();
  style_finish_page("forum");
}

/*
** WEBPAGE: forummain
** WEBPAGE: forum
**
** The main page for the forum feature.  Show a list of recent forum







|







1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
    @ Require moderator approval</label>
    @ <br><label><input type="checkbox" name="showqp" %s(PCK("showqp"))> \
    @ Show query parameters</label>
    @ </div>
  }
  @ </form>
  forum_emit_js();
  style_finish_page();
}

/*
** WEBPAGE: forummain
** WEBPAGE: forum
**
** The main page for the forum feature.  Show a list of recent forum
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
  int srchFlags;
  login_check_credentials();
  srchFlags = search_restrict(SRCH_FORUM);
  if( !g.perm.RdForum ){
    login_needed(g.anon.RdForum);
    return;
  }

  style_header("Forum");
  if( g.perm.WrForum ){
    style_submenu_element("New Thread","%R/forumnew");
  }else{
    /* Can't combine this with previous case using the ternary operator
     * because that causes an error yelling about "non-constant format"
     * with some compilers.  I can't see it, since both expressions have
     * the same format, but I'm no C spec lawyer. */
    style_submenu_element("New Thread","%R/login");
  }
  if( g.perm.ModForum && moderation_needed() ){
    style_submenu_element("Moderation Requests", "%R/modreq");
  }
  if( (srchFlags & SRCH_FORUM)!=0 ){
    if( search_screen(SRCH_FORUM, 0) ){
      style_submenu_element("Recent Threads","%R/forum");
      style_finish_page("forum");
      return;
    }
  }
  iLimit = atoi(PD("n","25"));
  iOfst = atoi(PD("x","0"));
  iCnt = 0;
  if( db_table_exists("repository","forumpost") ){







>
















|







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
  int srchFlags;
  login_check_credentials();
  srchFlags = search_restrict(SRCH_FORUM);
  if( !g.perm.RdForum ){
    login_needed(g.anon.RdForum);
    return;
  }
  style_set_current_feature("forum");
  style_header("Forum");
  if( g.perm.WrForum ){
    style_submenu_element("New Thread","%R/forumnew");
  }else{
    /* Can't combine this with previous case using the ternary operator
     * because that causes an error yelling about "non-constant format"
     * with some compilers.  I can't see it, since both expressions have
     * the same format, but I'm no C spec lawyer. */
    style_submenu_element("New Thread","%R/login");
  }
  if( g.perm.ModForum && moderation_needed() ){
    style_submenu_element("Moderation Requests", "%R/modreq");
  }
  if( (srchFlags & SRCH_FORUM)!=0 ){
    if( search_screen(SRCH_FORUM, 0) ){
      style_submenu_element("Recent Threads","%R/forum");
      style_finish_page();
      return;
    }
  }
  iLimit = atoi(PD("n","25"));
  iOfst = atoi(PD("x","0"));
  iCnt = 0;
  if( db_table_exists("repository","forumpost") ){
1453
1454
1455
1456
1457
1458
1459
1460
1461
    db_finalize(&q);
  }
  if( iCnt>0 ){
    @ </table></div>
  }else{
    @ <h1>No forum posts found</h1>
  }
  style_finish_page("forum");
}







|

1458
1459
1460
1461
1462
1463
1464
1465
1466
    db_finalize(&q);
  }
  if( iCnt>0 ){
    @ </table></div>
  }else{
    @ <h1>No forum posts found</h1>
  }
  style_finish_page();
}

Changes to src/fossil.dom.js.

110
111
112
113
114
115
116



117

118
119
120
121
122
123
124
    const e = this.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;
  };
  /**
     Returns a TEXTAREA element.







>
>
>
|
>







110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
    const e = this.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');
  /** Returns a new TEXT node which contains the text of all of the
      arguments appended together. */
  dom.text = function(/*...*/){
    return document.createTextNode(argsToArray(arguments).join(''));
  };
  dom.button = function(label){
    const b = this.create('button');
    if(label) b.appendChild(this.text(label));
    return b;
  };
  /**
     Returns a TEXTAREA element.
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
  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. If legendText is an HTMLElement then it is
     appended as-is, else it is assume (if truthy) to be a value

     suitable for passing to dom.append(aLegendElement,...).
  */
  dom.fieldset = function(legendText){
    const fs = this.create('fieldset');
    if(legendText){
      this.append(
        fs,
        (legendText instanceof HTMLElement)
          ? legendText
          : 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.







|
|
>
|








|




>
>
>
>
>
>
>
>
>
>







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
  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. If legendText is an HTMLElement then is is
     assumed to be a LEGEND and is appended as-is, else it is assumed
     (if truthy) to be a value suitable for passing to
     dom.append(aLegendElement,...).
  */
  dom.fieldset = function(legendText){
    const fs = this.create('fieldset');
    if(legendText){
      this.append(
        fs,
        (legendText instanceof HTMLElement)
          ? legendText
          : this.append(this.legend(legendText))
      );
    }
    return fs;
  };
  /**
     Returns a new LEGEND legend element. The given argument, if
     not falsy, is append()ed to the element (so it may be a string
     or DOM element.
  */
  dom.legend = function(legendText){
    const rc = this.create('legend');
    if(legendText) this.append(rc, legendText);
    return rc;
  };

  /**
     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.
287
288
289
290
291
292
293
294

295
296
297
298
299
300
301
      var e = a[i];
      if(isArray(e) || e.forEach){
        e.forEach((x)=>f.call(this, parent,x));
        continue;
      }
      if('string'===typeof e
         || 'number'===typeof e
         || 'boolean'===typeof e) e = this.text(e);

      parent.appendChild(e);
    }
    return parent;
  };

  dom.input = function(type){
    return this.attr(this.create('input'), 'type', type);







|
>







302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
      var e = a[i];
      if(isArray(e) || e.forEach){
        e.forEach((x)=>f.call(this, parent,x));
        continue;
      }
      if('string'===typeof e
         || 'number'===typeof e
         || 'boolean'===typeof e
         || e instanceof Error) e = this.text(e);
      parent.appendChild(e);
    }
    return parent;
  };

  dom.input = function(type){
    return this.attr(this.create('input'), 'type', type);
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
      };
    }
    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){







|







495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
      };
    }
    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 argument",i,"of",
                     arguments,arguments[i]);
        continue;
      }
      if(e.forEach){
        e.forEach((x)=>f.mv(dest, x));
      }else{
        while(e.firstChild){
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
    return e;
  };

  /**
     "Blinks" the given element a single time for the given number of
     milliseconds, defaulting (if the 2nd argument is falsy or not a
     number) to flashOnce.defaultTimeMs. If a 3rd argument is passed
     in, it must be a function, and it gets callback back at the end
     of the asynchronous flashing processes.

     This will only activate once per element during that timeframe -
     further calls will become no-ops until the blink is
     completed. This routine adds a dataset member to the element for
     the duration of the blink, to allow it to block multiple blinks.

     If passed 2 arguments and the 2nd is a function, it behaves as if







|
|







628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
    return e;
  };

  /**
     "Blinks" the given element a single time for the given number of
     milliseconds, defaulting (if the 2nd argument is falsy or not a
     number) to flashOnce.defaultTimeMs. If a 3rd argument is passed
     in, it must be a function, and it gets called at the end of the
     asynchronous flashing processes.

     This will only activate once per element during that timeframe -
     further calls will become no-ops until the blink is
     completed. This routine adds a dataset member to the element for
     the duration of the blink, to allow it to block multiple blinks.

     If passed 2 arguments and the 2nd is a function, it behaves as if
705
706
707
708
709
710
711



































712
713
714
715
716
717
718
      let k;
      for(k in style){
        if(style.hasOwnProperty(k)) e.style[k] = style[k];
      }
    }
    return e;
  };




































  /**
     Parses a string as HTML.

     Usages:

     Array (htmlString)







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







721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
      let k;
      for(k in style){
        if(style.hasOwnProperty(k)) e.style[k] = style[k];
      }
    }
    return e;
  };

  /**
     Given a DOM element, this routine measures its "effective
     height", which is the bounding top/bottom range of this element
     and all of its children, recursively. For some DOM structure
     cases, a parent may have a reported height of 0 even though
     children have non-0 sizes.

     Returns 0 if !e or if the element really has no height.
  */
  dom.effectiveHeight = function f(e){
    if(!e) return 0;
    if(!f.measure){
      f.measure = function callee(e, depth){
        if(!e) return;
        const m = e.getBoundingClientRect();
        if(0===depth){
          callee.top = m.top;
          callee.bottom = m.bottom;
        }else{
          callee.top = m.top ? Math.min(callee.top, m.top) : callee.top;
          callee.bottom = Math.max(callee.bottom, m.bottom);
        }
        Array.prototype.forEach.call(e.children,(e)=>callee(e,depth+1));
        if(0===depth){
          //console.debug("measure() height:",e.className, callee.top, callee.bottom, (callee.bottom - callee.top));
          f.extra += callee.bottom - callee.top;
        }
        return f.extra;
      };
    }
    f.extra = 0;
    f.measure(e,0);
    return f.extra;
  };

  /**
     Parses a string as HTML.

     Usages:

     Array (htmlString)

Changes to src/fossil.popupwidget.js.

10
11
12
13
14
15
16
17
18
19
20
21








22
23
24
25
26
27
28

  /**
     Creates a new tooltip-like widget using the given options object.

     Options:

     .refresh: callback which is called just before the tooltip is
     revealed or moved. It must refresh the contents of the tooltip,
     if needed, by applying the content to/within this.e, which is the
     base DOM element for the tooltip (and is a child of
     document.body). If the contents are static and set up via the
     .init option then this callback is not needed.









     .adjustX: an optional callback which is called when the tooltip
     is to be displayed at a given position and passed the X
     viewport-relative coordinate. This routine must either return its
     argument as-is or return an adjusted value. The intent is to
     allow a given tooltip may be positioned more appropriately for a
     given context, if needed (noting that the desired position can,







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







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

  /**
     Creates a new tooltip-like widget using the given options object.

     Options:

     .refresh: callback which is called just before the tooltip is
     revealed. It must refresh the contents of the tooltip, if needed,
     by applying the content to/within this.e, which is the base DOM
     element for the tooltip (and is a child of document.body). If the
     contents are static and set up via the .init option then this
     callback is not needed. When moving an already-shown tooltip,
     this is *not* called. It arguably should be, but the fact is that
     we often have to show() a popup twice in a row without hiding it
     between those calls: once to get its computed size and another to
     move it by some amount relative to that size. If the state of the
     popup depends on its position and a "double-show()" is needed
     then the client must hide() the popup between the two calls to
     show() in order to force a call to refresh() on the second
     show().

     .adjustX: an optional callback which is called when the tooltip
     is to be displayed at a given position and passed the X
     viewport-relative coordinate. This routine must either return its
     argument as-is or return an adjusted value. The intent is to
     allow a given tooltip may be positioned more appropriately for a
     given context, if needed (noting that the desired position can,
39
40
41
42
43
44
45
46



47
48
49
50
51
52
53
     removed from the object immediately after it is called.

     All callback options are called with the PopupWidget object as
     their "this".


     .cssClass: optional CSS class, or list of classes, to apply to
     the new element.




     .style: optional object of properties to copy directly into
     the element's style object.     

     The options passed to this constructor get normalized into a
     separate object which includes any default values for options not
     provided by the caller. That object is available this the







|
>
>
>







47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
     removed from the object immediately after it is called.

     All callback options are called with the PopupWidget object as
     their "this".


     .cssClass: optional CSS class, or list of classes, to apply to
     the new element. In addition to any supplied here (or inherited
     from the default), the class "fossil-PopupWidget" is always set
     in order to allow certain app-internal CSS to account for popup
     windows in special cases.

     .style: optional object of properties to copy directly into
     the element's style object.     

     The options passed to this constructor get normalized into a
     separate object which includes any default values for options not
     provided by the caller. That object is available this the
74
75
76
77
78
79
80
81

82
83
84
85
86
87
88
     tip.show(50, 100);
     // ^^^ viewport-relative coordinates. See show() for other options.

  */
  F.PopupWidget = function f(opt){
    opt = F.mergeLastWins(f.defaultOptions,opt);
    this.options = opt;
    const e = this.e = D.addClass(D.div(), opt.cssClass);

    this.show(false);
    if(opt.style){
      let k;
      for(k in opt.style){
        if(opt.style.hasOwnProperty(k)) e.style[k] = opt.style[k];
      }
    }







|
>







85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
     tip.show(50, 100);
     // ^^^ viewport-relative coordinates. See show() for other options.

  */
  F.PopupWidget = function f(opt){
    opt = F.mergeLastWins(f.defaultOptions,opt);
    this.options = opt;
    const e = this.e = D.addClass(D.div(), opt.cssClass,
                                  "fossil-PopupWidget");
    this.show(false);
    if(opt.style){
      let k;
      for(k in opt.style){
        if(opt.style.hasOwnProperty(k)) e.style[k] = opt.style[k];
      }
    }
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
       the given element (adjusted slightly).

       For the latter two, this.options.adjustX() and adjustY() will
       be called to adjust it further.

       Returns this object.





       Sidebar: showing/hiding the widget is, as is conventional for
       this framework, done by removing/adding the 'hidden' CSS class
       to it, so that class must be defined appropriately.
    */
    show: function(){
      var x = undefined, y = undefined, showIt;

      if(2===arguments.length){
        x = arguments[0];
        y = arguments[1];
        showIt = true;
      }else if(1===arguments.length){
        if(arguments[0] instanceof HTMLElement){
          const p = arguments[0];
          const r = p.getBoundingClientRect();
          x = r.x + r.x/5;
          y = r.y - r.height/2;
          showIt = true;
        }else{
          showIt = !!arguments[0];
        }
      }
      if(showIt){
        this.refresh();
        x = this.options.adjustX.call(this,x);
        y = this.options.adjustY.call(this,y);
        x += window.pageXOffset;
        y += window.pageYOffset;
      }
      if(showIt){
        if('number'===typeof x && 'number'===typeof y){
          this.e.style.left = x+"px";
          this.e.style.top = y+"px";
        }
        D.removeClass(this.e, 'hidden');
      }else{
        D.addClass(this.e, 'hidden');
        this.e.style.removeProperty('left');
        this.e.style.removeProperty('top');
      }
      return this;
    },












    hide: function(){return this.show(false)}






























  }/*F.PopupWidget.prototype*/;

  /**
     Internal impl for F.toast() and friends.

     args:








>
>
>
>





|
>
















|



















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







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
       the given element (adjusted slightly).

       For the latter two, this.options.adjustX() and adjustY() will
       be called to adjust it further.

       Returns this object.

       If this call will reveal the element then it calls
       this.refresh() to update the UI state. If the element was
       already revealed, the call to refresh() is skipped.

       Sidebar: showing/hiding the widget is, as is conventional for
       this framework, done by removing/adding the 'hidden' CSS class
       to it, so that class must be defined appropriately.
    */
    show: function(){
      var x = undefined, y = undefined, showIt,
          wasShown = !this.e.classList.contains('hidden');
      if(2===arguments.length){
        x = arguments[0];
        y = arguments[1];
        showIt = true;
      }else if(1===arguments.length){
        if(arguments[0] instanceof HTMLElement){
          const p = arguments[0];
          const r = p.getBoundingClientRect();
          x = r.x + r.x/5;
          y = r.y - r.height/2;
          showIt = true;
        }else{
          showIt = !!arguments[0];
        }
      }
      if(showIt){
        if(!wasShown) this.refresh();
        x = this.options.adjustX.call(this,x);
        y = this.options.adjustY.call(this,y);
        x += window.pageXOffset;
        y += window.pageYOffset;
      }
      if(showIt){
        if('number'===typeof x && 'number'===typeof y){
          this.e.style.left = x+"px";
          this.e.style.top = y+"px";
        }
        D.removeClass(this.e, 'hidden');
      }else{
        D.addClass(this.e, 'hidden');
        this.e.style.removeProperty('left');
        this.e.style.removeProperty('top');
      }
      return this;
    },

    /**
       Equivalent to show(false), but may be overridden by instances,
       so long as they also call this.show(false) to perform the
       actual hiding. Overriding can be used to clean up any state so
       that the next call to refresh() (before the popup is show()n
       again) can recognize whether it needs to do something, noting
       that it's legal, and sometimes necessary, to call show()
       multiple times without needing/wanting to completely refresh
       the popup between each call (e.g. when moving the popup after
       it's been show()n).
    */
    hide: function(){return this.show(false)},

    /**
       A convenience method which adds click handlers to this popup's
       main element and document.body to hide (via hide()) the popup
       when either element is clicked or the ESC key is pressed. Only
       call this once per instance, if at all. Returns this;

       The first argument specifies whether a click handler on this
       object is installed. The second specifies whether a click
       outside of this object should close it. The third specifies
       whether an ESC handler is installed.

       Passing no arguments is equivalent to passing (true,true,true),
       and passing fewer arguments defaults the unpassed parameters to
       true.
    */
    installHideHandlers: function f(onClickSelf, onClickOther, onEsc){
      if(!arguments.length) onClickSelf = onClickOther = onEsc = true;
      else if(1===arguments.length) onClickOther = onEsc = true;
      else if(2===arguments.length) onEsc = true;
      if(onClickSelf) this.e.addEventListener('click', ()=>this.hide(), false);
      if(onClickOther) document.body.addEventListener('click', ()=>this.hide(), true);
      if(onEsc){
        const self = this;
        document.body.addEventListener('keydown', function(ev){
          if(self.isShown() && 27===ev.which) self.hide();
        }, true);
      }
      return this;
    }
  }/*F.PopupWidget.prototype*/;

  /**
     Internal impl for F.toast() and friends.

     args:

206
207
208
209
210
211
212

213
214
215
216
217
218
219
     Returns F.toast.
  */
  const toastImpl = function f(cssClass, durationMult, argsObject){
    if(!f.toaster){
      f.toaster = new F.PopupWidget({
        cssClass: 'fossil-toast-message'
      });

    }
    const T = f.toaster;
    if(f._timer) clearTimeout(f._timer);
    D.clearElement(T.e);
    if(f._prevCssClass) T.e.classList.remove(f._prevCssClass);
    if(cssClass) T.e.classList.add(cssClass);
    f._prevCssClass = cssClass;







>







264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
     Returns F.toast.
  */
  const toastImpl = function f(cssClass, durationMult, argsObject){
    if(!f.toaster){
      f.toaster = new F.PopupWidget({
        cssClass: 'fossil-toast-message'
      });
      D.attr(f.toaster.e, 'role', 'alert');
    }
    const T = f.toaster;
    if(f._timer) clearTimeout(f._timer);
    D.clearElement(T.e);
    if(f._prevCssClass) T.e.classList.remove(f._prevCssClass);
    if(cssClass) T.e.classList.add(cssClass);
    f._prevCssClass = cssClass;
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
  F.toast = {
    config: {
      position: { x: 5, y: 5 /*viewport-relative, pixels*/ },
      displayTimeMs: 3000
    },
    /**
       Convenience wrapper around a PopupWidget which pops up a shared
       PopupWidget instance to show toast-style messages (commonly seen
       on Android). Its arguments may be anything suitable for passing
       to fossil.dom.append(), and each argument is first append()ed to
       the toast widget, then the widget is shown for
       F.toast.config.displayTimeMs milliseconds. This is called while
       a toast is currently being displayed, the first will be overwritten
       and the time until the message is hidden will be reset.


       The toast is always shown at the viewport-relative coordinates
       defined by the F.toast.config.position.

       The toaster's DOM element has the CSS classes fossil-tooltip
       and fossil-toast, so can be style via those.




    */
    message: function(/*...*/){
      return toastImpl(false,1, arguments);
    },
    /**
       Displays a toast with the 'warning' CSS class assigned to it. It
       displays for 1.5 times as long as a normal toast.
    */
    warning: function(/*...*/){
      return toastImpl('warning',1.5,arguments);
    },
    /**
       Displays a toast with the 'warning' CSS class assigned to it. It
       displays for twice as long as a normal toast.
    */
    error: function(/*...*/){
      return toastImpl('error',2,arguments);
    }
  }/*F.toast*/;








|
|
|
|
|
|
|
>




|
|
>
>
>
>












|







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
  F.toast = {
    config: {
      position: { x: 5, y: 5 /*viewport-relative, pixels*/ },
      displayTimeMs: 3000
    },
    /**
       Convenience wrapper around a PopupWidget which pops up a shared
       PopupWidget instance to show toast-style messages (commonly
       seen on Android). Its arguments may be anything suitable for
       passing to fossil.dom.append(), and each argument is first
       append()ed to the toast widget, then the widget is shown for
       F.toast.config.displayTimeMs milliseconds. If this is called
       while a toast is currently being displayed, the first will be
       overwritten and the time until the message is hidden will be
       reset.

       The toast is always shown at the viewport-relative coordinates
       defined by the F.toast.config.position.

       The toaster's DOM element has the CSS class fossil-tooltip
       and fossil-toast-message, so can be style via those.

       The 3 main message types (message, warning, error) each get a
       CSS class with that same name added to them. Thus CSS can
       select on .fossil-toast-message.error to style error toasts.
    */
    message: function(/*...*/){
      return toastImpl(false,1, arguments);
    },
    /**
       Displays a toast with the 'warning' CSS class assigned to it. It
       displays for 1.5 times as long as a normal toast.
    */
    warning: function(/*...*/){
      return toastImpl('warning',1.5,arguments);
    },
    /**
       Displays a toast with the 'error' CSS class assigned to it. It
       displays for twice as long as a normal toast.
    */
    error: function(/*...*/){
      return toastImpl('error',2,arguments);
    }
  }/*F.toast*/;

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
       during initialization and stashed away for use in a PopupWidget
       when the botton is clicked.

    */
    setup: function f(){
      if(!f.hasOwnProperty('clickHandler')){
        f.clickHandler = function fch(ev){

          if(!fch.popup){
            fch.popup = new F.PopupWidget({
              cssClass: ['fossil-tooltip', 'help-buttonlet-content'],
              refresh: function(){
              }
            });
            fch.popup.e.style.maxWidth = '80%'/*of body*/;
            const hide = ()=>fch.popup.hide();
            fch.popup.e.addEventListener('click', hide, false);
            document.body.addEventListener('click', hide, true);
            document.body.addEventListener('keydown', function(ev){
              if(fch.popup.isShown() && 27===ev.which){
                fch.popup.hide();
              }
            }, true);
          }
          D.append(D.clearElement(fch.popup.e), ev.target.$helpContent);
          var popupRect = ev.target.getClientRects()[0];
          var x = popupRect.left, y = popupRect.top;
          if(x<0) x = 0;
          if(y<0) y = 0;
          /* Shift the help around a bit to "better" fit the
             screen. However, fch.popup.e.getClientRects() is empty
             until the popup is shown, so we have to show it,
             calculate the resulting size, then move and/or resize it.

             This algorithm/these heuristics can certainly be improved
             upon.
          */





















          fch.popup.show(x, y);
          x = popupRect.left, y = popupRect.top;
          popupRect = fch.popup.e.getBoundingClientRect();
          const rectBody = document.body.getClientRects()[0];
          if(popupRect.right > rectBody.right){
            x -= (popupRect.right - rectBody.right);
          }







>







<
<
<
<
<
|
|
<
<

<
<
<
<








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







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
       during initialization and stashed away for use in a PopupWidget
       when the botton is clicked.

    */
    setup: function f(){
      if(!f.hasOwnProperty('clickHandler')){
        f.clickHandler = function fch(ev){
          ev.preventDefault();
          if(!fch.popup){
            fch.popup = new F.PopupWidget({
              cssClass: ['fossil-tooltip', 'help-buttonlet-content'],
              refresh: function(){
              }
            });
            fch.popup.e.style.maxWidth = '80%'/*of body*/;





            fch.popup.installHideHandlers();
          }


          D.append(D.clearElement(fch.popup.e), ev.target.$helpContent);




          /* Shift the help around a bit to "better" fit the
             screen. However, fch.popup.e.getClientRects() is empty
             until the popup is shown, so we have to show it,
             calculate the resulting size, then move and/or resize it.

             This algorithm/these heuristics can certainly be improved
             upon.
          */
          var popupRect, rectElem = ev.target;
          while(rectElem){
            popupRect = rectElem.getClientRects()[0]/*undefined if off-screen!*/;
            if(popupRect) break;
            rectElem = rectElem.parentNode;
          }
          if(!popupRect) popupRect = {x:0, y:0, left:0, right:0};
          var x = popupRect.left, y = popupRect.top;
          if(x<0) x = 0;
          if(y<0) y = 0;
          if(rectElem){
            /* Try to ensure that the popup's z-level is higher than this element's */
            const rz = window.getComputedStyle(rectElem).zIndex;
            var myZ;
            if(rz && !isNaN(+rz)){
              myZ = +rz + 1;
            }else{
              myZ = 10000/*guess!*/;
            }
            fch.popup.e.style.zIndex = myZ;
          }
          fch.popup.show(x, y);
          x = popupRect.left, y = popupRect.top;
          popupRect = fch.popup.e.getBoundingClientRect();
          const rectBody = document.body.getClientRects()[0];
          if(popupRect.right > rectBody.right){
            x -= (popupRect.right - rectBody.right);
          }

Changes to src/fossil.storage.js.

63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

     See: https://fossil-scm.org/forum/forumpost/4afc4d34de

     Sidebar: it might seem odd to provide a key prefix and stick all
     properties in the topmost level of the storage object. We do that
     because adding a layer of object to sandbox each repo would mean
     (de)serializing that whole tree on every storage property change
     (and we update storage often during editing
     sessions). e.g. instead of storageObject.projectName.foo we have
     storageObject[storageKeyPrefix+'foo']. That's soley for
     efficiency's sake (in terms of battery life and
     environment-internal storage-level effort). Even so, it might
     (or might not) be useful to do that someday.
  */
  const storageKeyPrefix = (
    $storageHolder===$storage/*localStorage or sessionStorage*/
      ? (
        F.config.projectCode || F.config.projectName
          || F.config.shortProjectName || window.location.pathname
      )+'::' : (







|
|


|
|







63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82

     See: https://fossil-scm.org/forum/forumpost/4afc4d34de

     Sidebar: it might seem odd to provide a key prefix and stick all
     properties in the topmost level of the storage object. We do that
     because adding a layer of object to sandbox each repo would mean
     (de)serializing that whole tree on every storage property change
     (and we update storage often during editing sessions).
     e.g. instead of storageObject.projectName.foo we have
     storageObject[storageKeyPrefix+'foo']. That's soley for
     efficiency's sake (in terms of battery life and
     environment-internal storage-level effort). Even so, it might (or
     might not) be useful to do that someday.
  */
  const storageKeyPrefix = (
    $storageHolder===$storage/*localStorage or sessionStorage*/
      ? (
        F.config.projectCode || F.config.projectName
          || F.config.shortProjectName || window.location.pathname
      )+'::' : (
99
100
101
102
103
104
105







106
107
108
109
110
111
112
    /** Sets storage key k to JSON.stringify(v). */
    setJSON: (k,v)=>$storage.setItem(storageKeyPrefix+k,JSON.stringify(v)),
    /** Returns the value for the given storage key, or
        dflt if the key is not found in the storage. */
    get: (k,dflt)=>$storageHolder.hasOwnProperty(
      storageKeyPrefix+k
    ) ? $storage.getItem(storageKeyPrefix+k) : dflt,







    /** Returns the JSON.parse()'d value of the given
        storage key's value, or dflt is the key is not
        found or JSON.parse() fails. */
    getJSON: function f(k,dflt){
      try {
        const x = this.get(k,f);
        return x===f ? dflt : JSON.parse(x);







>
>
>
>
>
>
>







99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
    /** Sets storage key k to JSON.stringify(v). */
    setJSON: (k,v)=>$storage.setItem(storageKeyPrefix+k,JSON.stringify(v)),
    /** Returns the value for the given storage key, or
        dflt if the key is not found in the storage. */
    get: (k,dflt)=>$storageHolder.hasOwnProperty(
      storageKeyPrefix+k
    ) ? $storage.getItem(storageKeyPrefix+k) : dflt,
    /** Returns true if the given key has a value of "true".  If the
        key is not found, it returns true if the boolean value of dflt
        is "true". (Remember that JS persistent storage values are all
        strings.) */
    getBool: function(k,dflt){
      return 'true'===this.get(k,''+(!!dflt));
    },
    /** Returns the JSON.parse()'d value of the given
        storage key's value, or dflt is the key is not
        found or JSON.parse() fails. */
    getJSON: function f(k,dflt){
      try {
        const x = this.get(k,f);
        return x===f ? dflt : JSON.parse(x);

Changes to src/hook.c.

235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
**            --dry-run          Print the script on stdout rather than run it
**            --base-rcvid  N    Pretend that the hook-last-rcvid value is N
**            --new-rcvid M      Pretend that the last rcvid valud is M
**            --aux-file NAME    NAME is substituted for %A in the script
**
**        The --base-rcvid and --new-rcvid options are silently ignored if
**        the hook type is not "after-receive".  The default values for
**        --base-rcvid and --new-rcvid cause the last recieve to be processed.
*/
void hook_cmd(void){
  const char *zCmd;
  int nCmd;
  db_find_and_open_repository(0, 0);
  if( g.argc<3 ){
    usage("SUBCOMMAND ...");







|







235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
**            --dry-run          Print the script on stdout rather than run it
**            --base-rcvid  N    Pretend that the hook-last-rcvid value is N
**            --new-rcvid M      Pretend that the last rcvid valud is M
**            --aux-file NAME    NAME is substituted for %A in the script
**
**        The --base-rcvid and --new-rcvid options are silently ignored if
**        the hook type is not "after-receive".  The default values for
**        --base-rcvid and --new-rcvid cause the last receive to be processed.
*/
void hook_cmd(void){
  const char *zCmd;
  int nCmd;
  db_find_and_open_repository(0, 0);
  if( g.argc<3 ){
    usage("SUBCOMMAND ...");

Changes to src/import.c.

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
    flatFlag = find_option("flat", 0, 0)!=0;
    gsvn.zTrunk = find_option("trunk", 0, 1);
    gsvn.zBranches = find_option("branches", 0, 1);
    gsvn.zTags = find_option("tags", 0, 1);
    gsvn.revFlag = find_option("rev-tags", 0, 0)
                || (incrFlag && !find_option("no-rev-tags", 0, 0));
  }else if( gitFlag ){

    markfile_in = find_option("import-marks", 0, 1);
    markfile_out = find_option("export-marks", 0, 1);
    if( !(ggit.zMasterName = find_option("rename-master", 0, 1)) ){
      ggit.zMasterName = "master";
    }
    ggit.authorFlag = find_option("use-author", 0, 0)!=0;
    /*
    ** Extract --attribute 'emailaddr username' args that will populate
    ** new 'fx_' table to later match username for check-in attribution.
    */
    const char *zGitUser = find_option("attribute", 0, 1);
    while( zGitUser != 0 ){

      ggit.gitUserInfo = fossil_realloc(ggit.gitUserInfo, ++ggit.nGitAttr
       * sizeof(ggit.gitUserInfo[0]));
      char *currGitUser = fossil_strdup(zGitUser);
      ggit.gitUserInfo[ggit.nGitAttr-1].zEmail = next_token(&currGitUser);
      ggit.gitUserInfo[ggit.nGitAttr-1].zUser = rest_of_line(&currGitUser);
      zGitUser = find_option("attribute", 0, 1);
    }
  }
  verify_all_options();








>










|

>


|







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
    flatFlag = find_option("flat", 0, 0)!=0;
    gsvn.zTrunk = find_option("trunk", 0, 1);
    gsvn.zBranches = find_option("branches", 0, 1);
    gsvn.zTags = find_option("tags", 0, 1);
    gsvn.revFlag = find_option("rev-tags", 0, 0)
                || (incrFlag && !find_option("no-rev-tags", 0, 0));
  }else if( gitFlag ){
    const char *zGitUser;
    markfile_in = find_option("import-marks", 0, 1);
    markfile_out = find_option("export-marks", 0, 1);
    if( !(ggit.zMasterName = find_option("rename-master", 0, 1)) ){
      ggit.zMasterName = "master";
    }
    ggit.authorFlag = find_option("use-author", 0, 0)!=0;
    /*
    ** Extract --attribute 'emailaddr username' args that will populate
    ** new 'fx_' table to later match username for check-in attribution.
    */
    zGitUser = find_option("attribute", 0, 1);
    while( zGitUser != 0 ){
      char *currGitUser;
      ggit.gitUserInfo = fossil_realloc(ggit.gitUserInfo, ++ggit.nGitAttr
       * sizeof(ggit.gitUserInfo[0]));
      currGitUser = fossil_strdup(zGitUser);
      ggit.gitUserInfo[ggit.nGitAttr-1].zEmail = next_token(&currGitUser);
      ggit.gitUserInfo[ggit.nGitAttr-1].zUser = rest_of_line(&currGitUser);
      zGitUser = find_option("attribute", 0, 1);
    }
  }
  verify_all_options();

Changes to src/info.c.

501
502
503
504
505
506
507
508
509
510
511
512
513
514
515

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  rid = name_to_rid_www("name");
  if( rid==0 ){
    style_header("Check-in Information Error");
    @ No such object: %h(g.argv[2])
    style_finish_page("ci_tags");
    return;
  }
  zHash = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
  style_header("Tags and Properties");
  @ <h1>Tags and Properties for Check-In \
  @ %z(href("%R/ci/%!S",zHash))%S(zHash)</a></h1>
  db_prepare(&q,







|







501
502
503
504
505
506
507
508
509
510
511
512
513
514
515

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  rid = name_to_rid_www("name");
  if( rid==0 ){
    style_header("Check-in Information Error");
    @ No such object: %h(g.argv[2])
    style_finish_page();
    return;
  }
  zHash = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
  style_header("Tags and Properties");
  @ <h1>Tags and Properties for Check-In \
  @ %z(href("%R/ci/%!S",zHash))%S(zHash)</a></h1>
  db_prepare(&q,
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
  blob_zero(&sql);
  blob_append(&sql, timeline_query_for_www(), -1);
  blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
  db_prepare(&q, "%s", blob_sql_text(&sql));
  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
                     0, 0, 0, rid, 0, 0);
  db_finalize(&q);
  style_finish_page("ci_tags");
}

/*
** WEBPAGE: vinfo
** WEBPAGE: ci
** URL:  /ci/ARTIFACTID
**  OR:  /ci?name=ARTIFACTID







|







593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
  blob_zero(&sql);
  blob_append(&sql, timeline_query_for_www(), -1);
  blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
  db_prepare(&q, "%s", blob_sql_text(&sql));
  www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
                     0, 0, 0, rid, 0, 0);
  db_finalize(&q);
  style_finish_page();
}

/*
** WEBPAGE: vinfo
** WEBPAGE: ci
** URL:  /ci/ARTIFACTID
**  OR:  /ci?name=ARTIFACTID
627
628
629
630
631
632
633

634
635
636
637
638
639
640
641
642
643
644
645
646
  const char *zW;      /* URL param for ignoring whitespace */
  const char *zPage = "vinfo";  /* Page that shows diffs */
  const char *zPageHide = "ci"; /* Page that hides diffs */
  const char *zBrName; /* Branch name */

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }

  zName = P("name");
  rid = name_to_rid_www("name");
  if( rid==0 ){
    style_header("Check-in Information Error");
    @ No such object: %h(g.argv[2])
    style_finish_page("vinfo");
    return;
  }
  zRe = P("regex");
  if( zRe ) re_compile(&pRe, zRe, 0);
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
  zParent = db_text(0,
    "SELECT uuid FROM plink, blob"







>





|







627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
  const char *zW;      /* URL param for ignoring whitespace */
  const char *zPage = "vinfo";  /* Page that shows diffs */
  const char *zPageHide = "ci"; /* Page that hides diffs */
  const char *zBrName; /* Branch name */

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  style_set_current_feature("vinfo");
  zName = P("name");
  rid = name_to_rid_www("name");
  if( rid==0 ){
    style_header("Check-in Information Error");
    @ No such object: %h(g.argv[2])
    style_finish_page();
    return;
  }
  zRe = P("regex");
  if( zRe ) re_compile(&pRe, zRe, 0);
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
  zParent = db_text(0,
    "SELECT uuid FROM plink, blob"
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
    const char *zOldName = db_column_text(&q3, 4);
    append_file_change_line(zUuid, zName, zOld, zNew, zOldName, 
                            diffFlags,pRe,mperm);
  }
  db_finalize(&q3);
  append_diff_javascript(diffType==2);
  cookie_render();
  style_finish_page("vinfo");
}

/*
** WEBPAGE: winfo
** URL:  /winfo?name=HASH
**
** Display information about a wiki page.







|







939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
    const char *zOldName = db_column_text(&q3, 4);
    append_file_change_line(zUuid, zName, zOld, zNew, zOldName, 
                            diffFlags,pRe,mperm);
  }
  db_finalize(&q3);
  append_diff_javascript(diffType==2);
  cookie_render();
  style_finish_page();
}

/*
** WEBPAGE: winfo
** URL:  /winfo?name=HASH
**
** Display information about a wiki page.
960
961
962
963
964
965
966

967
968
969
970
971
972
973
974
975
976
977
978
  int modPending;
  const char *zModAction;
  int tagid;
  int ridNext;

  login_check_credentials();
  if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }

  rid = name_to_rid_www("name");
  if( rid==0 || (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))==0 ){
    style_header("Wiki Page Information Error");
    @ No such object: %h(P("name"))
    style_finish_page("winfo");
    return;
  }
  if( g.perm.ModWiki && (zModAction = P("modaction"))!=0 ){
    if( strcmp(zModAction,"delete")==0 ){
      moderation_disapprove(rid);
      /*
      ** Next, check if the wiki page still exists; if not, we cannot







>




|







961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
  int modPending;
  const char *zModAction;
  int tagid;
  int ridNext;

  login_check_credentials();
  if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
  style_set_current_feature("winfo");
  rid = name_to_rid_www("name");
  if( rid==0 || (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))==0 ){
    style_header("Wiki Page Information Error");
    @ No such object: %h(P("name"))
    style_finish_page();
    return;
  }
  if( g.perm.ModWiki && (zModAction = P("modaction"))!=0 ){
    if( strcmp(zModAction,"delete")==0 ){
      moderation_disapprove(rid);
      /*
      ** Next, check if the wiki page still exists; if not, we cannot
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
  @ <div class="section">Content</div>
  blob_init(&wiki, pWiki->zWiki, -1);
  safe_html_context(DOCSRC_WIKI);
  wiki_render_by_mimetype(&wiki, pWiki->zMimetype);
  blob_reset(&wiki);
  manifest_destroy(pWiki);
  document_emit_js();
  style_finish_page("winfo");
}

/*
** Find an check-in based on query parameter zParam and parse its
** manifest.  Return the number of errors.
*/
static Manifest *vdiff_parse_manifest(const char *zParam, int *pRid){







|







1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
  @ <div class="section">Content</div>
  blob_init(&wiki, pWiki->zWiki, -1);
  safe_html_context(DOCSRC_WIKI);
  wiki_render_by_mimetype(&wiki, pWiki->zMimetype);
  blob_reset(&wiki);
  manifest_destroy(pWiki);
  document_emit_js();
  style_finish_page();
}

/*
** Find an check-in based on query parameter zParam and parse its
** manifest.  Return the number of errors.
*/
static Manifest *vdiff_parse_manifest(const char *zParam, int *pRid){
1208
1209
1210
1211
1212
1213
1214

1215
1216
1217
1218
1219
1220
1221
  zFrom = P("from");
  zTo = P("to");
  if(zGlob && !*zGlob){
    zGlob = NULL;
  }
  diffFlags = construct_diff_flags(diffType);
  zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";

  if( zBranch==0 ){
    style_submenu_element("Path", "%R/timeline?me=%T&you=%T", zFrom, zTo);
  }
  if( diffType!=0 ){
    style_submenu_element("Hide Diff", "%R/vdiff?%s&diff=0%s%T%s",
                          zQuery,
                          zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);







>







1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
  zFrom = P("from");
  zTo = P("to");
  if(zGlob && !*zGlob){
    zGlob = NULL;
  }
  diffFlags = construct_diff_flags(diffType);
  zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
  style_set_current_feature("vdiff");
  if( zBranch==0 ){
    style_submenu_element("Path", "%R/timeline?me=%T&you=%T", zFrom, zTo);
  }
  if( diffType!=0 ){
    style_submenu_element("Hide Diff", "%R/vdiff?%s&diff=0%s%T%s",
                          zQuery,
                          zGlob ? "&glob=" : "", zGlob ? zGlob : "", zW);
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
      pFileFrom = manifest_file_next(pFrom, 0);
      pFileTo = manifest_file_next(pTo, 0);
    }
  }
  manifest_destroy(pFrom);
  manifest_destroy(pTo);
  append_diff_javascript(diffType==2);
  style_finish_page("vdiff");
}

#if INTERFACE
/*
** Possible return values from object_description()
*/
#define OBJTYPE_CHECKIN    0x0001







|







1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
      pFileFrom = manifest_file_next(pFrom, 0);
      pFileTo = manifest_file_next(pTo, 0);
    }
  }
  manifest_destroy(pFrom);
  manifest_destroy(pTo);
  append_diff_javascript(diffType==2);
  style_finish_page();
}

#if INTERFACE
/*
** Possible return values from object_description()
*/
#define OBJTYPE_CHECKIN    0x0001
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
    hyperlink_to_user(zUser,zDate,",");
    @ size: %d(szFile))
    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?filename=%T&checkin=%!S",zName,zVers))[edit]</a>
      }
    }
    cnt++;
    if( pDownloadName && blob_size(pDownloadName)==0 ){
      blob_append(pDownloadName, zName, -1);







|







1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
    hyperlink_to_user(zUser,zDate,",");
    @ size: %d(szFile))
    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?uf=%!S",zUuid))[check-ins&nbsp;using]</a>
      if( fileedit_is_editable(zName) ){
        @ %z(href("%R/fileedit?filename=%T&checkin=%!S",zName,zVers))[edit]</a>
      }
    }
    cnt++;
    if( pDownloadName && blob_size(pDownloadName)==0 ){
      blob_append(pDownloadName, zName, -1);
1728
1729
1730
1731
1732
1733
1734

1735
1736
1737
1738
1739
1740
1741
    return;
  }

  zV1 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v1);
  zV2 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v2);
  diffFlags = construct_diff_flags(diffType) | DIFF_HTML;


  style_header("Diff");
  style_submenu_checkbox("w", "Ignore Whitespace", 0, 0);
  if( diffType==2 ){
    style_submenu_element("Unified Diff", "%R/fdiff?v1=%T&v2=%T&diff=1",
                           P("v1"), P("v2"));
  }else{
    style_submenu_element("Side-by-side Diff", "%R/fdiff?v1=%T&v2=%T&diff=2",







>







1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
    return;
  }

  zV1 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v1);
  zV2 = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", v2);
  diffFlags = construct_diff_flags(diffType) | DIFF_HTML;

  style_set_current_feature("fdiff");
  style_header("Diff");
  style_submenu_checkbox("w", "Ignore Whitespace", 0, 0);
  if( diffType==2 ){
    style_submenu_element("Unified Diff", "%R/fdiff?v1=%T&v2=%T&diff=1",
                           P("v1"), P("v2"));
  }else{
    style_submenu_element("Side-by-side Diff", "%R/fdiff?v1=%T&v2=%T&diff=2",
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
  if( pRe ){
    @ <b>Only differences that match regular expression "%h(zRe)"
    @ are shown.</b>
  }
  @ <hr />
  append_diff(zV1, zV2, diffFlags, pRe);
  append_diff_javascript(diffType);
  style_finish_page("fdiff");
}

/*
** WEBPAGE: raw
** URL: /raw/ARTIFACTID
** URL: /raw?ci=BRANCH&filename=NAME
**







|







1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
  if( pRe ){
    @ <b>Only differences that match regular expression "%h(zRe)"
    @ are shown.</b>
  }
  @ <hr />
  append_diff(zV1, zV2, diffFlags, pRe);
  append_diff_javascript(diffType);
  style_finish_page();
}

/*
** WEBPAGE: raw
** URL: /raw/ARTIFACTID
** URL: /raw?ci=BRANCH&filename=NAME
**
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
      }else{
        zLine[k+1] = ' ';
        zLine[k+2] = ' ';
      }
    }
    zLine[53] = ' ';
    zLine[54] = ' ';

    for(j=0; j<16; j++){
      k = j+55;
      if( i+j<n ){
        unsigned char c = x[i+j];
        if( c>=0x20 && c<=0x7e ){
          zLine[k] = c;


















        }else{
          zLine[k] = '.';
        }
      }else{
        zLine[k] = 0;
      }
    }
    zLine[71] = 0;
    @ %h(zLine)
  }
}

/*
** WEBPAGE: hexdump
** URL: /hexdump?name=ARTIFACTID
**







>
|
<


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

|


|


|
|







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
      }else{
        zLine[k+1] = ' ';
        zLine[k+2] = ' ';
      }
    }
    zLine[53] = ' ';
    zLine[54] = ' ';
    cgi_append_content(zLine, 55);
    for(j=k=0; j<16; j++){

      if( i+j<n ){
        unsigned char c = x[i+j];
        if( c>'>' && c<=0x7e ){
          zLine[k++] = c;
        }else if( c=='>' ){
          zLine[k++] = '&';
          zLine[k++] = 'g';
          zLine[k++] = 't';
          zLine[k++] = ';';
        }else if( c=='<' ){
          zLine[k++] = '&';
          zLine[k++] = 'l';
          zLine[k++] = 't';
          zLine[k++] = ';';
        }else if( c=='&' ){
          zLine[k++] = '&';
          zLine[k++] = 'a';
          zLine[k++] = 'm';
          zLine[k++] = 'p';
          zLine[k++] = ';';
        }else if( c>=' ' ){
          zLine[k++] = c;
        }else{
          zLine[k++] = '.';
        }
      }else{
        break;
      }
    }
    zLine[k++] = '\n';
    cgi_append_content(zLine, k);
  }
}

/*
** WEBPAGE: hexdump
** URL: /hexdump?name=ARTIFACTID
**
1960
1961
1962
1963
1964
1965
1966







1967
1968
1969

1970
1971
1972
1973
1974
1975
1976
1977
  blob_zero(&downloadName);
  if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL;
  object_description(rid, objdescFlags, 0, &downloadName);
  style_submenu_element("Download", "%R/raw/%s?at=%T",
                        zUuid, file_tail(blob_str(&downloadName)));
  @ <hr />
  content_get(rid, &content);







  @ <blockquote><pre>
  hexdump(&content);
  @ </pre></blockquote>

  style_finish_page("hexdump");
}

/*
** Look for "ci" and "filename" query parameters.  If found, try to
** use them to extract the record ID of an artifact for the file.
**
** Also look for "fn" and "name" as an aliases for "filename".  If any







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







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
  blob_zero(&downloadName);
  if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL;
  object_description(rid, objdescFlags, 0, &downloadName);
  style_submenu_element("Download", "%R/raw/%s?at=%T",
                        zUuid, file_tail(blob_str(&downloadName)));
  @ <hr />
  content_get(rid, &content);
  if( !g.isHuman ){
    /* Prevent robots from running hexdump on megabyte-sized source files
    ** and there by eating up lots of CPU time and bandwidth.  There is
    ** no good reason for a robot to need a hexdump. */
    @ <p>A hex dump of this file is not available.
    @  Please download the raw binary file and generate a hex dump yourself.</p>
  }else{
    @ <blockquote><pre>
    hexdump(&content);
    @ </pre></blockquote>
  }
  style_finish_page();
}

/*
** Look for "ci" and "filename" query parameters.  If found, try to
** use them to extract the record ID of an artifact for the file.
**
** Also look for "fn" and "name" as an aliases for "filename".  If any
2232
2233
2234
2235
2236
2237
2238

2239
2240
2241
2242
2243
2244
2245
  char *zCIUuid = 0;
  int isSymbolicCI = 0;  /* ci= exists and is a symbolic name, not a hash */
  int isBranchCI = 0;    /* ci= refers to a branch name */
  char *zHeader = 0;

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }


  /* Capture and normalize the name= and ci= query parameters */
  if( zName==0 ){
    zName = P("filename");
    if( zName==0 ){
      zName = P("fn");
    }







>







2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
  char *zCIUuid = 0;
  int isSymbolicCI = 0;  /* ci= exists and is a symbolic name, not a hash */
  int isBranchCI = 0;    /* ci= refers to a branch name */
  char *zHeader = 0;

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  style_set_current_feature("artifact");

  /* Capture and normalize the name= and ci= query parameters */
  if( zName==0 ){
    zName = P("filename");
    if( zName==0 ){
      zName = P("fn");
    }
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
    if( isFile ){
      if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
      page_tree();
      return;
    }
    style_header("Missing name= query parameter");
    @ The name= query parameter is missing
    style_finish_page("artifact");
    return;
  }

  url_initialize(&url, g.zPath);
  url_add_parameter(&url, "name", zName);
  url_add_parameter(&url, "ci", zCI);








|







2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
    if( isFile ){
      if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
      page_tree();
      return;
    }
    style_header("Missing name= query parameter");
    @ The name= query parameter is missing
    style_finish_page();
    return;
  }

  url_initialize(&url, g.zPath);
  url_add_parameter(&url, "name", zName);
  url_add_parameter(&url, "ci", zCI);

2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
        @ File '%h(zName)' does not exist in this repository.
      }
    }else{
      style_header("No such artifact");
      @ Artifact '%h(zName)' does not exist in this repository.
    }
    if( rid==0 ){
      style_finish_page("artifact");
      return;
    }
  }

  if( descOnly || P("verbose")!=0 ){
    url_add_parameter(&url, "verbose", "1");
    objdescFlags |= OBJDESC_DETAIL;







|







2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
        @ File '%h(zName)' does not exist in this repository.
      }
    }else{
      style_header("No such artifact");
      @ Artifact '%h(zName)' does not exist in this repository.
    }
    if( rid==0 ){
      style_finish_page();
      return;
    }
  }

  if( descOnly || P("verbose")!=0 ){
    url_add_parameter(&url, "verbose", "1");
    objdescFlags |= OBJDESC_DETAIL;
2359
2360
2361
2362
2363
2364
2365

2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383

2384
2385
2386
2387
2388
2389
2390
        @ part of check-in %z(href("%R/info/%!S",zCIUuid))%s(zCI)</a></h2>
      }else{
        @ part of check-in %z(href("%R/info/%!S",zCIUuid))%S(zCIUuid)</a></h2>
      }
      blob_reset(&path);
    }
    style_submenu_element("Artifact", "%R/artifact/%S", zUuid);

    style_submenu_element("Annotate", "%R/annotate?filename=%T&checkin=%T",
                          zName, zCI);
    style_submenu_element("Blame", "%R/blame?filename=%T&checkin=%T",
                          zName, zCI);
    blob_init(&downloadName, zName, -1);
    objType = OBJTYPE_CONTENT;
  }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);
    if( asText ) objdescFlags &= ~OBJDESC_BASE;
    objType = object_description(rid, objdescFlags,
                                (isFile?zName:0), &downloadName);

  }
  if( !descOnly && P("download")!=0 ){
    cgi_redirectf("%R/raw/%s?at=%T",
          db_text("x", "SELECT uuid FROM blob WHERE rid=%d", rid),
          file_tail(blob_str(&downloadName)));
    /*NOTREACHED*/
  }







>


















>







2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
        @ part of check-in %z(href("%R/info/%!S",zCIUuid))%s(zCI)</a></h2>
      }else{
        @ part of check-in %z(href("%R/info/%!S",zCIUuid))%S(zCIUuid)</a></h2>
      }
      blob_reset(&path);
    }
    style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
    zMime = mimetype_from_name(zName);
    style_submenu_element("Annotate", "%R/annotate?filename=%T&checkin=%T",
                          zName, zCI);
    style_submenu_element("Blame", "%R/blame?filename=%T&checkin=%T",
                          zName, zCI);
    blob_init(&downloadName, zName, -1);
    objType = OBJTYPE_CONTENT;
  }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);
    if( asText ) objdescFlags &= ~OBJDESC_BASE;
    objType = object_description(rid, objdescFlags,
                                (isFile?zName:0), &downloadName);
    zMime = mimetype_from_name(blob_str(&downloadName));
  }
  if( !descOnly && P("download")!=0 ){
    cgi_redirectf("%R/raw/%s?at=%T",
          db_text("x", "SELECT uuid FROM blob WHERE rid=%d", rid),
          file_tail(blob_str(&downloadName)));
    /*NOTREACHED*/
  }
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
      const char *zIp = db_column_text(&q,2);
      @ <p>Received on %s(zDate) from %h(zUser) at %h(zIp).</p>
    }
    db_finalize(&q);
  }
  style_submenu_element("Download", "%R/raw/%s?at=%T", zUuid, file_tail(zName));
  if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){
    style_submenu_element("Check-ins Using", "%R/timeline?n=200&uf=%s", zUuid);
  }
  zMime = mimetype_from_name(blob_str(&downloadName));
  if( zMime ){
    if( fossil_strcmp(zMime, "text/html")==0 ){
      if( asText ){
        style_submenu_element("Html", "%s", url_render(&url, "txt", 0, 0, 0));
      }else{
        renderAsHtml = 1;
        style_submenu_element("Text", "%s", url_render(&url, "txt", "1", 0, 0));







|

<







2463
2464
2465
2466
2467
2468
2469
2470
2471

2472
2473
2474
2475
2476
2477
2478
      const char *zIp = db_column_text(&q,2);
      @ <p>Received on %s(zDate) from %h(zUser) at %h(zIp).</p>
    }
    db_finalize(&q);
  }
  style_submenu_element("Download", "%R/raw/%s?at=%T", zUuid, file_tail(zName));
  if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){
    style_submenu_element("Check-ins Using", "%R/timeline?uf=%s", zUuid);
  }

  if( zMime ){
    if( fossil_strcmp(zMime, "text/html")==0 ){
      if( asText ){
        style_submenu_element("Html", "%s", url_render(&url, "txt", 0, 0, 0));
      }else{
        renderAsHtml = 1;
        style_submenu_element("Text", "%s", url_render(&url, "txt", "1", 0, 0));
2492
2493
2494
2495
2496
2497
2498

2499
2500
2501
2502
2503
2504

2505
2506
2507
2508
2509
2510
2511
2512
2513
      @     this.height=this.contentDocument.documentElement.scrollHeight + 75;
      @   }
      @ );
      @ </script>
    }else if( renderAsSvg ){
      @ <object type="image/svg+xml" data="%R/raw/%s(zUuid)"></object>
    }else{

      style_submenu_element("Hex", "%R/hexdump?name=%s", zUuid);
      if( zLn==0 || atoi(zLn)==0 ){
        style_submenu_checkbox("ln", "Line Numbers", 0, 0);
      }
      blob_to_utf8_no_bom(&content, 0);
      zMime = mimetype_from_content(&content);

      @ <blockquote class="file-content">
      if( zMime==0 ){
        const char *z, *zFileName, *zExt;
        z = blob_str(&content);
        zFileName = db_text(0,
         "SELECT name FROM mlink, filename"
         " WHERE filename.fnid=mlink.fnid"
         "   AND mlink.fid=%d",
         rid);







>





|
>

|







2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
      @     this.height=this.contentDocument.documentElement.scrollHeight + 75;
      @   }
      @ );
      @ </script>
    }else if( renderAsSvg ){
      @ <object type="image/svg+xml" data="%R/raw/%s(zUuid)"></object>
    }else{
      const char *zContentMime;
      style_submenu_element("Hex", "%R/hexdump?name=%s", zUuid);
      if( zLn==0 || atoi(zLn)==0 ){
        style_submenu_checkbox("ln", "Line Numbers", 0, 0);
      }
      blob_to_utf8_no_bom(&content, 0);
      zContentMime = mimetype_from_content(&content);
      if( zMime==0 ) zMime = zContentMime;
      @ <blockquote class="file-content">
      if( zContentMime==0 ){
        const char *z, *zFileName, *zExt;
        z = blob_str(&content);
        zFileName = db_text(0,
         "SELECT name FROM mlink, filename"
         " WHERE filename.fnid=mlink.fnid"
         "   AND mlink.fid=%d",
         rid);
2524
2525
2526
2527
2528
2529
2530





2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
          @ %h(z)
          @ </pre>
        }
      }else if( strncmp(zMime, "image/", 6)==0 ){
        @ <p>(file is %d(blob_size(&content)) bytes of image data)</i></p>
        @ <p><img src="%R/raw/%s(zUuid)?m=%s(zMime)"></p>
        style_submenu_element("Image", "%R/raw/%s?m=%s", zUuid, zMime);





      }else{
        @ <i>(file is %d(blob_size(&content)) bytes of binary data)</i>
      }
      @ </blockquote>
    }
  }
  style_finish_page("artifact");
}

/*
** WEBPAGE: tinfo
** URL: /tinfo?name=ARTIFACTID
**
** Show the details of a ticket change control artifact.







>
>
>
>
>






|







2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
          @ %h(z)
          @ </pre>
        }
      }else if( strncmp(zMime, "image/", 6)==0 ){
        @ <p>(file is %d(blob_size(&content)) bytes of image data)</i></p>
        @ <p><img src="%R/raw/%s(zUuid)?m=%s(zMime)"></p>
        style_submenu_element("Image", "%R/raw/%s?m=%s", zUuid, zMime);
      }else if( strncmp(zMime, "audio/", 6)==0 ){
        @ <p>(file is %d(blob_size(&content)) bytes of sound data)</i></p>
        @ <audio controls src="%R/raw/%s(zUuid)?m=%s(zMime)">
        @ (Not supported by this browser)
        @ </audio>
      }else{
        @ <i>(file is %d(blob_size(&content)) bytes of binary data)</i>
      }
      @ </blockquote>
    }
  }
  style_finish_page();
}

/*
** WEBPAGE: tinfo
** URL: /tinfo?name=ARTIFACTID
**
** Show the details of a ticket change control artifact.
2588
2589
2590
2591
2592
2593
2594

2595
2596
2597
2598
2599
2600
2601
      moderation_approve('t', rid);
    }
  }
  zTktTitle = db_table_has_column("repository", "ticket", "title" )
      ? db_text("(No title)", 
                "SELECT title FROM ticket WHERE tkt_uuid=%Q", zTktName)
      : 0;

  style_header("Ticket Change Details");
  style_submenu_element("Raw", "%R/artifact/%s", zUuid);
  style_submenu_element("History", "%R/tkthistory/%s", zTktName);
  style_submenu_element("Page", "%R/tktview/%t", zTktName);
  style_submenu_element("Timeline", "%R/tkttimeline/%t", zTktName);
  if( P("plaintext") ){
    style_submenu_element("Formatted", "%R/info/%s", zUuid);







>







2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
      moderation_approve('t', rid);
    }
  }
  zTktTitle = db_table_has_column("repository", "ticket", "title" )
      ? db_text("(No title)", 
                "SELECT title FROM ticket WHERE tkt_uuid=%Q", zTktName)
      : 0;
  style_set_current_feature("tinfo");
  style_header("Ticket Change Details");
  style_submenu_element("Raw", "%R/artifact/%s", zUuid);
  style_submenu_element("History", "%R/tkthistory/%s", zTktName);
  style_submenu_element("Page", "%R/tktview/%t", zTktName);
  style_submenu_element("Timeline", "%R/tkttimeline/%t", zTktName);
  if( P("plaintext") ){
    style_submenu_element("Formatted", "%R/info/%s", zUuid);
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
    @ </blockquote>
  }

  @ <div class="section">Changes</div>
  @ <p>
  ticket_output_change_artifact(pTktChng, 0, 1);
  manifest_destroy(pTktChng);
  style_finish_page("tinfo");
}


/*
** WEBPAGE: info
** URL: info/NAME
**







|







2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
    @ </blockquote>
  }

  @ <div class="section">Changes</div>
  @ <p>
  ticket_output_change_artifact(pTktChng, 0, 1);
  manifest_destroy(pTktChng);
  style_finish_page();
}


/*
** WEBPAGE: info
** URL: info/NAME
**
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
    }
    style_header("No Such Object");
    @ <p>No such object: %h(zName)</p>
    if( nLen<4 ){
      @ <p>Object name should be no less than 4 characters.  Ten or more
      @ characters are recommended.</p>
    }
    style_finish_page("info");
    return;
  }else if( rc==2 ){
    cgi_set_parameter("src","info");
    ambiguous_page();
    return;
  }
  zName = blob_str(&uuid);
  rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName);
  if( rid==0 ){
    style_header("Broken Link");
    @ <p>No such object: %h(zName)</p>
    style_finish_page("info");
    return;
  }
  if( db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
    ci_page();
  }else
  if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)"
                " WHERE rid=%d AND tagname LIKE 'wiki-%%'", rid) ){







|











|







2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
    }
    style_header("No Such Object");
    @ <p>No such object: %h(zName)</p>
    if( nLen<4 ){
      @ <p>Object name should be no less than 4 characters.  Ten or more
      @ characters are recommended.</p>
    }
    style_finish_page();
    return;
  }else if( rc==2 ){
    cgi_set_parameter("src","info");
    ambiguous_page();
    return;
  }
  zName = blob_str(&uuid);
  rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zName);
  if( rid==0 ){
    style_header("Broken Link");
    @ <p>No such object: %h(zName)</p>
    style_finish_page();
    return;
  }
  if( db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
    ci_page();
  }else
  if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)"
                " WHERE rid=%d AND tagname LIKE 'wiki-%%'", rid) ){
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
  if( P("preview") ){
    @ <input type="submit" name="apply" value="Apply Changes" />
  }
  @ </td></tr>
  @ </table>
  @ </div></form>
  builtin_request_js("ci_edit.js");
  style_finish_page("ci_edit");
}

/*
** Prepare an ammended commit comment.  Let the user modify it using the
** editor specified in the global_config table or either
** the VISUAL or EDITOR environment variable.
**







|







3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
  if( P("preview") ){
    @ <input type="submit" name="apply" value="Apply Changes" />
  }
  @ </td></tr>
  @ </table>
  @ </div></form>
  builtin_request_js("ci_edit.js");
  style_finish_page();
}

/*
** Prepare an ammended commit comment.  Let the user modify it using the
** editor specified in the global_config table or either
** the VISUAL or EDITOR environment variable.
**

Changes to src/interwiki.c.

341
342
343
344
345
346
347

348
349
350
351
352
353
354
        " json_object('base',%Q,'hash',%Q,'wiki',%Q),"
        " now());",
        zTag, zBase, zHash, zWiki);
      db_protect_pop();
    }
  }


  style_header("Interwiki Map Configuration");
  @ <p>Interwiki links are hyperlink targets of the form
  @ <blockquote><i>Tag</i><b>:</b><i>PageName</i></blockquote>
  @ <p>Such links resolve to links to <i>PageName</i> on a separate server
  @ identified by <i>Tag</i>.  The Interwiki Map or "intermap" is a mapping
  @ from <i>Tags</i> to complete Server URLs.
  db_prepare(&q,







>







341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
        " json_object('base',%Q,'hash',%Q,'wiki',%Q),"
        " now());",
        zTag, zBase, zHash, zWiki);
      db_protect_pop();
    }
  }

  style_set_current_feature("interwiki");
  style_header("Interwiki Map Configuration");
  @ <p>Interwiki links are hyperlink targets of the form
  @ <blockquote><i>Tag</i><b>:</b><i>PageName</i></blockquote>
  @ <p>Such links resolve to links to <i>PageName</i> on a separate server
  @ identified by <i>Tag</i>.  The Interwiki Map or "intermap" is a mapping
  @ from <i>Tags</i> to complete Server URLs.
  db_prepare(&q,
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
    @ </ol>
  }else{
    @ No mappings are currently defined.
  }

  if( !g.perm.Setup ){
    /* Do not show intermap editing fields to non-setup users */
    style_finish_page("interwiki");
    return;
  }

  @ <p>To add a new mapping, fill out the form below providing a unique name
  @ for the tag.  To edit an exist mapping, fill out the form and use the
  @ existing name as the tag.  To delete an existing mapping, fill in the
  @ tag field but leave the "Base URL" field blank.</p>







|







387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
    @ </ol>
  }else{
    @ No mappings are currently defined.
  }

  if( !g.perm.Setup ){
    /* Do not show intermap editing fields to non-setup users */
    style_finish_page();
    return;
  }

  @ <p>To add a new mapping, fill out the form below providing a unique name
  @ for the tag.  To edit an exist mapping, fill out the form and use the
  @ existing name as the tag.  To delete an existing mapping, fill in the
  @ tag field but leave the "Base URL" field blank.</p>
419
420
421
422
423
424
425
426
427
  @ size="20" value="%h(zWiki)">
  @ (use "<tt>/wiki?name=</tt>" when the target is Fossil)</td></tr>
  @ <tr><td></td>
  @ <td><input type="submit" name="submit" value="Apply Changes"></td></tr>
  @ </table>
  @ </form>

  style_finish_page("interwiki");
}







|

420
421
422
423
424
425
426
427
428
  @ size="20" value="%h(zWiki)">
  @ (use "<tt>/wiki?name=</tt>" when the target is Fossil)</td></tr>
  @ <tr><td></td>
  @ <td><input type="submit" name="submit" value="Apply Changes"></td></tr>
  @ </table>
  @ </form>

  style_finish_page();
}

Changes to src/json.c.

14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** Code for the JSON API.
**
** For notes regarding the public JSON interface, please see:
**
** https://docs.google.com/document/d/1fXViveNhDbiXgCuE7QDXQOKeFzf2qNUkBEgiUvoqFN4/view
**
**
** Notes for hackers...
**
** Here's how command/page dispatching works: json_page_top() (in HTTP mode) or
** json_cmd_top() (in CLI mode) catch the "json" path/command. Those functions then
** dispatch to a JSON-mode-specific command/page handler with the type fossil_json_f().
** See the API docs for that typedef (below) for the semantics of the callbacks.







|

|
<







14
15
16
17
18
19
20
21
22
23

24
25
26
27
28
29
30
**   drh@hwaci.com
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** Code for the JSON API.
**
** The JSON API's public interface is documented at:
**
** https://fossil-scm.org/fossil/doc/trunk/www/json-api/index.md

**
** Notes for hackers...
**
** Here's how command/page dispatching works: json_page_top() (in HTTP mode) or
** json_cmd_top() (in CLI mode) catch the "json" path/command. Those functions then
** dispatch to a JSON-mode-specific command/page handler with the type fossil_json_f().
** See the API docs for that typedef (below) for the semantics of the callbacks.
65
66
67
68
69
70
71

72

73
74
75
76
77
78
79
  int rc = 0;
  if(zPathInfo==0){
    rc = 0;
  }else if(0==strncmp("/json",zPathInfo,5)
           && (zPathInfo[5]==0 || zPathInfo[5]=='/')){
    rc = 1;
  }else if(g.zCmdName!=0 && (0==strcmp("server",g.zCmdName)

                             || 0==strcmp("cgi",g.zCmdName)) ){

    /* When running in server/cgi "directory" mode, zPathInfo is
    ** prefixed with the repository's name, so in order to determine
    ** whether or not we're really running in json mode we have to try
    ** a bit harder. Problem reported here:
    ** https://fossil-scm.org/forum/forumpost/e4953666d6
    */
    ReCompiled * pReg = 0;







>
|
>







64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
  int rc = 0;
  if(zPathInfo==0){
    rc = 0;
  }else if(0==strncmp("/json",zPathInfo,5)
           && (zPathInfo[5]==0 || zPathInfo[5]=='/')){
    rc = 1;
  }else if(g.zCmdName!=0 && (0==strcmp("server",g.zCmdName)
                             || 0==strcmp("ui",g.zCmdName)
                             || 0==strcmp("cgi",g.zCmdName)
                             || 0==strcmp("http",g.zCmdName)) ){
    /* When running in server/cgi "directory" mode, zPathInfo is
    ** prefixed with the repository's name, so in order to determine
    ** whether or not we're really running in json mode we have to try
    ** a bit harder. Problem reported here:
    ** https://fossil-scm.org/forum/forumpost/e4953666d6
    */
    ReCompiled * pReg = 0;
1976
1977
1978
1979
1980
1981
1982

1983
1984
1985
1986
1987
1988
1989
  ADD(WrForum,"writeForum");
  ADD(WrTForum,"writeTrustedForum");
  ADD(ModForum,"moderateForum");
  ADD(AdminForum,"adminForum");
  ADD(EmailAlert,"emailAlert");
  ADD(Announce,"announce");
  ADD(Debug,"debug");

#undef ADD
  return payload;
}

/*
** Implementation of the /json/stat page/command.
**







>







1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
  ADD(WrForum,"writeForum");
  ADD(WrTForum,"writeTrustedForum");
  ADD(ModForum,"moderateForum");
  ADD(AdminForum,"adminForum");
  ADD(EmailAlert,"emailAlert");
  ADD(Announce,"announce");
  ADD(Debug,"debug");
  ADD(Chat,"chat");
#undef ADD
  return payload;
}

/*
** Implementation of the /json/stat page/command.
**

Changes to src/linenoise.c.

121
122
123
124
125
126
127

128
129
130
131
132
133
134
#define LINENOISE_MAX_LINE 4096
static char *unsupported_term[] = {"dumb","cons25","emacs",NULL};
static linenoiseCompletionCallback *completionCallback = NULL;
static linenoiseHintsCallback *hintsCallback = NULL;
static linenoiseFreeHintsCallback *freeHintsCallback = NULL;

static struct termios orig_termios; /* In order to restore at exit.*/

static int rawmode = 0; /* For atexit() function to check if restore is needed*/
static int mlmode = 0;  /* Multi line mode. Default is single line. */
static int atexit_registered = 0; /* Register atexit just 1 time. */
static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
static int history_len = 0;
static char **history = NULL;








>







121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#define LINENOISE_MAX_LINE 4096
static char *unsupported_term[] = {"dumb","cons25","emacs",NULL};
static linenoiseCompletionCallback *completionCallback = NULL;
static linenoiseHintsCallback *hintsCallback = NULL;
static linenoiseFreeHintsCallback *freeHintsCallback = NULL;

static struct termios orig_termios; /* In order to restore at exit.*/
static int maskmode = 0; /* Show "***" instead of input. For passwords. */
static int rawmode = 0; /* For atexit() function to check if restore is needed*/
static int mlmode = 0;  /* Multi line mode. Default is single line. */
static int atexit_registered = 0; /* Register atexit just 1 time. */
static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
static int history_len = 0;
static char **history = NULL;

192
193
194
195
196
197
198













199
200
201
202
203
204
205
        fflush(lndebug_fp); \
    } while (0)
#else
#define lndebug(fmt, ...)
#endif

/* ======================= Low level terminal handling ====================== */














/* Set if to use or not the multi line mode. */
void linenoiseSetMultiLine(int ml) {
    mlmode = ml;
}

/* Return true if the terminal name is in the list of terminals we know are







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







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
        fflush(lndebug_fp); \
    } while (0)
#else
#define lndebug(fmt, ...)
#endif

/* ======================= Low level terminal handling ====================== */

/* Enable "mask mode". When it is enabled, instead of the input that
 * the user is typing, the terminal will just display a corresponding
 * number of asterisks, like "****". This is useful for passwords and other
 * secrets that should not be displayed. */
void linenoiseMaskModeEnable(void) {
    maskmode = 1;
}

/* Disable mask mode. */
void linenoiseMaskModeDisable(void) {
    maskmode = 0;
}

/* Set if to use or not the multi line mode. */
void linenoiseSetMultiLine(int ml) {
    mlmode = ml;
}

/* Return true if the terminal name is in the list of terminals we know are
481
482
483
484
485
486
487


488
489
490
491
492
493
494
        if (hint) {
            int hintlen = strlen(hint);
            int hintmaxlen = l->cols-(plen+l->len);
            if (hintlen > hintmaxlen) hintlen = hintmaxlen;
            if (bold == 1 && color == -1) color = 37;
            if (color != -1 || bold != 0)
                snprintf(seq,64,"\033[%d;%d;49m",bold,color);


            abAppend(ab,seq,strlen(seq));
            abAppend(ab,hint,hintlen);
            if (color != -1 || bold != 0)
                abAppend(ab,"\033[0m",4);
            /* Call the function to free the hint returned. */
            if (freeHintsCallback) freeHintsCallback(hint);
        }







>
>







495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
        if (hint) {
            int hintlen = strlen(hint);
            int hintmaxlen = l->cols-(plen+l->len);
            if (hintlen > hintmaxlen) hintlen = hintmaxlen;
            if (bold == 1 && color == -1) color = 37;
            if (color != -1 || bold != 0)
                snprintf(seq,64,"\033[%d;%d;49m",bold,color);
            else
                seq[0] = '\0';
            abAppend(ab,seq,strlen(seq));
            abAppend(ab,hint,hintlen);
            if (color != -1 || bold != 0)
                abAppend(ab,"\033[0m",4);
            /* Call the function to free the hint returned. */
            if (freeHintsCallback) freeHintsCallback(hint);
        }
519
520
521
522
523
524
525



526

527
528
529
530
531
532
533

    abInit(&ab);
    /* Cursor to left edge */
    snprintf(seq,64,"\r");
    abAppend(&ab,seq,strlen(seq));
    /* Write the prompt and the current buffer content */
    abAppend(&ab,l->prompt,strlen(l->prompt));



    abAppend(&ab,buf,len);

    /* Show hits if any. */
    refreshShowHints(&ab,l,plen);
    /* Erase to right */
    snprintf(seq,64,"\x1b[0K");
    abAppend(&ab,seq,strlen(seq));
    /* Move cursor to original position. */
    snprintf(seq,64,"\r\x1b[%dC", (int)(pos+plen));







>
>
>
|
>







535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553

    abInit(&ab);
    /* Cursor to left edge */
    snprintf(seq,64,"\r");
    abAppend(&ab,seq,strlen(seq));
    /* Write the prompt and the current buffer content */
    abAppend(&ab,l->prompt,strlen(l->prompt));
    if (maskmode == 1) {
        while (len--) abAppend(&ab,"*",1);
    } else {
        abAppend(&ab,buf,len);
    }
    /* Show hits if any. */
    refreshShowHints(&ab,l,plen);
    /* Erase to right */
    snprintf(seq,64,"\x1b[0K");
    abAppend(&ab,seq,strlen(seq));
    /* Move cursor to original position. */
    snprintf(seq,64,"\r\x1b[%dC", (int)(pos+plen));
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579




580

581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
    /* Update maxrows if needed. */
    if (rows > (int)l->maxrows) l->maxrows = rows;

    /* First step: clear all the lines used before. To do so start by
     * going to the last row. */
    abInit(&ab);
    if (old_rows-rpos > 0) {
        /* lndebug("go down %d", old_rows-rpos); */
        snprintf(seq,64,"\x1b[%dB", old_rows-rpos);
        abAppend(&ab,seq,strlen(seq));
    }

    /* Now for every row clear it, go up. */
    for (j = 0; j < old_rows-1; j++) {
        /* lndebug("clear+up"); */
        snprintf(seq,64,"\r\x1b[0K\x1b[1A");
        abAppend(&ab,seq,strlen(seq));
    }

    /* Clean the top line. */
    /* lndebug("clear"); */
    snprintf(seq,64,"\r\x1b[0K");
    abAppend(&ab,seq,strlen(seq));

    /* Write the prompt and the current buffer content */
    abAppend(&ab,l->prompt,strlen(l->prompt));




    abAppend(&ab,l->buf,l->len);


    /* Show hits if any. */
    refreshShowHints(&ab,l,plen);

    /* If we are at the very end of the screen with our prompt, we need to
     * emit a newline and move the prompt to the first column. */
    if (l->pos &&
        l->pos == l->len &&
        (l->pos+plen) % l->cols == 0)
    {
        /* lndebug("<newline>"); */
        abAppend(&ab,"\n",1);
        snprintf(seq,64,"\r");
        abAppend(&ab,seq,strlen(seq));
        rows++;
        if (rows > (int)l->maxrows) l->maxrows = rows;
    }

    /* Move cursor to right position. */
    rpos2 = (plen+l->pos+l->cols)/l->cols; /* current cursor relative row. */
    /* lndebug("rpos2 %d", rpos2); */

    /* Go up till we reach the expected positon. */
    if (rows-rpos2 > 0) {
        /* lndebug("go-up %d", rows-rpos2); */
        snprintf(seq,64,"\x1b[%dA", rows-rpos2);
        abAppend(&ab,seq,strlen(seq));
    }

    /* Set column. */
    col = (plen+(int)l->pos) % (int)l->cols;
    /* lndebug("set col %d", 1+col); */
    if (col)
        snprintf(seq,64,"\r\x1b[%dC", col);
    else
        snprintf(seq,64,"\r");
    abAppend(&ab,seq,strlen(seq));

    /* lndebug("\n"); */
    l->oldpos = l->pos;

    if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
    abFree(&ab);
}

/* Calls the two low level functions refreshSingleLine() or







|






|





|





>
>
>
>
|
>










|









|



|






|






|







574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
    /* Update maxrows if needed. */
    if (rows > (int)l->maxrows) l->maxrows = rows;

    /* First step: clear all the lines used before. To do so start by
     * going to the last row. */
    abInit(&ab);
    if (old_rows-rpos > 0) {
        lndebug("go down %d", old_rows-rpos);
        snprintf(seq,64,"\x1b[%dB", old_rows-rpos);
        abAppend(&ab,seq,strlen(seq));
    }

    /* Now for every row clear it, go up. */
    for (j = 0; j < old_rows-1; j++) {
        lndebug("clear+up");
        snprintf(seq,64,"\r\x1b[0K\x1b[1A");
        abAppend(&ab,seq,strlen(seq));
    }

    /* Clean the top line. */
    lndebug("clear");
    snprintf(seq,64,"\r\x1b[0K");
    abAppend(&ab,seq,strlen(seq));

    /* Write the prompt and the current buffer content */
    abAppend(&ab,l->prompt,strlen(l->prompt));
    if (maskmode == 1) {
        unsigned int i;
        for (i = 0; i < l->len; i++) abAppend(&ab,"*",1);
    } else {
        abAppend(&ab,l->buf,l->len);
    }

    /* Show hits if any. */
    refreshShowHints(&ab,l,plen);

    /* If we are at the very end of the screen with our prompt, we need to
     * emit a newline and move the prompt to the first column. */
    if (l->pos &&
        l->pos == l->len &&
        (l->pos+plen) % l->cols == 0)
    {
        lndebug("<newline>");
        abAppend(&ab,"\n",1);
        snprintf(seq,64,"\r");
        abAppend(&ab,seq,strlen(seq));
        rows++;
        if (rows > (int)l->maxrows) l->maxrows = rows;
    }

    /* Move cursor to right position. */
    rpos2 = (plen+l->pos+l->cols)/l->cols; /* current cursor relative row. */
    lndebug("rpos2 %d", rpos2);

    /* Go up till we reach the expected positon. */
    if (rows-rpos2 > 0) {
        lndebug("go-up %d", rows-rpos2);
        snprintf(seq,64,"\x1b[%dA", rows-rpos2);
        abAppend(&ab,seq,strlen(seq));
    }

    /* Set column. */
    col = (plen+(int)l->pos) % (int)l->cols;
    lndebug("set col %d", 1+col);
    if (col)
        snprintf(seq,64,"\r\x1b[%dC", col);
    else
        snprintf(seq,64,"\r");
    abAppend(&ab,seq,strlen(seq));

    lndebug("\n");
    l->oldpos = l->pos;

    if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */
    abFree(&ab);
}

/* Calls the two low level functions refreshSingleLine() or
641
642
643
644
645
646
647

648
649
650
651
652
653
654
655
            l->buf[l->pos] = c;
            l->pos++;
            l->len++;
            l->buf[l->len] = '\0';
            if ((!mlmode && l->plen+l->len < l->cols && !hintsCallback)) {
                /* Avoid a full update of the line in the
                 * trivial case. */

                if (write(l->ofd,&c,1) == -1) return -1;
            } else {
                refreshLine(l);
            }
        } else {
            memmove(l->buf+l->pos+1,l->buf+l->pos,l->len-l->pos);
            l->buf[l->pos] = c;
            l->len++;







>
|







666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
            l->buf[l->pos] = c;
            l->pos++;
            l->len++;
            l->buf[l->len] = '\0';
            if ((!mlmode && l->plen+l->len < l->cols && !hintsCallback)) {
                /* Avoid a full update of the line in the
                 * trivial case. */
                char d = (maskmode==1) ? '*' : c;
                if (write(l->ofd,&d,1) == -1) return -1;
            } else {
                refreshLine(l);
            }
        } else {
            memmove(l->buf+l->pos+1,l->buf+l->pos,l->len-l->pos);
            l->buf[l->pos] = c;
            l->len++;

Changes to src/linenoise.h.

61
62
63
64
65
66
67


68
69
70
71
72
73
int linenoiseHistoryAdd(const char *line);
int linenoiseHistorySetMaxLen(int len);
int linenoiseHistorySave(const char *filename);
int linenoiseHistoryLoad(const char *filename);
void linenoiseClearScreen(void);
void linenoiseSetMultiLine(int ml);
void linenoisePrintKeyCodes(void);



#ifdef __cplusplus
}
#endif

#endif /* __LINENOISE_H */







>
>






61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
int linenoiseHistoryAdd(const char *line);
int linenoiseHistorySetMaxLen(int len);
int linenoiseHistorySave(const char *filename);
int linenoiseHistoryLoad(const char *filename);
void linenoiseClearScreen(void);
void linenoiseSetMultiLine(int ml);
void linenoisePrintKeyCodes(void);
void linenoiseMaskModeEnable(void);
void linenoiseMaskModeDisable(void);

#ifdef __cplusplus
}
#endif

#endif /* __LINENOISE_H */

Changes to src/loadctrl.c.

50
51
52
53
54
55
56

57
58
59
60
61
62
63
64
65
66
** Abort the current operation of the load average of the host computer
** is too high.
*/
void load_control(void){
  double mxLoad = atof(db_get("max-loadavg", 0));
  if( mxLoad<=0.0 || mxLoad>=load_average() ) return;


  style_header("Server Overload");
  @ <h2>The server load is currently too high.
  @ Please try again later.</h2>
  @ <p>Current load average: %f(load_average()).<br />
  @ Load average limit: %f(mxLoad)</p>
  style_finish_page("test");
  cgi_set_status(503,"Server Overload");
  cgi_reply();
  exit(0);
}







>





|




50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
** Abort the current operation of the load average of the host computer
** is too high.
*/
void load_control(void){
  double mxLoad = atof(db_get("max-loadavg", 0));
  if( mxLoad<=0.0 || mxLoad>=load_average() ) return;

  style_set_current_feature("test");
  style_header("Server Overload");
  @ <h2>The server load is currently too high.
  @ Please try again later.</h2>
  @ <p>Current load average: %f(load_average()).<br />
  @ Load average limit: %f(mxLoad)</p>
  style_finish_page();
  cgi_set_status(503,"Server Overload");
  cgi_reply();
  exit(0);
}

Changes to src/login.c.

654
655
656
657
658
659
660

661
662
663
664
665
666
667
      ** where HASH is a random hex number, PROJECT is either project
      ** code prefix, and LOGIN is the user name.
      */
      login_set_user_cookie(zUsername, uid, NULL, rememberMe?0:1);
      redirect_to_g();
    }
  }

  style_header("Login/Logout");
  style_adunit_config(ADUNIT_OFF);
  @ %s(zErrMsg)
  if( zGoto && !noAnon ){
    char *zAbbrev = fossil_strdup(zGoto);
    int i;
    for(i=0; zAbbrev[i] && zAbbrev[i]!='?'; i++){}







>







654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
      ** where HASH is a random hex number, PROJECT is either project
      ** code prefix, and LOGIN is the user name.
      */
      login_set_user_cookie(zUsername, uid, NULL, rememberMe?0:1);
      redirect_to_g();
    }
  }
  style_set_current_feature("login");
  style_header("Login/Logout");
  style_adunit_config(ADUNIT_OFF);
  @ %s(zErrMsg)
  if( zGoto && !noAnon ){
    char *zAbbrev = fossil_strdup(zGoto);
    int i;
    for(i=0; zAbbrev[i] && zAbbrev[i]!='?'; i++){}
774
775
776
777
778
779
780

781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
  if( login_is_individual() ){
    if( g.perm.EmailAlert && alert_enabled() ){
      @ <hr>
      @ <p>Configure <a href="%R/alerts">Email Alerts</a>
      @ for user <b>%h(g.zLogin)</b></p>
    }
    if( g.perm.Password ){

      @ <hr>
      @ <p>Change Password for user <b>%h(g.zLogin)</b>:</p>
      form_begin(0, "%R/login");
      @ <table>
      @ <tr><td class="form_label" id="oldpw">Old Password:</td>
      @ <td><input aria-labelledby="oldpw" type="password" name="p" \
      @ size="30"/></td></tr>
      @ <tr><td class="form_label" id="newpw">New Password:</td>
      @ <td><input aria-labelledby="newpw" type="password" name="n1" \
      @ size="30" /></td></tr>
      @ <tr><td class="form_label" id="reppw">Repeat New Password:</td>
      @ <td><input aria-labledby="reppw" type="password" name="n2" \
      @ size="30" /></td></tr>
      @ <tr><td></td>
      @ <td><input type="submit" value="Change Password" /></td></tr>
      @ </table>
      @ </form>
    }
  }
  style_finish_page("login");
}

/*
** Attempt to find login credentials for user zLogin on a peer repository
** with project code zCode.  Transfer those credentials to the local
** repository.
**







>









|









|







775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
  if( login_is_individual() ){
    if( g.perm.EmailAlert && alert_enabled() ){
      @ <hr>
      @ <p>Configure <a href="%R/alerts">Email Alerts</a>
      @ for user <b>%h(g.zLogin)</b></p>
    }
    if( g.perm.Password ){
      char *zRPW = fossil_random_password(12);
      @ <hr>
      @ <p>Change Password for user <b>%h(g.zLogin)</b>:</p>
      form_begin(0, "%R/login");
      @ <table>
      @ <tr><td class="form_label" id="oldpw">Old Password:</td>
      @ <td><input aria-labelledby="oldpw" type="password" name="p" \
      @ size="30"/></td></tr>
      @ <tr><td class="form_label" id="newpw">New Password:</td>
      @ <td><input aria-labelledby="newpw" type="password" name="n1" \
      @ size="30" /> Suggestion: %z(zRPW)</td></tr>
      @ <tr><td class="form_label" id="reppw">Repeat New Password:</td>
      @ <td><input aria-labledby="reppw" type="password" name="n2" \
      @ size="30" /></td></tr>
      @ <tr><td></td>
      @ <td><input type="submit" value="Change Password" /></td></tr>
      @ </table>
      @ </form>
    }
  }
  style_finish_page();
}

/*
** Attempt to find login credentials for user zLogin on a peer repository
** with project code zCode.  Transfer those credentials to the local
** repository.
**
1074
1075
1076
1077
1078
1079
1080



















1081
1082
1083
1084
1085
1086
1087
  /* If the request didn't provide a login cookie or the login cookie didn't
  ** match a known valid user, check the HTTP "Authorization" header and
  ** see if those credentials are valid for a known user.
  */
  if( uid==0 && db_get_boolean("http_authentication_ok",0) ){
    uid = login_basic_authentication(zIpAddr);
  }




















  /* If no user found yet, try to log in as "nobody" */
  if( uid==0 ){
    uid = db_int(0, "SELECT uid FROM user WHERE login='nobody'");
    if( uid==0 ){
      /* If there is no user "nobody", then make one up - with no privileges */
      uid = -1;







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







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
  /* If the request didn't provide a login cookie or the login cookie didn't
  ** match a known valid user, check the HTTP "Authorization" header and
  ** see if those credentials are valid for a known user.
  */
  if( uid==0 && db_get_boolean("http_authentication_ok",0) ){
    uid = login_basic_authentication(zIpAddr);
  }

  /* Check for magic query parameters "resid" (for the username) and
  ** "token" for the password.  Both values (if they exist) will be
  ** obfuscated.
  */
  if( uid==0 ){
    char *zUsr, *zPW;
    if( (zUsr = unobscure(P("resid")))!=0
     && (zPW = unobscure(P("token")))!=0
    ){
      char *zSha1Pw = sha1_shared_secret(zPW, zUsr, 0);
      uid = db_int(0, "SELECT uid FROM user"
                      " WHERE login=%Q"
                      " AND (constant_time_cmp(pw,%Q)=0"
                      "      OR constant_time_cmp(pw,%Q)=0)",
                      zUsr, zSha1Pw, zPW);
      fossil_free(zSha1Pw);
    }
  }

  /* If no user found yet, try to log in as "nobody" */
  if( uid==0 ){
    uid = db_int(0, "SELECT uid FROM user WHERE login='nobody'");
    if( uid==0 ){
      /* If there is no user "nobody", then make one up - with no privileges */
      uid = -1;
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
      case 'a':   p->Admin = p->RdTkt = p->WrTkt = p->Zip =
                             p->RdWiki = p->WrWiki = p->NewWiki =
                             p->ApndWiki = p->Hyperlink = p->Clone =
                             p->NewTkt = p->Password = p->RdAddr =
                             p->TktFmt = p->Attach = p->ApndTkt =
                             p->ModWiki = p->ModTkt =
                             p->RdForum = p->WrForum = p->ModForum =
                             p->WrTForum = p->AdminForum =
                             p->EmailAlert = p->Announce = p->Debug = 1;
                             /* Fall thru into Read/Write */
      case 'i':   p->Read = p->Write = 1;                      break;
      case 'o':   p->Read = 1;                                 break;
      case 'z':   p->Zip = 1;                                  break;

      case 'h':   p->Hyperlink = 1;                            break;







|







1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
      case 'a':   p->Admin = p->RdTkt = p->WrTkt = p->Zip =
                             p->RdWiki = p->WrWiki = p->NewWiki =
                             p->ApndWiki = p->Hyperlink = p->Clone =
                             p->NewTkt = p->Password = p->RdAddr =
                             p->TktFmt = p->Attach = p->ApndTkt =
                             p->ModWiki = p->ModTkt =
                             p->RdForum = p->WrForum = p->ModForum =
                             p->WrTForum = p->AdminForum = p->Chat = 
                             p->EmailAlert = p->Announce = p->Debug = 1;
                             /* Fall thru into Read/Write */
      case 'i':   p->Read = p->Write = 1;                      break;
      case 'o':   p->Read = 1;                                 break;
      case 'z':   p->Zip = 1;                                  break;

      case 'h':   p->Hyperlink = 1;                            break;
1264
1265
1266
1267
1268
1269
1270

1271
1272
1273
1274
1275
1276
1277
      case '5':   p->ModForum = 1;
      case '4':   p->WrTForum = 1;
      case '3':   p->WrForum = 1;
      case '2':   p->RdForum = 1;                              break;

      case '7':   p->EmailAlert = 1;                           break;
      case 'A':   p->Announce = 1;                             break;

      case 'D':   p->Debug = 1;                                break;

      /* The "u" privilege recursively
      ** inherits all privileges of the user named "reader" */
      case 'u': {
        if( p->XReader==0 ){
          const char *zUser;







>







1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
      case '5':   p->ModForum = 1;
      case '4':   p->WrTForum = 1;
      case '3':   p->WrForum = 1;
      case '2':   p->RdForum = 1;                              break;

      case '7':   p->EmailAlert = 1;                           break;
      case 'A':   p->Announce = 1;                             break;
      case 'C':   p->Chat = 1;                                 break;
      case 'D':   p->Debug = 1;                                break;

      /* The "u" privilege recursively
      ** inherits all privileges of the user named "reader" */
      case 'u': {
        if( p->XReader==0 ){
          const char *zUser;
1347
1348
1349
1350
1351
1352
1353

1354
1355
1356
1357
1358
1359
1360
      case '2':  rc = p->RdForum;   break;
      case '3':  rc = p->WrForum;   break;
      case '4':  rc = p->WrTForum;  break;
      case '5':  rc = p->ModForum;  break;
      case '6':  rc = p->AdminForum;break;
      case '7':  rc = p->EmailAlert;break;
      case 'A':  rc = p->Announce;  break;

      case 'D':  rc = p->Debug;     break;
      default:   rc = 0;            break;
    }
  }
  return rc;
}








>







1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
      case '2':  rc = p->RdForum;   break;
      case '3':  rc = p->WrForum;   break;
      case '4':  rc = p->WrTForum;  break;
      case '5':  rc = p->ModForum;  break;
      case '6':  rc = p->AdminForum;break;
      case '7':  rc = p->EmailAlert;break;
      case 'A':  rc = p->Announce;  break;
      case 'C':  rc = p->Chat;      break;
      case 'D':  rc = p->Debug;     break;
      default:   rc = 0;            break;
    }
  }
  return rc;
}

1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
  char *zPerms;             /* Permissions for the default user */
  int canDoAlerts = 0;      /* True if receiving email alerts is possible */
  int doAlerts = 0;         /* True if subscription is wanted too */
  if( !db_get_boolean("self-register", 0) ){
    style_header("Registration not possible");
    @ <p>This project does not allow user self-registration. Please contact the
    @ project administrator to obtain an account.</p>
    style_finish_page("register");
    return;
  }
  zPerms = db_get("default-perms", "u");

  /* Prompt the user for email alerts if this repository is configured for
  ** email alerts and if the default permissions include "7" */
  canDoAlerts = alert_tables_exist() && db_int(0,







|







1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
  char *zPerms;             /* Permissions for the default user */
  int canDoAlerts = 0;      /* True if receiving email alerts is possible */
  int doAlerts = 0;         /* True if subscription is wanted too */
  if( !db_get_boolean("self-register", 0) ){
    style_header("Registration not possible");
    @ <p>This project does not allow user self-registration. Please contact the
    @ project administrator to obtain an account.</p>
    style_finish_page();
    return;
  }
  zPerms = db_get("default-perms", "u");

  /* Prompt the user for email alerts if this repository is configured for
  ** email alerts and if the default permissions include "7" */
  canDoAlerts = alert_tables_exist() && db_int(0,
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
        @ <p>An email has been sent to "%h(zEAddr)". That email contains a
        @ hyperlink that you must click to activate your account.</p>
      }
      alert_sender_free(pSender);
      if( zGoto ){
        @ <p><a href='%h(zGoto)'>Continue</a>
      }
      style_finish_page("register");
      return;
    }
    redirect_to_g();
  }

  /* Prepare the captcha. */
  if( captchaIsCorrect ){







|







1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
        @ <p>An email has been sent to "%h(zEAddr)". That email contains a
        @ hyperlink that you must click to activate your account.</p>
      }
      alert_sender_free(pSender);
      if( zGoto ){
        @ <p><a href='%h(zGoto)'>Continue</a>
      }
      style_finish_page();
      return;
    }
    redirect_to_g();
  }

  /* Prepare the captcha. */
  if( captchaIsCorrect ){
1766
1767
1768
1769
1770
1771
1772
1773






1774
1775
1776
1777
1778
1779
1780
    @       <option value="1" %s(a?"selected":"")>Yes</option>
    @       <option value="0" %s(!a?"selected":"")>No</option>
    @   </select></td></tr>
  }
  @ <tr>
  @   <td class="form_label" align="right" id="pswd">Password:</td>
  @   <td><input aria-labelledby="pswd" type="password" name="p" \
  @ value="%h(zPasswd)" size="30"></td>






  @ <tr>
  if( iErrLine==4 ){
    @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
  }
  @ <tr>
  @   <td class="form_label" align="right" id="pwcfrm">Confirm:</td>
  @   <td><input aria-labelledby="pwcfrm" type="password" name="cp" \







|
>
>
>
>
>
>







1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
    @       <option value="1" %s(a?"selected":"")>Yes</option>
    @       <option value="0" %s(!a?"selected":"")>No</option>
    @   </select></td></tr>
  }
  @ <tr>
  @   <td class="form_label" align="right" id="pswd">Password:</td>
  @   <td><input aria-labelledby="pswd" type="password" name="p" \
  @ value="%h(zPasswd)" size="30"> \
  if( zPasswd[0]==0 ){
    char *zRPW = fossil_random_password(12);
    @ Password suggestion: %z(zRPW)</td>
  }else{
    @ </td>
  }
  @ <tr>
  if( iErrLine==4 ){
    @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
  }
  @ <tr>
  @   <td class="form_label" align="right" id="pwcfrm">Confirm:</td>
  @   <td><input aria-labelledby="pwcfrm" type="password" name="cp" \
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
  @ </table>
  @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha">
  @ %h(zCaptcha)
  @ </pre>
  @ Enter this 8-letter code in the "Captcha" box above.
  @ </td></tr></table></div>
  @ </form>
  style_finish_page("register");

  free(zCaptcha);
}

/*
** Run SQL on the repository database for every repository in our
** login group.  The SQL is run in a separate database connection.







|







1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
  @ </table>
  @ <div class="captcha"><table class="captcha"><tr><td><pre class="captcha">
  @ %h(zCaptcha)
  @ </pre>
  @ Enter this 8-letter code in the "Captcha" box above.
  @ </td></tr></table></div>
  @ </form>
  style_finish_page();

  free(zCaptcha);
}

/*
** Run SQL on the repository database for every repository in our
** login group.  The SQL is run in a separate database connection.

Changes to src/main.c.

105
106
107
108
109
110
111

112
113
114
115
116
117
118
  char RdForum;          /* 2: Read forum posts */
  char WrForum;          /* 3: Create new forum posts */
  char WrTForum;         /* 4: Post to forums not subject to moderation */
  char ModForum;         /* 5: Moderate (approve or reject) forum posts */
  char AdminForum;       /* 6: Grant capability 4 to other users */
  char EmailAlert;       /* 7: Sign up for email notifications */
  char Announce;         /* A: Send announcements */

  char Debug;            /* D: show extra Fossil debugging features */
  /* These last two are included to block infinite recursion */
  char XReader;          /* u: Inherit all privileges of "reader" */
  char XDeveloper;       /* v: Inherit all privileges of "developer" */
};

#ifdef FOSSIL_ENABLE_TCL







>







105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
  char RdForum;          /* 2: Read forum posts */
  char WrForum;          /* 3: Create new forum posts */
  char WrTForum;         /* 4: Post to forums not subject to moderation */
  char ModForum;         /* 5: Moderate (approve or reject) forum posts */
  char AdminForum;       /* 6: Grant capability 4 to other users */
  char EmailAlert;       /* 7: Sign up for email notifications */
  char Announce;         /* A: Send announcements */
  char Chat;             /* C: read or write the chatroom */
  char Debug;            /* D: show extra Fossil debugging features */
  /* These last two are included to block infinite recursion */
  char XReader;          /* u: Inherit all privileges of "reader" */
  char XDeveloper;       /* v: Inherit all privileges of "developer" */
};

#ifdef FOSSIL_ENABLE_TCL
318
319
320
321
322
323
324

325
326
327
328
329
330
331
      cson_value *v;
      cson_object *o;
    } reqPayload;              /* request payload object (if any) */
    cson_array *warnings;      /* response warnings */
    int timerId;               /* fetched from fossil_timer_start() */
  } json;
#endif /* FOSSIL_ENABLE_JSON */

};

/*
** Macro for debugging:
*/
#define CGIDEBUG(X)  if( g.fDebug ) cgi_debug X








>







319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
      cson_value *v;
      cson_object *o;
    } reqPayload;              /* request payload object (if any) */
    cson_array *warnings;      /* response warnings */
    int timerId;               /* fetched from fossil_timer_start() */
  } json;
#endif /* FOSSIL_ENABLE_JSON */
  int diffCnt[3];         /* Counts for DIFF_NUMSTAT: files, ins, del */
};

/*
** Macro for debugging:
*/
#define CGIDEBUG(X)  if( g.fDebug ) cgi_debug X

342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
  if( once++ ) return; /* Ensure that this routine only runs once */
#if USE_SEE
  /*
  ** Zero, unlock, and free the saved database encryption key now.
  */
  db_unsave_encryption_key();
#endif
#if defined(_WIN32) || defined(__BIONIC__)
  /*
  ** Free the secure getpass() buffer now.
  */
  freepass();
#endif
#if defined(_WIN32) && !defined(_WIN64) && defined(FOSSIL_ENABLE_TCL) && \
    defined(USE_TCL_STUBS)







|







344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
  if( once++ ) return; /* Ensure that this routine only runs once */
#if USE_SEE
  /*
  ** Zero, unlock, and free the saved database encryption key now.
  */
  db_unsave_encryption_key();
#endif
#if defined(_WIN32) || defined(__BIONIC__) && !defined(FOSSIL_HAVE_GETPASS)
  /*
  ** Free the secure getpass() buffer now.
  */
  freepass();
#endif
#if defined(_WIN32) && !defined(_WIN64) && defined(FOSSIL_ENABLE_TCL) && \
    defined(USE_TCL_STUBS)
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
  verboseFlag = PD("verbose", 0) != 0;
  style_header("Version Information");
  style_submenu_element("Stat", "stat");
  fossil_version_blob(&versionInfo, verboseFlag);
  @ <pre>
  @ %h(blob_str(&versionInfo))
  @ </pre>
  style_finish_page("version");
}


/*
** Set the g.zBaseURL value to the full URL for the toplevel of
** the fossil tree.  Set g.zTop to g.zBaseURL without the
** leading "http://" and the host and port.







|







1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
  verboseFlag = PD("verbose", 0) != 0;
  style_header("Version Information");
  style_submenu_element("Stat", "stat");
  fossil_version_blob(&versionInfo, verboseFlag);
  @ <pre>
  @ %h(blob_str(&versionInfo))
  @ </pre>
  style_finish_page();
}


/*
** Set the g.zBaseURL value to the full URL for the toplevel of
** the fossil tree.  Set g.zTop to g.zBaseURL without the
** leading "http://" and the host and port.
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
  }else{
    cgi_set_parameter("REMOTE_ADDR", "127.0.0.1");
    cgi_handle_http_request(0);
    process_one_web_page(0, 0, 1);
  }
}

#if !defined(_WIN32)
#if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__)
/*
** Search for an executable on the PATH environment variable.
** Return true (1) if found and false (0) if not found.
*/
static int binaryOnPath(const char *zBinary){
  const char *zPath = fossil_getenv("PATH");
  char *zFull;
  int i;
  int bExists;
  while( zPath && zPath[0] ){
    while( zPath[0]==':' ) zPath++;
    for(i=0; zPath[i] && zPath[i]!=':'; i++){}
    zFull = mprintf("%.*s/%s", i, zPath, zBinary);
    bExists = file_access(zFull, X_OK);
    fossil_free(zFull);
    if( bExists==0 ) return 1;
    zPath += i;
  }
  return 0;
}
#endif
#endif

/*
** Respond to a SIGALRM by writing a message to the error log (if there
** is one) and exiting.
*/
#ifndef _WIN32
static void sigalrm_handler(int x){
  fossil_panic("TIMEOUT");







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







2656
2657
2658
2659
2660
2661
2662

























2663
2664
2665
2666
2667
2668
2669
  }else{
    cgi_set_parameter("REMOTE_ADDR", "127.0.0.1");
    cgi_handle_http_request(0);
    process_one_web_page(0, 0, 1);
  }
}


























/*
** Respond to a SIGALRM by writing a message to the error log (if there
** is one) and exiting.
*/
#ifndef _WIN32
static void sigalrm_handler(int x){
  fossil_panic("TIMEOUT");
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
  }else{
    iPort = db_get_int("http-port", 8080);
    mxPort = iPort+100;
  }
#if !defined(_WIN32)
  /* Unix implementation */
  if( isUiCmd ){
#if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__)
    zBrowser = db_get("web-browser", 0);
    if( zBrowser==0 ){
      static const char *const azBrowserProg[] =
          { "xdg-open", "gnome-open", "firefox", "google-chrome" };
      int i;
      zBrowser = "echo";
      for(i=0; i<count(azBrowserProg); i++){
        if( binaryOnPath(azBrowserProg[i]) ){
          zBrowser = azBrowserProg[i];
          break;
        }
      }
    }
#else
    zBrowser = db_get("web-browser", "open");
#endif
    if( zIpAddr==0 ){
      zBrowserCmd = mprintf("%s \"http://localhost:%%d/%s\" &",
                            zBrowser, zInitPage);
    }else if( strchr(zIpAddr,':') ){
      zBrowserCmd = mprintf("%s \"http://[%s]:%%d/%s\" &",
                            zBrowser, zIpAddr, zInitPage);
    }else{







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







2867
2868
2869
2870
2871
2872
2873






2874










2875
2876
2877
2878
2879
2880
2881
  }else{
    iPort = db_get_int("http-port", 8080);
    mxPort = iPort+100;
  }
#if !defined(_WIN32)
  /* Unix implementation */
  if( isUiCmd ){






    zBrowser = fossil_web_browser();










    if( zIpAddr==0 ){
      zBrowserCmd = mprintf("%s \"http://localhost:%%d/%s\" &",
                            zBrowser, zInitPage);
    }else if( strchr(zIpAddr,':') ){
      zBrowserCmd = mprintf("%s \"http://[%s]:%%d/%s\" &",
                            zBrowser, zIpAddr, zInitPage);
    }else{
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
  if( g.fAnyTrace ){
    fprintf(stderr, "/***** Webpage finished in subprocess %d *****/\n",
            getpid());
  }
#else
  /* Win32 implementation */
  if( isUiCmd ){
    zBrowser = db_get("web-browser", "start");
    if( zIpAddr==0 ){
      zBrowserCmd = mprintf("%s http://localhost:%%d/%s &",
                            zBrowser, zInitPage);
    }else if( strchr(zIpAddr,':') ){
      zBrowserCmd = mprintf("%s http://[%s]:%%d/%s &",
                            zBrowser, zIpAddr, zInitPage);
    }else{







|







2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
  if( g.fAnyTrace ){
    fprintf(stderr, "/***** Webpage finished in subprocess %d *****/\n",
            getpid());
  }
#else
  /* Win32 implementation */
  if( isUiCmd ){
    zBrowser = fossil_web_browser();
    if( zIpAddr==0 ){
      zBrowserCmd = mprintf("%s http://localhost:%%d/%s &",
                            zBrowser, zInitPage);
    }else if( strchr(zIpAddr,':') ){
      zBrowserCmd = mprintf("%s http://[%s]:%%d/%s &",
                            zBrowser, zIpAddr, zInitPage);
    }else{
3047
3048
3049
3050
3051
3052
3053

3054
3055
3056
3057
3058
3059
3060
  int iCase = atoi(PD("case","0"));
  int i;
  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }

  style_header("Warning Test Page");
  style_submenu_element("Error Log","%R/errorlog");
  if( iCase<1 || iCase>4 ){
    @ <p>Generate a message to the <a href="%R/errorlog">error log</a>
    @ by clicking on one of the following cases:
  }else{
    @ <p>This is the test page for case=%d(iCase).  All possible cases:







>







3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
  int iCase = atoi(PD("case","0"));
  int i;
  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }
  style_set_current_feature("test");
  style_header("Warning Test Page");
  style_submenu_element("Error Log","%R/errorlog");
  if( iCase<1 || iCase>4 ){
    @ <p>Generate a message to the <a href="%R/errorlog">error log</a>
    @ by clicking on one of the following cases:
  }else{
    @ <p>This is the test page for case=%d(iCase).  All possible cases:
3095
3096
3097
3098
3099
3100
3101
3102
3103
  @ <li value='7'> call webpage_error()"
  if( iCase==7 ){
    cgi_reset_content();
    webpage_error("Case 7 from /test-warning");
  }
  @ </ol>
  @ <p>End of test</p>
  style_finish_page("test");
}







|

3057
3058
3059
3060
3061
3062
3063
3064
3065
  @ <li value='7'> call webpage_error()"
  if( iCase==7 ){
    cgi_reset_content();
    webpage_error("Case 7 from /test-warning");
  }
  @ </ol>
  @ <p>End of test</p>
  style_finish_page();
}

Changes to src/main.mk.

30
31
32
33
34
35
36

37
38
39
40
41
42
43
  $(SRCDIR)/browse.c \
  $(SRCDIR)/builtin.c \
  $(SRCDIR)/bundle.c \
  $(SRCDIR)/cache.c \
  $(SRCDIR)/capabilities.c \
  $(SRCDIR)/captcha.c \
  $(SRCDIR)/cgi.c \

  $(SRCDIR)/checkin.c \
  $(SRCDIR)/checkout.c \
  $(SRCDIR)/clearsign.c \
  $(SRCDIR)/clone.c \
  $(SRCDIR)/comformat.c \
  $(SRCDIR)/configure.c \
  $(SRCDIR)/content.c \







>







30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
  $(SRCDIR)/browse.c \
  $(SRCDIR)/builtin.c \
  $(SRCDIR)/bundle.c \
  $(SRCDIR)/cache.c \
  $(SRCDIR)/capabilities.c \
  $(SRCDIR)/captcha.c \
  $(SRCDIR)/cgi.c \
  $(SRCDIR)/chat.c \
  $(SRCDIR)/checkin.c \
  $(SRCDIR)/checkout.c \
  $(SRCDIR)/clearsign.c \
  $(SRCDIR)/clone.c \
  $(SRCDIR)/comformat.c \
  $(SRCDIR)/configure.c \
  $(SRCDIR)/content.c \
217
218
219
220
221
222
223





224
225
226
227
228
229
230
  $(SRCDIR)/../skins/rounded1/footer.txt \
  $(SRCDIR)/../skins/rounded1/header.txt \
  $(SRCDIR)/../skins/xekri/css.txt \
  $(SRCDIR)/../skins/xekri/details.txt \
  $(SRCDIR)/../skins/xekri/footer.txt \
  $(SRCDIR)/../skins/xekri/header.txt \
  $(SRCDIR)/accordion.js \





  $(SRCDIR)/ci_edit.js \
  $(SRCDIR)/copybtn.js \
  $(SRCDIR)/default.css \
  $(SRCDIR)/diff.tcl \
  $(SRCDIR)/forum.js \
  $(SRCDIR)/fossil.bootstrap.js \
  $(SRCDIR)/fossil.confirmer.js \







>
>
>
>
>







218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
  $(SRCDIR)/../skins/rounded1/footer.txt \
  $(SRCDIR)/../skins/rounded1/header.txt \
  $(SRCDIR)/../skins/xekri/css.txt \
  $(SRCDIR)/../skins/xekri/details.txt \
  $(SRCDIR)/../skins/xekri/footer.txt \
  $(SRCDIR)/../skins/xekri/header.txt \
  $(SRCDIR)/accordion.js \
  $(SRCDIR)/alerts/bflat2.wav \
  $(SRCDIR)/alerts/bflat3.wav \
  $(SRCDIR)/alerts/bloop.wav \
  $(SRCDIR)/alerts/plunk.wav \
  $(SRCDIR)/chat.js \
  $(SRCDIR)/ci_edit.js \
  $(SRCDIR)/copybtn.js \
  $(SRCDIR)/default.css \
  $(SRCDIR)/diff.tcl \
  $(SRCDIR)/forum.js \
  $(SRCDIR)/fossil.bootstrap.js \
  $(SRCDIR)/fossil.confirmer.js \
289
290
291
292
293
294
295

296
297
298
299
300
301
302
  $(OBJDIR)/browse_.c \
  $(OBJDIR)/builtin_.c \
  $(OBJDIR)/bundle_.c \
  $(OBJDIR)/cache_.c \
  $(OBJDIR)/capabilities_.c \
  $(OBJDIR)/captcha_.c \
  $(OBJDIR)/cgi_.c \

  $(OBJDIR)/checkin_.c \
  $(OBJDIR)/checkout_.c \
  $(OBJDIR)/clearsign_.c \
  $(OBJDIR)/clone_.c \
  $(OBJDIR)/comformat_.c \
  $(OBJDIR)/configure_.c \
  $(OBJDIR)/content_.c \







>







295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
  $(OBJDIR)/browse_.c \
  $(OBJDIR)/builtin_.c \
  $(OBJDIR)/bundle_.c \
  $(OBJDIR)/cache_.c \
  $(OBJDIR)/capabilities_.c \
  $(OBJDIR)/captcha_.c \
  $(OBJDIR)/cgi_.c \
  $(OBJDIR)/chat_.c \
  $(OBJDIR)/checkin_.c \
  $(OBJDIR)/checkout_.c \
  $(OBJDIR)/clearsign_.c \
  $(OBJDIR)/clone_.c \
  $(OBJDIR)/comformat_.c \
  $(OBJDIR)/configure_.c \
  $(OBJDIR)/content_.c \
437
438
439
440
441
442
443

444
445
446
447
448
449
450
 $(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 \







>







444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
 $(OBJDIR)/browse.o \
 $(OBJDIR)/builtin.o \
 $(OBJDIR)/bundle.o \
 $(OBJDIR)/cache.o \
 $(OBJDIR)/capabilities.o \
 $(OBJDIR)/captcha.o \
 $(OBJDIR)/cgi.o \
 $(OBJDIR)/chat.o \
 $(OBJDIR)/checkin.o \
 $(OBJDIR)/checkout.o \
 $(OBJDIR)/clearsign.o \
 $(OBJDIR)/clone.o \
 $(OBJDIR)/comformat.o \
 $(OBJDIR)/configure.o \
 $(OBJDIR)/content.o \
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
 $(OBJDIR)/shell.o \
 $(OBJDIR)/th.o \
 $(OBJDIR)/th_lang.o \
 $(OBJDIR)/th_tcl.o \
 $(OBJDIR)/cson_amalgamation.o


$(APPNAME):	$(OBJDIR)/headers $(OBJDIR)/codecheck1 $(OBJ) $(EXTRAOBJ)
	$(OBJDIR)/codecheck1 $(TRANS_SRC)
	$(TCC) $(TCCFLAGS) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB)

# This rule prevents make from using its default rules to try build
# an executable named "manifest" out of the file named "manifest.c"
#
$(SRCDIR)/../manifest:
	# noop








|

|







744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
 $(OBJDIR)/shell.o \
 $(OBJDIR)/th.o \
 $(OBJDIR)/th_lang.o \
 $(OBJDIR)/th_tcl.o \
 $(OBJDIR)/cson_amalgamation.o


$(APPNAME):	$(OBJDIR)/headers $(OBJDIR)/codecheck1 $(EXTRAOBJ) $(OBJ)
	$(OBJDIR)/codecheck1 $(TRANS_SRC)
	$(TCC) $(TCCFLAGS) -o $(APPNAME) $(EXTRAOBJ) $(OBJ) $(LIB)

# This rule prevents make from using its default rules to try build
# an executable named "manifest" out of the file named "manifest.c"
#
$(SRCDIR)/../manifest:
	# noop

775
776
777
778
779
780
781

782
783
784
785
786
787
788
	$(OBJDIR)/browse_.c:$(OBJDIR)/browse.h \
	$(OBJDIR)/builtin_.c:$(OBJDIR)/builtin.h \
	$(OBJDIR)/bundle_.c:$(OBJDIR)/bundle.h \
	$(OBJDIR)/cache_.c:$(OBJDIR)/cache.h \
	$(OBJDIR)/capabilities_.c:$(OBJDIR)/capabilities.h \
	$(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h \
	$(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h \

	$(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h \
	$(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h \
	$(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h \
	$(OBJDIR)/clone_.c:$(OBJDIR)/clone.h \
	$(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \
	$(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \
	$(OBJDIR)/content_.c:$(OBJDIR)/content.h \







>







783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
	$(OBJDIR)/browse_.c:$(OBJDIR)/browse.h \
	$(OBJDIR)/builtin_.c:$(OBJDIR)/builtin.h \
	$(OBJDIR)/bundle_.c:$(OBJDIR)/bundle.h \
	$(OBJDIR)/cache_.c:$(OBJDIR)/cache.h \
	$(OBJDIR)/capabilities_.c:$(OBJDIR)/capabilities.h \
	$(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h \
	$(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h \
	$(OBJDIR)/chat_.c:$(OBJDIR)/chat.h \
	$(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h \
	$(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h \
	$(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h \
	$(OBJDIR)/clone_.c:$(OBJDIR)/clone.h \
	$(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \
	$(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \
	$(OBJDIR)/content_.c:$(OBJDIR)/content.h \
1053
1054
1055
1056
1057
1058
1059








1060
1061
1062
1063
1064
1065
1066
$(OBJDIR)/cgi_.c:	$(SRCDIR)/cgi.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/cgi.c >$@

$(OBJDIR)/cgi.o:	$(OBJDIR)/cgi_.c $(OBJDIR)/cgi.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/cgi.o -c $(OBJDIR)/cgi_.c

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









$(OBJDIR)/checkin_.c:	$(SRCDIR)/checkin.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/checkin.c >$@

$(OBJDIR)/checkin.o:	$(OBJDIR)/checkin_.c $(OBJDIR)/checkin.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/checkin.o -c $(OBJDIR)/checkin_.c








>
>
>
>
>
>
>
>







1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
$(OBJDIR)/cgi_.c:	$(SRCDIR)/cgi.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/cgi.c >$@

$(OBJDIR)/cgi.o:	$(OBJDIR)/cgi_.c $(OBJDIR)/cgi.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/cgi.o -c $(OBJDIR)/cgi_.c

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

$(OBJDIR)/chat_.c:	$(SRCDIR)/chat.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/chat.c >$@

$(OBJDIR)/chat.o:	$(OBJDIR)/chat_.c $(OBJDIR)/chat.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/chat.o -c $(OBJDIR)/chat_.c

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

$(OBJDIR)/checkin_.c:	$(SRCDIR)/checkin.c $(OBJDIR)/translate
	$(OBJDIR)/translate $(SRCDIR)/checkin.c >$@

$(OBJDIR)/checkin.o:	$(OBJDIR)/checkin_.c $(OBJDIR)/checkin.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/checkin.o -c $(OBJDIR)/checkin_.c

Changes to src/makemake.tcl.

41
42
43
44
45
46
47

48
49
50
51
52
53
54
  browse
  builtin
  bundle
  cache
  capabilities
  captcha
  cgi

  checkin
  checkout
  clearsign
  clone
  comformat
  configure
  content







>







41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
  browse
  builtin
  bundle
  cache
  capabilities
  captcha
  cgi
  chat
  checkin
  checkout
  clearsign
  clone
  comformat
  configure
  content
182
183
184
185
186
187
188

189
190
191
192
193
194
195
  markdown.md
  wiki.wiki
  *.js
  default.css
  style.*.css
  ../skins/*/*.txt
  sounds/*.wav

}

# Options used to compile the included SQLite library.
#
set SQLITE_OPTIONS {
  -DNDEBUG=1
  -DSQLITE_DQS=0







>







183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
  markdown.md
  wiki.wiki
  *.js
  default.css
  style.*.css
  ../skins/*/*.txt
  sounds/*.wav
  alerts/*.wav
}

# Options used to compile the included SQLite library.
#
set SQLITE_OPTIONS {
  -DNDEBUG=1
  -DSQLITE_DQS=0
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
 $(OBJDIR)/th.o <<<NEXT_LINE>>>
 $(OBJDIR)/th_lang.o <<<NEXT_LINE>>>
 $(OBJDIR)/th_tcl.o <<<NEXT_LINE>>>
 $(OBJDIR)/cson_amalgamation.o
}]

writeln {
$(APPNAME):	$(OBJDIR)/headers $(OBJDIR)/codecheck1 $(OBJ) $(EXTRAOBJ)
	$(OBJDIR)/codecheck1 $(TRANS_SRC)
	$(TCC) $(TCCFLAGS) -o $(APPNAME) $(OBJ) $(EXTRAOBJ) $(LIB)

# This rule prevents make from using its default rules to try build
# an executable named "manifest" out of the file named "manifest.c"
#
$(SRCDIR)/../manifest:
	# noop








|

|







445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
 $(OBJDIR)/th.o <<<NEXT_LINE>>>
 $(OBJDIR)/th_lang.o <<<NEXT_LINE>>>
 $(OBJDIR)/th_tcl.o <<<NEXT_LINE>>>
 $(OBJDIR)/cson_amalgamation.o
}]

writeln {
$(APPNAME):	$(OBJDIR)/headers $(OBJDIR)/codecheck1 $(EXTRAOBJ) $(OBJ)
	$(OBJDIR)/codecheck1 $(TRANS_SRC)
	$(TCC) $(TCCFLAGS) -o $(APPNAME) $(EXTRAOBJ) $(OBJ) $(LIB)

# This rule prevents make from using its default rules to try build
# an executable named "manifest" out of the file named "manifest.c"
#
$(SRCDIR)/../manifest:
	# noop

1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190

APPTARGETS += $(BLDTARGETS)

ifdef FOSSIL_BUILD_SSL
APPTARGETS += openssl
endif

$(APPNAME):	$(APPTARGETS) $(OBJDIR)/headers $(CODECHECK1) $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o
	$(CODECHECK1) $(TRANS_SRC)
	$(TCC) -o $@ $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o $(LIB)

# This rule prevents make from using its default rules to try build
# an executable named "manifest" out of the file named "manifest.c"
#
$(SRCDIR)/../manifest:
	# noop








|

|







1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192

APPTARGETS += $(BLDTARGETS)

ifdef FOSSIL_BUILD_SSL
APPTARGETS += openssl
endif

$(APPNAME):	$(APPTARGETS) $(OBJDIR)/headers $(CODECHECK1) $(EXTRAOBJ) $(OBJ) $(OBJDIR)/fossil.o
	$(CODECHECK1) $(TRANS_SRC)
	$(TCC) -o $@ $(EXTRAOBJ) $(OBJ) $(OBJDIR)/fossil.o $(LIB)

# This rule prevents make from using its default rules to try build
# an executable named "manifest" out of the file named "manifest.c"
#
$(SRCDIR)/../manifest:
	# noop

Changes to src/manifest.c.

166
167
168
169
170
171
172

























173
174
175
176
177
178
179

/*
** True if manifest_crosslink_begin() has been called but
** manifest_crosslink_end() is still pending.
*/
static int manifest_crosslink_busy = 0;


























/*
** Clear the memory allocated in a manifest object
*/
void manifest_destroy(Manifest *p){
  if( p ){
    blob_reset(&p->content);
    fossil_free(p->aFile);







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







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

/*
** True if manifest_crosslink_begin() has been called but
** manifest_crosslink_end() is still pending.
*/
static int manifest_crosslink_busy = 0;

/*
** There are some triggers that need to fire whenever new content
** is added to the EVENT table, to make corresponding changes to the
** PENDING_ALERT and CHAT tables.  These are done with TEMP triggers
** which are created as needed.  The reasons for using TEMP triggers:
**
**    *  A small minority of invocations of Fossil need to use those triggers.
**       So we save CPU cycles in the common case by not having to parse the
**       trigger definition
**
**    *  We don't have to worry about dangling table references inside
**       of triggers.  For example, we can create a trigger that adds
**       to the CHAT table.  But an admin can still drop that CHAT table
**       at any moment, since the trigger that refers to CHAT is a TEMP
**       trigger and won't persist to cause problems.
**
**    *  Because TEMP triggers are defined by the specific version of the
**       application that is running, we don't have to worry with legacy
**       compatibility of the triggers.
**
** This boolean variable is set when the TEMP triggers for EVENT
** have been created.
*/
static int manifest_event_triggers_are_enabled = 0;

/*
** Clear the memory allocated in a manifest object
*/
void manifest_destroy(Manifest *p){
  if( p ){
    blob_reset(&p->content);
    fossil_free(p->aFile);
1936
1937
1938
1939
1940
1941
1942

1943
1944
1945
1946
1947
1948
1949
** processing that must be deferred until all artifacts have been
** seen at least once.  The deferred processing is accomplished
** by the call to manifest_crosslink_end().
*/
void manifest_crosslink_begin(void){
  assert( manifest_crosslink_busy==0 );
  manifest_crosslink_busy = 1;

  db_begin_transaction();
  db_multi_exec(
     "CREATE TEMP TABLE pending_xlink(id TEXT PRIMARY KEY)WITHOUT ROWID;"
     "CREATE TEMP TABLE time_fudge("
     "  mid INTEGER PRIMARY KEY,"    /* The rid of a manifest */
     "  m1 REAL,"                    /* The timestamp on mid */
     "  cid INTEGER,"                /* A child or mid */







>







1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
1971
1972
1973
1974
1975
** processing that must be deferred until all artifacts have been
** seen at least once.  The deferred processing is accomplished
** by the call to manifest_crosslink_end().
*/
void manifest_crosslink_begin(void){
  assert( manifest_crosslink_busy==0 );
  manifest_crosslink_busy = 1;
  manifest_create_event_triggers();
  db_begin_transaction();
  db_multi_exec(
     "CREATE TEMP TABLE pending_xlink(id TEXT PRIMARY KEY)WITHOUT ROWID;"
     "CREATE TEMP TABLE time_fudge("
     "  mid INTEGER PRIMARY KEY,"    /* The rid of a manifest */
     "  m1 REAL,"                    /* The timestamp on mid */
     "  cid INTEGER,"                /* A child or mid */
2054
2055
2056
2057
2058
2059
2060






















2061
2062
2063
2064
2065
2066
2067
  }
  db_multi_exec("DROP TABLE time_fudge;");

  db_end_transaction(0);
  manifest_crosslink_busy = 0;
  return ( rc!=TH_ERROR );
}























/*
** Make an entry in the event table for a ticket change artifact.
*/
void manifest_ticket_event(
  int rid,                    /* Artifact ID of the change ticket artifact */
  const Manifest *pManifest,  /* Parsed content of the artifact */







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







2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
  }
  db_multi_exec("DROP TABLE time_fudge;");

  db_end_transaction(0);
  manifest_crosslink_busy = 0;
  return ( rc!=TH_ERROR );
}

/*
** Activate EVENT triggers if they do not already exist.
*/
void manifest_create_event_triggers(void){
  if( manifest_event_triggers_are_enabled ){
    return;  /* Triggers already exists.  No-op. */
  }
  alert_create_trigger();
  manifest_event_triggers_are_enabled = 1;  
}

/*
** Disable manifest event triggers.  Drop them if they exist, but mark
** them has having been created so that they won't be recreated.  This
** is used during "rebuild" to prevent triggers from firing then.
*/
void manifest_disable_event_triggers(void){
  alert_drop_trigger();
  manifest_event_triggers_are_enabled = 1;
}


/*
** Make an entry in the event table for a ticket change artifact.
*/
void manifest_ticket_event(
  int rid,                    /* Artifact ID of the change ticket artifact */
  const Manifest *pManifest,  /* Parsed content of the artifact */
2124
2125
2126
2127
2128
2129
2130

2131
2132
2133
2134
2135
2136
2137
    blob_appendf(&comment, "New ticket [%!S|%S] <i>%h</i>.",
      pManifest->zTicketUuid, pManifest->zTicketUuid, zTitle
    );
    blob_appendf(&brief, "New ticket [%!S|%S].", pManifest->zTicketUuid,
        pManifest->zTicketUuid);
  }
  fossil_free(zTitle);

  db_multi_exec(
    "REPLACE INTO event(type,tagid,mtime,objid,user,comment,brief)"
    "VALUES('t',%d,%.17g,%d,%Q,%Q,%Q)",
    tktTagId, pManifest->rDate, rid, pManifest->zUser,
    blob_str(&comment), blob_str(&brief)
  );
  blob_reset(&comment);







>







2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
    blob_appendf(&comment, "New ticket [%!S|%S] <i>%h</i>.",
      pManifest->zTicketUuid, pManifest->zTicketUuid, zTitle
    );
    blob_appendf(&brief, "New ticket [%!S|%S].", pManifest->zTicketUuid,
        pManifest->zTicketUuid);
  }
  fossil_free(zTitle);
  manifest_create_event_triggers();
  db_multi_exec(
    "REPLACE INTO event(type,tagid,mtime,objid,user,comment,brief)"
    "VALUES('t',%d,%.17g,%d,%Q,%Q,%Q)",
    tktTagId, pManifest->rDate, rid, pManifest->zUser,
    blob_str(&comment), blob_str(&brief)
  );
  blob_reset(&comment);
2225
2226
2227
2228
2229
2230
2231

2232
2233
2234
2235
2236
2237
2238
  int permitHooks = (flags & MC_PERMIT_HOOKS);
  const char *zScript = 0;
  const char *zUuid = 0;

  if( g.fSqlTrace ){
    fossil_trace("-- manifest_crosslink(%d)\n", rid);
  }

  if( (p = manifest_cache_find(rid))!=0 ){
    blob_reset(pContent);
  }else if( (p = manifest_parse(pContent, rid, 0))==0 ){
    assert( blob_is_reset(pContent) || pContent==0 );
    if( (flags & MC_NO_ERRORS)==0 ){
      char * zErrUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d",rid);
      fossil_error(1, "syntax error in manifest [%S]", zErrUuid);







>







2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
  int permitHooks = (flags & MC_PERMIT_HOOKS);
  const char *zScript = 0;
  const char *zUuid = 0;

  if( g.fSqlTrace ){
    fossil_trace("-- manifest_crosslink(%d)\n", rid);
  }
  manifest_create_event_triggers();
  if( (p = manifest_cache_find(rid))!=0 ){
    blob_reset(pContent);
  }else if( (p = manifest_parse(pContent, rid, 0))==0 ){
    assert( blob_is_reset(pContent) || pContent==0 );
    if( (flags & MC_NO_ERRORS)==0 ){
      char * zErrUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d",rid);
      fossil_error(1, "syntax error in manifest [%S]", zErrUuid);
2272
2273
2274
2275
2276
2277
2278

2279
2280
2281
2282
2283
2284
2285
        );
      }
    }
    if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
      char *zCom;
      parentid = manifest_add_checkin_linkages(rid,p,p->nParent,p->azParent);
      search_doc_touch('c', rid, 0);

      db_multi_exec(
        "REPLACE INTO event(type,mtime,objid,user,comment,"
                           "bgcolor,euser,ecomment,omtime)"
        "VALUES('ci',"
        "  coalesce("
        "    (SELECT julianday(value) FROM tagxref WHERE tagid=%d AND rid=%d),"
        "    %.17g"







>







2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
        );
      }
    }
    if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
      char *zCom;
      parentid = manifest_add_checkin_linkages(rid,p,p->nParent,p->azParent);
      search_doc_touch('c', rid, 0);
      assert( manifest_event_triggers_are_enabled );
      db_multi_exec(
        "REPLACE INTO event(type,mtime,objid,user,comment,"
                           "bgcolor,euser,ecomment,omtime)"
        "VALUES('ci',"
        "  coalesce("
        "    (SELECT julianday(value) FROM tagxref WHERE tagid=%d AND rid=%d),"
        "    %.17g"
2387
2388
2389
2390
2391
2392
2393

2394
2395
2396
2397
2398
2399
2400
    }
    search_doc_touch('w',rid,p->zWikiTitle);
    if( manifest_crosslink_busy ){
      add_pending_crosslink('w',p->zWikiTitle);
    }else{
      backlink_wiki_refresh(p->zWikiTitle);
    }

    db_multi_exec(
      "REPLACE INTO event(type,mtime,objid,user,comment)"
      "VALUES('w',%.17g,%d,%Q,'%c%q');",
      p->rDate, rid, p->zUser, cPrefix, p->zWikiTitle
    );
  }
  if( p->type==CFTYPE_EVENT ){







>







2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
    }
    search_doc_touch('w',rid,p->zWikiTitle);
    if( manifest_crosslink_busy ){
      add_pending_crosslink('w',p->zWikiTitle);
    }else{
      backlink_wiki_refresh(p->zWikiTitle);
    }
    assert( manifest_event_triggers_are_enabled );
    db_multi_exec(
      "REPLACE INTO event(type,mtime,objid,user,comment)"
      "VALUES('w',%.17g,%d,%Q,'%c%q');",
      p->rDate, rid, p->zUser, cPrefix, p->zWikiTitle
    );
  }
  if( p->type==CFTYPE_EVENT ){
2433
2434
2435
2436
2437
2438
2439

2440
2441
2442
2443
2444
2445
2446
        );
      }
    }
    if( subsequent ){
      content_deltify(rid, &subsequent, 1, 0);
    }else{
      search_doc_touch('e',rid,0);

      db_multi_exec(
        "REPLACE INTO event(type,mtime,objid,tagid,user,comment,bgcolor)"
        "VALUES('e',%.17g,%d,%d,%Q,%Q,"
        "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
        p->rEventDate, rid, tagid, p->zUser, p->zComment,
        TAG_BGCOLOR, rid
      );







>







2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
        );
      }
    }
    if( subsequent ){
      content_deltify(rid, &subsequent, 1, 0);
    }else{
      search_doc_touch('e',rid,0);
      assert( manifest_event_triggers_are_enabled );
      db_multi_exec(
        "REPLACE INTO event(type,mtime,objid,tagid,user,comment,bgcolor)"
        "VALUES('e',%.17g,%d,%d,%Q,%Q,"
        "  (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d));",
        p->rEventDate, rid, tagid, p->zUser, p->zComment,
        TAG_BGCOLOR, rid
      );
2573
2574
2575
2576
2577
2578
2579

2580
2581
2582
2583
2584
2585
2586
             "Add attachment [/artifact/%!S|%h] to ticket [%!S|%S]",
             p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget);
      }else{
        zComment = mprintf("Delete attachment \"%h\" from ticket [%!S|%S]",
             p->zAttachName, p->zAttachTarget, p->zAttachTarget);
      }
    }

    db_multi_exec(
        "REPLACE INTO event(type,mtime,objid,user,comment)"
        "VALUES('%c',%.17g,%d,%Q,%Q)",
        attachToType, p->rDate, rid, p->zUser, zComment
    );
    fossil_free(zComment);
  }







>







2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
             "Add attachment [/artifact/%!S|%h] to ticket [%!S|%S]",
             p->zAttachSrc, p->zAttachName, p->zAttachTarget, p->zAttachTarget);
      }else{
        zComment = mprintf("Delete attachment \"%h\" from ticket [%!S|%S]",
             p->zAttachName, p->zAttachTarget, p->zAttachTarget);
      }
    }
    assert( manifest_event_triggers_are_enabled );
    db_multi_exec(
        "REPLACE INTO event(type,mtime,objid,user,comment)"
        "VALUES('%c',%.17g,%d,%Q,%Q)",
        attachToType, p->rDate, rid, p->zUser, zComment
    );
    fossil_free(zComment);
  }
2676
2677
2678
2679
2680
2681
2682

2683
2684
2685
2686
2687
2688
2689
        blob_appendf(&comment, " with note \"%h\".", zValue);
      }else{
        blob_appendf(&comment, ".");
      }
    }
    /*blob_appendf(&comment, " &#91;[/info/%S | details]&#93;");*/
    if( blob_size(&comment)==0 ) blob_append(&comment, " ", 1);

    db_multi_exec(
      "REPLACE INTO event(type,mtime,objid,user,comment)"
      "VALUES('g',%.17g,%d,%Q,%Q)",
      p->rDate, rid, p->zUser, blob_str(&comment)+1
    );
    blob_reset(&comment);
  }







>







2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
        blob_appendf(&comment, " with note \"%h\".", zValue);
      }else{
        blob_appendf(&comment, ".");
      }
    }
    /*blob_appendf(&comment, " &#91;[/info/%S | details]&#93;");*/
    if( blob_size(&comment)==0 ) blob_append(&comment, " ", 1);
    assert( manifest_event_triggers_are_enabled );
    db_multi_exec(
      "REPLACE INTO event(type,mtime,objid,user,comment)"
      "VALUES('g',%.17g,%d,%Q,%Q)",
      p->rDate, rid, p->zUser, blob_str(&comment)+1
    );
    blob_reset(&comment);
  }
2705
2706
2707
2708
2709
2710
2711

2712
2713
2714
2715
2716
2717
2718
      /* This is the start of a new thread, either the initial entry
      ** or an edit of the initial entry. */
      zTitle = p->zThreadTitle;
      if( zTitle==0 || zTitle[0]==0 ){
        zTitle = "(Deleted)";
      }
      zFType = fprev ? "Edit" : "Post";

      db_multi_exec(
        "REPLACE INTO event(type,mtime,objid,user,comment)"
        "VALUES('f',%.17g,%d,%Q,'%q: %q')",
        p->rDate, rid, p->zUser, zFType, zTitle
      );
      /*
      ** If this edit is the most recent, then make it the title for







>







2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
      /* This is the start of a new thread, either the initial entry
      ** or an edit of the initial entry. */
      zTitle = p->zThreadTitle;
      if( zTitle==0 || zTitle[0]==0 ){
        zTitle = "(Deleted)";
      }
      zFType = fprev ? "Edit" : "Post";
      assert( manifest_event_triggers_are_enabled );
      db_multi_exec(
        "REPLACE INTO event(type,mtime,objid,user,comment)"
        "VALUES('f',%.17g,%d,%Q,'%q: %q')",
        p->rDate, rid, p->zUser, zFType, zTitle
      );
      /*
      ** If this edit is the most recent, then make it the title for
2737
2738
2739
2740
2741
2742
2743

2744
2745
2746
2747
2748
2749
2750
      if( p->zWiki[0]==0 ){
        zFType = "Delete reply";
      }else if( fprev ){
        zFType = "Edit reply";
      }else{
        zFType = "Reply";
      }

      db_multi_exec(
        "REPLACE INTO event(type,mtime,objid,user,comment)"
        "VALUES('f',%.17g,%d,%Q,'%q: %q')",
        p->rDate, rid, p->zUser, zFType, zTitle
      );
      fossil_free(zTitle);
    }







>







2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
      if( p->zWiki[0]==0 ){
        zFType = "Delete reply";
      }else if( fprev ){
        zFType = "Edit reply";
      }else{
        zFType = "Reply";
      }
      assert( manifest_event_triggers_are_enabled );
      db_multi_exec(
        "REPLACE INTO event(type,mtime,objid,user,comment)"
        "VALUES('f',%.17g,%d,%Q,'%q: %q')",
        p->rDate, rid, p->zUser, zFType, zTitle
      );
      fossil_free(zTitle);
    }

Changes to src/moderate.c.

187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
        " AND event.objid IN (SELECT objid FROM modreq)"
        " ORDER BY event.mtime DESC"
    );
    db_prepare(&q, "%s", blob_sql_text(&sql));
    www_print_timeline(&q, 0, 0, 0, 0, 0, 0, 0);
    db_finalize(&q);
  }
  style_finish_page("modreq");
}

/*
** Disapproves any entries in the modreq table which belong to any
** user whose name is no longer found in the user table. This is only
** intended to be called after user deletion via /setup_uedit.
**







|







187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
        " AND event.objid IN (SELECT objid FROM modreq)"
        " ORDER BY event.mtime DESC"
    );
    db_prepare(&q, "%s", blob_sql_text(&sql));
    www_print_timeline(&q, 0, 0, 0, 0, 0, 0, 0);
    db_finalize(&q);
  }
  style_finish_page();
}

/*
** Disapproves any entries in the modreq table which belong to any
** user whose name is no longer found in the user table. This is only
** intended to be called after user deletion via /setup_uedit.
**

Changes to src/name.c.

179
180
181
182
183
184
185











































186
187
188
189
190
191
192
  if( eType==2 && ans>0 ){
    zBr = branch_of_rid(ans);
    ans = compute_youngest_ancestor_in_branch(rid, zBr);
    fossil_free(zBr);
  }
  return ans;
}












































/*
** Convert a symbolic name into a RID.  Acceptable forms:
**
**   *  artifact hash (optionally enclosed in [...])
**   *  4-character or larger prefix of a artifact
**   *  Symbolic Name







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







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
  if( eType==2 && ans>0 ){
    zBr = branch_of_rid(ans);
    ans = compute_youngest_ancestor_in_branch(rid, zBr);
    fossil_free(zBr);
  }
  return ans;
}

/*
** Find the RID of the most recent object with symbolic tag zTag
** and having a type that matches zType.
**
** Return 0 if there are no matches.
**
** This is a tricky query to do efficiently.  
** If the tag is very common (ex: "trunk") then
** we want to use the query identified below as Q1 - which searching
** the most recent EVENT table entries for the most recent with the tag.
** But if the tag is relatively scarce (anything other than "trunk", basically)
** then we want to do the indexed search show below as Q2.
*/
static int most_recent_event_with_tag(const char *zTag, const char *zType){
  return db_int(0,
    "SELECT objid FROM ("
      /* Q1:  Begin by looking for the tag in the 30 most recent events */
      "SELECT objid"
       " FROM (SELECT * FROM event ORDER BY mtime DESC LIMIT 30) AS ex"
      " WHERE type GLOB '%q'"
        " AND EXISTS(SELECT 1 FROM tagxref, tag"
                     " WHERE tag.tagname='sym-%q'"
                       " AND tagxref.tagid=tag.tagid"
                       " AND tagxref.tagtype>0"
                       " AND tagxref.rid=ex.objid)"
      " ORDER BY mtime DESC LIMIT 1"
    ") UNION ALL SELECT * FROM ("
      /* Q2: If the tag is not found in the 30 most recent events, then using
      ** the tagxref table to index for the tag */
      "SELECT event.objid"
       " FROM tag, tagxref, event"
      " WHERE tag.tagname='sym-%q'"
        " AND tagxref.tagid=tag.tagid"
        " AND tagxref.tagtype>0"
        " AND event.objid=tagxref.rid"
        " AND event.type GLOB '%q'"
      " ORDER BY event.mtime DESC LIMIT 1"
    ") LIMIT 1;",
    zType, zTag, zTag, zType
  );
}


/*
** Convert a symbolic name into a RID.  Acceptable forms:
**
**   *  artifact hash (optionally enclosed in [...])
**   *  4-character or larger prefix of a artifact
**   *  Symbolic Name
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
  int rid = 0;
  int nTag;
  int i;
  int startOfBranch = 0;
  const char *zXTag;     /* zTag with optional [...] removed */
  int nXTag;             /* Size of zXTag */
  const char *zDate;     /* Expanded date-time string */
  const char *zTagPrefix = "sym";

  if( zType==0 || zType[0]==0 ){
    zType = "*";
  }else if( zType[0]=='b' ){
    zType = "ci";
    startOfBranch = 1;
  }







<







266
267
268
269
270
271
272

273
274
275
276
277
278
279
  int rid = 0;
  int nTag;
  int i;
  int startOfBranch = 0;
  const char *zXTag;     /* zTag with optional [...] removed */
  int nXTag;             /* Size of zXTag */
  const char *zDate;     /* Expanded date-time string */


  if( zType==0 || zType[0]==0 ){
    zType = "*";
  }else if( zType[0]=='b' ){
    zType = "ci";
    startOfBranch = 1;
  }
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
      " ORDER BY mtime DESC LIMIT 1",
      fossil_roundup_date(&zTag[4]), zType);
    return rid;
  }

  /* "tag:" + symbolic-name */
  if( memcmp(zTag, "tag:", 4)==0 ){
    rid = db_int(0,
       "SELECT event.objid, max(event.mtime)"
       "  FROM tag, tagxref, event"
       " WHERE tag.tagname='sym-%q' "
       "   AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
       "   AND event.objid=tagxref.rid "
       "   AND event.type GLOB '%q'",
       &zTag[4], zType
    );
    if( startOfBranch ) rid = start_of_branch(rid,1);
    return rid;
  }

  /* root:BR -> The origin of the branch named BR */
  if( strncmp(zTag, "root:", 5)==0 ){
    rid = symbolic_name_to_rid(zTag+5, zType);







<
<
<
<
<
<
<
|
<







341
342
343
344
345
346
347







348

349
350
351
352
353
354
355
      " ORDER BY mtime DESC LIMIT 1",
      fossil_roundup_date(&zTag[4]), zType);
    return rid;
  }

  /* "tag:" + symbolic-name */
  if( memcmp(zTag, "tag:", 4)==0 ){







    rid = most_recent_event_with_tag(&zTag[4], zType);

    if( startOfBranch ) rid = start_of_branch(rid,1);
    return rid;
  }

  /* root:BR -> The origin of the branch named BR */
  if( strncmp(zTag, "root:", 5)==0 ){
    rid = symbolic_name_to_rid(zTag+5, zType);
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
      if( db_step(&q)==SQLITE_ROW ) rid = -1;
    }
    db_finalize(&q);
    if( rid ) return rid;
  }

  if( zType[0]=='w' ){
    zTagPrefix = "wiki";
  }
  /* Symbolic name */
  rid = db_int(0,
    "SELECT event.objid, max(event.mtime)"
    "  FROM tag, tagxref, event"
    " WHERE tag.tagname='%q-%q' "
    "   AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
    "   AND event.objid=tagxref.rid "
    "   AND event.type GLOB '%q'",
    zTagPrefix, zTag, zType
  );




  if( rid>0 ){
    if( startOfBranch ) rid = start_of_branch(rid,1);
    return rid;
  }

  /* Pure numeric date/time */







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







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
      if( db_step(&q)==SQLITE_ROW ) rid = -1;
    }
    db_finalize(&q);
    if( rid ) return rid;
  }

  if( zType[0]=='w' ){



    rid = db_int(0,
      "SELECT event.objid, max(event.mtime)"
      "  FROM tag, tagxref, event"
      " WHERE tag.tagname='wiki-%q' "
      "   AND tagxref.tagid=tag.tagid AND tagxref.tagtype>0 "
      "   AND event.objid=tagxref.rid "
      "   AND event.type GLOB '%q'",
      zTag, zType
    );
  }else{
    rid = most_recent_event_with_tag(zTag, zType);
  }

  if( rid>0 ){
    if( startOfBranch ) rid = start_of_branch(rid,1);
    return rid;
  }

  /* Pure numeric date/time */
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
    @ <ul><li>
    object_description(rid, 0, 0, 0);
    @ </li></ul>
    @ </p></li>
  }
  @ </ol>
  db_finalize(&q);
  style_finish_page("ambiguous");
}

/*
** Convert the name in CGI parameter zParamName into a rid and return that
** rid.  If the CGI parameter is missing or is not a valid artifact tag,
** return 0.  If the CGI parameter is ambiguous, redirect to a page that
** shows all possibilities and do not return.







|







700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
    @ <ul><li>
    object_description(rid, 0, 0, 0);
    @ </li></ul>
    @ </p></li>
  }
  @ </ol>
  db_finalize(&q);
  style_finish_page();
}

/*
** Convert the name in CGI parameter zParamName into a rid and return that
** rid.  If the CGI parameter is missing or is not a valid artifact tag,
** return 0.  If the CGI parameter is ambiguous, redirect to a page that
** shows all possibilities and do not return.
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
    @ <p>Select a range of artifacts to view:</p>
    @ <ul>
    for(i=1; i<=mx; i+=n){
      @ <li> %z(href("%R/bloblist?s=%d&n=%d",i,n))
      @ %d(i)..%d(i+n-1<mx?i+n-1:mx)</a>
    }
    @ </ul>
    style_finish_page("bloblist");
    return;
  }
  if( phantomOnly || privOnly || mx>n ){
    style_submenu_element("Index", "bloblist");
  }
  if( privOnly ){
    zRange = mprintf("IN private");







|







1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
    @ <p>Select a range of artifacts to view:</p>
    @ <ul>
    for(i=1; i<=mx; i+=n){
      @ <li> %z(href("%R/bloblist?s=%d&n=%d",i,n))
      @ %d(i)..%d(i+n-1<mx?i+n-1:mx)</a>
    }
    @ </ul>
    style_finish_page();
    return;
  }
  if( phantomOnly || privOnly || mx>n ){
    style_submenu_element("Index", "bloblist");
  }
  if( privOnly ){
    zRange = mprintf("IN private");
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
    }else{
      @ <td>&nbsp;
    }
    @ </tr>
  }
  @ </table>
  db_finalize(&q);
  style_finish_page("bloblist");
}

/*
** Output HTML that shows a table of all public phantoms.
*/
void table_of_public_phantoms(void){
  Stmt q;







|







1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
    }else{
      @ <td>&nbsp;
    }
    @ </tr>
  }
  @ </table>
  db_finalize(&q);
  style_finish_page();
}

/*
** Output HTML that shows a table of all public phantoms.
*/
void table_of_public_phantoms(void){
  Stmt q;
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
    style_submenu_element("Artifact Log", "rcvfromlist");
    style_submenu_element("Artifact List", "bloblist");
  }
  if( g.perm.Write ){
    style_submenu_element("Artifact Stats", "artifact_stats");
  }
  table_of_public_phantoms();
  style_finish_page("phantoms");
}

/*
** WEBPAGE: bigbloblist
**
** Return a page showing the largest artifacts in the repository in order
** of decreasing size.







|







1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
    style_submenu_element("Artifact Log", "rcvfromlist");
    style_submenu_element("Artifact List", "bloblist");
  }
  if( g.perm.Write ){
    style_submenu_element("Artifact Stats", "artifact_stats");
  }
  table_of_public_phantoms();
  style_finish_page();
}

/*
** WEBPAGE: bigbloblist
**
** Return a page showing the largest artifacts in the repository in order
** of decreasing size.
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
    @ <td align="left">%h(zDesc)</td>
    @ <td align="left">%z(href("%R/timeline?c=%T",zDate))%s(zDate)</a></td>
    @ </tr>
  }
  @ </tbody></table>
  db_finalize(&q);
  style_table_sorter();
  style_finish_page("bigbloblist");
}

/*
** COMMAND: test-unsent
**
** Usage: %fossil test-unsent
**







|







1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
    @ <td align="left">%h(zDesc)</td>
    @ <td align="left">%z(href("%R/timeline?c=%T",zDate))%s(zDate)</a></td>
    @ </tr>
  }
  @ </tbody></table>
  db_finalize(&q);
  style_table_sorter();
  style_finish_page();
}

/*
** COMMAND: test-unsent
**
** Usage: %fossil test-unsent
**
1675
1676
1677
1678
1679
1680
1681
1682
1683
  style_submenu_element("Stats", "stat");
  @ <h1>Hash Prefix Collisions on Check-ins</h1>
  collision_report("SELECT (SELECT uuid FROM blob WHERE rid=objid)"
                   "  FROM event WHERE event.type='ci'"
                   " ORDER BY 1");
  @ <h1>Hash Prefix Collisions on All Artifacts</h1>
  collision_report("SELECT uuid FROM blob ORDER BY 1");
  style_finish_page("hash-collisions");
}







|

1709
1710
1711
1712
1713
1714
1715
1716
1717
  style_submenu_element("Stats", "stat");
  @ <h1>Hash Prefix Collisions on Check-ins</h1>
  collision_report("SELECT (SELECT uuid FROM blob WHERE rid=objid)"
                   "  FROM event WHERE event.type='ci'"
                   " ORDER BY 1");
  @ <h1>Hash Prefix Collisions on All Artifacts</h1>
  collision_report("SELECT uuid FROM blob ORDER BY 1");
  style_finish_page();
}

Changes to src/path.c.

605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621

622
623
624
625
626
627
628
@  GROUP BY 2, 3;
;

/*
** WEBPAGE: test-rename-list
**
** Print a list of all file rename operations throughout history.
** This page is intended for for testing purposes only and may change
** or be discontinued without notice.
*/
void test_rename_list_page(void){
  Stmt q;
  int nRename;
  int nCheckin;

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }

  if( P("all")!=0 ){
    style_header("List Of All Filename Changes");
    db_multi_exec("%s", zRenameQuery/*safe-for-%s*/);
    style_submenu_element("Distinct", "%R/test-rename-list");
  }else{
    style_header("List Of Distinct Filename Changes");
    db_multi_exec("%s", zDistinctRenameQuery/*safe-for-%s*/);







|









>







605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
@  GROUP BY 2, 3;
;

/*
** WEBPAGE: test-rename-list
**
** Print a list of all file rename operations throughout history.
** This page is intended for testing purposes only and may change
** or be discontinued without notice.
*/
void test_rename_list_page(void){
  Stmt q;
  int nRename;
  int nCheckin;

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  style_set_current_feature("test");
  if( P("all")!=0 ){
    style_header("List Of All Filename Changes");
    db_multi_exec("%s", zRenameQuery/*safe-for-%s*/);
    style_submenu_element("Distinct", "%R/test-rename-list");
  }else{
    style_header("List Of Distinct Filename Changes");
    db_multi_exec("%s", zDistinctRenameQuery/*safe-for-%s*/);
649
650
651
652
653
654
655
656
657
    @ <td>%z(href("%R/finfo?name=%t",zOld))%h(zOld)</a></td>
    @ <td>%z(href("%R/finfo?name=%t",zNew))%h(zNew)</a></td>
    @ <td>%z(href("%R/info/%!S",zUuid))%S(zUuid)</a></td></tr>
  }
  @ </tbody></table>
  db_finalize(&q);
  style_table_sorter();
  style_finish_page("test");
}







|

650
651
652
653
654
655
656
657
658
    @ <td>%z(href("%R/finfo?name=%t",zOld))%h(zOld)</a></td>
    @ <td>%z(href("%R/finfo?name=%t",zNew))%h(zNew)</a></td>
    @ <td>%z(href("%R/info/%!S",zUuid))%S(zUuid)</a></td></tr>
  }
  @ </tbody></table>
  db_finalize(&q);
  style_table_sorter();
  style_finish_page();
}

Changes to src/piechart.c.

275
276
277
278
279
280
281

282
283
284
285
286
287
288
  Stmt ins;
  int n = 0;
  int width;
  int height;
  int i, j;

  login_check_credentials();

  style_header("Pie Chart Test");
  db_multi_exec("CREATE TEMP TABLE piechart(amt REAL, label TEXT);");
  db_prepare(&ins, "INSERT INTO piechart(amt,label) VALUES(:amt,:label)");
  zData = PD("data","");
  width = atoi(PD("width","800"));
  height = atoi(PD("height","400"));
  i = 0;







>







275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
  Stmt ins;
  int n = 0;
  int width;
  int height;
  int i, j;

  login_check_credentials();
  style_set_current_feature("test");
  style_header("Pie Chart Test");
  db_multi_exec("CREATE TEMP TABLE piechart(amt REAL, label TEXT);");
  db_prepare(&ins, "INSERT INTO piechart(amt,label) VALUES(:amt,:label)");
  zData = PD("data","");
  width = atoi(PD("width","800"));
  height = atoi(PD("height","400"));
  i = 0;
326
327
328
329
330
331
332
333
334
  @ <ul>
  @ <li> <a href='test-piechart?data=44,2,2,2,2,2,3,2,2,2,2,2,44'>Case 1</a>
  @ <li> <a href='test-piechart?data=2,2,2,2,2,44,44,2,2,2,2,2'>Case 2</a>
  @ <li> <a href='test-piechart?data=20,2,2,2,2,2,2,2,2,2,2,80'>Case 3</a>
  @ <li> <a href='test-piechart?data=80,2,2,2,2,2,2,2,2,2,2,20'>Case 4</a>
  @ <li> <a href='test-piechart?data=2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2'>Case 5</a>
  @ </ul>
  style_finish_page("test");
}







|

327
328
329
330
331
332
333
334
335
  @ <ul>
  @ <li> <a href='test-piechart?data=44,2,2,2,2,2,3,2,2,2,2,2,44'>Case 1</a>
  @ <li> <a href='test-piechart?data=2,2,2,2,2,44,44,2,2,2,2,2'>Case 2</a>
  @ <li> <a href='test-piechart?data=20,2,2,2,2,2,2,2,2,2,2,80'>Case 3</a>
  @ <li> <a href='test-piechart?data=80,2,2,2,2,2,2,2,2,2,2,20'>Case 4</a>
  @ <li> <a href='test-piechart?data=2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2'>Case 5</a>
  @ </ul>
  style_finish_page();
}

Changes to src/pikchrshow.c.

372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
      } CX("</div>"/*#pikchrshow-output*/);
    } CX("</fieldset>"/*#pikchrshow-output-wrapper*/);
  } CX("</div>"/*sbs-wrapper*/);
  builtin_fossil_js_bundle_or("fetch", "copybutton", "popupwidget",
                              "storage", "pikchr", NULL);
  builtin_request_js("fossil.page.pikchrshow.js");
  builtin_fulfill_js_requests();
  style_finish_page("pikchrshow");
}

/*
** COMMAND: pikchr*
**
** Usage: %fossil pikchr [options] ?INFILE? ?OUTFILE?
**







|







372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
      } CX("</div>"/*#pikchrshow-output*/);
    } CX("</fieldset>"/*#pikchrshow-output-wrapper*/);
  } CX("</div>"/*sbs-wrapper*/);
  builtin_fossil_js_bundle_or("fetch", "copybutton", "popupwidget",
                              "storage", "pikchr", NULL);
  builtin_request_js("fossil.page.pikchrshow.js");
  builtin_fulfill_js_requests();
  style_finish_page();
}

/*
** COMMAND: pikchr*
**
** Usage: %fossil pikchr [options] ?INFILE? ?OUTFILE?
**

Changes to src/printf.c.

1114
1115
1116
1117
1118
1119
1120

1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
  }
  else
#endif
  if( g.cgiOutput==1 && g.db ){
    g.cgiOutput = 2;
    cgi_reset_content();
    cgi_set_content_type("text/html");

    style_header("Bad Request");
    etag_cancel();
    @ <p class="generalError">%h(z)</p>
    cgi_set_status(400, "Bad Request");
    style_finish_page("error");
    cgi_reply();
  }else if( !g.fQuiet ){
    fossil_force_newline();
    fossil_trace("%s\n", z);
  }
  return rc;
}







>




|







1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
  }
  else
#endif
  if( g.cgiOutput==1 && g.db ){
    g.cgiOutput = 2;
    cgi_reset_content();
    cgi_set_content_type("text/html");
    style_set_current_feature("error");
    style_header("Bad Request");
    etag_cancel();
    @ <p class="generalError">%h(z)</p>
    cgi_set_status(400, "Bad Request");
    style_finish_page();
    cgi_reply();
  }else if( !g.fQuiet ){
    fossil_force_newline();
    fossil_trace("%s\n", z);
  }
  return rc;
}

Changes to src/rebuild.c.

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

  bag_clear(&bagDone);
  ttyOutput = doOut;
  processCnt = 0;
  if (ttyOutput && !g.fQuiet) {
    percent_complete(0);
  }
  alert_triggers_disable();
  rebuild_update_schema();
  blob_init(&sql, 0, 0);
  db_unprotect(PROTECT_ALL);
  db_prepare(&q,
     "SELECT name FROM sqlite_schema /*scan*/"
     " WHERE type='table'"
     " AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user','alias',"
                       "'config','shun','private','reportfmt',"
                       "'concealed','accesslog','modreq',"
                       "'purgeevent','purgeitem','unversioned',"
                       "'subscriber','pending_alert','alert_bounce')"
     " AND name NOT GLOB 'sqlite_*'"
     " AND name NOT GLOB 'fx_*'"
  );
  while( db_step(&q)==SQLITE_ROW ){
    blob_appendf(&sql, "DROP TABLE IF EXISTS \"%w\";\n", db_column_text(&q,0));
  }
  db_finalize(&q);







|










|







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

  bag_clear(&bagDone);
  ttyOutput = doOut;
  processCnt = 0;
  if (ttyOutput && !g.fQuiet) {
    percent_complete(0);
  }
  manifest_disable_event_triggers();
  rebuild_update_schema();
  blob_init(&sql, 0, 0);
  db_unprotect(PROTECT_ALL);
  db_prepare(&q,
     "SELECT name FROM sqlite_schema /*scan*/"
     " WHERE type='table'"
     " AND name NOT IN ('admin_log', 'blob','delta','rcvfrom','user','alias',"
                       "'config','shun','private','reportfmt',"
                       "'concealed','accesslog','modreq',"
                       "'purgeevent','purgeitem','unversioned',"
                       "'subscriber','pending_alert','alert_bounce','chat')"
     " AND name NOT GLOB 'sqlite_*'"
     " AND name NOT GLOB 'fx_*'"
  );
  while( db_step(&q)==SQLITE_ROW ){
    blob_appendf(&sql, "DROP TABLE IF EXISTS \"%w\";\n", db_column_text(&q,0));
  }
  db_finalize(&q);
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
    percent_complete((processCnt*1000)/totalSize);
  }
  if( doClustering ) create_cluster();
  if( ttyOutput && !g.fQuiet && totalSize>0 ){
    processCnt += incrSize;
    percent_complete((processCnt*1000)/totalSize);
  }
  alert_triggers_enable();
  if(!g.fQuiet && ttyOutput ){
    percent_complete(1000);
    fossil_print("\n");
  }
  db_protect_pop();
  return errCnt;
}







<







473
474
475
476
477
478
479

480
481
482
483
484
485
486
    percent_complete((processCnt*1000)/totalSize);
  }
  if( doClustering ) create_cluster();
  if( ttyOutput && !g.fQuiet && totalSize>0 ){
    processCnt += incrSize;
    percent_complete((processCnt*1000)/totalSize);
  }

  if(!g.fQuiet && ttyOutput ){
    percent_complete(1000);
    fossil_print("\n");
  }
  db_protect_pop();
  return errCnt;
}
940
941
942
943
944
945
946

947
948
949
950
951
952
953
        "UPDATE rcvfrom SET ipaddr='unknown';\n"
        "DROP TABLE IF EXISTS accesslog;\n"
        "UPDATE user SET photo=NULL, info='';\n"
        "DROP TABLE IF EXISTS purgeevent;\n"
        "DROP TABLE IF EXISTS purgeitem;\n"
        "DROP TABLE IF EXISTS admin_log;\n"
        "DROP TABLE IF EXISTS vcache;\n"

      );
    }
    db_protect_pop();
  }
  if( !bNeedRebuild ){
    db_end_transaction(0);
    db_unprotect(PROTECT_ALL);







>







939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
        "UPDATE rcvfrom SET ipaddr='unknown';\n"
        "DROP TABLE IF EXISTS accesslog;\n"
        "UPDATE user SET photo=NULL, info='';\n"
        "DROP TABLE IF EXISTS purgeevent;\n"
        "DROP TABLE IF EXISTS purgeitem;\n"
        "DROP TABLE IF EXISTS admin_log;\n"
        "DROP TABLE IF EXISTS vcache;\n"
        "DROP TABLE IF EXISTS chat;\n"
      );
    }
    db_protect_pop();
  }
  if( !bNeedRebuild ){
    db_end_transaction(0);
    db_unprotect(PROTECT_ALL);

Changes to src/regexp.c.

809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
** Options:
**
**     -c|--count                 Suppress normal output; instead print a count
**                                of the number of matching files
**     -i|--ignore-case           Ignore case
**     -l|--files-with-matches    List only hash for each match
**     --once                     Stop searching after the first match
**     -s|--no-messages           Suppress error messages about nonexistant
**                                or unreadable files
**     -v|--invert-match          Invert the sense of matching.  Show only
**                                files that have no matches. Implies -l
**     --verbose                  Show each file as it is analyzed
*/
void re_grep_cmd(void){
  u32 flags = 0;







|







809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
** Options:
**
**     -c|--count                 Suppress normal output; instead print a count
**                                of the number of matching files
**     -i|--ignore-case           Ignore case
**     -l|--files-with-matches    List only hash for each match
**     --once                     Stop searching after the first match
**     -s|--no-messages           Suppress error messages about nonexistent
**                                or unreadable files
**     -v|--invert-match          Invert the sense of matching.  Show only
**                                files that have no matches. Implies -l
**     --verbose                  Show each file as it is analyzed
*/
void re_grep_cmd(void){
  u32 flags = 0;

Changes to src/repolist.c.

243
244
245
246
247
248
249

250
251
252
253
254
255
256
257
258
259
260
  }
  if( g.repositoryOpen ){
    /* This case runs if remote_repository_info() found a repository
    ** that has the "repolist_skin" property set to non-zero and left
    ** that repository open in g.db.  Use the skin of that repository
    ** for display. */
    login_check_credentials();

    style_header("Repository List");
    @ %s(blob_str(&html))
    style_table_sorter();
    style_finish_page("repolist");
  }else{
    /* If no repositories were found that had the "repolist_skin"
    ** property set, then use a default skin */
    @ <html>
    @ <head>
    @ <base href="%s(g.zBaseURL)/" />
    @ <meta name="viewport" content="width=device-width, initial-scale=1.0">







>



|







243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
  }
  if( g.repositoryOpen ){
    /* This case runs if remote_repository_info() found a repository
    ** that has the "repolist_skin" property set to non-zero and left
    ** that repository open in g.db.  Use the skin of that repository
    ** for display. */
    login_check_credentials();
    style_set_current_feature("repolist");
    style_header("Repository List");
    @ %s(blob_str(&html))
    style_table_sorter();
    style_finish_page();
  }else{
    /* If no repositories were found that had the "repolist_skin"
    ** property set, then use a default skin */
    @ <html>
    @ <head>
    @ <base href="%s(g.zBaseURL)/" />
    @ <meta name="viewport" content="width=device-width, initial-scale=1.0">

Changes to src/report.c.

95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
  Th_Store("report_items", blob_str(&ril));

  Th_Render(zScript);

  blob_reset(&ril);
  if( g.thTrace ) Th_Trace("END_REPORTLIST<br />\n", -1);

  style_finish_page("reportlist");
}

/*
** Remove whitespace from both ends of a string.
*/
char *trim_string(const char *zOrig){
  int i;







|







95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
  Th_Store("report_items", blob_str(&ril));

  Th_Render(zScript);

  blob_reset(&ril);
  if( g.thTrace ) Th_Trace("END_REPORTLIST<br />\n", -1);

  style_finish_page();
}

/*
** Remove whitespace from both ends of a string.
*/
char *trim_string(const char *zOrig){
  int i;
323
324
325
326
327
328
329

330
331
332
333
334
335
336
337
338
339
340
  if( !g.perm.TktFmt ){
    login_needed(g.anon.TktFmt);
    return;
  }
  rn = atoi(PD("rn","0"));
  db_prepare(&q, "SELECT title, sqlcode, owner, cols "
                   "FROM reportfmt WHERE rn=%d",rn);

  style_header("SQL For Report Format Number %d", rn);
  if( db_step(&q)!=SQLITE_ROW ){
    @ <p>Unknown report number: %d(rn)</p>
    style_finish_page("report");
    db_finalize(&q);
    return;
  }
  zTitle = db_column_text(&q, 0);
  zSQL = db_column_text(&q, 1);
  zOwner = db_column_text(&q, 2);
  zClrKey = db_column_text(&q, 3);







>



|







323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
  if( !g.perm.TktFmt ){
    login_needed(g.anon.TktFmt);
    return;
  }
  rn = atoi(PD("rn","0"));
  db_prepare(&q, "SELECT title, sqlcode, owner, cols "
                   "FROM reportfmt WHERE rn=%d",rn);
  style_set_current_feature("report");
  style_header("SQL For Report Format Number %d", rn);
  if( db_step(&q)!=SQLITE_ROW ){
    @ <p>Unknown report number: %d(rn)</p>
    style_finish_page();
    db_finalize(&q);
    return;
  }
  zTitle = db_column_text(&q, 0);
  zSQL = db_column_text(&q, 1);
  zOwner = db_column_text(&q, 2);
  zClrKey = db_column_text(&q, 3);
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
  @ %h(zSQL)
  @ </pre></td>
  @ <td width=15></td><td valign="top">
  output_color_key(zClrKey, 0, "border=0 cellspacing=0 cellpadding=3");
  @ </td>
  @ </tr></table>
  report_format_hints();
  style_finish_page("report");
  db_finalize(&q);
}

/*
** WEBPAGE: rptnew
** WEBPAGE: rptedit
**







|







349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
  @ %h(zSQL)
  @ </pre></td>
  @ <td width=15></td><td valign="top">
  output_color_key(zClrKey, 0, "border=0 cellspacing=0 cellpadding=3");
  @ </td>
  @ </tr></table>
  report_format_hints();
  style_finish_page();
  db_finalize(&q);
}

/*
** WEBPAGE: rptnew
** WEBPAGE: rptedit
**
379
380
381
382
383
384
385

386
387
388
389
390
391
392
  char *zErr = 0;

  login_check_credentials();
  if( !g.perm.TktFmt ){
    login_needed(g.anon.TktFmt);
    return;
  }

  /*view_add_functions(0);*/
  rn = atoi(PD("rn","0"));
  zTitle = P("t");
  zOwner = PD("w",g.zLogin);
  z = P("s");
  zSQL = z ? trim_string(z) : 0;
  zClrKey = trim_string(PD("k",""));







>







380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
  char *zErr = 0;

  login_check_credentials();
  if( !g.perm.TktFmt ){
    login_needed(g.anon.TktFmt);
    return;
  }
  style_set_current_feature("report");
  /*view_add_functions(0);*/
  rn = atoi(PD("rn","0"));
  zTitle = P("t");
  zOwner = PD("w",g.zLogin);
  z = P("s");
  zSQL = z ? trim_string(z) : 0;
  zClrKey = trim_string(PD("k",""));
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
    @ related to this report will be removed and cannot be recovered.</p>
    @
    @ <input type="hidden" name="rn" value="%d(rn)">
    login_insert_csrf_secret();
    @ <input type="submit" name="del2" value="Delete The Report">
    @ <input type="submit" name="can" value="Cancel">
    @ </form>
    style_finish_page("report");
    return;
  }else if( P("can") ){
    /* user cancelled */
    cgi_redirect("reportlist");
    return;
  }
  if( zTitle && zSQL ){







|







410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
    @ related to this report will be removed and cannot be recovered.</p>
    @
    @ <input type="hidden" name="rn" value="%d(rn)">
    login_insert_csrf_secret();
    @ <input type="submit" name="del2" value="Delete The Report">
    @ <input type="submit" name="can" value="Cancel">
    @ </form>
    style_finish_page();
    return;
  }else if( P("can") ){
    /* user cancelled */
    cgi_redirect("reportlist");
    return;
  }
  if( zTitle && zSQL ){
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
  @ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea>
  @ </p>
  if( !g.perm.Admin && fossil_strcmp(zOwner,g.zLogin)!=0 ){
    @ <p>This report format is owned by %h(zOwner).  You are not allowed
    @ to change it.</p>
    @ </form>
    report_format_hints();
    style_finish_page("report");
    return;
  }
  @ <input type="submit" value="Apply Changes" />
  if( rn>0 ){
    @ <input type="submit" value="Delete This Report" name="del1" />
  }
  @ </div></form>
  report_format_hints();
  style_finish_page("report");
}

/*
** Output a bunch of text that provides information about report
** formats
*/
static void report_format_hints(void){







|








|







502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
  @ <textarea name="k" rows="8" cols="50">%h(zClrKey)</textarea>
  @ </p>
  if( !g.perm.Admin && fossil_strcmp(zOwner,g.zLogin)!=0 ){
    @ <p>This report format is owned by %h(zOwner).  You are not allowed
    @ to change it.</p>
    @ </form>
    report_format_hints();
    style_finish_page();
    return;
  }
  @ <input type="submit" value="Apply Changes" />
  if( rn>0 ){
    @ <input type="submit" value="Delete This Report" name="del1" />
  }
  @ </div></form>
  report_format_hints();
  style_finish_page();
}

/*
** Output a bunch of text that provides information about report
** formats
*/
static void report_format_hints(void){
1027
1028
1029
1030
1031
1032
1033

1034
1035
1036
1037
1038
1039
1040
  }

  count = 0;
  if( !tabs ){
    struct GenerateHTML sState = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };

    db_multi_exec("PRAGMA empty_result_callbacks=ON");

    style_submenu_element("Raw", "rptview?tablist=1&%h", PD("QUERY_STRING",""));
    if( g.perm.Admin
       || (g.perm.TktFmt && g.zLogin && fossil_strcmp(g.zLogin,zOwner)==0) ){
      style_submenu_element("Edit", "rptedit?rn=%d", rn);
    }
    if( g.perm.TktFmt ){
      style_submenu_element("SQL", "rptsql?rn=%d",rn);







>







1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
  }

  count = 0;
  if( !tabs ){
    struct GenerateHTML sState = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };

    db_multi_exec("PRAGMA empty_result_callbacks=ON");
    style_set_current_feature("report");
    style_submenu_element("Raw", "rptview?tablist=1&%h", PD("QUERY_STRING",""));
    if( g.perm.Admin
       || (g.perm.TktFmt && g.zLogin && fossil_strcmp(g.zLogin,zOwner)==0) ){
      style_submenu_element("Edit", "rptedit?rn=%d", rn);
    }
    if( g.perm.TktFmt ){
      style_submenu_element("SQL", "rptsql?rn=%d",rn);
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
    @ </tbody></table>
    if( zErr1 ){
      @ <p class="reportError">Error: %h(zErr1)</p>
    }else if( zErr2 ){
      @ <p class="reportError">Error: %h(zErr2)</p>
    }
    style_table_sorter();
    style_finish_page("report");
  }else{
    report_restrict_sql(&zErr1);
    db_exec_readonly(g.db, zSql, output_tab_separated, &count, &zErr2);
    report_unrestrict_sql();
    cgi_set_content_type("text/plain");
  }
}







|







1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
    @ </tbody></table>
    if( zErr1 ){
      @ <p class="reportError">Error: %h(zErr1)</p>
    }else if( zErr2 ){
      @ <p class="reportError">Error: %h(zErr2)</p>
    }
    style_table_sorter();
    style_finish_page();
  }else{
    report_restrict_sql(&zErr1);
    db_exec_readonly(g.db, zSql, output_tab_separated, &count, &zErr2);
    report_unrestrict_sql();
    cgi_set_content_type("text/plain");
  }
}

Changes to src/search.c.

641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
              "WHERE 1 ", -1);
  if(!fAll){
    blob_append_sql(&sql,"AND x>%d ", iBest/3);
  }
  blob_append(&sql, "ORDER BY x DESC, date DESC ", -1);
  db_prepare(&q, "%s", blob_sql_text(&sql));
  blob_reset(&sql);
  print_timeline(&q, nLimit, width, 0);
  db_finalize(&q);
}

#if INTERFACE
/* What to search for */
#define SRCH_CKIN     0x0001    /* Search over check-in comments */
#define SRCH_DOC      0x0002    /* Search over embedded documents */







|







641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
              "WHERE 1 ", -1);
  if(!fAll){
    blob_append_sql(&sql,"AND x>%d ", iBest/3);
  }
  blob_append(&sql, "ORDER BY x DESC, date DESC ", -1);
  db_prepare(&q, "%s", blob_sql_text(&sql));
  blob_reset(&sql);
  print_timeline(&q, nLimit, width, 0, 0);
  db_finalize(&q);
}

#if INTERFACE
/* What to search for */
#define SRCH_CKIN     0x0001    /* Search over check-in comments */
#define SRCH_DOC      0x0002    /* Search over embedded documents */
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
**                      f -> forum
**                    all -> everything
*/
void search_page(void){
  login_check_credentials();
  style_header("Search");
  search_screen(SRCH_ALL, 1);
  style_finish_page("search");
}


/*
** This is a helper function for search_stext().  Writing into pOut
** the search text obtained from pIn according to zMimetype.
**







|







1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
**                      f -> forum
**                    all -> everything
*/
void search_page(void){
  login_check_credentials();
  style_header("Search");
  search_screen(SRCH_ALL, 1);
  style_finish_page();
}


/*
** This is a helper function for search_stext().  Writing into pOut
** the search text obtained from pIn according to zMimetype.
**
1977
1978
1979
1980
1981
1982
1983

1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
  const char *zId = P("id");
  const char *zType = P("y");
  const char *zIdxed = P("ixed");
  int id;
  int cnt1 = 0, cnt2 = 0, cnt3 = 0;
  login_check_credentials();
  if( !g.perm.Admin ){ login_needed(0); return; }

  if( !search_index_exists() ){
    @ <p>Indexed search is disabled
    style_finish_page("report");
    return;
  }
  search_sql_setup(g.db);
  style_submenu_element("Setup","%R/srchsetup");
  if( zId!=0 && (id = atoi(zId))>0 ){
    /* Show information about a single ftsdocs entry */
    style_header("Information about ftsdoc entry %d", id);







>


|







1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
  const char *zId = P("id");
  const char *zType = P("y");
  const char *zIdxed = P("ixed");
  int id;
  int cnt1 = 0, cnt2 = 0, cnt3 = 0;
  login_check_credentials();
  if( !g.perm.Admin ){ login_needed(0); return; }
  style_set_current_feature("test");
  if( !search_index_exists() ){
    @ <p>Indexed search is disabled
    style_finish_page();
    return;
  }
  search_sql_setup(g.db);
  style_submenu_element("Setup","%R/srchsetup");
  if( zId!=0 && (id = atoi(zId))>0 ){
    /* Show information about a single ftsdocs entry */
    style_header("Information about ftsdoc entry %d", id);
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
      @ </table>
      zName = mprintf("Indexed '%c' docs",zDocId[0]);
      style_submenu_element(zName,"%R/test-ftsdocs?y=%c&ixed=1",zDocId[0]);
      zName = mprintf("Unindexed '%c' docs",zDocId[0]);
      style_submenu_element(zName,"%R/test-ftsdocs?y=%c&ixed=0",zDocId[0]);
    }
    db_finalize(&q);
    style_finish_page("test");
    return;
  }
  if( zType!=0 && zType[0]!=0 && zType[1]==0 &&
      zIdxed!=0 && (zIdxed[0]=='1' || zIdxed[0]=='0') && zIdxed[1]==0
  ){
    int ixed = zIdxed[0]=='1';
    char *zName;







|







2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
      @ </table>
      zName = mprintf("Indexed '%c' docs",zDocId[0]);
      style_submenu_element(zName,"%R/test-ftsdocs?y=%c&ixed=1",zDocId[0]);
      zName = mprintf("Unindexed '%c' docs",zDocId[0]);
      style_submenu_element(zName,"%R/test-ftsdocs?y=%c&ixed=0",zDocId[0]);
    }
    db_finalize(&q);
    style_finish_page();
    return;
  }
  if( zType!=0 && zType[0]!=0 && zType[1]==0 &&
      zIdxed!=0 && (zIdxed[0]=='1' || zIdxed[0]=='0') && zIdxed[1]==0
  ){
    int ixed = zIdxed[0]=='1';
    char *zName;
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
    @ <ul>
    while( db_step(&q)==SQLITE_ROW ){
      @ <li> <a href='test-ftsdocs?id=%d(db_column_int(&q,0))'>
      @ %h(db_column_text(&q,1))</a>
    }
    @ </ul>
    db_finalize(&q);
    style_finish_page("test");
    return;
  }
  style_header("Summary of ftsdocs");
  db_prepare(&q,
     "SELECT type, sum(idxed IS TRUE), sum(idxed IS FALSE), count(*)"
     "  FROM ftsdocs"
     " GROUP BY 1 ORDER BY 4 DESC"







|







2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
    @ <ul>
    while( db_step(&q)==SQLITE_ROW ){
      @ <li> <a href='test-ftsdocs?id=%d(db_column_int(&q,0))'>
      @ %h(db_column_text(&q,1))</a>
    }
    @ </ul>
    db_finalize(&q);
    style_finish_page();
    return;
  }
  style_header("Summary of ftsdocs");
  db_prepare(&q,
     "SELECT type, sum(idxed IS TRUE), sum(idxed IS FALSE), count(*)"
     "  FROM ftsdocs"
     " GROUP BY 1 ORDER BY 4 DESC"
2097
2098
2099
2100
2101
2102
2103
2104
2105
  }
  db_finalize(&q);
  @ </tbody><tfooter>
  @ <tr><th>Total<th align="right">%d(cnt1)<th align="right">%d(cnt2)
  @ <th align="right">%d(cnt3)
  @ </tfooter>
  @ </table>
  style_finish_page("test");
}







|

2098
2099
2100
2101
2102
2103
2104
2105
2106
  }
  db_finalize(&q);
  @ </tbody><tfooter>
  @ <tr><th>Total<th align="right">%d(cnt1)<th align="right">%d(cnt2)
  @ <th align="right">%d(cnt3)
  @ </tfooter>
  @ </table>
  style_finish_page();
}

Changes to src/security_audit.c.

589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
    @ </pre></blockquote>
    @ </p>
    table_of_public_phantoms();
    @ </li>
  }

  @ </ol>
  style_finish_page("secaudit");
}

/*
** WEBPAGE: takeitprivate
**
** Disable anonymous access to this website
*/







|







589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
    @ </pre></blockquote>
    @ </p>
    table_of_public_phantoms();
    @ </li>
  }

  @ </ol>
  style_finish_page();
}

/*
** WEBPAGE: takeitprivate
**
** Disable anonymous access to this website
*/
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
  @ <p>Click the "Cancel" button to leave things as they are.</p>
  @
  @ <form action="%s(g.zPath)" method="post">
  @ <input type="submit" name="apply" value="Make It Private">
  @ <input type="submit" name="cancel" value="Cancel">
  @ </form>

  style_finish_page("takeitprivate");
}

/*
** The maximum number of bytes of log to show
*/
#define MXSHOWLOG 50000








|







631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
  @ <p>Click the "Cancel" button to leave things as they are.</p>
  @
  @ <form action="%s(g.zPath)" method="post">
  @ <input type="submit" name="apply" value="Make It Private">
  @ <input type="submit" name="cancel" value="Cancel">
  @ </form>

  style_finish_page();
}

/*
** The maximum number of bytes of log to show
*/
#define MXSHOWLOG 50000

672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
    @ </pre></blockquote>
    @ <li><p>
    @ If the server is running using one of 
    @ the "fossil http" or "fossil server" commands then add
    @ a command-line option "--errorlog <i>FILENAME</i>" to that
    @ command.
    @ </ol>
    style_finish_page("errorlog");
    return;
  }
  if( P("truncate1") && cgi_csrf_safe(1) ){
    fclose(fopen(g.zErrlog,"w"));
  }
  if( P("download") ){
    Blob log;
    blob_read_from_file(&log, g.zErrlog, ExtFILE);
    cgi_set_content_type("text/plain");
    cgi_set_content(&log);
    return;
  }
  szFile = file_size(g.zErrlog, ExtFILE);
  if( P("truncate") ){
    @ <form action="%R/errorlog" method="POST">
    @ <p>Confirm that you want to truncate the %,lld(szFile)-byte error log:
    @ <input type="submit" name="truncate1" value="Confirm">
    @ <input type="submit" name="cancel" value="Cancel">
    @ </form>
    style_finish_page("errorlog");
    return;
  }
  @ <p>The server error log at "%h(g.zErrlog)" is %,lld(szFile) bytes in size.
  style_submenu_element("Download", "%R/errorlog?download");
  style_submenu_element("Truncate", "%R/errorlog?truncate");
  in = fossil_fopen(g.zErrlog, "rb");
  if( in==0 ){
    @ <p class='generalError'>Unable to open that file for reading!</p>
    style_finish_page("errorlog");
    return;
  }
  if( szFile>MXSHOWLOG && P("all")==0 ){
    @ <form action="%R/errorlog" method="POST">
    @ <p>Only the last %,d(MXSHOWLOG) bytes are shown.
    @ <input type="submit" name="all" value="Show All">
    @ </form>
    fseek(in, -MXSHOWLOG, SEEK_END);
  }
  @ <hr>
  @ <pre>
  while( fgets(z, sizeof(z), in) ){
    @ %h(z)\
  }
  fclose(in);
  @ </pre>
  style_finish_page("errorlog");
}







|



















|








|
















|

672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
    @ </pre></blockquote>
    @ <li><p>
    @ If the server is running using one of 
    @ the "fossil http" or "fossil server" commands then add
    @ a command-line option "--errorlog <i>FILENAME</i>" to that
    @ command.
    @ </ol>
    style_finish_page();
    return;
  }
  if( P("truncate1") && cgi_csrf_safe(1) ){
    fclose(fopen(g.zErrlog,"w"));
  }
  if( P("download") ){
    Blob log;
    blob_read_from_file(&log, g.zErrlog, ExtFILE);
    cgi_set_content_type("text/plain");
    cgi_set_content(&log);
    return;
  }
  szFile = file_size(g.zErrlog, ExtFILE);
  if( P("truncate") ){
    @ <form action="%R/errorlog" method="POST">
    @ <p>Confirm that you want to truncate the %,lld(szFile)-byte error log:
    @ <input type="submit" name="truncate1" value="Confirm">
    @ <input type="submit" name="cancel" value="Cancel">
    @ </form>
    style_finish_page();
    return;
  }
  @ <p>The server error log at "%h(g.zErrlog)" is %,lld(szFile) bytes in size.
  style_submenu_element("Download", "%R/errorlog?download");
  style_submenu_element("Truncate", "%R/errorlog?truncate");
  in = fossil_fopen(g.zErrlog, "rb");
  if( in==0 ){
    @ <p class='generalError'>Unable to open that file for reading!</p>
    style_finish_page();
    return;
  }
  if( szFile>MXSHOWLOG && P("all")==0 ){
    @ <form action="%R/errorlog" method="POST">
    @ <p>Only the last %,d(MXSHOWLOG) bytes are shown.
    @ <input type="submit" name="all" value="Show All">
    @ </form>
    fseek(in, -MXSHOWLOG, SEEK_END);
  }
  @ <hr>
  @ <pre>
  while( fgets(z, sizeof(z), in) ){
    @ %h(z)\
  }
  fclose(in);
  @ </pre>
  style_finish_page();
}

Changes to src/setup.c.

71
72
73
74
75
76
77

78
79
80
81
82
83
84
  int setup_user = 0;
  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
  }
  setup_user = g.perm.Setup;


  style_header("Server Administration");

  /* Make sure the header contains <base href="...">.   Issue a warning
  ** if it does not. */
  if( !cgi_header_contains("<base href=") ){
    @ <p class="generalError"><b>Configuration Error:</b> Please add
    @ <tt>&lt;base href="$secureurl/$current_page"&gt;</tt> after







>







71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
  int setup_user = 0;
  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
  }
  setup_user = g.perm.Setup;

  style_set_current_feature("setup");
  style_header("Server Administration");

  /* Make sure the header contains <base href="...">.   Issue a warning
  ** if it does not. */
  if( !cgi_header_contains("<base href=") ){
    @ <p class="generalError"><b>Configuration Error:</b> Please add
    @ <tt>&lt;base href="$secureurl/$current_page"&gt;</tt> after
121
122
123
124
125
126
127


128
129
130
131
132
133
134
    setup_menu_entry("Login-Group", "setup_login_group",
      "Manage single sign-on between this repository and others"
      " on the same server");
    setup_menu_entry("Tickets", "tktsetup",
      "Configure the trouble-ticketing system for this repository");
    setup_menu_entry("Wiki", "setup_wiki",
      "Configure the wiki for this repository");


  }
  setup_menu_entry("Search","srchsetup",
    "Configure the built-in search engine");
  setup_menu_entry("URL Aliases", "waliassetup",
    "Configure URL aliases");
  if( setup_user ){
    setup_menu_entry("Notification", "setup_notification",







>
>







122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
    setup_menu_entry("Login-Group", "setup_login_group",
      "Manage single sign-on between this repository and others"
      " on the same server");
    setup_menu_entry("Tickets", "tktsetup",
      "Configure the trouble-ticketing system for this repository");
    setup_menu_entry("Wiki", "setup_wiki",
      "Configure the wiki for this repository");
    setup_menu_entry("Chat", "setup_chat",
      "Configure the chatroom");
  }
  setup_menu_entry("Search","srchsetup",
    "Configure the built-in search engine");
  setup_menu_entry("URL Aliases", "waliassetup",
    "Configure URL aliases");
  if( setup_user ){
    setup_menu_entry("Notification", "setup_notification",
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
    setup_menu_entry("SQL", "admin_sql",
      "Enter raw SQL commands");
    setup_menu_entry("TH1", "admin_th1",
      "Enter raw TH1 commands");
  }
  @ </table>

  style_finish_page("setup");
}

/*
** Generate a checkbox for an attribute.
*/
void onoff_attribute(
  const char *zLabel,   /* The text label on the checkbox */







|







176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
    setup_menu_entry("SQL", "admin_sql",
      "Enter raw SQL commands");
    setup_menu_entry("TH1", "admin_th1",
      "Enter raw TH1 commands");
  }
  @ </table>

  style_finish_page();
}

/*
** Generate a checkbox for an attribute.
*/
void onoff_attribute(
  const char *zLabel,   /* The text label on the checkbox */
337
338
339
340
341
342
343

344
345
346
347
348
349
350
  };
  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }


  style_header("Access Control Settings");
  db_begin_transaction();
  @ <form action="%R/setup_access" method="post"><div>
  login_insert_csrf_secret();
  @ <input type="submit"  name="submit" value="Apply Changes" /></p>
  @ <hr />
  multiple_choice_attribute("Redirect to HTTPS",







>







340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
  };
  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }

  style_set_current_feature("setup");
  style_header("Access Control Settings");
  db_begin_transaction();
  @ <form action="%R/setup_access" method="post"><div>
  login_insert_csrf_secret();
  @ <input type="submit"  name="submit" value="Apply Changes" /></p>
  @ <hr />
  multiple_choice_attribute("Redirect to HTTPS",
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
  @ probably secure enough and it is certainly more convenient for
  @ anonymous users.  (Property: "auto-captcha")</p>

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

/*
** WEBPAGE: setup_login_group
**
** Change how the current repository participates in a login
** group.







|







578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
  @ probably secure enough and it is certainly more convenient for
  @ anonymous users.  (Property: "auto-captcha")</p>

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

/*
** WEBPAGE: setup_login_group
**
** Change how the current repository participates in a login
** group.
606
607
608
609
610
611
612

613
614
615
616
617
618
619
  zSelfRepo = fossil_strdup(blob_str(&fullName));
  blob_reset(&fullName);
  if( P("join")!=0 ){
    login_group_join(zRepo, 1, zLogin, zPw, zNewName, &zErrMsg);
  }else if( P("leave") ){
    login_group_leave(&zErrMsg);
  }

  style_header("Login Group Configuration");
  if( zErrMsg ){
    @ <p class="generalError">%s(zErrMsg)</p>
  }
  zGroup = login_group_name();
  if( zGroup==0 ){
    @ <p>This repository (in the file named "%h(zSelfRepo)")







>







610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
  zSelfRepo = fossil_strdup(blob_str(&fullName));
  blob_reset(&fullName);
  if( P("join")!=0 ){
    login_group_join(zRepo, 1, zLogin, zPw, zNewName, &zErrMsg);
  }else if( P("leave") ){
    login_group_leave(&zErrMsg);
  }
  style_set_current_feature("setup");
  style_header("Login Group Configuration");
  if( zErrMsg ){
    @ <p class="generalError">%s(zErrMsg)</p>
  }
  zGroup = login_group_name();
  if( zGroup==0 ){
    @ <p>This repository (in the file named "%h(zSelfRepo)")
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
      @ <td>%h(db_column_text(&q,1))</td>
      @ <td>%h(db_column_text(&q,2))</td></tr>
    }
    db_finalize(&q);
    @ </tbody></table>
    style_table_sorter();
  }
  style_finish_page("setup");
}

/*
** WEBPAGE: setup_timeline
**
** Edit administrative settings controlling the display of
** timelines.







|







707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
      @ <td>%h(db_column_text(&q,1))</td>
      @ <td>%h(db_column_text(&q,2))</td></tr>
    }
    db_finalize(&q);
    @ </tbody></table>
    style_table_sorter();
  }
  style_finish_page();
}

/*
** WEBPAGE: setup_timeline
**
** Edit administrative settings controlling the display of
** timelines.
727
728
729
730
731
732
733

734
735
736
737
738
739
740
  };
  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }


  style_header("Timeline Display Preferences");
  db_begin_transaction();
  @ <form action="%R/setup_timeline" method="post"><div>
  login_insert_csrf_secret();
  @ <p><input type="submit"  name="submit" value="Apply Changes" /></p>

  @ <hr />







>







732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
  };
  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }

  style_set_current_feature("setup");
  style_header("Timeline Display Preferences");
  db_begin_transaction();
  @ <form action="%R/setup_timeline" method="post"><div>
  login_insert_csrf_secret();
  @ <p><input type="submit"  name="submit" value="Apply Changes" /></p>

  @ <hr />
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868

869
870
871
872
873
874
875
  @ right of the entry.
  @ <p>(Properties: "timeline-tslink-info")

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

/*
** WEBPAGE: setup_settings
**
** Change or view miscellaneous settings.  Part of the
** /setup pages requiring Setup privileges.
*/
void setup_settings(void){
  int nSetting;
  int i;
  Setting const *pSet;
  const Setting *aSetting = setting_info(&nSetting);

  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }


  style_header("Settings");
  if(!g.repositoryOpen){
    /* Provide read-only access to versioned settings,
       but only if no repo file was explicitly provided. */
    db_open_local(0);
  }
  db_begin_transaction();







|




















>







847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
  @ right of the entry.
  @ <p>(Properties: "timeline-tslink-info")

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

/*
** WEBPAGE: setup_settings
**
** Change or view miscellaneous settings.  Part of the
** /setup pages requiring Setup privileges.
*/
void setup_settings(void){
  int nSetting;
  int i;
  Setting const *pSet;
  const Setting *aSetting = setting_info(&nSetting);

  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }

  style_set_current_feature("setup");
  style_header("Settings");
  if(!g.repositoryOpen){
    /* Provide read-only access to versioned settings,
       but only if no repo file was explicitly provided. */
    db_open_local(0);
  }
  db_begin_transaction();
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
                      (char*)pSet->def, hasVersionableValue);
      @<br />
    }
  }
  @ </td></tr></table>
  @ </div></form>
  db_end_transaction(0);
  style_finish_page("setup");
}

/*
** WEBPAGE: setup_config
**
** The "Admin/Configuration" page.  Requires Setup privilege.
*/
void setup_config(void){
  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }


  style_header("WWW Configuration");
  db_begin_transaction();
  @ <form action="%R/setup_config" method="post"><div>
  login_insert_csrf_secret();
  @ <input type="submit"  name="submit" value="Apply Changes" /></p>
  @ <hr />
  entry_attribute("Project Name", 60, "project-name", "pn", "", 0);







|














>







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
                      (char*)pSet->def, hasVersionableValue);
      @<br />
    }
  }
  @ </td></tr></table>
  @ </div></form>
  db_end_transaction(0);
  style_finish_page();
}

/*
** WEBPAGE: setup_config
**
** The "Admin/Configuration" page.  Requires Setup privilege.
*/
void setup_config(void){
  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }

  style_set_current_feature("setup");
  style_header("WWW Configuration");
  db_begin_transaction();
  @ <form action="%R/setup_config" method="post"><div>
  login_insert_csrf_secret();
  @ <input type="submit"  name="submit" value="Apply Changes" /></p>
  @ <hr />
  entry_attribute("Project Name", 60, "project-name", "pn", "", 0);
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
  @ <p>And you have specified an index page of "/home" the above will
  @ automatically redirect to:</p>
  @
  @ <blockquote><p>%h(g.zBaseURL)/home</p></blockquote>
  @
  @ <p>The default "/home" page displays a Wiki page with the same name
  @ as the Project Name specified above.  Some sites prefer to redirect
  @ to a documentation page (ex: "/doc/tip/index.wiki") or to "/timeline".</p>
  @
  @ <p>Note:  To avoid a redirect loop or other problems, this entry must
  @ begin with "/" and it must specify a valid page.  For example,
  @ "<b>/home</b>" will work but "<b>home</b>" will not, since it omits the
  @ leading "/".</p>
  @ <p>(Property: "index-page")
  @ <hr>







|







1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
  @ <p>And you have specified an index page of "/home" the above will
  @ automatically redirect to:</p>
  @
  @ <blockquote><p>%h(g.zBaseURL)/home</p></blockquote>
  @
  @ <p>The default "/home" page displays a Wiki page with the same name
  @ as the Project Name specified above.  Some sites prefer to redirect
  @ to a documentation page (ex: "/doc/trunk/index.wiki") or to "/timeline".</p>
  @
  @ <p>Note:  To avoid a redirect loop or other problems, this entry must
  @ begin with "/" and it must specify a valid page.  For example,
  @ "<b>/home</b>" will work but "<b>home</b>" will not, since it omits the
  @ leading "/".</p>
  @ <p>(Property: "index-page")
  @ <hr>
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
  entry_attribute("Contact", 40, "sitemap-contact", "smcontact",
                  "", 0);
  @ (Property: sitemap-contact)
  @ <hr />
  @ <p><input type="submit"  name="submit" value="Apply Changes" /></p>
  @ </div></form>
  db_end_transaction(0);
  style_finish_page("setup");
}

/*
** WEBPAGE: setup_wiki
**
** The "Admin/Wiki" page.  Requires Setup privilege.
*/
void setup_wiki(void){
  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }


  style_header("Wiki Configuration");
  db_begin_transaction();
  @ <form action="%R/setup_wiki" method="post"><div>
  login_insert_csrf_secret();
  @ <input type="submit"  name="submit" value="Apply Changes" /></p>
  @ <hr />
  onoff_attribute("Associate Wiki Pages With Branches, Tags, or Checkins",







|














>







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
  entry_attribute("Contact", 40, "sitemap-contact", "smcontact",
                  "", 0);
  @ (Property: sitemap-contact)
  @ <hr />
  @ <p><input type="submit"  name="submit" value="Apply Changes" /></p>
  @ </div></form>
  db_end_transaction(0);
  style_finish_page();
}

/*
** WEBPAGE: setup_wiki
**
** The "Admin/Wiki" page.  Requires Setup privilege.
*/
void setup_wiki(void){
  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }

  style_set_current_feature("setup");
  style_header("Wiki Configuration");
  db_begin_transaction();
  @ <form action="%R/setup_wiki" method="post"><div>
  login_insert_csrf_secret();
  @ <input type="submit"  name="submit" value="Apply Changes" /></p>
  @ <hr />
  onoff_attribute("Associate Wiki Pages With Branches, Tags, or Checkins",
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
  @ to trusted users. It should <strong>not</strong> be used on a publicly
  @ editable wiki.</p>
  @ (Property: "wiki-use-html")
  @ <hr />
  @ <p><input type="submit"  name="submit" value="Apply Changes" /></p>
  @ </div></form>
  db_end_transaction(0);
  style_finish_page("setup");



















































































}

/*
** WEBPAGE: setup_modreq
**
** Admin page for setting up moderation of tickets and wiki.
*/
void setup_modreq(void){
  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }


  style_header("Moderator For Wiki And Tickets");
  db_begin_transaction();
  @ <form action="%R/setup_modreq" method="post"><div>
  login_insert_csrf_secret();
  @ <hr />
  onoff_attribute("Moderate ticket changes",
     "modreq-tkt", "modreq-tkt", 0, 0);







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














>







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
  @ to trusted users. It should <strong>not</strong> be used on a publicly
  @ editable wiki.</p>
  @ (Property: "wiki-use-html")
  @ <hr />
  @ <p><input type="submit"  name="submit" value="Apply Changes" /></p>
  @ </div></form>
  db_end_transaction(0);
  style_finish_page();
}

/*
** WEBPAGE: setup_chat
**
** The "Admin/Chat" page.  Requires Setup privilege.
*/
void setup_chat(void){
  static const char *const azAlerts[] = {
    "alerts/plunk.wav",  "Plunk",
    "alerts/bflat3.wav", "Tone-1",
    "alerts/bflat2.wav", "Tone-2",
    "alerts/bloop.wav",  "Bloop",
  };

  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }

  style_set_current_feature("setup");
  style_header("Chat Configuration");
  db_begin_transaction();
  @ <form action="%R/setup_chat" method="post"><div>
  login_insert_csrf_secret();
  @ <input type="submit"  name="submit" value="Apply Changes" /></p>
  @ <hr />
  entry_attribute("Initial Chat History Size", 10,
                  "chat-initial-history", "chatih", "50", 0);
  @ <p>When /chat first starts up, it preloads up to this many historical
  @ messages.
  @ (Property: "chat-initial-history")</p>
  @ <hr />
  entry_attribute("Minimum Number Of Historical Messages To Retain", 10,
                  "chat-keep-count", "chatkc", "50", 0);
  @ <p>The chat subsystem purges older messages.  But it will always retain
  @ the N most recent messages where N is the value of this setting.
  @ (Property: "chat-keep-count")</p>
  @ <hr />
  entry_attribute("Maximum Message Age In Days", 10,
                  "chat-keep-days", "chatkd", "7", 0);
  @ <p>Chat message are removed after N days, where N is the value of
  @ this setting.  N may be fractional.  So, for example, to only keep
  @ an historical record of chat messages for 12 hours, set this value
  @ to 0.5.
  @ (Property: "chat-keep-days")</p>
  @ <hr />
  entry_attribute("Chat Polling Timeout", 10,
                  "chat-poll-timeout", "chatpt", "420", 0);
  @ <p>New chat content is downloaded using the "long poll" technique.
  @ HTTP requests are made to /chat-poll which blocks waiting on new
  @ content to arrive.  But the /chat-poll cannot block forever.  It
  @ eventual must give up and return an empty message set.  This setting
  @ determines how long /chat-poll will wait before giving up.  The
  @ default setting of approximately 7 minutes works well on many systems.
  @ Shorter delays might be required on installations that use proxies
  @ or web-servers with short timeouts.  For best efficiency, this value
  @ should be larger rather than smaller.
  @ (Property: "chat-poll-timeout")</p>
  @ <hr />

  multiple_choice_attribute("Alert sound",
     "chat-alert-sound", "snd", azAlerts[0],
     count(azAlerts)/2, azAlerts);
  @ <p>The sound used in the client-side chat to indicate that a new
  @ chat message has arrived.
  @ (Property: "chat-alert-sound")</p>
  @ <hr/>
  @ <p><input type="submit"  name="submit" value="Apply Changes" /></p>
  @ </div></form>
  db_end_transaction(0);
  @ <script nonce="%h(style_nonce())">
  @ (function(){
  @   var w = document.getElementById('idsnd');
  @   w.onchange = function(){
  @     var audio = new Audio('%s(g.zBaseURL)/builtin/' + w.value);
  @     audio.currentTime = 0;
  @     audio.play();
  @   }
  @ })();
  @ </script>
  style_finish_page();
}

/*
** WEBPAGE: setup_modreq
**
** Admin page for setting up moderation of tickets and wiki.
*/
void setup_modreq(void){
  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }

  style_set_current_feature("setup");
  style_header("Moderator For Wiki And Tickets");
  db_begin_transaction();
  @ <form action="%R/setup_modreq" method="post"><div>
  login_insert_csrf_secret();
  @ <hr />
  onoff_attribute("Moderate ticket changes",
     "modreq-tkt", "modreq-tkt", 0, 0);
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
  @ moderation. (Property: "modreq-wiki")
  @ </p>

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

}

/*
** WEBPAGE: setup_adunit
**
** Administrative page for configuring and controlling ad units







|







1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
  @ moderation. (Property: "modreq-wiki")
  @ </p>

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

}

/*
** WEBPAGE: setup_adunit
**
** Administrative page for configuring and controlling ad units
1178
1179
1180
1181
1182
1183
1184

1185
1186
1187
1188
1189
1190
1191
    db_multi_exec("DELETE FROM config WHERE name GLOB 'adunit*'");
    db_protect_pop();
    cgi_replace_parameter("adunit","");
    cgi_replace_parameter("adright","");
    setup_incr_cfgcnt();
  }


  style_header("Edit Ad Unit");
  @ <form action="%R/setup_adunit" method="post"><div>
  login_insert_csrf_secret();
  @ <b>Banner Ad-Unit:</b><br />
 textarea_attribute("", 6, 80, "adunit", "adunit", "", 0);
  @ <br />
  @ <b>Right-Column Ad-Unit:</b><br />







>







1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
    db_multi_exec("DELETE FROM config WHERE name GLOB 'adunit*'");
    db_protect_pop();
    cgi_replace_parameter("adunit","");
    cgi_replace_parameter("adright","");
    setup_incr_cfgcnt();
  }

  style_set_current_feature("setup");
  style_header("Edit Ad Unit");
  @ <form action="%R/setup_adunit" method="post"><div>
  login_insert_csrf_secret();
  @ <b>Banner Ad-Unit:</b><br />
 textarea_attribute("", 6, 80, "adunit", "adunit", "", 0);
  @ <br />
  @ <b>Right-Column Ad-Unit:</b><br />
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
  @   width: 600px;
  @   height: 90px;
  @   border: 1px solid #f11;
  @   background-color: #fcc;
  @ '&gt;Demo Ad&lt;/div&gt;
  @ </pre></blockquote>
  @ </li>
  style_finish_page("setup");
  db_end_transaction(0);
}

/*
** WEBPAGE: setup_logo
**
** Administrative page for changing the logo, background, and icon images.







|







1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
  @   width: 600px;
  @   height: 90px;
  @   border: 1px solid #f11;
  @   background-color: #fcc;
  @ '&gt;Demo Ad&lt;/div&gt;
  @ </pre></blockquote>
  @ </li>
  style_finish_page();
  db_end_transaction(0);
}

/*
** WEBPAGE: setup_logo
**
** Administrative page for changing the logo, background, and icon images.
1359
1360
1361
1362
1363
1364
1365

1366
1367
1368
1369
1370
1371
1372
       "DELETE FROM config WHERE name IN "
           "('icon-image','icon-mimetype')"
    );
    db_protect_pop();
    db_end_transaction(0);
    cgi_redirect("setup_logo");
  }

  style_header("Edit Project Logo And Background");
  @ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b>
  @ and looks like this:</p>
  @ <blockquote><p><img src="%R/logo/%z(zLogoMtime)" \
  @ alt="logo" border="1" />
  @ </p></blockquote>
  @







>







1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
       "DELETE FROM config WHERE name IN "
           "('icon-image','icon-mimetype')"
    );
    db_protect_pop();
    db_end_transaction(0);
    cgi_redirect("setup_logo");
  }
  style_set_current_feature("setup");
  style_header("Edit Project Logo And Background");
  @ <p>The current project logo has a MIME-Type of <b>%h(zLogoMime)</b>
  @ and looks like this:</p>
  @ <blockquote><p><img src="%R/logo/%z(zLogoMtime)" \
  @ alt="logo" border="1" />
  @ </p></blockquote>
  @
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
  @ </div></form>
  @ <p>(Properties: "icon-image" and "icon-mimetype")
  @ <hr />
  @
  @ <p><span class="note">Note:</span>  Your browser has probably cached these
  @ images, so you may need to press the Reload button before changes will
  @ take effect. </p>
  style_finish_page("setup");
  db_end_transaction(0);
}

/*
** Prevent the RAW SQL feature from being used to ATTACH a different
** database and query it.
**







|







1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
  @ </div></form>
  @ <p>(Properties: "icon-image" and "icon-mimetype")
  @ <hr />
  @
  @ <p><span class="note">Note:</span>  Your browser has probably cached these
  @ images, so you may need to press the Reload button before changes will
  @ take effect. </p>
  style_finish_page();
  db_end_transaction(0);
}

/*
** Prevent the RAW SQL feature from being used to ATTACH a different
** database and query it.
**
1479
1480
1481
1482
1483
1484
1485

1486
1487
1488
1489
1490
1491
1492
  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }
  add_content_sql_commands(g.db);
  zQ = cgi_csrf_safe(1) ? P("q") : 0;

  style_header("Raw SQL Commands");
  @ <p><b>Caution:</b> There are no restrictions on the SQL that can be
  @ run by this page.  You can do serious and irrepairable damage to the
  @ repository.  Proceed with extreme caution.</p>
  @
#if 0
  @ <p>Only the first statement in the entry box will be run.







>







1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }
  add_content_sql_commands(g.db);
  zQ = cgi_csrf_safe(1) ? P("q") : 0;
  style_set_current_feature("setup");
  style_header("Raw SQL Commands");
  @ <p><b>Caution:</b> There are no restrictions on the SQL that can be
  @ run by this page.  You can do serious and irrepairable damage to the
  @ repository.  Proceed with extreme caution.</p>
  @
#if 0
  @ <p>Only the first statement in the entry box will be run.
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
        }
        @ </tr>
      }
      sqlite3_finalize(pStmt);
      @ </table>
    }
  }
  style_finish_page("setup");
}


/*
** WEBPAGE: admin_th1
**
** Run raw TH1 commands using the web interface.  If Tcl integration was
** enabled at compile-time and the "tcl" setting is enabled, Tcl commands
** may be run as well.  Requires Admin privilege.
*/
void th1_page(void){
  const char *zQ = P("q");
  int go = P("go")!=0;
  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }

  style_header("Raw TH1 Commands");
  @ <p><b>Caution:</b> There are no restrictions on the TH1 that can be
  @ run by this page.  If Tcl integration was enabled at compile-time and
  @ the "tcl" setting is enabled, Tcl commands may be run as well.</p>
  @
  @ <form method="post" action="%R/admin_th1">
  login_insert_csrf_secret();







|


















>







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
        }
        @ </tr>
      }
      sqlite3_finalize(pStmt);
      @ </table>
    }
  }
  style_finish_page();
}


/*
** WEBPAGE: admin_th1
**
** Run raw TH1 commands using the web interface.  If Tcl integration was
** enabled at compile-time and the "tcl" setting is enabled, Tcl commands
** may be run as well.  Requires Admin privilege.
*/
void th1_page(void){
  const char *zQ = P("q");
  int go = P("go")!=0;
  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }
  style_set_current_feature("setup");
  style_header("Raw TH1 Commands");
  @ <p><b>Caution:</b> There are no restrictions on the TH1 that can be
  @ run by this page.  If Tcl integration was enabled at compile-time and
  @ the "tcl" setting is enabled, Tcl commands may be run as well.</p>
  @
  @ <form method="post" action="%R/admin_th1">
  login_insert_csrf_secret();
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
    zR = Th_GetResult(g.interp, &n);
    if( rc==TH_OK ){
      @ <pre class="th1result">%h(zR)</pre>
    }else{
      @ <pre class="th1error">%h(zR)</pre>
    }
  }
  style_finish_page("setup");
}

/*
** WEBPAGE: admin_log
**
** Shows the contents of the admin_log table, which is only created if
** the admin-log setting is enabled. Requires Admin or Setup ('a' or
** 's') permissions.
*/
void page_admin_log(){
  Stmt stLog;
  int limit;                 /* How many entries to show */
  int ofst;                  /* Offset to the first entry */
  int fLogEnabled;
  int counter = 0;
  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }

  style_header("Admin Log");
  create_admin_log_table();
  limit = atoi(PD("n","200"));
  ofst = atoi(PD("x","0"));
  fLogEnabled = db_get_boolean("admin-log", 0);
  @ <div>Admin logging is %s(fLogEnabled?"on":"off").
  @ (Change this on the <a href="setup_settings">settings</a> page.)</div>







|




















>







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
    zR = Th_GetResult(g.interp, &n);
    if( rc==TH_OK ){
      @ <pre class="th1result">%h(zR)</pre>
    }else{
      @ <pre class="th1error">%h(zR)</pre>
    }
  }
  style_finish_page();
}

/*
** WEBPAGE: admin_log
**
** Shows the contents of the admin_log table, which is only created if
** the admin-log setting is enabled. Requires Admin or Setup ('a' or
** 's') permissions.
*/
void page_admin_log(){
  Stmt stLog;
  int limit;                 /* How many entries to show */
  int ofst;                  /* Offset to the first entry */
  int fLogEnabled;
  int counter = 0;
  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }
  style_set_current_feature("setup");
  style_header("Admin Log");
  create_admin_log_table();
  limit = atoi(PD("n","200"));
  ofst = atoi(PD("x","0"));
  fLogEnabled = db_get_boolean("admin-log", 0);
  @ <div>Admin logging is %s(fLogEnabled?"on":"off").
  @ (Change this on the <a href="setup_settings">settings</a> page.)</div>
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
    @ </tr>
  }
  db_finalize(&stLog);
  @ </tbody></table>
  if( counter>ofst+limit ){
    @ <p><a href="admin_log?n=%d(limit)&x=%d(limit+ofst)">[Older]</a></p>
  }
  style_finish_page("setup");
}

/*
** WEBPAGE: srchsetup
**
** Configure the search engine.  Requires Admin privilege.
*/
void page_srchsetup(){
  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }

  style_header("Search Configuration");
  @ <form action="%R/srchsetup" method="post"><div>
  login_insert_csrf_secret();
  @ <div style="text-align:center;font-weight:bold;">
  @ Server-specific settings that affect the
  @ <a href="%R/search">/search</a> webpage.
  @ </div>







|













>







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
    @ </tr>
  }
  db_finalize(&stLog);
  @ </tbody></table>
  if( counter>ofst+limit ){
    @ <p><a href="admin_log?n=%d(limit)&x=%d(limit+ofst)">[Older]</a></p>
  }
  style_finish_page();
}

/*
** WEBPAGE: srchsetup
**
** Configure the search engine.  Requires Admin privilege.
*/
void page_srchsetup(){
  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }
  style_set_current_feature("setup");
  style_header("Search Configuration");
  @ <form action="%R/srchsetup" method="post"><div>
  login_insert_csrf_secret();
  @ <div style="text-align:center;font-weight:bold;">
  @ Server-specific settings that affect the
  @ <a href="%R/search">/search</a> webpage.
  @ </div>
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
    @ <p>The SQLite FTS4 search index is disabled.  All searching will be
    @ a full-text scan.  This usually works fine, but can be slow for
    @ larger repositories.</p>
    onoff_attribute("Use Porter Stemmer","search-stemmer","ss",0,0);
    @ <p><input type="submit" name="fts1" value="Create A Full-Text Index">
  }
  @ </div></form>
  style_finish_page("setup");
}

/*
** A URL Alias originally called zOldName is now zNewName/zValue.
** Write SQL to make this change into pSql.
**
** If zNewName or zValue is an empty string, then delete the entry.







|







1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
    @ <p>The SQLite FTS4 search index is disabled.  All searching will be
    @ a full-text scan.  This usually works fine, but can be slow for
    @ larger repositories.</p>
    onoff_attribute("Use Porter Stemmer","search-stemmer","ss",0,0);
    @ <p><input type="submit" name="fts1" value="Create A Full-Text Index">
  }
  @ </div></form>
  style_finish_page();
}

/*
** A URL Alias originally called zOldName is now zNewName/zValue.
** Write SQL to make this change into pSql.
**
** If zNewName or zValue is an empty string, then delete the entry.
1847
1848
1849
1850
1851
1852
1853

1854
1855
1856
1857
1858
1859
1860
  int cnt = 0;
  Blob namelist;
  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }

  style_header("URL Alias Configuration");
  if( P("submit")!=0 ){
    Blob token;
    Blob sql;
    const char *zNewName;
    const char *zValue;
    char zCnt[10];







>







1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
  int cnt = 0;
  Blob namelist;
  login_check_credentials();
  if( !g.perm.Admin ){
    login_needed(0);
    return;
  }
  style_set_current_feature("setup");
  style_header("URL Alias Configuration");
  if( P("submit")!=0 ){
    Blob token;
    Blob sql;
    const char *zNewName;
    const char *zValue;
    char zCnt[10];
1946
1947
1948
1949
1950
1951
1952
1953
1954
  @ </ul>
  @
  @ <p>To delete an entry from the alias table, change its name or value to an
  @ empty string and press "Apply Changes".
  @
  @ <p>To add a new alias, fill in the name and value in the bottom row
  @ of the table above and press "Apply Changes".
  style_finish_page("setup");
}







|

2046
2047
2048
2049
2050
2051
2052
2053
2054
  @ </ul>
  @
  @ <p>To delete an entry from the alias table, change its name or value to an
  @ empty string and press "Apply Changes".
  @
  @ <p>To add a new alias, fill in the name and value in the bottom row
  @ of the table above and press "Apply Changes".
  style_finish_page();
}

Changes to src/setupuser.c.

14
15
16
17
18
19
20



21
22
23
24
25
26
27
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** Setup pages associated with user management.  The code in this
** file was formerly part of the "setup.c" module, but has been broken
** out into its own module to improve maintainability.



*/
#include "config.h"
#include <assert.h>
#include "setupuser.h"

/*
** WEBPAGE: setup_ulist







>
>
>







14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
**   http://www.hwaci.com/drh/
**
*******************************************************************************
**
** Setup pages associated with user management.  The code in this
** file was formerly part of the "setup.c" module, but has been broken
** out into its own module to improve maintainability.
**
** Note:  Do not confuse "Users" with "Subscribers".  Code to deal with
** subscribers is over in the "alerts.c" source file.
*/
#include "config.h"
#include <assert.h>
#include "setupuser.h"

/*
** WEBPAGE: setup_ulist
47
48
49
50
51
52
53

54
55
56
57
58
59
60

  style_submenu_element("Add", "setup_uedit");
  style_submenu_element("Log", "access_log");
  style_submenu_element("Help", "setup_ulist_notes");
  if( alert_tables_exist() ){
    style_submenu_element("Subscribers", "subscribers");
  }

  style_header("User List");
  if( (zWith==0 || zWith[0]==0) && !bUnusedOnly ){
    @ <table border=1 cellpadding=2 cellspacing=0 class='userTable'>
    @ <thead><tr>
    @   <th>Category
    @   <th>Capabilities (<a href='%R/setup_ucap_list'>key</a>)
    @   <th>Info <th>Last Change</tr></thead>







>







50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

  style_submenu_element("Add", "setup_uedit");
  style_submenu_element("Log", "access_log");
  style_submenu_element("Help", "setup_ulist_notes");
  if( alert_tables_exist() ){
    style_submenu_element("Subscribers", "subscribers");
  }
  style_set_current_feature("setup");
  style_header("User List");
  if( (zWith==0 || zWith[0]==0) && !bUnusedOnly ){
    @ <table border=1 cellpadding=2 cellspacing=0 class='userTable'>
    @ <thead><tr>
    @   <th>Category
    @   <th>Capabilities (<a href='%R/setup_ucap_list'>key</a>)
    @   <th>Info <th>Last Change</tr></thead>
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
      }
    }
  }
  if( !bUnusedOnly ){
    style_submenu_element("Unused", "setup_ulist?unused");
  }
  @ <table border=1 cellpadding=2 cellspacing=0 class='userTable sortable' \
  @  data-column-types='ktxTTK' data-init-sort='2'>
  @ <thead><tr>
  @ <th>Login Name<th>Caps<th>Info<th>Date<th>Expire<th>Last Login</tr></thead>

  @ <tbody>
  db_multi_exec(
    "CREATE TEMP TABLE lastAccess(uname TEXT PRIMARY KEY, atime REAL)"
    "WITHOUT ROWID;"
  );
  if( db_table_exists("repository","accesslog") ){
    db_multi_exec(
      "INSERT INTO lastAccess(uname, atime)"
      " SELECT uname, max(mtime) FROM ("
      "    SELECT uname, mtime FROM accesslog WHERE success"
      "    UNION ALL"
      "    SELECT login AS uname, rcvfrom.mtime AS mtime"
      "      FROM rcvfrom JOIN user USING(uid))"
      " GROUP BY 1;"
    );






  }
  if( bUnusedOnly ){
    zWith = mprintf(
        " AND login NOT IN ("
        "SELECT user FROM event WHERE user NOT NULL "
        "UNION ALL SELECT euser FROM event WHERE euser NOT NULL%s)"
        " AND uid NOT IN (SELECT uid FROM rcvfrom)",
        alert_tables_exist() ?
          " UNION ALL SELECT suname FROM subscriber WHERE suname NOT NULL":"");
  }else if( zWith && zWith[0] ){
    zWith = mprintf(" AND fullcap(cap) GLOB '*[%q]*'", zWith);
  }else{
    zWith = "";
  }
  db_prepare(&s,
     "SELECT uid, login, cap, info, date(mtime,'unixepoch'),"
     "       lower(login) AS sortkey, "
     "       CASE WHEN info LIKE '%%expires 20%%'"
             "    THEN substr(info,instr(lower(info),'expires')+8,10)"
             "    END AS exp,"
             "atime"

     "  FROM user LEFT JOIN lastAccess ON login=uname"

     " WHERE login NOT IN ('anonymous','nobody','developer','reader') %s"
     " ORDER BY sortkey", zWith/*safe-for-%s*/
  );
  rNow = db_double(0.0, "SELECT julianday('now');");
  while( db_step(&s)==SQLITE_ROW ){
    int uid = db_column_int(&s, 0);
    const char *zLogin = db_column_text(&s, 1);
    const char *zCap = db_column_text(&s, 2);
    const char *zInfo = db_column_text(&s, 3);
    const char *zDate = db_column_text(&s, 4);
    const char *zSortKey = db_column_text(&s,5);
    const char *zExp = db_column_text(&s,6);
    double rATime = db_column_double(&s,7);
    char *zAge = 0;


    if( rATime>0.0 ){
      zAge = human_readable_age(rNow - rATime);
    }
    @ <tr>
    @ <td data-sortkey='%h(zSortKey)'>\
    @ <a href='setup_uedit?id=%d(uid)'>%h(zLogin)</a>
    @ <td>%h(zCap)
    @ <td>%h(zInfo)
    @ <td>%h(zDate?zDate:"")
    @ <td>%h(zExp?zExp:"")
    @ <td data-sortkey='%f(rATime)' style='white-space:nowrap'>%s(zAge?zAge:"")








    @ </tr>
    fossil_free(zAge);
  }
  @ </tbody></table>
  db_finalize(&s);
  style_table_sorter();
  style_finish_page("setupuser");
}

/*
** WEBPAGE: setup_ulist_notes
**
** A documentation page showing notes about user configuration.  This
** information used to be a side-bar on the user list page, but has been
** factored out for improved presentation.
*/
void setup_ulist_notes(void){

  style_header("User Configuration Notes");
  @ <h1>User Configuration Notes:</h1>
  @ <ol>
  @ <li><p>
  @ Every user, logged in or not, inherits the privileges of
  @ <span class="usertype">nobody</span>.
  @ </p></li>







|

|
>















>
>
>
>
>
>















|




|
>

>














>
>











>
>
>
>
>
>
>
>






|










>







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
      }
    }
  }
  if( !bUnusedOnly ){
    style_submenu_element("Unused", "setup_ulist?unused");
  }
  @ <table border=1 cellpadding=2 cellspacing=0 class='userTable sortable' \
  @  data-column-types='ktxTTKt' data-init-sort='2'>
  @ <thead><tr>
  @ <th>Login Name<th>Caps<th>Info<th>Date<th>Expire<th>Last Login\
  @ <th>Alerts</tr></thead>
  @ <tbody>
  db_multi_exec(
    "CREATE TEMP TABLE lastAccess(uname TEXT PRIMARY KEY, atime REAL)"
    "WITHOUT ROWID;"
  );
  if( db_table_exists("repository","accesslog") ){
    db_multi_exec(
      "INSERT INTO lastAccess(uname, atime)"
      " SELECT uname, max(mtime) FROM ("
      "    SELECT uname, mtime FROM accesslog WHERE success"
      "    UNION ALL"
      "    SELECT login AS uname, rcvfrom.mtime AS mtime"
      "      FROM rcvfrom JOIN user USING(uid))"
      " GROUP BY 1;"
    );
  }
  if( !db_table_exists("repository","subscriber") ){
    db_multi_exec(
      "CREATE TEMP TABLE subscriber(suname PRIMARY KEY, ssub, subscriberId)"
      "WITHOUT ROWID;"
    );
  }
  if( bUnusedOnly ){
    zWith = mprintf(
        " AND login NOT IN ("
        "SELECT user FROM event WHERE user NOT NULL "
        "UNION ALL SELECT euser FROM event WHERE euser NOT NULL%s)"
        " AND uid NOT IN (SELECT uid FROM rcvfrom)",
        alert_tables_exist() ?
          " UNION ALL SELECT suname FROM subscriber WHERE suname NOT NULL":"");
  }else if( zWith && zWith[0] ){
    zWith = mprintf(" AND fullcap(cap) GLOB '*[%q]*'", zWith);
  }else{
    zWith = "";
  }
  db_prepare(&s,
     "SELECT uid, login, cap, info, date(user.mtime,'unixepoch'),"
     "       lower(login) AS sortkey, "
     "       CASE WHEN info LIKE '%%expires 20%%'"
             "    THEN substr(info,instr(lower(info),'expires')+8,10)"
             "    END AS exp,"
             "atime,"
     "       subscriber.ssub, subscriber.subscriberId"
     "  FROM user LEFT JOIN lastAccess ON login=uname"
     "            LEFT JOIN subscriber ON login=suname"
     " WHERE login NOT IN ('anonymous','nobody','developer','reader') %s"
     " ORDER BY sortkey", zWith/*safe-for-%s*/
  );
  rNow = db_double(0.0, "SELECT julianday('now');");
  while( db_step(&s)==SQLITE_ROW ){
    int uid = db_column_int(&s, 0);
    const char *zLogin = db_column_text(&s, 1);
    const char *zCap = db_column_text(&s, 2);
    const char *zInfo = db_column_text(&s, 3);
    const char *zDate = db_column_text(&s, 4);
    const char *zSortKey = db_column_text(&s,5);
    const char *zExp = db_column_text(&s,6);
    double rATime = db_column_double(&s,7);
    char *zAge = 0;
    const char *zSub;
    int sid = db_column_int(&s,9);
    if( rATime>0.0 ){
      zAge = human_readable_age(rNow - rATime);
    }
    @ <tr>
    @ <td data-sortkey='%h(zSortKey)'>\
    @ <a href='setup_uedit?id=%d(uid)'>%h(zLogin)</a>
    @ <td>%h(zCap)
    @ <td>%h(zInfo)
    @ <td>%h(zDate?zDate:"")
    @ <td>%h(zExp?zExp:"")
    @ <td data-sortkey='%f(rATime)' style='white-space:nowrap'>%s(zAge?zAge:"")
    if( db_column_type(&s,8)==SQLITE_NULL ){
      @ <td>
    }else if( (zSub = db_column_text(&s,8))==0 || zSub[0]==0 ){
      @ <td><a href="%R/alerts?sid=%d(sid)"><i>off</i></a>
    }else{
      @ <td><a href="%R/alerts?sid=%d(sid)">%h(zSub)</a>
    }

    @ </tr>
    fossil_free(zAge);
  }
  @ </tbody></table>
  db_finalize(&s);
  style_table_sorter();
  style_finish_page();
}

/*
** WEBPAGE: setup_ulist_notes
**
** A documentation page showing notes about user configuration.  This
** information used to be a side-bar on the user list page, but has been
** factored out for improved presentation.
*/
void setup_ulist_notes(void){
  style_set_current_feature("setup");
  style_header("User Configuration Notes");
  @ <h1>User Configuration Notes:</h1>
  @ <ol>
  @ <li><p>
  @ Every user, logged in or not, inherits the privileges of
  @ <span class="usertype">nobody</span>.
  @ </p></li>
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
  @ <span class="usertype">nobody</span>.
  @ </p></li>
  @
  @ <li><p>The permission flags are as follows:</p>
  capabilities_table(CAPCLASS_ALL);
  @ </li>
  @ </ol>
  style_finish_page("setupuser");
}

/*
** WEBPAGE: setup_ucap_list
**
** A documentation page showing the meaning of the various user capabilities
** code letters.
*/
void setup_ucap_list(void){

  style_header("User Capability Codes");
  @ <h1>All capabilities</h1>
  capabilities_table(CAPCLASS_ALL);
  @ <h1>Capabilities associated with checked-in content</h1>
  capabilities_table(CAPCLASS_CODE);
  @ <h1>Capabilities associated with data transfer and sync</h1>
  capabilities_table(CAPCLASS_DATA);
  @ <h1>Capabilities associated with the forum</h1>
  capabilities_table(CAPCLASS_FORUM);
  @ <h1>Capabilities associated with tickets</h1>
  capabilities_table(CAPCLASS_TKT);
  @ <h1>Capabilities associated with wiki</h1>
  capabilities_table(CAPCLASS_WIKI);
  @ <h1>Administrative capabilities</h1>
  capabilities_table(CAPCLASS_SUPER);
  @ <h1>Miscellaneous capabilities</h1>
  capabilities_table(CAPCLASS_OTHER);
  style_finish_page("setupuser");
}

/*
** Return true if zPw is a valid password string.  A valid
** password string is:
**
**  (1)  A zero-length string, or







|









>

















|







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
  @ <span class="usertype">nobody</span>.
  @ </p></li>
  @
  @ <li><p>The permission flags are as follows:</p>
  capabilities_table(CAPCLASS_ALL);
  @ </li>
  @ </ol>
  style_finish_page();
}

/*
** WEBPAGE: setup_ucap_list
**
** A documentation page showing the meaning of the various user capabilities
** code letters.
*/
void setup_ucap_list(void){
  style_set_current_feature("setup");
  style_header("User Capability Codes");
  @ <h1>All capabilities</h1>
  capabilities_table(CAPCLASS_ALL);
  @ <h1>Capabilities associated with checked-in content</h1>
  capabilities_table(CAPCLASS_CODE);
  @ <h1>Capabilities associated with data transfer and sync</h1>
  capabilities_table(CAPCLASS_DATA);
  @ <h1>Capabilities associated with the forum</h1>
  capabilities_table(CAPCLASS_FORUM);
  @ <h1>Capabilities associated with tickets</h1>
  capabilities_table(CAPCLASS_TKT);
  @ <h1>Capabilities associated with wiki</h1>
  capabilities_table(CAPCLASS_WIKI);
  @ <h1>Administrative capabilities</h1>
  capabilities_table(CAPCLASS_SUPER);
  @ <h1>Miscellaneous capabilities</h1>
  capabilities_table(CAPCLASS_OTHER);
  style_finish_page();
}

/*
** Return true if zPw is a valid password string.  A valid
** password string is:
**
**  (1)  A zero-length string, or
339
340
341
342
343
344
345


346
347
348
349
350
351
352
    }else{
      zDeleteVerify = mprintf(
        "User \"%s\" has %d or more artifacts in the block-chain. "
        "Delete anyhow?",
        P("login")/*safe-for-%s*/, n);
    }
  }



  /* If we have all the necessary information, write the new or
  ** modified user record.  After writing the user record, redirect
  ** to the page that displays a list of users.
  */
  if( !cgi_all("login","info","pw","apply") ){
    /* need all of the above properties to make a change.  Since one or







>
>







364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
    }else{
      zDeleteVerify = mprintf(
        "User \"%s\" has %d or more artifacts in the block-chain. "
        "Delete anyhow?",
        P("login")/*safe-for-%s*/, n);
    }
  }

  style_set_current_feature("setup");

  /* If we have all the necessary information, write the new or
  ** modified user record.  After writing the user record, redirect
  ** to the page that displays a list of users.
  */
  if( !cgi_all("login","info","pw","apply") ){
    /* need all of the above properties to make a change.  Since one or
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
    if( strlen(zLogin)==0 ){
      const char *zRef = cgi_referer("setup_ulist");
      style_header("User Creation Error");
      @ <span class="loginError">Empty login not allowed.</span>
      @
      @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)">
      @ [Bummer]</a></p>
      style_finish_page("setupuser");
      return;
    }
    if( isValidPwString(zPw) ){
      zPw = sha1_shared_secret(zPw, zLogin, 0);
    }else{
      zPw = db_text(0, "SELECT pw FROM user WHERE uid=%d", uid);
    }
    zOldLogin = db_text(0, "SELECT login FROM user WHERE uid=%d", uid);
    if( db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d",zLogin,uid) ){
      const char *zRef = cgi_referer("setup_ulist");
      style_header("User Creation Error");
      @ <span class="loginError">Login "%h(zLogin)" is already used by
      @ a different user.</span>
      @
      @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)">
      @ [Bummer]</a></p>
      style_finish_page("setupuser");
      return;
    }
    login_verify_csrf_secret();
    db_unprotect(PROTECT_USER);
    db_multi_exec(
       "REPLACE INTO user(uid,login,info,pw,cap,mtime) "
       "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now())",







|
















|







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
    if( strlen(zLogin)==0 ){
      const char *zRef = cgi_referer("setup_ulist");
      style_header("User Creation Error");
      @ <span class="loginError">Empty login not allowed.</span>
      @
      @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)">
      @ [Bummer]</a></p>
      style_finish_page();
      return;
    }
    if( isValidPwString(zPw) ){
      zPw = sha1_shared_secret(zPw, zLogin, 0);
    }else{
      zPw = db_text(0, "SELECT pw FROM user WHERE uid=%d", uid);
    }
    zOldLogin = db_text(0, "SELECT login FROM user WHERE uid=%d", uid);
    if( db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d",zLogin,uid) ){
      const char *zRef = cgi_referer("setup_ulist");
      style_header("User Creation Error");
      @ <span class="loginError">Login "%h(zLogin)" is already used by
      @ a different user.</span>
      @
      @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)">
      @ [Bummer]</a></p>
      style_finish_page();
      return;
    }
    login_verify_csrf_secret();
    db_unprotect(PROTECT_USER);
    db_multi_exec(
       "REPLACE INTO user(uid,login,info,pw,cap,mtime) "
       "VALUES(nullif(%d,0),%Q,%Q,%Q,%Q,now())",
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
        const char *zRef = cgi_referer("setup_ulist");
        style_header("User Change Error");
        admin_log( "Error updating user '%q': %s'.", zLogin, zErr );
        @ <span class="loginError">%h(zErr)</span>
        @
        @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)">
        @ [Bummer]</a></p>
        style_finish_page("setupuser");
        return;
      }
    }
    cgi_redirect(cgi_referer("setup_ulist"));
    return;
  }








|







481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
        const char *zRef = cgi_referer("setup_ulist");
        style_header("User Change Error");
        admin_log( "Error updating user '%q': %s'.", zLogin, zErr );
        @ <span class="loginError">%h(zErr)</span>
        @
        @ <p><a href="setup_uedit?id=%d(uid)&referer=%T(zRef)">
        @ [Bummer]</a></p>
        style_finish_page();
        return;
      }
    }
    cgi_redirect(cgi_referer("setup_ulist"));
    return;
  }

652
653
654
655
656
657
658


659
660
661
662
663
664
665
  @  Moderate Forum%s(B('5'))</label>
  @  <li><label><input type="checkbox" name="a6"%s(oa['6']) />
  @  Supervise Forum%s(B('6'))</label>
  @  <li><label><input type="checkbox" name="a7"%s(oa['7']) />
  @  Email Alerts%s(B('7'))</label>
  @  <li><label><input type="checkbox" name="aA"%s(oa['A']) />
  @  Send Announcements%s(B('A'))</label>


  @  <li><label><input type="checkbox" name="aD"%s(oa['D']) />
  @  Enable Debug%s(B('D'))</label>
  @ </ul></div>
  @   </td>
  @ </tr>
  @ <tr>
  @   <td class="usetupEditLabel">Selected Cap:</td>







>
>







679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
  @  Moderate Forum%s(B('5'))</label>
  @  <li><label><input type="checkbox" name="a6"%s(oa['6']) />
  @  Supervise Forum%s(B('6'))</label>
  @  <li><label><input type="checkbox" name="a7"%s(oa['7']) />
  @  Email Alerts%s(B('7'))</label>
  @  <li><label><input type="checkbox" name="aA"%s(oa['A']) />
  @  Send Announcements%s(B('A'))</label>
  @  <li><label><input type="checkbox" name="aC"%s(oa['C']) />
  @  Chatroom%s(B('C'))</label>
  @  <li><label><input type="checkbox" name="aD"%s(oa['D']) />
  @  Enable Debug%s(B('D'))</label>
  @ </ul></div>
  @   </td>
  @ </tr>
  @ <tr>
  @   <td class="usetupEditLabel">Selected Cap:</td>
673
674
675
676
677
678
679

680
681
682
683
684
685
686
687
688
    @   <td align="right" id="supw">Password:</td>
    if( zPw[0] ){
      /* Obscure the password for all users */
      @   <td><input aria-labelledby="supw" type="password" autocomplete="off" \
      @   name="pw" value="**********" /></td>
    }else{
      /* Show an empty password as an empty input field */

      @   <td><input aria-labelledby="supw" type="password" name="pw" \
      @        autocomplete="off" value="" /></td>
    }
    @ </tr>
  }
  zGroup = login_group_name();
  if( zGroup ){
    @ <tr>
    @ <td valign="top" align="right">Scope:</td>







>

|







702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
    @   <td align="right" id="supw">Password:</td>
    if( zPw[0] ){
      /* Obscure the password for all users */
      @   <td><input aria-labelledby="supw" type="password" autocomplete="off" \
      @   name="pw" value="**********" /></td>
    }else{
      /* Show an empty password as an empty input field */
      char *zRPW = fossil_random_password(12);
      @   <td><input aria-labelledby="supw" type="password" name="pw" \
      @   autocomplete="off" value="" /> Password suggestion: %z(zRPW)</td>
    }
    @ </tr>
  }
  zGroup = login_group_name();
  if( zGroup ){
    @ <tr>
    @ <td valign="top" align="right">Scope:</td>
872
873
874
875
876
877
878
879
880
  @ <span class="usertype">developer</span>
  @ user.  Similarly, the <span class="usertype">reader</span> user is a
  @ template for users who are allowed more access than
  @ <span class="usertype">anonymous</span>,
  @ but less than a <span class="usertype">developer</span>.
  @ </p></li>
  @ </ul>
  style_finish_page("setupuser");
}







|

902
903
904
905
906
907
908
909
910
  @ <span class="usertype">developer</span>
  @ user.  Similarly, the <span class="usertype">reader</span> user is a
  @ template for users who are allowed more access than
  @ <span class="usertype">anonymous</span>,
  @ but less than a <span class="usertype">developer</span>.
  @ </p></li>
  @ </ul>
  style_finish_page();
}

Changes to src/shell.c.

1420
1421
1422
1423
1424
1425
1426


1427

1428
1429
1430
1431
1432
1433
1434
** 384, or 512, to determine SHA3 hash variant that is computed.
*/
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
#include <stdarg.h>


/* typedef sqlite3_uint64 u64; */


/******************************************************************************
** The Hash Engine
*/
/*
** Macros to determine whether the machine is big or little endian,
** and whether or not that determination is run-time or compile-time.







>
>

>







1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
** 384, or 512, to determine SHA3 hash variant that is computed.
*/
/* #include "sqlite3ext.h" */
SQLITE_EXTENSION_INIT1
#include <assert.h>
#include <string.h>
#include <stdarg.h>

#ifndef SQLITE_AMALGAMATION
/* typedef sqlite3_uint64 u64; */
#endif /* SQLITE_AMALGAMATION */

/******************************************************************************
** The Hash Engine
*/
/*
** Macros to determine whether the machine is big or little endian,
** and whether or not that determination is run-time or compile-time.
2010
2011
2012
2013
2014
2015
2016

2017
2018
2019

2020
2021
2022
2023
2024
2025
2026
      sqlite3_finalize(pStmt);
      sqlite3_result_error(context, zMsg, -1);
      sqlite3_free(zMsg);
      return;
    }
    nCol = sqlite3_column_count(pStmt);
    z = sqlite3_sql(pStmt);

    n = (int)strlen(z);
    hash_step_vformat(&cx,"S%d:",n);
    SHA3Update(&cx,(unsigned char*)z,n);


    /* Compute a hash over the result of the query */
    while( SQLITE_ROW==sqlite3_step(pStmt) ){
      SHA3Update(&cx,(const unsigned char*)"R",1);
      for(i=0; i<nCol; i++){
        switch( sqlite3_column_type(pStmt,i) ){
          case SQLITE_NULL: {







>
|
|
|
>







2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
      sqlite3_finalize(pStmt);
      sqlite3_result_error(context, zMsg, -1);
      sqlite3_free(zMsg);
      return;
    }
    nCol = sqlite3_column_count(pStmt);
    z = sqlite3_sql(pStmt);
    if( z ){
      n = (int)strlen(z);
      hash_step_vformat(&cx,"S%d:",n);
      SHA3Update(&cx,(unsigned char*)z,n);
    }

    /* Compute a hash over the result of the query */
    while( SQLITE_ROW==sqlite3_step(pStmt) ){
      SHA3Update(&cx,(const unsigned char*)"R",1);
      for(i=0; i<nCol; i++){
        switch( sqlite3_column_type(pStmt,i) ){
          case SQLITE_NULL: {
5559
5560
5561
5562
5563
5564
5565
5566

5567
5568
5569
5570
5571
5572
5573
** is a bitmask showing which constraints are available:
**
**    1:    start=VALUE
**    2:    stop=VALUE
**    4:    step=VALUE
**
** Also, if bit 8 is set, that means that the series should be output
** in descending order rather than in ascending order.

**
** This routine should initialize the cursor and position it so that it
** is pointing at the first row, or pointing off the end of the table
** (so that seriesEof() will return true) if the table is empty.
*/
static int seriesFilter(
  sqlite3_vtab_cursor *pVtabCursor, 







|
>







5564
5565
5566
5567
5568
5569
5570
5571
5572
5573
5574
5575
5576
5577
5578
5579
** is a bitmask showing which constraints are available:
**
**    1:    start=VALUE
**    2:    stop=VALUE
**    4:    step=VALUE
**
** Also, if bit 8 is set, that means that the series should be output
** in descending order rather than in ascending order.  If bit 16 is
** set, then output must appear in ascending order.
**
** This routine should initialize the cursor and position it so that it
** is pointing at the first row, or pointing off the end of the table
** (so that seriesEof() will return true) if the table is empty.
*/
static int seriesFilter(
  sqlite3_vtab_cursor *pVtabCursor, 
5585
5586
5587
5588
5589
5590
5591
5592





5593
5594
5595
5596
5597
5598
5599
  if( idxNum & 2 ){
    pCur->mxValue = sqlite3_value_int64(argv[i++]);
  }else{
    pCur->mxValue = 0xffffffff;
  }
  if( idxNum & 4 ){
    pCur->iStep = sqlite3_value_int64(argv[i++]);
    if( pCur->iStep<1 ) pCur->iStep = 1;





  }else{
    pCur->iStep = 1;
  }
  for(i=0; i<argc; i++){
    if( sqlite3_value_type(argv[i])==SQLITE_NULL ){
      /* If any of the constraints have a NULL value, then return no rows.
      ** See ticket https://www.sqlite.org/src/info/fac496b61722daf2 */







|
>
>
>
>
>







5591
5592
5593
5594
5595
5596
5597
5598
5599
5600
5601
5602
5603
5604
5605
5606
5607
5608
5609
5610
  if( idxNum & 2 ){
    pCur->mxValue = sqlite3_value_int64(argv[i++]);
  }else{
    pCur->mxValue = 0xffffffff;
  }
  if( idxNum & 4 ){
    pCur->iStep = sqlite3_value_int64(argv[i++]);
    if( pCur->iStep==0 ){
      pCur->iStep = 1;
    }else if( pCur->iStep<0 ){
      pCur->iStep = -pCur->iStep;
      if( (idxNum & 16)==0 ) idxNum |= 8;
    }
  }else{
    pCur->iStep = 1;
  }
  for(i=0; i<argc; i++){
    if( sqlite3_value_type(argv[i])==SQLITE_NULL ){
      /* If any of the constraints have a NULL value, then return no rows.
      ** See ticket https://www.sqlite.org/src/info/fac496b61722daf2 */
5679
5680
5681
5682
5683
5684
5685
5686




5687
5688
5689
5690
5691
5692
5693
  }
  if( (idxNum & 3)==3 ){
    /* Both start= and stop= boundaries are available.  This is the 
    ** the preferred case */
    pIdxInfo->estimatedCost = (double)(2 - ((idxNum&4)!=0));
    pIdxInfo->estimatedRows = 1000;
    if( pIdxInfo->nOrderBy==1 ){
      if( pIdxInfo->aOrderBy[0].desc ) idxNum |= 8;




      pIdxInfo->orderByConsumed = 1;
    }
  }else{
    /* If either boundary is missing, we have to generate a huge span
    ** of numbers.  Make this case very expensive so that the query
    ** planner will work hard to avoid it. */
    pIdxInfo->estimatedRows = 2147483647;







|
>
>
>
>







5690
5691
5692
5693
5694
5695
5696
5697
5698
5699
5700
5701
5702
5703
5704
5705
5706
5707
5708
  }
  if( (idxNum & 3)==3 ){
    /* Both start= and stop= boundaries are available.  This is the 
    ** the preferred case */
    pIdxInfo->estimatedCost = (double)(2 - ((idxNum&4)!=0));
    pIdxInfo->estimatedRows = 1000;
    if( pIdxInfo->nOrderBy==1 ){
      if( pIdxInfo->aOrderBy[0].desc ){
        idxNum |= 8;
      }else{
        idxNum |= 16;
      }
      pIdxInfo->orderByConsumed = 1;
    }
  }else{
    /* If either boundary is missing, we have to generate a huge span
    ** of numbers.  Make this case very expensive so that the query
    ** planner will work hard to avoid it. */
    pIdxInfo->estimatedRows = 2147483647;
8930
8931
8932
8933
8934
8935
8936
8937
8938
8939
8940
8941
8942
8943
8944
  int nTab = STRLEN(zTab);
  int nByte = sizeof(IdxTable) + nTab + 1;
  IdxTable *pNew = 0;
  int rc, rc2;
  char *pCsr = 0;
  int nPk = 0;

  rc = idxPrintfPrepareStmt(db, &p1, pzErrmsg, "PRAGMA table_info=%Q", zTab);
  while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(p1) ){
    const char *zCol = (const char*)sqlite3_column_text(p1, 1);
    nByte += 1 + STRLEN(zCol);
    rc = sqlite3_table_column_metadata(
        db, "main", zTab, zCol, 0, &zCol, 0, 0, 0
    );
    nByte += 1 + STRLEN(zCol);







|







8945
8946
8947
8948
8949
8950
8951
8952
8953
8954
8955
8956
8957
8958
8959
  int nTab = STRLEN(zTab);
  int nByte = sizeof(IdxTable) + nTab + 1;
  IdxTable *pNew = 0;
  int rc, rc2;
  char *pCsr = 0;
  int nPk = 0;

  rc = idxPrintfPrepareStmt(db, &p1, pzErrmsg, "PRAGMA table_xinfo=%Q", zTab);
  while( rc==SQLITE_OK && SQLITE_ROW==sqlite3_step(p1) ){
    const char *zCol = (const char*)sqlite3_column_text(p1, 1);
    nByte += 1 + STRLEN(zCol);
    rc = sqlite3_table_column_metadata(
        db, "main", zTab, zCol, 0, &zCol, 0, 0, 0
    );
    nByte += 1 + STRLEN(zCol);
9964
9965
9966
9967
9968
9969
9970

9971
9972
9973
9974

9975
9976
9977
9978
9979
9980
9981
    );
  }

  idxFinalize(&rc, pAllIndex);
  idxFinalize(&rc, pIndexXInfo);
  idxFinalize(&rc, pWrite);


  for(i=0; i<pCtx->nSlot; i++){
    sqlite3_free(pCtx->aSlot[i].z);
  }
  sqlite3_free(pCtx);


  if( rc==SQLITE_OK ){
    rc = sqlite3_exec(p->dbm, "ANALYZE sqlite_schema", 0, 0, 0);
  }

  sqlite3_exec(p->db, "DROP TABLE IF EXISTS temp."UNIQUE_TABLE_NAME,0,0,0);
  return rc;







>
|
|
|
|
>







9979
9980
9981
9982
9983
9984
9985
9986
9987
9988
9989
9990
9991
9992
9993
9994
9995
9996
9997
9998
    );
  }

  idxFinalize(&rc, pAllIndex);
  idxFinalize(&rc, pIndexXInfo);
  idxFinalize(&rc, pWrite);

  if( pCtx ){
    for(i=0; i<pCtx->nSlot; i++){
      sqlite3_free(pCtx->aSlot[i].z);
    }
    sqlite3_free(pCtx);
  }

  if( rc==SQLITE_OK ){
    rc = sqlite3_exec(p->dbm, "ANALYZE sqlite_schema", 0, 0, 0);
  }

  sqlite3_exec(p->db, "DROP TABLE IF EXISTS temp."UNIQUE_TABLE_NAME,0,0,0);
  return rc;
11108
11109
11110
11111
11112
11113
11114
11115
11116
11117
11118
11119
11120

11121
11122
11123
11124
11125
11126
11127
typedef struct ShellState ShellState;
struct ShellState {
  sqlite3 *db;           /* The database */
  u8 autoExplain;        /* Automatically turn on .explain mode */
  u8 autoEQP;            /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */
  u8 autoEQPtest;        /* autoEQP is in test mode */
  u8 autoEQPtrace;       /* autoEQP is in trace mode */
  u8 statsOn;            /* True to display memory stats before each finalize */
  u8 scanstatsOn;        /* True to display scan stats before each finalize */
  u8 openMode;           /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */
  u8 doXdgOpen;          /* Invoke start/open/xdg-open in output_reset() */
  u8 nEqpLevel;          /* Depth of the EQP output graph */
  u8 eTraceType;         /* SHELL_TRACE_* value for type of trace */

  unsigned mEqpLines;    /* Mask of veritical lines in the EQP output graph */
  int outCount;          /* Revert to stdout when reaching zero */
  int cnt;               /* Number of records displayed so far */
  int lineno;            /* Line number of last line read from in */
  int openFlags;         /* Additional flags to open.  (SQLITE_OPEN_NOFOLLOW) */
  FILE *in;              /* Read commands from this stream */
  FILE *out;             /* Write results here */







<





>







11125
11126
11127
11128
11129
11130
11131

11132
11133
11134
11135
11136
11137
11138
11139
11140
11141
11142
11143
11144
typedef struct ShellState ShellState;
struct ShellState {
  sqlite3 *db;           /* The database */
  u8 autoExplain;        /* Automatically turn on .explain mode */
  u8 autoEQP;            /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */
  u8 autoEQPtest;        /* autoEQP is in test mode */
  u8 autoEQPtrace;       /* autoEQP is in trace mode */

  u8 scanstatsOn;        /* True to display scan stats before each finalize */
  u8 openMode;           /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */
  u8 doXdgOpen;          /* Invoke start/open/xdg-open in output_reset() */
  u8 nEqpLevel;          /* Depth of the EQP output graph */
  u8 eTraceType;         /* SHELL_TRACE_* value for type of trace */
  unsigned statsOn;      /* True to display memory stats before each finalize */
  unsigned mEqpLines;    /* Mask of veritical lines in the EQP output graph */
  int outCount;          /* Revert to stdout when reaching zero */
  int cnt;               /* Number of records displayed so far */
  int lineno;            /* Line number of last line read from in */
  int openFlags;         /* Additional flags to open.  (SQLITE_OPEN_NOFOLLOW) */
  FILE *in;              /* Read commands from this stream */
  FILE *out;             /* Write results here */
12050
12051
12052
12053
12054
12055
12056

12057
12058
12059
12060
12061
12062
12063
          print_dashes(p->out, w);
          fputs(i==nArg-1 ? "\n" : "  ", p->out);
        }
      }
      if( azArg==0 ) break;
      for(i=0; i<nArg; i++){
        int w = aExplainWidth[i];

        if( azArg[i] && strlenChar(azArg[i])>w ){
          w = strlenChar(azArg[i]);
        }
        if( i==1 && p->aiIndent && p->pStmt ){
          if( p->iIndent<p->nIndent ){
            utf8_printf(p->out, "%*.s", p->aiIndent[p->iIndent], "");
          }







>







12067
12068
12069
12070
12071
12072
12073
12074
12075
12076
12077
12078
12079
12080
12081
          print_dashes(p->out, w);
          fputs(i==nArg-1 ? "\n" : "  ", p->out);
        }
      }
      if( azArg==0 ) break;
      for(i=0; i<nArg; i++){
        int w = aExplainWidth[i];
        if( i==nArg-1 ) w = 0;
        if( azArg[i] && strlenChar(azArg[i])>w ){
          w = strlenChar(azArg[i]);
        }
        if( i==1 && p->aiIndent && p->pStmt ){
          if( p->iIndent<p->nIndent ){
            utf8_printf(p->out, "%*.s", p->aiIndent[p->iIndent], "");
          }
12614
12615
12616
12617
12618
12619
12620
12621
12622
12623
12624
12625
12626
12627
12628
){
  int iCur;
  int iHiwtr;
  FILE *out;
  if( pArg==0 || pArg->out==0 ) return 0;
  out = pArg->out;

  if( pArg->pStmt && (pArg->statsOn & 2) ){
    int nCol, i, x;
    sqlite3_stmt *pStmt = pArg->pStmt;
    char z[100];
    nCol = sqlite3_column_count(pStmt);
    raw_printf(out, "%-36s %d\n", "Number of output columns:", nCol);
    for(i=0; i<nCol; i++){
      sqlite3_snprintf(sizeof(z),z,"Column %d %nname:", i, &x);







|







12632
12633
12634
12635
12636
12637
12638
12639
12640
12641
12642
12643
12644
12645
12646
){
  int iCur;
  int iHiwtr;
  FILE *out;
  if( pArg==0 || pArg->out==0 ) return 0;
  out = pArg->out;

  if( pArg->pStmt && pArg->statsOn==2 ){
    int nCol, i, x;
    sqlite3_stmt *pStmt = pArg->pStmt;
    char z[100];
    nCol = sqlite3_column_count(pStmt);
    raw_printf(out, "%-36s %d\n", "Number of output columns:", nCol);
    for(i=0; i<nCol; i++){
      sqlite3_snprintf(sizeof(z),z,"Column %d %nname:", i, &x);
12637
12638
12639
12640
12641
12642
12643








12644
12645
12646
12647
12648
12649
12650
      sqlite3_snprintf(30, z+x, "table name:");
      utf8_printf(out, "%-36s %s\n", z, sqlite3_column_table_name(pStmt,i));
      sqlite3_snprintf(30, z+x, "origin name:");
      utf8_printf(out, "%-36s %s\n", z, sqlite3_column_origin_name(pStmt,i));
#endif
    }
  }









  displayStatLine(pArg, "Memory Used:",
     "%lld (max %lld) bytes", SQLITE_STATUS_MEMORY_USED, bReset);
  displayStatLine(pArg, "Number of Outstanding Allocations:",
     "%lld (max %lld)", SQLITE_STATUS_MALLOC_COUNT, bReset);
  if( pArg->shellFlgs & SHFLG_Pagecache ){
    displayStatLine(pArg, "Number of Pcache Pages Used:",







>
>
>
>
>
>
>
>







12655
12656
12657
12658
12659
12660
12661
12662
12663
12664
12665
12666
12667
12668
12669
12670
12671
12672
12673
12674
12675
12676
      sqlite3_snprintf(30, z+x, "table name:");
      utf8_printf(out, "%-36s %s\n", z, sqlite3_column_table_name(pStmt,i));
      sqlite3_snprintf(30, z+x, "origin name:");
      utf8_printf(out, "%-36s %s\n", z, sqlite3_column_origin_name(pStmt,i));
#endif
    }
  }

  if( pArg->statsOn==3 ){
    if( pArg->pStmt ){
      iCur = sqlite3_stmt_status(pArg->pStmt, SQLITE_STMTSTATUS_VM_STEP, bReset);
      raw_printf(pArg->out, "VM-steps: %d\n", iCur);
    }
    return 0;
  }

  displayStatLine(pArg, "Memory Used:",
     "%lld (max %lld) bytes", SQLITE_STATUS_MEMORY_USED, bReset);
  displayStatLine(pArg, "Number of Outstanding Allocations:",
     "%lld (max %lld)", SQLITE_STATUS_MALLOC_COUNT, bReset);
  if( pArg->shellFlgs & SHFLG_Pagecache ){
    displayStatLine(pArg, "Number of Pcache Pages Used:",
12904
12905
12906
12907
12908
12909
12910
12911
12912
12913
12914
12915
12916
12917
12918
12919
12920

12921
12922
12923
12924
12925
12926
12927
12928
12929
12930
12931
12932
12933
12934
12935
12936
12937
12938
12939
12940
12941
12942
  p->nIndent = 0;
  p->iIndent = 0;
}

/*
** Disable and restore .wheretrace and .selecttrace settings.
*/
#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE)
extern unsigned int sqlite3_unsupported_selecttrace;
static int savedSelectTrace;
#endif
#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE)
extern int sqlite3WhereTrace;
static int savedWhereTrace;
#endif
static void disable_debug_trace_modes(void){
#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE)

  savedSelectTrace = sqlite3_unsupported_selecttrace;
  sqlite3_unsupported_selecttrace = 0;
#endif
#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE)
  savedWhereTrace = sqlite3WhereTrace;
  sqlite3WhereTrace = 0;
#endif
}
static void restore_debug_trace_modes(void){
#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE)
  sqlite3_unsupported_selecttrace = savedSelectTrace;
#endif
#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE)
  sqlite3WhereTrace = savedWhereTrace;
#endif
}

/* Create the TEMP table used to store parameter bindings */
static void bind_table_init(ShellState *p){
  int wrSchema = 0;
  int defensiveMode = 0;
  sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, -1, &defensiveMode);







<
<
|
<
<
<
|
<

<
>
|
|
<
<
|
|
<


<
|
<
<
|
<







12930
12931
12932
12933
12934
12935
12936


12937



12938

12939

12940
12941
12942


12943
12944

12945
12946

12947


12948

12949
12950
12951
12952
12953
12954
12955
  p->nIndent = 0;
  p->iIndent = 0;
}

/*
** Disable and restore .wheretrace and .selecttrace settings.
*/


static unsigned int savedSelectTrace;



static unsigned int savedWhereTrace;

static void disable_debug_trace_modes(void){

  unsigned int zero = 0;
  sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 0, &savedSelectTrace);
  sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 1, &zero);


  sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 2, &savedWhereTrace);
  sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 3, &zero);

}
static void restore_debug_trace_modes(void){

  sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 1, &savedSelectTrace);


  sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 3, &savedWhereTrace);

}

/* Create the TEMP table used to store parameter bindings */
static void bind_table_init(ShellState *p){
  int wrSchema = 0;
  int defensiveMode = 0;
  sqlite3_db_config(p->db, SQLITE_DBCONFIG_DEFENSIVE, -1, &defensiveMode);
14083
14084
14085
14086
14087
14088
14089
14090




14091
14092
14093
14094
14095
14096
14097
  "      --sha3-384            Use the sha3-384 algorithm",
  "      --sha3-512            Use the sha3-512 algorithm",
  "    Any other argument is a LIKE pattern for tables to hash",
#ifndef SQLITE_NOHAVE_SYSTEM
  ".shell CMD ARGS...       Run CMD ARGS... in a system shell",
#endif
  ".show                    Show the current values for various settings",
  ".stats ?on|off?          Show stats or turn stats on or off",




#ifndef SQLITE_NOHAVE_SYSTEM
  ".system CMD ARGS...      Run CMD ARGS... in a system shell",
#endif
  ".tables ?TABLE?          List names of tables matching LIKE pattern TABLE",
  ".testcase NAME           Begin redirecting output to 'testcase-out.txt'",
  ".testctrl CMD ...        Run various sqlite3_test_control() operations",
  "                           Run \".testctrl\" with no arguments for details",







|
>
>
>
>







14096
14097
14098
14099
14100
14101
14102
14103
14104
14105
14106
14107
14108
14109
14110
14111
14112
14113
14114
  "      --sha3-384            Use the sha3-384 algorithm",
  "      --sha3-512            Use the sha3-512 algorithm",
  "    Any other argument is a LIKE pattern for tables to hash",
#ifndef SQLITE_NOHAVE_SYSTEM
  ".shell CMD ARGS...       Run CMD ARGS... in a system shell",
#endif
  ".show                    Show the current values for various settings",
  ".stats ?ARG?             Show stats or turn stats on or off",
  "   off                      Turn off automatic stat display",
  "   on                       Turn on automatic stat display",
  "   stmt                     Show statement stats",
  "   vmstep                   Show the virtual machine step count only",
#ifndef SQLITE_NOHAVE_SYSTEM
  ".system CMD ARGS...      Run CMD ARGS... in a system shell",
#endif
  ".tables ?TABLE?          List names of tables matching LIKE pattern TABLE",
  ".testcase NAME           Begin redirecting output to 'testcase-out.txt'",
  ".testctrl CMD ...        Run various sqlite3_test_control() operations",
  "                           Run \".testctrl\" with no arguments for details",
17899
17900
17901
17902
17903
17904
17905
17906
17907
17908


17909

17910
17911

17912
17913
17914
17915
17916
17917
17918
17919
17920
17921
17922

  if( c=='f' && strncmp(azArg[0], "filectrl", n)==0 ){
    static const struct {
       const char *zCtrlName;   /* Name of a test-control option */
       int ctrlCode;            /* Integer code for that option */
       const char *zUsage;      /* Usage notes */
    } aCtrl[] = {
      { "size_limit",     SQLITE_FCNTL_SIZE_LIMIT,      "[LIMIT]"        },
      { "chunk_size",     SQLITE_FCNTL_CHUNK_SIZE,      "SIZE"           },
   /* { "win32_av_retry", SQLITE_FCNTL_WIN32_AV_RETRY,  "COUNT DELAY"    },*/


      { "persist_wal",    SQLITE_FCNTL_PERSIST_WAL,     "[BOOLEAN]"      },

      { "psow",       SQLITE_FCNTL_POWERSAFE_OVERWRITE, "[BOOLEAN]"      },
   /* { "pragma",         SQLITE_FCNTL_PRAGMA,          "NAME ARG"       },*/

      { "tempfilename",   SQLITE_FCNTL_TEMPFILENAME,    ""               },
      { "has_moved",      SQLITE_FCNTL_HAS_MOVED,       ""               },  
      { "lock_timeout",   SQLITE_FCNTL_LOCK_TIMEOUT,    "MILLISEC"       },
      { "reserve_bytes",  SQLITE_FCNTL_RESERVE_BYTES,   "[N]"            },
    };
    int filectrl = -1;
    int iCtrl = -1;
    sqlite3_int64 iRes = 0;  /* Integer result to display if rc2==1 */
    int isOk = 0;            /* 0: usage  1: %lld  2: no-result */
    int n2, i;
    const char *zCmd = 0;







<

|
>
>

>

|
>

|
<
<







17916
17917
17918
17919
17920
17921
17922

17923
17924
17925
17926
17927
17928
17929
17930
17931
17932
17933


17934
17935
17936
17937
17938
17939
17940

  if( c=='f' && strncmp(azArg[0], "filectrl", n)==0 ){
    static const struct {
       const char *zCtrlName;   /* Name of a test-control option */
       int ctrlCode;            /* Integer code for that option */
       const char *zUsage;      /* Usage notes */
    } aCtrl[] = {

      { "chunk_size",     SQLITE_FCNTL_CHUNK_SIZE,      "SIZE"           },
      { "data_version",   SQLITE_FCNTL_DATA_VERSION,    ""               },
      { "has_moved",      SQLITE_FCNTL_HAS_MOVED,       ""               },  
      { "lock_timeout",   SQLITE_FCNTL_LOCK_TIMEOUT,    "MILLISEC"       },
      { "persist_wal",    SQLITE_FCNTL_PERSIST_WAL,     "[BOOLEAN]"      },
   /* { "pragma",         SQLITE_FCNTL_PRAGMA,          "NAME ARG"       },*/
      { "psow",       SQLITE_FCNTL_POWERSAFE_OVERWRITE, "[BOOLEAN]"      },
      { "reserve_bytes",  SQLITE_FCNTL_RESERVE_BYTES,   "[N]"            },
      { "size_limit",     SQLITE_FCNTL_SIZE_LIMIT,      "[LIMIT]"        },
      { "tempfilename",   SQLITE_FCNTL_TEMPFILENAME,    ""               },
   /* { "win32_av_retry", SQLITE_FCNTL_WIN32_AV_RETRY,  "COUNT DELAY"    },*/


    };
    int filectrl = -1;
    int iCtrl = -1;
    sqlite3_int64 iRes = 0;  /* Integer result to display if rc2==1 */
    int isOk = 0;            /* 0: usage  1: %lld  2: no-result */
    int n2, i;
    const char *zCmd = 0;
17995
17996
17997
17998
17999
18000
18001

18002
18003
18004
18005
18006
18007
18008
          if( nArg!=2 && nArg!=3 ) break;
          x = nArg==3 ? booleanValue(azArg[2]) : -1;
          sqlite3_file_control(p->db, zSchema, filectrl, &x);
          iRes = x;
          isOk = 1;
          break;
        }

        case SQLITE_FCNTL_HAS_MOVED: {
          int x;
          if( nArg!=2 ) break;
          sqlite3_file_control(p->db, zSchema, filectrl, &x);
          iRes = x;
          isOk = 1;
          break;







>







18013
18014
18015
18016
18017
18018
18019
18020
18021
18022
18023
18024
18025
18026
18027
          if( nArg!=2 && nArg!=3 ) break;
          x = nArg==3 ? booleanValue(azArg[2]) : -1;
          sqlite3_file_control(p->db, zSchema, filectrl, &x);
          iRes = x;
          isOk = 1;
          break;
        }
        case SQLITE_FCNTL_DATA_VERSION:
        case SQLITE_FCNTL_HAS_MOVED: {
          int x;
          if( nArg!=2 ) break;
          sqlite3_file_control(p->db, zSchema, filectrl, &x);
          iRes = x;
          isOk = 1;
          break;
18703
18704
18705
18706
18707
18708
18709
18710
18711
18712
18713
18714
18715
18716
18717
18718
18719
18720
18721
18722
18723
18724
18725
18726
18727
18728
18729
18730
18731
      raw_printf(p->out, "oomCounter = %d\n", oomCounter);
      raw_printf(p->out, "oomRepeat  = %d\n", oomRepeat);
    }
  }else
#endif /* SQLITE_DEBUG */

  if( c=='o' && strncmp(azArg[0], "open", n)==0 && n>=2 ){
    char *zNewFilename;  /* Name of the database file to open */
    int iName = 1;       /* Index in azArg[] of the filename */
    int newFlag = 0;     /* True to delete file before opening */
    /* Close the existing database */
    session_close_all(p);
    close_db(p->db);
    p->db = 0;
    p->zDbFilename = 0;
    sqlite3_free(p->zFreeOnClose);
    p->zFreeOnClose = 0;
    p->openMode = SHELL_OPEN_UNSPEC;
    p->openFlags = 0;
    p->szMax = 0;
    /* Check for command-line arguments */
    for(iName=1; iName<nArg && azArg[iName][0]=='-'; iName++){
      const char *z = azArg[iName];
      if( optionMatch(z,"new") ){
        newFlag = 1;
#ifdef SQLITE_HAVE_ZLIB
      }else if( optionMatch(z, "zip") ){
        p->openMode = SHELL_OPEN_ZIPFILE;
#endif







|
|
|











|







18722
18723
18724
18725
18726
18727
18728
18729
18730
18731
18732
18733
18734
18735
18736
18737
18738
18739
18740
18741
18742
18743
18744
18745
18746
18747
18748
18749
18750
      raw_printf(p->out, "oomCounter = %d\n", oomCounter);
      raw_printf(p->out, "oomRepeat  = %d\n", oomRepeat);
    }
  }else
#endif /* SQLITE_DEBUG */

  if( c=='o' && strncmp(azArg[0], "open", n)==0 && n>=2 ){
    char *zNewFilename = 0;  /* Name of the database file to open */
    int iName = 1;           /* Index in azArg[] of the filename */
    int newFlag = 0;         /* True to delete file before opening */
    /* Close the existing database */
    session_close_all(p);
    close_db(p->db);
    p->db = 0;
    p->zDbFilename = 0;
    sqlite3_free(p->zFreeOnClose);
    p->zFreeOnClose = 0;
    p->openMode = SHELL_OPEN_UNSPEC;
    p->openFlags = 0;
    p->szMax = 0;
    /* Check for command-line arguments */
    for(iName=1; iName<nArg; iName++){
      const char *z = azArg[iName];
      if( optionMatch(z,"new") ){
        newFlag = 1;
#ifdef SQLITE_HAVE_ZLIB
      }else if( optionMatch(z, "zip") ){
        p->openMode = SHELL_OPEN_ZIPFILE;
#endif
18743
18744
18745
18746
18747
18748
18749






18750
18751
18752
18753
18754
18755
18756
18757
18758
18759
18760
      }else if( optionMatch(z, "maxsize") && iName+1<nArg ){
        p->szMax = integerValue(azArg[++iName]);
#endif /* SQLITE_ENABLE_DESERIALIZE */
      }else if( z[0]=='-' ){
        utf8_printf(stderr, "unknown option: %s\n", z);
        rc = 1;
        goto meta_command_exit;






      }
    }
    /* If a filename is specified, try to open it first */
    zNewFilename = nArg>iName ? sqlite3_mprintf("%s", azArg[iName]) : 0;
    if( zNewFilename || p->openMode==SHELL_OPEN_HEXDB ){
      if( newFlag ) shellDeleteFile(zNewFilename);
      p->zDbFilename = zNewFilename;
      open_db(p, OPEN_DB_KEEPALIVE);
      if( p->db==0 ){
        utf8_printf(stderr, "Error: cannot open '%s'\n", zNewFilename);
        sqlite3_free(zNewFilename);







>
>
>
>
>
>



<







18762
18763
18764
18765
18766
18767
18768
18769
18770
18771
18772
18773
18774
18775
18776
18777

18778
18779
18780
18781
18782
18783
18784
      }else if( optionMatch(z, "maxsize") && iName+1<nArg ){
        p->szMax = integerValue(azArg[++iName]);
#endif /* SQLITE_ENABLE_DESERIALIZE */
      }else if( z[0]=='-' ){
        utf8_printf(stderr, "unknown option: %s\n", z);
        rc = 1;
        goto meta_command_exit;
      }else if( zNewFilename ){
        utf8_printf(stderr, "extra argument: \"%s\"\n", z);
        rc = 1;
        goto meta_command_exit;
      }else{
        zNewFilename = sqlite3_mprintf("%s", z);
      }
    }
    /* If a filename is specified, try to open it first */

    if( zNewFilename || p->openMode==SHELL_OPEN_HEXDB ){
      if( newFlag ) shellDeleteFile(zNewFilename);
      p->zDbFilename = zNewFilename;
      open_db(p, OPEN_DB_KEEPALIVE);
      if( p->db==0 ){
        utf8_printf(stderr, "Error: cannot open '%s'\n", zNewFilename);
        sqlite3_free(zNewFilename);
19269
19270
19271
19272
19273
19274
19275
19276
19277
19278

19279
19280
19281
19282
19283
19284
19285
19286
19287
      raw_printf(stderr,"Error: querying schema information\n");
      rc = 1;
    }else{
      rc = 0;
    }
  }else

#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE)
  if( c=='s' && n==11 && strncmp(azArg[0], "selecttrace", n)==0 ){
    sqlite3_unsupported_selecttrace = nArg>=2 ? (int)integerValue(azArg[1]) : 0xffff;

  }else
#endif

#if defined(SQLITE_ENABLE_SESSION)
  if( c=='s' && strncmp(azArg[0],"session",n)==0 && n>=3 ){
    OpenSession *pSession = &p->aSession[0];
    char **azCmd = &azArg[1];
    int iSes = 0;
    int nCmd = nArg - 1;







<

|
>

<







19293
19294
19295
19296
19297
19298
19299

19300
19301
19302
19303

19304
19305
19306
19307
19308
19309
19310
      raw_printf(stderr,"Error: querying schema information\n");
      rc = 1;
    }else{
      rc = 0;
    }
  }else


  if( c=='s' && n==11 && strncmp(azArg[0], "selecttrace", n)==0 ){
    unsigned int x = nArg>=2 ? (unsigned int)integerValue(azArg[1]) : 0xffffffff;
    sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 1, &x);
  }else


#if defined(SQLITE_ENABLE_SESSION)
  if( c=='s' && strncmp(azArg[0],"session",n)==0 && n>=3 ){
    OpenSession *pSession = &p->aSession[0];
    char **azCmd = &azArg[1];
    int iSes = 0;
    int nCmd = nArg - 1;
19754
19755
19756
19757
19758
19759
19760

19761
19762
19763
19764
19765
19766
19767
    sqlite3_free(zCmd);
    if( x ) raw_printf(stderr, "System command returns %d\n", x);
  }else
#endif /* !defined(SQLITE_NOHAVE_SYSTEM) */

  if( c=='s' && strncmp(azArg[0], "show", n)==0 ){
    static const char *azBool[] = { "off", "on", "trigger", "full"};

    int i;
    if( nArg!=1 ){
      raw_printf(stderr, "Usage: .show\n");
      rc = 1;
      goto meta_command_exit;
    }
    utf8_printf(p->out, "%12.12s: %s\n","echo",







>







19777
19778
19779
19780
19781
19782
19783
19784
19785
19786
19787
19788
19789
19790
19791
    sqlite3_free(zCmd);
    if( x ) raw_printf(stderr, "System command returns %d\n", x);
  }else
#endif /* !defined(SQLITE_NOHAVE_SYSTEM) */

  if( c=='s' && strncmp(azArg[0], "show", n)==0 ){
    static const char *azBool[] = { "off", "on", "trigger", "full"};
    const char *zOut;
    int i;
    if( nArg!=1 ){
      raw_printf(stderr, "Usage: .show\n");
      rc = 1;
      goto meta_command_exit;
    }
    utf8_printf(p->out, "%12.12s: %s\n","echo",
19778
19779
19780
19781
19782
19783
19784






19785
19786
19787
19788
19789
19790
19791
19792
19793
19794
19795
19796





19797

19798
19799
19800
19801
19802
19803
19804
19805
19806
19807
19808
            strlen30(p->outfile) ? p->outfile : "stdout");
    utf8_printf(p->out,"%12.12s: ", "colseparator");
      output_c_string(p->out, p->colSeparator);
      raw_printf(p->out, "\n");
    utf8_printf(p->out,"%12.12s: ", "rowseparator");
      output_c_string(p->out, p->rowSeparator);
      raw_printf(p->out, "\n");






    utf8_printf(p->out, "%12.12s: %s\n","stats", azBool[p->statsOn!=0]);
    utf8_printf(p->out, "%12.12s: ", "width");
    for (i=0;i<p->nWidth;i++) {
      raw_printf(p->out, "%d ", p->colWidth[i]);
    }
    raw_printf(p->out, "\n");
    utf8_printf(p->out, "%12.12s: %s\n", "filename",
                p->zDbFilename ? p->zDbFilename : "");
  }else

  if( c=='s' && strncmp(azArg[0], "stats", n)==0 ){
    if( nArg==2 ){





      p->statsOn = (u8)booleanValue(azArg[1]);

    }else if( nArg==1 ){
      display_stats(p->db, p, 0);
    }else{
      raw_printf(stderr, "Usage: .stats ?on|off?\n");
      rc = 1;
    }
  }else

  if( (c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0)
   || (c=='i' && (strncmp(azArg[0], "indices", n)==0
                 || strncmp(azArg[0], "indexes", n)==0) )







>
>
>
>
>
>
|











>
>
>
>
>
|
>



|







19802
19803
19804
19805
19806
19807
19808
19809
19810
19811
19812
19813
19814
19815
19816
19817
19818
19819
19820
19821
19822
19823
19824
19825
19826
19827
19828
19829
19830
19831
19832
19833
19834
19835
19836
19837
19838
19839
19840
19841
19842
19843
19844
            strlen30(p->outfile) ? p->outfile : "stdout");
    utf8_printf(p->out,"%12.12s: ", "colseparator");
      output_c_string(p->out, p->colSeparator);
      raw_printf(p->out, "\n");
    utf8_printf(p->out,"%12.12s: ", "rowseparator");
      output_c_string(p->out, p->rowSeparator);
      raw_printf(p->out, "\n");
    switch( p->statsOn ){
      case 0:  zOut = "off";     break;
      default: zOut = "on";      break;
      case 2:  zOut = "stmt";    break;
      case 3:  zOut = "vmstep";  break;
    }
    utf8_printf(p->out, "%12.12s: %s\n","stats", zOut);
    utf8_printf(p->out, "%12.12s: ", "width");
    for (i=0;i<p->nWidth;i++) {
      raw_printf(p->out, "%d ", p->colWidth[i]);
    }
    raw_printf(p->out, "\n");
    utf8_printf(p->out, "%12.12s: %s\n", "filename",
                p->zDbFilename ? p->zDbFilename : "");
  }else

  if( c=='s' && strncmp(azArg[0], "stats", n)==0 ){
    if( nArg==2 ){
      if( strcmp(azArg[1],"stmt")==0 ){
        p->statsOn = 2;
      }else if( strcmp(azArg[1],"vmstep")==0 ){
        p->statsOn = 3;
      }else{
        p->statsOn = (u8)booleanValue(azArg[1]);
      }
    }else if( nArg==1 ){
      display_stats(p->db, p, 0);
    }else{
      raw_printf(stderr, "Usage: .stats ?on|off|stmt|vmstep?\n");
      rc = 1;
    }
  }else

  if( (c=='t' && n>1 && strncmp(azArg[0], "tables", n)==0)
   || (c=='i' && (strncmp(azArg[0], "indices", n)==0
                 || strncmp(azArg[0], "indexes", n)==0) )
19999
20000
20001
20002
20003
20004
20005
20006
20007
20008
20009
20010
20011
20012
20013
                         "Use \".testctrl --help\" for help\n", zCmd);
    }else{
      switch(testctrl){

        /* sqlite3_test_control(int, db, int) */
        case SQLITE_TESTCTRL_OPTIMIZATIONS:
          if( nArg==3 ){
            int opt = (int)strtol(azArg[2], 0, 0);
            rc2 = sqlite3_test_control(testctrl, p->db, opt);
            isOk = 3;
          }
          break;

        /* sqlite3_test_control(int) */
        case SQLITE_TESTCTRL_PRNG_SAVE:







|







20035
20036
20037
20038
20039
20040
20041
20042
20043
20044
20045
20046
20047
20048
20049
                         "Use \".testctrl --help\" for help\n", zCmd);
    }else{
      switch(testctrl){

        /* sqlite3_test_control(int, db, int) */
        case SQLITE_TESTCTRL_OPTIMIZATIONS:
          if( nArg==3 ){
            unsigned int opt = (unsigned int)strtol(azArg[2], 0, 0);
            rc2 = sqlite3_test_control(testctrl, p->db, opt);
            isOk = 3;
          }
          break;

        /* sqlite3_test_control(int) */
        case SQLITE_TESTCTRL_PRNG_SAVE:
20328
20329
20330
20331
20332
20333
20334
20335
20336
20337

20338
20339
20340
20341
20342
20343
20344
20345
20346
      if( zVfsName ){
        utf8_printf(p->out, "%s\n", zVfsName);
        sqlite3_free(zVfsName);
      }
    }
  }else

#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE)
  if( c=='w' && strncmp(azArg[0], "wheretrace", n)==0 ){
    sqlite3WhereTrace = nArg>=2 ? booleanValue(azArg[1]) : 0xff;

  }else
#endif

  if( c=='w' && strncmp(azArg[0], "width", n)==0 ){
    int j;
    assert( nArg<=ArraySize(azArg) );
    p->nWidth = nArg-1;
    p->colWidth = realloc(p->colWidth, p->nWidth*sizeof(int)*2);
    if( p->colWidth==0 && p->nWidth>0 ) shell_out_of_memory();







<

|
>

<







20364
20365
20366
20367
20368
20369
20370

20371
20372
20373
20374

20375
20376
20377
20378
20379
20380
20381
      if( zVfsName ){
        utf8_printf(p->out, "%s\n", zVfsName);
        sqlite3_free(zVfsName);
      }
    }
  }else


  if( c=='w' && strncmp(azArg[0], "wheretrace", n)==0 ){
    unsigned int x = nArg>=2 ? (unsigned int)integerValue(azArg[1]) : 0xffffffff;
    sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, 3, &x);
  }else


  if( c=='w' && strncmp(azArg[0], "width", n)==0 ){
    int j;
    assert( nArg<=ArraySize(azArg) );
    p->nWidth = nArg-1;
    p->colWidth = realloc(p->colWidth, p->nWidth*sizeof(int)*2);
    if( p->colWidth==0 && p->nWidth>0 ) shell_out_of_memory();

Changes to src/shun.c.

261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
    }
  }
  if( cnt==0 ){
    @ <i>no artifacts are shunned on this server</i>
  }
  db_finalize(&q);
  @ </p></blockquote>
  style_finish_page("shun");
  fossil_free(zCanonical);
}

/*
** Remove from the BLOB table all artifacts that are in the SHUN table.
*/
void shun_artifacts(void){







|







261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
    }
  }
  if( cnt==0 ){
    @ <i>no artifacts are shunned on this server</i>
  }
  db_finalize(&q);
  @ </p></blockquote>
  style_finish_page();
  fossil_free(zCanonical);
}

/*
** Remove from the BLOB table all artifacts that are in the SHUN table.
*/
void shun_artifacts(void){
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
      @ <td style="padding-right: 15px;text-align: left;">%s(zHash)</td>
      @ <td style="text-align: left;">%s(zIpAddr)</td>
      @ </tr>
    }
  }
  db_finalize(&q);
  @ </table>
  style_finish_page("rcvfromlist");
}

/*
** WEBPAGE: rcvfrom
**
** Show a single RCVFROM table entry identified by the rcvid= query
** parameters.  Requires Admin privilege.







|







401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
      @ <td style="padding-right: 15px;text-align: left;">%s(zHash)</td>
      @ <td style="text-align: left;">%s(zIpAddr)</td>
      @ </tr>
    }
  }
  db_finalize(&q);
  @ </table>
  style_finish_page();
}

/*
** WEBPAGE: rcvfrom
**
** Show a single RCVFROM table entry identified by the rcvid= query
** parameters.  Requires Admin privilege.
547
548
549
550
551
552
553
554
555
      }
      @ </form>
      @ </td></tr>
    }
  }
  @ </table>
  db_finalize(&q);
  style_finish_page("rcvfrom");
}







|

547
548
549
550
551
552
553
554
555
      }
      @ </form>
      @ </td></tr>
    }
  }
  @ </table>
  db_finalize(&q);
  style_finish_page();
}

Changes to src/sitemap.c.

113
114
115
116
117
118
119



120
121
122
123
124
125
126
    @   <li>%z(href("%R/leaves"))Leaf Check-ins</a></li>
    @ </ul>
    @ </li>
  }
  if( srchFlags ){
    @ <li>%z(href("%R/search"))Search</a></li>
  }



  if( g.perm.RdForum ){
    @ <li>%z(href("%R/forum"))Forum</a>
    @ <ul>
    @   <li>%z(href("%R/timeline?y=f"))Recent activity</a></li>
    @ </ul>
    @ </li>
  }







>
>
>







113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
    @   <li>%z(href("%R/leaves"))Leaf Check-ins</a></li>
    @ </ul>
    @ </li>
  }
  if( srchFlags ){
    @ <li>%z(href("%R/search"))Search</a></li>
  }
  if( g.perm.Chat ){
    @ <li>%z(href("%R/chat"))Chat</a></li>
  }
  if( g.perm.RdForum ){
    @ <li>%z(href("%R/forum"))Forum</a>
    @ <ul>
    @   <li>%z(href("%R/timeline?y=f"))Recent activity</a></li>
    @ </ul>
    @ </li>
  }
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
  }
  @ <li>%z(href("%R/sitemap-test"))Test Pages</a></li>
  if( isPopup ){
    @ <li>%z(href("%R/sitemap"))Site Map</a></li>
  }
  @ </ul>
  if( !isPopup ){
    style_finish_page("sitemap");
  }
}

/*
** WEBPAGE: sitemap-test
**
** List some of the web pages offered by the Fossil web engine for testing
** purposes.  This is similar to /sitemap, but is focused only on showing
** pages associated with testing.
*/
void sitemap_test_page(void){
  int isPopup = 0;         /* This is an XMLHttpRequest() for /sitemap */

  login_check_credentials();

  if( P("popup")!=0 && cgi_csrf_safe(0) ){
    /* If this is a POST from the same origin with the popup=1 parameter,
    ** then disable anti-robot defenses */
    isPopup = 1;
    g.perm.Hyperlink = 1;
    g.javascriptHyperlink = 0;
  }







|














>







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
  }
  @ <li>%z(href("%R/sitemap-test"))Test Pages</a></li>
  if( isPopup ){
    @ <li>%z(href("%R/sitemap"))Site Map</a></li>
  }
  @ </ul>
  if( !isPopup ){
    style_finish_page();
  }
}

/*
** WEBPAGE: sitemap-test
**
** List some of the web pages offered by the Fossil web engine for testing
** purposes.  This is similar to /sitemap, but is focused only on showing
** pages associated with testing.
*/
void sitemap_test_page(void){
  int isPopup = 0;         /* This is an XMLHttpRequest() for /sitemap */

  login_check_credentials();
  style_set_current_feature("sitemap");
  if( P("popup")!=0 && cgi_csrf_safe(0) ){
    /* If this is a POST from the same origin with the popup=1 parameter,
    ** then disable anti-robot defenses */
    isPopup = 1;
    g.perm.Hyperlink = 1;
    g.javascriptHyperlink = 0;
  }
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
  if( g.perm.Read && g.perm.Hyperlink ){
    @ <li>%z(href("%R/timewarps"))Timeline of timewarps</a></li>
  }
  @ <li>%z(href("%R/cookies"))Content of display preference cookie</a></li>
  @ <li>%z(href("%R/test-captcha"))Random ASCII-art Captcha image</a></li>
  @ <li>%z(href("%R/test-piechart"))Pie-Chart generator test</a></li>
  if( !isPopup ){
    style_finish_page("sitemap");
  }
}

/*
** WEBPAGE: sitemap-timeline
**
** Generate a list of hyperlinks to various (obscure) variations on
** the /timeline page.
*/
void sitemap_timeline_page(void){
  int isPopup = 0;         /* This is an XMLHttpRequest() for /sitemap */

  login_check_credentials();

  if( P("popup")!=0 && cgi_csrf_safe(0) ){
    /* If this is a POST from the same origin with the popup=1 parameter,
    ** then disable anti-robot defenses */
    isPopup = 1;
    g.perm.Hyperlink = 1;
    g.javascriptHyperlink = 0;
  }







|













>







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
  if( g.perm.Read && g.perm.Hyperlink ){
    @ <li>%z(href("%R/timewarps"))Timeline of timewarps</a></li>
  }
  @ <li>%z(href("%R/cookies"))Content of display preference cookie</a></li>
  @ <li>%z(href("%R/test-captcha"))Random ASCII-art Captcha image</a></li>
  @ <li>%z(href("%R/test-piechart"))Pie-Chart generator test</a></li>
  if( !isPopup ){
    style_finish_page();
  }
}

/*
** WEBPAGE: sitemap-timeline
**
** Generate a list of hyperlinks to various (obscure) variations on
** the /timeline page.
*/
void sitemap_timeline_page(void){
  int isPopup = 0;         /* This is an XMLHttpRequest() for /sitemap */

  login_check_credentials();
  style_set_current_feature("sitemap");
  if( P("popup")!=0 && cgi_csrf_safe(0) ){
    /* If this is a POST from the same origin with the popup=1 parameter,
    ** then disable anti-robot defenses */
    isPopup = 1;
    g.perm.Hyperlink = 1;
    g.javascriptHyperlink = 0;
  }
311
312
313
314
315
316
317
318
319
320
  @ <li>%z(href("%R/timeline?forks"))Forks</a></li>
  @ <li>%z(href("%R/timeline?cherrypicks"))Cherrypick merges</a></li>
  @ <li>%z(href("%R/timewarps"))Timewarps</a></li>
  @ <li>%z(href("%R/timeline?ubg"))Color-coded by user</a></li>
  @ <li>%z(href("%R/timeline?deltabg"))Delta vs. baseline manifests</a></li>
  @ </ul>
  if( !isPopup ){
    style_finish_page("sitemap");
  }
}







|


316
317
318
319
320
321
322
323
324
325
  @ <li>%z(href("%R/timeline?forks"))Forks</a></li>
  @ <li>%z(href("%R/timeline?cherrypicks"))Cherrypick merges</a></li>
  @ <li>%z(href("%R/timewarps"))Timewarps</a></li>
  @ <li>%z(href("%R/timeline?ubg"))Color-coded by user</a></li>
  @ <li>%z(href("%R/timeline?deltabg"))Delta vs. baseline manifests</a></li>
  @ </ul>
  if( !isPopup ){
    style_finish_page();
  }
}

Changes to src/skins.c.

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
  int ex = 0;
  if( P("rename")==0 ) return 0;
  zOldName = P("sn");
  zNewName = P("newname");
  if( zOldName==0 ) return 0;
  if( zNewName==0 || zNewName[0]==0 || (ex = skinExists(zNewName))!=0 ){
    if( zNewName==0 ) zNewName = zOldName;

    style_header("Rename A Skin");
    if( ex ){
      @ <p><span class="generalError">There is already another skin
      @ named "%h(zNewName)".  Choose a different name.</span></p>
    }
    @ <form action="%R/setup_skin_admin" method="post"><div>
    @ <table border="0"><tr>
    @ <tr><td align="right">Current name:<td align="left"><b>%h(zOldName)</b>
    @ <tr><td align="right">New name:<td align="left">
    @ <input type="text" size="35" name="newname" value="%h(zNewName)">
    @ <tr><td><td>
    @ <input type="hidden" name="sn" value="%h(zOldName)">
    @ <input type="submit" name="rename" value="Rename">
    @ <input type="submit" name="canren" value="Cancel">
    @ </table>
    login_insert_csrf_secret();
    @ </div></form>
    style_finish_page("skins");
    return 1;
  }
  db_unprotect(PROTECT_CONFIG);
  db_multi_exec(
    "UPDATE config SET name='skin:%q' WHERE name='skin:%q';",
    zNewName, zOldName
  );







>

















|







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
  int ex = 0;
  if( P("rename")==0 ) return 0;
  zOldName = P("sn");
  zNewName = P("newname");
  if( zOldName==0 ) return 0;
  if( zNewName==0 || zNewName[0]==0 || (ex = skinExists(zNewName))!=0 ){
    if( zNewName==0 ) zNewName = zOldName;
    style_set_current_feature("skins");
    style_header("Rename A Skin");
    if( ex ){
      @ <p><span class="generalError">There is already another skin
      @ named "%h(zNewName)".  Choose a different name.</span></p>
    }
    @ <form action="%R/setup_skin_admin" method="post"><div>
    @ <table border="0"><tr>
    @ <tr><td align="right">Current name:<td align="left"><b>%h(zOldName)</b>
    @ <tr><td align="right">New name:<td align="left">
    @ <input type="text" size="35" name="newname" value="%h(zNewName)">
    @ <tr><td><td>
    @ <input type="hidden" name="sn" value="%h(zOldName)">
    @ <input type="submit" name="rename" value="Rename">
    @ <input type="submit" name="canren" value="Cancel">
    @ </table>
    login_insert_csrf_secret();
    @ </div></form>
    style_finish_page();
    return 1;
  }
  db_unprotect(PROTECT_CONFIG);
  db_multi_exec(
    "UPDATE config SET name='skin:%q' WHERE name='skin:%q';",
    zNewName, zOldName
  );
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
  int ex = 0;
  if( P("save")==0 ) return 0;
  zNewName = P("svname");
  if( zNewName && zNewName[0]!=0 ){
  }
  if( zNewName==0 || zNewName[0]==0 || (ex = skinExists(zNewName))!=0 ){
    if( zNewName==0 ) zNewName = "";

    style_header("Save Current Skin");
    if( ex ){
      @ <p><span class="generalError">There is already another skin
      @ named "%h(zNewName)".  Choose a different name.</span></p>
    }
    @ <form action="%R/setup_skin_admin" method="post"><div>
    @ <table border="0"><tr>
    @ <tr><td align="right">Name for this skin:<td align="left">
    @ <input type="text" size="35" name="svname" value="%h(zNewName)">
    @ <tr><td><td>
    @ <input type="submit" name="save" value="Save">
    @ <input type="submit" name="cansave" value="Cancel">
    @ </table>
    login_insert_csrf_secret();
    @ </div></form>
    style_finish_page("skins");
    return 1;
  }
  db_unprotect(PROTECT_CONFIG);
  db_multi_exec(
    "INSERT OR IGNORE INTO config(name, value, mtime)"
    "VALUES('skin:%q',%Q,now())",
    zNewName, zCurrent







>















|







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
  int ex = 0;
  if( P("save")==0 ) return 0;
  zNewName = P("svname");
  if( zNewName && zNewName[0]!=0 ){
  }
  if( zNewName==0 || zNewName[0]==0 || (ex = skinExists(zNewName))!=0 ){
    if( zNewName==0 ) zNewName = "";
    style_set_current_feature("skins");
    style_header("Save Current Skin");
    if( ex ){
      @ <p><span class="generalError">There is already another skin
      @ named "%h(zNewName)".  Choose a different name.</span></p>
    }
    @ <form action="%R/setup_skin_admin" method="post"><div>
    @ <table border="0"><tr>
    @ <tr><td align="right">Name for this skin:<td align="left">
    @ <input type="text" size="35" name="svname" value="%h(zNewName)">
    @ <tr><td><td>
    @ <input type="submit" name="save" value="Save">
    @ <input type="submit" name="cansave" value="Cancel">
    @ </table>
    login_insert_csrf_secret();
    @ </div></form>
    style_finish_page();
    return 1;
  }
  db_unprotect(PROTECT_CONFIG);
  db_multi_exec(
    "INSERT OR IGNORE INTO config(name, value, mtime)"
    "VALUES('skin:%q',%Q,now())",
    zNewName, zCurrent
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
  }
  db_begin_transaction();
  zCurrent = getSkin(0);
  for(i=0; i<count(aBuiltinSkin); i++){
    aBuiltinSkin[i].zSQL = getSkin(aBuiltinSkin[i].zLabel);
  }



  if( cgi_csrf_safe(1) ){
    /* Process requests to delete a user-defined skin */
    if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){
      style_header("Confirm Custom Skin Delete");
      @ <form action="%R/setup_skin_admin" method="post"><div>
      @ <p>Deletion of a custom skin is a permanent action that cannot
      @ be undone.  Please confirm that this is what you want to do:</p>
      @ <input type="hidden" name="sn" value="%h(P("sn"))" />
      @ <input type="submit" name="del2" value="Confirm - Delete The Skin" />
      @ <input type="submit" name="cancel" value="Cancel - Do Not Delete" />
      login_insert_csrf_secret();
      @ </div></form>
      style_finish_page("skins");
      db_end_transaction(1);
      return;
    }
    if( P("del2")!=0 && (zName = skinVarName(P("sn"), 1))!=0 ){
      db_unprotect(PROTECT_CONFIG);
      db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
      db_protect_pop();







>
>












|







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
  }
  db_begin_transaction();
  zCurrent = getSkin(0);
  for(i=0; i<count(aBuiltinSkin); i++){
    aBuiltinSkin[i].zSQL = getSkin(aBuiltinSkin[i].zLabel);
  }

  style_set_current_feature("skins");

  if( cgi_csrf_safe(1) ){
    /* Process requests to delete a user-defined skin */
    if( P("del1") && (zName = skinVarName(P("sn"), 1))!=0 ){
      style_header("Confirm Custom Skin Delete");
      @ <form action="%R/setup_skin_admin" method="post"><div>
      @ <p>Deletion of a custom skin is a permanent action that cannot
      @ be undone.  Please confirm that this is what you want to do:</p>
      @ <input type="hidden" name="sn" value="%h(P("sn"))" />
      @ <input type="submit" name="del2" value="Confirm - Delete The Skin" />
      @ <input type="submit" name="cancel" value="Cancel - Do Not Delete" />
      login_insert_csrf_secret();
      @ </div></form>
      style_finish_page();
      db_end_transaction(1);
      return;
    }
    if( P("del2")!=0 && (zName = skinVarName(P("sn"), 1))!=0 ){
      db_unprotect(PROTECT_CONFIG);
      db_multi_exec("DELETE FROM config WHERE name=%Q", zName);
      db_protect_pop();
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
    @ <input type="submit" name="draftdel" value="Delete">
    @ <input type="hidden" name="name" value="%h(zN)">
    @ </form></tr>
  }
  db_finalize(&q);

  @ </table>
  style_finish_page("skins");
  db_end_transaction(0);
}

/*
** Generate HTML for a <select> that lists all the available skin names,
** except for zExcept if zExcept!=NULL.
*/







|







650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
    @ <input type="submit" name="draftdel" value="Delete">
    @ <input type="hidden" name="name" value="%h(zN)">
    @ </form></tr>
  }
  db_finalize(&q);

  @ </table>
  style_finish_page();
  db_end_transaction(0);
}

/*
** Generate HTML for a <select> that lists all the available skin names,
** except for zExcept if zExcept!=NULL.
*/
794
795
796
797
798
799
800

801
802
803
804
805
806
807
  zContent = PD(zFile,zOrig);
  if( P("revert")!=0 && cgi_csrf_safe(0) ){
    zContent = zDflt;
    isRevert = 1;
  }

  db_begin_transaction();

  style_header("%s", zTitle);
  for(j=0; j<count(aSkinAttr); j++){
    style_submenu_element(aSkinAttr[j].zSubmenu,
          "%R/setup_skinedit?w=%d&basis=%h&sk=%d",j,zBasis,iSkin);
  }
  @ <form action="%R/setup_skinedit" method="post"><div>
  login_insert_csrf_secret();







>







798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
  zContent = PD(zFile,zOrig);
  if( P("revert")!=0 && cgi_csrf_safe(0) ){
    zContent = zDflt;
    isRevert = 1;
  }

  db_begin_transaction();
  style_set_current_feature("skins");
  style_header("%s", zTitle);
  for(j=0; j<count(aSkinAttr); j++){
    style_submenu_element(aSkinAttr[j].zSubmenu,
          "%R/setup_skinedit?w=%d&basis=%h&sk=%d",j,zBasis,iSkin);
  }
  @ <form action="%R/setup_skinedit" method="post"><div>
  login_insert_csrf_secret();
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
      @ </pre>
    }
    blob_reset(&from);
    blob_reset(&to);
    blob_reset(&out);
  }
  @ </div></form>
  style_finish_page("skins");
  db_end_transaction(0);
}

/*
** Try to initialize draft skin iSkin to the built-in or preexisting
** skin named by zTemplate.
*/







|







848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
      @ </pre>
    }
    blob_reset(&from);
    blob_reset(&to);
    blob_reset(&out);
  }
  @ </div></form>
  style_finish_page();
  db_end_transaction(0);
}

/*
** Try to initialize draft skin iSkin to the built-in or preexisting
** skin named by zTemplate.
*/
960
961
962
963
964
965
966

967
968
969
970
971
972
973
  }

  /* Publish the draft skin */
  if( P("pub7")!=0 && PB("pub7ck1") && PB("pub7ck2") ){
    skin_publish(iSkin);
  }


  style_header("Customize Skin");

  @ <p>Customize the look of this Fossil repository by making changes
  @ to the CSS, Header, Footer, and Detail Settings in one of nine "draft"
  @ configurations.  Then, after verifying that all is working correctly,
  @ publish the draft to become the new main Skin.<p>
  @







>







965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
  }

  /* Publish the draft skin */
  if( P("pub7")!=0 && PB("pub7ck1") && PB("pub7ck2") ){
    skin_publish(iSkin);
  }

  style_set_current_feature("skins");
  style_header("Customize Skin");

  @ <p>Customize the look of this Fossil repository by making changes
  @ to the CSS, Header, Footer, and Detail Settings in one of nine "draft"
  @ configurations.  Then, after verifying that all is working correctly,
  @ publish the draft to become the new main Skin.<p>
  @
1121
1122
1123
1124
1125
1126
1127
1128
1129
    @ <p>Administrators can optionally save or restore legacy skins, and/or
    @ undo a prior publish.
  }else{
    @ <p>Visit the <a href='%R/setup_skin_admin'>Skin Admin</a> page
    @ for cleanup and recovery actions.
  }
  builtin_request_js("skin.js");
  style_finish_page("skins");
}







|

1127
1128
1129
1130
1131
1132
1133
1134
1135
    @ <p>Administrators can optionally save or restore legacy skins, and/or
    @ undo a prior publish.
  }else{
    @ <p>Visit the <a href='%R/setup_skin_admin'>Skin Admin</a> page
    @ for cleanup and recovery actions.
  }
  builtin_request_js("skin.js");
  style_finish_page();
}

Changes to src/smtp.c.

767
768
769
770
771
772
773

774
775
776
777
778
779
780
  Stmt q;
  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }
  db_begin_transaction();

  style_header("Email Server Setup");
  if( db_table_exists("repository","emailroute") ){
    style_submenu_element("emailblob table", "%R/emailblob");
    style_submenu_element("emailoutq table", "%R/emailoutq");
    db_prepare(&q, "SELECT eaddr, epolicy FROM emailroute ORDER BY 1");
  }else{
    db_prepare(&q, "SELECT null, null WHERE false");







>







767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
  Stmt q;
  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }
  db_begin_transaction();
  style_set_current_feature("smtp");
  style_header("Email Server Setup");
  if( db_table_exists("repository","emailroute") ){
    style_submenu_element("emailblob table", "%R/emailblob");
    style_submenu_element("emailoutq table", "%R/emailoutq");
    db_prepare(&q, "SELECT eaddr, epolicy FROM emailroute ORDER BY 1");
  }else{
    db_prepare(&q, "SELECT null, null WHERE false");
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
  @ <tr>
  @   <td colspan="3">
  @   <form method="POST" action="%R/setup_smtp_route">
  @   <input type="submit" value="New">
  @    &larr; Add a new email address
  @   </form>
  @ </table>
  style_finish_page("smtp");
  db_end_transaction(0);
}

/*
** WEBPAGE: setup_smtp_route
**
** Edit a single entry in the emailroute table.







|







804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
  @ <tr>
  @   <td colspan="3">
  @   <form method="POST" action="%R/setup_smtp_route">
  @   <input type="submit" value="New">
  @    &larr; Add a new email address
  @   </form>
  @ </table>
  style_finish_page();
  db_end_transaction(0);
}

/*
** WEBPAGE: setup_smtp_route
**
** Edit a single entry in the emailroute table.
831
832
833
834
835
836
837

838
839
840
841
842
843
844
  char *zErr = 0;
  int iErr = 0;
  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }

  style_header("Email Route Editor");

  if( P("edit") && cgi_csrf_safe(1) && zEAddr!=0 && zEPolicy!=0 ){
    smtp_server_schema(0);
    if( (zOAddr==0 || fossil_strcmp(zEAddr,zOAddr)!=0) ){
      /* New or changed email address */
      if( db_exists("SELECT 1 FROM emailroute WHERE eaddr=%Q",zEAddr) ){







>







832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
  char *zErr = 0;
  int iErr = 0;
  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }
  style_set_current_feature("smtp");
  style_header("Email Route Editor");

  if( P("edit") && cgi_csrf_safe(1) && zEAddr!=0 && zEPolicy!=0 ){
    smtp_server_schema(0);
    if( (zOAddr==0 || fossil_strcmp(zEAddr,zOAddr)!=0) ){
      /* New or changed email address */
      if( db_exists("SELECT 1 FROM emailroute WHERE eaddr=%Q",zEAddr) ){
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
  @ <li><p><b>mbox</b> <i>login-name</i>
  @ <p>Store the message in the local mailbox for the user
  @ with USER.LOGIN=<i>login-name</i>.
  @ </ul>
  @
  @ <p>To delete a route &rarr; erase all text from the "Routing" field then
  @ press the "Apply" button.
  style_finish_page("smtp");
}

#if LOCAL_INTERFACE
/*
** State information for the server
*/
struct SmtpServer {







|







924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
  @ <li><p><b>mbox</b> <i>login-name</i>
  @ <p>Store the message in the local mailbox for the user
  @ with USER.LOGIN=<i>login-name</i>.
  @ </ul>
  @
  @ <p>To delete a route &rarr; erase all text from the "Routing" field then
  @ press the "Apply" button.
  style_finish_page();
}

#if LOCAL_INTERFACE
/*
** State information for the server
*/
struct SmtpServer {

Changes to src/sounds/README.md.

1
2
3
4
5
6

7
8
9
10
11
12
13
The *.wav files in this directory contain a human voice speaking each
of the 16 hexadecimal digits.  If a captcha string consists of just
hexadecimal digits (as is the case for captchas generated by the
[../captcha.c module](../captcha.c)) then these WAV files can be
concatenated together to generate an audio reading of the captcha, which
enables visually impaired users to complete the captcha.


Each of the WAV files uses 8000 samples per second, 8 bits per sample
and are 6000 samples in length.

The recordings are made by Philip Bennefall and are of his own voice.
Mr. Bennefall is himself blind and uses this system implemented with these
recordings to complete captchas for Fossil.
|
|
|
|
|
|
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
The `[0-9a-f].wav` files in this directory contain a human voice
speaking each of the 16 hexadecimal digits.  If a captcha string
consists of just hexadecimal digits (as is the case for captchas
generated by the [captcha.c module](/finfo/src/captcha.c)) then these WAV
files can be concatenated together to generate an audio reading of the
captcha, which enables visually impaired users to complete the
captcha.

Each of the WAV files uses 8000 samples per second, 8 bits per sample
and are 6000 samples in length.

The recordings are made by Philip Bennefall and are of his own voice.
Mr. Bennefall is himself blind and uses this system implemented with these
recordings to complete captchas for Fossil.

Changes to src/sqlite3.c.

1
2
3
4
5
6
7
8
9
10
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
** version 3.34.0.  By combining all the individual C code files into this
** single large file, the entire code can be compiled as a single translation
** unit.  This allows many compilers to do optimizations that would not be
** possible if the files were compiled separately.  Performance improvements
** of 5% or more are commonly seen when SQLite is compiled as a single
** translation unit.
**
** This file is all you need to compile SQLite.  To use SQLite in other


|







1
2
3
4
5
6
7
8
9
10
/******************************************************************************
** This file is an amalgamation of many separate C source files from SQLite
** version 3.35.0.  By combining all the individual C code files into this
** single large file, the entire code can be compiled as a single translation
** unit.  This allows many compilers to do optimizations that would not be
** possible if the files were compiled separately.  Performance improvements
** of 5% or more are commonly seen when SQLite is compiled as a single
** translation unit.
**
** This file is all you need to compile SQLite.  To use SQLite in other
279
280
281
282
283
284
285



286
287
288
289
290
291
292
  "ENABLE_JSON1",
#endif
#if SQLITE_ENABLE_LOAD_EXTENSION
  "ENABLE_LOAD_EXTENSION",
#endif
#ifdef SQLITE_ENABLE_LOCKING_STYLE
  "ENABLE_LOCKING_STYLE=" CTIMEOPT_VAL(SQLITE_ENABLE_LOCKING_STYLE),



#endif
#if SQLITE_ENABLE_MEMORY_MANAGEMENT
  "ENABLE_MEMORY_MANAGEMENT",
#endif
#if SQLITE_ENABLE_MEMSYS3
  "ENABLE_MEMSYS3",
#endif







>
>
>







279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
  "ENABLE_JSON1",
#endif
#if SQLITE_ENABLE_LOAD_EXTENSION
  "ENABLE_LOAD_EXTENSION",
#endif
#ifdef SQLITE_ENABLE_LOCKING_STYLE
  "ENABLE_LOCKING_STYLE=" CTIMEOPT_VAL(SQLITE_ENABLE_LOCKING_STYLE),
#endif
#if SQLITE_ENABLE_MATH_FUNCTIONS
  "ENABLE_MATH_FUNCTIONS",
#endif
#if SQLITE_ENABLE_MEMORY_MANAGEMENT
  "ENABLE_MEMORY_MANAGEMENT",
#endif
#if SQLITE_ENABLE_MEMSYS3
  "ENABLE_MEMSYS3",
#endif
986
987
988
989
990
991
992












993
994
995
996
997
998
999
#endif
#if defined(_MSC_VER) && !defined(SQLITE_DISABLE_INTRINSIC)
# define MSVC_VERSION _MSC_VER
#else
# define MSVC_VERSION 0
#endif













/* Needed for various definitions... */
#if defined(__GNUC__) && !defined(_GNU_SOURCE)
# define _GNU_SOURCE
#endif

#if defined(__OpenBSD__) && !defined(_BSD_SOURCE)
# define _BSD_SOURCE







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







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
#endif
#if defined(_MSC_VER) && !defined(SQLITE_DISABLE_INTRINSIC)
# define MSVC_VERSION _MSC_VER
#else
# define MSVC_VERSION 0
#endif

/*
** Some C99 functions in "math.h" are only present for MSVC when its version
** is associated with Visual Studio 2013 or higher.
*/
#ifndef SQLITE_HAVE_C99_MATH_FUNCS
# if MSVC_VERSION==0 || MSVC_VERSION>=1800
#  define SQLITE_HAVE_C99_MATH_FUNCS (1)
# else
#  define SQLITE_HAVE_C99_MATH_FUNCS (0)
# endif
#endif

/* Needed for various definitions... */
#if defined(__GNUC__) && !defined(_GNU_SOURCE)
# define _GNU_SOURCE
#endif

#if defined(__OpenBSD__) && !defined(_BSD_SOURCE)
# define _BSD_SOURCE
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
** been edited in any way since it was last checked in, then the last
** four hexadecimal digits of the hash may be modified.
**
** See also: [sqlite3_libversion()],
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION        "3.34.0"
#define SQLITE_VERSION_NUMBER 3034000
#define SQLITE_SOURCE_ID      "2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b"

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







|
|
|







1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
** been edited in any way since it was last checked in, then the last
** four hexadecimal digits of the hash may be modified.
**
** See also: [sqlite3_libversion()],
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION        "3.35.0"
#define SQLITE_VERSION_NUMBER 3035000
#define SQLITE_SOURCE_ID      "2021-01-18 12:35:16 c1862abb44873f06ec0d772469d8a2d128ae4670b1e98c2d97b0e2da18df9a04"

/*
** 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
4543
4544
4545
4546
4547
4548
4549

4550
4551
4552
4553
4554
4555
4556
**          Regardless of whether or not shared-cache mode is enabled by
**          default, use a private cache.
** <tr><td> file:/home/fred/data.db?vfs=unix-dotfile <td>
**          Open file "/home/fred/data.db". Use the special VFS "unix-dotfile"
**          that uses dot-files in place of posix advisory locking.
** <tr><td> file:data.db?mode=readonly <td>
**          An error. "readonly" is not a valid option for the "mode" parameter.

** </table>
**
** ^URI hexadecimal escape sequences (%HH) are supported within the path and
** query components of a URI. A hexadecimal escape sequence consists of a
** percent sign - "%" - followed by exactly two hexadecimal digits
** specifying an octet value. ^Before the path or query components of a
** URI filename are interpreted, they are encoded using UTF-8 and all







>







4558
4559
4560
4561
4562
4563
4564
4565
4566
4567
4568
4569
4570
4571
4572
**          Regardless of whether or not shared-cache mode is enabled by
**          default, use a private cache.
** <tr><td> file:/home/fred/data.db?vfs=unix-dotfile <td>
**          Open file "/home/fred/data.db". Use the special VFS "unix-dotfile"
**          that uses dot-files in place of posix advisory locking.
** <tr><td> file:data.db?mode=readonly <td>
**          An error. "readonly" is not a valid option for the "mode" parameter.
**          Use "ro" instead:  "file:data.db?mode=ro".
** </table>
**
** ^URI hexadecimal escape sequences (%HH) are supported within the path and
** query components of a URI. A hexadecimal escape sequence consists of a
** percent sign - "%" - followed by exactly two hexadecimal digits
** specifying an octet value. ^Before the path or query components of a
** URI filename are interpreted, they are encoded using UTF-8 and all
4741
4742
4743
4744
4745
4746
4747
4748
4749
4750
4751
4752
4753
4754
4755
** The sqlite3_free_filename(Y) routine releases a memory allocation
** previously obtained from sqlite3_create_filename().  Invoking
** 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,
** then the corresponding [sqlite3_module.xClose() method should also be
** invoked prior to calling sqlite3_free_filename(Y).
*/
SQLITE_API char *sqlite3_create_filename(
  const char *zDatabase,







|







4757
4758
4759
4760
4761
4762
4763
4764
4765
4766
4767
4768
4769
4770
4771
** The sqlite3_free_filename(Y) routine releases a memory allocation
** previously obtained from sqlite3_create_filename().  Invoking
** 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 not 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,
** then the corresponding [sqlite3_module.xClose() method should also be
** invoked prior to calling sqlite3_free_filename(Y).
*/
SQLITE_API char *sqlite3_create_filename(
  const char *zDatabase,
8809
8810
8811
8812
8813
8814
8815

8816
8817
8818
8819
8820
8821
8822
8823
#define SQLITE_TESTCTRL_SORTER_MMAP             24
#define SQLITE_TESTCTRL_IMPOSTER                25
#define SQLITE_TESTCTRL_PARSER_COVERAGE         26
#define SQLITE_TESTCTRL_RESULT_INTREAL          27
#define SQLITE_TESTCTRL_PRNG_SEED               28
#define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS     29
#define SQLITE_TESTCTRL_SEEK_COUNT              30

#define SQLITE_TESTCTRL_LAST                    30  /* Largest TESTCTRL */

/*
** CAPI3REF: SQL Keyword Checking
**
** These routines provide access to the set of SQL language keywords
** recognized by SQLite.  Applications can uses these routines to determine
** whether or not a specific identifier needs to be escaped (for example,







>
|







8825
8826
8827
8828
8829
8830
8831
8832
8833
8834
8835
8836
8837
8838
8839
8840
#define SQLITE_TESTCTRL_SORTER_MMAP             24
#define SQLITE_TESTCTRL_IMPOSTER                25
#define SQLITE_TESTCTRL_PARSER_COVERAGE         26
#define SQLITE_TESTCTRL_RESULT_INTREAL          27
#define SQLITE_TESTCTRL_PRNG_SEED               28
#define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS     29
#define SQLITE_TESTCTRL_SEEK_COUNT              30
#define SQLITE_TESTCTRL_TRACEFLAGS              31
#define SQLITE_TESTCTRL_LAST                    31  /* Largest TESTCTRL */

/*
** CAPI3REF: SQL Keyword Checking
**
** These routines provide access to the set of SQL language keywords
** recognized by SQLite.  Applications can uses these routines to determine
** whether or not a specific identifier needs to be escaped (for example,
11482
11483
11484
11485
11486
11487
11488








11489
11490
11491
11492
11493
11494
11495
** an attached table is modified and then later on the original values
** are restored. However, if this function returns non-zero, then it is
** guaranteed that a call to sqlite3session_changeset() will return a
** changeset containing zero changes.
*/
SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession);









/*
** CAPI3REF: Create An Iterator To Traverse A Changeset
** CONSTRUCTOR: sqlite3_changeset_iter
**
** Create an iterator used to iterate through the contents of a changeset.
** If successful, *pp is set to point to the iterator handle and SQLITE_OK
** is returned. Otherwise, if an error occurs, *pp is set to zero and an







>
>
>
>
>
>
>
>







11499
11500
11501
11502
11503
11504
11505
11506
11507
11508
11509
11510
11511
11512
11513
11514
11515
11516
11517
11518
11519
11520
** an attached table is modified and then later on the original values
** are restored. However, if this function returns non-zero, then it is
** guaranteed that a call to sqlite3session_changeset() will return a
** changeset containing zero changes.
*/
SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession);

/*
** CAPI3REF: Query for the amount of heap memory used by a session object.
**
** This API returns the total amount of heap memory in bytes currently
** used by the session object passed as the only argument.
*/
SQLITE_API sqlite3_int64 sqlite3session_memory_used(sqlite3_session *pSession);

/*
** CAPI3REF: Create An Iterator To Traverse A Changeset
** CONSTRUCTOR: sqlite3_changeset_iter
**
** Create an iterator used to iterate through the contents of a changeset.
** If successful, *pp is set to point to the iterator handle and SQLITE_OK
** is returned. Otherwise, if an error occurs, *pp is set to zero and an
13524
13525
13526
13527
13528
13529
13530
13531

13532
13533
13534
13535
13536
13537
13538
/*
** WAL mode depends on atomic aligned 32-bit loads and stores in a few
** places.  The following macros try to make this explicit.
*/
#ifndef __has_extension
# define __has_extension(x) 0     /* compatibility with non-clang compilers */
#endif
#if GCC_VERSION>=4007000 || __has_extension(c_atomic)

# define AtomicLoad(PTR)       __atomic_load_n((PTR),__ATOMIC_RELAXED)
# define AtomicStore(PTR,VAL)  __atomic_store_n((PTR),(VAL),__ATOMIC_RELAXED)
#else
# define AtomicLoad(PTR)       (*(PTR))
# define AtomicStore(PTR,VAL)  (*(PTR) = (VAL))
#endif








|
>







13549
13550
13551
13552
13553
13554
13555
13556
13557
13558
13559
13560
13561
13562
13563
13564
/*
** WAL mode depends on atomic aligned 32-bit loads and stores in a few
** places.  The following macros try to make this explicit.
*/
#ifndef __has_extension
# define __has_extension(x) 0     /* compatibility with non-clang compilers */
#endif
#if GCC_VERSION>=4007000 || \
    (__has_extension(c_atomic) && __has_extension(c_atomic_store_n))
# define AtomicLoad(PTR)       __atomic_load_n((PTR),__ATOMIC_RELAXED)
# define AtomicStore(PTR,VAL)  __atomic_store_n((PTR),(VAL),__ATOMIC_RELAXED)
#else
# define AtomicLoad(PTR)       (*(PTR))
# define AtomicStore(PTR,VAL)  (*(PTR) = (VAL))
#endif

14590
14591
14592
14593
14594
14595
14596
14597
14598
14599
14600
14601
14602

14603
14604
14605
14606
14607
14608
14609
14610
14611













14612
14613
14614
14615
14616
14617
14618
# define SQLITE_DEFAULT_MMAP_SIZE SQLITE_MAX_MMAP_SIZE
#endif

/*
** SELECTTRACE_ENABLED will be either 1 or 0 depending on whether or not
** the Select query generator tracing logic is turned on.
*/
#if defined(SQLITE_ENABLE_SELECTTRACE)
# define SELECTTRACE_ENABLED 1
#else
# define SELECTTRACE_ENABLED 0
#endif
#if defined(SQLITE_ENABLE_SELECTTRACE)

# define SELECTTRACE_ENABLED 1
# define SELECTTRACE(K,P,S,X)  \
  if(sqlite3_unsupported_selecttrace&(K))   \
    sqlite3DebugPrintf("%u/%d/%p: ",(S)->selId,(P)->addrExplain,(S)),\
    sqlite3DebugPrintf X
#else
# define SELECTTRACE(K,P,S,X)
# define SELECTTRACE_ENABLED 0
#endif














/*
** An instance of the following structure is used to store the busy-handler
** callback for a given sqlite handle.
**
** The sqlite.busyHandler member of the sqlite struct contains the busy
** callback for the database handle. Each pager opened via the sqlite







|
<
|
<

|
>


|






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







14616
14617
14618
14619
14620
14621
14622
14623

14624

14625
14626
14627
14628
14629
14630
14631
14632
14633
14634
14635
14636
14637
14638
14639
14640
14641
14642
14643
14644
14645
14646
14647
14648
14649
14650
14651
14652
14653
14654
14655
14656
# define SQLITE_DEFAULT_MMAP_SIZE SQLITE_MAX_MMAP_SIZE
#endif

/*
** SELECTTRACE_ENABLED will be either 1 or 0 depending on whether or not
** the Select query generator tracing logic is turned on.
*/
#if !defined(SQLITE_AMALGAMATION)

SQLITE_PRIVATE u32 sqlite3SelectTrace;

#endif
#if defined(SQLITE_DEBUG) \
    && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_SELECTTRACE))
# define SELECTTRACE_ENABLED 1
# define SELECTTRACE(K,P,S,X)  \
  if(sqlite3SelectTrace&(K))   \
    sqlite3DebugPrintf("%u/%d/%p: ",(S)->selId,(P)->addrExplain,(S)),\
    sqlite3DebugPrintf X
#else
# define SELECTTRACE(K,P,S,X)
# define SELECTTRACE_ENABLED 0
#endif

/*
** Macros for "wheretrace"
*/
SQLITE_PRIVATE u32 sqlite3WhereTrace;
#if defined(SQLITE_DEBUG) \
    && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE))
# define WHERETRACE(K,X)  if(sqlite3WhereTrace&(K)) sqlite3DebugPrintf X
# define WHERETRACE_ENABLED 1
#else
# define WHERETRACE(K,X)
#endif


/*
** An instance of the following structure is used to store the busy-handler
** callback for a given sqlite handle.
**
** The sqlite.busyHandler member of the sqlite struct contains the busy
** callback for the database handle. Each pager opened via the sqlite
14735
14736
14737
14738
14739
14740
14741

14742
14743
14744
14745
14746
14747
14748
typedef struct KeyClass KeyClass;
typedef struct KeyInfo KeyInfo;
typedef struct Lookaside Lookaside;
typedef struct LookasideSlot LookasideSlot;
typedef struct Module Module;
typedef struct NameContext NameContext;
typedef struct Parse Parse;

typedef struct PreUpdate PreUpdate;
typedef struct PrintfArguments PrintfArguments;
typedef struct RenameToken RenameToken;
typedef struct RowSet RowSet;
typedef struct Savepoint Savepoint;
typedef struct Select Select;
typedef struct SQLiteThread SQLiteThread;







>







14773
14774
14775
14776
14777
14778
14779
14780
14781
14782
14783
14784
14785
14786
14787
typedef struct KeyClass KeyClass;
typedef struct KeyInfo KeyInfo;
typedef struct Lookaside Lookaside;
typedef struct LookasideSlot LookasideSlot;
typedef struct Module Module;
typedef struct NameContext NameContext;
typedef struct Parse Parse;
typedef struct ParseCleanup ParseCleanup;
typedef struct PreUpdate PreUpdate;
typedef struct PrintfArguments PrintfArguments;
typedef struct RenameToken RenameToken;
typedef struct RowSet RowSet;
typedef struct Savepoint Savepoint;
typedef struct Select Select;
typedef struct SQLiteThread SQLiteThread;
15314
15315
15316
15317
15318
15319
15320

15321
15322
15323
15324
15325
15326
15327
SQLITE_PRIVATE int sqlite3BtreeCursorRestore(BtCursor*, int*);
SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor*, u8 flags);

/* Allowed flags for sqlite3BtreeDelete() and sqlite3BtreeInsert() */
#define BTREE_SAVEPOSITION 0x02  /* Leave cursor pointing at NEXT or PREV */
#define BTREE_AUXDELETE    0x04  /* not the primary delete operation */
#define BTREE_APPEND       0x08  /* Insert is likely an append */


/* An instance of the BtreePayload object describes the content of a single
** entry in either an index or table btree.
**
** Index btrees (used for indexes and also WITHOUT ROWID tables) contain
** an arbitrary key and no data.  These btrees have pKey,nKey set to the
** key and the pData,nData,nZero fields are uninitialized.  The aMem,nMem







>







15353
15354
15355
15356
15357
15358
15359
15360
15361
15362
15363
15364
15365
15366
15367
SQLITE_PRIVATE int sqlite3BtreeCursorRestore(BtCursor*, int*);
SQLITE_PRIVATE int sqlite3BtreeDelete(BtCursor*, u8 flags);

/* Allowed flags for sqlite3BtreeDelete() and sqlite3BtreeInsert() */
#define BTREE_SAVEPOSITION 0x02  /* Leave cursor pointing at NEXT or PREV */
#define BTREE_AUXDELETE    0x04  /* not the primary delete operation */
#define BTREE_APPEND       0x08  /* Insert is likely an append */
#define BTREE_PREFORMAT    0x80  /* Insert is likely an append */

/* An instance of the BtreePayload object describes the content of a single
** entry in either an index or table btree.
**
** Index btrees (used for indexes and also WITHOUT ROWID tables) contain
** an arbitrary key and no data.  These btrees have pKey,nKey set to the
** key and the pData,nData,nZero fields are uninitialized.  The aMem,nMem
15412
15413
15414
15415
15416
15417
15418


15419
15420
15421
15422
15423
15424
15425
SQLITE_PRIVATE int sqlite3BtreeCursorInfo(BtCursor*, int*, int);
SQLITE_PRIVATE void sqlite3BtreeCursorList(Btree*);
#endif

#ifndef SQLITE_OMIT_WAL
SQLITE_PRIVATE   int sqlite3BtreeCheckpoint(Btree*, int, int *, int *);
#endif



/*
** If we are not using shared cache, then there is no need to
** use mutexes to access the BtShared structures.  So make the
** Enter and Leave procedures no-ops.
*/
#ifndef SQLITE_OMIT_SHARED_CACHE







>
>







15452
15453
15454
15455
15456
15457
15458
15459
15460
15461
15462
15463
15464
15465
15466
15467
SQLITE_PRIVATE int sqlite3BtreeCursorInfo(BtCursor*, int*, int);
SQLITE_PRIVATE void sqlite3BtreeCursorList(Btree*);
#endif

#ifndef SQLITE_OMIT_WAL
SQLITE_PRIVATE   int sqlite3BtreeCheckpoint(Btree*, int, int *, int *);
#endif

SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor*, BtCursor*, i64);

/*
** If we are not using shared cache, then there is no need to
** use mutexes to access the BtShared structures.  So make the
** Enter and Leave procedures no-ops.
*/
#ifndef SQLITE_OMIT_SHARED_CACHE
15755
15756
15757
15758
15759
15760
15761

15762
15763
15764
15765
15766
15767
15768
15769
15770
15771
15772
15773
15774
15775
15776
15777
15778
15779
15780
15781
15782
15783
15784
15785
15786
15787
15788
15789

15790
15791
15792
15793
15794
15795
15796
15797
15798
15799
15800
15801
15802
15803
15804
15805
15806
15807
15808
15809
15810
15811
15812
15813
15814
15815
15816
15817
15818
15819
15820
15821
15822
#define OP_Close         116
#define OP_ColumnsUsed   117
#define OP_SeekScan      118 /* synopsis: Scan-ahead up to P1 rows         */
#define OP_SeekHit       119 /* synopsis: set P2<=seekHit<=P3              */
#define OP_Sequence      120 /* synopsis: r[P2]=cursor[P1].ctr++           */
#define OP_NewRowid      121 /* synopsis: r[P2]=rowid                      */
#define OP_Insert        122 /* synopsis: intkey=r[P3] data=r[P2]          */

#define OP_Delete        123
#define OP_ResetCount    124
#define OP_SorterCompare 125 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */
#define OP_SorterData    126 /* synopsis: r[P2]=data                       */
#define OP_RowData       127 /* synopsis: r[P2]=data                       */
#define OP_Rowid         128 /* synopsis: r[P2]=rowid                      */
#define OP_NullRow       129
#define OP_SeekEnd       130
#define OP_IdxInsert     131 /* synopsis: key=r[P2]                        */
#define OP_SorterInsert  132 /* synopsis: key=r[P2]                        */
#define OP_IdxDelete     133 /* synopsis: key=r[P2@P3]                     */
#define OP_DeferredSeek  134 /* synopsis: Move P3 to P1.rowid if needed    */
#define OP_IdxRowid      135 /* synopsis: r[P2]=rowid                      */
#define OP_FinishSeek    136
#define OP_Destroy       137
#define OP_Clear         138
#define OP_ResetSorter   139
#define OP_CreateBtree   140 /* synopsis: r[P2]=root iDb=P1 flags=P3       */
#define OP_SqlExec       141
#define OP_ParseSchema   142
#define OP_LoadAnalysis  143
#define OP_DropTable     144
#define OP_DropIndex     145
#define OP_DropTrigger   146
#define OP_IntegrityCk   147
#define OP_RowSetAdd     148 /* synopsis: rowset(P1)=r[P2]                 */
#define OP_Param         149
#define OP_Real          150 /* same as TK_FLOAT, synopsis: r[P2]=P4       */

#define OP_FkCounter     151 /* synopsis: fkctr[P1]+=P2                    */
#define OP_MemMax        152 /* synopsis: r[P1]=max(r[P1],r[P2])           */
#define OP_OffsetLimit   153 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */
#define OP_AggInverse    154 /* synopsis: accum=r[P3] inverse(r[P2@P5])    */
#define OP_AggStep       155 /* synopsis: accum=r[P3] step(r[P2@P5])       */
#define OP_AggStep1      156 /* synopsis: accum=r[P3] step(r[P2@P5])       */
#define OP_AggValue      157 /* synopsis: r[P3]=value N=P2                 */
#define OP_AggFinal      158 /* synopsis: accum=r[P1] N=P2                 */
#define OP_Expire        159
#define OP_CursorLock    160
#define OP_CursorUnlock  161
#define OP_TableLock     162 /* synopsis: iDb=P1 root=P2 write=P3          */
#define OP_VBegin        163
#define OP_VCreate       164
#define OP_VDestroy      165
#define OP_VOpen         166
#define OP_VColumn       167 /* synopsis: r[P3]=vcolumn(P2)                */
#define OP_VRename       168
#define OP_Pagecount     169
#define OP_MaxPgcnt      170
#define OP_Trace         171
#define OP_CursorHint    172
#define OP_ReleaseReg    173 /* synopsis: release r[P1@P2] mask P3         */
#define OP_Noop          174
#define OP_Explain       175
#define OP_Abortable     176

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







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

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







15797
15798
15799
15800
15801
15802
15803
15804
15805
15806
15807
15808
15809
15810
15811
15812
15813
15814
15815
15816
15817
15818
15819
15820
15821
15822
15823
15824
15825
15826
15827
15828
15829
15830

15831
15832
15833
15834
15835
15836
15837
15838
15839
15840
15841
15842
15843
15844
15845
15846
15847
15848
15849
15850
15851
15852
15853
15854
15855
15856
15857
15858
15859
15860
15861
15862
15863
15864
15865
#define OP_Close         116
#define OP_ColumnsUsed   117
#define OP_SeekScan      118 /* synopsis: Scan-ahead up to P1 rows         */
#define OP_SeekHit       119 /* synopsis: set P2<=seekHit<=P3              */
#define OP_Sequence      120 /* synopsis: r[P2]=cursor[P1].ctr++           */
#define OP_NewRowid      121 /* synopsis: r[P2]=rowid                      */
#define OP_Insert        122 /* synopsis: intkey=r[P3] data=r[P2]          */
#define OP_RowCell       123
#define OP_Delete        124
#define OP_ResetCount    125
#define OP_SorterCompare 126 /* synopsis: if key(P1)!=trim(r[P3],P4) goto P2 */
#define OP_SorterData    127 /* synopsis: r[P2]=data                       */
#define OP_RowData       128 /* synopsis: r[P2]=data                       */
#define OP_Rowid         129 /* synopsis: r[P2]=rowid                      */
#define OP_NullRow       130
#define OP_SeekEnd       131
#define OP_IdxInsert     132 /* synopsis: key=r[P2]                        */
#define OP_SorterInsert  133 /* synopsis: key=r[P2]                        */
#define OP_IdxDelete     134 /* synopsis: key=r[P2@P3]                     */
#define OP_DeferredSeek  135 /* synopsis: Move P3 to P1.rowid if needed    */
#define OP_IdxRowid      136 /* synopsis: r[P2]=rowid                      */
#define OP_FinishSeek    137
#define OP_Destroy       138
#define OP_Clear         139
#define OP_ResetSorter   140
#define OP_CreateBtree   141 /* synopsis: r[P2]=root iDb=P1 flags=P3       */
#define OP_SqlExec       142
#define OP_ParseSchema   143
#define OP_LoadAnalysis  144
#define OP_DropTable     145
#define OP_DropIndex     146
#define OP_DropTrigger   147
#define OP_IntegrityCk   148
#define OP_RowSetAdd     149 /* synopsis: rowset(P1)=r[P2]                 */

#define OP_Real          150 /* same as TK_FLOAT, synopsis: r[P2]=P4       */
#define OP_Param         151
#define OP_FkCounter     152 /* synopsis: fkctr[P1]+=P2                    */
#define OP_MemMax        153 /* synopsis: r[P1]=max(r[P1],r[P2])           */
#define OP_OffsetLimit   154 /* synopsis: if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1) */
#define OP_AggInverse    155 /* synopsis: accum=r[P3] inverse(r[P2@P5])    */
#define OP_AggStep       156 /* synopsis: accum=r[P3] step(r[P2@P5])       */
#define OP_AggStep1      157 /* synopsis: accum=r[P3] step(r[P2@P5])       */
#define OP_AggValue      158 /* synopsis: r[P3]=value N=P2                 */
#define OP_AggFinal      159 /* synopsis: accum=r[P1] N=P2                 */
#define OP_Expire        160
#define OP_CursorLock    161
#define OP_CursorUnlock  162
#define OP_TableLock     163 /* synopsis: iDb=P1 root=P2 write=P3          */
#define OP_VBegin        164
#define OP_VCreate       165
#define OP_VDestroy      166
#define OP_VOpen         167
#define OP_VColumn       168 /* synopsis: r[P3]=vcolumn(P2)                */
#define OP_VRename       169
#define OP_Pagecount     170
#define OP_MaxPgcnt      171
#define OP_Trace         172
#define OP_CursorHint    173
#define OP_ReleaseReg    174 /* synopsis: release r[P1@P2] mask P3         */
#define OP_Noop          175
#define OP_Explain       176
#define OP_Abortable     177

/* Properties such as "out2" or "jump" that are specified in
** comments following the "case" for each opcode in the vdbe.c
** are encoded into bitvectors as follows:
*/
#define OPFLG_JUMP        0x01  /* jump:  P2 holds jmp target */
#define OPFLG_IN1         0x02  /* in1:   P1 is an input */
15837
15838
15839
15840
15841
15842
15843
15844
15845
15846
15847
15848
15849
15850
15851
15852
15853
15854
15855
15856
15857
/*  72 */ 0x10, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10,\
/*  80 */ 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x12,\
/*  88 */ 0x20, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00,\
/*  96 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x26, 0x26,\
/* 104 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x00,\
/* 112 */ 0x12, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\
/* 120 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
/* 128 */ 0x10, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00, 0x10,\
/* 136 */ 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,\
/* 144 */ 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x10, 0x00,\
/* 152 */ 0x04, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
/* 160 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
/* 168 */ 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00,\
/* 176 */ 0x00,}

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







|
|
|
|

|
|







15880
15881
15882
15883
15884
15885
15886
15887
15888
15889
15890
15891
15892
15893
15894
15895
15896
15897
15898
15899
15900
/*  72 */ 0x10, 0x00, 0x10, 0x10, 0x00, 0x00, 0x10, 0x10,\
/*  80 */ 0x00, 0x00, 0x02, 0x02, 0x02, 0x00, 0x00, 0x12,\
/*  88 */ 0x20, 0x00, 0x00, 0x00, 0x10, 0x10, 0x00, 0x00,\
/*  96 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x26, 0x26,\
/* 104 */ 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x26, 0x00,\
/* 112 */ 0x12, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00,\
/* 120 */ 0x10, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
/* 128 */ 0x00, 0x10, 0x00, 0x00, 0x04, 0x04, 0x00, 0x00,\
/* 136 */ 0x10, 0x00, 0x10, 0x00, 0x00, 0x10, 0x00, 0x00,\
/* 144 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, 0x10,\
/* 152 */ 0x00, 0x04, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00,\
/* 160 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\
/* 168 */ 0x00, 0x00, 0x10, 0x10, 0x00, 0x00, 0x00, 0x00,\
/* 176 */ 0x00, 0x00,}

/* The sqlite3P2Values() routine is able to run faster if it knows
** the value of the largest JUMP opcode.  The smaller the maximum
** JUMP opcode the better, so the mkopcodeh.tcl script that
** generated this include file strives to group all JUMP opcodes
** together near the beginning of the list.
*/
16903
16904
16905
16906
16907
16908
16909
16910
16911
16912
16913
16914
16915
16916
16917
  i64 lastRowid;                /* ROWID of most recent insert (see above) */
  i64 szMmap;                   /* Default mmap_size setting */
  u32 nSchemaLock;              /* Do not reset the schema when non-zero */
  unsigned int openFlags;       /* Flags passed to sqlite3_vfs.xOpen() */
  int errCode;                  /* Most recent error code (SQLITE_*) */
  int errMask;                  /* & result codes with this before returning */
  int iSysErrno;                /* Errno value from last system error */
  u16 dbOptFlags;               /* Flags to enable/disable optimizations */
  u8 enc;                       /* Text encoding */
  u8 autoCommit;                /* The auto-commit flag. */
  u8 temp_store;                /* 1: file 2: memory 0: default */
  u8 mallocFailed;              /* True if we have seen a malloc failure */
  u8 bBenignMalloc;             /* Do not require OOMs if true */
  u8 dfltLockMode;              /* Default locking-mode for attached dbs */
  signed char nextAutovac;      /* Autovac setting after VACUUM if >=0 */







|







16946
16947
16948
16949
16950
16951
16952
16953
16954
16955
16956
16957
16958
16959
16960
  i64 lastRowid;                /* ROWID of most recent insert (see above) */
  i64 szMmap;                   /* Default mmap_size setting */
  u32 nSchemaLock;              /* Do not reset the schema when non-zero */
  unsigned int openFlags;       /* Flags passed to sqlite3_vfs.xOpen() */
  int errCode;                  /* Most recent error code (SQLITE_*) */
  int errMask;                  /* & result codes with this before returning */
  int iSysErrno;                /* Errno value from last system error */
  u32 dbOptFlags;               /* Flags to enable/disable optimizations */
  u8 enc;                       /* Text encoding */
  u8 autoCommit;                /* The auto-commit flag. */
  u8 temp_store;                /* 1: file 2: memory 0: default */
  u8 mallocFailed;              /* True if we have seen a malloc failure */
  u8 bBenignMalloc;             /* Do not require OOMs if true */
  u8 dfltLockMode;              /* Default locking-mode for attached dbs */
  signed char nextAutovac;      /* Autovac setting after VACUUM if >=0 */
17110
17111
17112
17113
17114
17115
17116
17117
17118
17119
17120
17121
17122
17123
17124
17125
17126
17127
17128
17129
17130
17131
17132
17133


17134
17135
17136
17137
17138
17139
17140
17141
#define DBFLAG_EncodingFixed  0x0040  /* No longer possible to change enc. */

/*
** Bits of the sqlite3.dbOptFlags field that are used by the
** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface to
** selectively disable various optimizations.
*/
#define SQLITE_QueryFlattener 0x0001   /* Query flattening */
#define SQLITE_WindowFunc     0x0002   /* Use xInverse for window functions */
#define SQLITE_GroupByOrder   0x0004   /* GROUPBY cover of ORDERBY */
#define SQLITE_FactorOutConst 0x0008   /* Constant factoring */
#define SQLITE_DistinctOpt    0x0010   /* DISTINCT using indexes */
#define SQLITE_CoverIdxScan   0x0020   /* Covering index scans */
#define SQLITE_OrderByIdxJoin 0x0040   /* ORDER BY of joins via index */
#define SQLITE_Transitive     0x0080   /* Transitive constraints */
#define SQLITE_OmitNoopJoin   0x0100   /* Omit unused tables in joins */
#define SQLITE_CountOfView    0x0200   /* The count-of-view optimization */
#define SQLITE_CursorHints    0x0400   /* Add OP_CursorHint opcodes */
#define SQLITE_Stat4          0x0800   /* Use STAT4 data */
   /* TH3 expects the Stat4   ^^^^^^ value to be 0x0800.  Don't change it */
#define SQLITE_PushDown       0x1000   /* The push-down optimization */
#define SQLITE_SimplifyJoin   0x2000   /* Convert LEFT JOIN to JOIN */
#define SQLITE_SkipScan       0x4000   /* Skip-scans */
#define SQLITE_PropagateConst 0x8000   /* The constant propagation opt */


#define SQLITE_AllOpts        0xffff   /* All optimizations */

/*
** Macros for testing whether or not optimizations are enabled or disabled.
*/
#define OptimizationDisabled(db, mask)  (((db)->dbOptFlags&(mask))!=0)
#define OptimizationEnabled(db, mask)   (((db)->dbOptFlags&(mask))==0)








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







17153
17154
17155
17156
17157
17158
17159
17160
17161
17162
17163
17164
17165
17166
17167
17168
17169
17170
17171
17172
17173
17174
17175
17176
17177
17178
17179
17180
17181
17182
17183
17184
17185
17186
#define DBFLAG_EncodingFixed  0x0040  /* No longer possible to change enc. */

/*
** Bits of the sqlite3.dbOptFlags field that are used by the
** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface to
** selectively disable various optimizations.
*/
#define SQLITE_QueryFlattener 0x00000001 /* Query flattening */
#define SQLITE_WindowFunc     0x00000002 /* Use xInverse for window functions */
#define SQLITE_GroupByOrder   0x00000004 /* GROUPBY cover of ORDERBY */
#define SQLITE_FactorOutConst 0x00000008 /* Constant factoring */
#define SQLITE_DistinctOpt    0x00000010 /* DISTINCT using indexes */
#define SQLITE_CoverIdxScan   0x00000020 /* Covering index scans */
#define SQLITE_OrderByIdxJoin 0x00000040 /* ORDER BY of joins via index */
#define SQLITE_Transitive     0x00000080 /* Transitive constraints */
#define SQLITE_OmitNoopJoin   0x00000100 /* Omit unused tables in joins */
#define SQLITE_CountOfView    0x00000200 /* The count-of-view optimization */
#define SQLITE_CursorHints    0x00000400 /* Add OP_CursorHint opcodes */
#define SQLITE_Stat4          0x00000800 /* Use STAT4 data */
   /* TH3 expects this value  ^^^^^^^^^^ to be 0x0000800. Don't change it */
#define SQLITE_PushDown       0x00001000 /* The push-down optimization */
#define SQLITE_SimplifyJoin   0x00002000 /* Convert LEFT JOIN to JOIN */
#define SQLITE_SkipScan       0x00004000 /* Skip-scans */
#define SQLITE_PropagateConst 0x00008000 /* The constant propagation opt */
#define SQLITE_MinMaxOpt      0x00010000 /* The min/max optimization */
#define SQLITE_ExistsToIN     0x00020000 /* The EXISTS-to-IN optimization */
#define SQLITE_AllOpts        0xffffffff /* All optimizations */

/*
** Macros for testing whether or not optimizations are enabled or disabled.
*/
#define OptimizationDisabled(db, mask)  (((db)->dbOptFlags&(mask))!=0)
#define OptimizationEnabled(db, mask)   (((db)->dbOptFlags&(mask))==0)

17283
17284
17285
17286
17287
17288
17289



17290
17291
17292
17293
17294
17295
17296
**   DFUNCTION(zName, nArg, iArg, bNC, xFunc)
**     Like FUNCTION except it omits the SQLITE_FUNC_CONSTANT flag and
**     adds the SQLITE_FUNC_SLOCHNG flag.  Used for date & time functions
**     and functions like sqlite_version() that can change, but not during
**     a single query.  The iArg is ignored.  The user-data is always set
**     to a NULL pointer.  The bNC parameter is not used.
**



**   PURE_DATE(zName, nArg, iArg, bNC, xFunc)
**     Used for "pure" date/time functions, this macro is like DFUNCTION
**     except that it does set the SQLITE_FUNC_CONSTANT flags.  iArg is
**     ignored and the user-data for these functions is set to an
**     arbitrary non-NULL pointer.  The bNC parameter is not used.
**
**   AGGREGATE(zName, nArg, iArg, bNC, xStep, xFinal)







>
>
>







17328
17329
17330
17331
17332
17333
17334
17335
17336
17337
17338
17339
17340
17341
17342
17343
17344
**   DFUNCTION(zName, nArg, iArg, bNC, xFunc)
**     Like FUNCTION except it omits the SQLITE_FUNC_CONSTANT flag and
**     adds the SQLITE_FUNC_SLOCHNG flag.  Used for date & time functions
**     and functions like sqlite_version() that can change, but not during
**     a single query.  The iArg is ignored.  The user-data is always set
**     to a NULL pointer.  The bNC parameter is not used.
**
**   MFUNCTION(zName, nArg, xPtr, xFunc)
**     For math-library functions.  xPtr is an arbitrary pointer.
**
**   PURE_DATE(zName, nArg, iArg, bNC, xFunc)
**     Used for "pure" date/time functions, this macro is like DFUNCTION
**     except that it does set the SQLITE_FUNC_CONSTANT flags.  iArg is
**     ignored and the user-data for these functions is set to an
**     arbitrary non-NULL pointer.  The bNC parameter is not used.
**
**   AGGREGATE(zName, nArg, iArg, bNC, xStep, xFinal)
17318
17319
17320
17321
17322
17323
17324



17325
17326
17327
17328
17329
17330
17331
   SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
#define VFUNCTION(zName, nArg, iArg, bNC, xFunc) \
  {nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
   SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
#define SFUNCTION(zName, nArg, iArg, bNC, xFunc) \
  {nArg, SQLITE_UTF8|SQLITE_DIRECTONLY|SQLITE_FUNC_UNSAFE, \
   SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }



#define INLINE_FUNC(zName, nArg, iArg, mFlags) \
  {nArg, SQLITE_UTF8|SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \
   SQLITE_INT_TO_PTR(iArg), 0, noopFunc, 0, 0, 0, #zName, {0} }
#define TEST_FUNC(zName, nArg, iArg, mFlags) \
  {nArg, SQLITE_UTF8|SQLITE_FUNC_INTERNAL|SQLITE_FUNC_TEST| \
         SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \
   SQLITE_INT_TO_PTR(iArg), 0, noopFunc, 0, 0, 0, #zName, {0} }







>
>
>







17366
17367
17368
17369
17370
17371
17372
17373
17374
17375
17376
17377
17378
17379
17380
17381
17382
   SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
#define VFUNCTION(zName, nArg, iArg, bNC, xFunc) \
  {nArg, SQLITE_UTF8|(bNC*SQLITE_FUNC_NEEDCOLL), \
   SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
#define SFUNCTION(zName, nArg, iArg, bNC, xFunc) \
  {nArg, SQLITE_UTF8|SQLITE_DIRECTONLY|SQLITE_FUNC_UNSAFE, \
   SQLITE_INT_TO_PTR(iArg), 0, xFunc, 0, 0, 0, #zName, {0} }
#define MFUNCTION(zName, nArg, xPtr, xFunc) \
  {nArg, SQLITE_FUNC_CONSTANT|SQLITE_UTF8, \
   xPtr, 0, xFunc, 0, 0, 0, #zName, {0} }
#define INLINE_FUNC(zName, nArg, iArg, mFlags) \
  {nArg, SQLITE_UTF8|SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \
   SQLITE_INT_TO_PTR(iArg), 0, noopFunc, 0, 0, 0, #zName, {0} }
#define TEST_FUNC(zName, nArg, iArg, mFlags) \
  {nArg, SQLITE_UTF8|SQLITE_FUNC_INTERNAL|SQLITE_FUNC_TEST| \
         SQLITE_FUNC_INLINE|SQLITE_FUNC_CONSTANT|(mFlags), \
   SQLITE_INT_TO_PTR(iArg), 0, noopFunc, 0, 0, 0, #zName, {0} }
17588
17589
17590
17591
17592
17593
17594
17595
17596
17597
17598
17599
17600
17601
17602
#ifndef SQLITE_OMIT_VIRTUALTABLE
  int nModuleArg;      /* Number of arguments to the module */
  char **azModuleArg;  /* 0: module 1: schema 2: vtab name 3...: args */
  VTable *pVTable;     /* List of VTable objects. */
#endif
  Trigger *pTrigger;   /* List of triggers stored in pSchema */
  Schema *pSchema;     /* Schema that contains this table */
  Table *pNextZombie;  /* Next on the Parse.pZombieTab list */
};

/*
** Allowed values for Table.tabFlags.
**
** TF_OOOHidden applies to tables or view that have hidden columns that are
** followed by non-hidden columns.  Example:  "CREATE VIRTUAL TABLE x USING







<







17639
17640
17641
17642
17643
17644
17645

17646
17647
17648
17649
17650
17651
17652
#ifndef SQLITE_OMIT_VIRTUALTABLE
  int nModuleArg;      /* Number of arguments to the module */
  char **azModuleArg;  /* 0: module 1: schema 2: vtab name 3...: args */
  VTable *pVTable;     /* List of VTable objects. */
#endif
  Trigger *pTrigger;   /* List of triggers stored in pSchema */
  Schema *pSchema;     /* Schema that contains this table */

};

/*
** Allowed values for Table.tabFlags.
**
** TF_OOOHidden applies to tables or view that have hidden columns that are
** followed by non-hidden columns.  Example:  "CREATE VIRTUAL TABLE x USING
17717
17718
17719
17720
17721
17722
17723


17724
17725
17726
17727

17728
17729
17730
17731



17732
17733
17734
17735
17736
17737
17738
17739
17740
** the operation in progress stops and returns an error code.  But prior
** changes due to the same operation are not backed out and no rollback
** occurs.  IGNORE means that the particular row that caused the constraint
** error is not inserted or updated.  Processing continues and no error
** is returned.  REPLACE means that preexisting database rows that caused
** a UNIQUE constraint violation are removed so that the new insert or
** update can proceed.  Processing continues and no error is reported.


**
** RESTRICT, SETNULL, and CASCADE actions apply only to foreign keys.
** RESTRICT is the same as ABORT for IMMEDIATE foreign keys and the
** same as ROLLBACK for DEFERRED keys.  SETNULL means that the foreign

** key is set to NULL.  CASCADE means that a DELETE or UPDATE of the
** referenced table row is propagated into the row that holds the
** foreign key.
**



** The following symbolic values are used to record which type
** of action to take.
*/
#define OE_None     0   /* There is no constraint to check */
#define OE_Rollback 1   /* Fail the operation and rollback the transaction */
#define OE_Abort    2   /* Back out changes but do no rollback transaction */
#define OE_Fail     3   /* Stop the operation but leave all prior changes */
#define OE_Ignore   4   /* Ignore the error. Do not do the INSERT or UPDATE */
#define OE_Replace  5   /* Delete existing record, then do INSERT or UPDATE */







>
>

|


>
|



>
>
>

|







17767
17768
17769
17770
17771
17772
17773
17774
17775
17776
17777
17778
17779
17780
17781
17782
17783
17784
17785
17786
17787
17788
17789
17790
17791
17792
17793
17794
17795
17796
** the operation in progress stops and returns an error code.  But prior
** changes due to the same operation are not backed out and no rollback
** occurs.  IGNORE means that the particular row that caused the constraint
** error is not inserted or updated.  Processing continues and no error
** is returned.  REPLACE means that preexisting database rows that caused
** a UNIQUE constraint violation are removed so that the new insert or
** update can proceed.  Processing continues and no error is reported.
** UPDATE applies to insert operations only and means that the insert
** is omitted and the DO UPDATE clause of an upsert is run instead.
**
** RESTRICT, SETNULL, SETDFLT, and CASCADE actions apply only to foreign keys.
** RESTRICT is the same as ABORT for IMMEDIATE foreign keys and the
** same as ROLLBACK for DEFERRED keys.  SETNULL means that the foreign
** key is set to NULL.  SETDFLT means that the foreign key is set
** to its default value.  CASCADE means that a DELETE or UPDATE of the
** referenced table row is propagated into the row that holds the
** foreign key.
**
** The OE_Default value is a place holder that means to use whatever
** conflict resolution algorthm is required from context.
**
** The following symbolic values are used to record which type
** of conflict resolution action to take.
*/
#define OE_None     0   /* There is no constraint to check */
#define OE_Rollback 1   /* Fail the operation and rollback the transaction */
#define OE_Abort    2   /* Back out changes but do no rollback transaction */
#define OE_Fail     3   /* Stop the operation but leave all prior changes */
#define OE_Ignore   4   /* Ignore the error. Do not do the INSERT or UPDATE */
#define OE_Replace  5   /* Delete existing record, then do INSERT or UPDATE */
18112
18113
18114
18115
18116
18117
18118
18119
18120
18121
18122
18123
18124
18125
18126
                         ** TK_IN: ephemerial table holding RHS
                         ** TK_SELECT_COLUMN: Number of columns on the LHS
                         ** TK_SELECT: 1st register of result vector */
  ynVar iColumn;         /* TK_COLUMN: column index.  -1 for rowid.
                         ** TK_VARIABLE: variable number (always >= 1).
                         ** TK_SELECT_COLUMN: column of the result vector */
  i16 iAgg;              /* Which entry in pAggInfo->aCol[] or ->aFunc[] */
  i16 iRightJoinTable;   /* If EP_FromJoin, the right table of the join */
  AggInfo *pAggInfo;     /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
  union {
    Table *pTab;           /* TK_COLUMN: Table containing column. Can be NULL
                           ** for a column of an index on an expression */
    Window *pWin;          /* EP_WinFunc: Window/Filter defn for a function */
    struct {               /* TK_IN, TK_SELECT, and TK_EXISTS */
      int iAddr;             /* Subroutine entry address */







|







18168
18169
18170
18171
18172
18173
18174
18175
18176
18177
18178
18179
18180
18181
18182
                         ** TK_IN: ephemerial table holding RHS
                         ** TK_SELECT_COLUMN: Number of columns on the LHS
                         ** TK_SELECT: 1st register of result vector */
  ynVar iColumn;         /* TK_COLUMN: column index.  -1 for rowid.
                         ** TK_VARIABLE: variable number (always >= 1).
                         ** TK_SELECT_COLUMN: column of the result vector */
  i16 iAgg;              /* Which entry in pAggInfo->aCol[] or ->aFunc[] */
  int iRightJoinTable;   /* If EP_FromJoin, the right table of the join */
  AggInfo *pAggInfo;     /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */
  union {
    Table *pTab;           /* TK_COLUMN: Table containing column. Can be NULL
                           ** for a column of an index on an expression */
    Window *pWin;          /* EP_WinFunc: Window/Filter defn for a function */
    struct {               /* TK_IN, TK_SELECT, and TK_EXISTS */
      int iAddr;             /* Subroutine entry address */
18480
18481
18482
18483
18484
18485
18486
18487
18488
18489
18490


18491



18492
18493
18494


18495
18496
18497
18498
18499
18500
18501
18502
**
** pUpsertSet is the list of column=expr terms of the UPDATE statement.
** The pUpsertSet field is NULL for a ON CONFLICT DO NOTHING.  The
** pUpsertWhere is the WHERE clause for the UPDATE and is NULL if the
** WHERE clause is omitted.
*/
struct Upsert {
  ExprList *pUpsertTarget;  /* Optional description of conflicting index */
  Expr *pUpsertTargetWhere; /* WHERE clause for partial index targets */
  ExprList *pUpsertSet;     /* The SET clause from an ON CONFLICT UPDATE */
  Expr *pUpsertWhere;       /* WHERE clause for the ON CONFLICT UPDATE */


  /* The fields above comprise the parse tree for the upsert clause.



  ** The fields below are used to transfer information from the INSERT
  ** processing down into the UPDATE processing while generating code.
  ** Upsert owns the memory allocated above, but not the memory below. */


  Index *pUpsertIdx;        /* Constraint that pUpsertTarget identifies */
  SrcList *pUpsertSrc;      /* Table to be updated */
  int regData;              /* First register holding array of VALUES */
  int iDataCur;             /* Index of the data cursor */
  int iIdxCur;              /* Index of the first index cursor */
};

/*







|



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







18536
18537
18538
18539
18540
18541
18542
18543
18544
18545
18546
18547
18548
18549
18550
18551
18552
18553
18554

18555
18556
18557
18558
18559
18560
18561
18562
18563
18564
**
** pUpsertSet is the list of column=expr terms of the UPDATE statement.
** The pUpsertSet field is NULL for a ON CONFLICT DO NOTHING.  The
** pUpsertWhere is the WHERE clause for the UPDATE and is NULL if the
** WHERE clause is omitted.
*/
struct Upsert {
  ExprList *pUpsertTarget;  /* Optional description of conflict target */
  Expr *pUpsertTargetWhere; /* WHERE clause for partial index targets */
  ExprList *pUpsertSet;     /* The SET clause from an ON CONFLICT UPDATE */
  Expr *pUpsertWhere;       /* WHERE clause for the ON CONFLICT UPDATE */
  Upsert *pNextUpsert;      /* Next ON CONFLICT clause in the list */
  u8 isDoUpdate;            /* True for DO UPDATE.  False for DO NOTHING */
  /* Above this point is the parse tree for the ON CONFLICT clauses.
  ** The next group of fields stores intermediate data. */
  void *pToFree;            /* Free memory when deleting the Upsert object */
  /* All fields above are owned by the Upsert object and must be freed
  ** when the Upsert is destroyed.  The fields below are used to transfer
  ** information from the INSERT processing down into the UPDATE processing

  ** while generating code.  The fields below are owned by the INSERT
  ** statement and will be freed by INSERT processing. */
  Index *pUpsertIdx;        /* UNIQUE constraint specified by pUpsertTarget */
  SrcList *pUpsertSrc;      /* Table to be updated */
  int regData;              /* First register holding array of VALUES */
  int iDataCur;             /* Index of the data cursor */
  int iIdxCur;              /* Index of the first index cursor */
};

/*
18738
18739
18740
18741
18742
18743
18744











18745
18746
18747
18748
18749
18750
18751
# define DbMaskTest(M,I)    (((M)&(((yDbMask)1)<<(I)))!=0)
# define DbMaskZero(M)      (M)=0
# define DbMaskSet(M,I)     (M)|=(((yDbMask)1)<<(I))
# define DbMaskAllZero(M)   (M)==0
# define DbMaskNonZero(M)   (M)!=0
#endif












/*
** An SQL parser context.  A copy of this structure is passed through
** the parser and down into all the parser action routine in order to
** carry around information that is global to the entire parse.
**
** The structure is divided into two parts.  When the parser and code
** generate call themselves recursively, the first part of the structure







>
>
>
>
>
>
>
>
>
>
>







18800
18801
18802
18803
18804
18805
18806
18807
18808
18809
18810
18811
18812
18813
18814
18815
18816
18817
18818
18819
18820
18821
18822
18823
18824
# define DbMaskTest(M,I)    (((M)&(((yDbMask)1)<<(I)))!=0)
# define DbMaskZero(M)      (M)=0
# define DbMaskSet(M,I)     (M)|=(((yDbMask)1)<<(I))
# define DbMaskAllZero(M)   (M)==0
# define DbMaskNonZero(M)   (M)!=0
#endif

/*
** An instance of the ParseCleanup object specifies an operation that
** should be performed after parsing to deallocation resources obtained
** during the parse and which are no longer needed.
*/
struct ParseCleanup {
  ParseCleanup *pNext;               /* Next cleanup task */
  void *pPtr;                        /* Pointer to object to deallocate */
  void (*xCleanup)(sqlite3*,void*);  /* Deallocation routine */
};

/*
** An SQL parser context.  A copy of this structure is passed through
** the parser and down into all the parser action routine in order to
** carry around information that is global to the entire parse.
**
** The structure is divided into two parts.  When the parser and code
** generate call themselves recursively, the first part of the structure
18769
18770
18771
18772
18773
18774
18775



18776
18777
18778
18779
18780
18781
18782
  u8 nTempReg;         /* Number of temporary registers in aTempReg[] */
  u8 isMultiWrite;     /* True if statement may modify/insert multiple rows */
  u8 mayAbort;         /* True if statement may throw an ABORT exception */
  u8 hasCompound;      /* Need to invoke convertCompoundSelectToSubquery() */
  u8 okConstFactor;    /* OK to factor out constants */
  u8 disableLookaside; /* Number of times lookaside has been disabled */
  u8 disableVtab;      /* Disable all virtual tables for this parse */



  int nRangeReg;       /* Size of the temporary register block */
  int iRangeReg;       /* First register in temporary register block */
  int nErr;            /* Number of errors seen */
  int nTab;            /* Number of previously allocated VDBE cursors */
  int nMem;            /* Number of memory cells used so far */
  int szOpAlloc;       /* Bytes of memory space allocated for Vdbe.aOp[] */
  int iSelfTab;        /* Table associated with an index on expr, or negative







>
>
>







18842
18843
18844
18845
18846
18847
18848
18849
18850
18851
18852
18853
18854
18855
18856
18857
18858
  u8 nTempReg;         /* Number of temporary registers in aTempReg[] */
  u8 isMultiWrite;     /* True if statement may modify/insert multiple rows */
  u8 mayAbort;         /* True if statement may throw an ABORT exception */
  u8 hasCompound;      /* Need to invoke convertCompoundSelectToSubquery() */
  u8 okConstFactor;    /* OK to factor out constants */
  u8 disableLookaside; /* Number of times lookaside has been disabled */
  u8 disableVtab;      /* Disable all virtual tables for this parse */
#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
  u8 earlyCleanup;     /* OOM inside sqlite3ParserAddCleanup() */
#endif
  int nRangeReg;       /* Size of the temporary register block */
  int iRangeReg;       /* First register in temporary register block */
  int nErr;            /* Number of errors seen */
  int nTab;            /* Number of previously allocated VDBE cursors */
  int nMem;            /* Number of memory cells used so far */
  int szOpAlloc;       /* Bytes of memory space allocated for Vdbe.aOp[] */
  int iSelfTab;        /* Table associated with an index on expr, or negative
18847
18848
18849
18850
18851
18852
18853
18854
18855
18856
18857

18858
18859
18860
18861
18862
18863
18864
                            ** during a RENAME COLUMN */
  Trigger *pNewTrigger;     /* Trigger under construct by a CREATE TRIGGER */
  const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */
#ifndef SQLITE_OMIT_VIRTUALTABLE
  Token sArg;               /* Complete text of a module argument */
  Table **apVtabLock;       /* Pointer to virtual tables needing locking */
#endif
  Table *pZombieTab;        /* List of Table objects to delete after code gen */
  TriggerPrg *pTriggerPrg;  /* Linked list of coded triggers */
  With *pWith;              /* Current WITH clause, or NULL */
  With *pWithToFree;        /* Free this WITH object at the end of the parse */

#ifndef SQLITE_OMIT_ALTERTABLE
  RenameToken *pRename;     /* Tokens subject to renaming by ALTER TABLE */
#endif
};

#define PARSE_MODE_NORMAL        0
#define PARSE_MODE_DECLARE_VTAB  1







<


<
>







18923
18924
18925
18926
18927
18928
18929

18930
18931

18932
18933
18934
18935
18936
18937
18938
18939
                            ** during a RENAME COLUMN */
  Trigger *pNewTrigger;     /* Trigger under construct by a CREATE TRIGGER */
  const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */
#ifndef SQLITE_OMIT_VIRTUALTABLE
  Token sArg;               /* Complete text of a module argument */
  Table **apVtabLock;       /* Pointer to virtual tables needing locking */
#endif

  TriggerPrg *pTriggerPrg;  /* Linked list of coded triggers */
  With *pWith;              /* Current WITH clause, or NULL */

  ParseCleanup *pCleanup;   /* List of cleanup operations to run after parse */
#ifndef SQLITE_OMIT_ALTERTABLE
  RenameToken *pRename;     /* Tokens subject to renaming by ALTER TABLE */
#endif
};

#define PARSE_MODE_NORMAL        0
#define PARSE_MODE_DECLARE_VTAB  1
18930
18931
18932
18933
18934
18935
18936

18937
18938
18939
18940
18941
18942
18943
#define OPFLAG_SEEKEQ        0x02    /* OP_Open** cursor uses EQ seek only */
#define OPFLAG_FORDELETE     0x08    /* OP_Open should use BTREE_FORDELETE */
#define OPFLAG_P2ISREG       0x10    /* P2 to OP_Open** is a register number */
#define OPFLAG_PERMUTE       0x01    /* OP_Compare: use the permutation */
#define OPFLAG_SAVEPOSITION  0x02    /* OP_Delete/Insert: save cursor pos */
#define OPFLAG_AUXDELETE     0x04    /* OP_Delete: index in a DELETE op */
#define OPFLAG_NOCHNG_MAGIC  0x6d    /* OP_MakeRecord: serialtype 10 is ok */


/*
 * Each trigger present in the database schema is stored as an instance of
 * struct Trigger.
 *
 * Pointers to instances of struct Trigger are stored in two ways.
 * 1. In the "trigHash" hash table (part of the sqlite3* that represents the







>







19005
19006
19007
19008
19009
19010
19011
19012
19013
19014
19015
19016
19017
19018
19019
#define OPFLAG_SEEKEQ        0x02    /* OP_Open** cursor uses EQ seek only */
#define OPFLAG_FORDELETE     0x08    /* OP_Open should use BTREE_FORDELETE */
#define OPFLAG_P2ISREG       0x10    /* P2 to OP_Open** is a register number */
#define OPFLAG_PERMUTE       0x01    /* OP_Compare: use the permutation */
#define OPFLAG_SAVEPOSITION  0x02    /* OP_Delete/Insert: save cursor pos */
#define OPFLAG_AUXDELETE     0x04    /* OP_Delete: index in a DELETE op */
#define OPFLAG_NOCHNG_MAGIC  0x6d    /* OP_MakeRecord: serialtype 10 is ok */
#define OPFLAG_PREFORMAT     0x80    /* OP_Insert uses preformatted cell */

/*
 * Each trigger present in the database schema is stored as an instance of
 * struct Trigger.
 *
 * Pointers to instances of struct Trigger are stored in two ways.
 * 1. In the "trigHash" hash table (part of the sqlite3* that represents the
19308
19309
19310
19311
19312
19313
19314
19315
19316
19317
19318
19319
19320
19321
19322
SQLITE_PRIVATE Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*, u8);
SQLITE_PRIVATE void sqlite3WindowAttach(Parse*, Expr*, Window*);
SQLITE_PRIVATE void sqlite3WindowLink(Select *pSel, Window *pWin);
SQLITE_PRIVATE int sqlite3WindowCompare(Parse*, Window*, Window*, int);
SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse*, Select*);
SQLITE_PRIVATE void sqlite3WindowCodeStep(Parse*, Select*, WhereInfo*, int, int);
SQLITE_PRIVATE int sqlite3WindowRewrite(Parse*, Select*);
SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse*, struct SrcList_item*);
SQLITE_PRIVATE void sqlite3WindowUpdate(Parse*, Window*, Window*, FuncDef*);
SQLITE_PRIVATE Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p);
SQLITE_PRIVATE Window *sqlite3WindowListDup(sqlite3 *db, Window *p);
SQLITE_PRIVATE void sqlite3WindowFunctions(void);
SQLITE_PRIVATE void sqlite3WindowChain(Parse*, Window*, Window*);
SQLITE_PRIVATE Window *sqlite3WindowAssemble(Parse*, Window*, ExprList*, ExprList*, Token*);
#else







<







19384
19385
19386
19387
19388
19389
19390

19391
19392
19393
19394
19395
19396
19397
SQLITE_PRIVATE Window *sqlite3WindowAlloc(Parse*, int, int, Expr*, int , Expr*, u8);
SQLITE_PRIVATE void sqlite3WindowAttach(Parse*, Expr*, Window*);
SQLITE_PRIVATE void sqlite3WindowLink(Select *pSel, Window *pWin);
SQLITE_PRIVATE int sqlite3WindowCompare(Parse*, Window*, Window*, int);
SQLITE_PRIVATE void sqlite3WindowCodeInit(Parse*, Select*);
SQLITE_PRIVATE void sqlite3WindowCodeStep(Parse*, Select*, WhereInfo*, int, int);
SQLITE_PRIVATE int sqlite3WindowRewrite(Parse*, Select*);

SQLITE_PRIVATE void sqlite3WindowUpdate(Parse*, Window*, Window*, FuncDef*);
SQLITE_PRIVATE Window *sqlite3WindowDup(sqlite3 *db, Expr *pOwner, Window *p);
SQLITE_PRIVATE Window *sqlite3WindowListDup(sqlite3 *db, Window *p);
SQLITE_PRIVATE void sqlite3WindowFunctions(void);
SQLITE_PRIVATE void sqlite3WindowChain(Parse*, Window*, Window*);
SQLITE_PRIVATE Window *sqlite3WindowAssemble(Parse*, Window*, ExprList*, ExprList*, Token*);
#else
19718
19719
19720
19721
19722
19723
19724

19725
19726
19727
19728
19729
19730
19731
                   Upsert*);
SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,ExprList*,u16,int);
SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo*);
SQLITE_PRIVATE LogEst sqlite3WhereOutputRowCount(WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereIsDistinct(WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereIsOrdered(WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereOrderByLimitOptLabel(WhereInfo*);

SQLITE_PRIVATE int sqlite3WhereIsSorted(WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereContinueLabel(WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereBreakLabel(WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereOkOnePass(WhereInfo*, int*);
#define ONEPASS_OFF      0        /* Use of ONEPASS not allowed */
#define ONEPASS_SINGLE   1        /* ONEPASS valid for a single row update */
#define ONEPASS_MULTI    2        /* ONEPASS is valid for multiple rows */







>







19793
19794
19795
19796
19797
19798
19799
19800
19801
19802
19803
19804
19805
19806
19807
                   Upsert*);
SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(Parse*,SrcList*,Expr*,ExprList*,ExprList*,u16,int);
SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo*);
SQLITE_PRIVATE LogEst sqlite3WhereOutputRowCount(WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereIsDistinct(WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereIsOrdered(WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereOrderByLimitOptLabel(WhereInfo*);
SQLITE_PRIVATE void sqlite3WhereMinMaxOptEarlyOut(Vdbe*,WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereIsSorted(WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereContinueLabel(WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereBreakLabel(WhereInfo*);
SQLITE_PRIVATE int sqlite3WhereOkOnePass(WhereInfo*, int*);
#define ONEPASS_OFF      0        /* Use of ONEPASS not allowed */
#define ONEPASS_SINGLE   1        /* ONEPASS valid for a single row update */
#define ONEPASS_MULTI    2        /* ONEPASS is valid for multiple rows */
19964
19965
19966
19967
19968
19969
19970

19971
19972
19973
19974
19975
19976
19977
SQLITE_PRIVATE int sqlite3IndexAffinityOk(const Expr *pExpr, char idx_affinity);
SQLITE_PRIVATE char sqlite3TableColumnAffinity(Table*,int);
SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr);
SQLITE_PRIVATE int sqlite3Atoi64(const char*, i64*, int, u8);
SQLITE_PRIVATE int sqlite3DecOrHexToI64(const char*, i64*);
SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3*, int, const char*,...);
SQLITE_PRIVATE void sqlite3Error(sqlite3*,int);

SQLITE_PRIVATE void sqlite3SystemError(sqlite3*,int);
SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n);
SQLITE_PRIVATE u8 sqlite3HexToInt(int h);
SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **);

#if defined(SQLITE_NEED_ERR_NAME)
SQLITE_PRIVATE const char *sqlite3ErrName(int);







>







20040
20041
20042
20043
20044
20045
20046
20047
20048
20049
20050
20051
20052
20053
20054
SQLITE_PRIVATE int sqlite3IndexAffinityOk(const Expr *pExpr, char idx_affinity);
SQLITE_PRIVATE char sqlite3TableColumnAffinity(Table*,int);
SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr);
SQLITE_PRIVATE int sqlite3Atoi64(const char*, i64*, int, u8);
SQLITE_PRIVATE int sqlite3DecOrHexToI64(const char*, i64*);
SQLITE_PRIVATE void sqlite3ErrorWithMsg(sqlite3*, int, const char*,...);
SQLITE_PRIVATE void sqlite3Error(sqlite3*,int);
SQLITE_PRIVATE void sqlite3ErrorClear(sqlite3*);
SQLITE_PRIVATE void sqlite3SystemError(sqlite3*,int);
SQLITE_PRIVATE void *sqlite3HexToBlob(sqlite3*, const char *z, int n);
SQLITE_PRIVATE u8 sqlite3HexToInt(int h);
SQLITE_PRIVATE int sqlite3TwoPartName(Parse *, Token *, Token *, Token **);

#if defined(SQLITE_NEED_ERR_NAME)
SQLITE_PRIVATE const char *sqlite3ErrName(int);
20027
20028
20029
20030
20031
20032
20033
20034
20035
20036
20037
20038
20039
20040
20041
20042
20043
20044
20045
20046
20047
20048
20049
20050
20051
20052

20053
20054
20055
20056
20057
20058
20059
#ifndef SQLITE_AMALGAMATION
SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[];
SQLITE_PRIVATE const char sqlite3StrBINARY[];
SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[];
SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[];
SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config;
SQLITE_PRIVATE FuncDefHash sqlite3BuiltinFunctions;
SQLITE_API extern u32 sqlite3_unsupported_selecttrace;
#ifndef SQLITE_OMIT_WSD
SQLITE_PRIVATE int sqlite3PendingByte;
#endif
#endif /* SQLITE_AMALGAMATION */
#ifdef VDBE_PROFILE
SQLITE_PRIVATE sqlite3_uint64 sqlite3NProfileCnt;
#endif
SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3*, int, Pgno, Pgno);
SQLITE_PRIVATE void sqlite3Reindex(Parse*, Token*, Token*);
SQLITE_PRIVATE void sqlite3AlterFunctions(void);
SQLITE_PRIVATE void sqlite3AlterRenameTable(Parse*, SrcList*, Token*);
SQLITE_PRIVATE void sqlite3AlterRenameColumn(Parse*, SrcList*, Token*, Token*);
SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *);
SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...);
SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*, int);
SQLITE_PRIVATE void sqlite3CodeRhsOfIN(Parse*, Expr*, int);
SQLITE_PRIVATE int sqlite3CodeSubselect(Parse*, Expr*);
SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*);

SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p);
SQLITE_PRIVATE int sqlite3MatchEName(
  const struct ExprList_item*,
  const char*,
  const char*,
  const char*
);







<


















>







20104
20105
20106
20107
20108
20109
20110

20111
20112
20113
20114
20115
20116
20117
20118
20119
20120
20121
20122
20123
20124
20125
20126
20127
20128
20129
20130
20131
20132
20133
20134
20135
20136
#ifndef SQLITE_AMALGAMATION
SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[];
SQLITE_PRIVATE const char sqlite3StrBINARY[];
SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[];
SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[];
SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config;
SQLITE_PRIVATE FuncDefHash sqlite3BuiltinFunctions;

#ifndef SQLITE_OMIT_WSD
SQLITE_PRIVATE int sqlite3PendingByte;
#endif
#endif /* SQLITE_AMALGAMATION */
#ifdef VDBE_PROFILE
SQLITE_PRIVATE sqlite3_uint64 sqlite3NProfileCnt;
#endif
SQLITE_PRIVATE void sqlite3RootPageMoved(sqlite3*, int, Pgno, Pgno);
SQLITE_PRIVATE void sqlite3Reindex(Parse*, Token*, Token*);
SQLITE_PRIVATE void sqlite3AlterFunctions(void);
SQLITE_PRIVATE void sqlite3AlterRenameTable(Parse*, SrcList*, Token*);
SQLITE_PRIVATE void sqlite3AlterRenameColumn(Parse*, SrcList*, Token*, Token*);
SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *);
SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...);
SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*, int);
SQLITE_PRIVATE void sqlite3CodeRhsOfIN(Parse*, Expr*, int);
SQLITE_PRIVATE int sqlite3CodeSubselect(Parse*, Expr*);
SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*);
SQLITE_PRIVATE int sqlite3ExpandSubquery(Parse*, struct SrcList_item*);
SQLITE_PRIVATE void sqlite3SelectWrongNumTermsError(Parse *pParse, Select *p);
SQLITE_PRIVATE int sqlite3MatchEName(
  const struct ExprList_item*,
  const char*,
  const char*,
  const char*
);
20216
20217
20218
20219
20220
20221
20222

20223
20224
20225
20226
20227
20228
20229
SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3*, int, const char *);
SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *, VTable *);
SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(sqlite3 *,FuncDef*, int nArg, Expr*);
SQLITE_PRIVATE sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context*);
SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe*, const char*, int);
SQLITE_PRIVATE int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *);
SQLITE_PRIVATE void sqlite3ParserReset(Parse*);

#ifdef SQLITE_ENABLE_NORMALIZE
SQLITE_PRIVATE char *sqlite3Normalize(Vdbe*, const char*);
#endif
SQLITE_PRIVATE int sqlite3Reprepare(Vdbe*);
SQLITE_PRIVATE void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*);
SQLITE_PRIVATE CollSeq *sqlite3ExprCompareCollSeq(Parse*,const Expr*);
SQLITE_PRIVATE CollSeq *sqlite3BinaryCompareCollSeq(Parse *, const Expr*, const Expr*);







>







20293
20294
20295
20296
20297
20298
20299
20300
20301
20302
20303
20304
20305
20306
20307
SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3*, int, const char *);
SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *, VTable *);
SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(sqlite3 *,FuncDef*, int nArg, Expr*);
SQLITE_PRIVATE sqlite3_int64 sqlite3StmtCurrentTime(sqlite3_context*);
SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe*, const char*, int);
SQLITE_PRIVATE int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *);
SQLITE_PRIVATE void sqlite3ParserReset(Parse*);
SQLITE_PRIVATE void sqlite3ParserAddCleanup(Parse*,void(*)(sqlite3*,void*),void*);
#ifdef SQLITE_ENABLE_NORMALIZE
SQLITE_PRIVATE char *sqlite3Normalize(Vdbe*, const char*);
#endif
SQLITE_PRIVATE int sqlite3Reprepare(Vdbe*);
SQLITE_PRIVATE void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*);
SQLITE_PRIVATE CollSeq *sqlite3ExprCompareCollSeq(Parse*,const Expr*);
SQLITE_PRIVATE CollSeq *sqlite3BinaryCompareCollSeq(Parse *, const Expr*, const Expr*);
20238
20239
20240
20241
20242
20243
20244
20245
20246
20247
20248
20249


20250
20251
20252
20253


20254
20255
20256
20257
20258
20259
20260
SQLITE_PRIVATE   void sqlite3WithDelete(sqlite3*,With*);
SQLITE_PRIVATE   void sqlite3WithPush(Parse*, With*, u8);
#else
#define sqlite3WithPush(x,y,z)
#define sqlite3WithDelete(x,y)
#endif
#ifndef SQLITE_OMIT_UPSERT
SQLITE_PRIVATE   Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*);
SQLITE_PRIVATE   void sqlite3UpsertDelete(sqlite3*,Upsert*);
SQLITE_PRIVATE   Upsert *sqlite3UpsertDup(sqlite3*,Upsert*);
SQLITE_PRIVATE   int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*);
SQLITE_PRIVATE   void sqlite3UpsertDoUpdate(Parse*,Upsert*,Table*,Index*,int);


#else
#define sqlite3UpsertNew(v,w,x,y,z) ((Upsert*)0)
#define sqlite3UpsertDelete(x,y)
#define sqlite3UpsertDup(x,y)       ((Upsert*)0)


#endif


/* Declarations for functions in fkey.c. All of these are replaced by
** no-op macros if OMIT_FOREIGN_KEY is defined. In this case no foreign
** key functionality is available. If OMIT_TRIGGER is defined but
** OMIT_FOREIGN_KEY is not, only some of the functions are no-oped. In







|




>
>

|

|
>
>







20316
20317
20318
20319
20320
20321
20322
20323
20324
20325
20326
20327
20328
20329
20330
20331
20332
20333
20334
20335
20336
20337
20338
20339
20340
20341
20342
SQLITE_PRIVATE   void sqlite3WithDelete(sqlite3*,With*);
SQLITE_PRIVATE   void sqlite3WithPush(Parse*, With*, u8);
#else
#define sqlite3WithPush(x,y,z)
#define sqlite3WithDelete(x,y)
#endif
#ifndef SQLITE_OMIT_UPSERT
SQLITE_PRIVATE   Upsert *sqlite3UpsertNew(sqlite3*,ExprList*,Expr*,ExprList*,Expr*,Upsert*);
SQLITE_PRIVATE   void sqlite3UpsertDelete(sqlite3*,Upsert*);
SQLITE_PRIVATE   Upsert *sqlite3UpsertDup(sqlite3*,Upsert*);
SQLITE_PRIVATE   int sqlite3UpsertAnalyzeTarget(Parse*,SrcList*,Upsert*);
SQLITE_PRIVATE   void sqlite3UpsertDoUpdate(Parse*,Upsert*,Table*,Index*,int);
SQLITE_PRIVATE   Upsert *sqlite3UpsertOfIndex(Upsert*,Index*);
SQLITE_PRIVATE   int sqlite3UpsertNextIsIPK(Upsert*);
#else
#define sqlite3UpsertNew(u,v,w,x,y,z) ((Upsert*)0)
#define sqlite3UpsertDelete(x,y)
#define sqlite3UpsertDup(x,y)         ((Upsert*)0)
#define sqlite3UpsertOfIndex(x,y)     ((Upsert*)0)
#define sqlite3UpsertNextIsIPK(x)     0
#endif


/* Declarations for functions in fkey.c. All of these are replaced by
** no-op macros if OMIT_FOREIGN_KEY is defined. In this case no foreign
** key functionality is available. If OMIT_TRIGGER is defined but
** OMIT_FOREIGN_KEY is not, only some of the functions are no-oped. In
20742
20743
20744
20745
20746
20747
20748
20749
20750
20751

20752
20753
20754
20755
20756
20757
20758
** and incorrect behavior.
*/
#ifndef SQLITE_OMIT_WSD
SQLITE_PRIVATE int sqlite3PendingByte = 0x40000000;
#endif

/*
** Flags for select tracing and the ".selecttrace" macro of the CLI
*/
SQLITE_API u32 sqlite3_unsupported_selecttrace = 0;


/* #include "opcodes.h" */
/*
** Properties of opcodes.  The OPFLG_INITIALIZER macro is
** created by mkopcodeh.awk during compilation.  Data is obtained
** from the comments following the "case OP_xxxx:" statements in
** the vdbe.c file.







|

|
>







20824
20825
20826
20827
20828
20829
20830
20831
20832
20833
20834
20835
20836
20837
20838
20839
20840
20841
** and incorrect behavior.
*/
#ifndef SQLITE_OMIT_WSD
SQLITE_PRIVATE int sqlite3PendingByte = 0x40000000;
#endif

/*
** Tracing flags set by SQLITE_TESTCTRL_TRACEFLAGS.
*/
SQLITE_PRIVATE u32 sqlite3SelectTrace = 0;
SQLITE_PRIVATE u32 sqlite3WhereTrace = 0;

/* #include "opcodes.h" */
/*
** Properties of opcodes.  The OPFLG_INITIALIZER macro is
** created by mkopcodeh.awk during compilation.  Data is obtained
** from the comments following the "case OP_xxxx:" statements in
** the vdbe.c file.
23168
23169
23170
23171
23172
23173
23174


23175
23176
23177
23178
23179
23180
23181
23182
23183
23184
23185





23186
23187
23188
23189
23190
23191
23192
** routine has no return value since the return value would be meaningless.
*/
SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){
  if( id->pMethods==0 ) return SQLITE_NOTFOUND;
#ifdef SQLITE_TEST
  if( op!=SQLITE_FCNTL_COMMIT_PHASETWO
   && op!=SQLITE_FCNTL_LOCK_TIMEOUT


  ){
    /* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite
    ** is using a regular VFS, it is called after the corresponding
    ** transaction has been committed. Injecting a fault at this point
    ** confuses the test scripts - the COMMIT comand returns SQLITE_NOMEM
    ** but the transaction is committed anyway.
    **
    ** The core must call OsFileControl() though, not OsFileControlHint(),
    ** as if a custom VFS (e.g. zipvfs) returns an error here, it probably
    ** means the commit really has failed and an error should be returned
    ** to the user.  */





    DO_OS_MALLOC_TEST(id);
  }
#endif
  return id->pMethods->xFileControl(id, op, pArg);
}
SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file *id, int op, void *pArg){
  if( id->pMethods ) (void)id->pMethods->xFileControl(id, op, pArg);







>
>










|
>
>
>
>
>







23251
23252
23253
23254
23255
23256
23257
23258
23259
23260
23261
23262
23263
23264
23265
23266
23267
23268
23269
23270
23271
23272
23273
23274
23275
23276
23277
23278
23279
23280
23281
23282
** routine has no return value since the return value would be meaningless.
*/
SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){
  if( id->pMethods==0 ) return SQLITE_NOTFOUND;
#ifdef SQLITE_TEST
  if( op!=SQLITE_FCNTL_COMMIT_PHASETWO
   && op!=SQLITE_FCNTL_LOCK_TIMEOUT
   && op!=SQLITE_FCNTL_CKPT_DONE
   && op!=SQLITE_FCNTL_CKPT_START
  ){
    /* Faults are not injected into COMMIT_PHASETWO because, assuming SQLite
    ** is using a regular VFS, it is called after the corresponding
    ** transaction has been committed. Injecting a fault at this point
    ** confuses the test scripts - the COMMIT comand returns SQLITE_NOMEM
    ** but the transaction is committed anyway.
    **
    ** The core must call OsFileControl() though, not OsFileControlHint(),
    ** as if a custom VFS (e.g. zipvfs) returns an error here, it probably
    ** means the commit really has failed and an error should be returned
    ** to the user.
    **
    ** The CKPT_DONE and CKPT_START file-controls are write-only signals
    ** to the cksumvfs.  Their return code is meaningless and is ignored
    ** by the SQLite core, so there is no point in simulating OOMs for them.
    */
    DO_OS_MALLOC_TEST(id);
  }
#endif
  return id->pMethods->xFileControl(id, op, pArg);
}
SQLITE_PRIVATE void sqlite3OsFileControlHint(sqlite3_file *id, int op, void *pArg){
  if( id->pMethods ) (void)id->pMethods->xFileControl(id, op, pArg);
31379
31380
31381
31382
31383
31384
31385










31386
31387
31388
31389
31390
31391
31392
** that would be appropriate.
*/
SQLITE_PRIVATE void sqlite3Error(sqlite3 *db, int err_code){
  assert( db!=0 );
  db->errCode = err_code;
  if( err_code || db->pErr ) sqlite3ErrorFinish(db, err_code);
}











/*
** Load the sqlite3.iSysErrno field if that is an appropriate thing
** to do based on the SQLite error code in rc.
*/
SQLITE_PRIVATE void sqlite3SystemError(sqlite3 *db, int rc){
  if( rc==SQLITE_IOERR_NOMEM ) return;







>
>
>
>
>
>
>
>
>
>







31469
31470
31471
31472
31473
31474
31475
31476
31477
31478
31479
31480
31481
31482
31483
31484
31485
31486
31487
31488
31489
31490
31491
31492
** that would be appropriate.
*/
SQLITE_PRIVATE void sqlite3Error(sqlite3 *db, int err_code){
  assert( db!=0 );
  db->errCode = err_code;
  if( err_code || db->pErr ) sqlite3ErrorFinish(db, err_code);
}

/*
** The equivalent of sqlite3Error(db, SQLITE_OK).  Clear the error state
** and error message.
*/
SQLITE_PRIVATE void sqlite3ErrorClear(sqlite3 *db){
  assert( db!=0 );
  db->errCode = SQLITE_OK;
  if( db->pErr ) sqlite3ValueSetNull(db->pErr);
}

/*
** Load the sqlite3.iSysErrno field if that is an appropriate thing
** to do based on the SQLite error code in rc.
*/
SQLITE_PRIVATE void sqlite3SystemError(sqlite3 *db, int rc){
  if( rc==SQLITE_IOERR_NOMEM ) return;
33369
33370
33371
33372
33373
33374
33375

33376
33377
33378
33379
33380
33381
33382
33383
33384
33385
33386
33387
33388
33389
33390
33391
33392
33393
33394
33395
33396
33397
33398
33399
33400
33401
33402
33403

33404
33405
33406
33407
33408
33409
33410
33411
33412
33413
33414
33415
33416
33417
33418
33419
33420
33421
33422
33423
33424
33425
33426
33427
33428
33429
33430
33431
33432
33433
33434
33435
33436
    /* 116 */ "Close"            OpHelp(""),
    /* 117 */ "ColumnsUsed"      OpHelp(""),
    /* 118 */ "SeekScan"         OpHelp("Scan-ahead up to P1 rows"),
    /* 119 */ "SeekHit"          OpHelp("set P2<=seekHit<=P3"),
    /* 120 */ "Sequence"         OpHelp("r[P2]=cursor[P1].ctr++"),
    /* 121 */ "NewRowid"         OpHelp("r[P2]=rowid"),
    /* 122 */ "Insert"           OpHelp("intkey=r[P3] data=r[P2]"),

    /* 123 */ "Delete"           OpHelp(""),
    /* 124 */ "ResetCount"       OpHelp(""),
    /* 125 */ "SorterCompare"    OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"),
    /* 126 */ "SorterData"       OpHelp("r[P2]=data"),
    /* 127 */ "RowData"          OpHelp("r[P2]=data"),
    /* 128 */ "Rowid"            OpHelp("r[P2]=rowid"),
    /* 129 */ "NullRow"          OpHelp(""),
    /* 130 */ "SeekEnd"          OpHelp(""),
    /* 131 */ "IdxInsert"        OpHelp("key=r[P2]"),
    /* 132 */ "SorterInsert"     OpHelp("key=r[P2]"),
    /* 133 */ "IdxDelete"        OpHelp("key=r[P2@P3]"),
    /* 134 */ "DeferredSeek"     OpHelp("Move P3 to P1.rowid if needed"),
    /* 135 */ "IdxRowid"         OpHelp("r[P2]=rowid"),
    /* 136 */ "FinishSeek"       OpHelp(""),
    /* 137 */ "Destroy"          OpHelp(""),
    /* 138 */ "Clear"            OpHelp(""),
    /* 139 */ "ResetSorter"      OpHelp(""),
    /* 140 */ "CreateBtree"      OpHelp("r[P2]=root iDb=P1 flags=P3"),
    /* 141 */ "SqlExec"          OpHelp(""),
    /* 142 */ "ParseSchema"      OpHelp(""),
    /* 143 */ "LoadAnalysis"     OpHelp(""),
    /* 144 */ "DropTable"        OpHelp(""),
    /* 145 */ "DropIndex"        OpHelp(""),
    /* 146 */ "DropTrigger"      OpHelp(""),
    /* 147 */ "IntegrityCk"      OpHelp(""),
    /* 148 */ "RowSetAdd"        OpHelp("rowset(P1)=r[P2]"),
    /* 149 */ "Param"            OpHelp(""),
    /* 150 */ "Real"             OpHelp("r[P2]=P4"),

    /* 151 */ "FkCounter"        OpHelp("fkctr[P1]+=P2"),
    /* 152 */ "MemMax"           OpHelp("r[P1]=max(r[P1],r[P2])"),
    /* 153 */ "OffsetLimit"      OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"),
    /* 154 */ "AggInverse"       OpHelp("accum=r[P3] inverse(r[P2@P5])"),
    /* 155 */ "AggStep"          OpHelp("accum=r[P3] step(r[P2@P5])"),
    /* 156 */ "AggStep1"         OpHelp("accum=r[P3] step(r[P2@P5])"),
    /* 157 */ "AggValue"         OpHelp("r[P3]=value N=P2"),
    /* 158 */ "AggFinal"         OpHelp("accum=r[P1] N=P2"),
    /* 159 */ "Expire"           OpHelp(""),
    /* 160 */ "CursorLock"       OpHelp(""),
    /* 161 */ "CursorUnlock"     OpHelp(""),
    /* 162 */ "TableLock"        OpHelp("iDb=P1 root=P2 write=P3"),
    /* 163 */ "VBegin"           OpHelp(""),
    /* 164 */ "VCreate"          OpHelp(""),
    /* 165 */ "VDestroy"         OpHelp(""),
    /* 166 */ "VOpen"            OpHelp(""),
    /* 167 */ "VColumn"          OpHelp("r[P3]=vcolumn(P2)"),
    /* 168 */ "VRename"          OpHelp(""),
    /* 169 */ "Pagecount"        OpHelp(""),
    /* 170 */ "MaxPgcnt"         OpHelp(""),
    /* 171 */ "Trace"            OpHelp(""),
    /* 172 */ "CursorHint"       OpHelp(""),
    /* 173 */ "ReleaseReg"       OpHelp("release r[P1@P2] mask P3"),
    /* 174 */ "Noop"             OpHelp(""),
    /* 175 */ "Explain"          OpHelp(""),
    /* 176 */ "Abortable"        OpHelp(""),
  };
  return azName[i];
}
#endif

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







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

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







33469
33470
33471
33472
33473
33474
33475
33476
33477
33478
33479
33480
33481
33482
33483
33484
33485
33486
33487
33488
33489
33490
33491
33492
33493
33494
33495
33496
33497
33498
33499
33500
33501
33502

33503
33504
33505
33506
33507
33508
33509
33510
33511
33512
33513
33514
33515
33516
33517
33518
33519
33520
33521
33522
33523
33524
33525
33526
33527
33528
33529
33530
33531
33532
33533
33534
33535
33536
33537
    /* 116 */ "Close"            OpHelp(""),
    /* 117 */ "ColumnsUsed"      OpHelp(""),
    /* 118 */ "SeekScan"         OpHelp("Scan-ahead up to P1 rows"),
    /* 119 */ "SeekHit"          OpHelp("set P2<=seekHit<=P3"),
    /* 120 */ "Sequence"         OpHelp("r[P2]=cursor[P1].ctr++"),
    /* 121 */ "NewRowid"         OpHelp("r[P2]=rowid"),
    /* 122 */ "Insert"           OpHelp("intkey=r[P3] data=r[P2]"),
    /* 123 */ "RowCell"          OpHelp(""),
    /* 124 */ "Delete"           OpHelp(""),
    /* 125 */ "ResetCount"       OpHelp(""),
    /* 126 */ "SorterCompare"    OpHelp("if key(P1)!=trim(r[P3],P4) goto P2"),
    /* 127 */ "SorterData"       OpHelp("r[P2]=data"),
    /* 128 */ "RowData"          OpHelp("r[P2]=data"),
    /* 129 */ "Rowid"            OpHelp("r[P2]=rowid"),
    /* 130 */ "NullRow"          OpHelp(""),
    /* 131 */ "SeekEnd"          OpHelp(""),
    /* 132 */ "IdxInsert"        OpHelp("key=r[P2]"),
    /* 133 */ "SorterInsert"     OpHelp("key=r[P2]"),
    /* 134 */ "IdxDelete"        OpHelp("key=r[P2@P3]"),
    /* 135 */ "DeferredSeek"     OpHelp("Move P3 to P1.rowid if needed"),
    /* 136 */ "IdxRowid"         OpHelp("r[P2]=rowid"),
    /* 137 */ "FinishSeek"       OpHelp(""),
    /* 138 */ "Destroy"          OpHelp(""),
    /* 139 */ "Clear"            OpHelp(""),
    /* 140 */ "ResetSorter"      OpHelp(""),
    /* 141 */ "CreateBtree"      OpHelp("r[P2]=root iDb=P1 flags=P3"),
    /* 142 */ "SqlExec"          OpHelp(""),
    /* 143 */ "ParseSchema"      OpHelp(""),
    /* 144 */ "LoadAnalysis"     OpHelp(""),
    /* 145 */ "DropTable"        OpHelp(""),
    /* 146 */ "DropIndex"        OpHelp(""),
    /* 147 */ "DropTrigger"      OpHelp(""),
    /* 148 */ "IntegrityCk"      OpHelp(""),
    /* 149 */ "RowSetAdd"        OpHelp("rowset(P1)=r[P2]"),

    /* 150 */ "Real"             OpHelp("r[P2]=P4"),
    /* 151 */ "Param"            OpHelp(""),
    /* 152 */ "FkCounter"        OpHelp("fkctr[P1]+=P2"),
    /* 153 */ "MemMax"           OpHelp("r[P1]=max(r[P1],r[P2])"),
    /* 154 */ "OffsetLimit"      OpHelp("if r[P1]>0 then r[P2]=r[P1]+max(0,r[P3]) else r[P2]=(-1)"),
    /* 155 */ "AggInverse"       OpHelp("accum=r[P3] inverse(r[P2@P5])"),
    /* 156 */ "AggStep"          OpHelp("accum=r[P3] step(r[P2@P5])"),
    /* 157 */ "AggStep1"         OpHelp("accum=r[P3] step(r[P2@P5])"),
    /* 158 */ "AggValue"         OpHelp("r[P3]=value N=P2"),
    /* 159 */ "AggFinal"         OpHelp("accum=r[P1] N=P2"),
    /* 160 */ "Expire"           OpHelp(""),
    /* 161 */ "CursorLock"       OpHelp(""),
    /* 162 */ "CursorUnlock"     OpHelp(""),
    /* 163 */ "TableLock"        OpHelp("iDb=P1 root=P2 write=P3"),
    /* 164 */ "VBegin"           OpHelp(""),
    /* 165 */ "VCreate"          OpHelp(""),
    /* 166 */ "VDestroy"         OpHelp(""),
    /* 167 */ "VOpen"            OpHelp(""),
    /* 168 */ "VColumn"          OpHelp("r[P3]=vcolumn(P2)"),
    /* 169 */ "VRename"          OpHelp(""),
    /* 170 */ "Pagecount"        OpHelp(""),
    /* 171 */ "MaxPgcnt"         OpHelp(""),
    /* 172 */ "Trace"            OpHelp(""),
    /* 173 */ "CursorHint"       OpHelp(""),
    /* 174 */ "ReleaseReg"       OpHelp("release r[P1@P2] mask P3"),
    /* 175 */ "Noop"             OpHelp(""),
    /* 176 */ "Explain"          OpHelp(""),
    /* 177 */ "Abortable"        OpHelp(""),
  };
  return azName[i];
}
#endif

/************** End of opcodes.c *********************************************/
/************** Begin file os_unix.c *****************************************/
64145
64146
64147
64148
64149
64150
64151

64152
64153
64154
64155
64156
64157
64158
#ifndef SQLITE_OMIT_SHARED_CACHE
  int nRef;             /* Number of references to this structure */
  BtShared *pNext;      /* Next on a list of sharable BtShared structs */
  BtLock *pLock;        /* List of locks held on this shared-btree struct */
  Btree *pWriter;       /* Btree with currently open write transaction */
#endif
  u8 *pTmpSpace;        /* Temp space sufficient to hold a single cell */

};

/*
** Allowed values for BtShared.btsFlags
*/
#define BTS_READ_ONLY        0x0001   /* Underlying file is readonly */
#define BTS_PAGESIZE_FIXED   0x0002   /* Page size can no longer be changed */







>







64246
64247
64248
64249
64250
64251
64252
64253
64254
64255
64256
64257
64258
64259
64260
#ifndef SQLITE_OMIT_SHARED_CACHE
  int nRef;             /* Number of references to this structure */
  BtShared *pNext;      /* Next on a list of sharable BtShared structs */
  BtLock *pLock;        /* List of locks held on this shared-btree struct */
  Btree *pWriter;       /* Btree with currently open write transaction */
#endif
  u8 *pTmpSpace;        /* Temp space sufficient to hold a single cell */
  int nPreformatSize;   /* Size of last cell written by TransferRow() */
};

/*
** Allowed values for BtShared.btsFlags
*/
#define BTS_READ_ONLY        0x0001   /* Underlying file is readonly */
#define BTS_PAGESIZE_FIXED   0x0002   /* Page size can no longer be changed */
65857
65858
65859
65860
65861
65862
65863


















65864
65865
65866
65867
65868
65869
65870
  if( surplus <= maxLocal ){
    pInfo->nLocal = (u16)surplus;
  }else{
    pInfo->nLocal = (u16)minLocal;
  }
  pInfo->nSize = (u16)(&pInfo->pPayload[pInfo->nLocal] - pCell) + 4;
}



















/*
** The following routines are implementations of the MemPage.xParseCell()
** method.
**
** Parse a cell content block and fill in the CellInfo structure.
**







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







65959
65960
65961
65962
65963
65964
65965
65966
65967
65968
65969
65970
65971
65972
65973
65974
65975
65976
65977
65978
65979
65980
65981
65982
65983
65984
65985
65986
65987
65988
65989
65990
  if( surplus <= maxLocal ){
    pInfo->nLocal = (u16)surplus;
  }else{
    pInfo->nLocal = (u16)minLocal;
  }
  pInfo->nSize = (u16)(&pInfo->pPayload[pInfo->nLocal] - pCell) + 4;
}

/*
** Given a record with nPayload bytes of payload stored within btree
** page pPage, return the number of bytes of payload stored locally.
*/
static int btreePayloadToLocal(MemPage *pPage, i64 nPayload){
  int maxLocal;  /* Maximum amount of payload held locally */
  maxLocal = pPage->maxLocal;
  if( nPayload<=maxLocal ){
    return nPayload;
  }else{
    int minLocal;  /* Minimum amount of payload held locally */
    int surplus;   /* Overflow payload available for local storage */
    minLocal = pPage->minLocal;
    surplus = minLocal + (nPayload - minLocal)%(pPage->pBt->usableSize-4);
    return ( surplus <= maxLocal ) ? surplus : minLocal;
  }
}

/*
** The following routines are implementations of the MemPage.xParseCell()
** method.
**
** Parse a cell content block and fill in the CellInfo structure.
**
73375
73376
73377
73378
73379
73380
73381
73382

73383
73384
73385
73386
73387
73388
73389
73390
73391
73392
73393
73394
73395
73396
73397
73398
73399
73400
73401
73402
73403
73404
73405
73406
73407
  int idx;
  MemPage *pPage;
  Btree *p = pCur->pBtree;
  BtShared *pBt = p->pBt;
  unsigned char *oldCell;
  unsigned char *newCell = 0;

  assert( (flags & (BTREE_SAVEPOSITION|BTREE_APPEND))==flags );


  if( pCur->eState==CURSOR_FAULT ){
    assert( pCur->skipNext!=SQLITE_OK );
    return pCur->skipNext;
  }

  assert( cursorOwnsBtShared(pCur) );
  assert( (pCur->curFlags & BTCF_WriteFlag)!=0
              && pBt->inTransaction==TRANS_WRITE
              && (pBt->btsFlags & BTS_READ_ONLY)==0 );
  assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );

  /* Assert that the caller has been consistent. If this cursor was opened
  ** expecting an index b-tree, then the caller should be inserting blob
  ** keys with no associated data. If the cursor was opened expecting an
  ** intkey table, the caller should be inserting integer keys with a
  ** blob of associated data.  */
  assert( (pX->pKey==0)==(pCur->pKeyInfo==0) );

  /* Save the positions of any other cursors open on this table.
  **
  ** In some cases, the call to btreeMoveto() below is a no-op. For
  ** example, when inserting data into a table with auto-generated integer
  ** keys, the VDBE layer invokes sqlite3BtreeLast() to figure out the
  ** integer key to use. It then calls this function to actually insert the







|
>

















|







73495
73496
73497
73498
73499
73500
73501
73502
73503
73504
73505
73506
73507
73508
73509
73510
73511
73512
73513
73514
73515
73516
73517
73518
73519
73520
73521
73522
73523
73524
73525
73526
73527
73528
  int idx;
  MemPage *pPage;
  Btree *p = pCur->pBtree;
  BtShared *pBt = p->pBt;
  unsigned char *oldCell;
  unsigned char *newCell = 0;

  assert( (flags & (BTREE_SAVEPOSITION|BTREE_APPEND|BTREE_PREFORMAT))==flags );
  assert( (flags & BTREE_PREFORMAT)==0 || seekResult || pCur->pKeyInfo==0 );

  if( pCur->eState==CURSOR_FAULT ){
    assert( pCur->skipNext!=SQLITE_OK );
    return pCur->skipNext;
  }

  assert( cursorOwnsBtShared(pCur) );
  assert( (pCur->curFlags & BTCF_WriteFlag)!=0
              && pBt->inTransaction==TRANS_WRITE
              && (pBt->btsFlags & BTS_READ_ONLY)==0 );
  assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) );

  /* Assert that the caller has been consistent. If this cursor was opened
  ** expecting an index b-tree, then the caller should be inserting blob
  ** keys with no associated data. If the cursor was opened expecting an
  ** intkey table, the caller should be inserting integer keys with a
  ** blob of associated data.  */
  assert( (flags & BTREE_PREFORMAT) || (pX->pKey==0)==(pCur->pKeyInfo==0) );

  /* Save the positions of any other cursors open on this table.
  **
  ** In some cases, the call to btreeMoveto() below is a no-op. For
  ** example, when inserting data into a table with auto-generated integer
  ** keys, the VDBE layer invokes sqlite3BtreeLast() to figure out the
  ** integer key to use. It then calls this function to actually insert the
73503
73504
73505
73506
73507
73508
73509
73510
73511
73512
73513
73514
73515
73516
73517
73518
73519
73520
73521
73522
73523
73524
73525
73526













73527

73528
73529
73530
73531
73532
73533
73534

  }
  assert( pCur->eState==CURSOR_VALID
       || (pCur->eState==CURSOR_INVALID && loc)
       || CORRUPT_DB );

  pPage = pCur->pPage;
  assert( pPage->intKey || pX->nKey>=0 );
  assert( pPage->leaf || !pPage->intKey );
  if( pPage->nFree<0 ){
    if( pCur->eState>CURSOR_INVALID ){
      rc = SQLITE_CORRUPT_BKPT;
    }else{
      rc = btreeComputeFreeSpace(pPage);
    }
    if( rc ) return rc;
  }

  TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n",
          pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno,
          loc==0 ? "overwrite" : "new entry"));
  assert( pPage->isInit );
  newCell = pBt->pTmpSpace;
  assert( newCell!=0 );













  rc = fillInCell(pPage, newCell, pX, &szNew);

  if( rc ) goto end_insert;
  assert( szNew==pPage->xCellSize(pPage, newCell) );
  assert( szNew <= MX_CELL_SIZE(pBt) );
  idx = pCur->ix;
  if( loc==0 ){
    CellInfo info;
    assert( idx<pPage->nCell );







|
















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







73624
73625
73626
73627
73628
73629
73630
73631
73632
73633
73634
73635
73636
73637
73638
73639
73640
73641
73642
73643
73644
73645
73646
73647
73648
73649
73650
73651
73652
73653
73654
73655
73656
73657
73658
73659
73660
73661
73662
73663
73664
73665
73666
73667
73668
73669

  }
  assert( pCur->eState==CURSOR_VALID
       || (pCur->eState==CURSOR_INVALID && loc)
       || CORRUPT_DB );

  pPage = pCur->pPage;
  assert( pPage->intKey || pX->nKey>=0 || (flags & BTREE_PREFORMAT) );
  assert( pPage->leaf || !pPage->intKey );
  if( pPage->nFree<0 ){
    if( pCur->eState>CURSOR_INVALID ){
      rc = SQLITE_CORRUPT_BKPT;
    }else{
      rc = btreeComputeFreeSpace(pPage);
    }
    if( rc ) return rc;
  }

  TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n",
          pCur->pgnoRoot, pX->nKey, pX->nData, pPage->pgno,
          loc==0 ? "overwrite" : "new entry"));
  assert( pPage->isInit );
  newCell = pBt->pTmpSpace;
  assert( newCell!=0 );
  if( flags & BTREE_PREFORMAT ){
    rc = SQLITE_OK;
    szNew = pBt->nPreformatSize;
    if( szNew<4 ) szNew = 4;
    if( ISAUTOVACUUM && szNew>pPage->maxLocal ){
      CellInfo info;
      pPage->xParseCell(pPage, newCell, &info);
      if( info.nPayload!=info.nLocal ){
        Pgno ovfl = get4byte(&newCell[szNew-4]);
        ptrmapPut(pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, &rc);
      }
    }
  }else{
    rc = fillInCell(pPage, newCell, pX, &szNew);
  }
  if( rc ) goto end_insert;
  assert( szNew==pPage->xCellSize(pPage, newCell) );
  assert( szNew <= MX_CELL_SIZE(pBt) );
  idx = pCur->ix;
  if( loc==0 ){
    CellInfo info;
    assert( idx<pPage->nCell );
73624
73625
73626
73627
73628
73629
73630












































































































73631
73632
73633
73634
73635
73636
73637
      pCur->eState = CURSOR_REQUIRESEEK;
      pCur->nKey = pX->nKey;
    }
  }
  assert( pCur->iPage<0 || pCur->pPage->nOverflow==0 );

end_insert:












































































































  return rc;
}

/*
** Delete the entry that the cursor is pointing to.
**
** If the BTREE_SAVEPOSITION bit of the flags parameter is zero, then







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







73759
73760
73761
73762
73763
73764
73765
73766
73767
73768
73769
73770
73771
73772
73773
73774
73775
73776
73777
73778
73779
73780
73781
73782
73783
73784
73785
73786
73787
73788
73789
73790
73791
73792
73793
73794
73795
73796
73797
73798
73799
73800
73801
73802
73803
73804
73805
73806
73807
73808
73809
73810
73811
73812
73813
73814
73815
73816
73817
73818
73819
73820
73821
73822
73823
73824
73825
73826
73827
73828
73829
73830
73831
73832
73833
73834
73835
73836
73837
73838
73839
73840
73841
73842
73843
73844
73845
73846
73847
73848
73849
73850
73851
73852
73853
73854
73855
73856
73857
73858
73859
73860
73861
73862
73863
73864
73865
73866
73867
73868
73869
73870
73871
73872
73873
73874
73875
73876
73877
73878
73879
73880
      pCur->eState = CURSOR_REQUIRESEEK;
      pCur->nKey = pX->nKey;
    }
  }
  assert( pCur->iPage<0 || pCur->pPage->nOverflow==0 );

end_insert:
  return rc;
}

/*
** This function is used as part of copying the current row from cursor
** pSrc into cursor pDest. If the cursors are open on intkey tables, then
** parameter iKey is used as the rowid value when the record is copied
** into pDest. Otherwise, the record is copied verbatim.
**
** This function does not actually write the new value to cursor pDest.
** Instead, it creates and populates any required overflow pages and
** writes the data for the new cell into the BtShared.pTmpSpace buffer
** for the destination database. The size of the cell, in bytes, is left
** in BtShared.nPreformatSize. The caller completes the insertion by
** calling sqlite3BtreeInsert() with the BTREE_PREFORMAT flag specified.
**
** SQLITE_OK is returned if successful, or an SQLite error code otherwise.
*/
SQLITE_PRIVATE int sqlite3BtreeTransferRow(BtCursor *pDest, BtCursor *pSrc, i64 iKey){
  int rc = SQLITE_OK;
  BtShared *pBt = pDest->pBt;
  u8 *aOut = pBt->pTmpSpace;    /* Pointer to next output buffer */
  const u8 *aIn;                /* Pointer to next input buffer */
  u32 nIn;                      /* Size of input buffer aIn[] */
  u32 nRem;                     /* Bytes of data still to copy */

  getCellInfo(pSrc);
  aOut += putVarint32(aOut, pSrc->info.nPayload);
  if( pDest->pKeyInfo==0 ) aOut += putVarint(aOut, iKey);
  nIn = pSrc->info.nLocal;
  aIn = pSrc->info.pPayload;
  if( aIn+nIn>pSrc->pPage->aDataEnd ){
    return SQLITE_CORRUPT_BKPT;
  }
  nRem = pSrc->info.nPayload;
  if( nIn==nRem && nIn<pDest->pPage->maxLocal ){
    memcpy(aOut, aIn, nIn);
    pBt->nPreformatSize = nIn + (aOut - pBt->pTmpSpace);
  }else{
    Pager *pSrcPager = pSrc->pBt->pPager;
    u8 *pPgnoOut = 0;
    Pgno ovflIn = 0;
    DbPage *pPageIn = 0;
    MemPage *pPageOut = 0;
    u32 nOut;                     /* Size of output buffer aOut[] */

    nOut = btreePayloadToLocal(pDest->pPage, pSrc->info.nPayload);
    pBt->nPreformatSize = nOut + (aOut - pBt->pTmpSpace);
    if( nOut<pSrc->info.nPayload ){
      pPgnoOut = &aOut[nOut];
      pBt->nPreformatSize += 4;
    }

    if( nRem>nIn ){
      if( aIn+nIn+4>pSrc->pPage->aDataEnd ){
        return SQLITE_CORRUPT_BKPT;
      }
      ovflIn = get4byte(&pSrc->info.pPayload[nIn]);
    }

    do {
      nRem -= nOut;
      do{
        assert( nOut>0 );
        if( nIn>0 ){
          int nCopy = MIN(nOut, nIn);
          memcpy(aOut, aIn, nCopy);
          nOut -= nCopy;
          nIn -= nCopy;
          aOut += nCopy;
          aIn += nCopy;
        }
        if( nOut>0 ){
          sqlite3PagerUnref(pPageIn);
          pPageIn = 0;
          rc = sqlite3PagerGet(pSrcPager, ovflIn, &pPageIn, PAGER_GET_READONLY);
          if( rc==SQLITE_OK ){
            aIn = (const u8*)sqlite3PagerGetData(pPageIn);
            ovflIn = get4byte(aIn);
            aIn += 4;
            nIn = pSrc->pBt->usableSize - 4;
          }
        }
      }while( rc==SQLITE_OK && nOut>0 );

      if( rc==SQLITE_OK && nRem>0 ){
        Pgno pgnoNew;
        MemPage *pNew = 0;
        rc = allocateBtreePage(pBt, &pNew, &pgnoNew, 0, 0);
        put4byte(pPgnoOut, pgnoNew);
        if( ISAUTOVACUUM && pPageOut ){
          ptrmapPut(pBt, pgnoNew, PTRMAP_OVERFLOW2, pPageOut->pgno, &rc);
        }
        releasePage(pPageOut);
        pPageOut = pNew;
        if( pPageOut ){
          pPgnoOut = pPageOut->aData;
          put4byte(pPgnoOut, 0);
          aOut = &pPgnoOut[4];
          nOut = MIN(pBt->usableSize - 4, nRem);
        }
      }
    }while( nRem>0 && rc==SQLITE_OK );

    releasePage(pPageOut);
    sqlite3PagerUnref(pPageIn);
  }

  return rc;
}

/*
** Delete the entry that the cursor is pointing to.
**
** If the BTREE_SAVEPOSITION bit of the flags parameter is zero, then
80338
80339
80340
80341
80342
80343
80344


80345
80346
80347
80348
80349
80350
80351
  struct ReusableSpace x;        /* Reusable bulk memory */

  assert( p!=0 );
  assert( p->nOp>0 );
  assert( pParse!=0 );
  assert( p->magic==VDBE_MAGIC_INIT );
  assert( pParse==p->pParse );


  db = p->db;
  assert( db->mallocFailed==0 );
  nVar = pParse->nVar;
  nMem = pParse->nMem;
  nCursor = pParse->nTab;
  nArg = pParse->nMaxArg;








>
>







80581
80582
80583
80584
80585
80586
80587
80588
80589
80590
80591
80592
80593
80594
80595
80596
  struct ReusableSpace x;        /* Reusable bulk memory */

  assert( p!=0 );
  assert( p->nOp>0 );
  assert( pParse!=0 );
  assert( p->magic==VDBE_MAGIC_INIT );
  assert( pParse==p->pParse );
  p->pVList = pParse->pVList;
  pParse->pVList =  0;
  db = p->db;
  assert( db->mallocFailed==0 );
  nVar = pParse->nVar;
  nMem = pParse->nMem;
  nCursor = pParse->nTab;
  nArg = pParse->nMaxArg;

80422
80423
80424
80425
80426
80427
80428
80429
80430
80431
80432
80433
80434
80435
80436
80437
      p->apCsr = allocSpace(&x, p->apCsr, nCursor*sizeof(VdbeCursor*));
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
      p->anExec = allocSpace(&x, p->anExec, p->nOp*sizeof(i64));
#endif
    }
  }

  p->pVList = pParse->pVList;
  pParse->pVList =  0;
  if( db->mallocFailed ){
    p->nVar = 0;
    p->nCursor = 0;
    p->nMem = 0;
  }else{
    p->nCursor = nCursor;
    p->nVar = (ynVar)nVar;







<
<







80667
80668
80669
80670
80671
80672
80673


80674
80675
80676
80677
80678
80679
80680
      p->apCsr = allocSpace(&x, p->apCsr, nCursor*sizeof(VdbeCursor*));
#ifdef SQLITE_ENABLE_STMT_SCANSTATUS
      p->anExec = allocSpace(&x, p->anExec, p->nOp*sizeof(i64));
#endif
    }
  }



  if( db->mallocFailed ){
    p->nVar = 0;
    p->nCursor = 0;
    p->nMem = 0;
  }else{
    p->nCursor = nCursor;
    p->nVar = (ynVar)nVar;
85412
85413
85414
85415
85416
85417
85418
85419
85420
85421
85422
85423
85424
85425
85426
        testcase( zRawSql[0]=='$' );
        testcase( zRawSql[0]=='@' );
        testcase( zRawSql[0]=='#' );
        idx = sqlite3VdbeParameterIndex(p, zRawSql, nToken);
        assert( idx>0 );
      }
      zRawSql += nToken;
      nextIndex = idx + 1;
      assert( idx>0 && idx<=p->nVar );
      pVar = &p->aVar[idx-1];
      if( pVar->flags & MEM_Null ){
        sqlite3_str_append(&out, "NULL", 4);
      }else if( pVar->flags & (MEM_Int|MEM_IntReal) ){
        sqlite3_str_appendf(&out, "%lld", pVar->u.i);
      }else if( pVar->flags & MEM_Real ){







|







85655
85656
85657
85658
85659
85660
85661
85662
85663
85664
85665
85666
85667
85668
85669
        testcase( zRawSql[0]=='$' );
        testcase( zRawSql[0]=='@' );
        testcase( zRawSql[0]=='#' );
        idx = sqlite3VdbeParameterIndex(p, zRawSql, nToken);
        assert( idx>0 );
      }
      zRawSql += nToken;
      nextIndex = MAX(idx + 1, nextIndex);
      assert( idx>0 && idx<=p->nVar );
      pVar = &p->aVar[idx-1];
      if( pVar->flags & MEM_Null ){
        sqlite3_str_append(&out, "NULL", 4);
      }else if( pVar->flags & (MEM_Int|MEM_IntReal) ){
        sqlite3_str_appendf(&out, "%lld", pVar->u.i);
      }else if( pVar->flags & MEM_Real ){
90699
90700
90701
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
  if( pData->flags & MEM_Zero ){
    x.nZero = pData->u.nZero;
  }else{
    x.nZero = 0;
  }
  x.pKey = 0;
  rc = sqlite3BtreeInsert(pC->uc.pCursor, &x,
      (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION)), seekResult

  );
  pC->deferredMoveto = 0;
  pC->cacheStatus = CACHE_STALE;

  /* Invoke the update-hook if required. */
  if( rc ) goto abort_due_to_error;
  if( pTab ){
    assert( db->xUpdateCallback!=0 );
    assert( pTab->aCol!=0 );
    db->xUpdateCallback(db->pUpdateArg,
           (pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT,
           zDb, pTab->zName, x.nKey);
  }
  break;
}




























/* Opcode: Delete P1 P2 P3 P4 P5
**
** Delete the record at which the P1 cursor is currently pointing.
**
** If the OPFLAG_SAVEPOSITION bit of the P5 parameter is set, then
** the cursor will be left pointing at  either the next or the previous







|
>















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







90942
90943
90944
90945
90946
90947
90948
90949
90950
90951
90952
90953
90954
90955
90956
90957
90958
90959
90960
90961
90962
90963
90964
90965
90966
90967
90968
90969
90970
90971
90972
90973
90974
90975
90976
90977
90978
90979
90980
90981
90982
90983
90984
90985
90986
90987
90988
90989
90990
90991
90992
90993
90994
90995
90996
90997
90998
90999
  if( pData->flags & MEM_Zero ){
    x.nZero = pData->u.nZero;
  }else{
    x.nZero = 0;
  }
  x.pKey = 0;
  rc = sqlite3BtreeInsert(pC->uc.pCursor, &x,
      (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION|OPFLAG_PREFORMAT)),
      seekResult
  );
  pC->deferredMoveto = 0;
  pC->cacheStatus = CACHE_STALE;

  /* Invoke the update-hook if required. */
  if( rc ) goto abort_due_to_error;
  if( pTab ){
    assert( db->xUpdateCallback!=0 );
    assert( pTab->aCol!=0 );
    db->xUpdateCallback(db->pUpdateArg,
           (pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT,
           zDb, pTab->zName, x.nKey);
  }
  break;
}

/* Opcode: RowCell P1 P2 P3 * *
**
** P1 and P2 are both open cursors. Both must be opened on the same type
** of table - intkey or index. This opcode is used as part of copying
** the current row from P2 into P1. If the cursors are opened on intkey
** tables, register P3 contains the rowid to use with the new record in
** P1. If they are opened on index tables, P3 is not used.
**
** This opcode must be followed by either an Insert or InsertIdx opcode
** with the OPFLAG_PREFORMAT flag set to complete the insert operation.
*/
case OP_RowCell: {
  VdbeCursor *pDest;              /* Cursor to write to */
  VdbeCursor *pSrc;               /* Cursor to read from */
  i64 iKey;                       /* Rowid value to insert with */
  assert( pOp[1].opcode==OP_Insert || pOp[1].opcode==OP_IdxInsert );
  assert( pOp[1].opcode==OP_Insert    || pOp->p3==0 );
  assert( pOp[1].opcode==OP_IdxInsert || pOp->p3>0 );
  assert( pOp[1].p5 & OPFLAG_PREFORMAT );
  pDest = p->apCsr[pOp->p1];
  pSrc = p->apCsr[pOp->p2];
  iKey = pOp->p3 ? aMem[pOp->p3].u.i : 0;
  rc = sqlite3BtreeTransferRow(pDest->uc.pCursor, pSrc->uc.pCursor, iKey);
  if( rc!=SQLITE_OK ) goto abort_due_to_error;
  break;
};

/* Opcode: Delete P1 P2 P3 P4 P5
**
** Delete the record at which the P1 cursor is currently pointing.
**
** If the OPFLAG_SAVEPOSITION bit of the P5 parameter is set, then
** the cursor will be left pointing at  either the next or the previous
91371
91372
91373
91374
91375
91376
91377
91378
91379
91380
91381
91382
91383
91384
91385
91386
91387
91388
91389
91390
91391
91392
91393
91394
91395
91396

  assert( pOp->p1>=0 && pOp->p1<p->nCursor );
  pC = p->apCsr[pOp->p1];
  sqlite3VdbeIncrWriteCounter(p, pC);
  assert( pC!=0 );
  assert( !isSorter(pC) );
  pIn2 = &aMem[pOp->p2];
  assert( pIn2->flags & MEM_Blob );
  if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
  assert( pC->eCurType==CURTYPE_BTREE );
  assert( pC->isTable==0 );
  rc = ExpandBlob(pIn2);
  if( rc ) goto abort_due_to_error;
  x.nKey = pIn2->n;
  x.pKey = pIn2->z;
  x.aMem = aMem + pOp->p3;
  x.nMem = (u16)pOp->p4.i;
  rc = sqlite3BtreeInsert(pC->uc.pCursor, &x,
       (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION)),
      ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0)
      );
  assert( pC->deferredMoveto==0 );
  pC->cacheStatus = CACHE_STALE;
  if( rc) goto abort_due_to_error;
  break;
}







|










|







91642
91643
91644
91645
91646
91647
91648
91649
91650
91651
91652
91653
91654
91655
91656
91657
91658
91659
91660
91661
91662
91663
91664
91665
91666
91667

  assert( pOp->p1>=0 && pOp->p1<p->nCursor );
  pC = p->apCsr[pOp->p1];
  sqlite3VdbeIncrWriteCounter(p, pC);
  assert( pC!=0 );
  assert( !isSorter(pC) );
  pIn2 = &aMem[pOp->p2];
  assert( (pIn2->flags & MEM_Blob) || (pOp->p5 & OPFLAG_PREFORMAT) );
  if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++;
  assert( pC->eCurType==CURTYPE_BTREE );
  assert( pC->isTable==0 );
  rc = ExpandBlob(pIn2);
  if( rc ) goto abort_due_to_error;
  x.nKey = pIn2->n;
  x.pKey = pIn2->z;
  x.aMem = aMem + pOp->p3;
  x.nMem = (u16)pOp->p4.i;
  rc = sqlite3BtreeInsert(pC->uc.pCursor, &x,
       (pOp->p5 & (OPFLAG_APPEND|OPFLAG_SAVEPOSITION|OPFLAG_PREFORMAT)),
      ((pOp->p5 & OPFLAG_USESEEKRESULT) ? pC->seekResult : 0)
      );
  assert( pC->deferredMoveto==0 );
  pC->cacheStatus = CACHE_STALE;
  if( rc) goto abort_due_to_error;
  break;
}
97973
97974
97975
97976
97977
97978
97979
97980
97981
97982
97983
97984
97985
97986
97987
97988
97989
97990
97991
97992
97993
97994
97995
97996
97997
97998

97999
98000
98001
98002
98003
98004
98005


#if !defined(SQLITE_OMIT_WINDOWFUNC)
/*
** Walk all expressions linked into the list of Window objects passed
** as the second argument.
*/
static int walkWindowList(Walker *pWalker, Window *pList){
  Window *pWin;
  for(pWin=pList; pWin; pWin=pWin->pNextWin){
    int rc;
    rc = sqlite3WalkExprList(pWalker, pWin->pOrderBy);
    if( rc ) return WRC_Abort;
    rc = sqlite3WalkExprList(pWalker, pWin->pPartition);
    if( rc ) return WRC_Abort;
    rc = sqlite3WalkExpr(pWalker, pWin->pFilter);
    if( rc ) return WRC_Abort;

    /* The next two are purely for calls to sqlite3RenameExprUnmap()
    ** within sqlite3WindowOffsetExpr().  Because of constraints imposed
    ** by sqlite3WindowOffsetExpr(), they can never fail.  The results do
    ** not matter anyhow. */
    rc = sqlite3WalkExpr(pWalker, pWin->pStart);
    if( NEVER(rc) ) return WRC_Abort;
    rc = sqlite3WalkExpr(pWalker, pWin->pEnd);
    if( NEVER(rc) ) return WRC_Abort;

  }
  return WRC_Continue;
}
#endif

/*
** Walk an expression tree.  Invoke the callback once for each node







|


















>







98244
98245
98246
98247
98248
98249
98250
98251
98252
98253
98254
98255
98256
98257
98258
98259
98260
98261
98262
98263
98264
98265
98266
98267
98268
98269
98270
98271
98272
98273
98274
98275
98276
98277


#if !defined(SQLITE_OMIT_WINDOWFUNC)
/*
** Walk all expressions linked into the list of Window objects passed
** as the second argument.
*/
static int walkWindowList(Walker *pWalker, Window *pList, int bOneOnly){
  Window *pWin;
  for(pWin=pList; pWin; pWin=pWin->pNextWin){
    int rc;
    rc = sqlite3WalkExprList(pWalker, pWin->pOrderBy);
    if( rc ) return WRC_Abort;
    rc = sqlite3WalkExprList(pWalker, pWin->pPartition);
    if( rc ) return WRC_Abort;
    rc = sqlite3WalkExpr(pWalker, pWin->pFilter);
    if( rc ) return WRC_Abort;

    /* The next two are purely for calls to sqlite3RenameExprUnmap()
    ** within sqlite3WindowOffsetExpr().  Because of constraints imposed
    ** by sqlite3WindowOffsetExpr(), they can never fail.  The results do
    ** not matter anyhow. */
    rc = sqlite3WalkExpr(pWalker, pWin->pStart);
    if( NEVER(rc) ) return WRC_Abort;
    rc = sqlite3WalkExpr(pWalker, pWin->pEnd);
    if( NEVER(rc) ) return WRC_Abort;
    if( bOneOnly ) break;
  }
  return WRC_Continue;
}
#endif

/*
** Walk an expression tree.  Invoke the callback once for each node
98039
98040
98041
98042
98043
98044
98045
98046
98047
98048
98049
98050
98051
98052
98053
        if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort;
      }else{
        if( pExpr->x.pList ){
          if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort;
        }
#ifndef SQLITE_OMIT_WINDOWFUNC
        if( ExprHasProperty(pExpr, EP_WinFunc) ){
          if( walkWindowList(pWalker, pExpr->y.pWin) ) return WRC_Abort;
        }
#endif
      }
    }
    break;
  }
  return WRC_Continue;







|







98311
98312
98313
98314
98315
98316
98317
98318
98319
98320
98321
98322
98323
98324
98325
        if( sqlite3WalkSelect(pWalker, pExpr->x.pSelect) ) return WRC_Abort;
      }else{
        if( pExpr->x.pList ){
          if( sqlite3WalkExprList(pWalker, pExpr->x.pList) ) return WRC_Abort;
        }
#ifndef SQLITE_OMIT_WINDOWFUNC
        if( ExprHasProperty(pExpr, EP_WinFunc) ){
          if( walkWindowList(pWalker, pExpr->y.pWin, 1) ) return WRC_Abort;
        }
#endif
      }
    }
    break;
  }
  return WRC_Continue;
98086
98087
98088
98089
98090
98091
98092
98093
98094
98095
98096
98097
98098
98099
98100
  if( sqlite3WalkExpr(pWalker, p->pLimit) ) return WRC_Abort;
#if !defined(SQLITE_OMIT_WINDOWFUNC) && !defined(SQLITE_OMIT_ALTERTABLE)
  {
    Parse *pParse = pWalker->pParse;
    if( pParse && IN_RENAME_OBJECT ){
      /* The following may return WRC_Abort if there are unresolvable
      ** symbols (e.g. a table that does not exist) in a window definition. */
      int rc = walkWindowList(pWalker, p->pWinDefn);
      return rc;
    }
  }
#endif
  return WRC_Continue;
}








|







98358
98359
98360
98361
98362
98363
98364
98365
98366
98367
98368
98369
98370
98371
98372
  if( sqlite3WalkExpr(pWalker, p->pLimit) ) return WRC_Abort;
#if !defined(SQLITE_OMIT_WINDOWFUNC) && !defined(SQLITE_OMIT_ALTERTABLE)
  {
    Parse *pParse = pWalker->pParse;
    if( pParse && IN_RENAME_OBJECT ){
      /* The following may return WRC_Abort if there are unresolvable
      ** symbols (e.g. a table that does not exist) in a window definition. */
      int rc = walkWindowList(pWalker, p->pWinDefn, 0);
      return rc;
    }
  }
#endif
  return WRC_Continue;
}

100256
100257
100258
100259
100260
100261
100262











100263
100264
100265
100266
100267
100268
100269
100270
*/
SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(
  Parse *pParse,           /* Parsing context */
  Expr *pExpr,             /* Add the "COLLATE" clause to this expression */
  const Token *pCollName,  /* Name of collating sequence */
  int dequote              /* True to dequote pCollName */
){











  if( pCollName->n>0 ){
    Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLLATE, pCollName, dequote);
    if( pNew ){
      pNew->pLeft = pExpr;
      pNew->flags |= EP_Collate|EP_Skip;
      pExpr = pNew;
    }
  }







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







100528
100529
100530
100531
100532
100533
100534
100535
100536
100537
100538
100539
100540
100541
100542
100543
100544
100545
100546
100547
100548
100549
100550
100551
100552
100553
*/
SQLITE_PRIVATE Expr *sqlite3ExprAddCollateToken(
  Parse *pParse,           /* Parsing context */
  Expr *pExpr,             /* Add the "COLLATE" clause to this expression */
  const Token *pCollName,  /* Name of collating sequence */
  int dequote              /* True to dequote pCollName */
){
  assert( pExpr!=0 || pParse->db->mallocFailed );
  if( pExpr==0 ) return 0;
  if( pExpr->op==TK_VECTOR ){
    ExprList *pList = pExpr->x.pList;
    if( ALWAYS(pList!=0) ){
      int i;
      for(i=0; i<pList->nExpr; i++){
        pList->a[i].pExpr = sqlite3ExprAddCollateToken(pParse,pList->a[i].pExpr,
                                                       pCollName, dequote);
      }
    }
  }else if( pCollName->n>0 ){
    Expr *pNew = sqlite3ExprAlloc(pParse->db, TK_COLLATE, pCollName, dequote);
    if( pNew ){
      pNew->pLeft = pExpr;
      pNew->flags |= EP_Collate|EP_Skip;
      pExpr = pNew;
    }
  }
106632
106633
106634
106635
106636
106637
106638


106639
106640

106641
106642
106643
106644
106645
106646
106647
106648
106649
106650
  if( zCol ){
    char *zEnd = &zCol[pColDef->n-1];
    u32 savedDbFlags = db->mDbFlags;
    while( zEnd>zCol && (*zEnd==';' || sqlite3Isspace(*zEnd)) ){
      *zEnd-- = '\0';
    }
    db->mDbFlags |= DBFLAG_PreferBuiltin;


    sqlite3NestedParse(pParse,
        "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET "

          "sql = substr(sql,1,%d) || ', ' || %Q || substr(sql,%d) "
        "WHERE type = 'table' AND name = %Q",
      zDb, pNew->addColOffset, zCol, pNew->addColOffset+1,
      zTab
    );
    sqlite3DbFree(db, zCol);
    db->mDbFlags = savedDbFlags;
  }

  /* Make sure the schema version is at least 3.  But do not upgrade







>
>


>
|

|







106915
106916
106917
106918
106919
106920
106921
106922
106923
106924
106925
106926
106927
106928
106929
106930
106931
106932
106933
106934
106935
106936
  if( zCol ){
    char *zEnd = &zCol[pColDef->n-1];
    u32 savedDbFlags = db->mDbFlags;
    while( zEnd>zCol && (*zEnd==';' || sqlite3Isspace(*zEnd)) ){
      *zEnd-- = '\0';
    }
    db->mDbFlags |= DBFLAG_PreferBuiltin;
    /* substr() operations on characters, but addColOffset is in bytes. So we
    ** have to use printf() to translate between these units: */
    sqlite3NestedParse(pParse,
        "UPDATE \"%w\"." DFLT_SCHEMA_TABLE " SET "
          "sql = printf('%%.%ds, ',sql) || %Q"
          " || substr(sql,1+length(printf('%%.%ds',sql))) "
        "WHERE type = 'table' AND name = %Q",
      zDb, pNew->addColOffset, zCol, pNew->addColOffset,
      zTab
    );
    sqlite3DbFree(db, zCol);
    db->mDbFlags = savedDbFlags;
  }

  /* Make sure the schema version is at least 3.  But do not upgrade
110796
110797
110798
110799
110800
110801
110802
110803
110804
110805
110806
110807
110808
110809
110810
110811
110812
110813
110814
  sqlite3 *db = pParse->db;
  int rc;

  /* Don't do any authorization checks if the database is initialising
  ** or if the parser is being invoked from within sqlite3_declare_vtab.
  */
  assert( !IN_RENAME_OBJECT || db->xAuth==0 );
  if( db->init.busy || IN_SPECIAL_PARSE ){
    return SQLITE_OK;
  }

  if( db->xAuth==0 ){
    return SQLITE_OK;
  }

  /* EVIDENCE-OF: R-43249-19882 The third through sixth parameters to the
  ** callback are either NULL pointers or zero-terminated strings that
  ** contain additional details about the action to be authorized.
  **







<
<
<
<
|







111082
111083
111084
111085
111086
111087
111088




111089
111090
111091
111092
111093
111094
111095
111096
  sqlite3 *db = pParse->db;
  int rc;

  /* Don't do any authorization checks if the database is initialising
  ** or if the parser is being invoked from within sqlite3_declare_vtab.
  */
  assert( !IN_RENAME_OBJECT || db->xAuth==0 );




  if( db->xAuth==0 || db->init.busy || IN_SPECIAL_PARSE ){
    return SQLITE_OK;
  }

  /* EVIDENCE-OF: R-43249-19882 The third through sixth parameters to the
  ** callback are either NULL pointers or zero-terminated strings that
  ** contain additional details about the action to be authorized.
  **
111006
111007
111008
111009
111010
111011
111012




111013
111014
111015
111016
111017
111018
111019
    if( pParse->rc==SQLITE_OK ) pParse->rc = SQLITE_ERROR;
    return;
  }

  /* Begin by generating some termination code at the end of the
  ** vdbe program
  */




  v = sqlite3GetVdbe(pParse);
  assert( !pParse->isMultiWrite
       || sqlite3VdbeAssertMayAbort(v, pParse->mayAbort));
  if( v ){
    sqlite3VdbeAddOp0(v, OP_Halt);

#if SQLITE_USER_AUTHENTICATION







>
>
>
>







111288
111289
111290
111291
111292
111293
111294
111295
111296
111297
111298
111299
111300
111301
111302
111303
111304
111305
    if( pParse->rc==SQLITE_OK ) pParse->rc = SQLITE_ERROR;
    return;
  }

  /* Begin by generating some termination code at the end of the
  ** vdbe program
  */
  if( pParse->pVdbe==0 && db->init.busy ){
    pParse->rc = SQLITE_DONE;
    return;
  }
  v = sqlite3GetVdbe(pParse);
  assert( !pParse->isMultiWrite
       || sqlite3VdbeAssertMayAbort(v, pParse->mayAbort));
  if( v ){
    sqlite3VdbeAddOp0(v, OP_Halt);

#if SQLITE_USER_AUTHENTICATION
112118
112119
112120
112121
112122
112123
112124


112125
112126
112127
112128
112129
112130
112131
112132
112133
112134
112135

112136
112137
112138
112139
112140
112141
112142
112143
112144
112145
112146
112147
112148
112149
112150
112151
112152
112153
112154
112155
112156
112157
112158
112159
112160
112161
112162
SQLITE_PRIVATE void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){
  Table *p;
  int i;
  char *z;
  char *zType;
  Column *pCol;
  sqlite3 *db = pParse->db;


  if( (p = pParse->pNewTable)==0 ) return;
  if( p->nCol+1>db->aLimit[SQLITE_LIMIT_COLUMN] ){
    sqlite3ErrorMsg(pParse, "too many columns on %s", p->zName);
    return;
  }
  z = sqlite3DbMallocRaw(db, pName->n + pType->n + 2);
  if( z==0 ) return;
  if( IN_RENAME_OBJECT ) sqlite3RenameTokenMap(pParse, (void*)z, pName);
  memcpy(z, pName->z, pName->n);
  z[pName->n] = 0;
  sqlite3Dequote(z);

  for(i=0; i<p->nCol; i++){
    if( sqlite3_stricmp(z, p->aCol[i].zName)==0 ){
      sqlite3ErrorMsg(pParse, "duplicate column name: %s", z);
      sqlite3DbFree(db, z);
      return;
    }
  }
  if( (p->nCol & 0x7)==0 ){
    Column *aNew;
    aNew = sqlite3DbRealloc(db,p->aCol,(p->nCol+8)*sizeof(p->aCol[0]));
    if( aNew==0 ){
      sqlite3DbFree(db, z);
      return;
    }
    p->aCol = aNew;
  }
  pCol = &p->aCol[p->nCol];
  memset(pCol, 0, sizeof(p->aCol[0]));
  pCol->zName = z;
  pCol->hName = sqlite3StrIHash(z);
  sqlite3ColumnPropertiesFromName(p, pCol);

  if( pType->n==0 ){
    /* If there is no type specified, columns have the default affinity
    ** 'BLOB' with a default size of 4 bytes. */
    pCol->affinity = SQLITE_AFF_BLOB;
    pCol->szEst = 1;







>
>











>

|

















|







112404
112405
112406
112407
112408
112409
112410
112411
112412
112413
112414
112415
112416
112417
112418
112419
112420
112421
112422
112423
112424
112425
112426
112427
112428
112429
112430
112431
112432
112433
112434
112435
112436
112437
112438
112439
112440
112441
112442
112443
112444
112445
112446
112447
112448
112449
112450
112451
SQLITE_PRIVATE void sqlite3AddColumn(Parse *pParse, Token *pName, Token *pType){
  Table *p;
  int i;
  char *z;
  char *zType;
  Column *pCol;
  sqlite3 *db = pParse->db;
  u8 hName;

  if( (p = pParse->pNewTable)==0 ) return;
  if( p->nCol+1>db->aLimit[SQLITE_LIMIT_COLUMN] ){
    sqlite3ErrorMsg(pParse, "too many columns on %s", p->zName);
    return;
  }
  z = sqlite3DbMallocRaw(db, pName->n + pType->n + 2);
  if( z==0 ) return;
  if( IN_RENAME_OBJECT ) sqlite3RenameTokenMap(pParse, (void*)z, pName);
  memcpy(z, pName->z, pName->n);
  z[pName->n] = 0;
  sqlite3Dequote(z);
  hName = sqlite3StrIHash(z);
  for(i=0; i<p->nCol; i++){
    if( p->aCol[i].hName==hName && sqlite3StrICmp(z, p->aCol[i].zName)==0 ){
      sqlite3ErrorMsg(pParse, "duplicate column name: %s", z);
      sqlite3DbFree(db, z);
      return;
    }
  }
  if( (p->nCol & 0x7)==0 ){
    Column *aNew;
    aNew = sqlite3DbRealloc(db,p->aCol,(p->nCol+8)*sizeof(p->aCol[0]));
    if( aNew==0 ){
      sqlite3DbFree(db, z);
      return;
    }
    p->aCol = aNew;
  }
  pCol = &p->aCol[p->nCol];
  memset(pCol, 0, sizeof(p->aCol[0]));
  pCol->zName = z;
  pCol->hName = hName;
  sqlite3ColumnPropertiesFromName(p, pCol);

  if( pType->n==0 ){
    /* If there is no type specified, columns have the default affinity
    ** 'BLOB' with a default size of 4 bytes. */
    pCol->affinity = SQLITE_AFF_BLOB;
    pCol->szEst = 1;
113424
113425
113426
113427
113428
113429
113430
113431
113432
113433
113434
113435
113436
113437
113438
      const char *zName = (const char *)pParse->sNameToken.z;
      int nName;
      assert( !pSelect && pCons && pEnd );
      if( pCons->z==0 ){
        pCons = pEnd;
      }
      nName = (int)((const char *)pCons->z - zName);
      p->addColOffset = 13 + sqlite3Utf8CharLen(zName, nName);
    }
#endif
  }
}

#ifndef SQLITE_OMIT_VIEW
/*







|







113713
113714
113715
113716
113717
113718
113719
113720
113721
113722
113723
113724
113725
113726
113727
      const char *zName = (const char *)pParse->sNameToken.z;
      int nName;
      assert( !pSelect && pCons && pEnd );
      if( pCons->z==0 ){
        pCons = pEnd;
      }
      nName = (int)((const char *)pCons->z - zName);
      p->addColOffset = 13 + nName;
    }
#endif
  }
}

#ifndef SQLITE_OMIT_VIEW
/*
119439
119440
119441
119442
119443
119444
119445




































































































































































































119446
119447
119448
119449
119450
119451
119452
    if( zEscape[0]==aWc[1] ) return 0;
    aWc[3] = zEscape[0];
  }

  *pIsNocase = (pDef->funcFlags & SQLITE_FUNC_CASE)==0;
  return 1;
}





































































































































































































/*
** All of the FuncDef structures in the aBuiltinFunc[] array above
** to the global function hash table.  This occurs at start-time (as
** a consequence of calling sqlite3_initialize()).
**
** After this routine runs







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







119728
119729
119730
119731
119732
119733
119734
119735
119736
119737
119738
119739
119740
119741
119742
119743
119744
119745
119746
119747
119748
119749
119750
119751
119752
119753
119754
119755
119756
119757
119758
119759
119760
119761
119762
119763
119764
119765
119766
119767
119768
119769
119770
119771
119772
119773
119774
119775
119776
119777
119778
119779
119780
119781
119782
119783
119784
119785
119786
119787
119788
119789
119790
119791
119792
119793
119794
119795
119796
119797
119798
119799
119800
119801
119802
119803
119804
119805
119806
119807
119808
119809
119810
119811
119812
119813
119814
119815
119816
119817
119818
119819
119820
119821
119822
119823
119824
119825
119826
119827
119828
119829
119830
119831
119832
119833
119834
119835
119836
119837
119838
119839
119840
119841
119842
119843
119844
119845
119846
119847
119848
119849
119850
119851
119852
119853
119854
119855
119856
119857
119858
119859
119860
119861
119862
119863
119864
119865
119866
119867
119868
119869
119870
119871
119872
119873
119874
119875
119876
119877
119878
119879
119880
119881
119882
119883
119884
119885
119886
119887
119888
119889
119890
119891
119892
119893
119894
119895
119896
119897
119898
119899
119900
119901
119902
119903
119904
119905
119906
119907
119908
119909
119910
119911
119912
119913
119914
119915
119916
119917
119918
119919
119920
119921
119922
119923
119924
119925
119926
119927
119928
119929
119930
119931
119932
119933
119934
119935
119936
119937
    if( zEscape[0]==aWc[1] ) return 0;
    aWc[3] = zEscape[0];
  }

  *pIsNocase = (pDef->funcFlags & SQLITE_FUNC_CASE)==0;
  return 1;
}

/* Mathematical Constants */
#ifndef M_PI
# define M_PI   3.141592653589793238462643383279502884
#endif
#ifndef M_LN10
# define M_LN10 2.302585092994045684017991454684364208
#endif
#ifndef M_LN2
# define M_LN2  0.693147180559945309417232121458176568
#endif


/* Extra math functions that require linking with -lm
*/
#ifdef SQLITE_ENABLE_MATH_FUNCTIONS
/*
** Implementation SQL functions:
**
**   ceil(X)
**   ceiling(X)
**   floor(X)
**
** The sqlite3_user_data() pointer is a pointer to the libm implementation
** of the underlying C function.
*/
static void ceilingFunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  assert( argc==1 );
  switch( sqlite3_value_numeric_type(argv[0]) ){
    case SQLITE_INTEGER: {
       sqlite3_result_int64(context, sqlite3_value_int64(argv[0]));
       break;
    }
    case SQLITE_FLOAT: {
       double (*x)(double) = (double(*)(double))sqlite3_user_data(context);
       sqlite3_result_double(context, x(sqlite3_value_double(argv[0])));
       break;
    }
    default: {
       break;
    }
  }
}

/*
** On some systems, ceil() and floor() are intrinsic function.  You are
** unable to take a pointer to these functions.  Hence, we here wrap them
** in our own actual functions.
*/
static double xCeil(double x){ return ceil(x); }
static double xFloor(double x){ return floor(x); }

/*
** Implementation of SQL functions:
**
**   ln(X)       - natural logarithm
**   log(X)      - log X base 10
**   log10(X)    - log X base 10
**   log(B,X)    - log X base B
*/
static void logFunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  double x, b, ans;
  assert( argc==1 || argc==2 );
  switch( sqlite3_value_numeric_type(argv[0]) ){
    case SQLITE_INTEGER:
    case SQLITE_FLOAT:
      x = sqlite3_value_double(argv[0]);
      if( x<0.0 ) return;
      break;
    default:
      return;
  }
  if( argc==2 ){
    switch( sqlite3_value_numeric_type(argv[0]) ){
      case SQLITE_INTEGER:
      case SQLITE_FLOAT:
        b = x;
        x = sqlite3_value_double(argv[1]);
        if( x<0.0 ) return;
        break;
     default:
        return;
    }
    ans = log(x)/log(b);
  }else{
    ans = log(x);
    switch( SQLITE_PTR_TO_INT(sqlite3_user_data(context)) ){
      case 1:
        /* Convert from natural logarithm to log base 10 */
        ans *= 1.0/M_LN10;
        break;
      case 2:
        /* Convert from natural logarithm to log base 2 */
        ans *= 1.0/M_LN2;
        break;
      default:
        break;
    }
  }
  sqlite3_result_double(context, ans);
}

/*
** Functions to converts degrees to radians and radians to degrees.
*/
static double degToRad(double x){ return x*(M_PI/180.0); }
static double radToDeg(double x){ return x*(180.0/M_PI); }

/*
** Implementation of 1-argument SQL math functions:
**
**   exp(X)  - Compute e to the X-th power
*/
static void math1Func(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  int type0;
  double v0, ans;
  double (*x)(double);
  assert( argc==1 );
  type0 = sqlite3_value_numeric_type(argv[0]);
  if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return;
  v0 = sqlite3_value_double(argv[0]);
  x = (double(*)(double))sqlite3_user_data(context);
  ans = x(v0);
  sqlite3_result_double(context, ans);
}

/*
** Implementation of 2-argument SQL math functions:
**
**   power(X,Y)  - Compute X to the Y-th power
*/
static void math2Func(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  int type0, type1;
  double v0, v1, ans;
  double (*x)(double,double);
  assert( argc==2 );
  type0 = sqlite3_value_numeric_type(argv[0]);
  if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return;
  type1 = sqlite3_value_numeric_type(argv[1]);
  if( type1!=SQLITE_INTEGER && type1!=SQLITE_FLOAT ) return;
  v0 = sqlite3_value_double(argv[0]);
  v1 = sqlite3_value_double(argv[1]);
  x = (double(*)(double,double))sqlite3_user_data(context);
  ans = x(v0, v1);
  sqlite3_result_double(context, ans);
}

/*
** Implementation of 2-argument SQL math functions:
**
**   power(X,Y)  - Compute X to the Y-th power
*/
static void piFunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  assert( argc==0 );
  sqlite3_result_double(context, M_PI);
}

#endif /* SQLITE_ENABLE_MATH_FUNCTIONS */

/*
** Implementation of sign(X) function.
*/
static void signFunc(
  sqlite3_context *context,
  int argc,
  sqlite3_value **argv
){
  int type0;
  double x;
  UNUSED_PARAMETER(argc);
  assert( argc==1 );
  type0 = sqlite3_value_numeric_type(argv[0]);
  if( type0!=SQLITE_INTEGER && type0!=SQLITE_FLOAT ) return;
  x = sqlite3_value_double(argv[0]);
  sqlite3_result_int(context, x<0.0 ? -1 : x>0.0 ? +1 : 0);
}

/*
** All of the FuncDef structures in the aBuiltinFunc[] array above
** to the global function hash table.  This occurs at start-time (as
** a consequence of calling sqlite3_initialize()).
**
** After this routine runs
119558
119559
119560
119561
119562
119563
119564





































119565
119566
119567
119568
119569
119570
119571
    LIKEFUNC(like, 3, &likeInfoNorm, SQLITE_FUNC_LIKE),
#endif
#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
    FUNCTION(unknown,           -1, 0, 0, unknownFunc      ),
#endif
    FUNCTION(coalesce,           1, 0, 0, 0                ),
    FUNCTION(coalesce,           0, 0, 0, 0                ),





































    INLINE_FUNC(coalesce,       -1, INLINEFUNC_coalesce, 0 ),
    INLINE_FUNC(iif,             3, INLINEFUNC_iif,      0 ),
  };
#ifndef SQLITE_OMIT_ALTERTABLE
  sqlite3AlterFunctions();
#endif
  sqlite3WindowFunctions();







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







120043
120044
120045
120046
120047
120048
120049
120050
120051
120052
120053
120054
120055
120056
120057
120058
120059
120060
120061
120062
120063
120064
120065
120066
120067
120068
120069
120070
120071
120072
120073
120074
120075
120076
120077
120078
120079
120080
120081
120082
120083
120084
120085
120086
120087
120088
120089
120090
120091
120092
120093
    LIKEFUNC(like, 3, &likeInfoNorm, SQLITE_FUNC_LIKE),
#endif
#ifdef SQLITE_ENABLE_UNKNOWN_SQL_FUNCTION
    FUNCTION(unknown,           -1, 0, 0, unknownFunc      ),
#endif
    FUNCTION(coalesce,           1, 0, 0, 0                ),
    FUNCTION(coalesce,           0, 0, 0, 0                ),
#ifdef SQLITE_ENABLE_MATH_FUNCTIONS
    MFUNCTION(ceil,              1, xCeil,     ceilingFunc ),
    MFUNCTION(ceiling,           1, xCeil,     ceilingFunc ),
    MFUNCTION(floor,             1, xFloor,    ceilingFunc ),
#if SQLITE_HAVE_C99_MATH_FUNCS
    MFUNCTION(trunc,             1, trunc,     ceilingFunc ),
#endif
    FUNCTION(ln,                 1, 0, 0,      logFunc     ),
    FUNCTION(log,                1, 1, 0,      logFunc     ),
    FUNCTION(log10,              1, 1, 0,      logFunc     ),
    FUNCTION(log2,               1, 2, 0,      logFunc     ),
    FUNCTION(log,                2, 0, 0,      logFunc     ),
    MFUNCTION(exp,               1, exp,       math1Func   ),
    MFUNCTION(pow,               2, pow,       math2Func   ),
    MFUNCTION(power,             2, pow,       math2Func   ),
    MFUNCTION(mod,               2, fmod,      math2Func   ),
    MFUNCTION(acos,              1, acos,      math1Func   ),
    MFUNCTION(asin,              1, asin,      math1Func   ),
    MFUNCTION(atan,              1, atan,      math1Func   ),
    MFUNCTION(atan2,             2, atan2,     math2Func   ),
    MFUNCTION(cos,               1, cos,       math1Func   ),
    MFUNCTION(sin,               1, sin,       math1Func   ),
    MFUNCTION(tan,               1, tan,       math1Func   ),
    MFUNCTION(cosh,              1, cosh,      math1Func   ),
    MFUNCTION(sinh,              1, sinh,      math1Func   ),
    MFUNCTION(tanh,              1, tanh,      math1Func   ),
#if SQLITE_HAVE_C99_MATH_FUNCS
    MFUNCTION(acosh,             1, acosh,     math1Func   ),
    MFUNCTION(asinh,             1, asinh,     math1Func   ),
    MFUNCTION(atanh,             1, atanh,     math1Func   ),
#endif
    MFUNCTION(sqrt,              1, sqrt,      math1Func   ),
    MFUNCTION(radians,           1, degToRad,  math1Func   ),
    MFUNCTION(degrees,           1, radToDeg,  math1Func   ),
    FUNCTION(pi,                 0, 0, 0,      piFunc      ),
#endif /* SQLITE_ENABLE_MATH_FUNCTIONS */
    FUNCTION(sign,               1, 0, 0,      signFunc    ),
    INLINE_FUNC(coalesce,       -1, INLINEFUNC_coalesce, 0 ),
    INLINE_FUNC(iif,             3, INLINEFUNC_iif,      0 ),
  };
#ifndef SQLITE_OMIT_ALTERTABLE
  sqlite3AlterFunctions();
#endif
  sqlite3WindowFunctions();
121411
121412
121413
121414
121415
121416
121417


121418
121419
121420
121421
121422
121423
121424
121425
      return 0;
    }

    pInfo = pToplevel->pAinc;
    while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; }
    if( pInfo==0 ){
      pInfo = sqlite3DbMallocRawNN(pParse->db, sizeof(*pInfo));


      if( pInfo==0 ) return 0;
      pInfo->pNext = pToplevel->pAinc;
      pToplevel->pAinc = pInfo;
      pInfo->pTab = pTab;
      pInfo->iDb = iDb;
      pToplevel->nMem++;                  /* Register to hold name of table */
      pInfo->regCtr = ++pToplevel->nMem;  /* Max rowid register */
      pToplevel->nMem +=2;       /* Rowid in sqlite_sequence + orig max val */







>
>
|







121933
121934
121935
121936
121937
121938
121939
121940
121941
121942
121943
121944
121945
121946
121947
121948
121949
      return 0;
    }

    pInfo = pToplevel->pAinc;
    while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; }
    if( pInfo==0 ){
      pInfo = sqlite3DbMallocRawNN(pParse->db, sizeof(*pInfo));
      sqlite3ParserAddCleanup(pToplevel, sqlite3DbFree, pInfo);
      testcase( pParse->earlyCleanup );
      if( pParse->db->mallocFailed ) return 0;
      pInfo->pNext = pToplevel->pAinc;
      pToplevel->pAinc = pInfo;
      pInfo->pTab = pTab;
      pInfo->iDb = iDb;
      pToplevel->nMem++;                  /* Register to hold name of table */
      pInfo->regCtr = ++pToplevel->nMem;  /* Max rowid register */
      pToplevel->nMem +=2;       /* Rowid in sqlite_sequence + orig max val */
122016
122017
122018
122019
122020
122021
122022

122023
122024
122025
122026
122027
122028
122029
122030
122031
122032
122033
122034
122035


122036
122037
122038
122039
122040
122041
122042


122043
122044
122045
122046
122047
122048
122049
      aRegIdx[i] = ++pParse->nMem;
      pParse->nMem += pIdx->nColumn;
    }
    aRegIdx[i] = ++pParse->nMem;  /* Register to store the table record */
  }
#ifndef SQLITE_OMIT_UPSERT
  if( pUpsert ){

    if( IsVirtual(pTab) ){
      sqlite3ErrorMsg(pParse, "UPSERT not implemented for virtual table \"%s\"",
              pTab->zName);
      goto insert_cleanup;
    }
    if( pTab->pSelect ){
      sqlite3ErrorMsg(pParse, "cannot UPSERT a view");
      goto insert_cleanup;
    }
    if( sqlite3HasExplicitNulls(pParse, pUpsert->pUpsertTarget) ){
      goto insert_cleanup;
    }
    pTabList->a[0].iCursor = iDataCur;


    pUpsert->pUpsertSrc = pTabList;
    pUpsert->regData = regData;
    pUpsert->iDataCur = iDataCur;
    pUpsert->iIdxCur = iIdxCur;
    if( pUpsert->pUpsertTarget ){
      sqlite3UpsertAnalyzeTarget(pParse, pTabList, pUpsert);
    }


  }
#endif


  /* This is the top of the main insertion loop */
  if( useTempTable ){
    /* This block codes the top of loop only.  The complete loop is the







>













>
>
|
|
|
|
|
|
|
>
>







122540
122541
122542
122543
122544
122545
122546
122547
122548
122549
122550
122551
122552
122553
122554
122555
122556
122557
122558
122559
122560
122561
122562
122563
122564
122565
122566
122567
122568
122569
122570
122571
122572
122573
122574
122575
122576
122577
122578
      aRegIdx[i] = ++pParse->nMem;
      pParse->nMem += pIdx->nColumn;
    }
    aRegIdx[i] = ++pParse->nMem;  /* Register to store the table record */
  }
#ifndef SQLITE_OMIT_UPSERT
  if( pUpsert ){
    Upsert *pNx;
    if( IsVirtual(pTab) ){
      sqlite3ErrorMsg(pParse, "UPSERT not implemented for virtual table \"%s\"",
              pTab->zName);
      goto insert_cleanup;
    }
    if( pTab->pSelect ){
      sqlite3ErrorMsg(pParse, "cannot UPSERT a view");
      goto insert_cleanup;
    }
    if( sqlite3HasExplicitNulls(pParse, pUpsert->pUpsertTarget) ){
      goto insert_cleanup;
    }
    pTabList->a[0].iCursor = iDataCur;
    pNx = pUpsert;
    do{
      pNx->pUpsertSrc = pTabList;
      pNx->regData = regData;
      pNx->iDataCur = iDataCur;
      pNx->iIdxCur = iIdxCur;
      if( pNx->pUpsertTarget ){
        sqlite3UpsertAnalyzeTarget(pParse, pTabList, pNx);
      }
      pNx = pNx->pNextUpsert;
    }while( pNx!=0 );
  }
#endif


  /* This is the top of the main insertion loop */
  if( useTempTable ){
    /* This block codes the top of loop only.  The complete loop is the
122439
122440
122441
122442
122443
122444
122445
































































122446
122447
122448
122449
122450
122451
122452
  }
  testcase( w.eCode==0 );
  testcase( w.eCode==CKCNSTRNT_COLUMN );
  testcase( w.eCode==CKCNSTRNT_ROWID );
  testcase( w.eCode==(CKCNSTRNT_ROWID|CKCNSTRNT_COLUMN) );
  return w.eCode!=0;
}

































































/*
** Generate code to do constraint checks prior to an INSERT or an UPDATE
** on table pTab.
**
** The regNewData parameter is the first register in a range that contains
** the data to be inserted or the data after the update.  There will be







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







122968
122969
122970
122971
122972
122973
122974
122975
122976
122977
122978
122979
122980
122981
122982
122983
122984
122985
122986
122987
122988
122989
122990
122991
122992
122993
122994
122995
122996
122997
122998
122999
123000
123001
123002
123003
123004
123005
123006
123007
123008
123009
123010
123011
123012
123013
123014
123015
123016
123017
123018
123019
123020
123021
123022
123023
123024
123025
123026
123027
123028
123029
123030
123031
123032
123033
123034
123035
123036
123037
123038
123039
123040
123041
123042
123043
123044
123045
  }
  testcase( w.eCode==0 );
  testcase( w.eCode==CKCNSTRNT_COLUMN );
  testcase( w.eCode==CKCNSTRNT_ROWID );
  testcase( w.eCode==(CKCNSTRNT_ROWID|CKCNSTRNT_COLUMN) );
  return w.eCode!=0;
}

/*
** The sqlite3GenerateConstraintChecks() routine usually wants to visit
** the indexes of a table in the order provided in the Table->pIndex list.
** However, sometimes (rarely - when there is an upsert) it wants to visit
** the indexes in a different order.  The following data structures accomplish
** this.
**
** The IndexIterator object is used to walk through all of the indexes
** of a table in either Index.pNext order, or in some other order established
** by an array of IndexListTerm objects.
*/
typedef struct IndexListTerm IndexListTerm;
typedef struct IndexIterator IndexIterator;
struct IndexIterator {
  int eType;    /* 0 for Index.pNext list.  1 for an array of IndexListTerm */
  int i;        /* Index of the current item from the list */
  union {
    struct {    /* Use this object for eType==0: A Index.pNext list */
      Index *pIdx;   /* The current Index */
    } lx;
    struct {    /* Use this object for eType==1; Array of IndexListTerm */
      int nIdx;               /* Size of the array */
      IndexListTerm *aIdx;    /* Array of IndexListTerms */
    } ax;
  } u;
};

/* When IndexIterator.eType==1, then each index is an array of instances
** of the following object
*/
struct IndexListTerm {
  Index *p;  /* The index */
  int ix;    /* Which entry in the original Table.pIndex list is this index*/
};

/* Return the first index on the list */
static Index *indexIteratorFirst(IndexIterator *pIter, int *pIx){
  assert( pIter->i==0 );
  if( pIter->eType ){
    *pIx = pIter->u.ax.aIdx[0].ix;
    return pIter->u.ax.aIdx[0].p;
  }else{
    *pIx = 0;
    return pIter->u.lx.pIdx;
  }
}

/* Return the next index from the list.  Return NULL when out of indexes */
static Index *indexIteratorNext(IndexIterator *pIter, int *pIx){
  if( pIter->eType ){
    int i = ++pIter->i;
    if( i>=pIter->u.ax.nIdx ){
      *pIx = i;
      return 0;
    }
    *pIx = pIter->u.ax.aIdx[i].ix;
    return pIter->u.ax.aIdx[i].p;
  }else{
    ++(*pIx);
    pIter->u.lx.pIdx = pIter->u.lx.pIdx->pNext;
    return pIter->u.lx.pIdx;
  }
}

/*
** Generate code to do constraint checks prior to an INSERT or an UPDATE
** on table pTab.
**
** The regNewData parameter is the first register in a range that contains
** the data to be inserted or the data after the update.  There will be
122548
122549
122550
122551
122552
122553
122554
122555
122556
122557
122558
122559
122560
122561
122562
122563
122564
122565
122566
122567
122568
122569
122570
122571
122572
122573
122574
122575
122576

122577
122578
122579
122580
122581
122582
122583
  int ignoreDest,      /* Jump to this label on an OE_Ignore resolution */
  int *pbMayReplace,   /* OUT: Set to true if constraint may cause a replace */
  int *aiChng,         /* column i is unchanged if aiChng[i]<0 */
  Upsert *pUpsert      /* ON CONFLICT clauses, if any.  NULL otherwise */
){
  Vdbe *v;             /* VDBE under constrution */
  Index *pIdx;         /* Pointer to one of the indices */
  Index *pPk = 0;      /* The PRIMARY KEY index */
  sqlite3 *db;         /* Database connection */
  int i;               /* loop counter */
  int ix;              /* Index loop counter */
  int nCol;            /* Number of columns */
  int onError;         /* Conflict resolution strategy */
  int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
  int nPkField;        /* Number of fields in PRIMARY KEY. 1 for ROWID tables */
  Index *pUpIdx = 0;   /* Index to which to apply the upsert */
  u8 isUpdate;         /* True if this is an UPDATE operation */
  u8 bAffinityDone = 0;  /* True if the OP_Affinity operation has been run */
  int upsertBypass = 0;  /* Address of Goto to bypass upsert subroutine */
  int upsertJump = 0;    /* Address of Goto that jumps into upsert subroutine */
  int ipkTop = 0;        /* Top of the IPK uniqueness check */
  int ipkBottom = 0;     /* OP_Goto at the end of the IPK uniqueness check */
  /* Variables associated with retesting uniqueness constraints after
  ** replace triggers fire have run */
  int regTrigCnt;       /* Register used to count replace trigger invocations */
  int addrRecheck = 0;  /* Jump here to recheck all uniqueness constraints */
  int lblRecheckOk = 0; /* Each recheck jumps to this label if it passes */
  Trigger *pTrigger;    /* List of DELETE triggers on the table pTab */
  int nReplaceTrig = 0; /* Number of replace triggers coded */


  isUpdate = regOldData!=0;
  db = pParse->db;
  v = pParse->pVdbe;
  assert( v!=0 );
  assert( pTab->pSelect==0 );  /* This table is not a VIEW */
  nCol = pTab->nCol;







|







|
|

|
|









>







123141
123142
123143
123144
123145
123146
123147
123148
123149
123150
123151
123152
123153
123154
123155
123156
123157
123158
123159
123160
123161
123162
123163
123164
123165
123166
123167
123168
123169
123170
123171
123172
123173
123174
123175
123176
123177
  int ignoreDest,      /* Jump to this label on an OE_Ignore resolution */
  int *pbMayReplace,   /* OUT: Set to true if constraint may cause a replace */
  int *aiChng,         /* column i is unchanged if aiChng[i]<0 */
  Upsert *pUpsert      /* ON CONFLICT clauses, if any.  NULL otherwise */
){
  Vdbe *v;             /* VDBE under constrution */
  Index *pIdx;         /* Pointer to one of the indices */
  Index *pPk = 0;      /* The PRIMARY KEY index for WITHOUT ROWID tables */
  sqlite3 *db;         /* Database connection */
  int i;               /* loop counter */
  int ix;              /* Index loop counter */
  int nCol;            /* Number of columns */
  int onError;         /* Conflict resolution strategy */
  int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */
  int nPkField;        /* Number of fields in PRIMARY KEY. 1 for ROWID tables */
  Upsert *pUpsertClause = 0;  /* The specific ON CONFLICT clause for pIdx */
  u8 isUpdate;           /* True if this is an UPDATE operation */
  u8 bAffinityDone = 0;  /* True if the OP_Affinity operation has been run */
  int upsertIpkReturn = 0; /* Address of Goto at end of IPK uniqueness check */
  int upsertIpkDelay = 0;  /* Address of Goto to bypass initial IPK check */
  int ipkTop = 0;        /* Top of the IPK uniqueness check */
  int ipkBottom = 0;     /* OP_Goto at the end of the IPK uniqueness check */
  /* Variables associated with retesting uniqueness constraints after
  ** replace triggers fire have run */
  int regTrigCnt;       /* Register used to count replace trigger invocations */
  int addrRecheck = 0;  /* Jump here to recheck all uniqueness constraints */
  int lblRecheckOk = 0; /* Each recheck jumps to this label if it passes */
  Trigger *pTrigger;    /* List of DELETE triggers on the table pTab */
  int nReplaceTrig = 0; /* Number of replace triggers coded */
  IndexIterator sIdxIter;  /* Index iterator */

  isUpdate = regOldData!=0;
  db = pParse->db;
  v = pParse->pVdbe;
  assert( v!=0 );
  assert( pTab->pSelect==0 );  /* This table is not a VIEW */
  nCol = pTab->nCol;
122767
122768
122769
122770
122771
122772
122773
122774



122775
122776



122777
122778
122779
122780
122781




122782

122783
122784










122785
122786

























122787
122788
122789
122790
122791
122792
122793
  **        default conflict resolution strategy
  **   (C)  Unique index that do use OE_Replace by default.
  **
  ** The ordering of (2) and (3) is accomplished by making sure the linked
  ** list of indexes attached to a table puts all OE_Replace indexes last
  ** in the list.  See sqlite3CreateIndex() for where that happens.
  */




  if( pUpsert ){
    if( pUpsert->pUpsertTarget==0 ){



      /* An ON CONFLICT DO NOTHING clause, without a constraint-target.
      ** Make all unique constraint resolution be OE_Ignore */
      assert( pUpsert->pUpsertSet==0 );
      overrideError = OE_Ignore;
      pUpsert = 0;




    }else if( (pUpIdx = pUpsert->pUpsertIdx)!=0 ){

      /* If the constraint-target uniqueness check must be run first.
      ** Jump to that uniqueness check now */










      upsertJump = sqlite3VdbeAddOp0(v, OP_Goto);
      VdbeComment((v, "UPSERT constraint goes first"));

























    }
  }

  /* Determine if it is possible that triggers (either explicitly coded
  ** triggers or FK resolution actions) might run as a result of deletes
  ** that happen when OE_Replace conflict resolution occurs. (Call these
  ** "replace triggers".)  If any replace triggers run, we will need to







|
>
>
>


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







123361
123362
123363
123364
123365
123366
123367
123368
123369
123370
123371
123372
123373
123374
123375
123376
123377
123378

123379
123380
123381
123382
123383
123384
123385
123386
123387
123388
123389
123390
123391
123392
123393
123394
123395
123396
123397
123398
123399

123400
123401
123402
123403
123404
123405
123406
123407
123408
123409
123410
123411
123412
123413
123414
123415
123416
123417
123418
123419
123420
123421
123422
123423
123424
123425
123426
123427
123428
123429
123430
123431
  **        default conflict resolution strategy
  **   (C)  Unique index that do use OE_Replace by default.
  **
  ** The ordering of (2) and (3) is accomplished by making sure the linked
  ** list of indexes attached to a table puts all OE_Replace indexes last
  ** in the list.  See sqlite3CreateIndex() for where that happens.
  */
  sIdxIter.eType = 0;
  sIdxIter.i = 0;
  sIdxIter.u.ax.aIdx = 0;  /* Silence harmless compiler warning */
  sIdxIter.u.lx.pIdx = pTab->pIndex;
  if( pUpsert ){
    if( pUpsert->pUpsertTarget==0 ){
      /* There is just on ON CONFLICT clause and it has no constraint-target */
      assert( pUpsert->pNextUpsert==0 );
      if( pUpsert->isDoUpdate==0 ){
        /* A single ON CONFLICT DO NOTHING clause, without a constraint-target.
        ** Make all unique constraint resolution be OE_Ignore */

        overrideError = OE_Ignore;
        pUpsert = 0;
      }else{
        /* A single ON CONFLICT DO UPDATE.  Make all resolutions OE_Update */
        overrideError = OE_Update;
      }
    }else if( pTab->pIndex!=0 ){
      /* Otherwise, we'll need to run the IndexListTerm array version of the
      ** iterator to ensure that all of the ON CONFLICT conditions are
      ** checked first and in order. */
      int nIdx, jj;
      u64 nByte;
      Upsert *pTerm;
      u8 *bUsed;
      for(nIdx=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, nIdx++){
         assert( aRegIdx[nIdx]>0 );
      }
      sIdxIter.eType = 1;
      sIdxIter.u.ax.nIdx = nIdx;
      nByte = (sizeof(IndexListTerm)+1)*nIdx + nIdx;
      sIdxIter.u.ax.aIdx = sqlite3DbMallocZero(db, nByte);

      if( sIdxIter.u.ax.aIdx==0 ) return; /* OOM */
      bUsed = (u8*)&sIdxIter.u.ax.aIdx[nIdx];
      pUpsert->pToFree = sIdxIter.u.ax.aIdx;
      for(i=0, pTerm=pUpsert; pTerm; pTerm=pTerm->pNextUpsert){
        if( pTerm->pUpsertTarget==0 ) break;
        if( pTerm->pUpsertIdx==0 ) continue;  /* Skip ON CONFLICT for the IPK */
        jj = 0;
        pIdx = pTab->pIndex;
        while( ALWAYS(pIdx!=0) && pIdx!=pTerm->pUpsertIdx ){
           pIdx = pIdx->pNext;
           jj++;
        }
        if( bUsed[jj] ) continue; /* Duplicate ON CONFLICT clause ignored */
        bUsed[jj] = 1;
        sIdxIter.u.ax.aIdx[i].p = pIdx;
        sIdxIter.u.ax.aIdx[i].ix = jj;
        i++;
      }
      for(jj=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, jj++){
        if( bUsed[jj] ) continue;
        sIdxIter.u.ax.aIdx[i].p = pIdx;
        sIdxIter.u.ax.aIdx[i].ix = jj;
        i++;
      }
      assert( i==nIdx );
    }
  }

  /* Determine if it is possible that triggers (either explicitly coded
  ** triggers or FK resolution actions) might run as a result of deletes
  ** that happen when OE_Replace conflict resolution occurs. (Call these
  ** "replace triggers".)  If any replace triggers run, we will need to
122842
122843
122844
122845
122846
122847
122848
122849

122850

122851
122852
122853







122854
122855
122856
122857
122858
122859
122860
122861
122862
122863
122864
122865
122866
122867
122868
122869
122870
    if( overrideError!=OE_Default ){
      onError = overrideError;
    }else if( onError==OE_Default ){
      onError = OE_Abort;
    }

    /* figure out whether or not upsert applies in this case */
    if( pUpsert && pUpsert->pUpsertIdx==0 ){

      if( pUpsert->pUpsertSet==0 ){

        onError = OE_Ignore;  /* DO NOTHING is the same as INSERT OR IGNORE */
      }else{
        onError = OE_Update;  /* DO UPDATE */







      }
    }

    /* If the response to a rowid conflict is REPLACE but the response
    ** to some other UNIQUE constraint is FAIL or IGNORE, then we need
    ** to defer the running of the rowid conflict checking until after
    ** the UNIQUE constraints have run.
    */
    if( onError==OE_Replace      /* IPK rule is REPLACE */
     && onError!=overrideError   /* Rules for other contraints are different */
     && pTab->pIndex             /* There exist other constraints */
    ){
      ipkTop = sqlite3VdbeAddOp0(v, OP_Goto)+1;
      VdbeComment((v, "defer IPK REPLACE until last"));
    }

    if( isUpdate ){







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









|







123480
123481
123482
123483
123484
123485
123486
123487
123488
123489
123490
123491
123492
123493
123494
123495
123496
123497
123498
123499
123500
123501
123502
123503
123504
123505
123506
123507
123508
123509
123510
123511
123512
123513
123514
123515
123516
123517
    if( overrideError!=OE_Default ){
      onError = overrideError;
    }else if( onError==OE_Default ){
      onError = OE_Abort;
    }

    /* figure out whether or not upsert applies in this case */
    if( pUpsert ){
      pUpsertClause = sqlite3UpsertOfIndex(pUpsert,0);
      if( pUpsertClause!=0 ){
        if( pUpsertClause->isDoUpdate==0 ){
          onError = OE_Ignore;  /* DO NOTHING is the same as INSERT OR IGNORE */
        }else{
          onError = OE_Update;  /* DO UPDATE */
        }
      }
      if( pUpsertClause!=pUpsert ){
        /* The first ON CONFLICT clause has a conflict target other than
        ** the IPK.  We have to jump ahead to that first ON CONFLICT clause
        ** and then come back here and deal with the IPK afterwards */
        upsertIpkDelay = sqlite3VdbeAddOp0(v, OP_Goto);
      }
    }

    /* If the response to a rowid conflict is REPLACE but the response
    ** to some other UNIQUE constraint is FAIL or IGNORE, then we need
    ** to defer the running of the rowid conflict checking until after
    ** the UNIQUE constraints have run.
    */
    if( onError==OE_Replace      /* IPK rule is REPLACE */
     && onError!=overrideError   /* Rules for other constraints are different */
     && pTab->pIndex             /* There exist other constraints */
    ){
      ipkTop = sqlite3VdbeAddOp0(v, OP_Goto)+1;
      VdbeComment((v, "defer IPK REPLACE until last"));
    }

    if( isUpdate ){
122953
122954
122955
122956
122957
122958
122959


122960
122961
122962
122963
122964
122965
122966
122967
122968
122969
122970
122971
122972

122973


122974
122975
122976
122977
122978
122979
122980
122981
122982
122983
122984
122985
122986
122987
122988


122989
122990
122991
122992
122993
122994
122995
122996
      case OE_Ignore: {
        testcase( onError==OE_Ignore );
        sqlite3VdbeGoto(v, ignoreDest);
        break;
      }
    }
    sqlite3VdbeResolveLabel(v, addrRowidOk);


    if( ipkTop ){
      ipkBottom = sqlite3VdbeAddOp0(v, OP_Goto);
      sqlite3VdbeJumpHere(v, ipkTop-1);
    }
  }

  /* Test all UNIQUE constraints by creating entries for each UNIQUE
  ** index and making sure that duplicate entries do not already exist.
  ** Compute the revised record entries for indices as we go.
  **
  ** This loop also handles the case of the PRIMARY KEY index for a
  ** WITHOUT ROWID table.
  */

  for(ix=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, ix++){


    int regIdx;          /* Range of registers hold conent for pIdx */
    int regR;            /* Range of registers holding conflicting PK */
    int iThisCur;        /* Cursor for this UNIQUE index */
    int addrUniqueOk;    /* Jump here if the UNIQUE constraint is satisfied */
    int addrConflictCk;  /* First opcode in the conflict check logic */

    if( aRegIdx[ix]==0 ) continue;  /* Skip indices that do not change */
    if( pUpIdx==pIdx ){
      addrUniqueOk = upsertJump+1;
      upsertBypass = sqlite3VdbeGoto(v, 0);
      VdbeComment((v, "Skip upsert subroutine"));
      sqlite3VdbeJumpHere(v, upsertJump);
    }else{
      addrUniqueOk = sqlite3VdbeMakeLabel(pParse);
    }


    if( bAffinityDone==0 && (pUpIdx==0 || pUpIdx==pIdx) ){
      sqlite3TableAffinity(v, pTab, regNewData+1);
      bAffinityDone = 1;
    }
    VdbeNoopComment((v, "prep index %s", pIdx->zName));
    iThisCur = iIdxCur+ix;









>
>
|












>
|
>
>







|
<
|
|
|
<
<
|
>
>
|







123600
123601
123602
123603
123604
123605
123606
123607
123608
123609
123610
123611
123612
123613
123614
123615
123616
123617
123618
123619
123620
123621
123622
123623
123624
123625
123626
123627
123628
123629
123630
123631
123632
123633

123634
123635
123636


123637
123638
123639
123640
123641
123642
123643
123644
123645
123646
123647
      case OE_Ignore: {
        testcase( onError==OE_Ignore );
        sqlite3VdbeGoto(v, ignoreDest);
        break;
      }
    }
    sqlite3VdbeResolveLabel(v, addrRowidOk);
    if( pUpsert && pUpsertClause!=pUpsert ){
      upsertIpkReturn = sqlite3VdbeAddOp0(v, OP_Goto);
    }else if( ipkTop ){
      ipkBottom = sqlite3VdbeAddOp0(v, OP_Goto);
      sqlite3VdbeJumpHere(v, ipkTop-1);
    }
  }

  /* Test all UNIQUE constraints by creating entries for each UNIQUE
  ** index and making sure that duplicate entries do not already exist.
  ** Compute the revised record entries for indices as we go.
  **
  ** This loop also handles the case of the PRIMARY KEY index for a
  ** WITHOUT ROWID table.
  */
  for(pIdx = indexIteratorFirst(&sIdxIter, &ix);
      pIdx;
      pIdx = indexIteratorNext(&sIdxIter, &ix)
  ){
    int regIdx;          /* Range of registers hold conent for pIdx */
    int regR;            /* Range of registers holding conflicting PK */
    int iThisCur;        /* Cursor for this UNIQUE index */
    int addrUniqueOk;    /* Jump here if the UNIQUE constraint is satisfied */
    int addrConflictCk;  /* First opcode in the conflict check logic */

    if( aRegIdx[ix]==0 ) continue;  /* Skip indices that do not change */
    if( pUpsert ){

      pUpsertClause = sqlite3UpsertOfIndex(pUpsert, pIdx);
      if( upsertIpkDelay && pUpsertClause==pUpsert ){
        sqlite3VdbeJumpHere(v, upsertIpkDelay);


      }
    }
    addrUniqueOk = sqlite3VdbeMakeLabel(pParse);
    if( bAffinityDone==0 ){
      sqlite3TableAffinity(v, pTab, regNewData+1);
      bAffinityDone = 1;
    }
    VdbeNoopComment((v, "prep index %s", pIdx->zName));
    iThisCur = iIdxCur+ix;


123053
123054
123055
123056
123057
123058
123059
123060
123061
123062
123063
123064
123065
123066
123067
123068
    if( overrideError!=OE_Default ){
      onError = overrideError;
    }else if( onError==OE_Default ){
      onError = OE_Abort;
    }

    /* Figure out if the upsert clause applies to this index */
    if( pUpIdx==pIdx ){
      if( pUpsert->pUpsertSet==0 ){
        onError = OE_Ignore;  /* DO NOTHING is the same as INSERT OR IGNORE */
      }else{
        onError = OE_Update;  /* DO UPDATE */
      }
    }

    /* Collision detection may be omitted if all of the following are true:







|
|







123704
123705
123706
123707
123708
123709
123710
123711
123712
123713
123714
123715
123716
123717
123718
123719
    if( overrideError!=OE_Default ){
      onError = overrideError;
    }else if( onError==OE_Default ){
      onError = OE_Abort;
    }

    /* Figure out if the upsert clause applies to this index */
    if( pUpsertClause ){
      if( pUpsertClause->isDoUpdate==0 ){
        onError = OE_Ignore;  /* DO NOTHING is the same as INSERT OR IGNORE */
      }else{
        onError = OE_Update;  /* DO UPDATE */
      }
    }

    /* Collision detection may be omitted if all of the following are true:
123092
123093
123094
123095
123096
123097
123098
123099
123100
123101
123102
123103
123104
123105
123106
    /* Check to see if the new index entry will be unique */
    sqlite3VdbeVerifyAbortable(v, onError);
    addrConflictCk =
      sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur, addrUniqueOk,
                           regIdx, pIdx->nKeyCol); VdbeCoverage(v);

    /* Generate code to handle collisions */
    regR = (pIdx==pPk) ? regIdx : sqlite3GetTempRange(pParse, nPkField);
    if( isUpdate || onError==OE_Replace ){
      if( HasRowid(pTab) ){
        sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR);
        /* Conflict only if the rowid of the existing index entry
        ** is different from old-rowid */
        if( isUpdate ){
          sqlite3VdbeAddOp3(v, OP_Eq, regR, addrUniqueOk, regOldData);







|







123743
123744
123745
123746
123747
123748
123749
123750
123751
123752
123753
123754
123755
123756
123757
    /* Check to see if the new index entry will be unique */
    sqlite3VdbeVerifyAbortable(v, onError);
    addrConflictCk =
      sqlite3VdbeAddOp4Int(v, OP_NoConflict, iThisCur, addrUniqueOk,
                           regIdx, pIdx->nKeyCol); VdbeCoverage(v);

    /* Generate code to handle collisions */
    regR = pIdx==pPk ? regIdx : sqlite3GetTempRange(pParse, nPkField);
    if( isUpdate || onError==OE_Replace ){
      if( HasRowid(pTab) ){
        sqlite3VdbeAddOp2(v, OP_IdxRowid, iThisCur, regR);
        /* Conflict only if the rowid of the existing index entry
        ** is different from old-rowid */
        if( isUpdate ){
          sqlite3VdbeAddOp3(v, OP_Eq, regR, addrUniqueOk, regOldData);
123244
123245
123246
123247
123248
123249
123250


123251



123252
123253
123254
123255
123256
123257
123258
123259
123260
123261
123262
123263
123264

          sqlite3VdbeJumpHere(v, addrBypass); /* Terminate the recheck bypass */
        }
        seenReplace = 1;
        break;
      }
    }


    if( pUpIdx==pIdx ){



      sqlite3VdbeGoto(v, upsertJump+1);
      sqlite3VdbeJumpHere(v, upsertBypass);
    }else{
      sqlite3VdbeResolveLabel(v, addrUniqueOk);
    }
    if( regR!=regIdx ) sqlite3ReleaseTempRange(pParse, regR, nPkField);
  }

  /* If the IPK constraint is a REPLACE, run it last */
  if( ipkTop ){
    sqlite3VdbeGoto(v, ipkTop);
    VdbeComment((v, "Do IPK REPLACE"));
    sqlite3VdbeJumpHere(v, ipkBottom);







>
>
|
>
>
>
|
|
|
<

<







123895
123896
123897
123898
123899
123900
123901
123902
123903
123904
123905
123906
123907
123908
123909
123910

123911

123912
123913
123914
123915
123916
123917
123918

          sqlite3VdbeJumpHere(v, addrBypass); /* Terminate the recheck bypass */
        }
        seenReplace = 1;
        break;
      }
    }
    sqlite3VdbeResolveLabel(v, addrUniqueOk);
    if( regR!=regIdx ) sqlite3ReleaseTempRange(pParse, regR, nPkField);
    if( pUpsertClause
     && upsertIpkReturn
     && sqlite3UpsertNextIsIPK(pUpsertClause)
    ){
      sqlite3VdbeGoto(v, upsertIpkDelay+1);
      sqlite3VdbeJumpHere(v, upsertIpkReturn);
      upsertIpkReturn = 0;

    }

  }

  /* If the IPK constraint is a REPLACE, run it last */
  if( ipkTop ){
    sqlite3VdbeGoto(v, ipkTop);
    VdbeComment((v, "Do IPK REPLACE"));
    sqlite3VdbeJumpHere(v, ipkBottom);
123789
123790
123791
123792
123793
123794
123795

123796
123797
123798
123799
123800
123801
123802
  iDbSrc = sqlite3SchemaToIndex(db, pSrc->pSchema);
  v = sqlite3GetVdbe(pParse);
  sqlite3CodeVerifySchema(pParse, iDbSrc);
  iSrc = pParse->nTab++;
  iDest = pParse->nTab++;
  regAutoinc = autoIncBegin(pParse, iDbDest, pDest);
  regData = sqlite3GetTempReg(pParse);

  regRowid = sqlite3GetTempReg(pParse);
  sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite);
  assert( HasRowid(pDest) || destHasUniqueIdx );
  if( (db->mDbFlags & DBFLAG_Vacuum)==0 && (
      (pDest->iPKey<0 && pDest->pIndex!=0)          /* (1) */
   || destHasUniqueIdx                              /* (2) */
   || (onError!=OE_Abort && onError!=OE_Rollback)   /* (3) */







>







124443
124444
124445
124446
124447
124448
124449
124450
124451
124452
124453
124454
124455
124456
124457
  iDbSrc = sqlite3SchemaToIndex(db, pSrc->pSchema);
  v = sqlite3GetVdbe(pParse);
  sqlite3CodeVerifySchema(pParse, iDbSrc);
  iSrc = pParse->nTab++;
  iDest = pParse->nTab++;
  regAutoinc = autoIncBegin(pParse, iDbDest, pDest);
  regData = sqlite3GetTempReg(pParse);
  sqlite3VdbeAddOp2(v, OP_Null, 0, regData);
  regRowid = sqlite3GetTempReg(pParse);
  sqlite3OpenTable(pParse, iDest, iDbDest, pDest, OP_OpenWrite);
  assert( HasRowid(pDest) || destHasUniqueIdx );
  if( (db->mDbFlags & DBFLAG_Vacuum)==0 && (
      (pDest->iPKey<0 && pDest->pIndex!=0)          /* (1) */
   || destHasUniqueIdx                              /* (2) */
   || (onError!=OE_Abort && onError!=OE_Rollback)   /* (3) */
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
  }
  if( HasRowid(pSrc) ){
    u8 insFlags;
    sqlite3OpenTable(pParse, iSrc, iDbSrc, pSrc, OP_OpenRead);
    emptySrcTest = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v);
    if( pDest->iPKey>=0 ){
      addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);

      sqlite3VdbeVerifyAbortable(v, onError);
      addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid);
      VdbeCoverage(v);
      sqlite3RowidConstraint(pParse, onError, pDest);
      sqlite3VdbeJumpHere(v, addr2);

      autoIncStep(pParse, regAutoinc, regRowid);
    }else if( pDest->pIndex==0 && !(db->mDbFlags & DBFLAG_VacuumInto) ){
      addr1 = sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid);
    }else{
      addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
      assert( (pDest->tabFlags & TF_Autoincrement)==0 );
    }

    if( db->mDbFlags & DBFLAG_Vacuum ){
      sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest);
      insFlags = OPFLAG_APPEND|OPFLAG_USESEEKRESULT;
    }else{
      insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND;
    }


    sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);






    sqlite3VdbeAddOp4(v, OP_Insert, iDest, regData, regRowid,
                      (char*)pDest, P4_TABLE);
    sqlite3VdbeChangeP5(v, insFlags);

    sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); VdbeCoverage(v);
    sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
    sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
  }else{
    sqlite3TableLock(pParse, iDbDest, pDest->tnum, 1, pDest->zName);
    sqlite3TableLock(pParse, iDbSrc, pSrc->tnum, 0, pSrc->zName);
  }







>
|
|
|
|
|
>







>


|

|

>
>
|
>
>
>
>
>
>

|

>







124479
124480
124481
124482
124483
124484
124485
124486
124487
124488
124489
124490
124491
124492
124493
124494
124495
124496
124497
124498
124499
124500
124501
124502
124503
124504
124505
124506
124507
124508
124509
124510
124511
124512
124513
124514
124515
124516
124517
124518
124519
124520
124521
124522
124523
124524
124525
124526
  }
  if( HasRowid(pSrc) ){
    u8 insFlags;
    sqlite3OpenTable(pParse, iSrc, iDbSrc, pSrc, OP_OpenRead);
    emptySrcTest = sqlite3VdbeAddOp2(v, OP_Rewind, iSrc, 0); VdbeCoverage(v);
    if( pDest->iPKey>=0 ){
      addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
      if( (db->mDbFlags & DBFLAG_Vacuum)==0 ){
        sqlite3VdbeVerifyAbortable(v, onError);
        addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid);
        VdbeCoverage(v);
        sqlite3RowidConstraint(pParse, onError, pDest);
        sqlite3VdbeJumpHere(v, addr2);
      }
      autoIncStep(pParse, regAutoinc, regRowid);
    }else if( pDest->pIndex==0 && !(db->mDbFlags & DBFLAG_VacuumInto) ){
      addr1 = sqlite3VdbeAddOp2(v, OP_NewRowid, iDest, regRowid);
    }else{
      addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid);
      assert( (pDest->tabFlags & TF_Autoincrement)==0 );
    }

    if( db->mDbFlags & DBFLAG_Vacuum ){
      sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest);
      insFlags = OPFLAG_APPEND|OPFLAG_USESEEKRESULT|OPFLAG_PREFORMAT;
    }else{
      insFlags = OPFLAG_NCHANGE|OPFLAG_LASTROWID|OPFLAG_APPEND|OPFLAG_PREFORMAT;
    }
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
    if( db->xPreUpdateCallback ){
      sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
      insFlags &= ~OPFLAG_PREFORMAT;
    }else
#endif
    {
      sqlite3VdbeAddOp3(v, OP_RowCell, iDest, iSrc, regRowid);
    }
    sqlite3VdbeAddOp4(v, OP_Insert, iDest, regData, regRowid,
        (char*)pDest, P4_TABLE);
    sqlite3VdbeChangeP5(v, insFlags);

    sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1); VdbeCoverage(v);
    sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
    sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
  }else{
    sqlite3TableLock(pParse, iDbDest, pDest->tnum, 1, pDest->zName);
    sqlite3TableLock(pParse, iDbSrc, pSrc->tnum, 0, pSrc->zName);
  }
123887
123888
123889
123890
123891
123892
123893
123894
123895

123896
123897
123898
123899

123900

123901
123902
123903
123904
123905
123906
123907
      ** a VACUUM command. In that case keys may not be written in strictly
      ** sorted order.  */
      for(i=0; i<pSrcIdx->nColumn; i++){
        const char *zColl = pSrcIdx->azColl[i];
        if( sqlite3_stricmp(sqlite3StrBINARY, zColl) ) break;
      }
      if( i==pSrcIdx->nColumn ){
        idxInsFlags = OPFLAG_USESEEKRESULT;
        sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest);

      }
    }else if( !HasRowid(pSrc) && pDestIdx->idxType==SQLITE_IDXTYPE_PRIMARYKEY ){
      idxInsFlags |= OPFLAG_NCHANGE;
    }

    sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);

    sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData);
    sqlite3VdbeChangeP5(v, idxInsFlags|OPFLAG_APPEND);
    sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); VdbeCoverage(v);
    sqlite3VdbeJumpHere(v, addr1);
    sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
    sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
  }







|

>




>
|
>







124554
124555
124556
124557
124558
124559
124560
124561
124562
124563
124564
124565
124566
124567
124568
124569
124570
124571
124572
124573
124574
124575
124576
124577
      ** a VACUUM command. In that case keys may not be written in strictly
      ** sorted order.  */
      for(i=0; i<pSrcIdx->nColumn; i++){
        const char *zColl = pSrcIdx->azColl[i];
        if( sqlite3_stricmp(sqlite3StrBINARY, zColl) ) break;
      }
      if( i==pSrcIdx->nColumn ){
        idxInsFlags = OPFLAG_USESEEKRESULT|OPFLAG_PREFORMAT;
        sqlite3VdbeAddOp1(v, OP_SeekEnd, iDest);
        sqlite3VdbeAddOp2(v, OP_RowCell, iDest, iSrc);
      }
    }else if( !HasRowid(pSrc) && pDestIdx->idxType==SQLITE_IDXTYPE_PRIMARYKEY ){
      idxInsFlags |= OPFLAG_NCHANGE;
    }
    if( idxInsFlags!=(OPFLAG_USESEEKRESULT|OPFLAG_PREFORMAT) ){
      sqlite3VdbeAddOp3(v, OP_RowData, iSrc, regData, 1);
    }
    sqlite3VdbeAddOp2(v, OP_IdxInsert, iDest, regData);
    sqlite3VdbeChangeP5(v, idxInsFlags|OPFLAG_APPEND);
    sqlite3VdbeAddOp2(v, OP_Next, iSrc, addr1+1); VdbeCoverage(v);
    sqlite3VdbeJumpHere(v, addr1);
    sqlite3VdbeAddOp2(v, OP_Close, iSrc, 0);
    sqlite3VdbeAddOp2(v, OP_Close, iDest, 0);
  }
128938
128939
128940
128941
128942
128943
128944
128945


128946
128947
128948
128949





128950
128951
128952
128953
128954
128955
128956
    return 1;
  }

  assert( iDb>=0 && iDb<db->nDb );
  if( argv==0 ) return 0;   /* Might happen if EMPTY_RESULT_CALLBACKS are on */
  if( argv[3]==0 ){
    corruptSchema(pData, argv[1], 0);
  }else if( sqlite3_strnicmp(argv[4],"create ",7)==0 ){


    /* Call the parser to process a CREATE TABLE, INDEX or VIEW.
    ** But because db->init.busy is set to 1, no VDBE code is generated
    ** or executed.  All the parser does is build the internal data
    ** structures that describe the table, index, or view.





    */
    int rc;
    u8 saved_iDb = db->init.iDb;
    sqlite3_stmt *pStmt;
    TESTONLY(int rcp);            /* Return code from sqlite3_prepare() */

    assert( db->init.busy );







|
>
>




>
>
>
>
>







129608
129609
129610
129611
129612
129613
129614
129615
129616
129617
129618
129619
129620
129621
129622
129623
129624
129625
129626
129627
129628
129629
129630
129631
129632
129633
    return 1;
  }

  assert( iDb>=0 && iDb<db->nDb );
  if( argv==0 ) return 0;   /* Might happen if EMPTY_RESULT_CALLBACKS are on */
  if( argv[3]==0 ){
    corruptSchema(pData, argv[1], 0);
  }else if( argv[4]
         && 'c'==sqlite3UpperToLower[(unsigned char)argv[4][0]]
         && 'r'==sqlite3UpperToLower[(unsigned char)argv[4][1]] ){
    /* Call the parser to process a CREATE TABLE, INDEX or VIEW.
    ** But because db->init.busy is set to 1, no VDBE code is generated
    ** or executed.  All the parser does is build the internal data
    ** structures that describe the table, index, or view.
    **
    ** No other valid SQL statement, other than the variable CREATE statements,
    ** can begin with the letters "C" and "R".  Thus, it is not possible run
    ** any other kind of statement while parsing the schema, even a corrupt
    ** schema.
    */
    int rc;
    u8 saved_iDb = db->init.iDb;
    sqlite3_stmt *pStmt;
    TESTONLY(int rcp);            /* Return code from sqlite3_prepare() */

    assert( db->init.busy );
129399
129400
129401
129402
129403
129404
129405






129406

129407

129408
129409
129410
129411
129412
129413
129414







































129415
129416
129417
129418
129419
129420
129421
  sqlite3 *db = pParse->db;
  AggInfo *pThis = pParse->pAggList;
  while( pThis ){
    AggInfo *pNext = pThis->pNext;
    agginfoFree(db, pThis);
    pThis = pNext;
  }






  sqlite3DbFree(db, pParse->aLabel);

  sqlite3ExprListDelete(db, pParse->pConstExpr);

  if( db ){
    assert( db->lookaside.bDisable >= pParse->disableLookaside );
    db->lookaside.bDisable -= pParse->disableLookaside;
    db->lookaside.sz = db->lookaside.bDisable ? 0 : db->lookaside.szTrue;
  }
  pParse->disableLookaside = 0;
}








































/*
** Compile the UTF-8 encoded SQL statement zSql into a statement handle.
*/
static int sqlite3Prepare(
  sqlite3 *db,              /* Database handle. */
  const char *zSql,         /* UTF-8 encoded SQL statement. */







>
>
>
>
>
>

>
|
>







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







130076
130077
130078
130079
130080
130081
130082
130083
130084
130085
130086
130087
130088
130089
130090
130091
130092
130093
130094
130095
130096
130097
130098
130099
130100
130101
130102
130103
130104
130105
130106
130107
130108
130109
130110
130111
130112
130113
130114
130115
130116
130117
130118
130119
130120
130121
130122
130123
130124
130125
130126
130127
130128
130129
130130
130131
130132
130133
130134
130135
130136
130137
130138
130139
130140
130141
130142
130143
130144
130145
  sqlite3 *db = pParse->db;
  AggInfo *pThis = pParse->pAggList;
  while( pThis ){
    AggInfo *pNext = pThis->pNext;
    agginfoFree(db, pThis);
    pThis = pNext;
  }
  while( pParse->pCleanup ){
    ParseCleanup *pCleanup = pParse->pCleanup;
    pParse->pCleanup = pCleanup->pNext;
    pCleanup->xCleanup(db, pCleanup->pPtr);
    sqlite3DbFree(db, pCleanup);
  }
  sqlite3DbFree(db, pParse->aLabel);
  if( pParse->pConstExpr ){
    sqlite3ExprListDelete(db, pParse->pConstExpr);
  }
  if( db ){
    assert( db->lookaside.bDisable >= pParse->disableLookaside );
    db->lookaside.bDisable -= pParse->disableLookaside;
    db->lookaside.sz = db->lookaside.bDisable ? 0 : db->lookaside.szTrue;
  }
  pParse->disableLookaside = 0;
}

/*
** Add a new cleanup operation to a Parser.  The cleanup should happen when
** the parser object is destroyed.  But, beware: the cleanup might happen
** immediately.
**
** Use this mechanism for uncommon cleanups.  There is a higher setup
** cost for this mechansim (an extra malloc), so it should not be used
** for common cleanups that happen on most calls.  But for less
** common cleanups, we save a single NULL-pointer comparison in
** sqlite3ParserReset(), which reduces the total CPU cycle count.
**
** If a memory allocation error occurs, then the cleanup happens immediately.
** When eithr SQLITE_DEBUG or SQLITE_COVERAGE_TEST are defined, the
** pParse->earlyCleanup flag is set in that case.  Calling code show verify
** that test cases exist for which this happens, to guard against possible
** use-after-free errors following an OOM.  The preferred way to do this is
** to immediately follow the call to this routine with:
**
**       testcase( pParse->earlyCleanup );
*/
SQLITE_PRIVATE void sqlite3ParserAddCleanup(
  Parse *pParse,                      /* Destroy when this Parser finishes */
  void (*xCleanup)(sqlite3*,void*),   /* The cleanup routine */
  void *pPtr                          /* Pointer to object to be cleaned up */
){
  ParseCleanup *pCleanup = sqlite3DbMallocRaw(pParse->db, sizeof(*pCleanup));
  if( pCleanup ){
    pCleanup->pNext = pParse->pCleanup;
    pParse->pCleanup = pCleanup;
    pCleanup->pPtr = pPtr;
    pCleanup->xCleanup = xCleanup;
  }else{
    xCleanup(pParse->db, pPtr);
#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST)
    pParse->earlyCleanup = 1;
#endif
  }
}

/*
** Compile the UTF-8 encoded SQL statement zSql into a statement handle.
*/
static int sqlite3Prepare(
  sqlite3 *db,              /* Database handle. */
  const char *zSql,         /* UTF-8 encoded SQL statement. */
129507
129508
129509
129510
129511
129512
129513
129514
129515
129516
129517
129518
129519
129520
129521
129522
129523
129524
129525
129526
129527
129528
129529
129530
129531


129532
129533
129534
129535
129536
129537

129538
129539
129540
129541
129542
129543







129544
129545
129546
129547
129548
129549
129550
      sParse.zTail = &zSql[nBytes];
    }
  }else{
    sqlite3RunParser(&sParse, zSql, &zErrMsg);
  }
  assert( 0==sParse.nQueryLoop );

  if( sParse.rc==SQLITE_DONE ){
    sParse.rc = SQLITE_OK;
  }
  if( sParse.checkSchema ){
    schemaIsValid(&sParse);
  }
  if( pzTail ){
    *pzTail = sParse.zTail;
  }

  if( db->init.busy==0 ){
    sqlite3VdbeSetSql(sParse.pVdbe, zSql, (int)(sParse.zTail-zSql), prepFlags);
  }
  if( db->mallocFailed ){
    sParse.rc = SQLITE_NOMEM_BKPT;
  }
  rc = sParse.rc;
  if( rc!=SQLITE_OK ){


    if( sParse.pVdbe ) sqlite3VdbeFinalize(sParse.pVdbe);
    assert(!(*ppStmt));
  }else{
    *ppStmt = (sqlite3_stmt*)sParse.pVdbe;
  }


  if( zErrMsg ){
    sqlite3ErrorWithMsg(db, rc, "%s", zErrMsg);
    sqlite3DbFree(db, zErrMsg);
  }else{
    sqlite3Error(db, rc);
  }








  /* Delete any TriggerPrg structures allocated while parsing this statement. */
  while( sParse.pTriggerPrg ){
    TriggerPrg *pT = sParse.pTriggerPrg;
    sParse.pTriggerPrg = pT->pNext;
    sqlite3DbFree(db, pT);
  }







<
<
<
<
<
<










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







130231
130232
130233
130234
130235
130236
130237






130238
130239
130240
130241
130242
130243
130244
130245
130246
130247
130248
130249
130250
130251
130252


130253
130254
130255
130256
130257
130258
130259
130260
130261
130262
130263
130264
130265
130266
130267
130268
130269
130270
130271
130272
130273
130274
130275
130276
      sParse.zTail = &zSql[nBytes];
    }
  }else{
    sqlite3RunParser(&sParse, zSql, &zErrMsg);
  }
  assert( 0==sParse.nQueryLoop );







  if( pzTail ){
    *pzTail = sParse.zTail;
  }

  if( db->init.busy==0 ){
    sqlite3VdbeSetSql(sParse.pVdbe, zSql, (int)(sParse.zTail-zSql), prepFlags);
  }
  if( db->mallocFailed ){
    sParse.rc = SQLITE_NOMEM_BKPT;
  }
  if( sParse.rc!=SQLITE_OK && sParse.rc!=SQLITE_DONE ){
    if( sParse.checkSchema ){
      schemaIsValid(&sParse);
    }
    if( sParse.pVdbe ){


      sqlite3VdbeFinalize(sParse.pVdbe);
    }
    assert( 0==(*ppStmt) );
    rc = sParse.rc;
    if( zErrMsg ){
      sqlite3ErrorWithMsg(db, rc, "%s", zErrMsg);
      sqlite3DbFree(db, zErrMsg);
    }else{
      sqlite3Error(db, rc);
    }
  }else{
    assert( zErrMsg==0 );
    *ppStmt = (sqlite3_stmt*)sParse.pVdbe;
    rc = SQLITE_OK;
    sqlite3ErrorClear(db);
  }


  /* Delete any TriggerPrg structures allocated while parsing this statement. */
  while( sParse.pTriggerPrg ){
    TriggerPrg *pT = sParse.pTriggerPrg;
    sParse.pTriggerPrg = pT->pNext;
    sqlite3DbFree(db, pT);
  }
130144
130145
130146
130147
130148
130149
130150
130151
130152
130153
130154
130155
130156
130157
130158
  pE2 = sqlite3CreateColumnExpr(db, pSrc, iRight, iColRight);

  pEq = sqlite3PExpr(pParse, TK_EQ, pE1, pE2);
  if( pEq && isOuterJoin ){
    ExprSetProperty(pEq, EP_FromJoin);
    assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) );
    ExprSetVVAProperty(pEq, EP_NoReduce);
    pEq->iRightJoinTable = (i16)pE2->iTable;
  }
  *ppWhere = sqlite3ExprAnd(pParse, *ppWhere, pEq);
}

/*
** Set the EP_FromJoin property on all terms of the given expression.
** And set the Expr.iRightJoinTable to iTable for every term in the







|







130870
130871
130872
130873
130874
130875
130876
130877
130878
130879
130880
130881
130882
130883
130884
  pE2 = sqlite3CreateColumnExpr(db, pSrc, iRight, iColRight);

  pEq = sqlite3PExpr(pParse, TK_EQ, pE1, pE2);
  if( pEq && isOuterJoin ){
    ExprSetProperty(pEq, EP_FromJoin);
    assert( !ExprHasProperty(pEq, EP_TokenOnly|EP_Reduced) );
    ExprSetVVAProperty(pEq, EP_NoReduce);
    pEq->iRightJoinTable = pE2->iTable;
  }
  *ppWhere = sqlite3ExprAnd(pParse, *ppWhere, pEq);
}

/*
** Set the EP_FromJoin property on all terms of the given expression.
** And set the Expr.iRightJoinTable to iTable for every term in the
130180
130181
130182
130183
130184
130185
130186
130187
130188
130189
130190
130191
130192
130193
130194
** the output, which is incorrect.
*/
SQLITE_PRIVATE void sqlite3SetJoinExpr(Expr *p, int iTable){
  while( p ){
    ExprSetProperty(p, EP_FromJoin);
    assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
    ExprSetVVAProperty(p, EP_NoReduce);
    p->iRightJoinTable = (i16)iTable;
    if( p->op==TK_FUNCTION && p->x.pList ){
      int i;
      for(i=0; i<p->x.pList->nExpr; i++){
        sqlite3SetJoinExpr(p->x.pList->a[i].pExpr, iTable);
      }
    }
    sqlite3SetJoinExpr(p->pLeft, iTable);







|







130906
130907
130908
130909
130910
130911
130912
130913
130914
130915
130916
130917
130918
130919
130920
** the output, which is incorrect.
*/
SQLITE_PRIVATE void sqlite3SetJoinExpr(Expr *p, int iTable){
  while( p ){
    ExprSetProperty(p, EP_FromJoin);
    assert( !ExprHasProperty(p, EP_TokenOnly|EP_Reduced) );
    ExprSetVVAProperty(p, EP_NoReduce);
    p->iRightJoinTable = iTable;
    if( p->op==TK_FUNCTION && p->x.pList ){
      int i;
      for(i=0; i<p->x.pList->nExpr; i++){
        sqlite3SetJoinExpr(p->x.pList->a[i].pExpr, iTable);
      }
    }
    sqlite3SetJoinExpr(p->pLeft, iTable);
133446
133447
133448
133449
133450
133451
133452


















































































133453
133454
133455
133456
133457
133458
133459
  w.xSelectCallback = sqlite3SelectWalkNoop;
  w.u.pSrcItem = pSrcItem;
  pSrcItem->colUsed = 0;
  sqlite3WalkSelect(&w, pSelect);
}
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */



















































































#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
/*
** This routine attempts to flatten subqueries as a performance optimization.
** This routine returns 1 if it makes changes and 0 if no flattening occurs.
**
** To understand the concept of flattening, consider the following
** query:







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







134172
134173
134174
134175
134176
134177
134178
134179
134180
134181
134182
134183
134184
134185
134186
134187
134188
134189
134190
134191
134192
134193
134194
134195
134196
134197
134198
134199
134200
134201
134202
134203
134204
134205
134206
134207
134208
134209
134210
134211
134212
134213
134214
134215
134216
134217
134218
134219
134220
134221
134222
134223
134224
134225
134226
134227
134228
134229
134230
134231
134232
134233
134234
134235
134236
134237
134238
134239
134240
134241
134242
134243
134244
134245
134246
134247
134248
134249
134250
134251
134252
134253
134254
134255
134256
134257
134258
134259
134260
134261
134262
134263
134264
134265
134266
134267
  w.xSelectCallback = sqlite3SelectWalkNoop;
  w.u.pSrcItem = pSrcItem;
  pSrcItem->colUsed = 0;
  sqlite3WalkSelect(&w, pSelect);
}
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */

#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
/*
** Assign new cursor numbers to each of the items in pSrc. For each
** new cursor number assigned, set an entry in the aCsrMap[] array
** to map the old cursor number to the new:
**
**     aCsrMap[iOld] = iNew;
**
** The array is guaranteed by the caller to be large enough for all
** existing cursor numbers in pSrc.
**
** If pSrc contains any sub-selects, call this routine recursively
** on the FROM clause of each such sub-select, with iExcept set to -1.
*/
static void srclistRenumberCursors(
  Parse *pParse,                  /* Parse context */
  int *aCsrMap,                   /* Array to store cursor mappings in */
  SrcList *pSrc,                  /* FROM clause to renumber */
  int iExcept                     /* FROM clause item to skip */
){
  int i;
  struct SrcList_item *pItem;
  for(i=0, pItem=pSrc->a; i<pSrc->nSrc; i++, pItem++){
    if( i!=iExcept ){
      Select *p;
      pItem->iCursor = aCsrMap[pItem->iCursor] = pParse->nTab++;
      for(p=pItem->pSelect; p; p=p->pPrior){
        srclistRenumberCursors(pParse, aCsrMap, p->pSrc, -1);
      }
    }
  }
}

/*
** Expression walker callback used by renumberCursors() to update
** Expr objects to match newly assigned cursor numbers.
*/
static int renumberCursorsCb(Walker *pWalker, Expr *pExpr){
  int *aCsrMap = pWalker->u.aiCol;
  if( pExpr->op==TK_COLUMN && aCsrMap[pExpr->iTable] ){
    pExpr->iTable = aCsrMap[pExpr->iTable];
  }
  if( ExprHasProperty(pExpr, EP_FromJoin) && aCsrMap[pExpr->iRightJoinTable] ){
    pExpr->iRightJoinTable = aCsrMap[pExpr->iRightJoinTable];
  }
  return WRC_Continue;
}

/*
** Assign a new cursor number to each cursor in the FROM clause (Select.pSrc)
** of the SELECT statement passed as the second argument, and to each
** cursor in the FROM clause of any FROM clause sub-selects, recursively.
** Except, do not assign a new cursor number to the iExcept'th element in
** the FROM clause of (*p). Update all expressions and other references
** to refer to the new cursor numbers.
**
** Argument aCsrMap is an array that may be used for temporary working
** space. Two guarantees are made by the caller:
**
**   * the array is larger than the largest cursor number used within the
**     select statement passed as an argument, and
**
**   * the array entries for all cursor numbers that do *not* appear in
**     FROM clauses of the select statement as described above are
**     initialized to zero.
*/
static void renumberCursors(
  Parse *pParse,                  /* Parse context */
  Select *p,                      /* Select to renumber cursors within */
  int iExcept,                    /* FROM clause item to skip */
  int *aCsrMap                    /* Working space */
){
  Walker w;
  srclistRenumberCursors(pParse, aCsrMap, p->pSrc, iExcept);
  memset(&w, 0, sizeof(w));
  w.u.aiCol = aCsrMap;
  w.xExprCallback = renumberCursorsCb;
  w.xSelectCallback = sqlite3SelectWalkNoop;
  sqlite3WalkSelect(&w, p);
}
#endif /* !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW) */

#if !defined(SQLITE_OMIT_SUBQUERY) || !defined(SQLITE_OMIT_VIEW)
/*
** This routine attempts to flatten subqueries as a performance optimization.
** This routine returns 1 if it makes changes and 0 if no flattening occurs.
**
** To understand the concept of flattening, consider the following
** query:
133540
133541
133542
133543
133544
133545
133546
133547
133548
133549

133550
133551
133552
133553
133554
133555
133556
133557
133558
133559
133560
133561
133562
133563
133564
133565
133566
133567
133568
133569
133570
133571
133572
133573
133574
133575
133576
133577
133578
133579
133580
133581
133582
133583
133584
133585
133586
133587
133588
133589
133590
133591
**  (17)  If the subquery is a compound select, then
**        (17a) all compound operators must be a UNION ALL, and
**        (17b) no terms within the subquery compound may be aggregate
**              or DISTINCT, and
**        (17c) every term within the subquery compound must have a FROM clause
**        (17d) the outer query may not be
**              (17d1) aggregate, or
**              (17d2) DISTINCT, or
**              (17d3) a join.
**        (17e) the subquery may not contain window functions

**
**        The parent and sub-query may contain WHERE clauses. Subject to
**        rules (11), (13) and (14), they may also contain ORDER BY,
**        LIMIT and OFFSET clauses.  The subquery cannot use any compound
**        operator other than UNION ALL because all the other compound
**        operators have an implied DISTINCT which is disallowed by
**        restriction (4).
**
**        Also, each component of the sub-query must return the same number
**        of result columns. This is actually a requirement for any compound
**        SELECT statement, but all the code here does is make sure that no
**        such (illegal) sub-query is flattened. The caller will detect the
**        syntax error and return a detailed message.
**
**  (18)  If the sub-query is a compound select, then all terms of the
**        ORDER BY clause of the parent must be simple references to
**        columns of the sub-query.
**
**  (19)  If the subquery uses LIMIT then the outer query may not
**        have a WHERE clause.
**
**  (20)  If the sub-query is a compound select, then it must not use
**        an ORDER BY clause.  Ticket #3773.  We could relax this constraint
**        somewhat by saying that the terms of the ORDER BY clause must
**        appear as unmodified result columns in the outer query.  But we
**        have other optimizations in mind to deal with that case.
**
**  (21)  If the subquery uses LIMIT then the outer query may not be
**        DISTINCT.  (See ticket [752e1646fc]).
**
**  (22)  The subquery may not be a recursive CTE.
**
**  (**)  Subsumed into restriction (17d3).  Was: If the outer query is
**        a recursive CTE, then the sub-query may not be a compound query.
**        This restriction is because transforming the
**        parent to a compound query confuses the code that handles
**        recursive queries in multiSelect().
**
**  (**)  We no longer attempt to flatten aggregate subqueries.  Was:
**        The subquery may not be an aggregate that uses the built-in min() or
**        or max() functions.  (Without this restriction, a query like:
**        "SELECT x FROM (SELECT max(y), x FROM t1)" would not necessarily







|
<
|
>















|
|















<
|
|







134348
134349
134350
134351
134352
134353
134354
134355

134356
134357
134358
134359
134360
134361
134362
134363
134364
134365
134366
134367
134368
134369
134370
134371
134372
134373
134374
134375
134376
134377
134378
134379
134380
134381
134382
134383
134384
134385
134386
134387
134388
134389

134390
134391
134392
134393
134394
134395
134396
134397
134398
**  (17)  If the subquery is a compound select, then
**        (17a) all compound operators must be a UNION ALL, and
**        (17b) no terms within the subquery compound may be aggregate
**              or DISTINCT, and
**        (17c) every term within the subquery compound must have a FROM clause
**        (17d) the outer query may not be
**              (17d1) aggregate, or
**              (17d2) DISTINCT

**        (17e) the subquery may not contain window functions, and
**        (17f) the subquery must not be the RHS of a LEFT JOIN.
**
**        The parent and sub-query may contain WHERE clauses. Subject to
**        rules (11), (13) and (14), they may also contain ORDER BY,
**        LIMIT and OFFSET clauses.  The subquery cannot use any compound
**        operator other than UNION ALL because all the other compound
**        operators have an implied DISTINCT which is disallowed by
**        restriction (4).
**
**        Also, each component of the sub-query must return the same number
**        of result columns. This is actually a requirement for any compound
**        SELECT statement, but all the code here does is make sure that no
**        such (illegal) sub-query is flattened. The caller will detect the
**        syntax error and return a detailed message.
**
**  (18)  If the sub-query is a compound select, then all terms of the
**        ORDER BY clause of the parent must be copies of a term returned
**        by the parent query.
**
**  (19)  If the subquery uses LIMIT then the outer query may not
**        have a WHERE clause.
**
**  (20)  If the sub-query is a compound select, then it must not use
**        an ORDER BY clause.  Ticket #3773.  We could relax this constraint
**        somewhat by saying that the terms of the ORDER BY clause must
**        appear as unmodified result columns in the outer query.  But we
**        have other optimizations in mind to deal with that case.
**
**  (21)  If the subquery uses LIMIT then the outer query may not be
**        DISTINCT.  (See ticket [752e1646fc]).
**
**  (22)  The subquery may not be a recursive CTE.
**

**  (23)  If the outer query is a recursive CTE, then the sub-query may not be
**        a compound query.  This restriction is because transforming the
**        parent to a compound query confuses the code that handles
**        recursive queries in multiSelect().
**
**  (**)  We no longer attempt to flatten aggregate subqueries.  Was:
**        The subquery may not be an aggregate that uses the built-in min() or
**        or max() functions.  (Without this restriction, a query like:
**        "SELECT x FROM (SELECT max(y), x FROM t1)" would not necessarily
133622
133623
133624
133625
133626
133627
133628

133629
133630
133631
133632
133633
133634
133635
  int iNewParent = -1;/* Replacement table for iParent */
  int isLeftJoin = 0; /* True if pSub is the right side of a LEFT JOIN */
  int i;              /* Loop counter */
  Expr *pWhere;                    /* The WHERE clause */
  struct SrcList_item *pSubitem;   /* The subquery */
  sqlite3 *db = pParse->db;
  Walker w;                        /* Walker to persist agginfo data */


  /* Check to see if flattening is permitted.  Return 0 if not.
  */
  assert( p!=0 );
  assert( p->pPrior==0 );
  if( OptimizationDisabled(db, SQLITE_QueryFlattener) ) return 0;
  pSrc = p->pSrc;







>







134429
134430
134431
134432
134433
134434
134435
134436
134437
134438
134439
134440
134441
134442
134443
  int iNewParent = -1;/* Replacement table for iParent */
  int isLeftJoin = 0; /* True if pSub is the right side of a LEFT JOIN */
  int i;              /* Loop counter */
  Expr *pWhere;                    /* The WHERE clause */
  struct SrcList_item *pSubitem;   /* The subquery */
  sqlite3 *db = pParse->db;
  Walker w;                        /* Walker to persist agginfo data */
  int *aCsrMap = 0;

  /* Check to see if flattening is permitted.  Return 0 if not.
  */
  assert( p!=0 );
  assert( p->pPrior==0 );
  if( OptimizationDisabled(db, SQLITE_QueryFlattener) ) return 0;
  pSrc = p->pSrc;
133717
133718
133719
133720
133721
133722
133723
133724
133725
133726
133727
133728
133729
133730

133731
133732
133733
133734
133735
133736
133737
133738
133739
133740
133741
133742
133743
133744
133745
133746
133747
133748
133749
133750
133751
133752
133753
133754
133755
133756
133757
133758
133759





133760
133761
133762
133763
133764
133765
133766
133767
133768
133769











133770
133771
133772
133773
133774
133775
133776
  ** that make up the compound SELECT are allowed to be aggregate or distinct
  ** queries.
  */
  if( pSub->pPrior ){
    if( pSub->pOrderBy ){
      return 0;  /* Restriction (20) */
    }
    if( isAgg || (p->selFlags & SF_Distinct)!=0 || pSrc->nSrc!=1 ){
      return 0; /* (17d1), (17d2), or (17d3) */
    }
    for(pSub1=pSub; pSub1; pSub1=pSub1->pPrior){
      testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct );
      testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Aggregate );
      assert( pSub->pSrc!=0 );

      assert( pSub->pEList->nExpr==pSub1->pEList->nExpr );
      if( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))!=0    /* (17b) */
       || (pSub1->pPrior && pSub1->op!=TK_ALL)                 /* (17a) */
       || pSub1->pSrc->nSrc<1                                  /* (17c) */
#ifndef SQLITE_OMIT_WINDOWFUNC
       || pSub1->pWin                                          /* (17e) */
#endif
      ){
        return 0;
      }
      testcase( pSub1->pSrc->nSrc>1 );
    }

    /* Restriction (18). */
    if( p->pOrderBy ){
      int ii;
      for(ii=0; ii<p->pOrderBy->nExpr; ii++){
        if( p->pOrderBy->a[ii].u.x.iOrderByCol==0 ) return 0;
      }
    }
  }

  /* Ex-restriction (23):
  ** The only way that the recursive part of a CTE can contain a compound
  ** subquery is for the subquery to be one term of a join.  But if the
  ** subquery is a join, then the flattening has already been stopped by
  ** restriction (17d3)
  */
  assert( (p->selFlags & SF_Recursive)==0 || pSub->pPrior==0 );






  /***** If we reach this point, flattening is permitted. *****/
  SELECTTRACE(1,pParse,p,("flatten %u.%p from term %d\n",
                   pSub->selId, pSub, iFrom));

  /* Authorize the subquery */
  pParse->zAuthContext = pSubitem->zName;
  TESTONLY(i =) sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0);
  testcase( i==SQLITE_DENY );
  pParse->zAuthContext = zSavedAuthContext;












  /* If the sub-query is a compound SELECT statement, then (by restrictions
  ** 17 and 18 above) it must be a UNION ALL and the parent query must
  ** be of the form:
  **
  **     SELECT <expr-list> FROM (<sub-query>) <where-clause>
  **







|
|





>




















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










>
>
>
>
>
>
>
>
>
>
>







134525
134526
134527
134528
134529
134530
134531
134532
134533
134534
134535
134536
134537
134538
134539
134540
134541
134542
134543
134544
134545
134546
134547
134548
134549
134550
134551
134552
134553
134554
134555
134556
134557
134558
134559
134560

134561





134562
134563
134564
134565
134566
134567
134568
134569
134570
134571
134572
134573
134574
134575
134576
134577
134578
134579
134580
134581
134582
134583
134584
134585
134586
134587
134588
134589
134590
134591
134592
134593
134594
134595
  ** that make up the compound SELECT are allowed to be aggregate or distinct
  ** queries.
  */
  if( pSub->pPrior ){
    if( pSub->pOrderBy ){
      return 0;  /* Restriction (20) */
    }
    if( isAgg || (p->selFlags & SF_Distinct)!=0 || isLeftJoin>0 ){
      return 0; /* (17d1), (17d2), or (17f) */
    }
    for(pSub1=pSub; pSub1; pSub1=pSub1->pPrior){
      testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Distinct );
      testcase( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))==SF_Aggregate );
      assert( pSub->pSrc!=0 );
      assert( (pSub->selFlags & SF_Recursive)==0 );
      assert( pSub->pEList->nExpr==pSub1->pEList->nExpr );
      if( (pSub1->selFlags & (SF_Distinct|SF_Aggregate))!=0    /* (17b) */
       || (pSub1->pPrior && pSub1->op!=TK_ALL)                 /* (17a) */
       || pSub1->pSrc->nSrc<1                                  /* (17c) */
#ifndef SQLITE_OMIT_WINDOWFUNC
       || pSub1->pWin                                          /* (17e) */
#endif
      ){
        return 0;
      }
      testcase( pSub1->pSrc->nSrc>1 );
    }

    /* Restriction (18). */
    if( p->pOrderBy ){
      int ii;
      for(ii=0; ii<p->pOrderBy->nExpr; ii++){
        if( p->pOrderBy->a[ii].u.x.iOrderByCol==0 ) return 0;
      }
    }


    /* Restriction (23) */





    if( (p->selFlags & SF_Recursive) ) return 0;

    if( pSrc->nSrc>1 ){
      aCsrMap = sqlite3DbMallocZero(db, pParse->nTab*sizeof(int));
    }
  }

  /***** If we reach this point, flattening is permitted. *****/
  SELECTTRACE(1,pParse,p,("flatten %u.%p from term %d\n",
                   pSub->selId, pSub, iFrom));

  /* Authorize the subquery */
  pParse->zAuthContext = pSubitem->zName;
  TESTONLY(i =) sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0);
  testcase( i==SQLITE_DENY );
  pParse->zAuthContext = zSavedAuthContext;

  /* Delete the transient structures associated with thesubquery */
  pSub1 = pSubitem->pSelect;
  sqlite3DbFree(db, pSubitem->zDatabase);
  sqlite3DbFree(db, pSubitem->zName);
  sqlite3DbFree(db, pSubitem->zAlias);
  pSubitem->zDatabase = 0;
  pSubitem->zName = 0;
  pSubitem->zAlias = 0;
  pSubitem->pSelect = 0;
  assert( pSubitem->pOn==0 );

  /* If the sub-query is a compound SELECT statement, then (by restrictions
  ** 17 and 18 above) it must be a UNION ALL and the parent query must
  ** be of the form:
  **
  **     SELECT <expr-list> FROM (<sub-query>) <where-clause>
  **
133802
133803
133804
133805
133806
133807
133808


133809
133810
133811
133812
133813
133814
133815
133816
133817

133818
133819
133820



133821
133822
133823
133824
133825
133826
133827
133828
133829
133830
133831
133832
133833
133834
133835
133836
133837
133838
133839
133840
133841
133842
133843
133844
133845


133846
133847
133848
133849
133850
133851
133852
133853
133854
133855
133856
133857


133858
133859
133860
133861
133862
133863
133864
133865
133866
133867
133868
133869
133870
133871
133872
133873
133874
133875
133876
133877
133878

133879
133880
133881
133882
133883
133884
133885
133886
133887
133888
133889
133890
133891
133892
133893
133894
133895
133896
133897
133898
133899
133900
133901
  ** We call this the "compound-subquery flattening".
  */
  for(pSub=pSub->pPrior; pSub; pSub=pSub->pPrior){
    Select *pNew;
    ExprList *pOrderBy = p->pOrderBy;
    Expr *pLimit = p->pLimit;
    Select *pPrior = p->pPrior;


    p->pOrderBy = 0;
    p->pSrc = 0;
    p->pPrior = 0;
    p->pLimit = 0;
    pNew = sqlite3SelectDup(db, p, 0);
    p->pLimit = pLimit;
    p->pOrderBy = pOrderBy;
    p->pSrc = pSrc;
    p->op = TK_ALL;

    if( pNew==0 ){
      p->pPrior = pPrior;
    }else{



      pNew->pPrior = pPrior;
      if( pPrior ) pPrior->pNext = pNew;
      pNew->pNext = p;
      p->pPrior = pNew;
      SELECTTRACE(2,pParse,p,("compound-subquery flattener"
                              " creates %u as peer\n",pNew->selId));
    }
    if( db->mallocFailed ) return 1;
  }

  /* Begin flattening the iFrom-th entry of the FROM clause
  ** in the outer query.
  */
  pSub = pSub1 = pSubitem->pSelect;

  /* Delete the transient table structure associated with the
  ** subquery
  */
  sqlite3DbFree(db, pSubitem->zDatabase);
  sqlite3DbFree(db, pSubitem->zName);
  sqlite3DbFree(db, pSubitem->zAlias);
  pSubitem->zDatabase = 0;
  pSubitem->zName = 0;
  pSubitem->zAlias = 0;
  pSubitem->pSelect = 0;



  /* Defer deleting the Table object associated with the
  ** subquery until code generation is
  ** complete, since there may still exist Expr.pTab entries that
  ** refer to the subquery even after flattening.  Ticket #3346.
  **
  ** pSubitem->pTab is always non-NULL by test restrictions and tests above.
  */
  if( ALWAYS(pSubitem->pTab!=0) ){
    Table *pTabToDel = pSubitem->pTab;
    if( pTabToDel->nTabRef==1 ){
      Parse *pToplevel = sqlite3ParseToplevel(pParse);


      pTabToDel->pNextZombie = pToplevel->pZombieTab;
      pToplevel->pZombieTab = pTabToDel;
    }else{
      pTabToDel->nTabRef--;
    }
    pSubitem->pTab = 0;
  }

  /* The following loop runs once for each term in a compound-subquery
  ** flattening (as described above).  If we are doing a different kind
  ** of flattening - a flattening other than a compound-subquery flattening -
  ** then this loop only runs once.
  **
  ** This loop moves all of the FROM elements of the subquery into the
  ** the FROM clause of the outer query.  Before doing this, remember
  ** the cursor number for the original outer query FROM element in
  ** iParent.  The iParent cursor will never be used.  Subsequent code
  ** will scan expressions looking for iParent references and replace
  ** those references with expressions that resolve to the subquery FROM
  ** elements we are now copying in.
  */

  for(pParent=p; pParent; pParent=pParent->pPrior, pSub=pSub->pPrior){
    int nSubSrc;
    u8 jointype = 0;
    assert( pSub!=0 );
    pSubSrc = pSub->pSrc;     /* FROM clause of subquery */
    nSubSrc = pSubSrc->nSrc;  /* Number of terms in subquery FROM clause */
    pSrc = pParent->pSrc;     /* FROM clause of the outer query */

    if( pSrc ){
      assert( pParent==p );  /* First time through the loop */
      jointype = pSubitem->fg.jointype;
    }else{
      assert( pParent!=p );  /* 2nd and subsequent times through the loop */
      pSrc = sqlite3SrcListAppend(pParse, 0, 0, 0);
      if( pSrc==0 ) break;
      pParent->pSrc = pSrc;
    }

    /* The subquery uses a single slot of the FROM clause of the outer
    ** query.  If the subquery has more than one element in its FROM clause,
    ** then expand the outer query to make space for it to hold all elements
    ** of the subquery.
    **







>
>

<





<

>



>
>
>







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












>
>
|
|



















>








<
|
|
<
<
<
<
<







134621
134622
134623
134624
134625
134626
134627
134628
134629
134630

134631
134632
134633
134634
134635

134636
134637
134638
134639
134640
134641
134642
134643
134644
134645
134646
134647
134648
134649
134650






134651
134652




134653


134654

134655
134656
134657
134658
134659
134660
134661
134662
134663
134664
134665
134666
134667
134668
134669
134670
134671
134672
134673
134674
134675
134676
134677
134678
134679
134680
134681
134682
134683
134684
134685
134686
134687
134688
134689
134690
134691
134692
134693
134694
134695
134696
134697
134698
134699
134700
134701

134702
134703





134704
134705
134706
134707
134708
134709
134710
  ** We call this the "compound-subquery flattening".
  */
  for(pSub=pSub->pPrior; pSub; pSub=pSub->pPrior){
    Select *pNew;
    ExprList *pOrderBy = p->pOrderBy;
    Expr *pLimit = p->pLimit;
    Select *pPrior = p->pPrior;
    Table *pItemTab = pSubitem->pTab;
    pSubitem->pTab = 0;
    p->pOrderBy = 0;

    p->pPrior = 0;
    p->pLimit = 0;
    pNew = sqlite3SelectDup(db, p, 0);
    p->pLimit = pLimit;
    p->pOrderBy = pOrderBy;

    p->op = TK_ALL;
    pSubitem->pTab = pItemTab;
    if( pNew==0 ){
      p->pPrior = pPrior;
    }else{
      if( aCsrMap && db->mallocFailed==0 ){
        renumberCursors(pParse, pNew, iFrom, aCsrMap);
      }
      pNew->pPrior = pPrior;
      if( pPrior ) pPrior->pNext = pNew;
      pNew->pNext = p;
      p->pPrior = pNew;
      SELECTTRACE(2,pParse,p,("compound-subquery flattener"
                              " creates %u as peer\n",pNew->selId));
    }






    assert( pSubitem->pSelect==0 );
  }




  sqlite3DbFree(db, aCsrMap);


  if( db->mallocFailed ){

    pSubitem->pSelect = pSub1;
    return 1;
  }

  /* Defer deleting the Table object associated with the
  ** subquery until code generation is
  ** complete, since there may still exist Expr.pTab entries that
  ** refer to the subquery even after flattening.  Ticket #3346.
  **
  ** pSubitem->pTab is always non-NULL by test restrictions and tests above.
  */
  if( ALWAYS(pSubitem->pTab!=0) ){
    Table *pTabToDel = pSubitem->pTab;
    if( pTabToDel->nTabRef==1 ){
      Parse *pToplevel = sqlite3ParseToplevel(pParse);
      sqlite3ParserAddCleanup(pToplevel,
         (void(*)(sqlite3*,void*))sqlite3DeleteTable,
         pTabToDel);
      testcase( pToplevel->earlyCleanup );
    }else{
      pTabToDel->nTabRef--;
    }
    pSubitem->pTab = 0;
  }

  /* The following loop runs once for each term in a compound-subquery
  ** flattening (as described above).  If we are doing a different kind
  ** of flattening - a flattening other than a compound-subquery flattening -
  ** then this loop only runs once.
  **
  ** This loop moves all of the FROM elements of the subquery into the
  ** the FROM clause of the outer query.  Before doing this, remember
  ** the cursor number for the original outer query FROM element in
  ** iParent.  The iParent cursor will never be used.  Subsequent code
  ** will scan expressions looking for iParent references and replace
  ** those references with expressions that resolve to the subquery FROM
  ** elements we are now copying in.
  */
  pSub = pSub1;
  for(pParent=p; pParent; pParent=pParent->pPrior, pSub=pSub->pPrior){
    int nSubSrc;
    u8 jointype = 0;
    assert( pSub!=0 );
    pSubSrc = pSub->pSrc;     /* FROM clause of subquery */
    nSubSrc = pSubSrc->nSrc;  /* Number of terms in subquery FROM clause */
    pSrc = pParent->pSrc;     /* FROM clause of the outer query */


    if( pParent==p ){
      jointype = pSubitem->fg.jointype;     /* First time through the loop */





    }

    /* The subquery uses a single slot of the FROM clause of the outer
    ** query.  If the subquery has more than one element in its FROM clause,
    ** then expand the outer query to make space for it to hold all elements
    ** of the subquery.
    **
134007
134008
134009
134010
134011
134012
134013
134014
134015
134016
134017
134018
134019
134020
134021
  ** success.
  */
  sqlite3AggInfoPersistWalkerInit(&w, pParse);
  sqlite3WalkSelect(&w,pSub1);
  sqlite3SelectDelete(db, pSub1);

#if SELECTTRACE_ENABLED
  if( sqlite3_unsupported_selecttrace & 0x100 ){
    SELECTTRACE(0x100,pParse,p,("After flattening:\n"));
    sqlite3TreeViewSelect(0, p, 0);
  }
#endif

  return 1;
}







|







134816
134817
134818
134819
134820
134821
134822
134823
134824
134825
134826
134827
134828
134829
134830
  ** success.
  */
  sqlite3AggInfoPersistWalkerInit(&w, pParse);
  sqlite3WalkSelect(&w,pSub1);
  sqlite3SelectDelete(db, pSub1);

#if SELECTTRACE_ENABLED
  if( sqlite3SelectTrace & 0x100 ){
    SELECTTRACE(0x100,pParse,p,("After flattening:\n"));
    sqlite3TreeViewSelect(0, p, 0);
  }
#endif

  return 1;
}
134354
134355
134356
134357
134358
134359
134360


134361


134362
134363
134364
134365
134366
134367
134368
  const char *zFunc;                    /* Name of aggregate function pFunc */
  ExprList *pOrderBy;
  u8 sortFlags = 0;

  assert( *ppMinMax==0 );
  assert( pFunc->op==TK_AGG_FUNCTION );
  assert( !IsWindowFunc(pFunc) );


  if( pEList==0 || pEList->nExpr!=1 || ExprHasProperty(pFunc, EP_WinFunc) ){


    return eRet;
  }
  zFunc = pFunc->u.zToken;
  if( sqlite3StrICmp(zFunc, "min")==0 ){
    eRet = WHERE_ORDERBY_MIN;
    if( sqlite3ExprCanBeNull(pEList->a[0].pExpr) ){
      sortFlags = KEYINFO_ORDER_BIGNULL;







>
>
|
>
>







135163
135164
135165
135166
135167
135168
135169
135170
135171
135172
135173
135174
135175
135176
135177
135178
135179
135180
135181
  const char *zFunc;                    /* Name of aggregate function pFunc */
  ExprList *pOrderBy;
  u8 sortFlags = 0;

  assert( *ppMinMax==0 );
  assert( pFunc->op==TK_AGG_FUNCTION );
  assert( !IsWindowFunc(pFunc) );
  if( pEList==0
   || pEList->nExpr!=1
   || ExprHasProperty(pFunc, EP_WinFunc)
   || OptimizationDisabled(db, SQLITE_MinMaxOpt)
  ){
    return eRet;
  }
  zFunc = pFunc->u.zToken;
  if( sqlite3StrICmp(zFunc, "min")==0 ){
    eRet = WHERE_ORDERBY_MIN;
    if( sqlite3ExprCanBeNull(pEList->a[0].pExpr) ){
      sortFlags = KEYINFO_ORDER_BIGNULL;
134572
134573
134574
134575
134576
134577
134578
134579
134580
134581
134582
134583
134584





134585
134586
134587
134588
134589
134590
134591
** onto the top of the stack. If argument bFree is true, then this
** WITH clause will never be popped from the stack. In this case it
** should be freed along with the Parse object. In other cases, when
** bFree==0, the With object will be freed along with the SELECT
** statement with which it is associated.
*/
SQLITE_PRIVATE void sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){
  assert( bFree==0 || (pParse->pWith==0 && pParse->pWithToFree==0) );
  if( pWith ){
    assert( pParse->pWith!=pWith );
    pWith->pOuter = pParse->pWith;
    pParse->pWith = pWith;
    if( bFree ) pParse->pWithToFree = pWith;





  }
}

/*
** This function checks if argument pFrom refers to a CTE declared by
** a WITH clause on the stack currently maintained by the parser. And,
** if currently processing a CTE expression, if it is a recursive







<




|
>
>
>
>
>







135385
135386
135387
135388
135389
135390
135391

135392
135393
135394
135395
135396
135397
135398
135399
135400
135401
135402
135403
135404
135405
135406
135407
135408
** onto the top of the stack. If argument bFree is true, then this
** WITH clause will never be popped from the stack. In this case it
** should be freed along with the Parse object. In other cases, when
** bFree==0, the With object will be freed along with the SELECT
** statement with which it is associated.
*/
SQLITE_PRIVATE void sqlite3WithPush(Parse *pParse, With *pWith, u8 bFree){

  if( pWith ){
    assert( pParse->pWith!=pWith );
    pWith->pOuter = pParse->pWith;
    pParse->pWith = pWith;
    if( bFree ){
      sqlite3ParserAddCleanup(pParse,
         (void(*)(sqlite3*,void*))sqlite3WithDelete,
         pWith);
      testcase( pParse->earlyCleanup );
    }
  }
}

/*
** This function checks if argument pFrom refers to a CTE declared by
** a WITH clause on the stack currently maintained by the parser. And,
** if currently processing a CTE expression, if it is a recursive
135412
135413
135414
135415
135416
135417
135418
135419


135420
135421
135422
135423
135424
135425
135426
** sub-expression matches the criteria for being moved to the WHERE
** clause. If so, add it to the WHERE clause and replace the sub-expression
** within the HAVING expression with a constant "1".
*/
static int havingToWhereExprCb(Walker *pWalker, Expr *pExpr){
  if( pExpr->op!=TK_AND ){
    Select *pS = pWalker->u.pSelect;
    if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, pS->pGroupBy) ){


      sqlite3 *db = pWalker->pParse->db;
      Expr *pNew = sqlite3Expr(db, TK_INTEGER, "1");
      if( pNew ){
        Expr *pWhere = pS->pWhere;
        SWAP(Expr, *pNew, *pExpr);
        pNew = sqlite3ExprAnd(pWalker->pParse, pWhere, pNew);
        pS->pWhere = pNew;







|
>
>







136229
136230
136231
136232
136233
136234
136235
136236
136237
136238
136239
136240
136241
136242
136243
136244
136245
** sub-expression matches the criteria for being moved to the WHERE
** clause. If so, add it to the WHERE clause and replace the sub-expression
** within the HAVING expression with a constant "1".
*/
static int havingToWhereExprCb(Walker *pWalker, Expr *pExpr){
  if( pExpr->op!=TK_AND ){
    Select *pS = pWalker->u.pSelect;
    if( sqlite3ExprIsConstantOrGroupBy(pWalker->pParse, pExpr, pS->pGroupBy)
     && ExprAlwaysFalse(pExpr)==0
    ){
      sqlite3 *db = pWalker->pParse->db;
      Expr *pNew = sqlite3Expr(db, TK_INTEGER, "1");
      if( pNew ){
        Expr *pWhere = pS->pWhere;
        SWAP(Expr, *pNew, *pExpr);
        pNew = sqlite3ExprAnd(pWalker->pParse, pWhere, pNew);
        pS->pWhere = pNew;
135451
135452
135453
135454
135455
135456
135457
135458
135459
135460
135461
135462
135463
135464
135465
  Walker sWalker;
  memset(&sWalker, 0, sizeof(sWalker));
  sWalker.pParse = pParse;
  sWalker.xExprCallback = havingToWhereExprCb;
  sWalker.u.pSelect = p;
  sqlite3WalkExpr(&sWalker, p->pHaving);
#if SELECTTRACE_ENABLED
  if( sWalker.eCode && (sqlite3_unsupported_selecttrace & 0x100)!=0 ){
    SELECTTRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n"));
    sqlite3TreeViewSelect(0, p, 0);
  }
#endif
}

/*







|







136270
136271
136272
136273
136274
136275
136276
136277
136278
136279
136280
136281
136282
136283
136284
  Walker sWalker;
  memset(&sWalker, 0, sizeof(sWalker));
  sWalker.pParse = pParse;
  sWalker.xExprCallback = havingToWhereExprCb;
  sWalker.u.pSelect = p;
  sqlite3WalkExpr(&sWalker, p->pHaving);
#if SELECTTRACE_ENABLED
  if( sWalker.eCode && (sqlite3SelectTrace & 0x100)!=0 ){
    SELECTTRACE(0x100,pParse,p,("Move HAVING terms into WHERE:\n"));
    sqlite3TreeViewSelect(0, p, 0);
  }
#endif
}

/*
135573
135574
135575
135576
135577
135578
135579
135580
135581
135582
135583
135584
135585
135586
135587
    }
    pSub = pPrior;
  }
  p->pEList->a[0].pExpr = pExpr;
  p->selFlags &= ~SF_Aggregate;

#if SELECTTRACE_ENABLED
  if( sqlite3_unsupported_selecttrace & 0x400 ){
    SELECTTRACE(0x400,pParse,p,("After count-of-view optimization:\n"));
    sqlite3TreeViewSelect(0, p, 0);
  }
#endif
  return 1;
}
#endif /* SQLITE_COUNTOFVIEW_OPTIMIZATION */







|







136392
136393
136394
136395
136396
136397
136398
136399
136400
136401
136402
136403
136404
136405
136406
    }
    pSub = pPrior;
  }
  p->pEList->a[0].pExpr = pExpr;
  p->selFlags &= ~SF_Aggregate;

#if SELECTTRACE_ENABLED
  if( sqlite3SelectTrace & 0x400 ){
    SELECTTRACE(0x400,pParse,p,("After count-of-view optimization:\n"));
    sqlite3TreeViewSelect(0, p, 0);
  }
#endif
  return 1;
}
#endif /* SQLITE_COUNTOFVIEW_OPTIMIZATION */
135626
135627
135628
135629
135630
135631
135632
135633
135634
135635
135636
135637
135638
135639
135640
  v = sqlite3GetVdbe(pParse);
  if( p==0 || db->mallocFailed || pParse->nErr ){
    return 1;
  }
  if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1;
#if SELECTTRACE_ENABLED
  SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->addrExplain));
  if( sqlite3_unsupported_selecttrace & 0x100 ){
    sqlite3TreeViewSelect(0, p, 0);
  }
#endif

  assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistFifo );
  assert( p->pOrderBy==0 || pDest->eDest!=SRT_Fifo );
  assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistQueue );







|







136445
136446
136447
136448
136449
136450
136451
136452
136453
136454
136455
136456
136457
136458
136459
  v = sqlite3GetVdbe(pParse);
  if( p==0 || db->mallocFailed || pParse->nErr ){
    return 1;
  }
  if( sqlite3AuthCheck(pParse, SQLITE_SELECT, 0, 0, 0) ) return 1;
#if SELECTTRACE_ENABLED
  SELECTTRACE(1,pParse,p, ("begin processing:\n", pParse->addrExplain));
  if( sqlite3SelectTrace & 0x100 ){
    sqlite3TreeViewSelect(0, p, 0);
  }
#endif

  assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistFifo );
  assert( p->pOrderBy==0 || pDest->eDest!=SRT_Fifo );
  assert( p->pOrderBy==0 || pDest->eDest!=SRT_DistQueue );
135651
135652
135653
135654
135655
135656
135657
135658
135659
135660
135661
135662
135663
135664
135665
  }
  sqlite3SelectPrep(pParse, p, 0);
  if( pParse->nErr || db->mallocFailed ){
    goto select_end;
  }
  assert( p->pEList!=0 );
#if SELECTTRACE_ENABLED
  if( sqlite3_unsupported_selecttrace & 0x104 ){
    SELECTTRACE(0x104,pParse,p, ("after name resolution:\n"));
    sqlite3TreeViewSelect(0, p, 0);
  }
#endif

  /* If the SF_UpdateFrom flag is set, then this function is being called
  ** as part of populating the temp table for an UPDATE...FROM statement.







|







136470
136471
136472
136473
136474
136475
136476
136477
136478
136479
136480
136481
136482
136483
136484
  }
  sqlite3SelectPrep(pParse, p, 0);
  if( pParse->nErr || db->mallocFailed ){
    goto select_end;
  }
  assert( p->pEList!=0 );
#if SELECTTRACE_ENABLED
  if( sqlite3SelectTrace & 0x104 ){
    SELECTTRACE(0x104,pParse,p, ("after name resolution:\n"));
    sqlite3TreeViewSelect(0, p, 0);
  }
#endif

  /* If the SF_UpdateFrom flag is set, then this function is being called
  ** as part of populating the temp table for an UPDATE...FROM statement.
135686
135687
135688
135689
135690
135691
135692
135693
135694
135695
135696
135697
135698
135699
135700
#ifndef SQLITE_OMIT_WINDOWFUNC
  rc = sqlite3WindowRewrite(pParse, p);
  if( rc ){
    assert( db->mallocFailed || pParse->nErr>0 );
    goto select_end;
  }
#if SELECTTRACE_ENABLED
  if( p->pWin && (sqlite3_unsupported_selecttrace & 0x108)!=0 ){
    SELECTTRACE(0x104,pParse,p, ("after window rewrite:\n"));
    sqlite3TreeViewSelect(0, p, 0);
  }
#endif
#endif /* SQLITE_OMIT_WINDOWFUNC */
  pTabList = p->pSrc;
  isAgg = (p->selFlags & SF_Aggregate)!=0;







|







136505
136506
136507
136508
136509
136510
136511
136512
136513
136514
136515
136516
136517
136518
136519
#ifndef SQLITE_OMIT_WINDOWFUNC
  rc = sqlite3WindowRewrite(pParse, p);
  if( rc ){
    assert( db->mallocFailed || pParse->nErr>0 );
    goto select_end;
  }
#if SELECTTRACE_ENABLED
  if( p->pWin && (sqlite3SelectTrace & 0x108)!=0 ){
    SELECTTRACE(0x104,pParse,p, ("after window rewrite:\n"));
    sqlite3TreeViewSelect(0, p, 0);
  }
#endif
#endif /* SQLITE_OMIT_WINDOWFUNC */
  pTabList = p->pSrc;
  isAgg = (p->selFlags & SF_Aggregate)!=0;
135793
135794
135795
135796
135797
135798
135799
135800
135801
135802
135803
135804
135805
135806
135807
135808
135809
135810
135811
135812
135813
135814
135815
135816
135817
135818
135819
135820
135821
135822
135823
135824
135825
135826
  /* Handle compound SELECT statements using the separate multiSelect()
  ** procedure.
  */
  if( p->pPrior ){
    rc = multiSelect(pParse, p, pDest);
#if SELECTTRACE_ENABLED
    SELECTTRACE(0x1,pParse,p,("end compound-select processing\n"));
    if( (sqlite3_unsupported_selecttrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
      sqlite3TreeViewSelect(0, p, 0);
    }
#endif
    if( p->pNext==0 ) ExplainQueryPlanPop(pParse);
    return rc;
  }
#endif

  /* Do the WHERE-clause constant propagation optimization if this is
  ** a join.  No need to speed time on this operation for non-join queries
  ** as the equivalent optimization will be handled by query planner in
  ** sqlite3WhereBegin().
  */
  if( pTabList->nSrc>1
   && OptimizationEnabled(db, SQLITE_PropagateConst)
   && propagateConstants(pParse, p)
  ){
#if SELECTTRACE_ENABLED
    if( sqlite3_unsupported_selecttrace & 0x100 ){
      SELECTTRACE(0x100,pParse,p,("After constant propagation:\n"));
      sqlite3TreeViewSelect(0, p, 0);
    }
#endif
  }else{
    SELECTTRACE(0x100,pParse,p,("Constant propagation not helpful\n"));
  }







|


















|







136612
136613
136614
136615
136616
136617
136618
136619
136620
136621
136622
136623
136624
136625
136626
136627
136628
136629
136630
136631
136632
136633
136634
136635
136636
136637
136638
136639
136640
136641
136642
136643
136644
136645
  /* Handle compound SELECT statements using the separate multiSelect()
  ** procedure.
  */
  if( p->pPrior ){
    rc = multiSelect(pParse, p, pDest);
#if SELECTTRACE_ENABLED
    SELECTTRACE(0x1,pParse,p,("end compound-select processing\n"));
    if( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
      sqlite3TreeViewSelect(0, p, 0);
    }
#endif
    if( p->pNext==0 ) ExplainQueryPlanPop(pParse);
    return rc;
  }
#endif

  /* Do the WHERE-clause constant propagation optimization if this is
  ** a join.  No need to speed time on this operation for non-join queries
  ** as the equivalent optimization will be handled by query planner in
  ** sqlite3WhereBegin().
  */
  if( pTabList->nSrc>1
   && OptimizationEnabled(db, SQLITE_PropagateConst)
   && propagateConstants(pParse, p)
  ){
#if SELECTTRACE_ENABLED
    if( sqlite3SelectTrace & 0x100 ){
      SELECTTRACE(0x100,pParse,p,("After constant propagation:\n"));
      sqlite3TreeViewSelect(0, p, 0);
    }
#endif
  }else{
    SELECTTRACE(0x100,pParse,p,("Constant propagation not helpful\n"));
  }
135900
135901
135902
135903
135904
135905
135906
135907
135908
135909
135910
135911
135912
135913
135914
    ** inside the subquery.  This can help the subquery to run more efficiently.
    */
    if( OptimizationEnabled(db, SQLITE_PushDown)
     && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor,
                           (pItem->fg.jointype & JT_OUTER)!=0)
    ){
#if SELECTTRACE_ENABLED
      if( sqlite3_unsupported_selecttrace & 0x100 ){
        SELECTTRACE(0x100,pParse,p,
            ("After WHERE-clause push-down into subquery %d:\n", pSub->selId));
        sqlite3TreeViewSelect(0, p, 0);
      }
#endif
    }else{
      SELECTTRACE(0x100,pParse,p,("Push-down not possible\n"));







|







136719
136720
136721
136722
136723
136724
136725
136726
136727
136728
136729
136730
136731
136732
136733
    ** inside the subquery.  This can help the subquery to run more efficiently.
    */
    if( OptimizationEnabled(db, SQLITE_PushDown)
     && pushDownWhereTerms(pParse, pSub, p->pWhere, pItem->iCursor,
                           (pItem->fg.jointype & JT_OUTER)!=0)
    ){
#if SELECTTRACE_ENABLED
      if( sqlite3SelectTrace & 0x100 ){
        SELECTTRACE(0x100,pParse,p,
            ("After WHERE-clause push-down into subquery %d:\n", pSub->selId));
        sqlite3TreeViewSelect(0, p, 0);
      }
#endif
    }else{
      SELECTTRACE(0x100,pParse,p,("Push-down not possible\n"));
136000
136001
136002
136003
136004
136005
136006
136007
136008
136009
136010
136011
136012
136013
136014
  pEList = p->pEList;
  pWhere = p->pWhere;
  pGroupBy = p->pGroupBy;
  pHaving = p->pHaving;
  sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0;

#if SELECTTRACE_ENABLED
  if( sqlite3_unsupported_selecttrace & 0x400 ){
    SELECTTRACE(0x400,pParse,p,("After all FROM-clause analysis:\n"));
    sqlite3TreeViewSelect(0, p, 0);
  }
#endif

  /* If the query is DISTINCT with an ORDER BY but is not an aggregate, and
  ** if the select-list is the same as the ORDER BY list, then this query







|







136819
136820
136821
136822
136823
136824
136825
136826
136827
136828
136829
136830
136831
136832
136833
  pEList = p->pEList;
  pWhere = p->pWhere;
  pGroupBy = p->pGroupBy;
  pHaving = p->pHaving;
  sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0;

#if SELECTTRACE_ENABLED
  if( sqlite3SelectTrace & 0x400 ){
    SELECTTRACE(0x400,pParse,p,("After all FROM-clause analysis:\n"));
    sqlite3TreeViewSelect(0, p, 0);
  }
#endif

  /* If the query is DISTINCT with an ORDER BY but is not an aggregate, and
  ** if the select-list is the same as the ORDER BY list, then this query
136036
136037
136038
136039
136040
136041
136042
136043
136044
136045
136046
136047
136048
136049
136050
    p->selFlags |= SF_Aggregate;
    /* Notice that even thought SF_Distinct has been cleared from p->selFlags,
    ** the sDistinct.isTnct is still set.  Hence, isTnct represents the
    ** original setting of the SF_Distinct flag, not the current setting */
    assert( sDistinct.isTnct );

#if SELECTTRACE_ENABLED
    if( sqlite3_unsupported_selecttrace & 0x400 ){
      SELECTTRACE(0x400,pParse,p,("Transform DISTINCT into GROUP BY:\n"));
      sqlite3TreeViewSelect(0, p, 0);
    }
#endif
  }

  /* If there is an ORDER BY clause, then create an ephemeral index to







|







136855
136856
136857
136858
136859
136860
136861
136862
136863
136864
136865
136866
136867
136868
136869
    p->selFlags |= SF_Aggregate;
    /* Notice that even thought SF_Distinct has been cleared from p->selFlags,
    ** the sDistinct.isTnct is still set.  Hence, isTnct represents the
    ** original setting of the SF_Distinct flag, not the current setting */
    assert( sDistinct.isTnct );

#if SELECTTRACE_ENABLED
    if( sqlite3SelectTrace & 0x400 ){
      SELECTTRACE(0x400,pParse,p,("Transform DISTINCT into GROUP BY:\n"));
      sqlite3TreeViewSelect(0, p, 0);
    }
#endif
  }

  /* If there is an ORDER BY clause, then create an ephemeral index to
136284
136285
136286
136287
136288
136289
136290
136291
136292
136293
136294




136295
136296
136297
136298
136299
136300
136301
      }
#endif
      sNC.ncFlags &= ~NC_InAggFunc;
    }
    pAggInfo->mxReg = pParse->nMem;
    if( db->mallocFailed ) goto select_end;
#if SELECTTRACE_ENABLED
    if( sqlite3_unsupported_selecttrace & 0x400 ){
      int ii;
      SELECTTRACE(0x400,pParse,p,("After aggregate analysis %p:\n", pAggInfo));
      sqlite3TreeViewSelect(0, p, 0);




      for(ii=0; ii<pAggInfo->nColumn; ii++){
        sqlite3DebugPrintf("agg-column[%d] iMem=%d\n",
            ii, pAggInfo->aCol[ii].iMem);
        sqlite3TreeViewExpr(0, pAggInfo->aCol[ii].pCExpr, 0);
      }
      for(ii=0; ii<pAggInfo->nFunc; ii++){
        sqlite3DebugPrintf("agg-func[%d]: iMem=%d\n",







|



>
>
>
>







137103
137104
137105
137106
137107
137108
137109
137110
137111
137112
137113
137114
137115
137116
137117
137118
137119
137120
137121
137122
137123
137124
      }
#endif
      sNC.ncFlags &= ~NC_InAggFunc;
    }
    pAggInfo->mxReg = pParse->nMem;
    if( db->mallocFailed ) goto select_end;
#if SELECTTRACE_ENABLED
    if( sqlite3SelectTrace & 0x400 ){
      int ii;
      SELECTTRACE(0x400,pParse,p,("After aggregate analysis %p:\n", pAggInfo));
      sqlite3TreeViewSelect(0, p, 0);
      if( minMaxFlag ){
        sqlite3DebugPrintf("MIN/MAX Optimization (0x%02x) adds:\n", minMaxFlag);
        sqlite3TreeViewExprList(0, pMinMaxOrderBy, 0, "ORDERBY");
      }
      for(ii=0; ii<pAggInfo->nColumn; ii++){
        sqlite3DebugPrintf("agg-column[%d] iMem=%d\n",
            ii, pAggInfo->aCol[ii].iMem);
        sqlite3TreeViewExpr(0, pAggInfo->aCol[ii].pCExpr, 0);
      }
      for(ii=0; ii<pAggInfo->nFunc; ii++){
        sqlite3DebugPrintf("agg-func[%d]: iMem=%d\n",
136477
136478
136479
136480
136481
136482
136483
136484
136485
136486
136487
136488
136489
136490
136491
      updateAccumulator(pParse, iUseFlag, pAggInfo);
      sqlite3VdbeAddOp2(v, OP_Integer, 1, iUseFlag);
      VdbeComment((v, "indicate data in accumulator"));

      /* End of the loop
      */
      if( groupBySort ){
        sqlite3VdbeAddOp2(v, OP_SorterNext, pAggInfo->sortingIdx, addrTopOfLoop);
        VdbeCoverage(v);
      }else{
        sqlite3WhereEnd(pWInfo);
        sqlite3VdbeChangeToNoop(v, addrSortingIdx);
      }

      /* Output the final row of result







|







137300
137301
137302
137303
137304
137305
137306
137307
137308
137309
137310
137311
137312
137313
137314
      updateAccumulator(pParse, iUseFlag, pAggInfo);
      sqlite3VdbeAddOp2(v, OP_Integer, 1, iUseFlag);
      VdbeComment((v, "indicate data in accumulator"));

      /* End of the loop
      */
      if( groupBySort ){
        sqlite3VdbeAddOp2(v, OP_SorterNext, pAggInfo->sortingIdx,addrTopOfLoop);
        VdbeCoverage(v);
      }else{
        sqlite3WhereEnd(pWInfo);
        sqlite3VdbeChangeToNoop(v, addrSortingIdx);
      }

      /* Output the final row of result
136589
136590
136591
136592
136593
136594
136595
136596
136597
136598
136599
136600
136601
136602
136603
          sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO);
        }
        sqlite3VdbeAddOp2(v, OP_Count, iCsr, pAggInfo->aFunc[0].iMem);
        sqlite3VdbeAddOp1(v, OP_Close, iCsr);
        explainSimpleCount(pParse, pTab, pBest);
      }else{
        int regAcc = 0;           /* "populate accumulators" flag */
        int addrSkip;

        /* 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
        ** there are no min() or max functions or (b) always updated for the







<







137412
137413
137414
137415
137416
137417
137418

137419
137420
137421
137422
137423
137424
137425
          sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO);
        }
        sqlite3VdbeAddOp2(v, OP_Count, iCsr, pAggInfo->aFunc[0].iMem);
        sqlite3VdbeAddOp1(v, OP_Close, iCsr);
        explainSimpleCount(pParse, pTab, pBest);
      }else{
        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
        ** there are no min() or max functions or (b) always updated for the
136638
136639
136640
136641
136642
136643
136644
136645
136646
136647
136648
136649
136650
136651
136652
136653
136654
        pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMaxOrderBy,
                                   0, minMaxFlag, 0);
        if( pWInfo==0 ){
          goto select_end;
        }
        updateAccumulator(pParse, regAcc, pAggInfo);
        if( regAcc ) sqlite3VdbeAddOp2(v, OP_Integer, 1, regAcc);
        addrSkip = sqlite3WhereOrderByLimitOptLabel(pWInfo);
        if( addrSkip!=sqlite3WhereContinueLabel(pWInfo) ){
          sqlite3VdbeGoto(v, addrSkip);
        }
        sqlite3WhereEnd(pWInfo);
        finalizeAggFunctions(pParse, pAggInfo);
      }

      sSort.pOrderBy = 0;
      sqlite3ExprIfFalse(pParse, pHaving, addrEnd, SQLITE_JUMPIFNULL);







|
|
<







137460
137461
137462
137463
137464
137465
137466
137467
137468

137469
137470
137471
137472
137473
137474
137475
        pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere, pMinMaxOrderBy,
                                   0, minMaxFlag, 0);
        if( pWInfo==0 ){
          goto select_end;
        }
        updateAccumulator(pParse, regAcc, pAggInfo);
        if( regAcc ) sqlite3VdbeAddOp2(v, OP_Integer, 1, regAcc);
        if( minMaxFlag ){
          sqlite3WhereMinMaxOptEarlyOut(v, pWInfo);

        }
        sqlite3WhereEnd(pWInfo);
        finalizeAggFunctions(pParse, pAggInfo);
      }

      sSort.pOrderBy = 0;
      sqlite3ExprIfFalse(pParse, pHaving, addrEnd, SQLITE_JUMPIFNULL);
136686
136687
136688
136689
136690
136691
136692
136693
136694
136695
136696
136697
136698
136699
136700
136701
136702
136703
136704
136705
136706
136707
136708
136709
136710
136711
136712
136713
136714
136715
136716
136717
  */
select_end:
  sqlite3ExprListDelete(db, pMinMaxOrderBy);
#ifdef SQLITE_DEBUG
  if( pAggInfo && !db->mallocFailed ){
    for(i=0; i<pAggInfo->nColumn; i++){
      Expr *pExpr = pAggInfo->aCol[i].pCExpr;
      assert( pExpr!=0 || db->mallocFailed );
      if( pExpr==0 ) continue;
      assert( pExpr->pAggInfo==pAggInfo );
      assert( pExpr->iAgg==i );
    }
    for(i=0; i<pAggInfo->nFunc; i++){
      Expr *pExpr = pAggInfo->aFunc[i].pFExpr;
      assert( pExpr!=0 || db->mallocFailed );
      if( pExpr==0 ) continue;
      assert( pExpr->pAggInfo==pAggInfo );
      assert( pExpr->iAgg==i );
    }
  }
#endif

#if SELECTTRACE_ENABLED
  SELECTTRACE(0x1,pParse,p,("end processing\n"));
  if( (sqlite3_unsupported_selecttrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
    sqlite3TreeViewSelect(0, p, 0);
  }
#endif
  ExplainQueryPlanPop(pParse);
  return rc;
}








|
<





|
<








|







137507
137508
137509
137510
137511
137512
137513
137514

137515
137516
137517
137518
137519
137520

137521
137522
137523
137524
137525
137526
137527
137528
137529
137530
137531
137532
137533
137534
137535
137536
  */
select_end:
  sqlite3ExprListDelete(db, pMinMaxOrderBy);
#ifdef SQLITE_DEBUG
  if( pAggInfo && !db->mallocFailed ){
    for(i=0; i<pAggInfo->nColumn; i++){
      Expr *pExpr = pAggInfo->aCol[i].pCExpr;
      assert( pExpr!=0 );

      assert( pExpr->pAggInfo==pAggInfo );
      assert( pExpr->iAgg==i );
    }
    for(i=0; i<pAggInfo->nFunc; i++){
      Expr *pExpr = pAggInfo->aFunc[i].pFExpr;
      assert( pExpr!=0 );

      assert( pExpr->pAggInfo==pAggInfo );
      assert( pExpr->iAgg==i );
    }
  }
#endif

#if SELECTTRACE_ENABLED
  SELECTTRACE(0x1,pParse,p,("end processing\n"));
  if( (sqlite3SelectTrace & 0x2000)!=0 && ExplainQueryPlanParent(pParse)==0 ){
    sqlite3TreeViewSelect(0, p, 0);
  }
#endif
  ExplainQueryPlanPop(pParse);
  return rc;
}

137916
137917
137918
137919
137920
137921
137922
137923
137924
137925
137926
137927
137928
137929
137930
    pProgram->nCsr = pSubParse->nTab;
    pProgram->token = (void *)pTrigger;
    pPrg->aColmask[0] = pSubParse->oldmask;
    pPrg->aColmask[1] = pSubParse->newmask;
    sqlite3VdbeDelete(v);
  }

  assert( !pSubParse->pAinc       && !pSubParse->pZombieTab );
  assert( !pSubParse->pTriggerPrg && !pSubParse->nMaxArg );
  sqlite3ParserReset(pSubParse);
  sqlite3StackFree(db, pSubParse);

  return pPrg;
}








<







138735
138736
138737
138738
138739
138740
138741

138742
138743
138744
138745
138746
138747
138748
    pProgram->nCsr = pSubParse->nTab;
    pProgram->token = (void *)pTrigger;
    pPrg->aColmask[0] = pSubParse->oldmask;
    pPrg->aColmask[1] = pSubParse->newmask;
    sqlite3VdbeDelete(v);
  }


  assert( !pSubParse->pTriggerPrg && !pSubParse->nMaxArg );
  sqlite3ParserReset(pSubParse);
  sqlite3StackFree(db, pSubParse);

  return pPrg;
}

139474
139475
139476
139477
139478
139479
139480
139481

139482
139483
139484
139485
139486

139487


139488


139489

139490
139491
139492
139493
139494
139495
139496
139497
139498
139499
139500

139501
139502
139503
139504
139505
139506
139507
139508
139509
139510
139511
139512

139513
139514
139515
139516
139517
139518
139519
139520

139521
139522
139523
139524
139525
139526

139527
139528
139529
139530
139531
139532
139533
139534
*/
/* #include "sqliteInt.h" */

#ifndef SQLITE_OMIT_UPSERT
/*
** Free a list of Upsert objects
*/
SQLITE_PRIVATE void sqlite3UpsertDelete(sqlite3 *db, Upsert *p){

  if( p ){
    sqlite3ExprListDelete(db, p->pUpsertTarget);
    sqlite3ExprDelete(db, p->pUpsertTargetWhere);
    sqlite3ExprListDelete(db, p->pUpsertSet);
    sqlite3ExprDelete(db, p->pUpsertWhere);

    sqlite3DbFree(db, p);


  }


}


/*
** Duplicate an Upsert object.
*/
SQLITE_PRIVATE Upsert *sqlite3UpsertDup(sqlite3 *db, Upsert *p){
  if( p==0 ) return 0;
  return sqlite3UpsertNew(db,
           sqlite3ExprListDup(db, p->pUpsertTarget, 0),
           sqlite3ExprDup(db, p->pUpsertTargetWhere, 0),
           sqlite3ExprListDup(db, p->pUpsertSet, 0),
           sqlite3ExprDup(db, p->pUpsertWhere, 0)

         );
}

/*
** Create a new Upsert object.
*/
SQLITE_PRIVATE Upsert *sqlite3UpsertNew(
  sqlite3 *db,           /* Determines which memory allocator to use */
  ExprList *pTarget,     /* Target argument to ON CONFLICT, or NULL */
  Expr *pTargetWhere,    /* Optional WHERE clause on the target */
  ExprList *pSet,        /* UPDATE columns, or NULL for a DO NOTHING */
  Expr *pWhere           /* WHERE clause for the ON CONFLICT UPDATE */

){
  Upsert *pNew;
  pNew = sqlite3DbMallocRaw(db, sizeof(Upsert));
  if( pNew==0 ){
    sqlite3ExprListDelete(db, pTarget);
    sqlite3ExprDelete(db, pTargetWhere);
    sqlite3ExprListDelete(db, pSet);
    sqlite3ExprDelete(db, pWhere);

    return 0;
  }else{
    pNew->pUpsertTarget = pTarget;
    pNew->pUpsertTargetWhere = pTargetWhere;
    pNew->pUpsertSet = pSet;
    pNew->pUpsertWhere = pWhere;

    pNew->pUpsertIdx = 0;
  }
  return pNew;
}

/*
** Analyze the ON CONFLICT clause described by pUpsert.  Resolve all
** symbols in the conflict-target.







|
>
|




>

>
>
|
>
>

>










|
>











|
>


|





>






>
|







140292
140293
140294
140295
140296
140297
140298
140299
140300
140301
140302
140303
140304
140305
140306
140307
140308
140309
140310
140311
140312
140313
140314
140315
140316
140317
140318
140319
140320
140321
140322
140323
140324
140325
140326
140327
140328
140329
140330
140331
140332
140333
140334
140335
140336
140337
140338
140339
140340
140341
140342
140343
140344
140345
140346
140347
140348
140349
140350
140351
140352
140353
140354
140355
140356
140357
140358
140359
140360
140361
140362
140363
*/
/* #include "sqliteInt.h" */

#ifndef SQLITE_OMIT_UPSERT
/*
** Free a list of Upsert objects
*/
static void SQLITE_NOINLINE upsertDelete(sqlite3 *db, Upsert *p){
  do{
    Upsert *pNext = p->pNextUpsert;
    sqlite3ExprListDelete(db, p->pUpsertTarget);
    sqlite3ExprDelete(db, p->pUpsertTargetWhere);
    sqlite3ExprListDelete(db, p->pUpsertSet);
    sqlite3ExprDelete(db, p->pUpsertWhere);
    sqlite3DbFree(db, p->pToFree);
    sqlite3DbFree(db, p);
    p = pNext;
  }while( p );
}
SQLITE_PRIVATE void sqlite3UpsertDelete(sqlite3 *db, Upsert *p){
  if( p ) upsertDelete(db, p);
}


/*
** Duplicate an Upsert object.
*/
SQLITE_PRIVATE Upsert *sqlite3UpsertDup(sqlite3 *db, Upsert *p){
  if( p==0 ) return 0;
  return sqlite3UpsertNew(db,
           sqlite3ExprListDup(db, p->pUpsertTarget, 0),
           sqlite3ExprDup(db, p->pUpsertTargetWhere, 0),
           sqlite3ExprListDup(db, p->pUpsertSet, 0),
           sqlite3ExprDup(db, p->pUpsertWhere, 0),
           sqlite3UpsertDup(db, p->pNextUpsert)
         );
}

/*
** Create a new Upsert object.
*/
SQLITE_PRIVATE Upsert *sqlite3UpsertNew(
  sqlite3 *db,           /* Determines which memory allocator to use */
  ExprList *pTarget,     /* Target argument to ON CONFLICT, or NULL */
  Expr *pTargetWhere,    /* Optional WHERE clause on the target */
  ExprList *pSet,        /* UPDATE columns, or NULL for a DO NOTHING */
  Expr *pWhere,          /* WHERE clause for the ON CONFLICT UPDATE */
  Upsert *pNext          /* Next ON CONFLICT clause in the list */
){
  Upsert *pNew;
  pNew = sqlite3DbMallocZero(db, sizeof(Upsert));
  if( pNew==0 ){
    sqlite3ExprListDelete(db, pTarget);
    sqlite3ExprDelete(db, pTargetWhere);
    sqlite3ExprListDelete(db, pSet);
    sqlite3ExprDelete(db, pWhere);
    sqlite3UpsertDelete(db, pNext);
    return 0;
  }else{
    pNew->pUpsertTarget = pTarget;
    pNew->pUpsertTargetWhere = pTargetWhere;
    pNew->pUpsertSet = pSet;
    pNew->pUpsertWhere = pWhere;
    pNew->isDoUpdate = pSet!=0;
    pNew->pNextUpsert = pNext;
  }
  return pNew;
}

/*
** Analyze the ON CONFLICT clause described by pUpsert.  Resolve all
** symbols in the conflict-target.
139545
139546
139547
139548
139549
139550
139551

139552
139553
139554
139555
139556
139557
139558
139559
139560
139561
139562
139563
139564


139565
139566
139567
139568
139569
139570
139571
139572
139573
139574
139575
139576
139577
139578
139579
139580
139581

139582
139583
139584
139585
139586
139587
139588
139589
139590
139591
139592
139593
139594
139595
139596
139597
139598
139599
139600
139601
139602
139603
139604
139605
139606
139607
139608
139609
139610
139611
139612
139613
139614
139615
139616
139617
139618
139619
139620
139621
139622
139623
139624
139625
139626
139627
139628
139629
139630
139631
139632
139633
139634
139635
139636
139637
139638
139639
139640
139641
139642







139643
139644
139645



































139646
139647
139648
139649
139650
139651
139652
  int rc;                 /* Result code */
  int iCursor;            /* Cursor used by pTab */
  Index *pIdx;            /* One of the indexes of pTab */
  ExprList *pTarget;      /* The conflict-target clause */
  Expr *pTerm;            /* One term of the conflict-target clause */
  NameContext sNC;        /* Context for resolving symbolic names */
  Expr sCol[2];           /* Index column converted into an Expr */


  assert( pTabList->nSrc==1 );
  assert( pTabList->a[0].pTab!=0 );
  assert( pUpsert!=0 );
  assert( pUpsert->pUpsertTarget!=0 );

  /* Resolve all symbolic names in the conflict-target clause, which
  ** includes both the list of columns and the optional partial-index
  ** WHERE clause.
  */
  memset(&sNC, 0, sizeof(sNC));
  sNC.pParse = pParse;
  sNC.pSrcList = pTabList;


  rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
  if( rc ) return rc;
  rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere);
  if( rc ) return rc;

  /* Check to see if the conflict target matches the rowid. */
  pTab = pTabList->a[0].pTab;
  pTarget = pUpsert->pUpsertTarget;
  iCursor = pTabList->a[0].iCursor;
  if( HasRowid(pTab)
   && pTarget->nExpr==1
   && (pTerm = pTarget->a[0].pExpr)->op==TK_COLUMN
   && pTerm->iColumn==XN_ROWID
  ){
    /* The conflict-target is the rowid of the primary table */
    assert( pUpsert->pUpsertIdx==0 );
    return SQLITE_OK;

  }

  /* Initialize sCol[0..1] to be an expression parse tree for a
  ** single column of an index.  The sCol[0] node will be the TK_COLLATE
  ** operator and sCol[1] will be the TK_COLUMN operator.  Code below
  ** will populate the specific collation and column number values
  ** prior to comparing against the conflict-target expression.
  */
  memset(sCol, 0, sizeof(sCol));
  sCol[0].op = TK_COLLATE;
  sCol[0].pLeft = &sCol[1];
  sCol[1].op = TK_COLUMN;
  sCol[1].iTable = pTabList->a[0].iCursor;

  /* Check for matches against other indexes */
  for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
    int ii, jj, nn;
    if( !IsUniqueIndex(pIdx) ) continue;
    if( pTarget->nExpr!=pIdx->nKeyCol ) continue;
    if( pIdx->pPartIdxWhere ){
      if( pUpsert->pUpsertTargetWhere==0 ) continue;
      if( sqlite3ExprCompare(pParse, pUpsert->pUpsertTargetWhere,
                             pIdx->pPartIdxWhere, iCursor)!=0 ){
        continue;
      }
    }
    nn = pIdx->nKeyCol;
    for(ii=0; ii<nn; ii++){
      Expr *pExpr;
      sCol[0].u.zToken = (char*)pIdx->azColl[ii];
      if( pIdx->aiColumn[ii]==XN_EXPR ){
        assert( pIdx->aColExpr!=0 );
        assert( pIdx->aColExpr->nExpr>ii );
        pExpr = pIdx->aColExpr->a[ii].pExpr;
        if( pExpr->op!=TK_COLLATE ){
          sCol[0].pLeft = pExpr;
          pExpr = &sCol[0];
        }
      }else{
        sCol[0].pLeft = &sCol[1];
        sCol[1].iColumn = pIdx->aiColumn[ii];
        pExpr = &sCol[0];
      }
      for(jj=0; jj<nn; jj++){
        if( sqlite3ExprCompare(pParse, pTarget->a[jj].pExpr, pExpr,iCursor)<2 ){
          break;  /* Column ii of the index matches column jj of target */
        }
      }
      if( jj>=nn ){
        /* The target contains no match for column jj of the index */
        break;
      }
    }
    if( ii<nn ){
      /* Column ii of the index did not match any term of the conflict target.
      ** Continue the search with the next index. */
      continue;
    }
    pUpsert->pUpsertIdx = pIdx;
    return SQLITE_OK;
  }







  sqlite3ErrorMsg(pParse, "ON CONFLICT clause does not match any "
                          "PRIMARY KEY or UNIQUE constraint");
  return SQLITE_ERROR;



































}

/*
** Generate bytecode that does an UPDATE as part of an upsert.
**
** If pIdx is NULL, then the UNIQUE constraint that failed was the IPK.
** In this case parameter iCur is a cursor open on the table b-tree that







>













>
>
|
|
|
|

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

|
|
|
|
|
|
|
|
|
|
|

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







140374
140375
140376
140377
140378
140379
140380
140381
140382
140383
140384
140385
140386
140387
140388
140389
140390
140391
140392
140393
140394
140395
140396
140397
140398
140399
140400
140401
140402
140403
140404
140405
140406
140407
140408
140409
140410
140411
140412

140413
140414
140415
140416
140417
140418
140419
140420
140421
140422
140423
140424
140425
140426
140427
140428
140429
140430
140431
140432
140433
140434
140435
140436
140437
140438
140439
140440
140441
140442
140443
140444
140445
140446
140447
140448
140449
140450
140451
140452
140453
140454
140455
140456
140457
140458
140459
140460
140461
140462
140463
140464
140465
140466
140467
140468
140469
140470
140471
140472
140473
140474
140475
140476
140477
140478
140479
140480
140481
140482
140483
140484
140485
140486
140487
140488
140489
140490
140491
140492
140493
140494
140495
140496
140497
140498
140499
140500
140501
140502
140503
140504
140505
140506
140507
140508
140509
140510
140511
140512
140513
140514
140515
140516
140517
140518
140519
140520
140521
140522
140523
140524
140525
140526
  int rc;                 /* Result code */
  int iCursor;            /* Cursor used by pTab */
  Index *pIdx;            /* One of the indexes of pTab */
  ExprList *pTarget;      /* The conflict-target clause */
  Expr *pTerm;            /* One term of the conflict-target clause */
  NameContext sNC;        /* Context for resolving symbolic names */
  Expr sCol[2];           /* Index column converted into an Expr */
  int nClause = 0;        /* Counter of ON CONFLICT clauses */

  assert( pTabList->nSrc==1 );
  assert( pTabList->a[0].pTab!=0 );
  assert( pUpsert!=0 );
  assert( pUpsert->pUpsertTarget!=0 );

  /* Resolve all symbolic names in the conflict-target clause, which
  ** includes both the list of columns and the optional partial-index
  ** WHERE clause.
  */
  memset(&sNC, 0, sizeof(sNC));
  sNC.pParse = pParse;
  sNC.pSrcList = pTabList;
  for(; pUpsert && pUpsert->pUpsertTarget;
        pUpsert=pUpsert->pNextUpsert, nClause++){
    rc = sqlite3ResolveExprListNames(&sNC, pUpsert->pUpsertTarget);
    if( rc ) return rc;
    rc = sqlite3ResolveExprNames(&sNC, pUpsert->pUpsertTargetWhere);
    if( rc ) return rc;

    /* Check to see if the conflict target matches the rowid. */
    pTab = pTabList->a[0].pTab;
    pTarget = pUpsert->pUpsertTarget;
    iCursor = pTabList->a[0].iCursor;
    if( HasRowid(pTab)
     && pTarget->nExpr==1
     && (pTerm = pTarget->a[0].pExpr)->op==TK_COLUMN
     && pTerm->iColumn==XN_ROWID
    ){
      /* The conflict-target is the rowid of the primary table */
      assert( pUpsert->pUpsertIdx==0 );

      continue;
    }

    /* Initialize sCol[0..1] to be an expression parse tree for a
    ** single column of an index.  The sCol[0] node will be the TK_COLLATE
    ** operator and sCol[1] will be the TK_COLUMN operator.  Code below
    ** will populate the specific collation and column number values
    ** prior to comparing against the conflict-target expression.
    */
    memset(sCol, 0, sizeof(sCol));
    sCol[0].op = TK_COLLATE;
    sCol[0].pLeft = &sCol[1];
    sCol[1].op = TK_COLUMN;
    sCol[1].iTable = pTabList->a[0].iCursor;

    /* Check for matches against other indexes */
    for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
      int ii, jj, nn;
      if( !IsUniqueIndex(pIdx) ) continue;
      if( pTarget->nExpr!=pIdx->nKeyCol ) continue;
      if( pIdx->pPartIdxWhere ){
        if( pUpsert->pUpsertTargetWhere==0 ) continue;
        if( sqlite3ExprCompare(pParse, pUpsert->pUpsertTargetWhere,
                               pIdx->pPartIdxWhere, iCursor)!=0 ){
          continue;
        }
      }
      nn = pIdx->nKeyCol;
      for(ii=0; ii<nn; ii++){
        Expr *pExpr;
        sCol[0].u.zToken = (char*)pIdx->azColl[ii];
        if( pIdx->aiColumn[ii]==XN_EXPR ){
          assert( pIdx->aColExpr!=0 );
          assert( pIdx->aColExpr->nExpr>ii );
          pExpr = pIdx->aColExpr->a[ii].pExpr;
          if( pExpr->op!=TK_COLLATE ){
            sCol[0].pLeft = pExpr;
            pExpr = &sCol[0];
          }
        }else{
          sCol[0].pLeft = &sCol[1];
          sCol[1].iColumn = pIdx->aiColumn[ii];
          pExpr = &sCol[0];
        }
        for(jj=0; jj<nn; jj++){
          if( sqlite3ExprCompare(pParse,pTarget->a[jj].pExpr,pExpr,iCursor)<2 ){
            break;  /* Column ii of the index matches column jj of target */
          }
        }
        if( jj>=nn ){
          /* The target contains no match for column jj of the index */
          break;
        }
      }
      if( ii<nn ){
        /* Column ii of the index did not match any term of the conflict target.
        ** Continue the search with the next index. */
        continue;
      }
      pUpsert->pUpsertIdx = pIdx;
      break;
    }
    if( pUpsert->pUpsertIdx==0 ){
      char zWhich[16];
      if( nClause==0 && pUpsert->pNextUpsert==0 ){
        zWhich[0] = 0;
      }else{
        sqlite3_snprintf(sizeof(zWhich),zWhich,"%r ", nClause+1);
      }
      sqlite3ErrorMsg(pParse, "%sON CONFLICT clause does not match any "
                              "PRIMARY KEY or UNIQUE constraint", zWhich);
      return SQLITE_ERROR;
    }
  }
  return SQLITE_OK;
}

/*
** Return true if pUpsert is the last ON CONFLICT clause with a
** conflict target, or if pUpsert is followed by another ON CONFLICT
** clause that targets the INTEGER PRIMARY KEY.
*/
SQLITE_PRIVATE int sqlite3UpsertNextIsIPK(Upsert *pUpsert){
  Upsert *pNext;
  if( NEVER(pUpsert==0) ) return 0;
  pNext = pUpsert->pNextUpsert;
  if( pNext==0 ) return 1;
  if( pNext->pUpsertTarget==0 ) return 1;
  if( pNext->pUpsertIdx==0 ) return 1;
  return 0;
}

/*
** Given the list of ON CONFLICT clauses described by pUpsert, and
** a particular index pIdx, return a pointer to the particular ON CONFLICT
** clause that applies to the index.  Or, if the index is not subject to
** any ON CONFLICT clause, return NULL.
*/
SQLITE_PRIVATE Upsert *sqlite3UpsertOfIndex(Upsert *pUpsert, Index *pIdx){
  while(
      pUpsert
   && pUpsert->pUpsertTarget!=0
   && pUpsert->pUpsertIdx!=pIdx
  ){
     pUpsert = pUpsert->pNextUpsert;
  }
  return pUpsert;
}

/*
** Generate bytecode that does an UPDATE as part of an upsert.
**
** If pIdx is NULL, then the UNIQUE constraint that failed was the IPK.
** In this case parameter iCur is a cursor open on the table b-tree that
139662
139663
139664
139665
139666
139667
139668

139669
139670
139671
139672
139673


139674
139675
139676
139677
139678
139679
139680
  int iCur              /* Cursor for pIdx (or pTab if pIdx==NULL) */
){
  Vdbe *v = pParse->pVdbe;
  sqlite3 *db = pParse->db;
  SrcList *pSrc;            /* FROM clause for the UPDATE */
  int iDataCur;
  int i;


  assert( v!=0 );
  assert( pUpsert!=0 );
  VdbeNoopComment((v, "Begin DO UPDATE of UPSERT"));
  iDataCur = pUpsert->iDataCur;


  if( pIdx && iCur!=iDataCur ){
    if( HasRowid(pTab) ){
      int regRowid = sqlite3GetTempReg(pParse);
      sqlite3VdbeAddOp2(v, OP_IdxRowid, iCur, regRowid);
      sqlite3VdbeAddOp3(v, OP_SeekRowid, iDataCur, 0, regRowid);
      VdbeCoverage(v);
      sqlite3ReleaseTempReg(pParse, regRowid);







>



<

>
>







140536
140537
140538
140539
140540
140541
140542
140543
140544
140545
140546

140547
140548
140549
140550
140551
140552
140553
140554
140555
140556
  int iCur              /* Cursor for pIdx (or pTab if pIdx==NULL) */
){
  Vdbe *v = pParse->pVdbe;
  sqlite3 *db = pParse->db;
  SrcList *pSrc;            /* FROM clause for the UPDATE */
  int iDataCur;
  int i;
  Upsert *pTop = pUpsert;

  assert( v!=0 );
  assert( pUpsert!=0 );

  iDataCur = pUpsert->iDataCur;
  pUpsert = sqlite3UpsertOfIndex(pTop, pIdx);
  VdbeNoopComment((v, "Begin DO UPDATE of UPSERT"));
  if( pIdx && iCur!=iDataCur ){
    if( HasRowid(pTab) ){
      int regRowid = sqlite3GetTempReg(pParse);
      sqlite3VdbeAddOp2(v, OP_IdxRowid, iCur, regRowid);
      sqlite3VdbeAddOp3(v, OP_SeekRowid, iDataCur, 0, regRowid);
      VdbeCoverage(v);
      sqlite3ReleaseTempReg(pParse, regRowid);
139696
139697
139698
139699
139700
139701
139702
139703
139704
139705
139706
139707
139708
139709
139710
139711
139712
139713
139714
139715
139716
139717
139718
139719
139720
139721
139722
      VdbeCoverage(v);
      sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CORRUPT, OE_Abort, 0,
            "corrupt database", P4_STATIC);
      sqlite3MayAbort(pParse);
      sqlite3VdbeJumpHere(v, i);
    }
  }
  /* pUpsert does not own pUpsertSrc - the outer INSERT statement does.  So
  ** we have to make a copy before passing it down into sqlite3Update() */
  pSrc = sqlite3SrcListDup(db, pUpsert->pUpsertSrc, 0);
  /* excluded.* columns of type REAL need to be converted to a hard real */
  for(i=0; i<pTab->nCol; i++){
    if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){
      sqlite3VdbeAddOp1(v, OP_RealAffinity, pUpsert->regData+i);
    }
  }
  sqlite3Update(pParse, pSrc, pUpsert->pUpsertSet,
      pUpsert->pUpsertWhere, OE_Abort, 0, 0, pUpsert);
  pUpsert->pUpsertSet = 0;    /* Will have been deleted by sqlite3Update() */
  pUpsert->pUpsertWhere = 0;  /* Will have been deleted by sqlite3Update() */
  VdbeNoopComment((v, "End DO UPDATE of UPSERT"));
}

#endif /* SQLITE_OMIT_UPSERT */

/************** End of upsert.c **********************************************/
/************** Begin file vacuum.c ******************************************/







|
|
|



|


|
|
<
<







140572
140573
140574
140575
140576
140577
140578
140579
140580
140581
140582
140583
140584
140585
140586
140587
140588
140589


140590
140591
140592
140593
140594
140595
140596
      VdbeCoverage(v);
      sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CORRUPT, OE_Abort, 0,
            "corrupt database", P4_STATIC);
      sqlite3MayAbort(pParse);
      sqlite3VdbeJumpHere(v, i);
    }
  }
  /* pUpsert does not own pTop->pUpsertSrc - the outer INSERT statement does.
  ** So we have to make a copy before passing it down into sqlite3Update() */
  pSrc = sqlite3SrcListDup(db, pTop->pUpsertSrc, 0);
  /* excluded.* columns of type REAL need to be converted to a hard real */
  for(i=0; i<pTab->nCol; i++){
    if( pTab->aCol[i].affinity==SQLITE_AFF_REAL ){
      sqlite3VdbeAddOp1(v, OP_RealAffinity, pTop->regData+i);
    }
  }
  sqlite3Update(pParse, pSrc, sqlite3ExprListDup(db,pUpsert->pUpsertSet,0),
      sqlite3ExprDup(db,pUpsert->pUpsertWhere,0), OE_Abort, 0, 0, pUpsert);


  VdbeNoopComment((v, "End DO UPDATE of UPSERT"));
}

#endif /* SQLITE_OMIT_UPSERT */

/************** End of upsert.c **********************************************/
/************** Begin file vacuum.c ******************************************/
141485
141486
141487
141488
141489
141490
141491
141492
141493
141494
141495
141496
141497
141498
141499
141500
141501
141502
141503
141504
141505
141506
141507
141508
141509
141510
141511
** This file contains structure and macro definitions for the query
** planner logic in "where.c".  These definitions are broken out into
** a separate source file for easier editing.
*/
#ifndef SQLITE_WHEREINT_H
#define SQLITE_WHEREINT_H

/*
** Trace output macros
*/
#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
/***/ extern int sqlite3WhereTrace;
#endif
#if defined(SQLITE_DEBUG) \
    && (defined(SQLITE_TEST) || defined(SQLITE_ENABLE_WHERETRACE))
# define WHERETRACE(K,X)  if(sqlite3WhereTrace&(K)) sqlite3DebugPrintf X
# define WHERETRACE_ENABLED 1
#else
# define WHERETRACE(K,X)
#endif

/* Forward references
*/
typedef struct WhereClause WhereClause;
typedef struct WhereMaskSet WhereMaskSet;
typedef struct WhereOrInfo WhereOrInfo;
typedef struct WhereAndInfo WhereAndInfo;







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







142359
142360
142361
142362
142363
142364
142365













142366
142367
142368
142369
142370
142371
142372
** This file contains structure and macro definitions for the query
** planner logic in "where.c".  These definitions are broken out into
** a separate source file for easier editing.
*/
#ifndef SQLITE_WHEREINT_H
#define SQLITE_WHEREINT_H















/* Forward references
*/
typedef struct WhereClause WhereClause;
typedef struct WhereMaskSet WhereMaskSet;
typedef struct WhereOrInfo WhereOrInfo;
typedef struct WhereAndInfo WhereAndInfo;
145571
145572
145573
145574
145575
145576
145577









































































































































































































































































145578
145579
145580
145581
145582
145583
145584
    aiCurCol[1] = pExpr->iColumn;
    return 1;
  }
  if( mPrereq==0 ) return 0;                 /* No table references */
  if( (mPrereq&(mPrereq-1))!=0 ) return 0;   /* Refs more than one table */
  return exprMightBeIndexed2(pFrom,mPrereq,aiCurCol,pExpr);
}










































































































































































































































































/*
** The input to this routine is an WhereTerm structure with only the
** "pExpr" field filled in.  The job of this routine is to analyze the
** subexpression and populate all the other fields of the WhereTerm
** structure.
**







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







146432
146433
146434
146435
146436
146437
146438
146439
146440
146441
146442
146443
146444
146445
146446
146447
146448
146449
146450
146451
146452
146453
146454
146455
146456
146457
146458
146459
146460
146461
146462
146463
146464
146465
146466
146467
146468
146469
146470
146471
146472
146473
146474
146475
146476
146477
146478
146479
146480
146481
146482
146483
146484
146485
146486
146487
146488
146489
146490
146491
146492
146493
146494
146495
146496
146497
146498
146499
146500
146501
146502
146503
146504
146505
146506
146507
146508
146509
146510
146511
146512
146513
146514
146515
146516
146517
146518
146519
146520
146521
146522
146523
146524
146525
146526
146527
146528
146529
146530
146531
146532
146533
146534
146535
146536
146537
146538
146539
146540
146541
146542
146543
146544
146545
146546
146547
146548
146549
146550
146551
146552
146553
146554
146555
146556
146557
146558
146559
146560
146561
146562
146563
146564
146565
146566
146567
146568
146569
146570
146571
146572
146573
146574
146575
146576
146577
146578
146579
146580
146581
146582
146583
146584
146585
146586
146587
146588
146589
146590
146591
146592
146593
146594
146595
146596
146597
146598
146599
146600
146601
146602
146603
146604
146605
146606
146607
146608
146609
146610
146611
146612
146613
146614
146615
146616
146617
146618
146619
146620
146621
146622
146623
146624
146625
146626
146627
146628
146629
146630
146631
146632
146633
146634
146635
146636
146637
146638
146639
146640
146641
146642
146643
146644
146645
146646
146647
146648
146649
146650
146651
146652
146653
146654
146655
146656
146657
146658
146659
146660
146661
146662
146663
146664
146665
146666
146667
146668
146669
146670
146671
146672
146673
146674
146675
146676
146677
146678
146679
146680
146681
146682
146683
146684
146685
146686
146687
146688
146689
146690
146691
146692
146693
146694
146695
146696
146697
146698
146699
146700
146701
146702
146703
146704
146705
146706
146707
146708
146709
146710
    aiCurCol[1] = pExpr->iColumn;
    return 1;
  }
  if( mPrereq==0 ) return 0;                 /* No table references */
  if( (mPrereq&(mPrereq-1))!=0 ) return 0;   /* Refs more than one table */
  return exprMightBeIndexed2(pFrom,mPrereq,aiCurCol,pExpr);
}

/*
** Expression callback for exprUsesSrclist().
*/
static int exprUsesSrclistCb(Walker *p, Expr *pExpr){
  if( pExpr->op==TK_COLUMN ){
    SrcList *pSrc = p->u.pSrcList;
    int iCsr = pExpr->iTable;
    int ii;
    for(ii=0; ii<pSrc->nSrc; ii++){
      if( pSrc->a[ii].iCursor==iCsr ){
        return p->eCode ? WRC_Abort : WRC_Continue;
      }
    }
    return p->eCode ? WRC_Continue : WRC_Abort;
  }
  return WRC_Continue;
}

/*
** Select callback for exprUsesSrclist().
*/
static int exprUsesSrclistSelectCb(Walker *p, Select *pSelect){
  return WRC_Abort;
}

/*
** This function always returns true if expression pExpr contains
** a sub-select.
**
** If there is no sub-select in pExpr, then return true if pExpr
** contains a TK_COLUMN node for a table that is (bUses==1)
** or is not (bUses==0) in pSrc.
**
** Said another way:
**
**   bUses      Return     Meaning
**   --------   ------     ------------------------------------------------
**
**   bUses==1   true       pExpr contains either a sub-select or a
**                         TK_COLUMN referencing pSrc.
**
**   bUses==1   false      pExpr contains no sub-selects and all TK_COLUMN
**                         nodes reference tables not found in pSrc
**
**   bUses==0   true       pExpr contains either a sub-select or a TK_COLUMN
**                         that references a table not in pSrc.
**
**   bUses==0   false      pExpr contains no sub-selects and all TK_COLUMN
**                         nodes reference pSrc
*/
static int exprUsesSrclist(SrcList *pSrc, Expr *pExpr, int bUses){
  Walker sWalker;
  memset(&sWalker, 0, sizeof(Walker));
  sWalker.eCode = bUses;
  sWalker.u.pSrcList = pSrc;
  sWalker.xExprCallback = exprUsesSrclistCb;
  sWalker.xSelectCallback = exprUsesSrclistSelectCb;
  return (sqlite3WalkExpr(&sWalker, pExpr)==WRC_Abort);
}

/*
** Context object used by exprExistsToInIter() as it iterates through an
** expression tree.
*/
struct ExistsToInCtx {
  SrcList *pSrc;    /* The tables in an EXISTS(SELECT ... FROM <here> ...) */
  Expr *pInLhs;     /* OUT:  Use this as the LHS of the IN operator */
  Expr *pEq;        /* OUT:  The == term that include pInLhs */
  Expr **ppAnd;     /* OUT:  The AND operator that includes pEq as a child */
  Expr **ppParent;  /* The AND operator currently being examined */
};

/*
** Iterate through all AND connected nodes in the expression tree
** headed by (*ppExpr), populating the structure passed as the first
** argument with the values required by exprAnalyzeExistsFindEq().
**
** This function returns non-zero if the expression tree does not meet
** the two conditions described by the header comment for
** exprAnalyzeExistsFindEq(), or zero if it does.
*/
static int exprExistsToInIter(struct ExistsToInCtx *p, Expr **ppExpr){
  Expr *pExpr = *ppExpr;
  switch( pExpr->op ){
    case TK_AND:
      p->ppParent = ppExpr;
      if( exprExistsToInIter(p, &pExpr->pLeft) ) return 1;
      p->ppParent = ppExpr;
      if( exprExistsToInIter(p, &pExpr->pRight) ) return 1;
      break;
    case TK_EQ: {
      int bLeft = exprUsesSrclist(p->pSrc, pExpr->pLeft, 0);
      int bRight = exprUsesSrclist(p->pSrc, pExpr->pRight, 0);
      if( bLeft || bRight ){
        if( (bLeft && bRight) || p->pInLhs ) return 1;
        p->pInLhs = bLeft ? pExpr->pLeft : pExpr->pRight;
        if( exprUsesSrclist(p->pSrc, p->pInLhs, 1) ) return 1;
        p->pEq = pExpr;
        p->ppAnd = p->ppParent;
      }
      break;
    }
    default:
      if( exprUsesSrclist(p->pSrc, pExpr, 0) ){
        return 1;
      }
      break;
  }

  return 0;
}

/*
** This function is used by exprAnalyzeExists() when creating virtual IN(...)
** terms equivalent to user-supplied EXIST(...) clauses. It splits the WHERE
** clause of the Select object passed as the first argument into one or more
** expressions joined by AND operators, and then tests if the following are
** true:
**
**   1. Exactly one of the AND separated terms refers to the outer
**      query, and it is an == (TK_EQ) expression.
**
**   2. Only one side of the == expression refers to the outer query, and
**      it does not refer to any columns from the inner query.
**
** If both these conditions are true, then a pointer to the side of the ==
** expression that refers to the outer query is returned. The caller will
** use this expression as the LHS of the IN(...) virtual term. Or, if one
** or both of the above conditions are not true, NULL is returned.
**
** If non-NULL is returned and ppEq is non-NULL, *ppEq is set to point
** to the == expression node before returning. If pppAnd is non-NULL and
** the == node is not the root of the WHERE clause, then *pppAnd is set
** to point to the pointer to the AND node that is the parent of the ==
** node within the WHERE expression tree.
*/
static Expr *exprAnalyzeExistsFindEq(
  Select *pSel,                   /* The SELECT of the EXISTS */
  Expr **ppEq,                    /* OUT: == node from WHERE clause */
  Expr ***pppAnd                  /* OUT: Pointer to parent of ==, if any */
){
  struct ExistsToInCtx ctx;
  memset(&ctx, 0, sizeof(ctx));
  ctx.pSrc = pSel->pSrc;
  if( exprExistsToInIter(&ctx, &pSel->pWhere) ){
    return 0;
  }
  if( ppEq ) *ppEq = ctx.pEq;
  if( pppAnd ) *pppAnd = ctx.ppAnd;
  return ctx.pInLhs;
}

/*
** Term idxTerm of the WHERE clause passed as the second argument is an
** EXISTS expression with a correlated SELECT statement on the RHS.
** This function analyzes the SELECT statement, and if possible adds an
** equivalent "? IN(SELECT...)" virtual term to the WHERE clause.
**
** For an EXISTS term such as the following:
**
**     EXISTS (SELECT ... FROM <srclist> WHERE <e1> = <e2> AND <e3>)
**
** The virtual IN() term added is:
**
**     <e1> IN (SELECT <e2> FROM <srclist> WHERE <e3>)
**
** The virtual term is only added if the following conditions are met:
**
**     1. The sub-select must not be an aggregate or use window functions,
**
**     2. The sub-select must not be a compound SELECT,
**
**     3. Expression <e1> must refer to at least one column from the outer
**        query, and must not refer to any column from the inner query
**        (i.e. from <srclist>).
**
**     4. <e2> and <e3> must not refer to any values from the outer query.
**        In other words, once <e1> has been removed, the inner query
**        must not be correlated.
**
*/
static void exprAnalyzeExists(
  SrcList *pSrc,            /* the FROM clause */
  WhereClause *pWC,         /* the WHERE clause */
  int idxTerm               /* Index of the term to be analyzed */
){
  Parse *pParse = pWC->pWInfo->pParse;
  WhereTerm *pTerm = &pWC->a[idxTerm];
  Expr *pExpr = pTerm->pExpr;
  Select *pSel = pExpr->x.pSelect;
  Expr *pDup = 0;
  Expr *pEq = 0;
  Expr *pRet = 0;
  Expr *pInLhs = 0;
  Expr **ppAnd = 0;
  int idxNew;
  sqlite3 *db = pParse->db;

  assert( pExpr->op==TK_EXISTS );
  assert( (pExpr->flags & EP_VarSelect) && (pExpr->flags & EP_xIsSelect) );

  if( (pSel->selFlags & SF_Aggregate) || pSel->pWin ) return;
  if( pSel->pPrior ) return;
  if( pSel->pWhere==0 ) return;
  if( 0==exprAnalyzeExistsFindEq(pSel, 0, 0) ) return;

  pDup = sqlite3ExprDup(db, pExpr, 0);
  if( db->mallocFailed ){
    sqlite3ExprDelete(db, pDup);
    return;
  }
  pSel = pDup->x.pSelect;
  sqlite3ExprListDelete(db, pSel->pEList);
  pSel->pEList = 0;

  pInLhs = exprAnalyzeExistsFindEq(pSel, &pEq, &ppAnd);
  assert( pInLhs && pEq );
  assert( pEq==pSel->pWhere || ppAnd );
  if( pInLhs==pEq->pLeft ){
    pRet = pEq->pRight;
  }else{
    CollSeq *p = sqlite3ExprCompareCollSeq(pParse, pEq);
    pInLhs = sqlite3ExprAddCollateString(pParse, pInLhs, p?p->zName:"BINARY");
    pRet = pEq->pLeft;
  }

  assert( pDup->pLeft==0 );
  pDup->op = TK_IN;
  pDup->pLeft = pInLhs;
  pDup->flags &= ~EP_VarSelect;
  if( pRet->op==TK_VECTOR ){
    pSel->pEList = pRet->x.pList;
    pRet->x.pList = 0;
    sqlite3ExprDelete(db, pRet);
  }else{
    pSel->pEList = sqlite3ExprListAppend(pParse, 0, pRet);
  }
  pEq->pLeft = 0;
  pEq->pRight = 0;
  if( ppAnd ){
    Expr *pAnd = *ppAnd;
    Expr *pOther = (pAnd->pLeft==pEq) ? pAnd->pRight : pAnd->pLeft;
    pAnd->pLeft = pAnd->pRight = 0;
    sqlite3ExprDelete(db, pAnd);
    *ppAnd = pOther;
  }else{
    assert( pSel->pWhere==pEq );
    pSel->pWhere = 0;
  }
  sqlite3ExprDelete(db, pEq);

#ifdef WHERETRACE_ENABLED  /* 0x20 */
  if( sqlite3WhereTrace & 0x20 ){
    sqlite3DebugPrintf("Convert EXISTS:\n");
    sqlite3TreeViewExpr(0, pExpr, 0);
    sqlite3DebugPrintf("into IN:\n");
    sqlite3TreeViewExpr(0, pDup, 0);
  }
#endif
  idxNew = whereClauseInsert(pWC, pDup, TERM_VIRTUAL|TERM_DYNAMIC);
  exprAnalyze(pSrc, pWC, idxNew);
  markTermAsChild(pWC, idxNew, idxTerm);
  pWC->a[idxTerm].wtFlags |= TERM_COPIED;
}

/*
** The input to this routine is an WhereTerm structure with only the
** "pExpr" field filled in.  The job of this routine is to analyze the
** subexpression and populate all the other fields of the WhereTerm
** structure.
**
145756
145757
145758
145759
145760
145761
145762










145763
145764
145765
145766
145767
145768
145769
  */
  else if( pExpr->op==TK_OR ){
    assert( pWC->op==TK_AND );
    exprAnalyzeOrTerm(pSrc, pWC, idxTerm);
    pTerm = &pWC->a[idxTerm];
  }
#endif /* SQLITE_OMIT_OR_OPTIMIZATION */











#ifndef SQLITE_OMIT_LIKE_OPTIMIZATION
  /* Add constraints to reduce the search space on a LIKE or GLOB
  ** operator.
  **
  ** A like pattern of the form "x LIKE 'aBc%'" is changed into constraints
  **







>
>
>
>
>
>
>
>
>
>







146882
146883
146884
146885
146886
146887
146888
146889
146890
146891
146892
146893
146894
146895
146896
146897
146898
146899
146900
146901
146902
146903
146904
146905
  */
  else if( pExpr->op==TK_OR ){
    assert( pWC->op==TK_AND );
    exprAnalyzeOrTerm(pSrc, pWC, idxTerm);
    pTerm = &pWC->a[idxTerm];
  }
#endif /* SQLITE_OMIT_OR_OPTIMIZATION */

  else if( pExpr->op==TK_EXISTS ){
    /* Perhaps treat an EXISTS operator as an IN operator */
    if( (pExpr->flags & EP_VarSelect)!=0
     && OptimizationEnabled(db, SQLITE_ExistsToIN)
    ){
      exprAnalyzeExists(pSrc, pWC, idxTerm);
    }
  }


#ifndef SQLITE_OMIT_LIKE_OPTIMIZATION
  /* Add constraints to reduce the search space on a LIKE or GLOB
  ** operator.
  **
  ** A like pattern of the form "x LIKE 'aBc%'" is changed into constraints
  **
146217
146218
146219
146220
146221
146222
146223
146224
146225
146226
146227
146228
146229
146230
146231
146232
146233
146234
146235
146236
struct HiddenIndexInfo {
  WhereClause *pWC;   /* The Where clause being analyzed */
  Parse *pParse;      /* The parsing context */
};

/* Forward declaration of methods */
static int whereLoopResize(sqlite3*, WhereLoop*, int);

/* Test variable that can be set to enable WHERE tracing */
#if defined(SQLITE_TEST) || defined(SQLITE_DEBUG)
/***/ int sqlite3WhereTrace = 0;
#endif


/*
** Return the estimated number of output rows from a WHERE clause
*/
SQLITE_PRIVATE LogEst sqlite3WhereOutputRowCount(WhereInfo *pWInfo){
  return pWInfo->nRowOut;
}







<
<
<
<
<
<







147353
147354
147355
147356
147357
147358
147359






147360
147361
147362
147363
147364
147365
147366
struct HiddenIndexInfo {
  WhereClause *pWC;   /* The Where clause being analyzed */
  Parse *pParse;      /* The parsing context */
};

/* Forward declaration of methods */
static int whereLoopResize(sqlite3*, WhereLoop*, int);







/*
** Return the estimated number of output rows from a WHERE clause
*/
SQLITE_PRIVATE LogEst sqlite3WhereOutputRowCount(WhereInfo *pWInfo){
  return pWInfo->nRowOut;
}
146285
146286
146287
146288
146289
146290
146291


























146292
146293
146294
146295
146296
146297
146298
    ** continuation of the inner-most loop. */
    return pWInfo->iContinue;
  }
  pInner = &pWInfo->a[pWInfo->nLevel-1];
  assert( pInner->addrNxt!=0 );
  return pInner->addrNxt;
}



























/*
** Return the VDBE address or label to jump to in order to continue
** immediately with the next row of a WHERE clause.
*/
SQLITE_PRIVATE int sqlite3WhereContinueLabel(WhereInfo *pWInfo){
  assert( pWInfo->iContinue!=0 );







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







147415
147416
147417
147418
147419
147420
147421
147422
147423
147424
147425
147426
147427
147428
147429
147430
147431
147432
147433
147434
147435
147436
147437
147438
147439
147440
147441
147442
147443
147444
147445
147446
147447
147448
147449
147450
147451
147452
147453
147454
    ** continuation of the inner-most loop. */
    return pWInfo->iContinue;
  }
  pInner = &pWInfo->a[pWInfo->nLevel-1];
  assert( pInner->addrNxt!=0 );
  return pInner->addrNxt;
}

/*
** While generating code for the min/max optimization, after handling
** the aggregate-step call to min() or max(), check to see if any
** additional looping is required.  If the output order is such that
** we are certain that the correct answer has already been found, then
** code an OP_Goto to by pass subsequent processing.
**
** Any extra OP_Goto that is coded here is an optimization.  The
** correct answer should be obtained regardless.  This OP_Goto just
** makes the answer appear faster.
*/
SQLITE_PRIVATE void sqlite3WhereMinMaxOptEarlyOut(Vdbe *v, WhereInfo *pWInfo){
  WhereLevel *pInner;
  int i;
  if( !pWInfo->bOrderedInnerLoop ) return;
  if( pWInfo->nOBSat==0 ) return;
  for(i=pWInfo->nLevel-1; i>=0; i--){
    pInner = &pWInfo->a[i];
    if( (pInner->pWLoop->wsFlags & WHERE_COLUMN_IN)!=0 ){
      sqlite3VdbeGoto(v, pInner->addrNxt);
      return;
    }
  }
  sqlite3VdbeGoto(v, pWInfo->iBreak);
}

/*
** Return the VDBE address or label to jump to in order to continue
** immediately with the next row of a WHERE clause.
*/
SQLITE_PRIVATE int sqlite3WhereContinueLabel(WhereInfo *pWInfo){
  assert( pWInfo->iContinue!=0 );
148792
148793
148794
148795
148796
148797
148798
148799
148800
148801
148802
148803
148804
148805
148806
      pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT;
      pNew->u.btree.nBtm = whereRangeVectorLen(
          pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm
      );
      pBtm = pTerm;
      pTop = 0;
      if( pTerm->wtFlags & TERM_LIKEOPT ){
        /* Range contraints that come from the LIKE optimization are
        ** always used in pairs. */
        pTop = &pTerm[1];
        assert( (pTop-(pTerm->pWC->a))<pTerm->pWC->nTerm );
        assert( pTop->wtFlags & TERM_LIKEOPT );
        assert( pTop->eOperator==WO_LT );
        if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */
        pNew->aLTerm[pNew->nLTerm++] = pTop;







|







149948
149949
149950
149951
149952
149953
149954
149955
149956
149957
149958
149959
149960
149961
149962
      pNew->wsFlags |= WHERE_COLUMN_RANGE|WHERE_BTM_LIMIT;
      pNew->u.btree.nBtm = whereRangeVectorLen(
          pParse, pSrc->iCursor, pProbe, saved_nEq, pTerm
      );
      pBtm = pTerm;
      pTop = 0;
      if( pTerm->wtFlags & TERM_LIKEOPT ){
        /* Range constraints that come from the LIKE optimization are
        ** always used in pairs. */
        pTop = &pTerm[1];
        assert( (pTop-(pTerm->pWC->a))<pTerm->pWC->nTerm );
        assert( pTop->wtFlags & TERM_LIKEOPT );
        assert( pTop->eOperator==WO_LT );
        if( whereLoopResize(db, pNew, pNew->nLTerm+1) ) break; /* OOM */
        pNew->aLTerm[pNew->nLTerm++] = pTop;
155317
155318
155319
155320
155321
155322
155323
155324
155325
155326
155327
155328
155329
155330
155331
155332
155333
155334
155335
155336
155337
155338
155339
155340
155341
155342
#define sqlite3ParserARG_STORE
#define sqlite3ParserCTX_SDECL Parse *pParse;
#define sqlite3ParserCTX_PDECL ,Parse *pParse
#define sqlite3ParserCTX_PARAM ,pParse
#define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse;
#define sqlite3ParserCTX_STORE yypParser->pParse=pParse;
#define YYFALLBACK 1
#define YYNSTATE             553
#define YYNRULE              385
#define YYNRULE_WITH_ACTION  325
#define YYNTOKEN             181
#define YY_MAX_SHIFT         552
#define YY_MIN_SHIFTREDUCE   803
#define YY_MAX_SHIFTREDUCE   1187
#define YY_ERROR_ACTION      1188
#define YY_ACCEPT_ACTION     1189
#define YY_NO_ACTION         1190
#define YY_MIN_REDUCE        1191
#define YY_MAX_REDUCE        1575
/************* End control #defines *******************************************/
#define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])))

/* Define the yytestcase() macro to be a no-op if is not already defined
** otherwise.
**
** Applications can choose to define yytestcase() in the %include section







|
|
|

|
|
|
|
|
|
|
|







156473
156474
156475
156476
156477
156478
156479
156480
156481
156482
156483
156484
156485
156486
156487
156488
156489
156490
156491
156492
156493
156494
156495
156496
156497
156498
#define sqlite3ParserARG_STORE
#define sqlite3ParserCTX_SDECL Parse *pParse;
#define sqlite3ParserCTX_PDECL ,Parse *pParse
#define sqlite3ParserCTX_PARAM ,pParse
#define sqlite3ParserCTX_FETCH Parse *pParse=yypParser->pParse;
#define sqlite3ParserCTX_STORE yypParser->pParse=pParse;
#define YYFALLBACK 1
#define YYNSTATE             558
#define YYNRULE              386
#define YYNRULE_WITH_ACTION  326
#define YYNTOKEN             181
#define YY_MAX_SHIFT         557
#define YY_MIN_SHIFTREDUCE   809
#define YY_MAX_SHIFTREDUCE   1194
#define YY_ERROR_ACTION      1195
#define YY_ACCEPT_ACTION     1196
#define YY_NO_ACTION         1197
#define YY_MIN_REDUCE        1198
#define YY_MAX_REDUCE        1583
/************* End control #defines *******************************************/
#define YY_NLOOKAHEAD ((int)(sizeof(yy_lookahead)/sizeof(yy_lookahead[0])))

/* Define the yytestcase() macro to be a no-op if is not already defined
** otherwise.
**
** Applications can choose to define yytestcase() in the %include section
155395
155396
155397
155398
155399
155400
155401
155402
155403
155404
155405
155406
155407
155408
155409
155410
155411
155412
155413
155414
155415
155416
155417
155418
155419
155420
155421
155422
155423
155424
155425
155426
155427
155428
155429
155430
155431
155432
155433
155434
155435
155436
155437
155438
155439
155440
155441
155442
155443
155444
155445
155446
155447
155448
155449
155450
155451
155452
155453
155454
155455
155456
155457
155458
155459
155460
155461
155462
155463
155464
155465
155466
155467
155468
155469
155470
155471
155472
155473
155474
155475
155476
155477
155478
155479
155480
155481
155482
155483
155484
155485
155486
155487
155488
155489
155490
155491
155492
155493
155494
155495
155496
155497
155498
155499
155500
155501
155502
155503
155504
155505
155506
155507
155508
155509
155510
155511
155512
155513
155514
155515
155516
155517
155518
155519
155520
155521
155522
155523
155524
155525
155526
155527
155528
155529
155530
155531
155532
155533
155534
155535
155536
155537
155538
155539
155540
155541
155542
155543
155544
155545
155546
155547
155548
155549
155550
155551
155552
155553
155554
155555
155556
155557
155558
155559
155560
155561
155562
155563
155564
155565
155566
155567
155568
155569
155570
155571
155572
155573
155574
155575
155576
155577
155578
155579
155580
155581
155582
155583
155584
155585
155586
155587
155588
155589
155590
155591
155592
155593
155594
155595
155596
155597
155598
155599
155600
155601
155602
155603
155604
155605
155606
155607
**  yy_shift_ofst[]    For each state, the offset into yy_action for
**                     shifting terminals.
**  yy_reduce_ofst[]   For each state, the offset into yy_action for
**                     shifting non-terminals after a reduce.
**  yy_default[]       Default action for each state.
**
*********** Begin parsing tables **********************************************/
#define YY_ACTTAB_COUNT (1962)
static const YYACTIONTYPE yy_action[] = {
 /*     0 */   546, 1222,  546,  451, 1260,  546, 1239,  546,  114,  111,
 /*    10 */   211,  546, 1537,  546, 1260,  523,  114,  111,  211,  392,
 /*    20 */  1232,  344,   42,   42,   42,   42, 1225,   42,   42,   71,
 /*    30 */    71,  937, 1224,   71,   71,   71,   71, 1462, 1493,  938,
 /*    40 */   820,  453,    6,  121,  122,  112, 1165, 1165, 1006, 1009,
 /*    50 */   999,  999,  119,  119,  120,  120,  120,  120, 1543,  392,
 /*    60 */  1358, 1517,  552,    2, 1193,  194,  528,  436,  143,  291,
 /*    70 */   528,  136,  528,  371,  261,  504,  272,  385, 1273,  527,
 /*    80 */   503,  493,  164,  121,  122,  112, 1165, 1165, 1006, 1009,
 /*    90 */   999,  999,  119,  119,  120,  120,  120,  120, 1358,  442,
 /*   100 */  1514,  118,  118,  118,  118,  117,  117,  116,  116,  116,
 /*   110 */   115,  424,  266,  266,  266,  266, 1498,  358, 1500,  435,
 /*   120 */   357, 1498,  517,  524, 1485,  543, 1114,  543, 1114,  392,
 /*   130 */   405,  241,  208,  114,  111,  211,   98,  290,  537,  221,
 /*   140 */  1029,  118,  118,  118,  118,  117,  117,  116,  116,  116,
 /*   150 */   115,  424, 1142,  121,  122,  112, 1165, 1165, 1006, 1009,
 /*   160 */   999,  999,  119,  119,  120,  120,  120,  120,  406,  428,
 /*   170 */   117,  117,  116,  116,  116,  115,  424, 1418,  468,  123,
 /*   180 */   118,  118,  118,  118,  117,  117,  116,  116,  116,  115,
 /*   190 */   424,  116,  116,  116,  115,  424,  540,  540,  540,  392,
 /*   200 */   505,  120,  120,  120,  120,  113, 1051, 1142, 1143, 1144,
 /*   210 */  1051,  118,  118,  118,  118,  117,  117,  116,  116,  116,
 /*   220 */   115,  424, 1461,  121,  122,  112, 1165, 1165, 1006, 1009,
 /*   230 */   999,  999,  119,  119,  120,  120,  120,  120,  392,  444,
 /*   240 */   316,   83,  463,   81,  359,  382, 1142,   80,  118,  118,
 /*   250 */   118,  118,  117,  117,  116,  116,  116,  115,  424,  179,
 /*   260 */   434,  424,  121,  122,  112, 1165, 1165, 1006, 1009,  999,
 /*   270 */   999,  119,  119,  120,  120,  120,  120,  434,  433,  266,
 /*   280 */   266,  118,  118,  118,  118,  117,  117,  116,  116,  116,
 /*   290 */   115,  424,  543, 1109,  903,  506, 1142,  114,  111,  211,
 /*   300 */  1431, 1142, 1143, 1144,  206,  491, 1109,  392,  449, 1109,
 /*   310 */   545,  330,  120,  120,  120,  120,  298, 1431, 1433,   17,
 /*   320 */   118,  118,  118,  118,  117,  117,  116,  116,  116,  115,
 /*   330 */   424,  121,  122,  112, 1165, 1165, 1006, 1009,  999,  999,
 /*   340 */   119,  119,  120,  120,  120,  120,  392, 1358,  434, 1142,
 /*   350 */   482, 1142, 1143, 1144,  996,  996, 1007, 1010,  445,  118,
 /*   360 */   118,  118,  118,  117,  117,  116,  116,  116,  115,  424,
 /*   370 */   121,  122,  112, 1165, 1165, 1006, 1009,  999,  999,  119,
 /*   380 */   119,  120,  120,  120,  120, 1054, 1054,  465, 1431,  118,
 /*   390 */   118,  118,  118,  117,  117,  116,  116,  116,  115,  424,
 /*   400 */  1142,  451,  546, 1426, 1142, 1143, 1144,  233,  966, 1142,
 /*   410 */   481,  478,  477,  171,  360,  392,  164,  407,  414,  842,
 /*   420 */   476,  164,  185,  334,   71,   71, 1243, 1000,  118,  118,
 /*   430 */   118,  118,  117,  117,  116,  116,  116,  115,  424,  121,
 /*   440 */   122,  112, 1165, 1165, 1006, 1009,  999,  999,  119,  119,
 /*   450 */   120,  120,  120,  120,  392, 1142, 1143, 1144,  835,   12,
 /*   460 */   314,  509,  163,  356, 1142, 1143, 1144,  114,  111,  211,
 /*   470 */   508,  290,  537,  546,  276,  180,  290,  537,  121,  122,
 /*   480 */   112, 1165, 1165, 1006, 1009,  999,  999,  119,  119,  120,
 /*   490 */   120,  120,  120,  345,  484,   71,   71,  118,  118,  118,
 /*   500 */   118,  117,  117,  116,  116,  116,  115,  424, 1142,  209,
 /*   510 */   411,  523, 1142, 1109, 1571,  378,  252,  269,  342,  487,
 /*   520 */   337,  486,  238,  392,  513,  364, 1109, 1127,  333, 1109,
 /*   530 */   191,  409,  286,   32,  457,  443,  118,  118,  118,  118,
 /*   540 */   117,  117,  116,  116,  116,  115,  424,  121,  122,  112,
 /*   550 */  1165, 1165, 1006, 1009,  999,  999,  119,  119,  120,  120,
 /*   560 */   120,  120,  392, 1142, 1143, 1144,  987, 1142, 1143, 1144,
 /*   570 */  1142,  233,  492, 1492,  481,  478,  477,    6,  163,  546,
 /*   580 */   512,  546,  115,  424,  476,    5,  121,  122,  112, 1165,
 /*   590 */  1165, 1006, 1009,  999,  999,  119,  119,  120,  120,  120,
 /*   600 */   120,   13,   13,   13,   13,  118,  118,  118,  118,  117,
 /*   610 */   117,  116,  116,  116,  115,  424,  403,  502,  408,  546,
 /*   620 */  1486,  544, 1142,  892,  892, 1142, 1143, 1144, 1473, 1142,
 /*   630 */   275,  392,  808,  809,  810,  971,  422,  422,  422,   16,
 /*   640 */    16,   55,   55, 1242,  118,  118,  118,  118,  117,  117,
 /*   650 */   116,  116,  116,  115,  424,  121,  122,  112, 1165, 1165,
 /*   660 */  1006, 1009,  999,  999,  119,  119,  120,  120,  120,  120,
 /*   670 */   392, 1189,    1,    1,  552,    2, 1193, 1142, 1143, 1144,
 /*   680 */   194,  291,  898,  136, 1142, 1143, 1144,  897,  521, 1492,
 /*   690 */  1273,    3,  380,    6,  121,  122,  112, 1165, 1165, 1006,
 /*   700 */  1009,  999,  999,  119,  119,  120,  120,  120,  120,  858,
 /*   710 */   546,  924,  546,  118,  118,  118,  118,  117,  117,  116,
 /*   720 */   116,  116,  115,  424,  266,  266, 1092, 1569, 1142,  551,
 /*   730 */  1569, 1193,   13,   13,   13,   13,  291,  543,  136,  392,
 /*   740 */   485,  421,  420,  966,  344, 1273,  468,  410,  859,  279,
 /*   750 */   140,  221,  118,  118,  118,  118,  117,  117,  116,  116,
 /*   760 */   116,  115,  424,  121,  122,  112, 1165, 1165, 1006, 1009,
 /*   770 */   999,  999,  119,  119,  120,  120,  120,  120,  546,  266,
 /*   780 */   266,  428,  392, 1142, 1143, 1144, 1172,  830, 1172,  468,
 /*   790 */   431,  145,  543, 1146,  401,  314,  439,  302,  838, 1490,
 /*   800 */    71,   71,  412,    6, 1090,  473,  221,  100,  112, 1165,
 /*   810 */  1165, 1006, 1009,  999,  999,  119,  119,  120,  120,  120,
 /*   820 */   120,  118,  118,  118,  118,  117,  117,  116,  116,  116,
 /*   830 */   115,  424,  237, 1425,  546,  451,  428,  287,  986,  546,
 /*   840 */   236,  235,  234,  830,   97,  529,  429, 1265, 1265, 1146,
 /*   850 */   494,  307,  430,  838,  977,  546,   71,   71,  976, 1241,
 /*   860 */   546,   51,   51,  300,  118,  118,  118,  118,  117,  117,
 /*   870 */   116,  116,  116,  115,  424,  194,  103,   70,   70,  266,
 /*   880 */   266,  546,   71,   71,  266,  266,   30,  391,  344,  976,
 /*   890 */   976,  978,  543,  528, 1109,  328,  392,  543,  495,  397,
 /*   900 */  1470,  195,  530,   13,   13, 1358,  240, 1109,  277,  280,
 /*   910 */  1109,  280,  304,  457,  306,  333,  392,   31,  188,  419,
 /*   920 */   121,  122,  112, 1165, 1165, 1006, 1009,  999,  999,  119,
 /*   930 */   119,  120,  120,  120,  120,  142,  392,  365,  457,  986,
 /*   940 */   121,  122,  112, 1165, 1165, 1006, 1009,  999,  999,  119,
 /*   950 */   119,  120,  120,  120,  120,  977,  323, 1142,  326,  976,
 /*   960 */   121,  110,  112, 1165, 1165, 1006, 1009,  999,  999,  119,
 /*   970 */   119,  120,  120,  120,  120,  464,  377, 1185,  118,  118,
 /*   980 */   118,  118,  117,  117,  116,  116,  116,  115,  424, 1142,
 /*   990 */   976,  976,  978,  305,    9,  366,  244,  362,  118,  118,
 /*  1000 */   118,  118,  117,  117,  116,  116,  116,  115,  424,  313,
 /*  1010 */   546,  344, 1142, 1143, 1144,  299,  290,  537,  118,  118,
 /*  1020 */   118,  118,  117,  117,  116,  116,  116,  115,  424, 1263,
 /*  1030 */  1263, 1163,   13,   13,  278,  421,  420,  468,  392,  923,
 /*  1040 */   260,  260,  289, 1169, 1142, 1143, 1144,  189, 1171,  266,
 /*  1050 */   266,  468,  390,  543, 1186,  546, 1170,  263,  144,  489,
 /*  1060 */   922,  546,  543,  122,  112, 1165, 1165, 1006, 1009,  999,
 /*  1070 */   999,  119,  119,  120,  120,  120,  120,   71,   71, 1142,
 /*  1080 */  1172, 1272, 1172,   13,   13,  898, 1070, 1163,  546,  468,
 /*  1090 */   897,  107,  538, 1491,    4, 1268, 1109,    6,  525, 1049,
 /*  1100 */    12, 1071, 1092, 1570,  312,  455, 1570,  520,  541, 1109,
 /*  1110 */    56,   56, 1109, 1489,  423, 1358, 1072,    6,  345,  285,
 /*  1120 */   118,  118,  118,  118,  117,  117,  116,  116,  116,  115,
 /*  1130 */   424,  425, 1271,  321, 1142, 1143, 1144,  878,  266,  266,
 /*  1140 */  1277,  107,  538,  535,    4, 1488,  293,  879, 1211,    6,
 /*  1150 */   210,  543,  543,  164,  294,  496,  416,  204,  541,  267,
 /*  1160 */   267, 1214,  398,  511,  499,  204,  266,  266,  396,  531,
 /*  1170 */     8,  986,  543,  519,  546,  922,  458,  105,  105,  543,
 /*  1180 */  1090,  425,  266,  266,  106,  417,  425,  548,  547,  266,
 /*  1190 */   266,  976,  518,  535, 1373,  543,   15,   15,  266,  266,
 /*  1200 */   456, 1120,  543,  266,  266, 1070, 1372,  515,  290,  537,
 /*  1210 */   546,  543,  514,   97,  444,  316,  543,  546,  922,  125,
 /*  1220 */  1071,  986,  976,  976,  978,  979,   27,  105,  105,  401,
 /*  1230 */   343, 1511,   44,   44,  106, 1072,  425,  548,  547,   57,
 /*  1240 */    57,  976,  343, 1511,  107,  538,  546,    4,  462,  401,
 /*  1250 */   214, 1120,  459,  297,  377, 1091,  534, 1309,  546,  539,
 /*  1260 */   398,  541,  290,  537,  104,  244,  102,  526,   58,   58,
 /*  1270 */   546,  199,  976,  976,  978,  979,   27, 1516, 1131,  427,
 /*  1280 */    59,   59,  270,  237,  425,  138,   95,  375,  375,  374,
 /*  1290 */   255,  372,   60,   60,  817, 1180,  535,  546,  273,  546,
 /*  1300 */  1163, 1308,  389,  388,  546,  438,  546,  215,  210,  296,
 /*  1310 */   515,  849,  546,  265,  208,  516, 1476,  295,  274,   61,
 /*  1320 */    61,   62,   62,  308,  986,  109,   45,   45,   46,   46,
 /*  1330 */   105,  105, 1186,  922,   47,   47,  341,  106,  546,  425,
 /*  1340 */   548,  547, 1542,  546,  976,  867,  340,  217,  546,  937,
 /*  1350 */   397,  107,  538,  218,    4,  156, 1163,  938,  158,  546,
 /*  1360 */    49,   49, 1162,  546,  268,   50,   50,  546,  541, 1450,
 /*  1370 */    63,   63,  546, 1449,  216,  976,  976,  978,  979,   27,
 /*  1380 */   446,   64,   64,  546,  460,   65,   65,  546,  318,   14,
 /*  1390 */    14,  425, 1305,  546,   66,   66, 1087,  546,  141,  379,
 /*  1400 */    38,  546,  963,  535,  322,  127,  127,  546,  393,   67,
 /*  1410 */    67,  546,  325,  290,  537,   52,   52,  515,  546,   68,
 /*  1420 */    68,  845,  514,   69,   69,  399,  165,  857,  856,   53,
 /*  1430 */    53,  986,  311,  151,  151,   97,  432,  105,  105,  327,
 /*  1440 */   152,  152,  526, 1048,  106, 1048,  425,  548,  547, 1131,
 /*  1450 */   427,  976, 1032,  270,  968,  239,  329,  243,  375,  375,
 /*  1460 */   374,  255,  372,  940,  941,  817, 1296,  546,  220,  546,
 /*  1470 */   107,  538,  546,    4,  546, 1256,  199,  845,  215, 1036,
 /*  1480 */   296, 1530,  976,  976,  978,  979,   27,  541,  295,   76,
 /*  1490 */    76,   54,   54,  980,   72,   72,  128,  128,  864,  865,
 /*  1500 */   107,  538,  546,    4, 1047,  546, 1047,  533,  469,  546,
 /*  1510 */   425,  546,  450, 1240,  546,  243,  546,  541,  217,  546,
 /*  1520 */   452,  197,  535,  243,   73,   73,  156,  129,  129,  158,
 /*  1530 */   336,  130,  130,  126,  126, 1036,  150,  150,  149,  149,
 /*  1540 */   425,  134,  134,  317,  474,  216,   97,  239,  331,  980,
 /*  1550 */   986,   97,  535,  346,  347,  546,  105,  105,  902,  931,
 /*  1560 */   546,  895,  243,  106,  109,  425,  548,  547,  546, 1505,
 /*  1570 */   976,  828,   99,  538,  139,    4,  546,  133,  133,  393,
 /*  1580 */   986, 1317,  131,  131,  290,  537,  105,  105, 1357,  541,
 /*  1590 */   132,  132, 1292,  106, 1303,  425,  548,  547,   75,   75,
 /*  1600 */   976,  976,  976,  978,  979,   27,  546,  432,  896, 1289,
 /*  1610 */   532,  109,  425, 1363,  546, 1221, 1213, 1202,  258,  546,
 /*  1620 */   349,  546, 1201,   11,  535, 1203, 1524,  351,   77,   77,
 /*  1630 */   376,  976,  976,  978,  979,   27,   74,   74,  353,  213,
 /*  1640 */   301,   43,   43,   48,   48,  437,  310,  201,  303, 1350,
 /*  1650 */   315,  355,  986,  454,  479, 1239,  339,  192,  105,  105,
 /*  1660 */  1422, 1421,  193,  536,  205,  106, 1527,  425,  548,  547,
 /*  1670 */  1180,  167,  976,  270,  247, 1469, 1467, 1177,  375,  375,
 /*  1680 */   374,  255,  372,  200,  369,  817,  400,   83,   79,   82,
 /*  1690 */  1427,  448,  177,   95, 1342,  161,  169, 1339,  215,  440,
 /*  1700 */   296,  172,  173,  976,  976,  978,  979,   27,  295,  174,
 /*  1710 */   175,  441,  472,  223, 1347,  383,   35,  381,   36,  461,
 /*  1720 */    88, 1353,  181,  447,  384, 1416,  227,  467,  259,  229,
 /*  1730 */   186,  488,  470,  324, 1250,  230,  231,  320,  217, 1204,
 /*  1740 */  1438, 1259,  386, 1258,  413,   90,  156,  849, 1541,  158,
 /*  1750 */   206,  415, 1540,  507, 1300, 1257,   94,  348, 1229, 1301,
 /*  1760 */   387, 1510, 1228,  338, 1227,  216,  350, 1539,  498,  283,
 /*  1770 */   284, 1249,  501, 1299,  352,  245,  246,  418, 1298,  354,
 /*  1780 */  1496, 1495,  124,   10,  526,  363,  101, 1324,  253,   96,
 /*  1790 */   510, 1210,   34,  549, 1137,  254,  256,  257,  166,  393,
 /*  1800 */   550, 1199, 1282,  361,  290,  537, 1281,  196,  367,  368,
 /*  1810 */  1194,  153, 1454,  137,  281, 1323, 1455,  804,  154,  426,
 /*  1820 */   198,  155, 1453, 1452,  292,  212,  202,  432, 1402,  203,
 /*  1830 */   271,  135,  288,   78, 1046, 1044,  960,  168,  157,  881,
 /*  1840 */   170,  219,  309,  222, 1060,  176,  964,  159,  402,   84,
 /*  1850 */   178,  404,   85,   86,   87,  160, 1063,  224,  394,  395,
 /*  1860 */   225, 1059,  146,   18,  226,  319,  243, 1174,  466,  228,
 /*  1870 */  1052,  182,  183,   37,  819,  471,  340,  232,  332,  483,
 /*  1880 */   184,   89,  162,   19,   20,  475,   91,  480,  847,  335,
 /*  1890 */   147,  860,  282,   92,  490,   93, 1125,  148, 1012, 1095,
 /*  1900 */    39,  497, 1096,   40,  500,  262,  207,  264,  930,  187,
 /*  1910 */   925,  109, 1111, 1115, 1113,    7, 1099,  242,   33, 1119,
 /*  1920 */    21,  522,   22,   23,   24, 1118,   25,  190,   97,   26,
 /*  1930 */  1027, 1013, 1011, 1015, 1069, 1016, 1068,  249,  248,   28,
 /*  1940 */    41,  891,  981,  829,  108,   29,  250,  542,  251,  370,
 /*  1950 */   373, 1133, 1132, 1190, 1190, 1190, 1190, 1190, 1190, 1190,
 /*  1960 */  1532, 1531,
};
static const YYCODETYPE yy_lookahead[] = {
 /*     0 */   189,  211,  189,  189,  218,  189,  220,  189,  267,  268,
 /*    10 */   269,  189,  210,  189,  228,  189,  267,  268,  269,   19,
 /*    20 */   218,  189,  211,  212,  211,  212,  211,  211,  212,  211,
 /*    30 */   212,   31,  211,  211,  212,  211,  212,  288,  300,   39,
 /*    40 */    21,  189,  304,   43,   44,   45,   46,   47,   48,   49,







|

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

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

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

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

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

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







156551
156552
156553
156554
156555
156556
156557
156558
156559
156560
156561
156562
156563
156564
156565
156566
156567
156568
156569
156570
156571
156572
156573
156574
156575
156576
156577
156578
156579
156580
156581
156582
156583
156584
156585
156586
156587
156588
156589
156590
156591
156592
156593
156594
156595
156596
156597
156598
156599
156600
156601
156602
156603
156604
156605
156606
156607
156608
156609
156610
156611
156612
156613
156614
156615
156616
156617
156618
156619
156620
156621
156622
156623
156624
156625
156626
156627
156628
156629
156630
156631
156632
156633
156634
156635
156636
156637
156638
156639
156640
156641
156642
156643
156644
156645
156646
156647
156648
156649
156650
156651
156652
156653
156654
156655
156656
156657
156658
156659
156660
156661
156662
156663
156664
156665
156666
156667
156668
156669
156670
156671
156672
156673
156674
156675
156676
156677
156678
156679
156680
156681
156682
156683
156684
156685
156686
156687
156688
156689
156690
156691
156692
156693
156694
156695
156696
156697
156698
156699
156700
156701
156702
156703
156704
156705
156706
156707
156708
156709
156710
156711
156712
156713
156714
156715
156716
156717
156718
156719
156720
156721
156722
156723
156724
156725
156726
156727
156728
156729
156730
156731
156732
156733
156734
156735
156736
156737
156738
156739
156740
156741
156742
156743
156744
156745
156746
156747
156748
156749
156750
156751
156752
156753
156754
156755
156756
156757
156758
156759
156760
156761
156762
156763
**  yy_shift_ofst[]    For each state, the offset into yy_action for
**                     shifting terminals.
**  yy_reduce_ofst[]   For each state, the offset into yy_action for
**                     shifting non-terminals after a reduce.
**  yy_default[]       Default action for each state.
**
*********** Begin parsing tables **********************************************/
#define YY_ACTTAB_COUNT (1968)
static const YYACTIONTYPE yy_action[] = {
 /*     0 */   551, 1229,  551,  456, 1267,  551, 1246,  551,  114,  111,
 /*    10 */   212,  551, 1545,  551, 1267,  528,  114,  111,  212,  396,
 /*    20 */  1239,  348,   42,   42,   42,   42, 1232,   42,   42,   71,
 /*    30 */    71,  943, 1231,   71,   71,   71,   71, 1470, 1501,  944,
 /*    40 */   826,  458,    6,  121,  122,  112, 1172, 1172, 1013, 1016,
 /*    50 */  1006, 1006,  119,  119,  120,  120,  120,  120, 1551,  396,
 /*    60 */  1366, 1525,  557,    2, 1200,  195,  533,  441,  143,  293,
 /*    70 */   533,  136,  533,  375,  262,  509,  273,  389, 1280,  532,
 /*    80 */   508,  498,  165,  121,  122,  112, 1172, 1172, 1013, 1016,
 /*    90 */  1006, 1006,  119,  119,  120,  120,  120,  120, 1366,  447,
 /*   100 */  1522,  118,  118,  118,  118,  117,  117,  116,  116,  116,
 /*   110 */   115,  429,  267,  267,  267,  267, 1506,  362, 1508,  440,
 /*   120 */   361, 1506,  522,  529, 1493,  548, 1121,  548, 1121,  396,
 /*   130 */   410,  242,  209,  114,  111,  212,   98,  292,  542,  222,
 /*   140 */  1036,  118,  118,  118,  118,  117,  117,  116,  116,  116,
 /*   150 */   115,  429, 1149,  121,  122,  112, 1172, 1172, 1013, 1016,
 /*   160 */  1006, 1006,  119,  119,  120,  120,  120,  120,  411,  433,
 /*   170 */   117,  117,  116,  116,  116,  115,  429, 1426,  473,  123,
 /*   180 */   118,  118,  118,  118,  117,  117,  116,  116,  116,  115,
 /*   190 */   429,  116,  116,  116,  115,  429,  545,  545,  545,  396,
 /*   200 */   510,  120,  120,  120,  120,  113, 1058, 1149, 1150, 1151,
 /*   210 */  1058,  118,  118,  118,  118,  117,  117,  116,  116,  116,
 /*   220 */   115,  429, 1469,  121,  122,  112, 1172, 1172, 1013, 1016,
 /*   230 */  1006, 1006,  119,  119,  120,  120,  120,  120,  396,  449,
 /*   240 */   320,   83,  468,   81,  363,  386, 1149,   80,  118,  118,
 /*   250 */   118,  118,  117,  117,  116,  116,  116,  115,  429,  180,
 /*   260 */   439,  429,  121,  122,  112, 1172, 1172, 1013, 1016, 1006,
 /*   270 */  1006,  119,  119,  120,  120,  120,  120,  439,  438,  267,
 /*   280 */   267,  118,  118,  118,  118,  117,  117,  116,  116,  116,
 /*   290 */   115,  429,  548, 1116,  909,  511, 1149,  114,  111,  212,
 /*   300 */  1439, 1149, 1150, 1151,  207,  496, 1116,  396,  454, 1116,
 /*   310 */   550,  334,  120,  120,  120,  120,  300, 1439, 1441,   17,
 /*   320 */   118,  118,  118,  118,  117,  117,  116,  116,  116,  115,
 /*   330 */   429,  121,  122,  112, 1172, 1172, 1013, 1016, 1006, 1006,
 /*   340 */   119,  119,  120,  120,  120,  120,  396, 1366,  439, 1149,
 /*   350 */   487, 1149, 1150, 1151, 1003, 1003, 1014, 1017,  406,  118,
 /*   360 */   118,  118,  118,  117,  117,  116,  116,  116,  115,  429,
 /*   370 */   121,  122,  112, 1172, 1172, 1013, 1016, 1006, 1006,  119,
 /*   380 */   119,  120,  120,  120,  120, 1061, 1061,  470, 1439,  118,
 /*   390 */   118,  118,  118,  117,  117,  116,  116,  116,  115,  429,
 /*   400 */  1149,  456,  551, 1434, 1149, 1150, 1151,  234,  973, 1149,
 /*   410 */   486,  483,  482,  172,  364,  396,  165,  412,  419,  848,
 /*   420 */   481,  165,  186,  338,   71,   71, 1250, 1007,  118,  118,
 /*   430 */   118,  118,  117,  117,  116,  116,  116,  115,  429,  121,
 /*   440 */   122,  112, 1172, 1172, 1013, 1016, 1006, 1006,  119,  119,
 /*   450 */   120,  120,  120,  120,  396, 1149, 1150, 1151,  841,   12,
 /*   460 */   318,  514,  164,  360, 1149, 1150, 1151,  114,  111,  212,
 /*   470 */   513,  292,  542,  551,  277,  181,  292,  542,  121,  122,
 /*   480 */   112, 1172, 1172, 1013, 1016, 1006, 1006,  119,  119,  120,
 /*   490 */   120,  120,  120,  349,  489,   71,   71,  118,  118,  118,
 /*   500 */   118,  117,  117,  116,  116,  116,  115,  429, 1149,  210,
 /*   510 */   416,  528, 1149, 1116, 1579,  382,  253,  270,  346,  492,
 /*   520 */   341,  491,  239,  396,  518,  368, 1116, 1134,  337, 1116,
 /*   530 */   192,  414,  288,   32,  462,  448,  118,  118,  118,  118,
 /*   540 */   117,  117,  116,  116,  116,  115,  429,  121,  122,  112,
 /*   550 */  1172, 1172, 1013, 1016, 1006, 1006,  119,  119,  120,  120,
 /*   560 */   120,  120,  396, 1149, 1150, 1151,  994, 1149, 1150, 1151,
 /*   570 */  1149,  234,  497, 1500,  486,  483,  482,    6,  164,  551,
 /*   580 */   517,  551,  115,  429,  481,    5,  121,  122,  112, 1172,
 /*   590 */  1172, 1013, 1016, 1006, 1006,  119,  119,  120,  120,  120,
 /*   600 */   120,   13,   13,   13,   13,  118,  118,  118,  118,  117,
 /*   610 */   117,  116,  116,  116,  115,  429,  408,  507,  413,  551,
 /*   620 */  1494,  549, 1149,  898,  898, 1149, 1150, 1151, 1481, 1149,
 /*   630 */   276,  396,  814,  815,  816,  978,  427,  427,  427,   16,
 /*   640 */    16,   55,   55, 1249,  118,  118,  118,  118,  117,  117,
 /*   650 */   116,  116,  116,  115,  429,  121,  122,  112, 1172, 1172,
 /*   660 */  1013, 1016, 1006, 1006,  119,  119,  120,  120,  120,  120,
 /*   670 */   396, 1196,    1,    1,  557,    2, 1200, 1149, 1150, 1151,
 /*   680 */   195,  293,  904,  136, 1149, 1150, 1151,  903,  526, 1500,
 /*   690 */  1280,    3,  384,    6,  121,  122,  112, 1172, 1172, 1013,
 /*   700 */  1016, 1006, 1006,  119,  119,  120,  120,  120,  120,  864,
 /*   710 */   551,  930,  551,  118,  118,  118,  118,  117,  117,  116,
 /*   720 */   116,  116,  115,  429,  267,  267, 1099, 1577, 1149,  556,
 /*   730 */  1577, 1200,   13,   13,   13,   13,  293,  548,  136,  396,
 /*   740 */   490,  426,  425,  973,  348, 1280,  473,  415,  865,  281,
 /*   750 */   140,  222,  118,  118,  118,  118,  117,  117,  116,  116,
 /*   760 */   116,  115,  429,  121,  122,  112, 1172, 1172, 1013, 1016,
 /*   770 */  1006, 1006,  119,  119,  120,  120,  120,  120,  551,  267,
 /*   780 */   267,  433,  396, 1149, 1150, 1151, 1179,  836, 1179,  473,
 /*   790 */   436,  145,  548, 1153,  405,  318,  444,  304,  844, 1498,
 /*   800 */    71,   71,  417,    6, 1097,  478,  222,  100,  112, 1172,
 /*   810 */  1172, 1013, 1016, 1006, 1006,  119,  119,  120,  120,  120,
 /*   820 */   120,  118,  118,  118,  118,  117,  117,  116,  116,  116,
 /*   830 */   115,  429,  238, 1433,  551,  456,  433,  289,  993,  551,
 /*   840 */   237,  236,  235,  836,   97,  534,  434, 1272, 1272, 1153,
 /*   850 */   499,  309,  435,  844,  984,  551,   71,   71,  983, 1248,
 /*   860 */   551,   51,   51,  302,  118,  118,  118,  118,  117,  117,
 /*   870 */   116,  116,  116,  115,  429,  195,  103,   70,   70,  267,
 /*   880 */   267,  551,   71,   71,  267,  267,   30,  395,  348,  983,
 /*   890 */   983,  985,  548,  533, 1116,  332,  396,  548,  500,  401,
 /*   900 */   460,  196,  535,   13,   13, 1366,  241, 1116,  278,  282,
 /*   910 */  1116,  282,  306,  462,  308,  337,  396,   31,  189,  424,
 /*   920 */   121,  122,  112, 1172, 1172, 1013, 1016, 1006, 1006,  119,
 /*   930 */   119,  120,  120,  120,  120,  142,  396,  369,  456,  993,
 /*   940 */   121,  122,  112, 1172, 1172, 1013, 1016, 1006, 1006,  119,
 /*   950 */   119,  120,  120,  120,  120,  984,  327, 1149,  330,  983,
 /*   960 */   121,  110,  112, 1172, 1172, 1013, 1016, 1006, 1006,  119,
 /*   970 */   119,  120,  120,  120,  120,  469,  381, 1192,  118,  118,
 /*   980 */   118,  118,  117,  117,  116,  116,  116,  115,  429, 1149,
 /*   990 */   983,  983,  985,  307,    9,  461,  245,  462,  118,  118,
 /*  1000 */   118,  118,  117,  117,  116,  116,  116,  115,  429,  317,
 /*  1010 */   551,  279, 1149, 1150, 1151,  301,  292,  542,  118,  118,
 /*  1020 */   118,  118,  117,  117,  116,  116,  116,  115,  429, 1270,
 /*  1030 */  1270, 1170,   13,   13,  531,  426,  425,  473,  396,  929,
 /*  1040 */   261,  261,   97, 1176, 1149, 1150, 1151,  190, 1178,  267,
 /*  1050 */   267,  473,  138,  548, 1193,  551, 1177,  264,  348,  494,
 /*  1060 */   928,  551,  548,  122,  112, 1172, 1172, 1013, 1016, 1006,
 /*  1070 */  1006,  119,  119,  120,  120,  120,  120,   71,   71, 1149,
 /*  1080 */  1179, 1279, 1179,   13,   13,  904, 1077, 1170,  551,  473,
 /*  1090 */   903,  107,  543,  280,    4, 1275, 1116,  450,  530, 1056,
 /*  1100 */    12, 1078, 1099, 1578,  316,  144, 1578,  525,  546, 1116,
 /*  1110 */    56,   56, 1116, 1499,  428, 1366, 1079,    6,  349,  970,
 /*  1120 */   118,  118,  118,  118,  117,  117,  116,  116,  116,  115,
 /*  1130 */   429,  430, 1278,  325, 1149, 1150, 1151,  884,  267,  267,
 /*  1140 */   855,  107,  543,  540,    4, 1497,  238,  885, 1218,    6,
 /*  1150 */   211,  548,  370,  165,  366,  501,  421, 1496,  546,  268,
 /*  1160 */   268,    6, 1550,  516,  504,  873,  267,  267,  400,  536,
 /*  1170 */     8,  993,  548,  524,  551,  928,  463,  105,  105,  548,
 /*  1180 */  1097,  430,  267,  267,  106,  422,  430,  553,  552,  267,
 /*  1190 */   267,  983,  523,  540, 1381,  548,   15,   15,  267,  267,
 /*  1200 */  1478, 1127,  548,  267,  267, 1077, 1380,  520,  292,  542,
 /*  1210 */   551,  548,  519,  401,  449,  320,  548,  551,  928,  125,
 /*  1220 */  1078,  993,  983,  983,  985,  986,   27,  105,  105,  405,
 /*  1230 */   347, 1519,   44,   44,  106, 1079,  430,  553,  552,   57,
 /*  1240 */    57,  983,  347, 1519,  107,  543,  551,    4,  467,  405,
 /*  1250 */   215, 1127,  464,  295,  381, 1098,  539,  296,  551, 1221,
 /*  1260 */   402,  546,  544,  402,  299,  245,  292,  542,   58,   58,
 /*  1270 */   551, 1284,  983,  983,  985,  986,   27, 1524, 1138,  432,
 /*  1280 */    59,   59,  271,  548,  430,  403,  166,  379,  379,  378,
 /*  1290 */   256,  376,   60,   60,  823, 1187,  540,  551,  274,  551,
 /*  1300 */  1170,  851,  393,  392,  551,  205,  551,  216,  211,  298,
 /*  1310 */   520, 1303,  551,  266,  209,  521, 1316,  297,  275,   61,
 /*  1320 */    61,   62,   62,  451,  993,  205,   45,   45,   46,   46,
 /*  1330 */   105,  105, 1193,  928,   47,   47,  291,  106,  551,  430,
 /*  1340 */   553,  552,  943,  551,  983,  313,  394,  218,  551,  109,
 /*  1350 */   944,  107,  543,  219,    4,  156, 1170,  851,  158,  551,
 /*  1360 */    49,   49,  104,  551,  102,   50,   50,  551,  546, 1315,
 /*  1370 */    63,   63,  551,  443,  217,  983,  983,  985,  986,   27,
 /*  1380 */  1484,   64,   64,  551,  310,   65,   65,  551, 1458,   14,
 /*  1390 */    14,  430, 1457,  551,   66,   66, 1094,  551, 1169,  383,
 /*  1400 */   141,  551,   38,  540,  269,  127,  127,  551,  397,   67,
 /*  1410 */    67,  551,  465,  292,  542,   52,   52,  520,  551,   68,
 /*  1420 */    68, 1043,  519,   69,   69,  315,   95,  322,   97,   53,
 /*  1430 */    53,  993,  975,  151,  151,  244,  437,  105,  105,  200,
 /*  1440 */   152,  152,  455, 1312,  106,  244,  430,  553,  552, 1138,
 /*  1450 */   432,  983,  457,  271,  321,  244,  326,   97,  379,  379,
 /*  1460 */   378,  256,  376,  863,  862,  823,  531,  551,  221,  551,
 /*  1470 */   107,  543,  551,    4,  551,  329,  479, 1043,  216,  240,
 /*  1480 */   298,  331,  983,  983,  985,  986,   27,  546,  297,   76,
 /*  1490 */    76,   54,   54,  333,   72,   72,  128,  128,  870,  871,
 /*  1500 */   107,  543,  551,    4, 1263,  551,  946,  947, 1247,  551,
 /*  1510 */   430,  551,  200, 1055,  551, 1055,  551,  546,  218,  551,
 /*  1520 */   335, 1538,  540,   97,   73,   73,  156,  129,  129,  158,
 /*  1530 */   340,  130,  130,  126,  126,  350,  150,  150,  149,  149,
 /*  1540 */   430,  134,  134,  345, 1039,  217,  937,  240,  901,  244,
 /*  1550 */   993,  109,  540,  344,  987,  551,  105,  105,  908,  351,
 /*  1560 */   551, 1513, 1054,  106, 1054,  430,  553,  552,  551, 1324,
 /*  1570 */   983,  834,   99,  543,  139,    4,  551,  133,  133,  397,
 /*  1580 */   993, 1365,  131,  131,  292,  542,  105,  105, 1299,  546,
 /*  1590 */   132,  132,  287,  106, 1310,  430,  553,  552,   75,   75,
 /*  1600 */   983,  983,  983,  985,  986,   27,  551,  437,  902,  537,
 /*  1610 */   987,  109,  430,  259,  551,  538, 1371, 1228,  474,  551,
 /*  1620 */   198,  551, 1220, 1209,  540, 1208, 1210, 1532,   77,   77,
 /*  1630 */   202,  983,  983,  985,  986,   27,   74,   74, 1296,  353,
 /*  1640 */   355,   43,   43,   48,   48,  357,   11,  380,  214,  343,
 /*  1650 */   303,  442,  993,  312,  305, 1360,  314,  484,  105,  105,
 /*  1660 */   459, 1246,  319,  206, 1430,  106, 1429,  430,  553,  552,
 /*  1670 */   359,  541,  983,  271, 1535, 1187,  168,  248,  379,  379,
 /*  1680 */   378,  256,  376,  201,  193,  823,  373,  194, 1477, 1475,
 /*  1690 */  1184,   79,  404,   82,   83,  453,  178,   95,  216, 1349,
 /*  1700 */   298,  162, 1435,  983,  983,  985,  986,   27,  297, 1354,
 /*  1710 */  1346,   35,  170,  445,  446,  477,  173,  174,  175,  176,
 /*  1720 */   385,  224, 1358, 1361, 1357,  466,  387,   36,  182,  452,
 /*  1730 */   388, 1424,  228,   88,  472,  260,  230, 1446,  218,  187,
 /*  1740 */   475,  328,  231,  390,  324, 1211,  156,  232,  493,  158,
 /*  1750 */   418,   90, 1257, 1266, 1549, 1265, 1264,  855, 1256,  207,
 /*  1760 */   420,  512, 1307, 1548,   94,  217,  352,  391, 1236, 1235,
 /*  1770 */   342, 1234, 1547, 1518,  354,  285,  503,  286,  506,  246,
 /*  1780 */   247, 1504, 1503,  423, 1308,  124,  531, 1306,  356,   10,
 /*  1790 */  1305,  367, 1331,  101,  290,   96,  254,  515, 1217,  397,
 /*  1800 */    34,  554, 1144,  255,  292,  542,  257,  372, 1289,  365,
 /*  1810 */   371,  358, 1288,  197,  258,  555, 1206, 1201, 1462,  153,
 /*  1820 */  1463, 1330, 1461,  154,  137,  283, 1460,  437,  155,  203,
 /*  1830 */   810,  204,   78,  431, 1410,  199,  294,  213,  272,  135,
 /*  1840 */  1053, 1051,  966,  157,  169,  220,  171,  887,  311,  223,
 /*  1850 */  1067,  177,  159,  160,  407,   84,  409,  179,   85,   86,
 /*  1860 */    87,  161, 1070,  225, 1066,  398,  167,  399,   18,  226,
 /*  1870 */   146,  227,  323,  244, 1181,  471,  229, 1059,  183,  184,
 /*  1880 */    37,  825,  344,  476,  233,  336,  488,  480,  185,   89,
 /*  1890 */    19,   20,  485,   92,  853,  339,   91,  163,  866,  147,
 /*  1900 */   284,  495,  502, 1132,  148, 1019,  936, 1102,   39,   93,
 /*  1910 */  1103,   40,  505,  263,  208,  265,  188,  931, 1122,  243,
 /*  1920 */  1126,  109,   33, 1120, 1118,   21, 1106,   22,  527, 1034,
 /*  1930 */    23,   24, 1125,   25,  191,   97,   26, 1020, 1018, 1022,
 /*  1940 */  1076,  250,    7, 1075,  249, 1023,   28,   41,  547,  988,
 /*  1950 */   835,  108,   29,  251,  252, 1540,  374,  897,  377, 1140,
 /*  1960 */  1139, 1197, 1197, 1197, 1197, 1197, 1197, 1539,
};
static const YYCODETYPE yy_lookahead[] = {
 /*     0 */   189,  211,  189,  189,  218,  189,  220,  189,  267,  268,
 /*    10 */   269,  189,  210,  189,  228,  189,  267,  268,  269,   19,
 /*    20 */   218,  189,  211,  212,  211,  212,  211,  211,  212,  211,
 /*    30 */   212,   31,  211,  211,  212,  211,  212,  288,  300,   39,
 /*    40 */    21,  189,  304,   43,   44,   45,   46,   47,   48,   49,
155686
155687
155688
155689
155690
155691
155692
155693
155694
155695
155696
155697
155698
155699
155700
155701
155702
155703
155704
155705
155706
155707
155708
155709
155710
155711
155712
155713
155714
155715
155716
155717
155718
155719
155720
155721
155722
155723
155724
155725
155726
155727
155728
155729
155730
155731
155732
155733
155734
155735
155736
155737
155738
155739
155740
155741
155742
155743
155744
155745
155746
155747
155748
155749
155750
155751
155752
155753
155754
155755
155756
155757
155758
155759
155760
155761
155762
155763
155764
155765
155766
155767
155768
155769
155770
155771
155772
155773
155774
155775
155776
155777
155778
155779
155780
155781
155782
155783
155784
155785
155786
155787

155788
155789
155790
155791
155792
155793
155794
155795
155796
155797
155798
155799
155800
155801
155802
155803
155804
155805
155806
155807
155808
155809
155810
155811
155812
155813
155814
155815
155816
155817
155818
155819
155820
155821
155822
155823
155824
155825
155826
155827
155828
155829
155830
155831
155832
155833
155834
155835
155836
155837
155838
155839
155840
155841
155842
155843
155844
155845
155846
155847
155848
155849
155850
155851
155852
155853
155854
155855
155856
155857
155858
155859
155860
155861
155862
155863
155864
155865
155866
155867
155868
155869
155870
155871
155872
155873
155874
155875
155876
155877
155878
155879
155880
155881
155882
155883
155884
155885
155886
155887
155888
155889
155890
155891
155892
155893
155894
155895
155896
155897
155898
155899
155900
155901
155902
155903
155904
155905
155906
155907
155908
155909
155910
155911
155912
155913
155914
155915
155916
155917
155918
155919
155920
155921
155922
155923
155924
155925
155926
155927
155928
155929
155930
155931
155932
155933
155934
155935
155936
155937
155938
155939
155940
155941
155942
155943
155944
155945
155946
155947
155948
155949
155950
155951
155952
155953
155954
155955
155956
155957
155958
155959
155960
155961
155962
155963
155964
155965
155966
155967
155968
155969
155970
155971
155972
155973
155974
155975
155976
155977
155978
155979
155980
155981
155982
155983
155984
155985
155986
155987
155988
 /*   830 */   110,  111,   46,  233,  189,  189,  291,  248,   99,  189,
 /*   840 */   125,  126,  127,  115,   26,  200,  289,  230,  231,  115,
 /*   850 */   200,   16,  189,  114,  115,  189,  211,  212,  119,  221,
 /*   860 */   189,  211,  212,  258,  101,  102,  103,  104,  105,  106,
 /*   870 */   107,  108,  109,  110,  111,  189,  156,  211,  212,  234,
 /*   880 */   235,  189,  211,  212,  234,  235,   22,  201,  189,  150,
 /*   890 */   151,  152,  247,  248,   76,   16,   19,  247,  248,  113,
 /*   900 */   189,   24,  257,  211,  212,  189,   26,   89,  262,  223,
 /*   910 */    92,  225,   77,  189,   79,  129,   19,   53,  226,  248,
 /*   920 */    43,   44,   45,   46,   47,   48,   49,   50,   51,   52,
 /*   930 */    53,   54,   55,   56,   57,  236,   19,  271,  189,   99,
 /*   940 */    43,   44,   45,   46,   47,   48,   49,   50,   51,   52,
 /*   950 */    53,   54,   55,   56,   57,  115,   77,   59,   79,  119,
 /*   960 */    43,   44,   45,   46,   47,   48,   49,   50,   51,   52,
 /*   970 */    53,   54,   55,   56,   57,  259,   22,   23,  101,  102,
 /*   980 */   103,  104,  105,  106,  107,  108,  109,  110,  111,   59,
 /*   990 */   150,  151,  152,  158,   22,  244,   24,  246,  101,  102,
 /*  1000 */   103,  104,  105,  106,  107,  108,  109,  110,  111,  285,
 /*  1010 */   189,  189,  114,  115,  116,  200,  136,  137,  101,  102,
 /*  1020 */   103,  104,  105,  106,  107,  108,  109,  110,  111,  230,
 /*  1030 */   231,   59,  211,  212,  285,  105,  106,  189,   19,  141,
 /*  1040 */   234,  235,  239,  113,  114,  115,  116,  226,  118,  234,
 /*  1050 */   235,  189,  249,  247,  100,  189,  126,   23,  236,  107,
 /*  1060 */    26,  189,  247,   44,   45,   46,   47,   48,   49,   50,
 /*  1070 */    51,   52,   53,   54,   55,   56,   57,  211,  212,   59,
 /*  1080 */   150,  233,  152,  211,  212,  133,   12,  115,  189,  189,
 /*  1090 */   138,   19,   20,  300,   22,  233,   76,  304,  226,   11,
 /*  1100 */   208,   27,   22,   23,  200,   19,   26,   87,   36,   89,
 /*  1110 */   211,  212,   92,  300,  248,  189,   42,  304,  189,  250,
 /*  1120 */   101,  102,  103,  104,  105,  106,  107,  108,  109,  110,
 /*  1130 */   111,   59,  200,  233,  114,  115,  116,   63,  234,  235,
 /*  1140 */   235,   19,   20,   71,   22,  300,  189,   73,  200,  304,
 /*  1150 */   116,  247,  247,   81,  189,  200,  227,   26,   36,  234,
 /*  1160 */   235,  203,  204,  143,  200,   26,  234,  235,  194,  200,
 /*  1170 */    48,   99,  247,   66,  189,  141,  284,  105,  106,  247,
 /*  1180 */   100,   59,  234,  235,  112,  259,  114,  115,  116,  234,
 /*  1190 */   235,  119,   85,   71,  266,  247,  211,  212,  234,  235,
 /*  1200 */   114,   94,  247,  234,  235,   12,  266,   85,  136,  137,
 /*  1210 */   189,  247,   90,   26,  126,  127,  247,  189,   26,   22,
 /*  1220 */    27,   99,  150,  151,  152,  153,  154,  105,  106,  189,
 /*  1230 */   302,  303,  211,  212,  112,   42,  114,  115,  116,  211,
 /*  1240 */   212,  119,  302,  303,   19,   20,  189,   22,  274,  189,
 /*  1250 */    15,  144,  278,  189,   22,   23,   63,  189,  189,  203,
 /*  1260 */   204,   36,  136,  137,  155,   24,  157,  143,  211,  212,
 /*  1270 */   189,  140,  150,  151,  152,  153,  154,    0,    1,    2,
 /*  1280 */   211,  212,    5,   46,   59,  161,  147,   10,   11,   12,
 /*  1290 */    13,   14,  211,  212,   17,   60,   71,  189,  258,  189,
 /*  1300 */    59,  189,  105,  106,  189,  189,  189,   30,  116,   32,
 /*  1310 */    85,  124,  189,  251,  252,   90,  189,   40,  258,  211,
 /*  1320 */   212,  211,  212,  189,   99,   26,  211,  212,  211,  212,
 /*  1330 */   105,  106,  100,  141,  211,  212,  119,  112,  189,  114,
 /*  1340 */   115,  116,   23,  189,  119,   26,  129,   70,  189,   31,
 /*  1350 */   113,   19,   20,   24,   22,   78,  115,   39,   81,  189,
 /*  1360 */   211,  212,   26,  189,   22,  211,  212,  189,   36,  189,
 /*  1370 */   211,  212,  189,  189,   97,  150,  151,  152,  153,  154,
 /*  1380 */   127,  211,  212,  189,  189,  211,  212,  189,  189,  211,
 /*  1390 */   212,   59,  189,  189,  211,  212,   23,  189,   22,   26,
 /*  1400 */    24,  189,  149,   71,  189,  211,  212,  189,  131,  211,
 /*  1410 */   212,  189,  189,  136,  137,  211,  212,   85,  189,  211,
 /*  1420 */   212,   59,   90,  211,  212,  292,  293,  118,  119,  211,
 /*  1430 */   212,   99,   23,  211,  212,   26,  159,  105,  106,  189,
 /*  1440 */   211,  212,  143,  150,  112,  152,  114,  115,  116,    1,
 /*  1450 */     2,  119,   23,    5,   23,   26,  189,   26,   10,   11,
 /*  1460 */    12,   13,   14,   83,   84,   17,  253,  189,  139,  189,
 /*  1470 */    19,   20,  189,   22,  189,  189,  140,  115,   30,   59,
 /*  1480 */    32,  139,  150,  151,  152,  153,  154,   36,   40,  211,
 /*  1490 */   212,  211,  212,   59,  211,  212,  211,  212,    7,    8,
 /*  1500 */    19,   20,  189,   22,  150,  189,  152,  231,  281,  189,
 /*  1510 */    59,  189,   23,  189,  189,   26,  189,   36,   70,  189,
 /*  1520 */    23,  237,   71,   26,  211,  212,   78,  211,  212,   81,
 /*  1530 */   189,  211,  212,  211,  212,  115,  211,  212,  211,  212,
 /*  1540 */    59,  211,  212,   23,   23,   97,   26,   26,   23,  115,
 /*  1550 */    99,   26,   71,  189,  189,  189,  105,  106,  107,   23,
 /*  1560 */   189,   23,   26,  112,   26,  114,  115,  116,  189,  309,
 /*  1570 */   119,   23,   19,   20,   26,   22,  189,  211,  212,  131,
 /*  1580 */    99,  189,  211,  212,  136,  137,  105,  106,  189,   36,
 /*  1590 */   211,  212,  189,  112,  189,  114,  115,  116,  211,  212,
 /*  1600 */   119,  150,  151,  152,  153,  154,  189,  159,   23,  250,
 /*  1610 */   189,   26,   59,  189,  189,  189,  189,  189,  280,  189,
 /*  1620 */   250,  189,  189,  238,   71,  189,  189,  250,  211,  212,
 /*  1630 */   187,  150,  151,  152,  153,  154,  211,  212,  250,  290,
 /*  1640 */   240,  211,  212,  211,  212,  254,  286,  209,  254,  241,
 /*  1650 */   240,  254,   99,  286,  215,  220,  214,  244,  105,  106,
 /*  1660 */   214,  214,  244,  273,  224,  112,  192,  114,  115,  116,
 /*  1670 */    60,  290,  119,    5,  139,  196,  196,   38,   10,   11,
 /*  1680 */    12,   13,   14,  238,  240,   17,  196,  148,  287,  287,
 /*  1690 */   276,  113,   22,  147,  241,   43,  229,  241,   30,   18,
 /*  1700 */    32,  232,  232,  150,  151,  152,  153,  154,   40,  232,
 /*  1710 */   232,  196,   18,  195,  265,  265,  264,  241,  264,  196,
 /*  1720 */   155,  229,  229,  241,  241,  241,  195,   62,  196,  195,
 /*  1730 */    22,  113,  216,  196,  222,  195,  195,  282,   70,  196,
 /*  1740 */   283,  213,  216,  213,   64,   22,   78,  124,  219,   81,
 /*  1750 */   162,  111,  219,  142,  256,  213,  113,  255,  213,  256,
 /*  1760 */   216,  303,  215,  213,  213,   97,  255,  213,  216,  275,
 /*  1770 */   275,  222,  216,  256,  255,  196,   91,   82,  256,  255,
 /*  1780 */   308,  308,  146,   22,  143,  196,  155,  260,   25,  145,
 /*  1790 */   144,  199,   26,  198,   13,  190,  190,    6,  293,  131,
 /*  1800 */   188,  188,  245,  244,  136,  137,  245,  243,  242,  241,
 /*  1810 */   188,  202,  208,  217,  217,  260,  208,    4,  202,    3,
 /*  1820 */    22,  202,  208,  208,  160,   15,  209,  159,  270,  209,
 /*  1830 */    98,   16,  272,  208,   23,   23,  137,  148,  128,   20,
 /*  1840 */   140,   24,   16,  142,    1,  140,  149,  128,   61,   53,

 /*  1850 */   148,   37,   53,   53,   53,  128,  114,   34,  296,  296,
 /*  1860 */   139,    1,    5,   22,  113,  158,   26,   75,   41,  139,
 /*  1870 */    68,   68,  113,   24,   20,   19,  129,  123,   23,   96,
 /*  1880 */    22,   22,   37,   22,   22,   67,   22,   67,   59,   24,
 /*  1890 */    23,   28,   67,  147,   22,   26,   23,   23,   23,   23,
 /*  1900 */    22,   24,   23,   22,   24,   23,  139,   23,  114,   22,
 /*  1910 */   141,   26,   88,   75,   86,   44,   23,   34,   22,   75,
 /*  1920 */    34,   24,   34,   34,   34,   93,   34,   26,   26,   34,
 /*  1930 */    23,   23,   23,   23,   23,   11,   23,   22,   26,   22,
 /*  1940 */    22,  133,   23,   23,   22,   22,  139,   26,  139,   23,
 /*  1950 */    15,    1,    1,  310,  310,  310,  310,  310,  310,  310,
 /*  1960 */   139,  139,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  1970 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  1980 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  1990 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2000 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2010 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2020 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2030 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2040 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2050 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2060 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2070 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2080 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2090 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2100 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2110 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2120 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2130 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2140 */   310,  310,  310,
};
#define YY_SHIFT_COUNT    (552)
#define YY_SHIFT_MIN      (0)
#define YY_SHIFT_MAX      (1951)
static const unsigned short int yy_shift_ofst[] = {
 /*     0 */  1448, 1277, 1668, 1072, 1072,  340, 1122, 1225, 1332, 1481,
 /*    10 */  1481, 1481,  335,    0,    0,  180,  897, 1481, 1481, 1481,
 /*    20 */  1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481,
 /*    30 */   930,  930, 1020, 1020,  290,    1,  340,  340,  340,  340,
 /*    40 */   340,  340,   40,  110,  219,  288,  327,  396,  435,  504,
 /*    50 */   543,  612,  651,  720,  877,  897,  897,  897,  897,  897,
 /*    60 */   897,  897,  897,  897,  897,  897,  897,  897,  897,  897,
 /*    70 */   897,  897,  897,  917,  897, 1019,  763,  763, 1451, 1481,
 /*    80 */  1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481,
 /*    90 */  1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481,
 /*   100 */  1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481,
 /*   110 */  1481, 1481, 1553, 1481, 1481, 1481, 1481, 1481, 1481, 1481,
 /*   120 */  1481, 1481, 1481, 1481, 1481, 1481,  147,  258,  258,  258,
 /*   130 */   258,  258,   79,   65,   84,  449,   19,  786,  449,  636,
 /*   140 */   636,  449,  880,  880,  880,  880,  113,  142,  142,  472,
 /*   150 */   150, 1962, 1962,  399,  399,  399,   93,  237,  341,  237,
 /*   160 */   237, 1074, 1074,  437,  350,  704, 1080,  449,  449,  449,
 /*   170 */   449,  449,  449,  449,  449,  449,  449,  449,  449,  449,
 /*   180 */   449,  449,  449,  449,  449,  449,  449,  449,  818,  818,
 /*   190 */   449, 1088,  217,  217,  734,  734, 1124, 1126, 1962, 1962,
 /*   200 */  1962,  739,  840,  840,  453,  454,  511,  187,  563,  570,
 /*   210 */   898,  669,  449,  449,  449,  449,  449,  449,  449,  449,
 /*   220 */   449,  670,  449,  449,  449,  449,  449,  449,  449,  449,
 /*   230 */   449,  449,  449,  449,  674,  674,  674,  449,  449,  449,
 /*   240 */   449, 1034,  449,  449,  449,  972, 1107,  449,  449, 1193,
 /*   250 */   449,  449,  449,  449,  449,  449,  449,  449,  260,  177,
 /*   260 */   489, 1241, 1241, 1241, 1241, 1192,  489,  489,  952, 1197,
 /*   270 */   625, 1235, 1131,  181,  181, 1086, 1139, 1131, 1086, 1187,
 /*   280 */  1319, 1237, 1318, 1318, 1318,  181, 1299, 1299, 1109, 1336,
 /*   290 */   549, 1376, 1610, 1535, 1535, 1639, 1639, 1535, 1539, 1578,
 /*   300 */  1670, 1546, 1652, 1546, 1681, 1681, 1681, 1681, 1535, 1694,
 /*   310 */  1546, 1546, 1578, 1670, 1652, 1546, 1652, 1546, 1535, 1694,
 /*   320 */  1565, 1665, 1535, 1694, 1708, 1535, 1694, 1535, 1694, 1708,
 /*   330 */  1618, 1618, 1618, 1680, 1723, 1723, 1708, 1618, 1623, 1618,
 /*   340 */  1680, 1618, 1618, 1588, 1708, 1640, 1640, 1708, 1611, 1643,
 /*   350 */  1611, 1643, 1611, 1643, 1611, 1643, 1535, 1685, 1685, 1695,
 /*   360 */  1695, 1636, 1641, 1761, 1535, 1631, 1636, 1644, 1646, 1546,
 /*   370 */  1763, 1766, 1781, 1781, 1791, 1791, 1791, 1962, 1962, 1962,
 /*   380 */  1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962, 1962,
 /*   390 */  1962, 1962,  308,  835,  954, 1232,  879,  715,  728, 1373,
 /*   400 */   864, 1329, 1253, 1409,  297, 1431, 1489, 1497, 1520, 1521,
 /*   410 */  1525, 1362, 1309, 1491, 1217, 1420, 1429, 1536, 1380, 1538,
 /*   420 */  1293, 1354, 1548, 1585, 1434, 1342, 1813, 1816, 1798, 1664,
 /*   430 */  1810, 1732, 1815, 1811, 1812, 1699, 1689, 1710, 1817, 1700,
 /*   440 */  1819, 1701, 1826, 1843, 1705, 1697, 1719, 1787, 1814, 1702,
 /*   450 */  1796, 1799, 1800, 1801, 1727, 1742, 1823, 1721, 1860, 1857,
 /*   460 */  1841, 1751, 1707, 1802, 1840, 1803, 1792, 1827, 1730, 1759,
 /*   470 */  1849, 1854, 1856, 1747, 1754, 1858, 1818, 1859, 1861, 1855,
 /*   480 */  1862, 1820, 1829, 1865, 1783, 1863, 1864, 1825, 1845, 1867,
 /*   490 */  1746, 1872, 1873, 1874, 1875, 1869, 1876, 1878, 1877, 1879,
 /*   500 */  1881, 1880, 1767, 1882, 1884, 1794, 1883, 1887, 1769, 1885,
 /*   510 */  1886, 1888, 1889, 1890, 1824, 1838, 1828, 1871, 1844, 1832,
 /*   520 */  1892, 1893, 1896, 1897, 1901, 1902, 1895, 1907, 1885, 1908,
 /*   530 */  1909, 1910, 1911, 1912, 1913, 1915, 1924, 1917, 1918, 1919,
 /*   540 */  1920, 1922, 1923, 1921, 1808, 1807, 1809, 1821, 1822, 1926,
 /*   550 */  1935, 1950, 1951,
};
#define YY_REDUCE_COUNT (391)
#define YY_REDUCE_MIN   (-262)
#define YY_REDUCE_MAX   (1625)
static const short yy_reduce_ofst[] = {
 /*     0 */   490, -122,  545,  645,  650, -120, -189, -187, -184, -182,
 /*    10 */  -178, -176,   45,   30,  200, -251, -134,  390,  392,  521,
 /*    20 */   523,  213,  692,  821,  284,  589,  872,  666,  671,  866,
 /*    30 */    71,  111,  273,  389,  686,  815,  904,  932,  948,  955,
 /*    40 */   964,  969, -259, -259, -259, -259, -259, -259, -259, -259,
 /*    50 */  -259, -259, -259, -259, -259, -259, -259, -259, -259, -259,
 /*    60 */  -259, -259, -259, -259, -259, -259, -259, -259, -259, -259,
 /*    70 */  -259, -259, -259, -259, -259, -259, -259, -259,  428,  430,
 /*    80 */   899,  985, 1021, 1028, 1057, 1069, 1081, 1108, 1110, 1115,
 /*    90 */  1117, 1123, 1149, 1154, 1159, 1170, 1174, 1178, 1183, 1194,
 /*   100 */  1198, 1204, 1208, 1212, 1218, 1222, 1229, 1278, 1280, 1283,
 /*   110 */  1285, 1313, 1316, 1320, 1322, 1325, 1327, 1330, 1366, 1371,
 /*   120 */  1379, 1387, 1417, 1425, 1430, 1432, -259, -259, -259, -259,
 /*   130 */  -259, -259, -259, -259, -259,  557,  974, -214, -174,   -9,
 /*   140 */   431, -124,  806,  925,  806,  925,  251,  928,  940, -259,
 /*   150 */  -259, -259, -259, -198, -198, -198,  127, -186, -168,  212,
 /*   160 */   646,  617,  799, -262,  555,  220,  220,  491,  605, 1040,
 /*   170 */  1060,  699,  -11,  600,  848,  862,  345, -129,  724,  -91,
 /*   180 */   158,  749,  716,  900,  304,  822,  929,  926,  499,  793,
 /*   190 */   322,  892,  813,  845,  958, 1056,  751,  905, 1133, 1062,
 /*   200 */   803, -210, -185, -179, -148, -167,  -89,  121,  274,  281,
 /*   210 */   320,  336,  439,  663,  711,  957,  965, 1064, 1068, 1112,
 /*   220 */  1116, -196, 1127, 1134, 1180, 1184, 1195, 1199, 1203, 1215,
 /*   230 */  1223, 1250, 1267, 1286,  205,  422,  638, 1324, 1341, 1364,
 /*   240 */  1365, 1213, 1392, 1399, 1403,  869, 1260, 1405, 1421, 1276,
 /*   250 */  1424,  121, 1426, 1427, 1428, 1433, 1436, 1437, 1227, 1338,
 /*   260 */  1284, 1359, 1370, 1377, 1388, 1213, 1284, 1284, 1385, 1438,
 /*   270 */  1443, 1349, 1400, 1391, 1394, 1360, 1408, 1410, 1367, 1439,
 /*   280 */  1440, 1435, 1442, 1446, 1447, 1397, 1413, 1418, 1390, 1444,
 /*   290 */  1445, 1474, 1381, 1479, 1480, 1401, 1402, 1490, 1414, 1449,
 /*   300 */  1452, 1453, 1467, 1456, 1469, 1470, 1477, 1478, 1515, 1518,
 /*   310 */  1476, 1482, 1450, 1454, 1492, 1483, 1493, 1484, 1523, 1531,
 /*   320 */  1457, 1455, 1532, 1534, 1516, 1537, 1540, 1543, 1541, 1526,
 /*   330 */  1528, 1530, 1542, 1512, 1529, 1533, 1544, 1545, 1547, 1550,
 /*   340 */  1549, 1551, 1554, 1458, 1552, 1494, 1495, 1556, 1498, 1502,
 /*   350 */  1503, 1511, 1517, 1519, 1522, 1524, 1579, 1472, 1473, 1527,
 /*   360 */  1555, 1557, 1559, 1558, 1589, 1560, 1561, 1564, 1566, 1568,
 /*   370 */  1592, 1595, 1605, 1606, 1612, 1613, 1622, 1562, 1563, 1505,
 /*   380 */  1609, 1604, 1608, 1614, 1615, 1616, 1596, 1597, 1617, 1620,
 /*   390 */  1625, 1619,
};
static const YYACTIONTYPE yy_default[] = {
 /*     0 */  1575, 1575, 1575, 1411, 1188, 1297, 1188, 1188, 1188, 1411,
 /*    10 */  1411, 1411, 1188, 1327, 1327, 1464, 1219, 1188, 1188, 1188,
 /*    20 */  1188, 1188, 1188, 1188, 1188, 1188, 1188, 1410, 1188, 1188,
 /*    30 */  1188, 1188, 1494, 1494, 1188, 1188, 1188, 1188, 1188, 1188,
 /*    40 */  1188, 1188, 1188, 1336, 1188, 1188, 1188, 1188, 1188, 1188,
 /*    50 */  1412, 1413, 1188, 1188, 1188, 1463, 1465, 1428, 1346, 1345,
 /*    60 */  1344, 1343, 1446, 1314, 1341, 1334, 1338, 1406, 1407, 1405,
 /*    70 */  1409, 1413, 1412, 1188, 1337, 1377, 1391, 1376, 1188, 1188,
 /*    80 */  1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188,
 /*    90 */  1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188,
 /*   100 */  1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188,
 /*   110 */  1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188,
 /*   120 */  1188, 1188, 1188, 1188, 1188, 1188, 1385, 1390, 1396, 1389,
 /*   130 */  1386, 1379, 1378, 1380, 1381, 1188, 1209, 1261, 1188, 1188,
 /*   140 */  1188, 1188, 1482, 1481, 1188, 1188, 1219, 1371, 1370, 1382,
 /*   150 */  1383, 1393, 1392, 1471, 1529, 1528, 1429, 1188, 1188, 1188,
 /*   160 */  1188, 1188, 1188, 1494, 1188, 1188, 1188, 1188, 1188, 1188,
 /*   170 */  1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188,
 /*   180 */  1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1494, 1494,
 /*   190 */  1188, 1219, 1494, 1494, 1215, 1215, 1321, 1188, 1477, 1297,
 /*   200 */  1288, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188,
 /*   210 */  1188, 1188, 1188, 1188, 1188, 1468, 1466, 1188, 1188, 1188,
 /*   220 */  1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188,
 /*   230 */  1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188,
 /*   240 */  1188, 1188, 1188, 1188, 1188, 1293, 1188, 1188, 1188, 1188,
 /*   250 */  1188, 1188, 1188, 1188, 1188, 1188, 1188, 1523, 1188, 1441,
 /*   260 */  1275, 1293, 1293, 1293, 1293, 1295, 1276, 1274, 1287, 1220,
 /*   270 */  1195, 1567, 1294, 1316, 1316, 1564, 1340, 1294, 1564, 1236,
 /*   280 */  1545, 1231, 1327, 1327, 1327, 1316, 1321, 1321, 1408, 1294,
 /*   290 */  1287, 1188, 1567, 1302, 1302, 1566, 1566, 1302, 1429, 1349,
 /*   300 */  1355, 1340, 1264, 1340, 1270, 1270, 1270, 1270, 1302, 1206,
 /*   310 */  1340, 1340, 1349, 1355, 1264, 1340, 1264, 1340, 1302, 1206,
 /*   320 */  1445, 1561, 1302, 1206, 1419, 1302, 1206, 1302, 1206, 1419,
 /*   330 */  1262, 1262, 1262, 1251, 1188, 1188, 1419, 1262, 1236, 1262,
 /*   340 */  1251, 1262, 1262, 1512, 1419, 1423, 1423, 1419, 1320, 1315,
 /*   350 */  1320, 1315, 1320, 1315, 1320, 1315, 1302, 1504, 1504, 1330,
 /*   360 */  1330, 1335, 1321, 1414, 1302, 1188, 1335, 1333, 1331, 1340,
 /*   370 */  1212, 1254, 1526, 1526, 1522, 1522, 1522, 1572, 1572, 1477,
 /*   380 */  1538, 1219, 1219, 1219, 1219, 1538, 1238, 1238, 1220, 1220,
 /*   390 */  1219, 1538, 1188, 1188, 1188, 1188, 1188, 1188, 1533, 1188,
 /*   400 */  1430, 1306, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188,
 /*   410 */  1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188,
 /*   420 */  1188, 1188, 1188, 1188, 1188, 1360, 1188, 1191, 1474, 1188,
 /*   430 */  1188, 1472, 1188, 1188, 1188, 1188, 1188, 1188, 1307, 1188,
 /*   440 */  1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188,
 /*   450 */  1188, 1188, 1188, 1188, 1188, 1188, 1188, 1563, 1188, 1188,
 /*   460 */  1188, 1188, 1188, 1188, 1444, 1443, 1188, 1188, 1304, 1188,
 /*   470 */  1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188,
 /*   480 */  1188, 1188, 1234, 1188, 1188, 1188, 1188, 1188, 1188, 1188,
 /*   490 */  1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188,
 /*   500 */  1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1332,
 /*   510 */  1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188,
 /*   520 */  1188, 1188, 1188, 1188, 1509, 1322, 1188, 1188, 1554, 1188,
 /*   530 */  1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188, 1188,
 /*   540 */  1188, 1188, 1188, 1549, 1278, 1362, 1188, 1361, 1365, 1188,
 /*   550 */  1200, 1188, 1188,
};
/********** End of lemon-generated parsing tables *****************************/

/* The next table maps tokens (terminal symbols) into fallback tokens.
** If a construct like the following:
**
**      %fallback ID X Y Z.







|








|

|

|
|
|



|
|
|


|
|
|



|
|




|
|
|

|
|
|
|
|
|
|

|
|
|

|
|
|

|
|
|
|
|
|
|
|
|
|
|


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

















|

|

|
















|
|

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

|

|

















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


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







156842
156843
156844
156845
156846
156847
156848
156849
156850
156851
156852
156853
156854
156855
156856
156857
156858
156859
156860
156861
156862
156863
156864
156865
156866
156867
156868
156869
156870
156871
156872
156873
156874
156875
156876
156877
156878
156879
156880
156881
156882
156883
156884
156885
156886
156887
156888
156889
156890
156891
156892
156893
156894
156895
156896
156897
156898
156899
156900
156901
156902
156903
156904
156905
156906
156907
156908
156909
156910
156911
156912
156913
156914
156915
156916
156917
156918
156919
156920
156921
156922
156923
156924
156925
156926
156927
156928
156929
156930
156931
156932
156933
156934
156935
156936
156937
156938
156939
156940
156941
156942
156943
156944
156945
156946
156947
156948
156949
156950
156951
156952
156953
156954
156955

156956
156957
156958
156959
156960
156961
156962
156963
156964
156965
156966
156967
156968
156969
156970
156971
156972
156973
156974
156975
156976
156977
156978
156979
156980
156981
156982
156983
156984
156985
156986
156987
156988
156989
156990
156991
156992
156993
156994
156995
156996
156997
156998
156999
157000
157001
157002
157003
157004
157005
157006
157007
157008
157009
157010
157011
157012
157013
157014
157015
157016
157017
157018
157019
157020
157021
157022
157023
157024
157025
157026
157027
157028
157029
157030
157031
157032
157033
157034
157035
157036
157037
157038
157039
157040
157041
157042
157043
157044
157045
157046
157047
157048
157049
157050
157051
157052
157053
157054
157055
157056
157057
157058
157059
157060
157061
157062
157063
157064
157065
157066
157067
157068
157069
157070
157071
157072
157073
157074
157075
157076
157077
157078
157079
157080
157081
157082
157083
157084
157085
157086
157087
157088
157089
157090
157091
157092
157093
157094
157095
157096
157097
157098
157099
157100
157101
157102
157103
157104
157105
157106
157107
157108
157109
157110
157111
157112
157113
157114
157115
157116
157117
157118
157119
157120
157121
157122
157123
157124
157125
157126
157127
157128
157129
157130
157131
157132
157133
157134
157135
157136
157137
157138
157139
157140
157141
157142
157143
157144
 /*   830 */   110,  111,   46,  233,  189,  189,  291,  248,   99,  189,
 /*   840 */   125,  126,  127,  115,   26,  200,  289,  230,  231,  115,
 /*   850 */   200,   16,  189,  114,  115,  189,  211,  212,  119,  221,
 /*   860 */   189,  211,  212,  258,  101,  102,  103,  104,  105,  106,
 /*   870 */   107,  108,  109,  110,  111,  189,  156,  211,  212,  234,
 /*   880 */   235,  189,  211,  212,  234,  235,   22,  201,  189,  150,
 /*   890 */   151,  152,  247,  248,   76,   16,   19,  247,  248,  113,
 /*   900 */    19,   24,  257,  211,  212,  189,   26,   89,  262,  223,
 /*   910 */    92,  225,   77,  189,   79,  129,   19,   53,  226,  248,
 /*   920 */    43,   44,   45,   46,   47,   48,   49,   50,   51,   52,
 /*   930 */    53,   54,   55,   56,   57,  236,   19,  271,  189,   99,
 /*   940 */    43,   44,   45,   46,   47,   48,   49,   50,   51,   52,
 /*   950 */    53,   54,   55,   56,   57,  115,   77,   59,   79,  119,
 /*   960 */    43,   44,   45,   46,   47,   48,   49,   50,   51,   52,
 /*   970 */    53,   54,   55,   56,   57,  259,   22,   23,  101,  102,
 /*   980 */   103,  104,  105,  106,  107,  108,  109,  110,  111,   59,
 /*   990 */   150,  151,  152,  158,   22,  114,   24,  189,  101,  102,
 /*  1000 */   103,  104,  105,  106,  107,  108,  109,  110,  111,  285,
 /*  1010 */   189,  262,  114,  115,  116,  200,  136,  137,  101,  102,
 /*  1020 */   103,  104,  105,  106,  107,  108,  109,  110,  111,  230,
 /*  1030 */   231,   59,  211,  212,  143,  105,  106,  189,   19,  141,
 /*  1040 */   234,  235,   26,  113,  114,  115,  116,  226,  118,  234,
 /*  1050 */   235,  189,  161,  247,  100,  189,  126,   23,  189,  107,
 /*  1060 */    26,  189,  247,   44,   45,   46,   47,   48,   49,   50,
 /*  1070 */    51,   52,   53,   54,   55,   56,   57,  211,  212,   59,
 /*  1080 */   150,  233,  152,  211,  212,  133,   12,  115,  189,  189,
 /*  1090 */   138,   19,   20,  285,   22,  233,   76,  127,  226,   11,
 /*  1100 */   208,   27,   22,   23,  200,  236,   26,   87,   36,   89,
 /*  1110 */   211,  212,   92,  300,  248,  189,   42,  304,  189,  149,
 /*  1120 */   101,  102,  103,  104,  105,  106,  107,  108,  109,  110,
 /*  1130 */   111,   59,  200,  233,  114,  115,  116,   63,  234,  235,
 /*  1140 */   124,   19,   20,   71,   22,  300,   46,   73,  200,  304,
 /*  1150 */   116,  247,  244,   81,  246,  200,  227,  300,   36,  234,
 /*  1160 */   235,  304,   23,  143,  200,   26,  234,  235,  194,  200,
 /*  1170 */    48,   99,  247,   66,  189,  141,  284,  105,  106,  247,
 /*  1180 */   100,   59,  234,  235,  112,  259,  114,  115,  116,  234,
 /*  1190 */   235,  119,   85,   71,  266,  247,  211,  212,  234,  235,
 /*  1200 */   189,   94,  247,  234,  235,   12,  266,   85,  136,  137,
 /*  1210 */   189,  247,   90,  113,  126,  127,  247,  189,   26,   22,
 /*  1220 */    27,   99,  150,  151,  152,  153,  154,  105,  106,  189,
 /*  1230 */   302,  303,  211,  212,  112,   42,  114,  115,  116,  211,
 /*  1240 */   212,  119,  302,  303,   19,   20,  189,   22,  274,  189,
 /*  1250 */    15,  144,  278,  189,   22,   23,   63,  189,  189,  203,
 /*  1260 */   204,   36,  203,  204,  189,   24,  136,  137,  211,  212,
 /*  1270 */   189,  235,  150,  151,  152,  153,  154,    0,    1,    2,
 /*  1280 */   211,  212,    5,  247,   59,  292,  293,   10,   11,   12,
 /*  1290 */    13,   14,  211,  212,   17,   60,   71,  189,  258,  189,
 /*  1300 */    59,   59,  105,  106,  189,   26,  189,   30,  116,   32,
 /*  1310 */    85,  253,  189,  251,  252,   90,  189,   40,  258,  211,
 /*  1320 */   212,  211,  212,  127,   99,   26,  211,  212,  211,  212,
 /*  1330 */   105,  106,  100,  141,  211,  212,  239,  112,  189,  114,
 /*  1340 */   115,  116,   31,  189,  119,  149,  249,   70,  189,   26,
 /*  1350 */    39,   19,   20,   24,   22,   78,  115,  115,   81,  189,
 /*  1360 */   211,  212,  155,  189,  157,  211,  212,  189,   36,  189,
 /*  1370 */   211,  212,  189,  189,   97,  150,  151,  152,  153,  154,
 /*  1380 */   189,  211,  212,  189,  189,  211,  212,  189,  189,  211,
 /*  1390 */   212,   59,  189,  189,  211,  212,   23,  189,   26,   26,
 /*  1400 */    22,  189,   24,   71,   22,  211,  212,  189,  131,  211,
 /*  1410 */   212,  189,  189,  136,  137,  211,  212,   85,  189,  211,
 /*  1420 */   212,   59,   90,  211,  212,   23,  147,  189,   26,  211,
 /*  1430 */   212,   99,   23,  211,  212,   26,  159,  105,  106,  140,
 /*  1440 */   211,  212,   23,  189,  112,   26,  114,  115,  116,    1,
 /*  1450 */     2,  119,   23,    5,   23,   26,  189,   26,   10,   11,
 /*  1460 */    12,   13,   14,  118,  119,   17,  143,  189,  139,  189,
 /*  1470 */    19,   20,  189,   22,  189,  189,   23,  115,   30,   26,
 /*  1480 */    32,  189,  150,  151,  152,  153,  154,   36,   40,  211,
 /*  1490 */   212,  211,  212,  189,  211,  212,  211,  212,    7,    8,
 /*  1500 */    19,   20,  189,   22,  189,  189,   83,   84,  189,  189,
 /*  1510 */    59,  189,  140,  150,  189,  152,  189,   36,   70,  189,
 /*  1520 */    23,  139,   71,   26,  211,  212,   78,  211,  212,   81,
 /*  1530 */   189,  211,  212,  211,  212,  189,  211,  212,  211,  212,
 /*  1540 */    59,  211,  212,  119,   23,   97,   23,   26,   23,   26,
 /*  1550 */    99,   26,   71,  129,   59,  189,  105,  106,  107,  189,
 /*  1560 */   189,  309,  150,  112,  152,  114,  115,  116,  189,  189,
 /*  1570 */   119,   23,   19,   20,   26,   22,  189,  211,  212,  131,
 /*  1580 */    99,  189,  211,  212,  136,  137,  105,  106,  189,   36,
 /*  1590 */   211,  212,  250,  112,  189,  114,  115,  116,  211,  212,
 /*  1600 */   119,  150,  151,  152,  153,  154,  189,  159,   23,  189,
 /*  1610 */   115,   26,   59,  280,  189,  231,  189,  189,  281,  189,
 /*  1620 */   237,  189,  189,  189,   71,  189,  189,  189,  211,  212,
 /*  1630 */   209,  150,  151,  152,  153,  154,  211,  212,  250,  250,
 /*  1640 */   250,  211,  212,  211,  212,  250,  238,  187,  290,  214,
 /*  1650 */   240,  254,   99,  286,  254,  241,  241,  215,  105,  106,
 /*  1660 */   286,  220,  240,  224,  214,  112,  214,  114,  115,  116,
 /*  1670 */   254,  273,  119,    5,  192,   60,  290,  139,   10,   11,
 /*  1680 */    12,   13,   14,  238,  244,   17,  240,  244,  196,  196,
 /*  1690 */    38,  287,  196,  287,  148,  113,   22,  147,   30,  241,
 /*  1700 */    32,   43,  276,  150,  151,  152,  153,  154,   40,  265,
 /*  1710 */   241,  264,  229,   18,  196,   18,  232,  232,  232,  232,
 /*  1720 */   241,  195,  265,  229,  265,  196,  265,  264,  229,  241,
 /*  1730 */   241,  241,  195,  155,   62,  196,  195,  283,   70,   22,
 /*  1740 */   216,  196,  195,  216,  282,  196,   78,  195,  113,   81,
 /*  1750 */    64,   22,  222,  213,  219,  213,  213,  124,  222,  162,
 /*  1760 */   111,  142,  256,  219,  113,   97,  255,  216,  213,  215,
 /*  1770 */   213,  213,  213,  303,  255,  275,  216,  275,  216,  196,
 /*  1780 */    91,  308,  308,   82,  256,  146,  143,  256,  255,   22,
 /*  1790 */   256,  196,  260,  155,  272,  145,   25,  144,  199,  131,
 /*  1800 */    26,  198,   13,  190,  136,  137,  190,  241,  245,  244,
 /*  1810 */   242,  255,  245,  243,    6,  188,  188,  188,  208,  202,
 /*  1820 */   208,  260,  208,  202,  217,  217,  208,  159,  202,  209,
 /*  1830 */     4,  209,  208,    3,  270,   22,  160,   15,   98,   16,
 /*  1840 */    23,   23,  137,  128,  148,   24,  140,   20,   16,  142,
 /*  1850 */     1,  140,  128,  128,   61,   53,   37,  148,   53,   53,
 /*  1860 */    53,  128,  114,   34,    1,  296,  293,  296,   22,  139,
 /*  1870 */     5,  113,  158,   26,   75,   41,  139,   68,   68,  113,
 /*  1880 */    24,   20,  129,   19,  123,   23,   96,   67,   22,   22,
 /*  1890 */    22,   22,   67,  147,   59,   24,   22,   37,   28,   23,
 /*  1900 */    67,   22,   24,   23,   23,   23,  114,   23,   22,   26,
 /*  1910 */    23,   22,   24,   23,  139,   23,   22,  141,   75,   34,
 /*  1920 */    75,   26,   22,   86,   88,   34,   23,   34,   24,   23,
 /*  1930 */    34,   34,   93,   34,   26,   26,   34,   23,   23,   23,
 /*  1940 */    23,   22,   44,   23,   26,   11,   22,   22,   26,   23,
 /*  1950 */    23,   22,   22,  139,  139,  139,   23,  133,   15,    1,
 /*  1960 */     1,  310,  310,  310,  310,  310,  310,  139,  310,  310,

 /*  1970 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  1980 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  1990 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2000 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2010 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2020 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2030 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2040 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2050 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2060 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2070 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2080 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2090 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2100 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2110 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2120 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2130 */   310,  310,  310,  310,  310,  310,  310,  310,  310,  310,
 /*  2140 */   310,  310,  310,  310,  310,  310,  310,  310,  310,
};
#define YY_SHIFT_COUNT    (557)
#define YY_SHIFT_MIN      (0)
#define YY_SHIFT_MAX      (1959)
static const unsigned short int yy_shift_ofst[] = {
 /*     0 */  1448, 1277, 1668, 1072, 1072,  340, 1122, 1225, 1332, 1481,
 /*    10 */  1481, 1481,  335,    0,    0,  180,  897, 1481, 1481, 1481,
 /*    20 */  1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481,
 /*    30 */   930,  930, 1020, 1020,  290,    1,  340,  340,  340,  340,
 /*    40 */   340,  340,   40,  110,  219,  288,  327,  396,  435,  504,
 /*    50 */   543,  612,  651,  720,  877,  897,  897,  897,  897,  897,
 /*    60 */   897,  897,  897,  897,  897,  897,  897,  897,  897,  897,
 /*    70 */   897,  897,  897,  917,  897, 1019,  763,  763, 1451, 1481,
 /*    80 */  1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481,
 /*    90 */  1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481,
 /*   100 */  1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481, 1481,
 /*   110 */  1481, 1481, 1553, 1481, 1481, 1481, 1481, 1481, 1481, 1481,
 /*   120 */  1481, 1481, 1481, 1481, 1481, 1481,  147,  258,  258,  258,
 /*   130 */   258,  258,   79,   65,   84,  449,   19,  786,  449,  636,
 /*   140 */   636,  449,  880,  880,  880,  880,  113,  142,  142,  472,
 /*   150 */   150, 1968, 1968,  399,  399,  399,   93,  237,  341,  237,
 /*   160 */   237,  237, 1074, 1074,  437,  350,  704, 1080,  449,  449,
 /*   170 */   449,  449,  449,  449,  449,  449,  449,  449,  449,  449,
 /*   180 */   449,  449,  449,  449,  449,  449,  449,  449,  449,  818,
 /*   190 */   818,  449, 1088,  217,  217,  734,  734,  891, 1130, 1968,
 /*   200 */  1968, 1968,  739,  840,  840,  453,  454,  511,  187,  563,
 /*   210 */   570,  898,  669,  449,  449,  449,  449,  449,  449,  449,
 /*   220 */   449,  449,  670,  449,  449,  449,  449,  449,  449,  449,
 /*   230 */   449,  449,  449,  449,  449,  674,  674,  674,  449,  449,
 /*   240 */   449,  449, 1034,  449,  449,  449,  972, 1107,  449,  449,
 /*   250 */  1193,  449,  449,  449,  449,  449,  449,  449,  449,  260,
 /*   260 */   177,  489, 1241, 1241, 1241, 1241, 1192,  489,  489,  952,
 /*   270 */  1197,  625, 1235, 1299,  181,  181,  881, 1279, 1279, 1299,
 /*   280 */   881, 1016, 1139, 1100, 1311, 1311, 1311,  181, 1323, 1323,
 /*   290 */  1207, 1372,  549, 1378, 1615, 1538, 1538, 1652, 1652, 1538,
 /*   300 */  1546, 1582, 1674, 1550, 1658, 1550, 1695, 1695, 1695, 1695,
 /*   310 */  1538, 1697, 1550, 1582, 1582, 1550, 1582, 1674, 1658, 1550,
 /*   320 */  1658, 1550, 1538, 1697, 1578, 1672, 1538, 1697, 1717, 1538,
 /*   330 */  1697, 1538, 1697, 1717, 1635, 1635, 1635, 1686, 1729, 1729,
 /*   340 */  1717, 1635, 1633, 1635, 1686, 1635, 1635, 1597, 1717, 1649,
 /*   350 */  1649, 1717, 1619, 1651, 1619, 1651, 1619, 1651, 1619, 1651,
 /*   360 */  1538, 1689, 1689, 1701, 1701, 1639, 1643, 1767, 1538, 1638,
 /*   370 */  1639, 1650, 1653, 1550, 1771, 1774, 1789, 1789, 1808, 1808,
 /*   380 */  1808, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968, 1968,
 /*   390 */  1968, 1968, 1968, 1968, 1968, 1968,  308,  835,  954, 1232,
 /*   400 */   879,  715,  728, 1373,  864, 1329,  970, 1196, 1402,  297,
 /*   410 */  1409, 1419, 1429, 1431, 1453, 1497, 1242, 1345, 1491, 1424,
 /*   420 */  1362, 1521, 1523, 1423, 1525, 1363, 1412, 1548, 1585, 1495,
 /*   430 */  1382, 1826, 1830, 1813, 1676, 1822, 1740, 1823, 1817, 1818,
 /*   440 */  1705, 1696, 1715, 1821, 1706, 1827, 1707, 1832, 1849, 1711,
 /*   450 */  1724, 1725, 1793, 1819, 1709, 1802, 1805, 1806, 1807, 1733,
 /*   460 */  1748, 1829, 1730, 1863, 1865, 1846, 1758, 1714, 1809, 1847,
 /*   470 */  1810, 1799, 1834, 1737, 1766, 1856, 1861, 1864, 1753, 1761,
 /*   480 */  1866, 1820, 1867, 1868, 1862, 1869, 1825, 1835, 1871, 1790,
 /*   490 */  1870, 1874, 1833, 1860, 1876, 1746, 1879, 1880, 1881, 1882,
 /*   500 */  1883, 1884, 1886, 1878, 1887, 1889, 1888, 1775, 1890, 1892,
 /*   510 */  1792, 1885, 1894, 1776, 1895, 1891, 1893, 1896, 1897, 1836,
 /*   520 */  1843, 1837, 1898, 1845, 1839, 1899, 1903, 1900, 1904, 1908,
 /*   530 */  1909, 1902, 1906, 1895, 1914, 1915, 1916, 1917, 1918, 1920,
 /*   540 */  1919, 1934, 1924, 1925, 1926, 1927, 1929, 1930, 1922, 1824,
 /*   550 */  1814, 1815, 1816, 1828, 1933, 1943, 1958, 1959,
};
#define YY_REDUCE_COUNT (395)
#define YY_REDUCE_MIN   (-262)
#define YY_REDUCE_MAX   (1629)
static const short yy_reduce_ofst[] = {
 /*     0 */   490, -122,  545,  645,  650, -120, -189, -187, -184, -182,
 /*    10 */  -178, -176,   45,   30,  200, -251, -134,  390,  392,  521,
 /*    20 */   523,  213,  692,  821,  284,  589,  872,  666,  671,  866,
 /*    30 */    71,  111,  273,  389,  686,  815,  904,  932,  948,  955,
 /*    40 */   964,  969, -259, -259, -259, -259, -259, -259, -259, -259,
 /*    50 */  -259, -259, -259, -259, -259, -259, -259, -259, -259, -259,
 /*    60 */  -259, -259, -259, -259, -259, -259, -259, -259, -259, -259,
 /*    70 */  -259, -259, -259, -259, -259, -259, -259, -259,  428,  430,
 /*    80 */   899,  985, 1021, 1028, 1057, 1069, 1081, 1108, 1110, 1115,
 /*    90 */  1117, 1123, 1149, 1154, 1159, 1170, 1174, 1178, 1183, 1194,
 /*   100 */  1198, 1204, 1208, 1212, 1218, 1222, 1229, 1278, 1280, 1283,
 /*   110 */  1285, 1313, 1316, 1320, 1322, 1325, 1327, 1330, 1366, 1371,
 /*   120 */  1379, 1387, 1417, 1425, 1430, 1432, -259, -259, -259, -259,
 /*   130 */  -259, -259, -259, -259, -259,  557,  974, -214, -174,   -9,
 /*   140 */   431, -124,  806,  925,  806,  925,  251,  928,  940, -259,
 /*   150 */  -259, -259, -259, -198, -198, -198,  127, -186, -168,  212,
 /*   160 */   646,  749,  617,  799, -262,  555,  220,  220,  491,  605,
 /*   170 */  1040, 1060,  699,  -11,  600,  848,  862,  345, -129,  724,
 /*   180 */   -91,  158,  808,  716,  900,  304,  869,  929,  926,  499,
 /*   190 */   813,  322,  892,  845,  857, 1056, 1059,  908, 1036,  993,
 /*   200 */  1062, 1097, -210, -185, -179, -148, -167,  -89,  121,  274,
 /*   210 */   281,  320,  336,  439,  663, 1011, 1064, 1068, 1075, 1127,
 /*   220 */  1180, 1184, -196, 1191, 1195, 1199, 1203, 1223, 1238, 1254,
 /*   230 */  1267, 1286, 1292, 1304, 1315,  205,  422,  638, 1319, 1341,
 /*   240 */  1346, 1370, 1058, 1380, 1392, 1399, 1342, 1252, 1405, 1420,
 /*   250 */  1384, 1427,  121, 1428, 1433, 1434, 1436, 1437, 1438, 1337,
 /*   260 */  1333, 1383, 1388, 1389, 1390, 1395, 1058, 1383, 1383, 1408,
 /*   270 */  1421, 1460, 1358, 1410, 1397, 1400, 1367, 1414, 1415, 1422,
 /*   280 */  1374, 1442, 1439, 1441, 1435, 1450, 1452, 1416, 1440, 1443,
 /*   290 */  1398, 1446, 1445, 1482, 1386, 1492, 1493, 1404, 1406, 1496,
 /*   300 */  1426, 1444, 1447, 1458, 1483, 1469, 1484, 1485, 1486, 1487,
 /*   310 */  1518, 1526, 1479, 1457, 1459, 1488, 1461, 1463, 1494, 1489,
 /*   320 */  1499, 1490, 1529, 1537, 1454, 1462, 1539, 1541, 1524, 1545,
 /*   330 */  1547, 1549, 1552, 1527, 1540, 1542, 1543, 1530, 1535, 1544,
 /*   340 */  1551, 1555, 1554, 1557, 1536, 1558, 1559, 1470, 1560, 1500,
 /*   350 */  1502, 1562, 1506, 1511, 1528, 1519, 1531, 1533, 1534, 1556,
 /*   360 */  1583, 1473, 1474, 1532, 1561, 1563, 1565, 1564, 1595, 1522,
 /*   370 */  1567, 1570, 1568, 1566, 1599, 1603, 1613, 1616, 1627, 1628,
 /*   380 */  1629, 1569, 1571, 1573, 1617, 1610, 1612, 1614, 1618, 1621,
 /*   390 */  1607, 1608, 1620, 1622, 1624, 1626,
};
static const YYACTIONTYPE yy_default[] = {
 /*     0 */  1583, 1583, 1583, 1419, 1195, 1304, 1195, 1195, 1195, 1419,
 /*    10 */  1419, 1419, 1195, 1334, 1334, 1472, 1226, 1195, 1195, 1195,
 /*    20 */  1195, 1195, 1195, 1195, 1195, 1195, 1195, 1418, 1195, 1195,
 /*    30 */  1195, 1195, 1502, 1502, 1195, 1195, 1195, 1195, 1195, 1195,
 /*    40 */  1195, 1195, 1195, 1343, 1195, 1195, 1195, 1195, 1195, 1195,
 /*    50 */  1420, 1421, 1195, 1195, 1195, 1471, 1473, 1436, 1353, 1352,
 /*    60 */  1351, 1350, 1454, 1321, 1348, 1341, 1345, 1414, 1415, 1413,
 /*    70 */  1417, 1421, 1420, 1195, 1344, 1385, 1399, 1384, 1195, 1195,
 /*    80 */  1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
 /*    90 */  1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
 /*   100 */  1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
 /*   110 */  1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
 /*   120 */  1195, 1195, 1195, 1195, 1195, 1195, 1393, 1398, 1404, 1397,
 /*   130 */  1394, 1387, 1386, 1388, 1389, 1195, 1216, 1268, 1195, 1195,
 /*   140 */  1195, 1195, 1490, 1489, 1195, 1195, 1226, 1379, 1378, 1390,
 /*   150 */  1391, 1401, 1400, 1479, 1537, 1536, 1437, 1195, 1195, 1195,
 /*   160 */  1195, 1195, 1195, 1195, 1502, 1195, 1195, 1195, 1195, 1195,
 /*   170 */  1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
 /*   180 */  1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1502,
 /*   190 */  1502, 1195, 1226, 1502, 1502, 1222, 1222, 1328, 1195, 1485,
 /*   200 */  1304, 1295, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
 /*   210 */  1195, 1195, 1195, 1195, 1195, 1195, 1476, 1474, 1195, 1195,
 /*   220 */  1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
 /*   230 */  1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
 /*   240 */  1195, 1195, 1195, 1195, 1195, 1195, 1300, 1195, 1195, 1195,
 /*   250 */  1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1531, 1195,
 /*   260 */  1449, 1282, 1300, 1300, 1300, 1300, 1302, 1283, 1281, 1294,
 /*   270 */  1227, 1202, 1575, 1301, 1323, 1323, 1572, 1347, 1347, 1301,
 /*   280 */  1572, 1243, 1553, 1238, 1334, 1334, 1334, 1323, 1328, 1328,
 /*   290 */  1416, 1301, 1294, 1195, 1575, 1309, 1309, 1574, 1574, 1309,
 /*   300 */  1437, 1356, 1363, 1347, 1271, 1347, 1277, 1277, 1277, 1277,
 /*   310 */  1309, 1213, 1347, 1356, 1356, 1347, 1356, 1363, 1271, 1347,
 /*   320 */  1271, 1347, 1309, 1213, 1453, 1569, 1309, 1213, 1427, 1309,
 /*   330 */  1213, 1309, 1213, 1427, 1269, 1269, 1269, 1258, 1195, 1195,
 /*   340 */  1427, 1269, 1243, 1269, 1258, 1269, 1269, 1520, 1427, 1431,
 /*   350 */  1431, 1427, 1327, 1322, 1327, 1322, 1327, 1322, 1327, 1322,
 /*   360 */  1309, 1512, 1512, 1337, 1337, 1342, 1328, 1422, 1309, 1195,
 /*   370 */  1342, 1340, 1338, 1347, 1219, 1261, 1534, 1534, 1530, 1530,
 /*   380 */  1530, 1580, 1580, 1485, 1546, 1226, 1226, 1226, 1226, 1546,
 /*   390 */  1245, 1245, 1227, 1227, 1226, 1546, 1195, 1195, 1195, 1195,
 /*   400 */  1195, 1195, 1541, 1195, 1438, 1313, 1195, 1195, 1195, 1195,
 /*   410 */  1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
 /*   420 */  1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
 /*   430 */  1368, 1195, 1198, 1482, 1195, 1195, 1480, 1195, 1195, 1195,
 /*   440 */  1195, 1195, 1195, 1314, 1195, 1195, 1195, 1195, 1195, 1195,
 /*   450 */  1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
 /*   460 */  1195, 1195, 1571, 1195, 1195, 1195, 1195, 1195, 1195, 1452,
 /*   470 */  1451, 1195, 1195, 1311, 1195, 1195, 1195, 1195, 1195, 1195,
 /*   480 */  1195, 1195, 1195, 1195, 1195, 1195, 1195, 1241, 1195, 1195,
 /*   490 */  1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
 /*   500 */  1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195,
 /*   510 */  1195, 1195, 1195, 1195, 1339, 1195, 1195, 1195, 1195, 1195,
 /*   520 */  1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1517,
 /*   530 */  1329, 1195, 1195, 1562, 1195, 1195, 1195, 1195, 1195, 1195,
 /*   540 */  1195, 1195, 1195, 1195, 1195, 1195, 1195, 1195, 1557, 1285,
 /*   550 */  1370, 1195, 1369, 1373, 1195, 1207, 1195, 1195,
};
/********** End of lemon-generated parsing tables *****************************/

/* The next table maps tokens (terminal symbols) into fallback tokens.
** If a construct like the following:
**
**      %fallback ID X Y Z.
156739
156740
156741
156742
156743
156744
156745
156746
156747
156748
156749
156750
156751
156752
156753
156754
156755
156756
156757
156758
156759
156760
156761
156762

156763
156764
156765
156766
156767
156768
156769
156770
156771
156772
156773
156774
156775
156776
156777
156778
156779
156780
156781
156782
156783
156784
156785
156786
156787
156788
156789

156790
156791
156792
156793
156794
156795
156796
156797
156798
156799
156800
156801
156802
156803
156804
156805
156806

156807
156808
156809
156810
156811
156812
156813
156814
156815
156816
156817
156818
156819
156820
156821
156822
156823
156824
156825
156826
156827
156828
156829
156830

156831
156832
156833
156834
156835
156836
156837
156838
156839
156840

156841
156842
156843
156844
156845
156846
156847
156848
156849
156850
156851
156852
156853
156854
156855
156856
156857
156858
156859
156860
156861
156862
156863
156864
156865

156866
156867
156868
156869
156870
156871
156872
156873
156874
156875
156876
156877
156878
156879
156880
156881
156882
156883
156884
156885
156886
156887
156888
156889

156890
156891
156892
156893
156894
156895
156896
156897
156898
156899
156900
156901
156902
156903
156904
156905
156906
156907
156908
156909
156910
156911
156912
156913
156914
156915
156916
156917
156918
156919
156920

156921
156922
156923
156924
156925
156926
156927
156928
156929
156930
156931
156932
156933
156934
156935
156936
156937
156938
156939
156940
156941
156942
156943
156944
156945
156946
156947
156948
156949
156950
156951
156952
156953
156954
156955

156956
156957
156958
156959
156960
156961
156962
156963
156964
156965
156966
156967
156968
156969
156970
156971
156972
156973
156974
156975
156976
156977
156978
 /* 152 */ "setlist ::= setlist COMMA nm EQ expr",
 /* 153 */ "setlist ::= setlist COMMA LP idlist RP EQ expr",
 /* 154 */ "setlist ::= nm EQ expr",
 /* 155 */ "setlist ::= LP idlist RP EQ expr",
 /* 156 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert",
 /* 157 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES",
 /* 158 */ "upsert ::=",
 /* 159 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt",
 /* 160 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING",
 /* 161 */ "upsert ::= ON CONFLICT DO NOTHING",
 /* 162 */ "insert_cmd ::= INSERT orconf",
 /* 163 */ "insert_cmd ::= REPLACE",
 /* 164 */ "idlist_opt ::=",
 /* 165 */ "idlist_opt ::= LP idlist RP",
 /* 166 */ "idlist ::= idlist COMMA nm",
 /* 167 */ "idlist ::= nm",
 /* 168 */ "expr ::= LP expr RP",
 /* 169 */ "expr ::= ID|INDEXED",
 /* 170 */ "expr ::= JOIN_KW",
 /* 171 */ "expr ::= nm DOT nm",
 /* 172 */ "expr ::= nm DOT nm DOT nm",
 /* 173 */ "term ::= NULL|FLOAT|BLOB",
 /* 174 */ "term ::= STRING",
 /* 175 */ "term ::= INTEGER",

 /* 176 */ "expr ::= VARIABLE",
 /* 177 */ "expr ::= expr COLLATE ID|STRING",
 /* 178 */ "expr ::= CAST LP expr AS typetoken RP",
 /* 179 */ "expr ::= ID|INDEXED LP distinct exprlist RP",
 /* 180 */ "expr ::= ID|INDEXED LP STAR RP",
 /* 181 */ "expr ::= ID|INDEXED LP distinct exprlist RP filter_over",
 /* 182 */ "expr ::= ID|INDEXED LP STAR RP filter_over",
 /* 183 */ "term ::= CTIME_KW",
 /* 184 */ "expr ::= LP nexprlist COMMA expr RP",
 /* 185 */ "expr ::= expr AND expr",
 /* 186 */ "expr ::= expr OR expr",
 /* 187 */ "expr ::= expr LT|GT|GE|LE expr",
 /* 188 */ "expr ::= expr EQ|NE expr",
 /* 189 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr",
 /* 190 */ "expr ::= expr PLUS|MINUS expr",
 /* 191 */ "expr ::= expr STAR|SLASH|REM expr",
 /* 192 */ "expr ::= expr CONCAT expr",
 /* 193 */ "likeop ::= NOT LIKE_KW|MATCH",
 /* 194 */ "expr ::= expr likeop expr",
 /* 195 */ "expr ::= expr likeop expr ESCAPE expr",
 /* 196 */ "expr ::= expr ISNULL|NOTNULL",
 /* 197 */ "expr ::= expr NOT NULL",
 /* 198 */ "expr ::= expr IS expr",
 /* 199 */ "expr ::= expr IS NOT expr",
 /* 200 */ "expr ::= NOT expr",
 /* 201 */ "expr ::= BITNOT expr",
 /* 202 */ "expr ::= PLUS|MINUS expr",

 /* 203 */ "between_op ::= BETWEEN",
 /* 204 */ "between_op ::= NOT BETWEEN",
 /* 205 */ "expr ::= expr between_op expr AND expr",
 /* 206 */ "in_op ::= IN",
 /* 207 */ "in_op ::= NOT IN",
 /* 208 */ "expr ::= expr in_op LP exprlist RP",
 /* 209 */ "expr ::= LP select RP",
 /* 210 */ "expr ::= expr in_op LP select RP",
 /* 211 */ "expr ::= expr in_op nm dbnm paren_exprlist",
 /* 212 */ "expr ::= EXISTS LP select RP",
 /* 213 */ "expr ::= CASE case_operand case_exprlist case_else END",
 /* 214 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
 /* 215 */ "case_exprlist ::= WHEN expr THEN expr",
 /* 216 */ "case_else ::= ELSE expr",
 /* 217 */ "case_else ::=",
 /* 218 */ "case_operand ::= expr",
 /* 219 */ "case_operand ::=",

 /* 220 */ "exprlist ::=",
 /* 221 */ "nexprlist ::= nexprlist COMMA expr",
 /* 222 */ "nexprlist ::= expr",
 /* 223 */ "paren_exprlist ::=",
 /* 224 */ "paren_exprlist ::= LP exprlist RP",
 /* 225 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt",
 /* 226 */ "uniqueflag ::= UNIQUE",
 /* 227 */ "uniqueflag ::=",
 /* 228 */ "eidlist_opt ::=",
 /* 229 */ "eidlist_opt ::= LP eidlist RP",
 /* 230 */ "eidlist ::= eidlist COMMA nm collate sortorder",
 /* 231 */ "eidlist ::= nm collate sortorder",
 /* 232 */ "collate ::=",
 /* 233 */ "collate ::= COLLATE ID|STRING",
 /* 234 */ "cmd ::= DROP INDEX ifexists fullname",
 /* 235 */ "cmd ::= VACUUM vinto",
 /* 236 */ "cmd ::= VACUUM nm vinto",
 /* 237 */ "vinto ::= INTO expr",
 /* 238 */ "vinto ::=",
 /* 239 */ "cmd ::= PRAGMA nm dbnm",
 /* 240 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
 /* 241 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
 /* 242 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
 /* 243 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP",

 /* 244 */ "plus_num ::= PLUS INTEGER|FLOAT",
 /* 245 */ "minus_num ::= MINUS INTEGER|FLOAT",
 /* 246 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END",
 /* 247 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",
 /* 248 */ "trigger_time ::= BEFORE|AFTER",
 /* 249 */ "trigger_time ::= INSTEAD OF",
 /* 250 */ "trigger_time ::=",
 /* 251 */ "trigger_event ::= DELETE|INSERT",
 /* 252 */ "trigger_event ::= UPDATE",
 /* 253 */ "trigger_event ::= UPDATE OF idlist",

 /* 254 */ "when_clause ::=",
 /* 255 */ "when_clause ::= WHEN expr",
 /* 256 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
 /* 257 */ "trigger_cmd_list ::= trigger_cmd SEMI",
 /* 258 */ "trnm ::= nm DOT nm",
 /* 259 */ "tridxby ::= INDEXED BY nm",
 /* 260 */ "tridxby ::= NOT INDEXED",
 /* 261 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt",
 /* 262 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt",
 /* 263 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt",
 /* 264 */ "trigger_cmd ::= scanpt select scanpt",
 /* 265 */ "expr ::= RAISE LP IGNORE RP",
 /* 266 */ "expr ::= RAISE LP raisetype COMMA nm RP",
 /* 267 */ "raisetype ::= ROLLBACK",
 /* 268 */ "raisetype ::= ABORT",
 /* 269 */ "raisetype ::= FAIL",
 /* 270 */ "cmd ::= DROP TRIGGER ifexists fullname",
 /* 271 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
 /* 272 */ "cmd ::= DETACH database_kw_opt expr",
 /* 273 */ "key_opt ::=",
 /* 274 */ "key_opt ::= KEY expr",
 /* 275 */ "cmd ::= REINDEX",
 /* 276 */ "cmd ::= REINDEX nm dbnm",
 /* 277 */ "cmd ::= ANALYZE",
 /* 278 */ "cmd ::= ANALYZE nm dbnm",

 /* 279 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
 /* 280 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist",
 /* 281 */ "add_column_fullname ::= fullname",
 /* 282 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm",
 /* 283 */ "cmd ::= create_vtab",
 /* 284 */ "cmd ::= create_vtab LP vtabarglist RP",
 /* 285 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm",
 /* 286 */ "vtabarg ::=",
 /* 287 */ "vtabargtoken ::= ANY",
 /* 288 */ "vtabargtoken ::= lp anylist RP",
 /* 289 */ "lp ::= LP",
 /* 290 */ "with ::= WITH wqlist",
 /* 291 */ "with ::= WITH RECURSIVE wqlist",
 /* 292 */ "wqlist ::= nm eidlist_opt AS LP select RP",
 /* 293 */ "wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP",
 /* 294 */ "windowdefn_list ::= windowdefn",
 /* 295 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn",
 /* 296 */ "windowdefn ::= nm AS LP window RP",
 /* 297 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt",
 /* 298 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt",
 /* 299 */ "window ::= ORDER BY sortlist frame_opt",
 /* 300 */ "window ::= nm ORDER BY sortlist frame_opt",
 /* 301 */ "window ::= frame_opt",
 /* 302 */ "window ::= nm frame_opt",

 /* 303 */ "frame_opt ::=",
 /* 304 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt",
 /* 305 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt",
 /* 306 */ "range_or_rows ::= RANGE|ROWS|GROUPS",
 /* 307 */ "frame_bound_s ::= frame_bound",
 /* 308 */ "frame_bound_s ::= UNBOUNDED PRECEDING",
 /* 309 */ "frame_bound_e ::= frame_bound",
 /* 310 */ "frame_bound_e ::= UNBOUNDED FOLLOWING",
 /* 311 */ "frame_bound ::= expr PRECEDING|FOLLOWING",
 /* 312 */ "frame_bound ::= CURRENT ROW",
 /* 313 */ "frame_exclude_opt ::=",
 /* 314 */ "frame_exclude_opt ::= EXCLUDE frame_exclude",
 /* 315 */ "frame_exclude ::= NO OTHERS",
 /* 316 */ "frame_exclude ::= CURRENT ROW",
 /* 317 */ "frame_exclude ::= GROUP|TIES",
 /* 318 */ "window_clause ::= WINDOW windowdefn_list",
 /* 319 */ "filter_over ::= filter_clause over_clause",
 /* 320 */ "filter_over ::= over_clause",
 /* 321 */ "filter_over ::= filter_clause",
 /* 322 */ "over_clause ::= OVER LP window RP",
 /* 323 */ "over_clause ::= OVER nm",
 /* 324 */ "filter_clause ::= FILTER LP WHERE expr RP",
 /* 325 */ "input ::= cmdlist",
 /* 326 */ "cmdlist ::= cmdlist ecmd",
 /* 327 */ "cmdlist ::= ecmd",
 /* 328 */ "ecmd ::= SEMI",
 /* 329 */ "ecmd ::= cmdx SEMI",
 /* 330 */ "ecmd ::= explain cmdx SEMI",
 /* 331 */ "trans_opt ::=",
 /* 332 */ "trans_opt ::= TRANSACTION",
 /* 333 */ "trans_opt ::= TRANSACTION nm",

 /* 334 */ "savepoint_opt ::= SAVEPOINT",
 /* 335 */ "savepoint_opt ::=",
 /* 336 */ "cmd ::= create_table create_table_args",
 /* 337 */ "columnlist ::= columnlist COMMA columnname carglist",
 /* 338 */ "columnlist ::= columnname carglist",
 /* 339 */ "nm ::= ID|INDEXED",
 /* 340 */ "nm ::= STRING",
 /* 341 */ "nm ::= JOIN_KW",
 /* 342 */ "typetoken ::= typename",
 /* 343 */ "typename ::= ID|STRING",
 /* 344 */ "signed ::= plus_num",
 /* 345 */ "signed ::= minus_num",
 /* 346 */ "carglist ::= carglist ccons",
 /* 347 */ "carglist ::=",
 /* 348 */ "ccons ::= NULL onconf",
 /* 349 */ "ccons ::= GENERATED ALWAYS AS generated",
 /* 350 */ "ccons ::= AS generated",
 /* 351 */ "conslist_opt ::= COMMA conslist",
 /* 352 */ "conslist ::= conslist tconscomma tcons",
 /* 353 */ "conslist ::= tcons",
 /* 354 */ "tconscomma ::=",
 /* 355 */ "defer_subclause_opt ::= defer_subclause",
 /* 356 */ "resolvetype ::= raisetype",
 /* 357 */ "selectnowith ::= oneselect",
 /* 358 */ "oneselect ::= values",
 /* 359 */ "sclp ::= selcollist COMMA",
 /* 360 */ "as ::= ID|STRING",
 /* 361 */ "expr ::= term",
 /* 362 */ "likeop ::= LIKE_KW|MATCH",
 /* 363 */ "exprlist ::= nexprlist",
 /* 364 */ "nmnum ::= plus_num",
 /* 365 */ "nmnum ::= nm",
 /* 366 */ "nmnum ::= ON",
 /* 367 */ "nmnum ::= DELETE",
 /* 368 */ "nmnum ::= DEFAULT",

 /* 369 */ "plus_num ::= INTEGER|FLOAT",
 /* 370 */ "foreach_clause ::=",
 /* 371 */ "foreach_clause ::= FOR EACH ROW",
 /* 372 */ "trnm ::= nm",
 /* 373 */ "tridxby ::=",
 /* 374 */ "database_kw_opt ::= DATABASE",
 /* 375 */ "database_kw_opt ::=",
 /* 376 */ "kwcolumn_opt ::=",
 /* 377 */ "kwcolumn_opt ::= COLUMNKW",
 /* 378 */ "vtabarglist ::= vtabarg",
 /* 379 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
 /* 380 */ "vtabarg ::= vtabarg vtabargtoken",
 /* 381 */ "anylist ::=",
 /* 382 */ "anylist ::= anylist LP anylist RP",
 /* 383 */ "anylist ::= anylist ANY",
 /* 384 */ "with ::=",
};
#endif /* NDEBUG */


#if YYSTACKDEPTH<=0
/*
** Try to increase the size of the parser stack.  Return the number







|
|

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







157895
157896
157897
157898
157899
157900
157901
157902
157903
157904
157905
157906
157907
157908
157909
157910
157911
157912
157913
157914
157915
157916
157917
157918
157919
157920
157921
157922
157923
157924
157925
157926
157927
157928
157929
157930
157931
157932
157933
157934
157935
157936
157937

157938
157939
157940
157941
157942
157943
157944
157945
157946
157947
157948
157949
157950
157951
157952
157953
157954
157955
157956
157957
157958
157959

157960
157961
157962
157963
157964
157965
157966
157967
157968
157969
157970
157971
157972
157973
157974
157975
157976
157977
157978
157979
157980

157981
157982
157983
157984
157985
157986
157987
157988
157989
157990
157991

157992
157993
157994
157995
157996
157997
157998
157999
158000
158001
158002
158003
158004
158005
158006
158007
158008
158009
158010
158011
158012
158013
158014
158015
158016

158017
158018
158019
158020
158021
158022
158023
158024
158025
158026
158027
158028
158029
158030
158031
158032
158033
158034
158035
158036
158037
158038
158039
158040
158041
158042
158043
158044

158045
158046
158047
158048
158049
158050
158051
158052
158053
158054
158055
158056
158057
158058
158059
158060
158061
158062
158063
158064
158065
158066
158067
158068
158069
158070
158071

158072
158073
158074
158075
158076
158077
158078
158079
158080
158081
158082
158083
158084
158085
158086
158087
158088
158089
158090
158091
158092
158093
158094
158095
158096
158097
158098
158099
158100
158101
158102
158103
158104
158105
158106
158107

158108
158109
158110
158111
158112
158113
158114
158115
158116
158117
158118
158119
158120
158121
158122
158123
158124
158125
158126
158127
158128
158129
158130
158131
158132
158133
158134
158135
 /* 152 */ "setlist ::= setlist COMMA nm EQ expr",
 /* 153 */ "setlist ::= setlist COMMA LP idlist RP EQ expr",
 /* 154 */ "setlist ::= nm EQ expr",
 /* 155 */ "setlist ::= LP idlist RP EQ expr",
 /* 156 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert",
 /* 157 */ "cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES",
 /* 158 */ "upsert ::=",
 /* 159 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert",
 /* 160 */ "upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert",
 /* 161 */ "upsert ::= ON CONFLICT DO NOTHING",
 /* 162 */ "upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt",
 /* 163 */ "insert_cmd ::= INSERT orconf",
 /* 164 */ "insert_cmd ::= REPLACE",
 /* 165 */ "idlist_opt ::=",
 /* 166 */ "idlist_opt ::= LP idlist RP",
 /* 167 */ "idlist ::= idlist COMMA nm",
 /* 168 */ "idlist ::= nm",
 /* 169 */ "expr ::= LP expr RP",
 /* 170 */ "expr ::= ID|INDEXED",
 /* 171 */ "expr ::= JOIN_KW",
 /* 172 */ "expr ::= nm DOT nm",
 /* 173 */ "expr ::= nm DOT nm DOT nm",
 /* 174 */ "term ::= NULL|FLOAT|BLOB",
 /* 175 */ "term ::= STRING",
 /* 176 */ "term ::= INTEGER",
 /* 177 */ "expr ::= VARIABLE",
 /* 178 */ "expr ::= expr COLLATE ID|STRING",
 /* 179 */ "expr ::= CAST LP expr AS typetoken RP",
 /* 180 */ "expr ::= ID|INDEXED LP distinct exprlist RP",
 /* 181 */ "expr ::= ID|INDEXED LP STAR RP",
 /* 182 */ "expr ::= ID|INDEXED LP distinct exprlist RP filter_over",
 /* 183 */ "expr ::= ID|INDEXED LP STAR RP filter_over",
 /* 184 */ "term ::= CTIME_KW",
 /* 185 */ "expr ::= LP nexprlist COMMA expr RP",
 /* 186 */ "expr ::= expr AND expr",
 /* 187 */ "expr ::= expr OR expr",
 /* 188 */ "expr ::= expr LT|GT|GE|LE expr",
 /* 189 */ "expr ::= expr EQ|NE expr",
 /* 190 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr",
 /* 191 */ "expr ::= expr PLUS|MINUS expr",
 /* 192 */ "expr ::= expr STAR|SLASH|REM expr",
 /* 193 */ "expr ::= expr CONCAT expr",
 /* 194 */ "likeop ::= NOT LIKE_KW|MATCH",

 /* 195 */ "expr ::= expr likeop expr",
 /* 196 */ "expr ::= expr likeop expr ESCAPE expr",
 /* 197 */ "expr ::= expr ISNULL|NOTNULL",
 /* 198 */ "expr ::= expr NOT NULL",
 /* 199 */ "expr ::= expr IS expr",
 /* 200 */ "expr ::= expr IS NOT expr",
 /* 201 */ "expr ::= NOT expr",
 /* 202 */ "expr ::= BITNOT expr",
 /* 203 */ "expr ::= PLUS|MINUS expr",
 /* 204 */ "between_op ::= BETWEEN",
 /* 205 */ "between_op ::= NOT BETWEEN",
 /* 206 */ "expr ::= expr between_op expr AND expr",
 /* 207 */ "in_op ::= IN",
 /* 208 */ "in_op ::= NOT IN",
 /* 209 */ "expr ::= expr in_op LP exprlist RP",
 /* 210 */ "expr ::= LP select RP",
 /* 211 */ "expr ::= expr in_op LP select RP",
 /* 212 */ "expr ::= expr in_op nm dbnm paren_exprlist",
 /* 213 */ "expr ::= EXISTS LP select RP",
 /* 214 */ "expr ::= CASE case_operand case_exprlist case_else END",
 /* 215 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr",
 /* 216 */ "case_exprlist ::= WHEN expr THEN expr",

 /* 217 */ "case_else ::= ELSE expr",
 /* 218 */ "case_else ::=",
 /* 219 */ "case_operand ::= expr",
 /* 220 */ "case_operand ::=",
 /* 221 */ "exprlist ::=",
 /* 222 */ "nexprlist ::= nexprlist COMMA expr",
 /* 223 */ "nexprlist ::= expr",
 /* 224 */ "paren_exprlist ::=",
 /* 225 */ "paren_exprlist ::= LP exprlist RP",
 /* 226 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt",
 /* 227 */ "uniqueflag ::= UNIQUE",
 /* 228 */ "uniqueflag ::=",
 /* 229 */ "eidlist_opt ::=",
 /* 230 */ "eidlist_opt ::= LP eidlist RP",
 /* 231 */ "eidlist ::= eidlist COMMA nm collate sortorder",
 /* 232 */ "eidlist ::= nm collate sortorder",
 /* 233 */ "collate ::=",
 /* 234 */ "collate ::= COLLATE ID|STRING",
 /* 235 */ "cmd ::= DROP INDEX ifexists fullname",
 /* 236 */ "cmd ::= VACUUM vinto",
 /* 237 */ "cmd ::= VACUUM nm vinto",

 /* 238 */ "vinto ::= INTO expr",
 /* 239 */ "vinto ::=",
 /* 240 */ "cmd ::= PRAGMA nm dbnm",
 /* 241 */ "cmd ::= PRAGMA nm dbnm EQ nmnum",
 /* 242 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP",
 /* 243 */ "cmd ::= PRAGMA nm dbnm EQ minus_num",
 /* 244 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP",
 /* 245 */ "plus_num ::= PLUS INTEGER|FLOAT",
 /* 246 */ "minus_num ::= MINUS INTEGER|FLOAT",
 /* 247 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END",
 /* 248 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause",

 /* 249 */ "trigger_time ::= BEFORE|AFTER",
 /* 250 */ "trigger_time ::= INSTEAD OF",
 /* 251 */ "trigger_time ::=",
 /* 252 */ "trigger_event ::= DELETE|INSERT",
 /* 253 */ "trigger_event ::= UPDATE",
 /* 254 */ "trigger_event ::= UPDATE OF idlist",
 /* 255 */ "when_clause ::=",
 /* 256 */ "when_clause ::= WHEN expr",
 /* 257 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI",
 /* 258 */ "trigger_cmd_list ::= trigger_cmd SEMI",
 /* 259 */ "trnm ::= nm DOT nm",
 /* 260 */ "tridxby ::= INDEXED BY nm",
 /* 261 */ "tridxby ::= NOT INDEXED",
 /* 262 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt",
 /* 263 */ "trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt",
 /* 264 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt",
 /* 265 */ "trigger_cmd ::= scanpt select scanpt",
 /* 266 */ "expr ::= RAISE LP IGNORE RP",
 /* 267 */ "expr ::= RAISE LP raisetype COMMA nm RP",
 /* 268 */ "raisetype ::= ROLLBACK",
 /* 269 */ "raisetype ::= ABORT",
 /* 270 */ "raisetype ::= FAIL",
 /* 271 */ "cmd ::= DROP TRIGGER ifexists fullname",
 /* 272 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt",
 /* 273 */ "cmd ::= DETACH database_kw_opt expr",

 /* 274 */ "key_opt ::=",
 /* 275 */ "key_opt ::= KEY expr",
 /* 276 */ "cmd ::= REINDEX",
 /* 277 */ "cmd ::= REINDEX nm dbnm",
 /* 278 */ "cmd ::= ANALYZE",
 /* 279 */ "cmd ::= ANALYZE nm dbnm",
 /* 280 */ "cmd ::= ALTER TABLE fullname RENAME TO nm",
 /* 281 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist",
 /* 282 */ "add_column_fullname ::= fullname",
 /* 283 */ "cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm",
 /* 284 */ "cmd ::= create_vtab",
 /* 285 */ "cmd ::= create_vtab LP vtabarglist RP",
 /* 286 */ "create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm",
 /* 287 */ "vtabarg ::=",
 /* 288 */ "vtabargtoken ::= ANY",
 /* 289 */ "vtabargtoken ::= lp anylist RP",
 /* 290 */ "lp ::= LP",
 /* 291 */ "with ::= WITH wqlist",
 /* 292 */ "with ::= WITH RECURSIVE wqlist",
 /* 293 */ "wqlist ::= nm eidlist_opt AS LP select RP",
 /* 294 */ "wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP",
 /* 295 */ "windowdefn_list ::= windowdefn",
 /* 296 */ "windowdefn_list ::= windowdefn_list COMMA windowdefn",
 /* 297 */ "windowdefn ::= nm AS LP window RP",
 /* 298 */ "window ::= PARTITION BY nexprlist orderby_opt frame_opt",
 /* 299 */ "window ::= nm PARTITION BY nexprlist orderby_opt frame_opt",
 /* 300 */ "window ::= ORDER BY sortlist frame_opt",
 /* 301 */ "window ::= nm ORDER BY sortlist frame_opt",

 /* 302 */ "window ::= frame_opt",
 /* 303 */ "window ::= nm frame_opt",
 /* 304 */ "frame_opt ::=",
 /* 305 */ "frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt",
 /* 306 */ "frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt",
 /* 307 */ "range_or_rows ::= RANGE|ROWS|GROUPS",
 /* 308 */ "frame_bound_s ::= frame_bound",
 /* 309 */ "frame_bound_s ::= UNBOUNDED PRECEDING",
 /* 310 */ "frame_bound_e ::= frame_bound",
 /* 311 */ "frame_bound_e ::= UNBOUNDED FOLLOWING",
 /* 312 */ "frame_bound ::= expr PRECEDING|FOLLOWING",
 /* 313 */ "frame_bound ::= CURRENT ROW",
 /* 314 */ "frame_exclude_opt ::=",
 /* 315 */ "frame_exclude_opt ::= EXCLUDE frame_exclude",
 /* 316 */ "frame_exclude ::= NO OTHERS",
 /* 317 */ "frame_exclude ::= CURRENT ROW",
 /* 318 */ "frame_exclude ::= GROUP|TIES",
 /* 319 */ "window_clause ::= WINDOW windowdefn_list",
 /* 320 */ "filter_over ::= filter_clause over_clause",
 /* 321 */ "filter_over ::= over_clause",
 /* 322 */ "filter_over ::= filter_clause",
 /* 323 */ "over_clause ::= OVER LP window RP",
 /* 324 */ "over_clause ::= OVER nm",
 /* 325 */ "filter_clause ::= FILTER LP WHERE expr RP",
 /* 326 */ "input ::= cmdlist",
 /* 327 */ "cmdlist ::= cmdlist ecmd",
 /* 328 */ "cmdlist ::= ecmd",

 /* 329 */ "ecmd ::= SEMI",
 /* 330 */ "ecmd ::= cmdx SEMI",
 /* 331 */ "ecmd ::= explain cmdx SEMI",
 /* 332 */ "trans_opt ::=",
 /* 333 */ "trans_opt ::= TRANSACTION",
 /* 334 */ "trans_opt ::= TRANSACTION nm",
 /* 335 */ "savepoint_opt ::= SAVEPOINT",
 /* 336 */ "savepoint_opt ::=",
 /* 337 */ "cmd ::= create_table create_table_args",
 /* 338 */ "columnlist ::= columnlist COMMA columnname carglist",
 /* 339 */ "columnlist ::= columnname carglist",
 /* 340 */ "nm ::= ID|INDEXED",
 /* 341 */ "nm ::= STRING",
 /* 342 */ "nm ::= JOIN_KW",
 /* 343 */ "typetoken ::= typename",
 /* 344 */ "typename ::= ID|STRING",
 /* 345 */ "signed ::= plus_num",
 /* 346 */ "signed ::= minus_num",
 /* 347 */ "carglist ::= carglist ccons",
 /* 348 */ "carglist ::=",
 /* 349 */ "ccons ::= NULL onconf",
 /* 350 */ "ccons ::= GENERATED ALWAYS AS generated",
 /* 351 */ "ccons ::= AS generated",
 /* 352 */ "conslist_opt ::= COMMA conslist",
 /* 353 */ "conslist ::= conslist tconscomma tcons",
 /* 354 */ "conslist ::= tcons",
 /* 355 */ "tconscomma ::=",
 /* 356 */ "defer_subclause_opt ::= defer_subclause",
 /* 357 */ "resolvetype ::= raisetype",
 /* 358 */ "selectnowith ::= oneselect",
 /* 359 */ "oneselect ::= values",
 /* 360 */ "sclp ::= selcollist COMMA",
 /* 361 */ "as ::= ID|STRING",
 /* 362 */ "expr ::= term",
 /* 363 */ "likeop ::= LIKE_KW|MATCH",
 /* 364 */ "exprlist ::= nexprlist",

 /* 365 */ "nmnum ::= plus_num",
 /* 366 */ "nmnum ::= nm",
 /* 367 */ "nmnum ::= ON",
 /* 368 */ "nmnum ::= DELETE",
 /* 369 */ "nmnum ::= DEFAULT",
 /* 370 */ "plus_num ::= INTEGER|FLOAT",
 /* 371 */ "foreach_clause ::=",
 /* 372 */ "foreach_clause ::= FOR EACH ROW",
 /* 373 */ "trnm ::= nm",
 /* 374 */ "tridxby ::=",
 /* 375 */ "database_kw_opt ::= DATABASE",
 /* 376 */ "database_kw_opt ::=",
 /* 377 */ "kwcolumn_opt ::=",
 /* 378 */ "kwcolumn_opt ::= COLUMNKW",
 /* 379 */ "vtabarglist ::= vtabarg",
 /* 380 */ "vtabarglist ::= vtabarglist COMMA vtabarg",
 /* 381 */ "vtabarg ::= vtabarg vtabargtoken",
 /* 382 */ "anylist ::=",
 /* 383 */ "anylist ::= anylist LP anylist RP",
 /* 384 */ "anylist ::= anylist ANY",
 /* 385 */ "with ::=",
};
#endif /* NDEBUG */


#if YYSTACKDEPTH<=0
/*
** Try to increase the size of the parser stack.  Return the number
157631
157632
157633
157634
157635
157636
157637
157638
157639
157640
157641
157642
157643
157644
157645
157646
157647
157648
157649
157650
157651
157652
157653
157654

157655
157656
157657
157658
157659
157660
157661
157662
157663
157664
157665
157666
157667

157668
157669
157670
157671
157672
157673
157674
157675
157676
157677
157678
157679
157680
157681

157682
157683
157684
157685
157686
157687
157688
157689
157690
157691
157692
157693
157694
157695
157696
157697
157698
157699
157700
157701
157702
157703
157704
157705
157706
157707
157708
157709
157710
157711
157712
157713
157714
157715
157716
157717
157718
157719
157720
157721
157722

157723
157724
157725
157726
157727
157728
157729
157730
157731
157732

157733
157734
157735
157736
157737
157738
157739
157740
157741
157742
157743
157744
157745
157746
157747
157748
157749
157750
157751
157752
157753
157754
157755
157756
157757

157758
157759
157760
157761
157762
157763
157764
157765
157766
157767
157768
157769
157770
157771
157772
157773
157774
157775
157776
157777
157778
157779
157780
157781

157782
157783
157784
157785
157786
157787
157788
157789
157790
157791
157792
157793
157794
157795
157796
157797
157798
157799
157800
157801
157802
157803
157804
157805
157806
157807
157808
157809
157810
157811
157812

157813
157814
157815
157816
157817
157818
157819
157820

157821
157822
157823
157824
157825
157826
157827
157828
157829
157830
157831
157832
157833
157834
157835
157836
157837
157838
157839
157840
157841
157842
157843
157844
157845
157846
157847

157848
157849
157850
157851
157852
157853
157854
157855
157856
157857
157858
157859
157860
157861
157862
157863
157864
157865
157866
157867
157868
157869
157870
   262,  /* (152) setlist ::= setlist COMMA nm EQ expr */
   262,  /* (153) setlist ::= setlist COMMA LP idlist RP EQ expr */
   262,  /* (154) setlist ::= nm EQ expr */
   262,  /* (155) setlist ::= LP idlist RP EQ expr */
   186,  /* (156) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
   186,  /* (157) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */
   265,  /* (158) upsert ::= */
   265,  /* (159) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */
   265,  /* (160) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */
   265,  /* (161) upsert ::= ON CONFLICT DO NOTHING */
   263,  /* (162) insert_cmd ::= INSERT orconf */
   263,  /* (163) insert_cmd ::= REPLACE */
   264,  /* (164) idlist_opt ::= */
   264,  /* (165) idlist_opt ::= LP idlist RP */
   259,  /* (166) idlist ::= idlist COMMA nm */
   259,  /* (167) idlist ::= nm */
   212,  /* (168) expr ::= LP expr RP */
   212,  /* (169) expr ::= ID|INDEXED */
   212,  /* (170) expr ::= JOIN_KW */
   212,  /* (171) expr ::= nm DOT nm */
   212,  /* (172) expr ::= nm DOT nm DOT nm */
   211,  /* (173) term ::= NULL|FLOAT|BLOB */
   211,  /* (174) term ::= STRING */
   211,  /* (175) term ::= INTEGER */

   212,  /* (176) expr ::= VARIABLE */
   212,  /* (177) expr ::= expr COLLATE ID|STRING */
   212,  /* (178) expr ::= CAST LP expr AS typetoken RP */
   212,  /* (179) expr ::= ID|INDEXED LP distinct exprlist RP */
   212,  /* (180) expr ::= ID|INDEXED LP STAR RP */
   212,  /* (181) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */
   212,  /* (182) expr ::= ID|INDEXED LP STAR RP filter_over */
   211,  /* (183) term ::= CTIME_KW */
   212,  /* (184) expr ::= LP nexprlist COMMA expr RP */
   212,  /* (185) expr ::= expr AND expr */
   212,  /* (186) expr ::= expr OR expr */
   212,  /* (187) expr ::= expr LT|GT|GE|LE expr */
   212,  /* (188) expr ::= expr EQ|NE expr */

   212,  /* (189) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
   212,  /* (190) expr ::= expr PLUS|MINUS expr */
   212,  /* (191) expr ::= expr STAR|SLASH|REM expr */
   212,  /* (192) expr ::= expr CONCAT expr */
   267,  /* (193) likeop ::= NOT LIKE_KW|MATCH */
   212,  /* (194) expr ::= expr likeop expr */
   212,  /* (195) expr ::= expr likeop expr ESCAPE expr */
   212,  /* (196) expr ::= expr ISNULL|NOTNULL */
   212,  /* (197) expr ::= expr NOT NULL */
   212,  /* (198) expr ::= expr IS expr */
   212,  /* (199) expr ::= expr IS NOT expr */
   212,  /* (200) expr ::= NOT expr */
   212,  /* (201) expr ::= BITNOT expr */
   212,  /* (202) expr ::= PLUS|MINUS expr */

   268,  /* (203) between_op ::= BETWEEN */
   268,  /* (204) between_op ::= NOT BETWEEN */
   212,  /* (205) expr ::= expr between_op expr AND expr */
   269,  /* (206) in_op ::= IN */
   269,  /* (207) in_op ::= NOT IN */
   212,  /* (208) expr ::= expr in_op LP exprlist RP */
   212,  /* (209) expr ::= LP select RP */
   212,  /* (210) expr ::= expr in_op LP select RP */
   212,  /* (211) expr ::= expr in_op nm dbnm paren_exprlist */
   212,  /* (212) expr ::= EXISTS LP select RP */
   212,  /* (213) expr ::= CASE case_operand case_exprlist case_else END */
   272,  /* (214) case_exprlist ::= case_exprlist WHEN expr THEN expr */
   272,  /* (215) case_exprlist ::= WHEN expr THEN expr */
   273,  /* (216) case_else ::= ELSE expr */
   273,  /* (217) case_else ::= */
   271,  /* (218) case_operand ::= expr */
   271,  /* (219) case_operand ::= */
   257,  /* (220) exprlist ::= */
   248,  /* (221) nexprlist ::= nexprlist COMMA expr */
   248,  /* (222) nexprlist ::= expr */
   270,  /* (223) paren_exprlist ::= */
   270,  /* (224) paren_exprlist ::= LP exprlist RP */
   186,  /* (225) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
   274,  /* (226) uniqueflag ::= UNIQUE */
   274,  /* (227) uniqueflag ::= */
   216,  /* (228) eidlist_opt ::= */
   216,  /* (229) eidlist_opt ::= LP eidlist RP */
   227,  /* (230) eidlist ::= eidlist COMMA nm collate sortorder */
   227,  /* (231) eidlist ::= nm collate sortorder */
   275,  /* (232) collate ::= */
   275,  /* (233) collate ::= COLLATE ID|STRING */
   186,  /* (234) cmd ::= DROP INDEX ifexists fullname */
   186,  /* (235) cmd ::= VACUUM vinto */
   186,  /* (236) cmd ::= VACUUM nm vinto */
   276,  /* (237) vinto ::= INTO expr */
   276,  /* (238) vinto ::= */
   186,  /* (239) cmd ::= PRAGMA nm dbnm */
   186,  /* (240) cmd ::= PRAGMA nm dbnm EQ nmnum */
   186,  /* (241) cmd ::= PRAGMA nm dbnm LP nmnum RP */
   186,  /* (242) cmd ::= PRAGMA nm dbnm EQ minus_num */
   186,  /* (243) cmd ::= PRAGMA nm dbnm LP minus_num RP */

   206,  /* (244) plus_num ::= PLUS INTEGER|FLOAT */
   207,  /* (245) minus_num ::= MINUS INTEGER|FLOAT */
   186,  /* (246) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
   278,  /* (247) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
   280,  /* (248) trigger_time ::= BEFORE|AFTER */
   280,  /* (249) trigger_time ::= INSTEAD OF */
   280,  /* (250) trigger_time ::= */
   281,  /* (251) trigger_event ::= DELETE|INSERT */
   281,  /* (252) trigger_event ::= UPDATE */
   281,  /* (253) trigger_event ::= UPDATE OF idlist */

   283,  /* (254) when_clause ::= */
   283,  /* (255) when_clause ::= WHEN expr */
   279,  /* (256) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
   279,  /* (257) trigger_cmd_list ::= trigger_cmd SEMI */
   285,  /* (258) trnm ::= nm DOT nm */
   286,  /* (259) tridxby ::= INDEXED BY nm */
   286,  /* (260) tridxby ::= NOT INDEXED */
   284,  /* (261) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
   284,  /* (262) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
   284,  /* (263) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
   284,  /* (264) trigger_cmd ::= scanpt select scanpt */
   212,  /* (265) expr ::= RAISE LP IGNORE RP */
   212,  /* (266) expr ::= RAISE LP raisetype COMMA nm RP */
   231,  /* (267) raisetype ::= ROLLBACK */
   231,  /* (268) raisetype ::= ABORT */
   231,  /* (269) raisetype ::= FAIL */
   186,  /* (270) cmd ::= DROP TRIGGER ifexists fullname */
   186,  /* (271) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
   186,  /* (272) cmd ::= DETACH database_kw_opt expr */
   288,  /* (273) key_opt ::= */
   288,  /* (274) key_opt ::= KEY expr */
   186,  /* (275) cmd ::= REINDEX */
   186,  /* (276) cmd ::= REINDEX nm dbnm */
   186,  /* (277) cmd ::= ANALYZE */
   186,  /* (278) cmd ::= ANALYZE nm dbnm */

   186,  /* (279) cmd ::= ALTER TABLE fullname RENAME TO nm */
   186,  /* (280) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
   289,  /* (281) add_column_fullname ::= fullname */
   186,  /* (282) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
   186,  /* (283) cmd ::= create_vtab */
   186,  /* (284) cmd ::= create_vtab LP vtabarglist RP */
   291,  /* (285) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
   293,  /* (286) vtabarg ::= */
   294,  /* (287) vtabargtoken ::= ANY */
   294,  /* (288) vtabargtoken ::= lp anylist RP */
   295,  /* (289) lp ::= LP */
   261,  /* (290) with ::= WITH wqlist */
   261,  /* (291) with ::= WITH RECURSIVE wqlist */
   236,  /* (292) wqlist ::= nm eidlist_opt AS LP select RP */
   236,  /* (293) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
   297,  /* (294) windowdefn_list ::= windowdefn */
   297,  /* (295) windowdefn_list ::= windowdefn_list COMMA windowdefn */
   298,  /* (296) windowdefn ::= nm AS LP window RP */
   299,  /* (297) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
   299,  /* (298) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
   299,  /* (299) window ::= ORDER BY sortlist frame_opt */
   299,  /* (300) window ::= nm ORDER BY sortlist frame_opt */
   299,  /* (301) window ::= frame_opt */
   299,  /* (302) window ::= nm frame_opt */

   300,  /* (303) frame_opt ::= */
   300,  /* (304) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
   300,  /* (305) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
   304,  /* (306) range_or_rows ::= RANGE|ROWS|GROUPS */
   306,  /* (307) frame_bound_s ::= frame_bound */
   306,  /* (308) frame_bound_s ::= UNBOUNDED PRECEDING */
   307,  /* (309) frame_bound_e ::= frame_bound */
   307,  /* (310) frame_bound_e ::= UNBOUNDED FOLLOWING */
   305,  /* (311) frame_bound ::= expr PRECEDING|FOLLOWING */
   305,  /* (312) frame_bound ::= CURRENT ROW */
   308,  /* (313) frame_exclude_opt ::= */
   308,  /* (314) frame_exclude_opt ::= EXCLUDE frame_exclude */
   309,  /* (315) frame_exclude ::= NO OTHERS */
   309,  /* (316) frame_exclude ::= CURRENT ROW */
   309,  /* (317) frame_exclude ::= GROUP|TIES */
   246,  /* (318) window_clause ::= WINDOW windowdefn_list */
   266,  /* (319) filter_over ::= filter_clause over_clause */
   266,  /* (320) filter_over ::= over_clause */
   266,  /* (321) filter_over ::= filter_clause */
   303,  /* (322) over_clause ::= OVER LP window RP */
   303,  /* (323) over_clause ::= OVER nm */
   302,  /* (324) filter_clause ::= FILTER LP WHERE expr RP */
   181,  /* (325) input ::= cmdlist */
   182,  /* (326) cmdlist ::= cmdlist ecmd */
   182,  /* (327) cmdlist ::= ecmd */
   183,  /* (328) ecmd ::= SEMI */
   183,  /* (329) ecmd ::= cmdx SEMI */
   183,  /* (330) ecmd ::= explain cmdx SEMI */
   188,  /* (331) trans_opt ::= */
   188,  /* (332) trans_opt ::= TRANSACTION */
   188,  /* (333) trans_opt ::= TRANSACTION nm */

   190,  /* (334) savepoint_opt ::= SAVEPOINT */
   190,  /* (335) savepoint_opt ::= */
   186,  /* (336) cmd ::= create_table create_table_args */
   197,  /* (337) columnlist ::= columnlist COMMA columnname carglist */
   197,  /* (338) columnlist ::= columnname carglist */
   189,  /* (339) nm ::= ID|INDEXED */
   189,  /* (340) nm ::= STRING */
   189,  /* (341) nm ::= JOIN_KW */

   203,  /* (342) typetoken ::= typename */
   204,  /* (343) typename ::= ID|STRING */
   205,  /* (344) signed ::= plus_num */
   205,  /* (345) signed ::= minus_num */
   202,  /* (346) carglist ::= carglist ccons */
   202,  /* (347) carglist ::= */
   210,  /* (348) ccons ::= NULL onconf */
   210,  /* (349) ccons ::= GENERATED ALWAYS AS generated */
   210,  /* (350) ccons ::= AS generated */
   198,  /* (351) conslist_opt ::= COMMA conslist */
   223,  /* (352) conslist ::= conslist tconscomma tcons */
   223,  /* (353) conslist ::= tcons */
   224,  /* (354) tconscomma ::= */
   228,  /* (355) defer_subclause_opt ::= defer_subclause */
   230,  /* (356) resolvetype ::= raisetype */
   234,  /* (357) selectnowith ::= oneselect */
   235,  /* (358) oneselect ::= values */
   249,  /* (359) sclp ::= selcollist COMMA */
   250,  /* (360) as ::= ID|STRING */
   212,  /* (361) expr ::= term */
   267,  /* (362) likeop ::= LIKE_KW|MATCH */
   257,  /* (363) exprlist ::= nexprlist */
   277,  /* (364) nmnum ::= plus_num */
   277,  /* (365) nmnum ::= nm */
   277,  /* (366) nmnum ::= ON */
   277,  /* (367) nmnum ::= DELETE */
   277,  /* (368) nmnum ::= DEFAULT */

   206,  /* (369) plus_num ::= INTEGER|FLOAT */
   282,  /* (370) foreach_clause ::= */
   282,  /* (371) foreach_clause ::= FOR EACH ROW */
   285,  /* (372) trnm ::= nm */
   286,  /* (373) tridxby ::= */
   287,  /* (374) database_kw_opt ::= DATABASE */
   287,  /* (375) database_kw_opt ::= */
   290,  /* (376) kwcolumn_opt ::= */
   290,  /* (377) kwcolumn_opt ::= COLUMNKW */
   292,  /* (378) vtabarglist ::= vtabarg */
   292,  /* (379) vtabarglist ::= vtabarglist COMMA vtabarg */
   293,  /* (380) vtabarg ::= vtabarg vtabargtoken */
   296,  /* (381) anylist ::= */
   296,  /* (382) anylist ::= anylist LP anylist RP */
   296,  /* (383) anylist ::= anylist ANY */
   261,  /* (384) with ::= */
};

/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number
** of symbols on the right-hand side of that rule. */
static const signed char yyRuleInfoNRhs[] = {
   -1,  /* (0) explain ::= EXPLAIN */
   -3,  /* (1) explain ::= EXPLAIN QUERY PLAN */







|
|

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







158788
158789
158790
158791
158792
158793
158794
158795
158796
158797
158798
158799
158800
158801
158802
158803
158804
158805
158806
158807
158808
158809
158810
158811
158812
158813
158814
158815
158816
158817
158818
158819
158820

158821
158822
158823
158824
158825
158826
158827
158828
158829
158830

158831
158832
158833
158834
158835
158836
158837
158838
158839
158840
158841
158842
158843
158844
158845
158846
158847
158848
158849
158850
158851
158852
158853
158854
158855
158856
158857
158858
158859
158860
158861
158862
158863
158864
158865
158866
158867
158868
158869
158870
158871
158872
158873

158874
158875
158876
158877
158878
158879
158880
158881
158882
158883
158884

158885
158886
158887
158888
158889
158890
158891
158892
158893
158894
158895
158896
158897
158898
158899
158900
158901
158902
158903
158904
158905
158906
158907
158908
158909

158910
158911
158912
158913
158914
158915
158916
158917
158918
158919
158920
158921
158922
158923
158924
158925
158926
158927
158928
158929
158930
158931
158932
158933
158934
158935
158936

158937
158938
158939
158940
158941
158942
158943
158944
158945
158946
158947
158948
158949
158950
158951
158952
158953
158954
158955
158956
158957
158958
158959
158960
158961
158962

158963
158964
158965
158966
158967
158968
158969
158970
158971
158972
158973
158974
158975

158976
158977
158978
158979
158980
158981
158982
158983
158984
158985
158986
158987
158988
158989
158990
158991
158992
158993
158994
158995
158996
158997
158998
158999
159000

159001
159002
159003
159004
159005
159006
159007
159008
159009
159010
159011
159012
159013
159014
159015
159016
159017
159018
159019
159020
159021
159022
159023
159024
159025
159026
159027
159028
   262,  /* (152) setlist ::= setlist COMMA nm EQ expr */
   262,  /* (153) setlist ::= setlist COMMA LP idlist RP EQ expr */
   262,  /* (154) setlist ::= nm EQ expr */
   262,  /* (155) setlist ::= LP idlist RP EQ expr */
   186,  /* (156) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
   186,  /* (157) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */
   265,  /* (158) upsert ::= */
   265,  /* (159) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
   265,  /* (160) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
   265,  /* (161) upsert ::= ON CONFLICT DO NOTHING */
   265,  /* (162) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt */
   263,  /* (163) insert_cmd ::= INSERT orconf */
   263,  /* (164) insert_cmd ::= REPLACE */
   264,  /* (165) idlist_opt ::= */
   264,  /* (166) idlist_opt ::= LP idlist RP */
   259,  /* (167) idlist ::= idlist COMMA nm */
   259,  /* (168) idlist ::= nm */
   212,  /* (169) expr ::= LP expr RP */
   212,  /* (170) expr ::= ID|INDEXED */
   212,  /* (171) expr ::= JOIN_KW */
   212,  /* (172) expr ::= nm DOT nm */
   212,  /* (173) expr ::= nm DOT nm DOT nm */
   211,  /* (174) term ::= NULL|FLOAT|BLOB */
   211,  /* (175) term ::= STRING */
   211,  /* (176) term ::= INTEGER */
   212,  /* (177) expr ::= VARIABLE */
   212,  /* (178) expr ::= expr COLLATE ID|STRING */
   212,  /* (179) expr ::= CAST LP expr AS typetoken RP */
   212,  /* (180) expr ::= ID|INDEXED LP distinct exprlist RP */
   212,  /* (181) expr ::= ID|INDEXED LP STAR RP */
   212,  /* (182) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */
   212,  /* (183) expr ::= ID|INDEXED LP STAR RP filter_over */
   211,  /* (184) term ::= CTIME_KW */

   212,  /* (185) expr ::= LP nexprlist COMMA expr RP */
   212,  /* (186) expr ::= expr AND expr */
   212,  /* (187) expr ::= expr OR expr */
   212,  /* (188) expr ::= expr LT|GT|GE|LE expr */
   212,  /* (189) expr ::= expr EQ|NE expr */
   212,  /* (190) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
   212,  /* (191) expr ::= expr PLUS|MINUS expr */
   212,  /* (192) expr ::= expr STAR|SLASH|REM expr */
   212,  /* (193) expr ::= expr CONCAT expr */
   267,  /* (194) likeop ::= NOT LIKE_KW|MATCH */

   212,  /* (195) expr ::= expr likeop expr */
   212,  /* (196) expr ::= expr likeop expr ESCAPE expr */
   212,  /* (197) expr ::= expr ISNULL|NOTNULL */
   212,  /* (198) expr ::= expr NOT NULL */
   212,  /* (199) expr ::= expr IS expr */
   212,  /* (200) expr ::= expr IS NOT expr */
   212,  /* (201) expr ::= NOT expr */
   212,  /* (202) expr ::= BITNOT expr */
   212,  /* (203) expr ::= PLUS|MINUS expr */
   268,  /* (204) between_op ::= BETWEEN */
   268,  /* (205) between_op ::= NOT BETWEEN */
   212,  /* (206) expr ::= expr between_op expr AND expr */
   269,  /* (207) in_op ::= IN */
   269,  /* (208) in_op ::= NOT IN */
   212,  /* (209) expr ::= expr in_op LP exprlist RP */
   212,  /* (210) expr ::= LP select RP */
   212,  /* (211) expr ::= expr in_op LP select RP */
   212,  /* (212) expr ::= expr in_op nm dbnm paren_exprlist */
   212,  /* (213) expr ::= EXISTS LP select RP */
   212,  /* (214) expr ::= CASE case_operand case_exprlist case_else END */
   272,  /* (215) case_exprlist ::= case_exprlist WHEN expr THEN expr */
   272,  /* (216) case_exprlist ::= WHEN expr THEN expr */
   273,  /* (217) case_else ::= ELSE expr */
   273,  /* (218) case_else ::= */
   271,  /* (219) case_operand ::= expr */
   271,  /* (220) case_operand ::= */
   257,  /* (221) exprlist ::= */
   248,  /* (222) nexprlist ::= nexprlist COMMA expr */
   248,  /* (223) nexprlist ::= expr */
   270,  /* (224) paren_exprlist ::= */
   270,  /* (225) paren_exprlist ::= LP exprlist RP */
   186,  /* (226) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
   274,  /* (227) uniqueflag ::= UNIQUE */
   274,  /* (228) uniqueflag ::= */
   216,  /* (229) eidlist_opt ::= */
   216,  /* (230) eidlist_opt ::= LP eidlist RP */
   227,  /* (231) eidlist ::= eidlist COMMA nm collate sortorder */
   227,  /* (232) eidlist ::= nm collate sortorder */
   275,  /* (233) collate ::= */
   275,  /* (234) collate ::= COLLATE ID|STRING */
   186,  /* (235) cmd ::= DROP INDEX ifexists fullname */
   186,  /* (236) cmd ::= VACUUM vinto */
   186,  /* (237) cmd ::= VACUUM nm vinto */

   276,  /* (238) vinto ::= INTO expr */
   276,  /* (239) vinto ::= */
   186,  /* (240) cmd ::= PRAGMA nm dbnm */
   186,  /* (241) cmd ::= PRAGMA nm dbnm EQ nmnum */
   186,  /* (242) cmd ::= PRAGMA nm dbnm LP nmnum RP */
   186,  /* (243) cmd ::= PRAGMA nm dbnm EQ minus_num */
   186,  /* (244) cmd ::= PRAGMA nm dbnm LP minus_num RP */
   206,  /* (245) plus_num ::= PLUS INTEGER|FLOAT */
   207,  /* (246) minus_num ::= MINUS INTEGER|FLOAT */
   186,  /* (247) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
   278,  /* (248) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */

   280,  /* (249) trigger_time ::= BEFORE|AFTER */
   280,  /* (250) trigger_time ::= INSTEAD OF */
   280,  /* (251) trigger_time ::= */
   281,  /* (252) trigger_event ::= DELETE|INSERT */
   281,  /* (253) trigger_event ::= UPDATE */
   281,  /* (254) trigger_event ::= UPDATE OF idlist */
   283,  /* (255) when_clause ::= */
   283,  /* (256) when_clause ::= WHEN expr */
   279,  /* (257) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
   279,  /* (258) trigger_cmd_list ::= trigger_cmd SEMI */
   285,  /* (259) trnm ::= nm DOT nm */
   286,  /* (260) tridxby ::= INDEXED BY nm */
   286,  /* (261) tridxby ::= NOT INDEXED */
   284,  /* (262) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
   284,  /* (263) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
   284,  /* (264) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
   284,  /* (265) trigger_cmd ::= scanpt select scanpt */
   212,  /* (266) expr ::= RAISE LP IGNORE RP */
   212,  /* (267) expr ::= RAISE LP raisetype COMMA nm RP */
   231,  /* (268) raisetype ::= ROLLBACK */
   231,  /* (269) raisetype ::= ABORT */
   231,  /* (270) raisetype ::= FAIL */
   186,  /* (271) cmd ::= DROP TRIGGER ifexists fullname */
   186,  /* (272) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
   186,  /* (273) cmd ::= DETACH database_kw_opt expr */

   288,  /* (274) key_opt ::= */
   288,  /* (275) key_opt ::= KEY expr */
   186,  /* (276) cmd ::= REINDEX */
   186,  /* (277) cmd ::= REINDEX nm dbnm */
   186,  /* (278) cmd ::= ANALYZE */
   186,  /* (279) cmd ::= ANALYZE nm dbnm */
   186,  /* (280) cmd ::= ALTER TABLE fullname RENAME TO nm */
   186,  /* (281) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
   289,  /* (282) add_column_fullname ::= fullname */
   186,  /* (283) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
   186,  /* (284) cmd ::= create_vtab */
   186,  /* (285) cmd ::= create_vtab LP vtabarglist RP */
   291,  /* (286) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
   293,  /* (287) vtabarg ::= */
   294,  /* (288) vtabargtoken ::= ANY */
   294,  /* (289) vtabargtoken ::= lp anylist RP */
   295,  /* (290) lp ::= LP */
   261,  /* (291) with ::= WITH wqlist */
   261,  /* (292) with ::= WITH RECURSIVE wqlist */
   236,  /* (293) wqlist ::= nm eidlist_opt AS LP select RP */
   236,  /* (294) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
   297,  /* (295) windowdefn_list ::= windowdefn */
   297,  /* (296) windowdefn_list ::= windowdefn_list COMMA windowdefn */
   298,  /* (297) windowdefn ::= nm AS LP window RP */
   299,  /* (298) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
   299,  /* (299) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
   299,  /* (300) window ::= ORDER BY sortlist frame_opt */

   299,  /* (301) window ::= nm ORDER BY sortlist frame_opt */
   299,  /* (302) window ::= frame_opt */
   299,  /* (303) window ::= nm frame_opt */
   300,  /* (304) frame_opt ::= */
   300,  /* (305) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
   300,  /* (306) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
   304,  /* (307) range_or_rows ::= RANGE|ROWS|GROUPS */
   306,  /* (308) frame_bound_s ::= frame_bound */
   306,  /* (309) frame_bound_s ::= UNBOUNDED PRECEDING */
   307,  /* (310) frame_bound_e ::= frame_bound */
   307,  /* (311) frame_bound_e ::= UNBOUNDED FOLLOWING */
   305,  /* (312) frame_bound ::= expr PRECEDING|FOLLOWING */
   305,  /* (313) frame_bound ::= CURRENT ROW */
   308,  /* (314) frame_exclude_opt ::= */
   308,  /* (315) frame_exclude_opt ::= EXCLUDE frame_exclude */
   309,  /* (316) frame_exclude ::= NO OTHERS */
   309,  /* (317) frame_exclude ::= CURRENT ROW */
   309,  /* (318) frame_exclude ::= GROUP|TIES */
   246,  /* (319) window_clause ::= WINDOW windowdefn_list */
   266,  /* (320) filter_over ::= filter_clause over_clause */
   266,  /* (321) filter_over ::= over_clause */
   266,  /* (322) filter_over ::= filter_clause */
   303,  /* (323) over_clause ::= OVER LP window RP */
   303,  /* (324) over_clause ::= OVER nm */
   302,  /* (325) filter_clause ::= FILTER LP WHERE expr RP */
   181,  /* (326) input ::= cmdlist */

   182,  /* (327) cmdlist ::= cmdlist ecmd */
   182,  /* (328) cmdlist ::= ecmd */
   183,  /* (329) ecmd ::= SEMI */
   183,  /* (330) ecmd ::= cmdx SEMI */
   183,  /* (331) ecmd ::= explain cmdx SEMI */
   188,  /* (332) trans_opt ::= */
   188,  /* (333) trans_opt ::= TRANSACTION */
   188,  /* (334) trans_opt ::= TRANSACTION nm */
   190,  /* (335) savepoint_opt ::= SAVEPOINT */
   190,  /* (336) savepoint_opt ::= */
   186,  /* (337) cmd ::= create_table create_table_args */
   197,  /* (338) columnlist ::= columnlist COMMA columnname carglist */
   197,  /* (339) columnlist ::= columnname carglist */

   189,  /* (340) nm ::= ID|INDEXED */
   189,  /* (341) nm ::= STRING */
   189,  /* (342) nm ::= JOIN_KW */
   203,  /* (343) typetoken ::= typename */
   204,  /* (344) typename ::= ID|STRING */
   205,  /* (345) signed ::= plus_num */
   205,  /* (346) signed ::= minus_num */
   202,  /* (347) carglist ::= carglist ccons */
   202,  /* (348) carglist ::= */
   210,  /* (349) ccons ::= NULL onconf */
   210,  /* (350) ccons ::= GENERATED ALWAYS AS generated */
   210,  /* (351) ccons ::= AS generated */
   198,  /* (352) conslist_opt ::= COMMA conslist */
   223,  /* (353) conslist ::= conslist tconscomma tcons */
   223,  /* (354) conslist ::= tcons */
   224,  /* (355) tconscomma ::= */
   228,  /* (356) defer_subclause_opt ::= defer_subclause */
   230,  /* (357) resolvetype ::= raisetype */
   234,  /* (358) selectnowith ::= oneselect */
   235,  /* (359) oneselect ::= values */
   249,  /* (360) sclp ::= selcollist COMMA */
   250,  /* (361) as ::= ID|STRING */
   212,  /* (362) expr ::= term */
   267,  /* (363) likeop ::= LIKE_KW|MATCH */
   257,  /* (364) exprlist ::= nexprlist */

   277,  /* (365) nmnum ::= plus_num */
   277,  /* (366) nmnum ::= nm */
   277,  /* (367) nmnum ::= ON */
   277,  /* (368) nmnum ::= DELETE */
   277,  /* (369) nmnum ::= DEFAULT */
   206,  /* (370) plus_num ::= INTEGER|FLOAT */
   282,  /* (371) foreach_clause ::= */
   282,  /* (372) foreach_clause ::= FOR EACH ROW */
   285,  /* (373) trnm ::= nm */
   286,  /* (374) tridxby ::= */
   287,  /* (375) database_kw_opt ::= DATABASE */
   287,  /* (376) database_kw_opt ::= */
   290,  /* (377) kwcolumn_opt ::= */
   290,  /* (378) kwcolumn_opt ::= COLUMNKW */
   292,  /* (379) vtabarglist ::= vtabarg */
   292,  /* (380) vtabarglist ::= vtabarglist COMMA vtabarg */
   293,  /* (381) vtabarg ::= vtabarg vtabargtoken */
   296,  /* (382) anylist ::= */
   296,  /* (383) anylist ::= anylist LP anylist RP */
   296,  /* (384) anylist ::= anylist ANY */
   261,  /* (385) with ::= */
};

/* For rule J, yyRuleInfoNRhs[J] contains the negative of the number
** of symbols on the right-hand side of that rule. */
static const signed char yyRuleInfoNRhs[] = {
   -1,  /* (0) explain ::= EXPLAIN */
   -3,  /* (1) explain ::= EXPLAIN QUERY PLAN */
158021
158022
158023
158024
158025
158026
158027
158028
158029
158030

158031
158032
158033
158034
158035
158036
158037
158038
158039
158040
158041
158042
158043
158044
158045
158046
158047
158048
158049
158050
158051
158052
158053
158054
158055
158056
158057

158058
158059
158060
158061
158062
158063
158064
158065
158066
158067
158068
158069
158070
158071
158072
158073
158074
158075
158076
158077
158078
158079
158080
158081
158082
158083
158084
158085
158086
158087
158088
158089
158090
158091
158092
158093
158094
158095
158096
158097
158098
158099
158100
158101
158102
158103
158104
158105
158106
158107
158108
158109
158110
158111
158112
158113
158114
158115
158116
158117
158118
158119
158120
158121
158122
158123
158124
158125
158126
158127
158128
158129
158130
158131
158132
158133
158134
158135
158136
158137
158138
158139
158140
158141
158142
158143

158144
158145
158146
158147
158148
158149
158150
158151
158152
158153
158154
158155
158156
158157
158158
158159
158160
158161
158162
158163
158164
158165
158166
158167
158168
158169
158170
158171
158172
158173
158174
158175
158176
158177
158178
158179
158180
158181
158182
158183
158184
158185
158186
158187
158188
158189
158190
158191
158192
158193
158194
158195
158196
158197
158198
158199
158200
158201
158202
158203
158204
158205
158206
158207
158208
158209
158210

158211
158212
158213
158214
158215
158216
158217
158218
158219
158220
158221
158222
158223
158224
158225
158226
158227
158228
158229
158230
158231
158232
158233
158234
158235
158236
158237

158238
158239
158240
158241
158242
158243
158244
158245
158246
158247
158248
158249
158250
158251
158252
158253
158254
158255
158256
158257
158258
158259
158260
   -5,  /* (152) setlist ::= setlist COMMA nm EQ expr */
   -7,  /* (153) setlist ::= setlist COMMA LP idlist RP EQ expr */
   -3,  /* (154) setlist ::= nm EQ expr */
   -5,  /* (155) setlist ::= LP idlist RP EQ expr */
   -7,  /* (156) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
   -7,  /* (157) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */
    0,  /* (158) upsert ::= */
  -11,  /* (159) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */
   -8,  /* (160) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */
   -4,  /* (161) upsert ::= ON CONFLICT DO NOTHING */

   -2,  /* (162) insert_cmd ::= INSERT orconf */
   -1,  /* (163) insert_cmd ::= REPLACE */
    0,  /* (164) idlist_opt ::= */
   -3,  /* (165) idlist_opt ::= LP idlist RP */
   -3,  /* (166) idlist ::= idlist COMMA nm */
   -1,  /* (167) idlist ::= nm */
   -3,  /* (168) expr ::= LP expr RP */
   -1,  /* (169) expr ::= ID|INDEXED */
   -1,  /* (170) expr ::= JOIN_KW */
   -3,  /* (171) expr ::= nm DOT nm */
   -5,  /* (172) expr ::= nm DOT nm DOT nm */
   -1,  /* (173) term ::= NULL|FLOAT|BLOB */
   -1,  /* (174) term ::= STRING */
   -1,  /* (175) term ::= INTEGER */
   -1,  /* (176) expr ::= VARIABLE */
   -3,  /* (177) expr ::= expr COLLATE ID|STRING */
   -6,  /* (178) expr ::= CAST LP expr AS typetoken RP */
   -5,  /* (179) expr ::= ID|INDEXED LP distinct exprlist RP */
   -4,  /* (180) expr ::= ID|INDEXED LP STAR RP */
   -6,  /* (181) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */
   -5,  /* (182) expr ::= ID|INDEXED LP STAR RP filter_over */
   -1,  /* (183) term ::= CTIME_KW */
   -5,  /* (184) expr ::= LP nexprlist COMMA expr RP */
   -3,  /* (185) expr ::= expr AND expr */
   -3,  /* (186) expr ::= expr OR expr */
   -3,  /* (187) expr ::= expr LT|GT|GE|LE expr */
   -3,  /* (188) expr ::= expr EQ|NE expr */

   -3,  /* (189) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
   -3,  /* (190) expr ::= expr PLUS|MINUS expr */
   -3,  /* (191) expr ::= expr STAR|SLASH|REM expr */
   -3,  /* (192) expr ::= expr CONCAT expr */
   -2,  /* (193) likeop ::= NOT LIKE_KW|MATCH */
   -3,  /* (194) expr ::= expr likeop expr */
   -5,  /* (195) expr ::= expr likeop expr ESCAPE expr */
   -2,  /* (196) expr ::= expr ISNULL|NOTNULL */
   -3,  /* (197) expr ::= expr NOT NULL */
   -3,  /* (198) expr ::= expr IS expr */
   -4,  /* (199) expr ::= expr IS NOT expr */
   -2,  /* (200) expr ::= NOT expr */
   -2,  /* (201) expr ::= BITNOT expr */
   -2,  /* (202) expr ::= PLUS|MINUS expr */
   -1,  /* (203) between_op ::= BETWEEN */
   -2,  /* (204) between_op ::= NOT BETWEEN */
   -5,  /* (205) expr ::= expr between_op expr AND expr */
   -1,  /* (206) in_op ::= IN */
   -2,  /* (207) in_op ::= NOT IN */
   -5,  /* (208) expr ::= expr in_op LP exprlist RP */
   -3,  /* (209) expr ::= LP select RP */
   -5,  /* (210) expr ::= expr in_op LP select RP */
   -5,  /* (211) expr ::= expr in_op nm dbnm paren_exprlist */
   -4,  /* (212) expr ::= EXISTS LP select RP */
   -5,  /* (213) expr ::= CASE case_operand case_exprlist case_else END */
   -5,  /* (214) case_exprlist ::= case_exprlist WHEN expr THEN expr */
   -4,  /* (215) case_exprlist ::= WHEN expr THEN expr */
   -2,  /* (216) case_else ::= ELSE expr */
    0,  /* (217) case_else ::= */
   -1,  /* (218) case_operand ::= expr */
    0,  /* (219) case_operand ::= */
    0,  /* (220) exprlist ::= */
   -3,  /* (221) nexprlist ::= nexprlist COMMA expr */
   -1,  /* (222) nexprlist ::= expr */
    0,  /* (223) paren_exprlist ::= */
   -3,  /* (224) paren_exprlist ::= LP exprlist RP */
  -12,  /* (225) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
   -1,  /* (226) uniqueflag ::= UNIQUE */
    0,  /* (227) uniqueflag ::= */
    0,  /* (228) eidlist_opt ::= */
   -3,  /* (229) eidlist_opt ::= LP eidlist RP */
   -5,  /* (230) eidlist ::= eidlist COMMA nm collate sortorder */
   -3,  /* (231) eidlist ::= nm collate sortorder */
    0,  /* (232) collate ::= */
   -2,  /* (233) collate ::= COLLATE ID|STRING */
   -4,  /* (234) cmd ::= DROP INDEX ifexists fullname */
   -2,  /* (235) cmd ::= VACUUM vinto */
   -3,  /* (236) cmd ::= VACUUM nm vinto */
   -2,  /* (237) vinto ::= INTO expr */
    0,  /* (238) vinto ::= */
   -3,  /* (239) cmd ::= PRAGMA nm dbnm */
   -5,  /* (240) cmd ::= PRAGMA nm dbnm EQ nmnum */
   -6,  /* (241) cmd ::= PRAGMA nm dbnm LP nmnum RP */
   -5,  /* (242) cmd ::= PRAGMA nm dbnm EQ minus_num */
   -6,  /* (243) cmd ::= PRAGMA nm dbnm LP minus_num RP */
   -2,  /* (244) plus_num ::= PLUS INTEGER|FLOAT */
   -2,  /* (245) minus_num ::= MINUS INTEGER|FLOAT */
   -5,  /* (246) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
  -11,  /* (247) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
   -1,  /* (248) trigger_time ::= BEFORE|AFTER */
   -2,  /* (249) trigger_time ::= INSTEAD OF */
    0,  /* (250) trigger_time ::= */
   -1,  /* (251) trigger_event ::= DELETE|INSERT */
   -1,  /* (252) trigger_event ::= UPDATE */
   -3,  /* (253) trigger_event ::= UPDATE OF idlist */
    0,  /* (254) when_clause ::= */
   -2,  /* (255) when_clause ::= WHEN expr */
   -3,  /* (256) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
   -2,  /* (257) trigger_cmd_list ::= trigger_cmd SEMI */
   -3,  /* (258) trnm ::= nm DOT nm */
   -3,  /* (259) tridxby ::= INDEXED BY nm */
   -2,  /* (260) tridxby ::= NOT INDEXED */
   -9,  /* (261) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
   -8,  /* (262) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
   -6,  /* (263) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
   -3,  /* (264) trigger_cmd ::= scanpt select scanpt */
   -4,  /* (265) expr ::= RAISE LP IGNORE RP */
   -6,  /* (266) expr ::= RAISE LP raisetype COMMA nm RP */
   -1,  /* (267) raisetype ::= ROLLBACK */
   -1,  /* (268) raisetype ::= ABORT */
   -1,  /* (269) raisetype ::= FAIL */
   -4,  /* (270) cmd ::= DROP TRIGGER ifexists fullname */
   -6,  /* (271) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
   -3,  /* (272) cmd ::= DETACH database_kw_opt expr */
    0,  /* (273) key_opt ::= */
   -2,  /* (274) key_opt ::= KEY expr */

   -1,  /* (275) cmd ::= REINDEX */
   -3,  /* (276) cmd ::= REINDEX nm dbnm */
   -1,  /* (277) cmd ::= ANALYZE */
   -3,  /* (278) cmd ::= ANALYZE nm dbnm */
   -6,  /* (279) cmd ::= ALTER TABLE fullname RENAME TO nm */
   -7,  /* (280) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
   -1,  /* (281) add_column_fullname ::= fullname */
   -8,  /* (282) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
   -1,  /* (283) cmd ::= create_vtab */
   -4,  /* (284) cmd ::= create_vtab LP vtabarglist RP */
   -8,  /* (285) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
    0,  /* (286) vtabarg ::= */
   -1,  /* (287) vtabargtoken ::= ANY */
   -3,  /* (288) vtabargtoken ::= lp anylist RP */
   -1,  /* (289) lp ::= LP */
   -2,  /* (290) with ::= WITH wqlist */
   -3,  /* (291) with ::= WITH RECURSIVE wqlist */
   -6,  /* (292) wqlist ::= nm eidlist_opt AS LP select RP */
   -8,  /* (293) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
   -1,  /* (294) windowdefn_list ::= windowdefn */
   -3,  /* (295) windowdefn_list ::= windowdefn_list COMMA windowdefn */
   -5,  /* (296) windowdefn ::= nm AS LP window RP */
   -5,  /* (297) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
   -6,  /* (298) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
   -4,  /* (299) window ::= ORDER BY sortlist frame_opt */
   -5,  /* (300) window ::= nm ORDER BY sortlist frame_opt */
   -1,  /* (301) window ::= frame_opt */
   -2,  /* (302) window ::= nm frame_opt */
    0,  /* (303) frame_opt ::= */
   -3,  /* (304) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
   -6,  /* (305) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
   -1,  /* (306) range_or_rows ::= RANGE|ROWS|GROUPS */
   -1,  /* (307) frame_bound_s ::= frame_bound */
   -2,  /* (308) frame_bound_s ::= UNBOUNDED PRECEDING */
   -1,  /* (309) frame_bound_e ::= frame_bound */
   -2,  /* (310) frame_bound_e ::= UNBOUNDED FOLLOWING */
   -2,  /* (311) frame_bound ::= expr PRECEDING|FOLLOWING */
   -2,  /* (312) frame_bound ::= CURRENT ROW */
    0,  /* (313) frame_exclude_opt ::= */
   -2,  /* (314) frame_exclude_opt ::= EXCLUDE frame_exclude */
   -2,  /* (315) frame_exclude ::= NO OTHERS */
   -2,  /* (316) frame_exclude ::= CURRENT ROW */
   -1,  /* (317) frame_exclude ::= GROUP|TIES */
   -2,  /* (318) window_clause ::= WINDOW windowdefn_list */
   -2,  /* (319) filter_over ::= filter_clause over_clause */
   -1,  /* (320) filter_over ::= over_clause */
   -1,  /* (321) filter_over ::= filter_clause */
   -4,  /* (322) over_clause ::= OVER LP window RP */
   -2,  /* (323) over_clause ::= OVER nm */
   -5,  /* (324) filter_clause ::= FILTER LP WHERE expr RP */
   -1,  /* (325) input ::= cmdlist */
   -2,  /* (326) cmdlist ::= cmdlist ecmd */
   -1,  /* (327) cmdlist ::= ecmd */
   -1,  /* (328) ecmd ::= SEMI */
   -2,  /* (329) ecmd ::= cmdx SEMI */
   -3,  /* (330) ecmd ::= explain cmdx SEMI */
    0,  /* (331) trans_opt ::= */
   -1,  /* (332) trans_opt ::= TRANSACTION */
   -2,  /* (333) trans_opt ::= TRANSACTION nm */
   -1,  /* (334) savepoint_opt ::= SAVEPOINT */
    0,  /* (335) savepoint_opt ::= */
   -2,  /* (336) cmd ::= create_table create_table_args */
   -4,  /* (337) columnlist ::= columnlist COMMA columnname carglist */
   -2,  /* (338) columnlist ::= columnname carglist */
   -1,  /* (339) nm ::= ID|INDEXED */
   -1,  /* (340) nm ::= STRING */
   -1,  /* (341) nm ::= JOIN_KW */

   -1,  /* (342) typetoken ::= typename */
   -1,  /* (343) typename ::= ID|STRING */
   -1,  /* (344) signed ::= plus_num */
   -1,  /* (345) signed ::= minus_num */
   -2,  /* (346) carglist ::= carglist ccons */
    0,  /* (347) carglist ::= */
   -2,  /* (348) ccons ::= NULL onconf */
   -4,  /* (349) ccons ::= GENERATED ALWAYS AS generated */
   -2,  /* (350) ccons ::= AS generated */
   -2,  /* (351) conslist_opt ::= COMMA conslist */
   -3,  /* (352) conslist ::= conslist tconscomma tcons */
   -1,  /* (353) conslist ::= tcons */
    0,  /* (354) tconscomma ::= */
   -1,  /* (355) defer_subclause_opt ::= defer_subclause */
   -1,  /* (356) resolvetype ::= raisetype */
   -1,  /* (357) selectnowith ::= oneselect */
   -1,  /* (358) oneselect ::= values */
   -2,  /* (359) sclp ::= selcollist COMMA */
   -1,  /* (360) as ::= ID|STRING */
   -1,  /* (361) expr ::= term */
   -1,  /* (362) likeop ::= LIKE_KW|MATCH */
   -1,  /* (363) exprlist ::= nexprlist */
   -1,  /* (364) nmnum ::= plus_num */
   -1,  /* (365) nmnum ::= nm */
   -1,  /* (366) nmnum ::= ON */
   -1,  /* (367) nmnum ::= DELETE */
   -1,  /* (368) nmnum ::= DEFAULT */

   -1,  /* (369) plus_num ::= INTEGER|FLOAT */
    0,  /* (370) foreach_clause ::= */
   -3,  /* (371) foreach_clause ::= FOR EACH ROW */
   -1,  /* (372) trnm ::= nm */
    0,  /* (373) tridxby ::= */
   -1,  /* (374) database_kw_opt ::= DATABASE */
    0,  /* (375) database_kw_opt ::= */
    0,  /* (376) kwcolumn_opt ::= */
   -1,  /* (377) kwcolumn_opt ::= COLUMNKW */
   -1,  /* (378) vtabarglist ::= vtabarg */
   -3,  /* (379) vtabarglist ::= vtabarglist COMMA vtabarg */
   -2,  /* (380) vtabarg ::= vtabarg vtabargtoken */
    0,  /* (381) anylist ::= */
   -4,  /* (382) anylist ::= anylist LP anylist RP */
   -2,  /* (383) anylist ::= anylist ANY */
    0,  /* (384) with ::= */
};

static void yy_accept(yyParser*);  /* Forward Declaration */

/*
** Perform a reduce action and the shift that must immediately
** follow the reduce.







|
|

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







159179
159180
159181
159182
159183
159184
159185
159186
159187
159188
159189
159190
159191
159192
159193
159194
159195
159196
159197
159198
159199
159200
159201
159202
159203
159204
159205
159206
159207
159208
159209
159210
159211
159212

159213
159214
159215
159216
159217
159218
159219
159220
159221
159222
159223
159224
159225
159226
159227
159228
159229
159230
159231
159232
159233
159234
159235
159236
159237
159238
159239
159240
159241
159242
159243
159244
159245
159246
159247
159248
159249
159250
159251
159252
159253
159254
159255
159256
159257
159258
159259
159260
159261
159262
159263
159264
159265
159266
159267
159268
159269
159270
159271
159272
159273
159274
159275
159276
159277
159278
159279
159280
159281
159282
159283
159284
159285
159286
159287
159288
159289
159290
159291
159292
159293
159294
159295
159296
159297
159298
159299
159300

159301
159302
159303
159304
159305
159306
159307
159308
159309
159310
159311
159312
159313
159314
159315
159316
159317
159318
159319
159320
159321
159322
159323
159324
159325
159326
159327
159328
159329
159330
159331
159332
159333
159334
159335
159336
159337
159338
159339
159340
159341
159342
159343
159344
159345
159346
159347
159348
159349
159350
159351
159352
159353
159354
159355
159356
159357
159358
159359
159360
159361
159362
159363
159364
159365
159366

159367
159368
159369
159370
159371
159372
159373
159374
159375
159376
159377
159378
159379
159380
159381
159382
159383
159384
159385
159386
159387
159388
159389
159390
159391

159392
159393
159394
159395
159396
159397
159398
159399
159400
159401
159402
159403
159404
159405
159406
159407
159408
159409
159410
159411
159412
159413
159414
159415
159416
159417
159418
159419
   -5,  /* (152) setlist ::= setlist COMMA nm EQ expr */
   -7,  /* (153) setlist ::= setlist COMMA LP idlist RP EQ expr */
   -3,  /* (154) setlist ::= nm EQ expr */
   -5,  /* (155) setlist ::= LP idlist RP EQ expr */
   -7,  /* (156) cmd ::= with insert_cmd INTO xfullname idlist_opt select upsert */
   -7,  /* (157) cmd ::= with insert_cmd INTO xfullname idlist_opt DEFAULT VALUES */
    0,  /* (158) upsert ::= */
  -12,  /* (159) upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
   -9,  /* (160) upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
   -4,  /* (161) upsert ::= ON CONFLICT DO NOTHING */
   -7,  /* (162) upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt */
   -2,  /* (163) insert_cmd ::= INSERT orconf */
   -1,  /* (164) insert_cmd ::= REPLACE */
    0,  /* (165) idlist_opt ::= */
   -3,  /* (166) idlist_opt ::= LP idlist RP */
   -3,  /* (167) idlist ::= idlist COMMA nm */
   -1,  /* (168) idlist ::= nm */
   -3,  /* (169) expr ::= LP expr RP */
   -1,  /* (170) expr ::= ID|INDEXED */
   -1,  /* (171) expr ::= JOIN_KW */
   -3,  /* (172) expr ::= nm DOT nm */
   -5,  /* (173) expr ::= nm DOT nm DOT nm */
   -1,  /* (174) term ::= NULL|FLOAT|BLOB */
   -1,  /* (175) term ::= STRING */
   -1,  /* (176) term ::= INTEGER */
   -1,  /* (177) expr ::= VARIABLE */
   -3,  /* (178) expr ::= expr COLLATE ID|STRING */
   -6,  /* (179) expr ::= CAST LP expr AS typetoken RP */
   -5,  /* (180) expr ::= ID|INDEXED LP distinct exprlist RP */
   -4,  /* (181) expr ::= ID|INDEXED LP STAR RP */
   -6,  /* (182) expr ::= ID|INDEXED LP distinct exprlist RP filter_over */
   -5,  /* (183) expr ::= ID|INDEXED LP STAR RP filter_over */
   -1,  /* (184) term ::= CTIME_KW */
   -5,  /* (185) expr ::= LP nexprlist COMMA expr RP */

   -3,  /* (186) expr ::= expr AND expr */
   -3,  /* (187) expr ::= expr OR expr */
   -3,  /* (188) expr ::= expr LT|GT|GE|LE expr */
   -3,  /* (189) expr ::= expr EQ|NE expr */
   -3,  /* (190) expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */
   -3,  /* (191) expr ::= expr PLUS|MINUS expr */
   -3,  /* (192) expr ::= expr STAR|SLASH|REM expr */
   -3,  /* (193) expr ::= expr CONCAT expr */
   -2,  /* (194) likeop ::= NOT LIKE_KW|MATCH */
   -3,  /* (195) expr ::= expr likeop expr */
   -5,  /* (196) expr ::= expr likeop expr ESCAPE expr */
   -2,  /* (197) expr ::= expr ISNULL|NOTNULL */
   -3,  /* (198) expr ::= expr NOT NULL */
   -3,  /* (199) expr ::= expr IS expr */
   -4,  /* (200) expr ::= expr IS NOT expr */
   -2,  /* (201) expr ::= NOT expr */
   -2,  /* (202) expr ::= BITNOT expr */
   -2,  /* (203) expr ::= PLUS|MINUS expr */
   -1,  /* (204) between_op ::= BETWEEN */
   -2,  /* (205) between_op ::= NOT BETWEEN */
   -5,  /* (206) expr ::= expr between_op expr AND expr */
   -1,  /* (207) in_op ::= IN */
   -2,  /* (208) in_op ::= NOT IN */
   -5,  /* (209) expr ::= expr in_op LP exprlist RP */
   -3,  /* (210) expr ::= LP select RP */
   -5,  /* (211) expr ::= expr in_op LP select RP */
   -5,  /* (212) expr ::= expr in_op nm dbnm paren_exprlist */
   -4,  /* (213) expr ::= EXISTS LP select RP */
   -5,  /* (214) expr ::= CASE case_operand case_exprlist case_else END */
   -5,  /* (215) case_exprlist ::= case_exprlist WHEN expr THEN expr */
   -4,  /* (216) case_exprlist ::= WHEN expr THEN expr */
   -2,  /* (217) case_else ::= ELSE expr */
    0,  /* (218) case_else ::= */
   -1,  /* (219) case_operand ::= expr */
    0,  /* (220) case_operand ::= */
    0,  /* (221) exprlist ::= */
   -3,  /* (222) nexprlist ::= nexprlist COMMA expr */
   -1,  /* (223) nexprlist ::= expr */
    0,  /* (224) paren_exprlist ::= */
   -3,  /* (225) paren_exprlist ::= LP exprlist RP */
  -12,  /* (226) cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
   -1,  /* (227) uniqueflag ::= UNIQUE */
    0,  /* (228) uniqueflag ::= */
    0,  /* (229) eidlist_opt ::= */
   -3,  /* (230) eidlist_opt ::= LP eidlist RP */
   -5,  /* (231) eidlist ::= eidlist COMMA nm collate sortorder */
   -3,  /* (232) eidlist ::= nm collate sortorder */
    0,  /* (233) collate ::= */
   -2,  /* (234) collate ::= COLLATE ID|STRING */
   -4,  /* (235) cmd ::= DROP INDEX ifexists fullname */
   -2,  /* (236) cmd ::= VACUUM vinto */
   -3,  /* (237) cmd ::= VACUUM nm vinto */
   -2,  /* (238) vinto ::= INTO expr */
    0,  /* (239) vinto ::= */
   -3,  /* (240) cmd ::= PRAGMA nm dbnm */
   -5,  /* (241) cmd ::= PRAGMA nm dbnm EQ nmnum */
   -6,  /* (242) cmd ::= PRAGMA nm dbnm LP nmnum RP */
   -5,  /* (243) cmd ::= PRAGMA nm dbnm EQ minus_num */
   -6,  /* (244) cmd ::= PRAGMA nm dbnm LP minus_num RP */
   -2,  /* (245) plus_num ::= PLUS INTEGER|FLOAT */
   -2,  /* (246) minus_num ::= MINUS INTEGER|FLOAT */
   -5,  /* (247) cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
  -11,  /* (248) trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
   -1,  /* (249) trigger_time ::= BEFORE|AFTER */
   -2,  /* (250) trigger_time ::= INSTEAD OF */
    0,  /* (251) trigger_time ::= */
   -1,  /* (252) trigger_event ::= DELETE|INSERT */
   -1,  /* (253) trigger_event ::= UPDATE */
   -3,  /* (254) trigger_event ::= UPDATE OF idlist */
    0,  /* (255) when_clause ::= */
   -2,  /* (256) when_clause ::= WHEN expr */
   -3,  /* (257) trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
   -2,  /* (258) trigger_cmd_list ::= trigger_cmd SEMI */
   -3,  /* (259) trnm ::= nm DOT nm */
   -3,  /* (260) tridxby ::= INDEXED BY nm */
   -2,  /* (261) tridxby ::= NOT INDEXED */
   -9,  /* (262) trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
   -8,  /* (263) trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
   -6,  /* (264) trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
   -3,  /* (265) trigger_cmd ::= scanpt select scanpt */
   -4,  /* (266) expr ::= RAISE LP IGNORE RP */
   -6,  /* (267) expr ::= RAISE LP raisetype COMMA nm RP */
   -1,  /* (268) raisetype ::= ROLLBACK */
   -1,  /* (269) raisetype ::= ABORT */
   -1,  /* (270) raisetype ::= FAIL */
   -4,  /* (271) cmd ::= DROP TRIGGER ifexists fullname */
   -6,  /* (272) cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
   -3,  /* (273) cmd ::= DETACH database_kw_opt expr */

    0,  /* (274) key_opt ::= */
   -2,  /* (275) key_opt ::= KEY expr */
   -1,  /* (276) cmd ::= REINDEX */
   -3,  /* (277) cmd ::= REINDEX nm dbnm */
   -1,  /* (278) cmd ::= ANALYZE */
   -3,  /* (279) cmd ::= ANALYZE nm dbnm */
   -6,  /* (280) cmd ::= ALTER TABLE fullname RENAME TO nm */
   -7,  /* (281) cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
   -1,  /* (282) add_column_fullname ::= fullname */
   -8,  /* (283) cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
   -1,  /* (284) cmd ::= create_vtab */
   -4,  /* (285) cmd ::= create_vtab LP vtabarglist RP */
   -8,  /* (286) create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
    0,  /* (287) vtabarg ::= */
   -1,  /* (288) vtabargtoken ::= ANY */
   -3,  /* (289) vtabargtoken ::= lp anylist RP */
   -1,  /* (290) lp ::= LP */
   -2,  /* (291) with ::= WITH wqlist */
   -3,  /* (292) with ::= WITH RECURSIVE wqlist */
   -6,  /* (293) wqlist ::= nm eidlist_opt AS LP select RP */
   -8,  /* (294) wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
   -1,  /* (295) windowdefn_list ::= windowdefn */
   -3,  /* (296) windowdefn_list ::= windowdefn_list COMMA windowdefn */
   -5,  /* (297) windowdefn ::= nm AS LP window RP */
   -5,  /* (298) window ::= PARTITION BY nexprlist orderby_opt frame_opt */
   -6,  /* (299) window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
   -4,  /* (300) window ::= ORDER BY sortlist frame_opt */
   -5,  /* (301) window ::= nm ORDER BY sortlist frame_opt */
   -1,  /* (302) window ::= frame_opt */
   -2,  /* (303) window ::= nm frame_opt */
    0,  /* (304) frame_opt ::= */
   -3,  /* (305) frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
   -6,  /* (306) frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
   -1,  /* (307) range_or_rows ::= RANGE|ROWS|GROUPS */
   -1,  /* (308) frame_bound_s ::= frame_bound */
   -2,  /* (309) frame_bound_s ::= UNBOUNDED PRECEDING */
   -1,  /* (310) frame_bound_e ::= frame_bound */
   -2,  /* (311) frame_bound_e ::= UNBOUNDED FOLLOWING */
   -2,  /* (312) frame_bound ::= expr PRECEDING|FOLLOWING */
   -2,  /* (313) frame_bound ::= CURRENT ROW */
    0,  /* (314) frame_exclude_opt ::= */
   -2,  /* (315) frame_exclude_opt ::= EXCLUDE frame_exclude */
   -2,  /* (316) frame_exclude ::= NO OTHERS */
   -2,  /* (317) frame_exclude ::= CURRENT ROW */
   -1,  /* (318) frame_exclude ::= GROUP|TIES */
   -2,  /* (319) window_clause ::= WINDOW windowdefn_list */
   -2,  /* (320) filter_over ::= filter_clause over_clause */
   -1,  /* (321) filter_over ::= over_clause */
   -1,  /* (322) filter_over ::= filter_clause */
   -4,  /* (323) over_clause ::= OVER LP window RP */
   -2,  /* (324) over_clause ::= OVER nm */
   -5,  /* (325) filter_clause ::= FILTER LP WHERE expr RP */
   -1,  /* (326) input ::= cmdlist */
   -2,  /* (327) cmdlist ::= cmdlist ecmd */
   -1,  /* (328) cmdlist ::= ecmd */
   -1,  /* (329) ecmd ::= SEMI */
   -2,  /* (330) ecmd ::= cmdx SEMI */
   -3,  /* (331) ecmd ::= explain cmdx SEMI */
    0,  /* (332) trans_opt ::= */
   -1,  /* (333) trans_opt ::= TRANSACTION */
   -2,  /* (334) trans_opt ::= TRANSACTION nm */
   -1,  /* (335) savepoint_opt ::= SAVEPOINT */
    0,  /* (336) savepoint_opt ::= */
   -2,  /* (337) cmd ::= create_table create_table_args */
   -4,  /* (338) columnlist ::= columnlist COMMA columnname carglist */
   -2,  /* (339) columnlist ::= columnname carglist */

   -1,  /* (340) nm ::= ID|INDEXED */
   -1,  /* (341) nm ::= STRING */
   -1,  /* (342) nm ::= JOIN_KW */
   -1,  /* (343) typetoken ::= typename */
   -1,  /* (344) typename ::= ID|STRING */
   -1,  /* (345) signed ::= plus_num */
   -1,  /* (346) signed ::= minus_num */
   -2,  /* (347) carglist ::= carglist ccons */
    0,  /* (348) carglist ::= */
   -2,  /* (349) ccons ::= NULL onconf */
   -4,  /* (350) ccons ::= GENERATED ALWAYS AS generated */
   -2,  /* (351) ccons ::= AS generated */
   -2,  /* (352) conslist_opt ::= COMMA conslist */
   -3,  /* (353) conslist ::= conslist tconscomma tcons */
   -1,  /* (354) conslist ::= tcons */
    0,  /* (355) tconscomma ::= */
   -1,  /* (356) defer_subclause_opt ::= defer_subclause */
   -1,  /* (357) resolvetype ::= raisetype */
   -1,  /* (358) selectnowith ::= oneselect */
   -1,  /* (359) oneselect ::= values */
   -2,  /* (360) sclp ::= selcollist COMMA */
   -1,  /* (361) as ::= ID|STRING */
   -1,  /* (362) expr ::= term */
   -1,  /* (363) likeop ::= LIKE_KW|MATCH */
   -1,  /* (364) exprlist ::= nexprlist */

   -1,  /* (365) nmnum ::= plus_num */
   -1,  /* (366) nmnum ::= nm */
   -1,  /* (367) nmnum ::= ON */
   -1,  /* (368) nmnum ::= DELETE */
   -1,  /* (369) nmnum ::= DEFAULT */
   -1,  /* (370) plus_num ::= INTEGER|FLOAT */
    0,  /* (371) foreach_clause ::= */
   -3,  /* (372) foreach_clause ::= FOR EACH ROW */
   -1,  /* (373) trnm ::= nm */
    0,  /* (374) tridxby ::= */
   -1,  /* (375) database_kw_opt ::= DATABASE */
    0,  /* (376) database_kw_opt ::= */
    0,  /* (377) kwcolumn_opt ::= */
   -1,  /* (378) kwcolumn_opt ::= COLUMNKW */
   -1,  /* (379) vtabarglist ::= vtabarg */
   -3,  /* (380) vtabarglist ::= vtabarglist COMMA vtabarg */
   -2,  /* (381) vtabarg ::= vtabarg vtabargtoken */
    0,  /* (382) anylist ::= */
   -4,  /* (383) anylist ::= anylist LP anylist RP */
   -2,  /* (384) anylist ::= anylist ANY */
    0,  /* (385) with ::= */
};

static void yy_accept(yyParser*);  /* Forward Declaration */

/*
** Perform a reduce action and the shift that must immediately
** follow the reduce.
158276
158277
158278
158279
158280
158281
158282
158283
158284
158285
158286
158287
158288
158289
158290
158291
158292
158293
158294
158295
158296
158297
158298
158299
158300
158301
158302
158303
158304
158305
158306
158307
158308
158309
158310
158311
158312
158313
158314
158315
158316
158317
158318
158319
158320
158321
158322
158323
158324
158325
158326
158327
158328
158329
158330
158331
158332
158333
158334
158335
158336
158337
158338
  YYACTIONTYPE yyact;             /* The next action */
  yyStackEntry *yymsp;            /* The top of the parser's stack */
  int yysize;                     /* Amount to pop the stack */
  sqlite3ParserARG_FETCH
  (void)yyLookahead;
  (void)yyLookaheadToken;
  yymsp = yypParser->yytos;
  assert( yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) );
#ifndef NDEBUG
  if( yyTraceFILE ){
    yysize = yyRuleInfoNRhs[yyruleno];
    if( yysize ){
      fprintf(yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n",
        yyTracePrompt,
        yyruleno, yyRuleName[yyruleno],
        yyruleno<YYNRULE_WITH_ACTION ? "" : " without external action",
        yymsp[yysize].stateno);
    }else{
      fprintf(yyTraceFILE, "%sReduce %d [%s]%s.\n",
        yyTracePrompt, yyruleno, yyRuleName[yyruleno],
        yyruleno<YYNRULE_WITH_ACTION ? "" : " without external action");
    }
  }
#endif /* NDEBUG */

  /* Check that the stack is large enough to grow by a single entry
  ** if the RHS of the rule is empty.  This ensures that there is room
  ** enough on the stack to push the LHS value */
  if( yyRuleInfoNRhs[yyruleno]==0 ){
#ifdef YYTRACKMAXSTACKDEPTH
    if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
      yypParser->yyhwm++;
      assert( yypParser->yyhwm == (int)(yypParser->yytos - yypParser->yystack));
    }
#endif
#if YYSTACKDEPTH>0
    if( yypParser->yytos>=yypParser->yystackEnd ){
      yyStackOverflow(yypParser);
      /* The call to yyStackOverflow() above pops the stack until it is
      ** empty, causing the main parser loop to exit.  So the return value
      ** is never used and does not matter. */
      return 0;
    }
#else
    if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){
      if( yyGrowStack(yypParser) ){
        yyStackOverflow(yypParser);
        /* The call to yyStackOverflow() above pops the stack until it is
        ** empty, causing the main parser loop to exit.  So the return value
        ** is never used and does not matter. */
        return 0;
      }
      yymsp = yypParser->yytos;
    }
#endif
  }

  switch( yyruleno ){
  /* Beginning here are the reduction cases.  A typical example
  ** follows:
  **   case 0:
  **  #line <lineno> <grammarfile>
  **     { ... }           // User supplied code







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







159435
159436
159437
159438
159439
159440
159441

















































159442
159443
159444
159445
159446
159447
159448
  YYACTIONTYPE yyact;             /* The next action */
  yyStackEntry *yymsp;            /* The top of the parser's stack */
  int yysize;                     /* Amount to pop the stack */
  sqlite3ParserARG_FETCH
  (void)yyLookahead;
  (void)yyLookaheadToken;
  yymsp = yypParser->yytos;


















































  switch( yyruleno ){
  /* Beginning here are the reduction cases.  A typical example
  ** follows:
  **   case 0:
  **  #line <lineno> <grammarfile>
  **     { ... }           // User supplied code
158355
158356
158357
158358
158359
158360
158361
158362
158363
158364
158365
158366
158367
158368
158369
        break;
      case 4: /* transtype ::= */
{yymsp[1].minor.yy192 = TK_DEFERRED;}
        break;
      case 5: /* transtype ::= DEFERRED */
      case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6);
      case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7);
      case 306: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==306);
{yymsp[0].minor.yy192 = yymsp[0].major; /*A-overwrites-X*/}
        break;
      case 8: /* cmd ::= COMMIT|END trans_opt */
      case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9);
{sqlite3EndTransaction(pParse,yymsp[-1].major);}
        break;
      case 10: /* cmd ::= SAVEPOINT nm */







|







159465
159466
159467
159468
159469
159470
159471
159472
159473
159474
159475
159476
159477
159478
159479
        break;
      case 4: /* transtype ::= */
{yymsp[1].minor.yy192 = TK_DEFERRED;}
        break;
      case 5: /* transtype ::= DEFERRED */
      case 6: /* transtype ::= IMMEDIATE */ yytestcase(yyruleno==6);
      case 7: /* transtype ::= EXCLUSIVE */ yytestcase(yyruleno==7);
      case 307: /* range_or_rows ::= RANGE|ROWS|GROUPS */ yytestcase(yyruleno==307);
{yymsp[0].minor.yy192 = yymsp[0].major; /*A-overwrites-X*/}
        break;
      case 8: /* cmd ::= COMMIT|END trans_opt */
      case 9: /* cmd ::= ROLLBACK trans_opt */ yytestcase(yyruleno==9);
{sqlite3EndTransaction(pParse,yymsp[-1].major);}
        break;
      case 10: /* cmd ::= SAVEPOINT nm */
158393
158394
158395
158396
158397
158398
158399
158400
158401
158402
158403
158404
158405
158406
158407
      case 18: /* temp ::= */ yytestcase(yyruleno==18);
      case 21: /* table_options ::= */ yytestcase(yyruleno==21);
      case 45: /* autoinc ::= */ yytestcase(yyruleno==45);
      case 60: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==60);
      case 70: /* defer_subclause_opt ::= */ yytestcase(yyruleno==70);
      case 79: /* ifexists ::= */ yytestcase(yyruleno==79);
      case 96: /* distinct ::= */ yytestcase(yyruleno==96);
      case 232: /* collate ::= */ yytestcase(yyruleno==232);
{yymsp[1].minor.yy192 = 0;}
        break;
      case 16: /* ifnotexists ::= IF NOT EXISTS */
{yymsp[-2].minor.yy192 = 1;}
        break;
      case 17: /* temp ::= TEMP */
      case 46: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==46);







|







159503
159504
159505
159506
159507
159508
159509
159510
159511
159512
159513
159514
159515
159516
159517
      case 18: /* temp ::= */ yytestcase(yyruleno==18);
      case 21: /* table_options ::= */ yytestcase(yyruleno==21);
      case 45: /* autoinc ::= */ yytestcase(yyruleno==45);
      case 60: /* init_deferred_pred_opt ::= */ yytestcase(yyruleno==60);
      case 70: /* defer_subclause_opt ::= */ yytestcase(yyruleno==70);
      case 79: /* ifexists ::= */ yytestcase(yyruleno==79);
      case 96: /* distinct ::= */ yytestcase(yyruleno==96);
      case 233: /* collate ::= */ yytestcase(yyruleno==233);
{yymsp[1].minor.yy192 = 0;}
        break;
      case 16: /* ifnotexists ::= IF NOT EXISTS */
{yymsp[-2].minor.yy192 = 1;}
        break;
      case 17: /* temp ::= TEMP */
      case 46: /* autoinc ::= AUTOINCR */ yytestcase(yyruleno==46);
158552
158553
158554
158555
158556
158557
158558
158559
158560
158561
158562
158563
158564
158565
158566
158567
158568
158569
158570
158571
158572
158573
{ yymsp[-1].minor.yy192 = OE_None;     /* EV: R-33326-45252 */}
        break;
      case 58: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
{yymsp[-2].minor.yy192 = 0;}
        break;
      case 59: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
      case 74: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==74);
      case 162: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==162);
{yymsp[-1].minor.yy192 = yymsp[0].minor.yy192;}
        break;
      case 61: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */
      case 78: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==78);
      case 204: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==204);
      case 207: /* in_op ::= NOT IN */ yytestcase(yyruleno==207);
      case 233: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==233);
{yymsp[-1].minor.yy192 = 1;}
        break;
      case 62: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
{yymsp[-1].minor.yy192 = 0;}
        break;
      case 64: /* tconscomma ::= COMMA */
{pParse->constraintName.n = 0;}







|




|
|
|







159662
159663
159664
159665
159666
159667
159668
159669
159670
159671
159672
159673
159674
159675
159676
159677
159678
159679
159680
159681
159682
159683
{ yymsp[-1].minor.yy192 = OE_None;     /* EV: R-33326-45252 */}
        break;
      case 58: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */
{yymsp[-2].minor.yy192 = 0;}
        break;
      case 59: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */
      case 74: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==74);
      case 163: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==163);
{yymsp[-1].minor.yy192 = yymsp[0].minor.yy192;}
        break;
      case 61: /* init_deferred_pred_opt ::= INITIALLY DEFERRED */
      case 78: /* ifexists ::= IF EXISTS */ yytestcase(yyruleno==78);
      case 205: /* between_op ::= NOT BETWEEN */ yytestcase(yyruleno==205);
      case 208: /* in_op ::= NOT IN */ yytestcase(yyruleno==208);
      case 234: /* collate ::= COLLATE ID|STRING */ yytestcase(yyruleno==234);
{yymsp[-1].minor.yy192 = 1;}
        break;
      case 62: /* init_deferred_pred_opt ::= INITIALLY IMMEDIATE */
{yymsp[-1].minor.yy192 = 0;}
        break;
      case 64: /* tconscomma ::= COMMA */
{pParse->constraintName.n = 0;}
158595
158596
158597
158598
158599
158600
158601
158602
158603
158604
158605
158606
158607
158608
158609
      case 72: /* onconf ::= ON CONFLICT resolvetype */
{yymsp[-2].minor.yy192 = yymsp[0].minor.yy192;}
        break;
      case 75: /* resolvetype ::= IGNORE */
{yymsp[0].minor.yy192 = OE_Ignore;}
        break;
      case 76: /* resolvetype ::= REPLACE */
      case 163: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==163);
{yymsp[0].minor.yy192 = OE_Replace;}
        break;
      case 77: /* cmd ::= DROP TABLE ifexists fullname */
{
  sqlite3DropTable(pParse, yymsp[0].minor.yy47, 0, yymsp[-1].minor.yy192);
}
        break;







|







159705
159706
159707
159708
159709
159710
159711
159712
159713
159714
159715
159716
159717
159718
159719
      case 72: /* onconf ::= ON CONFLICT resolvetype */
{yymsp[-2].minor.yy192 = yymsp[0].minor.yy192;}
        break;
      case 75: /* resolvetype ::= IGNORE */
{yymsp[0].minor.yy192 = OE_Ignore;}
        break;
      case 76: /* resolvetype ::= REPLACE */
      case 164: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==164);
{yymsp[0].minor.yy192 = OE_Replace;}
        break;
      case 77: /* cmd ::= DROP TABLE ifexists fullname */
{
  sqlite3DropTable(pParse, yymsp[0].minor.yy47, 0, yymsp[-1].minor.yy192);
}
        break;
158727
158728
158729
158730
158731
158732
158733
158734
158735
158736
158737
158738
158739
158740
158741
158742
158743
        break;
      case 95: /* distinct ::= ALL */
{yymsp[0].minor.yy192 = SF_All;}
        break;
      case 97: /* sclp ::= */
      case 130: /* orderby_opt ::= */ yytestcase(yyruleno==130);
      case 140: /* groupby_opt ::= */ yytestcase(yyruleno==140);
      case 220: /* exprlist ::= */ yytestcase(yyruleno==220);
      case 223: /* paren_exprlist ::= */ yytestcase(yyruleno==223);
      case 228: /* eidlist_opt ::= */ yytestcase(yyruleno==228);
{yymsp[1].minor.yy242 = 0;}
        break;
      case 98: /* selcollist ::= sclp scanpt expr scanpt as */
{
   yymsp[-4].minor.yy242 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy242, yymsp[-2].minor.yy202);
   if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy242, &yymsp[0].minor.yy0, 1);
   sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy242,yymsp[-3].minor.yy436,yymsp[-1].minor.yy436);







|
|
|







159837
159838
159839
159840
159841
159842
159843
159844
159845
159846
159847
159848
159849
159850
159851
159852
159853
        break;
      case 95: /* distinct ::= ALL */
{yymsp[0].minor.yy192 = SF_All;}
        break;
      case 97: /* sclp ::= */
      case 130: /* orderby_opt ::= */ yytestcase(yyruleno==130);
      case 140: /* groupby_opt ::= */ yytestcase(yyruleno==140);
      case 221: /* exprlist ::= */ yytestcase(yyruleno==221);
      case 224: /* paren_exprlist ::= */ yytestcase(yyruleno==224);
      case 229: /* eidlist_opt ::= */ yytestcase(yyruleno==229);
{yymsp[1].minor.yy242 = 0;}
        break;
      case 98: /* selcollist ::= sclp scanpt expr scanpt as */
{
   yymsp[-4].minor.yy242 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy242, yymsp[-2].minor.yy202);
   if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yymsp[-4].minor.yy242, &yymsp[0].minor.yy0, 1);
   sqlite3ExprListSetSpan(pParse,yymsp[-4].minor.yy242,yymsp[-3].minor.yy436,yymsp[-1].minor.yy436);
158755
158756
158757
158758
158759
158760
158761
158762
158763
158764
158765
158766
158767
158768
158769
158770
  Expr *pLeft = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
  Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight);
  yymsp[-4].minor.yy242 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy242, pDot);
}
        break;
      case 101: /* as ::= AS nm */
      case 112: /* dbnm ::= DOT nm */ yytestcase(yyruleno==112);
      case 244: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==244);
      case 245: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==245);
{yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;}
        break;
      case 103: /* from ::= */
      case 106: /* stl_prefix ::= */ yytestcase(yyruleno==106);
{yymsp[1].minor.yy47 = 0;}
        break;
      case 104: /* from ::= FROM seltablist */







|
|







159865
159866
159867
159868
159869
159870
159871
159872
159873
159874
159875
159876
159877
159878
159879
159880
  Expr *pLeft = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
  Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight);
  yymsp[-4].minor.yy242 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy242, pDot);
}
        break;
      case 101: /* as ::= AS nm */
      case 112: /* dbnm ::= DOT nm */ yytestcase(yyruleno==112);
      case 245: /* plus_num ::= PLUS INTEGER|FLOAT */ yytestcase(yyruleno==245);
      case 246: /* minus_num ::= MINUS INTEGER|FLOAT */ yytestcase(yyruleno==246);
{yymsp[-1].minor.yy0 = yymsp[0].minor.yy0;}
        break;
      case 103: /* from ::= */
      case 106: /* stl_prefix ::= */ yytestcase(yyruleno==106);
{yymsp[1].minor.yy47 = 0;}
        break;
      case 104: /* from ::= FROM seltablist */
158872
158873
158874
158875
158876
158877
158878
158879
158880
158881
158882
158883
158884
158885
158886
158887
158888
158889
158890
158891
158892
158893
158894
158895
158896
158897
158898
158899
158900
158901
158902
158903
158904
158905
158906
158907
158908
158909
        break;
      case 122: /* joinop ::= JOIN_KW nm nm JOIN */
{yymsp[-3].minor.yy192 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/}
        break;
      case 123: /* on_opt ::= ON expr */
      case 143: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==143);
      case 150: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==150);
      case 216: /* case_else ::= ELSE expr */ yytestcase(yyruleno==216);
      case 237: /* vinto ::= INTO expr */ yytestcase(yyruleno==237);
{yymsp[-1].minor.yy202 = yymsp[0].minor.yy202;}
        break;
      case 124: /* on_opt ::= */
      case 142: /* having_opt ::= */ yytestcase(yyruleno==142);
      case 144: /* limit_opt ::= */ yytestcase(yyruleno==144);
      case 149: /* where_opt ::= */ yytestcase(yyruleno==149);
      case 217: /* case_else ::= */ yytestcase(yyruleno==217);
      case 219: /* case_operand ::= */ yytestcase(yyruleno==219);
      case 238: /* vinto ::= */ yytestcase(yyruleno==238);
{yymsp[1].minor.yy202 = 0;}
        break;
      case 126: /* indexed_opt ::= INDEXED BY nm */
{yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;}
        break;
      case 127: /* indexed_opt ::= NOT INDEXED */
{yymsp[-1].minor.yy0.z=0; yymsp[-1].minor.yy0.n=1;}
        break;
      case 128: /* using_opt ::= USING LP idlist RP */
{yymsp[-3].minor.yy600 = yymsp[-1].minor.yy600;}
        break;
      case 129: /* using_opt ::= */
      case 164: /* idlist_opt ::= */ yytestcase(yyruleno==164);
{yymsp[1].minor.yy600 = 0;}
        break;
      case 131: /* orderby_opt ::= ORDER BY sortlist */
      case 141: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==141);
{yymsp[-2].minor.yy242 = yymsp[0].minor.yy242;}
        break;
      case 132: /* sortlist ::= sortlist COMMA expr sortorder nulls */







|
|






|
|
|












|







159982
159983
159984
159985
159986
159987
159988
159989
159990
159991
159992
159993
159994
159995
159996
159997
159998
159999
160000
160001
160002
160003
160004
160005
160006
160007
160008
160009
160010
160011
160012
160013
160014
160015
160016
160017
160018
160019
        break;
      case 122: /* joinop ::= JOIN_KW nm nm JOIN */
{yymsp[-3].minor.yy192 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);/*X-overwrites-A*/}
        break;
      case 123: /* on_opt ::= ON expr */
      case 143: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==143);
      case 150: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==150);
      case 217: /* case_else ::= ELSE expr */ yytestcase(yyruleno==217);
      case 238: /* vinto ::= INTO expr */ yytestcase(yyruleno==238);
{yymsp[-1].minor.yy202 = yymsp[0].minor.yy202;}
        break;
      case 124: /* on_opt ::= */
      case 142: /* having_opt ::= */ yytestcase(yyruleno==142);
      case 144: /* limit_opt ::= */ yytestcase(yyruleno==144);
      case 149: /* where_opt ::= */ yytestcase(yyruleno==149);
      case 218: /* case_else ::= */ yytestcase(yyruleno==218);
      case 220: /* case_operand ::= */ yytestcase(yyruleno==220);
      case 239: /* vinto ::= */ yytestcase(yyruleno==239);
{yymsp[1].minor.yy202 = 0;}
        break;
      case 126: /* indexed_opt ::= INDEXED BY nm */
{yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;}
        break;
      case 127: /* indexed_opt ::= NOT INDEXED */
{yymsp[-1].minor.yy0.z=0; yymsp[-1].minor.yy0.n=1;}
        break;
      case 128: /* using_opt ::= USING LP idlist RP */
{yymsp[-3].minor.yy600 = yymsp[-1].minor.yy600;}
        break;
      case 129: /* using_opt ::= */
      case 165: /* idlist_opt ::= */ yytestcase(yyruleno==165);
{yymsp[1].minor.yy600 = 0;}
        break;
      case 131: /* orderby_opt ::= ORDER BY sortlist */
      case 141: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==141);
{yymsp[-2].minor.yy242 = yymsp[0].minor.yy242;}
        break;
      case 132: /* sortlist ::= sortlist COMMA expr sortorder nulls */
158989
158990
158991
158992
158993
158994
158995
158996
158997
158998
158999
159000
159001
159002
159003
159004



159005
159006
159007
159008
159009
159010
159011
159012
159013
159014
159015
159016
159017
159018
159019
159020
159021
159022
159023
159024
159025
159026
159027
159028
159029
159030
159031
159032
159033
159034
159035
159036
159037
159038
159039
159040
159041
159042
159043
159044
159045
159046
159047
159048
159049
159050
159051
159052
159053
159054
159055
159056
159057
159058
159059
159060
159061
159062
159063
159064
{
  sqlite3Insert(pParse, yymsp[-3].minor.yy47, 0, yymsp[-2].minor.yy600, yymsp[-5].minor.yy192, 0);
}
        break;
      case 158: /* upsert ::= */
{ yymsp[1].minor.yy318 = 0; }
        break;
      case 159: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt */
{ yymsp[-10].minor.yy318 = sqlite3UpsertNew(pParse->db,yymsp[-7].minor.yy242,yymsp[-5].minor.yy202,yymsp[-1].minor.yy242,yymsp[0].minor.yy202);}
        break;
      case 160: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING */
{ yymsp[-7].minor.yy318 = sqlite3UpsertNew(pParse->db,yymsp[-4].minor.yy242,yymsp[-2].minor.yy202,0,0); }
        break;
      case 161: /* upsert ::= ON CONFLICT DO NOTHING */
{ yymsp[-3].minor.yy318 = sqlite3UpsertNew(pParse->db,0,0,0,0); }
        break;



      case 165: /* idlist_opt ::= LP idlist RP */
{yymsp[-2].minor.yy600 = yymsp[-1].minor.yy600;}
        break;
      case 166: /* idlist ::= idlist COMMA nm */
{yymsp[-2].minor.yy600 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy600,&yymsp[0].minor.yy0);}
        break;
      case 167: /* idlist ::= nm */
{yymsp[0].minor.yy600 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
        break;
      case 168: /* expr ::= LP expr RP */
{yymsp[-2].minor.yy202 = yymsp[-1].minor.yy202;}
        break;
      case 169: /* expr ::= ID|INDEXED */
      case 170: /* expr ::= JOIN_KW */ yytestcase(yyruleno==170);
{yymsp[0].minor.yy202=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
        break;
      case 171: /* expr ::= nm DOT nm */
{
  Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
  Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1);
  if( IN_RENAME_OBJECT ){
    sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[0].minor.yy0);
    sqlite3RenameTokenMap(pParse, (void*)temp1, &yymsp[-2].minor.yy0);
  }
  yylhsminor.yy202 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
}
  yymsp[-2].minor.yy202 = yylhsminor.yy202;
        break;
      case 172: /* expr ::= nm DOT nm DOT nm */
{
  Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-4].minor.yy0, 1);
  Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
  Expr *temp3 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1);
  Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3);
  if( IN_RENAME_OBJECT ){
    sqlite3RenameTokenMap(pParse, (void*)temp3, &yymsp[0].minor.yy0);
    sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[-2].minor.yy0);
  }
  yylhsminor.yy202 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
}
  yymsp[-4].minor.yy202 = yylhsminor.yy202;
        break;
      case 173: /* term ::= NULL|FLOAT|BLOB */
      case 174: /* term ::= STRING */ yytestcase(yyruleno==174);
{yymsp[0].minor.yy202=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/}
        break;
      case 175: /* term ::= INTEGER */
{
  yylhsminor.yy202 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1);
}
  yymsp[0].minor.yy202 = yylhsminor.yy202;
        break;
      case 176: /* expr ::= VARIABLE */
{
  if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){
    u32 n = yymsp[0].minor.yy0.n;
    yymsp[0].minor.yy202 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0);
    sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy202, n);
  }else{
    /* When doing a nested parse, one can include terms in an expression







|
|

|
|


|

>
>
>
|


|


|


|


|
|


|











|













|
|


|





|







160099
160100
160101
160102
160103
160104
160105
160106
160107
160108
160109
160110
160111
160112
160113
160114
160115
160116
160117
160118
160119
160120
160121
160122
160123
160124
160125
160126
160127
160128
160129
160130
160131
160132
160133
160134
160135
160136
160137
160138
160139
160140
160141
160142
160143
160144
160145
160146
160147
160148
160149
160150
160151
160152
160153
160154
160155
160156
160157
160158
160159
160160
160161
160162
160163
160164
160165
160166
160167
160168
160169
160170
160171
160172
160173
160174
160175
160176
160177
{
  sqlite3Insert(pParse, yymsp[-3].minor.yy47, 0, yymsp[-2].minor.yy600, yymsp[-5].minor.yy192, 0);
}
        break;
      case 158: /* upsert ::= */
{ yymsp[1].minor.yy318 = 0; }
        break;
      case 159: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO UPDATE SET setlist where_opt upsert */
{ yymsp[-11].minor.yy318 = sqlite3UpsertNew(pParse->db,yymsp[-8].minor.yy242,yymsp[-6].minor.yy202,yymsp[-2].minor.yy242,yymsp[-1].minor.yy202,yymsp[0].minor.yy318);}
        break;
      case 160: /* upsert ::= ON CONFLICT LP sortlist RP where_opt DO NOTHING upsert */
{ yymsp[-8].minor.yy318 = sqlite3UpsertNew(pParse->db,yymsp[-5].minor.yy242,yymsp[-3].minor.yy202,0,0,yymsp[0].minor.yy318); }
        break;
      case 161: /* upsert ::= ON CONFLICT DO NOTHING */
{ yymsp[-3].minor.yy318 = sqlite3UpsertNew(pParse->db,0,0,0,0,0); }
        break;
      case 162: /* upsert ::= ON CONFLICT DO UPDATE SET setlist where_opt */
{ yymsp[-6].minor.yy318 = sqlite3UpsertNew(pParse->db,0,0,yymsp[-1].minor.yy242,yymsp[0].minor.yy202,0);}
        break;
      case 166: /* idlist_opt ::= LP idlist RP */
{yymsp[-2].minor.yy600 = yymsp[-1].minor.yy600;}
        break;
      case 167: /* idlist ::= idlist COMMA nm */
{yymsp[-2].minor.yy600 = sqlite3IdListAppend(pParse,yymsp[-2].minor.yy600,&yymsp[0].minor.yy0);}
        break;
      case 168: /* idlist ::= nm */
{yymsp[0].minor.yy600 = sqlite3IdListAppend(pParse,0,&yymsp[0].minor.yy0); /*A-overwrites-Y*/}
        break;
      case 169: /* expr ::= LP expr RP */
{yymsp[-2].minor.yy202 = yymsp[-1].minor.yy202;}
        break;
      case 170: /* expr ::= ID|INDEXED */
      case 171: /* expr ::= JOIN_KW */ yytestcase(yyruleno==171);
{yymsp[0].minor.yy202=tokenExpr(pParse,TK_ID,yymsp[0].minor.yy0); /*A-overwrites-X*/}
        break;
      case 172: /* expr ::= nm DOT nm */
{
  Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
  Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1);
  if( IN_RENAME_OBJECT ){
    sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[0].minor.yy0);
    sqlite3RenameTokenMap(pParse, (void*)temp1, &yymsp[-2].minor.yy0);
  }
  yylhsminor.yy202 = sqlite3PExpr(pParse, TK_DOT, temp1, temp2);
}
  yymsp[-2].minor.yy202 = yylhsminor.yy202;
        break;
      case 173: /* expr ::= nm DOT nm DOT nm */
{
  Expr *temp1 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-4].minor.yy0, 1);
  Expr *temp2 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[-2].minor.yy0, 1);
  Expr *temp3 = sqlite3ExprAlloc(pParse->db, TK_ID, &yymsp[0].minor.yy0, 1);
  Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3);
  if( IN_RENAME_OBJECT ){
    sqlite3RenameTokenMap(pParse, (void*)temp3, &yymsp[0].minor.yy0);
    sqlite3RenameTokenMap(pParse, (void*)temp2, &yymsp[-2].minor.yy0);
  }
  yylhsminor.yy202 = sqlite3PExpr(pParse, TK_DOT, temp1, temp4);
}
  yymsp[-4].minor.yy202 = yylhsminor.yy202;
        break;
      case 174: /* term ::= NULL|FLOAT|BLOB */
      case 175: /* term ::= STRING */ yytestcase(yyruleno==175);
{yymsp[0].minor.yy202=tokenExpr(pParse,yymsp[0].major,yymsp[0].minor.yy0); /*A-overwrites-X*/}
        break;
      case 176: /* term ::= INTEGER */
{
  yylhsminor.yy202 = sqlite3ExprAlloc(pParse->db, TK_INTEGER, &yymsp[0].minor.yy0, 1);
}
  yymsp[0].minor.yy202 = yylhsminor.yy202;
        break;
      case 177: /* expr ::= VARIABLE */
{
  if( !(yymsp[0].minor.yy0.z[0]=='#' && sqlite3Isdigit(yymsp[0].minor.yy0.z[1])) ){
    u32 n = yymsp[0].minor.yy0.n;
    yymsp[0].minor.yy202 = tokenExpr(pParse, TK_VARIABLE, yymsp[0].minor.yy0);
    sqlite3ExprAssignVarNumber(pParse, yymsp[0].minor.yy202, n);
  }else{
    /* When doing a nested parse, one can include terms in an expression
159072
159073
159074
159075
159076
159077
159078
159079
159080
159081
159082
159083
159084
159085
159086
159087
159088
159089
159090
159091
159092
159093
159094
159095
159096
159097
159098
159099
159100
159101
159102
159103
159104
159105
159106
159107
159108
159109
159110
159111
159112
159113
159114
159115
159116
159117
159118
159119
159120
159121
159122
159123
159124
159125
159126
159127
159128
159129
159130
159131
159132
159133
159134
159135
159136
159137
159138
159139
159140
159141
159142
159143
159144
159145
159146
159147
159148
159149
159150
159151
159152
159153
159154
159155
159156
159157
159158
159159
159160
159161
159162
159163
159164
159165
159166
159167
159168
159169
159170
159171
159172
159173
159174
159175
159176
159177
159178
159179
159180
159181
159182
159183
159184
159185
159186
159187
159188
159189
159190
159191
159192
159193
159194
159195
159196
159197
159198
159199
159200
159201
159202
159203
159204
159205
159206
159207
159208
159209
159210
159211
159212
159213
159214
159215
159216
159217
159218
159219
159220
159221
159222
159223
159224
159225
159226
159227
159228
    }else{
      yymsp[0].minor.yy202 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
      if( yymsp[0].minor.yy202 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy202->iTable);
    }
  }
}
        break;
      case 177: /* expr ::= expr COLLATE ID|STRING */
{
  yymsp[-2].minor.yy202 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy202, &yymsp[0].minor.yy0, 1);
}
        break;
      case 178: /* expr ::= CAST LP expr AS typetoken RP */
{
  yymsp[-5].minor.yy202 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1);
  sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy202, yymsp[-3].minor.yy202, 0);
}
        break;
      case 179: /* expr ::= ID|INDEXED LP distinct exprlist RP */
{
  yylhsminor.yy202 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy242, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy192);
}
  yymsp[-4].minor.yy202 = yylhsminor.yy202;
        break;
      case 180: /* expr ::= ID|INDEXED LP STAR RP */
{
  yylhsminor.yy202 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0);
}
  yymsp[-3].minor.yy202 = yylhsminor.yy202;
        break;
      case 181: /* expr ::= ID|INDEXED LP distinct exprlist RP filter_over */
{
  yylhsminor.yy202 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy242, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy192);
  sqlite3WindowAttach(pParse, yylhsminor.yy202, yymsp[0].minor.yy303);
}
  yymsp[-5].minor.yy202 = yylhsminor.yy202;
        break;
      case 182: /* expr ::= ID|INDEXED LP STAR RP filter_over */
{
  yylhsminor.yy202 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0);
  sqlite3WindowAttach(pParse, yylhsminor.yy202, yymsp[0].minor.yy303);
}
  yymsp[-4].minor.yy202 = yylhsminor.yy202;
        break;
      case 183: /* term ::= CTIME_KW */
{
  yylhsminor.yy202 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0);
}
  yymsp[0].minor.yy202 = yylhsminor.yy202;
        break;
      case 184: /* expr ::= LP nexprlist COMMA expr RP */
{
  ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy242, yymsp[-1].minor.yy202);
  yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
  if( yymsp[-4].minor.yy202 ){
    yymsp[-4].minor.yy202->x.pList = pList;
    if( ALWAYS(pList->nExpr) ){
      yymsp[-4].minor.yy202->flags |= pList->a[0].pExpr->flags & EP_Propagate;
    }
  }else{
    sqlite3ExprListDelete(pParse->db, pList);
  }
}
        break;
      case 185: /* expr ::= expr AND expr */
{yymsp[-2].minor.yy202=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy202,yymsp[0].minor.yy202);}
        break;
      case 186: /* expr ::= expr OR expr */
      case 187: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==187);
      case 188: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==188);
      case 189: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==189);
      case 190: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==190);
      case 191: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==191);
      case 192: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==192);
{yymsp[-2].minor.yy202=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy202,yymsp[0].minor.yy202);}
        break;
      case 193: /* likeop ::= NOT LIKE_KW|MATCH */
{yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/}
        break;
      case 194: /* expr ::= expr likeop expr */
{
  ExprList *pList;
  int bNot = yymsp[-1].minor.yy0.n & 0x80000000;
  yymsp[-1].minor.yy0.n &= 0x7fffffff;
  pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy202);
  pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy202);
  yymsp[-2].minor.yy202 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
  if( bNot ) yymsp[-2].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy202, 0);
  if( yymsp[-2].minor.yy202 ) yymsp[-2].minor.yy202->flags |= EP_InfixFunc;
}
        break;
      case 195: /* expr ::= expr likeop expr ESCAPE expr */
{
  ExprList *pList;
  int bNot = yymsp[-3].minor.yy0.n & 0x80000000;
  yymsp[-3].minor.yy0.n &= 0x7fffffff;
  pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy202);
  pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy202);
  pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy202);
  yymsp[-4].minor.yy202 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0);
  if( bNot ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0);
  if( yymsp[-4].minor.yy202 ) yymsp[-4].minor.yy202->flags |= EP_InfixFunc;
}
        break;
      case 196: /* expr ::= expr ISNULL|NOTNULL */
{yymsp[-1].minor.yy202 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy202,0);}
        break;
      case 197: /* expr ::= expr NOT NULL */
{yymsp[-2].minor.yy202 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy202,0);}
        break;
      case 198: /* expr ::= expr IS expr */
{
  yymsp[-2].minor.yy202 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy202,yymsp[0].minor.yy202);
  binaryToUnaryIfNull(pParse, yymsp[0].minor.yy202, yymsp[-2].minor.yy202, TK_ISNULL);
}
        break;
      case 199: /* expr ::= expr IS NOT expr */
{
  yymsp[-3].minor.yy202 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy202,yymsp[0].minor.yy202);
  binaryToUnaryIfNull(pParse, yymsp[0].minor.yy202, yymsp[-3].minor.yy202, TK_NOTNULL);
}
        break;
      case 200: /* expr ::= NOT expr */
      case 201: /* expr ::= BITNOT expr */ yytestcase(yyruleno==201);
{yymsp[-1].minor.yy202 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy202, 0);/*A-overwrites-B*/}
        break;
      case 202: /* expr ::= PLUS|MINUS expr */
{
  yymsp[-1].minor.yy202 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy202, 0);
  /*A-overwrites-B*/
}
        break;
      case 203: /* between_op ::= BETWEEN */
      case 206: /* in_op ::= IN */ yytestcase(yyruleno==206);
{yymsp[0].minor.yy192 = 0;}
        break;
      case 205: /* expr ::= expr between_op expr AND expr */
{
  ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy202);
  pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy202);
  yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy202, 0);
  if( yymsp[-4].minor.yy202 ){
    yymsp[-4].minor.yy202->x.pList = pList;
  }else{
    sqlite3ExprListDelete(pParse->db, pList);
  }
  if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0);
}
        break;
      case 208: /* expr ::= expr in_op LP exprlist RP */
{
    if( yymsp[-1].minor.yy242==0 ){
      /* Expressions of the form
      **
      **      expr1 IN ()
      **      expr1 NOT IN ()
      **







|




|





|





|





|






|






|





|













|


|
|
|
|
|
|
|


|


|











|












|


|


|





|





|
|


|





|
|


|












|







160185
160186
160187
160188
160189
160190
160191
160192
160193
160194
160195
160196
160197
160198
160199
160200
160201
160202
160203
160204
160205
160206
160207
160208
160209
160210
160211
160212
160213
160214
160215
160216
160217
160218
160219
160220
160221
160222
160223
160224
160225
160226
160227
160228
160229
160230
160231
160232
160233
160234
160235
160236
160237
160238
160239
160240
160241
160242
160243
160244
160245
160246
160247
160248
160249
160250
160251
160252
160253
160254
160255
160256
160257
160258
160259
160260
160261
160262
160263
160264
160265
160266
160267
160268
160269
160270
160271
160272
160273
160274
160275
160276
160277
160278
160279
160280
160281
160282
160283
160284
160285
160286
160287
160288
160289
160290
160291
160292
160293
160294
160295
160296
160297
160298
160299
160300
160301
160302
160303
160304
160305
160306
160307
160308
160309
160310
160311
160312
160313
160314
160315
160316
160317
160318
160319
160320
160321
160322
160323
160324
160325
160326
160327
160328
160329
160330
160331
160332
160333
160334
160335
160336
160337
160338
160339
160340
160341
    }else{
      yymsp[0].minor.yy202 = sqlite3PExpr(pParse, TK_REGISTER, 0, 0);
      if( yymsp[0].minor.yy202 ) sqlite3GetInt32(&t.z[1], &yymsp[0].minor.yy202->iTable);
    }
  }
}
        break;
      case 178: /* expr ::= expr COLLATE ID|STRING */
{
  yymsp[-2].minor.yy202 = sqlite3ExprAddCollateToken(pParse, yymsp[-2].minor.yy202, &yymsp[0].minor.yy0, 1);
}
        break;
      case 179: /* expr ::= CAST LP expr AS typetoken RP */
{
  yymsp[-5].minor.yy202 = sqlite3ExprAlloc(pParse->db, TK_CAST, &yymsp[-1].minor.yy0, 1);
  sqlite3ExprAttachSubtrees(pParse->db, yymsp[-5].minor.yy202, yymsp[-3].minor.yy202, 0);
}
        break;
      case 180: /* expr ::= ID|INDEXED LP distinct exprlist RP */
{
  yylhsminor.yy202 = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy242, &yymsp[-4].minor.yy0, yymsp[-2].minor.yy192);
}
  yymsp[-4].minor.yy202 = yylhsminor.yy202;
        break;
      case 181: /* expr ::= ID|INDEXED LP STAR RP */
{
  yylhsminor.yy202 = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0, 0);
}
  yymsp[-3].minor.yy202 = yylhsminor.yy202;
        break;
      case 182: /* expr ::= ID|INDEXED LP distinct exprlist RP filter_over */
{
  yylhsminor.yy202 = sqlite3ExprFunction(pParse, yymsp[-2].minor.yy242, &yymsp[-5].minor.yy0, yymsp[-3].minor.yy192);
  sqlite3WindowAttach(pParse, yylhsminor.yy202, yymsp[0].minor.yy303);
}
  yymsp[-5].minor.yy202 = yylhsminor.yy202;
        break;
      case 183: /* expr ::= ID|INDEXED LP STAR RP filter_over */
{
  yylhsminor.yy202 = sqlite3ExprFunction(pParse, 0, &yymsp[-4].minor.yy0, 0);
  sqlite3WindowAttach(pParse, yylhsminor.yy202, yymsp[0].minor.yy303);
}
  yymsp[-4].minor.yy202 = yylhsminor.yy202;
        break;
      case 184: /* term ::= CTIME_KW */
{
  yylhsminor.yy202 = sqlite3ExprFunction(pParse, 0, &yymsp[0].minor.yy0, 0);
}
  yymsp[0].minor.yy202 = yylhsminor.yy202;
        break;
      case 185: /* expr ::= LP nexprlist COMMA expr RP */
{
  ExprList *pList = sqlite3ExprListAppend(pParse, yymsp[-3].minor.yy242, yymsp[-1].minor.yy202);
  yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_VECTOR, 0, 0);
  if( yymsp[-4].minor.yy202 ){
    yymsp[-4].minor.yy202->x.pList = pList;
    if( ALWAYS(pList->nExpr) ){
      yymsp[-4].minor.yy202->flags |= pList->a[0].pExpr->flags & EP_Propagate;
    }
  }else{
    sqlite3ExprListDelete(pParse->db, pList);
  }
}
        break;
      case 186: /* expr ::= expr AND expr */
{yymsp[-2].minor.yy202=sqlite3ExprAnd(pParse,yymsp[-2].minor.yy202,yymsp[0].minor.yy202);}
        break;
      case 187: /* expr ::= expr OR expr */
      case 188: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==188);
      case 189: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==189);
      case 190: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==190);
      case 191: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==191);
      case 192: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==192);
      case 193: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==193);
{yymsp[-2].minor.yy202=sqlite3PExpr(pParse,yymsp[-1].major,yymsp[-2].minor.yy202,yymsp[0].minor.yy202);}
        break;
      case 194: /* likeop ::= NOT LIKE_KW|MATCH */
{yymsp[-1].minor.yy0=yymsp[0].minor.yy0; yymsp[-1].minor.yy0.n|=0x80000000; /*yymsp[-1].minor.yy0-overwrite-yymsp[0].minor.yy0*/}
        break;
      case 195: /* expr ::= expr likeop expr */
{
  ExprList *pList;
  int bNot = yymsp[-1].minor.yy0.n & 0x80000000;
  yymsp[-1].minor.yy0.n &= 0x7fffffff;
  pList = sqlite3ExprListAppend(pParse,0, yymsp[0].minor.yy202);
  pList = sqlite3ExprListAppend(pParse,pList, yymsp[-2].minor.yy202);
  yymsp[-2].minor.yy202 = sqlite3ExprFunction(pParse, pList, &yymsp[-1].minor.yy0, 0);
  if( bNot ) yymsp[-2].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-2].minor.yy202, 0);
  if( yymsp[-2].minor.yy202 ) yymsp[-2].minor.yy202->flags |= EP_InfixFunc;
}
        break;
      case 196: /* expr ::= expr likeop expr ESCAPE expr */
{
  ExprList *pList;
  int bNot = yymsp[-3].minor.yy0.n & 0x80000000;
  yymsp[-3].minor.yy0.n &= 0x7fffffff;
  pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy202);
  pList = sqlite3ExprListAppend(pParse,pList, yymsp[-4].minor.yy202);
  pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy202);
  yymsp[-4].minor.yy202 = sqlite3ExprFunction(pParse, pList, &yymsp[-3].minor.yy0, 0);
  if( bNot ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0);
  if( yymsp[-4].minor.yy202 ) yymsp[-4].minor.yy202->flags |= EP_InfixFunc;
}
        break;
      case 197: /* expr ::= expr ISNULL|NOTNULL */
{yymsp[-1].minor.yy202 = sqlite3PExpr(pParse,yymsp[0].major,yymsp[-1].minor.yy202,0);}
        break;
      case 198: /* expr ::= expr NOT NULL */
{yymsp[-2].minor.yy202 = sqlite3PExpr(pParse,TK_NOTNULL,yymsp[-2].minor.yy202,0);}
        break;
      case 199: /* expr ::= expr IS expr */
{
  yymsp[-2].minor.yy202 = sqlite3PExpr(pParse,TK_IS,yymsp[-2].minor.yy202,yymsp[0].minor.yy202);
  binaryToUnaryIfNull(pParse, yymsp[0].minor.yy202, yymsp[-2].minor.yy202, TK_ISNULL);
}
        break;
      case 200: /* expr ::= expr IS NOT expr */
{
  yymsp[-3].minor.yy202 = sqlite3PExpr(pParse,TK_ISNOT,yymsp[-3].minor.yy202,yymsp[0].minor.yy202);
  binaryToUnaryIfNull(pParse, yymsp[0].minor.yy202, yymsp[-3].minor.yy202, TK_NOTNULL);
}
        break;
      case 201: /* expr ::= NOT expr */
      case 202: /* expr ::= BITNOT expr */ yytestcase(yyruleno==202);
{yymsp[-1].minor.yy202 = sqlite3PExpr(pParse, yymsp[-1].major, yymsp[0].minor.yy202, 0);/*A-overwrites-B*/}
        break;
      case 203: /* expr ::= PLUS|MINUS expr */
{
  yymsp[-1].minor.yy202 = sqlite3PExpr(pParse, yymsp[-1].major==TK_PLUS ? TK_UPLUS : TK_UMINUS, yymsp[0].minor.yy202, 0);
  /*A-overwrites-B*/
}
        break;
      case 204: /* between_op ::= BETWEEN */
      case 207: /* in_op ::= IN */ yytestcase(yyruleno==207);
{yymsp[0].minor.yy192 = 0;}
        break;
      case 206: /* expr ::= expr between_op expr AND expr */
{
  ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy202);
  pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy202);
  yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy202, 0);
  if( yymsp[-4].minor.yy202 ){
    yymsp[-4].minor.yy202->x.pList = pList;
  }else{
    sqlite3ExprListDelete(pParse->db, pList);
  }
  if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0);
}
        break;
      case 209: /* expr ::= expr in_op LP exprlist RP */
{
    if( yymsp[-1].minor.yy242==0 ){
      /* Expressions of the form
      **
      **      expr1 IN ()
      **      expr1 NOT IN ()
      **
159246
159247
159248
159249
159250
159251
159252
159253
159254
159255
159256
159257
159258
159259
159260
159261
159262
159263
159264
159265
159266
159267
159268
159269
159270
159271
159272
159273
159274
159275
159276
159277
159278
159279
159280
159281
159282
159283
159284
159285
159286
159287
159288
159289
159290
159291
159292
159293
159294
159295
159296
159297
159298
159299
159300
159301
159302
159303
159304
159305
159306
159307
159308
159309
159310
159311
159312
159313
159314
159315
159316
159317
159318
159319
159320
159321
159322
159323
159324
159325
159326
159327
159328
159329
159330
159331
159332
159333
159334
159335
159336
159337
159338
159339
159340
159341
159342
159343
159344
159345
159346
159347
159348
159349
159350
159351
159352
159353
159354
159355
159356
159357
159358
159359
159360
159361
159362
159363
159364
159365
159366
159367
159368
159369
159370
159371
159372
159373
159374
159375
159376
159377
159378
159379
159380
159381
159382
159383
159384
159385
159386
159387
159388
159389
159390
159391
159392
159393
159394
159395
159396
159397
159398
159399
159400
159401
159402
159403
159404
159405
159406
159407
159408
159409
159410
159411
159412
159413
159414
159415
159416
159417
159418
159419
159420
159421
159422
159423
159424
159425
159426
159427
159428
159429
159430
159431
159432
159433
159434
159435
159436
159437
159438
159439
159440
159441
159442
159443
159444
159445
159446
159447
159448
159449
159450
159451
159452
159453
159454
159455
159456
159457
159458
159459
159460
159461
159462
159463
159464
159465
159466
159467
159468
159469
159470
159471
159472
159473
159474
159475
159476
159477
159478
159479
159480
159481
159482
159483
159484
159485
159486
159487
159488
159489
159490
159491
159492
159493
159494
159495
159496
159497
159498
159499
159500
159501
159502
159503
159504
159505
159506
159507
159508
159509
159510
159511
159512
159513
159514
159515
159516
159517
159518
159519
159520
159521
159522
159523
159524
159525
159526
159527
159528
159529
159530
159531
159532
159533
159534
159535
159536
159537
159538
159539
159540
159541
159542
159543
159544
159545
159546
159547
159548
159549
159550
159551
159552
159553
159554
159555
159556
159557
159558
159559
159560
159561
159562
159563
159564
159565
159566
159567
159568
159569
159570
159571
159572
159573
159574
159575
159576
159577
159578
159579
159580
159581
159582
159583
159584
159585
159586
159587
159588
159589
159590
159591
159592
159593
159594
159595
159596
159597
159598
159599
159600
159601
159602
159603
159604
159605
159606
159607
159608
159609
159610
159611
159612
159613
159614
159615
159616
159617
159618
159619
159620
159621
159622
159623
159624
159625
159626
159627
159628
159629
159630
159631
159632
159633
159634
159635
159636
159637
159638
159639
159640
159641
159642
159643
159644
159645
159646
159647
159648
159649
159650
159651
159652
159653
159654
159655
159656
159657
159658
159659
159660
159661
159662
159663
159664
159665
159666
159667
159668
159669
159670
159671
159672
159673
159674
159675
159676
159677
159678
159679
159680
159681
159682
159683
159684
159685
159686
159687
159688
159689
159690
159691
159692
159693
159694
159695
159696
159697
159698
159699
159700
159701
159702
159703
159704
159705
159706
159707
159708
159709
159710
159711
159712
159713
159714
159715
159716
159717
159718
159719
159720
159721
159722
159723
159724
159725
159726
159727
159728
159729
159730
159731
159732
159733
159734
159735
159736
159737
159738
159739
159740
159741
159742
159743
159744
159745
159746
159747
159748
159749
159750
159751
159752
159753
159754
159755
159756
159757
159758
159759
159760
159761
159762
159763
159764
159765
159766
159767
159768
159769
159770
159771
159772
159773
159774
      }else{
        sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy242);
      }
      if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0);
    }
  }
        break;
      case 209: /* expr ::= LP select RP */
{
    yymsp[-2].minor.yy202 = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
    sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy202, yymsp[-1].minor.yy539);
  }
        break;
      case 210: /* expr ::= expr in_op LP select RP */
{
    yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy202, 0);
    sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy202, yymsp[-1].minor.yy539);
    if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0);
  }
        break;
      case 211: /* expr ::= expr in_op nm dbnm paren_exprlist */
{
    SrcList *pSrc = sqlite3SrcListAppend(pParse, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);
    Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0);
    if( yymsp[0].minor.yy242 )  sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy242);
    yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy202, 0);
    sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy202, pSelect);
    if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0);
  }
        break;
      case 212: /* expr ::= EXISTS LP select RP */
{
    Expr *p;
    p = yymsp[-3].minor.yy202 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
    sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy539);
  }
        break;
      case 213: /* expr ::= CASE case_operand case_exprlist case_else END */
{
  yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy202, 0);
  if( yymsp[-4].minor.yy202 ){
    yymsp[-4].minor.yy202->x.pList = yymsp[-1].minor.yy202 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy242,yymsp[-1].minor.yy202) : yymsp[-2].minor.yy242;
    sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy202);
  }else{
    sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy242);
    sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy202);
  }
}
        break;
      case 214: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
{
  yymsp[-4].minor.yy242 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy242, yymsp[-2].minor.yy202);
  yymsp[-4].minor.yy242 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy242, yymsp[0].minor.yy202);
}
        break;
      case 215: /* case_exprlist ::= WHEN expr THEN expr */
{
  yymsp[-3].minor.yy242 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy202);
  yymsp[-3].minor.yy242 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy242, yymsp[0].minor.yy202);
}
        break;
      case 218: /* case_operand ::= expr */
{yymsp[0].minor.yy202 = yymsp[0].minor.yy202; /*A-overwrites-X*/}
        break;
      case 221: /* nexprlist ::= nexprlist COMMA expr */
{yymsp[-2].minor.yy242 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy242,yymsp[0].minor.yy202);}
        break;
      case 222: /* nexprlist ::= expr */
{yymsp[0].minor.yy242 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy202); /*A-overwrites-Y*/}
        break;
      case 224: /* paren_exprlist ::= LP exprlist RP */
      case 229: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==229);
{yymsp[-2].minor.yy242 = yymsp[-1].minor.yy242;}
        break;
      case 225: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
{
  sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0,
                     sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy242, yymsp[-10].minor.yy192,
                      &yymsp[-11].minor.yy0, yymsp[0].minor.yy202, SQLITE_SO_ASC, yymsp[-8].minor.yy192, SQLITE_IDXTYPE_APPDEF);
  if( IN_RENAME_OBJECT && pParse->pNewIndex ){
    sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0);
  }
}
        break;
      case 226: /* uniqueflag ::= UNIQUE */
      case 268: /* raisetype ::= ABORT */ yytestcase(yyruleno==268);
{yymsp[0].minor.yy192 = OE_Abort;}
        break;
      case 227: /* uniqueflag ::= */
{yymsp[1].minor.yy192 = OE_None;}
        break;
      case 230: /* eidlist ::= eidlist COMMA nm collate sortorder */
{
  yymsp[-4].minor.yy242 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy242, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy192, yymsp[0].minor.yy192);
}
        break;
      case 231: /* eidlist ::= nm collate sortorder */
{
  yymsp[-2].minor.yy242 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy192, yymsp[0].minor.yy192); /*A-overwrites-Y*/
}
        break;
      case 234: /* cmd ::= DROP INDEX ifexists fullname */
{sqlite3DropIndex(pParse, yymsp[0].minor.yy47, yymsp[-1].minor.yy192);}
        break;
      case 235: /* cmd ::= VACUUM vinto */
{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy202);}
        break;
      case 236: /* cmd ::= VACUUM nm vinto */
{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy202);}
        break;
      case 239: /* cmd ::= PRAGMA nm dbnm */
{sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);}
        break;
      case 240: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);}
        break;
      case 241: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);}
        break;
      case 242: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);}
        break;
      case 243: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);}
        break;
      case 246: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
{
  Token all;
  all.z = yymsp[-3].minor.yy0.z;
  all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n;
  sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy447, &all);
}
        break;
      case 247: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
{
  sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy192, yymsp[-4].minor.yy230.a, yymsp[-4].minor.yy230.b, yymsp[-2].minor.yy47, yymsp[0].minor.yy202, yymsp[-10].minor.yy192, yymsp[-8].minor.yy192);
  yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/
}
        break;
      case 248: /* trigger_time ::= BEFORE|AFTER */
{ yymsp[0].minor.yy192 = yymsp[0].major; /*A-overwrites-X*/ }
        break;
      case 249: /* trigger_time ::= INSTEAD OF */
{ yymsp[-1].minor.yy192 = TK_INSTEAD;}
        break;
      case 250: /* trigger_time ::= */
{ yymsp[1].minor.yy192 = TK_BEFORE; }
        break;
      case 251: /* trigger_event ::= DELETE|INSERT */
      case 252: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==252);
{yymsp[0].minor.yy230.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy230.b = 0;}
        break;
      case 253: /* trigger_event ::= UPDATE OF idlist */
{yymsp[-2].minor.yy230.a = TK_UPDATE; yymsp[-2].minor.yy230.b = yymsp[0].minor.yy600;}
        break;
      case 254: /* when_clause ::= */
      case 273: /* key_opt ::= */ yytestcase(yyruleno==273);
{ yymsp[1].minor.yy202 = 0; }
        break;
      case 255: /* when_clause ::= WHEN expr */
      case 274: /* key_opt ::= KEY expr */ yytestcase(yyruleno==274);
{ yymsp[-1].minor.yy202 = yymsp[0].minor.yy202; }
        break;
      case 256: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
{
  assert( yymsp[-2].minor.yy447!=0 );
  yymsp[-2].minor.yy447->pLast->pNext = yymsp[-1].minor.yy447;
  yymsp[-2].minor.yy447->pLast = yymsp[-1].minor.yy447;
}
        break;
      case 257: /* trigger_cmd_list ::= trigger_cmd SEMI */
{
  assert( yymsp[-1].minor.yy447!=0 );
  yymsp[-1].minor.yy447->pLast = yymsp[-1].minor.yy447;
}
        break;
      case 258: /* trnm ::= nm DOT nm */
{
  yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;
  sqlite3ErrorMsg(pParse,
        "qualified table names are not allowed on INSERT, UPDATE, and DELETE "
        "statements within triggers");
}
        break;
      case 259: /* tridxby ::= INDEXED BY nm */
{
  sqlite3ErrorMsg(pParse,
        "the INDEXED BY clause is not allowed on UPDATE or DELETE statements "
        "within triggers");
}
        break;
      case 260: /* tridxby ::= NOT INDEXED */
{
  sqlite3ErrorMsg(pParse,
        "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements "
        "within triggers");
}
        break;
      case 261: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
{yylhsminor.yy447 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy47, yymsp[-3].minor.yy242, yymsp[-1].minor.yy202, yymsp[-7].minor.yy192, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy436);}
  yymsp[-8].minor.yy447 = yylhsminor.yy447;
        break;
      case 262: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
{
   yylhsminor.yy447 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy600,yymsp[-2].minor.yy539,yymsp[-6].minor.yy192,yymsp[-1].minor.yy318,yymsp[-7].minor.yy436,yymsp[0].minor.yy436);/*yylhsminor.yy447-overwrites-yymsp[-6].minor.yy192*/
}
  yymsp[-7].minor.yy447 = yylhsminor.yy447;
        break;
      case 263: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
{yylhsminor.yy447 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy202, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy436);}
  yymsp[-5].minor.yy447 = yylhsminor.yy447;
        break;
      case 264: /* trigger_cmd ::= scanpt select scanpt */
{yylhsminor.yy447 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy539, yymsp[-2].minor.yy436, yymsp[0].minor.yy436); /*yylhsminor.yy447-overwrites-yymsp[-1].minor.yy539*/}
  yymsp[-2].minor.yy447 = yylhsminor.yy447;
        break;
      case 265: /* expr ::= RAISE LP IGNORE RP */
{
  yymsp[-3].minor.yy202 = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
  if( yymsp[-3].minor.yy202 ){
    yymsp[-3].minor.yy202->affExpr = OE_Ignore;
  }
}
        break;
      case 266: /* expr ::= RAISE LP raisetype COMMA nm RP */
{
  yymsp[-5].minor.yy202 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1);
  if( yymsp[-5].minor.yy202 ) {
    yymsp[-5].minor.yy202->affExpr = (char)yymsp[-3].minor.yy192;
  }
}
        break;
      case 267: /* raisetype ::= ROLLBACK */
{yymsp[0].minor.yy192 = OE_Rollback;}
        break;
      case 269: /* raisetype ::= FAIL */
{yymsp[0].minor.yy192 = OE_Fail;}
        break;
      case 270: /* cmd ::= DROP TRIGGER ifexists fullname */
{
  sqlite3DropTrigger(pParse,yymsp[0].minor.yy47,yymsp[-1].minor.yy192);
}
        break;
      case 271: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
{
  sqlite3Attach(pParse, yymsp[-3].minor.yy202, yymsp[-1].minor.yy202, yymsp[0].minor.yy202);
}
        break;
      case 272: /* cmd ::= DETACH database_kw_opt expr */
{
  sqlite3Detach(pParse, yymsp[0].minor.yy202);
}
        break;
      case 275: /* cmd ::= REINDEX */
{sqlite3Reindex(pParse, 0, 0);}
        break;
      case 276: /* cmd ::= REINDEX nm dbnm */
{sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
        break;
      case 277: /* cmd ::= ANALYZE */
{sqlite3Analyze(pParse, 0, 0);}
        break;
      case 278: /* cmd ::= ANALYZE nm dbnm */
{sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
        break;
      case 279: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
{
  sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy47,&yymsp[0].minor.yy0);
}
        break;
      case 280: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
{
  yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n;
  sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0);
}
        break;
      case 281: /* add_column_fullname ::= fullname */
{
  disableLookaside(pParse);
  sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy47);
}
        break;
      case 282: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
{
  sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy47, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
}
        break;
      case 283: /* cmd ::= create_vtab */
{sqlite3VtabFinishParse(pParse,0);}
        break;
      case 284: /* cmd ::= create_vtab LP vtabarglist RP */
{sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);}
        break;
      case 285: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
{
    sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy192);
}
        break;
      case 286: /* vtabarg ::= */
{sqlite3VtabArgInit(pParse);}
        break;
      case 287: /* vtabargtoken ::= ANY */
      case 288: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==288);
      case 289: /* lp ::= LP */ yytestcase(yyruleno==289);
{sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);}
        break;
      case 290: /* with ::= WITH wqlist */
      case 291: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==291);
{ sqlite3WithPush(pParse, yymsp[0].minor.yy131, 1); }
        break;
      case 292: /* wqlist ::= nm eidlist_opt AS LP select RP */
{
  yymsp[-5].minor.yy131 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy242, yymsp[-1].minor.yy539); /*A-overwrites-X*/
}
        break;
      case 293: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
{
  yymsp[-7].minor.yy131 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy131, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy242, yymsp[-1].minor.yy539);
}
        break;
      case 294: /* windowdefn_list ::= windowdefn */
{ yylhsminor.yy303 = yymsp[0].minor.yy303; }
  yymsp[0].minor.yy303 = yylhsminor.yy303;
        break;
      case 295: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */
{
  assert( yymsp[0].minor.yy303!=0 );
  sqlite3WindowChain(pParse, yymsp[0].minor.yy303, yymsp[-2].minor.yy303);
  yymsp[0].minor.yy303->pNextWin = yymsp[-2].minor.yy303;
  yylhsminor.yy303 = yymsp[0].minor.yy303;
}
  yymsp[-2].minor.yy303 = yylhsminor.yy303;
        break;
      case 296: /* windowdefn ::= nm AS LP window RP */
{
  if( ALWAYS(yymsp[-1].minor.yy303) ){
    yymsp[-1].minor.yy303->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n);
  }
  yylhsminor.yy303 = yymsp[-1].minor.yy303;
}
  yymsp[-4].minor.yy303 = yylhsminor.yy303;
        break;
      case 297: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */
{
  yymsp[-4].minor.yy303 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, yymsp[-2].minor.yy242, yymsp[-1].minor.yy242, 0);
}
        break;
      case 298: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
{
  yylhsminor.yy303 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, yymsp[-2].minor.yy242, yymsp[-1].minor.yy242, &yymsp[-5].minor.yy0);
}
  yymsp[-5].minor.yy303 = yylhsminor.yy303;
        break;
      case 299: /* window ::= ORDER BY sortlist frame_opt */
{
  yymsp[-3].minor.yy303 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, 0, yymsp[-1].minor.yy242, 0);
}
        break;
      case 300: /* window ::= nm ORDER BY sortlist frame_opt */
{
  yylhsminor.yy303 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, 0, yymsp[-1].minor.yy242, &yymsp[-4].minor.yy0);
}
  yymsp[-4].minor.yy303 = yylhsminor.yy303;
        break;
      case 301: /* window ::= frame_opt */
      case 320: /* filter_over ::= over_clause */ yytestcase(yyruleno==320);
{
  yylhsminor.yy303 = yymsp[0].minor.yy303;
}
  yymsp[0].minor.yy303 = yylhsminor.yy303;
        break;
      case 302: /* window ::= nm frame_opt */
{
  yylhsminor.yy303 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, 0, 0, &yymsp[-1].minor.yy0);
}
  yymsp[-1].minor.yy303 = yylhsminor.yy303;
        break;
      case 303: /* frame_opt ::= */
{
  yymsp[1].minor.yy303 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0);
}
        break;
      case 304: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
{
  yylhsminor.yy303 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy192, yymsp[-1].minor.yy77.eType, yymsp[-1].minor.yy77.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy58);
}
  yymsp[-2].minor.yy303 = yylhsminor.yy303;
        break;
      case 305: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
{
  yylhsminor.yy303 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy192, yymsp[-3].minor.yy77.eType, yymsp[-3].minor.yy77.pExpr, yymsp[-1].minor.yy77.eType, yymsp[-1].minor.yy77.pExpr, yymsp[0].minor.yy58);
}
  yymsp[-5].minor.yy303 = yylhsminor.yy303;
        break;
      case 307: /* frame_bound_s ::= frame_bound */
      case 309: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==309);
{yylhsminor.yy77 = yymsp[0].minor.yy77;}
  yymsp[0].minor.yy77 = yylhsminor.yy77;
        break;
      case 308: /* frame_bound_s ::= UNBOUNDED PRECEDING */
      case 310: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==310);
      case 312: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==312);
{yylhsminor.yy77.eType = yymsp[-1].major; yylhsminor.yy77.pExpr = 0;}
  yymsp[-1].minor.yy77 = yylhsminor.yy77;
        break;
      case 311: /* frame_bound ::= expr PRECEDING|FOLLOWING */
{yylhsminor.yy77.eType = yymsp[0].major; yylhsminor.yy77.pExpr = yymsp[-1].minor.yy202;}
  yymsp[-1].minor.yy77 = yylhsminor.yy77;
        break;
      case 313: /* frame_exclude_opt ::= */
{yymsp[1].minor.yy58 = 0;}
        break;
      case 314: /* frame_exclude_opt ::= EXCLUDE frame_exclude */
{yymsp[-1].minor.yy58 = yymsp[0].minor.yy58;}
        break;
      case 315: /* frame_exclude ::= NO OTHERS */
      case 316: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==316);
{yymsp[-1].minor.yy58 = yymsp[-1].major; /*A-overwrites-X*/}
        break;
      case 317: /* frame_exclude ::= GROUP|TIES */
{yymsp[0].minor.yy58 = yymsp[0].major; /*A-overwrites-X*/}
        break;
      case 318: /* window_clause ::= WINDOW windowdefn_list */
{ yymsp[-1].minor.yy303 = yymsp[0].minor.yy303; }
        break;
      case 319: /* filter_over ::= filter_clause over_clause */
{
  yymsp[0].minor.yy303->pFilter = yymsp[-1].minor.yy202;
  yylhsminor.yy303 = yymsp[0].minor.yy303;
}
  yymsp[-1].minor.yy303 = yylhsminor.yy303;
        break;
      case 321: /* filter_over ::= filter_clause */
{
  yylhsminor.yy303 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
  if( yylhsminor.yy303 ){
    yylhsminor.yy303->eFrmType = TK_FILTER;
    yylhsminor.yy303->pFilter = yymsp[0].minor.yy202;
  }else{
    sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy202);
  }
}
  yymsp[0].minor.yy303 = yylhsminor.yy303;
        break;
      case 322: /* over_clause ::= OVER LP window RP */
{
  yymsp[-3].minor.yy303 = yymsp[-1].minor.yy303;
  assert( yymsp[-3].minor.yy303!=0 );
}
        break;
      case 323: /* over_clause ::= OVER nm */
{
  yymsp[-1].minor.yy303 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
  if( yymsp[-1].minor.yy303 ){
    yymsp[-1].minor.yy303->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n);
  }
}
        break;
      case 324: /* filter_clause ::= FILTER LP WHERE expr RP */
{ yymsp[-4].minor.yy202 = yymsp[-1].minor.yy202; }
        break;
      default:
      /* (325) input ::= cmdlist */ yytestcase(yyruleno==325);
      /* (326) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==326);
      /* (327) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=327);
      /* (328) ecmd ::= SEMI */ yytestcase(yyruleno==328);
      /* (329) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==329);
      /* (330) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=330);
      /* (331) trans_opt ::= */ yytestcase(yyruleno==331);
      /* (332) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==332);
      /* (333) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==333);
      /* (334) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==334);
      /* (335) savepoint_opt ::= */ yytestcase(yyruleno==335);
      /* (336) cmd ::= create_table create_table_args */ yytestcase(yyruleno==336);
      /* (337) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==337);
      /* (338) columnlist ::= columnname carglist */ yytestcase(yyruleno==338);
      /* (339) nm ::= ID|INDEXED */ yytestcase(yyruleno==339);
      /* (340) nm ::= STRING */ yytestcase(yyruleno==340);
      /* (341) nm ::= JOIN_KW */ yytestcase(yyruleno==341);
      /* (342) typetoken ::= typename */ yytestcase(yyruleno==342);
      /* (343) typename ::= ID|STRING */ yytestcase(yyruleno==343);
      /* (344) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=344);
      /* (345) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=345);
      /* (346) carglist ::= carglist ccons */ yytestcase(yyruleno==346);
      /* (347) carglist ::= */ yytestcase(yyruleno==347);
      /* (348) ccons ::= NULL onconf */ yytestcase(yyruleno==348);
      /* (349) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==349);
      /* (350) ccons ::= AS generated */ yytestcase(yyruleno==350);
      /* (351) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==351);
      /* (352) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==352);
      /* (353) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=353);
      /* (354) tconscomma ::= */ yytestcase(yyruleno==354);
      /* (355) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=355);
      /* (356) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=356);
      /* (357) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=357);
      /* (358) oneselect ::= values */ yytestcase(yyruleno==358);
      /* (359) sclp ::= selcollist COMMA */ yytestcase(yyruleno==359);
      /* (360) as ::= ID|STRING */ yytestcase(yyruleno==360);
      /* (361) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=361);
      /* (362) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==362);
      /* (363) exprlist ::= nexprlist */ yytestcase(yyruleno==363);
      /* (364) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=364);
      /* (365) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=365);
      /* (366) nmnum ::= ON */ yytestcase(yyruleno==366);
      /* (367) nmnum ::= DELETE */ yytestcase(yyruleno==367);
      /* (368) nmnum ::= DEFAULT */ yytestcase(yyruleno==368);
      /* (369) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==369);
      /* (370) foreach_clause ::= */ yytestcase(yyruleno==370);
      /* (371) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==371);
      /* (372) trnm ::= nm */ yytestcase(yyruleno==372);
      /* (373) tridxby ::= */ yytestcase(yyruleno==373);
      /* (374) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==374);
      /* (375) database_kw_opt ::= */ yytestcase(yyruleno==375);
      /* (376) kwcolumn_opt ::= */ yytestcase(yyruleno==376);
      /* (377) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==377);
      /* (378) vtabarglist ::= vtabarg */ yytestcase(yyruleno==378);
      /* (379) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==379);
      /* (380) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==380);
      /* (381) anylist ::= */ yytestcase(yyruleno==381);
      /* (382) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==382);
      /* (383) anylist ::= anylist ANY */ yytestcase(yyruleno==383);
      /* (384) with ::= */ yytestcase(yyruleno==384);
        break;
/********** End reduce actions ************************************************/
  };
  assert( yyruleno<sizeof(yyRuleInfoLhs)/sizeof(yyRuleInfoLhs[0]) );
  yygoto = yyRuleInfoLhs[yyruleno];
  yysize = yyRuleInfoNRhs[yyruleno];
  yyact = yy_find_reduce_action(yymsp[yysize].stateno,(YYCODETYPE)yygoto);







|





|






|









|






|











|





|





|


|


|


|
|


|









|
|


|


|




|




|


|


|


|


|


|


|


|


|







|





|


|


|


|
|


|


|
|


|
|


|






|





|







|






|






|



|





|



|



|







|







|


|


|




|




|




|


|


|


|


|




|





|





|




|


|


|




|


|
|
|


|
|


|




|




|



|








|








|




|





|




|





|
|





|





|




|





|





|
|



|
|
|



|



|


|


|
|


|


|


|






|











|





|







|



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







160359
160360
160361
160362
160363
160364
160365
160366
160367
160368
160369
160370
160371
160372
160373
160374
160375
160376
160377
160378
160379
160380
160381
160382
160383
160384
160385
160386
160387
160388
160389
160390
160391
160392
160393
160394
160395
160396
160397
160398
160399
160400
160401
160402
160403
160404
160405
160406
160407
160408
160409
160410
160411
160412
160413
160414
160415
160416
160417
160418
160419
160420
160421
160422
160423
160424
160425
160426
160427
160428
160429
160430
160431
160432
160433
160434
160435
160436
160437
160438
160439
160440
160441
160442
160443
160444
160445
160446
160447
160448
160449
160450
160451
160452
160453
160454
160455
160456
160457
160458
160459
160460
160461
160462
160463
160464
160465
160466
160467
160468
160469
160470
160471
160472
160473
160474
160475
160476
160477
160478
160479
160480
160481
160482
160483
160484
160485
160486
160487
160488
160489
160490
160491
160492
160493
160494
160495
160496
160497
160498
160499
160500
160501
160502
160503
160504
160505
160506
160507
160508
160509
160510
160511
160512
160513
160514
160515
160516
160517
160518
160519
160520
160521
160522
160523
160524
160525
160526
160527
160528
160529
160530
160531
160532
160533
160534
160535
160536
160537
160538
160539
160540
160541
160542
160543
160544
160545
160546
160547
160548
160549
160550
160551
160552
160553
160554
160555
160556
160557
160558
160559
160560
160561
160562
160563
160564
160565
160566
160567
160568
160569
160570
160571
160572
160573
160574
160575
160576
160577
160578
160579
160580
160581
160582
160583
160584
160585
160586
160587
160588
160589
160590
160591
160592
160593
160594
160595
160596
160597
160598
160599
160600
160601
160602
160603
160604
160605
160606
160607
160608
160609
160610
160611
160612
160613
160614
160615
160616
160617
160618
160619
160620
160621
160622
160623
160624
160625
160626
160627
160628
160629
160630
160631
160632
160633
160634
160635
160636
160637
160638
160639
160640
160641
160642
160643
160644
160645
160646
160647
160648
160649
160650
160651
160652
160653
160654
160655
160656
160657
160658
160659
160660
160661
160662
160663
160664
160665
160666
160667
160668
160669
160670
160671
160672
160673
160674
160675
160676
160677
160678
160679
160680
160681
160682
160683
160684
160685
160686
160687
160688
160689
160690
160691
160692
160693
160694
160695
160696
160697
160698
160699
160700
160701
160702
160703
160704
160705
160706
160707
160708
160709
160710
160711
160712
160713
160714
160715
160716
160717
160718
160719
160720
160721
160722
160723
160724
160725
160726
160727
160728
160729
160730
160731
160732
160733
160734
160735
160736
160737
160738
160739
160740
160741
160742
160743
160744
160745
160746
160747
160748
160749
160750
160751
160752
160753
160754
160755
160756
160757
160758
160759
160760
160761
160762
160763
160764
160765
160766
160767
160768
160769
160770
160771
160772
160773
160774
160775
160776
160777
160778
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
160883
160884
160885
160886
160887
      }else{
        sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy242);
      }
      if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0);
    }
  }
        break;
      case 210: /* expr ::= LP select RP */
{
    yymsp[-2].minor.yy202 = sqlite3PExpr(pParse, TK_SELECT, 0, 0);
    sqlite3PExprAddSelect(pParse, yymsp[-2].minor.yy202, yymsp[-1].minor.yy539);
  }
        break;
      case 211: /* expr ::= expr in_op LP select RP */
{
    yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy202, 0);
    sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy202, yymsp[-1].minor.yy539);
    if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0);
  }
        break;
      case 212: /* expr ::= expr in_op nm dbnm paren_exprlist */
{
    SrcList *pSrc = sqlite3SrcListAppend(pParse, 0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0);
    Select *pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0);
    if( yymsp[0].minor.yy242 )  sqlite3SrcListFuncArgs(pParse, pSelect ? pSrc : 0, yymsp[0].minor.yy242);
    yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy202, 0);
    sqlite3PExprAddSelect(pParse, yymsp[-4].minor.yy202, pSelect);
    if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0);
  }
        break;
      case 213: /* expr ::= EXISTS LP select RP */
{
    Expr *p;
    p = yymsp[-3].minor.yy202 = sqlite3PExpr(pParse, TK_EXISTS, 0, 0);
    sqlite3PExprAddSelect(pParse, p, yymsp[-1].minor.yy539);
  }
        break;
      case 214: /* expr ::= CASE case_operand case_exprlist case_else END */
{
  yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy202, 0);
  if( yymsp[-4].minor.yy202 ){
    yymsp[-4].minor.yy202->x.pList = yymsp[-1].minor.yy202 ? sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy242,yymsp[-1].minor.yy202) : yymsp[-2].minor.yy242;
    sqlite3ExprSetHeightAndFlags(pParse, yymsp[-4].minor.yy202);
  }else{
    sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy242);
    sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy202);
  }
}
        break;
      case 215: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */
{
  yymsp[-4].minor.yy242 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy242, yymsp[-2].minor.yy202);
  yymsp[-4].minor.yy242 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy242, yymsp[0].minor.yy202);
}
        break;
      case 216: /* case_exprlist ::= WHEN expr THEN expr */
{
  yymsp[-3].minor.yy242 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy202);
  yymsp[-3].minor.yy242 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy242, yymsp[0].minor.yy202);
}
        break;
      case 219: /* case_operand ::= expr */
{yymsp[0].minor.yy202 = yymsp[0].minor.yy202; /*A-overwrites-X*/}
        break;
      case 222: /* nexprlist ::= nexprlist COMMA expr */
{yymsp[-2].minor.yy242 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy242,yymsp[0].minor.yy202);}
        break;
      case 223: /* nexprlist ::= expr */
{yymsp[0].minor.yy242 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy202); /*A-overwrites-Y*/}
        break;
      case 225: /* paren_exprlist ::= LP exprlist RP */
      case 230: /* eidlist_opt ::= LP eidlist RP */ yytestcase(yyruleno==230);
{yymsp[-2].minor.yy242 = yymsp[-1].minor.yy242;}
        break;
      case 226: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP sortlist RP where_opt */
{
  sqlite3CreateIndex(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0,
                     sqlite3SrcListAppend(pParse,0,&yymsp[-4].minor.yy0,0), yymsp[-2].minor.yy242, yymsp[-10].minor.yy192,
                      &yymsp[-11].minor.yy0, yymsp[0].minor.yy202, SQLITE_SO_ASC, yymsp[-8].minor.yy192, SQLITE_IDXTYPE_APPDEF);
  if( IN_RENAME_OBJECT && pParse->pNewIndex ){
    sqlite3RenameTokenMap(pParse, pParse->pNewIndex->zName, &yymsp[-4].minor.yy0);
  }
}
        break;
      case 227: /* uniqueflag ::= UNIQUE */
      case 269: /* raisetype ::= ABORT */ yytestcase(yyruleno==269);
{yymsp[0].minor.yy192 = OE_Abort;}
        break;
      case 228: /* uniqueflag ::= */
{yymsp[1].minor.yy192 = OE_None;}
        break;
      case 231: /* eidlist ::= eidlist COMMA nm collate sortorder */
{
  yymsp[-4].minor.yy242 = parserAddExprIdListTerm(pParse, yymsp[-4].minor.yy242, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy192, yymsp[0].minor.yy192);
}
        break;
      case 232: /* eidlist ::= nm collate sortorder */
{
  yymsp[-2].minor.yy242 = parserAddExprIdListTerm(pParse, 0, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy192, yymsp[0].minor.yy192); /*A-overwrites-Y*/
}
        break;
      case 235: /* cmd ::= DROP INDEX ifexists fullname */
{sqlite3DropIndex(pParse, yymsp[0].minor.yy47, yymsp[-1].minor.yy192);}
        break;
      case 236: /* cmd ::= VACUUM vinto */
{sqlite3Vacuum(pParse,0,yymsp[0].minor.yy202);}
        break;
      case 237: /* cmd ::= VACUUM nm vinto */
{sqlite3Vacuum(pParse,&yymsp[-1].minor.yy0,yymsp[0].minor.yy202);}
        break;
      case 240: /* cmd ::= PRAGMA nm dbnm */
{sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);}
        break;
      case 241: /* cmd ::= PRAGMA nm dbnm EQ nmnum */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);}
        break;
      case 242: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);}
        break;
      case 243: /* cmd ::= PRAGMA nm dbnm EQ minus_num */
{sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);}
        break;
      case 244: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */
{sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);}
        break;
      case 247: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */
{
  Token all;
  all.z = yymsp[-3].minor.yy0.z;
  all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n;
  sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy447, &all);
}
        break;
      case 248: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */
{
  sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy192, yymsp[-4].minor.yy230.a, yymsp[-4].minor.yy230.b, yymsp[-2].minor.yy47, yymsp[0].minor.yy202, yymsp[-10].minor.yy192, yymsp[-8].minor.yy192);
  yymsp[-10].minor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); /*A-overwrites-T*/
}
        break;
      case 249: /* trigger_time ::= BEFORE|AFTER */
{ yymsp[0].minor.yy192 = yymsp[0].major; /*A-overwrites-X*/ }
        break;
      case 250: /* trigger_time ::= INSTEAD OF */
{ yymsp[-1].minor.yy192 = TK_INSTEAD;}
        break;
      case 251: /* trigger_time ::= */
{ yymsp[1].minor.yy192 = TK_BEFORE; }
        break;
      case 252: /* trigger_event ::= DELETE|INSERT */
      case 253: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==253);
{yymsp[0].minor.yy230.a = yymsp[0].major; /*A-overwrites-X*/ yymsp[0].minor.yy230.b = 0;}
        break;
      case 254: /* trigger_event ::= UPDATE OF idlist */
{yymsp[-2].minor.yy230.a = TK_UPDATE; yymsp[-2].minor.yy230.b = yymsp[0].minor.yy600;}
        break;
      case 255: /* when_clause ::= */
      case 274: /* key_opt ::= */ yytestcase(yyruleno==274);
{ yymsp[1].minor.yy202 = 0; }
        break;
      case 256: /* when_clause ::= WHEN expr */
      case 275: /* key_opt ::= KEY expr */ yytestcase(yyruleno==275);
{ yymsp[-1].minor.yy202 = yymsp[0].minor.yy202; }
        break;
      case 257: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */
{
  assert( yymsp[-2].minor.yy447!=0 );
  yymsp[-2].minor.yy447->pLast->pNext = yymsp[-1].minor.yy447;
  yymsp[-2].minor.yy447->pLast = yymsp[-1].minor.yy447;
}
        break;
      case 258: /* trigger_cmd_list ::= trigger_cmd SEMI */
{
  assert( yymsp[-1].minor.yy447!=0 );
  yymsp[-1].minor.yy447->pLast = yymsp[-1].minor.yy447;
}
        break;
      case 259: /* trnm ::= nm DOT nm */
{
  yymsp[-2].minor.yy0 = yymsp[0].minor.yy0;
  sqlite3ErrorMsg(pParse,
        "qualified table names are not allowed on INSERT, UPDATE, and DELETE "
        "statements within triggers");
}
        break;
      case 260: /* tridxby ::= INDEXED BY nm */
{
  sqlite3ErrorMsg(pParse,
        "the INDEXED BY clause is not allowed on UPDATE or DELETE statements "
        "within triggers");
}
        break;
      case 261: /* tridxby ::= NOT INDEXED */
{
  sqlite3ErrorMsg(pParse,
        "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements "
        "within triggers");
}
        break;
      case 262: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist from where_opt scanpt */
{yylhsminor.yy447 = sqlite3TriggerUpdateStep(pParse, &yymsp[-6].minor.yy0, yymsp[-2].minor.yy47, yymsp[-3].minor.yy242, yymsp[-1].minor.yy202, yymsp[-7].minor.yy192, yymsp[-8].minor.yy0.z, yymsp[0].minor.yy436);}
  yymsp[-8].minor.yy447 = yylhsminor.yy447;
        break;
      case 263: /* trigger_cmd ::= scanpt insert_cmd INTO trnm idlist_opt select upsert scanpt */
{
   yylhsminor.yy447 = sqlite3TriggerInsertStep(pParse,&yymsp[-4].minor.yy0,yymsp[-3].minor.yy600,yymsp[-2].minor.yy539,yymsp[-6].minor.yy192,yymsp[-1].minor.yy318,yymsp[-7].minor.yy436,yymsp[0].minor.yy436);/*yylhsminor.yy447-overwrites-yymsp[-6].minor.yy192*/
}
  yymsp[-7].minor.yy447 = yylhsminor.yy447;
        break;
      case 264: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt scanpt */
{yylhsminor.yy447 = sqlite3TriggerDeleteStep(pParse, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy202, yymsp[-5].minor.yy0.z, yymsp[0].minor.yy436);}
  yymsp[-5].minor.yy447 = yylhsminor.yy447;
        break;
      case 265: /* trigger_cmd ::= scanpt select scanpt */
{yylhsminor.yy447 = sqlite3TriggerSelectStep(pParse->db, yymsp[-1].minor.yy539, yymsp[-2].minor.yy436, yymsp[0].minor.yy436); /*yylhsminor.yy447-overwrites-yymsp[-1].minor.yy539*/}
  yymsp[-2].minor.yy447 = yylhsminor.yy447;
        break;
      case 266: /* expr ::= RAISE LP IGNORE RP */
{
  yymsp[-3].minor.yy202 = sqlite3PExpr(pParse, TK_RAISE, 0, 0);
  if( yymsp[-3].minor.yy202 ){
    yymsp[-3].minor.yy202->affExpr = OE_Ignore;
  }
}
        break;
      case 267: /* expr ::= RAISE LP raisetype COMMA nm RP */
{
  yymsp[-5].minor.yy202 = sqlite3ExprAlloc(pParse->db, TK_RAISE, &yymsp[-1].minor.yy0, 1);
  if( yymsp[-5].minor.yy202 ) {
    yymsp[-5].minor.yy202->affExpr = (char)yymsp[-3].minor.yy192;
  }
}
        break;
      case 268: /* raisetype ::= ROLLBACK */
{yymsp[0].minor.yy192 = OE_Rollback;}
        break;
      case 270: /* raisetype ::= FAIL */
{yymsp[0].minor.yy192 = OE_Fail;}
        break;
      case 271: /* cmd ::= DROP TRIGGER ifexists fullname */
{
  sqlite3DropTrigger(pParse,yymsp[0].minor.yy47,yymsp[-1].minor.yy192);
}
        break;
      case 272: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */
{
  sqlite3Attach(pParse, yymsp[-3].minor.yy202, yymsp[-1].minor.yy202, yymsp[0].minor.yy202);
}
        break;
      case 273: /* cmd ::= DETACH database_kw_opt expr */
{
  sqlite3Detach(pParse, yymsp[0].minor.yy202);
}
        break;
      case 276: /* cmd ::= REINDEX */
{sqlite3Reindex(pParse, 0, 0);}
        break;
      case 277: /* cmd ::= REINDEX nm dbnm */
{sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
        break;
      case 278: /* cmd ::= ANALYZE */
{sqlite3Analyze(pParse, 0, 0);}
        break;
      case 279: /* cmd ::= ANALYZE nm dbnm */
{sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);}
        break;
      case 280: /* cmd ::= ALTER TABLE fullname RENAME TO nm */
{
  sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy47,&yymsp[0].minor.yy0);
}
        break;
      case 281: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt columnname carglist */
{
  yymsp[-1].minor.yy0.n = (int)(pParse->sLastToken.z-yymsp[-1].minor.yy0.z) + pParse->sLastToken.n;
  sqlite3AlterFinishAddColumn(pParse, &yymsp[-1].minor.yy0);
}
        break;
      case 282: /* add_column_fullname ::= fullname */
{
  disableLookaside(pParse);
  sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy47);
}
        break;
      case 283: /* cmd ::= ALTER TABLE fullname RENAME kwcolumn_opt nm TO nm */
{
  sqlite3AlterRenameColumn(pParse, yymsp[-5].minor.yy47, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0);
}
        break;
      case 284: /* cmd ::= create_vtab */
{sqlite3VtabFinishParse(pParse,0);}
        break;
      case 285: /* cmd ::= create_vtab LP vtabarglist RP */
{sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);}
        break;
      case 286: /* create_vtab ::= createkw VIRTUAL TABLE ifnotexists nm dbnm USING nm */
{
    sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0, yymsp[-4].minor.yy192);
}
        break;
      case 287: /* vtabarg ::= */
{sqlite3VtabArgInit(pParse);}
        break;
      case 288: /* vtabargtoken ::= ANY */
      case 289: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==289);
      case 290: /* lp ::= LP */ yytestcase(yyruleno==290);
{sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);}
        break;
      case 291: /* with ::= WITH wqlist */
      case 292: /* with ::= WITH RECURSIVE wqlist */ yytestcase(yyruleno==292);
{ sqlite3WithPush(pParse, yymsp[0].minor.yy131, 1); }
        break;
      case 293: /* wqlist ::= nm eidlist_opt AS LP select RP */
{
  yymsp[-5].minor.yy131 = sqlite3WithAdd(pParse, 0, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy242, yymsp[-1].minor.yy539); /*A-overwrites-X*/
}
        break;
      case 294: /* wqlist ::= wqlist COMMA nm eidlist_opt AS LP select RP */
{
  yymsp[-7].minor.yy131 = sqlite3WithAdd(pParse, yymsp[-7].minor.yy131, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy242, yymsp[-1].minor.yy539);
}
        break;
      case 295: /* windowdefn_list ::= windowdefn */
{ yylhsminor.yy303 = yymsp[0].minor.yy303; }
  yymsp[0].minor.yy303 = yylhsminor.yy303;
        break;
      case 296: /* windowdefn_list ::= windowdefn_list COMMA windowdefn */
{
  assert( yymsp[0].minor.yy303!=0 );
  sqlite3WindowChain(pParse, yymsp[0].minor.yy303, yymsp[-2].minor.yy303);
  yymsp[0].minor.yy303->pNextWin = yymsp[-2].minor.yy303;
  yylhsminor.yy303 = yymsp[0].minor.yy303;
}
  yymsp[-2].minor.yy303 = yylhsminor.yy303;
        break;
      case 297: /* windowdefn ::= nm AS LP window RP */
{
  if( ALWAYS(yymsp[-1].minor.yy303) ){
    yymsp[-1].minor.yy303->zName = sqlite3DbStrNDup(pParse->db, yymsp[-4].minor.yy0.z, yymsp[-4].minor.yy0.n);
  }
  yylhsminor.yy303 = yymsp[-1].minor.yy303;
}
  yymsp[-4].minor.yy303 = yylhsminor.yy303;
        break;
      case 298: /* window ::= PARTITION BY nexprlist orderby_opt frame_opt */
{
  yymsp[-4].minor.yy303 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, yymsp[-2].minor.yy242, yymsp[-1].minor.yy242, 0);
}
        break;
      case 299: /* window ::= nm PARTITION BY nexprlist orderby_opt frame_opt */
{
  yylhsminor.yy303 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, yymsp[-2].minor.yy242, yymsp[-1].minor.yy242, &yymsp[-5].minor.yy0);
}
  yymsp[-5].minor.yy303 = yylhsminor.yy303;
        break;
      case 300: /* window ::= ORDER BY sortlist frame_opt */
{
  yymsp[-3].minor.yy303 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, 0, yymsp[-1].minor.yy242, 0);
}
        break;
      case 301: /* window ::= nm ORDER BY sortlist frame_opt */
{
  yylhsminor.yy303 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, 0, yymsp[-1].minor.yy242, &yymsp[-4].minor.yy0);
}
  yymsp[-4].minor.yy303 = yylhsminor.yy303;
        break;
      case 302: /* window ::= frame_opt */
      case 321: /* filter_over ::= over_clause */ yytestcase(yyruleno==321);
{
  yylhsminor.yy303 = yymsp[0].minor.yy303;
}
  yymsp[0].minor.yy303 = yylhsminor.yy303;
        break;
      case 303: /* window ::= nm frame_opt */
{
  yylhsminor.yy303 = sqlite3WindowAssemble(pParse, yymsp[0].minor.yy303, 0, 0, &yymsp[-1].minor.yy0);
}
  yymsp[-1].minor.yy303 = yylhsminor.yy303;
        break;
      case 304: /* frame_opt ::= */
{
  yymsp[1].minor.yy303 = sqlite3WindowAlloc(pParse, 0, TK_UNBOUNDED, 0, TK_CURRENT, 0, 0);
}
        break;
      case 305: /* frame_opt ::= range_or_rows frame_bound_s frame_exclude_opt */
{
  yylhsminor.yy303 = sqlite3WindowAlloc(pParse, yymsp[-2].minor.yy192, yymsp[-1].minor.yy77.eType, yymsp[-1].minor.yy77.pExpr, TK_CURRENT, 0, yymsp[0].minor.yy58);
}
  yymsp[-2].minor.yy303 = yylhsminor.yy303;
        break;
      case 306: /* frame_opt ::= range_or_rows BETWEEN frame_bound_s AND frame_bound_e frame_exclude_opt */
{
  yylhsminor.yy303 = sqlite3WindowAlloc(pParse, yymsp[-5].minor.yy192, yymsp[-3].minor.yy77.eType, yymsp[-3].minor.yy77.pExpr, yymsp[-1].minor.yy77.eType, yymsp[-1].minor.yy77.pExpr, yymsp[0].minor.yy58);
}
  yymsp[-5].minor.yy303 = yylhsminor.yy303;
        break;
      case 308: /* frame_bound_s ::= frame_bound */
      case 310: /* frame_bound_e ::= frame_bound */ yytestcase(yyruleno==310);
{yylhsminor.yy77 = yymsp[0].minor.yy77;}
  yymsp[0].minor.yy77 = yylhsminor.yy77;
        break;
      case 309: /* frame_bound_s ::= UNBOUNDED PRECEDING */
      case 311: /* frame_bound_e ::= UNBOUNDED FOLLOWING */ yytestcase(yyruleno==311);
      case 313: /* frame_bound ::= CURRENT ROW */ yytestcase(yyruleno==313);
{yylhsminor.yy77.eType = yymsp[-1].major; yylhsminor.yy77.pExpr = 0;}
  yymsp[-1].minor.yy77 = yylhsminor.yy77;
        break;
      case 312: /* frame_bound ::= expr PRECEDING|FOLLOWING */
{yylhsminor.yy77.eType = yymsp[0].major; yylhsminor.yy77.pExpr = yymsp[-1].minor.yy202;}
  yymsp[-1].minor.yy77 = yylhsminor.yy77;
        break;
      case 314: /* frame_exclude_opt ::= */
{yymsp[1].minor.yy58 = 0;}
        break;
      case 315: /* frame_exclude_opt ::= EXCLUDE frame_exclude */
{yymsp[-1].minor.yy58 = yymsp[0].minor.yy58;}
        break;
      case 316: /* frame_exclude ::= NO OTHERS */
      case 317: /* frame_exclude ::= CURRENT ROW */ yytestcase(yyruleno==317);
{yymsp[-1].minor.yy58 = yymsp[-1].major; /*A-overwrites-X*/}
        break;
      case 318: /* frame_exclude ::= GROUP|TIES */
{yymsp[0].minor.yy58 = yymsp[0].major; /*A-overwrites-X*/}
        break;
      case 319: /* window_clause ::= WINDOW windowdefn_list */
{ yymsp[-1].minor.yy303 = yymsp[0].minor.yy303; }
        break;
      case 320: /* filter_over ::= filter_clause over_clause */
{
  yymsp[0].minor.yy303->pFilter = yymsp[-1].minor.yy202;
  yylhsminor.yy303 = yymsp[0].minor.yy303;
}
  yymsp[-1].minor.yy303 = yylhsminor.yy303;
        break;
      case 322: /* filter_over ::= filter_clause */
{
  yylhsminor.yy303 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
  if( yylhsminor.yy303 ){
    yylhsminor.yy303->eFrmType = TK_FILTER;
    yylhsminor.yy303->pFilter = yymsp[0].minor.yy202;
  }else{
    sqlite3ExprDelete(pParse->db, yymsp[0].minor.yy202);
  }
}
  yymsp[0].minor.yy303 = yylhsminor.yy303;
        break;
      case 323: /* over_clause ::= OVER LP window RP */
{
  yymsp[-3].minor.yy303 = yymsp[-1].minor.yy303;
  assert( yymsp[-3].minor.yy303!=0 );
}
        break;
      case 324: /* over_clause ::= OVER nm */
{
  yymsp[-1].minor.yy303 = (Window*)sqlite3DbMallocZero(pParse->db, sizeof(Window));
  if( yymsp[-1].minor.yy303 ){
    yymsp[-1].minor.yy303->zName = sqlite3DbStrNDup(pParse->db, yymsp[0].minor.yy0.z, yymsp[0].minor.yy0.n);
  }
}
        break;
      case 325: /* filter_clause ::= FILTER LP WHERE expr RP */
{ yymsp[-4].minor.yy202 = yymsp[-1].minor.yy202; }
        break;
      default:
      /* (326) input ::= cmdlist */ yytestcase(yyruleno==326);
      /* (327) cmdlist ::= cmdlist ecmd */ yytestcase(yyruleno==327);
      /* (328) cmdlist ::= ecmd (OPTIMIZED OUT) */ assert(yyruleno!=328);
      /* (329) ecmd ::= SEMI */ yytestcase(yyruleno==329);
      /* (330) ecmd ::= cmdx SEMI */ yytestcase(yyruleno==330);
      /* (331) ecmd ::= explain cmdx SEMI (NEVER REDUCES) */ assert(yyruleno!=331);
      /* (332) trans_opt ::= */ yytestcase(yyruleno==332);
      /* (333) trans_opt ::= TRANSACTION */ yytestcase(yyruleno==333);
      /* (334) trans_opt ::= TRANSACTION nm */ yytestcase(yyruleno==334);
      /* (335) savepoint_opt ::= SAVEPOINT */ yytestcase(yyruleno==335);
      /* (336) savepoint_opt ::= */ yytestcase(yyruleno==336);
      /* (337) cmd ::= create_table create_table_args */ yytestcase(yyruleno==337);
      /* (338) columnlist ::= columnlist COMMA columnname carglist */ yytestcase(yyruleno==338);
      /* (339) columnlist ::= columnname carglist */ yytestcase(yyruleno==339);
      /* (340) nm ::= ID|INDEXED */ yytestcase(yyruleno==340);
      /* (341) nm ::= STRING */ yytestcase(yyruleno==341);
      /* (342) nm ::= JOIN_KW */ yytestcase(yyruleno==342);
      /* (343) typetoken ::= typename */ yytestcase(yyruleno==343);
      /* (344) typename ::= ID|STRING */ yytestcase(yyruleno==344);
      /* (345) signed ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=345);
      /* (346) signed ::= minus_num (OPTIMIZED OUT) */ assert(yyruleno!=346);
      /* (347) carglist ::= carglist ccons */ yytestcase(yyruleno==347);
      /* (348) carglist ::= */ yytestcase(yyruleno==348);
      /* (349) ccons ::= NULL onconf */ yytestcase(yyruleno==349);
      /* (350) ccons ::= GENERATED ALWAYS AS generated */ yytestcase(yyruleno==350);
      /* (351) ccons ::= AS generated */ yytestcase(yyruleno==351);
      /* (352) conslist_opt ::= COMMA conslist */ yytestcase(yyruleno==352);
      /* (353) conslist ::= conslist tconscomma tcons */ yytestcase(yyruleno==353);
      /* (354) conslist ::= tcons (OPTIMIZED OUT) */ assert(yyruleno!=354);
      /* (355) tconscomma ::= */ yytestcase(yyruleno==355);
      /* (356) defer_subclause_opt ::= defer_subclause (OPTIMIZED OUT) */ assert(yyruleno!=356);
      /* (357) resolvetype ::= raisetype (OPTIMIZED OUT) */ assert(yyruleno!=357);
      /* (358) selectnowith ::= oneselect (OPTIMIZED OUT) */ assert(yyruleno!=358);
      /* (359) oneselect ::= values */ yytestcase(yyruleno==359);
      /* (360) sclp ::= selcollist COMMA */ yytestcase(yyruleno==360);
      /* (361) as ::= ID|STRING */ yytestcase(yyruleno==361);
      /* (362) expr ::= term (OPTIMIZED OUT) */ assert(yyruleno!=362);
      /* (363) likeop ::= LIKE_KW|MATCH */ yytestcase(yyruleno==363);
      /* (364) exprlist ::= nexprlist */ yytestcase(yyruleno==364);
      /* (365) nmnum ::= plus_num (OPTIMIZED OUT) */ assert(yyruleno!=365);
      /* (366) nmnum ::= nm (OPTIMIZED OUT) */ assert(yyruleno!=366);
      /* (367) nmnum ::= ON */ yytestcase(yyruleno==367);
      /* (368) nmnum ::= DELETE */ yytestcase(yyruleno==368);
      /* (369) nmnum ::= DEFAULT */ yytestcase(yyruleno==369);
      /* (370) plus_num ::= INTEGER|FLOAT */ yytestcase(yyruleno==370);
      /* (371) foreach_clause ::= */ yytestcase(yyruleno==371);
      /* (372) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==372);
      /* (373) trnm ::= nm */ yytestcase(yyruleno==373);
      /* (374) tridxby ::= */ yytestcase(yyruleno==374);
      /* (375) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==375);
      /* (376) database_kw_opt ::= */ yytestcase(yyruleno==376);
      /* (377) kwcolumn_opt ::= */ yytestcase(yyruleno==377);
      /* (378) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==378);
      /* (379) vtabarglist ::= vtabarg */ yytestcase(yyruleno==379);
      /* (380) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==380);
      /* (381) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==381);
      /* (382) anylist ::= */ yytestcase(yyruleno==382);
      /* (383) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==383);
      /* (384) anylist ::= anylist ANY */ yytestcase(yyruleno==384);
      /* (385) with ::= */ yytestcase(yyruleno==385);
        break;
/********** End reduce actions ************************************************/
  };
  assert( yyruleno<sizeof(yyRuleInfoLhs)/sizeof(yyRuleInfoLhs[0]) );
  yygoto = yyRuleInfoLhs[yyruleno];
  yysize = yyRuleInfoNRhs[yyruleno];
  yyact = yy_find_reduce_action(yymsp[yysize].stateno,(YYCODETYPE)yygoto);
159912
159913
159914
159915
159916
159917
159918
159919

159920
159921
159922


















159923

























159924
159925
159926
159927
159928
159929
159930
159931
    }else{
      fprintf(yyTraceFILE,"%sInput '%s' with pending reduce %d\n",
              yyTracePrompt,yyTokenName[yymajor],yyact-YY_MIN_REDUCE);
    }
  }
#endif

  do{

    assert( yyact==yypParser->yytos->stateno );
    yyact = yy_find_shift_action((YYCODETYPE)yymajor,yyact);
    if( yyact >= YY_MIN_REDUCE ){


















      yyact = yy_reduce(yypParser,yyact-YY_MIN_REDUCE,yymajor,

























                        yyminor sqlite3ParserCTX_PARAM);
    }else if( yyact <= YY_MAX_SHIFTREDUCE ){
      yy_shift(yypParser,yyact,(YYCODETYPE)yymajor,yyminor);
#ifndef YYNOERRORRECOVERY
      yypParser->yyerrcnt--;
#endif
      break;
    }else if( yyact==YY_ACCEPT_ACTION ){







|
>



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







161025
161026
161027
161028
161029
161030
161031
161032
161033
161034
161035
161036
161037
161038
161039
161040
161041
161042
161043
161044
161045
161046
161047
161048
161049
161050
161051
161052
161053
161054
161055
161056
161057
161058
161059
161060
161061
161062
161063
161064
161065
161066
161067
161068
161069
161070
161071
161072
161073
161074
161075
161076
161077
161078
161079
161080
161081
161082
161083
161084
161085
161086
161087
161088
    }else{
      fprintf(yyTraceFILE,"%sInput '%s' with pending reduce %d\n",
              yyTracePrompt,yyTokenName[yymajor],yyact-YY_MIN_REDUCE);
    }
  }
#endif

  while(1){ /* Exit by "break" */
    assert( yypParser->yytos>=yypParser->yystack );
    assert( yyact==yypParser->yytos->stateno );
    yyact = yy_find_shift_action((YYCODETYPE)yymajor,yyact);
    if( yyact >= YY_MIN_REDUCE ){
      unsigned int yyruleno = yyact - YY_MIN_REDUCE; /* Reduce by this rule */
      assert( yyruleno<(int)(sizeof(yyRuleName)/sizeof(yyRuleName[0])) );
#ifndef NDEBUG
      if( yyTraceFILE ){
        int yysize = yyRuleInfoNRhs[yyruleno];
        if( yysize ){
          fprintf(yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n",
            yyTracePrompt,
            yyruleno, yyRuleName[yyruleno],
            yyruleno<YYNRULE_WITH_ACTION ? "" : " without external action",
            yypParser->yytos[yysize].stateno);
        }else{
          fprintf(yyTraceFILE, "%sReduce %d [%s]%s.\n",
            yyTracePrompt, yyruleno, yyRuleName[yyruleno],
            yyruleno<YYNRULE_WITH_ACTION ? "" : " without external action");
        }
      }
#endif /* NDEBUG */

      /* Check that the stack is large enough to grow by a single entry
      ** if the RHS of the rule is empty.  This ensures that there is room
      ** enough on the stack to push the LHS value */
      if( yyRuleInfoNRhs[yyruleno]==0 ){
#ifdef YYTRACKMAXSTACKDEPTH
        if( (int)(yypParser->yytos - yypParser->yystack)>yypParser->yyhwm ){
          yypParser->yyhwm++;
          assert( yypParser->yyhwm ==
                  (int)(yypParser->yytos - yypParser->yystack));
        }
#endif
#if YYSTACKDEPTH>0
        if( yypParser->yytos>=yypParser->yystackEnd ){
          yyStackOverflow(yypParser);
          break;
        }
#else
        if( yypParser->yytos>=&yypParser->yystack[yypParser->yystksz-1] ){
          if( yyGrowStack(yypParser) ){
            yyStackOverflow(yypParser);
            break;
          }
        }
#endif
      }
      yyact = yy_reduce(yypParser,yyruleno,yymajor,yyminor sqlite3ParserCTX_PARAM);
    }else if( yyact <= YY_MAX_SHIFTREDUCE ){
      yy_shift(yypParser,yyact,(YYCODETYPE)yymajor,yyminor);
#ifndef YYNOERRORRECOVERY
      yypParser->yyerrcnt--;
#endif
      break;
    }else if( yyact==YY_ACCEPT_ACTION ){
160030
160031
160032
160033
160034
160035
160036
160037

160038
160039
160040
160041
160042
160043
160044
#ifndef YYNOERRORRECOVERY
        yypParser->yyerrcnt = -1;
#endif
      }
      break;
#endif
    }
  }while( yypParser->yytos>yypParser->yystack );

#ifndef NDEBUG
  if( yyTraceFILE ){
    yyStackEntry *i;
    char cDiv = '[';
    fprintf(yyTraceFILE,"%sReturn. Stack=",yyTracePrompt);
    for(i=&yypParser->yystack[1]; i<=yypParser->yytos; i++){
      fprintf(yyTraceFILE,"%c%s", cDiv, yyTokenName[i->major]);







<
>







161187
161188
161189
161190
161191
161192
161193

161194
161195
161196
161197
161198
161199
161200
161201
#ifndef YYNOERRORRECOVERY
        yypParser->yyerrcnt = -1;
#endif
      }
      break;
#endif
    }

  }
#ifndef NDEBUG
  if( yyTraceFILE ){
    yyStackEntry *i;
    char cDiv = '[';
    fprintf(yyTraceFILE,"%sReturn. Stack=",yyTracePrompt);
    for(i=&yypParser->yystack[1]; i<=yypParser->yytos; i++){
      fprintf(yyTraceFILE,"%c%s", cDiv, yyTokenName[i->major]);
160091
160092
160093
160094
160095
160096
160097

160098
160099
160100
160101
160102
160103
160104
160105
160106
** In the sqlite3GetToken() function, a switch() on aiClass[c] is implemented
** using a lookup table, whereas a switch() directly on c uses a binary search.
** The lookup table is much faster.  To maximize speed, and to ensure that
** a lookup table is used, all of the classes need to be small integers and
** all of them need to be used within the switch.
*/
#define CC_X          0    /* The letter 'x', or start of BLOB literal */

#define CC_KYWD       1    /* Alphabetics or '_'.  Usable in a keyword */
#define CC_ID         2    /* unicode characters usable in IDs */
#define CC_DIGIT      3    /* Digits */
#define CC_DOLLAR     4    /* '$' */
#define CC_VARALPHA   5    /* '@', '#', ':'.  Alphabetic SQL variables */
#define CC_VARNUM     6    /* '?'.  Numeric SQL variables */
#define CC_SPACE      7    /* Space characters */
#define CC_QUOTE      8    /* '"', '\'', or '`'.  String literals, quoted ids */
#define CC_QUOTE2     9    /* '['.   [...] style quoted ids */







>
|
<







161248
161249
161250
161251
161252
161253
161254
161255
161256

161257
161258
161259
161260
161261
161262
161263
** In the sqlite3GetToken() function, a switch() on aiClass[c] is implemented
** using a lookup table, whereas a switch() directly on c uses a binary search.
** The lookup table is much faster.  To maximize speed, and to ensure that
** a lookup table is used, all of the classes need to be small integers and
** all of them need to be used within the switch.
*/
#define CC_X          0    /* The letter 'x', or start of BLOB literal */
#define CC_KYWD0      1    /* First letter of a keyword */
#define CC_KYWD       2    /* Alphabetics or '_'.  Usable in a keyword */

#define CC_DIGIT      3    /* Digits */
#define CC_DOLLAR     4    /* '$' */
#define CC_VARALPHA   5    /* '@', '#', ':'.  Alphabetic SQL variables */
#define CC_VARNUM     6    /* '?'.  Numeric SQL variables */
#define CC_SPACE      7    /* Space characters */
#define CC_QUOTE      8    /* '"', '\'', or '`'.  String literals, quoted ids */
#define CC_QUOTE2     9    /* '['.   [...] style quoted ids */
160117
160118
160119
160120
160121
160122
160123

160124
160125
160126
160127
160128
160129
160130
160131
160132
160133
160134
160135
160136
160137
160138
160139
160140
160141
160142
160143
160144
160145
160146
160147
160148
160149
160150
160151
160152
160153
160154
160155
160156
160157
160158
160159
160160
160161
160162
160163
160164
160165
160166
160167
160168
160169
160170
160171
#define CC_PLUS      20    /* '+' */
#define CC_STAR      21    /* '*' */
#define CC_PERCENT   22    /* '%' */
#define CC_COMMA     23    /* ',' */
#define CC_AND       24    /* '&' */
#define CC_TILDA     25    /* '~' */
#define CC_DOT       26    /* '.' */

#define CC_ILLEGAL   27    /* Illegal character */
#define CC_NUL       28    /* 0x00 */

static const unsigned char aiClass[] = {
#ifdef SQLITE_ASCII
/*         x0  x1  x2  x3  x4  x5  x6  x7  x8  x9  xa  xb  xc  xd  xe  xf */
/* 0x */   28, 27, 27, 27, 27, 27, 27, 27, 27,  7,  7, 27,  7,  7, 27, 27,
/* 1x */   27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
/* 2x */    7, 15,  8,  5,  4, 22, 24,  8, 17, 18, 21, 20, 23, 11, 26, 16,
/* 3x */    3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  5, 19, 12, 14, 13,  6,
/* 4x */    5,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
/* 5x */    1,  1,  1,  1,  1,  1,  1,  1,  0,  1,  1,  9, 27, 27, 27,  1,
/* 6x */    8,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
/* 7x */    1,  1,  1,  1,  1,  1,  1,  1,  0,  1,  1, 27, 10, 27, 25, 27,
/* 8x */    2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,
/* 9x */    2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,
/* Ax */    2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,
/* Bx */    2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,
/* Cx */    2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,
/* Dx */    2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,
/* Ex */    2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,
/* Fx */    2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2
#endif
#ifdef SQLITE_EBCDIC
/*         x0  x1  x2  x3  x4  x5  x6  x7  x8  x9  xa  xb  xc  xd  xe  xf */
/* 0x */   27, 27, 27, 27, 27,  7, 27, 27, 27, 27, 27, 27,  7,  7, 27, 27,
/* 1x */   27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
/* 2x */   27, 27, 27, 27, 27,  7, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
/* 3x */   27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27,
/* 4x */    7, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 26, 12, 17, 20, 10,
/* 5x */   24, 27, 27, 27, 27, 27, 27, 27, 27, 27, 15,  4, 21, 18, 19, 27,
/* 6x */   11, 16, 27, 27, 27, 27, 27, 27, 27, 27, 27, 23, 22,  1, 13,  6,
/* 7x */   27, 27, 27, 27, 27, 27, 27, 27, 27,  8,  5,  5,  5,  8, 14,  8,
/* 8x */   27,  1,  1,  1,  1,  1,  1,  1,  1,  1, 27, 27, 27, 27, 27, 27,
/* 9x */   27,  1,  1,  1,  1,  1,  1,  1,  1,  1, 27, 27, 27, 27, 27, 27,
/* Ax */   27, 25,  1,  1,  1,  1,  1,  0,  1,  1, 27, 27, 27, 27, 27, 27,
/* Bx */   27, 27, 27, 27, 27, 27, 27, 27, 27, 27,  9, 27, 27, 27, 27, 27,
/* Cx */   27,  1,  1,  1,  1,  1,  1,  1,  1,  1, 27, 27, 27, 27, 27, 27,
/* Dx */   27,  1,  1,  1,  1,  1,  1,  1,  1,  1, 27, 27, 27, 27, 27, 27,
/* Ex */   27, 27,  1,  1,  1,  1,  1,  0,  1,  1, 27, 27, 27, 27, 27, 27,
/* Fx */    3,  3,  3,  3,  3,  3,  3,  3,  3,  3, 27, 27, 27, 27, 27, 27,
#endif
};

/*
** The charMap() macro maps alphabetic characters (only) into their
** lower-case ASCII equivalent.  On ASCII machines, this is just
** an upper-to-lower case map.  On EBCDIC machines we also need







>
|
|




|
|



|

|











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







161274
161275
161276
161277
161278
161279
161280
161281
161282
161283
161284
161285
161286
161287
161288
161289
161290
161291
161292
161293
161294
161295
161296
161297
161298
161299
161300
161301
161302
161303
161304
161305
161306
161307
161308
161309
161310
161311
161312
161313
161314
161315
161316
161317
161318
161319
161320
161321
161322
161323
161324
161325
161326
161327
161328
161329
#define CC_PLUS      20    /* '+' */
#define CC_STAR      21    /* '*' */
#define CC_PERCENT   22    /* '%' */
#define CC_COMMA     23    /* ',' */
#define CC_AND       24    /* '&' */
#define CC_TILDA     25    /* '~' */
#define CC_DOT       26    /* '.' */
#define CC_ID        27    /* unicode characters usable in IDs */
#define CC_ILLEGAL   28    /* Illegal character */
#define CC_NUL       29    /* 0x00 */

static const unsigned char aiClass[] = {
#ifdef SQLITE_ASCII
/*         x0  x1  x2  x3  x4  x5  x6  x7  x8  x9  xa  xb  xc  xd  xe  xf */
/* 0x */   29, 28, 28, 28, 28, 28, 28, 28, 28,  7,  7, 28,  7,  7, 28, 28,
/* 1x */   28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
/* 2x */    7, 15,  8,  5,  4, 22, 24,  8, 17, 18, 21, 20, 23, 11, 26, 16,
/* 3x */    3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  5, 19, 12, 14, 13,  6,
/* 4x */    5,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
/* 5x */    1,  1,  1,  1,  1,  1,  1,  1,  0,  2,  2,  9, 28, 28, 28,  2,
/* 6x */    8,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
/* 7x */    1,  1,  1,  1,  1,  1,  1,  1,  0,  2,  2, 28, 10, 28, 25, 28,
/* 8x */    2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,
/* 9x */    2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,
/* Ax */    2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,
/* Bx */    2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,
/* Cx */    2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,
/* Dx */    2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,
/* Ex */    2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,
/* Fx */    2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2,  2
#endif
#ifdef SQLITE_EBCDIC
/*         x0  x1  x2  x3  x4  x5  x6  x7  x8  x9  xa  xb  xc  xd  xe  xf */
/* 0x */   29, 28, 28, 28, 28,  7, 28, 28, 28, 28, 28, 28,  7,  7, 28, 28,
/* 1x */   28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
/* 2x */   28, 28, 28, 28, 28,  7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
/* 3x */   28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28,
/* 4x */    7, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 26, 12, 17, 20, 10,
/* 5x */   24, 28, 28, 28, 28, 28, 28, 28, 28, 28, 15,  4, 21, 18, 19, 28,
/* 6x */   11, 16, 28, 28, 28, 28, 28, 28, 28, 28, 28, 23, 22,  2, 13,  6,
/* 7x */   28, 28, 28, 28, 28, 28, 28, 28, 28,  8,  5,  5,  5,  8, 14,  8,
/* 8x */   28,  1,  1,  1,  1,  1,  1,  1,  1,  1, 28, 28, 28, 28, 28, 28,
/* 9x */   28,  1,  1,  1,  1,  1,  1,  1,  1,  1, 28, 28, 28, 28, 28, 28,
/* Ax */   28, 25,  1,  1,  1,  1,  1,  0,  2,  2, 28, 28, 28, 28, 28, 28,
/* Bx */   28, 28, 28, 28, 28, 28, 28, 28, 28, 28,  9, 28, 28, 28, 28, 28,
/* Cx */   28,  1,  1,  1,  1,  1,  1,  1,  1,  1, 28, 28, 28, 28, 28, 28,
/* Dx */   28,  1,  1,  1,  1,  1,  1,  1,  1,  1, 28, 28, 28, 28, 28, 28,
/* Ex */   28, 28,  1,  1,  1,  1,  1,  0,  2,  2, 28, 28, 28, 28, 28, 28,
/* Fx */    3,  3,  3,  3,  3,  3,  3,  3,  3,  3, 28, 28, 28, 28, 28, 28,
#endif
};

/*
** The charMap() macro maps alphabetic characters (only) into their
** lower-case ASCII equivalent.  On ASCII machines, this is just
** an upper-to-lower case map.  On EBCDIC machines we also need
160502
160503
160504
160505
160506
160507
160508
160509
160510
160511
160512
160513
160514
160515
160516
/* Check to see if z[0..n-1] is a keyword. If it is, write the
** parser symbol code for that keyword into *pType.  Always
** return the integer n (the length of the token). */
static int keywordCode(const char *z, int n, int *pType){
  int i, j;
  const char *zKW;
  if( n>=2 ){
    i = ((charMap(z[0])*4) ^ (charMap(z[n-1])*3) ^ n) % 127;
    for(i=((int)aKWHash[i])-1; i>=0; i=((int)aKWNext[i])-1){
      if( aKWLen[i]!=n ) continue;
      zKW = &zKWText[aKWOffset[i]];
#ifdef SQLITE_ASCII
      if( (z[0]&~0x20)!=zKW[0] ) continue;
      if( (z[1]&~0x20)!=zKW[1] ) continue;
      j = 2;







|







161660
161661
161662
161663
161664
161665
161666
161667
161668
161669
161670
161671
161672
161673
161674
/* Check to see if z[0..n-1] is a keyword. If it is, write the
** parser symbol code for that keyword into *pType.  Always
** return the integer n (the length of the token). */
static int keywordCode(const char *z, int n, int *pType){
  int i, j;
  const char *zKW;
  if( n>=2 ){
    i = ((charMap(z[0])*4) ^ (charMap(z[n-1])*3) ^ n*1) % 127;
    for(i=((int)aKWHash[i])-1; i>=0; i=((int)aKWNext[i])-1){
      if( aKWLen[i]!=n ) continue;
      zKW = &zKWText[aKWOffset[i]];
#ifdef SQLITE_ASCII
      if( (z[0]&~0x20)!=zKW[0] ) continue;
      if( (z[1]&~0x20)!=zKW[1] ) continue;
      j = 2;
161044
161045
161046
161047
161048
161049
161050
161051
161052
161053
161054
161055
161056
161057
161058
        }else{
          break;
        }
      }
      if( n==0 ) *tokenType = TK_ILLEGAL;
      return i;
    }
    case CC_KYWD: {
      for(i=1; aiClass[z[i]]<=CC_KYWD; i++){}
      if( IdChar(z[i]) ){
        /* This token started out using characters that can appear in keywords,
        ** but z[i] is a character not allowed within keywords, so this must
        ** be an identifier instead */
        i++;
        break;







|







162202
162203
162204
162205
162206
162207
162208
162209
162210
162211
162212
162213
162214
162215
162216
        }else{
          break;
        }
      }
      if( n==0 ) *tokenType = TK_ILLEGAL;
      return i;
    }
    case CC_KYWD0: {
      for(i=1; aiClass[z[i]]<=CC_KYWD; i++){}
      if( IdChar(z[i]) ){
        /* This token started out using characters that can appear in keywords,
        ** but z[i] is a character not allowed within keywords, so this must
        ** be an identifier instead */
        i++;
        break;
161074
161075
161076
161077
161078
161079
161080

161081
161082
161083
161084
161085
161086
161087
        return i;
      }
#endif
      /* If it is not a BLOB literal, then it must be an ID, since no
      ** SQL keywords start with the letter 'x'.  Fall through */
      /* no break */ deliberate_fall_through
    }

    case CC_ID: {
      i = 1;
      break;
    }
    case CC_NUL: {
      *tokenType = TK_ILLEGAL;
      return 0;







>







162232
162233
162234
162235
162236
162237
162238
162239
162240
162241
162242
162243
162244
162245
162246
        return i;
      }
#endif
      /* If it is not a BLOB literal, then it must be an ID, since no
      ** SQL keywords start with the letter 'x'.  Fall through */
      /* no break */ deliberate_fall_through
    }
    case CC_KYWD:
    case CC_ID: {
      i = 1;
      break;
    }
    case CC_NUL: {
      *tokenType = TK_ILLEGAL;
      return 0;
161256
161257
161258
161259
161260
161261
161262
161263
161264
161265
161266
161267
161268
161269
161270
161271
161272
161273
161274
161275
161276
161277
161278
161279
161280
161281
161282
    ** will take responsibility for freeing the Table structure.
    */
    sqlite3DeleteTable(db, pParse->pNewTable);
  }
  if( !IN_RENAME_OBJECT ){
    sqlite3DeleteTrigger(db, pParse->pNewTrigger);
  }

  if( pParse->pWithToFree ) sqlite3WithDelete(db, pParse->pWithToFree);
  sqlite3DbFree(db, pParse->pVList);
  while( pParse->pAinc ){
    AutoincInfo *p = pParse->pAinc;
    pParse->pAinc = p->pNext;
    sqlite3DbFreeNN(db, p);
  }
  while( pParse->pZombieTab ){
    Table *p = pParse->pZombieTab;
    pParse->pZombieTab = p->pNextZombie;
    sqlite3DeleteTable(db, p);
  }
  db->pParse = pParse->pParentParse;
  pParse->pParentParse = 0;
  assert( nErr==0 || pParse->rc!=SQLITE_OK );
  return nErr;
}









<
<

<
<
<
<
<
<
<
<
<
<







162415
162416
162417
162418
162419
162420
162421


162422










162423
162424
162425
162426
162427
162428
162429
    ** will take responsibility for freeing the Table structure.
    */
    sqlite3DeleteTable(db, pParse->pNewTable);
  }
  if( !IN_RENAME_OBJECT ){
    sqlite3DeleteTrigger(db, pParse->pNewTrigger);
  }


  sqlite3DbFree(db, pParse->pVList);










  db->pParse = pParse->pParentParse;
  pParse->pParentParse = 0;
  assert( nErr==0 || pParse->rc!=SQLITE_OK );
  return nErr;
}


165881
165882
165883
165884
165885
165886
165887
165888
165889
165890
165891
165892
165893
165894
165895
    ** operation N should be 0.  The idea is that a test program (like the
    ** SQL Logic Test or SLT test module) can run the same SQL multiple times
    ** with various optimizations disabled to verify that the same answer
    ** is obtained in every case.
    */
    case SQLITE_TESTCTRL_OPTIMIZATIONS: {
      sqlite3 *db = va_arg(ap, sqlite3*);
      db->dbOptFlags = (u16)(va_arg(ap, int) & 0xffff);
      break;
    }

    /*   sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, int onoff);
    **
    ** If parameter onoff is non-zero, subsequent calls to localtime()
    ** and its variants fail. If onoff is zero, undo this setting.







|







167028
167029
167030
167031
167032
167033
167034
167035
167036
167037
167038
167039
167040
167041
167042
    ** operation N should be 0.  The idea is that a test program (like the
    ** SQL Logic Test or SLT test module) can run the same SQL multiple times
    ** with various optimizations disabled to verify that the same answer
    ** is obtained in every case.
    */
    case SQLITE_TESTCTRL_OPTIMIZATIONS: {
      sqlite3 *db = va_arg(ap, sqlite3*);
      db->dbOptFlags = va_arg(ap, u32);
      break;
    }

    /*   sqlite3_test_control(SQLITE_TESTCTRL_LOCALTIME_FAULT, int onoff);
    **
    ** If parameter onoff is non-zero, subsequent calls to localtime()
    ** and its variants fail. If onoff is zero, undo this setting.
166056
166057
166058
166059
166060
166061
166062

















166063


166064
166065
166066
166067
166068
166069
166070
      sqlite3 *db = va_arg(ap, sqlite3*);
      u64 *pn = va_arg(ap, sqlite3_uint64*);
      *pn = sqlite3BtreeSeekCount(db->aDb->pBt);
      (void)db;  /* Silence harmless unused variable warning */
      break;
    }





















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

/*







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







167203
167204
167205
167206
167207
167208
167209
167210
167211
167212
167213
167214
167215
167216
167217
167218
167219
167220
167221
167222
167223
167224
167225
167226
167227
167228
167229
167230
167231
167232
167233
167234
167235
167236
      sqlite3 *db = va_arg(ap, sqlite3*);
      u64 *pn = va_arg(ap, sqlite3_uint64*);
      *pn = sqlite3BtreeSeekCount(db->aDb->pBt);
      (void)db;  /* Silence harmless unused variable warning */
      break;
    }

    /*  sqlite3_test_control(SQLITE_TESTCTRL_TRACEFLAGS, op, ptr)
    **
    **  "ptr" is a pointer to a u32.
    **
    **   op==0       Store the current sqlite3SelectTrace in *ptr
    **   op==1       Set sqlite3SelectTrace to the value *ptr
    **   op==3       Store the current sqlite3WhereTrace in *ptr
    **   op==3       Set sqlite3WhereTrace to the value *ptr
    */
    case SQLITE_TESTCTRL_TRACEFLAGS: {
       int opTrace = va_arg(ap, int);
       u32 *ptr = va_arg(ap, u32*);
       switch( opTrace ){
         case 0:   *ptr = sqlite3SelectTrace;      break;
         case 1:   sqlite3SelectTrace = *ptr;      break;
         case 2:   *ptr = sqlite3WhereTrace;       break;
         case 3:   sqlite3WhereTrace = *ptr;       break;
       }
       break;
    }
  }
  va_end(ap);
#endif /* SQLITE_UNTESTABLE */
  return rc;
}

/*
203335
203336
203337
203338
203339
203340
203341

203342
203343
203344
203345
203346
203347
203348
  char *zDb;                      /* Name of database session is attached to */
  int bEnable;                    /* True if currently recording */
  int bIndirect;                  /* True if all changes are indirect */
  int bAutoAttach;                /* True to auto-attach tables */
  int rc;                         /* Non-zero if an error has occurred */
  void *pFilterCtx;               /* First argument to pass to xTableFilter */
  int (*xTableFilter)(void *pCtx, const char *zTab);

  sqlite3_value *pZeroBlob;       /* Value containing X'' */
  sqlite3_session *pNext;         /* Next session object on same db. */
  SessionTable *pTable;           /* List of attached tables */
  SessionHook hook;               /* APIs to grab new and old data with */
};

/*







>







204501
204502
204503
204504
204505
204506
204507
204508
204509
204510
204511
204512
204513
204514
204515
  char *zDb;                      /* Name of database session is attached to */
  int bEnable;                    /* True if currently recording */
  int bIndirect;                  /* True if all changes are indirect */
  int bAutoAttach;                /* True to auto-attach tables */
  int rc;                         /* Non-zero if an error has occurred */
  void *pFilterCtx;               /* First argument to pass to xTableFilter */
  int (*xTableFilter)(void *pCtx, const char *zTab);
  i64 nMalloc;                    /* Number of bytes of data allocated */
  sqlite3_value *pZeroBlob;       /* Value containing X'' */
  sqlite3_session *pNext;         /* Next session object on same db. */
  SessionTable *pTable;           /* List of attached tables */
  SessionHook hook;               /* APIs to grab new and old data with */
};

/*
203718
203719
203720
203721
203722
203723
203724




















203725
203726
203727
203728
203729
203730
203731
    if( aBuf ) aBuf[0] = '\0';
  }

  if( pnWrite ) *pnWrite += nByte;
  return SQLITE_OK;
}






















/*
** This macro is used to calculate hash key values for data structures. In
** order to use this macro, the entire data structure must be represented
** as a series of unsigned integers. In order to calculate a hash-key value
** for a data structure represented as three such integers, the macro may
** then be used as follows:







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







204885
204886
204887
204888
204889
204890
204891
204892
204893
204894
204895
204896
204897
204898
204899
204900
204901
204902
204903
204904
204905
204906
204907
204908
204909
204910
204911
204912
204913
204914
204915
204916
204917
204918
    if( aBuf ) aBuf[0] = '\0';
  }

  if( pnWrite ) *pnWrite += nByte;
  return SQLITE_OK;
}

/*
** Allocate and return a pointer to a buffer nByte bytes in size. If
** pSession is not NULL, increase the sqlite3_session.nMalloc variable
** by the number of bytes allocated.
*/
static void *sessionMalloc64(sqlite3_session *pSession, i64 nByte){
  void *pRet = sqlite3_malloc64(nByte);
  if( pSession ) pSession->nMalloc += sqlite3_msize(pRet);
  return pRet;
}

/*
** Free buffer pFree, which must have been allocated by an earlier
** call to sessionMalloc64(). If pSession is not NULL, decrease the
** sqlite3_session.nMalloc counter by the number of bytes freed.
*/
static void sessionFree(sqlite3_session *pSession, void *pFree){
  if( pSession ) pSession->nMalloc -= sqlite3_msize(pFree);
  sqlite3_free(pFree);
}

/*
** This macro is used to calculate hash key values for data structures. In
** order to use this macro, the entire data structure must be represented
** as a series of unsigned integers. In order to calculate a hash-key value
** for a data structure represented as three such integers, the macro may
** then be used as follows:
204185
204186
204187
204188
204189
204190
204191
204192




204193
204194
204195
204196
204197
204198


204199
204200
204201
204202
204203
204204
204205
204206
204207
204208
204209
204210
204211
204212
204213
204214
204215
204216
204217
204218
204219
204220
204221
204222
204223
204224
204225
204226
** SQLITE_OK.
**
** It is possible that a non-fatal OOM error occurs in this function. In
** that case the hash-table does not grow, but SQLITE_OK is returned anyway.
** Growing the hash table in this case is a performance optimization only,
** it is not required for correct operation.
*/
static int sessionGrowHash(int bPatchset, SessionTable *pTab){




  if( pTab->nChange==0 || pTab->nEntry>=(pTab->nChange/2) ){
    int i;
    SessionChange **apNew;
    sqlite3_int64 nNew = 2*(sqlite3_int64)(pTab->nChange ? pTab->nChange : 128);

    apNew = (SessionChange **)sqlite3_malloc64(sizeof(SessionChange *) * nNew);


    if( apNew==0 ){
      if( pTab->nChange==0 ){
        return SQLITE_ERROR;
      }
      return SQLITE_OK;
    }
    memset(apNew, 0, sizeof(SessionChange *) * nNew);

    for(i=0; i<pTab->nChange; i++){
      SessionChange *p;
      SessionChange *pNext;
      for(p=pTab->apChange[i]; p; p=pNext){
        int bPkOnly = (p->op==SQLITE_DELETE && bPatchset);
        int iHash = sessionChangeHash(pTab, bPkOnly, p->aRecord, nNew);
        pNext = p->pNext;
        p->pNext = apNew[iHash];
        apNew[iHash] = p;
      }
    }

    sqlite3_free(pTab->apChange);
    pTab->nChange = nNew;
    pTab->apChange = apNew;
  }

  return SQLITE_OK;
}








|
>
>
>
>





|
>
>




















|







205372
205373
205374
205375
205376
205377
205378
205379
205380
205381
205382
205383
205384
205385
205386
205387
205388
205389
205390
205391
205392
205393
205394
205395
205396
205397
205398
205399
205400
205401
205402
205403
205404
205405
205406
205407
205408
205409
205410
205411
205412
205413
205414
205415
205416
205417
205418
205419
** SQLITE_OK.
**
** It is possible that a non-fatal OOM error occurs in this function. In
** that case the hash-table does not grow, but SQLITE_OK is returned anyway.
** Growing the hash table in this case is a performance optimization only,
** it is not required for correct operation.
*/
static int sessionGrowHash(
  sqlite3_session *pSession,      /* For memory accounting. May be NULL */
  int bPatchset,
  SessionTable *pTab
){
  if( pTab->nChange==0 || pTab->nEntry>=(pTab->nChange/2) ){
    int i;
    SessionChange **apNew;
    sqlite3_int64 nNew = 2*(sqlite3_int64)(pTab->nChange ? pTab->nChange : 128);

    apNew = (SessionChange**)sessionMalloc64(
        pSession, sizeof(SessionChange*) * nNew
    );
    if( apNew==0 ){
      if( pTab->nChange==0 ){
        return SQLITE_ERROR;
      }
      return SQLITE_OK;
    }
    memset(apNew, 0, sizeof(SessionChange *) * nNew);

    for(i=0; i<pTab->nChange; i++){
      SessionChange *p;
      SessionChange *pNext;
      for(p=pTab->apChange[i]; p; p=pNext){
        int bPkOnly = (p->op==SQLITE_DELETE && bPatchset);
        int iHash = sessionChangeHash(pTab, bPkOnly, p->aRecord, nNew);
        pNext = p->pNext;
        p->pNext = apNew[iHash];
        apNew[iHash] = p;
      }
    }

    sessionFree(pSession, pTab->apChange);
    pTab->nChange = nNew;
    pTab->apChange = apNew;
  }

  return SQLITE_OK;
}

204246
204247
204248
204249
204250
204251
204252

204253
204254
204255
204256
204257
204258
204259
**     *pazCol = {"w", "x", "y", "z"}
**     *pabPK  = {1, 0, 0, 1}
**
** All returned buffers are part of the same single allocation, which must
** be freed using sqlite3_free() by the caller
*/
static int sessionTableInfo(

  sqlite3 *db,                    /* Database connection */
  const char *zDb,                /* Name of attached database (e.g. "main") */
  const char *zThis,              /* Table name */
  int *pnCol,                     /* OUT: number of columns */
  const char **pzTab,             /* OUT: Copy of zThis */
  const char ***pazCol,           /* OUT: Array of column names for table */
  u8 **pabPK                      /* OUT: Array of booleans - true for PK col */







>







205439
205440
205441
205442
205443
205444
205445
205446
205447
205448
205449
205450
205451
205452
205453
**     *pazCol = {"w", "x", "y", "z"}
**     *pabPK  = {1, 0, 0, 1}
**
** All returned buffers are part of the same single allocation, which must
** be freed using sqlite3_free() by the caller
*/
static int sessionTableInfo(
  sqlite3_session *pSession,      /* For memory accounting. May be NULL */
  sqlite3 *db,                    /* Database connection */
  const char *zDb,                /* Name of attached database (e.g. "main") */
  const char *zThis,              /* Table name */
  int *pnCol,                     /* OUT: number of columns */
  const char **pzTab,             /* OUT: Copy of zThis */
  const char ***pazCol,           /* OUT: Array of column names for table */
  u8 **pabPK                      /* OUT: Array of booleans - true for PK col */
204300
204301
204302
204303
204304
204305
204306
204307
204308
204309
204310
204311
204312
204313
204314
    nByte += sqlite3_column_bytes(pStmt, 1);
    nDbCol++;
  }
  rc = sqlite3_reset(pStmt);

  if( rc==SQLITE_OK ){
    nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1);
    pAlloc = sqlite3_malloc64(nByte);
    if( pAlloc==0 ){
      rc = SQLITE_NOMEM;
    }
  }
  if( rc==SQLITE_OK ){
    azCol = (char **)pAlloc;
    pAlloc = (u8 *)&azCol[nDbCol];







|







205494
205495
205496
205497
205498
205499
205500
205501
205502
205503
205504
205505
205506
205507
205508
    nByte += sqlite3_column_bytes(pStmt, 1);
    nDbCol++;
  }
  rc = sqlite3_reset(pStmt);

  if( rc==SQLITE_OK ){
    nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1);
    pAlloc = sessionMalloc64(pSession, nByte);
    if( pAlloc==0 ){
      rc = SQLITE_NOMEM;
    }
  }
  if( rc==SQLITE_OK ){
    azCol = (char **)pAlloc;
    pAlloc = (u8 *)&azCol[nDbCol];
204343
204344
204345
204346
204347
204348
204349
204350
204351
204352
204353
204354
204355
204356
204357
    *pabPK = abPK;
    *pnCol = nDbCol;
  }else{
    *pazCol = 0;
    *pabPK = 0;
    *pnCol = 0;
    if( pzTab ) *pzTab = 0;
    sqlite3_free(azCol);
  }
  sqlite3_finalize(pStmt);
  return rc;
}

/*
** This function is only called from within a pre-update handler for a







|







205537
205538
205539
205540
205541
205542
205543
205544
205545
205546
205547
205548
205549
205550
205551
    *pabPK = abPK;
    *pnCol = nDbCol;
  }else{
    *pazCol = 0;
    *pabPK = 0;
    *pnCol = 0;
    if( pzTab ) *pzTab = 0;
    sessionFree(pSession, azCol);
  }
  sqlite3_finalize(pStmt);
  return rc;
}

/*
** This function is only called from within a pre-update handler for a
204365
204366
204367
204368
204369
204370
204371
204372
204373
204374
204375
204376
204377
204378
204379
** indicate that updates on this table should be ignored. SessionTable.abPK
** is set to NULL in this case.
*/
static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){
  if( pTab->nCol==0 ){
    u8 *abPK;
    assert( pTab->azCol==0 || pTab->abPK==0 );
    pSession->rc = sessionTableInfo(pSession->db, pSession->zDb,
        pTab->zName, &pTab->nCol, 0, &pTab->azCol, &abPK
    );
    if( pSession->rc==SQLITE_OK ){
      int i;
      for(i=0; i<pTab->nCol; i++){
        if( abPK[i] ){
          pTab->abPK = abPK;







|







205559
205560
205561
205562
205563
205564
205565
205566
205567
205568
205569
205570
205571
205572
205573
** indicate that updates on this table should be ignored. SessionTable.abPK
** is set to NULL in this case.
*/
static int sessionInitTable(sqlite3_session *pSession, SessionTable *pTab){
  if( pTab->nCol==0 ){
    u8 *abPK;
    assert( pTab->azCol==0 || pTab->abPK==0 );
    pSession->rc = sessionTableInfo(pSession, pSession->db, pSession->zDb,
        pTab->zName, &pTab->nCol, 0, &pTab->azCol, &abPK
    );
    if( pSession->rc==SQLITE_OK ){
      int i;
      for(i=0; i<pTab->nCol; i++){
        if( abPK[i] ){
          pTab->abPK = abPK;
204456
204457
204458
204459
204460
204461
204462
204463
204464
204465
204466
204467
204468
204469
204470
  ** number of columns in the table.  */
  if( pTab->nCol!=pSession->hook.xCount(pSession->hook.pCtx) ){
    pSession->rc = SQLITE_SCHEMA;
    return;
  }

  /* Grow the hash table if required */
  if( sessionGrowHash(0, pTab) ){
    pSession->rc = SQLITE_NOMEM;
    return;
  }

  if( pTab->bStat1 ){
    stat1.hook = pSession->hook;
    stat1.pSession = pSession;







|







205650
205651
205652
205653
205654
205655
205656
205657
205658
205659
205660
205661
205662
205663
205664
  ** number of columns in the table.  */
  if( pTab->nCol!=pSession->hook.xCount(pSession->hook.pCtx) ){
    pSession->rc = SQLITE_SCHEMA;
    return;
  }

  /* Grow the hash table if required */
  if( sessionGrowHash(pSession, 0, pTab) ){
    pSession->rc = SQLITE_NOMEM;
    return;
  }

  if( pTab->bStat1 ){
    stat1.hook = pSession->hook;
    stat1.pSession = pSession;
204523
204524
204525
204526
204527
204528
204529
204530
204531
204532
204533
204534
204535
204536
204537
        /* This may fail if SQLite value p contains a utf-16 string that must
        ** be converted to utf-8 and an OOM error occurs while doing so. */
        rc = sessionSerializeValue(0, p, &nByte);
        if( rc!=SQLITE_OK ) goto error_out;
      }

      /* Allocate the change object */
      pChange = (SessionChange *)sqlite3_malloc64(nByte);
      if( !pChange ){
        rc = SQLITE_NOMEM;
        goto error_out;
      }else{
        memset(pChange, 0, sizeof(SessionChange));
        pChange->aRecord = (u8 *)&pChange[1];
      }







|







205717
205718
205719
205720
205721
205722
205723
205724
205725
205726
205727
205728
205729
205730
205731
        /* This may fail if SQLite value p contains a utf-16 string that must
        ** be converted to utf-8 and an OOM error occurs while doing so. */
        rc = sessionSerializeValue(0, p, &nByte);
        if( rc!=SQLITE_OK ) goto error_out;
      }

      /* Allocate the change object */
      pChange = (SessionChange *)sessionMalloc64(pSession, nByte);
      if( !pChange ){
        rc = SQLITE_NOMEM;
        goto error_out;
      }else{
        memset(pChange, 0, sizeof(SessionChange));
        pChange->aRecord = (u8 *)&pChange[1];
      }
204896
204897
204898
204899
204900
204901
204902
204903
204904
204905
204906
204907
204908
204909
204910
    /* Check the table schemas match */
    if( rc==SQLITE_OK ){
      int bHasPk = 0;
      int bMismatch = 0;
      int nCol;                   /* Columns in zFrom.zTbl */
      u8 *abPK;
      const char **azCol = 0;
      rc = sessionTableInfo(db, zFrom, zTbl, &nCol, 0, &azCol, &abPK);
      if( rc==SQLITE_OK ){
        if( pTo->nCol!=nCol ){
          bMismatch = 1;
        }else{
          int i;
          for(i=0; i<nCol; i++){
            if( pTo->abPK[i]!=abPK[i] ) bMismatch = 1;







|







206090
206091
206092
206093
206094
206095
206096
206097
206098
206099
206100
206101
206102
206103
206104
    /* Check the table schemas match */
    if( rc==SQLITE_OK ){
      int bHasPk = 0;
      int bMismatch = 0;
      int nCol;                   /* Columns in zFrom.zTbl */
      u8 *abPK;
      const char **azCol = 0;
      rc = sessionTableInfo(0, db, zFrom, zTbl, &nCol, 0, &azCol, &abPK);
      if( rc==SQLITE_OK ){
        if( pTo->nCol!=nCol ){
          bMismatch = 1;
        }else{
          int i;
          for(i=0; i<nCol; i++){
            if( pTo->abPK[i]!=abPK[i] ) bMismatch = 1;
204994
204995
204996
204997
204998
204999
205000
205001
205002
205003
205004
205005
205006
205007
205008
205009
205010
205011
205012
205013
205014
205015
205016
205017
205018
205019
205020
205021
205022
205023
205024
205025
  return SQLITE_OK;
}

/*
** Free the list of table objects passed as the first argument. The contents
** of the changed-rows hash tables are also deleted.
*/
static void sessionDeleteTable(SessionTable *pList){
  SessionTable *pNext;
  SessionTable *pTab;

  for(pTab=pList; pTab; pTab=pNext){
    int i;
    pNext = pTab->pNext;
    for(i=0; i<pTab->nChange; i++){
      SessionChange *p;
      SessionChange *pNextChange;
      for(p=pTab->apChange[i]; p; p=pNextChange){
        pNextChange = p->pNext;
        sqlite3_free(p);
      }
    }
    sqlite3_free((char*)pTab->azCol);  /* cast works around VC++ bug */
    sqlite3_free(pTab->apChange);
    sqlite3_free(pTab);
  }
}

/*
** Delete a session object previously allocated using sqlite3session_create().
*/
SQLITE_API void sqlite3session_delete(sqlite3_session *pSession){







|











|


|
|
|







206188
206189
206190
206191
206192
206193
206194
206195
206196
206197
206198
206199
206200
206201
206202
206203
206204
206205
206206
206207
206208
206209
206210
206211
206212
206213
206214
206215
206216
206217
206218
206219
  return SQLITE_OK;
}

/*
** Free the list of table objects passed as the first argument. The contents
** of the changed-rows hash tables are also deleted.
*/
static void sessionDeleteTable(sqlite3_session *pSession, SessionTable *pList){
  SessionTable *pNext;
  SessionTable *pTab;

  for(pTab=pList; pTab; pTab=pNext){
    int i;
    pNext = pTab->pNext;
    for(i=0; i<pTab->nChange; i++){
      SessionChange *p;
      SessionChange *pNextChange;
      for(p=pTab->apChange[i]; p; p=pNextChange){
        pNextChange = p->pNext;
        sessionFree(pSession, p);
      }
    }
    sessionFree(pSession, (char*)pTab->azCol);  /* cast works around VC++ bug */
    sessionFree(pSession, pTab->apChange);
    sessionFree(pSession, pTab);
  }
}

/*
** Delete a session object previously allocated using sqlite3session_create().
*/
SQLITE_API void sqlite3session_delete(sqlite3_session *pSession){
205039
205040
205041
205042
205043
205044
205045
205046
205047

205048

205049
205050
205051
205052
205053
205054
205055
    }
  }
  sqlite3_mutex_leave(sqlite3_db_mutex(db));
  sqlite3ValueFree(pSession->pZeroBlob);

  /* Delete all attached table objects. And the contents of their
  ** associated hash-tables. */
  sessionDeleteTable(pSession->pTable);


  /* Free the session object itself. */

  sqlite3_free(pSession);
}

/*
** Set a table filter on a Session Object.
*/
SQLITE_API void sqlite3session_table_filter(







|

>
|
>







206233
206234
206235
206236
206237
206238
206239
206240
206241
206242
206243
206244
206245
206246
206247
206248
206249
206250
206251
    }
  }
  sqlite3_mutex_leave(sqlite3_db_mutex(db));
  sqlite3ValueFree(pSession->pZeroBlob);

  /* Delete all attached table objects. And the contents of their
  ** associated hash-tables. */
  sessionDeleteTable(pSession, pSession->pTable);

  /* Assert that all allocations have been freed and then free the
  ** session object itself. */
  assert( pSession->nMalloc==0 );
  sqlite3_free(pSession);
}

/*
** Set a table filter on a Session Object.
*/
SQLITE_API void sqlite3session_table_filter(
205088
205089
205090
205091
205092
205093
205094
205095

205096
205097
205098
205099
205100
205101
205102
    nName = sqlite3Strlen30(zName);
    for(pTab=pSession->pTable; pTab; pTab=pTab->pNext){
      if( 0==sqlite3_strnicmp(pTab->zName, zName, nName+1) ) break;
    }

    if( !pTab ){
      /* Allocate new SessionTable object. */
      pTab = (SessionTable *)sqlite3_malloc64(sizeof(SessionTable) + nName + 1);

      if( !pTab ){
        rc = SQLITE_NOMEM;
      }else{
        /* Populate the new SessionTable object and link it into the list.
        ** The new object must be linked onto the end of the list, not
        ** simply added to the start of it in order to ensure that tables
        ** appear in the correct order when a changeset or patchset is







|
>







206284
206285
206286
206287
206288
206289
206290
206291
206292
206293
206294
206295
206296
206297
206298
206299
    nName = sqlite3Strlen30(zName);
    for(pTab=pSession->pTable; pTab; pTab=pTab->pNext){
      if( 0==sqlite3_strnicmp(pTab->zName, zName, nName+1) ) break;
    }

    if( !pTab ){
      /* Allocate new SessionTable object. */
      int nByte = sizeof(SessionTable) + nName + 1;
      pTab = (SessionTable*)sessionMalloc64(pSession, nByte);
      if( !pTab ){
        rc = SQLITE_NOMEM;
      }else{
        /* Populate the new SessionTable object and link it into the list.
        ** The new object must be linked onto the end of the list, not
        ** simply added to the start of it in order to ensure that tables
        ** appear in the correct order when a changeset or patchset is
205685
205686
205687
205688
205689
205690
205691
205692
205693
205694
205695
205696
205697
205698
205699
      const char **azCol = 0;     /* Table columns */
      int i;                      /* Used to iterate through hash buckets */
      sqlite3_stmt *pSel = 0;     /* SELECT statement to query table pTab */
      int nRewind = buf.nBuf;     /* Initial size of write buffer */
      int nNoop;                  /* Size of buffer after writing tbl header */

      /* Check the table schema is still Ok. */
      rc = sessionTableInfo(db, pSession->zDb, zName, &nCol, 0, &azCol, &abPK);
      if( !rc && (pTab->nCol!=nCol || memcmp(abPK, pTab->abPK, nCol)) ){
        rc = SQLITE_SCHEMA;
      }

      /* Write a table header */
      sessionAppendTableHdr(&buf, bPatchset, pTab, &rc);








|







206882
206883
206884
206885
206886
206887
206888
206889
206890
206891
206892
206893
206894
206895
206896
      const char **azCol = 0;     /* Table columns */
      int i;                      /* Used to iterate through hash buckets */
      sqlite3_stmt *pSel = 0;     /* SELECT statement to query table pTab */
      int nRewind = buf.nBuf;     /* Initial size of write buffer */
      int nNoop;                  /* Size of buffer after writing tbl header */

      /* Check the table schema is still Ok. */
      rc = sessionTableInfo(0, db, pSession->zDb, zName, &nCol, 0,&azCol,&abPK);
      if( !rc && (pTab->nCol!=nCol || memcmp(abPK, pTab->abPK, nCol)) ){
        rc = SQLITE_SCHEMA;
      }

      /* Write a table header */
      sessionAppendTableHdr(&buf, bPatchset, pTab, &rc);

205859
205860
205861
205862
205863
205864
205865







205866
205867
205868
205869
205870
205871
205872
  for(pTab=pSession->pTable; pTab && ret==0; pTab=pTab->pNext){
    ret = (pTab->nEntry>0);
  }
  sqlite3_mutex_leave(sqlite3_db_mutex(pSession->db));

  return (ret==0);
}








/*
** Do the work for either sqlite3changeset_start() or start_strm().
*/
static int sessionChangesetStart(
  sqlite3_changeset_iter **pp,    /* OUT: Changeset iterator handle */
  int (*xInput)(void *pIn, void *pData, int *pnData),







>
>
>
>
>
>
>







207056
207057
207058
207059
207060
207061
207062
207063
207064
207065
207066
207067
207068
207069
207070
207071
207072
207073
207074
207075
207076
  for(pTab=pSession->pTable; pTab && ret==0; pTab=pTab->pNext){
    ret = (pTab->nEntry>0);
  }
  sqlite3_mutex_leave(sqlite3_db_mutex(pSession->db));

  return (ret==0);
}

/*
** Return the amount of heap memory in use.
*/
SQLITE_API sqlite3_int64 sqlite3session_memory_used(sqlite3_session *pSession){
  return pSession->nMalloc;
}

/*
** Do the work for either sqlite3changeset_start() or start_strm().
*/
static int sessionChangesetStart(
  sqlite3_changeset_iter **pp,    /* OUT: Changeset iterator handle */
  int (*xInput)(void *pIn, void *pData, int *pnData),
207671
207672
207673
207674
207675
207676
207677
207678
207679
207680
207681
207682
207683
207684
207685
        nTab = (int)strlen(zTab);
        sApply.azCol = (const char **)zTab;
      }else{
        int nMinCol = 0;
        int i;

        sqlite3changeset_pk(pIter, &abPK, 0);
        rc = sessionTableInfo(
            db, "main", zNew, &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK
        );
        if( rc!=SQLITE_OK ) break;
        for(i=0; i<sApply.nCol; i++){
          if( sApply.abPK[i] ) nMinCol = i+1;
        }








|







208875
208876
208877
208878
208879
208880
208881
208882
208883
208884
208885
208886
208887
208888
208889
        nTab = (int)strlen(zTab);
        sApply.azCol = (const char **)zTab;
      }else{
        int nMinCol = 0;
        int i;

        sqlite3changeset_pk(pIter, &abPK, 0);
        rc = sessionTableInfo(0,
            db, "main", zNew, &sApply.nCol, &zTab, &sApply.azCol, &sApply.abPK
        );
        if( rc!=SQLITE_OK ) break;
        for(i=0; i<sApply.nCol; i++){
          if( sApply.abPK[i] ) nMinCol = i+1;
        }

208150
208151
208152
208153
208154
208155
208156
208157
208158
208159
208160
208161
208162
208163
208164
        *ppTab = pTab;
      }else if( pTab->nCol!=nCol || memcmp(pTab->abPK, abPK, nCol) ){
        rc = SQLITE_SCHEMA;
        break;
      }
    }

    if( sessionGrowHash(pIter->bPatchset, pTab) ){
      rc = SQLITE_NOMEM;
      break;
    }
    iHash = sessionChangeHash(
        pTab, (pIter->bPatchset && op==SQLITE_DELETE), aRec, pTab->nChange
    );








|







209354
209355
209356
209357
209358
209359
209360
209361
209362
209363
209364
209365
209366
209367
209368
        *ppTab = pTab;
      }else if( pTab->nCol!=nCol || memcmp(pTab->abPK, abPK, nCol) ){
        rc = SQLITE_SCHEMA;
        break;
      }
    }

    if( sessionGrowHash(0, pIter->bPatchset, pTab) ){
      rc = SQLITE_NOMEM;
      break;
    }
    iHash = sessionChangeHash(
        pTab, (pIter->bPatchset && op==SQLITE_DELETE), aRec, pTab->nChange
    );

208336
208337
208338
208339
208340
208341
208342
208343
208344
208345
208346
208347
208348
208349
208350
}

/*
** Delete a changegroup object.
*/
SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){
  if( pGrp ){
    sessionDeleteTable(pGrp->pList);
    sqlite3_free(pGrp);
  }
}

/*
** Combine two changesets together.
*/







|







209540
209541
209542
209543
209544
209545
209546
209547
209548
209549
209550
209551
209552
209553
209554
}

/*
** Delete a changegroup object.
*/
SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){
  if( pGrp ){
    sessionDeleteTable(0, pGrp->pList);
    sqlite3_free(pGrp);
  }
}

/*
** Combine two changesets together.
*/
208737
208738
208739
208740
208741
208742
208743
208744
208745
208746
208747
208748
208749
208750
208751
}

/*
** Destroy a rebaser object
*/
SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p){
  if( p ){
    sessionDeleteTable(p->grp.pList);
    sqlite3_free(p);
  }
}

/*
** Global configuration
*/







|







209941
209942
209943
209944
209945
209946
209947
209948
209949
209950
209951
209952
209953
209954
209955
}

/*
** Destroy a rebaser object
*/
SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p){
  if( p ){
    sessionDeleteTable(0, p->grp.pList);
    sqlite3_free(p);
  }
}

/*
** Global configuration
*/
211199
211200
211201
211202
211203
211204
211205
211206
211207
211208
211209
211210
211211
211212
211213
211214
211215
211216
211217
211218
211219
211220
211221
211222
211223
211224
211225
211226
211227
211228
211229
211230
211231
211232
211233
211234
211235
211236
211237
211238
211239
211240
211241
211242
211243
211244
211245
211246
211247
211248
211249
211250
211251
211252
211253
211254
211255
211256
211257
211258
211259
211260
211261
  fts5YYACTIONTYPE fts5yyact;             /* The next action */
  fts5yyStackEntry *fts5yymsp;            /* The top of the parser's stack */
  int fts5yysize;                     /* Amount to pop the stack */
  sqlite3Fts5ParserARG_FETCH
  (void)fts5yyLookahead;
  (void)fts5yyLookaheadToken;
  fts5yymsp = fts5yypParser->fts5yytos;
  assert( fts5yyruleno<(int)(sizeof(fts5yyRuleName)/sizeof(fts5yyRuleName[0])) );
#ifndef NDEBUG
  if( fts5yyTraceFILE ){
    fts5yysize = fts5yyRuleInfoNRhs[fts5yyruleno];
    if( fts5yysize ){
      fprintf(fts5yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n",
        fts5yyTracePrompt,
        fts5yyruleno, fts5yyRuleName[fts5yyruleno],
        fts5yyruleno<fts5YYNRULE_WITH_ACTION ? "" : " without external action",
        fts5yymsp[fts5yysize].stateno);
    }else{
      fprintf(fts5yyTraceFILE, "%sReduce %d [%s]%s.\n",
        fts5yyTracePrompt, fts5yyruleno, fts5yyRuleName[fts5yyruleno],
        fts5yyruleno<fts5YYNRULE_WITH_ACTION ? "" : " without external action");
    }
  }
#endif /* NDEBUG */

  /* Check that the stack is large enough to grow by a single entry
  ** if the RHS of the rule is empty.  This ensures that there is room
  ** enough on the stack to push the LHS value */
  if( fts5yyRuleInfoNRhs[fts5yyruleno]==0 ){
#ifdef fts5YYTRACKMAXSTACKDEPTH
    if( (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack)>fts5yypParser->fts5yyhwm ){
      fts5yypParser->fts5yyhwm++;
      assert( fts5yypParser->fts5yyhwm == (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack));
    }
#endif
#if fts5YYSTACKDEPTH>0
    if( fts5yypParser->fts5yytos>=fts5yypParser->fts5yystackEnd ){
      fts5yyStackOverflow(fts5yypParser);
      /* The call to fts5yyStackOverflow() above pops the stack until it is
      ** empty, causing the main parser loop to exit.  So the return value
      ** is never used and does not matter. */
      return 0;
    }
#else
    if( fts5yypParser->fts5yytos>=&fts5yypParser->fts5yystack[fts5yypParser->fts5yystksz-1] ){
      if( fts5yyGrowStack(fts5yypParser) ){
        fts5yyStackOverflow(fts5yypParser);
        /* The call to fts5yyStackOverflow() above pops the stack until it is
        ** empty, causing the main parser loop to exit.  So the return value
        ** is never used and does not matter. */
        return 0;
      }
      fts5yymsp = fts5yypParser->fts5yytos;
    }
#endif
  }

  switch( fts5yyruleno ){
  /* Beginning here are the reduction cases.  A typical example
  ** follows:
  **   case 0:
  **  #line <lineno> <grammarfile>
  **     { ... }           // User supplied code







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







212403
212404
212405
212406
212407
212408
212409

















































212410
212411
212412
212413
212414
212415
212416
  fts5YYACTIONTYPE fts5yyact;             /* The next action */
  fts5yyStackEntry *fts5yymsp;            /* The top of the parser's stack */
  int fts5yysize;                     /* Amount to pop the stack */
  sqlite3Fts5ParserARG_FETCH
  (void)fts5yyLookahead;
  (void)fts5yyLookaheadToken;
  fts5yymsp = fts5yypParser->fts5yytos;


















































  switch( fts5yyruleno ){
  /* Beginning here are the reduction cases.  A typical example
  ** follows:
  **   case 0:
  **  #line <lineno> <grammarfile>
  **     { ... }           // User supplied code
211550
211551
211552
211553
211554
211555
211556
211557

211558
211559
211560


















211561

























211562
211563
211564
211565
211566
211567
211568
211569
    }else{
      fprintf(fts5yyTraceFILE,"%sInput '%s' with pending reduce %d\n",
              fts5yyTracePrompt,fts5yyTokenName[fts5yymajor],fts5yyact-fts5YY_MIN_REDUCE);
    }
  }
#endif

  do{

    assert( fts5yyact==fts5yypParser->fts5yytos->stateno );
    fts5yyact = fts5yy_find_shift_action((fts5YYCODETYPE)fts5yymajor,fts5yyact);
    if( fts5yyact >= fts5YY_MIN_REDUCE ){


















      fts5yyact = fts5yy_reduce(fts5yypParser,fts5yyact-fts5YY_MIN_REDUCE,fts5yymajor,

























                        fts5yyminor sqlite3Fts5ParserCTX_PARAM);
    }else if( fts5yyact <= fts5YY_MAX_SHIFTREDUCE ){
      fts5yy_shift(fts5yypParser,fts5yyact,(fts5YYCODETYPE)fts5yymajor,fts5yyminor);
#ifndef fts5YYNOERRORRECOVERY
      fts5yypParser->fts5yyerrcnt--;
#endif
      break;
    }else if( fts5yyact==fts5YY_ACCEPT_ACTION ){







|
>



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







212705
212706
212707
212708
212709
212710
212711
212712
212713
212714
212715
212716
212717
212718
212719
212720
212721
212722
212723
212724
212725
212726
212727
212728
212729
212730
212731
212732
212733
212734
212735
212736
212737
212738
212739
212740
212741
212742
212743
212744
212745
212746
212747
212748
212749
212750
212751
212752
212753
212754
212755
212756
212757
212758
212759
212760
212761
212762
212763
212764
212765
212766
212767
212768
    }else{
      fprintf(fts5yyTraceFILE,"%sInput '%s' with pending reduce %d\n",
              fts5yyTracePrompt,fts5yyTokenName[fts5yymajor],fts5yyact-fts5YY_MIN_REDUCE);
    }
  }
#endif

  while(1){ /* Exit by "break" */
    assert( fts5yypParser->fts5yytos>=fts5yypParser->fts5yystack );
    assert( fts5yyact==fts5yypParser->fts5yytos->stateno );
    fts5yyact = fts5yy_find_shift_action((fts5YYCODETYPE)fts5yymajor,fts5yyact);
    if( fts5yyact >= fts5YY_MIN_REDUCE ){
      unsigned int fts5yyruleno = fts5yyact - fts5YY_MIN_REDUCE; /* Reduce by this rule */
      assert( fts5yyruleno<(int)(sizeof(fts5yyRuleName)/sizeof(fts5yyRuleName[0])) );
#ifndef NDEBUG
      if( fts5yyTraceFILE ){
        int fts5yysize = fts5yyRuleInfoNRhs[fts5yyruleno];
        if( fts5yysize ){
          fprintf(fts5yyTraceFILE, "%sReduce %d [%s]%s, pop back to state %d.\n",
            fts5yyTracePrompt,
            fts5yyruleno, fts5yyRuleName[fts5yyruleno],
            fts5yyruleno<fts5YYNRULE_WITH_ACTION ? "" : " without external action",
            fts5yypParser->fts5yytos[fts5yysize].stateno);
        }else{
          fprintf(fts5yyTraceFILE, "%sReduce %d [%s]%s.\n",
            fts5yyTracePrompt, fts5yyruleno, fts5yyRuleName[fts5yyruleno],
            fts5yyruleno<fts5YYNRULE_WITH_ACTION ? "" : " without external action");
        }
      }
#endif /* NDEBUG */

      /* Check that the stack is large enough to grow by a single entry
      ** if the RHS of the rule is empty.  This ensures that there is room
      ** enough on the stack to push the LHS value */
      if( fts5yyRuleInfoNRhs[fts5yyruleno]==0 ){
#ifdef fts5YYTRACKMAXSTACKDEPTH
        if( (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack)>fts5yypParser->fts5yyhwm ){
          fts5yypParser->fts5yyhwm++;
          assert( fts5yypParser->fts5yyhwm ==
                  (int)(fts5yypParser->fts5yytos - fts5yypParser->fts5yystack));
        }
#endif
#if fts5YYSTACKDEPTH>0
        if( fts5yypParser->fts5yytos>=fts5yypParser->fts5yystackEnd ){
          fts5yyStackOverflow(fts5yypParser);
          break;
        }
#else
        if( fts5yypParser->fts5yytos>=&fts5yypParser->fts5yystack[fts5yypParser->fts5yystksz-1] ){
          if( fts5yyGrowStack(fts5yypParser) ){
            fts5yyStackOverflow(fts5yypParser);
            break;
          }
        }
#endif
      }
      fts5yyact = fts5yy_reduce(fts5yypParser,fts5yyruleno,fts5yymajor,fts5yyminor sqlite3Fts5ParserCTX_PARAM);
    }else if( fts5yyact <= fts5YY_MAX_SHIFTREDUCE ){
      fts5yy_shift(fts5yypParser,fts5yyact,(fts5YYCODETYPE)fts5yymajor,fts5yyminor);
#ifndef fts5YYNOERRORRECOVERY
      fts5yypParser->fts5yyerrcnt--;
#endif
      break;
    }else if( fts5yyact==fts5YY_ACCEPT_ACTION ){
211668
211669
211670
211671
211672
211673
211674
211675

211676
211677
211678
211679
211680
211681
211682
#ifndef fts5YYNOERRORRECOVERY
        fts5yypParser->fts5yyerrcnt = -1;
#endif
      }
      break;
#endif
    }
  }while( fts5yypParser->fts5yytos>fts5yypParser->fts5yystack );

#ifndef NDEBUG
  if( fts5yyTraceFILE ){
    fts5yyStackEntry *i;
    char cDiv = '[';
    fprintf(fts5yyTraceFILE,"%sReturn. Stack=",fts5yyTracePrompt);
    for(i=&fts5yypParser->fts5yystack[1]; i<=fts5yypParser->fts5yytos; i++){
      fprintf(fts5yyTraceFILE,"%c%s", cDiv, fts5yyTokenName[i->major]);







<
>







212867
212868
212869
212870
212871
212872
212873

212874
212875
212876
212877
212878
212879
212880
212881
#ifndef fts5YYNOERRORRECOVERY
        fts5yypParser->fts5yyerrcnt = -1;
#endif
      }
      break;
#endif
    }

  }
#ifndef NDEBUG
  if( fts5yyTraceFILE ){
    fts5yyStackEntry *i;
    char cDiv = '[';
    fprintf(fts5yyTraceFILE,"%sReturn. Stack=",fts5yyTracePrompt);
    for(i=&fts5yypParser->fts5yystack[1]; i<=fts5yypParser->fts5yytos; i++){
      fprintf(fts5yyTraceFILE,"%c%s", cDiv, fts5yyTokenName[i->major]);
215280
215281
215282
215283
215284
215285
215286
215287
215288
215289
215290
215291
215292
215293
215294
215295
   && 0==pRoot->bEof
   && fts5RowidCmp(p, pRoot->iRowid, iFirst)<0
  ){
    rc = fts5ExprNodeNext(p, pRoot, 1, iFirst);
  }

  /* If the iterator is not at a real match, skip forward until it is. */
  while( pRoot->bNomatch ){
    assert( pRoot->bEof==0 && rc==SQLITE_OK );
    rc = fts5ExprNodeNext(p, pRoot, 0, 0);
  }
  return rc;
}

/*
** Move to the next document







|
|







216479
216480
216481
216482
216483
216484
216485
216486
216487
216488
216489
216490
216491
216492
216493
216494
   && 0==pRoot->bEof
   && fts5RowidCmp(p, pRoot->iRowid, iFirst)<0
  ){
    rc = fts5ExprNodeNext(p, pRoot, 1, iFirst);
  }

  /* If the iterator is not at a real match, skip forward until it is. */
  while( pRoot->bNomatch && rc==SQLITE_OK ){
    assert( pRoot->bEof==0 );
    rc = fts5ExprNodeNext(p, pRoot, 0, 0);
  }
  return rc;
}

/*
** Move to the next document
219466
219467
219468
219469
219470
219471
219472
219473
219474
219475
219476
219477
219478
219479
219480
219481
219482
219483
219484
219485
219486
219487
        fts5SegIterLoadTerm(p, pIter, nKeep);
        fts5SegIterLoadNPos(p, pIter);
        if( pbNewTerm ) *pbNewTerm = 1;
      }
    }else{
      /* The following could be done by calling fts5SegIterLoadNPos(). But
      ** this block is particularly performance critical, so equivalent
      ** code is inlined.
      **
      ** Later: Switched back to fts5SegIterLoadNPos() because it supports
      ** detail=none mode. Not ideal.
      */
      int nSz;
      assert( p->rc==SQLITE_OK );
      assert( pIter->iLeafOffset<=pIter->pLeaf->nn );
      fts5FastGetVarint32(pIter->pLeaf->p, pIter->iLeafOffset, nSz);
      pIter->bDel = (nSz & 0x0001);
      pIter->nPos = nSz>>1;
      assert_nc( pIter->nPos>=0 );
    }
  }
}







|
<
<
<
<


|







220665
220666
220667
220668
220669
220670
220671
220672




220673
220674
220675
220676
220677
220678
220679
220680
220681
220682
        fts5SegIterLoadTerm(p, pIter, nKeep);
        fts5SegIterLoadNPos(p, pIter);
        if( pbNewTerm ) *pbNewTerm = 1;
      }
    }else{
      /* The following could be done by calling fts5SegIterLoadNPos(). But
      ** this block is particularly performance critical, so equivalent
      ** code is inlined.  */




      int nSz;
      assert( p->rc==SQLITE_OK );
      assert_nc( pIter->iLeafOffset<=pIter->pLeaf->nn );
      fts5FastGetVarint32(pIter->pLeaf->p, pIter->iLeafOffset, nSz);
      pIter->bDel = (nSz & 0x0001);
      pIter->nPos = nSz>>1;
      assert_nc( pIter->nPos>=0 );
    }
  }
}
220465
220466
220467
220468
220469
220470
220471
220472
220473
220474
220475
220476
220477
220478
220479
220480
220481
220482
220483
220484



220485
220486
220487
220488
220489
220490
220491
  int nRem = pSeg->nPos;          /* Number of bytes still to come */
  Fts5Data *pData = 0;
  u8 *pChunk = &pSeg->pLeaf->p[pSeg->iLeafOffset];
  int nChunk = MIN(nRem, pSeg->pLeaf->szLeaf - pSeg->iLeafOffset);
  int pgno = pSeg->iLeafPgno;
  int pgnoSave = 0;

  /* This function does notmwork with detail=none databases. */
  assert( p->pConfig->eDetail!=FTS5_DETAIL_NONE );

  if( (pSeg->flags & FTS5_SEGITER_REVERSE)==0 ){
    pgnoSave = pgno+1;
  }

  while( 1 ){
    xChunk(p, pCtx, pChunk, nChunk);
    nRem -= nChunk;
    fts5DataRelease(pData);
    if( nRem<=0 ){
      break;



    }else{
      pgno++;
      pData = fts5LeafRead(p, FTS5_SEGMENT_ROWID(pSeg->pSeg->iSegid, pgno));
      if( pData==0 ) break;
      pChunk = &pData->p[4];
      nChunk = MIN(nRem, pData->szLeaf - 4);
      if( pgno==pgnoSave ){







|












>
>
>







221660
221661
221662
221663
221664
221665
221666
221667
221668
221669
221670
221671
221672
221673
221674
221675
221676
221677
221678
221679
221680
221681
221682
221683
221684
221685
221686
221687
221688
221689
  int nRem = pSeg->nPos;          /* Number of bytes still to come */
  Fts5Data *pData = 0;
  u8 *pChunk = &pSeg->pLeaf->p[pSeg->iLeafOffset];
  int nChunk = MIN(nRem, pSeg->pLeaf->szLeaf - pSeg->iLeafOffset);
  int pgno = pSeg->iLeafPgno;
  int pgnoSave = 0;

  /* This function does not work with detail=none databases. */
  assert( p->pConfig->eDetail!=FTS5_DETAIL_NONE );

  if( (pSeg->flags & FTS5_SEGITER_REVERSE)==0 ){
    pgnoSave = pgno+1;
  }

  while( 1 ){
    xChunk(p, pCtx, pChunk, nChunk);
    nRem -= nChunk;
    fts5DataRelease(pData);
    if( nRem<=0 ){
      break;
    }else if( pSeg->pSeg==0 ){
      p->rc = FTS5_CORRUPT;
      return;
    }else{
      pgno++;
      pData = fts5LeafRead(p, FTS5_SEGMENT_ROWID(pSeg->pSeg->iSegid, pgno));
      if( pData==0 ) break;
      pChunk = &pData->p[4];
      nChunk = MIN(nRem, pData->szLeaf - 4);
      if( pgno==pgnoSave ){
220529
220530
220531
220532
220533
220534
220535
220536
220537
220538
220539
220540
220541
220542
220543
220544
220545
220546
220547
220548
220549
220550
220551
220552
220553
220554
220555
220556
220557
220558
220559
220560
220561
220562
220563
220564
220565
220566
220567
220568
220569
220570
220571
220572
220573
220574
220575
220576
220577
220578
220579
220580
220581
220582
220583
220584
220585



220586

220587







220588





220589






220590
220591




220592
220593




220594





220595



220596
220597
220598
220599
220600
220601
220602
        fts5ChunkIterate(p, pSeg, (void*)&sCtx, fts5PoslistOffsetsCallback);
      }
    }
  }
}

/*
** IN/OUT parameter (*pa) points to a position list n bytes in size. If
** the position list contains entries for column iCol, then (*pa) is set
** to point to the sub-position-list for that column and the number of
** bytes in it returned. Or, if the argument position list does not
** contain any entries for column iCol, return 0.
*/
static int fts5IndexExtractCol(
  const u8 **pa,                  /* IN/OUT: Pointer to poslist */
  int n,                          /* IN: Size of poslist in bytes */
  int iCol                        /* Column to extract from poslist */
){
  int iCurrent = 0;               /* Anything before the first 0x01 is col 0 */
  const u8 *p = *pa;
  const u8 *pEnd = &p[n];         /* One byte past end of position list */

  while( iCol>iCurrent ){
    /* Advance pointer p until it points to pEnd or an 0x01 byte that is
    ** not part of a varint. Note that it is not possible for a negative
    ** or extremely large varint to occur within an uncorrupted position
    ** list. So the last byte of each varint may be assumed to have a clear
    ** 0x80 bit.  */
    while( *p!=0x01 ){
      while( *p++ & 0x80 );
      if( p>=pEnd ) return 0;
    }
    *pa = p++;
    iCurrent = *p++;
    if( iCurrent & 0x80 ){
      p--;
      p += fts5GetVarint32(p, iCurrent);
    }
  }
  if( iCol!=iCurrent ) return 0;

  /* Advance pointer p until it points to pEnd or an 0x01 byte that is
  ** not part of a varint */
  while( p<pEnd && *p!=0x01 ){
    while( *p++ & 0x80 );
  }

  return p - (*pa);
}

static void fts5IndexExtractColset(
  int *pRc,
  Fts5Colset *pColset,            /* Colset to filter on */
  const u8 *pPos, int nPos,       /* Position list */
  Fts5Buffer *pBuf                /* Output buffer */
){
  if( *pRc==SQLITE_OK ){



    int i;

    fts5BufferZero(pBuf);







    for(i=0; i<pColset->nCol; i++){





      const u8 *pSub = pPos;






      int nSub = fts5IndexExtractCol(&pSub, nPos, pColset->aiCol[i]);
      if( nSub ){




        fts5BufferAppendBlob(pRc, pBuf, nSub, pSub);
      }




    }





  }



}

/*
** xSetOutputs callback used by detail=none tables.
*/
static void fts5IterSetOutputs_None(Fts5Iter *pIter, Fts5SegIter *pSeg){
  assert( pIter->pIndex->pConfig->eDetail==FTS5_DETAIL_NONE );







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




|


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

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







221727
221728
221729
221730
221731
221732
221733
221734
221735
221736
221737









221738
221739





221740












221741





221742
221743

221744
221745
221746
221747
221748
221749
221750
221751
221752
221753
221754
221755
221756
221757
221758
221759
221760
221761
221762
221763
221764
221765
221766
221767
221768
221769
221770
221771
221772
221773
221774
221775
221776
221777
221778
221779
221780
221781
221782
221783
221784
221785
221786
221787
221788
221789
221790
221791
221792
221793
221794
221795
221796
221797
221798
221799
221800
221801
221802
221803
221804
221805
221806
        fts5ChunkIterate(p, pSeg, (void*)&sCtx, fts5PoslistOffsetsCallback);
      }
    }
  }
}

/*
** Parameter pPos points to a buffer containing a position list, size nPos.
** This function filters it according to pColset (which must be non-NULL)
** and sets pIter->base.pData/nData to point to the new position list.
** If memory is required for the new position list, use buffer pIter->poslist.









** Or, if the new position list is a contiguous subset of the input, set
** pIter->base.pData/nData to point directly to it.





**












** This function is a no-op if *pRc is other than SQLITE_OK when it is





** called. If an OOM error is encountered, *pRc is set to SQLITE_NOMEM
** before returning.

*/
static void fts5IndexExtractColset(
  int *pRc,
  Fts5Colset *pColset,            /* Colset to filter on */
  const u8 *pPos, int nPos,       /* Position list */
  Fts5Iter *pIter
){
  if( *pRc==SQLITE_OK ){
    const u8 *p = pPos;
    const u8 *aCopy = p;
    const u8 *pEnd = &p[nPos];    /* One byte past end of position list */
    int i = 0;
    int iCurrent = 0;

    if( pColset->nCol>1 && sqlite3Fts5BufferSize(pRc, &pIter->poslist, nPos) ){
      return;
    }

    while( 1 ){
      while( pColset->aiCol[i]<iCurrent ){
        i++;
        if( i==pColset->nCol ){
          pIter->base.pData = pIter->poslist.p;
          pIter->base.nData = pIter->poslist.n;
          return;
        }
      }

      /* Advance pointer p until it points to pEnd or an 0x01 byte that is
      ** not part of a varint */
      while( p<pEnd && *p!=0x01 ){
        while( *p++ & 0x80 );
      }

      if( pColset->aiCol[i]==iCurrent ){
        if( pColset->nCol==1 ){
          pIter->base.pData = aCopy;
          pIter->base.nData = p-aCopy;
          return;
        }
        fts5BufferSafeAppendBlob(&pIter->poslist, aCopy, p-aCopy);
      }
      if( p==pEnd ){
        pIter->base.pData = pIter->poslist.p;
        pIter->base.nData = pIter->poslist.n;
        return;
      }
      aCopy = p++;
      iCurrent = *p++;
      if( iCurrent & 0x80 ){
        p--;
        p += fts5GetVarint32(p, iCurrent);
      }
    }
  }

}

/*
** xSetOutputs callback used by detail=none tables.
*/
static void fts5IterSetOutputs_None(Fts5Iter *pIter, Fts5SegIter *pSeg){
  assert( pIter->pIndex->pConfig->eDetail==FTS5_DETAIL_NONE );
220708
220709
220710
220711
220712
220713
220714
220715
220716
220717
220718
220719
220720
220721
220722
220723
220724
220725
220726
220727
220728
220729
220730
220731
  assert( pIter->pIndex->pConfig->eDetail==FTS5_DETAIL_FULL );
  assert( pColset );

  if( pSeg->iLeafOffset+pSeg->nPos<=pSeg->pLeaf->szLeaf ){
    /* All data is stored on the current page. Populate the output
    ** variables to point into the body of the page object. */
    const u8 *a = &pSeg->pLeaf->p[pSeg->iLeafOffset];
    if( pColset->nCol==1 ){
      pIter->base.nData = fts5IndexExtractCol(&a, pSeg->nPos,pColset->aiCol[0]);
      pIter->base.pData = a;
    }else{
      int *pRc = &pIter->pIndex->rc;
      fts5BufferZero(&pIter->poslist);
      fts5IndexExtractColset(pRc, pColset, a, pSeg->nPos, &pIter->poslist);
      pIter->base.pData = pIter->poslist.p;
      pIter->base.nData = pIter->poslist.n;
    }
  }else{
    /* The data is distributed over two or more pages. Copy it into the
    ** Fts5Iter.poslist buffer and then set the output pointer to point
    ** to this buffer.  */
    fts5BufferZero(&pIter->poslist);
    fts5SegiterPoslist(pIter->pIndex, pSeg, pColset, &pIter->poslist);
    pIter->base.pData = pIter->poslist.p;







<
<
<
<
|
|
|
<
<
<







221912
221913
221914
221915
221916
221917
221918




221919
221920
221921



221922
221923
221924
221925
221926
221927
221928
  assert( pIter->pIndex->pConfig->eDetail==FTS5_DETAIL_FULL );
  assert( pColset );

  if( pSeg->iLeafOffset+pSeg->nPos<=pSeg->pLeaf->szLeaf ){
    /* All data is stored on the current page. Populate the output
    ** variables to point into the body of the page object. */
    const u8 *a = &pSeg->pLeaf->p[pSeg->iLeafOffset];




    int *pRc = &pIter->pIndex->rc;
    fts5BufferZero(&pIter->poslist);
    fts5IndexExtractColset(pRc, pColset, a, pSeg->nPos, pIter);



  }else{
    /* The data is distributed over two or more pages. Copy it into the
    ** Fts5Iter.poslist buffer and then set the output pointer to point
    ** to this buffer.  */
    fts5BufferZero(&pIter->poslist);
    fts5SegiterPoslist(pIter->pIndex, pSeg, pColset, &pIter->poslist);
    pIter->base.pData = pIter->poslist.p;
222200
222201
222202
222203
222204
222205
222206
222207
222208
222209
222210
222211
222212
222213
222214
  }
}


static void fts5DoclistIterNext(Fts5DoclistIter *pIter){
  u8 *p = pIter->aPoslist + pIter->nSize + pIter->nPoslist;

  assert( pIter->aPoslist );
  if( p>=pIter->aEof ){
    pIter->aPoslist = 0;
  }else{
    i64 iDelta;

    p += fts5GetVarint(p, (u64*)&iDelta);
    pIter->iRowid += iDelta;







|







223397
223398
223399
223400
223401
223402
223403
223404
223405
223406
223407
223408
223409
223410
223411
  }
}


static void fts5DoclistIterNext(Fts5DoclistIter *pIter){
  u8 *p = pIter->aPoslist + pIter->nSize + pIter->nPoslist;

  assert( pIter->aPoslist || (p==0 && pIter->aPoslist==0) );
  if( p>=pIter->aEof ){
    pIter->aPoslist = 0;
  }else{
    i64 iDelta;

    p += fts5GetVarint(p, (u64*)&iDelta);
    pIter->iRowid += iDelta;
222284
222285
222286
222287
222288
222289
222290

222291
222292
222293
222294
222295
222296
222297
222298
222299


222300

222301
222302
222303
222304
222305
222306
222307
/*
** This is the equivalent of fts5MergePrefixLists() for detail=none mode.
** In this case the buffers consist of a delta-encoded list of rowids only.
*/
static void fts5MergeRowidLists(
  Fts5Index *p,                   /* FTS5 backend object */
  Fts5Buffer *p1,                 /* First list to merge */

  Fts5Buffer *p2                  /* Second list to merge */
){
  int i1 = 0;
  int i2 = 0;
  i64 iRowid1 = 0;
  i64 iRowid2 = 0;
  i64 iOut = 0;

  Fts5Buffer out;


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

  sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n);
  if( p->rc ) return;

  fts5NextRowid(p1, &i1, &iRowid1);
  fts5NextRowid(p2, &i2, &iRowid2);
  while( i1>=0 || i2>=0 ){
    if( i1>=0 && (i2<0 || iRowid1<iRowid2) ){







>
|






|

>
>

>







223481
223482
223483
223484
223485
223486
223487
223488
223489
223490
223491
223492
223493
223494
223495
223496
223497
223498
223499
223500
223501
223502
223503
223504
223505
223506
223507
223508
/*
** This is the equivalent of fts5MergePrefixLists() for detail=none mode.
** In this case the buffers consist of a delta-encoded list of rowids only.
*/
static void fts5MergeRowidLists(
  Fts5Index *p,                   /* FTS5 backend object */
  Fts5Buffer *p1,                 /* First list to merge */
  int nBuf,                       /* Number of entries in apBuf[] */
  Fts5Buffer *aBuf                /* Array of other lists to merge into p1 */
){
  int i1 = 0;
  int i2 = 0;
  i64 iRowid1 = 0;
  i64 iRowid2 = 0;
  i64 iOut = 0;
  Fts5Buffer *p2 = &aBuf[0];
  Fts5Buffer out;

  (void)nBuf;
  memset(&out, 0, sizeof(out));
  assert( nBuf==1 );
  sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n);
  if( p->rc ) return;

  fts5NextRowid(p1, &i1, &iRowid1);
  fts5NextRowid(p2, &i2, &iRowid2);
  while( i1>=0 || i2>=0 ){
    if( i1>=0 && (i2<0 || iRowid1<iRowid2) ){
222319
222320
222321
222322
222323
222324
222325
222326





222327


222328













222329


222330







222331


222332
222333



222334
222335
222336
222337

222338
222339
222340




222341
222342
222343

222344
222345

222346















222347
222348
222349
222350
222351
222352
222353
222354
222355
222356
222357
222358
222359
222360
222361
222362
222363
222364
222365
222366
222367
222368
222369
222370
222371
222372
222373
222374
222375
222376
222377
222378
222379
222380

222381

222382
222383
222384
222385




222386

222387




222388


222389
222390
222391
222392
222393
222394
222395
222396
222397
222398
222399
222400
222401
222402
222403
222404
222405
222406
222407

222408
222409
222410
222411
222412
222413
222414

222415
222416
222417
222418

222419
222420
222421
222422
222423
222424

222425
222426
222427
222428
222429
222430
222431
222432
222433
222434
222435
222436
222437
222438
222439

222440
222441

222442
222443

222444


222445
222446
222447
222448
222449
222450
222451
222452
222453
222454
222455
222456
222457
222458
222459
222460
222461
222462
222463
222464
222465

222466
222467

222468



222469
222470
222471
222472
222473
222474
222475
222476
222477
222478
222479
222480
222481
222482
222483
222484
222485

222486
222487
222488
222489
222490
222491
222492
222493

222494
222495
222496
222497
222498
222499
222500


222501
222502
222503
222504
222505
222506
222507
222508
222509
222510
222511
222512
222513
222514
222515
222516
222517
222518
222519





















222520
222521
222522
222523
222524
222525
222526
222527
222528
222529
222530
222531
222532
222533
222534
222535
222536
222537
222538
222539


222540

222541
222542
222543
222544



222545

222546

222547
222548
222549
222550
222551
222552
222553
222554
222555

222556

222557
222558
222559

222560

222561
222562
222563
222564
222565
222566
222567
      fts5NextRowid(p2, &i2, &iRowid2);
    }
  }

  fts5BufferSwap(&out, p1);
  fts5BufferFree(&out);
}






/*


** Buffers p1 and p2 contain doclists. This function merges the content













** of the two doclists together and sets buffer p1 to the result before


** returning.







**


** If an error occurs, an error code is left in p->rc. If an error has
** already occurred, this function is a no-op.



*/
static void fts5MergePrefixLists(
  Fts5Index *p,                   /* FTS5 backend object */
  Fts5Buffer *p1,                 /* First list to merge */

  Fts5Buffer *p2                  /* Second list to merge */
){
  if( p2->n ){




    i64 iLastRowid = 0;
    Fts5DoclistIter i1;
    Fts5DoclistIter i2;

    Fts5Buffer out = {0, 0, 0};
    Fts5Buffer tmp = {0, 0, 0};

















    /* The maximum size of the output is equal to the sum of the two
    ** input sizes + 1 varint (9 bytes). The extra varint is because if the
    ** first rowid in one input is a large negative number, and the first in
    ** the other a non-negative number, the delta for the non-negative
    ** number will be larger on disk than the literal integer value
    ** was.
    **
    ** Or, if the input position-lists are corrupt, then the output might
    ** include up to 2 extra 10-byte positions created by interpreting -1
    ** (the value PoslistNext64() uses for EOF) as a position and appending
    ** it to the output. This can happen at most once for each input
    ** position-list, hence two 10 byte paddings.  */
    if( sqlite3Fts5BufferSize(&p->rc, &out, p1->n + p2->n + 9+10+10) ) return;
    fts5DoclistIterInit(p1, &i1);
    fts5DoclistIterInit(p2, &i2);

    while( 1 ){
      if( i1.iRowid<i2.iRowid ){
        /* Copy entry from i1 */
        fts5MergeAppendDocid(&out, iLastRowid, i1.iRowid);
        fts5BufferSafeAppendBlob(&out, i1.aPoslist, i1.nPoslist+i1.nSize);
        fts5DoclistIterNext(&i1);
        if( i1.aPoslist==0 ) break;
        assert( out.n<=((i1.aPoslist-p1->p) + (i2.aPoslist-p2->p)+9+10+10) );
      }
      else if( i2.iRowid!=i1.iRowid ){
        /* Copy entry from i2 */
        fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
        fts5BufferSafeAppendBlob(&out, i2.aPoslist, i2.nPoslist+i2.nSize);
        fts5DoclistIterNext(&i2);
        if( i2.aPoslist==0 ) break;
        assert( out.n<=((i1.aPoslist-p1->p) + (i2.aPoslist-p2->p)+9+10+10) );
      }
      else{

        /* Merge the two position lists. */

        i64 iPos1 = 0;
        i64 iPos2 = 0;
        int iOff1 = 0;
        int iOff2 = 0;




        u8 *a1 = &i1.aPoslist[i1.nSize];

        u8 *a2 = &i2.aPoslist[i2.nSize];




        int nCopy;


        u8 *aCopy;

        i64 iPrev = 0;
        Fts5PoslistWriter writer;
        memset(&writer, 0, sizeof(writer));

        /* See the earlier comment in this function for an explanation of why
        ** corrupt input position lists might cause the output to consume
        ** at most 20 bytes of unexpected space. */
        fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
        fts5BufferZero(&tmp);
        sqlite3Fts5BufferSize(&p->rc, &tmp,
            i1.nPoslist + i2.nPoslist + 10 + 10 + FTS5_DATA_ZERO_PADDING
        );
        if( p->rc ) break;

        sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1);
        sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2);
        assert_nc( iPos1>=0 && iPos2>=0 );


        if( iPos1<iPos2 ){
          sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos1);
          sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1);
        }else{
          sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2);
          sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2);

        }
        if( iPos1>=0 && iPos2>=0 ){
          while( 1 ){
            if( iPos1<iPos2 ){

              if( iPos1!=iPrev ){
                sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos1);
              }
              sqlite3Fts5PoslistNext64(a1, i1.nPoslist, &iOff1, &iPos1);
              if( iPos1<0 ) break;
            }else{

              assert_nc( iPos2!=iPrev );
              sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2);
              sqlite3Fts5PoslistNext64(a2, i2.nPoslist, &iOff2, &iPos2);
              if( iPos2<0 ) break;
            }
          }
        }

        if( iPos1>=0 ){
          if( iPos1!=iPrev ){
            sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos1);
          }
          aCopy = &a1[iOff1];
          nCopy = i1.nPoslist - iOff1;
        }else{

          assert_nc( iPos2>=0 && iPos2!=iPrev );
          sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, iPos2);

          aCopy = &a2[iOff2];
          nCopy = i2.nPoslist - iOff2;

        }


        if( nCopy>0 ){
          fts5BufferSafeAppendBlob(&tmp, aCopy, nCopy);
        }

        /* WRITEPOSLISTSIZE */
        assert_nc( tmp.n<=i1.nPoslist+i2.nPoslist );
        assert( tmp.n<=i1.nPoslist+i2.nPoslist+10+10 );
        if( tmp.n>i1.nPoslist+i2.nPoslist ){
          if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT;
          break;
        }
        fts5BufferSafeAppendVarint(&out, tmp.n * 2);
        fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n);
        fts5DoclistIterNext(&i1);
        fts5DoclistIterNext(&i2);
        assert_nc( out.n<=(p1->n+p2->n+9) );
        if( i1.aPoslist==0 || i2.aPoslist==0 ) break;
        assert( out.n<=((i1.aPoslist-p1->p) + (i2.aPoslist-p2->p)+9+10+10) );
      }
    }


    if( i1.aPoslist ){
      fts5MergeAppendDocid(&out, iLastRowid, i1.iRowid);

      fts5BufferSafeAppendBlob(&out, i1.aPoslist, i1.aEof - i1.aPoslist);



    }
    else if( i2.aPoslist ){
      fts5MergeAppendDocid(&out, iLastRowid, i2.iRowid);
      fts5BufferSafeAppendBlob(&out, i2.aPoslist, i2.aEof - i2.aPoslist);
    }
    assert_nc( out.n<=(p1->n+p2->n+9) );

    fts5BufferFree(p1);
    fts5BufferFree(&tmp);
    memset(&out.p[out.n], 0, FTS5_DATA_ZERO_PADDING);
    *p1 = out;
  }
}

static void fts5SetupPrefixIter(
  Fts5Index *p,                   /* Index to read from */
  int bDesc,                      /* True for "ORDER BY rowid DESC" */

  const u8 *pToken,               /* Buffer containing prefix to match */
  int nToken,                     /* Size of buffer pToken in bytes */
  Fts5Colset *pColset,            /* Restrict matches to these columns */
  Fts5Iter **ppIter          /* OUT: New iterator */
){
  Fts5Structure *pStruct;
  Fts5Buffer *aBuf;
  const int nBuf = 32;


  void (*xMerge)(Fts5Index*, Fts5Buffer*, Fts5Buffer*);
  void (*xAppend)(Fts5Index*, i64, Fts5Iter*, Fts5Buffer*);
  if( p->pConfig->eDetail==FTS5_DETAIL_NONE ){
    xMerge = fts5MergeRowidLists;
    xAppend = fts5AppendRowid;
  }else{


    xMerge = fts5MergePrefixLists;
    xAppend = fts5AppendPoslist;
  }

  aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf);
  pStruct = fts5StructureRead(p);

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

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





















    fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1);
    fts5IterSetOutputCb(&p->rc, p1);
    for( /* no-op */ ;
        fts5MultiIterEof(p, p1)==0;
        fts5MultiIterNext2(p, p1, &bNewTerm)
    ){
      Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ];
      int nTerm = pSeg->term.n;
      const u8 *pTerm = pSeg->term.p;
      p1->xSetOutputs(p1, pSeg);

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

      if( p1->base.nData==0 ) continue;

      if( p1->base.iRowid<=iLastRowid && doclist.n>0 ){
        for(i=0; p->rc==SQLITE_OK && doclist.n; i++){


          assert( i<nBuf );

          if( aBuf[i].n==0 ){
            fts5BufferSwap(&doclist, &aBuf[i]);
            fts5BufferZero(&doclist);
          }else{



            xMerge(p, &doclist, &aBuf[i]);

            fts5BufferZero(&aBuf[i]);

          }
        }
        iLastRowid = 0;
      }

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


    for(i=0; i<nBuf; i++){

      if( p->rc==SQLITE_OK ){
        xMerge(p, &doclist, &aBuf[i]);
      }

      fts5BufferFree(&aBuf[i]);

    }
    fts5MultiIterFree(p1);

    pData = fts5IdxMalloc(p, sizeof(Fts5Data)+doclist.n+FTS5_DATA_ZERO_PADDING);
    if( pData ){
      pData->p = (u8*)&pData[1];
      pData->nn = pData->szLeaf = doclist.n;








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




>
|

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

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

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

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

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

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

>
|
|
>
|
>
>
>

<
<
<
|
<

|
|
|
|
<





>
|






|
>

|





>
>



















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




















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









>
|
>

|

>
|
>







223520
223521
223522
223523
223524
223525
223526
223527
223528
223529
223530
223531
223532
223533
223534
223535
223536
223537
223538
223539
223540
223541
223542
223543
223544
223545
223546
223547
223548
223549
223550
223551
223552
223553
223554
223555
223556
223557
223558
223559
223560
223561
223562
223563
223564
223565
223566
223567
223568
223569
223570
223571
223572
223573
223574
223575

223576
223577
223578
223579
223580
223581

223582
223583
223584
223585
223586
223587
223588
223589
223590
223591
223592
223593
223594
223595
223596
223597
223598
223599
223600
223601
223602
223603
223604
223605
223606
223607
223608
223609
223610
223611
223612
223613
223614


223615
223616


223617




223618
223619




223620


223621
223622
223623
223624
223625
223626
223627
223628
223629
223630
223631
223632
223633
223634
223635
223636
223637
223638
223639
223640
223641
223642
223643
223644



223645
223646
223647
223648


223649


223650
223651



223652
223653


223654
223655
223656
223657
223658
223659

223660

223661
223662
223663
223664

223665

223666
223667



223668
223669



223670
223671
223672

223673
223674
223675
223676

223677
223678

223679
223680
223681
223682
223683
223684
223685
223686
223687
223688
223689
223690





223691
223692



223693
223694
223695
223696
223697
223698
223699
223700
223701
223702
223703
223704



223705

223706
223707
223708
223709
223710

223711
223712
223713
223714
223715
223716
223717
223718
223719
223720
223721
223722
223723
223724
223725
223726
223727
223728
223729
223730
223731
223732
223733
223734
223735
223736
223737
223738
223739
223740
223741
223742
223743
223744
223745
223746
223747
223748
223749
223750
223751
223752
223753
223754
223755
223756
223757
223758
223759
223760
223761
223762
223763
223764
223765
223766
223767
223768
223769
223770
223771
223772
223773
223774
223775
223776
223777
223778
223779
223780
223781
223782
223783
223784
223785
223786
223787
223788
223789
223790
223791
223792
223793
223794
223795
223796
223797
223798
223799
223800
223801
223802
223803
223804
223805
223806
223807
223808
223809
223810
223811
223812
223813
223814
223815
223816
223817
223818
223819
223820
223821
223822
223823
223824
223825
223826
223827
223828
223829
223830
223831
223832
223833
223834
      fts5NextRowid(p2, &i2, &iRowid2);
    }
  }

  fts5BufferSwap(&out, p1);
  fts5BufferFree(&out);
}

typedef struct PrefixMerger PrefixMerger;
struct PrefixMerger {
  Fts5DoclistIter iter;           /* Doclist iterator */
  i64 iPos;                       /* For iterating through a position list */
  int iOff;
  u8 *aPos;
  PrefixMerger *pNext;            /* Next in docid/poslist order */
};

static void fts5PrefixMergerInsertByRowid(
  PrefixMerger **ppHead,
  PrefixMerger *p
){
  if( p->iter.aPoslist ){
    PrefixMerger **pp = ppHead;
    while( *pp && p->iter.iRowid>(*pp)->iter.iRowid ){
      pp = &(*pp)->pNext;
    }
    p->pNext = *pp;
    *pp = p;
  }
}

static void fts5PrefixMergerInsertByPosition(
  PrefixMerger **ppHead,
  PrefixMerger *p
){
  if( p->iPos>=0 ){
    PrefixMerger **pp = ppHead;
    while( *pp && p->iPos>(*pp)->iPos ){
      pp = &(*pp)->pNext;
    }
    p->pNext = *pp;
    *pp = p;
  }
}


/*
** Array aBuf[] contains nBuf doclists. These are all merged in with the
** doclist in buffer p1.
*/
static void fts5MergePrefixLists(
  Fts5Index *p,                   /* FTS5 backend object */
  Fts5Buffer *p1,                 /* First list to merge */
  int nBuf,                       /* Number of buffers in array aBuf[] */
  Fts5Buffer *aBuf                /* Other lists to merge in */
){

#define fts5PrefixMergerNextPosition(p) \
  sqlite3Fts5PoslistNext64((p)->aPos,(p)->iter.nPoslist,&(p)->iOff,&(p)->iPos);
#define FTS5_MERGE_NLIST 16
  PrefixMerger aMerger[FTS5_MERGE_NLIST];
  PrefixMerger *pHead = 0;
  int i;

  int nOut = 0;
  Fts5Buffer out = {0, 0, 0};
  Fts5Buffer tmp = {0, 0, 0};
  i64 iLastRowid = 0;

  /* Initialize a doclist-iterator for each input buffer. Arrange them in
  ** a linked-list starting at pHead in ascending order of rowid. Avoid
  ** linking any iterators already at EOF into the linked list at all. */
  assert( nBuf+1<=sizeof(aMerger)/sizeof(aMerger[0]) );
  memset(aMerger, 0, sizeof(PrefixMerger)*(nBuf+1));
  pHead = &aMerger[nBuf];
  fts5DoclistIterInit(p1, &pHead->iter);
  for(i=0; i<nBuf; i++){
    fts5DoclistIterInit(&aBuf[i], &aMerger[i].iter);
    fts5PrefixMergerInsertByRowid(&pHead, &aMerger[i]);
    nOut += aBuf[i].n;
  }
  if( nOut==0 ) return;
  nOut += p1->n + 9 + 10*nBuf;

  /* The maximum size of the output is equal to the sum of the
  ** input sizes + 1 varint (9 bytes). The extra varint is because if the
  ** first rowid in one input is a large negative number, and the first in
  ** the other a non-negative number, the delta for the non-negative
  ** number will be larger on disk than the literal integer value
  ** was.
  **
  ** Or, if the input position-lists are corrupt, then the output might
  ** include up to (nBuf+1) extra 10-byte positions created by interpreting -1
  ** (the value PoslistNext64() uses for EOF) as a position and appending
  ** it to the output. This can happen at most once for each input
  ** position-list, hence (nBuf+1) 10 byte paddings.  */
  if( sqlite3Fts5BufferSize(&p->rc, &out, nOut) ) return;



  while( pHead ){


    fts5MergeAppendDocid(&out, iLastRowid, pHead->iter.iRowid);





    if( pHead->pNext && iLastRowid==pHead->pNext->iter.iRowid ){




      /* Merge data from two or more poslists */


      i64 iPrev = 0;
      int nTmp = FTS5_DATA_ZERO_PADDING;
      int nMerge = 0;
      PrefixMerger *pSave = pHead;
      PrefixMerger *pThis = 0;
      int nTail = 0;

      pHead = 0;
      while( pSave && pSave->iter.iRowid==iLastRowid ){
        PrefixMerger *pNext = pSave->pNext;
        pSave->iOff = 0;
        pSave->iPos = 0;
        pSave->aPos = &pSave->iter.aPoslist[pSave->iter.nSize];
        fts5PrefixMergerNextPosition(pSave);
        nTmp += pSave->iter.nPoslist + 10;
        nMerge++;
        fts5PrefixMergerInsertByPosition(&pHead, pSave);
        pSave = pNext;
      }

      if( pHead==0 || pHead->pNext==0 ){
        p->rc = FTS5_CORRUPT;
        break;
      }




      /* See the earlier comment in this function for an explanation of why
      ** corrupt input position lists might cause the output to consume
      ** at most nMerge*10 bytes of unexpected space. */


      if( sqlite3Fts5BufferSize(&p->rc, &tmp, nTmp+nMerge*10) ){


        break;
      }



      fts5BufferZero(&tmp);



      pThis = pHead;
      pHead = pThis->pNext;
      sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pThis->iPos);
      fts5PrefixMergerNextPosition(pThis);
      fts5PrefixMergerInsertByPosition(&pHead, pThis);


      while( pHead->pNext ){

        pThis = pHead;
        if( pThis->iPos!=iPrev ){
          sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pThis->iPos);
        }

        fts5PrefixMergerNextPosition(pThis);

        pHead = pThis->pNext;
        fts5PrefixMergerInsertByPosition(&pHead, pThis);



      }




      if( pHead->iPos!=iPrev ){
        sqlite3Fts5PoslistSafeAppend(&tmp, &iPrev, pHead->iPos);
      }

      nTail = pHead->iter.nPoslist - pHead->iOff;

      /* WRITEPOSLISTSIZE */
      assert( tmp.n+nTail<=nTmp );

      if( tmp.n+nTail>nTmp-FTS5_DATA_ZERO_PADDING ){
        if( p->rc==SQLITE_OK ) p->rc = FTS5_CORRUPT;

        break;
      }
      fts5BufferSafeAppendVarint(&out, (tmp.n+nTail) * 2);
      fts5BufferSafeAppendBlob(&out, tmp.p, tmp.n);
      if( nTail>0 ){
        fts5BufferSafeAppendBlob(&out, &pHead->aPos[pHead->iOff], nTail);
      }

      pHead = pSave;
      for(i=0; i<nBuf+1; i++){
        PrefixMerger *pX = &aMerger[i];
        if( pX->iter.aPoslist && pX->iter.iRowid==iLastRowid ){





          fts5DoclistIterNext(&pX->iter);
          fts5PrefixMergerInsertByRowid(&pHead, pX);



        }
      }

    }else{
      /* Copy poslist from pHead to output */
      PrefixMerger *pThis = pHead;
      Fts5DoclistIter *pI = &pThis->iter;
      fts5BufferSafeAppendBlob(&out, pI->aPoslist, pI->nPoslist+pI->nSize);
      fts5DoclistIterNext(pI);
      pHead = pThis->pNext;
      fts5PrefixMergerInsertByRowid(&pHead, pThis);
    }



  }


  fts5BufferFree(p1);
  fts5BufferFree(&tmp);
  memset(&out.p[out.n], 0, FTS5_DATA_ZERO_PADDING);
  *p1 = out;

}

static void fts5SetupPrefixIter(
  Fts5Index *p,                   /* Index to read from */
  int bDesc,                      /* True for "ORDER BY rowid DESC" */
  int iIdx,                       /* Index to scan for data */
  u8 *pToken,                     /* Buffer containing prefix to match */
  int nToken,                     /* Size of buffer pToken in bytes */
  Fts5Colset *pColset,            /* Restrict matches to these columns */
  Fts5Iter **ppIter          /* OUT: New iterator */
){
  Fts5Structure *pStruct;
  Fts5Buffer *aBuf;
  int nBuf = 32;
  int nMerge = 1;

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

  aBuf = (Fts5Buffer*)fts5IdxMalloc(p, sizeof(Fts5Buffer)*nBuf);
  pStruct = fts5StructureRead(p);

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

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

    pToken[0] = FTS5_MAIN_PREFIX + iIdx;
    fts5MultiIterNew(p, pStruct, flags, pColset, pToken, nToken, -1, 0, &p1);
    fts5IterSetOutputCb(&p->rc, p1);
    for( /* no-op */ ;
        fts5MultiIterEof(p, p1)==0;
        fts5MultiIterNext2(p, p1, &bNewTerm)
    ){
      Fts5SegIter *pSeg = &p1->aSeg[ p1->aFirst[1].iFirst ];
      int nTerm = pSeg->term.n;
      const u8 *pTerm = pSeg->term.p;
      p1->xSetOutputs(p1, pSeg);

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

      if( p1->base.nData==0 ) continue;

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

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

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

    pData = fts5IdxMalloc(p, sizeof(Fts5Data)+doclist.n+FTS5_DATA_ZERO_PADDING);
    if( pData ){
      pData->p = (u8*)&pData[1];
      pData->nn = pData->szLeaf = doclist.n;
222808
222809
222810
222811
222812
222813
222814

222815
222816
222817
222818
222819
222820
222821
  Fts5Buffer buf = {0, 0, 0};

  /* If the QUERY_SCAN flag is set, all other flags must be clear. */
  assert( (flags & FTS5INDEX_QUERY_SCAN)==0 || flags==FTS5INDEX_QUERY_SCAN );

  if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){
    int iIdx = 0;                 /* Index to search */

    if( nToken ) memcpy(&buf.p[1], pToken, nToken);

    /* Figure out which index to search and set iIdx accordingly. If this
    ** is a prefix query for which there is no prefix index, set iIdx to
    ** greater than pConfig->nPrefix to indicate that the query will be
    ** satisfied by scanning multiple terms in the main index.
    **







>







224075
224076
224077
224078
224079
224080
224081
224082
224083
224084
224085
224086
224087
224088
224089
  Fts5Buffer buf = {0, 0, 0};

  /* If the QUERY_SCAN flag is set, all other flags must be clear. */
  assert( (flags & FTS5INDEX_QUERY_SCAN)==0 || flags==FTS5INDEX_QUERY_SCAN );

  if( sqlite3Fts5BufferSize(&p->rc, &buf, nToken+1)==0 ){
    int iIdx = 0;                 /* Index to search */
    int iPrefixIdx = 0;           /* +1 prefix index */
    if( nToken ) memcpy(&buf.p[1], pToken, nToken);

    /* Figure out which index to search and set iIdx accordingly. If this
    ** is a prefix query for which there is no prefix index, set iIdx to
    ** greater than pConfig->nPrefix to indicate that the query will be
    ** satisfied by scanning multiple terms in the main index.
    **
222829
222830
222831
222832
222833
222834
222835
222836


222837
222838
222839
222840
222841
222842
222843
222844
222845
222846
222847
222848
222849
222850
222851
222852
222853
222854
222855
222856
222857
222858
222859
222860
222861
      assert( flags & FTS5INDEX_QUERY_PREFIX );
      iIdx = 1+pConfig->nPrefix;
    }else
#endif
    if( flags & FTS5INDEX_QUERY_PREFIX ){
      int nChar = fts5IndexCharlen(pToken, nToken);
      for(iIdx=1; iIdx<=pConfig->nPrefix; iIdx++){
        if( pConfig->aPrefix[iIdx-1]==nChar ) break;


      }
    }

    if( iIdx<=pConfig->nPrefix ){
      /* Straight index lookup */
      Fts5Structure *pStruct = fts5StructureRead(p);
      buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx);
      if( pStruct ){
        fts5MultiIterNew(p, pStruct, flags | FTS5INDEX_QUERY_SKIPEMPTY,
            pColset, buf.p, nToken+1, -1, 0, &pRet
        );
        fts5StructureRelease(pStruct);
      }
    }else{
      /* Scan multiple terms in the main index */
      int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0;
      buf.p[0] = FTS5_MAIN_PREFIX;
      fts5SetupPrefixIter(p, bDesc, buf.p, nToken+1, pColset, &pRet);
      assert( p->rc!=SQLITE_OK || pRet->pColset==0 );
      fts5IterSetOutputCb(&p->rc, pRet);
      if( p->rc==SQLITE_OK ){
        Fts5SegIter *pSeg = &pRet->aSeg[pRet->aFirst[1].iFirst];
        if( pSeg->pLeaf ) pRet->xSetOutputs(pRet, pSeg);
      }
    }







|
>
>
















<
|







224097
224098
224099
224100
224101
224102
224103
224104
224105
224106
224107
224108
224109
224110
224111
224112
224113
224114
224115
224116
224117
224118
224119
224120
224121
224122

224123
224124
224125
224126
224127
224128
224129
224130
      assert( flags & FTS5INDEX_QUERY_PREFIX );
      iIdx = 1+pConfig->nPrefix;
    }else
#endif
    if( flags & FTS5INDEX_QUERY_PREFIX ){
      int nChar = fts5IndexCharlen(pToken, nToken);
      for(iIdx=1; iIdx<=pConfig->nPrefix; iIdx++){
        int nIdxChar = pConfig->aPrefix[iIdx-1];
        if( nIdxChar==nChar ) break;
        if( nIdxChar==nChar+1 ) iPrefixIdx = iIdx;
      }
    }

    if( iIdx<=pConfig->nPrefix ){
      /* Straight index lookup */
      Fts5Structure *pStruct = fts5StructureRead(p);
      buf.p[0] = (u8)(FTS5_MAIN_PREFIX + iIdx);
      if( pStruct ){
        fts5MultiIterNew(p, pStruct, flags | FTS5INDEX_QUERY_SKIPEMPTY,
            pColset, buf.p, nToken+1, -1, 0, &pRet
        );
        fts5StructureRelease(pStruct);
      }
    }else{
      /* Scan multiple terms in the main index */
      int bDesc = (flags & FTS5INDEX_QUERY_DESC)!=0;

      fts5SetupPrefixIter(p, bDesc, iPrefixIdx, buf.p, nToken+1, pColset,&pRet);
      assert( p->rc!=SQLITE_OK || pRet->pColset==0 );
      fts5IterSetOutputCb(&p->rc, pRet);
      if( p->rc==SQLITE_OK ){
        Fts5SegIter *pSeg = &pRet->aSeg[pRet->aFirst[1].iFirst];
        if( pSeg->pLeaf ) pRet->xSetOutputs(pRet, pSeg);
      }
    }
226815
226816
226817
226818
226819
226820
226821
226822
226823
226824
226825
226826
226827
226828
226829
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-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b", -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){







|







228084
228085
228086
228087
228088
228089
228090
228091
228092
228093
228094
228095
228096
228097
228098
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: 2021-01-18 12:35:16 c1862abb44873f06ec0d772469d8a2d128ae4670b1e98c2d97b0e2da18df9a04", -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){
231741
231742
231743
231744
231745
231746
231747
231748
231749
231750
231751
231752
231753
231754
#endif
  return rc;
}
#endif /* SQLITE_CORE */
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */

/************** End of stmt.c ************************************************/
#if __LINE__!=231748
#undef SQLITE_SOURCE_ID
#define SQLITE_SOURCE_ID      "2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089falt2"
#endif
/* Return the source-id for this library */
SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
/************************** End of sqlite3.c ******************************/







|

|




233010
233011
233012
233013
233014
233015
233016
233017
233018
233019
233020
233021
233022
233023
#endif
  return rc;
}
#endif /* SQLITE_CORE */
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */

/************** End of stmt.c ************************************************/
#if __LINE__!=233017
#undef SQLITE_SOURCE_ID
#define SQLITE_SOURCE_ID      "2021-01-18 12:35:16 c1862abb44873f06ec0d772469d8a2d128ae4670b1e98c2d97b0e2da18dfalt2"
#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.

119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
** been edited in any way since it was last checked in, then the last
** four hexadecimal digits of the hash may be modified.
**
** See also: [sqlite3_libversion()],
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION        "3.34.0"
#define SQLITE_VERSION_NUMBER 3034000
#define SQLITE_SOURCE_ID      "2020-12-01 16:14:00 a26b6597e3ae272231b96f9982c3bcc17ddec2f2b6eb4df06a224b91089fed5b"

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







|
|
|







119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
** been edited in any way since it was last checked in, then the last
** four hexadecimal digits of the hash may be modified.
**
** See also: [sqlite3_libversion()],
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
** [sqlite_version()] and [sqlite_source_id()].
*/
#define SQLITE_VERSION        "3.35.0"
#define SQLITE_VERSION_NUMBER 3035000
#define SQLITE_SOURCE_ID      "2021-01-18 12:35:16 c1862abb44873f06ec0d772469d8a2d128ae4670b1e98c2d97b0e2da18df9a04"

/*
** 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
3495
3496
3497
3498
3499
3500
3501

3502
3503
3504
3505
3506
3507
3508
**          Regardless of whether or not shared-cache mode is enabled by
**          default, use a private cache.
** <tr><td> file:/home/fred/data.db?vfs=unix-dotfile <td>
**          Open file "/home/fred/data.db". Use the special VFS "unix-dotfile"
**          that uses dot-files in place of posix advisory locking.
** <tr><td> file:data.db?mode=readonly <td>
**          An error. "readonly" is not a valid option for the "mode" parameter.

** </table>
**
** ^URI hexadecimal escape sequences (%HH) are supported within the path and
** query components of a URI. A hexadecimal escape sequence consists of a
** percent sign - "%" - followed by exactly two hexadecimal digits
** specifying an octet value. ^Before the path or query components of a
** URI filename are interpreted, they are encoded using UTF-8 and all







>







3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
**          Regardless of whether or not shared-cache mode is enabled by
**          default, use a private cache.
** <tr><td> file:/home/fred/data.db?vfs=unix-dotfile <td>
**          Open file "/home/fred/data.db". Use the special VFS "unix-dotfile"
**          that uses dot-files in place of posix advisory locking.
** <tr><td> file:data.db?mode=readonly <td>
**          An error. "readonly" is not a valid option for the "mode" parameter.
**          Use "ro" instead:  "file:data.db?mode=ro".
** </table>
**
** ^URI hexadecimal escape sequences (%HH) are supported within the path and
** query components of a URI. A hexadecimal escape sequence consists of a
** percent sign - "%" - followed by exactly two hexadecimal digits
** specifying an octet value. ^Before the path or query components of a
** URI filename are interpreted, they are encoded using UTF-8 and all
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
** The sqlite3_free_filename(Y) routine releases a memory allocation
** previously obtained from sqlite3_create_filename().  Invoking
** 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,
** then the corresponding [sqlite3_module.xClose() method should also be
** invoked prior to calling sqlite3_free_filename(Y).
*/
SQLITE_API char *sqlite3_create_filename(
  const char *zDatabase,







|







3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
** The sqlite3_free_filename(Y) routine releases a memory allocation
** previously obtained from sqlite3_create_filename().  Invoking
** 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 not 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,
** then the corresponding [sqlite3_module.xClose() method should also be
** invoked prior to calling sqlite3_free_filename(Y).
*/
SQLITE_API char *sqlite3_create_filename(
  const char *zDatabase,
7761
7762
7763
7764
7765
7766
7767

7768
7769
7770
7771
7772
7773
7774
7775
#define SQLITE_TESTCTRL_SORTER_MMAP             24
#define SQLITE_TESTCTRL_IMPOSTER                25
#define SQLITE_TESTCTRL_PARSER_COVERAGE         26
#define SQLITE_TESTCTRL_RESULT_INTREAL          27
#define SQLITE_TESTCTRL_PRNG_SEED               28
#define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS     29
#define SQLITE_TESTCTRL_SEEK_COUNT              30

#define SQLITE_TESTCTRL_LAST                    30  /* Largest TESTCTRL */

/*
** CAPI3REF: SQL Keyword Checking
**
** These routines provide access to the set of SQL language keywords
** recognized by SQLite.  Applications can uses these routines to determine
** whether or not a specific identifier needs to be escaped (for example,







>
|







7762
7763
7764
7765
7766
7767
7768
7769
7770
7771
7772
7773
7774
7775
7776
7777
#define SQLITE_TESTCTRL_SORTER_MMAP             24
#define SQLITE_TESTCTRL_IMPOSTER                25
#define SQLITE_TESTCTRL_PARSER_COVERAGE         26
#define SQLITE_TESTCTRL_RESULT_INTREAL          27
#define SQLITE_TESTCTRL_PRNG_SEED               28
#define SQLITE_TESTCTRL_EXTRA_SCHEMA_CHECKS     29
#define SQLITE_TESTCTRL_SEEK_COUNT              30
#define SQLITE_TESTCTRL_TRACEFLAGS              31
#define SQLITE_TESTCTRL_LAST                    31  /* Largest TESTCTRL */

/*
** CAPI3REF: SQL Keyword Checking
**
** These routines provide access to the set of SQL language keywords
** recognized by SQLite.  Applications can uses these routines to determine
** whether or not a specific identifier needs to be escaped (for example,
10434
10435
10436
10437
10438
10439
10440








10441
10442
10443
10444
10445
10446
10447
** an attached table is modified and then later on the original values
** are restored. However, if this function returns non-zero, then it is
** guaranteed that a call to sqlite3session_changeset() will return a
** changeset containing zero changes.
*/
SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession);









/*
** CAPI3REF: Create An Iterator To Traverse A Changeset
** CONSTRUCTOR: sqlite3_changeset_iter
**
** Create an iterator used to iterate through the contents of a changeset.
** If successful, *pp is set to point to the iterator handle and SQLITE_OK
** is returned. Otherwise, if an error occurs, *pp is set to zero and an







>
>
>
>
>
>
>
>







10436
10437
10438
10439
10440
10441
10442
10443
10444
10445
10446
10447
10448
10449
10450
10451
10452
10453
10454
10455
10456
10457
** an attached table is modified and then later on the original values
** are restored. However, if this function returns non-zero, then it is
** guaranteed that a call to sqlite3session_changeset() will return a
** changeset containing zero changes.
*/
SQLITE_API int sqlite3session_isempty(sqlite3_session *pSession);

/*
** CAPI3REF: Query for the amount of heap memory used by a session object.
**
** This API returns the total amount of heap memory in bytes currently
** used by the session object passed as the only argument.
*/
SQLITE_API sqlite3_int64 sqlite3session_memory_used(sqlite3_session *pSession);

/*
** CAPI3REF: Create An Iterator To Traverse A Changeset
** CONSTRUCTOR: sqlite3_changeset_iter
**
** Create an iterator used to iterate through the contents of a changeset.
** If successful, *pp is set to point to the iterator handle and SQLITE_OK
** is returned. Otherwise, if an error occurs, *pp is set to zero and an

Changes to src/stat.c.

215
216
217
218
219
220
221











222
223
224
225
226
227
228
    @ %,d(n)
    @ </td></tr>
    @ <tr><th>Number&nbsp;Of&nbsp;Wiki&nbsp;Pages:</th><td>
    n = db_int(0, "SELECT count(*) FROM tag  /*scan*/"
                  " WHERE +tagname GLOB 'wiki-*'");
    @ %,d(n)
    @ </td></tr>











    n = db_int(0, "SELECT count(*) FROM tag  /*scan*/"
                  " WHERE +tagname GLOB 'tkt-*'");
    if( n>0 ){
      @ <tr><th>Number&nbsp;Of&nbsp;Tickets:</th><td>%,d(n)</td></tr>
    }
    if( db_table_exists("repository","forumpost") ){
      n = db_int(0, "SELECT count(*) FROM forumpost/*scan*/");







>
>
>
>
>
>
>
>
>
>
>







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
    @ %,d(n)
    @ </td></tr>
    @ <tr><th>Number&nbsp;Of&nbsp;Wiki&nbsp;Pages:</th><td>
    n = db_int(0, "SELECT count(*) FROM tag  /*scan*/"
                  " WHERE +tagname GLOB 'wiki-*'");
    @ %,d(n)
    @ </td></tr>
    if( db_table_exists("repository","chat") ){
      sqlite3_int64 sz = 0;
      char zSz[100];
      n = db_int(0, "SELECT max(msgid) FROM chat");
      m = db_int(0, "SELECT count(*) FROM chat WHERE mdel IS NOT TRUE");
      sz = db_int64(0, "SELECT sum(coalesce(length(xmsg),0)+"
                                  "coalesce(length(file),0)) FROM chat");
      approxSizeName(sizeof(zSz), zSz, sz);
      @ <tr><th>Number&nbsp;Of&nbsp;Chat&nbsp;Messages:</th>
      @ <td>%,d(n) (%,d(m) still alive, %s(zSz) in size)</td></tr>
    }
    n = db_int(0, "SELECT count(*) FROM tag  /*scan*/"
                  " WHERE +tagname GLOB 'tkt-*'");
    if( n>0 ){
      @ <tr><th>Number&nbsp;Of&nbsp;Tickets:</th><td>%,d(n)</td></tr>
    }
    if( db_table_exists("repository","forumpost") ){
      n = db_int(0, "SELECT count(*) FROM forumpost/*scan*/");
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
    @ <td>Last run: %z(backoffice_last_run())</td></tr>
  }
  if( g.perm.Admin && alert_enabled() ){
    stats_for_email();
  }

  @ </table>
  style_finish_page("stat");
}

/*
** COMMAND: dbstat
**
** Usage: %fossil dbstat OPTIONS
**







|







297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
    @ <td>Last run: %z(backoffice_last_run())</td></tr>
  }
  if( g.perm.Admin && alert_enabled() ){
    stats_for_email();
  }

  @ </table>
  style_finish_page();
}

/*
** COMMAND: dbstat
**
** Usage: %fossil dbstat OPTIONS
**
457
458
459
460
461
462
463

464
465
466
467
468
469
470
  int showAll = P("all")!=0;
  int nOmitted;
  sqlite3_int64 iNow;
  char *zRemote;
  login_check_credentials();
  if( !g.perm.Admin ){ login_needed(0); return; }


  style_header("URLs and Checkouts");
  style_adunit_config(ADUNIT_RIGHT_OK);
  style_submenu_element("Stat", "stat");
  style_submenu_element("Schema", "repo_schema");
  iNow = db_int64(0, "SELECT strftime('%%s','now')");
  @ <div class="section">URLs</div>
  @ <table border="0" width='100%%'>







>







468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
  int showAll = P("all")!=0;
  int nOmitted;
  sqlite3_int64 iNow;
  char *zRemote;
  login_check_credentials();
  if( !g.perm.Admin ){ login_needed(0); return; }

  style_set_current_feature("stat");
  style_header("URLs and Checkouts");
  style_adunit_config(ADUNIT_RIGHT_OK);
  style_submenu_element("Stat", "stat");
  style_submenu_element("Schema", "repo_schema");
  iNow = db_int64(0, "SELECT strftime('%%s','now')");
  @ <div class="section">URLs</div>
  @ <table border="0" width='100%%'>
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
      url_parse_local(zRemote, URL_OMIT_USER, &x);
      @ <p><a href='%h(x.canonical)'>%h(zRemote)</a>
    }else{
      @ <p>%h(zRemote)</p>
    }
    @ </div>
  }
  style_finish_page("stat");
}

/*
** WEBPAGE: repo_schema
**
** Show the repository schema
*/
void repo_schema_page(void){
  Stmt q;
  Blob sql;
  const char *zArg = P("n");
  login_check_credentials();
  if( !g.perm.Admin ){ login_needed(0); return; }


  style_header("Repository Schema");
  style_adunit_config(ADUNIT_RIGHT_OK);
  style_submenu_element("Stat", "stat");
  style_submenu_element("URLs", "urllist");
  if( sqlite3_compileoption_used("ENABLE_DBSTAT_VTAB") ){
    style_submenu_element("Table Sizes", "repo-tabsize");
  }







|














>







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
      url_parse_local(zRemote, URL_OMIT_USER, &x);
      @ <p><a href='%h(x.canonical)'>%h(zRemote)</a>
    }else{
      @ <p>%h(zRemote)</p>
    }
    @ </div>
  }
  style_finish_page();
}

/*
** WEBPAGE: repo_schema
**
** Show the repository schema
*/
void repo_schema_page(void){
  Stmt q;
  Blob sql;
  const char *zArg = P("n");
  login_check_credentials();
  if( !g.perm.Admin ){ login_needed(0); return; }

  style_set_current_feature("stat");
  style_header("Repository Schema");
  style_adunit_config(ADUNIT_RIGHT_OK);
  style_submenu_element("Stat", "stat");
  style_submenu_element("URLs", "urllist");
  if( sqlite3_compileoption_used("ENABLE_DBSTAT_VTAB") ){
    style_submenu_element("Table Sizes", "repo-tabsize");
  }
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590

591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625

626
627
628
629
630
631
632
      }
      @ </pre>
      db_finalize(&q);
    }else{
      style_submenu_element("Stat1","repo_stat1");
    }
  }
  style_finish_page("stat");
}

/*
** WEBPAGE: repo_stat1
**
** Show the sqlite_stat1 table for the repository schema
*/
void repo_stat1_page(void){
  login_check_credentials();
  if( !g.perm.Admin ){ login_needed(0); return; }


  style_header("Repository STAT1 Table");
  style_adunit_config(ADUNIT_RIGHT_OK);
  style_submenu_element("Stat", "stat");
  style_submenu_element("Schema", "repo_schema");
  if( db_table_exists("repository","sqlite_stat1") ){
    Stmt q;
    db_prepare(&q,
      "SELECT tbl, idx, stat FROM repository.sqlite_stat1"
      " ORDER BY tbl, idx");
    @ <pre>
    while( db_step(&q)==SQLITE_ROW ){
      const char *zTab = db_column_text(&q,0);
      const char *zIdx = db_column_text(&q,1);
      const char *zStat = db_column_text(&q,2);
      char *zUrl = href("%R/repo_schema?n=%t",zTab);
      @ INSERT INTO sqlite_stat1 VALUES('%z(zUrl)%h(zTab)</a>','%h(zIdx)','%h(zStat)');
    }
    @ </pre>
    db_finalize(&q);
  }
  style_finish_page("stat");
}

/*
** WEBPAGE: repo-tabsize
**
** Show relative sizes of tables in the repository database.
*/
void repo_tabsize_page(void){
  int nPageFree;
  sqlite3_int64 fsize;
  char zBuf[100];

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }

  style_header("Repository Table Sizes");
  style_adunit_config(ADUNIT_RIGHT_OK);
  style_submenu_element("Stat", "stat");
  if( g.perm.Admin ){
    style_submenu_element("Schema", "repo_schema");
  }
  db_multi_exec(







|











>




















|














>







585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
      }
      @ </pre>
      db_finalize(&q);
    }else{
      style_submenu_element("Stat1","repo_stat1");
    }
  }
  style_finish_page();
}

/*
** WEBPAGE: repo_stat1
**
** Show the sqlite_stat1 table for the repository schema
*/
void repo_stat1_page(void){
  login_check_credentials();
  if( !g.perm.Admin ){ login_needed(0); return; }

  style_set_current_feature("stat");
  style_header("Repository STAT1 Table");
  style_adunit_config(ADUNIT_RIGHT_OK);
  style_submenu_element("Stat", "stat");
  style_submenu_element("Schema", "repo_schema");
  if( db_table_exists("repository","sqlite_stat1") ){
    Stmt q;
    db_prepare(&q,
      "SELECT tbl, idx, stat FROM repository.sqlite_stat1"
      " ORDER BY tbl, idx");
    @ <pre>
    while( db_step(&q)==SQLITE_ROW ){
      const char *zTab = db_column_text(&q,0);
      const char *zIdx = db_column_text(&q,1);
      const char *zStat = db_column_text(&q,2);
      char *zUrl = href("%R/repo_schema?n=%t",zTab);
      @ INSERT INTO sqlite_stat1 VALUES('%z(zUrl)%h(zTab)</a>','%h(zIdx)','%h(zStat)');
    }
    @ </pre>
    db_finalize(&q);
  }
  style_finish_page();
}

/*
** WEBPAGE: repo-tabsize
**
** Show relative sizes of tables in the repository database.
*/
void repo_tabsize_page(void){
  int nPageFree;
  sqlite3_int64 fsize;
  char zBuf[100];

  login_check_credentials();
  if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  style_set_current_feature("stat");
  style_header("Repository Table Sizes");
  style_adunit_config(ADUNIT_RIGHT_OK);
  style_submenu_element("Stat", "stat");
  if( g.perm.Admin ){
    style_submenu_element("Schema", "repo_schema");
  }
  db_multi_exec(
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
    fsize = file_size(g.zLocalDbName, ExtFILE);
    approxSizeName(sizeof(zBuf), zBuf, fsize);
    @ <h2>%h(file_tail(g.zLocalDbName)) Size: %s(zBuf)</h2>
    @ <center><svg width='800' height='500'>
    piechart_render(800,500,PIE_OTHER|PIE_PERCENT);
    @ </svg></center>
  }
  style_finish_page("stat");
}

/*
** Gather statistics on artifact types, counts, and sizes.
**
** Only populate the artstat.atype field if the bWithTypes parameter is true.
*/







|







691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
    fsize = file_size(g.zLocalDbName, ExtFILE);
    approxSizeName(sizeof(zBuf), zBuf, fsize);
    @ <h2>%h(file_tail(g.zLocalDbName)) Size: %s(zBuf)</h2>
    @ <center><svg width='800' height='500'>
    piechart_render(800,500,PIE_OTHER|PIE_PERCENT);
    @ </svg></center>
  }
  style_finish_page();
}

/*
** Gather statistics on artifact types, counts, and sizes.
**
** Only populate the artstat.atype field if the bWithTypes parameter is true.
*/
796
797
798
799
800
801
802

803
804
805
806
807
808
809
  */
  if( !g.perm.Write && !db_get_boolean("artifact_stats_enable",0) ){
    login_needed(g.anon.Write);
    return;
  }
  load_control();


  style_header("Artifact Statistics");
  style_submenu_element("Repository Stats", "stat");
  style_submenu_element("Artifact List", "bloblist");
  gather_artifact_stats(1);

  db_prepare(&q,
    "SELECT count(*), sum(isDelta), max(szCmpr),"







>







811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
  */
  if( !g.perm.Write && !db_get_boolean("artifact_stats_enable",0) ){
    login_needed(g.anon.Write);
    return;
  }
  load_control();

  style_set_current_feature("stat");
  style_header("Artifact Statistics");
  style_submenu_element("Repository Stats", "stat");
  style_submenu_element("Artifact List", "bloblist");
  gather_artifact_stats(1);

  db_prepare(&q,
    "SELECT count(*), sum(isDelta), max(szCmpr),"
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
  mxCmpr = db_column_int(&q, 2);
  mxExp = db_column_int(&q, 3);
  sumCmpr = db_column_int64(&q, 4);
  sumExp = db_column_int64(&q, 5);
  db_finalize(&q);
  if( nTotal==0 ){
    @ No artifacts in this repository!
    style_finish_page("stat");
    return;
  }
  avgCmpr = (double)sumCmpr/nTotal;
  avgExp = (double)sumExp/nTotal;

  db_prepare(&q, "SELECT szCmpr FROM artstat ORDER BY 1 DESC");
  r = 0;







|







833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
  mxCmpr = db_column_int(&q, 2);
  mxExp = db_column_int(&q, 3);
  sumCmpr = db_column_int64(&q, 4);
  sumExp = db_column_int64(&q, 5);
  db_finalize(&q);
  if( nTotal==0 ){
    @ No artifacts in this repository!
    style_finish_page();
    return;
  }
  avgCmpr = (double)sumCmpr/nTotal;
  avgExp = (double)sumExp/nTotal;

  db_prepare(&q, "SELECT szCmpr FROM artstat ORDER BY 1 DESC");
  r = 0;
962
963
964
965
966
967
968
969
970
      @ <td>%h(zDate)</td>
      @ <td>%z(href("%R/rcvfrom?rcvid=%d",iRcvid))%d(iRcvid)</a></td></tr>
    }
    @ </tbody></table></div>
    db_finalize(&q);
  }
  style_table_sorter();
  style_finish_page("stat");
}







|

978
979
980
981
982
983
984
985
986
      @ <td>%h(zDate)</td>
      @ <td>%z(href("%R/rcvfrom?rcvid=%d",iRcvid))%d(iRcvid)</a></td></tr>
    }
    @ </tbody></table></div>
    db_finalize(&q);
  }
  style_table_sorter();
  style_finish_page();
}

Changes to src/statrep.c.

823
824
825
826
827
828
829
830
831
    case RPT_BYFILE:
      stats_report_by_file(zUserName);
      break;
    case RPT_LASTCHNG:
      stats_report_last_change();
      break;
  }
  style_finish_page("reports");
}







|

823
824
825
826
827
828
829
830
831
    case RPT_BYFILE:
      stats_report_by_file(zUserName);
      break;
    case RPT_LASTCHNG:
      stats_report_last_change();
      break;
  }
  style_finish_page();
}

Changes to src/style.c.

31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
**      style_submenu_element()
**      style_submenu_entry()
**      style_submenu_checkbox()
**      style_submenu_binary()
**      style_submenu_multichoice()
**      style_submenu_sql()
**
** prior to calling style_footer().  The style_footer() routine
** will generate the appropriate HTML text just below the main
** menu.
*/
static struct Submenu {
  const char *zLabel;        /* Button label */
  const char *zLink;         /* Jump to this link when button is pressed */
} aSubmenu[30];







|







31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
**      style_submenu_element()
**      style_submenu_entry()
**      style_submenu_checkbox()
**      style_submenu_binary()
**      style_submenu_multichoice()
**      style_submenu_sql()
**
** prior to calling style_finish_page().  The style_finish_page() routine
** will generate the appropriate HTML text just below the main
** menu.
*/
static struct Submenu {
  const char *zLabel;        /* Button label */
  const char *zLink;         /* Jump to this link when button is pressed */
} aSubmenu[30];
82
83
84
85
86
87
88






89
90
91
92
93
94
95
static unsigned adUnitFlags = 0;

/*
** Submenu disable flag
*/
static int submenuEnable = 1;







/*
** Flags for various javascript files needed prior to </body>
*/
static int needHrefJs = 0;      /* href.js */

/*
** Extra JS added to the end of the file.







>
>
>
>
>
>







82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
static unsigned adUnitFlags = 0;

/*
** Submenu disable flag
*/
static int submenuEnable = 1;

/*
** Disable content-security-policy.
** Warning:  Do not disable the CSP without careful consideration!
*/
static int disableCSP = 0;

/*
** Flags for various javascript files needed prior to </body>
*/
static int needHrefJs = 0;      /* href.js */

/*
** Extra JS added to the end of the file.
523
524
525
526
527
528
529
530
531
532
533
534


535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553











554
555
556
557
558
559
560
561
562
563

564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579

































580
581
582
583
584
585
586
** should be released by the caller.
*/
char *style_csp(int toHeader){
  static const char zBackupCSP[] = 
   "default-src 'self' data:; "
   "script-src 'self' 'nonce-$nonce'; "
   "style-src 'self' 'unsafe-inline'";
  const char *zFormat = db_get("default-csp","");
  Blob csp;
  char *zNonce;
  char *zCsp;
  int i;


  if( zFormat[0]==0 ){
    zFormat = zBackupCSP;
  }
  blob_init(&csp, 0, 0);
  while( zFormat[0] && (zNonce = strstr(zFormat,"$nonce"))!=0 ){
    blob_append(&csp, zFormat, (int)(zNonce - zFormat));
    blob_append(&csp, style_nonce(), -1);
    zFormat = zNonce + 6;
  }
  blob_append(&csp, zFormat, -1);
  zCsp = blob_str(&csp);
  /* No whitespace other than actual space characters allowed in the CSP
  ** string.  See https://fossil-scm.org/forum/forumpost/d29e3af43c */
  for(i=0; zCsp[i]; i++){ if( fossil_isspace(zCsp[i]) ) zCsp[i] = ' '; }
  if( toHeader ){
    cgi_printf_header("Content-Security-Policy: %s\r\n", zCsp);
  }
  return zCsp;
}












/*
** Default HTML page header text through <body>.  If the repository-specific
** header template lacks a <body> tag, then all of the following is
** prepended.
*/
static const char zDfltHeader[] = 
@ <html>
@ <head>
@ <base href="$baseurl/$current_page" />

@ <meta http-equiv="Content-Security-Policy" content="$default_csp" />
@ <meta name="viewport" content="width=device-width, initial-scale=1.0">
@ <title>$<project_name>: $<title></title>
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed" \
@  href="$home/timeline.rss" />
@ <link rel="stylesheet" href="$stylesheet_url" type="text/css" />
@ </head>
@ <body>
;

/*
** Returns the default page header.
*/
const char *get_default_header(){
  return zDfltHeader;
}


































/*
** Initialize all the default TH1 variables
*/
static void style_init_th1_vars(const char *zTitle){
  const char *zNonce = style_nonce();
  char *zDfltCsp;







|




>
>



















>
>
>
>
>
>
>
>
>
>
>










>







|








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







529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
** should be released by the caller.
*/
char *style_csp(int toHeader){
  static const char zBackupCSP[] = 
   "default-src 'self' data:; "
   "script-src 'self' 'nonce-$nonce'; "
   "style-src 'self' 'unsafe-inline'";
  const char *zFormat;
  Blob csp;
  char *zNonce;
  char *zCsp;
  int i;
  if( disableCSP ) return fossil_strdup("");
  zFormat = db_get("default-csp","");
  if( zFormat[0]==0 ){
    zFormat = zBackupCSP;
  }
  blob_init(&csp, 0, 0);
  while( zFormat[0] && (zNonce = strstr(zFormat,"$nonce"))!=0 ){
    blob_append(&csp, zFormat, (int)(zNonce - zFormat));
    blob_append(&csp, style_nonce(), -1);
    zFormat = zNonce + 6;
  }
  blob_append(&csp, zFormat, -1);
  zCsp = blob_str(&csp);
  /* No whitespace other than actual space characters allowed in the CSP
  ** string.  See https://fossil-scm.org/forum/forumpost/d29e3af43c */
  for(i=0; zCsp[i]; i++){ if( fossil_isspace(zCsp[i]) ) zCsp[i] = ' '; }
  if( toHeader ){
    cgi_printf_header("Content-Security-Policy: %s\r\n", zCsp);
  }
  return zCsp;
}

/*
** Disable content security policy for the current page.
** WARNING:  Do not do this lightly!
**
** This routine must be called before the CSP is sued by 
** style_header().
*/
void style_disable_csp(void){
  disableCSP = 1;
}

/*
** Default HTML page header text through <body>.  If the repository-specific
** header template lacks a <body> tag, then all of the following is
** prepended.
*/
static const char zDfltHeader[] = 
@ <html>
@ <head>
@ <base href="$baseurl/$current_page" />
@ <meta charset="UTF-8">
@ <meta http-equiv="Content-Security-Policy" content="$default_csp" />
@ <meta name="viewport" content="width=device-width, initial-scale=1.0">
@ <title>$<project_name>: $<title></title>
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed" \
@  href="$home/timeline.rss" />
@ <link rel="stylesheet" href="$stylesheet_url" type="text/css" />
@ </head>
@ <body class="$current_feature">
;

/*
** Returns the default page header.
*/
const char *get_default_header(){
  return zDfltHeader;
}

/*
** Given a URL path, extract the first element as a "feature" name,
** used as the <body class="FEATURE"> value by default, though
** later-running code may override this, typically to group multiple
** Fossil UI URLs into a single "feature" so you can have per-feature
** CSS rules.
**
** For example, "body.forum div.markdown blockquote" targets only
** block quotes made in forum posts, leaving other Markdown quotes
** alone.  Because feature class "forum" groups /forummain, /forumpost,
** and /forume2, it works across all renderings of Markdown to HTML
** within the Fossil forum feature.
*/
static const char* feature_from_page_path(const char *zPath){
  const char* zSlash = strchr(zPath, '/');
  if (zSlash) {
    return fossil_strndup(zPath, zSlash - zPath);
  } else {
    return zPath;
  }
}

/*
** Override the value of the TH1 variable current_feature, its default
** set by feature_from_page_path().  We do not call this from
** style_init_th1_vars() because that uses Th_MaybeStore() instead to
** allow webpage implementations to call this before style_header()
** to override that "maybe" default with something better.
*/
void style_set_current_feature(const char* zFeature){
  Th_Store("current_feature", zFeature);
}

/*
** Initialize all the default TH1 variables
*/
static void style_init_th1_vars(const char *zTitle){
  const char *zNonce = style_nonce();
  char *zDfltCsp;
610
611
612
613
614
615
616

617
618
619
620
621
622
623
  Th_Store("compiler_name", COMPILER_NAME);
  url_var("stylesheet", "css", "style.css");
  image_url_var("logo");
  image_url_var("background");
  if( !login_is_nobody() ){
    Th_Store("login", g.zLogin);
  }

}

/*
** Draw the header.
*/
void style_header(const char *zTitleFormat, ...){
  va_list ap;







>







663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
  Th_Store("compiler_name", COMPILER_NAME);
  url_var("stylesheet", "css", "style.css");
  image_url_var("logo");
  image_url_var("background");
  if( !login_is_nobody() ){
    Th_Store("login", g.zLogin);
  }
  Th_MaybeStore("current_feature", feature_from_page_path(local_zCurrentPage) );
}

/*
** Draw the header.
*/
void style_header(const char *zTitleFormat, ...){
  va_list ap;
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
**      submenu elements.  The header generation is deferred until this point
**      so that we know that all style_submenu_element() and similar have
**      been received.
**
**   *  Finalizes the page content.
**
**   *  Appends the footer.
**
** The zPageType argument is a class name inserted in the <div> that
** surrounds the page content.  CSS can use this to have different styles
** according to the page type.
*/
void style_finish_page(const char* zPageType){
  const char *zFooter;
  const char *zAd = 0;
  unsigned int mAdFlags = 0;

  if( !headerHasBeenGenerated ) return;

  /* Go back and put the submenu at the top of the page.  We delay the







<
<
<
<

|







800
801
802
803
804
805
806




807
808
809
810
811
812
813
814
815
**      submenu elements.  The header generation is deferred until this point
**      so that we know that all style_submenu_element() and similar have
**      been received.
**
**   *  Finalizes the page content.
**
**   *  Appends the footer.




*/
void style_finish_page(){
  const char *zFooter;
  const char *zAd = 0;
  unsigned int mAdFlags = 0;

  if( !headerHasBeenGenerated ) return;

  /* Go back and put the submenu at the top of the page.  We delay the
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
    @ </div>
  }else if( zAd ){
    @ <div class="adunit_banner">
    cgi_append_content(zAd, -1);
    @ </div>
  }

  @ <div class="content %s(zPageType)"><span id="debugMsg"></span>
  cgi_destination(CGI_BODY);

  if( sideboxUsed ){
    @ <div class="endContent"></div>
  }
  @ </div>








|







933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
    @ </div>
  }else if( zAd ){
    @ <div class="adunit_banner">
    cgi_append_content(zAd, -1);
    @ </div>
  }

  @ <div class="content"><span id="debugMsg"></span>
  cgi_destination(CGI_BODY);

  if( sideboxUsed ){
    @ <div class="endContent"></div>
  }
  @ </div>

1148
1149
1150
1151
1152
1153
1154

1155
1156
1157
1158
1159
1160
1161
  char zCap[100];

  login_check_credentials();
  if( g.perm.Admin || g.perm.Setup  || db_get_boolean("test_env_enable",0) ){
    isAuth = 1;
  }
  cgi_load_environment();

  if( zFormat[0] ){
    va_list ap;
    va_start(ap, zFormat);
    zErr = vmprintf(zFormat, ap);
    va_end(ap);
    style_header("Bad Request");
    @ <h1>/%h(g.zPath): %h(zErr)</h1>







>







1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
  char zCap[100];

  login_check_credentials();
  if( g.perm.Admin || g.perm.Setup  || db_get_boolean("test_env_enable",0) ){
    isAuth = 1;
  }
  cgi_load_environment();
  style_set_current_feature(zFormat[0]==0 ? "test" : "error");
  if( zFormat[0] ){
    va_list ap;
    va_start(ap, zFormat);
    zErr = vmprintf(zFormat, ap);
    va_end(ap);
    style_header("Bad Request");
    @ <h1>/%h(g.zPath): %h(zErr)</h1>
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
    }
    @ 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))
      @ </pre>
    }
  }
  if( zErr && zErr[0] ){
    style_finish_page("error");
    cgi_reply();
    fossil_exit(1);
  }else{
    style_finish_page("test");
  }
}

/*
** Generate a Not Yet Implemented error page.
*/
void webpage_not_yet_implemented(void){







>
>
>













|



|







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
    }
    @ 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 />
#ifndef _WIN32
    @ RSS = %.2f(fossil_rss()/1000000.0) MB</br />
#endif
    @ 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))
      @ </pre>
    }
  }
  if( zErr && zErr[0] ){
    style_finish_page();
    cgi_reply();
    fossil_exit(1);
  }else{
    style_finish_page();
  }
}

/*
** Generate a Not Yet Implemented error page.
*/
void webpage_not_yet_implemented(void){
1240
1241
1242
1243
1244
1245
1246

1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
  if( zFormat ){
    va_start(ap, zFormat);
    zMsg = vmprintf(zFormat, ap);
    va_end(ap);
  }else{
    zMsg = "Not Found";
  }

  style_header("Not Found");
  @ <p>%h(zMsg)</p>
  cgi_set_status(404, "Not Found");
  style_finish_page("enotfound");
}

#if INTERFACE
# define webpage_assert(T) if(!(T)){webpage_assert_page(__FILE__,__LINE__,#T);}
#endif

/*







>



|







1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
  if( zFormat ){
    va_start(ap, zFormat);
    zMsg = vmprintf(zFormat, ap);
    va_end(ap);
  }else{
    zMsg = "Not Found";
  }
  style_set_current_feature("enotfound");
  style_header("Not Found");
  @ <p>%h(zMsg)</p>
  cgi_set_status(404, "Not Found");
  style_finish_page();
}

#if INTERFACE
# define webpage_assert(T) if(!(T)){webpage_assert_page(__FILE__,__LINE__,#T);}
#endif

/*

Changes to src/sync.c.

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

/*
** COMMAND: remote
** COMMAND: remote-url*
**
** Usage: %fossil remote ?SUBCOMMAND ...?
**
** Use this command to view or modify the set of remote repositories
** used as the default target for sync, push, and pull and for autosync.


**
** The default remote is set automatically by a "clone" command or by any
** "sync", "push", or "pull" command that specifies an explicit URL

** and omits the --once flag.  The default remote is used by
** auto-syncing and by "sync", "push", and "pull" that omit the server URL.
** Additional remotes can be added using the "add" command or deleted
** using the "delete" command.  The name of any additional remote can be
** used as an argument to the "sync", "push", and "pull" commands where
** one would normally put a URL argument.

**
** See "fossil help clone" for further information about URL formats.


**
** The official name of this command is "remote-url" but most people
** use the shortened name "remote".
**
** > fossil remote
**
**     With no arguments, this command shows the current default remote.
**     Or if there is no default, it shows "off".  The default remote is
**     used by autosync.  The default remote is whatever URL was specified
**     for the most recent "sync", "push", or "pull" command that omitted
**     the --once option.
**
** > fossil remote add NAME URL
**
**     Add a new URL to the set of remotes.  The new URL is assigned the
**     symbolic identifier "NAME".  Subsequently, NAME can be used in place
**     of the full URL on commands like "push" and "pull".
**
** > fossil remote delete NAME
**
**     Delete a URL previously added by the "add" subcommand.
**
** > fossil remote list
**
**     Show all remote URLs
**
** > fossil remote off
**
**     Disable the default URL.  Use this as a shorthand to prevent
**     autosync while in airplane mode, for example.




**
** > fossil remote REF
**
**     Make REF the new default URL.  The prior default URL is replaced.
**     REF can be either an explicit URL or a NAME from a prior "add".
*/
void remote_url_cmd(void){
  char *zUrl, *zArg;
  int nArg;
  db_find_and_open_repository(0, 0);

  /* We should be done with options.. */







|
|
>
>

|
<
>
|
<
<
|
|
|
>

<
>
>

|
|



|
|
<
<
<



|
<
|



|



|



|
|
>
>
>
>



|
|







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

/*
** COMMAND: remote
** COMMAND: remote-url*
**
** Usage: %fossil remote ?SUBCOMMAND ...?
**
** View or modify the set of remote repository sync URLs used as the
** target in any command that uses the sync protocol: "sync", "push",
** and "pull", plus all other commands that trigger Fossil's autosync
** feature.  (Collectively, "sync operations".)
**
** See "fossil help clone" for the format of these sync URLs.

**
** Fossil implicitly sets the default remote sync URL from the initial


** "clone" or "open URL" command for a repository, then may subsequently
** change it when given a URL in commands that take a sync URL, except
** when given the --once flag.  Fossil uses this new sync URL as its
** default when not explicitly given one in subsequent sync operations.
**

** Named remotes added by "remote add" allow use of those names in place
** of a sync URL in any command that takes one.
**
** The full name of this command is "remote-url", but we anticipate no
** future collision from use of its shortened form "remote".
**
** > fossil remote
**
**     With no arguments, this command shows the current default remote
**     URL.  If there is no default, it shows "off".



**
** > fossil remote add NAME URL
**
**     Add a new named URL to the set of remote sync URLs for use in

**     place of a sync URL in commands that take one.
**
** > fossil remote delete NAME
**
**     Delete a sync URL previously added by the "add" subcommand.
**
** > fossil remote list
**
**     Show all remote repository sync URLs.
**
** > fossil remote off
**
**     Forget the default sync URL, disabling autosync.  Combined with
**     named sync URLs, it allows canceling this "airplane mode" with
**     "fossil remote NAME" to select a previously-set named URL.
**
**     To disable use of the default remote without forgetting its URL,
**     say "fossil set autosync 0" instead.
**
** > fossil remote REF
**
**     Make REF the new default URL, replacing the prior default.
**     REF may be a URL or a NAME from a prior "add".
*/
void remote_url_cmd(void){
  char *zUrl, *zArg;
  int nArg;
  db_find_and_open_repository(0, 0);

  /* We should be done with options.. */

Changes to src/tag.c.

539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
                    "  WHERE tagtype>0 AND tagid=%d"
                    ")"
          " ORDER BY event.mtime DESC /*sort*/",
          timeline_query_for_tty(), zType, tagid
        );
        db_prepare(&q, "%s", blob_sql_text(&sql));
        blob_reset(&sql);
        print_timeline(&q, nFindLimit, 79, 0);
        db_finalize(&q);
      }
    }
  }else

  if(( strncmp(g.argv[2],"list",n)==0 )||( strncmp(g.argv[2],"ls",n)==0 )){
    Stmt q;







|







539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
                    "  WHERE tagtype>0 AND tagid=%d"
                    ")"
          " ORDER BY event.mtime DESC /*sort*/",
          timeline_query_for_tty(), zType, tagid
        );
        db_prepare(&q, "%s", blob_sql_text(&sql));
        blob_reset(&sql);
        print_timeline(&q, nFindLimit, 79, 0, 0);
        db_finalize(&q);
      }
    }
  }else

  if(( strncmp(g.argv[2],"list",n)==0 )||( strncmp(g.argv[2],"ls",n)==0 )){
    Stmt q;
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
      @ %h(zName)</a></li>
    }else{
      @ <li><span class="tagDsp">%h(zName)</span></li>
    }
  }
  @ </ul>
  db_finalize(&q);
  style_finish_page("taglist");
}

/*
** WEBPAGE: /tagtimeline
**
** Render a timeline with all check-ins that contain non-propagating
** symbolic tags.







|







722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
      @ %h(zName)</a></li>
    }else{
      @ <li><span class="tagDsp">%h(zName)</span></li>
    }
  }
  @ </ul>
  db_finalize(&q);
  style_finish_page();
}

/*
** WEBPAGE: /tagtimeline
**
** Render a timeline with all check-ins that contain non-propagating
** symbolic tags.
779
780
781
782
783
784
785
786
787
  tmFlags = TIMELINE_XMERGE | TIMELINE_FILLGAPS | TIMELINE_NOSCROLL;
  if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH;
  if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR;
  if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR;
  www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0);
  db_finalize(&q);
  @ <br />
  style_finish_page("tagtimeline");
}







|

779
780
781
782
783
784
785
786
787
  tmFlags = TIMELINE_XMERGE | TIMELINE_FILLGAPS | TIMELINE_NOSCROLL;
  if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH;
  if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR;
  if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR;
  www_print_timeline(&q, tmFlags, 0, 0, 0, 0, 0, 0);
  db_finalize(&q);
  @ <br />
  style_finish_page();
}

Changes to src/tar.c.

783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
    if( zInclude ){
      @ zInclude = "%h(zInclude)"<br />
    }
    if( zExclude ){
      @ zExclude = "%h(zExclude)"<br />
    }
    @ zKey = "%h(zKey)"
    style_finish_page("tarball");
    return;
  }
  if( referred_from_login() ){
    style_header("Tarball Download");
    @ <form action='%R/tarball/%h(zName).tar.gz'>
    cgi_query_parameters_to_hidden();
    @ <p>Tarball named <b>%h(zName).tar.gz</b> holding the content
    @ of check-in <b>%h(zRid)</b>:
    @ <input type="submit" value="Download" />
    @ </form>
    style_finish_page("tarball");
    return;
  }
  blob_zero(&tarball);
  if( cache_read(&tarball, zKey)==0 ){
    tarball_of_checkin(rid, &tarball, zName, pInclude, pExclude);
    cache_write(&tarball, zKey);
  }







|










|







783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
    if( zInclude ){
      @ zInclude = "%h(zInclude)"<br />
    }
    if( zExclude ){
      @ zExclude = "%h(zExclude)"<br />
    }
    @ zKey = "%h(zKey)"
    style_finish_page();
    return;
  }
  if( referred_from_login() ){
    style_header("Tarball Download");
    @ <form action='%R/tarball/%h(zName).tar.gz'>
    cgi_query_parameters_to_hidden();
    @ <p>Tarball named <b>%h(zName).tar.gz</b> holding the content
    @ of check-in <b>%h(zRid)</b>:
    @ <input type="submit" value="Download" />
    @ </form>
    style_finish_page();
    return;
  }
  blob_zero(&tarball);
  if( cache_read(&tarball, zKey)==0 ){
    tarball_of_checkin(rid, &tarball, zName, pInclude, pExclude);
    cache_write(&tarball, zKey);
  }

Changes to src/terminal.c.

115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
  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);







|







115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
  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 characters 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/th_main.c.

1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
  const char **argv,
  int *argl
){
  if( argc!=1 ){
    return Th_WrongNumArgs(interp, "styleFooter");
  }
  if( Th_IsRepositoryOpen() ){
    style_finish_page("th1");    /* TODO: add optional parameter to pass along? */
    Th_SetResult(interp, 0, 0);
    return TH_OK;
  }else{
    Th_SetResult(interp, "repository unavailable", -1);
    return TH_ERROR;
  }
}







|







1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
  const char **argv,
  int *argl
){
  if( argc!=1 ){
    return Th_WrongNumArgs(interp, "styleFooter");
  }
  if( Th_IsRepositoryOpen() ){
    style_finish_page();
    Th_SetResult(interp, 0, 0);
    return TH_OK;
  }else{
    Th_SetResult(interp, "repository unavailable", -1);
    return TH_ERROR;
  }
}
2930
2931
2932
2933
2934
2935
2936


2937
2938
2939
2940
2941
2942
2943
2944
2945

2946
2947
2948
2949
2950
2951
2952
**
**     --cgi                Include a CGI response header in the output
**     --http               Include an HTTP response header in the output
**     --open-config        Open the configuration database
**     --set-anon-caps      Set anonymous login capabilities
**     --set-user-caps      Set user login capabilities
**     --th-trace           Trace TH1 execution (for debugging purposes)


*/
void test_th_source(void){
  int rc;
  const char *zRc;
  int forceCgi, fullHttpReply;
  Blob in;
  Th_InitTraceLog();
  forceCgi = find_option("cgi", 0, 0)!=0;
  fullHttpReply = find_option("http", 0, 0)!=0;

  if( fullHttpReply ) forceCgi = 1;
  if( forceCgi ) Th_ForceCgi(fullHttpReply);
  if( find_option("open-config", 0, 0)!=0 ){
    Th_OpenConfig(1);
  }
  if( find_option("set-anon-caps", 0, 0)!=0 ){
    const char *zCap = fossil_getenv("TH1_TEST_ANON_CAPS");







>
>




|




>







2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
**
**     --cgi                Include a CGI response header in the output
**     --http               Include an HTTP response header in the output
**     --open-config        Open the configuration database
**     --set-anon-caps      Set anonymous login capabilities
**     --set-user-caps      Set user login capabilities
**     --th-trace           Trace TH1 execution (for debugging purposes)
**     --no-print-result    Do not output the final result. Use if it
**                          interferes with script output.
*/
void test_th_source(void){
  int rc;
  const char *zRc;
  int forceCgi, fullHttpReply, fNoPrintRc;
  Blob in;
  Th_InitTraceLog();
  forceCgi = find_option("cgi", 0, 0)!=0;
  fullHttpReply = find_option("http", 0, 0)!=0;
  fNoPrintRc = find_option("no-print-result",0,0)!=0;
  if( fullHttpReply ) forceCgi = 1;
  if( forceCgi ) Th_ForceCgi(fullHttpReply);
  if( find_option("open-config", 0, 0)!=0 ){
    Th_OpenConfig(1);
  }
  if( find_option("set-anon-caps", 0, 0)!=0 ){
    const char *zCap = fossil_getenv("TH1_TEST_ANON_CAPS");
2963
2964
2965
2966
2967
2968
2969

2970


2971
2972
2973
2974
2975
2976
2977
    usage("file");
  }
  blob_zero(&in);
  blob_read_from_file(&in, g.argv[2], ExtFILE);
  Th_FossilInit(TH_INIT_DEFAULT);
  rc = Th_Eval(g.interp, 0, blob_str(&in), -1);
  zRc = Th_ReturnCodeName(rc, 1);

  fossil_print("%s%s%s\n", zRc, zRc ? ": " : "", Th_GetResult(g.interp, 0));


  Th_PrintTraceLog();
  if( forceCgi ) cgi_reply();
}

#ifdef FOSSIL_ENABLE_TH1_HOOKS
/*
** COMMAND: test-th-hook







>
|
>
>







2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
    usage("file");
  }
  blob_zero(&in);
  blob_read_from_file(&in, g.argv[2], ExtFILE);
  Th_FossilInit(TH_INIT_DEFAULT);
  rc = Th_Eval(g.interp, 0, blob_str(&in), -1);
  zRc = Th_ReturnCodeName(rc, 1);
  if(0==fNoPrintRc){
    fossil_print("%s%s%s\n", zRc, zRc ? ": " : "",
                 Th_GetResult(g.interp, 0));
  }
  Th_PrintTraceLog();
  if( forceCgi ) cgi_reply();
}

#ifdef FOSSIL_ENABLE_TH1_HOOKS
/*
** COMMAND: test-th-hook

Changes to src/timeline.c.

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
#define TIMELINE_FORUMTXT 0x4000000 /* Render all forum messages */
#define TIMELINE_REFS     0x8000000 /* Output intended for References tab */
#define TIMELINE_DELTA   0x10000000 /* Background color shows delta manifests */
#endif

/*
** Hash a string and use the hash to determine a background color.



*/
char *hash_color(const char *z){
  int i;                       /* Loop counter */
  unsigned int h = 0;          /* Hash on the branch name */
  int r, g, b;                 /* Values for red, green, and blue */
  int h1, h2, h3, h4;          /* Elements of the hash value */
  int mx, mn;                  /* Components of HSV */
  static char zColor[10];      /* The resulting color */
  static int ix[2] = {0,0};    /* Color chooser parameters */

  if( ix[0]==0 ){
    if( skin_detail_boolean("white-foreground") ){
      ix[0] = 140;
      ix[1] = 40;
    }else{
      ix[0] = 216;
      ix[1] = 16;
    }
  }
  for(i=0; z[i]; i++ ){
    h = (h<<11) ^ (h<<1) ^ (h>>3) ^ z[i];
  }
  h1 = h % 6;  h /= 6;
  h3 = h % 30; h /= 30;
  h4 = h % 40; h /= 40;
  mx = ix[0] - h3;
  mn = mx - h4 - ix[1];
  h2 = (h%(mx - mn)) + mn;
  switch( h1 ){
    case 0:  r = mx; g = h2, b = mn;  break;
    case 1:  r = h2; g = mx, b = mn;  break;
    case 2:  r = mn; g = mx, b = h2;  break;







>
>
>












|
|

|
|






|
|







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
#define TIMELINE_FORUMTXT 0x4000000 /* Render all forum messages */
#define TIMELINE_REFS     0x8000000 /* Output intended for References tab */
#define TIMELINE_DELTA   0x10000000 /* Background color shows delta manifests */
#endif

/*
** Hash a string and use the hash to determine a background color.
**
** This value returned is in static space and is overwritten with
** each subsequent call.
*/
char *hash_color(const char *z){
  int i;                       /* Loop counter */
  unsigned int h = 0;          /* Hash on the branch name */
  int r, g, b;                 /* Values for red, green, and blue */
  int h1, h2, h3, h4;          /* Elements of the hash value */
  int mx, mn;                  /* Components of HSV */
  static char zColor[10];      /* The resulting color */
  static int ix[2] = {0,0};    /* Color chooser parameters */

  if( ix[0]==0 ){
    if( skin_detail_boolean("white-foreground") ){
      ix[0] = 0x50;
      ix[1] = 0x20;
    }else{
      ix[0] = 0xf8;
      ix[1] = 0x20;
    }
  }
  for(i=0; z[i]; i++ ){
    h = (h<<11) ^ (h<<1) ^ (h>>3) ^ z[i];
  }
  h1 = h % 6;  h /= 6;
  h3 = h % 10; h /= 10;
  h4 = h % 10; h /= 10;
  mx = ix[0] - h3;
  mn = mx - h4 - ix[1];
  h2 = (h%(mx - mn)) + mn;
  switch( h1 ){
    case 0:  r = mx; g = h2, b = mn;  break;
    case 1:  r = h2; g = mx, b = mn;  break;
    case 2:  r = mn; g = mx, b = h2;  break;
187
188
189
190
191
192
193

194
195
196
197
198
199
200
*/
void test_hash_color_page(void){
  const char *zBr;
  char zNm[10];
  int i, cnt;
  login_check_credentials();


  style_header("Hash Color Test");
  for(i=cnt=0; i<10; i++){
    sqlite3_snprintf(sizeof(zNm),zNm,"b%d",i);
    zBr = P(zNm);
    if( zBr && zBr[0] ){
      @ <p style='border:1px solid;background-color:%s(hash_color(zBr));'>
      @ %h(zBr) - %s(hash_color(zBr)) -







>







190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
*/
void test_hash_color_page(void){
  const char *zBr;
  char zNm[10];
  int i, cnt;
  login_check_credentials();

  style_set_current_feature("test");
  style_header("Hash Color Test");
  for(i=cnt=0; i<10; i++){
    sqlite3_snprintf(sizeof(zNm),zNm,"b%d",i);
    zBr = P(zNm);
    if( zBr && zBr[0] ){
      @ <p style='border:1px solid;background-color:%s(hash_color(zBr));'>
      @ %h(zBr) - %s(hash_color(zBr)) -
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
  for(i=0; i<10; i++){
    sqlite3_snprintf(sizeof(zNm),zNm,"b%d",i);
    zBr = P(zNm);
    @ <input type="text" size="30" name='%s(zNm)' value='%h(PD(zNm,""))'><br />
  }
  @ <input type="submit">
  @ </form>
  style_finish_page("test");
}

/*
** Return a new timelineTable id.
*/
int timeline_tableid(void){
  static int id = 0;







|







216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
  for(i=0; i<10; i++){
    sqlite3_snprintf(sizeof(zNm),zNm,"b%d",i);
    zBr = P(zNm);
    @ <input type="text" size="30" name='%s(zNm)' value='%h(PD(zNm,""))'><br />
  }
  @ <input type="submit">
  @ </form>
  style_finish_page();
}

/*
** Return a new timelineTable id.
*/
int timeline_tableid(void){
  static int id = 0;
540
541
542
543
544
545
546
547



548

549
550
551
552
553
554
555
    }
    @</td>
    if( !isSelectedOrCurrent ){
      @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)" id='mc%d(gidx)'>
    }else{
      @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)">
    }
    if( pGraph && zType[0]!='c' ){



      @ &bull;

    }
    if( modPending ){
      @ <span class="modpending">(Awaiting Moderator Approval)</span>
    }
    if( (tmFlags & TIMELINE_BISECT)!=0 && zType[0]=='c' ){
      static Stmt bisectQuery;
      db_static_prepare(&bisectQuery,







|
>
>
>
|
>







544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
    }
    @</td>
    if( !isSelectedOrCurrent ){
      @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)" id='mc%d(gidx)'>
    }else{
      @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)">
    }
    if( pGraph ){
      if( zType[0]=='e' ){
        @ <b>Note:</b>
      }else if( zType[0]!='c' ){
        @ &bull;
      }
    }
    if( modPending ){
      @ <span class="modpending">(Awaiting Moderator Approval)</span>
    }
    if( (tmFlags & TIMELINE_BISECT)!=0 && zType[0]=='c' ){
      static Stmt bisectQuery;
      db_static_prepare(&bisectQuery,
1626
1627
1628
1629
1630
1631
1632


1633
1634
1635
1636





1637
1638
1639
1640
1641
1642
1643
**    a=TIMEORTAG     Show events after TIMEORTAG
**    b=TIMEORTAG     Show events before TIMEORTAG
**    c=TIMEORTAG     Show events that happen "circa" TIMEORTAG
**    cf=FILEHASH     Show events around the time of the first use of
**                    the file with FILEHASH
**    m=TIMEORTAG     Highlight the event at TIMEORTAG
**    n=COUNT         Maximum number of events. "all" for no limit


**    p=CHECKIN       Parents and ancestors of CHECKIN
**                       bt=PRIOR   ... going back to PRIOR
**    d=CHECKIN       Children and descendants of CHECKIN
**    dp=CHECKIN      The same as 'd=CHECKIN&p=CHECKIN'





**    t=TAG           Show only check-ins with the given TAG
**    r=TAG           Show check-ins related to TAG, equivalent to t=TAG&rel
**    rel             Show related check-ins as well as those matching t=TAG
**    mionly          Limit rel to show ancestors but not descendants
**    nowiki          Do not show wiki associated with branch or tag
**    ms=MATCHSTYLE   Set tag match style to EXACT, GLOB, LIKE, REGEXP
**    u=USER          Only show items associated with USER







>
>



|
>
>
>
>
>







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
**    a=TIMEORTAG     Show events after TIMEORTAG
**    b=TIMEORTAG     Show events before TIMEORTAG
**    c=TIMEORTAG     Show events that happen "circa" TIMEORTAG
**    cf=FILEHASH     Show events around the time of the first use of
**                    the file with FILEHASH
**    m=TIMEORTAG     Highlight the event at TIMEORTAG
**    n=COUNT         Maximum number of events. "all" for no limit
**    n1=COUNT        Same as "n" but doesn't set the display-preference cookie
**                       Use "n1=COUNT" for a one-time display change
**    p=CHECKIN       Parents and ancestors of CHECKIN
**                       bt=PRIOR   ... going back to PRIOR
**    d=CHECKIN       Children and descendants of CHECKIN
**    dp=CHECKIN      Same as 'd=CHECKIN&p=CHECKIN'
**    df=CHECKIN      Same as 'd=CHECKIN&n1=all&nd'.  Mnemonic: "Derived From"
**    bt=CHECKIN      In conjunction with p=CX, this means show all
**                       ancestors of CX going back to the time of CHECKIN.
**                       All qualifying check-ins are shown unless there
**                       is also an n= or n1= query parameter.
**    t=TAG           Show only check-ins with the given TAG
**    r=TAG           Show check-ins related to TAG, equivalent to t=TAG&rel
**    rel             Show related check-ins as well as those matching t=TAG
**    mionly          Limit rel to show ancestors but not descendants
**    nowiki          Do not show wiki associated with branch or tag
**    ms=MATCHSTYLE   Set tag match style to EXACT, GLOB, LIKE, REGEXP
**    u=USER          Only show items associated with USER
1653
1654
1655
1656
1657
1658
1659


1660
1661
1662
1663
1664
1665
1666
**    vfx             Show complete text of forum messages
**    f=CHECKIN       Show family (immediate parents and children) of CHECKIN
**    from=CHECKIN    Path from...
**                       to=CHECKIN      ... to this
**                       shortest        ... show only the shortest path
**                       rel             ... also show related checkins
**    uf=FILE_HASH    Show only check-ins that contain the given file version


**    chng=GLOBLIST   Show only check-ins that involve changes to a file whose
**                    name matches one of the comma-separate GLOBLIST
**    brbg            Background color determined by branch name
**    ubg             Background color determined by user
**    deltabg         Background color red for delta manifests or green
**                    for baseline manifests
**    namechng        Show only check-ins that have filename changes







>
>







1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
**    vfx             Show complete text of forum messages
**    f=CHECKIN       Show family (immediate parents and children) of CHECKIN
**    from=CHECKIN    Path from...
**                       to=CHECKIN      ... to this
**                       shortest        ... show only the shortest path
**                       rel             ... also show related checkins
**    uf=FILE_HASH    Show only check-ins that contain the given file version
**                       All qualifying check-ins are shown unless there is
**                       also an n= or n1= query parameter.
**    chng=GLOBLIST   Show only check-ins that involve changes to a file whose
**                    name matches one of the comma-separate GLOBLIST
**    brbg            Background color determined by branch name
**    ubg             Background color determined by user
**    deltabg         Background color red for delta manifests or green
**                    for baseline manifests
**    namechng        Show only check-ins that have filename changes
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
** name of a branch.
*/
void page_timeline(void){
  Stmt q;                            /* Query used to generate the timeline */
  Blob sql;                          /* text of SQL used to generate timeline */
  Blob desc;                         /* Description of the timeline */
  int nEntry;                        /* Max number of entries on timeline */
  int p_rid = name_to_typed_rid(P("p"),"ci");  /* artifact p and its parents */
  int d_rid = name_to_typed_rid(P("d"),"ci");  /* artifact d and descendants */
  int f_rid = name_to_typed_rid(P("f"),"ci");  /* artifact f and close family */
  const char *zUser = P("u");        /* All entries by this user if not NULL */
  const char *zType;                 /* Type of events to display */
  const char *zAfter = P("a");       /* Events after this time */
  const char *zBefore = P("b");      /* Events before this time */
  const char *zCirca = P("c");       /* Events near this time */
  const char *zMark = P("m");        /* Mark this event or an event this time */
  const char *zTagName = P("t");     /* Show events with this tag */







|
|
|







1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
** name of a branch.
*/
void page_timeline(void){
  Stmt q;                            /* Query used to generate the timeline */
  Blob sql;                          /* text of SQL used to generate timeline */
  Blob desc;                         /* Description of the timeline */
  int nEntry;                        /* Max number of entries on timeline */
  int p_rid;                         /* artifact p and its parents */
  int d_rid;                         /* artifact d and descendants */
  int f_rid;                         /* artifact f and close family */
  const char *zUser = P("u");        /* All entries by this user if not NULL */
  const char *zType;                 /* Type of events to display */
  const char *zAfter = P("a");       /* Events after this time */
  const char *zBefore = P("b");      /* Events before this time */
  const char *zCirca = P("c");       /* Events near this time */
  const char *zMark = P("m");        /* Mark this event or an event this time */
  const char *zTagName = P("t");     /* Show events with this tag */
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
  int advancedMenu = 0;               /* Use the advanced menu design */
  char *zPlural;                      /* Ending for plural forms */
  int showCherrypicks = 1;            /* True to show cherrypick merges */
  int haveParameterN;                 /* True if n= query parameter present */

  url_initialize(&url, "timeline");
  cgi_query_parameters_to_url(&url);


  /* Set number of rows to display */


  haveParameterN = P("n")!=0;




  cookie_read_parameter("n","n");
  z = P("n");

  if( z==0 ) z = db_get("timeline-default-length",0);









  if( z ){
    if( fossil_strcmp(z,"all")==0 ){
      nEntry = 0;
    }else{
      nEntry = atoi(z);
      if( nEntry<=0 ){
        z = "10";
        nEntry = 10;
      }
    }
  }else{




    z = "50";







    nEntry = 50;


  }

  /* Undocumented query parameter to set JS mode */
  builtin_set_js_delivery_mode(P("jsmode"),1);

  secondaryRid = name_to_typed_rid(P("sel2"),"ci");
  selectedRid = name_to_typed_rid(P("sel1"),"ci");
  cgi_replace_query_parameter("n",z);
  cookie_write_parameter("n","n",0);
  tmFlags |= timeline_ss_submenu();
  cookie_link_parameter("advm","advm","0");
  advancedMenu = atoi(PD("advm","0"));

  /* Omit all cherry-pick merge lines if the "ncp" query parameter is
  ** present or if this repository lacks a "cherrypick" table. */
  if( PB("ncp") || !db_table_exists("repository","cherrypick") ){








>

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











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







<
<







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
  int advancedMenu = 0;               /* Use the advanced menu design */
  char *zPlural;                      /* Ending for plural forms */
  int showCherrypicks = 1;            /* True to show cherrypick merges */
  int haveParameterN;                 /* True if n= query parameter present */

  url_initialize(&url, "timeline");
  cgi_query_parameters_to_url(&url);


  /* Set number of rows to display */
  z = P("n");
  if( z!=0 ){
    haveParameterN = 1;
    cookie_write_parameter("n","n",0);
  }else{
    const char *z2;
    haveParameterN = 0;
    cookie_read_parameter("n","n");
    z = P("n");
    if( z==0 ){
      z = db_get("timeline-default-length",0);
    }
    cgi_replace_query_parameter("n",fossil_strdup(z));
    cookie_write_parameter("n","n",0);
    z2 = P("n1");
    if( z2 ){
      haveParameterN = 2;
      z = z2;
    }
  }
  if( z ){
    if( fossil_strcmp(z,"all")==0 ){
      nEntry = 0;
    }else{
      nEntry = atoi(z);
      if( nEntry<=0 ){
        z = "10";
        nEntry = 10;
      }
    }
  }else{
    nEntry = 50;
  }

  /* Query parameters d=, p=, and f= and variants */
  z = P("p");
  p_rid = z ? name_to_typed_rid(z,"ci") : 0;
  z = P("d");
  d_rid = z ? name_to_typed_rid(z,"ci") : 0;
  z = P("f");
  f_rid = z ? name_to_typed_rid(z,"ci") : 0;
  z = P("df");
  if( z && (d_rid = name_to_typed_rid(z,"ci"))!=0 ){
    nEntry = 0;
    useDividers = 0;
    cgi_replace_query_parameter("d",fossil_strdup(z));
  }

  /* Undocumented query parameter to set JS mode */
  builtin_set_js_delivery_mode(P("jsmode"),1);

  secondaryRid = name_to_typed_rid(P("sel2"),"ci");
  selectedRid = name_to_typed_rid(P("sel1"),"ci");


  tmFlags |= timeline_ss_submenu();
  cookie_link_parameter("advm","advm","0");
  advancedMenu = atoi(PD("advm","0"));

  /* Omit all cherry-pick merge lines if the "ncp" query parameter is
  ** present or if this repository lacks a "cherrypick" table. */
  if( PB("ncp") || !db_table_exists("repository","cherrypick") ){
1911
1912
1913
1914
1915
1916
1917

1918
1919
1920
1921
1922
1923
1924
    int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses);
    if( ufid ){
      zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid);
      db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)");
      compute_uses_file("usesfile", ufid, 0);
      zType = "ci";
      disableY = 1;

    }else{
      zUses = 0;
    }
  }
  if( renameOnly ){
    db_multi_exec(
      "CREATE TEMP TABLE rnfile(rid INTEGER PRIMARY KEY);"







>







1956
1957
1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968
1969
1970
    int ufid = db_int(0, "SELECT rid FROM blob WHERE uuid GLOB '%q*'", zUses);
    if( ufid ){
      zUses = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ufid);
      db_multi_exec("CREATE TEMP TABLE usesfile(rid INTEGER PRIMARY KEY)");
      compute_uses_file("usesfile", ufid, 0);
      zType = "ci";
      disableY = 1;
      if( !haveParameterN ) nEntry = 0;
    }else{
      zUses = 0;
    }
  }
  if( renameOnly ){
    db_multi_exec(
      "CREATE TEMP TABLE rnfile(rid INTEGER PRIMARY KEY);"
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
    int np = 0, nd;
    const char *zBackTo = 0;
    int ridBackTo = 0;

    tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
    if( p_rid && d_rid ){
      if( p_rid!=d_rid ) p_rid = d_rid;
      if( P("n")==0 ) nEntry = 10;
    }
    db_multi_exec(
       "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
    );
    zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d",
                         p_rid ? p_rid : d_rid);
    zCiName = pd_rid ? P("pd") : p_rid ? P("p") : P("d");







|







2149
2150
2151
2152
2153
2154
2155
2156
2157
2158
2159
2160
2161
2162
2163
    int np = 0, nd;
    const char *zBackTo = 0;
    int ridBackTo = 0;

    tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
    if( p_rid && d_rid ){
      if( p_rid!=d_rid ) p_rid = d_rid;
      if( !haveParameterN ) nEntry = 10;
    }
    db_multi_exec(
       "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
    );
    zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d",
                         p_rid ? p_rid : d_rid);
    zCiName = pd_rid ? P("pd") : p_rid ? P("p") : P("d");
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
2140
2141
      }
      if( useDividers ) selectedRid = d_rid;
      db_multi_exec("DELETE FROM ok");
    }
    if( p_rid ){
      zBackTo = P("bt");
      ridBackTo = zBackTo ? name_to_typed_rid(zBackTo,"ci") : 0;
      if( !haveParameterN ) nEntry = 0;
      compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo);
      np = db_int(0, "SELECT count(*)-1 FROM ok");
      if( np>0 || nd==0 ){
        if( nd>0 ) blob_appendf(&desc, " and ");
        blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s");
        db_multi_exec("%s", blob_sql_text(&sql));
      }







|







2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
      }
      if( useDividers ) selectedRid = d_rid;
      db_multi_exec("DELETE FROM ok");
    }
    if( p_rid ){
      zBackTo = P("bt");
      ridBackTo = zBackTo ? name_to_typed_rid(zBackTo,"ci") : 0;
      if( ridBackTo && !haveParameterN ) nEntry = 0;
      compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0, ridBackTo);
      np = db_int(0, "SELECT count(*)-1 FROM ok");
      if( np>0 || nd==0 ){
        if( nd>0 ) blob_appendf(&desc, " and ");
        blob_appendf(&desc, "%d ancestor%s", np, (1==np)?"":"s");
        db_multi_exec("%s", blob_sql_text(&sql));
      }
2723
2724
2725
2726
2727
2728
2729
2730































































































2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755



2756
2757
2758
2759
2760
2761
2762
2763
2764
                     selectedRid, secondaryRid, 0);
  db_finalize(&q);
  if( zOlderButton ){
    @ %z(chref("button","%s",zOlderButton))%h(zOlderButtonLabel)\
    @ &nbsp;&darr;</a>
  }
  document_emit_js(/*handles pikchrs rendered above*/);
  style_finish_page("timeline");































































































}

/*
** The input query q selects various records.  Print a human-readable
** summary of those records.
**
** Limit number of lines or entries printed to nLimit.  If nLimit is zero
** there is no limit.  If nLimit is greater than zero, limit the number of
** complete entries printed.  If nLimit is less than zero, attempt to limit
** the number of lines printed (this is basically the legacy behavior).
** The line limit, if used, is approximate because it is only checked on a
** per-entry basis.  If verbose mode, the file name details are considered
** to be part of the entry.
**
** The query should return these columns:
**
**    0.  rid
**    1.  uuid
**    2.  Date/Time
**    3.  Comment string and user
**    4.  Number of non-merge children
**    5.  Number of parents
**    6.  mtime
**    7.  branch
**    8.  event-type: 'ci', 'w', 't', 'f', and so forth.



*/
void print_timeline(Stmt *q, int nLimit, int width, int verboseFlag){
  int nAbsLimit = (nLimit >= 0) ? nLimit : -nLimit;
  int nLine = 0;
  int nEntry = 0;
  char zPrevDate[20];
  const char *zCurrentUuid = 0;
  int fchngQueryInit = 0;     /* True if fchngQuery is initialized */
  Stmt fchngQuery;            /* Query for file changes on check-ins */







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



















|





>
>
>

|







2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
                     selectedRid, secondaryRid, 0);
  db_finalize(&q);
  if( zOlderButton ){
    @ %z(chref("button","%s",zOlderButton))%h(zOlderButtonLabel)\
    @ &nbsp;&darr;</a>
  }
  document_emit_js(/*handles pikchrs rendered above*/);
  style_finish_page();
}

/*
** Translate a timeline entry into the printable format by
** converting every %-substitutions as follows:
**
**     %n  newline
**     %%  a raw %
**     %H  commit hash
**     %h  abbreviated commit hash
**     %a  author name
**     %d  date
**     %c  comment (\n, \t replaced by space, \r deleted)
**     %b  branch
**     %t  tags
**     %p  phase (zero or more of: *CURRENT*, *MERGE*, *FORK*,
**                                 *UNPUBLISHED*, *LEAF*, *BRANCH*)
**
** The returned string is obtained from fossil_malloc() and should
** be freed by the caller.
*/
static char *timeline_entry_subst(
  const char *zFormat,
  int *nLine,
  const char *zId,
  const char *zDate,
  const char *zUser,
  const char *zCom,
  const char *zBranch,
  const char *zTags,
  const char *zPhase
){
  Blob r, co;
  int i, j;
  blob_init(&r, 0, 0);
  blob_init(&co, 0, 0);

  /* Replace LF and tab with space, delete CR */
  while( zCom[0] ){
    for(j=0; zCom[j] && zCom[j]!='\r' && zCom[j]!='\n' && zCom[j]!='\t'; j++){}
    blob_append(&co, zCom, j);
    if( zCom[j]==0 ) break;
    if( zCom[j]!='\r')
      blob_append(&co, " ", 1);
    zCom += j+1;
  }
  blob_str(&co);

  *nLine = 1;
  while( zFormat[0] ){
    for(i=0; zFormat[i] && zFormat[i]!='%'; i++){}
    blob_append(&r, zFormat, i);
    if( zFormat[i]==0 ) break;
    if( zFormat[i+1]=='%' ){
      blob_append(&r, "%", 1);
      zFormat += i+2;
    }else if( zFormat[i+1]=='n' ){
      blob_append(&r, "\n", 1);
      *nLine += 1;
      zFormat += i+2;
    }else if( zFormat[i+1]=='H' ){
      blob_append(&r, zId, -1);
      zFormat += i+2;
    }else if( zFormat[i+1]=='h' ){
      char *zFree = 0;
      zFree = mprintf("%S", zId);
      blob_append(&r, zFree, -1);
      fossil_free(zFree);
      zFormat += i+2;
    }else if( zFormat[i+1]=='d' ){
      blob_append(&r, zDate, -1);
      zFormat += i+2;
    }else if( zFormat[i+1]=='a' ){
      blob_append(&r, zUser, -1);
      zFormat += i+2;
    }else if( zFormat[i+1]=='c' ){
      blob_append(&r, co.aData, -1);
      zFormat += i+2;
    }else if( zFormat[i+1]=='b' ){
      if( zBranch ) blob_append(&r, zBranch, -1);
      zFormat += i+2;
    }else if( zFormat[i+1]=='t' ){
      blob_append(&r, zTags, -1);
      zFormat += i+2;
    }else if( zFormat[i+1]=='p' ){
      blob_append(&r, zPhase, -1);
      zFormat += i+2;
    }else{
      blob_append(&r, zFormat+i, 1);
      zFormat += i+1;
    }
  }
  fossil_free(co.aData);
  blob_str(&r);
  return r.aData;
}

/*
** The input query q selects various records.  Print a human-readable
** summary of those records.
**
** Limit number of lines or entries printed to nLimit.  If nLimit is zero
** there is no limit.  If nLimit is greater than zero, limit the number of
** complete entries printed.  If nLimit is less than zero, attempt to limit
** the number of lines printed (this is basically the legacy behavior).
** The line limit, if used, is approximate because it is only checked on a
** per-entry basis.  If verbose mode, the file name details are considered
** to be part of the entry.
**
** The query should return these columns:
**
**    0.  rid
**    1.  uuid
**    2.  Date/Time
**    3.  Comment string, user, and tags
**    4.  Number of non-merge children
**    5.  Number of parents
**    6.  mtime
**    7.  branch
**    8.  event-type: 'ci', 'w', 't', 'f', and so forth.
**    9.  comment
**   10.  user
**   11.  tags
*/
void print_timeline(Stmt *q, int nLimit, int width, const char *zFormat, int verboseFlag){
  int nAbsLimit = (nLimit >= 0) ? nLimit : -nLimit;
  int nLine = 0;
  int nEntry = 0;
  char zPrevDate[20];
  const char *zCurrentUuid = 0;
  int fchngQueryInit = 0;     /* True if fchngQuery is initialized */
  Stmt fchngQuery;            /* Query for file changes on check-ins */
2773
2774
2775
2776
2777
2778
2779

2780



2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799

2800
2801
2802
2803
2804
2805
2806
2807
  while( (rc=db_step(q))==SQLITE_ROW ){
    int rid = db_column_int(q, 0);
    const char *zId = db_column_text(q, 1);
    const char *zDate = db_column_text(q, 2);
    const char *zCom = db_column_text(q, 3);
    int nChild = db_column_int(q, 4);
    int nParent = db_column_int(q, 5);

    const char *zType = db_column_text(q, 8);



    char *zFree = 0;
    int n = 0;
    char zPrefix[80];

    if( nAbsLimit!=0 ){
      if( nLimit<0 && nLine>=nAbsLimit ){
        fossil_print("--- line limit (%d) reached ---\n", nAbsLimit);
        break; /* line count limit hit, stop. */
      }else if( nEntry>=nAbsLimit ){
        fossil_print("--- entry limit (%d) reached ---\n", nAbsLimit);
        break; /* entry count limit hit, stop. */
      }
    }
    if( fossil_strnicmp(zDate, zPrevDate, 10) ){
      fossil_print("=== %.10s ===\n", zDate);
      memcpy(zPrevDate, zDate, 10);
      nLine++; /* record another line */
    }
    if( zCom==0 ) zCom = "";

    fossil_print("%.8s ", &zDate[11]);
    zPrefix[0] = 0;
    if( nParent>1 ){
      sqlite3_snprintf(sizeof(zPrefix), zPrefix, "*MERGE* ");
      n = strlen(zPrefix);
    }
    if( nChild>1 ){
      const char *zBrType;







>

>
>
>













|





>
|







2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
  while( (rc=db_step(q))==SQLITE_ROW ){
    int rid = db_column_int(q, 0);
    const char *zId = db_column_text(q, 1);
    const char *zDate = db_column_text(q, 2);
    const char *zCom = db_column_text(q, 3);
    int nChild = db_column_int(q, 4);
    int nParent = db_column_int(q, 5);
    const char *zBranch = db_column_text(q, 7);
    const char *zType = db_column_text(q, 8);
    const char *zComShort = db_column_text(q, 9);
    const char *zUserShort = db_column_text(q, 10);
    const char *zTags = db_column_text(q, 11);
    char *zFree = 0;
    int n = 0;
    char zPrefix[80];

    if( nAbsLimit!=0 ){
      if( nLimit<0 && nLine>=nAbsLimit ){
        fossil_print("--- line limit (%d) reached ---\n", nAbsLimit);
        break; /* line count limit hit, stop. */
      }else if( nEntry>=nAbsLimit ){
        fossil_print("--- entry limit (%d) reached ---\n", nAbsLimit);
        break; /* entry count limit hit, stop. */
      }
    }
    if( zFormat == 0 && fossil_strnicmp(zDate, zPrevDate, 10) ){
      fossil_print("=== %.10s ===\n", zDate);
      memcpy(zPrevDate, zDate, 10);
      nLine++; /* record another line */
    }
    if( zCom==0 ) zCom = "";
    if( zFormat == 0 )
      fossil_print("%.8s ", &zDate[11]);
    zPrefix[0] = 0;
    if( nParent>1 ){
      sqlite3_snprintf(sizeof(zPrefix), zPrefix, "*MERGE* ");
      n = strlen(zPrefix);
    }
    if( nChild>1 ){
      const char *zBrType;
2829
2830
2831
2832
2833
2834
2835














2836
2837

2838
2839
2840
2841
2842
2843
2844
        zFree = mprintf("[%S] Delete wiki page \"%s\"", zId, zCom+1);
      }else{
        zFree = mprintf("[%S] Edit to wiki page \"%s\"", zId, zCom+1);
      }
    }else{
      zFree = mprintf("[%S] %s%s", zId, zPrefix, zCom);
    }














    /* record another X lines */
    nLine += comment_print(zFree, zCom, 9, width, get_comment_format());

    fossil_free(zFree);

    if(verboseFlag){
      if( !fchngQueryInit ){
        db_prepare(&fchngQuery,
           "SELECT (pid<=0) AS isnew,"
           "       (fid==0) AS isdel,"







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







2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
        zFree = mprintf("[%S] Delete wiki page \"%s\"", zId, zCom+1);
      }else{
        zFree = mprintf("[%S] Edit to wiki page \"%s\"", zId, zCom+1);
      }
    }else{
      zFree = mprintf("[%S] %s%s", zId, zPrefix, zCom);
    }

    if( zFormat ){
      char *zEntry;
      int nEntryLine = 0;
      if( nChild==0 ){
        sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*LEAF* ");
      }
      zEntry = timeline_entry_subst(zFormat, &nEntryLine, zId, zDate, zUserShort,
                                    zComShort, zBranch, zTags, zPrefix);
      nLine += nEntryLine;
      fossil_print("%s\n", zEntry);
      fossil_free(zEntry);
    }
    else{
      /* record another X lines */
      nLine += comment_print(zFree, zCom, 9, width, get_comment_format());
    }
    fossil_free(zFree);

    if(verboseFlag){
      if( !fchngQueryInit ){
        db_prepare(&fchngQuery,
           "SELECT (pid<=0) AS isnew,"
           "       (fid==0) AS isdel,"
2900
2901
2902
2903
2904
2905
2906







2907
2908
2909
2910
2911
2912
2913
    @     || ')' as comment,
    @   (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim)
    @        AS primPlinkCount,
    @   (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount,
    @   event.mtime AS mtime,
    @   tagxref.value AS branch,
    @   event.type







    @ FROM tag CROSS JOIN event CROSS JOIN blob
    @      LEFT JOIN tagxref ON tagxref.tagid=tag.tagid
    @   AND tagxref.tagtype>0
    @   AND tagxref.rid=blob.rid
    @ WHERE blob.rid=event.objid
    @   AND tag.tagname='branch'
  ;







>
>
>
>
>
>
>







3064
3065
3066
3067
3068
3069
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
    @     || ')' as comment,
    @   (SELECT count(*) FROM plink WHERE pid=blob.rid AND isprim)
    @        AS primPlinkCount,
    @   (SELECT count(*) FROM plink WHERE cid=blob.rid) AS plinkCount,
    @   event.mtime AS mtime,
    @   tagxref.value AS branch,
    @   event.type
    @   , coalesce(ecomment,comment) AS comment0
    @   , coalesce(euser,user,'?') AS user0
    @   , (SELECT case when length(x)>0 then x else '' end
    @         FROM (SELECT group_concat(substr(tagname,5), ', ') AS x
    @         FROM tag, tagxref
    @         WHERE tagname GLOB 'sym-*' AND tag.tagid=tagxref.tagid
    @          AND tagxref.rid=blob.rid AND tagxref.tagtype>0)) AS tags    
    @ FROM tag CROSS JOIN event CROSS JOIN blob
    @      LEFT JOIN tagxref ON tagxref.tagid=tag.tagid
    @   AND tagxref.tagtype>0
    @   AND tagxref.rid=blob.rid
    @ WHERE blob.rid=event.objid
    @   AND tag.tagname='branch'
  ;
2929
2930
2931
2932
2933
2934
2935

2936
2937
2938
2939
2940
2941
2942
/*
** Return true if the input string can be converted to a julianday.
*/
static int fossil_is_julianday(const char *zDate){
  return db_int(0, "SELECT EXISTS (SELECT julianday(%Q) AS jd"
                   " WHERE jd IS NOT NULL)", zDate);
}


/*
** COMMAND: timeline
**
** Usage: %fossil timeline ?WHEN? ?CHECKIN|DATETIME? ?OPTIONS?
**
** Print a summary of activity going backwards in date and time







>







3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
/*
** Return true if the input string can be converted to a julianday.
*/
static int fossil_is_julianday(const char *zDate){
  return db_int(0, "SELECT EXISTS (SELECT julianday(%Q) AS jd"
                   " WHERE jd IS NOT NULL)", zDate);
}


/*
** COMMAND: timeline
**
** Usage: %fossil timeline ?WHEN? ?CHECKIN|DATETIME? ?OPTIONS?
**
** Print a summary of activity going backwards in date and time
2966
2967
2968
2969
2970
2971
2972

2973
2974
2975
2976
2977
2978
2979
2980
2981

















2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000

3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012








3013
3014
3015
3016
3017
3018
3019
**   -p|--path PATH       Output items affecting PATH only.
**                        PATH can be a file or a sub directory.
**   --offset P           skip P changes
**   --sql                Show the SQL used to generate the timeline
**   -t|--type TYPE       Output items from the given types only, such as:
**                            ci = file commits only
**                            e  = technical notes only

**                            t  = tickets only
**                            w  = wiki commits only
**   -v|--verbose         Output the list of files changed by each commit
**                        and the type of each change (edited, deleted,
**                        etc.) after the check-in comment.
**   -W|--width N         Width of lines (default is to auto-detect). N must be
**                        either greater than 20 or it ust be zero 0 to
**                        indicate no limit, resulting in a single line per
**                        entry.

















**   -R REPO_FILE         Specifies the repository db to use. Default is
**                        the current checkout's repository.
*/
void timeline_cmd(void){
  Stmt q;
  int n, k, width;
  const char *zLimit;
  const char *zWidth;
  const char *zOffset;
  const char *zType;
  char *zOrigin;
  char *zDate;
  Blob sql;
  int objid = 0;
  Blob uuid;
  int mode = TIMELINE_MODE_NONE;
  int verboseFlag = 0 ;
  int iOffset;
  const char *zFilePattern = 0;

  Blob treeName;
  int showSql = 0;

  verboseFlag = find_option("verbose","v", 0)!=0;
  if( !verboseFlag){
    verboseFlag = find_option("showfiles","f", 0)!=0; /* deprecated */
  }
  db_find_and_open_repository(0, 0);
  zLimit = find_option("limit","n",1);
  zWidth = find_option("width","W",1);
  zType = find_option("type","t",1);
  zFilePattern = find_option("path","p",1);








  showSql = find_option("sql",0,0)!=0;

  if( !zLimit ){
    zLimit = find_option("count",0,1);
  }
  if( zLimit ){
    n = atoi(zLimit);







>









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



















>












>
>
>
>
>
>
>
>







3138
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194
3195
3196
3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3218
**   -p|--path PATH       Output items affecting PATH only.
**                        PATH can be a file or a sub directory.
**   --offset P           skip P changes
**   --sql                Show the SQL used to generate the timeline
**   -t|--type TYPE       Output items from the given types only, such as:
**                            ci = file commits only
**                            e  = technical notes only
**                            f  = forum posts only
**                            t  = tickets only
**                            w  = wiki commits only
**   -v|--verbose         Output the list of files changed by each commit
**                        and the type of each change (edited, deleted,
**                        etc.) after the check-in comment.
**   -W|--width N         Width of lines (default is to auto-detect). N must be
**                        either greater than 20 or it ust be zero 0 to
**                        indicate no limit, resulting in a single line per
**                        entry.
**   -F|--format          Entry format. Values "oneline", "medium", and "full"
**                        get mapped to the full options below. Otherwise a 
**                        string which can contain these placeholders:
**                            %n  newline
**                            %%  a raw %
**                            %H  commit hash
**                            %h  abbreviated commit hash
**                            %a  author name
**                            %d  date
**                            %c  comment (NL, TAB replaced by space, LF deleted)
**                            %b  branch
**                            %t  tags
**                            %p  phase: zero or more of *CURRENT*, *MERGE*,
**                                      *FORK*, *UNPUBLISHED*, *LEAF*, *BRANCH*
**   --oneline            Show only short hash and comment for each entry
**   --medium             Medium-verbose entry formatting
**   --full               Extra verbose entry formatting
**   -R REPO_FILE         Specifies the repository db to use. Default is
**                        the current checkout's repository.
*/
void timeline_cmd(void){
  Stmt q;
  int n, k, width;
  const char *zLimit;
  const char *zWidth;
  const char *zOffset;
  const char *zType;
  char *zOrigin;
  char *zDate;
  Blob sql;
  int objid = 0;
  Blob uuid;
  int mode = TIMELINE_MODE_NONE;
  int verboseFlag = 0 ;
  int iOffset;
  const char *zFilePattern = 0;
  const char *zFormat = 0;
  Blob treeName;
  int showSql = 0;

  verboseFlag = find_option("verbose","v", 0)!=0;
  if( !verboseFlag){
    verboseFlag = find_option("showfiles","f", 0)!=0; /* deprecated */
  }
  db_find_and_open_repository(0, 0);
  zLimit = find_option("limit","n",1);
  zWidth = find_option("width","W",1);
  zType = find_option("type","t",1);
  zFilePattern = find_option("path","p",1);
  zFormat = find_option("format","F",1);
  if( find_option("oneline",0,0)!= 0 || fossil_strcmp(zFormat,"oneline")==0 )
    zFormat = "%h %c";
  if( find_option("medium",0,0)!= 0 || fossil_strcmp(zFormat,"medium")==0 )
    zFormat = "Commit:   %h%nDate:     %d%nAuthor:   %a%nComment:  %c%n";
  if( find_option("full",0,0)!= 0 || fossil_strcmp(zFormat,"full")==0 )
    zFormat = "Commit:   %H%nDate:     %d%nAuthor:   %a%nComment:  %c%n"
              "Branch:   %b%nTags:     %t%nPhase:    %p%n";
  showSql = find_option("sql",0,0)!=0;

  if( !zLimit ){
    zLimit = find_option("count",0,1);
  }
  if( zLimit ){
    n = atoi(zLimit);
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
    blob_append_sql(&sql, "\n LIMIT -1 OFFSET %d", iOffset);
  }
  if( showSql ){
    fossil_print("%s\n", blob_str(&sql));
  }
  db_prepare_blob(&q, &sql);
  blob_reset(&sql);
  print_timeline(&q, n, width, verboseFlag);
  db_finalize(&q);
}

/*
** WEBPAGE: thisdayinhistory
**
** Generate a vanity page that shows project activity for the current







|







3354
3355
3356
3357
3358
3359
3360
3361
3362
3363
3364
3365
3366
3367
3368
    blob_append_sql(&sql, "\n LIMIT -1 OFFSET %d", iOffset);
  }
  if( showSql ){
    fossil_print("%s\n", blob_str(&sql));
  }
  db_prepare_blob(&q, &sql);
  blob_reset(&sql);
  print_timeline(&q, n, width, zFormat, verboseFlag);
  db_finalize(&q);
}

/*
** WEBPAGE: thisdayinhistory
**
** Generate a vanity page that shows project activity for the current
3182
3183
3184
3185
3186
3187
3188

3189
3190
3191
3192
3193
3194
3195
  char *z;

  login_check_credentials();
  if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum) ){
    login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
    return;
  }

  style_header("Today In History");
  zToday = (char*)P("today");
  if( zToday ){
    zToday = timeline_expand_datetime(zToday);
    if( !fossil_isdate(zToday) ) zToday = 0;
  }
  if( zToday==0 ){







>







3381
3382
3383
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
  char *z;

  login_check_credentials();
  if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum) ){
    login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
    return;
  }
  style_set_current_feature("timeline");
  style_header("Today In History");
  zToday = (char*)P("today");
  if( zToday ){
    zToday = timeline_expand_datetime(zToday);
    if( !fossil_isdate(zToday) ) zToday = 0;
  }
  if( zToday==0 ){
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
                     " ORDER BY sortby DESC LIMIT 1");
    @ <h2>%d(iAgo) Year%s(iAgo>1?"s":"") Ago
    @ <small>%z(href("%R/timeline?c=%t",zId))(more context)</a>\
    @ </small></h2>
    www_print_timeline(&q, TIMELINE_GRAPH, 0, 0, 0, 0, 0, 0);
  }
  db_finalize(&q);
  style_finish_page("timeline");
}


/*
** COMMAND: test-timewarp-list
**
** Usage: %fossil test-timewarp-list ?-v|---verbose?







|







3429
3430
3431
3432
3433
3434
3435
3436
3437
3438
3439
3440
3441
3442
3443
                     " ORDER BY sortby DESC LIMIT 1");
    @ <h2>%d(iAgo) Year%s(iAgo>1?"s":"") Ago
    @ <small>%z(href("%R/timeline?c=%t",zId))(more context)</a>\
    @ </small></h2>
    www_print_timeline(&q, TIMELINE_GRAPH, 0, 0, 0, 0, 0, 0);
  }
  db_finalize(&q);
  style_finish_page();
}


/*
** COMMAND: test-timewarp-list
**
** Usage: %fossil test-timewarp-list ?-v|---verbose?
3335
3336
3337
3338
3339
3340
3341
3342
3343
  }
  db_finalize(&q);
  if( cnt==0 ){
    @ <p>No timewarps in this repository</p>
  }else{
    @ </tbody></table></div>
  }
  style_finish_page("timewarps");
}







|

3535
3536
3537
3538
3539
3540
3541
3542
3543
  }
  db_finalize(&q);
  if( cnt==0 ){
    @ <p>No timewarps in this repository</p>
  }else{
    @ </tbody></table></div>
  }
  style_finish_page();
}

Changes to src/tkt.c.

583
584
585
586
587
588
589

590
591
592
593
594
595
596
        zUuid, zUuid);
  }
  if( P("plaintext") ){
    style_submenu_element("Formatted", "%R/tktview/%s", zUuid);
  }else{
    style_submenu_element("Plaintext", "%R/tktview/%s?plaintext", zUuid);
  }

  style_header("View Ticket");
  if( showTimeline ){
    int tagid = db_int(0,"SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",
                       zUuid);
    if( tagid ){
      tkt_draw_timeline(tagid, "a");
      @ <hr>







>







583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
        zUuid, zUuid);
  }
  if( P("plaintext") ){
    style_submenu_element("Formatted", "%R/tktview/%s", zUuid);
  }else{
    style_submenu_element("Plaintext", "%R/tktview/%s?plaintext", zUuid);
  }
  style_set_current_feature("tkt");
  style_header("View Ticket");
  if( showTimeline ){
    int tagid = db_int(0,"SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",
                       zUuid);
    if( tagid ){
      tkt_draw_timeline(tagid, "a");
      @ <hr>
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
  zFullName = db_text(0,
       "SELECT tkt_uuid FROM ticket"
       " WHERE tkt_uuid GLOB '%q*'", zUuid);
  if( zFullName ){
    attachment_list(zFullName, "<hr /><h2>Attachments:</h2><ul>");
  }

  style_finish_page("tkt");
}

/*
** TH1 command: append_field FIELD STRING
**
** FIELD is the name of a database column to which we might want
** to append text.  STRING is the text to be appended to that







|







617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
  zFullName = db_text(0,
       "SELECT tkt_uuid FROM ticket"
       " WHERE tkt_uuid GLOB '%q*'", zUuid);
  if( zFullName ){
    attachment_list(zFullName, "<hr /><h2>Attachments:</h2><ul>");
  }

  style_finish_page();
}

/*
** TH1 command: append_field FIELD STRING
**
** FIELD is the name of a database column to which we might want
** to append text.  STRING is the text to be appended to that
813
814
815
816
817
818
819

820
821
822
823
824
825
826
  char *zNewUuid = 0;

  login_check_credentials();
  if( !g.perm.NewTkt ){ login_needed(g.anon.NewTkt); return; }
  if( P("cancel") ){
    cgi_redirect("home");
  }

  style_header("New Ticket");
  ticket_standard_submenu(T_ALL_BUT(T_NEW));
  if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1);
  ticket_init();
  initializeVariablesFromCGI();
  getAllTicketFields();
  initializeVariablesFromDb();







>







814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
  char *zNewUuid = 0;

  login_check_credentials();
  if( !g.perm.NewTkt ){ login_needed(g.anon.NewTkt); return; }
  if( P("cancel") ){
    cgi_redirect("home");
  }
  style_set_current_feature("tkt");
  style_header("New Ticket");
  ticket_standard_submenu(T_ALL_BUT(T_NEW));
  if( g.thTrace ) Th_Trace("BEGIN_TKTNEW<br />\n", -1);
  ticket_init();
  initializeVariablesFromCGI();
  getAllTicketFields();
  initializeVariablesFromDb();
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
  if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){
    cgi_redirect(mprintf("%R/tktview/%s", zNewUuid));
    return;
  }
  captcha_generate(0);
  @ </form>
  if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
  style_finish_page("tkt");
}

/*
** WEBPAGE: tktedit
** WEBPAGE: debug_tktedit
**
** Edit a ticket.  The ticket is identified by the name CGI parameter.







|







841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
  if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zNewUuid ){
    cgi_redirect(mprintf("%R/tktview/%s", zNewUuid));
    return;
  }
  captcha_generate(0);
  @ </form>
  if( g.thTrace ) Th_Trace("END_TKTVIEW<br />\n", -1);
  style_finish_page();
}

/*
** WEBPAGE: tktedit
** WEBPAGE: debug_tktedit
**
** Edit a ticket.  The ticket is identified by the name CGI parameter.
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
    login_needed(g.anon.ApndTkt || g.anon.WrTkt);
    return;
  }
  zName = P("name");
  if( P("cancel") ){
    cgi_redirectf("tktview?name=%T", zName);
  }

  style_header("Edit Ticket");
  if( zName==0 || (nName = strlen(zName))<4 || nName>HNAME_LEN_SHA1
          || !validate16(zName,nName) ){
    @ <span class="tktError">Not a valid ticket id: "%h(zName)"</span>
    style_finish_page("tkt");
    return;
  }
  nRec = db_int(0, "SELECT count(*) FROM ticket WHERE tkt_uuid GLOB '%q*'",
                zName);
  if( nRec==0 ){
    @ <span class="tktError">No such ticket: "%h(zName)"</span>
    style_finish_page("tkt");
    return;
  }
  if( nRec>1 ){
    @ <span class="tktError">%d(nRec) tickets begin with:
    @ "%h(zName)"</span>
    style_finish_page("tkt");
    return;
  }
  if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
  ticket_init();
  getAllTicketFields();
  initializeVariablesFromCGI();
  initializeVariablesFromDb();







>




|






|





|







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
    login_needed(g.anon.ApndTkt || g.anon.WrTkt);
    return;
  }
  zName = P("name");
  if( P("cancel") ){
    cgi_redirectf("tktview?name=%T", zName);
  }
  style_set_current_feature("tkt");
  style_header("Edit Ticket");
  if( zName==0 || (nName = strlen(zName))<4 || nName>HNAME_LEN_SHA1
          || !validate16(zName,nName) ){
    @ <span class="tktError">Not a valid ticket id: "%h(zName)"</span>
    style_finish_page();
    return;
  }
  nRec = db_int(0, "SELECT count(*) FROM ticket WHERE tkt_uuid GLOB '%q*'",
                zName);
  if( nRec==0 ){
    @ <span class="tktError">No such ticket: "%h(zName)"</span>
    style_finish_page();
    return;
  }
  if( nRec>1 ){
    @ <span class="tktError">%d(nRec) tickets begin with:
    @ "%h(zName)"</span>
    style_finish_page();
    return;
  }
  if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
  ticket_init();
  getAllTicketFields();
  initializeVariablesFromCGI();
  initializeVariablesFromDb();
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
  if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){
    cgi_redirect(mprintf("%R/tktview/%s", zName));
    return;
  }
  captcha_generate(0);
  @ </form>
  if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
  style_finish_page("tkt");
}

/*
** Check the ticket table schema in zSchema to see if it appears to
** be well-formed.  If everything is OK, return NULL.  If something is
** amiss, then return a pointer to a string (obtained from malloc) that
** describes the problem.







|







913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
  if( Th_Render(zScript)==TH_RETURN && !g.thTrace && zName ){
    cgi_redirect(mprintf("%R/tktview/%s", zName));
    return;
  }
  captcha_generate(0);
  @ </form>
  if( g.thTrace ) Th_Trace("BEGIN_TKTEDIT<br />\n", -1);
  style_finish_page();
}

/*
** Check the ticket table schema in zSchema to see if it appears to
** be well-formed.  If everything is OK, return NULL.  If something is
** amiss, then return a pointer to a string (obtained from malloc) that
** describes the problem.
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
  style_submenu_element("History", "%R/tkthistory/%s", zUuid);
  style_submenu_element("Status", "%R/info/%s", zUuid);
  if( zType[0]=='c' ){
    zTitle = mprintf("Check-ins Associated With Ticket %h", zUuid);
  }else{
    zTitle = mprintf("Timeline Of Ticket %h", zUuid);
  }

  style_header("%z", zTitle);

  sqlite3_snprintf(6, zGlobPattern, "%s", zUuid);
  canonical16(zGlobPattern, strlen(zGlobPattern));
  tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zUuid);
  if( tagid==0 ){
    @ No such ticket: %h(zUuid)
    style_finish_page("tkt");
    return;
  }
  tkt_draw_timeline(tagid, zType);
  style_finish_page("tkt");
}

/*
** WEBPAGE: tkthistory
** URL: /tkthistory?name=TICKETUUID
**
** Show the complete change history for a single ticket.  Or (to put it
** another way) show a list of artifacts associated with a single ticket.
**
** By default, the artifacts are decoded and formatted.  Text fields
** are formatted as text/plain, since in the general case Fossil does
** not have knowledge of the encoding.  If the "raw" query parameter
** is present, then the* undecoded and unformatted text of each artifact
** is displayed.
*/
void tkthistory_page(void){
  Stmt q;
  char *zTitle;
  const char *zUuid;
  int tagid;







>







|



|












|







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
  style_submenu_element("History", "%R/tkthistory/%s", zUuid);
  style_submenu_element("Status", "%R/info/%s", zUuid);
  if( zType[0]=='c' ){
    zTitle = mprintf("Check-ins Associated With Ticket %h", zUuid);
  }else{
    zTitle = mprintf("Timeline Of Ticket %h", zUuid);
  }
  style_set_current_feature("tkt");
  style_header("%z", zTitle);

  sqlite3_snprintf(6, zGlobPattern, "%s", zUuid);
  canonical16(zGlobPattern, strlen(zGlobPattern));
  tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zUuid);
  if( tagid==0 ){
    @ No such ticket: %h(zUuid)
    style_finish_page();
    return;
  }
  tkt_draw_timeline(tagid, zType);
  style_finish_page();
}

/*
** WEBPAGE: tkthistory
** URL: /tkthistory?name=TICKETUUID
**
** Show the complete change history for a single ticket.  Or (to put it
** another way) show a list of artifacts associated with a single ticket.
**
** By default, the artifacts are decoded and formatted.  Text fields
** are formatted as text/plain, since in the general case Fossil does
** not have knowledge of the encoding.  If the "raw" query parameter
** is present, then the undecoded and unformatted text of each artifact
** is displayed.
*/
void tkthistory_page(void){
  Stmt q;
  char *zTitle;
  const char *zUuid;
  int tagid;
1082
1083
1084
1085
1086
1087
1088

1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
  style_submenu_element("Check-ins", "%R/tkttimeline?name=%s&y=ci", zUuid);
  style_submenu_element("Timeline", "%R/tkttimeline?name=%s", zUuid);
  if( P("raw")!=0 ){
    style_submenu_element("Decoded", "%R/tkthistory/%s", zUuid);
  }else if( g.perm.Admin ){
    style_submenu_element("Raw", "%R/tkthistory/%s?raw", zUuid);
  }

  style_header("%z", zTitle);

  tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zUuid);
  if( tagid==0 ){
    @ No such ticket: %h(zUuid)
    style_finish_page("tkt");
    return;
  }
  if( P("raw")!=0 ){
    @ <h2>Raw Artifacts Associated With Ticket %h(zUuid)</h2>
  }else{
    @ <h2>Artifacts Associated With Ticket %h(zUuid)</h2>
  }







>





|







1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
  style_submenu_element("Check-ins", "%R/tkttimeline?name=%s&y=ci", zUuid);
  style_submenu_element("Timeline", "%R/tkttimeline?name=%s", zUuid);
  if( P("raw")!=0 ){
    style_submenu_element("Decoded", "%R/tkthistory/%s", zUuid);
  }else if( g.perm.Admin ){
    style_submenu_element("Raw", "%R/tkthistory/%s?raw", zUuid);
  }
  style_set_current_feature("tkt");
  style_header("%z", zTitle);

  tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname GLOB 'tkt-%q*'",zUuid);
  if( tagid==0 ){
    @ No such ticket: %h(zUuid)
    style_finish_page();
    return;
  }
  if( P("raw")!=0 ){
    @ <h2>Raw Artifacts Associated With Ticket %h(zUuid)</h2>
  }else{
    @ <h2>Artifacts Associated With Ticket %h(zUuid)</h2>
  }
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
      manifest_destroy(pTicket);
    }
  }
  db_finalize(&q);
  if( nChng ){
    @ </ol>
  }
  style_finish_page("tkt");
}

/*
** Return TRUE if the given BLOB contains a newline character.
*/
static int contains_newline(Blob *p){
  const char *z = blob_str(p);







|







1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
      manifest_destroy(pTicket);
    }
  }
  db_finalize(&q);
  if( nChng ){
    @ </ol>
  }
  style_finish_page();
}

/*
** Return TRUE if the given BLOB contains a newline character.
*/
static int contains_newline(Blob *p){
  const char *z = blob_str(p);
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
        return;
      }
      /* read all given ticket field/value pairs from command line */
      if( i==g.argc ){
        fossil_fatal("empty %s command aborted!",g.argv[2]);
      }
      getAllTicketFields();
      /* read commandline and assign fields in the aField[].zValue array */
      while( i<g.argc ){
        char *zFName;
        char *zFValue;
        int j;
        int append = 0;

        zFName = g.argv[i++];







|







1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
        return;
      }
      /* read all given ticket field/value pairs from command line */
      if( i==g.argc ){
        fossil_fatal("empty %s command aborted!",g.argv[2]);
      }
      getAllTicketFields();
      /* read command-line and assign fields in the aField[].zValue array */
      while( i<g.argc ){
        char *zFName;
        char *zFValue;
        int j;
        int append = 0;

        zFName = g.argv[i++];
1609
1610
1611
1612
1613
1614
1615

1616
1617
1618
1619
1620
** WEBPAGE: tktsrch
** Usage:  /tktsrch?s=PATTERN
**
** Full-text search of all current tickets
*/
void tkt_srchpage(void){
  login_check_credentials();

  style_header("Ticket Search");
  ticket_standard_submenu(T_ALL_BUT(T_SRCH));
  search_screen(SRCH_TKT, 0);
  style_finish_page("tkt");
}







>



|

1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
** WEBPAGE: tktsrch
** Usage:  /tktsrch?s=PATTERN
**
** Full-text search of all current tickets
*/
void tkt_srchpage(void){
  login_check_credentials();
  style_set_current_feature("tkt");
  style_header("Ticket Search");
  ticket_standard_submenu(T_ALL_BUT(T_SRCH));
  search_screen(SRCH_TKT, 0);
  style_finish_page();
}

Changes to src/tktsetup.c.

52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  setup_menu_entry("Report List Page", "tktsetup_reportlist",
    "HTML with embedded TH1 code for the \"report list\" webpage.");
  setup_menu_entry("Report Template", "tktsetup_rpttplt",
    "The default ticket report format.");
  setup_menu_entry("Key Template", "tktsetup_keytplt",
    "The default color key for reports.");
  @ </table>
  style_finish_page("tktsetup");
}

/*
** NOTE:  When changing the table definition below, also change the
** equivalent definition found in schema.c.
*/
/* @-comment: ** */







|







52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
  setup_menu_entry("Report List Page", "tktsetup_reportlist",
    "HTML with embedded TH1 code for the \"report list\" webpage.");
  setup_menu_entry("Report Template", "tktsetup_rpttplt",
    "The default ticket report format.");
  setup_menu_entry("Key Template", "tktsetup_keytplt",
    "The default color key for reports.");
  @ </table>
  style_finish_page();
}

/*
** NOTE:  When changing the table definition below, also change the
** equivalent definition found in schema.c.
*/
/* @-comment: ** */
121
122
123
124
125
126
127

128
129
130
131
132
133
134
135

136
137
138
139
140
141
142
  int isSubmit;

  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }

  if( PB("setup") ){
    cgi_redirect("tktsetup");
  }
  isSubmit = P("submit")!=0;
  z = P("x");
  if( z==0 ){
    z = db_get(zDbField, zDfltValue);
  }

  style_header("Edit %s", zTitle);
  if( P("clear")!=0 ){
    login_verify_csrf_secret();
    db_unset(zDbField, 0);
    if( xRebuild ) xRebuild();
    cgi_redirect("tktsetup");
  }else if( isSubmit ){







>








>







121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
  int isSubmit;

  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }
  style_set_current_feature("tktsetup");
  if( PB("setup") ){
    cgi_redirect("tktsetup");
  }
  isSubmit = P("submit")!=0;
  z = P("x");
  if( z==0 ){
    z = db_get(zDbField, zDfltValue);
  }
  style_set_current_feature("tktsetup");
  style_header("Edit %s", zTitle);
  if( P("clear")!=0 ){
    login_verify_csrf_secret();
    db_unset(zDbField, 0);
    if( xRebuild ) xRebuild();
    cgi_redirect("tktsetup");
  }else if( isSubmit ){
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
  @ </p></blockquote>
  @ </div></form>
  @ <hr />
  @ <h2>Default %s(zTitle)</h2>
  @ <blockquote><pre>
  @ %h(zDfltValue)
  @ </pre></blockquote>
  style_finish_page("tktsetup");
}

/*
** WEBPAGE: tktsetup_tab
** Administrative page for defining the "ticket" table used
** to hold ticket information.
*/







|







163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
  @ </p></blockquote>
  @ </div></form>
  @ <hr />
  @ <h2>Default %s(zTitle)</h2>
  @ <blockquote><pre>
  @ %h(zDfltValue)
  @ </pre></blockquote>
  style_finish_page();
}

/*
** WEBPAGE: tktsetup_tab
** Administrative page for defining the "ticket" table used
** to hold ticket information.
*/
899
900
901
902
903
904
905

906
907
908
909
910
911
912
    login_needed(0);
    return;
  }

  if( P("setup") ){
    cgi_redirect("tktsetup");
  }

  style_header("Ticket Display On Timelines");
  db_begin_transaction();
  @ <form action="%R/tktsetup_timeline" method="post"><div>
  login_insert_csrf_secret();

  @ <hr />
  entry_attribute("Ticket Title", 40, "ticket-title-expr", "t",







>







901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
    login_needed(0);
    return;
  }

  if( P("setup") ){
    cgi_redirect("tktsetup");
  }
  style_set_current_feature("tktsetup");
  style_header("Ticket Display On Timelines");
  db_begin_transaction();
  @ <form action="%R/tktsetup_timeline" method="post"><div>
  login_insert_csrf_secret();

  @ <hr />
  entry_attribute("Ticket Title", 40, "ticket-title-expr", "t",
932
933
934
935
936
937
938
939
940
941
  @ <hr />
  @ <p>
  @ <input type="submit"  name="submit" value="Apply Changes" />
  @ <input type="submit" name="setup" value="Cancel" />
  @ </p>
  @ </div></form>
  db_end_transaction(0);
  style_finish_page("tktsetup");

}







|


935
936
937
938
939
940
941
942
943
944
  @ <hr />
  @ <p>
  @ <input type="submit"  name="submit" value="Apply Changes" />
  @ <input type="submit" name="setup" value="Cancel" />
  @ </p>
  @ </div></form>
  db_end_transaction(0);
  style_finish_page();

}

Changes to src/unversioned.c.

544
545
546
547
548
549
550
551
552
553
554
555
556
557
558

  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_finish_page("uvlist");
    return;
  }
  if( PB("byage") ) zOrderBy = "mtime DESC";
  if( PB("showdel") ) showDel = 1;
  db_prepare(&q,
    "SELECT"
    "   name,"







|







544
545
546
547
548
549
550
551
552
553
554
555
556
557
558

  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_finish_page();
    return;
  }
  if( PB("byage") ) zOrderBy = "mtime DESC";
  if( PB("showdel") ) showDel = 1;
  db_prepare(&q,
    "SELECT"
    "   name,"
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
    @ </tr>
    fossil_free(zAge);
  }
  db_finalize(&q);
  if( n ){
    approxSizeName(sizeof(zSzName), zSzName, iTotalSz);
    @ </tbody>
    @ <tfoot><tr><td><b>Total over %d(cnt) files</b><td><td>%s(zSzName)
    @ <td><td>
    if( g.perm.Admin ){
      @ <td>
    }
    @ </tfoot>
    @ </table></div>
  }else{
    @ No unversioned files on this server.
  }
  style_finish_page("uvlist");
}

/*
** WEBPAGE: juvlist
**
** Return a complete list of unversioned files as JSON.  The JSON
** looks like this:







|









|







620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
    @ </tr>
    fossil_free(zAge);
  }
  db_finalize(&q);
  if( n ){
    approxSizeName(sizeof(zSzName), zSzName, iTotalSz);
    @ </tbody>
    @ <tfoot><tr><td><b>Total for %d(cnt) files</b><td><td>%s(zSzName)
    @ <td><td>
    if( g.perm.Admin ){
      @ <td>
    }
    @ </tfoot>
    @ </table></div>
  }else{
    @ No unversioned files on this server.
  }
  style_finish_page();
}

/*
** WEBPAGE: juvlist
**
** Return a complete list of unversioned files as JSON.  The JSON
** looks like this:

Changes to src/update.c.

219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
        compute_leaves(vid, closeCode);
        db_prepare(&q,
          "%s "
          "   AND event.objid IN leaves"
          " ORDER BY event.mtime DESC",
          timeline_query_for_tty()
        );
        print_timeline(&q, -100, width, 0);
        db_finalize(&q);
        fossil_fatal("Multiple descendants");
      }
    }
    tid = db_int(0, "SELECT rid FROM leaves, event"
                    " WHERE event.objid=leaves.rid"
                    " ORDER BY event.mtime DESC");







|







219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
        compute_leaves(vid, closeCode);
        db_prepare(&q,
          "%s "
          "   AND event.objid IN leaves"
          " ORDER BY event.mtime DESC",
          timeline_query_for_tty()
        );
        print_timeline(&q, -100, width, 0, 0);
        db_finalize(&q);
        fossil_fatal("Multiple descendants");
      }
    }
    tid = db_int(0, "SELECT rid FROM leaves, event"
                    " WHERE event.objid=leaves.rid"
                    " ORDER BY event.mtime DESC");

Changes to src/user.c.

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
       break;
    }
    if( z[i]>0 && z[i]<' ' ) z[i] = ' ';
  }
  blob_append(pBlob, z, -1);
}

#if defined(_WIN32) || defined(__BIONIC__)
#ifdef _WIN32
#include <conio.h>
#endif

/*
** getpass() for Windows and Android.
*/







|







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
       break;
    }
    if( z[i]>0 && z[i]<' ' ) z[i] = ' ';
  }
  blob_append(pBlob, z, -1);
}

#if defined(_WIN32) || defined(__BIONIC__) && !defined(FOSSIL_HAVE_GETPASS)
#ifdef _WIN32
#include <conio.h>
#endif

/*
** getpass() for Windows and Android.
*/
770
771
772
773
774
775
776
777
778
  @ </form>
  @ <form method="post" action="%R/access_log">
  @ <label><input type="checkbox" name="delall">
  @ Delete all entries</input></label>
  @ <input type="submit" name="delallbtn" value="Delete"></input>
  @ </form>
  style_table_sorter();
  style_finish_page("access_log");
}







|

770
771
772
773
774
775
776
777
778
  @ </form>
  @ <form method="post" action="%R/access_log">
  @ <label><input type="checkbox" name="delall">
  @ Delete all entries</input></label>
  @ <input type="submit" name="delallbtn" value="Delete"></input>
  @ </form>
  style_table_sorter();
  style_finish_page();
}

Changes to src/util.c.

19
20
21
22
23
24
25

26
27
28
29
30
31
32
*/
#include "config.h"
#include "util.h"
#if defined(USE_MMAN_H)
# include <sys/mman.h>
# include <unistd.h>
#endif


/*
** For the fossil_timer_xxx() family of functions...
*/
#ifdef _WIN32
# include <windows.h>
#else







>







19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
*/
#include "config.h"
#include "util.h"
#if defined(USE_MMAN_H)
# include <sys/mman.h>
# include <unistd.h>
#endif
#include <math.h>

/*
** For the fossil_timer_xxx() family of functions...
*/
#ifdef _WIN32
# include <windows.h>
#else
422
423
424
425
426
427
428














429
430
431
432
433
434
435
  }
  if( piKernel ){
    *piKernel =
              ((sqlite3_uint64)s.ru_stime.tv_sec)*1000000 + s.ru_stime.tv_usec;
  }
#endif
}















/*
** Internal helper type for fossil_timer_xxx().
 */
enum FossilTimerEnum {
  FOSSIL_TIMER_COUNT = 10 /* Number of timers we can track. */
};







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







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
  }
  if( piKernel ){
    *piKernel =
              ((sqlite3_uint64)s.ru_stime.tv_sec)*1000000 + s.ru_stime.tv_usec;
  }
#endif
}

/*
** Return the resident set size for this process
*/
sqlite3_uint64 fossil_rss(void){
#ifdef _WIN32
  return 0;
#else
  struct rusage s;
  getrusage(RUSAGE_SELF, &s);
  return s.ru_maxrss*1024;
#endif
}


/*
** Internal helper type for fossil_timer_xxx().
 */
enum FossilTimerEnum {
  FOSSIL_TIMER_COUNT = 10 /* Number of timers we can track. */
};
695
696
697
698
699
700
701
702
703

704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724



725
726
727



728





729




730








731


732
733
734
735
736
737
738
739
740
741
742





















































  ** "1" and "I"  that might be easily confused */
  static const char zAlphabet[] =
           /*  0         1         2         3         4         5       */
           /*   123456789 123456789 123456789 123456789 123456789 123456 */
              "23456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";

  if( N<8 ) N = 8;
  else if( N>sizeof(zAlphabet)-2 ) N = sizeof(zAlphabet)-2;
  nSrc = sizeof(zAlphabet) - 1;

  memcpy(zSrc, zAlphabet, nSrc);

  for(i=0; i<N; i++){
    unsigned r;
    sqlite3_randomness(sizeof(r), &r);
    r %= nSrc;
    z[i] = zSrc[r];
    zSrc[r] = zSrc[--nSrc];
  }
  z[i] = 0;
  return fossil_strdup(z);
}

/*
** COMMAND: test-random-password
**
** Usage: %fossil test-random-password ?N?
**
** Generate a random password string of approximately N characters in length.
** If N is omitted, use 10.  Values of N less than 8 are changed to 8
** and greater than 55 and changed to 55.



*/
void test_random_password(void){
  int N = 10;



  if( g.argc>=3 ){





    N = atoi(g.argv[2]);




  }








  fossil_print("%s\n", fossil_random_password(N));


}

/*
** Return the number of decimal digits in a nonnegative integer.  This is useful
** when formatting text.
*/
int fossil_num_digits(int n){
  return n<      10 ? 1 : n<      100 ? 2 : n<      1000 ? 3
       : n<   10000 ? 4 : n<   100000 ? 5 : n<   1000000 ? 6
       : n<10000000 ? 7 : n<100000000 ? 8 : n<1000000000 ? 9 : 10;
}




























































<

>
















|


|
|
>
>
>


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











>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
710
711
712
713
714
715
716

717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
  ** "1" and "I"  that might be easily confused */
  static const char zAlphabet[] =
           /*  0         1         2         3         4         5       */
           /*   123456789 123456789 123456789 123456789 123456789 123456 */
              "23456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";

  if( N<8 ) N = 8;

  nSrc = sizeof(zAlphabet) - 1;
  if( N>nSrc ) N = nSrc;
  memcpy(zSrc, zAlphabet, nSrc);

  for(i=0; i<N; i++){
    unsigned r;
    sqlite3_randomness(sizeof(r), &r);
    r %= nSrc;
    z[i] = zSrc[r];
    zSrc[r] = zSrc[--nSrc];
  }
  z[i] = 0;
  return fossil_strdup(z);
}

/*
** COMMAND: test-random-password
**
** Usage: %fossil test-random-password [N] [--entropy]
**
** Generate a random password string of approximately N characters in length.
** If N is omitted, use 12.  Values of N less than 8 are changed to 8
** and greater than 57 and changed to 57.
**
** If the --entropy flag is included, the number of bits of entropy in
** the password is show as well.
*/
void test_random_password(void){
  int N = 12;
  int showEntropy = 0;
  int i;
  char *zPassword;
  for(i=2; i<g.argc; i++){
    const char *z = g.argv[i];
    if( z[0]=='-' && z[1]=='-' ) z++;
    if( strcmp(z,"-entropy")==0 ){
      showEntropy = 1;
    }else if( fossil_isdigit(z[0]) ){
      N = atoi(z);
      if( N<8 ) N = 8;
      if( N>57 ) N = 57;
    }else{
      usage("[N] [--entropy]");
    }
  }
  zPassword = fossil_random_password(N);
  if( showEntropy ){
    double et = 57.0;
    for(i=1; i<N; i++) et *= 57-i;
    fossil_print("%s (%d bits of entropy)\n", zPassword,
                 (int)(log(et)/log(2.0)));
  }else{
    fossil_print("%s\n", zPassword);
  }
  fossil_free(zPassword);
}

/*
** Return the number of decimal digits in a nonnegative integer.  This is useful
** when formatting text.
*/
int fossil_num_digits(int n){
  return n<      10 ? 1 : n<      100 ? 2 : n<      1000 ? 3
       : n<   10000 ? 4 : n<   100000 ? 5 : n<   1000000 ? 6
       : n<10000000 ? 7 : n<100000000 ? 8 : n<1000000000 ? 9 : 10;
}

#if !defined(_WIN32)
#if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__)
/*
** Search for an executable on the PATH environment variable.
** Return true (1) if found and false (0) if not found.
*/
static int binaryOnPath(const char *zBinary){
  const char *zPath = fossil_getenv("PATH");
  char *zFull;
  int i;
  int bExists;
  while( zPath && zPath[0] ){
    while( zPath[0]==':' ) zPath++;
    for(i=0; zPath[i] && zPath[i]!=':'; i++){}
    zFull = mprintf("%.*s/%s", i, zPath, zBinary);
    bExists = file_access(zFull, X_OK);
    fossil_free(zFull);
    if( bExists==0 ) return 1;
    zPath += i;
  }
  return 0;
}
#endif
#endif


/*
** Return the name of a command that will launch a web-browser.
*/
const char *fossil_web_browser(void){
  const char *zBrowser = 0;
#if defined(_WIN32)
  zBrowser = db_get("web-browser", "start");
#elif defined(__DARWIN__) || defined(__APPLE__) || defined(__HAIKU__)
  zBrowser = db_get("web-browser", "open");
#else
  zBrowser = db_get("web-browser", 0);
  if( zBrowser==0 ){
    static const char *const azBrowserProg[] =
        { "xdg-open", "gnome-open", "firefox", "google-chrome" };
    int i;
    zBrowser = "echo";
    for(i=0; i<count(azBrowserProg); i++){
      if( binaryOnPath(azBrowserProg[i]) ){
        zBrowser = azBrowserProg[i];
        break;
      }
    }
  }
#endif
  return zBrowser;
}

Changes to src/webmail.c.

405
406
407
408
409
410
411

412
413
414
415
416
417
418
    " FROM emailblob, emailbox"
    " WHERE emailid=emsgid AND ebid=%d",
     emailid
  );
  if( zUser ) blob_append_sql(&sql, " AND euser=%Q", zUser);
  db_prepare_blob(&q, &sql);
  blob_reset(&sql);

  style_header("Message %d",emailid);
  if( db_step(&q)==SQLITE_ROW ){
    Blob msg = db_column_text_as_blob(&q, 0);
    int eFormat = atoi(PD("f","0"));
    eState = db_column_int(&q, 1);
    eTranscript = db_column_int(&q, 2);
    if( eFormat==2 ){







>







405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
    " FROM emailblob, emailbox"
    " WHERE emailid=emsgid AND ebid=%d",
     emailid
  );
  if( zUser ) blob_append_sql(&sql, " AND euser=%Q", zUser);
  db_prepare_blob(&q, &sql);
  blob_reset(&sql);
  style_set_current_feature("webmail");
  style_header("Message %d",emailid);
  if( db_step(&q)==SQLITE_ROW ){
    Blob msg = db_column_text_as_blob(&q, 0);
    int eFormat = atoi(PD("f","0"));
    eState = db_column_int(&q, 1);
    eTranscript = db_column_int(&q, 2);
    if( eFormat==2 ){
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
  }
  if( eState==3 ){
    style_submenu_element("Delete", "%s",
      url_render(pUrl,"trash","1",zENum,"1"));
  }

  db_end_transaction(0);
  style_finish_page("webmail");
  return;
}

/*
** Scan the query parameters looking for parameters with name of the
** form "eN" where N is an integer.  For all such integers, change
** the state of every emailbox entry with ebid==N to eStateNew provided







|







512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
  }
  if( eState==3 ){
    style_submenu_element("Delete", "%s",
      url_render(pUrl,"trash","1",zENum,"1"));
  }

  db_end_transaction(0);
  style_finish_page();
  return;
}

/*
** Scan the query parameters looking for parameters with name of the
** form "eN" where N is an integer.  For all such integers, change
** the state of every emailbox entry with ebid==N to eStateNew provided
608
609
610
611
612
613
614

615
616
617
618
619
620
621
622
623
624
625
  char zNPg[30];           /* Next page */
  HQuery url;
  login_check_credentials();
  if( !login_is_individual() ){
    login_needed(0);
    return;
  }

  if( !db_table_exists("repository","emailbox") ){
    style_header("Webmail Not Available");
    @ <p>This repository is not configured to provide webmail</p>
    style_finish_page("webmail");
    return;
  }
  add_content_sql_commands(g.db);
  emailid = atoi(PD("id","0"));
  url_initialize(&url, "webmail");
  if( g.perm.Admin ){
    zUser = PD("user",g.zLogin);







>



|







609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
  char zNPg[30];           /* Next page */
  HQuery url;
  login_check_credentials();
  if( !login_is_individual() ){
    login_needed(0);
    return;
  }
  style_set_current_feature("webmail");
  if( !db_table_exists("repository","emailbox") ){
    style_header("Webmail Not Available");
    @ <p>This repository is not configured to provide webmail</p>
    style_finish_page();
    return;
  }
  add_content_sql_commands(g.db);
  emailid = atoi(PD("id","0"));
  url_initialize(&url, "webmail");
  if( g.perm.Admin ){
    zUser = PD("user",g.zLogin);
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
  @ function webmailSelectAll(){
  @   var x = document.getElementsByClassName("webmailckbox");
  @   for(i=0; i<x.length; i++){
  @     x[i].checked = true;
  @   }
  @ }
  @ </script>
  style_finish_page("webmail");
  db_end_transaction(0);
}

/*
** WEBPAGE:  emailblob
**
** This page, accessible only to administrators, allows easy viewing of







|







757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
  @ function webmailSelectAll(){
  @   var x = document.getElementsByClassName("webmailckbox");
  @   for(i=0; i<x.length; i++){
  @     x[i].checked = true;
  @   }
  @ }
  @ </script>
  style_finish_page();
  db_end_transaction(0);
}

/*
** WEBPAGE:  emailblob
**
** This page, accessible only to administrators, allows easy viewing of
778
779
780
781
782
783
784

785
786
787
788
789
790
791
  Stmt q;
  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }
  add_content_sql_commands(g.db);

  style_header("emailblob table");
  if( id>0 ){
    style_submenu_element("Index", "%R/emailblob");
    @ <ul>
    db_prepare(&q, "SELECT emailid FROM emailblob WHERE ets=%d", id);
    while( db_step(&q)==SQLITE_ROW ){
      int id = db_column_int(&q, 0);







>







780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
  Stmt q;
  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }
  add_content_sql_commands(g.db);
  style_set_current_feature("webmail");
  style_header("emailblob table");
  if( id>0 ){
    style_submenu_element("Index", "%R/emailblob");
    @ <ul>
    db_prepare(&q, "SELECT emailid FROM emailblob WHERE ets=%d", id);
    while( db_step(&q)==SQLITE_ROW ){
      int id = db_column_int(&q, 0);
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876

877
878
879
880
881
882
883
      @  <td align="right" data-sortkey='%08x(csz)'>%,d(csz)</td>
      @ </tr>
    }
    @ </tbody></table>
    db_finalize(&q);
    style_table_sorter();
  }
  style_finish_page("webmail");
}

/*
** WEBPAGE:  emailoutq
**
** This page, accessible only to administrators, allows easy viewing of
** the emailoutq table - the table that contains the email messages
** that are queued for transmission via SMTP.
*/
void webmail_emailoutq_page(void){
  Stmt q;
  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }
  add_content_sql_commands(g.db);

  style_header("emailoutq table");
  style_submenu_element("emailblob table","%R/emailblob");
  db_prepare(&q,
     "SELECT edomain, efrom, eto, emsgid, "
     "       datetime(ectime,'unixepoch'),"
     "       datetime(nullif(emtime,0),'unixepoch'),"
     "       ensend, ets"







|

















>







855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
      @  <td align="right" data-sortkey='%08x(csz)'>%,d(csz)</td>
      @ </tr>
    }
    @ </tbody></table>
    db_finalize(&q);
    style_table_sorter();
  }
  style_finish_page();
}

/*
** WEBPAGE:  emailoutq
**
** This page, accessible only to administrators, allows easy viewing of
** the emailoutq table - the table that contains the email messages
** that are queued for transmission via SMTP.
*/
void webmail_emailoutq_page(void){
  Stmt q;
  login_check_credentials();
  if( !g.perm.Setup ){
    login_needed(0);
    return;
  }
  add_content_sql_commands(g.db);
  style_set_current_feature("webmail");
  style_header("emailoutq table");
  style_submenu_element("emailblob table","%R/emailblob");
  db_prepare(&q,
     "SELECT edomain, efrom, eto, emsgid, "
     "       datetime(ectime,'unixepoch'),"
     "       datetime(nullif(emtime,0),'unixepoch'),"
     "       ensend, ets"
909
910
911
912
913
914
915
916
917
    }else{
      @  <td>&nbsp;</td>
    }
  }
  @ </tbody></table>
  db_finalize(&q);
  style_table_sorter();
  style_finish_page("webmail");
}







|

913
914
915
916
917
918
919
920
921
    }else{
      @  <td>&nbsp;</td>
    }
  }
  @ </tbody></table>
  db_finalize(&q);
  style_table_sorter();
  style_finish_page();
}

Changes to src/wiki.c.

60
61
62
63
64
65
66

67
68
69
70
71
72
73
74
75
76
77
78

/*
** Check a wiki name.  If it is not well-formed, then issue an error
** and return true.  If it is well-formed, return false.
*/
static int check_name(const char *z){
  if( !wiki_name_is_wellformed((const unsigned char *)z) ){

    style_header("Wiki Page Name Error");
    @ The wiki name "<span class="wikiError">%h(z)</span>" is not well-formed.
    @ Rules for wiki page names:
    well_formed_wiki_name_rules();
    style_finish_page("wiki");
    return 1;
  }
  return 0;
}

/*
** Return the tagid associated with a particular wiki page.







>




|







60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79

/*
** Check a wiki name.  If it is not well-formed, then issue an error
** and return true.  If it is well-formed, return false.
*/
static int check_name(const char *z){
  if( !wiki_name_is_wellformed((const unsigned char *)z) ){
    style_set_current_feature("wiki");
    style_header("Wiki Page Name Error");
    @ The wiki name "<span class="wikiError">%h(z)</span>" is not well-formed.
    @ Rules for wiki page names:
    well_formed_wiki_name_rules();
    style_finish_page();
    return 1;
  }
  return 0;
}

/*
** Return the tagid associated with a particular wiki page.
132
133
134
135
136
137
138

139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
    login_check_credentials();
    g.zExtra = zPageName;
    cgi_set_parameter_nocopy("name", g.zExtra, 1);
    g.isHome = 1;
    wiki_page();
    return;
  }

  style_header("Home");
  @ <p>This is a stub home-page for the project.
  @ To fill in this page, first go to
  @ %z(href("%R/setup_config"))setup/config</a>
  @ and establish a "Project Name".  Then create a
  @ wiki page with that name.  The content of that wiki page
  @ will be displayed in place of this message.</p>
  style_finish_page("wiki");
}

/*
** Return true if the given pagename is the name of the sandbox
*/
static int is_sandbox(const char *zPagename){
  return fossil_stricmp(zPagename,"sandbox")==0 ||







>







|







133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
    login_check_credentials();
    g.zExtra = zPageName;
    cgi_set_parameter_nocopy("name", g.zExtra, 1);
    g.isHome = 1;
    wiki_page();
    return;
  }
  style_set_current_feature("wiki");
  style_header("Home");
  @ <p>This is a stub home-page for the project.
  @ To fill in this page, first go to
  @ %z(href("%R/setup_config"))setup/config</a>
  @ and establish a "Project Name".  Then create a
  @ wiki page with that name.  The content of that wiki page
  @ will be displayed in place of this message.</p>
  style_finish_page();
}

/*
** Return true if the given pagename is the name of the sandbox
*/
static int is_sandbox(const char *zPagename){
  return fossil_stricmp(zPagename,"sandbox")==0 ||
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
** WEBPAGE: md_rules
**
** Show a summary of the Markdown wiki formatting rules.
*/
void markdown_rules_page(void){
  Blob x;
  int fTxt = P("txt")!=0;

  style_header("Markdown Formatting Rules");
  if( fTxt ){
    style_submenu_element("Formatted", "%R/md_rules");
  }else{
    style_submenu_element("Plain-Text", "%R/md_rules?txt=1");
  }
  style_submenu_element("Wiki", "%R/wiki_rules");
  blob_init(&x, builtin_text("markdown.md"), -1);
  blob_materialize(&x);
  interwiki_append_map_table(&x);
  safe_html_context(DOCSRC_TRUSTED);
  wiki_render_by_mimetype(&x, fTxt ? "text/plain" : "text/x-markdown");
  blob_reset(&x);
  style_finish_page("wiki");
}

/*
** WEBPAGE: wiki_rules
**
** Show a summary of the wiki formatting rules.
*/
void wiki_rules_page(void){
  Blob x;
  int fTxt = P("txt")!=0;

  style_header("Wiki Formatting Rules");
  if( fTxt ){
    style_submenu_element("Formatted", "%R/wiki_rules");
  }else{
    style_submenu_element("Plain-Text", "%R/wiki_rules?txt=1");
  }
  style_submenu_element("Markdown","%R/md_rules");
  blob_init(&x, builtin_text("wiki.wiki"), -1);
  blob_materialize(&x);
  interwiki_append_map_table(&x);
  safe_html_context(DOCSRC_TRUSTED);
  wiki_render_by_mimetype(&x, fTxt ? "text/plain" : "text/x-fossil-wiki");
  blob_reset(&x);
  style_finish_page("wiki");
}

/*
** WEBPAGE: markup_help
**
** Show links to the md_rules and wiki_rules pages.
*/
void markup_help_page(void){

  style_header("Fossil Markup Styles");
  @ <ul>
  @ <li><p>%z(href("%R/wiki_rules"))Fossil Wiki Formatting Rules</a></p></li>
  @ <li><p>%z(href("%R/md_rules"))Markdown Formatting Rules</a></p></li>
  @ </ul>
  style_finish_page("wiki");
}

/*
** Returns non-zero if moderation is required for wiki changes and wiki
** attachments.
*/
int wiki_need_moderation(







>













|










>













|








>





|







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
** WEBPAGE: md_rules
**
** Show a summary of the Markdown wiki formatting rules.
*/
void markdown_rules_page(void){
  Blob x;
  int fTxt = P("txt")!=0;
  style_set_current_feature("wiki");
  style_header("Markdown Formatting Rules");
  if( fTxt ){
    style_submenu_element("Formatted", "%R/md_rules");
  }else{
    style_submenu_element("Plain-Text", "%R/md_rules?txt=1");
  }
  style_submenu_element("Wiki", "%R/wiki_rules");
  blob_init(&x, builtin_text("markdown.md"), -1);
  blob_materialize(&x);
  interwiki_append_map_table(&x);
  safe_html_context(DOCSRC_TRUSTED);
  wiki_render_by_mimetype(&x, fTxt ? "text/plain" : "text/x-markdown");
  blob_reset(&x);
  style_finish_page();
}

/*
** WEBPAGE: wiki_rules
**
** Show a summary of the wiki formatting rules.
*/
void wiki_rules_page(void){
  Blob x;
  int fTxt = P("txt")!=0;
  style_set_current_feature("wiki");
  style_header("Wiki Formatting Rules");
  if( fTxt ){
    style_submenu_element("Formatted", "%R/wiki_rules");
  }else{
    style_submenu_element("Plain-Text", "%R/wiki_rules?txt=1");
  }
  style_submenu_element("Markdown","%R/md_rules");
  blob_init(&x, builtin_text("wiki.wiki"), -1);
  blob_materialize(&x);
  interwiki_append_map_table(&x);
  safe_html_context(DOCSRC_TRUSTED);
  wiki_render_by_mimetype(&x, fTxt ? "text/plain" : "text/x-fossil-wiki");
  blob_reset(&x);
  style_finish_page();
}

/*
** WEBPAGE: markup_help
**
** Show links to the md_rules and wiki_rules pages.
*/
void markup_help_page(void){
  style_set_current_feature("wiki");
  style_header("Fossil Markup Styles");
  @ <ul>
  @ <li><p>%z(href("%R/wiki_rules"))Fossil Wiki Formatting Rules</a></p></li>
  @ <li><p>%z(href("%R/md_rules"))Markdown Formatting Rules</a></p></li>
  @ </ul>
  style_finish_page();
}

/*
** Returns non-zero if moderation is required for wiki changes and wiki
** attachments.
*/
int wiki_need_moderation(
342
343
344
345
346
347
348

349
350
351
352
353
354
355
/*
** WEBPAGE: wikihelp
** A generic landing page for wiki.
*/
void wiki_helppage(void){
  login_check_credentials();
  if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }

  style_header("Wiki Help");
  wiki_standard_submenu(W_ALL_BUT(W_HELP));
  @ <h2>Wiki Links</h2>
  @ <ul>
  @ <li> %z(href("%R/timeline?y=w"))Recent changes</a> to wiki pages.</li>
  @ <li> Formatting rules for %z(href("%R/wiki_rules"))Fossil Wiki</a> and for
  @ %z(href("%R/md_rules"))Markdown Wiki</a>.</li>







>







347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
/*
** WEBPAGE: wikihelp
** A generic landing page for wiki.
*/
void wiki_helppage(void){
  login_check_credentials();
  if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
  style_set_current_feature("wiki");
  style_header("Wiki Help");
  wiki_standard_submenu(W_ALL_BUT(W_HELP));
  @ <h2>Wiki Links</h2>
  @ <ul>
  @ <li> %z(href("%R/timeline?y=w"))Recent changes</a> to wiki pages.</li>
  @ <li> Formatting rules for %z(href("%R/wiki_rules"))Fossil Wiki</a> and for
  @ %z(href("%R/md_rules"))Markdown Wiki</a>.</li>
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
    @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
  }
  if( search_restrict(SRCH_WIKI)!=0 ){
    @ <li> %z(href("%R/wikisrch"))Search</a> for wiki pages containing key
    @ words</li>
  }
  @ </ul>
  style_finish_page("wiki");
  return;
}

/*
** WEBPAGE: wikisrch
** Usage:  /wikisrch?s=PATTERN
**
** Full-text search of all current wiki text
*/
void wiki_srchpage(void){
  login_check_credentials();

  style_header("Wiki Search");
  wiki_standard_submenu(W_HELP|W_LIST|W_SANDBOX);
  search_screen(SRCH_WIKI, 0);
  style_finish_page("wiki");
}

/* Return values from wiki_page_type() */
#if INTERFACE
# define WIKITYPE_UNKNOWN    (-1)
# define WIKITYPE_NORMAL     0
# define WIKITYPE_BRANCH     1







|











>



|







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
    @ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
  }
  if( search_restrict(SRCH_WIKI)!=0 ){
    @ <li> %z(href("%R/wikisrch"))Search</a> for wiki pages containing key
    @ words</li>
  }
  @ </ul>
  style_finish_page();
  return;
}

/*
** WEBPAGE: wikisrch
** Usage:  /wikisrch?s=PATTERN
**
** Full-text search of all current wiki text
*/
void wiki_srchpage(void){
  login_check_credentials();
  style_set_current_feature("wiki");
  style_header("Wiki Search");
  wiki_standard_submenu(W_HELP|W_LIST|W_SANDBOX);
  search_screen(SRCH_WIKI, 0);
  style_finish_page();
}

/* Return values from wiki_page_type() */
#if INTERFACE
# define WIKITYPE_UNKNOWN    (-1)
# define WIKITYPE_NORMAL     0
# define WIKITYPE_BRANCH     1
444
445
446
447
448
449
450

451
452
453
454
455
456
457
** continuing to the plain wiki display.
*/
static int wiki_page_header(
  int eType,                /* Page type.  Might be WIKITYPE_UNKNOWN */
  const char *zPageName,    /* Name of the page */
  const char *zExtra        /* Extra prefix text on the page header */
){

  if( eType==WIKITYPE_UNKNOWN ) eType = wiki_page_type(zPageName);
  switch( eType ){
    case WIKITYPE_NORMAL: {
      style_header("%s%s", zExtra, zPageName);
      break;
    }
    case WIKITYPE_CHECKIN: {







>







451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
** continuing to the plain wiki display.
*/
static int wiki_page_header(
  int eType,                /* Page type.  Might be WIKITYPE_UNKNOWN */
  const char *zPageName,    /* Name of the page */
  const char *zExtra        /* Extra prefix text on the page header */
){
  style_set_current_feature("wiki");
  if( eType==WIKITYPE_UNKNOWN ) eType = wiki_page_type(zPageName);
  switch( eType ){
    case WIKITYPE_NORMAL: {
      style_header("%s%s", zExtra, zPageName);
      break;
    }
    case WIKITYPE_CHECKIN: {
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
    safe_html_context(DOCSRC_WIKI);
    wiki_render_by_mimetype(&wiki, zMimetype);
    blob_reset(&wiki);
  }
  attachment_list(zPageName, "<hr /><h2>Attachments:</h2><ul>");
  manifest_destroy(pWiki);
  document_emit_js(/*for optional pikchr support*/);
  style_finish_page("wiki");
}

/*
** Write a wiki artifact into the repository
*/
int wiki_put(Blob *pWiki, int parent, int needMod){
  int nrid;







|







603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
    safe_html_context(DOCSRC_WIKI);
    wiki_render_by_mimetype(&wiki, zMimetype);
    blob_reset(&wiki);
  }
  attachment_list(zPageName, "<hr /><h2>Attachments:</h2><ul>");
  manifest_destroy(pWiki);
  document_emit_js(/*for optional pikchr support*/);
  style_finish_page();
}

/*
** Write a wiki artifact into the repository
*/
int wiki_put(Blob *pWiki, int parent, int needMod){
  int nrid;
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
** in the name of an unknown page will trigger the creation
** of a new page (which is not actually created in the database
** until the user explicitly saves it). If passed no page name,
** the user may select a page from the list on the first UI tab.
**
** When creating a new page, the mimetype URL parameter may optionally
** be used to set its mimetype to one of text/x-fossil-wiki,
** text/x-markdown, or text/plain, defauling to the former.
*/
void wikiedit_page(void){
  const char *zPageName;
  const char * zMimetype = P("mimetype");
  int isSandbox;
  int found = 0;








|







1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
** in the name of an unknown page will trigger the creation
** of a new page (which is not actually created in the database
** until the user explicitly saves it). If passed no page name,
** the user may select a page from the list on the first UI tab.
**
** When creating a new page, the mimetype URL parameter may optionally
** be used to set its mimetype to one of text/x-fossil-wiki,
** text/x-markdown, or text/plain, defaulting to the former.
*/
void wikiedit_page(void){
  const char *zPageName;
  const char * zMimetype = P("mimetype");
  int isSandbox;
  int found = 0;

1136
1137
1138
1139
1140
1141
1142

1143
1144
1145
1146
1147
1148
1149
    }
  }else{
    if( !g.perm.RdWiki ){
      login_needed(g.anon.RdWiki);
      return;
    }
  }

  style_header("Wiki Editor");
  style_emit_noscript_for_js_page();

  /* Status bar */
  CX("<div id='fossil-status-bar' "
     "title='Status message area. Double-click to clear them.'>"
     "Status messages will go here.</div>\n"







>







1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
    }
  }else{
    if( !g.perm.RdWiki ){
      login_needed(g.anon.RdWiki);
      return;
    }
  }
  style_set_current_feature("wiki");
  style_header("Wiki Editor");
  style_emit_noscript_for_js_page();

  /* Status bar */
  CX("<div id='fossil-status-bar' "
     "title='Status message area. Double-click to clear them.'>"
     "Status messages will go here.</div>\n"
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
    CX("P.loadPage(%!j);\n", zPageName);
  }
  CX("}catch(e){"
     "fossil.error(e); console.error('Exception:',e);"
     "}\n");
  CX("});\n"/*fossil.onPageLoad()*/);
  style_script_end();
  style_finish_page("wiki");
}

/*
** WEBPAGE: wikinew
** URL /wikinew
**
** Prompt the user to enter the name of a new wiki page.  Then redirect







|







1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
    CX("P.loadPage(%!j);\n", zPageName);
  }
  CX("}catch(e){"
     "fossil.error(e); console.error('Exception:',e);"
     "}\n");
  CX("});\n"/*fossil.onPageLoad()*/);
  style_script_end();
  style_finish_page();
}

/*
** WEBPAGE: wikinew
** URL /wikinew
**
** Prompt the user to enter the name of a new wiki page.  Then redirect
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
    return;
  }
  zName = PD("name","");
  zMimetype = wiki_filter_mimetypes(P("mimetype"));
  if( zName[0] && wiki_name_is_wellformed((const unsigned char *)zName) ){
    cgi_redirectf("wikiedit?name=%T&mimetype=%s", zName, zMimetype);
  }

  style_header("Create A New Wiki Page");
  wiki_standard_submenu(W_ALL_BUT(W_NEW));
  @ <p>Rules for wiki page names:</p>
  well_formed_wiki_name_rules();
  form_begin(0, "%R/wikinew");
  @ <p>Name of new wiki page:
  @ <input style="width: 35;" type="text" name="name" value="%h(zName)" /><br />
  @ %z(href("%R/markup_help"))Markup style</a>:
  mimetype_option_menu("text/x-fossil-wiki");
  @ <br /><input type="submit" value="Create" />
  @ </p></form>
  if( zName[0] ){
    @ <p><span class="wikiError">
    @ "%h(zName)" is not a valid wiki page name!</span></p>
  }
  style_finish_page("wiki");
}


/*
** Append the wiki text for an remark to the end of the given BLOB.
*/
static void appendRemark(Blob *p, const char *zMimetype){







>















|







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
    return;
  }
  zName = PD("name","");
  zMimetype = wiki_filter_mimetypes(P("mimetype"));
  if( zName[0] && wiki_name_is_wellformed((const unsigned char *)zName) ){
    cgi_redirectf("wikiedit?name=%T&mimetype=%s", zName, zMimetype);
  }
  style_set_current_feature("wiki");
  style_header("Create A New Wiki Page");
  wiki_standard_submenu(W_ALL_BUT(W_NEW));
  @ <p>Rules for wiki page names:</p>
  well_formed_wiki_name_rules();
  form_begin(0, "%R/wikinew");
  @ <p>Name of new wiki page:
  @ <input style="width: 35;" type="text" name="name" value="%h(zName)" /><br />
  @ %z(href("%R/markup_help"))Markup style</a>:
  mimetype_option_menu("text/x-fossil-wiki");
  @ <br /><input type="submit" value="Create" />
  @ </p></form>
  if( zName[0] ){
    @ <p><span class="wikiError">
    @ "%h(zName)" is not a valid wiki page name!</span></p>
  }
  style_finish_page();
}


/*
** Append the wiki text for an remark to the end of the given BLOB.
*/
static void appendRemark(Blob *p, const char *zMimetype){
1511
1512
1513
1514
1515
1516
1517

1518
1519
1520
1521
1522
1523
1524
    cgi_redirectf("wiki?name=%T", zPageName);
  }
  if( P("cancel")!=0 ){
    cgi_redirectf("wiki?name=%T", zPageName);
    return;
  }
  style_set_current_page("%T?name=%T", g.zPath, zPageName);

  style_header("Append Comment To: %s", zPageName);
  if( !goodCaptcha ){
    @ <p class="generalError">Error: Incorrect security code.</p>
  }
  if( P("preview")!=0 ){
    Blob preview;
    blob_zero(&preview);







>







1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
    cgi_redirectf("wiki?name=%T", zPageName);
  }
  if( P("cancel")!=0 ){
    cgi_redirectf("wiki?name=%T", zPageName);
    return;
  }
  style_set_current_page("%T?name=%T", g.zPath, zPageName);
  style_set_current_feature("wiki");
  style_header("Append Comment To: %s", zPageName);
  if( !goodCaptcha ){
    @ <p class="generalError">Error: Incorrect security code.</p>
  }
  if( P("preview")!=0 ){
    Blob preview;
    blob_zero(&preview);
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
  @  rows="10" wrap="virtual">%h(PD("r",""))</textarea>
  @ <br />
  @ <input type="submit" name="preview" value="Preview Your Comment" />
  @ <input type="submit" name="submit" value="Append Your Changes" />
  @ <input type="submit" name="cancel" value="Cancel" />
  captcha_generate(0);
  @ </form>
  style_finish_page("wiki");
}

/*
** WEBPAGE: whistory
** URL: /whistory?name=PAGENAME
**
** Additional parameters:
**
**     showid          Show RID values
**
** Show the complete change history for a single wiki page.
*/
void whistory_page(void){
  Stmt q;
  const char *zPageName;
  double rNow;
  int showRid;
  login_check_credentials();
  if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
  zPageName = PD("name","");

  style_header("History Of %s", zPageName);
  showRid = P("showid")!=0;
  db_prepare(&q,
    "SELECT"
    "  event.mtime,"
    "  blob.uuid,"
    "  coalesce(event.euser,event.user),"







|




















>







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
  @  rows="10" wrap="virtual">%h(PD("r",""))</textarea>
  @ <br />
  @ <input type="submit" name="preview" value="Preview Your Comment" />
  @ <input type="submit" name="submit" value="Append Your Changes" />
  @ <input type="submit" name="cancel" value="Cancel" />
  captcha_generate(0);
  @ </form>
  style_finish_page();
}

/*
** WEBPAGE: whistory
** URL: /whistory?name=PAGENAME
**
** Additional parameters:
**
**     showid          Show RID values
**
** Show the complete change history for a single wiki page.
*/
void whistory_page(void){
  Stmt q;
  const char *zPageName;
  double rNow;
  int showRid;
  login_check_credentials();
  if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
  zPageName = PD("name","");
  style_set_current_feature("wiki");
  style_header("History Of %s", zPageName);
  showRid = P("showid")!=0;
  db_prepare(&q,
    "SELECT"
    "  event.mtime,"
    "  blob.uuid,"
    "  coalesce(event.euser,event.user),"
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
    @ <td>%z(chref("wh-difflink","%R/wdiff?id=%S",zUuid))diff</a></td>
    @ </tr>
  }
  @ </tbody></table></div>
  db_finalize(&q);
  builtin_request_js("fossil.page.whistory.js");
  /* style_table_sorter(); */
  style_finish_page("wiki");
}

/*
** WEBPAGE: wdiff
**
** Show the changes to a wiki page.
**







|







1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
    @ <td>%z(chref("wh-difflink","%R/wdiff?id=%S",zUuid))diff</a></td>
    @ </tr>
  }
  @ </tbody></table></div>
  db_finalize(&q);
  builtin_request_js("fossil.page.whistory.js");
  /* style_table_sorter(); */
  style_finish_page();
}

/*
** WEBPAGE: wdiff
**
** Show the changes to a wiki page.
**
1695
1696
1697
1698
1699
1700
1701

1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
    @ "%z(href("%R/whistory?name=%s",pW1->zWikiTitle))%h(pW1->zWikiTitle)</a>"\
    @ </h2>
  }
  nextRid = wiki_next(wiki_tagid(pW1->zWikiTitle),pW1->rDate);
  if( nextRid ){
    style_submenu_element("Next", "%R/wdiff?rid=%d", nextRid);
  }

  style_header("Changes To %s", pW1->zWikiTitle);
  blob_zero(&d);
  diffFlags = construct_diff_flags(1);
  text_diff(&w2, &w1, &d, 0, diffFlags | DIFF_HTML | DIFF_LINENO);
  @ <pre class="udiff">
  @ %s(blob_str(&d))
  @ <pre>
  manifest_destroy(pW1);
  manifest_destroy(pW2);
  style_finish_page("wiki");
}

/*
** A query that returns information about all wiki pages.
**
**    wname         Name of the wiki page
**    wsort         Sort names by this label







>









|







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
    @ "%z(href("%R/whistory?name=%s",pW1->zWikiTitle))%h(pW1->zWikiTitle)</a>"\
    @ </h2>
  }
  nextRid = wiki_next(wiki_tagid(pW1->zWikiTitle),pW1->rDate);
  if( nextRid ){
    style_submenu_element("Next", "%R/wdiff?rid=%d", nextRid);
  }
  style_set_current_feature("wiki");
  style_header("Changes To %s", pW1->zWikiTitle);
  blob_zero(&d);
  diffFlags = construct_diff_flags(1);
  text_diff(&w2, &w1, &d, 0, diffFlags | DIFF_HTML | DIFF_LINENO);
  @ <pre class="udiff">
  @ %s(blob_str(&d))
  @ <pre>
  manifest_destroy(pW1);
  manifest_destroy(pW2);
  style_finish_page();
}

/*
** A query that returns information about all wiki pages.
**
**    wname         Name of the wiki page
**    wsort         Sort names by this label
1751
1752
1753
1754
1755
1756
1757

1758
1759
1760
1761
1762
1763
1764
  Stmt q;
  double rNow;
  int showAll = P("all")!=0;
  int showRid = P("showid")!=0;

  login_check_credentials();
  if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }

  style_header("Available Wiki Pages");
  if( showAll ){
    style_submenu_element("Active", "%R/wcontent");
  }else{
    style_submenu_element("All", "%R/wcontent?all=1");
  }
  wiki_standard_submenu(W_ALL_BUT(W_LIST));







>







1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
  Stmt q;
  double rNow;
  int showAll = P("all")!=0;
  int showRid = P("showid")!=0;

  login_check_credentials();
  if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
  style_set_current_feature("wiki");
  style_header("Available Wiki Pages");
  if( showAll ){
    style_submenu_element("Active", "%R/wcontent");
  }else{
    style_submenu_element("All", "%R/wcontent?all=1");
  }
  wiki_standard_submenu(W_ALL_BUT(W_LIST));
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
    }
    @ </tr>
    fossil_free(zWDisplayName);
  }
  @ </tbody></table></div>
  db_finalize(&q);
  style_table_sorter();
  style_finish_page("wiki");
}

/*
** WEBPAGE: wfind
**
** URL: /wfind?title=TITLE
** List all wiki pages whose titles contain the search text
*/
void wfind_page(void){
  Stmt q;
  const char *zTitle;
  login_check_credentials();
  if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
  zTitle = PD("title","*");

  style_header("Wiki Pages Found");
  @ <ul>
  db_prepare(&q,
    "SELECT substr(tagname, 6, 1000) FROM tag WHERE tagname like 'wiki-%%%q%%'"
    " ORDER BY lower(tagname) /*sort*/" ,
    zTitle);
  while( db_step(&q)==SQLITE_ROW ){
    const char *zName = db_column_text(&q, 0);
    @ <li>%z(href("%R/wiki?name=%T",zName))%h(zName)</a></li>
  }
  db_finalize(&q);
  @ </ul>
  style_finish_page("wiki");
}

/*
** Add a new wiki page to the repository.  The page name is
** given by the zPageName parameter.  rid must be zero to create
** a new page otherwise the page identified by rid is updated.
**







|














>












|







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
    }
    @ </tr>
    fossil_free(zWDisplayName);
  }
  @ </tbody></table></div>
  db_finalize(&q);
  style_table_sorter();
  style_finish_page();
}

/*
** WEBPAGE: wfind
**
** URL: /wfind?title=TITLE
** List all wiki pages whose titles contain the search text
*/
void wfind_page(void){
  Stmt q;
  const char *zTitle;
  login_check_credentials();
  if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
  zTitle = PD("title","*");
  style_set_current_feature("wiki");
  style_header("Wiki Pages Found");
  @ <ul>
  db_prepare(&q,
    "SELECT substr(tagname, 6, 1000) FROM tag WHERE tagname like 'wiki-%%%q%%'"
    " ORDER BY lower(tagname) /*sort*/" ,
    zTitle);
  while( db_step(&q)==SQLITE_ROW ){
    const char *zName = db_column_text(&q, 0);
    @ <li>%z(href("%R/wiki?name=%T",zName))%h(zName)</a></li>
  }
  db_finalize(&q);
  @ </ul>
  style_finish_page();
}

/*
** Add a new wiki page to the repository.  The page name is
** given by the zPageName parameter.  rid must be zero to create
** a new page otherwise the page identified by rid is updated.
**

Changes to src/wiki.wiki.

30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
  3.  <b>Enumeration Lists.</b>
      An enumeration list item is a line that begins with a single "#"
      character surrounded on both sides by two or more spaces or by a tab.
      Or it can be a number and a "." (ex:  "5.") surrounded on both sides
      by two spaces or a tab.
      Only a single level of enumeration list is supported by wiki.
      For nested lists or for enumerations that count using letters or
      roman numerials, use HTML.

  4.  <b>Indented Paragraphs.</b>
      Any paragraph that begins with two or more spaces or a tab and which
      is not a bullet or enumeration list item is rendered indented.
      Only a single level of indentation is supported by wiki.
      Use HTML for deeper indentation.








|







30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
  3.  <b>Enumeration Lists.</b>
      An enumeration list item is a line that begins with a single "#"
      character surrounded on both sides by two or more spaces or by a tab.
      Or it can be a number and a "." (ex:  "5.") surrounded on both sides
      by two spaces or a tab.
      Only a single level of enumeration list is supported by wiki.
      For nested lists or for enumerations that count using letters or
      roman numerals, use HTML.

  4.  <b>Indented Paragraphs.</b>
      Any paragraph that begins with two or more spaces or a tab and which
      is not a bullet or enumeration list item is rendered indented.
      Only a single level of indentation is supported by wiki.
      Use HTML for deeper indentation.

Changes to src/wikiformat.c.

30
31
32
33
34
35
36

37
38
39
40
41
42
43
#define WIKI_NOBLOCK        0x004  /* No block markup of any kind */
#define WIKI_BUTTONS        0x008  /* Allow sub-menu buttons */
#define WIKI_NOBADLINKS     0x010  /* Ignore broken hyperlinks */
#define WIKI_LINKSONLY      0x020  /* No markup.  Only decorate links */
#define WIKI_NEWLINE        0x040  /* Honor \n - break lines at each \n */
#define WIKI_MARKDOWNLINKS  0x080  /* Resolve hyperlinks as in markdown */
#define WIKI_SAFE           0x100  /* Make the result safe for embedding */

#endif


/*
** These are the only markup attributes allowed.
*/
enum allowed_attr_t {







>







30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#define WIKI_NOBLOCK        0x004  /* No block markup of any kind */
#define WIKI_BUTTONS        0x008  /* Allow sub-menu buttons */
#define WIKI_NOBADLINKS     0x010  /* Ignore broken hyperlinks */
#define WIKI_LINKSONLY      0x020  /* No markup.  Only decorate links */
#define WIKI_NEWLINE        0x040  /* Honor \n - break lines at each \n */
#define WIKI_MARKDOWNLINKS  0x080  /* Resolve hyperlinks as in markdown */
#define WIKI_SAFE           0x100  /* Make the result safe for embedding */
#define WIKI_TARGET_BLANK   0x200  /* Hyperlinks go to a new window */
#endif


/*
** These are the only markup attributes allowed.
*/
enum allowed_attr_t {
1254
1255
1256
1257
1258
1259
1260



1261
1262
1263
1264
1265
1266
1267
  const char *z;
  char *zExtra = 0;
  const char *zExtraNS = 0;
  char *zRemote = 0;

  if( zTitle ){
    zExtra = mprintf(" title='%h'", zTitle);



    zExtraNS = zExtra+1;
  }
  assert( nClose>=20 );
  if( strncmp(zTarget, "http:", 5)==0
   || strncmp(zTarget, "https:", 6)==0
   || strncmp(zTarget, "ftp:", 4)==0
   || strncmp(zTarget, "mailto:", 7)==0







>
>
>







1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
  const char *z;
  char *zExtra = 0;
  const char *zExtraNS = 0;
  char *zRemote = 0;

  if( zTitle ){
    zExtra = mprintf(" title='%h'", zTitle);
    zExtraNS = zExtra+1;
  }else if( mFlags & WIKI_TARGET_BLANK ){
    zExtra = mprintf(" target='_blank'");
    zExtraNS = zExtra+1;
  }
  assert( nClose>=20 );
  if( strncmp(zTarget, "http:", 5)==0
   || strncmp(zTarget, "https:", 6)==0
   || strncmp(zTarget, "ftp:", 4)==0
   || strncmp(zTarget, "mailto:", 7)==0

Changes to src/xfersetup.c.

83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
      url_enable_proxy(0);
      @ <pre class="xfersetup">
      client_sync(syncFlags, 0, 0, 0);
      @ </pre>
    }
  }

  style_finish_page("xfersetup");
}

/*
** Common implementation for the transfer setup editor pages.
*/
static void xfersetup_generic(
  const char *zTitle,           /* Page title */







|







83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
      url_enable_proxy(0);
      @ <pre class="xfersetup">
      client_sync(syncFlags, 0, 0, 0);
      @ </pre>
    }
  }

  style_finish_page();
}

/*
** Common implementation for the transfer setup editor pages.
*/
static void xfersetup_generic(
  const char *zTitle,           /* Page title */
114
115
116
117
118
119
120

121
122
123
124
125
126
127
    cgi_redirect("xfersetup");
  }
  isSubmit = P("submit")!=0;
  z = P("x");
  if( z==0 ){
    z = db_get(zDbField, zDfltValue);
  }

  style_header("Edit %s", zTitle);
  if( P("clear")!=0 ){
    login_verify_csrf_secret();
    db_unset(zDbField, 0);
    if( xRebuild ) xRebuild();
    z = zDfltValue;
  }else if( isSubmit ){







>







114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
    cgi_redirect("xfersetup");
  }
  isSubmit = P("submit")!=0;
  z = P("x");
  if( z==0 ){
    z = db_get(zDbField, zDfltValue);
  }
  style_set_current_feature("xfersetup");
  style_header("Edit %s", zTitle);
  if( P("clear")!=0 ){
    login_verify_csrf_secret();
    db_unset(zDbField, 0);
    if( xRebuild ) xRebuild();
    z = zDfltValue;
  }else if( isSubmit ){
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
  if ( zDfltValue ){
    @ <hr />
    @ <h2>Default %s(zTitle)</h2>
    @ <blockquote><pre>
    @ %h(zDfltValue)
    @ </pre></blockquote>
  }
  style_finish_page("xfersetup");
}

static const char *zDefaultXferCommon = 0;

/*
** WEBPAGE: xfersetup_com
** View or edit the TH1 script that runs prior to receiving a







|







149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
  if ( zDfltValue ){
    @ <hr />
    @ <h2>Default %s(zTitle)</h2>
    @ <blockquote><pre>
    @ %h(zDfltValue)
    @ </pre></blockquote>
  }
  style_finish_page();
}

static const char *zDefaultXferCommon = 0;

/*
** WEBPAGE: xfersetup_com
** View or edit the TH1 script that runs prior to receiving a

Changes to src/zip.c.

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
  blob_appendf(&cacheKey, "/%s/%z", g.zPath, rid_to_uuid(rid));
  blob_appendf(&cacheKey, "/%q", zName);
  if( zInclude ) blob_appendf(&cacheKey, ",in=%Q", zInclude);
  if( zExclude ) blob_appendf(&cacheKey, ",ex=%Q", zExclude);
  zKey = blob_str(&cacheKey);
  etag_check(ETAG_HASH, zKey);


  if( P("debug")!=0 ){
    style_header("%s Archive Generator Debug Screen", zType);
    @ zName = "%h(zName)"<br />
    @ rid = %d(rid)<br />
    if( zInclude ){
      @ zInclude = "%h(zInclude)"<br />
    }
    if( zExclude ){
      @ zExclude = "%h(zExclude)"<br />
    }
    @ zKey = "%h(zKey)"
    style_finish_page("zip");
    return;
  }
  if( referred_from_login() ){
    style_header("%s Archive Download", zType);
    @ <form action='%R/%s(g.zPath)/%h(zName).%s(g.zPath)'>
    cgi_query_parameters_to_hidden();
    @ <p>%s(zType) Archive named <b>%h(zName).%s(g.zPath)</b>
    @ holding the content of check-in <b>%h(zRid)</b>:
    @ <input type="submit" value="Download" />
    @ </form>
    style_finish_page("zip");
    return;
  }
  blob_zero(&zip);
  if( cache_read(&zip, zKey)==0 ){
    zip_of_checkin(eType, rid, &zip, zName, pInclude, pExclude);
    cache_write(&zip, zKey);
  }







>











|










|







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
  blob_appendf(&cacheKey, "/%s/%z", g.zPath, rid_to_uuid(rid));
  blob_appendf(&cacheKey, "/%q", zName);
  if( zInclude ) blob_appendf(&cacheKey, ",in=%Q", zInclude);
  if( zExclude ) blob_appendf(&cacheKey, ",ex=%Q", zExclude);
  zKey = blob_str(&cacheKey);
  etag_check(ETAG_HASH, zKey);

  style_set_current_feature("zip");
  if( P("debug")!=0 ){
    style_header("%s Archive Generator Debug Screen", zType);
    @ zName = "%h(zName)"<br />
    @ rid = %d(rid)<br />
    if( zInclude ){
      @ zInclude = "%h(zInclude)"<br />
    }
    if( zExclude ){
      @ zExclude = "%h(zExclude)"<br />
    }
    @ zKey = "%h(zKey)"
    style_finish_page();
    return;
  }
  if( referred_from_login() ){
    style_header("%s Archive Download", zType);
    @ <form action='%R/%s(g.zPath)/%h(zName).%s(g.zPath)'>
    cgi_query_parameters_to_hidden();
    @ <p>%s(zType) Archive named <b>%h(zName).%s(g.zPath)</b>
    @ holding the content of check-in <b>%h(zRid)</b>:
    @ <input type="submit" value="Download" />
    @ </form>
    style_finish_page();
    return;
  }
  blob_zero(&zip);
  if( cache_read(&zip, zKey)==0 ){
    zip_of_checkin(eType, rid, &zip, zName, pInclude, pExclude);
    cache_write(&zip, zKey);
  }

Added tools/chat.tcl.



























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
#!/usr/bin/wapptclsh
#
# A chat program designed to run using the extcgi mechanism of Fossil.
#
encoding system utf-8

# The name of the chat database file
#
proc chat-db-name {} {
  set x [wapp-param SCRIPT_FILENAME]
  set dir [file dir $x]
  set fn [file tail $x]
  return $dir/-$fn.db
}

# Verify permission to use chat.  Return true if not authorized.
# Return false if the Fossil user is allowed to access chat.
#
proc not-authorized {} {
  set cap [wapp-param FOSSIL_CAPABILITIES]
  return [expr {![string match *i* $cap]}]
}

# The default page.
# Load the initial chat screen.
#
proc wapp-default {} {
  wapp-content-security-policy off
  wapp-trim {
    <div class="fossil-doc" data-title="Chat">
  }
  if {[not-authorized]} {
    wapp-trim {
      <h1>Not authorized</h1>
      <p>You must have privileges to use this chatroom</p>
      </div>
    }
    return
  }
  set scriptFile [wapp-param SCRIPT_FILENAME]
  set cgiFn [file tail $scriptFile]
  wapp-trim {
    <form accept-encoding="utf-8" id="chat-form">
    <div id='chat-input-area'>
      <div id='chat-input-line'>
        <input type="text" name="msg" id="sbox" placeholder="Type message here.">
        <input type="submit" value="Send">
      </div>
      <div id='chat-input-file'>
        <span>File:</span>
        <input type="file" name="file">
      </div>
    </div>
    </form>
    <hr>
    <span id='message-inject-point'><!--
    new chat messages get inserted immediately after this element
    --></span>

    </div><!-- .fossil-doc -->
    <hr>
    <p>
    <a href="%string($cgiFn)/env">CGI environment</a> |
    <a href="%string($cgiFn)/self">Wapp script</a>
    <style>
\#dialog {
  width: 97%;
}
\#chat-input-area {
  width: 100%;
  display: flex;
  flex-direction: column;
}
\#chat-input-line {
  display: flex;
  flex-direction: row;
  margin-bottom: 1em;
  align-items: center;
}
\#chat-input-line > input[type=submit] {
  flex: 1 5 auto;
  max-width: 6em;
}
\#chat-input-line > input[type=text] {
  flex: 5 1 auto;
}
\#chat-input-file {
  display: flex;
  flex-direction: row;
  align-items: center;
}
\#chat-input-file > input {
  flex: 1 0 auto;
}
span.at-name { /* for @USERNAME references */
  text-decoration: underline;
  font-weight: bold;
}
/* A wrapper for a single single message (one row of the UI) */
.message-row {
  margin-bottom: 0.5em;
  border: none;
  display: flex;
  flex-direction: row;
  justify-content: flex-start;
  /*border: 1px solid rgba(0,0,0,0.2);
  border-radius: 0.25em;
  box-shadow: 0.2em 0.2em 0.2em rgba(0, 0, 0, 0.29);*/
  border: none;
}
/* Rows for the current user have the .user-is-me CSS class
   and get right-aligned. */
.message-row.user-is-me {
  justify-content: flex-end;
  /*background-color: #d2dde1;*/
}
/* The content area of a message (the body element of a FIELDSET) */
.message-content {
  display: inline-block;
  border-radius: 0.25em;
  border: 1px solid rgba(0,0,0,0.2);
  box-shadow: 0.2em 0.2em 0.2em rgba(0, 0, 0, 0.29);
  padding: 0.25em 1em;
  margin-top: -0.75em;
}
.message-row.user-is-me .message-content {
  background-color: #d2dde1;
}
/* User name for the post (a LEGEND element) */
.message-row .message-user {
  background: inherit;
  border-radius: 0.25em 0.25em 0 0;
  padding: 0 0.5em;
  /*text-align: left; Firefox requires the 'align' attribute */
  margin-left: 0.25em;
  padding: 0 0.5em 0em 0.5em;
  margin-bottom: 0.4em;
  background-color: #d2dde1;
}
/* Reposition "my" posts to the right */
.message-row.user-is-me .message-user {
  /*text-align: right; Firefox requires the 'align' attribute */
  margin-left: 0;
  margin-right: 0.25em;
}
</style>
  }
  set nonce [wapp-param FOSSIL_NONCE]
  set submiturl [wapp-param SCRIPT_NAME]/send
  set pollurl [wapp-param SCRIPT_NAME]/poll
  set downloadurl [wapp-param SCRIPT_NAME]/download
  set me [wapp-param FOSSIL_USER]
  wapp-trim {
<script nonce="%string($nonce)">
(function(){
  const form = document.querySelector('#chat-form');
  let mxMsg = 0;
  let _me = "%string($me)";
  form.addEventListener('submit',(e)=>{
    e.preventDefault();
    if( form.msg.value.length>0 || form.file.value.length>0 ){
      fetch("%string($submiturl)",{
        method: 'POST',
        body: new FormData(form)
      });
    }
    form.msg.value = "";
    form.file.value = "";
    form.msg.focus();
  });
  const rxUrl = /\\b(?:https?|ftp):\\/\\/\[a-z0-9-+&@\#\\/%?=~_|!:,.;]*\[a-z0-9-+&@\#\\/%=~_|]/gim;
  const rxAtName = /@\\w+/gmi;
  // ^^^ achtung, extra backslashes needed for the outer TCL.
  const textNode = (T)=>document.createTextNode(T);

  // Converts a message string to a message-containing DOM element
  // and returns that element, which may contain child elements.
  // If 2nd arg is passed, it must be a DOM element to which all
  // child elements are appended.
  const messageToDOM = function f(str, tgtElem){
    "use strict";
    if(!f.rxUrl){
      f.rxUrl = rxUrl;
      f.rxAt = rxAtName;
      f.rxNS = /\\S/;
      f.ce = (T)=>document.createElement(T);
      f.ct = (T)=>document.createTextNode(T);
      f.replaceUrls = function ff(sub, offset, whole){
        if(offset > ff.prevStart){
          f.accum.push((ff.prevStart?' ':'')+whole.substring(ff.prevStart, offset-1)+' ');
        }
        const a = f.ce('a');
        a.setAttribute('href',sub);
        a.setAttribute('target','_blank');
        a.appendChild(f.ct(sub));
        f.accum.push(a);
        ff.prevStart = offset + sub.length + 1;
      };
      f.replaceAtName = function ff(sub, offset,whole){
        if(offset > ff.prevStart){
          ff.accum.push((ff.prevStart?' ':'')+whole.substring(ff.prevStart, offset-1)+' ');
        }else if(offset && f.rxNS.test(whole[offset-1])){
          // Sigh: https://stackoverflow.com/questions/52655367
          ff.accum.push(sub);
          return;
        }
        const e = f.ce('span');
        e.classList.add('at-name');
        e.appendChild(f.ct(sub));
        ff.accum.push(e);
        ff.prevStart = offset + sub.length + 1;
      };
    }
    f.accum = []; // accumulate strings and DOM elements here.
    f.rxUrl.lastIndex = f.replaceUrls.prevStart = 0; // reset regex cursor
    str.replace(f.rxUrl, f.replaceUrls);
    // Push remaining non-URL part of the string to the queue...
    if(f.replaceUrls.prevStart < str.length){
      f.accum.push((f.replaceUrls.prevStart?' ':'')+str.substring(f.replaceUrls.prevStart));
    }
    // Pass 2: process @NAME references...
    // TODO: only match NAME if it's the name of a currently participating
    // user. Add a second class if NAME == current user, and style that one
    // differently so that people can more easily see when they're spoken to.
    const accum2 = f.replaceAtName.accum = [];
    //console.debug("f.accum =",f.accum);
    f.accum.forEach(function(v){
      //console.debug("v =",v);
      if('string'===typeof v){
        f.rxAt.lastIndex = f.replaceAtName.prevStart = 0;
        v.replace(f.rxAt, f.replaceAtName);
        if(f.replaceAtName.prevStart < v.length){
          accum2.push((f.replaceAtName.prevStart?' ':'')+v.substring(f.replaceAtName.prevStart));
        }
      }else{
        accum2.push(v);
      }
      //console.debug("accum2 =",accum2);
    });
    delete f.accum;
    //console.debug("accum2 =",accum2);
    const span = tgtElem || f.ce('span');
    accum2.forEach(function(e){
      if('string'===typeof e) e = f.ct(e);
      span.appendChild(e);
    });
    //console.debug("span =",span.innerHTML);
    return span;
  }/*end messageToDOM()*/;
  /* Injects element e as a new row in the chat, at the top of the list */
  const injectMessage = function f(e){
    if(!f.injectPoint){
      f.injectPoint = document.querySelector('#message-inject-point');
    }
    if(f.injectPoint.nextSibling){
      f.injectPoint.parentNode.insertBefore(e, f.injectPoint.nextSibling);
    }else{
      f.injectPoint.parentNode.appendChild(e);
    }
  };
  /** Returns the local time string of Date object d, defaulting
      to the current time. */
  const localTimeString = function ff(d){
    if(!ff.pad){
      ff.pad = (x)=>(''+x).length>1 ? x : '0'+x;
    }
    d || (d = new Date());
    return [
      d.getFullYear(),'-',ff.pad(d.getMonth()+1/*sigh*/),
      '-',ff.pad(d.getDate()),
      ' ',ff.pad(d.getHours()),':',ff.pad(d.getMinutes()),
      ':',ff.pad(d.getSeconds())
    ].join('');
  };
  function newcontent(jx){
    var i;
    for(i=0; i<jx.msgs.length; ++i){
      let m = jx.msgs[i];
      let row = document.createElement("fieldset");
      if( m.msgid>mxMsg ) mxMsg = m.msgid;
      row.classList.add('message-row');
      injectMessage(row);
      const eWho = document.createElement('legend');
      eWho.setAttribute('align', (m.xfrom===_me ? 'right' : 'left'));
      row.appendChild(eWho);
      eWho.classList.add('message-user');
      let whoName;
      if( m.xfrom===_me ){
        whoName = 'me';
        row.classList.add('user-is-me');
      }else{
        whoName = m.xfrom;
      }
      eWho.append(textNode(
                  whoName+' @ '+
                  localTimeString(new Date(Date.parse(m.mtime+".000Z"))))
      );
      let span = document.createElement("div");
      span.classList.add('message-content');
      row.appendChild(span);
      if( m.fsize>0 ){
        if( m.fmime && m.fmime.startsWith("image/") ){
          let img = document.createElement("img");
          img.src = "%string($downloadurl)/" + m.msgid;
          span.appendChild(img);
        }else{
          let a = document.createElement("a");
          let txt = "(" + m.fname + " " + m.fsize + " bytes)";
          a.href = "%string($downloadurl)/" + m.msgid;
          a.appendChild(document.createTextNode(txt));
          span.appendChild(a);
        }
        let br = document.createElement("br");
        br.style.clear = "both";
        span.appendChild(br);
      }
      if(m.xmsg){
        messageToDOM(m.xmsg, span);
      }
      span.classList.add('chat-message');
      if( m.xfrom!=_me ){
        span.classList.add('chat-mx');
      }else{
        span.classList.add('chat-ms');
      }
    }
  }
  async function poll(){
    if(poll.running) return;
    poll.running = true;
    fetch("%string($pollurl)/" + mxMsg)
    .then(x=>x.json())
    .then(y=>newcontent(y))
    .finally(()=>poll.running=false)
  }
  setInterval(poll, 1000);
})();</script>
  }

  # Make sure the chat database exists
  sqlite3 db [chat-db-name]
  if {[db one {PRAGMA journal_mode}]!="wal"} {
    db eval {PRAGMA journal_mode=WAL}
  }
  db eval {
    CREATE TABLE IF NOT EXISTS chat(
      msgid INTEGER PRIMARY KEY AUTOINCREMENT,
      mtime JULIANDAY,
      xfrom TEXT,
      xto   TEXT,
      xmsg  TEXT,
      file  BLOB,
      fname TEXT,
      fmime TEXT
    );
    CREATE TABLE IF NOT EXISTS ustat(
      uname TEXT PRIMARY KEY,
      mtime JULIANDAY,  -- Last interaction
      seen  INT,        -- Last message seen
      logout JULIANDAY
    ) WITHOUT ROWID;
  }
  db close
}

# Show the CGI environment.  Used for testing only.
#
proc wapp-page-env {} {
  wapp-trim {
    <div class="fossil-doc" data-title="Chat CGI Environment">
    <pre>%html([wapp-debug-env])</pre>
    </div>
  }
}

# Log the CGI environment into the "-logfile.txt" file in the same
# directory as the script.  Used for testing and development only.
#
proc logenv {} {
  set fn [file dir [wapp-param SCRIPT_FILENAME]]/-logfile.txt
  set out [open $fn a]
  puts $out {************************************************************}
  puts $out [wapp-debug-env]
  close $out
}

# A no-op page.  Used for testing and development only.
#
proc noop-page {} {
  wapp-trim {
    <div class="fossil-doc" data-title="No-op"><h1>No-Op</h1></div>
  }
}

# Accept a new post via XHR.
# No reply expected.
#
proc wapp-page-send {} {
  if {[not-authorized]} return
  set user [wapp-param FOSSIL_USER]
  set fcontent [wapp-param file.content]
  set fname [wapp-param file.filename]
  set fmime [wapp-param file.mimetype]
  set msg [wapp-param msg]
  sqlite3 db [chat-db-name]
  db eval BEGIN
  if {$fcontent!=""} {
    db eval {
      INSERT INTO chat(mtime,xfrom,xmsg,file,fname,fmime)
      VALUES(julianday('now'),$user,@msg,@fcontent,$fname,$fmime)
    }
  } else {
    db eval {
      INSERT INTO chat(mtime,xfrom,xmsg)
      VALUES(julianday('now'),$user,@msg)
    }
  }
  db eval {
    INSERT INTO ustat(uname,mtime,seen) VALUES($user,julianday('now'),0)
    ON CONFLICT(uname) DO UPDATE set mtime=julianday('now')
  }
  db eval COMMIT
  db close
}

# Request updates.
# Delay the response until something changes (as this system works
# using the Hanging-GET or Long-Poll style of server-push).
# The result is javascript describing the new content.
#
# Call is like this:   /poll/N
# Where N is the last message received so far.  The reply stalls
# until newer messages are available.
#
proc wapp-page-poll {} {
  if {[not-authorized]} return
  wapp-mimetype text/json
  set msglist {}
  sqlite3 db [chat-db-name]
  set id 0
  scan [wapp-param PATH_TAIL] %d id
  while {1} {
    set datavers [db one {PRAGMA data_version}]
    db eval {SELECT msgid, datetime(mtime) AS dx, xfrom, CAST(xmsg AS text) mx,
                    length(file) AS lx, fname, fmime
               FROM chat
              WHERE msgid>$id
              ORDER BY msgid} {
      set quname [string map {\" \\\"} $xfrom]
      set qmsg [string map {\" \\\"} $mx]
      if {$lx==""} {set lx 0}
      set qfname [string map {\" \\\"} $fname]
      lappend msglist "\173\"msgid\":$msgid,\"mtime\":\"$dx\",\
        \"xfrom\":\"$quname\",\
        \"xmsg\":\"$qmsg\",\"fsize\":$lx,\
        \"fname\":\"$qfname\",\"fmime\":\"$fmime\"\175"
    }
    if {[llength $msglist]>0} {
      wapp-unsafe "\173\042msgs\042:\133[join $msglist ,]\135\175"
      db close
      return
    }
    after 2000
    while {[db one {PRAGMA data_version}]==$datavers} {after 2000}
  }
}

# Show the text of this script.
#
proc wapp-page-self {} {
  wapp-trim {
    <div class="fossil-doc" data-title="Wapp Script for Chat">
  }
  set fd [open [wapp-param SCRIPT_FILENAME] rb]
  set script [read $fd]
  wapp-trim {
    <pre>%html($script)</pre>
  }
  wapp-trim {
    </div>
  }
}

# Download the file associated with a message.
#
# Call like this:   /download/N
# Where N is the message id.
#
proc wapp-page-download {} {
  if {[not-authorized]} {
    wapp-trim {
      <h1>Not authorized</h1>
      <p>You must have privileges to use this chatroom</p>
      </div>
    }
    return
  }
  set id 0
  scan [wapp-param PATH_TAIL] %d id
  sqlite3 db [chat-db-name]
  db eval {SELECT fname, fmime, file FROM chat WHERE msgid=$id} {
    wapp-mimetype $fmime
    wapp $file
  }
  db close
}


wapp-start $argv

Changes to tools/fossil-stress.tcl.

13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  }
  if {$x=="-threads"} {
    incr i
    set nthread [lindex $argv $i]
  } elseif {[string index $x 0]=="-"} {
    error "unknown option \"$x\""
  } elseif {[info exists url]} {
    error "unknown argment \"$x\""
  } else {
    set url $x
  }
}
if {![info exists url]} {
  error "Usage: $argv0 [-threads N] URL"
}







|







13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  }
  if {$x=="-threads"} {
    incr i
    set nthread [lindex $argv $i]
  } elseif {[string index $x 0]=="-"} {
    error "unknown option \"$x\""
  } elseif {[info exists url]} {
    error "unknown argument \"$x\""
  } else {
    set url $x
  }
}
if {![info exists url]} {
  error "Usage: $argv0 [-threads N] URL"
}

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
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_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_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 ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c 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 hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pikchr_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c

OBJ   = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\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)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchr$O $(OBJDIR)\pikchrshow$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O


RC=$(DMDIR)\bin\rcc
RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__

APPNAME = $(OBJDIR)\fossil$(E)

all: $(APPNAME)

$(APPNAME) : translate$E mkindex$E codecheck1$E headers  $(OBJ) $(OBJDIR)\link
	cd $(OBJDIR)
	codecheck1$E $(SRC)
	$(DMDIR)\bin\link @link

$(OBJDIR)\fossil.res:	$B\win\fossil.rc
	$(RC) $(RCFLAGS) -o$@ $**

$(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
	+echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi 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 hook http http_socket http_ssl http_transport import info interwiki json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pikchr pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@
	+echo fossil >> $@
	+echo fossil >> $@
	+echo $(LIBS) >> $@
	+echo. >> $@
	+echo fossil >> $@

translate$E: $(SRCDIR)\translate.c







|

|


















|







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_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_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 ajax_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c chat_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c fileedit_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c hook_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c interwiki_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pikchr_.c pikchrshow_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c xfer_.c xfersetup_.c zip_.c

OBJ   = $(OBJDIR)\add$O $(OBJDIR)\ajax$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\chat$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\fileedit$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\hook$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\interwiki$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pikchr$O $(OBJDIR)\pikchrshow$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O


RC=$(DMDIR)\bin\rcc
RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__

APPNAME = $(OBJDIR)\fossil$(E)

all: $(APPNAME)

$(APPNAME) : translate$E mkindex$E codecheck1$E headers  $(OBJ) $(OBJDIR)\link
	cd $(OBJDIR)
	codecheck1$E $(SRC)
	$(DMDIR)\bin\link @link

$(OBJDIR)\fossil.res:	$B\win\fossil.rc
	$(RC) $(RCFLAGS) -o$@ $**

$(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
	+echo add ajax alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi chat checkin checkout clearsign clone comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file fileedit finfo foci forum fshell fusefs fuzz glob graph gzip hname hook http http_socket http_ssl http_transport import info interwiki json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pikchr pikchrshow pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp xfer xfersetup zip shell sqlite3 th th_lang > $@
	+echo fossil >> $@
	+echo fossil >> $@
	+echo $(LIBS) >> $@
	+echo. >> $@
	+echo fossil >> $@

translate$E: $(SRCDIR)\translate.c
227
228
229
230
231
232
233






234
235
236
237
238
239
240
	+translate$E $** > $@

$(OBJDIR)\cgi$O : cgi_.c cgi.h
	$(TCC) -o$@ -c cgi_.c

cgi_.c : $(SRCDIR)\cgi.c
	+translate$E $** > $@







$(OBJDIR)\checkin$O : checkin_.c checkin.h
	$(TCC) -o$@ -c checkin_.c

checkin_.c : $(SRCDIR)\checkin.c
	+translate$E $** > $@








>
>
>
>
>
>







227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
	+translate$E $** > $@

$(OBJDIR)\cgi$O : cgi_.c cgi.h
	$(TCC) -o$@ -c cgi_.c

cgi_.c : $(SRCDIR)\cgi.c
	+translate$E $** > $@

$(OBJDIR)\chat$O : chat_.c chat.h
	$(TCC) -o$@ -c chat_.c

chat_.c : $(SRCDIR)\chat.c
	+translate$E $** > $@

$(OBJDIR)\checkin$O : checkin_.c checkin.h
	$(TCC) -o$@ -c checkin_.c

checkin_.c : $(SRCDIR)\checkin.c
	+translate$E $** > $@

997
998
999
1000
1001
1002
1003
1004
1005
$(OBJDIR)\zip$O : zip_.c zip.h
	$(TCC) -o$@ -c zip_.c

zip_.c : $(SRCDIR)\zip.c
	+translate$E $** > $@

headers: makeheaders$E page_index.h builtin_data.h VERSION.h
	 +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h 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 hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h interwiki_.c:interwiki.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pikchr_.c:pikchr.h pikchrshow_.c:pikchrshow.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
	@copy /Y nul: headers







|

1003
1004
1005
1006
1007
1008
1009
1010
1011
$(OBJDIR)\zip$O : zip_.c zip.h
	$(TCC) -o$@ -c zip_.c

zip_.c : $(SRCDIR)\zip.c
	+translate$E $** > $@

headers: makeheaders$E page_index.h builtin_data.h VERSION.h
	 +makeheaders$E add_.c:add.h ajax_.c:ajax.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h chat_.c:chat.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h fileedit_.c:fileedit.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h hook_.c:hook.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h interwiki_.c:interwiki.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pikchr_.c:pikchr.h pikchrshow_.c:pikchrshow.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
	@copy /Y nul: headers

Changes to win/Makefile.mingw.

442
443
444
445
446
447
448

449
450
451
452
453
454
455
  $(SRCDIR)/browse.c \
  $(SRCDIR)/builtin.c \
  $(SRCDIR)/bundle.c \
  $(SRCDIR)/cache.c \
  $(SRCDIR)/capabilities.c \
  $(SRCDIR)/captcha.c \
  $(SRCDIR)/cgi.c \

  $(SRCDIR)/checkin.c \
  $(SRCDIR)/checkout.c \
  $(SRCDIR)/clearsign.c \
  $(SRCDIR)/clone.c \
  $(SRCDIR)/comformat.c \
  $(SRCDIR)/configure.c \
  $(SRCDIR)/content.c \







>







442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
  $(SRCDIR)/browse.c \
  $(SRCDIR)/builtin.c \
  $(SRCDIR)/bundle.c \
  $(SRCDIR)/cache.c \
  $(SRCDIR)/capabilities.c \
  $(SRCDIR)/captcha.c \
  $(SRCDIR)/cgi.c \
  $(SRCDIR)/chat.c \
  $(SRCDIR)/checkin.c \
  $(SRCDIR)/checkout.c \
  $(SRCDIR)/clearsign.c \
  $(SRCDIR)/clone.c \
  $(SRCDIR)/comformat.c \
  $(SRCDIR)/configure.c \
  $(SRCDIR)/content.c \
629
630
631
632
633
634
635





636
637
638
639
640
641
642
  $(SRCDIR)/../skins/rounded1/footer.txt \
  $(SRCDIR)/../skins/rounded1/header.txt \
  $(SRCDIR)/../skins/xekri/css.txt \
  $(SRCDIR)/../skins/xekri/details.txt \
  $(SRCDIR)/../skins/xekri/footer.txt \
  $(SRCDIR)/../skins/xekri/header.txt \
  $(SRCDIR)/accordion.js \





  $(SRCDIR)/ci_edit.js \
  $(SRCDIR)/copybtn.js \
  $(SRCDIR)/default.css \
  $(SRCDIR)/diff.tcl \
  $(SRCDIR)/forum.js \
  $(SRCDIR)/fossil.bootstrap.js \
  $(SRCDIR)/fossil.confirmer.js \







>
>
>
>
>







630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
  $(SRCDIR)/../skins/rounded1/footer.txt \
  $(SRCDIR)/../skins/rounded1/header.txt \
  $(SRCDIR)/../skins/xekri/css.txt \
  $(SRCDIR)/../skins/xekri/details.txt \
  $(SRCDIR)/../skins/xekri/footer.txt \
  $(SRCDIR)/../skins/xekri/header.txt \
  $(SRCDIR)/accordion.js \
  $(SRCDIR)/alerts/bflat2.wav \
  $(SRCDIR)/alerts/bflat3.wav \
  $(SRCDIR)/alerts/bloop.wav \
  $(SRCDIR)/alerts/plunk.wav \
  $(SRCDIR)/chat.js \
  $(SRCDIR)/ci_edit.js \
  $(SRCDIR)/copybtn.js \
  $(SRCDIR)/default.css \
  $(SRCDIR)/diff.tcl \
  $(SRCDIR)/forum.js \
  $(SRCDIR)/fossil.bootstrap.js \
  $(SRCDIR)/fossil.confirmer.js \
700
701
702
703
704
705
706

707
708
709
710
711
712
713
  $(OBJDIR)/browse_.c \
  $(OBJDIR)/builtin_.c \
  $(OBJDIR)/bundle_.c \
  $(OBJDIR)/cache_.c \
  $(OBJDIR)/capabilities_.c \
  $(OBJDIR)/captcha_.c \
  $(OBJDIR)/cgi_.c \

  $(OBJDIR)/checkin_.c \
  $(OBJDIR)/checkout_.c \
  $(OBJDIR)/clearsign_.c \
  $(OBJDIR)/clone_.c \
  $(OBJDIR)/comformat_.c \
  $(OBJDIR)/configure_.c \
  $(OBJDIR)/content_.c \







>







706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
  $(OBJDIR)/browse_.c \
  $(OBJDIR)/builtin_.c \
  $(OBJDIR)/bundle_.c \
  $(OBJDIR)/cache_.c \
  $(OBJDIR)/capabilities_.c \
  $(OBJDIR)/captcha_.c \
  $(OBJDIR)/cgi_.c \
  $(OBJDIR)/chat_.c \
  $(OBJDIR)/checkin_.c \
  $(OBJDIR)/checkout_.c \
  $(OBJDIR)/clearsign_.c \
  $(OBJDIR)/clone_.c \
  $(OBJDIR)/comformat_.c \
  $(OBJDIR)/configure_.c \
  $(OBJDIR)/content_.c \
848
849
850
851
852
853
854

855
856
857
858
859
860
861
 $(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 \







>







855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
 $(OBJDIR)/browse.o \
 $(OBJDIR)/builtin.o \
 $(OBJDIR)/bundle.o \
 $(OBJDIR)/cache.o \
 $(OBJDIR)/capabilities.o \
 $(OBJDIR)/captcha.o \
 $(OBJDIR)/cgi.o \
 $(OBJDIR)/chat.o \
 $(OBJDIR)/checkin.o \
 $(OBJDIR)/checkout.o \
 $(OBJDIR)/clearsign.o \
 $(OBJDIR)/clone.o \
 $(OBJDIR)/comformat.o \
 $(OBJDIR)/configure.o \
 $(OBJDIR)/content.o \
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177

APPTARGETS += $(BLDTARGETS)

ifdef FOSSIL_BUILD_SSL
APPTARGETS += openssl
endif

$(APPNAME):	$(APPTARGETS) $(OBJDIR)/headers $(CODECHECK1) $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o
	$(CODECHECK1) $(TRANS_SRC)
	$(TCC) -o $@ $(OBJ) $(EXTRAOBJ) $(OBJDIR)/fossil.o $(LIB)

# This rule prevents make from using its default rules to try build
# an executable named "manifest" out of the file named "manifest.c"
#
$(SRCDIR)/../manifest:
	# noop








|

|







1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185

APPTARGETS += $(BLDTARGETS)

ifdef FOSSIL_BUILD_SSL
APPTARGETS += openssl
endif

$(APPNAME):	$(APPTARGETS) $(OBJDIR)/headers $(CODECHECK1) $(EXTRAOBJ) $(OBJ) $(OBJDIR)/fossil.o
	$(CODECHECK1) $(TRANS_SRC)
	$(TCC) -o $@ $(EXTRAOBJ) $(OBJ) $(OBJDIR)/fossil.o $(LIB)

# This rule prevents make from using its default rules to try build
# an executable named "manifest" out of the file named "manifest.c"
#
$(SRCDIR)/../manifest:
	# noop

1211
1212
1213
1214
1215
1216
1217

1218
1219
1220
1221
1222
1223
1224
		$(OBJDIR)/browse_.c:$(OBJDIR)/browse.h \
		$(OBJDIR)/builtin_.c:$(OBJDIR)/builtin.h \
		$(OBJDIR)/bundle_.c:$(OBJDIR)/bundle.h \
		$(OBJDIR)/cache_.c:$(OBJDIR)/cache.h \
		$(OBJDIR)/capabilities_.c:$(OBJDIR)/capabilities.h \
		$(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h \
		$(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h \

		$(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h \
		$(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h \
		$(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h \
		$(OBJDIR)/clone_.c:$(OBJDIR)/clone.h \
		$(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \
		$(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \
		$(OBJDIR)/content_.c:$(OBJDIR)/content.h \







>







1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
		$(OBJDIR)/browse_.c:$(OBJDIR)/browse.h \
		$(OBJDIR)/builtin_.c:$(OBJDIR)/builtin.h \
		$(OBJDIR)/bundle_.c:$(OBJDIR)/bundle.h \
		$(OBJDIR)/cache_.c:$(OBJDIR)/cache.h \
		$(OBJDIR)/capabilities_.c:$(OBJDIR)/capabilities.h \
		$(OBJDIR)/captcha_.c:$(OBJDIR)/captcha.h \
		$(OBJDIR)/cgi_.c:$(OBJDIR)/cgi.h \
		$(OBJDIR)/chat_.c:$(OBJDIR)/chat.h \
		$(OBJDIR)/checkin_.c:$(OBJDIR)/checkin.h \
		$(OBJDIR)/checkout_.c:$(OBJDIR)/checkout.h \
		$(OBJDIR)/clearsign_.c:$(OBJDIR)/clearsign.h \
		$(OBJDIR)/clone_.c:$(OBJDIR)/clone.h \
		$(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \
		$(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \
		$(OBJDIR)/content_.c:$(OBJDIR)/content.h \
1491
1492
1493
1494
1495
1496
1497








1498
1499
1500
1501
1502
1503
1504
$(OBJDIR)/cgi_.c:	$(SRCDIR)/cgi.c $(TRANSLATE)
	$(TRANSLATE) $(SRCDIR)/cgi.c >$@

$(OBJDIR)/cgi.o:	$(OBJDIR)/cgi_.c $(OBJDIR)/cgi.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/cgi.o -c $(OBJDIR)/cgi_.c

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









$(OBJDIR)/checkin_.c:	$(SRCDIR)/checkin.c $(TRANSLATE)
	$(TRANSLATE) $(SRCDIR)/checkin.c >$@

$(OBJDIR)/checkin.o:	$(OBJDIR)/checkin_.c $(OBJDIR)/checkin.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/checkin.o -c $(OBJDIR)/checkin_.c








>
>
>
>
>
>
>
>







1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
$(OBJDIR)/cgi_.c:	$(SRCDIR)/cgi.c $(TRANSLATE)
	$(TRANSLATE) $(SRCDIR)/cgi.c >$@

$(OBJDIR)/cgi.o:	$(OBJDIR)/cgi_.c $(OBJDIR)/cgi.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/cgi.o -c $(OBJDIR)/cgi_.c

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

$(OBJDIR)/chat_.c:	$(SRCDIR)/chat.c $(TRANSLATE)
	$(TRANSLATE) $(SRCDIR)/chat.c >$@

$(OBJDIR)/chat.o:	$(OBJDIR)/chat_.c $(OBJDIR)/chat.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/chat.o -c $(OBJDIR)/chat_.c

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

$(OBJDIR)/checkin_.c:	$(SRCDIR)/checkin.c $(TRANSLATE)
	$(TRANSLATE) $(SRCDIR)/checkin.c >$@

$(OBJDIR)/checkin.o:	$(OBJDIR)/checkin_.c $(OBJDIR)/checkin.h $(SRCDIR)/config.h
	$(XTCC) -o $(OBJDIR)/checkin.o -c $(OBJDIR)/checkin_.c

Changes to win/Makefile.msc.

364
365
366
367
368
369
370

371
372
373
374
375
376
377
        "$(OX)\browse_.c" \
        "$(OX)\builtin_.c" \
        "$(OX)\bundle_.c" \
        "$(OX)\cache_.c" \
        "$(OX)\capabilities_.c" \
        "$(OX)\captcha_.c" \
        "$(OX)\cgi_.c" \

        "$(OX)\checkin_.c" \
        "$(OX)\checkout_.c" \
        "$(OX)\clearsign_.c" \
        "$(OX)\clone_.c" \
        "$(OX)\comformat_.c" \
        "$(OX)\configure_.c" \
        "$(OX)\content_.c" \







>







364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
        "$(OX)\browse_.c" \
        "$(OX)\builtin_.c" \
        "$(OX)\bundle_.c" \
        "$(OX)\cache_.c" \
        "$(OX)\capabilities_.c" \
        "$(OX)\captcha_.c" \
        "$(OX)\cgi_.c" \
        "$(OX)\chat_.c" \
        "$(OX)\checkin_.c" \
        "$(OX)\checkout_.c" \
        "$(OX)\clearsign_.c" \
        "$(OX)\clone_.c" \
        "$(OX)\comformat_.c" \
        "$(OX)\configure_.c" \
        "$(OX)\content_.c" \
550
551
552
553
554
555
556





557
558
559
560
561
562
563
        "$(SRCDIR)\..\skins\rounded1\footer.txt" \
        "$(SRCDIR)\..\skins\rounded1\header.txt" \
        "$(SRCDIR)\..\skins\xekri\css.txt" \
        "$(SRCDIR)\..\skins\xekri\details.txt" \
        "$(SRCDIR)\..\skins\xekri\footer.txt" \
        "$(SRCDIR)\..\skins\xekri\header.txt" \
        "$(SRCDIR)\accordion.js" \





        "$(SRCDIR)\ci_edit.js" \
        "$(SRCDIR)\copybtn.js" \
        "$(SRCDIR)\default.css" \
        "$(SRCDIR)\diff.tcl" \
        "$(SRCDIR)\forum.js" \
        "$(SRCDIR)\fossil.bootstrap.js" \
        "$(SRCDIR)\fossil.confirmer.js" \







>
>
>
>
>







551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
        "$(SRCDIR)\..\skins\rounded1\footer.txt" \
        "$(SRCDIR)\..\skins\rounded1\header.txt" \
        "$(SRCDIR)\..\skins\xekri\css.txt" \
        "$(SRCDIR)\..\skins\xekri\details.txt" \
        "$(SRCDIR)\..\skins\xekri\footer.txt" \
        "$(SRCDIR)\..\skins\xekri\header.txt" \
        "$(SRCDIR)\accordion.js" \
        "$(SRCDIR)\alerts\bflat2.wav" \
        "$(SRCDIR)\alerts\bflat3.wav" \
        "$(SRCDIR)\alerts\bloop.wav" \
        "$(SRCDIR)\alerts\plunk.wav" \
        "$(SRCDIR)\chat.js" \
        "$(SRCDIR)\ci_edit.js" \
        "$(SRCDIR)\copybtn.js" \
        "$(SRCDIR)\default.css" \
        "$(SRCDIR)\diff.tcl" \
        "$(SRCDIR)\forum.js" \
        "$(SRCDIR)\fossil.bootstrap.js" \
        "$(SRCDIR)\fossil.confirmer.js" \
620
621
622
623
624
625
626

627
628
629
630
631
632
633
        "$(OX)\browse$O" \
        "$(OX)\builtin$O" \
        "$(OX)\bundle$O" \
        "$(OX)\cache$O" \
        "$(OX)\capabilities$O" \
        "$(OX)\captcha$O" \
        "$(OX)\cgi$O" \

        "$(OX)\checkin$O" \
        "$(OX)\checkout$O" \
        "$(OX)\clearsign$O" \
        "$(OX)\clone$O" \
        "$(OX)\comformat$O" \
        "$(OX)\configure$O" \
        "$(OX)\content$O" \







>







626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
        "$(OX)\browse$O" \
        "$(OX)\builtin$O" \
        "$(OX)\bundle$O" \
        "$(OX)\cache$O" \
        "$(OX)\capabilities$O" \
        "$(OX)\captcha$O" \
        "$(OX)\cgi$O" \
        "$(OX)\chat$O" \
        "$(OX)\checkin$O" \
        "$(OX)\checkout$O" \
        "$(OX)\clearsign$O" \
        "$(OX)\clone$O" \
        "$(OX)\comformat$O" \
        "$(OX)\configure$O" \
        "$(OX)\content$O" \
849
850
851
852
853
854
855

856
857
858
859
860
861
862
	echo "$(OX)\browse.obj" >> $@
	echo "$(OX)\builtin.obj" >> $@
	echo "$(OX)\bundle.obj" >> $@
	echo "$(OX)\cache.obj" >> $@
	echo "$(OX)\capabilities.obj" >> $@
	echo "$(OX)\captcha.obj" >> $@
	echo "$(OX)\cgi.obj" >> $@

	echo "$(OX)\checkin.obj" >> $@
	echo "$(OX)\checkout.obj" >> $@
	echo "$(OX)\clearsign.obj" >> $@
	echo "$(OX)\clone.obj" >> $@
	echo "$(OX)\comformat.obj" >> $@
	echo "$(OX)\configure.obj" >> $@
	echo "$(OX)\content.obj" >> $@







>







856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
	echo "$(OX)\browse.obj" >> $@
	echo "$(OX)\builtin.obj" >> $@
	echo "$(OX)\bundle.obj" >> $@
	echo "$(OX)\cache.obj" >> $@
	echo "$(OX)\capabilities.obj" >> $@
	echo "$(OX)\captcha.obj" >> $@
	echo "$(OX)\cgi.obj" >> $@
	echo "$(OX)\chat.obj" >> $@
	echo "$(OX)\checkin.obj" >> $@
	echo "$(OX)\checkout.obj" >> $@
	echo "$(OX)\clearsign.obj" >> $@
	echo "$(OX)\clone.obj" >> $@
	echo "$(OX)\comformat.obj" >> $@
	echo "$(OX)\configure.obj" >> $@
	echo "$(OX)\content.obj" >> $@
1156
1157
1158
1159
1160
1161
1162





1163
1164
1165
1166
1167
1168
1169
	echo "$(SRCDIR)\../skins/rounded1/footer.txt" >> $@
	echo "$(SRCDIR)\../skins/rounded1/header.txt" >> $@
	echo "$(SRCDIR)\../skins/xekri/css.txt" >> $@
	echo "$(SRCDIR)\../skins/xekri/details.txt" >> $@
	echo "$(SRCDIR)\../skins/xekri/footer.txt" >> $@
	echo "$(SRCDIR)\../skins/xekri/header.txt" >> $@
	echo "$(SRCDIR)\accordion.js" >> $@





	echo "$(SRCDIR)\ci_edit.js" >> $@
	echo "$(SRCDIR)\copybtn.js" >> $@
	echo "$(SRCDIR)\default.css" >> $@
	echo "$(SRCDIR)\diff.tcl" >> $@
	echo "$(SRCDIR)\forum.js" >> $@
	echo "$(SRCDIR)\fossil.bootstrap.js" >> $@
	echo "$(SRCDIR)\fossil.confirmer.js" >> $@







>
>
>
>
>







1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
	echo "$(SRCDIR)\../skins/rounded1/footer.txt" >> $@
	echo "$(SRCDIR)\../skins/rounded1/header.txt" >> $@
	echo "$(SRCDIR)\../skins/xekri/css.txt" >> $@
	echo "$(SRCDIR)\../skins/xekri/details.txt" >> $@
	echo "$(SRCDIR)\../skins/xekri/footer.txt" >> $@
	echo "$(SRCDIR)\../skins/xekri/header.txt" >> $@
	echo "$(SRCDIR)\accordion.js" >> $@
	echo "$(SRCDIR)\alerts/bflat2.wav" >> $@
	echo "$(SRCDIR)\alerts/bflat3.wav" >> $@
	echo "$(SRCDIR)\alerts/bloop.wav" >> $@
	echo "$(SRCDIR)\alerts/plunk.wav" >> $@
	echo "$(SRCDIR)\chat.js" >> $@
	echo "$(SRCDIR)\ci_edit.js" >> $@
	echo "$(SRCDIR)\copybtn.js" >> $@
	echo "$(SRCDIR)\default.css" >> $@
	echo "$(SRCDIR)\diff.tcl" >> $@
	echo "$(SRCDIR)\forum.js" >> $@
	echo "$(SRCDIR)\fossil.bootstrap.js" >> $@
	echo "$(SRCDIR)\fossil.confirmer.js" >> $@
1315
1316
1317
1318
1319
1320
1321






1322
1323
1324
1325
1326
1327
1328
	"$(OBJDIR)\translate$E" $** > $@

"$(OX)\cgi$O" : "$(OX)\cgi_.c" "$(OX)\cgi.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\cgi_.c"

"$(OX)\cgi_.c" : "$(SRCDIR)\cgi.c"
	"$(OBJDIR)\translate$E" $** > $@







"$(OX)\checkin$O" : "$(OX)\checkin_.c" "$(OX)\checkin.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\checkin_.c"

"$(OX)\checkin_.c" : "$(SRCDIR)\checkin.c"
	"$(OBJDIR)\translate$E" $** > $@








>
>
>
>
>
>







1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
	"$(OBJDIR)\translate$E" $** > $@

"$(OX)\cgi$O" : "$(OX)\cgi_.c" "$(OX)\cgi.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\cgi_.c"

"$(OX)\cgi_.c" : "$(SRCDIR)\cgi.c"
	"$(OBJDIR)\translate$E" $** > $@

"$(OX)\chat$O" : "$(OX)\chat_.c" "$(OX)\chat.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\chat_.c"

"$(OX)\chat_.c" : "$(SRCDIR)\chat.c"
	"$(OBJDIR)\translate$E" $** > $@

"$(OX)\checkin$O" : "$(OX)\checkin_.c" "$(OX)\checkin.h"
	$(TCC) /Fo$@ /Fd$(@D)\ -c "$(OX)\checkin_.c"

"$(OX)\checkin_.c" : "$(SRCDIR)\checkin.c"
	"$(OBJDIR)\translate$E" $** > $@

2106
2107
2108
2109
2110
2111
2112

2113
2114
2115
2116
2117
2118
2119
			"$(OX)\browse_.c":"$(OX)\browse.h" \
			"$(OX)\builtin_.c":"$(OX)\builtin.h" \
			"$(OX)\bundle_.c":"$(OX)\bundle.h" \
			"$(OX)\cache_.c":"$(OX)\cache.h" \
			"$(OX)\capabilities_.c":"$(OX)\capabilities.h" \
			"$(OX)\captcha_.c":"$(OX)\captcha.h" \
			"$(OX)\cgi_.c":"$(OX)\cgi.h" \

			"$(OX)\checkin_.c":"$(OX)\checkin.h" \
			"$(OX)\checkout_.c":"$(OX)\checkout.h" \
			"$(OX)\clearsign_.c":"$(OX)\clearsign.h" \
			"$(OX)\clone_.c":"$(OX)\clone.h" \
			"$(OX)\comformat_.c":"$(OX)\comformat.h" \
			"$(OX)\configure_.c":"$(OX)\configure.h" \
			"$(OX)\content_.c":"$(OX)\content.h" \







>







2125
2126
2127
2128
2129
2130
2131
2132
2133
2134
2135
2136
2137
2138
2139
			"$(OX)\browse_.c":"$(OX)\browse.h" \
			"$(OX)\builtin_.c":"$(OX)\builtin.h" \
			"$(OX)\bundle_.c":"$(OX)\bundle.h" \
			"$(OX)\cache_.c":"$(OX)\cache.h" \
			"$(OX)\capabilities_.c":"$(OX)\capabilities.h" \
			"$(OX)\captcha_.c":"$(OX)\captcha.h" \
			"$(OX)\cgi_.c":"$(OX)\cgi.h" \
			"$(OX)\chat_.c":"$(OX)\chat.h" \
			"$(OX)\checkin_.c":"$(OX)\checkin.h" \
			"$(OX)\checkout_.c":"$(OX)\checkout.h" \
			"$(OX)\clearsign_.c":"$(OX)\clearsign.h" \
			"$(OX)\clone_.c":"$(OX)\clone.h" \
			"$(OX)\comformat_.c":"$(OX)\comformat.h" \
			"$(OX)\configure_.c":"$(OX)\configure.h" \
			"$(OX)\content_.c":"$(OX)\content.h" \

Changes to www/blockchain.md.

78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
    of [digital signatures][dsig] to prevent Type 1 and Type 3 frauds. This
    chain forms an additional link between the blocks, separate from the
    hash chain that applies an ordering and lookup scheme to the blocks.
    [_Blockchain: Simple Explanation_][bse] explains this “hash chain”
    vs. “block chain” distinction in more detail.

    These signatures prevent modification of the face value of each
    transation (Type 1 fraud) by ensuring that only the one signing a
    new block has the private signing key that could change an issued
    block after the fact.

    The fact that these signatures are also *chained* prevents Type
    3 frauds by making the *prior* owner of a block sign it over to
    the new owner. To avoid an O(n²) auditing problem as a result,
    cryptocurrencies add a separate chain of hashes to make checking







|







78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
    of [digital signatures][dsig] to prevent Type 1 and Type 3 frauds. This
    chain forms an additional link between the blocks, separate from the
    hash chain that applies an ordering and lookup scheme to the blocks.
    [_Blockchain: Simple Explanation_][bse] explains this “hash chain”
    vs. “block chain” distinction in more detail.

    These signatures prevent modification of the face value of each
    transaction (Type 1 fraud) by ensuring that only the one signing a
    new block has the private signing key that could change an issued
    block after the fact.

    The fact that these signatures are also *chained* prevents Type
    3 frauds by making the *prior* owner of a block sign it over to
    the new owner. To avoid an O(n²) auditing problem as a result,
    cryptocurrencies add a separate chain of hashes to make checking
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
    Cyrptocurrencies also use the work problem to prevent Type 2
    forgeries, but the application of that to Fossil is a matter we get
    to [later](#work).

    Although you have complete control over the contents of your local
    Fossil repository clone, you cannot perform Type 1 forgery on its
    contents short of executing a [preimage attack][prei] on the hash
    algorthm. ([SHA3-256][SHA-3] by default in the current version of
    Fossil.) Even if you could, Fossil’s sync protocol will prevent the
    modification from being pushed into another repository: the remote
    Fossil instance says, “I’ve already got that one, thanks,” and
    ignores the push.  Thus, short of breaking into the remote server
    and modifying the repository in place, you couldn’t even make use of
    a preimage attack if you had that power. This is an attack on the
    server itself, not on Fossil’s data structures, so while it is







|







117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
    Cyrptocurrencies also use the work problem to prevent Type 2
    forgeries, but the application of that to Fossil is a matter we get
    to [later](#work).

    Although you have complete control over the contents of your local
    Fossil repository clone, you cannot perform Type 1 forgery on its
    contents short of executing a [preimage attack][prei] on the hash
    algorithm. ([SHA3-256][SHA-3] by default in the current version of
    Fossil.) Even if you could, Fossil’s sync protocol will prevent the
    modification from being pushed into another repository: the remote
    Fossil instance says, “I’ve already got that one, thanks,” and
    ignores the push.  Thus, short of breaking into the remote server
    and modifying the repository in place, you couldn’t even make use of
    a preimage attack if you had that power. This is an attack on the
    server itself, not on Fossil’s data structures, so while it is
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
    commit Type 2 frauds. But let us be clear: your choice of setting
    does not answer the question of whether Fossil is a blockchain.)

    If Fossil signatures prevent Type 1 and Type 2 frauds, you
    may wonder why they are not enabled by default. It is because
    they are defense-in-depth measures, not the minimum sufficient
    measures needed to prevent repository fraud, unlike the equivalent
    protections in a cryptocurrency blockcahin. Fossil provides its
    primary protections through other means, so it doesn’t need to
    mandate signatures.

    Also, Fossil is not itself a [PKI], and there is no way for regular
    users of Fossil to link it to a PKI, since doing so would likely
    result in an unwanted [PII] disclosure.  There is no email address
    in a Fossil commit manifest that you could use to query one of the







|







144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
    commit Type 2 frauds. But let us be clear: your choice of setting
    does not answer the question of whether Fossil is a blockchain.)

    If Fossil signatures prevent Type 1 and Type 2 frauds, you
    may wonder why they are not enabled by default. It is because
    they are defense-in-depth measures, not the minimum sufficient
    measures needed to prevent repository fraud, unlike the equivalent
    protections in a cryptocurrency blockchain. Fossil provides its
    primary protections through other means, so it doesn’t need to
    mandate signatures.

    Also, Fossil is not itself a [PKI], and there is no way for regular
    users of Fossil to link it to a PKI, since doing so would likely
    result in an unwanted [PII] disclosure.  There is no email address
    in a Fossil commit manifest that you could use to query one of the

Changes to www/build.wiki.

314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
<pre><code>docker image rm THE_FOSSIL_ID THE_ALPINE_ID</code></pre>


<h2>6.0 Building on/for Android</h2>

<h3>6.1 Cross-compiling from Linux</h3>

The following instructions for building Fossil for Andoid,
without requiring a rooted OS, are adapted from
[https://fossil-scm.org/forum/forumpost/e0e9de4a7e | forumpost/e0e9de4a7e].

On the development machine, from the fossil source tree:

<pre><code>export CC=$NDK_PATH/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi21-clang
./configure --with-openssl=none







|







314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
<pre><code>docker image rm THE_FOSSIL_ID THE_ALPINE_ID</code></pre>


<h2>6.0 Building on/for Android</h2>

<h3>6.1 Cross-compiling from Linux</h3>

The following instructions for building Fossil for Android,
without requiring a rooted OS, are adapted from
[https://fossil-scm.org/forum/forumpost/e0e9de4a7e | forumpost/e0e9de4a7e].

On the development machine, from the fossil source tree:

<pre><code>export CC=$NDK_PATH/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi21-clang
./configure --with-openssl=none

Changes to www/caps/admin-v-setup.md.

346
347
348
349
350
351
352
353
354
355
356
357
358
359
360

*   **SQL**: The Admin → SQL feature allows the Setup user to enter raw
    SQL queries against the Fossil repository via Fossil UI. This not
    only allows arbitrary ability to modify the repository hash tree
    and its backing data tables, it can probably also be used to damage
    the host such as via `PRAGMA temp_store = FILE`.

*   **Tickets**: This section allows input of aribtrary TH1 code that
    runs on the server, affecting the way the Fossil ticketing system
    works. The justification in the **TH1** section below therefore
    applies.

*   **TH1**: The [TH1 language][th1] is quite restricted relative to the
    Tcl language it descends from, so this author does not believe there
    is a way to damage the Fossil repository or its host via the Admin →







|







346
347
348
349
350
351
352
353
354
355
356
357
358
359
360

*   **SQL**: The Admin → SQL feature allows the Setup user to enter raw
    SQL queries against the Fossil repository via Fossil UI. This not
    only allows arbitrary ability to modify the repository hash tree
    and its backing data tables, it can probably also be used to damage
    the host such as via `PRAGMA temp_store = FILE`.

*   **Tickets**: This section allows input of arbitrary TH1 code that
    runs on the server, affecting the way the Fossil ticketing system
    works. The justification in the **TH1** section below therefore
    applies.

*   **TH1**: The [TH1 language][th1] is quite restricted relative to the
    Tcl language it descends from, so this author does not believe there
    is a way to damage the Fossil repository or its host via the Admin →

Changes to www/changes.wiki.

1
2
3
4
5























6
7
8
9
10
11
12

13




14
15
16


17
18
19
20
21
22
23
24
25
26
27
28




29
30
31
32
33
34
35
<title>Change Log</title>

<a name='v2_14'></a>
<h2>Changes for Version 2.14 (pending)</h2>
























  *  The "[/help?cmd=clone|fossil clone]" command is enhanced so that
     if the repository filename is omitted, an appropriate name is derived
     from the remote URL and the newly cloned repo is opened.  This makes
     the clone command work more like Git, thus making it easier for
     people transitioning from Git.
  *  Changed the way that wiki edits are stored in the EVENT table of the
     database, for more flexibility and better features.

     <b>Run "fossil rebuild" to take advantage of this change</b>.




  *  Add the "contact" sub-command to [/help?cmd=user|fossil user].
  *  Added commands "[/help?cmd=all|fossil all git export]" and
     "[/help?cmd=all|fossil all git status]".


  *  Improvements to the "[/sitemap]" page.  Add subpages
     [/sitemap-timeline] and [/sitemap-test].
  *  Better text position in cylinder objects of Pikchr diagrams.
  *  New "details.txt" settings available to custom skins to better control
     the rendering of Pikchr diagrams:
     <ul>
     <li> pikchr-foreground
     <li> pikchr-scale
     <li> pikchr-fontscale
     </ul>
  *  Allow the use of SQL functions inside the ticket table definition
     for custom ticket configurations.




  *  Countless improvements and enhancements to the documentation

<a name='v2_13'></a>
<h2>Changes for Version 2.13 (2020-11-01)</h2>

  *  Added support for [./interwiki.md|interwiki links].
  *  Enable &lt;del&gt; and &lt;ins&gt; markup in  wiki.



|

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





|
|
>
|
>
>
>
>



>
>












>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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
<title>Change Log</title>

<a name='v2_14'></a>
<h2>Changes for Version 2.14 (2021-01-20)</h2>

  *  <b>Schema Update Notice #1:</b>
     This release drops a trigger from the database schema (replacing
     it with a TEMP trigger that is created as needed).  This
     change happens automatically the first time you
     add content to a repository using Fossil 2.14 or later.  No
     action is needed on your part. However, if you upgrade to 
     version 2.14 and then later downgrade or otherwise use an earlier
     version of Fossil, the email notification mechanism may fail
     to send out notifications for some events, due to the missing
     trigger.  If you want to
     permanently downgrade an installation, then you should run
     "[/help?cmd=rebuild|fossil rebuild]" after the downgrade
     to get email notifications working again.  If you are not using
     email notification, then the schema change will not affect you in
     any way.
  *  <b>Schema Update Notice #2:</b>
     This release changes how the descriptions of wiki edits are stored
     in the EVENT table, for improved display on timelines.  You must
     run "[/help?cmd=rebuild|fossil rebuild]" to take advantage of
     this enhancement.  Everything will still work without
     "fossil rebuild", except you will get goofy descriptions of
     wiki updates in the timeline.
  *  Add support for [./chat.md|Fossil chat].
  *  The "[/help?cmd=clone|fossil clone]" command is enhanced so that
     if the repository filename is omitted, an appropriate name is derived
     from the remote URL and the newly cloned repo is opened.  This makes
     the clone command work more like Git, thus making it easier for
     people transitioning from Git.
  *  Added the --mainbranch option to the [/help?cmd=git|fossil git export]
     command.
  *  Added the --format option to the 
     "[/help?cmd=timeline|fossil timeline]" command.
  *  Enhance the --numstat option on the
     "[/help?cmd=diff|fossil diff]" command so that it shows a total
     number of lines added and deleted and total number of files
     modified.
  *  Add the "contact" sub-command to [/help?cmd=user|fossil user].
  *  Added commands "[/help?cmd=all|fossil all git export]" and
     "[/help?cmd=all|fossil all git status]".
  *  Added the "df=CHECKIN" query parameter to the
     [/help?cmd=/timeline|/timeline page].
  *  Improvements to the "[/sitemap]" page.  Add subpages
     [/sitemap-timeline] and [/sitemap-test].
  *  Better text position in cylinder objects of Pikchr diagrams.
  *  New "details.txt" settings available to custom skins to better control
     the rendering of Pikchr diagrams:
     <ul>
     <li> pikchr-foreground
     <li> pikchr-scale
     <li> pikchr-fontscale
     </ul>
  *  Allow the use of SQL functions inside the ticket table definition
     for custom ticket configurations.
  *  The built-in SQLite is updated to version 3.35.0 alpha containing
     performance optimizations, especially performance associated with
     startup, and minor improvements to the CLI.
  *  Performance optimizations to Fossil itself.
  *  Countless improvements and enhancements to the documentation

<a name='v2_13'></a>
<h2>Changes for Version 2.13 (2020-11-01)</h2>

  *  Added support for [./interwiki.md|interwiki links].
  *  Enable &lt;del&gt; and &lt;ins&gt; markup in  wiki.
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
     using Ajax.

<a name='v2_0'></a>
<h2>Changes for Version 2.0 (2017-03-03)</h2>

  *  Use the
     [https://github.com/cr-marcstevens/sha1collisiondetection|hardened SHA1]
     implemenation by Marc Stevens and Dan Shumow.
  *  Add the ability to read and understand
     [./fileformat.wiki#names|artifact names] that are based on SHA3-256
     rather than SHA1, but do not actually generate any such names.
  *  Added the [/help?cmd=sha3sum|sha3sum] command.
  *  Update the built-in SQLite to version 3.17.0.

<a name='v1_37'></a>







|







637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
     using Ajax.

<a name='v2_0'></a>
<h2>Changes for Version 2.0 (2017-03-03)</h2>

  *  Use the
     [https://github.com/cr-marcstevens/sha1collisiondetection|hardened SHA1]
     implementation by Marc Stevens and Dan Shumow.
  *  Add the ability to read and understand
     [./fileformat.wiki#names|artifact names] that are based on SHA3-256
     rather than SHA1, but do not actually generate any such names.
  *  Added the [/help?cmd=sha3sum|sha3sum] command.
  *  Update the built-in SQLite to version 3.17.0.

<a name='v1_37'></a>
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
     [/help?cmd=/timeline|/timeline] webpage, with associated form widgets.
  *  Enhance the [/help/changes|changes] and [/help/status|status] commands
     with many new filter options so that specific kinds of changes can be
     found without having to pipe through grep or sed.
  *  Enhanced the [/help/sqlite3|fossil sql] command so that it opens the
     [./tech_overview.wiki#localdb|checkout database] and the
     [./tech_overview.wiki#configdb|configuration database] in addition to the
     respository database.
  *  TH1 enhancements:
     <ul><li>Add <nowiki>[unversioned content]</nowiki> command.</li>
     <li>Add <nowiki>[unversioned list]</nowiki> command.</li>
     <li>Add project_description variable.</li>
     </ul>
  *  Rename crnl-glob [/help/settings|setting] to crlf-glob, but keep
     crnl-glob as a compatibility alias.







|







661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
     [/help?cmd=/timeline|/timeline] webpage, with associated form widgets.
  *  Enhance the [/help/changes|changes] and [/help/status|status] commands
     with many new filter options so that specific kinds of changes can be
     found without having to pipe through grep or sed.
  *  Enhanced the [/help/sqlite3|fossil sql] command so that it opens the
     [./tech_overview.wiki#localdb|checkout database] and the
     [./tech_overview.wiki#configdb|configuration database] in addition to the
     repository database.
  *  TH1 enhancements:
     <ul><li>Add <nowiki>[unversioned content]</nowiki> command.</li>
     <li>Add <nowiki>[unversioned list]</nowiki> command.</li>
     <li>Add project_description variable.</li>
     </ul>
  *  Rename crnl-glob [/help/settings|setting] to crlf-glob, but keep
     crnl-glob as a compatibility alias.
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
  *  Added --include and --exclude options to [/help?cmd=tarball|fossil tarball]
     and [/help?cmd=zip|fossil zip] and the in= and ex= query parameters to the
     [/help?cmd=/tarball|/tarball] and [/help?cmd=/zip|/zip] web pages.
  *  Add support for [./encryptedrepos.wiki|encrypted Fossil repositories].
  *  If the FOSSIL_PWREADER environment variable is set, then use the program it
     names in place of getpass() to read passwords and passphrases
  *  Option --baseurl now works on Windows.
  *  Numerious documentation improvements.
  *  Update the built-in SQLite to version 3.13.0.

<a name='v1_34'></a>
<h2>Changes for Version 1.34 (2015-11-02)</h2>

  *  Make the [/help?cmd=clean|fossil clean] command undoable for files less
     than 10MiB.







|







754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
  *  Added --include and --exclude options to [/help?cmd=tarball|fossil tarball]
     and [/help?cmd=zip|fossil zip] and the in= and ex= query parameters to the
     [/help?cmd=/tarball|/tarball] and [/help?cmd=/zip|/zip] web pages.
  *  Add support for [./encryptedrepos.wiki|encrypted Fossil repositories].
  *  If the FOSSIL_PWREADER environment variable is set, then use the program it
     names in place of getpass() to read passwords and passphrases
  *  Option --baseurl now works on Windows.
  *  Numerous documentation improvements.
  *  Update the built-in SQLite to version 3.13.0.

<a name='v1_34'></a>
<h2>Changes for Version 1.34 (2015-11-02)</h2>

  *  Make the [/help?cmd=clean|fossil clean] command undoable for files less
     than 10MiB.

Added www/chat.md.





































































































































































































































































































































































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

## Introduction

As of version 2.14,
Fossil supports a developer chatroom feature.  The chatroom provides an
ephemeral discussion venue for insiders.  Design goals include:

  *  **Simple but functional** &rarr; Fossil chat is designed to provide a
     convenient real-time communication mechanism for geographically
     dispersed developers.  Fossil chat is *not* intended
     as a replacement or 
     competitor for IRC, Slack, Discord, Telegram, Google Hangouts, etc.

  *  **Low administration** &rarr;
     You can activate the chatroom in seconds without having to
     mess with configuration files or install new software.
     In an existing [server setup](./server/),
     simply enable the [C capability](/setup_ucap_list) for users
     whom you want to give access to the chatroom.

  *  **Ephemeral** &rarr;
     Chat messages do not sync to peer repositories, and they are
     automatically deleted after a configurable delay (default: 7 days).
     Individual messages or the entire conversation
     can be deleted at any time without impacting any other part
     of the system.

Fossil chat is designed for use by insiders - people with check-in
privileges or higher.  It is not intended as a general-purpose gathering
place for random passers-by on the internet. 
Fossil chat seeks to provide a communication venue for discussion
that does *not* become part of the permanent record for the project.
For persistent and durable discussion, use the [Forum](./forum.wiki).
Because the conversation is intended to be ephemeral, the chat messages
are local to a single repository.  Chat content does not sync.


## Setup

A Fossil repository must be functioning as a [server](./server/) in order
for chat to work.
To activate chat, simply add the [C capability](/setup_ucap_list)
to every user who is authorized to participate.  Anyone who can read chat
can also post to chat.

Setup ("s") and Admin ("a") users always have access to chat, without needing
the "C" capability.  A common configuration is to add the "C" capability
to "Developer" so that any individual user who has the "v" capability will
also have access to chat.

There are also some settings under /Admin/Chat that control the
behavior of chat, though the default settings are reasonable so in most
cases those settings can be ignored.  The settings control things like
the amount of time that chat messages are retained before being purged
from the repository database.

## Usage

For users with appropriate permissions, simply browse to the
[/chat](/help?cmd=/chat) to start up a chat session.  The default
skin includes a "Chat" entry on the menu bar on wide screens for
people with chat privilege.  There is also a "Chat" option on
the [Sitemap page](/sitemap), which means that chat will appear
as an option under the hamburger menu for many [skins](./customskin.md).

Message text is delivered verbatim.  There is no markup.  However,
the chat system does try to identify and tag hyperlinks, as follows:

  *  Any word that begins with "http://" or "https://" is assumed
     to be a hyperlink and is tagged.

  *  Text within `[...]` is parsed, and it if is a valid hyperlink
     target (according to the way that [Fossil Wiki](/wiki_rules) or
     [Markdown](/md_rules) understand hyperlinks), then that text is
     tagged. Note that only URLs and Fossil-internal constructs such
     as checkin hashes and wiki pages names are recognized here, not
     constructs such as `[URL | label]` or `[label](URL)`.

Apart from adding hyperlink anchor tags to bits of text that look
like hyperlinks, no changes are made to the input text.

Files may be sent via chat using the file selection element at the
bottom of the page. If the desktop environment system supports it,
files may be dragged and dropped onto that element. Files are not
automatically sent - selection of a file can be cancelled using the
Cancel button which appears only when a file is selected. When the
Send button is pressed, any pending text is submitted along with the
selected file. Image files sent this way will, by default, appear
inline in messages, but each user may toggle that via the settings
popup menu, such that images instead appear as downloadable links.
Non-image files always appear in messages as download links.

### Deletion of Messages

Any user may *locally* delete a given message by clicking on the "tab"
at the top of the message and clicking the button which appears. Such
deletions are local-only, and the messages will reappear if the page
is reloaded. Admin users may additionally choose to globally
delete a message from the chat record, which deletes it not only from
their own browser but also propagates the removal to all connected
clients the next time they poll for new messages.

## Implementation Details

*You do not need to understand how Fossil chat works in order to use it.
But many developers prefer to know how their tools work.
This section is provided for the benefit of those curious developers.*

The [/chat](/help?cmd=/chat) webpage downloads a small amount of HTML
and a small amount of javascript to run the chat session.  The
javascript uses XMLHttpRequest (XHR) to download chat content, post
new content, or delete historical messages.  The following web
interfaces are used by the XHR:

  *  **/chat-poll** &rarr;
     Downloads chat content as JSON.
     Chat messages are numbered sequentially.
     The client tells the server the largest chat message it currently
     holds, and the server sends back subsequent messages.  If there
     are no subsequent messages, the /chat-poll page blocks until new
     messages are available.

  *  **/chat-send** &rarr;
     Sends a new chat message to the server.

  *  **/chat-delete** &rarr;
     Deletes a chat message.

Fossil chat uses the venerable "hanging GET" or 
"[long polling](wikipedia:/wiki/Push_technology#Long_polling)"
technique to recieve asynchronous notification of new messages.
This is done because long polling works well with CGI and SCGI,
which are the usual mechanisms for setting up a Fossil server.
More advanced notification techniques such as 
[Server-sent events](wikipedia:/wiki/Server-sent_events) and especially
[WebSockets](wikipedia:/wiki/WebSocket) might seem more appropriate for
a chat system, but those technologies are not compatible with CGI.

Downloading of posted files and images uses a separate, non-XHR interface:

  * **/chat-download** &rarr;
    Fetches the file content associated with a post (one file per
    post, maximum). In the UI, this is accessed via links to uploaded
    files and via inlined image tags.

Chat messages are stored on the server-side in the CHAT table of
the repository.

~~~
   CREATE TABLE repository.chat(
      msgid INTEGER PRIMARY KEY AUTOINCREMENT,
      mtime JULIANDAY,
      ltime TEXT,
      xfrom TEXT,
      xmsg  TEXT,
      fname TEXT,
      fmime TEXT,
      mdel  INT,
      file  BLOB)
    );
~~~

The CHAT table is not cross-linked with any other tables in the repository
schema.  An administrator can "DROP TABLE chat;" at any time, without
harm (apart from deleting all chat history, of course).  The CHAT table
is dropped when running [fossil scrub --verily](/help?cmd=scrub).

On the server-side, message text is stored exactly as entered by the
users.  The /chat-poll page queries the CHAT table and constructs a
JSON reply described in the [/chat-poll
documentation](/help?cmd=/chat-poll).  The message text is translated
into HTML before being converted to JSON so that the text can be
safely added to the display using assignment to `innerHTML`. Though
`innerHTML` assignment is generally considered unsafe, it is only so
with untrusted content from untrusted sources. The chat content goes
through sanitization steps which eliminate any potential security
vulnerabilities of assigning that content to `innerHTML`.

Changes to www/ckout-workflows.md.

107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
to behave like `git clone` would therefore make the behavior surprising
to Fossil users. (See [our discussions][caod] if you want the full
details.)


#### <a id="clone"></a> Git-Like Clone-and-Open

With Fossil 2.14 — currently unreleased — we added a more Git-like
alternative:

        fossil clone https://fossil-scm.org/fossil
        cd fossil

This results in a `fossil.fossil` repo DB file and a `fossil/` working
directory.








|
<







107
108
109
110
111
112
113
114

115
116
117
118
119
120
121
to behave like `git clone` would therefore make the behavior surprising
to Fossil users. (See [our discussions][caod] if you want the full
details.)


#### <a id="clone"></a> Git-Like Clone-and-Open

In Fossil 2.14, we added a more Git-like alternative:


        fossil clone https://fossil-scm.org/fossil
        cd fossil

This results in a `fossil.fossil` repo DB file and a `fossil/` working
directory.

Added www/co-vs-up.md.































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
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
# Checkout vs Update

Fossil has two commands that look like they do the same thing on initial
examination, [`fossil update`][up] and [`fossil checkout`][co], but
there are several key differences:

1.  `fossil checkout` aborts if there are changed files in the local
    directory unless you give the `--force` option, whereas
    `fossil update` merges upstream changes with your local changes.
    Since Fossil tends to follow the CVS command design, and CVS
    popularized the [merge on update][cvsmu] workflow, we expect that
    Fossil’s update behavior is more likely to be what you want.

2.  Update triggers an autosync attempt; checkout does not.

3.  Several features in `fossil update` do not exist in
    `fossil checkout`, so developing a habit to type `fossil up` 
    means you’re more likely to have the features you want at hand.

4.  Inversely, the `fossil checkout --keep` feature doesn’t exist in
    `fossil update`, but it’s a rarely-needed operation, so it doesn’t
    provide a good reason to develop a habit of using `fossil checkout`
    instead.

In summary, these are two separate commands; neither is an alias for the
other. They overlap enough that they can be used interchangeably for
some use cases, but `update` is more powerful and more broadly useful.

[co]:    /help?cmd=checkout
[cvsmu]: http://web.mit.edu/gnu/doc/html/cvs_7.html#SEC37
[up]:    /help?cmd=update

Changes to www/contribute.wiki.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21



22
23
24
25
26





27
28
29

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
<title>Contributing To Fossil</title>

Users are encouraged to contributed enhancements back to the Fossil
project.  This note outlines some of the procedures for making
useful contributions.

<h2>1.0 Contributor Agreement</h2>

In order to accept your contributions, we <u>must</u> have a
[./copyright-release.pdf | Contributor Agreement (PDF)]
(or [./copyright-release.html | as HTML]) on file for you.  We require
this in order to maintain clear title to the Fossil code and prevent
the introduction of code with incompatible licenses or other entanglements
that might cause legal problems for Fossil users.  Many larger companies
and other lawyer-rich organizations require this as a precondition to using
Fossil.

If you do not wish to submit a Contributor Agreement, we would still
welcome your suggestions and example code, but we will not use your code
directly - we will be forced to re-implement your changes from scratch which
might take longer.




<h2>2.0 Submitting Patches</h2>

Suggested changes or bug fixes can be submitted by creating a patch
against the current source tree.  Email patches to





<a href="mailto:drh@sqlite.org">drh@sqlite.org</a>.  Be sure to
describe in detail what the patch does and which version of Fossil
it is written against.










A contributor agreement is not strictly necessary to submit a patch.
However, without a contributor agreement on file, your patch will be
used for reference only - it will not be applied to the code.  This
may delay acceptance of your patch.

Your patches or changes might not be accepted even if you do have
a contributor agreement on file.  Please do not take this personally
or as an affront to your coding ability.  Sometimes patches are rejected
because they seem to be taking the project in a direction that the
architect does not want to go.  Or, there might be an alternative
implementation of the same feature being prepared separately.

<h2>3.0 Check-in Privileges</h2>

Check-in privileges are granted on a case-by-case basis.   Your chances
of getting check-in privileges are much improved if you have a history
of submitting quality patches and/or making thoughtful posts on the
[http://www.mail-archive.com/fossil-users@lists.fossil-scm.org/ | mailing list].
A contributor agreement is, of course, a prerequisite for check-in
privileges.</p>

Contributors are asked to make all non-trivial changes on a branch.  The
Fossil Architect (Richard Hipp) will merge changes onto the trunk.</p>

Contributors are required to following the
[./checkin.wiki | pre-checkin checklist] prior to every check-in to
the Fossil self-hosting repository.  This checklist is short and succinct
and should only require a few seconds to follow.  Contributors
should print out a copy of the pre-checkin checklist and keep
it on a note card beside their workstations, for quick reference.

Contributors should review the
[./style.wiki | Coding Style Guidelines] and mimic the coding style
used through the rest of the Fossil source code.  Your code should
blend in.  A third-party reader should be unable to distinguish your
code from any other code in the source corpus.

<h2>4.0 Testing</h2>

Fossil has the beginnings of a
[../test/release-checklist.wiki | release checklist] but this is an




area that needs further work.  (Your contributions here are welcomed!)

Contributors with check-in privileges are expected to run the release




checklist on any major changes they contribute, and if appropriate expand





the checklist and/or the automated test scripts to cover their additions.













<h2>5.0 See Also</h2>

  *  [./build.wiki | How To Compile And Install Fossil]
  *  [./makefile.wiki | The Fossil Build Process]
  *  [./tech_overview.wiki | A Technical Overview of Fossil]
  *  [./adding_code.wiki | Adding Features To Fossil]


|





|




|
|




|

>
>
>




|
>
>
>
>
>


|
>

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

|

|

|






|
|
|





|




|









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

>
>
>
>

>
>
|





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
<title>Contributing To Fossil</title>

Fossil users are encouraged to contributed enhancements back to the
project.  This note outlines some of the procedures for making
useful contributions.

<h2>1.0 Contributor Agreement</h2>

In order to accept non-trivial contributions, we <u>must</u> have a
[./copyright-release.pdf | Contributor Agreement (PDF)]
(or [./copyright-release.html | as HTML]) on file for you.  We require
this in order to maintain clear title to the Fossil code and prevent
the introduction of code with incompatible licenses or other entanglements
that might cause legal problems for Fossil users.  Many
lawyer-rich organizations require this as a precondition to using
Fossil.

If you do not wish to submit a Contributor Agreement, we would still
welcome your suggestions and example code, but we will not use your code
directly: we will be forced to re-implement your changes from scratch, which
might take longer.

We've made exceptions for "trivial" changes in the past, but the
definition of that term is up to the project leader.

<h2>2.0 Submitting Patches</h2>

Suggested changes or bug fixes can be submitted by creating a patch
against the current source tree:

    <tt>fossil diff -i > my-change.patch</tt>

Post patches to
[https://fossil-scm.org/forum | the forum] or email them to
<a href="mailto:drh@sqlite.org">drh@sqlite.org</a>.  Be sure to
describe in detail what the patch does and which version of Fossil
it is written against. It's best to make patches against tip-of-trunk
rather than against past releases.

If your change is more complicated than a patch can properly encode, you
may submit [/help?cmd=bundle | a Fossil bundle] instead. Unlike patches,
bundles can contain multiple commits, check-in comments, file renames,
file deletions, branching decisions, and more which <tt>patch(1)</tt>
files cannot. It's best to make a bundle of a new branch so the change
can be integrated, tested, enhanced, and merged down to trunk in a
controlled fashion.

A contributor agreement is not strictly necessary to submit a patch or bundle,
but without a contributor agreement on file, your contribution will be
used for reference only: it will not be applied to the code.  This
may delay acceptance of your contribution.

Your contribution might not be accepted even if you do have
a contributor agreement on file.  Please do not take this personally
or as an affront to your coding ability.  Sometimes contributions are rejected
because they seem to be taking the project in a direction that the
architect does not want to go.  In other cases, there might be an alternative
implementation of the same feature being prepared separately.

<h2>3.0 Check-in Privileges</h2>

Check-in privileges are granted on a case-by-case basis.   Your chances
of getting check-in privileges are much improved if you have a history
of submitting quality patches and/or making thoughtful posts on
[https://fossil-scm.org/forum | the forum].
A signed contributor agreement is, of course, a prerequisite for check-in
privileges.</p>

Contributors are asked to make all non-trivial changes on a branch.  The
Fossil Architect (Richard Hipp) will merge changes onto the trunk.</p>

Contributors are required to follow the
[./checkin.wiki | pre-checkin checklist] prior to every check-in to
the Fossil self-hosting repository.  This checklist is short and succinct
and should only require a few seconds to follow.  Contributors
should print out a copy of the pre-checkin checklist and keep
it on a note card beside their workstations for quick reference.

Contributors should review the
[./style.wiki | Coding Style Guidelines] and mimic the coding style
used through the rest of the Fossil source code.  Your code should
blend in.  A third-party reader should be unable to distinguish your
code from any other code in the source corpus.

<h2>4.0 Testing</h2>


Fossil's [../test/release-checklist.wiki | release checklist] is of
primary benefit to the project leader, followed by him at release time,
but contributors are encouraged to run through its steps when making
major changes, since if the change doesn't pass this checklist, it won't
be included in the next release.

<h2>5.0 UI and Documentation Language</h2>

The Fossil project uses American English in its web interface and
documentation. Until there is some provision for translating the UI and
docs into other languages and dialects, we ask that you do not commit
changes that conflict with this.

We aren't opposed to such a project, but it would be a huge amount of
work, which no one's stepped up to do yet. Not only is each individual
translation a large ongoing job its own right, there is no
infrastructure for it yet, so the first few translations will be harder
than any future translation built on that infrastructure.

More immediately, we're likely to reject, revert, or rework commits that
use other English dialects. One example that comes up occasionally is
"artefact" versus "artifact." The UI and docs use the American English
spelling pervasively, so you have poor options if you insist on
"artefact:"

  *  attempt to slip one-off changes by your peers
  *  attempt to change all American English usages to Commonwealth English
  *  make the Fossil UI and docs translatable, then contribute a
     Commonwealth English translation

Only the latter is likely to succeed.

<h2>6.0 See Also</h2>

  *  [./build.wiki | How To Compile And Install Fossil]
  *  [./makefile.wiki | The Fossil Build Process]
  *  [./tech_overview.wiki | A Technical Overview of Fossil]
  *  [./adding_code.wiki | Adding Features To Fossil]

Changes to www/css-tricks.md.

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
This document is *not* an introduction to CSS - the web is
full of tutorials on that topic. It covers only the specifics
of customizing certain CSS-based behaviors in a Fossil UI. That said...

## Is it Really `!important`?

By and large, CSS's `!important` qualifier is not needed when
customzing Fossil's CSS. On occasion, however, particular styles may
be set directly on DOM elements when Fossil generates its HTML, and
such cases require the use of `!important` to override them.


<!-- ============================================================ -->
# Main UI CSS








|







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
This document is *not* an introduction to CSS - the web is
full of tutorials on that topic. It covers only the specifics
of customizing certain CSS-based behaviors in a Fossil UI. That said...

## Is it Really `!important`?

By and large, CSS's `!important` qualifier is not needed when
customizing Fossil's CSS. On occasion, however, particular styles may
be set directly on DOM elements when Fossil generates its HTML, and
such cases require the use of `!important` to override them.


<!-- ============================================================ -->
# Main UI CSS

Changes to www/customskin.md.

82
83
84
85
86
87
88
89












90
91
92
93
94
95
96
         <head>
         <base href="..." />
         <meta http-equiv="Content-Security-Policy" content="...." />
         <meta name="viewport" content="width=device-width, initial-scale=1.0">
         <title>....</title>
         <link rel="stylesheet" href="..." type="text/css" />
         </head>
         <body>













In most cases, it is best to leave the Fossil-generated HTML Header
alone. (One exception is when the administrator needs to include links
to additional CSS files.) The configurable part of the skin begins
with the Content Header section which should follow this template:

        <div class="header">







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







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
         <head>
         <base href="..." />
         <meta http-equiv="Content-Security-Policy" content="...." />
         <meta name="viewport" content="width=device-width, initial-scale=1.0">
         <title>....</title>
         <link rel="stylesheet" href="..." type="text/css" />
         </head>
         <body class="FEATURE">

…where `FEATURE` is either the top-level URL element (e.g. `doc`) or a
feature class that groups multiple URLs under a single name such as
`forum` to contain `/forummain`, `/forumpost`, `/forume2`, etc. This
allows per-feature CSS such as

         body.forum div.markdown blockquote {
           margin-left: 10px;
         }

That is, affect HTML `<blockquote>` tags specially only for forum posts
written in Markdown, leaving all other block quotes alone.

In most cases, it is best to leave the Fossil-generated HTML Header
alone. (One exception is when the administrator needs to include links
to additional CSS files.) The configurable part of the skin begins
with the Content Header section which should follow this template:

        <div class="header">
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169

The skin is controlled by five files:

<blockquote><dl>
<dt><b>css.txt</b></dt><dd>

<p>The css.txt file is the text of the CSS for Fossil.
Fossil might add additional CSS elements after the
the css.txt file, if it sees that the css.txt omits some
CSS components that Fossil needs.  But for the most part,
the content of the css.txt is the CSS for the page.</dd>

<dt><b>details.txt</b><dt><dd>

<p>The details.txt file is short list of settings that control







|







167
168
169
170
171
172
173
174
175
176
177
178
179
180
181

The skin is controlled by five files:

<blockquote><dl>
<dt><b>css.txt</b></dt><dd>

<p>The css.txt file is the text of the CSS for Fossil.
Fossil might add additional CSS elements after
the css.txt file, if it sees that the css.txt omits some
CSS components that Fossil needs.  But for the most part,
the content of the css.txt is the CSS for the page.</dd>

<dt><b>details.txt</b><dt><dd>

<p>The details.txt file is short list of settings that control
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
The three "timeline-" settings in details.txt control the appearance
of certain aspects of the timeline graph.  The number on the
right is a boolean - "1" to activate the feature and "0" to
disable it.  The "white-foreground:" setting should be set to
"1" if the page color has light-color text on a darker background,
and "0" if the page has dark text on a light-colored background.
<p>
If the "pikchr-foreground" setting (only available in Fossil 2.14 and
later) is defined and is not an empty string then it specifies a
foreground color to use for [pikchr diagrams](./pikchr.md).  The
default pikchr foreground color is black, or white if the
"white-foreground" boolean is set.
</dd>

<dt><b>footer.txt</b> and <b>header.txt</b></dt><dd>








|
|







193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
The three "timeline-" settings in details.txt control the appearance
of certain aspects of the timeline graph.  The number on the
right is a boolean - "1" to activate the feature and "0" to
disable it.  The "white-foreground:" setting should be set to
"1" if the page color has light-color text on a darker background,
and "0" if the page has dark text on a light-colored background.
<p>
If the "pikchr-foreground" setting (added in Fossil 2.14)
is defined and is not an empty string then it specifies a
foreground color to use for [pikchr diagrams](./pikchr.md).  The
default pikchr foreground color is black, or white if the
"white-foreground" boolean is set.
</dd>

<dt><b>footer.txt</b> and <b>header.txt</b></dt><dd>

249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
read out of the directory named.  You can then edit the control
files in the ./newskin folder using you favorite text editor, and
press "Reload" on your browser to see the effects.

## <a name="headfoot"></a>Header and Footer Processing

The `header.txt` and `footer.txt` control files of a skin are the HTML text
of the Contnet Header and Content Footer, except that before being inserted
into the output stream, the text is run through a
[TH1 interpreter](./th1.md) that might adjust the text as follows:

  *  All text within &lt;th1&gt;...&lt;/th1&gt; is omitted from the
     output and is instead run as a TH1 script.  That TH1
     script has the opportunity to insert new text in place of itself,
     or to inhibit or enable the output of subsequent text.







|







261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
read out of the directory named.  You can then edit the control
files in the ./newskin folder using you favorite text editor, and
press "Reload" on your browser to see the effects.

## <a name="headfoot"></a>Header and Footer Processing

The `header.txt` and `footer.txt` control files of a skin are the HTML text
of the Content Header and Content Footer, except that before being inserted
into the output stream, the text is run through a
[TH1 interpreter](./th1.md) that might adjust the text as follows:

  *  All text within &lt;th1&gt;...&lt;/th1&gt; is omitted from the
     output and is instead run as a TH1 script.  That TH1
     script has the opportunity to insert new text in place of itself,
     or to inhibit or enable the output of subsequent text.

Changes to www/defcsp.md.

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

        <p style="margin-left: 4em">Indented text.</p>

As the "`unsafe-`" prefix on the name implies, the `'unsafe-inline'`
feature is suboptimal for security.  However, there are
a few places in the Fossil-generated HTML that benefit from this
flexibility and the work-arounds are verbose and difficult to maintain.
Futhermore, the harm that can be done with style injections is far
less than the harm possible with injected javascript.  And so the
`'unsafe-inline'` compromise is accepted for now, though it might
go away in some future release of Fossil.

### <a name="script"></a> script-src 'self' 'nonce-%s'

This policy disables in-line JavaScript and only allows `<script>`







|







100
101
102
103
104
105
106
107
108
109
110
111
112
113
114

        <p style="margin-left: 4em">Indented text.</p>

As the "`unsafe-`" prefix on the name implies, the `'unsafe-inline'`
feature is suboptimal for security.  However, there are
a few places in the Fossil-generated HTML that benefit from this
flexibility and the work-arounds are verbose and difficult to maintain.
Furthermore, the harm that can be done with style injections is far
less than the harm possible with injected javascript.  And so the
`'unsafe-inline'` compromise is accepted for now, though it might
go away in some future release of Fossil.

### <a name="script"></a> script-src 'self' 'nonce-%s'

This policy disables in-line JavaScript and only allows `<script>`
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
visiting their repository home page, set to [an HTML-formatted embedded
doc page][hfed] via Admin → Configuration → Index&nbsp;Page, with this
content:

         <script src="/doc/trunk/bad.js"></script>

That script can then do anything allowed in JavaScript to *any other*
Chisel repository your browser can access.The possibilities for mischief
are *vast*. For just one example, if you have login cookies on four
different Chisel repositories, your attacker could harvest the login
cookies for all of them through this path if we allowed Fossil to serve
JavaScript files under the same CSP policy as we do for CSS files.

This is why the default configuration of Fossil has no way for [embedded
docs][ed], [wiki articles][wiki], [tickets][tkt], [forum posts][fp], or







|







171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
visiting their repository home page, set to [an HTML-formatted embedded
doc page][hfed] via Admin → Configuration → Index&nbsp;Page, with this
content:

         <script src="/doc/trunk/bad.js"></script>

That script can then do anything allowed in JavaScript to *any other*
Chisel repository your browser can access. The possibilities for mischief
are *vast*. For just one example, if you have login cookies on four
different Chisel repositories, your attacker could harvest the login
cookies for all of them through this path if we allowed Fossil to serve
JavaScript files under the same CSP policy as we do for CSS files.

This is why the default configuration of Fossil has no way for [embedded
docs][ed], [wiki articles][wiki], [tickets][tkt], [forum posts][fp], or
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252

4.  Use the [optional CGI server extensions feature](./serverext.wiki)
    to serve such content via `/ext` URLs.

5.  Put Fossil behind a [front-end proxy server][svr] as a virtual
    subdirectory within the site, so that our default CSP’s “self” rules
    match static file routes on that same site. For instance, your repo
    might be at `https://example.com/code`, allowing documentes in that
    repo to refer to:

    *   images as `/image/foo.png`
    *   JavaScript files  as `/js/bar.js`
    *   CSS style sheets as `/style/qux.css`

    Although those files are all outside the Fossil repo at `/code`,







|







238
239
240
241
242
243
244
245
246
247
248
249
250
251
252

4.  Use the [optional CGI server extensions feature](./serverext.wiki)
    to serve such content via `/ext` URLs.

5.  Put Fossil behind a [front-end proxy server][svr] as a virtual
    subdirectory within the site, so that our default CSP’s “self” rules
    match static file routes on that same site. For instance, your repo
    might be at `https://example.com/code`, allowing documents in that
    repo to refer to:

    *   images as `/image/foo.png`
    *   JavaScript files  as `/js/bar.js`
    *   CSS style sheets as `/style/qux.css`

    Although those files are all outside the Fossil repo at `/code`,

Changes to www/delta_format.wiki.

189
190
191
192
193
194
195



196



197






198
199
200
201
202
203
204
<p>
The format currently handles only 32 bit integer numbers. They are
written base-64 encoded, MSB first, and without leading
"0"-characters, except if they are significant (i.e. 0 => "0").
</p>

<p>



The base-64 coding is described in



<a href="http://www.ietf.org/rfc/rfc3548.txt">RFC 3548</a>.






</p>

<a name="examples"></a><h1>4.0 Examples</h1>

<a name="examplesint"></a><h2>4.1 Integer encoding</h2>

<table border=1>







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







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
<p>
The format currently handles only 32 bit integer numbers. They are
written base-64 encoded, MSB first, and without leading
"0"-characters, except if they are significant (i.e. 0 => "0").
</p>

<p>
The base-64 encoding uses one character for each 6 bits of
the integer to be encoded.  The encoding characters are:
</p>

<blockquote><pre>
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~
</pre></blockquote>

<p>The least significant 6 bits of the integer are encoded by
the first character, followed by
the next 6 bits, and so on until all non-zero bits of the integer
are encoded.  The minimum number of encoding characters is used.
Note that for integers less than 10, the base-64 coding is a
ASCII decimal rendering of the number itself.
</p>

<a name="examples"></a><h1>4.0 Examples</h1>

<a name="examplesint"></a><h2>4.1 Integer encoding</h2>

<table border=1>

Changes to www/env-opts.md.

111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
`--vfs VFSNAME`: Load the named VFS into SQLite.


Environment Variables
---------------------

The location of the user's account-wide [configuration database][configdb]
depends on the operating system and on the existance of various 
environment variables and/or files.  See the discussion of the
[configuration database location algorithm][configloc] for details.

`EDITOR`: Name the editor to use for check-in and stash comments.
Overridden by the local or global `editor` setting or the `VISUAL`
environment variable.








|







111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
`--vfs VFSNAME`: Load the named VFS into SQLite.


Environment Variables
---------------------

The location of the user's account-wide [configuration database][configdb]
depends on the operating system and on the existence of various 
environment variables and/or files.  See the discussion of the
[configuration database location algorithm][configloc] for details.

`EDITOR`: Name the editor to use for check-in and stash comments.
Overridden by the local or global `editor` setting or the `VISUAL`
environment variable.

391
392
393
394
395
396
397
398
399
400
401
402
403
404
405

Fossil keeps some information pertinent to each user in the user's
[configuration database file][configdb]. 
The configuration database file includes the global settings
and the list of repositories and checkouts used by `fossil all`.

The location of the configuration database file depends on the
operating system and on the existance of various environment
variables and/or files.  In brief, the configuration database is
usually:

  *  Traditional unix &rarr; "`$HOME/.fossil`"
  *  Windows &rarr; "`%LOCALAPPDATA%/_fossil`"
  *  [XDG-unix][xdg] &rarr; "`$HOME/.config/fossil.db`"








|







391
392
393
394
395
396
397
398
399
400
401
402
403
404
405

Fossil keeps some information pertinent to each user in the user's
[configuration database file][configdb]. 
The configuration database file includes the global settings
and the list of repositories and checkouts used by `fossil all`.

The location of the configuration database file depends on the
operating system and on the existence of various environment
variables and/or files.  In brief, the configuration database is
usually:

  *  Traditional unix &rarr; "`$HOME/.fossil`"
  *  Windows &rarr; "`%LOCALAPPDATA%/_fossil`"
  *  [XDG-unix][xdg] &rarr; "`$HOME/.config/fossil.db`"

Changes to www/fileedit-page.md.

135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
somwhere on the page outside of the editor.

Exactly how long `localStorage` will survive, and how much it or
`sessionStorage` can hold, is environment-dependent. `sessionStorage`
will survive until the current browser tab is closed, but it survives
across reloads of the same tab.

If `/filepage` determines that no peristent storage is available a
warning is displayed on the editor page.

[html5storage]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API

## <a id="power"></a> The Power is Yours, but...

> "With great power comes great responsibility."







|







135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
somwhere on the page outside of the editor.

Exactly how long `localStorage` will survive, and how much it or
`sessionStorage` can hold, is environment-dependent. `sessionStorage`
will survive until the current browser tab is closed, but it survives
across reloads of the same tab.

If `/filepage` determines that no persistent storage is available a
warning is displayed on the editor page.

[html5storage]: https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API

## <a id="power"></a> The Power is Yours, but...

> "With great power comes great responsibility."

Changes to www/forum.wiki.

1
2
3
4
5



6

7
8
9
10



11




12
13



14


15



16





17
18
19
20


21
22
23
24
25
26
27
28
29
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
<title>Fossil Forums</title>

<h2>Introduction</h2>

As of Fossil 2.7, Fossil includes a built-in discussion forum feature.





Any project complex enough to benefit from being managed by Fossil and
which has more than one user can probably also benefit from having a
discussion forum. Even if your project has a discussion forum already,
there are many benefits to using Fossil's built-in forum feature, some



of which you cannot get by using third-party alternatives:





  *  <b>Easy to Administer:</b> Third-party discussion forum and mailing



     list software tends to be difficult to install, set up, and


     administer. The Fossil forum feature aims to be as close to



     zero-configuration as is practical.






  *  <b>Malefactor Resistant:</b> Because Fossil accepts forum posts
     only via the web UI, it is inherently [./antibot.wiki | protected
     against bots].



  *  <b>Distributed and Tamper-Proof:</b> Posts are stored in the Fossil
     repository using the same [./fileformat.wiki | block chain technology]
     that Fossil uses to store your check-ins, wiki documents, etc.
     Posts sync to cloned repositories in a tamper-proof fashion.

  *  <b>Space Efficient:</b> Because of Fossil's [./delta_format.wiki |
     delta compression technology], discussions add little to the size
     of a cloned repository. Ten years of the SQLite project's
     discussions — averaging about 2 dozen posts per day — compress down
     to [https://fossil-scm.org/forum/forumpost/9b6f3f36bdb | just
     35&nbsp;MB of space] in a Fossil forum repository.

  *  <b>Built-in Full-Text Search:</b> Fossil forums use
     [https://sqlite.org/fts3.html | SQLite's powerful FTS4 engine] to
     handle searches. If your project currently uses a mailing list for
     discussions, this means you are no longer reliant upon third-party
     mailing list archive services to provide a useful search engine for
     your discussions. If you are running a private Fossil repository,
     you may not even have the <em>option</em> of delegating this useful
     service to a third-party; Fossil provides this service out of the
     box.

  *  <b>One Result Per Matching Post:</b> When you search the forum
     archives via the Fossil web interface, you get only one result for
     each matching post. When you search for project information via a
     standard web search engine, you might get a result from the project
     site's own mail archive plus one from Nabble, one from Gmane, one
     from The Mail Archive...

  *  <b>Search Off-Line:</b> Because Fossil is a [./concepts.wiki |
     distributed version control system], project members can search
     your forum archive while disconnected from the network where the
     central Fossil instance runs. Your past discussions are potentially
     just as valuable as a wiki document or checkin comment: there is no
     good reason why you should have to wait to get back on the Internet
     or back to the office before you can search for past posts.

  *  <b>Contribute Off-Line:</b> Fossil forum posts work like any other
     insertion into the repository, so a user can create new threads and
     reply to existing ones while off-line, then sync their
     contributions to the server they cloned from when back on-line.
     Yes, you can post to the forum from inside a tent, miles from the
     nearest WiFi router or cellular data tower.

  *  <b>Interlink with Other Fossil-Managed Artifacts:</b> Because forum
     posts are normal Fossil artifacts, you can interlink them with
     other Fossil artifacts using short internal links: link to forum
     threads from a [./tickets.wiki | ticket], link to a wiki document
     from a forum post, etc.

  *  <b>Durable Links:</b> Once you create a valid internal artifact
     link in Fossil, it <em>remains</em> valid, durably. With
     third-party forum software and mailing list search engines, your
     links are only valid until the third-party component changes its
     URL scheme or disappears from the web.

  *  <b>Role-Based Access Control:</b> The forum uses the same
     [./caps/ | capability-based access control
     system] that Fossil uses to control all other repository accesses.
     The Fossil forum feature simply adds [./caps/ref.html#2 | several new fine-grained
     capabilities] to the existing system.

  *  <b>Enduring, Open File Format:</b> Since Fossil has an
     [./fileformat.wiki | open and well-documented file format], your
     discussion archives are truly that: <em>archives</em>. You are no
     longer dependent on the lifetime and business model of a
     third-party piece of software or service. Should you choose to stop
     using Fossil, you can easily extract your discussion traffic for
     transfer to another system.

  *  <b>Lightweight Markup:</b> Posts can be marked up using Fossil's
     existing [/md_rules | Markdown] and [/wiki_rules | Wiki] markup
     processors. No longer must you choose between two bad options: to
     restrict posts to plain text only or to allow wild-west
     HTML-formatted MIME email. Fossil's lightweight markup language
     formatting features give you a middle path, providing your users
     enough formatting power to communicate complex ideas well without
     providing so much power as to risk
     [https://wonko.com/post/html-escaping | security problems].

  *  <b>Easy Email Alerts:</b> You can configure Fossil to
     [./alerts.md | send email alerts]. Forum post emails include the
     complete message content for the benefit of those that prefer to
     visit the forum only when they need to post something.  Alerts are
     optional, and each user gets the choice of immediate or daily
     digest delivery.


<h2 id="setup">Setting up a Fossil Forum</h2>

<h3 id="caps">Capabilities</h3>

By default, no Fossil user has permission to use the forums except for
users with Setup and Admin capabilities, which get these as part of the




|
>
>
>

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

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


|
<
>
>


|

|

|
<
<
<
<
<

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

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







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
<title>Fossil Forums</title>

<h2>Introduction</h2>

Fossil includes a built-in discussion forum, designed to substitute
for a mailing list.  Email notification is available to receive posts,
but the web-based UI must be used to enter new posts.  Advantages of
the forum include:

  *  <b>Easy to Administer:</b> If you have already set up a
     [./server/|Fossil server] with [./alerts.md|email alerts]


     then turning on the forum feature
     is just a matter of flipping some permission bits.  There is
     no new software to install and configure, and the same logins
     and passwords work.

  *  <b>Consistent Display:</b>  Forum posts can be in [/md_rules|Markdown], 
     [/wiki_rules|Fossil Wiki], or plain text.  Whichever format is used, the result is
     displayed consistently across all platforms and operating systems and
     between mobile devices and desktops.

  *  <b>Editable:</b>  Forum posts can be amended after they are sent,
     to fix typos or provide updates.  The original posts are preserved
     as part of the historical record, but only the amended posts are
     displayed by default.

  *  <b>Built-in Full-Text Search:</b> Forum posts can be included in
     the index used by the built-in Fossil search logic.

  *  <b>Off-Line Access:</b> Because forum posts are synced along with
     all other artifacts, you can search the forum, or add new posts, or
     edit existing posts, all while off-line.

  *  <b>Automatically Cross-Referenced To Other Fossil Artifacts:</b> Because forum
     posts are normal Fossil artifacts, you can link from them to
     other Fossil artifacts (check-ins, wiki, tickets) and from those other 
     artifacts to forum posts.  The reverse links are recognized and
     displayed automatically on the receiver.

  *  <b>Malefactor Resistant:</b> Because Fossil accepts forum posts
     only via the web UI, it is more resistent to spam.  Passers-by

     can post to the forum anonymously (subject to moderation), without
     the hassle of a sign-up process.

  *  <b>Distributed and Tamper-Proof:</b> Posts are stored in the Fossil
     repository using the same [./fileformat.wiki | DAG/Merkle-tree design]
     that Fossil uses to store your check-ins, wiki documents, etc.
     Forum posts sync to cloned repositories.

<h2>Example Installations</h2>






Both the [forum:/forum|Fossil project itself] and the
[https://sqlite.org/forum/forum|SQLite project] use the Fossil forum in place
of mailing lists.  The forum has worked well on both projects.  The ability




to post anonymously provides a low-resistance path for people to report

problems, resulting in more problems being reported and fixed.






The ability to moderate and amend forum posts means that the 







forums contain better information.  And backups and archives






are as easy as running "clone".











Both Fossil and SQLite keep their forums as separate repositories.





But there is no requirement to do this.  A forum can be coresident in







the same repository with the source code.


















<h2 id="setup">Setting up a Fossil Forum</h2>

<h3 id="caps">Capabilities</h3>

By default, no Fossil user has permission to use the forums except for
users with Setup and Admin capabilities, which get these as part of the

Changes to www/fossil-v-git.wiki.

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
Differences between Fossil and Git are summarized by the following table,
with further description in the text that follows.

<blockquote><table border=1 cellpadding=5 align=center>
<tr><th width="49%">GIT</th><th width="49%">FOSSIL</th><th width="2%">more</th></tr>
<tr>
    <td>File versioning only</td>
    <td>VCS, tickets, wiki, docs, notes, forum, UI,
    [https://en.wikipedia.org/wiki/Role-based_access_control|RBAC]</td>
    <td><a href="#features">2.1&nbsp;&darr;</a></td>
</tr>
<tr>
    <td>A federation of many small programs</td>
    <td>One self-contained, stand-alone executable</td>
    <td><a href="#selfcontained">2.2&nbsp;&darr;</a></td>







|







36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
Differences between Fossil and Git are summarized by the following table,
with further description in the text that follows.

<blockquote><table border=1 cellpadding=5 align=center>
<tr><th width="49%">GIT</th><th width="49%">FOSSIL</th><th width="2%">more</th></tr>
<tr>
    <td>File versioning only</td>
    <td>VCS, tickets, wiki, docs, notes, forum, chat, UI,
    [https://en.wikipedia.org/wiki/Role-based_access_control|RBAC]</td>
    <td><a href="#features">2.1&nbsp;&darr;</a></td>
</tr>
<tr>
    <td>A federation of many small programs</td>
    <td>One self-contained, stand-alone executable</td>
    <td><a href="#selfcontained">2.2&nbsp;&darr;</a></td>
103
104
105
106
107
108
109
110

111
112
113
114
115
116
117

<h3 id="features">2.1 Featureful</h3>

Git provides file versioning services only, whereas Fossil adds
an integrated [./wikitheory.wiki | wiki],
[./bugtheory.wiki | ticketing &amp; bug tracking],
[./embeddeddoc.wiki | embedded documentation], 
[./event.wiki | technical notes], and a [./forum.wiki | web forum],

all within a single nicely-designed [./customskin.md|skinnable] web
[/help?cmd=ui|UI],
protected by [./caps/ | a fine-grained role-based
access control system].
These additional capabilities are available for Git as 3rd-party
add-ons, but with Fossil they are integrated into
the design.  One way to describe Fossil is that it is







|
>







103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

<h3 id="features">2.1 Featureful</h3>

Git provides file versioning services only, whereas Fossil adds
an integrated [./wikitheory.wiki | wiki],
[./bugtheory.wiki | ticketing &amp; bug tracking],
[./embeddeddoc.wiki | embedded documentation], 
[./event.wiki | technical notes], a [./forum.wiki | web forum],
and a [./chat.md | chat service],
all within a single nicely-designed [./customskin.md|skinnable] web
[/help?cmd=ui|UI],
protected by [./caps/ | a fine-grained role-based
access control system].
These additional capabilities are available for Git as 3rd-party
add-ons, but with Fossil they are integrated into
the design.  One way to describe Fossil is that it is
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
manager, simply because the creation of complicated binary packages is
best delegated to people skilled in their creation. Normal Git users are
not expected to build Git from source and install it themselves.

Fossil is a single self-contained stand-alone executable which by default
depends only on common platform libraries.  You can statically link
to get an executable with no external dependencies at all &mdash; a useful
feature for running inside of restrictive 
[https://en.wikipedia.org/wiki/Chroot|chroot jail].

The precompiled Fossil binaries are delivered as just a single
executable.  The precompiled Windows deliveries are just a ZIP archive
containing only "<tt>fossil.exe</tt>".  There is no "<tt>setup.exe</tt>"
to run.  Linux and Mac precompiled binaries are a tarball containing
just the "<tt>fossil</tt>" executable.  To install, just put the







|







166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
manager, simply because the creation of complicated binary packages is
best delegated to people skilled in their creation. Normal Git users are
not expected to build Git from source and install it themselves.

Fossil is a single self-contained stand-alone executable which by default
depends only on common platform libraries.  You can statically link
to get an executable with no external dependencies at all &mdash; a useful
feature for running inside a restrictive 
[https://en.wikipedia.org/wiki/Chroot|chroot jail].

The precompiled Fossil binaries are delivered as just a single
executable.  The precompiled Windows deliveries are just a ZIP archive
containing only "<tt>fossil.exe</tt>".  There is no "<tt>setup.exe</tt>"
to run.  Linux and Mac precompiled binaries are a tarball containing
just the "<tt>fossil</tt>" executable.  To install, just put the
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
in the cloud, you can expect to pay about 8 times as much to comfortably host
GitLab as for Fossil.³ This difference is largely due to basic
technology choices: Ruby and PostgreSQL vs C and SQLite.

The Fossil project itself is [./selfhost.wiki|hosted on a small and
inexpensive VPS].  A bare-bones $5/month VPS or a
spare Raspberry Pi is sufficient to run a full-up project
site, complete with tickets, wiki, and forum, in addition to
being a code repository.

<h3 id="durable" name="database">2.3 Query Language</h3>

The baseline data structures for Fossil and Git are the same, modulo
formatting details. Both systems manage a
[https://en.wikipedia.org/wiki/Directed_acyclic_graph | directed acyclic







|







224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
in the cloud, you can expect to pay about 8 times as much to comfortably host
GitLab as for Fossil.³ This difference is largely due to basic
technology choices: Ruby and PostgreSQL vs C and SQLite.

The Fossil project itself is [./selfhost.wiki|hosted on a small and
inexpensive VPS].  A bare-bones $5/month VPS or a
spare Raspberry Pi is sufficient to run a full-up project
site, complete with tickets, wiki, chat, and forum, in addition to
being a code repository.

<h3 id="durable" name="database">2.3 Query Language</h3>

The baseline data structures for Fossil and Git are the same, modulo
formatting details. Both systems manage a
[https://en.wikipedia.org/wiki/Directed_acyclic_graph | directed acyclic
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
by following the pointers embedded in the check-in object, but it is
difficult to go the other direction and locate the descendants of a
check-in.  It is so difficult, in fact, that neither native Git nor
GitHub provide this capability short of crawling the
[https://www.git-scm.com/docs/git-log|commit log].  With Fossil,
on the other hand, finding descendents is a simple SQL query.
It is common in Fossil to ask to see
[/timeline?d=release&n=all&y=ci&nd|all check-ins since the last release].
Git lets you see "what came before".  Fossil makes it just as
easy to also see "what came after".

Leaf check-ins in Git that lack a "ref" become "detached," making them
difficult to locate and subject to garbage collection. This
[http://gitfaq.org/articles/what-is-a-detached-head.html|detached head
state] problem has caused grief for







|







256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
by following the pointers embedded in the check-in object, but it is
difficult to go the other direction and locate the descendants of a
check-in.  It is so difficult, in fact, that neither native Git nor
GitHub provide this capability short of crawling the
[https://www.git-scm.com/docs/git-log|commit log].  With Fossil,
on the other hand, finding descendents is a simple SQL query.
It is common in Fossil to ask to see
[/timeline?df=release&y=ci|all check-ins since the last release].
Git lets you see "what came before".  Fossil makes it just as
easy to also see "what came after".

Leaf check-ins in Git that lack a "ref" become "detached," making them
difficult to locate and subject to garbage collection. This
[http://gitfaq.org/articles/what-is-a-detached-head.html|detached head
state] problem has caused grief for
399
400
401
402
403
404
405








406
407
408
409
410
411
412
individual contributions into his version of the Linux kernel. Git
allows an anonymous developer to rebase and push specific locally-named
private branches, so that a Git repo clone often isn't really a clone at
all: it may have an arbitrary number of differences relative to the
repository it originally cloned from. Git encourages siloed development.
Select work in a developer's local repository may remain private
indefinitely.









All of this is exactly what one wants when doing bazaar-style
development.

Fossil's normal mode of operation differs on every one of these points,
with the specific designed-in goal of promoting SQLite's cathedral
development model:







>
>
>
>
>
>
>
>







400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
individual contributions into his version of the Linux kernel. Git
allows an anonymous developer to rebase and push specific locally-named
private branches, so that a Git repo clone often isn't really a clone at
all: it may have an arbitrary number of differences relative to the
repository it originally cloned from. Git encourages siloed development.
Select work in a developer's local repository may remain private
indefinitely.

The Git preference for siloed development has been strongly adopted by Github,
who say
"[https://guides.github.com/activities/forking|Forking is at the core of social coding at GitHub]".
As a result, as of January 2021,
[https://github.com/search?q=is:public|Github hosts 43 million distinct software projects],
most of them created as a results of Git/Github forking and very many
of them discontinued.

All of this is exactly what one wants when doing bazaar-style
development.

Fossil's normal mode of operation differs on every one of these points,
with the specific designed-in goal of promoting SQLite's cathedral
development model:
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
wrench] running at 8000 RPM driving an M8 socket-cap bolt at 16 cm/s is
not the best way to hang a picture on the living room wall.


<h4 id="contrib">2.5.3 Accepting Contributions</h4>

As of this writing, Git has received about 4.5 times as many commits as
Fossil resulting in about 2.5times as many lines of source code. The line
count excludes tests and in-tree third-party dependencies. It does not
exclude the default GUI for each, since it's integral for Fossil, so we
count the size of <tt>gitk</tt> in this.

It is obvious that Git is bigger in part because of its first-mover
advantage, which resulted in a larger user community, which results in
more contributions. But is that the <i>only</i> reason? We believe there







|







544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
wrench] running at 8000 RPM driving an M8 socket-cap bolt at 16 cm/s is
not the best way to hang a picture on the living room wall.


<h4 id="contrib">2.5.3 Accepting Contributions</h4>

As of this writing, Git has received about 4.5 times as many commits as
Fossil resulting in about 2.5 times as many lines of source code. The line
count excludes tests and in-tree third-party dependencies. It does not
exclude the default GUI for each, since it's integral for Fossil, so we
count the size of <tt>gitk</tt> in this.

It is obvious that Git is bigger in part because of its first-mover
advantage, which resulted in a larger user community, which results in
more contributions. But is that the <i>only</i> reason? We believe there
937
938
939
940
941
942
943
944



945
946
947
948
949
950
951
    several parts, so that it is not strictly true that "everything" on
    it is in the self-hosting Fossil project repo. The web forum is
    hosted as [https://fossil-scm.org/forum/|a separate Fossil repo]
    from the [https://fossil-scm.org/fossil/|main Fossil self-hosting
    repo] for administration reasons, and the Download page content
    isn't normally synchronized with a "<tt>fossil clone</tt>" command unless
    you add the "-u" option.  (See "[./aboutdownload.wiki|How the
    Download Page Works]" for details.) There may also be some purely



    static elements of the web site served via D. Richard Hipp's own
    lightweight web server,
    <tt>[https://sqlite.org/althttpd/|althttpd]</tt>,
    which is configured as a front end to Fossil running in CGI mode on
    these sites.

    <li><p>That estimate is based on pricing at Digital Ocean in







|
>
>
>







946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
    several parts, so that it is not strictly true that "everything" on
    it is in the self-hosting Fossil project repo. The web forum is
    hosted as [https://fossil-scm.org/forum/|a separate Fossil repo]
    from the [https://fossil-scm.org/fossil/|main Fossil self-hosting
    repo] for administration reasons, and the Download page content
    isn't normally synchronized with a "<tt>fossil clone</tt>" command unless
    you add the "-u" option.  (See "[./aboutdownload.wiki|How the
    Download Page Works]" for details.)
    Chat history is deliberately not synced as
    chat messages are intended to be ephemeral.
    There may also be some purely
    static elements of the web site served via D. Richard Hipp's own
    lightweight web server,
    <tt>[https://sqlite.org/althttpd/|althttpd]</tt>,
    which is configured as a front end to Fossil running in CGI mode on
    these sites.

    <li><p>That estimate is based on pricing at Digital Ocean in

Changes to www/fossil3.gif.

cannot compute difference between binary files

Changes to www/gitusers.md.

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
# Git to Fossil Translation Guide

## Introduction


This document attempts to provide equivalents for common Git commands
and workflows where possible, and where not, to explain those cases.



Although Fossil shares many similarities with Git, there are enough

differences that we can’t provide a simple “translation dictionary” for

some commands. This document is more concerned with those cases than the
simple 1:1 mappings, which you can likely find on your own. In many
cases, the sub-commands are identical: [`fossil bisect`][bis] does essentially
the same thing as `git bisect`, for example.






We present this from the perspective of Git users moving to Fossil, but
it is also possible to read this document as a Fossil user who speaks
only pidgin Git, who may often have questions of the form, “Now how do I
do X in Git again?”

This document’s authors are intimately familiar with Fossil, so it is
difficult for us to anticipate the perspective of people who are
intimately familiar with Git. If you have a lot of prior Git
experience, we welcome your contributions and questions on the [Fossil
Forum][ffor].

While we do try to explain Fossil-specific terminology inline here
as-needed, you may find it helpful to skim [the Fossil glossary][gloss].
It will give you another take on our definitions here, and it may help
you to understand some of the other Fossil docs better.

We focus more on practical command examples here than on [the
philosophical underpinnings][fvg] that drive these differences.

[bis]:   /help?cmd=bisect

[ffor]:  https://fossil-scm.org/forum
[fvg]:   ./fossil-v-git.wiki


<a id="mwd"></a>
## Repositories And Checkouts Are Distinct





>
|
<
>

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

















<
<
<
|
>







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
# Git to Fossil Translation Guide

## Introduction

Fossil shares many similarities with Git.  In many cases, the
sub-commands are identical: [`fossil bisect`][fbis] does essentially the

same thing as [`git bisect`][gbis], for example.

This document covers the cases where there is no simple 1:1 mapping,
usually because of intentional design differences in Fossil that prevent
it from working exactly like Git. We choose to explain these differences
rather than provide a simple “translation dictionary,since to
understand the conversion, you need to know why the difference exists.



We focus on practical command examples here, leaving discussions of the
philosophical underpinnings drive these command differences to [another
document][fvg]. The [case studies](#cs1) do get a bit philosophical, but
it is with the aim of illustrating how these Fossil design differences
cause Fossil to behave materially differently from Git in everyday
operation.

We present this from the perspective of Git users moving to Fossil, but
it is also possible to read this document as a Fossil user who speaks
only pidgin Git, who may often have questions of the form, “Now how do I
do X in Git again?”

This document’s authors are intimately familiar with Fossil, so it is
difficult for us to anticipate the perspective of people who are
intimately familiar with Git. If you have a lot of prior Git
experience, we welcome your contributions and questions on the [Fossil
Forum][ffor].

While we do try to explain Fossil-specific terminology inline here
as-needed, you may find it helpful to skim [the Fossil glossary][gloss].
It will give you another take on our definitions here, and it may help
you to understand some of the other Fossil docs better.




[fbis]:  /help?cmd=bisect
[gbis]:  https://git-scm.com/docs/git-bisect
[ffor]:  https://fossil-scm.org/forum
[fvg]:   ./fossil-v-git.wiki


<a id="mwd"></a>
## Repositories And Checkouts Are Distinct

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

        git checkout some-branch

…is best given as:

        fossil update some-branch

…in Fossil. There is a `fossil checkout` command, but it has two





restrictions that push you toward using `fossil update` instead:





1.  Several features in `fossil update` do not exist in
    `fossil checkout`.






2.  The lone exception is `fossil checkout --keep`, a rarely-needed



    operation.




3.  Fossil will have you typing “`fossil up`” frequently anyway to pull

    remote changes and merge them into the local check-out directory.
    Adding a `VERSION` string for the cases where you mean something
    other than “tip of the current branch” is an easy habit to develop.

Neither command is an alias for the other. They overlap enough that they


can be used interchangeably for everyday use cases, but since `update`
is more powerful, we recommend that you break the habit of typing
`checkout`.


[ckwf]: ./ckout-workflows.md



#### <a id="rname"></a> Naming Repositories

The Fossil repository database file can be named anything
you want, with a single exception: if you’re going to use the
[`fossil server DIRECTORY`][server] feature, the repositories you wish







|
>
>
>
>
>
|
>
>

>
>
|
|
>

>
>
>
>
|
>
>
>
|
>

>
>
|
>
|
|
|

|
>
>
|
<
|
>

<
>







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

        git checkout some-branch

…is best given as:

        fossil update some-branch

…in Fossil. There is a [`fossil checkout`][co] command, but it has
[several differences](./co-vs-up.md) that make it less broadly useful
than [`fossil update`][up] in everyday operation, so we recommend that
Git users moving to Fossil develop a habit of typing `fossil up` rather
than `fossil checkout`. That said, one of those differences does match
up with Git users’ expectations: `fossil checkout` doesn’t pull changes
from the remote repository into the local clone as `fossil update` does.
We think this is less broadly useful, but that’s the subject of the next
section.

[ckwf]: ./ckout-workflows.md
[co]:   /help?cmd=checkout


#### <a id="pullup"></a> Update vs Pull

The closest equivalent to [`git pull`][gpull] is [`fossil up`][up],
since Fossil tends to follow the CVS command design: `cvs up` pulls
changes from the central CVS repository and merges them into the local
working directory, so that’s what `fossil up` does, too.

There is a `fossil pull` command, but it is simply the reverse of
`fossil push`, so that `fossil sync` [is functionally equivalent
to](./sync.wiki#sync):

        fossil push ; fossil pull

There is no “and update the local working directory” step in Fossil’s
push, pull, or sync commands, as with `git pull`.

This makes `fossil up` dual-use:

*   Without the optional `VERSION` argument, it updates the working
    checkout to the tip of the current branch.

*   With that argument, it updates to the named version. If that’s the
    name of a branch, it updates to the tip of that branch rather than
    the current one.


We think this is a more sensible command design than `git checkout` vs
`git pull`.


[gpull]: https://git-scm.com/docs/git-pull


#### <a id="rname"></a> Naming Repositories

The Fossil repository database file can be named anything
you want, with a single exception: if you’re going to use the
[`fossil server DIRECTORY`][server] feature, the repositories you wish
632
633
634
635
636
637
638
639

640
641
642
643
644
645
646
In Fossil, the default name for the main branch
is "`trunk`".  The "`trunk`" branch in Fossil corresponds to the
"`master`" branch in stock Git or to [the “`main`” branch in GitHub][mbgh].

Because the `fossil git export` command has to work with both stock Git
and with GitHub, Fossil uses Git’s traditional default rather than
GitHub’s new default: your Fossil repo’s “trunk” branch becomes “master”
when [mirroring to GitHub][mirgh], not “main.”


We do not know what happens on subsequent exports if you later rename
this branch on the GitHub side.

[mbgh]:  https://github.com/github/renaming
[mirgh]: ./mirrortogithub.md








|
>







660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
In Fossil, the default name for the main branch
is "`trunk`".  The "`trunk`" branch in Fossil corresponds to the
"`master`" branch in stock Git or to [the “`main`” branch in GitHub][mbgh].

Because the `fossil git export` command has to work with both stock Git
and with GitHub, Fossil uses Git’s traditional default rather than
GitHub’s new default: your Fossil repo’s “trunk” branch becomes “master”
when [mirroring to GitHub][mirgh] unless you give the `--mainbranch`
option added in Fossil 2.14.

We do not know what happens on subsequent exports if you later rename
this branch on the GitHub side.

[mbgh]:  https://github.com/github/renaming
[mirgh]: ./mirrortogithub.md

672
673
674
675
676
677
678






















679
680
681
682
683
684
685

There is only one sub-feature of `git rebase` that is philosophically
compatible with Fossil yet which currently has no functional equivalent.
We cover [this and the workaround for it](#csplit) above.

[3]: ./rebaseharm.md
























## <a id="show"></a> Showing Information About Commits

While there is no direct equivalent to Git’s “`show`” command, similar
functionality may be present in Fossil under other commands:









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







701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736

There is only one sub-feature of `git rebase` that is philosophically
compatible with Fossil yet which currently has no functional equivalent.
We cover [this and the workaround for it](#csplit) above.

[3]: ./rebaseharm.md


## <a name="cdiff"></a> Colorized Diffs

The graphical diffs in the Fossil web UI and `fossil diff --tk` use
color to distinguish insertions, deletions, and replacements, but unlike
with `git diff` when the output is to an ANSI X3.64 capable terminal,
`fossil diff` does not.

There’s an easy way to add this feature to Fossil, though: install
[`colordiff`][cdiff], which is included in [many package systems][cdpkg],
then say:

        fossil set --global diff-command 'colordiff -wu'

Because this is unconditional, unlike `git diff --color=auto`, you will
then have to remember to add the `-i` option to `fossil diff` commands
when you want color disabled, such as when piping diff output to another
command that doesn’t understand ANSI escape sequences. There’s an
example of this [below](#dstat).

[cdpkg]: https://repology.org/project/colordiff/versions


## <a id="show"></a> Showing Information About Commits

While there is no direct equivalent to Git’s “`show`” command, similar
functionality may be present in Fossil under other commands:


739
740
741
742
743
744
745
746
747
748
749
750
751
752
753

        fossil diff -i -N --from 2020-04-01 | diffstat

We gave the `-i` flag in both cases to force Fossil to use its internal
diff implementation, bypassing [your local `diff-command` setting][dcset].
The `--numstat` option has no effect when you have an external diff
command set, and some diff command alternatives like
[`colordiff`][cdiff] produce output that confuses `diffstat`.

If you leave off the `-N` flag in the second example, the `diffstat`
output won’t include info about any newly-added files.

[cdiff]: https://www.colordiff.org/
[dcset]: https://fossil-scm.org/home/help?cmd=diff-command
[dst]:   https://invisible-island.net/diffstat/diffstat.html







|







790
791
792
793
794
795
796
797
798
799
800
801
802
803
804

        fossil diff -i -N --from 2020-04-01 | diffstat

We gave the `-i` flag in both cases to force Fossil to use its internal
diff implementation, bypassing [your local `diff-command` setting][dcset].
The `--numstat` option has no effect when you have an external diff
command set, and some diff command alternatives like
[`colordiff`][cdiff] (covered [above](#cdiff)) produce output that confuses `diffstat`.

If you leave off the `-N` flag in the second example, the `diffstat`
output won’t include info about any newly-added files.

[cdiff]: https://www.colordiff.org/
[dcset]: https://fossil-scm.org/home/help?cmd=diff-command
[dst]:   https://invisible-island.net/diffstat/diffstat.html
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
how the concepts lined out above cause Fossil to materially differ in
day-to-day operation from Git.

Why would you want to check out a version of a project by date?  Perhaps
because your customer gave you a vague bug report referencing only a
date rather than a version. Or, you may be poking semi-randomly through
history to find a “good” version to anchor the start point of a
[`bisect`][bis] operation.

My search engine’s first result for “git checkout by date” is [this
highly-upvoted accepted Stack Overflow answer][gcod]. The first command
it gives is based on Git’s [`rev-parse` feature][grp]:

        git checkout master@{2020-03-17}

There are a number of weaknesses in this command. From least to most
critical:

1.  It’s a bit cryptic. Leave off the refname or punctuation, and it
    means something else. You cannot simplify the cryptic incantation in
    the typical use case.

2.  A date string in Git without a time will be interpreted as
    “[at the local wall clock time on the given date][gapxd],” so the
    command means something different from one second to the next. This
    can be a problem if there are multiple commits on that date, because
    the command will give diffferent results depending on the time of
    day you run it.

3.  It gives misleading output if there is no close match for the date
    in the local [reflog]. It starts out empty after a fresh clone, and
    while it does build up as you use that clone, Git [automatically
    prunes][gle] the reflog to 90 days of history by default. This means
    the command above can give different results from one machine to the







|


















|







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
how the concepts lined out above cause Fossil to materially differ in
day-to-day operation from Git.

Why would you want to check out a version of a project by date?  Perhaps
because your customer gave you a vague bug report referencing only a
date rather than a version. Or, you may be poking semi-randomly through
history to find a “good” version to anchor the start point of a
[`fossil bisect`][fbis] operation.

My search engine’s first result for “git checkout by date” is [this
highly-upvoted accepted Stack Overflow answer][gcod]. The first command
it gives is based on Git’s [`rev-parse` feature][grp]:

        git checkout master@{2020-03-17}

There are a number of weaknesses in this command. From least to most
critical:

1.  It’s a bit cryptic. Leave off the refname or punctuation, and it
    means something else. You cannot simplify the cryptic incantation in
    the typical use case.

2.  A date string in Git without a time will be interpreted as
    “[at the local wall clock time on the given date][gapxd],” so the
    command means something different from one second to the next. This
    can be a problem if there are multiple commits on that date, because
    the command will give different results depending on the time of
    day you run it.

3.  It gives misleading output if there is no close match for the date
    in the local [reflog]. It starts out empty after a fresh clone, and
    while it does build up as you use that clone, Git [automatically
    prunes][gle] the reflog to 90 days of history by default. This means
    the command above can give different results from one machine to the

Changes to www/globs.md.

58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
sequences are allowed to match `/` directory separators as well as the
initial `.` in the name of a hidden file or directory. This is because
Fossil file names are stored as complete path names. The distinction
between file name and directory name is “below” Fossil in this sense.

[pg]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13

The bracket expresssions above require some additional explanation:

 *  A range of characters may be specified with `-`, so `[a-f]` matches
    exactly the same characters as `[abcdef]`. Ranges reflect Unicode
    code points without any locale-specific collation sequence.
    Therefore, this particular sequence never matches the Unicode
    pre-composed character `é`, for example. (U+00E9)








|







58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
sequences are allowed to match `/` directory separators as well as the
initial `.` in the name of a hidden file or directory. This is because
Fossil file names are stored as complete path names. The distinction
between file name and directory name is “below” Fossil in this sense.

[pg]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13

The bracket expressions above require some additional explanation:

 *  A range of characters may be specified with `-`, so `[a-f]` matches
    exactly the same characters as `[abcdef]`. Ranges reflect Unicode
    code points without any locale-specific collation sequence.
    Therefore, this particular sequence never matches the Unicode
    pre-composed character `é`, for example. (U+00E9)

482
483
484
485
486
487
488
489
490
491
492
493
494
495
496


## Experimenting

To preview the effects of command line glob pattern expansion for
various glob patterns (unquoted, quoted, comma-terminated), for any
combination of command shell, OS, C run time, and Fossil version,
preceed the command you want to test with [`test-echo`][] like so:

    $ fossil test-echo setting crlf-glob "*"
    C:\> echo * | fossil test-echo setting crlf-glob --args -

The [`test-glob`][] command is also handy to test if a string
matches a glob pattern.








|







482
483
484
485
486
487
488
489
490
491
492
493
494
495
496


## Experimenting

To preview the effects of command line glob pattern expansion for
various glob patterns (unquoted, quoted, comma-terminated), for any
combination of command shell, OS, C run time, and Fossil version,
precede the command you want to test with [`test-echo`][] like so:

    $ fossil test-echo setting crlf-glob "*"
    C:\> echo * | fossil test-echo setting crlf-glob --args -

The [`test-glob`][] command is also handy to test if a string
matches a glob pattern.

Changes to www/history.md.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# The History And Purpose Of Fossil

Fossil is a [distributed version control system (DVCS)][100] written
beginning in [2007][105] by the [architect of SQLite][110] for the
purpose of managing the [SQLite project][115].

[100]: https://en.wikipedia.org/wiki/Distributed_version_control
[105]: /timeline?a=1970-01-01&n=10
[110]: https://sqlite.org/crew.html
[115]: https://sqlite.org/

Though Fossil was originally written specifically to support SQLite,
it is now also used by countless other projects.  The SQLite architect (drh)
is still the top committer to Fossil, but there are also
[many other contributors][120].







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# The History And Purpose Of Fossil

Fossil is a [distributed version control system (DVCS)][100] written
beginning in [2007][105] by the [architect of SQLite][110] for the
purpose of managing the [SQLite project][115].

[100]: https://en.wikipedia.org/wiki/Distributed_version_control
[105]: /timeline?a=1970-01-01&n1=10
[110]: https://sqlite.org/crew.html
[115]: https://sqlite.org/

Though Fossil was originally written specifically to support SQLite,
it is now also used by countless other projects.  The SQLite architect (drh)
is still the top committer to Fossil, but there are also
[many other contributors][120].
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
was an amazing version control system for its day in that it allowed
multiple developers to be editing the same file at the same time.

[300]: https://en.wikipedia.org/wiki/Concurrent_Versions_System

Though innovative and much loved in its time, CVS was not without problems.
Among those was a lack of visibility into the project history and the
lack of integrated bug tracking.  To try to address these deficiencies,
the SQLite author developed the [CVSTrac][305] wrapper for CVS beginning
in [2002][310].

[305]: http://cvstrac.org/
[310]: http://cvstrac.org/fossil/timeline?a=19700101&n=10

CVSTrac greatly improved the usability of CVS and was adopted by
other projects.  CVSTrac also [inspired the design][315] of [Trac][320],
which was a similar system that was (and is) far more widely used.

[315]: https://trac.edgewall.org/wiki/TracHistory
[320]: https://trac.edgewall.org/

Historians can see the influence of CVSTrac on the development of
SQLite.  [Early SQLite check-ins][325] that happened before CVSTrac
often had a check-in comment that was just a "smiley".
That was not an unreasonable check-in comment, as check-in comments
were scarcely seen and of questionable utility in raw CVS.  CVSTrac
changed that, making check-in comments more visible and more useful.
The SQLite developers reacted by creating [better check-in comments][330].

[325]: https://sqlite.org/src/timeline?a=19700101&n=10
[330]: https://sqlite.org/src/timeline?c=20030101&n=10&nd

At about this same time, the [Monotone][335] system appeared.
Monotone was one of the first distributed version control systems. As far as
this author is aware, Monotone was the first VCS to make use of
SHA1 to identify artifacts.  Monotone stored its content in an SQLite
database, which is what brought it to the attention of the SQLite architect.
These design choices were a major source of inspiration for Fossil.

[335]: https://www.monotone.ca/

Beginning around 2005, the need for a better version control system
for SQLite began to become evident.  The SQLite architect looked
around for a suitable replacement.  Monotone, Git, and Mercurical were
all considered.  But at that time, none of these supported sync
over ordinary HTTP, none could be run from an inexpensive shell
account on a leased server (this was before the widespread availability
of affordable virtual machines), and none of them supported anything 
resembling the wiki and ticket features of CVSTrac that had been 
found to be so useful.  And so, the SQLite architect began writing
his own DVCS.

Early prototypes were done in [TCL][340].  As experiments proceeded,
however, it was found that the low-level byte manipulates needed for
things like delta compression and computing diffs
were better implemented in plain old C.
Experiments continued.  Finally, a prototype capable of self-hosting
was devised on [2007-07-16][345].

[340]: https://www.tcl.tk/
[345]: https://fossil-scm.org/fossil/timeline?c=200707211410&n=10

The first project hosted by Fossil was Fossil itself.  After a
few months of development work, the code was considered stable enough
to begin hosting the [SQLite documentation repository][350] which was
split off from the main SQLite CVS repository on [2007-11-12][355].
After two years of development work on Fossil, the
SQLite source code itself was transfered to Fossil on
[2009-08-11][360].

[350]: https://www.sqlite.org/docsrc/doc/trunk/README.md
[355]: https://www.sqlite.org/docsrc/timeline?c=200711120345&n=10
[360]: https://sqlite.org/src/timeline?c=b0848925babde524&n=12&y=ci







|




|
















|
|












|



|












|






|



|
|
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
was an amazing version control system for its day in that it allowed
multiple developers to be editing the same file at the same time.

[300]: https://en.wikipedia.org/wiki/Concurrent_Versions_System

Though innovative and much loved in its time, CVS was not without problems.
Among those was a lack of visibility into the project history and the
lack of integrated bug tracking.  To address these deficiencies,
the SQLite author developed the [CVSTrac][305] wrapper for CVS beginning
in [2002][310].

[305]: http://cvstrac.org/
[310]: http://cvstrac.org/fossil/timeline?a=19700101&n1=10

CVSTrac greatly improved the usability of CVS and was adopted by
other projects.  CVSTrac also [inspired the design][315] of [Trac][320],
which was a similar system that was (and is) far more widely used.

[315]: https://trac.edgewall.org/wiki/TracHistory
[320]: https://trac.edgewall.org/

Historians can see the influence of CVSTrac on the development of
SQLite.  [Early SQLite check-ins][325] that happened before CVSTrac
often had a check-in comment that was just a "smiley".
That was not an unreasonable check-in comment, as check-in comments
were scarcely seen and of questionable utility in raw CVS.  CVSTrac
changed that, making check-in comments more visible and more useful.
The SQLite developers reacted by creating [better check-in comments][330].

[325]: https://sqlite.org/src/timeline?a=19700101&n1=10
[330]: https://sqlite.org/src/timeline?c=20030101&n1=10&nd

At about this same time, the [Monotone][335] system appeared.
Monotone was one of the first distributed version control systems. As far as
this author is aware, Monotone was the first VCS to make use of
SHA1 to identify artifacts.  Monotone stored its content in an SQLite
database, which is what brought it to the attention of the SQLite architect.
These design choices were a major source of inspiration for Fossil.

[335]: https://www.monotone.ca/

Beginning around 2005, the need for a better version control system
for SQLite began to become evident.  The SQLite architect looked
around for a suitable replacement.  Monotone, Git, and Mercurial were
all considered.  But at that time, none of these supported sync
over ordinary HTTP, none could be run from an inexpensive shell
account on a leased server (this was before the widespread availability
of affordable virtual private servers), and none of them supported anything 
resembling the wiki and ticket features of CVSTrac that had been 
found to be so useful.  And so, the SQLite architect began writing
his own DVCS.

Early prototypes were done in [TCL][340].  As experiments proceeded,
however, it was found that the low-level byte manipulates needed for
things like delta compression and computing diffs
were better implemented in plain old C.
Experiments continued.  Finally, a prototype capable of self-hosting
was devised on [2007-07-16][345].

[340]: https://www.tcl.tk/
[345]: https://fossil-scm.org/fossil/timeline?c=200707211410&n1=10

The first project hosted by Fossil was Fossil itself.  After a
few months of development work, the code was considered stable enough
to begin hosting the [SQLite documentation repository][350] which was
split off from the main SQLite CVS repository on [2007-11-12][355].
After two years of development work on Fossil, the
SQLite source code itself was transferred to Fossil on
[2009-08-11][360].

[350]: https://www.sqlite.org/docsrc/doc/trunk/README.md
[355]: https://www.sqlite.org/docsrc/timeline?c=200711120345&n1=10
[360]: https://sqlite.org/src/timeline?c=b0848925babde524&n1=12&y=ci

Changes to www/hooks.md.

89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
     readers can co-exist with the writer.  Or the script might just
     launch a background process that waits until the hook script finishes
     and the transaction commits before it tries to access the repository
     database.

  *  A push might not deliver all of the artifacts for a checkin.  If
     Fossil knows that a /xfer HTTP request is incomplete, it will defer
     running the after-receive push for 60 seconds, or unti a complete
     /xfer request is received.  This helps to prevent after-receive hooks
     from running when incomplete checkins exist in the repository, but
     it does not provide hard guarantees, as there is no way to do that
     in a distributed system.

  *  The list of artifacts delivered to standard input of the
     after-receive hook will not contain more than 24-hours worth







|







89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
     readers can co-exist with the writer.  Or the script might just
     launch a background process that waits until the hook script finishes
     and the transaction commits before it tries to access the repository
     database.

  *  A push might not deliver all of the artifacts for a checkin.  If
     Fossil knows that a /xfer HTTP request is incomplete, it will defer
     running the after-receive push for 60 seconds, or until a complete
     /xfer request is received.  This helps to prevent after-receive hooks
     from running when incomplete checkins exist in the repository, but
     it does not provide hard guarantees, as there is no way to do that
     in a distributed system.

  *  The list of artifacts delivered to standard input of the
     after-receive hook will not contain more than 24-hours worth

Changes to www/image-format-vs-repo-size.md.

138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
    the zlib binary data compression algorithm. This shows that for
    repos where the image files are committed only once, there is
    virtually no penalty to using BMP or TIFF over PNG. The file sizes
    likely differ only because of differences in zlib settings between
    the cases.

*   Because JPEG’s lossy nature allows it to start smaller and have
    smaller size increases than than PNG, the crossover point with
    BMP/TIFF isn’t until 7-9 checkins in typical runs of this [Monte
    Carlo experiment][mce].  Given a choice among these four file
    formats and a willingness to use lossy image compression, a rational
    tradeoff is to choose JPEG for repositories where each image will
    change fewer than that number of times.









|







138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
    the zlib binary data compression algorithm. This shows that for
    repos where the image files are committed only once, there is
    virtually no penalty to using BMP or TIFF over PNG. The file sizes
    likely differ only because of differences in zlib settings between
    the cases.

*   Because JPEG’s lossy nature allows it to start smaller and have
    smaller size increases than PNG, the crossover point with
    BMP/TIFF isn’t until 7-9 checkins in typical runs of this [Monte
    Carlo experiment][mce].  Given a choice among these four file
    formats and a willingness to use lossy image compression, a rational
    tradeoff is to choose JPEG for repositories where each image will
    change fewer than that number of times.


Changes to www/index.wiki.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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
<title>Home</title>

<h3>What Is Fossil?</h3>

<div style='width:200px;float:right;border:2px solid #446979;padding:10px;margin:0px 10px;'>
<ul>
<li> [/uv/download.html | Download]
<li> [./quickstart.wiki | Quick Start]
<li> [./build.wiki | Install]
<li> [https://fossil-scm.org/forum | Support/Forum ]
<li> [./hints.wiki | Tips &amp; Hints]
<li> [./changes.wiki | Change Log]
<li> [../COPYRIGHT-BSD2.txt | License]
<li> [./userlinks.wiki | User Links]
<li> [./hacker-howto.wiki | Hacker How-To]
<li> [./fossil-v-git.wiki | Fossil vs. Git]
<li> [./permutedindex.html | Documentation Index]
</ul>
<img src="fossil3.gif" align="center">
</div>

<p>Fossil is a simple, high-reliability, distributed software configuration
management system with these advanced features:

  1.  <b>Integrated Bug Tracking, Wiki, Forum, and Technotes</b> -
      In addition to doing [./concepts.wiki | distributed version control]
      like Git and Mercurial,
      Fossil also supports [./bugtheory.wiki | bug tracking],
      [./wikitheory.wiki | wiki], [./forum.wiki | forum], and
      [./event.wiki | technotes].

  2.  <b>Built-in Web Interface</b> -
      Fossil has a built-in,
      [https://fossil-scm.org/skins/index.html | themeable],
      [./serverext.wiki | extensible],
      and intuitive [./webui.wiki | web interface]
      with a rich variety of information pages
      ([./webpage-ex.md|examples]) promoting situational awareness.
      <p>
      This entire website is just a running instance of Fossil.
      The pages you see here are all [./wikitheory.wiki | wiki] or
      [./embeddeddoc.wiki | embedded documentation] or (in the case of
      the [/uv/download.html|download] page)
      [./unvers.wiki | unversioned files].
      When you clone Fossil from one of its
      [./selfhost.wiki | self-hosting repositories],
      you get more than just source code - you get this entire website.

  3.  <b>Self-Contained</b> -
      Fossil is a single self-contained stand-alone executable.
      To install, simply download a
      [/uv/download.html | precompiled binary]
      for Linux, Mac, or Windows and put it on your $PATH.
      [./build.wiki | Easy-to-compile source code] is also available.









  4.  <b>Simple Networking</b> -
      No custom protocols or TCP ports.
      Fossil uses ordinary HTTP (or HTTPS or SSH)
      for network communications, so it works fine from behind
      restrictive firewalls, including [./quickstart.wiki#proxy|proxies].
      The protocol is
      [./stats.wiki | bandwidth efficient] to the point that Fossil can be
      used comfortably over dial-up or over the exceedingly slow Wifi on
      airliners.

  5.  <b>Simple Server Setup</b> -  No server is required, but if you want to
      set one up, Fossil supports [./server/ | several different server
      configurations] including CGI, SCGI, and direct HTTP.
      You can also easily set up your Fossil repository to automatically
      [./mirrortogithub.md | mirror content on GitHub].

  6.  <b>Autosync</b> -
      Fossil supports [./concepts.wiki#workflow | "autosync" mode]
      which helps to keep projects moving
      forward by reducing the amount of needless
      [./branching.wiki | forking and merging] often
      associated with distributed projects.

  7.  <b>Robust &amp; Reliable</b> -
      Fossil stores content using an [./fileformat.wiki | enduring file format]
      in an SQLite database so that transactions are
      atomic even if interrupted by a power loss or system crash.
      Automatic [./selfcheck.wiki | self-checks] verify that all aspects of
      the repository are consistent prior to each commit.

  8.  <b>Free and Open-Source</b> - Uses the [../COPYRIGHT-BSD2.txt|2-clause BSD license].

<hr>
<h3>Latest Release: 2.13 ([/timeline?c=version-2.13|2020-11-01])</h3>

  *  [/uv/download.html|Download]
  *  [./changes.wiki#v2_13|Change Summary]
  *  [/timeline?p=version-2.13&bt=version-2.12&n=all|Check-ins in version 2.13]
  *  [/timeline?d=version-2.13&n=all&nd|Check-ins derived from the 2.13 release]
  *  [/timeline?t=release|Timeline of all past releases]

<hr>
<h3>Quick Start</h3>

  1.  [/uv/download.html|Download] or install using a package manager or
      [./build.wiki|compile from sources].




|
|










|

|





|



|



















|
|





>
>
>
>
>
>
>
>
|
<
|

|


|
<
<
<
<
<
<
<















|


|


|
|
|







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
<title>Home</title>

<h3>What Is Fossil?</h3>

<div style='float:right;border:2px solid #446979;padding:0 15px 10px 0;margin:0 0 10px 10px;'>
<ul style='margin-left: -10px;'>
<li> [/uv/download.html | Download]
<li> [./quickstart.wiki | Quick Start]
<li> [./build.wiki | Install]
<li> [https://fossil-scm.org/forum | Support/Forum ]
<li> [./hints.wiki | Tips &amp; Hints]
<li> [./changes.wiki | Change Log]
<li> [../COPYRIGHT-BSD2.txt | License]
<li> [./userlinks.wiki | User Links]
<li> [./hacker-howto.wiki | Hacker How-To]
<li> [./fossil-v-git.wiki | Fossil vs. Git]
<li> [./permutedindex.html | Doc Index]
</ul>
<center><img src="fossil3.gif" align="center"></center>
</div>

<p>Fossil is a simple, high-reliability, distributed software configuration
management system with these advanced features:

  1.  <b>Project Management</b> -
      In addition to doing [./concepts.wiki | distributed version control]
      like Git and Mercurial,
      Fossil also supports [./bugtheory.wiki | bug tracking],
      [./wikitheory.wiki | wiki], [./forum.wiki | forum], [./chat.md | chat], and
      [./event.wiki | technotes].

  2.  <b>Built-in Web Interface</b> -
      Fossil has a built-in,
      [https://fossil-scm.org/skins/index.html | themeable],
      [./serverext.wiki | extensible],
      and intuitive [./webui.wiki | web interface]
      with a rich variety of information pages
      ([./webpage-ex.md|examples]) promoting situational awareness.
      <p>
      This entire website is just a running instance of Fossil.
      The pages you see here are all [./wikitheory.wiki | wiki] or
      [./embeddeddoc.wiki | embedded documentation] or (in the case of
      the [/uv/download.html|download] page)
      [./unvers.wiki | unversioned files].
      When you clone Fossil from one of its
      [./selfhost.wiki | self-hosting repositories],
      you get more than just source code - you get this entire website.

  3.  <b>All-in-one</b> -
      Fossil is a single self-contained, stand-alone executable.
      To install, simply download a
      [/uv/download.html | precompiled binary]
      for Linux, Mac, or Windows and put it on your $PATH.
      [./build.wiki | Easy-to-compile source code] is also available.


  4.  <b>Self-host Friendly</b> - Stand up a project website
      in minutes using [./server/ | a variety of techniques].
      Fossil is CPU and memory efficient. Most projects can be
      hosted comfortably on a $5/month VPS or a Raspberry Pi.
      You can also set up an automatic
      [./mirrortogithub.md | GitHub mirror].

  5.  <b>Simple Networking</b> -

      Fossil uses ordinary HTTPS (or SSH if you prefer)
      for network communications, so it works fine from behind
      firewalls and [./quickstart.wiki#proxy|proxies].
      The protocol is
      [./stats.wiki | bandwidth efficient] to the point that Fossil can be
      used comfortably over dial-up, weak 3G, or airliner Wifi.








  6.  <b>Autosync</b> -
      Fossil supports [./concepts.wiki#workflow | "autosync" mode]
      which helps to keep projects moving
      forward by reducing the amount of needless
      [./branching.wiki | forking and merging] often
      associated with distributed projects.

  7.  <b>Robust &amp; Reliable</b> -
      Fossil stores content using an [./fileformat.wiki | enduring file format]
      in an SQLite database so that transactions are
      atomic even if interrupted by a power loss or system crash.
      Automatic [./selfcheck.wiki | self-checks] verify that all aspects of
      the repository are consistent prior to each commit.

  8.  <b>Free and Open-Source</b> - [../COPYRIGHT-BSD2.txt|2-clause BSD license].

<hr>
<h3>Latest Release: 2.14 ([/timeline?c=version-2.14|2021-01-20])</h3>

  *  [/uv/download.html|Download]
  *  [./changes.wiki#v2_14|Change Summary]
  *  [/timeline?p=version-2.14&bt=version-2.13&y=ci|Check-ins in version 2.13]
  *  [/timeline?df=version-2.14&y=ci|Check-ins derived from the 2.14 release]
  *  [/timeline?t=release|Timeline of all past releases]

<hr>
<h3>Quick Start</h3>

  1.  [/uv/download.html|Download] or install using a package manager or
      [./build.wiki|compile from sources].

Changes to www/javascript.md.

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
# Use of JavaScript in Fossil

## Philosophy & Policy

The Fossil development project’s policy is to use JavaScript where it
helps make its web UI better, but to offer graceful fallbacks wherever
practical. The intent is that the UI be usable with JavaScript
entirely disabled. In almost all places where Fossil uses JavaScript,
it is an enhancement to provided functionality, and there is always
another way to accomplish a given end without using JavaScript.

This is not to say that Fossil’s fall-backs for such cases are always as
elegant and functional as a no-JS purist might wish. That is simply
because [the vast majority of web users run with JavaScript enabled](#stats),

and a minority of those run with some kind of [conditional JavaScript

blocking](#block) in place. Fossil’s active developers do not deviate from that
norm enough that we have many no-JS purists among us, so the no-JS case
doesn’t get as much attention as some might want. We do [accept code
contributions][cg], and we are philosophically in favor of graceful
fall-backs, so you are welcome to appoint yourself the position of no-JS
czar for the Fossil project!

Evil is in actions, not in nouns: we do not believe JavaScript *can*
be evil. It is an active technology, but the actions that matter here
are those of writing the code and checking it into the Fossil project
repository. None of the JavaScript code in Fossil is evil, a fact we
enforce by being careful about who we give check-in rights on the
repository to and by policing what code does get contributed. The Fossil
project does not accept non-trivial outside contributions.














|
>
|
>
|






|







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
# Use of JavaScript in Fossil

## Philosophy & Policy

The Fossil development project’s policy is to use JavaScript where it
helps make its web UI better, but to offer graceful fallbacks wherever
practical. The intent is that the UI be usable with JavaScript
entirely disabled. In almost all places where Fossil uses JavaScript,
it is an enhancement to provided functionality, and there is always
another way to accomplish a given end without using JavaScript.

This is not to say that Fossil’s fall-backs for such cases are always as
elegant and functional as a no-JS purist might wish. That is simply
because [the vast majority of web users leave JavaScript unconditionally
enabled](#stats), and of the small minority of those that do not, a
large chunk use some kind of [conditional blocking](#block) instead,
rather than disable JavaScript entirely.
Fossil’s active developers do not deviate from that
norm enough that we have many no-JS purists among us, so the no-JS case
doesn’t get as much attention as some might want. We do [accept code
contributions][cg], and we are philosophically in favor of graceful
fall-backs, so you are welcome to appoint yourself the position of no-JS
czar for the Fossil project!

Evil is in actions, not in objects: we do not believe JavaScript *can*
be evil. It is an active technology, but the actions that matter here
are those of writing the code and checking it into the Fossil project
repository. None of the JavaScript code in Fossil is evil, a fact we
enforce by being careful about who we give check-in rights on the
repository to and by policing what code does get contributed. The Fossil
project does not accept non-trivial outside contributions.

62
63
64
65
66
67
68
69
70

71
72
73
74
75
76
77
    a few seconds.

    Most JavaScript-based Fossil pages use less code than that.

    Atop that, Fossil sends HTTP headers to the browser that allow it
    to perform aggressive caching so that typical page loads will skip
    re-loading this content on subsequent loads. These features are
    currently optional: you must either set the new [`fossil server
    --jsmode bundle` option][fsrv] or the corresponding `jsmode` control line

    in your [`fossil cgi`][fcgi] script when setting up your
    [Fossil server][fshome]. That done, Fossil’s JavaScript files will
    load almost instantly from the browser’s cache after the initial
    page load, rather than be re-transferred over the network.

    Between the improved caching and the fact that it’s quicker to
    transfer a partial Ajax page load than reload the entire page, the







|
|
>







64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
    a few seconds.

    Most JavaScript-based Fossil pages use less code than that.

    Atop that, Fossil sends HTTP headers to the browser that allow it
    to perform aggressive caching so that typical page loads will skip
    re-loading this content on subsequent loads. These features are
    currently optional: you must either set the new
    [`fossil server --jsmode bundle` option][fsrv] or the corresponding
    `jsmode` control line
    in your [`fossil cgi`][fcgi] script when setting up your
    [Fossil server][fshome]. That done, Fossil’s JavaScript files will
    load almost instantly from the browser’s cache after the initial
    page load, rather than be re-transferred over the network.

    Between the improved caching and the fact that it’s quicker to
    transfer a partial Ajax page load than reload the entire page, the
90
91
92
93
94
95
96
97
98


99
100
101
102
103
104
105
    that did improve to approach V8’s speed.

    Nowadays JavaScript is, as a rule, astoundingly fast. As the world
    continues to move more and more to web-based applications and
    services, JavaScript engine developers have ample motivation to keep
    their engines fast and competitive.

    Once the scripts are cached, Ajax based page updates are faster than
    the alternative, a full HTTP POST round-trip.



3.  <a id="3pjs"></a>“**Third-party JavaScript cannot be trusted.**”

    Fossil does not use any third-party JavaScript libraries, not even
    very common ones like jQuery. Every bit of JavaScript served by the
    stock version of Fossil was written specifically for the Fossil
    project and is stored [in its code repository][fsrc].







|
|
>
>







93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
    that did improve to approach V8’s speed.

    Nowadays JavaScript is, as a rule, astoundingly fast. As the world
    continues to move more and more to web-based applications and
    services, JavaScript engine developers have ample motivation to keep
    their engines fast and competitive.

    Ajax partial page updates are faster than
    the no-JS alternative, a full HTTP POST round-trip to submit new
    data to the remote server, retrieve an entire new HTML document,
    and re-render the whole thing client-side.

3.  <a id="3pjs"></a>“**Third-party JavaScript cannot be trusted.**”

    Fossil does not use any third-party JavaScript libraries, not even
    very common ones like jQuery. Every bit of JavaScript served by the
    stock version of Fossil was written specifically for the Fossil
    project and is stored [in its code repository][fsrc].
544
545
546
547
548
549
550



















551
552
553
554
555
556
557
_Potential Graceful Fallback:_ You may consider showing the server’s
page generation time rather than the client’s wall clock time in the
local time zone to be a useful fallback for the current feature, so [a
patch to do this][cg] may well be accepted. Since this is not a
*necessary* Fossil feature, an interested user is unlikely to get the
core developers to do this work for them.




















----

## <a id="future"></a>Future Plans for JavaScript in Fossil

As of mid-2020, the informal provisional plan is to increase Fossil
UI's use of JavaScript considerably compared to its historically minimal
uses. To that end, a framework of Fossil-centric APIs is being developed







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







549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
_Potential Graceful Fallback:_ You may consider showing the server’s
page generation time rather than the client’s wall clock time in the
local time zone to be a useful fallback for the current feature, so [a
patch to do this][cg] may well be accepted. Since this is not a
*necessary* Fossil feature, an interested user is unlikely to get the
core developers to do this work for them.


### <a id="chat"></a>Chat

The [chat feature](./chat.md) added in Fossil 2.14 is deeply dependent
on JavaScript. There is no obvious way to do this sort of thing without
active client-side code of some sort.

_Potential Workaround:_ It would not be especially difficult for someone
sufficiently motivated to build a Fossil chat gateway, connecting to
IRC, Jabber, etc. The messages are stored in the repository’s `chat`
table with monotonically increasing IDs, so a poller that did something
like

       SELECT xfrom, xmsg FROM chat WHERE msgid > 1234;

…would pull the messages submitted since the last poll. Making the
gateway bidirectional should be possible as well, as long as it properly
uses SQLite transactions.

----

## <a id="future"></a>Future Plans for JavaScript in Fossil

As of mid-2020, the informal provisional plan is to increase Fossil
UI's use of JavaScript considerably compared to its historically minimal
uses. To that end, a framework of Fossil-centric APIs is being developed

Changes to www/json-api/intro.md.

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

<a id="considerations"></a>
# Technical Problems and Considerations

A random list of considerations which need to be made and potential
problem areas...

-   **Binary data:** HTML4 and JavaScript have no portable way of
    handling binary data, so commands which could potentially deal with
    binary data (e.g. committing a file) are ruled out for the time
    being. HTML5 and accompanying JavaScript additions will binary
    data usable client-side. That said, a JSON interface cannot natively
    work with binary unless it is encoded (base64 or hex or whatever),
    and such encoding would have to be understood on both the server and
    client sides, which may rule out usage in some environments.\
    **Status:** deferred until needed. My current thinking is to send
    URLs instead of binary data, and the URLs would point to some path
    which produces the raw artifact content. We could read POSTed binary
    input, but this might require some re-tooling of fossil's innards
    and it precludes the use of a JSON request envelope, so it would be
    limited to requests which can be configured solely via GET arguments
    (as opposed to POST envelope/payload options). i.e. configure the
    JSON bits via GET and POST the binary data.
-   **64-bit integers:** JSON does not specify integer precision,
    probably because it targets many different platforms and not all
    of them can support more than 32 bits. JavaScript (from which JSON
    derives) supports 53 bits of integer precision. That said, it's



    "highly unlikely" that we'll have any range problems with "only"
    53 bits of precision. The underlying JSON API supports *signed*




    32- or 64-bit integers on both 32- and 64-bit builds, but only if
    "long long" or `int64_t` are available (from the C99 header
    `stdint.h`). Only multi-gig repositories are ever expected to use
    large numbers, and even then only rarely (e.g. via the "stat"
    command).

-   **Timestamps:** for portability this API uses GMT Unix Epoch
    timestamps. They are the most portable time representation out
    there, easily usable in most programming environments. (In hindsight,
    this should have been Unix + Milliseconds, but the API already
    pervasively uses seconds-precision.)
-   **Artifact vs. Artefact:** both are correct vis-a-vis the
    english language but Fossil consistently uses the former, so we’ll
    use that.
-   **Multiple logins per user:** fossil currently does not allow
    multiple active logins for a given user except anonymous. For all
    others, the most recent login wins. This is only a very minor
    annoyance for the HTML interface but will be more problematic for
    JSON clients. e.g. a user might have a ticket poller and a commit poller
    running, and both would need to be logged in.\
    **Status:** as of 20120315 (commit
    [*73038baaa3*](http://www.fossil-scm.org/index.html/info/73038baaa3)),
    fossil allows a user to be logged in multiple times (confirm: only
    within the same network?). The only caveat is that if any one of
    them logs out, it will invalidate the login session for the others.
    This is good enough for the time being, however. It will likely only
    become painful if we actually get enough apps in the wild that
    someone might have some running on his mobile phone and some on his
    PC and some on his server. The workarounds for now are (A) not to
    log out and (B) program apps/applets/widgets to try to re-login
    occasionally. Fossil will at some point expire the login, anyway.
    FIXME: update the expiry time on each request? To do that right we'd
    have to re-set the cookie on each request :/. We could optionally
    add a new JSON request which simply updates the login cookie
    lifetime (e.g. /json/keepalive or expand /json/whoami to do that).







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






















<a id="considerations"></a>
# Technical Problems and Considerations

A random list of considerations which need to be made and potential
problem areas...

-   **Binary data:** JSON is a text serialization method, and it takes
    up the “payloadarea of each HTTP request, so there is no
    reasonable way to include binary data in the JSON message without
    some sort of codec like Base64, for which there is no provision in
    the current JSON API. You will therefore find no JSON API for
    committing changes to a file in the repository, for example. Other
    Fossil APIs such as [`/raw`](/help?cmd=/raw) or
    [`/fileedit`](../fileedit-page.md) may serve you better.








-   **64-bit integers:** The JSON standard does not specify integer precision,
    because it targets many different platforms, and not all of
    them can support more than 32 bits. JavaScript (from which JSON
    derives) supports 53 bits of integer precision, which may affect how
    a given client-side JSON implementation sends large integers to Fossil’s JSON
    API. Our JSON parser can cope with integers larger than 32 bits on input, and it
    can emit them, but it requires platform support. If you’re running
    Fossil on a 64-bit host, you should not run into problems in

    this area, but if you’re on a legacy 32-bit only or a mixed 32/64-bit
    system, it’s possible that some integers in the API could be
    clipped. Realize however that this is a rare case: Fossil currently
    cannot store files large enough to exceed a 32-bit `size_t` value,
    and `time_t` won’t roll past 32-bit integers until 2038. We’re aware


    of no other uses of integers in this API that could even in

    principle exceed the range of a 32-bit integer.
-   **Timestamps:** For portability, this API uses UTC Unix epoch
    timestamps. (`time_t`) They are the most portable time representation out
    there, easily usable in most programming environments. (In

    hindsight, we might better have used a higher-precision time format,


    but changing that now would break API compatibility.)





















Changes to www/makefile.wiki.

156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
The pathnames in the above command might need to be adjusted to get the
directories right.  The point is that the manifest.uuid, manifest, and
VERSION files
in the root of the source tree are the three arguments and
the generated VERSION.h file appears on standard output.

The builtin_data.h header file is generated by a C program: src/mkbuiltin.c.
The builtin_data.h file contains C-langauge byte-array definitions for
the content of resource files used by Fossil.  To generate the
builtin_data.h file, first compile the mkbuiltin.c program, then run:

<blockquote><pre>
mkbuiltin.exe diff.tcl <i>OtherFiles...</i> &gt;builtin_data.h
</pre></blockquote>








|







156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
The pathnames in the above command might need to be adjusted to get the
directories right.  The point is that the manifest.uuid, manifest, and
VERSION files
in the root of the source tree are the three arguments and
the generated VERSION.h file appears on standard output.

The builtin_data.h header file is generated by a C program: src/mkbuiltin.c.
The builtin_data.h file contains C-language byte-array definitions for
the content of resource files used by Fossil.  To generate the
builtin_data.h file, first compile the mkbuiltin.c program, then run:

<blockquote><pre>
mkbuiltin.exe diff.tcl <i>OtherFiles...</i> &gt;builtin_data.h
</pre></blockquote>

Changes to www/mirrortogithub.md.

105
106
107
108
109
110
111
112

113
114
115
116
117
118
119
     subsequent invocations of "`fossil git export`" will know where you
     left off the last time and what new content needs to be moved over into
     Git.  Be careful not to mess with the `.mirror_state` directory or
     any of its contents.  Do not put those files under Git management.  Do
     not edit or delete them.

  *  The name of the "trunk" branch is automatically translated into "master"
     in the Git mirror.


  *  Only check-ins and simple tags are translated to Git.  Git does not
     support wiki or tickets or unversioned content or any of the other
     features of Fossil that make it so convenient to use, so those other
     elements cannot be mirrored in Git.

  *  In Git, all tags must be unique.  If your Fossil repository has the







|
>







105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
     subsequent invocations of "`fossil git export`" will know where you
     left off the last time and what new content needs to be moved over into
     Git.  Be careful not to mess with the `.mirror_state` directory or
     any of its contents.  Do not put those files under Git management.  Do
     not edit or delete them.

  *  The name of the "trunk" branch is automatically translated into "master"
     in the Git mirror unless you give the `--mainbranch` option,
     added in Fossil 2.14.

  *  Only check-ins and simple tags are translated to Git.  Git does not
     support wiki or tickets or unversioned content or any of the other
     features of Fossil that make it so convenient to use, so those other
     elements cannot be mirrored in Git.

  *  In Git, all tags must be unique.  If your Fossil repository has the

Changes to www/mkindex.tcl.

22
23
24
25
26
27
28

29
30
31

32

33
34

35
36
37
38
39
40
41
  build.wiki {Compiling and Installing Fossil}
  cap-theorem.md {Fossil and the CAP Theorem}
  caps/ {Administering User Capabilities}
  caps/admin-v-setup.md {Differences Between Setup and Admin Users}
  caps/ref.html {User Capability Reference}
  cgi.wiki {CGI Script Configuration Options}
  changes.wiki {Fossil Changelog}

  checkin_names.wiki {Check-in And Version Names}
  checkin.wiki {Check-in Checklist}
  childprojects.wiki {Child Projects}

  ckout-workflows.md {Check-Out Workflows}

  copyright-release.html {Contributor License Agreement}
  concepts.wiki {Fossil Core Concepts}

  contribute.wiki {Contributing Code or Documentation To The Fossil Project}
  css-tricks.md {Fossil CSS Tips and Tricks}
  customgraph.md {Theming: Customizing the Timeline Graph}
  customskin.md {Theming: Customizing The Appearance of Web Pages}
  customskin.md {Custom Skins}
  custom_ticket.wiki {Customizing The Ticket System}
  defcsp.md {The Default Content Security Policy}







>



>

>


>







22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
  build.wiki {Compiling and Installing Fossil}
  cap-theorem.md {Fossil and the CAP Theorem}
  caps/ {Administering User Capabilities}
  caps/admin-v-setup.md {Differences Between Setup and Admin Users}
  caps/ref.html {User Capability Reference}
  cgi.wiki {CGI Script Configuration Options}
  changes.wiki {Fossil Changelog}
  chat.md {Fossil Chat}
  checkin_names.wiki {Check-in And Version Names}
  checkin.wiki {Check-in Checklist}
  childprojects.wiki {Child Projects}
  chroot.md {Server Chroot Jail}
  ckout-workflows.md {Check-Out Workflows}
  co-vs-up.md {Checkout vs Update}
  copyright-release.html {Contributor License Agreement}
  concepts.wiki {Fossil Core Concepts}
  contact.md {Developer Contact Information}
  contribute.wiki {Contributing Code or Documentation To The Fossil Project}
  css-tricks.md {Fossil CSS Tips and Tricks}
  customgraph.md {Theming: Customizing the Timeline Graph}
  customskin.md {Theming: Customizing The Appearance of Web Pages}
  customskin.md {Custom Skins}
  custom_ticket.wiki {Customizing The Ticket System}
  defcsp.md {The Default Content Security Policy}
65
66
67
68
69
70
71

72
73
74
75
76
77

78
79
80
81
82
83
84
  hints.wiki {Fossil Tips And Usage Hints}
  history.md {The Purpose And History Of Fossil}
  index.wiki {Home Page}
  inout.wiki {Import And Export To And From Git}
  interwiki.md {Interwiki Links}
  image-format-vs-repo-size.md {Image Format vs Fossil Repo Size}
  javascript.md {Use of JavaScript in Fossil}

  makefile.wiki {The Fossil Build Process}
  mirrorlimitations.md {Limitations On Git Mirrors}
  mirrortogithub.md {How To Mirror A Fossil Repository On GitHub}
  /md_rules {Markdown Formatting Rules}
  newrepo.wiki {How To Create A New Fossil Repository}
  password.wiki {Password Management And Authentication}

  pop.wiki {Principles Of Operation}
  private.wiki {Creating, Syncing, and Deleting Private Branches}
  qandc.wiki {Questions And Criticisms}
  quickstart.wiki {Fossil Quick Start Guide}
  quotes.wiki
      {Quotes: What People Are Saying About Fossil, Git, and DVCSes in General}
  ../test/release-checklist.wiki {Pre-Release Testing Checklist}







>






>







69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
  hints.wiki {Fossil Tips And Usage Hints}
  history.md {The Purpose And History Of Fossil}
  index.wiki {Home Page}
  inout.wiki {Import And Export To And From Git}
  interwiki.md {Interwiki Links}
  image-format-vs-repo-size.md {Image Format vs Fossil Repo Size}
  javascript.md {Use of JavaScript in Fossil}
  loadmgmt.md {Managing Server Load}
  makefile.wiki {The Fossil Build Process}
  mirrorlimitations.md {Limitations On Git Mirrors}
  mirrortogithub.md {How To Mirror A Fossil Repository On GitHub}
  /md_rules {Markdown Formatting Rules}
  newrepo.wiki {How To Create A New Fossil Repository}
  password.wiki {Password Management And Authentication}
  pikchr.md {The Pikchr Diagram Language}
  pop.wiki {Principles Of Operation}
  private.wiki {Creating, Syncing, and Deleting Private Branches}
  qandc.wiki {Questions And Criticisms}
  quickstart.wiki {Fossil Quick Start Guide}
  quotes.wiki
      {Quotes: What People Are Saying About Fossil, Git, and DVCSes in General}
  ../test/release-checklist.wiki {Pre-Release Testing Checklist}
96
97
98
99
100
101
102
103
104

105
106
107
108
109
110
111
  style.wiki {Source Code Style Guidelines}
  ssl.wiki {Using SSL with Fossil}
  sync.wiki {The Fossil Sync Protocol}
  tech_overview.wiki {A Technical Overview Of The Design And Implementation
                      Of Fossil}
  tech_overview.wiki {SQLite Databases Used By Fossil}
  th1.md {The TH1 Scripting Language}
  tickets.wiki {The Fossil Ticket System}
  theory1.wiki {Thoughts On The Design Of The Fossil DVCS}

  unvers.wiki {Unversioned Files}
  webpage-ex.md {Webpage Examples}
  webui.wiki {The Fossil Web Interface}
  whyusefossil.wiki {Why You Should Use Fossil}
  whyusefossil.wiki {Benefits Of Version Control}
  wikitheory.wiki {Wiki In Fossil}
  /wiki_rules {Wiki Formatting Rules}







<

>







102
103
104
105
106
107
108

109
110
111
112
113
114
115
116
117
  style.wiki {Source Code Style Guidelines}
  ssl.wiki {Using SSL with Fossil}
  sync.wiki {The Fossil Sync Protocol}
  tech_overview.wiki {A Technical Overview Of The Design And Implementation
                      Of Fossil}
  tech_overview.wiki {SQLite Databases Used By Fossil}
  th1.md {The TH1 Scripting Language}

  theory1.wiki {Thoughts On The Design Of The Fossil DVCS}
  tickets.wiki {The Fossil Ticket System}
  unvers.wiki {Unversioned Files}
  webpage-ex.md {Webpage Examples}
  webui.wiki {The Fossil Web Interface}
  whyusefossil.wiki {Why You Should Use Fossil}
  whyusefossil.wiki {Benefits Of Version Control}
  wikitheory.wiki {Wiki In Fossil}
  /wiki_rules {Wiki Formatting Rules}

Changes to www/permutedindex.html.

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
<li><a href="caps/">Capabilities &mdash; Administering User</a></li>
<li><a href="caps/ref.html">Capability Reference &mdash; User</a></li>
<li><a href="cgi.wiki"><b>CGI Script Configuration Options</b></a></li>
<li><a href="serverext.wiki">CGI Scripts &mdash; Adding Extensions To A Fossil Server Using</a></li>
<li><a href="serverext.wiki"><b>CGI Server Extensions</b></a></li>
<li><a href="aboutcgi.wiki">CGI Works In Fossil &mdash; How</a></li>
<li><a href="changes.wiki">Changelog &mdash; Fossil</a></li>

<li><a href="checkin_names.wiki"><b>Check-in And Version Names</b></a></li>
<li><a href="checkin.wiki"><b>Check-in Checklist</b></a></li>
<li><a href="ckout-workflows.md"><b>Check-Out Workflows</b></a></li>
<li><a href="checkin.wiki">Checklist &mdash; Check-in</a></li>
<li><a href="../test/release-checklist.wiki">Checklist &mdash; Pre-Release Testing</a></li>
<li><a href="foss-cklist.wiki"><b>Checklist For Successful Open-Source Projects</b></a></li>

<li><a href="selfcheck.wiki">Checks &mdash; Fossil Repository Integrity Self</a></li>
<li><a href="childprojects.wiki"><b>Child Projects</b></a></li>
<li><a href="hashpolicy.wiki">Choosing Between SHA1 and SHA3-256 &mdash; Hash Policy:</a></li>

<li><a href="contribute.wiki">Code or Documentation To The Fossil Project &mdash; Contributing</a></li>
<li><a href="style.wiki">Code Style Guidelines &mdash; Source</a></li>
<li><a href="../../../help">Commands and Webpages &mdash; Lists of</a></li>
<li><a href="build.wiki"><b>Compiling and Installing Fossil</b></a></li>
<li><a href="concepts.wiki">Concepts &mdash; Fossil Core</a></li>
<li><a href="cgi.wiki">Configuration Options &mdash; CGI Script</a></li>
<li><a href="server/">Configure A Fossil Server &mdash; How To</a></li>
<li><a href="rebaseharm.md">Considered Harmful &mdash; Rebase</a></li>

<li><a href="shunning.wiki">Content From Fossil &mdash; Shunning: Deleting</a></li>
<li><a href="defcsp.md">Content Security Policy &mdash; The Default</a></li>
<li><a href="contribute.wiki"><b>Contributing Code or Documentation To The Fossil Project</b></a></li>
<li><a href="copyright-release.html"><b>Contributor License Agreement</b></a></li>
<li><a href="whyusefossil.wiki">Control &mdash; Benefits Of Version</a></li>
<li><a href="concepts.wiki">Core Concepts &mdash; Fossil</a></li>
<li><a href="newrepo.wiki">Create A New Fossil Repository &mdash; How To</a></li>







>






>



>








>







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
<li><a href="caps/">Capabilities &mdash; Administering User</a></li>
<li><a href="caps/ref.html">Capability Reference &mdash; User</a></li>
<li><a href="cgi.wiki"><b>CGI Script Configuration Options</b></a></li>
<li><a href="serverext.wiki">CGI Scripts &mdash; Adding Extensions To A Fossil Server Using</a></li>
<li><a href="serverext.wiki"><b>CGI Server Extensions</b></a></li>
<li><a href="aboutcgi.wiki">CGI Works In Fossil &mdash; How</a></li>
<li><a href="changes.wiki">Changelog &mdash; Fossil</a></li>
<li><a href="chat.md">Chat &mdash; Fossil</a></li>
<li><a href="checkin_names.wiki"><b>Check-in And Version Names</b></a></li>
<li><a href="checkin.wiki"><b>Check-in Checklist</b></a></li>
<li><a href="ckout-workflows.md"><b>Check-Out Workflows</b></a></li>
<li><a href="checkin.wiki">Checklist &mdash; Check-in</a></li>
<li><a href="../test/release-checklist.wiki">Checklist &mdash; Pre-Release Testing</a></li>
<li><a href="foss-cklist.wiki"><b>Checklist For Successful Open-Source Projects</b></a></li>
<li><a href="co-vs-up.md"><b>Checkout vs Update</b></a></li>
<li><a href="selfcheck.wiki">Checks &mdash; Fossil Repository Integrity Self</a></li>
<li><a href="childprojects.wiki"><b>Child Projects</b></a></li>
<li><a href="hashpolicy.wiki">Choosing Between SHA1 and SHA3-256 &mdash; Hash Policy:</a></li>
<li><a href="chroot.md">Chroot Jail &mdash; Server</a></li>
<li><a href="contribute.wiki">Code or Documentation To The Fossil Project &mdash; Contributing</a></li>
<li><a href="style.wiki">Code Style Guidelines &mdash; Source</a></li>
<li><a href="../../../help">Commands and Webpages &mdash; Lists of</a></li>
<li><a href="build.wiki"><b>Compiling and Installing Fossil</b></a></li>
<li><a href="concepts.wiki">Concepts &mdash; Fossil Core</a></li>
<li><a href="cgi.wiki">Configuration Options &mdash; CGI Script</a></li>
<li><a href="server/">Configure A Fossil Server &mdash; How To</a></li>
<li><a href="rebaseharm.md">Considered Harmful &mdash; Rebase</a></li>
<li><a href="contact.md">Contact Information &mdash; Developer</a></li>
<li><a href="shunning.wiki">Content From Fossil &mdash; Shunning: Deleting</a></li>
<li><a href="defcsp.md">Content Security Policy &mdash; The Default</a></li>
<li><a href="contribute.wiki"><b>Contributing Code or Documentation To The Fossil Project</b></a></li>
<li><a href="copyright-release.html"><b>Contributor License Agreement</b></a></li>
<li><a href="whyusefossil.wiki">Control &mdash; Benefits Of Version</a></li>
<li><a href="concepts.wiki">Core Concepts &mdash; Fossil</a></li>
<li><a href="newrepo.wiki">Create A New Fossil Repository &mdash; How To</a></li>
94
95
96
97
98
99
100

101

102
103
104
105
106
107
108
<li><a href="antibot.wiki"><b>Defense against Spiders and Bots</b></a></li>
<li><a href="shunning.wiki">Deleting Content From Fossil &mdash; Shunning:</a></li>
<li><a href="private.wiki">Deleting Private Branches &mdash; Creating, Syncing, and</a></li>
<li><a href="delta_encoder_algorithm.wiki">Delta Encoding Algorithm &mdash; Fossil</a></li>
<li><a href="delta_format.wiki">Delta Format &mdash; Fossil</a></li>
<li><a href="tech_overview.wiki">Design And Implementation Of Fossil &mdash; A Technical Overview Of The</a></li>
<li><a href="theory1.wiki">Design Of The Fossil DVCS &mdash; Thoughts On The</a></li>

<li><a href="hacker-howto.wiki">Developers Guide &mdash; Fossil</a></li>

<li><a href="caps/admin-v-setup.md"><b>Differences Between Setup and Admin Users</b></a></li>
<li><a href="embeddeddoc.wiki">Documentation &mdash; Embedded Project</a></li>
<li><a href="contribute.wiki">Documentation To The Fossil Project &mdash; Contributing Code or</a></li>
<li><a href="aboutdownload.wiki">Download Page Works &mdash; How The</a></li>
<li><a href="theory1.wiki">DVCS &mdash; Thoughts On The Design Of The Fossil</a></li>
<li><a href="quotes.wiki">DVCSes in General &mdash; Quotes: What People Are Saying About Fossil, Git, and</a></li>
<li><a href="alerts.md"><b>Email Alerts And Notifications</b></a></li>







>

>







98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
<li><a href="antibot.wiki"><b>Defense against Spiders and Bots</b></a></li>
<li><a href="shunning.wiki">Deleting Content From Fossil &mdash; Shunning:</a></li>
<li><a href="private.wiki">Deleting Private Branches &mdash; Creating, Syncing, and</a></li>
<li><a href="delta_encoder_algorithm.wiki">Delta Encoding Algorithm &mdash; Fossil</a></li>
<li><a href="delta_format.wiki">Delta Format &mdash; Fossil</a></li>
<li><a href="tech_overview.wiki">Design And Implementation Of Fossil &mdash; A Technical Overview Of The</a></li>
<li><a href="theory1.wiki">Design Of The Fossil DVCS &mdash; Thoughts On The</a></li>
<li><a href="contact.md"><b>Developer Contact Information</b></a></li>
<li><a href="hacker-howto.wiki">Developers Guide &mdash; Fossil</a></li>
<li><a href="pikchr.md">Diagram Language &mdash; The Pikchr</a></li>
<li><a href="caps/admin-v-setup.md"><b>Differences Between Setup and Admin Users</b></a></li>
<li><a href="embeddeddoc.wiki">Documentation &mdash; Embedded Project</a></li>
<li><a href="contribute.wiki">Documentation To The Fossil Project &mdash; Contributing Code or</a></li>
<li><a href="aboutdownload.wiki">Download Page Works &mdash; How The</a></li>
<li><a href="theory1.wiki">DVCS &mdash; Thoughts On The Design Of The Fossil</a></li>
<li><a href="quotes.wiki">DVCSes in General &mdash; Quotes: What People Are Saying About Fossil, Git, and</a></li>
<li><a href="alerts.md"><b>Email Alerts And Notifications</b></a></li>
126
127
128
129
130
131
132

133
134
135
136
137
138
139
<li><a href="fileformat.wiki">Format &mdash; Fossil File</a></li>
<li><a href="image-format-vs-repo-size.md">Format vs Fossil Repo Size &mdash; Image</a></li>
<li><a href="../../../md_rules">Formatting Rules &mdash; Markdown</a></li>
<li><a href="../../../wiki_rules">Formatting Rules &mdash; Wiki</a></li>
<li><a href="forum.wiki">Forums &mdash; Fossil</a></li>
<li><a href="cap-theorem.md"><b>Fossil and the CAP Theorem</b></a></li>
<li><a href="changes.wiki"><b>Fossil Changelog</b></a></li>

<li><a href="concepts.wiki"><b>Fossil Core Concepts</b></a></li>
<li><a href="css-tricks.md"><b>Fossil CSS Tips and Tricks</b></a></li>
<li><a href="delta_encoder_algorithm.wiki"><b>Fossil Delta Encoding Algorithm</b></a></li>
<li><a href="delta_format.wiki"><b>Fossil Delta Format</b></a></li>
<li><a href="hacker-howto.wiki"><b>Fossil Developers Guide</b></a></li>
<li><a href="fileformat.wiki"><b>Fossil File Format</b></a></li>
<li><a href="forum.wiki"><b>Fossil Forums</b></a></li>







>







132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
<li><a href="fileformat.wiki">Format &mdash; Fossil File</a></li>
<li><a href="image-format-vs-repo-size.md">Format vs Fossil Repo Size &mdash; Image</a></li>
<li><a href="../../../md_rules">Formatting Rules &mdash; Markdown</a></li>
<li><a href="../../../wiki_rules">Formatting Rules &mdash; Wiki</a></li>
<li><a href="forum.wiki">Forums &mdash; Fossil</a></li>
<li><a href="cap-theorem.md"><b>Fossil and the CAP Theorem</b></a></li>
<li><a href="changes.wiki"><b>Fossil Changelog</b></a></li>
<li><a href="chat.md"><b>Fossil Chat</b></a></li>
<li><a href="concepts.wiki"><b>Fossil Core Concepts</b></a></li>
<li><a href="css-tricks.md"><b>Fossil CSS Tips and Tricks</b></a></li>
<li><a href="delta_encoder_algorithm.wiki"><b>Fossil Delta Encoding Algorithm</b></a></li>
<li><a href="delta_format.wiki"><b>Fossil Delta Format</b></a></li>
<li><a href="hacker-howto.wiki"><b>Fossil Developers Guide</b></a></li>
<li><a href="fileformat.wiki"><b>Fossil File Format</b></a></li>
<li><a href="forum.wiki"><b>Fossil Forums</b></a></li>
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
<li><a href="encryptedrepos.wiki"><b>How To Use Encrypted Repositories</b></a></li>
<li><a href="hacker-howto.wiki">How-To &mdash; Hacker</a></li>
<li><a href="fossil-from-msvc.wiki">IDE &mdash; Integrating Fossil in the Microsoft Express 2010</a></li>
<li><a href="hashes.md">Identification &mdash; Hashes: Fossil Artifact</a></li>
<li><a href="image-format-vs-repo-size.md"><b>Image Format vs Fossil Repo Size</b></a></li>
<li><a href="tech_overview.wiki">Implementation Of Fossil &mdash; A Technical Overview Of The Design And</a></li>
<li><a href="inout.wiki"><b>Import And Export To And From Git</b></a></li>

<li><a href="build.wiki">Installing Fossil &mdash; Compiling and</a></li>
<li><a href="fossil-from-msvc.wiki"><b>Integrating Fossil in the Microsoft Express 2010 IDE</b></a></li>
<li><a href="selfcheck.wiki">Integrity Self Checks &mdash; Fossil Repository</a></li>
<li><a href="webui.wiki">Interface &mdash; The Fossil Web</a></li>
<li><a href="interwiki.md"><b>Interwiki Links</b></a></li>
<li><a href="blockchain.md"><b>Is Fossil A Blockchain?</b></a></li>

<li><a href="javascript.md">JavaScript in Fossil &mdash; Use of</a></li>

<li><a href="th1.md">Language &mdash; The TH1 Scripting</a></li>
<li><a href="copyright-release.html">License Agreement &mdash; Contributor</a></li>
<li><a href="mirrorlimitations.md"><b>Limitations On Git Mirrors</b></a></li>
<li><a href="interwiki.md">Links &mdash; Interwiki</a></li>
<li><a href="../../../help"><b>Lists of Commands and Webpages</b></a></li>

<li><a href="password.wiki">Management And Authentication &mdash; Password</a></li>

<li><a href="../../../sitemap">Map &mdash; Site</a></li>
<li><a href="../../../md_rules"><b>Markdown Formatting Rules</b></a></li>
<li><a href="backoffice.md">mechanism of Fossil &mdash; The Backoffice</a></li>
<li><a href="branching.wiki">Merging, and Tagging &mdash; Branching, Forking,</a></li>
<li><a href="fossil-from-msvc.wiki">Microsoft Express 2010 IDE &mdash; Integrating Fossil in the</a></li>
<li><a href="fiveminutes.wiki">Minutes as a Single User &mdash; Up and Running in 5</a></li>
<li><a href="mirrortogithub.md">Mirror A Fossil Repository On GitHub &mdash; How To</a></li>







>






>

>





>

>







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
<li><a href="encryptedrepos.wiki"><b>How To Use Encrypted Repositories</b></a></li>
<li><a href="hacker-howto.wiki">How-To &mdash; Hacker</a></li>
<li><a href="fossil-from-msvc.wiki">IDE &mdash; Integrating Fossil in the Microsoft Express 2010</a></li>
<li><a href="hashes.md">Identification &mdash; Hashes: Fossil Artifact</a></li>
<li><a href="image-format-vs-repo-size.md"><b>Image Format vs Fossil Repo Size</b></a></li>
<li><a href="tech_overview.wiki">Implementation Of Fossil &mdash; A Technical Overview Of The Design And</a></li>
<li><a href="inout.wiki"><b>Import And Export To And From Git</b></a></li>
<li><a href="contact.md">Information &mdash; Developer Contact</a></li>
<li><a href="build.wiki">Installing Fossil &mdash; Compiling and</a></li>
<li><a href="fossil-from-msvc.wiki"><b>Integrating Fossil in the Microsoft Express 2010 IDE</b></a></li>
<li><a href="selfcheck.wiki">Integrity Self Checks &mdash; Fossil Repository</a></li>
<li><a href="webui.wiki">Interface &mdash; The Fossil Web</a></li>
<li><a href="interwiki.md"><b>Interwiki Links</b></a></li>
<li><a href="blockchain.md"><b>Is Fossil A Blockchain?</b></a></li>
<li><a href="chroot.md">Jail &mdash; Server Chroot</a></li>
<li><a href="javascript.md">JavaScript in Fossil &mdash; Use of</a></li>
<li><a href="pikchr.md">Language &mdash; The Pikchr Diagram</a></li>
<li><a href="th1.md">Language &mdash; The TH1 Scripting</a></li>
<li><a href="copyright-release.html">License Agreement &mdash; Contributor</a></li>
<li><a href="mirrorlimitations.md"><b>Limitations On Git Mirrors</b></a></li>
<li><a href="interwiki.md">Links &mdash; Interwiki</a></li>
<li><a href="../../../help"><b>Lists of Commands and Webpages</b></a></li>
<li><a href="loadmgmt.md">Load &mdash; Managing Server</a></li>
<li><a href="password.wiki">Management And Authentication &mdash; Password</a></li>
<li><a href="loadmgmt.md"><b>Managing Server Load</b></a></li>
<li><a href="../../../sitemap">Map &mdash; Site</a></li>
<li><a href="../../../md_rules"><b>Markdown Formatting Rules</b></a></li>
<li><a href="backoffice.md">mechanism of Fossil &mdash; The Backoffice</a></li>
<li><a href="branching.wiki">Merging, and Tagging &mdash; Branching, Forking,</a></li>
<li><a href="fossil-from-msvc.wiki">Microsoft Express 2010 IDE &mdash; Integrating Fossil in the</a></li>
<li><a href="fiveminutes.wiki">Minutes as a Single User &mdash; Up and Running in 5</a></li>
<li><a href="mirrortogithub.md">Mirror A Fossil Repository On GitHub &mdash; How To</a></li>
219
220
221
222
223
224
225

226
227
228
229
230
231
232
<li><a href="fileedit-page.md">Page &mdash; The fileedit</a></li>
<li><a href="aboutdownload.wiki">Page Works &mdash; How The Download</a></li>
<li><a href="customskin.md">Pages &mdash; Theming: Customizing The Appearance of Web</a></li>
<li><a href="password.wiki"><b>Password Management And Authentication</b></a></li>
<li><a href="globs.md">Patterns &mdash; File Name Glob</a></li>
<li><a href="quotes.wiki">People Are Saying About Fossil, Git, and DVCSes in General &mdash; Quotes: What</a></li>
<li><a href="stats.wiki"><b>Performance Statistics</b></a></li>

<li><a href="defcsp.md">Policy &mdash; The Default Content Security</a></li>
<li><a href="hashpolicy.wiki">Policy: Choosing Between SHA1 and SHA3-256 &mdash; Hash</a></li>
<li><a href="grep.md">POSIX grep &mdash; Fossil grep vs</a></li>
<li><a href="../test/release-checklist.wiki"><b>Pre-Release Testing Checklist</b></a></li>
<li><a href="pop.wiki"><b>Principles Of Operation</b></a></li>
<li><a href="private.wiki">Private Branches &mdash; Creating, Syncing, and Deleting</a></li>
<li><a href="makefile.wiki">Process &mdash; The Fossil Build</a></li>







>







231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
<li><a href="fileedit-page.md">Page &mdash; The fileedit</a></li>
<li><a href="aboutdownload.wiki">Page Works &mdash; How The Download</a></li>
<li><a href="customskin.md">Pages &mdash; Theming: Customizing The Appearance of Web</a></li>
<li><a href="password.wiki"><b>Password Management And Authentication</b></a></li>
<li><a href="globs.md">Patterns &mdash; File Name Glob</a></li>
<li><a href="quotes.wiki">People Are Saying About Fossil, Git, and DVCSes in General &mdash; Quotes: What</a></li>
<li><a href="stats.wiki"><b>Performance Statistics</b></a></li>
<li><a href="pikchr.md">Pikchr Diagram Language &mdash; The</a></li>
<li><a href="defcsp.md">Policy &mdash; The Default Content Security</a></li>
<li><a href="hashpolicy.wiki">Policy: Choosing Between SHA1 and SHA3-256 &mdash; Hash</a></li>
<li><a href="grep.md">POSIX grep &mdash; Fossil grep vs</a></li>
<li><a href="../test/release-checklist.wiki"><b>Pre-Release Testing Checklist</b></a></li>
<li><a href="pop.wiki"><b>Principles Of Operation</b></a></li>
<li><a href="private.wiki">Private Branches &mdash; Creating, Syncing, and Deleting</a></li>
<li><a href="makefile.wiki">Process &mdash; The Fossil Build</a></li>
259
260
261
262
263
264
265

266

267
268
269
270
271
272
273
<li><a href="cgi.wiki">Script Configuration Options &mdash; CGI</a></li>
<li><a href="th1.md">Scripting Language &mdash; The TH1</a></li>
<li><a href="serverext.wiki">Scripts &mdash; Adding Extensions To A Fossil Server Using CGI</a></li>
<li><a href="defcsp.md">Security Policy &mdash; The Default Content</a></li>
<li><a href="selfcheck.wiki">Self Checks &mdash; Fossil Repository Integrity</a></li>
<li><a href="selfhost.wiki">Self Hosting Repositories &mdash; Fossil</a></li>
<li><a href="server/">Server &mdash; How To Configure A Fossil</a></li>

<li><a href="serverext.wiki">Server Extensions &mdash; CGI</a></li>

<li><a href="serverext.wiki">Server Using CGI Scripts &mdash; Adding Extensions To A Fossil</a></li>
<li><a href="settings.wiki">Settings &mdash; Fossil</a></li>
<li><a href="caps/admin-v-setup.md">Setup and Admin Users &mdash; Differences Between</a></li>
<li><a href="hashpolicy.wiki">SHA1 and SHA3-256 &mdash; Hash Policy: Choosing Between</a></li>
<li><a href="hashpolicy.wiki">SHA3-256 &mdash; Hash Policy: Choosing Between SHA1 and</a></li>
<li><a href="shunning.wiki"><b>Shunning: Deleting Content From Fossil</b></a></li>
<li><a href="fiveminutes.wiki">Single User &mdash; Up and Running in 5 Minutes as a</a></li>







>

>







272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
<li><a href="cgi.wiki">Script Configuration Options &mdash; CGI</a></li>
<li><a href="th1.md">Scripting Language &mdash; The TH1</a></li>
<li><a href="serverext.wiki">Scripts &mdash; Adding Extensions To A Fossil Server Using CGI</a></li>
<li><a href="defcsp.md">Security Policy &mdash; The Default Content</a></li>
<li><a href="selfcheck.wiki">Self Checks &mdash; Fossil Repository Integrity</a></li>
<li><a href="selfhost.wiki">Self Hosting Repositories &mdash; Fossil</a></li>
<li><a href="server/">Server &mdash; How To Configure A Fossil</a></li>
<li><a href="chroot.md"><b>Server Chroot Jail</b></a></li>
<li><a href="serverext.wiki">Server Extensions &mdash; CGI</a></li>
<li><a href="loadmgmt.md">Server Load &mdash; Managing</a></li>
<li><a href="serverext.wiki">Server Using CGI Scripts &mdash; Adding Extensions To A Fossil</a></li>
<li><a href="settings.wiki">Settings &mdash; Fossil</a></li>
<li><a href="caps/admin-v-setup.md">Setup and Admin Users &mdash; Differences Between</a></li>
<li><a href="hashpolicy.wiki">SHA1 and SHA3-256 &mdash; Hash Policy: Choosing Between</a></li>
<li><a href="hashpolicy.wiki">SHA3-256 &mdash; Hash Policy: Choosing Between SHA1 and</a></li>
<li><a href="shunning.wiki"><b>Shunning: Deleting Content From Fossil</b></a></li>
<li><a href="fiveminutes.wiki">Single User &mdash; Up and Running in 5 Minutes as a</a></li>
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
<li><a href="blame.wiki"><b>The Annotate/Blame Algorithm Of Fossil</b></a></li>
<li><a href="defcsp.md"><b>The Default Content Security Policy</b></a></li>
<li><a href="fileedit-page.md"><b>The fileedit Page</b></a></li>
<li><a href="makefile.wiki"><b>The Fossil Build Process</b></a></li>
<li><a href="sync.wiki"><b>The Fossil Sync Protocol</b></a></li>
<li><a href="tickets.wiki"><b>The Fossil Ticket System</b></a></li>
<li><a href="webui.wiki"><b>The Fossil Web Interface</b></a></li>

<li><a href="history.md"><b>The Purpose And History Of Fossil</b></a></li>
<li><a href="th1.md"><b>The TH1 Scripting Language</b></a></li>
<li><a href="customskin.md"><b>Theming: Customizing The Appearance of Web Pages</b></a></li>
<li><a href="customgraph.md"><b>Theming: Customizing the Timeline Graph</b></a></li>
<li><a href="cap-theorem.md">Theorem &mdash; Fossil and the CAP</a></li>
<li><a href="theory1.wiki"><b>Thoughts On The Design Of The Fossil DVCS</b></a></li>
<li><a href="custom_ticket.wiki">Ticket System &mdash; Customizing The</a></li>
<li><a href="tickets.wiki">Ticket System &mdash; The Fossil</a></li>
<li><a href="customgraph.md">Timeline Graph &mdash; Theming: Customizing the</a></li>
<li><a href="css-tricks.md">Tips and Tricks &mdash; Fossil CSS</a></li>
<li><a href="hints.wiki">Tips And Usage Hints &mdash; Fossil</a></li>
<li><a href="bugtheory.wiki">Tracking In Fossil &mdash; Bug</a></li>
<li><a href="gitusers.md">Translation Guide &mdash; Git to Fossil</a></li>
<li><a href="css-tricks.md">Tricks &mdash; Fossil CSS Tips and</a></li>
<li><a href="unvers.wiki"><b>Unversioned Files</b></a></li>
<li><a href="backup.md">Up a Remote Fossil Repository &mdash; Backing</a></li>
<li><a href="fiveminutes.wiki"><b>Up and Running in 5 Minutes as a Single User</b></a></li>

<li><a href="hints.wiki">Usage Hints &mdash; Fossil Tips And</a></li>
<li><a href="javascript.md"><b>Use of JavaScript in Fossil</b></a></li>
<li><a href="fiveminutes.wiki">User &mdash; Up and Running in 5 Minutes as a Single</a></li>
<li><a href="caps/">User Capabilities &mdash; Administering</a></li>
<li><a href="caps/ref.html"><b>User Capability Reference</b></a></li>
<li><a href="caps/admin-v-setup.md">Users &mdash; Differences Between Setup and Admin</a></li>
<li><a href="serverext.wiki">Using CGI Scripts &mdash; Adding Extensions To A Fossil Server</a></li>
<li><a href="ssl.wiki"><b>Using SSL with Fossil</b></a></li>
<li><a href="env-opts.md">Variables and Global Options &mdash; Environment</a></li>
<li><a href="whyusefossil.wiki">Version Control &mdash; Benefits Of</a></li>
<li><a href="checkin_names.wiki">Version Names &mdash; Check-in And</a></li>
<li><a href="fossil-v-git.wiki">Versus Git &mdash; Fossil</a></li>
<li><a href="image-format-vs-repo-size.md">vs Fossil Repo Size &mdash; Image Format</a></li>
<li><a href="grep.md">vs POSIX grep &mdash; Fossil grep</a></li>

<li><a href="webui.wiki">Web Interface &mdash; The Fossil</a></li>
<li><a href="customskin.md">Web Pages &mdash; Theming: Customizing The Appearance of</a></li>
<li><a href="webpage-ex.md"><b>Webpage Examples</b></a></li>
<li><a href="../../../help">Webpages &mdash; Lists of Commands and</a></li>
<li><a href="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General &mdash; Quotes:</a></li>
<li><a href="whyusefossil.wiki"><b>Why You Should Use Fossil</b></a></li>
<li><a href="../../../wiki_rules"><b>Wiki Formatting Rules</b></a></li>
<li><a href="wikitheory.wiki"><b>Wiki In Fossil</b></a></li>
<li><a href="ckout-workflows.md">Workflows &mdash; Check-Out</a></li>
<li><a href="aboutdownload.wiki">Works &mdash; How The Download Page</a></li>
<li><a href="aboutcgi.wiki">Works In Fossil &mdash; How CGI</a></li>
<li><a href="whyusefossil.wiki">You Should Use Fossil &mdash; Why</a></li>
</ul></div>







>

















>














>













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
<li><a href="blame.wiki"><b>The Annotate/Blame Algorithm Of Fossil</b></a></li>
<li><a href="defcsp.md"><b>The Default Content Security Policy</b></a></li>
<li><a href="fileedit-page.md"><b>The fileedit Page</b></a></li>
<li><a href="makefile.wiki"><b>The Fossil Build Process</b></a></li>
<li><a href="sync.wiki"><b>The Fossil Sync Protocol</b></a></li>
<li><a href="tickets.wiki"><b>The Fossil Ticket System</b></a></li>
<li><a href="webui.wiki"><b>The Fossil Web Interface</b></a></li>
<li><a href="pikchr.md"><b>The Pikchr Diagram Language</b></a></li>
<li><a href="history.md"><b>The Purpose And History Of Fossil</b></a></li>
<li><a href="th1.md"><b>The TH1 Scripting Language</b></a></li>
<li><a href="customskin.md"><b>Theming: Customizing The Appearance of Web Pages</b></a></li>
<li><a href="customgraph.md"><b>Theming: Customizing the Timeline Graph</b></a></li>
<li><a href="cap-theorem.md">Theorem &mdash; Fossil and the CAP</a></li>
<li><a href="theory1.wiki"><b>Thoughts On The Design Of The Fossil DVCS</b></a></li>
<li><a href="custom_ticket.wiki">Ticket System &mdash; Customizing The</a></li>
<li><a href="tickets.wiki">Ticket System &mdash; The Fossil</a></li>
<li><a href="customgraph.md">Timeline Graph &mdash; Theming: Customizing the</a></li>
<li><a href="css-tricks.md">Tips and Tricks &mdash; Fossil CSS</a></li>
<li><a href="hints.wiki">Tips And Usage Hints &mdash; Fossil</a></li>
<li><a href="bugtheory.wiki">Tracking In Fossil &mdash; Bug</a></li>
<li><a href="gitusers.md">Translation Guide &mdash; Git to Fossil</a></li>
<li><a href="css-tricks.md">Tricks &mdash; Fossil CSS Tips and</a></li>
<li><a href="unvers.wiki"><b>Unversioned Files</b></a></li>
<li><a href="backup.md">Up a Remote Fossil Repository &mdash; Backing</a></li>
<li><a href="fiveminutes.wiki"><b>Up and Running in 5 Minutes as a Single User</b></a></li>
<li><a href="co-vs-up.md">Update &mdash; Checkout vs</a></li>
<li><a href="hints.wiki">Usage Hints &mdash; Fossil Tips And</a></li>
<li><a href="javascript.md"><b>Use of JavaScript in Fossil</b></a></li>
<li><a href="fiveminutes.wiki">User &mdash; Up and Running in 5 Minutes as a Single</a></li>
<li><a href="caps/">User Capabilities &mdash; Administering</a></li>
<li><a href="caps/ref.html"><b>User Capability Reference</b></a></li>
<li><a href="caps/admin-v-setup.md">Users &mdash; Differences Between Setup and Admin</a></li>
<li><a href="serverext.wiki">Using CGI Scripts &mdash; Adding Extensions To A Fossil Server</a></li>
<li><a href="ssl.wiki"><b>Using SSL with Fossil</b></a></li>
<li><a href="env-opts.md">Variables and Global Options &mdash; Environment</a></li>
<li><a href="whyusefossil.wiki">Version Control &mdash; Benefits Of</a></li>
<li><a href="checkin_names.wiki">Version Names &mdash; Check-in And</a></li>
<li><a href="fossil-v-git.wiki">Versus Git &mdash; Fossil</a></li>
<li><a href="image-format-vs-repo-size.md">vs Fossil Repo Size &mdash; Image Format</a></li>
<li><a href="grep.md">vs POSIX grep &mdash; Fossil grep</a></li>
<li><a href="co-vs-up.md">vs Update &mdash; Checkout</a></li>
<li><a href="webui.wiki">Web Interface &mdash; The Fossil</a></li>
<li><a href="customskin.md">Web Pages &mdash; Theming: Customizing The Appearance of</a></li>
<li><a href="webpage-ex.md"><b>Webpage Examples</b></a></li>
<li><a href="../../../help">Webpages &mdash; Lists of Commands and</a></li>
<li><a href="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General &mdash; Quotes:</a></li>
<li><a href="whyusefossil.wiki"><b>Why You Should Use Fossil</b></a></li>
<li><a href="../../../wiki_rules"><b>Wiki Formatting Rules</b></a></li>
<li><a href="wikitheory.wiki"><b>Wiki In Fossil</b></a></li>
<li><a href="ckout-workflows.md">Workflows &mdash; Check-Out</a></li>
<li><a href="aboutdownload.wiki">Works &mdash; How The Download Page</a></li>
<li><a href="aboutcgi.wiki">Works In Fossil &mdash; How CGI</a></li>
<li><a href="whyusefossil.wiki">You Should Use Fossil &mdash; Why</a></li>
</ul></div>

Changes to www/quickstart.wiki.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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
<title>Fossil Quick Start Guide</title>
<h1 align="center">Fossil Quick Start</h1>

<p>This is a guide to help you get started using the Fossil [https://en.wikipedia.org/wiki/Distributed_version_control|Distributed Version Control System] quickly
and painlessly.</p>

<h2 id="install">Installing</h2>

    <p>Fossil is a single self-contained C program.  You need to
    either download a
    [https://www.fossil-scm.org/fossil/uv/download.html|precompiled
    binary]
    or <a href="build.wiki">compile it yourself</a> from sources.
    Install Fossil by putting the fossil binary
    someplace on your $PATH.</p>
    You can test that Fossil is present and working like this:

    <blockquote>
    <b>
    fossil version<br>
    <tt>This is fossil version 2.13 [309af345ab] 2020-09-28 04:02:55 UTC</tt><br>
    </b>
    </blockquote>

<h2 id="workflow" name="fslclone">General Work Flow</h2>

    <p>Fossil works with repository files (a database in a single file with the project's
    complete history) and with checked-out local trees (the working directory
    you use to do your work). 
    (See [./whyusefossil.wiki#definitions | definitions] for more background.)
    The workflow looks like this:</p>

    <ul>
        <li>Create or clone a repository file.  ([/help/init|fossil init] or
            [/help/clone | fossil clone])
        <li>Check out a local tree.  ([/help/open | fossil open])
        <li>Perform operations on the repository (including repository
            configuration).
    </ul>

    Fossil can be entirely driven from the command line. Many features
    can also be conveniently accessed from the build-in web interface.

    <p>The following sections give a brief overview of these
    operations.</p>

<h2 id="new">Starting A New Project</h2>

    <p>To start a new project with fossil create a new empty repository
    this way: ([/help/init | more info]) </p>

    <blockquote>
    <b>fossil init </b><i> repository-filename</i>
    </blockquote>

    You can name the database anything you like, and you can place it anywhere in the filesystem.
    The <tt>.fossil</tt> extension is traditional but only required if you are going to use the 
    <tt>[./help?cmd=/server | fossil server DIRECTORY]</tt> feature.”

<h2 id="clone">Cloning An Existing Repository</h2>

    <p>Most fossil operations interact with a repository that is on the
    local disk drive, not on a remote system.  Hence, before accessing
    a remote repository it is necessary to make a local copy of that
    repository.  Making a local copy of a remote repository is called
    "cloning".</p>

    <p>Clone a remote repository as follows: ([/help/clone | more info])</p>

    <blockquote>
    <b>fossil clone</b> <i>URL  repository-filename</i>
    </blockquote>

    <p>The <i>URL</i> specifies the fossil repository
    you want to clone.  The <i>repository-filename</i> is the new local
    filename into which the cloned repository will be written.  For
    example, to clone the source code of Fossil itself:

    <blockquote>
    <b>fossil clone https://www.fossil-scm.org/ myclone.fossil</b>
    </blockquote>

    If your logged-in username is 'exampleuser', you should see output something like this:

    <blockquote>
    <b><tt>
            Round-trips: 8   Artifacts sent: 0  received: 39421<br>
            Clone done, sent: 2424  received: 42965725  ip: 10.10.10.0<br>
            Rebuilding repository meta-data...<br>
            100% complete...<br>
            Extra delta compression... <br>
            Vacuuming the database... <br>
            project-id: 94259BB9F186226D80E49D1FA2DB29F935CCA0333<br>
            server-id:  016595e9043054038a9ea9bc526d7f33f7ac0e42<br>
            admin-user: exampleuser (password is "yoWgDR42iv")><br>
    </tt></b>
    </blockquote>

    <p>If the remote repository requires a login, include a
    userid in the URL like this:

    <blockquote>
    <b>fossil clone https://</b><i>remoteuserid</i><b>@www.example.org/ myclone.fossil</b>
    </blockquote>

    <p>You will be prompted separately for the password.
     Use [https://en.wikipedia.org/wiki/Percent-encoding#Percent-encoding_reserved_characters|"%HH"] escapes for special characters in the userid.
     For example "/" would be replaced by "%2F" meaning that a userid of "Projects/Budget" would become "Projects%2FBudget") </p>

    <p>If you are behind a restrictive firewall, you might need
    to <a href="#proxy">specify an HTTP proxy</a>.</p>

    <p>A Fossil repository is a single disk file.  Instead of cloning,
    you can just make a copy of the repository file (for example, using
    "scp").  Note, however, that the repository file contains auxiliary
    information above and beyond the versioned files, including some
    sensitive information such as password hashes and email addresses.  If you
    want to share Fossil repositories directly by copying, consider running the
    [/help/scrub|fossil scrub] command to remove sensitive information
    before transmitting the file.

<h2 id="import">Importing From Another Version Control System</h2>

    <p>Rather than start a new project, or clone an existing Fossil project,
    you might prefer to
    <a href="./inout.wiki">import an existing Git project</a>
    into Fossil using the [/help/import | fossil import] command. 

    You can even decide to export your project back into git using the
    [/help/git | fossil git] command, which is how the Fossil project maintains
    [https://github.com/drhsqlite/fossil-mirror | its public GitHub mirror]. There
    is no limit to the number of times a tree can be imported and exported between
    Fossil and git.

    The [https://git-scm.com/docs/git-fast-export|Git fast-export format] has become
    a popular way to move files between version management systems, including from
    [https://www.mercurial-scm.org/|Mercurial].
    Fossil can also import [https://subversion.apache.org/|Subversion projects] directly.

<h2 id="checkout">Checking Out A Local Tree</h2>

    <p>To work on a project in fossil, you need to check out a local
    copy of the source tree.  Create the directory you want to be
    the root of your tree and cd into that directory.  Then
    do this: ([/help/open | more info])</p>

    <blockquote>
    <b>fossil open </b><i> repository-filename</i>
    </blockquote>

    for example:

    <blockquote>
    <b><tt>
        fossil open ../myclone.fossil<br>
        BUILD.txt<br>
        COPYRIGHT-BSD2.txt<br>
        README.md<br>
          ︙<br>
    </tt></b>
    </blockquote>

    (or "fossil open ..\myclone.fossil" on Windows).

    <p>This leaves you with the newest version of the tree
    checked out.
    From anywhere underneath the root of your local tree, you
    can type commands like the following to find out the status of
    your local tree:</p>

    <blockquote>
    <b>[/help/info | fossil info]</b><br>
    <b>[/help/status | fossil status]</b><br>
    <b>[/help/changes | fossil changes]</b><br>
    <b>[/help/diff | fossil diff]</b><br>
    <b>[/help/timeline | fossil timeline]</b><br>
    <b>[/help/ls | fossil ls]</b><br>
    <b>[/help/branch | fossil branch]</b><br>
    </blockquote>

   <p>If you created a new respository using "fossil init" some commands will not
    produce much output.</p>

    <p>Note that Fossil allows you to make multiple check-outs in
    separate directories from the same repository.  This enables you,
    for example, to do builds from multiple branches or versions at
    the same time without having to generate extra clones.</p>

    <p>To switch a checkout between different versions and branches,
    use:</p>

    <blockquote>
    <b>[/help/update | fossil update]</b><br>
    <b>[/help/checkout | fossil checkout]</b><br>
    </blockquote>

    <p>[/help/update | update] honors the "autosync" option and
    does a "soft" switch, merging any local changes into the target
    version, whereas [/help/checkout | checkout] does not
    automatically sync and does a "hard" switch, overwriting local
    changes if told to do so.</p>

<h2 id="changes">Making and Commiting Changes</h2>

    <p>To add new files to your project or remove existing ones, use these
    commands:</p>

    <blockquote>
    <b>[/help/add | fossil add]</b> <i>file...</i><br>
    <b>[/help/rm | fossil rm]</b> <i>file...</i><br>
    <b>[/help/addremove | fossil addremove]</b> <i>file...</i><br>
    </blockquote>

    <p>The command:</p>
    <blockquote>
    <b>
            [/help/changes | fossil changes]</b>
    </blockquote>
    <p>lists files that have changed since the last commit to the repository. For
    example, if you edit the file "README.md":</p>

    <blockquote>
    <b>
            fossil changes<br>
            EDITED     README.md
    </b>
    </blockquote>

    <p>To see exactly what change was made you can use the command</p>
    [/help/diff | fossil diff]:
    <blockquote>
    <b>
            fossil diff <br><tt>
            Index: README.md<br>
            ============================================================<br>
            --- README.md<br>
            +++ README.md<br>
            @@ -1,5 +1,6 @@<br>
            +Made some changes to the project<br>
            # Original text<br>
     </tt></b>
    </blockquote>
 
    <p>"fossil diff" is the difference between your tree on disk now and as the tree was
    when you did "fossil open". An open is the first checkout from a repository 
    into a new directory. </p>

    <p>To commit your changes to a local-only repository:</p>
    <blockquote>
    <b>
    fossil commit     </b><i>(... Fossil will start your editor, if defined)</i><b><br><tt>
    # Enter a commit message for this check-in. Lines beginning with # are ignored.<br>
    #<br>
    # user: exampleuser<br>
    # tags: trunk<br>
    #<br>
    # EDITED     README.md<br>
    Edited file to add description of code changes<br>
    New_Version: 7b9a416ced4a69a60589dde1aedd1a30fde8eec3528d265dbeed5135530440ab<br>
    </tt></b>
    </blockquote>

   <p>You will be prompted for check-in comments using whatever editor
    is specified by your VISUAL or EDITOR environment variable. If none is
    specified Fossil uses line-editing in the terminal.</p>

    <p>To commit your changes to a repository that was cloned from remote you 
    perform the same actions but the results are different. Fossil
    defaults to 'autosync' mode, a single-stage commit that sends all changes 
    committed to the local repository immediately on to the remote parent repository. This
    only works if you have write permission to the remote respository.</p>

<h2 id="naming">Naming of Files, Checkins, and Branches</h2>

    <p>Fossil deals with information artifacts. This Quickstart document only deals
    with files and collections of files, but be aware there are also tickets, wiki pages and more. 
    Every artifact in Fossil has a universally-unique hash id, and may also have a
    human-readable name.</p>

    <p>The following are all equivalent ways of identifying a Fossil file,
    checkin or branch artifact:</p>

    <ul>
    <li> the full unique SHA-256 hash, such as be836de35a821523beac2e53168e135d5ebd725d7af421e5f736a28e8034673a
    <li> an abbreviated hash prefix, such as the first ten characters: be836de35a . This won't be universally unique, but it is usually unique within any one repository. As an example, the [https://fossil-scm.org/home/hash-collisions|Fossil project hash collisions] showed at the time of writing that there are no artifacts with identical first 8 characters
    <li> a branch name, such as "special-features" or "juliet-testing". Each branch also has a unique SHA-256 hash
    </ul>

    <p>A special convenience branch is "trunk", which is Fossil's default branch name for
    the first checkin, and the default for any time a branch name is needed but not
    specified.</p>

    This will get you started on identifying checkins. The
    <a href="./checkin_names.wiki">Checkin Names document</a> is a complete reference, including
    how timestamps can also be used.

<h2 id="config">Configuring Your Local Repository</h2>

    <p>When you create a new repository, either by cloning an existing
    project or create a new project of your own, you usually want to do some
    local configuration.  This is easily accomplished using the web-server
    that is built into fossil.  Start the fossil web server like this:
    ([/help/ui | more info])</p>

    <blockquote>
    <b>fossil ui </b><i> repository-filename</i>
    </blockquote>

    <p>You can omit the <i>repository-filename</i> from the command above
    if you are inside a checked-out local tree.</p>

    <p>This starts a web server then automatically launches your
    web browser and makes it point to this web server.  If your system
    has an unusual configuration, fossil might not be able to figure out
    how to start your web browser.  In that case, first tell fossil
    where to find your web browser using a command like this:</p>

    <blockquote>
    <b>fossil setting web-browser </b><i>  path-to-web-browser</i>
    </blockquote>

    <p>By default, fossil does not require a login for HTTP connections
    coming in from the IP loopback address 127.0.0.1.  You can, and perhaps
    should, change this after you create a few users.</p>

    <p>When you are finished configuring, just press Control-C or use
    the <b>kill</b> command to shut down the mini-server.</p>

<h2 id="changes">Making Changes</h2>

    <p>To add new files to your project, or remove old files, use these
    commands:</p>

    <blockquote>
    <b>[/help/add | fossil add]</b> <i>file...</i><br>
    <b>[/help/rm | fossil rm]</b> <i>file...</i><br>
    <b>[/help/addremove | fossil addremove]</b> <i>file...</i><br>
    </blockquote>

    <p>You can also edit files freely.  Once you are ready to commit
    your changes, type:</p>

    <blockquote>
    <b>[/help/commit | fossil commit]</b>
    </blockquote>

    <p>You will be prompted for check-in comments using whatever editor
    is specified by your VISUAL or EDITOR environment variable.</p>

    In the default configuration, the [/help/commit|commit]
    command will also automatically [/help/push|push] your changes, but that
    feature can be disabled.  (More information about
    [./concepts.wiki#workflow|autosync] and how to disable it.)
    Remember that your coworkers can not see your changes until you
    commit and push them.</p>

<h2 id="sharing">Sharing Changes</h2>

    <p>When [./concepts.wiki#workflow|autosync] is turned off,
    the changes you [/help/commit | commit] are only
    on your local repository.
    To share those changes with other repositories, do:</p>

    <blockquote>
    <b>[/help/push | fossil push]</b> <i>URL</i>
    </blockquote>

    <p>Where <i>URL</i> is the http: URL of the server repository you
    want to share your changes with.  If you omit the <i>URL</i> argument,
    fossil will use whatever server you most recently synced with.</p>

    <p>The [/help/push | push] command only sends your changes to others.  To
    Receive changes from others, use [/help/pull | pull].  Or go both ways at
    once using [/help/sync | sync]:</p>

    <blockquote>
    <b>[/help/pull | fossil pull]</b> <i>URL</i><br>
    <b>[/help/sync | fossil sync]</b> <i>URL</i>
    </blockquote>

    <p>When you pull in changes from others, they go into your repository,
    not into your checked-out local tree.  To get the changes into your
    local tree, use [/help/update | update]:</p>

    <blockquote>
    <b>[/help/update | fossil update]</b> <i>VERSION</i>
    </blockquote>

    <p>The <i>VERSION</i> can be the name of a branch or tag or any
    abbreviation to the 40-character
    artifact identifier for a particular check-in, or it can be a
    date/time stamp.  ([./checkin_names.wiki | more info])
    If you omit
    the <i>VERSION</i>, then fossil moves you to the
    latest version of the branch your are currently on.</p>

    <p>The default behavior is for [./concepts.wiki#workflow|autosync] to
    be turned on.  That means that a [/help/pull|pull] automatically occurs
    when you run [/help/update|update] and a [/help/push|push] happens
    automatically after you [/help/commit|commit].  So in normal practice,
    the push, pull, and sync commands are rarely used.  But it is important
    to know about them, all the same.</p>

    <blockquote>
    <b>[/help/checkout | fossil checkout]</b> <i>VERSION</i>
    </blockquote>

    <p>Is similar to update except that it does not honor the autosync
    setting, nor does it merge in local changes - it prefers to overwrite
    them and fails if local changes exist unless the <tt>--force</tt>
    flag is used.</p>

<h2 id="branch" name="merge">Branching And Merging</h2>

    <p>Use the --branch option to the [/help/commit | commit] command
    to start a new branch.  Note that in Fossil, branches are normally
    created when you commit, not before you start editing.  You can
    use the [/help/branch | branch new] command to create a new branch
    before you start editing, if you want, but most people just wait
    until they are ready to commit.

    To merge two branches back together, first
    [/help/update | update] to the branch you want to merge into.
    Then do a [/help/merge|merge] of the other branch that you want to incorporate
    the changes from.  For example, to merge "featureX" changes into "trunk"
    do this:</p>

    <blockquote>
    <b>fossil [/help/update|update] trunk</b><br>
    <b>fossil [/help/merge|merge] featureX</b><br>
    <i># make sure the merge didn't break anything...</i><br>
    <b>fossil [/help/commit|commit]
    </blockquote>

    <p>The argument to the [/help/merge|merge] command can be any of the
    version identifier forms that work for [/help/update|update].
    ([./checkin_names.wiki|more info].)
    The merge command has options to cherry-pick individual
    changes, or to back out individual changes, if you don't want to
    do a full merge.</p>

    The merge command puts all changes in your working check-out.
    No changes are made to the repository.
    You must run [/help/commit|commit] separately
    to add the merge changes into your repository to make them persistent
    and so that your coworkers can see them.
    But before you do that, you will normally want to run a few tests
    to verify that the merge didn't cause logic breaks in your code.

    The same branch can be merged multiple times without trouble. Fossil
    automatically keeps up with things and avoids conflicts when doing
    multiple merges.  So even if you have merged the featureX branch
    into trunk previously, you can do so again and Fossil will automatically
    know to pull in only those changes that have occurred since the previous
    merge.

    <p>If a merge or update doesn't work out (perhaps something breaks or
    there are many merge conflicts) then you back up using:</p>

    <blockquote>
    <b>[/help/undo | fossil undo]</b>
    </blockquote>

    <p>This will back out the changes that the merge or update made to the
    working checkout.  There is also a [/help/redo|redo] command if you undo by
    mistake.  Undo and redo only work for changes that have
    not yet been checked in using commit and there is only a single
    level of undo/redo.</p>


<h2 id="server">Setting Up A Server</h2>

    <p>Fossil can act as a stand-alone web server using one of these
    commands:</p>

    <blockquote>
    <b>[/help/server | fossil server]</b> <i>repository-filename</i><br>
    <b>[/help/ui | fossil ui]</b> <i>repository-filename</i>
    </blockquote>

    <p>The <i>repository-filename</i> can be omitted when these commands
    are run from within an open check-out, which a particularly useful
    shortcut for the <b>fossil ui</b> command.

    <p>The <b>ui</b> command is intended for accessing the web interface
    from a local desktop.  The <b>ui</b> command binds to the loopback IP
    address only (and thus makes the web interface visible only on the
    local machine) and it automatically start your web browser pointing at the
    server.  For cross-machine collaboration, use the <b>server</b> command,
    which binds on all IP addresses and does not try to start a web browser.</p>

    <p>Servers are also easily configured as:
    <ul>
    <li>[./server/any/inetd.md|inetd]
    <li>[./server/debian/service.md|systemd]
    <li>[./server/any/cgi.md|CGI]
    <li>[./server/any/scgi.md|SCGI]
    </ul>

    <p>The [./selfhost.wiki | self-hosting fossil repositories] use
    CGI.

<h2 id="proxy">HTTP Proxies</h2>

    <p>If you are behind a restrictive firewall that requires you to use
    an HTTP proxy to reach the internet, then you can configure the proxy
    in three different ways.  You can tell fossil about your proxy using
    a command-line option on commands that use the network,
    <b>sync</b>, <b>clone</b>, <b>push</b>, and <b>pull</b>.</p>

    <blockquote>
    <b>fossil clone </b><i>URL</i>  <b>--proxy</b> <i>Proxy-URL</i>
    </blockquote>

    <p>It is annoying to have to type in the proxy URL every time you
    sync your project, though, so you can make the proxy configuration
    persistent using the [/help/setting | setting] command:</p>

    <blockquote>
    <b>fossil setting proxy </b><i>Proxy-URL</i>
    </blockquote>

    <p>Or, you can set the "<b>http_proxy</b>" environment variable:</p>

    <blockquote>
    <b>export http_proxy=</b><i>Proxy-URL</i>
    </blockquote>

    <p>To stop using the proxy, do:</p>

    <blockquote>
    <b>fossil setting proxy off</b>
    </blockquote>

    <p>Or unset the environment variable.  The fossil setting for the
    HTTP proxy takes precedence over the environment variable and the
    command-line option overrides both.  If you have an persistent
    proxy setting that you want to override for a one-time sync, that
    is easily done on the command-line.  For example, to sync with
    a co-workers repository on your LAN, you might type:</p>

    <blockquote>
    <b>fossil sync http://192.168.1.36:8080/ --proxy off</b>
    </blockquote>

<h2 id="links">Other Resources</h2>

   <ul>
   <li> <a href="./gitusers.md">Hints For Users With Prior Git Experience</a>
   <li> <a href="./whyusefossil.wiki">Why You Should Use Fossil</a>
   <li> <a href="./history.md">The History and Purpose of Fossil</a>
   <li> <a href="./branching.wiki">Branching, Forking, and Tagging</a>
   <li> <a href="./hints.wiki">Fossil Tips and Usage Hints</a>
   <li> <a href="./permutedindex.html">Comprehensive Fossil Doc Index</a>
   </ul>








|
|
|
|
|
|
|
|

|
|
|
|
|
|



|
|
|
|
|

|
|
|
|
|
|
|

|
|

|
|



|
|

|
|
|

|
|
|



|
|
|
|
|

|

|
|
|

|
|
|
|

|
|
|

|

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

|
|

|
|
|

|
|
|

|
|

|
|
|
|
|
|
|
|



|
|
|
|

|
|
|
|
|

|
|
|
|



|
|
|
|

|
|
|

|

|
|
|
|
|
|
|
|
|

|

|
|
|
|
|

|
|
|
|
|
|
|
|
|

|
|

|
|
|
|

|
|

|
|
|
|

|
|
|
|
|

|

|
|

|
|
|
|
|

|
|
|
|
|
|
|

|
|
|
|
|
|

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

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

|
|
|

|
|
|
|
|



|
|
|
|

|
|

|
|
|
|
|

|
|
|

|
|
|



|
|
|
|
|

|
|
|

|
|

|
|
|
|
|

|
|
|

|
|
|

|
|



|
|

|
|
|
|
|

|
|

|
|
|

|
|

|
|
|
|
|
|



|
|
|
|

|
|
|

|
|
|

|
|
|

|
|
|
|

|
|
|

|
|
|

|
|
|
|
|
|
|

|
|
|
|
|
|

|
|
|

|
|
|
|



|
|
|
|
|
|

|
|
|
|
|

|
|
|
|
|
|

|
|
|
|
|
|

|
|
|
|
|
|
|

|
|
|
|
|
|

|
|

|
|
|

|
|
|
|
|




|
|

|
|
|
|

|
|
|

|
|
|
|
|
|

|
|
|
|
|
|
|

|
|



|
|
|
|
|

|
|
|

|
|
|

|
|
|

|

|
|
|

|

|
|
|

|
|
|
|
|
|

|
|
|



|
|
|
|
|
|
|
|
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
<title>Fossil Quick Start Guide</title>
<h1 align="center">Fossil Quick Start</h1>

<p>This is a guide to help you get started using the Fossil [https://en.wikipedia.org/wiki/Distributed_version_control|Distributed Version Control System] quickly
and painlessly.</p>

<h2 id="install">Installing</h2>

<p>Fossil is a single self-contained C program.  You need to
either download a
[https://www.fossil-scm.org/fossil/uv/download.html|precompiled
binary]
or <a href="build.wiki">compile it yourself</a> from sources.
Install Fossil by putting the fossil binary
someplace on your $PATH.</p>
You can test that Fossil is present and working like this:

<blockquote>
<b>
fossil version<br>
<tt>This is fossil version 2.13 [309af345ab] 2020-09-28 04:02:55 UTC</tt><br>
</b>
</blockquote>

<h2 id="workflow" name="fslclone">General Work Flow</h2>

<p>Fossil works with repository files (a database in a single file with the project's
complete history) and with checked-out local trees (the working directory
you use to do your work). 
(See [./whyusefossil.wiki#definitions | definitions] for more background.)
The workflow looks like this:</p>

<ul>
    <li>Create or clone a repository file.  ([/help/init|fossil init] or
        [/help/clone | fossil clone])
    <li>Check out a local tree.  ([/help/open | fossil open])
    <li>Perform operations on the repository (including repository
        configuration).
</ul>

Fossil can be entirely driven from the command line. Many features
can also be conveniently accessed from the build-in web interface.

<p>The following sections give a brief overview of these
operations.</p>

<h2 id="new">Starting A New Project</h2>

<p>To start a new project with fossil create a new empty repository
this way: ([/help/init | more info]) </p>

<blockquote>
<b>fossil init </b><i> repository-filename</i>
</blockquote>

You can name the database anything you like, and you can place it anywhere in the filesystem.
The <tt>.fossil</tt> extension is traditional but only required if you are going to use the 
<tt>[./help?cmd=/server | fossil server DIRECTORY]</tt> feature.”

<h2 id="clone">Cloning An Existing Repository</h2>

<p>Most fossil operations interact with a repository that is on the
local disk drive, not on a remote system.  Hence, before accessing
a remote repository it is necessary to make a local copy of that
repository.  Making a local copy of a remote repository is called
"cloning".</p>

<p>Clone a remote repository as follows: ([/help/clone | more info])</p>

<blockquote>
<b>fossil clone</b> <i>URL  repository-filename</i>
</blockquote>

<p>The <i>URL</i> specifies the fossil repository
you want to clone.  The <i>repository-filename</i> is the new local
filename into which the cloned repository will be written.  For
example, to clone the source code of Fossil itself:

<blockquote>
<b>fossil clone https://www.fossil-scm.org/ myclone.fossil</b>
</blockquote>

If your logged-in username is 'exampleuser', you should see output something like this:

<blockquote>
<b><tt>
        Round-trips: 8   Artifacts sent: 0  received: 39421<br>
        Clone done, sent: 2424  received: 42965725  ip: 10.10.10.0<br>
        Rebuilding repository meta-data...<br>
        100% complete...<br>
        Extra delta compression... <br>
        Vacuuming the database... <br>
        project-id: 94259BB9F186226D80E49D1FA2DB29F935CCA0333<br>
        server-id:  016595e9043054038a9ea9bc526d7f33f7ac0e42<br>
        admin-user: exampleuser (password is "yoWgDR42iv")><br>
</tt></b>
</blockquote>

<p>If the remote repository requires a login, include a
userid in the URL like this:

<blockquote>
<b>fossil clone https://</b><i>remoteuserid</i><b>@www.example.org/ myclone.fossil</b>
</blockquote>

<p>You will be prompted separately for the password.
 Use [https://en.wikipedia.org/wiki/Percent-encoding#Percent-encoding_reserved_characters|"%HH"] escapes for special characters in the userid.
 For example "/" would be replaced by "%2F" meaning that a userid of "Projects/Budget" would become "Projects%2FBudget") </p>

<p>If you are behind a restrictive firewall, you might need
to <a href="#proxy">specify an HTTP proxy</a>.</p>

<p>A Fossil repository is a single disk file.  Instead of cloning,
you can just make a copy of the repository file (for example, using
"scp").  Note, however, that the repository file contains auxiliary
information above and beyond the versioned files, including some
sensitive information such as password hashes and email addresses.  If you
want to share Fossil repositories directly by copying, consider running the
[/help/scrub|fossil scrub] command to remove sensitive information
before transmitting the file.

<h2 id="import">Importing From Another Version Control System</h2>

<p>Rather than start a new project, or clone an existing Fossil project,
you might prefer to
<a href="./inout.wiki">import an existing Git project</a>
into Fossil using the [/help/import | fossil import] command. 

You can even decide to export your project back into git using the
[/help/git | fossil git] command, which is how the Fossil project maintains
[https://github.com/drhsqlite/fossil-mirror | its public GitHub mirror]. There
is no limit to the number of times a tree can be imported and exported between
Fossil and git.

The [https://git-scm.com/docs/git-fast-export|Git fast-export format] has become
a popular way to move files between version management systems, including from
[https://www.mercurial-scm.org/|Mercurial].
Fossil can also import [https://subversion.apache.org/|Subversion projects] directly.

<h2 id="checkout">Checking Out A Local Tree</h2>

<p>To work on a project in fossil, you need to check out a local
copy of the source tree.  Create the directory you want to be
the root of your tree and cd into that directory.  Then
do this: ([/help/open | more info])</p>

<blockquote>
<b>fossil open </b><i> repository-filename</i>
</blockquote>

for example:

<blockquote>
<b><tt>
    fossil open ../myclone.fossil<br>
    BUILD.txt<br>
    COPYRIGHT-BSD2.txt<br>
    README.md<br>
      ︙<br>
</tt></b>
</blockquote>

(or "fossil open ..\myclone.fossil" on Windows).

<p>This leaves you with the newest version of the tree
checked out.
From anywhere underneath the root of your local tree, you
can type commands like the following to find out the status of
your local tree:</p>

<blockquote>
<b>[/help/info | fossil info]</b><br>
<b>[/help/status | fossil status]</b><br>
<b>[/help/changes | fossil changes]</b><br>
<b>[/help/diff | fossil diff]</b><br>
<b>[/help/timeline | fossil timeline]</b><br>
<b>[/help/ls | fossil ls]</b><br>
<b>[/help/branch | fossil branch]</b><br>
</blockquote>

<p>If you created a new repository using "fossil init" some commands will not
produce much output.</p>

<p>Note that Fossil allows you to make multiple check-outs in
separate directories from the same repository.  This enables you,
for example, to do builds from multiple branches or versions at
the same time without having to generate extra clones.</p>

<p>To switch a checkout between different versions and branches,
use:</p>

<blockquote>
<b>[/help/update | fossil update]</b><br>
<b>[/help/checkout | fossil checkout]</b><br>
</blockquote>

<p>[/help/update | update] honors the "autosync" option and
does a "soft" switch, merging any local changes into the target
version, whereas [/help/checkout | checkout] does not
automatically sync and does a "hard" switch, overwriting local
changes if told to do so.</p>

<h2 id="changes">Making and Committing Changes</h2>

<p>To add new files to your project or remove existing ones, use these
commands:</p>

<blockquote>
<b>[/help/add | fossil add]</b> <i>file...</i><br>
<b>[/help/rm | fossil rm]</b> <i>file...</i><br>
<b>[/help/addremove | fossil addremove]</b> <i>file...</i><br>
</blockquote>

<p>The command:</p>
<blockquote>
<b>
        [/help/changes | fossil changes]</b>
</blockquote>
<p>lists files that have changed since the last commit to the repository. For
example, if you edit the file "README.md":</p>

<blockquote>
<b>
        fossil changes<br>
        EDITED     README.md
</b>
</blockquote>

<p>To see exactly what change was made you can use the command</p>
[/help/diff | fossil diff]:
<blockquote>
<b>
        fossil diff <br><tt>
        Index: README.md<br>
        ============================================================<br>
        --- README.md<br>
        +++ README.md<br>
        @@ -1,5 +1,6 @@<br>
        +Made some changes to the project<br>
        # Original text<br>
 </tt></b>
</blockquote>

<p>"fossil diff" is the difference between your tree on disk now and as the tree was
when you did "fossil open". An open is the first checkout from a repository 
into a new directory. </p>

<p>To commit your changes to a local-only repository:</p>
<blockquote>
<b>
fossil commit     </b><i>(... Fossil will start your editor, if defined)</i><b><br><tt>
# Enter a commit message for this check-in. Lines beginning with # are ignored.<br>
#<br>
# user: exampleuser<br>
# tags: trunk<br>
#<br>
# EDITED     README.md<br>
Edited file to add description of code changes<br>
New_Version: 7b9a416ced4a69a60589dde1aedd1a30fde8eec3528d265dbeed5135530440ab<br>
</tt></b>
</blockquote>

<p>You will be prompted for check-in comments using whatever editor
is specified by your VISUAL or EDITOR environment variable. If none is
specified Fossil uses line-editing in the terminal.</p>

<p>To commit your changes to a repository that was cloned from remote you 
perform the same actions but the results are different. Fossil
defaults to 'autosync' mode, a single-stage commit that sends all changes 
committed to the local repository immediately on to the remote parent repository. This
only works if you have write permission to the remote respository.</p>

<h2 id="naming">Naming of Files, Checkins, and Branches</h2>

<p>Fossil deals with information artifacts. This Quickstart document only deals
with files and collections of files, but be aware there are also tickets, wiki pages and more. 
Every artifact in Fossil has a universally-unique hash id, and may also have a
human-readable name.</p>

<p>The following are all equivalent ways of identifying a Fossil file,
checkin or branch artifact:</p>

<ul>
<li> the full unique SHA-256 hash, such as be836de35a821523beac2e53168e135d5ebd725d7af421e5f736a28e8034673a
<li> an abbreviated hash prefix, such as the first ten characters: be836de35a . This won't be universally unique, but it is usually unique within any one repository. As an example, the [https://fossil-scm.org/home/hash-collisions|Fossil project hash collisions] showed at the time of writing that there are no artifacts with identical first 8 characters
<li> a branch name, such as "special-features" or "juliet-testing". Each branch also has a unique SHA-256 hash
</ul>

<p>A special convenience branch is "trunk", which is Fossil's default branch name for
the first checkin, and the default for any time a branch name is needed but not
specified.</p>

This will get you started on identifying checkins. The
<a href="./checkin_names.wiki">Checkin Names document</a> is a complete reference, including
how timestamps can also be used.

<h2 id="config">Configuring Your Local Repository</h2>

<p>When you create a new repository, either by cloning an existing
project or create a new project of your own, you usually want to do some
local configuration.  This is easily accomplished using the web-server
that is built into fossil.  Start the fossil web server like this:
([/help/ui | more info])</p>

<blockquote>
<b>fossil ui </b><i> repository-filename</i>
</blockquote>

<p>You can omit the <i>repository-filename</i> from the command above
if you are inside a checked-out local tree.</p>

<p>This starts a web server then automatically launches your
web browser and makes it point to this web server.  If your system
has an unusual configuration, fossil might not be able to figure out
how to start your web browser.  In that case, first tell fossil
where to find your web browser using a command like this:</p>

<blockquote>
<b>fossil setting web-browser </b><i>  path-to-web-browser</i>
</blockquote>

<p>By default, fossil does not require a login for HTTP connections
coming in from the IP loopback address 127.0.0.1.  You can, and perhaps
should, change this after you create a few users.</p>

<p>When you are finished configuring, just press Control-C or use
the <b>kill</b> command to shut down the mini-server.</p>

<h2 id="changes">Making Changes</h2>

<p>To add new files to your project, or remove old files, use these
commands:</p>

<blockquote>
<b>[/help/add | fossil add]</b> <i>file...</i><br>
<b>[/help/rm | fossil rm]</b> <i>file...</i><br>
<b>[/help/addremove | fossil addremove]</b> <i>file...</i><br>
</blockquote>

<p>You can also edit files freely.  Once you are ready to commit
your changes, type:</p>

<blockquote>
<b>[/help/commit | fossil commit]</b>
</blockquote>

<p>You will be prompted for check-in comments using whatever editor
is specified by your VISUAL or EDITOR environment variable.</p>

In the default configuration, the [/help/commit|commit]
command will also automatically [/help/push|push] your changes, but that
feature can be disabled.  (More information about
[./concepts.wiki#workflow|autosync] and how to disable it.)
Remember that your coworkers can not see your changes until you
commit and push them.</p>

<h2 id="sharing">Sharing Changes</h2>

<p>When [./concepts.wiki#workflow|autosync] is turned off,
the changes you [/help/commit | commit] are only
on your local repository.
To share those changes with other repositories, do:</p>

<blockquote>
<b>[/help/push | fossil push]</b> <i>URL</i>
</blockquote>

<p>Where <i>URL</i> is the http: URL of the server repository you
want to share your changes with.  If you omit the <i>URL</i> argument,
fossil will use whatever server you most recently synced with.</p>

<p>The [/help/push | push] command only sends your changes to others.  To
Receive changes from others, use [/help/pull | pull].  Or go both ways at
once using [/help/sync | sync]:</p>

<blockquote>
<b>[/help/pull | fossil pull]</b> <i>URL</i><br>
<b>[/help/sync | fossil sync]</b> <i>URL</i>
</blockquote>

<p>When you pull in changes from others, they go into your repository,
not into your checked-out local tree.  To get the changes into your
local tree, use [/help/update | update]:</p>

<blockquote>
<b>[/help/update | fossil update]</b> <i>VERSION</i>
</blockquote>

<p>The <i>VERSION</i> can be the name of a branch or tag or any
abbreviation to the 40-character
artifact identifier for a particular check-in, or it can be a
date/time stamp.  ([./checkin_names.wiki | more info])
If you omit
the <i>VERSION</i>, then fossil moves you to the
latest version of the branch your are currently on.</p>

<p>The default behavior is for [./concepts.wiki#workflow|autosync] to
be turned on.  That means that a [/help/pull|pull] automatically occurs
when you run [/help/update|update] and a [/help/push|push] happens
automatically after you [/help/commit|commit].  So in normal practice,
the push, pull, and sync commands are rarely used.  But it is important
to know about them, all the same.</p>

<blockquote>
<b>[/help/checkout | fossil checkout]</b> <i>VERSION</i>
</blockquote>

<p>Is similar to update except that it does not honor the autosync
setting, nor does it merge in local changes - it prefers to overwrite
them and fails if local changes exist unless the <tt>--force</tt>
flag is used.</p>

<h2 id="branch" name="merge">Branching And Merging</h2>

<p>Use the --branch option to the [/help/commit | commit] command
to start a new branch.  Note that in Fossil, branches are normally
created when you commit, not before you start editing.  You can
use the [/help/branch | branch new] command to create a new branch
before you start editing, if you want, but most people just wait
until they are ready to commit.

To merge two branches back together, first
[/help/update | update] to the branch you want to merge into.
Then do a [/help/merge|merge] of the other branch that you want to incorporate
the changes from.  For example, to merge "featureX" changes into "trunk"
do this:</p>

<blockquote>
<b>fossil [/help/update|update] trunk</b><br>
<b>fossil [/help/merge|merge] featureX</b><br>
<i># make sure the merge didn't break anything...</i><br>
<b>fossil [/help/commit|commit]
</blockquote>

<p>The argument to the [/help/merge|merge] command can be any of the
version identifier forms that work for [/help/update|update].
([./checkin_names.wiki|more info].)
The merge command has options to cherry-pick individual
changes, or to back out individual changes, if you don't want to
do a full merge.</p>

The merge command puts all changes in your working check-out.
No changes are made to the repository.
You must run [/help/commit|commit] separately
to add the merge changes into your repository to make them persistent
and so that your coworkers can see them.
But before you do that, you will normally want to run a few tests
to verify that the merge didn't cause logic breaks in your code.

The same branch can be merged multiple times without trouble. Fossil
automatically keeps up with things and avoids conflicts when doing
multiple merges.  So even if you have merged the featureX branch
into trunk previously, you can do so again and Fossil will automatically
know to pull in only those changes that have occurred since the previous
merge.

<p>If a merge or update doesn't work out (perhaps something breaks or
there are many merge conflicts) then you back up using:</p>

<blockquote>
<b>[/help/undo | fossil undo]</b>
</blockquote>

<p>This will back out the changes that the merge or update made to the
working checkout.  There is also a [/help/redo|redo] command if you undo by
mistake.  Undo and redo only work for changes that have
not yet been checked in using commit and there is only a single
level of undo/redo.</p>


<h2 id="server">Setting Up A Server</h2>

<p>Fossil can act as a stand-alone web server using one of these
commands:</p>

<blockquote>
<b>[/help/server | fossil server]</b> <i>repository-filename</i><br>
<b>[/help/ui | fossil ui]</b> <i>repository-filename</i>
</blockquote>

<p>The <i>repository-filename</i> can be omitted when these commands
are run from within an open check-out, which a particularly useful
shortcut for the <b>fossil ui</b> command.

<p>The <b>ui</b> command is intended for accessing the web interface
from a local desktop.  The <b>ui</b> command binds to the loopback IP
address only (and thus makes the web interface visible only on the
local machine) and it automatically start your web browser pointing at the
server.  For cross-machine collaboration, use the <b>server</b> command,
which binds on all IP addresses and does not try to start a web browser.</p>

<p>Servers are also easily configured as:
<ul>
<li>[./server/any/inetd.md|inetd]
<li>[./server/debian/service.md|systemd]
<li>[./server/any/cgi.md|CGI]
<li>[./server/any/scgi.md|SCGI]
</ul>

<p>The [./selfhost.wiki | self-hosting fossil repositories] use
CGI.

<h2 id="proxy">HTTP Proxies</h2>

<p>If you are behind a restrictive firewall that requires you to use
an HTTP proxy to reach the internet, then you can configure the proxy
in three different ways.  You can tell fossil about your proxy using
a command-line option on commands that use the network,
<b>sync</b>, <b>clone</b>, <b>push</b>, and <b>pull</b>.</p>

<blockquote>
<b>fossil clone </b><i>URL</i>  <b>--proxy</b> <i>Proxy-URL</i>
</blockquote>

<p>It is annoying to have to type in the proxy URL every time you
sync your project, though, so you can make the proxy configuration
persistent using the [/help/setting | setting] command:</p>

<blockquote>
<b>fossil setting proxy </b><i>Proxy-URL</i>
</blockquote>

<p>Or, you can set the "<b>http_proxy</b>" environment variable:</p>

<blockquote>
<b>export http_proxy=</b><i>Proxy-URL</i>
</blockquote>

<p>To stop using the proxy, do:</p>

<blockquote>
<b>fossil setting proxy off</b>
</blockquote>

<p>Or unset the environment variable.  The fossil setting for the
HTTP proxy takes precedence over the environment variable and the
command-line option overrides both.  If you have a persistent
proxy setting that you want to override for a one-time sync, that
is easily done on the command-line.  For example, to sync with
a co-worker's repository on your LAN, you might type:</p>

<blockquote>
<b>fossil sync http://192.168.1.36:8080/ --proxy off</b>
</blockquote>

<h2 id="links">Other Resources</h2>

<ul>
<li> <a href="./gitusers.md">Hints For Users With Prior Git Experience</a>
<li> <a href="./whyusefossil.wiki">Why You Should Use Fossil</a>
<li> <a href="./history.md">The History and Purpose of Fossil</a>
<li> <a href="./branching.wiki">Branching, Forking, and Tagging</a>
<li> <a href="./hints.wiki">Fossil Tips and Usage Hints</a>
<li> <a href="./permutedindex.html">Comprehensive Fossil Doc Index</a>
</ul>

Changes to www/rebaseharm.md.

329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
doesn’t make the user tell counter-factual “stories,” it only allows the
user to provide annotations to provide a more readable edited
presentation for routine display purposes.

Git needs rebase because it lacks these annotation facilities.  Rather
than consider rebase a desirable feature missing in Fossil, ask instead
why Git lacks support for making editorial changes to check-ins without
modifyihng history?  Wouldn't it be better to fix the version control
tool rather than requiring users to fabricate a fictitious project
history?

## <a name="collapsing"></a>6.0 Collapsing check-ins throws away valuable information

One of the oft-cited advantages of rebasing in Git is that it lets you
collapse multiple check-ins down to a single check-in to make the







|







329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
doesn’t make the user tell counter-factual “stories,” it only allows the
user to provide annotations to provide a more readable edited
presentation for routine display purposes.

Git needs rebase because it lacks these annotation facilities.  Rather
than consider rebase a desirable feature missing in Fossil, ask instead
why Git lacks support for making editorial changes to check-ins without
modifying history?  Wouldn't it be better to fix the version control
tool rather than requiring users to fabricate a fictitious project
history?

## <a name="collapsing"></a>6.0 Collapsing check-ins throws away valuable information

One of the oft-cited advantages of rebasing in Git is that it lets you
collapse multiple check-ins down to a single check-in to make the

Changes to www/server/any/althttpd.md.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Serving via althttpd

[Althttpd][althttpd]
is a light-weight web server that has been used to implement the SQLite and
Fossil websites for well over a decade. Althttpd strives for simplicity,
security, ease of configuration, and low resource usage.

To set up a Fossil server as CGI on a host running the althttpd web
server, follow these steps.
<ol>
<li<p>Get the althttpd webserver running on the host.  This is easily
done by following the [althttpd documentation][althttpd].

<li><p>Create a CGI script for your Fossil respository.  The script will
be typically be two lines of code that look something like this:

~~~
    #!/usr/bin/fossil
    repository: /home/yourlogin/fossils/project.fossil
~~~














|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# Serving via althttpd

[Althttpd][althttpd]
is a light-weight web server that has been used to implement the SQLite and
Fossil websites for well over a decade. Althttpd strives for simplicity,
security, ease of configuration, and low resource usage.

To set up a Fossil server as CGI on a host running the althttpd web
server, follow these steps.
<ol>
<li<p>Get the althttpd webserver running on the host.  This is easily
done by following the [althttpd documentation][althttpd].

<li><p>Create a CGI script for your Fossil repository.  The script will
be typically be two lines of code that look something like this:

~~~
    #!/usr/bin/fossil
    repository: /home/yourlogin/fossils/project.fossil
~~~

Changes to www/server/debian/service.md.

101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
1.  Install the unit file to one of the persistent system-level unit
    file directories. Typically, these are:

        /etc/systemd/system
        /lib/systemd/system

2.  Add `User` and `Group` directives to the `[Service]` section so
    Fossil runs as a normal user, preferrably one with access only to
    the Fossil repo files, rather than running as `root`.


## Socket Activation

Another useful method to serve a Fossil repo via `systemd` is via a
socket listener, which `systemd` calls “[socket activation][sa].”







|







101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
1.  Install the unit file to one of the persistent system-level unit
    file directories. Typically, these are:

        /etc/systemd/system
        /lib/systemd/system

2.  Add `User` and `Group` directives to the `[Service]` section so
    Fossil runs as a normal user, preferably one with access only to
    the Fossil repo files, rather than running as `root`.


## Socket Activation

Another useful method to serve a Fossil repo via `systemd` is via a
socket listener, which `systemd` calls “[socket activation][sa].”

Changes to www/server/openbsd/fastcgi.md.

66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
## <a name="chroot"></a>Setup chroot

Fossil needs both `/dev/random` and `/dev/null`, which aren't accessible
from within the chroot, so need to be constructed; `/var`, however, is
mounted with the `nodev` option. Rather than removing this default
setting, create a small memory filesystem and then mount it on to
`/var/www/dev` with [`mount_mfs(8)`][mfs] so that the `random` and
`null` device files can be created. In order to avoid neccessitating a
startup script to recreate the device files at boot, create a template
of the needed ``/dev`` tree to automatically populate the memory
filesystem.

```console
    $ doas mkdir /var/www/dev
    $ doas install -d -g daemon /template/dev







|







66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
## <a name="chroot"></a>Setup chroot

Fossil needs both `/dev/random` and `/dev/null`, which aren't accessible
from within the chroot, so need to be constructed; `/var`, however, is
mounted with the `nodev` option. Rather than removing this default
setting, create a small memory filesystem and then mount it on to
`/var/www/dev` with [`mount_mfs(8)`][mfs] so that the `random` and
`null` device files can be created. In order to avoid necessitating a
startup script to recreate the device files at boot, create a template
of the needed ``/dev`` tree to automatically populate the memory
filesystem.

```console
    $ doas mkdir /var/www/dev
    $ doas install -d -g daemon /template/dev

Changes to www/server/whyuseaserver.wiki.

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

Fossil does not require a server, but a server can be very useful.
Here are a few reasons to set up a Fossil server for your project:

  1.  <b>A server works as a complete project website.</b><p>
      Fossil does more than just version control.  It also supports
      [../tickets.wiki|trouble-tickets], 
      [../wikitheory.wiki|wiki], and a [../forum.wiki|forum].

      The [../embeddeddoc.wiki|embedded documentation]
      feature provides  a great mechanism for providing project documentation.
      The [../unvers.wiki|unversioned files] feature is a convenient way
      to host builds and downloads on the project website.

  2.  <b>A server gives developers a common point of rendezvous for
      syncing their work.</b><p>
      It is possible for developers to synchronize peer-to-peer but
      that requires the developers coordinate the sync, which in turn
      requires that the developers both want to sync at the same moment.
      A server aleviates this time dependency by allowing each developer
      to sync whenever it is convenient (for example, automatically syncing
      after each commit and before each update).  Developers all stay
      in sync with each other, without having to interrupt each other
      constantly to set up a peer-to-peer sync.

  3.  <b>A server provides project leaders with up-to-date status.</b><p>
      Project coordinators and BDFLs can click on a link or two at the







|
>










|







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

Fossil does not require a server, but a server can be very useful.
Here are a few reasons to set up a Fossil server for your project:

  1.  <b>A server works as a complete project website.</b><p>
      Fossil does more than just version control.  It also supports
      [../tickets.wiki|trouble-tickets], 
      [../wikitheory.wiki|wiki], 
      a [../chat.md|developer chat room], and a [../forum.wiki|forum].
      The [../embeddeddoc.wiki|embedded documentation]
      feature provides  a great mechanism for providing project documentation.
      The [../unvers.wiki|unversioned files] feature is a convenient way
      to host builds and downloads on the project website.

  2.  <b>A server gives developers a common point of rendezvous for
      syncing their work.</b><p>
      It is possible for developers to synchronize peer-to-peer but
      that requires the developers coordinate the sync, which in turn
      requires that the developers both want to sync at the same moment.
      A server alleviates this time dependency by allowing each developer
      to sync whenever it is convenient (for example, automatically syncing
      after each commit and before each update).  Developers all stay
      in sync with each other, without having to interrupt each other
      constantly to set up a peer-to-peer sync.

  3.  <b>A server provides project leaders with up-to-date status.</b><p>
      Project coordinators and BDFLs can click on a link or two at the

Changes to www/serverext.wiki.

84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

The /sqlite-src-ext/checklist file is a
[https://wapp.tcl.tk|Wapp program].  The current source code to the
this program can be seen at
[https://www.sqlite.org/src/ext/checklist/3070700/self] and
recent historical versions are available at
[https://sqlite.org/docsrc/finfo/misc/checklist.tcl] with
older legacy at [https://sqlite.org/checklistapp/timeline?n=all]

There is a cascade of CGIs happening here.  The web server that receives
the initial HTTP request runs Fossil as a CGI based on the
"https://sqlite.org/src" portion of the URL.  The Fossil instance then
runs the checklist sub-CGI based on the "/ext/checklists" suffix.  The
output of the sub-CGI is read by Fossil and then relayed on to the
main web server which in turn relays the result back to the original client.







|







84
85
86
87
88
89
90
91
92
93
94
95
96
97
98

The /sqlite-src-ext/checklist file is a
[https://wapp.tcl.tk|Wapp program].  The current source code to the
this program can be seen at
[https://www.sqlite.org/src/ext/checklist/3070700/self] and
recent historical versions are available at
[https://sqlite.org/docsrc/finfo/misc/checklist.tcl] with
older legacy at [https://sqlite.org/checklistapp/timeline?n1=all]

There is a cascade of CGIs happening here.  The web server that receives
the initial HTTP request runs Fossil as a CGI based on the
"https://sqlite.org/src" portion of the URL.  The Fossil instance then
runs the checklist sub-CGI based on the "/ext/checklists" suffix.  The
output of the sub-CGI is read by Fossil and then relayed on to the
main web server which in turn relays the result back to the original client.

Changes to www/shunning.wiki.

90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
    check-in.</p></li>
</ul>

<h2>Exception: Non-versioned Content</h2>

It is normal and expected to delete data which is not versioned, such as
usernames and passwords in the user table. The [/help/scrub|fossil scrub]
command will remove all sensitive non-versioned data from a respository.

The scrub command will remove user 'bertina', along with their password,
any supplied IP address, any concealed email address etc. However, in the 
DAG, commits by 'bertina' will continue to be visible unchanged even though
there is no longer any such user in Fossil.

<h2>Shunning</h2>







|







90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
    check-in.</p></li>
</ul>

<h2>Exception: Non-versioned Content</h2>

It is normal and expected to delete data which is not versioned, such as
usernames and passwords in the user table. The [/help/scrub|fossil scrub]
command will remove all sensitive non-versioned data from a repository.

The scrub command will remove user 'bertina', along with their password,
any supplied IP address, any concealed email address etc. However, in the 
DAG, commits by 'bertina' will continue to be visible unchanged even though
there is no longer any such user in Fossil.

<h2>Shunning</h2>

Changes to www/ssl.wiki.

155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
CA's signing certificate in PEM format and pointing OpenSSL at it:

<pre>
     fossil set --global ssl-ca-location /path/to/local-ca.pem
</pre>

The use of <tt>--global</tt> with this option is common, since you may
have multiple reposotories served under certificates signed by that same
CA. However, if you have a mix of publicly-signed and locally-signed
certificates, you might want to drop the <tt>--global</tt> flag and set
this option on a per-repository basis instead.

A common way to run into the broader first problem is that you're on
FreeBSD, which does not install a CA certificate set by default, even as
a dependency of the OpenSSL library.  If you're using a certificate







|







155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
CA's signing certificate in PEM format and pointing OpenSSL at it:

<pre>
     fossil set --global ssl-ca-location /path/to/local-ca.pem
</pre>

The use of <tt>--global</tt> with this option is common, since you may
have multiple repositories served under certificates signed by that same
CA. However, if you have a mix of publicly-signed and locally-signed
certificates, you might want to drop the <tt>--global</tt> flag and set
this option on a per-repository basis instead.

A common way to run into the broader first problem is that you're on
FreeBSD, which does not install a CA certificate set by default, even as
a dependency of the OpenSSL library.  If you're using a certificate

Changes to www/sync.wiki.

911
912
913
914
915
916
917
918
919
920
921
922
923
924
925

<p>As with a pull, the steps of a push operation repeat until the
server knows all artifacts that exist on the client.  Also, as with
pull, the client attempts to keep the size of the request from
growing too large by suppressing file cards once the
size of the request reaches 1MB.</p>

<h3>5.3 Sync</h3>

<p>A sync is just a pull and a push that happen at the same time.
The first three steps of a pull are combined with the first five steps
of a push.  Steps (4) through (7) of a pull are combined with steps
(5) through (8) of a push.  And steps (8) through (10) of a pull
are combined with step (9) of a push.</p>








|







911
912
913
914
915
916
917
918
919
920
921
922
923
924
925

<p>As with a pull, the steps of a push operation repeat until the
server knows all artifacts that exist on the client.  Also, as with
pull, the client attempts to keep the size of the request from
growing too large by suppressing file cards once the
size of the request reaches 1MB.</p>

<h3 id="sync">5.3 Sync</h3>

<p>A sync is just a pull and a push that happen at the same time.
The first three steps of a pull are combined with the first five steps
of a push.  Steps (4) through (7) of a pull are combined with steps
(5) through (8) of a push.  And steps (8) through (10) of a pull
are combined with step (9) of a push.</p>

Changes to www/uitest.html.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//////////////////////////////////////////////////////////////////////////
{
 url: "timeline",
 desc:
   "Simple timeline of most recent check-ins. Verify that all submenus work."
},
{
 url: "timeline?n=125",
 desc:
   "Timeline with 125 entries.  Verify that submenus preserve the entry count."
},
{
 url: "wiki",
 desc:
   "The wiki homepage"







|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//////////////////////////////////////////////////////////////////////////
{
 url: "timeline",
 desc:
   "Simple timeline of most recent check-ins. Verify that all submenus work."
},
{
 url: "timeline?n1=125",
 desc:
   "Timeline with 125 entries.  Verify that submenus preserve the entry count."
},
{
 url: "wiki",
 desc:
   "The wiki homepage"

Changes to www/webpage-ex.md.

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
Web-Page Examples
=================

Here are just a few examples of the many web pages supported
by Fossil.  Follow hyperlinks on the examples below to see many
other examples.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?y=ci&n=100'>(Example)</a> &rarr;
     100 most recent check-ins.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/finfo?name=src/file.c'>(Example)</a> &rarr;
     All changes to the <b>src/file.c</b> source file.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?n=200&uf=0c3c2d086a'>(Example)</a> &rarr;
     All check-ins using a particular version of the <b>src/file.c</b>
     source file.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?n=11&y=ci&c=2014-01-01'>(Example)</a> &rarr;
     Check-ins proximate to an historical point in time (2014-01-01).

  *  <a target='_blank' class='exbtn'




     href='$ROOT/timeline?n=11&y=ci&c=2014-01-01&v=1'>(Example)</a> &rarr;
     The previous example augmented with file changes.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?n=25&y=ci&a=1970-01-01'>(Example)</a> &rarr;
     First 25 check-ins after 1970-01-01.  (The first 25 check-ins of
     the project.)

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?n=200&r=svn-import'>(Example)</a> &rarr;
     All check-ins of the "svn-import" branch together with check-ins
     that merge with that branch.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?n=200&t=svn-import'>(Example)</a> &rarr;
     All check-ins of the "svn-import" branch only.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?n=100&y=ci&ubg'>(Example)</a> &rarr;
     100 most recent check-ins color coded by committer rather than by branch.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?from=version-1.27&to=version-1.28'>(Example)</a> &rarr;
     All check-ins on the most direct path from
     version-1.27 to version-1.28









|







|




|



>
>
>
>
|



|




|




|



|







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
Web-Page Examples
=================

Here are just a few examples of the many web pages supported
by Fossil.  Follow hyperlinks on the examples below to see many
other examples.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?y=ci&n1=100'>(Example)</a> &rarr;
     100 most recent check-ins.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/finfo?name=src/file.c'>(Example)</a> &rarr;
     All changes to the <b>src/file.c</b> source file.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?n1=200&uf=0c3c2d086a'>(Example)</a> &rarr;
     All check-ins using a particular version of the <b>src/file.c</b>
     source file.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?n1=11&y=ci&c=2014-01-01'>(Example)</a> &rarr;
     Check-ins proximate to an historical point in time (2014-01-01).

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?df=release&y=ci'>(Example)</a> &rarr;
     All check-ins derived from the most recent release.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?n1=11&y=ci&c=2014-01-01&v=1'>(Example)</a> &rarr;
     The previous example augmented with file changes.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?n1=25&y=ci&a=1970-01-01'>(Example)</a> &rarr;
     First 25 check-ins after 1970-01-01.  (The first 25 check-ins of
     the project.)

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?n1=200&r=svn-import'>(Example)</a> &rarr;
     All check-ins of the "svn-import" branch together with check-ins
     that merge with that branch.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?n1=200&t=svn-import'>(Example)</a> &rarr;
     All check-ins of the "svn-import" branch only.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?n1=100&y=ci&ubg'>(Example)</a> &rarr;
     100 most recent check-ins color coded by committer rather than by branch.

  *  <a target='_blank' class='exbtn'
     href='$ROOT/timeline?from=version-1.27&to=version-1.28'>(Example)</a> &rarr;
     All check-ins on the most direct path from
     version-1.27 to version-1.28

Changes to www/webui.wiki.

1
2
3
4
5
6
7
8
9
10
11

12
13
14
15
16
17
18
<title>The Fossil Web Interface</title>

One of the innovative features of Fossil is its built-in web interface.
This web interface provides everything you need to run a software
development project:

  *  [./bugtheory.wiki | Ticketing and bug tracking]
  *  [./wikitheory.wiki | Wiki]
  *  [./embeddeddoc.wiki | On-line documentation]
  *  [./event.wiki | Technical notes]
  *  [./forum.wiki | Forum]

  *  Timelines
  *  Full text search over all of the above
  *  Status information
  *  Graphs of revision and branching history
  *  File and version lists and differences
  *  Download historical versions as ZIP archives
  *  Historical change data











>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<title>The Fossil Web Interface</title>

One of the innovative features of Fossil is its built-in web interface.
This web interface provides everything you need to run a software
development project:

  *  [./bugtheory.wiki | Ticketing and bug tracking]
  *  [./wikitheory.wiki | Wiki]
  *  [./embeddeddoc.wiki | On-line documentation]
  *  [./event.wiki | Technical notes]
  *  [./forum.wiki | Forum]
  *  [./chat.md | Chatroom]
  *  Timelines
  *  Full text search over all of the above
  *  Status information
  *  Graphs of revision and branching history
  *  File and version lists and differences
  *  Download historical versions as ZIP archives
  *  Historical change data
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
also create new tickets or look at summaries or complete histories of
existing tickets.  Any changes you make will automatically merge with
changes from your co-workers the next time your repository is synchronized.

You can view and edit <b>wiki</b> by following the "Wiki" link on the
menu bar.  Fossil has its own easy-to-remember
[/wiki_rules | markup rules], or if you prefer,  it also
support [/md_rules | Markdown]. And, as with tickets, all of
your edits will automatically merge with those of your co-workers when
your repository synchronizes.

You can view summary reports of <b>branches</b> in the
check-in graph by visiting the "Branches" link on the
menu bar.  From those pages you can follow hyperlinks to get additional
details.  These screens allow you to easily keep track of what is going







|







99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
also create new tickets or look at summaries or complete histories of
existing tickets.  Any changes you make will automatically merge with
changes from your co-workers the next time your repository is synchronized.

You can view and edit <b>wiki</b> by following the "Wiki" link on the
menu bar.  Fossil has its own easy-to-remember
[/wiki_rules | markup rules], or if you prefer,  it also
supports [/md_rules | Markdown]. And, as with tickets, all of
your edits will automatically merge with those of your co-workers when
your repository synchronizes.

You can view summary reports of <b>branches</b> in the
check-in graph by visiting the "Branches" link on the
menu bar.  From those pages you can follow hyperlinks to get additional
details.  These screens allow you to easily keep track of what is going
128
129
130
131
132
133
134
135









136
137
138
139
140
141
142
Users with appropriate permissions can customize the look and feel of
the web interface using the "Admin" link on the main menu of the web
interface.  Templates
for the header and footer of each page can be edited, as can the CSS
for the entire page.  You can even change around the main menu.
Timeline display preferences can be edited.  The page that is brought
up as the "Home" page can be changed.  It is often useful to set the
"Home" page to be a wiki page or an embedded document.










<h2>Installing On A Network Server</h2>

When you create a new Fossil project and after you have configured it
like you want it using the web interface, you can make the project
available to a distributed team by simply copying the single
repository file up to a web server that supports CGI or SCGI.  To







|
>
>
>
>
>
>
>
>
>







129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
Users with appropriate permissions can customize the look and feel of
the web interface using the "Admin" link on the main menu of the web
interface.  Templates
for the header and footer of each page can be edited, as can the CSS
for the entire page.  You can even change around the main menu.
Timeline display preferences can be edited.  The page that is brought
up as the "Home" page can be changed.  It is often useful to set the
"Home" page to be a wiki page or an embedded document. The built-in
pages <b>/home</b> and <b>/index</b> can be used as the "Home" page.
They have identical effect, which is to instruct Fossil to find and
display a wiki page with the same name as the project, or if that
does not exist, <b>/README.md</b> or <b>/index.wiki</b>.

An embedded document link such as <b>doc/trunk/README.md</b> can be
used for the "Home" page. If you specify one of the built-in keywords
<b>/home</b> or <b>/index</b>, the page will not be treated as an 
embedded document.

<h2>Installing On A Network Server</h2>

When you create a new Fossil project and after you have configured it
like you want it using the web interface, you can make the project
available to a distributed team by simply copying the single
repository file up to a web server that supports CGI or SCGI.  To

Changes to www/whyusefossil.wiki.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<title>Why Use Fossil</title>
<h1 align='center'>Why You Should Use Fossil</h1>
<p align='center'><b>Or, if not Fossil, at least some kind of modern
version control<br>such as Git, Mercurial, or Subversion.</b></p>
<p align='center'>(Presented in outline form, for people in a hurry)</p>

<ol>
<li><p><b>Benefits of Version Control</b>
  <ol type='A'>
  <li><p><b>Immutable file and version identification</b>
     <ol type='i'>
     <li>Simplified and unambiguous communication between developers
     <li>Detect accidental or surreptitious changes
     <li>Locate the origin of discovered files
     </ol>






<
|







1
2
3
4
5
6

7
8
9
10
11
12
13
14
<title>Why Use Fossil</title>
<h1 align='center'>Why You Should Use Fossil</h1>
<p align='center'><b>Or, if not Fossil, at least some kind of modern
version control<br>such as Git, Mercurial, or Subversion.</b></p>
<p align='center'>(Presented in outline form, for people in a hurry)</p>


<b>I. Benefits of Version Control</b>
  <ol type='A'>
  <li><p><b>Immutable file and version identification</b>
     <ol type='i'>
     <li>Simplified and unambiguous communication between developers
     <li>Detect accidental or surreptitious changes
     <li>Locate the origin of discovered files
     </ol>
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
     <ol type='i'>
     <li>Everyone always has the latest code
     <li>Failed disk-drives cause no loss of work
     <li>Avoid wasting time doing manual file copying
     <li>Avoid human errors during manual backups
     </ol>
  </ol>

<a name='definitions'></a>
<li><p><b>Definitions</b></p>

  <ul>
  <li><p><b>Project</b> &rarr;
       a collection of computer files that serve some common
      purpose.  Often the project is a software application and the
      individual files are source code together with makefiles, scripts, and
      "README.txt" files.  Other examples of projects include books or
      manuals in which each chapter or section is held in a separate file.
     <ul>
     <li><p>Projects change and evolve.  The whole purpose of version
         control is to track and manage that evolution.
     <li><p>Most projects contain many files, but it is possible
         to have a project consisting of just a single file.
     <li><p>Fossil requires that
         all the files for a project must be collected into a single
         directory hierarchy - a single folder possibly with layers
         of subfolders.  Fossil is not a good choice for managing a
         project that has files scattered hither and yon all over
         the disk.  In other words, Fossil only works for projects
         where the files are laid out such that they can be archived
         into a ZIP file or tarball.
     </ul>
  <li><p><b>Repository</b> &rarr;
      (also called "repo") a single file that contains
      all historical versions of all files in a project.  A repo is similar
      to a ZIP archive in that it is a single file that stores compressed
      versions of many other files.  Files can be extracted from the
      repo and new files can be added to the repo, just as with a ZIP
      archive.  But a repo has other capabilities above and beyond
      what a ZIP archive can do.
      <ul>
      <li><p>Fossil does not care what you name your repository files,
          though names ending with ".fossil" are recommended.
      <li><p>A single project typically has multiple, redundant repositories on
      separate machines.
      <li><p>All repositories stay synchronized with one another by exchanging
          information via HTTP or SSH.
      <li><p>All repos for a single project redundantly store all information
          about that project.  So if any one repo is lost due to a disk
          crash, all content is preserved in the surviving repos.
      <li><p>The usual arrangement is one repository per user.  And since
          most users these days have their own computer, that means one
          repository per computer.  But this is not a requirement.  It is
          ok to have multiple copies of the same repository on the same
          computer.
      <li><p>Fossil works fine with just a single copy of the repository.
          But in that case there is no redundancy.  If that one repository
          file is lost due to a hardware malfunction, then there is no way
          to recover the project.
      <li><p>Best practice is to keep all repositories for a user in a single
          folder.  Folders such as "~/Fossils" or "%USERPROFILE%\Fossils"
          are recommended.  Fossil itself does not care where the repositories
          are stored.  Nor does Fossil require repositories to be
          kept in the same folder.  But it is easier to organize your work
          if all repositories are kept in the same place.
      </ul>
  <li><p><b>Check-out</b> &rarr;
      a set of files that have been extracted from a
      repository and that represent a particular version or snapshot of
      the project.
      <ul>
      <li><p>Check-outs must be on the same computer as the repository from
          which they are extracted.  This is just like with a ZIP archive:
          one must have the ZIP archive file on the local machine before
          extracting files from ZIP archive.
      <li><p>There can be multiple check-outs (in different folders) from
          the same repository.
      <li><p>The repository must be on the same computer as the check-out, but
          the relative locations of the repo and the check-out are arbitrary.
          The repository may be located inside the folder holding the
          check-out, but it certainly does not have to be and usually is
          not.
      <li><p>A special file exists in every check-out that tells Fossil from
      which repository the check-out was extracted, and which version of
      the project the check-out represents.  This is the ".fslckout" file
      on unix systems or the "_FOSSIL_" file on Windows.
      </ul>
  <li><p><b>Check-in</b> &rarr;
      another name for a particular version of the project.
      A check-in is a collection of files inside of a repository that
      represent a snapshot of the project for an instant in time.
      Check-ins exist only inside of the repository.  This contrasts with
      a check-out which is a collection of files outside of the repository.
      <ul>
      <li><p>Every check-out knows the check-in from which it was derived.
          But check-outs might have been edited and so might not exactly
          match their associated check-in.
      <li><p>Check-ins are immutable.  They can never be changed.  But
          check-outs are collections of ordinary files on disk.  The
          files of a check-out can be edited just like any other file.
      <li><p>A check-in can be thought of as an historical snapshot of a
          check-out.
      <li><p>"Check-in", "version", "snapshot", and "revision" are synonyms.
      <li><p> When used as a noun, the word "commit" is another synonym
          for "check-in".  When used as a verb, the word "commit"
          means to create a new check-in.
      </ul>
  </ul>

<li><p><b>Basic Fossil commands</b>

  <ul>
  <li><p><b>clone</b> &rarr;
      Make a copy of a repository.  The original repository
      is usually (but not always) on a remote machine and the copy is on
      the local machine.  The copy remembers the network location from
      which it was copied and (by default) tries to keep itself synchronized
      with the original.
  <li><p><b>open</b> &rarr;
      Create a new check-out from a repository on the local machine.
  <li><p><b>update</b> &rarr;
      Modify an existing check-out so that it is derived from a
      different version of the same project.
  <li><p><b>commit</b> &rarr;
      Create a new version (a new check-in) of the project that
      is a snapshot of the current check-out.
  <li><p><b>revert</b> &rarr;
      Undo all local edits on a check-out.  Make the check-out
      be an exact copy of its associated check-in.
  <li><p><b>push</b> &rarr;
      Copy content found in a local repository over to a remote
      repository.  (Fossil usually does this automatically in response to
      a "commit" and so this command is seldom used, but it is important
      to understand it.)
  <li><p><b>pull</b> &rarr;
      Copy new content found in a remote repository into a local
      repository.  A "pull" by itself does not modify any check-out.  The
      "pull" command only moves content between repositories.  However,
      the "update" command will (often) automatically do a "pull" before
      attempting to update the local check-out.
  <li><p><b>sync</b> &rarr;
      Do both a "push" and a "pull" at the same time.
  <li><p><b>add</b> &rarr;
      Add a new file to the local check-out.  The file must already be
      on disk.  This command tells Fossil to start tracking and managing
      the file.  This command affects only the local check-out and does
      not modify any repository.  The new file is inserted into the
      repository at the next "commit" command.
  <li><p><b>rm/mv</b> &rarr;
      Short for 'remove' and 'move', these commands are like "add"
      in that they specify pending changes to the structure of the check-out.
      As with "add", no changes are made to the repository until the next
      "commit".
  </ul>

<li><p><b>The history of a project is a Directed Acyclic Graph (DAG)</b>

  <ul>
  <li><p>Fossil (and other distributed VCSes like Git and Mercurial, but
      not Subversion) represent
      the history of a project as a directed acyclic graph (DAG).
      <ul>
      <li><p>Each check-in is a node in the graph
      <li><p>If check-in Y is derived from check-in X then there is
          an arc in the graph from node X to node Y.
      <li><p>The older check-in (X) is call the "parent" and the newer
          check-in (Y) is the "child".  The child is derived from
          the parent.
      </ul>
  <li><p>Two users (or the same user working in different check-outs)
      might commit different changes against the same check-in.  This
      results in one parent node having two or more children.
  <li><p>Command: <b>merge</b> &rarr;
      combines the work of multiple check-ins into
      a single check-out.  That check-out can then be committed to create
      a new check-in that has two (or more) parents.
      <ul>
      <li><p>Most check-ins have just one parent, and either zero or
          one child.
      <li><p>When a check-in has two or more parents, one of those parents
          is the "primary parent". All the other parent nodes are "secondary"
          or "merge" parents.
          Conceptually, the primary parent shows the main line of
          development.  Content from the merge parents is added
          into the main line.
      <li><p>The "direct children" of a check-in X are all children that
          have X as their primary parent.
      <li><p>A check-in node with no direct children is sometimes called
          a "leaf".
      <li><p>The "merge" command changes only the check-out.
          The "commit" command must be run subsequently to make the merge
          a permanent part of project.
      </ul>
  <li><p>Definition: <b>branch</b> &rarr;
      a sequence of check-ins that are all linked
      together in the DAG through the primary parent.
       <ul>
       <li><p>Branches are often given names which propagate to direct children.
           The tradition in Fossil is to call the main branch "trunk".  In
           Git, the main branch is usually called "master".

       <li><p>It is possible to have multiple branches with the same name.
          Fossil has no problem with this, but it can be confusing to
          humans, so best practice is to give each branch a unique name.
       <li><p>The name of a branch can be changed by adding special tags
          to the first check-in of a branch.  The name assigned by this
          special tag automatically propagates to all direct children.
       </ul>
  </ul>

<li><p><b>Why version control is important (reprise)</b>

  <ol type="A">
  <li><p>Every check-in and every individual file has a unique name - its
      SHA1 or SHA3-256 hash.  Team members can unambiguously identify
      any specific
      version of the overall project or any specific version of an
      individual file.
  <li><p>Any historical version of the whole project or of any individual
      file can be easily recreated at any time and by any team member.
  <li><p>Accidental changes to files can be detected by recomputing their
      cryptographic hash.
  <li><p>Files of unknown origin can be identified using their hash.
  <li><p>Developers are able to work in parallel, review each others work,
      and easily merge their changes together.  External revisions to
      the baseline can be easily incorporated into the latest changes.
  <li><p>Developers can follow experimental lines of development,  then
      revert back to an earlier stable version if the experiment does
      not work out.  Creativity is enhanced by allowing crazy ideas to
      be investigated without destabilizing the project.
  <li><p>Developers can work on several independent subprojects, flipping
      back and forth from one subproject to another at will, and merge
      patches together or back into the main line of development as they
      mature.
  <li><p>Older changes can be easily backed out of recent revisions, for
      example if bugs are found long after the code was committed.
  <li><p>Enhancements in a branch can be easily copied into other branches,
      or into the trunk.
  <li><p>The complete history of all changes is plainly visible to
      all team members.  Project leaders can easily keep track of what
      all team members are doing.  Check-in comments help everyone to
      understand and/or remember the reason for each change.
  <li><p>New team members can be brought up-to-date with all of the historical
      code, quickly and easily.
  <li><p>New developers, interns, or inexperienced staff members who still
      do not understand all the details of the project or who are otherwise
      prone to making mistakes can be assigned significant subprojects to
      be carried out in branches without risking main line stability.
  <li><p>Code is automatically synchronized across all machines.  No human
      effort is wasted copying files from machine to machine.  The risk
      of human errors during file transfer and backup is eliminated.
  <li><p>A hardware failure results in minimal lost work because
      all previously committed changes will have been automatically
      replicated on other machines.
  <li><p>The complete work history of the project is conveniently archived
      in a single file, simplifying long-term record keeping.
  <li><p>A precise historical record is maintained which can be used to
      support copyright and patent claims or regulatory compliance.
  </ol>
</ol>







>

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

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
     <ol type='i'>
     <li>Everyone always has the latest code
     <li>Failed disk-drives cause no loss of work
     <li>Avoid wasting time doing manual file copying
     <li>Avoid human errors during manual backups
     </ol>
  </ol>

<a name='definitions'></a>
<p><b>II. Definitions</b></p>

<ul>
<li><p><b>Project</b> &rarr;
  a collection of computer files that serve some common
  purpose.  Often the project is a software application and the
  individual files are source code together with makefiles, scripts, and
  "README.txt" files.  Other examples of projects include books or
  manuals in which each chapter or section is held in a separate file.
 <ul>
 <li><p>Projects change and evolve.  The whole purpose of version
     control is to track and manage that evolution.
 <li><p>Most projects contain many files, but it is possible
     to have a project consisting of just a single file.
 <li><p>Fossil requires that
     all the files for a project must be collected into a single
     directory hierarchy - a single folder possibly with layers
     of subfolders.  Fossil is not a good choice for managing a
     project that has files scattered hither and yon all over
     the disk.  In other words, Fossil only works for projects
     where the files are laid out such that they can be archived
     into a ZIP file or tarball.
 </ul>
<li><p><b>Repository</b> &rarr;
 (also called "repo") a single file that contains
 all historical versions of all files in a project.  A repo is similar
 to a ZIP archive in that it is a single file that stores compressed
 versions of many other files.  Files can be extracted from the
 repo and new files can be added to the repo, just as with a ZIP
 archive.  But a repo has other capabilities above and beyond
 what a ZIP archive can do.
 <ul>
 <li><p>Fossil does not care what you name your repository files,
     though names ending with ".fossil" are recommended.
 <li><p>A single project typically has multiple, redundant repositories on
 separate machines.
 <li><p>All repositories stay synchronized with one another by exchanging
     information via HTTP or SSH.
 <li><p>All repos for a single project redundantly store all information
     about that project.  So if any one repo is lost due to a disk
     crash, all content is preserved in the surviving repos.
 <li><p>The usual arrangement is one repository per user.  And since
     most users these days have their own computer, that means one
     repository per computer.  But this is not a requirement.  It is
     ok to have multiple copies of the same repository on the same
     computer.
 <li><p>Fossil works fine with just a single copy of the repository.
     But in that case there is no redundancy.  If that one repository
     file is lost due to a hardware malfunction, then there is no way
     to recover the project.
 <li><p>Best practice is to keep all repositories for a user in a single
     folder.  Folders such as "~/Fossils" or "%USERPROFILE%\Fossils"
     are recommended.  Fossil itself does not care where the repositories
     are stored.  Nor does Fossil require repositories to be
     kept in the same folder.  But it is easier to organize your work
     if all repositories are kept in the same place.
 </ul>
<li><p><b>Check-out</b> &rarr;
 a set of files that have been extracted from a
 repository and that represent a particular version or snapshot of
 the project.
 <ul>
 <li><p>Check-outs must be on the same computer as the repository from
     which they are extracted.  This is just like with a ZIP archive:
     one must have the ZIP archive file on the local machine before
     extracting files from ZIP archive.
 <li><p>There can be multiple check-outs (in different folders) from
     the same repository.
 <li><p>The repository must be on the same computer as the check-out, but
     the relative locations of the repo and the check-out are arbitrary.
     The repository may be located inside the folder holding the
     check-out, but it certainly does not have to be and usually is
     not.
 <li><p>A special file exists in every check-out that tells Fossil from
 which repository the check-out was extracted, and which version of
 the project the check-out represents.  This is the ".fslckout" file
 on unix systems or the "_FOSSIL_" file on Windows.
 </ul>
<li><p><b>Check-in</b> &rarr;
 another name for a particular version of the project.
 A check-in is a collection of files inside of a repository that
 represent a snapshot of the project for an instant in time.
 Check-ins exist only inside of the repository.  This contrasts with
 a check-out which is a collection of files outside of the repository.
 <ul>
 <li><p>Every check-out knows the check-in from which it was derived.
     But check-outs might have been edited and so might not exactly
     match their associated check-in.
 <li><p>Check-ins are immutable.  They can never be changed.  But
     check-outs are collections of ordinary files on disk.  The
     files of a check-out can be edited just like any other file.
 <li><p>A check-in can be thought of as an historical snapshot of a
     check-out.
 <li><p>"Check-in", "version", "snapshot", and "revision" are synonyms.
 <li><p> When used as a noun, the word "commit" is another synonym
     for "check-in".  When used as a verb, the word "commit"
     means to create a new check-in.
 </ul>
</ul>

<p><b>III. Basic Fossil commands</b>

 <ul>
 <li><p><b>clone</b> &rarr;
     Make a copy of a repository.  The original repository
     is usually (but not always) on a remote machine and the copy is on
     the local machine.  The copy remembers the network location from
     which it was copied and (by default) tries to keep itself synchronized
     with the original.
 <li><p><b>open</b> &rarr;
     Create a new check-out from a repository on the local machine.
 <li><p><b>update</b> &rarr;
     Modify an existing check-out so that it is derived from a
     different version of the same project.
 <li><p><b>commit</b> &rarr;
     Create a new version (a new check-in) of the project that
     is a snapshot of the current check-out.
 <li><p><b>revert</b> &rarr;
     Undo all local edits on a check-out.  Make the check-out
     be an exact copy of its associated check-in.
 <li><p><b>push</b> &rarr;
     Copy content found in a local repository over to a remote
     repository.  (Fossil usually does this automatically in response to
     a "commit" and so this command is seldom used, but it is important
     to understand it.)
 <li><p><b>pull</b> &rarr;
     Copy new content found in a remote repository into a local
     repository.  A "pull" by itself does not modify any check-out.  The
     "pull" command only moves content between repositories.  However,
     the "update" command will (often) automatically do a "pull" before
     attempting to update the local check-out.
 <li><p><b>sync</b> &rarr;
     Do both a "push" and a "pull" at the same time.
 <li><p><b>add</b> &rarr;
     Add a new file to the local check-out.  The file must already be
     on disk.  This command tells Fossil to start tracking and managing
     the file.  This command affects only the local check-out and does
     not modify any repository.  The new file is inserted into the
     repository at the next "commit" command.
 <li><p><b>rm/mv</b> &rarr;
     Short for 'remove' and 'move', these commands are like "add"
     in that they specify pending changes to the structure of the check-out.
     As with "add", no changes are made to the repository until the next
     "commit".
 </ul>

<b>IV. The history of a project is a Directed Acyclic Graph (DAG)</b>

 <ul>
 <li><p>Fossil (and other distributed VCSes like Git and Mercurial, but
     not Subversion) represent
     the history of a project as a directed acyclic graph (DAG).
     <ul>
     <li><p>Each check-in is a node in the graph
     <li><p>If check-in Y is derived from check-in X then there is
         an arc in the graph from node X to node Y.
     <li><p>The older check-in (X) is call the "parent" and the newer
         check-in (Y) is the "child".  The child is derived from
         the parent.
     </ul>
 <li><p>Two users (or the same user working in different check-outs)
     might commit different changes against the same check-in.  This
     results in one parent node having two or more children.
 <li><p>Command: <b>merge</b> &rarr;
     combines the work of multiple check-ins into
     a single check-out.  That check-out can then be committed to create
     a new check-in that has two (or more) parents.
     <ul>
     <li><p>Most check-ins have just one parent, and either zero or
         one child.
     <li><p>When a check-in has two or more parents, one of those parents
         is the "primary parent". All the other parent nodes are "secondary"
         or "merge" parents.
         Conceptually, the primary parent shows the main line of
         development.  Content from the merge parents is added
         into the main line.
     <li><p>The "direct children" of a check-in X are all children that
         have X as their primary parent.
     <li><p>A check-in node with no direct children is sometimes called
         a "leaf".
     <li><p>The "merge" command changes only the check-out.
         The "commit" command must be run subsequently to make the merge
         a permanent part of project.
     </ul>
 <li><p>Definition: <b>branch</b> &rarr;
     a sequence of check-ins that are all linked
     together in the DAG through the primary parent.
      <ul>
      <li><p>Branches are often given names which propagate to direct children.
          The tradition in Fossil is to call the main branch "trunk".  In
          Git, it's called "master" by default, though some call it
          something else, like "main".
      <li><p>It is possible to have multiple branches with the same name.
         Fossil has no problem with this, but it can be confusing to
         humans, so best practice is to give each branch a unique name.
      <li><p>The name of a branch can be changed by adding special tags
         to the first check-in of a branch.  The name assigned by this
         special tag automatically propagates to all direct children.
      </ul>
 </ul>

<b>V. Why version control is important (reprise)</b>

 <ol>
 <li><p>Every check-in and every individual file has a unique name - its
     SHA1 or SHA3-256 hash.  Team members can unambiguously identify
     any specific
     version of the overall project or any specific version of an
     individual file.
 <li><p>Any historical version of the whole project or of any individual
     file can be easily recreated at any time and by any team member.
 <li><p>Accidental changes to files can be detected by recomputing their
     cryptographic hash.
 <li><p>Files of unknown origin can be identified using their hash.
 <li><p>Developers are able to work in parallel, review each others work,
     and easily merge their changes together.  External revisions to
     the baseline can be easily incorporated into the latest changes.
 <li><p>Developers can follow experimental lines of development,  then
     revert back to an earlier stable version if the experiment does
     not work out.  Creativity is enhanced by allowing crazy ideas to
     be investigated without destabilizing the project.
 <li><p>Developers can work on several independent subprojects, flipping
     back and forth from one subproject to another at will, and merge
     patches together or back into the main line of development as they
     mature.
 <li><p>Older changes can be easily backed out of recent revisions, for
     example if bugs are found long after the code was committed.
 <li><p>Enhancements in a branch can be easily copied into other branches,
     or into the trunk.
 <li><p>The complete history of all changes is plainly visible to
     all team members.  Project leaders can easily keep track of what
     all team members are doing.  Check-in comments help everyone to
     understand and/or remember the reason for each change.
 <li><p>New team members can be brought up-to-date with all of the historical
     code, quickly and easily.
 <li><p>New developers, interns, or inexperienced staff members who still
     do not understand all the details of the project or who are otherwise
     prone to making mistakes can be assigned significant subprojects to
     be carried out in branches without risking main line stability.
 <li><p>Code is automatically synchronized across all machines.  No human
     effort is wasted copying files from machine to machine.  The risk
     of human errors during file transfer and backup is eliminated.
 <li><p>A hardware failure results in minimal lost work because
     all previously committed changes will have been automatically
     replicated on other machines.
 <li><p>The complete work history of the project is conveniently archived
     in a single file, simplifying long-term record keeping.
 <li><p>A precise historical record is maintained which can be used to
     support copyright and patent claims or regulatory compliance.
 </ol>
</ol>