Fossil SCM

merge [trunk]

bch 2020-05-12 02:49 UTC NULLSeparated merge
Commit 19dfb072791bf0cb83d9b10d7399f12f5d813144e80c98d1e81b58f214f7e758
95 files changed +2 -2 +1 -1 +168 -68 +1 -1 +6 +14 -1 +144 -75 +10 -2 +1 -1 +11 -10 +38 -14 +38 -14 +17 -31 +1 +12 +122 -61 +19 +27 -2 +43 -6 +101 -1 +15 -11 +172 -19 +5 -1 +3 -1 +228 -121 +220 -203 +1 -4 +62 -14 +37 -6 +24 +4 -2 +60 -13 +7 +1 -1 +57 -3 +234 -39 +84 -14 +19 -1 +16 -1 +45 -13 +5 -6 +280 -102 +2 -2 +55 +1673 -736 +107 -31 +2 -1 +1 -13 +1 -1 +1 -1 +9 +51 -5 +32 -6 +51 -10 +18 -14 +15 +46 -23 +3 -21 +153 -56 +1 -1 +2 +1 -1 +72 +16 -4 +25 -1 +26 -1 +21 -1 +1 -1 +1 -1 +43 -4 +1 -2 +32 -44 +3 -1 +8 -8 +143 +72 +21 -72 +38 -16 +6 -3 +11 -3 +15 -2 +4 -4 +12 -10 +4 -4 +26 -13 +7 +51 -10 +1 +6 -3 +3 -2
~ auto.def ~ skins/default/css.txt ~ src/alerts.c ~ src/allrepo.c ~ src/backlink.c ~ src/branch.c ~ src/browse.c ~ src/builtin.c ~ src/capabilities.c ~ src/cgi.c ~ src/checkin.c ~ src/checkin.c ~ src/comformat.c ~ src/configure.c ~ src/content.c ~ src/db.c ~ src/default_css.txt ~ src/diff.c ~ src/etag.c ~ src/file.c ~ src/finfo.c ~ src/forum.c ~ src/fshell.c ~ src/graph.c ~ src/http_ssl.c ~ src/info.c ~ src/json_branch.c ~ src/login.c ~ src/main.c ~ src/main.mk ~ src/makemake.tcl ~ src/manifest.c ~ src/merge.c ~ src/merge3.c ~ src/mkversion.c ~ src/name.c ~ src/rebuild.c ~ src/schema.c ~ src/security_audit.c ~ src/setup.c ~ src/setupuser.c ~ src/shell.c ~ src/shun.c ~ src/skins.c ~ src/sqlite3.c ~ src/sqlite3.h ~ src/style.c ~ src/sync.c ~ src/tag.c ~ src/tar.c ~ src/terminal.c ~ src/timeline.c ~ src/tkt.c ~ src/unversioned.c ~ src/url.c ~ src/util.c ~ src/wiki.c ~ src/wikiformat.c ~ src/xfer.c ~ src/zip.c ~ test/subdir with spaces/filename with spaces.txt ~ test/tester.tcl ~ tools/email-monitor.tcl ~ tools/encode_math.sh ~ tools/fossil-autocomplete.bash ~ tools/fossil-diff-log ~ tools/fossil-stress.tcl ~ tools/fossil_chat.tcl ~ win/Makefile.dmc ~ win/Makefile.mingw ~ win/Makefile.mingw.mistachkin ~ win/Makefile.msc ~ www/antibot.wiki ~ www/build.wiki ~ www/changes.wiki ~ www/css-tricks.md ~ www/env-opts.md ~ www/faq.wiki ~ www/fossil-v-git.wiki ~ www/gitusers.md ~ www/history.md ~ www/index.wiki ~ www/mirrorlimitations.md ~ www/mkindex.tcl ~ www/permutedindex.html ~ www/private.wiki ~ www/qandc.wiki ~ www/quickstart.wiki ~ www/rebaseharm.md ~ www/ssl.wiki ~ www/sync.wiki ~ www/tech_overview.wiki ~ www/userlinks.wiki ~ www/whyusefossil.wiki ~ www/wikitheory.wiki
+2 -2
--- auto.def
+++ auto.def
@@ -273,11 +273,11 @@
273273
274274
# Check for libraries that need to be sorted out early
275275
cc-check-function-in-lib iconv iconv
276276
277277
# Helper for OpenSSL checking
278
-proc check-for-openssl {msg {cflags {}} {libs {-lssl -lcrypto}}} {
278
+proc check-for-openssl {msg {cflags {}} {libs {-lssl -lcrypto -lpthread}}} {
279279
msg-checking "Checking for $msg..."
280280
set rc 0
281281
if {[is_mingw]} {
282282
lappend libs -lgdi32 -lwsock32 -lcrypt32
283283
}
@@ -351,11 +351,11 @@
351351
user-error "The OpenSSL in source tree directory does not exist"
352352
}
353353
set msg "ssl in $ssldir"
354354
set cflags "-I$ssldir/include"
355355
set ldflags "-L$ssldir"
356
- set ssllibs "$ssldir/libssl.a $ssldir/libcrypto.a"
356
+ set ssllibs "$ssldir/libssl.a $ssldir/libcrypto.a -lpthread"
357357
set found [check-for-openssl "ssl in source tree" "$cflags $ldflags" $ssllibs]
358358
} else {
359359
if {$ssldirs in {auto ""}} {
360360
catch {
361361
set cflags [exec pkg-config openssl --cflags-only-I]
362362
--- auto.def
+++ auto.def
@@ -273,11 +273,11 @@
273
274 # Check for libraries that need to be sorted out early
275 cc-check-function-in-lib iconv iconv
276
277 # Helper for OpenSSL checking
278 proc check-for-openssl {msg {cflags {}} {libs {-lssl -lcrypto}}} {
279 msg-checking "Checking for $msg..."
280 set rc 0
281 if {[is_mingw]} {
282 lappend libs -lgdi32 -lwsock32 -lcrypt32
283 }
@@ -351,11 +351,11 @@
351 user-error "The OpenSSL in source tree directory does not exist"
352 }
353 set msg "ssl in $ssldir"
354 set cflags "-I$ssldir/include"
355 set ldflags "-L$ssldir"
356 set ssllibs "$ssldir/libssl.a $ssldir/libcrypto.a"
357 set found [check-for-openssl "ssl in source tree" "$cflags $ldflags" $ssllibs]
358 } else {
359 if {$ssldirs in {auto ""}} {
360 catch {
361 set cflags [exec pkg-config openssl --cflags-only-I]
362
--- auto.def
+++ auto.def
@@ -273,11 +273,11 @@
273
274 # Check for libraries that need to be sorted out early
275 cc-check-function-in-lib iconv iconv
276
277 # Helper for OpenSSL checking
278 proc check-for-openssl {msg {cflags {}} {libs {-lssl -lcrypto -lpthread}}} {
279 msg-checking "Checking for $msg..."
280 set rc 0
281 if {[is_mingw]} {
282 lappend libs -lgdi32 -lwsock32 -lcrypt32
283 }
@@ -351,11 +351,11 @@
351 user-error "The OpenSSL in source tree directory does not exist"
352 }
353 set msg "ssl in $ssldir"
354 set cflags "-I$ssldir/include"
355 set ldflags "-L$ssldir"
356 set ssllibs "$ssldir/libssl.a $ssldir/libcrypto.a -lpthread"
357 set found [check-for-openssl "ssl in source tree" "$cflags $ldflags" $ssllibs]
358 } else {
359 if {$ssldirs in {auto ""}} {
360 catch {
361 set cflags [exec pkg-config openssl --cflags-only-I]
362
--- skins/default/css.txt
+++ skins/default/css.txt
@@ -145,11 +145,11 @@
145145
border-radius: 5px;
146146
}
147147
.content blockquote {
148148
padding: 0 15px;
149149
}
150
-div.forumHierRoot blockquote, div.forumHier blockquote, div.forumEdit blockquote {
150
+div.forumHierRoot blockquote, div.forumHier blockquote, div.forumEdit blockquote, div.forumTime blockquote, div.forumTimeline blockquote {
151151
background-color: rgba(65, 131, 196, 0.1);
152152
border-left: 3px solid #254769;
153153
padding: .1em 1em;
154154
}
155155
156156
--- skins/default/css.txt
+++ skins/default/css.txt
@@ -145,11 +145,11 @@
145 border-radius: 5px;
146 }
147 .content blockquote {
148 padding: 0 15px;
149 }
150 div.forumHierRoot blockquote, div.forumHier blockquote, div.forumEdit blockquote {
151 background-color: rgba(65, 131, 196, 0.1);
152 border-left: 3px solid #254769;
153 padding: .1em 1em;
154 }
155
156
--- skins/default/css.txt
+++ skins/default/css.txt
@@ -145,11 +145,11 @@
145 border-radius: 5px;
146 }
147 .content blockquote {
148 padding: 0 15px;
149 }
150 div.forumHierRoot blockquote, div.forumHier blockquote, div.forumEdit blockquote, div.forumTime blockquote, div.forumTimeline blockquote {
151 background-color: rgba(65, 131, 196, 0.1);
152 border-left: 3px solid #254769;
153 padding: .1em 1em;
154 }
155
156
+168 -68
--- src/alerts.c
+++ src/alerts.c
@@ -1208,10 +1208,19 @@
12081208
int i, j, n;
12091209
char c;
12101210
12111211
*peErr = 0;
12121212
*pzErr = 0;
1213
+
1214
+ /* Verify the captcha first */
1215
+ if( needCaptcha ){
1216
+ if( !captcha_is_correct(1) ){
1217
+ *peErr = 2;
1218
+ *pzErr = mprintf("incorrect security code");
1219
+ return 0;
1220
+ }
1221
+ }
12131222
12141223
/* Check the validity of the email address.
12151224
**
12161225
** (1) Exactly one '@' character.
12171226
** (2) No other characters besides [a-zA-Z0-9._+-]
@@ -1219,11 +1228,15 @@
12191228
** The local part is currently more restrictive than RFC 5322 allows:
12201229
** https://stackoverflow.com/a/2049510/142454 We will expand this as
12211230
** necessary.
12221231
*/
12231232
zEAddr = P("e");
1224
- if( zEAddr==0 ) return 0;
1233
+ if( zEAddr==0 ){
1234
+ *peErr = 1;
1235
+ *pzErr = mprintf("required");
1236
+ return 0;
1237
+ }
12251238
for(i=j=n=0; (c = zEAddr[i])!=0; i++){
12261239
if( c=='@' ){
12271240
n = i;
12281241
j++;
12291242
continue;
@@ -1249,14 +1262,13 @@
12491262
*peErr = 1;
12501263
*pzErr = mprintf("email domain too short");
12511264
return 0;
12521265
}
12531266
1254
- /* Verify the captcha */
1255
- if( needCaptcha && !captcha_is_correct(1) ){
1256
- *peErr = 2;
1257
- *pzErr = mprintf("incorrect security code");
1267
+ if( authorized_subscription_email(zEAddr)==0 ){
1268
+ *peErr = 1;
1269
+ *pzErr = mprintf("not an authorized email address");
12581270
return 0;
12591271
}
12601272
12611273
/* Check to make sure the email address is available for reuse */
12621274
if( db_exists("SELECT 1 FROM subscriber WHERE semail=%Q", zEAddr) ){
@@ -1347,10 +1359,14 @@
13471359
/* Everybody else jumps to the page to administer their own
13481360
** account only. */
13491361
cgi_redirectf("%R/alerts");
13501362
return;
13511363
}
1364
+ }
1365
+ if( !g.perm.Admin && !db_get_boolean("anon-subscribe",1) ){
1366
+ register_page();
1367
+ return;
13521368
}
13531369
alert_submenu_common();
13541370
needCaptcha = !login_is_individual();
13551371
if( P("submit")
13561372
&& cgi_csrf_safe(1)
@@ -1368,10 +1384,11 @@
13681384
if( PB("sa") ) ssub[nsub++] = 'a';
13691385
if( g.perm.Read && PB("sc") ) ssub[nsub++] = 'c';
13701386
if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f';
13711387
if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't';
13721388
if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w';
1389
+ if( g.perm.RdForum && PB("sx") ) ssub[nsub++] = 'x';
13731390
ssub[nsub] = 0;
13741391
db_multi_exec(
13751392
"INSERT INTO subscriber(semail,suname,"
13761393
" sverified,sdonotcall,sdigest,ssub,sctime,mtime,smip)"
13771394
"VALUES(%Q,%Q,%d,0,%d,%Q,now(),now(),%Q)",
@@ -1388,11 +1405,15 @@
13881405
id);
13891406
if( !needCaptcha ){
13901407
/* The new subscription has been added on behalf of a logged-in user.
13911408
** No verification is required. Jump immediately to /alerts page.
13921409
*/
1393
- cgi_redirectf("%R/alerts/%s", zCode);
1410
+ if( g.perm.Admin ){
1411
+ cgi_redirectf("%R/alerts/%.32s", zCode);
1412
+ }else{
1413
+ cgi_redirectf("%R/alerts");
1414
+ }
13941415
return;
13951416
}else{
13961417
/* We need to send a verification email */
13971418
Blob hdr, body;
13981419
AlertSender *pSender = alert_sender_new(0,0);
@@ -1410,11 +1431,11 @@
14101431
@ <blockquote><pre>
14111432
@ %h(pSender->zErr)
14121433
@ </pre></blockquote>
14131434
}else{
14141435
@ <p>An email has been sent to "%h(zEAddr)". That email contains a
1415
- @ hyperlink that you must click on in order to activate your
1436
+ @ hyperlink that you must click to activate your
14161437
@ subscription.</p>
14171438
}
14181439
alert_sender_free(pSender);
14191440
style_footer();
14201441
}
@@ -1442,16 +1463,22 @@
14421463
if( eErr==1 ){
14431464
@ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
14441465
}
14451466
@ </tr>
14461467
if( needCaptcha ){
1447
- uSeed = captcha_seed();
1468
+ const char *zInit = "";
1469
+ if( P("captchaseed")!=0 && eErr!=2 ){
1470
+ uSeed = strtoul(P("captchaseed"),0,10);
1471
+ zInit = P("captcha");
1472
+ }else{
1473
+ uSeed = captcha_seed();
1474
+ }
14481475
zDecoded = captcha_decode(uSeed);
14491476
zCaptcha = captcha_render(zDecoded);
14501477
@ <tr>
14511478
@ <td class="form_label">Security Code:</td>
1452
- @ <td><input type="text" name="captcha" value="" size="30">
1479
+ @ <td><input type="text" name="captcha" value="%h(zInit)" size="30">
14531480
captcha_speakit_button(uSeed, "Speak the code");
14541481
@ <input type="hidden" name="captchaseed" value="%u(uSeed)"></td>
14551482
@ </tr>
14561483
if( eErr==2 ){
14571484
@ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
@@ -1478,10 +1505,12 @@
14781505
@ Check-ins</label><br>
14791506
}
14801507
if( g.perm.RdForum ){
14811508
@ <label><input type="checkbox" name="sf" %s(PCK("sf"))> \
14821509
@ Forum Posts</label><br>
1510
+ @ <label><input type="checkbox" name="sx" %s(PCK("sx"))> \
1511
+ @ Forum Edits</label><br>
14831512
}
14841513
if( g.perm.RdTkt ){
14851514
@ <label><input type="checkbox" name="st" %s(PCK("st"))> \
14861515
@ Ticket changes</label><br>
14871516
}
@@ -1531,21 +1560,20 @@
15311560
/*
15321561
** Either shutdown or completely delete a subscription entry given
15331562
** by the hex value zName. Then paint a webpage that explains that
15341563
** the entry has been removed.
15351564
*/
1536
-static void alert_unsubscribe(const char *zName){
1565
+static void alert_unsubscribe(int sid){
15371566
char *zEmail;
15381567
zEmail = db_text(0, "SELECT semail FROM subscriber"
1539
- " WHERE subscriberCode=hextoblob(%Q)", zName);
1568
+ " WHERE subscriberId=%d", sid);
15401569
if( zEmail==0 ){
15411570
style_header("Unsubscribe Fail");
15421571
@ <p>Unable to locate a subscriber with the requested key</p>
15431572
}else{
15441573
db_multi_exec(
1545
- "DELETE FROM subscriber WHERE subscriberCode=hextoblob(%Q)",
1546
- zName
1574
+ "DELETE FROM subscriber WHERE subscriberId=%d", sid
15471575
);
15481576
style_header("Unsubscribed");
15491577
@ <p>The "%h(zEmail)" email address has been delisted.
15501578
@ All traces of that email address have been removed</p>
15511579
}
@@ -1556,48 +1584,83 @@
15561584
/*
15571585
** WEBPAGE: alerts
15581586
**
15591587
** Edit email alert and notification settings.
15601588
**
1561
-** The subscriber is identified in either of two ways:
1589
+** The subscriber is identified in several ways:
1590
+**
1591
+** (1) The name= query parameter contains the complete subscriberCode.
1592
+** This only happens when the user receives a verification
1593
+** email and clicks on the link in the email. When a
1594
+** compilete subscriberCode is seen on the name= query parameter,
1595
+** that constitutes verification of the email address.
15621596
**
1563
-** (1) The name= query parameter contains the subscriberCode.
1597
+** (2) The sid= query parameter contains an integer subscriberId.
1598
+** This only works for the administrator. It allows the
1599
+** administrator to edit any subscription.
15641600
**
1565
-** (2) The user is logged into an account other than "nobody" or
1601
+** (3) The user is logged into an account other than "nobody" or
15661602
** "anonymous". In that case the notification settings
15671603
** associated with that account can be edited without needing
15681604
** to know the subscriber code.
1605
+**
1606
+** (4) The name= query parameter contains a 32-digit prefix of
1607
+** subscriber code. (Subscriber codes are normally 64 hex digits
1608
+** in length.) This uniquely identifies the subscriber without
1609
+** revealing the complete subscriber code, and hence without
1610
+** verifying the email address.
15691611
*/
15701612
void alert_page(void){
1571
- const char *zName = P("name");
1572
- Stmt q;
1573
- int sa, sc, sf, st, sw, sx;
1574
- int sdigest = 0, sdonotcall = 0, sverified = 0;
1575
- int isLogin; /* Logged in as an individual */
1576
- const char *ssub = 0;
1577
- const char *semail = 0;
1578
- const char *smip;
1579
- const char *suname = 0;
1580
- const char *mtime;
1581
- const char *sctime;
1582
- int eErr = 0;
1583
- char *zErr = 0;
1584
-
1585
- if( alert_webpages_disabled() ) return;
1613
+ const char *zName = 0; /* Value of the name= query parameter */
1614
+ Stmt q; /* For querying the database */
1615
+ int sa, sc, sf, st, sw, sx; /* Types of notifications requested */
1616
+ int sdigest = 0, sdonotcall = 0, sverified = 0; /* Other fields */
1617
+ int isLogin; /* True if logged in as an individual */
1618
+ const char *ssub = 0; /* Subscription flags */
1619
+ const char *semail = 0; /* Email address */
1620
+ const char *smip; /* */
1621
+ const char *suname = 0; /* Corresponding user.login value */
1622
+ const char *mtime; /* */
1623
+ const char *sctime; /* Time subscription created */
1624
+ int eErr = 0; /* Type of error */
1625
+ char *zErr = 0; /* Error message text */
1626
+ int sid = 0; /* Subscriber ID */
1627
+ int nName; /* Length of zName in bytes */
1628
+ char *zHalfCode; /* prefix of subscriberCode */
1629
+
1630
+ db_begin_transaction();
1631
+ if( alert_webpages_disabled() ){
1632
+ db_commit_transaction();
1633
+ return;
1634
+ }
15861635
login_check_credentials();
15871636
if( !g.perm.EmailAlert ){
1637
+ db_commit_transaction();
15881638
login_needed(g.anon.EmailAlert);
1589
- return;
1639
+ /*NOTREACHED*/
15901640
}
15911641
isLogin = login_is_individual();
1592
- if( zName==0 && isLogin ){
1593
- zName = db_text(0, "SELECT hex(subscriberCode) FROM subscriber"
1594
- " WHERE suname=%Q", g.zLogin);
1642
+ zName = P("name");
1643
+ nName = zName ? (int)strlen(zName) : 0;
1644
+ if( g.perm.Admin && P("sid")!=0 ){
1645
+ sid = atoi(P("sid"));
1646
+ }
1647
+ if( sid==0 && nName>=32 ){
1648
+ sid = db_int(0,
1649
+ "SELECT CASE WHEN hex(subscriberCode) LIKE (%Q||'%%')"
1650
+ " THEN subscriberId ELSE 0 END"
1651
+ " FROM subscriber WHERE subscriberCode>=hextoblob(%Q)"
1652
+ " LIMIT 1", zName, zName);
1653
+ }
1654
+ if( sid==0 && isLogin ){
1655
+ sid = db_int(0, "SELECT subscriberId FROM subscriber"
1656
+ " WHERE suname=%Q", g.zLogin);
15951657
}
1596
- if( zName==0 || !validate16(zName, -1) ){
1658
+ if( sid==0 ){
1659
+ db_commit_transaction();
15971660
cgi_redirect("subscribe");
1598
- return;
1661
+ /*NOTREACHED*/
15991662
}
16001663
alert_submenu_common();
16011664
if( P("submit")!=0 && cgi_csrf_safe(1) ){
16021665
char newSsub[10];
16031666
int nsub = 0;
@@ -1641,11 +1704,11 @@
16411704
if( semail==0 || email_address_is_valid(semail,0)==0 ){
16421705
eErr = 8;
16431706
}
16441707
blob_append_sql(&update, ", semail=%Q", semail);
16451708
}
1646
- blob_append_sql(&update," WHERE subscriberCode=hextoblob(%Q)", zName);
1709
+ blob_append_sql(&update," WHERE subscriberId=%d", sid);
16471710
if( eErr==0 ){
16481711
db_exec_sql(blob_str(&update));
16491712
ssub = 0;
16501713
}
16511714
blob_reset(&update);
@@ -1654,12 +1717,13 @@
16541717
if( !PB("dodelete") ){
16551718
eErr = 9;
16561719
zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to"
16571720
" unsubscribe");
16581721
}else{
1659
- alert_unsubscribe(zName);
1660
- return;
1722
+ alert_unsubscribe(sid);
1723
+ db_commit_transaction();
1724
+ return;
16611725
}
16621726
}
16631727
style_header("Update Subscription");
16641728
db_prepare(&q,
16651729
"SELECT"
@@ -1669,16 +1733,18 @@
16691733
" sdigest," /* 3 */
16701734
" ssub," /* 4 */
16711735
" smip," /* 5 */
16721736
" suname," /* 6 */
16731737
" datetime(mtime,'unixepoch')," /* 7 */
1674
- " datetime(sctime,'unixepoch')" /* 8 */
1675
- " FROM subscriber WHERE subscriberCode=hextoblob(%Q)", zName);
1738
+ " datetime(sctime,'unixepoch')," /* 8 */
1739
+ " hex(subscriberCode)" /* 9 */
1740
+ " FROM subscriber WHERE subscriberId=%d", sid);
16761741
if( db_step(&q)!=SQLITE_ROW ){
16771742
db_finalize(&q);
1743
+ db_commit_transaction();
16781744
cgi_redirect("subscribe");
1679
- return;
1745
+ /*NOTREACHED*/
16801746
}
16811747
if( ssub==0 ){
16821748
semail = db_column_text(&q, 0);
16831749
sdonotcall = db_column_int(&q, 2);
16841750
sdigest = db_column_int(&q, 3);
@@ -1696,23 +1762,45 @@
16961762
sx = strchr(ssub,'x')!=0;
16971763
smip = db_column_text(&q, 5);
16981764
mtime = db_column_text(&q, 7);
16991765
sctime = db_column_text(&q, 8);
17001766
if( !g.perm.Admin && !sverified ){
1701
- db_multi_exec(
1702
- "UPDATE subscriber SET sverified=1 WHERE subscriberCode=hextoblob(%Q)",
1703
- zName);
1704
- @ <h1>Your email alert subscription has been verified!</h1>
1705
- @ <p>Use the form below to update your subscription information.</p>
1706
- @ <p>Hint: Bookmark this page so that you can more easily update
1707
- @ your subscription information in the future</p>
1767
+ if( nName==64 ){
1768
+ db_multi_exec(
1769
+ "UPDATE subscriber SET sverified=1"
1770
+ " WHERE subscriberCode=hextoblob(%Q)",
1771
+ zName);
1772
+ if( db_get_boolean("selfreg-verify",0) ){
1773
+ char *zNewCap = db_get("default-perms","u");
1774
+ db_multi_exec(
1775
+ "UPDATE user"
1776
+ " SET cap=%Q"
1777
+ " WHERE cap='7' AND login=("
1778
+ " SELECT suname FROM subscriber"
1779
+ " WHERE subscriberCode=hextoblob(%Q))",
1780
+ zNewCap, zName
1781
+ );
1782
+ login_set_capabilities(zNewCap, 0);
1783
+ }
1784
+ @ <h1>Your email alert subscription has been verified!</h1>
1785
+ @ <p>Use the form below to update your subscription information.</p>
1786
+ @ <p>Hint: Bookmark this page so that you can more easily update
1787
+ @ your subscription information in the future</p>
1788
+ }else{
1789
+ @ <h2>Your email address is unverified</h2>
1790
+ @ <p>You should have received an email message containing a link
1791
+ @ that you must visit to verify your account. No email notifications
1792
+ @ will be sent until your email address has been verified.</p>
1793
+ }
17081794
}else{
17091795
@ <p>Make changes to the email subscription shown below and
17101796
@ press "Submit".</p>
17111797
}
17121798
form_begin(0, "%R/alerts");
1713
- @ <input type="hidden" name="name" value="%h(zName)">
1799
+ zHalfCode = db_text("x","SELECT hex(substr(subscriberCode,1,16))"
1800
+ " FROM subscriber WHERE subscriberId=%d", sid);
1801
+ @ <input type="hidden" name="name" value="%h(zHalfCode)">
17141802
@ <table class="subscribe">
17151803
@ <tr>
17161804
@ <td class="form_label">Email&nbsp;Address:</td>
17171805
if( isLogin ){
17181806
@ <td><input type="text" name="semail" value="%h(semail)" size="30">\
@@ -1739,10 +1827,13 @@
17391827
@ </tr>
17401828
@ <tr>
17411829
@ <td class='form_label'>IP Address:</td>
17421830
@ <td>%h(smip)</td>
17431831
@ </tr>
1832
+ @ <tr>
1833
+ @ <td class='form_label'>Subscriber&nbsp;Code:</td>
1834
+ @ <td>%h(db_column_text(&q,9))</td>
17441835
@ <tr>
17451836
@ <td class="form_label">User:</td>
17461837
@ <td><input type="text" name="suname" value="%h(suname?suname:"")" \
17471838
@ size="30">\
17481839
uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", suname);
@@ -1780,14 +1871,10 @@
17801871
@ <td><select size="1" name="sdigest">
17811872
@ <option value="0" %s(sdigest?"":"selected")>Individual Emails</option>
17821873
@ <option value="1" %s(sdigest?"selected":"")>Daily Digest</option>
17831874
@ </select></td>
17841875
@ </tr>
1785
-#if 0
1786
- @ <label><input type="checkbox" name="sdigest" %s(sdigest?"checked":"")>\
1787
- @ Daily digest only</label><br>
1788
-#endif
17891876
if( g.perm.Admin ){
17901877
@ <tr>
17911878
@ <td class="form_label">Admin Options:</td><td>
17921879
@ <label><input type="checkbox" name="sdonotcall" \
17931880
@ %s(sdonotcall?"checked":"")> Do not disturb</label><br>
@@ -1811,10 +1898,12 @@
18111898
@ </table>
18121899
@ </form>
18131900
fossil_free(zErr);
18141901
db_finalize(&q);
18151902
style_footer();
1903
+ db_commit_transaction();
1904
+ return;
18161905
}
18171906
18181907
/* This is the message that gets sent to describe how to change
18191908
** or modify a subscription
18201909
*/
@@ -1852,18 +1941,19 @@
18521941
char *zCaptcha = 0;
18531942
int dx;
18541943
int bSubmit;
18551944
const char *zEAddr;
18561945
char *zCode = 0;
1946
+ int sid = 0;
18571947
18581948
/* If a valid subscriber code is supplied, then unsubscribe immediately.
18591949
*/
18601950
if( zName
1861
- && db_exists("SELECT 1 FROM subscriber WHERE subscriberCode=hextoblob(%Q)",
1862
- zName)
1951
+ && (sid = db_int(0, "SELECT subscriberId FROM subscriber"
1952
+ " WHERE subscriberCode=hextoblob(%Q)", zName))!=0
18631953
){
1864
- alert_unsubscribe(zName);
1954
+ alert_unsubscribe(sid);
18651955
return;
18661956
}
18671957
18681958
/* Logged in users are redirected to the /alerts page */
18691959
login_check_credentials();
@@ -2021,11 +2111,11 @@
20212111
if( nDel>0 ){
20222112
@ <p>*** %d(nDel) pending subscriptions deleted ***</p>
20232113
}
20242114
blob_init(&sql, 0, 0);
20252115
blob_append_sql(&sql,
2026
- "SELECT hex(subscriberCode)," /* 0 */
2116
+ "SELECT subscriberId," /* 0 */
20272117
" semail," /* 1 */
20282118
" ssub," /* 2 */
20292119
" suname," /* 3 */
20302120
" sverified," /* 4 */
20312121
" sdigest," /* 5 */
@@ -2058,11 +2148,11 @@
20582148
sqlite3_int64 iMtime = db_column_int64(&q, 6);
20592149
double rAge = (iNow - iMtime)/86400.0;
20602150
int uid = db_column_int(&q, 8);
20612151
const char *zUname = db_column_text(&q, 3);
20622152
@ <tr>
2063
- @ <td><a href='%R/alerts/%s(db_column_text(&q,0))'>\
2153
+ @ <td><a href='%R/alerts?sid=%d(db_column_int(&q,0))'>\
20642154
@ %h(db_column_text(&q,1))</a></td>
20652155
@ <td>%h(db_column_text(&q,2))</td>
20662156
@ <td>%s(db_column_int(&q,5)?"digest":"")</td>
20672157
if( uid ){
20682158
@ <td><a href='%R/setup_uedit?id=%d(uid)'>%h(zUname)</a>
@@ -2210,18 +2300,28 @@
22102300
** is a normal email alert. Construct full-text forum post alerts
22112301
** using a format that enables them to be sent as separate emails.
22122302
*/
22132303
db_prepare(&q,
22142304
"SELECT"
2215
- " forumpost.fpid," /* 0 */
2216
- " (SELECT uuid FROM blob WHERE rid=forumpost.fpid)," /* 1 */
2217
- " datetime(event.mtime)," /* 2 */
2218
- " substr(comment,instr(comment,':')+2)," /* 3 */
2219
- " (SELECT uuid FROM blob WHERE rid=forumpost.firt)," /* 4 */
2220
- " wantalert.needMod," /* 5 */
2221
- " coalesce(display_name(info),euser,user)," /* 6 */
2222
- " forumpost.fprev IS NULL" /* 7 */
2305
+ " forumpost.fpid," /* 0: fpid */
2306
+ " (SELECT uuid FROM blob WHERE rid=forumpost.fpid)," /* 1: hash */
2307
+ " datetime(event.mtime)," /* 2: date/time */
2308
+ " substr(comment,instr(comment,':')+2)," /* 3: comment */
2309
+ " (WITH thread(fpid,fprev) AS ("
2310
+ " SELECT fpid,fprev FROM forumpost AS tx"
2311
+ " WHERE tx.froot=forumpost.froot),"
2312
+ " basepid(fpid,bpid) AS ("
2313
+ " SELECT fpid, fpid FROM thread WHERE fprev IS NULL"
2314
+ " UNION ALL"
2315
+ " SELECT thread.fpid, basepid.bpid FROM basepid, thread"
2316
+ " WHERE basepid.fpid=thread.fprev)"
2317
+ " SELECT uuid FROM blob, basepid"
2318
+ " WHERE basepid.fpid=forumpost.firt"
2319
+ " AND blob.rid=basepid.bpid)," /* 4: in-reply-to */
2320
+ " wantalert.needMod," /* 5: moderated */
2321
+ " coalesce(display_name(info),euser,user)," /* 6: user */
2322
+ " forumpost.fprev IS NULL" /* 7: is an edit */
22232323
" FROM temp.wantalert, event, forumpost"
22242324
" LEFT JOIN user ON (login=coalesce(euser,user))"
22252325
" WHERE event.objid=substr(wantalert.eventId,2)+0"
22262326
" AND eventId GLOB 'f*'"
22272327
" AND forumpost.fpid=event.objid"
22282328
--- src/alerts.c
+++ src/alerts.c
@@ -1208,10 +1208,19 @@
1208 int i, j, n;
1209 char c;
1210
1211 *peErr = 0;
1212 *pzErr = 0;
 
 
 
 
 
 
 
 
 
1213
1214 /* Check the validity of the email address.
1215 **
1216 ** (1) Exactly one '@' character.
1217 ** (2) No other characters besides [a-zA-Z0-9._+-]
@@ -1219,11 +1228,15 @@
1219 ** The local part is currently more restrictive than RFC 5322 allows:
1220 ** https://stackoverflow.com/a/2049510/142454 We will expand this as
1221 ** necessary.
1222 */
1223 zEAddr = P("e");
1224 if( zEAddr==0 ) return 0;
 
 
 
 
1225 for(i=j=n=0; (c = zEAddr[i])!=0; i++){
1226 if( c=='@' ){
1227 n = i;
1228 j++;
1229 continue;
@@ -1249,14 +1262,13 @@
1249 *peErr = 1;
1250 *pzErr = mprintf("email domain too short");
1251 return 0;
1252 }
1253
1254 /* Verify the captcha */
1255 if( needCaptcha && !captcha_is_correct(1) ){
1256 *peErr = 2;
1257 *pzErr = mprintf("incorrect security code");
1258 return 0;
1259 }
1260
1261 /* Check to make sure the email address is available for reuse */
1262 if( db_exists("SELECT 1 FROM subscriber WHERE semail=%Q", zEAddr) ){
@@ -1347,10 +1359,14 @@
1347 /* Everybody else jumps to the page to administer their own
1348 ** account only. */
1349 cgi_redirectf("%R/alerts");
1350 return;
1351 }
 
 
 
 
1352 }
1353 alert_submenu_common();
1354 needCaptcha = !login_is_individual();
1355 if( P("submit")
1356 && cgi_csrf_safe(1)
@@ -1368,10 +1384,11 @@
1368 if( PB("sa") ) ssub[nsub++] = 'a';
1369 if( g.perm.Read && PB("sc") ) ssub[nsub++] = 'c';
1370 if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f';
1371 if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't';
1372 if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w';
 
1373 ssub[nsub] = 0;
1374 db_multi_exec(
1375 "INSERT INTO subscriber(semail,suname,"
1376 " sverified,sdonotcall,sdigest,ssub,sctime,mtime,smip)"
1377 "VALUES(%Q,%Q,%d,0,%d,%Q,now(),now(),%Q)",
@@ -1388,11 +1405,15 @@
1388 id);
1389 if( !needCaptcha ){
1390 /* The new subscription has been added on behalf of a logged-in user.
1391 ** No verification is required. Jump immediately to /alerts page.
1392 */
1393 cgi_redirectf("%R/alerts/%s", zCode);
 
 
 
 
1394 return;
1395 }else{
1396 /* We need to send a verification email */
1397 Blob hdr, body;
1398 AlertSender *pSender = alert_sender_new(0,0);
@@ -1410,11 +1431,11 @@
1410 @ <blockquote><pre>
1411 @ %h(pSender->zErr)
1412 @ </pre></blockquote>
1413 }else{
1414 @ <p>An email has been sent to "%h(zEAddr)". That email contains a
1415 @ hyperlink that you must click on in order to activate your
1416 @ subscription.</p>
1417 }
1418 alert_sender_free(pSender);
1419 style_footer();
1420 }
@@ -1442,16 +1463,22 @@
1442 if( eErr==1 ){
1443 @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
1444 }
1445 @ </tr>
1446 if( needCaptcha ){
1447 uSeed = captcha_seed();
 
 
 
 
 
 
1448 zDecoded = captcha_decode(uSeed);
1449 zCaptcha = captcha_render(zDecoded);
1450 @ <tr>
1451 @ <td class="form_label">Security Code:</td>
1452 @ <td><input type="text" name="captcha" value="" size="30">
1453 captcha_speakit_button(uSeed, "Speak the code");
1454 @ <input type="hidden" name="captchaseed" value="%u(uSeed)"></td>
1455 @ </tr>
1456 if( eErr==2 ){
1457 @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
@@ -1478,10 +1505,12 @@
1478 @ Check-ins</label><br>
1479 }
1480 if( g.perm.RdForum ){
1481 @ <label><input type="checkbox" name="sf" %s(PCK("sf"))> \
1482 @ Forum Posts</label><br>
 
 
1483 }
1484 if( g.perm.RdTkt ){
1485 @ <label><input type="checkbox" name="st" %s(PCK("st"))> \
1486 @ Ticket changes</label><br>
1487 }
@@ -1531,21 +1560,20 @@
1531 /*
1532 ** Either shutdown or completely delete a subscription entry given
1533 ** by the hex value zName. Then paint a webpage that explains that
1534 ** the entry has been removed.
1535 */
1536 static void alert_unsubscribe(const char *zName){
1537 char *zEmail;
1538 zEmail = db_text(0, "SELECT semail FROM subscriber"
1539 " WHERE subscriberCode=hextoblob(%Q)", zName);
1540 if( zEmail==0 ){
1541 style_header("Unsubscribe Fail");
1542 @ <p>Unable to locate a subscriber with the requested key</p>
1543 }else{
1544 db_multi_exec(
1545 "DELETE FROM subscriber WHERE subscriberCode=hextoblob(%Q)",
1546 zName
1547 );
1548 style_header("Unsubscribed");
1549 @ <p>The "%h(zEmail)" email address has been delisted.
1550 @ All traces of that email address have been removed</p>
1551 }
@@ -1556,48 +1584,83 @@
1556 /*
1557 ** WEBPAGE: alerts
1558 **
1559 ** Edit email alert and notification settings.
1560 **
1561 ** The subscriber is identified in either of two ways:
 
 
 
 
 
 
1562 **
1563 ** (1) The name= query parameter contains the subscriberCode.
 
 
1564 **
1565 ** (2) The user is logged into an account other than "nobody" or
1566 ** "anonymous". In that case the notification settings
1567 ** associated with that account can be edited without needing
1568 ** to know the subscriber code.
 
 
 
 
 
 
1569 */
1570 void alert_page(void){
1571 const char *zName = P("name");
1572 Stmt q;
1573 int sa, sc, sf, st, sw, sx;
1574 int sdigest = 0, sdonotcall = 0, sverified = 0;
1575 int isLogin; /* Logged in as an individual */
1576 const char *ssub = 0;
1577 const char *semail = 0;
1578 const char *smip;
1579 const char *suname = 0;
1580 const char *mtime;
1581 const char *sctime;
1582 int eErr = 0;
1583 char *zErr = 0;
1584
1585 if( alert_webpages_disabled() ) return;
 
 
 
 
 
 
 
1586 login_check_credentials();
1587 if( !g.perm.EmailAlert ){
 
1588 login_needed(g.anon.EmailAlert);
1589 return;
1590 }
1591 isLogin = login_is_individual();
1592 if( zName==0 && isLogin ){
1593 zName = db_text(0, "SELECT hex(subscriberCode) FROM subscriber"
1594 " WHERE suname=%Q", g.zLogin);
 
 
 
 
 
 
 
 
 
 
 
 
1595 }
1596 if( zName==0 || !validate16(zName, -1) ){
 
1597 cgi_redirect("subscribe");
1598 return;
1599 }
1600 alert_submenu_common();
1601 if( P("submit")!=0 && cgi_csrf_safe(1) ){
1602 char newSsub[10];
1603 int nsub = 0;
@@ -1641,11 +1704,11 @@
1641 if( semail==0 || email_address_is_valid(semail,0)==0 ){
1642 eErr = 8;
1643 }
1644 blob_append_sql(&update, ", semail=%Q", semail);
1645 }
1646 blob_append_sql(&update," WHERE subscriberCode=hextoblob(%Q)", zName);
1647 if( eErr==0 ){
1648 db_exec_sql(blob_str(&update));
1649 ssub = 0;
1650 }
1651 blob_reset(&update);
@@ -1654,12 +1717,13 @@
1654 if( !PB("dodelete") ){
1655 eErr = 9;
1656 zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to"
1657 " unsubscribe");
1658 }else{
1659 alert_unsubscribe(zName);
1660 return;
 
1661 }
1662 }
1663 style_header("Update Subscription");
1664 db_prepare(&q,
1665 "SELECT"
@@ -1669,16 +1733,18 @@
1669 " sdigest," /* 3 */
1670 " ssub," /* 4 */
1671 " smip," /* 5 */
1672 " suname," /* 6 */
1673 " datetime(mtime,'unixepoch')," /* 7 */
1674 " datetime(sctime,'unixepoch')" /* 8 */
1675 " FROM subscriber WHERE subscriberCode=hextoblob(%Q)", zName);
 
1676 if( db_step(&q)!=SQLITE_ROW ){
1677 db_finalize(&q);
 
1678 cgi_redirect("subscribe");
1679 return;
1680 }
1681 if( ssub==0 ){
1682 semail = db_column_text(&q, 0);
1683 sdonotcall = db_column_int(&q, 2);
1684 sdigest = db_column_int(&q, 3);
@@ -1696,23 +1762,45 @@
1696 sx = strchr(ssub,'x')!=0;
1697 smip = db_column_text(&q, 5);
1698 mtime = db_column_text(&q, 7);
1699 sctime = db_column_text(&q, 8);
1700 if( !g.perm.Admin && !sverified ){
1701 db_multi_exec(
1702 "UPDATE subscriber SET sverified=1 WHERE subscriberCode=hextoblob(%Q)",
1703 zName);
1704 @ <h1>Your email alert subscription has been verified!</h1>
1705 @ <p>Use the form below to update your subscription information.</p>
1706 @ <p>Hint: Bookmark this page so that you can more easily update
1707 @ your subscription information in the future</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1708 }else{
1709 @ <p>Make changes to the email subscription shown below and
1710 @ press "Submit".</p>
1711 }
1712 form_begin(0, "%R/alerts");
1713 @ <input type="hidden" name="name" value="%h(zName)">
 
 
1714 @ <table class="subscribe">
1715 @ <tr>
1716 @ <td class="form_label">Email&nbsp;Address:</td>
1717 if( isLogin ){
1718 @ <td><input type="text" name="semail" value="%h(semail)" size="30">\
@@ -1739,10 +1827,13 @@
1739 @ </tr>
1740 @ <tr>
1741 @ <td class='form_label'>IP Address:</td>
1742 @ <td>%h(smip)</td>
1743 @ </tr>
 
 
 
1744 @ <tr>
1745 @ <td class="form_label">User:</td>
1746 @ <td><input type="text" name="suname" value="%h(suname?suname:"")" \
1747 @ size="30">\
1748 uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", suname);
@@ -1780,14 +1871,10 @@
1780 @ <td><select size="1" name="sdigest">
1781 @ <option value="0" %s(sdigest?"":"selected")>Individual Emails</option>
1782 @ <option value="1" %s(sdigest?"selected":"")>Daily Digest</option>
1783 @ </select></td>
1784 @ </tr>
1785 #if 0
1786 @ <label><input type="checkbox" name="sdigest" %s(sdigest?"checked":"")>\
1787 @ Daily digest only</label><br>
1788 #endif
1789 if( g.perm.Admin ){
1790 @ <tr>
1791 @ <td class="form_label">Admin Options:</td><td>
1792 @ <label><input type="checkbox" name="sdonotcall" \
1793 @ %s(sdonotcall?"checked":"")> Do not disturb</label><br>
@@ -1811,10 +1898,12 @@
1811 @ </table>
1812 @ </form>
1813 fossil_free(zErr);
1814 db_finalize(&q);
1815 style_footer();
 
 
1816 }
1817
1818 /* This is the message that gets sent to describe how to change
1819 ** or modify a subscription
1820 */
@@ -1852,18 +1941,19 @@
1852 char *zCaptcha = 0;
1853 int dx;
1854 int bSubmit;
1855 const char *zEAddr;
1856 char *zCode = 0;
 
1857
1858 /* If a valid subscriber code is supplied, then unsubscribe immediately.
1859 */
1860 if( zName
1861 && db_exists("SELECT 1 FROM subscriber WHERE subscriberCode=hextoblob(%Q)",
1862 zName)
1863 ){
1864 alert_unsubscribe(zName);
1865 return;
1866 }
1867
1868 /* Logged in users are redirected to the /alerts page */
1869 login_check_credentials();
@@ -2021,11 +2111,11 @@
2021 if( nDel>0 ){
2022 @ <p>*** %d(nDel) pending subscriptions deleted ***</p>
2023 }
2024 blob_init(&sql, 0, 0);
2025 blob_append_sql(&sql,
2026 "SELECT hex(subscriberCode)," /* 0 */
2027 " semail," /* 1 */
2028 " ssub," /* 2 */
2029 " suname," /* 3 */
2030 " sverified," /* 4 */
2031 " sdigest," /* 5 */
@@ -2058,11 +2148,11 @@
2058 sqlite3_int64 iMtime = db_column_int64(&q, 6);
2059 double rAge = (iNow - iMtime)/86400.0;
2060 int uid = db_column_int(&q, 8);
2061 const char *zUname = db_column_text(&q, 3);
2062 @ <tr>
2063 @ <td><a href='%R/alerts/%s(db_column_text(&q,0))'>\
2064 @ %h(db_column_text(&q,1))</a></td>
2065 @ <td>%h(db_column_text(&q,2))</td>
2066 @ <td>%s(db_column_int(&q,5)?"digest":"")</td>
2067 if( uid ){
2068 @ <td><a href='%R/setup_uedit?id=%d(uid)'>%h(zUname)</a>
@@ -2210,18 +2300,28 @@
2210 ** is a normal email alert. Construct full-text forum post alerts
2211 ** using a format that enables them to be sent as separate emails.
2212 */
2213 db_prepare(&q,
2214 "SELECT"
2215 " forumpost.fpid," /* 0 */
2216 " (SELECT uuid FROM blob WHERE rid=forumpost.fpid)," /* 1 */
2217 " datetime(event.mtime)," /* 2 */
2218 " substr(comment,instr(comment,':')+2)," /* 3 */
2219 " (SELECT uuid FROM blob WHERE rid=forumpost.firt)," /* 4 */
2220 " wantalert.needMod," /* 5 */
2221 " coalesce(display_name(info),euser,user)," /* 6 */
2222 " forumpost.fprev IS NULL" /* 7 */
 
 
 
 
 
 
 
 
 
 
2223 " FROM temp.wantalert, event, forumpost"
2224 " LEFT JOIN user ON (login=coalesce(euser,user))"
2225 " WHERE event.objid=substr(wantalert.eventId,2)+0"
2226 " AND eventId GLOB 'f*'"
2227 " AND forumpost.fpid=event.objid"
2228
--- src/alerts.c
+++ src/alerts.c
@@ -1208,10 +1208,19 @@
1208 int i, j, n;
1209 char c;
1210
1211 *peErr = 0;
1212 *pzErr = 0;
1213
1214 /* Verify the captcha first */
1215 if( needCaptcha ){
1216 if( !captcha_is_correct(1) ){
1217 *peErr = 2;
1218 *pzErr = mprintf("incorrect security code");
1219 return 0;
1220 }
1221 }
1222
1223 /* Check the validity of the email address.
1224 **
1225 ** (1) Exactly one '@' character.
1226 ** (2) No other characters besides [a-zA-Z0-9._+-]
@@ -1219,11 +1228,15 @@
1228 ** The local part is currently more restrictive than RFC 5322 allows:
1229 ** https://stackoverflow.com/a/2049510/142454 We will expand this as
1230 ** necessary.
1231 */
1232 zEAddr = P("e");
1233 if( zEAddr==0 ){
1234 *peErr = 1;
1235 *pzErr = mprintf("required");
1236 return 0;
1237 }
1238 for(i=j=n=0; (c = zEAddr[i])!=0; i++){
1239 if( c=='@' ){
1240 n = i;
1241 j++;
1242 continue;
@@ -1249,14 +1262,13 @@
1262 *peErr = 1;
1263 *pzErr = mprintf("email domain too short");
1264 return 0;
1265 }
1266
1267 if( authorized_subscription_email(zEAddr)==0 ){
1268 *peErr = 1;
1269 *pzErr = mprintf("not an authorized email address");
 
1270 return 0;
1271 }
1272
1273 /* Check to make sure the email address is available for reuse */
1274 if( db_exists("SELECT 1 FROM subscriber WHERE semail=%Q", zEAddr) ){
@@ -1347,10 +1359,14 @@
1359 /* Everybody else jumps to the page to administer their own
1360 ** account only. */
1361 cgi_redirectf("%R/alerts");
1362 return;
1363 }
1364 }
1365 if( !g.perm.Admin && !db_get_boolean("anon-subscribe",1) ){
1366 register_page();
1367 return;
1368 }
1369 alert_submenu_common();
1370 needCaptcha = !login_is_individual();
1371 if( P("submit")
1372 && cgi_csrf_safe(1)
@@ -1368,10 +1384,11 @@
1384 if( PB("sa") ) ssub[nsub++] = 'a';
1385 if( g.perm.Read && PB("sc") ) ssub[nsub++] = 'c';
1386 if( g.perm.RdForum && PB("sf") ) ssub[nsub++] = 'f';
1387 if( g.perm.RdTkt && PB("st") ) ssub[nsub++] = 't';
1388 if( g.perm.RdWiki && PB("sw") ) ssub[nsub++] = 'w';
1389 if( g.perm.RdForum && PB("sx") ) ssub[nsub++] = 'x';
1390 ssub[nsub] = 0;
1391 db_multi_exec(
1392 "INSERT INTO subscriber(semail,suname,"
1393 " sverified,sdonotcall,sdigest,ssub,sctime,mtime,smip)"
1394 "VALUES(%Q,%Q,%d,0,%d,%Q,now(),now(),%Q)",
@@ -1388,11 +1405,15 @@
1405 id);
1406 if( !needCaptcha ){
1407 /* The new subscription has been added on behalf of a logged-in user.
1408 ** No verification is required. Jump immediately to /alerts page.
1409 */
1410 if( g.perm.Admin ){
1411 cgi_redirectf("%R/alerts/%.32s", zCode);
1412 }else{
1413 cgi_redirectf("%R/alerts");
1414 }
1415 return;
1416 }else{
1417 /* We need to send a verification email */
1418 Blob hdr, body;
1419 AlertSender *pSender = alert_sender_new(0,0);
@@ -1410,11 +1431,11 @@
1431 @ <blockquote><pre>
1432 @ %h(pSender->zErr)
1433 @ </pre></blockquote>
1434 }else{
1435 @ <p>An email has been sent to "%h(zEAddr)". That email contains a
1436 @ hyperlink that you must click to activate your
1437 @ subscription.</p>
1438 }
1439 alert_sender_free(pSender);
1440 style_footer();
1441 }
@@ -1442,16 +1463,22 @@
1463 if( eErr==1 ){
1464 @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
1465 }
1466 @ </tr>
1467 if( needCaptcha ){
1468 const char *zInit = "";
1469 if( P("captchaseed")!=0 && eErr!=2 ){
1470 uSeed = strtoul(P("captchaseed"),0,10);
1471 zInit = P("captcha");
1472 }else{
1473 uSeed = captcha_seed();
1474 }
1475 zDecoded = captcha_decode(uSeed);
1476 zCaptcha = captcha_render(zDecoded);
1477 @ <tr>
1478 @ <td class="form_label">Security Code:</td>
1479 @ <td><input type="text" name="captcha" value="%h(zInit)" size="30">
1480 captcha_speakit_button(uSeed, "Speak the code");
1481 @ <input type="hidden" name="captchaseed" value="%u(uSeed)"></td>
1482 @ </tr>
1483 if( eErr==2 ){
1484 @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
@@ -1478,10 +1505,12 @@
1505 @ Check-ins</label><br>
1506 }
1507 if( g.perm.RdForum ){
1508 @ <label><input type="checkbox" name="sf" %s(PCK("sf"))> \
1509 @ Forum Posts</label><br>
1510 @ <label><input type="checkbox" name="sx" %s(PCK("sx"))> \
1511 @ Forum Edits</label><br>
1512 }
1513 if( g.perm.RdTkt ){
1514 @ <label><input type="checkbox" name="st" %s(PCK("st"))> \
1515 @ Ticket changes</label><br>
1516 }
@@ -1531,21 +1560,20 @@
1560 /*
1561 ** Either shutdown or completely delete a subscription entry given
1562 ** by the hex value zName. Then paint a webpage that explains that
1563 ** the entry has been removed.
1564 */
1565 static void alert_unsubscribe(int sid){
1566 char *zEmail;
1567 zEmail = db_text(0, "SELECT semail FROM subscriber"
1568 " WHERE subscriberId=%d", sid);
1569 if( zEmail==0 ){
1570 style_header("Unsubscribe Fail");
1571 @ <p>Unable to locate a subscriber with the requested key</p>
1572 }else{
1573 db_multi_exec(
1574 "DELETE FROM subscriber WHERE subscriberId=%d", sid
 
1575 );
1576 style_header("Unsubscribed");
1577 @ <p>The "%h(zEmail)" email address has been delisted.
1578 @ All traces of that email address have been removed</p>
1579 }
@@ -1556,48 +1584,83 @@
1584 /*
1585 ** WEBPAGE: alerts
1586 **
1587 ** Edit email alert and notification settings.
1588 **
1589 ** The subscriber is identified in several ways:
1590 **
1591 ** (1) The name= query parameter contains the complete subscriberCode.
1592 ** This only happens when the user receives a verification
1593 ** email and clicks on the link in the email. When a
1594 ** compilete subscriberCode is seen on the name= query parameter,
1595 ** that constitutes verification of the email address.
1596 **
1597 ** (2) The sid= query parameter contains an integer subscriberId.
1598 ** This only works for the administrator. It allows the
1599 ** administrator to edit any subscription.
1600 **
1601 ** (3) The user is logged into an account other than "nobody" or
1602 ** "anonymous". In that case the notification settings
1603 ** associated with that account can be edited without needing
1604 ** to know the subscriber code.
1605 **
1606 ** (4) The name= query parameter contains a 32-digit prefix of
1607 ** subscriber code. (Subscriber codes are normally 64 hex digits
1608 ** in length.) This uniquely identifies the subscriber without
1609 ** revealing the complete subscriber code, and hence without
1610 ** verifying the email address.
1611 */
1612 void alert_page(void){
1613 const char *zName = 0; /* Value of the name= query parameter */
1614 Stmt q; /* For querying the database */
1615 int sa, sc, sf, st, sw, sx; /* Types of notifications requested */
1616 int sdigest = 0, sdonotcall = 0, sverified = 0; /* Other fields */
1617 int isLogin; /* True if logged in as an individual */
1618 const char *ssub = 0; /* Subscription flags */
1619 const char *semail = 0; /* Email address */
1620 const char *smip; /* */
1621 const char *suname = 0; /* Corresponding user.login value */
1622 const char *mtime; /* */
1623 const char *sctime; /* Time subscription created */
1624 int eErr = 0; /* Type of error */
1625 char *zErr = 0; /* Error message text */
1626 int sid = 0; /* Subscriber ID */
1627 int nName; /* Length of zName in bytes */
1628 char *zHalfCode; /* prefix of subscriberCode */
1629
1630 db_begin_transaction();
1631 if( alert_webpages_disabled() ){
1632 db_commit_transaction();
1633 return;
1634 }
1635 login_check_credentials();
1636 if( !g.perm.EmailAlert ){
1637 db_commit_transaction();
1638 login_needed(g.anon.EmailAlert);
1639 /*NOTREACHED*/
1640 }
1641 isLogin = login_is_individual();
1642 zName = P("name");
1643 nName = zName ? (int)strlen(zName) : 0;
1644 if( g.perm.Admin && P("sid")!=0 ){
1645 sid = atoi(P("sid"));
1646 }
1647 if( sid==0 && nName>=32 ){
1648 sid = db_int(0,
1649 "SELECT CASE WHEN hex(subscriberCode) LIKE (%Q||'%%')"
1650 " THEN subscriberId ELSE 0 END"
1651 " FROM subscriber WHERE subscriberCode>=hextoblob(%Q)"
1652 " LIMIT 1", zName, zName);
1653 }
1654 if( sid==0 && isLogin ){
1655 sid = db_int(0, "SELECT subscriberId FROM subscriber"
1656 " WHERE suname=%Q", g.zLogin);
1657 }
1658 if( sid==0 ){
1659 db_commit_transaction();
1660 cgi_redirect("subscribe");
1661 /*NOTREACHED*/
1662 }
1663 alert_submenu_common();
1664 if( P("submit")!=0 && cgi_csrf_safe(1) ){
1665 char newSsub[10];
1666 int nsub = 0;
@@ -1641,11 +1704,11 @@
1704 if( semail==0 || email_address_is_valid(semail,0)==0 ){
1705 eErr = 8;
1706 }
1707 blob_append_sql(&update, ", semail=%Q", semail);
1708 }
1709 blob_append_sql(&update," WHERE subscriberId=%d", sid);
1710 if( eErr==0 ){
1711 db_exec_sql(blob_str(&update));
1712 ssub = 0;
1713 }
1714 blob_reset(&update);
@@ -1654,12 +1717,13 @@
1717 if( !PB("dodelete") ){
1718 eErr = 9;
1719 zErr = mprintf("Select this checkbox and press \"Unsubscribe\" again to"
1720 " unsubscribe");
1721 }else{
1722 alert_unsubscribe(sid);
1723 db_commit_transaction();
1724 return;
1725 }
1726 }
1727 style_header("Update Subscription");
1728 db_prepare(&q,
1729 "SELECT"
@@ -1669,16 +1733,18 @@
1733 " sdigest," /* 3 */
1734 " ssub," /* 4 */
1735 " smip," /* 5 */
1736 " suname," /* 6 */
1737 " datetime(mtime,'unixepoch')," /* 7 */
1738 " datetime(sctime,'unixepoch')," /* 8 */
1739 " hex(subscriberCode)" /* 9 */
1740 " FROM subscriber WHERE subscriberId=%d", sid);
1741 if( db_step(&q)!=SQLITE_ROW ){
1742 db_finalize(&q);
1743 db_commit_transaction();
1744 cgi_redirect("subscribe");
1745 /*NOTREACHED*/
1746 }
1747 if( ssub==0 ){
1748 semail = db_column_text(&q, 0);
1749 sdonotcall = db_column_int(&q, 2);
1750 sdigest = db_column_int(&q, 3);
@@ -1696,23 +1762,45 @@
1762 sx = strchr(ssub,'x')!=0;
1763 smip = db_column_text(&q, 5);
1764 mtime = db_column_text(&q, 7);
1765 sctime = db_column_text(&q, 8);
1766 if( !g.perm.Admin && !sverified ){
1767 if( nName==64 ){
1768 db_multi_exec(
1769 "UPDATE subscriber SET sverified=1"
1770 " WHERE subscriberCode=hextoblob(%Q)",
1771 zName);
1772 if( db_get_boolean("selfreg-verify",0) ){
1773 char *zNewCap = db_get("default-perms","u");
1774 db_multi_exec(
1775 "UPDATE user"
1776 " SET cap=%Q"
1777 " WHERE cap='7' AND login=("
1778 " SELECT suname FROM subscriber"
1779 " WHERE subscriberCode=hextoblob(%Q))",
1780 zNewCap, zName
1781 );
1782 login_set_capabilities(zNewCap, 0);
1783 }
1784 @ <h1>Your email alert subscription has been verified!</h1>
1785 @ <p>Use the form below to update your subscription information.</p>
1786 @ <p>Hint: Bookmark this page so that you can more easily update
1787 @ your subscription information in the future</p>
1788 }else{
1789 @ <h2>Your email address is unverified</h2>
1790 @ <p>You should have received an email message containing a link
1791 @ that you must visit to verify your account. No email notifications
1792 @ will be sent until your email address has been verified.</p>
1793 }
1794 }else{
1795 @ <p>Make changes to the email subscription shown below and
1796 @ press "Submit".</p>
1797 }
1798 form_begin(0, "%R/alerts");
1799 zHalfCode = db_text("x","SELECT hex(substr(subscriberCode,1,16))"
1800 " FROM subscriber WHERE subscriberId=%d", sid);
1801 @ <input type="hidden" name="name" value="%h(zHalfCode)">
1802 @ <table class="subscribe">
1803 @ <tr>
1804 @ <td class="form_label">Email&nbsp;Address:</td>
1805 if( isLogin ){
1806 @ <td><input type="text" name="semail" value="%h(semail)" size="30">\
@@ -1739,10 +1827,13 @@
1827 @ </tr>
1828 @ <tr>
1829 @ <td class='form_label'>IP Address:</td>
1830 @ <td>%h(smip)</td>
1831 @ </tr>
1832 @ <tr>
1833 @ <td class='form_label'>Subscriber&nbsp;Code:</td>
1834 @ <td>%h(db_column_text(&q,9))</td>
1835 @ <tr>
1836 @ <td class="form_label">User:</td>
1837 @ <td><input type="text" name="suname" value="%h(suname?suname:"")" \
1838 @ size="30">\
1839 uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", suname);
@@ -1780,14 +1871,10 @@
1871 @ <td><select size="1" name="sdigest">
1872 @ <option value="0" %s(sdigest?"":"selected")>Individual Emails</option>
1873 @ <option value="1" %s(sdigest?"selected":"")>Daily Digest</option>
1874 @ </select></td>
1875 @ </tr>
 
 
 
 
1876 if( g.perm.Admin ){
1877 @ <tr>
1878 @ <td class="form_label">Admin Options:</td><td>
1879 @ <label><input type="checkbox" name="sdonotcall" \
1880 @ %s(sdonotcall?"checked":"")> Do not disturb</label><br>
@@ -1811,10 +1898,12 @@
1898 @ </table>
1899 @ </form>
1900 fossil_free(zErr);
1901 db_finalize(&q);
1902 style_footer();
1903 db_commit_transaction();
1904 return;
1905 }
1906
1907 /* This is the message that gets sent to describe how to change
1908 ** or modify a subscription
1909 */
@@ -1852,18 +1941,19 @@
1941 char *zCaptcha = 0;
1942 int dx;
1943 int bSubmit;
1944 const char *zEAddr;
1945 char *zCode = 0;
1946 int sid = 0;
1947
1948 /* If a valid subscriber code is supplied, then unsubscribe immediately.
1949 */
1950 if( zName
1951 && (sid = db_int(0, "SELECT subscriberId FROM subscriber"
1952 " WHERE subscriberCode=hextoblob(%Q)", zName))!=0
1953 ){
1954 alert_unsubscribe(sid);
1955 return;
1956 }
1957
1958 /* Logged in users are redirected to the /alerts page */
1959 login_check_credentials();
@@ -2021,11 +2111,11 @@
2111 if( nDel>0 ){
2112 @ <p>*** %d(nDel) pending subscriptions deleted ***</p>
2113 }
2114 blob_init(&sql, 0, 0);
2115 blob_append_sql(&sql,
2116 "SELECT subscriberId," /* 0 */
2117 " semail," /* 1 */
2118 " ssub," /* 2 */
2119 " suname," /* 3 */
2120 " sverified," /* 4 */
2121 " sdigest," /* 5 */
@@ -2058,11 +2148,11 @@
2148 sqlite3_int64 iMtime = db_column_int64(&q, 6);
2149 double rAge = (iNow - iMtime)/86400.0;
2150 int uid = db_column_int(&q, 8);
2151 const char *zUname = db_column_text(&q, 3);
2152 @ <tr>
2153 @ <td><a href='%R/alerts?sid=%d(db_column_int(&q,0))'>\
2154 @ %h(db_column_text(&q,1))</a></td>
2155 @ <td>%h(db_column_text(&q,2))</td>
2156 @ <td>%s(db_column_int(&q,5)?"digest":"")</td>
2157 if( uid ){
2158 @ <td><a href='%R/setup_uedit?id=%d(uid)'>%h(zUname)</a>
@@ -2210,18 +2300,28 @@
2300 ** is a normal email alert. Construct full-text forum post alerts
2301 ** using a format that enables them to be sent as separate emails.
2302 */
2303 db_prepare(&q,
2304 "SELECT"
2305 " forumpost.fpid," /* 0: fpid */
2306 " (SELECT uuid FROM blob WHERE rid=forumpost.fpid)," /* 1: hash */
2307 " datetime(event.mtime)," /* 2: date/time */
2308 " substr(comment,instr(comment,':')+2)," /* 3: comment */
2309 " (WITH thread(fpid,fprev) AS ("
2310 " SELECT fpid,fprev FROM forumpost AS tx"
2311 " WHERE tx.froot=forumpost.froot),"
2312 " basepid(fpid,bpid) AS ("
2313 " SELECT fpid, fpid FROM thread WHERE fprev IS NULL"
2314 " UNION ALL"
2315 " SELECT thread.fpid, basepid.bpid FROM basepid, thread"
2316 " WHERE basepid.fpid=thread.fprev)"
2317 " SELECT uuid FROM blob, basepid"
2318 " WHERE basepid.fpid=forumpost.firt"
2319 " AND blob.rid=basepid.bpid)," /* 4: in-reply-to */
2320 " wantalert.needMod," /* 5: moderated */
2321 " coalesce(display_name(info),euser,user)," /* 6: user */
2322 " forumpost.fprev IS NULL" /* 7: is an edit */
2323 " FROM temp.wantalert, event, forumpost"
2324 " LEFT JOIN user ON (login=coalesce(euser,user))"
2325 " WHERE event.objid=substr(wantalert.eventId,2)+0"
2326 " AND eventId GLOB 'f*'"
2327 " AND forumpost.fpid=event.objid"
2328
+1 -1
--- src/allrepo.c
+++ src/allrepo.c
@@ -128,11 +128,11 @@
128128
** supported by the rebuild command itself, if any are
129129
** present, are passed along verbatim. The --force and
130130
** --randomize options are not supported.
131131
**
132132
** sync Run a "sync" on all repositories. Only the --verbose
133
-** option is supported.
133
+** and --unversioned options are supported.
134134
**
135135
** setting Run the "setting", "set", or "unset" commands on all
136136
** set repositories. These command are particularly useful in
137137
** unset conjunction with the "max-loadavg" setting which cannot
138138
** otherwise be set globally.
139139
140140
ADDED src/backlink.c
--- src/allrepo.c
+++ src/allrepo.c
@@ -128,11 +128,11 @@
128 ** supported by the rebuild command itself, if any are
129 ** present, are passed along verbatim. The --force and
130 ** --randomize options are not supported.
131 **
132 ** sync Run a "sync" on all repositories. Only the --verbose
133 ** option is supported.
134 **
135 ** setting Run the "setting", "set", or "unset" commands on all
136 ** set repositories. These command are particularly useful in
137 ** unset conjunction with the "max-loadavg" setting which cannot
138 ** otherwise be set globally.
139
140 DDED src/backlink.c
--- src/allrepo.c
+++ src/allrepo.c
@@ -128,11 +128,11 @@
128 ** supported by the rebuild command itself, if any are
129 ** present, are passed along verbatim. The --force and
130 ** --randomize options are not supported.
131 **
132 ** sync Run a "sync" on all repositories. Only the --verbose
133 ** and --unversioned options are supported.
134 **
135 ** setting Run the "setting", "set", or "unset" commands on all
136 ** set repositories. These command are particularly useful in
137 ** unset conjunction with the "max-loadavg" setting which cannot
138 ** otherwise be set globally.
139
140 DDED src/backlink.c
--- a/src/backlink.c
+++ b/src/backlink.c
@@ -0,0 +1,6 @@
1
+/*
2
+** Copyright (c) 2020 D. Richard Hipp
3
+**
4
+** This program is free software; you can redistribute it and/or
5
+** modify it under the terms of the Simplified BSD License (also
6
+** known as the "2-Clau
--- a/src/backlink.c
+++ b/src/backlink.c
@@ -0,0 +1,6 @@
 
 
 
 
 
 
--- a/src/backlink.c
+++ b/src/backlink.c
@@ -0,0 +1,6 @@
1 /*
2 ** Copyright (c) 2020 D. Richard Hipp
3 **
4 ** This program is free software; you can redistribute it and/or
5 ** modify it under the terms of the Simplified BSD License (also
6 ** known as the "2-Clau
+14 -1
--- src/branch.c
+++ src/branch.c
@@ -18,10 +18,24 @@
1818
** This file contains code used to create new branches within a repository.
1919
*/
2020
#include "config.h"
2121
#include "branch.h"
2222
#include <assert.h>
23
+
24
+/*
25
+** Return true if zBr is the branch name associated with check-in with
26
+** blob.uuid value of zUuid
27
+*/
28
+int branch_includes_uuid(const char *zBr, const char *zUuid){
29
+ return db_exists(
30
+ "SELECT 1 FROM tagxref, blob"
31
+ " WHERE blob.uuid=%Q AND tagxref.rid=blob.rid"
32
+ " AND tagxref.value=%Q AND tagxref.tagtype>0"
33
+ " AND tagxref.tagid=%d",
34
+ zUuid, zBr, TAG_BRANCH
35
+ );
36
+}
2337
2438
/*
2539
** If RID refers to a check-in, return the name of the branch for that
2640
** check-in.
2741
**
@@ -144,11 +158,10 @@
144158
blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
145159
}
146160
blob_appendf(&branch, "T *branch * %F\n", zBranch);
147161
blob_appendf(&branch, "T *sym-%F *\n", zBranch);
148162
if( isPrivate ){
149
- blob_appendf(&branch, "T +private *\n");
150163
noSign = 1;
151164
}
152165
153166
/* Cancel all other symbolic tags */
154167
db_prepare(&q,
155168
--- src/branch.c
+++ src/branch.c
@@ -18,10 +18,24 @@
18 ** This file contains code used to create new branches within a repository.
19 */
20 #include "config.h"
21 #include "branch.h"
22 #include <assert.h>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
24 /*
25 ** If RID refers to a check-in, return the name of the branch for that
26 ** check-in.
27 **
@@ -144,11 +158,10 @@
144 blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
145 }
146 blob_appendf(&branch, "T *branch * %F\n", zBranch);
147 blob_appendf(&branch, "T *sym-%F *\n", zBranch);
148 if( isPrivate ){
149 blob_appendf(&branch, "T +private *\n");
150 noSign = 1;
151 }
152
153 /* Cancel all other symbolic tags */
154 db_prepare(&q,
155
--- src/branch.c
+++ src/branch.c
@@ -18,10 +18,24 @@
18 ** This file contains code used to create new branches within a repository.
19 */
20 #include "config.h"
21 #include "branch.h"
22 #include <assert.h>
23
24 /*
25 ** Return true if zBr is the branch name associated with check-in with
26 ** blob.uuid value of zUuid
27 */
28 int branch_includes_uuid(const char *zBr, const char *zUuid){
29 return db_exists(
30 "SELECT 1 FROM tagxref, blob"
31 " WHERE blob.uuid=%Q AND tagxref.rid=blob.rid"
32 " AND tagxref.value=%Q AND tagxref.tagtype>0"
33 " AND tagxref.tagid=%d",
34 zUuid, zBr, TAG_BRANCH
35 );
36 }
37
38 /*
39 ** If RID refers to a check-in, return the name of the branch for that
40 ** check-in.
41 **
@@ -144,11 +158,10 @@
158 blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
159 }
160 blob_appendf(&branch, "T *branch * %F\n", zBranch);
161 blob_appendf(&branch, "T *sym-%F *\n", zBranch);
162 if( isPrivate ){
 
163 noSign = 1;
164 }
165
166 /* Cancel all other symbolic tags */
167 db_prepare(&q,
168
+144 -75
--- src/browse.c
+++ src/browse.c
@@ -59,10 +59,18 @@
5959
zOut = sqlite3_mprintf("/%.*s", i-n, &z[n]);
6060
sqlite3_result_text(context, zOut, i-n+1, sqlite3_free);
6161
}
6262
}
6363
64
+/*
65
+** Flag arguments for hyperlinked_path()
66
+*/
67
+#if INTERFACE
68
+# define LINKPATH_FINFO 0x0001 /* Link final term to /finfo */
69
+# define LINKPATH_FILE 0x0002 /* Link final term to /file */
70
+#endif
71
+
6472
/*
6573
** Given a pathname which is a relative path from the root of
6674
** the repository to a file or directory, compute a string which
6775
** is an HTML rendering of that path with hyperlinks on each
6876
** directory component of the path where the hyperlink redirects
@@ -76,29 +84,36 @@
7684
void hyperlinked_path(
7785
const char *zPath, /* Path to render */
7886
Blob *pOut, /* Write into this blob */
7987
const char *zCI, /* check-in name, or NULL */
8088
const char *zURI, /* "dir" or "tree" */
81
- const char *zREx /* Extra query parameters */
89
+ const char *zREx, /* Extra query parameters */
90
+ unsigned int mFlags /* Extra flags */
8291
){
8392
int i, j;
8493
char *zSep = "";
8594
8695
for(i=0; zPath[i]; i=j){
8796
for(j=i; zPath[j] && zPath[j]!='/'; j++){}
88
- if( zPath[j] && g.perm.Hyperlink ){
89
- if( zCI ){
90
- char *zLink = href("%R/%s?name=%#T%s&ci=%!S", zURI, j, zPath, zREx,zCI);
91
- blob_appendf(pOut, "%s%z%#h</a>",
92
- zSep, zLink, j-i, &zPath[i]);
93
- }else{
94
- char *zLink = href("%R/%s?name=%#T%s", zURI, j, zPath, zREx);
95
- blob_appendf(pOut, "%s%z%#h</a>",
96
- zSep, zLink, j-i, &zPath[i]);
97
- }
98
- }else{
99
- blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]);
97
+ if( zPath[j]==0 ){
98
+ if( mFlags & LINKPATH_FILE ){
99
+ zURI = "file";
100
+ }else if( mFlags & LINKPATH_FINFO ){
101
+ zURI = "finfo";
102
+ }else{
103
+ blob_appendf(pOut, "/%h", zPath+i);
104
+ break;
105
+ }
106
+ }
107
+ if( zCI ){
108
+ char *zLink = href("%R/%s?name=%#T%s&ci=%T", zURI, j, zPath, zREx,zCI);
109
+ blob_appendf(pOut, "%s%z%#h</a>",
110
+ zSep, zLink, j-i, &zPath[i]);
111
+ }else{
112
+ char *zLink = href("%R/%s?name=%#T%s", zURI, j, zPath, zREx);
113
+ blob_appendf(pOut, "%s%z%#h</a>",
114
+ zSep, zLink, j-i, &zPath[i]);
100115
}
101116
zSep = "/";
102117
while( zPath[j]=='/' ){ j++; }
103118
}
104119
}
@@ -127,27 +142,24 @@
127142
char *zPrefix;
128143
Stmt q;
129144
const char *zCI = P("ci");
130145
int rid = 0;
131146
char *zUuid = 0;
132
- Blob dirname;
133147
Manifest *pM = 0;
134148
const char *zSubdirLink;
135149
int linkTrunk = 1;
136150
int linkTip = 1;
137151
HQuery sURI;
152
+ int isSymbolicCI = 0; /* ci= is symbolic name, not a hash prefix */
153
+ int isBranchCI = 0; /* True if ci= refers to a branch name */
154
+ char *zHeader = 0;
138155
156
+ if( zCI && strlen(zCI)==0 ){ zCI = 0; }
139157
if( strcmp(PD("type","flat"),"tree")==0 ){ page_tree(); return; }
140158
login_check_credentials();
141159
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
142160
while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
143
- style_header("File List");
144
- style_adunit_config(ADUNIT_RIGHT_OK);
145
- sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
146
- pathelementFunc, 0, 0);
147
- url_initialize(&sURI, "dir");
148
- cgi_query_parameters_to_url(&sURI);
149161
150162
/* If the name= parameter is an empty string, make it a NULL pointer */
151163
if( zD && strlen(zD)==0 ){ zD = 0; }
152164
153165
/* If a specific check-in is requested, fetch and parse it. If the
@@ -159,53 +171,79 @@
159171
if( pM ){
160172
int trunkRid = symbolic_name_to_rid("tag:trunk", "ci");
161173
linkTrunk = trunkRid && rid != trunkRid;
162174
linkTip = rid != symbolic_name_to_rid("tip", "ci");
163175
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
176
+ isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI))!=0);
177
+ isBranchCI = branch_includes_uuid(zCI, zUuid);
164178
}else{
165179
zCI = 0;
166180
}
167181
}
168182
183
+ assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) );
184
+ if( zD==0 ){
185
+ if( zCI ){
186
+ zHeader = mprintf("Top-level Files of %s", zCI);
187
+ }else{
188
+ zHeader = mprintf("All Top-level Files");
189
+ }
190
+ }else{
191
+ if( zCI ){
192
+ zHeader = mprintf("Files in %s/ of %s", zD, zCI);
193
+ }else{
194
+ zHeader = mprintf("All File in %s/", zD);
195
+ }
196
+ }
197
+ style_header("%s", zHeader);
198
+ fossil_free(zHeader);
199
+ style_adunit_config(ADUNIT_RIGHT_OK);
200
+ sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
201
+ pathelementFunc, 0, 0);
202
+ url_initialize(&sURI, "dir");
203
+ cgi_query_parameters_to_url(&sURI);
204
+
169205
/* Compute the title of the page */
170
- blob_zero(&dirname);
171206
if( zD ){
172
- blob_append(&dirname, "in directory ", -1);
173
- hyperlinked_path(zD, &dirname, zCI, "dir", "");
207
+ Blob dirname;
208
+ blob_init(&dirname, 0, 0);
209
+ hyperlinked_path(zD, &dirname, zCI, "dir", "", 0);
210
+ @ <h2>Files in directory %s(blob_str(&dirname)) \
211
+ blob_reset(&dirname);
174212
zPrefix = mprintf("%s/", zD);
175213
style_submenu_element("Top-Level", "%s",
176214
url_render(&sURI, "name", 0, 0, 0));
177215
}else{
178
- blob_append(&dirname, "in the top-level directory", -1);
216
+ @ <h2>Files in the top-level directory \
179217
zPrefix = "";
180218
}
219
+ if( zCI ){
220
+ if( fossil_strcmp(zCI,"tip")==0 ){
221
+ @ from the %z(href("%R/info?name=%T",zCI))latest check-in</a></h2>
222
+ }else if( isBranchCI ){
223
+ @ from the %z(href("%R/info?name=%T",zCI))latest check-in</a> \
224
+ @ of branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a></h2>
225
+ }else {
226
+ @ of check-in %z(href("%R/info?name=%T",zCI))%h(zCI)</a></h2>
227
+ }
228
+ zSubdirLink = mprintf("%R/dir?ci=%T&name=%T", zCI, zPrefix);
229
+ if( nD==0 ){
230
+ style_submenu_element("File Ages", "%R/fileage?name=%T", zCI);
231
+ }
232
+ }else{
233
+ @ in any check-in</h2>
234
+ zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
235
+ }
181236
if( linkTrunk ){
182237
style_submenu_element("Trunk", "%s",
183238
url_render(&sURI, "ci", "trunk", 0, 0));
184239
}
185240
if( linkTip ){
186241
style_submenu_element("Tip", "%s", url_render(&sURI, "ci", "tip", 0, 0));
187242
}
188
- if( zCI ){
189
- @ <h2>Files of check-in [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>]
190
- @ %s(blob_str(&dirname))
191
- if( zD ){
192
- @ &nbsp;&nbsp;%z(href("%R/timeline?chng=%T/*", zD))[history]</a>
193
- }
194
- @ </h2>
195
- zSubdirLink = mprintf("%R/dir?ci=%!S&name=%T", zUuid, zPrefix);
196
- if( nD==0 ){
197
- style_submenu_element("File Ages", "%R/fileage?name=%!S", zUuid);
198
- }
199
- }else{
200
- @ <h2>The union of all files from all check-ins
201
- @ %s(blob_str(&dirname))
202
- if( zD ){
203
- @ &nbsp;&nbsp;%z(href("%R/timeline?chng=%T/*", zD))[history]</a>
204
- }
205
- @ </h2>
206
- zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
243
+ if( zD ){
244
+ style_submenu_element("History","%R/timeline?chng=%T/*", zD);
207245
}
208246
style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0));
209247
style_submenu_element("Tree-View", "%s",
210248
url_render(&sURI, "type", "tree", 0, 0));
211249
@@ -282,12 +320,11 @@
282320
zFN++;
283321
@ <li class="dir">%z(href("%s%T",zSubdirLink,zFN))%h(zFN)</a></li>
284322
}else{
285323
const char *zLink;
286324
if( zCI ){
287
- const char *zUuid = db_column_text(&q, 1);
288
- zLink = href("%R/artifact/%!S",zUuid);
325
+ zLink = href("%R/file?name=%T%T&ci=%T",zPrefix,zFN,zCI);
289326
}else{
290327
zLink = href("%R/finfo?name=%T%T",zPrefix,zFN);
291328
}
292329
@ <li class="%z(fileext_class(zFN))">%z(zLink)%h(zFN)</a></li>
293330
}
@@ -612,11 +649,15 @@
612649
HQuery sURI; /* Hyperlink */
613650
int startExpanded; /* True to start out with the tree expanded */
614651
int showDirOnly; /* Show directories only. Omit files */
615652
int nDir = 0; /* Number of directories. Used for ID attributes */
616653
char *zProjectName = db_get("project-name", 0);
654
+ int isSymbolicCI = 0; /* ci= is a symbolic name, not a hash prefix */
655
+ int isBranchCI = 0; /* ci= refers to a branch name */
656
+ char *zHeader = 0;
617657
658
+ if( zCI && strlen(zCI)==0 ){ zCI = 0; }
618659
if( strcmp(PD("type","flat"),"flat")==0 ){ page_dir(); return; }
619660
memset(&sTree, 0, sizeof(sTree));
620661
login_check_credentials();
621662
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
622663
while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
@@ -624,14 +665,12 @@
624665
pathelementFunc, 0, 0);
625666
url_initialize(&sURI, "tree");
626667
cgi_query_parameters_to_url(&sURI);
627668
if( PB("nofiles") ){
628669
showDirOnly = 1;
629
- style_header("Folder Hierarchy");
630670
}else{
631671
showDirOnly = 0;
632
- style_header("File Tree");
633672
}
634673
style_adunit_config(ADUNIT_RIGHT_OK);
635674
if( PB("expand") ){
636675
startExpanded = 1;
637676
}else{
@@ -660,37 +699,54 @@
660699
linkTip = rid != symbolic_name_to_rid("tip", "ci");
661700
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
662701
rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
663702
zNow = db_text("", "SELECT datetime(mtime,toLocal())"
664703
" FROM event WHERE objid=%d", rid);
704
+ isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI)) != 0);
705
+ isBranchCI = branch_includes_uuid(zCI, zUuid);
665706
}else{
666707
zCI = 0;
667708
}
668709
}
669710
if( zCI==0 ){
670711
rNow = db_double(0.0, "SELECT max(mtime) FROM event");
671712
zNow = db_text("", "SELECT datetime(max(mtime),toLocal()) FROM event");
672713
}
714
+
715
+ assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) );
716
+ if( zD==0 ){
717
+ if( zCI ){
718
+ zHeader = mprintf("Top-level Files of %s", zCI);
719
+ }else{
720
+ zHeader = mprintf("All Top-level Files");
721
+ }
722
+ }else{
723
+ if( zCI ){
724
+ zHeader = mprintf("Files in %s/ of %s", zD, zCI);
725
+ }else{
726
+ zHeader = mprintf("All File in %s/", zD);
727
+ }
728
+ }
729
+ style_header("%s", zHeader);
730
+ fossil_free(zHeader);
673731
674732
/* Compute the title of the page */
675733
blob_zero(&dirname);
676734
if( zD ){
677735
blob_append(&dirname, "within directory ", -1);
678
- hyperlinked_path(zD, &dirname, zCI, "tree", zREx);
736
+ hyperlinked_path(zD, &dirname, zCI, "tree", zREx, 0);
679737
if( zRE ) blob_appendf(&dirname, " matching \"%s\"", zRE);
680738
style_submenu_element("Top-Level", "%s",
681739
url_render(&sURI, "name", 0, 0, 0));
682
- }else{
683
- if( zRE ){
684
- blob_appendf(&dirname, "matching \"%s\"", zRE);
685
- }
740
+ }else if( zRE ){
741
+ blob_appendf(&dirname, "matching \"%s\"", zRE);
686742
}
687743
style_submenu_binary("mtime","Sort By Time","Sort By Filename", 0);
688744
if( zCI ){
689745
style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0));
690746
if( nD==0 && !showDirOnly ){
691
- style_submenu_element("File Ages", "%R/fileage?name=%s", zUuid);
747
+ style_submenu_element("File Ages", "%R/fileage?name=%T", zCI);
692748
}
693749
}
694750
if( linkTrunk ){
695751
style_submenu_element("Trunk", "%s",
696752
url_render(&sURI, "ci", "trunk", 0, 0));
@@ -745,10 +801,11 @@
745801
tree_add_node(&sTree, zName, zUuid, mtime);
746802
nFile++;
747803
}
748804
db_finalize(&q);
749805
}
806
+ style_submenu_checkbox("nofiles", "Folders Only", 0, 0);
750807
751808
if( showDirOnly ){
752809
for(nFile=0, p=sTree.pFirst; p; p=p->pNext){
753810
if( p->pChild!=0 && p->nFullName>nD ) nFile++;
754811
}
@@ -755,18 +812,24 @@
755812
zObjType = "Folders";
756813
}else{
757814
zObjType = "Files";
758815
}
759816
760
- style_submenu_checkbox("nofiles", "Folders Only", 0, 0);
761
-
762
- if( zCI ){
763
- @ <h2>%s(zObjType) from
764
- if( sqlite3_strnicmp(zCI, zUuid, (int)strlen(zCI))!=0 ){
765
- @ "%h(zCI)"
766
- }
767
- @ [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>] %s(blob_str(&dirname))
817
+ if( zCI && strcmp(zCI,"tip")==0 ){
818
+ @ <h2>%s(zObjType) in the %z(href("%R/info?name=tip"))latest check-in</a>
819
+ }else if( isBranchCI ){
820
+ @ <h2>%s(zObjType) in the %z(href("%R/info?name=%T",zCI))latest check-in\
821
+ @ </a> for branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a>
822
+ if( blob_size(&dirname) ){
823
+ @ and %s(blob_str(&dirname))</h2>
824
+ }
825
+ }else if( zCI ){
826
+ @ <h2>%s(zObjType) for check-in \
827
+ @ %z(href("%R/info?name=%T",zCI))%h(zCI)</a></h2>
828
+ if( blob_size(&dirname) ){
829
+ @ and %s(blob_str(&dirname))</h2>
830
+ }
768831
}else{
769832
int n = db_int(0, "SELECT count(*) FROM plink");
770833
@ <h2>%s(zObjType) from all %d(n) check-ins %s(blob_str(&dirname))
771834
}
772835
if( useMtime ){
@@ -824,11 +887,11 @@
824887
nDir++;
825888
}else if( !showDirOnly ){
826889
const char *zFileClass = fileext_class(p->zName);
827890
char *zLink;
828891
if( zCI ){
829
- zLink = href("%R/artifact/%!S",p->zUuid);
892
+ zLink = href("%R/file?name=%T&ci=%T",p->zFullName,zCI);
830893
}else{
831894
zLink = href("%R/finfo?name=%T",p->zFullName);
832895
}
833896
@ <li class="%z(zFileClass)%s(zLastClass)"><div class="filetreeline">
834897
@ %z(zLink)%h(p->zName)</a>
@@ -1001,10 +1064,11 @@
10011064
int rid;
10021065
const char *zName;
10031066
const char *zGlob;
10041067
const char *zUuid;
10051068
const char *zNow; /* Time of check-in */
1069
+ int isBranchCI; /* name= is a branch name */
10061070
int showId = PB("showid");
10071071
Stmt q1, q2;
10081072
double baseTime;
10091073
login_check_credentials();
10101074
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
@@ -1014,28 +1078,34 @@
10141078
rid = symbolic_name_to_rid(zName, "ci");
10151079
if( rid==0 ){
10161080
fossil_fatal("not a valid check-in: %s", zName);
10171081
}
10181082
zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
1083
+ isBranchCI = branch_includes_uuid(zName,zUuid);
10191084
baseTime = db_double(0.0,"SELECT mtime FROM event WHERE objid=%d", rid);
10201085
zNow = db_text("", "SELECT datetime(mtime,toLocal()) FROM event"
10211086
" WHERE objid=%d", rid);
10221087
style_submenu_element("Tree-View", "%R/tree?ci=%T&mtime=1&type=tree", zName);
10231088
style_header("File Ages");
10241089
zGlob = P("glob");
10251090
compute_fileage(rid,zGlob);
10261091
db_multi_exec("CREATE INDEX fileage_ix1 ON fileage(mid,pathname);");
10271092
1028
- @ <h1>Files in
1029
- @ %z(href("%R/info/%!S",zUuid))[%S(zUuid)]</a>
1093
+ if( fossil_strcmp(zName,"tip")==0 ){
1094
+ @ <h1>Files in the %z(href("%R/info?name=tip"))latest check-in</a>
1095
+ }else if( isBranchCI ){
1096
+ @ <h1>Files in the %z(href("%R/info?name=%T",zName))latest check-in</a>
1097
+ @ of branch %z(href("%R/timeline?r=%T",zName))%h(zName)</a>
1098
+ }else{
1099
+ @ <h1>Files in check-in %z(href("%R/info?name=%T",zName))%h(zName)</a>
1100
+ }
10301101
if( zGlob && zGlob[0] ){
10311102
@ that match "%h(zGlob)"
10321103
}
10331104
@ ordered by age</h1>
10341105
@
1035
- @ <p>File ages are expressed relative to the
1036
- @ %z(href("%R/ci/%!S",zUuid))[%S(zUuid)]</a> check-in time of
1106
+ @ <p>File ages are expressed relative to the check-in time of
10371107
@ %z(href("%R/timeline?c=%t",zNow))%s(zNow)</a>.</p>
10381108
@
10391109
@ <div class='fileage'><table>
10401110
@ <tr><th>Age</th><th>Files</th><th>Check-in</th></tr>
10411111
db_prepare(&q1,
@@ -1050,14 +1120,13 @@
10501120
" AND blob.rid=event.objid\n"
10511121
" ORDER BY event.mtime DESC;",
10521122
TAG_BRANCH
10531123
);
10541124
db_prepare(&q2,
1055
- "SELECT blob.uuid, filename.name, fileage.fid\n"
1056
- " FROM fileage, blob, filename\n"
1125
+ "SELECT filename.name, fileage.fid\n"
1126
+ " FROM fileage, filename\n"
10571127
" WHERE fileage.mid=:mid AND filename.fnid=fileage.fnid"
1058
- " AND blob.rid=fileage.fid;"
10591128
);
10601129
while( db_step(&q1)==SQLITE_ROW ){
10611130
double age = baseTime - db_column_double(&q1, 0);
10621131
int mid = db_column_int(&q1, 1);
10631132
const char *zUuid = db_column_text(&q1, 2);
@@ -1067,24 +1136,24 @@
10671136
char *zAge = human_readable_age(age);
10681137
@ <tr><td>%s(zAge)</td>
10691138
@ <td>
10701139
db_bind_int(&q2, ":mid", mid);
10711140
while( db_step(&q2)==SQLITE_ROW ){
1072
- const char *zFUuid = db_column_text(&q2,0);
1073
- const char *zFile = db_column_text(&q2,1);
1074
- int fid = db_column_int(&q2,2);
1141
+ const char *zFile = db_column_text(&q2,0);
1142
+ @ %z(href("%R/file?name=%T&ci=%!S",zFile,zUuid))%h(zFile)</a> \
10751143
if( showId ){
1076
- @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a> (%d(fid))<br />
1144
+ int fid = db_column_int(&q2,1);
1145
+ @ (%d(fid))<br />
10771146
}else{
1078
- @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a><br />
1147
+ @ </a><br />
10791148
}
10801149
}
10811150
db_reset(&q2);
10821151
@ </td>
10831152
@ <td>
10841153
@ %W(zComment)
1085
- @ (check-in:&nbsp;%z(href("%R/ci/%!S",zUuid))%S(zUuid)</a>,
1154
+ @ (check-in:&nbsp;%z(href("%R/info/%!S",zUuid))%S(zUuid)</a>,
10861155
if( showId ){
10871156
@ id: %d(mid)
10881157
}
10891158
@ user:&nbsp;%z(href("%R/timeline?u=%t&c=%!S&nd",zUser,zUuid))%h(zUser)</a>,
10901159
@ branch:&nbsp;\
10911160
--- src/browse.c
+++ src/browse.c
@@ -59,10 +59,18 @@
59 zOut = sqlite3_mprintf("/%.*s", i-n, &z[n]);
60 sqlite3_result_text(context, zOut, i-n+1, sqlite3_free);
61 }
62 }
63
 
 
 
 
 
 
 
 
64 /*
65 ** Given a pathname which is a relative path from the root of
66 ** the repository to a file or directory, compute a string which
67 ** is an HTML rendering of that path with hyperlinks on each
68 ** directory component of the path where the hyperlink redirects
@@ -76,29 +84,36 @@
76 void hyperlinked_path(
77 const char *zPath, /* Path to render */
78 Blob *pOut, /* Write into this blob */
79 const char *zCI, /* check-in name, or NULL */
80 const char *zURI, /* "dir" or "tree" */
81 const char *zREx /* Extra query parameters */
 
82 ){
83 int i, j;
84 char *zSep = "";
85
86 for(i=0; zPath[i]; i=j){
87 for(j=i; zPath[j] && zPath[j]!='/'; j++){}
88 if( zPath[j] && g.perm.Hyperlink ){
89 if( zCI ){
90 char *zLink = href("%R/%s?name=%#T%s&ci=%!S", zURI, j, zPath, zREx,zCI);
91 blob_appendf(pOut, "%s%z%#h</a>",
92 zSep, zLink, j-i, &zPath[i]);
93 }else{
94 char *zLink = href("%R/%s?name=%#T%s", zURI, j, zPath, zREx);
95 blob_appendf(pOut, "%s%z%#h</a>",
96 zSep, zLink, j-i, &zPath[i]);
97 }
98 }else{
99 blob_appendf(pOut, "%s%#h", zSep, j-i, &zPath[i]);
 
 
 
 
 
 
100 }
101 zSep = "/";
102 while( zPath[j]=='/' ){ j++; }
103 }
104 }
@@ -127,27 +142,24 @@
127 char *zPrefix;
128 Stmt q;
129 const char *zCI = P("ci");
130 int rid = 0;
131 char *zUuid = 0;
132 Blob dirname;
133 Manifest *pM = 0;
134 const char *zSubdirLink;
135 int linkTrunk = 1;
136 int linkTip = 1;
137 HQuery sURI;
 
 
 
138
 
139 if( strcmp(PD("type","flat"),"tree")==0 ){ page_tree(); return; }
140 login_check_credentials();
141 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
142 while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
143 style_header("File List");
144 style_adunit_config(ADUNIT_RIGHT_OK);
145 sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
146 pathelementFunc, 0, 0);
147 url_initialize(&sURI, "dir");
148 cgi_query_parameters_to_url(&sURI);
149
150 /* If the name= parameter is an empty string, make it a NULL pointer */
151 if( zD && strlen(zD)==0 ){ zD = 0; }
152
153 /* If a specific check-in is requested, fetch and parse it. If the
@@ -159,53 +171,79 @@
159 if( pM ){
160 int trunkRid = symbolic_name_to_rid("tag:trunk", "ci");
161 linkTrunk = trunkRid && rid != trunkRid;
162 linkTip = rid != symbolic_name_to_rid("tip", "ci");
163 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
 
 
164 }else{
165 zCI = 0;
166 }
167 }
168
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169 /* Compute the title of the page */
170 blob_zero(&dirname);
171 if( zD ){
172 blob_append(&dirname, "in directory ", -1);
173 hyperlinked_path(zD, &dirname, zCI, "dir", "");
 
 
 
174 zPrefix = mprintf("%s/", zD);
175 style_submenu_element("Top-Level", "%s",
176 url_render(&sURI, "name", 0, 0, 0));
177 }else{
178 blob_append(&dirname, "in the top-level directory", -1);
179 zPrefix = "";
180 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181 if( linkTrunk ){
182 style_submenu_element("Trunk", "%s",
183 url_render(&sURI, "ci", "trunk", 0, 0));
184 }
185 if( linkTip ){
186 style_submenu_element("Tip", "%s", url_render(&sURI, "ci", "tip", 0, 0));
187 }
188 if( zCI ){
189 @ <h2>Files of check-in [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>]
190 @ %s(blob_str(&dirname))
191 if( zD ){
192 @ &nbsp;&nbsp;%z(href("%R/timeline?chng=%T/*", zD))[history]</a>
193 }
194 @ </h2>
195 zSubdirLink = mprintf("%R/dir?ci=%!S&name=%T", zUuid, zPrefix);
196 if( nD==0 ){
197 style_submenu_element("File Ages", "%R/fileage?name=%!S", zUuid);
198 }
199 }else{
200 @ <h2>The union of all files from all check-ins
201 @ %s(blob_str(&dirname))
202 if( zD ){
203 @ &nbsp;&nbsp;%z(href("%R/timeline?chng=%T/*", zD))[history]</a>
204 }
205 @ </h2>
206 zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
207 }
208 style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0));
209 style_submenu_element("Tree-View", "%s",
210 url_render(&sURI, "type", "tree", 0, 0));
211
@@ -282,12 +320,11 @@
282 zFN++;
283 @ <li class="dir">%z(href("%s%T",zSubdirLink,zFN))%h(zFN)</a></li>
284 }else{
285 const char *zLink;
286 if( zCI ){
287 const char *zUuid = db_column_text(&q, 1);
288 zLink = href("%R/artifact/%!S",zUuid);
289 }else{
290 zLink = href("%R/finfo?name=%T%T",zPrefix,zFN);
291 }
292 @ <li class="%z(fileext_class(zFN))">%z(zLink)%h(zFN)</a></li>
293 }
@@ -612,11 +649,15 @@
612 HQuery sURI; /* Hyperlink */
613 int startExpanded; /* True to start out with the tree expanded */
614 int showDirOnly; /* Show directories only. Omit files */
615 int nDir = 0; /* Number of directories. Used for ID attributes */
616 char *zProjectName = db_get("project-name", 0);
 
 
 
617
 
618 if( strcmp(PD("type","flat"),"flat")==0 ){ page_dir(); return; }
619 memset(&sTree, 0, sizeof(sTree));
620 login_check_credentials();
621 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
622 while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
@@ -624,14 +665,12 @@
624 pathelementFunc, 0, 0);
625 url_initialize(&sURI, "tree");
626 cgi_query_parameters_to_url(&sURI);
627 if( PB("nofiles") ){
628 showDirOnly = 1;
629 style_header("Folder Hierarchy");
630 }else{
631 showDirOnly = 0;
632 style_header("File Tree");
633 }
634 style_adunit_config(ADUNIT_RIGHT_OK);
635 if( PB("expand") ){
636 startExpanded = 1;
637 }else{
@@ -660,37 +699,54 @@
660 linkTip = rid != symbolic_name_to_rid("tip", "ci");
661 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
662 rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
663 zNow = db_text("", "SELECT datetime(mtime,toLocal())"
664 " FROM event WHERE objid=%d", rid);
 
 
665 }else{
666 zCI = 0;
667 }
668 }
669 if( zCI==0 ){
670 rNow = db_double(0.0, "SELECT max(mtime) FROM event");
671 zNow = db_text("", "SELECT datetime(max(mtime),toLocal()) FROM event");
672 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
673
674 /* Compute the title of the page */
675 blob_zero(&dirname);
676 if( zD ){
677 blob_append(&dirname, "within directory ", -1);
678 hyperlinked_path(zD, &dirname, zCI, "tree", zREx);
679 if( zRE ) blob_appendf(&dirname, " matching \"%s\"", zRE);
680 style_submenu_element("Top-Level", "%s",
681 url_render(&sURI, "name", 0, 0, 0));
682 }else{
683 if( zRE ){
684 blob_appendf(&dirname, "matching \"%s\"", zRE);
685 }
686 }
687 style_submenu_binary("mtime","Sort By Time","Sort By Filename", 0);
688 if( zCI ){
689 style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0));
690 if( nD==0 && !showDirOnly ){
691 style_submenu_element("File Ages", "%R/fileage?name=%s", zUuid);
692 }
693 }
694 if( linkTrunk ){
695 style_submenu_element("Trunk", "%s",
696 url_render(&sURI, "ci", "trunk", 0, 0));
@@ -745,10 +801,11 @@
745 tree_add_node(&sTree, zName, zUuid, mtime);
746 nFile++;
747 }
748 db_finalize(&q);
749 }
 
750
751 if( showDirOnly ){
752 for(nFile=0, p=sTree.pFirst; p; p=p->pNext){
753 if( p->pChild!=0 && p->nFullName>nD ) nFile++;
754 }
@@ -755,18 +812,24 @@
755 zObjType = "Folders";
756 }else{
757 zObjType = "Files";
758 }
759
760 style_submenu_checkbox("nofiles", "Folders Only", 0, 0);
761
762 if( zCI ){
763 @ <h2>%s(zObjType) from
764 if( sqlite3_strnicmp(zCI, zUuid, (int)strlen(zCI))!=0 ){
765 @ "%h(zCI)"
766 }
767 @ [%z(href("vinfo?name=%!S",zUuid))%S(zUuid)</a>] %s(blob_str(&dirname))
 
 
 
 
 
 
768 }else{
769 int n = db_int(0, "SELECT count(*) FROM plink");
770 @ <h2>%s(zObjType) from all %d(n) check-ins %s(blob_str(&dirname))
771 }
772 if( useMtime ){
@@ -824,11 +887,11 @@
824 nDir++;
825 }else if( !showDirOnly ){
826 const char *zFileClass = fileext_class(p->zName);
827 char *zLink;
828 if( zCI ){
829 zLink = href("%R/artifact/%!S",p->zUuid);
830 }else{
831 zLink = href("%R/finfo?name=%T",p->zFullName);
832 }
833 @ <li class="%z(zFileClass)%s(zLastClass)"><div class="filetreeline">
834 @ %z(zLink)%h(p->zName)</a>
@@ -1001,10 +1064,11 @@
1001 int rid;
1002 const char *zName;
1003 const char *zGlob;
1004 const char *zUuid;
1005 const char *zNow; /* Time of check-in */
 
1006 int showId = PB("showid");
1007 Stmt q1, q2;
1008 double baseTime;
1009 login_check_credentials();
1010 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
@@ -1014,28 +1078,34 @@
1014 rid = symbolic_name_to_rid(zName, "ci");
1015 if( rid==0 ){
1016 fossil_fatal("not a valid check-in: %s", zName);
1017 }
1018 zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
 
1019 baseTime = db_double(0.0,"SELECT mtime FROM event WHERE objid=%d", rid);
1020 zNow = db_text("", "SELECT datetime(mtime,toLocal()) FROM event"
1021 " WHERE objid=%d", rid);
1022 style_submenu_element("Tree-View", "%R/tree?ci=%T&mtime=1&type=tree", zName);
1023 style_header("File Ages");
1024 zGlob = P("glob");
1025 compute_fileage(rid,zGlob);
1026 db_multi_exec("CREATE INDEX fileage_ix1 ON fileage(mid,pathname);");
1027
1028 @ <h1>Files in
1029 @ %z(href("%R/info/%!S",zUuid))[%S(zUuid)]</a>
 
 
 
 
 
 
1030 if( zGlob && zGlob[0] ){
1031 @ that match "%h(zGlob)"
1032 }
1033 @ ordered by age</h1>
1034 @
1035 @ <p>File ages are expressed relative to the
1036 @ %z(href("%R/ci/%!S",zUuid))[%S(zUuid)]</a> check-in time of
1037 @ %z(href("%R/timeline?c=%t",zNow))%s(zNow)</a>.</p>
1038 @
1039 @ <div class='fileage'><table>
1040 @ <tr><th>Age</th><th>Files</th><th>Check-in</th></tr>
1041 db_prepare(&q1,
@@ -1050,14 +1120,13 @@
1050 " AND blob.rid=event.objid\n"
1051 " ORDER BY event.mtime DESC;",
1052 TAG_BRANCH
1053 );
1054 db_prepare(&q2,
1055 "SELECT blob.uuid, filename.name, fileage.fid\n"
1056 " FROM fileage, blob, filename\n"
1057 " WHERE fileage.mid=:mid AND filename.fnid=fileage.fnid"
1058 " AND blob.rid=fileage.fid;"
1059 );
1060 while( db_step(&q1)==SQLITE_ROW ){
1061 double age = baseTime - db_column_double(&q1, 0);
1062 int mid = db_column_int(&q1, 1);
1063 const char *zUuid = db_column_text(&q1, 2);
@@ -1067,24 +1136,24 @@
1067 char *zAge = human_readable_age(age);
1068 @ <tr><td>%s(zAge)</td>
1069 @ <td>
1070 db_bind_int(&q2, ":mid", mid);
1071 while( db_step(&q2)==SQLITE_ROW ){
1072 const char *zFUuid = db_column_text(&q2,0);
1073 const char *zFile = db_column_text(&q2,1);
1074 int fid = db_column_int(&q2,2);
1075 if( showId ){
1076 @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a> (%d(fid))<br />
 
1077 }else{
1078 @ %z(href("%R/artifact/%!S",zFUuid))%h(zFile)</a><br />
1079 }
1080 }
1081 db_reset(&q2);
1082 @ </td>
1083 @ <td>
1084 @ %W(zComment)
1085 @ (check-in:&nbsp;%z(href("%R/ci/%!S",zUuid))%S(zUuid)</a>,
1086 if( showId ){
1087 @ id: %d(mid)
1088 }
1089 @ user:&nbsp;%z(href("%R/timeline?u=%t&c=%!S&nd",zUser,zUuid))%h(zUser)</a>,
1090 @ branch:&nbsp;\
1091
--- src/browse.c
+++ src/browse.c
@@ -59,10 +59,18 @@
59 zOut = sqlite3_mprintf("/%.*s", i-n, &z[n]);
60 sqlite3_result_text(context, zOut, i-n+1, sqlite3_free);
61 }
62 }
63
64 /*
65 ** Flag arguments for hyperlinked_path()
66 */
67 #if INTERFACE
68 # define LINKPATH_FINFO 0x0001 /* Link final term to /finfo */
69 # define LINKPATH_FILE 0x0002 /* Link final term to /file */
70 #endif
71
72 /*
73 ** Given a pathname which is a relative path from the root of
74 ** the repository to a file or directory, compute a string which
75 ** is an HTML rendering of that path with hyperlinks on each
76 ** directory component of the path where the hyperlink redirects
@@ -76,29 +84,36 @@
84 void hyperlinked_path(
85 const char *zPath, /* Path to render */
86 Blob *pOut, /* Write into this blob */
87 const char *zCI, /* check-in name, or NULL */
88 const char *zURI, /* "dir" or "tree" */
89 const char *zREx, /* Extra query parameters */
90 unsigned int mFlags /* Extra flags */
91 ){
92 int i, j;
93 char *zSep = "";
94
95 for(i=0; zPath[i]; i=j){
96 for(j=i; zPath[j] && zPath[j]!='/'; j++){}
97 if( zPath[j]==0 ){
98 if( mFlags & LINKPATH_FILE ){
99 zURI = "file";
100 }else if( mFlags & LINKPATH_FINFO ){
101 zURI = "finfo";
102 }else{
103 blob_appendf(pOut, "/%h", zPath+i);
104 break;
105 }
106 }
107 if( zCI ){
108 char *zLink = href("%R/%s?name=%#T%s&ci=%T", zURI, j, zPath, zREx,zCI);
109 blob_appendf(pOut, "%s%z%#h</a>",
110 zSep, zLink, j-i, &zPath[i]);
111 }else{
112 char *zLink = href("%R/%s?name=%#T%s", zURI, j, zPath, zREx);
113 blob_appendf(pOut, "%s%z%#h</a>",
114 zSep, zLink, j-i, &zPath[i]);
115 }
116 zSep = "/";
117 while( zPath[j]=='/' ){ j++; }
118 }
119 }
@@ -127,27 +142,24 @@
142 char *zPrefix;
143 Stmt q;
144 const char *zCI = P("ci");
145 int rid = 0;
146 char *zUuid = 0;
 
147 Manifest *pM = 0;
148 const char *zSubdirLink;
149 int linkTrunk = 1;
150 int linkTip = 1;
151 HQuery sURI;
152 int isSymbolicCI = 0; /* ci= is symbolic name, not a hash prefix */
153 int isBranchCI = 0; /* True if ci= refers to a branch name */
154 char *zHeader = 0;
155
156 if( zCI && strlen(zCI)==0 ){ zCI = 0; }
157 if( strcmp(PD("type","flat"),"tree")==0 ){ page_tree(); return; }
158 login_check_credentials();
159 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
160 while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
 
 
 
 
 
 
161
162 /* If the name= parameter is an empty string, make it a NULL pointer */
163 if( zD && strlen(zD)==0 ){ zD = 0; }
164
165 /* If a specific check-in is requested, fetch and parse it. If the
@@ -159,53 +171,79 @@
171 if( pM ){
172 int trunkRid = symbolic_name_to_rid("tag:trunk", "ci");
173 linkTrunk = trunkRid && rid != trunkRid;
174 linkTip = rid != symbolic_name_to_rid("tip", "ci");
175 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
176 isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI))!=0);
177 isBranchCI = branch_includes_uuid(zCI, zUuid);
178 }else{
179 zCI = 0;
180 }
181 }
182
183 assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) );
184 if( zD==0 ){
185 if( zCI ){
186 zHeader = mprintf("Top-level Files of %s", zCI);
187 }else{
188 zHeader = mprintf("All Top-level Files");
189 }
190 }else{
191 if( zCI ){
192 zHeader = mprintf("Files in %s/ of %s", zD, zCI);
193 }else{
194 zHeader = mprintf("All File in %s/", zD);
195 }
196 }
197 style_header("%s", zHeader);
198 fossil_free(zHeader);
199 style_adunit_config(ADUNIT_RIGHT_OK);
200 sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
201 pathelementFunc, 0, 0);
202 url_initialize(&sURI, "dir");
203 cgi_query_parameters_to_url(&sURI);
204
205 /* Compute the title of the page */
 
206 if( zD ){
207 Blob dirname;
208 blob_init(&dirname, 0, 0);
209 hyperlinked_path(zD, &dirname, zCI, "dir", "", 0);
210 @ <h2>Files in directory %s(blob_str(&dirname)) \
211 blob_reset(&dirname);
212 zPrefix = mprintf("%s/", zD);
213 style_submenu_element("Top-Level", "%s",
214 url_render(&sURI, "name", 0, 0, 0));
215 }else{
216 @ <h2>Files in the top-level directory \
217 zPrefix = "";
218 }
219 if( zCI ){
220 if( fossil_strcmp(zCI,"tip")==0 ){
221 @ from the %z(href("%R/info?name=%T",zCI))latest check-in</a></h2>
222 }else if( isBranchCI ){
223 @ from the %z(href("%R/info?name=%T",zCI))latest check-in</a> \
224 @ of branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a></h2>
225 }else {
226 @ of check-in %z(href("%R/info?name=%T",zCI))%h(zCI)</a></h2>
227 }
228 zSubdirLink = mprintf("%R/dir?ci=%T&name=%T", zCI, zPrefix);
229 if( nD==0 ){
230 style_submenu_element("File Ages", "%R/fileage?name=%T", zCI);
231 }
232 }else{
233 @ in any check-in</h2>
234 zSubdirLink = mprintf("%R/dir?name=%T", zPrefix);
235 }
236 if( linkTrunk ){
237 style_submenu_element("Trunk", "%s",
238 url_render(&sURI, "ci", "trunk", 0, 0));
239 }
240 if( linkTip ){
241 style_submenu_element("Tip", "%s", url_render(&sURI, "ci", "tip", 0, 0));
242 }
243 if( zD ){
244 style_submenu_element("History","%R/timeline?chng=%T/*", zD);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245 }
246 style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0));
247 style_submenu_element("Tree-View", "%s",
248 url_render(&sURI, "type", "tree", 0, 0));
249
@@ -282,12 +320,11 @@
320 zFN++;
321 @ <li class="dir">%z(href("%s%T",zSubdirLink,zFN))%h(zFN)</a></li>
322 }else{
323 const char *zLink;
324 if( zCI ){
325 zLink = href("%R/file?name=%T%T&ci=%T",zPrefix,zFN,zCI);
 
326 }else{
327 zLink = href("%R/finfo?name=%T%T",zPrefix,zFN);
328 }
329 @ <li class="%z(fileext_class(zFN))">%z(zLink)%h(zFN)</a></li>
330 }
@@ -612,11 +649,15 @@
649 HQuery sURI; /* Hyperlink */
650 int startExpanded; /* True to start out with the tree expanded */
651 int showDirOnly; /* Show directories only. Omit files */
652 int nDir = 0; /* Number of directories. Used for ID attributes */
653 char *zProjectName = db_get("project-name", 0);
654 int isSymbolicCI = 0; /* ci= is a symbolic name, not a hash prefix */
655 int isBranchCI = 0; /* ci= refers to a branch name */
656 char *zHeader = 0;
657
658 if( zCI && strlen(zCI)==0 ){ zCI = 0; }
659 if( strcmp(PD("type","flat"),"flat")==0 ){ page_dir(); return; }
660 memset(&sTree, 0, sizeof(sTree));
661 login_check_credentials();
662 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
663 while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
@@ -624,14 +665,12 @@
665 pathelementFunc, 0, 0);
666 url_initialize(&sURI, "tree");
667 cgi_query_parameters_to_url(&sURI);
668 if( PB("nofiles") ){
669 showDirOnly = 1;
 
670 }else{
671 showDirOnly = 0;
 
672 }
673 style_adunit_config(ADUNIT_RIGHT_OK);
674 if( PB("expand") ){
675 startExpanded = 1;
676 }else{
@@ -660,37 +699,54 @@
699 linkTip = rid != symbolic_name_to_rid("tip", "ci");
700 zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
701 rNow = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
702 zNow = db_text("", "SELECT datetime(mtime,toLocal())"
703 " FROM event WHERE objid=%d", rid);
704 isSymbolicCI = (sqlite3_strnicmp(zUuid, zCI, strlen(zCI)) != 0);
705 isBranchCI = branch_includes_uuid(zCI, zUuid);
706 }else{
707 zCI = 0;
708 }
709 }
710 if( zCI==0 ){
711 rNow = db_double(0.0, "SELECT max(mtime) FROM event");
712 zNow = db_text("", "SELECT datetime(max(mtime),toLocal()) FROM event");
713 }
714
715 assert( isSymbolicCI==0 || (zCI!=0 && zCI[0]!=0) );
716 if( zD==0 ){
717 if( zCI ){
718 zHeader = mprintf("Top-level Files of %s", zCI);
719 }else{
720 zHeader = mprintf("All Top-level Files");
721 }
722 }else{
723 if( zCI ){
724 zHeader = mprintf("Files in %s/ of %s", zD, zCI);
725 }else{
726 zHeader = mprintf("All File in %s/", zD);
727 }
728 }
729 style_header("%s", zHeader);
730 fossil_free(zHeader);
731
732 /* Compute the title of the page */
733 blob_zero(&dirname);
734 if( zD ){
735 blob_append(&dirname, "within directory ", -1);
736 hyperlinked_path(zD, &dirname, zCI, "tree", zREx, 0);
737 if( zRE ) blob_appendf(&dirname, " matching \"%s\"", zRE);
738 style_submenu_element("Top-Level", "%s",
739 url_render(&sURI, "name", 0, 0, 0));
740 }else if( zRE ){
741 blob_appendf(&dirname, "matching \"%s\"", zRE);
 
 
742 }
743 style_submenu_binary("mtime","Sort By Time","Sort By Filename", 0);
744 if( zCI ){
745 style_submenu_element("All", "%s", url_render(&sURI, "ci", 0, 0, 0));
746 if( nD==0 && !showDirOnly ){
747 style_submenu_element("File Ages", "%R/fileage?name=%T", zCI);
748 }
749 }
750 if( linkTrunk ){
751 style_submenu_element("Trunk", "%s",
752 url_render(&sURI, "ci", "trunk", 0, 0));
@@ -745,10 +801,11 @@
801 tree_add_node(&sTree, zName, zUuid, mtime);
802 nFile++;
803 }
804 db_finalize(&q);
805 }
806 style_submenu_checkbox("nofiles", "Folders Only", 0, 0);
807
808 if( showDirOnly ){
809 for(nFile=0, p=sTree.pFirst; p; p=p->pNext){
810 if( p->pChild!=0 && p->nFullName>nD ) nFile++;
811 }
@@ -755,18 +812,24 @@
812 zObjType = "Folders";
813 }else{
814 zObjType = "Files";
815 }
816
817 if( zCI && strcmp(zCI,"tip")==0 ){
818 @ <h2>%s(zObjType) in the %z(href("%R/info?name=tip"))latest check-in</a>
819 }else if( isBranchCI ){
820 @ <h2>%s(zObjType) in the %z(href("%R/info?name=%T",zCI))latest check-in\
821 @ </a> for branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a>
822 if( blob_size(&dirname) ){
823 @ and %s(blob_str(&dirname))</h2>
824 }
825 }else if( zCI ){
826 @ <h2>%s(zObjType) for check-in \
827 @ %z(href("%R/info?name=%T",zCI))%h(zCI)</a></h2>
828 if( blob_size(&dirname) ){
829 @ and %s(blob_str(&dirname))</h2>
830 }
831 }else{
832 int n = db_int(0, "SELECT count(*) FROM plink");
833 @ <h2>%s(zObjType) from all %d(n) check-ins %s(blob_str(&dirname))
834 }
835 if( useMtime ){
@@ -824,11 +887,11 @@
887 nDir++;
888 }else if( !showDirOnly ){
889 const char *zFileClass = fileext_class(p->zName);
890 char *zLink;
891 if( zCI ){
892 zLink = href("%R/file?name=%T&ci=%T",p->zFullName,zCI);
893 }else{
894 zLink = href("%R/finfo?name=%T",p->zFullName);
895 }
896 @ <li class="%z(zFileClass)%s(zLastClass)"><div class="filetreeline">
897 @ %z(zLink)%h(p->zName)</a>
@@ -1001,10 +1064,11 @@
1064 int rid;
1065 const char *zName;
1066 const char *zGlob;
1067 const char *zUuid;
1068 const char *zNow; /* Time of check-in */
1069 int isBranchCI; /* name= is a branch name */
1070 int showId = PB("showid");
1071 Stmt q1, q2;
1072 double baseTime;
1073 login_check_credentials();
1074 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
@@ -1014,28 +1078,34 @@
1078 rid = symbolic_name_to_rid(zName, "ci");
1079 if( rid==0 ){
1080 fossil_fatal("not a valid check-in: %s", zName);
1081 }
1082 zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", rid);
1083 isBranchCI = branch_includes_uuid(zName,zUuid);
1084 baseTime = db_double(0.0,"SELECT mtime FROM event WHERE objid=%d", rid);
1085 zNow = db_text("", "SELECT datetime(mtime,toLocal()) FROM event"
1086 " WHERE objid=%d", rid);
1087 style_submenu_element("Tree-View", "%R/tree?ci=%T&mtime=1&type=tree", zName);
1088 style_header("File Ages");
1089 zGlob = P("glob");
1090 compute_fileage(rid,zGlob);
1091 db_multi_exec("CREATE INDEX fileage_ix1 ON fileage(mid,pathname);");
1092
1093 if( fossil_strcmp(zName,"tip")==0 ){
1094 @ <h1>Files in the %z(href("%R/info?name=tip"))latest check-in</a>
1095 }else if( isBranchCI ){
1096 @ <h1>Files in the %z(href("%R/info?name=%T",zName))latest check-in</a>
1097 @ of branch %z(href("%R/timeline?r=%T",zName))%h(zName)</a>
1098 }else{
1099 @ <h1>Files in check-in %z(href("%R/info?name=%T",zName))%h(zName)</a>
1100 }
1101 if( zGlob && zGlob[0] ){
1102 @ that match "%h(zGlob)"
1103 }
1104 @ ordered by age</h1>
1105 @
1106 @ <p>File ages are expressed relative to the check-in time of
 
1107 @ %z(href("%R/timeline?c=%t",zNow))%s(zNow)</a>.</p>
1108 @
1109 @ <div class='fileage'><table>
1110 @ <tr><th>Age</th><th>Files</th><th>Check-in</th></tr>
1111 db_prepare(&q1,
@@ -1050,14 +1120,13 @@
1120 " AND blob.rid=event.objid\n"
1121 " ORDER BY event.mtime DESC;",
1122 TAG_BRANCH
1123 );
1124 db_prepare(&q2,
1125 "SELECT filename.name, fileage.fid\n"
1126 " FROM fileage, filename\n"
1127 " WHERE fileage.mid=:mid AND filename.fnid=fileage.fnid"
 
1128 );
1129 while( db_step(&q1)==SQLITE_ROW ){
1130 double age = baseTime - db_column_double(&q1, 0);
1131 int mid = db_column_int(&q1, 1);
1132 const char *zUuid = db_column_text(&q1, 2);
@@ -1067,24 +1136,24 @@
1136 char *zAge = human_readable_age(age);
1137 @ <tr><td>%s(zAge)</td>
1138 @ <td>
1139 db_bind_int(&q2, ":mid", mid);
1140 while( db_step(&q2)==SQLITE_ROW ){
1141 const char *zFile = db_column_text(&q2,0);
1142 @ %z(href("%R/file?name=%T&ci=%!S",zFile,zUuid))%h(zFile)</a> \
 
1143 if( showId ){
1144 int fid = db_column_int(&q2,1);
1145 @ (%d(fid))<br />
1146 }else{
1147 @ </a><br />
1148 }
1149 }
1150 db_reset(&q2);
1151 @ </td>
1152 @ <td>
1153 @ %W(zComment)
1154 @ (check-in:&nbsp;%z(href("%R/info/%!S",zUuid))%S(zUuid)</a>,
1155 if( showId ){
1156 @ id: %d(mid)
1157 }
1158 @ user:&nbsp;%z(href("%R/timeline?u=%t&c=%!S&nd",zUser,zUuid))%h(zUser)</a>,
1159 @ branch:&nbsp;\
1160
+10 -2
--- src/builtin.c
+++ src/builtin.c
@@ -55,17 +55,25 @@
5555
return (char*)builtin_file(zFilename, 0);
5656
}
5757
5858
/*
5959
** COMMAND: test-builtin-list
60
+**
61
+** If -verbose is used, it outputs a line at the end
62
+** with the total item count and size.
6063
**
6164
** List the names and sizes of all built-in resources.
6265
*/
6366
void test_builtin_list(void){
64
- int i;
67
+ int i, size = 0;;
6568
for(i=0; i<count(aBuiltinFiles); i++){
66
- fossil_print("%-30s %6d\n", aBuiltinFiles[i].zName,aBuiltinFiles[i].nByte);
69
+ const int n = aBuiltinFiles[i].nByte;
70
+ fossil_print("%-30s %6d\n", aBuiltinFiles[i].zName,n);
71
+ size += n;
72
+ }
73
+ if(find_option("verbose","v",0)!=0){
74
+ fossil_print("%d entries totaling %d bytes\n", i, size);
6775
}
6876
}
6977
7078
/*
7179
** WEBPAGE: test-builtin-files
7280
--- src/builtin.c
+++ src/builtin.c
@@ -55,17 +55,25 @@
55 return (char*)builtin_file(zFilename, 0);
56 }
57
58 /*
59 ** COMMAND: test-builtin-list
 
 
 
60 **
61 ** List the names and sizes of all built-in resources.
62 */
63 void test_builtin_list(void){
64 int i;
65 for(i=0; i<count(aBuiltinFiles); i++){
66 fossil_print("%-30s %6d\n", aBuiltinFiles[i].zName,aBuiltinFiles[i].nByte);
 
 
 
 
 
67 }
68 }
69
70 /*
71 ** WEBPAGE: test-builtin-files
72
--- src/builtin.c
+++ src/builtin.c
@@ -55,17 +55,25 @@
55 return (char*)builtin_file(zFilename, 0);
56 }
57
58 /*
59 ** COMMAND: test-builtin-list
60 **
61 ** If -verbose is used, it outputs a line at the end
62 ** with the total item count and size.
63 **
64 ** List the names and sizes of all built-in resources.
65 */
66 void test_builtin_list(void){
67 int i, size = 0;;
68 for(i=0; i<count(aBuiltinFiles); i++){
69 const int n = aBuiltinFiles[i].nByte;
70 fossil_print("%-30s %6d\n", aBuiltinFiles[i].zName,n);
71 size += n;
72 }
73 if(find_option("verbose","v",0)!=0){
74 fossil_print("%d entries totaling %d bytes\n", i, size);
75 }
76 }
77
78 /*
79 ** WEBPAGE: test-builtin-files
80
--- src/capabilities.c
+++ src/capabilities.c
@@ -366,11 +366,11 @@
366366
CapabilityString *pCap;
367367
char *zSelfCap;
368368
char *zPubPages = db_get("public-pages",0);
369369
int hasPubPages = zPubPages && zPubPages[0];
370370
371
- pCap = capability_add(0, db_get("default-perms",0));
371
+ pCap = capability_add(0, db_get("default-perms","u"));
372372
capability_expand(pCap);
373373
zSelfCap = capability_string(pCap);
374374
capability_free(pCap);
375375
376376
db_prepare(&q,
377377
--- src/capabilities.c
+++ src/capabilities.c
@@ -366,11 +366,11 @@
366 CapabilityString *pCap;
367 char *zSelfCap;
368 char *zPubPages = db_get("public-pages",0);
369 int hasPubPages = zPubPages && zPubPages[0];
370
371 pCap = capability_add(0, db_get("default-perms",0));
372 capability_expand(pCap);
373 zSelfCap = capability_string(pCap);
374 capability_free(pCap);
375
376 db_prepare(&q,
377
--- src/capabilities.c
+++ src/capabilities.c
@@ -366,11 +366,11 @@
366 CapabilityString *pCap;
367 char *zSelfCap;
368 char *zPubPages = db_get("public-pages",0);
369 int hasPubPages = zPubPages && zPubPages[0];
370
371 pCap = capability_add(0, db_get("default-perms","u"));
372 capability_expand(pCap);
373 zSelfCap = capability_string(pCap);
374 capability_free(pCap);
375
376 db_prepare(&q,
377
+11 -10
--- src/cgi.c
+++ src/cgi.c
@@ -174,12 +174,12 @@
174174
}
175175
176176
/*
177177
** Additional information used to form the HTTP reply
178178
*/
179
-static const char *zContentType = "text/html"; /* Content type of the reply */
180
-static const char *zReplyStatus = "OK"; /* Reply status description */
179
+static const char *zContentType = "text/html"; /* Content type of the reply */
180
+static const char *zReplyStatus = "OK"; /* Reply status description */
181181
static int iReplyStatus = 200; /* Reply status code */
182182
static Blob extraHeader = BLOB_INITIALIZER; /* Extra header text */
183183
static int rangeStart = 0; /* Start of Range: */
184184
static int rangeEnd = 0; /* End of Range: plus 1 */
185185
@@ -293,11 +293,11 @@
293293
}
294294
if( g.isConst ){
295295
/* isConst means that the reply is guaranteed to be invariant, even
296296
** after configuration changes and/or Fossil binary recompiles. */
297297
fprintf(g.httpOut, "Cache-Control: max-age=31536000\r\n");
298
- }else if( etag_tag()!=0 ){
298
+ }else if( etag_tag()[0]!=0 ){
299299
fprintf(g.httpOut, "ETag: %s\r\n", etag_tag());
300300
fprintf(g.httpOut, "Cache-Control: max-age=%d\r\n", etag_maxage());
301301
}else{
302302
fprintf(g.httpOut, "Cache-control: no-cache\r\n");
303303
}
@@ -329,17 +329,17 @@
329329
*/
330330
331331
/* Content intended for logged in users should only be cached in
332332
** the browser, not some shared location.
333333
*/
334
- fprintf(g.httpOut, "Content-Type: %s; charset=utf-8\r\n", zContentType);
335
- if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){
336
- cgi_combine_header_and_body();
337
- blob_compress(&cgiContent[0], &cgiContent[0]);
338
- }
339
-
340334
if( iReplyStatus!=304 ) {
335
+ fprintf(g.httpOut, "Content-Type: %s; charset=utf-8\r\n", zContentType);
336
+ if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){
337
+ cgi_combine_header_and_body();
338
+ blob_compress(&cgiContent[0], &cgiContent[0]);
339
+ }
340
+
341341
if( is_gzippable() && iReplyStatus!=206 ){
342342
int i;
343343
gzip_begin(0);
344344
for( i=0; i<2; i++ ){
345345
int size = blob_size(&cgiContent[i]);
@@ -359,11 +359,12 @@
359359
fprintf(g.httpOut, "Content-Length: %d\r\n", total_size);
360360
}else{
361361
total_size = 0;
362362
}
363363
fprintf(g.httpOut, "\r\n");
364
- if( total_size>0 && iReplyStatus != 304
364
+ if( total_size>0
365
+ && iReplyStatus!=304
365366
&& fossil_strcmp(P("REQUEST_METHOD"),"HEAD")!=0
366367
){
367368
int i, size;
368369
for(i=0; i<2; i++){
369370
size = blob_size(&cgiContent[i]);
370371
--- src/cgi.c
+++ src/cgi.c
@@ -174,12 +174,12 @@
174 }
175
176 /*
177 ** Additional information used to form the HTTP reply
178 */
179 static const char *zContentType = "text/html"; /* Content type of the reply */
180 static const char *zReplyStatus = "OK"; /* Reply status description */
181 static int iReplyStatus = 200; /* Reply status code */
182 static Blob extraHeader = BLOB_INITIALIZER; /* Extra header text */
183 static int rangeStart = 0; /* Start of Range: */
184 static int rangeEnd = 0; /* End of Range: plus 1 */
185
@@ -293,11 +293,11 @@
293 }
294 if( g.isConst ){
295 /* isConst means that the reply is guaranteed to be invariant, even
296 ** after configuration changes and/or Fossil binary recompiles. */
297 fprintf(g.httpOut, "Cache-Control: max-age=31536000\r\n");
298 }else if( etag_tag()!=0 ){
299 fprintf(g.httpOut, "ETag: %s\r\n", etag_tag());
300 fprintf(g.httpOut, "Cache-Control: max-age=%d\r\n", etag_maxage());
301 }else{
302 fprintf(g.httpOut, "Cache-control: no-cache\r\n");
303 }
@@ -329,17 +329,17 @@
329 */
330
331 /* Content intended for logged in users should only be cached in
332 ** the browser, not some shared location.
333 */
334 fprintf(g.httpOut, "Content-Type: %s; charset=utf-8\r\n", zContentType);
335 if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){
336 cgi_combine_header_and_body();
337 blob_compress(&cgiContent[0], &cgiContent[0]);
338 }
339
340 if( iReplyStatus!=304 ) {
 
 
 
 
 
 
341 if( is_gzippable() && iReplyStatus!=206 ){
342 int i;
343 gzip_begin(0);
344 for( i=0; i<2; i++ ){
345 int size = blob_size(&cgiContent[i]);
@@ -359,11 +359,12 @@
359 fprintf(g.httpOut, "Content-Length: %d\r\n", total_size);
360 }else{
361 total_size = 0;
362 }
363 fprintf(g.httpOut, "\r\n");
364 if( total_size>0 && iReplyStatus != 304
 
365 && fossil_strcmp(P("REQUEST_METHOD"),"HEAD")!=0
366 ){
367 int i, size;
368 for(i=0; i<2; i++){
369 size = blob_size(&cgiContent[i]);
370
--- src/cgi.c
+++ src/cgi.c
@@ -174,12 +174,12 @@
174 }
175
176 /*
177 ** Additional information used to form the HTTP reply
178 */
179 static const char *zContentType = "text/html"; /* Content type of the reply */
180 static const char *zReplyStatus = "OK"; /* Reply status description */
181 static int iReplyStatus = 200; /* Reply status code */
182 static Blob extraHeader = BLOB_INITIALIZER; /* Extra header text */
183 static int rangeStart = 0; /* Start of Range: */
184 static int rangeEnd = 0; /* End of Range: plus 1 */
185
@@ -293,11 +293,11 @@
293 }
294 if( g.isConst ){
295 /* isConst means that the reply is guaranteed to be invariant, even
296 ** after configuration changes and/or Fossil binary recompiles. */
297 fprintf(g.httpOut, "Cache-Control: max-age=31536000\r\n");
298 }else if( etag_tag()[0]!=0 ){
299 fprintf(g.httpOut, "ETag: %s\r\n", etag_tag());
300 fprintf(g.httpOut, "Cache-Control: max-age=%d\r\n", etag_maxage());
301 }else{
302 fprintf(g.httpOut, "Cache-control: no-cache\r\n");
303 }
@@ -329,17 +329,17 @@
329 */
330
331 /* Content intended for logged in users should only be cached in
332 ** the browser, not some shared location.
333 */
 
 
 
 
 
 
334 if( iReplyStatus!=304 ) {
335 fprintf(g.httpOut, "Content-Type: %s; charset=utf-8\r\n", zContentType);
336 if( fossil_strcmp(zContentType,"application/x-fossil")==0 ){
337 cgi_combine_header_and_body();
338 blob_compress(&cgiContent[0], &cgiContent[0]);
339 }
340
341 if( is_gzippable() && iReplyStatus!=206 ){
342 int i;
343 gzip_begin(0);
344 for( i=0; i<2; i++ ){
345 int size = blob_size(&cgiContent[i]);
@@ -359,11 +359,12 @@
359 fprintf(g.httpOut, "Content-Length: %d\r\n", total_size);
360 }else{
361 total_size = 0;
362 }
363 fprintf(g.httpOut, "\r\n");
364 if( total_size>0
365 && iReplyStatus!=304
366 && fossil_strcmp(P("REQUEST_METHOD"),"HEAD")!=0
367 ){
368 int i, size;
369 for(i=0; i<2; i++){
370 size = blob_size(&cgiContent[i]);
371
+38 -14
--- src/checkin.c
+++ src/checkin.c
@@ -801,11 +801,10 @@
801801
}
802802
if (nullSeparateFlag)
803803
separator='\0';
804804
if( showAge ){
805805
fossil_print("%s%s %s%c", type, db_column_text(&q, 5), zPathname, separator);
806
- if (nullSeparateFlag) putchar('\0');
807806
}else{
808807
fossil_print("%s%s%c", type, zPathname, separator);
809808
}
810809
free(zFullName);
811810
}
@@ -1681,10 +1680,18 @@
16811680
while( db_step(&q)==SQLITE_ROW ){
16821681
const char *zIntegrateUuid = db_column_text(&q, 0);
16831682
int rid = db_column_int(&q, 1);
16841683
if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref "
16851684
" WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){
1685
+#if 0
1686
+ /* Make sure the check-in manifest of the resulting merge child does not
1687
+ ** include a +close tag referring to the leaf check-in on a private
1688
+ ** branch, so as not to generate a missing artifact reference on
1689
+ ** repository clones without that private branch. The merge command
1690
+ ** should have dropped the --integrate option, at this point. */
1691
+ assert( !content_is_private(rid) );
1692
+#endif
16861693
blob_appendf(pOut, "T +closed %s\n", zIntegrateUuid);
16871694
}
16881695
}
16891696
db_finalize(&q);
16901697
@@ -2048,10 +2055,12 @@
20482055
const char *zComment; /* Check-in comment */
20492056
Stmt q; /* Various queries */
20502057
char *zUuid; /* UUID of the new check-in */
20512058
int useHash = 0; /* True to verify file status using hashing */
20522059
int noSign = 0; /* True to omit signing the manifest using GPG */
2060
+ int privateFlag = 0; /* True if the --private option is present */
2061
+ int privateParent = 0; /* True if the parent check-in is private */
20532062
int isAMerge = 0; /* True if checking in a merge */
20542063
int noWarningFlag = 0; /* True if skipping all warnings */
20552064
int noPrompt = 0; /* True if skipping all prompts */
20562065
int forceFlag = 0; /* Undocumented: Disables all checks */
20572066
int forceDelta = 0; /* Force a delta-manifest */
@@ -2085,14 +2094,20 @@
20852094
memset(&sCiInfo, 0, sizeof(sCiInfo));
20862095
url_proxy_options();
20872096
/* --sha1sum is an undocumented alias for --hash for backwards compatiblity */
20882097
useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
20892098
noSign = find_option("nosign",0,0)!=0;
2099
+ privateFlag = find_option("private",0,0)!=0;
20902100
forceDelta = find_option("delta",0,0)!=0;
20912101
forceBaseline = find_option("baseline",0,0)!=0;
2092
- if( forceDelta && forceBaseline ){
2093
- fossil_fatal("cannot use --delta and --baseline together");
2102
+ if( forceDelta ){
2103
+ if( forceBaseline ){
2104
+ fossil_fatal("cannot use --delta and --baseline together");
2105
+ }
2106
+ if( db_get_boolean("forbid-delta-manifests",0) ){
2107
+ fossil_fatal("delta manifests are prohibited in this repository");
2108
+ }
20942109
}
20952110
dryRunFlag = find_option("dry-run","n",0)!=0;
20962111
if( !dryRunFlag ){
20972112
dryRunFlag = find_option("test",0,0)!=0; /* deprecated */
20982113
}
@@ -2117,17 +2132,10 @@
21172132
sizeof(char*)*(nTag+2));
21182133
sCiInfo.azTag[nTag++] = zTag;
21192134
sCiInfo.azTag[nTag] = 0;
21202135
}
21212136
zComFile = find_option("message-file", "M", 1);
2122
- if( find_option("private",0,0) ){
2123
- g.markPrivate = 1;
2124
- if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private";
2125
- if( sCiInfo.zBrClr==0 && sCiInfo.zColor==0 ){
2126
- sCiInfo.zBrClr = "#fec084"; /* Orange */
2127
- }
2128
- }
21292137
sCiInfo.zDateOvrd = find_option("date-override",0,1);
21302138
sCiInfo.zUserOvrd = find_option("user-override",0,1);
21312139
db_must_be_within_tree();
21322140
noSign = db_get_boolean("omitsign", 0)|noSign;
21332141
if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
@@ -2137,15 +2145,25 @@
21372145
21382146
/* Get the ID of the parent manifest artifact */
21392147
vid = db_lget_int("checkout", 0);
21402148
if( vid==0 ){
21412149
useCksum = 1;
2142
- if( sCiInfo.zBranch==0 ) {
2150
+ if( privateFlag==0 && sCiInfo.zBranch==0 ) {
21432151
sCiInfo.zBranch=db_get("main-branch", 0);
21442152
}
2145
- }else if( content_is_private(vid) ){
2146
- g.markPrivate = 1;
2153
+ }else{
2154
+ privateParent = content_is_private(vid);
2155
+ }
2156
+
2157
+ /* Track the "private" status */
2158
+ g.markPrivate = privateFlag || privateParent;
2159
+ if( privateFlag && !privateParent ){
2160
+ /* Apply default branch name ("private") and color ("orange") if not
2161
+ ** specified otherwise on the command-line, and if the parent is not
2162
+ ** already private. */
2163
+ if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private";
2164
+ if( sCiInfo.zBrClr==0 && sCiInfo.zColor==0 ) sCiInfo.zBrClr = "#fec084";
21472165
}
21482166
21492167
/* Do not allow the creation of a new branch using an existing open
21502168
** branch name unless the --force flag is used */
21512169
if( sCiInfo.zBranch!=0
@@ -2168,11 +2186,14 @@
21682186
** manifest) can continue to use this repository, do not create a new
21692187
** delta-manifest unless this repository already contains one or more
21702188
** delta-manifests, or unless the delta-manifest is explicitly requested
21712189
** by the --delta option.
21722190
*/
2173
- if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){
2191
+ if( !forceDelta
2192
+ && !db_get_boolean("seen-delta-manifest",0)
2193
+ && !db_get_boolean("forbid-delta-manifests",0)
2194
+ ){
21742195
forceBaseline = 1;
21752196
}
21762197
21772198
/*
21782199
** Autosync if autosync is enabled and this is not a private check-in.
@@ -2642,10 +2663,11 @@
26422663
/* Commit */
26432664
db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'");
26442665
db_multi_exec("PRAGMA repository.application_id=252006673;");
26452666
db_multi_exec("PRAGMA localdb.application_id=252006674;");
26462667
if( dryRunFlag ){
2668
+ leaf_ambiguity_warning(nvid,nvid);
26472669
db_end_transaction(1);
26482670
exit(1);
26492671
}
26502672
db_end_transaction(0);
26512673
@@ -2664,7 +2686,9 @@
26642686
int nTries = db_get_int("autosync-tries",1);
26652687
autosync_loop(syncFlags, nTries, 0);
26662688
}
26672689
if( count_nonbranch_children(vid)>1 ){
26682690
fossil_print("**** warning: a fork has occurred *****\n");
2691
+ }else{
2692
+ leaf_ambiguity_warning(nvid,nvid);
26692693
}
26702694
}
26712695
--- src/checkin.c
+++ src/checkin.c
@@ -801,11 +801,10 @@
801 }
802 if (nullSeparateFlag)
803 separator='\0';
804 if( showAge ){
805 fossil_print("%s%s %s%c", type, db_column_text(&q, 5), zPathname, separator);
806 if (nullSeparateFlag) putchar('\0');
807 }else{
808 fossil_print("%s%s%c", type, zPathname, separator);
809 }
810 free(zFullName);
811 }
@@ -1681,10 +1680,18 @@
1681 while( db_step(&q)==SQLITE_ROW ){
1682 const char *zIntegrateUuid = db_column_text(&q, 0);
1683 int rid = db_column_int(&q, 1);
1684 if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref "
1685 " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){
 
 
 
 
 
 
 
 
1686 blob_appendf(pOut, "T +closed %s\n", zIntegrateUuid);
1687 }
1688 }
1689 db_finalize(&q);
1690
@@ -2048,10 +2055,12 @@
2048 const char *zComment; /* Check-in comment */
2049 Stmt q; /* Various queries */
2050 char *zUuid; /* UUID of the new check-in */
2051 int useHash = 0; /* True to verify file status using hashing */
2052 int noSign = 0; /* True to omit signing the manifest using GPG */
 
 
2053 int isAMerge = 0; /* True if checking in a merge */
2054 int noWarningFlag = 0; /* True if skipping all warnings */
2055 int noPrompt = 0; /* True if skipping all prompts */
2056 int forceFlag = 0; /* Undocumented: Disables all checks */
2057 int forceDelta = 0; /* Force a delta-manifest */
@@ -2085,14 +2094,20 @@
2085 memset(&sCiInfo, 0, sizeof(sCiInfo));
2086 url_proxy_options();
2087 /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */
2088 useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
2089 noSign = find_option("nosign",0,0)!=0;
 
2090 forceDelta = find_option("delta",0,0)!=0;
2091 forceBaseline = find_option("baseline",0,0)!=0;
2092 if( forceDelta && forceBaseline ){
2093 fossil_fatal("cannot use --delta and --baseline together");
 
 
 
 
 
2094 }
2095 dryRunFlag = find_option("dry-run","n",0)!=0;
2096 if( !dryRunFlag ){
2097 dryRunFlag = find_option("test",0,0)!=0; /* deprecated */
2098 }
@@ -2117,17 +2132,10 @@
2117 sizeof(char*)*(nTag+2));
2118 sCiInfo.azTag[nTag++] = zTag;
2119 sCiInfo.azTag[nTag] = 0;
2120 }
2121 zComFile = find_option("message-file", "M", 1);
2122 if( find_option("private",0,0) ){
2123 g.markPrivate = 1;
2124 if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private";
2125 if( sCiInfo.zBrClr==0 && sCiInfo.zColor==0 ){
2126 sCiInfo.zBrClr = "#fec084"; /* Orange */
2127 }
2128 }
2129 sCiInfo.zDateOvrd = find_option("date-override",0,1);
2130 sCiInfo.zUserOvrd = find_option("user-override",0,1);
2131 db_must_be_within_tree();
2132 noSign = db_get_boolean("omitsign", 0)|noSign;
2133 if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
@@ -2137,15 +2145,25 @@
2137
2138 /* Get the ID of the parent manifest artifact */
2139 vid = db_lget_int("checkout", 0);
2140 if( vid==0 ){
2141 useCksum = 1;
2142 if( sCiInfo.zBranch==0 ) {
2143 sCiInfo.zBranch=db_get("main-branch", 0);
2144 }
2145 }else if( content_is_private(vid) ){
2146 g.markPrivate = 1;
 
 
 
 
 
 
 
 
 
 
2147 }
2148
2149 /* Do not allow the creation of a new branch using an existing open
2150 ** branch name unless the --force flag is used */
2151 if( sCiInfo.zBranch!=0
@@ -2168,11 +2186,14 @@
2168 ** manifest) can continue to use this repository, do not create a new
2169 ** delta-manifest unless this repository already contains one or more
2170 ** delta-manifests, or unless the delta-manifest is explicitly requested
2171 ** by the --delta option.
2172 */
2173 if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){
 
 
 
2174 forceBaseline = 1;
2175 }
2176
2177 /*
2178 ** Autosync if autosync is enabled and this is not a private check-in.
@@ -2642,10 +2663,11 @@
2642 /* Commit */
2643 db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'");
2644 db_multi_exec("PRAGMA repository.application_id=252006673;");
2645 db_multi_exec("PRAGMA localdb.application_id=252006674;");
2646 if( dryRunFlag ){
 
2647 db_end_transaction(1);
2648 exit(1);
2649 }
2650 db_end_transaction(0);
2651
@@ -2664,7 +2686,9 @@
2664 int nTries = db_get_int("autosync-tries",1);
2665 autosync_loop(syncFlags, nTries, 0);
2666 }
2667 if( count_nonbranch_children(vid)>1 ){
2668 fossil_print("**** warning: a fork has occurred *****\n");
 
 
2669 }
2670 }
2671
--- src/checkin.c
+++ src/checkin.c
@@ -801,11 +801,10 @@
801 }
802 if (nullSeparateFlag)
803 separator='\0';
804 if( showAge ){
805 fossil_print("%s%s %s%c", type, db_column_text(&q, 5), zPathname, separator);
 
806 }else{
807 fossil_print("%s%s%c", type, zPathname, separator);
808 }
809 free(zFullName);
810 }
@@ -1681,10 +1680,18 @@
1680 while( db_step(&q)==SQLITE_ROW ){
1681 const char *zIntegrateUuid = db_column_text(&q, 0);
1682 int rid = db_column_int(&q, 1);
1683 if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref "
1684 " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){
1685 #if 0
1686 /* Make sure the check-in manifest of the resulting merge child does not
1687 ** include a +close tag referring to the leaf check-in on a private
1688 ** branch, so as not to generate a missing artifact reference on
1689 ** repository clones without that private branch. The merge command
1690 ** should have dropped the --integrate option, at this point. */
1691 assert( !content_is_private(rid) );
1692 #endif
1693 blob_appendf(pOut, "T +closed %s\n", zIntegrateUuid);
1694 }
1695 }
1696 db_finalize(&q);
1697
@@ -2048,10 +2055,12 @@
2055 const char *zComment; /* Check-in comment */
2056 Stmt q; /* Various queries */
2057 char *zUuid; /* UUID of the new check-in */
2058 int useHash = 0; /* True to verify file status using hashing */
2059 int noSign = 0; /* True to omit signing the manifest using GPG */
2060 int privateFlag = 0; /* True if the --private option is present */
2061 int privateParent = 0; /* True if the parent check-in is private */
2062 int isAMerge = 0; /* True if checking in a merge */
2063 int noWarningFlag = 0; /* True if skipping all warnings */
2064 int noPrompt = 0; /* True if skipping all prompts */
2065 int forceFlag = 0; /* Undocumented: Disables all checks */
2066 int forceDelta = 0; /* Force a delta-manifest */
@@ -2085,14 +2094,20 @@
2094 memset(&sCiInfo, 0, sizeof(sCiInfo));
2095 url_proxy_options();
2096 /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */
2097 useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
2098 noSign = find_option("nosign",0,0)!=0;
2099 privateFlag = find_option("private",0,0)!=0;
2100 forceDelta = find_option("delta",0,0)!=0;
2101 forceBaseline = find_option("baseline",0,0)!=0;
2102 if( forceDelta ){
2103 if( forceBaseline ){
2104 fossil_fatal("cannot use --delta and --baseline together");
2105 }
2106 if( db_get_boolean("forbid-delta-manifests",0) ){
2107 fossil_fatal("delta manifests are prohibited in this repository");
2108 }
2109 }
2110 dryRunFlag = find_option("dry-run","n",0)!=0;
2111 if( !dryRunFlag ){
2112 dryRunFlag = find_option("test",0,0)!=0; /* deprecated */
2113 }
@@ -2117,17 +2132,10 @@
2132 sizeof(char*)*(nTag+2));
2133 sCiInfo.azTag[nTag++] = zTag;
2134 sCiInfo.azTag[nTag] = 0;
2135 }
2136 zComFile = find_option("message-file", "M", 1);
 
 
 
 
 
 
 
2137 sCiInfo.zDateOvrd = find_option("date-override",0,1);
2138 sCiInfo.zUserOvrd = find_option("user-override",0,1);
2139 db_must_be_within_tree();
2140 noSign = db_get_boolean("omitsign", 0)|noSign;
2141 if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
@@ -2137,15 +2145,25 @@
2145
2146 /* Get the ID of the parent manifest artifact */
2147 vid = db_lget_int("checkout", 0);
2148 if( vid==0 ){
2149 useCksum = 1;
2150 if( privateFlag==0 && sCiInfo.zBranch==0 ) {
2151 sCiInfo.zBranch=db_get("main-branch", 0);
2152 }
2153 }else{
2154 privateParent = content_is_private(vid);
2155 }
2156
2157 /* Track the "private" status */
2158 g.markPrivate = privateFlag || privateParent;
2159 if( privateFlag && !privateParent ){
2160 /* Apply default branch name ("private") and color ("orange") if not
2161 ** specified otherwise on the command-line, and if the parent is not
2162 ** already private. */
2163 if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private";
2164 if( sCiInfo.zBrClr==0 && sCiInfo.zColor==0 ) sCiInfo.zBrClr = "#fec084";
2165 }
2166
2167 /* Do not allow the creation of a new branch using an existing open
2168 ** branch name unless the --force flag is used */
2169 if( sCiInfo.zBranch!=0
@@ -2168,11 +2186,14 @@
2186 ** manifest) can continue to use this repository, do not create a new
2187 ** delta-manifest unless this repository already contains one or more
2188 ** delta-manifests, or unless the delta-manifest is explicitly requested
2189 ** by the --delta option.
2190 */
2191 if( !forceDelta
2192 && !db_get_boolean("seen-delta-manifest",0)
2193 && !db_get_boolean("forbid-delta-manifests",0)
2194 ){
2195 forceBaseline = 1;
2196 }
2197
2198 /*
2199 ** Autosync if autosync is enabled and this is not a private check-in.
@@ -2642,10 +2663,11 @@
2663 /* Commit */
2664 db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'");
2665 db_multi_exec("PRAGMA repository.application_id=252006673;");
2666 db_multi_exec("PRAGMA localdb.application_id=252006674;");
2667 if( dryRunFlag ){
2668 leaf_ambiguity_warning(nvid,nvid);
2669 db_end_transaction(1);
2670 exit(1);
2671 }
2672 db_end_transaction(0);
2673
@@ -2664,7 +2686,9 @@
2686 int nTries = db_get_int("autosync-tries",1);
2687 autosync_loop(syncFlags, nTries, 0);
2688 }
2689 if( count_nonbranch_children(vid)>1 ){
2690 fossil_print("**** warning: a fork has occurred *****\n");
2691 }else{
2692 leaf_ambiguity_warning(nvid,nvid);
2693 }
2694 }
2695
+38 -14
--- src/checkin.c
+++ src/checkin.c
@@ -801,11 +801,10 @@
801801
}
802802
if (nullSeparateFlag)
803803
separator='\0';
804804
if( showAge ){
805805
fossil_print("%s%s %s%c", type, db_column_text(&q, 5), zPathname, separator);
806
- if (nullSeparateFlag) putchar('\0');
807806
}else{
808807
fossil_print("%s%s%c", type, zPathname, separator);
809808
}
810809
free(zFullName);
811810
}
@@ -1681,10 +1680,18 @@
16811680
while( db_step(&q)==SQLITE_ROW ){
16821681
const char *zIntegrateUuid = db_column_text(&q, 0);
16831682
int rid = db_column_int(&q, 1);
16841683
if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref "
16851684
" WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){
1685
+#if 0
1686
+ /* Make sure the check-in manifest of the resulting merge child does not
1687
+ ** include a +close tag referring to the leaf check-in on a private
1688
+ ** branch, so as not to generate a missing artifact reference on
1689
+ ** repository clones without that private branch. The merge command
1690
+ ** should have dropped the --integrate option, at this point. */
1691
+ assert( !content_is_private(rid) );
1692
+#endif
16861693
blob_appendf(pOut, "T +closed %s\n", zIntegrateUuid);
16871694
}
16881695
}
16891696
db_finalize(&q);
16901697
@@ -2048,10 +2055,12 @@
20482055
const char *zComment; /* Check-in comment */
20492056
Stmt q; /* Various queries */
20502057
char *zUuid; /* UUID of the new check-in */
20512058
int useHash = 0; /* True to verify file status using hashing */
20522059
int noSign = 0; /* True to omit signing the manifest using GPG */
2060
+ int privateFlag = 0; /* True if the --private option is present */
2061
+ int privateParent = 0; /* True if the parent check-in is private */
20532062
int isAMerge = 0; /* True if checking in a merge */
20542063
int noWarningFlag = 0; /* True if skipping all warnings */
20552064
int noPrompt = 0; /* True if skipping all prompts */
20562065
int forceFlag = 0; /* Undocumented: Disables all checks */
20572066
int forceDelta = 0; /* Force a delta-manifest */
@@ -2085,14 +2094,20 @@
20852094
memset(&sCiInfo, 0, sizeof(sCiInfo));
20862095
url_proxy_options();
20872096
/* --sha1sum is an undocumented alias for --hash for backwards compatiblity */
20882097
useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
20892098
noSign = find_option("nosign",0,0)!=0;
2099
+ privateFlag = find_option("private",0,0)!=0;
20902100
forceDelta = find_option("delta",0,0)!=0;
20912101
forceBaseline = find_option("baseline",0,0)!=0;
2092
- if( forceDelta && forceBaseline ){
2093
- fossil_fatal("cannot use --delta and --baseline together");
2102
+ if( forceDelta ){
2103
+ if( forceBaseline ){
2104
+ fossil_fatal("cannot use --delta and --baseline together");
2105
+ }
2106
+ if( db_get_boolean("forbid-delta-manifests",0) ){
2107
+ fossil_fatal("delta manifests are prohibited in this repository");
2108
+ }
20942109
}
20952110
dryRunFlag = find_option("dry-run","n",0)!=0;
20962111
if( !dryRunFlag ){
20972112
dryRunFlag = find_option("test",0,0)!=0; /* deprecated */
20982113
}
@@ -2117,17 +2132,10 @@
21172132
sizeof(char*)*(nTag+2));
21182133
sCiInfo.azTag[nTag++] = zTag;
21192134
sCiInfo.azTag[nTag] = 0;
21202135
}
21212136
zComFile = find_option("message-file", "M", 1);
2122
- if( find_option("private",0,0) ){
2123
- g.markPrivate = 1;
2124
- if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private";
2125
- if( sCiInfo.zBrClr==0 && sCiInfo.zColor==0 ){
2126
- sCiInfo.zBrClr = "#fec084"; /* Orange */
2127
- }
2128
- }
21292137
sCiInfo.zDateOvrd = find_option("date-override",0,1);
21302138
sCiInfo.zUserOvrd = find_option("user-override",0,1);
21312139
db_must_be_within_tree();
21322140
noSign = db_get_boolean("omitsign", 0)|noSign;
21332141
if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
@@ -2137,15 +2145,25 @@
21372145
21382146
/* Get the ID of the parent manifest artifact */
21392147
vid = db_lget_int("checkout", 0);
21402148
if( vid==0 ){
21412149
useCksum = 1;
2142
- if( sCiInfo.zBranch==0 ) {
2150
+ if( privateFlag==0 && sCiInfo.zBranch==0 ) {
21432151
sCiInfo.zBranch=db_get("main-branch", 0);
21442152
}
2145
- }else if( content_is_private(vid) ){
2146
- g.markPrivate = 1;
2153
+ }else{
2154
+ privateParent = content_is_private(vid);
2155
+ }
2156
+
2157
+ /* Track the "private" status */
2158
+ g.markPrivate = privateFlag || privateParent;
2159
+ if( privateFlag && !privateParent ){
2160
+ /* Apply default branch name ("private") and color ("orange") if not
2161
+ ** specified otherwise on the command-line, and if the parent is not
2162
+ ** already private. */
2163
+ if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private";
2164
+ if( sCiInfo.zBrClr==0 && sCiInfo.zColor==0 ) sCiInfo.zBrClr = "#fec084";
21472165
}
21482166
21492167
/* Do not allow the creation of a new branch using an existing open
21502168
** branch name unless the --force flag is used */
21512169
if( sCiInfo.zBranch!=0
@@ -2168,11 +2186,14 @@
21682186
** manifest) can continue to use this repository, do not create a new
21692187
** delta-manifest unless this repository already contains one or more
21702188
** delta-manifests, or unless the delta-manifest is explicitly requested
21712189
** by the --delta option.
21722190
*/
2173
- if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){
2191
+ if( !forceDelta
2192
+ && !db_get_boolean("seen-delta-manifest",0)
2193
+ && !db_get_boolean("forbid-delta-manifests",0)
2194
+ ){
21742195
forceBaseline = 1;
21752196
}
21762197
21772198
/*
21782199
** Autosync if autosync is enabled and this is not a private check-in.
@@ -2642,10 +2663,11 @@
26422663
/* Commit */
26432664
db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'");
26442665
db_multi_exec("PRAGMA repository.application_id=252006673;");
26452666
db_multi_exec("PRAGMA localdb.application_id=252006674;");
26462667
if( dryRunFlag ){
2668
+ leaf_ambiguity_warning(nvid,nvid);
26472669
db_end_transaction(1);
26482670
exit(1);
26492671
}
26502672
db_end_transaction(0);
26512673
@@ -2664,7 +2686,9 @@
26642686
int nTries = db_get_int("autosync-tries",1);
26652687
autosync_loop(syncFlags, nTries, 0);
26662688
}
26672689
if( count_nonbranch_children(vid)>1 ){
26682690
fossil_print("**** warning: a fork has occurred *****\n");
2691
+ }else{
2692
+ leaf_ambiguity_warning(nvid,nvid);
26692693
}
26702694
}
26712695
--- src/checkin.c
+++ src/checkin.c
@@ -801,11 +801,10 @@
801 }
802 if (nullSeparateFlag)
803 separator='\0';
804 if( showAge ){
805 fossil_print("%s%s %s%c", type, db_column_text(&q, 5), zPathname, separator);
806 if (nullSeparateFlag) putchar('\0');
807 }else{
808 fossil_print("%s%s%c", type, zPathname, separator);
809 }
810 free(zFullName);
811 }
@@ -1681,10 +1680,18 @@
1681 while( db_step(&q)==SQLITE_ROW ){
1682 const char *zIntegrateUuid = db_column_text(&q, 0);
1683 int rid = db_column_int(&q, 1);
1684 if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref "
1685 " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){
 
 
 
 
 
 
 
 
1686 blob_appendf(pOut, "T +closed %s\n", zIntegrateUuid);
1687 }
1688 }
1689 db_finalize(&q);
1690
@@ -2048,10 +2055,12 @@
2048 const char *zComment; /* Check-in comment */
2049 Stmt q; /* Various queries */
2050 char *zUuid; /* UUID of the new check-in */
2051 int useHash = 0; /* True to verify file status using hashing */
2052 int noSign = 0; /* True to omit signing the manifest using GPG */
 
 
2053 int isAMerge = 0; /* True if checking in a merge */
2054 int noWarningFlag = 0; /* True if skipping all warnings */
2055 int noPrompt = 0; /* True if skipping all prompts */
2056 int forceFlag = 0; /* Undocumented: Disables all checks */
2057 int forceDelta = 0; /* Force a delta-manifest */
@@ -2085,14 +2094,20 @@
2085 memset(&sCiInfo, 0, sizeof(sCiInfo));
2086 url_proxy_options();
2087 /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */
2088 useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
2089 noSign = find_option("nosign",0,0)!=0;
 
2090 forceDelta = find_option("delta",0,0)!=0;
2091 forceBaseline = find_option("baseline",0,0)!=0;
2092 if( forceDelta && forceBaseline ){
2093 fossil_fatal("cannot use --delta and --baseline together");
 
 
 
 
 
2094 }
2095 dryRunFlag = find_option("dry-run","n",0)!=0;
2096 if( !dryRunFlag ){
2097 dryRunFlag = find_option("test",0,0)!=0; /* deprecated */
2098 }
@@ -2117,17 +2132,10 @@
2117 sizeof(char*)*(nTag+2));
2118 sCiInfo.azTag[nTag++] = zTag;
2119 sCiInfo.azTag[nTag] = 0;
2120 }
2121 zComFile = find_option("message-file", "M", 1);
2122 if( find_option("private",0,0) ){
2123 g.markPrivate = 1;
2124 if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private";
2125 if( sCiInfo.zBrClr==0 && sCiInfo.zColor==0 ){
2126 sCiInfo.zBrClr = "#fec084"; /* Orange */
2127 }
2128 }
2129 sCiInfo.zDateOvrd = find_option("date-override",0,1);
2130 sCiInfo.zUserOvrd = find_option("user-override",0,1);
2131 db_must_be_within_tree();
2132 noSign = db_get_boolean("omitsign", 0)|noSign;
2133 if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
@@ -2137,15 +2145,25 @@
2137
2138 /* Get the ID of the parent manifest artifact */
2139 vid = db_lget_int("checkout", 0);
2140 if( vid==0 ){
2141 useCksum = 1;
2142 if( sCiInfo.zBranch==0 ) {
2143 sCiInfo.zBranch=db_get("main-branch", 0);
2144 }
2145 }else if( content_is_private(vid) ){
2146 g.markPrivate = 1;
 
 
 
 
 
 
 
 
 
 
2147 }
2148
2149 /* Do not allow the creation of a new branch using an existing open
2150 ** branch name unless the --force flag is used */
2151 if( sCiInfo.zBranch!=0
@@ -2168,11 +2186,14 @@
2168 ** manifest) can continue to use this repository, do not create a new
2169 ** delta-manifest unless this repository already contains one or more
2170 ** delta-manifests, or unless the delta-manifest is explicitly requested
2171 ** by the --delta option.
2172 */
2173 if( !forceDelta && !db_get_boolean("seen-delta-manifest",0) ){
 
 
 
2174 forceBaseline = 1;
2175 }
2176
2177 /*
2178 ** Autosync if autosync is enabled and this is not a private check-in.
@@ -2642,10 +2663,11 @@
2642 /* Commit */
2643 db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'");
2644 db_multi_exec("PRAGMA repository.application_id=252006673;");
2645 db_multi_exec("PRAGMA localdb.application_id=252006674;");
2646 if( dryRunFlag ){
 
2647 db_end_transaction(1);
2648 exit(1);
2649 }
2650 db_end_transaction(0);
2651
@@ -2664,7 +2686,9 @@
2664 int nTries = db_get_int("autosync-tries",1);
2665 autosync_loop(syncFlags, nTries, 0);
2666 }
2667 if( count_nonbranch_children(vid)>1 ){
2668 fossil_print("**** warning: a fork has occurred *****\n");
 
 
2669 }
2670 }
2671
--- src/checkin.c
+++ src/checkin.c
@@ -801,11 +801,10 @@
801 }
802 if (nullSeparateFlag)
803 separator='\0';
804 if( showAge ){
805 fossil_print("%s%s %s%c", type, db_column_text(&q, 5), zPathname, separator);
 
806 }else{
807 fossil_print("%s%s%c", type, zPathname, separator);
808 }
809 free(zFullName);
810 }
@@ -1681,10 +1680,18 @@
1680 while( db_step(&q)==SQLITE_ROW ){
1681 const char *zIntegrateUuid = db_column_text(&q, 0);
1682 int rid = db_column_int(&q, 1);
1683 if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref "
1684 " WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){
1685 #if 0
1686 /* Make sure the check-in manifest of the resulting merge child does not
1687 ** include a +close tag referring to the leaf check-in on a private
1688 ** branch, so as not to generate a missing artifact reference on
1689 ** repository clones without that private branch. The merge command
1690 ** should have dropped the --integrate option, at this point. */
1691 assert( !content_is_private(rid) );
1692 #endif
1693 blob_appendf(pOut, "T +closed %s\n", zIntegrateUuid);
1694 }
1695 }
1696 db_finalize(&q);
1697
@@ -2048,10 +2055,12 @@
2055 const char *zComment; /* Check-in comment */
2056 Stmt q; /* Various queries */
2057 char *zUuid; /* UUID of the new check-in */
2058 int useHash = 0; /* True to verify file status using hashing */
2059 int noSign = 0; /* True to omit signing the manifest using GPG */
2060 int privateFlag = 0; /* True if the --private option is present */
2061 int privateParent = 0; /* True if the parent check-in is private */
2062 int isAMerge = 0; /* True if checking in a merge */
2063 int noWarningFlag = 0; /* True if skipping all warnings */
2064 int noPrompt = 0; /* True if skipping all prompts */
2065 int forceFlag = 0; /* Undocumented: Disables all checks */
2066 int forceDelta = 0; /* Force a delta-manifest */
@@ -2085,14 +2094,20 @@
2094 memset(&sCiInfo, 0, sizeof(sCiInfo));
2095 url_proxy_options();
2096 /* --sha1sum is an undocumented alias for --hash for backwards compatiblity */
2097 useHash = find_option("hash",0,0)!=0 || find_option("sha1sum",0,0)!=0;
2098 noSign = find_option("nosign",0,0)!=0;
2099 privateFlag = find_option("private",0,0)!=0;
2100 forceDelta = find_option("delta",0,0)!=0;
2101 forceBaseline = find_option("baseline",0,0)!=0;
2102 if( forceDelta ){
2103 if( forceBaseline ){
2104 fossil_fatal("cannot use --delta and --baseline together");
2105 }
2106 if( db_get_boolean("forbid-delta-manifests",0) ){
2107 fossil_fatal("delta manifests are prohibited in this repository");
2108 }
2109 }
2110 dryRunFlag = find_option("dry-run","n",0)!=0;
2111 if( !dryRunFlag ){
2112 dryRunFlag = find_option("test",0,0)!=0; /* deprecated */
2113 }
@@ -2117,17 +2132,10 @@
2132 sizeof(char*)*(nTag+2));
2133 sCiInfo.azTag[nTag++] = zTag;
2134 sCiInfo.azTag[nTag] = 0;
2135 }
2136 zComFile = find_option("message-file", "M", 1);
 
 
 
 
 
 
 
2137 sCiInfo.zDateOvrd = find_option("date-override",0,1);
2138 sCiInfo.zUserOvrd = find_option("user-override",0,1);
2139 db_must_be_within_tree();
2140 noSign = db_get_boolean("omitsign", 0)|noSign;
2141 if( db_get_boolean("clearsign", 0)==0 ){ noSign = 1; }
@@ -2137,15 +2145,25 @@
2145
2146 /* Get the ID of the parent manifest artifact */
2147 vid = db_lget_int("checkout", 0);
2148 if( vid==0 ){
2149 useCksum = 1;
2150 if( privateFlag==0 && sCiInfo.zBranch==0 ) {
2151 sCiInfo.zBranch=db_get("main-branch", 0);
2152 }
2153 }else{
2154 privateParent = content_is_private(vid);
2155 }
2156
2157 /* Track the "private" status */
2158 g.markPrivate = privateFlag || privateParent;
2159 if( privateFlag && !privateParent ){
2160 /* Apply default branch name ("private") and color ("orange") if not
2161 ** specified otherwise on the command-line, and if the parent is not
2162 ** already private. */
2163 if( sCiInfo.zBranch==0 ) sCiInfo.zBranch = "private";
2164 if( sCiInfo.zBrClr==0 && sCiInfo.zColor==0 ) sCiInfo.zBrClr = "#fec084";
2165 }
2166
2167 /* Do not allow the creation of a new branch using an existing open
2168 ** branch name unless the --force flag is used */
2169 if( sCiInfo.zBranch!=0
@@ -2168,11 +2186,14 @@
2186 ** manifest) can continue to use this repository, do not create a new
2187 ** delta-manifest unless this repository already contains one or more
2188 ** delta-manifests, or unless the delta-manifest is explicitly requested
2189 ** by the --delta option.
2190 */
2191 if( !forceDelta
2192 && !db_get_boolean("seen-delta-manifest",0)
2193 && !db_get_boolean("forbid-delta-manifests",0)
2194 ){
2195 forceBaseline = 1;
2196 }
2197
2198 /*
2199 ** Autosync if autosync is enabled and this is not a private check-in.
@@ -2642,10 +2663,11 @@
2663 /* Commit */
2664 db_multi_exec("DELETE FROM vvar WHERE name='ci-comment'");
2665 db_multi_exec("PRAGMA repository.application_id=252006673;");
2666 db_multi_exec("PRAGMA localdb.application_id=252006674;");
2667 if( dryRunFlag ){
2668 leaf_ambiguity_warning(nvid,nvid);
2669 db_end_transaction(1);
2670 exit(1);
2671 }
2672 db_end_transaction(0);
2673
@@ -2664,7 +2686,9 @@
2686 int nTries = db_get_int("autosync-tries",1);
2687 autosync_loop(syncFlags, nTries, 0);
2688 }
2689 if( count_nonbranch_children(vid)>1 ){
2690 fossil_print("**** warning: a fork has occurred *****\n");
2691 }else{
2692 leaf_ambiguity_warning(nvid,nvid);
2693 }
2694 }
2695
+17 -31
--- src/comformat.c
+++ src/comformat.c
@@ -19,16 +19,10 @@
1919
** text on a TTY.
2020
*/
2121
#include "config.h"
2222
#include "comformat.h"
2323
#include <assert.h>
24
-#ifdef _WIN32
25
-# include <windows.h>
26
-#else
27
-# include <termios.h>
28
-# include <sys/ioctl.h>
29
-#endif
3024
3125
#if INTERFACE
3226
#define COMMENT_PRINT_NONE ((u32)0x00000000) /* No flags = non-legacy. */
3327
#define COMMENT_PRINT_LEGACY ((u32)0x00000001) /* Use legacy algorithm. */
3428
#define COMMENT_PRINT_TRIM_CRLF ((u32)0x00000002) /* Trim leading CR/LF. */
@@ -67,35 +61,27 @@
6761
*/
6862
static int comment_set_maxchars(
6963
int indent,
7064
int *pMaxChars
7165
){
72
-#if defined(_WIN32)
73
- CONSOLE_SCREEN_BUFFER_INFO csbi;
74
- memset(&csbi, 0, sizeof(CONSOLE_SCREEN_BUFFER_INFO));
75
- if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) ){
76
- *pMaxChars = csbi.srWindow.Right - csbi.srWindow.Left - indent;
77
- return 1;
78
- }
79
- return 0;
80
-#elif defined(TIOCGWINSZ)
81
- struct winsize w;
82
- memset(&w, 0, sizeof(struct winsize));
83
- if( ioctl(0, TIOCGWINSZ, &w)!=-1 ){
84
- *pMaxChars = w.ws_col - indent;
85
- return 1;
86
- }
87
- return 0;
88
-#else
89
- /*
90
- ** Fallback to using more-or-less the "legacy semantics" of hard-coding
91
- ** the maximum line length to a value reasonable for the vast majority
92
- ** of supported systems.
93
- */
94
- *pMaxChars = COMMENT_LEGACY_LINE_LENGTH - indent;
95
- return -1;
96
-#endif
66
+ struct TerminalSize ts;
67
+ if ( !terminal_get_size(&ts) ){
68
+ return 0;
69
+ }
70
+
71
+ if( ts.nColumns ){
72
+ *pMaxChars = ts.nColumns - indent;
73
+ return 1;
74
+ }else{
75
+ /*
76
+ ** Fallback to using more-or-less the "legacy semantics" of hard-coding
77
+ ** the maximum line length to a value reasonable for the vast majority
78
+ ** of supported systems.
79
+ */
80
+ *pMaxChars = COMMENT_LEGACY_LINE_LENGTH - indent;
81
+ return -1;
82
+ }
9783
}
9884
9985
/*
10086
** This function checks the current line being printed against the original
10187
** comment text. Upon matching, it updates the provided character and line
10288
--- src/comformat.c
+++ src/comformat.c
@@ -19,16 +19,10 @@
19 ** text on a TTY.
20 */
21 #include "config.h"
22 #include "comformat.h"
23 #include <assert.h>
24 #ifdef _WIN32
25 # include <windows.h>
26 #else
27 # include <termios.h>
28 # include <sys/ioctl.h>
29 #endif
30
31 #if INTERFACE
32 #define COMMENT_PRINT_NONE ((u32)0x00000000) /* No flags = non-legacy. */
33 #define COMMENT_PRINT_LEGACY ((u32)0x00000001) /* Use legacy algorithm. */
34 #define COMMENT_PRINT_TRIM_CRLF ((u32)0x00000002) /* Trim leading CR/LF. */
@@ -67,35 +61,27 @@
67 */
68 static int comment_set_maxchars(
69 int indent,
70 int *pMaxChars
71 ){
72 #if defined(_WIN32)
73 CONSOLE_SCREEN_BUFFER_INFO csbi;
74 memset(&csbi, 0, sizeof(CONSOLE_SCREEN_BUFFER_INFO));
75 if( GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi) ){
76 *pMaxChars = csbi.srWindow.Right - csbi.srWindow.Left - indent;
77 return 1;
78 }
79 return 0;
80 #elif defined(TIOCGWINSZ)
81 struct winsize w;
82 memset(&w, 0, sizeof(struct winsize));
83 if( ioctl(0, TIOCGWINSZ, &w)!=-1 ){
84 *pMaxChars = w.ws_col - indent;
85 return 1;
86 }
87 return 0;
88 #else
89 /*
90 ** Fallback to using more-or-less the "legacy semantics" of hard-coding
91 ** the maximum line length to a value reasonable for the vast majority
92 ** of supported systems.
93 */
94 *pMaxChars = COMMENT_LEGACY_LINE_LENGTH - indent;
95 return -1;
96 #endif
97 }
98
99 /*
100 ** This function checks the current line being printed against the original
101 ** comment text. Upon matching, it updates the provided character and line
102
--- src/comformat.c
+++ src/comformat.c
@@ -19,16 +19,10 @@
19 ** text on a TTY.
20 */
21 #include "config.h"
22 #include "comformat.h"
23 #include <assert.h>
 
 
 
 
 
 
24
25 #if INTERFACE
26 #define COMMENT_PRINT_NONE ((u32)0x00000000) /* No flags = non-legacy. */
27 #define COMMENT_PRINT_LEGACY ((u32)0x00000001) /* Use legacy algorithm. */
28 #define COMMENT_PRINT_TRIM_CRLF ((u32)0x00000002) /* Trim leading CR/LF. */
@@ -67,35 +61,27 @@
61 */
62 static int comment_set_maxchars(
63 int indent,
64 int *pMaxChars
65 ){
66 struct TerminalSize ts;
67 if ( !terminal_get_size(&ts) ){
68 return 0;
69 }
70
71 if( ts.nColumns ){
72 *pMaxChars = ts.nColumns - indent;
73 return 1;
74 }else{
75 /*
76 ** Fallback to using more-or-less the "legacy semantics" of hard-coding
77 ** the maximum line length to a value reasonable for the vast majority
78 ** of supported systems.
79 */
80 *pMaxChars = COMMENT_LEGACY_LINE_LENGTH - indent;
81 return -1;
82 }
 
 
 
 
 
 
 
 
83 }
84
85 /*
86 ** This function checks the current line being printed against the original
87 ** comment text. Upon matching, it updates the provided character and line
88
--- src/configure.c
+++ src/configure.c
@@ -147,10 +147,11 @@
147147
{ "parent-project-code", CONFIGSET_PROJ },
148148
{ "parent-project-name", CONFIGSET_PROJ },
149149
{ "hash-policy", CONFIGSET_PROJ },
150150
{ "comment-format", CONFIGSET_PROJ },
151151
{ "mimetypes", CONFIGSET_PROJ },
152
+ { "forbid-delta-manifests", CONFIGSET_PROJ },
152153
153154
#ifdef FOSSIL_ENABLE_LEGACY_MV_RM
154155
{ "mv-rm-files", CONFIGSET_PROJ },
155156
#endif
156157
157158
--- src/configure.c
+++ src/configure.c
@@ -147,10 +147,11 @@
147 { "parent-project-code", CONFIGSET_PROJ },
148 { "parent-project-name", CONFIGSET_PROJ },
149 { "hash-policy", CONFIGSET_PROJ },
150 { "comment-format", CONFIGSET_PROJ },
151 { "mimetypes", CONFIGSET_PROJ },
 
152
153 #ifdef FOSSIL_ENABLE_LEGACY_MV_RM
154 { "mv-rm-files", CONFIGSET_PROJ },
155 #endif
156
157
--- src/configure.c
+++ src/configure.c
@@ -147,10 +147,11 @@
147 { "parent-project-code", CONFIGSET_PROJ },
148 { "parent-project-name", CONFIGSET_PROJ },
149 { "hash-policy", CONFIGSET_PROJ },
150 { "comment-format", CONFIGSET_PROJ },
151 { "mimetypes", CONFIGSET_PROJ },
152 { "forbid-delta-manifests", CONFIGSET_PROJ },
153
154 #ifdef FOSSIL_ENABLE_LEGACY_MV_RM
155 { "mv-rm-files", CONFIGSET_PROJ },
156 #endif
157
158
--- src/content.c
+++ src/content.c
@@ -774,10 +774,22 @@
774774
void content_make_public(int rid){
775775
static Stmt s1;
776776
db_static_prepare(&s1,
777777
"DELETE FROM private WHERE rid=:rid"
778778
);
779
+ db_bind_int(&s1, ":rid", rid);
780
+ db_exec(&s1);
781
+}
782
+
783
+/*
784
+** Make sure an artifact is private
785
+*/
786
+void content_make_private(int rid){
787
+ static Stmt s1;
788
+ db_static_prepare(&s1,
789
+ "INSERT OR IGNORE INTO private(rid) VALUES(:rid)"
790
+ );
779791
db_bind_int(&s1, ":rid", rid);
780792
db_exec(&s1);
781793
}
782794
783795
/*
784796
--- src/content.c
+++ src/content.c
@@ -774,10 +774,22 @@
774 void content_make_public(int rid){
775 static Stmt s1;
776 db_static_prepare(&s1,
777 "DELETE FROM private WHERE rid=:rid"
778 );
 
 
 
 
 
 
 
 
 
 
 
 
779 db_bind_int(&s1, ":rid", rid);
780 db_exec(&s1);
781 }
782
783 /*
784
--- src/content.c
+++ src/content.c
@@ -774,10 +774,22 @@
774 void content_make_public(int rid){
775 static Stmt s1;
776 db_static_prepare(&s1,
777 "DELETE FROM private WHERE rid=:rid"
778 );
779 db_bind_int(&s1, ":rid", rid);
780 db_exec(&s1);
781 }
782
783 /*
784 ** Make sure an artifact is private
785 */
786 void content_make_private(int rid){
787 static Stmt s1;
788 db_static_prepare(&s1,
789 "INSERT OR IGNORE INTO private(rid) VALUES(:rid)"
790 );
791 db_bind_int(&s1, ":rid", rid);
792 db_exec(&s1);
793 }
794
795 /*
796
+122 -61
--- src/db.c
+++ src/db.c
@@ -18,11 +18,12 @@
1818
** Code for interfacing to the various databases.
1919
**
2020
** There are three separate database files that fossil interacts
2121
** with:
2222
**
23
-** (1) The "user" database in ~/.fossil
23
+** (1) The "configdb" database in ~/.fossil or ~/.config/fossil.db
24
+** or in %LOCALAPPDATA%/_fossil
2425
**
2526
** (2) The "repository" database
2627
**
2728
** (3) A local checkout database named "_FOSSIL_" or ".fslckout"
2829
** and located at the root of the local copy of the source tree.
@@ -1369,11 +1370,11 @@
13691370
db_attach(zDbName, zLabel);
13701371
}
13711372
}
13721373
13731374
/*
1374
-** Close the per-user database file in ~/.fossil
1375
+** Close the per-user configuration database file
13751376
*/
13761377
void db_close_config(){
13771378
int iSlot = db_database_slot("configdb");
13781379
if( iSlot>0 ){
13791380
db_detach("configdb");
@@ -1393,31 +1394,30 @@
13931394
fossil_free(g.zConfigDbName);
13941395
g.zConfigDbName = 0;
13951396
}
13961397
13971398
/*
1398
-** Open the user database in "~/.fossil". Create the database anew if
1399
-** it does not already exist.
1399
+** Compute the name of the configuration database. If unable to find the
1400
+** database, return 0 if isOptional is true, or panic if isOptional is false.
14001401
**
1401
-** If the useAttach flag is 0 (the usual case) then the user database is
1402
-** opened on a separate database connection g.dbConfig. This prevents
1403
-** the ~/.fossil database from becoming locked on long check-in or sync
1404
-** operations which hold an exclusive transaction. In a few cases, though,
1405
-** it is convenient for the ~/.fossil to be attached to the main database
1406
-** connection so that we can join between the various databases. In that
1407
-** case, invoke this routine with useAttach as 1.
1402
+** Space to hold the result comes from fossil_malloc().
14081403
*/
1409
-int db_open_config(int useAttach, int isOptional){
1410
- char *zDbName;
1411
- char *zHome;
1412
- if( g.zConfigDbName ){
1413
- int alreadyAttached = db_database_slot("configdb")>0;
1414
- if( useAttach==alreadyAttached ) return 1; /* Already open. */
1415
- db_close_config();
1416
- }
1417
- zHome = fossil_getenv("FOSSIL_HOME");
1404
+static char *db_configdb_name(int isOptional){
1405
+ char *zHome; /* Home directory */
1406
+ char *zDbName; /* Name of the database file */
1407
+
1408
+
1409
+ /* On Windows, look for these directories, in order:
1410
+ **
1411
+ ** FOSSIL_HOME
1412
+ ** LOCALAPPDATA
1413
+ ** APPDATA
1414
+ ** USERPROFILE
1415
+ ** HOMEDRIVE HOMEPATH
1416
+ */
14181417
#if defined(_WIN32) || defined(__CYGWIN__)
1418
+ zHome = fossil_getenv("FOSSIL_HOME");
14191419
if( zHome==0 ){
14201420
zHome = fossil_getenv("LOCALAPPDATA");
14211421
if( zHome==0 ){
14221422
zHome = fossil_getenv("APPDATA");
14231423
if( zHome==0 ){
@@ -1428,40 +1428,98 @@
14281428
if( zDrive && zPath ) zHome = mprintf("%s%s", zDrive, zPath);
14291429
}
14301430
}
14311431
}
14321432
}
1433
- if( zHome==0 ){
1434
- if( isOptional ) return 0;
1435
- fossil_panic("cannot locate home directory - please set the "
1436
- "FOSSIL_HOME, LOCALAPPDATA, APPDATA, USERPROFILE, "
1437
- "or HOMEDRIVE / HOMEPATH environment variables");
1438
- }
1439
-#else
1440
- if( zHome==0 ){
1441
- zHome = fossil_getenv("HOME");
1442
- }
1443
- if( zHome==0 ){
1444
- if( isOptional ) return 0;
1445
- fossil_panic("cannot locate home directory - please set the "
1446
- "FOSSIL_HOME or HOME environment variables");
1447
- }
1448
-#endif
1449
- if( file_isdir(zHome, ExtFILE)!=1 ){
1450
- if( isOptional ) return 0;
1451
- fossil_panic("invalid home directory: %s", zHome);
1452
- }
1453
-#if defined(_WIN32) || defined(__CYGWIN__)
1454
- /* . filenames give some window systems problems and many apps problems */
1455
- zDbName = mprintf("%//_fossil", zHome);
1456
-#else
1457
- zDbName = mprintf("%s/.fossil", zHome);
1458
-#endif
1459
- if( file_size(zDbName, ExtFILE)<1024*3 ){
1460
- if( file_access(zHome, W_OK) ){
1461
- if( isOptional ) return 0;
1462
- fossil_panic("home directory %s must be writeable", zHome);
1433
+ zDbName = mprintf("%//_fossil", zHome);
1434
+ fossil_free(zHome);
1435
+ return zDbName;
1436
+
1437
+#else /* if unix */
1438
+ char *zXdgHome;
1439
+
1440
+ /* For unix. a 5-step algorithm is used.
1441
+ ** See ../www/tech_overview.wiki for discussion.
1442
+ **
1443
+ ** Step 1: If FOSSIL_HOME exists -> $FOSSIL_HOME/.fossil
1444
+ */
1445
+ zHome = fossil_getenv("FOSSIL_HOME");
1446
+ if( zHome!=0 ) return mprintf("%s/.fossil", zHome);
1447
+
1448
+ /* Step 2: If HOME exists and file $HOME/.fossil exists -> $HOME/.fossil
1449
+ */
1450
+ zHome = fossil_getenv("HOME");
1451
+ if( zHome ){
1452
+ zDbName = mprintf("%s/.fossil", zHome);
1453
+ if( file_size(zDbName, ExtFILE)>1024*3 ){
1454
+ return zDbName;
1455
+ }
1456
+ fossil_free(zDbName);
1457
+ }
1458
+
1459
+ /* Step 3: if XDG_CONFIG_HOME exists -> $XDG_CONFIG_HOME/fossil.db
1460
+ */
1461
+ zXdgHome = fossil_getenv("XDG_CONFIG_HOME");
1462
+ if( zXdgHome!=0 ){
1463
+ return mprintf("%s/fossil.db", zXdgHome);
1464
+ }
1465
+
1466
+ /* The HOME variable is required in order to continue.
1467
+ */
1468
+ if( zHome==0 ){
1469
+ if( isOptional ) return 0;
1470
+ fossil_panic("cannot locate home directory - please set one of the "
1471
+ "FOSSIL_HOME, XDG_CONFIG_HOME, or HOME environment "
1472
+ "variables");
1473
+ }
1474
+
1475
+ /* Step 4: If $HOME/.config is a directory -> $HOME/.config/fossil.db
1476
+ */
1477
+ zXdgHome = mprintf("%s/.config", zHome);
1478
+ if( file_isdir(zXdgHome, ExtFILE)==1 ){
1479
+ fossil_free(zXdgHome);
1480
+ return mprintf("%s/.config/fossil.db", zHome);
1481
+ }
1482
+
1483
+ /* Step 5: Otherwise -> $HOME/.fossil
1484
+ */
1485
+ return mprintf("%s/.fossil", zHome);
1486
+#endif /* unix */
1487
+}
1488
+
1489
+/*
1490
+** Open the configuration database. Create the database anew if
1491
+** it does not already exist.
1492
+**
1493
+** If the useAttach flag is 0 (the usual case) then the configuration
1494
+** database is opened on a separate database connection g.dbConfig.
1495
+** This prevents the database from becoming locked on long check-in or sync
1496
+** operations which hold an exclusive transaction. In a few cases, though,
1497
+** it is convenient for the database to be attached to the main database
1498
+** connection so that we can join between the various databases. In that
1499
+** case, invoke this routine with useAttach as 1.
1500
+*/
1501
+int db_open_config(int useAttach, int isOptional){
1502
+ char *zDbName;
1503
+ if( g.zConfigDbName ){
1504
+ int alreadyAttached = db_database_slot("configdb")>0;
1505
+ if( useAttach==alreadyAttached ) return 1; /* Already open. */
1506
+ db_close_config();
1507
+ }
1508
+ zDbName = db_configdb_name(isOptional);
1509
+ if( zDbName==0 ) return 0;
1510
+ if( file_size(zDbName, ExtFILE)<1024*3 ){
1511
+ char *zHome = file_dirname(zDbName);
1512
+ int rc;
1513
+ if( file_isdir(zHome, ExtFILE)==0 ){
1514
+ file_mkdir(zHome, ExtFILE, 0);
1515
+ }
1516
+ rc = file_access(zHome, W_OK);
1517
+ fossil_free(zHome);
1518
+ if( rc ){
1519
+ if( isOptional ) return 0;
1520
+ fossil_panic("home directory \"%s\" must be writeable", zHome);
14631521
}
14641522
db_init_database(zDbName, zConfigSchema, (char*)0);
14651523
}
14661524
if( file_access(zDbName, W_OK) ){
14671525
if( isOptional ) return 0;
@@ -2310,11 +2368,11 @@
23102368
** SQL functions for debugging.
23112369
**
23122370
** The print() function writes its arguments on stdout, but only
23132371
** if the -sqlprint command-line option is turned on.
23142372
*/
2315
-LOCAL void db_sql_print(
2373
+void db_sql_print(
23162374
sqlite3_context *context,
23172375
int argc,
23182376
sqlite3_value **argv
23192377
){
23202378
int i;
@@ -2506,16 +2564,16 @@
25062564
return 0;
25072565
}
25082566
25092567
/*
25102568
** Swap the g.db and g.dbConfig connections so that the various db_* routines
2511
-** work on the ~/.fossil database instead of on the repository database.
2569
+** work on the configuration database instead of on the repository database.
25122570
** Be sure to swap them back after doing the operation.
25132571
**
2514
-** If the ~/.fossil database has already been opened as the main database or
2515
-** is attached to the main database, no connection swaps are required so this
2516
-** routine is a no-op.
2572
+** If the configuration database has already been opened as the main database
2573
+** or is attached to the main database, no connection swaps are required so
2574
+** this routine is a no-op.
25172575
*/
25182576
void db_swap_connections(void){
25192577
/*
25202578
** When swapping the main database connection with the config database
25212579
** connection, the config database connection must be open (not simply
@@ -3498,10 +3556,14 @@
34983556
/*
34993557
** SETTING: pgp-command width=40
35003558
** Command used to clear-sign manifests at check-in.
35013559
** Default value is "gpg --clearsign -o"
35023560
*/
3561
+/*
3562
+** SETTING: forbid-delta-manifests boolean default=off
3563
+** If enabled, new delta manifests are prohibited.
3564
+*/
35033565
/*
35043566
** SETTING: proxy width=32 default=off
35053567
** URL of the HTTP proxy. If undefined or "off" then
35063568
** the "http_proxy" environment variable is consulted.
35073569
** If the http_proxy environment variable is undefined
@@ -3720,14 +3782,13 @@
37203782
** The "unset" command clears a setting.
37213783
**
37223784
** Settings can have both a "local" repository-only value and "global" value
37233785
** that applies to all repositories. The local values are stored in the
37243786
** "config" table of the repository and the global values are stored in the
3725
-** $HOME/.fossil file on unix or in the %LOCALAPPDATA%/_fossil file on Windows.
3726
-** If both a local and a global value exists for a setting, the local value
3727
-** takes precedence. This command normally operates on the local settings.
3728
-** Use the --global option to change global settings.
3787
+** configuration database. If both a local and a global value exists for a
3788
+** setting, the local value takes precedence. This command normally operates
3789
+** on the local settings. Use the --global option to change global settings.
37293790
**
37303791
** Options:
37313792
** --global set or unset the given property globally instead of
37323793
** setting or unsetting it for the open repository only.
37333794
**
@@ -3858,12 +3919,12 @@
38583919
** COMMAND: test-without-rowid
38593920
**
38603921
** Usage: %fossil test-without-rowid FILENAME...
38613922
**
38623923
** Change the Fossil repository FILENAME to make use of the WITHOUT ROWID
3863
-** optimization. FILENAME can also be the ~/.fossil file or a local
3864
-** .fslckout or _FOSSIL_ file.
3924
+** optimization. FILENAME can also be the configuration database file
3925
+** (~/.fossil or ~/.config/fossil.db) or a local .fslckout or _FOSSIL_ file.
38653926
**
38663927
** The purpose of this command is for testing the WITHOUT ROWID capabilities
38673928
** of SQLite. There is no big advantage to using WITHOUT ROWID in Fossil.
38683929
**
38693930
** Options:
38703931
--- src/db.c
+++ src/db.c
@@ -18,11 +18,12 @@
18 ** Code for interfacing to the various databases.
19 **
20 ** There are three separate database files that fossil interacts
21 ** with:
22 **
23 ** (1) The "user" database in ~/.fossil
 
24 **
25 ** (2) The "repository" database
26 **
27 ** (3) A local checkout database named "_FOSSIL_" or ".fslckout"
28 ** and located at the root of the local copy of the source tree.
@@ -1369,11 +1370,11 @@
1369 db_attach(zDbName, zLabel);
1370 }
1371 }
1372
1373 /*
1374 ** Close the per-user database file in ~/.fossil
1375 */
1376 void db_close_config(){
1377 int iSlot = db_database_slot("configdb");
1378 if( iSlot>0 ){
1379 db_detach("configdb");
@@ -1393,31 +1394,30 @@
1393 fossil_free(g.zConfigDbName);
1394 g.zConfigDbName = 0;
1395 }
1396
1397 /*
1398 ** Open the user database in "~/.fossil". Create the database anew if
1399 ** it does not already exist.
1400 **
1401 ** If the useAttach flag is 0 (the usual case) then the user database is
1402 ** opened on a separate database connection g.dbConfig. This prevents
1403 ** the ~/.fossil database from becoming locked on long check-in or sync
1404 ** operations which hold an exclusive transaction. In a few cases, though,
1405 ** it is convenient for the ~/.fossil to be attached to the main database
1406 ** connection so that we can join between the various databases. In that
1407 ** case, invoke this routine with useAttach as 1.
1408 */
1409 int db_open_config(int useAttach, int isOptional){
1410 char *zDbName;
1411 char *zHome;
1412 if( g.zConfigDbName ){
1413 int alreadyAttached = db_database_slot("configdb")>0;
1414 if( useAttach==alreadyAttached ) return 1; /* Already open. */
1415 db_close_config();
1416 }
1417 zHome = fossil_getenv("FOSSIL_HOME");
 
 
 
 
1418 #if defined(_WIN32) || defined(__CYGWIN__)
 
1419 if( zHome==0 ){
1420 zHome = fossil_getenv("LOCALAPPDATA");
1421 if( zHome==0 ){
1422 zHome = fossil_getenv("APPDATA");
1423 if( zHome==0 ){
@@ -1428,40 +1428,98 @@
1428 if( zDrive && zPath ) zHome = mprintf("%s%s", zDrive, zPath);
1429 }
1430 }
1431 }
1432 }
1433 if( zHome==0 ){
1434 if( isOptional ) return 0;
1435 fossil_panic("cannot locate home directory - please set the "
1436 "FOSSIL_HOME, LOCALAPPDATA, APPDATA, USERPROFILE, "
1437 "or HOMEDRIVE / HOMEPATH environment variables");
1438 }
1439 #else
1440 if( zHome==0 ){
1441 zHome = fossil_getenv("HOME");
1442 }
1443 if( zHome==0 ){
1444 if( isOptional ) return 0;
1445 fossil_panic("cannot locate home directory - please set the "
1446 "FOSSIL_HOME or HOME environment variables");
1447 }
1448 #endif
1449 if( file_isdir(zHome, ExtFILE)!=1 ){
1450 if( isOptional ) return 0;
1451 fossil_panic("invalid home directory: %s", zHome);
1452 }
1453 #if defined(_WIN32) || defined(__CYGWIN__)
1454 /* . filenames give some window systems problems and many apps problems */
1455 zDbName = mprintf("%//_fossil", zHome);
1456 #else
1457 zDbName = mprintf("%s/.fossil", zHome);
1458 #endif
1459 if( file_size(zDbName, ExtFILE)<1024*3 ){
1460 if( file_access(zHome, W_OK) ){
1461 if( isOptional ) return 0;
1462 fossil_panic("home directory %s must be writeable", zHome);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1463 }
1464 db_init_database(zDbName, zConfigSchema, (char*)0);
1465 }
1466 if( file_access(zDbName, W_OK) ){
1467 if( isOptional ) return 0;
@@ -2310,11 +2368,11 @@
2310 ** SQL functions for debugging.
2311 **
2312 ** The print() function writes its arguments on stdout, but only
2313 ** if the -sqlprint command-line option is turned on.
2314 */
2315 LOCAL void db_sql_print(
2316 sqlite3_context *context,
2317 int argc,
2318 sqlite3_value **argv
2319 ){
2320 int i;
@@ -2506,16 +2564,16 @@
2506 return 0;
2507 }
2508
2509 /*
2510 ** Swap the g.db and g.dbConfig connections so that the various db_* routines
2511 ** work on the ~/.fossil database instead of on the repository database.
2512 ** Be sure to swap them back after doing the operation.
2513 **
2514 ** If the ~/.fossil database has already been opened as the main database or
2515 ** is attached to the main database, no connection swaps are required so this
2516 ** routine is a no-op.
2517 */
2518 void db_swap_connections(void){
2519 /*
2520 ** When swapping the main database connection with the config database
2521 ** connection, the config database connection must be open (not simply
@@ -3498,10 +3556,14 @@
3498 /*
3499 ** SETTING: pgp-command width=40
3500 ** Command used to clear-sign manifests at check-in.
3501 ** Default value is "gpg --clearsign -o"
3502 */
 
 
 
 
3503 /*
3504 ** SETTING: proxy width=32 default=off
3505 ** URL of the HTTP proxy. If undefined or "off" then
3506 ** the "http_proxy" environment variable is consulted.
3507 ** If the http_proxy environment variable is undefined
@@ -3720,14 +3782,13 @@
3720 ** The "unset" command clears a setting.
3721 **
3722 ** Settings can have both a "local" repository-only value and "global" value
3723 ** that applies to all repositories. The local values are stored in the
3724 ** "config" table of the repository and the global values are stored in the
3725 ** $HOME/.fossil file on unix or in the %LOCALAPPDATA%/_fossil file on Windows.
3726 ** If both a local and a global value exists for a setting, the local value
3727 ** takes precedence. This command normally operates on the local settings.
3728 ** Use the --global option to change global settings.
3729 **
3730 ** Options:
3731 ** --global set or unset the given property globally instead of
3732 ** setting or unsetting it for the open repository only.
3733 **
@@ -3858,12 +3919,12 @@
3858 ** COMMAND: test-without-rowid
3859 **
3860 ** Usage: %fossil test-without-rowid FILENAME...
3861 **
3862 ** Change the Fossil repository FILENAME to make use of the WITHOUT ROWID
3863 ** optimization. FILENAME can also be the ~/.fossil file or a local
3864 ** .fslckout or _FOSSIL_ file.
3865 **
3866 ** The purpose of this command is for testing the WITHOUT ROWID capabilities
3867 ** of SQLite. There is no big advantage to using WITHOUT ROWID in Fossil.
3868 **
3869 ** Options:
3870
--- src/db.c
+++ src/db.c
@@ -18,11 +18,12 @@
18 ** Code for interfacing to the various databases.
19 **
20 ** There are three separate database files that fossil interacts
21 ** with:
22 **
23 ** (1) The "configdb" database in ~/.fossil or ~/.config/fossil.db
24 ** or in %LOCALAPPDATA%/_fossil
25 **
26 ** (2) The "repository" database
27 **
28 ** (3) A local checkout database named "_FOSSIL_" or ".fslckout"
29 ** and located at the root of the local copy of the source tree.
@@ -1369,11 +1370,11 @@
1370 db_attach(zDbName, zLabel);
1371 }
1372 }
1373
1374 /*
1375 ** Close the per-user configuration database file
1376 */
1377 void db_close_config(){
1378 int iSlot = db_database_slot("configdb");
1379 if( iSlot>0 ){
1380 db_detach("configdb");
@@ -1393,31 +1394,30 @@
1394 fossil_free(g.zConfigDbName);
1395 g.zConfigDbName = 0;
1396 }
1397
1398 /*
1399 ** Compute the name of the configuration database. If unable to find the
1400 ** database, return 0 if isOptional is true, or panic if isOptional is false.
1401 **
1402 ** Space to hold the result comes from fossil_malloc().
 
 
 
 
 
 
1403 */
1404 static char *db_configdb_name(int isOptional){
1405 char *zHome; /* Home directory */
1406 char *zDbName; /* Name of the database file */
1407
1408
1409 /* On Windows, look for these directories, in order:
1410 **
1411 ** FOSSIL_HOME
1412 ** LOCALAPPDATA
1413 ** APPDATA
1414 ** USERPROFILE
1415 ** HOMEDRIVE HOMEPATH
1416 */
1417 #if defined(_WIN32) || defined(__CYGWIN__)
1418 zHome = fossil_getenv("FOSSIL_HOME");
1419 if( zHome==0 ){
1420 zHome = fossil_getenv("LOCALAPPDATA");
1421 if( zHome==0 ){
1422 zHome = fossil_getenv("APPDATA");
1423 if( zHome==0 ){
@@ -1428,40 +1428,98 @@
1428 if( zDrive && zPath ) zHome = mprintf("%s%s", zDrive, zPath);
1429 }
1430 }
1431 }
1432 }
1433 zDbName = mprintf("%//_fossil", zHome);
1434 fossil_free(zHome);
1435 return zDbName;
1436
1437 #else /* if unix */
1438 char *zXdgHome;
1439
1440 /* For unix. a 5-step algorithm is used.
1441 ** See ../www/tech_overview.wiki for discussion.
1442 **
1443 ** Step 1: If FOSSIL_HOME exists -> $FOSSIL_HOME/.fossil
1444 */
1445 zHome = fossil_getenv("FOSSIL_HOME");
1446 if( zHome!=0 ) return mprintf("%s/.fossil", zHome);
1447
1448 /* Step 2: If HOME exists and file $HOME/.fossil exists -> $HOME/.fossil
1449 */
1450 zHome = fossil_getenv("HOME");
1451 if( zHome ){
1452 zDbName = mprintf("%s/.fossil", zHome);
1453 if( file_size(zDbName, ExtFILE)>1024*3 ){
1454 return zDbName;
1455 }
1456 fossil_free(zDbName);
1457 }
1458
1459 /* Step 3: if XDG_CONFIG_HOME exists -> $XDG_CONFIG_HOME/fossil.db
1460 */
1461 zXdgHome = fossil_getenv("XDG_CONFIG_HOME");
1462 if( zXdgHome!=0 ){
1463 return mprintf("%s/fossil.db", zXdgHome);
1464 }
1465
1466 /* The HOME variable is required in order to continue.
1467 */
1468 if( zHome==0 ){
1469 if( isOptional ) return 0;
1470 fossil_panic("cannot locate home directory - please set one of the "
1471 "FOSSIL_HOME, XDG_CONFIG_HOME, or HOME environment "
1472 "variables");
1473 }
1474
1475 /* Step 4: If $HOME/.config is a directory -> $HOME/.config/fossil.db
1476 */
1477 zXdgHome = mprintf("%s/.config", zHome);
1478 if( file_isdir(zXdgHome, ExtFILE)==1 ){
1479 fossil_free(zXdgHome);
1480 return mprintf("%s/.config/fossil.db", zHome);
1481 }
1482
1483 /* Step 5: Otherwise -> $HOME/.fossil
1484 */
1485 return mprintf("%s/.fossil", zHome);
1486 #endif /* unix */
1487 }
1488
1489 /*
1490 ** Open the configuration database. Create the database anew if
1491 ** it does not already exist.
1492 **
1493 ** If the useAttach flag is 0 (the usual case) then the configuration
1494 ** database is opened on a separate database connection g.dbConfig.
1495 ** This prevents the database from becoming locked on long check-in or sync
1496 ** operations which hold an exclusive transaction. In a few cases, though,
1497 ** it is convenient for the database to be attached to the main database
1498 ** connection so that we can join between the various databases. In that
1499 ** case, invoke this routine with useAttach as 1.
1500 */
1501 int db_open_config(int useAttach, int isOptional){
1502 char *zDbName;
1503 if( g.zConfigDbName ){
1504 int alreadyAttached = db_database_slot("configdb")>0;
1505 if( useAttach==alreadyAttached ) return 1; /* Already open. */
1506 db_close_config();
1507 }
1508 zDbName = db_configdb_name(isOptional);
1509 if( zDbName==0 ) return 0;
1510 if( file_size(zDbName, ExtFILE)<1024*3 ){
1511 char *zHome = file_dirname(zDbName);
1512 int rc;
1513 if( file_isdir(zHome, ExtFILE)==0 ){
1514 file_mkdir(zHome, ExtFILE, 0);
1515 }
1516 rc = file_access(zHome, W_OK);
1517 fossil_free(zHome);
1518 if( rc ){
1519 if( isOptional ) return 0;
1520 fossil_panic("home directory \"%s\" must be writeable", zHome);
1521 }
1522 db_init_database(zDbName, zConfigSchema, (char*)0);
1523 }
1524 if( file_access(zDbName, W_OK) ){
1525 if( isOptional ) return 0;
@@ -2310,11 +2368,11 @@
2368 ** SQL functions for debugging.
2369 **
2370 ** The print() function writes its arguments on stdout, but only
2371 ** if the -sqlprint command-line option is turned on.
2372 */
2373 void db_sql_print(
2374 sqlite3_context *context,
2375 int argc,
2376 sqlite3_value **argv
2377 ){
2378 int i;
@@ -2506,16 +2564,16 @@
2564 return 0;
2565 }
2566
2567 /*
2568 ** Swap the g.db and g.dbConfig connections so that the various db_* routines
2569 ** work on the configuration database instead of on the repository database.
2570 ** Be sure to swap them back after doing the operation.
2571 **
2572 ** If the configuration database has already been opened as the main database
2573 ** or is attached to the main database, no connection swaps are required so
2574 ** this routine is a no-op.
2575 */
2576 void db_swap_connections(void){
2577 /*
2578 ** When swapping the main database connection with the config database
2579 ** connection, the config database connection must be open (not simply
@@ -3498,10 +3556,14 @@
3556 /*
3557 ** SETTING: pgp-command width=40
3558 ** Command used to clear-sign manifests at check-in.
3559 ** Default value is "gpg --clearsign -o"
3560 */
3561 /*
3562 ** SETTING: forbid-delta-manifests boolean default=off
3563 ** If enabled, new delta manifests are prohibited.
3564 */
3565 /*
3566 ** SETTING: proxy width=32 default=off
3567 ** URL of the HTTP proxy. If undefined or "off" then
3568 ** the "http_proxy" environment variable is consulted.
3569 ** If the http_proxy environment variable is undefined
@@ -3720,14 +3782,13 @@
3782 ** The "unset" command clears a setting.
3783 **
3784 ** Settings can have both a "local" repository-only value and "global" value
3785 ** that applies to all repositories. The local values are stored in the
3786 ** "config" table of the repository and the global values are stored in the
3787 ** configuration database. If both a local and a global value exists for a
3788 ** setting, the local value takes precedence. This command normally operates
3789 ** on the local settings. Use the --global option to change global settings.
 
3790 **
3791 ** Options:
3792 ** --global set or unset the given property globally instead of
3793 ** setting or unsetting it for the open repository only.
3794 **
@@ -3858,12 +3919,12 @@
3919 ** COMMAND: test-without-rowid
3920 **
3921 ** Usage: %fossil test-without-rowid FILENAME...
3922 **
3923 ** Change the Fossil repository FILENAME to make use of the WITHOUT ROWID
3924 ** optimization. FILENAME can also be the configuration database file
3925 ** (~/.fossil or ~/.config/fossil.db) or a local .fslckout or _FOSSIL_ file.
3926 **
3927 ** The purpose of this command is for testing the WITHOUT ROWID capabilities
3928 ** of SQLite. There is no big advantage to using WITHOUT ROWID in Fossil.
3929 **
3930 ** Options:
3931
--- src/default_css.txt
+++ src/default_css.txt
@@ -841,5 +841,24 @@
841841
}
842842
.accordion_panel {
843843
overflow: hidden;
844844
transition: max-height 0.25s ease-out;
845845
}
846
+#setup_skinedit_css_defaults {
847
+ max-width: 98%;
848
+ font-family: monospace;
849
+// These are for the UL-based implementation:
850
+ column-width: auto;
851
+ column-count: 2;
852
+ padding-top: 1em;
853
+}
854
+// These are for the alternate table-based skinedit CSS list:
855
+// #setup_skinedit_css_defaults > tbody > tr > td {
856
+// font-family: monospace;
857
+// white-space: pre-wrap;
858
+// border: 1px solid black;
859
+// vertical-align: top;
860
+// }
861
+// #setup_skinedit_css_defaults > tbody > tr > td:nth-of-type(2) > div {
862
+// max-width: 30em;
863
+// overflow: auto;
864
+// }
846865
--- src/default_css.txt
+++ src/default_css.txt
@@ -841,5 +841,24 @@
841 }
842 .accordion_panel {
843 overflow: hidden;
844 transition: max-height 0.25s ease-out;
845 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
846
--- src/default_css.txt
+++ src/default_css.txt
@@ -841,5 +841,24 @@
841 }
842 .accordion_panel {
843 overflow: hidden;
844 transition: max-height 0.25s ease-out;
845 }
846 #setup_skinedit_css_defaults {
847 max-width: 98%;
848 font-family: monospace;
849 // These are for the UL-based implementation:
850 column-width: auto;
851 column-count: 2;
852 padding-top: 1em;
853 }
854 // These are for the alternate table-based skinedit CSS list:
855 // #setup_skinedit_css_defaults > tbody > tr > td {
856 // font-family: monospace;
857 // white-space: pre-wrap;
858 // border: 1px solid black;
859 // vertical-align: top;
860 // }
861 // #setup_skinedit_css_defaults > tbody > tr > td:nth-of-type(2) > div {
862 // max-width: 30em;
863 // overflow: auto;
864 // }
865
+27 -2
--- src/diff.c
+++ src/diff.c
@@ -2,11 +2,11 @@
22
** Copyright (c) 2007 D. Richard Hipp
33
**
44
** This program is free software; you can redistribute it and/or
55
** modify it under the terms of the Simplified BSD License (also
66
** known as the "2-Clause License" or "FreeBSD License".)
7
-
7
+**
88
** This program is distributed in the hope that it will be useful,
99
** but without any warranty; without even the implied warranty of
1010
** merchantability or fitness for a particular purpose.
1111
**
1212
** Author contact information:
@@ -1826,14 +1826,39 @@
18261826
}
18271827
18281828
/*
18291829
** Extract the width of columns for side-by-side diff. Supply an
18301830
** appropriate default if no width is given.
1831
+**
1832
+** Calculate the default automatically, based on terminal's current width:
1833
+** term-width = 2*diff-col + diff-marker + 1
1834
+** diff-col = lineno + lmargin + text-width + rmargin
1835
+**
1836
+** text-width = (term-width - diff-marker - 1)/2 - lineno - lmargin - rmargin
18311837
*/
18321838
int diff_width(u64 diffFlags){
18331839
int w = (diffFlags & DIFF_WIDTH_MASK)/(DIFF_CONTEXT_MASK+1);
1834
- if( w==0 ) w = 80;
1840
+ if( w==0 ){
1841
+ static struct {
1842
+ unsigned int lineno, lmargin, text, rmargin, marker;
1843
+ } sbsW = { 5, 2, 0, 0, 3 };
1844
+ const unsigned int wMin = 24, wMax = 132;
1845
+ unsigned int tw = terminal_get_width(80);
1846
+ unsigned int twMin =
1847
+ (wMin + sbsW.lineno + sbsW.lmargin + sbsW.rmargin)*2 + sbsW.marker + 1;
1848
+ unsigned int twMax =
1849
+ (wMax + sbsW.lineno + sbsW.lmargin + sbsW.rmargin)*2 + sbsW.marker + 1;
1850
+
1851
+ if( tw<twMin ){
1852
+ tw = twMin;
1853
+ }else if( tw>twMax ){
1854
+ tw = twMax;
1855
+ }
1856
+ sbsW.text =
1857
+ (tw - sbsW.marker - 1)/2 - sbsW.lineno - sbsW.lmargin - sbsW.rmargin;
1858
+ w = sbsW.text;
1859
+ }
18351860
return w;
18361861
}
18371862
18381863
/*
18391864
** Append the error message to pOut.
18401865
--- src/diff.c
+++ src/diff.c
@@ -2,11 +2,11 @@
2 ** Copyright (c) 2007 D. Richard Hipp
3 **
4 ** This program is free software; you can redistribute it and/or
5 ** modify it under the terms of the Simplified BSD License (also
6 ** known as the "2-Clause License" or "FreeBSD License".)
7
8 ** This program is distributed in the hope that it will be useful,
9 ** but without any warranty; without even the implied warranty of
10 ** merchantability or fitness for a particular purpose.
11 **
12 ** Author contact information:
@@ -1826,14 +1826,39 @@
1826 }
1827
1828 /*
1829 ** Extract the width of columns for side-by-side diff. Supply an
1830 ** appropriate default if no width is given.
 
 
 
 
 
 
1831 */
1832 int diff_width(u64 diffFlags){
1833 int w = (diffFlags & DIFF_WIDTH_MASK)/(DIFF_CONTEXT_MASK+1);
1834 if( w==0 ) w = 80;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1835 return w;
1836 }
1837
1838 /*
1839 ** Append the error message to pOut.
1840
--- src/diff.c
+++ src/diff.c
@@ -2,11 +2,11 @@
2 ** Copyright (c) 2007 D. Richard Hipp
3 **
4 ** This program is free software; you can redistribute it and/or
5 ** modify it under the terms of the Simplified BSD License (also
6 ** known as the "2-Clause License" or "FreeBSD License".)
7 **
8 ** This program is distributed in the hope that it will be useful,
9 ** but without any warranty; without even the implied warranty of
10 ** merchantability or fitness for a particular purpose.
11 **
12 ** Author contact information:
@@ -1826,14 +1826,39 @@
1826 }
1827
1828 /*
1829 ** Extract the width of columns for side-by-side diff. Supply an
1830 ** appropriate default if no width is given.
1831 **
1832 ** Calculate the default automatically, based on terminal's current width:
1833 ** term-width = 2*diff-col + diff-marker + 1
1834 ** diff-col = lineno + lmargin + text-width + rmargin
1835 **
1836 ** text-width = (term-width - diff-marker - 1)/2 - lineno - lmargin - rmargin
1837 */
1838 int diff_width(u64 diffFlags){
1839 int w = (diffFlags & DIFF_WIDTH_MASK)/(DIFF_CONTEXT_MASK+1);
1840 if( w==0 ){
1841 static struct {
1842 unsigned int lineno, lmargin, text, rmargin, marker;
1843 } sbsW = { 5, 2, 0, 0, 3 };
1844 const unsigned int wMin = 24, wMax = 132;
1845 unsigned int tw = terminal_get_width(80);
1846 unsigned int twMin =
1847 (wMin + sbsW.lineno + sbsW.lmargin + sbsW.rmargin)*2 + sbsW.marker + 1;
1848 unsigned int twMax =
1849 (wMax + sbsW.lineno + sbsW.lmargin + sbsW.rmargin)*2 + sbsW.marker + 1;
1850
1851 if( tw<twMin ){
1852 tw = twMin;
1853 }else if( tw>twMax ){
1854 tw = twMax;
1855 }
1856 sbsW.text =
1857 (tw - sbsW.marker - 1)/2 - sbsW.lineno - sbsW.lmargin - sbsW.rmargin;
1858 w = sbsW.text;
1859 }
1860 return w;
1861 }
1862
1863 /*
1864 ** Append the error message to pOut.
1865
+43 -6
--- src/etag.c
+++ src/etag.c
@@ -24,10 +24,12 @@
2424
** (1) The mtime on the Fossil executable
2525
** (2) The last change to the CONFIG table
2626
** (3) The last change to the EVENT table
2727
** (4) The value of the display cookie
2828
** (5) A hash value supplied by the page generator
29
+** (6) The details of the request URI
30
+** (7) The name user as determined by the login cookie
2931
**
3032
** Item (1) is always included in the ETag. The other elements are
3133
** optional. Because (1) is always included as part of the ETag, all
3234
** outstanding ETags can be invalidated by touching the fossil executable.
3335
**
@@ -60,32 +62,50 @@
6062
*/
6163
#define ETAG_CONFIG 0x01 /* Output depends on the CONFIG table */
6264
#define ETAG_DATA 0x02 /* Output depends on the EVENT table */
6365
#define ETAG_COOKIE 0x04 /* Output depends on a display cookie value */
6466
#define ETAG_HASH 0x08 /* Output depends on a hash */
67
+#define ETAG_QUERY 0x10 /* Output depends on PATH_INFO and QUERY_STRING */
68
+ /* and the g.zLogin value */
6569
#endif
6670
6771
static char zETag[33]; /* The generated ETag */
6872
static int iMaxAge = 0; /* The max-age parameter in the reply */
6973
static sqlite3_int64 iEtagMtime = 0; /* Last-Modified time */
74
+
75
+/*
76
+** Return a hash that changes every time the Fossil source code is
77
+** rebuilt.
78
+**
79
+** The FOSSIL_BUILD_HASH string that is returned here gets computed by
80
+** the mkversion utility program. The result is a hash of MANIFEST_UUID
81
+** and the unix timestamp for when the mkversion utility program is run.
82
+**
83
+** During development rebuilds, if you need the source code id to change
84
+** in order to invalidate caches, simply "touch" the "manifest" file in
85
+** the top of the source directory prior to running "make" and a new
86
+** FOSSIL_BUILD_HASH will be generated automatically.
87
+*/
88
+const char *fossil_exe_id(void){
89
+ return FOSSIL_BUILD_HASH;
90
+}
7091
7192
/*
7293
** Generate an ETag
7394
*/
7495
void etag_check(unsigned eFlags, const char *zHash){
75
- sqlite3_int64 mtime;
7696
const char *zIfNoneMatch;
7797
char zBuf[50];
7898
assert( zETag[0]==0 ); /* Only call this routine once! */
7999
80100
iMaxAge = 86400;
81101
md5sum_init();
82102
83
- /* Always include the mtime of the executable as part of the hash */
84
- mtime = file_mtime(g.nameOfExe, ExtFILE);
85
- sqlite3_snprintf(sizeof(zBuf),zBuf,"mtime: %lld\n", mtime);
86
- md5sum_step_text(zBuf, -1);
103
+ /* Always include the executable ID as part of the hash */
104
+ md5sum_step_text("exe-id: ", -1);
105
+ md5sum_step_text(fossil_exe_id(), -1);
106
+ md5sum_step_text("\n", 1);
87107
88108
if( (eFlags & ETAG_HASH)!=0 && zHash ){
89109
md5sum_step_text("hash: ", -1);
90110
md5sum_step_text(zHash, -1);
91111
md5sum_step_text("\n", 1);
@@ -98,11 +118,11 @@
98118
md5sum_step_text("\n", 1);
99119
iMaxAge = 60;
100120
}else if( eFlags & ETAG_CONFIG ){
101121
int iKey = db_int(0, "SELECT value FROM config WHERE name='cfgcnt'");
102122
sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",iKey);
103
- md5sum_step_text("data: ", -1);
123
+ md5sum_step_text("config: ", -1);
104124
md5sum_step_text(zBuf, -1);
105125
md5sum_step_text("\n", 1);
106126
iMaxAge = 3600;
107127
}
108128
@@ -111,10 +131,27 @@
111131
md5sum_step_text("display-cookie: ", -1);
112132
md5sum_step_text(PD(DISPLAY_SETTINGS_COOKIE,""), -1);
113133
md5sum_step_text("\n", 1);
114134
iMaxAge = 0;
115135
}
136
+
137
+ /* Output depends on PATH_INFO and QUERY_STRING */
138
+ if( eFlags & ETAG_QUERY ){
139
+ const char *zQS = P("QUERY_STRING");
140
+ md5sum_step_text("query: ", -1);
141
+ md5sum_step_text(PD("PATH_INFO",""), -1);
142
+ if( zQS ){
143
+ md5sum_step_text("?", 1);
144
+ md5sum_step_text(zQS, -1);
145
+ }
146
+ md5sum_step_text("\n",1);
147
+ if( g.zLogin ){
148
+ md5sum_step_text("login: ", -1);
149
+ md5sum_step_text(g.zLogin, -1);
150
+ md5sum_step_text("\n", 1);
151
+ }
152
+ }
116153
117154
/* Generate the ETag */
118155
memcpy(zETag, md5sum_finish(0), 33);
119156
120157
/* Check to see if the generated ETag matches If-None-Match and
121158
--- src/etag.c
+++ src/etag.c
@@ -24,10 +24,12 @@
24 ** (1) The mtime on the Fossil executable
25 ** (2) The last change to the CONFIG table
26 ** (3) The last change to the EVENT table
27 ** (4) The value of the display cookie
28 ** (5) A hash value supplied by the page generator
 
 
29 **
30 ** Item (1) is always included in the ETag. The other elements are
31 ** optional. Because (1) is always included as part of the ETag, all
32 ** outstanding ETags can be invalidated by touching the fossil executable.
33 **
@@ -60,32 +62,50 @@
60 */
61 #define ETAG_CONFIG 0x01 /* Output depends on the CONFIG table */
62 #define ETAG_DATA 0x02 /* Output depends on the EVENT table */
63 #define ETAG_COOKIE 0x04 /* Output depends on a display cookie value */
64 #define ETAG_HASH 0x08 /* Output depends on a hash */
 
 
65 #endif
66
67 static char zETag[33]; /* The generated ETag */
68 static int iMaxAge = 0; /* The max-age parameter in the reply */
69 static sqlite3_int64 iEtagMtime = 0; /* Last-Modified time */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
71 /*
72 ** Generate an ETag
73 */
74 void etag_check(unsigned eFlags, const char *zHash){
75 sqlite3_int64 mtime;
76 const char *zIfNoneMatch;
77 char zBuf[50];
78 assert( zETag[0]==0 ); /* Only call this routine once! */
79
80 iMaxAge = 86400;
81 md5sum_init();
82
83 /* Always include the mtime of the executable as part of the hash */
84 mtime = file_mtime(g.nameOfExe, ExtFILE);
85 sqlite3_snprintf(sizeof(zBuf),zBuf,"mtime: %lld\n", mtime);
86 md5sum_step_text(zBuf, -1);
87
88 if( (eFlags & ETAG_HASH)!=0 && zHash ){
89 md5sum_step_text("hash: ", -1);
90 md5sum_step_text(zHash, -1);
91 md5sum_step_text("\n", 1);
@@ -98,11 +118,11 @@
98 md5sum_step_text("\n", 1);
99 iMaxAge = 60;
100 }else if( eFlags & ETAG_CONFIG ){
101 int iKey = db_int(0, "SELECT value FROM config WHERE name='cfgcnt'");
102 sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",iKey);
103 md5sum_step_text("data: ", -1);
104 md5sum_step_text(zBuf, -1);
105 md5sum_step_text("\n", 1);
106 iMaxAge = 3600;
107 }
108
@@ -111,10 +131,27 @@
111 md5sum_step_text("display-cookie: ", -1);
112 md5sum_step_text(PD(DISPLAY_SETTINGS_COOKIE,""), -1);
113 md5sum_step_text("\n", 1);
114 iMaxAge = 0;
115 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
117 /* Generate the ETag */
118 memcpy(zETag, md5sum_finish(0), 33);
119
120 /* Check to see if the generated ETag matches If-None-Match and
121
--- src/etag.c
+++ src/etag.c
@@ -24,10 +24,12 @@
24 ** (1) The mtime on the Fossil executable
25 ** (2) The last change to the CONFIG table
26 ** (3) The last change to the EVENT table
27 ** (4) The value of the display cookie
28 ** (5) A hash value supplied by the page generator
29 ** (6) The details of the request URI
30 ** (7) The name user as determined by the login cookie
31 **
32 ** Item (1) is always included in the ETag. The other elements are
33 ** optional. Because (1) is always included as part of the ETag, all
34 ** outstanding ETags can be invalidated by touching the fossil executable.
35 **
@@ -60,32 +62,50 @@
62 */
63 #define ETAG_CONFIG 0x01 /* Output depends on the CONFIG table */
64 #define ETAG_DATA 0x02 /* Output depends on the EVENT table */
65 #define ETAG_COOKIE 0x04 /* Output depends on a display cookie value */
66 #define ETAG_HASH 0x08 /* Output depends on a hash */
67 #define ETAG_QUERY 0x10 /* Output depends on PATH_INFO and QUERY_STRING */
68 /* and the g.zLogin value */
69 #endif
70
71 static char zETag[33]; /* The generated ETag */
72 static int iMaxAge = 0; /* The max-age parameter in the reply */
73 static sqlite3_int64 iEtagMtime = 0; /* Last-Modified time */
74
75 /*
76 ** Return a hash that changes every time the Fossil source code is
77 ** rebuilt.
78 **
79 ** The FOSSIL_BUILD_HASH string that is returned here gets computed by
80 ** the mkversion utility program. The result is a hash of MANIFEST_UUID
81 ** and the unix timestamp for when the mkversion utility program is run.
82 **
83 ** During development rebuilds, if you need the source code id to change
84 ** in order to invalidate caches, simply "touch" the "manifest" file in
85 ** the top of the source directory prior to running "make" and a new
86 ** FOSSIL_BUILD_HASH will be generated automatically.
87 */
88 const char *fossil_exe_id(void){
89 return FOSSIL_BUILD_HASH;
90 }
91
92 /*
93 ** Generate an ETag
94 */
95 void etag_check(unsigned eFlags, const char *zHash){
 
96 const char *zIfNoneMatch;
97 char zBuf[50];
98 assert( zETag[0]==0 ); /* Only call this routine once! */
99
100 iMaxAge = 86400;
101 md5sum_init();
102
103 /* Always include the executable ID as part of the hash */
104 md5sum_step_text("exe-id: ", -1);
105 md5sum_step_text(fossil_exe_id(), -1);
106 md5sum_step_text("\n", 1);
107
108 if( (eFlags & ETAG_HASH)!=0 && zHash ){
109 md5sum_step_text("hash: ", -1);
110 md5sum_step_text(zHash, -1);
111 md5sum_step_text("\n", 1);
@@ -98,11 +118,11 @@
118 md5sum_step_text("\n", 1);
119 iMaxAge = 60;
120 }else if( eFlags & ETAG_CONFIG ){
121 int iKey = db_int(0, "SELECT value FROM config WHERE name='cfgcnt'");
122 sqlite3_snprintf(sizeof(zBuf),zBuf,"%d",iKey);
123 md5sum_step_text("config: ", -1);
124 md5sum_step_text(zBuf, -1);
125 md5sum_step_text("\n", 1);
126 iMaxAge = 3600;
127 }
128
@@ -111,10 +131,27 @@
131 md5sum_step_text("display-cookie: ", -1);
132 md5sum_step_text(PD(DISPLAY_SETTINGS_COOKIE,""), -1);
133 md5sum_step_text("\n", 1);
134 iMaxAge = 0;
135 }
136
137 /* Output depends on PATH_INFO and QUERY_STRING */
138 if( eFlags & ETAG_QUERY ){
139 const char *zQS = P("QUERY_STRING");
140 md5sum_step_text("query: ", -1);
141 md5sum_step_text(PD("PATH_INFO",""), -1);
142 if( zQS ){
143 md5sum_step_text("?", 1);
144 md5sum_step_text(zQS, -1);
145 }
146 md5sum_step_text("\n",1);
147 if( g.zLogin ){
148 md5sum_step_text("login: ", -1);
149 md5sum_step_text(g.zLogin, -1);
150 md5sum_step_text("\n", 1);
151 }
152 }
153
154 /* Generate the ETag */
155 memcpy(zETag, md5sum_finish(0), 33);
156
157 /* Check to see if the generated ETag matches If-None-Match and
158
+101 -1
--- src/file.c
+++ src/file.c
@@ -294,11 +294,11 @@
294294
**
295295
** On windows, this routine returns only PERM_REG.
296296
*/
297297
int file_perm(const char *zFilename, int eFType){
298298
#if !defined(_WIN32)
299
- if( !getStat(zFilename, RepoFILE) ){
299
+ if( !getStat(zFilename, eFType) ){
300300
if( S_ISREG(fx.fileStat.st_mode) && ((S_IXUSR)&fx.fileStat.st_mode)!=0 )
301301
return PERM_EXE;
302302
else if( db_allow_symlinks() && S_ISLNK(fx.fileStat.st_mode) )
303303
return PERM_LNK;
304304
}
@@ -346,10 +346,50 @@
346346
rc = 2; /* It exists and is something else. */
347347
}
348348
free(zFN);
349349
return rc;
350350
}
351
+
352
+/*
353
+** Return true (1) if zFilename seems like it seems like a valid
354
+** repository database.
355
+*/
356
+int file_is_repository(const char *zFilename){
357
+ i64 sz;
358
+ sqlite3 *db = 0;
359
+ sqlite3_stmt *pStmt = 0;
360
+ int rc;
361
+ int i;
362
+ static const char *azReqTab[] = {
363
+ "blob", "delta", "rcvfrom", "user", "config"
364
+ };
365
+ if( !file_isfile(zFilename, ExtFILE) ) return 0;
366
+ sz = file_size(zFilename, ExtFILE);
367
+ if( sz<35328 ) return 0;
368
+ if( sz%512!=0 ) return 0;
369
+ rc = sqlite3_open_v2(zFilename, &db,
370
+ SQLITE_OPEN_READWRITE, 0);
371
+ if( rc!=0 ) goto not_a_repo;
372
+ for(i=0; i<count(azReqTab); i++){
373
+ if( sqlite3_table_column_metadata(db, "main", azReqTab[i],0,0,0,0,0,0) ){
374
+ goto not_a_repo;
375
+ }
376
+ }
377
+ rc = sqlite3_prepare_v2(db, "SELECT 1 FROM config WHERE name='project-code'",
378
+ -1, &pStmt, 0);
379
+ if( rc ) goto not_a_repo;
380
+ rc = sqlite3_step(pStmt);
381
+ if( rc!=SQLITE_ROW ) goto not_a_repo;
382
+ sqlite3_finalize(pStmt);
383
+ sqlite3_close(db);
384
+ return 1;
385
+
386
+not_a_repo:
387
+ sqlite3_finalize(pStmt);
388
+ sqlite3_close(db);
389
+ return 0;
390
+}
351391
352392
353393
/*
354394
** Wrapper around the access() system call.
355395
*/
@@ -1099,10 +1139,69 @@
10991139
}
11001140
#endif
11011141
blob_resize(pOut, file_simplify_name(blob_buffer(pOut),
11021142
blob_size(pOut), slash));
11031143
}
1144
+
1145
+/*
1146
+** The input is the name of an executable, such as one might
1147
+** type on a command-line. This routine resolves that name into
1148
+** a full pathname. The result is obtained from fossil_malloc()
1149
+** and should be freed by the caller.
1150
+**
1151
+** This routine only works on unix. On Windows, simply return
1152
+** a copy of the input.
1153
+*/
1154
+char *file_fullexename(const char *zCmd){
1155
+#ifdef _WIN32
1156
+ return fossil_strdup(zCmd);
1157
+#else
1158
+ char *zPath;
1159
+ char *z;
1160
+ if( zCmd[0]=='/' ){
1161
+ return fossil_strdup(zCmd);
1162
+ }
1163
+ if( strchr(zCmd,'/')!=0 ){
1164
+ Blob out = BLOB_INITIALIZER;
1165
+ file_canonical_name(zCmd, &out, 0);
1166
+ z = fossil_strdup(blob_str(&out));
1167
+ blob_reset(&out);
1168
+ return z;
1169
+ }
1170
+ zPath = fossil_getenv("PATH");
1171
+ while( zPath && zPath[0] ){
1172
+ int n;
1173
+ char *zColon;
1174
+ zColon = strchr(zPath, ':');
1175
+ n = zColon ? (int)(zColon-zPath) : (int)strlen(zPath);
1176
+ z = mprintf("%.*s/%s", n, zPath, zCmd);
1177
+ if( file_isexe(z, ExtFILE) ){
1178
+ return z;
1179
+ }
1180
+ fossil_free(z);
1181
+ if( zColon==0 ) break;
1182
+ zPath = zColon+1;
1183
+ }
1184
+ return fossil_strdup(zCmd);
1185
+#endif
1186
+}
1187
+
1188
+/*
1189
+** COMMAND: test-which
1190
+**
1191
+** Usage: %fossil test-which ARGS...
1192
+**
1193
+** For each argument, search the PATH for the executable with the name
1194
+** and print its full pathname.
1195
+*/
1196
+void test_which_cmd(void){
1197
+ int i;
1198
+ for(i=2; i<g.argc; i++){
1199
+ char *z = file_fullexename(g.argv[i]);
1200
+ fossil_print("%z\n", z);
1201
+ }
1202
+}
11041203
11051204
/*
11061205
** Emits the effective or raw stat() information for the specified
11071206
** file or directory, optionally preserving the trailing slash and
11081207
** resetting the cached stat() information.
@@ -1167,10 +1266,11 @@
11671266
fossil_print(" file_isfile(RepoFILE) = %d\n", file_isfile(zPath,RepoFILE));
11681267
fossil_print(" file_isfile_or_link = %d\n", file_isfile_or_link(zPath));
11691268
fossil_print(" file_islink = %d\n", file_islink(zPath));
11701269
fossil_print(" file_isexe(RepoFILE) = %d\n", file_isexe(zPath,RepoFILE));
11711270
fossil_print(" file_isdir(RepoFILE) = %d\n", file_isdir(zPath,RepoFILE));
1271
+ fossil_print(" file_is_repository = %d\n", file_is_repository(zPath));
11721272
if( reset ) resetStat();
11731273
}
11741274
11751275
/*
11761276
** COMMAND: test-file-environment
11771277
--- src/file.c
+++ src/file.c
@@ -294,11 +294,11 @@
294 **
295 ** On windows, this routine returns only PERM_REG.
296 */
297 int file_perm(const char *zFilename, int eFType){
298 #if !defined(_WIN32)
299 if( !getStat(zFilename, RepoFILE) ){
300 if( S_ISREG(fx.fileStat.st_mode) && ((S_IXUSR)&fx.fileStat.st_mode)!=0 )
301 return PERM_EXE;
302 else if( db_allow_symlinks() && S_ISLNK(fx.fileStat.st_mode) )
303 return PERM_LNK;
304 }
@@ -346,10 +346,50 @@
346 rc = 2; /* It exists and is something else. */
347 }
348 free(zFN);
349 return rc;
350 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351
352
353 /*
354 ** Wrapper around the access() system call.
355 */
@@ -1099,10 +1139,69 @@
1099 }
1100 #endif
1101 blob_resize(pOut, file_simplify_name(blob_buffer(pOut),
1102 blob_size(pOut), slash));
1103 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1104
1105 /*
1106 ** Emits the effective or raw stat() information for the specified
1107 ** file or directory, optionally preserving the trailing slash and
1108 ** resetting the cached stat() information.
@@ -1167,10 +1266,11 @@
1167 fossil_print(" file_isfile(RepoFILE) = %d\n", file_isfile(zPath,RepoFILE));
1168 fossil_print(" file_isfile_or_link = %d\n", file_isfile_or_link(zPath));
1169 fossil_print(" file_islink = %d\n", file_islink(zPath));
1170 fossil_print(" file_isexe(RepoFILE) = %d\n", file_isexe(zPath,RepoFILE));
1171 fossil_print(" file_isdir(RepoFILE) = %d\n", file_isdir(zPath,RepoFILE));
 
1172 if( reset ) resetStat();
1173 }
1174
1175 /*
1176 ** COMMAND: test-file-environment
1177
--- src/file.c
+++ src/file.c
@@ -294,11 +294,11 @@
294 **
295 ** On windows, this routine returns only PERM_REG.
296 */
297 int file_perm(const char *zFilename, int eFType){
298 #if !defined(_WIN32)
299 if( !getStat(zFilename, eFType) ){
300 if( S_ISREG(fx.fileStat.st_mode) && ((S_IXUSR)&fx.fileStat.st_mode)!=0 )
301 return PERM_EXE;
302 else if( db_allow_symlinks() && S_ISLNK(fx.fileStat.st_mode) )
303 return PERM_LNK;
304 }
@@ -346,10 +346,50 @@
346 rc = 2; /* It exists and is something else. */
347 }
348 free(zFN);
349 return rc;
350 }
351
352 /*
353 ** Return true (1) if zFilename seems like it seems like a valid
354 ** repository database.
355 */
356 int file_is_repository(const char *zFilename){
357 i64 sz;
358 sqlite3 *db = 0;
359 sqlite3_stmt *pStmt = 0;
360 int rc;
361 int i;
362 static const char *azReqTab[] = {
363 "blob", "delta", "rcvfrom", "user", "config"
364 };
365 if( !file_isfile(zFilename, ExtFILE) ) return 0;
366 sz = file_size(zFilename, ExtFILE);
367 if( sz<35328 ) return 0;
368 if( sz%512!=0 ) return 0;
369 rc = sqlite3_open_v2(zFilename, &db,
370 SQLITE_OPEN_READWRITE, 0);
371 if( rc!=0 ) goto not_a_repo;
372 for(i=0; i<count(azReqTab); i++){
373 if( sqlite3_table_column_metadata(db, "main", azReqTab[i],0,0,0,0,0,0) ){
374 goto not_a_repo;
375 }
376 }
377 rc = sqlite3_prepare_v2(db, "SELECT 1 FROM config WHERE name='project-code'",
378 -1, &pStmt, 0);
379 if( rc ) goto not_a_repo;
380 rc = sqlite3_step(pStmt);
381 if( rc!=SQLITE_ROW ) goto not_a_repo;
382 sqlite3_finalize(pStmt);
383 sqlite3_close(db);
384 return 1;
385
386 not_a_repo:
387 sqlite3_finalize(pStmt);
388 sqlite3_close(db);
389 return 0;
390 }
391
392
393 /*
394 ** Wrapper around the access() system call.
395 */
@@ -1099,10 +1139,69 @@
1139 }
1140 #endif
1141 blob_resize(pOut, file_simplify_name(blob_buffer(pOut),
1142 blob_size(pOut), slash));
1143 }
1144
1145 /*
1146 ** The input is the name of an executable, such as one might
1147 ** type on a command-line. This routine resolves that name into
1148 ** a full pathname. The result is obtained from fossil_malloc()
1149 ** and should be freed by the caller.
1150 **
1151 ** This routine only works on unix. On Windows, simply return
1152 ** a copy of the input.
1153 */
1154 char *file_fullexename(const char *zCmd){
1155 #ifdef _WIN32
1156 return fossil_strdup(zCmd);
1157 #else
1158 char *zPath;
1159 char *z;
1160 if( zCmd[0]=='/' ){
1161 return fossil_strdup(zCmd);
1162 }
1163 if( strchr(zCmd,'/')!=0 ){
1164 Blob out = BLOB_INITIALIZER;
1165 file_canonical_name(zCmd, &out, 0);
1166 z = fossil_strdup(blob_str(&out));
1167 blob_reset(&out);
1168 return z;
1169 }
1170 zPath = fossil_getenv("PATH");
1171 while( zPath && zPath[0] ){
1172 int n;
1173 char *zColon;
1174 zColon = strchr(zPath, ':');
1175 n = zColon ? (int)(zColon-zPath) : (int)strlen(zPath);
1176 z = mprintf("%.*s/%s", n, zPath, zCmd);
1177 if( file_isexe(z, ExtFILE) ){
1178 return z;
1179 }
1180 fossil_free(z);
1181 if( zColon==0 ) break;
1182 zPath = zColon+1;
1183 }
1184 return fossil_strdup(zCmd);
1185 #endif
1186 }
1187
1188 /*
1189 ** COMMAND: test-which
1190 **
1191 ** Usage: %fossil test-which ARGS...
1192 **
1193 ** For each argument, search the PATH for the executable with the name
1194 ** and print its full pathname.
1195 */
1196 void test_which_cmd(void){
1197 int i;
1198 for(i=2; i<g.argc; i++){
1199 char *z = file_fullexename(g.argv[i]);
1200 fossil_print("%z\n", z);
1201 }
1202 }
1203
1204 /*
1205 ** Emits the effective or raw stat() information for the specified
1206 ** file or directory, optionally preserving the trailing slash and
1207 ** resetting the cached stat() information.
@@ -1167,10 +1266,11 @@
1266 fossil_print(" file_isfile(RepoFILE) = %d\n", file_isfile(zPath,RepoFILE));
1267 fossil_print(" file_isfile_or_link = %d\n", file_isfile_or_link(zPath));
1268 fossil_print(" file_islink = %d\n", file_islink(zPath));
1269 fossil_print(" file_isexe(RepoFILE) = %d\n", file_isexe(zPath,RepoFILE));
1270 fossil_print(" file_isdir(RepoFILE) = %d\n", file_isdir(zPath,RepoFILE));
1271 fossil_print(" file_is_repository = %d\n", file_is_repository(zPath));
1272 if( reset ) resetStat();
1273 }
1274
1275 /*
1276 ** COMMAND: test-file-environment
1277
+15 -11
--- src/finfo.c
+++ src/finfo.c
@@ -199,11 +199,11 @@
199199
TAG_BRANCH, zFilename, filename_collation(),
200200
iLimit, iOffset
201201
);
202202
blob_zero(&line);
203203
if( iBrief ){
204
- fossil_print("History of %s\n", blob_str(&fname));
204
+ fossil_print("History for %s\n", blob_str(&fname));
205205
}
206206
while( db_step(&q)==SQLITE_ROW ){
207207
const char *zFileUuid = db_column_text(&q, 0);
208208
const char *zCiUuid = db_column_text(&q,1);
209209
const char *zDate = db_column_text(&q, 2);
@@ -296,11 +296,11 @@
296296
** timezone offset from UTC as "-HH:MM" (westward) or "+HH:MM"
297297
** (eastward). Either no timezone suffix or "Z" means UTC.
298298
*/
299299
void finfo_page(void){
300300
Stmt q;
301
- const char *zFilename;
301
+ const char *zFilename = PD("name","");
302302
char zPrevDate[20];
303303
const char *zA;
304304
const char *zB;
305305
int n;
306306
int baseCheckin;
@@ -321,11 +321,16 @@
321321
const char *zMark; /* Mark this version of the file */
322322
int selRid = 0; /* RID of the marked file version */
323323
324324
login_check_credentials();
325325
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
326
- style_header("File History");
326
+ fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename);
327
+ if( fnid==0 ){
328
+ style_header("No such file");
329
+ }else{
330
+ style_header("History for %s", zFilename);
331
+ }
327332
login_anonymous_available();
328333
tmFlags = timeline_ss_submenu();
329334
if( tmFlags & TIMELINE_COLUMNAR ){
330335
zStyle = "Columnar";
331336
}else if( tmFlags & TIMELINE_COMPACT ){
@@ -340,13 +345,11 @@
340345
url_initialize(&url, "finfo");
341346
if( brBg ) url_add_parameter(&url, "brbg", 0);
342347
if( uBg ) url_add_parameter(&url, "ubg", 0);
343348
baseCheckin = name_to_rid_www("ci");
344349
zPrevDate[0] = 0;
345
- zFilename = PD("name","");
346350
cookie_render();
347
- fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename);
348351
if( fnid==0 ){
349352
@ No such file: %h(zFilename)
350353
style_footer();
351354
return;
352355
}
@@ -435,12 +438,13 @@
435438
}else if( n>0 ){
436439
blob_appendf(&title, "First %d ancestors of file ", n);
437440
}else{
438441
blob_appendf(&title, "Ancestors of file ");
439442
}
440
- blob_appendf(&title,"<a href='%R/finfo?name=%T'>%h</a>",
441
- zFilename, zFilename);
443
+ blob_appendf(&title,"%z%h</a>",
444
+ href("%R/file?name=%T&ci=%!S", zFilename, zUuid),
445
+ zFilename);
442446
if( fShowId ) blob_appendf(&title, " (%d)", fnid);
443447
blob_append(&title, origCheckin ? " between " : " from ", -1);
444448
blob_appendf(&title, "check-in %z%S</a>", zLink, zUuid);
445449
if( fShowId ) blob_appendf(&title, " (%d)", baseCheckin);
446450
fossil_free(zUuid);
@@ -449,12 +453,12 @@
449453
zLink = href("%R/info/%!S", zUuid);
450454
blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid);
451455
fossil_free(zUuid);
452456
}
453457
}else{
454
- blob_appendf(&title, "History of ");
455
- hyperlinked_path(zFilename, &title, 0, "tree", "");
458
+ blob_appendf(&title, "History for ");
459
+ hyperlinked_path(zFilename, &title, 0, "tree", "", LINKPATH_FILE);
456460
if( fShowId ) blob_appendf(&title, " (%d)", fnid);
457461
}
458462
if( uBg ){
459463
blob_append(&title, " (color-coded by user)", -1);
460464
}
@@ -524,11 +528,11 @@
524528
@ <tr class='timelineSelected'>
525529
}else{
526530
@ <tr>
527531
}
528532
@ <td class="timelineTime">\
529
- @ %z(href("%R/artifact/%!S",zUuid))%s(zTime)</a></td>
533
+ @ %z(href("%R/file?name=%T&ci=%!S",zFilename,zCkin))%s(zTime)</a></td>
530534
@ <td class="timelineGraph"><div id="m%d(gidx)" class="tl-nodemark"></div>
531535
@ </td>
532536
if( zBgClr && zBgClr[0] ){
533537
@ <td class="timeline%s(zStyle)Cell" id='mc%d(gidx)'>
534538
}else{
@@ -561,11 +565,11 @@
561565
cgi_printf("<span class='clutter' id='detail-%d'>",frid);
562566
}
563567
cgi_printf("<span class='timeline%sDetail'>", zStyle);
564568
if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ) cgi_printf("(");
565569
if( zUuid && (tmFlags & TIMELINE_VERBOSE)==0 ){
566
- @ file:&nbsp;%z(href("%R/artifact/%!S",zUuid))[%S(zUuid)]</a>
570
+ @ file:&nbsp;%z(href("%R/file?name=%T&ci=%!S",zFilename,zCkin))[%S(zUuid)]</a>
567571
if( fShowId ){
568572
int srcId = delta_source_rid(frid);
569573
if( srcId>0 ){
570574
@ id:&nbsp;%d(frid)&larr;%d(srcId)
571575
}else{
572576
--- src/finfo.c
+++ src/finfo.c
@@ -199,11 +199,11 @@
199 TAG_BRANCH, zFilename, filename_collation(),
200 iLimit, iOffset
201 );
202 blob_zero(&line);
203 if( iBrief ){
204 fossil_print("History of %s\n", blob_str(&fname));
205 }
206 while( db_step(&q)==SQLITE_ROW ){
207 const char *zFileUuid = db_column_text(&q, 0);
208 const char *zCiUuid = db_column_text(&q,1);
209 const char *zDate = db_column_text(&q, 2);
@@ -296,11 +296,11 @@
296 ** timezone offset from UTC as "-HH:MM" (westward) or "+HH:MM"
297 ** (eastward). Either no timezone suffix or "Z" means UTC.
298 */
299 void finfo_page(void){
300 Stmt q;
301 const char *zFilename;
302 char zPrevDate[20];
303 const char *zA;
304 const char *zB;
305 int n;
306 int baseCheckin;
@@ -321,11 +321,16 @@
321 const char *zMark; /* Mark this version of the file */
322 int selRid = 0; /* RID of the marked file version */
323
324 login_check_credentials();
325 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
326 style_header("File History");
 
 
 
 
 
327 login_anonymous_available();
328 tmFlags = timeline_ss_submenu();
329 if( tmFlags & TIMELINE_COLUMNAR ){
330 zStyle = "Columnar";
331 }else if( tmFlags & TIMELINE_COMPACT ){
@@ -340,13 +345,11 @@
340 url_initialize(&url, "finfo");
341 if( brBg ) url_add_parameter(&url, "brbg", 0);
342 if( uBg ) url_add_parameter(&url, "ubg", 0);
343 baseCheckin = name_to_rid_www("ci");
344 zPrevDate[0] = 0;
345 zFilename = PD("name","");
346 cookie_render();
347 fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename);
348 if( fnid==0 ){
349 @ No such file: %h(zFilename)
350 style_footer();
351 return;
352 }
@@ -435,12 +438,13 @@
435 }else if( n>0 ){
436 blob_appendf(&title, "First %d ancestors of file ", n);
437 }else{
438 blob_appendf(&title, "Ancestors of file ");
439 }
440 blob_appendf(&title,"<a href='%R/finfo?name=%T'>%h</a>",
441 zFilename, zFilename);
 
442 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
443 blob_append(&title, origCheckin ? " between " : " from ", -1);
444 blob_appendf(&title, "check-in %z%S</a>", zLink, zUuid);
445 if( fShowId ) blob_appendf(&title, " (%d)", baseCheckin);
446 fossil_free(zUuid);
@@ -449,12 +453,12 @@
449 zLink = href("%R/info/%!S", zUuid);
450 blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid);
451 fossil_free(zUuid);
452 }
453 }else{
454 blob_appendf(&title, "History of ");
455 hyperlinked_path(zFilename, &title, 0, "tree", "");
456 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
457 }
458 if( uBg ){
459 blob_append(&title, " (color-coded by user)", -1);
460 }
@@ -524,11 +528,11 @@
524 @ <tr class='timelineSelected'>
525 }else{
526 @ <tr>
527 }
528 @ <td class="timelineTime">\
529 @ %z(href("%R/artifact/%!S",zUuid))%s(zTime)</a></td>
530 @ <td class="timelineGraph"><div id="m%d(gidx)" class="tl-nodemark"></div>
531 @ </td>
532 if( zBgClr && zBgClr[0] ){
533 @ <td class="timeline%s(zStyle)Cell" id='mc%d(gidx)'>
534 }else{
@@ -561,11 +565,11 @@
561 cgi_printf("<span class='clutter' id='detail-%d'>",frid);
562 }
563 cgi_printf("<span class='timeline%sDetail'>", zStyle);
564 if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ) cgi_printf("(");
565 if( zUuid && (tmFlags & TIMELINE_VERBOSE)==0 ){
566 @ file:&nbsp;%z(href("%R/artifact/%!S",zUuid))[%S(zUuid)]</a>
567 if( fShowId ){
568 int srcId = delta_source_rid(frid);
569 if( srcId>0 ){
570 @ id:&nbsp;%d(frid)&larr;%d(srcId)
571 }else{
572
--- src/finfo.c
+++ src/finfo.c
@@ -199,11 +199,11 @@
199 TAG_BRANCH, zFilename, filename_collation(),
200 iLimit, iOffset
201 );
202 blob_zero(&line);
203 if( iBrief ){
204 fossil_print("History for %s\n", blob_str(&fname));
205 }
206 while( db_step(&q)==SQLITE_ROW ){
207 const char *zFileUuid = db_column_text(&q, 0);
208 const char *zCiUuid = db_column_text(&q,1);
209 const char *zDate = db_column_text(&q, 2);
@@ -296,11 +296,11 @@
296 ** timezone offset from UTC as "-HH:MM" (westward) or "+HH:MM"
297 ** (eastward). Either no timezone suffix or "Z" means UTC.
298 */
299 void finfo_page(void){
300 Stmt q;
301 const char *zFilename = PD("name","");
302 char zPrevDate[20];
303 const char *zA;
304 const char *zB;
305 int n;
306 int baseCheckin;
@@ -321,11 +321,16 @@
321 const char *zMark; /* Mark this version of the file */
322 int selRid = 0; /* RID of the marked file version */
323
324 login_check_credentials();
325 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
326 fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q", zFilename);
327 if( fnid==0 ){
328 style_header("No such file");
329 }else{
330 style_header("History for %s", zFilename);
331 }
332 login_anonymous_available();
333 tmFlags = timeline_ss_submenu();
334 if( tmFlags & TIMELINE_COLUMNAR ){
335 zStyle = "Columnar";
336 }else if( tmFlags & TIMELINE_COMPACT ){
@@ -340,13 +345,11 @@
345 url_initialize(&url, "finfo");
346 if( brBg ) url_add_parameter(&url, "brbg", 0);
347 if( uBg ) url_add_parameter(&url, "ubg", 0);
348 baseCheckin = name_to_rid_www("ci");
349 zPrevDate[0] = 0;
 
350 cookie_render();
 
351 if( fnid==0 ){
352 @ No such file: %h(zFilename)
353 style_footer();
354 return;
355 }
@@ -435,12 +438,13 @@
438 }else if( n>0 ){
439 blob_appendf(&title, "First %d ancestors of file ", n);
440 }else{
441 blob_appendf(&title, "Ancestors of file ");
442 }
443 blob_appendf(&title,"%z%h</a>",
444 href("%R/file?name=%T&ci=%!S", zFilename, zUuid),
445 zFilename);
446 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
447 blob_append(&title, origCheckin ? " between " : " from ", -1);
448 blob_appendf(&title, "check-in %z%S</a>", zLink, zUuid);
449 if( fShowId ) blob_appendf(&title, " (%d)", baseCheckin);
450 fossil_free(zUuid);
@@ -449,12 +453,12 @@
453 zLink = href("%R/info/%!S", zUuid);
454 blob_appendf(&title, " and check-in %z%S</a>", zLink, zUuid);
455 fossil_free(zUuid);
456 }
457 }else{
458 blob_appendf(&title, "History for ");
459 hyperlinked_path(zFilename, &title, 0, "tree", "", LINKPATH_FILE);
460 if( fShowId ) blob_appendf(&title, " (%d)", fnid);
461 }
462 if( uBg ){
463 blob_append(&title, " (color-coded by user)", -1);
464 }
@@ -524,11 +528,11 @@
528 @ <tr class='timelineSelected'>
529 }else{
530 @ <tr>
531 }
532 @ <td class="timelineTime">\
533 @ %z(href("%R/file?name=%T&ci=%!S",zFilename,zCkin))%s(zTime)</a></td>
534 @ <td class="timelineGraph"><div id="m%d(gidx)" class="tl-nodemark"></div>
535 @ </td>
536 if( zBgClr && zBgClr[0] ){
537 @ <td class="timeline%s(zStyle)Cell" id='mc%d(gidx)'>
538 }else{
@@ -561,11 +565,11 @@
565 cgi_printf("<span class='clutter' id='detail-%d'>",frid);
566 }
567 cgi_printf("<span class='timeline%sDetail'>", zStyle);
568 if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ) cgi_printf("(");
569 if( zUuid && (tmFlags & TIMELINE_VERBOSE)==0 ){
570 @ file:&nbsp;%z(href("%R/file?name=%T&ci=%!S",zFilename,zCkin))[%S(zUuid)]</a>
571 if( fShowId ){
572 int srcId = delta_source_rid(frid);
573 if( srcId>0 ){
574 @ id:&nbsp;%d(frid)&larr;%d(srcId)
575 }else{
576
+172 -19
--- src/forum.c
+++ src/forum.c
@@ -156,10 +156,11 @@
156156
static ForumThread *forumthread_create(int froot, int computeHierarchy){
157157
ForumThread *pThread;
158158
ForumEntry *pEntry;
159159
Stmt q;
160160
int sid = 1;
161
+ Bag seen = Bag_INIT;
161162
pThread = fossil_malloc( sizeof(*pThread) );
162163
memset(pThread, 0, sizeof(*pThread));
163164
db_prepare(&q,
164165
"SELECT fpid, firt, fprev, (SELECT uuid FROM blob WHERE rid=fpid)"
165166
" FROM forumpost"
@@ -175,18 +176,24 @@
175176
pEntry->zUuid = fossil_strdup(db_column_text(&q,3));
176177
pEntry->mfirt = pEntry->firt;
177178
pEntry->sid = sid++;
178179
pEntry->pPrev = pThread->pLast;
179180
pEntry->pNext = 0;
181
+ bag_insert(&seen, pEntry->fpid);
180182
if( pThread->pLast==0 ){
181183
pThread->pFirst = pEntry;
182184
}else{
183185
pThread->pLast->pNext = pEntry;
184186
}
187
+ if( pEntry->firt && !bag_find(&seen,pEntry->firt) ){
188
+ pEntry->firt = froot;
189
+ pEntry->mfirt = froot;
190
+ }
185191
pThread->pLast = pEntry;
186192
}
187193
db_finalize(&q);
194
+ bag_clear(&seen);
188195
189196
/* Establish which entries are the latest edit. After this loop
190197
** completes, entries that have non-NULL pLeaf should not be
191198
** displayed.
192199
*/
@@ -283,16 +290,18 @@
283290
}
284291
fossil_print("fpid = %d\n", fpid);
285292
fossil_print("froot = %d\n", froot);
286293
pThread = forumthread_create(froot, 1);
287294
fossil_print("Chronological:\n");
288
- /* 123456789 123456789 123456789 123456789 123456789 123456789 */
289
- fossil_print(" fpid firt fprev mfirt pLeaf nReply\n");
295
+ fossil_print(
296
+/* 0 1 2 3 4 5 6 7 */
297
+/* 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123 */
298
+ " sid fpid firt fprev mfirt pLeaf nReply hash\n");
290299
for(p=pThread->pFirst; p; p=p->pNext){
291
- fossil_print("%9d %9d %9d %9d %9d %9d\n",
300
+ fossil_print("%4d %9d %9d %9d %9d %9d %6d %8.8s\n", p->sid,
292301
p->fpid, p->firt, p->fprev, p->mfirt, p->pLeaf ? p->pLeaf->fpid : 0,
293
- p->nReply);
302
+ p->nReply, p->zUuid);
294303
}
295304
fossil_print("\nDisplay\n");
296305
for(p=pThread->pDisplay; p; p=p->pDisplay){
297306
fossil_print("%*s", (p->nIndent-1)*3, "");
298307
if( p->pLeaf ){
@@ -408,10 +417,11 @@
408417
Manifest *pPost;
409418
int isPrivate; /* True for posts awaiting moderation */
410419
int sameUser; /* True if author is also the reader */
411420
const char *zUuid;
412421
char *zDisplayName; /* The display name */
422
+ int sid;
413423
414424
pPost = manifest_get(p->fpid, CFTYPE_FORUM, 0);
415425
if( pPost==0 ) continue;
416426
if( p->fpid==target ){
417427
@ <div id="forum%d(p->fpid)" class="forumTime forumSel">
@@ -423,20 +433,21 @@
423433
if( pPost->zThreadTitle ){
424434
@ <h1>%h(pPost->zThreadTitle)</h1>
425435
}
426436
zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate);
427437
zDisplayName = display_name_from_login(pPost->zUser);
428
- @ <h3 class='forumPostHdr'>(%d(p->sid)) By %h(zDisplayName) on %h(zDate)
438
+ sid = p->pEdit ? p->pEdit->sid : p->sid;
439
+ @ <h3 class='forumPostHdr'>(%d(sid)) By %h(zDisplayName) on %h(zDate)
429440
fossil_free(zDisplayName);
430441
fossil_free(zDate);
431442
if( p->pEdit ){
432443
@ edit of %z(href("%R/forumpost/%S?t=%c",p->pEdit->zUuid,cMode))\
433444
@ %d(p->pEdit->sid)</a>
434445
}
435446
if( g.perm.Debug ){
436447
@ <span class="debug">\
437
- @ <a href="%R/artifact/%h(p->zUuid)">(artifact)</a></span>
448
+ @ <a href="%R/artifact/%h(p->zUuid)">(artifact-%d(p->fpid))</a></span>
438449
}
439450
if( p->firt ){
440451
ForumEntry *pIrt = p->pPrev;
441452
while( pIrt && pIrt->fpid!=p->firt ) pIrt = pIrt->pPrev;
442453
if( pIrt ){
@@ -451,10 +462,125 @@
451462
zUuid = p->pLeaf->zUuid;
452463
}
453464
if( p->fpid!=target ){
454465
@ %z(href("%R/forumpost/%S?t=%c",zUuid,cMode))[link]</a>
455466
}
467
+ if( !bRawMode ){
468
+ @ %z(href("%R/forumpost/%S?raw",zUuid))[source]</a>
469
+ }
470
+ isPrivate = content_is_private(p->fpid);
471
+ sameUser = notAnon && fossil_strcmp(pPost->zUser, g.zLogin)==0;
472
+ @ </h3>
473
+ if( isPrivate && !g.perm.ModForum && !sameUser ){
474
+ @ <p><span class="modpending">Awaiting Moderator Approval</span></p>
475
+ }else{
476
+ const char *zMimetype;
477
+ if( bRawMode ){
478
+ zMimetype = "text/plain";
479
+ }else if( p->pLeaf!=0 ){
480
+ zMimetype = "text/plain";
481
+ }else{
482
+ zMimetype = pPost->zMimetype;
483
+ }
484
+ forum_render(0, zMimetype, pPost->zWiki, 0, 1);
485
+ }
486
+ if( g.perm.WrForum && p->pLeaf==0 ){
487
+ int sameUser = login_is_individual()
488
+ && fossil_strcmp(pPost->zUser, g.zLogin)==0;
489
+ @ <p><form action="%R/forumedit" method="POST">
490
+ @ <input type="hidden" name="fpid" value="%s(p->zUuid)">
491
+ if( !isPrivate ){
492
+ /* Reply and Edit are only available if the post has already
493
+ ** been approved */
494
+ @ <input type="submit" name="reply" value="Reply">
495
+ if( g.perm.Admin || sameUser ){
496
+ @ <input type="submit" name="edit" value="Edit">
497
+ @ <input type="submit" name="nullout" value="Delete">
498
+ }
499
+ }else if( g.perm.ModForum ){
500
+ /* Provide moderators with moderation buttons for posts that
501
+ ** are pending moderation */
502
+ @ <input type="submit" name="approve" value="Approve">
503
+ @ <input type="submit" name="reject" value="Reject">
504
+ generateTrustControls(pPost);
505
+ }else if( sameUser ){
506
+ /* A post that is pending moderation can be deleted by the
507
+ ** person who originally submitted the post */
508
+ @ <input type="submit" name="reject" value="Delete">
509
+ }
510
+ @ </form></p>
511
+ }
512
+ manifest_destroy(pPost);
513
+ @ </div>
514
+ }
515
+
516
+ /* Undocumented "threadtable" query parameter causes thread table
517
+ ** to be displayed for debugging purposes.
518
+ */
519
+ if( PB("threadtable") ){
520
+ @ <hr>
521
+ @ <table border="1" cellpadding="3" cellspacing="0">
522
+ @ <tr><th>sid<th>fpid<th>firt<th>fprev<th>mfirt<th>pLeaf<th>nReply<th>hash
523
+ for(p=pThread->pFirst; p; p=p->pNext){
524
+ @ <tr><td>%d(p->sid)<td>%d(p->fpid)<td>%d(p->firt)\
525
+ @ <td>%d(p->fprev)<td>%d(p->mfirt)\
526
+ @ <td>%d(p->pLeaf?p->pLeaf->fpid:0)<td>%d(p->nReply)\
527
+ @ <td>%S(p->zUuid)</tr>
528
+ }
529
+ @ </table>
530
+ }
531
+
532
+ forumthread_delete(pThread);
533
+}
534
+/*
535
+** Display all the edit history of post "target".
536
+*/
537
+static void forum_display_history(int froot, int target, int bRawMode){
538
+ ForumThread *pThread = forumthread_create(froot, 0);
539
+ ForumEntry *p;
540
+ int notAnon = login_is_individual();
541
+ char cMode = bRawMode ? 'r' : 'c';
542
+ ForumEntry *pLeaf = 0;
543
+ int cnt = 0;
544
+ for(p=pThread->pFirst; p; p=p->pNext){
545
+ if( p->fpid==target ){
546
+ pLeaf = p->pLeaf ? p->pLeaf : p;
547
+ break;
548
+ }
549
+ }
550
+ for(p=pThread->pFirst; p; p=p->pNext){
551
+ char *zDate;
552
+ Manifest *pPost;
553
+ int isPrivate; /* True for posts awaiting moderation */
554
+ int sameUser; /* True if author is also the reader */
555
+ const char *zUuid;
556
+ char *zDisplayName; /* The display name */
557
+
558
+ if( p->fpid!=pLeaf->fpid && p->pLeaf!=pLeaf ) continue;
559
+ cnt++;
560
+ pPost = manifest_get(p->fpid, CFTYPE_FORUM, 0);
561
+ if( pPost==0 ) continue;
562
+ @ <div id="forum%d(p->fpid)" class="forumTime">
563
+ zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate);
564
+ zDisplayName = display_name_from_login(pPost->zUser);
565
+ @ <h3 class='forumPostHdr'>(%d(p->sid)) By %h(zDisplayName) on %h(zDate)
566
+ fossil_free(zDisplayName);
567
+ fossil_free(zDate);
568
+ if( g.perm.Debug ){
569
+ @ <span class="debug">\
570
+ @ <a href="%R/artifact/%h(p->zUuid)">(artifact-%d(p->fpid))</a></span>
571
+ }
572
+ if( p->firt && cnt==1 ){
573
+ ForumEntry *pIrt = p->pPrev;
574
+ while( pIrt && pIrt->fpid!=p->firt ) pIrt = pIrt->pPrev;
575
+ if( pIrt ){
576
+ @ in reply to %z(href("%R/forumpost/%S?t=%c",pIrt->zUuid,cMode))\
577
+ @ %d(pIrt->sid)</a>
578
+ }
579
+ }
580
+ zUuid = p->zUuid;
581
+ @ %z(href("%R/forumpost/%S?t=c",zUuid))[link]</a>
456582
if( !bRawMode ){
457583
@ %z(href("%R/forumpost/%S?raw",zUuid))[source]</a>
458584
}
459585
isPrivate = content_is_private(p->fpid);
460586
sameUser = notAnon && fossil_strcmp(pPost->zUser, g.zLogin)==0;
@@ -549,16 +675,16 @@
549675
@ <h1>%h(pPost->zThreadTitle)</h1>
550676
}
551677
zDate = db_text(0, "SELECT datetime(%.17g)", pOPost->rDate);
552678
zDisplayName = display_name_from_login(pOPost->zUser);
553679
@ <h3 class='forumPostHdr'>\
554
- @ (%d(p->pLeaf?p->pLeaf->sid:p->sid)) By %h(zDisplayName) on %h(zDate)
680
+ @ (%d(p->sid)) By %h(zDisplayName) on %h(zDate)
555681
fossil_free(zDisplayName);
556682
fossil_free(zDate);
557683
if( g.perm.Debug ){
558684
@ <span class="debug">\
559
- @ <a href="%R/artifact/%h(p->zUuid)">(artifact)</a></span>
685
+ @ <a href="%R/artifact/%h(p->zUuid)">(artifact-%d(p->fpid))</a></span>
560686
}
561687
if( p->pLeaf ){
562688
zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate);
563689
if( fossil_strcmp(pOPost->zUser,pPost->zUser)==0 ){
564690
@ and edited on %h(zDate)
@@ -566,21 +692,23 @@
566692
@ as edited by %h(pPost->zUser) on %h(zDate)
567693
}
568694
fossil_free(zDate);
569695
if( g.perm.Debug ){
570696
@ <span class="debug">\
571
- @ <a href="%R/artifact/%h(p->pLeaf->zUuid)">(artifact)</a></span>
697
+ @ <a href="%R/artifact/%h(p->pLeaf->zUuid)">\
698
+ @ (artifact-%d(p->pLeaf->fpid))</a></span>
572699
}
700
+ @ %z(href("%R/forumpost/%S?t=y",p->zUuid))[history]</a>
573701
manifest_destroy(pOPost);
574702
}
575703
if( fpid!=target ){
576704
@ %z(href("%R/forumpost/%S",zUuid))[link]</a>
577705
}
578706
@ %z(href("%R/forumpost/%S?raw",zUuid))[source]</a>
579707
if( p->firt ){
580708
ForumEntry *pIrt = p->pPrev;
581
- while( pIrt && pIrt->fpid!=p->firt ) pIrt = pIrt->pPrev;
709
+ while( pIrt && pIrt->fpid!=p->mfirt ) pIrt = pIrt->pPrev;
582710
if( pIrt ){
583711
@ in reply to %z(href("%R/forumpost/%S?t=h",pIrt->zUuid))\
584712
@ %d(pIrt->sid)</a>
585713
}
586714
}
@@ -637,10 +765,11 @@
637765
** t=MODE Display mode.
638766
** 'c' for chronological
639767
** 'h' for hierarchical
640768
** 'a' for automatic
641769
** 'r' for raw
770
+** 'y' for history of post X only
642771
** raw If present, show only the post specified and
643772
** show its original unformatted source text.
644773
*/
645774
void forumpost_page(void){
646775
forumthread_page();
@@ -679,10 +808,12 @@
679808
** t=MODE Display mode. MODE is...
680809
** 'c' for chronological, or
681810
** 'h' for hierarchical, or
682811
** 'a' for automatic, or
683812
** 'r' for raw.
813
+** raw Show only the post given by name= and show it unformatted
814
+** hist Show only the edit history for the name= post
684815
*/
685816
void forumthread_page(void){
686817
int fpid;
687818
int froot;
688819
const char *zName = P("name");
@@ -710,11 +841,13 @@
710841
zMode = "c"; /* Default to chronological on mobile */
711842
}else{
712843
zMode = "h";
713844
}
714845
}
715
- forumthread_page_header(froot, fpid);
846
+ if( zMode[0]!='y' ){
847
+ forumthread_page_header(froot, fpid);
848
+ }
716849
if( bRaw && fpid ){
717850
Manifest *pPost;
718851
pPost = manifest_get(fpid, CFTYPE_FORUM, 0);
719852
if( pPost==0 ){
720853
@ <p>No such forum post: %h(zName)
@@ -735,10 +868,14 @@
735868
forum_display_chronological(froot, fpid, 0);
736869
}else if( zMode[0]=='r' ){
737870
style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName);
738871
style_submenu_element("Hierarchical", "%R/%s/%s?t=h", g.zPath, zName);
739872
forum_display_chronological(froot, fpid, 1);
873
+ }else if( zMode[0]=='y' ){
874
+ style_header("Edit History Of A Forum Post");
875
+ style_submenu_element("Complete Thread", "%R/%s/%s?t=a", g.zPath, zName);
876
+ forum_display_history(froot, fpid, 1);
740877
}else{
741878
style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName);
742879
style_submenu_element("Unformatted", "%R/%s/%s?t=r", g.zPath, zName);
743880
forum_display_hierarchical(froot, fpid);
744881
}
@@ -753,10 +890,19 @@
753890
if( P("domod") ) return 1;
754891
if( g.perm.WrTForum ) return 0;
755892
if( g.perm.ModForum ) return 0;
756893
return 1;
757894
}
895
+
896
+/*
897
+** Return true if the string is white-space only.
898
+*/
899
+static int whitespace_only(const char *z){
900
+ if( z==0 ) return 1;
901
+ while( z[0] && fossil_isspace(z[0]) ){ z++; }
902
+ return z[0]==0;
903
+}
758904
759905
/*
760906
** Add a new Forum Post artifact to the repository.
761907
**
762908
** Return true if a redirect occurs.
@@ -773,12 +919,16 @@
773919
char *zI;
774920
char *zG;
775921
int iBasis;
776922
Blob x, cksum, formatCheck, errMsg;
777923
Manifest *pPost;
924
+ int nContent = zContent ? (int)strlen(zContent) : 0;
778925
779926
schema_forum();
927
+ if( iEdit==0 && whitespace_only(zContent) ){
928
+ return 0;
929
+ }
780930
if( iInReplyTo==0 && iEdit>0 ){
781931
iBasis = iEdit;
782932
iInReplyTo = db_int(0, "SELECT firt FROM forumpost WHERE fpid=%d", iEdit);
783933
}else{
784934
iBasis = iInReplyTo;
@@ -819,11 +969,11 @@
819969
}else{
820970
zUser = login_name();
821971
}
822972
}
823973
blob_appendf(&x, "U %F\n", zUser);
824
- blob_appendf(&x, "W %d\n%s\n", strlen(zContent), zContent);
974
+ blob_appendf(&x, "W %d\n%s\n", nContent, zContent);
825975
md5sum_blob(&x, &cksum);
826976
blob_appendf(&x, "Z %b\n", &cksum);
827977
blob_reset(&cksum);
828978
829979
/* Verify that the artifact we are creating is well-formed */
@@ -950,24 +1100,24 @@
9501100
login_check_credentials();
9511101
if( !g.perm.WrForum ){
9521102
login_needed(g.anon.WrForum);
9531103
return;
9541104
}
955
- if( P("submit") ){
1105
+ if( P("submit") && cgi_csrf_safe(1) ){
9561106
if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent) ) return;
9571107
}
958
- if( P("preview") ){
1108
+ if( P("preview") && !whitespace_only(zContent) ){
9591109
@ <h1>Preview:</h1>
9601110
forum_render(zTitle, zMimetype, zContent, "forumEdit", 1);
9611111
}
9621112
style_header("New Forum Thread");
9631113
@ <form action="%R/forume1" method="POST">
9641114
@ <h1>New Thread:</h1>
9651115
forum_from_line();
9661116
forum_entry_widget(zTitle, zMimetype, zContent);
9671117
@ <input type="submit" name="preview" value="Preview">
968
- if( P("preview") ){
1118
+ if( P("preview") && !whitespace_only(zContent) ){
9691119
@ <input type="submit" name="submit" value="Submit">
9701120
}else{
9711121
@ <input type="submit" name="submit" value="Submit" disabled>
9721122
}
9731123
if( g.perm.Debug ){
@@ -1053,14 +1203,17 @@
10531203
}
10541204
return;
10551205
}
10561206
}
10571207
isDelete = P("nullout")!=0;
1058
- if( P("submit") && isCsrfSafe ){
1208
+ if( P("submit")
1209
+ && isCsrfSafe
1210
+ && (zContent = PDT("content",""))!=0
1211
+ && (!whitespace_only(zContent) || isDelete)
1212
+ ){
10591213
int done = 1;
10601214
const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE);
1061
- const char *zContent = PDT("content","");
10621215
if( P("reply") ){
10631216
done = forum_post(0, fpid, 0, 0, zMimetype, zContent);
10641217
}else if( P("edit") || isDelete ){
10651218
done = forum_post(P("title"), 0, fpid, 0, zMimetype, zContent);
10661219
}else{
@@ -1124,11 +1277,11 @@
11241277
zDisplayName = display_name_from_login(pPost->zUser);
11251278
@ <h3 class='forumPostHdr'>By %h(zDisplayName) on %h(zDate)</h3>
11261279
fossil_free(zDisplayName);
11271280
fossil_free(zDate);
11281281
forum_render(0, pPost->zMimetype, pPost->zWiki, "forumEdit", 1);
1129
- if( P("preview") ){
1282
+ if( P("preview") && !whitespace_only(zContent) ){
11301283
@ <h2>Preview:</h2>
11311284
forum_render(0, zMimetype,zContent, "forumEdit", 1);
11321285
}
11331286
@ <h2>Enter Reply:</h2>
11341287
@ <form action="%R/forume2" method="POST">
@@ -1139,11 +1292,11 @@
11391292
}
11401293
if( !isDelete ){
11411294
@ <input type="submit" name="preview" value="Preview">
11421295
}
11431296
@ <input type="submit" name="cancel" value="Cancel">
1144
- if( P("preview") || isDelete ){
1297
+ if( (P("preview") && !whitespace_only(zContent)) || isDelete ){
11451298
@ <input type="submit" name="submit" value="Submit">
11461299
}
11471300
if( g.perm.Debug ){
11481301
/* For the test-forumnew page add these extra debugging controls */
11491302
@ <div class="debug">
11501303
--- src/forum.c
+++ src/forum.c
@@ -156,10 +156,11 @@
156 static ForumThread *forumthread_create(int froot, int computeHierarchy){
157 ForumThread *pThread;
158 ForumEntry *pEntry;
159 Stmt q;
160 int sid = 1;
 
161 pThread = fossil_malloc( sizeof(*pThread) );
162 memset(pThread, 0, sizeof(*pThread));
163 db_prepare(&q,
164 "SELECT fpid, firt, fprev, (SELECT uuid FROM blob WHERE rid=fpid)"
165 " FROM forumpost"
@@ -175,18 +176,24 @@
175 pEntry->zUuid = fossil_strdup(db_column_text(&q,3));
176 pEntry->mfirt = pEntry->firt;
177 pEntry->sid = sid++;
178 pEntry->pPrev = pThread->pLast;
179 pEntry->pNext = 0;
 
180 if( pThread->pLast==0 ){
181 pThread->pFirst = pEntry;
182 }else{
183 pThread->pLast->pNext = pEntry;
184 }
 
 
 
 
185 pThread->pLast = pEntry;
186 }
187 db_finalize(&q);
 
188
189 /* Establish which entries are the latest edit. After this loop
190 ** completes, entries that have non-NULL pLeaf should not be
191 ** displayed.
192 */
@@ -283,16 +290,18 @@
283 }
284 fossil_print("fpid = %d\n", fpid);
285 fossil_print("froot = %d\n", froot);
286 pThread = forumthread_create(froot, 1);
287 fossil_print("Chronological:\n");
288 /* 123456789 123456789 123456789 123456789 123456789 123456789 */
289 fossil_print(" fpid firt fprev mfirt pLeaf nReply\n");
 
 
290 for(p=pThread->pFirst; p; p=p->pNext){
291 fossil_print("%9d %9d %9d %9d %9d %9d\n",
292 p->fpid, p->firt, p->fprev, p->mfirt, p->pLeaf ? p->pLeaf->fpid : 0,
293 p->nReply);
294 }
295 fossil_print("\nDisplay\n");
296 for(p=pThread->pDisplay; p; p=p->pDisplay){
297 fossil_print("%*s", (p->nIndent-1)*3, "");
298 if( p->pLeaf ){
@@ -408,10 +417,11 @@
408 Manifest *pPost;
409 int isPrivate; /* True for posts awaiting moderation */
410 int sameUser; /* True if author is also the reader */
411 const char *zUuid;
412 char *zDisplayName; /* The display name */
 
413
414 pPost = manifest_get(p->fpid, CFTYPE_FORUM, 0);
415 if( pPost==0 ) continue;
416 if( p->fpid==target ){
417 @ <div id="forum%d(p->fpid)" class="forumTime forumSel">
@@ -423,20 +433,21 @@
423 if( pPost->zThreadTitle ){
424 @ <h1>%h(pPost->zThreadTitle)</h1>
425 }
426 zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate);
427 zDisplayName = display_name_from_login(pPost->zUser);
428 @ <h3 class='forumPostHdr'>(%d(p->sid)) By %h(zDisplayName) on %h(zDate)
 
429 fossil_free(zDisplayName);
430 fossil_free(zDate);
431 if( p->pEdit ){
432 @ edit of %z(href("%R/forumpost/%S?t=%c",p->pEdit->zUuid,cMode))\
433 @ %d(p->pEdit->sid)</a>
434 }
435 if( g.perm.Debug ){
436 @ <span class="debug">\
437 @ <a href="%R/artifact/%h(p->zUuid)">(artifact)</a></span>
438 }
439 if( p->firt ){
440 ForumEntry *pIrt = p->pPrev;
441 while( pIrt && pIrt->fpid!=p->firt ) pIrt = pIrt->pPrev;
442 if( pIrt ){
@@ -451,10 +462,125 @@
451 zUuid = p->pLeaf->zUuid;
452 }
453 if( p->fpid!=target ){
454 @ %z(href("%R/forumpost/%S?t=%c",zUuid,cMode))[link]</a>
455 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
456 if( !bRawMode ){
457 @ %z(href("%R/forumpost/%S?raw",zUuid))[source]</a>
458 }
459 isPrivate = content_is_private(p->fpid);
460 sameUser = notAnon && fossil_strcmp(pPost->zUser, g.zLogin)==0;
@@ -549,16 +675,16 @@
549 @ <h1>%h(pPost->zThreadTitle)</h1>
550 }
551 zDate = db_text(0, "SELECT datetime(%.17g)", pOPost->rDate);
552 zDisplayName = display_name_from_login(pOPost->zUser);
553 @ <h3 class='forumPostHdr'>\
554 @ (%d(p->pLeaf?p->pLeaf->sid:p->sid)) By %h(zDisplayName) on %h(zDate)
555 fossil_free(zDisplayName);
556 fossil_free(zDate);
557 if( g.perm.Debug ){
558 @ <span class="debug">\
559 @ <a href="%R/artifact/%h(p->zUuid)">(artifact)</a></span>
560 }
561 if( p->pLeaf ){
562 zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate);
563 if( fossil_strcmp(pOPost->zUser,pPost->zUser)==0 ){
564 @ and edited on %h(zDate)
@@ -566,21 +692,23 @@
566 @ as edited by %h(pPost->zUser) on %h(zDate)
567 }
568 fossil_free(zDate);
569 if( g.perm.Debug ){
570 @ <span class="debug">\
571 @ <a href="%R/artifact/%h(p->pLeaf->zUuid)">(artifact)</a></span>
 
572 }
 
573 manifest_destroy(pOPost);
574 }
575 if( fpid!=target ){
576 @ %z(href("%R/forumpost/%S",zUuid))[link]</a>
577 }
578 @ %z(href("%R/forumpost/%S?raw",zUuid))[source]</a>
579 if( p->firt ){
580 ForumEntry *pIrt = p->pPrev;
581 while( pIrt && pIrt->fpid!=p->firt ) pIrt = pIrt->pPrev;
582 if( pIrt ){
583 @ in reply to %z(href("%R/forumpost/%S?t=h",pIrt->zUuid))\
584 @ %d(pIrt->sid)</a>
585 }
586 }
@@ -637,10 +765,11 @@
637 ** t=MODE Display mode.
638 ** 'c' for chronological
639 ** 'h' for hierarchical
640 ** 'a' for automatic
641 ** 'r' for raw
 
642 ** raw If present, show only the post specified and
643 ** show its original unformatted source text.
644 */
645 void forumpost_page(void){
646 forumthread_page();
@@ -679,10 +808,12 @@
679 ** t=MODE Display mode. MODE is...
680 ** 'c' for chronological, or
681 ** 'h' for hierarchical, or
682 ** 'a' for automatic, or
683 ** 'r' for raw.
 
 
684 */
685 void forumthread_page(void){
686 int fpid;
687 int froot;
688 const char *zName = P("name");
@@ -710,11 +841,13 @@
710 zMode = "c"; /* Default to chronological on mobile */
711 }else{
712 zMode = "h";
713 }
714 }
715 forumthread_page_header(froot, fpid);
 
 
716 if( bRaw && fpid ){
717 Manifest *pPost;
718 pPost = manifest_get(fpid, CFTYPE_FORUM, 0);
719 if( pPost==0 ){
720 @ <p>No such forum post: %h(zName)
@@ -735,10 +868,14 @@
735 forum_display_chronological(froot, fpid, 0);
736 }else if( zMode[0]=='r' ){
737 style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName);
738 style_submenu_element("Hierarchical", "%R/%s/%s?t=h", g.zPath, zName);
739 forum_display_chronological(froot, fpid, 1);
 
 
 
 
740 }else{
741 style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName);
742 style_submenu_element("Unformatted", "%R/%s/%s?t=r", g.zPath, zName);
743 forum_display_hierarchical(froot, fpid);
744 }
@@ -753,10 +890,19 @@
753 if( P("domod") ) return 1;
754 if( g.perm.WrTForum ) return 0;
755 if( g.perm.ModForum ) return 0;
756 return 1;
757 }
 
 
 
 
 
 
 
 
 
758
759 /*
760 ** Add a new Forum Post artifact to the repository.
761 **
762 ** Return true if a redirect occurs.
@@ -773,12 +919,16 @@
773 char *zI;
774 char *zG;
775 int iBasis;
776 Blob x, cksum, formatCheck, errMsg;
777 Manifest *pPost;
 
778
779 schema_forum();
 
 
 
780 if( iInReplyTo==0 && iEdit>0 ){
781 iBasis = iEdit;
782 iInReplyTo = db_int(0, "SELECT firt FROM forumpost WHERE fpid=%d", iEdit);
783 }else{
784 iBasis = iInReplyTo;
@@ -819,11 +969,11 @@
819 }else{
820 zUser = login_name();
821 }
822 }
823 blob_appendf(&x, "U %F\n", zUser);
824 blob_appendf(&x, "W %d\n%s\n", strlen(zContent), zContent);
825 md5sum_blob(&x, &cksum);
826 blob_appendf(&x, "Z %b\n", &cksum);
827 blob_reset(&cksum);
828
829 /* Verify that the artifact we are creating is well-formed */
@@ -950,24 +1100,24 @@
950 login_check_credentials();
951 if( !g.perm.WrForum ){
952 login_needed(g.anon.WrForum);
953 return;
954 }
955 if( P("submit") ){
956 if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent) ) return;
957 }
958 if( P("preview") ){
959 @ <h1>Preview:</h1>
960 forum_render(zTitle, zMimetype, zContent, "forumEdit", 1);
961 }
962 style_header("New Forum Thread");
963 @ <form action="%R/forume1" method="POST">
964 @ <h1>New Thread:</h1>
965 forum_from_line();
966 forum_entry_widget(zTitle, zMimetype, zContent);
967 @ <input type="submit" name="preview" value="Preview">
968 if( P("preview") ){
969 @ <input type="submit" name="submit" value="Submit">
970 }else{
971 @ <input type="submit" name="submit" value="Submit" disabled>
972 }
973 if( g.perm.Debug ){
@@ -1053,14 +1203,17 @@
1053 }
1054 return;
1055 }
1056 }
1057 isDelete = P("nullout")!=0;
1058 if( P("submit") && isCsrfSafe ){
 
 
 
 
1059 int done = 1;
1060 const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE);
1061 const char *zContent = PDT("content","");
1062 if( P("reply") ){
1063 done = forum_post(0, fpid, 0, 0, zMimetype, zContent);
1064 }else if( P("edit") || isDelete ){
1065 done = forum_post(P("title"), 0, fpid, 0, zMimetype, zContent);
1066 }else{
@@ -1124,11 +1277,11 @@
1124 zDisplayName = display_name_from_login(pPost->zUser);
1125 @ <h3 class='forumPostHdr'>By %h(zDisplayName) on %h(zDate)</h3>
1126 fossil_free(zDisplayName);
1127 fossil_free(zDate);
1128 forum_render(0, pPost->zMimetype, pPost->zWiki, "forumEdit", 1);
1129 if( P("preview") ){
1130 @ <h2>Preview:</h2>
1131 forum_render(0, zMimetype,zContent, "forumEdit", 1);
1132 }
1133 @ <h2>Enter Reply:</h2>
1134 @ <form action="%R/forume2" method="POST">
@@ -1139,11 +1292,11 @@
1139 }
1140 if( !isDelete ){
1141 @ <input type="submit" name="preview" value="Preview">
1142 }
1143 @ <input type="submit" name="cancel" value="Cancel">
1144 if( P("preview") || isDelete ){
1145 @ <input type="submit" name="submit" value="Submit">
1146 }
1147 if( g.perm.Debug ){
1148 /* For the test-forumnew page add these extra debugging controls */
1149 @ <div class="debug">
1150
--- src/forum.c
+++ src/forum.c
@@ -156,10 +156,11 @@
156 static ForumThread *forumthread_create(int froot, int computeHierarchy){
157 ForumThread *pThread;
158 ForumEntry *pEntry;
159 Stmt q;
160 int sid = 1;
161 Bag seen = Bag_INIT;
162 pThread = fossil_malloc( sizeof(*pThread) );
163 memset(pThread, 0, sizeof(*pThread));
164 db_prepare(&q,
165 "SELECT fpid, firt, fprev, (SELECT uuid FROM blob WHERE rid=fpid)"
166 " FROM forumpost"
@@ -175,18 +176,24 @@
176 pEntry->zUuid = fossil_strdup(db_column_text(&q,3));
177 pEntry->mfirt = pEntry->firt;
178 pEntry->sid = sid++;
179 pEntry->pPrev = pThread->pLast;
180 pEntry->pNext = 0;
181 bag_insert(&seen, pEntry->fpid);
182 if( pThread->pLast==0 ){
183 pThread->pFirst = pEntry;
184 }else{
185 pThread->pLast->pNext = pEntry;
186 }
187 if( pEntry->firt && !bag_find(&seen,pEntry->firt) ){
188 pEntry->firt = froot;
189 pEntry->mfirt = froot;
190 }
191 pThread->pLast = pEntry;
192 }
193 db_finalize(&q);
194 bag_clear(&seen);
195
196 /* Establish which entries are the latest edit. After this loop
197 ** completes, entries that have non-NULL pLeaf should not be
198 ** displayed.
199 */
@@ -283,16 +290,18 @@
290 }
291 fossil_print("fpid = %d\n", fpid);
292 fossil_print("froot = %d\n", froot);
293 pThread = forumthread_create(froot, 1);
294 fossil_print("Chronological:\n");
295 fossil_print(
296 /* 0 1 2 3 4 5 6 7 */
297 /* 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123 */
298 " sid fpid firt fprev mfirt pLeaf nReply hash\n");
299 for(p=pThread->pFirst; p; p=p->pNext){
300 fossil_print("%4d %9d %9d %9d %9d %9d %6d %8.8s\n", p->sid,
301 p->fpid, p->firt, p->fprev, p->mfirt, p->pLeaf ? p->pLeaf->fpid : 0,
302 p->nReply, p->zUuid);
303 }
304 fossil_print("\nDisplay\n");
305 for(p=pThread->pDisplay; p; p=p->pDisplay){
306 fossil_print("%*s", (p->nIndent-1)*3, "");
307 if( p->pLeaf ){
@@ -408,10 +417,11 @@
417 Manifest *pPost;
418 int isPrivate; /* True for posts awaiting moderation */
419 int sameUser; /* True if author is also the reader */
420 const char *zUuid;
421 char *zDisplayName; /* The display name */
422 int sid;
423
424 pPost = manifest_get(p->fpid, CFTYPE_FORUM, 0);
425 if( pPost==0 ) continue;
426 if( p->fpid==target ){
427 @ <div id="forum%d(p->fpid)" class="forumTime forumSel">
@@ -423,20 +433,21 @@
433 if( pPost->zThreadTitle ){
434 @ <h1>%h(pPost->zThreadTitle)</h1>
435 }
436 zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate);
437 zDisplayName = display_name_from_login(pPost->zUser);
438 sid = p->pEdit ? p->pEdit->sid : p->sid;
439 @ <h3 class='forumPostHdr'>(%d(sid)) By %h(zDisplayName) on %h(zDate)
440 fossil_free(zDisplayName);
441 fossil_free(zDate);
442 if( p->pEdit ){
443 @ edit of %z(href("%R/forumpost/%S?t=%c",p->pEdit->zUuid,cMode))\
444 @ %d(p->pEdit->sid)</a>
445 }
446 if( g.perm.Debug ){
447 @ <span class="debug">\
448 @ <a href="%R/artifact/%h(p->zUuid)">(artifact-%d(p->fpid))</a></span>
449 }
450 if( p->firt ){
451 ForumEntry *pIrt = p->pPrev;
452 while( pIrt && pIrt->fpid!=p->firt ) pIrt = pIrt->pPrev;
453 if( pIrt ){
@@ -451,10 +462,125 @@
462 zUuid = p->pLeaf->zUuid;
463 }
464 if( p->fpid!=target ){
465 @ %z(href("%R/forumpost/%S?t=%c",zUuid,cMode))[link]</a>
466 }
467 if( !bRawMode ){
468 @ %z(href("%R/forumpost/%S?raw",zUuid))[source]</a>
469 }
470 isPrivate = content_is_private(p->fpid);
471 sameUser = notAnon && fossil_strcmp(pPost->zUser, g.zLogin)==0;
472 @ </h3>
473 if( isPrivate && !g.perm.ModForum && !sameUser ){
474 @ <p><span class="modpending">Awaiting Moderator Approval</span></p>
475 }else{
476 const char *zMimetype;
477 if( bRawMode ){
478 zMimetype = "text/plain";
479 }else if( p->pLeaf!=0 ){
480 zMimetype = "text/plain";
481 }else{
482 zMimetype = pPost->zMimetype;
483 }
484 forum_render(0, zMimetype, pPost->zWiki, 0, 1);
485 }
486 if( g.perm.WrForum && p->pLeaf==0 ){
487 int sameUser = login_is_individual()
488 && fossil_strcmp(pPost->zUser, g.zLogin)==0;
489 @ <p><form action="%R/forumedit" method="POST">
490 @ <input type="hidden" name="fpid" value="%s(p->zUuid)">
491 if( !isPrivate ){
492 /* Reply and Edit are only available if the post has already
493 ** been approved */
494 @ <input type="submit" name="reply" value="Reply">
495 if( g.perm.Admin || sameUser ){
496 @ <input type="submit" name="edit" value="Edit">
497 @ <input type="submit" name="nullout" value="Delete">
498 }
499 }else if( g.perm.ModForum ){
500 /* Provide moderators with moderation buttons for posts that
501 ** are pending moderation */
502 @ <input type="submit" name="approve" value="Approve">
503 @ <input type="submit" name="reject" value="Reject">
504 generateTrustControls(pPost);
505 }else if( sameUser ){
506 /* A post that is pending moderation can be deleted by the
507 ** person who originally submitted the post */
508 @ <input type="submit" name="reject" value="Delete">
509 }
510 @ </form></p>
511 }
512 manifest_destroy(pPost);
513 @ </div>
514 }
515
516 /* Undocumented "threadtable" query parameter causes thread table
517 ** to be displayed for debugging purposes.
518 */
519 if( PB("threadtable") ){
520 @ <hr>
521 @ <table border="1" cellpadding="3" cellspacing="0">
522 @ <tr><th>sid<th>fpid<th>firt<th>fprev<th>mfirt<th>pLeaf<th>nReply<th>hash
523 for(p=pThread->pFirst; p; p=p->pNext){
524 @ <tr><td>%d(p->sid)<td>%d(p->fpid)<td>%d(p->firt)\
525 @ <td>%d(p->fprev)<td>%d(p->mfirt)\
526 @ <td>%d(p->pLeaf?p->pLeaf->fpid:0)<td>%d(p->nReply)\
527 @ <td>%S(p->zUuid)</tr>
528 }
529 @ </table>
530 }
531
532 forumthread_delete(pThread);
533 }
534 /*
535 ** Display all the edit history of post "target".
536 */
537 static void forum_display_history(int froot, int target, int bRawMode){
538 ForumThread *pThread = forumthread_create(froot, 0);
539 ForumEntry *p;
540 int notAnon = login_is_individual();
541 char cMode = bRawMode ? 'r' : 'c';
542 ForumEntry *pLeaf = 0;
543 int cnt = 0;
544 for(p=pThread->pFirst; p; p=p->pNext){
545 if( p->fpid==target ){
546 pLeaf = p->pLeaf ? p->pLeaf : p;
547 break;
548 }
549 }
550 for(p=pThread->pFirst; p; p=p->pNext){
551 char *zDate;
552 Manifest *pPost;
553 int isPrivate; /* True for posts awaiting moderation */
554 int sameUser; /* True if author is also the reader */
555 const char *zUuid;
556 char *zDisplayName; /* The display name */
557
558 if( p->fpid!=pLeaf->fpid && p->pLeaf!=pLeaf ) continue;
559 cnt++;
560 pPost = manifest_get(p->fpid, CFTYPE_FORUM, 0);
561 if( pPost==0 ) continue;
562 @ <div id="forum%d(p->fpid)" class="forumTime">
563 zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate);
564 zDisplayName = display_name_from_login(pPost->zUser);
565 @ <h3 class='forumPostHdr'>(%d(p->sid)) By %h(zDisplayName) on %h(zDate)
566 fossil_free(zDisplayName);
567 fossil_free(zDate);
568 if( g.perm.Debug ){
569 @ <span class="debug">\
570 @ <a href="%R/artifact/%h(p->zUuid)">(artifact-%d(p->fpid))</a></span>
571 }
572 if( p->firt && cnt==1 ){
573 ForumEntry *pIrt = p->pPrev;
574 while( pIrt && pIrt->fpid!=p->firt ) pIrt = pIrt->pPrev;
575 if( pIrt ){
576 @ in reply to %z(href("%R/forumpost/%S?t=%c",pIrt->zUuid,cMode))\
577 @ %d(pIrt->sid)</a>
578 }
579 }
580 zUuid = p->zUuid;
581 @ %z(href("%R/forumpost/%S?t=c",zUuid))[link]</a>
582 if( !bRawMode ){
583 @ %z(href("%R/forumpost/%S?raw",zUuid))[source]</a>
584 }
585 isPrivate = content_is_private(p->fpid);
586 sameUser = notAnon && fossil_strcmp(pPost->zUser, g.zLogin)==0;
@@ -549,16 +675,16 @@
675 @ <h1>%h(pPost->zThreadTitle)</h1>
676 }
677 zDate = db_text(0, "SELECT datetime(%.17g)", pOPost->rDate);
678 zDisplayName = display_name_from_login(pOPost->zUser);
679 @ <h3 class='forumPostHdr'>\
680 @ (%d(p->sid)) By %h(zDisplayName) on %h(zDate)
681 fossil_free(zDisplayName);
682 fossil_free(zDate);
683 if( g.perm.Debug ){
684 @ <span class="debug">\
685 @ <a href="%R/artifact/%h(p->zUuid)">(artifact-%d(p->fpid))</a></span>
686 }
687 if( p->pLeaf ){
688 zDate = db_text(0, "SELECT datetime(%.17g)", pPost->rDate);
689 if( fossil_strcmp(pOPost->zUser,pPost->zUser)==0 ){
690 @ and edited on %h(zDate)
@@ -566,21 +692,23 @@
692 @ as edited by %h(pPost->zUser) on %h(zDate)
693 }
694 fossil_free(zDate);
695 if( g.perm.Debug ){
696 @ <span class="debug">\
697 @ <a href="%R/artifact/%h(p->pLeaf->zUuid)">\
698 @ (artifact-%d(p->pLeaf->fpid))</a></span>
699 }
700 @ %z(href("%R/forumpost/%S?t=y",p->zUuid))[history]</a>
701 manifest_destroy(pOPost);
702 }
703 if( fpid!=target ){
704 @ %z(href("%R/forumpost/%S",zUuid))[link]</a>
705 }
706 @ %z(href("%R/forumpost/%S?raw",zUuid))[source]</a>
707 if( p->firt ){
708 ForumEntry *pIrt = p->pPrev;
709 while( pIrt && pIrt->fpid!=p->mfirt ) pIrt = pIrt->pPrev;
710 if( pIrt ){
711 @ in reply to %z(href("%R/forumpost/%S?t=h",pIrt->zUuid))\
712 @ %d(pIrt->sid)</a>
713 }
714 }
@@ -637,10 +765,11 @@
765 ** t=MODE Display mode.
766 ** 'c' for chronological
767 ** 'h' for hierarchical
768 ** 'a' for automatic
769 ** 'r' for raw
770 ** 'y' for history of post X only
771 ** raw If present, show only the post specified and
772 ** show its original unformatted source text.
773 */
774 void forumpost_page(void){
775 forumthread_page();
@@ -679,10 +808,12 @@
808 ** t=MODE Display mode. MODE is...
809 ** 'c' for chronological, or
810 ** 'h' for hierarchical, or
811 ** 'a' for automatic, or
812 ** 'r' for raw.
813 ** raw Show only the post given by name= and show it unformatted
814 ** hist Show only the edit history for the name= post
815 */
816 void forumthread_page(void){
817 int fpid;
818 int froot;
819 const char *zName = P("name");
@@ -710,11 +841,13 @@
841 zMode = "c"; /* Default to chronological on mobile */
842 }else{
843 zMode = "h";
844 }
845 }
846 if( zMode[0]!='y' ){
847 forumthread_page_header(froot, fpid);
848 }
849 if( bRaw && fpid ){
850 Manifest *pPost;
851 pPost = manifest_get(fpid, CFTYPE_FORUM, 0);
852 if( pPost==0 ){
853 @ <p>No such forum post: %h(zName)
@@ -735,10 +868,14 @@
868 forum_display_chronological(froot, fpid, 0);
869 }else if( zMode[0]=='r' ){
870 style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName);
871 style_submenu_element("Hierarchical", "%R/%s/%s?t=h", g.zPath, zName);
872 forum_display_chronological(froot, fpid, 1);
873 }else if( zMode[0]=='y' ){
874 style_header("Edit History Of A Forum Post");
875 style_submenu_element("Complete Thread", "%R/%s/%s?t=a", g.zPath, zName);
876 forum_display_history(froot, fpid, 1);
877 }else{
878 style_submenu_element("Chronological", "%R/%s/%s?t=c", g.zPath, zName);
879 style_submenu_element("Unformatted", "%R/%s/%s?t=r", g.zPath, zName);
880 forum_display_hierarchical(froot, fpid);
881 }
@@ -753,10 +890,19 @@
890 if( P("domod") ) return 1;
891 if( g.perm.WrTForum ) return 0;
892 if( g.perm.ModForum ) return 0;
893 return 1;
894 }
895
896 /*
897 ** Return true if the string is white-space only.
898 */
899 static int whitespace_only(const char *z){
900 if( z==0 ) return 1;
901 while( z[0] && fossil_isspace(z[0]) ){ z++; }
902 return z[0]==0;
903 }
904
905 /*
906 ** Add a new Forum Post artifact to the repository.
907 **
908 ** Return true if a redirect occurs.
@@ -773,12 +919,16 @@
919 char *zI;
920 char *zG;
921 int iBasis;
922 Blob x, cksum, formatCheck, errMsg;
923 Manifest *pPost;
924 int nContent = zContent ? (int)strlen(zContent) : 0;
925
926 schema_forum();
927 if( iEdit==0 && whitespace_only(zContent) ){
928 return 0;
929 }
930 if( iInReplyTo==0 && iEdit>0 ){
931 iBasis = iEdit;
932 iInReplyTo = db_int(0, "SELECT firt FROM forumpost WHERE fpid=%d", iEdit);
933 }else{
934 iBasis = iInReplyTo;
@@ -819,11 +969,11 @@
969 }else{
970 zUser = login_name();
971 }
972 }
973 blob_appendf(&x, "U %F\n", zUser);
974 blob_appendf(&x, "W %d\n%s\n", nContent, zContent);
975 md5sum_blob(&x, &cksum);
976 blob_appendf(&x, "Z %b\n", &cksum);
977 blob_reset(&cksum);
978
979 /* Verify that the artifact we are creating is well-formed */
@@ -950,24 +1100,24 @@
1100 login_check_credentials();
1101 if( !g.perm.WrForum ){
1102 login_needed(g.anon.WrForum);
1103 return;
1104 }
1105 if( P("submit") && cgi_csrf_safe(1) ){
1106 if( forum_post(zTitle, 0, 0, 0, zMimetype, zContent) ) return;
1107 }
1108 if( P("preview") && !whitespace_only(zContent) ){
1109 @ <h1>Preview:</h1>
1110 forum_render(zTitle, zMimetype, zContent, "forumEdit", 1);
1111 }
1112 style_header("New Forum Thread");
1113 @ <form action="%R/forume1" method="POST">
1114 @ <h1>New Thread:</h1>
1115 forum_from_line();
1116 forum_entry_widget(zTitle, zMimetype, zContent);
1117 @ <input type="submit" name="preview" value="Preview">
1118 if( P("preview") && !whitespace_only(zContent) ){
1119 @ <input type="submit" name="submit" value="Submit">
1120 }else{
1121 @ <input type="submit" name="submit" value="Submit" disabled>
1122 }
1123 if( g.perm.Debug ){
@@ -1053,14 +1203,17 @@
1203 }
1204 return;
1205 }
1206 }
1207 isDelete = P("nullout")!=0;
1208 if( P("submit")
1209 && isCsrfSafe
1210 && (zContent = PDT("content",""))!=0
1211 && (!whitespace_only(zContent) || isDelete)
1212 ){
1213 int done = 1;
1214 const char *zMimetype = PD("mimetype",DEFAULT_FORUM_MIMETYPE);
 
1215 if( P("reply") ){
1216 done = forum_post(0, fpid, 0, 0, zMimetype, zContent);
1217 }else if( P("edit") || isDelete ){
1218 done = forum_post(P("title"), 0, fpid, 0, zMimetype, zContent);
1219 }else{
@@ -1124,11 +1277,11 @@
1277 zDisplayName = display_name_from_login(pPost->zUser);
1278 @ <h3 class='forumPostHdr'>By %h(zDisplayName) on %h(zDate)</h3>
1279 fossil_free(zDisplayName);
1280 fossil_free(zDate);
1281 forum_render(0, pPost->zMimetype, pPost->zWiki, "forumEdit", 1);
1282 if( P("preview") && !whitespace_only(zContent) ){
1283 @ <h2>Preview:</h2>
1284 forum_render(0, zMimetype,zContent, "forumEdit", 1);
1285 }
1286 @ <h2>Enter Reply:</h2>
1287 @ <form action="%R/forume2" method="POST">
@@ -1139,11 +1292,11 @@
1292 }
1293 if( !isDelete ){
1294 @ <input type="submit" name="preview" value="Preview">
1295 }
1296 @ <input type="submit" name="cancel" value="Cancel">
1297 if( (P("preview") && !whitespace_only(zContent)) || isDelete ){
1298 @ <input type="submit" name="submit" value="Submit">
1299 }
1300 if( g.perm.Debug ){
1301 /* For the test-forumnew page add these extra debugging controls */
1302 @ <div class="debug">
1303
+5 -1
--- src/fshell.c
+++ src/fshell.c
@@ -60,11 +60,15 @@
6060
pid_t childPid;
6161
char *zLine = 0;
6262
char *zPrompt = 0;
6363
fDebug = find_option("debug", 0, 0)!=0;
6464
db_find_and_open_repository(OPEN_ANY_SCHEMA|OPEN_OK_NOT_FOUND, 0);
65
- zPrompt = mprintf("fossil (%z)> ", db_get("project-name","no repo"));
65
+ if(g.zRepositoryName!=0){
66
+ zPrompt = mprintf("fossil (%z)> ", db_get("project-name","unnamed"));
67
+ }else{
68
+ zPrompt = mprintf("fossil (no repo)> ");
69
+ }
6670
db_close(0);
6771
sqlite3_shutdown();
6872
linenoiseSetMultiLine(1);
6973
while( (free(zLine), zLine = linenoise(zPrompt)) ){
7074
/* Remember shell history within the current session */
7175
--- src/fshell.c
+++ src/fshell.c
@@ -60,11 +60,15 @@
60 pid_t childPid;
61 char *zLine = 0;
62 char *zPrompt = 0;
63 fDebug = find_option("debug", 0, 0)!=0;
64 db_find_and_open_repository(OPEN_ANY_SCHEMA|OPEN_OK_NOT_FOUND, 0);
65 zPrompt = mprintf("fossil (%z)> ", db_get("project-name","no repo"));
 
 
 
 
66 db_close(0);
67 sqlite3_shutdown();
68 linenoiseSetMultiLine(1);
69 while( (free(zLine), zLine = linenoise(zPrompt)) ){
70 /* Remember shell history within the current session */
71
--- src/fshell.c
+++ src/fshell.c
@@ -60,11 +60,15 @@
60 pid_t childPid;
61 char *zLine = 0;
62 char *zPrompt = 0;
63 fDebug = find_option("debug", 0, 0)!=0;
64 db_find_and_open_repository(OPEN_ANY_SCHEMA|OPEN_OK_NOT_FOUND, 0);
65 if(g.zRepositoryName!=0){
66 zPrompt = mprintf("fossil (%z)> ", db_get("project-name","unnamed"));
67 }else{
68 zPrompt = mprintf("fossil (no repo)> ");
69 }
70 db_close(0);
71 sqlite3_shutdown();
72 linenoiseSetMultiLine(1);
73 while( (free(zLine), zLine = linenoise(zPrompt)) ){
74 /* Remember shell history within the current session */
75
+3 -1
--- src/graph.c
+++ src/graph.c
@@ -534,17 +534,19 @@
534534
pParent->idxTop = pRow->idxTop;
535535
}
536536
}
537537
538538
if( tmFlags & TIMELINE_FILLGAPS ){
539
- /* If a node has no pChild and there is a node higher up in the graph
539
+ /* If a node has no pChild in the graph
540
+ ** and there is a node higher up in the graph
540541
** that is in the same branch and has no in-graph parent, then
541542
** make the lower node a step-child of the upper node. This will
542543
** be represented on the graph by a thick dotted line without an arrowhead.
543544
*/
544545
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
545546
if( pRow->pChild ) continue;
547
+ if( pRow->isLeaf ) continue;
546548
for(pLoop=pRow->pPrev; pLoop; pLoop=pLoop->pPrev){
547549
if( pLoop->nParent>0
548550
&& pLoop->zBranch==pRow->zBranch
549551
&& hashFind(p,pLoop->aParent[0])==0
550552
){
551553
--- src/graph.c
+++ src/graph.c
@@ -534,17 +534,19 @@
534 pParent->idxTop = pRow->idxTop;
535 }
536 }
537
538 if( tmFlags & TIMELINE_FILLGAPS ){
539 /* If a node has no pChild and there is a node higher up in the graph
 
540 ** that is in the same branch and has no in-graph parent, then
541 ** make the lower node a step-child of the upper node. This will
542 ** be represented on the graph by a thick dotted line without an arrowhead.
543 */
544 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
545 if( pRow->pChild ) continue;
 
546 for(pLoop=pRow->pPrev; pLoop; pLoop=pLoop->pPrev){
547 if( pLoop->nParent>0
548 && pLoop->zBranch==pRow->zBranch
549 && hashFind(p,pLoop->aParent[0])==0
550 ){
551
--- src/graph.c
+++ src/graph.c
@@ -534,17 +534,19 @@
534 pParent->idxTop = pRow->idxTop;
535 }
536 }
537
538 if( tmFlags & TIMELINE_FILLGAPS ){
539 /* If a node has no pChild in the graph
540 ** and there is a node higher up in the graph
541 ** that is in the same branch and has no in-graph parent, then
542 ** make the lower node a step-child of the upper node. This will
543 ** be represented on the graph by a thick dotted line without an arrowhead.
544 */
545 for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
546 if( pRow->pChild ) continue;
547 if( pRow->isLeaf ) continue;
548 for(pLoop=pRow->pPrev; pLoop; pLoop=pLoop->pPrev){
549 if( pLoop->nParent>0
550 && pLoop->zBranch==pRow->zBranch
551 && hashFind(p,pLoop->aParent[0])==0
552 ){
553
+228 -121
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -30,10 +30,11 @@
3030
#ifdef FOSSIL_ENABLE_SSL
3131
3232
#include <openssl/bio.h>
3333
#include <openssl/ssl.h>
3434
#include <openssl/err.h>
35
+#include <openssl/x509.h>
3536
3637
#include "http_ssl.h"
3738
#include <assert.h>
3839
#include <sys/types.h>
3940
@@ -45,11 +46,15 @@
4546
static int sslIsInit = 0; /* True after global initialization */
4647
static BIO *iBio = 0; /* OpenSSL I/O abstraction */
4748
static char *sslErrMsg = 0; /* Text of most recent OpenSSL error */
4849
static SSL_CTX *sslCtx; /* SSL context */
4950
static SSL *ssl;
50
-
51
+static struct { /* Accept this SSL cert for this session only */
52
+ char *zHost; /* Subject or host name */
53
+ char *zHash; /* SHA2-256 hash of the cert */
54
+} sException;
55
+static int sslNoCertVerify = 0; /* Do not verify SSL certs */
5156
5257
/*
5358
** Clear the SSL error message
5459
*/
5560
static void ssl_clear_errmsg(void){
@@ -184,11 +189,12 @@
184189
Blob snd, reply;
185190
int done=0,end=0;
186191
blob_zero(&snd);
187192
blob_appendf(&snd, "CONNECT %s:%d HTTP/1.1\r\n", pUrlData->hostname,
188193
pUrlData->proxyOrigPort);
189
- blob_appendf(&snd, "Host: %s:%d\r\n", pUrlData->hostname, pUrlData->proxyOrigPort);
194
+ blob_appendf(&snd, "Host: %s:%d\r\n",
195
+ pUrlData->hostname, pUrlData->proxyOrigPort);
190196
if( pUrlData->proxyAuth ){
191197
blob_appendf(&snd, "Proxy-Authorization: %s\r\n", pUrlData->proxyAuth);
192198
}
193199
blob_append(&snd, "Proxy-Connection: keep-alive\r\n", -1);
194200
blob_appendf(&snd, "User-Agent: %s\r\n", get_user_agent());
@@ -222,46 +228,45 @@
222228
}while(!done);
223229
sscanf(bbuf, "HTTP/1.%d %d", &httpVerMin, &rc);
224230
blob_reset(&reply);
225231
return rc;
226232
}
233
+
234
+/*
235
+** Invoke this routine to disable SSL cert verification. After
236
+** this call is made, any SSL cert that the server provides will
237
+** be accepted. Communication will still be encrypted, but the
238
+** client has no way of knowing whether it is talking to the
239
+** real server or a man-in-the-middle imposter.
240
+*/
241
+void ssl_disable_cert_verification(void){
242
+ sslNoCertVerify = 1;
243
+}
227244
228245
/*
229246
** Open an SSL connection. The identify of the server is determined
230247
** as follows:
231248
**
232
-** g.url.name Name of the server. Ex: www.fossil-scm.org
249
+** pUrlData->name Name of the server. Ex: www.fossil-scm.org
250
+** g.url.name Name of the proxy server, if proxying.
233251
** pUrlData->port TCP/IP port to use. Ex: 80
234252
**
235253
** Return the number of errors.
236254
*/
237255
int ssl_open(UrlData *pUrlData){
238256
X509 *cert;
239
- int hasSavedCertificate = 0;
240
- int trusted = 0;
241
- unsigned long e;
242257
243258
ssl_global_init();
244
-
245
- /* Get certificate for current server from global config and
246
- * (if we have it in config) add it to certificate store.
247
- */
248
- cert = ssl_get_certificate(pUrlData, &trusted);
249
- if ( cert!=NULL ){
250
- X509_STORE_add_cert(SSL_CTX_get_cert_store(sslCtx), cert);
251
- X509_free(cert);
252
- hasSavedCertificate = 1;
253
- }
254
-
255259
if( pUrlData->useProxy ){
256260
int rc;
257261
char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port);
258262
BIO *sBio = BIO_new_connect(connStr);
259263
free(connStr);
260264
if( BIO_do_connect(sBio)<=0 ){
261265
ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)",
262
- pUrlData->name, pUrlData->port, ERR_reason_error_string(ERR_get_error()));
266
+ pUrlData->name, pUrlData->port,
267
+ ERR_reason_error_string(ERR_get_error()));
263268
ssl_close();
264269
return 1;
265270
}
266271
rc = establish_proxy_tunnel(pUrlData, sBio);
267272
if( rc<200||rc>299 ){
@@ -282,11 +287,13 @@
282287
return 1;
283288
}
284289
BIO_get_ssl(iBio, &ssl);
285290
286291
#if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT)
287
- if( !SSL_set_tlsext_host_name(ssl, (pUrlData->useProxy?pUrlData->hostname:pUrlData->name)) ){
292
+ if( !SSL_set_tlsext_host_name(ssl,
293
+ (pUrlData->useProxy?pUrlData->hostname:pUrlData->name))
294
+ ){
288295
fossil_warning("WARNING: failed to set server name indication (SNI), "
289296
"continuing without it.\n");
290297
}
291298
#endif
292299
@@ -296,11 +303,12 @@
296303
char *connStr = mprintf("%s:%d", pUrlData->name, pUrlData->port);
297304
BIO_set_conn_hostname(iBio, connStr);
298305
free(connStr);
299306
if( BIO_do_connect(iBio)<=0 ){
300307
ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
301
- pUrlData->name, pUrlData->port, ERR_reason_error_string(ERR_get_error()));
308
+ pUrlData->name, pUrlData->port,
309
+ ERR_reason_error_string(ERR_get_error()));
302310
ssl_close();
303311
return 1;
304312
}
305313
}
306314
@@ -319,81 +327,78 @@
319327
ssl_set_errmsg("No SSL certificate was presented by the peer");
320328
ssl_close();
321329
return 1;
322330
}
323331
324
- if( trusted<=0 && (e = SSL_get_verify_result(ssl)) != X509_V_OK ){
332
+ if( !sslNoCertVerify && SSL_get_verify_result(ssl)!=X509_V_OK ){
325333
char *desc, *prompt;
326
- const char *warning = "";
327334
Blob ans;
328335
char cReply;
329336
BIO *mem;
330337
unsigned char md[32];
331
- unsigned int mdLength = 31;
332
-
333
- mem = BIO_new(BIO_s_mem());
334
- X509_NAME_print_ex(mem, X509_get_subject_name(cert), 2, XN_FLAG_MULTILINE);
335
- BIO_puts(mem, "\n\nIssued By:\n\n");
336
- X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 2, XN_FLAG_MULTILINE);
337
- BIO_puts(mem, "\n\nSHA1 Fingerprint:\n\n ");
338
- if(X509_digest(cert, EVP_sha1(), md, &mdLength)){
339
- int j;
340
- for( j = 0; j < mdLength; ++j ) {
341
- BIO_printf(mem, " %02x", md[j]);
342
- }
343
- }
344
- BIO_write(mem, "", 1); /* nul-terminate mem buffer */
345
- BIO_get_mem_data(mem, &desc);
346
-
347
- if( hasSavedCertificate ){
348
- warning = "WARNING: Certificate doesn't match the "
349
- "saved certificate for this host!";
350
- }
351
- prompt = mprintf("\nSSL verification failed: %s\n"
352
- "Certificate received: \n\n%s\n\n%s\n"
353
- "Either:\n"
354
- " * verify the certificate is correct using the "
355
- "SHA1 fingerprint above\n"
356
- " * use the global ssl-ca-location setting to specify your CA root\n"
357
- " certificates list\n\n"
358
- "If you are not expecting this message, answer no and "
359
- "contact your server\nadministrator.\n\n"
360
- "Accept certificate for host %s (a=always/y/N)? ",
361
- X509_verify_cert_error_string(e), desc, warning,
362
- pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
363
- BIO_free(mem);
364
-
365
- prompt_user(prompt, &ans);
366
- free(prompt);
367
- cReply = blob_str(&ans)[0];
368
- blob_reset(&ans);
369
- if( cReply!='y' && cReply!='Y' && cReply!='a' && cReply!='A') {
370
- X509_free(cert);
371
- ssl_set_errmsg("SSL certificate declined");
372
- ssl_close();
373
- return 1;
374
- }
375
- if( cReply=='a' || cReply=='A') {
376
- if ( trusted==0 ){
377
- prompt_user("\nSave this certificate as fully trusted (a=always/N)? ",
378
- &ans);
379
- cReply = blob_str(&ans)[0];
380
- trusted = ( cReply=='a' || cReply=='A' );
381
- blob_reset(&ans);
382
- }
383
- ssl_save_certificate(pUrlData, cert, trusted);
338
+ char zHash[32*2+1];
339
+ unsigned int mdLength = (int)sizeof(md);
340
+
341
+ memset(md, 0, sizeof(md));
342
+ zHash[0] = 0;
343
+ if( X509_digest(cert, EVP_sha256(), md, &mdLength) ){
344
+ int j;
345
+ for(j=0; j<mdLength && j*2+1<sizeof(zHash); ++j){
346
+ zHash[j*2] = "0123456789abcdef"[md[j]>>4];
347
+ zHash[j*2+1] = "0123456789abcdef"[md[j]&0xf];
348
+ }
349
+ zHash[j*2] = 0;
350
+ }
351
+
352
+ if( ssl_certificate_exception_exists(pUrlData, zHash) ){
353
+ /* Ignore the failure because an exception exists */
354
+ ssl_one_time_exception(pUrlData, zHash);
355
+ }else{
356
+ /* Tell the user about the failure and ask what to do */
357
+ mem = BIO_new(BIO_s_mem());
358
+ BIO_puts(mem, " subject: ");
359
+ X509_NAME_print_ex(mem, X509_get_subject_name(cert), 0, XN_FLAG_ONELINE);
360
+ BIO_puts(mem, "\n issuer: ");
361
+ X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 0, XN_FLAG_ONELINE);
362
+ BIO_printf(mem, "\n sha256: %s", zHash);
363
+ BIO_get_mem_data(mem, &desc);
364
+
365
+ prompt = mprintf("Unable to verify SSL cert from %s\n%s\n"
366
+ "accept this cert and continue (y/N)? ",
367
+ pUrlData->name, desc);
368
+ BIO_free(mem);
369
+
370
+ prompt_user(prompt, &ans);
371
+ free(prompt);
372
+ cReply = blob_str(&ans)[0];
373
+ blob_reset(&ans);
374
+ if( cReply!='y' && cReply!='Y' ){
375
+ X509_free(cert);
376
+ ssl_set_errmsg("SSL cert declined");
377
+ ssl_close();
378
+ return 1;
379
+ }
380
+ ssl_one_time_exception(pUrlData, zHash);
381
+ prompt_user("remember this exception (y/N)? ", &ans);
382
+ cReply = blob_str(&ans)[0];
383
+ if( cReply=='y' || cReply=='Y') {
384
+ ssl_remember_certificate_exception(pUrlData, zHash);
385
+ }
386
+ blob_reset(&ans);
384387
}
385388
}
386389
387390
/* Set the Global.zIpAddr variable to the server we are talking to.
388391
** This is used to populate the ipaddr column of the rcvfrom table,
389392
** if any files are received from the server.
390393
*/
391394
{
392
- /* As soon as libressl implements BIO_ADDR_hostname_string/BIO_get_conn_address.
393
- * check here for the correct LIBRESSL_VERSION_NUMBER too. For now: disable */
394
- #if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L \
395
+ /* As soon as libressl implements
396
+ ** BIO_ADDR_hostname_string/BIO_get_conn_address.
397
+ ** check here for the correct LIBRESSL_VERSION_NUMBER too. For now: disable
398
+ */
399
+#if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L \
395400
&& !defined(LIBRESSL_VERSION_NUMBER)
396401
char *ip = BIO_ADDR_hostname_string(BIO_get_conn_address(iBio),1);
397402
g.zIpAddr = mprintf("%s", ip);
398403
OPENSSL_free(ip);
399404
#else
@@ -407,58 +412,53 @@
407412
X509_free(cert);
408413
return 0;
409414
}
410415
411416
/*
412
-** Save certificate to global config.
417
+** Remember that the cert with the given hash is a acceptable for
418
+** use with pUrlData->name.
419
+*/
420
+LOCAL void ssl_remember_certificate_exception(
421
+ UrlData *pUrlData,
422
+ const char *zHash
423
+){
424
+ char *zName = mprintf("cert:%s", pUrlData->name);
425
+ db_set(zName, zHash, 1);
426
+ fossil_free(zName);
427
+}
428
+
429
+/*
430
+** Return true if the there exists a certificate exception for
431
+** pUrlData->name that matches the hash.
413432
*/
414
-void ssl_save_certificate(UrlData *pUrlData, X509 *cert, int trusted){
415
- BIO *mem;
416
- char *zCert, *zHost;
417
-
418
- mem = BIO_new(BIO_s_mem());
419
- PEM_write_bio_X509(mem, cert);
420
- BIO_write(mem, "", 1); /* nul-terminate mem buffer */
421
- BIO_get_mem_data(mem, &zCert);
422
- zHost = mprintf("cert:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
423
- db_set(zHost, zCert, 1);
424
- free(zHost);
425
- zHost = mprintf("trusted:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
426
- db_set_int(zHost, trusted, 1);
427
- free(zHost);
428
- BIO_free(mem);
433
+LOCAL int ssl_certificate_exception_exists(
434
+ UrlData *pUrlData,
435
+ const char *zHash
436
+){
437
+ char *zName, *zValue;
438
+ if( fossil_strcmp(sException.zHost,pUrlData->name)==0
439
+ && fossil_strcmp(sException.zHash,zHash)==0
440
+ ){
441
+ return 1;
442
+ }
443
+ zName = mprintf("cert:%s", pUrlData->name);
444
+ zValue = db_get(zName,0);
445
+ fossil_free(zName);
446
+ return zValue!=0 && strcmp(zHash,zValue)==0;
429447
}
430448
431449
/*
432
-** Get certificate for pUrlData->urlName from global config.
433
-** Return NULL if no certificate found.
450
+** Remember zHash as an acceptable certificate for this session only.
434451
*/
435
-X509 *ssl_get_certificate(UrlData *pUrlData, int *pTrusted){
436
- char *zHost, *zCert;
437
- BIO *mem;
438
- X509 *cert;
439
-
440
- zHost = mprintf("cert:%s",
441
- pUrlData->useProxy ? pUrlData->hostname : pUrlData->name);
442
- zCert = db_get(zHost, NULL);
443
- free(zHost);
444
- if ( zCert==NULL )
445
- return NULL;
446
-
447
- if ( pTrusted!=0 ){
448
- zHost = mprintf("trusted:%s",
449
- pUrlData->useProxy ? pUrlData->hostname : pUrlData->name);
450
- *pTrusted = db_get_int(zHost, 0);
451
- free(zHost);
452
- }
453
-
454
- mem = BIO_new(BIO_s_mem());
455
- BIO_puts(mem, zCert);
456
- cert = PEM_read_bio_X509(mem, NULL, 0, NULL);
457
- free(zCert);
458
- BIO_free(mem);
459
- return cert;
452
+LOCAL void ssl_one_time_exception(
453
+ UrlData *pUrlData,
454
+ const char *zHash
455
+){
456
+ fossil_free(sException.zHost);
457
+ sException.zHost = fossil_strdup(pUrlData->name);
458
+ fossil_free(sException.zHash);
459
+ sException.zHash = fossil_strdup(zHash);
460460
}
461461
462462
/*
463463
** Send content out over the SSL connection.
464464
*/
@@ -498,5 +498,112 @@
498498
}
499499
return total;
500500
}
501501
502502
#endif /* FOSSIL_ENABLE_SSL */
503
+
504
+/*
505
+** COMMAND: tls-config*
506
+**
507
+** Usage: %fossil tls-config [SUBCOMMAND] [OPTIONS...] [ARGS...]
508
+**
509
+** This command is used to view or modify the TLS (Transport Layer
510
+** Security) configuration for Fossil. TLS (formerly SSL) is the
511
+** encryption technology used for secure HTTPS transport.
512
+**
513
+** Sub-commands:
514
+**
515
+** show Show the TLS configuration
516
+**
517
+** remove-exception DOMAIN... Remove TLS cert exceptions
518
+** for the domains listed. Or if
519
+** the --all option is specified,
520
+** remove all TLS cert exceptions.
521
+*/
522
+void test_tlsconfig_info(void){
523
+ const char *zCmd;
524
+ size_t nCmd;
525
+ int nHit = 0;
526
+#if !defined(FOSSIL_ENABLE_SSL)
527
+ fossil_print("TLS disabled in this build\n");
528
+#else
529
+ db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
530
+ db_open_config(1,0);
531
+ zCmd = g.argc>=3 ? g.argv[2] : "show";
532
+ nCmd = strlen(zCmd);
533
+ if( strncmp("show",zCmd,nCmd)==0 ){
534
+ const char *zName, *zValue;
535
+ size_t nName;
536
+ Stmt q;
537
+ fossil_print("OpenSSL-version: %s\n", SSLeay_version(SSLEAY_VERSION));
538
+ fossil_print("OpenSSL-cert-file: %s\n", X509_get_default_cert_file());
539
+ fossil_print("OpenSSL-cert-dir: %s\n", X509_get_default_cert_dir());
540
+ zName = X509_get_default_cert_file_env();
541
+ zValue = fossil_getenv(zName);
542
+ if( zValue==0 ) zValue = "";
543
+ nName = strlen(zName);
544
+ fossil_print("%s:%.*s%s\n", zName, 19-nName, "", zValue);
545
+ zName = X509_get_default_cert_dir_env();
546
+ zValue = fossil_getenv(zName);
547
+ if( zValue==0 ) zValue = "";
548
+ nName = strlen(zName);
549
+ fossil_print("%s:%.*s%s\n", zName, 19-nName, "", zValue);
550
+ nHit++;
551
+ fossil_print("ssl-ca-location: %s\n", db_get("ssl-ca-location",""));
552
+ fossil_print("ssl-identity: %s\n", db_get("ssl-identity",""));
553
+ db_prepare(&q,
554
+ "SELECT name FROM global_config"
555
+ " WHERE name GLOB 'cert:*'"
556
+ "UNION ALL "
557
+ "SELECT name FROM config"
558
+ " WHERE name GLOB 'cert:*'"
559
+ " ORDER BY name"
560
+ );
561
+ while( db_step(&q)==SQLITE_ROW ){
562
+ fossil_print("exception: %s\n", db_column_text(&q,0)+5);
563
+ }
564
+ db_finalize(&q);
565
+ }else
566
+ if( strncmp("remove-exception",zCmd,nCmd)==0 ){
567
+ int i;
568
+ Blob sql;
569
+ char *zSep = "(";
570
+ db_begin_transaction();
571
+ blob_init(&sql, 0, 0);
572
+ if( g.argc==4 && find_option("all",0,0)!=0 ){
573
+ blob_append_sql(&sql,
574
+ "DELETE FROM global_config WHERE name GLOB 'cert:*';\n"
575
+ "DELETE FROM global_config WHERE name GLOB 'trusted:*';\n"
576
+ "DELETE FROM config WHERE name GLOB 'cert:*';\n"
577
+ "DELETE FROM config WHERE name GLOB 'trusted:*';\n"
578
+ );
579
+ }else{
580
+ if( g.argc<4 ){
581
+ usage("remove-exception DOMAIN-NAME ...");
582
+ }
583
+ blob_append_sql(&sql,"DELETE FROM global_config WHERE name IN ");
584
+ for(i=3; i<g.argc; i++){
585
+ blob_append_sql(&sql,"%s'cert:%q','trust:%q'",
586
+ zSep/*safe-for-%s*/, g.argv[i], g.argv[i]);
587
+ zSep = ",";
588
+ }
589
+ blob_append_sql(&sql,");\n");
590
+ zSep = "(";
591
+ blob_append_sql(&sql,"DELETE FROM config WHERE name IN ");
592
+ for(i=3; i<g.argc; i++){
593
+ blob_append_sql(&sql,"%s'cert:%q','trusted:%q'",
594
+ zSep/*safe-for-%s*/, g.argv[i], g.argv[i]);
595
+ zSep = ",";
596
+ }
597
+ blob_append_sql(&sql,");");
598
+ }
599
+ db_exec_sql(blob_str(&sql));
600
+ db_commit_transaction();
601
+ blob_reset(&sql);
602
+ }else
603
+ /*default*/{
604
+ fossil_fatal("unknown sub-command \"%s\".\nshould be one of:"
605
+ " remove-exception show",
606
+ zCmd);
607
+ }
608
+#endif
609
+}
503610
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -30,10 +30,11 @@
30 #ifdef FOSSIL_ENABLE_SSL
31
32 #include <openssl/bio.h>
33 #include <openssl/ssl.h>
34 #include <openssl/err.h>
 
35
36 #include "http_ssl.h"
37 #include <assert.h>
38 #include <sys/types.h>
39
@@ -45,11 +46,15 @@
45 static int sslIsInit = 0; /* True after global initialization */
46 static BIO *iBio = 0; /* OpenSSL I/O abstraction */
47 static char *sslErrMsg = 0; /* Text of most recent OpenSSL error */
48 static SSL_CTX *sslCtx; /* SSL context */
49 static SSL *ssl;
50
 
 
 
 
51
52 /*
53 ** Clear the SSL error message
54 */
55 static void ssl_clear_errmsg(void){
@@ -184,11 +189,12 @@
184 Blob snd, reply;
185 int done=0,end=0;
186 blob_zero(&snd);
187 blob_appendf(&snd, "CONNECT %s:%d HTTP/1.1\r\n", pUrlData->hostname,
188 pUrlData->proxyOrigPort);
189 blob_appendf(&snd, "Host: %s:%d\r\n", pUrlData->hostname, pUrlData->proxyOrigPort);
 
190 if( pUrlData->proxyAuth ){
191 blob_appendf(&snd, "Proxy-Authorization: %s\r\n", pUrlData->proxyAuth);
192 }
193 blob_append(&snd, "Proxy-Connection: keep-alive\r\n", -1);
194 blob_appendf(&snd, "User-Agent: %s\r\n", get_user_agent());
@@ -222,46 +228,45 @@
222 }while(!done);
223 sscanf(bbuf, "HTTP/1.%d %d", &httpVerMin, &rc);
224 blob_reset(&reply);
225 return rc;
226 }
 
 
 
 
 
 
 
 
 
 
 
227
228 /*
229 ** Open an SSL connection. The identify of the server is determined
230 ** as follows:
231 **
232 ** g.url.name Name of the server. Ex: www.fossil-scm.org
 
233 ** pUrlData->port TCP/IP port to use. Ex: 80
234 **
235 ** Return the number of errors.
236 */
237 int ssl_open(UrlData *pUrlData){
238 X509 *cert;
239 int hasSavedCertificate = 0;
240 int trusted = 0;
241 unsigned long e;
242
243 ssl_global_init();
244
245 /* Get certificate for current server from global config and
246 * (if we have it in config) add it to certificate store.
247 */
248 cert = ssl_get_certificate(pUrlData, &trusted);
249 if ( cert!=NULL ){
250 X509_STORE_add_cert(SSL_CTX_get_cert_store(sslCtx), cert);
251 X509_free(cert);
252 hasSavedCertificate = 1;
253 }
254
255 if( pUrlData->useProxy ){
256 int rc;
257 char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port);
258 BIO *sBio = BIO_new_connect(connStr);
259 free(connStr);
260 if( BIO_do_connect(sBio)<=0 ){
261 ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)",
262 pUrlData->name, pUrlData->port, ERR_reason_error_string(ERR_get_error()));
 
263 ssl_close();
264 return 1;
265 }
266 rc = establish_proxy_tunnel(pUrlData, sBio);
267 if( rc<200||rc>299 ){
@@ -282,11 +287,13 @@
282 return 1;
283 }
284 BIO_get_ssl(iBio, &ssl);
285
286 #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT)
287 if( !SSL_set_tlsext_host_name(ssl, (pUrlData->useProxy?pUrlData->hostname:pUrlData->name)) ){
 
 
288 fossil_warning("WARNING: failed to set server name indication (SNI), "
289 "continuing without it.\n");
290 }
291 #endif
292
@@ -296,11 +303,12 @@
296 char *connStr = mprintf("%s:%d", pUrlData->name, pUrlData->port);
297 BIO_set_conn_hostname(iBio, connStr);
298 free(connStr);
299 if( BIO_do_connect(iBio)<=0 ){
300 ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
301 pUrlData->name, pUrlData->port, ERR_reason_error_string(ERR_get_error()));
 
302 ssl_close();
303 return 1;
304 }
305 }
306
@@ -319,81 +327,78 @@
319 ssl_set_errmsg("No SSL certificate was presented by the peer");
320 ssl_close();
321 return 1;
322 }
323
324 if( trusted<=0 && (e = SSL_get_verify_result(ssl)) != X509_V_OK ){
325 char *desc, *prompt;
326 const char *warning = "";
327 Blob ans;
328 char cReply;
329 BIO *mem;
330 unsigned char md[32];
331 unsigned int mdLength = 31;
332
333 mem = BIO_new(BIO_s_mem());
334 X509_NAME_print_ex(mem, X509_get_subject_name(cert), 2, XN_FLAG_MULTILINE);
335 BIO_puts(mem, "\n\nIssued By:\n\n");
336 X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 2, XN_FLAG_MULTILINE);
337 BIO_puts(mem, "\n\nSHA1 Fingerprint:\n\n ");
338 if(X509_digest(cert, EVP_sha1(), md, &mdLength)){
339 int j;
340 for( j = 0; j < mdLength; ++j ) {
341 BIO_printf(mem, " %02x", md[j]);
342 }
343 }
344 BIO_write(mem, "", 1); /* nul-terminate mem buffer */
345 BIO_get_mem_data(mem, &desc);
346
347 if( hasSavedCertificate ){
348 warning = "WARNING: Certificate doesn't match the "
349 "saved certificate for this host!";
350 }
351 prompt = mprintf("\nSSL verification failed: %s\n"
352 "Certificate received: \n\n%s\n\n%s\n"
353 "Either:\n"
354 " * verify the certificate is correct using the "
355 "SHA1 fingerprint above\n"
356 " * use the global ssl-ca-location setting to specify your CA root\n"
357 " certificates list\n\n"
358 "If you are not expecting this message, answer no and "
359 "contact your server\nadministrator.\n\n"
360 "Accept certificate for host %s (a=always/y/N)? ",
361 X509_verify_cert_error_string(e), desc, warning,
362 pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
363 BIO_free(mem);
364
365 prompt_user(prompt, &ans);
366 free(prompt);
367 cReply = blob_str(&ans)[0];
368 blob_reset(&ans);
369 if( cReply!='y' && cReply!='Y' && cReply!='a' && cReply!='A') {
370 X509_free(cert);
371 ssl_set_errmsg("SSL certificate declined");
372 ssl_close();
373 return 1;
374 }
375 if( cReply=='a' || cReply=='A') {
376 if ( trusted==0 ){
377 prompt_user("\nSave this certificate as fully trusted (a=always/N)? ",
378 &ans);
379 cReply = blob_str(&ans)[0];
380 trusted = ( cReply=='a' || cReply=='A' );
381 blob_reset(&ans);
382 }
383 ssl_save_certificate(pUrlData, cert, trusted);
384 }
385 }
386
387 /* Set the Global.zIpAddr variable to the server we are talking to.
388 ** This is used to populate the ipaddr column of the rcvfrom table,
389 ** if any files are received from the server.
390 */
391 {
392 /* As soon as libressl implements BIO_ADDR_hostname_string/BIO_get_conn_address.
393 * check here for the correct LIBRESSL_VERSION_NUMBER too. For now: disable */
394 #if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L \
 
 
395 && !defined(LIBRESSL_VERSION_NUMBER)
396 char *ip = BIO_ADDR_hostname_string(BIO_get_conn_address(iBio),1);
397 g.zIpAddr = mprintf("%s", ip);
398 OPENSSL_free(ip);
399 #else
@@ -407,58 +412,53 @@
407 X509_free(cert);
408 return 0;
409 }
410
411 /*
412 ** Save certificate to global config.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
413 */
414 void ssl_save_certificate(UrlData *pUrlData, X509 *cert, int trusted){
415 BIO *mem;
416 char *zCert, *zHost;
417
418 mem = BIO_new(BIO_s_mem());
419 PEM_write_bio_X509(mem, cert);
420 BIO_write(mem, "", 1); /* nul-terminate mem buffer */
421 BIO_get_mem_data(mem, &zCert);
422 zHost = mprintf("cert:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
423 db_set(zHost, zCert, 1);
424 free(zHost);
425 zHost = mprintf("trusted:%s", pUrlData->useProxy?pUrlData->hostname:pUrlData->name);
426 db_set_int(zHost, trusted, 1);
427 free(zHost);
428 BIO_free(mem);
429 }
430
431 /*
432 ** Get certificate for pUrlData->urlName from global config.
433 ** Return NULL if no certificate found.
434 */
435 X509 *ssl_get_certificate(UrlData *pUrlData, int *pTrusted){
436 char *zHost, *zCert;
437 BIO *mem;
438 X509 *cert;
439
440 zHost = mprintf("cert:%s",
441 pUrlData->useProxy ? pUrlData->hostname : pUrlData->name);
442 zCert = db_get(zHost, NULL);
443 free(zHost);
444 if ( zCert==NULL )
445 return NULL;
446
447 if ( pTrusted!=0 ){
448 zHost = mprintf("trusted:%s",
449 pUrlData->useProxy ? pUrlData->hostname : pUrlData->name);
450 *pTrusted = db_get_int(zHost, 0);
451 free(zHost);
452 }
453
454 mem = BIO_new(BIO_s_mem());
455 BIO_puts(mem, zCert);
456 cert = PEM_read_bio_X509(mem, NULL, 0, NULL);
457 free(zCert);
458 BIO_free(mem);
459 return cert;
460 }
461
462 /*
463 ** Send content out over the SSL connection.
464 */
@@ -498,5 +498,112 @@
498 }
499 return total;
500 }
501
502 #endif /* FOSSIL_ENABLE_SSL */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
503
--- src/http_ssl.c
+++ src/http_ssl.c
@@ -30,10 +30,11 @@
30 #ifdef FOSSIL_ENABLE_SSL
31
32 #include <openssl/bio.h>
33 #include <openssl/ssl.h>
34 #include <openssl/err.h>
35 #include <openssl/x509.h>
36
37 #include "http_ssl.h"
38 #include <assert.h>
39 #include <sys/types.h>
40
@@ -45,11 +46,15 @@
46 static int sslIsInit = 0; /* True after global initialization */
47 static BIO *iBio = 0; /* OpenSSL I/O abstraction */
48 static char *sslErrMsg = 0; /* Text of most recent OpenSSL error */
49 static SSL_CTX *sslCtx; /* SSL context */
50 static SSL *ssl;
51 static struct { /* Accept this SSL cert for this session only */
52 char *zHost; /* Subject or host name */
53 char *zHash; /* SHA2-256 hash of the cert */
54 } sException;
55 static int sslNoCertVerify = 0; /* Do not verify SSL certs */
56
57 /*
58 ** Clear the SSL error message
59 */
60 static void ssl_clear_errmsg(void){
@@ -184,11 +189,12 @@
189 Blob snd, reply;
190 int done=0,end=0;
191 blob_zero(&snd);
192 blob_appendf(&snd, "CONNECT %s:%d HTTP/1.1\r\n", pUrlData->hostname,
193 pUrlData->proxyOrigPort);
194 blob_appendf(&snd, "Host: %s:%d\r\n",
195 pUrlData->hostname, pUrlData->proxyOrigPort);
196 if( pUrlData->proxyAuth ){
197 blob_appendf(&snd, "Proxy-Authorization: %s\r\n", pUrlData->proxyAuth);
198 }
199 blob_append(&snd, "Proxy-Connection: keep-alive\r\n", -1);
200 blob_appendf(&snd, "User-Agent: %s\r\n", get_user_agent());
@@ -222,46 +228,45 @@
228 }while(!done);
229 sscanf(bbuf, "HTTP/1.%d %d", &httpVerMin, &rc);
230 blob_reset(&reply);
231 return rc;
232 }
233
234 /*
235 ** Invoke this routine to disable SSL cert verification. After
236 ** this call is made, any SSL cert that the server provides will
237 ** be accepted. Communication will still be encrypted, but the
238 ** client has no way of knowing whether it is talking to the
239 ** real server or a man-in-the-middle imposter.
240 */
241 void ssl_disable_cert_verification(void){
242 sslNoCertVerify = 1;
243 }
244
245 /*
246 ** Open an SSL connection. The identify of the server is determined
247 ** as follows:
248 **
249 ** pUrlData->name Name of the server. Ex: www.fossil-scm.org
250 ** g.url.name Name of the proxy server, if proxying.
251 ** pUrlData->port TCP/IP port to use. Ex: 80
252 **
253 ** Return the number of errors.
254 */
255 int ssl_open(UrlData *pUrlData){
256 X509 *cert;
 
 
 
257
258 ssl_global_init();
 
 
 
 
 
 
 
 
 
 
 
259 if( pUrlData->useProxy ){
260 int rc;
261 char *connStr = mprintf("%s:%d", g.url.name, pUrlData->port);
262 BIO *sBio = BIO_new_connect(connStr);
263 free(connStr);
264 if( BIO_do_connect(sBio)<=0 ){
265 ssl_set_errmsg("SSL: cannot connect to proxy %s:%d (%s)",
266 pUrlData->name, pUrlData->port,
267 ERR_reason_error_string(ERR_get_error()));
268 ssl_close();
269 return 1;
270 }
271 rc = establish_proxy_tunnel(pUrlData, sBio);
272 if( rc<200||rc>299 ){
@@ -282,11 +287,13 @@
287 return 1;
288 }
289 BIO_get_ssl(iBio, &ssl);
290
291 #if (SSLEAY_VERSION_NUMBER >= 0x00908070) && !defined(OPENSSL_NO_TLSEXT)
292 if( !SSL_set_tlsext_host_name(ssl,
293 (pUrlData->useProxy?pUrlData->hostname:pUrlData->name))
294 ){
295 fossil_warning("WARNING: failed to set server name indication (SNI), "
296 "continuing without it.\n");
297 }
298 #endif
299
@@ -296,11 +303,12 @@
303 char *connStr = mprintf("%s:%d", pUrlData->name, pUrlData->port);
304 BIO_set_conn_hostname(iBio, connStr);
305 free(connStr);
306 if( BIO_do_connect(iBio)<=0 ){
307 ssl_set_errmsg("SSL: cannot connect to host %s:%d (%s)",
308 pUrlData->name, pUrlData->port,
309 ERR_reason_error_string(ERR_get_error()));
310 ssl_close();
311 return 1;
312 }
313 }
314
@@ -319,81 +327,78 @@
327 ssl_set_errmsg("No SSL certificate was presented by the peer");
328 ssl_close();
329 return 1;
330 }
331
332 if( !sslNoCertVerify && SSL_get_verify_result(ssl)!=X509_V_OK ){
333 char *desc, *prompt;
 
334 Blob ans;
335 char cReply;
336 BIO *mem;
337 unsigned char md[32];
338 char zHash[32*2+1];
339 unsigned int mdLength = (int)sizeof(md);
340
341 memset(md, 0, sizeof(md));
342 zHash[0] = 0;
343 if( X509_digest(cert, EVP_sha256(), md, &mdLength) ){
344 int j;
345 for(j=0; j<mdLength && j*2+1<sizeof(zHash); ++j){
346 zHash[j*2] = "0123456789abcdef"[md[j]>>4];
347 zHash[j*2+1] = "0123456789abcdef"[md[j]&0xf];
348 }
349 zHash[j*2] = 0;
350 }
351
352 if( ssl_certificate_exception_exists(pUrlData, zHash) ){
353 /* Ignore the failure because an exception exists */
354 ssl_one_time_exception(pUrlData, zHash);
355 }else{
356 /* Tell the user about the failure and ask what to do */
357 mem = BIO_new(BIO_s_mem());
358 BIO_puts(mem, " subject: ");
359 X509_NAME_print_ex(mem, X509_get_subject_name(cert), 0, XN_FLAG_ONELINE);
360 BIO_puts(mem, "\n issuer: ");
361 X509_NAME_print_ex(mem, X509_get_issuer_name(cert), 0, XN_FLAG_ONELINE);
362 BIO_printf(mem, "\n sha256: %s", zHash);
363 BIO_get_mem_data(mem, &desc);
364
365 prompt = mprintf("Unable to verify SSL cert from %s\n%s\n"
366 "accept this cert and continue (y/N)? ",
367 pUrlData->name, desc);
368 BIO_free(mem);
369
370 prompt_user(prompt, &ans);
371 free(prompt);
372 cReply = blob_str(&ans)[0];
373 blob_reset(&ans);
374 if( cReply!='y' && cReply!='Y' ){
375 X509_free(cert);
376 ssl_set_errmsg("SSL cert declined");
377 ssl_close();
378 return 1;
379 }
380 ssl_one_time_exception(pUrlData, zHash);
381 prompt_user("remember this exception (y/N)? ", &ans);
382 cReply = blob_str(&ans)[0];
383 if( cReply=='y' || cReply=='Y') {
384 ssl_remember_certificate_exception(pUrlData, zHash);
385 }
386 blob_reset(&ans);
 
 
 
 
387 }
388 }
389
390 /* Set the Global.zIpAddr variable to the server we are talking to.
391 ** This is used to populate the ipaddr column of the rcvfrom table,
392 ** if any files are received from the server.
393 */
394 {
395 /* As soon as libressl implements
396 ** BIO_ADDR_hostname_string/BIO_get_conn_address.
397 ** check here for the correct LIBRESSL_VERSION_NUMBER too. For now: disable
398 */
399 #if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x10100000L \
400 && !defined(LIBRESSL_VERSION_NUMBER)
401 char *ip = BIO_ADDR_hostname_string(BIO_get_conn_address(iBio),1);
402 g.zIpAddr = mprintf("%s", ip);
403 OPENSSL_free(ip);
404 #else
@@ -407,58 +412,53 @@
412 X509_free(cert);
413 return 0;
414 }
415
416 /*
417 ** Remember that the cert with the given hash is a acceptable for
418 ** use with pUrlData->name.
419 */
420 LOCAL void ssl_remember_certificate_exception(
421 UrlData *pUrlData,
422 const char *zHash
423 ){
424 char *zName = mprintf("cert:%s", pUrlData->name);
425 db_set(zName, zHash, 1);
426 fossil_free(zName);
427 }
428
429 /*
430 ** Return true if the there exists a certificate exception for
431 ** pUrlData->name that matches the hash.
432 */
433 LOCAL int ssl_certificate_exception_exists(
434 UrlData *pUrlData,
435 const char *zHash
436 ){
437 char *zName, *zValue;
438 if( fossil_strcmp(sException.zHost,pUrlData->name)==0
439 && fossil_strcmp(sException.zHash,zHash)==0
440 ){
441 return 1;
442 }
443 zName = mprintf("cert:%s", pUrlData->name);
444 zValue = db_get(zName,0);
445 fossil_free(zName);
446 return zValue!=0 && strcmp(zHash,zValue)==0;
 
447 }
448
449 /*
450 ** Remember zHash as an acceptable certificate for this session only.
 
451 */
452 LOCAL void ssl_one_time_exception(
453 UrlData *pUrlData,
454 const char *zHash
455 ){
456 fossil_free(sException.zHost);
457 sException.zHost = fossil_strdup(pUrlData->name);
458 fossil_free(sException.zHash);
459 sException.zHash = fossil_strdup(zHash);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
460 }
461
462 /*
463 ** Send content out over the SSL connection.
464 */
@@ -498,5 +498,112 @@
498 }
499 return total;
500 }
501
502 #endif /* FOSSIL_ENABLE_SSL */
503
504 /*
505 ** COMMAND: tls-config*
506 **
507 ** Usage: %fossil tls-config [SUBCOMMAND] [OPTIONS...] [ARGS...]
508 **
509 ** This command is used to view or modify the TLS (Transport Layer
510 ** Security) configuration for Fossil. TLS (formerly SSL) is the
511 ** encryption technology used for secure HTTPS transport.
512 **
513 ** Sub-commands:
514 **
515 ** show Show the TLS configuration
516 **
517 ** remove-exception DOMAIN... Remove TLS cert exceptions
518 ** for the domains listed. Or if
519 ** the --all option is specified,
520 ** remove all TLS cert exceptions.
521 */
522 void test_tlsconfig_info(void){
523 const char *zCmd;
524 size_t nCmd;
525 int nHit = 0;
526 #if !defined(FOSSIL_ENABLE_SSL)
527 fossil_print("TLS disabled in this build\n");
528 #else
529 db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
530 db_open_config(1,0);
531 zCmd = g.argc>=3 ? g.argv[2] : "show";
532 nCmd = strlen(zCmd);
533 if( strncmp("show",zCmd,nCmd)==0 ){
534 const char *zName, *zValue;
535 size_t nName;
536 Stmt q;
537 fossil_print("OpenSSL-version: %s\n", SSLeay_version(SSLEAY_VERSION));
538 fossil_print("OpenSSL-cert-file: %s\n", X509_get_default_cert_file());
539 fossil_print("OpenSSL-cert-dir: %s\n", X509_get_default_cert_dir());
540 zName = X509_get_default_cert_file_env();
541 zValue = fossil_getenv(zName);
542 if( zValue==0 ) zValue = "";
543 nName = strlen(zName);
544 fossil_print("%s:%.*s%s\n", zName, 19-nName, "", zValue);
545 zName = X509_get_default_cert_dir_env();
546 zValue = fossil_getenv(zName);
547 if( zValue==0 ) zValue = "";
548 nName = strlen(zName);
549 fossil_print("%s:%.*s%s\n", zName, 19-nName, "", zValue);
550 nHit++;
551 fossil_print("ssl-ca-location: %s\n", db_get("ssl-ca-location",""));
552 fossil_print("ssl-identity: %s\n", db_get("ssl-identity",""));
553 db_prepare(&q,
554 "SELECT name FROM global_config"
555 " WHERE name GLOB 'cert:*'"
556 "UNION ALL "
557 "SELECT name FROM config"
558 " WHERE name GLOB 'cert:*'"
559 " ORDER BY name"
560 );
561 while( db_step(&q)==SQLITE_ROW ){
562 fossil_print("exception: %s\n", db_column_text(&q,0)+5);
563 }
564 db_finalize(&q);
565 }else
566 if( strncmp("remove-exception",zCmd,nCmd)==0 ){
567 int i;
568 Blob sql;
569 char *zSep = "(";
570 db_begin_transaction();
571 blob_init(&sql, 0, 0);
572 if( g.argc==4 && find_option("all",0,0)!=0 ){
573 blob_append_sql(&sql,
574 "DELETE FROM global_config WHERE name GLOB 'cert:*';\n"
575 "DELETE FROM global_config WHERE name GLOB 'trusted:*';\n"
576 "DELETE FROM config WHERE name GLOB 'cert:*';\n"
577 "DELETE FROM config WHERE name GLOB 'trusted:*';\n"
578 );
579 }else{
580 if( g.argc<4 ){
581 usage("remove-exception DOMAIN-NAME ...");
582 }
583 blob_append_sql(&sql,"DELETE FROM global_config WHERE name IN ");
584 for(i=3; i<g.argc; i++){
585 blob_append_sql(&sql,"%s'cert:%q','trust:%q'",
586 zSep/*safe-for-%s*/, g.argv[i], g.argv[i]);
587 zSep = ",";
588 }
589 blob_append_sql(&sql,");\n");
590 zSep = "(";
591 blob_append_sql(&sql,"DELETE FROM config WHERE name IN ");
592 for(i=3; i<g.argc; i++){
593 blob_append_sql(&sql,"%s'cert:%q','trusted:%q'",
594 zSep/*safe-for-%s*/, g.argv[i], g.argv[i]);
595 zSep = ",";
596 }
597 blob_append_sql(&sql,");");
598 }
599 db_exec_sql(blob_str(&sql));
600 db_commit_transaction();
601 blob_reset(&sql);
602 }else
603 /*default*/{
604 fossil_fatal("unknown sub-command \"%s\".\nshould be one of:"
605 " remove-exception show",
606 zCmd);
607 }
608 #endif
609 }
610
+220 -203
--- src/info.c
+++ src/info.c
@@ -167,11 +167,10 @@
167167
fossil_print("derived-from: %s %s\n", zParentCode,
168168
db_get("parent-project-name",""));
169169
}
170170
}
171171
172
-
173172
/*
174173
** COMMAND: info
175174
**
176175
** Usage: %fossil info ?VERSION | REPOSITORY_FILENAME? ?OPTIONS?
177176
**
@@ -215,33 +214,54 @@
215214
fossil_print("project-code: %s\n", db_get("project-code", "<none>"));
216215
showParentProject();
217216
extraRepoInfo();
218217
return;
219218
}
220
- db_find_and_open_repository(0,0);
219
+ db_find_and_open_repository(OPEN_OK_NOT_FOUND,0);
221220
verify_all_options();
222221
if( g.argc==2 ){
223222
int vid;
224
- /* 012345678901234 */
225
- db_record_repository_filename(0);
226
- fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>"));
223
+ if( g.repositoryOpen ){
224
+ db_record_repository_filename(0);
225
+ fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>"));
226
+ }else{
227
+ db_open_config(0,1);
228
+ }
227229
if( g.localOpen ){
228230
fossil_print("repository: %s\n", db_repository_filename());
229231
fossil_print("local-root: %s\n", g.zLocalRoot);
230232
}
231
- if( verboseFlag ) extraRepoInfo();
233
+ if( verboseFlag && g.repositoryOpen ){
234
+ extraRepoInfo();
235
+ }
232236
if( g.zConfigDbName ){
233237
fossil_print("config-db: %s\n", g.zConfigDbName);
234238
}
235
- fossil_print("project-code: %s\n", db_get("project-code", ""));
236
- showParentProject();
237
- vid = g.localOpen ? db_lget_int("checkout", 0) : 0;
238
- if( vid ){
239
- show_common_info(vid, "checkout:", 1, 1);
240
- }
241
- fossil_print("check-ins: %d\n",
239
+ if( g.repositoryOpen ){
240
+ fossil_print("project-code: %s\n", db_get("project-code", ""));
241
+ showParentProject();
242
+ vid = g.localOpen ? db_lget_int("checkout", 0) : 0;
243
+ if( vid ){
244
+ show_common_info(vid, "checkout:", 1, 1);
245
+ }
246
+ fossil_print("check-ins: %d\n",
242247
db_int(-1, "SELECT count(*) FROM event WHERE type='ci' /*scan*/"));
248
+ }
249
+ if( verboseFlag || !g.repositoryOpen ){
250
+ Blob vx;
251
+ char *z;
252
+ fossil_version_blob(&vx, 0);
253
+ z = strstr(blob_str(&vx), "version");
254
+ if( z ){
255
+ z += 8;
256
+ }else{
257
+ z = blob_str(&vx);
258
+ }
259
+ fossil_print("fossil: %z\n", file_fullexename(g.nameOfExe));
260
+ fossil_print("version: %s", z);
261
+ blob_reset(&vx);
262
+ }
243263
}else{
244264
int rid;
245265
rid = name_to_rid(g.argv[2]);
246266
if( rid==0 ){
247267
fossil_fatal("no such object: %s", g.argv[2]);
@@ -302,75 +322,10 @@
302322
|TIMELINE_CHPICK,
303323
0, 0, 0, rid, rid2, 0);
304324
db_finalize(&q);
305325
}
306326
307
-/*
308
-** Show a graph all wiki, tickets, and check-ins that refer to object zUuid.
309
-**
310
-** If zLabel is not NULL and the graph is not empty, then output zLabel as
311
-** a prefix to the graph.
312
-*/
313
-void render_backlink_graph(const char *zUuid, const char *zLabel){
314
- Blob sql;
315
- Stmt q;
316
- char *zGlob;
317
- zGlob = mprintf("%.5s*", zUuid);
318
- db_multi_exec(
319
- "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);"
320
- "DELETE FROM ok;"
321
- "INSERT OR IGNORE INTO ok"
322
- " SELECT srcid FROM backlink"
323
- " WHERE target GLOB %Q"
324
- " AND %Q GLOB (target || '*');",
325
- zGlob, zUuid
326
- );
327
- if( !db_exists("SELECT 1 FROM ok") ) return;
328
- if( zLabel ) cgi_printf("%s", zLabel);
329
- blob_zero(&sql);
330
- blob_append(&sql, timeline_query_for_www(), -1);
331
- blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
332
- db_prepare(&q, "%s", blob_sql_text(&sql));
333
- www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
334
- 0, 0, 0, 0, 0, 0);
335
- db_finalize(&q);
336
-}
337
-
338
-/*
339
-** WEBPAGE: test-backlinks
340
-**
341
-** Show a timeline of all check-ins and other events that have entries
342
-** in the backlink table. This is used for testing the rendering
343
-** of the "References" section of the /info page.
344
-*/
345
-void backlink_timeline_page(void){
346
- Blob sql;
347
- Stmt q;
348
-
349
- login_check_credentials();
350
- if( !g.perm.Read || !g.perm.RdTkt || !g.perm.RdWiki ){
351
- login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
352
- return;
353
- }
354
- style_header("Backlink Timeline (Internal Testing Use)");
355
- db_multi_exec(
356
- "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);"
357
- "DELETE FROM ok;"
358
- "INSERT OR IGNORE INTO ok"
359
- " SELECT blob.rid FROM backlink, blob"
360
- " WHERE blob.uuid BETWEEN backlink.target AND (backlink.target||'x')"
361
- );
362
- blob_zero(&sql);
363
- blob_append(&sql, timeline_query_for_www(), -1);
364
- blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
365
- db_prepare(&q, "%s", blob_sql_text(&sql));
366
- www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
367
- 0, 0, 0, 0, 0, 0);
368
- db_finalize(&q);
369
- style_footer();
370
-}
371
-
372327
373328
/*
374329
** Append the difference between artifacts to the output
375330
*/
376331
static void append_diff(
@@ -849,21 +804,23 @@
849804
@ (<a href="%R/rcvfrom?rcvid=%d(rcvid)">Rcvid %d(rcvid)</a>)</td></tr>
850805
}
851806
db_finalize(&q2);
852807
}
853808
854
- /* Only show links to read wiki pages if the users can read wiki
809
+ /* Only show links to edit wiki pages if the users can read wiki
855810
** and if the wiki pages already exist */
856
- if( g.perm.RdWiki
811
+ if( g.perm.WrWiki
812
+ && g.perm.RdWiki
813
+ && g.perm.Write
857814
&& ((okWiki = wiki_tagid2("checkin",zUuid))!=0 ||
858815
blob_size(&wiki_read_links)>0)
859816
&& db_get_boolean("wiki-about",1)
860817
){
861818
const char *zLinks = blob_str(&wiki_read_links);
862
- @ <tr><th>Wiki:</th><td>\
819
+ @ <tr><th>Edit&nbsp;Wiki:</th><td>\
863820
if( okWiki ){
864
- @ %z(href("%R/wiki?name=checkin/%s",zUuid))this checkin</a>\
821
+ @ %z(href("%R/wikiedit?name=checkin/%s",zUuid))this checkin</a>\
865822
}else if( zLinks[0] ){
866823
zLinks += 3;
867824
}
868825
@ %s(zLinks)</td></tr>
869826
}
@@ -1383,33 +1340,22 @@
13831340
13841341
/*
13851342
** Possible flags for the second parameter to
13861343
** object_description()
13871344
*/
1388
-#define OBJDESC_DETAIL 0x0001 /* more detail */
1345
+#define OBJDESC_DETAIL 0x0001 /* Show more detail */
13891346
#define OBJDESC_BASE 0x0002 /* Set <base> using this object */
13901347
#endif
13911348
13921349
/*
13931350
** Write a description of an object to the www reply.
1394
-**
1395
-** If the object is a file then mention:
1396
-**
1397
-** * It's artifact ID
1398
-** * All its filenames
1399
-** * The check-in it was part of, with times and users
1400
-**
1401
-** If the object is a manifest, then mention:
1402
-**
1403
-** * It's artifact ID
1404
-** * date of check-in
1405
-** * Comment & user
14061351
*/
14071352
int object_description(
1408
- int rid, /* The artifact ID */
1353
+ int rid, /* The artifact ID for the object to describe */
14091354
u32 objdescFlags, /* Flags to control display */
1410
- Blob *pDownloadName /* Fill with an appropriate download name */
1355
+ const char *zFileName, /* For file objects, use this name. Can be NULL */
1356
+ Blob *pDownloadName /* Fill with a good download name. Can be NULL */
14111357
){
14121358
Stmt q;
14131359
int cnt = 0;
14141360
int nWiki = 0;
14151361
int objType = 0;
@@ -1444,10 +1390,11 @@
14441390
const char *zVers = db_column_text(&q, 4);
14451391
int mPerm = db_column_int(&q, 5);
14461392
const char *zBr = db_column_text(&q, 6);
14471393
int szFile = db_column_int(&q,7);
14481394
int sameFilename = prevName!=0 && fossil_strcmp(zName,prevName)==0;
1395
+ if( zFileName && fossil_strcmp(zName,zFileName)!=0 ) continue;
14491396
if( sameFilename && !showDetail ){
14501397
if( cnt==1 ){
14511398
@ %z(href("%R/whatis/%!S",zUuid))[more...]</a>
14521399
}
14531400
cnt++;
@@ -1714,12 +1661,12 @@
17141661
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
17151662
cookie_link_parameter("diff","diff","2");
17161663
diffType = atoi(PD("diff","2"));
17171664
cookie_render();
17181665
if( P("from") && P("to") ){
1719
- v1 = artifact_from_ci_and_filename(0, "from");
1720
- v2 = artifact_from_ci_and_filename(0, "to");
1666
+ v1 = artifact_from_ci_and_filename("from");
1667
+ v2 = artifact_from_ci_and_filename("to");
17211668
}else{
17221669
Stmt q;
17231670
v1 = name_to_rid_www("v1");
17241671
v2 = name_to_rid_www("v2");
17251672
@@ -1790,13 +1737,13 @@
17901737
@ %z(href("%R/artifact/%!S",zV1))[%S(zV1)]</a> To
17911738
@ %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>.</h2>
17921739
}else{
17931740
@ <h2>Differences From
17941741
@ Artifact %z(href("%R/artifact/%!S",zV1))[%S(zV1)]</a>:</h2>
1795
- object_description(v1, objdescFlags, 0);
1742
+ object_description(v1, objdescFlags,0, 0);
17961743
@ <h2>To Artifact %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>:</h2>
1797
- object_description(v2, objdescFlags, 0);
1744
+ object_description(v2, objdescFlags,0, 0);
17981745
}
17991746
if( pRe ){
18001747
@ <b>Only differences that match regular expression "%h(zRe)"
18011748
@ are shown.</b>
18021749
}
@@ -1816,12 +1763,12 @@
18161763
*/
18171764
void rawartifact_page(void){
18181765
int rid = 0;
18191766
char *zUuid;
18201767
1821
- if( P("ci") && P("filename") ){
1822
- rid = artifact_from_ci_and_filename(0, 0);
1768
+ if( P("ci") ){
1769
+ rid = artifact_from_ci_and_filename(0);
18231770
}
18241771
if( rid==0 ){
18251772
rid = name_to_rid_www("name");
18261773
}
18271774
login_check_credentials();
@@ -1980,11 +1927,11 @@
19801927
}else{
19811928
@ :</h2>
19821929
}
19831930
blob_zero(&downloadName);
19841931
if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL;
1985
- object_description(rid, objdescFlags, &downloadName);
1932
+ object_description(rid, objdescFlags, 0, &downloadName);
19861933
style_submenu_element("Download", "%s/raw/%T?name=%s",
19871934
g.zTop, blob_str(&downloadName), zUuid);
19881935
@ <hr />
19891936
content_get(rid, &content);
19901937
@ <blockquote><pre>
@@ -1995,59 +1942,53 @@
19951942
19961943
/*
19971944
** Look for "ci" and "filename" query parameters. If found, try to
19981945
** use them to extract the record ID of an artifact for the file.
19991946
**
2000
-** Also look for "fn" as an alias for "filename". If either "filename"
2001
-** or "fn" is present but "ci" is missing, use "tip" as a default value
2002
-** for "ci".
2003
-**
2004
-** If zNameParam is not NULL, this use that parameter as the filename
2005
-** rather than "fn" or "filename".
2006
-**
2007
-** If pUrl is not NULL, then record the "ci" and "filename" values in
2008
-** pUrl.
2009
-**
2010
-** At least one of pUrl or zNameParam must be NULL.
1947
+** Also look for "fn" and "name" as an aliases for "filename". If any
1948
+** "filename" or "fn" or "name" are present but "ci" is missing, then
1949
+** use "tip" as the default value for "ci".
1950
+**
1951
+** If zNameParam is not NULL, then use that parameter as the filename
1952
+** rather than "fn" or "filename" or "name". the zNameParam is used
1953
+** for the from= and to= query parameters of /fdiff.
20111954
*/
2012
-int artifact_from_ci_and_filename(HQuery *pUrl, const char *zNameParam){
1955
+int artifact_from_ci_and_filename(const char *zNameParam){
20131956
const char *zFilename;
20141957
const char *zCI;
20151958
int cirid;
20161959
Manifest *pManifest;
20171960
ManifestFile *pFile;
1961
+ int rid = 0;
20181962
20191963
if( zNameParam ){
20201964
zFilename = P(zNameParam);
20211965
}else{
20221966
zFilename = P("filename");
20231967
if( zFilename==0 ){
20241968
zFilename = P("fn");
20251969
}
1970
+ if( zFilename==0 ){
1971
+ zFilename = P("name");
1972
+ }
20261973
}
20271974
if( zFilename==0 ) return 0;
20281975
2029
- zCI = P("ci");
2030
- cirid = name_to_typed_rid(zCI ? zCI : "tip", "ci");
1976
+ zCI = PD("ci", "tip");
1977
+ cirid = name_to_typed_rid(zCI, "ci");
20311978
if( cirid<=0 ) return 0;
20321979
pManifest = manifest_get(cirid, CFTYPE_MANIFEST, 0);
20331980
if( pManifest==0 ) return 0;
20341981
manifest_file_rewind(pManifest);
20351982
while( (pFile = manifest_file_next(pManifest,0))!=0 ){
20361983
if( fossil_strcmp(zFilename, pFile->zName)==0 ){
2037
- int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid);
2038
- manifest_destroy(pManifest);
2039
- if( pUrl ){
2040
- assert( zNameParam==0 );
2041
- url_add_parameter(pUrl, "fn", zFilename);
2042
- if( zCI ) url_add_parameter(pUrl, "ci", zCI);
2043
- }
2044
- return rid;
1984
+ rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid);
1985
+ break;
20451986
}
20461987
}
20471988
manifest_destroy(pManifest);
2048
- return 0;
1989
+ return rid;
20491990
}
20501991
20511992
/*
20521993
** The "z" argument is a string that contains the text of a source code
20531994
** file. This routine appends that text to the HTTP reply with line numbering.
@@ -2149,21 +2090,31 @@
21492090
** ln=N - highlight line number N
21502091
** ln=M-N - highlight lines M through N inclusive
21512092
** ln=M-N+Y-Z - highlight lines M through N and Y through Z (inclusive)
21522093
** verbose - show more detail in the description
21532094
** download - redirect to the download (artifact page only)
2154
-** name=SHA1HASH - Provide the SHA1HASH as a query parameter
2155
-** filename=NAME - Show information for content file NAME
2156
-** fn=NAME - "fn" is shorthand for "filename"
2157
-** ci=VERSION - The specific check-in to use for "filename=".
2095
+** name=NAME - filename or hash as a query parameter
2096
+** filename=NAME - alternative spelling for "name="
2097
+** fn=NAME - alternative spelling for "name="
2098
+** ci=VERSION - The specific check-in to use with "name=" to
2099
+** identify the file.
21582100
**
21592101
** The /artifact page show the complete content of a file
2160
-** identified by HASH as preformatted text. The
2161
-** /whatis page shows only a description of the file. The /file
2162
-** page shows the most recent version of the file or directory
2163
-** called NAME, or a list of the top-level directory if NAME is
2164
-** omitted.
2102
+** identified by HASH. The /whatis page shows only a description
2103
+** of how the artifact is used. The /file page shows the most recent
2104
+** version of the file or directory called NAME, or a list of the
2105
+** top-level directory if NAME is omitted.
2106
+**
2107
+** For /artifact and /whatis, the name= query parameter can refer to
2108
+** either the name of a file, or an artifact hash. If the ci= query
2109
+** parameter is also present, then name= must refer to a file name.
2110
+** If ci= is omitted, then the hash interpretation is preferred but
2111
+** if name= cannot be understood as a hash, a default "tip" value is
2112
+** used for ci=.
2113
+**
2114
+** For /file, name= can only be interpreted as a filename. As before,
2115
+** a default value of "tip" is used for ci= if ci= is omitted.
21652116
*/
21662117
void artifact_page(void){
21672118
int rid = 0;
21682119
Blob content;
21692120
const char *zMime;
@@ -2170,95 +2121,146 @@
21702121
Blob downloadName;
21712122
int renderAsWiki = 0;
21722123
int renderAsHtml = 0;
21732124
int objType;
21742125
int asText;
2175
- const char *zUuid;
2126
+ const char *zUuid = 0;
21762127
u32 objdescFlags = OBJDESC_BASE;
21772128
int descOnly = fossil_strcmp(g.zPath,"whatis")==0;
21782129
int isFile = fossil_strcmp(g.zPath,"file")==0;
21792130
const char *zLn = P("ln");
21802131
const char *zName = P("name");
2181
- HQuery url;
2182
-
2183
- url_initialize(&url, g.zPath);
2184
- rid = artifact_from_ci_and_filename(&url, 0);
2185
- if( rid==0 ){
2186
- url_add_parameter(&url, "name", zName);
2187
- if( isFile ){
2188
- /* Do a top-level directory listing in /file mode if no argument
2189
- ** specified */
2190
- if( zName==0 || zName[0]==0 ){
2191
- if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2192
- page_tree();
2193
- return;
2194
- }
2195
- /* Look for a single file with the given name */
2196
- rid = db_int(0,
2197
- "SELECT fid FROM filename, mlink, event"
2198
- " WHERE name=%Q"
2199
- " AND mlink.fnid=filename.fnid"
2200
- " AND event.objid=mlink.mid"
2201
- " ORDER BY event.mtime DESC LIMIT 1",
2202
- zName
2203
- );
2204
- /* If no file called NAME exists, instead look for a directory
2205
- ** with that name, and do a directory listing */
2206
- if( rid==0 ){
2207
- int nName = (int)strlen(zName);
2208
- if( nName && zName[nName-1]=='/' ) nName--;
2209
- if( db_exists(
2210
- "SELECT 1 FROM filename"
2211
- " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';",
2212
- nName, zName, nName+1, nName, zName
2213
- ) ){
2214
- if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2215
- page_tree();
2216
- return;
2217
- }
2218
- }
2219
- /* If no file or directory called NAME: issue an error */
2220
- if( rid==0 ){
2221
- style_header("No such file");
2222
- @ File '%h(zName)' does not exist in this repository.
2223
- style_footer();
2224
- return;
2225
- }
2226
- }else{
2227
- rid = name_to_rid_www("name");
2228
- }
2229
- }
2230
-
2231
- login_check_credentials();
2232
- if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
2233
- if( rid==0 ){
2234
- style_header("No such artifact");
2235
- @ Artifact '%h(zName)' does not exist in this repository.
2236
- style_footer();
2237
- return;
2238
- }
2132
+ const char *zCI = P("ci");
2133
+ HQuery url;
2134
+ char *zCIUuid = 0;
2135
+ int isSymbolicCI = 0; /* ci= exists and is a symbolic name, not a hash */
2136
+ int isBranchCI = 0; /* ci= refers to a branch name */
2137
+ char *zHeader = 0;
2138
+
2139
+ login_check_credentials();
2140
+ if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
2141
+
2142
+ /* Capture and normalize the name= and ci= query parameters */
2143
+ if( zName==0 ){
2144
+ zName = P("filename");
2145
+ if( zName==0 ){
2146
+ zName = P("fn");
2147
+ }
2148
+ }
2149
+ if( zCI && strlen(zCI)==0 ){ zCI = 0; }
2150
+ if( zCI
2151
+ && name_to_uuid2(zCI, "ci", &zCIUuid)
2152
+ && sqlite3_strnicmp(zCIUuid, zCI, strlen(zCI))!=0
2153
+ ){
2154
+ isSymbolicCI = 1;
2155
+ isBranchCI = branch_includes_uuid(zCI, zCIUuid);
2156
+ }
2157
+
2158
+ /* The name= query parameter (or at least one of its alternative
2159
+ ** spellings) is required. Except for /file, show a top-level
2160
+ ** directory listing if name= is omitted.
2161
+ */
2162
+ if( zName==0 ){
2163
+ if( isFile ){
2164
+ if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2165
+ page_tree();
2166
+ return;
2167
+ }
2168
+ style_header("Missing name= query parameter");
2169
+ @ The name= query parameter is missing
2170
+ style_footer();
2171
+ return;
2172
+ }
2173
+
2174
+ url_initialize(&url, g.zPath);
2175
+ url_add_parameter(&url, "name", zName);
2176
+ url_add_parameter(&url, "ci", zCI);
2177
+
2178
+ if( zCI==0 && !isFile ){
2179
+ /* If there is no ci= query parameter, then prefer to interpret
2180
+ ** name= as a hash for /artifact and /whatis. But for not for /file.
2181
+ ** For /file, a name= without a ci= while prefer to use the default
2182
+ ** "tip" value for ci=. */
2183
+ rid = name_to_rid(zName);
2184
+ }
2185
+ if( rid==0 ){
2186
+ rid = artifact_from_ci_and_filename(0);
2187
+ }
2188
+
2189
+ if( rid==0 ){ /* Artifact not found */
2190
+ if( isFile ){
2191
+ /* For /file, also check to see if name= refers to a directory,
2192
+ ** and if so, do a listing for that directory */
2193
+ int nName = (int)strlen(zName);
2194
+ if( nName && zName[nName-1]=='/' ) nName--;
2195
+ if( db_exists(
2196
+ "SELECT 1 FROM filename"
2197
+ " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';",
2198
+ nName, zName, nName+1, nName, zName
2199
+ ) ){
2200
+ if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2201
+ page_tree();
2202
+ return;
2203
+ }
2204
+ style_header("No such file");
2205
+ @ File '%h(zName)' does not exist in this repository.
2206
+ }else{
2207
+ style_header("No such artifact");
2208
+ @ Artifact '%h(zName)' does not exist in this repository.
2209
+ }
2210
+ style_footer();
2211
+ return;
2212
+ }
2213
+
22392214
if( descOnly || P("verbose")!=0 ){
22402215
url_add_parameter(&url, "verbose", "1");
22412216
objdescFlags |= OBJDESC_DETAIL;
22422217
}
22432218
zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
2219
+
2220
+ asText = P("txt")!=0;
22442221
if( isFile ){
2245
- @ <h2>Latest version of file '%h(zName)':</h2>
2222
+ if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){
2223
+ zCI = "tip";
2224
+ @ <h2>File %z(href("%R/finfo?name=%T&m=tip",zName))%h(zName)</a>
2225
+ @ from the %z(href("%R/info/tip"))latest check-in</a></h2>
2226
+ }else{
2227
+ const char *zPath;
2228
+ Blob path;
2229
+ blob_zero(&path);
2230
+ hyperlinked_path(zName, &path, zCI, "dir", "", LINKPATH_FINFO);
2231
+ zPath = blob_str(&path);
2232
+ @ <h2>File %s(zPath) \
2233
+ if( isBranchCI ){
2234
+ @ on branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a></h2>
2235
+ }else if( isSymbolicCI ){
2236
+ @ as of check-in %z(href("%R/info/%!S",zCIUuid))%s(zCI)</a></h2>
2237
+ }else{
2238
+ @ as of check-in [%z(href("%R/info/%!S",zCIUuid))%S(zCIUuid)</a>]</h2>
2239
+ }
2240
+ blob_reset(&path);
2241
+ }
22462242
style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
2243
+ style_submenu_element("Annotate", "%R/annotate?filename=%T&checkin=%T",
2244
+ zName, zCI);
2245
+ style_submenu_element("Blame", "%R/blame?filename=%T&checkin=%T",
2246
+ zName, zCI);
2247
+ blob_init(&downloadName, zName, -1);
2248
+ objType = OBJTYPE_CONTENT;
22472249
}else{
22482250
@ <h2>Artifact
22492251
style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);
22502252
if( g.perm.Setup ){
22512253
@ (%d(rid)):</h2>
22522254
}else{
22532255
@ :</h2>
22542256
}
2257
+ blob_zero(&downloadName);
2258
+ if( asText ) objdescFlags &= ~OBJDESC_BASE;
2259
+ objType = object_description(rid, objdescFlags,
2260
+ (isFile?zName:0), &downloadName);
22552261
}
2256
- blob_zero(&downloadName);
2257
- asText = P("txt")!=0;
2258
- if( asText ) objdescFlags &= ~OBJDESC_BASE;
2259
- objType = object_description(rid, objdescFlags, &downloadName);
22602262
if( !descOnly && P("download")!=0 ){
22612263
cgi_redirectf("%R/raw/%T?name=%s", blob_str(&downloadName),
22622264
db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid));
22632265
/*NOTREACHED*/
22642266
}
@@ -2269,13 +2271,28 @@
22692271
g.zTop, zUuid);
22702272
}else{
22712273
style_submenu_element("Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid);
22722274
}
22732275
}
2274
- style_header("%s", isFile ? "File Content" :
2275
- descOnly ? "Artifact Description" : "Artifact Content");
2276
- if( g.perm.Admin ){
2276
+
2277
+ if( isFile ){
2278
+ if( isSymbolicCI ){
2279
+ zHeader = mprintf("%s at %s", file_tail(zName), zCI);
2280
+ }else if( zCI ){
2281
+ zHeader = mprintf("%s at [%S]", file_tail(zName), zCIUuid);
2282
+ }else{
2283
+ zHeader = mprintf("%s", file_tail(zName));
2284
+ }
2285
+ }else if( descOnly ){
2286
+ zHeader = mprintf("Artifact Description [%S]", zUuid);
2287
+ }else{
2288
+ zHeader = mprintf("Artifact [%S]", zUuid);
2289
+ }
2290
+ style_header("%s", zHeader);
2291
+ fossil_free(zCIUuid);
2292
+ fossil_free(zHeader);
2293
+ if( !isFile && g.perm.Admin ){
22772294
Stmt q;
22782295
db_prepare(&q,
22792296
"SELECT coalesce(user.login,rcvfrom.uid),"
22802297
" datetime(rcvfrom.mtime,toLocal()), rcvfrom.ipaddr"
22812298
" FROM blob, rcvfrom LEFT JOIN user ON user.uid=rcvfrom.uid"
22822299
--- src/info.c
+++ src/info.c
@@ -167,11 +167,10 @@
167 fossil_print("derived-from: %s %s\n", zParentCode,
168 db_get("parent-project-name",""));
169 }
170 }
171
172
173 /*
174 ** COMMAND: info
175 **
176 ** Usage: %fossil info ?VERSION | REPOSITORY_FILENAME? ?OPTIONS?
177 **
@@ -215,33 +214,54 @@
215 fossil_print("project-code: %s\n", db_get("project-code", "<none>"));
216 showParentProject();
217 extraRepoInfo();
218 return;
219 }
220 db_find_and_open_repository(0,0);
221 verify_all_options();
222 if( g.argc==2 ){
223 int vid;
224 /* 012345678901234 */
225 db_record_repository_filename(0);
226 fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>"));
 
 
 
227 if( g.localOpen ){
228 fossil_print("repository: %s\n", db_repository_filename());
229 fossil_print("local-root: %s\n", g.zLocalRoot);
230 }
231 if( verboseFlag ) extraRepoInfo();
 
 
232 if( g.zConfigDbName ){
233 fossil_print("config-db: %s\n", g.zConfigDbName);
234 }
235 fossil_print("project-code: %s\n", db_get("project-code", ""));
236 showParentProject();
237 vid = g.localOpen ? db_lget_int("checkout", 0) : 0;
238 if( vid ){
239 show_common_info(vid, "checkout:", 1, 1);
240 }
241 fossil_print("check-ins: %d\n",
 
242 db_int(-1, "SELECT count(*) FROM event WHERE type='ci' /*scan*/"));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243 }else{
244 int rid;
245 rid = name_to_rid(g.argv[2]);
246 if( rid==0 ){
247 fossil_fatal("no such object: %s", g.argv[2]);
@@ -302,75 +322,10 @@
302 |TIMELINE_CHPICK,
303 0, 0, 0, rid, rid2, 0);
304 db_finalize(&q);
305 }
306
307 /*
308 ** Show a graph all wiki, tickets, and check-ins that refer to object zUuid.
309 **
310 ** If zLabel is not NULL and the graph is not empty, then output zLabel as
311 ** a prefix to the graph.
312 */
313 void render_backlink_graph(const char *zUuid, const char *zLabel){
314 Blob sql;
315 Stmt q;
316 char *zGlob;
317 zGlob = mprintf("%.5s*", zUuid);
318 db_multi_exec(
319 "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);"
320 "DELETE FROM ok;"
321 "INSERT OR IGNORE INTO ok"
322 " SELECT srcid FROM backlink"
323 " WHERE target GLOB %Q"
324 " AND %Q GLOB (target || '*');",
325 zGlob, zUuid
326 );
327 if( !db_exists("SELECT 1 FROM ok") ) return;
328 if( zLabel ) cgi_printf("%s", zLabel);
329 blob_zero(&sql);
330 blob_append(&sql, timeline_query_for_www(), -1);
331 blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
332 db_prepare(&q, "%s", blob_sql_text(&sql));
333 www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
334 0, 0, 0, 0, 0, 0);
335 db_finalize(&q);
336 }
337
338 /*
339 ** WEBPAGE: test-backlinks
340 **
341 ** Show a timeline of all check-ins and other events that have entries
342 ** in the backlink table. This is used for testing the rendering
343 ** of the "References" section of the /info page.
344 */
345 void backlink_timeline_page(void){
346 Blob sql;
347 Stmt q;
348
349 login_check_credentials();
350 if( !g.perm.Read || !g.perm.RdTkt || !g.perm.RdWiki ){
351 login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
352 return;
353 }
354 style_header("Backlink Timeline (Internal Testing Use)");
355 db_multi_exec(
356 "CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);"
357 "DELETE FROM ok;"
358 "INSERT OR IGNORE INTO ok"
359 " SELECT blob.rid FROM backlink, blob"
360 " WHERE blob.uuid BETWEEN backlink.target AND (backlink.target||'x')"
361 );
362 blob_zero(&sql);
363 blob_append(&sql, timeline_query_for_www(), -1);
364 blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
365 db_prepare(&q, "%s", blob_sql_text(&sql));
366 www_print_timeline(&q, TIMELINE_DISJOINT|TIMELINE_GRAPH|TIMELINE_NOSCROLL,
367 0, 0, 0, 0, 0, 0);
368 db_finalize(&q);
369 style_footer();
370 }
371
372
373 /*
374 ** Append the difference between artifacts to the output
375 */
376 static void append_diff(
@@ -849,21 +804,23 @@
849 @ (<a href="%R/rcvfrom?rcvid=%d(rcvid)">Rcvid %d(rcvid)</a>)</td></tr>
850 }
851 db_finalize(&q2);
852 }
853
854 /* Only show links to read wiki pages if the users can read wiki
855 ** and if the wiki pages already exist */
856 if( g.perm.RdWiki
 
 
857 && ((okWiki = wiki_tagid2("checkin",zUuid))!=0 ||
858 blob_size(&wiki_read_links)>0)
859 && db_get_boolean("wiki-about",1)
860 ){
861 const char *zLinks = blob_str(&wiki_read_links);
862 @ <tr><th>Wiki:</th><td>\
863 if( okWiki ){
864 @ %z(href("%R/wiki?name=checkin/%s",zUuid))this checkin</a>\
865 }else if( zLinks[0] ){
866 zLinks += 3;
867 }
868 @ %s(zLinks)</td></tr>
869 }
@@ -1383,33 +1340,22 @@
1383
1384 /*
1385 ** Possible flags for the second parameter to
1386 ** object_description()
1387 */
1388 #define OBJDESC_DETAIL 0x0001 /* more detail */
1389 #define OBJDESC_BASE 0x0002 /* Set <base> using this object */
1390 #endif
1391
1392 /*
1393 ** Write a description of an object to the www reply.
1394 **
1395 ** If the object is a file then mention:
1396 **
1397 ** * It's artifact ID
1398 ** * All its filenames
1399 ** * The check-in it was part of, with times and users
1400 **
1401 ** If the object is a manifest, then mention:
1402 **
1403 ** * It's artifact ID
1404 ** * date of check-in
1405 ** * Comment & user
1406 */
1407 int object_description(
1408 int rid, /* The artifact ID */
1409 u32 objdescFlags, /* Flags to control display */
1410 Blob *pDownloadName /* Fill with an appropriate download name */
 
1411 ){
1412 Stmt q;
1413 int cnt = 0;
1414 int nWiki = 0;
1415 int objType = 0;
@@ -1444,10 +1390,11 @@
1444 const char *zVers = db_column_text(&q, 4);
1445 int mPerm = db_column_int(&q, 5);
1446 const char *zBr = db_column_text(&q, 6);
1447 int szFile = db_column_int(&q,7);
1448 int sameFilename = prevName!=0 && fossil_strcmp(zName,prevName)==0;
 
1449 if( sameFilename && !showDetail ){
1450 if( cnt==1 ){
1451 @ %z(href("%R/whatis/%!S",zUuid))[more...]</a>
1452 }
1453 cnt++;
@@ -1714,12 +1661,12 @@
1714 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1715 cookie_link_parameter("diff","diff","2");
1716 diffType = atoi(PD("diff","2"));
1717 cookie_render();
1718 if( P("from") && P("to") ){
1719 v1 = artifact_from_ci_and_filename(0, "from");
1720 v2 = artifact_from_ci_and_filename(0, "to");
1721 }else{
1722 Stmt q;
1723 v1 = name_to_rid_www("v1");
1724 v2 = name_to_rid_www("v2");
1725
@@ -1790,13 +1737,13 @@
1790 @ %z(href("%R/artifact/%!S",zV1))[%S(zV1)]</a> To
1791 @ %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>.</h2>
1792 }else{
1793 @ <h2>Differences From
1794 @ Artifact %z(href("%R/artifact/%!S",zV1))[%S(zV1)]</a>:</h2>
1795 object_description(v1, objdescFlags, 0);
1796 @ <h2>To Artifact %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>:</h2>
1797 object_description(v2, objdescFlags, 0);
1798 }
1799 if( pRe ){
1800 @ <b>Only differences that match regular expression "%h(zRe)"
1801 @ are shown.</b>
1802 }
@@ -1816,12 +1763,12 @@
1816 */
1817 void rawartifact_page(void){
1818 int rid = 0;
1819 char *zUuid;
1820
1821 if( P("ci") && P("filename") ){
1822 rid = artifact_from_ci_and_filename(0, 0);
1823 }
1824 if( rid==0 ){
1825 rid = name_to_rid_www("name");
1826 }
1827 login_check_credentials();
@@ -1980,11 +1927,11 @@
1980 }else{
1981 @ :</h2>
1982 }
1983 blob_zero(&downloadName);
1984 if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL;
1985 object_description(rid, objdescFlags, &downloadName);
1986 style_submenu_element("Download", "%s/raw/%T?name=%s",
1987 g.zTop, blob_str(&downloadName), zUuid);
1988 @ <hr />
1989 content_get(rid, &content);
1990 @ <blockquote><pre>
@@ -1995,59 +1942,53 @@
1995
1996 /*
1997 ** Look for "ci" and "filename" query parameters. If found, try to
1998 ** use them to extract the record ID of an artifact for the file.
1999 **
2000 ** Also look for "fn" as an alias for "filename". If either "filename"
2001 ** or "fn" is present but "ci" is missing, use "tip" as a default value
2002 ** for "ci".
2003 **
2004 ** If zNameParam is not NULL, this use that parameter as the filename
2005 ** rather than "fn" or "filename".
2006 **
2007 ** If pUrl is not NULL, then record the "ci" and "filename" values in
2008 ** pUrl.
2009 **
2010 ** At least one of pUrl or zNameParam must be NULL.
2011 */
2012 int artifact_from_ci_and_filename(HQuery *pUrl, const char *zNameParam){
2013 const char *zFilename;
2014 const char *zCI;
2015 int cirid;
2016 Manifest *pManifest;
2017 ManifestFile *pFile;
 
2018
2019 if( zNameParam ){
2020 zFilename = P(zNameParam);
2021 }else{
2022 zFilename = P("filename");
2023 if( zFilename==0 ){
2024 zFilename = P("fn");
2025 }
 
 
 
2026 }
2027 if( zFilename==0 ) return 0;
2028
2029 zCI = P("ci");
2030 cirid = name_to_typed_rid(zCI ? zCI : "tip", "ci");
2031 if( cirid<=0 ) return 0;
2032 pManifest = manifest_get(cirid, CFTYPE_MANIFEST, 0);
2033 if( pManifest==0 ) return 0;
2034 manifest_file_rewind(pManifest);
2035 while( (pFile = manifest_file_next(pManifest,0))!=0 ){
2036 if( fossil_strcmp(zFilename, pFile->zName)==0 ){
2037 int rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid);
2038 manifest_destroy(pManifest);
2039 if( pUrl ){
2040 assert( zNameParam==0 );
2041 url_add_parameter(pUrl, "fn", zFilename);
2042 if( zCI ) url_add_parameter(pUrl, "ci", zCI);
2043 }
2044 return rid;
2045 }
2046 }
2047 manifest_destroy(pManifest);
2048 return 0;
2049 }
2050
2051 /*
2052 ** The "z" argument is a string that contains the text of a source code
2053 ** file. This routine appends that text to the HTTP reply with line numbering.
@@ -2149,21 +2090,31 @@
2149 ** ln=N - highlight line number N
2150 ** ln=M-N - highlight lines M through N inclusive
2151 ** ln=M-N+Y-Z - highlight lines M through N and Y through Z (inclusive)
2152 ** verbose - show more detail in the description
2153 ** download - redirect to the download (artifact page only)
2154 ** name=SHA1HASH - Provide the SHA1HASH as a query parameter
2155 ** filename=NAME - Show information for content file NAME
2156 ** fn=NAME - "fn" is shorthand for "filename"
2157 ** ci=VERSION - The specific check-in to use for "filename=".
 
2158 **
2159 ** The /artifact page show the complete content of a file
2160 ** identified by HASH as preformatted text. The
2161 ** /whatis page shows only a description of the file. The /file
2162 ** page shows the most recent version of the file or directory
2163 ** called NAME, or a list of the top-level directory if NAME is
2164 ** omitted.
 
 
 
 
 
 
 
 
 
2165 */
2166 void artifact_page(void){
2167 int rid = 0;
2168 Blob content;
2169 const char *zMime;
@@ -2170,95 +2121,146 @@
2170 Blob downloadName;
2171 int renderAsWiki = 0;
2172 int renderAsHtml = 0;
2173 int objType;
2174 int asText;
2175 const char *zUuid;
2176 u32 objdescFlags = OBJDESC_BASE;
2177 int descOnly = fossil_strcmp(g.zPath,"whatis")==0;
2178 int isFile = fossil_strcmp(g.zPath,"file")==0;
2179 const char *zLn = P("ln");
2180 const char *zName = P("name");
2181 HQuery url;
2182
2183 url_initialize(&url, g.zPath);
2184 rid = artifact_from_ci_and_filename(&url, 0);
2185 if( rid==0 ){
2186 url_add_parameter(&url, "name", zName);
2187 if( isFile ){
2188 /* Do a top-level directory listing in /file mode if no argument
2189 ** specified */
2190 if( zName==0 || zName[0]==0 ){
2191 if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2192 page_tree();
2193 return;
2194 }
2195 /* Look for a single file with the given name */
2196 rid = db_int(0,
2197 "SELECT fid FROM filename, mlink, event"
2198 " WHERE name=%Q"
2199 " AND mlink.fnid=filename.fnid"
2200 " AND event.objid=mlink.mid"
2201 " ORDER BY event.mtime DESC LIMIT 1",
2202 zName
2203 );
2204 /* If no file called NAME exists, instead look for a directory
2205 ** with that name, and do a directory listing */
2206 if( rid==0 ){
2207 int nName = (int)strlen(zName);
2208 if( nName && zName[nName-1]=='/' ) nName--;
2209 if( db_exists(
2210 "SELECT 1 FROM filename"
2211 " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';",
2212 nName, zName, nName+1, nName, zName
2213 ) ){
2214 if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2215 page_tree();
2216 return;
2217 }
2218 }
2219 /* If no file or directory called NAME: issue an error */
2220 if( rid==0 ){
2221 style_header("No such file");
2222 @ File '%h(zName)' does not exist in this repository.
2223 style_footer();
2224 return;
2225 }
2226 }else{
2227 rid = name_to_rid_www("name");
2228 }
2229 }
2230
2231 login_check_credentials();
2232 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
2233 if( rid==0 ){
2234 style_header("No such artifact");
2235 @ Artifact '%h(zName)' does not exist in this repository.
2236 style_footer();
2237 return;
2238 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2239 if( descOnly || P("verbose")!=0 ){
2240 url_add_parameter(&url, "verbose", "1");
2241 objdescFlags |= OBJDESC_DETAIL;
2242 }
2243 zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
 
 
2244 if( isFile ){
2245 @ <h2>Latest version of file '%h(zName)':</h2>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2246 style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
 
 
 
 
 
 
2247 }else{
2248 @ <h2>Artifact
2249 style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);
2250 if( g.perm.Setup ){
2251 @ (%d(rid)):</h2>
2252 }else{
2253 @ :</h2>
2254 }
 
 
 
 
2255 }
2256 blob_zero(&downloadName);
2257 asText = P("txt")!=0;
2258 if( asText ) objdescFlags &= ~OBJDESC_BASE;
2259 objType = object_description(rid, objdescFlags, &downloadName);
2260 if( !descOnly && P("download")!=0 ){
2261 cgi_redirectf("%R/raw/%T?name=%s", blob_str(&downloadName),
2262 db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid));
2263 /*NOTREACHED*/
2264 }
@@ -2269,13 +2271,28 @@
2269 g.zTop, zUuid);
2270 }else{
2271 style_submenu_element("Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid);
2272 }
2273 }
2274 style_header("%s", isFile ? "File Content" :
2275 descOnly ? "Artifact Description" : "Artifact Content");
2276 if( g.perm.Admin ){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2277 Stmt q;
2278 db_prepare(&q,
2279 "SELECT coalesce(user.login,rcvfrom.uid),"
2280 " datetime(rcvfrom.mtime,toLocal()), rcvfrom.ipaddr"
2281 " FROM blob, rcvfrom LEFT JOIN user ON user.uid=rcvfrom.uid"
2282
--- src/info.c
+++ src/info.c
@@ -167,11 +167,10 @@
167 fossil_print("derived-from: %s %s\n", zParentCode,
168 db_get("parent-project-name",""));
169 }
170 }
171
 
172 /*
173 ** COMMAND: info
174 **
175 ** Usage: %fossil info ?VERSION | REPOSITORY_FILENAME? ?OPTIONS?
176 **
@@ -215,33 +214,54 @@
214 fossil_print("project-code: %s\n", db_get("project-code", "<none>"));
215 showParentProject();
216 extraRepoInfo();
217 return;
218 }
219 db_find_and_open_repository(OPEN_OK_NOT_FOUND,0);
220 verify_all_options();
221 if( g.argc==2 ){
222 int vid;
223 if( g.repositoryOpen ){
224 db_record_repository_filename(0);
225 fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>"));
226 }else{
227 db_open_config(0,1);
228 }
229 if( g.localOpen ){
230 fossil_print("repository: %s\n", db_repository_filename());
231 fossil_print("local-root: %s\n", g.zLocalRoot);
232 }
233 if( verboseFlag && g.repositoryOpen ){
234 extraRepoInfo();
235 }
236 if( g.zConfigDbName ){
237 fossil_print("config-db: %s\n", g.zConfigDbName);
238 }
239 if( g.repositoryOpen ){
240 fossil_print("project-code: %s\n", db_get("project-code", ""));
241 showParentProject();
242 vid = g.localOpen ? db_lget_int("checkout", 0) : 0;
243 if( vid ){
244 show_common_info(vid, "checkout:", 1, 1);
245 }
246 fossil_print("check-ins: %d\n",
247 db_int(-1, "SELECT count(*) FROM event WHERE type='ci' /*scan*/"));
248 }
249 if( verboseFlag || !g.repositoryOpen ){
250 Blob vx;
251 char *z;
252 fossil_version_blob(&vx, 0);
253 z = strstr(blob_str(&vx), "version");
254 if( z ){
255 z += 8;
256 }else{
257 z = blob_str(&vx);
258 }
259 fossil_print("fossil: %z\n", file_fullexename(g.nameOfExe));
260 fossil_print("version: %s", z);
261 blob_reset(&vx);
262 }
263 }else{
264 int rid;
265 rid = name_to_rid(g.argv[2]);
266 if( rid==0 ){
267 fossil_fatal("no such object: %s", g.argv[2]);
@@ -302,75 +322,10 @@
322 |TIMELINE_CHPICK,
323 0, 0, 0, rid, rid2, 0);
324 db_finalize(&q);
325 }
326
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
327
328 /*
329 ** Append the difference between artifacts to the output
330 */
331 static void append_diff(
@@ -849,21 +804,23 @@
804 @ (<a href="%R/rcvfrom?rcvid=%d(rcvid)">Rcvid %d(rcvid)</a>)</td></tr>
805 }
806 db_finalize(&q2);
807 }
808
809 /* Only show links to edit wiki pages if the users can read wiki
810 ** and if the wiki pages already exist */
811 if( g.perm.WrWiki
812 && g.perm.RdWiki
813 && g.perm.Write
814 && ((okWiki = wiki_tagid2("checkin",zUuid))!=0 ||
815 blob_size(&wiki_read_links)>0)
816 && db_get_boolean("wiki-about",1)
817 ){
818 const char *zLinks = blob_str(&wiki_read_links);
819 @ <tr><th>Edit&nbsp;Wiki:</th><td>\
820 if( okWiki ){
821 @ %z(href("%R/wikiedit?name=checkin/%s",zUuid))this checkin</a>\
822 }else if( zLinks[0] ){
823 zLinks += 3;
824 }
825 @ %s(zLinks)</td></tr>
826 }
@@ -1383,33 +1340,22 @@
1340
1341 /*
1342 ** Possible flags for the second parameter to
1343 ** object_description()
1344 */
1345 #define OBJDESC_DETAIL 0x0001 /* Show more detail */
1346 #define OBJDESC_BASE 0x0002 /* Set <base> using this object */
1347 #endif
1348
1349 /*
1350 ** Write a description of an object to the www reply.
 
 
 
 
 
 
 
 
 
 
 
 
1351 */
1352 int object_description(
1353 int rid, /* The artifact ID for the object to describe */
1354 u32 objdescFlags, /* Flags to control display */
1355 const char *zFileName, /* For file objects, use this name. Can be NULL */
1356 Blob *pDownloadName /* Fill with a good download name. Can be NULL */
1357 ){
1358 Stmt q;
1359 int cnt = 0;
1360 int nWiki = 0;
1361 int objType = 0;
@@ -1444,10 +1390,11 @@
1390 const char *zVers = db_column_text(&q, 4);
1391 int mPerm = db_column_int(&q, 5);
1392 const char *zBr = db_column_text(&q, 6);
1393 int szFile = db_column_int(&q,7);
1394 int sameFilename = prevName!=0 && fossil_strcmp(zName,prevName)==0;
1395 if( zFileName && fossil_strcmp(zName,zFileName)!=0 ) continue;
1396 if( sameFilename && !showDetail ){
1397 if( cnt==1 ){
1398 @ %z(href("%R/whatis/%!S",zUuid))[more...]</a>
1399 }
1400 cnt++;
@@ -1714,12 +1661,12 @@
1661 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1662 cookie_link_parameter("diff","diff","2");
1663 diffType = atoi(PD("diff","2"));
1664 cookie_render();
1665 if( P("from") && P("to") ){
1666 v1 = artifact_from_ci_and_filename("from");
1667 v2 = artifact_from_ci_and_filename("to");
1668 }else{
1669 Stmt q;
1670 v1 = name_to_rid_www("v1");
1671 v2 = name_to_rid_www("v2");
1672
@@ -1790,13 +1737,13 @@
1737 @ %z(href("%R/artifact/%!S",zV1))[%S(zV1)]</a> To
1738 @ %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>.</h2>
1739 }else{
1740 @ <h2>Differences From
1741 @ Artifact %z(href("%R/artifact/%!S",zV1))[%S(zV1)]</a>:</h2>
1742 object_description(v1, objdescFlags,0, 0);
1743 @ <h2>To Artifact %z(href("%R/artifact/%!S",zV2))[%S(zV2)]</a>:</h2>
1744 object_description(v2, objdescFlags,0, 0);
1745 }
1746 if( pRe ){
1747 @ <b>Only differences that match regular expression "%h(zRe)"
1748 @ are shown.</b>
1749 }
@@ -1816,12 +1763,12 @@
1763 */
1764 void rawartifact_page(void){
1765 int rid = 0;
1766 char *zUuid;
1767
1768 if( P("ci") ){
1769 rid = artifact_from_ci_and_filename(0);
1770 }
1771 if( rid==0 ){
1772 rid = name_to_rid_www("name");
1773 }
1774 login_check_credentials();
@@ -1980,11 +1927,11 @@
1927 }else{
1928 @ :</h2>
1929 }
1930 blob_zero(&downloadName);
1931 if( P("verbose")!=0 ) objdescFlags |= OBJDESC_DETAIL;
1932 object_description(rid, objdescFlags, 0, &downloadName);
1933 style_submenu_element("Download", "%s/raw/%T?name=%s",
1934 g.zTop, blob_str(&downloadName), zUuid);
1935 @ <hr />
1936 content_get(rid, &content);
1937 @ <blockquote><pre>
@@ -1995,59 +1942,53 @@
1942
1943 /*
1944 ** Look for "ci" and "filename" query parameters. If found, try to
1945 ** use them to extract the record ID of an artifact for the file.
1946 **
1947 ** Also look for "fn" and "name" as an aliases for "filename". If any
1948 ** "filename" or "fn" or "name" are present but "ci" is missing, then
1949 ** use "tip" as the default value for "ci".
1950 **
1951 ** If zNameParam is not NULL, then use that parameter as the filename
1952 ** rather than "fn" or "filename" or "name". the zNameParam is used
1953 ** for the from= and to= query parameters of /fdiff.
 
 
 
 
1954 */
1955 int artifact_from_ci_and_filename(const char *zNameParam){
1956 const char *zFilename;
1957 const char *zCI;
1958 int cirid;
1959 Manifest *pManifest;
1960 ManifestFile *pFile;
1961 int rid = 0;
1962
1963 if( zNameParam ){
1964 zFilename = P(zNameParam);
1965 }else{
1966 zFilename = P("filename");
1967 if( zFilename==0 ){
1968 zFilename = P("fn");
1969 }
1970 if( zFilename==0 ){
1971 zFilename = P("name");
1972 }
1973 }
1974 if( zFilename==0 ) return 0;
1975
1976 zCI = PD("ci", "tip");
1977 cirid = name_to_typed_rid(zCI, "ci");
1978 if( cirid<=0 ) return 0;
1979 pManifest = manifest_get(cirid, CFTYPE_MANIFEST, 0);
1980 if( pManifest==0 ) return 0;
1981 manifest_file_rewind(pManifest);
1982 while( (pFile = manifest_file_next(pManifest,0))!=0 ){
1983 if( fossil_strcmp(zFilename, pFile->zName)==0 ){
1984 rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", pFile->zUuid);
1985 break;
 
 
 
 
 
 
1986 }
1987 }
1988 manifest_destroy(pManifest);
1989 return rid;
1990 }
1991
1992 /*
1993 ** The "z" argument is a string that contains the text of a source code
1994 ** file. This routine appends that text to the HTTP reply with line numbering.
@@ -2149,21 +2090,31 @@
2090 ** ln=N - highlight line number N
2091 ** ln=M-N - highlight lines M through N inclusive
2092 ** ln=M-N+Y-Z - highlight lines M through N and Y through Z (inclusive)
2093 ** verbose - show more detail in the description
2094 ** download - redirect to the download (artifact page only)
2095 ** name=NAME - filename or hash as a query parameter
2096 ** filename=NAME - alternative spelling for "name="
2097 ** fn=NAME - alternative spelling for "name="
2098 ** ci=VERSION - The specific check-in to use with "name=" to
2099 ** identify the file.
2100 **
2101 ** The /artifact page show the complete content of a file
2102 ** identified by HASH. The /whatis page shows only a description
2103 ** of how the artifact is used. The /file page shows the most recent
2104 ** version of the file or directory called NAME, or a list of the
2105 ** top-level directory if NAME is omitted.
2106 **
2107 ** For /artifact and /whatis, the name= query parameter can refer to
2108 ** either the name of a file, or an artifact hash. If the ci= query
2109 ** parameter is also present, then name= must refer to a file name.
2110 ** If ci= is omitted, then the hash interpretation is preferred but
2111 ** if name= cannot be understood as a hash, a default "tip" value is
2112 ** used for ci=.
2113 **
2114 ** For /file, name= can only be interpreted as a filename. As before,
2115 ** a default value of "tip" is used for ci= if ci= is omitted.
2116 */
2117 void artifact_page(void){
2118 int rid = 0;
2119 Blob content;
2120 const char *zMime;
@@ -2170,95 +2121,146 @@
2121 Blob downloadName;
2122 int renderAsWiki = 0;
2123 int renderAsHtml = 0;
2124 int objType;
2125 int asText;
2126 const char *zUuid = 0;
2127 u32 objdescFlags = OBJDESC_BASE;
2128 int descOnly = fossil_strcmp(g.zPath,"whatis")==0;
2129 int isFile = fossil_strcmp(g.zPath,"file")==0;
2130 const char *zLn = P("ln");
2131 const char *zName = P("name");
2132 const char *zCI = P("ci");
2133 HQuery url;
2134 char *zCIUuid = 0;
2135 int isSymbolicCI = 0; /* ci= exists and is a symbolic name, not a hash */
2136 int isBranchCI = 0; /* ci= refers to a branch name */
2137 char *zHeader = 0;
2138
2139 login_check_credentials();
2140 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
2141
2142 /* Capture and normalize the name= and ci= query parameters */
2143 if( zName==0 ){
2144 zName = P("filename");
2145 if( zName==0 ){
2146 zName = P("fn");
2147 }
2148 }
2149 if( zCI && strlen(zCI)==0 ){ zCI = 0; }
2150 if( zCI
2151 && name_to_uuid2(zCI, "ci", &zCIUuid)
2152 && sqlite3_strnicmp(zCIUuid, zCI, strlen(zCI))!=0
2153 ){
2154 isSymbolicCI = 1;
2155 isBranchCI = branch_includes_uuid(zCI, zCIUuid);
2156 }
2157
2158 /* The name= query parameter (or at least one of its alternative
2159 ** spellings) is required. Except for /file, show a top-level
2160 ** directory listing if name= is omitted.
2161 */
2162 if( zName==0 ){
2163 if( isFile ){
2164 if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2165 page_tree();
2166 return;
2167 }
2168 style_header("Missing name= query parameter");
2169 @ The name= query parameter is missing
2170 style_footer();
2171 return;
2172 }
2173
2174 url_initialize(&url, g.zPath);
2175 url_add_parameter(&url, "name", zName);
2176 url_add_parameter(&url, "ci", zCI);
2177
2178 if( zCI==0 && !isFile ){
2179 /* If there is no ci= query parameter, then prefer to interpret
2180 ** name= as a hash for /artifact and /whatis. But for not for /file.
2181 ** For /file, a name= without a ci= while prefer to use the default
2182 ** "tip" value for ci=. */
2183 rid = name_to_rid(zName);
2184 }
2185 if( rid==0 ){
2186 rid = artifact_from_ci_and_filename(0);
2187 }
2188
2189 if( rid==0 ){ /* Artifact not found */
2190 if( isFile ){
2191 /* For /file, also check to see if name= refers to a directory,
2192 ** and if so, do a listing for that directory */
2193 int nName = (int)strlen(zName);
2194 if( nName && zName[nName-1]=='/' ) nName--;
2195 if( db_exists(
2196 "SELECT 1 FROM filename"
2197 " WHERE name GLOB '%.*q/*' AND substr(name,1,%d)=='%.*q/';",
2198 nName, zName, nName+1, nName, zName
2199 ) ){
2200 if( P("ci")==0 ) cgi_set_query_parameter("ci","tip");
2201 page_tree();
2202 return;
2203 }
2204 style_header("No such file");
2205 @ File '%h(zName)' does not exist in this repository.
2206 }else{
2207 style_header("No such artifact");
2208 @ Artifact '%h(zName)' does not exist in this repository.
2209 }
2210 style_footer();
2211 return;
2212 }
2213
2214 if( descOnly || P("verbose")!=0 ){
2215 url_add_parameter(&url, "verbose", "1");
2216 objdescFlags |= OBJDESC_DETAIL;
2217 }
2218 zUuid = db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid);
2219
2220 asText = P("txt")!=0;
2221 if( isFile ){
2222 if( zCI==0 || fossil_strcmp(zCI,"tip")==0 ){
2223 zCI = "tip";
2224 @ <h2>File %z(href("%R/finfo?name=%T&m=tip",zName))%h(zName)</a>
2225 @ from the %z(href("%R/info/tip"))latest check-in</a></h2>
2226 }else{
2227 const char *zPath;
2228 Blob path;
2229 blob_zero(&path);
2230 hyperlinked_path(zName, &path, zCI, "dir", "", LINKPATH_FINFO);
2231 zPath = blob_str(&path);
2232 @ <h2>File %s(zPath) \
2233 if( isBranchCI ){
2234 @ on branch %z(href("%R/timeline?r=%T",zCI))%h(zCI)</a></h2>
2235 }else if( isSymbolicCI ){
2236 @ as of check-in %z(href("%R/info/%!S",zCIUuid))%s(zCI)</a></h2>
2237 }else{
2238 @ as of check-in [%z(href("%R/info/%!S",zCIUuid))%S(zCIUuid)</a>]</h2>
2239 }
2240 blob_reset(&path);
2241 }
2242 style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
2243 style_submenu_element("Annotate", "%R/annotate?filename=%T&checkin=%T",
2244 zName, zCI);
2245 style_submenu_element("Blame", "%R/blame?filename=%T&checkin=%T",
2246 zName, zCI);
2247 blob_init(&downloadName, zName, -1);
2248 objType = OBJTYPE_CONTENT;
2249 }else{
2250 @ <h2>Artifact
2251 style_copy_button(1, "hash-ar", 0, 2, "%s", zUuid);
2252 if( g.perm.Setup ){
2253 @ (%d(rid)):</h2>
2254 }else{
2255 @ :</h2>
2256 }
2257 blob_zero(&downloadName);
2258 if( asText ) objdescFlags &= ~OBJDESC_BASE;
2259 objType = object_description(rid, objdescFlags,
2260 (isFile?zName:0), &downloadName);
2261 }
 
 
 
 
2262 if( !descOnly && P("download")!=0 ){
2263 cgi_redirectf("%R/raw/%T?name=%s", blob_str(&downloadName),
2264 db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid));
2265 /*NOTREACHED*/
2266 }
@@ -2269,13 +2271,28 @@
2271 g.zTop, zUuid);
2272 }else{
2273 style_submenu_element("Shun", "%s/shun?shun=%s#addshun", g.zTop, zUuid);
2274 }
2275 }
2276
2277 if( isFile ){
2278 if( isSymbolicCI ){
2279 zHeader = mprintf("%s at %s", file_tail(zName), zCI);
2280 }else if( zCI ){
2281 zHeader = mprintf("%s at [%S]", file_tail(zName), zCIUuid);
2282 }else{
2283 zHeader = mprintf("%s", file_tail(zName));
2284 }
2285 }else if( descOnly ){
2286 zHeader = mprintf("Artifact Description [%S]", zUuid);
2287 }else{
2288 zHeader = mprintf("Artifact [%S]", zUuid);
2289 }
2290 style_header("%s", zHeader);
2291 fossil_free(zCIUuid);
2292 fossil_free(zHeader);
2293 if( !isFile && g.perm.Admin ){
2294 Stmt q;
2295 db_prepare(&q,
2296 "SELECT coalesce(user.login,rcvfrom.uid),"
2297 " datetime(rcvfrom.mtime,toLocal()), rcvfrom.ipaddr"
2298 " FROM blob, rcvfrom LEFT JOIN user ON user.uid=rcvfrom.uid"
2299
--- src/json_branch.c
+++ src/json_branch.c
@@ -266,13 +266,10 @@
266266
if( zColor!=0 ){
267267
blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
268268
}
269269
blob_appendf(&branch, "T *branch * %F\n", zBranch);
270270
blob_appendf(&branch, "T *sym-%F *\n", zBranch);
271
- if( zOpt->isPrivate ){
272
- blob_appendf(&branch, "T +private *\n");
273
- }
274271
275272
/* Cancel all other symbolic tags */
276273
db_prepare(&q,
277274
"SELECT tagname FROM tagxref, tag"
278275
" WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
@@ -287,11 +284,11 @@
287284
288285
blob_appendf(&branch, "U %F\n", g.zLogin);
289286
md5sum_blob(&branch, &mcksum);
290287
blob_appendf(&branch, "Z %b\n", &mcksum);
291288
292
- brid = content_put(&branch);
289
+ brid = content_put_ex(&branch, 0, 0, 0, zOpt->isPrivate);
293290
if( brid==0 ){
294291
fossil_panic("Problem committing manifest: %s", g.zErrMsg);
295292
}
296293
db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid);
297294
if( manifest_crosslink(brid, &branch, MC_PERMIT_HOOKS)==0 ){
298295
--- src/json_branch.c
+++ src/json_branch.c
@@ -266,13 +266,10 @@
266 if( zColor!=0 ){
267 blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
268 }
269 blob_appendf(&branch, "T *branch * %F\n", zBranch);
270 blob_appendf(&branch, "T *sym-%F *\n", zBranch);
271 if( zOpt->isPrivate ){
272 blob_appendf(&branch, "T +private *\n");
273 }
274
275 /* Cancel all other symbolic tags */
276 db_prepare(&q,
277 "SELECT tagname FROM tagxref, tag"
278 " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
@@ -287,11 +284,11 @@
287
288 blob_appendf(&branch, "U %F\n", g.zLogin);
289 md5sum_blob(&branch, &mcksum);
290 blob_appendf(&branch, "Z %b\n", &mcksum);
291
292 brid = content_put(&branch);
293 if( brid==0 ){
294 fossil_panic("Problem committing manifest: %s", g.zErrMsg);
295 }
296 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid);
297 if( manifest_crosslink(brid, &branch, MC_PERMIT_HOOKS)==0 ){
298
--- src/json_branch.c
+++ src/json_branch.c
@@ -266,13 +266,10 @@
266 if( zColor!=0 ){
267 blob_appendf(&branch, "T *bgcolor * %F\n", zColor);
268 }
269 blob_appendf(&branch, "T *branch * %F\n", zBranch);
270 blob_appendf(&branch, "T *sym-%F *\n", zBranch);
 
 
 
271
272 /* Cancel all other symbolic tags */
273 db_prepare(&q,
274 "SELECT tagname FROM tagxref, tag"
275 " WHERE tagxref.rid=%d AND tagxref.tagid=tag.tagid"
@@ -287,11 +284,11 @@
284
285 blob_appendf(&branch, "U %F\n", g.zLogin);
286 md5sum_blob(&branch, &mcksum);
287 blob_appendf(&branch, "Z %b\n", &mcksum);
288
289 brid = content_put_ex(&branch, 0, 0, 0, zOpt->isPrivate);
290 if( brid==0 ){
291 fossil_panic("Problem committing manifest: %s", g.zErrMsg);
292 }
293 db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", brid);
294 if( manifest_crosslink(brid, &branch, MC_PERMIT_HOOKS)==0 ){
295
+62 -14
--- src/login.c
+++ src/login.c
@@ -481,11 +481,11 @@
481481
int login_self_register_available(const char *zNeeded){
482482
CapabilityString *pCap;
483483
int rc;
484484
if( !db_get_boolean("self-register",0) ) return 0;
485485
if( zNeeded==0 ) return 1;
486
- pCap = capability_add(0, db_get("default-perms", 0));
486
+ pCap = capability_add(0, db_get("default-perms", "u"));
487487
capability_expand(pCap);
488488
rc = capability_has_any(pCap, zNeeded);
489489
capability_free(pCap);
490490
return rc;
491491
}
@@ -1128,11 +1128,11 @@
11281128
if( zPublicPages!=0 ){
11291129
Glob *pGlob = glob_create(zPublicPages);
11301130
const char *zUri = PD("REQUEST_URI","");
11311131
zUri += (int)strlen(g.zTop);
11321132
if( glob_match(pGlob, zUri) ){
1133
- login_set_capabilities(db_get("default-perms", 0), 0);
1133
+ login_set_capabilities(db_get("default-perms", "u"), 0);
11341134
}
11351135
glob_free(pGlob);
11361136
}
11371137
}
11381138
@@ -1459,10 +1459,38 @@
14591459
"SELECT 1 FROM event WHERE user=%Q OR euser=%Q",
14601460
zUserID, zUserID, zUserID
14611461
);
14621462
return rc;
14631463
}
1464
+
1465
+/*
1466
+** Check an email address and confirm that it is valid for self-registration.
1467
+** The email address is known already to be well-formed. Return true
1468
+** if the email address is on the allowed list.
1469
+**
1470
+** The default behavior is that any valid email address is accepted.
1471
+** But if the "auth-sub-email" setting exists and is not empty, then
1472
+** it is a comma-separated list of GLOB patterns for email addresses
1473
+** that are authorized to self-register.
1474
+*/
1475
+int authorized_subscription_email(const char *zEAddr){
1476
+ char *zGlob = db_get("auth-sub-email",0);
1477
+ Glob *pGlob;
1478
+ char *zAddr;
1479
+ int rc;
1480
+
1481
+ if( zGlob==0 || zGlob[0]==0 ) return 1;
1482
+ zGlob = fossil_strtolwr(fossil_strdup(zGlob));
1483
+ pGlob = glob_create(zGlob);
1484
+ fossil_free(zGlob);
1485
+
1486
+ zAddr = fossil_strtolwr(fossil_strdup(zEAddr));
1487
+ rc = glob_match(pGlob, zAddr);
1488
+ fossil_free(zAddr);
1489
+ glob_free(pGlob);
1490
+ return rc!=0;
1491
+}
14641492
14651493
/*
14661494
** WEBPAGE: register
14671495
**
14681496
** Page to allow users to self-register. The "self-register" setting
@@ -1471,13 +1499,14 @@
14711499
void register_page(void){
14721500
const char *zUserID, *zPasswd, *zConfirm, *zEAddr;
14731501
const char *zDName;
14741502
unsigned int uSeed;
14751503
const char *zDecoded;
1476
- char *zCaptcha;
14771504
int iErrLine = -1;
14781505
const char *zErr = 0;
1506
+ int captchaIsCorrect = 0; /* True on a correct captcha */
1507
+ char *zCaptcha = ""; /* Value of the captcha text */
14791508
char *zPerms; /* Permissions for the default user */
14801509
int canDoAlerts = 0; /* True if receiving email alerts is possible */
14811510
int doAlerts = 0; /* True if subscription is wanted too */
14821511
if( !db_get_boolean("self-register", 0) ){
14831512
style_header("Registration not possible");
@@ -1484,11 +1513,11 @@
14841513
@ <p>This project does not allow user self-registration. Please contact the
14851514
@ project administrator to obtain an account.</p>
14861515
style_footer();
14871516
return;
14881517
}
1489
- zPerms = db_get("default-perms", 0);
1518
+ zPerms = db_get("default-perms", "u");
14901519
14911520
/* Prompt the user for email alerts if this repository is configured for
14921521
** email alerts and if the default permissions include "7" */
14931522
canDoAlerts = alert_tables_exist() && db_int(0,
14941523
"SELECT fullcap(%Q) GLOB '*7*'", zPerms
@@ -1503,11 +1532,11 @@
15031532
15041533
/* Verify user imputs */
15051534
if( P("new")==0 || !cgi_csrf_safe(1) ){
15061535
/* This is not a valid form submission. Fall through into
15071536
** the form display */
1508
- }else if( !captcha_is_correct(1) ){
1537
+ }else if( (captchaIsCorrect = captcha_is_correct(1))==0 ){
15091538
iErrLine = 6;
15101539
zErr = "Incorrect CAPTCHA";
15111540
}else if( strlen(zUserID)<6 ){
15121541
iErrLine = 1;
15131542
zErr = "User ID too short. Must be at least 6 characters.";
@@ -1521,10 +1550,13 @@
15211550
iErrLine = 3;
15221551
zErr = "Required";
15231552
}else if( email_address_is_valid(zEAddr,0)==0 ){
15241553
iErrLine = 3;
15251554
zErr = "Not a valid email address";
1555
+ }else if( authorized_subscription_email(zEAddr)==0 ){
1556
+ iErrLine = 3;
1557
+ zErr = "Not an authorized email address";
15261558
}else if( strlen(zPasswd)<6 ){
15271559
iErrLine = 4;
15281560
zErr = "Password must be at least 6 characters long";
15291561
}else if( fossil_strcmp(zPasswd,zConfirm)!=0 ){
15301562
iErrLine = 5;
@@ -1548,16 +1580,24 @@
15481580
/* If all of the tests above have passed, that means that the submitted
15491581
** form contains valid data and we can proceed to create the new login */
15501582
Blob sql;
15511583
int uid;
15521584
char *zPass = sha1_shared_secret(zPasswd, zUserID, 0);
1585
+ const char *zStartPerms = zPerms;
1586
+ if( db_get_boolean("selfreg-verify",0) ){
1587
+ /* If email verification is required for self-registration, initalize
1588
+ ** the new user capabilities to just "7" (Sign up for email). The
1589
+ ** full "default-perms" permissions will be added when they click
1590
+ ** the verification link on the email they are sent. */
1591
+ zStartPerms = "7";
1592
+ }
15531593
blob_init(&sql, 0, 0);
15541594
blob_append_sql(&sql,
15551595
"INSERT INTO user(login,pw,cap,info,mtime)\n"
15561596
"VALUES(%Q,%Q,%Q,"
15571597
"'%q <%q>\nself-register from ip %q on '||datetime('now'),now())",
1558
- zUserID, zPass, zPerms, zDName, zEAddr, g.zIpAddr);
1598
+ zUserID, zPass, zStartPerms, zDName, zEAddr, g.zIpAddr);
15591599
fossil_free(zPass);
15601600
db_multi_exec("%s", blob_sql_text(&sql));
15611601
uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUserID);
15621602
login_set_user_cookie(zUserID, uid, NULL);
15631603
if( doAlerts ){
@@ -1567,16 +1607,20 @@
15671607
sqlite3_int64 id; /* New subscriber Id */
15681608
const char *zCode; /* New subscriber code (in hex) */
15691609
const char *zGoto = P("g");
15701610
int nsub = 0;
15711611
char ssub[20];
1612
+ CapabilityString *pCap;
1613
+ pCap = capability_add(0, zPerms);
1614
+ capability_expand(pCap);
15721615
ssub[nsub++] = 'a';
1573
- if( g.perm.Read ) ssub[nsub++] = 'c';
1574
- if( g.perm.RdForum ) ssub[nsub++] = 'f';
1575
- if( g.perm.RdTkt ) ssub[nsub++] = 't';
1576
- if( g.perm.RdWiki ) ssub[nsub++] = 'w';
1616
+ if( capability_has_any(pCap,"o") ) ssub[nsub++] = 'c';
1617
+ if( capability_has_any(pCap,"2") ) ssub[nsub++] = 'f';
1618
+ if( capability_has_any(pCap,"r") ) ssub[nsub++] = 't';
1619
+ if( capability_has_any(pCap,"j") ) ssub[nsub++] = 'w';
15771620
ssub[nsub] = 0;
1621
+ capability_free(pCap);
15781622
/* Also add the user to the subscriber table. */
15791623
db_multi_exec(
15801624
"INSERT INTO subscriber(semail,suname,"
15811625
" sverified,sdonotcall,sdigest,ssub,sctime,mtime,smip)"
15821626
" VALUES(%Q,%Q,%d,0,%d,%Q,now(),now(),%Q)"
@@ -1616,12 +1660,11 @@
16161660
@ <blockquote><pre>
16171661
@ %h(pSender->zErr)
16181662
@ </pre></blockquote>
16191663
}else{
16201664
@ <p>An email has been sent to "%h(zEAddr)". That email contains a
1621
- @ hyperlink that you must click on in order to activate your
1622
- @ subscription.</p>
1665
+ @ hyperlink that you must click to activate your account.</p>
16231666
}
16241667
alert_sender_free(pSender);
16251668
if( zGoto ){
16261669
@ <p><a href='%h(zGoto)'>Continue</a>
16271670
}
@@ -1630,11 +1673,15 @@
16301673
}
16311674
redirect_to_g();
16321675
}
16331676
16341677
/* Prepare the captcha. */
1635
- uSeed = captcha_seed();
1678
+ if( captchaIsCorrect ){
1679
+ uSeed = strtoul(P("captchaseed"),0,10);
1680
+ }else{
1681
+ uSeed = captcha_seed();
1682
+ }
16361683
zDecoded = captcha_decode(uSeed);
16371684
zCaptcha = captcha_render(zDecoded);
16381685
16391686
style_header("Register");
16401687
/* Print out the registration form. */
@@ -1689,11 +1736,12 @@
16891736
if( iErrLine==5 ){
16901737
@ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
16911738
}
16921739
@ <tr>
16931740
@ <td class="form_label" align="right">Captcha:</td>
1694
- @ <td><input type="text" name="captcha" value="" size="30">
1741
+ @ <td><input type="text" name="captcha" \
1742
+ @ value="%h(captchaIsCorrect?zDecoded:"")" size="30">
16951743
captcha_speakit_button(uSeed, "Speak the captcha text");
16961744
@ </td>
16971745
@ </tr>
16981746
if( iErrLine==6 ){
16991747
@ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
17001748
--- src/login.c
+++ src/login.c
@@ -481,11 +481,11 @@
481 int login_self_register_available(const char *zNeeded){
482 CapabilityString *pCap;
483 int rc;
484 if( !db_get_boolean("self-register",0) ) return 0;
485 if( zNeeded==0 ) return 1;
486 pCap = capability_add(0, db_get("default-perms", 0));
487 capability_expand(pCap);
488 rc = capability_has_any(pCap, zNeeded);
489 capability_free(pCap);
490 return rc;
491 }
@@ -1128,11 +1128,11 @@
1128 if( zPublicPages!=0 ){
1129 Glob *pGlob = glob_create(zPublicPages);
1130 const char *zUri = PD("REQUEST_URI","");
1131 zUri += (int)strlen(g.zTop);
1132 if( glob_match(pGlob, zUri) ){
1133 login_set_capabilities(db_get("default-perms", 0), 0);
1134 }
1135 glob_free(pGlob);
1136 }
1137 }
1138
@@ -1459,10 +1459,38 @@
1459 "SELECT 1 FROM event WHERE user=%Q OR euser=%Q",
1460 zUserID, zUserID, zUserID
1461 );
1462 return rc;
1463 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1464
1465 /*
1466 ** WEBPAGE: register
1467 **
1468 ** Page to allow users to self-register. The "self-register" setting
@@ -1471,13 +1499,14 @@
1471 void register_page(void){
1472 const char *zUserID, *zPasswd, *zConfirm, *zEAddr;
1473 const char *zDName;
1474 unsigned int uSeed;
1475 const char *zDecoded;
1476 char *zCaptcha;
1477 int iErrLine = -1;
1478 const char *zErr = 0;
 
 
1479 char *zPerms; /* Permissions for the default user */
1480 int canDoAlerts = 0; /* True if receiving email alerts is possible */
1481 int doAlerts = 0; /* True if subscription is wanted too */
1482 if( !db_get_boolean("self-register", 0) ){
1483 style_header("Registration not possible");
@@ -1484,11 +1513,11 @@
1484 @ <p>This project does not allow user self-registration. Please contact the
1485 @ project administrator to obtain an account.</p>
1486 style_footer();
1487 return;
1488 }
1489 zPerms = db_get("default-perms", 0);
1490
1491 /* Prompt the user for email alerts if this repository is configured for
1492 ** email alerts and if the default permissions include "7" */
1493 canDoAlerts = alert_tables_exist() && db_int(0,
1494 "SELECT fullcap(%Q) GLOB '*7*'", zPerms
@@ -1503,11 +1532,11 @@
1503
1504 /* Verify user imputs */
1505 if( P("new")==0 || !cgi_csrf_safe(1) ){
1506 /* This is not a valid form submission. Fall through into
1507 ** the form display */
1508 }else if( !captcha_is_correct(1) ){
1509 iErrLine = 6;
1510 zErr = "Incorrect CAPTCHA";
1511 }else if( strlen(zUserID)<6 ){
1512 iErrLine = 1;
1513 zErr = "User ID too short. Must be at least 6 characters.";
@@ -1521,10 +1550,13 @@
1521 iErrLine = 3;
1522 zErr = "Required";
1523 }else if( email_address_is_valid(zEAddr,0)==0 ){
1524 iErrLine = 3;
1525 zErr = "Not a valid email address";
 
 
 
1526 }else if( strlen(zPasswd)<6 ){
1527 iErrLine = 4;
1528 zErr = "Password must be at least 6 characters long";
1529 }else if( fossil_strcmp(zPasswd,zConfirm)!=0 ){
1530 iErrLine = 5;
@@ -1548,16 +1580,24 @@
1548 /* If all of the tests above have passed, that means that the submitted
1549 ** form contains valid data and we can proceed to create the new login */
1550 Blob sql;
1551 int uid;
1552 char *zPass = sha1_shared_secret(zPasswd, zUserID, 0);
 
 
 
 
 
 
 
 
1553 blob_init(&sql, 0, 0);
1554 blob_append_sql(&sql,
1555 "INSERT INTO user(login,pw,cap,info,mtime)\n"
1556 "VALUES(%Q,%Q,%Q,"
1557 "'%q <%q>\nself-register from ip %q on '||datetime('now'),now())",
1558 zUserID, zPass, zPerms, zDName, zEAddr, g.zIpAddr);
1559 fossil_free(zPass);
1560 db_multi_exec("%s", blob_sql_text(&sql));
1561 uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUserID);
1562 login_set_user_cookie(zUserID, uid, NULL);
1563 if( doAlerts ){
@@ -1567,16 +1607,20 @@
1567 sqlite3_int64 id; /* New subscriber Id */
1568 const char *zCode; /* New subscriber code (in hex) */
1569 const char *zGoto = P("g");
1570 int nsub = 0;
1571 char ssub[20];
 
 
 
1572 ssub[nsub++] = 'a';
1573 if( g.perm.Read ) ssub[nsub++] = 'c';
1574 if( g.perm.RdForum ) ssub[nsub++] = 'f';
1575 if( g.perm.RdTkt ) ssub[nsub++] = 't';
1576 if( g.perm.RdWiki ) ssub[nsub++] = 'w';
1577 ssub[nsub] = 0;
 
1578 /* Also add the user to the subscriber table. */
1579 db_multi_exec(
1580 "INSERT INTO subscriber(semail,suname,"
1581 " sverified,sdonotcall,sdigest,ssub,sctime,mtime,smip)"
1582 " VALUES(%Q,%Q,%d,0,%d,%Q,now(),now(),%Q)"
@@ -1616,12 +1660,11 @@
1616 @ <blockquote><pre>
1617 @ %h(pSender->zErr)
1618 @ </pre></blockquote>
1619 }else{
1620 @ <p>An email has been sent to "%h(zEAddr)". That email contains a
1621 @ hyperlink that you must click on in order to activate your
1622 @ subscription.</p>
1623 }
1624 alert_sender_free(pSender);
1625 if( zGoto ){
1626 @ <p><a href='%h(zGoto)'>Continue</a>
1627 }
@@ -1630,11 +1673,15 @@
1630 }
1631 redirect_to_g();
1632 }
1633
1634 /* Prepare the captcha. */
1635 uSeed = captcha_seed();
 
 
 
 
1636 zDecoded = captcha_decode(uSeed);
1637 zCaptcha = captcha_render(zDecoded);
1638
1639 style_header("Register");
1640 /* Print out the registration form. */
@@ -1689,11 +1736,12 @@
1689 if( iErrLine==5 ){
1690 @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
1691 }
1692 @ <tr>
1693 @ <td class="form_label" align="right">Captcha:</td>
1694 @ <td><input type="text" name="captcha" value="" size="30">
 
1695 captcha_speakit_button(uSeed, "Speak the captcha text");
1696 @ </td>
1697 @ </tr>
1698 if( iErrLine==6 ){
1699 @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
1700
--- src/login.c
+++ src/login.c
@@ -481,11 +481,11 @@
481 int login_self_register_available(const char *zNeeded){
482 CapabilityString *pCap;
483 int rc;
484 if( !db_get_boolean("self-register",0) ) return 0;
485 if( zNeeded==0 ) return 1;
486 pCap = capability_add(0, db_get("default-perms", "u"));
487 capability_expand(pCap);
488 rc = capability_has_any(pCap, zNeeded);
489 capability_free(pCap);
490 return rc;
491 }
@@ -1128,11 +1128,11 @@
1128 if( zPublicPages!=0 ){
1129 Glob *pGlob = glob_create(zPublicPages);
1130 const char *zUri = PD("REQUEST_URI","");
1131 zUri += (int)strlen(g.zTop);
1132 if( glob_match(pGlob, zUri) ){
1133 login_set_capabilities(db_get("default-perms", "u"), 0);
1134 }
1135 glob_free(pGlob);
1136 }
1137 }
1138
@@ -1459,10 +1459,38 @@
1459 "SELECT 1 FROM event WHERE user=%Q OR euser=%Q",
1460 zUserID, zUserID, zUserID
1461 );
1462 return rc;
1463 }
1464
1465 /*
1466 ** Check an email address and confirm that it is valid for self-registration.
1467 ** The email address is known already to be well-formed. Return true
1468 ** if the email address is on the allowed list.
1469 **
1470 ** The default behavior is that any valid email address is accepted.
1471 ** But if the "auth-sub-email" setting exists and is not empty, then
1472 ** it is a comma-separated list of GLOB patterns for email addresses
1473 ** that are authorized to self-register.
1474 */
1475 int authorized_subscription_email(const char *zEAddr){
1476 char *zGlob = db_get("auth-sub-email",0);
1477 Glob *pGlob;
1478 char *zAddr;
1479 int rc;
1480
1481 if( zGlob==0 || zGlob[0]==0 ) return 1;
1482 zGlob = fossil_strtolwr(fossil_strdup(zGlob));
1483 pGlob = glob_create(zGlob);
1484 fossil_free(zGlob);
1485
1486 zAddr = fossil_strtolwr(fossil_strdup(zEAddr));
1487 rc = glob_match(pGlob, zAddr);
1488 fossil_free(zAddr);
1489 glob_free(pGlob);
1490 return rc!=0;
1491 }
1492
1493 /*
1494 ** WEBPAGE: register
1495 **
1496 ** Page to allow users to self-register. The "self-register" setting
@@ -1471,13 +1499,14 @@
1499 void register_page(void){
1500 const char *zUserID, *zPasswd, *zConfirm, *zEAddr;
1501 const char *zDName;
1502 unsigned int uSeed;
1503 const char *zDecoded;
 
1504 int iErrLine = -1;
1505 const char *zErr = 0;
1506 int captchaIsCorrect = 0; /* True on a correct captcha */
1507 char *zCaptcha = ""; /* Value of the captcha text */
1508 char *zPerms; /* Permissions for the default user */
1509 int canDoAlerts = 0; /* True if receiving email alerts is possible */
1510 int doAlerts = 0; /* True if subscription is wanted too */
1511 if( !db_get_boolean("self-register", 0) ){
1512 style_header("Registration not possible");
@@ -1484,11 +1513,11 @@
1513 @ <p>This project does not allow user self-registration. Please contact the
1514 @ project administrator to obtain an account.</p>
1515 style_footer();
1516 return;
1517 }
1518 zPerms = db_get("default-perms", "u");
1519
1520 /* Prompt the user for email alerts if this repository is configured for
1521 ** email alerts and if the default permissions include "7" */
1522 canDoAlerts = alert_tables_exist() && db_int(0,
1523 "SELECT fullcap(%Q) GLOB '*7*'", zPerms
@@ -1503,11 +1532,11 @@
1532
1533 /* Verify user imputs */
1534 if( P("new")==0 || !cgi_csrf_safe(1) ){
1535 /* This is not a valid form submission. Fall through into
1536 ** the form display */
1537 }else if( (captchaIsCorrect = captcha_is_correct(1))==0 ){
1538 iErrLine = 6;
1539 zErr = "Incorrect CAPTCHA";
1540 }else if( strlen(zUserID)<6 ){
1541 iErrLine = 1;
1542 zErr = "User ID too short. Must be at least 6 characters.";
@@ -1521,10 +1550,13 @@
1550 iErrLine = 3;
1551 zErr = "Required";
1552 }else if( email_address_is_valid(zEAddr,0)==0 ){
1553 iErrLine = 3;
1554 zErr = "Not a valid email address";
1555 }else if( authorized_subscription_email(zEAddr)==0 ){
1556 iErrLine = 3;
1557 zErr = "Not an authorized email address";
1558 }else if( strlen(zPasswd)<6 ){
1559 iErrLine = 4;
1560 zErr = "Password must be at least 6 characters long";
1561 }else if( fossil_strcmp(zPasswd,zConfirm)!=0 ){
1562 iErrLine = 5;
@@ -1548,16 +1580,24 @@
1580 /* If all of the tests above have passed, that means that the submitted
1581 ** form contains valid data and we can proceed to create the new login */
1582 Blob sql;
1583 int uid;
1584 char *zPass = sha1_shared_secret(zPasswd, zUserID, 0);
1585 const char *zStartPerms = zPerms;
1586 if( db_get_boolean("selfreg-verify",0) ){
1587 /* If email verification is required for self-registration, initalize
1588 ** the new user capabilities to just "7" (Sign up for email). The
1589 ** full "default-perms" permissions will be added when they click
1590 ** the verification link on the email they are sent. */
1591 zStartPerms = "7";
1592 }
1593 blob_init(&sql, 0, 0);
1594 blob_append_sql(&sql,
1595 "INSERT INTO user(login,pw,cap,info,mtime)\n"
1596 "VALUES(%Q,%Q,%Q,"
1597 "'%q <%q>\nself-register from ip %q on '||datetime('now'),now())",
1598 zUserID, zPass, zStartPerms, zDName, zEAddr, g.zIpAddr);
1599 fossil_free(zPass);
1600 db_multi_exec("%s", blob_sql_text(&sql));
1601 uid = db_int(0, "SELECT uid FROM user WHERE login=%Q", zUserID);
1602 login_set_user_cookie(zUserID, uid, NULL);
1603 if( doAlerts ){
@@ -1567,16 +1607,20 @@
1607 sqlite3_int64 id; /* New subscriber Id */
1608 const char *zCode; /* New subscriber code (in hex) */
1609 const char *zGoto = P("g");
1610 int nsub = 0;
1611 char ssub[20];
1612 CapabilityString *pCap;
1613 pCap = capability_add(0, zPerms);
1614 capability_expand(pCap);
1615 ssub[nsub++] = 'a';
1616 if( capability_has_any(pCap,"o") ) ssub[nsub++] = 'c';
1617 if( capability_has_any(pCap,"2") ) ssub[nsub++] = 'f';
1618 if( capability_has_any(pCap,"r") ) ssub[nsub++] = 't';
1619 if( capability_has_any(pCap,"j") ) ssub[nsub++] = 'w';
1620 ssub[nsub] = 0;
1621 capability_free(pCap);
1622 /* Also add the user to the subscriber table. */
1623 db_multi_exec(
1624 "INSERT INTO subscriber(semail,suname,"
1625 " sverified,sdonotcall,sdigest,ssub,sctime,mtime,smip)"
1626 " VALUES(%Q,%Q,%d,0,%d,%Q,now(),now(),%Q)"
@@ -1616,12 +1660,11 @@
1660 @ <blockquote><pre>
1661 @ %h(pSender->zErr)
1662 @ </pre></blockquote>
1663 }else{
1664 @ <p>An email has been sent to "%h(zEAddr)". That email contains a
1665 @ hyperlink that you must click to activate your account.</p>
 
1666 }
1667 alert_sender_free(pSender);
1668 if( zGoto ){
1669 @ <p><a href='%h(zGoto)'>Continue</a>
1670 }
@@ -1630,11 +1673,15 @@
1673 }
1674 redirect_to_g();
1675 }
1676
1677 /* Prepare the captcha. */
1678 if( captchaIsCorrect ){
1679 uSeed = strtoul(P("captchaseed"),0,10);
1680 }else{
1681 uSeed = captcha_seed();
1682 }
1683 zDecoded = captcha_decode(uSeed);
1684 zCaptcha = captcha_render(zDecoded);
1685
1686 style_header("Register");
1687 /* Print out the registration form. */
@@ -1689,11 +1736,12 @@
1736 if( iErrLine==5 ){
1737 @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
1738 }
1739 @ <tr>
1740 @ <td class="form_label" align="right">Captcha:</td>
1741 @ <td><input type="text" name="captcha" \
1742 @ value="%h(captchaIsCorrect?zDecoded:"")" size="30">
1743 captcha_speakit_button(uSeed, "Speak the captcha text");
1744 @ </td>
1745 @ </tr>
1746 if( iErrLine==6 ){
1747 @ <tr><td><td><span class='loginError'>&uarr; %h(zErr)</span></td></tr>
1748
+37 -6
--- src/main.c
+++ src/main.c
@@ -782,11 +782,21 @@
782782
nNewArgc = g.argc+1;
783783
zNewArgv[i+1] = 0;
784784
}
785785
g.argc = nNewArgc;
786786
g.argv = zNewArgv;
787
- }
787
+#if 0
788
+ }else if( g.argc==2 && file_is_repository(g.argv[1]) ){
789
+ char **zNewArgv = fossil_malloc( sizeof(char*)*4 );
790
+ zNewArgv[0] = g.argv[0];
791
+ zNewArgv[1] = "ui";
792
+ zNewArgv[2] = g.argv[1];
793
+ zNewArgv[3] = 0;
794
+ g.argc = 3;
795
+ g.argv = zNewArgv;
796
+#endif
797
+ }
788798
zCmdName = g.argv[1];
789799
}
790800
#ifndef _WIN32
791801
/* There is a bug in stunnel4 in which it sometimes starts up client
792802
** processes without first opening file descriptor 2 (standard error).
@@ -812,10 +822,25 @@
812822
}
813823
}
814824
#endif
815825
g.zCmdName = zCmdName;
816826
rc = dispatch_name_search(zCmdName, CMDFLAG_COMMAND|CMDFLAG_PREFIX, &pCmd);
827
+ if( rc==1 && g.argc==2 && file_is_repository(g.argv[1]) ){
828
+ /* If the command-line is "fossil ABC" and "ABC" is no a valid command,
829
+ ** but "ABC" is the name of a repository file, make the command be
830
+ ** "fossil ui ABC" instead.
831
+ */
832
+ char **zNewArgv = fossil_malloc( sizeof(char*)*4 );
833
+ zNewArgv[0] = g.argv[0];
834
+ zNewArgv[1] = "ui";
835
+ zNewArgv[2] = g.argv[1];
836
+ zNewArgv[3] = 0;
837
+ g.argc = 3;
838
+ g.argv = zNewArgv;
839
+ g.zCmdName = zCmdName = "ui";
840
+ rc = dispatch_name_search(zCmdName, CMDFLAG_COMMAND|CMDFLAG_PREFIX, &pCmd);
841
+ }
817842
if( rc==1 ){
818843
#ifdef FOSSIL_ENABLE_TH1_HOOKS
819844
if( !g.isHTTP && !g.fNoThHook ){
820845
rc = Th_CommandHook(zCmdName, 0);
821846
}else{
@@ -1091,11 +1116,11 @@
10911116
/*
10921117
** This function populates a blob with version information. It is used by
10931118
** the "version" command and "test-version" web page. It assumes the blob
10941119
** passed to it is uninitialized; otherwise, it will leak memory.
10951120
*/
1096
-static void get_version_blob(
1121
+void fossil_version_blob(
10971122
Blob *pOut, /* Write the manifest here */
10981123
int bVerbose /* Non-zero for full information. */
10991124
){
11001125
#if defined(FOSSIL_ENABLE_TCL)
11011126
int rc;
@@ -1226,11 +1251,11 @@
12261251
Blob versionInfo;
12271252
int verboseFlag = find_option("verbose","v",0)!=0;
12281253
12291254
/* We should be done with options.. */
12301255
verify_all_options();
1231
- get_version_blob(&versionInfo, verboseFlag);
1256
+ fossil_version_blob(&versionInfo, verboseFlag);
12321257
fossil_print("%s", blob_str(&versionInfo));
12331258
}
12341259
12351260
12361261
/*
@@ -1249,11 +1274,11 @@
12491274
login_check_credentials();
12501275
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
12511276
verboseFlag = PD("verbose", 0) != 0;
12521277
style_header("Version Information");
12531278
style_submenu_element("Stat", "stat");
1254
- get_version_blob(&versionInfo, verboseFlag);
1279
+ fossil_version_blob(&versionInfo, verboseFlag);
12551280
@ <pre>
12561281
@ %h(blob_str(&versionInfo))
12571282
@ </pre>
12581283
style_footer();
12591284
}
@@ -2498,18 +2523,24 @@
24982523
**
24992524
** Works like the http command but gives setup permission to all users.
25002525
**
25012526
** Options:
25022527
** --th-trace trace TH1 execution (for debugging purposes)
2528
+** --usercap CAP user capability string. (Default: "sx")
25032529
**
25042530
*/
25052531
void cmd_test_http(void){
25062532
const char *zIpAddr; /* IP address of remote client */
2533
+ const char *zUserCap;
25072534
25082535
Th_InitTraceLog();
2509
- login_set_capabilities("sx", 0);
2510
- g.useLocalauth = 1;
2536
+ zUserCap = find_option("usercap",0,1);
2537
+ if( zUserCap==0 ){
2538
+ g.useLocalauth = 1;
2539
+ zUserCap = "sx";
2540
+ }
2541
+ login_set_capabilities(zUserCap, 0);
25112542
g.httpIn = stdin;
25122543
g.httpOut = stdout;
25132544
fossil_binary_mode(g.httpOut);
25142545
fossil_binary_mode(g.httpIn);
25152546
g.zExtRoot = find_option("extroot",0,1);
25162547
--- src/main.c
+++ src/main.c
@@ -782,11 +782,21 @@
782 nNewArgc = g.argc+1;
783 zNewArgv[i+1] = 0;
784 }
785 g.argc = nNewArgc;
786 g.argv = zNewArgv;
787 }
 
 
 
 
 
 
 
 
 
 
788 zCmdName = g.argv[1];
789 }
790 #ifndef _WIN32
791 /* There is a bug in stunnel4 in which it sometimes starts up client
792 ** processes without first opening file descriptor 2 (standard error).
@@ -812,10 +822,25 @@
812 }
813 }
814 #endif
815 g.zCmdName = zCmdName;
816 rc = dispatch_name_search(zCmdName, CMDFLAG_COMMAND|CMDFLAG_PREFIX, &pCmd);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
817 if( rc==1 ){
818 #ifdef FOSSIL_ENABLE_TH1_HOOKS
819 if( !g.isHTTP && !g.fNoThHook ){
820 rc = Th_CommandHook(zCmdName, 0);
821 }else{
@@ -1091,11 +1116,11 @@
1091 /*
1092 ** This function populates a blob with version information. It is used by
1093 ** the "version" command and "test-version" web page. It assumes the blob
1094 ** passed to it is uninitialized; otherwise, it will leak memory.
1095 */
1096 static void get_version_blob(
1097 Blob *pOut, /* Write the manifest here */
1098 int bVerbose /* Non-zero for full information. */
1099 ){
1100 #if defined(FOSSIL_ENABLE_TCL)
1101 int rc;
@@ -1226,11 +1251,11 @@
1226 Blob versionInfo;
1227 int verboseFlag = find_option("verbose","v",0)!=0;
1228
1229 /* We should be done with options.. */
1230 verify_all_options();
1231 get_version_blob(&versionInfo, verboseFlag);
1232 fossil_print("%s", blob_str(&versionInfo));
1233 }
1234
1235
1236 /*
@@ -1249,11 +1274,11 @@
1249 login_check_credentials();
1250 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1251 verboseFlag = PD("verbose", 0) != 0;
1252 style_header("Version Information");
1253 style_submenu_element("Stat", "stat");
1254 get_version_blob(&versionInfo, verboseFlag);
1255 @ <pre>
1256 @ %h(blob_str(&versionInfo))
1257 @ </pre>
1258 style_footer();
1259 }
@@ -2498,18 +2523,24 @@
2498 **
2499 ** Works like the http command but gives setup permission to all users.
2500 **
2501 ** Options:
2502 ** --th-trace trace TH1 execution (for debugging purposes)
 
2503 **
2504 */
2505 void cmd_test_http(void){
2506 const char *zIpAddr; /* IP address of remote client */
 
2507
2508 Th_InitTraceLog();
2509 login_set_capabilities("sx", 0);
2510 g.useLocalauth = 1;
 
 
 
 
2511 g.httpIn = stdin;
2512 g.httpOut = stdout;
2513 fossil_binary_mode(g.httpOut);
2514 fossil_binary_mode(g.httpIn);
2515 g.zExtRoot = find_option("extroot",0,1);
2516
--- src/main.c
+++ src/main.c
@@ -782,11 +782,21 @@
782 nNewArgc = g.argc+1;
783 zNewArgv[i+1] = 0;
784 }
785 g.argc = nNewArgc;
786 g.argv = zNewArgv;
787 #if 0
788 }else if( g.argc==2 && file_is_repository(g.argv[1]) ){
789 char **zNewArgv = fossil_malloc( sizeof(char*)*4 );
790 zNewArgv[0] = g.argv[0];
791 zNewArgv[1] = "ui";
792 zNewArgv[2] = g.argv[1];
793 zNewArgv[3] = 0;
794 g.argc = 3;
795 g.argv = zNewArgv;
796 #endif
797 }
798 zCmdName = g.argv[1];
799 }
800 #ifndef _WIN32
801 /* There is a bug in stunnel4 in which it sometimes starts up client
802 ** processes without first opening file descriptor 2 (standard error).
@@ -812,10 +822,25 @@
822 }
823 }
824 #endif
825 g.zCmdName = zCmdName;
826 rc = dispatch_name_search(zCmdName, CMDFLAG_COMMAND|CMDFLAG_PREFIX, &pCmd);
827 if( rc==1 && g.argc==2 && file_is_repository(g.argv[1]) ){
828 /* If the command-line is "fossil ABC" and "ABC" is no a valid command,
829 ** but "ABC" is the name of a repository file, make the command be
830 ** "fossil ui ABC" instead.
831 */
832 char **zNewArgv = fossil_malloc( sizeof(char*)*4 );
833 zNewArgv[0] = g.argv[0];
834 zNewArgv[1] = "ui";
835 zNewArgv[2] = g.argv[1];
836 zNewArgv[3] = 0;
837 g.argc = 3;
838 g.argv = zNewArgv;
839 g.zCmdName = zCmdName = "ui";
840 rc = dispatch_name_search(zCmdName, CMDFLAG_COMMAND|CMDFLAG_PREFIX, &pCmd);
841 }
842 if( rc==1 ){
843 #ifdef FOSSIL_ENABLE_TH1_HOOKS
844 if( !g.isHTTP && !g.fNoThHook ){
845 rc = Th_CommandHook(zCmdName, 0);
846 }else{
@@ -1091,11 +1116,11 @@
1116 /*
1117 ** This function populates a blob with version information. It is used by
1118 ** the "version" command and "test-version" web page. It assumes the blob
1119 ** passed to it is uninitialized; otherwise, it will leak memory.
1120 */
1121 void fossil_version_blob(
1122 Blob *pOut, /* Write the manifest here */
1123 int bVerbose /* Non-zero for full information. */
1124 ){
1125 #if defined(FOSSIL_ENABLE_TCL)
1126 int rc;
@@ -1226,11 +1251,11 @@
1251 Blob versionInfo;
1252 int verboseFlag = find_option("verbose","v",0)!=0;
1253
1254 /* We should be done with options.. */
1255 verify_all_options();
1256 fossil_version_blob(&versionInfo, verboseFlag);
1257 fossil_print("%s", blob_str(&versionInfo));
1258 }
1259
1260
1261 /*
@@ -1249,11 +1274,11 @@
1274 login_check_credentials();
1275 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1276 verboseFlag = PD("verbose", 0) != 0;
1277 style_header("Version Information");
1278 style_submenu_element("Stat", "stat");
1279 fossil_version_blob(&versionInfo, verboseFlag);
1280 @ <pre>
1281 @ %h(blob_str(&versionInfo))
1282 @ </pre>
1283 style_footer();
1284 }
@@ -2498,18 +2523,24 @@
2523 **
2524 ** Works like the http command but gives setup permission to all users.
2525 **
2526 ** Options:
2527 ** --th-trace trace TH1 execution (for debugging purposes)
2528 ** --usercap CAP user capability string. (Default: "sx")
2529 **
2530 */
2531 void cmd_test_http(void){
2532 const char *zIpAddr; /* IP address of remote client */
2533 const char *zUserCap;
2534
2535 Th_InitTraceLog();
2536 zUserCap = find_option("usercap",0,1);
2537 if( zUserCap==0 ){
2538 g.useLocalauth = 1;
2539 zUserCap = "sx";
2540 }
2541 login_set_capabilities(zUserCap, 0);
2542 g.httpIn = stdin;
2543 g.httpOut = stdout;
2544 fossil_binary_mode(g.httpOut);
2545 fossil_binary_mode(g.httpIn);
2546 g.zExtRoot = find_option("extroot",0,1);
2547
+24
--- src/main.mk
+++ src/main.mk
@@ -18,10 +18,11 @@
1818
SRC = \
1919
$(SRCDIR)/add.c \
2020
$(SRCDIR)/alerts.c \
2121
$(SRCDIR)/allrepo.c \
2222
$(SRCDIR)/attach.c \
23
+ $(SRCDIR)/backlink.c \
2324
$(SRCDIR)/backoffice.c \
2425
$(SRCDIR)/bag.c \
2526
$(SRCDIR)/bisect.c \
2627
$(SRCDIR)/blob.c \
2728
$(SRCDIR)/branch.c \
@@ -130,10 +131,11 @@
130131
$(SRCDIR)/statrep.c \
131132
$(SRCDIR)/style.c \
132133
$(SRCDIR)/sync.c \
133134
$(SRCDIR)/tag.c \
134135
$(SRCDIR)/tar.c \
136
+ $(SRCDIR)/terminal.c \
135137
$(SRCDIR)/th_main.c \
136138
$(SRCDIR)/timeline.c \
137139
$(SRCDIR)/tkt.c \
138140
$(SRCDIR)/tktsetup.c \
139141
$(SRCDIR)/undo.c \
@@ -250,10 +252,11 @@
250252
TRANS_SRC = \
251253
$(OBJDIR)/add_.c \
252254
$(OBJDIR)/alerts_.c \
253255
$(OBJDIR)/allrepo_.c \
254256
$(OBJDIR)/attach_.c \
257
+ $(OBJDIR)/backlink_.c \
255258
$(OBJDIR)/backoffice_.c \
256259
$(OBJDIR)/bag_.c \
257260
$(OBJDIR)/bisect_.c \
258261
$(OBJDIR)/blob_.c \
259262
$(OBJDIR)/branch_.c \
@@ -362,10 +365,11 @@
362365
$(OBJDIR)/statrep_.c \
363366
$(OBJDIR)/style_.c \
364367
$(OBJDIR)/sync_.c \
365368
$(OBJDIR)/tag_.c \
366369
$(OBJDIR)/tar_.c \
370
+ $(OBJDIR)/terminal_.c \
367371
$(OBJDIR)/th_main_.c \
368372
$(OBJDIR)/timeline_.c \
369373
$(OBJDIR)/tkt_.c \
370374
$(OBJDIR)/tktsetup_.c \
371375
$(OBJDIR)/undo_.c \
@@ -391,10 +395,11 @@
391395
OBJ = \
392396
$(OBJDIR)/add.o \
393397
$(OBJDIR)/alerts.o \
394398
$(OBJDIR)/allrepo.o \
395399
$(OBJDIR)/attach.o \
400
+ $(OBJDIR)/backlink.o \
396401
$(OBJDIR)/backoffice.o \
397402
$(OBJDIR)/bag.o \
398403
$(OBJDIR)/bisect.o \
399404
$(OBJDIR)/blob.o \
400405
$(OBJDIR)/branch.o \
@@ -503,10 +508,11 @@
503508
$(OBJDIR)/statrep.o \
504509
$(OBJDIR)/style.o \
505510
$(OBJDIR)/sync.o \
506511
$(OBJDIR)/tag.o \
507512
$(OBJDIR)/tar.o \
513
+ $(OBJDIR)/terminal.o \
508514
$(OBJDIR)/th_main.o \
509515
$(OBJDIR)/timeline.o \
510516
$(OBJDIR)/tkt.o \
511517
$(OBJDIR)/tktsetup.o \
512518
$(OBJDIR)/undo.o \
@@ -727,10 +733,11 @@
727733
$(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h
728734
$(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h \
729735
$(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \
730736
$(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \
731737
$(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \
738
+ $(OBJDIR)/backlink_.c:$(OBJDIR)/backlink.h \
732739
$(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \
733740
$(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \
734741
$(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \
735742
$(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \
736743
$(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \
@@ -839,10 +846,11 @@
839846
$(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \
840847
$(OBJDIR)/style_.c:$(OBJDIR)/style.h \
841848
$(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \
842849
$(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \
843850
$(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \
851
+ $(OBJDIR)/terminal_.c:$(OBJDIR)/terminal.h \
844852
$(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \
845853
$(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
846854
$(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
847855
$(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
848856
$(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
@@ -900,10 +908,18 @@
900908
901909
$(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h
902910
$(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c
903911
904912
$(OBJDIR)/attach.h: $(OBJDIR)/headers
913
+
914
+$(OBJDIR)/backlink_.c: $(SRCDIR)/backlink.c $(OBJDIR)/translate
915
+ $(OBJDIR)/translate $(SRCDIR)/backlink.c >$@
916
+
917
+$(OBJDIR)/backlink.o: $(OBJDIR)/backlink_.c $(OBJDIR)/backlink.h $(SRCDIR)/config.h
918
+ $(XTCC) -o $(OBJDIR)/backlink.o -c $(OBJDIR)/backlink_.c
919
+
920
+$(OBJDIR)/backlink.h: $(OBJDIR)/headers
905921
906922
$(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(OBJDIR)/translate
907923
$(OBJDIR)/translate $(SRCDIR)/backoffice.c >$@
908924
909925
$(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h
@@ -1796,10 +1812,18 @@
17961812
17971813
$(OBJDIR)/tar.o: $(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h
17981814
$(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c
17991815
18001816
$(OBJDIR)/tar.h: $(OBJDIR)/headers
1817
+
1818
+$(OBJDIR)/terminal_.c: $(SRCDIR)/terminal.c $(OBJDIR)/translate
1819
+ $(OBJDIR)/translate $(SRCDIR)/terminal.c >$@
1820
+
1821
+$(OBJDIR)/terminal.o: $(OBJDIR)/terminal_.c $(OBJDIR)/terminal.h $(SRCDIR)/config.h
1822
+ $(XTCC) -o $(OBJDIR)/terminal.o -c $(OBJDIR)/terminal_.c
1823
+
1824
+$(OBJDIR)/terminal.h: $(OBJDIR)/headers
18011825
18021826
$(OBJDIR)/th_main_.c: $(SRCDIR)/th_main.c $(OBJDIR)/translate
18031827
$(OBJDIR)/translate $(SRCDIR)/th_main.c >$@
18041828
18051829
$(OBJDIR)/th_main.o: $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h
18061830
--- src/main.mk
+++ src/main.mk
@@ -18,10 +18,11 @@
18 SRC = \
19 $(SRCDIR)/add.c \
20 $(SRCDIR)/alerts.c \
21 $(SRCDIR)/allrepo.c \
22 $(SRCDIR)/attach.c \
 
23 $(SRCDIR)/backoffice.c \
24 $(SRCDIR)/bag.c \
25 $(SRCDIR)/bisect.c \
26 $(SRCDIR)/blob.c \
27 $(SRCDIR)/branch.c \
@@ -130,10 +131,11 @@
130 $(SRCDIR)/statrep.c \
131 $(SRCDIR)/style.c \
132 $(SRCDIR)/sync.c \
133 $(SRCDIR)/tag.c \
134 $(SRCDIR)/tar.c \
 
135 $(SRCDIR)/th_main.c \
136 $(SRCDIR)/timeline.c \
137 $(SRCDIR)/tkt.c \
138 $(SRCDIR)/tktsetup.c \
139 $(SRCDIR)/undo.c \
@@ -250,10 +252,11 @@
250 TRANS_SRC = \
251 $(OBJDIR)/add_.c \
252 $(OBJDIR)/alerts_.c \
253 $(OBJDIR)/allrepo_.c \
254 $(OBJDIR)/attach_.c \
 
255 $(OBJDIR)/backoffice_.c \
256 $(OBJDIR)/bag_.c \
257 $(OBJDIR)/bisect_.c \
258 $(OBJDIR)/blob_.c \
259 $(OBJDIR)/branch_.c \
@@ -362,10 +365,11 @@
362 $(OBJDIR)/statrep_.c \
363 $(OBJDIR)/style_.c \
364 $(OBJDIR)/sync_.c \
365 $(OBJDIR)/tag_.c \
366 $(OBJDIR)/tar_.c \
 
367 $(OBJDIR)/th_main_.c \
368 $(OBJDIR)/timeline_.c \
369 $(OBJDIR)/tkt_.c \
370 $(OBJDIR)/tktsetup_.c \
371 $(OBJDIR)/undo_.c \
@@ -391,10 +395,11 @@
391 OBJ = \
392 $(OBJDIR)/add.o \
393 $(OBJDIR)/alerts.o \
394 $(OBJDIR)/allrepo.o \
395 $(OBJDIR)/attach.o \
 
396 $(OBJDIR)/backoffice.o \
397 $(OBJDIR)/bag.o \
398 $(OBJDIR)/bisect.o \
399 $(OBJDIR)/blob.o \
400 $(OBJDIR)/branch.o \
@@ -503,10 +508,11 @@
503 $(OBJDIR)/statrep.o \
504 $(OBJDIR)/style.o \
505 $(OBJDIR)/sync.o \
506 $(OBJDIR)/tag.o \
507 $(OBJDIR)/tar.o \
 
508 $(OBJDIR)/th_main.o \
509 $(OBJDIR)/timeline.o \
510 $(OBJDIR)/tkt.o \
511 $(OBJDIR)/tktsetup.o \
512 $(OBJDIR)/undo.o \
@@ -727,10 +733,11 @@
727 $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h
728 $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h \
729 $(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \
730 $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \
731 $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \
 
732 $(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \
733 $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \
734 $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \
735 $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \
736 $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \
@@ -839,10 +846,11 @@
839 $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \
840 $(OBJDIR)/style_.c:$(OBJDIR)/style.h \
841 $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \
842 $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \
843 $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \
 
844 $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \
845 $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
846 $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
847 $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
848 $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
@@ -900,10 +908,18 @@
900
901 $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h
902 $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c
903
904 $(OBJDIR)/attach.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
 
905
906 $(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(OBJDIR)/translate
907 $(OBJDIR)/translate $(SRCDIR)/backoffice.c >$@
908
909 $(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h
@@ -1796,10 +1812,18 @@
1796
1797 $(OBJDIR)/tar.o: $(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h
1798 $(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c
1799
1800 $(OBJDIR)/tar.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
 
1801
1802 $(OBJDIR)/th_main_.c: $(SRCDIR)/th_main.c $(OBJDIR)/translate
1803 $(OBJDIR)/translate $(SRCDIR)/th_main.c >$@
1804
1805 $(OBJDIR)/th_main.o: $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h
1806
--- src/main.mk
+++ src/main.mk
@@ -18,10 +18,11 @@
18 SRC = \
19 $(SRCDIR)/add.c \
20 $(SRCDIR)/alerts.c \
21 $(SRCDIR)/allrepo.c \
22 $(SRCDIR)/attach.c \
23 $(SRCDIR)/backlink.c \
24 $(SRCDIR)/backoffice.c \
25 $(SRCDIR)/bag.c \
26 $(SRCDIR)/bisect.c \
27 $(SRCDIR)/blob.c \
28 $(SRCDIR)/branch.c \
@@ -130,10 +131,11 @@
131 $(SRCDIR)/statrep.c \
132 $(SRCDIR)/style.c \
133 $(SRCDIR)/sync.c \
134 $(SRCDIR)/tag.c \
135 $(SRCDIR)/tar.c \
136 $(SRCDIR)/terminal.c \
137 $(SRCDIR)/th_main.c \
138 $(SRCDIR)/timeline.c \
139 $(SRCDIR)/tkt.c \
140 $(SRCDIR)/tktsetup.c \
141 $(SRCDIR)/undo.c \
@@ -250,10 +252,11 @@
252 TRANS_SRC = \
253 $(OBJDIR)/add_.c \
254 $(OBJDIR)/alerts_.c \
255 $(OBJDIR)/allrepo_.c \
256 $(OBJDIR)/attach_.c \
257 $(OBJDIR)/backlink_.c \
258 $(OBJDIR)/backoffice_.c \
259 $(OBJDIR)/bag_.c \
260 $(OBJDIR)/bisect_.c \
261 $(OBJDIR)/blob_.c \
262 $(OBJDIR)/branch_.c \
@@ -362,10 +365,11 @@
365 $(OBJDIR)/statrep_.c \
366 $(OBJDIR)/style_.c \
367 $(OBJDIR)/sync_.c \
368 $(OBJDIR)/tag_.c \
369 $(OBJDIR)/tar_.c \
370 $(OBJDIR)/terminal_.c \
371 $(OBJDIR)/th_main_.c \
372 $(OBJDIR)/timeline_.c \
373 $(OBJDIR)/tkt_.c \
374 $(OBJDIR)/tktsetup_.c \
375 $(OBJDIR)/undo_.c \
@@ -391,10 +395,11 @@
395 OBJ = \
396 $(OBJDIR)/add.o \
397 $(OBJDIR)/alerts.o \
398 $(OBJDIR)/allrepo.o \
399 $(OBJDIR)/attach.o \
400 $(OBJDIR)/backlink.o \
401 $(OBJDIR)/backoffice.o \
402 $(OBJDIR)/bag.o \
403 $(OBJDIR)/bisect.o \
404 $(OBJDIR)/blob.o \
405 $(OBJDIR)/branch.o \
@@ -503,10 +508,11 @@
508 $(OBJDIR)/statrep.o \
509 $(OBJDIR)/style.o \
510 $(OBJDIR)/sync.o \
511 $(OBJDIR)/tag.o \
512 $(OBJDIR)/tar.o \
513 $(OBJDIR)/terminal.o \
514 $(OBJDIR)/th_main.o \
515 $(OBJDIR)/timeline.o \
516 $(OBJDIR)/tkt.o \
517 $(OBJDIR)/tktsetup.o \
518 $(OBJDIR)/undo.o \
@@ -727,10 +733,11 @@
733 $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(OBJDIR)/makeheaders $(OBJDIR)/VERSION.h
734 $(OBJDIR)/makeheaders $(OBJDIR)/add_.c:$(OBJDIR)/add.h \
735 $(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \
736 $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \
737 $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \
738 $(OBJDIR)/backlink_.c:$(OBJDIR)/backlink.h \
739 $(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \
740 $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \
741 $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \
742 $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \
743 $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \
@@ -839,10 +846,11 @@
846 $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \
847 $(OBJDIR)/style_.c:$(OBJDIR)/style.h \
848 $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \
849 $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \
850 $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \
851 $(OBJDIR)/terminal_.c:$(OBJDIR)/terminal.h \
852 $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \
853 $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
854 $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
855 $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
856 $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
@@ -900,10 +908,18 @@
908
909 $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h
910 $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c
911
912 $(OBJDIR)/attach.h: $(OBJDIR)/headers
913
914 $(OBJDIR)/backlink_.c: $(SRCDIR)/backlink.c $(OBJDIR)/translate
915 $(OBJDIR)/translate $(SRCDIR)/backlink.c >$@
916
917 $(OBJDIR)/backlink.o: $(OBJDIR)/backlink_.c $(OBJDIR)/backlink.h $(SRCDIR)/config.h
918 $(XTCC) -o $(OBJDIR)/backlink.o -c $(OBJDIR)/backlink_.c
919
920 $(OBJDIR)/backlink.h: $(OBJDIR)/headers
921
922 $(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(OBJDIR)/translate
923 $(OBJDIR)/translate $(SRCDIR)/backoffice.c >$@
924
925 $(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h
@@ -1796,10 +1812,18 @@
1812
1813 $(OBJDIR)/tar.o: $(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h
1814 $(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c
1815
1816 $(OBJDIR)/tar.h: $(OBJDIR)/headers
1817
1818 $(OBJDIR)/terminal_.c: $(SRCDIR)/terminal.c $(OBJDIR)/translate
1819 $(OBJDIR)/translate $(SRCDIR)/terminal.c >$@
1820
1821 $(OBJDIR)/terminal.o: $(OBJDIR)/terminal_.c $(OBJDIR)/terminal.h $(SRCDIR)/config.h
1822 $(XTCC) -o $(OBJDIR)/terminal.o -c $(OBJDIR)/terminal_.c
1823
1824 $(OBJDIR)/terminal.h: $(OBJDIR)/headers
1825
1826 $(OBJDIR)/th_main_.c: $(SRCDIR)/th_main.c $(OBJDIR)/translate
1827 $(OBJDIR)/translate $(SRCDIR)/th_main.c >$@
1828
1829 $(OBJDIR)/th_main.o: $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h
1830
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -29,10 +29,11 @@
2929
set src {
3030
add
3131
alerts
3232
allrepo
3333
attach
34
+ backlink
3435
backoffice
3536
bag
3637
bisect
3738
blob
3839
branch
@@ -140,10 +141,11 @@
140141
statrep
141142
style
142143
sync
143144
tag
144145
tar
146
+ terminal
145147
th_main
146148
timeline
147149
tkt
148150
tktsetup
149151
undo
@@ -713,11 +715,11 @@
713715
#### The directories where the OpenSSL include and library files are located.
714716
# The recommended usage here is to use the Sysinternals junction tool
715717
# to create a hard link between an "openssl-1.x" sub-directory of the
716718
# Fossil source code directory and the target OpenSSL source directory.
717719
#
718
-OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1f
720
+OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1g
719721
OPENSSLINCDIR = $(OPENSSLDIR)/include
720722
OPENSSLLIBDIR = $(OPENSSLDIR)
721723
722724
#### Either the directory where the Tcl library is installed or the Tcl
723725
# source code directory resides (depending on the value of the macro
@@ -1570,11 +1572,11 @@
15701572
!ifndef USE_SEE
15711573
USE_SEE = 0
15721574
!endif
15731575
15741576
!if $(FOSSIL_ENABLE_SSL)!=0
1575
-SSLDIR = $(B)\compat\openssl-1.1.1f
1577
+SSLDIR = $(B)\compat\openssl-1.1.1g
15761578
SSLINCDIR = $(SSLDIR)\include
15771579
!if $(FOSSIL_DYNAMIC_BUILD)!=0
15781580
SSLLIBDIR = $(SSLDIR)
15791581
!else
15801582
SSLLIBDIR = $(SSLDIR)
15811583
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -29,10 +29,11 @@
29 set src {
30 add
31 alerts
32 allrepo
33 attach
 
34 backoffice
35 bag
36 bisect
37 blob
38 branch
@@ -140,10 +141,11 @@
140 statrep
141 style
142 sync
143 tag
144 tar
 
145 th_main
146 timeline
147 tkt
148 tktsetup
149 undo
@@ -713,11 +715,11 @@
713 #### The directories where the OpenSSL include and library files are located.
714 # The recommended usage here is to use the Sysinternals junction tool
715 # to create a hard link between an "openssl-1.x" sub-directory of the
716 # Fossil source code directory and the target OpenSSL source directory.
717 #
718 OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1f
719 OPENSSLINCDIR = $(OPENSSLDIR)/include
720 OPENSSLLIBDIR = $(OPENSSLDIR)
721
722 #### Either the directory where the Tcl library is installed or the Tcl
723 # source code directory resides (depending on the value of the macro
@@ -1570,11 +1572,11 @@
1570 !ifndef USE_SEE
1571 USE_SEE = 0
1572 !endif
1573
1574 !if $(FOSSIL_ENABLE_SSL)!=0
1575 SSLDIR = $(B)\compat\openssl-1.1.1f
1576 SSLINCDIR = $(SSLDIR)\include
1577 !if $(FOSSIL_DYNAMIC_BUILD)!=0
1578 SSLLIBDIR = $(SSLDIR)
1579 !else
1580 SSLLIBDIR = $(SSLDIR)
1581
--- src/makemake.tcl
+++ src/makemake.tcl
@@ -29,10 +29,11 @@
29 set src {
30 add
31 alerts
32 allrepo
33 attach
34 backlink
35 backoffice
36 bag
37 bisect
38 blob
39 branch
@@ -140,10 +141,11 @@
141 statrep
142 style
143 sync
144 tag
145 tar
146 terminal
147 th_main
148 timeline
149 tkt
150 tktsetup
151 undo
@@ -713,11 +715,11 @@
715 #### The directories where the OpenSSL include and library files are located.
716 # The recommended usage here is to use the Sysinternals junction tool
717 # to create a hard link between an "openssl-1.x" sub-directory of the
718 # Fossil source code directory and the target OpenSSL source directory.
719 #
720 OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1g
721 OPENSSLINCDIR = $(OPENSSLDIR)/include
722 OPENSSLLIBDIR = $(OPENSSLDIR)
723
724 #### Either the directory where the Tcl library is installed or the Tcl
725 # source code directory resides (depending on the value of the macro
@@ -1570,11 +1572,11 @@
1572 !ifndef USE_SEE
1573 USE_SEE = 0
1574 !endif
1575
1576 !if $(FOSSIL_ENABLE_SSL)!=0
1577 SSLDIR = $(B)\compat\openssl-1.1.1g
1578 SSLINCDIR = $(SSLDIR)\include
1579 !if $(FOSSIL_DYNAMIC_BUILD)!=0
1580 SSLLIBDIR = $(SSLDIR)
1581 !else
1582 SSLLIBDIR = $(SSLDIR)
1583
+60 -13
--- src/manifest.c
+++ src/manifest.c
@@ -1828,19 +1828,30 @@
18281828
void manifest_crosslink_begin(void){
18291829
assert( manifest_crosslink_busy==0 );
18301830
manifest_crosslink_busy = 1;
18311831
db_begin_transaction();
18321832
db_multi_exec(
1833
- "CREATE TEMP TABLE pending_tkt(uuid TEXT UNIQUE);"
1833
+ "CREATE TEMP TABLE pending_xlink(id TEXT PRIMARY KEY)WITHOUT ROWID;"
18341834
"CREATE TEMP TABLE time_fudge("
18351835
" mid INTEGER PRIMARY KEY," /* The rid of a manifest */
18361836
" m1 REAL," /* The timestamp on mid */
18371837
" cid INTEGER," /* A child or mid */
18381838
" m2 REAL" /* Timestamp on the child */
18391839
");"
18401840
);
18411841
}
1842
+
1843
+/*
1844
+** Add a new entry to the pending_xlink table.
1845
+*/
1846
+static void add_pending_crosslink(char cType, const char *zId){
1847
+ assert( manifest_crosslink_busy==1 );
1848
+ db_multi_exec(
1849
+ "INSERT OR IGNORE INTO pending_xlink VALUES('%c%q')",
1850
+ cType, zId
1851
+ );
1852
+}
18421853
18431854
#if INTERFACE
18441855
/* Timestamps might be adjusted slightly to ensure that check-ins appear
18451856
** on the timeline in chronological order. This is the maximum amount
18461857
** of the adjustment window, in days.
@@ -1879,20 +1890,28 @@
18791890
int rid = db_column_int(&q,0);
18801891
const char *zValue = db_column_text(&q,1);
18811892
manifest_reparent_checkin(rid, zValue);
18821893
}
18831894
db_finalize(&q);
1884
- db_prepare(&q, "SELECT uuid FROM pending_tkt");
1895
+ db_prepare(&q, "SELECT id FROM pending_xlink");
18851896
while( db_step(&q)==SQLITE_ROW ){
1886
- const char *zUuid = db_column_text(&q, 0);
1887
- ticket_rebuild_entry(zUuid);
1888
- if( permitHooks && rc==TH_OK ){
1889
- rc = xfer_run_script(zScript, zUuid, 0);
1897
+ const char *zId = db_column_text(&q, 0);
1898
+ char cType;
1899
+ if( zId==0 || zId[0]==0 ) continue;
1900
+ cType = zId[0];
1901
+ zId++;
1902
+ if( cType=='t' ){
1903
+ ticket_rebuild_entry(zId);
1904
+ if( permitHooks && rc==TH_OK ){
1905
+ rc = xfer_run_script(zScript, zId, 0);
1906
+ }
1907
+ }else if( cType=='w' ){
1908
+ backlink_wiki_refresh(zId);
18901909
}
18911910
}
18921911
db_finalize(&q);
1893
- db_multi_exec("DROP TABLE pending_tkt");
1912
+ db_multi_exec("DROP TABLE pending_xlink");
18941913
18951914
/* If multiple check-ins happen close together in time, adjust their
18961915
** times by a few milliseconds to make sure they appear in chronological
18971916
** order.
18981917
*/
@@ -2162,11 +2181,11 @@
21622181
TAG_USER, rid,
21632182
TAG_COMMENT, rid, p->rDate
21642183
);
21652184
zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event"
21662185
" WHERE rowid=last_insert_rowid()");
2167
- wiki_extract_links(zCom, rid, 0, p->rDate, 1, WIKI_INLINE);
2186
+ backlink_extract(zCom, 0, rid, BKLNK_COMMENT, p->rDate, 1);
21682187
fossil_free(zCom);
21692188
21702189
/* If this is a delta-manifest, record the fact that this repository
21712190
** contains delta manifests, to free the "commit" logic to generate
21722191
** new delta manifests.
@@ -2227,10 +2246,11 @@
22272246
if( p->type==CFTYPE_WIKI ){
22282247
char *zTag = mprintf("wiki-%s", p->zWikiTitle);
22292248
int tagid = tag_findid(zTag, 1);
22302249
int prior;
22312250
char *zComment;
2251
+ const char *zPrefix;
22322252
int nWiki;
22332253
char zLength[40];
22342254
while( fossil_isspace(p->zWiki[0]) ) p->zWiki++;
22352255
nWiki = strlen(p->zWiki);
22362256
sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
@@ -2243,16 +2263,44 @@
22432263
tagid, p->rDate
22442264
);
22452265
if( prior ){
22462266
content_deltify(prior, &rid, 1, 0);
22472267
}
2248
- if( nWiki>0 ){
2249
- zComment = mprintf("Changes to wiki page [%h]", p->zWikiTitle);
2268
+ if( nWiki<=0 ){
2269
+ zPrefix = "Deleted";
2270
+ }else if( !prior ){
2271
+ zPrefix = "Added";
22502272
}else{
2251
- zComment = mprintf("Deleted wiki page [%h]", p->zWikiTitle);
2273
+ zPrefix = "Changes to";
2274
+ }
2275
+ switch( wiki_page_type(p->zWikiTitle) ){
2276
+ case WIKITYPE_CHECKIN: {
2277
+ zComment = mprintf("%s wiki for check-in [%S]", zPrefix,
2278
+ p->zWikiTitle+8);
2279
+ break;
2280
+ }
2281
+ case WIKITYPE_BRANCH: {
2282
+ zComment = mprintf("%s wiki for branch [/timeline?r=%t|%h]",
2283
+ zPrefix, p->zWikiTitle+7, p->zWikiTitle+7);
2284
+ break;
2285
+ }
2286
+ case WIKITYPE_TAG: {
2287
+ zComment = mprintf("%s wiki for tag [/timeline?t=%t|%h]",
2288
+ zPrefix, p->zWikiTitle+4, p->zWikiTitle+4);
2289
+ break;
2290
+ }
2291
+ default: {
2292
+ zComment = mprintf("%s wiki page [%h]", zPrefix, p->zWikiTitle);
2293
+ break;
2294
+ }
22522295
}
22532296
search_doc_touch('w',rid,p->zWikiTitle);
2297
+ if( manifest_crosslink_busy ){
2298
+ add_pending_crosslink('w',p->zWikiTitle);
2299
+ }else{
2300
+ backlink_wiki_refresh(p->zWikiTitle);
2301
+ }
22542302
db_multi_exec(
22552303
"REPLACE INTO event(type,mtime,objid,user,comment,"
22562304
" bgcolor,euser,ecomment)"
22572305
"VALUES('w',%.17g,%d,%Q,%Q,"
22582306
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1),"
@@ -2349,12 +2397,11 @@
23492397
Stmt qatt;
23502398
assert( manifest_crosslink_busy==1 );
23512399
zTag = mprintf("tkt-%s", p->zTicketUuid);
23522400
tag_insert(zTag, 1, 0, rid, p->rDate, rid);
23532401
fossil_free(zTag);
2354
- db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
2355
- p->zTicketUuid);
2402
+ add_pending_crosslink('t',p->zTicketUuid);
23562403
/* Locate and update comment for any attachments */
23572404
db_prepare(&qatt,
23582405
"SELECT attachid, src, target, filename FROM attachment"
23592406
" WHERE target=%Q",
23602407
p->zTicketUuid
23612408
--- src/manifest.c
+++ src/manifest.c
@@ -1828,19 +1828,30 @@
1828 void manifest_crosslink_begin(void){
1829 assert( manifest_crosslink_busy==0 );
1830 manifest_crosslink_busy = 1;
1831 db_begin_transaction();
1832 db_multi_exec(
1833 "CREATE TEMP TABLE pending_tkt(uuid TEXT UNIQUE);"
1834 "CREATE TEMP TABLE time_fudge("
1835 " mid INTEGER PRIMARY KEY," /* The rid of a manifest */
1836 " m1 REAL," /* The timestamp on mid */
1837 " cid INTEGER," /* A child or mid */
1838 " m2 REAL" /* Timestamp on the child */
1839 ");"
1840 );
1841 }
 
 
 
 
 
 
 
 
 
 
 
1842
1843 #if INTERFACE
1844 /* Timestamps might be adjusted slightly to ensure that check-ins appear
1845 ** on the timeline in chronological order. This is the maximum amount
1846 ** of the adjustment window, in days.
@@ -1879,20 +1890,28 @@
1879 int rid = db_column_int(&q,0);
1880 const char *zValue = db_column_text(&q,1);
1881 manifest_reparent_checkin(rid, zValue);
1882 }
1883 db_finalize(&q);
1884 db_prepare(&q, "SELECT uuid FROM pending_tkt");
1885 while( db_step(&q)==SQLITE_ROW ){
1886 const char *zUuid = db_column_text(&q, 0);
1887 ticket_rebuild_entry(zUuid);
1888 if( permitHooks && rc==TH_OK ){
1889 rc = xfer_run_script(zScript, zUuid, 0);
 
 
 
 
 
 
 
 
1890 }
1891 }
1892 db_finalize(&q);
1893 db_multi_exec("DROP TABLE pending_tkt");
1894
1895 /* If multiple check-ins happen close together in time, adjust their
1896 ** times by a few milliseconds to make sure they appear in chronological
1897 ** order.
1898 */
@@ -2162,11 +2181,11 @@
2162 TAG_USER, rid,
2163 TAG_COMMENT, rid, p->rDate
2164 );
2165 zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event"
2166 " WHERE rowid=last_insert_rowid()");
2167 wiki_extract_links(zCom, rid, 0, p->rDate, 1, WIKI_INLINE);
2168 fossil_free(zCom);
2169
2170 /* If this is a delta-manifest, record the fact that this repository
2171 ** contains delta manifests, to free the "commit" logic to generate
2172 ** new delta manifests.
@@ -2227,10 +2246,11 @@
2227 if( p->type==CFTYPE_WIKI ){
2228 char *zTag = mprintf("wiki-%s", p->zWikiTitle);
2229 int tagid = tag_findid(zTag, 1);
2230 int prior;
2231 char *zComment;
 
2232 int nWiki;
2233 char zLength[40];
2234 while( fossil_isspace(p->zWiki[0]) ) p->zWiki++;
2235 nWiki = strlen(p->zWiki);
2236 sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
@@ -2243,16 +2263,44 @@
2243 tagid, p->rDate
2244 );
2245 if( prior ){
2246 content_deltify(prior, &rid, 1, 0);
2247 }
2248 if( nWiki>0 ){
2249 zComment = mprintf("Changes to wiki page [%h]", p->zWikiTitle);
 
 
2250 }else{
2251 zComment = mprintf("Deleted wiki page [%h]", p->zWikiTitle);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2252 }
2253 search_doc_touch('w',rid,p->zWikiTitle);
 
 
 
 
 
2254 db_multi_exec(
2255 "REPLACE INTO event(type,mtime,objid,user,comment,"
2256 " bgcolor,euser,ecomment)"
2257 "VALUES('w',%.17g,%d,%Q,%Q,"
2258 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1),"
@@ -2349,12 +2397,11 @@
2349 Stmt qatt;
2350 assert( manifest_crosslink_busy==1 );
2351 zTag = mprintf("tkt-%s", p->zTicketUuid);
2352 tag_insert(zTag, 1, 0, rid, p->rDate, rid);
2353 fossil_free(zTag);
2354 db_multi_exec("INSERT OR IGNORE INTO pending_tkt VALUES(%Q)",
2355 p->zTicketUuid);
2356 /* Locate and update comment for any attachments */
2357 db_prepare(&qatt,
2358 "SELECT attachid, src, target, filename FROM attachment"
2359 " WHERE target=%Q",
2360 p->zTicketUuid
2361
--- src/manifest.c
+++ src/manifest.c
@@ -1828,19 +1828,30 @@
1828 void manifest_crosslink_begin(void){
1829 assert( manifest_crosslink_busy==0 );
1830 manifest_crosslink_busy = 1;
1831 db_begin_transaction();
1832 db_multi_exec(
1833 "CREATE TEMP TABLE pending_xlink(id TEXT PRIMARY KEY)WITHOUT ROWID;"
1834 "CREATE TEMP TABLE time_fudge("
1835 " mid INTEGER PRIMARY KEY," /* The rid of a manifest */
1836 " m1 REAL," /* The timestamp on mid */
1837 " cid INTEGER," /* A child or mid */
1838 " m2 REAL" /* Timestamp on the child */
1839 ");"
1840 );
1841 }
1842
1843 /*
1844 ** Add a new entry to the pending_xlink table.
1845 */
1846 static void add_pending_crosslink(char cType, const char *zId){
1847 assert( manifest_crosslink_busy==1 );
1848 db_multi_exec(
1849 "INSERT OR IGNORE INTO pending_xlink VALUES('%c%q')",
1850 cType, zId
1851 );
1852 }
1853
1854 #if INTERFACE
1855 /* Timestamps might be adjusted slightly to ensure that check-ins appear
1856 ** on the timeline in chronological order. This is the maximum amount
1857 ** of the adjustment window, in days.
@@ -1879,20 +1890,28 @@
1890 int rid = db_column_int(&q,0);
1891 const char *zValue = db_column_text(&q,1);
1892 manifest_reparent_checkin(rid, zValue);
1893 }
1894 db_finalize(&q);
1895 db_prepare(&q, "SELECT id FROM pending_xlink");
1896 while( db_step(&q)==SQLITE_ROW ){
1897 const char *zId = db_column_text(&q, 0);
1898 char cType;
1899 if( zId==0 || zId[0]==0 ) continue;
1900 cType = zId[0];
1901 zId++;
1902 if( cType=='t' ){
1903 ticket_rebuild_entry(zId);
1904 if( permitHooks && rc==TH_OK ){
1905 rc = xfer_run_script(zScript, zId, 0);
1906 }
1907 }else if( cType=='w' ){
1908 backlink_wiki_refresh(zId);
1909 }
1910 }
1911 db_finalize(&q);
1912 db_multi_exec("DROP TABLE pending_xlink");
1913
1914 /* If multiple check-ins happen close together in time, adjust their
1915 ** times by a few milliseconds to make sure they appear in chronological
1916 ** order.
1917 */
@@ -2162,11 +2181,11 @@
2181 TAG_USER, rid,
2182 TAG_COMMENT, rid, p->rDate
2183 );
2184 zCom = db_text(0, "SELECT coalesce(ecomment, comment) FROM event"
2185 " WHERE rowid=last_insert_rowid()");
2186 backlink_extract(zCom, 0, rid, BKLNK_COMMENT, p->rDate, 1);
2187 fossil_free(zCom);
2188
2189 /* If this is a delta-manifest, record the fact that this repository
2190 ** contains delta manifests, to free the "commit" logic to generate
2191 ** new delta manifests.
@@ -2227,10 +2246,11 @@
2246 if( p->type==CFTYPE_WIKI ){
2247 char *zTag = mprintf("wiki-%s", p->zWikiTitle);
2248 int tagid = tag_findid(zTag, 1);
2249 int prior;
2250 char *zComment;
2251 const char *zPrefix;
2252 int nWiki;
2253 char zLength[40];
2254 while( fossil_isspace(p->zWiki[0]) ) p->zWiki++;
2255 nWiki = strlen(p->zWiki);
2256 sqlite3_snprintf(sizeof(zLength), zLength, "%d", nWiki);
@@ -2243,16 +2263,44 @@
2263 tagid, p->rDate
2264 );
2265 if( prior ){
2266 content_deltify(prior, &rid, 1, 0);
2267 }
2268 if( nWiki<=0 ){
2269 zPrefix = "Deleted";
2270 }else if( !prior ){
2271 zPrefix = "Added";
2272 }else{
2273 zPrefix = "Changes to";
2274 }
2275 switch( wiki_page_type(p->zWikiTitle) ){
2276 case WIKITYPE_CHECKIN: {
2277 zComment = mprintf("%s wiki for check-in [%S]", zPrefix,
2278 p->zWikiTitle+8);
2279 break;
2280 }
2281 case WIKITYPE_BRANCH: {
2282 zComment = mprintf("%s wiki for branch [/timeline?r=%t|%h]",
2283 zPrefix, p->zWikiTitle+7, p->zWikiTitle+7);
2284 break;
2285 }
2286 case WIKITYPE_TAG: {
2287 zComment = mprintf("%s wiki for tag [/timeline?t=%t|%h]",
2288 zPrefix, p->zWikiTitle+4, p->zWikiTitle+4);
2289 break;
2290 }
2291 default: {
2292 zComment = mprintf("%s wiki page [%h]", zPrefix, p->zWikiTitle);
2293 break;
2294 }
2295 }
2296 search_doc_touch('w',rid,p->zWikiTitle);
2297 if( manifest_crosslink_busy ){
2298 add_pending_crosslink('w',p->zWikiTitle);
2299 }else{
2300 backlink_wiki_refresh(p->zWikiTitle);
2301 }
2302 db_multi_exec(
2303 "REPLACE INTO event(type,mtime,objid,user,comment,"
2304 " bgcolor,euser,ecomment)"
2305 "VALUES('w',%.17g,%d,%Q,%Q,"
2306 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=%d AND tagtype>1),"
@@ -2349,12 +2397,11 @@
2397 Stmt qatt;
2398 assert( manifest_crosslink_busy==1 );
2399 zTag = mprintf("tkt-%s", p->zTicketUuid);
2400 tag_insert(zTag, 1, 0, rid, p->rDate, rid);
2401 fossil_free(zTag);
2402 add_pending_crosslink('t',p->zTicketUuid);
 
2403 /* Locate and update comment for any attachments */
2404 db_prepare(&qatt,
2405 "SELECT attachid, src, target, filename FROM attachment"
2406 " WHERE target=%Q",
2407 p->zTicketUuid
2408
--- src/merge.c
+++ src/merge.c
@@ -405,10 +405,17 @@
405405
return;
406406
}
407407
if( integrateFlag && !is_a_leaf(mid)){
408408
fossil_warning("ignoring --integrate: %s is not a leaf", g.argv[2]);
409409
integrateFlag = 0;
410
+ }
411
+ if( integrateFlag && content_is_private(mid) ){
412
+ fossil_warning(
413
+ "ignoring --integrate: %s is on a private branch"
414
+ "\n Use \"fossil amend --close\" (after commit) to close the leaf.",
415
+ g.argv[2]);
416
+ integrateFlag = 0;
410417
}
411418
if( verboseFlag ){
412419
print_checkin_description(mid, 12,
413420
integrateFlag ? "integrate:" : "merge-from:");
414421
print_checkin_description(pid, 12, "baseline:");
415422
--- src/merge.c
+++ src/merge.c
@@ -405,10 +405,17 @@
405 return;
406 }
407 if( integrateFlag && !is_a_leaf(mid)){
408 fossil_warning("ignoring --integrate: %s is not a leaf", g.argv[2]);
409 integrateFlag = 0;
 
 
 
 
 
 
 
410 }
411 if( verboseFlag ){
412 print_checkin_description(mid, 12,
413 integrateFlag ? "integrate:" : "merge-from:");
414 print_checkin_description(pid, 12, "baseline:");
415
--- src/merge.c
+++ src/merge.c
@@ -405,10 +405,17 @@
405 return;
406 }
407 if( integrateFlag && !is_a_leaf(mid)){
408 fossil_warning("ignoring --integrate: %s is not a leaf", g.argv[2]);
409 integrateFlag = 0;
410 }
411 if( integrateFlag && content_is_private(mid) ){
412 fossil_warning(
413 "ignoring --integrate: %s is on a private branch"
414 "\n Use \"fossil amend --close\" (after commit) to close the leaf.",
415 g.argv[2]);
416 integrateFlag = 0;
417 }
418 if( verboseFlag ){
419 print_checkin_description(mid, 12,
420 integrateFlag ? "integrate:" : "merge-from:");
421 print_checkin_description(pid, 12, "baseline:");
422
+1 -1
--- src/merge3.c
+++ src/merge3.c
@@ -139,11 +139,11 @@
139139
** Text of boundary markers for merge conflicts.
140140
*/
141141
static const char *const mergeMarker[] = {
142142
/*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/
143143
"<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<\n",
144
- "======= COMMON ANCESTOR content follows ============================\n",
144
+ "||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||||||\n",
145145
"======= MERGED IN content follows ==================================\n",
146146
">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
147147
};
148148
149149
150150
--- src/merge3.c
+++ src/merge3.c
@@ -139,11 +139,11 @@
139 ** Text of boundary markers for merge conflicts.
140 */
141 static const char *const mergeMarker[] = {
142 /*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/
143 "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<\n",
144 "======= COMMON ANCESTOR content follows ============================\n",
145 "======= MERGED IN content follows ==================================\n",
146 ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
147 };
148
149
150
--- src/merge3.c
+++ src/merge3.c
@@ -139,11 +139,11 @@
139 ** Text of boundary markers for merge conflicts.
140 */
141 static const char *const mergeMarker[] = {
142 /*123456789 123456789 123456789 123456789 123456789 123456789 123456789*/
143 "<<<<<<< BEGIN MERGE CONFLICT: local copy shown first <<<<<<<<<<<<<<<\n",
144 "||||||| COMMON ANCESTOR content follows ||||||||||||||||||||||||||||\n",
145 "======= MERGED IN content follows ==================================\n",
146 ">>>>>>> END MERGE CONFLICT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"
147 };
148
149
150
+57 -3
--- src/mkversion.c
+++ src/mkversion.c
@@ -8,27 +8,60 @@
88
** Note that the manifest.uuid and manifest files are generated by Fossil.
99
*/
1010
#include <stdio.h>
1111
#include <string.h>
1212
#include <stdlib.h>
13
+#include <ctype.h>
14
+#include <time.h>
1315
1416
static FILE *open_for_reading(const char *zFilename){
1517
FILE *f = fopen(zFilename, "r");
1618
if( f==0 ){
1719
fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename);
1820
exit(1);
1921
}
2022
return f;
2123
}
24
+
25
+/*
26
+** Given an arbitrary-length input string key zIn, generate
27
+** an N-byte hexadecimal hash of that string into zOut.
28
+*/
29
+static void hash(const char *zIn, int N, char *zOut){
30
+ unsigned char i, j, t;
31
+ int m, n;
32
+ unsigned char s[256];
33
+ for(m=0; m<256; m++){ s[m] = m; }
34
+ for(j=0, m=n=0; m<256; m++, n++){
35
+ j += s[m] + zIn[n];
36
+ if( zIn[n]==0 ){ n = -1; }
37
+ t = s[j];
38
+ s[j] = s[m];
39
+ s[m] = t;
40
+ }
41
+ i = j = 0;
42
+ for(n=0; n<N-2; n+=2){
43
+ i++;
44
+ t = s[i];
45
+ j += t;
46
+ s[i] = s[j];
47
+ s[j] = t;
48
+ t += s[i];
49
+ zOut[n] = "0123456789abcdef"[(t>>4)&0xf];
50
+ zOut[n+1] = "0123456789abcdef"[t&0xf];
51
+ }
52
+ zOut[n] = 0;
53
+}
2254
2355
int main(int argc, char *argv[]){
2456
FILE *m,*u,*v;
2557
char *z;
2658
#if defined(__DMC__) /* e.g. 0x857 */
2759
int i = 0;
2860
#endif
2961
int j = 0, x = 0, d = 0;
62
+ size_t n;
3063
int vn[3];
3164
char b[1000];
3265
char vx[1000];
3366
if( argc!=4 ){
3467
fprintf(stderr, "Usage: %s manifest.uuid manifest VERSION\n", argv[0]);
@@ -44,16 +77,37 @@
4477
fclose(u);
4578
for(z=b; z[0] && z[0]!='\r' && z[0]!='\n'; z++){}
4679
*z = 0;
4780
printf("#define MANIFEST_UUID \"%s\"\n",b);
4881
printf("#define MANIFEST_VERSION \"[%10.10s]\"\n",b);
82
+ n = strlen(b);
83
+ if( n + 50 < sizeof(b) ){
84
+ sprintf(b+n, "%d", (int)time(0));
85
+ hash(b,33,vx);
86
+ printf("#define FOSSIL_BUILD_HASH \"%s\"\n", vx);
87
+ }
4988
m = open_for_reading(argv[2]);
5089
while(b == fgets(b, sizeof(b)-1,m)){
51
- if(0 == strncmp("D ",b,2)){
52
- printf("#define MANIFEST_DATE \"%.10s %.8s\"\n",b+2,b+13);
53
- printf("#define MANIFEST_YEAR \"%.4s\"\n",b+2);
90
+ if(0 == strncmp("D ",b,2)){
91
+ int k, n;
92
+ char zDateNum[30];
93
+ printf("#define MANIFEST_DATE \"%.10s %.8s\"\n",b+2,b+13);
94
+ printf("#define MANIFEST_YEAR \"%.4s\"\n",b+2);
95
+ n = 0;
96
+ for(k=0; k<10; k++){
97
+ if( isdigit(b[k+2]) ) zDateNum[n++] = b[k+2];
98
+ }
99
+ zDateNum[n] = 0;
100
+ printf("#define MANIFEST_NUMERIC_DATE %s\n", zDateNum);
101
+ n = 0;
102
+ for(k=0; k<8; k++){
103
+ if( isdigit(b[k+13]) ) zDateNum[n++] = b[k+13];
54104
}
105
+ zDateNum[n] = 0;
106
+ for(k=0; zDateNum[k]=='0'; k++){}
107
+ printf("#define MANIFEST_NUMERIC_TIME %s\n", zDateNum+k);
108
+ }
55109
}
56110
fclose(m);
57111
v = open_for_reading(argv[3]);
58112
if( fgets(b, sizeof(b)-1,v)==0 ){
59113
fprintf(stderr, "malformed VERSION file: %s\n", argv[3]);
60114
--- src/mkversion.c
+++ src/mkversion.c
@@ -8,27 +8,60 @@
8 ** Note that the manifest.uuid and manifest files are generated by Fossil.
9 */
10 #include <stdio.h>
11 #include <string.h>
12 #include <stdlib.h>
 
 
13
14 static FILE *open_for_reading(const char *zFilename){
15 FILE *f = fopen(zFilename, "r");
16 if( f==0 ){
17 fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename);
18 exit(1);
19 }
20 return f;
21 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
23 int main(int argc, char *argv[]){
24 FILE *m,*u,*v;
25 char *z;
26 #if defined(__DMC__) /* e.g. 0x857 */
27 int i = 0;
28 #endif
29 int j = 0, x = 0, d = 0;
 
30 int vn[3];
31 char b[1000];
32 char vx[1000];
33 if( argc!=4 ){
34 fprintf(stderr, "Usage: %s manifest.uuid manifest VERSION\n", argv[0]);
@@ -44,16 +77,37 @@
44 fclose(u);
45 for(z=b; z[0] && z[0]!='\r' && z[0]!='\n'; z++){}
46 *z = 0;
47 printf("#define MANIFEST_UUID \"%s\"\n",b);
48 printf("#define MANIFEST_VERSION \"[%10.10s]\"\n",b);
 
 
 
 
 
 
49 m = open_for_reading(argv[2]);
50 while(b == fgets(b, sizeof(b)-1,m)){
51 if(0 == strncmp("D ",b,2)){
52 printf("#define MANIFEST_DATE \"%.10s %.8s\"\n",b+2,b+13);
53 printf("#define MANIFEST_YEAR \"%.4s\"\n",b+2);
 
 
 
 
 
 
 
 
 
 
 
54 }
 
 
 
 
55 }
56 fclose(m);
57 v = open_for_reading(argv[3]);
58 if( fgets(b, sizeof(b)-1,v)==0 ){
59 fprintf(stderr, "malformed VERSION file: %s\n", argv[3]);
60
--- src/mkversion.c
+++ src/mkversion.c
@@ -8,27 +8,60 @@
8 ** Note that the manifest.uuid and manifest files are generated by Fossil.
9 */
10 #include <stdio.h>
11 #include <string.h>
12 #include <stdlib.h>
13 #include <ctype.h>
14 #include <time.h>
15
16 static FILE *open_for_reading(const char *zFilename){
17 FILE *f = fopen(zFilename, "r");
18 if( f==0 ){
19 fprintf(stderr, "cannot open \"%s\" for reading\n", zFilename);
20 exit(1);
21 }
22 return f;
23 }
24
25 /*
26 ** Given an arbitrary-length input string key zIn, generate
27 ** an N-byte hexadecimal hash of that string into zOut.
28 */
29 static void hash(const char *zIn, int N, char *zOut){
30 unsigned char i, j, t;
31 int m, n;
32 unsigned char s[256];
33 for(m=0; m<256; m++){ s[m] = m; }
34 for(j=0, m=n=0; m<256; m++, n++){
35 j += s[m] + zIn[n];
36 if( zIn[n]==0 ){ n = -1; }
37 t = s[j];
38 s[j] = s[m];
39 s[m] = t;
40 }
41 i = j = 0;
42 for(n=0; n<N-2; n+=2){
43 i++;
44 t = s[i];
45 j += t;
46 s[i] = s[j];
47 s[j] = t;
48 t += s[i];
49 zOut[n] = "0123456789abcdef"[(t>>4)&0xf];
50 zOut[n+1] = "0123456789abcdef"[t&0xf];
51 }
52 zOut[n] = 0;
53 }
54
55 int main(int argc, char *argv[]){
56 FILE *m,*u,*v;
57 char *z;
58 #if defined(__DMC__) /* e.g. 0x857 */
59 int i = 0;
60 #endif
61 int j = 0, x = 0, d = 0;
62 size_t n;
63 int vn[3];
64 char b[1000];
65 char vx[1000];
66 if( argc!=4 ){
67 fprintf(stderr, "Usage: %s manifest.uuid manifest VERSION\n", argv[0]);
@@ -44,16 +77,37 @@
77 fclose(u);
78 for(z=b; z[0] && z[0]!='\r' && z[0]!='\n'; z++){}
79 *z = 0;
80 printf("#define MANIFEST_UUID \"%s\"\n",b);
81 printf("#define MANIFEST_VERSION \"[%10.10s]\"\n",b);
82 n = strlen(b);
83 if( n + 50 < sizeof(b) ){
84 sprintf(b+n, "%d", (int)time(0));
85 hash(b,33,vx);
86 printf("#define FOSSIL_BUILD_HASH \"%s\"\n", vx);
87 }
88 m = open_for_reading(argv[2]);
89 while(b == fgets(b, sizeof(b)-1,m)){
90 if(0 == strncmp("D ",b,2)){
91 int k, n;
92 char zDateNum[30];
93 printf("#define MANIFEST_DATE \"%.10s %.8s\"\n",b+2,b+13);
94 printf("#define MANIFEST_YEAR \"%.4s\"\n",b+2);
95 n = 0;
96 for(k=0; k<10; k++){
97 if( isdigit(b[k+2]) ) zDateNum[n++] = b[k+2];
98 }
99 zDateNum[n] = 0;
100 printf("#define MANIFEST_NUMERIC_DATE %s\n", zDateNum);
101 n = 0;
102 for(k=0; k<8; k++){
103 if( isdigit(b[k+13]) ) zDateNum[n++] = b[k+13];
104 }
105 zDateNum[n] = 0;
106 for(k=0; zDateNum[k]=='0'; k++){}
107 printf("#define MANIFEST_NUMERIC_TIME %s\n", zDateNum+k);
108 }
109 }
110 fclose(m);
111 v = open_for_reading(argv[3]);
112 if( fgets(b, sizeof(b)-1,v)==0 ){
113 fprintf(stderr, "malformed VERSION file: %s\n", argv[3]);
114
+234 -39
--- src/name.c
+++ src/name.c
@@ -614,11 +614,11 @@
614614
while( db_step(&q)==SQLITE_ROW ){
615615
const char *zUuid = db_column_text(&q, 0);
616616
int rid = db_column_int(&q, 1);
617617
@ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)">
618618
@ %s(zUuid)</a> -
619
- object_description(rid, 0, 0);
619
+ object_description(rid, 0, 0, 0);
620620
@ </p></li>
621621
}
622622
db_finalize(&q);
623623
db_prepare(&q,
624624
" SELECT tkt_rid, tkt_uuid, title"
@@ -636,11 +636,11 @@
636636
@ <ul></ul>
637637
@ Ticket
638638
hyperlink_to_uuid(zUuid);
639639
@ - %h(zTitle).
640640
@ <ul><li>
641
- object_description(rid, 0, 0);
641
+ object_description(rid, 0, 0, 0);
642642
@ </li></ul>
643643
@ </p></li>
644644
}
645645
db_finalize(&q);
646646
db_prepare(&q,
@@ -652,11 +652,11 @@
652652
int rid = db_column_int(&q, 0);
653653
const char* zUuid = db_column_text(&q, 1);
654654
@ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)">
655655
@ %s(zUuid)</a> -
656656
@ <ul><li>
657
- object_description(rid, 0, 0);
657
+ object_description(rid, 0, 0, 0);
658658
@ </li></ul>
659659
@ </p></li>
660660
}
661661
@ </ol>
662662
db_finalize(&q);
@@ -962,14 +962,81 @@
962962
@ rid INTEGER PRIMARY KEY, -- RID of the object
963963
@ uuid TEXT, -- hash of the object
964964
@ ctime DATETIME, -- Time of creation
965965
@ isPrivate BOOLEAN DEFAULT 0, -- True for unpublished artifacts
966966
@ type TEXT, -- file, checkin, wiki, ticket, etc.
967
+@ rcvid INT, -- When the artifact was received
967968
@ summary TEXT, -- Summary comment for the object
968
-@ detail TEXT -- File name, check-in comment, etc
969
+@ ref TEXT -- hash of an object to link against
969970
@ );
971
+@ CREATE INDEX desctype ON description(summary) WHERE summary='unknown';
970972
;
973
+
974
+/*
975
+** Attempt to describe all phantom artifacts. The artifacts are
976
+** already loaded into the description table and have summary='unknown'.
977
+** This routine attempts to generate a better summary, and possibly
978
+** fill in the ref field.
979
+*/
980
+static void describe_unknown_artifacts(){
981
+ /* Try to figure out the origin of unknown artifacts */
982
+ db_multi_exec(
983
+ "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n"
984
+ " SELECT description.rid, description.uuid, isPrivate, type,\n"
985
+ " CASE WHEN plink.isprim THEN '' ELSE 'merge ' END ||\n"
986
+ " 'parent of check-in', blob.uuid\n"
987
+ " FROM description, plink, blob\n"
988
+ " WHERE description.summary='unknown'\n"
989
+ " AND plink.pid=description.rid\n"
990
+ " AND blob.rid=plink.cid;"
991
+ );
992
+ db_multi_exec(
993
+ "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n"
994
+ " SELECT description.rid, description.uuid, isPrivate, type,\n"
995
+ " 'child of check-in', blob.uuid\n"
996
+ " FROM description, plink, blob\n"
997
+ " WHERE description.summary='unknown'\n"
998
+ " AND plink.cid=description.rid\n"
999
+ " AND blob.rid=plink.pid;"
1000
+ );
1001
+ db_multi_exec(
1002
+ "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n"
1003
+ " SELECT description.rid, description.uuid, isPrivate, type,\n"
1004
+ " 'check-in referenced by \"'||tag.tagname ||'\" tag',\n"
1005
+ " blob.uuid\n"
1006
+ " FROM description, tagxref, tag, blob\n"
1007
+ " WHERE description.summary='unknown'\n"
1008
+ " AND tagxref.origid=description.rid\n"
1009
+ " AND tag.tagid=tagxref.tagid\n"
1010
+ " AND blob.rid=tagxref.srcid;"
1011
+ );
1012
+ db_multi_exec(
1013
+ "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n"
1014
+ " SELECT description.rid, description.uuid, isPrivate, type,\n"
1015
+ " 'file \"'||filename.name||'\"',\n"
1016
+ " blob.uuid\n"
1017
+ " FROM description, mlink, filename, blob\n"
1018
+ " WHERE description.summary='unknown'\n"
1019
+ " AND mlink.fid=description.rid\n"
1020
+ " AND blob.rid=mlink.mid\n"
1021
+ " AND filename.fnid=mlink.fnid;"
1022
+ );
1023
+ if( !db_exists("SELECT 1 FROM description WHERE summary='unknown'") ){
1024
+ return;
1025
+ }
1026
+ add_content_sql_commands(g.db);
1027
+ db_multi_exec(
1028
+ "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n"
1029
+ " SELECT description.rid, description.uuid, isPrivate, type,\n"
1030
+ " 'referenced by cluster', blob.uuid\n"
1031
+ " FROM description, tagxref, blob\n"
1032
+ " WHERE description.summary='unknown'\n"
1033
+ " AND tagxref.tagid=(SELECT tagid FROM tag WHERE tagname='cluster')\n"
1034
+ " AND blob.rid=tagxref.rid\n"
1035
+ " AND content(blob.uuid) GLOB ('*M '||blob.uuid||'*');"
1036
+ );
1037
+}
9711038
9721039
/*
9731040
** Create the description table if it does not already exists.
9741041
** Populate fields of this table with descriptions for all artifacts
9751042
** whose RID matches the SQL expression in zWhere.
@@ -977,23 +1044,24 @@
9771044
void describe_artifacts(const char *zWhere){
9781045
db_multi_exec("%s", zDescTab/*safe-for-%s*/);
9791046
9801047
/* Describe check-ins */
9811048
db_multi_exec(
982
- "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n"
983
- "SELECT blob.rid, blob.uuid, event.mtime, 'checkin',\n"
1049
+ "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n"
1050
+ "SELECT blob.rid, blob.uuid, blob.rcvid, event.mtime, 'checkin',\n"
9841051
" 'check-in on ' || strftime('%%Y-%%m-%%d %%H:%%M',event.mtime)\n"
9851052
" FROM event, blob\n"
9861053
" WHERE (event.objid %s) AND event.type='ci'\n"
9871054
" AND event.objid=blob.rid;",
9881055
zWhere /*safe-for-%s*/
9891056
);
9901057
9911058
/* Describe files */
9921059
db_multi_exec(
993
- "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n"
994
- "SELECT blob.rid, blob.uuid, event.mtime, 'file', 'file '||filename.name\n"
1060
+ "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n"
1061
+ "SELECT blob.rid, blob.uuid, blob.rcvid, event.mtime,"
1062
+ " 'file', 'file '||filename.name\n"
9951063
" FROM mlink, blob, event, filename\n"
9961064
" WHERE (mlink.fid %s)\n"
9971065
" AND mlink.mid=event.objid\n"
9981066
" AND filename.fnid=mlink.fnid\n"
9991067
" AND mlink.fid=blob.rid;",
@@ -1000,23 +1068,24 @@
10001068
zWhere /*safe-for-%s*/
10011069
);
10021070
10031071
/* Describe tags */
10041072
db_multi_exec(
1005
- "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n"
1006
- "SELECT blob.rid, blob.uuid, tagxref.mtime, 'tag',\n"
1073
+ "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n"
1074
+ "SELECT blob.rid, blob.uuid, blob.rcvid, tagxref.mtime, 'tag',\n"
10071075
" 'tag '||substr((SELECT uuid FROM blob WHERE rid=tagxref.rid),1,16)\n"
10081076
" FROM tagxref, blob\n"
10091077
" WHERE (tagxref.srcid %s) AND tagxref.srcid!=tagxref.rid\n"
10101078
" AND tagxref.srcid=blob.rid;",
10111079
zWhere /*safe-for-%s*/
10121080
);
10131081
10141082
/* Cluster artifacts */
10151083
db_multi_exec(
1016
- "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n"
1017
- "SELECT blob.rid, blob.uuid, rcvfrom.mtime, 'cluster', 'cluster'\n"
1084
+ "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n"
1085
+ "SELECT blob.rid, blob.uuid, blob.rcvid, rcvfrom.mtime,"
1086
+ " 'cluster', 'cluster'\n"
10181087
" FROM tagxref, blob, rcvfrom\n"
10191088
" WHERE (tagxref.rid %s)\n"
10201089
" AND tagxref.tagid=(SELECT tagid FROM tag WHERE tagname='cluster')\n"
10211090
" AND blob.rid=tagxref.rid"
10221091
" AND rcvfrom.rcvid=blob.rcvid;",
@@ -1023,12 +1092,12 @@
10231092
zWhere /*safe-for-%s*/
10241093
);
10251094
10261095
/* Ticket change artifacts */
10271096
db_multi_exec(
1028
- "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n"
1029
- "SELECT blob.rid, blob.uuid, tagxref.mtime, 'ticket',\n"
1097
+ "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n"
1098
+ "SELECT blob.rid, blob.uuid, blob.rcvid, tagxref.mtime, 'ticket',\n"
10301099
" 'ticket '||substr(tag.tagname,5,21)\n"
10311100
" FROM tagxref, tag, blob\n"
10321101
" WHERE (tagxref.rid %s)\n"
10331102
" AND tag.tagid=tagxref.tagid\n"
10341103
" AND tag.tagname GLOB 'tkt-*'"
@@ -1036,12 +1105,12 @@
10361105
zWhere /*safe-for-%s*/
10371106
);
10381107
10391108
/* Wiki edit artifacts */
10401109
db_multi_exec(
1041
- "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n"
1042
- "SELECT blob.rid, blob.uuid, tagxref.mtime, 'wiki',\n"
1110
+ "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n"
1111
+ "SELECT blob.rid, blob.uuid, blob.rcvid, tagxref.mtime, 'wiki',\n"
10431112
" printf('wiki \"%%s\"',substr(tag.tagname,6))\n"
10441113
" FROM tagxref, tag, blob\n"
10451114
" WHERE (tagxref.rid %s)\n"
10461115
" AND tag.tagid=tagxref.tagid\n"
10471116
" AND tag.tagname GLOB 'wiki-*'"
@@ -1049,12 +1118,12 @@
10491118
zWhere /*safe-for-%s*/
10501119
);
10511120
10521121
/* Event edit artifacts */
10531122
db_multi_exec(
1054
- "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n"
1055
- "SELECT blob.rid, blob.uuid, tagxref.mtime, 'event',\n"
1123
+ "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n"
1124
+ "SELECT blob.rid, blob.uuid, blob.rcvid, tagxref.mtime, 'event',\n"
10561125
" 'event '||substr(tag.tagname,7)\n"
10571126
" FROM tagxref, tag, blob\n"
10581127
" WHERE (tagxref.rid %s)\n"
10591128
" AND tag.tagid=tagxref.tagid\n"
10601129
" AND tag.tagname GLOB 'event-*'"
@@ -1062,21 +1131,22 @@
10621131
zWhere /*safe-for-%s*/
10631132
);
10641133
10651134
/* Attachments */
10661135
db_multi_exec(
1067
- "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n"
1068
- "SELECT blob.rid, blob.uuid, attachment.mtime, 'attach-control',\n"
1136
+ "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n"
1137
+ "SELECT blob.rid, blob.uuid, blob.rcvid, attachment.mtime,"
1138
+ " 'attach-control',\n"
10691139
" 'attachment-control for '||attachment.filename\n"
10701140
" FROM attachment, blob\n"
10711141
" WHERE (attachment.attachid %s)\n"
10721142
" AND blob.rid=attachment.attachid",
10731143
zWhere /*safe-for-%s*/
10741144
);
10751145
db_multi_exec(
1076
- "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n"
1077
- "SELECT blob.rid, blob.uuid, attachment.mtime, 'attachment',\n"
1146
+ "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n"
1147
+ "SELECT blob.rid, blob.uuid, blob.rcvid, attachment.mtime, 'attachment',\n"
10781148
" 'attachment '||attachment.filename\n"
10791149
" FROM attachment, blob\n"
10801150
" WHERE (blob.rid %s)\n"
10811151
" AND blob.rid NOT IN (SELECT rid FROM description)\n"
10821152
" AND blob.uuid=attachment.src",
@@ -1084,12 +1154,13 @@
10841154
);
10851155
10861156
/* Forum posts */
10871157
if( db_table_exists("repository","forumpost") ){
10881158
db_multi_exec(
1089
- "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n"
1090
- "SELECT postblob.rid, postblob.uuid, forumpost.fmtime, 'forumpost',\n"
1159
+ "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n"
1160
+ "SELECT postblob.rid, postblob.uuid, postblob.rcvid,"
1161
+ " forumpost.fmtime, 'forumpost',\n"
10911162
" CASE WHEN fpid=froot THEN 'forum-post '\n"
10921163
" ELSE 'forum-reply-to ' END || substr(rootblob.uuid,1,14)\n"
10931164
" FROM forumpost, blob AS postblob, blob AS rootblob\n"
10941165
" WHERE (forumpost.fpid %s)\n"
10951166
" AND postblob.rid=forumpost.fpid"
@@ -1096,24 +1167,31 @@
10961167
" AND rootblob.rid=forumpost.froot",
10971168
zWhere /*safe-for-%s*/
10981169
);
10991170
}
11001171
1101
- /* Everything else */
1172
+ /* Mark all other artifacts as "unknown" for now */
11021173
db_multi_exec(
1103
- "INSERT OR IGNORE INTO description(rid,uuid,type,summary)\n"
1104
- "SELECT blob.rid, blob.uuid,"
1105
- " CASE WHEN blob.size<0 THEN 'phantom' ELSE '' END,\n"
1174
+ "INSERT OR IGNORE INTO description(rid,uuid,rcvid,type,summary)\n"
1175
+ "SELECT blob.rid, blob.uuid,blob.rcvid,\n"
1176
+ " CASE WHEN EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)\n"
1177
+ " THEN 'phantom' ELSE '' END,\n"
11061178
" 'unknown'\n"
1107
- " FROM blob WHERE (blob.rid %s);",
1179
+ " FROM blob\n"
1180
+ " WHERE (blob.rid %s)\n"
1181
+ " AND (blob.rid NOT IN (SELECT rid FROM description));",
11081182
zWhere /*safe-for-%s*/
11091183
);
11101184
11111185
/* Mark private elements */
11121186
db_multi_exec(
11131187
"UPDATE description SET isPrivate=1 WHERE rid IN private"
11141188
);
1189
+
1190
+ if( db_exists("SELECT 1 FROM description WHERE summary='unknown'") ){
1191
+ describe_unknown_artifacts();
1192
+ }
11151193
}
11161194
11171195
/*
11181196
** Print the content of the description table on stdout.
11191197
**
@@ -1135,11 +1213,11 @@
11351213
if( zLabel ){
11361214
fossil_print("%s\n", zLabel);
11371215
zLabel = 0;
11381216
}
11391217
fossil_print(" %.16s %s", db_column_text(&q,0), db_column_text(&q,1));
1140
- if( db_column_int(&q,2) ) fossil_print(" (unpublished)");
1218
+ if( db_column_int(&q,2) ) fossil_print(" (private)");
11411219
fossil_print("\n");
11421220
cnt++;
11431221
}
11441222
db_finalize(&q);
11451223
if( zWhere!=0 ) db_multi_exec("DELETE FROM description;");
@@ -1172,19 +1250,21 @@
11721250
**
11731251
** Return a page showing all artifacts in the repository. Query parameters:
11741252
**
11751253
** n=N Show N artifacts
11761254
** s=S Start with artifact number S
1177
-** unpub Show only unpublished artifacts
1255
+** priv Show only unpublished or private artifacts
1256
+** phan Show only phantom artifacts
11781257
** hclr Color code hash types (SHA1 vs SHA3)
11791258
*/
11801259
void bloblist_page(void){
11811260
Stmt q;
11821261
int s = atoi(PD("s","0"));
11831262
int n = atoi(PD("n","5000"));
11841263
int mx = db_int(0, "SELECT max(rid) FROM blob");
1185
- int unpubOnly = PB("unpub");
1264
+ int privOnly = PB("priv");
1265
+ int phantomOnly = PB("phan");
11861266
int hashClr = PB("hclr");
11871267
char *zRange;
11881268
char *zSha1Bg;
11891269
char *zSha3Bg;
11901270
@@ -1192,15 +1272,25 @@
11921272
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
11931273
style_header("List Of Artifacts");
11941274
style_submenu_element("250 Largest", "bigbloblist");
11951275
if( g.perm.Admin ){
11961276
style_submenu_element("Artifact Log", "rcvfromlist");
1277
+ }
1278
+ if( !phantomOnly ){
1279
+ style_submenu_element("Phantoms", "bloblist?phan");
1280
+ }
1281
+ if( g.perm.Private || g.perm.Admin ){
1282
+ if( !privOnly ){
1283
+ style_submenu_element("Private", "bloblist?priv");
1284
+ }
1285
+ }else{
1286
+ privOnly = 0;
11971287
}
11981288
if( g.perm.Write ){
11991289
style_submenu_element("Artifact Stats", "artifact_stats");
12001290
}
1201
- if( !unpubOnly && mx>n && P("s")==0 ){
1291
+ if( !privOnly && !phantomOnly && mx>n && P("s")==0 ){
12021292
int i;
12031293
@ <p>Select a range of artifacts to view:</p>
12041294
@ <ul>
12051295
for(i=1; i<=mx; i+=n){
12061296
@ <li> %z(href("%R/bloblist?s=%d&n=%d",i,n))
@@ -1208,51 +1298,156 @@
12081298
}
12091299
@ </ul>
12101300
style_footer();
12111301
return;
12121302
}
1213
- if( !unpubOnly && mx>n ){
1303
+ if( phantomOnly || privOnly || mx>n ){
12141304
style_submenu_element("Index", "bloblist");
12151305
}
1216
- if( unpubOnly ){
1306
+ if( privOnly ){
12171307
zRange = mprintf("IN private");
1308
+ }else if( phantomOnly ){
1309
+ zRange = mprintf("IN phantom");
12181310
}else{
12191311
zRange = mprintf("BETWEEN %d AND %d", s, s+n-1);
12201312
}
12211313
describe_artifacts(zRange);
12221314
fossil_free(zRange);
12231315
db_prepare(&q,
1224
- "SELECT rid, uuid, summary, isPrivate FROM description ORDER BY rid"
1316
+ "SELECT rid, uuid, summary, isPrivate, type='phantom', rcvid, ref"
1317
+ " FROM description ORDER BY rid"
12251318
);
12261319
if( skin_detail_boolean("white-foreground") ){
12271320
zSha1Bg = "#714417";
12281321
zSha3Bg = "#177117";
12291322
}else{
12301323
zSha1Bg = "#ebffb0";
12311324
zSha3Bg = "#b0ffb0";
12321325
}
1233
- @ <table cellpadding="0" cellspacing="0">
1326
+ @ <table cellpadding="2" cellspacing="0" border="1">
1327
+ if( g.perm.Admin ){
1328
+ @ <tr><th>RID<th>Hash<th>Rcvid<th>Description<th>Ref<th>Remarks
1329
+ }else{
1330
+ @ <tr><th>RID<th>Hash<th>Description<th>Ref<th>Remarks
1331
+ }
12341332
while( db_step(&q)==SQLITE_ROW ){
12351333
int rid = db_column_int(&q,0);
12361334
const char *zUuid = db_column_text(&q, 1);
12371335
const char *zDesc = db_column_text(&q, 2);
12381336
int isPriv = db_column_int(&q,3);
1337
+ int isPhantom = db_column_int(&q,4);
1338
+ const char *zRef = db_column_text(&q,6);
1339
+ if( isPriv && !isPhantom && !g.perm.Private && !g.perm.Admin ){
1340
+ /* Don't show private artifacts to users without Private (x) permission */
1341
+ continue;
1342
+ }
12391343
if( hashClr ){
12401344
const char *zClr = db_column_bytes(&q,1)>40 ? zSha3Bg : zSha1Bg;
12411345
@ <tr style='background-color:%s(zClr);'><td align="right">%d(rid)</td>
12421346
}else{
12431347
@ <tr><td align="right">%d(rid)</td>
12441348
}
12451349
@ <td>&nbsp;%z(href("%R/info/%!S",zUuid))%S(zUuid)</a>&nbsp;</td>
1350
+ if( g.perm.Admin ){
1351
+ int rcvid = db_column_int(&q,5);
1352
+ if( rcvid<=0 ){
1353
+ @ <td>&nbsp;
1354
+ }else{
1355
+ @ <td><a href='%R/rcvfrom?rcvid=%d(rcvid)'>%d(rcvid)</a>
1356
+ }
1357
+ }
12461358
@ <td align="left">%h(zDesc)</td>
1247
- if( isPriv ){
1248
- @ <td>(unpublished)</td>
1359
+ if( zRef && zRef[0] ){
1360
+ @ <td>%z(href("%R/info/%!S",zRef))%S(zRef)</a>
1361
+ }else{
1362
+ @ <td>&nbsp;
1363
+ }
1364
+ if( isPriv || isPhantom ){
1365
+ if( isPriv==0 ){
1366
+ @ <td>phantom</td>
1367
+ }else if( isPhantom==0 ){
1368
+ @ <td>private</td>
1369
+ }else{
1370
+ @ <td>private,phantom</td>
1371
+ }
1372
+ }else{
1373
+ @ <td>&nbsp;
1374
+ }
1375
+ @ </tr>
1376
+ }
1377
+ @ </table>
1378
+ db_finalize(&q);
1379
+ style_footer();
1380
+}
1381
+
1382
+/*
1383
+** Output HTML that shows a table of all public phantoms.
1384
+*/
1385
+void table_of_public_phantoms(void){
1386
+ Stmt q;
1387
+ char *zRange;
1388
+ zRange = mprintf("IN (SELECT rid FROM phantom EXCEPT"
1389
+ " SELECT rid FROM private)");
1390
+ describe_artifacts(zRange);
1391
+ fossil_free(zRange);
1392
+ db_prepare(&q,
1393
+ "SELECT rid, uuid, summary, ref"
1394
+ " FROM description ORDER BY rid"
1395
+ );
1396
+ @ <table cellpadding="2" cellspacing="0" border="1">
1397
+ @ <tr><th>RID<th>Description<th>Source
1398
+ while( db_step(&q)==SQLITE_ROW ){
1399
+ int rid = db_column_int(&q,0);
1400
+ const char *zUuid = db_column_text(&q, 1);
1401
+ const char *zDesc = db_column_text(&q, 2);
1402
+ const char *zRef = db_column_text(&q,3);
1403
+ @ <tr><td valign="top">%d(rid)</td>
1404
+ @ <td valign="top" align="left">%h(zUuid)<br>%h(zDesc)</td>
1405
+ if( zRef && zRef[0] ){
1406
+ @ <td valign="top">%z(href("%R/info/%!S",zRef))%!S(zRef)</a>
1407
+ }else{
1408
+ @ <td>&nbsp;
12491409
}
12501410
@ </tr>
12511411
}
12521412
@ </table>
12531413
db_finalize(&q);
1414
+}
1415
+
1416
+/*
1417
+** WEBPAGE: phantoms
1418
+**
1419
+** Show a list of all "phantom" artifacts that are not marked as "private".
1420
+**
1421
+** A "phantom" artifact is an artifact whose hash named appears in some
1422
+** artifact but whose content is unknown. For example, if a manifest
1423
+** references a particular SHA3 hash of a file, but that SHA3 hash is
1424
+** not on the shunning list and is not in the database, then the file
1425
+** is a phantom. We know it exists, but we do not know its content.
1426
+**
1427
+** Whenever a sync occurs, both each party looks at its phantom list
1428
+** and for every phantom that is not also marked private, it asks the
1429
+** other party to send it the content. This mechanism helps keep all
1430
+** repositories synced up.
1431
+**
1432
+** This page is similar to the /bloblist page in that it lists artifacts.
1433
+** But this page is a special case in that it only shows phantoms that
1434
+** are not private. In other words, this page shows all phantoms that
1435
+** generate extra network traffic on every sync request.
1436
+*/
1437
+void phantom_list_page(void){
1438
+ login_check_credentials();
1439
+ if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1440
+ style_header("Public Phantom Artifacts");
1441
+ if( g.perm.Admin ){
1442
+ style_submenu_element("Artifact Log", "rcvfromlist");
1443
+ style_submenu_element("Artifact List", "bloblist");
1444
+ }
1445
+ if( g.perm.Write ){
1446
+ style_submenu_element("Artifact Stats", "artifact_stats");
1447
+ }
1448
+ table_of_public_phantoms();
12541449
style_footer();
12551450
}
12561451
12571452
/*
12581453
** WEBPAGE: bigbloblist
12591454
--- src/name.c
+++ src/name.c
@@ -614,11 +614,11 @@
614 while( db_step(&q)==SQLITE_ROW ){
615 const char *zUuid = db_column_text(&q, 0);
616 int rid = db_column_int(&q, 1);
617 @ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)">
618 @ %s(zUuid)</a> -
619 object_description(rid, 0, 0);
620 @ </p></li>
621 }
622 db_finalize(&q);
623 db_prepare(&q,
624 " SELECT tkt_rid, tkt_uuid, title"
@@ -636,11 +636,11 @@
636 @ <ul></ul>
637 @ Ticket
638 hyperlink_to_uuid(zUuid);
639 @ - %h(zTitle).
640 @ <ul><li>
641 object_description(rid, 0, 0);
642 @ </li></ul>
643 @ </p></li>
644 }
645 db_finalize(&q);
646 db_prepare(&q,
@@ -652,11 +652,11 @@
652 int rid = db_column_int(&q, 0);
653 const char* zUuid = db_column_text(&q, 1);
654 @ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)">
655 @ %s(zUuid)</a> -
656 @ <ul><li>
657 object_description(rid, 0, 0);
658 @ </li></ul>
659 @ </p></li>
660 }
661 @ </ol>
662 db_finalize(&q);
@@ -962,14 +962,81 @@
962 @ rid INTEGER PRIMARY KEY, -- RID of the object
963 @ uuid TEXT, -- hash of the object
964 @ ctime DATETIME, -- Time of creation
965 @ isPrivate BOOLEAN DEFAULT 0, -- True for unpublished artifacts
966 @ type TEXT, -- file, checkin, wiki, ticket, etc.
 
967 @ summary TEXT, -- Summary comment for the object
968 @ detail TEXT -- File name, check-in comment, etc
969 @ );
 
970 ;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
971
972 /*
973 ** Create the description table if it does not already exists.
974 ** Populate fields of this table with descriptions for all artifacts
975 ** whose RID matches the SQL expression in zWhere.
@@ -977,23 +1044,24 @@
977 void describe_artifacts(const char *zWhere){
978 db_multi_exec("%s", zDescTab/*safe-for-%s*/);
979
980 /* Describe check-ins */
981 db_multi_exec(
982 "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n"
983 "SELECT blob.rid, blob.uuid, event.mtime, 'checkin',\n"
984 " 'check-in on ' || strftime('%%Y-%%m-%%d %%H:%%M',event.mtime)\n"
985 " FROM event, blob\n"
986 " WHERE (event.objid %s) AND event.type='ci'\n"
987 " AND event.objid=blob.rid;",
988 zWhere /*safe-for-%s*/
989 );
990
991 /* Describe files */
992 db_multi_exec(
993 "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n"
994 "SELECT blob.rid, blob.uuid, event.mtime, 'file', 'file '||filename.name\n"
 
995 " FROM mlink, blob, event, filename\n"
996 " WHERE (mlink.fid %s)\n"
997 " AND mlink.mid=event.objid\n"
998 " AND filename.fnid=mlink.fnid\n"
999 " AND mlink.fid=blob.rid;",
@@ -1000,23 +1068,24 @@
1000 zWhere /*safe-for-%s*/
1001 );
1002
1003 /* Describe tags */
1004 db_multi_exec(
1005 "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n"
1006 "SELECT blob.rid, blob.uuid, tagxref.mtime, 'tag',\n"
1007 " 'tag '||substr((SELECT uuid FROM blob WHERE rid=tagxref.rid),1,16)\n"
1008 " FROM tagxref, blob\n"
1009 " WHERE (tagxref.srcid %s) AND tagxref.srcid!=tagxref.rid\n"
1010 " AND tagxref.srcid=blob.rid;",
1011 zWhere /*safe-for-%s*/
1012 );
1013
1014 /* Cluster artifacts */
1015 db_multi_exec(
1016 "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n"
1017 "SELECT blob.rid, blob.uuid, rcvfrom.mtime, 'cluster', 'cluster'\n"
 
1018 " FROM tagxref, blob, rcvfrom\n"
1019 " WHERE (tagxref.rid %s)\n"
1020 " AND tagxref.tagid=(SELECT tagid FROM tag WHERE tagname='cluster')\n"
1021 " AND blob.rid=tagxref.rid"
1022 " AND rcvfrom.rcvid=blob.rcvid;",
@@ -1023,12 +1092,12 @@
1023 zWhere /*safe-for-%s*/
1024 );
1025
1026 /* Ticket change artifacts */
1027 db_multi_exec(
1028 "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n"
1029 "SELECT blob.rid, blob.uuid, tagxref.mtime, 'ticket',\n"
1030 " 'ticket '||substr(tag.tagname,5,21)\n"
1031 " FROM tagxref, tag, blob\n"
1032 " WHERE (tagxref.rid %s)\n"
1033 " AND tag.tagid=tagxref.tagid\n"
1034 " AND tag.tagname GLOB 'tkt-*'"
@@ -1036,12 +1105,12 @@
1036 zWhere /*safe-for-%s*/
1037 );
1038
1039 /* Wiki edit artifacts */
1040 db_multi_exec(
1041 "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n"
1042 "SELECT blob.rid, blob.uuid, tagxref.mtime, 'wiki',\n"
1043 " printf('wiki \"%%s\"',substr(tag.tagname,6))\n"
1044 " FROM tagxref, tag, blob\n"
1045 " WHERE (tagxref.rid %s)\n"
1046 " AND tag.tagid=tagxref.tagid\n"
1047 " AND tag.tagname GLOB 'wiki-*'"
@@ -1049,12 +1118,12 @@
1049 zWhere /*safe-for-%s*/
1050 );
1051
1052 /* Event edit artifacts */
1053 db_multi_exec(
1054 "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n"
1055 "SELECT blob.rid, blob.uuid, tagxref.mtime, 'event',\n"
1056 " 'event '||substr(tag.tagname,7)\n"
1057 " FROM tagxref, tag, blob\n"
1058 " WHERE (tagxref.rid %s)\n"
1059 " AND tag.tagid=tagxref.tagid\n"
1060 " AND tag.tagname GLOB 'event-*'"
@@ -1062,21 +1131,22 @@
1062 zWhere /*safe-for-%s*/
1063 );
1064
1065 /* Attachments */
1066 db_multi_exec(
1067 "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n"
1068 "SELECT blob.rid, blob.uuid, attachment.mtime, 'attach-control',\n"
 
1069 " 'attachment-control for '||attachment.filename\n"
1070 " FROM attachment, blob\n"
1071 " WHERE (attachment.attachid %s)\n"
1072 " AND blob.rid=attachment.attachid",
1073 zWhere /*safe-for-%s*/
1074 );
1075 db_multi_exec(
1076 "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n"
1077 "SELECT blob.rid, blob.uuid, attachment.mtime, 'attachment',\n"
1078 " 'attachment '||attachment.filename\n"
1079 " FROM attachment, blob\n"
1080 " WHERE (blob.rid %s)\n"
1081 " AND blob.rid NOT IN (SELECT rid FROM description)\n"
1082 " AND blob.uuid=attachment.src",
@@ -1084,12 +1154,13 @@
1084 );
1085
1086 /* Forum posts */
1087 if( db_table_exists("repository","forumpost") ){
1088 db_multi_exec(
1089 "INSERT OR IGNORE INTO description(rid,uuid,ctime,type,summary)\n"
1090 "SELECT postblob.rid, postblob.uuid, forumpost.fmtime, 'forumpost',\n"
 
1091 " CASE WHEN fpid=froot THEN 'forum-post '\n"
1092 " ELSE 'forum-reply-to ' END || substr(rootblob.uuid,1,14)\n"
1093 " FROM forumpost, blob AS postblob, blob AS rootblob\n"
1094 " WHERE (forumpost.fpid %s)\n"
1095 " AND postblob.rid=forumpost.fpid"
@@ -1096,24 +1167,31 @@
1096 " AND rootblob.rid=forumpost.froot",
1097 zWhere /*safe-for-%s*/
1098 );
1099 }
1100
1101 /* Everything else */
1102 db_multi_exec(
1103 "INSERT OR IGNORE INTO description(rid,uuid,type,summary)\n"
1104 "SELECT blob.rid, blob.uuid,"
1105 " CASE WHEN blob.size<0 THEN 'phantom' ELSE '' END,\n"
 
1106 " 'unknown'\n"
1107 " FROM blob WHERE (blob.rid %s);",
 
 
1108 zWhere /*safe-for-%s*/
1109 );
1110
1111 /* Mark private elements */
1112 db_multi_exec(
1113 "UPDATE description SET isPrivate=1 WHERE rid IN private"
1114 );
 
 
 
 
1115 }
1116
1117 /*
1118 ** Print the content of the description table on stdout.
1119 **
@@ -1135,11 +1213,11 @@
1135 if( zLabel ){
1136 fossil_print("%s\n", zLabel);
1137 zLabel = 0;
1138 }
1139 fossil_print(" %.16s %s", db_column_text(&q,0), db_column_text(&q,1));
1140 if( db_column_int(&q,2) ) fossil_print(" (unpublished)");
1141 fossil_print("\n");
1142 cnt++;
1143 }
1144 db_finalize(&q);
1145 if( zWhere!=0 ) db_multi_exec("DELETE FROM description;");
@@ -1172,19 +1250,21 @@
1172 **
1173 ** Return a page showing all artifacts in the repository. Query parameters:
1174 **
1175 ** n=N Show N artifacts
1176 ** s=S Start with artifact number S
1177 ** unpub Show only unpublished artifacts
 
1178 ** hclr Color code hash types (SHA1 vs SHA3)
1179 */
1180 void bloblist_page(void){
1181 Stmt q;
1182 int s = atoi(PD("s","0"));
1183 int n = atoi(PD("n","5000"));
1184 int mx = db_int(0, "SELECT max(rid) FROM blob");
1185 int unpubOnly = PB("unpub");
 
1186 int hashClr = PB("hclr");
1187 char *zRange;
1188 char *zSha1Bg;
1189 char *zSha3Bg;
1190
@@ -1192,15 +1272,25 @@
1192 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1193 style_header("List Of Artifacts");
1194 style_submenu_element("250 Largest", "bigbloblist");
1195 if( g.perm.Admin ){
1196 style_submenu_element("Artifact Log", "rcvfromlist");
 
 
 
 
 
 
 
 
 
 
1197 }
1198 if( g.perm.Write ){
1199 style_submenu_element("Artifact Stats", "artifact_stats");
1200 }
1201 if( !unpubOnly && mx>n && P("s")==0 ){
1202 int i;
1203 @ <p>Select a range of artifacts to view:</p>
1204 @ <ul>
1205 for(i=1; i<=mx; i+=n){
1206 @ <li> %z(href("%R/bloblist?s=%d&n=%d",i,n))
@@ -1208,51 +1298,156 @@
1208 }
1209 @ </ul>
1210 style_footer();
1211 return;
1212 }
1213 if( !unpubOnly && mx>n ){
1214 style_submenu_element("Index", "bloblist");
1215 }
1216 if( unpubOnly ){
1217 zRange = mprintf("IN private");
 
 
1218 }else{
1219 zRange = mprintf("BETWEEN %d AND %d", s, s+n-1);
1220 }
1221 describe_artifacts(zRange);
1222 fossil_free(zRange);
1223 db_prepare(&q,
1224 "SELECT rid, uuid, summary, isPrivate FROM description ORDER BY rid"
 
1225 );
1226 if( skin_detail_boolean("white-foreground") ){
1227 zSha1Bg = "#714417";
1228 zSha3Bg = "#177117";
1229 }else{
1230 zSha1Bg = "#ebffb0";
1231 zSha3Bg = "#b0ffb0";
1232 }
1233 @ <table cellpadding="0" cellspacing="0">
 
 
 
 
 
1234 while( db_step(&q)==SQLITE_ROW ){
1235 int rid = db_column_int(&q,0);
1236 const char *zUuid = db_column_text(&q, 1);
1237 const char *zDesc = db_column_text(&q, 2);
1238 int isPriv = db_column_int(&q,3);
 
 
 
 
 
 
1239 if( hashClr ){
1240 const char *zClr = db_column_bytes(&q,1)>40 ? zSha3Bg : zSha1Bg;
1241 @ <tr style='background-color:%s(zClr);'><td align="right">%d(rid)</td>
1242 }else{
1243 @ <tr><td align="right">%d(rid)</td>
1244 }
1245 @ <td>&nbsp;%z(href("%R/info/%!S",zUuid))%S(zUuid)</a>&nbsp;</td>
 
 
 
 
 
 
 
 
1246 @ <td align="left">%h(zDesc)</td>
1247 if( isPriv ){
1248 @ <td>(unpublished)</td>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1249 }
1250 @ </tr>
1251 }
1252 @ </table>
1253 db_finalize(&q);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1254 style_footer();
1255 }
1256
1257 /*
1258 ** WEBPAGE: bigbloblist
1259
--- src/name.c
+++ src/name.c
@@ -614,11 +614,11 @@
614 while( db_step(&q)==SQLITE_ROW ){
615 const char *zUuid = db_column_text(&q, 0);
616 int rid = db_column_int(&q, 1);
617 @ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)">
618 @ %s(zUuid)</a> -
619 object_description(rid, 0, 0, 0);
620 @ </p></li>
621 }
622 db_finalize(&q);
623 db_prepare(&q,
624 " SELECT tkt_rid, tkt_uuid, title"
@@ -636,11 +636,11 @@
636 @ <ul></ul>
637 @ Ticket
638 hyperlink_to_uuid(zUuid);
639 @ - %h(zTitle).
640 @ <ul><li>
641 object_description(rid, 0, 0, 0);
642 @ </li></ul>
643 @ </p></li>
644 }
645 db_finalize(&q);
646 db_prepare(&q,
@@ -652,11 +652,11 @@
652 int rid = db_column_int(&q, 0);
653 const char* zUuid = db_column_text(&q, 1);
654 @ <li><p><a href="%R/%T(zSrc)/%!S(zUuid)">
655 @ %s(zUuid)</a> -
656 @ <ul><li>
657 object_description(rid, 0, 0, 0);
658 @ </li></ul>
659 @ </p></li>
660 }
661 @ </ol>
662 db_finalize(&q);
@@ -962,14 +962,81 @@
962 @ rid INTEGER PRIMARY KEY, -- RID of the object
963 @ uuid TEXT, -- hash of the object
964 @ ctime DATETIME, -- Time of creation
965 @ isPrivate BOOLEAN DEFAULT 0, -- True for unpublished artifacts
966 @ type TEXT, -- file, checkin, wiki, ticket, etc.
967 @ rcvid INT, -- When the artifact was received
968 @ summary TEXT, -- Summary comment for the object
969 @ ref TEXT -- hash of an object to link against
970 @ );
971 @ CREATE INDEX desctype ON description(summary) WHERE summary='unknown';
972 ;
973
974 /*
975 ** Attempt to describe all phantom artifacts. The artifacts are
976 ** already loaded into the description table and have summary='unknown'.
977 ** This routine attempts to generate a better summary, and possibly
978 ** fill in the ref field.
979 */
980 static void describe_unknown_artifacts(){
981 /* Try to figure out the origin of unknown artifacts */
982 db_multi_exec(
983 "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n"
984 " SELECT description.rid, description.uuid, isPrivate, type,\n"
985 " CASE WHEN plink.isprim THEN '' ELSE 'merge ' END ||\n"
986 " 'parent of check-in', blob.uuid\n"
987 " FROM description, plink, blob\n"
988 " WHERE description.summary='unknown'\n"
989 " AND plink.pid=description.rid\n"
990 " AND blob.rid=plink.cid;"
991 );
992 db_multi_exec(
993 "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n"
994 " SELECT description.rid, description.uuid, isPrivate, type,\n"
995 " 'child of check-in', blob.uuid\n"
996 " FROM description, plink, blob\n"
997 " WHERE description.summary='unknown'\n"
998 " AND plink.cid=description.rid\n"
999 " AND blob.rid=plink.pid;"
1000 );
1001 db_multi_exec(
1002 "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n"
1003 " SELECT description.rid, description.uuid, isPrivate, type,\n"
1004 " 'check-in referenced by \"'||tag.tagname ||'\" tag',\n"
1005 " blob.uuid\n"
1006 " FROM description, tagxref, tag, blob\n"
1007 " WHERE description.summary='unknown'\n"
1008 " AND tagxref.origid=description.rid\n"
1009 " AND tag.tagid=tagxref.tagid\n"
1010 " AND blob.rid=tagxref.srcid;"
1011 );
1012 db_multi_exec(
1013 "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n"
1014 " SELECT description.rid, description.uuid, isPrivate, type,\n"
1015 " 'file \"'||filename.name||'\"',\n"
1016 " blob.uuid\n"
1017 " FROM description, mlink, filename, blob\n"
1018 " WHERE description.summary='unknown'\n"
1019 " AND mlink.fid=description.rid\n"
1020 " AND blob.rid=mlink.mid\n"
1021 " AND filename.fnid=mlink.fnid;"
1022 );
1023 if( !db_exists("SELECT 1 FROM description WHERE summary='unknown'") ){
1024 return;
1025 }
1026 add_content_sql_commands(g.db);
1027 db_multi_exec(
1028 "REPLACE INTO description(rid,uuid,isPrivate,type,summary,ref)\n"
1029 " SELECT description.rid, description.uuid, isPrivate, type,\n"
1030 " 'referenced by cluster', blob.uuid\n"
1031 " FROM description, tagxref, blob\n"
1032 " WHERE description.summary='unknown'\n"
1033 " AND tagxref.tagid=(SELECT tagid FROM tag WHERE tagname='cluster')\n"
1034 " AND blob.rid=tagxref.rid\n"
1035 " AND content(blob.uuid) GLOB ('*M '||blob.uuid||'*');"
1036 );
1037 }
1038
1039 /*
1040 ** Create the description table if it does not already exists.
1041 ** Populate fields of this table with descriptions for all artifacts
1042 ** whose RID matches the SQL expression in zWhere.
@@ -977,23 +1044,24 @@
1044 void describe_artifacts(const char *zWhere){
1045 db_multi_exec("%s", zDescTab/*safe-for-%s*/);
1046
1047 /* Describe check-ins */
1048 db_multi_exec(
1049 "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n"
1050 "SELECT blob.rid, blob.uuid, blob.rcvid, event.mtime, 'checkin',\n"
1051 " 'check-in on ' || strftime('%%Y-%%m-%%d %%H:%%M',event.mtime)\n"
1052 " FROM event, blob\n"
1053 " WHERE (event.objid %s) AND event.type='ci'\n"
1054 " AND event.objid=blob.rid;",
1055 zWhere /*safe-for-%s*/
1056 );
1057
1058 /* Describe files */
1059 db_multi_exec(
1060 "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n"
1061 "SELECT blob.rid, blob.uuid, blob.rcvid, event.mtime,"
1062 " 'file', 'file '||filename.name\n"
1063 " FROM mlink, blob, event, filename\n"
1064 " WHERE (mlink.fid %s)\n"
1065 " AND mlink.mid=event.objid\n"
1066 " AND filename.fnid=mlink.fnid\n"
1067 " AND mlink.fid=blob.rid;",
@@ -1000,23 +1068,24 @@
1068 zWhere /*safe-for-%s*/
1069 );
1070
1071 /* Describe tags */
1072 db_multi_exec(
1073 "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n"
1074 "SELECT blob.rid, blob.uuid, blob.rcvid, tagxref.mtime, 'tag',\n"
1075 " 'tag '||substr((SELECT uuid FROM blob WHERE rid=tagxref.rid),1,16)\n"
1076 " FROM tagxref, blob\n"
1077 " WHERE (tagxref.srcid %s) AND tagxref.srcid!=tagxref.rid\n"
1078 " AND tagxref.srcid=blob.rid;",
1079 zWhere /*safe-for-%s*/
1080 );
1081
1082 /* Cluster artifacts */
1083 db_multi_exec(
1084 "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n"
1085 "SELECT blob.rid, blob.uuid, blob.rcvid, rcvfrom.mtime,"
1086 " 'cluster', 'cluster'\n"
1087 " FROM tagxref, blob, rcvfrom\n"
1088 " WHERE (tagxref.rid %s)\n"
1089 " AND tagxref.tagid=(SELECT tagid FROM tag WHERE tagname='cluster')\n"
1090 " AND blob.rid=tagxref.rid"
1091 " AND rcvfrom.rcvid=blob.rcvid;",
@@ -1023,12 +1092,12 @@
1092 zWhere /*safe-for-%s*/
1093 );
1094
1095 /* Ticket change artifacts */
1096 db_multi_exec(
1097 "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n"
1098 "SELECT blob.rid, blob.uuid, blob.rcvid, tagxref.mtime, 'ticket',\n"
1099 " 'ticket '||substr(tag.tagname,5,21)\n"
1100 " FROM tagxref, tag, blob\n"
1101 " WHERE (tagxref.rid %s)\n"
1102 " AND tag.tagid=tagxref.tagid\n"
1103 " AND tag.tagname GLOB 'tkt-*'"
@@ -1036,12 +1105,12 @@
1105 zWhere /*safe-for-%s*/
1106 );
1107
1108 /* Wiki edit artifacts */
1109 db_multi_exec(
1110 "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n"
1111 "SELECT blob.rid, blob.uuid, blob.rcvid, tagxref.mtime, 'wiki',\n"
1112 " printf('wiki \"%%s\"',substr(tag.tagname,6))\n"
1113 " FROM tagxref, tag, blob\n"
1114 " WHERE (tagxref.rid %s)\n"
1115 " AND tag.tagid=tagxref.tagid\n"
1116 " AND tag.tagname GLOB 'wiki-*'"
@@ -1049,12 +1118,12 @@
1118 zWhere /*safe-for-%s*/
1119 );
1120
1121 /* Event edit artifacts */
1122 db_multi_exec(
1123 "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n"
1124 "SELECT blob.rid, blob.uuid, blob.rcvid, tagxref.mtime, 'event',\n"
1125 " 'event '||substr(tag.tagname,7)\n"
1126 " FROM tagxref, tag, blob\n"
1127 " WHERE (tagxref.rid %s)\n"
1128 " AND tag.tagid=tagxref.tagid\n"
1129 " AND tag.tagname GLOB 'event-*'"
@@ -1062,21 +1131,22 @@
1131 zWhere /*safe-for-%s*/
1132 );
1133
1134 /* Attachments */
1135 db_multi_exec(
1136 "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n"
1137 "SELECT blob.rid, blob.uuid, blob.rcvid, attachment.mtime,"
1138 " 'attach-control',\n"
1139 " 'attachment-control for '||attachment.filename\n"
1140 " FROM attachment, blob\n"
1141 " WHERE (attachment.attachid %s)\n"
1142 " AND blob.rid=attachment.attachid",
1143 zWhere /*safe-for-%s*/
1144 );
1145 db_multi_exec(
1146 "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n"
1147 "SELECT blob.rid, blob.uuid, blob.rcvid, attachment.mtime, 'attachment',\n"
1148 " 'attachment '||attachment.filename\n"
1149 " FROM attachment, blob\n"
1150 " WHERE (blob.rid %s)\n"
1151 " AND blob.rid NOT IN (SELECT rid FROM description)\n"
1152 " AND blob.uuid=attachment.src",
@@ -1084,12 +1154,13 @@
1154 );
1155
1156 /* Forum posts */
1157 if( db_table_exists("repository","forumpost") ){
1158 db_multi_exec(
1159 "INSERT OR IGNORE INTO description(rid,uuid,rcvid,ctime,type,summary)\n"
1160 "SELECT postblob.rid, postblob.uuid, postblob.rcvid,"
1161 " forumpost.fmtime, 'forumpost',\n"
1162 " CASE WHEN fpid=froot THEN 'forum-post '\n"
1163 " ELSE 'forum-reply-to ' END || substr(rootblob.uuid,1,14)\n"
1164 " FROM forumpost, blob AS postblob, blob AS rootblob\n"
1165 " WHERE (forumpost.fpid %s)\n"
1166 " AND postblob.rid=forumpost.fpid"
@@ -1096,24 +1167,31 @@
1167 " AND rootblob.rid=forumpost.froot",
1168 zWhere /*safe-for-%s*/
1169 );
1170 }
1171
1172 /* Mark all other artifacts as "unknown" for now */
1173 db_multi_exec(
1174 "INSERT OR IGNORE INTO description(rid,uuid,rcvid,type,summary)\n"
1175 "SELECT blob.rid, blob.uuid,blob.rcvid,\n"
1176 " CASE WHEN EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)\n"
1177 " THEN 'phantom' ELSE '' END,\n"
1178 " 'unknown'\n"
1179 " FROM blob\n"
1180 " WHERE (blob.rid %s)\n"
1181 " AND (blob.rid NOT IN (SELECT rid FROM description));",
1182 zWhere /*safe-for-%s*/
1183 );
1184
1185 /* Mark private elements */
1186 db_multi_exec(
1187 "UPDATE description SET isPrivate=1 WHERE rid IN private"
1188 );
1189
1190 if( db_exists("SELECT 1 FROM description WHERE summary='unknown'") ){
1191 describe_unknown_artifacts();
1192 }
1193 }
1194
1195 /*
1196 ** Print the content of the description table on stdout.
1197 **
@@ -1135,11 +1213,11 @@
1213 if( zLabel ){
1214 fossil_print("%s\n", zLabel);
1215 zLabel = 0;
1216 }
1217 fossil_print(" %.16s %s", db_column_text(&q,0), db_column_text(&q,1));
1218 if( db_column_int(&q,2) ) fossil_print(" (private)");
1219 fossil_print("\n");
1220 cnt++;
1221 }
1222 db_finalize(&q);
1223 if( zWhere!=0 ) db_multi_exec("DELETE FROM description;");
@@ -1172,19 +1250,21 @@
1250 **
1251 ** Return a page showing all artifacts in the repository. Query parameters:
1252 **
1253 ** n=N Show N artifacts
1254 ** s=S Start with artifact number S
1255 ** priv Show only unpublished or private artifacts
1256 ** phan Show only phantom artifacts
1257 ** hclr Color code hash types (SHA1 vs SHA3)
1258 */
1259 void bloblist_page(void){
1260 Stmt q;
1261 int s = atoi(PD("s","0"));
1262 int n = atoi(PD("n","5000"));
1263 int mx = db_int(0, "SELECT max(rid) FROM blob");
1264 int privOnly = PB("priv");
1265 int phantomOnly = PB("phan");
1266 int hashClr = PB("hclr");
1267 char *zRange;
1268 char *zSha1Bg;
1269 char *zSha3Bg;
1270
@@ -1192,15 +1272,25 @@
1272 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1273 style_header("List Of Artifacts");
1274 style_submenu_element("250 Largest", "bigbloblist");
1275 if( g.perm.Admin ){
1276 style_submenu_element("Artifact Log", "rcvfromlist");
1277 }
1278 if( !phantomOnly ){
1279 style_submenu_element("Phantoms", "bloblist?phan");
1280 }
1281 if( g.perm.Private || g.perm.Admin ){
1282 if( !privOnly ){
1283 style_submenu_element("Private", "bloblist?priv");
1284 }
1285 }else{
1286 privOnly = 0;
1287 }
1288 if( g.perm.Write ){
1289 style_submenu_element("Artifact Stats", "artifact_stats");
1290 }
1291 if( !privOnly && !phantomOnly && mx>n && P("s")==0 ){
1292 int i;
1293 @ <p>Select a range of artifacts to view:</p>
1294 @ <ul>
1295 for(i=1; i<=mx; i+=n){
1296 @ <li> %z(href("%R/bloblist?s=%d&n=%d",i,n))
@@ -1208,51 +1298,156 @@
1298 }
1299 @ </ul>
1300 style_footer();
1301 return;
1302 }
1303 if( phantomOnly || privOnly || mx>n ){
1304 style_submenu_element("Index", "bloblist");
1305 }
1306 if( privOnly ){
1307 zRange = mprintf("IN private");
1308 }else if( phantomOnly ){
1309 zRange = mprintf("IN phantom");
1310 }else{
1311 zRange = mprintf("BETWEEN %d AND %d", s, s+n-1);
1312 }
1313 describe_artifacts(zRange);
1314 fossil_free(zRange);
1315 db_prepare(&q,
1316 "SELECT rid, uuid, summary, isPrivate, type='phantom', rcvid, ref"
1317 " FROM description ORDER BY rid"
1318 );
1319 if( skin_detail_boolean("white-foreground") ){
1320 zSha1Bg = "#714417";
1321 zSha3Bg = "#177117";
1322 }else{
1323 zSha1Bg = "#ebffb0";
1324 zSha3Bg = "#b0ffb0";
1325 }
1326 @ <table cellpadding="2" cellspacing="0" border="1">
1327 if( g.perm.Admin ){
1328 @ <tr><th>RID<th>Hash<th>Rcvid<th>Description<th>Ref<th>Remarks
1329 }else{
1330 @ <tr><th>RID<th>Hash<th>Description<th>Ref<th>Remarks
1331 }
1332 while( db_step(&q)==SQLITE_ROW ){
1333 int rid = db_column_int(&q,0);
1334 const char *zUuid = db_column_text(&q, 1);
1335 const char *zDesc = db_column_text(&q, 2);
1336 int isPriv = db_column_int(&q,3);
1337 int isPhantom = db_column_int(&q,4);
1338 const char *zRef = db_column_text(&q,6);
1339 if( isPriv && !isPhantom && !g.perm.Private && !g.perm.Admin ){
1340 /* Don't show private artifacts to users without Private (x) permission */
1341 continue;
1342 }
1343 if( hashClr ){
1344 const char *zClr = db_column_bytes(&q,1)>40 ? zSha3Bg : zSha1Bg;
1345 @ <tr style='background-color:%s(zClr);'><td align="right">%d(rid)</td>
1346 }else{
1347 @ <tr><td align="right">%d(rid)</td>
1348 }
1349 @ <td>&nbsp;%z(href("%R/info/%!S",zUuid))%S(zUuid)</a>&nbsp;</td>
1350 if( g.perm.Admin ){
1351 int rcvid = db_column_int(&q,5);
1352 if( rcvid<=0 ){
1353 @ <td>&nbsp;
1354 }else{
1355 @ <td><a href='%R/rcvfrom?rcvid=%d(rcvid)'>%d(rcvid)</a>
1356 }
1357 }
1358 @ <td align="left">%h(zDesc)</td>
1359 if( zRef && zRef[0] ){
1360 @ <td>%z(href("%R/info/%!S",zRef))%S(zRef)</a>
1361 }else{
1362 @ <td>&nbsp;
1363 }
1364 if( isPriv || isPhantom ){
1365 if( isPriv==0 ){
1366 @ <td>phantom</td>
1367 }else if( isPhantom==0 ){
1368 @ <td>private</td>
1369 }else{
1370 @ <td>private,phantom</td>
1371 }
1372 }else{
1373 @ <td>&nbsp;
1374 }
1375 @ </tr>
1376 }
1377 @ </table>
1378 db_finalize(&q);
1379 style_footer();
1380 }
1381
1382 /*
1383 ** Output HTML that shows a table of all public phantoms.
1384 */
1385 void table_of_public_phantoms(void){
1386 Stmt q;
1387 char *zRange;
1388 zRange = mprintf("IN (SELECT rid FROM phantom EXCEPT"
1389 " SELECT rid FROM private)");
1390 describe_artifacts(zRange);
1391 fossil_free(zRange);
1392 db_prepare(&q,
1393 "SELECT rid, uuid, summary, ref"
1394 " FROM description ORDER BY rid"
1395 );
1396 @ <table cellpadding="2" cellspacing="0" border="1">
1397 @ <tr><th>RID<th>Description<th>Source
1398 while( db_step(&q)==SQLITE_ROW ){
1399 int rid = db_column_int(&q,0);
1400 const char *zUuid = db_column_text(&q, 1);
1401 const char *zDesc = db_column_text(&q, 2);
1402 const char *zRef = db_column_text(&q,3);
1403 @ <tr><td valign="top">%d(rid)</td>
1404 @ <td valign="top" align="left">%h(zUuid)<br>%h(zDesc)</td>
1405 if( zRef && zRef[0] ){
1406 @ <td valign="top">%z(href("%R/info/%!S",zRef))%!S(zRef)</a>
1407 }else{
1408 @ <td>&nbsp;
1409 }
1410 @ </tr>
1411 }
1412 @ </table>
1413 db_finalize(&q);
1414 }
1415
1416 /*
1417 ** WEBPAGE: phantoms
1418 **
1419 ** Show a list of all "phantom" artifacts that are not marked as "private".
1420 **
1421 ** A "phantom" artifact is an artifact whose hash named appears in some
1422 ** artifact but whose content is unknown. For example, if a manifest
1423 ** references a particular SHA3 hash of a file, but that SHA3 hash is
1424 ** not on the shunning list and is not in the database, then the file
1425 ** is a phantom. We know it exists, but we do not know its content.
1426 **
1427 ** Whenever a sync occurs, both each party looks at its phantom list
1428 ** and for every phantom that is not also marked private, it asks the
1429 ** other party to send it the content. This mechanism helps keep all
1430 ** repositories synced up.
1431 **
1432 ** This page is similar to the /bloblist page in that it lists artifacts.
1433 ** But this page is a special case in that it only shows phantoms that
1434 ** are not private. In other words, this page shows all phantoms that
1435 ** generate extra network traffic on every sync request.
1436 */
1437 void phantom_list_page(void){
1438 login_check_credentials();
1439 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
1440 style_header("Public Phantom Artifacts");
1441 if( g.perm.Admin ){
1442 style_submenu_element("Artifact Log", "rcvfromlist");
1443 style_submenu_element("Artifact List", "bloblist");
1444 }
1445 if( g.perm.Write ){
1446 style_submenu_element("Artifact Stats", "artifact_stats");
1447 }
1448 table_of_public_phantoms();
1449 style_footer();
1450 }
1451
1452 /*
1453 ** WEBPAGE: bigbloblist
1454
+84 -14
--- src/rebuild.c
+++ src/rebuild.c
@@ -1140,30 +1140,79 @@
11401140
free(zFNameFormat);
11411141
zFNameFormat = 0;
11421142
cchFNamePrefix = 0;
11431143
}
11441144
#endif
1145
+
1146
+/*
1147
+** Helper functions used by the `deconstruct' and `reconstruct' commands to
1148
+** save and restore the contents of the PRIVATE table.
1149
+*/
1150
+void private_export(char *zFileName)
1151
+{
1152
+ Stmt q;
1153
+ Blob fctx = empty_blob;
1154
+ blob_append(&fctx, "# The UUIDs of private artifacts\n", -1);
1155
+ db_prepare(&q,
1156
+ "SELECT uuid FROM blob WHERE rid IN ( SELECT rid FROM private );");
1157
+ while( db_step(&q)==SQLITE_ROW ){
1158
+ const char *zUuid = db_column_text(&q, 0);
1159
+ blob_append(&fctx, zUuid, -1);
1160
+ blob_append(&fctx, "\n", -1);
1161
+ }
1162
+ db_finalize(&q);
1163
+ blob_write_to_file(&fctx, zFileName);
1164
+ blob_reset(&fctx);
1165
+}
1166
+void private_import(char *zFileName)
1167
+{
1168
+ Blob fctx;
1169
+ if( blob_read_from_file(&fctx, zFileName, ExtFILE)!=-1 ){
1170
+ Blob line, value;
1171
+ while( blob_line(&fctx, &line)>0 ){
1172
+ char *zUuid;
1173
+ int nUuid;
1174
+ if( blob_token(&line, &value)==0 ) continue; /* Empty line */
1175
+ if( blob_buffer(&value)[0]=='#' ) continue; /* Comment */
1176
+ blob_trim(&value);
1177
+ zUuid = blob_buffer(&value);
1178
+ nUuid = blob_size(&value);
1179
+ zUuid[nUuid] = 0;
1180
+ if( hname_validate(zUuid, nUuid)!=HNAME_ERROR ){
1181
+ canonical16(zUuid, nUuid);
1182
+ db_multi_exec(
1183
+ "INSERT OR IGNORE INTO private"
1184
+ " SELECT rid FROM blob WHERE uuid = %Q;",
1185
+ zUuid);
1186
+ }
1187
+ }
1188
+ blob_reset(&fctx);
1189
+ }
1190
+}
11451191
11461192
/*
11471193
** COMMAND: reconstruct*
11481194
**
11491195
** Usage: %fossil reconstruct ?OPTIONS? FILENAME DIRECTORY
11501196
**
1151
-** This command studies the artifacts (files) in DIRECTORY and
1152
-** reconstructs the fossil record from them. It places the new
1153
-** fossil repository in FILENAME. Subdirectories are read, files
1154
-** with leading '.' in the filename are ignored.
1197
+** This command studies the artifacts (files) in DIRECTORY and reconstructs the
1198
+** Fossil record from them. It places the new Fossil repository in FILENAME.
1199
+** Subdirectories are read, files with leading '.' in the filename are ignored.
11551200
**
11561201
** Options:
1157
-** -K|--keep-rid1 Read the filename of the artifact with
1158
-** RID=1 from the file .rid in DIRECTORY.
1202
+** -K|--keep-rid1 Read the filename of the artifact with RID=1 from the
1203
+** file .rid in DIRECTORY.
1204
+** -P|--keep-private Mark the artifacts listed in the file .private in
1205
+** DIRECTORY as private in the new Fossil repository.
11591206
**
11601207
** See also: deconstruct, rebuild
11611208
*/
11621209
void reconstruct_cmd(void) {
11631210
char *zPassword;
1211
+ int fKeepPrivate;
11641212
fKeepRid1 = find_option("keep-rid1","K",0)!=0;
1213
+ fKeepPrivate = find_option("keep-private","P",0)!=0;
11651214
if( g.argc!=4 ){
11661215
usage("FILENAME DIRECTORY");
11671216
}
11681217
if( file_isdir(g.argv[3], ExtFILE)!=1 ){
11691218
fossil_print("\"%s\" is not a directory\n\n", g.argv[3]);
@@ -1182,11 +1231,19 @@
11821231
fossil_print("Reading files from directory \"%s\"...\n", g.argv[3]);
11831232
recon_read_dir(g.argv[3]);
11841233
fossil_print("\nBuilding the Fossil repository...\n");
11851234
11861235
rebuild_db(0, 1, 1);
1236
+
1237
+ /* Backwards compatibility: Mark check-ins with "+private" tags as private. */
11871238
reconstruct_private_table();
1239
+ /* Newer method: Import the list of private artifacts to the PRIVATE table. */
1240
+ if( fKeepPrivate ){
1241
+ char *zFnDotPrivate = mprintf("%s/.private", g.argv[3]);
1242
+ private_import(zFnDotPrivate);
1243
+ free(zFnDotPrivate);
1244
+ }
11881245
11891246
/* Skip the verify_before_commit() step on a reconstruct. Most artifacts
11901247
** will have been changed and verification therefore takes a really, really
11911248
** long time.
11921249
*/
@@ -1202,32 +1259,35 @@
12021259
/*
12031260
** COMMAND: deconstruct*
12041261
**
12051262
** Usage %fossil deconstruct ?OPTIONS? DESTINATION
12061263
**
1207
-**
1208
-** This command exports all artifacts of a given repository and
1209
-** writes all artifacts to the file system. The DESTINATION directory
1210
-** will be populated with subdirectories AA and files AA/BBBBBBBBB.., where
1211
-** AABBBBBBBBB.. is the 40+ character artifact ID, AA the first 2 characters.
1212
-** If -L|--prefixlength is given, the length (default 2) of the directory
1213
-** prefix can be set to 0,1,..,9 characters.
1264
+** This command exports all artifacts of a given repository and writes all
1265
+** artifacts to the file system. The DESTINATION directory will be populated
1266
+** with subdirectories AA and files AA/BBBBBBBBB.., where AABBBBBBBBB.. is the
1267
+** 40+ character artifact ID, AA the first 2 characters.
1268
+** If -L|--prefixlength is given, the length (default 2) of the directory prefix
1269
+** can be set to 0,1,..,9 characters.
12141270
**
12151271
** Options:
12161272
** -R|--repository REPOSITORY Deconstruct given REPOSITORY.
12171273
** -K|--keep-rid1 Save the filename of the artifact with RID=1 to
12181274
** the file .rid1 in the DESTINATION directory.
12191275
** -L|--prefixlength N Set the length of the names of the DESTINATION
12201276
** subdirectories to N.
12211277
** --private Include private artifacts.
1278
+** -P|--keep-private Save the list of private artifacts to the file
1279
+** .private in the DESTINATION directory (implies
1280
+** the --private option).
12221281
**
1223
-** See also: rebuild, reconstruct
1282
+** See also: reconstruct, rebuild
12241283
*/
12251284
void deconstruct_cmd(void){
12261285
const char *zPrefixOpt;
12271286
Stmt s;
12281287
int privateFlag;
1288
+ int fKeepPrivate;
12291289
12301290
fKeepRid1 = find_option("keep-rid1","K",0)!=0;
12311291
/* get and check prefix length argument and build format string */
12321292
zPrefixOpt=find_option("prefixlength","L",1);
12331293
if( !zPrefixOpt ){
@@ -1240,10 +1300,12 @@
12401300
}
12411301
}
12421302
/* open repository and open query for all artifacts */
12431303
db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
12441304
privateFlag = find_option("private",0,0)!=0;
1305
+ fKeepPrivate = find_option("keep-private","P",0)!=0;
1306
+ if( fKeepPrivate ) privateFlag = 1;
12451307
verify_all_options();
12461308
/* check number of arguments */
12471309
if( g.argc!=3 ){
12481310
usage ("?OPTIONS? DESTINATION");
12491311
}
@@ -1307,13 +1369,21 @@
13071369
rebuild_step(rid, size, &content);
13081370
}
13091371
}
13101372
}
13111373
db_finalize(&s);
1374
+
1375
+ /* Export the list of private artifacts. */
1376
+ if( fKeepPrivate ){
1377
+ char *zFnDotPrivate = mprintf("%s/.private", zDestDir);
1378
+ private_export(zFnDotPrivate);
1379
+ free(zFnDotPrivate);
1380
+ }
1381
+
13121382
if(!g.fQuiet && ttyOutput ){
13131383
fossil_print("\n");
13141384
}
13151385
13161386
/* free filename format string */
13171387
free(zFNameFormat);
13181388
zFNameFormat = 0;
13191389
}
13201390
--- src/rebuild.c
+++ src/rebuild.c
@@ -1140,30 +1140,79 @@
1140 free(zFNameFormat);
1141 zFNameFormat = 0;
1142 cchFNamePrefix = 0;
1143 }
1144 #endif
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1145
1146 /*
1147 ** COMMAND: reconstruct*
1148 **
1149 ** Usage: %fossil reconstruct ?OPTIONS? FILENAME DIRECTORY
1150 **
1151 ** This command studies the artifacts (files) in DIRECTORY and
1152 ** reconstructs the fossil record from them. It places the new
1153 ** fossil repository in FILENAME. Subdirectories are read, files
1154 ** with leading '.' in the filename are ignored.
1155 **
1156 ** Options:
1157 ** -K|--keep-rid1 Read the filename of the artifact with
1158 ** RID=1 from the file .rid in DIRECTORY.
 
 
1159 **
1160 ** See also: deconstruct, rebuild
1161 */
1162 void reconstruct_cmd(void) {
1163 char *zPassword;
 
1164 fKeepRid1 = find_option("keep-rid1","K",0)!=0;
 
1165 if( g.argc!=4 ){
1166 usage("FILENAME DIRECTORY");
1167 }
1168 if( file_isdir(g.argv[3], ExtFILE)!=1 ){
1169 fossil_print("\"%s\" is not a directory\n\n", g.argv[3]);
@@ -1182,11 +1231,19 @@
1182 fossil_print("Reading files from directory \"%s\"...\n", g.argv[3]);
1183 recon_read_dir(g.argv[3]);
1184 fossil_print("\nBuilding the Fossil repository...\n");
1185
1186 rebuild_db(0, 1, 1);
 
 
1187 reconstruct_private_table();
 
 
 
 
 
 
1188
1189 /* Skip the verify_before_commit() step on a reconstruct. Most artifacts
1190 ** will have been changed and verification therefore takes a really, really
1191 ** long time.
1192 */
@@ -1202,32 +1259,35 @@
1202 /*
1203 ** COMMAND: deconstruct*
1204 **
1205 ** Usage %fossil deconstruct ?OPTIONS? DESTINATION
1206 **
1207 **
1208 ** This command exports all artifacts of a given repository and
1209 ** writes all artifacts to the file system. The DESTINATION directory
1210 ** will be populated with subdirectories AA and files AA/BBBBBBBBB.., where
1211 ** AABBBBBBBBB.. is the 40+ character artifact ID, AA the first 2 characters.
1212 ** If -L|--prefixlength is given, the length (default 2) of the directory
1213 ** prefix can be set to 0,1,..,9 characters.
1214 **
1215 ** Options:
1216 ** -R|--repository REPOSITORY Deconstruct given REPOSITORY.
1217 ** -K|--keep-rid1 Save the filename of the artifact with RID=1 to
1218 ** the file .rid1 in the DESTINATION directory.
1219 ** -L|--prefixlength N Set the length of the names of the DESTINATION
1220 ** subdirectories to N.
1221 ** --private Include private artifacts.
 
 
 
1222 **
1223 ** See also: rebuild, reconstruct
1224 */
1225 void deconstruct_cmd(void){
1226 const char *zPrefixOpt;
1227 Stmt s;
1228 int privateFlag;
 
1229
1230 fKeepRid1 = find_option("keep-rid1","K",0)!=0;
1231 /* get and check prefix length argument and build format string */
1232 zPrefixOpt=find_option("prefixlength","L",1);
1233 if( !zPrefixOpt ){
@@ -1240,10 +1300,12 @@
1240 }
1241 }
1242 /* open repository and open query for all artifacts */
1243 db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
1244 privateFlag = find_option("private",0,0)!=0;
 
 
1245 verify_all_options();
1246 /* check number of arguments */
1247 if( g.argc!=3 ){
1248 usage ("?OPTIONS? DESTINATION");
1249 }
@@ -1307,13 +1369,21 @@
1307 rebuild_step(rid, size, &content);
1308 }
1309 }
1310 }
1311 db_finalize(&s);
 
 
 
 
 
 
 
 
1312 if(!g.fQuiet && ttyOutput ){
1313 fossil_print("\n");
1314 }
1315
1316 /* free filename format string */
1317 free(zFNameFormat);
1318 zFNameFormat = 0;
1319 }
1320
--- src/rebuild.c
+++ src/rebuild.c
@@ -1140,30 +1140,79 @@
1140 free(zFNameFormat);
1141 zFNameFormat = 0;
1142 cchFNamePrefix = 0;
1143 }
1144 #endif
1145
1146 /*
1147 ** Helper functions used by the `deconstruct' and `reconstruct' commands to
1148 ** save and restore the contents of the PRIVATE table.
1149 */
1150 void private_export(char *zFileName)
1151 {
1152 Stmt q;
1153 Blob fctx = empty_blob;
1154 blob_append(&fctx, "# The UUIDs of private artifacts\n", -1);
1155 db_prepare(&q,
1156 "SELECT uuid FROM blob WHERE rid IN ( SELECT rid FROM private );");
1157 while( db_step(&q)==SQLITE_ROW ){
1158 const char *zUuid = db_column_text(&q, 0);
1159 blob_append(&fctx, zUuid, -1);
1160 blob_append(&fctx, "\n", -1);
1161 }
1162 db_finalize(&q);
1163 blob_write_to_file(&fctx, zFileName);
1164 blob_reset(&fctx);
1165 }
1166 void private_import(char *zFileName)
1167 {
1168 Blob fctx;
1169 if( blob_read_from_file(&fctx, zFileName, ExtFILE)!=-1 ){
1170 Blob line, value;
1171 while( blob_line(&fctx, &line)>0 ){
1172 char *zUuid;
1173 int nUuid;
1174 if( blob_token(&line, &value)==0 ) continue; /* Empty line */
1175 if( blob_buffer(&value)[0]=='#' ) continue; /* Comment */
1176 blob_trim(&value);
1177 zUuid = blob_buffer(&value);
1178 nUuid = blob_size(&value);
1179 zUuid[nUuid] = 0;
1180 if( hname_validate(zUuid, nUuid)!=HNAME_ERROR ){
1181 canonical16(zUuid, nUuid);
1182 db_multi_exec(
1183 "INSERT OR IGNORE INTO private"
1184 " SELECT rid FROM blob WHERE uuid = %Q;",
1185 zUuid);
1186 }
1187 }
1188 blob_reset(&fctx);
1189 }
1190 }
1191
1192 /*
1193 ** COMMAND: reconstruct*
1194 **
1195 ** Usage: %fossil reconstruct ?OPTIONS? FILENAME DIRECTORY
1196 **
1197 ** This command studies the artifacts (files) in DIRECTORY and reconstructs the
1198 ** Fossil record from them. It places the new Fossil repository in FILENAME.
1199 ** Subdirectories are read, files with leading '.' in the filename are ignored.
 
1200 **
1201 ** Options:
1202 ** -K|--keep-rid1 Read the filename of the artifact with RID=1 from the
1203 ** file .rid in DIRECTORY.
1204 ** -P|--keep-private Mark the artifacts listed in the file .private in
1205 ** DIRECTORY as private in the new Fossil repository.
1206 **
1207 ** See also: deconstruct, rebuild
1208 */
1209 void reconstruct_cmd(void) {
1210 char *zPassword;
1211 int fKeepPrivate;
1212 fKeepRid1 = find_option("keep-rid1","K",0)!=0;
1213 fKeepPrivate = find_option("keep-private","P",0)!=0;
1214 if( g.argc!=4 ){
1215 usage("FILENAME DIRECTORY");
1216 }
1217 if( file_isdir(g.argv[3], ExtFILE)!=1 ){
1218 fossil_print("\"%s\" is not a directory\n\n", g.argv[3]);
@@ -1182,11 +1231,19 @@
1231 fossil_print("Reading files from directory \"%s\"...\n", g.argv[3]);
1232 recon_read_dir(g.argv[3]);
1233 fossil_print("\nBuilding the Fossil repository...\n");
1234
1235 rebuild_db(0, 1, 1);
1236
1237 /* Backwards compatibility: Mark check-ins with "+private" tags as private. */
1238 reconstruct_private_table();
1239 /* Newer method: Import the list of private artifacts to the PRIVATE table. */
1240 if( fKeepPrivate ){
1241 char *zFnDotPrivate = mprintf("%s/.private", g.argv[3]);
1242 private_import(zFnDotPrivate);
1243 free(zFnDotPrivate);
1244 }
1245
1246 /* Skip the verify_before_commit() step on a reconstruct. Most artifacts
1247 ** will have been changed and verification therefore takes a really, really
1248 ** long time.
1249 */
@@ -1202,32 +1259,35 @@
1259 /*
1260 ** COMMAND: deconstruct*
1261 **
1262 ** Usage %fossil deconstruct ?OPTIONS? DESTINATION
1263 **
1264 ** This command exports all artifacts of a given repository and writes all
1265 ** artifacts to the file system. The DESTINATION directory will be populated
1266 ** with subdirectories AA and files AA/BBBBBBBBB.., where AABBBBBBBBB.. is the
1267 ** 40+ character artifact ID, AA the first 2 characters.
1268 ** If -L|--prefixlength is given, the length (default 2) of the directory prefix
1269 ** can be set to 0,1,..,9 characters.
 
1270 **
1271 ** Options:
1272 ** -R|--repository REPOSITORY Deconstruct given REPOSITORY.
1273 ** -K|--keep-rid1 Save the filename of the artifact with RID=1 to
1274 ** the file .rid1 in the DESTINATION directory.
1275 ** -L|--prefixlength N Set the length of the names of the DESTINATION
1276 ** subdirectories to N.
1277 ** --private Include private artifacts.
1278 ** -P|--keep-private Save the list of private artifacts to the file
1279 ** .private in the DESTINATION directory (implies
1280 ** the --private option).
1281 **
1282 ** See also: reconstruct, rebuild
1283 */
1284 void deconstruct_cmd(void){
1285 const char *zPrefixOpt;
1286 Stmt s;
1287 int privateFlag;
1288 int fKeepPrivate;
1289
1290 fKeepRid1 = find_option("keep-rid1","K",0)!=0;
1291 /* get and check prefix length argument and build format string */
1292 zPrefixOpt=find_option("prefixlength","L",1);
1293 if( !zPrefixOpt ){
@@ -1240,10 +1300,12 @@
1300 }
1301 }
1302 /* open repository and open query for all artifacts */
1303 db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
1304 privateFlag = find_option("private",0,0)!=0;
1305 fKeepPrivate = find_option("keep-private","P",0)!=0;
1306 if( fKeepPrivate ) privateFlag = 1;
1307 verify_all_options();
1308 /* check number of arguments */
1309 if( g.argc!=3 ){
1310 usage ("?OPTIONS? DESTINATION");
1311 }
@@ -1307,13 +1369,21 @@
1369 rebuild_step(rid, size, &content);
1370 }
1371 }
1372 }
1373 db_finalize(&s);
1374
1375 /* Export the list of private artifacts. */
1376 if( fKeepPrivate ){
1377 char *zFnDotPrivate = mprintf("%s/.private", zDestDir);
1378 private_export(zFnDotPrivate);
1379 free(zFnDotPrivate);
1380 }
1381
1382 if(!g.fQuiet && ttyOutput ){
1383 fossil_print("\n");
1384 }
1385
1386 /* free filename format string */
1387 free(zFNameFormat);
1388 zFNameFormat = 0;
1389 }
1390
+19 -1
--- src/schema.c
+++ src/schema.c
@@ -158,10 +158,16 @@
158158
@ );
159159
@
160160
@ -- Artifacts that should not be pushed are stored in the "private"
161161
@ -- table. Private artifacts are omitted from the "unclustered" and
162162
@ -- "unsent" tables.
163
+@ --
164
+@ -- A phantom artifact (that is, an artifact with BLOB.SIZE<0 - an artifact
165
+@ -- for which we do not know the content) might also be marked as private.
166
+@ -- This comes about when an artifact is named in a manifest or tag but
167
+@ -- the content of that artifact is held privately by some other peer
168
+@ -- repository.
163169
@ --
164170
@ CREATE TABLE private(rid INTEGER PRIMARY KEY);
165171
@
166172
@ -- An entry in this table describes a database query that generates a
167173
@ -- table of tickets.
@@ -401,11 +407,11 @@
401407
@ -- the following table for that hyperlink. This table is used to
402408
@ -- facilitate the display of "back links".
403409
@ --
404410
@ CREATE TABLE backlink(
405411
@ target TEXT, -- Where the hyperlink points to
406
-@ srctype INT, -- 0: check-in 1: ticket 2: wiki
412
+@ srctype INT, -- 0=comment 1=ticket 2=wiki. See BKLNK_* below.
407413
@ srcid INT, -- EVENT.OBJID for the source document
408414
@ mtime TIMESTAMP, -- time that the hyperlink was added. Julian day.
409415
@ UNIQUE(target, srctype, srcid)
410416
@ );
411417
@ CREATE INDEX backlink_src ON backlink(srcid, srctype);
@@ -470,10 +476,22 @@
470476
@ PRIMARY KEY(parentid, childid)
471477
@ ) WITHOUT ROWID;
472478
@ CREATE INDEX cherrypick_cid ON cherrypick(childid);
473479
;
474480
481
+/*
482
+** Allowed values for backlink.srctype
483
+*/
484
+#if INTERFACE
485
+# define BKLNK_COMMENT 0 /* Check-in comment */
486
+# define BKLNK_TICKET 1 /* Ticket body or title */
487
+# define BKLNK_WIKI 2 /* Wiki */
488
+# define BKLNK_EVENT 3 /* Technote */
489
+# define BKLNK_FORUM 4 /* Forum post */
490
+# define ValidBklnk(X) (X>=0 && X<=4) /* True if backlink.srctype is valid */
491
+#endif
492
+
475493
/*
476494
** Predefined tagid values
477495
*/
478496
#if INTERFACE
479497
# define TAG_BGCOLOR 1 /* Set the background color for display */
480498
--- src/schema.c
+++ src/schema.c
@@ -158,10 +158,16 @@
158 @ );
159 @
160 @ -- Artifacts that should not be pushed are stored in the "private"
161 @ -- table. Private artifacts are omitted from the "unclustered" and
162 @ -- "unsent" tables.
 
 
 
 
 
 
163 @ --
164 @ CREATE TABLE private(rid INTEGER PRIMARY KEY);
165 @
166 @ -- An entry in this table describes a database query that generates a
167 @ -- table of tickets.
@@ -401,11 +407,11 @@
401 @ -- the following table for that hyperlink. This table is used to
402 @ -- facilitate the display of "back links".
403 @ --
404 @ CREATE TABLE backlink(
405 @ target TEXT, -- Where the hyperlink points to
406 @ srctype INT, -- 0: check-in 1: ticket 2: wiki
407 @ srcid INT, -- EVENT.OBJID for the source document
408 @ mtime TIMESTAMP, -- time that the hyperlink was added. Julian day.
409 @ UNIQUE(target, srctype, srcid)
410 @ );
411 @ CREATE INDEX backlink_src ON backlink(srcid, srctype);
@@ -470,10 +476,22 @@
470 @ PRIMARY KEY(parentid, childid)
471 @ ) WITHOUT ROWID;
472 @ CREATE INDEX cherrypick_cid ON cherrypick(childid);
473 ;
474
 
 
 
 
 
 
 
 
 
 
 
 
475 /*
476 ** Predefined tagid values
477 */
478 #if INTERFACE
479 # define TAG_BGCOLOR 1 /* Set the background color for display */
480
--- src/schema.c
+++ src/schema.c
@@ -158,10 +158,16 @@
158 @ );
159 @
160 @ -- Artifacts that should not be pushed are stored in the "private"
161 @ -- table. Private artifacts are omitted from the "unclustered" and
162 @ -- "unsent" tables.
163 @ --
164 @ -- A phantom artifact (that is, an artifact with BLOB.SIZE<0 - an artifact
165 @ -- for which we do not know the content) might also be marked as private.
166 @ -- This comes about when an artifact is named in a manifest or tag but
167 @ -- the content of that artifact is held privately by some other peer
168 @ -- repository.
169 @ --
170 @ CREATE TABLE private(rid INTEGER PRIMARY KEY);
171 @
172 @ -- An entry in this table describes a database query that generates a
173 @ -- table of tickets.
@@ -401,11 +407,11 @@
407 @ -- the following table for that hyperlink. This table is used to
408 @ -- facilitate the display of "back links".
409 @ --
410 @ CREATE TABLE backlink(
411 @ target TEXT, -- Where the hyperlink points to
412 @ srctype INT, -- 0=comment 1=ticket 2=wiki. See BKLNK_* below.
413 @ srcid INT, -- EVENT.OBJID for the source document
414 @ mtime TIMESTAMP, -- time that the hyperlink was added. Julian day.
415 @ UNIQUE(target, srctype, srcid)
416 @ );
417 @ CREATE INDEX backlink_src ON backlink(srcid, srctype);
@@ -470,10 +476,22 @@
476 @ PRIMARY KEY(parentid, childid)
477 @ ) WITHOUT ROWID;
478 @ CREATE INDEX cherrypick_cid ON cherrypick(childid);
479 ;
480
481 /*
482 ** Allowed values for backlink.srctype
483 */
484 #if INTERFACE
485 # define BKLNK_COMMENT 0 /* Check-in comment */
486 # define BKLNK_TICKET 1 /* Ticket body or title */
487 # define BKLNK_WIKI 2 /* Wiki */
488 # define BKLNK_EVENT 3 /* Technote */
489 # define BKLNK_FORUM 4 /* Forum post */
490 # define ValidBklnk(X) (X>=0 && X<=4) /* True if backlink.srctype is valid */
491 #endif
492
493 /*
494 ** Predefined tagid values
495 */
496 #if INTERFACE
497 # define TAG_BGCOLOR 1 /* Set the background color for display */
498
--- src/security_audit.c
+++ src/security_audit.c
@@ -122,11 +122,11 @@
122122
zAnonCap = db_text("", "SELECT fullcap(NULL)");
123123
zDevCap = db_text("", "SELECT fullcap('v')");
124124
zReadCap = db_text("", "SELECT fullcap('u')");
125125
zPubPages = db_get("public-pages",0);
126126
hasSelfReg = db_get_boolean("self-register",0);
127
- pCap = capability_add(0, db_get("default-perms",0));
127
+ pCap = capability_add(0, db_get("default-perms","u"));
128128
capability_expand(pCap);
129129
zSelfCap = capability_string(pCap);
130130
capability_free(pCap);
131131
if( hasAnyCap(zAnonCap,"as") ){
132132
@ <li><p>This repository is <big><b>Wildly INSECURE</b></big> because
@@ -553,10 +553,25 @@
553553
stats_for_email();
554554
@ </table>
555555
}else{
556556
@ <li><p> Email alerts are disabled
557557
}
558
+
559
+ n = db_int(0,"SELECT count(*) FROM ("
560
+ "SELECT rid FROM phantom EXCEPT SELECT rid FROM private)");
561
+ if( n>0 ){
562
+ @ <li><p>\
563
+ @ There exists public phantom artifacts in this repository, shown below.
564
+ @ Phantom artifacts are artifacts whose hash name is referenced by some
565
+ @ other artifact but whose content is unknown. Some phantoms are marked
566
+ @ private and those are ignored. But public phantoms cause unnecessary
567
+ @ sync traffic and might represent malicious attempts to corrupt the
568
+ @ repository structure.
569
+ @ </p>
570
+ table_of_public_phantoms();
571
+ @ </li>
572
+ }
558573
559574
@ </ol>
560575
style_footer();
561576
}
562577
563578
--- src/security_audit.c
+++ src/security_audit.c
@@ -122,11 +122,11 @@
122 zAnonCap = db_text("", "SELECT fullcap(NULL)");
123 zDevCap = db_text("", "SELECT fullcap('v')");
124 zReadCap = db_text("", "SELECT fullcap('u')");
125 zPubPages = db_get("public-pages",0);
126 hasSelfReg = db_get_boolean("self-register",0);
127 pCap = capability_add(0, db_get("default-perms",0));
128 capability_expand(pCap);
129 zSelfCap = capability_string(pCap);
130 capability_free(pCap);
131 if( hasAnyCap(zAnonCap,"as") ){
132 @ <li><p>This repository is <big><b>Wildly INSECURE</b></big> because
@@ -553,10 +553,25 @@
553 stats_for_email();
554 @ </table>
555 }else{
556 @ <li><p> Email alerts are disabled
557 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
558
559 @ </ol>
560 style_footer();
561 }
562
563
--- src/security_audit.c
+++ src/security_audit.c
@@ -122,11 +122,11 @@
122 zAnonCap = db_text("", "SELECT fullcap(NULL)");
123 zDevCap = db_text("", "SELECT fullcap('v')");
124 zReadCap = db_text("", "SELECT fullcap('u')");
125 zPubPages = db_get("public-pages",0);
126 hasSelfReg = db_get_boolean("self-register",0);
127 pCap = capability_add(0, db_get("default-perms","u"));
128 capability_expand(pCap);
129 zSelfCap = capability_string(pCap);
130 capability_free(pCap);
131 if( hasAnyCap(zAnonCap,"as") ){
132 @ <li><p>This repository is <big><b>Wildly INSECURE</b></big> because
@@ -553,10 +553,25 @@
553 stats_for_email();
554 @ </table>
555 }else{
556 @ <li><p> Email alerts are disabled
557 }
558
559 n = db_int(0,"SELECT count(*) FROM ("
560 "SELECT rid FROM phantom EXCEPT SELECT rid FROM private)");
561 if( n>0 ){
562 @ <li><p>\
563 @ There exists public phantom artifacts in this repository, shown below.
564 @ Phantom artifacts are artifacts whose hash name is referenced by some
565 @ other artifact but whose content is unknown. Some phantoms are marked
566 @ private and those are ignored. But public phantoms cause unnecessary
567 @ sync traffic and might represent malicious attempts to corrupt the
568 @ repository structure.
569 @ </p>
570 table_of_public_phantoms();
571 @ </li>
572 }
573
574 @ </ol>
575 style_footer();
576 }
577
578
+45 -13
--- src/setup.c
+++ src/setup.c
@@ -500,17 +500,44 @@
500500
@ (Property: "public-pages")
501501
@ </p>
502502
503503
@ <hr />
504504
onoff_attribute("Allow users to register themselves",
505
- "self-register", "selfregister", 0, 0);
506
- @ <p>Allow users to register themselves through the HTTP UI.
507
- @ The registration form always requires filling in a CAPTCHA
508
- @ (<em>auto-captcha</em> setting is ignored). Still, bear in mind that anyone
509
- @ can register under any user name. This option is useful for public projects
510
- @ where you do not want everyone in any ticket discussion to be named
511
- @ "Anonymous". (Property: "self-register")</p>
505
+ "self-register", "selfreg", 0, 0);
506
+ @ <p>Allow users to register themselves on the /register webpage.
507
+ @ A self-registration creates a new entry in the USER table and
508
+ @ perhaps also in the SUBSCRIBER table if email notification is
509
+ @ enabled.
510
+ @ (Property: "self-register")</p>
511
+
512
+ @ <hr />
513
+ onoff_attribute("Email verification required for self-registration",
514
+ "selfreg-verify", "sfverify", 0, 0);
515
+ @ <p>If enabled, self-registration creates a new entry in the USER table
516
+ @ with only capabilities "7". The default user capabilities are not
517
+ @ added until the email address associated with the self-registration
518
+ @ has been verified. This setting only makes sense if
519
+ @ email notifications are enabled.
520
+ @ (Property: "selfreg-verify")</p>
521
+
522
+ @ <hr />
523
+ onoff_attribute("Allow anonymous subscriptions",
524
+ "anon-subscribe", "anonsub", 1, 0);
525
+ @ <p>If disabled, email notification subscriptions are only allowed
526
+ @ for users with a login. If Nobody or Anonymous visit the /subscribe
527
+ @ page, they are redirected to /register or /login.
528
+ @ (Property: "anon-subscribe")</p>
529
+
530
+ @ <hr />
531
+ entry_attribute("Authorized subscription email addresses", 35,
532
+ "auth-sub-email", "asemail", "", 0);
533
+ @ <p>This is a comma-separated list of GLOB patterns that specify
534
+ @ email addresses that are authorized to subscriptions. If blank
535
+ @ (the usual case), then any email address can be used to self-register.
536
+ @ This setting is used to limit subscriptions to members of a particular
537
+ @ organization or group based on their email address.
538
+ @ (Property: "auth-sub-email")</p>
512539
513540
@ <hr />
514541
entry_attribute("Default privileges", 10, "default-perms",
515542
"defaultperms", "u", 0);
516543
@ <p>Permissions given to users that... <ul><li>register themselves using
@@ -849,25 +876,30 @@
849876
}
850877
}
851878
}
852879
@ <br /><input type="submit" name="submit" value="Apply Changes" />
853880
@ </td><td style="width:50px;"></td><td valign="top">
881
+ @ <table>
854882
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
855883
if( pSet->width>0 && !pSet->forceTextArea ){
856884
int hasVersionableValue = pSet->versionable &&
857885
(db_get_versioned(pSet->name, NULL)!=0);
886
+ @ <tr><td>
887
+ @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
888
+ if( pSet->versionable ){
889
+ @ (v)
890
+ } else {
891
+ @
892
+ }
893
+ @</td><td>
858894
entry_attribute("", /*pSet->width*/ 25, pSet->name,
859895
pSet->var!=0 ? pSet->var : pSet->name,
860896
(char*)pSet->def, hasVersionableValue);
861
- @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
862
- if( pSet->versionable ){
863
- @ (v)<br />
864
- } else {
865
- @ <br />
866
- }
897
+ @</td></tr>
867898
}
868899
}
900
+ @</table>
869901
@ </td><td style="width:50px;"></td><td valign="top">
870902
for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
871903
if( pSet->width>0 && pSet->forceTextArea ){
872904
int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0;
873905
@ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a>
874906
--- src/setup.c
+++ src/setup.c
@@ -500,17 +500,44 @@
500 @ (Property: "public-pages")
501 @ </p>
502
503 @ <hr />
504 onoff_attribute("Allow users to register themselves",
505 "self-register", "selfregister", 0, 0);
506 @ <p>Allow users to register themselves through the HTTP UI.
507 @ The registration form always requires filling in a CAPTCHA
508 @ (<em>auto-captcha</em> setting is ignored). Still, bear in mind that anyone
509 @ can register under any user name. This option is useful for public projects
510 @ where you do not want everyone in any ticket discussion to be named
511 @ "Anonymous". (Property: "self-register")</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
512
513 @ <hr />
514 entry_attribute("Default privileges", 10, "default-perms",
515 "defaultperms", "u", 0);
516 @ <p>Permissions given to users that... <ul><li>register themselves using
@@ -849,25 +876,30 @@
849 }
850 }
851 }
852 @ <br /><input type="submit" name="submit" value="Apply Changes" />
853 @ </td><td style="width:50px;"></td><td valign="top">
 
854 for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
855 if( pSet->width>0 && !pSet->forceTextArea ){
856 int hasVersionableValue = pSet->versionable &&
857 (db_get_versioned(pSet->name, NULL)!=0);
 
 
 
 
 
 
 
 
858 entry_attribute("", /*pSet->width*/ 25, pSet->name,
859 pSet->var!=0 ? pSet->var : pSet->name,
860 (char*)pSet->def, hasVersionableValue);
861 @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
862 if( pSet->versionable ){
863 @ (v)<br />
864 } else {
865 @ <br />
866 }
867 }
868 }
 
869 @ </td><td style="width:50px;"></td><td valign="top">
870 for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
871 if( pSet->width>0 && pSet->forceTextArea ){
872 int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0;
873 @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a>
874
--- src/setup.c
+++ src/setup.c
@@ -500,17 +500,44 @@
500 @ (Property: "public-pages")
501 @ </p>
502
503 @ <hr />
504 onoff_attribute("Allow users to register themselves",
505 "self-register", "selfreg", 0, 0);
506 @ <p>Allow users to register themselves on the /register webpage.
507 @ A self-registration creates a new entry in the USER table and
508 @ perhaps also in the SUBSCRIBER table if email notification is
509 @ enabled.
510 @ (Property: "self-register")</p>
511
512 @ <hr />
513 onoff_attribute("Email verification required for self-registration",
514 "selfreg-verify", "sfverify", 0, 0);
515 @ <p>If enabled, self-registration creates a new entry in the USER table
516 @ with only capabilities "7". The default user capabilities are not
517 @ added until the email address associated with the self-registration
518 @ has been verified. This setting only makes sense if
519 @ email notifications are enabled.
520 @ (Property: "selfreg-verify")</p>
521
522 @ <hr />
523 onoff_attribute("Allow anonymous subscriptions",
524 "anon-subscribe", "anonsub", 1, 0);
525 @ <p>If disabled, email notification subscriptions are only allowed
526 @ for users with a login. If Nobody or Anonymous visit the /subscribe
527 @ page, they are redirected to /register or /login.
528 @ (Property: "anon-subscribe")</p>
529
530 @ <hr />
531 entry_attribute("Authorized subscription email addresses", 35,
532 "auth-sub-email", "asemail", "", 0);
533 @ <p>This is a comma-separated list of GLOB patterns that specify
534 @ email addresses that are authorized to subscriptions. If blank
535 @ (the usual case), then any email address can be used to self-register.
536 @ This setting is used to limit subscriptions to members of a particular
537 @ organization or group based on their email address.
538 @ (Property: "auth-sub-email")</p>
539
540 @ <hr />
541 entry_attribute("Default privileges", 10, "default-perms",
542 "defaultperms", "u", 0);
543 @ <p>Permissions given to users that... <ul><li>register themselves using
@@ -849,25 +876,30 @@
876 }
877 }
878 }
879 @ <br /><input type="submit" name="submit" value="Apply Changes" />
880 @ </td><td style="width:50px;"></td><td valign="top">
881 @ <table>
882 for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
883 if( pSet->width>0 && !pSet->forceTextArea ){
884 int hasVersionableValue = pSet->versionable &&
885 (db_get_versioned(pSet->name, NULL)!=0);
886 @ <tr><td>
887 @ <a href='%R/help?cmd=%s(pSet->name)'>%h(pSet->name)</a>
888 if( pSet->versionable ){
889 @ (v)
890 } else {
891 @
892 }
893 @</td><td>
894 entry_attribute("", /*pSet->width*/ 25, pSet->name,
895 pSet->var!=0 ? pSet->var : pSet->name,
896 (char*)pSet->def, hasVersionableValue);
897 @</td></tr>
 
 
 
 
 
898 }
899 }
900 @</table>
901 @ </td><td style="width:50px;"></td><td valign="top">
902 for(i=0, pSet=aSetting; i<nSetting; i++, pSet++){
903 if( pSet->width>0 && pSet->forceTextArea ){
904 int hasVersionableValue = db_get_versioned(pSet->name, NULL)!=0;
905 @ <a href='%R/help?cmd=%s(pSet->name)'>%s(pSet->name)</a>
906
+5 -6
--- src/setupuser.c
+++ src/setupuser.c
@@ -549,18 +549,17 @@
549549
if( login_is_special(zLogin) ){
550550
@ <td><b>%h(zLogin)</b></td>
551551
}else{
552552
@ <td><input type="text" name="login" value="%h(zLogin)" />\
553553
if( alert_tables_exist() ){
554
- char *zSCode; /* Subscriber Code */
555
- zSCode = db_text(0, "SELECT hex(subscriberCode) FROM subscriber"
556
- " WHERE suname=%Q", zLogin);
557
- if( zSCode && zSCode[0] ){
558
- @ &nbsp;&nbsp;<a href="%R/alerts/%s(zSCode)">\
554
+ int sid;
555
+ sid = db_int(0, "SELECT subscriberId FROM subscriber"
556
+ " WHERE suname=%Q", zLogin);
557
+ if( sid>0 ){
558
+ @ &nbsp;&nbsp;<a href="%R/alerts?sid=%d(sid)">\
559559
@ (subscription info for %h(zLogin))</a>\
560560
}
561
- fossil_free(zSCode);
562561
}
563562
@ </td></tr>
564563
@ <tr>
565564
@ <td class="usetupEditLabel">Contact&nbsp;Info:</td>
566565
@ <td><textarea name="info" cols="40" rows="2">%h(zInfo)</textarea></td>
567566
--- src/setupuser.c
+++ src/setupuser.c
@@ -549,18 +549,17 @@
549 if( login_is_special(zLogin) ){
550 @ <td><b>%h(zLogin)</b></td>
551 }else{
552 @ <td><input type="text" name="login" value="%h(zLogin)" />\
553 if( alert_tables_exist() ){
554 char *zSCode; /* Subscriber Code */
555 zSCode = db_text(0, "SELECT hex(subscriberCode) FROM subscriber"
556 " WHERE suname=%Q", zLogin);
557 if( zSCode && zSCode[0] ){
558 @ &nbsp;&nbsp;<a href="%R/alerts/%s(zSCode)">\
559 @ (subscription info for %h(zLogin))</a>\
560 }
561 fossil_free(zSCode);
562 }
563 @ </td></tr>
564 @ <tr>
565 @ <td class="usetupEditLabel">Contact&nbsp;Info:</td>
566 @ <td><textarea name="info" cols="40" rows="2">%h(zInfo)</textarea></td>
567
--- src/setupuser.c
+++ src/setupuser.c
@@ -549,18 +549,17 @@
549 if( login_is_special(zLogin) ){
550 @ <td><b>%h(zLogin)</b></td>
551 }else{
552 @ <td><input type="text" name="login" value="%h(zLogin)" />\
553 if( alert_tables_exist() ){
554 int sid;
555 sid = db_int(0, "SELECT subscriberId FROM subscriber"
556 " WHERE suname=%Q", zLogin);
557 if( sid>0 ){
558 @ &nbsp;&nbsp;<a href="%R/alerts?sid=%d(sid)">\
559 @ (subscription info for %h(zLogin))</a>\
560 }
 
561 }
562 @ </td></tr>
563 @ <tr>
564 @ <td class="usetupEditLabel">Contact&nbsp;Info:</td>
565 @ <td><textarea name="info" cols="40" rows="2">%h(zInfo)</textarea></td>
566
+280 -102
--- src/shell.c
+++ src/shell.c
@@ -33,10 +33,18 @@
3333
#if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS)
3434
/* This needs to come before any includes for MSVC compiler */
3535
#define _CRT_SECURE_NO_WARNINGS
3636
#endif
3737
38
+/*
39
+** Determine if we are dealing with WinRT, which provides only a subset of
40
+** the full Win32 API.
41
+*/
42
+#if !defined(SQLITE_OS_WINRT)
43
+# define SQLITE_OS_WINRT 0
44
+#endif
45
+
3846
/*
3947
** Warning pragmas copied from msvc.h in the core.
4048
*/
4149
#if defined(_MSC_VER)
4250
#pragma warning(disable : 4054)
@@ -145,26 +153,30 @@
145153
# define SHELL_USE_LOCAL_GETLINE 1
146154
#endif
147155
148156
149157
#if defined(_WIN32) || defined(WIN32)
150
-# include <io.h>
151
-# include <fcntl.h>
152
-# define isatty(h) _isatty(h)
153
-# ifndef access
154
-# define access(f,m) _access((f),(m))
155
-# endif
156
-# ifndef unlink
157
-# define unlink _unlink
158
-# endif
159
-# ifndef strdup
160
-# define strdup _strdup
161
-# endif
162
-# undef popen
163
-# define popen _popen
164
-# undef pclose
165
-# define pclose _pclose
158
+# if SQLITE_OS_WINRT
159
+# define SQLITE_OMIT_POPEN 1
160
+# else
161
+# include <io.h>
162
+# include <fcntl.h>
163
+# define isatty(h) _isatty(h)
164
+# ifndef access
165
+# define access(f,m) _access((f),(m))
166
+# endif
167
+# ifndef unlink
168
+# define unlink _unlink
169
+# endif
170
+# ifndef strdup
171
+# define strdup _strdup
172
+# endif
173
+# undef popen
174
+# define popen _popen
175
+# undef pclose
176
+# define pclose _pclose
177
+# endif
166178
#else
167179
/* Make sure isatty() has a prototype. */
168180
extern int isatty(int);
169181
170182
# if !defined(__RTP__) && !defined(_WRS_KERNEL)
@@ -189,10 +201,13 @@
189201
#define IsSpace(X) isspace((unsigned char)X)
190202
#define IsDigit(X) isdigit((unsigned char)X)
191203
#define ToLower(X) (char)tolower((unsigned char)X)
192204
193205
#if defined(_WIN32) || defined(WIN32)
206
+#if SQLITE_OS_WINRT
207
+#include <intrin.h>
208
+#endif
194209
#include <windows.h>
195210
196211
/* string conversion routines only needed on Win32 */
197212
extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR);
198213
extern char *sqlite3_win32_mbcs_to_utf8_v2(const char *, int);
@@ -204,11 +219,11 @@
204219
** are automatically translated into \r\n. However, this behavior needs
205220
** to be disabled in some cases (ex: when generating CSV output and when
206221
** rendering quoted strings that contain \n characters). The following
207222
** routines take care of that.
208223
*/
209
-#if defined(_WIN32) || defined(WIN32)
224
+#if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT
210225
static void setBinaryMode(FILE *file, int isOutput){
211226
if( isOutput ) fflush(file);
212227
_setmode(_fileno(file), _O_BINARY);
213228
}
214229
static void setTextMode(FILE *file, int isOutput){
@@ -308,10 +323,11 @@
308323
*/
309324
static int hasTimer(void){
310325
if( getProcessTimesAddr ){
311326
return 1;
312327
} else {
328
+#if !SQLITE_OS_WINRT
313329
/* GetProcessTimes() isn't supported in WIN95 and some other Windows
314330
** versions. See if the version we are running on has it, and if it
315331
** does, save off a pointer to it and the current process handle.
316332
*/
317333
hProcess = GetCurrentProcess();
@@ -324,10 +340,11 @@
324340
return 1;
325341
}
326342
FreeLibrary(hinstLib);
327343
}
328344
}
345
+#endif
329346
}
330347
return 0;
331348
}
332349
333350
/*
@@ -2476,10 +2493,11 @@
24762493
}
24772494
}
24782495
24792496
if( mtime>=0 ){
24802497
#if defined(_WIN32)
2498
+#if !SQLITE_OS_WINRT
24812499
/* Windows */
24822500
FILETIME lastAccess;
24832501
FILETIME lastWrite;
24842502
SYSTEMTIME currentTime;
24852503
LONGLONG intervals;
@@ -2506,10 +2524,11 @@
25062524
CloseHandle(hFile);
25072525
return !bResult;
25082526
}else{
25092527
return 1;
25102528
}
2529
+#endif
25112530
#elif defined(AT_FDCWD) && 0 /* utimensat() is not universally available */
25122531
/* Recent unix */
25132532
struct timespec times[2];
25142533
times[0].tv_nsec = times[1].tv_nsec = 0;
25152534
times[0].tv_sec = time(0);
@@ -4263,10 +4282,105 @@
42634282
memtraceOut = 0;
42644283
return rc;
42654284
}
42664285
42674286
/************************* End ../ext/misc/memtrace.c ********************/
4287
+/************************* Begin ../ext/misc/uint.c ******************/
4288
+/*
4289
+** 2020-04-14
4290
+**
4291
+** The author disclaims copyright to this source code. In place of
4292
+** a legal notice, here is a blessing:
4293
+**
4294
+** May you do good and not evil.
4295
+** May you find forgiveness for yourself and forgive others.
4296
+** May you share freely, never taking more than you give.
4297
+**
4298
+******************************************************************************
4299
+**
4300
+** This SQLite extension implements the UINT collating sequence.
4301
+**
4302
+** UINT works like BINARY for text, except that embedded strings
4303
+** of digits compare in numeric order.
4304
+**
4305
+** * Leading zeros are handled properly, in the sense that
4306
+** they do not mess of the maginitude comparison of embedded
4307
+** strings of digits. "x00123y" is equal to "x123y".
4308
+**
4309
+** * Only unsigned integers are recognized. Plus and minus
4310
+** signs are ignored. Decimal points and exponential notation
4311
+** are ignored.
4312
+**
4313
+** * Embedded integers can be of arbitrary length. Comparison
4314
+** is *not* limited integers that can be expressed as a
4315
+** 64-bit machine integer.
4316
+*/
4317
+/* #include "sqlite3ext.h" */
4318
+SQLITE_EXTENSION_INIT1
4319
+#include <assert.h>
4320
+#include <string.h>
4321
+#include <ctype.h>
4322
+
4323
+/*
4324
+** Compare text in lexicographic order, except strings of digits
4325
+** compare in numeric order.
4326
+*/
4327
+static int uintCollFunc(
4328
+ void *notUsed,
4329
+ int nKey1, const void *pKey1,
4330
+ int nKey2, const void *pKey2
4331
+){
4332
+ const unsigned char *zA = (const unsigned char*)pKey1;
4333
+ const unsigned char *zB = (const unsigned char*)pKey2;
4334
+ int i=0, j=0, x;
4335
+ (void)notUsed;
4336
+ while( i<nKey1 && j<nKey2 ){
4337
+ x = zA[i] - zB[j];
4338
+ if( isdigit(zA[i]) ){
4339
+ int k;
4340
+ if( !isdigit(zB[j]) ) return x;
4341
+ while( i<nKey1 && zA[i]=='0' ){ i++; }
4342
+ while( j<nKey2 && zB[j]=='0' ){ j++; }
4343
+ k = 0;
4344
+ while( i+k<nKey1 && isdigit(zA[i+k])
4345
+ && j+k<nKey2 && isdigit(zB[j+k]) ){
4346
+ k++;
4347
+ }
4348
+ if( i+k<nKey1 && isdigit(zA[i+k]) ){
4349
+ return +1;
4350
+ }else if( j+k<nKey2 && isdigit(zB[j+k]) ){
4351
+ return -1;
4352
+ }else{
4353
+ x = memcmp(zA+i, zB+j, k);
4354
+ if( x ) return x;
4355
+ i += k;
4356
+ j += k;
4357
+ }
4358
+ }else if( x ){
4359
+ return x;
4360
+ }else{
4361
+ i++;
4362
+ j++;
4363
+ }
4364
+ }
4365
+ return (nKey1 - i) - (nKey2 - j);
4366
+}
4367
+
4368
+#ifdef _WIN32
4369
+
4370
+#endif
4371
+int sqlite3_uint_init(
4372
+ sqlite3 *db,
4373
+ char **pzErrMsg,
4374
+ const sqlite3_api_routines *pApi
4375
+){
4376
+ SQLITE_EXTENSION_INIT2(pApi);
4377
+ (void)pzErrMsg; /* Unused parameter */
4378
+ return sqlite3_create_collation(db, "uint", SQLITE_UTF8, 0, uintCollFunc);
4379
+}
4380
+
4381
+/************************* End ../ext/misc/uint.c ********************/
42684382
#ifdef SQLITE_HAVE_ZLIB
42694383
/************************* Begin ../ext/misc/zipfile.c ******************/
42704384
/*
42714385
** 2017-12-26
42724386
**
@@ -9652,10 +9766,11 @@
96529766
int nCheck; /* Number of ".check" commands run */
96539767
unsigned nProgress; /* Number of progress callbacks encountered */
96549768
unsigned mxProgress; /* Maximum progress callbacks before failing */
96559769
unsigned flgProgress; /* Flags for the progress callback */
96569770
unsigned shellFlgs; /* Various flags */
9771
+ unsigned priorShFlgs; /* Saved copy of flags */
96579772
sqlite3_int64 szMax; /* --maxsize argument to .open */
96589773
char *zDestTable; /* Name of destination table when MODE_Insert */
96599774
char *zTempFile; /* Temporary file that might need deleting */
96609775
char zTestcase[30]; /* Name of current test case */
96619776
char colSeparator[20]; /* Column separator character for several modes */
@@ -9952,15 +10067,17 @@
995210067
/*
995310068
** Save or restore the current output mode
995410069
*/
995510070
static void outputModePush(ShellState *p){
995610071
p->modePrior = p->mode;
10072
+ p->priorShFlgs = p->shellFlgs;
995710073
memcpy(p->colSepPrior, p->colSeparator, sizeof(p->colSeparator));
995810074
memcpy(p->rowSepPrior, p->rowSeparator, sizeof(p->rowSeparator));
995910075
}
996010076
static void outputModePop(ShellState *p){
996110077
p->mode = p->modePrior;
10078
+ p->shellFlgs = p->priorShFlgs;
996210079
memcpy(p->colSeparator, p->colSepPrior, sizeof(p->colSeparator));
996310080
memcpy(p->rowSeparator, p->rowSepPrior, sizeof(p->rowSeparator));
996410081
}
996510082
996610083
/*
@@ -10921,12 +11038,11 @@
1092111038
** "--" comment occurs at the end of the statement, the comment
1092211039
** won't consume the semicolon terminator.
1092311040
*/
1092411041
static int run_table_dump_query(
1092511042
ShellState *p, /* Query context */
10926
- const char *zSelect, /* SELECT statement to extract content */
10927
- const char *zFirstRow /* Print before first row, if not NULL */
11043
+ const char *zSelect /* SELECT statement to extract content */
1092811044
){
1092911045
sqlite3_stmt *pSelect;
1093011046
int rc;
1093111047
int nResult;
1093211048
int i;
@@ -10939,14 +11055,10 @@
1093911055
return rc;
1094011056
}
1094111057
rc = sqlite3_step(pSelect);
1094211058
nResult = sqlite3_column_count(pSelect);
1094311059
while( rc==SQLITE_ROW ){
10944
- if( zFirstRow ){
10945
- utf8_printf(p->out, "%s", zFirstRow);
10946
- zFirstRow = 0;
10947
- }
1094811060
z = (const char*)sqlite3_column_text(pSelect, 0);
1094911061
utf8_printf(p->out, "%s", z);
1095011062
for(i=1; i<nResult; i++){
1095111063
utf8_printf(p->out, ",%s", sqlite3_column_text(pSelect, i));
1095211064
}
@@ -11399,13 +11511,13 @@
1139911511
** Parameter bindings are taken from a TEMP table of the form:
1140011512
**
1140111513
** CREATE TEMP TABLE sqlite_parameters(key TEXT PRIMARY KEY, value)
1140211514
** WITHOUT ROWID;
1140311515
**
11404
-** No bindings occur if this table does not exist. The special character '$'
11405
-** is included in the table name to help prevent collisions with actual tables.
11406
-** The table must be in the TEMP schema.
11516
+** No bindings occur if this table does not exist. The name of the table
11517
+** begins with "sqlite_" so that it will not collide with ordinary application
11518
+** tables. The table must be in the TEMP schema.
1140711519
*/
1140811520
static void bind_prepared_stmt(ShellState *pArg, sqlite3_stmt *pStmt){
1140911521
int nVar;
1141011522
int i;
1141111523
int rc;
@@ -11705,10 +11817,11 @@
1170511817
if( rc==SQLITE_OK ){
1170611818
while( sqlite3_step(pExplain)==SQLITE_ROW ){
1170711819
const char *zEQPLine = (const char*)sqlite3_column_text(pExplain,3);
1170811820
int iEqpId = sqlite3_column_int(pExplain, 0);
1170911821
int iParentId = sqlite3_column_int(pExplain, 1);
11822
+ if( zEQPLine==0 ) zEQPLine = "";
1171011823
if( zEQPLine[0]=='-' ) eqp_render(pArg);
1171111824
eqp_append(pArg, iEqpId, iParentId, zEQPLine);
1171211825
}
1171311826
eqp_render(pArg);
1171411827
}
@@ -12115,29 +12228,32 @@
1211512228
".check GLOB Fail if output since .testcase does not match",
1211612229
".clone NEWDB Clone data into NEWDB from the existing database",
1211712230
".databases List names and files of attached databases",
1211812231
".dbconfig ?op? ?val? List or change sqlite3_db_config() options",
1211912232
".dbinfo ?DB? Show status information about the database",
12120
- ".dump ?TABLE? ... Render all database content as SQL",
12233
+ ".dump ?TABLE? Render database content as SQL",
1212112234
" Options:",
1212212235
" --preserve-rowids Include ROWID values in the output",
1212312236
" --newlines Allow unescaped newline characters in output",
1212412237
" TABLE is a LIKE pattern for the tables to dump",
12238
+ " Additional LIKE patterns can be given in subsequent arguments",
1212512239
".echo on|off Turn command echo on or off",
1212612240
".eqp on|off|full|... Enable or disable automatic EXPLAIN QUERY PLAN",
1212712241
" Other Modes:",
1212812242
#ifdef SQLITE_DEBUG
1212912243
" test Show raw EXPLAIN QUERY PLAN output",
1213012244
" trace Like \"full\" but enable \"PRAGMA vdbe_trace\"",
1213112245
#endif
1213212246
" trigger Like \"full\" but also show trigger bytecode",
1213312247
".excel Display the output of next command in spreadsheet",
12248
+ " --bom Put a UTF8 byte-order mark on intermediate file",
1213412249
".exit ?CODE? Exit this program with return-code CODE",
1213512250
".expert EXPERIMENTAL. Suggest indexes for queries",
1213612251
".explain ?on|off|auto? Change the EXPLAIN formatting mode. Default: auto",
1213712252
".filectrl CMD ... Run various sqlite3_file_control() operations",
12138
- " Run \".filectrl\" with no arguments for details",
12253
+ " --schema SCHEMA Use SCHEMA instead of \"main\"",
12254
+ " --help Show CMD details",
1213912255
".fullschema ?--indent? Show schema and the content of sqlite_stat tables",
1214012256
".headers on|off Turn display of headers on or off",
1214112257
".help ?-all? ?PATTERN? Show help text for PATTERN",
1214212258
".import FILE TABLE Import data from FILE into TABLE",
1214312259
" Options:",
@@ -12180,15 +12296,15 @@
1218012296
" list Values delimited by \"|\"",
1218112297
" quote Escape answers as for SQL",
1218212298
" tabs Tab-separated values",
1218312299
" tcl TCL list elements",
1218412300
".nullvalue STRING Use STRING in place of NULL values",
12185
- ".once (-e|-x|FILE) Output for the next SQL command only to FILE",
12301
+ ".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE",
1218612302
" If FILE begins with '|' then open as a pipe",
12187
- " Other options:",
12188
- " -e Invoke system text editor",
12189
- " -x Open in a spreadsheet",
12303
+ " --bom Put a UTF8 byte-order mark at the beginning",
12304
+ " -e Send output to the system text editor",
12305
+ " -x Send output as CSV to a spreadsheet (same as \".excel\")",
1219012306
#ifdef SQLITE_DEBUG
1219112307
".oom [--repeat M] [N] Simulate an OOM error on the N-th allocation",
1219212308
#endif
1219312309
".open ?OPTIONS? ?FILE? Close existing database and reopen FILE",
1219412310
" Options:",
@@ -12201,11 +12317,15 @@
1220112317
" --new Initialize FILE to an empty database",
1220212318
" --nofollow Do not follow symbolic links",
1220312319
" --readonly Open FILE readonly",
1220412320
" --zip FILE is a ZIP archive",
1220512321
".output ?FILE? Send output to FILE or stdout if FILE is omitted",
12206
- " If FILE begins with '|' then open it as a pipe.",
12322
+ " If FILE begins with '|' then open it as a pipe.",
12323
+ " Options:",
12324
+ " --bom Prefix output with a UTF8 byte-order mark",
12325
+ " -e Send output to the system text editor",
12326
+ " -x Send output as CSV to a spreadsheet",
1220712327
".parameter CMD ... Manage SQL parameter bindings",
1220812328
" clear Erase all bindings",
1220912329
" init Initialize the TEMP table that holds bindings",
1221012330
" list List the current parameter bindings",
1221112331
" set PARAMETER VALUE Given SQL parameter PARAMETER a value of VALUE",
@@ -12321,10 +12441,11 @@
1232112441
char *zPat;
1232212442
if( zPattern==0
1232312443
|| zPattern[0]=='0'
1232412444
|| strcmp(zPattern,"-a")==0
1232512445
|| strcmp(zPattern,"-all")==0
12446
+ || strcmp(zPattern,"--all")==0
1232612447
){
1232712448
/* Show all commands, but only one line per command */
1232812449
if( zPattern==0 ) zPattern = "";
1232912450
for(i=0; i<ArraySize(azHelp); i++){
1233012451
if( azHelp[i][0]=='.' || zPattern[0] ){
@@ -12802,10 +12923,11 @@
1280212923
sqlite3_enable_load_extension(p->db, 1);
1280312924
#endif
1280412925
sqlite3_fileio_init(p->db, 0, 0);
1280512926
sqlite3_shathree_init(p->db, 0, 0);
1280612927
sqlite3_completion_init(p->db, 0, 0);
12928
+ sqlite3_uint_init(p->db, 0, 0);
1280712929
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
1280812930
sqlite3_dbdata_init(p->db, 0, 0);
1280912931
#endif
1281012932
#ifdef SQLITE_HAVE_ZLIB
1281112933
sqlite3_zipfile_init(p->db, 0, 0);
@@ -13521,15 +13643,19 @@
1352113643
#endif
1352213644
char *zCmd;
1352313645
zCmd = sqlite3_mprintf("%s %s", zXdgOpenCmd, p->zTempFile);
1352413646
if( system(zCmd) ){
1352513647
utf8_printf(stderr, "Failed: [%s]\n", zCmd);
13648
+ }else{
13649
+ /* Give the start/open/xdg-open command some time to get
13650
+ ** going before we continue, and potential delete the
13651
+ ** p->zTempFile data file out from under it */
13652
+ sqlite3_sleep(2000);
1352613653
}
1352713654
sqlite3_free(zCmd);
1352813655
outputModePop(p);
1352913656
p->doXdgOpen = 0;
13530
- sqlite3_sleep(100);
1353113657
}
1353213658
#endif /* !defined(SQLITE_NOHAVE_SYSTEM) */
1353313659
}
1353413660
p->outfile[0] = 0;
1353513661
p->out = stdout;
@@ -13601,16 +13727,11 @@
1360113727
if( p->db==0 ) return 1;
1360213728
rc = sqlite3_prepare_v2(p->db,
1360313729
"SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1",
1360413730
-1, &pStmt, 0);
1360513731
if( rc ){
13606
- if( !sqlite3_compileoption_used("ENABLE_DBPAGE_VTAB") ){
13607
- utf8_printf(stderr, "the \".dbinfo\" command requires the "
13608
- "-DSQLITE_ENABLE_DBPAGE_VTAB compile-time options\n");
13609
- }else{
13610
- utf8_printf(stderr, "error: %s\n", sqlite3_errmsg(p->db));
13611
- }
13732
+ utf8_printf(stderr, "error: %s\n", sqlite3_errmsg(p->db));
1361213733
sqlite3_finalize(pStmt);
1361313734
return 1;
1361413735
}
1361513736
sqlite3_bind_text(pStmt, 1, zDb, -1, SQLITE_STATIC);
1361613737
if( sqlite3_step(pStmt)==SQLITE_ROW
@@ -13815,13 +13936,25 @@
1381513936
p->zTempFile = 0;
1381613937
if( p->db ){
1381713938
sqlite3_file_control(p->db, 0, SQLITE_FCNTL_TEMPFILENAME, &p->zTempFile);
1381813939
}
1381913940
if( p->zTempFile==0 ){
13941
+ /* If p->db is an in-memory database then the TEMPFILENAME file-control
13942
+ ** will not work and we will need to fallback to guessing */
13943
+ char *zTemp;
1382013944
sqlite3_uint64 r;
1382113945
sqlite3_randomness(sizeof(r), &r);
13822
- p->zTempFile = sqlite3_mprintf("temp%llx.%s", r, zSuffix);
13946
+ zTemp = getenv("TEMP");
13947
+ if( zTemp==0 ) zTemp = getenv("TMP");
13948
+ if( zTemp==0 ){
13949
+#ifdef _WIN32
13950
+ zTemp = "\\tmp";
13951
+#else
13952
+ zTemp = "/tmp";
13953
+#endif
13954
+ }
13955
+ p->zTempFile = sqlite3_mprintf("%s/temp%llx.%s", zTemp, r, zSuffix);
1382313956
}else{
1382413957
p->zTempFile = sqlite3_mprintf("%z.%s", p->zTempFile, zSuffix);
1382513958
}
1382613959
if( p->zTempFile==0 ){
1382713960
raw_printf(stderr, "out of memory\n");
@@ -15841,11 +15974,12 @@
1584115974
rc = recoverDatabaseCmd(p, nArg, azArg);
1584215975
}else
1584315976
#endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */
1584415977
1584515978
if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){
15846
- const char *zLike = 0;
15979
+ char *zLike = 0;
15980
+ char *zSql;
1584715981
int i;
1584815982
int savedShowHeader = p->showHeader;
1584915983
int savedShellFlags = p->shellFlgs;
1585015984
ShellClearFlag(p, SHFLG_PreserveRowid|SHFLG_Newlines|SHFLG_Echo);
1585115985
for(i=1; i<nArg; i++){
@@ -15869,16 +16003,14 @@
1586916003
raw_printf(stderr, "Unknown option \"%s\" on \".dump\"\n", azArg[i]);
1587016004
rc = 1;
1587116005
goto meta_command_exit;
1587216006
}
1587316007
}else if( zLike ){
15874
- raw_printf(stderr, "Usage: .dump ?--preserve-rowids? "
15875
- "?--newlines? ?LIKE-PATTERN?\n");
15876
- rc = 1;
15877
- goto meta_command_exit;
16008
+ zLike = sqlite3_mprintf("%z OR name LIKE %Q ESCAPE '\\'",
16009
+ zLike, azArg[i]);
1587816010
}else{
15879
- zLike = azArg[i];
16011
+ zLike = sqlite3_mprintf("name LIKE %Q ESCAPE '\\'", azArg[i]);
1588016012
}
1588116013
}
1588216014
1588316015
open_db(p, 0);
1588416016
@@ -15892,39 +16024,29 @@
1589216024
/* Set writable_schema=ON since doing so forces SQLite to initialize
1589316025
** as much of the schema as it can even if the sqlite_master table is
1589416026
** corrupt. */
1589516027
sqlite3_exec(p->db, "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0);
1589616028
p->nErr = 0;
15897
- if( zLike==0 ){
15898
- run_schema_dump_query(p,
15899
- "SELECT name, type, sql FROM sqlite_master "
15900
- "WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'"
15901
- );
15902
- run_schema_dump_query(p,
15903
- "SELECT name, type, sql FROM sqlite_master "
15904
- "WHERE name=='sqlite_sequence'"
15905
- );
15906
- run_table_dump_query(p,
15907
- "SELECT sql FROM sqlite_master "
15908
- "WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0
15909
- );
15910
- }else{
15911
- char *zSql;
15912
- zSql = sqlite3_mprintf(
15913
- "SELECT name, type, sql FROM sqlite_master "
15914
- "WHERE tbl_name LIKE %Q AND type=='table'"
15915
- " AND sql NOT NULL", zLike);
15916
- run_schema_dump_query(p,zSql);
15917
- sqlite3_free(zSql);
15918
- zSql = sqlite3_mprintf(
15919
- "SELECT sql FROM sqlite_master "
15920
- "WHERE sql NOT NULL"
15921
- " AND type IN ('index','trigger','view')"
15922
- " AND tbl_name LIKE %Q", zLike);
15923
- run_table_dump_query(p, zSql, 0);
15924
- sqlite3_free(zSql);
15925
- }
16029
+ if( zLike==0 ) zLike = sqlite3_mprintf("true");
16030
+ zSql = sqlite3_mprintf(
16031
+ "SELECT name, type, sql FROM sqlite_master "
16032
+ "WHERE (%s) AND type=='table'"
16033
+ " AND sql NOT NULL"
16034
+ " ORDER BY tbl_name='sqlite_sequence', rowid",
16035
+ zLike
16036
+ );
16037
+ run_schema_dump_query(p,zSql);
16038
+ sqlite3_free(zSql);
16039
+ zSql = sqlite3_mprintf(
16040
+ "SELECT sql FROM sqlite_master "
16041
+ "WHERE (%s) AND sql NOT NULL"
16042
+ " AND type IN ('index','trigger','view')",
16043
+ zLike
16044
+ );
16045
+ run_table_dump_query(p, zSql);
16046
+ sqlite3_free(zSql);
16047
+ sqlite3_free(zLike);
1592616048
if( p->writableSchema ){
1592716049
raw_printf(p->out, "PRAGMA writable_schema=OFF;\n");
1592816050
p->writableSchema = 0;
1592916051
}
1593016052
sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0);
@@ -16023,20 +16145,32 @@
1602316145
{ "psow", SQLITE_FCNTL_POWERSAFE_OVERWRITE, "[BOOLEAN]" },
1602416146
/* { "pragma", SQLITE_FCNTL_PRAGMA, "NAME ARG" },*/
1602516147
{ "tempfilename", SQLITE_FCNTL_TEMPFILENAME, "" },
1602616148
{ "has_moved", SQLITE_FCNTL_HAS_MOVED, "" },
1602716149
{ "lock_timeout", SQLITE_FCNTL_LOCK_TIMEOUT, "MILLISEC" },
16150
+ { "reserve_bytes", SQLITE_FCNTL_RESERVE_BYTES, "[N]" },
1602816151
};
1602916152
int filectrl = -1;
1603016153
int iCtrl = -1;
1603116154
sqlite3_int64 iRes = 0; /* Integer result to display if rc2==1 */
1603216155
int isOk = 0; /* 0: usage 1: %lld 2: no-result */
1603316156
int n2, i;
1603416157
const char *zCmd = 0;
16158
+ const char *zSchema = 0;
1603516159
1603616160
open_db(p, 0);
1603716161
zCmd = nArg>=2 ? azArg[1] : "help";
16162
+
16163
+ if( zCmd[0]=='-'
16164
+ && (strcmp(zCmd,"--schema")==0 || strcmp(zCmd,"-schema")==0)
16165
+ && nArg>=4
16166
+ ){
16167
+ zSchema = azArg[2];
16168
+ for(i=3; i<nArg; i++) azArg[i-2] = azArg[i];
16169
+ nArg -= 2;
16170
+ zCmd = azArg[1];
16171
+ }
1603816172
1603916173
/* The argument can optionally begin with "-" or "--" */
1604016174
if( zCmd[0]=='-' && zCmd[1] ){
1604116175
zCmd++;
1604216176
if( zCmd[0]=='-' && zCmd[1] ) zCmd++;
@@ -16075,51 +16209,63 @@
1607516209
}else{
1607616210
switch(filectrl){
1607716211
case SQLITE_FCNTL_SIZE_LIMIT: {
1607816212
if( nArg!=2 && nArg!=3 ) break;
1607916213
iRes = nArg==3 ? integerValue(azArg[2]) : -1;
16080
- sqlite3_file_control(p->db, 0, SQLITE_FCNTL_SIZE_LIMIT, &iRes);
16214
+ sqlite3_file_control(p->db, zSchema, SQLITE_FCNTL_SIZE_LIMIT, &iRes);
1608116215
isOk = 1;
1608216216
break;
1608316217
}
1608416218
case SQLITE_FCNTL_LOCK_TIMEOUT:
1608516219
case SQLITE_FCNTL_CHUNK_SIZE: {
1608616220
int x;
1608716221
if( nArg!=3 ) break;
1608816222
x = (int)integerValue(azArg[2]);
16089
- sqlite3_file_control(p->db, 0, filectrl, &x);
16223
+ sqlite3_file_control(p->db, zSchema, filectrl, &x);
1609016224
isOk = 2;
1609116225
break;
1609216226
}
1609316227
case SQLITE_FCNTL_PERSIST_WAL:
1609416228
case SQLITE_FCNTL_POWERSAFE_OVERWRITE: {
1609516229
int x;
1609616230
if( nArg!=2 && nArg!=3 ) break;
1609716231
x = nArg==3 ? booleanValue(azArg[2]) : -1;
16098
- sqlite3_file_control(p->db, 0, filectrl, &x);
16232
+ sqlite3_file_control(p->db, zSchema, filectrl, &x);
1609916233
iRes = x;
1610016234
isOk = 1;
1610116235
break;
1610216236
}
1610316237
case SQLITE_FCNTL_HAS_MOVED: {
1610416238
int x;
1610516239
if( nArg!=2 ) break;
16106
- sqlite3_file_control(p->db, 0, filectrl, &x);
16240
+ sqlite3_file_control(p->db, zSchema, filectrl, &x);
1610716241
iRes = x;
1610816242
isOk = 1;
1610916243
break;
1611016244
}
1611116245
case SQLITE_FCNTL_TEMPFILENAME: {
1611216246
char *z = 0;
1611316247
if( nArg!=2 ) break;
16114
- sqlite3_file_control(p->db, 0, filectrl, &z);
16248
+ sqlite3_file_control(p->db, zSchema, filectrl, &z);
1611516249
if( z ){
1611616250
utf8_printf(p->out, "%s\n", z);
1611716251
sqlite3_free(z);
1611816252
}
1611916253
isOk = 2;
1612016254
break;
16255
+ }
16256
+ case SQLITE_FCNTL_RESERVE_BYTES: {
16257
+ int x;
16258
+ if( nArg>=3 ){
16259
+ x = atoi(azArg[2]);
16260
+ sqlite3_file_control(p->db, zSchema, filectrl, &x);
16261
+ }
16262
+ x = -1;
16263
+ sqlite3_file_control(p->db, zSchema, filectrl, &x);
16264
+ utf8_printf(p->out,"%d\n", x);
16265
+ isOk = 2;
16266
+ break;
1612116267
}
1612216268
}
1612316269
}
1612416270
if( isOk==0 && iCtrl>=0 ){
1612516271
utf8_printf(p->out, "Usage: .filectrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage);
@@ -16850,46 +16996,70 @@
1685016996
1685116997
if( (c=='o'
1685216998
&& (strncmp(azArg[0], "output", n)==0||strncmp(azArg[0], "once", n)==0))
1685316999
|| (c=='e' && n==5 && strcmp(azArg[0],"excel")==0)
1685417000
){
16855
- const char *zFile = nArg>=2 ? azArg[1] : "stdout";
17001
+ const char *zFile = 0;
1685617002
int bTxtMode = 0;
16857
- if( azArg[0][0]=='e' ){
16858
- /* Transform the ".excel" command into ".once -x" */
16859
- nArg = 2;
16860
- azArg[0] = "once";
16861
- zFile = azArg[1] = "-x";
16862
- n = 4;
16863
- }
16864
- if( nArg>2 ){
16865
- utf8_printf(stderr, "Usage: .%s [-e|-x|FILE]\n", azArg[0]);
16866
- rc = 1;
16867
- goto meta_command_exit;
16868
- }
16869
- if( n>1 && strncmp(azArg[0], "once", n)==0 ){
16870
- if( nArg<2 ){
16871
- raw_printf(stderr, "Usage: .once (-e|-x|FILE)\n");
17003
+ int i;
17004
+ int eMode = 0;
17005
+ int bBOM = 0;
17006
+ int bOnce = 0; /* 0: .output, 1: .once, 2: .excel */
17007
+
17008
+ if( c=='e' ){
17009
+ eMode = 'x';
17010
+ bOnce = 2;
17011
+ }else if( strncmp(azArg[0],"once",n)==0 ){
17012
+ bOnce = 1;
17013
+ }
17014
+ for(i=1; i<nArg; i++){
17015
+ char *z = azArg[i];
17016
+ if( z[0]=='-' ){
17017
+ if( z[1]=='-' ) z++;
17018
+ if( strcmp(z,"-bom")==0 ){
17019
+ bBOM = 1;
17020
+ }else if( c!='e' && strcmp(z,"-x")==0 ){
17021
+ eMode = 'x'; /* spreadsheet */
17022
+ }else if( c!='e' && strcmp(z,"-e")==0 ){
17023
+ eMode = 'e'; /* text editor */
17024
+ }else{
17025
+ utf8_printf(p->out, "ERROR: unknown option: \"%s\". Usage:\n",
17026
+ azArg[i]);
17027
+ showHelp(p->out, azArg[0]);
17028
+ rc = 1;
17029
+ goto meta_command_exit;
17030
+ }
17031
+ }else if( zFile==0 ){
17032
+ zFile = z;
17033
+ }else{
17034
+ utf8_printf(p->out,"ERROR: extra parameter: \"%s\". Usage:\n",
17035
+ azArg[i]);
17036
+ showHelp(p->out, azArg[0]);
1687217037
rc = 1;
1687317038
goto meta_command_exit;
1687417039
}
17040
+ }
17041
+ if( zFile==0 ) zFile = "stdout";
17042
+ if( bOnce ){
1687517043
p->outCount = 2;
1687617044
}else{
1687717045
p->outCount = 0;
1687817046
}
1687917047
output_reset(p);
16880
- if( zFile[0]=='-' && zFile[1]=='-' ) zFile++;
1688117048
#ifndef SQLITE_NOHAVE_SYSTEM
16882
- if( strcmp(zFile, "-e")==0 || strcmp(zFile, "-x")==0 ){
17049
+ if( eMode=='e' || eMode=='x' ){
1688317050
p->doXdgOpen = 1;
1688417051
outputModePush(p);
16885
- if( zFile[1]=='x' ){
17052
+ if( eMode=='x' ){
17053
+ /* spreadsheet mode. Output as CSV. */
1688617054
newTempFile(p, "csv");
17055
+ ShellClearFlag(p, SHFLG_Echo);
1688717056
p->mode = MODE_Csv;
1688817057
sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma);
1688917058
sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf);
1689017059
}else{
17060
+ /* text editor mode */
1689117061
newTempFile(p, "txt");
1689217062
bTxtMode = 1;
1689317063
}
1689417064
zFile = p->zTempFile;
1689517065
}
@@ -16904,10 +17074,11 @@
1690417074
if( p->out==0 ){
1690517075
utf8_printf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1);
1690617076
p->out = stdout;
1690717077
rc = 1;
1690817078
}else{
17079
+ if( bBOM ) fprintf(p->out,"\357\273\277");
1690917080
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
1691017081
}
1691117082
#endif
1691217083
}else{
1691317084
p->out = output_file_open(zFile, bTxtMode);
@@ -16916,10 +17087,11 @@
1691617087
utf8_printf(stderr,"Error: cannot write to \"%s\"\n", zFile);
1691717088
}
1691817089
p->out = stdout;
1691917090
rc = 1;
1692017091
} else {
17092
+ if( bBOM ) fprintf(p->out,"\357\273\277");
1692117093
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
1692217094
}
1692317095
}
1692417096
}else
1692517097
@@ -17979,11 +18151,10 @@
1797918151
#endif
1798018152
{ "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE, "OFFSET " },
1798118153
{ "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE, "" },
1798218154
{ "prng_save", SQLITE_TESTCTRL_PRNG_SAVE, "" },
1798318155
{ "prng_seed", SQLITE_TESTCTRL_PRNG_SEED, "SEED ?db?" },
17984
- { "reserve", SQLITE_TESTCTRL_RESERVE, "BYTES-OF-RESERVE"},
1798518156
};
1798618157
int testctrl = -1;
1798718158
int iCtrl = -1;
1798818159
int rc2 = 0; /* 0: usage. 1: %d 2: %x 3: no-output */
1798918160
int isOk = 0;
@@ -18032,11 +18203,10 @@
1803218203
}else{
1803318204
switch(testctrl){
1803418205
1803518206
/* sqlite3_test_control(int, db, int) */
1803618207
case SQLITE_TESTCTRL_OPTIMIZATIONS:
18037
- case SQLITE_TESTCTRL_RESERVE:
1803818208
if( nArg==3 ){
1803918209
int opt = (int)strtol(azArg[2], 0, 0);
1804018210
rc2 = sqlite3_test_control(testctrl, p->db, opt);
1804118211
isOk = 3;
1804218212
}
@@ -18804,18 +18974,22 @@
1880418974
/*
1880518975
** Output text to the console in a font that attracts extra attention.
1880618976
*/
1880718977
#ifdef _WIN32
1880818978
static void printBold(const char *zText){
18979
+#if !SQLITE_OS_WINRT
1880918980
HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
1881018981
CONSOLE_SCREEN_BUFFER_INFO defaultScreenInfo;
1881118982
GetConsoleScreenBufferInfo(out, &defaultScreenInfo);
1881218983
SetConsoleTextAttribute(out,
1881318984
FOREGROUND_RED|FOREGROUND_INTENSITY
1881418985
);
18986
+#endif
1881518987
printf("%s", zText);
18988
+#if !SQLITE_OS_WINRT
1881618989
SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes);
18990
+#endif
1881718991
}
1881818992
#else
1881918993
static void printBold(const char *zText){
1882018994
printf("\033[1m%s\033[0m", zText);
1882118995
}
@@ -18879,11 +19053,15 @@
1887919053
"attach debugger to process %d and press any key to continue.\n",
1888019054
GETPID());
1888119055
fgetc(stdin);
1888219056
}else{
1888319057
#if defined(_WIN32) || defined(WIN32)
19058
+#if SQLITE_OS_WINRT
19059
+ __debugbreak();
19060
+#else
1888419061
DebugBreak();
19062
+#endif
1888519063
#elif defined(SIGTRAP)
1888619064
raise(SIGTRAP);
1888719065
#endif
1888819066
}
1888919067
}
1889019068
--- src/shell.c
+++ src/shell.c
@@ -33,10 +33,18 @@
33 #if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS)
34 /* This needs to come before any includes for MSVC compiler */
35 #define _CRT_SECURE_NO_WARNINGS
36 #endif
37
 
 
 
 
 
 
 
 
38 /*
39 ** Warning pragmas copied from msvc.h in the core.
40 */
41 #if defined(_MSC_VER)
42 #pragma warning(disable : 4054)
@@ -145,26 +153,30 @@
145 # define SHELL_USE_LOCAL_GETLINE 1
146 #endif
147
148
149 #if defined(_WIN32) || defined(WIN32)
150 # include <io.h>
151 # include <fcntl.h>
152 # define isatty(h) _isatty(h)
153 # ifndef access
154 # define access(f,m) _access((f),(m))
155 # endif
156 # ifndef unlink
157 # define unlink _unlink
158 # endif
159 # ifndef strdup
160 # define strdup _strdup
161 # endif
162 # undef popen
163 # define popen _popen
164 # undef pclose
165 # define pclose _pclose
 
 
 
 
166 #else
167 /* Make sure isatty() has a prototype. */
168 extern int isatty(int);
169
170 # if !defined(__RTP__) && !defined(_WRS_KERNEL)
@@ -189,10 +201,13 @@
189 #define IsSpace(X) isspace((unsigned char)X)
190 #define IsDigit(X) isdigit((unsigned char)X)
191 #define ToLower(X) (char)tolower((unsigned char)X)
192
193 #if defined(_WIN32) || defined(WIN32)
 
 
 
194 #include <windows.h>
195
196 /* string conversion routines only needed on Win32 */
197 extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR);
198 extern char *sqlite3_win32_mbcs_to_utf8_v2(const char *, int);
@@ -204,11 +219,11 @@
204 ** are automatically translated into \r\n. However, this behavior needs
205 ** to be disabled in some cases (ex: when generating CSV output and when
206 ** rendering quoted strings that contain \n characters). The following
207 ** routines take care of that.
208 */
209 #if defined(_WIN32) || defined(WIN32)
210 static void setBinaryMode(FILE *file, int isOutput){
211 if( isOutput ) fflush(file);
212 _setmode(_fileno(file), _O_BINARY);
213 }
214 static void setTextMode(FILE *file, int isOutput){
@@ -308,10 +323,11 @@
308 */
309 static int hasTimer(void){
310 if( getProcessTimesAddr ){
311 return 1;
312 } else {
 
313 /* GetProcessTimes() isn't supported in WIN95 and some other Windows
314 ** versions. See if the version we are running on has it, and if it
315 ** does, save off a pointer to it and the current process handle.
316 */
317 hProcess = GetCurrentProcess();
@@ -324,10 +340,11 @@
324 return 1;
325 }
326 FreeLibrary(hinstLib);
327 }
328 }
 
329 }
330 return 0;
331 }
332
333 /*
@@ -2476,10 +2493,11 @@
2476 }
2477 }
2478
2479 if( mtime>=0 ){
2480 #if defined(_WIN32)
 
2481 /* Windows */
2482 FILETIME lastAccess;
2483 FILETIME lastWrite;
2484 SYSTEMTIME currentTime;
2485 LONGLONG intervals;
@@ -2506,10 +2524,11 @@
2506 CloseHandle(hFile);
2507 return !bResult;
2508 }else{
2509 return 1;
2510 }
 
2511 #elif defined(AT_FDCWD) && 0 /* utimensat() is not universally available */
2512 /* Recent unix */
2513 struct timespec times[2];
2514 times[0].tv_nsec = times[1].tv_nsec = 0;
2515 times[0].tv_sec = time(0);
@@ -4263,10 +4282,105 @@
4263 memtraceOut = 0;
4264 return rc;
4265 }
4266
4267 /************************* End ../ext/misc/memtrace.c ********************/
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4268 #ifdef SQLITE_HAVE_ZLIB
4269 /************************* Begin ../ext/misc/zipfile.c ******************/
4270 /*
4271 ** 2017-12-26
4272 **
@@ -9652,10 +9766,11 @@
9652 int nCheck; /* Number of ".check" commands run */
9653 unsigned nProgress; /* Number of progress callbacks encountered */
9654 unsigned mxProgress; /* Maximum progress callbacks before failing */
9655 unsigned flgProgress; /* Flags for the progress callback */
9656 unsigned shellFlgs; /* Various flags */
 
9657 sqlite3_int64 szMax; /* --maxsize argument to .open */
9658 char *zDestTable; /* Name of destination table when MODE_Insert */
9659 char *zTempFile; /* Temporary file that might need deleting */
9660 char zTestcase[30]; /* Name of current test case */
9661 char colSeparator[20]; /* Column separator character for several modes */
@@ -9952,15 +10067,17 @@
9952 /*
9953 ** Save or restore the current output mode
9954 */
9955 static void outputModePush(ShellState *p){
9956 p->modePrior = p->mode;
 
9957 memcpy(p->colSepPrior, p->colSeparator, sizeof(p->colSeparator));
9958 memcpy(p->rowSepPrior, p->rowSeparator, sizeof(p->rowSeparator));
9959 }
9960 static void outputModePop(ShellState *p){
9961 p->mode = p->modePrior;
 
9962 memcpy(p->colSeparator, p->colSepPrior, sizeof(p->colSeparator));
9963 memcpy(p->rowSeparator, p->rowSepPrior, sizeof(p->rowSeparator));
9964 }
9965
9966 /*
@@ -10921,12 +11038,11 @@
10921 ** "--" comment occurs at the end of the statement, the comment
10922 ** won't consume the semicolon terminator.
10923 */
10924 static int run_table_dump_query(
10925 ShellState *p, /* Query context */
10926 const char *zSelect, /* SELECT statement to extract content */
10927 const char *zFirstRow /* Print before first row, if not NULL */
10928 ){
10929 sqlite3_stmt *pSelect;
10930 int rc;
10931 int nResult;
10932 int i;
@@ -10939,14 +11055,10 @@
10939 return rc;
10940 }
10941 rc = sqlite3_step(pSelect);
10942 nResult = sqlite3_column_count(pSelect);
10943 while( rc==SQLITE_ROW ){
10944 if( zFirstRow ){
10945 utf8_printf(p->out, "%s", zFirstRow);
10946 zFirstRow = 0;
10947 }
10948 z = (const char*)sqlite3_column_text(pSelect, 0);
10949 utf8_printf(p->out, "%s", z);
10950 for(i=1; i<nResult; i++){
10951 utf8_printf(p->out, ",%s", sqlite3_column_text(pSelect, i));
10952 }
@@ -11399,13 +11511,13 @@
11399 ** Parameter bindings are taken from a TEMP table of the form:
11400 **
11401 ** CREATE TEMP TABLE sqlite_parameters(key TEXT PRIMARY KEY, value)
11402 ** WITHOUT ROWID;
11403 **
11404 ** No bindings occur if this table does not exist. The special character '$'
11405 ** is included in the table name to help prevent collisions with actual tables.
11406 ** The table must be in the TEMP schema.
11407 */
11408 static void bind_prepared_stmt(ShellState *pArg, sqlite3_stmt *pStmt){
11409 int nVar;
11410 int i;
11411 int rc;
@@ -11705,10 +11817,11 @@
11705 if( rc==SQLITE_OK ){
11706 while( sqlite3_step(pExplain)==SQLITE_ROW ){
11707 const char *zEQPLine = (const char*)sqlite3_column_text(pExplain,3);
11708 int iEqpId = sqlite3_column_int(pExplain, 0);
11709 int iParentId = sqlite3_column_int(pExplain, 1);
 
11710 if( zEQPLine[0]=='-' ) eqp_render(pArg);
11711 eqp_append(pArg, iEqpId, iParentId, zEQPLine);
11712 }
11713 eqp_render(pArg);
11714 }
@@ -12115,29 +12228,32 @@
12115 ".check GLOB Fail if output since .testcase does not match",
12116 ".clone NEWDB Clone data into NEWDB from the existing database",
12117 ".databases List names and files of attached databases",
12118 ".dbconfig ?op? ?val? List or change sqlite3_db_config() options",
12119 ".dbinfo ?DB? Show status information about the database",
12120 ".dump ?TABLE? ... Render all database content as SQL",
12121 " Options:",
12122 " --preserve-rowids Include ROWID values in the output",
12123 " --newlines Allow unescaped newline characters in output",
12124 " TABLE is a LIKE pattern for the tables to dump",
 
12125 ".echo on|off Turn command echo on or off",
12126 ".eqp on|off|full|... Enable or disable automatic EXPLAIN QUERY PLAN",
12127 " Other Modes:",
12128 #ifdef SQLITE_DEBUG
12129 " test Show raw EXPLAIN QUERY PLAN output",
12130 " trace Like \"full\" but enable \"PRAGMA vdbe_trace\"",
12131 #endif
12132 " trigger Like \"full\" but also show trigger bytecode",
12133 ".excel Display the output of next command in spreadsheet",
 
12134 ".exit ?CODE? Exit this program with return-code CODE",
12135 ".expert EXPERIMENTAL. Suggest indexes for queries",
12136 ".explain ?on|off|auto? Change the EXPLAIN formatting mode. Default: auto",
12137 ".filectrl CMD ... Run various sqlite3_file_control() operations",
12138 " Run \".filectrl\" with no arguments for details",
 
12139 ".fullschema ?--indent? Show schema and the content of sqlite_stat tables",
12140 ".headers on|off Turn display of headers on or off",
12141 ".help ?-all? ?PATTERN? Show help text for PATTERN",
12142 ".import FILE TABLE Import data from FILE into TABLE",
12143 " Options:",
@@ -12180,15 +12296,15 @@
12180 " list Values delimited by \"|\"",
12181 " quote Escape answers as for SQL",
12182 " tabs Tab-separated values",
12183 " tcl TCL list elements",
12184 ".nullvalue STRING Use STRING in place of NULL values",
12185 ".once (-e|-x|FILE) Output for the next SQL command only to FILE",
12186 " If FILE begins with '|' then open as a pipe",
12187 " Other options:",
12188 " -e Invoke system text editor",
12189 " -x Open in a spreadsheet",
12190 #ifdef SQLITE_DEBUG
12191 ".oom [--repeat M] [N] Simulate an OOM error on the N-th allocation",
12192 #endif
12193 ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE",
12194 " Options:",
@@ -12201,11 +12317,15 @@
12201 " --new Initialize FILE to an empty database",
12202 " --nofollow Do not follow symbolic links",
12203 " --readonly Open FILE readonly",
12204 " --zip FILE is a ZIP archive",
12205 ".output ?FILE? Send output to FILE or stdout if FILE is omitted",
12206 " If FILE begins with '|' then open it as a pipe.",
 
 
 
 
12207 ".parameter CMD ... Manage SQL parameter bindings",
12208 " clear Erase all bindings",
12209 " init Initialize the TEMP table that holds bindings",
12210 " list List the current parameter bindings",
12211 " set PARAMETER VALUE Given SQL parameter PARAMETER a value of VALUE",
@@ -12321,10 +12441,11 @@
12321 char *zPat;
12322 if( zPattern==0
12323 || zPattern[0]=='0'
12324 || strcmp(zPattern,"-a")==0
12325 || strcmp(zPattern,"-all")==0
 
12326 ){
12327 /* Show all commands, but only one line per command */
12328 if( zPattern==0 ) zPattern = "";
12329 for(i=0; i<ArraySize(azHelp); i++){
12330 if( azHelp[i][0]=='.' || zPattern[0] ){
@@ -12802,10 +12923,11 @@
12802 sqlite3_enable_load_extension(p->db, 1);
12803 #endif
12804 sqlite3_fileio_init(p->db, 0, 0);
12805 sqlite3_shathree_init(p->db, 0, 0);
12806 sqlite3_completion_init(p->db, 0, 0);
 
12807 #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
12808 sqlite3_dbdata_init(p->db, 0, 0);
12809 #endif
12810 #ifdef SQLITE_HAVE_ZLIB
12811 sqlite3_zipfile_init(p->db, 0, 0);
@@ -13521,15 +13643,19 @@
13521 #endif
13522 char *zCmd;
13523 zCmd = sqlite3_mprintf("%s %s", zXdgOpenCmd, p->zTempFile);
13524 if( system(zCmd) ){
13525 utf8_printf(stderr, "Failed: [%s]\n", zCmd);
 
 
 
 
 
13526 }
13527 sqlite3_free(zCmd);
13528 outputModePop(p);
13529 p->doXdgOpen = 0;
13530 sqlite3_sleep(100);
13531 }
13532 #endif /* !defined(SQLITE_NOHAVE_SYSTEM) */
13533 }
13534 p->outfile[0] = 0;
13535 p->out = stdout;
@@ -13601,16 +13727,11 @@
13601 if( p->db==0 ) return 1;
13602 rc = sqlite3_prepare_v2(p->db,
13603 "SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1",
13604 -1, &pStmt, 0);
13605 if( rc ){
13606 if( !sqlite3_compileoption_used("ENABLE_DBPAGE_VTAB") ){
13607 utf8_printf(stderr, "the \".dbinfo\" command requires the "
13608 "-DSQLITE_ENABLE_DBPAGE_VTAB compile-time options\n");
13609 }else{
13610 utf8_printf(stderr, "error: %s\n", sqlite3_errmsg(p->db));
13611 }
13612 sqlite3_finalize(pStmt);
13613 return 1;
13614 }
13615 sqlite3_bind_text(pStmt, 1, zDb, -1, SQLITE_STATIC);
13616 if( sqlite3_step(pStmt)==SQLITE_ROW
@@ -13815,13 +13936,25 @@
13815 p->zTempFile = 0;
13816 if( p->db ){
13817 sqlite3_file_control(p->db, 0, SQLITE_FCNTL_TEMPFILENAME, &p->zTempFile);
13818 }
13819 if( p->zTempFile==0 ){
 
 
 
13820 sqlite3_uint64 r;
13821 sqlite3_randomness(sizeof(r), &r);
13822 p->zTempFile = sqlite3_mprintf("temp%llx.%s", r, zSuffix);
 
 
 
 
 
 
 
 
 
13823 }else{
13824 p->zTempFile = sqlite3_mprintf("%z.%s", p->zTempFile, zSuffix);
13825 }
13826 if( p->zTempFile==0 ){
13827 raw_printf(stderr, "out of memory\n");
@@ -15841,11 +15974,12 @@
15841 rc = recoverDatabaseCmd(p, nArg, azArg);
15842 }else
15843 #endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */
15844
15845 if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){
15846 const char *zLike = 0;
 
15847 int i;
15848 int savedShowHeader = p->showHeader;
15849 int savedShellFlags = p->shellFlgs;
15850 ShellClearFlag(p, SHFLG_PreserveRowid|SHFLG_Newlines|SHFLG_Echo);
15851 for(i=1; i<nArg; i++){
@@ -15869,16 +16003,14 @@
15869 raw_printf(stderr, "Unknown option \"%s\" on \".dump\"\n", azArg[i]);
15870 rc = 1;
15871 goto meta_command_exit;
15872 }
15873 }else if( zLike ){
15874 raw_printf(stderr, "Usage: .dump ?--preserve-rowids? "
15875 "?--newlines? ?LIKE-PATTERN?\n");
15876 rc = 1;
15877 goto meta_command_exit;
15878 }else{
15879 zLike = azArg[i];
15880 }
15881 }
15882
15883 open_db(p, 0);
15884
@@ -15892,39 +16024,29 @@
15892 /* Set writable_schema=ON since doing so forces SQLite to initialize
15893 ** as much of the schema as it can even if the sqlite_master table is
15894 ** corrupt. */
15895 sqlite3_exec(p->db, "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0);
15896 p->nErr = 0;
15897 if( zLike==0 ){
15898 run_schema_dump_query(p,
15899 "SELECT name, type, sql FROM sqlite_master "
15900 "WHERE sql NOT NULL AND type=='table' AND name!='sqlite_sequence'"
15901 );
15902 run_schema_dump_query(p,
15903 "SELECT name, type, sql FROM sqlite_master "
15904 "WHERE name=='sqlite_sequence'"
15905 );
15906 run_table_dump_query(p,
15907 "SELECT sql FROM sqlite_master "
15908 "WHERE sql NOT NULL AND type IN ('index','trigger','view')", 0
15909 );
15910 }else{
15911 char *zSql;
15912 zSql = sqlite3_mprintf(
15913 "SELECT name, type, sql FROM sqlite_master "
15914 "WHERE tbl_name LIKE %Q AND type=='table'"
15915 " AND sql NOT NULL", zLike);
15916 run_schema_dump_query(p,zSql);
15917 sqlite3_free(zSql);
15918 zSql = sqlite3_mprintf(
15919 "SELECT sql FROM sqlite_master "
15920 "WHERE sql NOT NULL"
15921 " AND type IN ('index','trigger','view')"
15922 " AND tbl_name LIKE %Q", zLike);
15923 run_table_dump_query(p, zSql, 0);
15924 sqlite3_free(zSql);
15925 }
15926 if( p->writableSchema ){
15927 raw_printf(p->out, "PRAGMA writable_schema=OFF;\n");
15928 p->writableSchema = 0;
15929 }
15930 sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0);
@@ -16023,20 +16145,32 @@
16023 { "psow", SQLITE_FCNTL_POWERSAFE_OVERWRITE, "[BOOLEAN]" },
16024 /* { "pragma", SQLITE_FCNTL_PRAGMA, "NAME ARG" },*/
16025 { "tempfilename", SQLITE_FCNTL_TEMPFILENAME, "" },
16026 { "has_moved", SQLITE_FCNTL_HAS_MOVED, "" },
16027 { "lock_timeout", SQLITE_FCNTL_LOCK_TIMEOUT, "MILLISEC" },
 
16028 };
16029 int filectrl = -1;
16030 int iCtrl = -1;
16031 sqlite3_int64 iRes = 0; /* Integer result to display if rc2==1 */
16032 int isOk = 0; /* 0: usage 1: %lld 2: no-result */
16033 int n2, i;
16034 const char *zCmd = 0;
 
16035
16036 open_db(p, 0);
16037 zCmd = nArg>=2 ? azArg[1] : "help";
 
 
 
 
 
 
 
 
 
 
16038
16039 /* The argument can optionally begin with "-" or "--" */
16040 if( zCmd[0]=='-' && zCmd[1] ){
16041 zCmd++;
16042 if( zCmd[0]=='-' && zCmd[1] ) zCmd++;
@@ -16075,51 +16209,63 @@
16075 }else{
16076 switch(filectrl){
16077 case SQLITE_FCNTL_SIZE_LIMIT: {
16078 if( nArg!=2 && nArg!=3 ) break;
16079 iRes = nArg==3 ? integerValue(azArg[2]) : -1;
16080 sqlite3_file_control(p->db, 0, SQLITE_FCNTL_SIZE_LIMIT, &iRes);
16081 isOk = 1;
16082 break;
16083 }
16084 case SQLITE_FCNTL_LOCK_TIMEOUT:
16085 case SQLITE_FCNTL_CHUNK_SIZE: {
16086 int x;
16087 if( nArg!=3 ) break;
16088 x = (int)integerValue(azArg[2]);
16089 sqlite3_file_control(p->db, 0, filectrl, &x);
16090 isOk = 2;
16091 break;
16092 }
16093 case SQLITE_FCNTL_PERSIST_WAL:
16094 case SQLITE_FCNTL_POWERSAFE_OVERWRITE: {
16095 int x;
16096 if( nArg!=2 && nArg!=3 ) break;
16097 x = nArg==3 ? booleanValue(azArg[2]) : -1;
16098 sqlite3_file_control(p->db, 0, filectrl, &x);
16099 iRes = x;
16100 isOk = 1;
16101 break;
16102 }
16103 case SQLITE_FCNTL_HAS_MOVED: {
16104 int x;
16105 if( nArg!=2 ) break;
16106 sqlite3_file_control(p->db, 0, filectrl, &x);
16107 iRes = x;
16108 isOk = 1;
16109 break;
16110 }
16111 case SQLITE_FCNTL_TEMPFILENAME: {
16112 char *z = 0;
16113 if( nArg!=2 ) break;
16114 sqlite3_file_control(p->db, 0, filectrl, &z);
16115 if( z ){
16116 utf8_printf(p->out, "%s\n", z);
16117 sqlite3_free(z);
16118 }
16119 isOk = 2;
16120 break;
 
 
 
 
 
 
 
 
 
 
 
 
16121 }
16122 }
16123 }
16124 if( isOk==0 && iCtrl>=0 ){
16125 utf8_printf(p->out, "Usage: .filectrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage);
@@ -16850,46 +16996,70 @@
16850
16851 if( (c=='o'
16852 && (strncmp(azArg[0], "output", n)==0||strncmp(azArg[0], "once", n)==0))
16853 || (c=='e' && n==5 && strcmp(azArg[0],"excel")==0)
16854 ){
16855 const char *zFile = nArg>=2 ? azArg[1] : "stdout";
16856 int bTxtMode = 0;
16857 if( azArg[0][0]=='e' ){
16858 /* Transform the ".excel" command into ".once -x" */
16859 nArg = 2;
16860 azArg[0] = "once";
16861 zFile = azArg[1] = "-x";
16862 n = 4;
16863 }
16864 if( nArg>2 ){
16865 utf8_printf(stderr, "Usage: .%s [-e|-x|FILE]\n", azArg[0]);
16866 rc = 1;
16867 goto meta_command_exit;
16868 }
16869 if( n>1 && strncmp(azArg[0], "once", n)==0 ){
16870 if( nArg<2 ){
16871 raw_printf(stderr, "Usage: .once (-e|-x|FILE)\n");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16872 rc = 1;
16873 goto meta_command_exit;
16874 }
 
 
 
16875 p->outCount = 2;
16876 }else{
16877 p->outCount = 0;
16878 }
16879 output_reset(p);
16880 if( zFile[0]=='-' && zFile[1]=='-' ) zFile++;
16881 #ifndef SQLITE_NOHAVE_SYSTEM
16882 if( strcmp(zFile, "-e")==0 || strcmp(zFile, "-x")==0 ){
16883 p->doXdgOpen = 1;
16884 outputModePush(p);
16885 if( zFile[1]=='x' ){
 
16886 newTempFile(p, "csv");
 
16887 p->mode = MODE_Csv;
16888 sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma);
16889 sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf);
16890 }else{
 
16891 newTempFile(p, "txt");
16892 bTxtMode = 1;
16893 }
16894 zFile = p->zTempFile;
16895 }
@@ -16904,10 +17074,11 @@
16904 if( p->out==0 ){
16905 utf8_printf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1);
16906 p->out = stdout;
16907 rc = 1;
16908 }else{
 
16909 sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
16910 }
16911 #endif
16912 }else{
16913 p->out = output_file_open(zFile, bTxtMode);
@@ -16916,10 +17087,11 @@
16916 utf8_printf(stderr,"Error: cannot write to \"%s\"\n", zFile);
16917 }
16918 p->out = stdout;
16919 rc = 1;
16920 } else {
 
16921 sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
16922 }
16923 }
16924 }else
16925
@@ -17979,11 +18151,10 @@
17979 #endif
17980 { "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE, "OFFSET " },
17981 { "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE, "" },
17982 { "prng_save", SQLITE_TESTCTRL_PRNG_SAVE, "" },
17983 { "prng_seed", SQLITE_TESTCTRL_PRNG_SEED, "SEED ?db?" },
17984 { "reserve", SQLITE_TESTCTRL_RESERVE, "BYTES-OF-RESERVE"},
17985 };
17986 int testctrl = -1;
17987 int iCtrl = -1;
17988 int rc2 = 0; /* 0: usage. 1: %d 2: %x 3: no-output */
17989 int isOk = 0;
@@ -18032,11 +18203,10 @@
18032 }else{
18033 switch(testctrl){
18034
18035 /* sqlite3_test_control(int, db, int) */
18036 case SQLITE_TESTCTRL_OPTIMIZATIONS:
18037 case SQLITE_TESTCTRL_RESERVE:
18038 if( nArg==3 ){
18039 int opt = (int)strtol(azArg[2], 0, 0);
18040 rc2 = sqlite3_test_control(testctrl, p->db, opt);
18041 isOk = 3;
18042 }
@@ -18804,18 +18974,22 @@
18804 /*
18805 ** Output text to the console in a font that attracts extra attention.
18806 */
18807 #ifdef _WIN32
18808 static void printBold(const char *zText){
 
18809 HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
18810 CONSOLE_SCREEN_BUFFER_INFO defaultScreenInfo;
18811 GetConsoleScreenBufferInfo(out, &defaultScreenInfo);
18812 SetConsoleTextAttribute(out,
18813 FOREGROUND_RED|FOREGROUND_INTENSITY
18814 );
 
18815 printf("%s", zText);
 
18816 SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes);
 
18817 }
18818 #else
18819 static void printBold(const char *zText){
18820 printf("\033[1m%s\033[0m", zText);
18821 }
@@ -18879,11 +19053,15 @@
18879 "attach debugger to process %d and press any key to continue.\n",
18880 GETPID());
18881 fgetc(stdin);
18882 }else{
18883 #if defined(_WIN32) || defined(WIN32)
 
 
 
18884 DebugBreak();
 
18885 #elif defined(SIGTRAP)
18886 raise(SIGTRAP);
18887 #endif
18888 }
18889 }
18890
--- src/shell.c
+++ src/shell.c
@@ -33,10 +33,18 @@
33 #if (defined(_WIN32) || defined(WIN32)) && !defined(_CRT_SECURE_NO_WARNINGS)
34 /* This needs to come before any includes for MSVC compiler */
35 #define _CRT_SECURE_NO_WARNINGS
36 #endif
37
38 /*
39 ** Determine if we are dealing with WinRT, which provides only a subset of
40 ** the full Win32 API.
41 */
42 #if !defined(SQLITE_OS_WINRT)
43 # define SQLITE_OS_WINRT 0
44 #endif
45
46 /*
47 ** Warning pragmas copied from msvc.h in the core.
48 */
49 #if defined(_MSC_VER)
50 #pragma warning(disable : 4054)
@@ -145,26 +153,30 @@
153 # define SHELL_USE_LOCAL_GETLINE 1
154 #endif
155
156
157 #if defined(_WIN32) || defined(WIN32)
158 # if SQLITE_OS_WINRT
159 # define SQLITE_OMIT_POPEN 1
160 # else
161 # include <io.h>
162 # include <fcntl.h>
163 # define isatty(h) _isatty(h)
164 # ifndef access
165 # define access(f,m) _access((f),(m))
166 # endif
167 # ifndef unlink
168 # define unlink _unlink
169 # endif
170 # ifndef strdup
171 # define strdup _strdup
172 # endif
173 # undef popen
174 # define popen _popen
175 # undef pclose
176 # define pclose _pclose
177 # endif
178 #else
179 /* Make sure isatty() has a prototype. */
180 extern int isatty(int);
181
182 # if !defined(__RTP__) && !defined(_WRS_KERNEL)
@@ -189,10 +201,13 @@
201 #define IsSpace(X) isspace((unsigned char)X)
202 #define IsDigit(X) isdigit((unsigned char)X)
203 #define ToLower(X) (char)tolower((unsigned char)X)
204
205 #if defined(_WIN32) || defined(WIN32)
206 #if SQLITE_OS_WINRT
207 #include <intrin.h>
208 #endif
209 #include <windows.h>
210
211 /* string conversion routines only needed on Win32 */
212 extern char *sqlite3_win32_unicode_to_utf8(LPCWSTR);
213 extern char *sqlite3_win32_mbcs_to_utf8_v2(const char *, int);
@@ -204,11 +219,11 @@
219 ** are automatically translated into \r\n. However, this behavior needs
220 ** to be disabled in some cases (ex: when generating CSV output and when
221 ** rendering quoted strings that contain \n characters). The following
222 ** routines take care of that.
223 */
224 #if (defined(_WIN32) || defined(WIN32)) && !SQLITE_OS_WINRT
225 static void setBinaryMode(FILE *file, int isOutput){
226 if( isOutput ) fflush(file);
227 _setmode(_fileno(file), _O_BINARY);
228 }
229 static void setTextMode(FILE *file, int isOutput){
@@ -308,10 +323,11 @@
323 */
324 static int hasTimer(void){
325 if( getProcessTimesAddr ){
326 return 1;
327 } else {
328 #if !SQLITE_OS_WINRT
329 /* GetProcessTimes() isn't supported in WIN95 and some other Windows
330 ** versions. See if the version we are running on has it, and if it
331 ** does, save off a pointer to it and the current process handle.
332 */
333 hProcess = GetCurrentProcess();
@@ -324,10 +340,11 @@
340 return 1;
341 }
342 FreeLibrary(hinstLib);
343 }
344 }
345 #endif
346 }
347 return 0;
348 }
349
350 /*
@@ -2476,10 +2493,11 @@
2493 }
2494 }
2495
2496 if( mtime>=0 ){
2497 #if defined(_WIN32)
2498 #if !SQLITE_OS_WINRT
2499 /* Windows */
2500 FILETIME lastAccess;
2501 FILETIME lastWrite;
2502 SYSTEMTIME currentTime;
2503 LONGLONG intervals;
@@ -2506,10 +2524,11 @@
2524 CloseHandle(hFile);
2525 return !bResult;
2526 }else{
2527 return 1;
2528 }
2529 #endif
2530 #elif defined(AT_FDCWD) && 0 /* utimensat() is not universally available */
2531 /* Recent unix */
2532 struct timespec times[2];
2533 times[0].tv_nsec = times[1].tv_nsec = 0;
2534 times[0].tv_sec = time(0);
@@ -4263,10 +4282,105 @@
4282 memtraceOut = 0;
4283 return rc;
4284 }
4285
4286 /************************* End ../ext/misc/memtrace.c ********************/
4287 /************************* Begin ../ext/misc/uint.c ******************/
4288 /*
4289 ** 2020-04-14
4290 **
4291 ** The author disclaims copyright to this source code. In place of
4292 ** a legal notice, here is a blessing:
4293 **
4294 ** May you do good and not evil.
4295 ** May you find forgiveness for yourself and forgive others.
4296 ** May you share freely, never taking more than you give.
4297 **
4298 ******************************************************************************
4299 **
4300 ** This SQLite extension implements the UINT collating sequence.
4301 **
4302 ** UINT works like BINARY for text, except that embedded strings
4303 ** of digits compare in numeric order.
4304 **
4305 ** * Leading zeros are handled properly, in the sense that
4306 ** they do not mess of the maginitude comparison of embedded
4307 ** strings of digits. "x00123y" is equal to "x123y".
4308 **
4309 ** * Only unsigned integers are recognized. Plus and minus
4310 ** signs are ignored. Decimal points and exponential notation
4311 ** are ignored.
4312 **
4313 ** * Embedded integers can be of arbitrary length. Comparison
4314 ** is *not* limited integers that can be expressed as a
4315 ** 64-bit machine integer.
4316 */
4317 /* #include "sqlite3ext.h" */
4318 SQLITE_EXTENSION_INIT1
4319 #include <assert.h>
4320 #include <string.h>
4321 #include <ctype.h>
4322
4323 /*
4324 ** Compare text in lexicographic order, except strings of digits
4325 ** compare in numeric order.
4326 */
4327 static int uintCollFunc(
4328 void *notUsed,
4329 int nKey1, const void *pKey1,
4330 int nKey2, const void *pKey2
4331 ){
4332 const unsigned char *zA = (const unsigned char*)pKey1;
4333 const unsigned char *zB = (const unsigned char*)pKey2;
4334 int i=0, j=0, x;
4335 (void)notUsed;
4336 while( i<nKey1 && j<nKey2 ){
4337 x = zA[i] - zB[j];
4338 if( isdigit(zA[i]) ){
4339 int k;
4340 if( !isdigit(zB[j]) ) return x;
4341 while( i<nKey1 && zA[i]=='0' ){ i++; }
4342 while( j<nKey2 && zB[j]=='0' ){ j++; }
4343 k = 0;
4344 while( i+k<nKey1 && isdigit(zA[i+k])
4345 && j+k<nKey2 && isdigit(zB[j+k]) ){
4346 k++;
4347 }
4348 if( i+k<nKey1 && isdigit(zA[i+k]) ){
4349 return +1;
4350 }else if( j+k<nKey2 && isdigit(zB[j+k]) ){
4351 return -1;
4352 }else{
4353 x = memcmp(zA+i, zB+j, k);
4354 if( x ) return x;
4355 i += k;
4356 j += k;
4357 }
4358 }else if( x ){
4359 return x;
4360 }else{
4361 i++;
4362 j++;
4363 }
4364 }
4365 return (nKey1 - i) - (nKey2 - j);
4366 }
4367
4368 #ifdef _WIN32
4369
4370 #endif
4371 int sqlite3_uint_init(
4372 sqlite3 *db,
4373 char **pzErrMsg,
4374 const sqlite3_api_routines *pApi
4375 ){
4376 SQLITE_EXTENSION_INIT2(pApi);
4377 (void)pzErrMsg; /* Unused parameter */
4378 return sqlite3_create_collation(db, "uint", SQLITE_UTF8, 0, uintCollFunc);
4379 }
4380
4381 /************************* End ../ext/misc/uint.c ********************/
4382 #ifdef SQLITE_HAVE_ZLIB
4383 /************************* Begin ../ext/misc/zipfile.c ******************/
4384 /*
4385 ** 2017-12-26
4386 **
@@ -9652,10 +9766,11 @@
9766 int nCheck; /* Number of ".check" commands run */
9767 unsigned nProgress; /* Number of progress callbacks encountered */
9768 unsigned mxProgress; /* Maximum progress callbacks before failing */
9769 unsigned flgProgress; /* Flags for the progress callback */
9770 unsigned shellFlgs; /* Various flags */
9771 unsigned priorShFlgs; /* Saved copy of flags */
9772 sqlite3_int64 szMax; /* --maxsize argument to .open */
9773 char *zDestTable; /* Name of destination table when MODE_Insert */
9774 char *zTempFile; /* Temporary file that might need deleting */
9775 char zTestcase[30]; /* Name of current test case */
9776 char colSeparator[20]; /* Column separator character for several modes */
@@ -9952,15 +10067,17 @@
10067 /*
10068 ** Save or restore the current output mode
10069 */
10070 static void outputModePush(ShellState *p){
10071 p->modePrior = p->mode;
10072 p->priorShFlgs = p->shellFlgs;
10073 memcpy(p->colSepPrior, p->colSeparator, sizeof(p->colSeparator));
10074 memcpy(p->rowSepPrior, p->rowSeparator, sizeof(p->rowSeparator));
10075 }
10076 static void outputModePop(ShellState *p){
10077 p->mode = p->modePrior;
10078 p->shellFlgs = p->priorShFlgs;
10079 memcpy(p->colSeparator, p->colSepPrior, sizeof(p->colSeparator));
10080 memcpy(p->rowSeparator, p->rowSepPrior, sizeof(p->rowSeparator));
10081 }
10082
10083 /*
@@ -10921,12 +11038,11 @@
11038 ** "--" comment occurs at the end of the statement, the comment
11039 ** won't consume the semicolon terminator.
11040 */
11041 static int run_table_dump_query(
11042 ShellState *p, /* Query context */
11043 const char *zSelect /* SELECT statement to extract content */
 
11044 ){
11045 sqlite3_stmt *pSelect;
11046 int rc;
11047 int nResult;
11048 int i;
@@ -10939,14 +11055,10 @@
11055 return rc;
11056 }
11057 rc = sqlite3_step(pSelect);
11058 nResult = sqlite3_column_count(pSelect);
11059 while( rc==SQLITE_ROW ){
 
 
 
 
11060 z = (const char*)sqlite3_column_text(pSelect, 0);
11061 utf8_printf(p->out, "%s", z);
11062 for(i=1; i<nResult; i++){
11063 utf8_printf(p->out, ",%s", sqlite3_column_text(pSelect, i));
11064 }
@@ -11399,13 +11511,13 @@
11511 ** Parameter bindings are taken from a TEMP table of the form:
11512 **
11513 ** CREATE TEMP TABLE sqlite_parameters(key TEXT PRIMARY KEY, value)
11514 ** WITHOUT ROWID;
11515 **
11516 ** No bindings occur if this table does not exist. The name of the table
11517 ** begins with "sqlite_" so that it will not collide with ordinary application
11518 ** tables. The table must be in the TEMP schema.
11519 */
11520 static void bind_prepared_stmt(ShellState *pArg, sqlite3_stmt *pStmt){
11521 int nVar;
11522 int i;
11523 int rc;
@@ -11705,10 +11817,11 @@
11817 if( rc==SQLITE_OK ){
11818 while( sqlite3_step(pExplain)==SQLITE_ROW ){
11819 const char *zEQPLine = (const char*)sqlite3_column_text(pExplain,3);
11820 int iEqpId = sqlite3_column_int(pExplain, 0);
11821 int iParentId = sqlite3_column_int(pExplain, 1);
11822 if( zEQPLine==0 ) zEQPLine = "";
11823 if( zEQPLine[0]=='-' ) eqp_render(pArg);
11824 eqp_append(pArg, iEqpId, iParentId, zEQPLine);
11825 }
11826 eqp_render(pArg);
11827 }
@@ -12115,29 +12228,32 @@
12228 ".check GLOB Fail if output since .testcase does not match",
12229 ".clone NEWDB Clone data into NEWDB from the existing database",
12230 ".databases List names and files of attached databases",
12231 ".dbconfig ?op? ?val? List or change sqlite3_db_config() options",
12232 ".dbinfo ?DB? Show status information about the database",
12233 ".dump ?TABLE? Render database content as SQL",
12234 " Options:",
12235 " --preserve-rowids Include ROWID values in the output",
12236 " --newlines Allow unescaped newline characters in output",
12237 " TABLE is a LIKE pattern for the tables to dump",
12238 " Additional LIKE patterns can be given in subsequent arguments",
12239 ".echo on|off Turn command echo on or off",
12240 ".eqp on|off|full|... Enable or disable automatic EXPLAIN QUERY PLAN",
12241 " Other Modes:",
12242 #ifdef SQLITE_DEBUG
12243 " test Show raw EXPLAIN QUERY PLAN output",
12244 " trace Like \"full\" but enable \"PRAGMA vdbe_trace\"",
12245 #endif
12246 " trigger Like \"full\" but also show trigger bytecode",
12247 ".excel Display the output of next command in spreadsheet",
12248 " --bom Put a UTF8 byte-order mark on intermediate file",
12249 ".exit ?CODE? Exit this program with return-code CODE",
12250 ".expert EXPERIMENTAL. Suggest indexes for queries",
12251 ".explain ?on|off|auto? Change the EXPLAIN formatting mode. Default: auto",
12252 ".filectrl CMD ... Run various sqlite3_file_control() operations",
12253 " --schema SCHEMA Use SCHEMA instead of \"main\"",
12254 " --help Show CMD details",
12255 ".fullschema ?--indent? Show schema and the content of sqlite_stat tables",
12256 ".headers on|off Turn display of headers on or off",
12257 ".help ?-all? ?PATTERN? Show help text for PATTERN",
12258 ".import FILE TABLE Import data from FILE into TABLE",
12259 " Options:",
@@ -12180,15 +12296,15 @@
12296 " list Values delimited by \"|\"",
12297 " quote Escape answers as for SQL",
12298 " tabs Tab-separated values",
12299 " tcl TCL list elements",
12300 ".nullvalue STRING Use STRING in place of NULL values",
12301 ".once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE",
12302 " If FILE begins with '|' then open as a pipe",
12303 " --bom Put a UTF8 byte-order mark at the beginning",
12304 " -e Send output to the system text editor",
12305 " -x Send output as CSV to a spreadsheet (same as \".excel\")",
12306 #ifdef SQLITE_DEBUG
12307 ".oom [--repeat M] [N] Simulate an OOM error on the N-th allocation",
12308 #endif
12309 ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE",
12310 " Options:",
@@ -12201,11 +12317,15 @@
12317 " --new Initialize FILE to an empty database",
12318 " --nofollow Do not follow symbolic links",
12319 " --readonly Open FILE readonly",
12320 " --zip FILE is a ZIP archive",
12321 ".output ?FILE? Send output to FILE or stdout if FILE is omitted",
12322 " If FILE begins with '|' then open it as a pipe.",
12323 " Options:",
12324 " --bom Prefix output with a UTF8 byte-order mark",
12325 " -e Send output to the system text editor",
12326 " -x Send output as CSV to a spreadsheet",
12327 ".parameter CMD ... Manage SQL parameter bindings",
12328 " clear Erase all bindings",
12329 " init Initialize the TEMP table that holds bindings",
12330 " list List the current parameter bindings",
12331 " set PARAMETER VALUE Given SQL parameter PARAMETER a value of VALUE",
@@ -12321,10 +12441,11 @@
12441 char *zPat;
12442 if( zPattern==0
12443 || zPattern[0]=='0'
12444 || strcmp(zPattern,"-a")==0
12445 || strcmp(zPattern,"-all")==0
12446 || strcmp(zPattern,"--all")==0
12447 ){
12448 /* Show all commands, but only one line per command */
12449 if( zPattern==0 ) zPattern = "";
12450 for(i=0; i<ArraySize(azHelp); i++){
12451 if( azHelp[i][0]=='.' || zPattern[0] ){
@@ -12802,10 +12923,11 @@
12923 sqlite3_enable_load_extension(p->db, 1);
12924 #endif
12925 sqlite3_fileio_init(p->db, 0, 0);
12926 sqlite3_shathree_init(p->db, 0, 0);
12927 sqlite3_completion_init(p->db, 0, 0);
12928 sqlite3_uint_init(p->db, 0, 0);
12929 #if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB)
12930 sqlite3_dbdata_init(p->db, 0, 0);
12931 #endif
12932 #ifdef SQLITE_HAVE_ZLIB
12933 sqlite3_zipfile_init(p->db, 0, 0);
@@ -13521,15 +13643,19 @@
13643 #endif
13644 char *zCmd;
13645 zCmd = sqlite3_mprintf("%s %s", zXdgOpenCmd, p->zTempFile);
13646 if( system(zCmd) ){
13647 utf8_printf(stderr, "Failed: [%s]\n", zCmd);
13648 }else{
13649 /* Give the start/open/xdg-open command some time to get
13650 ** going before we continue, and potential delete the
13651 ** p->zTempFile data file out from under it */
13652 sqlite3_sleep(2000);
13653 }
13654 sqlite3_free(zCmd);
13655 outputModePop(p);
13656 p->doXdgOpen = 0;
 
13657 }
13658 #endif /* !defined(SQLITE_NOHAVE_SYSTEM) */
13659 }
13660 p->outfile[0] = 0;
13661 p->out = stdout;
@@ -13601,16 +13727,11 @@
13727 if( p->db==0 ) return 1;
13728 rc = sqlite3_prepare_v2(p->db,
13729 "SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1",
13730 -1, &pStmt, 0);
13731 if( rc ){
13732 utf8_printf(stderr, "error: %s\n", sqlite3_errmsg(p->db));
 
 
 
 
 
13733 sqlite3_finalize(pStmt);
13734 return 1;
13735 }
13736 sqlite3_bind_text(pStmt, 1, zDb, -1, SQLITE_STATIC);
13737 if( sqlite3_step(pStmt)==SQLITE_ROW
@@ -13815,13 +13936,25 @@
13936 p->zTempFile = 0;
13937 if( p->db ){
13938 sqlite3_file_control(p->db, 0, SQLITE_FCNTL_TEMPFILENAME, &p->zTempFile);
13939 }
13940 if( p->zTempFile==0 ){
13941 /* If p->db is an in-memory database then the TEMPFILENAME file-control
13942 ** will not work and we will need to fallback to guessing */
13943 char *zTemp;
13944 sqlite3_uint64 r;
13945 sqlite3_randomness(sizeof(r), &r);
13946 zTemp = getenv("TEMP");
13947 if( zTemp==0 ) zTemp = getenv("TMP");
13948 if( zTemp==0 ){
13949 #ifdef _WIN32
13950 zTemp = "\\tmp";
13951 #else
13952 zTemp = "/tmp";
13953 #endif
13954 }
13955 p->zTempFile = sqlite3_mprintf("%s/temp%llx.%s", zTemp, r, zSuffix);
13956 }else{
13957 p->zTempFile = sqlite3_mprintf("%z.%s", p->zTempFile, zSuffix);
13958 }
13959 if( p->zTempFile==0 ){
13960 raw_printf(stderr, "out of memory\n");
@@ -15841,11 +15974,12 @@
15974 rc = recoverDatabaseCmd(p, nArg, azArg);
15975 }else
15976 #endif /* !(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_ENABLE_DBPAGE_VTAB) */
15977
15978 if( c=='d' && strncmp(azArg[0], "dump", n)==0 ){
15979 char *zLike = 0;
15980 char *zSql;
15981 int i;
15982 int savedShowHeader = p->showHeader;
15983 int savedShellFlags = p->shellFlgs;
15984 ShellClearFlag(p, SHFLG_PreserveRowid|SHFLG_Newlines|SHFLG_Echo);
15985 for(i=1; i<nArg; i++){
@@ -15869,16 +16003,14 @@
16003 raw_printf(stderr, "Unknown option \"%s\" on \".dump\"\n", azArg[i]);
16004 rc = 1;
16005 goto meta_command_exit;
16006 }
16007 }else if( zLike ){
16008 zLike = sqlite3_mprintf("%z OR name LIKE %Q ESCAPE '\\'",
16009 zLike, azArg[i]);
 
 
16010 }else{
16011 zLike = sqlite3_mprintf("name LIKE %Q ESCAPE '\\'", azArg[i]);
16012 }
16013 }
16014
16015 open_db(p, 0);
16016
@@ -15892,39 +16024,29 @@
16024 /* Set writable_schema=ON since doing so forces SQLite to initialize
16025 ** as much of the schema as it can even if the sqlite_master table is
16026 ** corrupt. */
16027 sqlite3_exec(p->db, "SAVEPOINT dump; PRAGMA writable_schema=ON", 0, 0, 0);
16028 p->nErr = 0;
16029 if( zLike==0 ) zLike = sqlite3_mprintf("true");
16030 zSql = sqlite3_mprintf(
16031 "SELECT name, type, sql FROM sqlite_master "
16032 "WHERE (%s) AND type=='table'"
16033 " AND sql NOT NULL"
16034 " ORDER BY tbl_name='sqlite_sequence', rowid",
16035 zLike
16036 );
16037 run_schema_dump_query(p,zSql);
16038 sqlite3_free(zSql);
16039 zSql = sqlite3_mprintf(
16040 "SELECT sql FROM sqlite_master "
16041 "WHERE (%s) AND sql NOT NULL"
16042 " AND type IN ('index','trigger','view')",
16043 zLike
16044 );
16045 run_table_dump_query(p, zSql);
16046 sqlite3_free(zSql);
16047 sqlite3_free(zLike);
 
 
 
 
 
 
 
 
 
 
16048 if( p->writableSchema ){
16049 raw_printf(p->out, "PRAGMA writable_schema=OFF;\n");
16050 p->writableSchema = 0;
16051 }
16052 sqlite3_exec(p->db, "PRAGMA writable_schema=OFF;", 0, 0, 0);
@@ -16023,20 +16145,32 @@
16145 { "psow", SQLITE_FCNTL_POWERSAFE_OVERWRITE, "[BOOLEAN]" },
16146 /* { "pragma", SQLITE_FCNTL_PRAGMA, "NAME ARG" },*/
16147 { "tempfilename", SQLITE_FCNTL_TEMPFILENAME, "" },
16148 { "has_moved", SQLITE_FCNTL_HAS_MOVED, "" },
16149 { "lock_timeout", SQLITE_FCNTL_LOCK_TIMEOUT, "MILLISEC" },
16150 { "reserve_bytes", SQLITE_FCNTL_RESERVE_BYTES, "[N]" },
16151 };
16152 int filectrl = -1;
16153 int iCtrl = -1;
16154 sqlite3_int64 iRes = 0; /* Integer result to display if rc2==1 */
16155 int isOk = 0; /* 0: usage 1: %lld 2: no-result */
16156 int n2, i;
16157 const char *zCmd = 0;
16158 const char *zSchema = 0;
16159
16160 open_db(p, 0);
16161 zCmd = nArg>=2 ? azArg[1] : "help";
16162
16163 if( zCmd[0]=='-'
16164 && (strcmp(zCmd,"--schema")==0 || strcmp(zCmd,"-schema")==0)
16165 && nArg>=4
16166 ){
16167 zSchema = azArg[2];
16168 for(i=3; i<nArg; i++) azArg[i-2] = azArg[i];
16169 nArg -= 2;
16170 zCmd = azArg[1];
16171 }
16172
16173 /* The argument can optionally begin with "-" or "--" */
16174 if( zCmd[0]=='-' && zCmd[1] ){
16175 zCmd++;
16176 if( zCmd[0]=='-' && zCmd[1] ) zCmd++;
@@ -16075,51 +16209,63 @@
16209 }else{
16210 switch(filectrl){
16211 case SQLITE_FCNTL_SIZE_LIMIT: {
16212 if( nArg!=2 && nArg!=3 ) break;
16213 iRes = nArg==3 ? integerValue(azArg[2]) : -1;
16214 sqlite3_file_control(p->db, zSchema, SQLITE_FCNTL_SIZE_LIMIT, &iRes);
16215 isOk = 1;
16216 break;
16217 }
16218 case SQLITE_FCNTL_LOCK_TIMEOUT:
16219 case SQLITE_FCNTL_CHUNK_SIZE: {
16220 int x;
16221 if( nArg!=3 ) break;
16222 x = (int)integerValue(azArg[2]);
16223 sqlite3_file_control(p->db, zSchema, filectrl, &x);
16224 isOk = 2;
16225 break;
16226 }
16227 case SQLITE_FCNTL_PERSIST_WAL:
16228 case SQLITE_FCNTL_POWERSAFE_OVERWRITE: {
16229 int x;
16230 if( nArg!=2 && nArg!=3 ) break;
16231 x = nArg==3 ? booleanValue(azArg[2]) : -1;
16232 sqlite3_file_control(p->db, zSchema, filectrl, &x);
16233 iRes = x;
16234 isOk = 1;
16235 break;
16236 }
16237 case SQLITE_FCNTL_HAS_MOVED: {
16238 int x;
16239 if( nArg!=2 ) break;
16240 sqlite3_file_control(p->db, zSchema, filectrl, &x);
16241 iRes = x;
16242 isOk = 1;
16243 break;
16244 }
16245 case SQLITE_FCNTL_TEMPFILENAME: {
16246 char *z = 0;
16247 if( nArg!=2 ) break;
16248 sqlite3_file_control(p->db, zSchema, filectrl, &z);
16249 if( z ){
16250 utf8_printf(p->out, "%s\n", z);
16251 sqlite3_free(z);
16252 }
16253 isOk = 2;
16254 break;
16255 }
16256 case SQLITE_FCNTL_RESERVE_BYTES: {
16257 int x;
16258 if( nArg>=3 ){
16259 x = atoi(azArg[2]);
16260 sqlite3_file_control(p->db, zSchema, filectrl, &x);
16261 }
16262 x = -1;
16263 sqlite3_file_control(p->db, zSchema, filectrl, &x);
16264 utf8_printf(p->out,"%d\n", x);
16265 isOk = 2;
16266 break;
16267 }
16268 }
16269 }
16270 if( isOk==0 && iCtrl>=0 ){
16271 utf8_printf(p->out, "Usage: .filectrl %s %s\n", zCmd,aCtrl[iCtrl].zUsage);
@@ -16850,46 +16996,70 @@
16996
16997 if( (c=='o'
16998 && (strncmp(azArg[0], "output", n)==0||strncmp(azArg[0], "once", n)==0))
16999 || (c=='e' && n==5 && strcmp(azArg[0],"excel")==0)
17000 ){
17001 const char *zFile = 0;
17002 int bTxtMode = 0;
17003 int i;
17004 int eMode = 0;
17005 int bBOM = 0;
17006 int bOnce = 0; /* 0: .output, 1: .once, 2: .excel */
17007
17008 if( c=='e' ){
17009 eMode = 'x';
17010 bOnce = 2;
17011 }else if( strncmp(azArg[0],"once",n)==0 ){
17012 bOnce = 1;
17013 }
17014 for(i=1; i<nArg; i++){
17015 char *z = azArg[i];
17016 if( z[0]=='-' ){
17017 if( z[1]=='-' ) z++;
17018 if( strcmp(z,"-bom")==0 ){
17019 bBOM = 1;
17020 }else if( c!='e' && strcmp(z,"-x")==0 ){
17021 eMode = 'x'; /* spreadsheet */
17022 }else if( c!='e' && strcmp(z,"-e")==0 ){
17023 eMode = 'e'; /* text editor */
17024 }else{
17025 utf8_printf(p->out, "ERROR: unknown option: \"%s\". Usage:\n",
17026 azArg[i]);
17027 showHelp(p->out, azArg[0]);
17028 rc = 1;
17029 goto meta_command_exit;
17030 }
17031 }else if( zFile==0 ){
17032 zFile = z;
17033 }else{
17034 utf8_printf(p->out,"ERROR: extra parameter: \"%s\". Usage:\n",
17035 azArg[i]);
17036 showHelp(p->out, azArg[0]);
17037 rc = 1;
17038 goto meta_command_exit;
17039 }
17040 }
17041 if( zFile==0 ) zFile = "stdout";
17042 if( bOnce ){
17043 p->outCount = 2;
17044 }else{
17045 p->outCount = 0;
17046 }
17047 output_reset(p);
 
17048 #ifndef SQLITE_NOHAVE_SYSTEM
17049 if( eMode=='e' || eMode=='x' ){
17050 p->doXdgOpen = 1;
17051 outputModePush(p);
17052 if( eMode=='x' ){
17053 /* spreadsheet mode. Output as CSV. */
17054 newTempFile(p, "csv");
17055 ShellClearFlag(p, SHFLG_Echo);
17056 p->mode = MODE_Csv;
17057 sqlite3_snprintf(sizeof(p->colSeparator), p->colSeparator, SEP_Comma);
17058 sqlite3_snprintf(sizeof(p->rowSeparator), p->rowSeparator, SEP_CrLf);
17059 }else{
17060 /* text editor mode */
17061 newTempFile(p, "txt");
17062 bTxtMode = 1;
17063 }
17064 zFile = p->zTempFile;
17065 }
@@ -16904,10 +17074,11 @@
17074 if( p->out==0 ){
17075 utf8_printf(stderr,"Error: cannot open pipe \"%s\"\n", zFile + 1);
17076 p->out = stdout;
17077 rc = 1;
17078 }else{
17079 if( bBOM ) fprintf(p->out,"\357\273\277");
17080 sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
17081 }
17082 #endif
17083 }else{
17084 p->out = output_file_open(zFile, bTxtMode);
@@ -16916,10 +17087,11 @@
17087 utf8_printf(stderr,"Error: cannot write to \"%s\"\n", zFile);
17088 }
17089 p->out = stdout;
17090 rc = 1;
17091 } else {
17092 if( bBOM ) fprintf(p->out,"\357\273\277");
17093 sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
17094 }
17095 }
17096 }else
17097
@@ -17979,11 +18151,10 @@
18151 #endif
18152 { "pending_byte", SQLITE_TESTCTRL_PENDING_BYTE, "OFFSET " },
18153 { "prng_restore", SQLITE_TESTCTRL_PRNG_RESTORE, "" },
18154 { "prng_save", SQLITE_TESTCTRL_PRNG_SAVE, "" },
18155 { "prng_seed", SQLITE_TESTCTRL_PRNG_SEED, "SEED ?db?" },
 
18156 };
18157 int testctrl = -1;
18158 int iCtrl = -1;
18159 int rc2 = 0; /* 0: usage. 1: %d 2: %x 3: no-output */
18160 int isOk = 0;
@@ -18032,11 +18203,10 @@
18203 }else{
18204 switch(testctrl){
18205
18206 /* sqlite3_test_control(int, db, int) */
18207 case SQLITE_TESTCTRL_OPTIMIZATIONS:
 
18208 if( nArg==3 ){
18209 int opt = (int)strtol(azArg[2], 0, 0);
18210 rc2 = sqlite3_test_control(testctrl, p->db, opt);
18211 isOk = 3;
18212 }
@@ -18804,18 +18974,22 @@
18974 /*
18975 ** Output text to the console in a font that attracts extra attention.
18976 */
18977 #ifdef _WIN32
18978 static void printBold(const char *zText){
18979 #if !SQLITE_OS_WINRT
18980 HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE);
18981 CONSOLE_SCREEN_BUFFER_INFO defaultScreenInfo;
18982 GetConsoleScreenBufferInfo(out, &defaultScreenInfo);
18983 SetConsoleTextAttribute(out,
18984 FOREGROUND_RED|FOREGROUND_INTENSITY
18985 );
18986 #endif
18987 printf("%s", zText);
18988 #if !SQLITE_OS_WINRT
18989 SetConsoleTextAttribute(out, defaultScreenInfo.wAttributes);
18990 #endif
18991 }
18992 #else
18993 static void printBold(const char *zText){
18994 printf("\033[1m%s\033[0m", zText);
18995 }
@@ -18879,11 +19053,15 @@
19053 "attach debugger to process %d and press any key to continue.\n",
19054 GETPID());
19055 fgetc(stdin);
19056 }else{
19057 #if defined(_WIN32) || defined(WIN32)
19058 #if SQLITE_OS_WINRT
19059 __debugbreak();
19060 #else
19061 DebugBreak();
19062 #endif
19063 #elif defined(SIGTRAP)
19064 raise(SIGTRAP);
19065 #endif
19066 }
19067 }
19068
+2 -2
--- src/shun.c
+++ src/shun.c
@@ -187,11 +187,11 @@
187187
@ sight - set the "hidden" tag on such artifacts instead.</p>
188188
@
189189
@ <blockquote>
190190
@ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div>
191191
login_insert_csrf_secret();
192
- @ <textarea class="fullsize-text" cols="50" rows="%d(numRows)" name="uuid">
192
+ @ <textarea class="fullsize-text" cols="70" rows="%d(numRows)" name="uuid">
193193
if( zShun ){
194194
if( strlen(zShun) ){
195195
@ %h(zShun)
196196
}else if( nRcvid ){
197197
db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid);
@@ -214,11 +214,11 @@
214214
@ operations.</p>
215215
@
216216
@ <blockquote>
217217
@ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div>
218218
login_insert_csrf_secret();
219
- @ <textarea class="fullsize-text" cols="50" rows="%d(numRows)" name="uuid">
219
+ @ <textarea class="fullsize-text" cols="70" rows="%d(numRows)" name="uuid">
220220
if( zAccept ){
221221
if( strlen(zAccept) ){
222222
@ %h(zAccept)
223223
}else if( nRcvid ){
224224
db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid);
225225
--- src/shun.c
+++ src/shun.c
@@ -187,11 +187,11 @@
187 @ sight - set the "hidden" tag on such artifacts instead.</p>
188 @
189 @ <blockquote>
190 @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div>
191 login_insert_csrf_secret();
192 @ <textarea class="fullsize-text" cols="50" rows="%d(numRows)" name="uuid">
193 if( zShun ){
194 if( strlen(zShun) ){
195 @ %h(zShun)
196 }else if( nRcvid ){
197 db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid);
@@ -214,11 +214,11 @@
214 @ operations.</p>
215 @
216 @ <blockquote>
217 @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div>
218 login_insert_csrf_secret();
219 @ <textarea class="fullsize-text" cols="50" rows="%d(numRows)" name="uuid">
220 if( zAccept ){
221 if( strlen(zAccept) ){
222 @ %h(zAccept)
223 }else if( nRcvid ){
224 db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid);
225
--- src/shun.c
+++ src/shun.c
@@ -187,11 +187,11 @@
187 @ sight - set the "hidden" tag on such artifacts instead.</p>
188 @
189 @ <blockquote>
190 @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div>
191 login_insert_csrf_secret();
192 @ <textarea class="fullsize-text" cols="70" rows="%d(numRows)" name="uuid">
193 if( zShun ){
194 if( strlen(zShun) ){
195 @ %h(zShun)
196 }else if( nRcvid ){
197 db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid);
@@ -214,11 +214,11 @@
214 @ operations.</p>
215 @
216 @ <blockquote>
217 @ <form method="post" action="%s(g.zTop)/%s(g.zPath)"><div>
218 login_insert_csrf_secret();
219 @ <textarea class="fullsize-text" cols="70" rows="%d(numRows)" name="uuid">
220 if( zAccept ){
221 if( strlen(zAccept) ){
222 @ %h(zAccept)
223 }else if( nRcvid ){
224 db_prepare(&q, "SELECT uuid FROM blob WHERE rcvid=%d", nRcvid);
225
+55
--- src/skins.c
+++ src/skins.c
@@ -688,10 +688,62 @@
688688
}
689689
}
690690
return zResult;
691691
}
692692
693
+extern const struct strctCssDefaults {
694
+/* From the generated default_css.h, which we cannot #include here
695
+** without causing an ODR violation.
696
+*/
697
+ const char *elementClass; /* Name of element needed */
698
+ const char *value; /* CSS text */
699
+} cssDefaultList[];
700
+
701
+/*
702
+** Emits the list of built-in default CSS selectors. Intended
703
+** for use only from the /setup_skinedit page.
704
+*/
705
+static void skin_emit_css_defaults(){
706
+ struct strctCssDefaults const * pCss;
707
+ fossil_print("<h1>CSS Defaults</h1>");
708
+ fossil_print("If a skin defines any of the following CSS selectors, "
709
+ "that definition replaces the default, as opposed to "
710
+ "cascading from it. ");
711
+ fossil_print("See <a href=\"https://fossil-scm.org/fossil/"
712
+ "doc/trunk/www/css-tricks.md\">this "
713
+ "document</a> for more details.");
714
+ /* To discuss: do we want to list only the default selectors or
715
+ ** also their default values? The latter increases the size of the
716
+ ** page considerably, but is arguably more useful. We could, of
717
+ ** course, offer a URL param to toggle the view, but that currently
718
+ ** seems like overkill.
719
+ **
720
+ ** Be sure to adjust the default_css.txt #setup_skinedit_css entry
721
+ ** for whichever impl ends up being selected.
722
+ */
723
+#if 1
724
+ /* List impl which elides style values */
725
+ fossil_print("<div class=\"columns\" "
726
+ "id=\"setup_skinedit_css_defaults\"><ul>");
727
+ for(pCss = &cssDefaultList[0]; pCss->value!=0; ++pCss){
728
+ fossil_print("<li>%s</li>", pCss->elementClass);
729
+ }
730
+ fossil_print("</ul>");
731
+#else
732
+ /* Table impl which also includes style values. */
733
+ fossil_print("<table id=\"setup_skinedit_css_defaults\"><tbody>");
734
+ for(pCss = &cssDefaultList[0]; pCss->value!=0; ++pCss){
735
+ fossil_print("<tr><td>%s</td>", pCss->elementClass);
736
+ /* A TD element apparently cannot be told to scroll its contents,
737
+ ** so we require a DIV inside the value TD to scroll the long
738
+ ** url(data:...) entries. */
739
+ fossil_print("<td><div>%s</div></td>", pCss->value);
740
+ fossil_print("</td></tr>");
741
+ }
742
+ fossil_print("</tbody></table>");
743
+#endif
744
+}
693745
694746
/*
695747
** WEBPAGE: setup_skinedit
696748
**
697749
** Edit aspects of a skin determined by the w= query parameter.
@@ -814,10 +866,13 @@
814866
blob_reset(&from);
815867
blob_reset(&to);
816868
blob_reset(&out);
817869
}
818870
@ </div></form>
871
+ if(ii==0/*CSS*/){
872
+ skin_emit_css_defaults();
873
+ }
819874
style_footer();
820875
db_end_transaction(0);
821876
}
822877
823878
/*
824879
--- src/skins.c
+++ src/skins.c
@@ -688,10 +688,62 @@
688 }
689 }
690 return zResult;
691 }
692
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
693
694 /*
695 ** WEBPAGE: setup_skinedit
696 **
697 ** Edit aspects of a skin determined by the w= query parameter.
@@ -814,10 +866,13 @@
814 blob_reset(&from);
815 blob_reset(&to);
816 blob_reset(&out);
817 }
818 @ </div></form>
 
 
 
819 style_footer();
820 db_end_transaction(0);
821 }
822
823 /*
824
--- src/skins.c
+++ src/skins.c
@@ -688,10 +688,62 @@
688 }
689 }
690 return zResult;
691 }
692
693 extern const struct strctCssDefaults {
694 /* From the generated default_css.h, which we cannot #include here
695 ** without causing an ODR violation.
696 */
697 const char *elementClass; /* Name of element needed */
698 const char *value; /* CSS text */
699 } cssDefaultList[];
700
701 /*
702 ** Emits the list of built-in default CSS selectors. Intended
703 ** for use only from the /setup_skinedit page.
704 */
705 static void skin_emit_css_defaults(){
706 struct strctCssDefaults const * pCss;
707 fossil_print("<h1>CSS Defaults</h1>");
708 fossil_print("If a skin defines any of the following CSS selectors, "
709 "that definition replaces the default, as opposed to "
710 "cascading from it. ");
711 fossil_print("See <a href=\"https://fossil-scm.org/fossil/"
712 "doc/trunk/www/css-tricks.md\">this "
713 "document</a> for more details.");
714 /* To discuss: do we want to list only the default selectors or
715 ** also their default values? The latter increases the size of the
716 ** page considerably, but is arguably more useful. We could, of
717 ** course, offer a URL param to toggle the view, but that currently
718 ** seems like overkill.
719 **
720 ** Be sure to adjust the default_css.txt #setup_skinedit_css entry
721 ** for whichever impl ends up being selected.
722 */
723 #if 1
724 /* List impl which elides style values */
725 fossil_print("<div class=\"columns\" "
726 "id=\"setup_skinedit_css_defaults\"><ul>");
727 for(pCss = &cssDefaultList[0]; pCss->value!=0; ++pCss){
728 fossil_print("<li>%s</li>", pCss->elementClass);
729 }
730 fossil_print("</ul>");
731 #else
732 /* Table impl which also includes style values. */
733 fossil_print("<table id=\"setup_skinedit_css_defaults\"><tbody>");
734 for(pCss = &cssDefaultList[0]; pCss->value!=0; ++pCss){
735 fossil_print("<tr><td>%s</td>", pCss->elementClass);
736 /* A TD element apparently cannot be told to scroll its contents,
737 ** so we require a DIV inside the value TD to scroll the long
738 ** url(data:...) entries. */
739 fossil_print("<td><div>%s</div></td>", pCss->value);
740 fossil_print("</td></tr>");
741 }
742 fossil_print("</tbody></table>");
743 #endif
744 }
745
746 /*
747 ** WEBPAGE: setup_skinedit
748 **
749 ** Edit aspects of a skin determined by the w= query parameter.
@@ -814,10 +866,13 @@
866 blob_reset(&from);
867 blob_reset(&to);
868 blob_reset(&out);
869 }
870 @ </div></form>
871 if(ii==0/*CSS*/){
872 skin_emit_css_defaults();
873 }
874 style_footer();
875 db_end_transaction(0);
876 }
877
878 /*
879
+1673 -736
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -216,10 +216,13 @@
216216
"ENABLE_ATOMIC_WRITE",
217217
#endif
218218
#if SQLITE_ENABLE_BATCH_ATOMIC_WRITE
219219
"ENABLE_BATCH_ATOMIC_WRITE",
220220
#endif
221
+#if SQLITE_ENABLE_BYTECODE_VTAB
222
+ "ENABLE_BYTECODE_VTAB",
223
+#endif
221224
#if SQLITE_ENABLE_CEROD
222225
"ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD),
223226
#endif
224227
#if SQLITE_ENABLE_COLUMN_METADATA
225228
"ENABLE_COLUMN_METADATA",
@@ -534,13 +537,10 @@
534537
"OMIT_BETWEEN_OPTIMIZATION",
535538
#endif
536539
#if SQLITE_OMIT_BLOB_LITERAL
537540
"OMIT_BLOB_LITERAL",
538541
#endif
539
-#if SQLITE_OMIT_BTREECOUNT
540
- "OMIT_BTREECOUNT",
541
-#endif
542542
#if SQLITE_OMIT_CAST
543543
"OMIT_CAST",
544544
#endif
545545
#if SQLITE_OMIT_CHECK
546546
"OMIT_CHECK",
@@ -1162,11 +1162,11 @@
11621162
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
11631163
** [sqlite_version()] and [sqlite_source_id()].
11641164
*/
11651165
#define SQLITE_VERSION "3.32.0"
11661166
#define SQLITE_VERSION_NUMBER 3032000
1167
-#define SQLITE_SOURCE_ID "2020-03-21 23:10:38 5d14a1c4f2fc17de98ad685ad1422cdfda89dfccb00afcaf32ee416b6f84f525"
1167
+#define SQLITE_SOURCE_ID "2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f9654ff"
11681168
11691169
/*
11701170
** CAPI3REF: Run-Time Library Version Numbers
11711171
** KEYWORDS: sqlite3_version sqlite3_sourceid
11721172
**
@@ -1336,30 +1336,26 @@
13361336
** for the [sqlite3] object.
13371337
** ^Calls to sqlite3_close() and sqlite3_close_v2() return [SQLITE_OK] if
13381338
** the [sqlite3] object is successfully destroyed and all associated
13391339
** resources are deallocated.
13401340
**
1341
+** Ideally, applications should [sqlite3_finalize | finalize] all
1342
+** [prepared statements], [sqlite3_blob_close | close] all [BLOB handles], and
1343
+** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated
1344
+** with the [sqlite3] object prior to attempting to close the object.
13411345
** ^If the database connection is associated with unfinalized prepared
1342
-** statements or unfinished sqlite3_backup objects then sqlite3_close()
1343
-** will leave the database connection open and return [SQLITE_BUSY].
1344
-** ^If sqlite3_close_v2() is called with unfinalized prepared statements
1345
-** and/or unfinished sqlite3_backups, then the database connection becomes
1346
-** an unusable "zombie" which will automatically be deallocated when the
1347
-** last prepared statement is finalized or the last sqlite3_backup is
1348
-** finished. The sqlite3_close_v2() interface is intended for use with
1349
-** host languages that are garbage collected, and where the order in which
1350
-** destructors are called is arbitrary.
1351
-**
1352
-** Applications should [sqlite3_finalize | finalize] all [prepared statements],
1353
-** [sqlite3_blob_close | close] all [BLOB handles], and
1354
-** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated
1355
-** with the [sqlite3] object prior to attempting to close the object. ^If
1356
-** sqlite3_close_v2() is called on a [database connection] that still has
1357
-** outstanding [prepared statements], [BLOB handles], and/or
1358
-** [sqlite3_backup] objects then it returns [SQLITE_OK] and the deallocation
1359
-** of resources is deferred until all [prepared statements], [BLOB handles],
1360
-** and [sqlite3_backup] objects are also destroyed.
1346
+** statements, BLOB handlers, and/or unfinished sqlite3_backup objects then
1347
+** sqlite3_close() will leave the database connection open and return
1348
+** [SQLITE_BUSY]. ^If sqlite3_close_v2() is called with unfinalized prepared
1349
+** statements, unclosed BLOB handlers, and/or unfinished sqlite3_backups,
1350
+** it returns [SQLITE_OK] regardless, but instead of deallocating the database
1351
+** connection immediately, it marks the database connection as an unusable
1352
+** "zombie" and makes arrangements to automatically deallocate the database
1353
+** connection after all prepared statements are finalized, all BLOB handles
1354
+** are closed, and all backups have finished. The sqlite3_close_v2() interface
1355
+** is intended for use with host languages that are garbage collected, and
1356
+** where the order in which destructors are called is arbitrary.
13611357
**
13621358
** ^If an [sqlite3] object is destroyed while a transaction is open,
13631359
** the transaction is automatically rolled back.
13641360
**
13651361
** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)]
@@ -1544,22 +1540,25 @@
15441540
#define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8))
15451541
#define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
15461542
#define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8))
15471543
#define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
15481544
#define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
1545
+#define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
15491546
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
15501547
#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
15511548
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
15521549
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
1550
+#define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8))
15531551
#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
15541552
#define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8))
15551553
#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8))
15561554
#define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8))
15571555
#define SQLITE_CANTOPEN_DIRTYWAL (SQLITE_CANTOPEN | (5<<8)) /* Not Used */
15581556
#define SQLITE_CANTOPEN_SYMLINK (SQLITE_CANTOPEN | (6<<8))
15591557
#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
15601558
#define SQLITE_CORRUPT_SEQUENCE (SQLITE_CORRUPT | (2<<8))
1559
+#define SQLITE_CORRUPT_INDEX (SQLITE_CORRUPT | (3<<8))
15611560
#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
15621561
#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
15631562
#define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8))
15641563
#define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4<<8))
15651564
#define SQLITE_READONLY_CANTINIT (SQLITE_READONLY | (5<<8))
@@ -2124,14 +2123,16 @@
21242123
** so that all subsequent write operations are independent.
21252124
** ^SQLite will never invoke SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE without
21262125
** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE].
21272126
**
21282127
** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]]
2129
-** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode causes attempts to obtain
2130
-** a file lock using the xLock or xShmLock methods of the VFS to wait
2131
-** for up to M milliseconds before failing, where M is the single
2132
-** unsigned integer parameter.
2128
+** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode is used to configure a VFS
2129
+** to block for up to M milliseconds before failing when attempting to
2130
+** obtain a file lock using the xLock or xShmLock methods of the VFS.
2131
+** The parameter is a pointer to a 32-bit signed integer that contains
2132
+** the value that M is to be set to. Before returning, the 32-bit signed
2133
+** integer is overwritten with the previous value of M.
21332134
**
21342135
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
21352136
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
21362137
** a database file. The argument is a pointer to a 32-bit unsigned integer.
21372138
** The "data version" for the pager is written into the pointer. The
@@ -2148,10 +2149,15 @@
21482149
** a single attached database that occur due to other database connections,
21492150
** but omits changes implemented by the database connection on which it is
21502151
** called. This file control is the only mechanism to detect changes that
21512152
** happen either internally or externally and that are associated with
21522153
** a particular attached database.
2154
+**
2155
+** <li>[[SQLITE_FCNTL_CKPT_START]]
2156
+** The [SQLITE_FCNTL_CKPT_START] opcode is invoked from within a checkpoint
2157
+** in wal mode before the client starts to copy pages from the wal
2158
+** file to the database file.
21532159
**
21542160
** <li>[[SQLITE_FCNTL_CKPT_DONE]]
21552161
** The [SQLITE_FCNTL_CKPT_DONE] opcode is invoked from within a checkpoint
21562162
** in wal mode after the client has finished copying pages from the wal
21572163
** file to the database file, but before the *-shm file is updated to
@@ -2192,10 +2198,12 @@
21922198
#define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33
21932199
#define SQLITE_FCNTL_LOCK_TIMEOUT 34
21942200
#define SQLITE_FCNTL_DATA_VERSION 35
21952201
#define SQLITE_FCNTL_SIZE_LIMIT 36
21962202
#define SQLITE_FCNTL_CKPT_DONE 37
2203
+#define SQLITE_FCNTL_RESERVE_BYTES 38
2204
+#define SQLITE_FCNTL_CKPT_START 39
21972205
21982206
/* deprecated names */
21992207
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
22002208
#define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE
22012209
#define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO
@@ -4570,12 +4578,23 @@
45704578
**
45714579
** These are utility routines, useful to [VFS|custom VFS implementations],
45724580
** that check if a database file was a URI that contained a specific query
45734581
** parameter, and if so obtains the value of that query parameter.
45744582
**
4575
-** If F is the database filename pointer passed into the xOpen() method of
4576
-** a VFS implementation or it is the return value of [sqlite3_db_filename()]
4583
+** The first parameter to these interfaces (hereafter referred to
4584
+** as F) must be one of:
4585
+** <ul>
4586
+** <li> A database filename pointer created by the SQLite core and
4587
+** passed into the xOpen() method of a VFS implemention, or
4588
+** <li> A filename obtained from [sqlite3_db_filename()], or
4589
+** <li> A new filename constructed using [sqlite3_create_filename()].
4590
+** </ul>
4591
+** If the F parameter is not one of the above, then the behavior is
4592
+** undefined and probably undesirable. Older versions of SQLite were
4593
+** more tolerant of invalid F parameters than newer versions.
4594
+**
4595
+** If F is a suitable filename (as described in the previous paragraph)
45774596
** and if P is the name of the query parameter, then
45784597
** sqlite3_uri_parameter(F,P) returns the value of the P
45794598
** parameter if it exists or a NULL pointer if P does not appear as a
45804599
** query parameter on F. If P is a query parameter of F and it
45814600
** has no explicit value, then sqlite3_uri_parameter(F,P) returns
@@ -4654,10 +4673,29 @@
46544673
*/
46554674
SQLITE_API const char *sqlite3_filename_database(const char*);
46564675
SQLITE_API const char *sqlite3_filename_journal(const char*);
46574676
SQLITE_API const char *sqlite3_filename_wal(const char*);
46584677
4678
+/*
4679
+** CAPI3REF: Database File Corresponding To A Journal
4680
+**
4681
+** ^If X is the name of a rollback or WAL-mode journal file that is
4682
+** passed into the xOpen method of [sqlite3_vfs], then
4683
+** sqlite3_database_file_object(X) returns a pointer to the [sqlite3_file]
4684
+** object that represents the main database file.
4685
+**
4686
+** This routine is intended for use in custom [VFS] implementations
4687
+** only. It is not a general-purpose interface.
4688
+** The argument sqlite3_file_object(X) must be a filename pointer that
4689
+** has been passed into [sqlite3_vfs].xOpen method where the
4690
+** flags parameter to xOpen contains one of the bits
4691
+** [SQLITE_OPEN_MAIN_JOURNAL] or [SQLITE_OPEN_WAL]. Any other use
4692
+** of this routine results in undefined and probably undesirable
4693
+** behavior.
4694
+*/
4695
+SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*);
4696
+
46594697
/*
46604698
** CAPI3REF: Create and Destroy VFS Filenames
46614699
**
46624700
** These interfces are provided for use by [VFS shim] implementations and
46634701
** are not useful outside of that context.
@@ -4688,11 +4726,11 @@
46884726
** None of the D, J, or W parameters to sqlite3_create_filename(D,J,W,N,P) may
46894727
** be NULL pointers, though they can be empty strings.
46904728
**
46914729
** The sqlite3_free_filename(Y) routine releases a memory allocation
46924730
** previously obtained from sqlite3_create_filename(). Invoking
4693
-** sqlite3_free_filename(Y) is a NULL pointer is a harmless no-op.
4731
+** sqlite3_free_filename(Y) where Y is a NULL pointer is a harmless no-op.
46944732
**
46954733
** If the Y parameter to sqlite3_free_filename(Y) is anything other
46964734
** than a NULL pointer or a pointer previously acquired from
46974735
** sqlite3_create_filename(), then bad things such as heap
46984736
** corruption or segfaults may occur. The value Y should be
@@ -5295,10 +5333,28 @@
52955333
**
52965334
** ^The third argument is the value to bind to the parameter.
52975335
** ^If the third parameter to sqlite3_bind_text() or sqlite3_bind_text16()
52985336
** or sqlite3_bind_blob() is a NULL pointer then the fourth parameter
52995337
** is ignored and the end result is the same as sqlite3_bind_null().
5338
+** ^If the third parameter to sqlite3_bind_text() is not NULL, then
5339
+** it should be a pointer to well-formed UTF8 text.
5340
+** ^If the third parameter to sqlite3_bind_text16() is not NULL, then
5341
+** it should be a pointer to well-formed UTF16 text.
5342
+** ^If the third parameter to sqlite3_bind_text64() is not NULL, then
5343
+** it should be a pointer to a well-formed unicode string that is
5344
+** either UTF8 if the sixth parameter is SQLITE_UTF8, or UTF16
5345
+** otherwise.
5346
+**
5347
+** [[byte-order determination rules]] ^The byte-order of
5348
+** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF)
5349
+** found in first character, which is removed, or in the absence of a BOM
5350
+** the byte order is the native byte order of the host
5351
+** machine for sqlite3_bind_text16() or the byte order specified in
5352
+** the 6th parameter for sqlite3_bind_text64().)^
5353
+** ^If UTF16 input text contains invalid unicode
5354
+** characters, then SQLite might change those invalid characters
5355
+** into the unicode replacement character: U+FFFD.
53005356
**
53015357
** ^(In those routines that have a fourth argument, its value is the
53025358
** number of bytes in the parameter. To be clear: the value is the
53035359
** number of <u>bytes</u> in the value, not the number of characters.)^
53045360
** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16()
@@ -5308,11 +5364,11 @@
53085364
** the behavior is undefined.
53095365
** If a non-negative fourth parameter is provided to sqlite3_bind_text()
53105366
** or sqlite3_bind_text16() or sqlite3_bind_text64() then
53115367
** that parameter must be the byte offset
53125368
** where the NUL terminator would occur assuming the string were NUL
5313
-** terminated. If any NUL characters occur at byte offsets less than
5369
+** terminated. If any NUL characters occurs at byte offsets less than
53145370
** the value of the fourth parameter then the resulting string value will
53155371
** contain embedded NULs. The result of expressions involving strings
53165372
** with embedded NULs is undefined.
53175373
**
53185374
** ^The fifth argument to the BLOB and string binding interfaces
@@ -6633,12 +6689,13 @@
66336689
** cause the implemented SQL function to throw an exception.
66346690
** ^SQLite uses the string pointed to by the
66356691
** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16()
66366692
** as the text of an error message. ^SQLite interprets the error
66376693
** message string from sqlite3_result_error() as UTF-8. ^SQLite
6638
-** interprets the string from sqlite3_result_error16() as UTF-16 in native
6639
-** byte order. ^If the third parameter to sqlite3_result_error()
6694
+** interprets the string from sqlite3_result_error16() as UTF-16 using
6695
+** the same [byte-order determination rules] as [sqlite3_bind_text16()].
6696
+** ^If the third parameter to sqlite3_result_error()
66406697
** or sqlite3_result_error16() is negative then SQLite takes as the error
66416698
** message all text up through the first zero character.
66426699
** ^If the third parameter to sqlite3_result_error() or
66436700
** sqlite3_result_error16() is non-negative then SQLite takes that many
66446701
** bytes (not characters) from the 2nd parameter as the error message.
@@ -6701,10 +6758,29 @@
67016758
** when it has finished using that result.
67026759
** ^If the 4th parameter to the sqlite3_result_text* interfaces
67036760
** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT
67046761
** then SQLite makes a copy of the result into space obtained
67056762
** from [sqlite3_malloc()] before it returns.
6763
+**
6764
+** ^For the sqlite3_result_text16(), sqlite3_result_text16le(), and
6765
+** sqlite3_result_text16be() routines, and for sqlite3_result_text64()
6766
+** when the encoding is not UTF8, if the input UTF16 begins with a
6767
+** byte-order mark (BOM, U+FEFF) then the BOM is removed from the
6768
+** string and the rest of the string is interpreted according to the
6769
+** byte-order specified by the BOM. ^The byte-order specified by
6770
+** the BOM at the beginning of the text overrides the byte-order
6771
+** specified by the interface procedure. ^So, for example, if
6772
+** sqlite3_result_text16le() is invoked with text that begins
6773
+** with bytes 0xfe, 0xff (a big-endian byte-order mark) then the
6774
+** first two bytes of input are skipped and the remaining input
6775
+** is interpreted as UTF16BE text.
6776
+**
6777
+** ^For UTF16 input text to the sqlite3_result_text16(),
6778
+** sqlite3_result_text16be(), sqlite3_result_text16le(), and
6779
+** sqlite3_result_text64() routines, if the text contains invalid
6780
+** UTF16 characters, the invalid characters might be converted
6781
+** into the unicode replacement character, U+FFFD.
67066782
**
67076783
** ^The sqlite3_result_value() interface sets the result of
67086784
** the application-defined function to be a copy of the
67096785
** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The
67106786
** sqlite3_result_value() interface makes a copy of the [sqlite3_value]
@@ -8649,11 +8725,11 @@
86498725
#define SQLITE_TESTCTRL_FAULT_INSTALL 9
86508726
#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
86518727
#define SQLITE_TESTCTRL_PENDING_BYTE 11
86528728
#define SQLITE_TESTCTRL_ASSERT 12
86538729
#define SQLITE_TESTCTRL_ALWAYS 13
8654
-#define SQLITE_TESTCTRL_RESERVE 14
8730
+#define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */
86558731
#define SQLITE_TESTCTRL_OPTIMIZATIONS 15
86568732
#define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */
86578733
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */
86588734
#define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17
86598735
#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
@@ -13369,10 +13445,25 @@
1336913445
#pragma warn -aus /* Assigned value is never used */
1337013446
#pragma warn -csu /* Comparing signed and unsigned */
1337113447
#pragma warn -spa /* Suspicious pointer arithmetic */
1337213448
#endif
1337313449
13450
+/*
13451
+** WAL mode depends on atomic aligned 32-bit loads and stores in a few
13452
+** places. The following macros try to make this explicit.
13453
+*/
13454
+#ifndef __has_feature
13455
+# define __has_feature(x) 0 /* compatibility with non-clang compilers */
13456
+#endif
13457
+#if GCC_VERSION>=4007000 || __has_feature(c_atomic)
13458
+# define AtomicLoad(PTR) __atomic_load_n((PTR),__ATOMIC_RELAXED)
13459
+# define AtomicStore(PTR,VAL) __atomic_store_n((PTR),(VAL),__ATOMIC_RELAXED)
13460
+#else
13461
+# define AtomicLoad(PTR) (*(PTR))
13462
+# define AtomicStore(PTR,VAL) (*(PTR) = (VAL))
13463
+#endif
13464
+
1337413465
/*
1337513466
** Include standard header files as necessary
1337613467
*/
1337713468
#ifdef HAVE_STDINT_H
1337813469
#include <stdint.h>
@@ -14446,11 +14537,10 @@
1444614537
typedef struct BusyHandler BusyHandler;
1444714538
struct BusyHandler {
1444814539
int (*xBusyHandler)(void *,int); /* The busy callback */
1444914540
void *pBusyArg; /* First arg to busy callback */
1445014541
int nBusy; /* Incremented with each busy call */
14451
- u8 bExtraFileArg; /* Include sqlite3_file as callback arg */
1445214542
};
1445314543
1445414544
/*
1445514545
** Name of the master database table. The master database table
1445614546
** is a special table that holds the names and attributes of all
@@ -14704,11 +14794,11 @@
1470414794
SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix);
1470514795
SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*);
1470614796
SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int);
1470714797
SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree*);
1470814798
SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree*,int);
14709
-SQLITE_PRIVATE int sqlite3BtreeGetOptimalReserve(Btree*);
14799
+SQLITE_PRIVATE int sqlite3BtreeGetRequestedReserve(Btree*);
1471014800
SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p);
1471114801
SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int);
1471214802
SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *);
1471314803
SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int,int*);
1471414804
SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
@@ -14966,13 +15056,11 @@
1496615056
#ifndef NDEBUG
1496715057
SQLITE_PRIVATE int sqlite3BtreeCursorIsValid(BtCursor*);
1496815058
#endif
1496915059
SQLITE_PRIVATE int sqlite3BtreeCursorIsValidNN(BtCursor*);
1497015060
14971
-#ifndef SQLITE_OMIT_BTREECOUNT
1497215061
SQLITE_PRIVATE int sqlite3BtreeCount(sqlite3*, BtCursor*, i64*);
14973
-#endif
1497415062
1497515063
#ifdef SQLITE_TEST
1497615064
SQLITE_PRIVATE int sqlite3BtreeCursorInfo(BtCursor*, int*, int);
1497715065
SQLITE_PRIVATE void sqlite3BtreeCursorList(Btree*);
1497815066
#endif
@@ -15543,10 +15631,13 @@
1554315631
1554415632
SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *);
1554515633
SQLITE_PRIVATE int sqlite3VdbeHasSubProgram(Vdbe*);
1554615634
1554715635
SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*);
15636
+#ifdef SQLITE_ENABLE_BYTECODE_VTAB
15637
+SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*);
15638
+#endif
1554815639
1554915640
/* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on
1555015641
** each VDBE opcode.
1555115642
**
1555215643
** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op
@@ -15828,17 +15919,25 @@
1582815919
SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager);
1582915920
SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager);
1583015921
SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
1583115922
SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3*);
1583215923
# ifdef SQLITE_ENABLE_SNAPSHOT
15833
-SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot);
15834
-SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot);
15924
+SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager*, sqlite3_snapshot **ppSnapshot);
15925
+SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager*, sqlite3_snapshot *pSnapshot);
1583515926
SQLITE_PRIVATE int sqlite3PagerSnapshotRecover(Pager *pPager);
1583615927
SQLITE_PRIVATE int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot);
1583715928
SQLITE_PRIVATE void sqlite3PagerSnapshotUnlock(Pager *pPager);
1583815929
# endif
1583915930
#endif
15931
+
15932
+#if !defined(SQLITE_OMIT_WAL) && defined(SQLITE_ENABLE_SETLK_TIMEOUT)
15933
+SQLITE_PRIVATE int sqlite3PagerWalWriteLock(Pager*, int);
15934
+SQLITE_PRIVATE void sqlite3PagerWalDb(Pager*, sqlite3*);
15935
+#else
15936
+# define sqlite3PagerWalWriteLock(y,z) SQLITE_OK
15937
+# define sqlite3PagerWalDb(x,y)
15938
+#endif
1584015939
1584115940
#ifdef SQLITE_DIRECT_OVERFLOW_READ
1584215941
SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno);
1584315942
#endif
1584415943
@@ -15861,15 +15960,10 @@
1586115960
SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*);
1586215961
SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*);
1586315962
SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *);
1586415963
SQLITE_PRIVATE void sqlite3PagerClearCache(Pager*);
1586515964
SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *);
15866
-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
15867
-SQLITE_PRIVATE void sqlite3PagerResetLockTimeout(Pager *pPager);
15868
-#else
15869
-# define sqlite3PagerResetLockTimeout(X)
15870
-#endif
1587115965
1587215966
/* Functions used to truncate the database file. */
1587315967
SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno);
1587415968
1587515969
SQLITE_PRIVATE void sqlite3PagerRekey(DbPage*, Pgno, u16);
@@ -16799,10 +16893,11 @@
1679916893
Hash aFunc; /* Hash table of connection functions */
1680016894
Hash aCollSeq; /* All collating sequences */
1680116895
BusyHandler busyHandler; /* Busy callback */
1680216896
Db aDbStatic[2]; /* Static space for the 2 default backends */
1680316897
Savepoint *pSavepoint; /* List of active savepoints */
16898
+ int nAnalysisLimit; /* Number of index rows to ANALYZE */
1680416899
int busyTimeout; /* Busy handler timeout, in msec */
1680516900
int nSavepoint; /* Number of non-transaction savepoints */
1680616901
int nStatement; /* Number of nested statement-transactions */
1680716902
i64 nDeferredCons; /* Net deferred constraints this transaction. */
1680816903
i64 nDeferredImmCons; /* Net deferred immediate constraints */
@@ -17209,10 +17304,11 @@
1720917304
Expr *pDflt; /* Default value or GENERATED ALWAYS AS value */
1721017305
char *zColl; /* Collating sequence. If NULL, use the default */
1721117306
u8 notNull; /* An OE_ code for handling a NOT NULL constraint */
1721217307
char affinity; /* One of the SQLITE_AFF_... values */
1721317308
u8 szEst; /* Estimated size of value in this column. sizeof(INT)==1 */
17309
+ u8 hName; /* Column name hash for faster lookup */
1721417310
u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */
1721517311
};
1721617312
1721717313
/* Allowed values for Column.colFlags:
1721817314
*/
@@ -19827,10 +19923,11 @@
1982719923
const char*,
1982819924
const char*,
1982919925
const char*
1983019926
);
1983119927
SQLITE_PRIVATE Bitmask sqlite3ExprColUsed(Expr*);
19928
+SQLITE_PRIVATE u8 sqlite3StrIHash(const char*);
1983219929
SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*);
1983319930
SQLITE_PRIVATE int sqlite3ResolveExprListNames(NameContext*, ExprList*);
1983419931
SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*);
1983519932
SQLITE_PRIVATE int sqlite3ResolveSelfReference(Parse*,Table*,int,Expr*,ExprList*);
1983619933
SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*);
@@ -19842,11 +19939,11 @@
1984219939
SQLITE_PRIVATE void sqlite3RenameExprUnmap(Parse*, Expr*);
1984319940
SQLITE_PRIVATE void sqlite3RenameExprlistUnmap(Parse*, ExprList*);
1984419941
SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*);
1984519942
SQLITE_PRIVATE char sqlite3AffinityType(const char*, Column*);
1984619943
SQLITE_PRIVATE void sqlite3Analyze(Parse*, Token*, Token*);
19847
-SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler*, sqlite3_file*);
19944
+SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler*);
1984819945
SQLITE_PRIVATE int sqlite3FindDb(sqlite3*, Token*);
1984919946
SQLITE_PRIVATE int sqlite3FindDbName(sqlite3 *, const char *);
1985019947
SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3*,int iDB);
1985119948
SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3*,Index*);
1985219949
SQLITE_PRIVATE void sqlite3DefaultRowEst(Index*);
@@ -20578,11 +20675,12 @@
2057820675
/*
2057920676
** VDBE_DISPLAY_P4 is true or false depending on whether or not the
2058020677
** "explain" P4 display logic is enabled.
2058120678
*/
2058220679
#if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \
20583
- || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
20680
+ || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) \
20681
+ || defined(SQLITE_ENABLE_BYTECODE_VTAB)
2058420682
# define VDBE_DISPLAY_P4 1
2058520683
#else
2058620684
# define VDBE_DISPLAY_P4 0
2058720685
#endif
2058820686
@@ -21043,11 +21141,18 @@
2104321141
2104421142
int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *);
2104521143
SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(sqlite3*,VdbeCursor*,UnpackedRecord*,int*);
2104621144
SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3*, BtCursor*, i64*);
2104721145
SQLITE_PRIVATE int sqlite3VdbeExec(Vdbe*);
21048
-#ifndef SQLITE_OMIT_EXPLAIN
21146
+#if !defined(SQLITE_OMIT_EXPLAIN) || defined(SQLITE_ENABLE_BYTECODE_VTAB)
21147
+SQLITE_PRIVATE int sqlite3VdbeNextOpcode(Vdbe*,Mem*,int,int*,int*,Op**);
21148
+SQLITE_PRIVATE char *sqlite3VdbeDisplayP4(sqlite3*,Op*);
21149
+#endif
21150
+#if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS)
21151
+SQLITE_PRIVATE char *sqlite3VdbeDisplayComment(sqlite3*,const Op*,const char*);
21152
+#endif
21153
+#if !defined(SQLITE_OMIT_EXPLAIN)
2104921154
SQLITE_PRIVATE int sqlite3VdbeList(Vdbe*);
2105021155
#endif
2105121156
SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe*);
2105221157
SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *, int);
2105321158
SQLITE_PRIVATE int sqlite3VdbeMemTooBig(Mem*);
@@ -21085,11 +21190,11 @@
2108521190
SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p);
2108621191
SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
2108721192
#ifndef SQLITE_OMIT_WINDOWFUNC
2108821193
SQLITE_PRIVATE int sqlite3VdbeMemAggValue(Mem*, Mem*, FuncDef*);
2108921194
#endif
21090
-#ifndef SQLITE_OMIT_EXPLAIN
21195
+#if !defined(SQLITE_OMIT_EXPLAIN) || defined(SQLITE_ENABLE_BYTECODE_VTAB)
2109121196
SQLITE_PRIVATE const char *sqlite3OpcodeName(int);
2109221197
#endif
2109321198
SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
2109421199
SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int n);
2109521200
SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int);
@@ -22165,16 +22270,16 @@
2216522270
u8 nName; /* Length of th name */
2216622271
char *zName; /* Name of the transformation */
2216722272
double rLimit; /* Maximum NNN value for this transform */
2216822273
double rXform; /* Constant used for this transform */
2216922274
} aXformType[] = {
22170
- { 0, 6, "second", 464269060800.0, 86400000.0/(24.0*60.0*60.0) },
22171
- { 0, 6, "minute", 7737817680.0, 86400000.0/(24.0*60.0) },
22172
- { 0, 4, "hour", 128963628.0, 86400000.0/24.0 },
22173
- { 0, 3, "day", 5373485.0, 86400000.0 },
22174
- { 1, 5, "month", 176546.0, 30.0*86400000.0 },
22175
- { 2, 4, "year", 14713.0, 365.0*86400000.0 },
22275
+ { 0, 6, "second", 464269060800.0, 1000.0 },
22276
+ { 0, 6, "minute", 7737817680.0, 60000.0 },
22277
+ { 0, 4, "hour", 128963628.0, 3600000.0 },
22278
+ { 0, 3, "day", 5373485.0, 86400000.0 },
22279
+ { 1, 5, "month", 176546.0, 2592000000.0 },
22280
+ { 2, 4, "year", 14713.0, 31536000000.0 },
2217622281
};
2217722282
2217822283
/*
2217922284
** Process a modifier to a date-time stamp. The modifiers are
2218022285
** as follows:
@@ -27258,11 +27363,11 @@
2725827363
if( mem0.hardLimit>0 && (n>mem0.hardLimit || n==0) ){
2725927364
n = mem0.hardLimit;
2726027365
}
2726127366
mem0.alarmThreshold = n;
2726227367
nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
27263
- mem0.nearlyFull = (n>0 && n<=nUsed);
27368
+ AtomicStore(&mem0.nearlyFull, n>0 && n<=nUsed);
2726427369
sqlite3_mutex_leave(mem0.mutex);
2726527370
excess = sqlite3_memory_used() - n;
2726627371
if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff));
2726727372
return priorLimit;
2726827373
}
@@ -27326,11 +27431,11 @@
2732627431
** Return true if the heap is currently under memory pressure - in other
2732727432
** words if the amount of heap used is close to the limit set by
2732827433
** sqlite3_soft_heap_limit().
2732927434
*/
2733027435
SQLITE_PRIVATE int sqlite3HeapNearlyFull(void){
27331
- return mem0.nearlyFull;
27436
+ return AtomicLoad(&mem0.nearlyFull);
2733227437
}
2733327438
2733427439
/*
2733527440
** Deinitialize the memory allocation subsystem.
2733627441
*/
@@ -27390,21 +27495,21 @@
2739027495
2739127496
sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, n);
2739227497
if( mem0.alarmThreshold>0 ){
2739327498
sqlite3_int64 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
2739427499
if( nUsed >= mem0.alarmThreshold - nFull ){
27395
- mem0.nearlyFull = 1;
27500
+ AtomicStore(&mem0.nearlyFull, 1);
2739627501
sqlite3MallocAlarm(nFull);
2739727502
if( mem0.hardLimit ){
2739827503
nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
2739927504
if( nUsed >= mem0.hardLimit - nFull ){
2740027505
*pp = 0;
2740127506
return;
2740227507
}
2740327508
}
2740427509
}else{
27405
- mem0.nearlyFull = 0;
27510
+ AtomicStore(&mem0.nearlyFull, 0);
2740627511
}
2740727512
}
2740827513
p = sqlite3GlobalConfig.m.xMalloc(nFull);
2740927514
#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
2741027515
if( p==0 && mem0.alarmThreshold>0 ){
@@ -27629,14 +27734,16 @@
2762927734
if( nDiff>0 && sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >=
2763027735
mem0.alarmThreshold-nDiff ){
2763127736
sqlite3MallocAlarm(nDiff);
2763227737
}
2763327738
pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
27739
+#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
2763427740
if( pNew==0 && mem0.alarmThreshold>0 ){
2763527741
sqlite3MallocAlarm((int)nBytes);
2763627742
pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
2763727743
}
27744
+#endif
2763827745
if( pNew ){
2763927746
nNew = sqlite3MallocSize(pNew);
2764027747
sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nNew-nOld);
2764127748
}
2764227749
sqlite3_mutex_leave(mem0.mutex);
@@ -27907,11 +28014,11 @@
2790728014
*/
2790828015
SQLITE_PRIVATE void sqlite3OomFault(sqlite3 *db){
2790928016
if( db->mallocFailed==0 && db->bBenignMalloc==0 ){
2791028017
db->mallocFailed = 1;
2791128018
if( db->nVdbeExec>0 ){
27912
- db->u1.isInterrupted = 1;
28019
+ AtomicStore(&db->u1.isInterrupted, 1);
2791328020
}
2791428021
DisableLookaside;
2791528022
if( db->pParse ){
2791628023
db->pParse->rc = SQLITE_NOMEM_BKPT;
2791728024
}
@@ -27926,11 +28033,11 @@
2792628033
** VDBEs.
2792728034
*/
2792828035
SQLITE_PRIVATE void sqlite3OomClear(sqlite3 *db){
2792928036
if( db->mallocFailed && db->nVdbeExec==0 ){
2793028037
db->mallocFailed = 0;
27931
- db->u1.isInterrupted = 0;
28038
+ AtomicStore(&db->u1.isInterrupted, 0);
2793228039
assert( db->lookaside.bDisable>0 );
2793328040
EnableLookaside;
2793428041
}
2793528042
}
2793628043
@@ -31300,10 +31407,23 @@
3130031407
a = (unsigned char *)zLeft;
3130131408
b = (unsigned char *)zRight;
3130231409
while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; }
3130331410
return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b];
3130431411
}
31412
+
31413
+/*
31414
+** Compute an 8-bit hash on a string that is insensitive to case differences
31415
+*/
31416
+SQLITE_PRIVATE u8 sqlite3StrIHash(const char *z){
31417
+ u8 h = 0;
31418
+ if( z==0 ) return 0;
31419
+ while( z[0] ){
31420
+ h += UpperToLower[(unsigned char)z[0]];
31421
+ z++;
31422
+ }
31423
+ return h;
31424
+}
3130531425
3130631426
/*
3130731427
** Compute 10 to the E-th power. Examples: E==1 results in 10.
3130831428
** E==2 results in 100. E==50 results in 1.0e50.
3130931429
**
@@ -34857,20 +34977,21 @@
3485734977
static int osSetPosixAdvisoryLock(
3485834978
int h, /* The file descriptor on which to take the lock */
3485934979
struct flock *pLock, /* The description of the lock */
3486034980
unixFile *pFile /* Structure holding timeout value */
3486134981
){
34982
+ int tm = pFile->iBusyTimeout;
3486234983
int rc = osFcntl(h,F_SETLK,pLock);
34863
- while( rc<0 && pFile->iBusyTimeout>0 ){
34984
+ while( rc<0 && tm>0 ){
3486434985
/* On systems that support some kind of blocking file lock with a timeout,
3486534986
** make appropriate changes here to invoke that blocking file lock. On
3486634987
** generic posix, however, there is no such API. So we simply try the
3486734988
** lock once every millisecond until either the timeout expires, or until
3486834989
** the lock is obtained. */
3486934990
usleep(1000);
3487034991
rc = osFcntl(h,F_SETLK,pLock);
34871
- pFile->iBusyTimeout--;
34992
+ tm--;
3487234993
}
3487334994
return rc;
3487434995
}
3487534996
#endif /* SQLITE_ENABLE_SETLK_TIMEOUT */
3487634997
@@ -36977,11 +37098,11 @@
3697737098
zDirname[ii] = '\0';
3697837099
}else{
3697937100
if( zDirname[0]!='/' ) zDirname[0] = '.';
3698037101
zDirname[1] = 0;
3698137102
}
36982
- fd = robust_open(zDirname, O_RDONLY|O_BINARY|O_NOFOLLOW, 0);
37103
+ fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0);
3698337104
if( fd>=0 ){
3698437105
OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname));
3698537106
}
3698637107
*pFd = fd;
3698737108
if( fd>=0 ) return SQLITE_OK;
@@ -37287,11 +37408,13 @@
3728737408
*(int*)pArg = fileHasMoved(pFile);
3728837409
return SQLITE_OK;
3728937410
}
3729037411
#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
3729137412
case SQLITE_FCNTL_LOCK_TIMEOUT: {
37413
+ int iOld = pFile->iBusyTimeout;
3729237414
pFile->iBusyTimeout = *(int*)pArg;
37415
+ *(int*)pArg = iOld;
3729337416
return SQLITE_OK;
3729437417
}
3729537418
#endif
3729637419
#if SQLITE_MAX_MMAP_SIZE>0
3729737420
case SQLITE_FCNTL_MMAP_SIZE: {
@@ -37606,17 +37729,24 @@
3760637729
3760737730
/* Locks are within range */
3760837731
assert( n>=1 && n<=SQLITE_SHM_NLOCK );
3760937732
3761037733
if( pShmNode->hShm>=0 ){
37734
+ int res;
3761137735
/* Initialize the locking parameters */
3761237736
f.l_type = lockType;
3761337737
f.l_whence = SEEK_SET;
3761437738
f.l_start = ofst;
3761537739
f.l_len = n;
37616
- rc = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile);
37617
- rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY;
37740
+ res = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile);
37741
+ if( res==-1 ){
37742
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
37743
+ rc = (pFile->iBusyTimeout ? SQLITE_BUSY_TIMEOUT : SQLITE_BUSY);
37744
+#else
37745
+ rc = SQLITE_BUSY;
37746
+#endif
37747
+ }
3761837748
}
3761937749
3762037750
/* Update the global lock state and do debug tracing */
3762137751
#ifdef SQLITE_DEBUG
3762237752
{ u16 mask;
@@ -38108,10 +38238,29 @@
3810838238
|| flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED)
3810938239
|| flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
3811038240
assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 );
3811138241
assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 );
3811238242
assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 );
38243
+
38244
+ /* Check that, if this to be a blocking lock, no locks that occur later
38245
+ ** in the following list than the lock being obtained are already held:
38246
+ **
38247
+ ** 1. Checkpointer lock (ofst==1).
38248
+ ** 2. Write lock (ofst==0).
38249
+ ** 3. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK).
38250
+ **
38251
+ ** In other words, if this is a blocking lock, none of the locks that
38252
+ ** occur later in the above list than the lock being obtained may be
38253
+ ** held. */
38254
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
38255
+ assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
38256
+ (ofst!=2) /* not RECOVER */
38257
+ && (ofst!=1 || (p->exclMask|p->sharedMask)==0)
38258
+ && (ofst!=0 || (p->exclMask|p->sharedMask)<3)
38259
+ && (ofst<3 || (p->exclMask|p->sharedMask)<(1<<ofst))
38260
+ ));
38261
+#endif
3811338262
3811438263
mask = (1<<(ofst+n)) - (1<<ofst);
3811538264
assert( n>1 || mask==(1<<ofst) );
3811638265
sqlite3_mutex_enter(pShmNode->pShmMutex);
3811738266
if( flags & SQLITE_SHM_UNLOCK ){
@@ -51414,10 +51563,15 @@
5141451563
SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal);
5141551564
#endif
5141651565
5141751566
/* Return the sqlite3_file object for the WAL file */
5141851567
SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal);
51568
+
51569
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
51570
+SQLITE_PRIVATE int sqlite3WalWriteLock(Wal *pWal, int bLock);
51571
+SQLITE_PRIVATE void sqlite3WalDb(Wal *pWal, sqlite3 *db);
51572
+#endif
5141951573
5142051574
#endif /* ifndef SQLITE_OMIT_WAL */
5142151575
#endif /* SQLITE_WAL_H */
5142251576
5142351577
/************** End of wal.h *************************************************/
@@ -53935,13 +54089,16 @@
5393554089
}
5393654090
if( exists ){
5393754091
/* One of the journals pointed to by the master journal exists.
5393854092
** Open it and check if it points at the master journal. If
5393954093
** so, return without deleting the master journal file.
54094
+ ** NB: zJournal is really a MAIN_JOURNAL. But call it a
54095
+ ** MASTER_JOURNAL here so that the VFS will not send the zJournal
54096
+ ** name into sqlite3_database_file_object().
5394054097
*/
5394154098
int c;
53942
- int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL);
54099
+ int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MASTER_JOURNAL);
5394354100
rc = sqlite3OsOpen(pVfs, zJournal, pJournal, flags, 0);
5394454101
if( rc!=SQLITE_OK ){
5394554102
goto delmaster_out;
5394654103
}
5394754104
@@ -56141,10 +56298,11 @@
5614156298
** Pager object (sizeof(Pager) bytes)
5614256299
** PCache object (sqlite3PcacheSize() bytes)
5614356300
** Database file handle (pVfs->szOsFile bytes)
5614456301
** Sub-journal file handle (journalFileSize bytes)
5614556302
** Main journal file handle (journalFileSize bytes)
56303
+ ** Ptr back to the Pager (sizeof(Pager*) bytes)
5614656304
** \0\0\0\0 database prefix (4 bytes)
5614756305
** Database file name (nPathname+1 bytes)
5614856306
** URI query parameters (nUriByte bytes)
5614956307
** Journal filename (nPathname+8+1 bytes)
5615056308
** WAL filename (nPathname+4+1 bytes)
@@ -56180,10 +56338,11 @@
5618056338
pPtr = (u8 *)sqlite3MallocZero(
5618156339
ROUND8(sizeof(*pPager)) + /* Pager structure */
5618256340
ROUND8(pcacheSize) + /* PCache object */
5618356341
ROUND8(pVfs->szOsFile) + /* The main db file */
5618456342
journalFileSize * 2 + /* The two journal files */
56343
+ sizeof(pPager) + /* Space to hold a pointer */
5618556344
4 + /* Database prefix */
5618656345
nPathname + 1 + /* database filename */
5618756346
nUriByte + /* query parameters */
5618856347
nPathname + 8 + 1 + /* Journal filename */
5618956348
#ifndef SQLITE_OMIT_WAL
@@ -56200,10 +56359,11 @@
5620056359
pPager->pPCache = (PCache*)pPtr; pPtr += ROUND8(pcacheSize);
5620156360
pPager->fd = (sqlite3_file*)pPtr; pPtr += ROUND8(pVfs->szOsFile);
5620256361
pPager->sjfd = (sqlite3_file*)pPtr; pPtr += journalFileSize;
5620356362
pPager->jfd = (sqlite3_file*)pPtr; pPtr += journalFileSize;
5620456363
assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) );
56364
+ memcpy(pPtr, &pPager, sizeof(pPager)); pPtr += sizeof(pPager);
5620556365
5620656366
/* Fill in the Pager.zFilename and pPager.zQueryParam fields */
5620756367
pPtr += 4; /* Skip zero prefix */
5620856368
pPager->zFilename = (char*)pPtr;
5620956369
if( nPathname>0 ){
@@ -56400,10 +56560,23 @@
5640056560
5640156561
*ppPager = pPager;
5640256562
return SQLITE_OK;
5640356563
}
5640456564
56565
+/*
56566
+** Return the sqlite3_file for the main database given the name
56567
+** of the corresonding WAL or Journal name as passed into
56568
+** xOpen.
56569
+*/
56570
+SQLITE_API sqlite3_file *sqlite3_database_file_object(const char *zName){
56571
+ Pager *pPager;
56572
+ while( zName[-1]!=0 || zName[-2]!=0 || zName[-3]!=0 || zName[-4]!=0 ){
56573
+ zName--;
56574
+ }
56575
+ pPager = *(Pager**)(zName - 4 - sizeof(Pager*));
56576
+ return pPager->fd;
56577
+}
5640556578
5640656579
5640756580
/*
5640856581
** This function is called after transitioning from PAGER_UNLOCK to
5640956582
** PAGER_SHARED state. It tests if there is a hot journal present in
@@ -57085,11 +57258,10 @@
5708557258
Pager *pPager;
5708657259
assert( pPg!=0 );
5708757260
assert( pPg->pgno==1 );
5708857261
assert( (pPg->flags & PGHDR_MMAP)==0 ); /* Page1 is never memory mapped */
5708957262
pPager = pPg->pPager;
57090
- sqlite3PagerResetLockTimeout(pPager);
5709157263
sqlite3PcacheRelease(pPg);
5709257264
pagerUnlockIfUnused(pPager);
5709357265
}
5709457266
5709557267
/*
@@ -58378,20 +58550,10 @@
5837858550
*/
5837958551
SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager *pPager){
5838058552
return pPager->fd;
5838158553
}
5838258554
58383
-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
58384
-/*
58385
-** Reset the lock timeout for pager.
58386
-*/
58387
-SQLITE_PRIVATE void sqlite3PagerResetLockTimeout(Pager *pPager){
58388
- int x = 0;
58389
- sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCK_TIMEOUT, &x);
58390
-}
58391
-#endif
58392
-
5839358555
/*
5839458556
** Return the file handle for the journal file (if it exists).
5839558557
** This will be either the rollback journal or the WAL file.
5839658558
*/
5839758559
SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager *pPager){
@@ -58801,11 +58963,10 @@
5880158963
(eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler),
5880258964
pPager->pBusyHandlerArg,
5880358965
pPager->walSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace,
5880458966
pnLog, pnCkpt
5880558967
);
58806
- sqlite3PagerResetLockTimeout(pPager);
5880758968
}
5880858969
return rc;
5880958970
}
5881058971
5881158972
SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager){
@@ -58966,11 +59127,35 @@
5896659127
}
5896759128
}
5896859129
return rc;
5896959130
}
5897059131
59132
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
59133
+/*
59134
+** If pager pPager is a wal-mode database not in exclusive locking mode,
59135
+** invoke the sqlite3WalWriteLock() function on the associated Wal object
59136
+** with the same db and bLock parameters as were passed to this function.
59137
+** Return an SQLite error code if an error occurs, or SQLITE_OK otherwise.
59138
+*/
59139
+SQLITE_PRIVATE int sqlite3PagerWalWriteLock(Pager *pPager, int bLock){
59140
+ int rc = SQLITE_OK;
59141
+ if( pagerUseWal(pPager) && pPager->exclusiveMode==0 ){
59142
+ rc = sqlite3WalWriteLock(pPager->pWal, bLock);
59143
+ }
59144
+ return rc;
59145
+}
5897159146
59147
+/*
59148
+** Set the database handle used by the wal layer to determine if
59149
+** blocking locks are required.
59150
+*/
59151
+SQLITE_PRIVATE void sqlite3PagerWalDb(Pager *pPager, sqlite3 *db){
59152
+ if( pagerUseWal(pPager) ){
59153
+ sqlite3WalDb(pPager->pWal, db);
59154
+ }
59155
+}
59156
+#endif
5897259157
5897359158
#ifdef SQLITE_ENABLE_SNAPSHOT
5897459159
/*
5897559160
** If this is a WAL database, obtain a snapshot handle for the snapshot
5897659161
** currently open. Otherwise, return an error.
@@ -58986,11 +59171,14 @@
5898659171
/*
5898759172
** If this is a WAL database, store a pointer to pSnapshot. Next time a
5898859173
** read transaction is opened, attempt to read from the snapshot it
5898959174
** identifies. If this is not a WAL database, return an error.
5899059175
*/
58991
-SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot){
59176
+SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(
59177
+ Pager *pPager,
59178
+ sqlite3_snapshot *pSnapshot
59179
+){
5899259180
int rc = SQLITE_OK;
5899359181
if( pPager->pWal ){
5899459182
sqlite3WalSnapshotOpen(pPager->pWal, pSnapshot);
5899559183
}else{
5899659184
rc = SQLITE_ERROR;
@@ -59322,22 +59510,10 @@
5932259510
# define WALTRACE(X) if(sqlite3WalTrace) sqlite3DebugPrintf X
5932359511
#else
5932459512
# define WALTRACE(X)
5932559513
#endif
5932659514
59327
-/*
59328
-** WAL mode depends on atomic aligned 32-bit loads and stores in a few
59329
-** places. The following macros try to make this explicit.
59330
-*/
59331
-#if GCC_VESRION>=5004000
59332
-# define AtomicLoad(PTR) __atomic_load_n((PTR),__ATOMIC_RELAXED)
59333
-# define AtomicStore(PTR,VAL) __atomic_store_n((PTR),(VAL),__ATOMIC_RELAXED)
59334
-#else
59335
-# define AtomicLoad(PTR) (*(PTR))
59336
-# define AtomicStore(PTR,VAL) (*(PTR) = (VAL))
59337
-#endif
59338
-
5933959515
/*
5934059516
** The maximum (and only) versions of the wal and wal-index formats
5934159517
** that may be interpreted by this version of SQLite.
5934259518
**
5934359519
** If a client begins recovering a WAL file and finds that (a) the checksum
@@ -59542,10 +59718,13 @@
5954259718
#ifdef SQLITE_DEBUG
5954359719
u8 lockError; /* True if a locking error has occurred */
5954459720
#endif
5954559721
#ifdef SQLITE_ENABLE_SNAPSHOT
5954659722
WalIndexHdr *pSnapshot; /* Start transaction here if not NULL */
59723
+#endif
59724
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
59725
+ sqlite3 *db;
5954759726
#endif
5954859727
};
5954959728
5955059729
/*
5955159730
** Candidate values for Wal.exclusiveMode.
@@ -59916,11 +60095,11 @@
5991660095
if( pWal->exclusiveMode ) return SQLITE_OK;
5991760096
rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1,
5991860097
SQLITE_SHM_LOCK | SQLITE_SHM_SHARED);
5991960098
WALTRACE(("WAL%p: acquire SHARED-%s %s\n", pWal,
5992060099
walLockName(lockIdx), rc ? "failed" : "ok"));
59921
- VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && rc!=SQLITE_BUSY); )
60100
+ VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); )
5992260101
return rc;
5992360102
}
5992460103
static void walUnlockShared(Wal *pWal, int lockIdx){
5992560104
if( pWal->exclusiveMode ) return;
5992660105
(void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1,
@@ -59932,11 +60111,11 @@
5993260111
if( pWal->exclusiveMode ) return SQLITE_OK;
5993360112
rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, n,
5993460113
SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE);
5993560114
WALTRACE(("WAL%p: acquire EXCLUSIVE-%s cnt=%d %s\n", pWal,
5993660115
walLockName(lockIdx), n, rc ? "failed" : "ok"));
59937
- VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && rc!=SQLITE_BUSY); )
60116
+ VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); )
5993860117
return rc;
5993960118
}
5994060119
static void walUnlockExclusive(Wal *pWal, int lockIdx, int n){
5994160120
if( pWal->exclusiveMode ) return;
5994260121
(void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, n,
@@ -60751,10 +60930,93 @@
6075160930
p = 0;
6075260931
}
6075360932
*pp = p;
6075460933
return rc;
6075560934
}
60935
+
60936
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
60937
+/*
60938
+** Attempt to enable blocking locks. Blocking locks are enabled only if (a)
60939
+** they are supported by the VFS, and (b) the database handle is configured
60940
+** with a busy-timeout. Return 1 if blocking locks are successfully enabled,
60941
+** or 0 otherwise.
60942
+*/
60943
+static int walEnableBlocking(Wal *pWal){
60944
+ int res = 0;
60945
+ if( pWal->db ){
60946
+ int tmout = pWal->db->busyTimeout;
60947
+ if( tmout ){
60948
+ int rc;
60949
+ rc = sqlite3OsFileControl(
60950
+ pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout
60951
+ );
60952
+ res = (rc==SQLITE_OK);
60953
+ }
60954
+ }
60955
+ return res;
60956
+}
60957
+
60958
+/*
60959
+** Disable blocking locks.
60960
+*/
60961
+static void walDisableBlocking(Wal *pWal){
60962
+ int tmout = 0;
60963
+ sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout);
60964
+}
60965
+
60966
+/*
60967
+** If parameter bLock is true, attempt to enable blocking locks, take
60968
+** the WRITER lock, and then disable blocking locks. If blocking locks
60969
+** cannot be enabled, no attempt to obtain the WRITER lock is made. Return
60970
+** an SQLite error code if an error occurs, or SQLITE_OK otherwise. It is not
60971
+** an error if blocking locks can not be enabled.
60972
+**
60973
+** If the bLock parameter is false and the WRITER lock is held, release it.
60974
+*/
60975
+SQLITE_PRIVATE int sqlite3WalWriteLock(Wal *pWal, int bLock){
60976
+ int rc = SQLITE_OK;
60977
+ assert( pWal->readLock<0 || bLock==0 );
60978
+ if( bLock ){
60979
+ assert( pWal->db );
60980
+ if( walEnableBlocking(pWal) ){
60981
+ rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1);
60982
+ if( rc==SQLITE_OK ){
60983
+ pWal->writeLock = 1;
60984
+ }
60985
+ walDisableBlocking(pWal);
60986
+ }
60987
+ }else if( pWal->writeLock ){
60988
+ walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
60989
+ pWal->writeLock = 0;
60990
+ }
60991
+ return rc;
60992
+}
60993
+
60994
+/*
60995
+** Set the database handle used to determine if blocking locks are required.
60996
+*/
60997
+SQLITE_PRIVATE void sqlite3WalDb(Wal *pWal, sqlite3 *db){
60998
+ pWal->db = db;
60999
+}
61000
+
61001
+/*
61002
+** Take an exclusive WRITE lock. Blocking if so configured.
61003
+*/
61004
+static int walLockWriter(Wal *pWal){
61005
+ int rc;
61006
+ walEnableBlocking(pWal);
61007
+ rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1);
61008
+ walDisableBlocking(pWal);
61009
+ return rc;
61010
+}
61011
+#else
61012
+# define walEnableBlocking(x) 0
61013
+# define walDisableBlocking(x)
61014
+# define walLockWriter(pWal) walLockExclusive((pWal), WAL_WRITE_LOCK, 1)
61015
+# define sqlite3WalDb(pWal, db)
61016
+#endif /* ifdef SQLITE_ENABLE_SETLK_TIMEOUT */
61017
+
6075661018
6075761019
/*
6075861020
** Attempt to obtain the exclusive WAL lock defined by parameters lockIdx and
6075961021
** n. If the attempt fails and parameter xBusy is not NULL, then it is a
6076061022
** busy-handler function. Invoke it and retry the lock until either the
@@ -60769,10 +61031,16 @@
6076961031
){
6077061032
int rc;
6077161033
do {
6077261034
rc = walLockExclusive(pWal, lockIdx, n);
6077361035
}while( xBusy && rc==SQLITE_BUSY && xBusy(pBusyArg) );
61036
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
61037
+ if( rc==SQLITE_BUSY_TIMEOUT ){
61038
+ walDisableBlocking(pWal);
61039
+ rc = SQLITE_BUSY;
61040
+ }
61041
+#endif
6077461042
return rc;
6077561043
}
6077661044
6077761045
/*
6077861046
** The cache of the wal-index header must be valid to call this function.
@@ -60939,10 +61207,11 @@
6093961207
** about the eventual size of the db file to the VFS layer.
6094061208
*/
6094161209
if( rc==SQLITE_OK ){
6094261210
i64 nReq = ((i64)mxPage * szPage);
6094361211
i64 nSize; /* Current size of database file */
61212
+ sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_START, 0);
6094461213
rc = sqlite3OsFileSize(pWal->pDbFd, &nSize);
6094561214
if( rc==SQLITE_OK && nSize<nReq ){
6094661215
sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq);
6094761216
}
6094861217
}
@@ -60950,11 +61219,11 @@
6095061219
6095161220
/* Iterate through the contents of the WAL, copying data to the db file */
6095261221
while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){
6095361222
i64 iOffset;
6095461223
assert( walFramePgno(pWal, iFrame)==iDbpage );
60955
- if( db->u1.isInterrupted ){
61224
+ if( AtomicLoad(&db->u1.isInterrupted) ){
6095661225
rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT;
6095761226
break;
6095861227
}
6095961228
if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ){
6096061229
continue;
@@ -60966,10 +61235,11 @@
6096661235
iOffset = (iDbpage-1)*(i64)szPage;
6096761236
testcase( IS_BIG_INT(iOffset) );
6096861237
rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset);
6096961238
if( rc!=SQLITE_OK ) break;
6097061239
}
61240
+ sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_DONE, 0);
6097161241
6097261242
/* If work was actually accomplished... */
6097361243
if( rc==SQLITE_OK ){
6097461244
if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){
6097561245
i64 szDb = pWal->hdr.nPage*(i64)szPage;
@@ -60977,14 +61247,10 @@
6097761247
rc = sqlite3OsTruncate(pWal->pDbFd, szDb);
6097861248
if( rc==SQLITE_OK ){
6097961249
rc = sqlite3OsSync(pWal->pDbFd, CKPT_SYNC_FLAGS(sync_flags));
6098061250
}
6098161251
}
60982
- if( rc==SQLITE_OK ){
60983
- rc = sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_DONE, 0);
60984
- if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
60985
- }
6098661252
if( rc==SQLITE_OK ){
6098761253
pInfo->nBackfill = mxSafeFrame;
6098861254
}
6098961255
}
6099061256
@@ -61250,32 +61516,36 @@
6125061516
badHdr = (page0 ? walIndexTryHdr(pWal, pChanged) : 1);
6125161517
6125261518
/* If the first attempt failed, it might have been due to a race
6125361519
** with a writer. So get a WRITE lock and try again.
6125461520
*/
61255
- assert( badHdr==0 || pWal->writeLock==0 );
6125661521
if( badHdr ){
6125761522
if( pWal->bShmUnreliable==0 && (pWal->readOnly & WAL_SHM_RDONLY) ){
6125861523
if( SQLITE_OK==(rc = walLockShared(pWal, WAL_WRITE_LOCK)) ){
6125961524
walUnlockShared(pWal, WAL_WRITE_LOCK);
6126061525
rc = SQLITE_READONLY_RECOVERY;
6126161526
}
61262
- }else if( SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ){
61263
- pWal->writeLock = 1;
61264
- if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){
61265
- badHdr = walIndexTryHdr(pWal, pChanged);
61266
- if( badHdr ){
61267
- /* If the wal-index header is still malformed even while holding
61268
- ** a WRITE lock, it can only mean that the header is corrupted and
61269
- ** needs to be reconstructed. So run recovery to do exactly that.
61270
- */
61271
- rc = walIndexRecover(pWal);
61272
- *pChanged = 1;
61273
- }
61274
- }
61275
- pWal->writeLock = 0;
61276
- walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
61527
+ }else{
61528
+ int bWriteLock = pWal->writeLock;
61529
+ if( bWriteLock || SQLITE_OK==(rc = walLockWriter(pWal)) ){
61530
+ pWal->writeLock = 1;
61531
+ if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){
61532
+ badHdr = walIndexTryHdr(pWal, pChanged);
61533
+ if( badHdr ){
61534
+ /* If the wal-index header is still malformed even while holding
61535
+ ** a WRITE lock, it can only mean that the header is corrupted and
61536
+ ** needs to be reconstructed. So run recovery to do exactly that.
61537
+ */
61538
+ rc = walIndexRecover(pWal);
61539
+ *pChanged = 1;
61540
+ }
61541
+ }
61542
+ if( bWriteLock==0 ){
61543
+ pWal->writeLock = 0;
61544
+ walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
61545
+ }
61546
+ }
6127761547
}
6127861548
}
6127961549
6128061550
/* If the header is read successfully, check the version number to make
6128161551
** sure the wal-index was not constructed with some future format that
@@ -61663,11 +61933,12 @@
6166361933
&& (mxReadMark<mxFrame || mxI==0)
6166461934
){
6166561935
for(i=1; i<WAL_NREADER; i++){
6166661936
rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
6166761937
if( rc==SQLITE_OK ){
61668
- mxReadMark = AtomicStore(pInfo->aReadMark+i,mxFrame);
61938
+ AtomicStore(pInfo->aReadMark+i,mxFrame);
61939
+ mxReadMark = mxFrame;
6166961940
mxI = i;
6167061941
walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
6167161942
break;
6167261943
}else if( rc!=SQLITE_BUSY ){
6167361944
return rc;
@@ -61823,15 +62094,36 @@
6182362094
*/
6182462095
SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
6182562096
int rc; /* Return code */
6182662097
int cnt = 0; /* Number of TryBeginRead attempts */
6182762098
62099
+ assert( pWal->ckptLock==0 );
62100
+
6182862101
#ifdef SQLITE_ENABLE_SNAPSHOT
6182962102
int bChanged = 0;
6183062103
WalIndexHdr *pSnapshot = pWal->pSnapshot;
61831
- if( pSnapshot && memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){
61832
- bChanged = 1;
62104
+ if( pSnapshot ){
62105
+ if( memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){
62106
+ bChanged = 1;
62107
+ }
62108
+
62109
+ /* It is possible that there is a checkpointer thread running
62110
+ ** concurrent with this code. If this is the case, it may be that the
62111
+ ** checkpointer has already determined that it will checkpoint
62112
+ ** snapshot X, where X is later in the wal file than pSnapshot, but
62113
+ ** has not yet set the pInfo->nBackfillAttempted variable to indicate
62114
+ ** its intent. To avoid the race condition this leads to, ensure that
62115
+ ** there is no checkpointer process by taking a shared CKPT lock
62116
+ ** before checking pInfo->nBackfillAttempted. */
62117
+ (void)walEnableBlocking(pWal);
62118
+ rc = walLockShared(pWal, WAL_CKPT_LOCK);
62119
+ walDisableBlocking(pWal);
62120
+
62121
+ if( rc!=SQLITE_OK ){
62122
+ return rc;
62123
+ }
62124
+ pWal->ckptLock = 1;
6183362125
}
6183462126
#endif
6183562127
6183662128
do{
6183762129
rc = walTryBeginRead(pWal, pChanged, 0, ++cnt);
@@ -61860,52 +62152,46 @@
6186062152
volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
6186162153
6186262154
assert( pWal->readLock>0 || pWal->hdr.mxFrame==0 );
6186362155
assert( pInfo->aReadMark[pWal->readLock]<=pSnapshot->mxFrame );
6186462156
61865
- /* It is possible that there is a checkpointer thread running
61866
- ** concurrent with this code. If this is the case, it may be that the
61867
- ** checkpointer has already determined that it will checkpoint
61868
- ** snapshot X, where X is later in the wal file than pSnapshot, but
61869
- ** has not yet set the pInfo->nBackfillAttempted variable to indicate
61870
- ** its intent. To avoid the race condition this leads to, ensure that
61871
- ** there is no checkpointer process by taking a shared CKPT lock
61872
- ** before checking pInfo->nBackfillAttempted.
61873
- **
61874
- ** TODO: Does the aReadMark[] lock prevent a checkpointer from doing
61875
- ** this already?
61876
- */
61877
- rc = walLockShared(pWal, WAL_CKPT_LOCK);
61878
-
61879
- if( rc==SQLITE_OK ){
61880
- /* Check that the wal file has not been wrapped. Assuming that it has
61881
- ** not, also check that no checkpointer has attempted to checkpoint any
61882
- ** frames beyond pSnapshot->mxFrame. If either of these conditions are
61883
- ** true, return SQLITE_ERROR_SNAPSHOT. Otherwise, overwrite pWal->hdr
61884
- ** with *pSnapshot and set *pChanged as appropriate for opening the
61885
- ** snapshot. */
61886
- if( !memcmp(pSnapshot->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))
61887
- && pSnapshot->mxFrame>=pInfo->nBackfillAttempted
61888
- ){
61889
- assert( pWal->readLock>0 );
61890
- memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr));
61891
- *pChanged = bChanged;
61892
- }else{
61893
- rc = SQLITE_ERROR_SNAPSHOT;
61894
- }
61895
-
61896
- /* Release the shared CKPT lock obtained above. */
61897
- walUnlockShared(pWal, WAL_CKPT_LOCK);
61898
- pWal->minFrame = 1;
61899
- }
61900
-
62157
+ /* Check that the wal file has not been wrapped. Assuming that it has
62158
+ ** not, also check that no checkpointer has attempted to checkpoint any
62159
+ ** frames beyond pSnapshot->mxFrame. If either of these conditions are
62160
+ ** true, return SQLITE_ERROR_SNAPSHOT. Otherwise, overwrite pWal->hdr
62161
+ ** with *pSnapshot and set *pChanged as appropriate for opening the
62162
+ ** snapshot. */
62163
+ if( !memcmp(pSnapshot->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))
62164
+ && pSnapshot->mxFrame>=pInfo->nBackfillAttempted
62165
+ ){
62166
+ assert( pWal->readLock>0 );
62167
+ memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr));
62168
+ *pChanged = bChanged;
62169
+ }else{
62170
+ rc = SQLITE_ERROR_SNAPSHOT;
62171
+ }
62172
+
62173
+ /* A client using a non-current snapshot may not ignore any frames
62174
+ ** from the start of the wal file. This is because, for a system
62175
+ ** where (minFrame < iSnapshot < maxFrame), a checkpointer may
62176
+ ** have omitted to checkpoint a frame earlier than minFrame in
62177
+ ** the file because there exists a frame after iSnapshot that
62178
+ ** is the same database page. */
62179
+ pWal->minFrame = 1;
6190162180
6190262181
if( rc!=SQLITE_OK ){
6190362182
sqlite3WalEndReadTransaction(pWal);
6190462183
}
6190562184
}
6190662185
}
62186
+
62187
+ /* Release the shared CKPT lock obtained above. */
62188
+ if( pWal->ckptLock ){
62189
+ assert( pSnapshot );
62190
+ walUnlockShared(pWal, WAL_CKPT_LOCK);
62191
+ pWal->ckptLock = 0;
62192
+ }
6190762193
#endif
6190862194
return rc;
6190962195
}
6191062196
6191162197
/*
@@ -62071,10 +62357,20 @@
6207162357
**
6207262358
** There can only be a single writer active at a time.
6207362359
*/
6207462360
SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){
6207562361
int rc;
62362
+
62363
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
62364
+ /* If the write-lock is already held, then it was obtained before the
62365
+ ** read-transaction was even opened, making this call a no-op.
62366
+ ** Return early. */
62367
+ if( pWal->writeLock ){
62368
+ assert( !memcmp(&pWal->hdr,(void *)walIndexHdr(pWal),sizeof(WalIndexHdr)) );
62369
+ return SQLITE_OK;
62370
+ }
62371
+#endif
6207662372
6207762373
/* Cannot start a write transaction without first holding a read
6207862374
** transaction. */
6207962375
assert( pWal->readLock>=0 );
6208062376
assert( pWal->writeLock==0 && pWal->iReCksum==0 );
@@ -62647,50 +62943,57 @@
6264762943
** in the SQLITE_CHECKPOINT_PASSIVE mode. */
6264862944
assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 );
6264962945
6265062946
if( pWal->readOnly ) return SQLITE_READONLY;
6265162947
WALTRACE(("WAL%p: checkpoint begins\n", pWal));
62948
+
62949
+ /* Enable blocking locks, if possible. If blocking locks are successfully
62950
+ ** enabled, set xBusy2=0 so that the busy-handler is never invoked. */
62951
+ sqlite3WalDb(pWal, db);
62952
+ (void)walEnableBlocking(pWal);
6265262953
6265362954
/* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive
62654
- ** "checkpoint" lock on the database file. */
62955
+ ** "checkpoint" lock on the database file.
62956
+ ** EVIDENCE-OF: R-10421-19736 If any other process is running a
62957
+ ** checkpoint operation at the same time, the lock cannot be obtained and
62958
+ ** SQLITE_BUSY is returned.
62959
+ ** EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured,
62960
+ ** it will not be invoked in this case.
62961
+ */
6265562962
rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
62656
- if( rc ){
62657
- /* EVIDENCE-OF: R-10421-19736 If any other process is running a
62658
- ** checkpoint operation at the same time, the lock cannot be obtained and
62659
- ** SQLITE_BUSY is returned.
62660
- ** EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured,
62661
- ** it will not be invoked in this case.
62662
- */
62663
- testcase( rc==SQLITE_BUSY );
62664
- testcase( xBusy!=0 );
62665
- return rc;
62666
- }
62667
- pWal->ckptLock = 1;
62668
-
62669
- /* IMPLEMENTATION-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART and
62670
- ** TRUNCATE modes also obtain the exclusive "writer" lock on the database
62671
- ** file.
62672
- **
62673
- ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained
62674
- ** immediately, and a busy-handler is configured, it is invoked and the
62675
- ** writer lock retried until either the busy-handler returns 0 or the
62676
- ** lock is successfully obtained.
62677
- */
62678
- if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
62679
- rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_WRITE_LOCK, 1);
62680
- if( rc==SQLITE_OK ){
62681
- pWal->writeLock = 1;
62682
- }else if( rc==SQLITE_BUSY ){
62683
- eMode2 = SQLITE_CHECKPOINT_PASSIVE;
62684
- xBusy2 = 0;
62685
- rc = SQLITE_OK;
62686
- }
62687
- }
62963
+ testcase( rc==SQLITE_BUSY );
62964
+ testcase( rc!=SQLITE_OK && xBusy2!=0 );
62965
+ if( rc==SQLITE_OK ){
62966
+ pWal->ckptLock = 1;
62967
+
62968
+ /* IMPLEMENTATION-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART and
62969
+ ** TRUNCATE modes also obtain the exclusive "writer" lock on the database
62970
+ ** file.
62971
+ **
62972
+ ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained
62973
+ ** immediately, and a busy-handler is configured, it is invoked and the
62974
+ ** writer lock retried until either the busy-handler returns 0 or the
62975
+ ** lock is successfully obtained.
62976
+ */
62977
+ if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
62978
+ rc = walBusyLock(pWal, xBusy2, pBusyArg, WAL_WRITE_LOCK, 1);
62979
+ if( rc==SQLITE_OK ){
62980
+ pWal->writeLock = 1;
62981
+ }else if( rc==SQLITE_BUSY ){
62982
+ eMode2 = SQLITE_CHECKPOINT_PASSIVE;
62983
+ xBusy2 = 0;
62984
+ rc = SQLITE_OK;
62985
+ }
62986
+ }
62987
+ }
62988
+
6268862989
6268962990
/* Read the wal-index header. */
6269062991
if( rc==SQLITE_OK ){
62992
+ walDisableBlocking(pWal);
6269162993
rc = walIndexReadHdr(pWal, &isChanged);
62994
+ (void)walEnableBlocking(pWal);
6269262995
if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){
6269362996
sqlite3OsUnfetch(pWal->pDbFd, 0, 0);
6269462997
}
6269562998
}
6269662999
@@ -62717,16 +63020,24 @@
6271763020
** next time the pager opens a snapshot on this database it knows that
6271863021
** the cache needs to be reset.
6271963022
*/
6272063023
memset(&pWal->hdr, 0, sizeof(WalIndexHdr));
6272163024
}
63025
+
63026
+ walDisableBlocking(pWal);
63027
+ sqlite3WalDb(pWal, 0);
6272263028
6272363029
/* Release the locks. */
6272463030
sqlite3WalEndWriteTransaction(pWal);
62725
- walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
62726
- pWal->ckptLock = 0;
63031
+ if( pWal->ckptLock ){
63032
+ walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
63033
+ pWal->ckptLock = 0;
63034
+ }
6272763035
WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok"));
63036
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
63037
+ if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY;
63038
+#endif
6272863039
return (rc==SQLITE_OK && eMode!=eMode2 ? SQLITE_BUSY : rc);
6272963040
}
6273063041
6273163042
/* Return the value to pass to a sqlite3_wal_hook callback, the
6273263043
** number of frames in the WAL at the point of the last commit since
@@ -62839,11 +63150,14 @@
6283963150
return rc;
6284063151
}
6284163152
6284263153
/* Try to open on pSnapshot when the next read-transaction starts
6284363154
*/
62844
-SQLITE_PRIVATE void sqlite3WalSnapshotOpen(Wal *pWal, sqlite3_snapshot *pSnapshot){
63155
+SQLITE_PRIVATE void sqlite3WalSnapshotOpen(
63156
+ Wal *pWal,
63157
+ sqlite3_snapshot *pSnapshot
63158
+){
6284563159
pWal->pSnapshot = (WalIndexHdr*)pSnapshot;
6284663160
}
6284763161
6284863162
/*
6284963163
** Return a +ve value if snapshot p1 is newer than p2. A -ve value if
@@ -63358,10 +63672,11 @@
6335863672
u8 incrVacuum; /* True if incr-vacuum is enabled */
6335963673
u8 bDoTruncate; /* True to truncate db on commit */
6336063674
#endif
6336163675
u8 inTransaction; /* Transaction state */
6336263676
u8 max1bytePayload; /* Maximum first byte of cell for a 1-byte payload */
63677
+ u8 nReserveWanted; /* Desired number of extra bytes per page */
6336363678
u16 btsFlags; /* Boolean parameters. See BTS_* macros below */
6336463679
u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */
6336563680
u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */
6336663681
u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */
6336763682
u16 minLeaf; /* Minimum local payload in a LEAFDATA table */
@@ -66251,12 +66566,11 @@
6625166566
*/
6625266567
static int btreeInvokeBusyHandler(void *pArg){
6625366568
BtShared *pBt = (BtShared*)pArg;
6625466569
assert( pBt->db );
6625566570
assert( sqlite3_mutex_held(pBt->db->mutex) );
66256
- return sqlite3InvokeBusyHandler(&pBt->db->busyHandler,
66257
- sqlite3PagerFile(pBt->pPager));
66571
+ return sqlite3InvokeBusyHandler(&pBt->db->busyHandler);
6625866572
}
6625966573
6626066574
/*
6626166575
** Open a database file.
6626266576
**
@@ -66803,20 +67117,21 @@
6680367117
** If the iFix!=0 then the BTS_PAGESIZE_FIXED flag is set so that the page size
6680467118
** and autovacuum mode can no longer be changed.
6680567119
*/
6680667120
SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){
6680767121
int rc = SQLITE_OK;
67122
+ int x;
6680867123
BtShared *pBt = p->pBt;
66809
- assert( nReserve>=-1 && nReserve<=255 );
67124
+ assert( nReserve>=0 && nReserve<=255 );
6681067125
sqlite3BtreeEnter(p);
67126
+ pBt->nReserveWanted = nReserve;
67127
+ x = pBt->pageSize - pBt->usableSize;
67128
+ if( nReserve<x ) nReserve = x;
6681167129
if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){
6681267130
sqlite3BtreeLeave(p);
6681367131
return SQLITE_READONLY;
6681467132
}
66815
- if( nReserve<0 ){
66816
- nReserve = pBt->pageSize - pBt->usableSize;
66817
- }
6681867133
assert( nReserve>=0 && nReserve<=255 );
6681967134
if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE &&
6682067135
((pageSize-1)&pageSize)==0 ){
6682167136
assert( (pageSize & 7)==0 );
6682267137
assert( !pBt->pCursor );
@@ -66858,20 +67173,21 @@
6685867173
/*
6685967174
** Return the number of bytes of space at the end of every page that
6686067175
** are intentually left unused. This is the "reserved" space that is
6686167176
** sometimes used by extensions.
6686267177
**
66863
-** If SQLITE_HAS_MUTEX is defined then the number returned is the
66864
-** greater of the current reserved space and the maximum requested
66865
-** reserve space.
67178
+** The value returned is the larger of the current reserve size and
67179
+** the latest reserve size requested by SQLITE_FILECTRL_RESERVE_BYTES.
67180
+** The amount of reserve can only grow - never shrink.
6686667181
*/
66867
-SQLITE_PRIVATE int sqlite3BtreeGetOptimalReserve(Btree *p){
66868
- int n;
67182
+SQLITE_PRIVATE int sqlite3BtreeGetRequestedReserve(Btree *p){
67183
+ int n1, n2;
6686967184
sqlite3BtreeEnter(p);
66870
- n = sqlite3BtreeGetReserveNoMutex(p);
67185
+ n1 = (int)p->pBt->nReserveWanted;
67186
+ n2 = sqlite3BtreeGetReserveNoMutex(p);
6687167187
sqlite3BtreeLeave(p);
66872
- return n;
67188
+ return n1>n2 ? n1 : n2;
6687367189
}
6687467190
6687567191
6687667192
/*
6687767193
** Set the maximum page count for a database if mxPage is positive.
@@ -67317,10 +67633,11 @@
6731767633
** when A already has a read lock, we encourage A to give up and let B
6731867634
** proceed.
6731967635
*/
6732067636
SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){
6732167637
BtShared *pBt = p->pBt;
67638
+ Pager *pPager = pBt->pPager;
6732267639
int rc = SQLITE_OK;
6732367640
6732467641
sqlite3BtreeEnter(p);
6732567642
btreeIntegrity(p);
6732667643
@@ -67332,11 +67649,11 @@
6733267649
goto trans_begun;
6733367650
}
6733467651
assert( pBt->inTransaction==TRANS_WRITE || IfNotOmitAV(pBt->bDoTruncate)==0 );
6733567652
6733667653
if( (p->db->flags & SQLITE_ResetDatabase)
67337
- && sqlite3PagerIsreadonly(pBt->pPager)==0
67654
+ && sqlite3PagerIsreadonly(pPager)==0
6733867655
){
6733967656
pBt->btsFlags &= ~BTS_READ_ONLY;
6734067657
}
6734167658
6734267659
/* Write transactions are not possible on a read-only database */
@@ -67380,10 +67697,22 @@
6738067697
if( SQLITE_OK!=rc ) goto trans_begun;
6738167698
6738267699
pBt->btsFlags &= ~BTS_INITIALLY_EMPTY;
6738367700
if( pBt->nPage==0 ) pBt->btsFlags |= BTS_INITIALLY_EMPTY;
6738467701
do {
67702
+ sqlite3PagerWalDb(pPager, p->db);
67703
+
67704
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
67705
+ /* If transitioning from no transaction directly to a write transaction,
67706
+ ** block for the WRITER lock first if possible. */
67707
+ if( pBt->pPage1==0 && wrflag ){
67708
+ assert( pBt->inTransaction==TRANS_NONE );
67709
+ rc = sqlite3PagerWalWriteLock(pPager, 1);
67710
+ if( rc!=SQLITE_BUSY && rc!=SQLITE_OK ) break;
67711
+ }
67712
+#endif
67713
+
6738567714
/* Call lockBtree() until either pBt->pPage1 is populated or
6738667715
** lockBtree() returns something other than SQLITE_OK. lockBtree()
6738767716
** may return SQLITE_OK but leave pBt->pPage1 set to 0 if after
6738867717
** reading page 1 it discovers that the page-size of the database
6738967718
** file is not pBt->pageSize. In this case lockBtree() will update
@@ -67393,11 +67722,11 @@
6739367722
6739467723
if( rc==SQLITE_OK && wrflag ){
6739567724
if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){
6739667725
rc = SQLITE_READONLY;
6739767726
}else{
67398
- rc = sqlite3PagerBegin(pBt->pPager,wrflag>1,sqlite3TempInMemory(p->db));
67727
+ rc = sqlite3PagerBegin(pPager, wrflag>1, sqlite3TempInMemory(p->db));
6739967728
if( rc==SQLITE_OK ){
6740067729
rc = newDatabase(pBt);
6740167730
}else if( rc==SQLITE_BUSY_SNAPSHOT && pBt->inTransaction==TRANS_NONE ){
6740267731
/* if there was no transaction opened when this function was
6740367732
** called and SQLITE_BUSY_SNAPSHOT is returned, change the error
@@ -67406,15 +67735,19 @@
6740667735
}
6740767736
}
6740867737
}
6740967738
6741067739
if( rc!=SQLITE_OK ){
67740
+ (void)sqlite3PagerWalWriteLock(pPager, 0);
6741167741
unlockBtreeIfUnused(pBt);
6741267742
}
6741367743
}while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&
6741467744
btreeInvokeBusyHandler(pBt) );
67415
- sqlite3PagerResetLockTimeout(pBt->pPager);
67745
+ sqlite3PagerWalDb(pPager, 0);
67746
+#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
67747
+ if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY;
67748
+#endif
6741667749
6741767750
if( rc==SQLITE_OK ){
6741867751
if( p->inTrans==TRANS_NONE ){
6741967752
pBt->nTransaction++;
6742067753
#ifndef SQLITE_OMIT_SHARED_CACHE
@@ -67462,11 +67795,11 @@
6746267795
if( wrflag ){
6746367796
/* This call makes sure that the pager has the correct number of
6746467797
** open savepoints. If the second parameter is greater than 0 and
6746567798
** the sub-journal is not already open, then it will be opened here.
6746667799
*/
67467
- rc = sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint);
67800
+ rc = sqlite3PagerOpenSavepoint(pPager, p->db->nSavepoint);
6746867801
}
6746967802
}
6747067803
6747167804
btreeIntegrity(p);
6747267805
sqlite3BtreeLeave(p);
@@ -71098,11 +71431,11 @@
7109871431
7109971432
/* Remove cells from the start and end of the page */
7110071433
assert( nCell>=0 );
7110171434
if( iOld<iNew ){
7110271435
int nShift = pageFreeArray(pPg, iOld, iNew-iOld, pCArray);
71103
- if( nShift>nCell ) return SQLITE_CORRUPT_BKPT;
71436
+ if( NEVER(nShift>nCell) ) return SQLITE_CORRUPT_BKPT;
7110471437
memmove(pPg->aCellIdx, &pPg->aCellIdx[nShift*2], nCell*2);
7110571438
nCell -= nShift;
7110671439
}
7110771440
if( iNewEnd < iOldEnd ){
7110871441
int nTail = pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray);
@@ -73455,11 +73788,10 @@
7345573788
}
7345673789
sqlite3BtreeLeave(p);
7345773790
return rc;
7345873791
}
7345973792
73460
-#ifndef SQLITE_OMIT_BTREECOUNT
7346173793
/*
7346273794
** The first argument, pCur, is a cursor opened on some b-tree. Count the
7346373795
** number of entries in the b-tree and write the result to *pnEntry.
7346473796
**
7346573797
** SQLITE_OK is returned if the operation is successfully executed.
@@ -73477,11 +73809,11 @@
7347773809
}
7347873810
7347973811
/* Unless an error occurs, the following loop runs one iteration for each
7348073812
** page in the B-Tree structure (not including overflow pages).
7348173813
*/
73482
- while( rc==SQLITE_OK && !db->u1.isInterrupted ){
73814
+ while( rc==SQLITE_OK && !AtomicLoad(&db->u1.isInterrupted) ){
7348373815
int iIdx; /* Index of child node in parent */
7348473816
MemPage *pPage; /* Current page of the b-tree */
7348573817
7348673818
/* If this is a leaf page or the tree is not an int-key tree, then
7348773819
** this page contains countable entries. Increment the entry counter
@@ -73528,11 +73860,10 @@
7352873860
}
7352973861
7353073862
/* An error has occurred. Return an error code. */
7353173863
return rc;
7353273864
}
73533
-#endif
7353473865
7353573866
/*
7353673867
** Return the pager associated with a BTree. This routine is used for
7353773868
** testing and debugging only.
7353873869
*/
@@ -73603,11 +73934,11 @@
7360373934
}
7360473935
if( getPageReferenced(pCheck, iPage) ){
7360573936
checkAppendMsg(pCheck, "2nd reference to page %d", iPage);
7360673937
return 1;
7360773938
}
73608
- if( pCheck->db->u1.isInterrupted ) return 1;
73939
+ if( AtomicLoad(&pCheck->db->u1.isInterrupted) ) return 1;
7360973940
setPageReferenced(pCheck, iPage);
7361073941
return 0;
7361173942
}
7361273943
7361373944
#ifndef SQLITE_OMIT_AUTOVACUUM
@@ -74579,11 +74910,11 @@
7457974910
** Attempt to set the page size of the destination to match the page size
7458074911
** of the source.
7458174912
*/
7458274913
static int setDestPgsz(sqlite3_backup *p){
7458374914
int rc;
74584
- rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),-1,0);
74915
+ rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),0,0);
7458574916
return rc;
7458674917
}
7458774918
7458874919
/*
7458974920
** Check that there is no open read-transaction on the b-tree passed as the
@@ -78604,24 +78935,23 @@
7860478935
** "PX" -> "r[X]"
7860578936
** "PX@PY" -> "r[X..X+Y-1]" or "r[x]" if y is 0 or 1
7860678937
** "PX@PY+1" -> "r[X..X+Y]" or "r[x]" if y is 0
7860778938
** "PY..PY" -> "r[X..Y]" or "r[x]" if y<=x
7860878939
*/
78609
-static int displayComment(
78940
+SQLITE_PRIVATE char *sqlite3VdbeDisplayComment(
78941
+ sqlite3 *db, /* Optional - Oom error reporting only */
7861078942
const Op *pOp, /* The opcode to be commented */
78611
- const char *zP4, /* Previously obtained value for P4 */
78612
- char *zTemp, /* Write result here */
78613
- int nTemp /* Space available in zTemp[] */
78943
+ const char *zP4 /* Previously obtained value for P4 */
7861478944
){
7861578945
const char *zOpName;
7861678946
const char *zSynopsis;
7861778947
int nOpName;
7861878948
int ii;
7861978949
char zAlt[50];
7862078950
StrAccum x;
78621
- sqlite3StrAccumInit(&x, 0, zTemp, nTemp, 0);
7862278951
78952
+ sqlite3StrAccumInit(&x, 0, 0, 0, SQLITE_MAX_LENGTH);
7862378953
zOpName = sqlite3OpcodeName(pOp->opcode);
7862478954
nOpName = sqlite3Strlen30(zOpName);
7862578955
if( zOpName[nOpName+1] ){
7862678956
int seenCom = 0;
7862778957
char c;
@@ -78684,14 +79014,16 @@
7868479014
sqlite3_str_appendf(&x, "; %s", pOp->zComment);
7868579015
}
7868679016
}else if( pOp->zComment ){
7868779017
sqlite3_str_appendall(&x, pOp->zComment);
7868879018
}
78689
- sqlite3StrAccumFinish(&x);
78690
- return x.nChar;
79019
+ if( (x.accError & SQLITE_NOMEM)!=0 && db!=0 ){
79020
+ sqlite3OomFault(db);
79021
+ }
79022
+ return sqlite3StrAccumFinish(&x);
7869179023
}
78692
-#endif /* SQLITE_DEBUG */
79024
+#endif /* SQLITE_ENABLE_EXPLAIN_COMMENTS */
7869379025
7869479026
#if VDBE_DISPLAY_P4 && defined(SQLITE_ENABLE_CURSOR_HINTS)
7869579027
/*
7869679028
** Translate the P4.pExpr value for an OP_CursorHint opcode into text
7869779029
** that can be displayed in the P4 column of EXPLAIN output.
@@ -78768,15 +79100,15 @@
7876879100
#if VDBE_DISPLAY_P4
7876979101
/*
7877079102
** Compute a string that describes the P4 parameter for an opcode.
7877179103
** Use zTemp for any required temporary buffer space.
7877279104
*/
78773
-static char *displayP4(Op *pOp, char *zTemp, int nTemp){
78774
- char *zP4 = zTemp;
79105
+SQLITE_PRIVATE char *sqlite3VdbeDisplayP4(sqlite3 *db, Op *pOp){
79106
+ char *zP4 = 0;
7877579107
StrAccum x;
78776
- assert( nTemp>=20 );
78777
- sqlite3StrAccumInit(&x, 0, zTemp, nTemp, 0);
79108
+
79109
+ sqlite3StrAccumInit(&x, 0, 0, 0, SQLITE_MAX_LENGTH);
7877879110
switch( pOp->p4type ){
7877979111
case P4_KEYINFO: {
7878079112
int j;
7878179113
KeyInfo *pKeyInfo = pOp->p4.pKeyInfo;
7878279114
assert( pKeyInfo->aSortFlags!=0 );
@@ -78856,40 +79188,36 @@
7885679188
int i;
7885779189
int *ai = pOp->p4.ai;
7885879190
int n = ai[0]; /* The first element of an INTARRAY is always the
7885979191
** count of the number of elements to follow */
7886079192
for(i=1; i<=n; i++){
78861
- sqlite3_str_appendf(&x, ",%d", ai[i]);
79193
+ sqlite3_str_appendf(&x, "%c%d", (i==1 ? '[' : ','), ai[i]);
7886279194
}
78863
- zTemp[0] = '[';
7886479195
sqlite3_str_append(&x, "]", 1);
7886579196
break;
7886679197
}
7886779198
case P4_SUBPROGRAM: {
78868
- sqlite3_str_appendf(&x, "program");
79199
+ zP4 = "program";
7886979200
break;
7887079201
}
7887179202
case P4_DYNBLOB:
7887279203
case P4_ADVANCE: {
78873
- zTemp[0] = 0;
7887479204
break;
7887579205
}
7887679206
case P4_TABLE: {
78877
- sqlite3_str_appendf(&x, "%s", pOp->p4.pTab->zName);
79207
+ zP4 = pOp->p4.pTab->zName;
7887879208
break;
7887979209
}
7888079210
default: {
7888179211
zP4 = pOp->p4.z;
78882
- if( zP4==0 ){
78883
- zP4 = zTemp;
78884
- zTemp[0] = 0;
78885
- }
7888679212
}
7888779213
}
78888
- sqlite3StrAccumFinish(&x);
78889
- assert( zP4!=0 );
78890
- return zP4;
79214
+ if( zP4 ) sqlite3_str_appendall(&x, zP4);
79215
+ if( (x.accError & SQLITE_NOMEM)!=0 ){
79216
+ sqlite3OomFault(db);
79217
+ }
79218
+ return sqlite3StrAccumFinish(&x);
7889179219
}
7889279220
#endif /* VDBE_DISPLAY_P4 */
7889379221
7889479222
/*
7889579223
** Declare to the Vdbe that the BTree object at db->aDb[i] is used.
@@ -78975,28 +79303,32 @@
7897579303
/*
7897679304
** Print a single opcode. This routine is used for debugging only.
7897779305
*/
7897879306
SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE *pOut, int pc, VdbeOp *pOp){
7897979307
char *zP4;
78980
- char zPtr[50];
78981
- char zCom[100];
79308
+ char *zCom;
79309
+ sqlite3 dummyDb;
7898279310
static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-13s %.2X %s\n";
7898379311
if( pOut==0 ) pOut = stdout;
78984
- zP4 = displayP4(pOp, zPtr, sizeof(zPtr));
79312
+ dummyDb.mallocFailed = 1;
79313
+ zP4 = sqlite3VdbeDisplayP4(&dummyDb, pOp);
7898579314
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
78986
- displayComment(pOp, zP4, zCom, sizeof(zCom));
79315
+ zCom = sqlite3VdbeDisplayComment(0, pOp, zP4);
7898779316
#else
78988
- zCom[0] = 0;
79317
+ zCom = 0;
7898979318
#endif
7899079319
/* NB: The sqlite3OpcodeName() function is implemented by code created
7899179320
** by the mkopcodeh.awk and mkopcodec.awk scripts which extract the
7899279321
** information from the vdbe.c source text */
7899379322
fprintf(pOut, zFormat1, pc,
78994
- sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, zP4, pOp->p5,
78995
- zCom
79323
+ sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3,
79324
+ zP4 ? zP4 : "", pOp->p5,
79325
+ zCom ? zCom : ""
7899679326
);
7899779327
fflush(pOut);
79328
+ sqlite3_free(zP4);
79329
+ sqlite3_free(zCom);
7899879330
}
7899979331
#endif
7900079332
7900179333
/*
7900279334
** Initialize an array of N Mem element.
@@ -79083,10 +79415,125 @@
7908379415
assert( sqlite3VdbeFrameIsValid(pFrame) );
7908479416
pFrame->pParent = pFrame->v->pDelFrame;
7908579417
pFrame->v->pDelFrame = pFrame;
7908679418
}
7908779419
79420
+#if defined(SQLITE_ENABLE_BYTECODE_VTAB) || !defined(SQLITE_OMIT_EXPLAIN)
79421
+/*
79422
+** Locate the next opcode to be displayed in EXPLAIN or EXPLAIN
79423
+** QUERY PLAN output.
79424
+**
79425
+** Return SQLITE_ROW on success. Return SQLITE_DONE if there are no
79426
+** more opcodes to be displayed.
79427
+*/
79428
+SQLITE_PRIVATE int sqlite3VdbeNextOpcode(
79429
+ Vdbe *p, /* The statement being explained */
79430
+ Mem *pSub, /* Storage for keeping track of subprogram nesting */
79431
+ int eMode, /* 0: normal. 1: EQP. 2: TablesUsed */
79432
+ int *piPc, /* IN/OUT: Current rowid. Overwritten with next rowid */
79433
+ int *piAddr, /* OUT: Write index into (*paOp)[] here */
79434
+ Op **paOp /* OUT: Write the opcode array here */
79435
+){
79436
+ int nRow; /* Stop when row count reaches this */
79437
+ int nSub = 0; /* Number of sub-vdbes seen so far */
79438
+ SubProgram **apSub = 0; /* Array of sub-vdbes */
79439
+ int i; /* Next instruction address */
79440
+ int rc = SQLITE_OK; /* Result code */
79441
+ Op *aOp = 0; /* Opcode array */
79442
+ int iPc; /* Rowid. Copy of value in *piPc */
79443
+
79444
+ /* When the number of output rows reaches nRow, that means the
79445
+ ** listing has finished and sqlite3_step() should return SQLITE_DONE.
79446
+ ** nRow is the sum of the number of rows in the main program, plus
79447
+ ** the sum of the number of rows in all trigger subprograms encountered
79448
+ ** so far. The nRow value will increase as new trigger subprograms are
79449
+ ** encountered, but p->pc will eventually catch up to nRow.
79450
+ */
79451
+ nRow = p->nOp;
79452
+ if( pSub!=0 ){
79453
+ if( pSub->flags&MEM_Blob ){
79454
+ /* pSub is initiallly NULL. It is initialized to a BLOB by
79455
+ ** the P4_SUBPROGRAM processing logic below */
79456
+ nSub = pSub->n/sizeof(Vdbe*);
79457
+ apSub = (SubProgram **)pSub->z;
79458
+ }
79459
+ for(i=0; i<nSub; i++){
79460
+ nRow += apSub[i]->nOp;
79461
+ }
79462
+ }
79463
+ iPc = *piPc;
79464
+ while(1){ /* Loop exits via break */
79465
+ i = iPc++;
79466
+ if( i>=nRow ){
79467
+ p->rc = SQLITE_OK;
79468
+ rc = SQLITE_DONE;
79469
+ break;
79470
+ }
79471
+ if( i<p->nOp ){
79472
+ /* The rowid is small enough that we are still in the
79473
+ ** main program. */
79474
+ aOp = p->aOp;
79475
+ }else{
79476
+ /* We are currently listing subprograms. Figure out which one and
79477
+ ** pick up the appropriate opcode. */
79478
+ int j;
79479
+ i -= p->nOp;
79480
+ assert( apSub!=0 );
79481
+ assert( nSub>0 );
79482
+ for(j=0; i>=apSub[j]->nOp; j++){
79483
+ i -= apSub[j]->nOp;
79484
+ assert( i<apSub[j]->nOp || j+1<nSub );
79485
+ }
79486
+ aOp = apSub[j]->aOp;
79487
+ }
79488
+
79489
+ /* When an OP_Program opcode is encounter (the only opcode that has
79490
+ ** a P4_SUBPROGRAM argument), expand the size of the array of subprograms
79491
+ ** kept in p->aMem[9].z to hold the new program - assuming this subprogram
79492
+ ** has not already been seen.
79493
+ */
79494
+ if( pSub!=0 && aOp[i].p4type==P4_SUBPROGRAM ){
79495
+ int nByte = (nSub+1)*sizeof(SubProgram*);
79496
+ int j;
79497
+ for(j=0; j<nSub; j++){
79498
+ if( apSub[j]==aOp[i].p4.pProgram ) break;
79499
+ }
79500
+ if( j==nSub ){
79501
+ p->rc = sqlite3VdbeMemGrow(pSub, nByte, nSub!=0);
79502
+ if( p->rc!=SQLITE_OK ){
79503
+ rc = SQLITE_ERROR;
79504
+ break;
79505
+ }
79506
+ apSub = (SubProgram **)pSub->z;
79507
+ apSub[nSub++] = aOp[i].p4.pProgram;
79508
+ MemSetTypeFlag(pSub, MEM_Blob);
79509
+ pSub->n = nSub*sizeof(SubProgram*);
79510
+ nRow += aOp[i].p4.pProgram->nOp;
79511
+ }
79512
+ }
79513
+ if( eMode==0 ) break;
79514
+#ifdef SQLITE_ENABLE_BYTECODE_VTAB
79515
+ if( eMode==2 ){
79516
+ Op *pOp = aOp + i;
79517
+ if( pOp->opcode==OP_OpenRead ) break;
79518
+ if( pOp->opcode==OP_OpenWrite && (pOp->p5 & OPFLAG_P2ISREG)==0 ) break;
79519
+ if( pOp->opcode==OP_ReopenIdx ) break;
79520
+ }else
79521
+#endif
79522
+ {
79523
+ assert( eMode==1 );
79524
+ if( aOp[i].opcode==OP_Explain ) break;
79525
+ if( aOp[i].opcode==OP_Init && iPc>1 ) break;
79526
+ }
79527
+ }
79528
+ *piPc = iPc;
79529
+ *piAddr = i;
79530
+ *paOp = aOp;
79531
+ return rc;
79532
+}
79533
+#endif /* SQLITE_ENABLE_BYTECODE_VTAB || !SQLITE_OMIT_EXPLAIN */
79534
+
7908879535
7908979536
/*
7909079537
** Delete a VdbeFrame object and its contents. VdbeFrame objects are
7909179538
** allocated by the OP_Program opcode in sqlite3VdbeExec().
7909279539
*/
@@ -79123,20 +79570,18 @@
7912379570
** the trigger subprograms are listed one by one.
7912479571
*/
7912579572
SQLITE_PRIVATE int sqlite3VdbeList(
7912679573
Vdbe *p /* The VDBE */
7912779574
){
79128
- int nRow; /* Stop when row count reaches this */
79129
- int nSub = 0; /* Number of sub-vdbes seen so far */
79130
- SubProgram **apSub = 0; /* Array of sub-vdbes */
7913179575
Mem *pSub = 0; /* Memory cell hold array of subprogs */
7913279576
sqlite3 *db = p->db; /* The database connection */
7913379577
int i; /* Loop counter */
7913479578
int rc = SQLITE_OK; /* Return code */
7913579579
Mem *pMem = &p->aMem[1]; /* First Mem of result set */
7913679580
int bListSubprogs = (p->explain==1 || (db->flags & SQLITE_TriggerEQP)!=0);
79137
- Op *pOp = 0;
79581
+ Op *aOp; /* Array of opcodes */
79582
+ Op *pOp; /* Current opcode */
7913879583
7913979584
assert( p->explain );
7914079585
assert( p->magic==VDBE_MAGIC_RUN );
7914179586
assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || p->rc==SQLITE_NOMEM );
7914279587
@@ -79152,166 +79597,66 @@
7915279597
** sqlite3_column_text16() failed. */
7915379598
sqlite3OomFault(db);
7915479599
return SQLITE_ERROR;
7915579600
}
7915679601
79157
- /* When the number of output rows reaches nRow, that means the
79158
- ** listing has finished and sqlite3_step() should return SQLITE_DONE.
79159
- ** nRow is the sum of the number of rows in the main program, plus
79160
- ** the sum of the number of rows in all trigger subprograms encountered
79161
- ** so far. The nRow value will increase as new trigger subprograms are
79162
- ** encountered, but p->pc will eventually catch up to nRow.
79163
- */
79164
- nRow = p->nOp;
7916579602
if( bListSubprogs ){
7916679603
/* The first 8 memory cells are used for the result set. So we will
7916779604
** commandeer the 9th cell to use as storage for an array of pointers
7916879605
** to trigger subprograms. The VDBE is guaranteed to have at least 9
7916979606
** cells. */
7917079607
assert( p->nMem>9 );
7917179608
pSub = &p->aMem[9];
79172
- if( pSub->flags&MEM_Blob ){
79173
- /* On the first call to sqlite3_step(), pSub will hold a NULL. It is
79174
- ** initialized to a BLOB by the P4_SUBPROGRAM processing logic below */
79175
- nSub = pSub->n/sizeof(Vdbe*);
79176
- apSub = (SubProgram **)pSub->z;
79177
- }
79178
- for(i=0; i<nSub; i++){
79179
- nRow += apSub[i]->nOp;
79180
- }
79181
- }
79182
-
79183
- while(1){ /* Loop exits via break */
79184
- i = p->pc++;
79185
- if( i>=nRow ){
79186
- p->rc = SQLITE_OK;
79187
- rc = SQLITE_DONE;
79188
- break;
79189
- }
79190
- if( i<p->nOp ){
79191
- /* The output line number is small enough that we are still in the
79192
- ** main program. */
79193
- pOp = &p->aOp[i];
79194
- }else{
79195
- /* We are currently listing subprograms. Figure out which one and
79196
- ** pick up the appropriate opcode. */
79197
- int j;
79198
- i -= p->nOp;
79199
- assert( apSub!=0 );
79200
- assert( nSub>0 );
79201
- for(j=0; i>=apSub[j]->nOp; j++){
79202
- i -= apSub[j]->nOp;
79203
- assert( i<apSub[j]->nOp || j+1<nSub );
79204
- }
79205
- pOp = &apSub[j]->aOp[i];
79206
- }
79207
-
79208
- /* When an OP_Program opcode is encounter (the only opcode that has
79209
- ** a P4_SUBPROGRAM argument), expand the size of the array of subprograms
79210
- ** kept in p->aMem[9].z to hold the new program - assuming this subprogram
79211
- ** has not already been seen.
79212
- */
79213
- if( bListSubprogs && pOp->p4type==P4_SUBPROGRAM ){
79214
- int nByte = (nSub+1)*sizeof(SubProgram*);
79215
- int j;
79216
- for(j=0; j<nSub; j++){
79217
- if( apSub[j]==pOp->p4.pProgram ) break;
79218
- }
79219
- if( j==nSub ){
79220
- p->rc = sqlite3VdbeMemGrow(pSub, nByte, nSub!=0);
79221
- if( p->rc!=SQLITE_OK ){
79222
- rc = SQLITE_ERROR;
79223
- break;
79224
- }
79225
- apSub = (SubProgram **)pSub->z;
79226
- apSub[nSub++] = pOp->p4.pProgram;
79227
- pSub->flags |= MEM_Blob;
79228
- pSub->n = nSub*sizeof(SubProgram*);
79229
- nRow += pOp->p4.pProgram->nOp;
79230
- }
79231
- }
79232
- if( p->explain<2 ) break;
79233
- if( pOp->opcode==OP_Explain ) break;
79234
- if( pOp->opcode==OP_Init && p->pc>1 ) break;
79235
- }
79236
-
79237
- if( rc==SQLITE_OK ){
79238
- if( db->u1.isInterrupted ){
79609
+ }else{
79610
+ pSub = 0;
79611
+ }
79612
+
79613
+ /* Figure out which opcode is next to display */
79614
+ rc = sqlite3VdbeNextOpcode(p, pSub, p->explain==2, &p->pc, &i, &aOp);
79615
+
79616
+ if( rc==SQLITE_OK ){
79617
+ pOp = aOp + i;
79618
+ if( AtomicLoad(&db->u1.isInterrupted) ){
7923979619
p->rc = SQLITE_INTERRUPT;
7924079620
rc = SQLITE_ERROR;
7924179621
sqlite3VdbeError(p, sqlite3ErrStr(p->rc));
7924279622
}else{
79243
- char *zP4;
79244
- if( p->explain==1 ){
79245
- pMem->flags = MEM_Int;
79246
- pMem->u.i = i; /* Program counter */
79247
- pMem++;
79248
-
79249
- pMem->flags = MEM_Static|MEM_Str|MEM_Term;
79250
- pMem->z = (char*)sqlite3OpcodeName(pOp->opcode); /* Opcode */
79251
- assert( pMem->z!=0 );
79252
- pMem->n = sqlite3Strlen30(pMem->z);
79253
- pMem->enc = SQLITE_UTF8;
79254
- pMem++;
79255
- }
79256
-
79257
- pMem->flags = MEM_Int;
79258
- pMem->u.i = pOp->p1; /* P1 */
79259
- pMem++;
79260
-
79261
- pMem->flags = MEM_Int;
79262
- pMem->u.i = pOp->p2; /* P2 */
79263
- pMem++;
79264
-
79265
- pMem->flags = MEM_Int;
79266
- pMem->u.i = pOp->p3; /* P3 */
79267
- pMem++;
79268
-
79269
- if( sqlite3VdbeMemClearAndResize(pMem, 100) ){ /* P4 */
79270
- assert( p->db->mallocFailed );
79271
- return SQLITE_ERROR;
79272
- }
79273
- pMem->flags = MEM_Str|MEM_Term;
79274
- zP4 = displayP4(pOp, pMem->z, pMem->szMalloc);
79275
- if( zP4!=pMem->z ){
79276
- pMem->n = 0;
79277
- sqlite3VdbeMemSetStr(pMem, zP4, -1, SQLITE_UTF8, 0);
79623
+ char *zP4 = sqlite3VdbeDisplayP4(db, pOp);
79624
+ if( p->explain==2 ){
79625
+ sqlite3VdbeMemSetInt64(pMem, pOp->p1);
79626
+ sqlite3VdbeMemSetInt64(pMem+1, pOp->p2);
79627
+ sqlite3VdbeMemSetInt64(pMem+2, pOp->p3);
79628
+ sqlite3VdbeMemSetStr(pMem+3, zP4, -1, SQLITE_UTF8, sqlite3_free);
79629
+ p->nResColumn = 4;
7927879630
}else{
79279
- assert( pMem->z!=0 );
79280
- pMem->n = sqlite3Strlen30(pMem->z);
79281
- pMem->enc = SQLITE_UTF8;
79282
- }
79283
- pMem++;
79284
-
79285
- if( p->explain==1 ){
79286
- if( sqlite3VdbeMemClearAndResize(pMem, 4) ){
79287
- assert( p->db->mallocFailed );
79288
- return SQLITE_ERROR;
79289
- }
79290
- pMem->flags = MEM_Str|MEM_Term;
79291
- pMem->n = 2;
79292
- sqlite3_snprintf(3, pMem->z, "%.2x", pOp->p5); /* P5 */
79293
- pMem->enc = SQLITE_UTF8;
79294
- pMem++;
79295
-
79631
+ sqlite3VdbeMemSetInt64(pMem+0, i);
79632
+ sqlite3VdbeMemSetStr(pMem+1, (char*)sqlite3OpcodeName(pOp->opcode),
79633
+ -1, SQLITE_UTF8, SQLITE_STATIC);
79634
+ sqlite3VdbeMemSetInt64(pMem+2, pOp->p1);
79635
+ sqlite3VdbeMemSetInt64(pMem+3, pOp->p2);
79636
+ sqlite3VdbeMemSetInt64(pMem+4, pOp->p3);
79637
+ /* pMem+5 for p4 is done last */
79638
+ sqlite3VdbeMemSetInt64(pMem+6, pOp->p5);
7929679639
#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
79297
- if( sqlite3VdbeMemClearAndResize(pMem, 500) ){
79298
- assert( p->db->mallocFailed );
79299
- return SQLITE_ERROR;
79300
- }
79301
- pMem->flags = MEM_Str|MEM_Term;
79302
- pMem->n = displayComment(pOp, zP4, pMem->z, 500);
79303
- pMem->enc = SQLITE_UTF8;
79304
-#else
79305
- pMem->flags = MEM_Null; /* Comment */
79306
-#endif
79307
- }
79308
-
79309
- p->nResColumn = 8 - 4*(p->explain-1);
79310
- p->pResultSet = &p->aMem[1];
79311
- p->rc = SQLITE_OK;
79312
- rc = SQLITE_ROW;
79640
+ {
79641
+ char *zCom = sqlite3VdbeDisplayComment(db, pOp, zP4);
79642
+ sqlite3VdbeMemSetStr(pMem+7, zCom, -1, SQLITE_UTF8, sqlite3_free);
79643
+ }
79644
+#else
79645
+ sqlite3VdbeMemSetNull(pMem+7);
79646
+#endif
79647
+ sqlite3VdbeMemSetStr(pMem+5, zP4, -1, SQLITE_UTF8, sqlite3_free);
79648
+ p->nResColumn = 8;
79649
+ }
79650
+ p->pResultSet = pMem;
79651
+ if( db->mallocFailed ){
79652
+ p->rc = SQLITE_NOMEM;
79653
+ rc = SQLITE_ERROR;
79654
+ }else{
79655
+ p->rc = SQLITE_OK;
79656
+ rc = SQLITE_ROW;
79657
+ }
7931379658
}
7931479659
}
7931579660
return rc;
7931679661
}
7931779662
#endif /* SQLITE_OMIT_EXPLAIN */
@@ -79877,12 +80222,13 @@
7987780222
int retryCount = 0;
7987880223
int nMainFile;
7987980224
7988080225
/* Select a master journal file name */
7988180226
nMainFile = sqlite3Strlen30(zMainFile);
79882
- zMaster = sqlite3MPrintf(db, "%s-mjXXXXXX9XXz%c%c", zMainFile, 0, 0);
80227
+ zMaster = sqlite3MPrintf(db, "%.4c%s%.16c", 0,zMainFile,0);
7988380228
if( zMaster==0 ) return SQLITE_NOMEM_BKPT;
80229
+ zMaster += 4;
7988480230
do {
7988580231
u32 iRandom;
7988680232
if( retryCount ){
7988780233
if( retryCount>100 ){
7988880234
sqlite3_log(SQLITE_FULL, "MJ delete: %s", zMaster);
@@ -79908,11 +80254,11 @@
7990880254
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|
7990980255
SQLITE_OPEN_EXCLUSIVE|SQLITE_OPEN_MASTER_JOURNAL, 0
7991080256
);
7991180257
}
7991280258
if( rc!=SQLITE_OK ){
79913
- sqlite3DbFree(db, zMaster);
80259
+ sqlite3DbFree(db, zMaster-4);
7991480260
return rc;
7991580261
}
7991680262
7991780263
/* Write the name of each database file in the transaction into the new
7991880264
** master journal file. If an error occurs at this point close
@@ -79931,11 +80277,11 @@
7993180277
rc = sqlite3OsWrite(pMaster, zFile, sqlite3Strlen30(zFile)+1, offset);
7993280278
offset += sqlite3Strlen30(zFile)+1;
7993380279
if( rc!=SQLITE_OK ){
7993480280
sqlite3OsCloseFree(pMaster);
7993580281
sqlite3OsDelete(pVfs, zMaster, 0);
79936
- sqlite3DbFree(db, zMaster);
80282
+ sqlite3DbFree(db, zMaster-4);
7993780283
return rc;
7993880284
}
7993980285
}
7994080286
}
7994180287
@@ -79945,11 +80291,11 @@
7994580291
if( 0==(sqlite3OsDeviceCharacteristics(pMaster)&SQLITE_IOCAP_SEQUENTIAL)
7994680292
&& SQLITE_OK!=(rc = sqlite3OsSync(pMaster, SQLITE_SYNC_NORMAL))
7994780293
){
7994880294
sqlite3OsCloseFree(pMaster);
7994980295
sqlite3OsDelete(pVfs, zMaster, 0);
79950
- sqlite3DbFree(db, zMaster);
80296
+ sqlite3DbFree(db, zMaster-4);
7995180297
return rc;
7995280298
}
7995380299
7995480300
/* Sync all the db files involved in the transaction. The same call
7995580301
** sets the master journal pointer in each individual journal. If
@@ -79968,20 +80314,20 @@
7996880314
}
7996980315
}
7997080316
sqlite3OsCloseFree(pMaster);
7997180317
assert( rc!=SQLITE_BUSY );
7997280318
if( rc!=SQLITE_OK ){
79973
- sqlite3DbFree(db, zMaster);
80319
+ sqlite3DbFree(db, zMaster-4);
7997480320
return rc;
7997580321
}
7997680322
7997780323
/* Delete the master journal file. This commits the transaction. After
7997880324
** doing this the directory is synced again before any individual
7997980325
** transaction files are deleted.
7998080326
*/
7998180327
rc = sqlite3OsDelete(pVfs, zMaster, 1);
79982
- sqlite3DbFree(db, zMaster);
80328
+ sqlite3DbFree(db, zMaster-4);
7998380329
zMaster = 0;
7998480330
if( rc ){
7998580331
return rc;
7998680332
}
7998780333
@@ -83028,11 +83374,11 @@
8302883374
/* If there are no other statements currently running, then
8302983375
** reset the interrupt flag. This prevents a call to sqlite3_interrupt
8303083376
** from interrupting a statement that has not yet started.
8303183377
*/
8303283378
if( db->nVdbeActive==0 ){
83033
- db->u1.isInterrupted = 0;
83379
+ AtomicStore(&db->u1.isInterrupted, 0);
8303483380
}
8303583381
8303683382
assert( db->nVdbeWrite>0 || db->autoCommit==0
8303783383
|| (db->nDeferredCons==0 && db->nDeferredImmCons==0)
8303883384
);
@@ -85413,11 +85759,11 @@
8541385759
assert( p->bIsReader || p->readOnly!=0 );
8541485760
p->iCurrentTime = 0;
8541585761
assert( p->explain==0 );
8541685762
p->pResultSet = 0;
8541785763
db->busyHandler.nBusy = 0;
85418
- if( db->u1.isInterrupted ) goto abort_due_to_interrupt;
85764
+ if( AtomicLoad(&db->u1.isInterrupted) ) goto abort_due_to_interrupt;
8541985765
sqlite3VdbeIOTraceSql(p);
8542085766
#ifdef SQLITE_DEBUG
8542185767
sqlite3BeginBenignMalloc();
8542285768
if( p->pc==0
8542385769
&& (p->db->flags & (SQLITE_VdbeListing|SQLITE_VdbeEQP|SQLITE_VdbeTrace))!=0
@@ -85597,11 +85943,11 @@
8559785943
** But that is not due to sloppy coding habits. The code is written this
8559885944
** way for performance, to avoid having to run the interrupt and progress
8559985945
** checks on every opcode. This helps sqlite3_step() to run about 1.5%
8560085946
** faster according to "valgrind --tool=cachegrind" */
8560185947
check_for_interrupt:
85602
- if( db->u1.isInterrupted ) goto abort_due_to_interrupt;
85948
+ if( AtomicLoad(&db->u1.isInterrupted) ) goto abort_due_to_interrupt;
8560385949
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
8560485950
/* Call the progress callback if it is configured and the required number
8560585951
** of VDBE ops have been executed (either since this invocation of
8560685952
** sqlite3VdbeExec() or since last time the progress callback was called).
8560785953
** If the progress callback returns non-zero, exit the virtual machine with
@@ -87893,32 +88239,38 @@
8789388239
assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
8789488240
REGISTER_TRACE(pOp->p3, pOut);
8789588241
break;
8789688242
}
8789788243
87898
-/* Opcode: Count P1 P2 * * *
88244
+/* Opcode: Count P1 P2 p3 * *
8789988245
** Synopsis: r[P2]=count()
8790088246
**
8790188247
** Store the number of entries (an integer value) in the table or index
87902
-** opened by cursor P1 in register P2
88248
+** opened by cursor P1 in register P2.
88249
+**
88250
+** If P3==0, then an exact count is obtained, which involves visiting
88251
+** every btree page of the table. But if P3 is non-zero, an estimate
88252
+** is returned based on the current cursor position.
8790388253
*/
87904
-#ifndef SQLITE_OMIT_BTREECOUNT
8790588254
case OP_Count: { /* out2 */
8790688255
i64 nEntry;
8790788256
BtCursor *pCrsr;
8790888257
8790988258
assert( p->apCsr[pOp->p1]->eCurType==CURTYPE_BTREE );
8791088259
pCrsr = p->apCsr[pOp->p1]->uc.pCursor;
8791188260
assert( pCrsr );
87912
- nEntry = 0; /* Not needed. Only used to silence a warning. */
87913
- rc = sqlite3BtreeCount(db, pCrsr, &nEntry);
87914
- if( rc ) goto abort_due_to_error;
88261
+ if( pOp->p3 ){
88262
+ nEntry = sqlite3BtreeRowCountEst(pCrsr);
88263
+ }else{
88264
+ nEntry = 0; /* Not needed. Only used to silence a warning. */
88265
+ rc = sqlite3BtreeCount(db, pCrsr, &nEntry);
88266
+ if( rc ) goto abort_due_to_error;
88267
+ }
8791588268
pOut = out2Prerelease(p, pOp);
8791688269
pOut->u.i = nEntry;
8791788270
goto check_for_interrupt;
8791888271
}
87919
-#endif
8792088272
8792188273
/* Opcode: Savepoint P1 * * P4 *
8792288274
**
8792388275
** Open, release or rollback the savepoint named by parameter P4, depending
8792488276
** on the value of P1. To open a new savepoint set P1==0 (SAVEPOINT_BEGIN).
@@ -90352,16 +90704,23 @@
9035290704
rc = sqlite3VdbeSorterWrite(pC, pIn2);
9035390705
if( rc) goto abort_due_to_error;
9035490706
break;
9035590707
}
9035690708
90357
-/* Opcode: IdxDelete P1 P2 P3 * *
90709
+/* Opcode: IdxDelete P1 P2 P3 * P5
9035890710
** Synopsis: key=r[P2@P3]
9035990711
**
9036090712
** The content of P3 registers starting at register P2 form
9036190713
** an unpacked index key. This opcode removes that entry from the
9036290714
** index opened by cursor P1.
90715
+**
90716
+** If P5 is not zero, then raise an SQLITE_CORRUPT_INDEX error
90717
+** if no matching index entry is found. This happens when running
90718
+** an UPDATE or DELETE statement and the index entry to be updated
90719
+** or deleted is not found. For some uses of IdxDelete
90720
+** (example: the EXCEPT operator) it does not matter that no matching
90721
+** entry is found. For those cases, P5 is zero.
9036390722
*/
9036490723
case OP_IdxDelete: {
9036590724
VdbeCursor *pC;
9036690725
BtCursor *pCrsr;
9036790726
int res;
@@ -90374,20 +90733,22 @@
9037490733
assert( pC!=0 );
9037590734
assert( pC->eCurType==CURTYPE_BTREE );
9037690735
sqlite3VdbeIncrWriteCounter(p, pC);
9037790736
pCrsr = pC->uc.pCursor;
9037890737
assert( pCrsr!=0 );
90379
- assert( pOp->p5==0 );
9038090738
r.pKeyInfo = pC->pKeyInfo;
9038190739
r.nField = (u16)pOp->p3;
9038290740
r.default_rc = 0;
9038390741
r.aMem = &aMem[pOp->p2];
9038490742
rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res);
9038590743
if( rc ) goto abort_due_to_error;
9038690744
if( res==0 ){
9038790745
rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE);
9038890746
if( rc ) goto abort_due_to_error;
90747
+ }else if( pOp->p5 ){
90748
+ rc = SQLITE_CORRUPT_INDEX;
90749
+ goto abort_due_to_error;
9038990750
}
9039090751
assert( pC->deferredMoveto==0 );
9039190752
pC->cacheStatus = CACHE_STALE;
9039290753
pC->seekResult = 0;
9039390754
break;
@@ -92705,11 +93066,11 @@
9270593066
9270693067
/* Jump to here if the sqlite3_interrupt() API sets the interrupt
9270793068
** flag.
9270893069
*/
9270993070
abort_due_to_interrupt:
92710
- assert( db->u1.isInterrupted );
93071
+ assert( AtomicLoad(&db->u1.isInterrupted) );
9271193072
rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT;
9271293073
p->rc = rc;
9271393074
sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc));
9271493075
goto abort_due_to_error;
9271593076
}
@@ -95988,10 +96349,435 @@
9598896349
*pRes = sqlite3VdbeRecordCompare(pVal->n, pVal->z, r2);
9598996350
return SQLITE_OK;
9599096351
}
9599196352
9599296353
/************** End of vdbesort.c ********************************************/
96354
+/************** Begin file vdbevtab.c ****************************************/
96355
+/*
96356
+** 2020-03-23
96357
+**
96358
+** The author disclaims copyright to this source code. In place of
96359
+** a legal notice, here is a blessing:
96360
+**
96361
+** May you do good and not evil.
96362
+** May you find forgiveness for yourself and forgive others.
96363
+** May you share freely, never taking more than you give.
96364
+**
96365
+*************************************************************************
96366
+**
96367
+** This file implements virtual-tables for examining the bytecode content
96368
+** of a prepared statement.
96369
+*/
96370
+#ifdef SQLITE_ENABLE_BYTECODE_VTAB
96371
+/* #include "sqliteInt.h" */
96372
+/* #include "vdbeInt.h" */
96373
+
96374
+/* An instance of the bytecode() table-valued function.
96375
+*/
96376
+typedef struct bytecodevtab bytecodevtab;
96377
+struct bytecodevtab {
96378
+ sqlite3_vtab base; /* Base class - must be first */
96379
+ sqlite3 *db; /* Database connection */
96380
+ int bTablesUsed; /* 2 for tables_used(). 0 for bytecode(). */
96381
+};
96382
+
96383
+/* A cursor for scanning through the bytecode
96384
+*/
96385
+typedef struct bytecodevtab_cursor bytecodevtab_cursor;
96386
+struct bytecodevtab_cursor {
96387
+ sqlite3_vtab_cursor base; /* Base class - must be first */
96388
+ sqlite3_stmt *pStmt; /* The statement whose bytecode is displayed */
96389
+ int iRowid; /* The rowid of the output table */
96390
+ int iAddr; /* Address */
96391
+ int needFinalize; /* Cursors owns pStmt and must finalize it */
96392
+ int showSubprograms; /* Provide a listing of subprograms */
96393
+ Op *aOp; /* Operand array */
96394
+ char *zP4; /* Rendered P4 value */
96395
+ const char *zType; /* tables_used.type */
96396
+ const char *zSchema; /* tables_used.schema */
96397
+ const char *zName; /* tables_used.name */
96398
+ Mem sub; /* Subprograms */
96399
+};
96400
+
96401
+/*
96402
+** Create a new bytecode() table-valued function.
96403
+*/
96404
+static int bytecodevtabConnect(
96405
+ sqlite3 *db,
96406
+ void *pAux,
96407
+ int argc, const char *const*argv,
96408
+ sqlite3_vtab **ppVtab,
96409
+ char **pzErr
96410
+){
96411
+ bytecodevtab *pNew;
96412
+ int rc;
96413
+ int isTabUsed = pAux!=0;
96414
+ const char *azSchema[2] = {
96415
+ /* bytecode() schema */
96416
+ "CREATE TABLE x("
96417
+ "addr INT,"
96418
+ "opcode TEXT,"
96419
+ "p1 INT,"
96420
+ "p2 INT,"
96421
+ "p3 INT,"
96422
+ "p4 TEXT,"
96423
+ "p5 INT,"
96424
+ "comment TEXT,"
96425
+ "subprog TEXT,"
96426
+ "stmt HIDDEN"
96427
+ ");",
96428
+
96429
+ /* Tables_used() schema */
96430
+ "CREATE TABLE x("
96431
+ "type TEXT,"
96432
+ "schema TEXT,"
96433
+ "name TEXT,"
96434
+ "wr INT,"
96435
+ "subprog TEXT,"
96436
+ "stmt HIDDEN"
96437
+ ");"
96438
+ };
96439
+
96440
+ rc = sqlite3_declare_vtab(db, azSchema[isTabUsed]);
96441
+ if( rc==SQLITE_OK ){
96442
+ pNew = sqlite3_malloc( sizeof(*pNew) );
96443
+ *ppVtab = (sqlite3_vtab*)pNew;
96444
+ if( pNew==0 ) return SQLITE_NOMEM;
96445
+ memset(pNew, 0, sizeof(*pNew));
96446
+ pNew->db = db;
96447
+ pNew->bTablesUsed = isTabUsed*2;
96448
+ }
96449
+ return rc;
96450
+}
96451
+
96452
+/*
96453
+** This method is the destructor for bytecodevtab objects.
96454
+*/
96455
+static int bytecodevtabDisconnect(sqlite3_vtab *pVtab){
96456
+ bytecodevtab *p = (bytecodevtab*)pVtab;
96457
+ sqlite3_free(p);
96458
+ return SQLITE_OK;
96459
+}
96460
+
96461
+/*
96462
+** Constructor for a new bytecodevtab_cursor object.
96463
+*/
96464
+static int bytecodevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
96465
+ bytecodevtab *pVTab = (bytecodevtab*)p;
96466
+ bytecodevtab_cursor *pCur;
96467
+ pCur = sqlite3_malloc( sizeof(*pCur) );
96468
+ if( pCur==0 ) return SQLITE_NOMEM;
96469
+ memset(pCur, 0, sizeof(*pCur));
96470
+ sqlite3VdbeMemInit(&pCur->sub, pVTab->db, 1);
96471
+ *ppCursor = &pCur->base;
96472
+ return SQLITE_OK;
96473
+}
96474
+
96475
+/*
96476
+** Clear all internal content from a bytecodevtab cursor.
96477
+*/
96478
+static void bytecodevtabCursorClear(bytecodevtab_cursor *pCur){
96479
+ sqlite3_free(pCur->zP4);
96480
+ pCur->zP4 = 0;
96481
+ sqlite3VdbeMemRelease(&pCur->sub);
96482
+ sqlite3VdbeMemSetNull(&pCur->sub);
96483
+ if( pCur->needFinalize ){
96484
+ sqlite3_finalize(pCur->pStmt);
96485
+ }
96486
+ pCur->pStmt = 0;
96487
+ pCur->needFinalize = 0;
96488
+ pCur->zType = 0;
96489
+ pCur->zSchema = 0;
96490
+ pCur->zName = 0;
96491
+}
96492
+
96493
+/*
96494
+** Destructor for a bytecodevtab_cursor.
96495
+*/
96496
+static int bytecodevtabClose(sqlite3_vtab_cursor *cur){
96497
+ bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
96498
+ bytecodevtabCursorClear(pCur);
96499
+ sqlite3_free(pCur);
96500
+ return SQLITE_OK;
96501
+}
96502
+
96503
+
96504
+/*
96505
+** Advance a bytecodevtab_cursor to its next row of output.
96506
+*/
96507
+static int bytecodevtabNext(sqlite3_vtab_cursor *cur){
96508
+ bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
96509
+ bytecodevtab *pTab = (bytecodevtab*)cur->pVtab;
96510
+ int rc;
96511
+ if( pCur->zP4 ){
96512
+ sqlite3_free(pCur->zP4);
96513
+ pCur->zP4 = 0;
96514
+ }
96515
+ if( pCur->zName ){
96516
+ pCur->zName = 0;
96517
+ pCur->zType = 0;
96518
+ pCur->zSchema = 0;
96519
+ }
96520
+ rc = sqlite3VdbeNextOpcode(
96521
+ (Vdbe*)pCur->pStmt,
96522
+ pCur->showSubprograms ? &pCur->sub : 0,
96523
+ pTab->bTablesUsed,
96524
+ &pCur->iRowid,
96525
+ &pCur->iAddr,
96526
+ &pCur->aOp);
96527
+ if( rc!=SQLITE_OK ){
96528
+ sqlite3VdbeMemSetNull(&pCur->sub);
96529
+ pCur->aOp = 0;
96530
+ }
96531
+ return SQLITE_OK;
96532
+}
96533
+
96534
+/*
96535
+** Return TRUE if the cursor has been moved off of the last
96536
+** row of output.
96537
+*/
96538
+static int bytecodevtabEof(sqlite3_vtab_cursor *cur){
96539
+ bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
96540
+ return pCur->aOp==0;
96541
+}
96542
+
96543
+/*
96544
+** Return values of columns for the row at which the bytecodevtab_cursor
96545
+** is currently pointing.
96546
+*/
96547
+static int bytecodevtabColumn(
96548
+ sqlite3_vtab_cursor *cur, /* The cursor */
96549
+ sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
96550
+ int i /* Which column to return */
96551
+){
96552
+ bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
96553
+ bytecodevtab *pVTab = (bytecodevtab*)cur->pVtab;
96554
+ Op *pOp = pCur->aOp + pCur->iAddr;
96555
+ if( pVTab->bTablesUsed ){
96556
+ if( i==4 ){
96557
+ i = 8;
96558
+ }else{
96559
+ if( i<=2 && pCur->zType==0 ){
96560
+ Schema *pSchema;
96561
+ HashElem *k;
96562
+ int iDb = pOp->p3;
96563
+ int iRoot = pOp->p2;
96564
+ sqlite3 *db = pVTab->db;
96565
+ pSchema = db->aDb[iDb].pSchema;
96566
+ pCur->zSchema = db->aDb[iDb].zDbSName;
96567
+ for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){
96568
+ Table *pTab = (Table*)sqliteHashData(k);
96569
+ if( !IsVirtual(pTab) && pTab->tnum==iRoot ){
96570
+ pCur->zName = pTab->zName;
96571
+ pCur->zType = "table";
96572
+ break;
96573
+ }
96574
+ }
96575
+ if( pCur->zName==0 ){
96576
+ for(k=sqliteHashFirst(&pSchema->idxHash); k; k=sqliteHashNext(k)){
96577
+ Index *pIdx = (Index*)sqliteHashData(k);
96578
+ if( pIdx->tnum==iRoot ){
96579
+ pCur->zName = pIdx->zName;
96580
+ pCur->zType = "index";
96581
+ }
96582
+ }
96583
+ }
96584
+ }
96585
+ i += 10;
96586
+ }
96587
+ }
96588
+ switch( i ){
96589
+ case 0: /* addr */
96590
+ sqlite3_result_int(ctx, pCur->iAddr);
96591
+ break;
96592
+ case 1: /* opcode */
96593
+ sqlite3_result_text(ctx, (char*)sqlite3OpcodeName(pOp->opcode),
96594
+ -1, SQLITE_STATIC);
96595
+ break;
96596
+ case 2: /* p1 */
96597
+ sqlite3_result_int(ctx, pOp->p1);
96598
+ break;
96599
+ case 3: /* p2 */
96600
+ sqlite3_result_int(ctx, pOp->p2);
96601
+ break;
96602
+ case 4: /* p3 */
96603
+ sqlite3_result_int(ctx, pOp->p3);
96604
+ break;
96605
+ case 5: /* p4 */
96606
+ case 7: /* comment */
96607
+ if( pCur->zP4==0 ){
96608
+ pCur->zP4 = sqlite3VdbeDisplayP4(pVTab->db, pOp);
96609
+ }
96610
+ if( i==5 ){
96611
+ sqlite3_result_text(ctx, pCur->zP4, -1, SQLITE_STATIC);
96612
+ }else{
96613
+#ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
96614
+ char *zCom = sqlite3VdbeDisplayComment(pVTab->db, pOp, pCur->zP4);
96615
+ sqlite3_result_text(ctx, zCom, -1, sqlite3_free);
96616
+#endif
96617
+ }
96618
+ break;
96619
+ case 6: /* p5 */
96620
+ sqlite3_result_int(ctx, pOp->p5);
96621
+ break;
96622
+ case 8: { /* subprog */
96623
+ Op *aOp = pCur->aOp;
96624
+ assert( aOp[0].opcode==OP_Init );
96625
+ assert( aOp[0].p4.z==0 || strncmp(aOp[0].p4.z,"-" "- ",3)==0 );
96626
+ if( pCur->iRowid==pCur->iAddr+1 ){
96627
+ break; /* Result is NULL for the main program */
96628
+ }else if( aOp[0].p4.z!=0 ){
96629
+ sqlite3_result_text(ctx, aOp[0].p4.z+3, -1, SQLITE_STATIC);
96630
+ }else{
96631
+ sqlite3_result_text(ctx, "(FK)", 4, SQLITE_STATIC);
96632
+ }
96633
+ break;
96634
+ }
96635
+ case 10: /* tables_used.type */
96636
+ sqlite3_result_text(ctx, pCur->zType, -1, SQLITE_STATIC);
96637
+ break;
96638
+ case 11: /* tables_used.schema */
96639
+ sqlite3_result_text(ctx, pCur->zSchema, -1, SQLITE_STATIC);
96640
+ break;
96641
+ case 12: /* tables_used.name */
96642
+ sqlite3_result_text(ctx, pCur->zName, -1, SQLITE_STATIC);
96643
+ break;
96644
+ case 13: /* tables_used.wr */
96645
+ sqlite3_result_int(ctx, pOp->opcode==OP_OpenWrite);
96646
+ break;
96647
+ }
96648
+ return SQLITE_OK;
96649
+}
96650
+
96651
+/*
96652
+** Return the rowid for the current row. In this implementation, the
96653
+** rowid is the same as the output value.
96654
+*/
96655
+static int bytecodevtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
96656
+ bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
96657
+ *pRowid = pCur->iRowid;
96658
+ return SQLITE_OK;
96659
+}
96660
+
96661
+/*
96662
+** Initialize a cursor.
96663
+**
96664
+** idxNum==0 means show all subprograms
96665
+** idxNum==1 means show only the main bytecode and omit subprograms.
96666
+*/
96667
+static int bytecodevtabFilter(
96668
+ sqlite3_vtab_cursor *pVtabCursor,
96669
+ int idxNum, const char *idxStr,
96670
+ int argc, sqlite3_value **argv
96671
+){
96672
+ bytecodevtab_cursor *pCur = (bytecodevtab_cursor *)pVtabCursor;
96673
+ bytecodevtab *pVTab = (bytecodevtab *)pVtabCursor->pVtab;
96674
+ int rc = SQLITE_OK;
96675
+
96676
+ bytecodevtabCursorClear(pCur);
96677
+ pCur->iRowid = 0;
96678
+ pCur->iAddr = 0;
96679
+ pCur->showSubprograms = idxNum==0;
96680
+ assert( argc==1 );
96681
+ if( sqlite3_value_type(argv[0])==SQLITE_TEXT ){
96682
+ const char *zSql = (const char*)sqlite3_value_text(argv[0]);
96683
+ if( zSql==0 ){
96684
+ rc = SQLITE_NOMEM;
96685
+ }else{
96686
+ rc = sqlite3_prepare_v2(pVTab->db, zSql, -1, &pCur->pStmt, 0);
96687
+ pCur->needFinalize = 1;
96688
+ }
96689
+ }else{
96690
+ pCur->pStmt = (sqlite3_stmt*)sqlite3_value_pointer(argv[0],"stmt-pointer");
96691
+ }
96692
+ if( pCur->pStmt==0 ){
96693
+ pVTab->base.zErrMsg = sqlite3_mprintf(
96694
+ "argument to %s() is not a valid SQL statement",
96695
+ pVTab->bTablesUsed ? "tables_used" : "bytecode"
96696
+ );
96697
+ rc = SQLITE_ERROR;
96698
+ }else{
96699
+ bytecodevtabNext(pVtabCursor);
96700
+ }
96701
+ return rc;
96702
+}
96703
+
96704
+/*
96705
+** We must have a single stmt=? constraint that will be passed through
96706
+** into the xFilter method. If there is no valid stmt=? constraint,
96707
+** then return an SQLITE_CONSTRAINT error.
96708
+*/
96709
+static int bytecodevtabBestIndex(
96710
+ sqlite3_vtab *tab,
96711
+ sqlite3_index_info *pIdxInfo
96712
+){
96713
+ int i;
96714
+ int rc = SQLITE_CONSTRAINT;
96715
+ struct sqlite3_index_constraint *p;
96716
+ bytecodevtab *pVTab = (bytecodevtab*)tab;
96717
+ int iBaseCol = pVTab->bTablesUsed ? 4 : 8;
96718
+ pIdxInfo->estimatedCost = (double)100;
96719
+ pIdxInfo->estimatedRows = 100;
96720
+ pIdxInfo->idxNum = 0;
96721
+ for(i=0, p=pIdxInfo->aConstraint; i<pIdxInfo->nConstraint; i++, p++){
96722
+ if( p->usable==0 ) continue;
96723
+ if( p->op==SQLITE_INDEX_CONSTRAINT_EQ && p->iColumn==iBaseCol+1 ){
96724
+ rc = SQLITE_OK;
96725
+ pIdxInfo->aConstraintUsage[i].omit = 1;
96726
+ pIdxInfo->aConstraintUsage[i].argvIndex = 1;
96727
+ }
96728
+ if( p->op==SQLITE_INDEX_CONSTRAINT_ISNULL && p->iColumn==iBaseCol ){
96729
+ pIdxInfo->aConstraintUsage[i].omit = 1;
96730
+ pIdxInfo->idxNum = 1;
96731
+ }
96732
+ }
96733
+ return rc;
96734
+}
96735
+
96736
+/*
96737
+** This following structure defines all the methods for the
96738
+** virtual table.
96739
+*/
96740
+static sqlite3_module bytecodevtabModule = {
96741
+ /* iVersion */ 0,
96742
+ /* xCreate */ 0,
96743
+ /* xConnect */ bytecodevtabConnect,
96744
+ /* xBestIndex */ bytecodevtabBestIndex,
96745
+ /* xDisconnect */ bytecodevtabDisconnect,
96746
+ /* xDestroy */ 0,
96747
+ /* xOpen */ bytecodevtabOpen,
96748
+ /* xClose */ bytecodevtabClose,
96749
+ /* xFilter */ bytecodevtabFilter,
96750
+ /* xNext */ bytecodevtabNext,
96751
+ /* xEof */ bytecodevtabEof,
96752
+ /* xColumn */ bytecodevtabColumn,
96753
+ /* xRowid */ bytecodevtabRowid,
96754
+ /* xUpdate */ 0,
96755
+ /* xBegin */ 0,
96756
+ /* xSync */ 0,
96757
+ /* xCommit */ 0,
96758
+ /* xRollback */ 0,
96759
+ /* xFindMethod */ 0,
96760
+ /* xRename */ 0,
96761
+ /* xSavepoint */ 0,
96762
+ /* xRelease */ 0,
96763
+ /* xRollbackTo */ 0,
96764
+ /* xShadowName */ 0
96765
+};
96766
+
96767
+
96768
+SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3 *db){
96769
+ int rc;
96770
+ rc = sqlite3_create_module(db, "bytecode", &bytecodevtabModule, 0);
96771
+ if( rc==SQLITE_OK ){
96772
+ rc = sqlite3_create_module(db, "tables_used", &bytecodevtabModule, &db);
96773
+ }
96774
+ return rc;
96775
+}
96776
+#endif /* SQLITE_ENABLE_BYTECODE_VTAB */
96777
+
96778
+/************** End of vdbevtab.c ********************************************/
9599396779
/************** Begin file memjournal.c **************************************/
9599496780
/*
9599596781
** 2008 October 7
9599696782
**
9599796783
** The author disclaims copyright to this source code. In place of
@@ -96775,11 +97561,11 @@
9677597561
const char *zTab,
9677697562
const char *zDb
9677797563
){
9677897564
int n;
9677997565
const char *zSpan;
96780
- if( NEVER(pItem->eEName!=ENAME_TAB) ) return 0;
97566
+ if( pItem->eEName!=ENAME_TAB ) return 0;
9678197567
zSpan = pItem->zEName;
9678297568
for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){}
9678397569
if( zDb && (sqlite3StrNICmp(zSpan, zDb, n)!=0 || zDb[n]!=0) ){
9678497570
return 0;
9678597571
}
@@ -96929,10 +97715,11 @@
9692997715
ExprList *pEList;
9693097716
SrcList *pSrcList = pNC->pSrcList;
9693197717
9693297718
if( pSrcList ){
9693397719
for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){
97720
+ u8 hCol;
9693497721
pTab = pItem->pTab;
9693597722
assert( pTab!=0 && pTab->zName!=0 );
9693697723
assert( pTab->nCol>0 );
9693797724
if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){
9693897725
int hit = 0;
@@ -96962,12 +97749,13 @@
9696297749
}
9696397750
}
9696497751
if( 0==(cntTab++) ){
9696597752
pMatch = pItem;
9696697753
}
97754
+ hCol = sqlite3StrIHash(zCol);
9696797755
for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){
96968
- if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
97756
+ if( pCol->hName==hCol && sqlite3StrICmp(pCol->zName, zCol)==0 ){
9696997757
/* If there has been exactly one prior match and this match
9697097758
** is for the right-hand table of a NATURAL JOIN or is in a
9697197759
** USING clause, then skip this match.
9697297760
*/
9697397761
if( cnt==1 ){
@@ -97024,14 +97812,15 @@
9702497812
}
9702597813
#endif /* SQLITE_OMIT_UPSERT */
9702697814
9702797815
if( pTab ){
9702897816
int iCol;
97817
+ u8 hCol = sqlite3StrIHash(zCol);
9702997818
pSchema = pTab->pSchema;
9703097819
cntTab++;
9703197820
for(iCol=0, pCol=pTab->aCol; iCol<pTab->nCol; iCol++, pCol++){
97032
- if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
97821
+ if( pCol->hName==hCol && sqlite3StrICmp(pCol->zName, zCol)==0 ){
9703397822
if( iCol==pTab->iPKey ){
9703497823
iCol = -1;
9703597824
}
9703697825
break;
9703797826
}
@@ -97828,11 +98617,11 @@
9782898617
nc.uNC.pEList = pEList;
9782998618
nc.ncFlags = NC_AllowAgg|NC_UEList;
9783098619
nc.nErr = 0;
9783198620
db = pParse->db;
9783298621
savedSuppErr = db->suppressErr;
97833
- db->suppressErr = 1;
98622
+ if( IN_RENAME_OBJECT==0 ) db->suppressErr = 1;
9783498623
rc = sqlite3ResolveExprNames(&nc, pE);
9783598624
db->suppressErr = savedSuppErr;
9783698625
if( rc ) return 0;
9783798626
9783898627
/* Try to match the ORDER BY expression against an expression
@@ -98463,15 +99252,45 @@
9846399252
SQLITE_PRIVATE int sqlite3ResolveExprListNames(
9846499253
NameContext *pNC, /* Namespace to resolve expressions in. */
9846599254
ExprList *pList /* The expression list to be analyzed. */
9846699255
){
9846799256
int i;
98468
- if( pList ){
98469
- for(i=0; i<pList->nExpr; i++){
98470
- if( sqlite3ResolveExprNames(pNC, pList->a[i].pExpr) ) return WRC_Abort;
99257
+ int savedHasAgg = 0;
99258
+ Walker w;
99259
+ if( pList==0 ) return WRC_Continue;
99260
+ w.pParse = pNC->pParse;
99261
+ w.xExprCallback = resolveExprStep;
99262
+ w.xSelectCallback = resolveSelectStep;
99263
+ w.xSelectCallback2 = 0;
99264
+ w.u.pNC = pNC;
99265
+ savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin);
99266
+ pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin);
99267
+ for(i=0; i<pList->nExpr; i++){
99268
+ Expr *pExpr = pList->a[i].pExpr;
99269
+ if( pExpr==0 ) continue;
99270
+#if SQLITE_MAX_EXPR_DEPTH>0
99271
+ w.pParse->nHeight += pExpr->nHeight;
99272
+ if( sqlite3ExprCheckHeight(w.pParse, w.pParse->nHeight) ){
99273
+ return WRC_Abort;
9847199274
}
99275
+#endif
99276
+ sqlite3WalkExpr(&w, pExpr);
99277
+#if SQLITE_MAX_EXPR_DEPTH>0
99278
+ w.pParse->nHeight -= pExpr->nHeight;
99279
+#endif
99280
+ assert( EP_Agg==NC_HasAgg );
99281
+ assert( EP_Win==NC_HasWin );
99282
+ testcase( pNC->ncFlags & NC_HasAgg );
99283
+ testcase( pNC->ncFlags & NC_HasWin );
99284
+ if( pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin) ){
99285
+ ExprSetProperty(pExpr, pNC->ncFlags & (NC_HasAgg|NC_HasWin) );
99286
+ savedHasAgg |= pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin);
99287
+ pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin);
99288
+ }
99289
+ if( pNC->nErr>0 || w.pParse->nErr>0 ) return WRC_Abort;
9847299290
}
99291
+ pNC->ncFlags |= savedHasAgg;
9847399292
return WRC_Continue;
9847499293
}
9847599294
9847699295
/*
9847799296
** Resolve all names in all expressions of a SELECT and in all
@@ -98601,11 +99420,11 @@
9860199420
** SELECT * FROM t1 WHERE (select a from t1);
9860299421
*/
9860399422
SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr){
9860499423
int op;
9860599424
while( ExprHasProperty(pExpr, EP_Skip) ){
98606
- assert( pExpr->op==TK_COLLATE );
99425
+ assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW );
9860799426
pExpr = pExpr->pLeft;
9860899427
assert( pExpr!=0 );
9860999428
}
9861099429
op = pExpr->op;
9861199430
if( op==TK_SELECT ){
@@ -98668,11 +99487,11 @@
9866899487
/*
9866999488
** Skip over any TK_COLLATE operators.
9867099489
*/
9867199490
SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr *pExpr){
9867299491
while( pExpr && ExprHasProperty(pExpr, EP_Skip) ){
98673
- assert( pExpr->op==TK_COLLATE );
99492
+ assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW );
9867499493
pExpr = pExpr->pLeft;
9867599494
}
9867699495
return pExpr;
9867799496
}
9867899497
@@ -98687,11 +99506,11 @@
9868799506
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
9868899507
assert( pExpr->x.pList->nExpr>0 );
9868999508
assert( pExpr->op==TK_FUNCTION );
9869099509
pExpr = pExpr->x.pList->a[0].pExpr;
9869199510
}else{
98692
- assert( pExpr->op==TK_COLLATE );
99511
+ assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW );
9869399512
pExpr = pExpr->pLeft;
9869499513
}
9869599514
}
9869699515
return pExpr;
9869799516
}
@@ -100343,20 +101162,26 @@
100343101162
ExprList *pList, /* List to which to add the span. */
100344101163
Token *pName, /* Name to be added */
100345101164
int dequote /* True to cause the name to be dequoted */
100346101165
){
100347101166
assert( pList!=0 || pParse->db->mallocFailed!=0 );
101167
+ assert( pParse->eParseMode!=PARSE_MODE_UNMAP || dequote==0 );
100348101168
if( pList ){
100349101169
struct ExprList_item *pItem;
100350101170
assert( pList->nExpr>0 );
100351101171
pItem = &pList->a[pList->nExpr-1];
100352101172
assert( pItem->zEName==0 );
100353101173
assert( pItem->eEName==ENAME_NAME );
100354101174
pItem->zEName = sqlite3DbStrNDup(pParse->db, pName->z, pName->n);
100355
- if( dequote ) sqlite3Dequote(pItem->zEName);
100356
- if( IN_RENAME_OBJECT ){
100357
- sqlite3RenameTokenMap(pParse, (void*)pItem->zEName, pName);
101175
+ if( dequote ){
101176
+ /* If dequote==0, then pName->z does not point to part of a DDL
101177
+ ** statement handled by the parser. And so no token need be added
101178
+ ** to the token-map. */
101179
+ sqlite3Dequote(pItem->zEName);
101180
+ if( IN_RENAME_OBJECT ){
101181
+ sqlite3RenameTokenMap(pParse, (void*)pItem->zEName, pName);
101182
+ }
100358101183
}
100359101184
}
100360101185
}
100361101186
100362101187
/*
@@ -101497,10 +102322,12 @@
101497102322
struct ExprList_item *pItem;
101498102323
int r1, r2;
101499102324
affinity = sqlite3ExprAffinity(pLeft);
101500102325
if( affinity<=SQLITE_AFF_NONE ){
101501102326
affinity = SQLITE_AFF_BLOB;
102327
+ }else if( affinity==SQLITE_AFF_REAL ){
102328
+ affinity = SQLITE_AFF_NUMERIC;
101502102329
}
101503102330
if( pKeyInfo ){
101504102331
assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
101505102332
pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
101506102333
}
@@ -101735,10 +102562,11 @@
101735102562
int destStep6 = 0; /* Start of code for Step 6 */
101736102563
int addrTruthOp; /* Address of opcode that determines the IN is true */
101737102564
int destNotNull; /* Jump here if a comparison is not true in step 6 */
101738102565
int addrTop; /* Top of the step-6 loop */
101739102566
int iTab = 0; /* Index to use */
102567
+ u8 okConstFactor = pParse->okConstFactor;
101740102568
101741102569
assert( !ExprHasVVAProperty(pExpr,EP_Immutable) );
101742102570
pLeft = pExpr->pLeft;
101743102571
if( sqlite3ExprCheckIN(pParse, pExpr) ) return;
101744102572
zAff = exprINAffinity(pParse, pExpr);
@@ -101779,12 +102607,18 @@
101779102607
**
101780102608
** sqlite3FindInIndex() might have reordered the fields of the LHS vector
101781102609
** so that the fields are in the same order as an existing index. The
101782102610
** aiMap[] array contains a mapping from the original LHS field order to
101783102611
** the field order that matches the RHS index.
101784
- */
102612
+ **
102613
+ ** Avoid factoring the LHS of the IN(...) expression out of the loop,
102614
+ ** even if it is constant, as OP_Affinity may be used on the register
102615
+ ** by code generated below. */
102616
+ assert( pParse->okConstFactor==okConstFactor );
102617
+ pParse->okConstFactor = 0;
101785102618
rLhsOrig = exprCodeVector(pParse, pLeft, &iDummy);
102619
+ pParse->okConstFactor = okConstFactor;
101786102620
for(i=0; i<nVector && aiMap[i]==i; i++){} /* Are LHS fields reordered? */
101787102621
if( i==nVector ){
101788102622
/* LHS fields are not reordered */
101789102623
rLhs = rLhsOrig;
101790102624
}else{
@@ -101806,25 +102640,17 @@
101806102640
CollSeq *pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
101807102641
int labelOk = sqlite3VdbeMakeLabel(pParse);
101808102642
int r2, regToFree;
101809102643
int regCkNull = 0;
101810102644
int ii;
101811
- int bLhsReal; /* True if the LHS of the IN has REAL affinity */
101812102645
assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
101813102646
if( destIfNull!=destIfFalse ){
101814102647
regCkNull = sqlite3GetTempReg(pParse);
101815102648
sqlite3VdbeAddOp3(v, OP_BitAnd, rLhs, rLhs, regCkNull);
101816102649
}
101817
- bLhsReal = sqlite3ExprAffinity(pExpr->pLeft)==SQLITE_AFF_REAL;
101818102650
for(ii=0; ii<pList->nExpr; ii++){
101819
- if( bLhsReal ){
101820
- r2 = regToFree = sqlite3GetTempReg(pParse);
101821
- sqlite3ExprCode(pParse, pList->a[ii].pExpr, r2);
101822
- sqlite3VdbeAddOp4(v, OP_Affinity, r2, 1, 0, "E", P4_STATIC);
101823
- }else{
101824
- r2 = sqlite3ExprCodeTemp(pParse, pList->a[ii].pExpr, &regToFree);
101825
- }
102651
+ r2 = sqlite3ExprCodeTemp(pParse, pList->a[ii].pExpr, &regToFree);
101826102652
if( regCkNull && sqlite3ExprCanBeNull(pList->a[ii].pExpr) ){
101827102653
sqlite3VdbeAddOp3(v, OP_BitAnd, regCkNull, r2, regCkNull);
101828102654
}
101829102655
sqlite3ReleaseTempReg(pParse, regToFree);
101830102656
if( ii<pList->nExpr-1 || destIfNull!=destIfFalse ){
@@ -102394,14 +103220,10 @@
102394103220
}
102395103221
if( aff>SQLITE_AFF_BLOB ){
102396103222
static const char zAff[] = "B\000C\000D\000E";
102397103223
assert( SQLITE_AFF_BLOB=='A' );
102398103224
assert( SQLITE_AFF_TEXT=='B' );
102399
- if( iReg!=target ){
102400
- sqlite3VdbeAddOp2(v, OP_SCopy, iReg, target);
102401
- iReg = target;
102402
- }
102403103225
sqlite3VdbeAddOp4(v, OP_Affinity, iReg, 1, 0,
102404103226
&zAff[(aff-'B')*2], P4_STATIC);
102405103227
}
102406103228
return iReg;
102407103229
}
@@ -103035,11 +103857,11 @@
103035103857
assert( pExpr->affExpr==OE_Rollback
103036103858
|| pExpr->affExpr==OE_Abort
103037103859
|| pExpr->affExpr==OE_Fail
103038103860
|| pExpr->affExpr==OE_Ignore
103039103861
);
103040
- if( !pParse->pTriggerTab ){
103862
+ if( !pParse->pTriggerTab && !pParse->nested ){
103041103863
sqlite3ErrorMsg(pParse,
103042103864
"RAISE() may only be used within a trigger-program");
103043103865
return 0;
103044103866
}
103045103867
if( pExpr->affExpr==OE_Abort ){
@@ -103049,12 +103871,13 @@
103049103871
if( pExpr->affExpr==OE_Ignore ){
103050103872
sqlite3VdbeAddOp4(
103051103873
v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0);
103052103874
VdbeCoverage(v);
103053103875
}else{
103054
- sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_TRIGGER,
103055
- pExpr->affExpr, pExpr->u.zToken, 0, 0);
103876
+ sqlite3HaltConstraint(pParse,
103877
+ pParse->pTriggerTab ? SQLITE_CONSTRAINT_TRIGGER : SQLITE_ERROR,
103878
+ pExpr->affExpr, pExpr->u.zToken, 0, 0);
103056103879
}
103057103880
103058103881
break;
103059103882
}
103060103883
#endif
@@ -104805,10 +105628,26 @@
104805105628
exit_rename_table:
104806105629
sqlite3SrcListDelete(db, pSrc);
104807105630
sqlite3DbFree(db, zName);
104808105631
db->mDbFlags = savedDbFlags;
104809105632
}
105633
+
105634
+/*
105635
+** Write code that will raise an error if the table described by
105636
+** zDb and zTab is not empty.
105637
+*/
105638
+static void sqlite3ErrorIfNotEmpty(
105639
+ Parse *pParse, /* Parsing context */
105640
+ const char *zDb, /* Schema holding the table */
105641
+ const char *zTab, /* Table to check for empty */
105642
+ const char *zErr /* Error message text */
105643
+){
105644
+ sqlite3NestedParse(pParse,
105645
+ "SELECT raise(ABORT,%Q) FROM \"%w\".\"%w\"",
105646
+ zErr, zDb, zTab
105647
+ );
105648
+}
104810105649
104811105650
/*
104812105651
** This function is called after an "ALTER TABLE ... ADD" statement
104813105652
** has been parsed. Argument pColDef contains the text of the new
104814105653
** column definition.
@@ -104858,11 +105697,12 @@
104858105697
if( pCol->colFlags & COLFLAG_PRIMKEY ){
104859105698
sqlite3ErrorMsg(pParse, "Cannot add a PRIMARY KEY column");
104860105699
return;
104861105700
}
104862105701
if( pNew->pIndex ){
104863
- sqlite3ErrorMsg(pParse, "Cannot add a UNIQUE column");
105702
+ sqlite3ErrorMsg(pParse,
105703
+ "Cannot add a UNIQUE column");
104864105704
return;
104865105705
}
104866105706
if( (pCol->colFlags & COLFLAG_GENERATED)==0 ){
104867105707
/* If the default value for the new column was specified with a
104868105708
** literal NULL, then set pDflt to 0. This simplifies checking
@@ -104871,19 +105711,18 @@
104871105711
assert( pDflt==0 || pDflt->op==TK_SPAN );
104872105712
if( pDflt && pDflt->pLeft->op==TK_NULL ){
104873105713
pDflt = 0;
104874105714
}
104875105715
if( (db->flags&SQLITE_ForeignKeys) && pNew->pFKey && pDflt ){
104876
- sqlite3ErrorMsg(pParse,
105716
+ sqlite3ErrorIfNotEmpty(pParse, zDb, zTab,
104877105717
"Cannot add a REFERENCES column with non-NULL default value");
104878
- return;
104879105718
}
104880105719
if( pCol->notNull && !pDflt ){
104881
- sqlite3ErrorMsg(pParse,
105720
+ sqlite3ErrorIfNotEmpty(pParse, zDb, zTab,
104882105721
"Cannot add a NOT NULL column with default value NULL");
104883
- return;
104884105722
}
105723
+
104885105724
104886105725
/* Ensure the default expression is something that sqlite3ValueFromExpr()
104887105726
** can handle (i.e. not CURRENT_TIME etc.)
104888105727
*/
104889105728
if( pDflt ){
@@ -104894,18 +105733,17 @@
104894105733
if( rc!=SQLITE_OK ){
104895105734
assert( db->mallocFailed == 1 );
104896105735
return;
104897105736
}
104898105737
if( !pVal ){
104899
- sqlite3ErrorMsg(pParse,"Cannot add a column with non-constant default");
104900
- return;
105738
+ sqlite3ErrorIfNotEmpty(pParse, zDb, zTab,
105739
+ "Cannot add a column with non-constant default");
104901105740
}
104902105741
sqlite3ValueFree(pVal);
104903105742
}
104904105743
}else if( pCol->colFlags & COLFLAG_STORED ){
104905
- sqlite3ErrorMsg(pParse, "cannot add a STORED column");
104906
- return;
105744
+ sqlite3ErrorIfNotEmpty(pParse, zDb, zTab, "cannot add a STORED column");
104907105745
}
104908105746
104909105747
104910105748
/* Modify the CREATE TABLE statement. */
104911105749
zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n);
@@ -105020,10 +105858,11 @@
105020105858
}
105021105859
memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol);
105022105860
for(i=0; i<pNew->nCol; i++){
105023105861
Column *pCol = &pNew->aCol[i];
105024105862
pCol->zName = sqlite3DbStrDup(db, pCol->zName);
105863
+ pCol->hName = sqlite3StrIHash(pCol->zName);
105025105864
pCol->zColl = 0;
105026105865
pCol->pDflt = 0;
105027105866
}
105028105867
pNew->pSchema = db->aDb[iDb].pSchema;
105029105868
pNew->addColOffset = pTab->addColOffset;
@@ -105248,11 +106087,11 @@
105248106087
*/
105249106088
SQLITE_PRIVATE void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){
105250106089
RenameToken *pNew;
105251106090
assert( pPtr || pParse->db->mallocFailed );
105252106091
renameTokenCheckAll(pParse, pPtr);
105253
- if( pParse->eParseMode!=PARSE_MODE_UNMAP ){
106092
+ if( ALWAYS(pParse->eParseMode!=PARSE_MODE_UNMAP) ){
105254106093
pNew = sqlite3DbMallocZero(pParse->db, sizeof(RenameToken));
105255106094
if( pNew ){
105256106095
pNew->p = pPtr;
105257106096
pNew->t = *pToken;
105258106097
pNew->pNext = pParse->pRename;
@@ -105305,10 +106144,25 @@
105305106144
sqlite3WalkSelect(pWalker, p);
105306106145
sqlite3RenameExprlistUnmap(pWalker->pParse, pWith->a[i].pCols);
105307106146
}
105308106147
}
105309106148
}
106149
+
106150
+/*
106151
+** Unmap all tokens in the IdList object passed as the second argument.
106152
+*/
106153
+static void unmapColumnIdlistNames(
106154
+ Parse *pParse,
106155
+ IdList *pIdList
106156
+){
106157
+ if( pIdList ){
106158
+ int ii;
106159
+ for(ii=0; ii<pIdList->nId; ii++){
106160
+ sqlite3RenameTokenRemap(pParse, 0, (void*)pIdList->a[ii].zName);
106161
+ }
106162
+ }
106163
+}
105310106164
105311106165
/*
105312106166
** Walker callback used by sqlite3RenameExprUnmap().
105313106167
*/
105314106168
static int renameUnmapSelectCb(Walker *pWalker, Select *p){
@@ -105327,10 +106181,11 @@
105327106181
if( ALWAYS(p->pSrc) ){ /* Every Select as a SrcList, even if it is empty */
105328106182
SrcList *pSrc = p->pSrc;
105329106183
for(i=0; i<pSrc->nSrc; i++){
105330106184
sqlite3RenameTokenRemap(pParse, 0, (void*)pSrc->a[i].zName);
105331106185
if( sqlite3WalkExpr(pWalker, pSrc->a[i].pOn) ) return WRC_Abort;
106186
+ unmapColumnIdlistNames(pParse, pSrc->a[i].pUsing);
105332106187
}
105333106188
}
105334106189
105335106190
renameWalkWith(pWalker, p);
105336106191
return WRC_Continue;
@@ -105534,10 +106389,11 @@
105534106389
renameTokenFind(pParse, pCtx, (void*)zName);
105535106390
}
105536106391
}
105537106392
}
105538106393
}
106394
+
105539106395
105540106396
/*
105541106397
** Parse the SQL statement zSql using Parse object (*p). The Parse object
105542106398
** is initialized by this function before it is used.
105543106399
*/
@@ -106446,10 +107302,15 @@
106446107302
sqlite3 *db = pParse->db;
106447107303
Db *pDb;
106448107304
Vdbe *v = sqlite3GetVdbe(pParse);
106449107305
int aRoot[ArraySize(aTable)];
106450107306
u8 aCreateTbl[ArraySize(aTable)];
107307
+#ifdef SQLITE_ENABLE_STAT4
107308
+ const int nToOpen = OptimizationEnabled(db,SQLITE_Stat4) ? 2 : 1;
107309
+#else
107310
+ const int nToOpen = 1;
107311
+#endif
106451107312
106452107313
if( v==0 ) return;
106453107314
assert( sqlite3BtreeHoldsAllMutexes(db) );
106454107315
assert( sqlite3VdbeDb(v)==db );
106455107316
pDb = &db->aDb[iDb];
@@ -106458,12 +107319,13 @@
106458107319
** if they do already exist.
106459107320
*/
106460107321
for(i=0; i<ArraySize(aTable); i++){
106461107322
const char *zTab = aTable[i].zName;
106462107323
Table *pStat;
107324
+ aCreateTbl[i] = 0;
106463107325
if( (pStat = sqlite3FindTable(db, zTab, pDb->zDbSName))==0 ){
106464
- if( aTable[i].zCols ){
107326
+ if( i<nToOpen ){
106465107327
/* The sqlite_statN table does not exist. Create it. Note that a
106466107328
** side-effect of the CREATE TABLE statement is to leave the rootpage
106467107329
** of the new table in register pParse->regRoot. This is important
106468107330
** because the OpenWrite opcode below will be needing it. */
106469107331
sqlite3NestedParse(pParse,
@@ -106475,11 +107337,10 @@
106475107337
}else{
106476107338
/* The table already exists. If zWhere is not NULL, delete all entries
106477107339
** associated with the table zWhere. If zWhere is NULL, delete the
106478107340
** entire contents of the table. */
106479107341
aRoot[i] = pStat->tnum;
106480
- aCreateTbl[i] = 0;
106481107342
sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab);
106482107343
if( zWhere ){
106483107344
sqlite3NestedParse(pParse,
106484107345
"DELETE FROM %Q.%s WHERE %s=%Q",
106485107346
pDb->zDbSName, zTab, zWhereType, zWhere
@@ -106494,11 +107355,11 @@
106494107355
}
106495107356
}
106496107357
}
106497107358
106498107359
/* Open the sqlite_stat[134] tables for writing. */
106499
- for(i=0; aTable[i].zCols; i++){
107360
+ for(i=0; i<nToOpen; i++){
106500107361
assert( i<ArraySize(aTable) );
106501107362
sqlite3VdbeAddOp4Int(v, OP_OpenWrite, iStatCur+i, aRoot[i], iDb, 3);
106502107363
sqlite3VdbeChangeP5(v, aCreateTbl[i]);
106503107364
VdbeComment((v, aTable[i].zName));
106504107365
}
@@ -106533,13 +107394,16 @@
106533107394
u32 iHash; /* Tiebreaker hash */
106534107395
#endif
106535107396
};
106536107397
struct StatAccum {
106537107398
sqlite3 *db; /* Database connection, for malloc() */
106538
- tRowcnt nRow; /* Number of rows in the entire table */
107399
+ tRowcnt nEst; /* Estimated number of rows */
107400
+ tRowcnt nRow; /* Number of rows visited so far */
107401
+ int nLimit; /* Analysis row-scan limit */
106539107402
int nCol; /* Number of columns in index + pk/rowid */
106540107403
int nKeyCol; /* Number of index columns w/o the pk/rowid */
107404
+ u8 nSkipAhead; /* Number of times of skip-ahead */
106541107405
StatSample current; /* Current row as a StatSample */
106542107406
#ifdef SQLITE_ENABLE_STAT4
106543107407
tRowcnt nPSample; /* How often to do a periodic sample */
106544107408
int mxSample; /* Maximum number of samples to accumulate */
106545107409
u32 iPrn; /* Pseudo-random number used for sampling */
@@ -106615,31 +107479,32 @@
106615107479
** Reclaim all memory of a StatAccum structure.
106616107480
*/
106617107481
static void statAccumDestructor(void *pOld){
106618107482
StatAccum *p = (StatAccum*)pOld;
106619107483
#ifdef SQLITE_ENABLE_STAT4
106620
- int i;
106621
- for(i=0; i<p->nCol; i++) sampleClear(p->db, p->aBest+i);
106622
- for(i=0; i<p->mxSample; i++) sampleClear(p->db, p->a+i);
106623
- sampleClear(p->db, &p->current);
107484
+ if( p->mxSample ){
107485
+ int i;
107486
+ for(i=0; i<p->nCol; i++) sampleClear(p->db, p->aBest+i);
107487
+ for(i=0; i<p->mxSample; i++) sampleClear(p->db, p->a+i);
107488
+ sampleClear(p->db, &p->current);
107489
+ }
106624107490
#endif
106625107491
sqlite3DbFree(p->db, p);
106626107492
}
106627107493
106628107494
/*
106629
-** Implementation of the stat_init(N,K,C) SQL function. The three parameters
107495
+** Implementation of the stat_init(N,K,C,L) SQL function. The four parameters
106630107496
** are:
106631107497
** N: The number of columns in the index including the rowid/pk (note 1)
106632107498
** K: The number of columns in the index excluding the rowid/pk.
106633
-** C: The number of rows in the index (note 2)
107499
+** C: Estimated number of rows in the index
107500
+** L: A limit on the number of rows to scan, or 0 for no-limit
106634107501
**
106635107502
** Note 1: In the special case of the covering index that implements a
106636107503
** WITHOUT ROWID table, N is the number of PRIMARY KEY columns, not the
106637107504
** total number of columns in the table.
106638107505
**
106639
-** Note 2: C is only used for STAT4.
106640
-**
106641107506
** For indexes on ordinary rowid tables, N==K+1. But for indexes on
106642107507
** WITHOUT ROWID tables, N=K+P where P is the number of columns in the
106643107508
** PRIMARY KEY of the table. The covering index that implements the
106644107509
** original WITHOUT ROWID table as N==K as a special case.
106645107510
**
@@ -106656,13 +107521,14 @@
106656107521
StatAccum *p;
106657107522
int nCol; /* Number of columns in index being sampled */
106658107523
int nKeyCol; /* Number of key columns */
106659107524
int nColUp; /* nCol rounded up for alignment */
106660107525
int n; /* Bytes of space to allocate */
106661
- sqlite3 *db; /* Database connection */
107526
+ sqlite3 *db = sqlite3_context_db_handle(context); /* Database connection */
106662107527
#ifdef SQLITE_ENABLE_STAT4
106663
- int mxSample = SQLITE_STAT4_SAMPLES;
107528
+ /* Maximum number of samples. 0 if STAT4 data is not collected */
107529
+ int mxSample = OptimizationEnabled(db,SQLITE_Stat4) ?SQLITE_STAT4_SAMPLES :0;
106664107530
#endif
106665107531
106666107532
/* Decode the three function arguments */
106667107533
UNUSED_PARAMETER(argc);
106668107534
nCol = sqlite3_value_int(argv[0]);
@@ -106673,39 +107539,43 @@
106673107539
assert( nKeyCol>0 );
106674107540
106675107541
/* Allocate the space required for the StatAccum object */
106676107542
n = sizeof(*p)
106677107543
+ sizeof(tRowcnt)*nColUp /* StatAccum.anEq */
106678
- + sizeof(tRowcnt)*nColUp /* StatAccum.anDLt */
107544
+ + sizeof(tRowcnt)*nColUp; /* StatAccum.anDLt */
106679107545
#ifdef SQLITE_ENABLE_STAT4
106680
- + sizeof(tRowcnt)*nColUp /* StatAccum.anLt */
106681
- + sizeof(StatSample)*(nCol+mxSample) /* StatAccum.aBest[], a[] */
106682
- + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample)
107546
+ if( mxSample ){
107547
+ n += sizeof(tRowcnt)*nColUp /* StatAccum.anLt */
107548
+ + sizeof(StatSample)*(nCol+mxSample) /* StatAccum.aBest[], a[] */
107549
+ + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample);
107550
+ }
106683107551
#endif
106684
- ;
106685107552
db = sqlite3_context_db_handle(context);
106686107553
p = sqlite3DbMallocZero(db, n);
106687107554
if( p==0 ){
106688107555
sqlite3_result_error_nomem(context);
106689107556
return;
106690107557
}
106691107558
106692107559
p->db = db;
107560
+ p->nEst = sqlite3_value_int64(argv[2]);
106693107561
p->nRow = 0;
107562
+ p->nLimit = sqlite3_value_int64(argv[3]);
106694107563
p->nCol = nCol;
106695107564
p->nKeyCol = nKeyCol;
107565
+ p->nSkipAhead = 0;
106696107566
p->current.anDLt = (tRowcnt*)&p[1];
106697107567
p->current.anEq = &p->current.anDLt[nColUp];
106698107568
106699107569
#ifdef SQLITE_ENABLE_STAT4
106700
- {
107570
+ p->mxSample = p->nLimit==0 ? mxSample : 0;
107571
+ if( mxSample ){
106701107572
u8 *pSpace; /* Allocated space not yet assigned */
106702107573
int i; /* Used to iterate through p->aSample[] */
106703107574
106704107575
p->iGet = -1;
106705
- p->mxSample = mxSample;
106706
- p->nPSample = (tRowcnt)(sqlite3_value_int64(argv[2])/(mxSample/3+1) + 1);
107576
+ p->nPSample = (tRowcnt)(p->nEst/(mxSample/3+1) + 1);
106707107577
p->current.anLt = &p->current.anEq[nColUp];
106708107578
p->iPrn = 0x689e962d*(u32)nCol ^ 0xd0944565*(u32)sqlite3_value_int(argv[2]);
106709107579
106710107580
/* Set up the StatAccum.a[] and aBest[] arrays */
106711107581
p->a = (struct StatSample*)&p->current.anLt[nColUp];
@@ -106729,11 +107599,11 @@
106729107599
** (given by the 3rd parameter) is never used and can be any positive
106730107600
** value. */
106731107601
sqlite3_result_blob(context, p, sizeof(*p), statAccumDestructor);
106732107602
}
106733107603
static const FuncDef statInitFuncdef = {
106734
- 2+IsStat4, /* nArg */
107604
+ 4, /* nArg */
106735107605
SQLITE_UTF8, /* funcFlags */
106736107606
0, /* pUserData */
106737107607
0, /* pNext */
106738107608
statInit, /* xSFunc */
106739107609
0, /* xFinalize */
@@ -106933,14 +107803,17 @@
106933107803
** P Pointer to the StatAccum object created by stat_init()
106934107804
** C Index of left-most column to differ from previous row
106935107805
** R Rowid for the current row. Might be a key record for
106936107806
** WITHOUT ROWID tables.
106937107807
**
106938
-** This SQL function always returns NULL. It's purpose it to accumulate
106939
-** statistical data and/or samples in the StatAccum object about the
106940
-** index being analyzed. The stat_get() SQL function will later be used to
106941
-** extract relevant information for constructing the sqlite_statN tables.
107808
+** The purpose of this routine is to collect statistical data and/or
107809
+** samples from the index being analyzed into the StatAccum object.
107810
+** The stat_get() SQL function will be used afterwards to
107811
+** retrieve the information gathered.
107812
+**
107813
+** This SQL function usually returns NULL, but might return an integer
107814
+** if it wants the byte-code to do special processing.
106942107815
**
106943107816
** The R parameter is only used for STAT4
106944107817
*/
106945107818
static void statPush(
106946107819
sqlite3_context *context,
@@ -106962,11 +107835,11 @@
106962107835
/* This is the first call to this function. Do initialization. */
106963107836
for(i=0; i<p->nCol; i++) p->current.anEq[i] = 1;
106964107837
}else{
106965107838
/* Second and subsequent calls get processed here */
106966107839
#ifdef SQLITE_ENABLE_STAT4
106967
- samplePushPrevious(p, iChng);
107840
+ if( p->mxSample ) samplePushPrevious(p, iChng);
106968107841
#endif
106969107842
106970107843
/* Update anDLt[], anLt[] and anEq[] to reflect the values that apply
106971107844
** to the current row of the index. */
106972107845
for(i=0; i<iChng; i++){
@@ -106973,30 +107846,29 @@
106973107846
p->current.anEq[i]++;
106974107847
}
106975107848
for(i=iChng; i<p->nCol; i++){
106976107849
p->current.anDLt[i]++;
106977107850
#ifdef SQLITE_ENABLE_STAT4
106978
- p->current.anLt[i] += p->current.anEq[i];
107851
+ if( p->mxSample ) p->current.anLt[i] += p->current.anEq[i];
106979107852
#endif
106980107853
p->current.anEq[i] = 1;
106981107854
}
106982107855
}
107856
+
106983107857
p->nRow++;
106984107858
#ifdef SQLITE_ENABLE_STAT4
106985
- if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){
106986
- sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2]));
106987
- }else{
106988
- sampleSetRowid(p->db, &p->current, sqlite3_value_bytes(argv[2]),
106989
- sqlite3_value_blob(argv[2]));
106990
- }
106991
- p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345;
106992
-#endif
106993
-
106994
-#ifdef SQLITE_ENABLE_STAT4
106995
- {
106996
- tRowcnt nLt = p->current.anLt[p->nCol-1];
106997
-
107859
+ if( p->mxSample ){
107860
+ tRowcnt nLt;
107861
+ if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){
107862
+ sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2]));
107863
+ }else{
107864
+ sampleSetRowid(p->db, &p->current, sqlite3_value_bytes(argv[2]),
107865
+ sqlite3_value_blob(argv[2]));
107866
+ }
107867
+ p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345;
107868
+
107869
+ nLt = p->current.anLt[p->nCol-1];
106998107870
/* Check if this is to be a periodic sample. If so, add it. */
106999107871
if( (nLt/p->nPSample)!=(nLt+1)/p->nPSample ){
107000107872
p->current.isPSample = 1;
107001107873
p->current.iCol = 0;
107002107874
sampleInsert(p, &p->current, p->nCol-1);
@@ -107008,13 +107880,18 @@
107008107880
p->current.iCol = i;
107009107881
if( i>=iChng || sampleIsBetterPost(p, &p->current, &p->aBest[i]) ){
107010107882
sampleCopy(p, &p->aBest[i], &p->current);
107011107883
}
107012107884
}
107885
+ }else
107886
+#endif
107887
+ if( p->nLimit && p->nRow>(tRowcnt)p->nLimit*(p->nSkipAhead+1) ){
107888
+ p->nSkipAhead++;
107889
+ sqlite3_result_int(context, p->current.anDLt[0]>0);
107013107890
}
107014
-#endif
107015107891
}
107892
+
107016107893
static const FuncDef statPushFuncdef = {
107017107894
2+IsStat4, /* nArg */
107018107895
SQLITE_UTF8, /* funcFlags */
107019107896
0, /* pUserData */
107020107897
0, /* pNext */
@@ -107062,10 +107939,11 @@
107062107939
assert( argc==2 );
107063107940
assert( eCall==STAT_GET_STAT1 || eCall==STAT_GET_NEQ
107064107941
|| eCall==STAT_GET_ROWID || eCall==STAT_GET_NLT
107065107942
|| eCall==STAT_GET_NDLT
107066107943
);
107944
+ assert( eCall==STAT_GET_STAT1 || p->mxSample );
107067107945
if( eCall==STAT_GET_STAT1 )
107068107946
#else
107069107947
assert( argc==1 );
107070107948
#endif
107071107949
{
@@ -107097,11 +107975,12 @@
107097107975
if( zRet==0 ){
107098107976
sqlite3_result_error_nomem(context);
107099107977
return;
107100107978
}
107101107979
107102
- sqlite3_snprintf(24, zRet, "%llu", (u64)p->nRow);
107980
+ sqlite3_snprintf(24, zRet, "%llu",
107981
+ p->nSkipAhead ? (u64)p->nEst : (u64)p->nRow);
107103107982
z = zRet + sqlite3Strlen30(zRet);
107104107983
for(i=0; i<p->nKeyCol; i++){
107105107984
u64 nDistinct = p->current.anDLt[i] + 1;
107106107985
u64 iVal = (p->nRow + nDistinct - 1) / nDistinct;
107107107986
sqlite3_snprintf(24, z, " %llu", iVal);
@@ -107173,20 +108052,20 @@
107173108052
0, 0, /* xValue, xInverse */
107174108053
"stat_get", /* zName */
107175108054
{0}
107176108055
};
107177108056
107178
-static void callStatGet(Parse *pParse, int regStat4, int iParam, int regOut){
108057
+static void callStatGet(Parse *pParse, int regStat, int iParam, int regOut){
107179108058
#ifdef SQLITE_ENABLE_STAT4
107180
- sqlite3VdbeAddOp2(pParse->pVdbe, OP_Integer, iParam, regStat4+1);
108059
+ sqlite3VdbeAddOp2(pParse->pVdbe, OP_Integer, iParam, regStat+1);
107181108060
#elif SQLITE_DEBUG
107182108061
assert( iParam==STAT_GET_STAT1 );
107183108062
#else
107184108063
UNUSED_PARAMETER( iParam );
107185108064
#endif
107186
- assert( regOut!=regStat4 && regOut!=regStat4+1 );
107187
- sqlite3VdbeAddFunctionCall(pParse, 0, regStat4, regOut, 1+IsStat4,
108065
+ assert( regOut!=regStat && regOut!=regStat+1 );
108066
+ sqlite3VdbeAddFunctionCall(pParse, 0, regStat, regOut, 1+IsStat4,
107188108067
&statGetFuncdef, 0);
107189108068
}
107190108069
107191108070
/*
107192108071
** Generate code to do an analysis of all indices associated with
@@ -107208,16 +108087,15 @@
107208108087
int i; /* Loop counter */
107209108088
int jZeroRows = -1; /* Jump from here if number of rows is zero */
107210108089
int iDb; /* Index of database containing pTab */
107211108090
u8 needTableCnt = 1; /* True to count the table */
107212108091
int regNewRowid = iMem++; /* Rowid for the inserted record */
107213
- int regStat4 = iMem++; /* Register to hold StatAccum object */
108092
+ int regStat = iMem++; /* Register to hold StatAccum object */
107214108093
int regChng = iMem++; /* Index of changed index field */
107215
-#ifdef SQLITE_ENABLE_STAT4
107216108094
int regRowid = iMem++; /* Rowid argument passed to stat_push() */
107217
-#endif
107218108095
int regTemp = iMem++; /* Temporary use register */
108096
+ int regTemp2 = iMem++; /* Second temporary use register */
107219108097
int regTabname = iMem++; /* Register containing table name */
107220108098
int regIdxname = iMem++; /* Register containing index name */
107221108099
int regStat1 = iMem++; /* Value for the stat column of sqlite_stat1 */
107222108100
int regPrev = iMem; /* MUST BE LAST (see below) */
107223108101
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
@@ -107341,21 +108219,30 @@
107341108219
/* Invoke the stat_init() function. The arguments are:
107342108220
**
107343108221
** (1) the number of columns in the index including the rowid
107344108222
** (or for a WITHOUT ROWID table, the number of PK columns),
107345108223
** (2) the number of columns in the key without the rowid/pk
107346
- ** (3) the number of rows in the index,
107347
- **
107348
- **
107349
- ** The third argument is only used for STAT4
108224
+ ** (3) estimated number of rows in the index,
107350108225
*/
108226
+ sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat+1);
108227
+ assert( regRowid==regStat+2 );
108228
+ sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regRowid);
107351108229
#ifdef SQLITE_ENABLE_STAT4
107352
- sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat4+3);
108230
+ if( OptimizationEnabled(db, SQLITE_Stat4) ){
108231
+ sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regTemp);
108232
+ addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
108233
+ VdbeCoverage(v);
108234
+ }else
107353108235
#endif
107354
- sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat4+1);
107355
- sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regStat4+2);
107356
- sqlite3VdbeAddFunctionCall(pParse, 0, regStat4+1, regStat4, 2+IsStat4,
108236
+ {
108237
+ addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
108238
+ VdbeCoverage(v);
108239
+ sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, 1);
108240
+ }
108241
+ assert( regTemp2==regStat+4 );
108242
+ sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2);
108243
+ sqlite3VdbeAddFunctionCall(pParse, 0, regStat+1, regStat, 4,
107357108244
&statInitFuncdef, 0);
107358108245
107359108246
/* Implementation of the following:
107360108247
**
107361108248
** Rewind csr
@@ -107362,12 +108249,10 @@
107362108249
** if eof(csr) goto end_of_scan;
107363108250
** regChng = 0
107364108251
** goto next_push_0;
107365108252
**
107366108253
*/
107367
- addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
107368
- VdbeCoverage(v);
107369108254
sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng);
107370108255
addrNextRow = sqlite3VdbeCurrentAddr(v);
107371108256
107372108257
if( nColTest>0 ){
107373108258
int endDistinctTest = sqlite3VdbeMakeLabel(pParse);
@@ -107396,10 +108281,11 @@
107396108281
}
107397108282
for(i=0; i<nColTest; i++){
107398108283
char *pColl = (char*)sqlite3LocateCollSeq(pParse, pIdx->azColl[i]);
107399108284
sqlite3VdbeAddOp2(v, OP_Integer, i, regChng);
107400108285
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regTemp);
108286
+ VdbeComment((v, "%s.column(%d)", pIdx->zName, i));
107401108287
aGotoChng[i] =
107402108288
sqlite3VdbeAddOp4(v, OP_Ne, regTemp, 0, regPrev+i, pColl, P4_COLLSEQ);
107403108289
sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
107404108290
VdbeCoverage(v);
107405108291
}
@@ -107416,10 +108302,11 @@
107416108302
*/
107417108303
sqlite3VdbeJumpHere(v, addrNextRow-1);
107418108304
for(i=0; i<nColTest; i++){
107419108305
sqlite3VdbeJumpHere(v, aGotoChng[i]);
107420108306
sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regPrev+i);
108307
+ VdbeComment((v, "%s.column(%d)", pIdx->zName, i));
107421108308
}
107422108309
sqlite3VdbeResolveLabel(v, endDistinctTest);
107423108310
sqlite3DbFree(db, aGotoChng);
107424108311
}
107425108312
@@ -107429,34 +108316,50 @@
107429108316
** stat_push(P, regChng, regRowid) // 3rd parameter STAT4 only
107430108317
** Next csr
107431108318
** if !eof(csr) goto next_row;
107432108319
*/
107433108320
#ifdef SQLITE_ENABLE_STAT4
107434
- assert( regRowid==(regStat4+2) );
107435
- if( HasRowid(pTab) ){
107436
- sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid);
107437
- }else{
107438
- Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
107439
- int j, k, regKey;
107440
- regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol);
107441
- for(j=0; j<pPk->nKeyCol; j++){
107442
- k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[j]);
107443
- assert( k>=0 && k<pIdx->nColumn );
107444
- sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j);
107445
- VdbeComment((v, "%s", pTab->aCol[pPk->aiColumn[j]].zName));
107446
- }
107447
- sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid);
107448
- sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol);
108321
+ if( OptimizationEnabled(db, SQLITE_Stat4) ){
108322
+ assert( regRowid==(regStat+2) );
108323
+ if( HasRowid(pTab) ){
108324
+ sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid);
108325
+ }else{
108326
+ Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
108327
+ int j, k, regKey;
108328
+ regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol);
108329
+ for(j=0; j<pPk->nKeyCol; j++){
108330
+ k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[j]);
108331
+ assert( k>=0 && k<pIdx->nColumn );
108332
+ sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j);
108333
+ VdbeComment((v, "%s.column(%d)", pIdx->zName, i));
108334
+ }
108335
+ sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid);
108336
+ sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol);
108337
+ }
107449108338
}
107450108339
#endif
107451
- assert( regChng==(regStat4+1) );
107452
- sqlite3VdbeAddFunctionCall(pParse, 1, regStat4, regTemp, 2+IsStat4,
107453
- &statPushFuncdef, 0);
107454
- sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
108340
+ assert( regChng==(regStat+1) );
108341
+ {
108342
+ sqlite3VdbeAddFunctionCall(pParse, 1, regStat, regTemp, 2+IsStat4,
108343
+ &statPushFuncdef, 0);
108344
+ if( db->nAnalysisLimit ){
108345
+ int j1, j2, j3;
108346
+ j1 = sqlite3VdbeAddOp1(v, OP_IsNull, regTemp); VdbeCoverage(v);
108347
+ j2 = sqlite3VdbeAddOp1(v, OP_If, regTemp); VdbeCoverage(v);
108348
+ j3 = sqlite3VdbeAddOp4Int(v, OP_SeekGT, iIdxCur, 0, regPrev, 1);
108349
+ VdbeCoverage(v);
108350
+ sqlite3VdbeJumpHere(v, j1);
108351
+ sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
108352
+ sqlite3VdbeJumpHere(v, j2);
108353
+ sqlite3VdbeJumpHere(v, j3);
108354
+ }else{
108355
+ sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
108356
+ }
108357
+ }
107455108358
107456108359
/* Add the entry to the stat1 table. */
107457
- callStatGet(pParse, regStat4, STAT_GET_STAT1, regStat1);
108360
+ callStatGet(pParse, regStat, STAT_GET_STAT1, regStat1);
107458108361
assert( "BBB"[0]==SQLITE_AFF_TEXT );
107459108362
sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0);
107460108363
sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
107461108364
sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid);
107462108365
#ifdef SQLITE_ENABLE_PREUPDATE_HOOK
@@ -107464,11 +108367,11 @@
107464108367
#endif
107465108368
sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
107466108369
107467108370
/* Add the entries to the stat4 table. */
107468108371
#ifdef SQLITE_ENABLE_STAT4
107469
- {
108372
+ if( OptimizationEnabled(db, SQLITE_Stat4) && db->nAnalysisLimit==0 ){
107470108373
int regEq = regStat1;
107471108374
int regLt = regStat1+1;
107472108375
int regDLt = regStat1+2;
107473108376
int regSample = regStat1+3;
107474108377
int regCol = regStat1+4;
@@ -107478,16 +108381,16 @@
107478108381
u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
107479108382
107480108383
pParse->nMem = MAX(pParse->nMem, regCol+nCol);
107481108384
107482108385
addrNext = sqlite3VdbeCurrentAddr(v);
107483
- callStatGet(pParse, regStat4, STAT_GET_ROWID, regSampleRowid);
108386
+ callStatGet(pParse, regStat, STAT_GET_ROWID, regSampleRowid);
107484108387
addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regSampleRowid);
107485108388
VdbeCoverage(v);
107486
- callStatGet(pParse, regStat4, STAT_GET_NEQ, regEq);
107487
- callStatGet(pParse, regStat4, STAT_GET_NLT, regLt);
107488
- callStatGet(pParse, regStat4, STAT_GET_NDLT, regDLt);
108389
+ callStatGet(pParse, regStat, STAT_GET_NEQ, regEq);
108390
+ callStatGet(pParse, regStat, STAT_GET_NLT, regLt);
108391
+ callStatGet(pParse, regStat, STAT_GET_NDLT, regDLt);
107489108392
sqlite3VdbeAddOp4Int(v, seekOp, iTabCur, addrNext, regSampleRowid, 0);
107490108393
VdbeCoverage(v);
107491108394
for(i=0; i<nCol; i++){
107492108395
sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iTabCur, i, regCol+i);
107493108396
}
@@ -109619,10 +110522,11 @@
109619110522
int i;
109620110523
Column *pCol;
109621110524
assert( pTable!=0 );
109622110525
if( (pCol = pTable->aCol)!=0 ){
109623110526
for(i=0; i<pTable->nCol; i++, pCol++){
110527
+ assert( pCol->zName==0 || pCol->hName==sqlite3StrIHash(pCol->zName) );
109624110528
sqlite3DbFree(db, pCol->zName);
109625110529
sqlite3ExprDelete(db, pCol->pDflt);
109626110530
sqlite3DbFree(db, pCol->zColl);
109627110531
}
109628110532
sqlite3DbFree(db, pTable->aCol);
@@ -110267,10 +111171,11 @@
110267111171
p->aCol = aNew;
110268111172
}
110269111173
pCol = &p->aCol[p->nCol];
110270111174
memset(pCol, 0, sizeof(p->aCol[0]));
110271111175
pCol->zName = z;
111176
+ pCol->hName = sqlite3StrIHash(z);
110272111177
sqlite3ColumnPropertiesFromName(p, pCol);
110273111178
110274111179
if( pType->n==0 ){
110275111180
/* If there is no type specified, columns have the default affinity
110276111181
** 'BLOB' with a default size of 4 bytes. */
@@ -113657,11 +114562,11 @@
113657114562
pParse->rc = rc;
113658114563
return 1;
113659114564
}
113660114565
db->aDb[1].pBt = pBt;
113661114566
assert( db->aDb[1].pSchema );
113662
- if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, -1, 0) ){
114567
+ if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, 0, 0) ){
113663114568
sqlite3OomFault(db);
113664114569
return 1;
113665114570
}
113666114571
}
113667114572
return 0;
@@ -113768,11 +114673,11 @@
113768114673
char *p4, /* Error message */
113769114674
i8 p4type, /* P4_STATIC or P4_TRANSIENT */
113770114675
u8 p5Errmsg /* P5_ErrMsg type */
113771114676
){
113772114677
Vdbe *v = sqlite3GetVdbe(pParse);
113773
- assert( (errCode&0xff)==SQLITE_CONSTRAINT );
114678
+ assert( (errCode&0xff)==SQLITE_CONSTRAINT || pParse->nested );
113774114679
if( onError==OE_Abort ){
113775114680
sqlite3MayAbort(pParse);
113776114681
}
113777114682
sqlite3VdbeAddOp4(v, OP_Halt, errCode, onError, 0, p4, p4type);
113778114683
sqlite3VdbeChangeP5(v, p5Errmsg);
@@ -115481,10 +116386,11 @@
115481116386
VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName));
115482116387
r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1,
115483116388
&iPartIdxLabel, pPrior, r1);
115484116389
sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1,
115485116390
pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn);
116391
+ sqlite3VdbeChangeP5(v, 1); /* Cause IdxDelete to error if no entry found */
115486116392
sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel);
115487116393
pPrior = pIdx;
115488116394
}
115489116395
}
115490116396
@@ -122468,10 +123374,11 @@
122468123374
const char *(*filename_wal)(const char*);
122469123375
/* Version 3.32.0 and later */
122470123376
char *(*create_filename)(const char*,const char*,const char*,
122471123377
int,const char**);
122472123378
void (*free_filename)(char*);
123379
+ sqlite3_file *(*database_file_object)(const char*);
122473123380
};
122474123381
122475123382
/*
122476123383
** This is the function signature used for all extension entry points. It
122477123384
** is also defined in the file "loadext.c".
@@ -122771,10 +123678,11 @@
122771123678
#define sqlite3_filename_journal sqlite3_api->filename_journal
122772123679
#define sqlite3_filename_wal sqlite3_api->filename_wal
122773123680
/* Version 3.32.0 and later */
122774123681
#define sqlite3_create_filename sqlite3_api->create_filename
122775123682
#define sqlite3_free_filename sqlite3_api->free_filename
123683
+#define sqlite3_database_file_object sqlite3_api->database_file_object
122776123684
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
122777123685
122778123686
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
122779123687
/* This case when the file really is being compiled as a loadable
122780123688
** extension */
@@ -123252,11 +124160,20 @@
123252124160
sqlite3_filename_journal,
123253124161
sqlite3_filename_wal,
123254124162
/* Version 3.32.0 and later */
123255124163
sqlite3_create_filename,
123256124164
sqlite3_free_filename,
124165
+ sqlite3_database_file_object,
123257124166
};
124167
+
124168
+/* True if x is the directory separator character
124169
+*/
124170
+#if SQLITE_OS_WIN
124171
+# define DirSep(X) ((X)=='/'||(X)=='\\')
124172
+#else
124173
+# define DirSep(X) ((X)=='/')
124174
+#endif
123258124175
123259124176
/*
123260124177
** Attempt to load an SQLite extension library contained in the file
123261124178
** zFile. The entry point is zProc. zProc may be 0 in which case a
123262124179
** default entry point name (sqlite3_extension_init) is used. Use
@@ -123355,11 +124272,11 @@
123355124272
if( zAltEntry==0 ){
123356124273
sqlite3OsDlClose(pVfs, handle);
123357124274
return SQLITE_NOMEM_BKPT;
123358124275
}
123359124276
memcpy(zAltEntry, "sqlite3_", 8);
123360
- for(iFile=ncFile-1; iFile>=0 && zFile[iFile]!='/'; iFile--){}
124277
+ for(iFile=ncFile-1; iFile>=0 && !DirSep(zFile[iFile]); iFile--){}
123361124278
iFile++;
123362124279
if( sqlite3_strnicmp(zFile+iFile, "lib", 3)==0 ) iFile += 3;
123363124280
for(iEntry=8; (c = zFile[iFile])!=0 && c!='.'; iFile++){
123364124281
if( sqlite3Isalpha(c) ){
123365124282
zAltEntry[iEntry++] = (char)sqlite3UpperToLower[(unsigned)c];
@@ -123659,53 +124576,54 @@
123659124576
** that script and rerun it.
123660124577
*/
123661124578
123662124579
/* The various pragma types */
123663124580
#define PragTyp_ACTIVATE_EXTENSIONS 0
123664
-#define PragTyp_HEADER_VALUE 1
123665
-#define PragTyp_AUTO_VACUUM 2
123666
-#define PragTyp_FLAG 3
123667
-#define PragTyp_BUSY_TIMEOUT 4
123668
-#define PragTyp_CACHE_SIZE 5
123669
-#define PragTyp_CACHE_SPILL 6
123670
-#define PragTyp_CASE_SENSITIVE_LIKE 7
123671
-#define PragTyp_COLLATION_LIST 8
123672
-#define PragTyp_COMPILE_OPTIONS 9
123673
-#define PragTyp_DATA_STORE_DIRECTORY 10
123674
-#define PragTyp_DATABASE_LIST 11
123675
-#define PragTyp_DEFAULT_CACHE_SIZE 12
123676
-#define PragTyp_ENCODING 13
123677
-#define PragTyp_FOREIGN_KEY_CHECK 14
123678
-#define PragTyp_FOREIGN_KEY_LIST 15
123679
-#define PragTyp_FUNCTION_LIST 16
123680
-#define PragTyp_HARD_HEAP_LIMIT 17
123681
-#define PragTyp_INCREMENTAL_VACUUM 18
123682
-#define PragTyp_INDEX_INFO 19
123683
-#define PragTyp_INDEX_LIST 20
123684
-#define PragTyp_INTEGRITY_CHECK 21
123685
-#define PragTyp_JOURNAL_MODE 22
123686
-#define PragTyp_JOURNAL_SIZE_LIMIT 23
123687
-#define PragTyp_LOCK_PROXY_FILE 24
123688
-#define PragTyp_LOCKING_MODE 25
123689
-#define PragTyp_PAGE_COUNT 26
123690
-#define PragTyp_MMAP_SIZE 27
123691
-#define PragTyp_MODULE_LIST 28
123692
-#define PragTyp_OPTIMIZE 29
123693
-#define PragTyp_PAGE_SIZE 30
123694
-#define PragTyp_PRAGMA_LIST 31
123695
-#define PragTyp_SECURE_DELETE 32
123696
-#define PragTyp_SHRINK_MEMORY 33
123697
-#define PragTyp_SOFT_HEAP_LIMIT 34
123698
-#define PragTyp_SYNCHRONOUS 35
123699
-#define PragTyp_TABLE_INFO 36
123700
-#define PragTyp_TEMP_STORE 37
123701
-#define PragTyp_TEMP_STORE_DIRECTORY 38
123702
-#define PragTyp_THREADS 39
123703
-#define PragTyp_WAL_AUTOCHECKPOINT 40
123704
-#define PragTyp_WAL_CHECKPOINT 41
123705
-#define PragTyp_LOCK_STATUS 42
123706
-#define PragTyp_STATS 43
124581
+#define PragTyp_ANALYSIS_LIMIT 1
124582
+#define PragTyp_HEADER_VALUE 2
124583
+#define PragTyp_AUTO_VACUUM 3
124584
+#define PragTyp_FLAG 4
124585
+#define PragTyp_BUSY_TIMEOUT 5
124586
+#define PragTyp_CACHE_SIZE 6
124587
+#define PragTyp_CACHE_SPILL 7
124588
+#define PragTyp_CASE_SENSITIVE_LIKE 8
124589
+#define PragTyp_COLLATION_LIST 9
124590
+#define PragTyp_COMPILE_OPTIONS 10
124591
+#define PragTyp_DATA_STORE_DIRECTORY 11
124592
+#define PragTyp_DATABASE_LIST 12
124593
+#define PragTyp_DEFAULT_CACHE_SIZE 13
124594
+#define PragTyp_ENCODING 14
124595
+#define PragTyp_FOREIGN_KEY_CHECK 15
124596
+#define PragTyp_FOREIGN_KEY_LIST 16
124597
+#define PragTyp_FUNCTION_LIST 17
124598
+#define PragTyp_HARD_HEAP_LIMIT 18
124599
+#define PragTyp_INCREMENTAL_VACUUM 19
124600
+#define PragTyp_INDEX_INFO 20
124601
+#define PragTyp_INDEX_LIST 21
124602
+#define PragTyp_INTEGRITY_CHECK 22
124603
+#define PragTyp_JOURNAL_MODE 23
124604
+#define PragTyp_JOURNAL_SIZE_LIMIT 24
124605
+#define PragTyp_LOCK_PROXY_FILE 25
124606
+#define PragTyp_LOCKING_MODE 26
124607
+#define PragTyp_PAGE_COUNT 27
124608
+#define PragTyp_MMAP_SIZE 28
124609
+#define PragTyp_MODULE_LIST 29
124610
+#define PragTyp_OPTIMIZE 30
124611
+#define PragTyp_PAGE_SIZE 31
124612
+#define PragTyp_PRAGMA_LIST 32
124613
+#define PragTyp_SECURE_DELETE 33
124614
+#define PragTyp_SHRINK_MEMORY 34
124615
+#define PragTyp_SOFT_HEAP_LIMIT 35
124616
+#define PragTyp_SYNCHRONOUS 36
124617
+#define PragTyp_TABLE_INFO 37
124618
+#define PragTyp_TEMP_STORE 38
124619
+#define PragTyp_TEMP_STORE_DIRECTORY 39
124620
+#define PragTyp_THREADS 40
124621
+#define PragTyp_WAL_AUTOCHECKPOINT 41
124622
+#define PragTyp_WAL_CHECKPOINT 42
124623
+#define PragTyp_LOCK_STATUS 43
124624
+#define PragTyp_STATS 44
123707124625
123708124626
/* Property flags associated with various pragma. */
123709124627
#define PragFlg_NeedSchema 0x01 /* Force schema load before running */
123710124628
#define PragFlg_NoColumns 0x02 /* OP_ResultRow called with zero columns */
123711124629
#define PragFlg_NoColumns1 0x04 /* zero columns if RHS argument is present */
@@ -123792,10 +124710,15 @@
123792124710
/* ePragTyp: */ PragTyp_ACTIVATE_EXTENSIONS,
123793124711
/* ePragFlg: */ 0,
123794124712
/* ColNames: */ 0, 0,
123795124713
/* iArg: */ 0 },
123796124714
#endif
124715
+ {/* zName: */ "analysis_limit",
124716
+ /* ePragTyp: */ PragTyp_ANALYSIS_LIMIT,
124717
+ /* ePragFlg: */ PragFlg_Result0,
124718
+ /* ColNames: */ 0, 0,
124719
+ /* iArg: */ 0 },
123797124720
#if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
123798124721
{/* zName: */ "application_id",
123799124722
/* ePragTyp: */ PragTyp_HEADER_VALUE,
123800124723
/* ePragFlg: */ PragFlg_NoColumns1|PragFlg_Result0,
123801124724
/* ColNames: */ 0, 0,
@@ -124292,11 +125215,11 @@
124292125215
/* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1,
124293125216
/* ColNames: */ 0, 0,
124294125217
/* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError },
124295125218
#endif
124296125219
};
124297
-/* Number of pragmas: 66 on by default, 76 total. */
125220
+/* Number of pragmas: 67 on by default, 77 total. */
124298125221
124299125222
/************** End of pragma.h **********************************************/
124300125223
/************** Continuing where we left off in pragma.c *********************/
124301125224
124302125225
/*
@@ -124822,11 +125745,11 @@
124822125745
}else{
124823125746
/* Malloc may fail when setting the page-size, as there is an internal
124824125747
** buffer that the pager module resizes using sqlite3_realloc().
124825125748
*/
124826125749
db->nextPagesize = sqlite3Atoi(zRight);
124827
- if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize,-1,0) ){
125750
+ if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize,0,0) ){
124828125751
sqlite3OomFault(db);
124829125752
}
124830125753
}
124831125754
break;
124832125755
}
@@ -125996,11 +126919,10 @@
125996126919
sqlite3ResolvePartIdxLabel(pParse, jmp3);
125997126920
}
125998126921
}
125999126922
sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v);
126000126923
sqlite3VdbeJumpHere(v, loopTop-1);
126001
-#ifndef SQLITE_OMIT_BTREECOUNT
126002126924
if( !isQuick ){
126003126925
sqlite3VdbeLoadString(v, 2, "wrong # of entries in index ");
126004126926
for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
126005126927
if( pPk==pIdx ) continue;
126006126928
sqlite3VdbeAddOp2(v, OP_Count, iIdxCur+j, 3);
@@ -126010,11 +126932,10 @@
126010126932
sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3);
126011126933
integrityCheckResultRow(v);
126012126934
sqlite3VdbeJumpHere(v, addr);
126013126935
}
126014126936
}
126015
-#endif /* SQLITE_OMIT_BTREECOUNT */
126016126937
}
126017126938
}
126018126939
{
126019126940
static const int iLn = VDBE_OFFSET_LINENO(2);
126020126941
static const VdbeOpList endCode[] = {
@@ -126444,10 +127365,29 @@
126444127365
sqlite3_limit(db, SQLITE_LIMIT_WORKER_THREADS, (int)(N&0x7fffffff));
126445127366
}
126446127367
returnSingleInt(v, sqlite3_limit(db, SQLITE_LIMIT_WORKER_THREADS, -1));
126447127368
break;
126448127369
}
127370
+
127371
+ /*
127372
+ ** PRAGMA analysis_limit
127373
+ ** PRAGMA analysis_limit = N
127374
+ **
127375
+ ** Configure the maximum number of rows that ANALYZE will examine
127376
+ ** in each index that it looks at. Return the new limit.
127377
+ */
127378
+ case PragTyp_ANALYSIS_LIMIT: {
127379
+ sqlite3_int64 N;
127380
+ if( zRight
127381
+ && sqlite3DecOrHexToI64(zRight, &N)==SQLITE_OK
127382
+ && N>=0
127383
+ ){
127384
+ db->nAnalysisLimit = (int)(N&0x7fffffff);
127385
+ }
127386
+ returnSingleInt(v, db->nAnalysisLimit);
127387
+ break;
127388
+ }
126449127389
126450127390
#if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
126451127391
/*
126452127392
** Report the current state of file logs for all databases
126453127393
*/
@@ -129768,10 +130708,11 @@
129768130708
}
129769130709
zName = sqlite3MPrintf(db, "%.*z:%u", nName, zName, ++cnt);
129770130710
if( cnt>3 ) sqlite3_randomness(sizeof(cnt), &cnt);
129771130711
}
129772130712
pCol->zName = zName;
130713
+ pCol->hName = sqlite3StrIHash(zName);
129773130714
sqlite3ColumnPropertiesFromName(0, pCol);
129774130715
if( zName && sqlite3HashInsert(&ht, zName, pCol)==pCol ){
129775130716
sqlite3OomFault(db);
129776130717
}
129777130718
}
@@ -131221,11 +132162,14 @@
131221132162
if( ExprHasProperty(pExpr, EP_FromJoin)
131222132163
&& pExpr->iRightJoinTable==pSubst->iTable
131223132164
){
131224132165
pExpr->iRightJoinTable = pSubst->iNewTable;
131225132166
}
131226
- if( pExpr->op==TK_COLUMN && pExpr->iTable==pSubst->iTable ){
132167
+ if( pExpr->op==TK_COLUMN
132168
+ && pExpr->iTable==pSubst->iTable
132169
+ && !ExprHasProperty(pExpr, EP_FixedCol)
132170
+ ){
131227132171
if( pExpr->iColumn<0 ){
131228132172
pExpr->op = TK_NULL;
131229132173
}else{
131230132174
Expr *pNew;
131231132175
Expr *pCopy = pSubst->pEList->a[pExpr->iColumn].pExpr;
@@ -131239,10 +132183,11 @@
131239132183
if( pSubst->isLeftJoin && pCopy->op!=TK_COLUMN ){
131240132184
memset(&ifNullRow, 0, sizeof(ifNullRow));
131241132185
ifNullRow.op = TK_IF_NULL_ROW;
131242132186
ifNullRow.pLeft = pCopy;
131243132187
ifNullRow.iTable = pSubst->iNewTable;
132188
+ ifNullRow.flags = EP_Skip;
131244132189
pCopy = &ifNullRow;
131245132190
}
131246132191
testcase( ExprHasProperty(pCopy, EP_Subquery) );
131247132192
pNew = sqlite3ExprDup(db, pCopy, 0);
131248132193
if( pNew && pSubst->isLeftJoin ){
@@ -133133,10 +134078,11 @@
133133134078
Vdbe *v = pParse->pVdbe;
133134134079
int i;
133135134080
struct AggInfo_func *pFunc;
133136134081
int nReg = pAggInfo->nFunc + pAggInfo->nColumn;
133137134082
if( nReg==0 ) return;
134083
+ if( pParse->nErr ) return;
133138134084
#ifdef SQLITE_DEBUG
133139134085
/* Verify that all AggInfo registers are within the range specified by
133140134086
** AggInfo.mnReg..AggInfo.mxReg */
133141134087
assert( nReg==pAggInfo->mxReg-pAggInfo->mnReg+1 );
133142134088
for(i=0; i<pAggInfo->nColumn; i++){
@@ -134402,11 +135348,10 @@
134402135348
VdbeComment((v, "indicate accumulator empty"));
134403135349
sqlite3VdbeAddOp1(v, OP_Return, regReset);
134404135350
134405135351
} /* endif pGroupBy. Begin aggregate queries without GROUP BY: */
134406135352
else {
134407
-#ifndef SQLITE_OMIT_BTREECOUNT
134408135353
Table *pTab;
134409135354
if( (pTab = isSimpleCount(p, &sAggInfo))!=0 ){
134410135355
/* If isSimpleCount() returns a pointer to a Table structure, then
134411135356
** the SQL statement is of the form:
134412135357
**
@@ -134438,17 +135383,19 @@
134438135383
**
134439135384
** In practice the KeyInfo structure will not be used. It is only
134440135385
** passed to keep OP_OpenRead happy.
134441135386
*/
134442135387
if( !HasRowid(pTab) ) pBest = sqlite3PrimaryKeyIndex(pTab);
134443
- for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
134444
- if( pIdx->bUnordered==0
134445
- && pIdx->szIdxRow<pTab->szTabRow
134446
- && pIdx->pPartIdxWhere==0
134447
- && (!pBest || pIdx->szIdxRow<pBest->szIdxRow)
134448
- ){
134449
- pBest = pIdx;
135388
+ if( !p->pSrc->a[0].fg.notIndexed ){
135389
+ for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
135390
+ if( pIdx->bUnordered==0
135391
+ && pIdx->szIdxRow<pTab->szTabRow
135392
+ && pIdx->pPartIdxWhere==0
135393
+ && (!pBest || pIdx->szIdxRow<pBest->szIdxRow)
135394
+ ){
135395
+ pBest = pIdx;
135396
+ }
134450135397
}
134451135398
}
134452135399
if( pBest ){
134453135400
iRoot = pBest->tnum;
134454135401
pKeyInfo = sqlite3KeyInfoOfIndex(pParse, pBest);
@@ -134460,13 +135407,11 @@
134460135407
sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO);
134461135408
}
134462135409
sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem);
134463135410
sqlite3VdbeAddOp1(v, OP_Close, iCsr);
134464135411
explainSimpleCount(pParse, pTab, pBest);
134465
- }else
134466
-#endif /* SQLITE_OMIT_BTREECOUNT */
134467
- {
135412
+ }else{
134468135413
int regAcc = 0; /* "populate accumulators" flag */
134469135414
134470135415
/* If there are accumulator registers but no min() or max() functions
134471135416
** without FILTER clauses, allocate register regAcc. Register regAcc
134472135417
** will contain 0 the first time the inner loop runs, and 1 thereafter.
@@ -137542,11 +138487,11 @@
137542138487
sqlite3SetString(pzErrMsg, db, "output file already exists");
137543138488
goto end_of_vacuum;
137544138489
}
137545138490
db->mDbFlags |= DBFLAG_VacuumInto;
137546138491
}
137547
- nRes = sqlite3BtreeGetOptimalReserve(pMain);
138492
+ nRes = sqlite3BtreeGetRequestedReserve(pMain);
137548138493
137549138494
sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size);
137550138495
sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0));
137551138496
sqlite3BtreeSetPagerFlags(pTemp, PAGER_SYNCHRONOUS_OFF|PAGER_CACHESPILL);
137552138497
@@ -137686,11 +138631,11 @@
137686138631
db->mDbFlags = saved_mDbFlags;
137687138632
db->flags = saved_flags;
137688138633
db->nChange = saved_nChange;
137689138634
db->nTotalChange = saved_nTotalChange;
137690138635
db->mTrace = saved_mTrace;
137691
- sqlite3BtreeSetPageSize(pMain, -1, -1, 1);
138636
+ sqlite3BtreeSetPageSize(pMain, -1, 0, 1);
137692138637
137693138638
/* Currently there is an SQL level transaction open on the vacuum
137694138639
** database. No locks are held on any other files (since the main file
137695138640
** was committed at the btree level). So it safe to end the transaction
137696138641
** by manually setting the autoCommit flag to true and detaching the
@@ -156491,14 +157436,15 @@
156491157436
** simplify to constants 0 (false) and 1 (true), respectively,
156492157437
** regardless of the value of expr1.
156493157438
*/
156494157439
sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy202);
156495157440
yymsp[-4].minor.yy202 = sqlite3Expr(pParse->db, TK_INTEGER, yymsp[-3].minor.yy192 ? "1" : "0");
156496
- }else if( 0 && yymsp[-1].minor.yy242->nExpr==1 && sqlite3ExprIsConstant(yymsp[-1].minor.yy242->a[0].pExpr) ){
157441
+ }else if( yymsp[-1].minor.yy242->nExpr==1 && sqlite3ExprIsConstant(yymsp[-1].minor.yy242->a[0].pExpr) ){
156497157442
Expr *pRHS = yymsp[-1].minor.yy242->a[0].pExpr;
156498157443
yymsp[-1].minor.yy242->a[0].pExpr = 0;
156499157444
sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy242);
157445
+ pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0);
156500157446
yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy202, pRHS);
156501157447
if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0);
156502157448
}else{
156503157449
yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy202, 0);
156504157450
if( yymsp[-4].minor.yy202 ){
@@ -158376,11 +159322,11 @@
158376159322
VVA_ONLY( u8 startedWithOom = db->mallocFailed );
158377159323
158378159324
assert( zSql!=0 );
158379159325
mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
158380159326
if( db->nVdbeActive==0 ){
158381
- db->u1.isInterrupted = 0;
159327
+ AtomicStore(&db->u1.isInterrupted, 0);
158382159328
}
158383159329
pParse->rc = SQLITE_OK;
158384159330
pParse->zTail = zSql;
158385159331
assert( pzErrMsg!=0 );
158386159332
#ifdef SQLITE_DEBUG
@@ -158421,11 +159367,11 @@
158421159367
);
158422159368
#else
158423159369
if( tokenType>=TK_SPACE ){
158424159370
assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL );
158425159371
#endif /* SQLITE_OMIT_WINDOWFUNC */
158426
- if( db->u1.isInterrupted ){
159372
+ if( AtomicLoad(&db->u1.isInterrupted) ){
158427159373
pParse->rc = SQLITE_INTERRUPT;
158428159374
break;
158429159375
}
158430159376
if( tokenType==TK_SPACE ){
158431159377
zSql += n;
@@ -159088,19 +160034,82 @@
159088160034
159089160035
159090160036
/************** End of sqliteicu.h *******************************************/
159091160037
/************** Continuing where we left off in main.c ***********************/
159092160038
#endif
160039
+
160040
+/*
160041
+** This is an extension initializer that is a no-op and always
160042
+** succeeds, except that it fails if the fault-simulation is set
160043
+** to 500.
160044
+*/
160045
+static int sqlite3TestExtInit(sqlite3 *db){
160046
+ (void)db;
160047
+ return sqlite3FaultSim(500);
160048
+}
160049
+
160050
+
160051
+/*
160052
+** Forward declarations of external module initializer functions
160053
+** for modules that need them.
160054
+*/
160055
+#ifdef SQLITE_ENABLE_FTS1
160056
+SQLITE_PRIVATE int sqlite3Fts1Init(sqlite3*);
160057
+#endif
160058
+#ifdef SQLITE_ENABLE_FTS2
160059
+SQLITE_PRIVATE int sqlite3Fts2Init(sqlite3*);
160060
+#endif
160061
+#ifdef SQLITE_ENABLE_FTS5
160062
+SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3*);
160063
+#endif
159093160064
#ifdef SQLITE_ENABLE_JSON1
159094160065
SQLITE_PRIVATE int sqlite3Json1Init(sqlite3*);
159095160066
#endif
159096160067
#ifdef SQLITE_ENABLE_STMTVTAB
159097160068
SQLITE_PRIVATE int sqlite3StmtVtabInit(sqlite3*);
159098160069
#endif
160070
+
160071
+/*
160072
+** An array of pointers to extension initializer functions for
160073
+** built-in extensions.
160074
+*/
160075
+static int (*const sqlite3BuiltinExtensions[])(sqlite3*) = {
160076
+#ifdef SQLITE_ENABLE_FTS1
160077
+ sqlite3Fts1Init,
160078
+#endif
160079
+#ifdef SQLITE_ENABLE_FTS2
160080
+ sqlite3Fts2Init,
160081
+#endif
160082
+#ifdef SQLITE_ENABLE_FTS3
160083
+ sqlite3Fts3Init,
160084
+#endif
159099160085
#ifdef SQLITE_ENABLE_FTS5
159100
-SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3*);
160086
+ sqlite3Fts5Init,
159101160087
#endif
160088
+#if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS)
160089
+ sqlite3IcuInit,
160090
+#endif
160091
+#ifdef SQLITE_ENABLE_RTREE
160092
+ sqlite3RtreeInit,
160093
+#endif
160094
+#ifdef SQLITE_ENABLE_DBPAGE_VTAB
160095
+ sqlite3DbpageRegister,
160096
+#endif
160097
+#ifdef SQLITE_ENABLE_DBSTAT_VTAB
160098
+ sqlite3DbstatRegister,
160099
+#endif
160100
+ sqlite3TestExtInit,
160101
+#ifdef SQLITE_ENABLE_JSON1
160102
+ sqlite3Json1Init,
160103
+#endif
160104
+#ifdef SQLITE_ENABLE_STMTVTAB
160105
+ sqlite3StmtVtabInit,
160106
+#endif
160107
+#ifdef SQLITE_ENABLE_BYTECODE_VTAB
160108
+ sqlite3VdbeBytecodeVtabInit,
160109
+#endif
160110
+};
159102160111
159103160112
#ifndef SQLITE_AMALGAMATION
159104160113
/* IMPLEMENTATION-OF: R-46656-45156 The sqlite3_version[] string constant
159105160114
** contains the text of SQLITE_VERSION macro.
159106160115
*/
@@ -160614,12 +161623,11 @@
160614161623
** Return non-zero to retry the lock. Return zero to stop trying
160615161624
** and cause SQLite to return SQLITE_BUSY.
160616161625
*/
160617161626
static int sqliteDefaultBusyCallback(
160618161627
void *ptr, /* Database connection */
160619
- int count, /* Number of times table has been busy */
160620
- sqlite3_file *pFile /* The file on which the lock occurred */
161628
+ int count /* Number of times table has been busy */
160621161629
){
160622161630
#if SQLITE_OS_WIN || HAVE_USLEEP
160623161631
/* This case is for systems that have support for sleeping for fractions of
160624161632
** a second. Examples: All windows systems, unix systems with usleep() */
160625161633
static const u8 delays[] =
@@ -160629,23 +161637,10 @@
160629161637
# define NDELAY ArraySize(delays)
160630161638
sqlite3 *db = (sqlite3 *)ptr;
160631161639
int tmout = db->busyTimeout;
160632161640
int delay, prior;
160633161641
160634
-#ifdef SQLITE_ENABLE_SETLK_TIMEOUT
160635
- if( sqlite3OsFileControl(pFile,SQLITE_FCNTL_LOCK_TIMEOUT,&tmout)==SQLITE_OK ){
160636
- if( count ){
160637
- tmout = 0;
160638
- sqlite3OsFileControl(pFile, SQLITE_FCNTL_LOCK_TIMEOUT, &tmout);
160639
- return 0;
160640
- }else{
160641
- return 1;
160642
- }
160643
- }
160644
-#else
160645
- UNUSED_PARAMETER(pFile);
160646
-#endif
160647161642
assert( count>=0 );
160648161643
if( count < NDELAY ){
160649161644
delay = delays[count];
160650161645
prior = totals[count];
160651161646
}else{
@@ -160661,11 +161656,10 @@
160661161656
#else
160662161657
/* This case for unix systems that lack usleep() support. Sleeping
160663161658
** must be done in increments of whole seconds */
160664161659
sqlite3 *db = (sqlite3 *)ptr;
160665161660
int tmout = ((sqlite3 *)ptr)->busyTimeout;
160666
- UNUSED_PARAMETER(pFile);
160667161661
if( (count+1)*1000 > tmout ){
160668161662
return 0;
160669161663
}
160670161664
sqlite3OsSleep(db->pVfs, 1000000);
160671161665
return 1;
@@ -160679,23 +161673,14 @@
160679161673
** lock on VFS file pFile.
160680161674
**
160681161675
** If this routine returns non-zero, the lock is retried. If it
160682161676
** returns 0, the operation aborts with an SQLITE_BUSY error.
160683161677
*/
160684
-SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler *p, sqlite3_file *pFile){
161678
+SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler *p){
160685161679
int rc;
160686161680
if( p->xBusyHandler==0 || p->nBusy<0 ) return 0;
160687
- if( p->bExtraFileArg ){
160688
- /* Add an extra parameter with the pFile pointer to the end of the
160689
- ** callback argument list */
160690
- int (*xTra)(void*,int,sqlite3_file*);
160691
- xTra = (int(*)(void*,int,sqlite3_file*))p->xBusyHandler;
160692
- rc = xTra(p->pBusyArg, p->nBusy, pFile);
160693
- }else{
160694
- /* Legacy style busy handler callback */
160695
- rc = p->xBusyHandler(p->pBusyArg, p->nBusy);
160696
- }
161681
+ rc = p->xBusyHandler(p->pBusyArg, p->nBusy);
160697161682
if( rc==0 ){
160698161683
p->nBusy = -1;
160699161684
}else{
160700161685
p->nBusy++;
160701161686
}
@@ -160716,11 +161701,10 @@
160716161701
#endif
160717161702
sqlite3_mutex_enter(db->mutex);
160718161703
db->busyHandler.xBusyHandler = xBusy;
160719161704
db->busyHandler.pBusyArg = pArg;
160720161705
db->busyHandler.nBusy = 0;
160721
- db->busyHandler.bExtraFileArg = 0;
160722161706
db->busyTimeout = 0;
160723161707
sqlite3_mutex_leave(db->mutex);
160724161708
return SQLITE_OK;
160725161709
}
160726161710
@@ -160767,11 +161751,10 @@
160767161751
#endif
160768161752
if( ms>0 ){
160769161753
sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback,
160770161754
(void*)db);
160771161755
db->busyTimeout = ms;
160772
- db->busyHandler.bExtraFileArg = 1;
160773161756
}else{
160774161757
sqlite3_busy_handler(db, 0, 0);
160775161758
}
160776161759
return SQLITE_OK;
160777161760
}
@@ -160784,11 +161767,11 @@
160784161767
if( !sqlite3SafetyCheckOk(db) && (db==0 || db->magic!=SQLITE_MAGIC_ZOMBIE) ){
160785161768
(void)SQLITE_MISUSE_BKPT;
160786161769
return;
160787161770
}
160788161771
#endif
160789
- db->u1.isInterrupted = 1;
161772
+ AtomicStore(&db->u1.isInterrupted, 1);
160790161773
}
160791161774
160792161775
160793161776
/*
160794161777
** This function is exactly the same as sqlite3_create_function(), except
@@ -161406,11 +162389,11 @@
161406162389
rc = sqlite3ApiExit(db, rc);
161407162390
161408162391
/* If there are no active statements, clear the interrupt flag at this
161409162392
** point. */
161410162393
if( db->nVdbeActive==0 ){
161411
- db->u1.isInterrupted = 0;
162394
+ AtomicStore(&db->u1.isInterrupted, 0);
161412162395
}
161413162396
161414162397
sqlite3_mutex_leave(db->mutex);
161415162398
return rc;
161416162399
#endif
@@ -162093,10 +163076,11 @@
162093163076
sqlite3 *db; /* Store allocated handle here */
162094163077
int rc; /* Return code */
162095163078
int isThreadsafe; /* True for threadsafe connections */
162096163079
char *zOpen = 0; /* Filename argument to pass to BtreeOpen() */
162097163080
char *zErrMsg = 0; /* Error message from sqlite3ParseUri() */
163081
+ int i; /* Loop counter */
162098163082
162099163083
#ifdef SQLITE_ENABLE_API_ARMOR
162100163084
if( ppDb==0 ) return SQLITE_MISUSE_BKPT;
162101163085
#endif
162102163086
*ppDb = 0;
@@ -162241,10 +163225,13 @@
162241163225
| SQLITE_EnableQPSG
162242163226
#endif
162243163227
#if defined(SQLITE_DEFAULT_DEFENSIVE)
162244163228
| SQLITE_Defensive
162245163229
#endif
163230
+#if defined(SQLITE_DEFAULT_LEGACY_ALTER_TABLE)
163231
+ | SQLITE_LegacyAlter
163232
+#endif
162246163233
;
162247163234
sqlite3HashInit(&db->aCollSeq);
162248163235
#ifndef SQLITE_OMIT_VIRTUALTABLE
162249163236
sqlite3HashInit(&db->aModule);
162250163237
#endif
@@ -162333,18 +163320,15 @@
162333163320
*/
162334163321
sqlite3Error(db, SQLITE_OK);
162335163322
sqlite3RegisterPerConnectionBuiltinFunctions(db);
162336163323
rc = sqlite3_errcode(db);
162337163324
162338
-#ifdef SQLITE_ENABLE_FTS5
162339
- /* Register any built-in FTS5 module before loading the automatic
162340
- ** extensions. This allows automatic extensions to register FTS5
162341
- ** tokenizers and auxiliary functions. */
162342
- if( !db->mallocFailed && rc==SQLITE_OK ){
162343
- rc = sqlite3Fts5Init(db);
162344
- }
162345
-#endif
163325
+
163326
+ /* Load compiled-in extensions */
163327
+ for(i=0; rc==SQLITE_OK && i<ArraySize(sqlite3BuiltinExtensions); i++){
163328
+ rc = sqlite3BuiltinExtensions[i](db);
163329
+ }
162346163330
162347163331
/* Load automatic extensions - extensions that have been registered
162348163332
** using the sqlite3_automatic_extension() API.
162349163333
*/
162350163334
if( rc==SQLITE_OK ){
@@ -162353,66 +163337,10 @@
162353163337
if( rc!=SQLITE_OK ){
162354163338
goto opendb_out;
162355163339
}
162356163340
}
162357163341
162358
-#ifdef SQLITE_ENABLE_FTS1
162359
- if( !db->mallocFailed ){
162360
- extern int sqlite3Fts1Init(sqlite3*);
162361
- rc = sqlite3Fts1Init(db);
162362
- }
162363
-#endif
162364
-
162365
-#ifdef SQLITE_ENABLE_FTS2
162366
- if( !db->mallocFailed && rc==SQLITE_OK ){
162367
- extern int sqlite3Fts2Init(sqlite3*);
162368
- rc = sqlite3Fts2Init(db);
162369
- }
162370
-#endif
162371
-
162372
-#ifdef SQLITE_ENABLE_FTS3 /* automatically defined by SQLITE_ENABLE_FTS4 */
162373
- if( !db->mallocFailed && rc==SQLITE_OK ){
162374
- rc = sqlite3Fts3Init(db);
162375
- }
162376
-#endif
162377
-
162378
-#if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS)
162379
- if( !db->mallocFailed && rc==SQLITE_OK ){
162380
- rc = sqlite3IcuInit(db);
162381
- }
162382
-#endif
162383
-
162384
-#ifdef SQLITE_ENABLE_RTREE
162385
- if( !db->mallocFailed && rc==SQLITE_OK){
162386
- rc = sqlite3RtreeInit(db);
162387
- }
162388
-#endif
162389
-
162390
-#ifdef SQLITE_ENABLE_DBPAGE_VTAB
162391
- if( !db->mallocFailed && rc==SQLITE_OK){
162392
- rc = sqlite3DbpageRegister(db);
162393
- }
162394
-#endif
162395
-
162396
-#ifdef SQLITE_ENABLE_DBSTAT_VTAB
162397
- if( !db->mallocFailed && rc==SQLITE_OK){
162398
- rc = sqlite3DbstatRegister(db);
162399
- }
162400
-#endif
162401
-
162402
-#ifdef SQLITE_ENABLE_JSON1
162403
- if( !db->mallocFailed && rc==SQLITE_OK){
162404
- rc = sqlite3Json1Init(db);
162405
- }
162406
-#endif
162407
-
162408
-#ifdef SQLITE_ENABLE_STMTVTAB
162409
- if( !db->mallocFailed && rc==SQLITE_OK){
162410
- rc = sqlite3StmtVtabInit(db);
162411
- }
162412
-#endif
162413
-
162414163342
#ifdef SQLITE_ENABLE_INTERNAL_FUNCTIONS
162415163343
/* Testing use only!!! The -DSQLITE_ENABLE_INTERNAL_FUNCTIONS=1 compile-time
162416163344
** option gives access to internal functions by default.
162417163345
** Testing use only!!! */
162418163346
db->mDbFlags |= DBFLAG_InternalFunc;
@@ -162893,10 +163821,17 @@
162893163821
}else if( op==SQLITE_FCNTL_JOURNAL_POINTER ){
162894163822
*(sqlite3_file**)pArg = sqlite3PagerJrnlFile(pPager);
162895163823
rc = SQLITE_OK;
162896163824
}else if( op==SQLITE_FCNTL_DATA_VERSION ){
162897163825
*(unsigned int*)pArg = sqlite3PagerDataVersion(pPager);
163826
+ rc = SQLITE_OK;
163827
+ }else if( op==SQLITE_FCNTL_RESERVE_BYTES ){
163828
+ int iNew = *(int*)pArg;
163829
+ *(int*)pArg = sqlite3BtreeGetRequestedReserve(pBtree);
163830
+ if( iNew>=0 && iNew<=255 ){
163831
+ sqlite3BtreeSetPageSize(pBtree, 0, iNew, 0);
163832
+ }
162898163833
rc = SQLITE_OK;
162899163834
}else{
162900163835
rc = sqlite3OsFileControl(fd, op, pArg);
162901163836
}
162902163837
sqlite3BtreeLeave(pBtree);
@@ -163110,24 +164045,10 @@
163110164045
case SQLITE_TESTCTRL_BYTEORDER: {
163111164046
rc = SQLITE_BYTEORDER*100 + SQLITE_LITTLEENDIAN*10 + SQLITE_BIGENDIAN;
163112164047
break;
163113164048
}
163114164049
163115
- /* sqlite3_test_control(SQLITE_TESTCTRL_RESERVE, sqlite3 *db, int N)
163116
- **
163117
- ** Set the nReserve size to N for the main database on the database
163118
- ** connection db.
163119
- */
163120
- case SQLITE_TESTCTRL_RESERVE: {
163121
- sqlite3 *db = va_arg(ap, sqlite3*);
163122
- int x = va_arg(ap,int);
163123
- sqlite3_mutex_enter(db->mutex);
163124
- sqlite3BtreeSetPageSize(db->aDb[0].pBt, 0, x, 0);
163125
- sqlite3_mutex_leave(db->mutex);
163126
- break;
163127
- }
163128
-
163129164050
/* sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, sqlite3 *db, int N)
163130164051
**
163131164052
** Enable or disable various optimizations for testing purposes. The
163132164053
** argument N is a bitmask of optimizations to be disabled. For normal
163133164054
** operation N should be 0. The idea is that a test program (like the
@@ -165185,10 +166106,11 @@
165185166106
SQLITE_PRIVATE void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,int*,u8*);
165186166107
SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *);
165187166108
SQLITE_PRIVATE int sqlite3Fts3FirstFilter(sqlite3_int64, char *, int, char *);
165188166109
SQLITE_PRIVATE void sqlite3Fts3CreateStatTable(int*, Fts3Table*);
165189166110
SQLITE_PRIVATE int sqlite3Fts3EvalTestDeferred(Fts3Cursor *pCsr, int *pRc);
166111
+SQLITE_PRIVATE int sqlite3Fts3ReadInt(const char *z, int *pnOut);
165190166112
165191166113
/* fts3_tokenizer.c */
165192166114
SQLITE_PRIVATE const char *sqlite3Fts3NextToken(const char *, int *);
165193166115
SQLITE_PRIVATE int sqlite3Fts3InitHashTable(sqlite3 *, Fts3Hash *, const char *);
165194166116
SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(Fts3Hash *pHash, const char *,
@@ -165916,10 +166838,26 @@
165916166838
fts3Appendf(pRc, &zRet, ", ?");
165917166839
}
165918166840
sqlite3_free(zFree);
165919166841
return zRet;
165920166842
}
166843
+
166844
+/*
166845
+** Buffer z contains a positive integer value encoded as utf-8 text.
166846
+** Decode this value and store it in *pnOut, returning the number of bytes
166847
+** consumed. If an overflow error occurs return a negative value.
166848
+*/
166849
+SQLITE_PRIVATE int sqlite3Fts3ReadInt(const char *z, int *pnOut){
166850
+ u64 iVal = 0;
166851
+ int i;
166852
+ for(i=0; z[i]>='0' && z[i]<='9'; i++){
166853
+ iVal = iVal*10 + (z[i] - '0');
166854
+ if( iVal>0x7FFFFFFF ) return -1;
166855
+ }
166856
+ *pnOut = (int)iVal;
166857
+ return i;
166858
+}
165921166859
165922166860
/*
165923166861
** This function interprets the string at (*pp) as a non-negative integer
165924166862
** value. It reads the integer and sets *pnOut to the value read, then
165925166863
** sets *pp to point to the byte immediately following the last byte of
@@ -165932,23 +166870,21 @@
165932166870
**
165933166871
** This function is used when parsing the "prefix=" FTS4 parameter.
165934166872
*/
165935166873
static int fts3GobbleInt(const char **pp, int *pnOut){
165936166874
const int MAX_NPREFIX = 10000000;
165937
- const char *p; /* Iterator pointer */
165938166875
int nInt = 0; /* Output value */
165939
-
165940
- for(p=*pp; p[0]>='0' && p[0]<='9'; p++){
165941
- nInt = nInt * 10 + (p[0] - '0');
165942
- if( nInt>MAX_NPREFIX ){
165943
- nInt = 0;
165944
- break;
165945
- }
165946
- }
165947
- if( p==*pp ) return SQLITE_ERROR;
166876
+ int nByte;
166877
+ nByte = sqlite3Fts3ReadInt(*pp, &nInt);
166878
+ if( nInt>MAX_NPREFIX ){
166879
+ nInt = 0;
166880
+ }
166881
+ if( nByte==0 ){
166882
+ return SQLITE_ERROR;
166883
+ }
165948166884
*pnOut = nInt;
165949
- *pp = p;
166885
+ *pp += nByte;
165950166886
return SQLITE_OK;
165951166887
}
165952166888
165953166889
/*
165954166890
** This function is called to allocate an array of Fts3Index structures
@@ -167126,11 +168062,13 @@
167126168062
static void fts3ReadNextPos(
167127168063
char **pp, /* IN/OUT: Pointer into position-list buffer */
167128168064
sqlite3_int64 *pi /* IN/OUT: Value read from position-list */
167129168065
){
167130168066
if( (**pp)&0xFE ){
167131
- fts3GetDeltaVarint(pp, pi);
168067
+ int iVal;
168068
+ *pp += fts3GetVarint32((*pp), &iVal);
168069
+ *pi += iVal;
167132168070
*pi -= 2;
167133168071
}else{
167134168072
*pi = POSITION_LIST_END;
167135168073
}
167136168074
}
@@ -172026,14 +172964,11 @@
172026172964
172027172965
/* If this is a "NEAR" keyword, check for an explicit nearness. */
172028172966
if( pKey->eType==FTSQUERY_NEAR ){
172029172967
assert( nKey==4 );
172030172968
if( zInput[4]=='/' && zInput[5]>='0' && zInput[5]<='9' ){
172031
- nNear = 0;
172032
- for(nKey=5; zInput[nKey]>='0' && zInput[nKey]<='9'; nKey++){
172033
- nNear = nNear * 10 + (zInput[nKey] - '0');
172034
- }
172969
+ nKey += 1+sqlite3Fts3ReadInt(&zInput[nKey+1], &nNear);
172035172970
}
172036172971
}
172037172972
172038172973
/* At this point this is probably a keyword. But for that to be true,
172039172974
** the next byte must contain either whitespace, an open or close
@@ -176558,10 +177493,11 @@
176558177493
** b-tree node. And that the final byte of the doclist is 0x00. If either
176559177494
** of these statements is untrue, then the data structure is corrupt.
176560177495
*/
176561177496
if( pReader->nDoclist > pReader->nNode-(pReader->aDoclist-pReader->aNode)
176562177497
|| (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1])
177498
+ || pReader->nDoclist==0
176563177499
){
176564177500
return FTS_CORRUPT_VTAB;
176565177501
}
176566177502
return SQLITE_OK;
176567177503
}
@@ -178211,25 +179147,25 @@
178211179147
){
178212179148
const unsigned char *zText = sqlite3_column_text(pStmt, iCol);
178213179149
if( zText ){
178214179150
int i;
178215179151
int iMul = 1;
178216
- i64 iVal = 0;
179152
+ u64 iVal = 0;
178217179153
for(i=0; zText[i]>='0' && zText[i]<='9'; i++){
178218179154
iVal = iVal*10 + (zText[i] - '0');
178219179155
}
178220
- *piEndBlock = iVal;
179156
+ *piEndBlock = (i64)iVal;
178221179157
while( zText[i]==' ' ) i++;
178222179158
iVal = 0;
178223179159
if( zText[i]=='-' ){
178224179160
i++;
178225179161
iMul = -1;
178226179162
}
178227179163
for(/* no-op */; zText[i]>='0' && zText[i]<='9'; i++){
178228179164
iVal = iVal*10 + (zText[i] - '0');
178229179165
}
178230
- *pnByte = (iVal * (i64)iMul);
179166
+ *pnByte = ((i64)iVal * (i64)iMul);
178231179167
}
178232179168
}
178233179169
178234179170
178235179171
/*
@@ -223728,11 +224664,11 @@
223728224664
int nArg, /* Number of args */
223729224665
sqlite3_value **apUnused /* Function arguments */
223730224666
){
223731224667
assert( nArg==0 );
223732224668
UNUSED_PARAM2(nArg, apUnused);
223733
- sqlite3_result_text(pCtx, "fts5: 2020-03-21 23:10:38 5d14a1c4f2fc17de98ad685ad1422cdfda89dfccb00afcaf32ee416b6f84f525", -1, SQLITE_TRANSIENT);
224669
+ sqlite3_result_text(pCtx, "fts5: 2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f9654ff", -1, SQLITE_TRANSIENT);
223734224670
}
223735224671
223736224672
/*
223737224673
** Return true if zName is the extension on one of the shadow tables used
223738224674
** by this module.
@@ -228379,11 +229315,12 @@
228379229315
}
228380229316
case STMT_COLUMN_BUSY: {
228381229317
sqlite3_result_int(ctx, sqlite3_stmt_busy(pCur->pStmt));
228382229318
break;
228383229319
}
228384
- case STMT_COLUMN_MEM: {
229320
+ default: {
229321
+ assert( i==STMT_COLUMN_MEM );
228385229322
i = SQLITE_STMTSTATUS_MEMUSED +
228386229323
STMT_COLUMN_NSCAN - SQLITE_STMTSTATUS_FULLSCAN_STEP;
228387229324
/* Fall thru */
228388229325
}
228389229326
case STMT_COLUMN_NSCAN:
@@ -228510,12 +229447,12 @@
228510229447
}
228511229448
#endif /* SQLITE_CORE */
228512229449
#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
228513229450
228514229451
/************** End of stmt.c ************************************************/
228515
-#if __LINE__!=228515
229452
+#if __LINE__!=229452
228516229453
#undef SQLITE_SOURCE_ID
228517
-#define SQLITE_SOURCE_ID "2020-03-21 23:10:38 5d14a1c4f2fc17de98ad685ad1422cdfda89dfccb00afcaf32ee416b6f84alt2"
229454
+#define SQLITE_SOURCE_ID "2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f96alt2"
228518229455
#endif
228519229456
/* Return the source-id for this library */
228520229457
SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
228521229458
/************************** End of sqlite3.c ******************************/
228522229459
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -216,10 +216,13 @@
216 "ENABLE_ATOMIC_WRITE",
217 #endif
218 #if SQLITE_ENABLE_BATCH_ATOMIC_WRITE
219 "ENABLE_BATCH_ATOMIC_WRITE",
220 #endif
 
 
 
221 #if SQLITE_ENABLE_CEROD
222 "ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD),
223 #endif
224 #if SQLITE_ENABLE_COLUMN_METADATA
225 "ENABLE_COLUMN_METADATA",
@@ -534,13 +537,10 @@
534 "OMIT_BETWEEN_OPTIMIZATION",
535 #endif
536 #if SQLITE_OMIT_BLOB_LITERAL
537 "OMIT_BLOB_LITERAL",
538 #endif
539 #if SQLITE_OMIT_BTREECOUNT
540 "OMIT_BTREECOUNT",
541 #endif
542 #if SQLITE_OMIT_CAST
543 "OMIT_CAST",
544 #endif
545 #if SQLITE_OMIT_CHECK
546 "OMIT_CHECK",
@@ -1162,11 +1162,11 @@
1162 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
1163 ** [sqlite_version()] and [sqlite_source_id()].
1164 */
1165 #define SQLITE_VERSION "3.32.0"
1166 #define SQLITE_VERSION_NUMBER 3032000
1167 #define SQLITE_SOURCE_ID "2020-03-21 23:10:38 5d14a1c4f2fc17de98ad685ad1422cdfda89dfccb00afcaf32ee416b6f84f525"
1168
1169 /*
1170 ** CAPI3REF: Run-Time Library Version Numbers
1171 ** KEYWORDS: sqlite3_version sqlite3_sourceid
1172 **
@@ -1336,30 +1336,26 @@
1336 ** for the [sqlite3] object.
1337 ** ^Calls to sqlite3_close() and sqlite3_close_v2() return [SQLITE_OK] if
1338 ** the [sqlite3] object is successfully destroyed and all associated
1339 ** resources are deallocated.
1340 **
 
 
 
 
1341 ** ^If the database connection is associated with unfinalized prepared
1342 ** statements or unfinished sqlite3_backup objects then sqlite3_close()
1343 ** will leave the database connection open and return [SQLITE_BUSY].
1344 ** ^If sqlite3_close_v2() is called with unfinalized prepared statements
1345 ** and/or unfinished sqlite3_backups, then the database connection becomes
1346 ** an unusable "zombie" which will automatically be deallocated when the
1347 ** last prepared statement is finalized or the last sqlite3_backup is
1348 ** finished. The sqlite3_close_v2() interface is intended for use with
1349 ** host languages that are garbage collected, and where the order in which
1350 ** destructors are called is arbitrary.
1351 **
1352 ** Applications should [sqlite3_finalize | finalize] all [prepared statements],
1353 ** [sqlite3_blob_close | close] all [BLOB handles], and
1354 ** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated
1355 ** with the [sqlite3] object prior to attempting to close the object. ^If
1356 ** sqlite3_close_v2() is called on a [database connection] that still has
1357 ** outstanding [prepared statements], [BLOB handles], and/or
1358 ** [sqlite3_backup] objects then it returns [SQLITE_OK] and the deallocation
1359 ** of resources is deferred until all [prepared statements], [BLOB handles],
1360 ** and [sqlite3_backup] objects are also destroyed.
1361 **
1362 ** ^If an [sqlite3] object is destroyed while a transaction is open,
1363 ** the transaction is automatically rolled back.
1364 **
1365 ** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)]
@@ -1544,22 +1540,25 @@
1544 #define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8))
1545 #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
1546 #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8))
1547 #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
1548 #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
 
1549 #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
1550 #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
1551 #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
1552 #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
 
1553 #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
1554 #define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8))
1555 #define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8))
1556 #define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8))
1557 #define SQLITE_CANTOPEN_DIRTYWAL (SQLITE_CANTOPEN | (5<<8)) /* Not Used */
1558 #define SQLITE_CANTOPEN_SYMLINK (SQLITE_CANTOPEN | (6<<8))
1559 #define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
1560 #define SQLITE_CORRUPT_SEQUENCE (SQLITE_CORRUPT | (2<<8))
 
1561 #define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
1562 #define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
1563 #define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8))
1564 #define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4<<8))
1565 #define SQLITE_READONLY_CANTINIT (SQLITE_READONLY | (5<<8))
@@ -2124,14 +2123,16 @@
2124 ** so that all subsequent write operations are independent.
2125 ** ^SQLite will never invoke SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE without
2126 ** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE].
2127 **
2128 ** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]]
2129 ** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode causes attempts to obtain
2130 ** a file lock using the xLock or xShmLock methods of the VFS to wait
2131 ** for up to M milliseconds before failing, where M is the single
2132 ** unsigned integer parameter.
 
 
2133 **
2134 ** <li>[[SQLITE_FCNTL_DATA_VERSION]]
2135 ** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
2136 ** a database file. The argument is a pointer to a 32-bit unsigned integer.
2137 ** The "data version" for the pager is written into the pointer. The
@@ -2148,10 +2149,15 @@
2148 ** a single attached database that occur due to other database connections,
2149 ** but omits changes implemented by the database connection on which it is
2150 ** called. This file control is the only mechanism to detect changes that
2151 ** happen either internally or externally and that are associated with
2152 ** a particular attached database.
 
 
 
 
 
2153 **
2154 ** <li>[[SQLITE_FCNTL_CKPT_DONE]]
2155 ** The [SQLITE_FCNTL_CKPT_DONE] opcode is invoked from within a checkpoint
2156 ** in wal mode after the client has finished copying pages from the wal
2157 ** file to the database file, but before the *-shm file is updated to
@@ -2192,10 +2198,12 @@
2192 #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33
2193 #define SQLITE_FCNTL_LOCK_TIMEOUT 34
2194 #define SQLITE_FCNTL_DATA_VERSION 35
2195 #define SQLITE_FCNTL_SIZE_LIMIT 36
2196 #define SQLITE_FCNTL_CKPT_DONE 37
 
 
2197
2198 /* deprecated names */
2199 #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
2200 #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE
2201 #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO
@@ -4570,12 +4578,23 @@
4570 **
4571 ** These are utility routines, useful to [VFS|custom VFS implementations],
4572 ** that check if a database file was a URI that contained a specific query
4573 ** parameter, and if so obtains the value of that query parameter.
4574 **
4575 ** If F is the database filename pointer passed into the xOpen() method of
4576 ** a VFS implementation or it is the return value of [sqlite3_db_filename()]
 
 
 
 
 
 
 
 
 
 
 
4577 ** and if P is the name of the query parameter, then
4578 ** sqlite3_uri_parameter(F,P) returns the value of the P
4579 ** parameter if it exists or a NULL pointer if P does not appear as a
4580 ** query parameter on F. If P is a query parameter of F and it
4581 ** has no explicit value, then sqlite3_uri_parameter(F,P) returns
@@ -4654,10 +4673,29 @@
4654 */
4655 SQLITE_API const char *sqlite3_filename_database(const char*);
4656 SQLITE_API const char *sqlite3_filename_journal(const char*);
4657 SQLITE_API const char *sqlite3_filename_wal(const char*);
4658
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4659 /*
4660 ** CAPI3REF: Create and Destroy VFS Filenames
4661 **
4662 ** These interfces are provided for use by [VFS shim] implementations and
4663 ** are not useful outside of that context.
@@ -4688,11 +4726,11 @@
4688 ** None of the D, J, or W parameters to sqlite3_create_filename(D,J,W,N,P) may
4689 ** be NULL pointers, though they can be empty strings.
4690 **
4691 ** The sqlite3_free_filename(Y) routine releases a memory allocation
4692 ** previously obtained from sqlite3_create_filename(). Invoking
4693 ** sqlite3_free_filename(Y) is a NULL pointer is a harmless no-op.
4694 **
4695 ** If the Y parameter to sqlite3_free_filename(Y) is anything other
4696 ** than a NULL pointer or a pointer previously acquired from
4697 ** sqlite3_create_filename(), then bad things such as heap
4698 ** corruption or segfaults may occur. The value Y should be
@@ -5295,10 +5333,28 @@
5295 **
5296 ** ^The third argument is the value to bind to the parameter.
5297 ** ^If the third parameter to sqlite3_bind_text() or sqlite3_bind_text16()
5298 ** or sqlite3_bind_blob() is a NULL pointer then the fourth parameter
5299 ** is ignored and the end result is the same as sqlite3_bind_null().
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5300 **
5301 ** ^(In those routines that have a fourth argument, its value is the
5302 ** number of bytes in the parameter. To be clear: the value is the
5303 ** number of <u>bytes</u> in the value, not the number of characters.)^
5304 ** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16()
@@ -5308,11 +5364,11 @@
5308 ** the behavior is undefined.
5309 ** If a non-negative fourth parameter is provided to sqlite3_bind_text()
5310 ** or sqlite3_bind_text16() or sqlite3_bind_text64() then
5311 ** that parameter must be the byte offset
5312 ** where the NUL terminator would occur assuming the string were NUL
5313 ** terminated. If any NUL characters occur at byte offsets less than
5314 ** the value of the fourth parameter then the resulting string value will
5315 ** contain embedded NULs. The result of expressions involving strings
5316 ** with embedded NULs is undefined.
5317 **
5318 ** ^The fifth argument to the BLOB and string binding interfaces
@@ -6633,12 +6689,13 @@
6633 ** cause the implemented SQL function to throw an exception.
6634 ** ^SQLite uses the string pointed to by the
6635 ** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16()
6636 ** as the text of an error message. ^SQLite interprets the error
6637 ** message string from sqlite3_result_error() as UTF-8. ^SQLite
6638 ** interprets the string from sqlite3_result_error16() as UTF-16 in native
6639 ** byte order. ^If the third parameter to sqlite3_result_error()
 
6640 ** or sqlite3_result_error16() is negative then SQLite takes as the error
6641 ** message all text up through the first zero character.
6642 ** ^If the third parameter to sqlite3_result_error() or
6643 ** sqlite3_result_error16() is non-negative then SQLite takes that many
6644 ** bytes (not characters) from the 2nd parameter as the error message.
@@ -6701,10 +6758,29 @@
6701 ** when it has finished using that result.
6702 ** ^If the 4th parameter to the sqlite3_result_text* interfaces
6703 ** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT
6704 ** then SQLite makes a copy of the result into space obtained
6705 ** from [sqlite3_malloc()] before it returns.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6706 **
6707 ** ^The sqlite3_result_value() interface sets the result of
6708 ** the application-defined function to be a copy of the
6709 ** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The
6710 ** sqlite3_result_value() interface makes a copy of the [sqlite3_value]
@@ -8649,11 +8725,11 @@
8649 #define SQLITE_TESTCTRL_FAULT_INSTALL 9
8650 #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
8651 #define SQLITE_TESTCTRL_PENDING_BYTE 11
8652 #define SQLITE_TESTCTRL_ASSERT 12
8653 #define SQLITE_TESTCTRL_ALWAYS 13
8654 #define SQLITE_TESTCTRL_RESERVE 14
8655 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15
8656 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */
8657 #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */
8658 #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17
8659 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
@@ -13369,10 +13445,25 @@
13369 #pragma warn -aus /* Assigned value is never used */
13370 #pragma warn -csu /* Comparing signed and unsigned */
13371 #pragma warn -spa /* Suspicious pointer arithmetic */
13372 #endif
13373
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13374 /*
13375 ** Include standard header files as necessary
13376 */
13377 #ifdef HAVE_STDINT_H
13378 #include <stdint.h>
@@ -14446,11 +14537,10 @@
14446 typedef struct BusyHandler BusyHandler;
14447 struct BusyHandler {
14448 int (*xBusyHandler)(void *,int); /* The busy callback */
14449 void *pBusyArg; /* First arg to busy callback */
14450 int nBusy; /* Incremented with each busy call */
14451 u8 bExtraFileArg; /* Include sqlite3_file as callback arg */
14452 };
14453
14454 /*
14455 ** Name of the master database table. The master database table
14456 ** is a special table that holds the names and attributes of all
@@ -14704,11 +14794,11 @@
14704 SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix);
14705 SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*);
14706 SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int);
14707 SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree*);
14708 SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree*,int);
14709 SQLITE_PRIVATE int sqlite3BtreeGetOptimalReserve(Btree*);
14710 SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p);
14711 SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int);
14712 SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *);
14713 SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int,int*);
14714 SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
@@ -14966,13 +15056,11 @@
14966 #ifndef NDEBUG
14967 SQLITE_PRIVATE int sqlite3BtreeCursorIsValid(BtCursor*);
14968 #endif
14969 SQLITE_PRIVATE int sqlite3BtreeCursorIsValidNN(BtCursor*);
14970
14971 #ifndef SQLITE_OMIT_BTREECOUNT
14972 SQLITE_PRIVATE int sqlite3BtreeCount(sqlite3*, BtCursor*, i64*);
14973 #endif
14974
14975 #ifdef SQLITE_TEST
14976 SQLITE_PRIVATE int sqlite3BtreeCursorInfo(BtCursor*, int*, int);
14977 SQLITE_PRIVATE void sqlite3BtreeCursorList(Btree*);
14978 #endif
@@ -15543,10 +15631,13 @@
15543
15544 SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *);
15545 SQLITE_PRIVATE int sqlite3VdbeHasSubProgram(Vdbe*);
15546
15547 SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*);
 
 
 
15548
15549 /* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on
15550 ** each VDBE opcode.
15551 **
15552 ** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op
@@ -15828,17 +15919,25 @@
15828 SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager);
15829 SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager);
15830 SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
15831 SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3*);
15832 # ifdef SQLITE_ENABLE_SNAPSHOT
15833 SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager *pPager, sqlite3_snapshot **ppSnapshot);
15834 SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot);
15835 SQLITE_PRIVATE int sqlite3PagerSnapshotRecover(Pager *pPager);
15836 SQLITE_PRIVATE int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot);
15837 SQLITE_PRIVATE void sqlite3PagerSnapshotUnlock(Pager *pPager);
15838 # endif
15839 #endif
 
 
 
 
 
 
 
 
15840
15841 #ifdef SQLITE_DIRECT_OVERFLOW_READ
15842 SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno);
15843 #endif
15844
@@ -15861,15 +15960,10 @@
15861 SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*);
15862 SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*);
15863 SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *);
15864 SQLITE_PRIVATE void sqlite3PagerClearCache(Pager*);
15865 SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *);
15866 #ifdef SQLITE_ENABLE_SETLK_TIMEOUT
15867 SQLITE_PRIVATE void sqlite3PagerResetLockTimeout(Pager *pPager);
15868 #else
15869 # define sqlite3PagerResetLockTimeout(X)
15870 #endif
15871
15872 /* Functions used to truncate the database file. */
15873 SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno);
15874
15875 SQLITE_PRIVATE void sqlite3PagerRekey(DbPage*, Pgno, u16);
@@ -16799,10 +16893,11 @@
16799 Hash aFunc; /* Hash table of connection functions */
16800 Hash aCollSeq; /* All collating sequences */
16801 BusyHandler busyHandler; /* Busy callback */
16802 Db aDbStatic[2]; /* Static space for the 2 default backends */
16803 Savepoint *pSavepoint; /* List of active savepoints */
 
16804 int busyTimeout; /* Busy handler timeout, in msec */
16805 int nSavepoint; /* Number of non-transaction savepoints */
16806 int nStatement; /* Number of nested statement-transactions */
16807 i64 nDeferredCons; /* Net deferred constraints this transaction. */
16808 i64 nDeferredImmCons; /* Net deferred immediate constraints */
@@ -17209,10 +17304,11 @@
17209 Expr *pDflt; /* Default value or GENERATED ALWAYS AS value */
17210 char *zColl; /* Collating sequence. If NULL, use the default */
17211 u8 notNull; /* An OE_ code for handling a NOT NULL constraint */
17212 char affinity; /* One of the SQLITE_AFF_... values */
17213 u8 szEst; /* Estimated size of value in this column. sizeof(INT)==1 */
 
17214 u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */
17215 };
17216
17217 /* Allowed values for Column.colFlags:
17218 */
@@ -19827,10 +19923,11 @@
19827 const char*,
19828 const char*,
19829 const char*
19830 );
19831 SQLITE_PRIVATE Bitmask sqlite3ExprColUsed(Expr*);
 
19832 SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*);
19833 SQLITE_PRIVATE int sqlite3ResolveExprListNames(NameContext*, ExprList*);
19834 SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*);
19835 SQLITE_PRIVATE int sqlite3ResolveSelfReference(Parse*,Table*,int,Expr*,ExprList*);
19836 SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*);
@@ -19842,11 +19939,11 @@
19842 SQLITE_PRIVATE void sqlite3RenameExprUnmap(Parse*, Expr*);
19843 SQLITE_PRIVATE void sqlite3RenameExprlistUnmap(Parse*, ExprList*);
19844 SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*);
19845 SQLITE_PRIVATE char sqlite3AffinityType(const char*, Column*);
19846 SQLITE_PRIVATE void sqlite3Analyze(Parse*, Token*, Token*);
19847 SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler*, sqlite3_file*);
19848 SQLITE_PRIVATE int sqlite3FindDb(sqlite3*, Token*);
19849 SQLITE_PRIVATE int sqlite3FindDbName(sqlite3 *, const char *);
19850 SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3*,int iDB);
19851 SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3*,Index*);
19852 SQLITE_PRIVATE void sqlite3DefaultRowEst(Index*);
@@ -20578,11 +20675,12 @@
20578 /*
20579 ** VDBE_DISPLAY_P4 is true or false depending on whether or not the
20580 ** "explain" P4 display logic is enabled.
20581 */
20582 #if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \
20583 || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG)
 
20584 # define VDBE_DISPLAY_P4 1
20585 #else
20586 # define VDBE_DISPLAY_P4 0
20587 #endif
20588
@@ -21043,11 +21141,18 @@
21043
21044 int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *);
21045 SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(sqlite3*,VdbeCursor*,UnpackedRecord*,int*);
21046 SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3*, BtCursor*, i64*);
21047 SQLITE_PRIVATE int sqlite3VdbeExec(Vdbe*);
21048 #ifndef SQLITE_OMIT_EXPLAIN
 
 
 
 
 
 
 
21049 SQLITE_PRIVATE int sqlite3VdbeList(Vdbe*);
21050 #endif
21051 SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe*);
21052 SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *, int);
21053 SQLITE_PRIVATE int sqlite3VdbeMemTooBig(Mem*);
@@ -21085,11 +21190,11 @@
21085 SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p);
21086 SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
21087 #ifndef SQLITE_OMIT_WINDOWFUNC
21088 SQLITE_PRIVATE int sqlite3VdbeMemAggValue(Mem*, Mem*, FuncDef*);
21089 #endif
21090 #ifndef SQLITE_OMIT_EXPLAIN
21091 SQLITE_PRIVATE const char *sqlite3OpcodeName(int);
21092 #endif
21093 SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
21094 SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int n);
21095 SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int);
@@ -22165,16 +22270,16 @@
22165 u8 nName; /* Length of th name */
22166 char *zName; /* Name of the transformation */
22167 double rLimit; /* Maximum NNN value for this transform */
22168 double rXform; /* Constant used for this transform */
22169 } aXformType[] = {
22170 { 0, 6, "second", 464269060800.0, 86400000.0/(24.0*60.0*60.0) },
22171 { 0, 6, "minute", 7737817680.0, 86400000.0/(24.0*60.0) },
22172 { 0, 4, "hour", 128963628.0, 86400000.0/24.0 },
22173 { 0, 3, "day", 5373485.0, 86400000.0 },
22174 { 1, 5, "month", 176546.0, 30.0*86400000.0 },
22175 { 2, 4, "year", 14713.0, 365.0*86400000.0 },
22176 };
22177
22178 /*
22179 ** Process a modifier to a date-time stamp. The modifiers are
22180 ** as follows:
@@ -27258,11 +27363,11 @@
27258 if( mem0.hardLimit>0 && (n>mem0.hardLimit || n==0) ){
27259 n = mem0.hardLimit;
27260 }
27261 mem0.alarmThreshold = n;
27262 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
27263 mem0.nearlyFull = (n>0 && n<=nUsed);
27264 sqlite3_mutex_leave(mem0.mutex);
27265 excess = sqlite3_memory_used() - n;
27266 if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff));
27267 return priorLimit;
27268 }
@@ -27326,11 +27431,11 @@
27326 ** Return true if the heap is currently under memory pressure - in other
27327 ** words if the amount of heap used is close to the limit set by
27328 ** sqlite3_soft_heap_limit().
27329 */
27330 SQLITE_PRIVATE int sqlite3HeapNearlyFull(void){
27331 return mem0.nearlyFull;
27332 }
27333
27334 /*
27335 ** Deinitialize the memory allocation subsystem.
27336 */
@@ -27390,21 +27495,21 @@
27390
27391 sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, n);
27392 if( mem0.alarmThreshold>0 ){
27393 sqlite3_int64 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
27394 if( nUsed >= mem0.alarmThreshold - nFull ){
27395 mem0.nearlyFull = 1;
27396 sqlite3MallocAlarm(nFull);
27397 if( mem0.hardLimit ){
27398 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
27399 if( nUsed >= mem0.hardLimit - nFull ){
27400 *pp = 0;
27401 return;
27402 }
27403 }
27404 }else{
27405 mem0.nearlyFull = 0;
27406 }
27407 }
27408 p = sqlite3GlobalConfig.m.xMalloc(nFull);
27409 #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
27410 if( p==0 && mem0.alarmThreshold>0 ){
@@ -27629,14 +27734,16 @@
27629 if( nDiff>0 && sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >=
27630 mem0.alarmThreshold-nDiff ){
27631 sqlite3MallocAlarm(nDiff);
27632 }
27633 pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
 
27634 if( pNew==0 && mem0.alarmThreshold>0 ){
27635 sqlite3MallocAlarm((int)nBytes);
27636 pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
27637 }
 
27638 if( pNew ){
27639 nNew = sqlite3MallocSize(pNew);
27640 sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nNew-nOld);
27641 }
27642 sqlite3_mutex_leave(mem0.mutex);
@@ -27907,11 +28014,11 @@
27907 */
27908 SQLITE_PRIVATE void sqlite3OomFault(sqlite3 *db){
27909 if( db->mallocFailed==0 && db->bBenignMalloc==0 ){
27910 db->mallocFailed = 1;
27911 if( db->nVdbeExec>0 ){
27912 db->u1.isInterrupted = 1;
27913 }
27914 DisableLookaside;
27915 if( db->pParse ){
27916 db->pParse->rc = SQLITE_NOMEM_BKPT;
27917 }
@@ -27926,11 +28033,11 @@
27926 ** VDBEs.
27927 */
27928 SQLITE_PRIVATE void sqlite3OomClear(sqlite3 *db){
27929 if( db->mallocFailed && db->nVdbeExec==0 ){
27930 db->mallocFailed = 0;
27931 db->u1.isInterrupted = 0;
27932 assert( db->lookaside.bDisable>0 );
27933 EnableLookaside;
27934 }
27935 }
27936
@@ -31300,10 +31407,23 @@
31300 a = (unsigned char *)zLeft;
31301 b = (unsigned char *)zRight;
31302 while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; }
31303 return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b];
31304 }
 
 
 
 
 
 
 
 
 
 
 
 
 
31305
31306 /*
31307 ** Compute 10 to the E-th power. Examples: E==1 results in 10.
31308 ** E==2 results in 100. E==50 results in 1.0e50.
31309 **
@@ -34857,20 +34977,21 @@
34857 static int osSetPosixAdvisoryLock(
34858 int h, /* The file descriptor on which to take the lock */
34859 struct flock *pLock, /* The description of the lock */
34860 unixFile *pFile /* Structure holding timeout value */
34861 ){
 
34862 int rc = osFcntl(h,F_SETLK,pLock);
34863 while( rc<0 && pFile->iBusyTimeout>0 ){
34864 /* On systems that support some kind of blocking file lock with a timeout,
34865 ** make appropriate changes here to invoke that blocking file lock. On
34866 ** generic posix, however, there is no such API. So we simply try the
34867 ** lock once every millisecond until either the timeout expires, or until
34868 ** the lock is obtained. */
34869 usleep(1000);
34870 rc = osFcntl(h,F_SETLK,pLock);
34871 pFile->iBusyTimeout--;
34872 }
34873 return rc;
34874 }
34875 #endif /* SQLITE_ENABLE_SETLK_TIMEOUT */
34876
@@ -36977,11 +37098,11 @@
36977 zDirname[ii] = '\0';
36978 }else{
36979 if( zDirname[0]!='/' ) zDirname[0] = '.';
36980 zDirname[1] = 0;
36981 }
36982 fd = robust_open(zDirname, O_RDONLY|O_BINARY|O_NOFOLLOW, 0);
36983 if( fd>=0 ){
36984 OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname));
36985 }
36986 *pFd = fd;
36987 if( fd>=0 ) return SQLITE_OK;
@@ -37287,11 +37408,13 @@
37287 *(int*)pArg = fileHasMoved(pFile);
37288 return SQLITE_OK;
37289 }
37290 #ifdef SQLITE_ENABLE_SETLK_TIMEOUT
37291 case SQLITE_FCNTL_LOCK_TIMEOUT: {
 
37292 pFile->iBusyTimeout = *(int*)pArg;
 
37293 return SQLITE_OK;
37294 }
37295 #endif
37296 #if SQLITE_MAX_MMAP_SIZE>0
37297 case SQLITE_FCNTL_MMAP_SIZE: {
@@ -37606,17 +37729,24 @@
37606
37607 /* Locks are within range */
37608 assert( n>=1 && n<=SQLITE_SHM_NLOCK );
37609
37610 if( pShmNode->hShm>=0 ){
 
37611 /* Initialize the locking parameters */
37612 f.l_type = lockType;
37613 f.l_whence = SEEK_SET;
37614 f.l_start = ofst;
37615 f.l_len = n;
37616 rc = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile);
37617 rc = (rc!=(-1)) ? SQLITE_OK : SQLITE_BUSY;
 
 
 
 
 
 
37618 }
37619
37620 /* Update the global lock state and do debug tracing */
37621 #ifdef SQLITE_DEBUG
37622 { u16 mask;
@@ -38108,10 +38238,29 @@
38108 || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED)
38109 || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
38110 assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 );
38111 assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 );
38112 assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38113
38114 mask = (1<<(ofst+n)) - (1<<ofst);
38115 assert( n>1 || mask==(1<<ofst) );
38116 sqlite3_mutex_enter(pShmNode->pShmMutex);
38117 if( flags & SQLITE_SHM_UNLOCK ){
@@ -51414,10 +51563,15 @@
51414 SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal);
51415 #endif
51416
51417 /* Return the sqlite3_file object for the WAL file */
51418 SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal);
 
 
 
 
 
51419
51420 #endif /* ifndef SQLITE_OMIT_WAL */
51421 #endif /* SQLITE_WAL_H */
51422
51423 /************** End of wal.h *************************************************/
@@ -53935,13 +54089,16 @@
53935 }
53936 if( exists ){
53937 /* One of the journals pointed to by the master journal exists.
53938 ** Open it and check if it points at the master journal. If
53939 ** so, return without deleting the master journal file.
 
 
 
53940 */
53941 int c;
53942 int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MAIN_JOURNAL);
53943 rc = sqlite3OsOpen(pVfs, zJournal, pJournal, flags, 0);
53944 if( rc!=SQLITE_OK ){
53945 goto delmaster_out;
53946 }
53947
@@ -56141,10 +56298,11 @@
56141 ** Pager object (sizeof(Pager) bytes)
56142 ** PCache object (sqlite3PcacheSize() bytes)
56143 ** Database file handle (pVfs->szOsFile bytes)
56144 ** Sub-journal file handle (journalFileSize bytes)
56145 ** Main journal file handle (journalFileSize bytes)
 
56146 ** \0\0\0\0 database prefix (4 bytes)
56147 ** Database file name (nPathname+1 bytes)
56148 ** URI query parameters (nUriByte bytes)
56149 ** Journal filename (nPathname+8+1 bytes)
56150 ** WAL filename (nPathname+4+1 bytes)
@@ -56180,10 +56338,11 @@
56180 pPtr = (u8 *)sqlite3MallocZero(
56181 ROUND8(sizeof(*pPager)) + /* Pager structure */
56182 ROUND8(pcacheSize) + /* PCache object */
56183 ROUND8(pVfs->szOsFile) + /* The main db file */
56184 journalFileSize * 2 + /* The two journal files */
 
56185 4 + /* Database prefix */
56186 nPathname + 1 + /* database filename */
56187 nUriByte + /* query parameters */
56188 nPathname + 8 + 1 + /* Journal filename */
56189 #ifndef SQLITE_OMIT_WAL
@@ -56200,10 +56359,11 @@
56200 pPager->pPCache = (PCache*)pPtr; pPtr += ROUND8(pcacheSize);
56201 pPager->fd = (sqlite3_file*)pPtr; pPtr += ROUND8(pVfs->szOsFile);
56202 pPager->sjfd = (sqlite3_file*)pPtr; pPtr += journalFileSize;
56203 pPager->jfd = (sqlite3_file*)pPtr; pPtr += journalFileSize;
56204 assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) );
 
56205
56206 /* Fill in the Pager.zFilename and pPager.zQueryParam fields */
56207 pPtr += 4; /* Skip zero prefix */
56208 pPager->zFilename = (char*)pPtr;
56209 if( nPathname>0 ){
@@ -56400,10 +56560,23 @@
56400
56401 *ppPager = pPager;
56402 return SQLITE_OK;
56403 }
56404
 
 
 
 
 
 
 
 
 
 
 
 
 
56405
56406
56407 /*
56408 ** This function is called after transitioning from PAGER_UNLOCK to
56409 ** PAGER_SHARED state. It tests if there is a hot journal present in
@@ -57085,11 +57258,10 @@
57085 Pager *pPager;
57086 assert( pPg!=0 );
57087 assert( pPg->pgno==1 );
57088 assert( (pPg->flags & PGHDR_MMAP)==0 ); /* Page1 is never memory mapped */
57089 pPager = pPg->pPager;
57090 sqlite3PagerResetLockTimeout(pPager);
57091 sqlite3PcacheRelease(pPg);
57092 pagerUnlockIfUnused(pPager);
57093 }
57094
57095 /*
@@ -58378,20 +58550,10 @@
58378 */
58379 SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager *pPager){
58380 return pPager->fd;
58381 }
58382
58383 #ifdef SQLITE_ENABLE_SETLK_TIMEOUT
58384 /*
58385 ** Reset the lock timeout for pager.
58386 */
58387 SQLITE_PRIVATE void sqlite3PagerResetLockTimeout(Pager *pPager){
58388 int x = 0;
58389 sqlite3OsFileControl(pPager->fd, SQLITE_FCNTL_LOCK_TIMEOUT, &x);
58390 }
58391 #endif
58392
58393 /*
58394 ** Return the file handle for the journal file (if it exists).
58395 ** This will be either the rollback journal or the WAL file.
58396 */
58397 SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager *pPager){
@@ -58801,11 +58963,10 @@
58801 (eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler),
58802 pPager->pBusyHandlerArg,
58803 pPager->walSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace,
58804 pnLog, pnCkpt
58805 );
58806 sqlite3PagerResetLockTimeout(pPager);
58807 }
58808 return rc;
58809 }
58810
58811 SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager){
@@ -58966,11 +59127,35 @@
58966 }
58967 }
58968 return rc;
58969 }
58970
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58971
 
 
 
 
 
 
 
 
 
 
58972
58973 #ifdef SQLITE_ENABLE_SNAPSHOT
58974 /*
58975 ** If this is a WAL database, obtain a snapshot handle for the snapshot
58976 ** currently open. Otherwise, return an error.
@@ -58986,11 +59171,14 @@
58986 /*
58987 ** If this is a WAL database, store a pointer to pSnapshot. Next time a
58988 ** read transaction is opened, attempt to read from the snapshot it
58989 ** identifies. If this is not a WAL database, return an error.
58990 */
58991 SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager *pPager, sqlite3_snapshot *pSnapshot){
 
 
 
58992 int rc = SQLITE_OK;
58993 if( pPager->pWal ){
58994 sqlite3WalSnapshotOpen(pPager->pWal, pSnapshot);
58995 }else{
58996 rc = SQLITE_ERROR;
@@ -59322,22 +59510,10 @@
59322 # define WALTRACE(X) if(sqlite3WalTrace) sqlite3DebugPrintf X
59323 #else
59324 # define WALTRACE(X)
59325 #endif
59326
59327 /*
59328 ** WAL mode depends on atomic aligned 32-bit loads and stores in a few
59329 ** places. The following macros try to make this explicit.
59330 */
59331 #if GCC_VESRION>=5004000
59332 # define AtomicLoad(PTR) __atomic_load_n((PTR),__ATOMIC_RELAXED)
59333 # define AtomicStore(PTR,VAL) __atomic_store_n((PTR),(VAL),__ATOMIC_RELAXED)
59334 #else
59335 # define AtomicLoad(PTR) (*(PTR))
59336 # define AtomicStore(PTR,VAL) (*(PTR) = (VAL))
59337 #endif
59338
59339 /*
59340 ** The maximum (and only) versions of the wal and wal-index formats
59341 ** that may be interpreted by this version of SQLite.
59342 **
59343 ** If a client begins recovering a WAL file and finds that (a) the checksum
@@ -59542,10 +59718,13 @@
59542 #ifdef SQLITE_DEBUG
59543 u8 lockError; /* True if a locking error has occurred */
59544 #endif
59545 #ifdef SQLITE_ENABLE_SNAPSHOT
59546 WalIndexHdr *pSnapshot; /* Start transaction here if not NULL */
 
 
 
59547 #endif
59548 };
59549
59550 /*
59551 ** Candidate values for Wal.exclusiveMode.
@@ -59916,11 +60095,11 @@
59916 if( pWal->exclusiveMode ) return SQLITE_OK;
59917 rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1,
59918 SQLITE_SHM_LOCK | SQLITE_SHM_SHARED);
59919 WALTRACE(("WAL%p: acquire SHARED-%s %s\n", pWal,
59920 walLockName(lockIdx), rc ? "failed" : "ok"));
59921 VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && rc!=SQLITE_BUSY); )
59922 return rc;
59923 }
59924 static void walUnlockShared(Wal *pWal, int lockIdx){
59925 if( pWal->exclusiveMode ) return;
59926 (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1,
@@ -59932,11 +60111,11 @@
59932 if( pWal->exclusiveMode ) return SQLITE_OK;
59933 rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, n,
59934 SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE);
59935 WALTRACE(("WAL%p: acquire EXCLUSIVE-%s cnt=%d %s\n", pWal,
59936 walLockName(lockIdx), n, rc ? "failed" : "ok"));
59937 VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && rc!=SQLITE_BUSY); )
59938 return rc;
59939 }
59940 static void walUnlockExclusive(Wal *pWal, int lockIdx, int n){
59941 if( pWal->exclusiveMode ) return;
59942 (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, n,
@@ -60751,10 +60930,93 @@
60751 p = 0;
60752 }
60753 *pp = p;
60754 return rc;
60755 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60756
60757 /*
60758 ** Attempt to obtain the exclusive WAL lock defined by parameters lockIdx and
60759 ** n. If the attempt fails and parameter xBusy is not NULL, then it is a
60760 ** busy-handler function. Invoke it and retry the lock until either the
@@ -60769,10 +61031,16 @@
60769 ){
60770 int rc;
60771 do {
60772 rc = walLockExclusive(pWal, lockIdx, n);
60773 }while( xBusy && rc==SQLITE_BUSY && xBusy(pBusyArg) );
 
 
 
 
 
 
60774 return rc;
60775 }
60776
60777 /*
60778 ** The cache of the wal-index header must be valid to call this function.
@@ -60939,10 +61207,11 @@
60939 ** about the eventual size of the db file to the VFS layer.
60940 */
60941 if( rc==SQLITE_OK ){
60942 i64 nReq = ((i64)mxPage * szPage);
60943 i64 nSize; /* Current size of database file */
 
60944 rc = sqlite3OsFileSize(pWal->pDbFd, &nSize);
60945 if( rc==SQLITE_OK && nSize<nReq ){
60946 sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq);
60947 }
60948 }
@@ -60950,11 +61219,11 @@
60950
60951 /* Iterate through the contents of the WAL, copying data to the db file */
60952 while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){
60953 i64 iOffset;
60954 assert( walFramePgno(pWal, iFrame)==iDbpage );
60955 if( db->u1.isInterrupted ){
60956 rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT;
60957 break;
60958 }
60959 if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ){
60960 continue;
@@ -60966,10 +61235,11 @@
60966 iOffset = (iDbpage-1)*(i64)szPage;
60967 testcase( IS_BIG_INT(iOffset) );
60968 rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset);
60969 if( rc!=SQLITE_OK ) break;
60970 }
 
60971
60972 /* If work was actually accomplished... */
60973 if( rc==SQLITE_OK ){
60974 if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){
60975 i64 szDb = pWal->hdr.nPage*(i64)szPage;
@@ -60977,14 +61247,10 @@
60977 rc = sqlite3OsTruncate(pWal->pDbFd, szDb);
60978 if( rc==SQLITE_OK ){
60979 rc = sqlite3OsSync(pWal->pDbFd, CKPT_SYNC_FLAGS(sync_flags));
60980 }
60981 }
60982 if( rc==SQLITE_OK ){
60983 rc = sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_DONE, 0);
60984 if( rc==SQLITE_NOTFOUND ) rc = SQLITE_OK;
60985 }
60986 if( rc==SQLITE_OK ){
60987 pInfo->nBackfill = mxSafeFrame;
60988 }
60989 }
60990
@@ -61250,32 +61516,36 @@
61250 badHdr = (page0 ? walIndexTryHdr(pWal, pChanged) : 1);
61251
61252 /* If the first attempt failed, it might have been due to a race
61253 ** with a writer. So get a WRITE lock and try again.
61254 */
61255 assert( badHdr==0 || pWal->writeLock==0 );
61256 if( badHdr ){
61257 if( pWal->bShmUnreliable==0 && (pWal->readOnly & WAL_SHM_RDONLY) ){
61258 if( SQLITE_OK==(rc = walLockShared(pWal, WAL_WRITE_LOCK)) ){
61259 walUnlockShared(pWal, WAL_WRITE_LOCK);
61260 rc = SQLITE_READONLY_RECOVERY;
61261 }
61262 }else if( SQLITE_OK==(rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1)) ){
61263 pWal->writeLock = 1;
61264 if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){
61265 badHdr = walIndexTryHdr(pWal, pChanged);
61266 if( badHdr ){
61267 /* If the wal-index header is still malformed even while holding
61268 ** a WRITE lock, it can only mean that the header is corrupted and
61269 ** needs to be reconstructed. So run recovery to do exactly that.
61270 */
61271 rc = walIndexRecover(pWal);
61272 *pChanged = 1;
61273 }
61274 }
61275 pWal->writeLock = 0;
61276 walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
 
 
 
 
 
61277 }
61278 }
61279
61280 /* If the header is read successfully, check the version number to make
61281 ** sure the wal-index was not constructed with some future format that
@@ -61663,11 +61933,12 @@
61663 && (mxReadMark<mxFrame || mxI==0)
61664 ){
61665 for(i=1; i<WAL_NREADER; i++){
61666 rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
61667 if( rc==SQLITE_OK ){
61668 mxReadMark = AtomicStore(pInfo->aReadMark+i,mxFrame);
 
61669 mxI = i;
61670 walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
61671 break;
61672 }else if( rc!=SQLITE_BUSY ){
61673 return rc;
@@ -61823,15 +62094,36 @@
61823 */
61824 SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
61825 int rc; /* Return code */
61826 int cnt = 0; /* Number of TryBeginRead attempts */
61827
 
 
61828 #ifdef SQLITE_ENABLE_SNAPSHOT
61829 int bChanged = 0;
61830 WalIndexHdr *pSnapshot = pWal->pSnapshot;
61831 if( pSnapshot && memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){
61832 bChanged = 1;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61833 }
61834 #endif
61835
61836 do{
61837 rc = walTryBeginRead(pWal, pChanged, 0, ++cnt);
@@ -61860,52 +62152,46 @@
61860 volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
61861
61862 assert( pWal->readLock>0 || pWal->hdr.mxFrame==0 );
61863 assert( pInfo->aReadMark[pWal->readLock]<=pSnapshot->mxFrame );
61864
61865 /* It is possible that there is a checkpointer thread running
61866 ** concurrent with this code. If this is the case, it may be that the
61867 ** checkpointer has already determined that it will checkpoint
61868 ** snapshot X, where X is later in the wal file than pSnapshot, but
61869 ** has not yet set the pInfo->nBackfillAttempted variable to indicate
61870 ** its intent. To avoid the race condition this leads to, ensure that
61871 ** there is no checkpointer process by taking a shared CKPT lock
61872 ** before checking pInfo->nBackfillAttempted.
61873 **
61874 ** TODO: Does the aReadMark[] lock prevent a checkpointer from doing
61875 ** this already?
61876 */
61877 rc = walLockShared(pWal, WAL_CKPT_LOCK);
61878
61879 if( rc==SQLITE_OK ){
61880 /* Check that the wal file has not been wrapped. Assuming that it has
61881 ** not, also check that no checkpointer has attempted to checkpoint any
61882 ** frames beyond pSnapshot->mxFrame. If either of these conditions are
61883 ** true, return SQLITE_ERROR_SNAPSHOT. Otherwise, overwrite pWal->hdr
61884 ** with *pSnapshot and set *pChanged as appropriate for opening the
61885 ** snapshot. */
61886 if( !memcmp(pSnapshot->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))
61887 && pSnapshot->mxFrame>=pInfo->nBackfillAttempted
61888 ){
61889 assert( pWal->readLock>0 );
61890 memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr));
61891 *pChanged = bChanged;
61892 }else{
61893 rc = SQLITE_ERROR_SNAPSHOT;
61894 }
61895
61896 /* Release the shared CKPT lock obtained above. */
61897 walUnlockShared(pWal, WAL_CKPT_LOCK);
61898 pWal->minFrame = 1;
61899 }
61900
61901
61902 if( rc!=SQLITE_OK ){
61903 sqlite3WalEndReadTransaction(pWal);
61904 }
61905 }
61906 }
 
 
 
 
 
 
 
61907 #endif
61908 return rc;
61909 }
61910
61911 /*
@@ -62071,10 +62357,20 @@
62071 **
62072 ** There can only be a single writer active at a time.
62073 */
62074 SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){
62075 int rc;
 
 
 
 
 
 
 
 
 
 
62076
62077 /* Cannot start a write transaction without first holding a read
62078 ** transaction. */
62079 assert( pWal->readLock>=0 );
62080 assert( pWal->writeLock==0 && pWal->iReCksum==0 );
@@ -62647,50 +62943,57 @@
62647 ** in the SQLITE_CHECKPOINT_PASSIVE mode. */
62648 assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 );
62649
62650 if( pWal->readOnly ) return SQLITE_READONLY;
62651 WALTRACE(("WAL%p: checkpoint begins\n", pWal));
 
 
 
 
 
62652
62653 /* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive
62654 ** "checkpoint" lock on the database file. */
 
 
 
 
 
 
62655 rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
62656 if( rc ){
62657 /* EVIDENCE-OF: R-10421-19736 If any other process is running a
62658 ** checkpoint operation at the same time, the lock cannot be obtained and
62659 ** SQLITE_BUSY is returned.
62660 ** EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured,
62661 ** it will not be invoked in this case.
62662 */
62663 testcase( rc==SQLITE_BUSY );
62664 testcase( xBusy!=0 );
62665 return rc;
62666 }
62667 pWal->ckptLock = 1;
62668
62669 /* IMPLEMENTATION-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART and
62670 ** TRUNCATE modes also obtain the exclusive "writer" lock on the database
62671 ** file.
62672 **
62673 ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained
62674 ** immediately, and a busy-handler is configured, it is invoked and the
62675 ** writer lock retried until either the busy-handler returns 0 or the
62676 ** lock is successfully obtained.
62677 */
62678 if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
62679 rc = walBusyLock(pWal, xBusy, pBusyArg, WAL_WRITE_LOCK, 1);
62680 if( rc==SQLITE_OK ){
62681 pWal->writeLock = 1;
62682 }else if( rc==SQLITE_BUSY ){
62683 eMode2 = SQLITE_CHECKPOINT_PASSIVE;
62684 xBusy2 = 0;
62685 rc = SQLITE_OK;
62686 }
62687 }
62688
62689 /* Read the wal-index header. */
62690 if( rc==SQLITE_OK ){
 
62691 rc = walIndexReadHdr(pWal, &isChanged);
 
62692 if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){
62693 sqlite3OsUnfetch(pWal->pDbFd, 0, 0);
62694 }
62695 }
62696
@@ -62717,16 +63020,24 @@
62717 ** next time the pager opens a snapshot on this database it knows that
62718 ** the cache needs to be reset.
62719 */
62720 memset(&pWal->hdr, 0, sizeof(WalIndexHdr));
62721 }
 
 
 
62722
62723 /* Release the locks. */
62724 sqlite3WalEndWriteTransaction(pWal);
62725 walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
62726 pWal->ckptLock = 0;
 
 
62727 WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok"));
 
 
 
62728 return (rc==SQLITE_OK && eMode!=eMode2 ? SQLITE_BUSY : rc);
62729 }
62730
62731 /* Return the value to pass to a sqlite3_wal_hook callback, the
62732 ** number of frames in the WAL at the point of the last commit since
@@ -62839,11 +63150,14 @@
62839 return rc;
62840 }
62841
62842 /* Try to open on pSnapshot when the next read-transaction starts
62843 */
62844 SQLITE_PRIVATE void sqlite3WalSnapshotOpen(Wal *pWal, sqlite3_snapshot *pSnapshot){
 
 
 
62845 pWal->pSnapshot = (WalIndexHdr*)pSnapshot;
62846 }
62847
62848 /*
62849 ** Return a +ve value if snapshot p1 is newer than p2. A -ve value if
@@ -63358,10 +63672,11 @@
63358 u8 incrVacuum; /* True if incr-vacuum is enabled */
63359 u8 bDoTruncate; /* True to truncate db on commit */
63360 #endif
63361 u8 inTransaction; /* Transaction state */
63362 u8 max1bytePayload; /* Maximum first byte of cell for a 1-byte payload */
 
63363 u16 btsFlags; /* Boolean parameters. See BTS_* macros below */
63364 u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */
63365 u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */
63366 u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */
63367 u16 minLeaf; /* Minimum local payload in a LEAFDATA table */
@@ -66251,12 +66566,11 @@
66251 */
66252 static int btreeInvokeBusyHandler(void *pArg){
66253 BtShared *pBt = (BtShared*)pArg;
66254 assert( pBt->db );
66255 assert( sqlite3_mutex_held(pBt->db->mutex) );
66256 return sqlite3InvokeBusyHandler(&pBt->db->busyHandler,
66257 sqlite3PagerFile(pBt->pPager));
66258 }
66259
66260 /*
66261 ** Open a database file.
66262 **
@@ -66803,20 +67117,21 @@
66803 ** If the iFix!=0 then the BTS_PAGESIZE_FIXED flag is set so that the page size
66804 ** and autovacuum mode can no longer be changed.
66805 */
66806 SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){
66807 int rc = SQLITE_OK;
 
66808 BtShared *pBt = p->pBt;
66809 assert( nReserve>=-1 && nReserve<=255 );
66810 sqlite3BtreeEnter(p);
 
 
 
66811 if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){
66812 sqlite3BtreeLeave(p);
66813 return SQLITE_READONLY;
66814 }
66815 if( nReserve<0 ){
66816 nReserve = pBt->pageSize - pBt->usableSize;
66817 }
66818 assert( nReserve>=0 && nReserve<=255 );
66819 if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE &&
66820 ((pageSize-1)&pageSize)==0 ){
66821 assert( (pageSize & 7)==0 );
66822 assert( !pBt->pCursor );
@@ -66858,20 +67173,21 @@
66858 /*
66859 ** Return the number of bytes of space at the end of every page that
66860 ** are intentually left unused. This is the "reserved" space that is
66861 ** sometimes used by extensions.
66862 **
66863 ** If SQLITE_HAS_MUTEX is defined then the number returned is the
66864 ** greater of the current reserved space and the maximum requested
66865 ** reserve space.
66866 */
66867 SQLITE_PRIVATE int sqlite3BtreeGetOptimalReserve(Btree *p){
66868 int n;
66869 sqlite3BtreeEnter(p);
66870 n = sqlite3BtreeGetReserveNoMutex(p);
 
66871 sqlite3BtreeLeave(p);
66872 return n;
66873 }
66874
66875
66876 /*
66877 ** Set the maximum page count for a database if mxPage is positive.
@@ -67317,10 +67633,11 @@
67317 ** when A already has a read lock, we encourage A to give up and let B
67318 ** proceed.
67319 */
67320 SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){
67321 BtShared *pBt = p->pBt;
 
67322 int rc = SQLITE_OK;
67323
67324 sqlite3BtreeEnter(p);
67325 btreeIntegrity(p);
67326
@@ -67332,11 +67649,11 @@
67332 goto trans_begun;
67333 }
67334 assert( pBt->inTransaction==TRANS_WRITE || IfNotOmitAV(pBt->bDoTruncate)==0 );
67335
67336 if( (p->db->flags & SQLITE_ResetDatabase)
67337 && sqlite3PagerIsreadonly(pBt->pPager)==0
67338 ){
67339 pBt->btsFlags &= ~BTS_READ_ONLY;
67340 }
67341
67342 /* Write transactions are not possible on a read-only database */
@@ -67380,10 +67697,22 @@
67380 if( SQLITE_OK!=rc ) goto trans_begun;
67381
67382 pBt->btsFlags &= ~BTS_INITIALLY_EMPTY;
67383 if( pBt->nPage==0 ) pBt->btsFlags |= BTS_INITIALLY_EMPTY;
67384 do {
 
 
 
 
 
 
 
 
 
 
 
 
67385 /* Call lockBtree() until either pBt->pPage1 is populated or
67386 ** lockBtree() returns something other than SQLITE_OK. lockBtree()
67387 ** may return SQLITE_OK but leave pBt->pPage1 set to 0 if after
67388 ** reading page 1 it discovers that the page-size of the database
67389 ** file is not pBt->pageSize. In this case lockBtree() will update
@@ -67393,11 +67722,11 @@
67393
67394 if( rc==SQLITE_OK && wrflag ){
67395 if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){
67396 rc = SQLITE_READONLY;
67397 }else{
67398 rc = sqlite3PagerBegin(pBt->pPager,wrflag>1,sqlite3TempInMemory(p->db));
67399 if( rc==SQLITE_OK ){
67400 rc = newDatabase(pBt);
67401 }else if( rc==SQLITE_BUSY_SNAPSHOT && pBt->inTransaction==TRANS_NONE ){
67402 /* if there was no transaction opened when this function was
67403 ** called and SQLITE_BUSY_SNAPSHOT is returned, change the error
@@ -67406,15 +67735,19 @@
67406 }
67407 }
67408 }
67409
67410 if( rc!=SQLITE_OK ){
 
67411 unlockBtreeIfUnused(pBt);
67412 }
67413 }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&
67414 btreeInvokeBusyHandler(pBt) );
67415 sqlite3PagerResetLockTimeout(pBt->pPager);
 
 
 
67416
67417 if( rc==SQLITE_OK ){
67418 if( p->inTrans==TRANS_NONE ){
67419 pBt->nTransaction++;
67420 #ifndef SQLITE_OMIT_SHARED_CACHE
@@ -67462,11 +67795,11 @@
67462 if( wrflag ){
67463 /* This call makes sure that the pager has the correct number of
67464 ** open savepoints. If the second parameter is greater than 0 and
67465 ** the sub-journal is not already open, then it will be opened here.
67466 */
67467 rc = sqlite3PagerOpenSavepoint(pBt->pPager, p->db->nSavepoint);
67468 }
67469 }
67470
67471 btreeIntegrity(p);
67472 sqlite3BtreeLeave(p);
@@ -71098,11 +71431,11 @@
71098
71099 /* Remove cells from the start and end of the page */
71100 assert( nCell>=0 );
71101 if( iOld<iNew ){
71102 int nShift = pageFreeArray(pPg, iOld, iNew-iOld, pCArray);
71103 if( nShift>nCell ) return SQLITE_CORRUPT_BKPT;
71104 memmove(pPg->aCellIdx, &pPg->aCellIdx[nShift*2], nCell*2);
71105 nCell -= nShift;
71106 }
71107 if( iNewEnd < iOldEnd ){
71108 int nTail = pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray);
@@ -73455,11 +73788,10 @@
73455 }
73456 sqlite3BtreeLeave(p);
73457 return rc;
73458 }
73459
73460 #ifndef SQLITE_OMIT_BTREECOUNT
73461 /*
73462 ** The first argument, pCur, is a cursor opened on some b-tree. Count the
73463 ** number of entries in the b-tree and write the result to *pnEntry.
73464 **
73465 ** SQLITE_OK is returned if the operation is successfully executed.
@@ -73477,11 +73809,11 @@
73477 }
73478
73479 /* Unless an error occurs, the following loop runs one iteration for each
73480 ** page in the B-Tree structure (not including overflow pages).
73481 */
73482 while( rc==SQLITE_OK && !db->u1.isInterrupted ){
73483 int iIdx; /* Index of child node in parent */
73484 MemPage *pPage; /* Current page of the b-tree */
73485
73486 /* If this is a leaf page or the tree is not an int-key tree, then
73487 ** this page contains countable entries. Increment the entry counter
@@ -73528,11 +73860,10 @@
73528 }
73529
73530 /* An error has occurred. Return an error code. */
73531 return rc;
73532 }
73533 #endif
73534
73535 /*
73536 ** Return the pager associated with a BTree. This routine is used for
73537 ** testing and debugging only.
73538 */
@@ -73603,11 +73934,11 @@
73603 }
73604 if( getPageReferenced(pCheck, iPage) ){
73605 checkAppendMsg(pCheck, "2nd reference to page %d", iPage);
73606 return 1;
73607 }
73608 if( pCheck->db->u1.isInterrupted ) return 1;
73609 setPageReferenced(pCheck, iPage);
73610 return 0;
73611 }
73612
73613 #ifndef SQLITE_OMIT_AUTOVACUUM
@@ -74579,11 +74910,11 @@
74579 ** Attempt to set the page size of the destination to match the page size
74580 ** of the source.
74581 */
74582 static int setDestPgsz(sqlite3_backup *p){
74583 int rc;
74584 rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),-1,0);
74585 return rc;
74586 }
74587
74588 /*
74589 ** Check that there is no open read-transaction on the b-tree passed as the
@@ -78604,24 +78935,23 @@
78604 ** "PX" -> "r[X]"
78605 ** "PX@PY" -> "r[X..X+Y-1]" or "r[x]" if y is 0 or 1
78606 ** "PX@PY+1" -> "r[X..X+Y]" or "r[x]" if y is 0
78607 ** "PY..PY" -> "r[X..Y]" or "r[x]" if y<=x
78608 */
78609 static int displayComment(
 
78610 const Op *pOp, /* The opcode to be commented */
78611 const char *zP4, /* Previously obtained value for P4 */
78612 char *zTemp, /* Write result here */
78613 int nTemp /* Space available in zTemp[] */
78614 ){
78615 const char *zOpName;
78616 const char *zSynopsis;
78617 int nOpName;
78618 int ii;
78619 char zAlt[50];
78620 StrAccum x;
78621 sqlite3StrAccumInit(&x, 0, zTemp, nTemp, 0);
78622
 
78623 zOpName = sqlite3OpcodeName(pOp->opcode);
78624 nOpName = sqlite3Strlen30(zOpName);
78625 if( zOpName[nOpName+1] ){
78626 int seenCom = 0;
78627 char c;
@@ -78684,14 +79014,16 @@
78684 sqlite3_str_appendf(&x, "; %s", pOp->zComment);
78685 }
78686 }else if( pOp->zComment ){
78687 sqlite3_str_appendall(&x, pOp->zComment);
78688 }
78689 sqlite3StrAccumFinish(&x);
78690 return x.nChar;
 
 
78691 }
78692 #endif /* SQLITE_DEBUG */
78693
78694 #if VDBE_DISPLAY_P4 && defined(SQLITE_ENABLE_CURSOR_HINTS)
78695 /*
78696 ** Translate the P4.pExpr value for an OP_CursorHint opcode into text
78697 ** that can be displayed in the P4 column of EXPLAIN output.
@@ -78768,15 +79100,15 @@
78768 #if VDBE_DISPLAY_P4
78769 /*
78770 ** Compute a string that describes the P4 parameter for an opcode.
78771 ** Use zTemp for any required temporary buffer space.
78772 */
78773 static char *displayP4(Op *pOp, char *zTemp, int nTemp){
78774 char *zP4 = zTemp;
78775 StrAccum x;
78776 assert( nTemp>=20 );
78777 sqlite3StrAccumInit(&x, 0, zTemp, nTemp, 0);
78778 switch( pOp->p4type ){
78779 case P4_KEYINFO: {
78780 int j;
78781 KeyInfo *pKeyInfo = pOp->p4.pKeyInfo;
78782 assert( pKeyInfo->aSortFlags!=0 );
@@ -78856,40 +79188,36 @@
78856 int i;
78857 int *ai = pOp->p4.ai;
78858 int n = ai[0]; /* The first element of an INTARRAY is always the
78859 ** count of the number of elements to follow */
78860 for(i=1; i<=n; i++){
78861 sqlite3_str_appendf(&x, ",%d", ai[i]);
78862 }
78863 zTemp[0] = '[';
78864 sqlite3_str_append(&x, "]", 1);
78865 break;
78866 }
78867 case P4_SUBPROGRAM: {
78868 sqlite3_str_appendf(&x, "program");
78869 break;
78870 }
78871 case P4_DYNBLOB:
78872 case P4_ADVANCE: {
78873 zTemp[0] = 0;
78874 break;
78875 }
78876 case P4_TABLE: {
78877 sqlite3_str_appendf(&x, "%s", pOp->p4.pTab->zName);
78878 break;
78879 }
78880 default: {
78881 zP4 = pOp->p4.z;
78882 if( zP4==0 ){
78883 zP4 = zTemp;
78884 zTemp[0] = 0;
78885 }
78886 }
78887 }
78888 sqlite3StrAccumFinish(&x);
78889 assert( zP4!=0 );
78890 return zP4;
 
 
78891 }
78892 #endif /* VDBE_DISPLAY_P4 */
78893
78894 /*
78895 ** Declare to the Vdbe that the BTree object at db->aDb[i] is used.
@@ -78975,28 +79303,32 @@
78975 /*
78976 ** Print a single opcode. This routine is used for debugging only.
78977 */
78978 SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE *pOut, int pc, VdbeOp *pOp){
78979 char *zP4;
78980 char zPtr[50];
78981 char zCom[100];
78982 static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-13s %.2X %s\n";
78983 if( pOut==0 ) pOut = stdout;
78984 zP4 = displayP4(pOp, zPtr, sizeof(zPtr));
 
78985 #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
78986 displayComment(pOp, zP4, zCom, sizeof(zCom));
78987 #else
78988 zCom[0] = 0;
78989 #endif
78990 /* NB: The sqlite3OpcodeName() function is implemented by code created
78991 ** by the mkopcodeh.awk and mkopcodec.awk scripts which extract the
78992 ** information from the vdbe.c source text */
78993 fprintf(pOut, zFormat1, pc,
78994 sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3, zP4, pOp->p5,
78995 zCom
 
78996 );
78997 fflush(pOut);
 
 
78998 }
78999 #endif
79000
79001 /*
79002 ** Initialize an array of N Mem element.
@@ -79083,10 +79415,125 @@
79083 assert( sqlite3VdbeFrameIsValid(pFrame) );
79084 pFrame->pParent = pFrame->v->pDelFrame;
79085 pFrame->v->pDelFrame = pFrame;
79086 }
79087
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79088
79089 /*
79090 ** Delete a VdbeFrame object and its contents. VdbeFrame objects are
79091 ** allocated by the OP_Program opcode in sqlite3VdbeExec().
79092 */
@@ -79123,20 +79570,18 @@
79123 ** the trigger subprograms are listed one by one.
79124 */
79125 SQLITE_PRIVATE int sqlite3VdbeList(
79126 Vdbe *p /* The VDBE */
79127 ){
79128 int nRow; /* Stop when row count reaches this */
79129 int nSub = 0; /* Number of sub-vdbes seen so far */
79130 SubProgram **apSub = 0; /* Array of sub-vdbes */
79131 Mem *pSub = 0; /* Memory cell hold array of subprogs */
79132 sqlite3 *db = p->db; /* The database connection */
79133 int i; /* Loop counter */
79134 int rc = SQLITE_OK; /* Return code */
79135 Mem *pMem = &p->aMem[1]; /* First Mem of result set */
79136 int bListSubprogs = (p->explain==1 || (db->flags & SQLITE_TriggerEQP)!=0);
79137 Op *pOp = 0;
 
79138
79139 assert( p->explain );
79140 assert( p->magic==VDBE_MAGIC_RUN );
79141 assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || p->rc==SQLITE_NOMEM );
79142
@@ -79152,166 +79597,66 @@
79152 ** sqlite3_column_text16() failed. */
79153 sqlite3OomFault(db);
79154 return SQLITE_ERROR;
79155 }
79156
79157 /* When the number of output rows reaches nRow, that means the
79158 ** listing has finished and sqlite3_step() should return SQLITE_DONE.
79159 ** nRow is the sum of the number of rows in the main program, plus
79160 ** the sum of the number of rows in all trigger subprograms encountered
79161 ** so far. The nRow value will increase as new trigger subprograms are
79162 ** encountered, but p->pc will eventually catch up to nRow.
79163 */
79164 nRow = p->nOp;
79165 if( bListSubprogs ){
79166 /* The first 8 memory cells are used for the result set. So we will
79167 ** commandeer the 9th cell to use as storage for an array of pointers
79168 ** to trigger subprograms. The VDBE is guaranteed to have at least 9
79169 ** cells. */
79170 assert( p->nMem>9 );
79171 pSub = &p->aMem[9];
79172 if( pSub->flags&MEM_Blob ){
79173 /* On the first call to sqlite3_step(), pSub will hold a NULL. It is
79174 ** initialized to a BLOB by the P4_SUBPROGRAM processing logic below */
79175 nSub = pSub->n/sizeof(Vdbe*);
79176 apSub = (SubProgram **)pSub->z;
79177 }
79178 for(i=0; i<nSub; i++){
79179 nRow += apSub[i]->nOp;
79180 }
79181 }
79182
79183 while(1){ /* Loop exits via break */
79184 i = p->pc++;
79185 if( i>=nRow ){
79186 p->rc = SQLITE_OK;
79187 rc = SQLITE_DONE;
79188 break;
79189 }
79190 if( i<p->nOp ){
79191 /* The output line number is small enough that we are still in the
79192 ** main program. */
79193 pOp = &p->aOp[i];
79194 }else{
79195 /* We are currently listing subprograms. Figure out which one and
79196 ** pick up the appropriate opcode. */
79197 int j;
79198 i -= p->nOp;
79199 assert( apSub!=0 );
79200 assert( nSub>0 );
79201 for(j=0; i>=apSub[j]->nOp; j++){
79202 i -= apSub[j]->nOp;
79203 assert( i<apSub[j]->nOp || j+1<nSub );
79204 }
79205 pOp = &apSub[j]->aOp[i];
79206 }
79207
79208 /* When an OP_Program opcode is encounter (the only opcode that has
79209 ** a P4_SUBPROGRAM argument), expand the size of the array of subprograms
79210 ** kept in p->aMem[9].z to hold the new program - assuming this subprogram
79211 ** has not already been seen.
79212 */
79213 if( bListSubprogs && pOp->p4type==P4_SUBPROGRAM ){
79214 int nByte = (nSub+1)*sizeof(SubProgram*);
79215 int j;
79216 for(j=0; j<nSub; j++){
79217 if( apSub[j]==pOp->p4.pProgram ) break;
79218 }
79219 if( j==nSub ){
79220 p->rc = sqlite3VdbeMemGrow(pSub, nByte, nSub!=0);
79221 if( p->rc!=SQLITE_OK ){
79222 rc = SQLITE_ERROR;
79223 break;
79224 }
79225 apSub = (SubProgram **)pSub->z;
79226 apSub[nSub++] = pOp->p4.pProgram;
79227 pSub->flags |= MEM_Blob;
79228 pSub->n = nSub*sizeof(SubProgram*);
79229 nRow += pOp->p4.pProgram->nOp;
79230 }
79231 }
79232 if( p->explain<2 ) break;
79233 if( pOp->opcode==OP_Explain ) break;
79234 if( pOp->opcode==OP_Init && p->pc>1 ) break;
79235 }
79236
79237 if( rc==SQLITE_OK ){
79238 if( db->u1.isInterrupted ){
79239 p->rc = SQLITE_INTERRUPT;
79240 rc = SQLITE_ERROR;
79241 sqlite3VdbeError(p, sqlite3ErrStr(p->rc));
79242 }else{
79243 char *zP4;
79244 if( p->explain==1 ){
79245 pMem->flags = MEM_Int;
79246 pMem->u.i = i; /* Program counter */
79247 pMem++;
79248
79249 pMem->flags = MEM_Static|MEM_Str|MEM_Term;
79250 pMem->z = (char*)sqlite3OpcodeName(pOp->opcode); /* Opcode */
79251 assert( pMem->z!=0 );
79252 pMem->n = sqlite3Strlen30(pMem->z);
79253 pMem->enc = SQLITE_UTF8;
79254 pMem++;
79255 }
79256
79257 pMem->flags = MEM_Int;
79258 pMem->u.i = pOp->p1; /* P1 */
79259 pMem++;
79260
79261 pMem->flags = MEM_Int;
79262 pMem->u.i = pOp->p2; /* P2 */
79263 pMem++;
79264
79265 pMem->flags = MEM_Int;
79266 pMem->u.i = pOp->p3; /* P3 */
79267 pMem++;
79268
79269 if( sqlite3VdbeMemClearAndResize(pMem, 100) ){ /* P4 */
79270 assert( p->db->mallocFailed );
79271 return SQLITE_ERROR;
79272 }
79273 pMem->flags = MEM_Str|MEM_Term;
79274 zP4 = displayP4(pOp, pMem->z, pMem->szMalloc);
79275 if( zP4!=pMem->z ){
79276 pMem->n = 0;
79277 sqlite3VdbeMemSetStr(pMem, zP4, -1, SQLITE_UTF8, 0);
79278 }else{
79279 assert( pMem->z!=0 );
79280 pMem->n = sqlite3Strlen30(pMem->z);
79281 pMem->enc = SQLITE_UTF8;
79282 }
79283 pMem++;
79284
79285 if( p->explain==1 ){
79286 if( sqlite3VdbeMemClearAndResize(pMem, 4) ){
79287 assert( p->db->mallocFailed );
79288 return SQLITE_ERROR;
79289 }
79290 pMem->flags = MEM_Str|MEM_Term;
79291 pMem->n = 2;
79292 sqlite3_snprintf(3, pMem->z, "%.2x", pOp->p5); /* P5 */
79293 pMem->enc = SQLITE_UTF8;
79294 pMem++;
79295
79296 #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
79297 if( sqlite3VdbeMemClearAndResize(pMem, 500) ){
79298 assert( p->db->mallocFailed );
79299 return SQLITE_ERROR;
79300 }
79301 pMem->flags = MEM_Str|MEM_Term;
79302 pMem->n = displayComment(pOp, zP4, pMem->z, 500);
79303 pMem->enc = SQLITE_UTF8;
79304 #else
79305 pMem->flags = MEM_Null; /* Comment */
79306 #endif
79307 }
79308
79309 p->nResColumn = 8 - 4*(p->explain-1);
79310 p->pResultSet = &p->aMem[1];
79311 p->rc = SQLITE_OK;
79312 rc = SQLITE_ROW;
 
 
79313 }
79314 }
79315 return rc;
79316 }
79317 #endif /* SQLITE_OMIT_EXPLAIN */
@@ -79877,12 +80222,13 @@
79877 int retryCount = 0;
79878 int nMainFile;
79879
79880 /* Select a master journal file name */
79881 nMainFile = sqlite3Strlen30(zMainFile);
79882 zMaster = sqlite3MPrintf(db, "%s-mjXXXXXX9XXz%c%c", zMainFile, 0, 0);
79883 if( zMaster==0 ) return SQLITE_NOMEM_BKPT;
 
79884 do {
79885 u32 iRandom;
79886 if( retryCount ){
79887 if( retryCount>100 ){
79888 sqlite3_log(SQLITE_FULL, "MJ delete: %s", zMaster);
@@ -79908,11 +80254,11 @@
79908 SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|
79909 SQLITE_OPEN_EXCLUSIVE|SQLITE_OPEN_MASTER_JOURNAL, 0
79910 );
79911 }
79912 if( rc!=SQLITE_OK ){
79913 sqlite3DbFree(db, zMaster);
79914 return rc;
79915 }
79916
79917 /* Write the name of each database file in the transaction into the new
79918 ** master journal file. If an error occurs at this point close
@@ -79931,11 +80277,11 @@
79931 rc = sqlite3OsWrite(pMaster, zFile, sqlite3Strlen30(zFile)+1, offset);
79932 offset += sqlite3Strlen30(zFile)+1;
79933 if( rc!=SQLITE_OK ){
79934 sqlite3OsCloseFree(pMaster);
79935 sqlite3OsDelete(pVfs, zMaster, 0);
79936 sqlite3DbFree(db, zMaster);
79937 return rc;
79938 }
79939 }
79940 }
79941
@@ -79945,11 +80291,11 @@
79945 if( 0==(sqlite3OsDeviceCharacteristics(pMaster)&SQLITE_IOCAP_SEQUENTIAL)
79946 && SQLITE_OK!=(rc = sqlite3OsSync(pMaster, SQLITE_SYNC_NORMAL))
79947 ){
79948 sqlite3OsCloseFree(pMaster);
79949 sqlite3OsDelete(pVfs, zMaster, 0);
79950 sqlite3DbFree(db, zMaster);
79951 return rc;
79952 }
79953
79954 /* Sync all the db files involved in the transaction. The same call
79955 ** sets the master journal pointer in each individual journal. If
@@ -79968,20 +80314,20 @@
79968 }
79969 }
79970 sqlite3OsCloseFree(pMaster);
79971 assert( rc!=SQLITE_BUSY );
79972 if( rc!=SQLITE_OK ){
79973 sqlite3DbFree(db, zMaster);
79974 return rc;
79975 }
79976
79977 /* Delete the master journal file. This commits the transaction. After
79978 ** doing this the directory is synced again before any individual
79979 ** transaction files are deleted.
79980 */
79981 rc = sqlite3OsDelete(pVfs, zMaster, 1);
79982 sqlite3DbFree(db, zMaster);
79983 zMaster = 0;
79984 if( rc ){
79985 return rc;
79986 }
79987
@@ -83028,11 +83374,11 @@
83028 /* If there are no other statements currently running, then
83029 ** reset the interrupt flag. This prevents a call to sqlite3_interrupt
83030 ** from interrupting a statement that has not yet started.
83031 */
83032 if( db->nVdbeActive==0 ){
83033 db->u1.isInterrupted = 0;
83034 }
83035
83036 assert( db->nVdbeWrite>0 || db->autoCommit==0
83037 || (db->nDeferredCons==0 && db->nDeferredImmCons==0)
83038 );
@@ -85413,11 +85759,11 @@
85413 assert( p->bIsReader || p->readOnly!=0 );
85414 p->iCurrentTime = 0;
85415 assert( p->explain==0 );
85416 p->pResultSet = 0;
85417 db->busyHandler.nBusy = 0;
85418 if( db->u1.isInterrupted ) goto abort_due_to_interrupt;
85419 sqlite3VdbeIOTraceSql(p);
85420 #ifdef SQLITE_DEBUG
85421 sqlite3BeginBenignMalloc();
85422 if( p->pc==0
85423 && (p->db->flags & (SQLITE_VdbeListing|SQLITE_VdbeEQP|SQLITE_VdbeTrace))!=0
@@ -85597,11 +85943,11 @@
85597 ** But that is not due to sloppy coding habits. The code is written this
85598 ** way for performance, to avoid having to run the interrupt and progress
85599 ** checks on every opcode. This helps sqlite3_step() to run about 1.5%
85600 ** faster according to "valgrind --tool=cachegrind" */
85601 check_for_interrupt:
85602 if( db->u1.isInterrupted ) goto abort_due_to_interrupt;
85603 #ifndef SQLITE_OMIT_PROGRESS_CALLBACK
85604 /* Call the progress callback if it is configured and the required number
85605 ** of VDBE ops have been executed (either since this invocation of
85606 ** sqlite3VdbeExec() or since last time the progress callback was called).
85607 ** If the progress callback returns non-zero, exit the virtual machine with
@@ -87893,32 +88239,38 @@
87893 assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
87894 REGISTER_TRACE(pOp->p3, pOut);
87895 break;
87896 }
87897
87898 /* Opcode: Count P1 P2 * * *
87899 ** Synopsis: r[P2]=count()
87900 **
87901 ** Store the number of entries (an integer value) in the table or index
87902 ** opened by cursor P1 in register P2
 
 
 
 
87903 */
87904 #ifndef SQLITE_OMIT_BTREECOUNT
87905 case OP_Count: { /* out2 */
87906 i64 nEntry;
87907 BtCursor *pCrsr;
87908
87909 assert( p->apCsr[pOp->p1]->eCurType==CURTYPE_BTREE );
87910 pCrsr = p->apCsr[pOp->p1]->uc.pCursor;
87911 assert( pCrsr );
87912 nEntry = 0; /* Not needed. Only used to silence a warning. */
87913 rc = sqlite3BtreeCount(db, pCrsr, &nEntry);
87914 if( rc ) goto abort_due_to_error;
 
 
 
 
87915 pOut = out2Prerelease(p, pOp);
87916 pOut->u.i = nEntry;
87917 goto check_for_interrupt;
87918 }
87919 #endif
87920
87921 /* Opcode: Savepoint P1 * * P4 *
87922 **
87923 ** Open, release or rollback the savepoint named by parameter P4, depending
87924 ** on the value of P1. To open a new savepoint set P1==0 (SAVEPOINT_BEGIN).
@@ -90352,16 +90704,23 @@
90352 rc = sqlite3VdbeSorterWrite(pC, pIn2);
90353 if( rc) goto abort_due_to_error;
90354 break;
90355 }
90356
90357 /* Opcode: IdxDelete P1 P2 P3 * *
90358 ** Synopsis: key=r[P2@P3]
90359 **
90360 ** The content of P3 registers starting at register P2 form
90361 ** an unpacked index key. This opcode removes that entry from the
90362 ** index opened by cursor P1.
 
 
 
 
 
 
 
90363 */
90364 case OP_IdxDelete: {
90365 VdbeCursor *pC;
90366 BtCursor *pCrsr;
90367 int res;
@@ -90374,20 +90733,22 @@
90374 assert( pC!=0 );
90375 assert( pC->eCurType==CURTYPE_BTREE );
90376 sqlite3VdbeIncrWriteCounter(p, pC);
90377 pCrsr = pC->uc.pCursor;
90378 assert( pCrsr!=0 );
90379 assert( pOp->p5==0 );
90380 r.pKeyInfo = pC->pKeyInfo;
90381 r.nField = (u16)pOp->p3;
90382 r.default_rc = 0;
90383 r.aMem = &aMem[pOp->p2];
90384 rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res);
90385 if( rc ) goto abort_due_to_error;
90386 if( res==0 ){
90387 rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE);
90388 if( rc ) goto abort_due_to_error;
 
 
 
90389 }
90390 assert( pC->deferredMoveto==0 );
90391 pC->cacheStatus = CACHE_STALE;
90392 pC->seekResult = 0;
90393 break;
@@ -92705,11 +93066,11 @@
92705
92706 /* Jump to here if the sqlite3_interrupt() API sets the interrupt
92707 ** flag.
92708 */
92709 abort_due_to_interrupt:
92710 assert( db->u1.isInterrupted );
92711 rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT;
92712 p->rc = rc;
92713 sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc));
92714 goto abort_due_to_error;
92715 }
@@ -95988,10 +96349,435 @@
95988 *pRes = sqlite3VdbeRecordCompare(pVal->n, pVal->z, r2);
95989 return SQLITE_OK;
95990 }
95991
95992 /************** End of vdbesort.c ********************************************/
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95993 /************** Begin file memjournal.c **************************************/
95994 /*
95995 ** 2008 October 7
95996 **
95997 ** The author disclaims copyright to this source code. In place of
@@ -96775,11 +97561,11 @@
96775 const char *zTab,
96776 const char *zDb
96777 ){
96778 int n;
96779 const char *zSpan;
96780 if( NEVER(pItem->eEName!=ENAME_TAB) ) return 0;
96781 zSpan = pItem->zEName;
96782 for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){}
96783 if( zDb && (sqlite3StrNICmp(zSpan, zDb, n)!=0 || zDb[n]!=0) ){
96784 return 0;
96785 }
@@ -96929,10 +97715,11 @@
96929 ExprList *pEList;
96930 SrcList *pSrcList = pNC->pSrcList;
96931
96932 if( pSrcList ){
96933 for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){
 
96934 pTab = pItem->pTab;
96935 assert( pTab!=0 && pTab->zName!=0 );
96936 assert( pTab->nCol>0 );
96937 if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){
96938 int hit = 0;
@@ -96962,12 +97749,13 @@
96962 }
96963 }
96964 if( 0==(cntTab++) ){
96965 pMatch = pItem;
96966 }
 
96967 for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){
96968 if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
96969 /* If there has been exactly one prior match and this match
96970 ** is for the right-hand table of a NATURAL JOIN or is in a
96971 ** USING clause, then skip this match.
96972 */
96973 if( cnt==1 ){
@@ -97024,14 +97812,15 @@
97024 }
97025 #endif /* SQLITE_OMIT_UPSERT */
97026
97027 if( pTab ){
97028 int iCol;
 
97029 pSchema = pTab->pSchema;
97030 cntTab++;
97031 for(iCol=0, pCol=pTab->aCol; iCol<pTab->nCol; iCol++, pCol++){
97032 if( sqlite3StrICmp(pCol->zName, zCol)==0 ){
97033 if( iCol==pTab->iPKey ){
97034 iCol = -1;
97035 }
97036 break;
97037 }
@@ -97828,11 +98617,11 @@
97828 nc.uNC.pEList = pEList;
97829 nc.ncFlags = NC_AllowAgg|NC_UEList;
97830 nc.nErr = 0;
97831 db = pParse->db;
97832 savedSuppErr = db->suppressErr;
97833 db->suppressErr = 1;
97834 rc = sqlite3ResolveExprNames(&nc, pE);
97835 db->suppressErr = savedSuppErr;
97836 if( rc ) return 0;
97837
97838 /* Try to match the ORDER BY expression against an expression
@@ -98463,15 +99252,45 @@
98463 SQLITE_PRIVATE int sqlite3ResolveExprListNames(
98464 NameContext *pNC, /* Namespace to resolve expressions in. */
98465 ExprList *pList /* The expression list to be analyzed. */
98466 ){
98467 int i;
98468 if( pList ){
98469 for(i=0; i<pList->nExpr; i++){
98470 if( sqlite3ResolveExprNames(pNC, pList->a[i].pExpr) ) return WRC_Abort;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98471 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98472 }
 
98473 return WRC_Continue;
98474 }
98475
98476 /*
98477 ** Resolve all names in all expressions of a SELECT and in all
@@ -98601,11 +99420,11 @@
98601 ** SELECT * FROM t1 WHERE (select a from t1);
98602 */
98603 SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr){
98604 int op;
98605 while( ExprHasProperty(pExpr, EP_Skip) ){
98606 assert( pExpr->op==TK_COLLATE );
98607 pExpr = pExpr->pLeft;
98608 assert( pExpr!=0 );
98609 }
98610 op = pExpr->op;
98611 if( op==TK_SELECT ){
@@ -98668,11 +99487,11 @@
98668 /*
98669 ** Skip over any TK_COLLATE operators.
98670 */
98671 SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr *pExpr){
98672 while( pExpr && ExprHasProperty(pExpr, EP_Skip) ){
98673 assert( pExpr->op==TK_COLLATE );
98674 pExpr = pExpr->pLeft;
98675 }
98676 return pExpr;
98677 }
98678
@@ -98687,11 +99506,11 @@
98687 assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
98688 assert( pExpr->x.pList->nExpr>0 );
98689 assert( pExpr->op==TK_FUNCTION );
98690 pExpr = pExpr->x.pList->a[0].pExpr;
98691 }else{
98692 assert( pExpr->op==TK_COLLATE );
98693 pExpr = pExpr->pLeft;
98694 }
98695 }
98696 return pExpr;
98697 }
@@ -100343,20 +101162,26 @@
100343 ExprList *pList, /* List to which to add the span. */
100344 Token *pName, /* Name to be added */
100345 int dequote /* True to cause the name to be dequoted */
100346 ){
100347 assert( pList!=0 || pParse->db->mallocFailed!=0 );
 
100348 if( pList ){
100349 struct ExprList_item *pItem;
100350 assert( pList->nExpr>0 );
100351 pItem = &pList->a[pList->nExpr-1];
100352 assert( pItem->zEName==0 );
100353 assert( pItem->eEName==ENAME_NAME );
100354 pItem->zEName = sqlite3DbStrNDup(pParse->db, pName->z, pName->n);
100355 if( dequote ) sqlite3Dequote(pItem->zEName);
100356 if( IN_RENAME_OBJECT ){
100357 sqlite3RenameTokenMap(pParse, (void*)pItem->zEName, pName);
 
 
 
 
 
100358 }
100359 }
100360 }
100361
100362 /*
@@ -101497,10 +102322,12 @@
101497 struct ExprList_item *pItem;
101498 int r1, r2;
101499 affinity = sqlite3ExprAffinity(pLeft);
101500 if( affinity<=SQLITE_AFF_NONE ){
101501 affinity = SQLITE_AFF_BLOB;
 
 
101502 }
101503 if( pKeyInfo ){
101504 assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
101505 pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
101506 }
@@ -101735,10 +102562,11 @@
101735 int destStep6 = 0; /* Start of code for Step 6 */
101736 int addrTruthOp; /* Address of opcode that determines the IN is true */
101737 int destNotNull; /* Jump here if a comparison is not true in step 6 */
101738 int addrTop; /* Top of the step-6 loop */
101739 int iTab = 0; /* Index to use */
 
101740
101741 assert( !ExprHasVVAProperty(pExpr,EP_Immutable) );
101742 pLeft = pExpr->pLeft;
101743 if( sqlite3ExprCheckIN(pParse, pExpr) ) return;
101744 zAff = exprINAffinity(pParse, pExpr);
@@ -101779,12 +102607,18 @@
101779 **
101780 ** sqlite3FindInIndex() might have reordered the fields of the LHS vector
101781 ** so that the fields are in the same order as an existing index. The
101782 ** aiMap[] array contains a mapping from the original LHS field order to
101783 ** the field order that matches the RHS index.
101784 */
 
 
 
 
 
101785 rLhsOrig = exprCodeVector(pParse, pLeft, &iDummy);
 
101786 for(i=0; i<nVector && aiMap[i]==i; i++){} /* Are LHS fields reordered? */
101787 if( i==nVector ){
101788 /* LHS fields are not reordered */
101789 rLhs = rLhsOrig;
101790 }else{
@@ -101806,25 +102640,17 @@
101806 CollSeq *pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
101807 int labelOk = sqlite3VdbeMakeLabel(pParse);
101808 int r2, regToFree;
101809 int regCkNull = 0;
101810 int ii;
101811 int bLhsReal; /* True if the LHS of the IN has REAL affinity */
101812 assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
101813 if( destIfNull!=destIfFalse ){
101814 regCkNull = sqlite3GetTempReg(pParse);
101815 sqlite3VdbeAddOp3(v, OP_BitAnd, rLhs, rLhs, regCkNull);
101816 }
101817 bLhsReal = sqlite3ExprAffinity(pExpr->pLeft)==SQLITE_AFF_REAL;
101818 for(ii=0; ii<pList->nExpr; ii++){
101819 if( bLhsReal ){
101820 r2 = regToFree = sqlite3GetTempReg(pParse);
101821 sqlite3ExprCode(pParse, pList->a[ii].pExpr, r2);
101822 sqlite3VdbeAddOp4(v, OP_Affinity, r2, 1, 0, "E", P4_STATIC);
101823 }else{
101824 r2 = sqlite3ExprCodeTemp(pParse, pList->a[ii].pExpr, &regToFree);
101825 }
101826 if( regCkNull && sqlite3ExprCanBeNull(pList->a[ii].pExpr) ){
101827 sqlite3VdbeAddOp3(v, OP_BitAnd, regCkNull, r2, regCkNull);
101828 }
101829 sqlite3ReleaseTempReg(pParse, regToFree);
101830 if( ii<pList->nExpr-1 || destIfNull!=destIfFalse ){
@@ -102394,14 +103220,10 @@
102394 }
102395 if( aff>SQLITE_AFF_BLOB ){
102396 static const char zAff[] = "B\000C\000D\000E";
102397 assert( SQLITE_AFF_BLOB=='A' );
102398 assert( SQLITE_AFF_TEXT=='B' );
102399 if( iReg!=target ){
102400 sqlite3VdbeAddOp2(v, OP_SCopy, iReg, target);
102401 iReg = target;
102402 }
102403 sqlite3VdbeAddOp4(v, OP_Affinity, iReg, 1, 0,
102404 &zAff[(aff-'B')*2], P4_STATIC);
102405 }
102406 return iReg;
102407 }
@@ -103035,11 +103857,11 @@
103035 assert( pExpr->affExpr==OE_Rollback
103036 || pExpr->affExpr==OE_Abort
103037 || pExpr->affExpr==OE_Fail
103038 || pExpr->affExpr==OE_Ignore
103039 );
103040 if( !pParse->pTriggerTab ){
103041 sqlite3ErrorMsg(pParse,
103042 "RAISE() may only be used within a trigger-program");
103043 return 0;
103044 }
103045 if( pExpr->affExpr==OE_Abort ){
@@ -103049,12 +103871,13 @@
103049 if( pExpr->affExpr==OE_Ignore ){
103050 sqlite3VdbeAddOp4(
103051 v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0);
103052 VdbeCoverage(v);
103053 }else{
103054 sqlite3HaltConstraint(pParse, SQLITE_CONSTRAINT_TRIGGER,
103055 pExpr->affExpr, pExpr->u.zToken, 0, 0);
 
103056 }
103057
103058 break;
103059 }
103060 #endif
@@ -104805,10 +105628,26 @@
104805 exit_rename_table:
104806 sqlite3SrcListDelete(db, pSrc);
104807 sqlite3DbFree(db, zName);
104808 db->mDbFlags = savedDbFlags;
104809 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104810
104811 /*
104812 ** This function is called after an "ALTER TABLE ... ADD" statement
104813 ** has been parsed. Argument pColDef contains the text of the new
104814 ** column definition.
@@ -104858,11 +105697,12 @@
104858 if( pCol->colFlags & COLFLAG_PRIMKEY ){
104859 sqlite3ErrorMsg(pParse, "Cannot add a PRIMARY KEY column");
104860 return;
104861 }
104862 if( pNew->pIndex ){
104863 sqlite3ErrorMsg(pParse, "Cannot add a UNIQUE column");
 
104864 return;
104865 }
104866 if( (pCol->colFlags & COLFLAG_GENERATED)==0 ){
104867 /* If the default value for the new column was specified with a
104868 ** literal NULL, then set pDflt to 0. This simplifies checking
@@ -104871,19 +105711,18 @@
104871 assert( pDflt==0 || pDflt->op==TK_SPAN );
104872 if( pDflt && pDflt->pLeft->op==TK_NULL ){
104873 pDflt = 0;
104874 }
104875 if( (db->flags&SQLITE_ForeignKeys) && pNew->pFKey && pDflt ){
104876 sqlite3ErrorMsg(pParse,
104877 "Cannot add a REFERENCES column with non-NULL default value");
104878 return;
104879 }
104880 if( pCol->notNull && !pDflt ){
104881 sqlite3ErrorMsg(pParse,
104882 "Cannot add a NOT NULL column with default value NULL");
104883 return;
104884 }
 
104885
104886 /* Ensure the default expression is something that sqlite3ValueFromExpr()
104887 ** can handle (i.e. not CURRENT_TIME etc.)
104888 */
104889 if( pDflt ){
@@ -104894,18 +105733,17 @@
104894 if( rc!=SQLITE_OK ){
104895 assert( db->mallocFailed == 1 );
104896 return;
104897 }
104898 if( !pVal ){
104899 sqlite3ErrorMsg(pParse,"Cannot add a column with non-constant default");
104900 return;
104901 }
104902 sqlite3ValueFree(pVal);
104903 }
104904 }else if( pCol->colFlags & COLFLAG_STORED ){
104905 sqlite3ErrorMsg(pParse, "cannot add a STORED column");
104906 return;
104907 }
104908
104909
104910 /* Modify the CREATE TABLE statement. */
104911 zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n);
@@ -105020,10 +105858,11 @@
105020 }
105021 memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol);
105022 for(i=0; i<pNew->nCol; i++){
105023 Column *pCol = &pNew->aCol[i];
105024 pCol->zName = sqlite3DbStrDup(db, pCol->zName);
 
105025 pCol->zColl = 0;
105026 pCol->pDflt = 0;
105027 }
105028 pNew->pSchema = db->aDb[iDb].pSchema;
105029 pNew->addColOffset = pTab->addColOffset;
@@ -105248,11 +106087,11 @@
105248 */
105249 SQLITE_PRIVATE void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){
105250 RenameToken *pNew;
105251 assert( pPtr || pParse->db->mallocFailed );
105252 renameTokenCheckAll(pParse, pPtr);
105253 if( pParse->eParseMode!=PARSE_MODE_UNMAP ){
105254 pNew = sqlite3DbMallocZero(pParse->db, sizeof(RenameToken));
105255 if( pNew ){
105256 pNew->p = pPtr;
105257 pNew->t = *pToken;
105258 pNew->pNext = pParse->pRename;
@@ -105305,10 +106144,25 @@
105305 sqlite3WalkSelect(pWalker, p);
105306 sqlite3RenameExprlistUnmap(pWalker->pParse, pWith->a[i].pCols);
105307 }
105308 }
105309 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105310
105311 /*
105312 ** Walker callback used by sqlite3RenameExprUnmap().
105313 */
105314 static int renameUnmapSelectCb(Walker *pWalker, Select *p){
@@ -105327,10 +106181,11 @@
105327 if( ALWAYS(p->pSrc) ){ /* Every Select as a SrcList, even if it is empty */
105328 SrcList *pSrc = p->pSrc;
105329 for(i=0; i<pSrc->nSrc; i++){
105330 sqlite3RenameTokenRemap(pParse, 0, (void*)pSrc->a[i].zName);
105331 if( sqlite3WalkExpr(pWalker, pSrc->a[i].pOn) ) return WRC_Abort;
 
105332 }
105333 }
105334
105335 renameWalkWith(pWalker, p);
105336 return WRC_Continue;
@@ -105534,10 +106389,11 @@
105534 renameTokenFind(pParse, pCtx, (void*)zName);
105535 }
105536 }
105537 }
105538 }
 
105539
105540 /*
105541 ** Parse the SQL statement zSql using Parse object (*p). The Parse object
105542 ** is initialized by this function before it is used.
105543 */
@@ -106446,10 +107302,15 @@
106446 sqlite3 *db = pParse->db;
106447 Db *pDb;
106448 Vdbe *v = sqlite3GetVdbe(pParse);
106449 int aRoot[ArraySize(aTable)];
106450 u8 aCreateTbl[ArraySize(aTable)];
 
 
 
 
 
106451
106452 if( v==0 ) return;
106453 assert( sqlite3BtreeHoldsAllMutexes(db) );
106454 assert( sqlite3VdbeDb(v)==db );
106455 pDb = &db->aDb[iDb];
@@ -106458,12 +107319,13 @@
106458 ** if they do already exist.
106459 */
106460 for(i=0; i<ArraySize(aTable); i++){
106461 const char *zTab = aTable[i].zName;
106462 Table *pStat;
 
106463 if( (pStat = sqlite3FindTable(db, zTab, pDb->zDbSName))==0 ){
106464 if( aTable[i].zCols ){
106465 /* The sqlite_statN table does not exist. Create it. Note that a
106466 ** side-effect of the CREATE TABLE statement is to leave the rootpage
106467 ** of the new table in register pParse->regRoot. This is important
106468 ** because the OpenWrite opcode below will be needing it. */
106469 sqlite3NestedParse(pParse,
@@ -106475,11 +107337,10 @@
106475 }else{
106476 /* The table already exists. If zWhere is not NULL, delete all entries
106477 ** associated with the table zWhere. If zWhere is NULL, delete the
106478 ** entire contents of the table. */
106479 aRoot[i] = pStat->tnum;
106480 aCreateTbl[i] = 0;
106481 sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab);
106482 if( zWhere ){
106483 sqlite3NestedParse(pParse,
106484 "DELETE FROM %Q.%s WHERE %s=%Q",
106485 pDb->zDbSName, zTab, zWhereType, zWhere
@@ -106494,11 +107355,11 @@
106494 }
106495 }
106496 }
106497
106498 /* Open the sqlite_stat[134] tables for writing. */
106499 for(i=0; aTable[i].zCols; i++){
106500 assert( i<ArraySize(aTable) );
106501 sqlite3VdbeAddOp4Int(v, OP_OpenWrite, iStatCur+i, aRoot[i], iDb, 3);
106502 sqlite3VdbeChangeP5(v, aCreateTbl[i]);
106503 VdbeComment((v, aTable[i].zName));
106504 }
@@ -106533,13 +107394,16 @@
106533 u32 iHash; /* Tiebreaker hash */
106534 #endif
106535 };
106536 struct StatAccum {
106537 sqlite3 *db; /* Database connection, for malloc() */
106538 tRowcnt nRow; /* Number of rows in the entire table */
 
 
106539 int nCol; /* Number of columns in index + pk/rowid */
106540 int nKeyCol; /* Number of index columns w/o the pk/rowid */
 
106541 StatSample current; /* Current row as a StatSample */
106542 #ifdef SQLITE_ENABLE_STAT4
106543 tRowcnt nPSample; /* How often to do a periodic sample */
106544 int mxSample; /* Maximum number of samples to accumulate */
106545 u32 iPrn; /* Pseudo-random number used for sampling */
@@ -106615,31 +107479,32 @@
106615 ** Reclaim all memory of a StatAccum structure.
106616 */
106617 static void statAccumDestructor(void *pOld){
106618 StatAccum *p = (StatAccum*)pOld;
106619 #ifdef SQLITE_ENABLE_STAT4
106620 int i;
106621 for(i=0; i<p->nCol; i++) sampleClear(p->db, p->aBest+i);
106622 for(i=0; i<p->mxSample; i++) sampleClear(p->db, p->a+i);
106623 sampleClear(p->db, &p->current);
 
 
106624 #endif
106625 sqlite3DbFree(p->db, p);
106626 }
106627
106628 /*
106629 ** Implementation of the stat_init(N,K,C) SQL function. The three parameters
106630 ** are:
106631 ** N: The number of columns in the index including the rowid/pk (note 1)
106632 ** K: The number of columns in the index excluding the rowid/pk.
106633 ** C: The number of rows in the index (note 2)
 
106634 **
106635 ** Note 1: In the special case of the covering index that implements a
106636 ** WITHOUT ROWID table, N is the number of PRIMARY KEY columns, not the
106637 ** total number of columns in the table.
106638 **
106639 ** Note 2: C is only used for STAT4.
106640 **
106641 ** For indexes on ordinary rowid tables, N==K+1. But for indexes on
106642 ** WITHOUT ROWID tables, N=K+P where P is the number of columns in the
106643 ** PRIMARY KEY of the table. The covering index that implements the
106644 ** original WITHOUT ROWID table as N==K as a special case.
106645 **
@@ -106656,13 +107521,14 @@
106656 StatAccum *p;
106657 int nCol; /* Number of columns in index being sampled */
106658 int nKeyCol; /* Number of key columns */
106659 int nColUp; /* nCol rounded up for alignment */
106660 int n; /* Bytes of space to allocate */
106661 sqlite3 *db; /* Database connection */
106662 #ifdef SQLITE_ENABLE_STAT4
106663 int mxSample = SQLITE_STAT4_SAMPLES;
 
106664 #endif
106665
106666 /* Decode the three function arguments */
106667 UNUSED_PARAMETER(argc);
106668 nCol = sqlite3_value_int(argv[0]);
@@ -106673,39 +107539,43 @@
106673 assert( nKeyCol>0 );
106674
106675 /* Allocate the space required for the StatAccum object */
106676 n = sizeof(*p)
106677 + sizeof(tRowcnt)*nColUp /* StatAccum.anEq */
106678 + sizeof(tRowcnt)*nColUp /* StatAccum.anDLt */
106679 #ifdef SQLITE_ENABLE_STAT4
106680 + sizeof(tRowcnt)*nColUp /* StatAccum.anLt */
106681 + sizeof(StatSample)*(nCol+mxSample) /* StatAccum.aBest[], a[] */
106682 + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample)
 
 
106683 #endif
106684 ;
106685 db = sqlite3_context_db_handle(context);
106686 p = sqlite3DbMallocZero(db, n);
106687 if( p==0 ){
106688 sqlite3_result_error_nomem(context);
106689 return;
106690 }
106691
106692 p->db = db;
 
106693 p->nRow = 0;
 
106694 p->nCol = nCol;
106695 p->nKeyCol = nKeyCol;
 
106696 p->current.anDLt = (tRowcnt*)&p[1];
106697 p->current.anEq = &p->current.anDLt[nColUp];
106698
106699 #ifdef SQLITE_ENABLE_STAT4
106700 {
 
106701 u8 *pSpace; /* Allocated space not yet assigned */
106702 int i; /* Used to iterate through p->aSample[] */
106703
106704 p->iGet = -1;
106705 p->mxSample = mxSample;
106706 p->nPSample = (tRowcnt)(sqlite3_value_int64(argv[2])/(mxSample/3+1) + 1);
106707 p->current.anLt = &p->current.anEq[nColUp];
106708 p->iPrn = 0x689e962d*(u32)nCol ^ 0xd0944565*(u32)sqlite3_value_int(argv[2]);
106709
106710 /* Set up the StatAccum.a[] and aBest[] arrays */
106711 p->a = (struct StatSample*)&p->current.anLt[nColUp];
@@ -106729,11 +107599,11 @@
106729 ** (given by the 3rd parameter) is never used and can be any positive
106730 ** value. */
106731 sqlite3_result_blob(context, p, sizeof(*p), statAccumDestructor);
106732 }
106733 static const FuncDef statInitFuncdef = {
106734 2+IsStat4, /* nArg */
106735 SQLITE_UTF8, /* funcFlags */
106736 0, /* pUserData */
106737 0, /* pNext */
106738 statInit, /* xSFunc */
106739 0, /* xFinalize */
@@ -106933,14 +107803,17 @@
106933 ** P Pointer to the StatAccum object created by stat_init()
106934 ** C Index of left-most column to differ from previous row
106935 ** R Rowid for the current row. Might be a key record for
106936 ** WITHOUT ROWID tables.
106937 **
106938 ** This SQL function always returns NULL. It's purpose it to accumulate
106939 ** statistical data and/or samples in the StatAccum object about the
106940 ** index being analyzed. The stat_get() SQL function will later be used to
106941 ** extract relevant information for constructing the sqlite_statN tables.
 
 
 
106942 **
106943 ** The R parameter is only used for STAT4
106944 */
106945 static void statPush(
106946 sqlite3_context *context,
@@ -106962,11 +107835,11 @@
106962 /* This is the first call to this function. Do initialization. */
106963 for(i=0; i<p->nCol; i++) p->current.anEq[i] = 1;
106964 }else{
106965 /* Second and subsequent calls get processed here */
106966 #ifdef SQLITE_ENABLE_STAT4
106967 samplePushPrevious(p, iChng);
106968 #endif
106969
106970 /* Update anDLt[], anLt[] and anEq[] to reflect the values that apply
106971 ** to the current row of the index. */
106972 for(i=0; i<iChng; i++){
@@ -106973,30 +107846,29 @@
106973 p->current.anEq[i]++;
106974 }
106975 for(i=iChng; i<p->nCol; i++){
106976 p->current.anDLt[i]++;
106977 #ifdef SQLITE_ENABLE_STAT4
106978 p->current.anLt[i] += p->current.anEq[i];
106979 #endif
106980 p->current.anEq[i] = 1;
106981 }
106982 }
 
106983 p->nRow++;
106984 #ifdef SQLITE_ENABLE_STAT4
106985 if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){
106986 sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2]));
106987 }else{
106988 sampleSetRowid(p->db, &p->current, sqlite3_value_bytes(argv[2]),
106989 sqlite3_value_blob(argv[2]));
106990 }
106991 p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345;
106992 #endif
106993
106994 #ifdef SQLITE_ENABLE_STAT4
106995 {
106996 tRowcnt nLt = p->current.anLt[p->nCol-1];
106997
106998 /* Check if this is to be a periodic sample. If so, add it. */
106999 if( (nLt/p->nPSample)!=(nLt+1)/p->nPSample ){
107000 p->current.isPSample = 1;
107001 p->current.iCol = 0;
107002 sampleInsert(p, &p->current, p->nCol-1);
@@ -107008,13 +107880,18 @@
107008 p->current.iCol = i;
107009 if( i>=iChng || sampleIsBetterPost(p, &p->current, &p->aBest[i]) ){
107010 sampleCopy(p, &p->aBest[i], &p->current);
107011 }
107012 }
 
 
 
 
 
107013 }
107014 #endif
107015 }
 
107016 static const FuncDef statPushFuncdef = {
107017 2+IsStat4, /* nArg */
107018 SQLITE_UTF8, /* funcFlags */
107019 0, /* pUserData */
107020 0, /* pNext */
@@ -107062,10 +107939,11 @@
107062 assert( argc==2 );
107063 assert( eCall==STAT_GET_STAT1 || eCall==STAT_GET_NEQ
107064 || eCall==STAT_GET_ROWID || eCall==STAT_GET_NLT
107065 || eCall==STAT_GET_NDLT
107066 );
 
107067 if( eCall==STAT_GET_STAT1 )
107068 #else
107069 assert( argc==1 );
107070 #endif
107071 {
@@ -107097,11 +107975,12 @@
107097 if( zRet==0 ){
107098 sqlite3_result_error_nomem(context);
107099 return;
107100 }
107101
107102 sqlite3_snprintf(24, zRet, "%llu", (u64)p->nRow);
 
107103 z = zRet + sqlite3Strlen30(zRet);
107104 for(i=0; i<p->nKeyCol; i++){
107105 u64 nDistinct = p->current.anDLt[i] + 1;
107106 u64 iVal = (p->nRow + nDistinct - 1) / nDistinct;
107107 sqlite3_snprintf(24, z, " %llu", iVal);
@@ -107173,20 +108052,20 @@
107173 0, 0, /* xValue, xInverse */
107174 "stat_get", /* zName */
107175 {0}
107176 };
107177
107178 static void callStatGet(Parse *pParse, int regStat4, int iParam, int regOut){
107179 #ifdef SQLITE_ENABLE_STAT4
107180 sqlite3VdbeAddOp2(pParse->pVdbe, OP_Integer, iParam, regStat4+1);
107181 #elif SQLITE_DEBUG
107182 assert( iParam==STAT_GET_STAT1 );
107183 #else
107184 UNUSED_PARAMETER( iParam );
107185 #endif
107186 assert( regOut!=regStat4 && regOut!=regStat4+1 );
107187 sqlite3VdbeAddFunctionCall(pParse, 0, regStat4, regOut, 1+IsStat4,
107188 &statGetFuncdef, 0);
107189 }
107190
107191 /*
107192 ** Generate code to do an analysis of all indices associated with
@@ -107208,16 +108087,15 @@
107208 int i; /* Loop counter */
107209 int jZeroRows = -1; /* Jump from here if number of rows is zero */
107210 int iDb; /* Index of database containing pTab */
107211 u8 needTableCnt = 1; /* True to count the table */
107212 int regNewRowid = iMem++; /* Rowid for the inserted record */
107213 int regStat4 = iMem++; /* Register to hold StatAccum object */
107214 int regChng = iMem++; /* Index of changed index field */
107215 #ifdef SQLITE_ENABLE_STAT4
107216 int regRowid = iMem++; /* Rowid argument passed to stat_push() */
107217 #endif
107218 int regTemp = iMem++; /* Temporary use register */
 
107219 int regTabname = iMem++; /* Register containing table name */
107220 int regIdxname = iMem++; /* Register containing index name */
107221 int regStat1 = iMem++; /* Value for the stat column of sqlite_stat1 */
107222 int regPrev = iMem; /* MUST BE LAST (see below) */
107223 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
@@ -107341,21 +108219,30 @@
107341 /* Invoke the stat_init() function. The arguments are:
107342 **
107343 ** (1) the number of columns in the index including the rowid
107344 ** (or for a WITHOUT ROWID table, the number of PK columns),
107345 ** (2) the number of columns in the key without the rowid/pk
107346 ** (3) the number of rows in the index,
107347 **
107348 **
107349 ** The third argument is only used for STAT4
107350 */
 
 
 
107351 #ifdef SQLITE_ENABLE_STAT4
107352 sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regStat4+3);
 
 
 
 
107353 #endif
107354 sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat4+1);
107355 sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regStat4+2);
107356 sqlite3VdbeAddFunctionCall(pParse, 0, regStat4+1, regStat4, 2+IsStat4,
 
 
 
 
 
107357 &statInitFuncdef, 0);
107358
107359 /* Implementation of the following:
107360 **
107361 ** Rewind csr
@@ -107362,12 +108249,10 @@
107362 ** if eof(csr) goto end_of_scan;
107363 ** regChng = 0
107364 ** goto next_push_0;
107365 **
107366 */
107367 addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
107368 VdbeCoverage(v);
107369 sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng);
107370 addrNextRow = sqlite3VdbeCurrentAddr(v);
107371
107372 if( nColTest>0 ){
107373 int endDistinctTest = sqlite3VdbeMakeLabel(pParse);
@@ -107396,10 +108281,11 @@
107396 }
107397 for(i=0; i<nColTest; i++){
107398 char *pColl = (char*)sqlite3LocateCollSeq(pParse, pIdx->azColl[i]);
107399 sqlite3VdbeAddOp2(v, OP_Integer, i, regChng);
107400 sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regTemp);
 
107401 aGotoChng[i] =
107402 sqlite3VdbeAddOp4(v, OP_Ne, regTemp, 0, regPrev+i, pColl, P4_COLLSEQ);
107403 sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
107404 VdbeCoverage(v);
107405 }
@@ -107416,10 +108302,11 @@
107416 */
107417 sqlite3VdbeJumpHere(v, addrNextRow-1);
107418 for(i=0; i<nColTest; i++){
107419 sqlite3VdbeJumpHere(v, aGotoChng[i]);
107420 sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regPrev+i);
 
107421 }
107422 sqlite3VdbeResolveLabel(v, endDistinctTest);
107423 sqlite3DbFree(db, aGotoChng);
107424 }
107425
@@ -107429,34 +108316,50 @@
107429 ** stat_push(P, regChng, regRowid) // 3rd parameter STAT4 only
107430 ** Next csr
107431 ** if !eof(csr) goto next_row;
107432 */
107433 #ifdef SQLITE_ENABLE_STAT4
107434 assert( regRowid==(regStat4+2) );
107435 if( HasRowid(pTab) ){
107436 sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid);
107437 }else{
107438 Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
107439 int j, k, regKey;
107440 regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol);
107441 for(j=0; j<pPk->nKeyCol; j++){
107442 k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[j]);
107443 assert( k>=0 && k<pIdx->nColumn );
107444 sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j);
107445 VdbeComment((v, "%s", pTab->aCol[pPk->aiColumn[j]].zName));
107446 }
107447 sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid);
107448 sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol);
 
 
107449 }
107450 #endif
107451 assert( regChng==(regStat4+1) );
107452 sqlite3VdbeAddFunctionCall(pParse, 1, regStat4, regTemp, 2+IsStat4,
107453 &statPushFuncdef, 0);
107454 sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107455
107456 /* Add the entry to the stat1 table. */
107457 callStatGet(pParse, regStat4, STAT_GET_STAT1, regStat1);
107458 assert( "BBB"[0]==SQLITE_AFF_TEXT );
107459 sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0);
107460 sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
107461 sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid);
107462 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
@@ -107464,11 +108367,11 @@
107464 #endif
107465 sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
107466
107467 /* Add the entries to the stat4 table. */
107468 #ifdef SQLITE_ENABLE_STAT4
107469 {
107470 int regEq = regStat1;
107471 int regLt = regStat1+1;
107472 int regDLt = regStat1+2;
107473 int regSample = regStat1+3;
107474 int regCol = regStat1+4;
@@ -107478,16 +108381,16 @@
107478 u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
107479
107480 pParse->nMem = MAX(pParse->nMem, regCol+nCol);
107481
107482 addrNext = sqlite3VdbeCurrentAddr(v);
107483 callStatGet(pParse, regStat4, STAT_GET_ROWID, regSampleRowid);
107484 addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regSampleRowid);
107485 VdbeCoverage(v);
107486 callStatGet(pParse, regStat4, STAT_GET_NEQ, regEq);
107487 callStatGet(pParse, regStat4, STAT_GET_NLT, regLt);
107488 callStatGet(pParse, regStat4, STAT_GET_NDLT, regDLt);
107489 sqlite3VdbeAddOp4Int(v, seekOp, iTabCur, addrNext, regSampleRowid, 0);
107490 VdbeCoverage(v);
107491 for(i=0; i<nCol; i++){
107492 sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iTabCur, i, regCol+i);
107493 }
@@ -109619,10 +110522,11 @@
109619 int i;
109620 Column *pCol;
109621 assert( pTable!=0 );
109622 if( (pCol = pTable->aCol)!=0 ){
109623 for(i=0; i<pTable->nCol; i++, pCol++){
 
109624 sqlite3DbFree(db, pCol->zName);
109625 sqlite3ExprDelete(db, pCol->pDflt);
109626 sqlite3DbFree(db, pCol->zColl);
109627 }
109628 sqlite3DbFree(db, pTable->aCol);
@@ -110267,10 +111171,11 @@
110267 p->aCol = aNew;
110268 }
110269 pCol = &p->aCol[p->nCol];
110270 memset(pCol, 0, sizeof(p->aCol[0]));
110271 pCol->zName = z;
 
110272 sqlite3ColumnPropertiesFromName(p, pCol);
110273
110274 if( pType->n==0 ){
110275 /* If there is no type specified, columns have the default affinity
110276 ** 'BLOB' with a default size of 4 bytes. */
@@ -113657,11 +114562,11 @@
113657 pParse->rc = rc;
113658 return 1;
113659 }
113660 db->aDb[1].pBt = pBt;
113661 assert( db->aDb[1].pSchema );
113662 if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, -1, 0) ){
113663 sqlite3OomFault(db);
113664 return 1;
113665 }
113666 }
113667 return 0;
@@ -113768,11 +114673,11 @@
113768 char *p4, /* Error message */
113769 i8 p4type, /* P4_STATIC or P4_TRANSIENT */
113770 u8 p5Errmsg /* P5_ErrMsg type */
113771 ){
113772 Vdbe *v = sqlite3GetVdbe(pParse);
113773 assert( (errCode&0xff)==SQLITE_CONSTRAINT );
113774 if( onError==OE_Abort ){
113775 sqlite3MayAbort(pParse);
113776 }
113777 sqlite3VdbeAddOp4(v, OP_Halt, errCode, onError, 0, p4, p4type);
113778 sqlite3VdbeChangeP5(v, p5Errmsg);
@@ -115481,10 +116386,11 @@
115481 VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName));
115482 r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1,
115483 &iPartIdxLabel, pPrior, r1);
115484 sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1,
115485 pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn);
 
115486 sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel);
115487 pPrior = pIdx;
115488 }
115489 }
115490
@@ -122468,10 +123374,11 @@
122468 const char *(*filename_wal)(const char*);
122469 /* Version 3.32.0 and later */
122470 char *(*create_filename)(const char*,const char*,const char*,
122471 int,const char**);
122472 void (*free_filename)(char*);
 
122473 };
122474
122475 /*
122476 ** This is the function signature used for all extension entry points. It
122477 ** is also defined in the file "loadext.c".
@@ -122771,10 +123678,11 @@
122771 #define sqlite3_filename_journal sqlite3_api->filename_journal
122772 #define sqlite3_filename_wal sqlite3_api->filename_wal
122773 /* Version 3.32.0 and later */
122774 #define sqlite3_create_filename sqlite3_api->create_filename
122775 #define sqlite3_free_filename sqlite3_api->free_filename
 
122776 #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
122777
122778 #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
122779 /* This case when the file really is being compiled as a loadable
122780 ** extension */
@@ -123252,11 +124160,20 @@
123252 sqlite3_filename_journal,
123253 sqlite3_filename_wal,
123254 /* Version 3.32.0 and later */
123255 sqlite3_create_filename,
123256 sqlite3_free_filename,
 
123257 };
 
 
 
 
 
 
 
 
123258
123259 /*
123260 ** Attempt to load an SQLite extension library contained in the file
123261 ** zFile. The entry point is zProc. zProc may be 0 in which case a
123262 ** default entry point name (sqlite3_extension_init) is used. Use
@@ -123355,11 +124272,11 @@
123355 if( zAltEntry==0 ){
123356 sqlite3OsDlClose(pVfs, handle);
123357 return SQLITE_NOMEM_BKPT;
123358 }
123359 memcpy(zAltEntry, "sqlite3_", 8);
123360 for(iFile=ncFile-1; iFile>=0 && zFile[iFile]!='/'; iFile--){}
123361 iFile++;
123362 if( sqlite3_strnicmp(zFile+iFile, "lib", 3)==0 ) iFile += 3;
123363 for(iEntry=8; (c = zFile[iFile])!=0 && c!='.'; iFile++){
123364 if( sqlite3Isalpha(c) ){
123365 zAltEntry[iEntry++] = (char)sqlite3UpperToLower[(unsigned)c];
@@ -123659,53 +124576,54 @@
123659 ** that script and rerun it.
123660 */
123661
123662 /* The various pragma types */
123663 #define PragTyp_ACTIVATE_EXTENSIONS 0
123664 #define PragTyp_HEADER_VALUE 1
123665 #define PragTyp_AUTO_VACUUM 2
123666 #define PragTyp_FLAG 3
123667 #define PragTyp_BUSY_TIMEOUT 4
123668 #define PragTyp_CACHE_SIZE 5
123669 #define PragTyp_CACHE_SPILL 6
123670 #define PragTyp_CASE_SENSITIVE_LIKE 7
123671 #define PragTyp_COLLATION_LIST 8
123672 #define PragTyp_COMPILE_OPTIONS 9
123673 #define PragTyp_DATA_STORE_DIRECTORY 10
123674 #define PragTyp_DATABASE_LIST 11
123675 #define PragTyp_DEFAULT_CACHE_SIZE 12
123676 #define PragTyp_ENCODING 13
123677 #define PragTyp_FOREIGN_KEY_CHECK 14
123678 #define PragTyp_FOREIGN_KEY_LIST 15
123679 #define PragTyp_FUNCTION_LIST 16
123680 #define PragTyp_HARD_HEAP_LIMIT 17
123681 #define PragTyp_INCREMENTAL_VACUUM 18
123682 #define PragTyp_INDEX_INFO 19
123683 #define PragTyp_INDEX_LIST 20
123684 #define PragTyp_INTEGRITY_CHECK 21
123685 #define PragTyp_JOURNAL_MODE 22
123686 #define PragTyp_JOURNAL_SIZE_LIMIT 23
123687 #define PragTyp_LOCK_PROXY_FILE 24
123688 #define PragTyp_LOCKING_MODE 25
123689 #define PragTyp_PAGE_COUNT 26
123690 #define PragTyp_MMAP_SIZE 27
123691 #define PragTyp_MODULE_LIST 28
123692 #define PragTyp_OPTIMIZE 29
123693 #define PragTyp_PAGE_SIZE 30
123694 #define PragTyp_PRAGMA_LIST 31
123695 #define PragTyp_SECURE_DELETE 32
123696 #define PragTyp_SHRINK_MEMORY 33
123697 #define PragTyp_SOFT_HEAP_LIMIT 34
123698 #define PragTyp_SYNCHRONOUS 35
123699 #define PragTyp_TABLE_INFO 36
123700 #define PragTyp_TEMP_STORE 37
123701 #define PragTyp_TEMP_STORE_DIRECTORY 38
123702 #define PragTyp_THREADS 39
123703 #define PragTyp_WAL_AUTOCHECKPOINT 40
123704 #define PragTyp_WAL_CHECKPOINT 41
123705 #define PragTyp_LOCK_STATUS 42
123706 #define PragTyp_STATS 43
 
123707
123708 /* Property flags associated with various pragma. */
123709 #define PragFlg_NeedSchema 0x01 /* Force schema load before running */
123710 #define PragFlg_NoColumns 0x02 /* OP_ResultRow called with zero columns */
123711 #define PragFlg_NoColumns1 0x04 /* zero columns if RHS argument is present */
@@ -123792,10 +124710,15 @@
123792 /* ePragTyp: */ PragTyp_ACTIVATE_EXTENSIONS,
123793 /* ePragFlg: */ 0,
123794 /* ColNames: */ 0, 0,
123795 /* iArg: */ 0 },
123796 #endif
 
 
 
 
 
123797 #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
123798 {/* zName: */ "application_id",
123799 /* ePragTyp: */ PragTyp_HEADER_VALUE,
123800 /* ePragFlg: */ PragFlg_NoColumns1|PragFlg_Result0,
123801 /* ColNames: */ 0, 0,
@@ -124292,11 +125215,11 @@
124292 /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1,
124293 /* ColNames: */ 0, 0,
124294 /* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError },
124295 #endif
124296 };
124297 /* Number of pragmas: 66 on by default, 76 total. */
124298
124299 /************** End of pragma.h **********************************************/
124300 /************** Continuing where we left off in pragma.c *********************/
124301
124302 /*
@@ -124822,11 +125745,11 @@
124822 }else{
124823 /* Malloc may fail when setting the page-size, as there is an internal
124824 ** buffer that the pager module resizes using sqlite3_realloc().
124825 */
124826 db->nextPagesize = sqlite3Atoi(zRight);
124827 if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize,-1,0) ){
124828 sqlite3OomFault(db);
124829 }
124830 }
124831 break;
124832 }
@@ -125996,11 +126919,10 @@
125996 sqlite3ResolvePartIdxLabel(pParse, jmp3);
125997 }
125998 }
125999 sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v);
126000 sqlite3VdbeJumpHere(v, loopTop-1);
126001 #ifndef SQLITE_OMIT_BTREECOUNT
126002 if( !isQuick ){
126003 sqlite3VdbeLoadString(v, 2, "wrong # of entries in index ");
126004 for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
126005 if( pPk==pIdx ) continue;
126006 sqlite3VdbeAddOp2(v, OP_Count, iIdxCur+j, 3);
@@ -126010,11 +126932,10 @@
126010 sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3);
126011 integrityCheckResultRow(v);
126012 sqlite3VdbeJumpHere(v, addr);
126013 }
126014 }
126015 #endif /* SQLITE_OMIT_BTREECOUNT */
126016 }
126017 }
126018 {
126019 static const int iLn = VDBE_OFFSET_LINENO(2);
126020 static const VdbeOpList endCode[] = {
@@ -126444,10 +127365,29 @@
126444 sqlite3_limit(db, SQLITE_LIMIT_WORKER_THREADS, (int)(N&0x7fffffff));
126445 }
126446 returnSingleInt(v, sqlite3_limit(db, SQLITE_LIMIT_WORKER_THREADS, -1));
126447 break;
126448 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126449
126450 #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
126451 /*
126452 ** Report the current state of file logs for all databases
126453 */
@@ -129768,10 +130708,11 @@
129768 }
129769 zName = sqlite3MPrintf(db, "%.*z:%u", nName, zName, ++cnt);
129770 if( cnt>3 ) sqlite3_randomness(sizeof(cnt), &cnt);
129771 }
129772 pCol->zName = zName;
 
129773 sqlite3ColumnPropertiesFromName(0, pCol);
129774 if( zName && sqlite3HashInsert(&ht, zName, pCol)==pCol ){
129775 sqlite3OomFault(db);
129776 }
129777 }
@@ -131221,11 +132162,14 @@
131221 if( ExprHasProperty(pExpr, EP_FromJoin)
131222 && pExpr->iRightJoinTable==pSubst->iTable
131223 ){
131224 pExpr->iRightJoinTable = pSubst->iNewTable;
131225 }
131226 if( pExpr->op==TK_COLUMN && pExpr->iTable==pSubst->iTable ){
 
 
 
131227 if( pExpr->iColumn<0 ){
131228 pExpr->op = TK_NULL;
131229 }else{
131230 Expr *pNew;
131231 Expr *pCopy = pSubst->pEList->a[pExpr->iColumn].pExpr;
@@ -131239,10 +132183,11 @@
131239 if( pSubst->isLeftJoin && pCopy->op!=TK_COLUMN ){
131240 memset(&ifNullRow, 0, sizeof(ifNullRow));
131241 ifNullRow.op = TK_IF_NULL_ROW;
131242 ifNullRow.pLeft = pCopy;
131243 ifNullRow.iTable = pSubst->iNewTable;
 
131244 pCopy = &ifNullRow;
131245 }
131246 testcase( ExprHasProperty(pCopy, EP_Subquery) );
131247 pNew = sqlite3ExprDup(db, pCopy, 0);
131248 if( pNew && pSubst->isLeftJoin ){
@@ -133133,10 +134078,11 @@
133133 Vdbe *v = pParse->pVdbe;
133134 int i;
133135 struct AggInfo_func *pFunc;
133136 int nReg = pAggInfo->nFunc + pAggInfo->nColumn;
133137 if( nReg==0 ) return;
 
133138 #ifdef SQLITE_DEBUG
133139 /* Verify that all AggInfo registers are within the range specified by
133140 ** AggInfo.mnReg..AggInfo.mxReg */
133141 assert( nReg==pAggInfo->mxReg-pAggInfo->mnReg+1 );
133142 for(i=0; i<pAggInfo->nColumn; i++){
@@ -134402,11 +135348,10 @@
134402 VdbeComment((v, "indicate accumulator empty"));
134403 sqlite3VdbeAddOp1(v, OP_Return, regReset);
134404
134405 } /* endif pGroupBy. Begin aggregate queries without GROUP BY: */
134406 else {
134407 #ifndef SQLITE_OMIT_BTREECOUNT
134408 Table *pTab;
134409 if( (pTab = isSimpleCount(p, &sAggInfo))!=0 ){
134410 /* If isSimpleCount() returns a pointer to a Table structure, then
134411 ** the SQL statement is of the form:
134412 **
@@ -134438,17 +135383,19 @@
134438 **
134439 ** In practice the KeyInfo structure will not be used. It is only
134440 ** passed to keep OP_OpenRead happy.
134441 */
134442 if( !HasRowid(pTab) ) pBest = sqlite3PrimaryKeyIndex(pTab);
134443 for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
134444 if( pIdx->bUnordered==0
134445 && pIdx->szIdxRow<pTab->szTabRow
134446 && pIdx->pPartIdxWhere==0
134447 && (!pBest || pIdx->szIdxRow<pBest->szIdxRow)
134448 ){
134449 pBest = pIdx;
 
 
134450 }
134451 }
134452 if( pBest ){
134453 iRoot = pBest->tnum;
134454 pKeyInfo = sqlite3KeyInfoOfIndex(pParse, pBest);
@@ -134460,13 +135407,11 @@
134460 sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO);
134461 }
134462 sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem);
134463 sqlite3VdbeAddOp1(v, OP_Close, iCsr);
134464 explainSimpleCount(pParse, pTab, pBest);
134465 }else
134466 #endif /* SQLITE_OMIT_BTREECOUNT */
134467 {
134468 int regAcc = 0; /* "populate accumulators" flag */
134469
134470 /* If there are accumulator registers but no min() or max() functions
134471 ** without FILTER clauses, allocate register regAcc. Register regAcc
134472 ** will contain 0 the first time the inner loop runs, and 1 thereafter.
@@ -137542,11 +138487,11 @@
137542 sqlite3SetString(pzErrMsg, db, "output file already exists");
137543 goto end_of_vacuum;
137544 }
137545 db->mDbFlags |= DBFLAG_VacuumInto;
137546 }
137547 nRes = sqlite3BtreeGetOptimalReserve(pMain);
137548
137549 sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size);
137550 sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0));
137551 sqlite3BtreeSetPagerFlags(pTemp, PAGER_SYNCHRONOUS_OFF|PAGER_CACHESPILL);
137552
@@ -137686,11 +138631,11 @@
137686 db->mDbFlags = saved_mDbFlags;
137687 db->flags = saved_flags;
137688 db->nChange = saved_nChange;
137689 db->nTotalChange = saved_nTotalChange;
137690 db->mTrace = saved_mTrace;
137691 sqlite3BtreeSetPageSize(pMain, -1, -1, 1);
137692
137693 /* Currently there is an SQL level transaction open on the vacuum
137694 ** database. No locks are held on any other files (since the main file
137695 ** was committed at the btree level). So it safe to end the transaction
137696 ** by manually setting the autoCommit flag to true and detaching the
@@ -156491,14 +157436,15 @@
156491 ** simplify to constants 0 (false) and 1 (true), respectively,
156492 ** regardless of the value of expr1.
156493 */
156494 sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy202);
156495 yymsp[-4].minor.yy202 = sqlite3Expr(pParse->db, TK_INTEGER, yymsp[-3].minor.yy192 ? "1" : "0");
156496 }else if( 0 && yymsp[-1].minor.yy242->nExpr==1 && sqlite3ExprIsConstant(yymsp[-1].minor.yy242->a[0].pExpr) ){
156497 Expr *pRHS = yymsp[-1].minor.yy242->a[0].pExpr;
156498 yymsp[-1].minor.yy242->a[0].pExpr = 0;
156499 sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy242);
 
156500 yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy202, pRHS);
156501 if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0);
156502 }else{
156503 yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy202, 0);
156504 if( yymsp[-4].minor.yy202 ){
@@ -158376,11 +159322,11 @@
158376 VVA_ONLY( u8 startedWithOom = db->mallocFailed );
158377
158378 assert( zSql!=0 );
158379 mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
158380 if( db->nVdbeActive==0 ){
158381 db->u1.isInterrupted = 0;
158382 }
158383 pParse->rc = SQLITE_OK;
158384 pParse->zTail = zSql;
158385 assert( pzErrMsg!=0 );
158386 #ifdef SQLITE_DEBUG
@@ -158421,11 +159367,11 @@
158421 );
158422 #else
158423 if( tokenType>=TK_SPACE ){
158424 assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL );
158425 #endif /* SQLITE_OMIT_WINDOWFUNC */
158426 if( db->u1.isInterrupted ){
158427 pParse->rc = SQLITE_INTERRUPT;
158428 break;
158429 }
158430 if( tokenType==TK_SPACE ){
158431 zSql += n;
@@ -159088,19 +160034,82 @@
159088
159089
159090 /************** End of sqliteicu.h *******************************************/
159091 /************** Continuing where we left off in main.c ***********************/
159092 #endif
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159093 #ifdef SQLITE_ENABLE_JSON1
159094 SQLITE_PRIVATE int sqlite3Json1Init(sqlite3*);
159095 #endif
159096 #ifdef SQLITE_ENABLE_STMTVTAB
159097 SQLITE_PRIVATE int sqlite3StmtVtabInit(sqlite3*);
159098 #endif
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159099 #ifdef SQLITE_ENABLE_FTS5
159100 SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3*);
159101 #endif
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159102
159103 #ifndef SQLITE_AMALGAMATION
159104 /* IMPLEMENTATION-OF: R-46656-45156 The sqlite3_version[] string constant
159105 ** contains the text of SQLITE_VERSION macro.
159106 */
@@ -160614,12 +161623,11 @@
160614 ** Return non-zero to retry the lock. Return zero to stop trying
160615 ** and cause SQLite to return SQLITE_BUSY.
160616 */
160617 static int sqliteDefaultBusyCallback(
160618 void *ptr, /* Database connection */
160619 int count, /* Number of times table has been busy */
160620 sqlite3_file *pFile /* The file on which the lock occurred */
160621 ){
160622 #if SQLITE_OS_WIN || HAVE_USLEEP
160623 /* This case is for systems that have support for sleeping for fractions of
160624 ** a second. Examples: All windows systems, unix systems with usleep() */
160625 static const u8 delays[] =
@@ -160629,23 +161637,10 @@
160629 # define NDELAY ArraySize(delays)
160630 sqlite3 *db = (sqlite3 *)ptr;
160631 int tmout = db->busyTimeout;
160632 int delay, prior;
160633
160634 #ifdef SQLITE_ENABLE_SETLK_TIMEOUT
160635 if( sqlite3OsFileControl(pFile,SQLITE_FCNTL_LOCK_TIMEOUT,&tmout)==SQLITE_OK ){
160636 if( count ){
160637 tmout = 0;
160638 sqlite3OsFileControl(pFile, SQLITE_FCNTL_LOCK_TIMEOUT, &tmout);
160639 return 0;
160640 }else{
160641 return 1;
160642 }
160643 }
160644 #else
160645 UNUSED_PARAMETER(pFile);
160646 #endif
160647 assert( count>=0 );
160648 if( count < NDELAY ){
160649 delay = delays[count];
160650 prior = totals[count];
160651 }else{
@@ -160661,11 +161656,10 @@
160661 #else
160662 /* This case for unix systems that lack usleep() support. Sleeping
160663 ** must be done in increments of whole seconds */
160664 sqlite3 *db = (sqlite3 *)ptr;
160665 int tmout = ((sqlite3 *)ptr)->busyTimeout;
160666 UNUSED_PARAMETER(pFile);
160667 if( (count+1)*1000 > tmout ){
160668 return 0;
160669 }
160670 sqlite3OsSleep(db->pVfs, 1000000);
160671 return 1;
@@ -160679,23 +161673,14 @@
160679 ** lock on VFS file pFile.
160680 **
160681 ** If this routine returns non-zero, the lock is retried. If it
160682 ** returns 0, the operation aborts with an SQLITE_BUSY error.
160683 */
160684 SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler *p, sqlite3_file *pFile){
160685 int rc;
160686 if( p->xBusyHandler==0 || p->nBusy<0 ) return 0;
160687 if( p->bExtraFileArg ){
160688 /* Add an extra parameter with the pFile pointer to the end of the
160689 ** callback argument list */
160690 int (*xTra)(void*,int,sqlite3_file*);
160691 xTra = (int(*)(void*,int,sqlite3_file*))p->xBusyHandler;
160692 rc = xTra(p->pBusyArg, p->nBusy, pFile);
160693 }else{
160694 /* Legacy style busy handler callback */
160695 rc = p->xBusyHandler(p->pBusyArg, p->nBusy);
160696 }
160697 if( rc==0 ){
160698 p->nBusy = -1;
160699 }else{
160700 p->nBusy++;
160701 }
@@ -160716,11 +161701,10 @@
160716 #endif
160717 sqlite3_mutex_enter(db->mutex);
160718 db->busyHandler.xBusyHandler = xBusy;
160719 db->busyHandler.pBusyArg = pArg;
160720 db->busyHandler.nBusy = 0;
160721 db->busyHandler.bExtraFileArg = 0;
160722 db->busyTimeout = 0;
160723 sqlite3_mutex_leave(db->mutex);
160724 return SQLITE_OK;
160725 }
160726
@@ -160767,11 +161751,10 @@
160767 #endif
160768 if( ms>0 ){
160769 sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback,
160770 (void*)db);
160771 db->busyTimeout = ms;
160772 db->busyHandler.bExtraFileArg = 1;
160773 }else{
160774 sqlite3_busy_handler(db, 0, 0);
160775 }
160776 return SQLITE_OK;
160777 }
@@ -160784,11 +161767,11 @@
160784 if( !sqlite3SafetyCheckOk(db) && (db==0 || db->magic!=SQLITE_MAGIC_ZOMBIE) ){
160785 (void)SQLITE_MISUSE_BKPT;
160786 return;
160787 }
160788 #endif
160789 db->u1.isInterrupted = 1;
160790 }
160791
160792
160793 /*
160794 ** This function is exactly the same as sqlite3_create_function(), except
@@ -161406,11 +162389,11 @@
161406 rc = sqlite3ApiExit(db, rc);
161407
161408 /* If there are no active statements, clear the interrupt flag at this
161409 ** point. */
161410 if( db->nVdbeActive==0 ){
161411 db->u1.isInterrupted = 0;
161412 }
161413
161414 sqlite3_mutex_leave(db->mutex);
161415 return rc;
161416 #endif
@@ -162093,10 +163076,11 @@
162093 sqlite3 *db; /* Store allocated handle here */
162094 int rc; /* Return code */
162095 int isThreadsafe; /* True for threadsafe connections */
162096 char *zOpen = 0; /* Filename argument to pass to BtreeOpen() */
162097 char *zErrMsg = 0; /* Error message from sqlite3ParseUri() */
 
162098
162099 #ifdef SQLITE_ENABLE_API_ARMOR
162100 if( ppDb==0 ) return SQLITE_MISUSE_BKPT;
162101 #endif
162102 *ppDb = 0;
@@ -162241,10 +163225,13 @@
162241 | SQLITE_EnableQPSG
162242 #endif
162243 #if defined(SQLITE_DEFAULT_DEFENSIVE)
162244 | SQLITE_Defensive
162245 #endif
 
 
 
162246 ;
162247 sqlite3HashInit(&db->aCollSeq);
162248 #ifndef SQLITE_OMIT_VIRTUALTABLE
162249 sqlite3HashInit(&db->aModule);
162250 #endif
@@ -162333,18 +163320,15 @@
162333 */
162334 sqlite3Error(db, SQLITE_OK);
162335 sqlite3RegisterPerConnectionBuiltinFunctions(db);
162336 rc = sqlite3_errcode(db);
162337
162338 #ifdef SQLITE_ENABLE_FTS5
162339 /* Register any built-in FTS5 module before loading the automatic
162340 ** extensions. This allows automatic extensions to register FTS5
162341 ** tokenizers and auxiliary functions. */
162342 if( !db->mallocFailed && rc==SQLITE_OK ){
162343 rc = sqlite3Fts5Init(db);
162344 }
162345 #endif
162346
162347 /* Load automatic extensions - extensions that have been registered
162348 ** using the sqlite3_automatic_extension() API.
162349 */
162350 if( rc==SQLITE_OK ){
@@ -162353,66 +163337,10 @@
162353 if( rc!=SQLITE_OK ){
162354 goto opendb_out;
162355 }
162356 }
162357
162358 #ifdef SQLITE_ENABLE_FTS1
162359 if( !db->mallocFailed ){
162360 extern int sqlite3Fts1Init(sqlite3*);
162361 rc = sqlite3Fts1Init(db);
162362 }
162363 #endif
162364
162365 #ifdef SQLITE_ENABLE_FTS2
162366 if( !db->mallocFailed && rc==SQLITE_OK ){
162367 extern int sqlite3Fts2Init(sqlite3*);
162368 rc = sqlite3Fts2Init(db);
162369 }
162370 #endif
162371
162372 #ifdef SQLITE_ENABLE_FTS3 /* automatically defined by SQLITE_ENABLE_FTS4 */
162373 if( !db->mallocFailed && rc==SQLITE_OK ){
162374 rc = sqlite3Fts3Init(db);
162375 }
162376 #endif
162377
162378 #if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS)
162379 if( !db->mallocFailed && rc==SQLITE_OK ){
162380 rc = sqlite3IcuInit(db);
162381 }
162382 #endif
162383
162384 #ifdef SQLITE_ENABLE_RTREE
162385 if( !db->mallocFailed && rc==SQLITE_OK){
162386 rc = sqlite3RtreeInit(db);
162387 }
162388 #endif
162389
162390 #ifdef SQLITE_ENABLE_DBPAGE_VTAB
162391 if( !db->mallocFailed && rc==SQLITE_OK){
162392 rc = sqlite3DbpageRegister(db);
162393 }
162394 #endif
162395
162396 #ifdef SQLITE_ENABLE_DBSTAT_VTAB
162397 if( !db->mallocFailed && rc==SQLITE_OK){
162398 rc = sqlite3DbstatRegister(db);
162399 }
162400 #endif
162401
162402 #ifdef SQLITE_ENABLE_JSON1
162403 if( !db->mallocFailed && rc==SQLITE_OK){
162404 rc = sqlite3Json1Init(db);
162405 }
162406 #endif
162407
162408 #ifdef SQLITE_ENABLE_STMTVTAB
162409 if( !db->mallocFailed && rc==SQLITE_OK){
162410 rc = sqlite3StmtVtabInit(db);
162411 }
162412 #endif
162413
162414 #ifdef SQLITE_ENABLE_INTERNAL_FUNCTIONS
162415 /* Testing use only!!! The -DSQLITE_ENABLE_INTERNAL_FUNCTIONS=1 compile-time
162416 ** option gives access to internal functions by default.
162417 ** Testing use only!!! */
162418 db->mDbFlags |= DBFLAG_InternalFunc;
@@ -162893,10 +163821,17 @@
162893 }else if( op==SQLITE_FCNTL_JOURNAL_POINTER ){
162894 *(sqlite3_file**)pArg = sqlite3PagerJrnlFile(pPager);
162895 rc = SQLITE_OK;
162896 }else if( op==SQLITE_FCNTL_DATA_VERSION ){
162897 *(unsigned int*)pArg = sqlite3PagerDataVersion(pPager);
 
 
 
 
 
 
 
162898 rc = SQLITE_OK;
162899 }else{
162900 rc = sqlite3OsFileControl(fd, op, pArg);
162901 }
162902 sqlite3BtreeLeave(pBtree);
@@ -163110,24 +164045,10 @@
163110 case SQLITE_TESTCTRL_BYTEORDER: {
163111 rc = SQLITE_BYTEORDER*100 + SQLITE_LITTLEENDIAN*10 + SQLITE_BIGENDIAN;
163112 break;
163113 }
163114
163115 /* sqlite3_test_control(SQLITE_TESTCTRL_RESERVE, sqlite3 *db, int N)
163116 **
163117 ** Set the nReserve size to N for the main database on the database
163118 ** connection db.
163119 */
163120 case SQLITE_TESTCTRL_RESERVE: {
163121 sqlite3 *db = va_arg(ap, sqlite3*);
163122 int x = va_arg(ap,int);
163123 sqlite3_mutex_enter(db->mutex);
163124 sqlite3BtreeSetPageSize(db->aDb[0].pBt, 0, x, 0);
163125 sqlite3_mutex_leave(db->mutex);
163126 break;
163127 }
163128
163129 /* sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, sqlite3 *db, int N)
163130 **
163131 ** Enable or disable various optimizations for testing purposes. The
163132 ** argument N is a bitmask of optimizations to be disabled. For normal
163133 ** operation N should be 0. The idea is that a test program (like the
@@ -165185,10 +166106,11 @@
165185 SQLITE_PRIVATE void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,int*,u8*);
165186 SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *);
165187 SQLITE_PRIVATE int sqlite3Fts3FirstFilter(sqlite3_int64, char *, int, char *);
165188 SQLITE_PRIVATE void sqlite3Fts3CreateStatTable(int*, Fts3Table*);
165189 SQLITE_PRIVATE int sqlite3Fts3EvalTestDeferred(Fts3Cursor *pCsr, int *pRc);
 
165190
165191 /* fts3_tokenizer.c */
165192 SQLITE_PRIVATE const char *sqlite3Fts3NextToken(const char *, int *);
165193 SQLITE_PRIVATE int sqlite3Fts3InitHashTable(sqlite3 *, Fts3Hash *, const char *);
165194 SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(Fts3Hash *pHash, const char *,
@@ -165916,10 +166838,26 @@
165916 fts3Appendf(pRc, &zRet, ", ?");
165917 }
165918 sqlite3_free(zFree);
165919 return zRet;
165920 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165921
165922 /*
165923 ** This function interprets the string at (*pp) as a non-negative integer
165924 ** value. It reads the integer and sets *pnOut to the value read, then
165925 ** sets *pp to point to the byte immediately following the last byte of
@@ -165932,23 +166870,21 @@
165932 **
165933 ** This function is used when parsing the "prefix=" FTS4 parameter.
165934 */
165935 static int fts3GobbleInt(const char **pp, int *pnOut){
165936 const int MAX_NPREFIX = 10000000;
165937 const char *p; /* Iterator pointer */
165938 int nInt = 0; /* Output value */
165939
165940 for(p=*pp; p[0]>='0' && p[0]<='9'; p++){
165941 nInt = nInt * 10 + (p[0] - '0');
165942 if( nInt>MAX_NPREFIX ){
165943 nInt = 0;
165944 break;
165945 }
165946 }
165947 if( p==*pp ) return SQLITE_ERROR;
165948 *pnOut = nInt;
165949 *pp = p;
165950 return SQLITE_OK;
165951 }
165952
165953 /*
165954 ** This function is called to allocate an array of Fts3Index structures
@@ -167126,11 +168062,13 @@
167126 static void fts3ReadNextPos(
167127 char **pp, /* IN/OUT: Pointer into position-list buffer */
167128 sqlite3_int64 *pi /* IN/OUT: Value read from position-list */
167129 ){
167130 if( (**pp)&0xFE ){
167131 fts3GetDeltaVarint(pp, pi);
 
 
167132 *pi -= 2;
167133 }else{
167134 *pi = POSITION_LIST_END;
167135 }
167136 }
@@ -172026,14 +172964,11 @@
172026
172027 /* If this is a "NEAR" keyword, check for an explicit nearness. */
172028 if( pKey->eType==FTSQUERY_NEAR ){
172029 assert( nKey==4 );
172030 if( zInput[4]=='/' && zInput[5]>='0' && zInput[5]<='9' ){
172031 nNear = 0;
172032 for(nKey=5; zInput[nKey]>='0' && zInput[nKey]<='9'; nKey++){
172033 nNear = nNear * 10 + (zInput[nKey] - '0');
172034 }
172035 }
172036 }
172037
172038 /* At this point this is probably a keyword. But for that to be true,
172039 ** the next byte must contain either whitespace, an open or close
@@ -176558,10 +177493,11 @@
176558 ** b-tree node. And that the final byte of the doclist is 0x00. If either
176559 ** of these statements is untrue, then the data structure is corrupt.
176560 */
176561 if( pReader->nDoclist > pReader->nNode-(pReader->aDoclist-pReader->aNode)
176562 || (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1])
 
176563 ){
176564 return FTS_CORRUPT_VTAB;
176565 }
176566 return SQLITE_OK;
176567 }
@@ -178211,25 +179147,25 @@
178211 ){
178212 const unsigned char *zText = sqlite3_column_text(pStmt, iCol);
178213 if( zText ){
178214 int i;
178215 int iMul = 1;
178216 i64 iVal = 0;
178217 for(i=0; zText[i]>='0' && zText[i]<='9'; i++){
178218 iVal = iVal*10 + (zText[i] - '0');
178219 }
178220 *piEndBlock = iVal;
178221 while( zText[i]==' ' ) i++;
178222 iVal = 0;
178223 if( zText[i]=='-' ){
178224 i++;
178225 iMul = -1;
178226 }
178227 for(/* no-op */; zText[i]>='0' && zText[i]<='9'; i++){
178228 iVal = iVal*10 + (zText[i] - '0');
178229 }
178230 *pnByte = (iVal * (i64)iMul);
178231 }
178232 }
178233
178234
178235 /*
@@ -223728,11 +224664,11 @@
223728 int nArg, /* Number of args */
223729 sqlite3_value **apUnused /* Function arguments */
223730 ){
223731 assert( nArg==0 );
223732 UNUSED_PARAM2(nArg, apUnused);
223733 sqlite3_result_text(pCtx, "fts5: 2020-03-21 23:10:38 5d14a1c4f2fc17de98ad685ad1422cdfda89dfccb00afcaf32ee416b6f84f525", -1, SQLITE_TRANSIENT);
223734 }
223735
223736 /*
223737 ** Return true if zName is the extension on one of the shadow tables used
223738 ** by this module.
@@ -228379,11 +229315,12 @@
228379 }
228380 case STMT_COLUMN_BUSY: {
228381 sqlite3_result_int(ctx, sqlite3_stmt_busy(pCur->pStmt));
228382 break;
228383 }
228384 case STMT_COLUMN_MEM: {
 
228385 i = SQLITE_STMTSTATUS_MEMUSED +
228386 STMT_COLUMN_NSCAN - SQLITE_STMTSTATUS_FULLSCAN_STEP;
228387 /* Fall thru */
228388 }
228389 case STMT_COLUMN_NSCAN:
@@ -228510,12 +229447,12 @@
228510 }
228511 #endif /* SQLITE_CORE */
228512 #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
228513
228514 /************** End of stmt.c ************************************************/
228515 #if __LINE__!=228515
228516 #undef SQLITE_SOURCE_ID
228517 #define SQLITE_SOURCE_ID "2020-03-21 23:10:38 5d14a1c4f2fc17de98ad685ad1422cdfda89dfccb00afcaf32ee416b6f84alt2"
228518 #endif
228519 /* Return the source-id for this library */
228520 SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
228521 /************************** End of sqlite3.c ******************************/
228522
--- src/sqlite3.c
+++ src/sqlite3.c
@@ -216,10 +216,13 @@
216 "ENABLE_ATOMIC_WRITE",
217 #endif
218 #if SQLITE_ENABLE_BATCH_ATOMIC_WRITE
219 "ENABLE_BATCH_ATOMIC_WRITE",
220 #endif
221 #if SQLITE_ENABLE_BYTECODE_VTAB
222 "ENABLE_BYTECODE_VTAB",
223 #endif
224 #if SQLITE_ENABLE_CEROD
225 "ENABLE_CEROD=" CTIMEOPT_VAL(SQLITE_ENABLE_CEROD),
226 #endif
227 #if SQLITE_ENABLE_COLUMN_METADATA
228 "ENABLE_COLUMN_METADATA",
@@ -534,13 +537,10 @@
537 "OMIT_BETWEEN_OPTIMIZATION",
538 #endif
539 #if SQLITE_OMIT_BLOB_LITERAL
540 "OMIT_BLOB_LITERAL",
541 #endif
 
 
 
542 #if SQLITE_OMIT_CAST
543 "OMIT_CAST",
544 #endif
545 #if SQLITE_OMIT_CHECK
546 "OMIT_CHECK",
@@ -1162,11 +1162,11 @@
1162 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
1163 ** [sqlite_version()] and [sqlite_source_id()].
1164 */
1165 #define SQLITE_VERSION "3.32.0"
1166 #define SQLITE_VERSION_NUMBER 3032000
1167 #define SQLITE_SOURCE_ID "2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f9654ff"
1168
1169 /*
1170 ** CAPI3REF: Run-Time Library Version Numbers
1171 ** KEYWORDS: sqlite3_version sqlite3_sourceid
1172 **
@@ -1336,30 +1336,26 @@
1336 ** for the [sqlite3] object.
1337 ** ^Calls to sqlite3_close() and sqlite3_close_v2() return [SQLITE_OK] if
1338 ** the [sqlite3] object is successfully destroyed and all associated
1339 ** resources are deallocated.
1340 **
1341 ** Ideally, applications should [sqlite3_finalize | finalize] all
1342 ** [prepared statements], [sqlite3_blob_close | close] all [BLOB handles], and
1343 ** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated
1344 ** with the [sqlite3] object prior to attempting to close the object.
1345 ** ^If the database connection is associated with unfinalized prepared
1346 ** statements, BLOB handlers, and/or unfinished sqlite3_backup objects then
1347 ** sqlite3_close() will leave the database connection open and return
1348 ** [SQLITE_BUSY]. ^If sqlite3_close_v2() is called with unfinalized prepared
1349 ** statements, unclosed BLOB handlers, and/or unfinished sqlite3_backups,
1350 ** it returns [SQLITE_OK] regardless, but instead of deallocating the database
1351 ** connection immediately, it marks the database connection as an unusable
1352 ** "zombie" and makes arrangements to automatically deallocate the database
1353 ** connection after all prepared statements are finalized, all BLOB handles
1354 ** are closed, and all backups have finished. The sqlite3_close_v2() interface
1355 ** is intended for use with host languages that are garbage collected, and
1356 ** where the order in which destructors are called is arbitrary.
 
 
 
 
 
 
 
 
1357 **
1358 ** ^If an [sqlite3] object is destroyed while a transaction is open,
1359 ** the transaction is automatically rolled back.
1360 **
1361 ** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)]
@@ -1544,22 +1540,25 @@
1540 #define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8))
1541 #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
1542 #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8))
1543 #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
1544 #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
1545 #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
1546 #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
1547 #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
1548 #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
1549 #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
1550 #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8))
1551 #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
1552 #define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8))
1553 #define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8))
1554 #define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8))
1555 #define SQLITE_CANTOPEN_DIRTYWAL (SQLITE_CANTOPEN | (5<<8)) /* Not Used */
1556 #define SQLITE_CANTOPEN_SYMLINK (SQLITE_CANTOPEN | (6<<8))
1557 #define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
1558 #define SQLITE_CORRUPT_SEQUENCE (SQLITE_CORRUPT | (2<<8))
1559 #define SQLITE_CORRUPT_INDEX (SQLITE_CORRUPT | (3<<8))
1560 #define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
1561 #define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
1562 #define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8))
1563 #define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4<<8))
1564 #define SQLITE_READONLY_CANTINIT (SQLITE_READONLY | (5<<8))
@@ -2124,14 +2123,16 @@
2123 ** so that all subsequent write operations are independent.
2124 ** ^SQLite will never invoke SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE without
2125 ** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE].
2126 **
2127 ** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]]
2128 ** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode is used to configure a VFS
2129 ** to block for up to M milliseconds before failing when attempting to
2130 ** obtain a file lock using the xLock or xShmLock methods of the VFS.
2131 ** The parameter is a pointer to a 32-bit signed integer that contains
2132 ** the value that M is to be set to. Before returning, the 32-bit signed
2133 ** integer is overwritten with the previous value of M.
2134 **
2135 ** <li>[[SQLITE_FCNTL_DATA_VERSION]]
2136 ** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
2137 ** a database file. The argument is a pointer to a 32-bit unsigned integer.
2138 ** The "data version" for the pager is written into the pointer. The
@@ -2148,10 +2149,15 @@
2149 ** a single attached database that occur due to other database connections,
2150 ** but omits changes implemented by the database connection on which it is
2151 ** called. This file control is the only mechanism to detect changes that
2152 ** happen either internally or externally and that are associated with
2153 ** a particular attached database.
2154 **
2155 ** <li>[[SQLITE_FCNTL_CKPT_START]]
2156 ** The [SQLITE_FCNTL_CKPT_START] opcode is invoked from within a checkpoint
2157 ** in wal mode before the client starts to copy pages from the wal
2158 ** file to the database file.
2159 **
2160 ** <li>[[SQLITE_FCNTL_CKPT_DONE]]
2161 ** The [SQLITE_FCNTL_CKPT_DONE] opcode is invoked from within a checkpoint
2162 ** in wal mode after the client has finished copying pages from the wal
2163 ** file to the database file, but before the *-shm file is updated to
@@ -2192,10 +2198,12 @@
2198 #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33
2199 #define SQLITE_FCNTL_LOCK_TIMEOUT 34
2200 #define SQLITE_FCNTL_DATA_VERSION 35
2201 #define SQLITE_FCNTL_SIZE_LIMIT 36
2202 #define SQLITE_FCNTL_CKPT_DONE 37
2203 #define SQLITE_FCNTL_RESERVE_BYTES 38
2204 #define SQLITE_FCNTL_CKPT_START 39
2205
2206 /* deprecated names */
2207 #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
2208 #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE
2209 #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO
@@ -4570,12 +4578,23 @@
4578 **
4579 ** These are utility routines, useful to [VFS|custom VFS implementations],
4580 ** that check if a database file was a URI that contained a specific query
4581 ** parameter, and if so obtains the value of that query parameter.
4582 **
4583 ** The first parameter to these interfaces (hereafter referred to
4584 ** as F) must be one of:
4585 ** <ul>
4586 ** <li> A database filename pointer created by the SQLite core and
4587 ** passed into the xOpen() method of a VFS implemention, or
4588 ** <li> A filename obtained from [sqlite3_db_filename()], or
4589 ** <li> A new filename constructed using [sqlite3_create_filename()].
4590 ** </ul>
4591 ** If the F parameter is not one of the above, then the behavior is
4592 ** undefined and probably undesirable. Older versions of SQLite were
4593 ** more tolerant of invalid F parameters than newer versions.
4594 **
4595 ** If F is a suitable filename (as described in the previous paragraph)
4596 ** and if P is the name of the query parameter, then
4597 ** sqlite3_uri_parameter(F,P) returns the value of the P
4598 ** parameter if it exists or a NULL pointer if P does not appear as a
4599 ** query parameter on F. If P is a query parameter of F and it
4600 ** has no explicit value, then sqlite3_uri_parameter(F,P) returns
@@ -4654,10 +4673,29 @@
4673 */
4674 SQLITE_API const char *sqlite3_filename_database(const char*);
4675 SQLITE_API const char *sqlite3_filename_journal(const char*);
4676 SQLITE_API const char *sqlite3_filename_wal(const char*);
4677
4678 /*
4679 ** CAPI3REF: Database File Corresponding To A Journal
4680 **
4681 ** ^If X is the name of a rollback or WAL-mode journal file that is
4682 ** passed into the xOpen method of [sqlite3_vfs], then
4683 ** sqlite3_database_file_object(X) returns a pointer to the [sqlite3_file]
4684 ** object that represents the main database file.
4685 **
4686 ** This routine is intended for use in custom [VFS] implementations
4687 ** only. It is not a general-purpose interface.
4688 ** The argument sqlite3_file_object(X) must be a filename pointer that
4689 ** has been passed into [sqlite3_vfs].xOpen method where the
4690 ** flags parameter to xOpen contains one of the bits
4691 ** [SQLITE_OPEN_MAIN_JOURNAL] or [SQLITE_OPEN_WAL]. Any other use
4692 ** of this routine results in undefined and probably undesirable
4693 ** behavior.
4694 */
4695 SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*);
4696
4697 /*
4698 ** CAPI3REF: Create and Destroy VFS Filenames
4699 **
4700 ** These interfces are provided for use by [VFS shim] implementations and
4701 ** are not useful outside of that context.
@@ -4688,11 +4726,11 @@
4726 ** None of the D, J, or W parameters to sqlite3_create_filename(D,J,W,N,P) may
4727 ** be NULL pointers, though they can be empty strings.
4728 **
4729 ** The sqlite3_free_filename(Y) routine releases a memory allocation
4730 ** previously obtained from sqlite3_create_filename(). Invoking
4731 ** sqlite3_free_filename(Y) where Y is a NULL pointer is a harmless no-op.
4732 **
4733 ** If the Y parameter to sqlite3_free_filename(Y) is anything other
4734 ** than a NULL pointer or a pointer previously acquired from
4735 ** sqlite3_create_filename(), then bad things such as heap
4736 ** corruption or segfaults may occur. The value Y should be
@@ -5295,10 +5333,28 @@
5333 **
5334 ** ^The third argument is the value to bind to the parameter.
5335 ** ^If the third parameter to sqlite3_bind_text() or sqlite3_bind_text16()
5336 ** or sqlite3_bind_blob() is a NULL pointer then the fourth parameter
5337 ** is ignored and the end result is the same as sqlite3_bind_null().
5338 ** ^If the third parameter to sqlite3_bind_text() is not NULL, then
5339 ** it should be a pointer to well-formed UTF8 text.
5340 ** ^If the third parameter to sqlite3_bind_text16() is not NULL, then
5341 ** it should be a pointer to well-formed UTF16 text.
5342 ** ^If the third parameter to sqlite3_bind_text64() is not NULL, then
5343 ** it should be a pointer to a well-formed unicode string that is
5344 ** either UTF8 if the sixth parameter is SQLITE_UTF8, or UTF16
5345 ** otherwise.
5346 **
5347 ** [[byte-order determination rules]] ^The byte-order of
5348 ** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF)
5349 ** found in first character, which is removed, or in the absence of a BOM
5350 ** the byte order is the native byte order of the host
5351 ** machine for sqlite3_bind_text16() or the byte order specified in
5352 ** the 6th parameter for sqlite3_bind_text64().)^
5353 ** ^If UTF16 input text contains invalid unicode
5354 ** characters, then SQLite might change those invalid characters
5355 ** into the unicode replacement character: U+FFFD.
5356 **
5357 ** ^(In those routines that have a fourth argument, its value is the
5358 ** number of bytes in the parameter. To be clear: the value is the
5359 ** number of <u>bytes</u> in the value, not the number of characters.)^
5360 ** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16()
@@ -5308,11 +5364,11 @@
5364 ** the behavior is undefined.
5365 ** If a non-negative fourth parameter is provided to sqlite3_bind_text()
5366 ** or sqlite3_bind_text16() or sqlite3_bind_text64() then
5367 ** that parameter must be the byte offset
5368 ** where the NUL terminator would occur assuming the string were NUL
5369 ** terminated. If any NUL characters occurs at byte offsets less than
5370 ** the value of the fourth parameter then the resulting string value will
5371 ** contain embedded NULs. The result of expressions involving strings
5372 ** with embedded NULs is undefined.
5373 **
5374 ** ^The fifth argument to the BLOB and string binding interfaces
@@ -6633,12 +6689,13 @@
6689 ** cause the implemented SQL function to throw an exception.
6690 ** ^SQLite uses the string pointed to by the
6691 ** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16()
6692 ** as the text of an error message. ^SQLite interprets the error
6693 ** message string from sqlite3_result_error() as UTF-8. ^SQLite
6694 ** interprets the string from sqlite3_result_error16() as UTF-16 using
6695 ** the same [byte-order determination rules] as [sqlite3_bind_text16()].
6696 ** ^If the third parameter to sqlite3_result_error()
6697 ** or sqlite3_result_error16() is negative then SQLite takes as the error
6698 ** message all text up through the first zero character.
6699 ** ^If the third parameter to sqlite3_result_error() or
6700 ** sqlite3_result_error16() is non-negative then SQLite takes that many
6701 ** bytes (not characters) from the 2nd parameter as the error message.
@@ -6701,10 +6758,29 @@
6758 ** when it has finished using that result.
6759 ** ^If the 4th parameter to the sqlite3_result_text* interfaces
6760 ** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT
6761 ** then SQLite makes a copy of the result into space obtained
6762 ** from [sqlite3_malloc()] before it returns.
6763 **
6764 ** ^For the sqlite3_result_text16(), sqlite3_result_text16le(), and
6765 ** sqlite3_result_text16be() routines, and for sqlite3_result_text64()
6766 ** when the encoding is not UTF8, if the input UTF16 begins with a
6767 ** byte-order mark (BOM, U+FEFF) then the BOM is removed from the
6768 ** string and the rest of the string is interpreted according to the
6769 ** byte-order specified by the BOM. ^The byte-order specified by
6770 ** the BOM at the beginning of the text overrides the byte-order
6771 ** specified by the interface procedure. ^So, for example, if
6772 ** sqlite3_result_text16le() is invoked with text that begins
6773 ** with bytes 0xfe, 0xff (a big-endian byte-order mark) then the
6774 ** first two bytes of input are skipped and the remaining input
6775 ** is interpreted as UTF16BE text.
6776 **
6777 ** ^For UTF16 input text to the sqlite3_result_text16(),
6778 ** sqlite3_result_text16be(), sqlite3_result_text16le(), and
6779 ** sqlite3_result_text64() routines, if the text contains invalid
6780 ** UTF16 characters, the invalid characters might be converted
6781 ** into the unicode replacement character, U+FFFD.
6782 **
6783 ** ^The sqlite3_result_value() interface sets the result of
6784 ** the application-defined function to be a copy of the
6785 ** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The
6786 ** sqlite3_result_value() interface makes a copy of the [sqlite3_value]
@@ -8649,11 +8725,11 @@
8725 #define SQLITE_TESTCTRL_FAULT_INSTALL 9
8726 #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
8727 #define SQLITE_TESTCTRL_PENDING_BYTE 11
8728 #define SQLITE_TESTCTRL_ASSERT 12
8729 #define SQLITE_TESTCTRL_ALWAYS 13
8730 #define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */
8731 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15
8732 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */
8733 #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */
8734 #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17
8735 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
@@ -13369,10 +13445,25 @@
13445 #pragma warn -aus /* Assigned value is never used */
13446 #pragma warn -csu /* Comparing signed and unsigned */
13447 #pragma warn -spa /* Suspicious pointer arithmetic */
13448 #endif
13449
13450 /*
13451 ** WAL mode depends on atomic aligned 32-bit loads and stores in a few
13452 ** places. The following macros try to make this explicit.
13453 */
13454 #ifndef __has_feature
13455 # define __has_feature(x) 0 /* compatibility with non-clang compilers */
13456 #endif
13457 #if GCC_VERSION>=4007000 || __has_feature(c_atomic)
13458 # define AtomicLoad(PTR) __atomic_load_n((PTR),__ATOMIC_RELAXED)
13459 # define AtomicStore(PTR,VAL) __atomic_store_n((PTR),(VAL),__ATOMIC_RELAXED)
13460 #else
13461 # define AtomicLoad(PTR) (*(PTR))
13462 # define AtomicStore(PTR,VAL) (*(PTR) = (VAL))
13463 #endif
13464
13465 /*
13466 ** Include standard header files as necessary
13467 */
13468 #ifdef HAVE_STDINT_H
13469 #include <stdint.h>
@@ -14446,11 +14537,10 @@
14537 typedef struct BusyHandler BusyHandler;
14538 struct BusyHandler {
14539 int (*xBusyHandler)(void *,int); /* The busy callback */
14540 void *pBusyArg; /* First arg to busy callback */
14541 int nBusy; /* Incremented with each busy call */
 
14542 };
14543
14544 /*
14545 ** Name of the master database table. The master database table
14546 ** is a special table that holds the names and attributes of all
@@ -14704,11 +14794,11 @@
14794 SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int nPagesize, int nReserve, int eFix);
14795 SQLITE_PRIVATE int sqlite3BtreeGetPageSize(Btree*);
14796 SQLITE_PRIVATE int sqlite3BtreeMaxPageCount(Btree*,int);
14797 SQLITE_PRIVATE u32 sqlite3BtreeLastPage(Btree*);
14798 SQLITE_PRIVATE int sqlite3BtreeSecureDelete(Btree*,int);
14799 SQLITE_PRIVATE int sqlite3BtreeGetRequestedReserve(Btree*);
14800 SQLITE_PRIVATE int sqlite3BtreeGetReserveNoMutex(Btree *p);
14801 SQLITE_PRIVATE int sqlite3BtreeSetAutoVacuum(Btree *, int);
14802 SQLITE_PRIVATE int sqlite3BtreeGetAutoVacuum(Btree *);
14803 SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree*,int,int*);
14804 SQLITE_PRIVATE int sqlite3BtreeCommitPhaseOne(Btree*, const char *zMaster);
@@ -14966,13 +15056,11 @@
15056 #ifndef NDEBUG
15057 SQLITE_PRIVATE int sqlite3BtreeCursorIsValid(BtCursor*);
15058 #endif
15059 SQLITE_PRIVATE int sqlite3BtreeCursorIsValidNN(BtCursor*);
15060
 
15061 SQLITE_PRIVATE int sqlite3BtreeCount(sqlite3*, BtCursor*, i64*);
 
15062
15063 #ifdef SQLITE_TEST
15064 SQLITE_PRIVATE int sqlite3BtreeCursorInfo(BtCursor*, int*, int);
15065 SQLITE_PRIVATE void sqlite3BtreeCursorList(Btree*);
15066 #endif
@@ -15543,10 +15631,13 @@
15631
15632 SQLITE_PRIVATE void sqlite3VdbeLinkSubProgram(Vdbe *, SubProgram *);
15633 SQLITE_PRIVATE int sqlite3VdbeHasSubProgram(Vdbe*);
15634
15635 SQLITE_PRIVATE int sqlite3NotPureFunc(sqlite3_context*);
15636 #ifdef SQLITE_ENABLE_BYTECODE_VTAB
15637 SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3*);
15638 #endif
15639
15640 /* Use SQLITE_ENABLE_COMMENTS to enable generation of extra comments on
15641 ** each VDBE opcode.
15642 **
15643 ** Use the SQLITE_ENABLE_MODULE_COMMENTS macro to see some extra no-op
@@ -15828,17 +15919,25 @@
15919 SQLITE_PRIVATE int sqlite3PagerWalSupported(Pager *pPager);
15920 SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager);
15921 SQLITE_PRIVATE int sqlite3PagerOpenWal(Pager *pPager, int *pisOpen);
15922 SQLITE_PRIVATE int sqlite3PagerCloseWal(Pager *pPager, sqlite3*);
15923 # ifdef SQLITE_ENABLE_SNAPSHOT
15924 SQLITE_PRIVATE int sqlite3PagerSnapshotGet(Pager*, sqlite3_snapshot **ppSnapshot);
15925 SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(Pager*, sqlite3_snapshot *pSnapshot);
15926 SQLITE_PRIVATE int sqlite3PagerSnapshotRecover(Pager *pPager);
15927 SQLITE_PRIVATE int sqlite3PagerSnapshotCheck(Pager *pPager, sqlite3_snapshot *pSnapshot);
15928 SQLITE_PRIVATE void sqlite3PagerSnapshotUnlock(Pager *pPager);
15929 # endif
15930 #endif
15931
15932 #if !defined(SQLITE_OMIT_WAL) && defined(SQLITE_ENABLE_SETLK_TIMEOUT)
15933 SQLITE_PRIVATE int sqlite3PagerWalWriteLock(Pager*, int);
15934 SQLITE_PRIVATE void sqlite3PagerWalDb(Pager*, sqlite3*);
15935 #else
15936 # define sqlite3PagerWalWriteLock(y,z) SQLITE_OK
15937 # define sqlite3PagerWalDb(x,y)
15938 #endif
15939
15940 #ifdef SQLITE_DIRECT_OVERFLOW_READ
15941 SQLITE_PRIVATE int sqlite3PagerDirectReadOk(Pager *pPager, Pgno pgno);
15942 #endif
15943
@@ -15861,15 +15960,10 @@
15960 SQLITE_PRIVATE void *sqlite3PagerTempSpace(Pager*);
15961 SQLITE_PRIVATE int sqlite3PagerIsMemdb(Pager*);
15962 SQLITE_PRIVATE void sqlite3PagerCacheStat(Pager *, int, int, int *);
15963 SQLITE_PRIVATE void sqlite3PagerClearCache(Pager*);
15964 SQLITE_PRIVATE int sqlite3SectorSize(sqlite3_file *);
 
 
 
 
 
15965
15966 /* Functions used to truncate the database file. */
15967 SQLITE_PRIVATE void sqlite3PagerTruncateImage(Pager*,Pgno);
15968
15969 SQLITE_PRIVATE void sqlite3PagerRekey(DbPage*, Pgno, u16);
@@ -16799,10 +16893,11 @@
16893 Hash aFunc; /* Hash table of connection functions */
16894 Hash aCollSeq; /* All collating sequences */
16895 BusyHandler busyHandler; /* Busy callback */
16896 Db aDbStatic[2]; /* Static space for the 2 default backends */
16897 Savepoint *pSavepoint; /* List of active savepoints */
16898 int nAnalysisLimit; /* Number of index rows to ANALYZE */
16899 int busyTimeout; /* Busy handler timeout, in msec */
16900 int nSavepoint; /* Number of non-transaction savepoints */
16901 int nStatement; /* Number of nested statement-transactions */
16902 i64 nDeferredCons; /* Net deferred constraints this transaction. */
16903 i64 nDeferredImmCons; /* Net deferred immediate constraints */
@@ -17209,10 +17304,11 @@
17304 Expr *pDflt; /* Default value or GENERATED ALWAYS AS value */
17305 char *zColl; /* Collating sequence. If NULL, use the default */
17306 u8 notNull; /* An OE_ code for handling a NOT NULL constraint */
17307 char affinity; /* One of the SQLITE_AFF_... values */
17308 u8 szEst; /* Estimated size of value in this column. sizeof(INT)==1 */
17309 u8 hName; /* Column name hash for faster lookup */
17310 u16 colFlags; /* Boolean properties. See COLFLAG_ defines below */
17311 };
17312
17313 /* Allowed values for Column.colFlags:
17314 */
@@ -19827,10 +19923,11 @@
19923 const char*,
19924 const char*,
19925 const char*
19926 );
19927 SQLITE_PRIVATE Bitmask sqlite3ExprColUsed(Expr*);
19928 SQLITE_PRIVATE u8 sqlite3StrIHash(const char*);
19929 SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*);
19930 SQLITE_PRIVATE int sqlite3ResolveExprListNames(NameContext*, ExprList*);
19931 SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*);
19932 SQLITE_PRIVATE int sqlite3ResolveSelfReference(Parse*,Table*,int,Expr*,ExprList*);
19933 SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*);
@@ -19842,11 +19939,11 @@
19939 SQLITE_PRIVATE void sqlite3RenameExprUnmap(Parse*, Expr*);
19940 SQLITE_PRIVATE void sqlite3RenameExprlistUnmap(Parse*, ExprList*);
19941 SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(Parse*, u8, CollSeq *, const char*);
19942 SQLITE_PRIVATE char sqlite3AffinityType(const char*, Column*);
19943 SQLITE_PRIVATE void sqlite3Analyze(Parse*, Token*, Token*);
19944 SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler*);
19945 SQLITE_PRIVATE int sqlite3FindDb(sqlite3*, Token*);
19946 SQLITE_PRIVATE int sqlite3FindDbName(sqlite3 *, const char *);
19947 SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3*,int iDB);
19948 SQLITE_PRIVATE void sqlite3DeleteIndexSamples(sqlite3*,Index*);
19949 SQLITE_PRIVATE void sqlite3DefaultRowEst(Index*);
@@ -20578,11 +20675,12 @@
20675 /*
20676 ** VDBE_DISPLAY_P4 is true or false depending on whether or not the
20677 ** "explain" P4 display logic is enabled.
20678 */
20679 #if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) \
20680 || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) \
20681 || defined(SQLITE_ENABLE_BYTECODE_VTAB)
20682 # define VDBE_DISPLAY_P4 1
20683 #else
20684 # define VDBE_DISPLAY_P4 0
20685 #endif
20686
@@ -21043,11 +21141,18 @@
21141
21142 int sqlite2BtreeKeyCompare(BtCursor *, const void *, int, int, int *);
21143 SQLITE_PRIVATE int sqlite3VdbeIdxKeyCompare(sqlite3*,VdbeCursor*,UnpackedRecord*,int*);
21144 SQLITE_PRIVATE int sqlite3VdbeIdxRowid(sqlite3*, BtCursor*, i64*);
21145 SQLITE_PRIVATE int sqlite3VdbeExec(Vdbe*);
21146 #if !defined(SQLITE_OMIT_EXPLAIN) || defined(SQLITE_ENABLE_BYTECODE_VTAB)
21147 SQLITE_PRIVATE int sqlite3VdbeNextOpcode(Vdbe*,Mem*,int,int*,int*,Op**);
21148 SQLITE_PRIVATE char *sqlite3VdbeDisplayP4(sqlite3*,Op*);
21149 #endif
21150 #if defined(SQLITE_ENABLE_EXPLAIN_COMMENTS)
21151 SQLITE_PRIVATE char *sqlite3VdbeDisplayComment(sqlite3*,const Op*,const char*);
21152 #endif
21153 #if !defined(SQLITE_OMIT_EXPLAIN)
21154 SQLITE_PRIVATE int sqlite3VdbeList(Vdbe*);
21155 #endif
21156 SQLITE_PRIVATE int sqlite3VdbeHalt(Vdbe*);
21157 SQLITE_PRIVATE int sqlite3VdbeChangeEncoding(Mem *, int);
21158 SQLITE_PRIVATE int sqlite3VdbeMemTooBig(Mem*);
@@ -21085,11 +21190,11 @@
21190 SQLITE_PRIVATE void sqlite3VdbeMemRelease(Mem *p);
21191 SQLITE_PRIVATE int sqlite3VdbeMemFinalize(Mem*, FuncDef*);
21192 #ifndef SQLITE_OMIT_WINDOWFUNC
21193 SQLITE_PRIVATE int sqlite3VdbeMemAggValue(Mem*, Mem*, FuncDef*);
21194 #endif
21195 #if !defined(SQLITE_OMIT_EXPLAIN) || defined(SQLITE_ENABLE_BYTECODE_VTAB)
21196 SQLITE_PRIVATE const char *sqlite3OpcodeName(int);
21197 #endif
21198 SQLITE_PRIVATE int sqlite3VdbeMemGrow(Mem *pMem, int n, int preserve);
21199 SQLITE_PRIVATE int sqlite3VdbeMemClearAndResize(Mem *pMem, int n);
21200 SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *, int);
@@ -22165,16 +22270,16 @@
22270 u8 nName; /* Length of th name */
22271 char *zName; /* Name of the transformation */
22272 double rLimit; /* Maximum NNN value for this transform */
22273 double rXform; /* Constant used for this transform */
22274 } aXformType[] = {
22275 { 0, 6, "second", 464269060800.0, 1000.0 },
22276 { 0, 6, "minute", 7737817680.0, 60000.0 },
22277 { 0, 4, "hour", 128963628.0, 3600000.0 },
22278 { 0, 3, "day", 5373485.0, 86400000.0 },
22279 { 1, 5, "month", 176546.0, 2592000000.0 },
22280 { 2, 4, "year", 14713.0, 31536000000.0 },
22281 };
22282
22283 /*
22284 ** Process a modifier to a date-time stamp. The modifiers are
22285 ** as follows:
@@ -27258,11 +27363,11 @@
27363 if( mem0.hardLimit>0 && (n>mem0.hardLimit || n==0) ){
27364 n = mem0.hardLimit;
27365 }
27366 mem0.alarmThreshold = n;
27367 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
27368 AtomicStore(&mem0.nearlyFull, n>0 && n<=nUsed);
27369 sqlite3_mutex_leave(mem0.mutex);
27370 excess = sqlite3_memory_used() - n;
27371 if( excess>0 ) sqlite3_release_memory((int)(excess & 0x7fffffff));
27372 return priorLimit;
27373 }
@@ -27326,11 +27431,11 @@
27431 ** Return true if the heap is currently under memory pressure - in other
27432 ** words if the amount of heap used is close to the limit set by
27433 ** sqlite3_soft_heap_limit().
27434 */
27435 SQLITE_PRIVATE int sqlite3HeapNearlyFull(void){
27436 return AtomicLoad(&mem0.nearlyFull);
27437 }
27438
27439 /*
27440 ** Deinitialize the memory allocation subsystem.
27441 */
@@ -27390,21 +27495,21 @@
27495
27496 sqlite3StatusHighwater(SQLITE_STATUS_MALLOC_SIZE, n);
27497 if( mem0.alarmThreshold>0 ){
27498 sqlite3_int64 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
27499 if( nUsed >= mem0.alarmThreshold - nFull ){
27500 AtomicStore(&mem0.nearlyFull, 1);
27501 sqlite3MallocAlarm(nFull);
27502 if( mem0.hardLimit ){
27503 nUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED);
27504 if( nUsed >= mem0.hardLimit - nFull ){
27505 *pp = 0;
27506 return;
27507 }
27508 }
27509 }else{
27510 AtomicStore(&mem0.nearlyFull, 0);
27511 }
27512 }
27513 p = sqlite3GlobalConfig.m.xMalloc(nFull);
27514 #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
27515 if( p==0 && mem0.alarmThreshold>0 ){
@@ -27629,14 +27734,16 @@
27734 if( nDiff>0 && sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED) >=
27735 mem0.alarmThreshold-nDiff ){
27736 sqlite3MallocAlarm(nDiff);
27737 }
27738 pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
27739 #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT
27740 if( pNew==0 && mem0.alarmThreshold>0 ){
27741 sqlite3MallocAlarm((int)nBytes);
27742 pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew);
27743 }
27744 #endif
27745 if( pNew ){
27746 nNew = sqlite3MallocSize(pNew);
27747 sqlite3StatusUp(SQLITE_STATUS_MEMORY_USED, nNew-nOld);
27748 }
27749 sqlite3_mutex_leave(mem0.mutex);
@@ -27907,11 +28014,11 @@
28014 */
28015 SQLITE_PRIVATE void sqlite3OomFault(sqlite3 *db){
28016 if( db->mallocFailed==0 && db->bBenignMalloc==0 ){
28017 db->mallocFailed = 1;
28018 if( db->nVdbeExec>0 ){
28019 AtomicStore(&db->u1.isInterrupted, 1);
28020 }
28021 DisableLookaside;
28022 if( db->pParse ){
28023 db->pParse->rc = SQLITE_NOMEM_BKPT;
28024 }
@@ -27926,11 +28033,11 @@
28033 ** VDBEs.
28034 */
28035 SQLITE_PRIVATE void sqlite3OomClear(sqlite3 *db){
28036 if( db->mallocFailed && db->nVdbeExec==0 ){
28037 db->mallocFailed = 0;
28038 AtomicStore(&db->u1.isInterrupted, 0);
28039 assert( db->lookaside.bDisable>0 );
28040 EnableLookaside;
28041 }
28042 }
28043
@@ -31300,10 +31407,23 @@
31407 a = (unsigned char *)zLeft;
31408 b = (unsigned char *)zRight;
31409 while( N-- > 0 && *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; }
31410 return N<0 ? 0 : UpperToLower[*a] - UpperToLower[*b];
31411 }
31412
31413 /*
31414 ** Compute an 8-bit hash on a string that is insensitive to case differences
31415 */
31416 SQLITE_PRIVATE u8 sqlite3StrIHash(const char *z){
31417 u8 h = 0;
31418 if( z==0 ) return 0;
31419 while( z[0] ){
31420 h += UpperToLower[(unsigned char)z[0]];
31421 z++;
31422 }
31423 return h;
31424 }
31425
31426 /*
31427 ** Compute 10 to the E-th power. Examples: E==1 results in 10.
31428 ** E==2 results in 100. E==50 results in 1.0e50.
31429 **
@@ -34857,20 +34977,21 @@
34977 static int osSetPosixAdvisoryLock(
34978 int h, /* The file descriptor on which to take the lock */
34979 struct flock *pLock, /* The description of the lock */
34980 unixFile *pFile /* Structure holding timeout value */
34981 ){
34982 int tm = pFile->iBusyTimeout;
34983 int rc = osFcntl(h,F_SETLK,pLock);
34984 while( rc<0 && tm>0 ){
34985 /* On systems that support some kind of blocking file lock with a timeout,
34986 ** make appropriate changes here to invoke that blocking file lock. On
34987 ** generic posix, however, there is no such API. So we simply try the
34988 ** lock once every millisecond until either the timeout expires, or until
34989 ** the lock is obtained. */
34990 usleep(1000);
34991 rc = osFcntl(h,F_SETLK,pLock);
34992 tm--;
34993 }
34994 return rc;
34995 }
34996 #endif /* SQLITE_ENABLE_SETLK_TIMEOUT */
34997
@@ -36977,11 +37098,11 @@
37098 zDirname[ii] = '\0';
37099 }else{
37100 if( zDirname[0]!='/' ) zDirname[0] = '.';
37101 zDirname[1] = 0;
37102 }
37103 fd = robust_open(zDirname, O_RDONLY|O_BINARY, 0);
37104 if( fd>=0 ){
37105 OSTRACE(("OPENDIR %-3d %s\n", fd, zDirname));
37106 }
37107 *pFd = fd;
37108 if( fd>=0 ) return SQLITE_OK;
@@ -37287,11 +37408,13 @@
37408 *(int*)pArg = fileHasMoved(pFile);
37409 return SQLITE_OK;
37410 }
37411 #ifdef SQLITE_ENABLE_SETLK_TIMEOUT
37412 case SQLITE_FCNTL_LOCK_TIMEOUT: {
37413 int iOld = pFile->iBusyTimeout;
37414 pFile->iBusyTimeout = *(int*)pArg;
37415 *(int*)pArg = iOld;
37416 return SQLITE_OK;
37417 }
37418 #endif
37419 #if SQLITE_MAX_MMAP_SIZE>0
37420 case SQLITE_FCNTL_MMAP_SIZE: {
@@ -37606,17 +37729,24 @@
37729
37730 /* Locks are within range */
37731 assert( n>=1 && n<=SQLITE_SHM_NLOCK );
37732
37733 if( pShmNode->hShm>=0 ){
37734 int res;
37735 /* Initialize the locking parameters */
37736 f.l_type = lockType;
37737 f.l_whence = SEEK_SET;
37738 f.l_start = ofst;
37739 f.l_len = n;
37740 res = osSetPosixAdvisoryLock(pShmNode->hShm, &f, pFile);
37741 if( res==-1 ){
37742 #ifdef SQLITE_ENABLE_SETLK_TIMEOUT
37743 rc = (pFile->iBusyTimeout ? SQLITE_BUSY_TIMEOUT : SQLITE_BUSY);
37744 #else
37745 rc = SQLITE_BUSY;
37746 #endif
37747 }
37748 }
37749
37750 /* Update the global lock state and do debug tracing */
37751 #ifdef SQLITE_DEBUG
37752 { u16 mask;
@@ -38108,10 +38238,29 @@
38238 || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_SHARED)
38239 || flags==(SQLITE_SHM_UNLOCK | SQLITE_SHM_EXCLUSIVE) );
38240 assert( n==1 || (flags & SQLITE_SHM_EXCLUSIVE)!=0 );
38241 assert( pShmNode->hShm>=0 || pDbFd->pInode->bProcessLock==1 );
38242 assert( pShmNode->hShm<0 || pDbFd->pInode->bProcessLock==0 );
38243
38244 /* Check that, if this to be a blocking lock, no locks that occur later
38245 ** in the following list than the lock being obtained are already held:
38246 **
38247 ** 1. Checkpointer lock (ofst==1).
38248 ** 2. Write lock (ofst==0).
38249 ** 3. Read locks (ofst>=3 && ofst<SQLITE_SHM_NLOCK).
38250 **
38251 ** In other words, if this is a blocking lock, none of the locks that
38252 ** occur later in the above list than the lock being obtained may be
38253 ** held. */
38254 #ifdef SQLITE_ENABLE_SETLK_TIMEOUT
38255 assert( (flags & SQLITE_SHM_UNLOCK) || pDbFd->iBusyTimeout==0 || (
38256 (ofst!=2) /* not RECOVER */
38257 && (ofst!=1 || (p->exclMask|p->sharedMask)==0)
38258 && (ofst!=0 || (p->exclMask|p->sharedMask)<3)
38259 && (ofst<3 || (p->exclMask|p->sharedMask)<(1<<ofst))
38260 ));
38261 #endif
38262
38263 mask = (1<<(ofst+n)) - (1<<ofst);
38264 assert( n>1 || mask==(1<<ofst) );
38265 sqlite3_mutex_enter(pShmNode->pShmMutex);
38266 if( flags & SQLITE_SHM_UNLOCK ){
@@ -51414,10 +51563,15 @@
51563 SQLITE_PRIVATE int sqlite3WalFramesize(Wal *pWal);
51564 #endif
51565
51566 /* Return the sqlite3_file object for the WAL file */
51567 SQLITE_PRIVATE sqlite3_file *sqlite3WalFile(Wal *pWal);
51568
51569 #ifdef SQLITE_ENABLE_SETLK_TIMEOUT
51570 SQLITE_PRIVATE int sqlite3WalWriteLock(Wal *pWal, int bLock);
51571 SQLITE_PRIVATE void sqlite3WalDb(Wal *pWal, sqlite3 *db);
51572 #endif
51573
51574 #endif /* ifndef SQLITE_OMIT_WAL */
51575 #endif /* SQLITE_WAL_H */
51576
51577 /************** End of wal.h *************************************************/
@@ -53935,13 +54089,16 @@
54089 }
54090 if( exists ){
54091 /* One of the journals pointed to by the master journal exists.
54092 ** Open it and check if it points at the master journal. If
54093 ** so, return without deleting the master journal file.
54094 ** NB: zJournal is really a MAIN_JOURNAL. But call it a
54095 ** MASTER_JOURNAL here so that the VFS will not send the zJournal
54096 ** name into sqlite3_database_file_object().
54097 */
54098 int c;
54099 int flags = (SQLITE_OPEN_READONLY|SQLITE_OPEN_MASTER_JOURNAL);
54100 rc = sqlite3OsOpen(pVfs, zJournal, pJournal, flags, 0);
54101 if( rc!=SQLITE_OK ){
54102 goto delmaster_out;
54103 }
54104
@@ -56141,10 +56298,11 @@
56298 ** Pager object (sizeof(Pager) bytes)
56299 ** PCache object (sqlite3PcacheSize() bytes)
56300 ** Database file handle (pVfs->szOsFile bytes)
56301 ** Sub-journal file handle (journalFileSize bytes)
56302 ** Main journal file handle (journalFileSize bytes)
56303 ** Ptr back to the Pager (sizeof(Pager*) bytes)
56304 ** \0\0\0\0 database prefix (4 bytes)
56305 ** Database file name (nPathname+1 bytes)
56306 ** URI query parameters (nUriByte bytes)
56307 ** Journal filename (nPathname+8+1 bytes)
56308 ** WAL filename (nPathname+4+1 bytes)
@@ -56180,10 +56338,11 @@
56338 pPtr = (u8 *)sqlite3MallocZero(
56339 ROUND8(sizeof(*pPager)) + /* Pager structure */
56340 ROUND8(pcacheSize) + /* PCache object */
56341 ROUND8(pVfs->szOsFile) + /* The main db file */
56342 journalFileSize * 2 + /* The two journal files */
56343 sizeof(pPager) + /* Space to hold a pointer */
56344 4 + /* Database prefix */
56345 nPathname + 1 + /* database filename */
56346 nUriByte + /* query parameters */
56347 nPathname + 8 + 1 + /* Journal filename */
56348 #ifndef SQLITE_OMIT_WAL
@@ -56200,10 +56359,11 @@
56359 pPager->pPCache = (PCache*)pPtr; pPtr += ROUND8(pcacheSize);
56360 pPager->fd = (sqlite3_file*)pPtr; pPtr += ROUND8(pVfs->szOsFile);
56361 pPager->sjfd = (sqlite3_file*)pPtr; pPtr += journalFileSize;
56362 pPager->jfd = (sqlite3_file*)pPtr; pPtr += journalFileSize;
56363 assert( EIGHT_BYTE_ALIGNMENT(pPager->jfd) );
56364 memcpy(pPtr, &pPager, sizeof(pPager)); pPtr += sizeof(pPager);
56365
56366 /* Fill in the Pager.zFilename and pPager.zQueryParam fields */
56367 pPtr += 4; /* Skip zero prefix */
56368 pPager->zFilename = (char*)pPtr;
56369 if( nPathname>0 ){
@@ -56400,10 +56560,23 @@
56560
56561 *ppPager = pPager;
56562 return SQLITE_OK;
56563 }
56564
56565 /*
56566 ** Return the sqlite3_file for the main database given the name
56567 ** of the corresonding WAL or Journal name as passed into
56568 ** xOpen.
56569 */
56570 SQLITE_API sqlite3_file *sqlite3_database_file_object(const char *zName){
56571 Pager *pPager;
56572 while( zName[-1]!=0 || zName[-2]!=0 || zName[-3]!=0 || zName[-4]!=0 ){
56573 zName--;
56574 }
56575 pPager = *(Pager**)(zName - 4 - sizeof(Pager*));
56576 return pPager->fd;
56577 }
56578
56579
56580 /*
56581 ** This function is called after transitioning from PAGER_UNLOCK to
56582 ** PAGER_SHARED state. It tests if there is a hot journal present in
@@ -57085,11 +57258,10 @@
57258 Pager *pPager;
57259 assert( pPg!=0 );
57260 assert( pPg->pgno==1 );
57261 assert( (pPg->flags & PGHDR_MMAP)==0 ); /* Page1 is never memory mapped */
57262 pPager = pPg->pPager;
 
57263 sqlite3PcacheRelease(pPg);
57264 pagerUnlockIfUnused(pPager);
57265 }
57266
57267 /*
@@ -58378,20 +58550,10 @@
58550 */
58551 SQLITE_PRIVATE sqlite3_file *sqlite3PagerFile(Pager *pPager){
58552 return pPager->fd;
58553 }
58554
 
 
 
 
 
 
 
 
 
 
58555 /*
58556 ** Return the file handle for the journal file (if it exists).
58557 ** This will be either the rollback journal or the WAL file.
58558 */
58559 SQLITE_PRIVATE sqlite3_file *sqlite3PagerJrnlFile(Pager *pPager){
@@ -58801,11 +58963,10 @@
58963 (eMode==SQLITE_CHECKPOINT_PASSIVE ? 0 : pPager->xBusyHandler),
58964 pPager->pBusyHandlerArg,
58965 pPager->walSyncFlags, pPager->pageSize, (u8 *)pPager->pTmpSpace,
58966 pnLog, pnCkpt
58967 );
 
58968 }
58969 return rc;
58970 }
58971
58972 SQLITE_PRIVATE int sqlite3PagerWalCallback(Pager *pPager){
@@ -58966,11 +59127,35 @@
59127 }
59128 }
59129 return rc;
59130 }
59131
59132 #ifdef SQLITE_ENABLE_SETLK_TIMEOUT
59133 /*
59134 ** If pager pPager is a wal-mode database not in exclusive locking mode,
59135 ** invoke the sqlite3WalWriteLock() function on the associated Wal object
59136 ** with the same db and bLock parameters as were passed to this function.
59137 ** Return an SQLite error code if an error occurs, or SQLITE_OK otherwise.
59138 */
59139 SQLITE_PRIVATE int sqlite3PagerWalWriteLock(Pager *pPager, int bLock){
59140 int rc = SQLITE_OK;
59141 if( pagerUseWal(pPager) && pPager->exclusiveMode==0 ){
59142 rc = sqlite3WalWriteLock(pPager->pWal, bLock);
59143 }
59144 return rc;
59145 }
59146
59147 /*
59148 ** Set the database handle used by the wal layer to determine if
59149 ** blocking locks are required.
59150 */
59151 SQLITE_PRIVATE void sqlite3PagerWalDb(Pager *pPager, sqlite3 *db){
59152 if( pagerUseWal(pPager) ){
59153 sqlite3WalDb(pPager->pWal, db);
59154 }
59155 }
59156 #endif
59157
59158 #ifdef SQLITE_ENABLE_SNAPSHOT
59159 /*
59160 ** If this is a WAL database, obtain a snapshot handle for the snapshot
59161 ** currently open. Otherwise, return an error.
@@ -58986,11 +59171,14 @@
59171 /*
59172 ** If this is a WAL database, store a pointer to pSnapshot. Next time a
59173 ** read transaction is opened, attempt to read from the snapshot it
59174 ** identifies. If this is not a WAL database, return an error.
59175 */
59176 SQLITE_PRIVATE int sqlite3PagerSnapshotOpen(
59177 Pager *pPager,
59178 sqlite3_snapshot *pSnapshot
59179 ){
59180 int rc = SQLITE_OK;
59181 if( pPager->pWal ){
59182 sqlite3WalSnapshotOpen(pPager->pWal, pSnapshot);
59183 }else{
59184 rc = SQLITE_ERROR;
@@ -59322,22 +59510,10 @@
59510 # define WALTRACE(X) if(sqlite3WalTrace) sqlite3DebugPrintf X
59511 #else
59512 # define WALTRACE(X)
59513 #endif
59514
 
 
 
 
 
 
 
 
 
 
 
 
59515 /*
59516 ** The maximum (and only) versions of the wal and wal-index formats
59517 ** that may be interpreted by this version of SQLite.
59518 **
59519 ** If a client begins recovering a WAL file and finds that (a) the checksum
@@ -59542,10 +59718,13 @@
59718 #ifdef SQLITE_DEBUG
59719 u8 lockError; /* True if a locking error has occurred */
59720 #endif
59721 #ifdef SQLITE_ENABLE_SNAPSHOT
59722 WalIndexHdr *pSnapshot; /* Start transaction here if not NULL */
59723 #endif
59724 #ifdef SQLITE_ENABLE_SETLK_TIMEOUT
59725 sqlite3 *db;
59726 #endif
59727 };
59728
59729 /*
59730 ** Candidate values for Wal.exclusiveMode.
@@ -59916,11 +60095,11 @@
60095 if( pWal->exclusiveMode ) return SQLITE_OK;
60096 rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1,
60097 SQLITE_SHM_LOCK | SQLITE_SHM_SHARED);
60098 WALTRACE(("WAL%p: acquire SHARED-%s %s\n", pWal,
60099 walLockName(lockIdx), rc ? "failed" : "ok"));
60100 VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); )
60101 return rc;
60102 }
60103 static void walUnlockShared(Wal *pWal, int lockIdx){
60104 if( pWal->exclusiveMode ) return;
60105 (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, 1,
@@ -59932,11 +60111,11 @@
60111 if( pWal->exclusiveMode ) return SQLITE_OK;
60112 rc = sqlite3OsShmLock(pWal->pDbFd, lockIdx, n,
60113 SQLITE_SHM_LOCK | SQLITE_SHM_EXCLUSIVE);
60114 WALTRACE(("WAL%p: acquire EXCLUSIVE-%s cnt=%d %s\n", pWal,
60115 walLockName(lockIdx), n, rc ? "failed" : "ok"));
60116 VVA_ONLY( pWal->lockError = (u8)(rc!=SQLITE_OK && (rc&0xFF)!=SQLITE_BUSY); )
60117 return rc;
60118 }
60119 static void walUnlockExclusive(Wal *pWal, int lockIdx, int n){
60120 if( pWal->exclusiveMode ) return;
60121 (void)sqlite3OsShmLock(pWal->pDbFd, lockIdx, n,
@@ -60751,10 +60930,93 @@
60930 p = 0;
60931 }
60932 *pp = p;
60933 return rc;
60934 }
60935
60936 #ifdef SQLITE_ENABLE_SETLK_TIMEOUT
60937 /*
60938 ** Attempt to enable blocking locks. Blocking locks are enabled only if (a)
60939 ** they are supported by the VFS, and (b) the database handle is configured
60940 ** with a busy-timeout. Return 1 if blocking locks are successfully enabled,
60941 ** or 0 otherwise.
60942 */
60943 static int walEnableBlocking(Wal *pWal){
60944 int res = 0;
60945 if( pWal->db ){
60946 int tmout = pWal->db->busyTimeout;
60947 if( tmout ){
60948 int rc;
60949 rc = sqlite3OsFileControl(
60950 pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout
60951 );
60952 res = (rc==SQLITE_OK);
60953 }
60954 }
60955 return res;
60956 }
60957
60958 /*
60959 ** Disable blocking locks.
60960 */
60961 static void walDisableBlocking(Wal *pWal){
60962 int tmout = 0;
60963 sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_LOCK_TIMEOUT, (void*)&tmout);
60964 }
60965
60966 /*
60967 ** If parameter bLock is true, attempt to enable blocking locks, take
60968 ** the WRITER lock, and then disable blocking locks. If blocking locks
60969 ** cannot be enabled, no attempt to obtain the WRITER lock is made. Return
60970 ** an SQLite error code if an error occurs, or SQLITE_OK otherwise. It is not
60971 ** an error if blocking locks can not be enabled.
60972 **
60973 ** If the bLock parameter is false and the WRITER lock is held, release it.
60974 */
60975 SQLITE_PRIVATE int sqlite3WalWriteLock(Wal *pWal, int bLock){
60976 int rc = SQLITE_OK;
60977 assert( pWal->readLock<0 || bLock==0 );
60978 if( bLock ){
60979 assert( pWal->db );
60980 if( walEnableBlocking(pWal) ){
60981 rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1);
60982 if( rc==SQLITE_OK ){
60983 pWal->writeLock = 1;
60984 }
60985 walDisableBlocking(pWal);
60986 }
60987 }else if( pWal->writeLock ){
60988 walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
60989 pWal->writeLock = 0;
60990 }
60991 return rc;
60992 }
60993
60994 /*
60995 ** Set the database handle used to determine if blocking locks are required.
60996 */
60997 SQLITE_PRIVATE void sqlite3WalDb(Wal *pWal, sqlite3 *db){
60998 pWal->db = db;
60999 }
61000
61001 /*
61002 ** Take an exclusive WRITE lock. Blocking if so configured.
61003 */
61004 static int walLockWriter(Wal *pWal){
61005 int rc;
61006 walEnableBlocking(pWal);
61007 rc = walLockExclusive(pWal, WAL_WRITE_LOCK, 1);
61008 walDisableBlocking(pWal);
61009 return rc;
61010 }
61011 #else
61012 # define walEnableBlocking(x) 0
61013 # define walDisableBlocking(x)
61014 # define walLockWriter(pWal) walLockExclusive((pWal), WAL_WRITE_LOCK, 1)
61015 # define sqlite3WalDb(pWal, db)
61016 #endif /* ifdef SQLITE_ENABLE_SETLK_TIMEOUT */
61017
61018
61019 /*
61020 ** Attempt to obtain the exclusive WAL lock defined by parameters lockIdx and
61021 ** n. If the attempt fails and parameter xBusy is not NULL, then it is a
61022 ** busy-handler function. Invoke it and retry the lock until either the
@@ -60769,10 +61031,16 @@
61031 ){
61032 int rc;
61033 do {
61034 rc = walLockExclusive(pWal, lockIdx, n);
61035 }while( xBusy && rc==SQLITE_BUSY && xBusy(pBusyArg) );
61036 #ifdef SQLITE_ENABLE_SETLK_TIMEOUT
61037 if( rc==SQLITE_BUSY_TIMEOUT ){
61038 walDisableBlocking(pWal);
61039 rc = SQLITE_BUSY;
61040 }
61041 #endif
61042 return rc;
61043 }
61044
61045 /*
61046 ** The cache of the wal-index header must be valid to call this function.
@@ -60939,10 +61207,11 @@
61207 ** about the eventual size of the db file to the VFS layer.
61208 */
61209 if( rc==SQLITE_OK ){
61210 i64 nReq = ((i64)mxPage * szPage);
61211 i64 nSize; /* Current size of database file */
61212 sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_START, 0);
61213 rc = sqlite3OsFileSize(pWal->pDbFd, &nSize);
61214 if( rc==SQLITE_OK && nSize<nReq ){
61215 sqlite3OsFileControlHint(pWal->pDbFd, SQLITE_FCNTL_SIZE_HINT, &nReq);
61216 }
61217 }
@@ -60950,11 +61219,11 @@
61219
61220 /* Iterate through the contents of the WAL, copying data to the db file */
61221 while( rc==SQLITE_OK && 0==walIteratorNext(pIter, &iDbpage, &iFrame) ){
61222 i64 iOffset;
61223 assert( walFramePgno(pWal, iFrame)==iDbpage );
61224 if( AtomicLoad(&db->u1.isInterrupted) ){
61225 rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT;
61226 break;
61227 }
61228 if( iFrame<=nBackfill || iFrame>mxSafeFrame || iDbpage>mxPage ){
61229 continue;
@@ -60966,10 +61235,11 @@
61235 iOffset = (iDbpage-1)*(i64)szPage;
61236 testcase( IS_BIG_INT(iOffset) );
61237 rc = sqlite3OsWrite(pWal->pDbFd, zBuf, szPage, iOffset);
61238 if( rc!=SQLITE_OK ) break;
61239 }
61240 sqlite3OsFileControl(pWal->pDbFd, SQLITE_FCNTL_CKPT_DONE, 0);
61241
61242 /* If work was actually accomplished... */
61243 if( rc==SQLITE_OK ){
61244 if( mxSafeFrame==walIndexHdr(pWal)->mxFrame ){
61245 i64 szDb = pWal->hdr.nPage*(i64)szPage;
@@ -60977,14 +61247,10 @@
61247 rc = sqlite3OsTruncate(pWal->pDbFd, szDb);
61248 if( rc==SQLITE_OK ){
61249 rc = sqlite3OsSync(pWal->pDbFd, CKPT_SYNC_FLAGS(sync_flags));
61250 }
61251 }
 
 
 
 
61252 if( rc==SQLITE_OK ){
61253 pInfo->nBackfill = mxSafeFrame;
61254 }
61255 }
61256
@@ -61250,32 +61516,36 @@
61516 badHdr = (page0 ? walIndexTryHdr(pWal, pChanged) : 1);
61517
61518 /* If the first attempt failed, it might have been due to a race
61519 ** with a writer. So get a WRITE lock and try again.
61520 */
 
61521 if( badHdr ){
61522 if( pWal->bShmUnreliable==0 && (pWal->readOnly & WAL_SHM_RDONLY) ){
61523 if( SQLITE_OK==(rc = walLockShared(pWal, WAL_WRITE_LOCK)) ){
61524 walUnlockShared(pWal, WAL_WRITE_LOCK);
61525 rc = SQLITE_READONLY_RECOVERY;
61526 }
61527 }else{
61528 int bWriteLock = pWal->writeLock;
61529 if( bWriteLock || SQLITE_OK==(rc = walLockWriter(pWal)) ){
61530 pWal->writeLock = 1;
61531 if( SQLITE_OK==(rc = walIndexPage(pWal, 0, &page0)) ){
61532 badHdr = walIndexTryHdr(pWal, pChanged);
61533 if( badHdr ){
61534 /* If the wal-index header is still malformed even while holding
61535 ** a WRITE lock, it can only mean that the header is corrupted and
61536 ** needs to be reconstructed. So run recovery to do exactly that.
61537 */
61538 rc = walIndexRecover(pWal);
61539 *pChanged = 1;
61540 }
61541 }
61542 if( bWriteLock==0 ){
61543 pWal->writeLock = 0;
61544 walUnlockExclusive(pWal, WAL_WRITE_LOCK, 1);
61545 }
61546 }
61547 }
61548 }
61549
61550 /* If the header is read successfully, check the version number to make
61551 ** sure the wal-index was not constructed with some future format that
@@ -61663,11 +61933,12 @@
61933 && (mxReadMark<mxFrame || mxI==0)
61934 ){
61935 for(i=1; i<WAL_NREADER; i++){
61936 rc = walLockExclusive(pWal, WAL_READ_LOCK(i), 1);
61937 if( rc==SQLITE_OK ){
61938 AtomicStore(pInfo->aReadMark+i,mxFrame);
61939 mxReadMark = mxFrame;
61940 mxI = i;
61941 walUnlockExclusive(pWal, WAL_READ_LOCK(i), 1);
61942 break;
61943 }else if( rc!=SQLITE_BUSY ){
61944 return rc;
@@ -61823,15 +62094,36 @@
62094 */
62095 SQLITE_PRIVATE int sqlite3WalBeginReadTransaction(Wal *pWal, int *pChanged){
62096 int rc; /* Return code */
62097 int cnt = 0; /* Number of TryBeginRead attempts */
62098
62099 assert( pWal->ckptLock==0 );
62100
62101 #ifdef SQLITE_ENABLE_SNAPSHOT
62102 int bChanged = 0;
62103 WalIndexHdr *pSnapshot = pWal->pSnapshot;
62104 if( pSnapshot ){
62105 if( memcmp(pSnapshot, &pWal->hdr, sizeof(WalIndexHdr))!=0 ){
62106 bChanged = 1;
62107 }
62108
62109 /* It is possible that there is a checkpointer thread running
62110 ** concurrent with this code. If this is the case, it may be that the
62111 ** checkpointer has already determined that it will checkpoint
62112 ** snapshot X, where X is later in the wal file than pSnapshot, but
62113 ** has not yet set the pInfo->nBackfillAttempted variable to indicate
62114 ** its intent. To avoid the race condition this leads to, ensure that
62115 ** there is no checkpointer process by taking a shared CKPT lock
62116 ** before checking pInfo->nBackfillAttempted. */
62117 (void)walEnableBlocking(pWal);
62118 rc = walLockShared(pWal, WAL_CKPT_LOCK);
62119 walDisableBlocking(pWal);
62120
62121 if( rc!=SQLITE_OK ){
62122 return rc;
62123 }
62124 pWal->ckptLock = 1;
62125 }
62126 #endif
62127
62128 do{
62129 rc = walTryBeginRead(pWal, pChanged, 0, ++cnt);
@@ -61860,52 +62152,46 @@
62152 volatile WalCkptInfo *pInfo = walCkptInfo(pWal);
62153
62154 assert( pWal->readLock>0 || pWal->hdr.mxFrame==0 );
62155 assert( pInfo->aReadMark[pWal->readLock]<=pSnapshot->mxFrame );
62156
62157 /* Check that the wal file has not been wrapped. Assuming that it has
62158 ** not, also check that no checkpointer has attempted to checkpoint any
62159 ** frames beyond pSnapshot->mxFrame. If either of these conditions are
62160 ** true, return SQLITE_ERROR_SNAPSHOT. Otherwise, overwrite pWal->hdr
62161 ** with *pSnapshot and set *pChanged as appropriate for opening the
62162 ** snapshot. */
62163 if( !memcmp(pSnapshot->aSalt, pWal->hdr.aSalt, sizeof(pWal->hdr.aSalt))
62164 && pSnapshot->mxFrame>=pInfo->nBackfillAttempted
62165 ){
62166 assert( pWal->readLock>0 );
62167 memcpy(&pWal->hdr, pSnapshot, sizeof(WalIndexHdr));
62168 *pChanged = bChanged;
62169 }else{
62170 rc = SQLITE_ERROR_SNAPSHOT;
62171 }
62172
62173 /* A client using a non-current snapshot may not ignore any frames
62174 ** from the start of the wal file. This is because, for a system
62175 ** where (minFrame < iSnapshot < maxFrame), a checkpointer may
62176 ** have omitted to checkpoint a frame earlier than minFrame in
62177 ** the file because there exists a frame after iSnapshot that
62178 ** is the same database page. */
62179 pWal->minFrame = 1;
 
 
 
 
 
 
 
 
 
 
 
 
 
62180
62181 if( rc!=SQLITE_OK ){
62182 sqlite3WalEndReadTransaction(pWal);
62183 }
62184 }
62185 }
62186
62187 /* Release the shared CKPT lock obtained above. */
62188 if( pWal->ckptLock ){
62189 assert( pSnapshot );
62190 walUnlockShared(pWal, WAL_CKPT_LOCK);
62191 pWal->ckptLock = 0;
62192 }
62193 #endif
62194 return rc;
62195 }
62196
62197 /*
@@ -62071,10 +62357,20 @@
62357 **
62358 ** There can only be a single writer active at a time.
62359 */
62360 SQLITE_PRIVATE int sqlite3WalBeginWriteTransaction(Wal *pWal){
62361 int rc;
62362
62363 #ifdef SQLITE_ENABLE_SETLK_TIMEOUT
62364 /* If the write-lock is already held, then it was obtained before the
62365 ** read-transaction was even opened, making this call a no-op.
62366 ** Return early. */
62367 if( pWal->writeLock ){
62368 assert( !memcmp(&pWal->hdr,(void *)walIndexHdr(pWal),sizeof(WalIndexHdr)) );
62369 return SQLITE_OK;
62370 }
62371 #endif
62372
62373 /* Cannot start a write transaction without first holding a read
62374 ** transaction. */
62375 assert( pWal->readLock>=0 );
62376 assert( pWal->writeLock==0 && pWal->iReCksum==0 );
@@ -62647,50 +62943,57 @@
62943 ** in the SQLITE_CHECKPOINT_PASSIVE mode. */
62944 assert( eMode!=SQLITE_CHECKPOINT_PASSIVE || xBusy==0 );
62945
62946 if( pWal->readOnly ) return SQLITE_READONLY;
62947 WALTRACE(("WAL%p: checkpoint begins\n", pWal));
62948
62949 /* Enable blocking locks, if possible. If blocking locks are successfully
62950 ** enabled, set xBusy2=0 so that the busy-handler is never invoked. */
62951 sqlite3WalDb(pWal, db);
62952 (void)walEnableBlocking(pWal);
62953
62954 /* IMPLEMENTATION-OF: R-62028-47212 All calls obtain an exclusive
62955 ** "checkpoint" lock on the database file.
62956 ** EVIDENCE-OF: R-10421-19736 If any other process is running a
62957 ** checkpoint operation at the same time, the lock cannot be obtained and
62958 ** SQLITE_BUSY is returned.
62959 ** EVIDENCE-OF: R-53820-33897 Even if there is a busy-handler configured,
62960 ** it will not be invoked in this case.
62961 */
62962 rc = walLockExclusive(pWal, WAL_CKPT_LOCK, 1);
62963 testcase( rc==SQLITE_BUSY );
62964 testcase( rc!=SQLITE_OK && xBusy2!=0 );
62965 if( rc==SQLITE_OK ){
62966 pWal->ckptLock = 1;
62967
62968 /* IMPLEMENTATION-OF: R-59782-36818 The SQLITE_CHECKPOINT_FULL, RESTART and
62969 ** TRUNCATE modes also obtain the exclusive "writer" lock on the database
62970 ** file.
62971 **
62972 ** EVIDENCE-OF: R-60642-04082 If the writer lock cannot be obtained
62973 ** immediately, and a busy-handler is configured, it is invoked and the
62974 ** writer lock retried until either the busy-handler returns 0 or the
62975 ** lock is successfully obtained.
62976 */
62977 if( eMode!=SQLITE_CHECKPOINT_PASSIVE ){
62978 rc = walBusyLock(pWal, xBusy2, pBusyArg, WAL_WRITE_LOCK, 1);
62979 if( rc==SQLITE_OK ){
62980 pWal->writeLock = 1;
62981 }else if( rc==SQLITE_BUSY ){
62982 eMode2 = SQLITE_CHECKPOINT_PASSIVE;
62983 xBusy2 = 0;
62984 rc = SQLITE_OK;
62985 }
62986 }
62987 }
62988
 
 
 
 
 
 
62989
62990 /* Read the wal-index header. */
62991 if( rc==SQLITE_OK ){
62992 walDisableBlocking(pWal);
62993 rc = walIndexReadHdr(pWal, &isChanged);
62994 (void)walEnableBlocking(pWal);
62995 if( isChanged && pWal->pDbFd->pMethods->iVersion>=3 ){
62996 sqlite3OsUnfetch(pWal->pDbFd, 0, 0);
62997 }
62998 }
62999
@@ -62717,16 +63020,24 @@
63020 ** next time the pager opens a snapshot on this database it knows that
63021 ** the cache needs to be reset.
63022 */
63023 memset(&pWal->hdr, 0, sizeof(WalIndexHdr));
63024 }
63025
63026 walDisableBlocking(pWal);
63027 sqlite3WalDb(pWal, 0);
63028
63029 /* Release the locks. */
63030 sqlite3WalEndWriteTransaction(pWal);
63031 if( pWal->ckptLock ){
63032 walUnlockExclusive(pWal, WAL_CKPT_LOCK, 1);
63033 pWal->ckptLock = 0;
63034 }
63035 WALTRACE(("WAL%p: checkpoint %s\n", pWal, rc ? "failed" : "ok"));
63036 #ifdef SQLITE_ENABLE_SETLK_TIMEOUT
63037 if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY;
63038 #endif
63039 return (rc==SQLITE_OK && eMode!=eMode2 ? SQLITE_BUSY : rc);
63040 }
63041
63042 /* Return the value to pass to a sqlite3_wal_hook callback, the
63043 ** number of frames in the WAL at the point of the last commit since
@@ -62839,11 +63150,14 @@
63150 return rc;
63151 }
63152
63153 /* Try to open on pSnapshot when the next read-transaction starts
63154 */
63155 SQLITE_PRIVATE void sqlite3WalSnapshotOpen(
63156 Wal *pWal,
63157 sqlite3_snapshot *pSnapshot
63158 ){
63159 pWal->pSnapshot = (WalIndexHdr*)pSnapshot;
63160 }
63161
63162 /*
63163 ** Return a +ve value if snapshot p1 is newer than p2. A -ve value if
@@ -63358,10 +63672,11 @@
63672 u8 incrVacuum; /* True if incr-vacuum is enabled */
63673 u8 bDoTruncate; /* True to truncate db on commit */
63674 #endif
63675 u8 inTransaction; /* Transaction state */
63676 u8 max1bytePayload; /* Maximum first byte of cell for a 1-byte payload */
63677 u8 nReserveWanted; /* Desired number of extra bytes per page */
63678 u16 btsFlags; /* Boolean parameters. See BTS_* macros below */
63679 u16 maxLocal; /* Maximum local payload in non-LEAFDATA tables */
63680 u16 minLocal; /* Minimum local payload in non-LEAFDATA tables */
63681 u16 maxLeaf; /* Maximum local payload in a LEAFDATA table */
63682 u16 minLeaf; /* Minimum local payload in a LEAFDATA table */
@@ -66251,12 +66566,11 @@
66566 */
66567 static int btreeInvokeBusyHandler(void *pArg){
66568 BtShared *pBt = (BtShared*)pArg;
66569 assert( pBt->db );
66570 assert( sqlite3_mutex_held(pBt->db->mutex) );
66571 return sqlite3InvokeBusyHandler(&pBt->db->busyHandler);
 
66572 }
66573
66574 /*
66575 ** Open a database file.
66576 **
@@ -66803,20 +67117,21 @@
67117 ** If the iFix!=0 then the BTS_PAGESIZE_FIXED flag is set so that the page size
67118 ** and autovacuum mode can no longer be changed.
67119 */
67120 SQLITE_PRIVATE int sqlite3BtreeSetPageSize(Btree *p, int pageSize, int nReserve, int iFix){
67121 int rc = SQLITE_OK;
67122 int x;
67123 BtShared *pBt = p->pBt;
67124 assert( nReserve>=0 && nReserve<=255 );
67125 sqlite3BtreeEnter(p);
67126 pBt->nReserveWanted = nReserve;
67127 x = pBt->pageSize - pBt->usableSize;
67128 if( nReserve<x ) nReserve = x;
67129 if( pBt->btsFlags & BTS_PAGESIZE_FIXED ){
67130 sqlite3BtreeLeave(p);
67131 return SQLITE_READONLY;
67132 }
 
 
 
67133 assert( nReserve>=0 && nReserve<=255 );
67134 if( pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE &&
67135 ((pageSize-1)&pageSize)==0 ){
67136 assert( (pageSize & 7)==0 );
67137 assert( !pBt->pCursor );
@@ -66858,20 +67173,21 @@
67173 /*
67174 ** Return the number of bytes of space at the end of every page that
67175 ** are intentually left unused. This is the "reserved" space that is
67176 ** sometimes used by extensions.
67177 **
67178 ** The value returned is the larger of the current reserve size and
67179 ** the latest reserve size requested by SQLITE_FILECTRL_RESERVE_BYTES.
67180 ** The amount of reserve can only grow - never shrink.
67181 */
67182 SQLITE_PRIVATE int sqlite3BtreeGetRequestedReserve(Btree *p){
67183 int n1, n2;
67184 sqlite3BtreeEnter(p);
67185 n1 = (int)p->pBt->nReserveWanted;
67186 n2 = sqlite3BtreeGetReserveNoMutex(p);
67187 sqlite3BtreeLeave(p);
67188 return n1>n2 ? n1 : n2;
67189 }
67190
67191
67192 /*
67193 ** Set the maximum page count for a database if mxPage is positive.
@@ -67317,10 +67633,11 @@
67633 ** when A already has a read lock, we encourage A to give up and let B
67634 ** proceed.
67635 */
67636 SQLITE_PRIVATE int sqlite3BtreeBeginTrans(Btree *p, int wrflag, int *pSchemaVersion){
67637 BtShared *pBt = p->pBt;
67638 Pager *pPager = pBt->pPager;
67639 int rc = SQLITE_OK;
67640
67641 sqlite3BtreeEnter(p);
67642 btreeIntegrity(p);
67643
@@ -67332,11 +67649,11 @@
67649 goto trans_begun;
67650 }
67651 assert( pBt->inTransaction==TRANS_WRITE || IfNotOmitAV(pBt->bDoTruncate)==0 );
67652
67653 if( (p->db->flags & SQLITE_ResetDatabase)
67654 && sqlite3PagerIsreadonly(pPager)==0
67655 ){
67656 pBt->btsFlags &= ~BTS_READ_ONLY;
67657 }
67658
67659 /* Write transactions are not possible on a read-only database */
@@ -67380,10 +67697,22 @@
67697 if( SQLITE_OK!=rc ) goto trans_begun;
67698
67699 pBt->btsFlags &= ~BTS_INITIALLY_EMPTY;
67700 if( pBt->nPage==0 ) pBt->btsFlags |= BTS_INITIALLY_EMPTY;
67701 do {
67702 sqlite3PagerWalDb(pPager, p->db);
67703
67704 #ifdef SQLITE_ENABLE_SETLK_TIMEOUT
67705 /* If transitioning from no transaction directly to a write transaction,
67706 ** block for the WRITER lock first if possible. */
67707 if( pBt->pPage1==0 && wrflag ){
67708 assert( pBt->inTransaction==TRANS_NONE );
67709 rc = sqlite3PagerWalWriteLock(pPager, 1);
67710 if( rc!=SQLITE_BUSY && rc!=SQLITE_OK ) break;
67711 }
67712 #endif
67713
67714 /* Call lockBtree() until either pBt->pPage1 is populated or
67715 ** lockBtree() returns something other than SQLITE_OK. lockBtree()
67716 ** may return SQLITE_OK but leave pBt->pPage1 set to 0 if after
67717 ** reading page 1 it discovers that the page-size of the database
67718 ** file is not pBt->pageSize. In this case lockBtree() will update
@@ -67393,11 +67722,11 @@
67722
67723 if( rc==SQLITE_OK && wrflag ){
67724 if( (pBt->btsFlags & BTS_READ_ONLY)!=0 ){
67725 rc = SQLITE_READONLY;
67726 }else{
67727 rc = sqlite3PagerBegin(pPager, wrflag>1, sqlite3TempInMemory(p->db));
67728 if( rc==SQLITE_OK ){
67729 rc = newDatabase(pBt);
67730 }else if( rc==SQLITE_BUSY_SNAPSHOT && pBt->inTransaction==TRANS_NONE ){
67731 /* if there was no transaction opened when this function was
67732 ** called and SQLITE_BUSY_SNAPSHOT is returned, change the error
@@ -67406,15 +67735,19 @@
67735 }
67736 }
67737 }
67738
67739 if( rc!=SQLITE_OK ){
67740 (void)sqlite3PagerWalWriteLock(pPager, 0);
67741 unlockBtreeIfUnused(pBt);
67742 }
67743 }while( (rc&0xFF)==SQLITE_BUSY && pBt->inTransaction==TRANS_NONE &&
67744 btreeInvokeBusyHandler(pBt) );
67745 sqlite3PagerWalDb(pPager, 0);
67746 #ifdef SQLITE_ENABLE_SETLK_TIMEOUT
67747 if( rc==SQLITE_BUSY_TIMEOUT ) rc = SQLITE_BUSY;
67748 #endif
67749
67750 if( rc==SQLITE_OK ){
67751 if( p->inTrans==TRANS_NONE ){
67752 pBt->nTransaction++;
67753 #ifndef SQLITE_OMIT_SHARED_CACHE
@@ -67462,11 +67795,11 @@
67795 if( wrflag ){
67796 /* This call makes sure that the pager has the correct number of
67797 ** open savepoints. If the second parameter is greater than 0 and
67798 ** the sub-journal is not already open, then it will be opened here.
67799 */
67800 rc = sqlite3PagerOpenSavepoint(pPager, p->db->nSavepoint);
67801 }
67802 }
67803
67804 btreeIntegrity(p);
67805 sqlite3BtreeLeave(p);
@@ -71098,11 +71431,11 @@
71431
71432 /* Remove cells from the start and end of the page */
71433 assert( nCell>=0 );
71434 if( iOld<iNew ){
71435 int nShift = pageFreeArray(pPg, iOld, iNew-iOld, pCArray);
71436 if( NEVER(nShift>nCell) ) return SQLITE_CORRUPT_BKPT;
71437 memmove(pPg->aCellIdx, &pPg->aCellIdx[nShift*2], nCell*2);
71438 nCell -= nShift;
71439 }
71440 if( iNewEnd < iOldEnd ){
71441 int nTail = pageFreeArray(pPg, iNewEnd, iOldEnd - iNewEnd, pCArray);
@@ -73455,11 +73788,10 @@
73788 }
73789 sqlite3BtreeLeave(p);
73790 return rc;
73791 }
73792
 
73793 /*
73794 ** The first argument, pCur, is a cursor opened on some b-tree. Count the
73795 ** number of entries in the b-tree and write the result to *pnEntry.
73796 **
73797 ** SQLITE_OK is returned if the operation is successfully executed.
@@ -73477,11 +73809,11 @@
73809 }
73810
73811 /* Unless an error occurs, the following loop runs one iteration for each
73812 ** page in the B-Tree structure (not including overflow pages).
73813 */
73814 while( rc==SQLITE_OK && !AtomicLoad(&db->u1.isInterrupted) ){
73815 int iIdx; /* Index of child node in parent */
73816 MemPage *pPage; /* Current page of the b-tree */
73817
73818 /* If this is a leaf page or the tree is not an int-key tree, then
73819 ** this page contains countable entries. Increment the entry counter
@@ -73528,11 +73860,10 @@
73860 }
73861
73862 /* An error has occurred. Return an error code. */
73863 return rc;
73864 }
 
73865
73866 /*
73867 ** Return the pager associated with a BTree. This routine is used for
73868 ** testing and debugging only.
73869 */
@@ -73603,11 +73934,11 @@
73934 }
73935 if( getPageReferenced(pCheck, iPage) ){
73936 checkAppendMsg(pCheck, "2nd reference to page %d", iPage);
73937 return 1;
73938 }
73939 if( AtomicLoad(&pCheck->db->u1.isInterrupted) ) return 1;
73940 setPageReferenced(pCheck, iPage);
73941 return 0;
73942 }
73943
73944 #ifndef SQLITE_OMIT_AUTOVACUUM
@@ -74579,11 +74910,11 @@
74910 ** Attempt to set the page size of the destination to match the page size
74911 ** of the source.
74912 */
74913 static int setDestPgsz(sqlite3_backup *p){
74914 int rc;
74915 rc = sqlite3BtreeSetPageSize(p->pDest,sqlite3BtreeGetPageSize(p->pSrc),0,0);
74916 return rc;
74917 }
74918
74919 /*
74920 ** Check that there is no open read-transaction on the b-tree passed as the
@@ -78604,24 +78935,23 @@
78935 ** "PX" -> "r[X]"
78936 ** "PX@PY" -> "r[X..X+Y-1]" or "r[x]" if y is 0 or 1
78937 ** "PX@PY+1" -> "r[X..X+Y]" or "r[x]" if y is 0
78938 ** "PY..PY" -> "r[X..Y]" or "r[x]" if y<=x
78939 */
78940 SQLITE_PRIVATE char *sqlite3VdbeDisplayComment(
78941 sqlite3 *db, /* Optional - Oom error reporting only */
78942 const Op *pOp, /* The opcode to be commented */
78943 const char *zP4 /* Previously obtained value for P4 */
 
 
78944 ){
78945 const char *zOpName;
78946 const char *zSynopsis;
78947 int nOpName;
78948 int ii;
78949 char zAlt[50];
78950 StrAccum x;
 
78951
78952 sqlite3StrAccumInit(&x, 0, 0, 0, SQLITE_MAX_LENGTH);
78953 zOpName = sqlite3OpcodeName(pOp->opcode);
78954 nOpName = sqlite3Strlen30(zOpName);
78955 if( zOpName[nOpName+1] ){
78956 int seenCom = 0;
78957 char c;
@@ -78684,14 +79014,16 @@
79014 sqlite3_str_appendf(&x, "; %s", pOp->zComment);
79015 }
79016 }else if( pOp->zComment ){
79017 sqlite3_str_appendall(&x, pOp->zComment);
79018 }
79019 if( (x.accError & SQLITE_NOMEM)!=0 && db!=0 ){
79020 sqlite3OomFault(db);
79021 }
79022 return sqlite3StrAccumFinish(&x);
79023 }
79024 #endif /* SQLITE_ENABLE_EXPLAIN_COMMENTS */
79025
79026 #if VDBE_DISPLAY_P4 && defined(SQLITE_ENABLE_CURSOR_HINTS)
79027 /*
79028 ** Translate the P4.pExpr value for an OP_CursorHint opcode into text
79029 ** that can be displayed in the P4 column of EXPLAIN output.
@@ -78768,15 +79100,15 @@
79100 #if VDBE_DISPLAY_P4
79101 /*
79102 ** Compute a string that describes the P4 parameter for an opcode.
79103 ** Use zTemp for any required temporary buffer space.
79104 */
79105 SQLITE_PRIVATE char *sqlite3VdbeDisplayP4(sqlite3 *db, Op *pOp){
79106 char *zP4 = 0;
79107 StrAccum x;
79108
79109 sqlite3StrAccumInit(&x, 0, 0, 0, SQLITE_MAX_LENGTH);
79110 switch( pOp->p4type ){
79111 case P4_KEYINFO: {
79112 int j;
79113 KeyInfo *pKeyInfo = pOp->p4.pKeyInfo;
79114 assert( pKeyInfo->aSortFlags!=0 );
@@ -78856,40 +79188,36 @@
79188 int i;
79189 int *ai = pOp->p4.ai;
79190 int n = ai[0]; /* The first element of an INTARRAY is always the
79191 ** count of the number of elements to follow */
79192 for(i=1; i<=n; i++){
79193 sqlite3_str_appendf(&x, "%c%d", (i==1 ? '[' : ','), ai[i]);
79194 }
 
79195 sqlite3_str_append(&x, "]", 1);
79196 break;
79197 }
79198 case P4_SUBPROGRAM: {
79199 zP4 = "program";
79200 break;
79201 }
79202 case P4_DYNBLOB:
79203 case P4_ADVANCE: {
 
79204 break;
79205 }
79206 case P4_TABLE: {
79207 zP4 = pOp->p4.pTab->zName;
79208 break;
79209 }
79210 default: {
79211 zP4 = pOp->p4.z;
 
 
 
 
79212 }
79213 }
79214 if( zP4 ) sqlite3_str_appendall(&x, zP4);
79215 if( (x.accError & SQLITE_NOMEM)!=0 ){
79216 sqlite3OomFault(db);
79217 }
79218 return sqlite3StrAccumFinish(&x);
79219 }
79220 #endif /* VDBE_DISPLAY_P4 */
79221
79222 /*
79223 ** Declare to the Vdbe that the BTree object at db->aDb[i] is used.
@@ -78975,28 +79303,32 @@
79303 /*
79304 ** Print a single opcode. This routine is used for debugging only.
79305 */
79306 SQLITE_PRIVATE void sqlite3VdbePrintOp(FILE *pOut, int pc, VdbeOp *pOp){
79307 char *zP4;
79308 char *zCom;
79309 sqlite3 dummyDb;
79310 static const char *zFormat1 = "%4d %-13s %4d %4d %4d %-13s %.2X %s\n";
79311 if( pOut==0 ) pOut = stdout;
79312 dummyDb.mallocFailed = 1;
79313 zP4 = sqlite3VdbeDisplayP4(&dummyDb, pOp);
79314 #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
79315 zCom = sqlite3VdbeDisplayComment(0, pOp, zP4);
79316 #else
79317 zCom = 0;
79318 #endif
79319 /* NB: The sqlite3OpcodeName() function is implemented by code created
79320 ** by the mkopcodeh.awk and mkopcodec.awk scripts which extract the
79321 ** information from the vdbe.c source text */
79322 fprintf(pOut, zFormat1, pc,
79323 sqlite3OpcodeName(pOp->opcode), pOp->p1, pOp->p2, pOp->p3,
79324 zP4 ? zP4 : "", pOp->p5,
79325 zCom ? zCom : ""
79326 );
79327 fflush(pOut);
79328 sqlite3_free(zP4);
79329 sqlite3_free(zCom);
79330 }
79331 #endif
79332
79333 /*
79334 ** Initialize an array of N Mem element.
@@ -79083,10 +79415,125 @@
79415 assert( sqlite3VdbeFrameIsValid(pFrame) );
79416 pFrame->pParent = pFrame->v->pDelFrame;
79417 pFrame->v->pDelFrame = pFrame;
79418 }
79419
79420 #if defined(SQLITE_ENABLE_BYTECODE_VTAB) || !defined(SQLITE_OMIT_EXPLAIN)
79421 /*
79422 ** Locate the next opcode to be displayed in EXPLAIN or EXPLAIN
79423 ** QUERY PLAN output.
79424 **
79425 ** Return SQLITE_ROW on success. Return SQLITE_DONE if there are no
79426 ** more opcodes to be displayed.
79427 */
79428 SQLITE_PRIVATE int sqlite3VdbeNextOpcode(
79429 Vdbe *p, /* The statement being explained */
79430 Mem *pSub, /* Storage for keeping track of subprogram nesting */
79431 int eMode, /* 0: normal. 1: EQP. 2: TablesUsed */
79432 int *piPc, /* IN/OUT: Current rowid. Overwritten with next rowid */
79433 int *piAddr, /* OUT: Write index into (*paOp)[] here */
79434 Op **paOp /* OUT: Write the opcode array here */
79435 ){
79436 int nRow; /* Stop when row count reaches this */
79437 int nSub = 0; /* Number of sub-vdbes seen so far */
79438 SubProgram **apSub = 0; /* Array of sub-vdbes */
79439 int i; /* Next instruction address */
79440 int rc = SQLITE_OK; /* Result code */
79441 Op *aOp = 0; /* Opcode array */
79442 int iPc; /* Rowid. Copy of value in *piPc */
79443
79444 /* When the number of output rows reaches nRow, that means the
79445 ** listing has finished and sqlite3_step() should return SQLITE_DONE.
79446 ** nRow is the sum of the number of rows in the main program, plus
79447 ** the sum of the number of rows in all trigger subprograms encountered
79448 ** so far. The nRow value will increase as new trigger subprograms are
79449 ** encountered, but p->pc will eventually catch up to nRow.
79450 */
79451 nRow = p->nOp;
79452 if( pSub!=0 ){
79453 if( pSub->flags&MEM_Blob ){
79454 /* pSub is initiallly NULL. It is initialized to a BLOB by
79455 ** the P4_SUBPROGRAM processing logic below */
79456 nSub = pSub->n/sizeof(Vdbe*);
79457 apSub = (SubProgram **)pSub->z;
79458 }
79459 for(i=0; i<nSub; i++){
79460 nRow += apSub[i]->nOp;
79461 }
79462 }
79463 iPc = *piPc;
79464 while(1){ /* Loop exits via break */
79465 i = iPc++;
79466 if( i>=nRow ){
79467 p->rc = SQLITE_OK;
79468 rc = SQLITE_DONE;
79469 break;
79470 }
79471 if( i<p->nOp ){
79472 /* The rowid is small enough that we are still in the
79473 ** main program. */
79474 aOp = p->aOp;
79475 }else{
79476 /* We are currently listing subprograms. Figure out which one and
79477 ** pick up the appropriate opcode. */
79478 int j;
79479 i -= p->nOp;
79480 assert( apSub!=0 );
79481 assert( nSub>0 );
79482 for(j=0; i>=apSub[j]->nOp; j++){
79483 i -= apSub[j]->nOp;
79484 assert( i<apSub[j]->nOp || j+1<nSub );
79485 }
79486 aOp = apSub[j]->aOp;
79487 }
79488
79489 /* When an OP_Program opcode is encounter (the only opcode that has
79490 ** a P4_SUBPROGRAM argument), expand the size of the array of subprograms
79491 ** kept in p->aMem[9].z to hold the new program - assuming this subprogram
79492 ** has not already been seen.
79493 */
79494 if( pSub!=0 && aOp[i].p4type==P4_SUBPROGRAM ){
79495 int nByte = (nSub+1)*sizeof(SubProgram*);
79496 int j;
79497 for(j=0; j<nSub; j++){
79498 if( apSub[j]==aOp[i].p4.pProgram ) break;
79499 }
79500 if( j==nSub ){
79501 p->rc = sqlite3VdbeMemGrow(pSub, nByte, nSub!=0);
79502 if( p->rc!=SQLITE_OK ){
79503 rc = SQLITE_ERROR;
79504 break;
79505 }
79506 apSub = (SubProgram **)pSub->z;
79507 apSub[nSub++] = aOp[i].p4.pProgram;
79508 MemSetTypeFlag(pSub, MEM_Blob);
79509 pSub->n = nSub*sizeof(SubProgram*);
79510 nRow += aOp[i].p4.pProgram->nOp;
79511 }
79512 }
79513 if( eMode==0 ) break;
79514 #ifdef SQLITE_ENABLE_BYTECODE_VTAB
79515 if( eMode==2 ){
79516 Op *pOp = aOp + i;
79517 if( pOp->opcode==OP_OpenRead ) break;
79518 if( pOp->opcode==OP_OpenWrite && (pOp->p5 & OPFLAG_P2ISREG)==0 ) break;
79519 if( pOp->opcode==OP_ReopenIdx ) break;
79520 }else
79521 #endif
79522 {
79523 assert( eMode==1 );
79524 if( aOp[i].opcode==OP_Explain ) break;
79525 if( aOp[i].opcode==OP_Init && iPc>1 ) break;
79526 }
79527 }
79528 *piPc = iPc;
79529 *piAddr = i;
79530 *paOp = aOp;
79531 return rc;
79532 }
79533 #endif /* SQLITE_ENABLE_BYTECODE_VTAB || !SQLITE_OMIT_EXPLAIN */
79534
79535
79536 /*
79537 ** Delete a VdbeFrame object and its contents. VdbeFrame objects are
79538 ** allocated by the OP_Program opcode in sqlite3VdbeExec().
79539 */
@@ -79123,20 +79570,18 @@
79570 ** the trigger subprograms are listed one by one.
79571 */
79572 SQLITE_PRIVATE int sqlite3VdbeList(
79573 Vdbe *p /* The VDBE */
79574 ){
 
 
 
79575 Mem *pSub = 0; /* Memory cell hold array of subprogs */
79576 sqlite3 *db = p->db; /* The database connection */
79577 int i; /* Loop counter */
79578 int rc = SQLITE_OK; /* Return code */
79579 Mem *pMem = &p->aMem[1]; /* First Mem of result set */
79580 int bListSubprogs = (p->explain==1 || (db->flags & SQLITE_TriggerEQP)!=0);
79581 Op *aOp; /* Array of opcodes */
79582 Op *pOp; /* Current opcode */
79583
79584 assert( p->explain );
79585 assert( p->magic==VDBE_MAGIC_RUN );
79586 assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || p->rc==SQLITE_NOMEM );
79587
@@ -79152,166 +79597,66 @@
79597 ** sqlite3_column_text16() failed. */
79598 sqlite3OomFault(db);
79599 return SQLITE_ERROR;
79600 }
79601
 
 
 
 
 
 
 
 
79602 if( bListSubprogs ){
79603 /* The first 8 memory cells are used for the result set. So we will
79604 ** commandeer the 9th cell to use as storage for an array of pointers
79605 ** to trigger subprograms. The VDBE is guaranteed to have at least 9
79606 ** cells. */
79607 assert( p->nMem>9 );
79608 pSub = &p->aMem[9];
79609 }else{
79610 pSub = 0;
79611 }
79612
79613 /* Figure out which opcode is next to display */
79614 rc = sqlite3VdbeNextOpcode(p, pSub, p->explain==2, &p->pc, &i, &aOp);
79615
79616 if( rc==SQLITE_OK ){
79617 pOp = aOp + i;
79618 if( AtomicLoad(&db->u1.isInterrupted) ){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79619 p->rc = SQLITE_INTERRUPT;
79620 rc = SQLITE_ERROR;
79621 sqlite3VdbeError(p, sqlite3ErrStr(p->rc));
79622 }else{
79623 char *zP4 = sqlite3VdbeDisplayP4(db, pOp);
79624 if( p->explain==2 ){
79625 sqlite3VdbeMemSetInt64(pMem, pOp->p1);
79626 sqlite3VdbeMemSetInt64(pMem+1, pOp->p2);
79627 sqlite3VdbeMemSetInt64(pMem+2, pOp->p3);
79628 sqlite3VdbeMemSetStr(pMem+3, zP4, -1, SQLITE_UTF8, sqlite3_free);
79629 p->nResColumn = 4;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79630 }else{
79631 sqlite3VdbeMemSetInt64(pMem+0, i);
79632 sqlite3VdbeMemSetStr(pMem+1, (char*)sqlite3OpcodeName(pOp->opcode),
79633 -1, SQLITE_UTF8, SQLITE_STATIC);
79634 sqlite3VdbeMemSetInt64(pMem+2, pOp->p1);
79635 sqlite3VdbeMemSetInt64(pMem+3, pOp->p2);
79636 sqlite3VdbeMemSetInt64(pMem+4, pOp->p3);
79637 /* pMem+5 for p4 is done last */
79638 sqlite3VdbeMemSetInt64(pMem+6, pOp->p5);
 
 
 
 
 
 
 
 
 
79639 #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
79640 {
79641 char *zCom = sqlite3VdbeDisplayComment(db, pOp, zP4);
79642 sqlite3VdbeMemSetStr(pMem+7, zCom, -1, SQLITE_UTF8, sqlite3_free);
79643 }
79644 #else
79645 sqlite3VdbeMemSetNull(pMem+7);
79646 #endif
79647 sqlite3VdbeMemSetStr(pMem+5, zP4, -1, SQLITE_UTF8, sqlite3_free);
79648 p->nResColumn = 8;
79649 }
79650 p->pResultSet = pMem;
79651 if( db->mallocFailed ){
79652 p->rc = SQLITE_NOMEM;
79653 rc = SQLITE_ERROR;
79654 }else{
79655 p->rc = SQLITE_OK;
79656 rc = SQLITE_ROW;
79657 }
79658 }
79659 }
79660 return rc;
79661 }
79662 #endif /* SQLITE_OMIT_EXPLAIN */
@@ -79877,12 +80222,13 @@
80222 int retryCount = 0;
80223 int nMainFile;
80224
80225 /* Select a master journal file name */
80226 nMainFile = sqlite3Strlen30(zMainFile);
80227 zMaster = sqlite3MPrintf(db, "%.4c%s%.16c", 0,zMainFile,0);
80228 if( zMaster==0 ) return SQLITE_NOMEM_BKPT;
80229 zMaster += 4;
80230 do {
80231 u32 iRandom;
80232 if( retryCount ){
80233 if( retryCount>100 ){
80234 sqlite3_log(SQLITE_FULL, "MJ delete: %s", zMaster);
@@ -79908,11 +80254,11 @@
80254 SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|
80255 SQLITE_OPEN_EXCLUSIVE|SQLITE_OPEN_MASTER_JOURNAL, 0
80256 );
80257 }
80258 if( rc!=SQLITE_OK ){
80259 sqlite3DbFree(db, zMaster-4);
80260 return rc;
80261 }
80262
80263 /* Write the name of each database file in the transaction into the new
80264 ** master journal file. If an error occurs at this point close
@@ -79931,11 +80277,11 @@
80277 rc = sqlite3OsWrite(pMaster, zFile, sqlite3Strlen30(zFile)+1, offset);
80278 offset += sqlite3Strlen30(zFile)+1;
80279 if( rc!=SQLITE_OK ){
80280 sqlite3OsCloseFree(pMaster);
80281 sqlite3OsDelete(pVfs, zMaster, 0);
80282 sqlite3DbFree(db, zMaster-4);
80283 return rc;
80284 }
80285 }
80286 }
80287
@@ -79945,11 +80291,11 @@
80291 if( 0==(sqlite3OsDeviceCharacteristics(pMaster)&SQLITE_IOCAP_SEQUENTIAL)
80292 && SQLITE_OK!=(rc = sqlite3OsSync(pMaster, SQLITE_SYNC_NORMAL))
80293 ){
80294 sqlite3OsCloseFree(pMaster);
80295 sqlite3OsDelete(pVfs, zMaster, 0);
80296 sqlite3DbFree(db, zMaster-4);
80297 return rc;
80298 }
80299
80300 /* Sync all the db files involved in the transaction. The same call
80301 ** sets the master journal pointer in each individual journal. If
@@ -79968,20 +80314,20 @@
80314 }
80315 }
80316 sqlite3OsCloseFree(pMaster);
80317 assert( rc!=SQLITE_BUSY );
80318 if( rc!=SQLITE_OK ){
80319 sqlite3DbFree(db, zMaster-4);
80320 return rc;
80321 }
80322
80323 /* Delete the master journal file. This commits the transaction. After
80324 ** doing this the directory is synced again before any individual
80325 ** transaction files are deleted.
80326 */
80327 rc = sqlite3OsDelete(pVfs, zMaster, 1);
80328 sqlite3DbFree(db, zMaster-4);
80329 zMaster = 0;
80330 if( rc ){
80331 return rc;
80332 }
80333
@@ -83028,11 +83374,11 @@
83374 /* If there are no other statements currently running, then
83375 ** reset the interrupt flag. This prevents a call to sqlite3_interrupt
83376 ** from interrupting a statement that has not yet started.
83377 */
83378 if( db->nVdbeActive==0 ){
83379 AtomicStore(&db->u1.isInterrupted, 0);
83380 }
83381
83382 assert( db->nVdbeWrite>0 || db->autoCommit==0
83383 || (db->nDeferredCons==0 && db->nDeferredImmCons==0)
83384 );
@@ -85413,11 +85759,11 @@
85759 assert( p->bIsReader || p->readOnly!=0 );
85760 p->iCurrentTime = 0;
85761 assert( p->explain==0 );
85762 p->pResultSet = 0;
85763 db->busyHandler.nBusy = 0;
85764 if( AtomicLoad(&db->u1.isInterrupted) ) goto abort_due_to_interrupt;
85765 sqlite3VdbeIOTraceSql(p);
85766 #ifdef SQLITE_DEBUG
85767 sqlite3BeginBenignMalloc();
85768 if( p->pc==0
85769 && (p->db->flags & (SQLITE_VdbeListing|SQLITE_VdbeEQP|SQLITE_VdbeTrace))!=0
@@ -85597,11 +85943,11 @@
85943 ** But that is not due to sloppy coding habits. The code is written this
85944 ** way for performance, to avoid having to run the interrupt and progress
85945 ** checks on every opcode. This helps sqlite3_step() to run about 1.5%
85946 ** faster according to "valgrind --tool=cachegrind" */
85947 check_for_interrupt:
85948 if( AtomicLoad(&db->u1.isInterrupted) ) goto abort_due_to_interrupt;
85949 #ifndef SQLITE_OMIT_PROGRESS_CALLBACK
85950 /* Call the progress callback if it is configured and the required number
85951 ** of VDBE ops have been executed (either since this invocation of
85952 ** sqlite3VdbeExec() or since last time the progress callback was called).
85953 ** If the progress callback returns non-zero, exit the virtual machine with
@@ -87893,32 +88239,38 @@
88239 assert( pOp->p3>0 && pOp->p3<=(p->nMem+1 - p->nCursor) );
88240 REGISTER_TRACE(pOp->p3, pOut);
88241 break;
88242 }
88243
88244 /* Opcode: Count P1 P2 p3 * *
88245 ** Synopsis: r[P2]=count()
88246 **
88247 ** Store the number of entries (an integer value) in the table or index
88248 ** opened by cursor P1 in register P2.
88249 **
88250 ** If P3==0, then an exact count is obtained, which involves visiting
88251 ** every btree page of the table. But if P3 is non-zero, an estimate
88252 ** is returned based on the current cursor position.
88253 */
 
88254 case OP_Count: { /* out2 */
88255 i64 nEntry;
88256 BtCursor *pCrsr;
88257
88258 assert( p->apCsr[pOp->p1]->eCurType==CURTYPE_BTREE );
88259 pCrsr = p->apCsr[pOp->p1]->uc.pCursor;
88260 assert( pCrsr );
88261 if( pOp->p3 ){
88262 nEntry = sqlite3BtreeRowCountEst(pCrsr);
88263 }else{
88264 nEntry = 0; /* Not needed. Only used to silence a warning. */
88265 rc = sqlite3BtreeCount(db, pCrsr, &nEntry);
88266 if( rc ) goto abort_due_to_error;
88267 }
88268 pOut = out2Prerelease(p, pOp);
88269 pOut->u.i = nEntry;
88270 goto check_for_interrupt;
88271 }
 
88272
88273 /* Opcode: Savepoint P1 * * P4 *
88274 **
88275 ** Open, release or rollback the savepoint named by parameter P4, depending
88276 ** on the value of P1. To open a new savepoint set P1==0 (SAVEPOINT_BEGIN).
@@ -90352,16 +90704,23 @@
90704 rc = sqlite3VdbeSorterWrite(pC, pIn2);
90705 if( rc) goto abort_due_to_error;
90706 break;
90707 }
90708
90709 /* Opcode: IdxDelete P1 P2 P3 * P5
90710 ** Synopsis: key=r[P2@P3]
90711 **
90712 ** The content of P3 registers starting at register P2 form
90713 ** an unpacked index key. This opcode removes that entry from the
90714 ** index opened by cursor P1.
90715 **
90716 ** If P5 is not zero, then raise an SQLITE_CORRUPT_INDEX error
90717 ** if no matching index entry is found. This happens when running
90718 ** an UPDATE or DELETE statement and the index entry to be updated
90719 ** or deleted is not found. For some uses of IdxDelete
90720 ** (example: the EXCEPT operator) it does not matter that no matching
90721 ** entry is found. For those cases, P5 is zero.
90722 */
90723 case OP_IdxDelete: {
90724 VdbeCursor *pC;
90725 BtCursor *pCrsr;
90726 int res;
@@ -90374,20 +90733,22 @@
90733 assert( pC!=0 );
90734 assert( pC->eCurType==CURTYPE_BTREE );
90735 sqlite3VdbeIncrWriteCounter(p, pC);
90736 pCrsr = pC->uc.pCursor;
90737 assert( pCrsr!=0 );
 
90738 r.pKeyInfo = pC->pKeyInfo;
90739 r.nField = (u16)pOp->p3;
90740 r.default_rc = 0;
90741 r.aMem = &aMem[pOp->p2];
90742 rc = sqlite3BtreeMovetoUnpacked(pCrsr, &r, 0, 0, &res);
90743 if( rc ) goto abort_due_to_error;
90744 if( res==0 ){
90745 rc = sqlite3BtreeDelete(pCrsr, BTREE_AUXDELETE);
90746 if( rc ) goto abort_due_to_error;
90747 }else if( pOp->p5 ){
90748 rc = SQLITE_CORRUPT_INDEX;
90749 goto abort_due_to_error;
90750 }
90751 assert( pC->deferredMoveto==0 );
90752 pC->cacheStatus = CACHE_STALE;
90753 pC->seekResult = 0;
90754 break;
@@ -92705,11 +93066,11 @@
93066
93067 /* Jump to here if the sqlite3_interrupt() API sets the interrupt
93068 ** flag.
93069 */
93070 abort_due_to_interrupt:
93071 assert( AtomicLoad(&db->u1.isInterrupted) );
93072 rc = db->mallocFailed ? SQLITE_NOMEM_BKPT : SQLITE_INTERRUPT;
93073 p->rc = rc;
93074 sqlite3VdbeError(p, "%s", sqlite3ErrStr(rc));
93075 goto abort_due_to_error;
93076 }
@@ -95988,10 +96349,435 @@
96349 *pRes = sqlite3VdbeRecordCompare(pVal->n, pVal->z, r2);
96350 return SQLITE_OK;
96351 }
96352
96353 /************** End of vdbesort.c ********************************************/
96354 /************** Begin file vdbevtab.c ****************************************/
96355 /*
96356 ** 2020-03-23
96357 **
96358 ** The author disclaims copyright to this source code. In place of
96359 ** a legal notice, here is a blessing:
96360 **
96361 ** May you do good and not evil.
96362 ** May you find forgiveness for yourself and forgive others.
96363 ** May you share freely, never taking more than you give.
96364 **
96365 *************************************************************************
96366 **
96367 ** This file implements virtual-tables for examining the bytecode content
96368 ** of a prepared statement.
96369 */
96370 #ifdef SQLITE_ENABLE_BYTECODE_VTAB
96371 /* #include "sqliteInt.h" */
96372 /* #include "vdbeInt.h" */
96373
96374 /* An instance of the bytecode() table-valued function.
96375 */
96376 typedef struct bytecodevtab bytecodevtab;
96377 struct bytecodevtab {
96378 sqlite3_vtab base; /* Base class - must be first */
96379 sqlite3 *db; /* Database connection */
96380 int bTablesUsed; /* 2 for tables_used(). 0 for bytecode(). */
96381 };
96382
96383 /* A cursor for scanning through the bytecode
96384 */
96385 typedef struct bytecodevtab_cursor bytecodevtab_cursor;
96386 struct bytecodevtab_cursor {
96387 sqlite3_vtab_cursor base; /* Base class - must be first */
96388 sqlite3_stmt *pStmt; /* The statement whose bytecode is displayed */
96389 int iRowid; /* The rowid of the output table */
96390 int iAddr; /* Address */
96391 int needFinalize; /* Cursors owns pStmt and must finalize it */
96392 int showSubprograms; /* Provide a listing of subprograms */
96393 Op *aOp; /* Operand array */
96394 char *zP4; /* Rendered P4 value */
96395 const char *zType; /* tables_used.type */
96396 const char *zSchema; /* tables_used.schema */
96397 const char *zName; /* tables_used.name */
96398 Mem sub; /* Subprograms */
96399 };
96400
96401 /*
96402 ** Create a new bytecode() table-valued function.
96403 */
96404 static int bytecodevtabConnect(
96405 sqlite3 *db,
96406 void *pAux,
96407 int argc, const char *const*argv,
96408 sqlite3_vtab **ppVtab,
96409 char **pzErr
96410 ){
96411 bytecodevtab *pNew;
96412 int rc;
96413 int isTabUsed = pAux!=0;
96414 const char *azSchema[2] = {
96415 /* bytecode() schema */
96416 "CREATE TABLE x("
96417 "addr INT,"
96418 "opcode TEXT,"
96419 "p1 INT,"
96420 "p2 INT,"
96421 "p3 INT,"
96422 "p4 TEXT,"
96423 "p5 INT,"
96424 "comment TEXT,"
96425 "subprog TEXT,"
96426 "stmt HIDDEN"
96427 ");",
96428
96429 /* Tables_used() schema */
96430 "CREATE TABLE x("
96431 "type TEXT,"
96432 "schema TEXT,"
96433 "name TEXT,"
96434 "wr INT,"
96435 "subprog TEXT,"
96436 "stmt HIDDEN"
96437 ");"
96438 };
96439
96440 rc = sqlite3_declare_vtab(db, azSchema[isTabUsed]);
96441 if( rc==SQLITE_OK ){
96442 pNew = sqlite3_malloc( sizeof(*pNew) );
96443 *ppVtab = (sqlite3_vtab*)pNew;
96444 if( pNew==0 ) return SQLITE_NOMEM;
96445 memset(pNew, 0, sizeof(*pNew));
96446 pNew->db = db;
96447 pNew->bTablesUsed = isTabUsed*2;
96448 }
96449 return rc;
96450 }
96451
96452 /*
96453 ** This method is the destructor for bytecodevtab objects.
96454 */
96455 static int bytecodevtabDisconnect(sqlite3_vtab *pVtab){
96456 bytecodevtab *p = (bytecodevtab*)pVtab;
96457 sqlite3_free(p);
96458 return SQLITE_OK;
96459 }
96460
96461 /*
96462 ** Constructor for a new bytecodevtab_cursor object.
96463 */
96464 static int bytecodevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
96465 bytecodevtab *pVTab = (bytecodevtab*)p;
96466 bytecodevtab_cursor *pCur;
96467 pCur = sqlite3_malloc( sizeof(*pCur) );
96468 if( pCur==0 ) return SQLITE_NOMEM;
96469 memset(pCur, 0, sizeof(*pCur));
96470 sqlite3VdbeMemInit(&pCur->sub, pVTab->db, 1);
96471 *ppCursor = &pCur->base;
96472 return SQLITE_OK;
96473 }
96474
96475 /*
96476 ** Clear all internal content from a bytecodevtab cursor.
96477 */
96478 static void bytecodevtabCursorClear(bytecodevtab_cursor *pCur){
96479 sqlite3_free(pCur->zP4);
96480 pCur->zP4 = 0;
96481 sqlite3VdbeMemRelease(&pCur->sub);
96482 sqlite3VdbeMemSetNull(&pCur->sub);
96483 if( pCur->needFinalize ){
96484 sqlite3_finalize(pCur->pStmt);
96485 }
96486 pCur->pStmt = 0;
96487 pCur->needFinalize = 0;
96488 pCur->zType = 0;
96489 pCur->zSchema = 0;
96490 pCur->zName = 0;
96491 }
96492
96493 /*
96494 ** Destructor for a bytecodevtab_cursor.
96495 */
96496 static int bytecodevtabClose(sqlite3_vtab_cursor *cur){
96497 bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
96498 bytecodevtabCursorClear(pCur);
96499 sqlite3_free(pCur);
96500 return SQLITE_OK;
96501 }
96502
96503
96504 /*
96505 ** Advance a bytecodevtab_cursor to its next row of output.
96506 */
96507 static int bytecodevtabNext(sqlite3_vtab_cursor *cur){
96508 bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
96509 bytecodevtab *pTab = (bytecodevtab*)cur->pVtab;
96510 int rc;
96511 if( pCur->zP4 ){
96512 sqlite3_free(pCur->zP4);
96513 pCur->zP4 = 0;
96514 }
96515 if( pCur->zName ){
96516 pCur->zName = 0;
96517 pCur->zType = 0;
96518 pCur->zSchema = 0;
96519 }
96520 rc = sqlite3VdbeNextOpcode(
96521 (Vdbe*)pCur->pStmt,
96522 pCur->showSubprograms ? &pCur->sub : 0,
96523 pTab->bTablesUsed,
96524 &pCur->iRowid,
96525 &pCur->iAddr,
96526 &pCur->aOp);
96527 if( rc!=SQLITE_OK ){
96528 sqlite3VdbeMemSetNull(&pCur->sub);
96529 pCur->aOp = 0;
96530 }
96531 return SQLITE_OK;
96532 }
96533
96534 /*
96535 ** Return TRUE if the cursor has been moved off of the last
96536 ** row of output.
96537 */
96538 static int bytecodevtabEof(sqlite3_vtab_cursor *cur){
96539 bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
96540 return pCur->aOp==0;
96541 }
96542
96543 /*
96544 ** Return values of columns for the row at which the bytecodevtab_cursor
96545 ** is currently pointing.
96546 */
96547 static int bytecodevtabColumn(
96548 sqlite3_vtab_cursor *cur, /* The cursor */
96549 sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
96550 int i /* Which column to return */
96551 ){
96552 bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
96553 bytecodevtab *pVTab = (bytecodevtab*)cur->pVtab;
96554 Op *pOp = pCur->aOp + pCur->iAddr;
96555 if( pVTab->bTablesUsed ){
96556 if( i==4 ){
96557 i = 8;
96558 }else{
96559 if( i<=2 && pCur->zType==0 ){
96560 Schema *pSchema;
96561 HashElem *k;
96562 int iDb = pOp->p3;
96563 int iRoot = pOp->p2;
96564 sqlite3 *db = pVTab->db;
96565 pSchema = db->aDb[iDb].pSchema;
96566 pCur->zSchema = db->aDb[iDb].zDbSName;
96567 for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){
96568 Table *pTab = (Table*)sqliteHashData(k);
96569 if( !IsVirtual(pTab) && pTab->tnum==iRoot ){
96570 pCur->zName = pTab->zName;
96571 pCur->zType = "table";
96572 break;
96573 }
96574 }
96575 if( pCur->zName==0 ){
96576 for(k=sqliteHashFirst(&pSchema->idxHash); k; k=sqliteHashNext(k)){
96577 Index *pIdx = (Index*)sqliteHashData(k);
96578 if( pIdx->tnum==iRoot ){
96579 pCur->zName = pIdx->zName;
96580 pCur->zType = "index";
96581 }
96582 }
96583 }
96584 }
96585 i += 10;
96586 }
96587 }
96588 switch( i ){
96589 case 0: /* addr */
96590 sqlite3_result_int(ctx, pCur->iAddr);
96591 break;
96592 case 1: /* opcode */
96593 sqlite3_result_text(ctx, (char*)sqlite3OpcodeName(pOp->opcode),
96594 -1, SQLITE_STATIC);
96595 break;
96596 case 2: /* p1 */
96597 sqlite3_result_int(ctx, pOp->p1);
96598 break;
96599 case 3: /* p2 */
96600 sqlite3_result_int(ctx, pOp->p2);
96601 break;
96602 case 4: /* p3 */
96603 sqlite3_result_int(ctx, pOp->p3);
96604 break;
96605 case 5: /* p4 */
96606 case 7: /* comment */
96607 if( pCur->zP4==0 ){
96608 pCur->zP4 = sqlite3VdbeDisplayP4(pVTab->db, pOp);
96609 }
96610 if( i==5 ){
96611 sqlite3_result_text(ctx, pCur->zP4, -1, SQLITE_STATIC);
96612 }else{
96613 #ifdef SQLITE_ENABLE_EXPLAIN_COMMENTS
96614 char *zCom = sqlite3VdbeDisplayComment(pVTab->db, pOp, pCur->zP4);
96615 sqlite3_result_text(ctx, zCom, -1, sqlite3_free);
96616 #endif
96617 }
96618 break;
96619 case 6: /* p5 */
96620 sqlite3_result_int(ctx, pOp->p5);
96621 break;
96622 case 8: { /* subprog */
96623 Op *aOp = pCur->aOp;
96624 assert( aOp[0].opcode==OP_Init );
96625 assert( aOp[0].p4.z==0 || strncmp(aOp[0].p4.z,"-" "- ",3)==0 );
96626 if( pCur->iRowid==pCur->iAddr+1 ){
96627 break; /* Result is NULL for the main program */
96628 }else if( aOp[0].p4.z!=0 ){
96629 sqlite3_result_text(ctx, aOp[0].p4.z+3, -1, SQLITE_STATIC);
96630 }else{
96631 sqlite3_result_text(ctx, "(FK)", 4, SQLITE_STATIC);
96632 }
96633 break;
96634 }
96635 case 10: /* tables_used.type */
96636 sqlite3_result_text(ctx, pCur->zType, -1, SQLITE_STATIC);
96637 break;
96638 case 11: /* tables_used.schema */
96639 sqlite3_result_text(ctx, pCur->zSchema, -1, SQLITE_STATIC);
96640 break;
96641 case 12: /* tables_used.name */
96642 sqlite3_result_text(ctx, pCur->zName, -1, SQLITE_STATIC);
96643 break;
96644 case 13: /* tables_used.wr */
96645 sqlite3_result_int(ctx, pOp->opcode==OP_OpenWrite);
96646 break;
96647 }
96648 return SQLITE_OK;
96649 }
96650
96651 /*
96652 ** Return the rowid for the current row. In this implementation, the
96653 ** rowid is the same as the output value.
96654 */
96655 static int bytecodevtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
96656 bytecodevtab_cursor *pCur = (bytecodevtab_cursor*)cur;
96657 *pRowid = pCur->iRowid;
96658 return SQLITE_OK;
96659 }
96660
96661 /*
96662 ** Initialize a cursor.
96663 **
96664 ** idxNum==0 means show all subprograms
96665 ** idxNum==1 means show only the main bytecode and omit subprograms.
96666 */
96667 static int bytecodevtabFilter(
96668 sqlite3_vtab_cursor *pVtabCursor,
96669 int idxNum, const char *idxStr,
96670 int argc, sqlite3_value **argv
96671 ){
96672 bytecodevtab_cursor *pCur = (bytecodevtab_cursor *)pVtabCursor;
96673 bytecodevtab *pVTab = (bytecodevtab *)pVtabCursor->pVtab;
96674 int rc = SQLITE_OK;
96675
96676 bytecodevtabCursorClear(pCur);
96677 pCur->iRowid = 0;
96678 pCur->iAddr = 0;
96679 pCur->showSubprograms = idxNum==0;
96680 assert( argc==1 );
96681 if( sqlite3_value_type(argv[0])==SQLITE_TEXT ){
96682 const char *zSql = (const char*)sqlite3_value_text(argv[0]);
96683 if( zSql==0 ){
96684 rc = SQLITE_NOMEM;
96685 }else{
96686 rc = sqlite3_prepare_v2(pVTab->db, zSql, -1, &pCur->pStmt, 0);
96687 pCur->needFinalize = 1;
96688 }
96689 }else{
96690 pCur->pStmt = (sqlite3_stmt*)sqlite3_value_pointer(argv[0],"stmt-pointer");
96691 }
96692 if( pCur->pStmt==0 ){
96693 pVTab->base.zErrMsg = sqlite3_mprintf(
96694 "argument to %s() is not a valid SQL statement",
96695 pVTab->bTablesUsed ? "tables_used" : "bytecode"
96696 );
96697 rc = SQLITE_ERROR;
96698 }else{
96699 bytecodevtabNext(pVtabCursor);
96700 }
96701 return rc;
96702 }
96703
96704 /*
96705 ** We must have a single stmt=? constraint that will be passed through
96706 ** into the xFilter method. If there is no valid stmt=? constraint,
96707 ** then return an SQLITE_CONSTRAINT error.
96708 */
96709 static int bytecodevtabBestIndex(
96710 sqlite3_vtab *tab,
96711 sqlite3_index_info *pIdxInfo
96712 ){
96713 int i;
96714 int rc = SQLITE_CONSTRAINT;
96715 struct sqlite3_index_constraint *p;
96716 bytecodevtab *pVTab = (bytecodevtab*)tab;
96717 int iBaseCol = pVTab->bTablesUsed ? 4 : 8;
96718 pIdxInfo->estimatedCost = (double)100;
96719 pIdxInfo->estimatedRows = 100;
96720 pIdxInfo->idxNum = 0;
96721 for(i=0, p=pIdxInfo->aConstraint; i<pIdxInfo->nConstraint; i++, p++){
96722 if( p->usable==0 ) continue;
96723 if( p->op==SQLITE_INDEX_CONSTRAINT_EQ && p->iColumn==iBaseCol+1 ){
96724 rc = SQLITE_OK;
96725 pIdxInfo->aConstraintUsage[i].omit = 1;
96726 pIdxInfo->aConstraintUsage[i].argvIndex = 1;
96727 }
96728 if( p->op==SQLITE_INDEX_CONSTRAINT_ISNULL && p->iColumn==iBaseCol ){
96729 pIdxInfo->aConstraintUsage[i].omit = 1;
96730 pIdxInfo->idxNum = 1;
96731 }
96732 }
96733 return rc;
96734 }
96735
96736 /*
96737 ** This following structure defines all the methods for the
96738 ** virtual table.
96739 */
96740 static sqlite3_module bytecodevtabModule = {
96741 /* iVersion */ 0,
96742 /* xCreate */ 0,
96743 /* xConnect */ bytecodevtabConnect,
96744 /* xBestIndex */ bytecodevtabBestIndex,
96745 /* xDisconnect */ bytecodevtabDisconnect,
96746 /* xDestroy */ 0,
96747 /* xOpen */ bytecodevtabOpen,
96748 /* xClose */ bytecodevtabClose,
96749 /* xFilter */ bytecodevtabFilter,
96750 /* xNext */ bytecodevtabNext,
96751 /* xEof */ bytecodevtabEof,
96752 /* xColumn */ bytecodevtabColumn,
96753 /* xRowid */ bytecodevtabRowid,
96754 /* xUpdate */ 0,
96755 /* xBegin */ 0,
96756 /* xSync */ 0,
96757 /* xCommit */ 0,
96758 /* xRollback */ 0,
96759 /* xFindMethod */ 0,
96760 /* xRename */ 0,
96761 /* xSavepoint */ 0,
96762 /* xRelease */ 0,
96763 /* xRollbackTo */ 0,
96764 /* xShadowName */ 0
96765 };
96766
96767
96768 SQLITE_PRIVATE int sqlite3VdbeBytecodeVtabInit(sqlite3 *db){
96769 int rc;
96770 rc = sqlite3_create_module(db, "bytecode", &bytecodevtabModule, 0);
96771 if( rc==SQLITE_OK ){
96772 rc = sqlite3_create_module(db, "tables_used", &bytecodevtabModule, &db);
96773 }
96774 return rc;
96775 }
96776 #endif /* SQLITE_ENABLE_BYTECODE_VTAB */
96777
96778 /************** End of vdbevtab.c ********************************************/
96779 /************** Begin file memjournal.c **************************************/
96780 /*
96781 ** 2008 October 7
96782 **
96783 ** The author disclaims copyright to this source code. In place of
@@ -96775,11 +97561,11 @@
97561 const char *zTab,
97562 const char *zDb
97563 ){
97564 int n;
97565 const char *zSpan;
97566 if( pItem->eEName!=ENAME_TAB ) return 0;
97567 zSpan = pItem->zEName;
97568 for(n=0; ALWAYS(zSpan[n]) && zSpan[n]!='.'; n++){}
97569 if( zDb && (sqlite3StrNICmp(zSpan, zDb, n)!=0 || zDb[n]!=0) ){
97570 return 0;
97571 }
@@ -96929,10 +97715,11 @@
97715 ExprList *pEList;
97716 SrcList *pSrcList = pNC->pSrcList;
97717
97718 if( pSrcList ){
97719 for(i=0, pItem=pSrcList->a; i<pSrcList->nSrc; i++, pItem++){
97720 u8 hCol;
97721 pTab = pItem->pTab;
97722 assert( pTab!=0 && pTab->zName!=0 );
97723 assert( pTab->nCol>0 );
97724 if( pItem->pSelect && (pItem->pSelect->selFlags & SF_NestedFrom)!=0 ){
97725 int hit = 0;
@@ -96962,12 +97749,13 @@
97749 }
97750 }
97751 if( 0==(cntTab++) ){
97752 pMatch = pItem;
97753 }
97754 hCol = sqlite3StrIHash(zCol);
97755 for(j=0, pCol=pTab->aCol; j<pTab->nCol; j++, pCol++){
97756 if( pCol->hName==hCol && sqlite3StrICmp(pCol->zName, zCol)==0 ){
97757 /* If there has been exactly one prior match and this match
97758 ** is for the right-hand table of a NATURAL JOIN or is in a
97759 ** USING clause, then skip this match.
97760 */
97761 if( cnt==1 ){
@@ -97024,14 +97812,15 @@
97812 }
97813 #endif /* SQLITE_OMIT_UPSERT */
97814
97815 if( pTab ){
97816 int iCol;
97817 u8 hCol = sqlite3StrIHash(zCol);
97818 pSchema = pTab->pSchema;
97819 cntTab++;
97820 for(iCol=0, pCol=pTab->aCol; iCol<pTab->nCol; iCol++, pCol++){
97821 if( pCol->hName==hCol && sqlite3StrICmp(pCol->zName, zCol)==0 ){
97822 if( iCol==pTab->iPKey ){
97823 iCol = -1;
97824 }
97825 break;
97826 }
@@ -97828,11 +98617,11 @@
98617 nc.uNC.pEList = pEList;
98618 nc.ncFlags = NC_AllowAgg|NC_UEList;
98619 nc.nErr = 0;
98620 db = pParse->db;
98621 savedSuppErr = db->suppressErr;
98622 if( IN_RENAME_OBJECT==0 ) db->suppressErr = 1;
98623 rc = sqlite3ResolveExprNames(&nc, pE);
98624 db->suppressErr = savedSuppErr;
98625 if( rc ) return 0;
98626
98627 /* Try to match the ORDER BY expression against an expression
@@ -98463,15 +99252,45 @@
99252 SQLITE_PRIVATE int sqlite3ResolveExprListNames(
99253 NameContext *pNC, /* Namespace to resolve expressions in. */
99254 ExprList *pList /* The expression list to be analyzed. */
99255 ){
99256 int i;
99257 int savedHasAgg = 0;
99258 Walker w;
99259 if( pList==0 ) return WRC_Continue;
99260 w.pParse = pNC->pParse;
99261 w.xExprCallback = resolveExprStep;
99262 w.xSelectCallback = resolveSelectStep;
99263 w.xSelectCallback2 = 0;
99264 w.u.pNC = pNC;
99265 savedHasAgg = pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin);
99266 pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin);
99267 for(i=0; i<pList->nExpr; i++){
99268 Expr *pExpr = pList->a[i].pExpr;
99269 if( pExpr==0 ) continue;
99270 #if SQLITE_MAX_EXPR_DEPTH>0
99271 w.pParse->nHeight += pExpr->nHeight;
99272 if( sqlite3ExprCheckHeight(w.pParse, w.pParse->nHeight) ){
99273 return WRC_Abort;
99274 }
99275 #endif
99276 sqlite3WalkExpr(&w, pExpr);
99277 #if SQLITE_MAX_EXPR_DEPTH>0
99278 w.pParse->nHeight -= pExpr->nHeight;
99279 #endif
99280 assert( EP_Agg==NC_HasAgg );
99281 assert( EP_Win==NC_HasWin );
99282 testcase( pNC->ncFlags & NC_HasAgg );
99283 testcase( pNC->ncFlags & NC_HasWin );
99284 if( pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin) ){
99285 ExprSetProperty(pExpr, pNC->ncFlags & (NC_HasAgg|NC_HasWin) );
99286 savedHasAgg |= pNC->ncFlags & (NC_HasAgg|NC_MinMaxAgg|NC_HasWin);
99287 pNC->ncFlags &= ~(NC_HasAgg|NC_MinMaxAgg|NC_HasWin);
99288 }
99289 if( pNC->nErr>0 || w.pParse->nErr>0 ) return WRC_Abort;
99290 }
99291 pNC->ncFlags |= savedHasAgg;
99292 return WRC_Continue;
99293 }
99294
99295 /*
99296 ** Resolve all names in all expressions of a SELECT and in all
@@ -98601,11 +99420,11 @@
99420 ** SELECT * FROM t1 WHERE (select a from t1);
99421 */
99422 SQLITE_PRIVATE char sqlite3ExprAffinity(const Expr *pExpr){
99423 int op;
99424 while( ExprHasProperty(pExpr, EP_Skip) ){
99425 assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW );
99426 pExpr = pExpr->pLeft;
99427 assert( pExpr!=0 );
99428 }
99429 op = pExpr->op;
99430 if( op==TK_SELECT ){
@@ -98668,11 +99487,11 @@
99487 /*
99488 ** Skip over any TK_COLLATE operators.
99489 */
99490 SQLITE_PRIVATE Expr *sqlite3ExprSkipCollate(Expr *pExpr){
99491 while( pExpr && ExprHasProperty(pExpr, EP_Skip) ){
99492 assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW );
99493 pExpr = pExpr->pLeft;
99494 }
99495 return pExpr;
99496 }
99497
@@ -98687,11 +99506,11 @@
99506 assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
99507 assert( pExpr->x.pList->nExpr>0 );
99508 assert( pExpr->op==TK_FUNCTION );
99509 pExpr = pExpr->x.pList->a[0].pExpr;
99510 }else{
99511 assert( pExpr->op==TK_COLLATE || pExpr->op==TK_IF_NULL_ROW );
99512 pExpr = pExpr->pLeft;
99513 }
99514 }
99515 return pExpr;
99516 }
@@ -100343,20 +101162,26 @@
101162 ExprList *pList, /* List to which to add the span. */
101163 Token *pName, /* Name to be added */
101164 int dequote /* True to cause the name to be dequoted */
101165 ){
101166 assert( pList!=0 || pParse->db->mallocFailed!=0 );
101167 assert( pParse->eParseMode!=PARSE_MODE_UNMAP || dequote==0 );
101168 if( pList ){
101169 struct ExprList_item *pItem;
101170 assert( pList->nExpr>0 );
101171 pItem = &pList->a[pList->nExpr-1];
101172 assert( pItem->zEName==0 );
101173 assert( pItem->eEName==ENAME_NAME );
101174 pItem->zEName = sqlite3DbStrNDup(pParse->db, pName->z, pName->n);
101175 if( dequote ){
101176 /* If dequote==0, then pName->z does not point to part of a DDL
101177 ** statement handled by the parser. And so no token need be added
101178 ** to the token-map. */
101179 sqlite3Dequote(pItem->zEName);
101180 if( IN_RENAME_OBJECT ){
101181 sqlite3RenameTokenMap(pParse, (void*)pItem->zEName, pName);
101182 }
101183 }
101184 }
101185 }
101186
101187 /*
@@ -101497,10 +102322,12 @@
102322 struct ExprList_item *pItem;
102323 int r1, r2;
102324 affinity = sqlite3ExprAffinity(pLeft);
102325 if( affinity<=SQLITE_AFF_NONE ){
102326 affinity = SQLITE_AFF_BLOB;
102327 }else if( affinity==SQLITE_AFF_REAL ){
102328 affinity = SQLITE_AFF_NUMERIC;
102329 }
102330 if( pKeyInfo ){
102331 assert( sqlite3KeyInfoIsWriteable(pKeyInfo) );
102332 pKeyInfo->aColl[0] = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
102333 }
@@ -101735,10 +102562,11 @@
102562 int destStep6 = 0; /* Start of code for Step 6 */
102563 int addrTruthOp; /* Address of opcode that determines the IN is true */
102564 int destNotNull; /* Jump here if a comparison is not true in step 6 */
102565 int addrTop; /* Top of the step-6 loop */
102566 int iTab = 0; /* Index to use */
102567 u8 okConstFactor = pParse->okConstFactor;
102568
102569 assert( !ExprHasVVAProperty(pExpr,EP_Immutable) );
102570 pLeft = pExpr->pLeft;
102571 if( sqlite3ExprCheckIN(pParse, pExpr) ) return;
102572 zAff = exprINAffinity(pParse, pExpr);
@@ -101779,12 +102607,18 @@
102607 **
102608 ** sqlite3FindInIndex() might have reordered the fields of the LHS vector
102609 ** so that the fields are in the same order as an existing index. The
102610 ** aiMap[] array contains a mapping from the original LHS field order to
102611 ** the field order that matches the RHS index.
102612 **
102613 ** Avoid factoring the LHS of the IN(...) expression out of the loop,
102614 ** even if it is constant, as OP_Affinity may be used on the register
102615 ** by code generated below. */
102616 assert( pParse->okConstFactor==okConstFactor );
102617 pParse->okConstFactor = 0;
102618 rLhsOrig = exprCodeVector(pParse, pLeft, &iDummy);
102619 pParse->okConstFactor = okConstFactor;
102620 for(i=0; i<nVector && aiMap[i]==i; i++){} /* Are LHS fields reordered? */
102621 if( i==nVector ){
102622 /* LHS fields are not reordered */
102623 rLhs = rLhsOrig;
102624 }else{
@@ -101806,25 +102640,17 @@
102640 CollSeq *pColl = sqlite3ExprCollSeq(pParse, pExpr->pLeft);
102641 int labelOk = sqlite3VdbeMakeLabel(pParse);
102642 int r2, regToFree;
102643 int regCkNull = 0;
102644 int ii;
 
102645 assert( !ExprHasProperty(pExpr, EP_xIsSelect) );
102646 if( destIfNull!=destIfFalse ){
102647 regCkNull = sqlite3GetTempReg(pParse);
102648 sqlite3VdbeAddOp3(v, OP_BitAnd, rLhs, rLhs, regCkNull);
102649 }
 
102650 for(ii=0; ii<pList->nExpr; ii++){
102651 r2 = sqlite3ExprCodeTemp(pParse, pList->a[ii].pExpr, &regToFree);
 
 
 
 
 
 
102652 if( regCkNull && sqlite3ExprCanBeNull(pList->a[ii].pExpr) ){
102653 sqlite3VdbeAddOp3(v, OP_BitAnd, regCkNull, r2, regCkNull);
102654 }
102655 sqlite3ReleaseTempReg(pParse, regToFree);
102656 if( ii<pList->nExpr-1 || destIfNull!=destIfFalse ){
@@ -102394,14 +103220,10 @@
103220 }
103221 if( aff>SQLITE_AFF_BLOB ){
103222 static const char zAff[] = "B\000C\000D\000E";
103223 assert( SQLITE_AFF_BLOB=='A' );
103224 assert( SQLITE_AFF_TEXT=='B' );
 
 
 
 
103225 sqlite3VdbeAddOp4(v, OP_Affinity, iReg, 1, 0,
103226 &zAff[(aff-'B')*2], P4_STATIC);
103227 }
103228 return iReg;
103229 }
@@ -103035,11 +103857,11 @@
103857 assert( pExpr->affExpr==OE_Rollback
103858 || pExpr->affExpr==OE_Abort
103859 || pExpr->affExpr==OE_Fail
103860 || pExpr->affExpr==OE_Ignore
103861 );
103862 if( !pParse->pTriggerTab && !pParse->nested ){
103863 sqlite3ErrorMsg(pParse,
103864 "RAISE() may only be used within a trigger-program");
103865 return 0;
103866 }
103867 if( pExpr->affExpr==OE_Abort ){
@@ -103049,12 +103871,13 @@
103871 if( pExpr->affExpr==OE_Ignore ){
103872 sqlite3VdbeAddOp4(
103873 v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0);
103874 VdbeCoverage(v);
103875 }else{
103876 sqlite3HaltConstraint(pParse,
103877 pParse->pTriggerTab ? SQLITE_CONSTRAINT_TRIGGER : SQLITE_ERROR,
103878 pExpr->affExpr, pExpr->u.zToken, 0, 0);
103879 }
103880
103881 break;
103882 }
103883 #endif
@@ -104805,10 +105628,26 @@
105628 exit_rename_table:
105629 sqlite3SrcListDelete(db, pSrc);
105630 sqlite3DbFree(db, zName);
105631 db->mDbFlags = savedDbFlags;
105632 }
105633
105634 /*
105635 ** Write code that will raise an error if the table described by
105636 ** zDb and zTab is not empty.
105637 */
105638 static void sqlite3ErrorIfNotEmpty(
105639 Parse *pParse, /* Parsing context */
105640 const char *zDb, /* Schema holding the table */
105641 const char *zTab, /* Table to check for empty */
105642 const char *zErr /* Error message text */
105643 ){
105644 sqlite3NestedParse(pParse,
105645 "SELECT raise(ABORT,%Q) FROM \"%w\".\"%w\"",
105646 zErr, zDb, zTab
105647 );
105648 }
105649
105650 /*
105651 ** This function is called after an "ALTER TABLE ... ADD" statement
105652 ** has been parsed. Argument pColDef contains the text of the new
105653 ** column definition.
@@ -104858,11 +105697,12 @@
105697 if( pCol->colFlags & COLFLAG_PRIMKEY ){
105698 sqlite3ErrorMsg(pParse, "Cannot add a PRIMARY KEY column");
105699 return;
105700 }
105701 if( pNew->pIndex ){
105702 sqlite3ErrorMsg(pParse,
105703 "Cannot add a UNIQUE column");
105704 return;
105705 }
105706 if( (pCol->colFlags & COLFLAG_GENERATED)==0 ){
105707 /* If the default value for the new column was specified with a
105708 ** literal NULL, then set pDflt to 0. This simplifies checking
@@ -104871,19 +105711,18 @@
105711 assert( pDflt==0 || pDflt->op==TK_SPAN );
105712 if( pDflt && pDflt->pLeft->op==TK_NULL ){
105713 pDflt = 0;
105714 }
105715 if( (db->flags&SQLITE_ForeignKeys) && pNew->pFKey && pDflt ){
105716 sqlite3ErrorIfNotEmpty(pParse, zDb, zTab,
105717 "Cannot add a REFERENCES column with non-NULL default value");
 
105718 }
105719 if( pCol->notNull && !pDflt ){
105720 sqlite3ErrorIfNotEmpty(pParse, zDb, zTab,
105721 "Cannot add a NOT NULL column with default value NULL");
 
105722 }
105723
105724
105725 /* Ensure the default expression is something that sqlite3ValueFromExpr()
105726 ** can handle (i.e. not CURRENT_TIME etc.)
105727 */
105728 if( pDflt ){
@@ -104894,18 +105733,17 @@
105733 if( rc!=SQLITE_OK ){
105734 assert( db->mallocFailed == 1 );
105735 return;
105736 }
105737 if( !pVal ){
105738 sqlite3ErrorIfNotEmpty(pParse, zDb, zTab,
105739 "Cannot add a column with non-constant default");
105740 }
105741 sqlite3ValueFree(pVal);
105742 }
105743 }else if( pCol->colFlags & COLFLAG_STORED ){
105744 sqlite3ErrorIfNotEmpty(pParse, zDb, zTab, "cannot add a STORED column");
 
105745 }
105746
105747
105748 /* Modify the CREATE TABLE statement. */
105749 zCol = sqlite3DbStrNDup(db, (char*)pColDef->z, pColDef->n);
@@ -105020,10 +105858,11 @@
105858 }
105859 memcpy(pNew->aCol, pTab->aCol, sizeof(Column)*pNew->nCol);
105860 for(i=0; i<pNew->nCol; i++){
105861 Column *pCol = &pNew->aCol[i];
105862 pCol->zName = sqlite3DbStrDup(db, pCol->zName);
105863 pCol->hName = sqlite3StrIHash(pCol->zName);
105864 pCol->zColl = 0;
105865 pCol->pDflt = 0;
105866 }
105867 pNew->pSchema = db->aDb[iDb].pSchema;
105868 pNew->addColOffset = pTab->addColOffset;
@@ -105248,11 +106087,11 @@
106087 */
106088 SQLITE_PRIVATE void *sqlite3RenameTokenMap(Parse *pParse, void *pPtr, Token *pToken){
106089 RenameToken *pNew;
106090 assert( pPtr || pParse->db->mallocFailed );
106091 renameTokenCheckAll(pParse, pPtr);
106092 if( ALWAYS(pParse->eParseMode!=PARSE_MODE_UNMAP) ){
106093 pNew = sqlite3DbMallocZero(pParse->db, sizeof(RenameToken));
106094 if( pNew ){
106095 pNew->p = pPtr;
106096 pNew->t = *pToken;
106097 pNew->pNext = pParse->pRename;
@@ -105305,10 +106144,25 @@
106144 sqlite3WalkSelect(pWalker, p);
106145 sqlite3RenameExprlistUnmap(pWalker->pParse, pWith->a[i].pCols);
106146 }
106147 }
106148 }
106149
106150 /*
106151 ** Unmap all tokens in the IdList object passed as the second argument.
106152 */
106153 static void unmapColumnIdlistNames(
106154 Parse *pParse,
106155 IdList *pIdList
106156 ){
106157 if( pIdList ){
106158 int ii;
106159 for(ii=0; ii<pIdList->nId; ii++){
106160 sqlite3RenameTokenRemap(pParse, 0, (void*)pIdList->a[ii].zName);
106161 }
106162 }
106163 }
106164
106165 /*
106166 ** Walker callback used by sqlite3RenameExprUnmap().
106167 */
106168 static int renameUnmapSelectCb(Walker *pWalker, Select *p){
@@ -105327,10 +106181,11 @@
106181 if( ALWAYS(p->pSrc) ){ /* Every Select as a SrcList, even if it is empty */
106182 SrcList *pSrc = p->pSrc;
106183 for(i=0; i<pSrc->nSrc; i++){
106184 sqlite3RenameTokenRemap(pParse, 0, (void*)pSrc->a[i].zName);
106185 if( sqlite3WalkExpr(pWalker, pSrc->a[i].pOn) ) return WRC_Abort;
106186 unmapColumnIdlistNames(pParse, pSrc->a[i].pUsing);
106187 }
106188 }
106189
106190 renameWalkWith(pWalker, p);
106191 return WRC_Continue;
@@ -105534,10 +106389,11 @@
106389 renameTokenFind(pParse, pCtx, (void*)zName);
106390 }
106391 }
106392 }
106393 }
106394
106395
106396 /*
106397 ** Parse the SQL statement zSql using Parse object (*p). The Parse object
106398 ** is initialized by this function before it is used.
106399 */
@@ -106446,10 +107302,15 @@
107302 sqlite3 *db = pParse->db;
107303 Db *pDb;
107304 Vdbe *v = sqlite3GetVdbe(pParse);
107305 int aRoot[ArraySize(aTable)];
107306 u8 aCreateTbl[ArraySize(aTable)];
107307 #ifdef SQLITE_ENABLE_STAT4
107308 const int nToOpen = OptimizationEnabled(db,SQLITE_Stat4) ? 2 : 1;
107309 #else
107310 const int nToOpen = 1;
107311 #endif
107312
107313 if( v==0 ) return;
107314 assert( sqlite3BtreeHoldsAllMutexes(db) );
107315 assert( sqlite3VdbeDb(v)==db );
107316 pDb = &db->aDb[iDb];
@@ -106458,12 +107319,13 @@
107319 ** if they do already exist.
107320 */
107321 for(i=0; i<ArraySize(aTable); i++){
107322 const char *zTab = aTable[i].zName;
107323 Table *pStat;
107324 aCreateTbl[i] = 0;
107325 if( (pStat = sqlite3FindTable(db, zTab, pDb->zDbSName))==0 ){
107326 if( i<nToOpen ){
107327 /* The sqlite_statN table does not exist. Create it. Note that a
107328 ** side-effect of the CREATE TABLE statement is to leave the rootpage
107329 ** of the new table in register pParse->regRoot. This is important
107330 ** because the OpenWrite opcode below will be needing it. */
107331 sqlite3NestedParse(pParse,
@@ -106475,11 +107337,10 @@
107337 }else{
107338 /* The table already exists. If zWhere is not NULL, delete all entries
107339 ** associated with the table zWhere. If zWhere is NULL, delete the
107340 ** entire contents of the table. */
107341 aRoot[i] = pStat->tnum;
 
107342 sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab);
107343 if( zWhere ){
107344 sqlite3NestedParse(pParse,
107345 "DELETE FROM %Q.%s WHERE %s=%Q",
107346 pDb->zDbSName, zTab, zWhereType, zWhere
@@ -106494,11 +107355,11 @@
107355 }
107356 }
107357 }
107358
107359 /* Open the sqlite_stat[134] tables for writing. */
107360 for(i=0; i<nToOpen; i++){
107361 assert( i<ArraySize(aTable) );
107362 sqlite3VdbeAddOp4Int(v, OP_OpenWrite, iStatCur+i, aRoot[i], iDb, 3);
107363 sqlite3VdbeChangeP5(v, aCreateTbl[i]);
107364 VdbeComment((v, aTable[i].zName));
107365 }
@@ -106533,13 +107394,16 @@
107394 u32 iHash; /* Tiebreaker hash */
107395 #endif
107396 };
107397 struct StatAccum {
107398 sqlite3 *db; /* Database connection, for malloc() */
107399 tRowcnt nEst; /* Estimated number of rows */
107400 tRowcnt nRow; /* Number of rows visited so far */
107401 int nLimit; /* Analysis row-scan limit */
107402 int nCol; /* Number of columns in index + pk/rowid */
107403 int nKeyCol; /* Number of index columns w/o the pk/rowid */
107404 u8 nSkipAhead; /* Number of times of skip-ahead */
107405 StatSample current; /* Current row as a StatSample */
107406 #ifdef SQLITE_ENABLE_STAT4
107407 tRowcnt nPSample; /* How often to do a periodic sample */
107408 int mxSample; /* Maximum number of samples to accumulate */
107409 u32 iPrn; /* Pseudo-random number used for sampling */
@@ -106615,31 +107479,32 @@
107479 ** Reclaim all memory of a StatAccum structure.
107480 */
107481 static void statAccumDestructor(void *pOld){
107482 StatAccum *p = (StatAccum*)pOld;
107483 #ifdef SQLITE_ENABLE_STAT4
107484 if( p->mxSample ){
107485 int i;
107486 for(i=0; i<p->nCol; i++) sampleClear(p->db, p->aBest+i);
107487 for(i=0; i<p->mxSample; i++) sampleClear(p->db, p->a+i);
107488 sampleClear(p->db, &p->current);
107489 }
107490 #endif
107491 sqlite3DbFree(p->db, p);
107492 }
107493
107494 /*
107495 ** Implementation of the stat_init(N,K,C,L) SQL function. The four parameters
107496 ** are:
107497 ** N: The number of columns in the index including the rowid/pk (note 1)
107498 ** K: The number of columns in the index excluding the rowid/pk.
107499 ** C: Estimated number of rows in the index
107500 ** L: A limit on the number of rows to scan, or 0 for no-limit
107501 **
107502 ** Note 1: In the special case of the covering index that implements a
107503 ** WITHOUT ROWID table, N is the number of PRIMARY KEY columns, not the
107504 ** total number of columns in the table.
107505 **
 
 
107506 ** For indexes on ordinary rowid tables, N==K+1. But for indexes on
107507 ** WITHOUT ROWID tables, N=K+P where P is the number of columns in the
107508 ** PRIMARY KEY of the table. The covering index that implements the
107509 ** original WITHOUT ROWID table as N==K as a special case.
107510 **
@@ -106656,13 +107521,14 @@
107521 StatAccum *p;
107522 int nCol; /* Number of columns in index being sampled */
107523 int nKeyCol; /* Number of key columns */
107524 int nColUp; /* nCol rounded up for alignment */
107525 int n; /* Bytes of space to allocate */
107526 sqlite3 *db = sqlite3_context_db_handle(context); /* Database connection */
107527 #ifdef SQLITE_ENABLE_STAT4
107528 /* Maximum number of samples. 0 if STAT4 data is not collected */
107529 int mxSample = OptimizationEnabled(db,SQLITE_Stat4) ?SQLITE_STAT4_SAMPLES :0;
107530 #endif
107531
107532 /* Decode the three function arguments */
107533 UNUSED_PARAMETER(argc);
107534 nCol = sqlite3_value_int(argv[0]);
@@ -106673,39 +107539,43 @@
107539 assert( nKeyCol>0 );
107540
107541 /* Allocate the space required for the StatAccum object */
107542 n = sizeof(*p)
107543 + sizeof(tRowcnt)*nColUp /* StatAccum.anEq */
107544 + sizeof(tRowcnt)*nColUp; /* StatAccum.anDLt */
107545 #ifdef SQLITE_ENABLE_STAT4
107546 if( mxSample ){
107547 n += sizeof(tRowcnt)*nColUp /* StatAccum.anLt */
107548 + sizeof(StatSample)*(nCol+mxSample) /* StatAccum.aBest[], a[] */
107549 + sizeof(tRowcnt)*3*nColUp*(nCol+mxSample);
107550 }
107551 #endif
 
107552 db = sqlite3_context_db_handle(context);
107553 p = sqlite3DbMallocZero(db, n);
107554 if( p==0 ){
107555 sqlite3_result_error_nomem(context);
107556 return;
107557 }
107558
107559 p->db = db;
107560 p->nEst = sqlite3_value_int64(argv[2]);
107561 p->nRow = 0;
107562 p->nLimit = sqlite3_value_int64(argv[3]);
107563 p->nCol = nCol;
107564 p->nKeyCol = nKeyCol;
107565 p->nSkipAhead = 0;
107566 p->current.anDLt = (tRowcnt*)&p[1];
107567 p->current.anEq = &p->current.anDLt[nColUp];
107568
107569 #ifdef SQLITE_ENABLE_STAT4
107570 p->mxSample = p->nLimit==0 ? mxSample : 0;
107571 if( mxSample ){
107572 u8 *pSpace; /* Allocated space not yet assigned */
107573 int i; /* Used to iterate through p->aSample[] */
107574
107575 p->iGet = -1;
107576 p->nPSample = (tRowcnt)(p->nEst/(mxSample/3+1) + 1);
 
107577 p->current.anLt = &p->current.anEq[nColUp];
107578 p->iPrn = 0x689e962d*(u32)nCol ^ 0xd0944565*(u32)sqlite3_value_int(argv[2]);
107579
107580 /* Set up the StatAccum.a[] and aBest[] arrays */
107581 p->a = (struct StatSample*)&p->current.anLt[nColUp];
@@ -106729,11 +107599,11 @@
107599 ** (given by the 3rd parameter) is never used and can be any positive
107600 ** value. */
107601 sqlite3_result_blob(context, p, sizeof(*p), statAccumDestructor);
107602 }
107603 static const FuncDef statInitFuncdef = {
107604 4, /* nArg */
107605 SQLITE_UTF8, /* funcFlags */
107606 0, /* pUserData */
107607 0, /* pNext */
107608 statInit, /* xSFunc */
107609 0, /* xFinalize */
@@ -106933,14 +107803,17 @@
107803 ** P Pointer to the StatAccum object created by stat_init()
107804 ** C Index of left-most column to differ from previous row
107805 ** R Rowid for the current row. Might be a key record for
107806 ** WITHOUT ROWID tables.
107807 **
107808 ** The purpose of this routine is to collect statistical data and/or
107809 ** samples from the index being analyzed into the StatAccum object.
107810 ** The stat_get() SQL function will be used afterwards to
107811 ** retrieve the information gathered.
107812 **
107813 ** This SQL function usually returns NULL, but might return an integer
107814 ** if it wants the byte-code to do special processing.
107815 **
107816 ** The R parameter is only used for STAT4
107817 */
107818 static void statPush(
107819 sqlite3_context *context,
@@ -106962,11 +107835,11 @@
107835 /* This is the first call to this function. Do initialization. */
107836 for(i=0; i<p->nCol; i++) p->current.anEq[i] = 1;
107837 }else{
107838 /* Second and subsequent calls get processed here */
107839 #ifdef SQLITE_ENABLE_STAT4
107840 if( p->mxSample ) samplePushPrevious(p, iChng);
107841 #endif
107842
107843 /* Update anDLt[], anLt[] and anEq[] to reflect the values that apply
107844 ** to the current row of the index. */
107845 for(i=0; i<iChng; i++){
@@ -106973,30 +107846,29 @@
107846 p->current.anEq[i]++;
107847 }
107848 for(i=iChng; i<p->nCol; i++){
107849 p->current.anDLt[i]++;
107850 #ifdef SQLITE_ENABLE_STAT4
107851 if( p->mxSample ) p->current.anLt[i] += p->current.anEq[i];
107852 #endif
107853 p->current.anEq[i] = 1;
107854 }
107855 }
107856
107857 p->nRow++;
107858 #ifdef SQLITE_ENABLE_STAT4
107859 if( p->mxSample ){
107860 tRowcnt nLt;
107861 if( sqlite3_value_type(argv[2])==SQLITE_INTEGER ){
107862 sampleSetRowidInt64(p->db, &p->current, sqlite3_value_int64(argv[2]));
107863 }else{
107864 sampleSetRowid(p->db, &p->current, sqlite3_value_bytes(argv[2]),
107865 sqlite3_value_blob(argv[2]));
107866 }
107867 p->current.iHash = p->iPrn = p->iPrn*1103515245 + 12345;
107868
107869 nLt = p->current.anLt[p->nCol-1];
 
 
107870 /* Check if this is to be a periodic sample. If so, add it. */
107871 if( (nLt/p->nPSample)!=(nLt+1)/p->nPSample ){
107872 p->current.isPSample = 1;
107873 p->current.iCol = 0;
107874 sampleInsert(p, &p->current, p->nCol-1);
@@ -107008,13 +107880,18 @@
107880 p->current.iCol = i;
107881 if( i>=iChng || sampleIsBetterPost(p, &p->current, &p->aBest[i]) ){
107882 sampleCopy(p, &p->aBest[i], &p->current);
107883 }
107884 }
107885 }else
107886 #endif
107887 if( p->nLimit && p->nRow>(tRowcnt)p->nLimit*(p->nSkipAhead+1) ){
107888 p->nSkipAhead++;
107889 sqlite3_result_int(context, p->current.anDLt[0]>0);
107890 }
 
107891 }
107892
107893 static const FuncDef statPushFuncdef = {
107894 2+IsStat4, /* nArg */
107895 SQLITE_UTF8, /* funcFlags */
107896 0, /* pUserData */
107897 0, /* pNext */
@@ -107062,10 +107939,11 @@
107939 assert( argc==2 );
107940 assert( eCall==STAT_GET_STAT1 || eCall==STAT_GET_NEQ
107941 || eCall==STAT_GET_ROWID || eCall==STAT_GET_NLT
107942 || eCall==STAT_GET_NDLT
107943 );
107944 assert( eCall==STAT_GET_STAT1 || p->mxSample );
107945 if( eCall==STAT_GET_STAT1 )
107946 #else
107947 assert( argc==1 );
107948 #endif
107949 {
@@ -107097,11 +107975,12 @@
107975 if( zRet==0 ){
107976 sqlite3_result_error_nomem(context);
107977 return;
107978 }
107979
107980 sqlite3_snprintf(24, zRet, "%llu",
107981 p->nSkipAhead ? (u64)p->nEst : (u64)p->nRow);
107982 z = zRet + sqlite3Strlen30(zRet);
107983 for(i=0; i<p->nKeyCol; i++){
107984 u64 nDistinct = p->current.anDLt[i] + 1;
107985 u64 iVal = (p->nRow + nDistinct - 1) / nDistinct;
107986 sqlite3_snprintf(24, z, " %llu", iVal);
@@ -107173,20 +108052,20 @@
108052 0, 0, /* xValue, xInverse */
108053 "stat_get", /* zName */
108054 {0}
108055 };
108056
108057 static void callStatGet(Parse *pParse, int regStat, int iParam, int regOut){
108058 #ifdef SQLITE_ENABLE_STAT4
108059 sqlite3VdbeAddOp2(pParse->pVdbe, OP_Integer, iParam, regStat+1);
108060 #elif SQLITE_DEBUG
108061 assert( iParam==STAT_GET_STAT1 );
108062 #else
108063 UNUSED_PARAMETER( iParam );
108064 #endif
108065 assert( regOut!=regStat && regOut!=regStat+1 );
108066 sqlite3VdbeAddFunctionCall(pParse, 0, regStat, regOut, 1+IsStat4,
108067 &statGetFuncdef, 0);
108068 }
108069
108070 /*
108071 ** Generate code to do an analysis of all indices associated with
@@ -107208,16 +108087,15 @@
108087 int i; /* Loop counter */
108088 int jZeroRows = -1; /* Jump from here if number of rows is zero */
108089 int iDb; /* Index of database containing pTab */
108090 u8 needTableCnt = 1; /* True to count the table */
108091 int regNewRowid = iMem++; /* Rowid for the inserted record */
108092 int regStat = iMem++; /* Register to hold StatAccum object */
108093 int regChng = iMem++; /* Index of changed index field */
 
108094 int regRowid = iMem++; /* Rowid argument passed to stat_push() */
 
108095 int regTemp = iMem++; /* Temporary use register */
108096 int regTemp2 = iMem++; /* Second temporary use register */
108097 int regTabname = iMem++; /* Register containing table name */
108098 int regIdxname = iMem++; /* Register containing index name */
108099 int regStat1 = iMem++; /* Value for the stat column of sqlite_stat1 */
108100 int regPrev = iMem; /* MUST BE LAST (see below) */
108101 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
@@ -107341,21 +108219,30 @@
108219 /* Invoke the stat_init() function. The arguments are:
108220 **
108221 ** (1) the number of columns in the index including the rowid
108222 ** (or for a WITHOUT ROWID table, the number of PK columns),
108223 ** (2) the number of columns in the key without the rowid/pk
108224 ** (3) estimated number of rows in the index,
 
 
 
108225 */
108226 sqlite3VdbeAddOp2(v, OP_Integer, nCol, regStat+1);
108227 assert( regRowid==regStat+2 );
108228 sqlite3VdbeAddOp2(v, OP_Integer, pIdx->nKeyCol, regRowid);
108229 #ifdef SQLITE_ENABLE_STAT4
108230 if( OptimizationEnabled(db, SQLITE_Stat4) ){
108231 sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regTemp);
108232 addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
108233 VdbeCoverage(v);
108234 }else
108235 #endif
108236 {
108237 addrRewind = sqlite3VdbeAddOp1(v, OP_Rewind, iIdxCur);
108238 VdbeCoverage(v);
108239 sqlite3VdbeAddOp3(v, OP_Count, iIdxCur, regTemp, 1);
108240 }
108241 assert( regTemp2==regStat+4 );
108242 sqlite3VdbeAddOp2(v, OP_Integer, db->nAnalysisLimit, regTemp2);
108243 sqlite3VdbeAddFunctionCall(pParse, 0, regStat+1, regStat, 4,
108244 &statInitFuncdef, 0);
108245
108246 /* Implementation of the following:
108247 **
108248 ** Rewind csr
@@ -107362,12 +108249,10 @@
108249 ** if eof(csr) goto end_of_scan;
108250 ** regChng = 0
108251 ** goto next_push_0;
108252 **
108253 */
 
 
108254 sqlite3VdbeAddOp2(v, OP_Integer, 0, regChng);
108255 addrNextRow = sqlite3VdbeCurrentAddr(v);
108256
108257 if( nColTest>0 ){
108258 int endDistinctTest = sqlite3VdbeMakeLabel(pParse);
@@ -107396,10 +108281,11 @@
108281 }
108282 for(i=0; i<nColTest; i++){
108283 char *pColl = (char*)sqlite3LocateCollSeq(pParse, pIdx->azColl[i]);
108284 sqlite3VdbeAddOp2(v, OP_Integer, i, regChng);
108285 sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regTemp);
108286 VdbeComment((v, "%s.column(%d)", pIdx->zName, i));
108287 aGotoChng[i] =
108288 sqlite3VdbeAddOp4(v, OP_Ne, regTemp, 0, regPrev+i, pColl, P4_COLLSEQ);
108289 sqlite3VdbeChangeP5(v, SQLITE_NULLEQ);
108290 VdbeCoverage(v);
108291 }
@@ -107416,10 +108302,11 @@
108302 */
108303 sqlite3VdbeJumpHere(v, addrNextRow-1);
108304 for(i=0; i<nColTest; i++){
108305 sqlite3VdbeJumpHere(v, aGotoChng[i]);
108306 sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, i, regPrev+i);
108307 VdbeComment((v, "%s.column(%d)", pIdx->zName, i));
108308 }
108309 sqlite3VdbeResolveLabel(v, endDistinctTest);
108310 sqlite3DbFree(db, aGotoChng);
108311 }
108312
@@ -107429,34 +108316,50 @@
108316 ** stat_push(P, regChng, regRowid) // 3rd parameter STAT4 only
108317 ** Next csr
108318 ** if !eof(csr) goto next_row;
108319 */
108320 #ifdef SQLITE_ENABLE_STAT4
108321 if( OptimizationEnabled(db, SQLITE_Stat4) ){
108322 assert( regRowid==(regStat+2) );
108323 if( HasRowid(pTab) ){
108324 sqlite3VdbeAddOp2(v, OP_IdxRowid, iIdxCur, regRowid);
108325 }else{
108326 Index *pPk = sqlite3PrimaryKeyIndex(pIdx->pTable);
108327 int j, k, regKey;
108328 regKey = sqlite3GetTempRange(pParse, pPk->nKeyCol);
108329 for(j=0; j<pPk->nKeyCol; j++){
108330 k = sqlite3TableColumnToIndex(pIdx, pPk->aiColumn[j]);
108331 assert( k>=0 && k<pIdx->nColumn );
108332 sqlite3VdbeAddOp3(v, OP_Column, iIdxCur, k, regKey+j);
108333 VdbeComment((v, "%s.column(%d)", pIdx->zName, i));
108334 }
108335 sqlite3VdbeAddOp3(v, OP_MakeRecord, regKey, pPk->nKeyCol, regRowid);
108336 sqlite3ReleaseTempRange(pParse, regKey, pPk->nKeyCol);
108337 }
108338 }
108339 #endif
108340 assert( regChng==(regStat+1) );
108341 {
108342 sqlite3VdbeAddFunctionCall(pParse, 1, regStat, regTemp, 2+IsStat4,
108343 &statPushFuncdef, 0);
108344 if( db->nAnalysisLimit ){
108345 int j1, j2, j3;
108346 j1 = sqlite3VdbeAddOp1(v, OP_IsNull, regTemp); VdbeCoverage(v);
108347 j2 = sqlite3VdbeAddOp1(v, OP_If, regTemp); VdbeCoverage(v);
108348 j3 = sqlite3VdbeAddOp4Int(v, OP_SeekGT, iIdxCur, 0, regPrev, 1);
108349 VdbeCoverage(v);
108350 sqlite3VdbeJumpHere(v, j1);
108351 sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
108352 sqlite3VdbeJumpHere(v, j2);
108353 sqlite3VdbeJumpHere(v, j3);
108354 }else{
108355 sqlite3VdbeAddOp2(v, OP_Next, iIdxCur, addrNextRow); VdbeCoverage(v);
108356 }
108357 }
108358
108359 /* Add the entry to the stat1 table. */
108360 callStatGet(pParse, regStat, STAT_GET_STAT1, regStat1);
108361 assert( "BBB"[0]==SQLITE_AFF_TEXT );
108362 sqlite3VdbeAddOp4(v, OP_MakeRecord, regTabname, 3, regTemp, "BBB", 0);
108363 sqlite3VdbeAddOp2(v, OP_NewRowid, iStatCur, regNewRowid);
108364 sqlite3VdbeAddOp3(v, OP_Insert, iStatCur, regTemp, regNewRowid);
108365 #ifdef SQLITE_ENABLE_PREUPDATE_HOOK
@@ -107464,11 +108367,11 @@
108367 #endif
108368 sqlite3VdbeChangeP5(v, OPFLAG_APPEND);
108369
108370 /* Add the entries to the stat4 table. */
108371 #ifdef SQLITE_ENABLE_STAT4
108372 if( OptimizationEnabled(db, SQLITE_Stat4) && db->nAnalysisLimit==0 ){
108373 int regEq = regStat1;
108374 int regLt = regStat1+1;
108375 int regDLt = regStat1+2;
108376 int regSample = regStat1+3;
108377 int regCol = regStat1+4;
@@ -107478,16 +108381,16 @@
108381 u8 seekOp = HasRowid(pTab) ? OP_NotExists : OP_NotFound;
108382
108383 pParse->nMem = MAX(pParse->nMem, regCol+nCol);
108384
108385 addrNext = sqlite3VdbeCurrentAddr(v);
108386 callStatGet(pParse, regStat, STAT_GET_ROWID, regSampleRowid);
108387 addrIsNull = sqlite3VdbeAddOp1(v, OP_IsNull, regSampleRowid);
108388 VdbeCoverage(v);
108389 callStatGet(pParse, regStat, STAT_GET_NEQ, regEq);
108390 callStatGet(pParse, regStat, STAT_GET_NLT, regLt);
108391 callStatGet(pParse, regStat, STAT_GET_NDLT, regDLt);
108392 sqlite3VdbeAddOp4Int(v, seekOp, iTabCur, addrNext, regSampleRowid, 0);
108393 VdbeCoverage(v);
108394 for(i=0; i<nCol; i++){
108395 sqlite3ExprCodeLoadIndexColumn(pParse, pIdx, iTabCur, i, regCol+i);
108396 }
@@ -109619,10 +110522,11 @@
110522 int i;
110523 Column *pCol;
110524 assert( pTable!=0 );
110525 if( (pCol = pTable->aCol)!=0 ){
110526 for(i=0; i<pTable->nCol; i++, pCol++){
110527 assert( pCol->zName==0 || pCol->hName==sqlite3StrIHash(pCol->zName) );
110528 sqlite3DbFree(db, pCol->zName);
110529 sqlite3ExprDelete(db, pCol->pDflt);
110530 sqlite3DbFree(db, pCol->zColl);
110531 }
110532 sqlite3DbFree(db, pTable->aCol);
@@ -110267,10 +111171,11 @@
111171 p->aCol = aNew;
111172 }
111173 pCol = &p->aCol[p->nCol];
111174 memset(pCol, 0, sizeof(p->aCol[0]));
111175 pCol->zName = z;
111176 pCol->hName = sqlite3StrIHash(z);
111177 sqlite3ColumnPropertiesFromName(p, pCol);
111178
111179 if( pType->n==0 ){
111180 /* If there is no type specified, columns have the default affinity
111181 ** 'BLOB' with a default size of 4 bytes. */
@@ -113657,11 +114562,11 @@
114562 pParse->rc = rc;
114563 return 1;
114564 }
114565 db->aDb[1].pBt = pBt;
114566 assert( db->aDb[1].pSchema );
114567 if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize, 0, 0) ){
114568 sqlite3OomFault(db);
114569 return 1;
114570 }
114571 }
114572 return 0;
@@ -113768,11 +114673,11 @@
114673 char *p4, /* Error message */
114674 i8 p4type, /* P4_STATIC or P4_TRANSIENT */
114675 u8 p5Errmsg /* P5_ErrMsg type */
114676 ){
114677 Vdbe *v = sqlite3GetVdbe(pParse);
114678 assert( (errCode&0xff)==SQLITE_CONSTRAINT || pParse->nested );
114679 if( onError==OE_Abort ){
114680 sqlite3MayAbort(pParse);
114681 }
114682 sqlite3VdbeAddOp4(v, OP_Halt, errCode, onError, 0, p4, p4type);
114683 sqlite3VdbeChangeP5(v, p5Errmsg);
@@ -115481,10 +116386,11 @@
116386 VdbeModuleComment((v, "GenRowIdxDel for %s", pIdx->zName));
116387 r1 = sqlite3GenerateIndexKey(pParse, pIdx, iDataCur, 0, 1,
116388 &iPartIdxLabel, pPrior, r1);
116389 sqlite3VdbeAddOp3(v, OP_IdxDelete, iIdxCur+i, r1,
116390 pIdx->uniqNotNull ? pIdx->nKeyCol : pIdx->nColumn);
116391 sqlite3VdbeChangeP5(v, 1); /* Cause IdxDelete to error if no entry found */
116392 sqlite3ResolvePartIdxLabel(pParse, iPartIdxLabel);
116393 pPrior = pIdx;
116394 }
116395 }
116396
@@ -122468,10 +123374,11 @@
123374 const char *(*filename_wal)(const char*);
123375 /* Version 3.32.0 and later */
123376 char *(*create_filename)(const char*,const char*,const char*,
123377 int,const char**);
123378 void (*free_filename)(char*);
123379 sqlite3_file *(*database_file_object)(const char*);
123380 };
123381
123382 /*
123383 ** This is the function signature used for all extension entry points. It
123384 ** is also defined in the file "loadext.c".
@@ -122771,10 +123678,11 @@
123678 #define sqlite3_filename_journal sqlite3_api->filename_journal
123679 #define sqlite3_filename_wal sqlite3_api->filename_wal
123680 /* Version 3.32.0 and later */
123681 #define sqlite3_create_filename sqlite3_api->create_filename
123682 #define sqlite3_free_filename sqlite3_api->free_filename
123683 #define sqlite3_database_file_object sqlite3_api->database_file_object
123684 #endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
123685
123686 #if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
123687 /* This case when the file really is being compiled as a loadable
123688 ** extension */
@@ -123252,11 +124160,20 @@
124160 sqlite3_filename_journal,
124161 sqlite3_filename_wal,
124162 /* Version 3.32.0 and later */
124163 sqlite3_create_filename,
124164 sqlite3_free_filename,
124165 sqlite3_database_file_object,
124166 };
124167
124168 /* True if x is the directory separator character
124169 */
124170 #if SQLITE_OS_WIN
124171 # define DirSep(X) ((X)=='/'||(X)=='\\')
124172 #else
124173 # define DirSep(X) ((X)=='/')
124174 #endif
124175
124176 /*
124177 ** Attempt to load an SQLite extension library contained in the file
124178 ** zFile. The entry point is zProc. zProc may be 0 in which case a
124179 ** default entry point name (sqlite3_extension_init) is used. Use
@@ -123355,11 +124272,11 @@
124272 if( zAltEntry==0 ){
124273 sqlite3OsDlClose(pVfs, handle);
124274 return SQLITE_NOMEM_BKPT;
124275 }
124276 memcpy(zAltEntry, "sqlite3_", 8);
124277 for(iFile=ncFile-1; iFile>=0 && !DirSep(zFile[iFile]); iFile--){}
124278 iFile++;
124279 if( sqlite3_strnicmp(zFile+iFile, "lib", 3)==0 ) iFile += 3;
124280 for(iEntry=8; (c = zFile[iFile])!=0 && c!='.'; iFile++){
124281 if( sqlite3Isalpha(c) ){
124282 zAltEntry[iEntry++] = (char)sqlite3UpperToLower[(unsigned)c];
@@ -123659,53 +124576,54 @@
124576 ** that script and rerun it.
124577 */
124578
124579 /* The various pragma types */
124580 #define PragTyp_ACTIVATE_EXTENSIONS 0
124581 #define PragTyp_ANALYSIS_LIMIT 1
124582 #define PragTyp_HEADER_VALUE 2
124583 #define PragTyp_AUTO_VACUUM 3
124584 #define PragTyp_FLAG 4
124585 #define PragTyp_BUSY_TIMEOUT 5
124586 #define PragTyp_CACHE_SIZE 6
124587 #define PragTyp_CACHE_SPILL 7
124588 #define PragTyp_CASE_SENSITIVE_LIKE 8
124589 #define PragTyp_COLLATION_LIST 9
124590 #define PragTyp_COMPILE_OPTIONS 10
124591 #define PragTyp_DATA_STORE_DIRECTORY 11
124592 #define PragTyp_DATABASE_LIST 12
124593 #define PragTyp_DEFAULT_CACHE_SIZE 13
124594 #define PragTyp_ENCODING 14
124595 #define PragTyp_FOREIGN_KEY_CHECK 15
124596 #define PragTyp_FOREIGN_KEY_LIST 16
124597 #define PragTyp_FUNCTION_LIST 17
124598 #define PragTyp_HARD_HEAP_LIMIT 18
124599 #define PragTyp_INCREMENTAL_VACUUM 19
124600 #define PragTyp_INDEX_INFO 20
124601 #define PragTyp_INDEX_LIST 21
124602 #define PragTyp_INTEGRITY_CHECK 22
124603 #define PragTyp_JOURNAL_MODE 23
124604 #define PragTyp_JOURNAL_SIZE_LIMIT 24
124605 #define PragTyp_LOCK_PROXY_FILE 25
124606 #define PragTyp_LOCKING_MODE 26
124607 #define PragTyp_PAGE_COUNT 27
124608 #define PragTyp_MMAP_SIZE 28
124609 #define PragTyp_MODULE_LIST 29
124610 #define PragTyp_OPTIMIZE 30
124611 #define PragTyp_PAGE_SIZE 31
124612 #define PragTyp_PRAGMA_LIST 32
124613 #define PragTyp_SECURE_DELETE 33
124614 #define PragTyp_SHRINK_MEMORY 34
124615 #define PragTyp_SOFT_HEAP_LIMIT 35
124616 #define PragTyp_SYNCHRONOUS 36
124617 #define PragTyp_TABLE_INFO 37
124618 #define PragTyp_TEMP_STORE 38
124619 #define PragTyp_TEMP_STORE_DIRECTORY 39
124620 #define PragTyp_THREADS 40
124621 #define PragTyp_WAL_AUTOCHECKPOINT 41
124622 #define PragTyp_WAL_CHECKPOINT 42
124623 #define PragTyp_LOCK_STATUS 43
124624 #define PragTyp_STATS 44
124625
124626 /* Property flags associated with various pragma. */
124627 #define PragFlg_NeedSchema 0x01 /* Force schema load before running */
124628 #define PragFlg_NoColumns 0x02 /* OP_ResultRow called with zero columns */
124629 #define PragFlg_NoColumns1 0x04 /* zero columns if RHS argument is present */
@@ -123792,10 +124710,15 @@
124710 /* ePragTyp: */ PragTyp_ACTIVATE_EXTENSIONS,
124711 /* ePragFlg: */ 0,
124712 /* ColNames: */ 0, 0,
124713 /* iArg: */ 0 },
124714 #endif
124715 {/* zName: */ "analysis_limit",
124716 /* ePragTyp: */ PragTyp_ANALYSIS_LIMIT,
124717 /* ePragFlg: */ PragFlg_Result0,
124718 /* ColNames: */ 0, 0,
124719 /* iArg: */ 0 },
124720 #if !defined(SQLITE_OMIT_SCHEMA_VERSION_PRAGMAS)
124721 {/* zName: */ "application_id",
124722 /* ePragTyp: */ PragTyp_HEADER_VALUE,
124723 /* ePragFlg: */ PragFlg_NoColumns1|PragFlg_Result0,
124724 /* ColNames: */ 0, 0,
@@ -124292,11 +125215,11 @@
125215 /* ePragFlg: */ PragFlg_Result0|PragFlg_NoColumns1,
125216 /* ColNames: */ 0, 0,
125217 /* iArg: */ SQLITE_WriteSchema|SQLITE_NoSchemaError },
125218 #endif
125219 };
125220 /* Number of pragmas: 67 on by default, 77 total. */
125221
125222 /************** End of pragma.h **********************************************/
125223 /************** Continuing where we left off in pragma.c *********************/
125224
125225 /*
@@ -124822,11 +125745,11 @@
125745 }else{
125746 /* Malloc may fail when setting the page-size, as there is an internal
125747 ** buffer that the pager module resizes using sqlite3_realloc().
125748 */
125749 db->nextPagesize = sqlite3Atoi(zRight);
125750 if( SQLITE_NOMEM==sqlite3BtreeSetPageSize(pBt, db->nextPagesize,0,0) ){
125751 sqlite3OomFault(db);
125752 }
125753 }
125754 break;
125755 }
@@ -125996,11 +126919,10 @@
126919 sqlite3ResolvePartIdxLabel(pParse, jmp3);
126920 }
126921 }
126922 sqlite3VdbeAddOp2(v, OP_Next, iDataCur, loopTop); VdbeCoverage(v);
126923 sqlite3VdbeJumpHere(v, loopTop-1);
 
126924 if( !isQuick ){
126925 sqlite3VdbeLoadString(v, 2, "wrong # of entries in index ");
126926 for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){
126927 if( pPk==pIdx ) continue;
126928 sqlite3VdbeAddOp2(v, OP_Count, iIdxCur+j, 3);
@@ -126010,11 +126932,10 @@
126932 sqlite3VdbeAddOp3(v, OP_Concat, 4, 2, 3);
126933 integrityCheckResultRow(v);
126934 sqlite3VdbeJumpHere(v, addr);
126935 }
126936 }
 
126937 }
126938 }
126939 {
126940 static const int iLn = VDBE_OFFSET_LINENO(2);
126941 static const VdbeOpList endCode[] = {
@@ -126444,10 +127365,29 @@
127365 sqlite3_limit(db, SQLITE_LIMIT_WORKER_THREADS, (int)(N&0x7fffffff));
127366 }
127367 returnSingleInt(v, sqlite3_limit(db, SQLITE_LIMIT_WORKER_THREADS, -1));
127368 break;
127369 }
127370
127371 /*
127372 ** PRAGMA analysis_limit
127373 ** PRAGMA analysis_limit = N
127374 **
127375 ** Configure the maximum number of rows that ANALYZE will examine
127376 ** in each index that it looks at. Return the new limit.
127377 */
127378 case PragTyp_ANALYSIS_LIMIT: {
127379 sqlite3_int64 N;
127380 if( zRight
127381 && sqlite3DecOrHexToI64(zRight, &N)==SQLITE_OK
127382 && N>=0
127383 ){
127384 db->nAnalysisLimit = (int)(N&0x7fffffff);
127385 }
127386 returnSingleInt(v, db->nAnalysisLimit);
127387 break;
127388 }
127389
127390 #if defined(SQLITE_DEBUG) || defined(SQLITE_TEST)
127391 /*
127392 ** Report the current state of file logs for all databases
127393 */
@@ -129768,10 +130708,11 @@
130708 }
130709 zName = sqlite3MPrintf(db, "%.*z:%u", nName, zName, ++cnt);
130710 if( cnt>3 ) sqlite3_randomness(sizeof(cnt), &cnt);
130711 }
130712 pCol->zName = zName;
130713 pCol->hName = sqlite3StrIHash(zName);
130714 sqlite3ColumnPropertiesFromName(0, pCol);
130715 if( zName && sqlite3HashInsert(&ht, zName, pCol)==pCol ){
130716 sqlite3OomFault(db);
130717 }
130718 }
@@ -131221,11 +132162,14 @@
132162 if( ExprHasProperty(pExpr, EP_FromJoin)
132163 && pExpr->iRightJoinTable==pSubst->iTable
132164 ){
132165 pExpr->iRightJoinTable = pSubst->iNewTable;
132166 }
132167 if( pExpr->op==TK_COLUMN
132168 && pExpr->iTable==pSubst->iTable
132169 && !ExprHasProperty(pExpr, EP_FixedCol)
132170 ){
132171 if( pExpr->iColumn<0 ){
132172 pExpr->op = TK_NULL;
132173 }else{
132174 Expr *pNew;
132175 Expr *pCopy = pSubst->pEList->a[pExpr->iColumn].pExpr;
@@ -131239,10 +132183,11 @@
132183 if( pSubst->isLeftJoin && pCopy->op!=TK_COLUMN ){
132184 memset(&ifNullRow, 0, sizeof(ifNullRow));
132185 ifNullRow.op = TK_IF_NULL_ROW;
132186 ifNullRow.pLeft = pCopy;
132187 ifNullRow.iTable = pSubst->iNewTable;
132188 ifNullRow.flags = EP_Skip;
132189 pCopy = &ifNullRow;
132190 }
132191 testcase( ExprHasProperty(pCopy, EP_Subquery) );
132192 pNew = sqlite3ExprDup(db, pCopy, 0);
132193 if( pNew && pSubst->isLeftJoin ){
@@ -133133,10 +134078,11 @@
134078 Vdbe *v = pParse->pVdbe;
134079 int i;
134080 struct AggInfo_func *pFunc;
134081 int nReg = pAggInfo->nFunc + pAggInfo->nColumn;
134082 if( nReg==0 ) return;
134083 if( pParse->nErr ) return;
134084 #ifdef SQLITE_DEBUG
134085 /* Verify that all AggInfo registers are within the range specified by
134086 ** AggInfo.mnReg..AggInfo.mxReg */
134087 assert( nReg==pAggInfo->mxReg-pAggInfo->mnReg+1 );
134088 for(i=0; i<pAggInfo->nColumn; i++){
@@ -134402,11 +135348,10 @@
135348 VdbeComment((v, "indicate accumulator empty"));
135349 sqlite3VdbeAddOp1(v, OP_Return, regReset);
135350
135351 } /* endif pGroupBy. Begin aggregate queries without GROUP BY: */
135352 else {
 
135353 Table *pTab;
135354 if( (pTab = isSimpleCount(p, &sAggInfo))!=0 ){
135355 /* If isSimpleCount() returns a pointer to a Table structure, then
135356 ** the SQL statement is of the form:
135357 **
@@ -134438,17 +135383,19 @@
135383 **
135384 ** In practice the KeyInfo structure will not be used. It is only
135385 ** passed to keep OP_OpenRead happy.
135386 */
135387 if( !HasRowid(pTab) ) pBest = sqlite3PrimaryKeyIndex(pTab);
135388 if( !p->pSrc->a[0].fg.notIndexed ){
135389 for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){
135390 if( pIdx->bUnordered==0
135391 && pIdx->szIdxRow<pTab->szTabRow
135392 && pIdx->pPartIdxWhere==0
135393 && (!pBest || pIdx->szIdxRow<pBest->szIdxRow)
135394 ){
135395 pBest = pIdx;
135396 }
135397 }
135398 }
135399 if( pBest ){
135400 iRoot = pBest->tnum;
135401 pKeyInfo = sqlite3KeyInfoOfIndex(pParse, pBest);
@@ -134460,13 +135407,11 @@
135407 sqlite3VdbeChangeP4(v, -1, (char *)pKeyInfo, P4_KEYINFO);
135408 }
135409 sqlite3VdbeAddOp2(v, OP_Count, iCsr, sAggInfo.aFunc[0].iMem);
135410 sqlite3VdbeAddOp1(v, OP_Close, iCsr);
135411 explainSimpleCount(pParse, pTab, pBest);
135412 }else{
 
 
135413 int regAcc = 0; /* "populate accumulators" flag */
135414
135415 /* If there are accumulator registers but no min() or max() functions
135416 ** without FILTER clauses, allocate register regAcc. Register regAcc
135417 ** will contain 0 the first time the inner loop runs, and 1 thereafter.
@@ -137542,11 +138487,11 @@
138487 sqlite3SetString(pzErrMsg, db, "output file already exists");
138488 goto end_of_vacuum;
138489 }
138490 db->mDbFlags |= DBFLAG_VacuumInto;
138491 }
138492 nRes = sqlite3BtreeGetRequestedReserve(pMain);
138493
138494 sqlite3BtreeSetCacheSize(pTemp, db->aDb[iDb].pSchema->cache_size);
138495 sqlite3BtreeSetSpillSize(pTemp, sqlite3BtreeSetSpillSize(pMain,0));
138496 sqlite3BtreeSetPagerFlags(pTemp, PAGER_SYNCHRONOUS_OFF|PAGER_CACHESPILL);
138497
@@ -137686,11 +138631,11 @@
138631 db->mDbFlags = saved_mDbFlags;
138632 db->flags = saved_flags;
138633 db->nChange = saved_nChange;
138634 db->nTotalChange = saved_nTotalChange;
138635 db->mTrace = saved_mTrace;
138636 sqlite3BtreeSetPageSize(pMain, -1, 0, 1);
138637
138638 /* Currently there is an SQL level transaction open on the vacuum
138639 ** database. No locks are held on any other files (since the main file
138640 ** was committed at the btree level). So it safe to end the transaction
138641 ** by manually setting the autoCommit flag to true and detaching the
@@ -156491,14 +157436,15 @@
157436 ** simplify to constants 0 (false) and 1 (true), respectively,
157437 ** regardless of the value of expr1.
157438 */
157439 sqlite3ExprUnmapAndDelete(pParse, yymsp[-4].minor.yy202);
157440 yymsp[-4].minor.yy202 = sqlite3Expr(pParse->db, TK_INTEGER, yymsp[-3].minor.yy192 ? "1" : "0");
157441 }else if( yymsp[-1].minor.yy242->nExpr==1 && sqlite3ExprIsConstant(yymsp[-1].minor.yy242->a[0].pExpr) ){
157442 Expr *pRHS = yymsp[-1].minor.yy242->a[0].pExpr;
157443 yymsp[-1].minor.yy242->a[0].pExpr = 0;
157444 sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy242);
157445 pRHS = sqlite3PExpr(pParse, TK_UPLUS, pRHS, 0);
157446 yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_EQ, yymsp[-4].minor.yy202, pRHS);
157447 if( yymsp[-3].minor.yy192 ) yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_NOT, yymsp[-4].minor.yy202, 0);
157448 }else{
157449 yymsp[-4].minor.yy202 = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy202, 0);
157450 if( yymsp[-4].minor.yy202 ){
@@ -158376,11 +159322,11 @@
159322 VVA_ONLY( u8 startedWithOom = db->mallocFailed );
159323
159324 assert( zSql!=0 );
159325 mxSqlLen = db->aLimit[SQLITE_LIMIT_SQL_LENGTH];
159326 if( db->nVdbeActive==0 ){
159327 AtomicStore(&db->u1.isInterrupted, 0);
159328 }
159329 pParse->rc = SQLITE_OK;
159330 pParse->zTail = zSql;
159331 assert( pzErrMsg!=0 );
159332 #ifdef SQLITE_DEBUG
@@ -158421,11 +159367,11 @@
159367 );
159368 #else
159369 if( tokenType>=TK_SPACE ){
159370 assert( tokenType==TK_SPACE || tokenType==TK_ILLEGAL );
159371 #endif /* SQLITE_OMIT_WINDOWFUNC */
159372 if( AtomicLoad(&db->u1.isInterrupted) ){
159373 pParse->rc = SQLITE_INTERRUPT;
159374 break;
159375 }
159376 if( tokenType==TK_SPACE ){
159377 zSql += n;
@@ -159088,19 +160034,82 @@
160034
160035
160036 /************** End of sqliteicu.h *******************************************/
160037 /************** Continuing where we left off in main.c ***********************/
160038 #endif
160039
160040 /*
160041 ** This is an extension initializer that is a no-op and always
160042 ** succeeds, except that it fails if the fault-simulation is set
160043 ** to 500.
160044 */
160045 static int sqlite3TestExtInit(sqlite3 *db){
160046 (void)db;
160047 return sqlite3FaultSim(500);
160048 }
160049
160050
160051 /*
160052 ** Forward declarations of external module initializer functions
160053 ** for modules that need them.
160054 */
160055 #ifdef SQLITE_ENABLE_FTS1
160056 SQLITE_PRIVATE int sqlite3Fts1Init(sqlite3*);
160057 #endif
160058 #ifdef SQLITE_ENABLE_FTS2
160059 SQLITE_PRIVATE int sqlite3Fts2Init(sqlite3*);
160060 #endif
160061 #ifdef SQLITE_ENABLE_FTS5
160062 SQLITE_PRIVATE int sqlite3Fts5Init(sqlite3*);
160063 #endif
160064 #ifdef SQLITE_ENABLE_JSON1
160065 SQLITE_PRIVATE int sqlite3Json1Init(sqlite3*);
160066 #endif
160067 #ifdef SQLITE_ENABLE_STMTVTAB
160068 SQLITE_PRIVATE int sqlite3StmtVtabInit(sqlite3*);
160069 #endif
160070
160071 /*
160072 ** An array of pointers to extension initializer functions for
160073 ** built-in extensions.
160074 */
160075 static int (*const sqlite3BuiltinExtensions[])(sqlite3*) = {
160076 #ifdef SQLITE_ENABLE_FTS1
160077 sqlite3Fts1Init,
160078 #endif
160079 #ifdef SQLITE_ENABLE_FTS2
160080 sqlite3Fts2Init,
160081 #endif
160082 #ifdef SQLITE_ENABLE_FTS3
160083 sqlite3Fts3Init,
160084 #endif
160085 #ifdef SQLITE_ENABLE_FTS5
160086 sqlite3Fts5Init,
160087 #endif
160088 #if defined(SQLITE_ENABLE_ICU) || defined(SQLITE_ENABLE_ICU_COLLATIONS)
160089 sqlite3IcuInit,
160090 #endif
160091 #ifdef SQLITE_ENABLE_RTREE
160092 sqlite3RtreeInit,
160093 #endif
160094 #ifdef SQLITE_ENABLE_DBPAGE_VTAB
160095 sqlite3DbpageRegister,
160096 #endif
160097 #ifdef SQLITE_ENABLE_DBSTAT_VTAB
160098 sqlite3DbstatRegister,
160099 #endif
160100 sqlite3TestExtInit,
160101 #ifdef SQLITE_ENABLE_JSON1
160102 sqlite3Json1Init,
160103 #endif
160104 #ifdef SQLITE_ENABLE_STMTVTAB
160105 sqlite3StmtVtabInit,
160106 #endif
160107 #ifdef SQLITE_ENABLE_BYTECODE_VTAB
160108 sqlite3VdbeBytecodeVtabInit,
160109 #endif
160110 };
160111
160112 #ifndef SQLITE_AMALGAMATION
160113 /* IMPLEMENTATION-OF: R-46656-45156 The sqlite3_version[] string constant
160114 ** contains the text of SQLITE_VERSION macro.
160115 */
@@ -160614,12 +161623,11 @@
161623 ** Return non-zero to retry the lock. Return zero to stop trying
161624 ** and cause SQLite to return SQLITE_BUSY.
161625 */
161626 static int sqliteDefaultBusyCallback(
161627 void *ptr, /* Database connection */
161628 int count /* Number of times table has been busy */
 
161629 ){
161630 #if SQLITE_OS_WIN || HAVE_USLEEP
161631 /* This case is for systems that have support for sleeping for fractions of
161632 ** a second. Examples: All windows systems, unix systems with usleep() */
161633 static const u8 delays[] =
@@ -160629,23 +161637,10 @@
161637 # define NDELAY ArraySize(delays)
161638 sqlite3 *db = (sqlite3 *)ptr;
161639 int tmout = db->busyTimeout;
161640 int delay, prior;
161641
 
 
 
 
 
 
 
 
 
 
 
 
 
161642 assert( count>=0 );
161643 if( count < NDELAY ){
161644 delay = delays[count];
161645 prior = totals[count];
161646 }else{
@@ -160661,11 +161656,10 @@
161656 #else
161657 /* This case for unix systems that lack usleep() support. Sleeping
161658 ** must be done in increments of whole seconds */
161659 sqlite3 *db = (sqlite3 *)ptr;
161660 int tmout = ((sqlite3 *)ptr)->busyTimeout;
 
161661 if( (count+1)*1000 > tmout ){
161662 return 0;
161663 }
161664 sqlite3OsSleep(db->pVfs, 1000000);
161665 return 1;
@@ -160679,23 +161673,14 @@
161673 ** lock on VFS file pFile.
161674 **
161675 ** If this routine returns non-zero, the lock is retried. If it
161676 ** returns 0, the operation aborts with an SQLITE_BUSY error.
161677 */
161678 SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler *p){
161679 int rc;
161680 if( p->xBusyHandler==0 || p->nBusy<0 ) return 0;
161681 rc = p->xBusyHandler(p->pBusyArg, p->nBusy);
 
 
 
 
 
 
 
 
 
161682 if( rc==0 ){
161683 p->nBusy = -1;
161684 }else{
161685 p->nBusy++;
161686 }
@@ -160716,11 +161701,10 @@
161701 #endif
161702 sqlite3_mutex_enter(db->mutex);
161703 db->busyHandler.xBusyHandler = xBusy;
161704 db->busyHandler.pBusyArg = pArg;
161705 db->busyHandler.nBusy = 0;
 
161706 db->busyTimeout = 0;
161707 sqlite3_mutex_leave(db->mutex);
161708 return SQLITE_OK;
161709 }
161710
@@ -160767,11 +161751,10 @@
161751 #endif
161752 if( ms>0 ){
161753 sqlite3_busy_handler(db, (int(*)(void*,int))sqliteDefaultBusyCallback,
161754 (void*)db);
161755 db->busyTimeout = ms;
 
161756 }else{
161757 sqlite3_busy_handler(db, 0, 0);
161758 }
161759 return SQLITE_OK;
161760 }
@@ -160784,11 +161767,11 @@
161767 if( !sqlite3SafetyCheckOk(db) && (db==0 || db->magic!=SQLITE_MAGIC_ZOMBIE) ){
161768 (void)SQLITE_MISUSE_BKPT;
161769 return;
161770 }
161771 #endif
161772 AtomicStore(&db->u1.isInterrupted, 1);
161773 }
161774
161775
161776 /*
161777 ** This function is exactly the same as sqlite3_create_function(), except
@@ -161406,11 +162389,11 @@
162389 rc = sqlite3ApiExit(db, rc);
162390
162391 /* If there are no active statements, clear the interrupt flag at this
162392 ** point. */
162393 if( db->nVdbeActive==0 ){
162394 AtomicStore(&db->u1.isInterrupted, 0);
162395 }
162396
162397 sqlite3_mutex_leave(db->mutex);
162398 return rc;
162399 #endif
@@ -162093,10 +163076,11 @@
163076 sqlite3 *db; /* Store allocated handle here */
163077 int rc; /* Return code */
163078 int isThreadsafe; /* True for threadsafe connections */
163079 char *zOpen = 0; /* Filename argument to pass to BtreeOpen() */
163080 char *zErrMsg = 0; /* Error message from sqlite3ParseUri() */
163081 int i; /* Loop counter */
163082
163083 #ifdef SQLITE_ENABLE_API_ARMOR
163084 if( ppDb==0 ) return SQLITE_MISUSE_BKPT;
163085 #endif
163086 *ppDb = 0;
@@ -162241,10 +163225,13 @@
163225 | SQLITE_EnableQPSG
163226 #endif
163227 #if defined(SQLITE_DEFAULT_DEFENSIVE)
163228 | SQLITE_Defensive
163229 #endif
163230 #if defined(SQLITE_DEFAULT_LEGACY_ALTER_TABLE)
163231 | SQLITE_LegacyAlter
163232 #endif
163233 ;
163234 sqlite3HashInit(&db->aCollSeq);
163235 #ifndef SQLITE_OMIT_VIRTUALTABLE
163236 sqlite3HashInit(&db->aModule);
163237 #endif
@@ -162333,18 +163320,15 @@
163320 */
163321 sqlite3Error(db, SQLITE_OK);
163322 sqlite3RegisterPerConnectionBuiltinFunctions(db);
163323 rc = sqlite3_errcode(db);
163324
163325
163326 /* Load compiled-in extensions */
163327 for(i=0; rc==SQLITE_OK && i<ArraySize(sqlite3BuiltinExtensions); i++){
163328 rc = sqlite3BuiltinExtensions[i](db);
163329 }
 
 
 
163330
163331 /* Load automatic extensions - extensions that have been registered
163332 ** using the sqlite3_automatic_extension() API.
163333 */
163334 if( rc==SQLITE_OK ){
@@ -162353,66 +163337,10 @@
163337 if( rc!=SQLITE_OK ){
163338 goto opendb_out;
163339 }
163340 }
163341
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163342 #ifdef SQLITE_ENABLE_INTERNAL_FUNCTIONS
163343 /* Testing use only!!! The -DSQLITE_ENABLE_INTERNAL_FUNCTIONS=1 compile-time
163344 ** option gives access to internal functions by default.
163345 ** Testing use only!!! */
163346 db->mDbFlags |= DBFLAG_InternalFunc;
@@ -162893,10 +163821,17 @@
163821 }else if( op==SQLITE_FCNTL_JOURNAL_POINTER ){
163822 *(sqlite3_file**)pArg = sqlite3PagerJrnlFile(pPager);
163823 rc = SQLITE_OK;
163824 }else if( op==SQLITE_FCNTL_DATA_VERSION ){
163825 *(unsigned int*)pArg = sqlite3PagerDataVersion(pPager);
163826 rc = SQLITE_OK;
163827 }else if( op==SQLITE_FCNTL_RESERVE_BYTES ){
163828 int iNew = *(int*)pArg;
163829 *(int*)pArg = sqlite3BtreeGetRequestedReserve(pBtree);
163830 if( iNew>=0 && iNew<=255 ){
163831 sqlite3BtreeSetPageSize(pBtree, 0, iNew, 0);
163832 }
163833 rc = SQLITE_OK;
163834 }else{
163835 rc = sqlite3OsFileControl(fd, op, pArg);
163836 }
163837 sqlite3BtreeLeave(pBtree);
@@ -163110,24 +164045,10 @@
164045 case SQLITE_TESTCTRL_BYTEORDER: {
164046 rc = SQLITE_BYTEORDER*100 + SQLITE_LITTLEENDIAN*10 + SQLITE_BIGENDIAN;
164047 break;
164048 }
164049
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164050 /* sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, sqlite3 *db, int N)
164051 **
164052 ** Enable or disable various optimizations for testing purposes. The
164053 ** argument N is a bitmask of optimizations to be disabled. For normal
164054 ** operation N should be 0. The idea is that a test program (like the
@@ -165185,10 +166106,11 @@
166106 SQLITE_PRIVATE void sqlite3Fts3DoclistPrev(int,char*,int,char**,sqlite3_int64*,int*,u8*);
166107 SQLITE_PRIVATE int sqlite3Fts3EvalPhraseStats(Fts3Cursor *, Fts3Expr *, u32 *);
166108 SQLITE_PRIVATE int sqlite3Fts3FirstFilter(sqlite3_int64, char *, int, char *);
166109 SQLITE_PRIVATE void sqlite3Fts3CreateStatTable(int*, Fts3Table*);
166110 SQLITE_PRIVATE int sqlite3Fts3EvalTestDeferred(Fts3Cursor *pCsr, int *pRc);
166111 SQLITE_PRIVATE int sqlite3Fts3ReadInt(const char *z, int *pnOut);
166112
166113 /* fts3_tokenizer.c */
166114 SQLITE_PRIVATE const char *sqlite3Fts3NextToken(const char *, int *);
166115 SQLITE_PRIVATE int sqlite3Fts3InitHashTable(sqlite3 *, Fts3Hash *, const char *);
166116 SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(Fts3Hash *pHash, const char *,
@@ -165916,10 +166838,26 @@
166838 fts3Appendf(pRc, &zRet, ", ?");
166839 }
166840 sqlite3_free(zFree);
166841 return zRet;
166842 }
166843
166844 /*
166845 ** Buffer z contains a positive integer value encoded as utf-8 text.
166846 ** Decode this value and store it in *pnOut, returning the number of bytes
166847 ** consumed. If an overflow error occurs return a negative value.
166848 */
166849 SQLITE_PRIVATE int sqlite3Fts3ReadInt(const char *z, int *pnOut){
166850 u64 iVal = 0;
166851 int i;
166852 for(i=0; z[i]>='0' && z[i]<='9'; i++){
166853 iVal = iVal*10 + (z[i] - '0');
166854 if( iVal>0x7FFFFFFF ) return -1;
166855 }
166856 *pnOut = (int)iVal;
166857 return i;
166858 }
166859
166860 /*
166861 ** This function interprets the string at (*pp) as a non-negative integer
166862 ** value. It reads the integer and sets *pnOut to the value read, then
166863 ** sets *pp to point to the byte immediately following the last byte of
@@ -165932,23 +166870,21 @@
166870 **
166871 ** This function is used when parsing the "prefix=" FTS4 parameter.
166872 */
166873 static int fts3GobbleInt(const char **pp, int *pnOut){
166874 const int MAX_NPREFIX = 10000000;
 
166875 int nInt = 0; /* Output value */
166876 int nByte;
166877 nByte = sqlite3Fts3ReadInt(*pp, &nInt);
166878 if( nInt>MAX_NPREFIX ){
166879 nInt = 0;
166880 }
166881 if( nByte==0 ){
166882 return SQLITE_ERROR;
166883 }
 
166884 *pnOut = nInt;
166885 *pp += nByte;
166886 return SQLITE_OK;
166887 }
166888
166889 /*
166890 ** This function is called to allocate an array of Fts3Index structures
@@ -167126,11 +168062,13 @@
168062 static void fts3ReadNextPos(
168063 char **pp, /* IN/OUT: Pointer into position-list buffer */
168064 sqlite3_int64 *pi /* IN/OUT: Value read from position-list */
168065 ){
168066 if( (**pp)&0xFE ){
168067 int iVal;
168068 *pp += fts3GetVarint32((*pp), &iVal);
168069 *pi += iVal;
168070 *pi -= 2;
168071 }else{
168072 *pi = POSITION_LIST_END;
168073 }
168074 }
@@ -172026,14 +172964,11 @@
172964
172965 /* If this is a "NEAR" keyword, check for an explicit nearness. */
172966 if( pKey->eType==FTSQUERY_NEAR ){
172967 assert( nKey==4 );
172968 if( zInput[4]=='/' && zInput[5]>='0' && zInput[5]<='9' ){
172969 nKey += 1+sqlite3Fts3ReadInt(&zInput[nKey+1], &nNear);
 
 
 
172970 }
172971 }
172972
172973 /* At this point this is probably a keyword. But for that to be true,
172974 ** the next byte must contain either whitespace, an open or close
@@ -176558,10 +177493,11 @@
177493 ** b-tree node. And that the final byte of the doclist is 0x00. If either
177494 ** of these statements is untrue, then the data structure is corrupt.
177495 */
177496 if( pReader->nDoclist > pReader->nNode-(pReader->aDoclist-pReader->aNode)
177497 || (pReader->nPopulate==0 && pReader->aDoclist[pReader->nDoclist-1])
177498 || pReader->nDoclist==0
177499 ){
177500 return FTS_CORRUPT_VTAB;
177501 }
177502 return SQLITE_OK;
177503 }
@@ -178211,25 +179147,25 @@
179147 ){
179148 const unsigned char *zText = sqlite3_column_text(pStmt, iCol);
179149 if( zText ){
179150 int i;
179151 int iMul = 1;
179152 u64 iVal = 0;
179153 for(i=0; zText[i]>='0' && zText[i]<='9'; i++){
179154 iVal = iVal*10 + (zText[i] - '0');
179155 }
179156 *piEndBlock = (i64)iVal;
179157 while( zText[i]==' ' ) i++;
179158 iVal = 0;
179159 if( zText[i]=='-' ){
179160 i++;
179161 iMul = -1;
179162 }
179163 for(/* no-op */; zText[i]>='0' && zText[i]<='9'; i++){
179164 iVal = iVal*10 + (zText[i] - '0');
179165 }
179166 *pnByte = ((i64)iVal * (i64)iMul);
179167 }
179168 }
179169
179170
179171 /*
@@ -223728,11 +224664,11 @@
224664 int nArg, /* Number of args */
224665 sqlite3_value **apUnused /* Function arguments */
224666 ){
224667 assert( nArg==0 );
224668 UNUSED_PARAM2(nArg, apUnused);
224669 sqlite3_result_text(pCtx, "fts5: 2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f9654ff", -1, SQLITE_TRANSIENT);
224670 }
224671
224672 /*
224673 ** Return true if zName is the extension on one of the shadow tables used
224674 ** by this module.
@@ -228379,11 +229315,12 @@
229315 }
229316 case STMT_COLUMN_BUSY: {
229317 sqlite3_result_int(ctx, sqlite3_stmt_busy(pCur->pStmt));
229318 break;
229319 }
229320 default: {
229321 assert( i==STMT_COLUMN_MEM );
229322 i = SQLITE_STMTSTATUS_MEMUSED +
229323 STMT_COLUMN_NSCAN - SQLITE_STMTSTATUS_FULLSCAN_STEP;
229324 /* Fall thru */
229325 }
229326 case STMT_COLUMN_NSCAN:
@@ -228510,12 +229447,12 @@
229447 }
229448 #endif /* SQLITE_CORE */
229449 #endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_STMTVTAB) */
229450
229451 /************** End of stmt.c ************************************************/
229452 #if __LINE__!=229452
229453 #undef SQLITE_SOURCE_ID
229454 #define SQLITE_SOURCE_ID "2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f96alt2"
229455 #endif
229456 /* Return the source-id for this library */
229457 SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; }
229458 /************************** End of sqlite3.c ******************************/
229459
+107 -31
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -123,11 +123,11 @@
123123
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
124124
** [sqlite_version()] and [sqlite_source_id()].
125125
*/
126126
#define SQLITE_VERSION "3.32.0"
127127
#define SQLITE_VERSION_NUMBER 3032000
128
-#define SQLITE_SOURCE_ID "2020-03-21 23:10:38 5d14a1c4f2fc17de98ad685ad1422cdfda89dfccb00afcaf32ee416b6f84f525"
128
+#define SQLITE_SOURCE_ID "2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f9654ff"
129129
130130
/*
131131
** CAPI3REF: Run-Time Library Version Numbers
132132
** KEYWORDS: sqlite3_version sqlite3_sourceid
133133
**
@@ -297,30 +297,26 @@
297297
** for the [sqlite3] object.
298298
** ^Calls to sqlite3_close() and sqlite3_close_v2() return [SQLITE_OK] if
299299
** the [sqlite3] object is successfully destroyed and all associated
300300
** resources are deallocated.
301301
**
302
+** Ideally, applications should [sqlite3_finalize | finalize] all
303
+** [prepared statements], [sqlite3_blob_close | close] all [BLOB handles], and
304
+** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated
305
+** with the [sqlite3] object prior to attempting to close the object.
302306
** ^If the database connection is associated with unfinalized prepared
303
-** statements or unfinished sqlite3_backup objects then sqlite3_close()
304
-** will leave the database connection open and return [SQLITE_BUSY].
305
-** ^If sqlite3_close_v2() is called with unfinalized prepared statements
306
-** and/or unfinished sqlite3_backups, then the database connection becomes
307
-** an unusable "zombie" which will automatically be deallocated when the
308
-** last prepared statement is finalized or the last sqlite3_backup is
309
-** finished. The sqlite3_close_v2() interface is intended for use with
310
-** host languages that are garbage collected, and where the order in which
311
-** destructors are called is arbitrary.
312
-**
313
-** Applications should [sqlite3_finalize | finalize] all [prepared statements],
314
-** [sqlite3_blob_close | close] all [BLOB handles], and
315
-** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated
316
-** with the [sqlite3] object prior to attempting to close the object. ^If
317
-** sqlite3_close_v2() is called on a [database connection] that still has
318
-** outstanding [prepared statements], [BLOB handles], and/or
319
-** [sqlite3_backup] objects then it returns [SQLITE_OK] and the deallocation
320
-** of resources is deferred until all [prepared statements], [BLOB handles],
321
-** and [sqlite3_backup] objects are also destroyed.
307
+** statements, BLOB handlers, and/or unfinished sqlite3_backup objects then
308
+** sqlite3_close() will leave the database connection open and return
309
+** [SQLITE_BUSY]. ^If sqlite3_close_v2() is called with unfinalized prepared
310
+** statements, unclosed BLOB handlers, and/or unfinished sqlite3_backups,
311
+** it returns [SQLITE_OK] regardless, but instead of deallocating the database
312
+** connection immediately, it marks the database connection as an unusable
313
+** "zombie" and makes arrangements to automatically deallocate the database
314
+** connection after all prepared statements are finalized, all BLOB handles
315
+** are closed, and all backups have finished. The sqlite3_close_v2() interface
316
+** is intended for use with host languages that are garbage collected, and
317
+** where the order in which destructors are called is arbitrary.
322318
**
323319
** ^If an [sqlite3] object is destroyed while a transaction is open,
324320
** the transaction is automatically rolled back.
325321
**
326322
** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)]
@@ -505,22 +501,25 @@
505501
#define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8))
506502
#define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
507503
#define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8))
508504
#define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
509505
#define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
506
+#define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
510507
#define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
511508
#define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
512509
#define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
513510
#define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
511
+#define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8))
514512
#define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
515513
#define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8))
516514
#define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8))
517515
#define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8))
518516
#define SQLITE_CANTOPEN_DIRTYWAL (SQLITE_CANTOPEN | (5<<8)) /* Not Used */
519517
#define SQLITE_CANTOPEN_SYMLINK (SQLITE_CANTOPEN | (6<<8))
520518
#define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
521519
#define SQLITE_CORRUPT_SEQUENCE (SQLITE_CORRUPT | (2<<8))
520
+#define SQLITE_CORRUPT_INDEX (SQLITE_CORRUPT | (3<<8))
522521
#define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
523522
#define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
524523
#define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8))
525524
#define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4<<8))
526525
#define SQLITE_READONLY_CANTINIT (SQLITE_READONLY | (5<<8))
@@ -1085,14 +1084,16 @@
10851084
** so that all subsequent write operations are independent.
10861085
** ^SQLite will never invoke SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE without
10871086
** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE].
10881087
**
10891088
** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]]
1090
-** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode causes attempts to obtain
1091
-** a file lock using the xLock or xShmLock methods of the VFS to wait
1092
-** for up to M milliseconds before failing, where M is the single
1093
-** unsigned integer parameter.
1089
+** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode is used to configure a VFS
1090
+** to block for up to M milliseconds before failing when attempting to
1091
+** obtain a file lock using the xLock or xShmLock methods of the VFS.
1092
+** The parameter is a pointer to a 32-bit signed integer that contains
1093
+** the value that M is to be set to. Before returning, the 32-bit signed
1094
+** integer is overwritten with the previous value of M.
10941095
**
10951096
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
10961097
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
10971098
** a database file. The argument is a pointer to a 32-bit unsigned integer.
10981099
** The "data version" for the pager is written into the pointer. The
@@ -1109,10 +1110,15 @@
11091110
** a single attached database that occur due to other database connections,
11101111
** but omits changes implemented by the database connection on which it is
11111112
** called. This file control is the only mechanism to detect changes that
11121113
** happen either internally or externally and that are associated with
11131114
** a particular attached database.
1115
+**
1116
+** <li>[[SQLITE_FCNTL_CKPT_START]]
1117
+** The [SQLITE_FCNTL_CKPT_START] opcode is invoked from within a checkpoint
1118
+** in wal mode before the client starts to copy pages from the wal
1119
+** file to the database file.
11141120
**
11151121
** <li>[[SQLITE_FCNTL_CKPT_DONE]]
11161122
** The [SQLITE_FCNTL_CKPT_DONE] opcode is invoked from within a checkpoint
11171123
** in wal mode after the client has finished copying pages from the wal
11181124
** file to the database file, but before the *-shm file is updated to
@@ -1153,10 +1159,12 @@
11531159
#define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33
11541160
#define SQLITE_FCNTL_LOCK_TIMEOUT 34
11551161
#define SQLITE_FCNTL_DATA_VERSION 35
11561162
#define SQLITE_FCNTL_SIZE_LIMIT 36
11571163
#define SQLITE_FCNTL_CKPT_DONE 37
1164
+#define SQLITE_FCNTL_RESERVE_BYTES 38
1165
+#define SQLITE_FCNTL_CKPT_START 39
11581166
11591167
/* deprecated names */
11601168
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
11611169
#define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE
11621170
#define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO
@@ -3531,12 +3539,23 @@
35313539
**
35323540
** These are utility routines, useful to [VFS|custom VFS implementations],
35333541
** that check if a database file was a URI that contained a specific query
35343542
** parameter, and if so obtains the value of that query parameter.
35353543
**
3536
-** If F is the database filename pointer passed into the xOpen() method of
3537
-** a VFS implementation or it is the return value of [sqlite3_db_filename()]
3544
+** The first parameter to these interfaces (hereafter referred to
3545
+** as F) must be one of:
3546
+** <ul>
3547
+** <li> A database filename pointer created by the SQLite core and
3548
+** passed into the xOpen() method of a VFS implemention, or
3549
+** <li> A filename obtained from [sqlite3_db_filename()], or
3550
+** <li> A new filename constructed using [sqlite3_create_filename()].
3551
+** </ul>
3552
+** If the F parameter is not one of the above, then the behavior is
3553
+** undefined and probably undesirable. Older versions of SQLite were
3554
+** more tolerant of invalid F parameters than newer versions.
3555
+**
3556
+** If F is a suitable filename (as described in the previous paragraph)
35383557
** and if P is the name of the query parameter, then
35393558
** sqlite3_uri_parameter(F,P) returns the value of the P
35403559
** parameter if it exists or a NULL pointer if P does not appear as a
35413560
** query parameter on F. If P is a query parameter of F and it
35423561
** has no explicit value, then sqlite3_uri_parameter(F,P) returns
@@ -3615,10 +3634,29 @@
36153634
*/
36163635
SQLITE_API const char *sqlite3_filename_database(const char*);
36173636
SQLITE_API const char *sqlite3_filename_journal(const char*);
36183637
SQLITE_API const char *sqlite3_filename_wal(const char*);
36193638
3639
+/*
3640
+** CAPI3REF: Database File Corresponding To A Journal
3641
+**
3642
+** ^If X is the name of a rollback or WAL-mode journal file that is
3643
+** passed into the xOpen method of [sqlite3_vfs], then
3644
+** sqlite3_database_file_object(X) returns a pointer to the [sqlite3_file]
3645
+** object that represents the main database file.
3646
+**
3647
+** This routine is intended for use in custom [VFS] implementations
3648
+** only. It is not a general-purpose interface.
3649
+** The argument sqlite3_file_object(X) must be a filename pointer that
3650
+** has been passed into [sqlite3_vfs].xOpen method where the
3651
+** flags parameter to xOpen contains one of the bits
3652
+** [SQLITE_OPEN_MAIN_JOURNAL] or [SQLITE_OPEN_WAL]. Any other use
3653
+** of this routine results in undefined and probably undesirable
3654
+** behavior.
3655
+*/
3656
+SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*);
3657
+
36203658
/*
36213659
** CAPI3REF: Create and Destroy VFS Filenames
36223660
**
36233661
** These interfces are provided for use by [VFS shim] implementations and
36243662
** are not useful outside of that context.
@@ -3649,11 +3687,11 @@
36493687
** None of the D, J, or W parameters to sqlite3_create_filename(D,J,W,N,P) may
36503688
** be NULL pointers, though they can be empty strings.
36513689
**
36523690
** The sqlite3_free_filename(Y) routine releases a memory allocation
36533691
** previously obtained from sqlite3_create_filename(). Invoking
3654
-** sqlite3_free_filename(Y) is a NULL pointer is a harmless no-op.
3692
+** sqlite3_free_filename(Y) where Y is a NULL pointer is a harmless no-op.
36553693
**
36563694
** If the Y parameter to sqlite3_free_filename(Y) is anything other
36573695
** than a NULL pointer or a pointer previously acquired from
36583696
** sqlite3_create_filename(), then bad things such as heap
36593697
** corruption or segfaults may occur. The value Y should be
@@ -4256,10 +4294,28 @@
42564294
**
42574295
** ^The third argument is the value to bind to the parameter.
42584296
** ^If the third parameter to sqlite3_bind_text() or sqlite3_bind_text16()
42594297
** or sqlite3_bind_blob() is a NULL pointer then the fourth parameter
42604298
** is ignored and the end result is the same as sqlite3_bind_null().
4299
+** ^If the third parameter to sqlite3_bind_text() is not NULL, then
4300
+** it should be a pointer to well-formed UTF8 text.
4301
+** ^If the third parameter to sqlite3_bind_text16() is not NULL, then
4302
+** it should be a pointer to well-formed UTF16 text.
4303
+** ^If the third parameter to sqlite3_bind_text64() is not NULL, then
4304
+** it should be a pointer to a well-formed unicode string that is
4305
+** either UTF8 if the sixth parameter is SQLITE_UTF8, or UTF16
4306
+** otherwise.
4307
+**
4308
+** [[byte-order determination rules]] ^The byte-order of
4309
+** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF)
4310
+** found in first character, which is removed, or in the absence of a BOM
4311
+** the byte order is the native byte order of the host
4312
+** machine for sqlite3_bind_text16() or the byte order specified in
4313
+** the 6th parameter for sqlite3_bind_text64().)^
4314
+** ^If UTF16 input text contains invalid unicode
4315
+** characters, then SQLite might change those invalid characters
4316
+** into the unicode replacement character: U+FFFD.
42614317
**
42624318
** ^(In those routines that have a fourth argument, its value is the
42634319
** number of bytes in the parameter. To be clear: the value is the
42644320
** number of <u>bytes</u> in the value, not the number of characters.)^
42654321
** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16()
@@ -4269,11 +4325,11 @@
42694325
** the behavior is undefined.
42704326
** If a non-negative fourth parameter is provided to sqlite3_bind_text()
42714327
** or sqlite3_bind_text16() or sqlite3_bind_text64() then
42724328
** that parameter must be the byte offset
42734329
** where the NUL terminator would occur assuming the string were NUL
4274
-** terminated. If any NUL characters occur at byte offsets less than
4330
+** terminated. If any NUL characters occurs at byte offsets less than
42754331
** the value of the fourth parameter then the resulting string value will
42764332
** contain embedded NULs. The result of expressions involving strings
42774333
** with embedded NULs is undefined.
42784334
**
42794335
** ^The fifth argument to the BLOB and string binding interfaces
@@ -5594,12 +5650,13 @@
55945650
** cause the implemented SQL function to throw an exception.
55955651
** ^SQLite uses the string pointed to by the
55965652
** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16()
55975653
** as the text of an error message. ^SQLite interprets the error
55985654
** message string from sqlite3_result_error() as UTF-8. ^SQLite
5599
-** interprets the string from sqlite3_result_error16() as UTF-16 in native
5600
-** byte order. ^If the third parameter to sqlite3_result_error()
5655
+** interprets the string from sqlite3_result_error16() as UTF-16 using
5656
+** the same [byte-order determination rules] as [sqlite3_bind_text16()].
5657
+** ^If the third parameter to sqlite3_result_error()
56015658
** or sqlite3_result_error16() is negative then SQLite takes as the error
56025659
** message all text up through the first zero character.
56035660
** ^If the third parameter to sqlite3_result_error() or
56045661
** sqlite3_result_error16() is non-negative then SQLite takes that many
56055662
** bytes (not characters) from the 2nd parameter as the error message.
@@ -5662,10 +5719,29 @@
56625719
** when it has finished using that result.
56635720
** ^If the 4th parameter to the sqlite3_result_text* interfaces
56645721
** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT
56655722
** then SQLite makes a copy of the result into space obtained
56665723
** from [sqlite3_malloc()] before it returns.
5724
+**
5725
+** ^For the sqlite3_result_text16(), sqlite3_result_text16le(), and
5726
+** sqlite3_result_text16be() routines, and for sqlite3_result_text64()
5727
+** when the encoding is not UTF8, if the input UTF16 begins with a
5728
+** byte-order mark (BOM, U+FEFF) then the BOM is removed from the
5729
+** string and the rest of the string is interpreted according to the
5730
+** byte-order specified by the BOM. ^The byte-order specified by
5731
+** the BOM at the beginning of the text overrides the byte-order
5732
+** specified by the interface procedure. ^So, for example, if
5733
+** sqlite3_result_text16le() is invoked with text that begins
5734
+** with bytes 0xfe, 0xff (a big-endian byte-order mark) then the
5735
+** first two bytes of input are skipped and the remaining input
5736
+** is interpreted as UTF16BE text.
5737
+**
5738
+** ^For UTF16 input text to the sqlite3_result_text16(),
5739
+** sqlite3_result_text16be(), sqlite3_result_text16le(), and
5740
+** sqlite3_result_text64() routines, if the text contains invalid
5741
+** UTF16 characters, the invalid characters might be converted
5742
+** into the unicode replacement character, U+FFFD.
56675743
**
56685744
** ^The sqlite3_result_value() interface sets the result of
56695745
** the application-defined function to be a copy of the
56705746
** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The
56715747
** sqlite3_result_value() interface makes a copy of the [sqlite3_value]
@@ -7610,11 +7686,11 @@
76107686
#define SQLITE_TESTCTRL_FAULT_INSTALL 9
76117687
#define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
76127688
#define SQLITE_TESTCTRL_PENDING_BYTE 11
76137689
#define SQLITE_TESTCTRL_ASSERT 12
76147690
#define SQLITE_TESTCTRL_ALWAYS 13
7615
-#define SQLITE_TESTCTRL_RESERVE 14
7691
+#define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */
76167692
#define SQLITE_TESTCTRL_OPTIMIZATIONS 15
76177693
#define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */
76187694
#define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */
76197695
#define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17
76207696
#define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
76217697
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -123,11 +123,11 @@
123 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
124 ** [sqlite_version()] and [sqlite_source_id()].
125 */
126 #define SQLITE_VERSION "3.32.0"
127 #define SQLITE_VERSION_NUMBER 3032000
128 #define SQLITE_SOURCE_ID "2020-03-21 23:10:38 5d14a1c4f2fc17de98ad685ad1422cdfda89dfccb00afcaf32ee416b6f84f525"
129
130 /*
131 ** CAPI3REF: Run-Time Library Version Numbers
132 ** KEYWORDS: sqlite3_version sqlite3_sourceid
133 **
@@ -297,30 +297,26 @@
297 ** for the [sqlite3] object.
298 ** ^Calls to sqlite3_close() and sqlite3_close_v2() return [SQLITE_OK] if
299 ** the [sqlite3] object is successfully destroyed and all associated
300 ** resources are deallocated.
301 **
 
 
 
 
302 ** ^If the database connection is associated with unfinalized prepared
303 ** statements or unfinished sqlite3_backup objects then sqlite3_close()
304 ** will leave the database connection open and return [SQLITE_BUSY].
305 ** ^If sqlite3_close_v2() is called with unfinalized prepared statements
306 ** and/or unfinished sqlite3_backups, then the database connection becomes
307 ** an unusable "zombie" which will automatically be deallocated when the
308 ** last prepared statement is finalized or the last sqlite3_backup is
309 ** finished. The sqlite3_close_v2() interface is intended for use with
310 ** host languages that are garbage collected, and where the order in which
311 ** destructors are called is arbitrary.
312 **
313 ** Applications should [sqlite3_finalize | finalize] all [prepared statements],
314 ** [sqlite3_blob_close | close] all [BLOB handles], and
315 ** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated
316 ** with the [sqlite3] object prior to attempting to close the object. ^If
317 ** sqlite3_close_v2() is called on a [database connection] that still has
318 ** outstanding [prepared statements], [BLOB handles], and/or
319 ** [sqlite3_backup] objects then it returns [SQLITE_OK] and the deallocation
320 ** of resources is deferred until all [prepared statements], [BLOB handles],
321 ** and [sqlite3_backup] objects are also destroyed.
322 **
323 ** ^If an [sqlite3] object is destroyed while a transaction is open,
324 ** the transaction is automatically rolled back.
325 **
326 ** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)]
@@ -505,22 +501,25 @@
505 #define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8))
506 #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
507 #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8))
508 #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
509 #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
 
510 #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
511 #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
512 #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
513 #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
 
514 #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
515 #define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8))
516 #define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8))
517 #define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8))
518 #define SQLITE_CANTOPEN_DIRTYWAL (SQLITE_CANTOPEN | (5<<8)) /* Not Used */
519 #define SQLITE_CANTOPEN_SYMLINK (SQLITE_CANTOPEN | (6<<8))
520 #define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
521 #define SQLITE_CORRUPT_SEQUENCE (SQLITE_CORRUPT | (2<<8))
 
522 #define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
523 #define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
524 #define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8))
525 #define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4<<8))
526 #define SQLITE_READONLY_CANTINIT (SQLITE_READONLY | (5<<8))
@@ -1085,14 +1084,16 @@
1085 ** so that all subsequent write operations are independent.
1086 ** ^SQLite will never invoke SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE without
1087 ** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE].
1088 **
1089 ** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]]
1090 ** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode causes attempts to obtain
1091 ** a file lock using the xLock or xShmLock methods of the VFS to wait
1092 ** for up to M milliseconds before failing, where M is the single
1093 ** unsigned integer parameter.
 
 
1094 **
1095 ** <li>[[SQLITE_FCNTL_DATA_VERSION]]
1096 ** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
1097 ** a database file. The argument is a pointer to a 32-bit unsigned integer.
1098 ** The "data version" for the pager is written into the pointer. The
@@ -1109,10 +1110,15 @@
1109 ** a single attached database that occur due to other database connections,
1110 ** but omits changes implemented by the database connection on which it is
1111 ** called. This file control is the only mechanism to detect changes that
1112 ** happen either internally or externally and that are associated with
1113 ** a particular attached database.
 
 
 
 
 
1114 **
1115 ** <li>[[SQLITE_FCNTL_CKPT_DONE]]
1116 ** The [SQLITE_FCNTL_CKPT_DONE] opcode is invoked from within a checkpoint
1117 ** in wal mode after the client has finished copying pages from the wal
1118 ** file to the database file, but before the *-shm file is updated to
@@ -1153,10 +1159,12 @@
1153 #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33
1154 #define SQLITE_FCNTL_LOCK_TIMEOUT 34
1155 #define SQLITE_FCNTL_DATA_VERSION 35
1156 #define SQLITE_FCNTL_SIZE_LIMIT 36
1157 #define SQLITE_FCNTL_CKPT_DONE 37
 
 
1158
1159 /* deprecated names */
1160 #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
1161 #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE
1162 #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO
@@ -3531,12 +3539,23 @@
3531 **
3532 ** These are utility routines, useful to [VFS|custom VFS implementations],
3533 ** that check if a database file was a URI that contained a specific query
3534 ** parameter, and if so obtains the value of that query parameter.
3535 **
3536 ** If F is the database filename pointer passed into the xOpen() method of
3537 ** a VFS implementation or it is the return value of [sqlite3_db_filename()]
 
 
 
 
 
 
 
 
 
 
 
3538 ** and if P is the name of the query parameter, then
3539 ** sqlite3_uri_parameter(F,P) returns the value of the P
3540 ** parameter if it exists or a NULL pointer if P does not appear as a
3541 ** query parameter on F. If P is a query parameter of F and it
3542 ** has no explicit value, then sqlite3_uri_parameter(F,P) returns
@@ -3615,10 +3634,29 @@
3615 */
3616 SQLITE_API const char *sqlite3_filename_database(const char*);
3617 SQLITE_API const char *sqlite3_filename_journal(const char*);
3618 SQLITE_API const char *sqlite3_filename_wal(const char*);
3619
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3620 /*
3621 ** CAPI3REF: Create and Destroy VFS Filenames
3622 **
3623 ** These interfces are provided for use by [VFS shim] implementations and
3624 ** are not useful outside of that context.
@@ -3649,11 +3687,11 @@
3649 ** None of the D, J, or W parameters to sqlite3_create_filename(D,J,W,N,P) may
3650 ** be NULL pointers, though they can be empty strings.
3651 **
3652 ** The sqlite3_free_filename(Y) routine releases a memory allocation
3653 ** previously obtained from sqlite3_create_filename(). Invoking
3654 ** sqlite3_free_filename(Y) is a NULL pointer is a harmless no-op.
3655 **
3656 ** If the Y parameter to sqlite3_free_filename(Y) is anything other
3657 ** than a NULL pointer or a pointer previously acquired from
3658 ** sqlite3_create_filename(), then bad things such as heap
3659 ** corruption or segfaults may occur. The value Y should be
@@ -4256,10 +4294,28 @@
4256 **
4257 ** ^The third argument is the value to bind to the parameter.
4258 ** ^If the third parameter to sqlite3_bind_text() or sqlite3_bind_text16()
4259 ** or sqlite3_bind_blob() is a NULL pointer then the fourth parameter
4260 ** is ignored and the end result is the same as sqlite3_bind_null().
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4261 **
4262 ** ^(In those routines that have a fourth argument, its value is the
4263 ** number of bytes in the parameter. To be clear: the value is the
4264 ** number of <u>bytes</u> in the value, not the number of characters.)^
4265 ** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16()
@@ -4269,11 +4325,11 @@
4269 ** the behavior is undefined.
4270 ** If a non-negative fourth parameter is provided to sqlite3_bind_text()
4271 ** or sqlite3_bind_text16() or sqlite3_bind_text64() then
4272 ** that parameter must be the byte offset
4273 ** where the NUL terminator would occur assuming the string were NUL
4274 ** terminated. If any NUL characters occur at byte offsets less than
4275 ** the value of the fourth parameter then the resulting string value will
4276 ** contain embedded NULs. The result of expressions involving strings
4277 ** with embedded NULs is undefined.
4278 **
4279 ** ^The fifth argument to the BLOB and string binding interfaces
@@ -5594,12 +5650,13 @@
5594 ** cause the implemented SQL function to throw an exception.
5595 ** ^SQLite uses the string pointed to by the
5596 ** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16()
5597 ** as the text of an error message. ^SQLite interprets the error
5598 ** message string from sqlite3_result_error() as UTF-8. ^SQLite
5599 ** interprets the string from sqlite3_result_error16() as UTF-16 in native
5600 ** byte order. ^If the third parameter to sqlite3_result_error()
 
5601 ** or sqlite3_result_error16() is negative then SQLite takes as the error
5602 ** message all text up through the first zero character.
5603 ** ^If the third parameter to sqlite3_result_error() or
5604 ** sqlite3_result_error16() is non-negative then SQLite takes that many
5605 ** bytes (not characters) from the 2nd parameter as the error message.
@@ -5662,10 +5719,29 @@
5662 ** when it has finished using that result.
5663 ** ^If the 4th parameter to the sqlite3_result_text* interfaces
5664 ** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT
5665 ** then SQLite makes a copy of the result into space obtained
5666 ** from [sqlite3_malloc()] before it returns.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5667 **
5668 ** ^The sqlite3_result_value() interface sets the result of
5669 ** the application-defined function to be a copy of the
5670 ** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The
5671 ** sqlite3_result_value() interface makes a copy of the [sqlite3_value]
@@ -7610,11 +7686,11 @@
7610 #define SQLITE_TESTCTRL_FAULT_INSTALL 9
7611 #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
7612 #define SQLITE_TESTCTRL_PENDING_BYTE 11
7613 #define SQLITE_TESTCTRL_ASSERT 12
7614 #define SQLITE_TESTCTRL_ALWAYS 13
7615 #define SQLITE_TESTCTRL_RESERVE 14
7616 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15
7617 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */
7618 #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */
7619 #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17
7620 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
7621
--- src/sqlite3.h
+++ src/sqlite3.h
@@ -123,11 +123,11 @@
123 ** [sqlite3_libversion_number()], [sqlite3_sourceid()],
124 ** [sqlite_version()] and [sqlite_source_id()].
125 */
126 #define SQLITE_VERSION "3.32.0"
127 #define SQLITE_VERSION_NUMBER 3032000
128 #define SQLITE_SOURCE_ID "2020-05-08 19:02:21 3a16c0ce4d8851f79f670d94786032c8007619154ece44647dc9cc5b1f9654ff"
129
130 /*
131 ** CAPI3REF: Run-Time Library Version Numbers
132 ** KEYWORDS: sqlite3_version sqlite3_sourceid
133 **
@@ -297,30 +297,26 @@
297 ** for the [sqlite3] object.
298 ** ^Calls to sqlite3_close() and sqlite3_close_v2() return [SQLITE_OK] if
299 ** the [sqlite3] object is successfully destroyed and all associated
300 ** resources are deallocated.
301 **
302 ** Ideally, applications should [sqlite3_finalize | finalize] all
303 ** [prepared statements], [sqlite3_blob_close | close] all [BLOB handles], and
304 ** [sqlite3_backup_finish | finish] all [sqlite3_backup] objects associated
305 ** with the [sqlite3] object prior to attempting to close the object.
306 ** ^If the database connection is associated with unfinalized prepared
307 ** statements, BLOB handlers, and/or unfinished sqlite3_backup objects then
308 ** sqlite3_close() will leave the database connection open and return
309 ** [SQLITE_BUSY]. ^If sqlite3_close_v2() is called with unfinalized prepared
310 ** statements, unclosed BLOB handlers, and/or unfinished sqlite3_backups,
311 ** it returns [SQLITE_OK] regardless, but instead of deallocating the database
312 ** connection immediately, it marks the database connection as an unusable
313 ** "zombie" and makes arrangements to automatically deallocate the database
314 ** connection after all prepared statements are finalized, all BLOB handles
315 ** are closed, and all backups have finished. The sqlite3_close_v2() interface
316 ** is intended for use with host languages that are garbage collected, and
317 ** where the order in which destructors are called is arbitrary.
 
 
 
 
 
 
 
 
318 **
319 ** ^If an [sqlite3] object is destroyed while a transaction is open,
320 ** the transaction is automatically rolled back.
321 **
322 ** The C parameter to [sqlite3_close(C)] and [sqlite3_close_v2(C)]
@@ -505,22 +501,25 @@
501 #define SQLITE_IOERR_VNODE (SQLITE_IOERR | (27<<8))
502 #define SQLITE_IOERR_AUTH (SQLITE_IOERR | (28<<8))
503 #define SQLITE_IOERR_BEGIN_ATOMIC (SQLITE_IOERR | (29<<8))
504 #define SQLITE_IOERR_COMMIT_ATOMIC (SQLITE_IOERR | (30<<8))
505 #define SQLITE_IOERR_ROLLBACK_ATOMIC (SQLITE_IOERR | (31<<8))
506 #define SQLITE_IOERR_DATA (SQLITE_IOERR | (32<<8))
507 #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8))
508 #define SQLITE_LOCKED_VTAB (SQLITE_LOCKED | (2<<8))
509 #define SQLITE_BUSY_RECOVERY (SQLITE_BUSY | (1<<8))
510 #define SQLITE_BUSY_SNAPSHOT (SQLITE_BUSY | (2<<8))
511 #define SQLITE_BUSY_TIMEOUT (SQLITE_BUSY | (3<<8))
512 #define SQLITE_CANTOPEN_NOTEMPDIR (SQLITE_CANTOPEN | (1<<8))
513 #define SQLITE_CANTOPEN_ISDIR (SQLITE_CANTOPEN | (2<<8))
514 #define SQLITE_CANTOPEN_FULLPATH (SQLITE_CANTOPEN | (3<<8))
515 #define SQLITE_CANTOPEN_CONVPATH (SQLITE_CANTOPEN | (4<<8))
516 #define SQLITE_CANTOPEN_DIRTYWAL (SQLITE_CANTOPEN | (5<<8)) /* Not Used */
517 #define SQLITE_CANTOPEN_SYMLINK (SQLITE_CANTOPEN | (6<<8))
518 #define SQLITE_CORRUPT_VTAB (SQLITE_CORRUPT | (1<<8))
519 #define SQLITE_CORRUPT_SEQUENCE (SQLITE_CORRUPT | (2<<8))
520 #define SQLITE_CORRUPT_INDEX (SQLITE_CORRUPT | (3<<8))
521 #define SQLITE_READONLY_RECOVERY (SQLITE_READONLY | (1<<8))
522 #define SQLITE_READONLY_CANTLOCK (SQLITE_READONLY | (2<<8))
523 #define SQLITE_READONLY_ROLLBACK (SQLITE_READONLY | (3<<8))
524 #define SQLITE_READONLY_DBMOVED (SQLITE_READONLY | (4<<8))
525 #define SQLITE_READONLY_CANTINIT (SQLITE_READONLY | (5<<8))
@@ -1085,14 +1084,16 @@
1084 ** so that all subsequent write operations are independent.
1085 ** ^SQLite will never invoke SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE without
1086 ** a prior successful call to [SQLITE_FCNTL_BEGIN_ATOMIC_WRITE].
1087 **
1088 ** <li>[[SQLITE_FCNTL_LOCK_TIMEOUT]]
1089 ** The [SQLITE_FCNTL_LOCK_TIMEOUT] opcode is used to configure a VFS
1090 ** to block for up to M milliseconds before failing when attempting to
1091 ** obtain a file lock using the xLock or xShmLock methods of the VFS.
1092 ** The parameter is a pointer to a 32-bit signed integer that contains
1093 ** the value that M is to be set to. Before returning, the 32-bit signed
1094 ** integer is overwritten with the previous value of M.
1095 **
1096 ** <li>[[SQLITE_FCNTL_DATA_VERSION]]
1097 ** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
1098 ** a database file. The argument is a pointer to a 32-bit unsigned integer.
1099 ** The "data version" for the pager is written into the pointer. The
@@ -1109,10 +1110,15 @@
1110 ** a single attached database that occur due to other database connections,
1111 ** but omits changes implemented by the database connection on which it is
1112 ** called. This file control is the only mechanism to detect changes that
1113 ** happen either internally or externally and that are associated with
1114 ** a particular attached database.
1115 **
1116 ** <li>[[SQLITE_FCNTL_CKPT_START]]
1117 ** The [SQLITE_FCNTL_CKPT_START] opcode is invoked from within a checkpoint
1118 ** in wal mode before the client starts to copy pages from the wal
1119 ** file to the database file.
1120 **
1121 ** <li>[[SQLITE_FCNTL_CKPT_DONE]]
1122 ** The [SQLITE_FCNTL_CKPT_DONE] opcode is invoked from within a checkpoint
1123 ** in wal mode after the client has finished copying pages from the wal
1124 ** file to the database file, but before the *-shm file is updated to
@@ -1153,10 +1159,12 @@
1159 #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33
1160 #define SQLITE_FCNTL_LOCK_TIMEOUT 34
1161 #define SQLITE_FCNTL_DATA_VERSION 35
1162 #define SQLITE_FCNTL_SIZE_LIMIT 36
1163 #define SQLITE_FCNTL_CKPT_DONE 37
1164 #define SQLITE_FCNTL_RESERVE_BYTES 38
1165 #define SQLITE_FCNTL_CKPT_START 39
1166
1167 /* deprecated names */
1168 #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
1169 #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE
1170 #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO
@@ -3531,12 +3539,23 @@
3539 **
3540 ** These are utility routines, useful to [VFS|custom VFS implementations],
3541 ** that check if a database file was a URI that contained a specific query
3542 ** parameter, and if so obtains the value of that query parameter.
3543 **
3544 ** The first parameter to these interfaces (hereafter referred to
3545 ** as F) must be one of:
3546 ** <ul>
3547 ** <li> A database filename pointer created by the SQLite core and
3548 ** passed into the xOpen() method of a VFS implemention, or
3549 ** <li> A filename obtained from [sqlite3_db_filename()], or
3550 ** <li> A new filename constructed using [sqlite3_create_filename()].
3551 ** </ul>
3552 ** If the F parameter is not one of the above, then the behavior is
3553 ** undefined and probably undesirable. Older versions of SQLite were
3554 ** more tolerant of invalid F parameters than newer versions.
3555 **
3556 ** If F is a suitable filename (as described in the previous paragraph)
3557 ** and if P is the name of the query parameter, then
3558 ** sqlite3_uri_parameter(F,P) returns the value of the P
3559 ** parameter if it exists or a NULL pointer if P does not appear as a
3560 ** query parameter on F. If P is a query parameter of F and it
3561 ** has no explicit value, then sqlite3_uri_parameter(F,P) returns
@@ -3615,10 +3634,29 @@
3634 */
3635 SQLITE_API const char *sqlite3_filename_database(const char*);
3636 SQLITE_API const char *sqlite3_filename_journal(const char*);
3637 SQLITE_API const char *sqlite3_filename_wal(const char*);
3638
3639 /*
3640 ** CAPI3REF: Database File Corresponding To A Journal
3641 **
3642 ** ^If X is the name of a rollback or WAL-mode journal file that is
3643 ** passed into the xOpen method of [sqlite3_vfs], then
3644 ** sqlite3_database_file_object(X) returns a pointer to the [sqlite3_file]
3645 ** object that represents the main database file.
3646 **
3647 ** This routine is intended for use in custom [VFS] implementations
3648 ** only. It is not a general-purpose interface.
3649 ** The argument sqlite3_file_object(X) must be a filename pointer that
3650 ** has been passed into [sqlite3_vfs].xOpen method where the
3651 ** flags parameter to xOpen contains one of the bits
3652 ** [SQLITE_OPEN_MAIN_JOURNAL] or [SQLITE_OPEN_WAL]. Any other use
3653 ** of this routine results in undefined and probably undesirable
3654 ** behavior.
3655 */
3656 SQLITE_API sqlite3_file *sqlite3_database_file_object(const char*);
3657
3658 /*
3659 ** CAPI3REF: Create and Destroy VFS Filenames
3660 **
3661 ** These interfces are provided for use by [VFS shim] implementations and
3662 ** are not useful outside of that context.
@@ -3649,11 +3687,11 @@
3687 ** None of the D, J, or W parameters to sqlite3_create_filename(D,J,W,N,P) may
3688 ** be NULL pointers, though they can be empty strings.
3689 **
3690 ** The sqlite3_free_filename(Y) routine releases a memory allocation
3691 ** previously obtained from sqlite3_create_filename(). Invoking
3692 ** sqlite3_free_filename(Y) where Y is a NULL pointer is a harmless no-op.
3693 **
3694 ** If the Y parameter to sqlite3_free_filename(Y) is anything other
3695 ** than a NULL pointer or a pointer previously acquired from
3696 ** sqlite3_create_filename(), then bad things such as heap
3697 ** corruption or segfaults may occur. The value Y should be
@@ -4256,10 +4294,28 @@
4294 **
4295 ** ^The third argument is the value to bind to the parameter.
4296 ** ^If the third parameter to sqlite3_bind_text() or sqlite3_bind_text16()
4297 ** or sqlite3_bind_blob() is a NULL pointer then the fourth parameter
4298 ** is ignored and the end result is the same as sqlite3_bind_null().
4299 ** ^If the third parameter to sqlite3_bind_text() is not NULL, then
4300 ** it should be a pointer to well-formed UTF8 text.
4301 ** ^If the third parameter to sqlite3_bind_text16() is not NULL, then
4302 ** it should be a pointer to well-formed UTF16 text.
4303 ** ^If the third parameter to sqlite3_bind_text64() is not NULL, then
4304 ** it should be a pointer to a well-formed unicode string that is
4305 ** either UTF8 if the sixth parameter is SQLITE_UTF8, or UTF16
4306 ** otherwise.
4307 **
4308 ** [[byte-order determination rules]] ^The byte-order of
4309 ** UTF16 input text is determined by the byte-order mark (BOM, U+FEFF)
4310 ** found in first character, which is removed, or in the absence of a BOM
4311 ** the byte order is the native byte order of the host
4312 ** machine for sqlite3_bind_text16() or the byte order specified in
4313 ** the 6th parameter for sqlite3_bind_text64().)^
4314 ** ^If UTF16 input text contains invalid unicode
4315 ** characters, then SQLite might change those invalid characters
4316 ** into the unicode replacement character: U+FFFD.
4317 **
4318 ** ^(In those routines that have a fourth argument, its value is the
4319 ** number of bytes in the parameter. To be clear: the value is the
4320 ** number of <u>bytes</u> in the value, not the number of characters.)^
4321 ** ^If the fourth parameter to sqlite3_bind_text() or sqlite3_bind_text16()
@@ -4269,11 +4325,11 @@
4325 ** the behavior is undefined.
4326 ** If a non-negative fourth parameter is provided to sqlite3_bind_text()
4327 ** or sqlite3_bind_text16() or sqlite3_bind_text64() then
4328 ** that parameter must be the byte offset
4329 ** where the NUL terminator would occur assuming the string were NUL
4330 ** terminated. If any NUL characters occurs at byte offsets less than
4331 ** the value of the fourth parameter then the resulting string value will
4332 ** contain embedded NULs. The result of expressions involving strings
4333 ** with embedded NULs is undefined.
4334 **
4335 ** ^The fifth argument to the BLOB and string binding interfaces
@@ -5594,12 +5650,13 @@
5650 ** cause the implemented SQL function to throw an exception.
5651 ** ^SQLite uses the string pointed to by the
5652 ** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16()
5653 ** as the text of an error message. ^SQLite interprets the error
5654 ** message string from sqlite3_result_error() as UTF-8. ^SQLite
5655 ** interprets the string from sqlite3_result_error16() as UTF-16 using
5656 ** the same [byte-order determination rules] as [sqlite3_bind_text16()].
5657 ** ^If the third parameter to sqlite3_result_error()
5658 ** or sqlite3_result_error16() is negative then SQLite takes as the error
5659 ** message all text up through the first zero character.
5660 ** ^If the third parameter to sqlite3_result_error() or
5661 ** sqlite3_result_error16() is non-negative then SQLite takes that many
5662 ** bytes (not characters) from the 2nd parameter as the error message.
@@ -5662,10 +5719,29 @@
5719 ** when it has finished using that result.
5720 ** ^If the 4th parameter to the sqlite3_result_text* interfaces
5721 ** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT
5722 ** then SQLite makes a copy of the result into space obtained
5723 ** from [sqlite3_malloc()] before it returns.
5724 **
5725 ** ^For the sqlite3_result_text16(), sqlite3_result_text16le(), and
5726 ** sqlite3_result_text16be() routines, and for sqlite3_result_text64()
5727 ** when the encoding is not UTF8, if the input UTF16 begins with a
5728 ** byte-order mark (BOM, U+FEFF) then the BOM is removed from the
5729 ** string and the rest of the string is interpreted according to the
5730 ** byte-order specified by the BOM. ^The byte-order specified by
5731 ** the BOM at the beginning of the text overrides the byte-order
5732 ** specified by the interface procedure. ^So, for example, if
5733 ** sqlite3_result_text16le() is invoked with text that begins
5734 ** with bytes 0xfe, 0xff (a big-endian byte-order mark) then the
5735 ** first two bytes of input are skipped and the remaining input
5736 ** is interpreted as UTF16BE text.
5737 **
5738 ** ^For UTF16 input text to the sqlite3_result_text16(),
5739 ** sqlite3_result_text16be(), sqlite3_result_text16le(), and
5740 ** sqlite3_result_text64() routines, if the text contains invalid
5741 ** UTF16 characters, the invalid characters might be converted
5742 ** into the unicode replacement character, U+FFFD.
5743 **
5744 ** ^The sqlite3_result_value() interface sets the result of
5745 ** the application-defined function to be a copy of the
5746 ** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The
5747 ** sqlite3_result_value() interface makes a copy of the [sqlite3_value]
@@ -7610,11 +7686,11 @@
7686 #define SQLITE_TESTCTRL_FAULT_INSTALL 9
7687 #define SQLITE_TESTCTRL_BENIGN_MALLOC_HOOKS 10
7688 #define SQLITE_TESTCTRL_PENDING_BYTE 11
7689 #define SQLITE_TESTCTRL_ASSERT 12
7690 #define SQLITE_TESTCTRL_ALWAYS 13
7691 #define SQLITE_TESTCTRL_RESERVE 14 /* NOT USED */
7692 #define SQLITE_TESTCTRL_OPTIMIZATIONS 15
7693 #define SQLITE_TESTCTRL_ISKEYWORD 16 /* NOT USED */
7694 #define SQLITE_TESTCTRL_SCRATCHMALLOC 17 /* NOT USED */
7695 #define SQLITE_TESTCTRL_INTERNAL_FUNCTIONS 17
7696 #define SQLITE_TESTCTRL_LOCALTIME_FAULT 18
7697
+2 -1
--- src/style.c
+++ src/style.c
@@ -707,11 +707,11 @@
707707
708708
/*
709709
** Generate code to load a single javascript file
710710
*/
711711
void style_load_one_js_file(const char *zFile){
712
- @ <script src='%R/builtin/%s(zFile)?id=%S(MANIFEST_UUID)'></script>
712
+ @ <script src='%R/builtin/%s(zFile)?id=%S(fossil_exe_id())'></script>
713713
}
714714
715715
/*
716716
** All extra JS files to load.
717717
*/
@@ -1259,10 +1259,11 @@
12591259
@ anonymous-adds = %s(find_anon_capabilities(zCap))<br />
12601260
}
12611261
@ g.zRepositoryName = %h(g.zRepositoryName)<br />
12621262
@ load_average() = %f(load_average())<br />
12631263
@ cgi_csrf_safe(0) = %d(cgi_csrf_safe(0))<br />
1264
+ @ fossil_exe_id() = %h(fossil_exe_id())<br />
12641265
@ <hr />
12651266
P("HTTP_USER_AGENT");
12661267
cgi_print_all(showAll, 0);
12671268
if( showAll && blob_size(&g.httpHeader)>0 ){
12681269
@ <hr />
12691270
--- src/style.c
+++ src/style.c
@@ -707,11 +707,11 @@
707
708 /*
709 ** Generate code to load a single javascript file
710 */
711 void style_load_one_js_file(const char *zFile){
712 @ <script src='%R/builtin/%s(zFile)?id=%S(MANIFEST_UUID)'></script>
713 }
714
715 /*
716 ** All extra JS files to load.
717 */
@@ -1259,10 +1259,11 @@
1259 @ anonymous-adds = %s(find_anon_capabilities(zCap))<br />
1260 }
1261 @ g.zRepositoryName = %h(g.zRepositoryName)<br />
1262 @ load_average() = %f(load_average())<br />
1263 @ cgi_csrf_safe(0) = %d(cgi_csrf_safe(0))<br />
 
1264 @ <hr />
1265 P("HTTP_USER_AGENT");
1266 cgi_print_all(showAll, 0);
1267 if( showAll && blob_size(&g.httpHeader)>0 ){
1268 @ <hr />
1269
--- src/style.c
+++ src/style.c
@@ -707,11 +707,11 @@
707
708 /*
709 ** Generate code to load a single javascript file
710 */
711 void style_load_one_js_file(const char *zFile){
712 @ <script src='%R/builtin/%s(zFile)?id=%S(fossil_exe_id())'></script>
713 }
714
715 /*
716 ** All extra JS files to load.
717 */
@@ -1259,10 +1259,11 @@
1259 @ anonymous-adds = %s(find_anon_capabilities(zCap))<br />
1260 }
1261 @ g.zRepositoryName = %h(g.zRepositoryName)<br />
1262 @ load_average() = %f(load_average())<br />
1263 @ cgi_csrf_safe(0) = %d(cgi_csrf_safe(0))<br />
1264 @ fossil_exe_id() = %h(fossil_exe_id())<br />
1265 @ <hr />
1266 P("HTTP_USER_AGENT");
1267 cgi_print_all(showAll, 0);
1268 if( showAll && blob_size(&g.httpHeader)>0 ){
1269 @ <hr />
1270
+1 -13
--- src/sync.c
+++ src/sync.c
@@ -61,22 +61,10 @@
6161
g.url.flags |= URL_PROMPT_PW;
6262
url_prompt_for_password();
6363
}
6464
g.zHttpAuth = get_httpauth();
6565
url_remember();
66
-#if 0 /* Disabled for now */
67
- if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){
68
- /* When doing an automatic pull, also automatically pull shuns from
69
- ** the server if pull_shuns is enabled.
70
- **
71
- ** TODO: What happens if the shun list gets really big?
72
- ** Maybe the shunning list should only be pulled on every 10th
73
- ** autosync, or something?
74
- */
75
- configSync = CONFIGSET_SHUN;
76
- }
77
-#endif
7866
if( find_option("verbose","v",0)!=0 ) flags |= SYNC_VERBOSE;
7967
fossil_print("Autosync: %s\n", g.url.canonical);
8068
url_enable_proxy("via proxy: ");
8169
rc = client_sync(flags, configSync, 0, 0);
8270
return rc;
@@ -163,11 +151,11 @@
163151
url_proxy_options();
164152
clone_ssh_find_options();
165153
if( !uvOnly ) db_find_and_open_repository(0, 0);
166154
db_open_config(0, 1);
167155
if( g.argc==2 ){
168
- if( db_get_boolean("auto-shun",1) ) configSync = CONFIGSET_SHUN;
156
+ if( db_get_boolean("auto-shun",0) ) configSync = CONFIGSET_SHUN;
169157
}else if( g.argc==3 ){
170158
zUrl = g.argv[2];
171159
}
172160
if( ((*pSyncFlags) & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL)
173161
&& db_get_boolean("uv-sync",0)
174162
--- src/sync.c
+++ src/sync.c
@@ -61,22 +61,10 @@
61 g.url.flags |= URL_PROMPT_PW;
62 url_prompt_for_password();
63 }
64 g.zHttpAuth = get_httpauth();
65 url_remember();
66 #if 0 /* Disabled for now */
67 if( (flags & AUTOSYNC_PULL)!=0 && db_get_boolean("auto-shun",1) ){
68 /* When doing an automatic pull, also automatically pull shuns from
69 ** the server if pull_shuns is enabled.
70 **
71 ** TODO: What happens if the shun list gets really big?
72 ** Maybe the shunning list should only be pulled on every 10th
73 ** autosync, or something?
74 */
75 configSync = CONFIGSET_SHUN;
76 }
77 #endif
78 if( find_option("verbose","v",0)!=0 ) flags |= SYNC_VERBOSE;
79 fossil_print("Autosync: %s\n", g.url.canonical);
80 url_enable_proxy("via proxy: ");
81 rc = client_sync(flags, configSync, 0, 0);
82 return rc;
@@ -163,11 +151,11 @@
163 url_proxy_options();
164 clone_ssh_find_options();
165 if( !uvOnly ) db_find_and_open_repository(0, 0);
166 db_open_config(0, 1);
167 if( g.argc==2 ){
168 if( db_get_boolean("auto-shun",1) ) configSync = CONFIGSET_SHUN;
169 }else if( g.argc==3 ){
170 zUrl = g.argv[2];
171 }
172 if( ((*pSyncFlags) & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL)
173 && db_get_boolean("uv-sync",0)
174
--- src/sync.c
+++ src/sync.c
@@ -61,22 +61,10 @@
61 g.url.flags |= URL_PROMPT_PW;
62 url_prompt_for_password();
63 }
64 g.zHttpAuth = get_httpauth();
65 url_remember();
 
 
 
 
 
 
 
 
 
 
 
 
66 if( find_option("verbose","v",0)!=0 ) flags |= SYNC_VERBOSE;
67 fossil_print("Autosync: %s\n", g.url.canonical);
68 url_enable_proxy("via proxy: ");
69 rc = client_sync(flags, configSync, 0, 0);
70 return rc;
@@ -163,11 +151,11 @@
151 url_proxy_options();
152 clone_ssh_find_options();
153 if( !uvOnly ) db_find_and_open_repository(0, 0);
154 db_open_config(0, 1);
155 if( g.argc==2 ){
156 if( db_get_boolean("auto-shun",0) ) configSync = CONFIGSET_SHUN;
157 }else if( g.argc==3 ){
158 zUrl = g.argv[2];
159 }
160 if( ((*pSyncFlags) & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL)
161 && db_get_boolean("uv-sync",0)
162
+1 -1
--- src/tag.c
+++ src/tag.c
@@ -220,11 +220,11 @@
220220
if( zCol ){
221221
db_multi_exec("UPDATE event SET \"%w\"=%Q WHERE objid=%d",
222222
zCol, zValue, rid);
223223
if( tagid==TAG_COMMENT ){
224224
char *zCopy = mprintf("%s", zValue);
225
- wiki_extract_links(zCopy, rid, 0, mtime, 1, WIKI_INLINE);
225
+ backlink_extract(zCopy, 0, rid, BKLNK_COMMENT, mtime, 1);
226226
free(zCopy);
227227
}
228228
}
229229
if( tagid==TAG_DATE ){
230230
db_multi_exec("UPDATE event "
231231
--- src/tag.c
+++ src/tag.c
@@ -220,11 +220,11 @@
220 if( zCol ){
221 db_multi_exec("UPDATE event SET \"%w\"=%Q WHERE objid=%d",
222 zCol, zValue, rid);
223 if( tagid==TAG_COMMENT ){
224 char *zCopy = mprintf("%s", zValue);
225 wiki_extract_links(zCopy, rid, 0, mtime, 1, WIKI_INLINE);
226 free(zCopy);
227 }
228 }
229 if( tagid==TAG_DATE ){
230 db_multi_exec("UPDATE event "
231
--- src/tag.c
+++ src/tag.c
@@ -220,11 +220,11 @@
220 if( zCol ){
221 db_multi_exec("UPDATE event SET \"%w\"=%Q WHERE objid=%d",
222 zCol, zValue, rid);
223 if( tagid==TAG_COMMENT ){
224 char *zCopy = mprintf("%s", zValue);
225 backlink_extract(zCopy, 0, rid, BKLNK_COMMENT, mtime, 1);
226 free(zCopy);
227 }
228 }
229 if( tagid==TAG_DATE ){
230 db_multi_exec("UPDATE event "
231
+1 -1
--- src/tar.c
+++ src/tar.c
@@ -443,11 +443,11 @@
443443
tar_begin(-1);
444444
for(i=3; i<g.argc; i++){
445445
Blob file;
446446
blob_zero(&file);
447447
blob_read_from_file(&file, g.argv[i], eFType);
448
- tar_add_file(g.argv[i], &file, file_perm(0,0), file_mtime(0,0));
448
+ tar_add_file(g.argv[i], &file, file_perm(0,eFType), file_mtime(0,eFType));
449449
blob_reset(&file);
450450
}
451451
tar_finish(&zip);
452452
blob_write_to_file(&zip, g.argv[2]);
453453
}
454454
455455
ADDED src/terminal.c
--- src/tar.c
+++ src/tar.c
@@ -443,11 +443,11 @@
443 tar_begin(-1);
444 for(i=3; i<g.argc; i++){
445 Blob file;
446 blob_zero(&file);
447 blob_read_from_file(&file, g.argv[i], eFType);
448 tar_add_file(g.argv[i], &file, file_perm(0,0), file_mtime(0,0));
449 blob_reset(&file);
450 }
451 tar_finish(&zip);
452 blob_write_to_file(&zip, g.argv[2]);
453 }
454
455 DDED src/terminal.c
--- src/tar.c
+++ src/tar.c
@@ -443,11 +443,11 @@
443 tar_begin(-1);
444 for(i=3; i<g.argc; i++){
445 Blob file;
446 blob_zero(&file);
447 blob_read_from_file(&file, g.argv[i], eFType);
448 tar_add_file(g.argv[i], &file, file_perm(0,eFType), file_mtime(0,eFType));
449 blob_reset(&file);
450 }
451 tar_finish(&zip);
452 blob_write_to_file(&zip, g.argv[2]);
453 }
454
455 DDED src/terminal.c
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -0,0 +1,9 @@
1
+COMMAND: test-terminal-size
2
+**
3
+** Show the size of the terminal window from which the command is launched
4
+** as two integers, the width in characters and the height in lines.
5
+**
6
+** If the size cannot be determined, two zeros are shown.
7
+*/
8
+void test_terminal_size_cmd(void* Technically, this infosuch is supported; set;
9
+ fossil_print("%d %d\nchar *zNoC!=-1!=-1
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
--- a/src/terminal.c
+++ b/src/terminal.c
@@ -0,0 +1,9 @@
1 COMMAND: test-terminal-size
2 **
3 ** Show the size of the terminal window from which the command is launched
4 ** as two integers, the width in characters and the height in lines.
5 **
6 ** If the size cannot be determined, two zeros are shown.
7 */
8 void test_terminal_size_cmd(void* Technically, this infosuch is supported; set;
9 fossil_print("%d %d\nchar *zNoC!=-1!=-1
+51 -5
--- src/timeline.c
+++ src/timeline.c
@@ -41,11 +41,11 @@
4141
** Add an appropriate tag to the output if "rid" is unpublished (private)
4242
*/
4343
#define UNPUB_TAG "<em>(unpublished)</em>"
4444
void tag_private_status(int rid){
4545
if( content_is_private(rid) ){
46
- cgi_printf("%s", UNPUB_TAG);
46
+ cgi_printf(" %s", UNPUB_TAG);
4747
}
4848
}
4949
5050
/*
5151
** Generate a hyperlink to a version.
@@ -116,10 +116,11 @@
116116
#define TIMELINE_CHPICK 0x0400000 /* Show cherrypick merges */
117117
#define TIMELINE_FILLGAPS 0x0800000 /* Dotted lines for missing nodes */
118118
#define TIMELINE_XMERGE 0x1000000 /* Omit merges from off-graph nodes */
119119
#define TIMELINE_NOTKT 0x2000000 /* Omit extra ticket classes */
120120
#define TIMELINE_FORUMTXT 0x4000000 /* Render all forum messages */
121
+#define TIMELINE_REFS 0x8000000 /* Output intended for References tab */
121122
#endif
122123
123124
/*
124125
** Hash a string and use the hash to determine a background color.
125126
*/
@@ -560,12 +561,27 @@
560561
}
561562
if( zType[0]!='c' ){
562563
/* Comments for anything other than a check-in are generated by
563564
** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */
564565
if( zType[0]=='w' ){
566
+ const char *zCom = blob_str(&comment);
567
+ char *zWiki;
565568
wiki_hyperlink_override(zUuid);
566
- wiki_convert(&comment, 0, WIKI_INLINE);
569
+ if( (tmFlags & TIMELINE_REFS)!=0
570
+ && (zWiki = strstr(zCom,"wiki"))!=0
571
+ ){
572
+ /* The TIMELINE_REFS flag causes timeline comments of the
573
+ ** form "Changes to wiki..." or "Added wiki" to be changed
574
+ ** into just "Wiki..." */
575
+ Blob rcom;
576
+ blob_init(&rcom, 0, 0);
577
+ blob_appendf(&rcom, "W%s", zWiki+1);
578
+ wiki_convert(&rcom, 0, WIKI_INLINE);
579
+ blob_reset(&rcom);
580
+ }else{
581
+ wiki_convert(&comment, 0, WIKI_INLINE);
582
+ }
567583
wiki_hyperlink_override(0);
568584
}else{
569585
wiki_convert(&comment, 0, WIKI_INLINE);
570586
}
571587
}else{
@@ -641,11 +657,16 @@
641657
|| zType[0]=='n' || zType[0]=='f'){
642658
cgi_printf("artifact:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
643659
}
644660
645661
if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){
646
- char *zLink = mprintf("%R/timeline?u=%h&c=%t&y=a", zDispUser, zDate);
662
+ char *zLink;
663
+ if( zType[0]!='f' || (tmFlags & TIMELINE_FORUMTXT)==0 ){
664
+ zLink = mprintf("%R/timeline?u=%h&c=%t&y=a", zDispUser, zDate);
665
+ }else{
666
+ zLink = mprintf("%R/timeline?u=%h&c=%t&y=a&vfx", zDispUser, zDate);
667
+ }
647668
cgi_printf("user:&nbsp;%z%h</a>", href("%z",zLink), zDispUser);
648669
}else{
649670
cgi_printf("user:&nbsp;%h", zDispUser);
650671
}
651672
@@ -1291,11 +1312,11 @@
12911312
){
12921313
if( zChng==0 || zChng[0]==0 ) return;
12931314
blob_append_sql(pSql," AND event.objid IN ("
12941315
"SELECT mlink.mid FROM mlink, filename"
12951316
" WHERE mlink.fnid=filename.fnid AND %s)",
1296
- glob_expr("filename.name", zChng));
1317
+ glob_expr("filename.name", mprintf("\"%s\"", zChng)));
12971318
}
12981319
static void addFileGlobDescription(
12991320
const char *zChng, /* The filename GLOB list */
13001321
Blob *pDescription /* Result description */
13011322
){
@@ -1721,10 +1742,11 @@
17211742
|| (bisectLocal && !g.perm.Setup)
17221743
){
17231744
login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
17241745
return;
17251746
}
1747
+ etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA, 0);
17261748
cookie_read_parameter("y","y");
17271749
zType = P("y");
17281750
if( zType==0 ){
17291751
zType = g.perm.Read ? "ci" : "all";
17301752
cgi_set_parameter("y", zType);
@@ -1856,11 +1878,25 @@
18561878
" HAVING count(*)>1;\n"
18571879
"INSERT OR IGNORE INTO rnfork(rid)"
18581880
" SELECT cid FROM plink\n"
18591881
" WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)=="
18601882
" (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
1861
- " AND pid IN rnfork;",
1883
+ " GROUP BY cid"
1884
+ " HAVING count(*)>1;\n",
1885
+ TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH
1886
+ );
1887
+ db_multi_exec(
1888
+ "INSERT OR IGNORE INTO rnfork(rid)\n"
1889
+ " SELECT cid FROM plink\n"
1890
+ " WHERE pid IN rnfork"
1891
+ " AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)=="
1892
+ " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
1893
+ " UNION "
1894
+ " SELECT pid FROM plink\n"
1895
+ " WHERE cid IN rnfork"
1896
+ " AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)=="
1897
+ " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n",
18621898
TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH
18631899
);
18641900
tmFlags |= TIMELINE_UNHIDE;
18651901
zType = "ci";
18661902
disableY = 1;
@@ -2165,10 +2201,17 @@
21652201
"CREATE TEMP TABLE selected_nodes(rid INTEGER PRIMARY KEY);"
21662202
"INSERT OR IGNORE INTO selected_nodes"
21672203
" SELECT tagxref.rid FROM tagxref NATURAL JOIN tag"
21682204
" WHERE %s AND tagtype>0", zTagSql/*safe-for-%s*/
21692205
);
2206
+ if( zMark ){
2207
+ /* If the t=release option is used with m=UUID, then also
2208
+ ** include the UUID check-in in the display list */
2209
+ int ridMark = name_to_rid(zMark);
2210
+ db_multi_exec(
2211
+ "INSERT OR IGNORE INTO selected_nodes(rid) VALUES(%d)", ridMark);
2212
+ }
21702213
if( !related ){
21712214
blob_append_sql(&cond, " AND blob.rid IN selected_nodes");
21722215
}else{
21732216
db_multi_exec(
21742217
"CREATE TEMP TABLE related_nodes(rid INTEGER PRIMARY KEY);"
@@ -2391,10 +2434,13 @@
23912434
blob_appendf(&desc, " related to tags matching %h", zMatchDesc);
23922435
}else{
23932436
blob_appendf(&desc, " with tags matching %h", zMatchDesc);
23942437
}
23952438
}
2439
+ if( zMark ){
2440
+ blob_appendf(&desc," plus check-in \"%h\"", zMark);
2441
+ }
23962442
tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
23972443
}
23982444
addFileGlobDescription(zChng, &desc);
23992445
if( rAfter>0.0 ){
24002446
if( rBefore>0.0 ){
24012447
--- src/timeline.c
+++ src/timeline.c
@@ -41,11 +41,11 @@
41 ** Add an appropriate tag to the output if "rid" is unpublished (private)
42 */
43 #define UNPUB_TAG "<em>(unpublished)</em>"
44 void tag_private_status(int rid){
45 if( content_is_private(rid) ){
46 cgi_printf("%s", UNPUB_TAG);
47 }
48 }
49
50 /*
51 ** Generate a hyperlink to a version.
@@ -116,10 +116,11 @@
116 #define TIMELINE_CHPICK 0x0400000 /* Show cherrypick merges */
117 #define TIMELINE_FILLGAPS 0x0800000 /* Dotted lines for missing nodes */
118 #define TIMELINE_XMERGE 0x1000000 /* Omit merges from off-graph nodes */
119 #define TIMELINE_NOTKT 0x2000000 /* Omit extra ticket classes */
120 #define TIMELINE_FORUMTXT 0x4000000 /* Render all forum messages */
 
121 #endif
122
123 /*
124 ** Hash a string and use the hash to determine a background color.
125 */
@@ -560,12 +561,27 @@
560 }
561 if( zType[0]!='c' ){
562 /* Comments for anything other than a check-in are generated by
563 ** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */
564 if( zType[0]=='w' ){
 
 
565 wiki_hyperlink_override(zUuid);
566 wiki_convert(&comment, 0, WIKI_INLINE);
 
 
 
 
 
 
 
 
 
 
 
 
 
567 wiki_hyperlink_override(0);
568 }else{
569 wiki_convert(&comment, 0, WIKI_INLINE);
570 }
571 }else{
@@ -641,11 +657,16 @@
641 || zType[0]=='n' || zType[0]=='f'){
642 cgi_printf("artifact:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
643 }
644
645 if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){
646 char *zLink = mprintf("%R/timeline?u=%h&c=%t&y=a", zDispUser, zDate);
 
 
 
 
 
647 cgi_printf("user:&nbsp;%z%h</a>", href("%z",zLink), zDispUser);
648 }else{
649 cgi_printf("user:&nbsp;%h", zDispUser);
650 }
651
@@ -1291,11 +1312,11 @@
1291 ){
1292 if( zChng==0 || zChng[0]==0 ) return;
1293 blob_append_sql(pSql," AND event.objid IN ("
1294 "SELECT mlink.mid FROM mlink, filename"
1295 " WHERE mlink.fnid=filename.fnid AND %s)",
1296 glob_expr("filename.name", zChng));
1297 }
1298 static void addFileGlobDescription(
1299 const char *zChng, /* The filename GLOB list */
1300 Blob *pDescription /* Result description */
1301 ){
@@ -1721,10 +1742,11 @@
1721 || (bisectLocal && !g.perm.Setup)
1722 ){
1723 login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
1724 return;
1725 }
 
1726 cookie_read_parameter("y","y");
1727 zType = P("y");
1728 if( zType==0 ){
1729 zType = g.perm.Read ? "ci" : "all";
1730 cgi_set_parameter("y", zType);
@@ -1856,11 +1878,25 @@
1856 " HAVING count(*)>1;\n"
1857 "INSERT OR IGNORE INTO rnfork(rid)"
1858 " SELECT cid FROM plink\n"
1859 " WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)=="
1860 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
1861 " AND pid IN rnfork;",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1862 TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH
1863 );
1864 tmFlags |= TIMELINE_UNHIDE;
1865 zType = "ci";
1866 disableY = 1;
@@ -2165,10 +2201,17 @@
2165 "CREATE TEMP TABLE selected_nodes(rid INTEGER PRIMARY KEY);"
2166 "INSERT OR IGNORE INTO selected_nodes"
2167 " SELECT tagxref.rid FROM tagxref NATURAL JOIN tag"
2168 " WHERE %s AND tagtype>0", zTagSql/*safe-for-%s*/
2169 );
 
 
 
 
 
 
 
2170 if( !related ){
2171 blob_append_sql(&cond, " AND blob.rid IN selected_nodes");
2172 }else{
2173 db_multi_exec(
2174 "CREATE TEMP TABLE related_nodes(rid INTEGER PRIMARY KEY);"
@@ -2391,10 +2434,13 @@
2391 blob_appendf(&desc, " related to tags matching %h", zMatchDesc);
2392 }else{
2393 blob_appendf(&desc, " with tags matching %h", zMatchDesc);
2394 }
2395 }
 
 
 
2396 tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
2397 }
2398 addFileGlobDescription(zChng, &desc);
2399 if( rAfter>0.0 ){
2400 if( rBefore>0.0 ){
2401
--- src/timeline.c
+++ src/timeline.c
@@ -41,11 +41,11 @@
41 ** Add an appropriate tag to the output if "rid" is unpublished (private)
42 */
43 #define UNPUB_TAG "<em>(unpublished)</em>"
44 void tag_private_status(int rid){
45 if( content_is_private(rid) ){
46 cgi_printf(" %s", UNPUB_TAG);
47 }
48 }
49
50 /*
51 ** Generate a hyperlink to a version.
@@ -116,10 +116,11 @@
116 #define TIMELINE_CHPICK 0x0400000 /* Show cherrypick merges */
117 #define TIMELINE_FILLGAPS 0x0800000 /* Dotted lines for missing nodes */
118 #define TIMELINE_XMERGE 0x1000000 /* Omit merges from off-graph nodes */
119 #define TIMELINE_NOTKT 0x2000000 /* Omit extra ticket classes */
120 #define TIMELINE_FORUMTXT 0x4000000 /* Render all forum messages */
121 #define TIMELINE_REFS 0x8000000 /* Output intended for References tab */
122 #endif
123
124 /*
125 ** Hash a string and use the hash to determine a background color.
126 */
@@ -560,12 +561,27 @@
561 }
562 if( zType[0]!='c' ){
563 /* Comments for anything other than a check-in are generated by
564 ** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */
565 if( zType[0]=='w' ){
566 const char *zCom = blob_str(&comment);
567 char *zWiki;
568 wiki_hyperlink_override(zUuid);
569 if( (tmFlags & TIMELINE_REFS)!=0
570 && (zWiki = strstr(zCom,"wiki"))!=0
571 ){
572 /* The TIMELINE_REFS flag causes timeline comments of the
573 ** form "Changes to wiki..." or "Added wiki" to be changed
574 ** into just "Wiki..." */
575 Blob rcom;
576 blob_init(&rcom, 0, 0);
577 blob_appendf(&rcom, "W%s", zWiki+1);
578 wiki_convert(&rcom, 0, WIKI_INLINE);
579 blob_reset(&rcom);
580 }else{
581 wiki_convert(&comment, 0, WIKI_INLINE);
582 }
583 wiki_hyperlink_override(0);
584 }else{
585 wiki_convert(&comment, 0, WIKI_INLINE);
586 }
587 }else{
@@ -641,11 +657,16 @@
657 || zType[0]=='n' || zType[0]=='f'){
658 cgi_printf("artifact:&nbsp;%z%S</a> ",href("%R/info/%!S",zUuid),zUuid);
659 }
660
661 if( g.perm.Hyperlink && fossil_strcmp(zDispUser, zThisUser)!=0 ){
662 char *zLink;
663 if( zType[0]!='f' || (tmFlags & TIMELINE_FORUMTXT)==0 ){
664 zLink = mprintf("%R/timeline?u=%h&c=%t&y=a", zDispUser, zDate);
665 }else{
666 zLink = mprintf("%R/timeline?u=%h&c=%t&y=a&vfx", zDispUser, zDate);
667 }
668 cgi_printf("user:&nbsp;%z%h</a>", href("%z",zLink), zDispUser);
669 }else{
670 cgi_printf("user:&nbsp;%h", zDispUser);
671 }
672
@@ -1291,11 +1312,11 @@
1312 ){
1313 if( zChng==0 || zChng[0]==0 ) return;
1314 blob_append_sql(pSql," AND event.objid IN ("
1315 "SELECT mlink.mid FROM mlink, filename"
1316 " WHERE mlink.fnid=filename.fnid AND %s)",
1317 glob_expr("filename.name", mprintf("\"%s\"", zChng)));
1318 }
1319 static void addFileGlobDescription(
1320 const char *zChng, /* The filename GLOB list */
1321 Blob *pDescription /* Result description */
1322 ){
@@ -1721,10 +1742,11 @@
1742 || (bisectLocal && !g.perm.Setup)
1743 ){
1744 login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
1745 return;
1746 }
1747 etag_check(ETAG_QUERY|ETAG_COOKIE|ETAG_DATA, 0);
1748 cookie_read_parameter("y","y");
1749 zType = P("y");
1750 if( zType==0 ){
1751 zType = g.perm.Read ? "ci" : "all";
1752 cgi_set_parameter("y", zType);
@@ -1856,11 +1878,25 @@
1878 " HAVING count(*)>1;\n"
1879 "INSERT OR IGNORE INTO rnfork(rid)"
1880 " SELECT cid FROM plink\n"
1881 " WHERE (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)=="
1882 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
1883 " GROUP BY cid"
1884 " HAVING count(*)>1;\n",
1885 TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH
1886 );
1887 db_multi_exec(
1888 "INSERT OR IGNORE INTO rnfork(rid)\n"
1889 " SELECT cid FROM plink\n"
1890 " WHERE pid IN rnfork"
1891 " AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)=="
1892 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n"
1893 " UNION "
1894 " SELECT pid FROM plink\n"
1895 " WHERE cid IN rnfork"
1896 " AND (SELECT value FROM tagxref WHERE tagid=%d AND rid=cid)=="
1897 " (SELECT value FROM tagxref WHERE tagid=%d AND rid=pid)\n",
1898 TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH
1899 );
1900 tmFlags |= TIMELINE_UNHIDE;
1901 zType = "ci";
1902 disableY = 1;
@@ -2165,10 +2201,17 @@
2201 "CREATE TEMP TABLE selected_nodes(rid INTEGER PRIMARY KEY);"
2202 "INSERT OR IGNORE INTO selected_nodes"
2203 " SELECT tagxref.rid FROM tagxref NATURAL JOIN tag"
2204 " WHERE %s AND tagtype>0", zTagSql/*safe-for-%s*/
2205 );
2206 if( zMark ){
2207 /* If the t=release option is used with m=UUID, then also
2208 ** include the UUID check-in in the display list */
2209 int ridMark = name_to_rid(zMark);
2210 db_multi_exec(
2211 "INSERT OR IGNORE INTO selected_nodes(rid) VALUES(%d)", ridMark);
2212 }
2213 if( !related ){
2214 blob_append_sql(&cond, " AND blob.rid IN selected_nodes");
2215 }else{
2216 db_multi_exec(
2217 "CREATE TEMP TABLE related_nodes(rid INTEGER PRIMARY KEY);"
@@ -2391,10 +2434,13 @@
2434 blob_appendf(&desc, " related to tags matching %h", zMatchDesc);
2435 }else{
2436 blob_appendf(&desc, " with tags matching %h", zMatchDesc);
2437 }
2438 }
2439 if( zMark ){
2440 blob_appendf(&desc," plus check-in \"%h\"", zMark);
2441 }
2442 tmFlags |= TIMELINE_XMERGE | TIMELINE_FILLGAPS;
2443 }
2444 addFileGlobDescription(zChng, &desc);
2445 if( rAfter>0.0 ){
2446 if( rBefore>0.0 ){
2447
+32 -6
--- src/tkt.c
+++ src/tkt.c
@@ -194,10 +194,11 @@
194194
static int ticket_insert(const Manifest *p, int rid, int tktid){
195195
Blob sql1, sql2, sql3;
196196
Stmt q;
197197
int i, j;
198198
char *aUsed;
199
+ const char *zMimetype = 0;
199200
200201
if( tktid==0 ){
201202
db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) "
202203
"VALUES(%Q, 0)", p->zTicketUuid);
203204
tktid = db_last_insert_rowid();
@@ -233,12 +234,22 @@
233234
zUsedByName++;
234235
}
235236
blob_append_sql(&sql2, ",\"%w\"", zUsedByName);
236237
blob_append_sql(&sql3, ",%Q", p->aField[i].zValue);
237238
}
238
- if( rid>0 ){
239
- wiki_extract_links(p->aField[i].zValue, rid, 1, p->rDate, i==0, 0);
239
+ if( strcmp(zBaseName,"mimetype")==0 ){
240
+ zMimetype = p->aField[i].zValue;
241
+ }
242
+ }
243
+ if( rid>0 ){
244
+ for(i=0; i<p->nField; i++){
245
+ const char *zName = p->aField[i].zName;
246
+ const char *zBaseName = zName[0]=='+' ? zName+1 : zName;
247
+ j = fieldId(zBaseName);
248
+ if( j<0 ) continue;
249
+ backlink_extract(p->aField[i].zValue, zMimetype, rid, BKLNK_TICKET,
250
+ p->rDate, i==0);
240251
}
241252
}
242253
blob_append_sql(&sql1, " WHERE tkt_id=%d", tktid);
243254
db_prepare(&q, "%s", blob_sql_text(&sql1));
244255
db_bind_double(&q, ":mtime", p->rDate);
@@ -845,10 +856,14 @@
845856
}
846857
847858
/*
848859
** Draw a timeline for a ticket with tag.tagid given by the tagid
849860
** parameter.
861
+**
862
+** If zType[0]=='c' then only show check-ins associated with the
863
+** ticket. For any other value of zType, show all events associated
864
+** with the ticket.
850865
*/
851866
void tkt_draw_timeline(int tagid, const char *zType){
852867
Stmt q;
853868
char *zFullUuid;
854869
char *zSQL;
@@ -855,20 +870,26 @@
855870
zFullUuid = db_text(0, "SELECT substr(tagname, 5) FROM tag WHERE tagid=%d",
856871
tagid);
857872
if( zType[0]=='c' ){
858873
zSQL = mprintf(
859874
"%s AND event.objid IN "
860
- " (SELECT srcid FROM backlink WHERE target GLOB '%.4s*' "
875
+ " (SELECT srcid FROM backlink WHERE target GLOB '%.4s*' "
876
+ "AND srctype=0 "
861877
"AND '%s' GLOB (target||'*')) "
862878
"ORDER BY mtime DESC",
863879
timeline_query_for_www(), zFullUuid, zFullUuid
864880
);
865881
}else{
866882
zSQL = mprintf(
867883
"%s AND event.objid IN "
868884
" (SELECT rid FROM tagxref WHERE tagid=%d"
869
- " UNION SELECT srcid FROM backlink"
885
+ " UNION"
886
+ " SELECT CASE srctype WHEN 2 THEN"
887
+ " (SELECT rid FROM tagxref WHERE tagid=backlink.srcid"
888
+ " ORDER BY mtime DESC LIMIT 1)"
889
+ " ELSE srcid END"
890
+ " FROM backlink"
870891
" WHERE target GLOB '%.4s*'"
871892
" AND '%s' GLOB (target||'*')"
872893
" UNION SELECT attachid FROM attachment"
873894
" WHERE target=%Q) "
874895
"ORDER BY mtime DESC",
@@ -875,21 +896,26 @@
875896
timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid
876897
);
877898
}
878899
db_prepare(&q, "%z", zSQL/*safe-for-%s*/);
879900
www_print_timeline(&q,
880
- TIMELINE_ARTID | TIMELINE_DISJOINT | TIMELINE_GRAPH | TIMELINE_NOTKT,
901
+ TIMELINE_ARTID | TIMELINE_DISJOINT | TIMELINE_GRAPH | TIMELINE_NOTKT |
902
+ TIMELINE_REFS,
881903
0, 0, 0, 0, 0, 0);
882904
db_finalize(&q);
883905
fossil_free(zFullUuid);
884906
}
885907
886908
/*
887909
** WEBPAGE: tkttimeline
888
-** URL: /tkttimeline?name=TICKETUUID&y=TYPE
910
+** URL: /tkttimeline/TICKETUUID
889911
**
890912
** Show the change history for a single ticket in timeline format.
913
+**
914
+** Query parameters:
915
+**
916
+** y=ci Show only check-ins associated with the ticket
891917
*/
892918
void tkttimeline_page(void){
893919
char *zTitle;
894920
const char *zUuid;
895921
int tagid;
896922
--- src/tkt.c
+++ src/tkt.c
@@ -194,10 +194,11 @@
194 static int ticket_insert(const Manifest *p, int rid, int tktid){
195 Blob sql1, sql2, sql3;
196 Stmt q;
197 int i, j;
198 char *aUsed;
 
199
200 if( tktid==0 ){
201 db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) "
202 "VALUES(%Q, 0)", p->zTicketUuid);
203 tktid = db_last_insert_rowid();
@@ -233,12 +234,22 @@
233 zUsedByName++;
234 }
235 blob_append_sql(&sql2, ",\"%w\"", zUsedByName);
236 blob_append_sql(&sql3, ",%Q", p->aField[i].zValue);
237 }
238 if( rid>0 ){
239 wiki_extract_links(p->aField[i].zValue, rid, 1, p->rDate, i==0, 0);
 
 
 
 
 
 
 
 
 
 
240 }
241 }
242 blob_append_sql(&sql1, " WHERE tkt_id=%d", tktid);
243 db_prepare(&q, "%s", blob_sql_text(&sql1));
244 db_bind_double(&q, ":mtime", p->rDate);
@@ -845,10 +856,14 @@
845 }
846
847 /*
848 ** Draw a timeline for a ticket with tag.tagid given by the tagid
849 ** parameter.
 
 
 
 
850 */
851 void tkt_draw_timeline(int tagid, const char *zType){
852 Stmt q;
853 char *zFullUuid;
854 char *zSQL;
@@ -855,20 +870,26 @@
855 zFullUuid = db_text(0, "SELECT substr(tagname, 5) FROM tag WHERE tagid=%d",
856 tagid);
857 if( zType[0]=='c' ){
858 zSQL = mprintf(
859 "%s AND event.objid IN "
860 " (SELECT srcid FROM backlink WHERE target GLOB '%.4s*' "
 
861 "AND '%s' GLOB (target||'*')) "
862 "ORDER BY mtime DESC",
863 timeline_query_for_www(), zFullUuid, zFullUuid
864 );
865 }else{
866 zSQL = mprintf(
867 "%s AND event.objid IN "
868 " (SELECT rid FROM tagxref WHERE tagid=%d"
869 " UNION SELECT srcid FROM backlink"
 
 
 
 
 
870 " WHERE target GLOB '%.4s*'"
871 " AND '%s' GLOB (target||'*')"
872 " UNION SELECT attachid FROM attachment"
873 " WHERE target=%Q) "
874 "ORDER BY mtime DESC",
@@ -875,21 +896,26 @@
875 timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid
876 );
877 }
878 db_prepare(&q, "%z", zSQL/*safe-for-%s*/);
879 www_print_timeline(&q,
880 TIMELINE_ARTID | TIMELINE_DISJOINT | TIMELINE_GRAPH | TIMELINE_NOTKT,
 
881 0, 0, 0, 0, 0, 0);
882 db_finalize(&q);
883 fossil_free(zFullUuid);
884 }
885
886 /*
887 ** WEBPAGE: tkttimeline
888 ** URL: /tkttimeline?name=TICKETUUID&y=TYPE
889 **
890 ** Show the change history for a single ticket in timeline format.
 
 
 
 
891 */
892 void tkttimeline_page(void){
893 char *zTitle;
894 const char *zUuid;
895 int tagid;
896
--- src/tkt.c
+++ src/tkt.c
@@ -194,10 +194,11 @@
194 static int ticket_insert(const Manifest *p, int rid, int tktid){
195 Blob sql1, sql2, sql3;
196 Stmt q;
197 int i, j;
198 char *aUsed;
199 const char *zMimetype = 0;
200
201 if( tktid==0 ){
202 db_multi_exec("INSERT INTO ticket(tkt_uuid, tkt_mtime) "
203 "VALUES(%Q, 0)", p->zTicketUuid);
204 tktid = db_last_insert_rowid();
@@ -233,12 +234,22 @@
234 zUsedByName++;
235 }
236 blob_append_sql(&sql2, ",\"%w\"", zUsedByName);
237 blob_append_sql(&sql3, ",%Q", p->aField[i].zValue);
238 }
239 if( strcmp(zBaseName,"mimetype")==0 ){
240 zMimetype = p->aField[i].zValue;
241 }
242 }
243 if( rid>0 ){
244 for(i=0; i<p->nField; i++){
245 const char *zName = p->aField[i].zName;
246 const char *zBaseName = zName[0]=='+' ? zName+1 : zName;
247 j = fieldId(zBaseName);
248 if( j<0 ) continue;
249 backlink_extract(p->aField[i].zValue, zMimetype, rid, BKLNK_TICKET,
250 p->rDate, i==0);
251 }
252 }
253 blob_append_sql(&sql1, " WHERE tkt_id=%d", tktid);
254 db_prepare(&q, "%s", blob_sql_text(&sql1));
255 db_bind_double(&q, ":mtime", p->rDate);
@@ -845,10 +856,14 @@
856 }
857
858 /*
859 ** Draw a timeline for a ticket with tag.tagid given by the tagid
860 ** parameter.
861 **
862 ** If zType[0]=='c' then only show check-ins associated with the
863 ** ticket. For any other value of zType, show all events associated
864 ** with the ticket.
865 */
866 void tkt_draw_timeline(int tagid, const char *zType){
867 Stmt q;
868 char *zFullUuid;
869 char *zSQL;
@@ -855,20 +870,26 @@
870 zFullUuid = db_text(0, "SELECT substr(tagname, 5) FROM tag WHERE tagid=%d",
871 tagid);
872 if( zType[0]=='c' ){
873 zSQL = mprintf(
874 "%s AND event.objid IN "
875 " (SELECT srcid FROM backlink WHERE target GLOB '%.4s*' "
876 "AND srctype=0 "
877 "AND '%s' GLOB (target||'*')) "
878 "ORDER BY mtime DESC",
879 timeline_query_for_www(), zFullUuid, zFullUuid
880 );
881 }else{
882 zSQL = mprintf(
883 "%s AND event.objid IN "
884 " (SELECT rid FROM tagxref WHERE tagid=%d"
885 " UNION"
886 " SELECT CASE srctype WHEN 2 THEN"
887 " (SELECT rid FROM tagxref WHERE tagid=backlink.srcid"
888 " ORDER BY mtime DESC LIMIT 1)"
889 " ELSE srcid END"
890 " FROM backlink"
891 " WHERE target GLOB '%.4s*'"
892 " AND '%s' GLOB (target||'*')"
893 " UNION SELECT attachid FROM attachment"
894 " WHERE target=%Q) "
895 "ORDER BY mtime DESC",
@@ -875,21 +896,26 @@
896 timeline_query_for_www(), tagid, zFullUuid, zFullUuid, zFullUuid
897 );
898 }
899 db_prepare(&q, "%z", zSQL/*safe-for-%s*/);
900 www_print_timeline(&q,
901 TIMELINE_ARTID | TIMELINE_DISJOINT | TIMELINE_GRAPH | TIMELINE_NOTKT |
902 TIMELINE_REFS,
903 0, 0, 0, 0, 0, 0);
904 db_finalize(&q);
905 fossil_free(zFullUuid);
906 }
907
908 /*
909 ** WEBPAGE: tkttimeline
910 ** URL: /tkttimeline/TICKETUUID
911 **
912 ** Show the change history for a single ticket in timeline format.
913 **
914 ** Query parameters:
915 **
916 ** y=ci Show only check-ins associated with the ticket
917 */
918 void tkttimeline_page(void){
919 char *zTitle;
920 const char *zUuid;
921 int tagid;
922
+51 -10
--- src/unversioned.c
+++ src/unversioned.c
@@ -237,11 +237,14 @@
237237
** edit FILE Bring up FILE in a text editor for modification.
238238
**
239239
** export FILE OUTPUT Write the content of FILE into OUTPUT on disk
240240
**
241241
** list | ls Show all unversioned files held in the local
242
-** repository.
242
+** repository. Options:
243
+**
244
+** --glob PATTERN Show only files that match
245
+** --like PATTERN Show only files that match
243246
**
244247
** revert ?URL? Restore the state of all unversioned files in the
245248
** local repository to match the remote repository
246249
** URL.
247250
**
@@ -250,11 +253,14 @@
250253
** -n|--dryrun Show what would have happened
251254
**
252255
** remove|rm|delete FILE ...
253256
** Remove unversioned files from the local repository.
254257
** Changes are not pushed to other repositories until
255
-** the next sync.
258
+** the next sync. Options:
259
+**
260
+** --glob PATTERN Remove files that match
261
+** --like PATTERN Remove files that match
256262
**
257263
** sync ?URL? Synchronize the state of all unversioned files with
258264
** the remote repository URL. The most recent version
259265
** of each file is propagated to all repositories and
260266
** all prior versions are permanently forgotten.
@@ -339,11 +345,13 @@
339345
340346
verify_all_options();
341347
if( g.argc!=4) usage("edit UVFILE");
342348
zUVFile = g.argv[3];
343349
zEditor = fossil_text_editor();
344
- if( zEditor==0 ) fossil_fatal("no text editor - set the VISUAL env variable");
350
+ if( zEditor==0 ){
351
+ fossil_fatal("no text editor - set the VISUAL env variable");
352
+ }
345353
zTFile = fossil_temp_filename();
346354
if( zTFile==0 ) fossil_fatal("cannot find a temporary filename");
347355
db_begin_transaction();
348356
content_rcvid_init("#!fossil unversioned edit");
349357
if( unversioned_content(zUVFile, &content) ){
@@ -386,26 +394,40 @@
386394
fossil_print("%s\n", unversioned_content_hash(debugFlag));
387395
}else if( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){
388396
Stmt q;
389397
int allFlag = find_option("all","a",0)!=0;
390398
int longFlag = find_option("l",0,0)!=0 || (nCmd>1 && zCmd[1]=='i');
399
+ char *zPattern = sqlite3_mprintf("true");
400
+ const char *zGlob;
401
+ zGlob = find_option("glob",0,1);
402
+ if( zGlob ){
403
+ sqlite3_free(zPattern);
404
+ zPattern = sqlite3_mprintf("(name GLOB %Q)", zGlob);
405
+ }
406
+ zGlob = find_option("like",0,1);
407
+ if( zGlob ){
408
+ sqlite3_free(zPattern);
409
+ zPattern = sqlite3_mprintf("(name LIKE %Q)", zGlob);
410
+ }
391411
verify_all_options();
392412
if( !longFlag ){
393413
if( allFlag ){
394
- db_prepare(&q, "SELECT name FROM unversioned ORDER BY name");
414
+ db_prepare(&q, "SELECT name FROM unversioned WHERE %s ORDER BY name",
415
+ zPattern/*safe-for-%s*/);
395416
}else{
396
- db_prepare(&q, "SELECT name FROM unversioned WHERE hash IS NOT NULL"
397
- " ORDER BY name");
417
+ db_prepare(&q, "SELECT name FROM unversioned"
418
+ " WHERE %s AND hash IS NOT NULL"
419
+ " ORDER BY name", zPattern/*safe-for-%s*/);
398420
}
399421
while( db_step(&q)==SQLITE_ROW ){
400422
fossil_print("%s\n", db_column_text(&q,0));
401423
}
402424
}else{
403425
db_prepare(&q,
404426
"SELECT hash, datetime(mtime,'unixepoch'), sz, length(content), name"
405
- " FROM unversioned"
406
- " ORDER BY name;"
427
+ " FROM unversioned WHERE %s"
428
+ " ORDER BY name;", zPattern/*safe-for-%s*/
407429
);
408430
while( db_step(&q)==SQLITE_ROW ){
409431
const char *zHash = db_column_text(&q, 0);
410432
const char *zNoContent = "";
411433
if( zHash==0 ){
@@ -423,20 +445,37 @@
423445
zNoContent
424446
);
425447
}
426448
}
427449
db_finalize(&q);
450
+ sqlite3_free(zPattern);
428451
}else if( memcmp(zCmd, "revert", nCmd)==0 ){
429
- unsigned syncFlags = unversioned_sync_flags(SYNC_UNVERSIONED|SYNC_UV_REVERT);
452
+ unsigned syncFlags =
453
+ unversioned_sync_flags(SYNC_UNVERSIONED|SYNC_UV_REVERT);
430454
g.argv[1] = "sync";
431455
g.argv[2] = "--uv-noop";
432456
sync_unversioned(syncFlags);
433457
}else if( memcmp(zCmd, "remove", nCmd)==0 || memcmp(zCmd, "rm", nCmd)==0
434458
|| memcmp(zCmd, "delete", nCmd)==0 ){
435459
int i;
436
- verify_all_options();
460
+ const char *zGlob;
437461
db_begin_transaction();
462
+ while( (zGlob = find_option("glob",0,1))!=0 ){
463
+ db_multi_exec(
464
+ "UPDATE unversioned"
465
+ " SET hash=NULL, content=NULL, mtime=%lld, sz=0 WHERE name GLOB %Q",
466
+ mtime, zGlob
467
+ );
468
+ }
469
+ while( (zGlob = find_option("like",0,1))!=0 ){
470
+ db_multi_exec(
471
+ "UPDATE unversioned"
472
+ " SET hash=NULL, content=NULL, mtime=%lld, sz=0 WHERE name LIKE %Q",
473
+ mtime, zGlob
474
+ );
475
+ }
476
+ verify_all_options();
438477
for(i=3; i<g.argc; i++){
439478
db_multi_exec(
440479
"UPDATE unversioned"
441480
" SET hash=NULL, content=NULL, mtime=%lld, sz=0 WHERE name=%Q",
442481
mtime, g.argv[i]
@@ -485,10 +524,11 @@
485524
int showDel = 0;
486525
char zSzName[100];
487526
488527
login_check_credentials();
489528
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
529
+ etag_check(ETAG_DATA,0);
490530
style_header("Unversioned Files");
491531
if( !db_table_exists("repository","unversioned") ){
492532
@ No unversioned files on this server
493533
style_footer();
494534
return;
@@ -597,10 +637,11 @@
597637
Blob json;
598638
599639
login_check_credentials();
600640
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
601641
cgi_set_content_type("text/json");
642
+ etag_check(ETAG_DATA,0);
602643
if( !db_table_exists("repository","unversioned") ){
603644
blob_init(&json, "[]", -1);
604645
cgi_set_content(&json);
605646
return;
606647
}
607648
--- src/unversioned.c
+++ src/unversioned.c
@@ -237,11 +237,14 @@
237 ** edit FILE Bring up FILE in a text editor for modification.
238 **
239 ** export FILE OUTPUT Write the content of FILE into OUTPUT on disk
240 **
241 ** list | ls Show all unversioned files held in the local
242 ** repository.
 
 
 
243 **
244 ** revert ?URL? Restore the state of all unversioned files in the
245 ** local repository to match the remote repository
246 ** URL.
247 **
@@ -250,11 +253,14 @@
250 ** -n|--dryrun Show what would have happened
251 **
252 ** remove|rm|delete FILE ...
253 ** Remove unversioned files from the local repository.
254 ** Changes are not pushed to other repositories until
255 ** the next sync.
 
 
 
256 **
257 ** sync ?URL? Synchronize the state of all unversioned files with
258 ** the remote repository URL. The most recent version
259 ** of each file is propagated to all repositories and
260 ** all prior versions are permanently forgotten.
@@ -339,11 +345,13 @@
339
340 verify_all_options();
341 if( g.argc!=4) usage("edit UVFILE");
342 zUVFile = g.argv[3];
343 zEditor = fossil_text_editor();
344 if( zEditor==0 ) fossil_fatal("no text editor - set the VISUAL env variable");
 
 
345 zTFile = fossil_temp_filename();
346 if( zTFile==0 ) fossil_fatal("cannot find a temporary filename");
347 db_begin_transaction();
348 content_rcvid_init("#!fossil unversioned edit");
349 if( unversioned_content(zUVFile, &content) ){
@@ -386,26 +394,40 @@
386 fossil_print("%s\n", unversioned_content_hash(debugFlag));
387 }else if( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){
388 Stmt q;
389 int allFlag = find_option("all","a",0)!=0;
390 int longFlag = find_option("l",0,0)!=0 || (nCmd>1 && zCmd[1]=='i');
 
 
 
 
 
 
 
 
 
 
 
 
391 verify_all_options();
392 if( !longFlag ){
393 if( allFlag ){
394 db_prepare(&q, "SELECT name FROM unversioned ORDER BY name");
 
395 }else{
396 db_prepare(&q, "SELECT name FROM unversioned WHERE hash IS NOT NULL"
397 " ORDER BY name");
 
398 }
399 while( db_step(&q)==SQLITE_ROW ){
400 fossil_print("%s\n", db_column_text(&q,0));
401 }
402 }else{
403 db_prepare(&q,
404 "SELECT hash, datetime(mtime,'unixepoch'), sz, length(content), name"
405 " FROM unversioned"
406 " ORDER BY name;"
407 );
408 while( db_step(&q)==SQLITE_ROW ){
409 const char *zHash = db_column_text(&q, 0);
410 const char *zNoContent = "";
411 if( zHash==0 ){
@@ -423,20 +445,37 @@
423 zNoContent
424 );
425 }
426 }
427 db_finalize(&q);
 
428 }else if( memcmp(zCmd, "revert", nCmd)==0 ){
429 unsigned syncFlags = unversioned_sync_flags(SYNC_UNVERSIONED|SYNC_UV_REVERT);
 
430 g.argv[1] = "sync";
431 g.argv[2] = "--uv-noop";
432 sync_unversioned(syncFlags);
433 }else if( memcmp(zCmd, "remove", nCmd)==0 || memcmp(zCmd, "rm", nCmd)==0
434 || memcmp(zCmd, "delete", nCmd)==0 ){
435 int i;
436 verify_all_options();
437 db_begin_transaction();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
438 for(i=3; i<g.argc; i++){
439 db_multi_exec(
440 "UPDATE unversioned"
441 " SET hash=NULL, content=NULL, mtime=%lld, sz=0 WHERE name=%Q",
442 mtime, g.argv[i]
@@ -485,10 +524,11 @@
485 int showDel = 0;
486 char zSzName[100];
487
488 login_check_credentials();
489 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
 
490 style_header("Unversioned Files");
491 if( !db_table_exists("repository","unversioned") ){
492 @ No unversioned files on this server
493 style_footer();
494 return;
@@ -597,10 +637,11 @@
597 Blob json;
598
599 login_check_credentials();
600 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
601 cgi_set_content_type("text/json");
 
602 if( !db_table_exists("repository","unversioned") ){
603 blob_init(&json, "[]", -1);
604 cgi_set_content(&json);
605 return;
606 }
607
--- src/unversioned.c
+++ src/unversioned.c
@@ -237,11 +237,14 @@
237 ** edit FILE Bring up FILE in a text editor for modification.
238 **
239 ** export FILE OUTPUT Write the content of FILE into OUTPUT on disk
240 **
241 ** list | ls Show all unversioned files held in the local
242 ** repository. Options:
243 **
244 ** --glob PATTERN Show only files that match
245 ** --like PATTERN Show only files that match
246 **
247 ** revert ?URL? Restore the state of all unversioned files in the
248 ** local repository to match the remote repository
249 ** URL.
250 **
@@ -250,11 +253,14 @@
253 ** -n|--dryrun Show what would have happened
254 **
255 ** remove|rm|delete FILE ...
256 ** Remove unversioned files from the local repository.
257 ** Changes are not pushed to other repositories until
258 ** the next sync. Options:
259 **
260 ** --glob PATTERN Remove files that match
261 ** --like PATTERN Remove files that match
262 **
263 ** sync ?URL? Synchronize the state of all unversioned files with
264 ** the remote repository URL. The most recent version
265 ** of each file is propagated to all repositories and
266 ** all prior versions are permanently forgotten.
@@ -339,11 +345,13 @@
345
346 verify_all_options();
347 if( g.argc!=4) usage("edit UVFILE");
348 zUVFile = g.argv[3];
349 zEditor = fossil_text_editor();
350 if( zEditor==0 ){
351 fossil_fatal("no text editor - set the VISUAL env variable");
352 }
353 zTFile = fossil_temp_filename();
354 if( zTFile==0 ) fossil_fatal("cannot find a temporary filename");
355 db_begin_transaction();
356 content_rcvid_init("#!fossil unversioned edit");
357 if( unversioned_content(zUVFile, &content) ){
@@ -386,26 +394,40 @@
394 fossil_print("%s\n", unversioned_content_hash(debugFlag));
395 }else if( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){
396 Stmt q;
397 int allFlag = find_option("all","a",0)!=0;
398 int longFlag = find_option("l",0,0)!=0 || (nCmd>1 && zCmd[1]=='i');
399 char *zPattern = sqlite3_mprintf("true");
400 const char *zGlob;
401 zGlob = find_option("glob",0,1);
402 if( zGlob ){
403 sqlite3_free(zPattern);
404 zPattern = sqlite3_mprintf("(name GLOB %Q)", zGlob);
405 }
406 zGlob = find_option("like",0,1);
407 if( zGlob ){
408 sqlite3_free(zPattern);
409 zPattern = sqlite3_mprintf("(name LIKE %Q)", zGlob);
410 }
411 verify_all_options();
412 if( !longFlag ){
413 if( allFlag ){
414 db_prepare(&q, "SELECT name FROM unversioned WHERE %s ORDER BY name",
415 zPattern/*safe-for-%s*/);
416 }else{
417 db_prepare(&q, "SELECT name FROM unversioned"
418 " WHERE %s AND hash IS NOT NULL"
419 " ORDER BY name", zPattern/*safe-for-%s*/);
420 }
421 while( db_step(&q)==SQLITE_ROW ){
422 fossil_print("%s\n", db_column_text(&q,0));
423 }
424 }else{
425 db_prepare(&q,
426 "SELECT hash, datetime(mtime,'unixepoch'), sz, length(content), name"
427 " FROM unversioned WHERE %s"
428 " ORDER BY name;", zPattern/*safe-for-%s*/
429 );
430 while( db_step(&q)==SQLITE_ROW ){
431 const char *zHash = db_column_text(&q, 0);
432 const char *zNoContent = "";
433 if( zHash==0 ){
@@ -423,20 +445,37 @@
445 zNoContent
446 );
447 }
448 }
449 db_finalize(&q);
450 sqlite3_free(zPattern);
451 }else if( memcmp(zCmd, "revert", nCmd)==0 ){
452 unsigned syncFlags =
453 unversioned_sync_flags(SYNC_UNVERSIONED|SYNC_UV_REVERT);
454 g.argv[1] = "sync";
455 g.argv[2] = "--uv-noop";
456 sync_unversioned(syncFlags);
457 }else if( memcmp(zCmd, "remove", nCmd)==0 || memcmp(zCmd, "rm", nCmd)==0
458 || memcmp(zCmd, "delete", nCmd)==0 ){
459 int i;
460 const char *zGlob;
461 db_begin_transaction();
462 while( (zGlob = find_option("glob",0,1))!=0 ){
463 db_multi_exec(
464 "UPDATE unversioned"
465 " SET hash=NULL, content=NULL, mtime=%lld, sz=0 WHERE name GLOB %Q",
466 mtime, zGlob
467 );
468 }
469 while( (zGlob = find_option("like",0,1))!=0 ){
470 db_multi_exec(
471 "UPDATE unversioned"
472 " SET hash=NULL, content=NULL, mtime=%lld, sz=0 WHERE name LIKE %Q",
473 mtime, zGlob
474 );
475 }
476 verify_all_options();
477 for(i=3; i<g.argc; i++){
478 db_multi_exec(
479 "UPDATE unversioned"
480 " SET hash=NULL, content=NULL, mtime=%lld, sz=0 WHERE name=%Q",
481 mtime, g.argv[i]
@@ -485,10 +524,11 @@
524 int showDel = 0;
525 char zSzName[100];
526
527 login_check_credentials();
528 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
529 etag_check(ETAG_DATA,0);
530 style_header("Unversioned Files");
531 if( !db_table_exists("repository","unversioned") ){
532 @ No unversioned files on this server
533 style_footer();
534 return;
@@ -597,10 +637,11 @@
637 Blob json;
638
639 login_check_credentials();
640 if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
641 cgi_set_content_type("text/json");
642 etag_check(ETAG_DATA,0);
643 if( !db_table_exists("repository","unversioned") ){
644 blob_init(&json, "[]", -1);
645 cgi_set_content(&json);
646 return;
647 }
648
+18 -14
--- src/url.c
+++ src/url.c
@@ -66,20 +66,10 @@
6666
int proxyOrigPort; /* Tunneled port number for https through proxy */
6767
};
6868
#endif /* INTERFACE */
6969
7070
71
-/*
72
-** Convert a string to lower-case.
73
-*/
74
-static void url_tolower(char *z){
75
- while( *z ){
76
- *z = fossil_tolower(*z);
77
- z++;
78
- }
79
-}
80
-
8171
/*
8272
** Parse the given URL. Populate members of the provided UrlData structure
8373
** as follows:
8474
**
8575
** isFile True if FILE:
@@ -177,11 +167,11 @@
177167
pUrlData->name++;
178168
pUrlData->name[n-2] = 0;
179169
}
180170
zLogin = mprintf("");
181171
}
182
- url_tolower(pUrlData->name);
172
+ fossil_strtolwr(pUrlData->name);
183173
if( c==':' ){
184174
pUrlData->port = 0;
185175
i++;
186176
while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){
187177
pUrlData->port = pUrlData->port*10 + c - '0';
@@ -370,18 +360,32 @@
370360
/*
371361
** Extract any proxy options from the command-line.
372362
**
373363
** --proxy URL|off
374364
**
375
-** This also happens to be a convenient function to use to look for
376
-** the --nosync option that will temporarily disable the "autosync"
377
-** feature.
365
+** The original purpose of this routine is the above. But this
366
+** also happens to be a convenient place to look for other
367
+** network-related options:
368
+**
369
+** --nosync Temporarily disable "autosync"
370
+**
371
+** --ipv4 Disallow IPv6. Use only IPv4.
372
+**
373
+** --accept-any-cert Disable server SSL cert validation. Accept
374
+** any SSL cert that the server provides.
375
+** WARNING: this option opens you up to
376
+** forged-DNS and man-in-the-middle attacks!
378377
*/
379378
void url_proxy_options(void){
380379
zProxyOpt = find_option("proxy", 0, 1);
381380
if( find_option("nosync",0,0) ) g.fNoSync = 1;
382381
if( find_option("ipv4",0,0) ) g.fIPv4 = 1;
382
+#ifdef FOSSIL_ENABLE_SSL
383
+ if( find_option("accept-any-cert",0,0) ){
384
+ ssl_disable_cert_verification();
385
+ }
386
+#endif /* FOSSIL_ENABLE_SSL */
383387
}
384388
385389
/*
386390
** If the "proxy" setting is defined, then change the URL settings
387391
** (initialized by a prior call to url_parse()) so that the HTTP
388392
--- src/url.c
+++ src/url.c
@@ -66,20 +66,10 @@
66 int proxyOrigPort; /* Tunneled port number for https through proxy */
67 };
68 #endif /* INTERFACE */
69
70
71 /*
72 ** Convert a string to lower-case.
73 */
74 static void url_tolower(char *z){
75 while( *z ){
76 *z = fossil_tolower(*z);
77 z++;
78 }
79 }
80
81 /*
82 ** Parse the given URL. Populate members of the provided UrlData structure
83 ** as follows:
84 **
85 ** isFile True if FILE:
@@ -177,11 +167,11 @@
177 pUrlData->name++;
178 pUrlData->name[n-2] = 0;
179 }
180 zLogin = mprintf("");
181 }
182 url_tolower(pUrlData->name);
183 if( c==':' ){
184 pUrlData->port = 0;
185 i++;
186 while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){
187 pUrlData->port = pUrlData->port*10 + c - '0';
@@ -370,18 +360,32 @@
370 /*
371 ** Extract any proxy options from the command-line.
372 **
373 ** --proxy URL|off
374 **
375 ** This also happens to be a convenient function to use to look for
376 ** the --nosync option that will temporarily disable the "autosync"
377 ** feature.
 
 
 
 
 
 
 
 
 
378 */
379 void url_proxy_options(void){
380 zProxyOpt = find_option("proxy", 0, 1);
381 if( find_option("nosync",0,0) ) g.fNoSync = 1;
382 if( find_option("ipv4",0,0) ) g.fIPv4 = 1;
 
 
 
 
 
383 }
384
385 /*
386 ** If the "proxy" setting is defined, then change the URL settings
387 ** (initialized by a prior call to url_parse()) so that the HTTP
388
--- src/url.c
+++ src/url.c
@@ -66,20 +66,10 @@
66 int proxyOrigPort; /* Tunneled port number for https through proxy */
67 };
68 #endif /* INTERFACE */
69
70
 
 
 
 
 
 
 
 
 
 
71 /*
72 ** Parse the given URL. Populate members of the provided UrlData structure
73 ** as follows:
74 **
75 ** isFile True if FILE:
@@ -177,11 +167,11 @@
167 pUrlData->name++;
168 pUrlData->name[n-2] = 0;
169 }
170 zLogin = mprintf("");
171 }
172 fossil_strtolwr(pUrlData->name);
173 if( c==':' ){
174 pUrlData->port = 0;
175 i++;
176 while( (c = zUrl[i])!=0 && fossil_isdigit(c) ){
177 pUrlData->port = pUrlData->port*10 + c - '0';
@@ -370,18 +360,32 @@
360 /*
361 ** Extract any proxy options from the command-line.
362 **
363 ** --proxy URL|off
364 **
365 ** The original purpose of this routine is the above. But this
366 ** also happens to be a convenient place to look for other
367 ** network-related options:
368 **
369 ** --nosync Temporarily disable "autosync"
370 **
371 ** --ipv4 Disallow IPv6. Use only IPv4.
372 **
373 ** --accept-any-cert Disable server SSL cert validation. Accept
374 ** any SSL cert that the server provides.
375 ** WARNING: this option opens you up to
376 ** forged-DNS and man-in-the-middle attacks!
377 */
378 void url_proxy_options(void){
379 zProxyOpt = find_option("proxy", 0, 1);
380 if( find_option("nosync",0,0) ) g.fNoSync = 1;
381 if( find_option("ipv4",0,0) ) g.fIPv4 = 1;
382 #ifdef FOSSIL_ENABLE_SSL
383 if( find_option("accept-any-cert",0,0) ){
384 ssl_disable_cert_verification();
385 }
386 #endif /* FOSSIL_ENABLE_SSL */
387 }
388
389 /*
390 ** If the "proxy" setting is defined, then change the URL settings
391 ** (initialized by a prior call to url_parse()) so that the HTTP
392
+15
--- src/util.c
+++ src/util.c
@@ -140,10 +140,25 @@
140140
}
141141
#else
142142
fossil_free(p);
143143
#endif
144144
}
145
+
146
+/*
147
+** Translate every upper-case character in the input string into
148
+** its equivalent lower-case.
149
+*/
150
+char *fossil_strtolwr(char *zIn){
151
+ char *zStart = zIn;
152
+ if( zIn ){
153
+ while( *zIn ){
154
+ *zIn = fossil_tolower(*zIn);
155
+ zIn++;
156
+ }
157
+ }
158
+ return zStart;
159
+}
145160
146161
/*
147162
** This function implements a cross-platform "system()" interface.
148163
*/
149164
int fossil_system(const char *zOrigCmd){
150165
--- src/util.c
+++ src/util.c
@@ -140,10 +140,25 @@
140 }
141 #else
142 fossil_free(p);
143 #endif
144 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
146 /*
147 ** This function implements a cross-platform "system()" interface.
148 */
149 int fossil_system(const char *zOrigCmd){
150
--- src/util.c
+++ src/util.c
@@ -140,10 +140,25 @@
140 }
141 #else
142 fossil_free(p);
143 #endif
144 }
145
146 /*
147 ** Translate every upper-case character in the input string into
148 ** its equivalent lower-case.
149 */
150 char *fossil_strtolwr(char *zIn){
151 char *zStart = zIn;
152 if( zIn ){
153 while( *zIn ){
154 *zIn = fossil_tolower(*zIn);
155 zIn++;
156 }
157 }
158 return zStart;
159 }
160
161 /*
162 ** This function implements a cross-platform "system()" interface.
163 */
164 int fossil_system(const char *zOrigCmd){
165
+46 -23
--- src/wiki.c
+++ src/wiki.c
@@ -365,20 +365,22 @@
365365
search_screen(SRCH_WIKI, 0);
366366
style_footer();
367367
}
368368
369369
/* Return values from wiki_page_type() */
370
-#define WIKITYPE_UNKNOWN (-1)
371
-#define WIKITYPE_NORMAL 0
372
-#define WIKITYPE_BRANCH 1
373
-#define WIKITYPE_CHECKIN 2
374
-#define WIKITYPE_TAG 3
370
+#if INTERFACE
371
+# define WIKITYPE_UNKNOWN (-1)
372
+# define WIKITYPE_NORMAL 0
373
+# define WIKITYPE_BRANCH 1
374
+# define WIKITYPE_CHECKIN 2
375
+# define WIKITYPE_TAG 3
376
+#endif
375377
376378
/*
377379
** Figure out what type of wiki page we are dealing with.
378380
*/
379
-static int wiki_page_type(const char *zPageName){
381
+int wiki_page_type(const char *zPageName){
380382
if( db_get_boolean("wiki-about",1)==0 ){
381383
return WIKITYPE_NORMAL;
382384
}else
383385
if( sqlite3_strglob("checkin/*", zPageName)==0
384386
&& db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8)
@@ -394,40 +396,58 @@
394396
return WIKITYPE_NORMAL;
395397
}
396398
397399
/*
398400
** Add an appropriate style_header() for either the /wiki or /wikiedit page
399
-** for zPageName.
401
+** for zPageName. zExtra is an empty string for /wiki but has the text
402
+** "Edit: " for /wikiedit.
403
+**
404
+** If the page is /wiki and the page is one of the special times (check-in,
405
+** branch, or tag) and the "p" query parameter is omitted, then do a
406
+** redirect to the display of the check-in, branch, or tag rather than
407
+** continuing to the plain wiki display.
400408
*/
401409
static int wiki_page_header(
402
- int eType, /* Page type. -1 for unknown */
410
+ int eType, /* Page type. Might be WIKITYPE_UNKNOWN */
403411
const char *zPageName, /* Name of the page */
404412
const char *zExtra /* Extra prefix text on the page header */
405413
){
406
- if( eType<0 ) eType = wiki_page_type(zPageName);
414
+ if( eType==WIKITYPE_UNKNOWN ) eType = wiki_page_type(zPageName);
407415
switch( eType ){
408416
case WIKITYPE_NORMAL: {
409417
style_header("%s%s", zExtra, zPageName);
410418
break;
411419
}
412420
case WIKITYPE_CHECKIN: {
413421
zPageName += 8;
414
- style_header("Notes About Checkin %S", zPageName);
415
- style_submenu_element("Checkin Timeline","%R/timeline?f=%s", zPageName);
416
- style_submenu_element("Checkin Info","%R/info/%s", zPageName);
422
+ if( zExtra[0]==0 && !P("p") ){
423
+ cgi_redirectf("%R/info/%s",zPageName);
424
+ }else{
425
+ style_header("Notes About Checkin %S", zPageName);
426
+ style_submenu_element("Checkin Timeline","%R/timeline?f=%s", zPageName);
427
+ style_submenu_element("Checkin Info","%R/info/%s", zPageName);
428
+ }
417429
break;
418430
}
419431
case WIKITYPE_BRANCH: {
420432
zPageName += 7;
421
- style_header("Notes About Branch %h", zPageName);
422
- style_submenu_element("Branch Timeline","%R/timeline?r=%t", zPageName);
433
+ if( zExtra[0]==0 && !P("p") ){
434
+ cgi_redirectf("%R/timeline?r=%t", zPageName);
435
+ }else{
436
+ style_header("Notes About Branch %h", zPageName);
437
+ style_submenu_element("Branch Timeline","%R/timeline?r=%t", zPageName);
438
+ }
423439
break;
424440
}
425441
case WIKITYPE_TAG: {
426442
zPageName += 4;
427
- style_header("Notes About Tag %h", zPageName);
428
- style_submenu_element("Tag Timeline","%R/timeline?t=%t",zPageName);
443
+ if( zExtra[0]==0 && !P("p") ){
444
+ cgi_redirectf("%R/timeline?t=%t",zPageName);
445
+ }else{
446
+ style_header("Notes About Tag %h", zPageName);
447
+ style_submenu_element("Tag Timeline","%R/timeline?t=%t",zPageName);
448
+ }
429449
break;
430450
}
431451
}
432452
return eType;
433453
}
@@ -459,11 +479,14 @@
459479
**
460480
** Query parameters:
461481
**
462482
** name=NAME Name of the wiki page to display. Required.
463483
** nsm Omit the submenu if present. (Mnemonic: No SubMenu)
464
-**
484
+** p Always show just the wiki page. For special
485
+** pages for check-ins, branches, or tags, there will
486
+** be a redirect to the associated /info page unless
487
+** this query parameter is present.
465488
*/
466489
void wiki_page(void){
467490
char *zTag;
468491
int rid = 0;
469492
int isSandbox;
@@ -1724,17 +1747,17 @@
17241747
}
17251748
17261749
/*
17271750
** Add an "Wiki" button in a submenu that links to the read-wiki page.
17281751
*/
1729
-static void wiki_submenu_to_read_wiki(
1752
+static void wiki_submenu_to_edit_wiki(
17301753
const char *zPrefix, /* "branch", "tag", or "checkin" */
17311754
const char *zName, /* Name of the object */
17321755
unsigned int mFlags /* Zero or more WIKIASSOC_* flags */
17331756
){
17341757
if( g.perm.RdWiki && (mFlags & WIKIASSOC_MENU_READ)!=0 ){
1735
- style_submenu_element("Wiki", "%R/wiki?name=%s/%t", zPrefix, zName);
1758
+ style_submenu_element("Wiki", "%R/wikiedit?name=%s/%t", zPrefix, zName);
17361759
}
17371760
}
17381761
17391762
/*
17401763
** Check to see if there exists a wiki page with a name zPrefix/zName.
@@ -1770,24 +1793,24 @@
17701793
Blob title = BLOB_INITIALIZER;
17711794
Blob markdown;
17721795
blob_init(&markdown, pWiki->zWiki, -1);
17731796
markdown_to_html(&markdown, &title, &tail);
17741797
if( blob_size(&title) ){
1775
- @ <div class="section">%h(blob_str(&title))</div>
1798
+ @ <div class="section accordion">%h(blob_str(&title))</div>
17761799
}else{
17771800
wiki_section_label(zPrefix, zName, mFlags);
17781801
}
1779
- wiki_submenu_to_read_wiki(zPrefix, zName, mFlags);
1802
+ wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags);
17801803
@ <div class="accordion_panel">
17811804
convert_href_and_output(&tail);
17821805
@ </div>
17831806
blob_reset(&tail);
17841807
blob_reset(&title);
17851808
blob_reset(&markdown);
17861809
}else if( fossil_strcmp(pWiki->zMimetype, "text/plain")==0 ){
17871810
wiki_section_label(zPrefix, zName, mFlags);
1788
- wiki_submenu_to_read_wiki(zPrefix, zName, mFlags);
1811
+ wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags);
17891812
@ <div class="accordion_panel"><pre>
17901813
@ %h(pWiki->zWiki)
17911814
@ </pre></div>
17921815
}else{
17931816
Blob tail = BLOB_INITIALIZER;
@@ -1800,11 +1823,11 @@
18001823
pBody = &tail;
18011824
}else{
18021825
wiki_section_label(zPrefix, zName, mFlags);
18031826
pBody = &wiki;
18041827
}
1805
- wiki_submenu_to_read_wiki(zPrefix, zName, mFlags);
1828
+ wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags);
18061829
@ <div class="accordion_panel"><div class="wiki">
18071830
wiki_convert(pBody, 0, WIKI_BUTTONS);
18081831
@ </div></div>
18091832
blob_reset(&tail);
18101833
blob_reset(&title);
18111834
--- src/wiki.c
+++ src/wiki.c
@@ -365,20 +365,22 @@
365 search_screen(SRCH_WIKI, 0);
366 style_footer();
367 }
368
369 /* Return values from wiki_page_type() */
370 #define WIKITYPE_UNKNOWN (-1)
371 #define WIKITYPE_NORMAL 0
372 #define WIKITYPE_BRANCH 1
373 #define WIKITYPE_CHECKIN 2
374 #define WIKITYPE_TAG 3
 
 
375
376 /*
377 ** Figure out what type of wiki page we are dealing with.
378 */
379 static int wiki_page_type(const char *zPageName){
380 if( db_get_boolean("wiki-about",1)==0 ){
381 return WIKITYPE_NORMAL;
382 }else
383 if( sqlite3_strglob("checkin/*", zPageName)==0
384 && db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8)
@@ -394,40 +396,58 @@
394 return WIKITYPE_NORMAL;
395 }
396
397 /*
398 ** Add an appropriate style_header() for either the /wiki or /wikiedit page
399 ** for zPageName.
 
 
 
 
 
 
400 */
401 static int wiki_page_header(
402 int eType, /* Page type. -1 for unknown */
403 const char *zPageName, /* Name of the page */
404 const char *zExtra /* Extra prefix text on the page header */
405 ){
406 if( eType<0 ) eType = wiki_page_type(zPageName);
407 switch( eType ){
408 case WIKITYPE_NORMAL: {
409 style_header("%s%s", zExtra, zPageName);
410 break;
411 }
412 case WIKITYPE_CHECKIN: {
413 zPageName += 8;
414 style_header("Notes About Checkin %S", zPageName);
415 style_submenu_element("Checkin Timeline","%R/timeline?f=%s", zPageName);
416 style_submenu_element("Checkin Info","%R/info/%s", zPageName);
 
 
 
 
417 break;
418 }
419 case WIKITYPE_BRANCH: {
420 zPageName += 7;
421 style_header("Notes About Branch %h", zPageName);
422 style_submenu_element("Branch Timeline","%R/timeline?r=%t", zPageName);
 
 
 
 
423 break;
424 }
425 case WIKITYPE_TAG: {
426 zPageName += 4;
427 style_header("Notes About Tag %h", zPageName);
428 style_submenu_element("Tag Timeline","%R/timeline?t=%t",zPageName);
 
 
 
 
429 break;
430 }
431 }
432 return eType;
433 }
@@ -459,11 +479,14 @@
459 **
460 ** Query parameters:
461 **
462 ** name=NAME Name of the wiki page to display. Required.
463 ** nsm Omit the submenu if present. (Mnemonic: No SubMenu)
464 **
 
 
 
465 */
466 void wiki_page(void){
467 char *zTag;
468 int rid = 0;
469 int isSandbox;
@@ -1724,17 +1747,17 @@
1724 }
1725
1726 /*
1727 ** Add an "Wiki" button in a submenu that links to the read-wiki page.
1728 */
1729 static void wiki_submenu_to_read_wiki(
1730 const char *zPrefix, /* "branch", "tag", or "checkin" */
1731 const char *zName, /* Name of the object */
1732 unsigned int mFlags /* Zero or more WIKIASSOC_* flags */
1733 ){
1734 if( g.perm.RdWiki && (mFlags & WIKIASSOC_MENU_READ)!=0 ){
1735 style_submenu_element("Wiki", "%R/wiki?name=%s/%t", zPrefix, zName);
1736 }
1737 }
1738
1739 /*
1740 ** Check to see if there exists a wiki page with a name zPrefix/zName.
@@ -1770,24 +1793,24 @@
1770 Blob title = BLOB_INITIALIZER;
1771 Blob markdown;
1772 blob_init(&markdown, pWiki->zWiki, -1);
1773 markdown_to_html(&markdown, &title, &tail);
1774 if( blob_size(&title) ){
1775 @ <div class="section">%h(blob_str(&title))</div>
1776 }else{
1777 wiki_section_label(zPrefix, zName, mFlags);
1778 }
1779 wiki_submenu_to_read_wiki(zPrefix, zName, mFlags);
1780 @ <div class="accordion_panel">
1781 convert_href_and_output(&tail);
1782 @ </div>
1783 blob_reset(&tail);
1784 blob_reset(&title);
1785 blob_reset(&markdown);
1786 }else if( fossil_strcmp(pWiki->zMimetype, "text/plain")==0 ){
1787 wiki_section_label(zPrefix, zName, mFlags);
1788 wiki_submenu_to_read_wiki(zPrefix, zName, mFlags);
1789 @ <div class="accordion_panel"><pre>
1790 @ %h(pWiki->zWiki)
1791 @ </pre></div>
1792 }else{
1793 Blob tail = BLOB_INITIALIZER;
@@ -1800,11 +1823,11 @@
1800 pBody = &tail;
1801 }else{
1802 wiki_section_label(zPrefix, zName, mFlags);
1803 pBody = &wiki;
1804 }
1805 wiki_submenu_to_read_wiki(zPrefix, zName, mFlags);
1806 @ <div class="accordion_panel"><div class="wiki">
1807 wiki_convert(pBody, 0, WIKI_BUTTONS);
1808 @ </div></div>
1809 blob_reset(&tail);
1810 blob_reset(&title);
1811
--- src/wiki.c
+++ src/wiki.c
@@ -365,20 +365,22 @@
365 search_screen(SRCH_WIKI, 0);
366 style_footer();
367 }
368
369 /* Return values from wiki_page_type() */
370 #if INTERFACE
371 # define WIKITYPE_UNKNOWN (-1)
372 # define WIKITYPE_NORMAL 0
373 # define WIKITYPE_BRANCH 1
374 # define WIKITYPE_CHECKIN 2
375 # define WIKITYPE_TAG 3
376 #endif
377
378 /*
379 ** Figure out what type of wiki page we are dealing with.
380 */
381 int wiki_page_type(const char *zPageName){
382 if( db_get_boolean("wiki-about",1)==0 ){
383 return WIKITYPE_NORMAL;
384 }else
385 if( sqlite3_strglob("checkin/*", zPageName)==0
386 && db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8)
@@ -394,40 +396,58 @@
396 return WIKITYPE_NORMAL;
397 }
398
399 /*
400 ** Add an appropriate style_header() for either the /wiki or /wikiedit page
401 ** for zPageName. zExtra is an empty string for /wiki but has the text
402 ** "Edit: " for /wikiedit.
403 **
404 ** If the page is /wiki and the page is one of the special times (check-in,
405 ** branch, or tag) and the "p" query parameter is omitted, then do a
406 ** redirect to the display of the check-in, branch, or tag rather than
407 ** continuing to the plain wiki display.
408 */
409 static int wiki_page_header(
410 int eType, /* Page type. Might be WIKITYPE_UNKNOWN */
411 const char *zPageName, /* Name of the page */
412 const char *zExtra /* Extra prefix text on the page header */
413 ){
414 if( eType==WIKITYPE_UNKNOWN ) eType = wiki_page_type(zPageName);
415 switch( eType ){
416 case WIKITYPE_NORMAL: {
417 style_header("%s%s", zExtra, zPageName);
418 break;
419 }
420 case WIKITYPE_CHECKIN: {
421 zPageName += 8;
422 if( zExtra[0]==0 && !P("p") ){
423 cgi_redirectf("%R/info/%s",zPageName);
424 }else{
425 style_header("Notes About Checkin %S", zPageName);
426 style_submenu_element("Checkin Timeline","%R/timeline?f=%s", zPageName);
427 style_submenu_element("Checkin Info","%R/info/%s", zPageName);
428 }
429 break;
430 }
431 case WIKITYPE_BRANCH: {
432 zPageName += 7;
433 if( zExtra[0]==0 && !P("p") ){
434 cgi_redirectf("%R/timeline?r=%t", zPageName);
435 }else{
436 style_header("Notes About Branch %h", zPageName);
437 style_submenu_element("Branch Timeline","%R/timeline?r=%t", zPageName);
438 }
439 break;
440 }
441 case WIKITYPE_TAG: {
442 zPageName += 4;
443 if( zExtra[0]==0 && !P("p") ){
444 cgi_redirectf("%R/timeline?t=%t",zPageName);
445 }else{
446 style_header("Notes About Tag %h", zPageName);
447 style_submenu_element("Tag Timeline","%R/timeline?t=%t",zPageName);
448 }
449 break;
450 }
451 }
452 return eType;
453 }
@@ -459,11 +479,14 @@
479 **
480 ** Query parameters:
481 **
482 ** name=NAME Name of the wiki page to display. Required.
483 ** nsm Omit the submenu if present. (Mnemonic: No SubMenu)
484 ** p Always show just the wiki page. For special
485 ** pages for check-ins, branches, or tags, there will
486 ** be a redirect to the associated /info page unless
487 ** this query parameter is present.
488 */
489 void wiki_page(void){
490 char *zTag;
491 int rid = 0;
492 int isSandbox;
@@ -1724,17 +1747,17 @@
1747 }
1748
1749 /*
1750 ** Add an "Wiki" button in a submenu that links to the read-wiki page.
1751 */
1752 static void wiki_submenu_to_edit_wiki(
1753 const char *zPrefix, /* "branch", "tag", or "checkin" */
1754 const char *zName, /* Name of the object */
1755 unsigned int mFlags /* Zero or more WIKIASSOC_* flags */
1756 ){
1757 if( g.perm.RdWiki && (mFlags & WIKIASSOC_MENU_READ)!=0 ){
1758 style_submenu_element("Wiki", "%R/wikiedit?name=%s/%t", zPrefix, zName);
1759 }
1760 }
1761
1762 /*
1763 ** Check to see if there exists a wiki page with a name zPrefix/zName.
@@ -1770,24 +1793,24 @@
1793 Blob title = BLOB_INITIALIZER;
1794 Blob markdown;
1795 blob_init(&markdown, pWiki->zWiki, -1);
1796 markdown_to_html(&markdown, &title, &tail);
1797 if( blob_size(&title) ){
1798 @ <div class="section accordion">%h(blob_str(&title))</div>
1799 }else{
1800 wiki_section_label(zPrefix, zName, mFlags);
1801 }
1802 wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags);
1803 @ <div class="accordion_panel">
1804 convert_href_and_output(&tail);
1805 @ </div>
1806 blob_reset(&tail);
1807 blob_reset(&title);
1808 blob_reset(&markdown);
1809 }else if( fossil_strcmp(pWiki->zMimetype, "text/plain")==0 ){
1810 wiki_section_label(zPrefix, zName, mFlags);
1811 wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags);
1812 @ <div class="accordion_panel"><pre>
1813 @ %h(pWiki->zWiki)
1814 @ </pre></div>
1815 }else{
1816 Blob tail = BLOB_INITIALIZER;
@@ -1800,11 +1823,11 @@
1823 pBody = &tail;
1824 }else{
1825 wiki_section_label(zPrefix, zName, mFlags);
1826 pBody = &wiki;
1827 }
1828 wiki_submenu_to_edit_wiki(zPrefix, zName, mFlags);
1829 @ <div class="accordion_panel"><div class="wiki">
1830 wiki_convert(pBody, 0, WIKI_BUTTONS);
1831 @ </div></div>
1832 blob_reset(&tail);
1833 blob_reset(&title);
1834
+3 -21
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -1869,14 +1869,11 @@
18691869
** name. For each such hyperlink found, add an entry to the
18701870
** backlink table.
18711871
*/
18721872
void wiki_extract_links(
18731873
char *z, /* The wiki text from which to extract links */
1874
- int srcid, /* srcid field for new BACKLINK table entries */
1875
- int srctype, /* srctype field for new BACKLINK table entries */
1876
- double mtime, /* mtime field for new BACKLINK table entries */
1877
- int replaceFlag, /* True first delete prior BACKLINK entries */
1874
+ Backlink *pBklnk, /* Backlink extraction context */
18781875
int flags /* wiki parsing flags */
18791876
){
18801877
Renderer renderer;
18811878
int tokenType;
18821879
ParsedMarkup markup;
@@ -1892,14 +1889,10 @@
18921889
if( wikiUsesHtml() ){
18931890
renderer.state |= WIKI_HTMLONLY;
18941891
wikiHtmlOnly = 1;
18951892
}
18961893
inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0;
1897
- if( replaceFlag ){
1898
- db_multi_exec("DELETE FROM backlink WHERE srctype=%d AND srcid=%d",
1899
- srctype, srcid);
1900
- }
19011894
19021895
while( z[0] ){
19031896
if( wikiHtmlOnly ){
19041897
n = nextRawToken(z, &renderer, &tokenType);
19051898
}else{
@@ -1906,27 +1899,16 @@
19061899
n = nextWikiToken(z, &renderer, &tokenType);
19071900
}
19081901
switch( tokenType ){
19091902
case TOKEN_LINK: {
19101903
char *zTarget;
1911
- int i, c;
1912
- char zLink[HNAME_MAX+4];
1904
+ int i;
19131905
19141906
zTarget = &z[1];
19151907
for(i=0; zTarget[i] && zTarget[i]!='|' && zTarget[i]!=']'; i++){}
19161908
while(i>1 && zTarget[i-1]==' '){ i--; }
1917
- c = zTarget[i];
1918
- zTarget[i] = 0;
1919
- if( is_valid_hname(zTarget) ){
1920
- memcpy(zLink, zTarget, i+1);
1921
- canonical16(zLink, i);
1922
- db_multi_exec(
1923
- "REPLACE INTO backlink(target,srctype,srcid,mtime)"
1924
- "VALUES(%Q,%d,%d,%g)", zLink, srctype, srcid, mtime
1925
- );
1926
- }
1927
- zTarget[i] = c;
1909
+ backlink_create(pBklnk, zTarget, i);
19281910
break;
19291911
}
19301912
case TOKEN_MARKUP: {
19311913
const char *zId;
19321914
int iDiv;
19331915
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -1869,14 +1869,11 @@
1869 ** name. For each such hyperlink found, add an entry to the
1870 ** backlink table.
1871 */
1872 void wiki_extract_links(
1873 char *z, /* The wiki text from which to extract links */
1874 int srcid, /* srcid field for new BACKLINK table entries */
1875 int srctype, /* srctype field for new BACKLINK table entries */
1876 double mtime, /* mtime field for new BACKLINK table entries */
1877 int replaceFlag, /* True first delete prior BACKLINK entries */
1878 int flags /* wiki parsing flags */
1879 ){
1880 Renderer renderer;
1881 int tokenType;
1882 ParsedMarkup markup;
@@ -1892,14 +1889,10 @@
1892 if( wikiUsesHtml() ){
1893 renderer.state |= WIKI_HTMLONLY;
1894 wikiHtmlOnly = 1;
1895 }
1896 inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0;
1897 if( replaceFlag ){
1898 db_multi_exec("DELETE FROM backlink WHERE srctype=%d AND srcid=%d",
1899 srctype, srcid);
1900 }
1901
1902 while( z[0] ){
1903 if( wikiHtmlOnly ){
1904 n = nextRawToken(z, &renderer, &tokenType);
1905 }else{
@@ -1906,27 +1899,16 @@
1906 n = nextWikiToken(z, &renderer, &tokenType);
1907 }
1908 switch( tokenType ){
1909 case TOKEN_LINK: {
1910 char *zTarget;
1911 int i, c;
1912 char zLink[HNAME_MAX+4];
1913
1914 zTarget = &z[1];
1915 for(i=0; zTarget[i] && zTarget[i]!='|' && zTarget[i]!=']'; i++){}
1916 while(i>1 && zTarget[i-1]==' '){ i--; }
1917 c = zTarget[i];
1918 zTarget[i] = 0;
1919 if( is_valid_hname(zTarget) ){
1920 memcpy(zLink, zTarget, i+1);
1921 canonical16(zLink, i);
1922 db_multi_exec(
1923 "REPLACE INTO backlink(target,srctype,srcid,mtime)"
1924 "VALUES(%Q,%d,%d,%g)", zLink, srctype, srcid, mtime
1925 );
1926 }
1927 zTarget[i] = c;
1928 break;
1929 }
1930 case TOKEN_MARKUP: {
1931 const char *zId;
1932 int iDiv;
1933
--- src/wikiformat.c
+++ src/wikiformat.c
@@ -1869,14 +1869,11 @@
1869 ** name. For each such hyperlink found, add an entry to the
1870 ** backlink table.
1871 */
1872 void wiki_extract_links(
1873 char *z, /* The wiki text from which to extract links */
1874 Backlink *pBklnk, /* Backlink extraction context */
 
 
 
1875 int flags /* wiki parsing flags */
1876 ){
1877 Renderer renderer;
1878 int tokenType;
1879 ParsedMarkup markup;
@@ -1892,14 +1889,10 @@
1889 if( wikiUsesHtml() ){
1890 renderer.state |= WIKI_HTMLONLY;
1891 wikiHtmlOnly = 1;
1892 }
1893 inlineOnly = (renderer.state & INLINE_MARKUP_ONLY)!=0;
 
 
 
 
1894
1895 while( z[0] ){
1896 if( wikiHtmlOnly ){
1897 n = nextRawToken(z, &renderer, &tokenType);
1898 }else{
@@ -1906,27 +1899,16 @@
1899 n = nextWikiToken(z, &renderer, &tokenType);
1900 }
1901 switch( tokenType ){
1902 case TOKEN_LINK: {
1903 char *zTarget;
1904 int i;
 
1905
1906 zTarget = &z[1];
1907 for(i=0; zTarget[i] && zTarget[i]!='|' && zTarget[i]!=']'; i++){}
1908 while(i>1 && zTarget[i-1]==' '){ i--; }
1909 backlink_create(pBklnk, zTarget, i);
 
 
 
 
 
 
 
 
 
 
1910 break;
1911 }
1912 case TOKEN_MARKUP: {
1913 const char *zId;
1914 int iDiv;
1915
+153 -56
--- src/xfer.c
+++ src/xfer.c
@@ -39,10 +39,11 @@
3939
Blob line; /* The current line of input */
4040
Blob aToken[6]; /* Tokenized version of line */
4141
Blob err; /* Error message text */
4242
int nToken; /* Number of tokens in line */
4343
int nIGotSent; /* Number of "igot" cards sent */
44
+ int nPrivIGot; /* Number of private "igot" cards */
4445
int nGimmeSent; /* Number of gimme cards sent */
4546
int nFileSent; /* Number of files sent */
4647
int nDeltaSent; /* Number of deltas sent */
4748
int nFileRcvd; /* Number of files received */
4849
int nDeltaRcvd; /* Number of deltas received */
@@ -49,11 +50,13 @@
4950
int nDanglingFile; /* Number of dangling deltas received */
5051
int mxSend; /* Stop sending "file" when pOut reaches this size */
5152
int resync; /* Send igot cards for all holdings */
5253
u8 syncPrivate; /* True to enable syncing private content */
5354
u8 nextIsPrivate; /* If true, next "file" received is a private */
54
- u32 clientVersion; /* Version of the client software */
55
+ u32 remoteVersion; /* Version of fossil running on the other side */
56
+ u32 remoteDate; /* Date for specific client software edition */
57
+ u32 remoteTime; /* Time of date correspoding on remoteDate */
5558
time_t maxTime; /* Time when this transfer should be finished */
5659
};
5760
5861
5962
/*
@@ -524,20 +527,32 @@
524527
static void send_file(Xfer *pXfer, int rid, Blob *pUuid, int nativeDelta){
525528
Blob content, uuid;
526529
int size = 0;
527530
int isPriv = content_is_private(rid);
528531
529
- if( pXfer->syncPrivate==0 && isPriv ) return;
532
+ if( isPriv && pXfer->syncPrivate==0 ){
533
+ if( pXfer->remoteDate>=20200413 && pUuid && blob_size(pUuid)>0 ){
534
+ /* If the artifact is private and we are not doing a private sync,
535
+ ** at least tell the other side that the artifact exists and is
536
+ ** known to be private. But only do this for newer clients since
537
+ ** older ones will throw an error if they get a private igot card
538
+ ** and private syncing is disallowed */
539
+ blob_appendf(pXfer->pOut, "igot %b 1\n", pUuid);
540
+ pXfer->nIGotSent++;
541
+ pXfer->nPrivIGot++;
542
+ }
543
+ return;
544
+ }
530545
if( db_exists("SELECT 1 FROM onremote WHERE rid=%d", rid) ){
531546
return;
532547
}
533548
blob_zero(&uuid);
534549
db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d AND size>=0", rid);
535550
if( blob_size(&uuid)==0 ){
536551
return;
537552
}
538
- if( blob_size(&uuid)>HNAME_LEN_SHA1 && pXfer->clientVersion<20000 ){
553
+ if( blob_size(&uuid)>HNAME_LEN_SHA1 && pXfer->remoteVersion<20000 ){
539554
xfer_cannot_send_sha3_error(pXfer);
540555
return;
541556
}
542557
if( pUuid ){
543558
if( blob_compare(pUuid, &uuid)!=0 ){
@@ -626,11 +641,11 @@
626641
szC = db_column_bytes(&q1, 2);
627642
zContent = db_column_raw(&q1, 2);
628643
srcIsPrivate = db_column_int(&q1, 3);
629644
zDelta = db_column_text(&q1, 4);
630645
if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1);
631
- if( pXfer->clientVersion<20000 && db_column_bytes(&q1,0)!=HNAME_LEN_SHA1 ){
646
+ if( pXfer->remoteVersion<20000 && db_column_bytes(&q1,0)!=HNAME_LEN_SHA1 ){
632647
xfer_cannot_send_sha3_error(pXfer);
633648
db_reset(&q1);
634649
return;
635650
}
636651
blob_appendf(pXfer->pOut, "cfile %s ", zUuid);
@@ -690,11 +705,11 @@
690705
);
691706
}
692707
if( db_step(&q1)==SQLITE_ROW ){
693708
sqlite3_int64 mtime = db_column_int64(&q1, 0);
694709
const char *zHash = db_column_text(&q1, 1);
695
- if( pXfer->clientVersion<20000 && db_column_bytes(&q1,1)>HNAME_LEN_SHA1 ){
710
+ if( pXfer->remoteVersion<20000 && db_column_bytes(&q1,1)>HNAME_LEN_SHA1 ){
696711
xfer_cannot_send_sha3_error(pXfer);
697712
db_reset(&q1);
698713
return;
699714
}
700715
if( blob_size(pXfer->pOut)>=pXfer->mxSend ){
@@ -956,30 +971,48 @@
956971
}
957972
958973
/*
959974
** Send an igot message for every entry in unclustered table.
960975
** Return the number of cards sent.
976
+**
977
+** Except:
978
+** * Do not send igot cards for shunned artifacts
979
+** * Do not send igot cards for phantoms
980
+** * Do not send igot cards for private artifacts
981
+** * Do not send igot cards for any artifact that is in the
982
+** ONREMOTE table, if that table exists.
983
+**
984
+** If the pXfer->resync flag is set, that means we are doing a "--verily"
985
+** sync and all artifacts that don't meet the restrictions above should
986
+** be sent.
961987
*/
962988
static int send_unclustered(Xfer *pXfer){
963989
Stmt q;
964990
int cnt = 0;
991
+ const char *zExtra;
992
+ if( db_table_exists("temp","onremote") ){
993
+ zExtra = " AND NOT EXISTS(SELECT 1 FROM onremote WHERE rid=blob.rid)";
994
+ }else{
995
+ zExtra = "";
996
+ }
965997
if( pXfer->resync ){
966998
db_prepare(&q,
967999
"SELECT uuid, rid FROM blob"
9681000
" WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
9691001
" AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)"
970
- " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
1002
+ " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)%s"
9711003
" AND blob.rid<=%d"
9721004
" ORDER BY blob.rid DESC",
973
- pXfer->resync
1005
+ zExtra /*safe-for-%s*/, pXfer->resync
9741006
);
9751007
}else{
9761008
db_prepare(&q,
9771009
"SELECT uuid FROM unclustered JOIN blob USING(rid) /*scan*/"
9781010
" WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
9791011
" AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)"
980
- " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
1012
+ " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)%s",
1013
+ zExtra /*safe-for-%s*/
9811014
);
9821015
}
9831016
while( db_step(&q)==SQLITE_ROW ){
9841017
blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
9851018
cnt++;
@@ -1191,11 +1224,11 @@
11911224
xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
11921225
11931226
/* file HASH SIZE \n CONTENT
11941227
** file HASH DELTASRC SIZE \n CONTENT
11951228
**
1196
- ** Accept a file from the client.
1229
+ ** Server accepts a file from the client.
11971230
*/
11981231
if( blob_eq(&xfer.aToken[0], "file") ){
11991232
if( !isPush ){
12001233
cgi_reset_content();
12011234
@ error not\sauthorized\sto\swrite
@@ -1212,11 +1245,11 @@
12121245
}else
12131246
12141247
/* cfile HASH USIZE CSIZE \n CONTENT
12151248
** cfile HASH DELTASRC USIZE CSIZE \n CONTENT
12161249
**
1217
- ** Accept a file from the client.
1250
+ ** Server accepts a compressed file from the client.
12181251
*/
12191252
if( blob_eq(&xfer.aToken[0], "cfile") ){
12201253
if( !isPush ){
12211254
cgi_reset_content();
12221255
@ error not\sauthorized\sto\swrite
@@ -1232,11 +1265,11 @@
12321265
}
12331266
}else
12341267
12351268
/* uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT
12361269
**
1237
- ** Accept an unversioned file from the client.
1270
+ ** Server accepts an unversioned file from the client.
12381271
*/
12391272
if( blob_eq(&xfer.aToken[0], "uvfile") ){
12401273
xfer_accept_unversioned_file(&xfer, g.perm.WrUnver);
12411274
if( blob_size(&xfer.err) ){
12421275
cgi_reset_content();
@@ -1246,11 +1279,11 @@
12461279
}
12471280
}else
12481281
12491282
/* gimme HASH
12501283
**
1251
- ** Client is requesting a file. Send it.
1284
+ ** Client is requesting a file from the server. Send it.
12521285
*/
12531286
if( blob_eq(&xfer.aToken[0], "gimme")
12541287
&& xfer.nToken==2
12551288
&& blob_is_hname(&xfer.aToken[1])
12561289
){
@@ -1263,11 +1296,11 @@
12631296
}
12641297
}else
12651298
12661299
/* uvgimme NAME
12671300
**
1268
- ** Client is requesting an unversioned file. Send it.
1301
+ ** Client is requesting an unversioned file from the server. Send it.
12691302
*/
12701303
if( blob_eq(&xfer.aToken[0], "uvgimme")
12711304
&& xfer.nToken==2
12721305
&& blob_is_filename(&xfer.aToken[1])
12731306
){
@@ -1275,23 +1308,44 @@
12751308
}else
12761309
12771310
/* igot HASH ?ISPRIVATE?
12781311
**
12791312
** Client announces that it has a particular file. If the ISPRIVATE
1280
- ** argument exists and is non-zero, then the file is a private file.
1313
+ ** argument exists and is "1", then the file is a private file.
12811314
*/
12821315
if( xfer.nToken>=2
12831316
&& blob_eq(&xfer.aToken[0], "igot")
12841317
&& blob_is_hname(&xfer.aToken[1])
12851318
){
12861319
if( isPush ){
1320
+ int rid = 0;
1321
+ int isPriv = 0;
12871322
if( xfer.nToken==2 || blob_eq(&xfer.aToken[2],"1")==0 ){
1288
- rid_from_uuid(&xfer.aToken[1], 1, 0);
1323
+ /* Client says the artifact is public */
1324
+ rid = rid_from_uuid(&xfer.aToken[1], 1, 0);
12891325
}else if( g.perm.Private ){
1290
- rid_from_uuid(&xfer.aToken[1], 1, 1);
1326
+ /* Client says the artifact is private and the client has
1327
+ ** permission to push private content. Create a new phantom
1328
+ ** artifact that is marked private. */
1329
+ rid = rid_from_uuid(&xfer.aToken[1], 1, 1);
1330
+ isPriv = 1;
12911331
}else{
1292
- server_private_xfer_not_authorized();
1332
+ /* Client says the artifact is private and the client is unable
1333
+ ** or unwilling to send us the artifact. If we already hold the
1334
+ ** artifact here on the server as a phantom, make sure that
1335
+ ** phantom is marked as private so that we don't keep asking about
1336
+ ** it in subsequent sync requests. */
1337
+ rid = rid_from_uuid(&xfer.aToken[1], 0, 1);
1338
+ isPriv = 1;
1339
+ }
1340
+ if( rid ){
1341
+ remote_has(rid);
1342
+ if( isPriv ){
1343
+ content_make_private(rid);
1344
+ }else{
1345
+ content_make_public(rid);
1346
+ }
12931347
}
12941348
}
12951349
}else
12961350
12971351
@@ -1388,11 +1442,12 @@
13881442
@ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x"))
13891443
}else
13901444
13911445
/* login USER NONCE SIGNATURE
13921446
**
1393
- ** Check for a valid login. This has to happen before anything else.
1447
+ ** The client has sent login credentials to the server.
1448
+ ** Validate the login. This has to happen before anything else.
13941449
** The client can send multiple logins. Permissions are cumulative.
13951450
*/
13961451
if( blob_eq(&xfer.aToken[0], "login")
13971452
&& xfer.nToken==4
13981453
){
@@ -1410,11 +1465,11 @@
14101465
}
14111466
}else
14121467
14131468
/* reqconfig NAME
14141469
**
1415
- ** Request a configuration value
1470
+ ** Client is requesting a configuration value from the server
14161471
*/
14171472
if( blob_eq(&xfer.aToken[0], "reqconfig")
14181473
&& xfer.nToken==2
14191474
){
14201475
if( g.perm.Read ){
@@ -1429,12 +1484,12 @@
14291484
}
14301485
}else
14311486
14321487
/* config NAME SIZE \n CONTENT
14331488
**
1434
- ** Receive a configuration value from the client. This is only
1435
- ** permitted for high-privilege users.
1489
+ ** Client has sent a configuration value to the server.
1490
+ ** This is only permitted for high-privilege users.
14361491
*/
14371492
if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3
14381493
&& blob_is_int(&xfer.aToken[2], &size) ){
14391494
const char *zName = blob_str(&xfer.aToken[1]);
14401495
Blob content;
@@ -1474,11 +1529,11 @@
14741529
}else
14751530
14761531
14771532
/* private
14781533
**
1479
- ** This card indicates that the next "file" or "cfile" will contain
1534
+ ** The client card indicates that the next "file" or "cfile" will contain
14801535
** private content.
14811536
*/
14821537
if( blob_eq(&xfer.aToken[0], "private") ){
14831538
if( !g.perm.Private ){
14841539
server_private_xfer_not_authorized();
@@ -1495,10 +1550,12 @@
14951550
** ignored.
14961551
*/
14971552
if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){
14981553
14991554
/* pragma send-private
1555
+ **
1556
+ ** The client is requesting private artifacts.
15001557
**
15011558
** If the user has the "x" privilege (which must be set explicitly -
15021559
** it is not automatic with "a" or "s") then this pragma causes
15031560
** private information to be pulled in addition to public records.
15041561
*/
@@ -1511,22 +1568,32 @@
15111568
}
15121569
}
15131570
15141571
/* pragma send-catalog
15151572
**
1516
- ** Send igot cards for all known artifacts.
1573
+ ** The client wants to see igot cards for all known artifacts.
1574
+ ** This is used as part of "sync --verily" to help ensure that
1575
+ ** no artifacts have been missed on prior syncs.
15171576
*/
15181577
if( blob_eq(&xfer.aToken[1], "send-catalog") ){
15191578
xfer.resync = 0x7fffffff;
15201579
}
15211580
1522
- /* pragma client-version VERSION
1581
+ /* pragma client-version VERSION ?DATE? ?TIME?
15231582
**
1524
- ** Let the server know what version of Fossil is running on the client.
1583
+ ** The client announces to the server what version of Fossil it
1584
+ ** is running. The DATE and TIME are a pure numeric ISO8601 time
1585
+ ** for the specific check-in of the client.
15251586
*/
15261587
if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "client-version") ){
1527
- xfer.clientVersion = atoi(blob_str(&xfer.aToken[2]));
1588
+ xfer.remoteVersion = atoi(blob_str(&xfer.aToken[2]));
1589
+ if( xfer.nToken>=5 ){
1590
+ xfer.remoteDate = atoi(blob_str(&xfer.aToken[3]));
1591
+ xfer.remoteTime = atoi(blob_str(&xfer.aToken[4]));
1592
+ @ pragma server-version %d(RELEASE_VERSION_NUMBER) \
1593
+ @ %d(MANIFEST_NUMERIC_DATE) %d(MANIFEST_NUMERIC_TIME)
1594
+ }
15281595
}
15291596
15301597
/* pragma uv-hash HASH
15311598
**
15321599
** The client wants to make sure that unversioned files are all synced.
@@ -1550,11 +1617,11 @@
15501617
15511618
/* pragma ci-lock CHECKIN-HASH CLIENT-ID
15521619
**
15531620
** The client wants to make non-branch commit against the check-in
15541621
** identified by CHECKIN-HASH. The server will remember this and
1555
- ** subsequent ci-lock request from different clients will generate
1622
+ ** subsequent ci-lock requests from different clients will generate
15561623
** a ci-lock-fail pragma in the reply.
15571624
*/
15581625
if( blob_eq(&xfer.aToken[1], "ci-lock")
15591626
&& xfer.nToken==4
15601627
&& blob_is_hname(&xfer.aToken[2])
@@ -1808,11 +1875,11 @@
18081875
memset(&xfer, 0, sizeof(xfer));
18091876
xfer.pIn = &recv;
18101877
xfer.pOut = &send;
18111878
xfer.mxSend = db_get_int("max-upload", 250000);
18121879
xfer.maxTime = -1;
1813
- xfer.clientVersion = RELEASE_VERSION_NUMBER;
1880
+ xfer.remoteVersion = RELEASE_VERSION_NUMBER;
18141881
if( syncFlags & SYNC_PRIVATE ){
18151882
g.perm.Private = 1;
18161883
xfer.syncPrivate = 1;
18171884
}
18181885
@@ -1856,13 +1923,16 @@
18561923
" SELECT name, 0 FROM unversioned WHERE hash IS NOT NULL;"
18571924
);
18581925
}
18591926
18601927
/*
1861
- ** Always begin with a clone, pull, or push message
1928
+ ** The request from the client always begin with a clone, pull,
1929
+ ** or push message.
18621930
*/
1863
- blob_appendf(&send, "pragma client-version %d\n", RELEASE_VERSION_NUMBER);
1931
+ blob_appendf(&send, "pragma client-version %d %d %d\n",
1932
+ RELEASE_VERSION_NUMBER, MANIFEST_NUMERIC_DATE,
1933
+ MANIFEST_NUMERIC_TIME);
18641934
if( syncFlags & SYNC_CLONE ){
18651935
blob_appendf(&send, "clone 3 %d\n", cloneSeqno);
18661936
syncFlags &= ~(SYNC_PUSH|SYNC_PULL);
18671937
nCardSent++;
18681938
/* TBD: Request all transferable configuration values */
@@ -1898,20 +1968,19 @@
18981968
"CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
18991969
);
19001970
manifest_crosslink_begin();
19011971
19021972
1903
- /* Send back the most recently received cookie. Let the server
1904
- ** figure out if this is a cookie that it cares about.
1973
+ /* Client sends the most recently received cookie back to the server.
1974
+ ** Let the server figure out if this is a cookie that it cares about.
19051975
*/
19061976
zCookie = db_get("cookie", 0);
19071977
if( zCookie ){
19081978
blob_appendf(&send, "cookie %s\n", zCookie);
19091979
}
19101980
1911
- /* Generate gimme cards for phantoms and leaf cards
1912
- ** for all leaves.
1981
+ /* Client sends gimme cards for phantoms
19131982
*/
19141983
if( (syncFlags & SYNC_PULL)!=0
19151984
|| ((syncFlags & SYNC_CLONE)!=0 && cloneSeqno==1)
19161985
){
19171986
request_phantoms(&xfer, mxPhantomReq);
@@ -1920,11 +1989,11 @@
19201989
send_unsent(&xfer);
19211990
nCardSent += send_unclustered(&xfer);
19221991
if( syncFlags & SYNC_PRIVATE ) send_private(&xfer);
19231992
}
19241993
1925
- /* Send configuration parameter requests. On a clone, delay sending
1994
+ /* Client sends configuration parameter requests. On a clone, delay sending
19261995
** this until the second cycle since the login card might fail on
19271996
** the first cycle.
19281997
*/
19291998
if( configRcvMask && ((syncFlags & SYNC_CLONE)==0 || nCycle>0) ){
19301999
const char *zName;
@@ -1937,13 +2006,13 @@
19372006
}
19382007
origConfigRcvMask = configRcvMask;
19392008
configRcvMask = 0;
19402009
}
19412010
1942
- /* Send a request to sync unversioned files. On a clone, delay sending
1943
- ** this until the second cycle since the login card might fail on
1944
- ** the first cycle.
2011
+ /* Client sends a request to sync unversioned files.
2012
+ ** On a clone, delay sending this until the second cycle since
2013
+ ** the login card might fail on the first cycle.
19452014
*/
19462015
if( (syncFlags & SYNC_UNVERSIONED)!=0
19472016
&& ((syncFlags & SYNC_CLONE)==0 || nCycle>0)
19482017
&& !uvHashSent
19492018
){
@@ -1950,11 +2019,12 @@
19502019
blob_appendf(&send, "pragma uv-hash %s\n", unversioned_content_hash(0));
19512020
nCardSent++;
19522021
uvHashSent = 1;
19532022
}
19542023
1955
- /* Send configuration parameters being pushed */
2024
+ /* On a "fossil config push", the client send configuration parameters
2025
+ ** being pushed up to the server */
19562026
if( configSendMask ){
19572027
if( zOpType==0 ) zOpType = "Push";
19582028
nCardSent += configure_send_group(xfer.pOut, configSendMask, 0);
19592029
configSendMask = 0;
19602030
}
@@ -2010,11 +2080,11 @@
20102080
zCkinLock = 0;
20112081
}else if( zClientId ){
20122082
blob_appendf(&send, "pragma ci-unlock %s\n", zClientId);
20132083
}
20142084
2015
- /* Append randomness to the end of the message. This makes all
2085
+ /* Append randomness to the end of the uplink message. This makes all
20162086
** messages unique so that that the login-card nonce will always
20172087
** be unique.
20182088
*/
20192089
zRandomness = db_text(0, "SELECT hex(randomblob(20))");
20202090
blob_appendf(&send, "# %s\n", zRandomness);
@@ -2052,14 +2122,17 @@
20522122
nCardRcvd = 0;
20532123
xfer.nFileSent = 0;
20542124
xfer.nDeltaSent = 0;
20552125
xfer.nGimmeSent = 0;
20562126
xfer.nIGotSent = 0;
2127
+ xfer.nPrivIGot = 0;
20572128
20582129
lastPctDone = -1;
20592130
blob_reset(&send);
2060
- blob_appendf(&send, "pragma client-version %d\n", RELEASE_VERSION_NUMBER);
2131
+ blob_appendf(&send, "pragma client-version %d %d %d\n",
2132
+ RELEASE_VERSION_NUMBER, MANIFEST_NUMERIC_DATE,
2133
+ MANIFEST_NUMERIC_TIME);
20612134
rArrivalTime = db_double(0.0, "SELECT julianday('now')");
20622135
20632136
/* Send the send-private pragma if we are trying to sync private data */
20642137
if( syncFlags & SYNC_PRIVATE ){
20652138
blob_append(&send, "pragma send-private\n", -1);
@@ -2112,30 +2185,30 @@
21122185
}
21132186
21142187
/* file HASH SIZE \n CONTENT
21152188
** file HASH DELTASRC SIZE \n CONTENT
21162189
**
2117
- ** Receive a file transmitted from the server.
2190
+ ** Client receives a file transmitted from the server.
21182191
*/
21192192
if( blob_eq(&xfer.aToken[0],"file") ){
21202193
xfer_accept_file(&xfer, (syncFlags & SYNC_CLONE)!=0, 0, 0);
21212194
nArtifactRcvd++;
21222195
}else
21232196
21242197
/* cfile HASH USIZE CSIZE \n CONTENT
21252198
** cfile HASH DELTASRC USIZE CSIZE \n CONTENT
21262199
**
2127
- ** Receive a compressed file transmitted from the server.
2200
+ ** Client receives a compressed file transmitted from the server.
21282201
*/
21292202
if( blob_eq(&xfer.aToken[0],"cfile") ){
21302203
xfer_accept_compressed_file(&xfer, 0, 0);
21312204
nArtifactRcvd++;
21322205
}else
21332206
21342207
/* uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT
21352208
**
2136
- ** Accept an unversioned file from the server.
2209
+ ** Client accepts an unversioned file from the server.
21372210
*/
21382211
if( blob_eq(&xfer.aToken[0], "uvfile") ){
21392212
xfer_accept_unversioned_file(&xfer, 1);
21402213
nArtifactRcvd++;
21412214
nUvFileRcvd++;
@@ -2145,13 +2218,14 @@
21452218
}
21462219
}else
21472220
21482221
/* gimme HASH
21492222
**
2150
- ** Server is requesting a file. If the file is a manifest, assume
2151
- ** that the server will also want to know all of the content files
2152
- ** associated with the manifest and send those too.
2223
+ ** Client receives an artifact request from the server.
2224
+ ** If the file is a manifest, assume that the server will also want
2225
+ ** to know all of the content artifacts associated with the manifest
2226
+ ** and send those too.
21532227
*/
21542228
if( blob_eq(&xfer.aToken[0], "gimme")
21552229
&& xfer.nToken==2
21562230
&& blob_is_hname(&xfer.aToken[1])
21572231
){
@@ -2179,11 +2253,15 @@
21792253
){
21802254
int rid;
21812255
int isPriv = xfer.nToken>=3 && blob_eq(&xfer.aToken[2],"1");
21822256
rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
21832257
if( rid>0 ){
2184
- if( !isPriv ) content_make_public(rid);
2258
+ if( isPriv ){
2259
+ content_make_private(rid);
2260
+ }else{
2261
+ content_make_public(rid);
2262
+ }
21852263
}else if( isPriv && !g.perm.Private ){
21862264
/* ignore private files */
21872265
}else if( (syncFlags & (SYNC_PULL|SYNC_CLONE))!=0 ){
21882266
rid = content_new(blob_str(&xfer.aToken[1]), isPriv);
21892267
if( rid ) newPhantom = 1;
@@ -2264,11 +2342,11 @@
22642342
}else
22652343
22662344
/* push SERVERCODE PRODUCTCODE
22672345
**
22682346
** Should only happen in response to a clone. This message tells
2269
- ** the client what product to use for the new database.
2347
+ ** the client what product code to use for the new database.
22702348
*/
22712349
if( blob_eq(&xfer.aToken[0],"push")
22722350
&& xfer.nToken==3
22732351
&& (syncFlags & SYNC_CLONE)!=0
22742352
&& blob_is_hname(&xfer.aToken[2])
@@ -2281,11 +2359,11 @@
22812359
nCardSent++;
22822360
}else
22832361
22842362
/* config NAME SIZE \n CONTENT
22852363
**
2286
- ** Receive a configuration value from the server.
2364
+ ** Client receive a configuration value from the server.
22872365
**
22882366
** The received configuration setting is silently ignored if it was
22892367
** not requested by a prior "reqconfig" sent from client to server.
22902368
*/
22912369
if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3
@@ -2303,11 +2381,11 @@
23032381
}else
23042382
23052383
23062384
/* cookie TEXT
23072385
**
2308
- ** The server might include a cookie in its reply. The client
2386
+ ** The client reserves a cookie from the server. The client
23092387
** should remember this cookie and send it back to the server
23102388
** in its next query.
23112389
**
23122390
** Each cookie received overwrites the prior cookie from the
23132391
** same server.
@@ -2317,12 +2395,12 @@
23172395
}else
23182396
23192397
23202398
/* private
23212399
**
2322
- ** This card indicates that the next "file" or "cfile" will contain
2323
- ** private content.
2400
+ ** The server tells the client that the next "file" or "cfile" will
2401
+ ** contain private content.
23242402
*/
23252403
if( blob_eq(&xfer.aToken[0], "private") ){
23262404
xfer.nextIsPrivate = 1;
23272405
}else
23282406
@@ -2338,11 +2416,12 @@
23382416
blob_is_int(&xfer.aToken[1], &cloneSeqno);
23392417
}else
23402418
23412419
/* message MESSAGE
23422420
**
2343
- ** Print a message. Similar to "error" but does not stop processing.
2421
+ ** A message is received from the server. Print it.
2422
+ ** Similar to "error" but does not stop processing.
23442423
**
23452424
** If the "login failed" message is seen, clear the sync password prior
23462425
** to the next cycle.
23472426
*/
23482427
if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){
@@ -2364,11 +2443,27 @@
23642443
** The server can send pragmas to try to convey meta-information to
23652444
** the client. These are informational only. Unknown pragmas are
23662445
** silently ignored.
23672446
*/
23682447
if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){
2369
- /* If the server is unwill to accept new unversioned content (because
2448
+ /* pragma server-version VERSION ?DATE? ?TIME?
2449
+ **
2450
+ ** The servger announces to the server what version of Fossil it
2451
+ ** is running. The DATE and TIME are a pure numeric ISO8601 time
2452
+ ** for the specific check-in of the client.
2453
+ */
2454
+ if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "server-version") ){
2455
+ xfer.remoteVersion = atoi(blob_str(&xfer.aToken[2]));
2456
+ if( xfer.nToken>=5 ){
2457
+ xfer.remoteDate = atoi(blob_str(&xfer.aToken[3]));
2458
+ xfer.remoteTime = atoi(blob_str(&xfer.aToken[4]));
2459
+ }
2460
+ }
2461
+
2462
+ /* pragma uv-pull-only
2463
+ **
2464
+ ** If the server is unwill to accept new unversioned content (because
23702465
** this client lacks the necessary permissions) then it sends a
23712466
** "uv-pull-only" pragma so that the client will know not to waste
23722467
** bandwidth trying to upload unversioned content. If the server
23732468
** does accept new unversioned content, it sends "uv-push-ok".
23742469
*/
@@ -2407,11 +2502,12 @@
24072502
}
24082503
}else
24092504
24102505
/* error MESSAGE
24112506
**
2412
- ** Report an error and abandon the sync session.
2507
+ ** The server is reporting an error. The client will abandon
2508
+ ** the sync session.
24132509
**
24142510
** Except, when cloning we will sometimes get an error on the
24152511
** first message exchange because the project-code is unknown
24162512
** and so the login card on the request was invalid. The project-code
24172513
** is returned in the reply before the error card, so second and
@@ -2491,10 +2587,11 @@
24912587
** another round
24922588
*/
24932589
if( xfer.nFileSent+xfer.nDeltaSent>0 || uvDoPush ){
24942590
go = 1;
24952591
}
2592
+ if( xfer.nPrivIGot>0 && nCycle==1 ) go = 1;
24962593
24972594
/* If this is a clone, the go at least two rounds */
24982595
if( (syncFlags & SYNC_CLONE)!=0 && nCycle==1 ) go = 1;
24992596
25002597
/* Stop the cycle if the server sends a "clone_seqno 0" card and
25012598
--- src/xfer.c
+++ src/xfer.c
@@ -39,10 +39,11 @@
39 Blob line; /* The current line of input */
40 Blob aToken[6]; /* Tokenized version of line */
41 Blob err; /* Error message text */
42 int nToken; /* Number of tokens in line */
43 int nIGotSent; /* Number of "igot" cards sent */
 
44 int nGimmeSent; /* Number of gimme cards sent */
45 int nFileSent; /* Number of files sent */
46 int nDeltaSent; /* Number of deltas sent */
47 int nFileRcvd; /* Number of files received */
48 int nDeltaRcvd; /* Number of deltas received */
@@ -49,11 +50,13 @@
49 int nDanglingFile; /* Number of dangling deltas received */
50 int mxSend; /* Stop sending "file" when pOut reaches this size */
51 int resync; /* Send igot cards for all holdings */
52 u8 syncPrivate; /* True to enable syncing private content */
53 u8 nextIsPrivate; /* If true, next "file" received is a private */
54 u32 clientVersion; /* Version of the client software */
 
 
55 time_t maxTime; /* Time when this transfer should be finished */
56 };
57
58
59 /*
@@ -524,20 +527,32 @@
524 static void send_file(Xfer *pXfer, int rid, Blob *pUuid, int nativeDelta){
525 Blob content, uuid;
526 int size = 0;
527 int isPriv = content_is_private(rid);
528
529 if( pXfer->syncPrivate==0 && isPriv ) return;
 
 
 
 
 
 
 
 
 
 
 
 
530 if( db_exists("SELECT 1 FROM onremote WHERE rid=%d", rid) ){
531 return;
532 }
533 blob_zero(&uuid);
534 db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d AND size>=0", rid);
535 if( blob_size(&uuid)==0 ){
536 return;
537 }
538 if( blob_size(&uuid)>HNAME_LEN_SHA1 && pXfer->clientVersion<20000 ){
539 xfer_cannot_send_sha3_error(pXfer);
540 return;
541 }
542 if( pUuid ){
543 if( blob_compare(pUuid, &uuid)!=0 ){
@@ -626,11 +641,11 @@
626 szC = db_column_bytes(&q1, 2);
627 zContent = db_column_raw(&q1, 2);
628 srcIsPrivate = db_column_int(&q1, 3);
629 zDelta = db_column_text(&q1, 4);
630 if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1);
631 if( pXfer->clientVersion<20000 && db_column_bytes(&q1,0)!=HNAME_LEN_SHA1 ){
632 xfer_cannot_send_sha3_error(pXfer);
633 db_reset(&q1);
634 return;
635 }
636 blob_appendf(pXfer->pOut, "cfile %s ", zUuid);
@@ -690,11 +705,11 @@
690 );
691 }
692 if( db_step(&q1)==SQLITE_ROW ){
693 sqlite3_int64 mtime = db_column_int64(&q1, 0);
694 const char *zHash = db_column_text(&q1, 1);
695 if( pXfer->clientVersion<20000 && db_column_bytes(&q1,1)>HNAME_LEN_SHA1 ){
696 xfer_cannot_send_sha3_error(pXfer);
697 db_reset(&q1);
698 return;
699 }
700 if( blob_size(pXfer->pOut)>=pXfer->mxSend ){
@@ -956,30 +971,48 @@
956 }
957
958 /*
959 ** Send an igot message for every entry in unclustered table.
960 ** Return the number of cards sent.
 
 
 
 
 
 
 
 
 
 
 
961 */
962 static int send_unclustered(Xfer *pXfer){
963 Stmt q;
964 int cnt = 0;
 
 
 
 
 
 
965 if( pXfer->resync ){
966 db_prepare(&q,
967 "SELECT uuid, rid FROM blob"
968 " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
969 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)"
970 " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
971 " AND blob.rid<=%d"
972 " ORDER BY blob.rid DESC",
973 pXfer->resync
974 );
975 }else{
976 db_prepare(&q,
977 "SELECT uuid FROM unclustered JOIN blob USING(rid) /*scan*/"
978 " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
979 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)"
980 " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
 
981 );
982 }
983 while( db_step(&q)==SQLITE_ROW ){
984 blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
985 cnt++;
@@ -1191,11 +1224,11 @@
1191 xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
1192
1193 /* file HASH SIZE \n CONTENT
1194 ** file HASH DELTASRC SIZE \n CONTENT
1195 **
1196 ** Accept a file from the client.
1197 */
1198 if( blob_eq(&xfer.aToken[0], "file") ){
1199 if( !isPush ){
1200 cgi_reset_content();
1201 @ error not\sauthorized\sto\swrite
@@ -1212,11 +1245,11 @@
1212 }else
1213
1214 /* cfile HASH USIZE CSIZE \n CONTENT
1215 ** cfile HASH DELTASRC USIZE CSIZE \n CONTENT
1216 **
1217 ** Accept a file from the client.
1218 */
1219 if( blob_eq(&xfer.aToken[0], "cfile") ){
1220 if( !isPush ){
1221 cgi_reset_content();
1222 @ error not\sauthorized\sto\swrite
@@ -1232,11 +1265,11 @@
1232 }
1233 }else
1234
1235 /* uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT
1236 **
1237 ** Accept an unversioned file from the client.
1238 */
1239 if( blob_eq(&xfer.aToken[0], "uvfile") ){
1240 xfer_accept_unversioned_file(&xfer, g.perm.WrUnver);
1241 if( blob_size(&xfer.err) ){
1242 cgi_reset_content();
@@ -1246,11 +1279,11 @@
1246 }
1247 }else
1248
1249 /* gimme HASH
1250 **
1251 ** Client is requesting a file. Send it.
1252 */
1253 if( blob_eq(&xfer.aToken[0], "gimme")
1254 && xfer.nToken==2
1255 && blob_is_hname(&xfer.aToken[1])
1256 ){
@@ -1263,11 +1296,11 @@
1263 }
1264 }else
1265
1266 /* uvgimme NAME
1267 **
1268 ** Client is requesting an unversioned file. Send it.
1269 */
1270 if( blob_eq(&xfer.aToken[0], "uvgimme")
1271 && xfer.nToken==2
1272 && blob_is_filename(&xfer.aToken[1])
1273 ){
@@ -1275,23 +1308,44 @@
1275 }else
1276
1277 /* igot HASH ?ISPRIVATE?
1278 **
1279 ** Client announces that it has a particular file. If the ISPRIVATE
1280 ** argument exists and is non-zero, then the file is a private file.
1281 */
1282 if( xfer.nToken>=2
1283 && blob_eq(&xfer.aToken[0], "igot")
1284 && blob_is_hname(&xfer.aToken[1])
1285 ){
1286 if( isPush ){
 
 
1287 if( xfer.nToken==2 || blob_eq(&xfer.aToken[2],"1")==0 ){
1288 rid_from_uuid(&xfer.aToken[1], 1, 0);
 
1289 }else if( g.perm.Private ){
1290 rid_from_uuid(&xfer.aToken[1], 1, 1);
 
 
 
 
1291 }else{
1292 server_private_xfer_not_authorized();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1293 }
1294 }
1295 }else
1296
1297
@@ -1388,11 +1442,12 @@
1388 @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x"))
1389 }else
1390
1391 /* login USER NONCE SIGNATURE
1392 **
1393 ** Check for a valid login. This has to happen before anything else.
 
1394 ** The client can send multiple logins. Permissions are cumulative.
1395 */
1396 if( blob_eq(&xfer.aToken[0], "login")
1397 && xfer.nToken==4
1398 ){
@@ -1410,11 +1465,11 @@
1410 }
1411 }else
1412
1413 /* reqconfig NAME
1414 **
1415 ** Request a configuration value
1416 */
1417 if( blob_eq(&xfer.aToken[0], "reqconfig")
1418 && xfer.nToken==2
1419 ){
1420 if( g.perm.Read ){
@@ -1429,12 +1484,12 @@
1429 }
1430 }else
1431
1432 /* config NAME SIZE \n CONTENT
1433 **
1434 ** Receive a configuration value from the client. This is only
1435 ** permitted for high-privilege users.
1436 */
1437 if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3
1438 && blob_is_int(&xfer.aToken[2], &size) ){
1439 const char *zName = blob_str(&xfer.aToken[1]);
1440 Blob content;
@@ -1474,11 +1529,11 @@
1474 }else
1475
1476
1477 /* private
1478 **
1479 ** This card indicates that the next "file" or "cfile" will contain
1480 ** private content.
1481 */
1482 if( blob_eq(&xfer.aToken[0], "private") ){
1483 if( !g.perm.Private ){
1484 server_private_xfer_not_authorized();
@@ -1495,10 +1550,12 @@
1495 ** ignored.
1496 */
1497 if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){
1498
1499 /* pragma send-private
 
 
1500 **
1501 ** If the user has the "x" privilege (which must be set explicitly -
1502 ** it is not automatic with "a" or "s") then this pragma causes
1503 ** private information to be pulled in addition to public records.
1504 */
@@ -1511,22 +1568,32 @@
1511 }
1512 }
1513
1514 /* pragma send-catalog
1515 **
1516 ** Send igot cards for all known artifacts.
 
 
1517 */
1518 if( blob_eq(&xfer.aToken[1], "send-catalog") ){
1519 xfer.resync = 0x7fffffff;
1520 }
1521
1522 /* pragma client-version VERSION
1523 **
1524 ** Let the server know what version of Fossil is running on the client.
 
 
1525 */
1526 if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "client-version") ){
1527 xfer.clientVersion = atoi(blob_str(&xfer.aToken[2]));
 
 
 
 
 
 
1528 }
1529
1530 /* pragma uv-hash HASH
1531 **
1532 ** The client wants to make sure that unversioned files are all synced.
@@ -1550,11 +1617,11 @@
1550
1551 /* pragma ci-lock CHECKIN-HASH CLIENT-ID
1552 **
1553 ** The client wants to make non-branch commit against the check-in
1554 ** identified by CHECKIN-HASH. The server will remember this and
1555 ** subsequent ci-lock request from different clients will generate
1556 ** a ci-lock-fail pragma in the reply.
1557 */
1558 if( blob_eq(&xfer.aToken[1], "ci-lock")
1559 && xfer.nToken==4
1560 && blob_is_hname(&xfer.aToken[2])
@@ -1808,11 +1875,11 @@
1808 memset(&xfer, 0, sizeof(xfer));
1809 xfer.pIn = &recv;
1810 xfer.pOut = &send;
1811 xfer.mxSend = db_get_int("max-upload", 250000);
1812 xfer.maxTime = -1;
1813 xfer.clientVersion = RELEASE_VERSION_NUMBER;
1814 if( syncFlags & SYNC_PRIVATE ){
1815 g.perm.Private = 1;
1816 xfer.syncPrivate = 1;
1817 }
1818
@@ -1856,13 +1923,16 @@
1856 " SELECT name, 0 FROM unversioned WHERE hash IS NOT NULL;"
1857 );
1858 }
1859
1860 /*
1861 ** Always begin with a clone, pull, or push message
 
1862 */
1863 blob_appendf(&send, "pragma client-version %d\n", RELEASE_VERSION_NUMBER);
 
 
1864 if( syncFlags & SYNC_CLONE ){
1865 blob_appendf(&send, "clone 3 %d\n", cloneSeqno);
1866 syncFlags &= ~(SYNC_PUSH|SYNC_PULL);
1867 nCardSent++;
1868 /* TBD: Request all transferable configuration values */
@@ -1898,20 +1968,19 @@
1898 "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
1899 );
1900 manifest_crosslink_begin();
1901
1902
1903 /* Send back the most recently received cookie. Let the server
1904 ** figure out if this is a cookie that it cares about.
1905 */
1906 zCookie = db_get("cookie", 0);
1907 if( zCookie ){
1908 blob_appendf(&send, "cookie %s\n", zCookie);
1909 }
1910
1911 /* Generate gimme cards for phantoms and leaf cards
1912 ** for all leaves.
1913 */
1914 if( (syncFlags & SYNC_PULL)!=0
1915 || ((syncFlags & SYNC_CLONE)!=0 && cloneSeqno==1)
1916 ){
1917 request_phantoms(&xfer, mxPhantomReq);
@@ -1920,11 +1989,11 @@
1920 send_unsent(&xfer);
1921 nCardSent += send_unclustered(&xfer);
1922 if( syncFlags & SYNC_PRIVATE ) send_private(&xfer);
1923 }
1924
1925 /* Send configuration parameter requests. On a clone, delay sending
1926 ** this until the second cycle since the login card might fail on
1927 ** the first cycle.
1928 */
1929 if( configRcvMask && ((syncFlags & SYNC_CLONE)==0 || nCycle>0) ){
1930 const char *zName;
@@ -1937,13 +2006,13 @@
1937 }
1938 origConfigRcvMask = configRcvMask;
1939 configRcvMask = 0;
1940 }
1941
1942 /* Send a request to sync unversioned files. On a clone, delay sending
1943 ** this until the second cycle since the login card might fail on
1944 ** the first cycle.
1945 */
1946 if( (syncFlags & SYNC_UNVERSIONED)!=0
1947 && ((syncFlags & SYNC_CLONE)==0 || nCycle>0)
1948 && !uvHashSent
1949 ){
@@ -1950,11 +2019,12 @@
1950 blob_appendf(&send, "pragma uv-hash %s\n", unversioned_content_hash(0));
1951 nCardSent++;
1952 uvHashSent = 1;
1953 }
1954
1955 /* Send configuration parameters being pushed */
 
1956 if( configSendMask ){
1957 if( zOpType==0 ) zOpType = "Push";
1958 nCardSent += configure_send_group(xfer.pOut, configSendMask, 0);
1959 configSendMask = 0;
1960 }
@@ -2010,11 +2080,11 @@
2010 zCkinLock = 0;
2011 }else if( zClientId ){
2012 blob_appendf(&send, "pragma ci-unlock %s\n", zClientId);
2013 }
2014
2015 /* Append randomness to the end of the message. This makes all
2016 ** messages unique so that that the login-card nonce will always
2017 ** be unique.
2018 */
2019 zRandomness = db_text(0, "SELECT hex(randomblob(20))");
2020 blob_appendf(&send, "# %s\n", zRandomness);
@@ -2052,14 +2122,17 @@
2052 nCardRcvd = 0;
2053 xfer.nFileSent = 0;
2054 xfer.nDeltaSent = 0;
2055 xfer.nGimmeSent = 0;
2056 xfer.nIGotSent = 0;
 
2057
2058 lastPctDone = -1;
2059 blob_reset(&send);
2060 blob_appendf(&send, "pragma client-version %d\n", RELEASE_VERSION_NUMBER);
 
 
2061 rArrivalTime = db_double(0.0, "SELECT julianday('now')");
2062
2063 /* Send the send-private pragma if we are trying to sync private data */
2064 if( syncFlags & SYNC_PRIVATE ){
2065 blob_append(&send, "pragma send-private\n", -1);
@@ -2112,30 +2185,30 @@
2112 }
2113
2114 /* file HASH SIZE \n CONTENT
2115 ** file HASH DELTASRC SIZE \n CONTENT
2116 **
2117 ** Receive a file transmitted from the server.
2118 */
2119 if( blob_eq(&xfer.aToken[0],"file") ){
2120 xfer_accept_file(&xfer, (syncFlags & SYNC_CLONE)!=0, 0, 0);
2121 nArtifactRcvd++;
2122 }else
2123
2124 /* cfile HASH USIZE CSIZE \n CONTENT
2125 ** cfile HASH DELTASRC USIZE CSIZE \n CONTENT
2126 **
2127 ** Receive a compressed file transmitted from the server.
2128 */
2129 if( blob_eq(&xfer.aToken[0],"cfile") ){
2130 xfer_accept_compressed_file(&xfer, 0, 0);
2131 nArtifactRcvd++;
2132 }else
2133
2134 /* uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT
2135 **
2136 ** Accept an unversioned file from the server.
2137 */
2138 if( blob_eq(&xfer.aToken[0], "uvfile") ){
2139 xfer_accept_unversioned_file(&xfer, 1);
2140 nArtifactRcvd++;
2141 nUvFileRcvd++;
@@ -2145,13 +2218,14 @@
2145 }
2146 }else
2147
2148 /* gimme HASH
2149 **
2150 ** Server is requesting a file. If the file is a manifest, assume
2151 ** that the server will also want to know all of the content files
2152 ** associated with the manifest and send those too.
 
2153 */
2154 if( blob_eq(&xfer.aToken[0], "gimme")
2155 && xfer.nToken==2
2156 && blob_is_hname(&xfer.aToken[1])
2157 ){
@@ -2179,11 +2253,15 @@
2179 ){
2180 int rid;
2181 int isPriv = xfer.nToken>=3 && blob_eq(&xfer.aToken[2],"1");
2182 rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
2183 if( rid>0 ){
2184 if( !isPriv ) content_make_public(rid);
 
 
 
 
2185 }else if( isPriv && !g.perm.Private ){
2186 /* ignore private files */
2187 }else if( (syncFlags & (SYNC_PULL|SYNC_CLONE))!=0 ){
2188 rid = content_new(blob_str(&xfer.aToken[1]), isPriv);
2189 if( rid ) newPhantom = 1;
@@ -2264,11 +2342,11 @@
2264 }else
2265
2266 /* push SERVERCODE PRODUCTCODE
2267 **
2268 ** Should only happen in response to a clone. This message tells
2269 ** the client what product to use for the new database.
2270 */
2271 if( blob_eq(&xfer.aToken[0],"push")
2272 && xfer.nToken==3
2273 && (syncFlags & SYNC_CLONE)!=0
2274 && blob_is_hname(&xfer.aToken[2])
@@ -2281,11 +2359,11 @@
2281 nCardSent++;
2282 }else
2283
2284 /* config NAME SIZE \n CONTENT
2285 **
2286 ** Receive a configuration value from the server.
2287 **
2288 ** The received configuration setting is silently ignored if it was
2289 ** not requested by a prior "reqconfig" sent from client to server.
2290 */
2291 if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3
@@ -2303,11 +2381,11 @@
2303 }else
2304
2305
2306 /* cookie TEXT
2307 **
2308 ** The server might include a cookie in its reply. The client
2309 ** should remember this cookie and send it back to the server
2310 ** in its next query.
2311 **
2312 ** Each cookie received overwrites the prior cookie from the
2313 ** same server.
@@ -2317,12 +2395,12 @@
2317 }else
2318
2319
2320 /* private
2321 **
2322 ** This card indicates that the next "file" or "cfile" will contain
2323 ** private content.
2324 */
2325 if( blob_eq(&xfer.aToken[0], "private") ){
2326 xfer.nextIsPrivate = 1;
2327 }else
2328
@@ -2338,11 +2416,12 @@
2338 blob_is_int(&xfer.aToken[1], &cloneSeqno);
2339 }else
2340
2341 /* message MESSAGE
2342 **
2343 ** Print a message. Similar to "error" but does not stop processing.
 
2344 **
2345 ** If the "login failed" message is seen, clear the sync password prior
2346 ** to the next cycle.
2347 */
2348 if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){
@@ -2364,11 +2443,27 @@
2364 ** The server can send pragmas to try to convey meta-information to
2365 ** the client. These are informational only. Unknown pragmas are
2366 ** silently ignored.
2367 */
2368 if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){
2369 /* If the server is unwill to accept new unversioned content (because
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2370 ** this client lacks the necessary permissions) then it sends a
2371 ** "uv-pull-only" pragma so that the client will know not to waste
2372 ** bandwidth trying to upload unversioned content. If the server
2373 ** does accept new unversioned content, it sends "uv-push-ok".
2374 */
@@ -2407,11 +2502,12 @@
2407 }
2408 }else
2409
2410 /* error MESSAGE
2411 **
2412 ** Report an error and abandon the sync session.
 
2413 **
2414 ** Except, when cloning we will sometimes get an error on the
2415 ** first message exchange because the project-code is unknown
2416 ** and so the login card on the request was invalid. The project-code
2417 ** is returned in the reply before the error card, so second and
@@ -2491,10 +2587,11 @@
2491 ** another round
2492 */
2493 if( xfer.nFileSent+xfer.nDeltaSent>0 || uvDoPush ){
2494 go = 1;
2495 }
 
2496
2497 /* If this is a clone, the go at least two rounds */
2498 if( (syncFlags & SYNC_CLONE)!=0 && nCycle==1 ) go = 1;
2499
2500 /* Stop the cycle if the server sends a "clone_seqno 0" card and
2501
--- src/xfer.c
+++ src/xfer.c
@@ -39,10 +39,11 @@
39 Blob line; /* The current line of input */
40 Blob aToken[6]; /* Tokenized version of line */
41 Blob err; /* Error message text */
42 int nToken; /* Number of tokens in line */
43 int nIGotSent; /* Number of "igot" cards sent */
44 int nPrivIGot; /* Number of private "igot" cards */
45 int nGimmeSent; /* Number of gimme cards sent */
46 int nFileSent; /* Number of files sent */
47 int nDeltaSent; /* Number of deltas sent */
48 int nFileRcvd; /* Number of files received */
49 int nDeltaRcvd; /* Number of deltas received */
@@ -49,11 +50,13 @@
50 int nDanglingFile; /* Number of dangling deltas received */
51 int mxSend; /* Stop sending "file" when pOut reaches this size */
52 int resync; /* Send igot cards for all holdings */
53 u8 syncPrivate; /* True to enable syncing private content */
54 u8 nextIsPrivate; /* If true, next "file" received is a private */
55 u32 remoteVersion; /* Version of fossil running on the other side */
56 u32 remoteDate; /* Date for specific client software edition */
57 u32 remoteTime; /* Time of date correspoding on remoteDate */
58 time_t maxTime; /* Time when this transfer should be finished */
59 };
60
61
62 /*
@@ -524,20 +527,32 @@
527 static void send_file(Xfer *pXfer, int rid, Blob *pUuid, int nativeDelta){
528 Blob content, uuid;
529 int size = 0;
530 int isPriv = content_is_private(rid);
531
532 if( isPriv && pXfer->syncPrivate==0 ){
533 if( pXfer->remoteDate>=20200413 && pUuid && blob_size(pUuid)>0 ){
534 /* If the artifact is private and we are not doing a private sync,
535 ** at least tell the other side that the artifact exists and is
536 ** known to be private. But only do this for newer clients since
537 ** older ones will throw an error if they get a private igot card
538 ** and private syncing is disallowed */
539 blob_appendf(pXfer->pOut, "igot %b 1\n", pUuid);
540 pXfer->nIGotSent++;
541 pXfer->nPrivIGot++;
542 }
543 return;
544 }
545 if( db_exists("SELECT 1 FROM onremote WHERE rid=%d", rid) ){
546 return;
547 }
548 blob_zero(&uuid);
549 db_blob(&uuid, "SELECT uuid FROM blob WHERE rid=%d AND size>=0", rid);
550 if( blob_size(&uuid)==0 ){
551 return;
552 }
553 if( blob_size(&uuid)>HNAME_LEN_SHA1 && pXfer->remoteVersion<20000 ){
554 xfer_cannot_send_sha3_error(pXfer);
555 return;
556 }
557 if( pUuid ){
558 if( blob_compare(pUuid, &uuid)!=0 ){
@@ -626,11 +641,11 @@
641 szC = db_column_bytes(&q1, 2);
642 zContent = db_column_raw(&q1, 2);
643 srcIsPrivate = db_column_int(&q1, 3);
644 zDelta = db_column_text(&q1, 4);
645 if( isPrivate ) blob_append(pXfer->pOut, "private\n", -1);
646 if( pXfer->remoteVersion<20000 && db_column_bytes(&q1,0)!=HNAME_LEN_SHA1 ){
647 xfer_cannot_send_sha3_error(pXfer);
648 db_reset(&q1);
649 return;
650 }
651 blob_appendf(pXfer->pOut, "cfile %s ", zUuid);
@@ -690,11 +705,11 @@
705 );
706 }
707 if( db_step(&q1)==SQLITE_ROW ){
708 sqlite3_int64 mtime = db_column_int64(&q1, 0);
709 const char *zHash = db_column_text(&q1, 1);
710 if( pXfer->remoteVersion<20000 && db_column_bytes(&q1,1)>HNAME_LEN_SHA1 ){
711 xfer_cannot_send_sha3_error(pXfer);
712 db_reset(&q1);
713 return;
714 }
715 if( blob_size(pXfer->pOut)>=pXfer->mxSend ){
@@ -956,30 +971,48 @@
971 }
972
973 /*
974 ** Send an igot message for every entry in unclustered table.
975 ** Return the number of cards sent.
976 **
977 ** Except:
978 ** * Do not send igot cards for shunned artifacts
979 ** * Do not send igot cards for phantoms
980 ** * Do not send igot cards for private artifacts
981 ** * Do not send igot cards for any artifact that is in the
982 ** ONREMOTE table, if that table exists.
983 **
984 ** If the pXfer->resync flag is set, that means we are doing a "--verily"
985 ** sync and all artifacts that don't meet the restrictions above should
986 ** be sent.
987 */
988 static int send_unclustered(Xfer *pXfer){
989 Stmt q;
990 int cnt = 0;
991 const char *zExtra;
992 if( db_table_exists("temp","onremote") ){
993 zExtra = " AND NOT EXISTS(SELECT 1 FROM onremote WHERE rid=blob.rid)";
994 }else{
995 zExtra = "";
996 }
997 if( pXfer->resync ){
998 db_prepare(&q,
999 "SELECT uuid, rid FROM blob"
1000 " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
1001 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)"
1002 " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)%s"
1003 " AND blob.rid<=%d"
1004 " ORDER BY blob.rid DESC",
1005 zExtra /*safe-for-%s*/, pXfer->resync
1006 );
1007 }else{
1008 db_prepare(&q,
1009 "SELECT uuid FROM unclustered JOIN blob USING(rid) /*scan*/"
1010 " WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
1011 " AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)"
1012 " AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)%s",
1013 zExtra /*safe-for-%s*/
1014 );
1015 }
1016 while( db_step(&q)==SQLITE_ROW ){
1017 blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
1018 cnt++;
@@ -1191,11 +1224,11 @@
1224 xfer.nToken = blob_tokenize(&xfer.line, xfer.aToken, count(xfer.aToken));
1225
1226 /* file HASH SIZE \n CONTENT
1227 ** file HASH DELTASRC SIZE \n CONTENT
1228 **
1229 ** Server accepts a file from the client.
1230 */
1231 if( blob_eq(&xfer.aToken[0], "file") ){
1232 if( !isPush ){
1233 cgi_reset_content();
1234 @ error not\sauthorized\sto\swrite
@@ -1212,11 +1245,11 @@
1245 }else
1246
1247 /* cfile HASH USIZE CSIZE \n CONTENT
1248 ** cfile HASH DELTASRC USIZE CSIZE \n CONTENT
1249 **
1250 ** Server accepts a compressed file from the client.
1251 */
1252 if( blob_eq(&xfer.aToken[0], "cfile") ){
1253 if( !isPush ){
1254 cgi_reset_content();
1255 @ error not\sauthorized\sto\swrite
@@ -1232,11 +1265,11 @@
1265 }
1266 }else
1267
1268 /* uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT
1269 **
1270 ** Server accepts an unversioned file from the client.
1271 */
1272 if( blob_eq(&xfer.aToken[0], "uvfile") ){
1273 xfer_accept_unversioned_file(&xfer, g.perm.WrUnver);
1274 if( blob_size(&xfer.err) ){
1275 cgi_reset_content();
@@ -1246,11 +1279,11 @@
1279 }
1280 }else
1281
1282 /* gimme HASH
1283 **
1284 ** Client is requesting a file from the server. Send it.
1285 */
1286 if( blob_eq(&xfer.aToken[0], "gimme")
1287 && xfer.nToken==2
1288 && blob_is_hname(&xfer.aToken[1])
1289 ){
@@ -1263,11 +1296,11 @@
1296 }
1297 }else
1298
1299 /* uvgimme NAME
1300 **
1301 ** Client is requesting an unversioned file from the server. Send it.
1302 */
1303 if( blob_eq(&xfer.aToken[0], "uvgimme")
1304 && xfer.nToken==2
1305 && blob_is_filename(&xfer.aToken[1])
1306 ){
@@ -1275,23 +1308,44 @@
1308 }else
1309
1310 /* igot HASH ?ISPRIVATE?
1311 **
1312 ** Client announces that it has a particular file. If the ISPRIVATE
1313 ** argument exists and is "1", then the file is a private file.
1314 */
1315 if( xfer.nToken>=2
1316 && blob_eq(&xfer.aToken[0], "igot")
1317 && blob_is_hname(&xfer.aToken[1])
1318 ){
1319 if( isPush ){
1320 int rid = 0;
1321 int isPriv = 0;
1322 if( xfer.nToken==2 || blob_eq(&xfer.aToken[2],"1")==0 ){
1323 /* Client says the artifact is public */
1324 rid = rid_from_uuid(&xfer.aToken[1], 1, 0);
1325 }else if( g.perm.Private ){
1326 /* Client says the artifact is private and the client has
1327 ** permission to push private content. Create a new phantom
1328 ** artifact that is marked private. */
1329 rid = rid_from_uuid(&xfer.aToken[1], 1, 1);
1330 isPriv = 1;
1331 }else{
1332 /* Client says the artifact is private and the client is unable
1333 ** or unwilling to send us the artifact. If we already hold the
1334 ** artifact here on the server as a phantom, make sure that
1335 ** phantom is marked as private so that we don't keep asking about
1336 ** it in subsequent sync requests. */
1337 rid = rid_from_uuid(&xfer.aToken[1], 0, 1);
1338 isPriv = 1;
1339 }
1340 if( rid ){
1341 remote_has(rid);
1342 if( isPriv ){
1343 content_make_private(rid);
1344 }else{
1345 content_make_public(rid);
1346 }
1347 }
1348 }
1349 }else
1350
1351
@@ -1388,11 +1442,12 @@
1442 @ push %s(db_get("server-code", "x")) %s(db_get("project-code", "x"))
1443 }else
1444
1445 /* login USER NONCE SIGNATURE
1446 **
1447 ** The client has sent login credentials to the server.
1448 ** Validate the login. This has to happen before anything else.
1449 ** The client can send multiple logins. Permissions are cumulative.
1450 */
1451 if( blob_eq(&xfer.aToken[0], "login")
1452 && xfer.nToken==4
1453 ){
@@ -1410,11 +1465,11 @@
1465 }
1466 }else
1467
1468 /* reqconfig NAME
1469 **
1470 ** Client is requesting a configuration value from the server
1471 */
1472 if( blob_eq(&xfer.aToken[0], "reqconfig")
1473 && xfer.nToken==2
1474 ){
1475 if( g.perm.Read ){
@@ -1429,12 +1484,12 @@
1484 }
1485 }else
1486
1487 /* config NAME SIZE \n CONTENT
1488 **
1489 ** Client has sent a configuration value to the server.
1490 ** This is only permitted for high-privilege users.
1491 */
1492 if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3
1493 && blob_is_int(&xfer.aToken[2], &size) ){
1494 const char *zName = blob_str(&xfer.aToken[1]);
1495 Blob content;
@@ -1474,11 +1529,11 @@
1529 }else
1530
1531
1532 /* private
1533 **
1534 ** The client card indicates that the next "file" or "cfile" will contain
1535 ** private content.
1536 */
1537 if( blob_eq(&xfer.aToken[0], "private") ){
1538 if( !g.perm.Private ){
1539 server_private_xfer_not_authorized();
@@ -1495,10 +1550,12 @@
1550 ** ignored.
1551 */
1552 if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){
1553
1554 /* pragma send-private
1555 **
1556 ** The client is requesting private artifacts.
1557 **
1558 ** If the user has the "x" privilege (which must be set explicitly -
1559 ** it is not automatic with "a" or "s") then this pragma causes
1560 ** private information to be pulled in addition to public records.
1561 */
@@ -1511,22 +1568,32 @@
1568 }
1569 }
1570
1571 /* pragma send-catalog
1572 **
1573 ** The client wants to see igot cards for all known artifacts.
1574 ** This is used as part of "sync --verily" to help ensure that
1575 ** no artifacts have been missed on prior syncs.
1576 */
1577 if( blob_eq(&xfer.aToken[1], "send-catalog") ){
1578 xfer.resync = 0x7fffffff;
1579 }
1580
1581 /* pragma client-version VERSION ?DATE? ?TIME?
1582 **
1583 ** The client announces to the server what version of Fossil it
1584 ** is running. The DATE and TIME are a pure numeric ISO8601 time
1585 ** for the specific check-in of the client.
1586 */
1587 if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "client-version") ){
1588 xfer.remoteVersion = atoi(blob_str(&xfer.aToken[2]));
1589 if( xfer.nToken>=5 ){
1590 xfer.remoteDate = atoi(blob_str(&xfer.aToken[3]));
1591 xfer.remoteTime = atoi(blob_str(&xfer.aToken[4]));
1592 @ pragma server-version %d(RELEASE_VERSION_NUMBER) \
1593 @ %d(MANIFEST_NUMERIC_DATE) %d(MANIFEST_NUMERIC_TIME)
1594 }
1595 }
1596
1597 /* pragma uv-hash HASH
1598 **
1599 ** The client wants to make sure that unversioned files are all synced.
@@ -1550,11 +1617,11 @@
1617
1618 /* pragma ci-lock CHECKIN-HASH CLIENT-ID
1619 **
1620 ** The client wants to make non-branch commit against the check-in
1621 ** identified by CHECKIN-HASH. The server will remember this and
1622 ** subsequent ci-lock requests from different clients will generate
1623 ** a ci-lock-fail pragma in the reply.
1624 */
1625 if( blob_eq(&xfer.aToken[1], "ci-lock")
1626 && xfer.nToken==4
1627 && blob_is_hname(&xfer.aToken[2])
@@ -1808,11 +1875,11 @@
1875 memset(&xfer, 0, sizeof(xfer));
1876 xfer.pIn = &recv;
1877 xfer.pOut = &send;
1878 xfer.mxSend = db_get_int("max-upload", 250000);
1879 xfer.maxTime = -1;
1880 xfer.remoteVersion = RELEASE_VERSION_NUMBER;
1881 if( syncFlags & SYNC_PRIVATE ){
1882 g.perm.Private = 1;
1883 xfer.syncPrivate = 1;
1884 }
1885
@@ -1856,13 +1923,16 @@
1923 " SELECT name, 0 FROM unversioned WHERE hash IS NOT NULL;"
1924 );
1925 }
1926
1927 /*
1928 ** The request from the client always begin with a clone, pull,
1929 ** or push message.
1930 */
1931 blob_appendf(&send, "pragma client-version %d %d %d\n",
1932 RELEASE_VERSION_NUMBER, MANIFEST_NUMERIC_DATE,
1933 MANIFEST_NUMERIC_TIME);
1934 if( syncFlags & SYNC_CLONE ){
1935 blob_appendf(&send, "clone 3 %d\n", cloneSeqno);
1936 syncFlags &= ~(SYNC_PUSH|SYNC_PULL);
1937 nCardSent++;
1938 /* TBD: Request all transferable configuration values */
@@ -1898,20 +1968,19 @@
1968 "CREATE TEMP TABLE onremote(rid INTEGER PRIMARY KEY);"
1969 );
1970 manifest_crosslink_begin();
1971
1972
1973 /* Client sends the most recently received cookie back to the server.
1974 ** Let the server figure out if this is a cookie that it cares about.
1975 */
1976 zCookie = db_get("cookie", 0);
1977 if( zCookie ){
1978 blob_appendf(&send, "cookie %s\n", zCookie);
1979 }
1980
1981 /* Client sends gimme cards for phantoms
 
1982 */
1983 if( (syncFlags & SYNC_PULL)!=0
1984 || ((syncFlags & SYNC_CLONE)!=0 && cloneSeqno==1)
1985 ){
1986 request_phantoms(&xfer, mxPhantomReq);
@@ -1920,11 +1989,11 @@
1989 send_unsent(&xfer);
1990 nCardSent += send_unclustered(&xfer);
1991 if( syncFlags & SYNC_PRIVATE ) send_private(&xfer);
1992 }
1993
1994 /* Client sends configuration parameter requests. On a clone, delay sending
1995 ** this until the second cycle since the login card might fail on
1996 ** the first cycle.
1997 */
1998 if( configRcvMask && ((syncFlags & SYNC_CLONE)==0 || nCycle>0) ){
1999 const char *zName;
@@ -1937,13 +2006,13 @@
2006 }
2007 origConfigRcvMask = configRcvMask;
2008 configRcvMask = 0;
2009 }
2010
2011 /* Client sends a request to sync unversioned files.
2012 ** On a clone, delay sending this until the second cycle since
2013 ** the login card might fail on the first cycle.
2014 */
2015 if( (syncFlags & SYNC_UNVERSIONED)!=0
2016 && ((syncFlags & SYNC_CLONE)==0 || nCycle>0)
2017 && !uvHashSent
2018 ){
@@ -1950,11 +2019,12 @@
2019 blob_appendf(&send, "pragma uv-hash %s\n", unversioned_content_hash(0));
2020 nCardSent++;
2021 uvHashSent = 1;
2022 }
2023
2024 /* On a "fossil config push", the client send configuration parameters
2025 ** being pushed up to the server */
2026 if( configSendMask ){
2027 if( zOpType==0 ) zOpType = "Push";
2028 nCardSent += configure_send_group(xfer.pOut, configSendMask, 0);
2029 configSendMask = 0;
2030 }
@@ -2010,11 +2080,11 @@
2080 zCkinLock = 0;
2081 }else if( zClientId ){
2082 blob_appendf(&send, "pragma ci-unlock %s\n", zClientId);
2083 }
2084
2085 /* Append randomness to the end of the uplink message. This makes all
2086 ** messages unique so that that the login-card nonce will always
2087 ** be unique.
2088 */
2089 zRandomness = db_text(0, "SELECT hex(randomblob(20))");
2090 blob_appendf(&send, "# %s\n", zRandomness);
@@ -2052,14 +2122,17 @@
2122 nCardRcvd = 0;
2123 xfer.nFileSent = 0;
2124 xfer.nDeltaSent = 0;
2125 xfer.nGimmeSent = 0;
2126 xfer.nIGotSent = 0;
2127 xfer.nPrivIGot = 0;
2128
2129 lastPctDone = -1;
2130 blob_reset(&send);
2131 blob_appendf(&send, "pragma client-version %d %d %d\n",
2132 RELEASE_VERSION_NUMBER, MANIFEST_NUMERIC_DATE,
2133 MANIFEST_NUMERIC_TIME);
2134 rArrivalTime = db_double(0.0, "SELECT julianday('now')");
2135
2136 /* Send the send-private pragma if we are trying to sync private data */
2137 if( syncFlags & SYNC_PRIVATE ){
2138 blob_append(&send, "pragma send-private\n", -1);
@@ -2112,30 +2185,30 @@
2185 }
2186
2187 /* file HASH SIZE \n CONTENT
2188 ** file HASH DELTASRC SIZE \n CONTENT
2189 **
2190 ** Client receives a file transmitted from the server.
2191 */
2192 if( blob_eq(&xfer.aToken[0],"file") ){
2193 xfer_accept_file(&xfer, (syncFlags & SYNC_CLONE)!=0, 0, 0);
2194 nArtifactRcvd++;
2195 }else
2196
2197 /* cfile HASH USIZE CSIZE \n CONTENT
2198 ** cfile HASH DELTASRC USIZE CSIZE \n CONTENT
2199 **
2200 ** Client receives a compressed file transmitted from the server.
2201 */
2202 if( blob_eq(&xfer.aToken[0],"cfile") ){
2203 xfer_accept_compressed_file(&xfer, 0, 0);
2204 nArtifactRcvd++;
2205 }else
2206
2207 /* uvfile NAME MTIME HASH SIZE FLAGS \n CONTENT
2208 **
2209 ** Client accepts an unversioned file from the server.
2210 */
2211 if( blob_eq(&xfer.aToken[0], "uvfile") ){
2212 xfer_accept_unversioned_file(&xfer, 1);
2213 nArtifactRcvd++;
2214 nUvFileRcvd++;
@@ -2145,13 +2218,14 @@
2218 }
2219 }else
2220
2221 /* gimme HASH
2222 **
2223 ** Client receives an artifact request from the server.
2224 ** If the file is a manifest, assume that the server will also want
2225 ** to know all of the content artifacts associated with the manifest
2226 ** and send those too.
2227 */
2228 if( blob_eq(&xfer.aToken[0], "gimme")
2229 && xfer.nToken==2
2230 && blob_is_hname(&xfer.aToken[1])
2231 ){
@@ -2179,11 +2253,15 @@
2253 ){
2254 int rid;
2255 int isPriv = xfer.nToken>=3 && blob_eq(&xfer.aToken[2],"1");
2256 rid = rid_from_uuid(&xfer.aToken[1], 0, 0);
2257 if( rid>0 ){
2258 if( isPriv ){
2259 content_make_private(rid);
2260 }else{
2261 content_make_public(rid);
2262 }
2263 }else if( isPriv && !g.perm.Private ){
2264 /* ignore private files */
2265 }else if( (syncFlags & (SYNC_PULL|SYNC_CLONE))!=0 ){
2266 rid = content_new(blob_str(&xfer.aToken[1]), isPriv);
2267 if( rid ) newPhantom = 1;
@@ -2264,11 +2342,11 @@
2342 }else
2343
2344 /* push SERVERCODE PRODUCTCODE
2345 **
2346 ** Should only happen in response to a clone. This message tells
2347 ** the client what product code to use for the new database.
2348 */
2349 if( blob_eq(&xfer.aToken[0],"push")
2350 && xfer.nToken==3
2351 && (syncFlags & SYNC_CLONE)!=0
2352 && blob_is_hname(&xfer.aToken[2])
@@ -2281,11 +2359,11 @@
2359 nCardSent++;
2360 }else
2361
2362 /* config NAME SIZE \n CONTENT
2363 **
2364 ** Client receive a configuration value from the server.
2365 **
2366 ** The received configuration setting is silently ignored if it was
2367 ** not requested by a prior "reqconfig" sent from client to server.
2368 */
2369 if( blob_eq(&xfer.aToken[0],"config") && xfer.nToken==3
@@ -2303,11 +2381,11 @@
2381 }else
2382
2383
2384 /* cookie TEXT
2385 **
2386 ** The client reserves a cookie from the server. The client
2387 ** should remember this cookie and send it back to the server
2388 ** in its next query.
2389 **
2390 ** Each cookie received overwrites the prior cookie from the
2391 ** same server.
@@ -2317,12 +2395,12 @@
2395 }else
2396
2397
2398 /* private
2399 **
2400 ** The server tells the client that the next "file" or "cfile" will
2401 ** contain private content.
2402 */
2403 if( blob_eq(&xfer.aToken[0], "private") ){
2404 xfer.nextIsPrivate = 1;
2405 }else
2406
@@ -2338,11 +2416,12 @@
2416 blob_is_int(&xfer.aToken[1], &cloneSeqno);
2417 }else
2418
2419 /* message MESSAGE
2420 **
2421 ** A message is received from the server. Print it.
2422 ** Similar to "error" but does not stop processing.
2423 **
2424 ** If the "login failed" message is seen, clear the sync password prior
2425 ** to the next cycle.
2426 */
2427 if( blob_eq(&xfer.aToken[0],"message") && xfer.nToken==2 ){
@@ -2364,11 +2443,27 @@
2443 ** The server can send pragmas to try to convey meta-information to
2444 ** the client. These are informational only. Unknown pragmas are
2445 ** silently ignored.
2446 */
2447 if( blob_eq(&xfer.aToken[0], "pragma") && xfer.nToken>=2 ){
2448 /* pragma server-version VERSION ?DATE? ?TIME?
2449 **
2450 ** The servger announces to the server what version of Fossil it
2451 ** is running. The DATE and TIME are a pure numeric ISO8601 time
2452 ** for the specific check-in of the client.
2453 */
2454 if( xfer.nToken>=3 && blob_eq(&xfer.aToken[1], "server-version") ){
2455 xfer.remoteVersion = atoi(blob_str(&xfer.aToken[2]));
2456 if( xfer.nToken>=5 ){
2457 xfer.remoteDate = atoi(blob_str(&xfer.aToken[3]));
2458 xfer.remoteTime = atoi(blob_str(&xfer.aToken[4]));
2459 }
2460 }
2461
2462 /* pragma uv-pull-only
2463 **
2464 ** If the server is unwill to accept new unversioned content (because
2465 ** this client lacks the necessary permissions) then it sends a
2466 ** "uv-pull-only" pragma so that the client will know not to waste
2467 ** bandwidth trying to upload unversioned content. If the server
2468 ** does accept new unversioned content, it sends "uv-push-ok".
2469 */
@@ -2407,11 +2502,12 @@
2502 }
2503 }else
2504
2505 /* error MESSAGE
2506 **
2507 ** The server is reporting an error. The client will abandon
2508 ** the sync session.
2509 **
2510 ** Except, when cloning we will sometimes get an error on the
2511 ** first message exchange because the project-code is unknown
2512 ** and so the login card on the request was invalid. The project-code
2513 ** is returned in the reply before the error card, so second and
@@ -2491,10 +2587,11 @@
2587 ** another round
2588 */
2589 if( xfer.nFileSent+xfer.nDeltaSent>0 || uvDoPush ){
2590 go = 1;
2591 }
2592 if( xfer.nPrivIGot>0 && nCycle==1 ) go = 1;
2593
2594 /* If this is a clone, the go at least two rounds */
2595 if( (syncFlags & SYNC_CLONE)!=0 && nCycle==1 ) go = 1;
2596
2597 /* Stop the cycle if the server sends a "clone_seqno 0" card and
2598
+1 -1
--- src/zip.c
+++ src/zip.c
@@ -592,11 +592,11 @@
592592
}
593593
zip_open();
594594
for(i=3; i<g.argc; i++){
595595
blob_zero(&file);
596596
blob_read_from_file(&file, g.argv[i], eFType);
597
- zip_add_file(&sArchive, g.argv[i], &file, file_perm(0,0));
597
+ zip_add_file(&sArchive, g.argv[i], &file, file_perm(0,eFType));
598598
blob_reset(&file);
599599
}
600600
zip_close(&sArchive);
601601
blob_write_to_file(&zip, g.argv[2]);
602602
}
603603
604604
ADDED test/subdir with spaces/filename with spaces.txt
--- src/zip.c
+++ src/zip.c
@@ -592,11 +592,11 @@
592 }
593 zip_open();
594 for(i=3; i<g.argc; i++){
595 blob_zero(&file);
596 blob_read_from_file(&file, g.argv[i], eFType);
597 zip_add_file(&sArchive, g.argv[i], &file, file_perm(0,0));
598 blob_reset(&file);
599 }
600 zip_close(&sArchive);
601 blob_write_to_file(&zip, g.argv[2]);
602 }
603
604 DDED test/subdir with spaces/filename with spaces.txt
--- src/zip.c
+++ src/zip.c
@@ -592,11 +592,11 @@
592 }
593 zip_open();
594 for(i=3; i<g.argc; i++){
595 blob_zero(&file);
596 blob_read_from_file(&file, g.argv[i], eFType);
597 zip_add_file(&sArchive, g.argv[i], &file, file_perm(0,eFType));
598 blob_reset(&file);
599 }
600 zip_close(&sArchive);
601 blob_write_to_file(&zip, g.argv[2]);
602 }
603
604 DDED test/subdir with spaces/filename with spaces.txt
--- a/test/subdir with spaces/filename with spaces.txt
+++ b/test/subdir with spaces/filename with spaces.txt
@@ -0,0 +1,2 @@
1
+This file has a name that contains spaces. It is used to help verify
2
+that fossil can handle filenames that contain spaces.
--- a/test/subdir with spaces/filename with spaces.txt
+++ b/test/subdir with spaces/filename with spaces.txt
@@ -0,0 +1,2 @@
 
 
--- a/test/subdir with spaces/filename with spaces.txt
+++ b/test/subdir with spaces/filename with spaces.txt
@@ -0,0 +1,2 @@
1 This file has a name that contains spaces. It is used to help verify
2 that fossil can handle filenames that contain spaces.
+1 -1
--- test/tester.tcl
+++ test/tester.tcl
@@ -391,11 +391,11 @@
391391
if {[info exists ::env(FOSSIL_TEST_DANGEROUS_IGNORE_OPEN_CHECKOUT)] && \
392392
$::env(FOSSIL_TEST_DANGEROUS_IGNORE_OPEN_CHECKOUT) eq "YES_DO_IT"} {
393393
return
394394
}
395395
catch {exec $::fossilexe info} res
396
- if {![regexp {use --repository} $res]} {
396
+ if {[regexp {local-root:} $res]} {
397397
set projectName <unknown>
398398
set localRoot <unknown>
399399
regexp -line -- {^project-name: (.*)$} $res dummy projectName
400400
set projectName [string trim $projectName]
401401
regexp -line -- {^local-root: (.*)$} $res dummy localRoot
402402
403403
ADDED tools/fossil-diff-log
--- test/tester.tcl
+++ test/tester.tcl
@@ -391,11 +391,11 @@
391 if {[info exists ::env(FOSSIL_TEST_DANGEROUS_IGNORE_OPEN_CHECKOUT)] && \
392 $::env(FOSSIL_TEST_DANGEROUS_IGNORE_OPEN_CHECKOUT) eq "YES_DO_IT"} {
393 return
394 }
395 catch {exec $::fossilexe info} res
396 if {![regexp {use --repository} $res]} {
397 set projectName <unknown>
398 set localRoot <unknown>
399 regexp -line -- {^project-name: (.*)$} $res dummy projectName
400 set projectName [string trim $projectName]
401 regexp -line -- {^local-root: (.*)$} $res dummy localRoot
402
403 DDED tools/fossil-diff-log
--- test/tester.tcl
+++ test/tester.tcl
@@ -391,11 +391,11 @@
391 if {[info exists ::env(FOSSIL_TEST_DANGEROUS_IGNORE_OPEN_CHECKOUT)] && \
392 $::env(FOSSIL_TEST_DANGEROUS_IGNORE_OPEN_CHECKOUT) eq "YES_DO_IT"} {
393 return
394 }
395 catch {exec $::fossilexe info} res
396 if {[regexp {local-root:} $res]} {
397 set projectName <unknown>
398 set localRoot <unknown>
399 regexp -line -- {^project-name: (.*)$} $res dummy projectName
400 set projectName [string trim $projectName]
401 regexp -line -- {^local-root: (.*)$} $res dummy localRoot
402
403 DDED tools/fossil-diff-log

No diff available

No diff available

No diff available

--- a/tools/fossil-diff-log
+++ b/tools/fossil-diff-log
@@ -0,0 +1,72 @@
1
+#!/usr/bin/env perl
2
+# Fossil emulation of the "git log --patch / -p" feature: emit a stream
3
+# of diffs from one version to the next for each file named on the
4
+# command line.
5
+#
6
+# LIMITATIONS: It does not assume "all files" if you give no args, and
7
+# it cannot take a directory to mean "all files under this parent".
8
+#
9
+# PREREQUISITES: This script needs several CPAN modules to run properly.
10
+# There are multiple methods to install them:
11
+#
12
+# sudo dnf install perl-File-Which perl-IO-Interactive
13
+# sudo apt install libfile-which-perl libio-interactive-perl
14
+# sudo cpanm File::Which IO::Interactive
15
+# ...etc...
16
+
17
+use strict;
18
+use warnings;
19
+
20
+use Carp;
21
+use File::Which;
22
+use IO::Interactive qw(is_interactive);
23
+
24
+die "usage: $0 <files...>\n\n" unless @ARGV;
25
+
26
+my $out;
27
+if (is_interactive()) {
28
+ my $pager = $ENV{PAGER} || which('less') || which('more');
29
+ open $out, '|-', $pager or croak "Cannot pipe to $pager: $!";
30
+}
31
+else {
32
+ $out = *STDOUT;
33
+}
34
+
35
+open my $bcmd, '-|', 'fossil branch current'
36
+ or die "Cannot get branch: $!\n";
37
+my $cbranch = <$bcmd>;
38
+chomp $cbranch;
39
+close $bcmd;
40
+
41
+for my $file (@ARGV) {
42
+ my $lastckid;
43
+ open my $finfo, '-|', "fossil finfo --brief --limit 0 '$file'"
44
+ or die "Failed to get file info: $!\n";
45
+ my @filines = <$finfo>;
46
+ close $finfo;
47
+
48
+ for my $line (@filines) {
49
+ my ($currckid, $date, $user, $branch, @cwords) = split ' ', $line;
50
+ next unless $branch eq $cbranch;
51
+ if (defined $lastckid and defined $branch) {
52
+ my $comment = join ' ', @cwords;
53
+ open my $diff, '-|', 'fossil', 'diff', $file,
54
+ '--from', $currckid,
55
+ '--to', $lastckid,
56
+ or die "Failed to diff $currckid -> $lastckid: $!\n";
57
+ my @dl = <$diff>;
58
+ close $diff;
59
+ my $patch = join '', @dl;
60
+
61
+ print $out <<"OUT"
62
+Checkin ID $currckid to $branch by $user on $date
63
+Comment: $comment
64
+
65
+$patch
66
+
67
+OUT
68
+ }
69
+
70
+ $lastckid = $currckid;
71
+ }
72
+}
--- a/tools/fossil-diff-log
+++ b/tools/fossil-diff-log
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/tools/fossil-diff-log
+++ b/tools/fossil-diff-log
@@ -0,0 +1,72 @@
1 #!/usr/bin/env perl
2 # Fossil emulation of the "git log --patch / -p" feature: emit a stream
3 # of diffs from one version to the next for each file named on the
4 # command line.
5 #
6 # LIMITATIONS: It does not assume "all files" if you give no args, and
7 # it cannot take a directory to mean "all files under this parent".
8 #
9 # PREREQUISITES: This script needs several CPAN modules to run properly.
10 # There are multiple methods to install them:
11 #
12 # sudo dnf install perl-File-Which perl-IO-Interactive
13 # sudo apt install libfile-which-perl libio-interactive-perl
14 # sudo cpanm File::Which IO::Interactive
15 # ...etc...
16
17 use strict;
18 use warnings;
19
20 use Carp;
21 use File::Which;
22 use IO::Interactive qw(is_interactive);
23
24 die "usage: $0 <files...>\n\n" unless @ARGV;
25
26 my $out;
27 if (is_interactive()) {
28 my $pager = $ENV{PAGER} || which('less') || which('more');
29 open $out, '|-', $pager or croak "Cannot pipe to $pager: $!";
30 }
31 else {
32 $out = *STDOUT;
33 }
34
35 open my $bcmd, '-|', 'fossil branch current'
36 or die "Cannot get branch: $!\n";
37 my $cbranch = <$bcmd>;
38 chomp $cbranch;
39 close $bcmd;
40
41 for my $file (@ARGV) {
42 my $lastckid;
43 open my $finfo, '-|', "fossil finfo --brief --limit 0 '$file'"
44 or die "Failed to get file info: $!\n";
45 my @filines = <$finfo>;
46 close $finfo;
47
48 for my $line (@filines) {
49 my ($currckid, $date, $user, $branch, @cwords) = split ' ', $line;
50 next unless $branch eq $cbranch;
51 if (defined $lastckid and defined $branch) {
52 my $comment = join ' ', @cwords;
53 open my $diff, '-|', 'fossil', 'diff', $file,
54 '--from', $currckid,
55 '--to', $lastckid,
56 or die "Failed to diff $currckid -> $lastckid: $!\n";
57 my @dl = <$diff>;
58 close $diff;
59 my $patch = join '', @dl;
60
61 print $out <<"OUT"
62 Checkin ID $currckid to $branch by $user on $date
63 Comment: $comment
64
65 $patch
66
67 OUT
68 }
69
70 $lastckid = $currckid;
71 }
72 }

No diff available

No diff available

+16 -4
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -28,13 +28,13 @@
2828
2929
SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0
3030
3131
SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
3232
33
-SRC = add_.c alerts_.c allrepo_.c attach_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c
33
+SRC = add_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c
3434
35
-OBJ = $(OBJDIR)\add$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
35
+OBJ = $(OBJDIR)\add$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
3636
3737
3838
RC=$(DMDIR)\bin\rcc
3939
RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
4040
@@ -49,11 +49,11 @@
4949
5050
$(OBJDIR)\fossil.res: $B\win\fossil.rc
5151
$(RC) $(RCFLAGS) -o$@ $**
5252
5353
$(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
54
- +echo add alerts allrepo attach backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file finfo foci forum fshell fusefs fuzz glob graph gzip hname http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
54
+ +echo add alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file finfo foci forum fshell fusefs fuzz glob graph gzip hname http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
5555
+echo fossil >> $@
5656
+echo fossil >> $@
5757
+echo $(LIBS) >> $@
5858
+echo. >> $@
5959
+echo fossil >> $@
@@ -152,10 +152,16 @@
152152
$(OBJDIR)\attach$O : attach_.c attach.h
153153
$(TCC) -o$@ -c attach_.c
154154
155155
attach_.c : $(SRCDIR)\attach.c
156156
+translate$E $** > $@
157
+
158
+$(OBJDIR)\backlink$O : backlink_.c backlink.h
159
+ $(TCC) -o$@ -c backlink_.c
160
+
161
+backlink_.c : $(SRCDIR)\backlink.c
162
+ +translate$E $** > $@
157163
158164
$(OBJDIR)\backoffice$O : backoffice_.c backoffice.h
159165
$(TCC) -o$@ -c backoffice_.c
160166
161167
backoffice_.c : $(SRCDIR)\backoffice.c
@@ -824,10 +830,16 @@
824830
$(OBJDIR)\tar$O : tar_.c tar.h
825831
$(TCC) -o$@ -c tar_.c
826832
827833
tar_.c : $(SRCDIR)\tar.c
828834
+translate$E $** > $@
835
+
836
+$(OBJDIR)\terminal$O : terminal_.c terminal.h
837
+ $(TCC) -o$@ -c terminal_.c
838
+
839
+terminal_.c : $(SRCDIR)\terminal.c
840
+ +translate$E $** > $@
829841
830842
$(OBJDIR)\th_main$O : th_main_.c th_main.h
831843
$(TCC) -o$@ -c th_main_.c
832844
833845
th_main_.c : $(SRCDIR)\th_main.c
@@ -964,7 +976,7 @@
964976
965977
zip_.c : $(SRCDIR)\zip.c
966978
+translate$E $** > $@
967979
968980
headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h
969
- +makeheaders$E add_.c:add.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
981
+ +makeheaders$E add_.c:add.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
970982
@copy /Y nul: headers
971983
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -28,13 +28,13 @@
28
29 SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0
30
31 SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
32
33 SRC = add_.c alerts_.c allrepo_.c attach_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c
34
35 OBJ = $(OBJDIR)\add$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
36
37
38 RC=$(DMDIR)\bin\rcc
39 RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
40
@@ -49,11 +49,11 @@
49
50 $(OBJDIR)\fossil.res: $B\win\fossil.rc
51 $(RC) $(RCFLAGS) -o$@ $**
52
53 $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
54 +echo add alerts allrepo attach backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file finfo foci forum fshell fusefs fuzz glob graph gzip hname http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
55 +echo fossil >> $@
56 +echo fossil >> $@
57 +echo $(LIBS) >> $@
58 +echo. >> $@
59 +echo fossil >> $@
@@ -152,10 +152,16 @@
152 $(OBJDIR)\attach$O : attach_.c attach.h
153 $(TCC) -o$@ -c attach_.c
154
155 attach_.c : $(SRCDIR)\attach.c
156 +translate$E $** > $@
 
 
 
 
 
 
157
158 $(OBJDIR)\backoffice$O : backoffice_.c backoffice.h
159 $(TCC) -o$@ -c backoffice_.c
160
161 backoffice_.c : $(SRCDIR)\backoffice.c
@@ -824,10 +830,16 @@
824 $(OBJDIR)\tar$O : tar_.c tar.h
825 $(TCC) -o$@ -c tar_.c
826
827 tar_.c : $(SRCDIR)\tar.c
828 +translate$E $** > $@
 
 
 
 
 
 
829
830 $(OBJDIR)\th_main$O : th_main_.c th_main.h
831 $(TCC) -o$@ -c th_main_.c
832
833 th_main_.c : $(SRCDIR)\th_main.c
@@ -964,7 +976,7 @@
964
965 zip_.c : $(SRCDIR)\zip.c
966 +translate$E $** > $@
967
968 headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h
969 +makeheaders$E add_.c:add.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
970 @copy /Y nul: headers
971
--- win/Makefile.dmc
+++ win/Makefile.dmc
@@ -28,13 +28,13 @@
28
29 SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0
30
31 SHELL_OPTIONS = -DNDEBUG=1 -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 -DSQLITE_DEFAULT_WAL_SYNCHRONOUS=1 -DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_OMIT_DECLTYPE -DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_GET_TABLE -DSQLITE_OMIT_PROGRESS_CALLBACK -DSQLITE_OMIT_SHARED_CACHE -DSQLITE_OMIT_LOAD_EXTENSION -DSQLITE_MAX_EXPR_DEPTH=0 -DSQLITE_USE_ALLOCA -DSQLITE_ENABLE_LOCKING_STYLE=0 -DSQLITE_DEFAULT_FILE_FORMAT=4 -DSQLITE_ENABLE_EXPLAIN_COMMENTS -DSQLITE_ENABLE_FTS4 -DSQLITE_ENABLE_DBSTAT_VTAB -DSQLITE_ENABLE_JSON1 -DSQLITE_ENABLE_FTS5 -DSQLITE_ENABLE_STMTVTAB -DSQLITE_HAVE_ZLIB -DSQLITE_INTROSPECTION_PRAGMAS -DSQLITE_ENABLE_DBPAGE_VTAB -DSQLITE_TRUSTED_SCHEMA=0 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen
32
33 SRC = add_.c alerts_.c allrepo_.c attach_.c backlink_.c backoffice_.c bag_.c bisect_.c blob_.c branch_.c browse_.c builtin_.c bundle_.c cache_.c capabilities_.c captcha_.c cgi_.c checkin_.c checkout_.c clearsign_.c clone_.c comformat_.c configure_.c content_.c cookies_.c db_.c delta_.c deltacmd_.c deltafunc_.c descendants_.c diff_.c diffcmd_.c dispatch_.c doc_.c encode_.c etag_.c event_.c export_.c extcgi_.c file_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.c fuzz_.c glob_.c graph_.c gzip_.c hname_.c http_.c http_socket_.c http_ssl_.c http_transport_.c import_.c info_.c json_.c json_artifact_.c json_branch_.c json_config_.c json_diff_.c json_dir_.c json_finfo_.c json_login_.c json_query_.c json_report_.c json_status_.c json_tag_.c json_timeline_.c json_user_.c json_wiki_.c leaf_.c loadctrl_.c login_.c lookslike_.c main_.c manifest_.c markdown_.c markdown_html_.c md5_.c merge_.c merge3_.c moderate_.c name_.c path_.c piechart_.c pivot_.c popen_.c pqueue_.c printf_.c publish_.c purge_.c rebuild_.c regexp_.c repolist_.c report_.c rss_.c schema_.c search_.c security_audit_.c setup_.c setupuser_.c sha1_.c sha1hard_.c sha3_.c shun_.c sitemap_.c skins_.c smtp_.c sqlcmd_.c stash_.c stat_.c statrep_.c style_.c sync_.c tag_.c tar_.c terminal_.c th_main_.c timeline_.c tkt_.c tktsetup_.c undo_.c unicode_.c unversioned_.c update_.c url_.c user_.c utf8_.c util_.c verify_.c vfile_.c webmail_.c wiki_.c wikiformat_.c winfile_.c winhttp_.c wysiwyg_.c xfer_.c xfersetup_.c zip_.c
34
35 OBJ = $(OBJDIR)\add$O $(OBJDIR)\alerts$O $(OBJDIR)\allrepo$O $(OBJDIR)\attach$O $(OBJDIR)\backlink$O $(OBJDIR)\backoffice$O $(OBJDIR)\bag$O $(OBJDIR)\bisect$O $(OBJDIR)\blob$O $(OBJDIR)\branch$O $(OBJDIR)\browse$O $(OBJDIR)\builtin$O $(OBJDIR)\bundle$O $(OBJDIR)\cache$O $(OBJDIR)\capabilities$O $(OBJDIR)\captcha$O $(OBJDIR)\cgi$O $(OBJDIR)\checkin$O $(OBJDIR)\checkout$O $(OBJDIR)\clearsign$O $(OBJDIR)\clone$O $(OBJDIR)\comformat$O $(OBJDIR)\configure$O $(OBJDIR)\content$O $(OBJDIR)\cookies$O $(OBJDIR)\db$O $(OBJDIR)\delta$O $(OBJDIR)\deltacmd$O $(OBJDIR)\deltafunc$O $(OBJDIR)\descendants$O $(OBJDIR)\diff$O $(OBJDIR)\diffcmd$O $(OBJDIR)\dispatch$O $(OBJDIR)\doc$O $(OBJDIR)\encode$O $(OBJDIR)\etag$O $(OBJDIR)\event$O $(OBJDIR)\export$O $(OBJDIR)\extcgi$O $(OBJDIR)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$O $(OBJDIR)\fuzz$O $(OBJDIR)\glob$O $(OBJDIR)\graph$O $(OBJDIR)\gzip$O $(OBJDIR)\hname$O $(OBJDIR)\http$O $(OBJDIR)\http_socket$O $(OBJDIR)\http_ssl$O $(OBJDIR)\http_transport$O $(OBJDIR)\import$O $(OBJDIR)\info$O $(OBJDIR)\json$O $(OBJDIR)\json_artifact$O $(OBJDIR)\json_branch$O $(OBJDIR)\json_config$O $(OBJDIR)\json_diff$O $(OBJDIR)\json_dir$O $(OBJDIR)\json_finfo$O $(OBJDIR)\json_login$O $(OBJDIR)\json_query$O $(OBJDIR)\json_report$O $(OBJDIR)\json_status$O $(OBJDIR)\json_tag$O $(OBJDIR)\json_timeline$O $(OBJDIR)\json_user$O $(OBJDIR)\json_wiki$O $(OBJDIR)\leaf$O $(OBJDIR)\loadctrl$O $(OBJDIR)\login$O $(OBJDIR)\lookslike$O $(OBJDIR)\main$O $(OBJDIR)\manifest$O $(OBJDIR)\markdown$O $(OBJDIR)\markdown_html$O $(OBJDIR)\md5$O $(OBJDIR)\merge$O $(OBJDIR)\merge3$O $(OBJDIR)\moderate$O $(OBJDIR)\name$O $(OBJDIR)\path$O $(OBJDIR)\piechart$O $(OBJDIR)\pivot$O $(OBJDIR)\popen$O $(OBJDIR)\pqueue$O $(OBJDIR)\printf$O $(OBJDIR)\publish$O $(OBJDIR)\purge$O $(OBJDIR)\rebuild$O $(OBJDIR)\regexp$O $(OBJDIR)\repolist$O $(OBJDIR)\report$O $(OBJDIR)\rss$O $(OBJDIR)\schema$O $(OBJDIR)\search$O $(OBJDIR)\security_audit$O $(OBJDIR)\setup$O $(OBJDIR)\setupuser$O $(OBJDIR)\sha1$O $(OBJDIR)\sha1hard$O $(OBJDIR)\sha3$O $(OBJDIR)\shun$O $(OBJDIR)\sitemap$O $(OBJDIR)\skins$O $(OBJDIR)\smtp$O $(OBJDIR)\sqlcmd$O $(OBJDIR)\stash$O $(OBJDIR)\stat$O $(OBJDIR)\statrep$O $(OBJDIR)\style$O $(OBJDIR)\sync$O $(OBJDIR)\tag$O $(OBJDIR)\tar$O $(OBJDIR)\terminal$O $(OBJDIR)\th_main$O $(OBJDIR)\timeline$O $(OBJDIR)\tkt$O $(OBJDIR)\tktsetup$O $(OBJDIR)\undo$O $(OBJDIR)\unicode$O $(OBJDIR)\unversioned$O $(OBJDIR)\update$O $(OBJDIR)\url$O $(OBJDIR)\user$O $(OBJDIR)\utf8$O $(OBJDIR)\util$O $(OBJDIR)\verify$O $(OBJDIR)\vfile$O $(OBJDIR)\webmail$O $(OBJDIR)\wiki$O $(OBJDIR)\wikiformat$O $(OBJDIR)\winfile$O $(OBJDIR)\winhttp$O $(OBJDIR)\wysiwyg$O $(OBJDIR)\xfer$O $(OBJDIR)\xfersetup$O $(OBJDIR)\zip$O $(OBJDIR)\shell$O $(OBJDIR)\sqlite3$O $(OBJDIR)\th$O $(OBJDIR)\th_lang$O
36
37
38 RC=$(DMDIR)\bin\rcc
39 RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__
40
@@ -49,11 +49,11 @@
49
50 $(OBJDIR)\fossil.res: $B\win\fossil.rc
51 $(RC) $(RCFLAGS) -o$@ $**
52
53 $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res
54 +echo add alerts allrepo attach backlink backoffice bag bisect blob branch browse builtin bundle cache capabilities captcha cgi checkin checkout clearsign clone comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag event export extcgi file finfo foci forum fshell fusefs fuzz glob graph gzip hname http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar terminal th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@
55 +echo fossil >> $@
56 +echo fossil >> $@
57 +echo $(LIBS) >> $@
58 +echo. >> $@
59 +echo fossil >> $@
@@ -152,10 +152,16 @@
152 $(OBJDIR)\attach$O : attach_.c attach.h
153 $(TCC) -o$@ -c attach_.c
154
155 attach_.c : $(SRCDIR)\attach.c
156 +translate$E $** > $@
157
158 $(OBJDIR)\backlink$O : backlink_.c backlink.h
159 $(TCC) -o$@ -c backlink_.c
160
161 backlink_.c : $(SRCDIR)\backlink.c
162 +translate$E $** > $@
163
164 $(OBJDIR)\backoffice$O : backoffice_.c backoffice.h
165 $(TCC) -o$@ -c backoffice_.c
166
167 backoffice_.c : $(SRCDIR)\backoffice.c
@@ -824,10 +830,16 @@
830 $(OBJDIR)\tar$O : tar_.c tar.h
831 $(TCC) -o$@ -c tar_.c
832
833 tar_.c : $(SRCDIR)\tar.c
834 +translate$E $** > $@
835
836 $(OBJDIR)\terminal$O : terminal_.c terminal.h
837 $(TCC) -o$@ -c terminal_.c
838
839 terminal_.c : $(SRCDIR)\terminal.c
840 +translate$E $** > $@
841
842 $(OBJDIR)\th_main$O : th_main_.c th_main.h
843 $(TCC) -o$@ -c th_main_.c
844
845 th_main_.c : $(SRCDIR)\th_main.c
@@ -964,7 +976,7 @@
976
977 zip_.c : $(SRCDIR)\zip.c
978 +translate$E $** > $@
979
980 headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h
981 +makeheaders$E add_.c:add.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h backlink_.c:backlink.h backoffice_.c:backoffice.h bag_.c:bag.h bisect_.c:bisect.h blob_.c:blob.h branch_.c:branch.h browse_.c:browse.h builtin_.c:builtin.h bundle_.c:bundle.h cache_.c:cache.h capabilities_.c:capabilities.h captcha_.c:captcha.h cgi_.c:cgi.h checkin_.c:checkin.h checkout_.c:checkout.h clearsign_.c:clearsign.h clone_.c:clone.h comformat_.c:comformat.h configure_.c:configure.h content_.c:content.h cookies_.c:cookies.h db_.c:db.h delta_.c:delta.h deltacmd_.c:deltacmd.h deltafunc_.c:deltafunc.h descendants_.c:descendants.h diff_.c:diff.h diffcmd_.c:diffcmd.h dispatch_.c:dispatch.h doc_.c:doc.h encode_.c:encode.h etag_.c:etag.h event_.c:event.h export_.c:export.h extcgi_.c:extcgi.h file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.h fuzz_.c:fuzz.h glob_.c:glob.h graph_.c:graph.h gzip_.c:gzip.h hname_.c:hname.h http_.c:http.h http_socket_.c:http_socket.h http_ssl_.c:http_ssl.h http_transport_.c:http_transport.h import_.c:import.h info_.c:info.h json_.c:json.h json_artifact_.c:json_artifact.h json_branch_.c:json_branch.h json_config_.c:json_config.h json_diff_.c:json_diff.h json_dir_.c:json_dir.h json_finfo_.c:json_finfo.h json_login_.c:json_login.h json_query_.c:json_query.h json_report_.c:json_report.h json_status_.c:json_status.h json_tag_.c:json_tag.h json_timeline_.c:json_timeline.h json_user_.c:json_user.h json_wiki_.c:json_wiki.h leaf_.c:leaf.h loadctrl_.c:loadctrl.h login_.c:login.h lookslike_.c:lookslike.h main_.c:main.h manifest_.c:manifest.h markdown_.c:markdown.h markdown_html_.c:markdown_html.h md5_.c:md5.h merge_.c:merge.h merge3_.c:merge3.h moderate_.c:moderate.h name_.c:name.h path_.c:path.h piechart_.c:piechart.h pivot_.c:pivot.h popen_.c:popen.h pqueue_.c:pqueue.h printf_.c:printf.h publish_.c:publish.h purge_.c:purge.h rebuild_.c:rebuild.h regexp_.c:regexp.h repolist_.c:repolist.h report_.c:report.h rss_.c:rss.h schema_.c:schema.h search_.c:search.h security_audit_.c:security_audit.h setup_.c:setup.h setupuser_.c:setupuser.h sha1_.c:sha1.h sha1hard_.c:sha1hard.h sha3_.c:sha3.h shun_.c:shun.h sitemap_.c:sitemap.h skins_.c:skins.h smtp_.c:smtp.h sqlcmd_.c:sqlcmd.h stash_.c:stash.h stat_.c:stat.h statrep_.c:statrep.h style_.c:style.h sync_.c:sync.h tag_.c:tag.h tar_.c:tar.h terminal_.c:terminal.h th_main_.c:th_main.h timeline_.c:timeline.h tkt_.c:tkt.h tktsetup_.c:tktsetup.h undo_.c:undo.h unicode_.c:unicode.h unversioned_.c:unversioned.h update_.c:update.h url_.c:url.h user_.c:user.h utf8_.c:utf8.h util_.c:util.h verify_.c:verify.h vfile_.c:vfile.h webmail_.c:webmail.h wiki_.c:wiki.h wikiformat_.c:wikiformat.h winfile_.c:winfile.h winhttp_.c:winhttp.h wysiwyg_.c:wysiwyg.h xfer_.c:xfer.h xfersetup_.c:xfersetup.h zip_.c:zip.h $(SRCDIR)\sqlite3.h $(SRCDIR)\th.h VERSION.h $(SRCDIR)\cson_amalgamation.h
982 @copy /Y nul: headers
983
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -174,11 +174,11 @@
174174
#### The directories where the OpenSSL include and library files are located.
175175
# The recommended usage here is to use the Sysinternals junction tool
176176
# to create a hard link between an "openssl-1.x" sub-directory of the
177177
# Fossil source code directory and the target OpenSSL source directory.
178178
#
179
-OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1f
179
+OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1g
180180
OPENSSLINCDIR = $(OPENSSLDIR)/include
181181
OPENSSLLIBDIR = $(OPENSSLDIR)
182182
183183
#### Either the directory where the Tcl library is installed or the Tcl
184184
# source code directory resides (depending on the value of the macro
@@ -440,10 +440,11 @@
440440
SRC = \
441441
$(SRCDIR)/add.c \
442442
$(SRCDIR)/alerts.c \
443443
$(SRCDIR)/allrepo.c \
444444
$(SRCDIR)/attach.c \
445
+ $(SRCDIR)/backlink.c \
445446
$(SRCDIR)/backoffice.c \
446447
$(SRCDIR)/bag.c \
447448
$(SRCDIR)/bisect.c \
448449
$(SRCDIR)/blob.c \
449450
$(SRCDIR)/branch.c \
@@ -552,10 +553,11 @@
552553
$(SRCDIR)/statrep.c \
553554
$(SRCDIR)/style.c \
554555
$(SRCDIR)/sync.c \
555556
$(SRCDIR)/tag.c \
556557
$(SRCDIR)/tar.c \
558
+ $(SRCDIR)/terminal.c \
557559
$(SRCDIR)/th_main.c \
558560
$(SRCDIR)/timeline.c \
559561
$(SRCDIR)/tkt.c \
560562
$(SRCDIR)/tktsetup.c \
561563
$(SRCDIR)/undo.c \
@@ -672,10 +674,11 @@
672674
TRANS_SRC = \
673675
$(OBJDIR)/add_.c \
674676
$(OBJDIR)/alerts_.c \
675677
$(OBJDIR)/allrepo_.c \
676678
$(OBJDIR)/attach_.c \
679
+ $(OBJDIR)/backlink_.c \
677680
$(OBJDIR)/backoffice_.c \
678681
$(OBJDIR)/bag_.c \
679682
$(OBJDIR)/bisect_.c \
680683
$(OBJDIR)/blob_.c \
681684
$(OBJDIR)/branch_.c \
@@ -784,10 +787,11 @@
784787
$(OBJDIR)/statrep_.c \
785788
$(OBJDIR)/style_.c \
786789
$(OBJDIR)/sync_.c \
787790
$(OBJDIR)/tag_.c \
788791
$(OBJDIR)/tar_.c \
792
+ $(OBJDIR)/terminal_.c \
789793
$(OBJDIR)/th_main_.c \
790794
$(OBJDIR)/timeline_.c \
791795
$(OBJDIR)/tkt_.c \
792796
$(OBJDIR)/tktsetup_.c \
793797
$(OBJDIR)/undo_.c \
@@ -813,10 +817,11 @@
813817
OBJ = \
814818
$(OBJDIR)/add.o \
815819
$(OBJDIR)/alerts.o \
816820
$(OBJDIR)/allrepo.o \
817821
$(OBJDIR)/attach.o \
822
+ $(OBJDIR)/backlink.o \
818823
$(OBJDIR)/backoffice.o \
819824
$(OBJDIR)/bag.o \
820825
$(OBJDIR)/bisect.o \
821826
$(OBJDIR)/blob.o \
822827
$(OBJDIR)/branch.o \
@@ -925,10 +930,11 @@
925930
$(OBJDIR)/statrep.o \
926931
$(OBJDIR)/style.o \
927932
$(OBJDIR)/sync.o \
928933
$(OBJDIR)/tag.o \
929934
$(OBJDIR)/tar.o \
935
+ $(OBJDIR)/terminal.o \
930936
$(OBJDIR)/th_main.o \
931937
$(OBJDIR)/timeline.o \
932938
$(OBJDIR)/tkt.o \
933939
$(OBJDIR)/tktsetup.o \
934940
$(OBJDIR)/undo.o \
@@ -1174,10 +1180,11 @@
11741180
$(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h
11751181
$(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \
11761182
$(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \
11771183
$(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \
11781184
$(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \
1185
+ $(OBJDIR)/backlink_.c:$(OBJDIR)/backlink.h \
11791186
$(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \
11801187
$(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \
11811188
$(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \
11821189
$(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \
11831190
$(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \
@@ -1286,10 +1293,11 @@
12861293
$(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \
12871294
$(OBJDIR)/style_.c:$(OBJDIR)/style.h \
12881295
$(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \
12891296
$(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \
12901297
$(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \
1298
+ $(OBJDIR)/terminal_.c:$(OBJDIR)/terminal.h \
12911299
$(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \
12921300
$(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
12931301
$(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
12941302
$(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
12951303
$(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
@@ -1349,10 +1357,18 @@
13491357
13501358
$(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h
13511359
$(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c
13521360
13531361
$(OBJDIR)/attach.h: $(OBJDIR)/headers
1362
+
1363
+$(OBJDIR)/backlink_.c: $(SRCDIR)/backlink.c $(TRANSLATE)
1364
+ $(TRANSLATE) $(SRCDIR)/backlink.c >$@
1365
+
1366
+$(OBJDIR)/backlink.o: $(OBJDIR)/backlink_.c $(OBJDIR)/backlink.h $(SRCDIR)/config.h
1367
+ $(XTCC) -o $(OBJDIR)/backlink.o -c $(OBJDIR)/backlink_.c
1368
+
1369
+$(OBJDIR)/backlink.h: $(OBJDIR)/headers
13541370
13551371
$(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(TRANSLATE)
13561372
$(TRANSLATE) $(SRCDIR)/backoffice.c >$@
13571373
13581374
$(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h
@@ -2245,10 +2261,18 @@
22452261
22462262
$(OBJDIR)/tar.o: $(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h
22472263
$(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c
22482264
22492265
$(OBJDIR)/tar.h: $(OBJDIR)/headers
2266
+
2267
+$(OBJDIR)/terminal_.c: $(SRCDIR)/terminal.c $(TRANSLATE)
2268
+ $(TRANSLATE) $(SRCDIR)/terminal.c >$@
2269
+
2270
+$(OBJDIR)/terminal.o: $(OBJDIR)/terminal_.c $(OBJDIR)/terminal.h $(SRCDIR)/config.h
2271
+ $(XTCC) -o $(OBJDIR)/terminal.o -c $(OBJDIR)/terminal_.c
2272
+
2273
+$(OBJDIR)/terminal.h: $(OBJDIR)/headers
22502274
22512275
$(OBJDIR)/th_main_.c: $(SRCDIR)/th_main.c $(TRANSLATE)
22522276
$(TRANSLATE) $(SRCDIR)/th_main.c >$@
22532277
22542278
$(OBJDIR)/th_main.o: $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h
22552279
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -174,11 +174,11 @@
174 #### The directories where the OpenSSL include and library files are located.
175 # The recommended usage here is to use the Sysinternals junction tool
176 # to create a hard link between an "openssl-1.x" sub-directory of the
177 # Fossil source code directory and the target OpenSSL source directory.
178 #
179 OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1f
180 OPENSSLINCDIR = $(OPENSSLDIR)/include
181 OPENSSLLIBDIR = $(OPENSSLDIR)
182
183 #### Either the directory where the Tcl library is installed or the Tcl
184 # source code directory resides (depending on the value of the macro
@@ -440,10 +440,11 @@
440 SRC = \
441 $(SRCDIR)/add.c \
442 $(SRCDIR)/alerts.c \
443 $(SRCDIR)/allrepo.c \
444 $(SRCDIR)/attach.c \
 
445 $(SRCDIR)/backoffice.c \
446 $(SRCDIR)/bag.c \
447 $(SRCDIR)/bisect.c \
448 $(SRCDIR)/blob.c \
449 $(SRCDIR)/branch.c \
@@ -552,10 +553,11 @@
552 $(SRCDIR)/statrep.c \
553 $(SRCDIR)/style.c \
554 $(SRCDIR)/sync.c \
555 $(SRCDIR)/tag.c \
556 $(SRCDIR)/tar.c \
 
557 $(SRCDIR)/th_main.c \
558 $(SRCDIR)/timeline.c \
559 $(SRCDIR)/tkt.c \
560 $(SRCDIR)/tktsetup.c \
561 $(SRCDIR)/undo.c \
@@ -672,10 +674,11 @@
672 TRANS_SRC = \
673 $(OBJDIR)/add_.c \
674 $(OBJDIR)/alerts_.c \
675 $(OBJDIR)/allrepo_.c \
676 $(OBJDIR)/attach_.c \
 
677 $(OBJDIR)/backoffice_.c \
678 $(OBJDIR)/bag_.c \
679 $(OBJDIR)/bisect_.c \
680 $(OBJDIR)/blob_.c \
681 $(OBJDIR)/branch_.c \
@@ -784,10 +787,11 @@
784 $(OBJDIR)/statrep_.c \
785 $(OBJDIR)/style_.c \
786 $(OBJDIR)/sync_.c \
787 $(OBJDIR)/tag_.c \
788 $(OBJDIR)/tar_.c \
 
789 $(OBJDIR)/th_main_.c \
790 $(OBJDIR)/timeline_.c \
791 $(OBJDIR)/tkt_.c \
792 $(OBJDIR)/tktsetup_.c \
793 $(OBJDIR)/undo_.c \
@@ -813,10 +817,11 @@
813 OBJ = \
814 $(OBJDIR)/add.o \
815 $(OBJDIR)/alerts.o \
816 $(OBJDIR)/allrepo.o \
817 $(OBJDIR)/attach.o \
 
818 $(OBJDIR)/backoffice.o \
819 $(OBJDIR)/bag.o \
820 $(OBJDIR)/bisect.o \
821 $(OBJDIR)/blob.o \
822 $(OBJDIR)/branch.o \
@@ -925,10 +930,11 @@
925 $(OBJDIR)/statrep.o \
926 $(OBJDIR)/style.o \
927 $(OBJDIR)/sync.o \
928 $(OBJDIR)/tag.o \
929 $(OBJDIR)/tar.o \
 
930 $(OBJDIR)/th_main.o \
931 $(OBJDIR)/timeline.o \
932 $(OBJDIR)/tkt.o \
933 $(OBJDIR)/tktsetup.o \
934 $(OBJDIR)/undo.o \
@@ -1174,10 +1180,11 @@
1174 $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h
1175 $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \
1176 $(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \
1177 $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \
1178 $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \
 
1179 $(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \
1180 $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \
1181 $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \
1182 $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \
1183 $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \
@@ -1286,10 +1293,11 @@
1286 $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \
1287 $(OBJDIR)/style_.c:$(OBJDIR)/style.h \
1288 $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \
1289 $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \
1290 $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \
 
1291 $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \
1292 $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
1293 $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
1294 $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
1295 $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
@@ -1349,10 +1357,18 @@
1349
1350 $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h
1351 $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c
1352
1353 $(OBJDIR)/attach.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
 
1354
1355 $(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(TRANSLATE)
1356 $(TRANSLATE) $(SRCDIR)/backoffice.c >$@
1357
1358 $(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h
@@ -2245,10 +2261,18 @@
2245
2246 $(OBJDIR)/tar.o: $(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h
2247 $(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c
2248
2249 $(OBJDIR)/tar.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
 
2250
2251 $(OBJDIR)/th_main_.c: $(SRCDIR)/th_main.c $(TRANSLATE)
2252 $(TRANSLATE) $(SRCDIR)/th_main.c >$@
2253
2254 $(OBJDIR)/th_main.o: $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h
2255
--- win/Makefile.mingw
+++ win/Makefile.mingw
@@ -174,11 +174,11 @@
174 #### The directories where the OpenSSL include and library files are located.
175 # The recommended usage here is to use the Sysinternals junction tool
176 # to create a hard link between an "openssl-1.x" sub-directory of the
177 # Fossil source code directory and the target OpenSSL source directory.
178 #
179 OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1g
180 OPENSSLINCDIR = $(OPENSSLDIR)/include
181 OPENSSLLIBDIR = $(OPENSSLDIR)
182
183 #### Either the directory where the Tcl library is installed or the Tcl
184 # source code directory resides (depending on the value of the macro
@@ -440,10 +440,11 @@
440 SRC = \
441 $(SRCDIR)/add.c \
442 $(SRCDIR)/alerts.c \
443 $(SRCDIR)/allrepo.c \
444 $(SRCDIR)/attach.c \
445 $(SRCDIR)/backlink.c \
446 $(SRCDIR)/backoffice.c \
447 $(SRCDIR)/bag.c \
448 $(SRCDIR)/bisect.c \
449 $(SRCDIR)/blob.c \
450 $(SRCDIR)/branch.c \
@@ -552,10 +553,11 @@
553 $(SRCDIR)/statrep.c \
554 $(SRCDIR)/style.c \
555 $(SRCDIR)/sync.c \
556 $(SRCDIR)/tag.c \
557 $(SRCDIR)/tar.c \
558 $(SRCDIR)/terminal.c \
559 $(SRCDIR)/th_main.c \
560 $(SRCDIR)/timeline.c \
561 $(SRCDIR)/tkt.c \
562 $(SRCDIR)/tktsetup.c \
563 $(SRCDIR)/undo.c \
@@ -672,10 +674,11 @@
674 TRANS_SRC = \
675 $(OBJDIR)/add_.c \
676 $(OBJDIR)/alerts_.c \
677 $(OBJDIR)/allrepo_.c \
678 $(OBJDIR)/attach_.c \
679 $(OBJDIR)/backlink_.c \
680 $(OBJDIR)/backoffice_.c \
681 $(OBJDIR)/bag_.c \
682 $(OBJDIR)/bisect_.c \
683 $(OBJDIR)/blob_.c \
684 $(OBJDIR)/branch_.c \
@@ -784,10 +787,11 @@
787 $(OBJDIR)/statrep_.c \
788 $(OBJDIR)/style_.c \
789 $(OBJDIR)/sync_.c \
790 $(OBJDIR)/tag_.c \
791 $(OBJDIR)/tar_.c \
792 $(OBJDIR)/terminal_.c \
793 $(OBJDIR)/th_main_.c \
794 $(OBJDIR)/timeline_.c \
795 $(OBJDIR)/tkt_.c \
796 $(OBJDIR)/tktsetup_.c \
797 $(OBJDIR)/undo_.c \
@@ -813,10 +817,11 @@
817 OBJ = \
818 $(OBJDIR)/add.o \
819 $(OBJDIR)/alerts.o \
820 $(OBJDIR)/allrepo.o \
821 $(OBJDIR)/attach.o \
822 $(OBJDIR)/backlink.o \
823 $(OBJDIR)/backoffice.o \
824 $(OBJDIR)/bag.o \
825 $(OBJDIR)/bisect.o \
826 $(OBJDIR)/blob.o \
827 $(OBJDIR)/branch.o \
@@ -925,10 +930,11 @@
930 $(OBJDIR)/statrep.o \
931 $(OBJDIR)/style.o \
932 $(OBJDIR)/sync.o \
933 $(OBJDIR)/tag.o \
934 $(OBJDIR)/tar.o \
935 $(OBJDIR)/terminal.o \
936 $(OBJDIR)/th_main.o \
937 $(OBJDIR)/timeline.o \
938 $(OBJDIR)/tkt.o \
939 $(OBJDIR)/tktsetup.o \
940 $(OBJDIR)/undo.o \
@@ -1174,10 +1180,11 @@
1180 $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h
1181 $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \
1182 $(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \
1183 $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \
1184 $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \
1185 $(OBJDIR)/backlink_.c:$(OBJDIR)/backlink.h \
1186 $(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \
1187 $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \
1188 $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \
1189 $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \
1190 $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \
@@ -1286,10 +1293,11 @@
1293 $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \
1294 $(OBJDIR)/style_.c:$(OBJDIR)/style.h \
1295 $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \
1296 $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \
1297 $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \
1298 $(OBJDIR)/terminal_.c:$(OBJDIR)/terminal.h \
1299 $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \
1300 $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
1301 $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
1302 $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
1303 $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
@@ -1349,10 +1357,18 @@
1357
1358 $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h
1359 $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c
1360
1361 $(OBJDIR)/attach.h: $(OBJDIR)/headers
1362
1363 $(OBJDIR)/backlink_.c: $(SRCDIR)/backlink.c $(TRANSLATE)
1364 $(TRANSLATE) $(SRCDIR)/backlink.c >$@
1365
1366 $(OBJDIR)/backlink.o: $(OBJDIR)/backlink_.c $(OBJDIR)/backlink.h $(SRCDIR)/config.h
1367 $(XTCC) -o $(OBJDIR)/backlink.o -c $(OBJDIR)/backlink_.c
1368
1369 $(OBJDIR)/backlink.h: $(OBJDIR)/headers
1370
1371 $(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(TRANSLATE)
1372 $(TRANSLATE) $(SRCDIR)/backoffice.c >$@
1373
1374 $(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h
@@ -2245,10 +2261,18 @@
2261
2262 $(OBJDIR)/tar.o: $(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h
2263 $(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c
2264
2265 $(OBJDIR)/tar.h: $(OBJDIR)/headers
2266
2267 $(OBJDIR)/terminal_.c: $(SRCDIR)/terminal.c $(TRANSLATE)
2268 $(TRANSLATE) $(SRCDIR)/terminal.c >$@
2269
2270 $(OBJDIR)/terminal.o: $(OBJDIR)/terminal_.c $(OBJDIR)/terminal.h $(SRCDIR)/config.h
2271 $(XTCC) -o $(OBJDIR)/terminal.o -c $(OBJDIR)/terminal_.c
2272
2273 $(OBJDIR)/terminal.h: $(OBJDIR)/headers
2274
2275 $(OBJDIR)/th_main_.c: $(SRCDIR)/th_main.c $(TRANSLATE)
2276 $(TRANSLATE) $(SRCDIR)/th_main.c >$@
2277
2278 $(OBJDIR)/th_main.o: $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h
2279
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -174,11 +174,11 @@
174174
#### The directories where the OpenSSL include and library files are located.
175175
# The recommended usage here is to use the Sysinternals junction tool
176176
# to create a hard link between an "openssl-1.x" sub-directory of the
177177
# Fossil source code directory and the target OpenSSL source directory.
178178
#
179
-OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1f
179
+OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1g
180180
OPENSSLINCDIR = $(OPENSSLDIR)/include
181181
OPENSSLLIBDIR = $(OPENSSLDIR)
182182
183183
#### Either the directory where the Tcl library is installed or the Tcl
184184
# source code directory resides (depending on the value of the macro
@@ -440,10 +440,11 @@
440440
SRC = \
441441
$(SRCDIR)/add.c \
442442
$(SRCDIR)/alerts.c \
443443
$(SRCDIR)/allrepo.c \
444444
$(SRCDIR)/attach.c \
445
+ $(SRCDIR)/backlink.c \
445446
$(SRCDIR)/backoffice.c \
446447
$(SRCDIR)/bag.c \
447448
$(SRCDIR)/bisect.c \
448449
$(SRCDIR)/blob.c \
449450
$(SRCDIR)/branch.c \
@@ -552,10 +553,11 @@
552553
$(SRCDIR)/statrep.c \
553554
$(SRCDIR)/style.c \
554555
$(SRCDIR)/sync.c \
555556
$(SRCDIR)/tag.c \
556557
$(SRCDIR)/tar.c \
558
+ $(SRCDIR)/terminal.c \
557559
$(SRCDIR)/th_main.c \
558560
$(SRCDIR)/timeline.c \
559561
$(SRCDIR)/tkt.c \
560562
$(SRCDIR)/tktsetup.c \
561563
$(SRCDIR)/undo.c \
@@ -633,10 +635,11 @@
633635
$(SRCDIR)/../skins/rounded1/header.txt \
634636
$(SRCDIR)/../skins/xekri/css.txt \
635637
$(SRCDIR)/../skins/xekri/details.txt \
636638
$(SRCDIR)/../skins/xekri/footer.txt \
637639
$(SRCDIR)/../skins/xekri/header.txt \
640
+ $(SRCDIR)/accordion.js \
638641
$(SRCDIR)/ci_edit.js \
639642
$(SRCDIR)/copybtn.js \
640643
$(SRCDIR)/diff.tcl \
641644
$(SRCDIR)/forum.js \
642645
$(SRCDIR)/graph.js \
@@ -671,10 +674,11 @@
671674
TRANS_SRC = \
672675
$(OBJDIR)/add_.c \
673676
$(OBJDIR)/alerts_.c \
674677
$(OBJDIR)/allrepo_.c \
675678
$(OBJDIR)/attach_.c \
679
+ $(OBJDIR)/backlink_.c \
676680
$(OBJDIR)/backoffice_.c \
677681
$(OBJDIR)/bag_.c \
678682
$(OBJDIR)/bisect_.c \
679683
$(OBJDIR)/blob_.c \
680684
$(OBJDIR)/branch_.c \
@@ -783,10 +787,11 @@
783787
$(OBJDIR)/statrep_.c \
784788
$(OBJDIR)/style_.c \
785789
$(OBJDIR)/sync_.c \
786790
$(OBJDIR)/tag_.c \
787791
$(OBJDIR)/tar_.c \
792
+ $(OBJDIR)/terminal_.c \
788793
$(OBJDIR)/th_main_.c \
789794
$(OBJDIR)/timeline_.c \
790795
$(OBJDIR)/tkt_.c \
791796
$(OBJDIR)/tktsetup_.c \
792797
$(OBJDIR)/undo_.c \
@@ -812,10 +817,11 @@
812817
OBJ = \
813818
$(OBJDIR)/add.o \
814819
$(OBJDIR)/alerts.o \
815820
$(OBJDIR)/allrepo.o \
816821
$(OBJDIR)/attach.o \
822
+ $(OBJDIR)/backlink.o \
817823
$(OBJDIR)/backoffice.o \
818824
$(OBJDIR)/bag.o \
819825
$(OBJDIR)/bisect.o \
820826
$(OBJDIR)/blob.o \
821827
$(OBJDIR)/branch.o \
@@ -924,10 +930,11 @@
924930
$(OBJDIR)/statrep.o \
925931
$(OBJDIR)/style.o \
926932
$(OBJDIR)/sync.o \
927933
$(OBJDIR)/tag.o \
928934
$(OBJDIR)/tar.o \
935
+ $(OBJDIR)/terminal.o \
929936
$(OBJDIR)/th_main.o \
930937
$(OBJDIR)/timeline.o \
931938
$(OBJDIR)/tkt.o \
932939
$(OBJDIR)/tktsetup.o \
933940
$(OBJDIR)/undo.o \
@@ -1173,10 +1180,11 @@
11731180
$(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h
11741181
$(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \
11751182
$(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \
11761183
$(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \
11771184
$(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \
1185
+ $(OBJDIR)/backlink_.c:$(OBJDIR)/backlink.h \
11781186
$(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \
11791187
$(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \
11801188
$(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \
11811189
$(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \
11821190
$(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \
@@ -1285,10 +1293,11 @@
12851293
$(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \
12861294
$(OBJDIR)/style_.c:$(OBJDIR)/style.h \
12871295
$(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \
12881296
$(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \
12891297
$(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \
1298
+ $(OBJDIR)/terminal_.c:$(OBJDIR)/terminal.h \
12901299
$(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \
12911300
$(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
12921301
$(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
12931302
$(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
12941303
$(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
@@ -1348,10 +1357,18 @@
13481357
13491358
$(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h
13501359
$(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c
13511360
13521361
$(OBJDIR)/attach.h: $(OBJDIR)/headers
1362
+
1363
+$(OBJDIR)/backlink_.c: $(SRCDIR)/backlink.c $(TRANSLATE)
1364
+ $(TRANSLATE) $(SRCDIR)/backlink.c >$@
1365
+
1366
+$(OBJDIR)/backlink.o: $(OBJDIR)/backlink_.c $(OBJDIR)/backlink.h $(SRCDIR)/config.h
1367
+ $(XTCC) -o $(OBJDIR)/backlink.o -c $(OBJDIR)/backlink_.c
1368
+
1369
+$(OBJDIR)/backlink.h: $(OBJDIR)/headers
13531370
13541371
$(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(TRANSLATE)
13551372
$(TRANSLATE) $(SRCDIR)/backoffice.c >$@
13561373
13571374
$(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h
@@ -2244,10 +2261,18 @@
22442261
22452262
$(OBJDIR)/tar.o: $(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h
22462263
$(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c
22472264
22482265
$(OBJDIR)/tar.h: $(OBJDIR)/headers
2266
+
2267
+$(OBJDIR)/terminal_.c: $(SRCDIR)/terminal.c $(TRANSLATE)
2268
+ $(TRANSLATE) $(SRCDIR)/terminal.c >$@
2269
+
2270
+$(OBJDIR)/terminal.o: $(OBJDIR)/terminal_.c $(OBJDIR)/terminal.h $(SRCDIR)/config.h
2271
+ $(XTCC) -o $(OBJDIR)/terminal.o -c $(OBJDIR)/terminal_.c
2272
+
2273
+$(OBJDIR)/terminal.h: $(OBJDIR)/headers
22492274
22502275
$(OBJDIR)/th_main_.c: $(SRCDIR)/th_main.c $(TRANSLATE)
22512276
$(TRANSLATE) $(SRCDIR)/th_main.c >$@
22522277
22532278
$(OBJDIR)/th_main.o: $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h
22542279
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -174,11 +174,11 @@
174 #### The directories where the OpenSSL include and library files are located.
175 # The recommended usage here is to use the Sysinternals junction tool
176 # to create a hard link between an "openssl-1.x" sub-directory of the
177 # Fossil source code directory and the target OpenSSL source directory.
178 #
179 OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1f
180 OPENSSLINCDIR = $(OPENSSLDIR)/include
181 OPENSSLLIBDIR = $(OPENSSLDIR)
182
183 #### Either the directory where the Tcl library is installed or the Tcl
184 # source code directory resides (depending on the value of the macro
@@ -440,10 +440,11 @@
440 SRC = \
441 $(SRCDIR)/add.c \
442 $(SRCDIR)/alerts.c \
443 $(SRCDIR)/allrepo.c \
444 $(SRCDIR)/attach.c \
 
445 $(SRCDIR)/backoffice.c \
446 $(SRCDIR)/bag.c \
447 $(SRCDIR)/bisect.c \
448 $(SRCDIR)/blob.c \
449 $(SRCDIR)/branch.c \
@@ -552,10 +553,11 @@
552 $(SRCDIR)/statrep.c \
553 $(SRCDIR)/style.c \
554 $(SRCDIR)/sync.c \
555 $(SRCDIR)/tag.c \
556 $(SRCDIR)/tar.c \
 
557 $(SRCDIR)/th_main.c \
558 $(SRCDIR)/timeline.c \
559 $(SRCDIR)/tkt.c \
560 $(SRCDIR)/tktsetup.c \
561 $(SRCDIR)/undo.c \
@@ -633,10 +635,11 @@
633 $(SRCDIR)/../skins/rounded1/header.txt \
634 $(SRCDIR)/../skins/xekri/css.txt \
635 $(SRCDIR)/../skins/xekri/details.txt \
636 $(SRCDIR)/../skins/xekri/footer.txt \
637 $(SRCDIR)/../skins/xekri/header.txt \
 
638 $(SRCDIR)/ci_edit.js \
639 $(SRCDIR)/copybtn.js \
640 $(SRCDIR)/diff.tcl \
641 $(SRCDIR)/forum.js \
642 $(SRCDIR)/graph.js \
@@ -671,10 +674,11 @@
671 TRANS_SRC = \
672 $(OBJDIR)/add_.c \
673 $(OBJDIR)/alerts_.c \
674 $(OBJDIR)/allrepo_.c \
675 $(OBJDIR)/attach_.c \
 
676 $(OBJDIR)/backoffice_.c \
677 $(OBJDIR)/bag_.c \
678 $(OBJDIR)/bisect_.c \
679 $(OBJDIR)/blob_.c \
680 $(OBJDIR)/branch_.c \
@@ -783,10 +787,11 @@
783 $(OBJDIR)/statrep_.c \
784 $(OBJDIR)/style_.c \
785 $(OBJDIR)/sync_.c \
786 $(OBJDIR)/tag_.c \
787 $(OBJDIR)/tar_.c \
 
788 $(OBJDIR)/th_main_.c \
789 $(OBJDIR)/timeline_.c \
790 $(OBJDIR)/tkt_.c \
791 $(OBJDIR)/tktsetup_.c \
792 $(OBJDIR)/undo_.c \
@@ -812,10 +817,11 @@
812 OBJ = \
813 $(OBJDIR)/add.o \
814 $(OBJDIR)/alerts.o \
815 $(OBJDIR)/allrepo.o \
816 $(OBJDIR)/attach.o \
 
817 $(OBJDIR)/backoffice.o \
818 $(OBJDIR)/bag.o \
819 $(OBJDIR)/bisect.o \
820 $(OBJDIR)/blob.o \
821 $(OBJDIR)/branch.o \
@@ -924,10 +930,11 @@
924 $(OBJDIR)/statrep.o \
925 $(OBJDIR)/style.o \
926 $(OBJDIR)/sync.o \
927 $(OBJDIR)/tag.o \
928 $(OBJDIR)/tar.o \
 
929 $(OBJDIR)/th_main.o \
930 $(OBJDIR)/timeline.o \
931 $(OBJDIR)/tkt.o \
932 $(OBJDIR)/tktsetup.o \
933 $(OBJDIR)/undo.o \
@@ -1173,10 +1180,11 @@
1173 $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h
1174 $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \
1175 $(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \
1176 $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \
1177 $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \
 
1178 $(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \
1179 $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \
1180 $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \
1181 $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \
1182 $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \
@@ -1285,10 +1293,11 @@
1285 $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \
1286 $(OBJDIR)/style_.c:$(OBJDIR)/style.h \
1287 $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \
1288 $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \
1289 $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \
 
1290 $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \
1291 $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
1292 $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
1293 $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
1294 $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
@@ -1348,10 +1357,18 @@
1348
1349 $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h
1350 $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c
1351
1352 $(OBJDIR)/attach.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
 
1353
1354 $(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(TRANSLATE)
1355 $(TRANSLATE) $(SRCDIR)/backoffice.c >$@
1356
1357 $(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h
@@ -2244,10 +2261,18 @@
2244
2245 $(OBJDIR)/tar.o: $(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h
2246 $(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c
2247
2248 $(OBJDIR)/tar.h: $(OBJDIR)/headers
 
 
 
 
 
 
 
 
2249
2250 $(OBJDIR)/th_main_.c: $(SRCDIR)/th_main.c $(TRANSLATE)
2251 $(TRANSLATE) $(SRCDIR)/th_main.c >$@
2252
2253 $(OBJDIR)/th_main.o: $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h
2254
--- win/Makefile.mingw.mistachkin
+++ win/Makefile.mingw.mistachkin
@@ -174,11 +174,11 @@
174 #### The directories where the OpenSSL include and library files are located.
175 # The recommended usage here is to use the Sysinternals junction tool
176 # to create a hard link between an "openssl-1.x" sub-directory of the
177 # Fossil source code directory and the target OpenSSL source directory.
178 #
179 OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.1.1g
180 OPENSSLINCDIR = $(OPENSSLDIR)/include
181 OPENSSLLIBDIR = $(OPENSSLDIR)
182
183 #### Either the directory where the Tcl library is installed or the Tcl
184 # source code directory resides (depending on the value of the macro
@@ -440,10 +440,11 @@
440 SRC = \
441 $(SRCDIR)/add.c \
442 $(SRCDIR)/alerts.c \
443 $(SRCDIR)/allrepo.c \
444 $(SRCDIR)/attach.c \
445 $(SRCDIR)/backlink.c \
446 $(SRCDIR)/backoffice.c \
447 $(SRCDIR)/bag.c \
448 $(SRCDIR)/bisect.c \
449 $(SRCDIR)/blob.c \
450 $(SRCDIR)/branch.c \
@@ -552,10 +553,11 @@
553 $(SRCDIR)/statrep.c \
554 $(SRCDIR)/style.c \
555 $(SRCDIR)/sync.c \
556 $(SRCDIR)/tag.c \
557 $(SRCDIR)/tar.c \
558 $(SRCDIR)/terminal.c \
559 $(SRCDIR)/th_main.c \
560 $(SRCDIR)/timeline.c \
561 $(SRCDIR)/tkt.c \
562 $(SRCDIR)/tktsetup.c \
563 $(SRCDIR)/undo.c \
@@ -633,10 +635,11 @@
635 $(SRCDIR)/../skins/rounded1/header.txt \
636 $(SRCDIR)/../skins/xekri/css.txt \
637 $(SRCDIR)/../skins/xekri/details.txt \
638 $(SRCDIR)/../skins/xekri/footer.txt \
639 $(SRCDIR)/../skins/xekri/header.txt \
640 $(SRCDIR)/accordion.js \
641 $(SRCDIR)/ci_edit.js \
642 $(SRCDIR)/copybtn.js \
643 $(SRCDIR)/diff.tcl \
644 $(SRCDIR)/forum.js \
645 $(SRCDIR)/graph.js \
@@ -671,10 +674,11 @@
674 TRANS_SRC = \
675 $(OBJDIR)/add_.c \
676 $(OBJDIR)/alerts_.c \
677 $(OBJDIR)/allrepo_.c \
678 $(OBJDIR)/attach_.c \
679 $(OBJDIR)/backlink_.c \
680 $(OBJDIR)/backoffice_.c \
681 $(OBJDIR)/bag_.c \
682 $(OBJDIR)/bisect_.c \
683 $(OBJDIR)/blob_.c \
684 $(OBJDIR)/branch_.c \
@@ -783,10 +787,11 @@
787 $(OBJDIR)/statrep_.c \
788 $(OBJDIR)/style_.c \
789 $(OBJDIR)/sync_.c \
790 $(OBJDIR)/tag_.c \
791 $(OBJDIR)/tar_.c \
792 $(OBJDIR)/terminal_.c \
793 $(OBJDIR)/th_main_.c \
794 $(OBJDIR)/timeline_.c \
795 $(OBJDIR)/tkt_.c \
796 $(OBJDIR)/tktsetup_.c \
797 $(OBJDIR)/undo_.c \
@@ -812,10 +817,11 @@
817 OBJ = \
818 $(OBJDIR)/add.o \
819 $(OBJDIR)/alerts.o \
820 $(OBJDIR)/allrepo.o \
821 $(OBJDIR)/attach.o \
822 $(OBJDIR)/backlink.o \
823 $(OBJDIR)/backoffice.o \
824 $(OBJDIR)/bag.o \
825 $(OBJDIR)/bisect.o \
826 $(OBJDIR)/blob.o \
827 $(OBJDIR)/branch.o \
@@ -924,10 +930,11 @@
930 $(OBJDIR)/statrep.o \
931 $(OBJDIR)/style.o \
932 $(OBJDIR)/sync.o \
933 $(OBJDIR)/tag.o \
934 $(OBJDIR)/tar.o \
935 $(OBJDIR)/terminal.o \
936 $(OBJDIR)/th_main.o \
937 $(OBJDIR)/timeline.o \
938 $(OBJDIR)/tkt.o \
939 $(OBJDIR)/tktsetup.o \
940 $(OBJDIR)/undo.o \
@@ -1173,10 +1180,11 @@
1180 $(OBJDIR)/headers: $(OBJDIR)/page_index.h $(OBJDIR)/builtin_data.h $(OBJDIR)/default_css.h $(MAKEHEADERS) $(OBJDIR)/VERSION.h
1181 $(MAKEHEADERS) $(OBJDIR)/add_.c:$(OBJDIR)/add.h \
1182 $(OBJDIR)/alerts_.c:$(OBJDIR)/alerts.h \
1183 $(OBJDIR)/allrepo_.c:$(OBJDIR)/allrepo.h \
1184 $(OBJDIR)/attach_.c:$(OBJDIR)/attach.h \
1185 $(OBJDIR)/backlink_.c:$(OBJDIR)/backlink.h \
1186 $(OBJDIR)/backoffice_.c:$(OBJDIR)/backoffice.h \
1187 $(OBJDIR)/bag_.c:$(OBJDIR)/bag.h \
1188 $(OBJDIR)/bisect_.c:$(OBJDIR)/bisect.h \
1189 $(OBJDIR)/blob_.c:$(OBJDIR)/blob.h \
1190 $(OBJDIR)/branch_.c:$(OBJDIR)/branch.h \
@@ -1285,10 +1293,11 @@
1293 $(OBJDIR)/statrep_.c:$(OBJDIR)/statrep.h \
1294 $(OBJDIR)/style_.c:$(OBJDIR)/style.h \
1295 $(OBJDIR)/sync_.c:$(OBJDIR)/sync.h \
1296 $(OBJDIR)/tag_.c:$(OBJDIR)/tag.h \
1297 $(OBJDIR)/tar_.c:$(OBJDIR)/tar.h \
1298 $(OBJDIR)/terminal_.c:$(OBJDIR)/terminal.h \
1299 $(OBJDIR)/th_main_.c:$(OBJDIR)/th_main.h \
1300 $(OBJDIR)/timeline_.c:$(OBJDIR)/timeline.h \
1301 $(OBJDIR)/tkt_.c:$(OBJDIR)/tkt.h \
1302 $(OBJDIR)/tktsetup_.c:$(OBJDIR)/tktsetup.h \
1303 $(OBJDIR)/undo_.c:$(OBJDIR)/undo.h \
@@ -1348,10 +1357,18 @@
1357
1358 $(OBJDIR)/attach.o: $(OBJDIR)/attach_.c $(OBJDIR)/attach.h $(SRCDIR)/config.h
1359 $(XTCC) -o $(OBJDIR)/attach.o -c $(OBJDIR)/attach_.c
1360
1361 $(OBJDIR)/attach.h: $(OBJDIR)/headers
1362
1363 $(OBJDIR)/backlink_.c: $(SRCDIR)/backlink.c $(TRANSLATE)
1364 $(TRANSLATE) $(SRCDIR)/backlink.c >$@
1365
1366 $(OBJDIR)/backlink.o: $(OBJDIR)/backlink_.c $(OBJDIR)/backlink.h $(SRCDIR)/config.h
1367 $(XTCC) -o $(OBJDIR)/backlink.o -c $(OBJDIR)/backlink_.c
1368
1369 $(OBJDIR)/backlink.h: $(OBJDIR)/headers
1370
1371 $(OBJDIR)/backoffice_.c: $(SRCDIR)/backoffice.c $(TRANSLATE)
1372 $(TRANSLATE) $(SRCDIR)/backoffice.c >$@
1373
1374 $(OBJDIR)/backoffice.o: $(OBJDIR)/backoffice_.c $(OBJDIR)/backoffice.h $(SRCDIR)/config.h
@@ -2244,10 +2261,18 @@
2261
2262 $(OBJDIR)/tar.o: $(OBJDIR)/tar_.c $(OBJDIR)/tar.h $(SRCDIR)/config.h
2263 $(XTCC) -o $(OBJDIR)/tar.o -c $(OBJDIR)/tar_.c
2264
2265 $(OBJDIR)/tar.h: $(OBJDIR)/headers
2266
2267 $(OBJDIR)/terminal_.c: $(SRCDIR)/terminal.c $(TRANSLATE)
2268 $(TRANSLATE) $(SRCDIR)/terminal.c >$@
2269
2270 $(OBJDIR)/terminal.o: $(OBJDIR)/terminal_.c $(OBJDIR)/terminal.h $(SRCDIR)/config.h
2271 $(XTCC) -o $(OBJDIR)/terminal.o -c $(OBJDIR)/terminal_.c
2272
2273 $(OBJDIR)/terminal.h: $(OBJDIR)/headers
2274
2275 $(OBJDIR)/th_main_.c: $(SRCDIR)/th_main.c $(TRANSLATE)
2276 $(TRANSLATE) $(SRCDIR)/th_main.c >$@
2277
2278 $(OBJDIR)/th_main.o: $(OBJDIR)/th_main_.c $(OBJDIR)/th_main.h $(SRCDIR)/config.h
2279
+21 -1
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -98,11 +98,11 @@
9898
!ifndef USE_SEE
9999
USE_SEE = 0
100100
!endif
101101
102102
!if $(FOSSIL_ENABLE_SSL)!=0
103
-SSLDIR = $(B)\compat\openssl-1.1.1f
103
+SSLDIR = $(B)\compat\openssl-1.1.1g
104104
SSLINCDIR = $(SSLDIR)\include
105105
!if $(FOSSIL_DYNAMIC_BUILD)!=0
106106
SSLLIBDIR = $(SSLDIR)
107107
!else
108108
SSLLIBDIR = $(SSLDIR)
@@ -348,10 +348,11 @@
348348
349349
SRC = add_.c \
350350
alerts_.c \
351351
allrepo_.c \
352352
attach_.c \
353
+ backlink_.c \
353354
backoffice_.c \
354355
bag_.c \
355356
bisect_.c \
356357
blob_.c \
357358
branch_.c \
@@ -460,10 +461,11 @@
460461
statrep_.c \
461462
style_.c \
462463
sync_.c \
463464
tag_.c \
464465
tar_.c \
466
+ terminal_.c \
465467
th_main_.c \
466468
timeline_.c \
467469
tkt_.c \
468470
tktsetup_.c \
469471
undo_.c \
@@ -578,10 +580,11 @@
578580
579581
OBJ = $(OX)\add$O \
580582
$(OX)\alerts$O \
581583
$(OX)\allrepo$O \
582584
$(OX)\attach$O \
585
+ $(OX)\backlink$O \
583586
$(OX)\backoffice$O \
584587
$(OX)\bag$O \
585588
$(OX)\bisect$O \
586589
$(OX)\blob$O \
587590
$(OX)\branch$O \
@@ -693,10 +696,11 @@
693696
$(OX)\statrep$O \
694697
$(OX)\style$O \
695698
$(OX)\sync$O \
696699
$(OX)\tag$O \
697700
$(OX)\tar$O \
701
+ $(OX)\terminal$O \
698702
$(OX)\th$O \
699703
$(OX)\th_lang$O \
700704
$(OX)\th_main$O \
701705
$(OX)\th_tcl$O \
702706
$(OX)\timeline$O \
@@ -781,10 +785,11 @@
781785
$(OX)\linkopts: $B\win\Makefile.msc
782786
echo $(OX)\add.obj > $@
783787
echo $(OX)\alerts.obj >> $@
784788
echo $(OX)\allrepo.obj >> $@
785789
echo $(OX)\attach.obj >> $@
790
+ echo $(OX)\backlink.obj >> $@
786791
echo $(OX)\backoffice.obj >> $@
787792
echo $(OX)\bag.obj >> $@
788793
echo $(OX)\bisect.obj >> $@
789794
echo $(OX)\blob.obj >> $@
790795
echo $(OX)\branch.obj >> $@
@@ -896,10 +901,11 @@
896901
echo $(OX)\statrep.obj >> $@
897902
echo $(OX)\style.obj >> $@
898903
echo $(OX)\sync.obj >> $@
899904
echo $(OX)\tag.obj >> $@
900905
echo $(OX)\tar.obj >> $@
906
+ echo $(OX)\terminal.obj >> $@
901907
echo $(OX)\th.obj >> $@
902908
echo $(OX)\th_lang.obj >> $@
903909
echo $(OX)\th_main.obj >> $@
904910
echo $(OX)\th_tcl.obj >> $@
905911
echo $(OX)\timeline.obj >> $@
@@ -1063,10 +1069,16 @@
10631069
$(OX)\attach$O : attach_.c attach.h
10641070
$(TCC) /Fo$@ -c attach_.c
10651071
10661072
attach_.c : $(SRCDIR)\attach.c
10671073
translate$E $** > $@
1074
+
1075
+$(OX)\backlink$O : backlink_.c backlink.h
1076
+ $(TCC) /Fo$@ -c backlink_.c
1077
+
1078
+backlink_.c : $(SRCDIR)\backlink.c
1079
+ translate$E $** > $@
10681080
10691081
$(OX)\backoffice$O : backoffice_.c backoffice.h
10701082
$(TCC) /Fo$@ -c backoffice_.c
10711083
10721084
backoffice_.c : $(SRCDIR)\backoffice.c
@@ -1735,10 +1747,16 @@
17351747
$(OX)\tar$O : tar_.c tar.h
17361748
$(TCC) /Fo$@ -c tar_.c
17371749
17381750
tar_.c : $(SRCDIR)\tar.c
17391751
translate$E $** > $@
1752
+
1753
+$(OX)\terminal$O : terminal_.c terminal.h
1754
+ $(TCC) /Fo$@ -c terminal_.c
1755
+
1756
+terminal_.c : $(SRCDIR)\terminal.c
1757
+ translate$E $** > $@
17401758
17411759
$(OX)\th_main$O : th_main_.c th_main.h
17421760
$(TCC) /Fo$@ -c th_main_.c
17431761
17441762
th_main_.c : $(SRCDIR)\th_main.c
@@ -1882,10 +1900,11 @@
18821900
headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h
18831901
makeheaders$E add_.c:add.h \
18841902
alerts_.c:alerts.h \
18851903
allrepo_.c:allrepo.h \
18861904
attach_.c:attach.h \
1905
+ backlink_.c:backlink.h \
18871906
backoffice_.c:backoffice.h \
18881907
bag_.c:bag.h \
18891908
bisect_.c:bisect.h \
18901909
blob_.c:blob.h \
18911910
branch_.c:branch.h \
@@ -1994,10 +2013,11 @@
19942013
statrep_.c:statrep.h \
19952014
style_.c:style.h \
19962015
sync_.c:sync.h \
19972016
tag_.c:tag.h \
19982017
tar_.c:tar.h \
2018
+ terminal_.c:terminal.h \
19992019
th_main_.c:th_main.h \
20002020
timeline_.c:timeline.h \
20012021
tkt_.c:tkt.h \
20022022
tktsetup_.c:tktsetup.h \
20032023
undo_.c:undo.h \
20042024
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -98,11 +98,11 @@
98 !ifndef USE_SEE
99 USE_SEE = 0
100 !endif
101
102 !if $(FOSSIL_ENABLE_SSL)!=0
103 SSLDIR = $(B)\compat\openssl-1.1.1f
104 SSLINCDIR = $(SSLDIR)\include
105 !if $(FOSSIL_DYNAMIC_BUILD)!=0
106 SSLLIBDIR = $(SSLDIR)
107 !else
108 SSLLIBDIR = $(SSLDIR)
@@ -348,10 +348,11 @@
348
349 SRC = add_.c \
350 alerts_.c \
351 allrepo_.c \
352 attach_.c \
 
353 backoffice_.c \
354 bag_.c \
355 bisect_.c \
356 blob_.c \
357 branch_.c \
@@ -460,10 +461,11 @@
460 statrep_.c \
461 style_.c \
462 sync_.c \
463 tag_.c \
464 tar_.c \
 
465 th_main_.c \
466 timeline_.c \
467 tkt_.c \
468 tktsetup_.c \
469 undo_.c \
@@ -578,10 +580,11 @@
578
579 OBJ = $(OX)\add$O \
580 $(OX)\alerts$O \
581 $(OX)\allrepo$O \
582 $(OX)\attach$O \
 
583 $(OX)\backoffice$O \
584 $(OX)\bag$O \
585 $(OX)\bisect$O \
586 $(OX)\blob$O \
587 $(OX)\branch$O \
@@ -693,10 +696,11 @@
693 $(OX)\statrep$O \
694 $(OX)\style$O \
695 $(OX)\sync$O \
696 $(OX)\tag$O \
697 $(OX)\tar$O \
 
698 $(OX)\th$O \
699 $(OX)\th_lang$O \
700 $(OX)\th_main$O \
701 $(OX)\th_tcl$O \
702 $(OX)\timeline$O \
@@ -781,10 +785,11 @@
781 $(OX)\linkopts: $B\win\Makefile.msc
782 echo $(OX)\add.obj > $@
783 echo $(OX)\alerts.obj >> $@
784 echo $(OX)\allrepo.obj >> $@
785 echo $(OX)\attach.obj >> $@
 
786 echo $(OX)\backoffice.obj >> $@
787 echo $(OX)\bag.obj >> $@
788 echo $(OX)\bisect.obj >> $@
789 echo $(OX)\blob.obj >> $@
790 echo $(OX)\branch.obj >> $@
@@ -896,10 +901,11 @@
896 echo $(OX)\statrep.obj >> $@
897 echo $(OX)\style.obj >> $@
898 echo $(OX)\sync.obj >> $@
899 echo $(OX)\tag.obj >> $@
900 echo $(OX)\tar.obj >> $@
 
901 echo $(OX)\th.obj >> $@
902 echo $(OX)\th_lang.obj >> $@
903 echo $(OX)\th_main.obj >> $@
904 echo $(OX)\th_tcl.obj >> $@
905 echo $(OX)\timeline.obj >> $@
@@ -1063,10 +1069,16 @@
1063 $(OX)\attach$O : attach_.c attach.h
1064 $(TCC) /Fo$@ -c attach_.c
1065
1066 attach_.c : $(SRCDIR)\attach.c
1067 translate$E $** > $@
 
 
 
 
 
 
1068
1069 $(OX)\backoffice$O : backoffice_.c backoffice.h
1070 $(TCC) /Fo$@ -c backoffice_.c
1071
1072 backoffice_.c : $(SRCDIR)\backoffice.c
@@ -1735,10 +1747,16 @@
1735 $(OX)\tar$O : tar_.c tar.h
1736 $(TCC) /Fo$@ -c tar_.c
1737
1738 tar_.c : $(SRCDIR)\tar.c
1739 translate$E $** > $@
 
 
 
 
 
 
1740
1741 $(OX)\th_main$O : th_main_.c th_main.h
1742 $(TCC) /Fo$@ -c th_main_.c
1743
1744 th_main_.c : $(SRCDIR)\th_main.c
@@ -1882,10 +1900,11 @@
1882 headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h
1883 makeheaders$E add_.c:add.h \
1884 alerts_.c:alerts.h \
1885 allrepo_.c:allrepo.h \
1886 attach_.c:attach.h \
 
1887 backoffice_.c:backoffice.h \
1888 bag_.c:bag.h \
1889 bisect_.c:bisect.h \
1890 blob_.c:blob.h \
1891 branch_.c:branch.h \
@@ -1994,10 +2013,11 @@
1994 statrep_.c:statrep.h \
1995 style_.c:style.h \
1996 sync_.c:sync.h \
1997 tag_.c:tag.h \
1998 tar_.c:tar.h \
 
1999 th_main_.c:th_main.h \
2000 timeline_.c:timeline.h \
2001 tkt_.c:tkt.h \
2002 tktsetup_.c:tktsetup.h \
2003 undo_.c:undo.h \
2004
--- win/Makefile.msc
+++ win/Makefile.msc
@@ -98,11 +98,11 @@
98 !ifndef USE_SEE
99 USE_SEE = 0
100 !endif
101
102 !if $(FOSSIL_ENABLE_SSL)!=0
103 SSLDIR = $(B)\compat\openssl-1.1.1g
104 SSLINCDIR = $(SSLDIR)\include
105 !if $(FOSSIL_DYNAMIC_BUILD)!=0
106 SSLLIBDIR = $(SSLDIR)
107 !else
108 SSLLIBDIR = $(SSLDIR)
@@ -348,10 +348,11 @@
348
349 SRC = add_.c \
350 alerts_.c \
351 allrepo_.c \
352 attach_.c \
353 backlink_.c \
354 backoffice_.c \
355 bag_.c \
356 bisect_.c \
357 blob_.c \
358 branch_.c \
@@ -460,10 +461,11 @@
461 statrep_.c \
462 style_.c \
463 sync_.c \
464 tag_.c \
465 tar_.c \
466 terminal_.c \
467 th_main_.c \
468 timeline_.c \
469 tkt_.c \
470 tktsetup_.c \
471 undo_.c \
@@ -578,10 +580,11 @@
580
581 OBJ = $(OX)\add$O \
582 $(OX)\alerts$O \
583 $(OX)\allrepo$O \
584 $(OX)\attach$O \
585 $(OX)\backlink$O \
586 $(OX)\backoffice$O \
587 $(OX)\bag$O \
588 $(OX)\bisect$O \
589 $(OX)\blob$O \
590 $(OX)\branch$O \
@@ -693,10 +696,11 @@
696 $(OX)\statrep$O \
697 $(OX)\style$O \
698 $(OX)\sync$O \
699 $(OX)\tag$O \
700 $(OX)\tar$O \
701 $(OX)\terminal$O \
702 $(OX)\th$O \
703 $(OX)\th_lang$O \
704 $(OX)\th_main$O \
705 $(OX)\th_tcl$O \
706 $(OX)\timeline$O \
@@ -781,10 +785,11 @@
785 $(OX)\linkopts: $B\win\Makefile.msc
786 echo $(OX)\add.obj > $@
787 echo $(OX)\alerts.obj >> $@
788 echo $(OX)\allrepo.obj >> $@
789 echo $(OX)\attach.obj >> $@
790 echo $(OX)\backlink.obj >> $@
791 echo $(OX)\backoffice.obj >> $@
792 echo $(OX)\bag.obj >> $@
793 echo $(OX)\bisect.obj >> $@
794 echo $(OX)\blob.obj >> $@
795 echo $(OX)\branch.obj >> $@
@@ -896,10 +901,11 @@
901 echo $(OX)\statrep.obj >> $@
902 echo $(OX)\style.obj >> $@
903 echo $(OX)\sync.obj >> $@
904 echo $(OX)\tag.obj >> $@
905 echo $(OX)\tar.obj >> $@
906 echo $(OX)\terminal.obj >> $@
907 echo $(OX)\th.obj >> $@
908 echo $(OX)\th_lang.obj >> $@
909 echo $(OX)\th_main.obj >> $@
910 echo $(OX)\th_tcl.obj >> $@
911 echo $(OX)\timeline.obj >> $@
@@ -1063,10 +1069,16 @@
1069 $(OX)\attach$O : attach_.c attach.h
1070 $(TCC) /Fo$@ -c attach_.c
1071
1072 attach_.c : $(SRCDIR)\attach.c
1073 translate$E $** > $@
1074
1075 $(OX)\backlink$O : backlink_.c backlink.h
1076 $(TCC) /Fo$@ -c backlink_.c
1077
1078 backlink_.c : $(SRCDIR)\backlink.c
1079 translate$E $** > $@
1080
1081 $(OX)\backoffice$O : backoffice_.c backoffice.h
1082 $(TCC) /Fo$@ -c backoffice_.c
1083
1084 backoffice_.c : $(SRCDIR)\backoffice.c
@@ -1735,10 +1747,16 @@
1747 $(OX)\tar$O : tar_.c tar.h
1748 $(TCC) /Fo$@ -c tar_.c
1749
1750 tar_.c : $(SRCDIR)\tar.c
1751 translate$E $** > $@
1752
1753 $(OX)\terminal$O : terminal_.c terminal.h
1754 $(TCC) /Fo$@ -c terminal_.c
1755
1756 terminal_.c : $(SRCDIR)\terminal.c
1757 translate$E $** > $@
1758
1759 $(OX)\th_main$O : th_main_.c th_main.h
1760 $(TCC) /Fo$@ -c th_main_.c
1761
1762 th_main_.c : $(SRCDIR)\th_main.c
@@ -1882,10 +1900,11 @@
1900 headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h
1901 makeheaders$E add_.c:add.h \
1902 alerts_.c:alerts.h \
1903 allrepo_.c:allrepo.h \
1904 attach_.c:attach.h \
1905 backlink_.c:backlink.h \
1906 backoffice_.c:backoffice.h \
1907 bag_.c:bag.h \
1908 bisect_.c:bisect.h \
1909 blob_.c:blob.h \
1910 branch_.c:branch.h \
@@ -1994,10 +2013,11 @@
2013 statrep_.c:statrep.h \
2014 style_.c:style.h \
2015 sync_.c:sync.h \
2016 tag_.c:tag.h \
2017 tar_.c:tar.h \
2018 terminal_.c:terminal.h \
2019 th_main_.c:th_main.h \
2020 timeline_.c:timeline.h \
2021 tkt_.c:tkt.h \
2022 tktsetup_.c:tktsetup.h \
2023 undo_.c:undo.h \
2024
--- www/antibot.wiki
+++ www/antibot.wiki
@@ -1,11 +1,11 @@
11
<title>Defense Against Spiders</title>
22
33
The website presented by a Fossil server has many hyperlinks.
44
Even a modest project can have millions of pages in its
55
tree, and many of those pages (for example diffs and annotations
6
-and ZIP archive of older check-ins) can be expensive to compute.
6
+and ZIP archives of older check-ins) can be expensive to compute.
77
If a spider or bot tries to walk a website implemented by
88
Fossil, it can present a crippling bandwidth and CPU load.
99
1010
The website presented by a Fossil server is intended to be used
1111
interactively by humans, not walked by spiders. This article
1212
--- www/antibot.wiki
+++ www/antibot.wiki
@@ -1,11 +1,11 @@
1 <title>Defense Against Spiders</title>
2
3 The website presented by a Fossil server has many hyperlinks.
4 Even a modest project can have millions of pages in its
5 tree, and many of those pages (for example diffs and annotations
6 and ZIP archive of older check-ins) can be expensive to compute.
7 If a spider or bot tries to walk a website implemented by
8 Fossil, it can present a crippling bandwidth and CPU load.
9
10 The website presented by a Fossil server is intended to be used
11 interactively by humans, not walked by spiders. This article
12
--- www/antibot.wiki
+++ www/antibot.wiki
@@ -1,11 +1,11 @@
1 <title>Defense Against Spiders</title>
2
3 The website presented by a Fossil server has many hyperlinks.
4 Even a modest project can have millions of pages in its
5 tree, and many of those pages (for example diffs and annotations
6 and ZIP archives of older check-ins) can be expensive to compute.
7 If a spider or bot tries to walk a website implemented by
8 Fossil, it can present a crippling bandwidth and CPU load.
9
10 The website presented by a Fossil server is intended to be used
11 interactively by humans, not walked by spiders. This article
12
+1 -1
--- www/build.wiki
+++ www/build.wiki
@@ -161,11 +161,11 @@
161161
the optional <a href="https://www.openssl.org/">OpenSSL</a> support,
162162
first <a href="https://www.openssl.org/source/">download the official
163163
source code for OpenSSL</a> and extract it to an appropriately named
164164
"<b>openssl-X.Y.ZA</b>" subdirectory within the local
165165
[/tree?ci=trunk&name=compat | compat] directory (e.g.
166
-"<b>compat/openssl-1.1.1f</b>"), then make sure that some recent
166
+"<b>compat/openssl-1.1.1g</b>"), then make sure that some recent
167167
<a href="http://www.perl.org/">Perl</a> binaries are installed locally,
168168
and finally run one of the following commands:
169169
<blockquote><pre>
170170
nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin
171171
</pre></blockquote>
172172
--- www/build.wiki
+++ www/build.wiki
@@ -161,11 +161,11 @@
161 the optional <a href="https://www.openssl.org/">OpenSSL</a> support,
162 first <a href="https://www.openssl.org/source/">download the official
163 source code for OpenSSL</a> and extract it to an appropriately named
164 "<b>openssl-X.Y.ZA</b>" subdirectory within the local
165 [/tree?ci=trunk&name=compat | compat] directory (e.g.
166 "<b>compat/openssl-1.1.1f</b>"), then make sure that some recent
167 <a href="http://www.perl.org/">Perl</a> binaries are installed locally,
168 and finally run one of the following commands:
169 <blockquote><pre>
170 nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin
171 </pre></blockquote>
172
--- www/build.wiki
+++ www/build.wiki
@@ -161,11 +161,11 @@
161 the optional <a href="https://www.openssl.org/">OpenSSL</a> support,
162 first <a href="https://www.openssl.org/source/">download the official
163 source code for OpenSSL</a> and extract it to an appropriately named
164 "<b>openssl-X.Y.ZA</b>" subdirectory within the local
165 [/tree?ci=trunk&name=compat | compat] directory (e.g.
166 "<b>compat/openssl-1.1.1g</b>"), then make sure that some recent
167 <a href="http://www.perl.org/">Perl</a> binaries are installed locally,
168 and finally run one of the following commands:
169 <blockquote><pre>
170 nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin
171 </pre></blockquote>
172
+43 -4
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,16 +1,41 @@
11
<title>Change Log</title>
22
33
<a name='v2_11'></a>
44
<h2>Changes for Version 2.11 (pending)</h2>
55
6
- * Support Markdown in the default ticket configuration.
6
+ * Support [/md_rules|Markdown] in the default ticket configuration.
7
+ * Timestamp strings in [./checkin_names.wiki|object names]
8
+ can now omit punctation. So, for example, "202004181942" and
9
+ "2020-04-18 19:42" mean the same thing.
10
+ * Enhance backlink processing so that it works with Markdown-formatted
11
+ tickets and so that it works for wiki pages.
12
+ Ticket [a3572c6a5b47cd5a].
13
+ <ul><li> "[/help?cmd=rebuild|fossil rebuild]" is needed to
14
+ take full advantage of this fix. Fossil will continue
15
+ to work without the rebuild, but the new backlinks will be missing.</ul>
16
+ * The algorithm for finding the
17
+ [./tech_overview.wiki#configloc|location of the configuration database]
18
+ is enhanced to be XDG-compliant.
19
+ * Add a hide/show feature to
20
+ [./wikitheory.wiki#assocwiki|associated wiki] display on
21
+ check-in and branch information pages.
22
+ * Enhance the "[/help?cmd=info|fossil info]" command so that it
23
+ works with no arguments even if not within an open check-out.
24
+ * Many improvements to the forum and especially email notification
25
+ of forum posts, in response to community feedback after switching
26
+ SQLite support from a mailing list over to the forum.
27
+ * Minimum length of a self-registered user ID increased from 3 to 6
28
+ characters.
29
+ * When the "vfx" query parameter is used on the
30
+ "[/help?cmd=/timeline|/timeline]" page, it causes the complete
31
+ text of forum posts to be displayed.
732
* Rework the "[/help?cmd=grep|fossil grep]" command to be more useful.
833
* Expose the [/help?cmd=redirect-to-https|redirect-to-https]
934
setting to the [/help?cmd=settings|settings] command.
1035
* Improve support for CGI on IIS web servers.
11
- * The [/help?cmd=/ext|/ext page] can now render index files,
36
+ * The [./serverext.wiki|/ext page] can now render index files,
1237
in the same way as the embedded docs.
1338
* Most commands now support the Unix-conventional "<tt>--</tt>"
1439
flag to treat all following arguments as filenames
1540
instead of flags.
1641
* Added the [/help?cmd=mimetypes|mimetypes config setting]
@@ -18,18 +43,24 @@
1843
* Add an option on the /Admin/Timeline setup page to set a default
1944
timeline style other than "Modern".
2045
* In [./embeddeddoc.wiki|embedded documentation], hyperlink URLs
2146
of the form "/doc/$CURRENT/..." the "$CURRENT" text is translated
2247
into the check-in hash for the document currently being viewed.
48
+ * Added the [/help?cmd=/phantoms|/phantoms] webpage that shows all
49
+ phantom artifacts.
50
+ * Enhancements to phantom processing to try to reduce
51
+ bandwidth-using chatter about phantoms on the sync protocol.
2352
* Security: Fossil now assumes that the schema of every
2453
database it opens has been tampered with by an adversary and takes
2554
extra precautions to ensure that such tampering is harmless.
2655
* Security: Fossil now puts the Content-Security-Policy in the
2756
HTTP reply header, in addition to also leaving it in the
28
- HTML &lt;head&gt; section, so that it is always available, if
57
+ HTML &lt;head&gt; section, so that it is always available, even
2958
if a custom skin overrides the HTML &lt;head&gt; and omits
3059
the CSP in the process.
60
+ * Output of the [/help?cmd=diff|fossil diff -y] command automatically
61
+ adjusts according to the terminal width.
3162
* The Content-Security-Policy is now set using the
3263
[/help?cmd=default-csp|default-csp setting].
3364
* Merge conflicts caused via the [/help?cmd=merge|merge] and
3465
[/help?cmd=update|update] commands no longer leave temporary
3566
files behind unless the new <tt>--keep-merge-files</tt> flag
@@ -36,21 +67,29 @@
3667
is used.
3768
* The [/help?cmd=/artifact_stats|/artifact_stats page] is now accessible
3869
to all users if the new "artifact_stats_enable" setting is turned
3970
on. There is a new checkbox under the /Admin/Access menu to turn
4071
that capability on and off.
72
+ * Add the [/help?cmd=tls-config|fossil tls-config] command for viewing
73
+ the TLS configuration and the list of SSL Cert exceptions.
4174
* Captchas all include a button to read the captcha using an audio
4275
file, so that they can be completed by the visually impaired.
76
+ * Stop using the IP address as part of the login cookie.
77
+ * Bug fix: fix the SSL cert validation logic so that if an exception
78
+ is allowed for particular site, the exception expires as soon as the
79
+ cert changes values.
80
+ * Bug fix: the FTS search into for forum posts is now kept up-to-date
81
+ correctly.
4382
* Bug fix: the "fossil git export" command is now working on Windows
4483
* Bug fix: display Technote items on the timeline correctly
4584
* Bug fix: fix the capability summary matrix of the Security Audit
4685
page so that it does not add "anonymous" capabilities to the
4786
"nobody" user.
4887
* Update internal Unicode character tables, used in regular expression
4988
handling, from version 12.1 to 13.
5089
* Many documentation enhancements.
51
- * Several minor enhancements to existing features.
90
+ * Many minor enhancements to existing features.
5291
5392
<a name='v2_10'></a>
5493
<h2>Changes for Version 2.10 (2019-10-04)</h2>
5594
5695
* Added support for [./serverext.wiki|CGI-based Server Extensions].
5796
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,16 +1,41 @@
1 <title>Change Log</title>
2
3 <a name='v2_11'></a>
4 <h2>Changes for Version 2.11 (pending)</h2>
5
6 * Support Markdown in the default ticket configuration.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7 * Rework the "[/help?cmd=grep|fossil grep]" command to be more useful.
8 * Expose the [/help?cmd=redirect-to-https|redirect-to-https]
9 setting to the [/help?cmd=settings|settings] command.
10 * Improve support for CGI on IIS web servers.
11 * The [/help?cmd=/ext|/ext page] can now render index files,
12 in the same way as the embedded docs.
13 * Most commands now support the Unix-conventional "<tt>--</tt>"
14 flag to treat all following arguments as filenames
15 instead of flags.
16 * Added the [/help?cmd=mimetypes|mimetypes config setting]
@@ -18,18 +43,24 @@
18 * Add an option on the /Admin/Timeline setup page to set a default
19 timeline style other than "Modern".
20 * In [./embeddeddoc.wiki|embedded documentation], hyperlink URLs
21 of the form "/doc/$CURRENT/..." the "$CURRENT" text is translated
22 into the check-in hash for the document currently being viewed.
 
 
 
 
23 * Security: Fossil now assumes that the schema of every
24 database it opens has been tampered with by an adversary and takes
25 extra precautions to ensure that such tampering is harmless.
26 * Security: Fossil now puts the Content-Security-Policy in the
27 HTTP reply header, in addition to also leaving it in the
28 HTML &lt;head&gt; section, so that it is always available, if
29 if a custom skin overrides the HTML &lt;head&gt; and omits
30 the CSP in the process.
 
 
31 * The Content-Security-Policy is now set using the
32 [/help?cmd=default-csp|default-csp setting].
33 * Merge conflicts caused via the [/help?cmd=merge|merge] and
34 [/help?cmd=update|update] commands no longer leave temporary
35 files behind unless the new <tt>--keep-merge-files</tt> flag
@@ -36,21 +67,29 @@
36 is used.
37 * The [/help?cmd=/artifact_stats|/artifact_stats page] is now accessible
38 to all users if the new "artifact_stats_enable" setting is turned
39 on. There is a new checkbox under the /Admin/Access menu to turn
40 that capability on and off.
 
 
41 * Captchas all include a button to read the captcha using an audio
42 file, so that they can be completed by the visually impaired.
 
 
 
 
 
 
43 * Bug fix: the "fossil git export" command is now working on Windows
44 * Bug fix: display Technote items on the timeline correctly
45 * Bug fix: fix the capability summary matrix of the Security Audit
46 page so that it does not add "anonymous" capabilities to the
47 "nobody" user.
48 * Update internal Unicode character tables, used in regular expression
49 handling, from version 12.1 to 13.
50 * Many documentation enhancements.
51 * Several minor enhancements to existing features.
52
53 <a name='v2_10'></a>
54 <h2>Changes for Version 2.10 (2019-10-04)</h2>
55
56 * Added support for [./serverext.wiki|CGI-based Server Extensions].
57
--- www/changes.wiki
+++ www/changes.wiki
@@ -1,16 +1,41 @@
1 <title>Change Log</title>
2
3 <a name='v2_11'></a>
4 <h2>Changes for Version 2.11 (pending)</h2>
5
6 * Support [/md_rules|Markdown] in the default ticket configuration.
7 * Timestamp strings in [./checkin_names.wiki|object names]
8 can now omit punctation. So, for example, "202004181942" and
9 "2020-04-18 19:42" mean the same thing.
10 * Enhance backlink processing so that it works with Markdown-formatted
11 tickets and so that it works for wiki pages.
12 Ticket [a3572c6a5b47cd5a].
13 <ul><li> "[/help?cmd=rebuild|fossil rebuild]" is needed to
14 take full advantage of this fix. Fossil will continue
15 to work without the rebuild, but the new backlinks will be missing.</ul>
16 * The algorithm for finding the
17 [./tech_overview.wiki#configloc|location of the configuration database]
18 is enhanced to be XDG-compliant.
19 * Add a hide/show feature to
20 [./wikitheory.wiki#assocwiki|associated wiki] display on
21 check-in and branch information pages.
22 * Enhance the "[/help?cmd=info|fossil info]" command so that it
23 works with no arguments even if not within an open check-out.
24 * Many improvements to the forum and especially email notification
25 of forum posts, in response to community feedback after switching
26 SQLite support from a mailing list over to the forum.
27 * Minimum length of a self-registered user ID increased from 3 to 6
28 characters.
29 * When the "vfx" query parameter is used on the
30 "[/help?cmd=/timeline|/timeline]" page, it causes the complete
31 text of forum posts to be displayed.
32 * Rework the "[/help?cmd=grep|fossil grep]" command to be more useful.
33 * Expose the [/help?cmd=redirect-to-https|redirect-to-https]
34 setting to the [/help?cmd=settings|settings] command.
35 * Improve support for CGI on IIS web servers.
36 * The [./serverext.wiki|/ext page] can now render index files,
37 in the same way as the embedded docs.
38 * Most commands now support the Unix-conventional "<tt>--</tt>"
39 flag to treat all following arguments as filenames
40 instead of flags.
41 * Added the [/help?cmd=mimetypes|mimetypes config setting]
@@ -18,18 +43,24 @@
43 * Add an option on the /Admin/Timeline setup page to set a default
44 timeline style other than "Modern".
45 * In [./embeddeddoc.wiki|embedded documentation], hyperlink URLs
46 of the form "/doc/$CURRENT/..." the "$CURRENT" text is translated
47 into the check-in hash for the document currently being viewed.
48 * Added the [/help?cmd=/phantoms|/phantoms] webpage that shows all
49 phantom artifacts.
50 * Enhancements to phantom processing to try to reduce
51 bandwidth-using chatter about phantoms on the sync protocol.
52 * Security: Fossil now assumes that the schema of every
53 database it opens has been tampered with by an adversary and takes
54 extra precautions to ensure that such tampering is harmless.
55 * Security: Fossil now puts the Content-Security-Policy in the
56 HTTP reply header, in addition to also leaving it in the
57 HTML &lt;head&gt; section, so that it is always available, even
58 if a custom skin overrides the HTML &lt;head&gt; and omits
59 the CSP in the process.
60 * Output of the [/help?cmd=diff|fossil diff -y] command automatically
61 adjusts according to the terminal width.
62 * The Content-Security-Policy is now set using the
63 [/help?cmd=default-csp|default-csp setting].
64 * Merge conflicts caused via the [/help?cmd=merge|merge] and
65 [/help?cmd=update|update] commands no longer leave temporary
66 files behind unless the new <tt>--keep-merge-files</tt> flag
@@ -36,21 +67,29 @@
67 is used.
68 * The [/help?cmd=/artifact_stats|/artifact_stats page] is now accessible
69 to all users if the new "artifact_stats_enable" setting is turned
70 on. There is a new checkbox under the /Admin/Access menu to turn
71 that capability on and off.
72 * Add the [/help?cmd=tls-config|fossil tls-config] command for viewing
73 the TLS configuration and the list of SSL Cert exceptions.
74 * Captchas all include a button to read the captcha using an audio
75 file, so that they can be completed by the visually impaired.
76 * Stop using the IP address as part of the login cookie.
77 * Bug fix: fix the SSL cert validation logic so that if an exception
78 is allowed for particular site, the exception expires as soon as the
79 cert changes values.
80 * Bug fix: the FTS search into for forum posts is now kept up-to-date
81 correctly.
82 * Bug fix: the "fossil git export" command is now working on Windows
83 * Bug fix: display Technote items on the timeline correctly
84 * Bug fix: fix the capability summary matrix of the Security Audit
85 page so that it does not add "anonymous" capabilities to the
86 "nobody" user.
87 * Update internal Unicode character tables, used in regular expression
88 handling, from version 12.1 to 13.
89 * Many documentation enhancements.
90 * Many minor enhancements to existing features.
91
92 <a name='v2_10'></a>
93 <h2>Changes for Version 2.10 (2019-10-04)</h2>
94
95 * Added support for [./serverext.wiki|CGI-based Server Extensions].
96
--- www/css-tricks.md
+++ www/css-tricks.md
@@ -48,11 +48,11 @@
4848
## Is it Really `!important`?
4949
5050
By and large, CSS's `!important` qualifier is not needed when
5151
customzing Fossil's CSS. On occasion, however, particular styles may
5252
be set directly on DOM elements when Fossil generates its HTML, and
53
-such cases require the user of `!important` to override them.
53
+such cases require the use of `!important` to override them.
5454
5555
5656
<!-- ============================================================ -->
5757
# Main UI CSS
5858
@@ -103,6 +103,5 @@
103103
div.forumPostBody {
104104
max-height: 25em; /* change to the preferred maximum effective height */
105105
overflow: auto; /* tells the browser to add scrollbars as needed */
106106
}
107107
```
108
-
109108
--- www/css-tricks.md
+++ www/css-tricks.md
@@ -48,11 +48,11 @@
48 ## Is it Really `!important`?
49
50 By and large, CSS's `!important` qualifier is not needed when
51 customzing Fossil's CSS. On occasion, however, particular styles may
52 be set directly on DOM elements when Fossil generates its HTML, and
53 such cases require the user of `!important` to override them.
54
55
56 <!-- ============================================================ -->
57 # Main UI CSS
58
@@ -103,6 +103,5 @@
103 div.forumPostBody {
104 max-height: 25em; /* change to the preferred maximum effective height */
105 overflow: auto; /* tells the browser to add scrollbars as needed */
106 }
107 ```
108
109
--- www/css-tricks.md
+++ www/css-tricks.md
@@ -48,11 +48,11 @@
48 ## Is it Really `!important`?
49
50 By and large, CSS's `!important` qualifier is not needed when
51 customzing Fossil's CSS. On occasion, however, particular styles may
52 be set directly on DOM elements when Fossil generates its HTML, and
53 such cases require the use of `!important` to override them.
54
55
56 <!-- ============================================================ -->
57 # Main UI CSS
58
@@ -103,6 +103,5 @@
103 div.forumPostBody {
104 max-height: 25em; /* change to the preferred maximum effective height */
105 overflow: auto; /* tells the browser to add scrollbars as needed */
106 }
107 ```
 
108
+32 -44
--- www/env-opts.md
+++ www/env-opts.md
@@ -112,25 +112,14 @@
112112
113113
114114
Environment Variables
115115
---------------------
116116
117
-On most platforms, the location of the user’s account-wide `.fossil`
118
-file is either `FOSSIL_HOME` or `HOME`, in that order. This ordering
119
-lets you put this file somewhere other than at the top of your user’s
120
-home directory by defining `FOSSIL_HOME` to mask the always-defined
121
-`HOME`.
122
-
123
-For native Windows builds and for Cygwin builds, the file is called
124
-`_fossil` instead to avoid problems with old programs that assume file
125
-names cannot begin with a dot, as was true in old versions of Windows
126
-and in MS-DOS. (Newer Microsoft OSes and file systems don’t have a
127
-problem with such files, but still we take the safe path in case you’re
128
-on a system with software that can’t cope.) We start our search with
129
-`FOSSIL_HOME` again, but instead of falling back to `HOME`, we instead
130
-try `USERPROFILE`, then `LOCALAPPDATA`, then `APPDATA`, and finally we
131
-concatenate `HOMEDRIVE` + `HOMEPATH`.
117
+The location of the user's account-wide [configuration database][configdb]
118
+depends on the operating system and on the existance of various
119
+environment variables and/or files. See the discussion of the
120
+[configuration database location algorithm][configloc] for details.
132121
133122
`EDITOR`: Name the editor to use for check-in and stash comments.
134123
Overridden by the local or global `editor` setting or the `VISUAL`
135124
environment variable.
136125
@@ -149,16 +138,13 @@
149138
local interactive user via the command line). This can be useful for
150139
local (or remote) testing of the moderation subsystem and its impact
151140
on the contents and status of wiki pages.
152141
153142
154
-`FOSSIL_HOME`: Location of the `~/.fossil` file. The first environment
155
-variable found in the environment from the list `FOSSIL_HOME`,
156
-`LOCALAPPDATA` (Windows), `APPDATA` (Windows), `HOMEDRIVE` and
157
-`HOMEPATH` (Windows, used together), and `HOME` is used as the
158
-location of the `~/.fossil` file.
159
-
143
+`FOSSIL_HOME`: Location of [configuration database][configdb].
144
+See the [configuration database location][configloc] description
145
+for additional information.
160146
161147
`FOSSIL_USE_SEE_TEXTKEY`: If set, treat the encryption key string for
162148
SEE as text to be hashed into the actual encryption key. This has no
163149
effect if Fossil was not compiled with SEE support enabled.
164150
@@ -195,15 +181,12 @@
195181
196182
`GATEWAY_INTERFACE`: If present and the `--nocgi` option is not, assume
197183
fossil is invoked from a web server as a CGI command, and act
198184
accordingly.
199185
200
-`HOME`: Location of the `~/.fossil` file. The first environment
201
-variable found in the environment from the list `FOSSIL_HOME`,
202
-`LOCALAPPDATA` (Windows), `APPDATA` (Windows), `HOMEDRIVE` and
203
-`HOMEPATH` (Windows, used together), and `HOME` is used as the
204
-location of the `~/.fossil` file.
186
+`HOME`: Potential location of the [configuration database][configdb].
187
+See the [configuration database location][configloc] description for details.
205188
206189
`HOMEDRIVE`, `HOMEPATH`: (Windows) Location of the `~/.fossil` file.
207190
The first environment variable found in the environment from the list
208191
`FOSSIL_HOME`, `LOCALAPPDATA` (Windows), `APPDATA` (Windows),
209192
`HOMEDRIVE` and `HOMEPATH` (Windows, used together), and `HOME` is
@@ -402,28 +385,30 @@
402385
first found environment variable from the list `FOSSIL_USER`, `USER`,
403386
`LOGNAME`, and `USERNAME`, is the user name. As a final fallback, if
404387
none of those are set, then the default user name is "root".
405388
406389
407
-### Home Directory
408
-
409
-Fossil keeps some information interesting to each user in the user's
410
-home directory. This includes the global settings and the list of
411
-repositories and checkouts used by `fossil all`.
412
-
413
-The user's home directory is specified by the first environment
414
-variable found in the environment from the list `FOSSIL_HOME`,
415
-`LOCALAPPDATA` (Windows), `APPDATA` (Windows), `HOMEDRIVE` and
416
-`HOMEPATH` (Windows, used together), and `HOME`.
417
-
418
-SQLite has its own notion of the user's home directory, which is only
419
-exposed if the interactive SQL shell is run with the "fossil
420
-sqlite3" command. Being a separate library, SQLite uses many of the
421
-same variables to find the home directory, but uses them in a
422
-different order, and does not use the `FOSSIL_HOME` variable at all.
423
-
424
-
390
+### Configuration Database Location
391
+
392
+Fossil keeps some information pertinent to each user in the user's
393
+[configuration database file][configdb].
394
+The configuration database file includes the global settings
395
+and the list of repositories and checkouts used by `fossil all`.
396
+
397
+The location of the configuration database file depends on the
398
+operating system and on the existance of various environment
399
+variables and/or files. In brief, the configuration database is
400
+usually:
401
+
402
+ * Traditional unix &rarr; "`$HOME/.fossil`"
403
+ * Windows &rarr; "`%LOCALAPPDATA%/_fossil`"
404
+ * [XDG-unix][xdg] &rarr; "`$HOME/.config/fossil.db`"
405
+
406
+[xdg]: https://www.freedesktop.org/wiki/
407
+
408
+See the [configuration database location
409
+algorithm][configloc] discussion for full information.
425410
426411
### SQLite VFS to use
427412
428413
See [the SQLite documentation](http://www.sqlite.org/vfs.html) for an
429414
explanation of what a VFS actually is and what it does.
@@ -491,5 +476,8 @@
491476
On Apple platforms, it assumes that `open` is the command to open an
492477
URL in the user's configured default browser.
493478
494479
On Windows platforms, it assumes that `start` is the command to open
495480
an URL in the user's configured default browser.
481
+
482
+[configdb]: ./tech_overview.wiki#configdb
483
+[configloc]: ./tech_overview.wiki#configloc
496484
--- www/env-opts.md
+++ www/env-opts.md
@@ -112,25 +112,14 @@
112
113
114 Environment Variables
115 ---------------------
116
117 On most platforms, the location of the user’s account-wide `.fossil`
118 file is either `FOSSIL_HOME` or `HOME`, in that order. This ordering
119 lets you put this file somewhere other than at the top of your user’s
120 home directory by defining `FOSSIL_HOME` to mask the always-defined
121 `HOME`.
122
123 For native Windows builds and for Cygwin builds, the file is called
124 `_fossil` instead to avoid problems with old programs that assume file
125 names cannot begin with a dot, as was true in old versions of Windows
126 and in MS-DOS. (Newer Microsoft OSes and file systems don’t have a
127 problem with such files, but still we take the safe path in case you’re
128 on a system with software that can’t cope.) We start our search with
129 `FOSSIL_HOME` again, but instead of falling back to `HOME`, we instead
130 try `USERPROFILE`, then `LOCALAPPDATA`, then `APPDATA`, and finally we
131 concatenate `HOMEDRIVE` + `HOMEPATH`.
132
133 `EDITOR`: Name the editor to use for check-in and stash comments.
134 Overridden by the local or global `editor` setting or the `VISUAL`
135 environment variable.
136
@@ -149,16 +138,13 @@
149 local interactive user via the command line). This can be useful for
150 local (or remote) testing of the moderation subsystem and its impact
151 on the contents and status of wiki pages.
152
153
154 `FOSSIL_HOME`: Location of the `~/.fossil` file. The first environment
155 variable found in the environment from the list `FOSSIL_HOME`,
156 `LOCALAPPDATA` (Windows), `APPDATA` (Windows), `HOMEDRIVE` and
157 `HOMEPATH` (Windows, used together), and `HOME` is used as the
158 location of the `~/.fossil` file.
159
160
161 `FOSSIL_USE_SEE_TEXTKEY`: If set, treat the encryption key string for
162 SEE as text to be hashed into the actual encryption key. This has no
163 effect if Fossil was not compiled with SEE support enabled.
164
@@ -195,15 +181,12 @@
195
196 `GATEWAY_INTERFACE`: If present and the `--nocgi` option is not, assume
197 fossil is invoked from a web server as a CGI command, and act
198 accordingly.
199
200 `HOME`: Location of the `~/.fossil` file. The first environment
201 variable found in the environment from the list `FOSSIL_HOME`,
202 `LOCALAPPDATA` (Windows), `APPDATA` (Windows), `HOMEDRIVE` and
203 `HOMEPATH` (Windows, used together), and `HOME` is used as the
204 location of the `~/.fossil` file.
205
206 `HOMEDRIVE`, `HOMEPATH`: (Windows) Location of the `~/.fossil` file.
207 The first environment variable found in the environment from the list
208 `FOSSIL_HOME`, `LOCALAPPDATA` (Windows), `APPDATA` (Windows),
209 `HOMEDRIVE` and `HOMEPATH` (Windows, used together), and `HOME` is
@@ -402,28 +385,30 @@
402 first found environment variable from the list `FOSSIL_USER`, `USER`,
403 `LOGNAME`, and `USERNAME`, is the user name. As a final fallback, if
404 none of those are set, then the default user name is "root".
405
406
407 ### Home Directory
408
409 Fossil keeps some information interesting to each user in the user's
410 home directory. This includes the global settings and the list of
411 repositories and checkouts used by `fossil all`.
412
413 The user's home directory is specified by the first environment
414 variable found in the environment from the list `FOSSIL_HOME`,
415 `LOCALAPPDATA` (Windows), `APPDATA` (Windows), `HOMEDRIVE` and
416 `HOMEPATH` (Windows, used together), and `HOME`.
417
418 SQLite has its own notion of the user's home directory, which is only
419 exposed if the interactive SQL shell is run with the "fossil
420 sqlite3" command. Being a separate library, SQLite uses many of the
421 same variables to find the home directory, but uses them in a
422 different order, and does not use the `FOSSIL_HOME` variable at all.
423
424
 
 
425
426 ### SQLite VFS to use
427
428 See [the SQLite documentation](http://www.sqlite.org/vfs.html) for an
429 explanation of what a VFS actually is and what it does.
@@ -491,5 +476,8 @@
491 On Apple platforms, it assumes that `open` is the command to open an
492 URL in the user's configured default browser.
493
494 On Windows platforms, it assumes that `start` is the command to open
495 an URL in the user's configured default browser.
 
 
 
496
--- www/env-opts.md
+++ www/env-opts.md
@@ -112,25 +112,14 @@
112
113
114 Environment Variables
115 ---------------------
116
117 The location of the user's account-wide [configuration database][configdb]
118 depends on the operating system and on the existance of various
119 environment variables and/or files. See the discussion of the
120 [configuration database location algorithm][configloc] for details.
 
 
 
 
 
 
 
 
 
 
 
121
122 `EDITOR`: Name the editor to use for check-in and stash comments.
123 Overridden by the local or global `editor` setting or the `VISUAL`
124 environment variable.
125
@@ -149,16 +138,13 @@
138 local interactive user via the command line). This can be useful for
139 local (or remote) testing of the moderation subsystem and its impact
140 on the contents and status of wiki pages.
141
142
143 `FOSSIL_HOME`: Location of [configuration database][configdb].
144 See the [configuration database location][configloc] description
145 for additional information.
 
 
 
146
147 `FOSSIL_USE_SEE_TEXTKEY`: If set, treat the encryption key string for
148 SEE as text to be hashed into the actual encryption key. This has no
149 effect if Fossil was not compiled with SEE support enabled.
150
@@ -195,15 +181,12 @@
181
182 `GATEWAY_INTERFACE`: If present and the `--nocgi` option is not, assume
183 fossil is invoked from a web server as a CGI command, and act
184 accordingly.
185
186 `HOME`: Potential location of the [configuration database][configdb].
187 See the [configuration database location][configloc] description for details.
 
 
 
188
189 `HOMEDRIVE`, `HOMEPATH`: (Windows) Location of the `~/.fossil` file.
190 The first environment variable found in the environment from the list
191 `FOSSIL_HOME`, `LOCALAPPDATA` (Windows), `APPDATA` (Windows),
192 `HOMEDRIVE` and `HOMEPATH` (Windows, used together), and `HOME` is
@@ -402,28 +385,30 @@
385 first found environment variable from the list `FOSSIL_USER`, `USER`,
386 `LOGNAME`, and `USERNAME`, is the user name. As a final fallback, if
387 none of those are set, then the default user name is "root".
388
389
390 ### Configuration Database Location
391
392 Fossil keeps some information pertinent to each user in the user's
393 [configuration database file][configdb].
394 The configuration database file includes the global settings
395 and the list of repositories and checkouts used by `fossil all`.
396
397 The location of the configuration database file depends on the
398 operating system and on the existance of various environment
399 variables and/or files. In brief, the configuration database is
400 usually:
401
402 * Traditional unix &rarr; "`$HOME/.fossil`"
403 * Windows &rarr; "`%LOCALAPPDATA%/_fossil`"
404 * [XDG-unix][xdg] &rarr; "`$HOME/.config/fossil.db`"
405
406 [xdg]: https://www.freedesktop.org/wiki/
407
408 See the [configuration database location
409 algorithm][configloc] discussion for full information.
410
411 ### SQLite VFS to use
412
413 See [the SQLite documentation](http://www.sqlite.org/vfs.html) for an
414 explanation of what a VFS actually is and what it does.
@@ -491,5 +476,8 @@
476 On Apple platforms, it assumes that `open` is the command to open an
477 URL in the user's configured default browser.
478
479 On Windows platforms, it assumes that `start` is the command to open
480 an URL in the user's configured default browser.
481
482 [configdb]: ./tech_overview.wiki#configdb
483 [configloc]: ./tech_overview.wiki#configloc
484
+3 -1
--- www/faq.wiki
+++ www/faq.wiki
@@ -1,9 +1,11 @@
11
<title>Fossil FAQ</title>
22
<h1 align="center">Frequently Asked Questions</h1>
33
4
-<p>Note: See also <a href="qandc.wiki">Questions and Criticisms</a>.
4
+<p>Note:
5
+This page is old and has not been kept up-to-date. See the
6
+[/finfo?name=www/faq.wiki|change history of this page].</p>
57
68
<ol>
79
<li><a href="#q1">What GUIs are available for fossil?</a></li>
810
<li><a href="#q2">What is the difference between a "branch" and a "fork"?</a></li>
911
<li><a href="#q3">How do I create a new branch?</a></li>
1012
--- www/faq.wiki
+++ www/faq.wiki
@@ -1,9 +1,11 @@
1 <title>Fossil FAQ</title>
2 <h1 align="center">Frequently Asked Questions</h1>
3
4 <p>Note: See also <a href="qandc.wiki">Questions and Criticisms</a>.
 
 
5
6 <ol>
7 <li><a href="#q1">What GUIs are available for fossil?</a></li>
8 <li><a href="#q2">What is the difference between a "branch" and a "fork"?</a></li>
9 <li><a href="#q3">How do I create a new branch?</a></li>
10
--- www/faq.wiki
+++ www/faq.wiki
@@ -1,9 +1,11 @@
1 <title>Fossil FAQ</title>
2 <h1 align="center">Frequently Asked Questions</h1>
3
4 <p>Note:
5 This page is old and has not been kept up-to-date. See the
6 [/finfo?name=www/faq.wiki|change history of this page].</p>
7
8 <ol>
9 <li><a href="#q1">What GUIs are available for fossil?</a></li>
10 <li><a href="#q2">What is the difference between a "branch" and a "fork"?</a></li>
11 <li><a href="#q3">How do I create a new branch?</a></li>
12
--- www/fossil-v-git.wiki
+++ www/fossil-v-git.wiki
@@ -114,14 +114,14 @@
114114
the design. One way to describe Fossil is that it is
115115
"[https://github.com/ | GitHub]-in-a-box."
116116
117117
Fossil can do operations over all local repo clones and check-out
118118
directories with a single command. For example, Fossil lets you say
119
-<tt>fossil all pull</tt> on a laptop prior to taking it off the network
119
+<tt>fossil all sync</tt> on a laptop prior to taking it off the network
120120
hosting those repos. You can sync up to all of the private repos on your
121121
company network plus those public Internet-hosted repos you use. Whether
122
-going out for a working lunch or on a transoceanic an airplane trip, one
122
+going out for a working lunch or on a transoceanic airplane trip, one
123123
command gets you in sync. This works with several other Fossil
124124
sub-commands, such as <tt>fossil all changes</tt> to get a list of files
125125
that you forgot to commit prior to the end of your working day, across
126126
all repos.
127127
@@ -258,11 +258,11 @@
258258
are looking at some historical check-in then you cannot ask "What came
259259
next?" or "What are the children of this check-in?"
260260
261261
Fossil, on the other hand, parses essential information about check-ins
262262
(parents, children, committers, comments, files changed, etc.) into a
263
-relational database that can be easily queried using concise SQL
263
+relational database that can easily be queried using concise SQL
264264
statements to find both ancestors and descendants of a check-in. This is
265265
the hybrid data model mentioned above: Fossil manages your check-in and
266266
other data in a NoSQL block chain structured data store, but that's backed
267267
by a set of relational lookup tables for quick indexing into that
268268
artifact store. (See "[./theory1.wiki|Thoughts On The Design Of The
@@ -759,13 +759,13 @@
759759
760760
Fossil cannot sensibly work that way because of its default-enabled
761761
autosync feature. Instead of jumping straight to the commit step, Fossil
762762
applies the proposed merge to the local working directory only,
763763
requiring a separate check-in step before the change is committed to the
764
-repository blockchain. This gives you a chance to test the change,
765
-whether manually, or by running your software's automatic tests, or
766
-both.
764
+repository blockchain. This gives you a chance to test the change first,
765
+either manually or by running your software's automatic tests. (Ideally,
766
+both!)
767767
768768
Another difference is that because Fossil requires an explicit commit
769769
for a merge, it makes you give an explicit commit <i>message</i> for
770770
each merge, whereas Git writes that commit message itself by default
771771
unless you give the optional <tt>--edit</tt> flag to override it.
@@ -805,11 +805,11 @@
805805
brief online help for <tt>[/help?cmd=merge | fossil merge]</tt> is
806806
currently 41 lines long, to which you want to add the 600 lines of
807807
[./branching.wiki | the branching document]. The equivalent
808808
documentation in Git is the aggregation of the man pages for the above
809809
three commands, which is over 1000 lines, much of it mutually redundant.
810
-(e.g. the <tt>--edit</tt> and <tt>--no-commit</tt> options get
810
+(e.g. Git's <tt>--edit</tt> and <tt>--no-commit</tt> options get
811811
described three times, each time differently.) Fossil's
812812
documentation is not only more concise, it gives a nice split of brief
813813
online help and full online documentation.
814814
815815
@@ -855,11 +855,11 @@
855855
weakening the case for continuing to use SHA-1.
856856
857857
The practical impact of attacks like SHAttered and SHAmbles on the
858858
Git and Fossil blockchains isn't clear, but you want to have your repositories
859859
moved over to a stronger hash algorithm before someone figures out how
860
-to make use of the weaknesses in the old one. Fossil had this covered
860
+to make use of the weaknesses in the old one. Fossil has had this covered
861861
for years now, so that the solution is now almost universally deployed.
862862
863863
<hr/>
864864
865865
<h3>Asides and Digressions</h3>
866866
867867
ADDED www/gitusers.md
868868
ADDED www/history.md
--- www/fossil-v-git.wiki
+++ www/fossil-v-git.wiki
@@ -114,14 +114,14 @@
114 the design. One way to describe Fossil is that it is
115 "[https://github.com/ | GitHub]-in-a-box."
116
117 Fossil can do operations over all local repo clones and check-out
118 directories with a single command. For example, Fossil lets you say
119 <tt>fossil all pull</tt> on a laptop prior to taking it off the network
120 hosting those repos. You can sync up to all of the private repos on your
121 company network plus those public Internet-hosted repos you use. Whether
122 going out for a working lunch or on a transoceanic an airplane trip, one
123 command gets you in sync. This works with several other Fossil
124 sub-commands, such as <tt>fossil all changes</tt> to get a list of files
125 that you forgot to commit prior to the end of your working day, across
126 all repos.
127
@@ -258,11 +258,11 @@
258 are looking at some historical check-in then you cannot ask "What came
259 next?" or "What are the children of this check-in?"
260
261 Fossil, on the other hand, parses essential information about check-ins
262 (parents, children, committers, comments, files changed, etc.) into a
263 relational database that can be easily queried using concise SQL
264 statements to find both ancestors and descendants of a check-in. This is
265 the hybrid data model mentioned above: Fossil manages your check-in and
266 other data in a NoSQL block chain structured data store, but that's backed
267 by a set of relational lookup tables for quick indexing into that
268 artifact store. (See "[./theory1.wiki|Thoughts On The Design Of The
@@ -759,13 +759,13 @@
759
760 Fossil cannot sensibly work that way because of its default-enabled
761 autosync feature. Instead of jumping straight to the commit step, Fossil
762 applies the proposed merge to the local working directory only,
763 requiring a separate check-in step before the change is committed to the
764 repository blockchain. This gives you a chance to test the change,
765 whether manually, or by running your software's automatic tests, or
766 both.
767
768 Another difference is that because Fossil requires an explicit commit
769 for a merge, it makes you give an explicit commit <i>message</i> for
770 each merge, whereas Git writes that commit message itself by default
771 unless you give the optional <tt>--edit</tt> flag to override it.
@@ -805,11 +805,11 @@
805 brief online help for <tt>[/help?cmd=merge | fossil merge]</tt> is
806 currently 41 lines long, to which you want to add the 600 lines of
807 [./branching.wiki | the branching document]. The equivalent
808 documentation in Git is the aggregation of the man pages for the above
809 three commands, which is over 1000 lines, much of it mutually redundant.
810 (e.g. the <tt>--edit</tt> and <tt>--no-commit</tt> options get
811 described three times, each time differently.) Fossil's
812 documentation is not only more concise, it gives a nice split of brief
813 online help and full online documentation.
814
815
@@ -855,11 +855,11 @@
855 weakening the case for continuing to use SHA-1.
856
857 The practical impact of attacks like SHAttered and SHAmbles on the
858 Git and Fossil blockchains isn't clear, but you want to have your repositories
859 moved over to a stronger hash algorithm before someone figures out how
860 to make use of the weaknesses in the old one. Fossil had this covered
861 for years now, so that the solution is now almost universally deployed.
862
863 <hr/>
864
865 <h3>Asides and Digressions</h3>
866
867 DDED www/gitusers.md
868 DDED www/history.md
--- www/fossil-v-git.wiki
+++ www/fossil-v-git.wiki
@@ -114,14 +114,14 @@
114 the design. One way to describe Fossil is that it is
115 "[https://github.com/ | GitHub]-in-a-box."
116
117 Fossil can do operations over all local repo clones and check-out
118 directories with a single command. For example, Fossil lets you say
119 <tt>fossil all sync</tt> on a laptop prior to taking it off the network
120 hosting those repos. You can sync up to all of the private repos on your
121 company network plus those public Internet-hosted repos you use. Whether
122 going out for a working lunch or on a transoceanic airplane trip, one
123 command gets you in sync. This works with several other Fossil
124 sub-commands, such as <tt>fossil all changes</tt> to get a list of files
125 that you forgot to commit prior to the end of your working day, across
126 all repos.
127
@@ -258,11 +258,11 @@
258 are looking at some historical check-in then you cannot ask "What came
259 next?" or "What are the children of this check-in?"
260
261 Fossil, on the other hand, parses essential information about check-ins
262 (parents, children, committers, comments, files changed, etc.) into a
263 relational database that can easily be queried using concise SQL
264 statements to find both ancestors and descendants of a check-in. This is
265 the hybrid data model mentioned above: Fossil manages your check-in and
266 other data in a NoSQL block chain structured data store, but that's backed
267 by a set of relational lookup tables for quick indexing into that
268 artifact store. (See "[./theory1.wiki|Thoughts On The Design Of The
@@ -759,13 +759,13 @@
759
760 Fossil cannot sensibly work that way because of its default-enabled
761 autosync feature. Instead of jumping straight to the commit step, Fossil
762 applies the proposed merge to the local working directory only,
763 requiring a separate check-in step before the change is committed to the
764 repository blockchain. This gives you a chance to test the change first,
765 either manually or by running your software's automatic tests. (Ideally,
766 both!)
767
768 Another difference is that because Fossil requires an explicit commit
769 for a merge, it makes you give an explicit commit <i>message</i> for
770 each merge, whereas Git writes that commit message itself by default
771 unless you give the optional <tt>--edit</tt> flag to override it.
@@ -805,11 +805,11 @@
805 brief online help for <tt>[/help?cmd=merge | fossil merge]</tt> is
806 currently 41 lines long, to which you want to add the 600 lines of
807 [./branching.wiki | the branching document]. The equivalent
808 documentation in Git is the aggregation of the man pages for the above
809 three commands, which is over 1000 lines, much of it mutually redundant.
810 (e.g. Git's <tt>--edit</tt> and <tt>--no-commit</tt> options get
811 described three times, each time differently.) Fossil's
812 documentation is not only more concise, it gives a nice split of brief
813 online help and full online documentation.
814
815
@@ -855,11 +855,11 @@
855 weakening the case for continuing to use SHA-1.
856
857 The practical impact of attacks like SHAttered and SHAmbles on the
858 Git and Fossil blockchains isn't clear, but you want to have your repositories
859 moved over to a stronger hash algorithm before someone figures out how
860 to make use of the weaknesses in the old one. Fossil has had this covered
861 for years now, so that the solution is now almost universally deployed.
862
863 <hr/>
864
865 <h3>Asides and Digressions</h3>
866
867 DDED www/gitusers.md
868 DDED www/history.md
--- a/www/gitusers.md
+++ b/www/gitusers.md
@@ -0,0 +1,143 @@
1
+.
2
+
3
+
4
+## Help Improve This Document
5
+
6
+ experience, and you are new to Fossil
7
+and are struggling with some concepts, please ask for help on the
8
+[Fossil Forum][1]. The people who write this document are intimately
9
+familiar with Fossil and less It is difficult for
10
+ initimately familiar
11
+with Git ## Help Improve This Document
12
+
13
+ experience, and you are new to Fossil
14
+and are struggling with some concepts, please ask for help on the
15
+[Fossil Forum][1]. The people who write this document are intimately
16
+familiar with Fossil and less It is difficult for
17
+ initimately familiar
18
+with Git and less familiar with Fossil. AskingForum
19
+will help us tSpecific suggestions on how to improve this document are also welcomed,
20
+of course.
21
+
22
+
23
+
24
+## <a
25
+----in
26
+which is stored. A(See for more Fossil terms of art that may
27
+be unfamiliar to a Git user.)moreFossil can operate in the Git modeit can(Note that Ffor you as
28
+Git d.
29
+
30
+
31
+## Help Improve This Document
32
+
33
+ experience, and you are new to Fossil
34
+and are struggling with some concepts, please ask for help on the
35
+[Fossil Forum][1]. The people who write this document are intimately
36
+familiar with Fossil and less It is difficult for
37
+ initimately familiar
38
+with Git and less familiar with Fossil. AskingForum
39
+will help us tSpecific suggestions on how to improve this document are also welcomed,
40
+of course.
41
+
42
+
43
+
44
+## <a
45
+----in
46
+which is stored. A(See for more Fossil terms of art that may
47
+be unfamiliar to a Git user.)moreFossil can operate in the Git modeit can(Note that Ffor you as
48
+Gfter-The-Fact
49
+
50
+Fossil per“open” command][open], which hwhen you commit using
51
+the "`--branch` _BRANCH-NAME_" command-line option. he
52
+[Fossil Forum][1]. The pecommium][1]. The people who wrIt is not necessary to create branches ahead of time, as in Git, though
53
+that is allowedme concepts, please branch new`prove This Document
54
+
55
+ experience, and you are new to Fossil
56
+and are struggling with some concepts, please ask for help on the
57
+[Fossil Forum][1]. The people who wrfic suggestions on how to ik2p@40C,L:,
58
+so what you want is3d@43a,G6@47E,l@4NQ,2:isUF@4O9,2b@4rP,3X@4u0,6q:Unlike Git, Fossil’s “clone and open� call here that isn’t
59
+needed in the Git case. This is an indirect reflection of Fossil’s
60
+[multiple working directories](#mwd) design philosophy: its
61
+[`open` command][open] requires that you either issue it in an empty
62
+directory or one containing a prior closed check-out. In exchange for
63
+this extra command, we getS@5Bc,1s:
64
+[superior handling][shwmd] of multiple workingy can reside
65
+h fromS@4xk,3X:
66
+comma hierarchy with the check-out as with Git, but it
67
+is more common to putopen� call herein a separate directory.
68
+
69
+[2]:
70
+Fossil repositories are a single file, rather than being a directory
71
+hierarsitory file
72
+rn to parenwillI, but acum[1]. but it is best to usence, and you are new to Fossil
73
+and are struggling all branches, all wiki, all tickets,Fossil -i,3m@3uC,3:—S@3xx,1:,1W@3yS,P:maybe one part-day a week2p@40C,L:ne and open� - everything.
74
+�s
75
+[multiple working directories](#mwd) design philosophy: its
76
+[`open` command][open] requires that you either issue it in an empty
77
+directory or one containing a prior closed check-out. In exchange for
78
+this extra command, we getS@5Bc,1s:
79
+[superior handling][shwmd] of multiple working directories. To get the
80
+full power of this feature, you’d switch fromS@4xk,3X:
81
+command form to the separate clone-and-open form shown in
82
+[the quick start guide][qs], which adds one more command.
83
+
84
+We can’t spin the longer final command as a trade-off giving us extra
85
+power, though: the simple fact is, 5f@50N,1:KU@56j## T[2]:
86
+Fossil repositornge for
87
+this extra command, we getS@5Bc,1s:
88
+[superior handling][shwmd] of multiple workingy can reside
89
+h fromS@4xk,3X:
90
+comma hierarchy with the check-out as with Git, but it
91
+is more common to putopen� call herein a separate directory.
92
+
93
+[2]:
94
+Fossil repositories are a single file, rather than being a directory
95
+hierarchy as with the "` parenwillI, but acum[1]. .
96
+This is a [, all wiki, all tickets,Fo open� call here that isn�the [Rebase Considered Harmndirect reflection of Fossilflection of Fossil’s
97
+[m�s
98
+[multiple working directories](#mwd) design philosophy: its
99
+[`open` command][open] requires that you either issue it in an empty
100
+directory or one containing a prior closed check-out. In exchange for
101
+this extra command, we getS@5Bc,1s:
102
+[superior handling][shwmd] of multiple working directories. To get the
103
+full power of this feature, you’d switch fromS@4xk,3X:
104
+command form to the separate clone-and-open form show
105
+
106
+## Only One "origin" At A Time
107
+
108
+A extra command, we only keeps track of one "origin" server at a time.
109
+If you specify a new "origin" it forgets the previous one. Use the
110
+"`fossil remote`" command to see or change the "origin".
111
+
112
+Fossil uses a very different sync protocol than Git, so it is, not "`masterisn't as
113
+important for Fossil to keep track of multiple origins as it is with
114
+Git. So only having a single origin has never been a big enough problem
115
+in Fossil thGit.
116
+
117
+These naming conventions are so embedded in each system, that the
118
+"trunk" branch name is automatically translated to "master" when
119
+a [Fossil rerget to give the `--baactually
120
+showsadding more DAG display, whereas
121
+there is no provision for recording cherry-picks in the Git file
122
+format, so you have to talk about the cherry-pick in the commit
123
+comment if you want to remember it.
124
+
125
+## The "`fossil mv`" and "`fossil rm`" Commands Do Not Actually Rename Or Delete The Files (by default)
126
+
127
+By default,
128
+the "`fossil mv`" and "`fossil rm`" commands wal repo, so
129
+doi is no provision for recording cherry-picks in the Git file
130
+format, so you have to talk about the cherry-pick in the commit
131
+comment if you want to remember it.
132
+
133
+## The "`fossil mv`" and "`fossil rm`" Commands Do Not Actually Rename Or Delete The Files (by default)
134
+
135
+By default,
136
+the "`fossil mv`" and "`fossil rm`" commands wal repo, so
137
+doing this , but do not actually
138
+dn’t use `--set-upstream/-u` hercause we didn’t use `--set-upstream/-u` here, we have to name the
139
+“worwork” origin explicitly in these commands. (This also shows Git’s
140
+unwillingness to sync branch names, covered elsewhere in this document.) check-out.
141
+If you runis also shows Git’s
142
+uit makes a notation in your per-user "~/.fossil" settings file so that
143
+the "--hard" behavio
--- a/www/gitusers.md
+++ b/www/gitusers.md
@@ -0,0 +1,143 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/www/gitusers.md
+++ b/www/gitusers.md
@@ -0,0 +1,143 @@
1 .
2
3
4 ## Help Improve This Document
5
6 experience, and you are new to Fossil
7 and are struggling with some concepts, please ask for help on the
8 [Fossil Forum][1]. The people who write this document are intimately
9 familiar with Fossil and less It is difficult for
10 initimately familiar
11 with Git ## Help Improve This Document
12
13 experience, and you are new to Fossil
14 and are struggling with some concepts, please ask for help on the
15 [Fossil Forum][1]. The people who write this document are intimately
16 familiar with Fossil and less It is difficult for
17 initimately familiar
18 with Git and less familiar with Fossil. AskingForum
19 will help us tSpecific suggestions on how to improve this document are also welcomed,
20 of course.
21
22
23
24 ## <a
25 ----in
26 which is stored. A(See for more Fossil terms of art that may
27 be unfamiliar to a Git user.)moreFossil can operate in the Git modeit can(Note that Ffor you as
28 Git d.
29
30
31 ## Help Improve This Document
32
33 experience, and you are new to Fossil
34 and are struggling with some concepts, please ask for help on the
35 [Fossil Forum][1]. The people who write this document are intimately
36 familiar with Fossil and less It is difficult for
37 initimately familiar
38 with Git and less familiar with Fossil. AskingForum
39 will help us tSpecific suggestions on how to improve this document are also welcomed,
40 of course.
41
42
43
44 ## <a
45 ----in
46 which is stored. A(See for more Fossil terms of art that may
47 be unfamiliar to a Git user.)moreFossil can operate in the Git modeit can(Note that Ffor you as
48 Gfter-The-Fact
49
50 Fossil per“open” command][open], which hwhen you commit using
51 the "`--branch` _BRANCH-NAME_" command-line option. he
52 [Fossil Forum][1]. The pecommium][1]. The people who wrIt is not necessary to create branches ahead of time, as in Git, though
53 that is allowedme concepts, please branch new`prove This Document
54
55 experience, and you are new to Fossil
56 and are struggling with some concepts, please ask for help on the
57 [Fossil Forum][1]. The people who wrfic suggestions on how to ik2p@40C,L:,
58 so what you want is3d@43a,G6@47E,l@4NQ,2:isUF@4O9,2b@4rP,3X@4u0,6q:Unlike Git, Fossil’s “clone and open� call here that isn’t
59 needed in the Git case. This is an indirect reflection of Fossil’s
60 [multiple working directories](#mwd) design philosophy: its
61 [`open` command][open] requires that you either issue it in an empty
62 directory or one containing a prior closed check-out. In exchange for
63 this extra command, we getS@5Bc,1s:
64 [superior handling][shwmd] of multiple workingy can reside
65 h fromS@4xk,3X:
66 comma hierarchy with the check-out as with Git, but it
67 is more common to putopen� call herein a separate directory.
68
69 [2]:
70 Fossil repositories are a single file, rather than being a directory
71 hierarsitory file
72 rn to parenwillI, but acum[1]. but it is best to usence, and you are new to Fossil
73 and are struggling all branches, all wiki, all tickets,Fossil -i,3m@3uC,3:—S@3xx,1:,1W@3yS,P:maybe one part-day a week2p@40C,L:ne and open� - everything.
74 �s
75 [multiple working directories](#mwd) design philosophy: its
76 [`open` command][open] requires that you either issue it in an empty
77 directory or one containing a prior closed check-out. In exchange for
78 this extra command, we getS@5Bc,1s:
79 [superior handling][shwmd] of multiple working directories. To get the
80 full power of this feature, you’d switch fromS@4xk,3X:
81 command form to the separate clone-and-open form shown in
82 [the quick start guide][qs], which adds one more command.
83
84 We can’t spin the longer final command as a trade-off giving us extra
85 power, though: the simple fact is, 5f@50N,1:KU@56j## T[2]:
86 Fossil repositornge for
87 this extra command, we getS@5Bc,1s:
88 [superior handling][shwmd] of multiple workingy can reside
89 h fromS@4xk,3X:
90 comma hierarchy with the check-out as with Git, but it
91 is more common to putopen� call herein a separate directory.
92
93 [2]:
94 Fossil repositories are a single file, rather than being a directory
95 hierarchy as with the "` parenwillI, but acum[1]. .
96 This is a [, all wiki, all tickets,Fo open� call here that isn�the [Rebase Considered Harmndirect reflection of Fossilflection of Fossil’s
97 [m�s
98 [multiple working directories](#mwd) design philosophy: its
99 [`open` command][open] requires that you either issue it in an empty
100 directory or one containing a prior closed check-out. In exchange for
101 this extra command, we getS@5Bc,1s:
102 [superior handling][shwmd] of multiple working directories. To get the
103 full power of this feature, you’d switch fromS@4xk,3X:
104 command form to the separate clone-and-open form show
105
106 ## Only One "origin" At A Time
107
108 A extra command, we only keeps track of one "origin" server at a time.
109 If you specify a new "origin" it forgets the previous one. Use the
110 "`fossil remote`" command to see or change the "origin".
111
112 Fossil uses a very different sync protocol than Git, so it is, not "`masterisn't as
113 important for Fossil to keep track of multiple origins as it is with
114 Git. So only having a single origin has never been a big enough problem
115 in Fossil thGit.
116
117 These naming conventions are so embedded in each system, that the
118 "trunk" branch name is automatically translated to "master" when
119 a [Fossil rerget to give the `--baactually
120 showsadding more DAG display, whereas
121 there is no provision for recording cherry-picks in the Git file
122 format, so you have to talk about the cherry-pick in the commit
123 comment if you want to remember it.
124
125 ## The "`fossil mv`" and "`fossil rm`" Commands Do Not Actually Rename Or Delete The Files (by default)
126
127 By default,
128 the "`fossil mv`" and "`fossil rm`" commands wal repo, so
129 doi is no provision for recording cherry-picks in the Git file
130 format, so you have to talk about the cherry-pick in the commit
131 comment if you want to remember it.
132
133 ## The "`fossil mv`" and "`fossil rm`" Commands Do Not Actually Rename Or Delete The Files (by default)
134
135 By default,
136 the "`fossil mv`" and "`fossil rm`" commands wal repo, so
137 doing this , but do not actually
138 dn’t use `--set-upstream/-u` hercause we didn’t use `--set-upstream/-u` here, we have to name the
139 “worwork” origin explicitly in these commands. (This also shows Git’s
140 unwillingness to sync branch names, covered elsewhere in this document.) check-out.
141 If you runis also shows Git’s
142 uit makes a notation in your per-user "~/.fossil" settings file so that
143 the "--hard" behavio
--- a/www/history.md
+++ b/www/history.md
@@ -0,0 +1,72 @@
1
+# The History And Purpose Of Fossil
2
+
3
+Fossil is a [distributed version control system (DVCS)][100] written
4
+beginning in [2007][105] by the [architect of SQLite][110] for the
5
+purpose of managing the [SQLite project][115].
6
+
7
+[100]: https://en.wikipedia.org/wiki/Distributed_version_control
8
+[105: /timeline?a=1970-01-01&n1=10
9
+[110]: https://sqlite.org/crew.html
10
+[115]: https://sqlite.org/
11
+
12
+Though Fossil was originally written specifically to support SQLite,
13
+it is now also used by countless other projects. The SQLite architect (drh)
14
+is still the top committer to Fossil, but there are also
15
+[many other contributors][120].
16
+
17
+[120]: /reports?type=ci&view=byuser
18
+
19
+## Histor
20
+The SQLite project started out using [CVS][300], as CVS was the most
21
+commonly used version control system in thant era (circa 2000). CVS
22
+was an amazing version control system for its day in that it allowed
23
+multiple developers to be editing the same file at the same time.
24
+
25
+[300]: https://en.wikipedia.org/wiki/Concurrent_Versions_System
26
+
27
+Though innovative and much loved in its time, CVS was not without problems.
28
+Among those was a lack of visibility into the project history and the
29
+lack of itry tPurcpose Of Fossil
30
+
31
+Fossil is a [distributed version control system (DVCS)][100] written
32
+beginning in [2007][105] by the [architect of SQLite][110] for the
33
+purpose of managin
34
+
35
+[100]: https://en.wikipedia.org/wiki/Distributed_version_control
36
+[105]: /timeline?a=1970-01-01&n1=10
37
+[110]: https://sqlite.org/crew.html
38
+[115]: https://sqlite.org/
39
+
40
+Though Fossil was originally written specifically to support SQLite,
41
+it is now also used by countless other projects. The SQLite architect (drh)
42
+is still the top committer to Fossil, but there are also
43
+[many other contributors][120].
44
+
45
+[120]: /reports?type=ci&view=byuser
46
+
47
+## Histor
48
+The SQLite project started out using [CVS][300], as CVS was the most
49
+commonly used version control system in thant era (circa 2000). CVS
50
+was an amazing version control system for its day in that it allowed
51
+multiple developers to be editing the same file at the same time.
52
+
53
+[300]: https://en.wikipedia.org/wiki/Concurrent_Versions_System
54
+
55
+T=10
56
+[3320030101&n into the project history and the
57
+lack of integrated bug tracking. To address these deone was the first VCS to make use of
58
+SHA1 to identify artifacts. Monotone stored its content in a SQLite
59
+database, which is what brought it to the ssil
60
+
61
+Fossil is a [distributed version control system (DVCS)][100] written
62
+beginning in [2007][105] by the [architect of SQLite][110] for the
63
+purpose of managing the [SQLite project][115].
64
+
65
+[100]: https://en.wikipedia.org/wiki/Distributed_version_control
66
+[105]: /timeline?a=1970-01-01&n1=10
67
+[110]: https://sqlite.org/crew.html
68
+[115]: https://sqlite.org/
69
+
70
+Though Fossil was originally written specifically to support SQLite,
71
+it is now also used by countless other projects. The SQLite architect (drh)
72
+is still the top co=12&y=ci
--- a/www/history.md
+++ b/www/history.md
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/www/history.md
+++ b/www/history.md
@@ -0,0 +1,72 @@
1 # The History And Purpose Of Fossil
2
3 Fossil is a [distributed version control system (DVCS)][100] written
4 beginning in [2007][105] by the [architect of SQLite][110] for the
5 purpose of managing the [SQLite project][115].
6
7 [100]: https://en.wikipedia.org/wiki/Distributed_version_control
8 [105: /timeline?a=1970-01-01&n1=10
9 [110]: https://sqlite.org/crew.html
10 [115]: https://sqlite.org/
11
12 Though Fossil was originally written specifically to support SQLite,
13 it is now also used by countless other projects. The SQLite architect (drh)
14 is still the top committer to Fossil, but there are also
15 [many other contributors][120].
16
17 [120]: /reports?type=ci&view=byuser
18
19 ## Histor
20 The SQLite project started out using [CVS][300], as CVS was the most
21 commonly used version control system in thant era (circa 2000). CVS
22 was an amazing version control system for its day in that it allowed
23 multiple developers to be editing the same file at the same time.
24
25 [300]: https://en.wikipedia.org/wiki/Concurrent_Versions_System
26
27 Though innovative and much loved in its time, CVS was not without problems.
28 Among those was a lack of visibility into the project history and the
29 lack of itry tPurcpose Of Fossil
30
31 Fossil is a [distributed version control system (DVCS)][100] written
32 beginning in [2007][105] by the [architect of SQLite][110] for the
33 purpose of managin
34
35 [100]: https://en.wikipedia.org/wiki/Distributed_version_control
36 [105]: /timeline?a=1970-01-01&n1=10
37 [110]: https://sqlite.org/crew.html
38 [115]: https://sqlite.org/
39
40 Though Fossil was originally written specifically to support SQLite,
41 it is now also used by countless other projects. The SQLite architect (drh)
42 is still the top committer to Fossil, but there are also
43 [many other contributors][120].
44
45 [120]: /reports?type=ci&view=byuser
46
47 ## Histor
48 The SQLite project started out using [CVS][300], as CVS was the most
49 commonly used version control system in thant era (circa 2000). CVS
50 was an amazing version control system for its day in that it allowed
51 multiple developers to be editing the same file at the same time.
52
53 [300]: https://en.wikipedia.org/wiki/Concurrent_Versions_System
54
55 T=10
56 [3320030101&n into the project history and the
57 lack of integrated bug tracking. To address these deone was the first VCS to make use of
58 SHA1 to identify artifacts. Monotone stored its content in a SQLite
59 database, which is what brought it to the ssil
60
61 Fossil is a [distributed version control system (DVCS)][100] written
62 beginning in [2007][105] by the [architect of SQLite][110] for the
63 purpose of managing the [SQLite project][115].
64
65 [100]: https://en.wikipedia.org/wiki/Distributed_version_control
66 [105]: /timeline?a=1970-01-01&n1=10
67 [110]: https://sqlite.org/crew.html
68 [115]: https://sqlite.org/
69
70 Though Fossil was originally written specifically to support SQLite,
71 it is now also used by countless other projects. The SQLite architect (drh)
72 is still the top co=12&y=ci
+21 -72
--- www/index.wiki
+++ www/index.wiki
@@ -5,18 +5,18 @@
55
<div style='width:200px;float:right;border:2px solid #446979;padding:10px;margin:0px 10px;'>
66
<ul>
77
<li> [/uv/download.html | Download]
88
<li> [./quickstart.wiki | Quick Start]
99
<li> [./build.wiki | Install]
10
-<li> [../COPYRIGHT-BSD2.txt | License]
11
-<li> [./faq.wiki | FAQ]
10
+<li> [https://fossil-scm.org/forum | Support/Forum ]
11
+<li> [./hints.wiki | Tips &amp; Hints]
1212
<li> [./changes.wiki | Change Log]
13
+<li> [../COPYRIGHT-BSD2.txt | License]
14
+<li> [./userlinks.wiki | User inks]
1315
<li> [./hacker-howto.wiki | Hacker How-To]
1416
<li> [./fossil-v-git.wiki | Fossil vs. Git]
15
-<li> [./hints.wiki | Tip &amp; Hints]
1617
<li> [./permutedindex.html | Documentation Index]
17
-<li> [https://fossil-scm.org/forum | Forum ]
1818
</ul>
1919
<img src="fossil3.gif" align="center">
2020
</div>
2121
2222
<p>Fossil is a simple, high-reliability, distributed software configuration
@@ -83,73 +83,22 @@
8383
the repository are consistent prior to each commit.
8484
8585
8. <b>Free and Open-Source</b> - Uses the [../COPYRIGHT-BSD2.txt|2-clause BSD license].
8686
8787
<hr>
88
-<h3>Links For Fossil Users:</h3>
89
-
90
- * [./permutedindex.html | Documentation index] with [/search?c=d | full text search].
91
- * [./reviews.wiki | Testimonials] from satisfied Fossil users and
92
- [./quotes.wiki | Quotes] about Fossil and other DVCSes.
93
- * [./faq.wiki | Frequently Asked Questions]
94
- * The [./concepts.wiki | concepts] behind Fossil.
95
- [./whyusefossil.wiki#definitions | Another viewpoint].
96
- * [./quickstart.wiki | Quick Start] guide to using Fossil.
97
- * [./qandc.wiki | Questions &amp; Criticisms] directed at Fossil.
98
- * [./build.wiki | Compiling and Installing]
99
- * Fossil supports [./embeddeddoc.wiki | embedded documentation]
100
- that is versioned along with project source code.
101
- * Fossil uses an [./fileformat.wiki | enduring file format] that is
102
- designed to be readable, searchable, and extensible by people
103
- not yet born.
104
- * A tutorial on [./branching.wiki | branching], what it means and how
105
- to do it using Fossil.
106
- * The [./selfcheck.wiki | automatic self-check] mechanism
107
- helps insure project integrity.
108
- * Fossil contains a [./wikitheory.wiki | built-in wiki].
109
- * An [./event.wiki | Event] is a special kind of wiki page associated
110
- with a point in time rather than a name.
111
- * [./settings.wiki | Settings] control the behaviour of Fossil.
112
- * [./ssl.wiki | Use SSL] to encrypt communication with the server.
113
- * There is a
114
- [http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users | mailing list]
115
- (with publicly readable
116
- [http://www.mail-archive.com/[email protected] | archives])
117
- available for discussing Fossil issues.
118
- * [./stats.wiki | Performance statistics] taken from real-world projects
119
- hosted on Fossil.
120
- * How to [./shunning.wiki | delete content] from a Fossil repository.
121
- * How Fossil does [./password.wiki | password management].
122
- * On-line [/help | help].
123
- * Documentation on the
124
- [http://www.sqliteconcepts.org/THManual.pdf | TH1 scripting language],
125
- used to customize [./custom_ticket.wiki | ticketing], and several other
126
- subsystems, including [./customskin.md | theming].
127
- * List of [./th1.md | TH1 commands provided by Fossil itself] that expose
128
- its key functionality to TH1 scripts.
129
- * List of [./th1-hooks.md | TH1 hooks exposed by Fossil] that enable
130
- customization of commands and web pages.
131
- * A free hosting server for Fossil repositories is available at
132
- [http://chiselapp.com/].
133
- * How to [./server/ | set up a server] for your repository.
134
- * Customizing the [./custom_ticket.wiki | ticket system].
135
- * Methods to [./checkin_names.wiki | identify a specific check-in].
136
- * [./inout.wiki | Import and export] from and to Git.
137
- * [./fossil-v-git.wiki | Fossil versus Git].
138
- * [./fiveminutes.wiki | Up and running in 5 minutes as a single user]
139
- (contributed by Gilles Ganault on 2013-01-08).
140
- * [./antibot.wiki | How Fossil defends against abuse by spiders and bots].
141
-
142
-<h3>Links For Fossil Developers:</h3>
143
-
144
- * [./contribute.wiki | Contributing] code or documentation to the
145
- Fossil project.
146
- * [./theory1.wiki | Thoughts On The Design Of Fossil].
147
- * [./pop.wiki | Principles Of Operation]
148
- * [./tech_overview.wiki | A Technical Overview Of Fossil].
149
- * The [./fileformat.wiki | file format] used by every content
150
- file stored in the repository.
151
- * The [./delta_format.wiki | format of deltas] used to
152
- efficiently store changes between file revisions.
153
- * The [./delta_encoder_algorithm.wiki | encoder algorithm] used to
154
- efficiently generate deltas.
155
- * The [./sync.wiki | synchronization protocol].
88
+<h3>Latest Release: 2.10 (2019-10-04)</h3>
89
+
90
+ * [/uv/download.html|Download]
91
+ * [./changes.wiki#v2_10|Change Summary]
92
+
93
+<hr>
94
+<h3>Quick Start</h3>
95
+
96
+ 1. [/uv/download.html|Download] or install using a package manager or
97
+ [./build.wiki|compile from sources].
98
+ 2. <tt>fossil init</tt> <i>new-repository</i>
99
+ 3. <tt>fossil open</tt> <i>new-repository</i>
100
+ 4. <tt>fossil add</tt> <i>files-or-directories</i>
101
+ 5. <tt>fossil commit -m</tt> "<i>commit message</i>"
102
+ 6. <tt>fossil ui</tt>
103
+ 7. Repeat steps 4, 5, and 6, in any order, as necessary.
104
+ See the [./quickstart.wiki|Quick Start Guide] for more detail.
156105
--- www/index.wiki
+++ www/index.wiki
@@ -5,18 +5,18 @@
5 <div style='width:200px;float:right;border:2px solid #446979;padding:10px;margin:0px 10px;'>
6 <ul>
7 <li> [/uv/download.html | Download]
8 <li> [./quickstart.wiki | Quick Start]
9 <li> [./build.wiki | Install]
10 <li> [../COPYRIGHT-BSD2.txt | License]
11 <li> [./faq.wiki | FAQ]
12 <li> [./changes.wiki | Change Log]
 
 
13 <li> [./hacker-howto.wiki | Hacker How-To]
14 <li> [./fossil-v-git.wiki | Fossil vs. Git]
15 <li> [./hints.wiki | Tip &amp; Hints]
16 <li> [./permutedindex.html | Documentation Index]
17 <li> [https://fossil-scm.org/forum | Forum ]
18 </ul>
19 <img src="fossil3.gif" align="center">
20 </div>
21
22 <p>Fossil is a simple, high-reliability, distributed software configuration
@@ -83,73 +83,22 @@
83 the repository are consistent prior to each commit.
84
85 8. <b>Free and Open-Source</b> - Uses the [../COPYRIGHT-BSD2.txt|2-clause BSD license].
86
87 <hr>
88 <h3>Links For Fossil Users:</h3>
89
90 * [./permutedindex.html | Documentation index] with [/search?c=d | full text search].
91 * [./reviews.wiki | Testimonials] from satisfied Fossil users and
92 [./quotes.wiki | Quotes] about Fossil and other DVCSes.
93 * [./faq.wiki | Frequently Asked Questions]
94 * The [./concepts.wiki | concepts] behind Fossil.
95 [./whyusefossil.wiki#definitions | Another viewpoint].
96 * [./quickstart.wiki | Quick Start] guide to using Fossil.
97 * [./qandc.wiki | Questions &amp; Criticisms] directed at Fossil.
98 * [./build.wiki | Compiling and Installing]
99 * Fossil supports [./embeddeddoc.wiki | embedded documentation]
100 that is versioned along with project source code.
101 * Fossil uses an [./fileformat.wiki | enduring file format] that is
102 designed to be readable, searchable, and extensible by people
103 not yet born.
104 * A tutorial on [./branching.wiki | branching], what it means and how
105 to do it using Fossil.
106 * The [./selfcheck.wiki | automatic self-check] mechanism
107 helps insure project integrity.
108 * Fossil contains a [./wikitheory.wiki | built-in wiki].
109 * An [./event.wiki | Event] is a special kind of wiki page associated
110 with a point in time rather than a name.
111 * [./settings.wiki | Settings] control the behaviour of Fossil.
112 * [./ssl.wiki | Use SSL] to encrypt communication with the server.
113 * There is a
114 [http://lists.fossil-scm.org:8080/cgi-bin/mailman/listinfo/fossil-users | mailing list]
115 (with publicly readable
116 [http://www.mail-archive.com/[email protected] | archives])
117 available for discussing Fossil issues.
118 * [./stats.wiki | Performance statistics] taken from real-world projects
119 hosted on Fossil.
120 * How to [./shunning.wiki | delete content] from a Fossil repository.
121 * How Fossil does [./password.wiki | password management].
122 * On-line [/help | help].
123 * Documentation on the
124 [http://www.sqliteconcepts.org/THManual.pdf | TH1 scripting language],
125 used to customize [./custom_ticket.wiki | ticketing], and several other
126 subsystems, including [./customskin.md | theming].
127 * List of [./th1.md | TH1 commands provided by Fossil itself] that expose
128 its key functionality to TH1 scripts.
129 * List of [./th1-hooks.md | TH1 hooks exposed by Fossil] that enable
130 customization of commands and web pages.
131 * A free hosting server for Fossil repositories is available at
132 [http://chiselapp.com/].
133 * How to [./server/ | set up a server] for your repository.
134 * Customizing the [./custom_ticket.wiki | ticket system].
135 * Methods to [./checkin_names.wiki | identify a specific check-in].
136 * [./inout.wiki | Import and export] from and to Git.
137 * [./fossil-v-git.wiki | Fossil versus Git].
138 * [./fiveminutes.wiki | Up and running in 5 minutes as a single user]
139 (contributed by Gilles Ganault on 2013-01-08).
140 * [./antibot.wiki | How Fossil defends against abuse by spiders and bots].
141
142 <h3>Links For Fossil Developers:</h3>
143
144 * [./contribute.wiki | Contributing] code or documentation to the
145 Fossil project.
146 * [./theory1.wiki | Thoughts On The Design Of Fossil].
147 * [./pop.wiki | Principles Of Operation]
148 * [./tech_overview.wiki | A Technical Overview Of Fossil].
149 * The [./fileformat.wiki | file format] used by every content
150 file stored in the repository.
151 * The [./delta_format.wiki | format of deltas] used to
152 efficiently store changes between file revisions.
153 * The [./delta_encoder_algorithm.wiki | encoder algorithm] used to
154 efficiently generate deltas.
155 * The [./sync.wiki | synchronization protocol].
156
--- www/index.wiki
+++ www/index.wiki
@@ -5,18 +5,18 @@
5 <div style='width:200px;float:right;border:2px solid #446979;padding:10px;margin:0px 10px;'>
6 <ul>
7 <li> [/uv/download.html | Download]
8 <li> [./quickstart.wiki | Quick Start]
9 <li> [./build.wiki | Install]
10 <li> [https://fossil-scm.org/forum | Support/Forum ]
11 <li> [./hints.wiki | Tips &amp; Hints]
12 <li> [./changes.wiki | Change Log]
13 <li> [../COPYRIGHT-BSD2.txt | License]
14 <li> [./userlinks.wiki | User inks]
15 <li> [./hacker-howto.wiki | Hacker How-To]
16 <li> [./fossil-v-git.wiki | Fossil vs. Git]
 
17 <li> [./permutedindex.html | Documentation Index]
 
18 </ul>
19 <img src="fossil3.gif" align="center">
20 </div>
21
22 <p>Fossil is a simple, high-reliability, distributed software configuration
@@ -83,73 +83,22 @@
83 the repository are consistent prior to each commit.
84
85 8. <b>Free and Open-Source</b> - Uses the [../COPYRIGHT-BSD2.txt|2-clause BSD license].
86
87 <hr>
88 <h3>Latest Release: 2.10 (2019-10-04)</h3>
89
90 * [/uv/download.html|Download]
91 * [./changes.wiki#v2_10|Change Summary]
92
93 <hr>
94 <h3>Quick Start</h3>
95
96 1. [/uv/download.html|Download] or install using a package manager or
97 [./build.wiki|compile from sources].
98 2. <tt>fossil init</tt> <i>new-repository</i>
99 3. <tt>fossil open</tt> <i>new-repository</i>
100 4. <tt>fossil add</tt> <i>files-or-directories</i>
101 5. <tt>fossil commit -m</tt> "<i>commit message</i>"
102 6. <tt>fossil ui</tt>
103 7. Repeat steps 4, 5, and 6, in any order, as necessary.
104 See the [./quickstart.wiki|Quick Start Guide] for more detail.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
--- www/mirrorlimitations.md
+++ www/mirrorlimitations.md
@@ -9,52 +9,74 @@
99
Fossil is not included in an export to Git.
1010
1111
## (1) Wiki, Tickets, Technotes, Forum
1212
1313
Git only supports version control. The additional features of Fossil such
14
-as Wiki, Tickets, Technotes, and the Forum are not supported in Git and
14
+as Wiki, Tickets, Technotes, and the Forum are not supported in Git,
1515
so those features are not included in an export.
16
+
17
+Third-party Git based tooling may add some of these features (e.g.
18
+GitHub, GitLab) but because their data are not stored in the Git
19
+blockchain, there is no single destination for Fossil to convert its
20
+equivalent data *to*. For instance, Fossil tickets do not become GitHub
21
+issues, because that is a proprietary feature of GitHub separate from
22
+Git proper, stored outside the blockchain on the GitHub servers.
23
+
24
+You can also see the problem in its inverse case: you do not get a copy
25
+of your GitHub issues when cloning the Git repository. You *do* get the
26
+Fossil tickets, wiki, forum posts, etc. when cloning a remote Fossil
27
+repo.
1628
1729
## (2) Cherrypick Merges
1830
19
-The Git client supports cherrypick merges but does not remember them.
20
-In other words, Git does not record a history of cherrypick merges
21
-in its blockchain.
22
-
23
-Fossil tracks cherrypick merges in its blockchain and display cherrypicks
24
-(as dashed lines) in its timeline ([example](/timeline?c=0a9f12ce6655b7a5)).
25
-But history information of cherrypicks cannot be exported to Git because
26
-there is no way to represent it in the Git.
31
+The Git client supports cherrypick merges but does not record the
32
+cherrypick parent(s).
33
+
34
+Fossil tracks cherrypick merges in its blockchain and displays
35
+cherrypicks in its timeline. (As an example, the dashed lines
36
+[here](/timeline?c=0a9f12ce6655b7a5) are cherrypicks.) Because Git does
37
+not have a way to represent this same information in its blockchain, the
38
+history of Fossil cherrypicks cannot be exported to Git, only their
39
+direct effects on the managed file data.
2740
2841
## (3) Named Branches
2942
3043
Git has only limited support for named branches. Git identifies the head
3144
check-in of each branch. Depending on the check-in graph topology, this
3245
is sufficient to infer the branch for many historical check-ins as well.
3346
However, complex histories with lots of cross-merging
3447
can lead to ambiguities. Fossil keeps
35
-track of historical branch names unambiguously.
36
-But the extra details about branch names that Fossil keeps
48
+track of historical branch names unambiguously,
49
+but the extra details about branch names that Fossil keeps
3750
at hand cannot be exported to Git.
3851
3952
## (4) Non-unique Tags
4053
41
-Git requires tags to be unique. Each tag must refer to exactly one
54
+Git requires tags to be unique: each tag must refer to exactly one
4255
check-in. Fossil does not have this restriction, and so it is common
4356
in Fossil to tag multiple check-ins with the same name. For example,
44
-it is common in Fossil to tag every release check-in with the "release"
45
-tag, so that all historical releases can be found all at once.
46
-([example](/timeline?t=release))
57
+it is common in Fossil to tag each check-in creating a release both
58
+with a unique version tag *and* a common tag like "release"
59
+so that all historical releases can be found at once.
60
+([Example](/timeline?t=release).)
4761
4862
Git does not allow this. The "release" tag must refer to just one
4963
check-in. The work-around is that the non-unique tag in the Git export is
5064
made to refer to only the most recent check-in with that tag.
5165
66
+This is why the ["release" tag view][ghrtv] in the GitHub mirror of this
67
+repository shows only the latest release version; contrast the prior
68
+example. Both URLs are asking the repository the same question, but
69
+because of Git's relatively impoverished data model, it cannot give the
70
+same answer that Fossil does.
71
+
72
+[ghrtv]: https://github.com/drhsqlite/fossil-mirror/tree/release
73
+
5274
## (5) Amendments To Check-ins
5375
5476
Check-ins are immutable in both Fossil and Git.
55
-However, Fossil has a mechanism by which tags can be added
77
+However, Fossil has a mechanism by which tags can be added to
5678
its blockchain to provide after-the-fact corrections to prior check-ins.
5779
5880
For example, tags can be added to check-ins that correct typos in the
5981
check-in comment. The original check-in is immutable and so the
6082
original comment is preserved in addition to the correction. But
6183
--- www/mirrorlimitations.md
+++ www/mirrorlimitations.md
@@ -9,52 +9,74 @@
9 Fossil is not included in an export to Git.
10
11 ## (1) Wiki, Tickets, Technotes, Forum
12
13 Git only supports version control. The additional features of Fossil such
14 as Wiki, Tickets, Technotes, and the Forum are not supported in Git and
15 so those features are not included in an export.
 
 
 
 
 
 
 
 
 
 
 
 
16
17 ## (2) Cherrypick Merges
18
19 The Git client supports cherrypick merges but does not remember them.
20 In other words, Git does not record a history of cherrypick merges
21 in its blockchain.
22
23 Fossil tracks cherrypick merges in its blockchain and display cherrypicks
24 (as dashed lines) in its timeline ([example](/timeline?c=0a9f12ce6655b7a5)).
25 But history information of cherrypicks cannot be exported to Git because
26 there is no way to represent it in the Git.
 
27
28 ## (3) Named Branches
29
30 Git has only limited support for named branches. Git identifies the head
31 check-in of each branch. Depending on the check-in graph topology, this
32 is sufficient to infer the branch for many historical check-ins as well.
33 However, complex histories with lots of cross-merging
34 can lead to ambiguities. Fossil keeps
35 track of historical branch names unambiguously.
36 But the extra details about branch names that Fossil keeps
37 at hand cannot be exported to Git.
38
39 ## (4) Non-unique Tags
40
41 Git requires tags to be unique. Each tag must refer to exactly one
42 check-in. Fossil does not have this restriction, and so it is common
43 in Fossil to tag multiple check-ins with the same name. For example,
44 it is common in Fossil to tag every release check-in with the "release"
45 tag, so that all historical releases can be found all at once.
46 ([example](/timeline?t=release))
 
47
48 Git does not allow this. The "release" tag must refer to just one
49 check-in. The work-around is that the non-unique tag in the Git export is
50 made to refer to only the most recent check-in with that tag.
51
 
 
 
 
 
 
 
 
52 ## (5) Amendments To Check-ins
53
54 Check-ins are immutable in both Fossil and Git.
55 However, Fossil has a mechanism by which tags can be added
56 its blockchain to provide after-the-fact corrections to prior check-ins.
57
58 For example, tags can be added to check-ins that correct typos in the
59 check-in comment. The original check-in is immutable and so the
60 original comment is preserved in addition to the correction. But
61
--- www/mirrorlimitations.md
+++ www/mirrorlimitations.md
@@ -9,52 +9,74 @@
9 Fossil is not included in an export to Git.
10
11 ## (1) Wiki, Tickets, Technotes, Forum
12
13 Git only supports version control. The additional features of Fossil such
14 as Wiki, Tickets, Technotes, and the Forum are not supported in Git,
15 so those features are not included in an export.
16
17 Third-party Git based tooling may add some of these features (e.g.
18 GitHub, GitLab) but because their data are not stored in the Git
19 blockchain, there is no single destination for Fossil to convert its
20 equivalent data *to*. For instance, Fossil tickets do not become GitHub
21 issues, because that is a proprietary feature of GitHub separate from
22 Git proper, stored outside the blockchain on the GitHub servers.
23
24 You can also see the problem in its inverse case: you do not get a copy
25 of your GitHub issues when cloning the Git repository. You *do* get the
26 Fossil tickets, wiki, forum posts, etc. when cloning a remote Fossil
27 repo.
28
29 ## (2) Cherrypick Merges
30
31 The Git client supports cherrypick merges but does not record the
32 cherrypick parent(s).
33
34 Fossil tracks cherrypick merges in its blockchain and displays
35 cherrypicks in its timeline. (As an example, the dashed lines
36 [here](/timeline?c=0a9f12ce6655b7a5) are cherrypicks.) Because Git does
37 not have a way to represent this same information in its blockchain, the
38 history of Fossil cherrypicks cannot be exported to Git, only their
39 direct effects on the managed file data.
40
41 ## (3) Named Branches
42
43 Git has only limited support for named branches. Git identifies the head
44 check-in of each branch. Depending on the check-in graph topology, this
45 is sufficient to infer the branch for many historical check-ins as well.
46 However, complex histories with lots of cross-merging
47 can lead to ambiguities. Fossil keeps
48 track of historical branch names unambiguously,
49 but the extra details about branch names that Fossil keeps
50 at hand cannot be exported to Git.
51
52 ## (4) Non-unique Tags
53
54 Git requires tags to be unique: each tag must refer to exactly one
55 check-in. Fossil does not have this restriction, and so it is common
56 in Fossil to tag multiple check-ins with the same name. For example,
57 it is common in Fossil to tag each check-in creating a release both
58 with a unique version tag *and* a common tag like "release"
59 so that all historical releases can be found at once.
60 ([Example](/timeline?t=release).)
61
62 Git does not allow this. The "release" tag must refer to just one
63 check-in. The work-around is that the non-unique tag in the Git export is
64 made to refer to only the most recent check-in with that tag.
65
66 This is why the ["release" tag view][ghrtv] in the GitHub mirror of this
67 repository shows only the latest release version; contrast the prior
68 example. Both URLs are asking the repository the same question, but
69 because of Git's relatively impoverished data model, it cannot give the
70 same answer that Fossil does.
71
72 [ghrtv]: https://github.com/drhsqlite/fossil-mirror/tree/release
73
74 ## (5) Amendments To Check-ins
75
76 Check-ins are immutable in both Fossil and Git.
77 However, Fossil has a mechanism by which tags can be added to
78 its blockchain to provide after-the-fact corrections to prior check-ins.
79
80 For example, tags can be added to check-ins that correct typos in the
81 check-in comment. The original check-in is immutable and so the
82 original comment is preserved in addition to the correction. But
83
+6 -3
--- www/mkindex.tcl
+++ www/mkindex.tcl
@@ -49,16 +49,18 @@
4949
foss-cklist.wiki {Checklist For Successful Open-Source Projects}
5050
fossil-from-msvc.wiki {Integrating Fossil in the Microsoft Express 2010 IDE}
5151
fossil_prompt.wiki {Fossilized Bash Prompt}
5252
fossil-v-git.wiki {Fossil Versus Git}
5353
globs.md {File Name Glob Patterns}
54
+ gitusers.md {Hints For Users With Git Experience}
5455
grep.md {Fossil grep vs POSIX grep}
5556
hacker-howto.wiki {Hacker How-To}
5657
hacker-howto.wiki {Fossil Developers Guide}
5758
hashpolicy.wiki {Hash Policy: Choosing Between SHA1 and SHA3-256}
5859
/help {Lists of Commands and Webpages}
5960
hints.wiki {Fossil Tips And Usage Hints}
61
+ history.md {The Purpose And History Of Fossil}
6062
index.wiki {Home Page}
6163
inout.wiki {Import And Export To And From Git}
6264
image-format-vs-repo-size.md {Image Format vs Fossil Repo Size}
6365
javascript.md {Use of JavaScript in Fossil}
6466
makefile.wiki {The Fossil Build Process}
@@ -135,17 +137,18 @@
135137
</form>
136138
</center>
137139
<h2>Primary Documents:</h2>
138140
<ul>
139141
<li> <a href='quickstart.wiki'>Quick-start Guide</a>
140
-<li> <a href='faq.wiki'>FAQ</a>
142
+<li> <a href='history.md'>Purpose and History of Fossil</a>
141143
<li> <a href='build.wiki'>Compiling and installing Fossil</a>
142144
<li> <a href='../COPYRIGHT-BSD2.txt'>License</a>
145
+<li> <a href='$ROOT/help'>List of commands, web-pages, and settings</a>
146
+<li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a>
147
+<li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a>
143148
<li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's
144149
book</a>
145
-<li> <a href='$ROOT/help'>List of commands, web-pages, and settings</a>
146
-<li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a>
147150
</ul>
148151
<a name="pindex"></a>
149152
<h2>Permuted Index:</h2>
150153
<ul>}
151154
foreach entry $permindex {
152155
--- www/mkindex.tcl
+++ www/mkindex.tcl
@@ -49,16 +49,18 @@
49 foss-cklist.wiki {Checklist For Successful Open-Source Projects}
50 fossil-from-msvc.wiki {Integrating Fossil in the Microsoft Express 2010 IDE}
51 fossil_prompt.wiki {Fossilized Bash Prompt}
52 fossil-v-git.wiki {Fossil Versus Git}
53 globs.md {File Name Glob Patterns}
 
54 grep.md {Fossil grep vs POSIX grep}
55 hacker-howto.wiki {Hacker How-To}
56 hacker-howto.wiki {Fossil Developers Guide}
57 hashpolicy.wiki {Hash Policy: Choosing Between SHA1 and SHA3-256}
58 /help {Lists of Commands and Webpages}
59 hints.wiki {Fossil Tips And Usage Hints}
 
60 index.wiki {Home Page}
61 inout.wiki {Import And Export To And From Git}
62 image-format-vs-repo-size.md {Image Format vs Fossil Repo Size}
63 javascript.md {Use of JavaScript in Fossil}
64 makefile.wiki {The Fossil Build Process}
@@ -135,17 +137,18 @@
135 </form>
136 </center>
137 <h2>Primary Documents:</h2>
138 <ul>
139 <li> <a href='quickstart.wiki'>Quick-start Guide</a>
140 <li> <a href='faq.wiki'>FAQ</a>
141 <li> <a href='build.wiki'>Compiling and installing Fossil</a>
142 <li> <a href='../COPYRIGHT-BSD2.txt'>License</a>
 
 
 
143 <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's
144 book</a>
145 <li> <a href='$ROOT/help'>List of commands, web-pages, and settings</a>
146 <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a>
147 </ul>
148 <a name="pindex"></a>
149 <h2>Permuted Index:</h2>
150 <ul>}
151 foreach entry $permindex {
152
--- www/mkindex.tcl
+++ www/mkindex.tcl
@@ -49,16 +49,18 @@
49 foss-cklist.wiki {Checklist For Successful Open-Source Projects}
50 fossil-from-msvc.wiki {Integrating Fossil in the Microsoft Express 2010 IDE}
51 fossil_prompt.wiki {Fossilized Bash Prompt}
52 fossil-v-git.wiki {Fossil Versus Git}
53 globs.md {File Name Glob Patterns}
54 gitusers.md {Hints For Users With Git Experience}
55 grep.md {Fossil grep vs POSIX grep}
56 hacker-howto.wiki {Hacker How-To}
57 hacker-howto.wiki {Fossil Developers Guide}
58 hashpolicy.wiki {Hash Policy: Choosing Between SHA1 and SHA3-256}
59 /help {Lists of Commands and Webpages}
60 hints.wiki {Fossil Tips And Usage Hints}
61 history.md {The Purpose And History Of Fossil}
62 index.wiki {Home Page}
63 inout.wiki {Import And Export To And From Git}
64 image-format-vs-repo-size.md {Image Format vs Fossil Repo Size}
65 javascript.md {Use of JavaScript in Fossil}
66 makefile.wiki {The Fossil Build Process}
@@ -135,17 +137,18 @@
137 </form>
138 </center>
139 <h2>Primary Documents:</h2>
140 <ul>
141 <li> <a href='quickstart.wiki'>Quick-start Guide</a>
142 <li> <a href='history.md'>Purpose and History of Fossil</a>
143 <li> <a href='build.wiki'>Compiling and installing Fossil</a>
144 <li> <a href='../COPYRIGHT-BSD2.txt'>License</a>
145 <li> <a href='$ROOT/help'>List of commands, web-pages, and settings</a>
146 <li> <a href='userlinks.wiki'>Miscellaneous Docs for Fossil Users</a>
147 <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a>
148 <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's
149 book</a>
 
 
150 </ul>
151 <a name="pindex"></a>
152 <h2>Permuted Index:</h2>
153 <ul>}
154 foreach entry $permindex {
155
--- www/permutedindex.html
+++ www/permutedindex.html
@@ -7,17 +7,18 @@
77
</form>
88
</center>
99
<h2>Primary Documents:</h2>
1010
<ul>
1111
<li> <a href='quickstart.wiki'>Quick-start Guide</a>
12
-<li> <a href='faq.wiki'>FAQ</a>
12
+<li> <a href='history.md'>Purpose and History of Fossil</a>
1313
<li> <a href='build.wiki'>Compiling and installing Fossil</a>
1414
<li> <a href='../COPYRIGHT-BSD2.txt'>License</a>
15
+<li> <a href='$ROOT/help'>List of commands, web-pages, and settings</a>
16
+<li> <a href='userlinks.wiki'>Key Docs for Fossil Users</a>
17
+<li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a>
1518
<li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's
1619
book</a>
17
-<li> <a href='$ROOT/help'>List of commands, web-pages, and settings</a>
18
-<li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a>
1920
</ul>
2021
<a name="pindex"></a>
2122
<h2>Permuted Index:</h2>
2223
<ul>
2324
<li><a href="fiveminutes.wiki">5 Minutes as a Single User &mdash; Up and Running in</a></li>
@@ -104,10 +105,11 @@
104105
<li><a href="delta_encoder_algorithm.wiki">Encoding Algorithm &mdash; Fossil Delta</a></li>
105106
<li><a href="encryptedrepos.wiki">Encrypted Repositories &mdash; How To Use</a></li>
106107
<li><a href="env-opts.md"><b>Environment Variables and Global Options</b></a></li>
107108
<li><a href="event.wiki"><b>Events</b></a></li>
108109
<li><a href="webpage-ex.md">Examples &mdash; Webpage</a></li>
110
+<li><a href="gitusers.md">Experience &mdash; Hints For Users With Git</a></li>
109111
<li><a href="inout.wiki">Export To And From Git &mdash; Import And</a></li>
110112
<li><a href="fossil-from-msvc.wiki">Express 2010 IDE &mdash; Integrating Fossil in the Microsoft</a></li>
111113
<li><a href="serverext.wiki">Extensions &mdash; CGI Server</a></li>
112114
<li><a href="serverext.wiki">Extensions To A Fossil Server Using CGI Scripts &mdash; Adding</a></li>
113115
<li><a href="adding_code.wiki">Features To Fossil &mdash; Adding New</a></li>
@@ -141,10 +143,11 @@
141143
<li><a href="fossil_prompt.wiki"><b>Fossilized Bash Prompt</b></a></li>
142144
<li><a href="faq.wiki"><b>Frequently Asked Questions</b></a></li>
143145
<li><a href="quotes.wiki">General &mdash; Quotes: What People Are Saying About Fossil, Git, and DVCSes in</a></li>
144146
<li><a href="fossil-v-git.wiki">Git &mdash; Fossil Versus</a></li>
145147
<li><a href="inout.wiki">Git &mdash; Import And Export To And From</a></li>
148
+<li><a href="gitusers.md">Git Experience &mdash; Hints For Users With</a></li>
146149
<li><a href="mirrorlimitations.md">Git Mirrors &mdash; Limitations On</a></li>
147150
<li><a href="quotes.wiki">Git, and DVCSes in General &mdash; Quotes: What People Are Saying About Fossil,</a></li>
148151
<li><a href="mirrortogithub.md">GitHub &mdash; How To Mirror A Fossil Repository On</a></li>
149152
<li><a href="globs.md">Glob Patterns &mdash; File Name</a></li>
150153
<li><a href="env-opts.md">Global Options &mdash; Environment Variables and</a></li>
@@ -157,10 +160,12 @@
157160
<li><a href="hacker-howto.wiki"><b>Hacker How-To</b></a></li>
158161
<li><a href="adding_code.wiki"><b>Hacking Fossil</b></a></li>
159162
<li><a href="rebaseharm.md">Harmful &mdash; Rebase Considered</a></li>
160163
<li><a href="hashpolicy.wiki"><b>Hash Policy: Choosing Between SHA1 and SHA3-256</b></a></li>
161164
<li><a href="hints.wiki">Hints &mdash; Fossil Tips And Usage</a></li>
165
+<li><a href="gitusers.md"><b>Hints For Users With Git Experience</b></a></li>
166
+<li><a href="history.md">History Of Fossil &mdash; The Purpose And</a></li>
162167
<li><a href="index.wiki"><b>Home Page</b></a></li>
163168
<li><a href="selfhost.wiki">Hosting Repositories &mdash; Fossil Self</a></li>
164169
<li><a href="aboutcgi.wiki"><b>How CGI Works In Fossil</b></a></li>
165170
<li><a href="aboutdownload.wiki"><b>How The Download Page Works</b></a></li>
166171
<li><a href="server/"><b>How To Configure A Fossil Server</b></a></li>
@@ -221,10 +226,11 @@
221226
<li><a href="foss-cklist.wiki">Projects &mdash; Checklist For Successful Open-Source</a></li>
222227
<li><a href="childprojects.wiki">Projects &mdash; Child</a></li>
223228
<li><a href="fossil_prompt.wiki">Prompt &mdash; Fossilized Bash</a></li>
224229
<li><a href="sync.wiki">Protocol &mdash; The Fossil Sync</a></li>
225230
<li><a href="tls-nginx.md"><b>Proxying Fossil via HTTPS with nginx</b></a></li>
231
+<li><a href="history.md">Purpose And History Of Fossil &mdash; The</a></li>
226232
<li><a href="faq.wiki">Questions &mdash; Frequently Asked</a></li>
227233
<li><a href="qandc.wiki"><b>Questions And Criticisms</b></a></li>
228234
<li><a href="quickstart.wiki">Quick Start Guide &mdash; Fossil</a></li>
229235
<li><a href="quotes.wiki"><b>Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</b></a></li>
230236
<li><a href="rebaseharm.md"><b>Rebase Considered Harmful</b></a></li>
@@ -279,10 +285,11 @@
279285
<li><a href="defcsp.md"><b>The Default Content Security Policy</b></a></li>
280286
<li><a href="makefile.wiki"><b>The Fossil Build Process</b></a></li>
281287
<li><a href="sync.wiki"><b>The Fossil Sync Protocol</b></a></li>
282288
<li><a href="tickets.wiki"><b>The Fossil Ticket System</b></a></li>
283289
<li><a href="webui.wiki"><b>The Fossil Web Interface</b></a></li>
290
+<li><a href="history.md"><b>The Purpose And History Of Fossil</b></a></li>
284291
<li><a href="th1.md"><b>The TH1 Scripting Language</b></a></li>
285292
<li><a href="customskin.md"><b>Theming: Customizing The Appearance of Web Pages</b></a></li>
286293
<li><a href="customgraph.md"><b>Theming: Customizing the Timeline Graph</b></a></li>
287294
<li><a href="theory1.wiki"><b>Thoughts On The Design Of The Fossil DVCS</b></a></li>
288295
<li><a href="custom_ticket.wiki">Ticket System &mdash; Customizing The</a></li>
@@ -298,10 +305,11 @@
298305
<li><a href="javascript.md"><b>Use of JavaScript in Fossil</b></a></li>
299306
<li><a href="fiveminutes.wiki">User &mdash; Up and Running in 5 Minutes as a Single</a></li>
300307
<li><a href="caps/">User Capabilities &mdash; Administering</a></li>
301308
<li><a href="caps/ref.html"><b>User Capability Reference</b></a></li>
302309
<li><a href="caps/admin-v-setup.md">Users &mdash; Differences Between Setup and Admin</a></li>
310
+<li><a href="gitusers.md">Users With Git Experience &mdash; Hints For</a></li>
303311
<li><a href="serverext.wiki">Using CGI Scripts &mdash; Adding Extensions To A Fossil Server</a></li>
304312
<li><a href="ssl.wiki"><b>Using SSL with Fossil</b></a></li>
305313
<li><a href="env-opts.md">Variables and Global Options &mdash; Environment</a></li>
306314
<li><a href="whyusefossil.wiki">Version Control &mdash; Benefits Of</a></li>
307315
<li><a href="checkin_names.wiki">Version Names &mdash; Check-in And</a></li>
308316
--- www/permutedindex.html
+++ www/permutedindex.html
@@ -7,17 +7,18 @@
7 </form>
8 </center>
9 <h2>Primary Documents:</h2>
10 <ul>
11 <li> <a href='quickstart.wiki'>Quick-start Guide</a>
12 <li> <a href='faq.wiki'>FAQ</a>
13 <li> <a href='build.wiki'>Compiling and installing Fossil</a>
14 <li> <a href='../COPYRIGHT-BSD2.txt'>License</a>
 
 
 
15 <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's
16 book</a>
17 <li> <a href='$ROOT/help'>List of commands, web-pages, and settings</a>
18 <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a>
19 </ul>
20 <a name="pindex"></a>
21 <h2>Permuted Index:</h2>
22 <ul>
23 <li><a href="fiveminutes.wiki">5 Minutes as a Single User &mdash; Up and Running in</a></li>
@@ -104,10 +105,11 @@
104 <li><a href="delta_encoder_algorithm.wiki">Encoding Algorithm &mdash; Fossil Delta</a></li>
105 <li><a href="encryptedrepos.wiki">Encrypted Repositories &mdash; How To Use</a></li>
106 <li><a href="env-opts.md"><b>Environment Variables and Global Options</b></a></li>
107 <li><a href="event.wiki"><b>Events</b></a></li>
108 <li><a href="webpage-ex.md">Examples &mdash; Webpage</a></li>
 
109 <li><a href="inout.wiki">Export To And From Git &mdash; Import And</a></li>
110 <li><a href="fossil-from-msvc.wiki">Express 2010 IDE &mdash; Integrating Fossil in the Microsoft</a></li>
111 <li><a href="serverext.wiki">Extensions &mdash; CGI Server</a></li>
112 <li><a href="serverext.wiki">Extensions To A Fossil Server Using CGI Scripts &mdash; Adding</a></li>
113 <li><a href="adding_code.wiki">Features To Fossil &mdash; Adding New</a></li>
@@ -141,10 +143,11 @@
141 <li><a href="fossil_prompt.wiki"><b>Fossilized Bash Prompt</b></a></li>
142 <li><a href="faq.wiki"><b>Frequently Asked Questions</b></a></li>
143 <li><a href="quotes.wiki">General &mdash; Quotes: What People Are Saying About Fossil, Git, and DVCSes in</a></li>
144 <li><a href="fossil-v-git.wiki">Git &mdash; Fossil Versus</a></li>
145 <li><a href="inout.wiki">Git &mdash; Import And Export To And From</a></li>
 
146 <li><a href="mirrorlimitations.md">Git Mirrors &mdash; Limitations On</a></li>
147 <li><a href="quotes.wiki">Git, and DVCSes in General &mdash; Quotes: What People Are Saying About Fossil,</a></li>
148 <li><a href="mirrortogithub.md">GitHub &mdash; How To Mirror A Fossil Repository On</a></li>
149 <li><a href="globs.md">Glob Patterns &mdash; File Name</a></li>
150 <li><a href="env-opts.md">Global Options &mdash; Environment Variables and</a></li>
@@ -157,10 +160,12 @@
157 <li><a href="hacker-howto.wiki"><b>Hacker How-To</b></a></li>
158 <li><a href="adding_code.wiki"><b>Hacking Fossil</b></a></li>
159 <li><a href="rebaseharm.md">Harmful &mdash; Rebase Considered</a></li>
160 <li><a href="hashpolicy.wiki"><b>Hash Policy: Choosing Between SHA1 and SHA3-256</b></a></li>
161 <li><a href="hints.wiki">Hints &mdash; Fossil Tips And Usage</a></li>
 
 
162 <li><a href="index.wiki"><b>Home Page</b></a></li>
163 <li><a href="selfhost.wiki">Hosting Repositories &mdash; Fossil Self</a></li>
164 <li><a href="aboutcgi.wiki"><b>How CGI Works In Fossil</b></a></li>
165 <li><a href="aboutdownload.wiki"><b>How The Download Page Works</b></a></li>
166 <li><a href="server/"><b>How To Configure A Fossil Server</b></a></li>
@@ -221,10 +226,11 @@
221 <li><a href="foss-cklist.wiki">Projects &mdash; Checklist For Successful Open-Source</a></li>
222 <li><a href="childprojects.wiki">Projects &mdash; Child</a></li>
223 <li><a href="fossil_prompt.wiki">Prompt &mdash; Fossilized Bash</a></li>
224 <li><a href="sync.wiki">Protocol &mdash; The Fossil Sync</a></li>
225 <li><a href="tls-nginx.md"><b>Proxying Fossil via HTTPS with nginx</b></a></li>
 
226 <li><a href="faq.wiki">Questions &mdash; Frequently Asked</a></li>
227 <li><a href="qandc.wiki"><b>Questions And Criticisms</b></a></li>
228 <li><a href="quickstart.wiki">Quick Start Guide &mdash; Fossil</a></li>
229 <li><a href="quotes.wiki"><b>Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</b></a></li>
230 <li><a href="rebaseharm.md"><b>Rebase Considered Harmful</b></a></li>
@@ -279,10 +285,11 @@
279 <li><a href="defcsp.md"><b>The Default Content Security Policy</b></a></li>
280 <li><a href="makefile.wiki"><b>The Fossil Build Process</b></a></li>
281 <li><a href="sync.wiki"><b>The Fossil Sync Protocol</b></a></li>
282 <li><a href="tickets.wiki"><b>The Fossil Ticket System</b></a></li>
283 <li><a href="webui.wiki"><b>The Fossil Web Interface</b></a></li>
 
284 <li><a href="th1.md"><b>The TH1 Scripting Language</b></a></li>
285 <li><a href="customskin.md"><b>Theming: Customizing The Appearance of Web Pages</b></a></li>
286 <li><a href="customgraph.md"><b>Theming: Customizing the Timeline Graph</b></a></li>
287 <li><a href="theory1.wiki"><b>Thoughts On The Design Of The Fossil DVCS</b></a></li>
288 <li><a href="custom_ticket.wiki">Ticket System &mdash; Customizing The</a></li>
@@ -298,10 +305,11 @@
298 <li><a href="javascript.md"><b>Use of JavaScript in Fossil</b></a></li>
299 <li><a href="fiveminutes.wiki">User &mdash; Up and Running in 5 Minutes as a Single</a></li>
300 <li><a href="caps/">User Capabilities &mdash; Administering</a></li>
301 <li><a href="caps/ref.html"><b>User Capability Reference</b></a></li>
302 <li><a href="caps/admin-v-setup.md">Users &mdash; Differences Between Setup and Admin</a></li>
 
303 <li><a href="serverext.wiki">Using CGI Scripts &mdash; Adding Extensions To A Fossil Server</a></li>
304 <li><a href="ssl.wiki"><b>Using SSL with Fossil</b></a></li>
305 <li><a href="env-opts.md">Variables and Global Options &mdash; Environment</a></li>
306 <li><a href="whyusefossil.wiki">Version Control &mdash; Benefits Of</a></li>
307 <li><a href="checkin_names.wiki">Version Names &mdash; Check-in And</a></li>
308
--- www/permutedindex.html
+++ www/permutedindex.html
@@ -7,17 +7,18 @@
7 </form>
8 </center>
9 <h2>Primary Documents:</h2>
10 <ul>
11 <li> <a href='quickstart.wiki'>Quick-start Guide</a>
12 <li> <a href='history.md'>Purpose and History of Fossil</a>
13 <li> <a href='build.wiki'>Compiling and installing Fossil</a>
14 <li> <a href='../COPYRIGHT-BSD2.txt'>License</a>
15 <li> <a href='$ROOT/help'>List of commands, web-pages, and settings</a>
16 <li> <a href='userlinks.wiki'>Key Docs for Fossil Users</a>
17 <li> <a href='hacker-howto.wiki'>Fossil Developer's Guide</a>
18 <li> <a href='http://www.fossil-scm.org/schimpf-book/home'>Jim Schimpf's
19 book</a>
 
 
20 </ul>
21 <a name="pindex"></a>
22 <h2>Permuted Index:</h2>
23 <ul>
24 <li><a href="fiveminutes.wiki">5 Minutes as a Single User &mdash; Up and Running in</a></li>
@@ -104,10 +105,11 @@
105 <li><a href="delta_encoder_algorithm.wiki">Encoding Algorithm &mdash; Fossil Delta</a></li>
106 <li><a href="encryptedrepos.wiki">Encrypted Repositories &mdash; How To Use</a></li>
107 <li><a href="env-opts.md"><b>Environment Variables and Global Options</b></a></li>
108 <li><a href="event.wiki"><b>Events</b></a></li>
109 <li><a href="webpage-ex.md">Examples &mdash; Webpage</a></li>
110 <li><a href="gitusers.md">Experience &mdash; Hints For Users With Git</a></li>
111 <li><a href="inout.wiki">Export To And From Git &mdash; Import And</a></li>
112 <li><a href="fossil-from-msvc.wiki">Express 2010 IDE &mdash; Integrating Fossil in the Microsoft</a></li>
113 <li><a href="serverext.wiki">Extensions &mdash; CGI Server</a></li>
114 <li><a href="serverext.wiki">Extensions To A Fossil Server Using CGI Scripts &mdash; Adding</a></li>
115 <li><a href="adding_code.wiki">Features To Fossil &mdash; Adding New</a></li>
@@ -141,10 +143,11 @@
143 <li><a href="fossil_prompt.wiki"><b>Fossilized Bash Prompt</b></a></li>
144 <li><a href="faq.wiki"><b>Frequently Asked Questions</b></a></li>
145 <li><a href="quotes.wiki">General &mdash; Quotes: What People Are Saying About Fossil, Git, and DVCSes in</a></li>
146 <li><a href="fossil-v-git.wiki">Git &mdash; Fossil Versus</a></li>
147 <li><a href="inout.wiki">Git &mdash; Import And Export To And From</a></li>
148 <li><a href="gitusers.md">Git Experience &mdash; Hints For Users With</a></li>
149 <li><a href="mirrorlimitations.md">Git Mirrors &mdash; Limitations On</a></li>
150 <li><a href="quotes.wiki">Git, and DVCSes in General &mdash; Quotes: What People Are Saying About Fossil,</a></li>
151 <li><a href="mirrortogithub.md">GitHub &mdash; How To Mirror A Fossil Repository On</a></li>
152 <li><a href="globs.md">Glob Patterns &mdash; File Name</a></li>
153 <li><a href="env-opts.md">Global Options &mdash; Environment Variables and</a></li>
@@ -157,10 +160,12 @@
160 <li><a href="hacker-howto.wiki"><b>Hacker How-To</b></a></li>
161 <li><a href="adding_code.wiki"><b>Hacking Fossil</b></a></li>
162 <li><a href="rebaseharm.md">Harmful &mdash; Rebase Considered</a></li>
163 <li><a href="hashpolicy.wiki"><b>Hash Policy: Choosing Between SHA1 and SHA3-256</b></a></li>
164 <li><a href="hints.wiki">Hints &mdash; Fossil Tips And Usage</a></li>
165 <li><a href="gitusers.md"><b>Hints For Users With Git Experience</b></a></li>
166 <li><a href="history.md">History Of Fossil &mdash; The Purpose And</a></li>
167 <li><a href="index.wiki"><b>Home Page</b></a></li>
168 <li><a href="selfhost.wiki">Hosting Repositories &mdash; Fossil Self</a></li>
169 <li><a href="aboutcgi.wiki"><b>How CGI Works In Fossil</b></a></li>
170 <li><a href="aboutdownload.wiki"><b>How The Download Page Works</b></a></li>
171 <li><a href="server/"><b>How To Configure A Fossil Server</b></a></li>
@@ -221,10 +226,11 @@
226 <li><a href="foss-cklist.wiki">Projects &mdash; Checklist For Successful Open-Source</a></li>
227 <li><a href="childprojects.wiki">Projects &mdash; Child</a></li>
228 <li><a href="fossil_prompt.wiki">Prompt &mdash; Fossilized Bash</a></li>
229 <li><a href="sync.wiki">Protocol &mdash; The Fossil Sync</a></li>
230 <li><a href="tls-nginx.md"><b>Proxying Fossil via HTTPS with nginx</b></a></li>
231 <li><a href="history.md">Purpose And History Of Fossil &mdash; The</a></li>
232 <li><a href="faq.wiki">Questions &mdash; Frequently Asked</a></li>
233 <li><a href="qandc.wiki"><b>Questions And Criticisms</b></a></li>
234 <li><a href="quickstart.wiki">Quick Start Guide &mdash; Fossil</a></li>
235 <li><a href="quotes.wiki"><b>Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</b></a></li>
236 <li><a href="rebaseharm.md"><b>Rebase Considered Harmful</b></a></li>
@@ -279,10 +285,11 @@
285 <li><a href="defcsp.md"><b>The Default Content Security Policy</b></a></li>
286 <li><a href="makefile.wiki"><b>The Fossil Build Process</b></a></li>
287 <li><a href="sync.wiki"><b>The Fossil Sync Protocol</b></a></li>
288 <li><a href="tickets.wiki"><b>The Fossil Ticket System</b></a></li>
289 <li><a href="webui.wiki"><b>The Fossil Web Interface</b></a></li>
290 <li><a href="history.md"><b>The Purpose And History Of Fossil</b></a></li>
291 <li><a href="th1.md"><b>The TH1 Scripting Language</b></a></li>
292 <li><a href="customskin.md"><b>Theming: Customizing The Appearance of Web Pages</b></a></li>
293 <li><a href="customgraph.md"><b>Theming: Customizing the Timeline Graph</b></a></li>
294 <li><a href="theory1.wiki"><b>Thoughts On The Design Of The Fossil DVCS</b></a></li>
295 <li><a href="custom_ticket.wiki">Ticket System &mdash; Customizing The</a></li>
@@ -298,10 +305,11 @@
305 <li><a href="javascript.md"><b>Use of JavaScript in Fossil</b></a></li>
306 <li><a href="fiveminutes.wiki">User &mdash; Up and Running in 5 Minutes as a Single</a></li>
307 <li><a href="caps/">User Capabilities &mdash; Administering</a></li>
308 <li><a href="caps/ref.html"><b>User Capability Reference</b></a></li>
309 <li><a href="caps/admin-v-setup.md">Users &mdash; Differences Between Setup and Admin</a></li>
310 <li><a href="gitusers.md">Users With Git Experience &mdash; Hints For</a></li>
311 <li><a href="serverext.wiki">Using CGI Scripts &mdash; Adding Extensions To A Fossil Server</a></li>
312 <li><a href="ssl.wiki"><b>Using SSL with Fossil</b></a></li>
313 <li><a href="env-opts.md">Variables and Global Options &mdash; Environment</a></li>
314 <li><a href="whyusefossil.wiki">Version Control &mdash; Benefits Of</a></li>
315 <li><a href="checkin_names.wiki">Version Names &mdash; Check-in And</a></li>
316
+15 -2
--- www/private.wiki
+++ www/private.wiki
@@ -31,14 +31,27 @@
3131
fossil update trunk
3232
fossil merge private
3333
fossil commit
3434
</pre></blockquote>
3535
36
-The private branch remains private. (There is no way to convert a private
37
-branch into a public branch.) But all of the changes associated with
36
+The private branch remains private, but all of the changes associated with
3837
the private branch are now folded into the public branch and are hence
3938
visible to other users of the project.
39
+
40
+A private branch created with Fossil version 1.30 or newer can also be
41
+converted into a public branch using the <code>fossil publish</code>
42
+command. However, there is no way to convert a private branch created with
43
+older versions of Fossil into a public branch.
44
+
45
+The <code>--integrate</code> option of <code>fossil merge</code> (to close
46
+the merged branch when committing) is ignored for a private branch -- or the
47
+check-in manifest of the resulting merge child would include a
48
+<code>+close</code> tag referring to the leaf check-in on the private branch,
49
+and generate a missing artifact reference on repository clones without that
50
+private branch. It's still possible to close the leaf of the private branch
51
+(after committing the merge child) with the <code>fossil amend --close</code>
52
+command.
4053
4154
<h2>Syncing Private Branches</h2>
4255
4356
A private branch normally stays on the one repository where it was
4457
originally created. But sometimes you want to share private branches
4558
--- www/private.wiki
+++ www/private.wiki
@@ -31,14 +31,27 @@
31 fossil update trunk
32 fossil merge private
33 fossil commit
34 </pre></blockquote>
35
36 The private branch remains private. (There is no way to convert a private
37 branch into a public branch.) But all of the changes associated with
38 the private branch are now folded into the public branch and are hence
39 visible to other users of the project.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
41 <h2>Syncing Private Branches</h2>
42
43 A private branch normally stays on the one repository where it was
44 originally created. But sometimes you want to share private branches
45
--- www/private.wiki
+++ www/private.wiki
@@ -31,14 +31,27 @@
31 fossil update trunk
32 fossil merge private
33 fossil commit
34 </pre></blockquote>
35
36 The private branch remains private, but all of the changes associated with
 
37 the private branch are now folded into the public branch and are hence
38 visible to other users of the project.
39
40 A private branch created with Fossil version 1.30 or newer can also be
41 converted into a public branch using the <code>fossil publish</code>
42 command. However, there is no way to convert a private branch created with
43 older versions of Fossil into a public branch.
44
45 The <code>--integrate</code> option of <code>fossil merge</code> (to close
46 the merged branch when committing) is ignored for a private branch -- or the
47 check-in manifest of the resulting merge child would include a
48 <code>+close</code> tag referring to the leaf check-in on the private branch,
49 and generate a missing artifact reference on repository clones without that
50 private branch. It's still possible to close the leaf of the private branch
51 (after committing the merge child) with the <code>fossil amend --close</code>
52 command.
53
54 <h2>Syncing Private Branches</h2>
55
56 A private branch normally stays on the one repository where it was
57 originally created. But sometimes you want to share private branches
58
+4 -4
--- www/qandc.wiki
+++ www/qandc.wiki
@@ -1,13 +1,13 @@
11
<title>Questions And Criticisms</title>
22
<nowiki>
33
<h1 align="center">Questions And Criticisms</h1>
44
5
-<p>This page is a collection of real questions and criticisms that have been
6
-raised against fossil together with responses from the program's author.</p>
7
-
8
-<p>Note: See also the <a href="faq.wiki">Frequently Asked Questions</a>.</p>
5
+<p>This page is a collection of real questions and criticisms that were
6
+raised against Fossil early in its history (circa 2008).
7
+This page is old and has not been kept up-to-date. See the
8
+</nowiki>[/finfo?name=www/qandc.wiki|change history of this page]<nowiki>.</p>
99
1010
<b>Fossil sounds like a lot of reinvention of the wheel.
1111
Why create your own DVCS when you could have reused mercurial?</b>
1212
1313
<blockquote>
1414
--- www/qandc.wiki
+++ www/qandc.wiki
@@ -1,13 +1,13 @@
1 <title>Questions And Criticisms</title>
2 <nowiki>
3 <h1 align="center">Questions And Criticisms</h1>
4
5 <p>This page is a collection of real questions and criticisms that have been
6 raised against fossil together with responses from the program's author.</p>
7
8 <p>Note: See also the <a href="faq.wiki">Frequently Asked Questions</a>.</p>
9
10 <b>Fossil sounds like a lot of reinvention of the wheel.
11 Why create your own DVCS when you could have reused mercurial?</b>
12
13 <blockquote>
14
--- www/qandc.wiki
+++ www/qandc.wiki
@@ -1,13 +1,13 @@
1 <title>Questions And Criticisms</title>
2 <nowiki>
3 <h1 align="center">Questions And Criticisms</h1>
4
5 <p>This page is a collection of real questions and criticisms that were
6 raised against Fossil early in its history (circa 2008).
7 This page is old and has not been kept up-to-date. See the
8 </nowiki>[/finfo?name=www/qandc.wiki|change history of this page]<nowiki>.</p>
9
10 <b>Fossil sounds like a lot of reinvention of the wheel.
11 Why create your own DVCS when you could have reused mercurial?</b>
12
13 <blockquote>
14
--- www/quickstart.wiki
+++ www/quickstart.wiki
@@ -1,19 +1,19 @@
11
<title>Fossil Quick Start Guide</title>
22
<h1 align="center">Fossil Quick Start</h1>
33
4
-<p>This is a guide to get you started using fossil quickly
4
+<p>This is a guide to help you get started using Fossil quickly
55
and painlessly.</p>
66
77
<h2 id="install">Installing</h2>
88
99
<p>Fossil is a single self-contained C program. You need to
1010
either download a
1111
<a href="https://www.fossil-scm.org/fossil/uv/download.html">precompiled
1212
binary</a>
1313
or <a href="build.wiki">compile it yourself</a> from sources.
14
- Install fossil by putting the fossil binary
14
+ Install Fossil by putting the fossil binary
1515
someplace on your $PATH.</p>
1616
1717
<a name="fslclone"></a>
1818
<h2>General Work Flow</h2>
1919
@@ -391,13 +391,15 @@
391391
392392
<blockquote>
393393
<b>fossil sync http://192.168.1.36:8080/ --proxy off</b>
394394
</blockquote>
395395
396
-<h2>More Hints</h2>
397
-
398
- <p>A [/help | complete list of commands] is available, as is the
399
- [./hints.wiki|helpful hints] document. See the
400
- [./permutedindex.html#pindex|permuted index] for additional
401
- documentation.
402
-
403
- <p>Explore and have fun!</p>
396
+<h2 id="links">Other Resources</h2>
397
+
398
+ <ul>
399
+ <li> <a href="./gitusers.md">Hints for users with prior Git experience</a>
400
+ <li> <a href="./whyusefossil.wiki">Benefits Of Version Control</a>
401
+ <li> <a href="./history.md">The Purpose And History of Fossil</a>
402
+ <li> <a href="./branching.wiki">Branching, Forking, Merge, and Taggings</a>
403
+ <li> <a href="./hints.wiki">Tips and Usage Hints</a>
404
+ <li> <a href="./permutedindex.html">Comprehensive documentation index</a>
405
+ </ul>
404406
--- www/quickstart.wiki
+++ www/quickstart.wiki
@@ -1,19 +1,19 @@
1 <title>Fossil Quick Start Guide</title>
2 <h1 align="center">Fossil Quick Start</h1>
3
4 <p>This is a guide to get you started using fossil quickly
5 and painlessly.</p>
6
7 <h2 id="install">Installing</h2>
8
9 <p>Fossil is a single self-contained C program. You need to
10 either download a
11 <a href="https://www.fossil-scm.org/fossil/uv/download.html">precompiled
12 binary</a>
13 or <a href="build.wiki">compile it yourself</a> from sources.
14 Install fossil by putting the fossil binary
15 someplace on your $PATH.</p>
16
17 <a name="fslclone"></a>
18 <h2>General Work Flow</h2>
19
@@ -391,13 +391,15 @@
391
392 <blockquote>
393 <b>fossil sync http://192.168.1.36:8080/ --proxy off</b>
394 </blockquote>
395
396 <h2>More Hints</h2>
397
398 <p>A [/help | complete list of commands] is available, as is the
399 [./hints.wiki|helpful hints] document. See the
400 [./permutedindex.html#pindex|permuted index] for additional
401 documentation.
402
403 <p>Explore and have fun!</p>
 
 
404
--- www/quickstart.wiki
+++ www/quickstart.wiki
@@ -1,19 +1,19 @@
1 <title>Fossil Quick Start Guide</title>
2 <h1 align="center">Fossil Quick Start</h1>
3
4 <p>This is a guide to help you get started using Fossil quickly
5 and painlessly.</p>
6
7 <h2 id="install">Installing</h2>
8
9 <p>Fossil is a single self-contained C program. You need to
10 either download a
11 <a href="https://www.fossil-scm.org/fossil/uv/download.html">precompiled
12 binary</a>
13 or <a href="build.wiki">compile it yourself</a> from sources.
14 Install Fossil by putting the fossil binary
15 someplace on your $PATH.</p>
16
17 <a name="fslclone"></a>
18 <h2>General Work Flow</h2>
19
@@ -391,13 +391,15 @@
391
392 <blockquote>
393 <b>fossil sync http://192.168.1.36:8080/ --proxy off</b>
394 </blockquote>
395
396 <h2 id="links">Other Resources</h2>
397
398 <ul>
399 <li> <a href="./gitusers.md">Hints for users with prior Git experience</a>
400 <li> <a href="./whyusefossil.wiki">Benefits Of Version Control</a>
401 <li> <a href="./history.md">The Purpose And History of Fossil</a>
402 <li> <a href="./branching.wiki">Branching, Forking, Merge, and Taggings</a>
403 <li> <a href="./hints.wiki">Tips and Usage Hints</a>
404 <li> <a href="./permutedindex.html">Comprehensive documentation index</a>
405 </ul>
406
--- www/rebaseharm.md
+++ www/rebaseharm.md
@@ -317,14 +317,14 @@
317317
that blames the merged check-in as the source of the problem they’re
318318
chasing down; they then have to manually work out which of the 10 steps
319319
the original developer took to create it to find the source of the
320320
actual problem.
321321
322
-Fossil pushes all 11 check-ins to the parent repository by default, so
323
-that someone doing that bisect sees the complete check-in history, so
324
-the bisect will point them at the single original check-in that caused
325
-the problem.
322
+An equivalent push in Fossil will send all 11 check-ins to the parent
323
+repository so that a later investigator doing the same sort of bisect
324
+sees the complete check-in history. That bisect will point the
325
+investigator at the single original check-in that caused the problem.
326326
327327
### <a name="comments"></a>7.3 Multiple check-ins require multiple check-in comments
328328
329329
The more comments you have from a given developer on a given body of
330330
code, the more concise documentation you have of that developer's
331331
--- www/rebaseharm.md
+++ www/rebaseharm.md
@@ -317,14 +317,14 @@
317 that blames the merged check-in as the source of the problem they’re
318 chasing down; they then have to manually work out which of the 10 steps
319 the original developer took to create it to find the source of the
320 actual problem.
321
322 Fossil pushes all 11 check-ins to the parent repository by default, so
323 that someone doing that bisect sees the complete check-in history, so
324 the bisect will point them at the single original check-in that caused
325 the problem.
326
327 ### <a name="comments"></a>7.3 Multiple check-ins require multiple check-in comments
328
329 The more comments you have from a given developer on a given body of
330 code, the more concise documentation you have of that developer's
331
--- www/rebaseharm.md
+++ www/rebaseharm.md
@@ -317,14 +317,14 @@
317 that blames the merged check-in as the source of the problem they’re
318 chasing down; they then have to manually work out which of the 10 steps
319 the original developer took to create it to find the source of the
320 actual problem.
321
322 An equivalent push in Fossil will send all 11 check-ins to the parent
323 repository so that a later investigator doing the same sort of bisect
324 sees the complete check-in history. That bisect will point the
325 investigator at the single original check-in that caused the problem.
326
327 ### <a name="comments"></a>7.3 Multiple check-ins require multiple check-in comments
328
329 The more comments you have from a given developer on a given body of
330 code, the more concise documentation you have of that developer's
331
+26 -13
--- www/ssl.wiki
+++ www/ssl.wiki
@@ -104,25 +104,36 @@
104104
105105
106106
<h3 id="certs">Certificates</h3>
107107
108108
To verify the identify of a server, TLS uses
109
-[https://en.wikipedia.org/wiki/X.509#Certificates|X.509 certificates].
109
+[https://en.wikipedia.org/wiki/X.509#Certificates|X.509 certificates], a
110
+scheme that depends on a trust hierarchy of so-called
111
+[https://en.wikipedia.org/wiki/Certificate_authority | Certificate
112
+Authorities]. The tree of trust relationships ultimately ends in the
113
+CA roots, which are considered the ultimate arbiters of who to trust in
114
+this scheme.
115
+
116
+The question then is, what CA roots does Fossil trust?
110117
111
-If you are using a self-signed certificate, you'll be asked if you want
118
+If you are using a self-signed certificate, Fossil will initially not
119
+know that it can trust your certificate, so you'll be asked if you want
112120
to accept the certificate the first time you communicate with the
113121
server. Verify the certificate fingerprint is correct, then answer
114
-"always" to remember your decision.
122
+"always" if you want Fossil to remember your decision.
115123
116124
If you are cloning from or syncing to Fossil servers that use a
117
-certificate signed by a
118
-[https://en.wikipedia.org/wiki/Certificate_authority|certificate
119
-authority] (CA), Fossil needs to know which CAs you trust to sign those
120
-certificates. Fossil relies on the OpenSSL library to have some way to
121
-check a trusted list of CA signing keys.
125
+certificate signed by a well-known CA or one of its delegates, Fossil
126
+still has to know which CA roots to trust. When this fails, you get a
127
+big long error message that starts with this text:
128
+
129
+<pre>
130
+ SSL verification failed: unable to get local issuer certificate
131
+</pre>
122132
123
-There are two common ways this fails:
133
+Fossil relies on the OpenSSL library to have some way to check a trusted
134
+list of CA signing keys. There are two common ways this fails:
124135
125136
# <p>The OpenSSL library Fossil is linked to doesn't have a CA
126137
signing key set at all, so that it initially trusts no certificates
127138
at all.</p>
128139
# <p>The OpenSSL library does have a CA cert set, but your Fossil server's
@@ -137,11 +148,13 @@
137148
fossil set --global ssl-ca-location /path/to/local-ca.pem
138149
</pre>
139150
140151
The use of <tt>--global</tt> with this option is common, since you may
141152
have multiple reposotories served under certificates signed by that same
142
-CA.
153
+CA. However, if you have a mix of publicly-signed and locally-signed
154
+certificates, you might want to drop the <tt>--global</tt> flag and set
155
+this option on a per-repository basis instead.
143156
144157
A common way to run into the broader first problem is that you're on
145158
FreeBSD, which does not install a CA certificate set by default, even as
146159
a dependency of the OpenSSL library. If you're using a certificate
147160
signed by one of the major public CAs, you can solve this by installing
@@ -155,15 +168,15 @@
155168
certificate set, but it's not in a format that OpenSSL understands how
156169
to use. Rather than try to find a way to convert the data format, you
157170
may find it acceptable to use the same Mozilla NSS cert set. I do not
158171
know of a way to easily get this from Mozilla themselves, but I did find
159172
a [https://curl.haxx.se/docs/caextract.html|third party source] for the
160
-<tt>cacert.pem</tt> file. Install it somewhere on your system, then
161
-point Fossil at it like so:
173
+<tt>cacert.pem</tt> file. I suggest placing the file into your Windows
174
+user home directory so that you can then point Fossil at it like so:
162175
163176
<pre>
164
- fossil set --global ssl-ca-location /path/to/cacert.pem
177
+ fossil set --global ssl-ca-location %userprofile%\cacert.pem
165178
</pre>
166179
167180
This can also happen if you've linked Fossil to a version of OpenSSL
168181
[#openssl-src|built from source]. That same <tt>cacert.pem</tt> fix can
169182
work in that case, too.
170183
--- www/ssl.wiki
+++ www/ssl.wiki
@@ -104,25 +104,36 @@
104
105
106 <h3 id="certs">Certificates</h3>
107
108 To verify the identify of a server, TLS uses
109 [https://en.wikipedia.org/wiki/X.509#Certificates|X.509 certificates].
 
 
 
 
 
 
 
110
111 If you are using a self-signed certificate, you'll be asked if you want
 
112 to accept the certificate the first time you communicate with the
113 server. Verify the certificate fingerprint is correct, then answer
114 "always" to remember your decision.
115
116 If you are cloning from or syncing to Fossil servers that use a
117 certificate signed by a
118 [https://en.wikipedia.org/wiki/Certificate_authority|certificate
119 authority] (CA), Fossil needs to know which CAs you trust to sign those
120 certificates. Fossil relies on the OpenSSL library to have some way to
121 check a trusted list of CA signing keys.
 
 
122
123 There are two common ways this fails:
 
124
125 # <p>The OpenSSL library Fossil is linked to doesn't have a CA
126 signing key set at all, so that it initially trusts no certificates
127 at all.</p>
128 # <p>The OpenSSL library does have a CA cert set, but your Fossil server's
@@ -137,11 +148,13 @@
137 fossil set --global ssl-ca-location /path/to/local-ca.pem
138 </pre>
139
140 The use of <tt>--global</tt> with this option is common, since you may
141 have multiple reposotories served under certificates signed by that same
142 CA.
 
 
143
144 A common way to run into the broader first problem is that you're on
145 FreeBSD, which does not install a CA certificate set by default, even as
146 a dependency of the OpenSSL library. If you're using a certificate
147 signed by one of the major public CAs, you can solve this by installing
@@ -155,15 +168,15 @@
155 certificate set, but it's not in a format that OpenSSL understands how
156 to use. Rather than try to find a way to convert the data format, you
157 may find it acceptable to use the same Mozilla NSS cert set. I do not
158 know of a way to easily get this from Mozilla themselves, but I did find
159 a [https://curl.haxx.se/docs/caextract.html|third party source] for the
160 <tt>cacert.pem</tt> file. Install it somewhere on your system, then
161 point Fossil at it like so:
162
163 <pre>
164 fossil set --global ssl-ca-location /path/to/cacert.pem
165 </pre>
166
167 This can also happen if you've linked Fossil to a version of OpenSSL
168 [#openssl-src|built from source]. That same <tt>cacert.pem</tt> fix can
169 work in that case, too.
170
--- www/ssl.wiki
+++ www/ssl.wiki
@@ -104,25 +104,36 @@
104
105
106 <h3 id="certs">Certificates</h3>
107
108 To verify the identify of a server, TLS uses
109 [https://en.wikipedia.org/wiki/X.509#Certificates|X.509 certificates], a
110 scheme that depends on a trust hierarchy of so-called
111 [https://en.wikipedia.org/wiki/Certificate_authority | Certificate
112 Authorities]. The tree of trust relationships ultimately ends in the
113 CA roots, which are considered the ultimate arbiters of who to trust in
114 this scheme.
115
116 The question then is, what CA roots does Fossil trust?
117
118 If you are using a self-signed certificate, Fossil will initially not
119 know that it can trust your certificate, so you'll be asked if you want
120 to accept the certificate the first time you communicate with the
121 server. Verify the certificate fingerprint is correct, then answer
122 "always" if you want Fossil to remember your decision.
123
124 If you are cloning from or syncing to Fossil servers that use a
125 certificate signed by a well-known CA or one of its delegates, Fossil
126 still has to know which CA roots to trust. When this fails, you get a
127 big long error message that starts with this text:
128
129 <pre>
130 SSL verification failed: unable to get local issuer certificate
131 </pre>
132
133 Fossil relies on the OpenSSL library to have some way to check a trusted
134 list of CA signing keys. There are two common ways this fails:
135
136 # <p>The OpenSSL library Fossil is linked to doesn't have a CA
137 signing key set at all, so that it initially trusts no certificates
138 at all.</p>
139 # <p>The OpenSSL library does have a CA cert set, but your Fossil server's
@@ -137,11 +148,13 @@
148 fossil set --global ssl-ca-location /path/to/local-ca.pem
149 </pre>
150
151 The use of <tt>--global</tt> with this option is common, since you may
152 have multiple reposotories served under certificates signed by that same
153 CA. However, if you have a mix of publicly-signed and locally-signed
154 certificates, you might want to drop the <tt>--global</tt> flag and set
155 this option on a per-repository basis instead.
156
157 A common way to run into the broader first problem is that you're on
158 FreeBSD, which does not install a CA certificate set by default, even as
159 a dependency of the OpenSSL library. If you're using a certificate
160 signed by one of the major public CAs, you can solve this by installing
@@ -155,15 +168,15 @@
168 certificate set, but it's not in a format that OpenSSL understands how
169 to use. Rather than try to find a way to convert the data format, you
170 may find it acceptable to use the same Mozilla NSS cert set. I do not
171 know of a way to easily get this from Mozilla themselves, but I did find
172 a [https://curl.haxx.se/docs/caextract.html|third party source] for the
173 <tt>cacert.pem</tt> file. I suggest placing the file into your Windows
174 user home directory so that you can then point Fossil at it like so:
175
176 <pre>
177 fossil set --global ssl-ca-location %userprofile%\cacert.pem
178 </pre>
179
180 This can also happen if you've linked Fossil to a version of OpenSSL
181 [#openssl-src|built from source]. That same <tt>cacert.pem</tt> fix can
182 work in that case, too.
183
--- www/sync.wiki
+++ www/sync.wiki
@@ -421,10 +421,13 @@
421421
using a gimme card in either the reply or in the next message.</p>
422422
423423
<p>If the second argument exists and is "1", then the artifact
424424
identified by the first argument is private on the sender and should
425425
be ignored unless a "--private" [/help?cmd=sync|sync] is occurring.
426
+
427
+<p>The name "igot" comes from the English slang expression "I got" meaning
428
+"I have".
426429
427430
<h4>3.6.1 Unversioned Igot Cards</h4>
428431
429432
<p>Zero or more "uvigot" cards are sent from server to client when
430433
synchronizing unversioned content. The format of a uvigot card is
@@ -469,10 +472,14 @@
469472
<p>The argument to the gimme card is the ID of the artifact that
470473
the sender wants. The receiver will typically respond to a
471474
gimme card by sending a file card in its reply or in the next
472475
message.</p>
473476
477
+<p>The "gimme" name means "give me". The imperative "give me" is
478
+pronounced as if it were a single word "gimme" in some dialects of
479
+English (including the dialect spoken by the original author of Fossil).
480
+
474481
<h4>3.7.1 Unversioned Gimme Cards</h4>
475482
476483
<p>Sync synchronizing unversioned content, the client may send "uvgimme"
477484
cards to the server. A uvgimme card requests that the server send
478485
unversioned content to the client. The format of a uvgimme card is
479486
--- www/sync.wiki
+++ www/sync.wiki
@@ -421,10 +421,13 @@
421 using a gimme card in either the reply or in the next message.</p>
422
423 <p>If the second argument exists and is "1", then the artifact
424 identified by the first argument is private on the sender and should
425 be ignored unless a "--private" [/help?cmd=sync|sync] is occurring.
 
 
 
426
427 <h4>3.6.1 Unversioned Igot Cards</h4>
428
429 <p>Zero or more "uvigot" cards are sent from server to client when
430 synchronizing unversioned content. The format of a uvigot card is
@@ -469,10 +472,14 @@
469 <p>The argument to the gimme card is the ID of the artifact that
470 the sender wants. The receiver will typically respond to a
471 gimme card by sending a file card in its reply or in the next
472 message.</p>
473
 
 
 
 
474 <h4>3.7.1 Unversioned Gimme Cards</h4>
475
476 <p>Sync synchronizing unversioned content, the client may send "uvgimme"
477 cards to the server. A uvgimme card requests that the server send
478 unversioned content to the client. The format of a uvgimme card is
479
--- www/sync.wiki
+++ www/sync.wiki
@@ -421,10 +421,13 @@
421 using a gimme card in either the reply or in the next message.</p>
422
423 <p>If the second argument exists and is "1", then the artifact
424 identified by the first argument is private on the sender and should
425 be ignored unless a "--private" [/help?cmd=sync|sync] is occurring.
426
427 <p>The name "igot" comes from the English slang expression "I got" meaning
428 "I have".
429
430 <h4>3.6.1 Unversioned Igot Cards</h4>
431
432 <p>Zero or more "uvigot" cards are sent from server to client when
433 synchronizing unversioned content. The format of a uvigot card is
@@ -469,10 +472,14 @@
472 <p>The argument to the gimme card is the ID of the artifact that
473 the sender wants. The receiver will typically respond to a
474 gimme card by sending a file card in its reply or in the next
475 message.</p>
476
477 <p>The "gimme" name means "give me". The imperative "give me" is
478 pronounced as if it were a single word "gimme" in some dialects of
479 English (including the dialect spoken by the original author of Fossil).
480
481 <h4>3.7.1 Unversioned Gimme Cards</h4>
482
483 <p>Sync synchronizing unversioned content, the client may send "uvgimme"
484 cards to the server. A uvgimme card requests that the server send
485 unversioned content to the client. The format of a uvgimme card is
486
--- www/tech_overview.wiki
+++ www/tech_overview.wiki
@@ -68,11 +68,12 @@
6868
database files are used by Fossil, with detailed discussion following.
6969
7070
<table border="1" width="80%" cellpadding="0" align="center">
7171
<tr>
7272
<td width="33%" valign="top">
73
-<h3 align="center">Configuration Database<br>"~/.fossil"</h3>
73
+<h3 align="center">Configuration Database<br>"~/.fossil" or<br>
74
+"~/.config/fossil.db"</h3>
7475
<ul>
7576
<li>Global [/help/settings |settings]
7677
<li>List of active repositories used by the [/help/all | all] command
7778
</ul>
7879
</td>
@@ -87,11 +88,11 @@
8788
<li>Metadata about the global state to facilitate rapid
8889
queries
8990
</ul>
9091
</td>
9192
<td width="33%" valign="top">
92
-<h3 align="center">Checkout Database<br>"_FOSSIL_"</h3>
93
+<h3 align="center">Checkout Database<br>"_FOSSIL_" or ".fslckout"</h3>
9394
<ul>
9495
<li>The repository database used by this checkout
9596
<li>The version currently checked out
9697
<li>Other versions [/help/merge | merged] in but not
9798
yet [/help/commit | committed]
@@ -124,18 +125,58 @@
124125
125126
The configuration database also maintains a list of repositories. This
126127
list is used by the [/help/all | fossil all] command in order to run various
127128
operations such as "sync" or "rebuild" on all repositories managed by a user.
128129
129
-On Unix systems, the configuration database is named ".fossil" and is
130
-located in the user's home directory. On Windows, the configuration
131
-database is named "_fossil" (using an underscore as the first character
132
-instead of a dot) and is located in the directory specified by the
133
-LOCALAPPDATA, APPDATA, or HOMEPATH environment variables, in that order.
134
-
135
-You can override this default location by defining the environment
136
-variable FOSSIL_HOME pointing to an appropriate (writable) directory.
130
+<a name='configloc'></a>
131
+<h4>2.1.1 Location Of The Configuration Database</h4>
132
+
133
+On Unix systems, the configuration database is named by the following
134
+algorithm:
135
+
136
+<blockquote><table border="0">
137
+<tr><td>1. if environment variable FOSSIL_HOME exists
138
+<td>&nbsp;&rarr;&nbsp;<td>$FOSSIL_HOME/.fossil
139
+<tr><td>2. if file ~/.fossil exists<td>&nbsp;&rarr;<td>~/.fossil
140
+<tr><td>3. if environment variable XDG_CONFIG_HOME exists
141
+ <td>&nbsp;&rarr;<td>$XDG_CONFIG_HOME/fossil.db
142
+<tr><td>4. if the directory ~/.config exists
143
+ <td>&nbsp;&rarr;<td>~/.config/fossil.db
144
+<tr><td>5. Otherwise<td>&nbsp;&rarr;<td>~/.fossil
145
+</table></blockquote>
146
+
147
+Another way of thinking of this algorithm is the following:
148
+
149
+ * Use "$FOSSIL_HOME/.fossil" if the FOSSIL_HOME variable is defined
150
+ * Use the XDG-compatible name (usually ~/.config/fossil.db) on XDG systems
151
+ if the ~/.fossil file does not already exist
152
+ * Otherwise, use the traditional unix name of "~/.fossil"
153
+
154
+This algorithm is complex due to the need for historical compatibility.
155
+Originally, the database was always just "~/.fossil". Then support
156
+for the FOSSIL_HOME environment variable as added. Later, support for the
157
+[https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html|XDG-compatible configation filenames]
158
+was added. Each of these changes needed to continue to support legacy
159
+installations.
160
+
161
+On Windows, the configuration database is the first of the following
162
+for which the corresponding environment variables exist:
163
+
164
+ * %FOSSIL_HOME%/_fossil
165
+ * %LOCALAPPDATA%/_fossil
166
+ * %APPDATA%/_fossil
167
+ * %USERPROFILES%/_fossil
168
+ * %HOMEDRIVE%%HOMEPATH%/_fossil
169
+
170
+The second case is the one that usually determines the name Note that the
171
+FOSSIL_HOME environment variable can always be set to determine the
172
+location of the configuration database. Note also that the configuration
173
+database file itself is called ".fossil" or "fossil.db" on unix but
174
+"_fossil" on windows.
175
+
176
+The [/help?cmd=info|fossil info] command will show the location of
177
+the configuration database on a line that starts with "config-db:".
137178
138179
<h3>2.2 Repository Databases</h3>
139180
140181
The repository database is the file that is commonly referred to as
141182
"the repository". This is because the repository database contains,
142183
143184
ADDED www/userlinks.wiki
--- www/tech_overview.wiki
+++ www/tech_overview.wiki
@@ -68,11 +68,12 @@
68 database files are used by Fossil, with detailed discussion following.
69
70 <table border="1" width="80%" cellpadding="0" align="center">
71 <tr>
72 <td width="33%" valign="top">
73 <h3 align="center">Configuration Database<br>"~/.fossil"</h3>
 
74 <ul>
75 <li>Global [/help/settings |settings]
76 <li>List of active repositories used by the [/help/all | all] command
77 </ul>
78 </td>
@@ -87,11 +88,11 @@
87 <li>Metadata about the global state to facilitate rapid
88 queries
89 </ul>
90 </td>
91 <td width="33%" valign="top">
92 <h3 align="center">Checkout Database<br>"_FOSSIL_"</h3>
93 <ul>
94 <li>The repository database used by this checkout
95 <li>The version currently checked out
96 <li>Other versions [/help/merge | merged] in but not
97 yet [/help/commit | committed]
@@ -124,18 +125,58 @@
124
125 The configuration database also maintains a list of repositories. This
126 list is used by the [/help/all | fossil all] command in order to run various
127 operations such as "sync" or "rebuild" on all repositories managed by a user.
128
129 On Unix systems, the configuration database is named ".fossil" and is
130 located in the user's home directory. On Windows, the configuration
131 database is named "_fossil" (using an underscore as the first character
132 instead of a dot) and is located in the directory specified by the
133 LOCALAPPDATA, APPDATA, or HOMEPATH environment variables, in that order.
134
135 You can override this default location by defining the environment
136 variable FOSSIL_HOME pointing to an appropriate (writable) directory.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
138 <h3>2.2 Repository Databases</h3>
139
140 The repository database is the file that is commonly referred to as
141 "the repository". This is because the repository database contains,
142
143 DDED www/userlinks.wiki
--- www/tech_overview.wiki
+++ www/tech_overview.wiki
@@ -68,11 +68,12 @@
68 database files are used by Fossil, with detailed discussion following.
69
70 <table border="1" width="80%" cellpadding="0" align="center">
71 <tr>
72 <td width="33%" valign="top">
73 <h3 align="center">Configuration Database<br>"~/.fossil" or<br>
74 "~/.config/fossil.db"</h3>
75 <ul>
76 <li>Global [/help/settings |settings]
77 <li>List of active repositories used by the [/help/all | all] command
78 </ul>
79 </td>
@@ -87,11 +88,11 @@
88 <li>Metadata about the global state to facilitate rapid
89 queries
90 </ul>
91 </td>
92 <td width="33%" valign="top">
93 <h3 align="center">Checkout Database<br>"_FOSSIL_" or ".fslckout"</h3>
94 <ul>
95 <li>The repository database used by this checkout
96 <li>The version currently checked out
97 <li>Other versions [/help/merge | merged] in but not
98 yet [/help/commit | committed]
@@ -124,18 +125,58 @@
125
126 The configuration database also maintains a list of repositories. This
127 list is used by the [/help/all | fossil all] command in order to run various
128 operations such as "sync" or "rebuild" on all repositories managed by a user.
129
130 <a name='configloc'></a>
131 <h4>2.1.1 Location Of The Configuration Database</h4>
132
133 On Unix systems, the configuration database is named by the following
134 algorithm:
135
136 <blockquote><table border="0">
137 <tr><td>1. if environment variable FOSSIL_HOME exists
138 <td>&nbsp;&rarr;&nbsp;<td>$FOSSIL_HOME/.fossil
139 <tr><td>2. if file ~/.fossil exists<td>&nbsp;&rarr;<td>~/.fossil
140 <tr><td>3. if environment variable XDG_CONFIG_HOME exists
141 <td>&nbsp;&rarr;<td>$XDG_CONFIG_HOME/fossil.db
142 <tr><td>4. if the directory ~/.config exists
143 <td>&nbsp;&rarr;<td>~/.config/fossil.db
144 <tr><td>5. Otherwise<td>&nbsp;&rarr;<td>~/.fossil
145 </table></blockquote>
146
147 Another way of thinking of this algorithm is the following:
148
149 * Use "$FOSSIL_HOME/.fossil" if the FOSSIL_HOME variable is defined
150 * Use the XDG-compatible name (usually ~/.config/fossil.db) on XDG systems
151 if the ~/.fossil file does not already exist
152 * Otherwise, use the traditional unix name of "~/.fossil"
153
154 This algorithm is complex due to the need for historical compatibility.
155 Originally, the database was always just "~/.fossil". Then support
156 for the FOSSIL_HOME environment variable as added. Later, support for the
157 [https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html|XDG-compatible configation filenames]
158 was added. Each of these changes needed to continue to support legacy
159 installations.
160
161 On Windows, the configuration database is the first of the following
162 for which the corresponding environment variables exist:
163
164 * %FOSSIL_HOME%/_fossil
165 * %LOCALAPPDATA%/_fossil
166 * %APPDATA%/_fossil
167 * %USERPROFILES%/_fossil
168 * %HOMEDRIVE%%HOMEPATH%/_fossil
169
170 The second case is the one that usually determines the name Note that the
171 FOSSIL_HOME environment variable can always be set to determine the
172 location of the configuration database. Note also that the configuration
173 database file itself is called ".fossil" or "fossil.db" on unix but
174 "_fossil" on windows.
175
176 The [/help?cmd=info|fossil info] command will show the location of
177 the configuration database on a line that starts with "config-db:".
178
179 <h3>2.2 Repository Databases</h3>
180
181 The repository database is the file that is commonly referred to as
182 "the repository". This is because the repository database contains,
183
184 DDED www/userlinks.wiki
--- a/www/userlinks.wiki
+++ b/www/userlinks.wiki
@@ -0,0 +1 @@
1
+<title>Links For Foswhyusefossil.wiki#definitions
--- a/www/userlinks.wiki
+++ b/www/userlinks.wiki
@@ -0,0 +1 @@
 
--- a/www/userlinks.wiki
+++ b/www/userlinks.wiki
@@ -0,0 +1 @@
1 <title>Links For Foswhyusefossil.wiki#definitions
--- www/whyusefossil.wiki
+++ www/whyusefossil.wiki
@@ -199,18 +199,19 @@
199199
might commit different changes against the same check-in. This
200200
results in one parent node having two or more children.
201201
<li><p>Command: <b>merge</b> &rarr;
202202
combines the work of multiple check-ins into
203203
a single check-out. That check-out can then be committed to create
204
- a new that has two (or more) parents.
204
+ a new check-in that has two (or more) parents.
205205
<ul>
206206
<li><p>Most check-ins have just one parent, and either zero or
207207
one child.
208208
<li><p>When a check-in has two or more parents, one of those parents
209
- is the "primary parent". All the other parent nodes are "secondary".
209
+ is the "primary parent". All the other parent nodes are "secondary"
210
+ or "merge" parents.
210211
Conceptually, the primary parent shows the main line of
211
- development. Content from the secondary parents is added
212
+ development. Content from the merge parents is added
212213
into the main line.
213214
<li><p>The "direct children" of a check-in X are all children that
214215
have X as their primary parent.
215216
<li><p>A check-in node with no direct children is sometimes called
216217
a "leaf".
@@ -221,10 +222,12 @@
221222
<li><p>Definition: <b>branch</b> &rarr;
222223
a sequence of check-ins that are all linked
223224
together in the DAG through the primary parent.
224225
<ul>
225226
<li><p>Branches are often given names which propagate to direct children.
227
+ The tradition in Fossil is to call the main branch "trunk". In
228
+ Git, the main branch is usually called "master".
226229
<li><p>It is possible to have multiple branches with the same name.
227230
Fossil has no problem with this, but it can be confusing to
228231
humans, so best practice is to give each branch a unique name.
229232
<li><p>The name of a branch can be changed by adding special tags
230233
to the first check-in of a branch. The name assigned by this
231234
--- www/whyusefossil.wiki
+++ www/whyusefossil.wiki
@@ -199,18 +199,19 @@
199 might commit different changes against the same check-in. This
200 results in one parent node having two or more children.
201 <li><p>Command: <b>merge</b> &rarr;
202 combines the work of multiple check-ins into
203 a single check-out. That check-out can then be committed to create
204 a new that has two (or more) parents.
205 <ul>
206 <li><p>Most check-ins have just one parent, and either zero or
207 one child.
208 <li><p>When a check-in has two or more parents, one of those parents
209 is the "primary parent". All the other parent nodes are "secondary".
 
210 Conceptually, the primary parent shows the main line of
211 development. Content from the secondary parents is added
212 into the main line.
213 <li><p>The "direct children" of a check-in X are all children that
214 have X as their primary parent.
215 <li><p>A check-in node with no direct children is sometimes called
216 a "leaf".
@@ -221,10 +222,12 @@
221 <li><p>Definition: <b>branch</b> &rarr;
222 a sequence of check-ins that are all linked
223 together in the DAG through the primary parent.
224 <ul>
225 <li><p>Branches are often given names which propagate to direct children.
 
 
226 <li><p>It is possible to have multiple branches with the same name.
227 Fossil has no problem with this, but it can be confusing to
228 humans, so best practice is to give each branch a unique name.
229 <li><p>The name of a branch can be changed by adding special tags
230 to the first check-in of a branch. The name assigned by this
231
--- www/whyusefossil.wiki
+++ www/whyusefossil.wiki
@@ -199,18 +199,19 @@
199 might commit different changes against the same check-in. This
200 results in one parent node having two or more children.
201 <li><p>Command: <b>merge</b> &rarr;
202 combines the work of multiple check-ins into
203 a single check-out. That check-out can then be committed to create
204 a new check-in that has two (or more) parents.
205 <ul>
206 <li><p>Most check-ins have just one parent, and either zero or
207 one child.
208 <li><p>When a check-in has two or more parents, one of those parents
209 is the "primary parent". All the other parent nodes are "secondary"
210 or "merge" parents.
211 Conceptually, the primary parent shows the main line of
212 development. Content from the merge parents is added
213 into the main line.
214 <li><p>The "direct children" of a check-in X are all children that
215 have X as their primary parent.
216 <li><p>A check-in node with no direct children is sometimes called
217 a "leaf".
@@ -221,10 +222,12 @@
222 <li><p>Definition: <b>branch</b> &rarr;
223 a sequence of check-ins that are all linked
224 together in the DAG through the primary parent.
225 <ul>
226 <li><p>Branches are often given names which propagate to direct children.
227 The tradition in Fossil is to call the main branch "trunk". In
228 Git, the main branch is usually called "master".
229 <li><p>It is possible to have multiple branches with the same name.
230 Fossil has no problem with this, but it can be confusing to
231 humans, so best practice is to give each branch a unique name.
232 <li><p>The name of a branch can be changed by adding special tags
233 to the first check-in of a branch. The name assigned by this
234
--- www/wikitheory.wiki
+++ www/wikitheory.wiki
@@ -65,21 +65,22 @@
6565
The comments on check-ins and the text in the descriptions of bug reports
6666
both use wiki formatting. Exactly the same set of formatting rules apply.
6767
There is never a need to learn one formatting language for documentation
6868
and a different markup for bugs or for check-in comments.
6969
70
+<a name="assocwiki"></a>
7071
<h2>Auxiliary notes attached to check-ins or branches</h2>
7172
7273
Stand-alone wiki pages with special names "branch/<i>BRANCHNAME</i>"
7374
or "checkin/<i>HASH</i>" are associated with the corresponding
7475
branch or check-in. The wiki text appears in an "About" section of
7576
timelines and info screens. Examples:
7677
7778
* [/timeline?r=graph-test-branch] shows the text of the
78
- [/wiki?name=branch/graph-test-branch|branch/graph-test-branch]
79
+ [/wiki?name=branch/graph-test-branch&p|branch/graph-test-branch]
7980
wiki page at the top of the timeline
8081
* [/info/19c60b7fc9e2] shows the text of the
81
- [/wiki?name=checkin/19c60b7fc9e2400e56a6f938bbad0e34ca746ca2eabdecac10945539f1f5e8c6|checkin/19c60b7fc9e2...]
82
+ [/wiki?name=checkin/19c60b7fc9e2400e56a6f938bbad0e34ca746ca2eabdecac10945539f1f5e8c6&p|checkin/19c60b7fc9e2...]
8283
wiki page in the "About" section.
8384
8485
This special wiki pages are very useful for recording historical
8586
notes.
8687
--- www/wikitheory.wiki
+++ www/wikitheory.wiki
@@ -65,21 +65,22 @@
65 The comments on check-ins and the text in the descriptions of bug reports
66 both use wiki formatting. Exactly the same set of formatting rules apply.
67 There is never a need to learn one formatting language for documentation
68 and a different markup for bugs or for check-in comments.
69
 
70 <h2>Auxiliary notes attached to check-ins or branches</h2>
71
72 Stand-alone wiki pages with special names "branch/<i>BRANCHNAME</i>"
73 or "checkin/<i>HASH</i>" are associated with the corresponding
74 branch or check-in. The wiki text appears in an "About" section of
75 timelines and info screens. Examples:
76
77 * [/timeline?r=graph-test-branch] shows the text of the
78 [/wiki?name=branch/graph-test-branch|branch/graph-test-branch]
79 wiki page at the top of the timeline
80 * [/info/19c60b7fc9e2] shows the text of the
81 [/wiki?name=checkin/19c60b7fc9e2400e56a6f938bbad0e34ca746ca2eabdecac10945539f1f5e8c6|checkin/19c60b7fc9e2...]
82 wiki page in the "About" section.
83
84 This special wiki pages are very useful for recording historical
85 notes.
86
--- www/wikitheory.wiki
+++ www/wikitheory.wiki
@@ -65,21 +65,22 @@
65 The comments on check-ins and the text in the descriptions of bug reports
66 both use wiki formatting. Exactly the same set of formatting rules apply.
67 There is never a need to learn one formatting language for documentation
68 and a different markup for bugs or for check-in comments.
69
70 <a name="assocwiki"></a>
71 <h2>Auxiliary notes attached to check-ins or branches</h2>
72
73 Stand-alone wiki pages with special names "branch/<i>BRANCHNAME</i>"
74 or "checkin/<i>HASH</i>" are associated with the corresponding
75 branch or check-in. The wiki text appears in an "About" section of
76 timelines and info screens. Examples:
77
78 * [/timeline?r=graph-test-branch] shows the text of the
79 [/wiki?name=branch/graph-test-branch&p|branch/graph-test-branch]
80 wiki page at the top of the timeline
81 * [/info/19c60b7fc9e2] shows the text of the
82 [/wiki?name=checkin/19c60b7fc9e2400e56a6f938bbad0e34ca746ca2eabdecac10945539f1f5e8c6&p|checkin/19c60b7fc9e2...]
83 wiki page in the "About" section.
84
85 This special wiki pages are very useful for recording historical
86 notes.
87

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button