Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Comment: | Merge version-2.14 |
---|---|
Timelines: | family | ancestors | descendants | both | wiki-history |
Files: | files | file ages | folders |
SHA3-256: |
0e31c8d25896220092c79840324a02a0 |
User & Date: | george 2021-01-20 19:19:22 |
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 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 | *.a *.lib *.manifest *.o *.obj *.pdb *.res Makefile autosetup/jimsh0 autosetup/jimsh0.exe bld/* | | | 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 | # 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]} { | > > > > > > > > > > | | 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 | 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 } } | | | > > > > > > | 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 | \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: | | | | | | | | | < < < < < | 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 | 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> | | | 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 | 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); | < | | > > | | | | > | | 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 | @ 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); | | | 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 | "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' ){ | | | 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 | @ </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); | | | 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 | @ %h(zCaptcha) @ </pre> @ Enter the 8 characters above in the "Security Code" box<br/> @ </td></tr></table></div> } @ </form> fossil_free(zErr); | | > | | 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 | @ <td><input type="submit" name="submit" value="Submit"> @ <input type="submit" name="delete" value="Unsubscribe"> @ </tr> @ </table> @ </form> fossil_free(zErr); db_finalize(&q); | | | 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 | @ %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); | | | 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 | @ <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); | | | 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 | @ <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(); | | | 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 | 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 | > | | 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 | @ %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); | | > | 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 Code:</td> |
︙ | ︙ | |||
2879 2880 2881 2882 2883 2884 2885 | @ <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> | | | 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 | */ 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> } | > | | 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 | @ <td><input type="submit" name="submit" value="Dry Run"> }else{ @ <td><input type="submit" name="submit" value="Send Message"> } @ </tr> @ </table> @ </form> | | | 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 | } @ by %h(zDispUser) on hyperlink_to_date(zDate, "."); free(zUrlTail); } db_finalize(&q); @ </ol> | | | 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 | " ORDER BY mtime DESC LIMIT 1", zTarget, zFile ); } if( zUUID==0 || zUUID[0]==0 ){ style_header("No Such Attachment"); @ No such attachment.... | | | | 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 | } @ <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> | | | 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 | }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); | | | 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 | 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); | > | > | 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 | } } @ <td>%h(zMtime)</tr> } @ </tbody> @ </table> db_finalize(&q); | | | 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 | ** ** 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 | | | 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 | } /* ** COMMAND: test-escaped-arg ** ** Usage %fossil ARG ... ** | | | 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 | @ <td></td> } @ </tr> } @ </tbody></table></div> db_finalize(&q); style_table_sorter(); | | | 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 | @ <li>%z(href("%R/timeline?r=%T",zBr))%h(zBr)</a></li> } } if( cnt ){ @ </ul> } db_finalize(&q); | | | 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 | ** 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); | | | 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 | 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 ){ | | | 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 | safe_html_context(DOCSRC_FILE); wiki_render_by_mimetype(&content, zMime); document_emit_js(); } } } db_finalize(&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(); } /* ** Objects used by the "tree" webpage. */ typedef struct FileTreeNode FileTreeNode; typedef struct FileTree FileTree; |
︙ | ︙ | |||
784 785 786 787 788 789 790 | tree_add_node(&sTree, zFile, zUuid, mtime); nFile++; } db_finalize(&q); }else{ Stmt q; db_prepare(&q, | | | | > | | | | 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 | @ </ul> } } } @ </ul> @ </ul></div> builtin_request_js("tree.js"); | | | 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 | @ pathname TEXT @ ); @ CREATE VIRTUAL TABLE IF NOT EXISTS temp.foci USING files_of_checkin; ; static const char zComputeFileAgeRun[] = @ WITH RECURSIVE | | | | | | < | 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 | @ </td></tr> @ fossil_free(zAge); } @ </table></div> db_finalize(&q1); db_finalize(&q2); | | | 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 | /* ** 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> | > | | 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 | ** ** 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"); | | > | | | 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 | if( zId && (nId = (int)strlen(zId))>=8 && strncmp(zId,fossil_exe_id(),nId)==0 ){ g.isConst = 1; } etag_check(0,0); | | | 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 | 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); } | | > | | 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 | " 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"> | | | 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> <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 | 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> | > | | 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 | 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> | > | | 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 | 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 | | | 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">↑</button> @ <button id="chat-scroll-bottom" class="hidden">↓</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 | const char *zEditor; char *zCmd; char *zFile; Blob reply, line; char *zComment; int i; | | | 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 | /* ** COMMAND: checkout* ** COMMAND: co* ** ** Usage: %fossil checkout ?VERSION | --latest? ?OPTIONS? ** or: %fossil co ?VERSION | --latest? ?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 | }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> } | | | 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 | @ "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> | | | 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 | ** 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; | | | 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 | /* ** 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 | | | 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 | 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; } | > > > > > | | | 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 | 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() ); | | | 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 | 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 /> | | | 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 | 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]; } | > > > > | > | 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 | sqlite3_snprintf(sizeof(zPrefix), zPrefix, "%*s%4d:",szHash+12,"",i+1); } } @ %s(zPrefix) %h(z) } @ </pre> | | | 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 | ** * 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++; } | > > > > | | | | > > > > > > > > > > > > > > > | 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 | 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; } | | | 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 | }else{ @ <li>%s(z)</li> } } @ </ul></div> } | | > | 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 | @ <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); | | | 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 | int i; int n; const unsigned char *x; /* A table of mimetypes based on file content prefixes */ static const struct { | | | > > > | | | | | > > | | > > > > | 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 | 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(); | | | 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 | 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(); | | | | | | | | 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 | } 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> } | | | 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 | ** ** s=PATTERN Search for PATTERN */ void doc_search_page(void){ login_check_credentials(); style_header("Document Search"); search_screen(SRCH_DOC, 0); | | | 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 | int * nOut){ const unsigned char *z; char *zOut; u32 c; int n, i, j; z = (const unsigned char*)zStr; n = 0; | | | | | | 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 | 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>. | > | | 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 | } 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(); | | | 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 | 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. | | | 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 | @ <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> | | | 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 | /* 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); | > > | > < > | | > | | | > > | | > > > > | > > | | 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 | 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", | > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | < < < < < | 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 | " --quiet --done"); gitmirror_message(VERB_NORMAL, "%s\n", zCmd); #ifdef _WIN32 xCmd = popen(zCmd, "wb"); #else xCmd = popen(zCmd, "w"); #endif | | | 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 | ); 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); | | | 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 | ** 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" | | | 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 | } } @ </tr> } db_finalize(&q); @ </tbody> @ </table> | | | 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 | "<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"); } | | | 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 | "}\n"); CX("})();")/*anonymous function*/; style_script_end(); } blob_reset(&err); CheckinMiniInfo_cleanup(&cimi); db_end_transaction(0); | | | 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 | 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) | | | 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 | 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> | | | 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 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 | }else{ @ <tr class="timelineBottom" id="btm-%d(iTableId)">\ @ <td></td><td></td><td></td></tr> } } @ </table> timeline_output_graph_javascript(pGraph, TIMELINE_FILEDIFF, iTableId); | | | 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 | @ </tr> } db_finalize(&q); @ </tbody> @ </table> @ </div> } | | | 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 | ** 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. | | | 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 | forum_display_thread(froot, fpid, mode, bUnf, bHist); /* Emit Forum Javascript. */ builtin_request_js("forum.js"); forum_emit_js(); /* Emit the page style. */ | | | 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 | @ <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(); | | | 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 | @ Require moderator approval</label> @ <br><label><input type="checkbox" name="showqp" %s(PCK("showqp"))> \ @ Show query parameters</label> @ </div> } @ </form> forum_emit_js(); | | | 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 | @ Require moderator approval</label> @ <br><label><input type="checkbox" name="showqp" %s(PCK("showqp"))> \ @ Show query parameters</label> @ </div> } @ </form> forum_emit_js(); | | | 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 | 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"); | > | | 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 | db_finalize(&q); } if( iCnt>0 ){ @ </table></div> }else{ @ <h1>No forum posts found</h1> } | | | 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 | 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'); | > > > | > | 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 | 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 | | | > | | > > > > > > > > > > | 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 | 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 | | > | 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 | }; } const n = arguments.length; var i = 1; for( ; i < n; ++i ){ e = arguments[i]; if(!e){ | | | 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 | 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 | | | | 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 | /** Creates a new tooltip-like widget using the given options object. Options: .refresh: callback which is called just before the tooltip is | | | | | | > > > > > > > > | 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 | 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 | | > > > | 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 | 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; | | > | 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 | 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(){ | > > > > | > | > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | F.toast = { config: { position: { x: 5, y: 5 /*viewport-relative, pixels*/ }, displayTimeMs: 3000 }, /** Convenience wrapper around a PopupWidget which pops up a shared | | | | | | | | > | | > > > > | | 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 | 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*/; | > < < < < < | | < < < < < < > > > > > > > > > > > > > > > > > > > > > | 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 | 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 | | | | | | 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 | ** --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 | | | 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 | 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. */ | > | > | | 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 | 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]) | | | 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 | 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); | | | 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 | 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]) | > | | 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 | 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(); | | | 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 | 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")) | > | | 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 | @ <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(); | | | 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 | pFileFrom = manifest_file_next(pFrom, 0); pFileTo = manifest_file_next(pTo, 0); } } manifest_destroy(pFrom); manifest_destroy(pTo); append_diff_javascript(diffType==2); | | | 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 | 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> | | | 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 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 | 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); | | | 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 | }else{ zLine[k+1] = ' '; zLine[k+2] = ' '; } } zLine[53] = ' '; zLine[54] = ' '; | > | < | | > > > > > > > > > > > > > > > > > > | | | | | 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 | 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); | > > > > > > > | | | > | | 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 | 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 | | | 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 | @ 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 ){ | | | 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 | 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) ){ | | < | 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 | @ 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); | > | > | | 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 | @ %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> } } | > > > > > | | 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 | @ </blockquote> } @ <div class="section">Changes</div> @ <p> ticket_output_change_artifact(pTktChng, 0, 1); manifest_destroy(pTktChng); | | | 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 | } 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> } | | | | 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 | if( P("preview") ){ @ <input type="submit" name="apply" value="Apply Changes" /> } @ </td></tr> @ </table> @ </div></form> builtin_request_js("ci_edit.js"); | | | 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 | @ </ol> }else{ @ No mappings are currently defined. } if( !g.perm.Setup ){ /* Do not show intermap editing fields to non-setup users */ | | | 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 | @ 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> | | | 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 | ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** Code for the JSON API. ** | | | < | 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 | 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) | > | > | 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 | 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)); | > > > | > | 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 | /* 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) { | | | | > > > > | > | | | | | | 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 | 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. */ | > | | 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 | ** 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> | > | | 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 | 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" \ | > | | | 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 | 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 = | | | 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 | 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> | | | 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 | @ <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> } | | | 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 | @ <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" \ | | > > > > > > | 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'>↑ %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 | @ </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> | | | 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 | 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 | | | 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 | 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> | | | 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 | }else{ cgi_set_parameter("REMOTE_ADDR", "127.0.0.1"); cgi_handle_http_request(0); process_one_web_page(0, 0, 1); } } | < < < < < < < < < < < < < < < < < < < < < < < < < | 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 | }else{ iPort = db_get_int("http-port", 8080); mxPort = iPort+100; } #if !defined(_WIN32) /* Unix implementation */ if( isUiCmd ){ | < < < < < < | < < < < < < < < < < | 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 | if( g.fAnyTrace ){ fprintf(stderr, "/***** Webpage finished in subprocess %d *****/\n", getpid()); } #else /* Win32 implementation */ if( isUiCmd ){ | | | 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 | @ <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> | | | 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 | $(OBJDIR)/shell.o \ $(OBJDIR)/th.o \ $(OBJDIR)/th_lang.o \ $(OBJDIR)/th_tcl.o \ $(OBJDIR)/cson_amalgamation.o | | | | 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 | $(OBJDIR)/th.o <<<NEXT_LINE>>> $(OBJDIR)/th_lang.o <<<NEXT_LINE>>> $(OBJDIR)/th_tcl.o <<<NEXT_LINE>>> $(OBJDIR)/cson_amalgamation.o }] writeln { | | | | 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 | APPTARGETS += $(BLDTARGETS) ifdef FOSSIL_BUILD_SSL APPTARGETS += openssl endif | | | | 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, " [[/info/%S | details]]");*/ 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, " [[/info/%S | details]]");*/ 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 | " 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); } | | | 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 | 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 */ | < | 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 | " ORDER BY mtime DESC LIMIT 1", fossil_roundup_date(&zTag[4]), zType); return rid; } /* "tag:" + symbolic-name */ if( memcmp(zTag, "tag:", 4)==0 ){ | < < < < < < < | < | 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 | if( db_step(&q)==SQLITE_ROW ) rid = -1; } db_finalize(&q); if( rid ) return rid; } if( zType[0]=='w' ){ | < < < | | | | | | | | | > > > | 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 | @ <ul><li> object_description(rid, 0, 0, 0); @ </li></ul> @ </p></li> } @ </ol> db_finalize(&q); | | | 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 | @ <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> | | | 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 | }else{ @ <td> } @ </tr> } @ </table> db_finalize(&q); | | | 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 | }else{ @ <td> } @ </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 | 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(); | | | 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 | @ <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(); | | | 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 | 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"); | | | 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 | @ GROUP BY 2, 3; ; /* ** WEBPAGE: test-rename-list ** ** Print a list of all file rename operations throughout history. | | > | 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 | @ <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(); | | | 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 | @ <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> | | | 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 | } 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(); | | | 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 | } 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"); | > | | 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 | bag_clear(&bagDone); ttyOutput = doOut; processCnt = 0; if (ttyOutput && !g.fQuiet) { percent_complete(0); } | | | | 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 | percent_complete((processCnt*1000)/totalSize); } if( doClustering ) create_cluster(); if( ttyOutput && !g.fQuiet && totalSize>0 ){ processCnt += incrSize; percent_complete((processCnt*1000)/totalSize); } | < | 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 | ** 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 | | | 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 | } 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(); | > | | 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 | Th_Store("report_items", blob_str(&ril)); Th_Render(zScript); blob_reset(&ril); if( g.thTrace ) Th_Trace("END_REPORTLIST<br />\n", -1); | | | 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 | 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> | > | | 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 | @ %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(); | | | 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 | @ 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> | | | 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 | @ <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(); | | | | 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 | @ </tbody></table> if( zErr1 ){ @ <p class="reportError">Error: %h(zErr1)</p> }else if( zErr2 ){ @ <p class="reportError">Error: %h(zErr2)</p> } style_table_sorter(); | | | 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 | "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); | | | 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 | ** f -> forum ** all -> everything */ void search_page(void){ login_check_credentials(); style_header("Search"); search_screen(SRCH_ALL, 1); | | | 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 | 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 | > | | 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 | @ </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); | | | 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 | @ <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); | | | 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 | } 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> | | | 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 | @ </pre></blockquote> @ </p> table_of_public_phantoms(); @ </li> } @ </ol> | | | 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 | @ <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> | | | 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 | @ </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> | | | | | | 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><base href="$secureurl/$current_page"></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><base href="$secureurl/$current_page"></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 | setup_menu_entry("SQL", "admin_sql", "Enter raw SQL commands"); setup_menu_entry("TH1", "admin_th1", "Enter raw TH1 commands"); } @ </table> | | | 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 | @ 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); | | | 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 | @ <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(); } | | | 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 | @ 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); | | > | 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 | (char*)pSet->def, hasVersionableValue); @<br /> } } @ </td></tr></table> @ </div></form> db_end_transaction(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 | @ <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 | | | 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 | 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); | | > | 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 | @ 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); | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | @ moderation. (Property: "modreq-wiki") @ </p> @ <hr /> @ <p><input type="submit" name="submit" value="Apply Changes" /></p> @ </div></form> db_end_transaction(0); | | | 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 | @ width: 600px; @ height: 90px; @ border: 1px solid #f11; @ background-color: #fcc; @ '>Demo Ad</div> @ </pre></blockquote> @ </li> | | | 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; @ '>Demo Ad</div> @ </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 | @ </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> | | | 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 | } @ </tr> } sqlite3_finalize(pStmt); @ </table> } } | | > | 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 | zR = Th_GetResult(g.interp, &n); if( rc==TH_OK ){ @ <pre class="th1result">%h(zR)</pre> }else{ @ <pre class="th1error">%h(zR)</pre> } } | | > | 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 | @ </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> } | | > | 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 | @ <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> | | | 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 | @ </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". | | | 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 | } } } if( !bUnusedOnly ){ style_submenu_element("Unused", "setup_ulist?unused"); } @ <table border=1 cellpadding=2 cellspacing=0 class='userTable sortable' \ | | | > > > > > > > | | > > > > > > > > > > > > | > | 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 | @ <span class="usertype">nobody</span>. @ </p></li> @ @ <li><p>The permission flags are as follows:</p> capabilities_table(CAPCLASS_ALL); @ </li> @ </ol> | | > | | 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 | 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> | | | | 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 | 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> | | | 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 | @ <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" \ | > | | 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 | @ <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> | | | 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 | sqlite3_finalize(pStmt); sqlite3_result_error(context, zMsg, -1); sqlite3_free(zMsg); return; } nCol = sqlite3_column_count(pStmt); z = sqlite3_sql(pStmt); | > | | | > | 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 | ** 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 | | > | 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 | if( idxNum & 2 ){ pCur->mxValue = sqlite3_value_int64(argv[i++]); }else{ pCur->mxValue = 0xffffffff; } if( idxNum & 4 ){ pCur->iStep = sqlite3_value_int64(argv[i++]); | | > > > > > | 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 | } 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 ){ | | > > > > | 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 | int nTab = STRLEN(zTab); int nByte = sizeof(IdxTable) + nTab + 1; IdxTable *pNew = 0; int rc, rc2; char *pCsr = 0; int nPk = 0; | | | 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 | ); } idxFinalize(&rc, pAllIndex); idxFinalize(&rc, pIndexXInfo); idxFinalize(&rc, pWrite); | > | | | | > | 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 | 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 */ | < > | 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 | ){ int iCur; int iHiwtr; FILE *out; if( pArg==0 || pArg->out==0 ) return 0; out = pArg->out; | | | 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 | p->nIndent = 0; p->iIndent = 0; } /* ** Disable and restore .wheretrace and .selecttrace settings. */ | < < | < < < | < < > | | < < | | < < | < < | < | 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 | " --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", | | > > > > | 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 | 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[] = { | < | > > > | > | < < | 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 | 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 ){ | | | | | | 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 | }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 */ | > > > > > > < | 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 | raw_printf(stderr,"Error: querying schema information\n"); rc = 1; }else{ rc = 0; } }else | < | > < | 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 | 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"); | > > > > > > | > > > > > | > | | 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 | "Use \".testctrl --help\" for help\n", zCmd); }else{ switch(testctrl){ /* sqlite3_test_control(int, db, int) */ case SQLITE_TESTCTRL_OPTIMIZATIONS: if( nArg==3 ){ | | | 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 | if( zVfsName ){ utf8_printf(p->out, "%s\n", zVfsName); sqlite3_free(zVfsName); } } }else | < | > < | 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 | } } if( cnt==0 ){ @ <i>no artifacts are shunned on this server</i> } db_finalize(&q); @ </p></blockquote> | | | 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 | @ <td style="padding-right: 15px;text-align: left;">%s(zHash)</td> @ <td style="text-align: left;">%s(zIpAddr)</td> @ </tr> } } db_finalize(&q); @ </table> | | | 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 | } @ </form> @ </td></tr> } } @ </table> db_finalize(&q); | | | 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 | } @ <li>%z(href("%R/sitemap-test"))Test Pages</a></li> if( isPopup ){ @ <li>%z(href("%R/sitemap"))Site Map</a></li> } @ </ul> if( !isPopup ){ | | > | 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 | 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 ){ | | > | 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 | @ <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 ){ | | | 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 | 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> | > | | 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 | 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> | > | | 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 | } 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> | > > | | 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 | @ <input type="submit" name="draftdel" value="Delete"> @ <input type="hidden" name="name" value="%h(zN)"> @ </form></tr> } db_finalize(&q); @ </table> | | | 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 | @ </pre> } blob_reset(&from); blob_reset(&to); blob_reset(&out); } @ </div></form> | | | 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 | @ <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"); | | | 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 | @ <tr> @ <td colspan="3"> @ <form method="POST" action="%R/setup_smtp_route"> @ <input type="submit" value="New"> @ ← Add a new email address @ </form> @ </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"> @ ← 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 | @ <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 → erase all text from the "Routing" field then @ press the "Apply" button. | | | 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 → 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 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 | /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite | | | 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 | ** 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()]. */ | | | | | 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 | ** 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 | | | 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 | #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 | > | | 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 | /* ** 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 | | > | 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 | # 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. */ | | < | < | > | > > > > > > > > > > > > > | 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 | #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] */ | > | | | | | | | | | | | | | | | | | | | | | | | | | | < > | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 | /* 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,\ | | | | | | | | 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 | 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 */ | | | 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 | #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. */ | | | | | | | | | | | | | | | | | | > > | | 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 | #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 */ | < | 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 | ** 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. ** | > > | > | > > > | | 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 | ** 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[] */ | | | 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 | ** ** 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 { | | > > | > > > | | < > > | | 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 | ** 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 | < < > | 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 | 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*); | < | 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 | #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; | < > | 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 | 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 | | > > | | > > | 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 | ** and incorrect behavior. */ #ifndef SQLITE_OMIT_WSD SQLITE_PRIVATE int sqlite3PendingByte = 0x40000000; #endif /* | | | > | 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 | ** 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 | > > | > > > > > | 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 | /* 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]"), | > | | | | | | | | | | | | | | | | | | | | | | | | | | < > | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 | int idx; MemPage *pPage; Btree *p = pCur->pBtree; BtShared *pBt = p->pBt; unsigned char *oldCell; unsigned char *newCell = 0; | | > | | 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 | } assert( pCur->eState==CURSOR_VALID || (pCur->eState==CURSOR_INVALID && loc) || CORRUPT_DB ); pPage = pCur->pPage; | | > > > > > > > > > > > > > | > | 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 | 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 } } | < < | 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 | testcase( zRawSql[0]=='$' ); testcase( zRawSql[0]=='@' ); testcase( zRawSql[0]=='#' ); idx = sqlite3VdbeParameterIndex(p, zRawSql, nToken); assert( idx>0 ); } zRawSql += nToken; | | | 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 | if( pData->flags & MEM_Zero ){ x.nZero = pData->u.nZero; }else{ x.nZero = 0; } x.pKey = 0; rc = sqlite3BtreeInsert(pC->uc.pCursor, &x, | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | 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]; | | | | 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 | #if !defined(SQLITE_OMIT_WINDOWFUNC) /* ** Walk all expressions linked into the list of Window objects passed ** as the second argument. */ | | > | 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 | 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) ){ | | | 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 | 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. */ | | | 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 | */ 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 */ ){ | > > > > > > > > > > > | | 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 | 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 " | > > > | | | 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 | 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 ); | < < < < | | 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 | 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++){ | > > > | | | 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 | 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); | | | 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 | return 0; } pInfo = pToplevel->pAinc; while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; } if( pInfo==0 ){ pInfo = sqlite3DbMallocRawNN(pParse->db, sizeof(*pInfo)); | > > | | 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 | 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; | > > > | | | | | | | > > | 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 | 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 */ | | | | | | > | 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 | ** 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. */ | | > > > > > > | | < | | > > > > | > | | > > > > > > > > > > | < > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | if( overrideError!=OE_Default ){ onError = overrideError; }else if( onError==OE_Default ){ onError = OE_Abort; } /* figure out whether or not upsert applies in this case */ | | > | > | | | > > > > > > > | | 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 | case OE_Ignore: { testcase( onError==OE_Ignore ); sqlite3VdbeGoto(v, ignoreDest); break; } } sqlite3VdbeResolveLabel(v, addrRowidOk); | > > | > | > > | < | | | < < | > > | | 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 | if( overrideError!=OE_Default ){ onError = overrideError; }else if( onError==OE_Default ){ onError = OE_Abort; } /* Figure out if the upsert clause applies to this index */ | | | | 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 | /* 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 */ | | | 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 | sqlite3VdbeJumpHere(v, addrBypass); /* Terminate the recheck bypass */ } seenReplace = 1; break; } } | > > | > > > | | | < < | 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 | } 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); | > | | | | | > > | | > > | > > > > > > | > | 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 | ** 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 ){ | | > > | > | 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 | 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); | | > > > > > > > | 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 | sqlite3 *db = pParse->db; AggInfo *pThis = pParse->pAggList; while( pThis ){ AggInfo *pNext = pThis->pNext; agginfoFree(db, pThis); pThis = pNext; } sqlite3DbFree(db, pParse->aLabel); | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | sParse.zTail = &zSql[nBytes]; } }else{ sqlite3RunParser(&sParse, zSql, &zErrMsg); } assert( 0==sParse.nQueryLoop ); | < < < < < < | | > > | < < | | | > | | | | | | > > > > > > > | 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 | 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); | | | 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 | ** 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); | | | 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 | ** (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 | | < | > | | < | | | 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 | ** that make up the compound SELECT are allowed to be aggregate or distinct ** queries. */ if( pSub->pPrior ){ if( pSub->pOrderBy ){ return 0; /* Restriction (20) */ } | | | > | < | < < < < < | > > > > > > > > > > > > > > > > | 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 | ** 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; | > > < < > > > > < < < < < < | | < < < < | < < | < | > > > > | | > < | | < < < < < | 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 | ** success. */ sqlite3AggInfoPersistWalkerInit(&w, pParse); sqlite3WalkSelect(&w,pSub1); sqlite3SelectDelete(db, pSub1); #if SELECTTRACE_ENABLED | | | 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 | 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) ); | > > | > > | 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 | ** 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){ | < | > > > > > | 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 | ** 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; | | > > | 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 | Walker sWalker; memset(&sWalker, 0, sizeof(sWalker)); sWalker.pParse = pParse; sWalker.xExprCallback = havingToWhereExprCb; sWalker.u.pSelect = p; sqlite3WalkExpr(&sWalker, p->pHaving); #if SELECTTRACE_ENABLED | | | 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 | } pSub = pPrior; } p->pEList->a[0].pExpr = pExpr; p->selFlags &= ~SF_Aggregate; #if SELECTTRACE_ENABLED | | | 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 | 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)); | | | 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 | } sqlite3SelectPrep(pParse, p, 0); if( pParse->nErr || db->mallocFailed ){ goto select_end; } assert( p->pEList!=0 ); #if SELECTTRACE_ENABLED | | | 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 | #ifndef SQLITE_OMIT_WINDOWFUNC rc = sqlite3WindowRewrite(pParse, p); if( rc ){ assert( db->mallocFailed || pParse->nErr>0 ); goto select_end; } #if SELECTTRACE_ENABLED | | | 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 | /* 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")); | | | | 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 | ** 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 | | | 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 | pEList = p->pEList; pWhere = p->pWhere; pGroupBy = p->pGroupBy; pHaving = p->pHaving; sDistinct.isTnct = (p->selFlags & SF_Distinct)!=0; #if SELECTTRACE_ENABLED | | | 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 | 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 | | | 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 | } #endif sNC.ncFlags &= ~NC_InAggFunc; } pAggInfo->mxReg = pParse->nMem; if( db->mallocFailed ) goto select_end; #if SELECTTRACE_ENABLED | | > > > > | 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 | updateAccumulator(pParse, iUseFlag, pAggInfo); sqlite3VdbeAddOp2(v, OP_Integer, 1, iUseFlag); VdbeComment((v, "indicate data in accumulator")); /* End of the loop */ if( groupBySort ){ | | | 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 | 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 */ | < | 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 | 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); | | | < | 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 | */ 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; | | < | < | | 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 | pProgram->nCsr = pSubParse->nTab; pProgram->token = (void *)pTrigger; pPrg->aColmask[0] = pSubParse->oldmask; pPrg->aColmask[1] = pSubParse->newmask; sqlite3VdbeDelete(v); } | < | 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 | */ /* #include "sqliteInt.h" */ #ifndef SQLITE_OMIT_UPSERT /* ** Free a list of Upsert objects */ | | > | > > > | > > > | > | > | > > | | 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 | 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; | > > > | | | | | | | | | | | | | | | < > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > > > > > > > | | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | 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 ); | > < > > | 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 | VdbeCoverage(v); sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CORRUPT, OE_Abort, 0, "corrupt database", P4_STATIC); sqlite3MayAbort(pParse); sqlite3VdbeJumpHere(v, i); } } | | | | | | | < < | 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 | ** 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 | < < < < < < < < < < < < < | 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 | struct HiddenIndexInfo { WhereClause *pWC; /* The Where clause being analyzed */ Parse *pParse; /* The parsing context */ }; /* Forward declaration of methods */ static int whereLoopResize(sqlite3*, WhereLoop*, int); | < < < < < < | 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 | 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 ){ | | | 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 | #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 | | | | | | | | | | | | | 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 | ** 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 **********************************************/ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 | /* 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, | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | | | | < | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 | /* 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 ::=", | | | | | | | | | | | | | | | | | > | | | | | | | | | | | | | | | | | | < | | | | | | | | > | | | | | | | | | | | | | < | | | > | | | | | | | | | | | | | | | | | < | | | | | | > | | | | < | | | | | > | | | | | | | | | | | | | | | | | | | < | | | | | > | | | | | | | | | | | | | | | | | | | | | | < | > | | | | | | | | | | | | | | | | | | | | | | | | | < | | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | | | | > | | | | | | | | | | | | | | | | | 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 | 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 ::= */ | | | | | | | | | | | | | | | | | > | | | | | | | | < | | | | > | | | | | < | | | | | | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | | | | | | > | | | | < | | | | | > | | | | | | | | | | | | | | | | | | | < | | | | | > | | | | | | | | | | | | | | | | | | | | | < | | > | | | | | | | | | | | | | | | | | | | | | | | < | | | | | | | > | | | | | < | | > | | | | | | | | | | | | | | | | | | | | | | < | | | | > | | | | | | | | | | | | | | | | | 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 | -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 ::= */ | | | > | | | | | | | | | | | | | | | | | | | | | | | < | | | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | < | | > | | | | | | | | | | | | | | | | | | | | | | < | | | | > | | | | | | | | | | | | | | | | | 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 | 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; | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 | 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); | | | 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 | 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); | | | 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 | { 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); | | | | | | 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 | 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 */ | | | 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 | 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); | | | | | 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 | 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); | | | | 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 | 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); | | | | | | | | 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 | { 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; | | | | | | > > > | | | | | | | | | | | | | 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 | }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; | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 | }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; | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 | }else{ fprintf(yyTraceFILE,"%sInput '%s' with pending reduce %d\n", yyTracePrompt,yyTokenName[yymajor],yyact-YY_MIN_REDUCE); } } #endif | | > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 | #ifndef YYNOERRORRECOVERY yypParser->yyerrcnt = -1; #endif } break; #endif } | < > | 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 | ** 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 */ | > | < | 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 | #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 /* '.' */ | > | | | | | | | | | | | | | | | | | | | | | | | 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 | /* 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 ){ | | | 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 | }else{ break; } } if( n==0 ) *tokenType = TK_ILLEGAL; return i; } | | | 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 | ** will take responsibility for freeing the Table structure. */ sqlite3DeleteTable(db, pParse->pNewTable); } if( !IN_RENAME_OBJECT ){ sqlite3DeleteTrigger(db, pParse->pNewTrigger); } | < < < < < < < < < < < < | 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 | ** 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*); | | | 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 | 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; } | > > > > > > > > > > > > > > > > > | > > | 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 | ** 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. */ | | > > > > | > > | | 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 | nByte += sqlite3_column_bytes(pStmt, 1); nDbCol++; } rc = sqlite3_reset(pStmt); if( rc==SQLITE_OK ){ nByte += nDbCol * (sizeof(const char *) + sizeof(u8) + 1); | | | 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 | *pabPK = abPK; *pnCol = nDbCol; }else{ *pazCol = 0; *pabPK = 0; *pnCol = 0; if( pzTab ) *pzTab = 0; | | | 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 | ** 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 ); | | | 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 | ** 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 */ | | | 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 | /* 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 */ | | | 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 | /* 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; | | | 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 | 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. */ | | | | | | | 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 | } } sqlite3_mutex_leave(sqlite3_db_mutex(db)); sqlite3ValueFree(pSession->pZeroBlob); /* Delete all attached table objects. And the contents of their ** associated hash-tables. */ | | > | > | 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 | 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. */ | | > | 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 | 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. */ | | | 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 | nTab = (int)strlen(zTab); sApply.azCol = (const char **)zTab; }else{ int nMinCol = 0; int i; sqlite3changeset_pk(pIter, &abPK, 0); | | | 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 | *ppTab = pTab; }else if( pTab->nCol!=nCol || memcmp(pTab->abPK, abPK, nCol) ){ rc = SQLITE_SCHEMA; break; } } | | | 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 | } /* ** Delete a changegroup object. */ SQLITE_API void sqlite3changegroup_delete(sqlite3_changegroup *pGrp){ if( pGrp ){ | | | 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 | } /* ** Destroy a rebaser object */ SQLITE_API void sqlite3rebaser_delete(sqlite3_rebaser *p){ if( p ){ | | | 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 | 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; | < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < < | 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 | }else{ fprintf(fts5yyTraceFILE,"%sInput '%s' with pending reduce %d\n", fts5yyTracePrompt,fts5yyTokenName[fts5yymajor],fts5yyact-fts5YY_MIN_REDUCE); } } #endif | | > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > | | 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 | #ifndef fts5YYNOERRORRECOVERY fts5yypParser->fts5yyerrcnt = -1; #endif } break; #endif } | < > | 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 | && 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. */ | | | | 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 | 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 | | < < < < | | 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 | 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; | | > > > | 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 | fts5ChunkIterate(p, pSeg, (void*)&sCtx, fts5PoslistOffsetsCallback); } } } } /* | | | | | < < < < < < < < < | | < < < < < | < < < < < < < < < < < < | < < < < < | | < | | > > > | > | > > > > > > > | > > > > > | > > > > > > | | > > > > | > > > > | > > > > > | > > > | 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 | 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]; | < < < < | | | < < < | 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 | } } static void fts5DoclistIterNext(Fts5DoclistIter *pIter){ u8 *p = pIter->aPoslist + pIter->nSize + pIter->nPoslist; | | | 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 | /* ** 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 */ | > | | > > > | 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 | fts5NextRowid(p2, &i2, &iRowid2); } } fts5BufferSwap(&out, p1); fts5BufferFree(&out); } | > > > > > | > > | > > > > > > > > > > > > > | > > | > > > > > > > | > > | | > > > > | < > > > > | | < > | | > > > > > > > > > > > > > > > > | | | | | | | | | | | | | < < | < < | < < < < | | < < < < | < < | > | > | | | | > > > > | > | > > > > | > > | | < < < | | | < < | < < | | < < < > < < | | | | > | < | < > | | | < | < > | < < < | | < < < | | | < | | > | < > | < > | > > | | | | | | | < < < < < | | < < < | | > | | > | > > > < < < | < | | | | < > | | > | > > > > > > > > > > > > > > > > > > > > > > > > > | > | | | | > > > | > | > > | > | > | > | 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 | 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++){ | | > > < | | 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 | 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); | | | 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 | #endif return rc; } #endif /* SQLITE_CORE */ #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */ /************** End of stmt.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 | ** 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()]. */ | | | | | 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 | ** 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 | | | 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 | #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 | > | | 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 Of Wiki 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 Of 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 Of Wiki 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 Of Chat 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 Of 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 | @ <td>Last run: %z(backoffice_last_run())</td></tr> } if( g.perm.Admin && alert_enabled() ){ stats_for_email(); } @ </table> | | | 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 | url_parse_local(zRemote, URL_OMIT_USER, &x); @ <p><a href='%h(x.canonical)'>%h(zRemote)</a> }else{ @ <p>%h(zRemote)</p> } @ </div> } | | > | 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 | } @ </pre> db_finalize(&q); }else{ style_submenu_element("Stat1","repo_stat1"); } } | | > | > | 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 | 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> } | | | 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 | 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! | | | 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 | @ <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(); | | | 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 | case RPT_BYFILE: stats_report_by_file(zUserName); break; case RPT_LASTCHNG: stats_report_last_change(); break; } | | | 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 | ** style_submenu_element() ** style_submenu_entry() ** style_submenu_checkbox() ** style_submenu_binary() ** style_submenu_multichoice() ** style_submenu_sql() ** | | | 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 | ** 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'"; | | > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | ** 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. | < < < < | | 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 | @ </div> }else if( zAd ){ @ <div class="adunit_banner"> cgi_append_content(zAd, -1); @ </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 | } @ 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] ){ | > > > | | | 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 | 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"); | > | | 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 | /* ** COMMAND: remote ** COMMAND: remote-url* ** ** Usage: %fossil remote ?SUBCOMMAND ...? ** | | | > > | < > | < < | | | > < > > | | | | < < < | < | | | | | > > > > | | | 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 | " 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); | | | 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 | @ %h(zName)</a></li> }else{ @ <li><span class="tagDsp">%h(zName)</span></li> } } @ </ul> db_finalize(&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(); } /* ** WEBPAGE: /tagtimeline ** ** Render a timeline with all check-ins that contain non-propagating ** symbolic tags. |
︙ | ︙ | |||
779 780 781 782 783 784 785 | 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 /> | | | 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 | if( zInclude ){ @ zInclude = "%h(zInclude)"<br /> } if( zExclude ){ @ zExclude = "%h(zExclude)"<br /> } @ zKey = "%h(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 | return nDefault; } /* ** COMMAND: test-terminal-size ** ** Show the size of the terminal window from which the command is launched | | | 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 | const char **argv, int *argl ){ if( argc!=1 ){ return Th_WrongNumArgs(interp, "styleFooter"); } if( Th_IsRepositoryOpen() ){ | | | 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 | ** ** --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; | > > | > | 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 | 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); | > | > > | 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 | #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") ){ | > > > | | | | | | | 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 | 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> | | | 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 | } @</td> if( !isSelectedOrCurrent ){ @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)" id='mc%d(gidx)'> }else{ @ <td class="timeline%s(zStyle)Cell%s(zExtraClass)"> } | | > > > | > | 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' ){ @ • } } 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 | ** 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 | > > | > > > > > | 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 | ** 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 */ | | | | | 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 | 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 */ | > > > | > > > > | | > | > > > > > > > > > > > > > | > > > > > > > | > > < < | 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 | 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; | | | 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 | } 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; | | | 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 | selectedRid, secondaryRid, 0); db_finalize(&q); if( zOlderButton ){ @ %z(chref("button","%s",zOlderButton))%h(zOlderButtonLabel)\ @ ↓</a> } document_emit_js(/*handles pikchrs rendered above*/); | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > | | 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)\ @ ↓</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 | 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. */ } } | > > > > | > | | 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 | 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); } | > > > > > > > > > > > > > > | | > | 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 | 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); | | | 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 | " 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); | | | 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 | } db_finalize(&q); if( cnt==0 ){ @ <p>No timewarps in this repository</p> }else{ @ </tbody></table></div> } | | | 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 | 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>"); } | | | 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 | 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); | | | 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 | 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> | > | | | | 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 | 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); | | | 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 | 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) | > | | | | 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 | 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) | > | | 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 | manifest_destroy(pTicket); } } db_finalize(&q); if( nChng ){ @ </ol> } | | | 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 | 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(); | | | 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 | ** 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); | > | | 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 | 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> | | | 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 | @ </p></blockquote> @ </div></form> @ <hr /> @ <h2>Default %s(zTitle)</h2> @ <blockquote><pre> @ %h(zDfltValue) @ </pre></blockquote> | | | 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 | @ <hr /> @ <p> @ <input type="submit" name="submit" value="Apply Changes" /> @ <input type="submit" name="setup" value="Cancel" /> @ </p> @ </div></form> db_end_transaction(0); | | | 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 | 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 | | | 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 | @ </tr> fossil_free(zAge); } db_finalize(&q); if( n ){ approxSizeName(sizeof(zSzName), zSzName, iTotalSz); @ </tbody> | | | | 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 | compute_leaves(vid, closeCode); db_prepare(&q, "%s " " AND event.objid IN leaves" " ORDER BY event.mtime DESC", timeline_query_for_tty() ); | | | 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 | break; } if( z[i]>0 && z[i]<' ' ) z[i] = ' '; } blob_append(pBlob, z, -1); } | | | 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 | @ </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(); | | | 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 | ** "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; | < > | | | > > > | > > > | > > > > > | > > > > | > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 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 | } if( eState==3 ){ style_submenu_element("Delete", "%s", url_render(pUrl,"trash","1",zENum,"1")); } db_end_transaction(0); | | | 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 | 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> | > | | 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 | @ function webmailSelectAll(){ @ var x = document.getElementsByClassName("webmailckbox"); @ for(i=0; i<x.length; i++){ @ x[i].checked = true; @ } @ } @ </script> | | | 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 | @ <td align="right" data-sortkey='%08x(csz)'>%,d(csz)</td> @ </tr> } @ </tbody></table> db_finalize(&q); style_table_sorter(); } | | > | 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 | }else{ @ <td> </td> } } @ </tbody></table> db_finalize(&q); style_table_sorter(); | | | 913 914 915 916 917 918 919 920 921 | }else{ @ <td> </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 | /* ** 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(); | > | | 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 | 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> | > | | 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 | ** 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); | > | > | > | | 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 | @ <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> | | > | | 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 | 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*/); | | | 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 | ** 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, | | | 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 | CX("P.loadPage(%!j);\n", zPageName); } CX("}catch(e){" "fossil.error(e); console.error('Exception:',e);" "}\n"); CX("});\n"/*fossil.onPageLoad()*/); style_script_end(); | | | 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 | 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> } | > | | 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 | @ 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> | | > | 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 | @ <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(); */ | | | 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 | @ "%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); | > | | 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 | } @ </tr> fossil_free(zWDisplayName); } @ </tbody></table></div> db_finalize(&q); style_table_sorter(); | | > | | 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 | 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 | | | 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 | url_enable_proxy(0); @ <pre class="xfersetup"> client_sync(syncFlags, 0, 0, 0); @ </pre> } } | | | 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 | if ( zDfltValue ){ @ <hr /> @ <h2>Default %s(zTitle)</h2> @ <blockquote><pre> @ %h(zDfltValue) @ </pre></blockquote> } | | | 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 | 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)" | > | | | 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 | } if {$x=="-threads"} { incr i set nthread [lindex $argv $i] } elseif {[string index $x 0]=="-"} { error "unknown option \"$x\"" } elseif {[info exists 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 | 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 | | | | | 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 | $(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 | | | 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 | APPTARGETS += $(BLDTARGETS) ifdef FOSSIL_BUILD_SSL APPTARGETS += openssl endif | | | | 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 | 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 | | | 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 | 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 | | | 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 | 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 | | | 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 | <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> | | | 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 | * **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`. | | | 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 | <title>Change Log</title> <a name='v2_14'></a> | | > > > > > > > > > > > > > > > > > > > > > > > | | > | > > > > > > > > > > | 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 <del> and <ins> markup in wiki. |
︙ | ︙ | |||
603 604 605 606 607 608 609 | 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] | | | 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 | [/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 | | | 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 | * 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. | | | 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** → 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** → 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** → 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** → 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** → Sends a new chat message to the server. * **/chat-delete** → 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** → 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 | 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 | | < | 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 | <title>Contributing To Fossil</title> | | | | | | > > > | > > > > > | > > > > > > > > > | | | | | | | | | | | | < | > > > > | > | > > > > | > > > > > | > > > > > > > > > > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 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 | 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 | | | 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 | <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> | | > > > > > > > > > > > > | 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 | 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. | | | 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 | 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> | | | | 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 | 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 | | | 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 <th1>...</th1> 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 | <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. | | | 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 | visiting their repository home page, set to [an HTML-formatted embedded doc page][hfed] via Admin → Configuration → Index Page, with this content: <script src="/doc/trunk/bad.js"></script> That script can then do anything allowed in JavaScript to *any other* | | | 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 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 | 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 | | | 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 | <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> | > > > | > > > | > > > > > > | 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 | `--vfs VFSNAME`: Load the named VFS into SQLite. Environment Variables --------------------- The location of the user's account-wide [configuration database][configdb] | | | 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 | 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 | | | 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 → "`$HOME/.fossil`" * Windows → "`%LOCALAPPDATA%/_fossil`" * [XDG-unix][xdg] → "`$HOME/.config/fossil.db`" |
︙ | ︙ |
Changes to www/fileedit-page.md.
︙ | ︙ | |||
135 136 137 138 139 140 141 | 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. | | | 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 | <title>Fossil Forums</title> <h2>Introduction</h2> | | > > > > | < < | > > > | > > > > | > > > | > > | > > > | > > > > > | < > > | | | < < < < < | | | < < < < | < | < < < < < < | < < < < < < < | < < < < < < | < < < < < < < < < < | < < < < < | < < < < < < < | < < < < < < < < < < < < < < < < < | 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 | 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> | | | 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 ↓</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 ↓</a></td> |
︙ | ︙ | |||
103 104 105 106 107 108 109 | <h3 id="features">2.1 Featureful</h3> Git provides file versioning services only, whereas Fossil adds an integrated [./wikitheory.wiki | wiki], [./bugtheory.wiki | ticketing & bug tracking], [./embeddeddoc.wiki | embedded documentation], | | > | 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 & 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 | 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 — a useful | | | 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 — 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 | 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 | | | 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 | 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 | | | 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 | 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 | | | 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 | 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 | | > > > | 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 | # Git to Fossil Translation Guide ## Introduction | > | < > > | > | > | < < | > > > > > < < < | > | 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 | git checkout some-branch …is best given as: fossil update some-branch | | > > > > > | > > > > | | > > > > > | > > > | > > > | > | | | | > > | < | > < > | 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 | 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” | | > | 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 | 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 | | | 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 | 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 | | | | 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 | 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 | | | 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 | ## 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, | | | 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 | # 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 | | | 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 | 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 | | | | | | | | | | | | 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 | 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 | | | 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 | 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 | | | 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 | <title>Home</title> <h3>What Is Fossil?</h3> | | | | | | | | | > > > > > > > > | < | | | < < < < < < < | | | | | | 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 & 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 & 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 | # 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 | | > | > | | | 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 | 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 | | | > | 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 | 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. | | | > > | 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 | <a id="considerations"></a> # Technical Problems and Considerations A random list of considerations which need to be made and potential problem areas... | | | | | | | | | < < < < < < < < | | | | > > > | < > > > > | < < | < > | | | < | < < | < < < < < < < < < < < < < < < < < < < < < | 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 “payload” area 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 | 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. | | | 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> >builtin_data.h </pre></blockquote> |
︙ | ︙ |
Changes to www/mirrortogithub.md.
︙ | ︙ | |||
105 106 107 108 109 110 111 | 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" | | > | 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 | 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} | < > | 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 — Administering User</a></li> <li><a href="caps/ref.html">Capability Reference — User</a></li> <li><a href="cgi.wiki"><b>CGI Script Configuration Options</b></a></li> <li><a href="serverext.wiki">CGI Scripts — 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 — How</a></li> <li><a href="changes.wiki">Changelog — 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 — Check-in</a></li> <li><a href="../test/release-checklist.wiki">Checklist — 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 — 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 — Hash Policy:</a></li> <li><a href="contribute.wiki">Code or Documentation To The Fossil Project — Contributing</a></li> <li><a href="style.wiki">Code Style Guidelines — Source</a></li> <li><a href="../../../help">Commands and Webpages — Lists of</a></li> <li><a href="build.wiki"><b>Compiling and Installing Fossil</b></a></li> <li><a href="concepts.wiki">Concepts — Fossil Core</a></li> <li><a href="cgi.wiki">Configuration Options — CGI Script</a></li> <li><a href="server/">Configure A Fossil Server — How To</a></li> <li><a href="rebaseharm.md">Considered Harmful — Rebase</a></li> <li><a href="shunning.wiki">Content From Fossil — Shunning: Deleting</a></li> <li><a href="defcsp.md">Content Security Policy — 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 — Benefits Of Version</a></li> <li><a href="concepts.wiki">Core Concepts — Fossil</a></li> <li><a href="newrepo.wiki">Create A New Fossil Repository — 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 — Administering User</a></li> <li><a href="caps/ref.html">Capability Reference — User</a></li> <li><a href="cgi.wiki"><b>CGI Script Configuration Options</b></a></li> <li><a href="serverext.wiki">CGI Scripts — 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 — How</a></li> <li><a href="changes.wiki">Changelog — Fossil</a></li> <li><a href="chat.md">Chat — 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 — Check-in</a></li> <li><a href="../test/release-checklist.wiki">Checklist — 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 — 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 — Hash Policy:</a></li> <li><a href="chroot.md">Chroot Jail — Server</a></li> <li><a href="contribute.wiki">Code or Documentation To The Fossil Project — Contributing</a></li> <li><a href="style.wiki">Code Style Guidelines — Source</a></li> <li><a href="../../../help">Commands and Webpages — Lists of</a></li> <li><a href="build.wiki"><b>Compiling and Installing Fossil</b></a></li> <li><a href="concepts.wiki">Concepts — Fossil Core</a></li> <li><a href="cgi.wiki">Configuration Options — CGI Script</a></li> <li><a href="server/">Configure A Fossil Server — How To</a></li> <li><a href="rebaseharm.md">Considered Harmful — Rebase</a></li> <li><a href="contact.md">Contact Information — Developer</a></li> <li><a href="shunning.wiki">Content From Fossil — Shunning: Deleting</a></li> <li><a href="defcsp.md">Content Security Policy — 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 — Benefits Of Version</a></li> <li><a href="concepts.wiki">Core Concepts — Fossil</a></li> <li><a href="newrepo.wiki">Create A New Fossil Repository — 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 — Shunning:</a></li> <li><a href="private.wiki">Deleting Private Branches — Creating, Syncing, and</a></li> <li><a href="delta_encoder_algorithm.wiki">Delta Encoding Algorithm — Fossil</a></li> <li><a href="delta_format.wiki">Delta Format — Fossil</a></li> <li><a href="tech_overview.wiki">Design And Implementation Of Fossil — A Technical Overview Of The</a></li> <li><a href="theory1.wiki">Design Of The Fossil DVCS — Thoughts On The</a></li> <li><a href="hacker-howto.wiki">Developers Guide — 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 — Embedded Project</a></li> <li><a href="contribute.wiki">Documentation To The Fossil Project — Contributing Code or</a></li> <li><a href="aboutdownload.wiki">Download Page Works — How The</a></li> <li><a href="theory1.wiki">DVCS — Thoughts On The Design Of The Fossil</a></li> <li><a href="quotes.wiki">DVCSes in General — 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 — Shunning:</a></li> <li><a href="private.wiki">Deleting Private Branches — Creating, Syncing, and</a></li> <li><a href="delta_encoder_algorithm.wiki">Delta Encoding Algorithm — Fossil</a></li> <li><a href="delta_format.wiki">Delta Format — Fossil</a></li> <li><a href="tech_overview.wiki">Design And Implementation Of Fossil — A Technical Overview Of The</a></li> <li><a href="theory1.wiki">Design Of The Fossil DVCS — 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 — Fossil</a></li> <li><a href="pikchr.md">Diagram Language — 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 — Embedded Project</a></li> <li><a href="contribute.wiki">Documentation To The Fossil Project — Contributing Code or</a></li> <li><a href="aboutdownload.wiki">Download Page Works — How The</a></li> <li><a href="theory1.wiki">DVCS — Thoughts On The Design Of The Fossil</a></li> <li><a href="quotes.wiki">DVCSes in General — 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 — Fossil File</a></li> <li><a href="image-format-vs-repo-size.md">Format vs Fossil Repo Size — Image</a></li> <li><a href="../../../md_rules">Formatting Rules — Markdown</a></li> <li><a href="../../../wiki_rules">Formatting Rules — Wiki</a></li> <li><a href="forum.wiki">Forums — 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 — Fossil File</a></li> <li><a href="image-format-vs-repo-size.md">Format vs Fossil Repo Size — Image</a></li> <li><a href="../../../md_rules">Formatting Rules — Markdown</a></li> <li><a href="../../../wiki_rules">Formatting Rules — Wiki</a></li> <li><a href="forum.wiki">Forums — 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 — Hacker</a></li> <li><a href="fossil-from-msvc.wiki">IDE — Integrating Fossil in the Microsoft Express 2010</a></li> <li><a href="hashes.md">Identification — 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 — 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 — 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 — Fossil Repository</a></li> <li><a href="webui.wiki">Interface — 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 — Use of</a></li> <li><a href="th1.md">Language — The TH1 Scripting</a></li> <li><a href="copyright-release.html">License Agreement — Contributor</a></li> <li><a href="mirrorlimitations.md"><b>Limitations On Git Mirrors</b></a></li> <li><a href="interwiki.md">Links — Interwiki</a></li> <li><a href="../../../help"><b>Lists of Commands and Webpages</b></a></li> <li><a href="password.wiki">Management And Authentication — Password</a></li> <li><a href="../../../sitemap">Map — Site</a></li> <li><a href="../../../md_rules"><b>Markdown Formatting Rules</b></a></li> <li><a href="backoffice.md">mechanism of Fossil — The Backoffice</a></li> <li><a href="branching.wiki">Merging, and Tagging — Branching, Forking,</a></li> <li><a href="fossil-from-msvc.wiki">Microsoft Express 2010 IDE — Integrating Fossil in the</a></li> <li><a href="fiveminutes.wiki">Minutes as a Single User — Up and Running in 5</a></li> <li><a href="mirrortogithub.md">Mirror A Fossil Repository On GitHub — 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 — Hacker</a></li> <li><a href="fossil-from-msvc.wiki">IDE — Integrating Fossil in the Microsoft Express 2010</a></li> <li><a href="hashes.md">Identification — 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 — 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 — Developer Contact</a></li> <li><a href="build.wiki">Installing Fossil — 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 — Fossil Repository</a></li> <li><a href="webui.wiki">Interface — 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 — Server Chroot</a></li> <li><a href="javascript.md">JavaScript in Fossil — Use of</a></li> <li><a href="pikchr.md">Language — The Pikchr Diagram</a></li> <li><a href="th1.md">Language — The TH1 Scripting</a></li> <li><a href="copyright-release.html">License Agreement — Contributor</a></li> <li><a href="mirrorlimitations.md"><b>Limitations On Git Mirrors</b></a></li> <li><a href="interwiki.md">Links — Interwiki</a></li> <li><a href="../../../help"><b>Lists of Commands and Webpages</b></a></li> <li><a href="loadmgmt.md">Load — Managing Server</a></li> <li><a href="password.wiki">Management And Authentication — Password</a></li> <li><a href="loadmgmt.md"><b>Managing Server Load</b></a></li> <li><a href="../../../sitemap">Map — Site</a></li> <li><a href="../../../md_rules"><b>Markdown Formatting Rules</b></a></li> <li><a href="backoffice.md">mechanism of Fossil — The Backoffice</a></li> <li><a href="branching.wiki">Merging, and Tagging — Branching, Forking,</a></li> <li><a href="fossil-from-msvc.wiki">Microsoft Express 2010 IDE — Integrating Fossil in the</a></li> <li><a href="fiveminutes.wiki">Minutes as a Single User — Up and Running in 5</a></li> <li><a href="mirrortogithub.md">Mirror A Fossil Repository On GitHub — 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 — The fileedit</a></li> <li><a href="aboutdownload.wiki">Page Works — How The Download</a></li> <li><a href="customskin.md">Pages — 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 — File Name Glob</a></li> <li><a href="quotes.wiki">People Are Saying About Fossil, Git, and DVCSes in General — Quotes: What</a></li> <li><a href="stats.wiki"><b>Performance Statistics</b></a></li> <li><a href="defcsp.md">Policy — The Default Content Security</a></li> <li><a href="hashpolicy.wiki">Policy: Choosing Between SHA1 and SHA3-256 — Hash</a></li> <li><a href="grep.md">POSIX grep — 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 — Creating, Syncing, and Deleting</a></li> <li><a href="makefile.wiki">Process — 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 — The fileedit</a></li> <li><a href="aboutdownload.wiki">Page Works — How The Download</a></li> <li><a href="customskin.md">Pages — 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 — File Name Glob</a></li> <li><a href="quotes.wiki">People Are Saying About Fossil, Git, and DVCSes in General — Quotes: What</a></li> <li><a href="stats.wiki"><b>Performance Statistics</b></a></li> <li><a href="pikchr.md">Pikchr Diagram Language — The</a></li> <li><a href="defcsp.md">Policy — The Default Content Security</a></li> <li><a href="hashpolicy.wiki">Policy: Choosing Between SHA1 and SHA3-256 — Hash</a></li> <li><a href="grep.md">POSIX grep — 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 — Creating, Syncing, and Deleting</a></li> <li><a href="makefile.wiki">Process — 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 — CGI</a></li> <li><a href="th1.md">Scripting Language — The TH1</a></li> <li><a href="serverext.wiki">Scripts — Adding Extensions To A Fossil Server Using CGI</a></li> <li><a href="defcsp.md">Security Policy — The Default Content</a></li> <li><a href="selfcheck.wiki">Self Checks — Fossil Repository Integrity</a></li> <li><a href="selfhost.wiki">Self Hosting Repositories — Fossil</a></li> <li><a href="server/">Server — How To Configure A Fossil</a></li> <li><a href="serverext.wiki">Server Extensions — CGI</a></li> <li><a href="serverext.wiki">Server Using CGI Scripts — Adding Extensions To A Fossil</a></li> <li><a href="settings.wiki">Settings — Fossil</a></li> <li><a href="caps/admin-v-setup.md">Setup and Admin Users — Differences Between</a></li> <li><a href="hashpolicy.wiki">SHA1 and SHA3-256 — Hash Policy: Choosing Between</a></li> <li><a href="hashpolicy.wiki">SHA3-256 — 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 — 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 — CGI</a></li> <li><a href="th1.md">Scripting Language — The TH1</a></li> <li><a href="serverext.wiki">Scripts — Adding Extensions To A Fossil Server Using CGI</a></li> <li><a href="defcsp.md">Security Policy — The Default Content</a></li> <li><a href="selfcheck.wiki">Self Checks — Fossil Repository Integrity</a></li> <li><a href="selfhost.wiki">Self Hosting Repositories — Fossil</a></li> <li><a href="server/">Server — 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 — CGI</a></li> <li><a href="loadmgmt.md">Server Load — Managing</a></li> <li><a href="serverext.wiki">Server Using CGI Scripts — Adding Extensions To A Fossil</a></li> <li><a href="settings.wiki">Settings — Fossil</a></li> <li><a href="caps/admin-v-setup.md">Setup and Admin Users — Differences Between</a></li> <li><a href="hashpolicy.wiki">SHA1 and SHA3-256 — Hash Policy: Choosing Between</a></li> <li><a href="hashpolicy.wiki">SHA3-256 — 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 — 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 — 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 — Customizing The</a></li> <li><a href="tickets.wiki">Ticket System — The Fossil</a></li> <li><a href="customgraph.md">Timeline Graph — Theming: Customizing the</a></li> <li><a href="css-tricks.md">Tips and Tricks — Fossil CSS</a></li> <li><a href="hints.wiki">Tips And Usage Hints — Fossil</a></li> <li><a href="bugtheory.wiki">Tracking In Fossil — Bug</a></li> <li><a href="gitusers.md">Translation Guide — Git to Fossil</a></li> <li><a href="css-tricks.md">Tricks — 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 — 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 — 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 — Up and Running in 5 Minutes as a Single</a></li> <li><a href="caps/">User Capabilities — 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 — Differences Between Setup and Admin</a></li> <li><a href="serverext.wiki">Using CGI Scripts — 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 — Environment</a></li> <li><a href="whyusefossil.wiki">Version Control — Benefits Of</a></li> <li><a href="checkin_names.wiki">Version Names — Check-in And</a></li> <li><a href="fossil-v-git.wiki">Versus Git — Fossil</a></li> <li><a href="image-format-vs-repo-size.md">vs Fossil Repo Size — Image Format</a></li> <li><a href="grep.md">vs POSIX grep — Fossil grep</a></li> <li><a href="webui.wiki">Web Interface — The Fossil</a></li> <li><a href="customskin.md">Web Pages — Theming: Customizing The Appearance of</a></li> <li><a href="webpage-ex.md"><b>Webpage Examples</b></a></li> <li><a href="../../../help">Webpages — Lists of Commands and</a></li> <li><a href="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General — 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 — Check-Out</a></li> <li><a href="aboutdownload.wiki">Works — How The Download Page</a></li> <li><a href="aboutcgi.wiki">Works In Fossil — How CGI</a></li> <li><a href="whyusefossil.wiki">You Should Use Fossil — 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 — 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 — Customizing The</a></li> <li><a href="tickets.wiki">Ticket System — The Fossil</a></li> <li><a href="customgraph.md">Timeline Graph — Theming: Customizing the</a></li> <li><a href="css-tricks.md">Tips and Tricks — Fossil CSS</a></li> <li><a href="hints.wiki">Tips And Usage Hints — Fossil</a></li> <li><a href="bugtheory.wiki">Tracking In Fossil — Bug</a></li> <li><a href="gitusers.md">Translation Guide — Git to Fossil</a></li> <li><a href="css-tricks.md">Tricks — 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 — 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 — Checkout vs</a></li> <li><a href="hints.wiki">Usage Hints — 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 — Up and Running in 5 Minutes as a Single</a></li> <li><a href="caps/">User Capabilities — 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 — Differences Between Setup and Admin</a></li> <li><a href="serverext.wiki">Using CGI Scripts — 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 — Environment</a></li> <li><a href="whyusefossil.wiki">Version Control — Benefits Of</a></li> <li><a href="checkin_names.wiki">Version Names — Check-in And</a></li> <li><a href="fossil-v-git.wiki">Versus Git — Fossil</a></li> <li><a href="image-format-vs-repo-size.md">vs Fossil Repo Size — Image Format</a></li> <li><a href="grep.md">vs POSIX grep — Fossil grep</a></li> <li><a href="co-vs-up.md">vs Update — Checkout</a></li> <li><a href="webui.wiki">Web Interface — The Fossil</a></li> <li><a href="customskin.md">Web Pages — Theming: Customizing The Appearance of</a></li> <li><a href="webpage-ex.md"><b>Webpage Examples</b></a></li> <li><a href="../../../help">Webpages — Lists of Commands and</a></li> <li><a href="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General — 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 — Check-Out</a></li> <li><a href="aboutdownload.wiki">Works — How The Download Page</a></li> <li><a href="aboutcgi.wiki">Works In Fossil — How CGI</a></li> <li><a href="whyusefossil.wiki">You Should Use Fossil — Why</a></li> </ul></div> |
Changes to www/quickstart.wiki.
1 2 3 4 5 6 7 8 | <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> | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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 | 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 | | | 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 | # 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]. | | | 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 | 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 | | | 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 | ## <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 | | | 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 | 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], | | > | | 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 | 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 | | | 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 | 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] | | | 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 | 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 | | | 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 | <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> | | | 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 | ////////////////////////////////////////////////////////////////////////// { url: "timeline", desc: "Simple timeline of most recent check-ins. Verify that all submenus work." }, { | | | 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 | 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' | | | | > > > > | | | | | | 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> → 100 most recent check-ins. * <a target='_blank' class='exbtn' href='$ROOT/finfo?name=src/file.c'>(Example)</a> → 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> → 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> → 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> → 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> → 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> → 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> → 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> → All check-ins of the "svn-import" branch only. * <a target='_blank' class='exbtn' href='$ROOT/timeline?n1=100&y=ci&ubg'>(Example)</a> → 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> → 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 | 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 | | | 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 | 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 | | > > > > > > > > > | 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 | <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> | < | | 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 | <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> | > | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | > | > | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | 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> → 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> → (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> → 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> → 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> → 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> → Create a new check-out from a repository on the local machine. <li><p><b>update</b> → Modify an existing check-out so that it is derived from a different version of the same project. <li><p><b>commit</b> → 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> → 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> → 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> → 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> → Do both a "push" and a "pull" at the same time. <li><p><b>add</b> → 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> → 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> → 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> → 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> |