Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Merge trunk. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | backoffice-win |
| Files: | files | file ages | folders |
| SHA3-256: |
3a19db886f4a20e90386f97b981dc8b1 |
| User & Date: | tsbg 2019-04-26 16:39:30.322 |
Context
|
2019-04-26
| ||
| 16:45 | Make sure that the "--nocgi" option gets removed from the command line (hint: short circuit evaluation of the && operator). ... (check-in: 417e2b4149 user: tsbg tags: backoffice-win) | |
| 16:39 | Merge trunk. ... (check-in: 3a19db886f user: tsbg tags: backoffice-win) | |
|
2019-04-25
| ||
| 14:28 | Make full use of the new file_tempname() in "fossil diff" with both --from and --to options. Improve file_tempname() to allow for a suggested differentiator tag. ... (check-in: 1dd2527f2a user: drh tags: trunk) | |
|
2018-12-10
| ||
| 08:07 | Attempt to get the backoffice running on Windows. ... (check-in: c52fb5eddb user: tsbg tags: backoffice-win) | |
Changes
Changes to Makefile.osx-jaguar.
| ︙ | ︙ | |||
36 37 38 39 40 41 42 | # BCC = cc BCCFLAGS = $(CFLAGS) #### The suffix to add to final executable file. When cross-compiling # to windows, make this ".exe". Otherwise leave it blank. # | | | | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 | # BCC = cc BCCFLAGS = $(CFLAGS) #### The suffix to add to final executable file. When cross-compiling # to windows, make this ".exe". Otherwise leave it blank. # E = TCC = cc TCCFLAGS = $(CFLAGS) #### Tcl shell for use in running the fossil testsuite. If you do not # care about testing the end result, this can be blank. # TCLSH = tclsh # LIB = -lz LIB = compat/zlib/libz.a TCC += -g -O0 -DHAVE_AUTOCONFIG_H TCC += -Icompat/zlib TCC += -DSQLITE_WITHOUT_ZONEMALLOC TCC += -D_BSD_SOURCE=1 TCC += -DWITHOUT_ICONV TCC += -Dsocklen_t=int TCC += -DSQLITE_MAX_MMAP_SIZE=0 TCC += -DFOSSIL_ENABLE_LEGACY_MV_RM=1 INSTALLDIR = $(DESTDIR)/usr/local/bin USE_SYSTEM_SQLITE = USE_LINENOISE = 1 # FOSSIL_ENABLE_TCL = @FOSSIL_ENABLE_TCL@ FOSSIL_ENABLE_TCL = 0 FOSSIL_ENABLE_MINIZ = 0 include $(SRCDIR)/main.mk distclean: clean rm -f autoconfig.h config.log Makefile |
Added README.md.
> > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # About Fossil Fossil is a distributed version control system that has been widely used since 2007. Fossil was originally designed to support the [SQLite](https://sqlite.org) project but has been adopted by many other projects as well. Fossil is self-hosting at <https://fossil-scm.org>. If you are reading this on GitHub, then you are looking at a Git mirror of the self-hosting Fossil repository. The purpose of that mirror is to test and exercise Fossil's ability to export a Git mirror. Nobody much uses the GitHub mirror, except to verify that the mirror logic works. If you want to know more about Fossil, visit the official self-hosting site linked above. |
Changes to VERSION.
|
| | | 1 | 2.9 |
Changes to auto.def.
| ︙ | ︙ | |||
124 125 126 127 128 129 130 |
# search for the system SQLite once with -ldl, and once without. If
# the library can only be found with $extralibs set to -ldl, then
# the code below will append -ldl to LIBS.
#
foreach extralibs {{} {-ldl}} {
# Locate the system SQLite by searching for sqlite3_open(). Then check
| | | | | | 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 |
# search for the system SQLite once with -ldl, and once without. If
# the library can only be found with $extralibs set to -ldl, then
# the code below will append -ldl to LIBS.
#
foreach extralibs {{} {-ldl}} {
# Locate the system SQLite by searching for sqlite3_open(). Then check
# if sqlite3_stmt_isexplain can be found as well. If we can find open() but
# not stmt_isexplain(), then the system SQLite is too old to link against
# fossil.
#
if {[check-function-in-lib sqlite3_open sqlite3 $extralibs]} {
if {![check-function-in-lib sqlite3_stmt_isexplain sqlite3 $extralibs]} {
user-error "system sqlite3 too old (require >= 3.28.0)"
}
# Success. Update symbols and return.
#
define USE_SYSTEM_SQLITE 1
define-append LIBS -lsqlite3
define-append LIBS $extralibs
|
| ︙ | ︙ | |||
374 375 376 377 378 379 380 381 382 383 384 385 386 387 |
# Silence OpenSSL deprecation warnings on Mac OS X 10.7.
if {[string match *-darwin* [get-define host]]} {
if {[cctest -cflags {-Wdeprecated-declarations}]} {
define-append EXTRA_CFLAGS -Wdeprecated-declarations
}
}
} else {
user-error "OpenSSL not found. Consider --with-openssl=none to disable HTTPS support"
}
} else {
if {[info exists ::zlib_lib]} {
define-append LIBS $::zlib_lib
}
| > | 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 |
# Silence OpenSSL deprecation warnings on Mac OS X 10.7.
if {[string match *-darwin* [get-define host]]} {
if {[cctest -cflags {-Wdeprecated-declarations}]} {
define-append EXTRA_CFLAGS -Wdeprecated-declarations
}
}
cc-check-function-in-lib BIO_ADDR_hostname_string ssl
} else {
user-error "OpenSSL not found. Consider --with-openssl=none to disable HTTPS support"
}
} else {
if {[info exists ::zlib_lib]} {
define-append LIBS $::zlib_lib
}
|
| ︙ | ︙ |
Changes to skins/ardoise/css.txt.
| ︙ | ︙ | |||
1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 |
}
.tl-arrow.merge.r {
border-left: 3px solid #bbb
}
.tl-line.merge {
width: 1px
}
.intLink[title="Add indentation"],
.intLink[title="Center align"],
.intLink[title="Dotted list"],
.intLink[title="Left align"],
.intLink[title="Numbered list"],
.intLink[title="Remove formatting"],
.intLink[title="Right align"],
| > > > > > > > > > > > > > > > > > > > > > > > | 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 |
}
.tl-arrow.merge.r {
border-left: 3px solid #bbb
}
.tl-line.merge {
width: 1px
}
.tl-arrow.cherrypick {
height: 1px;
border-width: 2px 0;
}
.tl-arrow.cherrypick.l {
border-right: 3px solid #bbb;
}
.tl-arrow.cherrypick.r {
border-left: 3px solid #bbb;
}
.tl-line.cherrypick.h {
width: 0px;
border-top: 1px dashed #bbb;
border-left: 0px dashed #bbb;
background: rgba(255,255,255,0);
}
.tl-line.cherrypick.v {
width: 0px;
border-top: 0px dashed #bbb;
border-left: 1px dashed #bbb;
background: rgba(255,255,255,0);
}
.intLink[title="Add indentation"],
.intLink[title="Center align"],
.intLink[title="Dotted list"],
.intLink[title="Left align"],
.intLink[title="Numbered list"],
.intLink[title="Remove formatting"],
.intLink[title="Right align"],
|
| ︙ | ︙ |
Changes to skins/default/header.txt.
| ︙ | ︙ | |||
15 16 17 18 19 20 21 |
upvar home home
if {[string range $url 0 [string length $current]] eq "/$current"} {
html "<a href='$home$url' class='active $cls'>$name</a>\n"
} else {
html "<a href='$home$url' class='$cls'>$name</a>\n"
}
}
| | | 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
upvar home home
if {[string range $url 0 [string length $current]] eq "/$current"} {
html "<a href='$home$url' class='active $cls'>$name</a>\n"
} else {
html "<a href='$home$url' class='$cls'>$name</a>\n"
}
}
html "<a id='hbbtn' href='#'>☰</a>"
menulink $index_page Home {}
if {[anycap jor]} {
menulink /timeline Timeline {}
}
if {[hascap oh]} {
menulink /dir?ci=tip Files desktoponly
}
|
| ︙ | ︙ |
Changes to skins/default/js.txt.
| ︙ | ︙ | |||
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
*******************************************************************************
**
** This file contains the JS code specific to the Fossil default skin.
** Currently, the only thing this does is handle clicks on its hamburger
** menu button.
*/
(function() {
var panel = document.getElementById("hbdrop");
if (!panel) return; // site admin might've nuked it
if (!panel.style) return; // shouldn't happen, but be sure
var panelBorder = panel.style.border;
// Disable animation if this browser doesn't support CSS transitions.
//
// We need this ugly calling form for old browsers that don't allow
// panel.style.hasOwnProperty('transition'); catering to old browsers
// is the whole point here.
var animate = panel.style.transition !== null && (typeof(panel.style.transition) == "string");
| > > > > > > > > > > > > > > > > > > > > > | | > > > > > | 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
*******************************************************************************
**
** This file contains the JS code specific to the Fossil default skin.
** Currently, the only thing this does is handle clicks on its hamburger
** menu button.
*/
(function() {
var hbButton = document.getElementById("hbbtn");
if (!hbButton) return; // no hamburger button
if (!document.addEventListener) {
// Turn the button into a link to the sitemap for incompatible browsers.
hbButton.href = "$home/sitemap";
return;
}
var panel = document.getElementById("hbdrop");
if (!panel) return; // site admin might've nuked it
if (!panel.style) return; // shouldn't happen, but be sure
var panelBorder = panel.style.border;
var panelInitialized = false; // reset if browser window is resized
var panelResetBorderTimerID = 0; // used to cancel post-animation tasks
// Disable animation if this browser doesn't support CSS transitions.
//
// We need this ugly calling form for old browsers that don't allow
// panel.style.hasOwnProperty('transition'); catering to old browsers
// is the whole point here.
var animate = panel.style.transition !== null && (typeof(panel.style.transition) == "string");
// The duration of the animation can be overridden from the default skin
// header.txt by setting the "data-anim-ms" attribute of the panel.
var animMS = panel.getAttribute("data-anim-ms");
if (animMS) { // not null or empty string, parse it
animMS = parseInt(animMS);
if (isNaN(animMS) || animMS == 0)
animate = false; // disable animation if non-numeric or zero
else if (animMS < 0)
animMS = 400; // set default animation duration if negative
}
else // attribute is null or empty string, use default
animMS = 400;
// Calculate panel height despite its being hidden at call time.
// Based on https://stackoverflow.com/a/29047447/142454
var panelHeight; // computed on first panel display
function calculatePanelHeight() {
// Clear the max-height CSS property in case the panel size is recalculated
// after the browser window was resized.
panel.style.maxHeight = '';
// Get initial panel styles so we can restore them below.
var es = window.getComputedStyle(panel),
edis = es.display,
epos = es.position,
evis = es.visibility;
// Restyle the panel so we can measure its height while invisible.
|
| ︙ | ︙ | |||
60 61 62 63 64 65 66 67 68 69 70 71 72 |
// first call, where the div was initially invisible. If we were
// to change the panel's visibility and height at the same time
// instead, that would prevent the browser from seeing the height
// change as a state transition, so it'd skip the CSS transition:
//
// https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions#JavaScript_examples
function showPanel() {
if (animate) {
setTimeout(function() {
panel.style.maxHeight = panelHeight;
panel.style.border = panelBorder;
}, 40); // 25ms is insufficient with Firefox 62
}
| > > > > > > > > > > > > > > > > > > < | > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > | > | > > > > > > > > | | | > | > | > | | | | | | | | | < | < | < < < < < < < < < < < | | | | | | | | | < | > | 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
// first call, where the div was initially invisible. If we were
// to change the panel's visibility and height at the same time
// instead, that would prevent the browser from seeing the height
// change as a state transition, so it'd skip the CSS transition:
//
// https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions#JavaScript_examples
function showPanel() {
// Cancel the timer to remove the panel border after the closing animation,
// otherwise double-clicking the hamburger button with the panel opened will
// remove the borders from the (closed and immediately reopened) panel.
if (panelResetBorderTimerID) {
clearTimeout(panelResetBorderTimerID);
panelResetBorderTimerID = 0;
}
if (animate) {
if (!panelInitialized) {
panelInitialized = true;
// Set up a CSS transition to animate the panel open and
// closed. Only needs to be done once per page load.
// Based on https://stackoverflow.com/a/29047447/142454
calculatePanelHeight();
panel.style.transition = 'max-height ' + animMS +
'ms ease-in-out';
panel.style.overflowY = 'hidden';
panel.style.maxHeight = '0';
}
setTimeout(function() {
panel.style.maxHeight = panelHeight;
panel.style.border = panelBorder;
}, 40); // 25ms is insufficient with Firefox 62
}
panel.style.display = 'block';
document.addEventListener('keydown',panelKeydown,/* useCapture == */true);
document.addEventListener('click',panelClick,false);
}
var panelKeydown = function(event) {
var key = event.which || event.keyCode;
if (key == 27) {
event.stopPropagation(); // ignore other keydown handlers
panelToggle(true);
}
};
var panelClick = function(event) {
if (!panel.contains(event.target)) {
// Call event.preventDefault() to have clicks outside the opened panel
// just close the panel, and swallow clicks on links or form elements.
//event.preventDefault();
panelToggle(true);
}
};
// Return true if the panel is showing.
function panelShowing() {
if (animate) {
return panel.style.maxHeight == panelHeight;
}
else {
return panel.style.display == 'block';
}
}
// Check if the specified HTML element has any child elements. Note that plain
// text nodes, comments, and any spaces (presentational or not) are ignored.
function hasChildren(element) {
var childElement = element.firstChild;
while (childElement) {
if (childElement.nodeType == 1) // Node.ELEMENT_NODE == 1
return true;
childElement = childElement.nextSibling;
}
return false;
}
// Reset the state of the panel to uninitialized if the browser window is
// resized, so the dimensions are recalculated the next time it's opened.
window.addEventListener('resize',function(event) {
panelInitialized = false;
},false);
// Click handler for the hamburger button.
hbButton.addEventListener('click',function(event) {
// Break the event handler chain, or the handler for document → click
// (about to be installed) may already be triggered by the current event.
event.stopPropagation();
event.preventDefault(); // prevent browser from acting on <a> click
panelToggle(false);
},false);
function panelToggle(suppressAnimation) {
if (panelShowing()) {
document.removeEventListener('keydown',panelKeydown,/* useCapture == */true);
document.removeEventListener('click',panelClick,false);
// Transition back to hidden state.
if (animate) {
if (suppressAnimation) {
var transition = panel.style.transition;
panel.style.transition = '';
panel.style.maxHeight = '0';
panel.style.border = 'none';
setTimeout(function() {
// Make sure CSS transition won't take effect now, so restore it
// asynchronously. Outer variable 'transition' still valid here.
panel.style.transition = transition;
}, 40); // 25ms is insufficient with Firefox 62
}
else {
panel.style.maxHeight = '0';
panelResetBorderTimerID = setTimeout(function() {
// Browsers show a 1px high border line when maxHeight == 0,
// our "hidden" state, so hide the borders in that state, too.
panel.style.border = 'none';
panelResetBorderTimerID = 0; // clear ID of completed timer
}, animMS);
}
}
else {
panel.style.display = 'none';
}
}
else {
if (!hasChildren(panel)) {
// Only get the sitemap once per page load: it isn't likely to
// change on us.
var xhr = new XMLHttpRequest();
xhr.onload = function() {
var doc = xhr.responseXML;
if (doc) {
var sm = doc.querySelector("ul#sitemap");
if (sm && xhr.status == 200) {
// Got sitemap. Insert it into the drop-down panel.
panel.innerHTML = sm.outerHTML;
// Display the panel
showPanel();
}
}
// else, can't parse response as HTML or XML
}
xhr.open("GET", "$home/sitemap?popup"); // note the TH1 substitution!
xhr.responseType = "document";
xhr.send();
}
else {
showPanel(); // just show what we built above
}
}
}
})();
|
Changes to skins/eagle/css.txt.
| ︙ | ︙ | |||
217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
border-right: 3px solid #fff;
}
/* right merge arrow */
.tl-arrow.merge.r {
border-left: 3px solid #fff;
}
/* Side-by-side diff */
table.sbsdiff {
background-color: #485D7B;
font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
font-size: 8pt;
border-collapse:collapse;
| > > > > > > > > > > > > > > > > > > > > > > > > | 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
border-right: 3px solid #fff;
}
/* right merge arrow */
.tl-arrow.merge.r {
border-left: 3px solid #fff;
}
.tl-arrow.cherrypick {
height: 1px;
border-width: 2px 0;
}
.tl-arrow.cherrypick.l {
border-right: 3px solid #fff;
}
.tl-arrow.cherrypick.r {
border-left: 3px solid #fff;
}
.tl-line.cherrypick.h {
width: 0px;
border-top: 1px dashed #fff;
border-left: 0px dashed #fff;
background: rgba(255,255,255,0);
}
.tl-line.cherrypick.v {
width: 0px;
border-top: 0px dashed #fff;
border-left: 1px dashed #fff;
background: rgba(255,255,255,0);
}
/* Side-by-side diff */
table.sbsdiff {
background-color: #485D7B;
font-family: fixed, Dejavu Sans Mono, Monaco, Lucida Console, monospace;
font-size: 8pt;
border-collapse:collapse;
|
| ︙ | ︙ |
Changes to skins/enhanced1/header.txt.
| ︙ | ︙ | |||
61 62 63 64 65 66 67 |
set logourl [getLogoUrl $baseurl]
</th1>
<a href="$logourl">
<img src="$logo_image_url" border="0" alt="$project_name">
</a>
</div>
<div class="title">$<title></div>
| | | 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
set logourl [getLogoUrl $baseurl]
</th1>
<a href="$logourl">
<img src="$logo_image_url" border="0" alt="$project_name">
</a>
</div>
<div class="title">$<title></div>
<div class="status"><nobr><th1>
if {[info exists login]} {
puts "Logged in as $login"
} else {
puts "Not logged in"
}
</th1></nobr><small><div id="clock"></div></small></div>
</div>
|
| ︙ | ︙ |
Changes to src/add.c.
| ︙ | ︙ | |||
185 186 187 188 189 190 191 |
db_multi_exec("UPDATE vfile SET deleted=0"
" WHERE pathname=%Q %s AND deleted",
zPath, filename_collation());
}else{
char *zFullname = mprintf("%s%s", g.zLocalRoot, zPath);
int isExe = file_isexe(zFullname, RepoFILE);
db_multi_exec(
| | | | 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
db_multi_exec("UPDATE vfile SET deleted=0"
" WHERE pathname=%Q %s AND deleted",
zPath, filename_collation());
}else{
char *zFullname = mprintf("%s%s", g.zLocalRoot, zPath);
int isExe = file_isexe(zFullname, RepoFILE);
db_multi_exec(
"INSERT INTO vfile(vid,deleted,rid,mrid,pathname,isexe,islink,mhash)"
"VALUES(%d,0,0,0,%Q,%d,%d,NULL)",
vid, zPath, isExe, file_islink(0));
fossil_free(zFullname);
}
if( db_changes() ){
fossil_print("ADDED %s\n", zPath);
return 1;
}else{
|
| ︙ | ︙ |
Changes to src/backoffice.c.
| ︙ | ︙ | |||
78 79 80 81 82 83 84 85 86 87 88 89 90 91 | # include <unistd.h> # include <sys/types.h> # include <signal.h> # include <errno.h> # include <fcntl.h> # define GETPID getpid #endif /* ** The BKOFCE_LEASE_TIME is the amount of time for which a single backoffice ** processing run is valid. Each backoffice run monopolizes the lease for ** at least this amount of time. Hopefully all backoffice processing is ** finished much faster than this - usually in less than a second. But ** regardless of how long each invocation lasts, successive backoffice runs | > | 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 | # include <unistd.h> # include <sys/types.h> # include <signal.h> # include <errno.h> # include <fcntl.h> # define GETPID getpid #endif #include <time.h> /* ** The BKOFCE_LEASE_TIME is the amount of time for which a single backoffice ** processing run is valid. Each backoffice run monopolizes the lease for ** at least this amount of time. Hopefully all backoffice processing is ** finished much faster than this - usually in less than a second. But ** regardless of how long each invocation lasts, successive backoffice runs |
| ︙ | ︙ | |||
452 453 454 455 456 457 458 459 460 461 462 463 464 465 |
&& x.idNext!=idSelf
&& backofficeProcessExists(x.idNext)
){
/* Another backoffice process is already queued up to run. This
** process does not need to do any backoffice work and can stop
** immediately. */
db_end_transaction(0);
break;
}
if( x.tmCurrent<tmNow && backofficeProcessDone(x.idCurrent) ){
/* This process can start doing backoffice work immediately */
x.idCurrent = idSelf;
x.tmCurrent = tmNow + BKOFCE_LEASE_TIME;
x.idNext = 0;
| > > | 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 |
&& x.idNext!=idSelf
&& backofficeProcessExists(x.idNext)
){
/* Another backoffice process is already queued up to run. This
** process does not need to do any backoffice work and can stop
** immediately. */
db_end_transaction(0);
backofficeTrace("/***** Backoffice Processing Not Needed In %d *****/\n",
GETPID());
break;
}
if( x.tmCurrent<tmNow && backofficeProcessDone(x.idCurrent) ){
/* This process can start doing backoffice work immediately */
x.idCurrent = idSelf;
x.tmCurrent = tmNow + BKOFCE_LEASE_TIME;
x.idNext = 0;
|
| ︙ | ︙ | |||
474 475 476 477 478 479 480 481 482 483 484 485 486 487 |
break;
}
if( backofficeNoDelay || db_get_boolean("backoffice-nodelay",0) ){
/* If the no-delay flag is set, exit immediately rather than queuing
** up. Assume that some future request will come along and handle any
** necessary backoffice work. */
db_end_transaction(0);
break;
}
/* This process needs to queue up and wait for the current lease
** to expire before continuing. */
x.idNext = idSelf;
x.tmNext = (tmNow>x.tmCurrent ? tmNow : x.tmCurrent) + BKOFCE_LEASE_TIME;
backofficeWriteLease(&x);
| > > > | 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 |
break;
}
if( backofficeNoDelay || db_get_boolean("backoffice-nodelay",0) ){
/* If the no-delay flag is set, exit immediately rather than queuing
** up. Assume that some future request will come along and handle any
** necessary backoffice work. */
db_end_transaction(0);
backofficeTrace(
"/***** Backoffice No-Delay Exit For %d *****/\n",
GETPID());
break;
}
/* This process needs to queue up and wait for the current lease
** to expire before continuing. */
x.idNext = idSelf;
x.tmNext = (tmNow>x.tmCurrent ? tmNow : x.tmCurrent) + BKOFCE_LEASE_TIME;
backofficeWriteLease(&x);
|
| ︙ | ︙ | |||
535 536 537 538 539 540 541 | alert_backoffice(0); smtp_cleanup(); } /* ** COMMAND: backoffice* ** | | > > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 |
alert_backoffice(0);
smtp_cleanup();
}
/*
** COMMAND: backoffice*
**
** Usage: backoffice [OPTIONS...] [REPOSITORIES...]
**
** Run backoffice processing on the repositories listed. If no
** repository is specified, run it on the repository of the local checkout.
**
** This might be done by a cron job or similar to make sure backoffice
** processing happens periodically. Or, the --poll option can be used
** to run this command as a daemon that will periodically invoke backoffice
** on collection of repositories.
**
** OPTIONS:
**
** --debug Show what this command is doing.
**
** --nodelay Do not queue up or wait for a backoffice job
** to complete. If no work is available or if
** backoffice has run recently, return immediately.
** The --nodelay option is implied if more than
** one repository is listed on the command-line.
**
** --poll N Repeat backoffice calls for repositories that
** change in appoximately N-second intervals.
** N less than 1 turns polling off (the default).
**
** --trace Enable debugging output on stderr
*/
void backoffice_command(void){
int nPoll;
const char *zPoll;
int bDebug = 0;
unsigned int nCmd = 0;
if( find_option("trace",0,0)!=0 ) g.fAnyTrace = 1;
if( find_option("nodelay",0,0)!=0 ) backofficeNoDelay = 1;
zPoll = find_option("poll",0,1);
nPoll = zPoll ? atoi(zPoll) : 0;
bDebug = find_option("debug",0,0)!=0;
/* Silently consume the -R or --repository flag, leaving behind its
** argument. This is for legacy compatibility. Older versions of the
** backoffice command only ran on a single repository that was specified
** using the -R option. */
(void)find_option("repository","R",0);
verify_all_options();
if( g.argc>3 || nPoll>0 ){
/* Either there are multiple repositories named on the command-line
** or we are polling. In either case, each backoffice should be run
** using a separate sub-process */
int i;
time_t iNow = 0;
time_t ix;
while( 1 /* exit via "break;" */){
time_t iNext = time(0);
for(i=2; i<g.argc; i++){
Blob cmd;
if( !file_isfile(g.argv[i], ExtFILE) ) continue;
if( iNow && iNow>file_mtime(g.argv[i],ExtFILE) ) continue;
blob_init(&cmd, 0, 0);
blob_append_escaped_arg(&cmd, g.nameOfExe);
blob_append(&cmd, " backoffice --nodelay", -1);
if( g.fAnyTrace ){
blob_append(&cmd, " --trace", -1);
}
blob_append_escaped_arg(&cmd, g.argv[i]);
nCmd++;
if( bDebug ){
fossil_print("COMMAND[%u]: %s\n", nCmd, blob_str(&cmd));
}
fossil_system(blob_str(&cmd));
blob_reset(&cmd);
}
if( nPoll<1 ) break;
iNow = iNext;
ix = time(0);
if( ix < iNow+nPoll ){
sqlite3_int64 nMS = (iNow + nPoll - ix)*1000;
if( bDebug )fossil_print("SLEEP: %lld\n", nMS);
sqlite3_sleep((int)nMS);
}
}
}else{
if( g.argc==3 ){
g.zRepositoryOption = g.argv[2];
g.argc--;
}
db_find_and_open_repository(0,0);
backoffice_thread();
}
}
/*
** This is the main interface to backoffice from the rest of the system.
** This routine launches either backoffice_thread() directly or as a
** subprocess.
*/
|
| ︙ | ︙ |
Changes to src/bisect.c.
| ︙ | ︙ | |||
170 171 172 173 174 175 176 |
" || '%d')", rid);
}
/*
** Create a TEMP table named "bilog" that contains the complete history
** of the current bisect.
*/
| | | > > > > > > > > > > > > > > > > > > > > > > > | > | 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
" || '%d')", rid);
}
/*
** Create a TEMP table named "bilog" that contains the complete history
** of the current bisect.
*/
int bisect_create_bilog_table(int iCurrent, const char *zDesc){
char *zLog;
Blob log, id;
Stmt q;
int cnt = 0;
if( zDesc!=0 ){
blob_init(&log, 0, 0);
while( zDesc[0]=='y' || zDesc[0]=='n' ){
int i;
char c;
int rid;
if( blob_size(&log) ) blob_append(&log, " ", 1);
if( zDesc[0]=='n' ) blob_append(&log, "-", 1);
for(i=1; ((c = zDesc[i])>='0' && c<='9') || (c>='a' && c<='f'); i++){}
if( i==1 ) break;
rid = db_int(0,
"SELECT rid FROM blob"
" WHERE uuid LIKE '%.*q%%'"
" AND EXISTS(SELECT 1 FROM plink WHERE cid=rid)",
i-1, zDesc+1
);
if( rid==0 ) break;
blob_appendf(&log, "%d", rid);
zDesc += i;
}
}else{
zLog = db_lget("bisect-log","");
blob_init(&log, zLog, -1);
}
db_multi_exec(
"CREATE TEMP TABLE bilog("
" seq INTEGER PRIMARY KEY," /* Sequence of events */
" stat TEXT," /* Type of occurrence */
" rid INTEGER UNIQUE" /* Check-in number */
");"
);
|
| ︙ | ︙ | |||
200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
if( iCurrent>0 ){
db_bind_int(&q, ":seq", ++cnt);
db_bind_text(&q, ":stat", "CURRENT");
db_bind_int(&q, ":rid", iCurrent);
db_step(&q);
}
db_finalize(&q);
}
/*
** Show a chart of bisect "good" and "bad" versions. The chart can be
** sorted either chronologically by bisect time, or by check-in time.
*/
static void bisect_chart(int sortByCkinTime){
Stmt q;
int iCurrent = db_lget_int("checkout",0);
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 |
if( iCurrent>0 ){
db_bind_int(&q, ":seq", ++cnt);
db_bind_text(&q, ":stat", "CURRENT");
db_bind_int(&q, ":rid", iCurrent);
db_step(&q);
}
db_finalize(&q);
return 1;
}
/* Return a permalink description of a bisect. Space is obtained from
** fossil_malloc() and should be freed by the caller.
**
** A bisect description consists of characters 'y' and 'n' and lowercase
** hex digits. Each term begins with 'y' or 'n' (success or failure) and
** is followed by a hash prefix in lowercase hex.
*/
char *bisect_permalink(void){
char *zLog = db_lget("bisect-log","");
char *zResult;
Blob log;
Blob link = BLOB_INITIALIZER;
Blob id;
blob_init(&log, zLog, -1);
while( blob_token(&log, &id) ){
int rid = atoi(blob_str(&id));
char *zUuid = db_text(0,"SELECT lower(uuid) FROM blob WHERE rid=%d",
rid<0 ? -rid : rid);
blob_appendf(&link, "%c%.10s", rid<0 ? 'n' : 'y', zUuid);
}
zResult = mprintf("%s", blob_str(&link));
blob_reset(&link);
blob_reset(&log);
blob_reset(&id);
return zResult;
}
/*
** Show a chart of bisect "good" and "bad" versions. The chart can be
** sorted either chronologically by bisect time, or by check-in time.
*/
static void bisect_chart(int sortByCkinTime){
Stmt q;
int iCurrent = db_lget_int("checkout",0);
bisect_create_bilog_table(iCurrent, 0);
db_prepare(&q,
"SELECT bilog.seq, bilog.stat,"
" substr(blob.uuid,1,16), datetime(event.mtime),"
" blob.rid==%d"
" FROM bilog, blob, event"
" WHERE blob.rid=bilog.rid AND event.objid=bilog.rid"
" AND event.type='ci'"
|
| ︙ | ︙ | |||
232 233 234 235 236 237 238 239 240 241 242 243 244 245 |
db_column_text(&q, 3),
db_column_text(&q, 2),
(db_column_int(&q, 4) && zGoodBad[0]!='C') ? " CURRENT" : "");
}
db_finalize(&q);
}
/*
** COMMAND: bisect
**
** Usage: %fossil bisect SUBCOMMAND ...
**
** Run various subcommands useful for searching for bugs.
**
| > > > > > > > > > > > | 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 |
db_column_text(&q, 3),
db_column_text(&q, 2),
(db_column_int(&q, 4) && zGoodBad[0]!='C') ? " CURRENT" : "");
}
db_finalize(&q);
}
/*
** Reset the bisect subsystem.
*/
void bisect_reset(void){
db_multi_exec(
"DELETE FROM vvar WHERE name IN "
" ('bisect-good', 'bisect-bad', 'bisect-log')"
);
}
/*
** COMMAND: bisect
**
** Usage: %fossil bisect SUBCOMMAND ...
**
** Run various subcommands useful for searching for bugs.
**
|
| ︙ | ︙ | |||
412 413 414 415 416 417 418 |
if( g.argc==3 ){
unsigned int i;
for(i=0; i<count(aBisectOption); i++){
char *z = mprintf("bisect-%s", aBisectOption[i].zName);
fossil_print(" %-15s %-6s ", aBisectOption[i].zName,
db_lget(z, (char*)aBisectOption[i].zDefault));
fossil_free(z);
| | | 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 |
if( g.argc==3 ){
unsigned int i;
for(i=0; i<count(aBisectOption); i++){
char *z = mprintf("bisect-%s", aBisectOption[i].zName);
fossil_print(" %-15s %-6s ", aBisectOption[i].zName,
db_lget(z, (char*)aBisectOption[i].zDefault));
fossil_free(z);
comment_print(aBisectOption[i].zDesc, 0, 27, -1, get_comment_format());
}
}else if( g.argc==4 || g.argc==5 ){
unsigned int i;
n = strlen(g.argv[3]);
for(i=0; i<count(aBisectOption); i++){
if( strncmp(g.argv[3], aBisectOption[i].zName, n)==0 ){
char *z = mprintf("bisect-%s", aBisectOption[i].zName);
|
| ︙ | ︙ | |||
435 436 437 438 439 440 441 |
if( i>=count(aBisectOption) ){
fossil_fatal("no such bisect option: %s", g.argv[3]);
}
}else{
usage("options ?NAME? ?VALUE?");
}
}else if( strncmp(zCmd, "reset", n)==0 ){
| < < < | | 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 |
if( i>=count(aBisectOption) ){
fossil_fatal("no such bisect option: %s", g.argv[3]);
}
}else{
usage("options ?NAME? ?VALUE?");
}
}else if( strncmp(zCmd, "reset", n)==0 ){
bisect_reset();
}else if( strcmp(zCmd, "ui")==0 ){
char *newArgv[8];
newArgv[0] = g.argv[0];
newArgv[1] = "ui";
newArgv[2] = "--page";
newArgv[3] = "timeline?bisect";
newArgv[4] = 0;
|
| ︙ | ︙ |
Changes to src/blob.c.
| ︙ | ︙ | |||
277 278 279 280 281 282 283 284 285 286 287 |
pBlob->xRealloc = blobReallocStatic;
}
/*
** Append text or data to the end of a blob.
*/
void blob_append(Blob *pBlob, const char *aData, int nData){
assert( aData!=0 || nData==0 );
blob_is_init(pBlob);
if( nData<0 ) nData = strlen(aData);
if( nData==0 ) return;
| > > > | > > > > > | | 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
pBlob->xRealloc = blobReallocStatic;
}
/*
** Append text or data to the end of a blob.
*/
void blob_append(Blob *pBlob, const char *aData, int nData){
sqlite3_int64 nNew;
assert( aData!=0 || nData==0 );
blob_is_init(pBlob);
if( nData<0 ) nData = strlen(aData);
if( nData==0 ) return;
nNew = pBlob->nUsed;
nNew += nData;
if( nNew >= pBlob->nAlloc ){
nNew += pBlob->nAlloc;
nNew += 100;
if( nNew>=0x7fff0000 ){
blob_panic();
}
pBlob->xRealloc(pBlob, (int)nNew);
if( pBlob->nUsed + nData >= pBlob->nAlloc ){
blob_panic();
}
}
memcpy(&pBlob->aData[pBlob->nUsed], aData, nData);
pBlob->nUsed += nData;
pBlob->aData[pBlob->nUsed] = 0; /* Blobs are always nul-terminated */
|
| ︙ | ︙ | |||
1253 1254 1255 1256 1257 1258 1259 |
#if defined(_WIN32)
const char cQuote = '"'; /* Use "..." quoting on windows */
#else
const char cQuote = '\''; /* Use '...' quoting on unix */
#endif
for(i=0; (c = zIn[i])!=0; i++){
| > | | 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 |
#if defined(_WIN32)
const char cQuote = '"'; /* Use "..." quoting on windows */
#else
const char cQuote = '\''; /* Use '...' quoting on unix */
#endif
for(i=0; (c = zIn[i])!=0; i++){
if( c==cQuote || (unsigned char)c<' ' ||
c=='\\' || c==';' || c=='*' || c=='?' || c=='[' ){
Blob bad;
blob_token(pBlob, &bad);
fossil_fatal("the [%s] argument to the \"%s\" command contains "
"a character (ascii 0x%02x) that is a security risk",
zIn, blob_str(&bad), c);
}
if( !needEscape && !fossil_isalnum(c) && c!='/' && c!='.' && c!='_' ){
|
| ︙ | ︙ |
Changes to src/branch.c.
| ︙ | ︙ | |||
18 19 20 21 22 23 24 | ** This file contains code used to create new branches within a repository. */ #include "config.h" #include "branch.h" #include <assert.h> /* | | | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
** This file contains code used to create new branches within a repository.
*/
#include "config.h"
#include "branch.h"
#include <assert.h>
/*
** fossil branch new NAME BASIS ?OPTIONS?
** argv0 argv1 argv2 argv3 argv4
*/
void branch_new(void){
int rootid; /* RID of the root check-in - what we branch off of */
int brid; /* RID of the branch check-in */
int noSign; /* True if the branch is unsigned */
int i; /* Loop counter */
|
| ︙ | ︙ | |||
433 434 435 436 437 438 439 |
brlist_create_temp_table();
db_prepare(&q, "SELECT * FROM tmp_brlist ORDER BY mtime DESC");
rNow = db_double(0.0, "SELECT julianday('now')");
@ <div class="brlist">
@ <table class='sortable' data-column-types='tkNtt' data-init-sort='2'>
@ <thead><tr>
@ <th>Branch Name</th>
| | | 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 |
brlist_create_temp_table();
db_prepare(&q, "SELECT * FROM tmp_brlist ORDER BY mtime DESC");
rNow = db_double(0.0, "SELECT julianday('now')");
@ <div class="brlist">
@ <table class='sortable' data-column-types='tkNtt' data-init-sort='2'>
@ <thead><tr>
@ <th>Branch Name</th>
@ <th>Last Change</th>
@ <th>Check-ins</th>
@ <th>Status</th>
@ <th>Resolution</th>
@ </tr></thead><tbody>
while( db_step(&q)==SQLITE_ROW ){
const char *zBranch = db_column_text(&q, 0);
double rMtime = db_column_double(&q, 1);
|
| ︙ | ︙ | |||
461 462 463 464 465 466 467 |
}
}
if( zBgClr && zBgClr[0] && show_colors ){
@ <tr style="background-color:%s(zBgClr)">
}else{
@ <tr>
}
| | | 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 |
}
}
if( zBgClr && zBgClr[0] && show_colors ){
@ <tr style="background-color:%s(zBgClr)">
}else{
@ <tr>
}
@ <td>%z(href("%R/timeline?r=%T",zBranch))%h(zBranch)</a></td>
@ <td data-sortkey="%016llx(iMtime)">%s(zAge)</td>
@ <td>%d(nCkin)</td>
fossil_free(zAge);
@ <td>%s(isClosed?"closed":"")</td>
if( zMergeTo ){
@ <td>merged into
@ %z(href("%R/timeline?f=%!S",zLastCkin))%h(zMergeTo)</a></td>
|
| ︙ | ︙ | |||
574 575 576 577 578 579 580 |
cnt++;
}
if( colorTest ){
const char *zColor = hash_color(zBr);
@ <li><span style="background-color: %s(zColor)">
@ %h(zBr) → %s(zColor)</span></li>
}else{
| | | 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 |
cnt++;
}
if( colorTest ){
const char *zColor = hash_color(zBr);
@ <li><span style="background-color: %s(zColor)">
@ %h(zBr) → %s(zColor)</span></li>
}else{
@ <li>%z(href("%R/timeline?r=%T",zBr))%h(zBr)</a></li>
}
}
if( cnt ){
@ </ul>
}
db_finalize(&q);
style_footer();
|
| ︙ | ︙ | |||
602 603 604 605 606 607 608 |
" AND tagxref.tagid=tag.tagid"
" AND tagxref.tagtype>0"
" AND tag.tagname GLOB 'sym-*'",
rid
);
while( db_step(&q)==SQLITE_ROW ){
const char *zTagName = db_column_text(&q, 0);
| | > > > > > > > > > > > > > > > | | | > > > > > > > | < | > > > > > > | | 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 |
" AND tagxref.tagid=tag.tagid"
" AND tagxref.tagtype>0"
" AND tag.tagname GLOB 'sym-*'",
rid
);
while( db_step(&q)==SQLITE_ROW ){
const char *zTagName = db_column_text(&q, 0);
@ %z(href("%R/timeline?r=%T",zTagName))[timeline]</a>
}
db_finalize(&q);
}
/*
** WEBPAGE: brtimeline
**
** Show a timeline of all branches
**
** Query parameters:
**
** ng No graph
** nohidden Hide check-ins with "hidden" tag
** onlyhidden Show only check-ins with "hidden" tag
** brbg Background color by branch name
** ubg Background color by user name
*/
void brtimeline_page(void){
Blob sql = empty_blob;
Stmt q;
int tmFlags; /* Timeline display flags */
int fNoHidden = PB("nohidden")!=0; /* The "nohidden" query parameter */
int fOnlyHidden = PB("onlyhidden")!=0; /* The "onlyhidden" query parameter */
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
style_header("Branches");
style_submenu_element("List", "brlist");
login_anonymous_available();
timeline_ss_submenu();
cookie_render();
@ <h2>The initial check-in for each branch:</h2>
blob_append(&sql, timeline_query_for_www(), -1);
blob_append_sql(&sql,
"AND blob.rid IN (SELECT rid FROM tagxref"
" WHERE tagtype>0 AND tagid=%d AND srcid!=0)", TAG_BRANCH);
if( fNoHidden || fOnlyHidden ){
const char* zUnaryOp = fNoHidden ? "NOT" : "";
blob_append_sql(&sql,
" AND %s EXISTS(SELECT 1 FROM tagxref"
" WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)\n",
zUnaryOp/*safe-for-%s*/, TAG_HIDDEN);
}
db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_sql_text(&sql));
blob_reset(&sql);
/* Always specify TIMELINE_DISJOINT, or graph_finish() may fail because of too
** many descenders to (off-screen) parents. */
tmFlags = TIMELINE_DISJOINT | TIMELINE_NOSCROLL;
if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH;
if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR;
if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR;
www_print_timeline(&q, tmFlags, 0, 0, 0, brtimeline_extra);
db_finalize(&q);
style_footer();
}
|
Changes to src/capabilities.c.
| ︙ | ︙ | |||
362 363 364 365 366 367 368 |
"('developer',4))"
" SELECT id, fullcap(user.cap),seq,1"
" FROM t LEFT JOIN user ON t.id=user.login"
" UNION ALL"
" SELECT 'New User Default', fullcap(%Q), 10, 1"
" UNION ALL"
" SELECT 'Regular User', fullcap(capunion(cap)), 20, count(*) FROM user"
| | | | 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 |
"('developer',4))"
" SELECT id, fullcap(user.cap),seq,1"
" FROM t LEFT JOIN user ON t.id=user.login"
" UNION ALL"
" SELECT 'New User Default', fullcap(%Q), 10, 1"
" UNION ALL"
" SELECT 'Regular User', fullcap(capunion(cap)), 20, count(*) FROM user"
" WHERE cap NOT GLOB '*[as]*' AND login NOT IN (SELECT id FROM t)"
" UNION ALL"
" SELECT 'Adminstrator', fullcap(capunion(cap)), 30, count(*) FROM user"
" WHERE cap GLOB '*[as]*'"
" ORDER BY 3 ASC",
db_get("default-perms","")
);
@ <table id='capabilitySummary' cellpadding="0" cellspacing="0" border="1">
@ <tr><th> <th>Code<th>Forum<th>Tickets<th>Wiki\
@ <th>Unversioned Content</th></tr>
|
| ︙ | ︙ |
Changes to src/cgi.c.
| ︙ | ︙ | |||
352 353 354 355 356 357 358 | } /* ** Do a redirect request to the URL given in the argument. ** ** The URL must be relative to the base of the fossil server. */ | | | 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 |
}
/*
** Do a redirect request to the URL given in the argument.
**
** The URL must be relative to the base of the fossil server.
*/
NORETURN void cgi_redirect_with_status(
const char *zURL,
int iStat,
const char *zStat
){
char *zLocation;
CGIDEBUG(("redirect to %s\n", zURL));
if( strncmp(zURL,"http:",5)==0 || strncmp(zURL,"https:",6)==0 ){
|
| ︙ | ︙ |
Changes to src/checkin.c.
| ︙ | ︙ | |||
305 306 307 308 309 310 311 |
}
blob_reset(&rewrittenPathname);
db_finalize(&q);
/* If C_MERGE, put merge contributors at the end of the report. */
skipFiles:
if( flags & C_MERGE ){
| | < | 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
}
blob_reset(&rewrittenPathname);
db_finalize(&q);
/* If C_MERGE, put merge contributors at the end of the report. */
skipFiles:
if( flags & C_MERGE ){
db_prepare(&q, "SELECT mhash, id FROM vmerge WHERE id<=0" );
while( db_step(&q)==SQLITE_ROW ){
if( flags & C_COMMENT ){
blob_append(report, "# ", 2);
}
if( flags & C_CLASSIFY ){
const char *zClass;
switch( db_column_int(&q, 1) ){
|
| ︙ | ︙ | |||
1208 1209 1210 1211 1212 1213 1214 |
Blob fname;
blob_zero(&fname);
if( g.zLocalRoot!=0 ){
file_relative_name(g.zLocalRoot, &fname, 1);
zFile = db_text(0, "SELECT '%qci-comment-'||hex(randomblob(6))||'.txt'",
blob_str(&fname));
}else{
| | | 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 |
Blob fname;
blob_zero(&fname);
if( g.zLocalRoot!=0 ){
file_relative_name(g.zLocalRoot, &fname, 1);
zFile = db_text(0, "SELECT '%qci-comment-'||hex(randomblob(6))||'.txt'",
blob_str(&fname));
}else{
file_tempname(&fname, "ci-comment",0);
zFile = mprintf("%s", blob_str(&fname));
}
blob_reset(&fname);
}
#if defined(_WIN32)
blob_add_cr(pPrompt);
#endif
|
| ︙ | ︙ | |||
1633 1634 1635 1636 1637 1638 1639 |
}
db_finalize(&q);
blob_appendf(pOut, "\n");
}
free(zDate);
db_prepare(&q,
| | | < | 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 |
}
db_finalize(&q);
blob_appendf(pOut, "\n");
}
free(zDate);
db_prepare(&q,
"SELECT CASE vmerge.id WHEN -1 THEN '+' ELSE '-' END || mhash, merge"
" FROM vmerge"
" WHERE (vmerge.id=-1 OR vmerge.id=-2)"
" ORDER BY 1");
while( db_step(&q)==SQLITE_ROW ){
const char *zCherrypickUuid = db_column_text(&q, 0);
int mid = db_column_int(&q, 1);
if( mid != vid ){
blob_appendf(pOut, "Q %s\n", zCherrypickUuid);
}
|
| ︙ | ︙ | |||
1665 1666 1667 1668 1669 1670 1671 |
if( zColor && zColor[0] ){
/* One-time background color */
blob_appendf(pOut, "T +bgcolor * %F\n", zColor);
}
if( p->closeFlag ){
blob_appendf(pOut, "T +closed *\n");
}
| | | 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 |
if( zColor && zColor[0] ){
/* One-time background color */
blob_appendf(pOut, "T +bgcolor * %F\n", zColor);
}
if( p->closeFlag ){
blob_appendf(pOut, "T +closed *\n");
}
db_prepare(&q, "SELECT mhash,merge FROM vmerge"
" WHERE id %s ORDER BY 1",
p->integrateFlag ? "IN(0,-4)" : "=(-4)");
while( db_step(&q)==SQLITE_ROW ){
const char *zIntegrateUuid = db_column_text(&q, 0);
int rid = db_column_int(&q, 1);
if( is_a_leaf(rid) && !db_exists("SELECT 1 FROM tagxref "
" WHERE tagid=%d AND rid=%d AND tagtype>0", TAG_CLOSED, rid)){
|
| ︙ | ︙ | |||
2398 2399 2400 2401 2402 2403 2404 |
blob_reset(&fname);
}
nrid = content_put(&content);
blob_reset(&content);
if( rid>0 ){
content_deltify(rid, &nrid, 1, 0);
}
| | > | 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 |
blob_reset(&fname);
}
nrid = content_put(&content);
blob_reset(&content);
if( rid>0 ){
content_deltify(rid, &nrid, 1, 0);
}
db_multi_exec("UPDATE vfile SET mrid=%d, rid=%d, mhash=NULL WHERE id=%d",
nrid,nrid,id);
db_multi_exec("INSERT OR IGNORE INTO unsent VALUES(%d)", nrid);
}
db_finalize(&q);
if( nConflict && !allowConflict ){
fossil_fatal("abort due to unresolved merge conflicts; "
"use --allow-conflict to override");
}else if( abortCommit ){
|
| ︙ | ︙ | |||
2506 2507 2508 2509 2510 2511 2512 |
dryRunFlag ? MC_NONE : MC_PERMIT_HOOKS)==0 ){
fossil_fatal("%s", g.zErrMsg);
}
assert( blob_is_reset(&manifest) );
content_deltify(vid, &nvid, 1, 0);
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid);
| < | | 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 |
dryRunFlag ? MC_NONE : MC_PERMIT_HOOKS)==0 ){
fossil_fatal("%s", g.zErrMsg);
}
assert( blob_is_reset(&manifest) );
content_deltify(vid, &nvid, 1, 0);
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", nvid);
db_prepare(&q, "SELECT mhash,merge FROM vmerge WHERE id=-4");
while( db_step(&q)==SQLITE_ROW ){
const char *zIntegrateUuid = db_column_text(&q, 0);
if( is_a_leaf(db_column_int(&q, 1)) ){
fossil_print("Closed: %s\n", zIntegrateUuid);
}else{
fossil_print("Not_Closed: %s (not a leaf any more)\n", zIntegrateUuid);
}
|
| ︙ | ︙ | |||
2533 2534 2535 2536 2537 2538 2539 |
}
/* Update the vfile and vmerge tables */
db_multi_exec(
"DELETE FROM vfile WHERE (vid!=%d OR deleted) AND is_selected(id);"
"DELETE FROM vmerge;"
"UPDATE vfile SET vid=%d;"
| | | | 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 |
}
/* Update the vfile and vmerge tables */
db_multi_exec(
"DELETE FROM vfile WHERE (vid!=%d OR deleted) AND is_selected(id);"
"DELETE FROM vmerge;"
"UPDATE vfile SET vid=%d;"
"UPDATE vfile SET rid=mrid, mhash=NULL, chnged=0, deleted=0, origname=NULL"
" WHERE is_selected(id);"
, vid, nvid
);
db_set_checkout(nvid);
/* Update the isexe and islink columns of the vfile table */
db_prepare(&q,
"UPDATE vfile SET isexe=:exec, islink=:link"
" WHERE vid=:vid AND pathname=:path AND (isexe!=:exec OR islink!=:link)"
);
db_bind_int(&q, ":vid", nvid);
|
| ︙ | ︙ | |||
2613 2614 2615 2616 2617 2618 2619 |
get_checkin_taglist(nvid, &tagslist);
blob_write_to_file(&tagslist, zManifestFile);
blob_reset(&tagslist);
free(zManifestFile);
}
if( !g.markPrivate ){
| > | > | 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 |
get_checkin_taglist(nvid, &tagslist);
blob_write_to_file(&tagslist, zManifestFile);
blob_reset(&tagslist);
free(zManifestFile);
}
if( !g.markPrivate ){
int syncFlags = SYNC_PUSH | SYNC_PULL | SYNC_IFABLE;
int nTries = db_get_int("autosync-tries",1);
autosync_loop(syncFlags, nTries, 0);
}
if( count_nonbranch_children(vid)>1 ){
fossil_print("**** warning: a fork has occurred *****\n");
}
}
|
Changes to src/checkout.c.
| ︙ | ︙ | |||
37 38 39 40 41 42 43 44 45 |
return db_exists("SELECT 1 FROM vfile WHERE chnged"
" OR coalesce(origname!=pathname,0)");
}
/*
** Undo the current check-out. Unlink all files from the disk.
** Clear the VFILE table.
*/
void uncheckout(int vid){
| > > > > > | > > > > > > > > > > > > > > | < > > > > > > > > > > > > > > > > > > > > | 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
return db_exists("SELECT 1 FROM vfile WHERE chnged"
" OR coalesce(origname!=pathname,0)");
}
/*
** Undo the current check-out. Unlink all files from the disk.
** Clear the VFILE table.
**
** Also delete any directory that becomes empty as a result of deleting
** files due to this operation, as long as that directory is not the
** current working directory and is not on the empty-dirs list.
*/
void uncheckout(int vid){
char *zPwd;
if( vid<=0 ) return;
sqlite3_create_function(g.db, "dirname",1,SQLITE_UTF8,0,
file_dirname_sql_function, 0, 0);
sqlite3_create_function(g.db, "unlink",1,SQLITE_UTF8,0,
file_delete_sql_function, 0, 0);
sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8, 0,
file_rmdir_sql_function, 0, 0);
db_multi_exec(
"CREATE TEMP TABLE dir_to_delete(name TEXT %s PRIMARY KEY)WITHOUT ROWID",
filename_collation()
);
db_multi_exec(
"INSERT OR IGNORE INTO dir_to_delete(name)"
" SELECT dirname(pathname) FROM vfile"
" WHERE vid=%d AND mrid>0",
vid
);
do{
db_multi_exec(
"INSERT OR IGNORE INTO dir_to_delete(name)"
" SELECT dirname(name) FROM dir_to_delete;"
);
}while( db_changes() );
db_multi_exec(
"SELECT unlink(%Q||pathname) FROM vfile"
" WHERE vid=%d AND mrid>0;",
g.zLocalRoot, vid
);
ensure_empty_dirs_created(1);
zPwd = file_getcwd(0,0);
db_multi_exec(
"SELECT rmdir(%Q||name) FROM dir_to_delete"
" WHERE (%Q||name)<>%Q ORDER BY name DESC",
g.zLocalRoot, g.zLocalRoot, zPwd
);
fossil_free(zPwd);
db_multi_exec("DELETE FROM vfile WHERE vid=%d", vid);
}
/*
** Given the abbreviated UUID name of a version, load the content of that
** version in the VFILE table. Return the VID for the version.
|
| ︙ | ︙ | |||
301 302 303 304 305 306 307 |
}
db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
if( !keepFlag ){
vfile_to_disk(vid, 0, !g.fQuiet, promptFlag);
}
checkout_set_all_exe(vid);
manifest_to_disk(vid);
| | | | 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 |
}
db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
if( !keepFlag ){
vfile_to_disk(vid, 0, !g.fQuiet, promptFlag);
}
checkout_set_all_exe(vid);
manifest_to_disk(vid);
ensure_empty_dirs_created(0);
db_set_checkout(vid);
undo_reset();
db_multi_exec("DELETE FROM vmerge");
if( !keepFlag && db_get_boolean("repo-cksum",1) ){
vfile_aggregate_checksum_manifest(vid, &cksum1, &cksum1b);
vfile_aggregate_checksum_disk(vid, &cksum2);
if( blob_compare(&cksum1, &cksum2) ){
fossil_print("WARNING: manifest checksum does not agree with disk\n");
|
| ︙ | ︙ |
Changes to src/clone.c.
| ︙ | ︙ | |||
312 313 314 315 316 317 318 |
void download_page(void){
login_check_credentials();
style_header("Download Page");
if( !g.perm.Zip ){
@ <p>Bummer. You do not have permission to download.
if( g.zLogin==0 || g.zLogin[0]==0 ){
@ Maybe it would work better if you
| | | | 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 |
void download_page(void){
login_check_credentials();
style_header("Download Page");
if( !g.perm.Zip ){
@ <p>Bummer. You do not have permission to download.
if( g.zLogin==0 || g.zLogin[0]==0 ){
@ Maybe it would work better if you
@ %z(href("%R/login"))logged in</a>.
}else{
@ Contact the site administrator and ask them to give
@ you "Download Zip" privileges.
}
}else{
const char *zDLTag = db_get("download-tag","trunk");
const char *zNm = db_get("short-project-name","download");
char *zUrl = href("%R/zip/%t/%t.zip", zDLTag, zNm);
@ <p>ZIP Archive: %z(zUrl)%h(zNm).zip</a>
zUrl = href("%R/tarball/%t/%t.tar.gz", zDLTag, zNm);
@ <p>Tarball: %z(zUrl)%h(zNm).tar.gz</a>
zUrl = href("%R/sqlar/%t/%t.sqlar", zDLTag, zNm);
@ <p>SQLite Archive: %z(zUrl)%h(zNm).sqlar</a>
}
if( !g.perm.Clone ){
@ <p>You are not authorized to clone this repository.
if( g.zLogin==0 || g.zLogin[0]==0 ){
@ Maybe you would be able to clone if you
@ %z(href("%R/login"))logged in</a>.
}else{
@ Contact the site administrator and ask them to give
@ you "Clone" privileges in order to clone.
}
}else{
const char *zNm = db_get("short-project-name","clone");
@ <p>Clone the repository using this command:
@ <blockquote><pre>
@ fossil clone %s(g.zBaseURL) %h(zNm).fossil
@ </pre></blockquote>
}
style_footer();
}
|
Changes to src/comformat.c.
| ︙ | ︙ | |||
25 26 27 28 29 30 31 | # include <windows.h> #else # include <termios.h> # include <sys/ioctl.h> #endif #if INTERFACE | | > | 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | # include <windows.h> #else # include <termios.h> # include <sys/ioctl.h> #endif #if INTERFACE #define COMMENT_PRINT_NONE ((u32)0x00000000) /* No flags = non-legacy. */ #define COMMENT_PRINT_LEGACY ((u32)0x00000001) /* Use legacy algorithm. */ #define COMMENT_PRINT_TRIM_CRLF ((u32)0x00000002) /* Trim leading CR/LF. */ #define COMMENT_PRINT_TRIM_SPACE ((u32)0x00000004) /* Trim leading/trailing. */ #define COMMENT_PRINT_WORD_BREAK ((u32)0x00000008) /* Break lines on words. */ #define COMMENT_PRINT_ORIG_BREAK ((u32)0x00000010) /* Break before original. */ #define COMMENT_PRINT_DEFAULT (COMMENT_PRINT_LEGACY) /* Defaults. */ #define COMMENT_PRINT_UNSET (-1) /* Not initialized. */ #endif /* ** This is the previous value used by most external callers when they ** needed to specify a default maximum line length to be used with the ** comment_print() function. */ |
| ︙ | ︙ | |||
512 513 514 515 516 517 518 519 520 521 522 523 524 525 |
comment_print_line(zOrigText, zLine, indent, zLine>zText ? indent : 0,
maxChars, trimCrLf, trimSpace, wordBreak, origBreak,
&lineCnt, &zLine);
if( !zLine || !zLine[0] ) break;
}
return lineCnt;
}
/*
**
** COMMAND: test-comment-format
**
** Usage: %fossil test-comment-format ?OPTIONS? PREFIX TEXT ?ORIGTEXT?
**
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 |
comment_print_line(zOrigText, zLine, indent, zLine>zText ? indent : 0,
maxChars, trimCrLf, trimSpace, wordBreak, origBreak,
&lineCnt, &zLine);
if( !zLine || !zLine[0] ) break;
}
return lineCnt;
}
/*
** Return the "COMMENT_PRINT_*" flags specified by the following sources,
** evaluated in the following cascading order:
**
** 1. The global --comfmtflags (alias --comment-format) command-line option.
** 2. The local (per-repository) "comment-format" setting.
** 3. The global (all-repositories) "comment-format" setting.
** 4. The default value COMMENT_PRINT_DEFAULT.
*/
int get_comment_format(){
int comFmtFlags;
/* The global command-line option is present, or the value has been cached. */
if( g.comFmtFlags!=COMMENT_PRINT_UNSET ){
comFmtFlags = g.comFmtFlags;
return comFmtFlags;
}
/* Load the local (per-repository) or global (all-repositories) value, and use
** g.comFmtFlags as a cache. */
comFmtFlags = db_get_int("comment-format", COMMENT_PRINT_UNSET);
if( comFmtFlags!=COMMENT_PRINT_UNSET ){
g.comFmtFlags = comFmtFlags;
return comFmtFlags;
}
/* Fallback to the default value. */
comFmtFlags = COMMENT_PRINT_DEFAULT;
return comFmtFlags;
}
/*
**
** COMMAND: test-comment-format
**
** Usage: %fossil test-comment-format ?OPTIONS? PREFIX TEXT ?ORIGTEXT?
**
|
| ︙ | ︙ |
Changes to src/configure.c.
| ︙ | ︙ | |||
134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
{ "encoding-glob", CONFIGSET_PROJ },
{ "empty-dirs", CONFIGSET_PROJ },
{ "allow-symlinks", CONFIGSET_PROJ },
{ "dotfiles", CONFIGSET_PROJ },
{ "parent-project-code", CONFIGSET_PROJ },
{ "parent-project-name", CONFIGSET_PROJ },
{ "hash-policy", CONFIGSET_PROJ },
#ifdef FOSSIL_ENABLE_LEGACY_MV_RM
{ "mv-rm-files", CONFIGSET_PROJ },
#endif
{ "ticket-table", CONFIGSET_TKT },
{ "ticket-common", CONFIGSET_TKT },
| > | 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
{ "encoding-glob", CONFIGSET_PROJ },
{ "empty-dirs", CONFIGSET_PROJ },
{ "allow-symlinks", CONFIGSET_PROJ },
{ "dotfiles", CONFIGSET_PROJ },
{ "parent-project-code", CONFIGSET_PROJ },
{ "parent-project-name", CONFIGSET_PROJ },
{ "hash-policy", CONFIGSET_PROJ },
{ "comment-format", CONFIGSET_PROJ },
#ifdef FOSSIL_ENABLE_LEGACY_MV_RM
{ "mv-rm-files", CONFIGSET_PROJ },
#endif
{ "ticket-table", CONFIGSET_TKT },
{ "ticket-common", CONFIGSET_TKT },
|
| ︙ | ︙ | |||
417 418 419 420 421 422 423 |
}
blob_append_sql(&sql, "REPLACE INTO ");
}else{
blob_append_sql(&sql, "INSERT OR IGNORE INTO ");
}
blob_append_sql(&sql, "\"%w\"(\"%w\",mtime",
&zName[1], aType[ii].zPrimKey);
| | | 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 |
}
blob_append_sql(&sql, "REPLACE INTO ");
}else{
blob_append_sql(&sql, "INSERT OR IGNORE INTO ");
}
blob_append_sql(&sql, "\"%w\"(\"%w\",mtime",
&zName[1], aType[ii].zPrimKey);
if( fossil_stricmp(zName,"/subscriber")==0 ) alert_schema(0);
for(jj=2; jj<nToken; jj+=2){
blob_append_sql(&sql, ",\"%w\"", azToken[jj]);
}
blob_append_sql(&sql,") VALUES(%s,%s",
azToken[1] /*safe-for-%s*/, azToken[0]/*safe-for-%s*/);
for(jj=2; jj<nToken; jj+=2){
blob_append_sql(&sql, ",%s", azToken[jj+1] /*safe-for-%s*/);
|
| ︙ | ︙ |
Changes to src/content.c.
| ︙ | ︙ | |||
596 597 598 599 600 601 602 |
db_exec(&s1);
rid = db_last_insert_rowid();
if( !pBlob ){
db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
}
}
if( g.markPrivate || isPrivate ){
| | | 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 |
db_exec(&s1);
rid = db_last_insert_rowid();
if( !pBlob ){
db_multi_exec("INSERT OR IGNORE INTO phantom VALUES(%d)", rid);
}
}
if( g.markPrivate || isPrivate ){
db_multi_exec("INSERT OR IGNORE INTO private VALUES(%d)", rid);
markAsUnclustered = 0;
}
if( nBlob==0 ) blob_reset(&cmpr);
/* If the srcId is specified, then the data we just added is
** really a delta. Record this fact in the delta table.
*/
|
| ︙ | ︙ | |||
1216 1217 1218 1219 1220 1221 1222 | ** WARNING: This command destroys data and can cause you to lose work. ** Make sure you have a backup copy before using this command! ** ** WARNING: You must run "fossil rebuild" after this command to rebuild ** the metadata. ** ** Note that the arguments are the integer raw RID values from the BLOB table, | | | 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 |
** WARNING: This command destroys data and can cause you to lose work.
** Make sure you have a backup copy before using this command!
**
** WARNING: You must run "fossil rebuild" after this command to rebuild
** the metadata.
**
** Note that the arguments are the integer raw RID values from the BLOB table,
** not artifact hashes or labels.
*/
void test_content_erase(void){
int i;
Blob x;
char c;
Stmt q;
prompt_user("This command erases information from the repository and\n"
|
| ︙ | ︙ |
Changes to src/db.c.
| ︙ | ︙ | |||
84 85 86 87 88 89 90 |
else
#endif /* FOSSIL_ENABLE_JSON */
if( g.xferPanic && g.cgiOutput==1 ){
cgi_reset_content();
@ error Database\serror:\s%F(z)
cgi_reply();
}
| | | 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
else
#endif /* FOSSIL_ENABLE_JSON */
if( g.xferPanic && g.cgiOutput==1 ){
cgi_reset_content();
@ error Database\serror:\s%F(z)
cgi_reply();
}
fossil_fatal("Database error: %s", z);
}
/*
** All static variable that a used by only this file are gathered into
** the following structure.
*/
static struct DbLocalData {
|
| ︙ | ︙ | |||
359 360 361 362 363 364 365 366 367 368 369 370 371 372 |
va_list ap;
va_start(ap, zFormat);
rc = db_vprepare(pStmt, DB_PREPARE_PERSISTENT, zFormat, ap);
va_end(ap);
}
return rc;
}
/* Prepare a statement using text placed inside a Blob
** using blob_append_sql().
*/
int db_prepare_blob(Stmt *pStmt, Blob *pSql){
int rc;
char *zSql;
| > > > > > > | 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 |
va_list ap;
va_start(ap, zFormat);
rc = db_vprepare(pStmt, DB_PREPARE_PERSISTENT, zFormat, ap);
va_end(ap);
}
return rc;
}
/* Return TRUE if static Stmt object pStmt has been initialized.
*/
int db_static_stmt_is_init(Stmt *pStmt){
return blob_size(&pStmt->sql)>0;
}
/* Prepare a statement using text placed inside a Blob
** using blob_append_sql().
*/
int db_prepare_blob(Stmt *pStmt, Blob *pSql){
int rc;
char *zSql;
|
| ︙ | ︙ | |||
1471 1472 1473 1474 1475 1476 1477 |
/*
** If zDbName is a valid local database file, open it and return
** true. If it is not a valid local database file, return 0.
*/
static int isValidLocalDb(const char *zDbName){
i64 lsize;
| < | > > > > > > > > | > > > | > | | | > > > > > | 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 |
/*
** If zDbName is a valid local database file, open it and return
** true. If it is not a valid local database file, return 0.
*/
static int isValidLocalDb(const char *zDbName){
i64 lsize;
if( file_access(zDbName, F_OK) ) return 0;
lsize = file_size(zDbName, ExtFILE);
if( lsize%1024!=0 || lsize<4096 ) return 0;
db_open_or_attach(zDbName, "localdb");
/* Check to see if the checkout database has the lastest schema changes.
** The most recent schema change (2019-01-19) is the addition of the
** vmerge.mhash and vfile.mhash fields. If the schema has the vmerge.mhash
** column, assume everything else is up-to-date.
*/
if( db_table_has_column("localdb","vmerge","mhash") ){
return 1; /* This is a checkout database with the latest schema */
}
/* If there is no vfile table, then assume we have picked up something
** that is not even close to being a valid checkout database */
if( !db_table_exists("localdb","vfile") ){
return 0; /* Not a DB */
}
/* If the "isexe" column is missing from the vfile table, then
** add it now. This code added on 2010-03-06. After all users have
** upgraded, this code can be safely deleted.
*/
if( !db_table_has_column("localdb","vfile","isexe") ){
db_multi_exec("ALTER TABLE vfile ADD COLUMN isexe BOOLEAN DEFAULT 0");
}
/* If "islink"/"isLink" columns are missing from tables, then
** add them now. This code added on 2011-01-17 and 2011-08-27.
** After all users have upgraded, this code can be safely deleted.
*/
if( !db_table_has_column("localdb","vfile","isLink") ){
db_multi_exec("ALTER TABLE vfile ADD COLUMN islink BOOLEAN DEFAULT 0");
if( db_local_table_exists_but_lacks_column("stashfile", "isLink") ){
db_multi_exec("ALTER TABLE stashfile ADD COLUMN isLink BOOL DEFAULT 0");
}
if( db_local_table_exists_but_lacks_column("undo", "isLink") ){
db_multi_exec("ALTER TABLE undo ADD COLUMN isLink BOOLEAN DEFAULT 0");
}
if( db_local_table_exists_but_lacks_column("undo_vfile", "islink") ){
db_multi_exec("ALTER TABLE undo_vfile ADD COLUMN islink BOOL DEFAULT 0");
}
}
/* The design of the checkout database changed on 2019-01-19, adding the mhash
** column to vfile and vmerge and changing the UNIQUE index on vmerge into
** a PRIMARY KEY that includes the new mhash column. However, we must have
** the repository database at hand in order to do the migration, so that
** step is deferred. */
return 1;
}
/*
** Locate the root directory of the local repository tree. The root
** directory is found by searching for a file named "_FOSSIL_" or ".fslckout"
** that contains a valid repository database.
|
| ︙ | ︙ | |||
1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 |
db_set_int("hash-policy", g.eHashPolicy, 0);
}
/* Make a change to the CHECK constraint on the BLOB table for
** version 2.0 and later.
*/
rebuild_schema_update_2_0(); /* Do the Fossil-2.0 schema updates */
}
/*
** Return true if there have been any changes to the repository
** database since it was opened.
**
** Changes to "config" and "localdb" and "temp" do not count.
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 |
db_set_int("hash-policy", g.eHashPolicy, 0);
}
/* Make a change to the CHECK constraint on the BLOB table for
** version 2.0 and later.
*/
rebuild_schema_update_2_0(); /* Do the Fossil-2.0 schema updates */
/* Additional checks that occur when opening the checkout database */
if( g.localOpen ){
/* If the repository database that was just opened has been
** eplaced by a clone of the same project, with different RID
** values, then renumber the RID values stored in various tables
** of the checkout database, so that the repository and checkout
** databases align.
*/
if( !db_fingerprint_ok() ){
if( find_option("no-rid-adjust",0,0)!=0 ){
/* The --no-rid-adjust command-line option bypasses the RID value
** updates. Intended for use during debugging, especially to be
** able to run "fossil sql" after a database swap. */
fossil_print(
"WARNING: repository change detected, but no adjust made.\n"
);
}else if( find_option("rid-renumber-dryrun",0,0)!=0 ){
/* the --rid-renumber-dryrun option shows how RID values would be
** renumbered, but does not actually perform the renumbering.
** This is a debugging-only option. */
vfile_rid_renumbering_event(1);
exit(0);
}else{
char *z;
stash_rid_renumbering_event();
vfile_rid_renumbering_event(0);
undo_reset();
bisect_reset();
z = db_fingerprint(0);
db_lset("fingerprint", z);
fossil_free(z);
fossil_print(
"WARNING: The repository database has been replaced by a clone.\n"
"Bisect history and undo have been lost.\n"
);
}
}
/* Make sure the checkout database schema migration of 2019-01-20
** has occurred.
**
** The 2019-01-19 migration is the addition of the vmerge.mhash and
** vfile.mhash columns and making the vmerge.mhash column part of the
** PRIMARY KEY for vmerge.
*/
if( !db_table_has_column("localdb", "vfile", "mhash") ){
db_multi_exec("ALTER TABLE vfile ADD COLUMN mhash;");
db_multi_exec(
"UPDATE vfile"
" SET mhash=(SELECT uuid FROM blob WHERE blob.rid=vfile.mrid)"
" WHERE mrid!=rid;"
);
if( !db_table_has_column("localdb", "vmerge", "mhash") ){
db_multi_exec("ALTER TABLE vmerge RENAME TO old_vmerge;");
db_multi_exec(zLocalSchemaVmerge /*works-like:""*/);
db_multi_exec(
"INSERT OR IGNORE INTO vmerge(id,merge,mhash)"
" SELECT id, merge, blob.uuid FROM old_vmerge, blob"
" WHERE old_vmerge.merge=blob.rid;"
"DROP TABLE old_vmerge;"
);
}
}
}
}
/*
** Return true if there have been any changes to the repository
** database since it was opened.
**
** Changes to "config" and "localdb" and "temp" do not count.
|
| ︙ | ︙ | |||
2845 2846 2847 2848 2849 2850 2851 | } #if defined(_WIN32) || defined(__CYGWIN__) # define LOCALDB_NAME "./_FOSSIL_" #else # define LOCALDB_NAME "./.fslckout" #endif | | | 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 |
}
#if defined(_WIN32) || defined(__CYGWIN__)
# define LOCALDB_NAME "./_FOSSIL_"
#else
# define LOCALDB_NAME "./.fslckout"
#endif
db_init_database(LOCALDB_NAME, zLocalSchema, zLocalSchemaVmerge,
#ifdef FOSSIL_LOCAL_WAL
"COMMIT; PRAGMA journal_mode=WAL; BEGIN;",
#endif
(char*)0);
db_delete_on_failure(LOCALDB_NAME);
db_open_local(0);
if( allowSymlinks>=0 ){
|
| ︙ | ︙ | |||
2870 2871 2872 2873 2874 2875 2876 |
** point, this will probably be the setting value from the
** repository or global configuration databases. */
g.allowSymlinks = db_get_boolean("allow-symlinks",
db_allow_symlinks_by_default());
}
db_lset("repository", g.argv[2]);
db_record_repository_filename(g.argv[2]);
| | | 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 |
** point, this will probably be the setting value from the
** repository or global configuration databases. */
g.allowSymlinks = db_get_boolean("allow-symlinks",
db_allow_symlinks_by_default());
}
db_lset("repository", g.argv[2]);
db_record_repository_filename(g.argv[2]);
db_set_checkout(0);
azNewArgv[0] = g.argv[0];
g.argv = azNewArgv;
if( !emptyFlag ){
g.argc = 3;
if( g.zOpenRevision ){
azNewArgv[g.argc-1] = g.zOpenRevision;
}else{
|
| ︙ | ︙ | |||
3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 | ** Example: *.a,*.lib,*.o */ /* ** SETTING: clearsign boolean default=off ** When enabled, fossil will attempt to sign all commits ** with gpg. When disabled, commits will be unsigned. */ /* ** SETTING: crlf-glob width=40 versionable block-text ** The value is a comma or newline-separated list of GLOB patterns for ** text files in which it is ok to have CR, CR+LF or mixed ** line endings. Set to "*" to disable CR+LF checking. ** The crnl-glob setting is a compatibility alias. */ | > > > > > > > > > > > > > > > > > > > > > > > | 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 | ** Example: *.a,*.lib,*.o */ /* ** SETTING: clearsign boolean default=off ** When enabled, fossil will attempt to sign all commits ** with gpg. When disabled, commits will be unsigned. */ /* ** SETTING: comment-format width=16 default=1 ** Set the default options for printing timeline comments to the console. ** ** The global --comfmtflags command-line option (or alias --comment-format) ** overrides this setting. ** ** Possible values are: ** 1 Activate the legacy comment printing format (default). ** ** Or a bitwise combination of the following flags: ** 0 Activate the newer (non-legacy) comment printing format. ** 2 Trim leading and trailing CR and LF characters. ** 4 Trim leading and trailing white space characters. ** 8 Attempt to break lines on word boundaries. ** 16 Break lines before the original comment embedded in other text. ** ** Note: To preserve line breaks, activate the newer (non-legacy) comment ** printing format (i.e. set to "0", or a combination not including "1"). ** ** Note: The options for timeline comments displayed on the web UI can be ** configured through the /setup_timeline web page. */ /* ** SETTING: crlf-glob width=40 versionable block-text ** The value is a comma or newline-separated list of GLOB patterns for ** text files in which it is ok to have CR, CR+LF or mixed ** line endings. Set to "*" to disable CR+LF checking. ** The crnl-glob setting is a compatibility alias. */ |
| ︙ | ︙ | |||
3702 3703 3704 3705 3706 3707 3708 |
*/
void test_database_name_cmd(void){
db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
fossil_print("Repository database: %s\n", g.zRepositoryName);
fossil_print("Local database: %s\n", g.zLocalDbName);
fossil_print("Config database: %s\n", g.zConfigDbName);
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 3813 3814 3815 3816 3817 3818 3819 3820 3821 3822 3823 3824 3825 3826 3827 3828 3829 3830 3831 3832 3833 3834 3835 3836 3837 3838 3839 3840 3841 3842 3843 3844 3845 3846 3847 3848 3849 3850 3851 3852 3853 3854 3855 3856 3857 3858 3859 3860 3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 3875 3876 3877 3878 3879 3880 3881 3882 3883 3884 3885 3886 3887 3888 3889 3890 3891 3892 3893 3894 3895 3896 3897 3898 3899 3900 3901 3902 3903 3904 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 3931 3932 3933 3934 |
*/
void test_database_name_cmd(void){
db_find_and_open_repository(OPEN_ANY_SCHEMA, 0);
fossil_print("Repository database: %s\n", g.zRepositoryName);
fossil_print("Local database: %s\n", g.zLocalDbName);
fossil_print("Config database: %s\n", g.zConfigDbName);
}
/*
** Compute a "fingerprint" on the repository. A fingerprint is used
** to verify that that the repository has not been replaced by a clone
** of the same repository. More precisely, a fingerprint are used to
** verify that the mapping between SHA3 hashes and RID values is unchanged.
**
** The checkout database ("localdb") stores RID values. When associating
** a checkout database against a repository database, it is useful to verify
** the fingerprint so that we know tha the RID values in the checkout
** database still correspond to the correct entries in the BLOB table of
** the repository.
**
** The fingerprint is based on the RCVFROM table. When constructing a
** new fingerprint, use the most recent RCVFROM entry. (Set rcvid==0 to
** accomplish this.) When verifying an old fingerprint, use the same
** RCVFROM entry that generated the fingerprint in the first place.
**
** The fingerprint consists of the rcvid, a "/", and the MD5 checksum of
** the remaining fields of the RCVFROM table entry. MD5 is used for this
** because it is 4x faster than SHA3 and 5x faster than SHA1, and there
** are no security concerns - this is just a checksum, not a security
** token.
*/
char *db_fingerprint(int rcvid){
char *z = 0;
Blob sql = BLOB_INITIALIZER;
Stmt q;
blob_append_sql(&sql,
"SELECT rcvid, quote(uid), quote(mtime), quote(nonce), quote(ipaddr)"
" FROM rcvfrom"
);
if( rcvid<=0 ){
blob_append_sql(&sql, " ORDER BY rcvid DESC LIMIT 1");
}else{
blob_append_sql(&sql, " WHERE rcvid=%d", rcvid);
}
db_prepare_blob(&q, &sql);
blob_reset(&sql);
if( db_step(&q)==SQLITE_ROW ){
int i;
md5sum_init();
for(i=1; i<=4; i++){
md5sum_step_text(db_column_text(&q,i),-1);
}
z = mprintf("%d/%s",db_column_int(&q,0),md5sum_finish(0));
}
db_finalize(&q);
return z;
}
/*
** COMMAND: test-fingerprint
**
** Usage: %fossil test-fingerprint ?RCVID? ?--check?
**
** Display the repository fingerprint. Or if the --check option
** is provided and this command is run from a checkout, invoke the
** db_fingerprint_ok() method and print its result.
*/
void test_fingerprint(void){
int rcvid = 0;
if( find_option("check",0,0)!=0 ){
db_must_be_within_tree();
fossil_print("db_fingerprint_ok() => %d\n", db_fingerprint_ok());
return;
}
db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
if( g.argc==3 ){
rcvid = atoi(g.argv[2]);
}else if( g.argc!=2 ){
fossil_fatal("wrong number of arguments");
}
fossil_print("%z\n", db_fingerprint(rcvid));
}
/*
** Set the value of the "checkout" entry in the VVAR table.
**
** Also set "fingerprint" and "checkout-hash".
*/
void db_set_checkout(int rid){
char *z;
db_lset_int("checkout", rid);
z = db_text(0,"SELECT uuid FROM blob WHERE rid=%d",rid);
db_lset("checkout-hash", z);
fossil_free(z);
z = db_fingerprint(0);
db_lset("fingerprint", z);
fossil_free(z);
}
/*
** Verify that the fingerprint recorded in the "fingerprint" entry
** of the VVAR table matches the fingerprint on the currently
** connected repository. Return true if the fingerprint is ok, and
** return false if the fingerprint does not match.
*/
int db_fingerprint_ok(void){
char *zCkout; /* The fingerprint recorded in the checkout database */
char *zRepo; /* The fingerprint of the repository */
int rc; /* Result */
zCkout = db_text(0,"SELECT value FROM localdb.vvar WHERE name='fingerprint'");
if( zCkout==0 ){
/* This is an older checkout that does not record a fingerprint.
** We have to assume everything is ok */
return 2;
}
zRepo = db_fingerprint(atoi(zCkout));
rc = fossil_strcmp(zCkout,zRepo)==0;
fossil_free(zCkout);
fossil_free(zRepo);
return rc;
}
|
Changes to src/default_css.txt.
| ︙ | ︙ | |||
157 158 159 160 161 162 163 164 165 166 167 168 169 170 |
}
.tl-arrow.merge.r {
border-left: 3px solid #000;
}
.tl-line.merge {
width: 1px;
}
.tl-arrow.warp {
margin-left: 1px;
border-width: 3px 0;
border-left: 7px solid #600000;
}
.tl-line.warp {
background: #600000;
| > > > > > > > > > > > > > > > > > > > > > > | 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 |
}
.tl-arrow.merge.r {
border-left: 3px solid #000;
}
.tl-line.merge {
width: 1px;
}
.tl-arrow.cherrypick {
height: 1px;
border-width: 2px 0;
}
.tl-arrow.cherrypick.l {
border-right: 3px solid #000;
}
.tl-arrow.cherrypick.r {
border-left: 3px solid #000;
}
.tl-line.cherrypick.h {
width: 0px;
border-top: 1px dashed #000;
border-left: 0px dashed #000;
background: rgba(255,255,255,0);
}
.tl-line.cherrypick.v {
width: 0px;
border-top: 0px dashed #000;
border-left: 1px dashed #000;
background: rgba(255,255,255,0);
}
.tl-arrow.warp {
margin-left: 1px;
border-width: 3px 0;
border-left: 7px solid #600000;
}
.tl-line.warp {
background: #600000;
|
| ︙ | ︙ | |||
195 196 197 198 199 200 201 202 203 204 205 206 207 208 |
padding: 0 0 0 1em;
}
div.columns > ul li:first-child {
margin-top:0px;
}
.columns li {
break-inside: avoid;
}
.filetree {
margin: 1em 0;
line-height: 1.5;
}
.filetree > ul {
display: inline-block;
| > | 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 |
padding: 0 0 0 1em;
}
div.columns > ul li:first-child {
margin-top:0px;
}
.columns li {
break-inside: avoid;
page-break-inside: avoid;
}
.filetree {
margin: 1em 0;
line-height: 1.5;
}
.filetree > ul {
display: inline-block;
|
| ︙ | ︙ |
Added src/deltafunc.c.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 |
/*
** Copyright (c) 2019 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)
**
** This program is distributed in the hope that it will be useful,
** but without any warranty; without even the implied warranty of
** merchantability or fitness for a particular purpose.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This module implements SQL interfaces to the delta logic. The code
** here is adapted from the ext/misc/fossildelta.c extension in SQLite.
*/
#include "config.h"
#include "deltafunc.h"
/*
** SQL functions: delta_create(X,Y)
**
** Return a delta that will transform X into Y.
*/
static void deltaCreateFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *aOrig; int nOrig; /* old blob */
const char *aNew; int nNew; /* new blob */
char *aOut; int nOut; /* output delta */
assert( argc==2 );
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
if( sqlite3_value_type(argv[1])==SQLITE_NULL ) return;
nOrig = sqlite3_value_bytes(argv[0]);
aOrig = (const char*)sqlite3_value_blob(argv[0]);
nNew = sqlite3_value_bytes(argv[1]);
aNew = (const char*)sqlite3_value_blob(argv[1]);
aOut = sqlite3_malloc64(nNew+70);
if( aOut==0 ){
sqlite3_result_error_nomem(context);
}else{
nOut = delta_create(aOrig, nOrig, aNew, nNew, aOut);
if( nOut<0 ){
sqlite3_free(aOut);
sqlite3_result_error(context, "cannot create fossil delta", -1);
}else{
sqlite3_result_blob(context, aOut, nOut, sqlite3_free);
}
}
}
/*
** SQL functions: delta_apply(X,D)
**
** Return the result of applying delta D to input X.
*/
static void deltaApplyFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *aOrig; int nOrig; /* The X input */
const char *aDelta; int nDelta; /* The input delta (D) */
char *aOut; int nOut, nOut2; /* The output */
assert( argc==2 );
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
if( sqlite3_value_type(argv[1])==SQLITE_NULL ) return;
nOrig = sqlite3_value_bytes(argv[0]);
aOrig = (const char*)sqlite3_value_blob(argv[0]);
nDelta = sqlite3_value_bytes(argv[1]);
aDelta = (const char*)sqlite3_value_blob(argv[1]);
/* Figure out the size of the output */
nOut = delta_output_size(aDelta, nDelta);
if( nOut<0 ){
sqlite3_result_error(context, "corrupt fossil delta", -1);
return;
}
aOut = sqlite3_malloc64((sqlite3_int64)nOut+1);
if( aOut==0 ){
sqlite3_result_error_nomem(context);
}else{
nOut2 = delta_apply(aOrig, nOrig, aDelta, nDelta, aOut);
if( nOut2!=nOut ){
sqlite3_free(aOut);
sqlite3_result_error(context, "corrupt fossil delta", -1);
}else{
sqlite3_result_blob(context, aOut, nOut, sqlite3_free);
}
}
}
/*
** SQL functions: delta_output_size(D)
**
** Return the size of the output that results from applying delta D.
*/
static void deltaOutputSizeFunc(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *aDelta; int nDelta; /* The input delta (D) */
int nOut; /* Size of output */
assert( argc==1 );
if( sqlite3_value_type(argv[0])==SQLITE_NULL ) return;
nDelta = sqlite3_value_bytes(argv[0]);
aDelta = (const char*)sqlite3_value_blob(argv[0]);
/* Figure out the size of the output */
nOut = delta_output_size(aDelta, nDelta);
if( nOut<0 ){
sqlite3_result_error(context, "corrupt fossil delta", -1);
return;
}else{
sqlite3_result_int(context, nOut);
}
}
/*****************************************************************************
** Table-valued SQL function: delta_parse(DELTA)
**
** Schema:
**
** CREATE TABLE delta_parse(
** op TEXT,
** a1 INT,
** a2 ANY,
** delta HIDDEN BLOB
** );
**
** Given an input DELTA, this function parses the delta and returns
** rows for each entry in the delta. The op column has one of the
** values SIZE, COPY, INSERT, CHECKSUM, ERROR.
**
** Assuming no errors, the first row has op='SIZE'. a1 is the size of
** the output in bytes and a2 is NULL.
**
** After the initial SIZE row, there are zero or more 'COPY' and/or 'INSERT'
** rows. A COPY row means content is copied from the source into the
** output. Column a1 is the number of bytes to copy and a2 is the offset
** into source from which to begin copying. An INSERT row means to
** insert text into the output stream. Column a1 is the number of bytes
** to insert and column is a BLOB that contains the text to be inserted.
**
** The last row of a well-formed delta will have an op value of 'CHECKSUM'.
** The a1 column will be the value of the checksum and a2 will be NULL.
**
** If the input delta is not well-formed, then a row with an op value
** of 'ERROR' is returned. The a1 value of the ERROR row is the offset
** into the delta where the error was encountered and a2 is NULL.
*/
typedef struct deltaparsevtab_vtab deltaparsevtab_vtab;
typedef struct deltaparsevtab_cursor deltaparsevtab_cursor;
struct deltaparsevtab_vtab {
sqlite3_vtab base; /* Base class - must be first */
/* No additional information needed */
};
struct deltaparsevtab_cursor {
sqlite3_vtab_cursor base; /* Base class - must be first */
char *aDelta; /* The delta being parsed */
int nDelta; /* Number of bytes in the delta */
int iCursor; /* Current cursor location */
int eOp; /* Name of current operator */
unsigned int a1, a2; /* Arguments to current operator */
int iNext; /* Next cursor value */
};
/* Operator names:
*/
static const char *azOp[] = {
"SIZE", "COPY", "INSERT", "CHECKSUM", "ERROR", "EOF"
};
#define DELTAPARSE_OP_SIZE 0
#define DELTAPARSE_OP_COPY 1
#define DELTAPARSE_OP_INSERT 2
#define DELTAPARSE_OP_CHECKSUM 3
#define DELTAPARSE_OP_ERROR 4
#define DELTAPARSE_OP_EOF 5
/*
** Read bytes from *pz and convert them into a positive integer. When
** finished, leave *pz pointing to the first character past the end of
** the integer. The *pLen parameter holds the length of the string
** in *pz and is decremented once for each character in the integer.
*/
static unsigned int deltaGetInt(const char **pz, int *pLen){
static const signed char zValue[] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1,
-1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, 36,
-1, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, -1, -1, -1, 63, -1,
};
unsigned int v = 0;
int c;
unsigned char *z = (unsigned char*)*pz;
unsigned char *zStart = z;
while( (c = zValue[0x7f&*(z++)])>=0 ){
v = (v<<6) + c;
}
z--;
*pLen -= z - zStart;
*pz = (char*)z;
return v;
}
/*
** The deltaparsevtabConnect() method is invoked to create a new
** deltaparse virtual table.
**
** Think of this routine as the constructor for deltaparsevtab_vtab objects.
**
** All this routine needs to do is:
**
** (1) Allocate the deltaparsevtab_vtab object and initialize all fields.
**
** (2) Tell SQLite (via the sqlite3_declare_vtab() interface) what the
** result set of queries against the virtual table will look like.
*/
static int deltaparsevtabConnect(
sqlite3 *db,
void *pAux,
int argc, const char *const*argv,
sqlite3_vtab **ppVtab,
char **pzErr
){
deltaparsevtab_vtab *pNew;
int rc;
rc = sqlite3_declare_vtab(db,
"CREATE TABLE x(op,a1,a2,delta HIDDEN)"
);
/* For convenience, define symbolic names for the index to each column. */
#define DELTAPARSEVTAB_OP 0
#define DELTAPARSEVTAB_A1 1
#define DELTAPARSEVTAB_A2 2
#define DELTAPARSEVTAB_DELTA 3
if( rc==SQLITE_OK ){
pNew = sqlite3_malloc64( sizeof(*pNew) );
*ppVtab = (sqlite3_vtab*)pNew;
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, sizeof(*pNew));
}
return rc;
}
/*
** This method is the destructor for deltaparsevtab_vtab objects.
*/
static int deltaparsevtabDisconnect(sqlite3_vtab *pVtab){
deltaparsevtab_vtab *p = (deltaparsevtab_vtab*)pVtab;
sqlite3_free(p);
return SQLITE_OK;
}
/*
** Constructor for a new deltaparsevtab_cursor object.
*/
static int deltaparsevtabOpen(sqlite3_vtab *p, sqlite3_vtab_cursor **ppCursor){
deltaparsevtab_cursor *pCur;
pCur = sqlite3_malloc( sizeof(*pCur) );
if( pCur==0 ) return SQLITE_NOMEM;
memset(pCur, 0, sizeof(*pCur));
*ppCursor = &pCur->base;
return SQLITE_OK;
}
/*
** Destructor for a deltaparsevtab_cursor.
*/
static int deltaparsevtabClose(sqlite3_vtab_cursor *cur){
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
sqlite3_free(pCur);
return SQLITE_OK;
}
/*
** Advance a deltaparsevtab_cursor to its next row of output.
*/
static int deltaparsevtabNext(sqlite3_vtab_cursor *cur){
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
const char *z;
int i = 0;
pCur->iCursor = pCur->iNext;
z = pCur->aDelta + pCur->iCursor;
pCur->a1 = deltaGetInt(&z, &i);
switch( z[0] ){
case '@': {
z++;
pCur->a2 = deltaGetInt(&z, &i);
pCur->eOp = DELTAPARSE_OP_COPY;
pCur->iNext = (int)(&z[1] - pCur->aDelta);
break;
}
case ':': {
z++;
pCur->a2 = (unsigned int)(z - pCur->aDelta);
pCur->eOp = DELTAPARSE_OP_INSERT;
pCur->iNext = (int)(&z[pCur->a1] - pCur->aDelta);
break;
}
case ';': {
pCur->eOp = DELTAPARSE_OP_CHECKSUM;
pCur->iNext = pCur->nDelta;
break;
}
default: {
if( pCur->iNext==pCur->nDelta ){
pCur->eOp = DELTAPARSE_OP_EOF;
}else{
pCur->eOp = DELTAPARSE_OP_ERROR;
pCur->iNext = pCur->nDelta;
}
break;
}
}
return SQLITE_OK;
}
/*
** Return values of columns for the row at which the deltaparsevtab_cursor
** is currently pointing.
*/
static int deltaparsevtabColumn(
sqlite3_vtab_cursor *cur, /* The cursor */
sqlite3_context *ctx, /* First argument to sqlite3_result_...() */
int i /* Which column to return */
){
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
switch( i ){
case DELTAPARSEVTAB_OP: {
sqlite3_result_text(ctx, azOp[pCur->eOp], -1, SQLITE_STATIC);
break;
}
case DELTAPARSEVTAB_A1: {
sqlite3_result_int(ctx, pCur->a1);
break;
}
case DELTAPARSEVTAB_A2: {
if( pCur->eOp==DELTAPARSE_OP_COPY ){
sqlite3_result_int(ctx, pCur->a2);
}else if( pCur->eOp==DELTAPARSE_OP_INSERT ){
sqlite3_result_blob(ctx, pCur->aDelta+pCur->a2, pCur->a1,
SQLITE_TRANSIENT);
}
break;
}
case DELTAPARSEVTAB_DELTA: {
sqlite3_result_blob(ctx, pCur->aDelta, pCur->nDelta, SQLITE_TRANSIENT);
break;
}
}
return SQLITE_OK;
}
/*
** Return the rowid for the current row. In this implementation, the
** rowid is the same as the output value.
*/
static int deltaparsevtabRowid(sqlite3_vtab_cursor *cur, sqlite_int64 *pRowid){
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
*pRowid = pCur->iCursor;
return SQLITE_OK;
}
/*
** Return TRUE if the cursor has been moved off of the last
** row of output.
*/
static int deltaparsevtabEof(sqlite3_vtab_cursor *cur){
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor*)cur;
return pCur->eOp==DELTAPARSE_OP_EOF;
}
/*
** This method is called to "rewind" the deltaparsevtab_cursor object back
** to the first row of output. This method is always called at least
** once prior to any call to deltaparsevtabColumn() or deltaparsevtabRowid() or
** deltaparsevtabEof().
*/
static int deltaparsevtabFilter(
sqlite3_vtab_cursor *pVtabCursor,
int idxNum, const char *idxStr,
int argc, sqlite3_value **argv
){
deltaparsevtab_cursor *pCur = (deltaparsevtab_cursor *)pVtabCursor;
const char *a;
int i = 0;
pCur->eOp = DELTAPARSE_OP_ERROR;
if( idxNum!=1 ){
return SQLITE_OK;
}
pCur->nDelta = sqlite3_value_bytes(argv[0]);
a = (const char*)sqlite3_value_blob(argv[0]);
if( pCur->nDelta==0 || a==0 ){
return SQLITE_OK;
}
pCur->aDelta = sqlite3_malloc64( pCur->nDelta+1 );
if( pCur->aDelta==0 ){
pCur->nDelta = 0;
return SQLITE_NOMEM;
}
memcpy(pCur->aDelta, a, pCur->nDelta);
pCur->aDelta[pCur->nDelta] = 0;
a = pCur->aDelta;
pCur->eOp = DELTAPARSE_OP_SIZE;
pCur->a1 = deltaGetInt(&a, &i);
if( a[0]!='\n' ){
pCur->eOp = DELTAPARSE_OP_ERROR;
pCur->a1 = pCur->a2 = 0;
pCur->iNext = pCur->nDelta;
return SQLITE_OK;
}
a++;
pCur->iNext = (unsigned int)(a - pCur->aDelta);
return SQLITE_OK;
}
/*
** SQLite will invoke this method one or more times while planning a query
** that uses the virtual table. This routine needs to create
** a query plan for each invocation and compute an estimated cost for that
** plan.
*/
static int deltaparsevtabBestIndex(
sqlite3_vtab *tab,
sqlite3_index_info *pIdxInfo
){
int i;
for(i=0; i<pIdxInfo->nConstraint; i++){
if( pIdxInfo->aConstraint[i].iColumn != DELTAPARSEVTAB_DELTA ) continue;
if( pIdxInfo->aConstraint[i].usable==0 ) continue;
if( pIdxInfo->aConstraint[i].op!=SQLITE_INDEX_CONSTRAINT_EQ ) continue;
pIdxInfo->aConstraintUsage[i].argvIndex = 1;
pIdxInfo->aConstraintUsage[i].omit = 1;
pIdxInfo->estimatedCost = (double)1;
pIdxInfo->estimatedRows = 10;
pIdxInfo->idxNum = 1;
return SQLITE_OK;
}
pIdxInfo->idxNum = 0;
pIdxInfo->estimatedCost = (double)0x7fffffff;
pIdxInfo->estimatedRows = 0x7fffffff;
return SQLITE_CONSTRAINT;
}
/*
** This following structure defines all the methods for the
** virtual table.
*/
static sqlite3_module deltaparsevtabModule = {
/* iVersion */ 0,
/* xCreate */ 0,
/* xConnect */ deltaparsevtabConnect,
/* xBestIndex */ deltaparsevtabBestIndex,
/* xDisconnect */ deltaparsevtabDisconnect,
/* xDestroy */ 0,
/* xOpen */ deltaparsevtabOpen,
/* xClose */ deltaparsevtabClose,
/* xFilter */ deltaparsevtabFilter,
/* xNext */ deltaparsevtabNext,
/* xEof */ deltaparsevtabEof,
/* xColumn */ deltaparsevtabColumn,
/* xRowid */ deltaparsevtabRowid,
/* xUpdate */ 0,
/* xBegin */ 0,
/* xSync */ 0,
/* xCommit */ 0,
/* xRollback */ 0,
/* xFindMethod */ 0,
/* xRename */ 0,
/* xSavepoint */ 0,
/* xRelease */ 0,
/* xRollbackTo */ 0,
/* xShadowName */ 0
};
/*
** Invoke this routine to register the various delta functions.
*/
int deltafunc_init(sqlite3 *db){
int rc = SQLITE_OK;
rc = sqlite3_create_function(db, "delta_create", 2, SQLITE_UTF8, 0,
deltaCreateFunc, 0, 0);
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "delta_apply", 2, SQLITE_UTF8, 0,
deltaApplyFunc, 0, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_function(db, "delta_output_size", 1, SQLITE_UTF8, 0,
deltaOutputSizeFunc, 0, 0);
}
if( rc==SQLITE_OK ){
rc = sqlite3_create_module(db, "delta_parse", &deltaparsevtabModule, 0);
}
return rc;
}
|
Changes to src/descendants.c.
| ︙ | ︙ | |||
431 432 433 434 435 436 437 |
zLastBr = fossil_strdup(zBr);
if( multipleFlag ) n = 0;
}
n++;
sqlite3_snprintf(sizeof(zLineNo), zLineNo, "(%d)", n);
fossil_print("%6s ", zLineNo);
z = mprintf("%s [%S] %s", zDate, zId, zCom);
| | > > > > > > > > > > > > | > > > > > | | | > > > | 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 |
zLastBr = fossil_strdup(zBr);
if( multipleFlag ) n = 0;
}
n++;
sqlite3_snprintf(sizeof(zLineNo), zLineNo, "(%d)", n);
fossil_print("%6s ", zLineNo);
z = mprintf("%s [%S] %s", zDate, zId, zCom);
comment_print(z, zCom, 7, width, get_comment_format());
fossil_free(z);
}
fossil_free(zLastBr);
db_finalize(&q);
}
/*
** WEBPAGE: leaves
**
** Show leaf check-ins in a timeline. By default only open leaves
** are listed.
**
** A "leaf" is a check-in with no children in the same branch. A
** "closed leaf" is a leaf that has a "closed" tag. An "open leaf"
** is a leaf without a "closed" tag.
**
** Query parameters:
**
** all Show all leaves
** closed Show only closed leaves
** ng No graph
** nohidden Hide check-ins with "hidden" tag
** onlyhidden Show only check-ins with "hidden" tag
** brbg Background color by branch name
** ubg Background color by user name
*/
void leaves_page(void){
Blob sql;
Stmt q;
int showAll = P("all")!=0;
int showClosed = P("closed")!=0;
int fNg = PB("ng")!=0; /* Flag for the "ng" query parameter */
int fNoHidden = PB("nohidden")!=0; /* "nohidden" query parameter */
int fOnlyHidden = PB("onlyhidden")!=0; /* "onlyhidden" query parameter */
int fBrBg = PB("brbg")!=0; /* Flag for the "brbg" query parameter */
int fUBg = PB("ubg")!=0; /* Flag for the "ubg" query parameter */
HQuery url; /* URL to /leaves plus query parameters */
int tmFlags; /* Timeline display flags */
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
url_initialize(&url, "leaves");
if( fNg ) url_add_parameter(&url, "ng", "");
if( fNoHidden ) url_add_parameter(&url, "nohidden", "");
if( fOnlyHidden ) url_add_parameter(&url, "onlyhidden", "");
if( fBrBg ) url_add_parameter(&url, "brbg", "");
if( fUBg ) url_add_parameter(&url, "ubg", "");
if( !showAll ){
style_submenu_element("All", "%s", url_render(&url, "all", "", 0, 0));
}
if( !showClosed ){
style_submenu_element("Closed", "%s", url_render(&url, "closed", "", 0, 0));
}
if( showClosed || showAll ){
style_submenu_element("Open", "%s", url_render(&url, 0, 0, 0, 0));
}
url_reset(&url);
style_header("Leaves");
login_anonymous_available();
timeline_ss_submenu();
cookie_render();
#if 0
style_sidebox_begin("Nomenclature:", "33%");
@ <ol>
@ <li> A <div class="sideboxDescribed">leaf</div>
@ is a check-in with no descendants in the same branch.</li>
@ <li> An <div class="sideboxDescribed">open leaf</div>
@ is a leaf that does not have a "closed" tag
|
| ︙ | ︙ | |||
503 504 505 506 507 508 509 510 511 |
blob_append(&sql, timeline_query_for_www(), -1);
blob_append_sql(&sql, " AND blob.rid IN leaf");
if( showClosed ){
blob_append_sql(&sql," AND %z", leaf_is_closed_sql("blob.rid"));
}else if( !showAll ){
blob_append_sql(&sql," AND NOT %z", leaf_is_closed_sql("blob.rid"));
}
db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_sql_text(&sql));
blob_reset(&sql);
| > > > > > > > > > > > > > | | 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 |
blob_append(&sql, timeline_query_for_www(), -1);
blob_append_sql(&sql, " AND blob.rid IN leaf");
if( showClosed ){
blob_append_sql(&sql," AND %z", leaf_is_closed_sql("blob.rid"));
}else if( !showAll ){
blob_append_sql(&sql," AND NOT %z", leaf_is_closed_sql("blob.rid"));
}
if( fNoHidden || fOnlyHidden ){
const char* zUnaryOp = fNoHidden ? "NOT" : "";
blob_append_sql(&sql,
" AND %s EXISTS(SELECT 1 FROM tagxref"
" WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)\n",
zUnaryOp/*safe-for-%s*/, TAG_HIDDEN);
}
db_prepare(&q, "%s ORDER BY event.mtime DESC", blob_sql_text(&sql));
blob_reset(&sql);
/* Always specify TIMELINE_DISJOINT, or graph_finish() may fail because of too
** many descenders to (off-screen) parents. */
tmFlags = TIMELINE_LEAFONLY | TIMELINE_DISJOINT | TIMELINE_NOSCROLL;
if( fNg==0 ) tmFlags |= TIMELINE_GRAPH;
if( fBrBg ) tmFlags |= TIMELINE_BRCOLOR;
if( fUBg ) tmFlags |= TIMELINE_UCOLOR;
www_print_timeline(&q, tmFlags, 0, 0, 0, 0);
db_finalize(&q);
@ <br />
style_footer();
}
#if INTERFACE
/* Flag parameters to compute_uses_file() */
|
| ︙ | ︙ |
Changes to src/diff.c.
| ︙ | ︙ | |||
438 439 440 441 442 443 444 |
if( html ) blob_appendf(pOut, "<span class=\"diffln\">");
/*
* If the patch changes an empty file or results in an empty file,
* the block header must use 0,0 as position indicator and not 1,0.
* Otherwise, patch would be confused and may reject the diff.
*/
blob_appendf(pOut,"@@ -%d,%d +%d,%d @@",
| | | | 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 |
if( html ) blob_appendf(pOut, "<span class=\"diffln\">");
/*
* If the patch changes an empty file or results in an empty file,
* the block header must use 0,0 as position indicator and not 1,0.
* Otherwise, patch would be confused and may reject the diff.
*/
blob_appendf(pOut,"@@ -%d,%d +%d,%d @@",
na ? a+skip+1 : a+skip, na,
nb ? b+skip+1 : b+skip, nb);
if( html ) blob_appendf(pOut, "</span>");
blob_append(pOut, "\n", 1);
}
/* Show the initial common area */
a += skip;
b += skip;
|
| ︙ | ︙ |
Changes to src/diffcmd.c.
| ︙ | ︙ | |||
211 212 213 214 215 216 217 |
}
blob_reset(&out);
}
/* Release memory resources */
blob_reset(&file2);
}else{
| < | 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
}
blob_reset(&out);
}
/* Release memory resources */
blob_reset(&file2);
}else{
Blob nameFile1; /* Name of temporary file to old pFile1 content */
Blob cmd; /* Text of command to run */
if( !fIncludeBinary ){
Blob file2;
if( isBin1 ){
fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
|
| ︙ | ︙ | |||
244 245 246 247 248 249 250 |
return;
}
blob_reset(&file2);
}
/* Construct a temporary file to hold pFile1 based on the name of
** zFile2 */
| | < < < < | 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 |
return;
}
blob_reset(&file2);
}
/* Construct a temporary file to hold pFile1 based on the name of
** zFile2 */
file_tempname(&nameFile1, zFile2, "orig");
blob_write_to_file(pFile1, blob_str(&nameFile1));
/* Construct the external diff command */
blob_zero(&cmd);
blob_append(&cmd, zDiffCmd, -1);
if( fSwapDiff ){
blob_append_escaped_arg(&cmd, zFile2);
|
| ︙ | ︙ | |||
315 316 317 318 319 320 321 |
/* Release memory resources */
blob_reset(&out);
}else{
Blob cmd;
Blob temp1;
Blob temp2;
| < < < < < < < < | | < < | 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 |
/* Release memory resources */
blob_reset(&out);
}else{
Blob cmd;
Blob temp1;
Blob temp2;
if( !fIncludeBinary ){
if( isBin1 || isBin2 ){
fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
return;
}
if( zBinGlob ){
Glob *pBinary = glob_create(zBinGlob);
if( glob_match(pBinary, zName) ){
fossil_print("%s",DIFF_CANNOT_COMPUTE_BINARY);
glob_free(pBinary);
return;
}
glob_free(pBinary);
}
}
/* Construct a temporary file names */
file_tempname(&temp1, zName, "before");
file_tempname(&temp2, zName, "after");
blob_write_to_file(pFile1, blob_str(&temp1));
blob_write_to_file(pFile2, blob_str(&temp2));
/* Construct the external diff command */
blob_zero(&cmd);
blob_append(&cmd, zDiffCmd, -1);
blob_append_escaped_arg(&cmd, blob_str(&temp1));
blob_append_escaped_arg(&cmd, blob_str(&temp2));
/* Run the external diff command */
fossil_system(blob_str(&cmd));
/* Delete the temporary file and clean up memory used */
file_delete(blob_str(&temp1));
file_delete(blob_str(&temp2));
blob_reset(&temp1);
blob_reset(&temp2);
blob_reset(&cmd);
}
}
/*
|
| ︙ | ︙ |
Changes to src/dispatch.c.
| ︙ | ︙ | |||
263 264 265 266 267 268 269 |
int mask = CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER;
int useHtml = find_option("html","h",0)!=0;
if( find_option("www","w",0) ){
mask = CMDFLAG_WEBPAGE;
}
if( find_option("everything","e",0) ){
| | > | 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
int mask = CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER;
int useHtml = find_option("html","h",0)!=0;
if( find_option("www","w",0) ){
mask = CMDFLAG_WEBPAGE;
}
if( find_option("everything","e",0) ){
mask = CMDFLAG_1ST_TIER | CMDFLAG_2ND_TIER | CMDFLAG_WEBPAGE |
CMDFLAG_SETTING | CMDFLAG_TEST;
}
if( find_option("settings","s",0) ){
mask = CMDFLAG_SETTING;
}
if( find_option("test","t",0) ){
mask |= CMDFLAG_TEST;
}
|
| ︙ | ︙ | |||
498 499 500 501 502 503 504 505 506 507 508 509 510 511 | /* @-comment: # */ static const char zOptions[] = @ Command-line options common to all commands: @ @ --args FILENAME Read additional arguments and options from FILENAME @ --cgitrace Active CGI tracing @ --comfmtflags VALUE Set comment formatting flags to VALUE @ --errorlog FILENAME Log errors to FILENAME @ --help Show help on the command rather than running it @ --httptrace Trace outbound HTTP requests @ --localtime Display times using the local timezone @ --no-th-hook Do not run TH1 hooks @ --quiet Reduce the amount of output @ --sqlstats Show SQL usage statistics when done | > | 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 | /* @-comment: # */ static const char zOptions[] = @ Command-line options common to all commands: @ @ --args FILENAME Read additional arguments and options from FILENAME @ --cgitrace Active CGI tracing @ --comfmtflags VALUE Set comment formatting flags to VALUE @ --comment-format VALUE Alias for --comfmtflags @ --errorlog FILENAME Log errors to FILENAME @ --help Show help on the command rather than running it @ --httptrace Trace outbound HTTP requests @ --localtime Display times using the local timezone @ --no-th-hook Do not run TH1 hooks @ --quiet Reduce the amount of output @ --sqlstats Show SQL usage statistics when done |
| ︙ | ︙ |
Changes to src/doc.c.
| ︙ | ︙ | |||
508 509 510 511 512 513 514 | ** ** href="$ROOT/ ** action="$ROOT/ ** ** Convert $ROOT to the root URI of the repository. Allow ' in place of " ** and any case for href or action. */ | | | 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 |
**
** href="$ROOT/
** action="$ROOT/
**
** Convert $ROOT to the root URI of the repository. Allow ' in place of "
** and any case for href or action.
*/
void convert_href_and_output(Blob *pIn){
int i, base;
int n = blob_size(pIn);
char *z = blob_buffer(pIn);
for(base=0, i=7; i<n; i++){
if( z[i]=='$'
&& strncmp(&z[i],"$ROOT/", 6)==0
&& (z[i-1]=='\'' || z[i-1]=='"')
|
| ︙ | ︙ |
Changes to src/export.c.
| ︙ | ︙ | |||
26 27 28 29 30 31 32 |
*/
static struct {
const char *zTrunkName; /* Name of trunk branch */
} gexport;
#if INTERFACE
/*
| | < | < < < < < | | | | > > > > > > > | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
*/
static struct {
const char *zTrunkName; /* Name of trunk branch */
} gexport;
#if INTERFACE
/*
** Each line in a git-fast-export "marK" file is an instance of
** this object.
*/
struct mark_t {
char *name; /* Name of the mark. Also starts with ":" */
int rid; /* Corresponding object in the BLOB table */
char uuid[65]; /* The GIT hash name for this object */
};
#endif
#if defined(_WIN32) || defined(WIN32)
# undef popen
# define popen _popen
# undef pclose
# define pclose _pclose
#endif
/*
** Output a "committer" record for the given user.
** NOTE: the given user name may be an email itself.
*/
static void print_person(const char *zUser){
static Stmt q;
|
| ︙ | ︙ | |||
295 296 297 298 299 300 301 |
return NULL;
}
}
return zMark;
}
/*
| > | < < | | | | > | | 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 |
return NULL;
}
}
return zMark;
}
/*
** Parse a single line of the mark file. Store the result in the mark object.
**
** "line" is a single line of input.
** This function returns -1 in the case that the line is blank, malformed, or
** the rid/uuid named in 'line' does not match what is in the repository
** database. Otherwise, 0 is returned.
**
** mark->name is dynamically allocated, and owned by the caller.
*/
int parse_mark(char *line, struct mark_t *mark){
char *cur_tok;
char type_;
cur_tok = strtok(line, " \t");
if( !cur_tok || strlen(cur_tok)<2 ){
return -1;
|
| ︙ | ︙ | |||
359 360 361 362 363 364 365 | /* insert a cross-ref into the 'xmark' table */ insert_commit_xref(mark->rid, mark->name, mark->uuid); return 0; } /* | < | | | | | | > | | | > | | | | 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 |
/* insert a cross-ref into the 'xmark' table */
insert_commit_xref(mark->rid, mark->name, mark->uuid);
return 0;
}
/*
** Import the marks specified in file 'f';
** If 'blobs' is non-null, insert all blob marks into it.
** If 'vers' is non-null, insert all commit marks into it.
** If 'unused_marks' is non-null, upon return of this function, all values
** x >= *unused_marks are free to use as marks, i.e. they do not clash with
** any marks appearing in the marks file.
**
** Each line in the file must be at most 100 characters in length. This
** seems like a reasonable maximum for a 40-character uuid, and 1-13
** character rid.
**
** The function returns -1 if any of the lines in file 'f' are malformed,
** or the rid/uuid information doesn't match what is in the repository
** database. Otherwise, 0 is returned.
*/
int import_marks(FILE* f, Bag *blobs, Bag *vers, unsigned int *unused_mark){
char line[101];
while(fgets(line, sizeof(line), f)){
struct mark_t mark;
if( strlen(line)==100 && line[99]!='\n' ){
/* line too long */
|
| ︙ | ︙ | |||
450 451 452 453 454 455 456 |
do{
export_mark(f, rid, 'c');
}while( (rid = bag_next(vers, rid))!=0 );
}
}
}
| | | | | 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 |
do{
export_mark(f, rid, 'c');
}while( (rid = bag_next(vers, rid))!=0 );
}
}
}
/* This is the original header command (and hence documentation) for
** the "fossil export" command:
**
** Usage: %fossil export --git ?OPTIONS? ?REPOSITORY?
**
** Write an export of all check-ins to standard output. The export is
** written in the git-fast-export file format assuming the --git option is
** provided. The git-fast-export format is currently the only VCS
** interchange format supported, though other formats may be added in
** the future.
|
| ︙ | ︙ | |||
481 482 483 484 485 486 487 488 489 490 491 492 493 494 |
** --export-marks FILE export rids of exported data to FILE
** --import-marks FILE read rids of data to ignore from FILE
** --rename-trunk NAME use NAME as name of exported trunk branch
** --repository|-R REPOSITORY export the given REPOSITORY
**
** See also: import
*/
void export_cmd(void){
Stmt q, q2, q3;
Bag blobs, vers;
unsigned int unused_mark = 1;
const char *markfile_in;
const char *markfile_out;
| > > > > > | 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 |
** --export-marks FILE export rids of exported data to FILE
** --import-marks FILE read rids of data to ignore from FILE
** --rename-trunk NAME use NAME as name of exported trunk branch
** --repository|-R REPOSITORY export the given REPOSITORY
**
** See also: import
*/
/*
** COMMAND: export*
**
** This command is deprecated. Use "fossil git export" instead.
*/
void export_cmd(void){
Stmt q, q2, q3;
Bag blobs, vers;
unsigned int unused_mark = 1;
const char *markfile_in;
const char *markfile_out;
|
| ︙ | ︙ | |||
505 506 507 508 509 510 511 |
db_find_and_open_repository(0, 2);
verify_all_options();
if( g.argc!=2 && g.argc!=3 ){ usage("--git ?REPOSITORY?"); }
db_multi_exec("CREATE TEMPORARY TABLE oldblob(rid INTEGER PRIMARY KEY)");
db_multi_exec("CREATE TEMPORARY TABLE oldcommit(rid INTEGER PRIMARY KEY)");
| | > | 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 |
db_find_and_open_repository(0, 2);
verify_all_options();
if( g.argc!=2 && g.argc!=3 ){ usage("--git ?REPOSITORY?"); }
db_multi_exec("CREATE TEMPORARY TABLE oldblob(rid INTEGER PRIMARY KEY)");
db_multi_exec("CREATE TEMPORARY TABLE oldcommit(rid INTEGER PRIMARY KEY)");
db_multi_exec("CREATE TEMP TABLE xmark(tname TEXT UNIQUE, trid INT,"
" tuuid TEXT)");
db_multi_exec("CREATE INDEX xmark_trid ON xmark(trid)");
if( markfile_in!=0 ){
Stmt qb,qc;
FILE *f;
int rid;
f = fossil_fopen(markfile_in, "r");
|
| ︙ | ︙ | |||
752 753 754 755 756 757 758 | ** tid INTEGER PRIMARY KEY, -- Check-in id ** tseq INT -- integer total order on check-ins. ** ); ** ** This table contains all check-ins of the repository in topological ** order. "Topological order" means that every parent check-in comes ** before all of its children. Topological order is *almost* the same | | | 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 | ** tid INTEGER PRIMARY KEY, -- Check-in id ** tseq INT -- integer total order on check-ins. ** ); ** ** This table contains all check-ins of the repository in topological ** order. "Topological order" means that every parent check-in comes ** before all of its children. Topological order is *almost* the same ** thing as "ORDER BY event.mtime". Differences only arise when there ** are timewarps. In as much as Git hates timewarps, we have to compute ** a correct topological order when doing an export. ** ** Since mtime is a usually already nearly in topological order, the ** algorithm is to start with mtime, then make adjustments as necessary ** for timewarps. This is not a great algorithm for the general case, ** but it is very fast for the overwhelmingly common case where there |
| ︙ | ︙ | |||
833 834 835 836 837 838 839 |
*/
void test_topological_sort(void){
int n;
db_find_and_open_repository(0, 0);
n = topological_sort_checkins(1);
fossil_print("%d reorderings required\n", n);
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 |
*/
void test_topological_sort(void){
int n;
db_find_and_open_repository(0, 0);
n = topological_sort_checkins(1);
fossil_print("%d reorderings required\n", n);
}
/***************************************************************************
** Implementation of the "fossil git" command follows. We hope that the
** new code that follows will largely replace the legacy "fossil export"
** and "fossil import" code above.
*/
/* Verbosity level. Higher means more output.
**
** 0 print nothing at all
** 1 Errors only
** 2 Progress information (This is the default)
** 3 Extra details
*/
#define VERB_ERROR 1
#define VERB_NORMAL 2
#define VERB_EXTRA 3
static int gitmirror_verbosity = VERB_NORMAL;
/*
** Output routine that depends on verbosity
*/
static void gitmirror_message(int iLevel, const char *zFormat, ...){
va_list ap;
if( iLevel>gitmirror_verbosity ) return;
va_start(ap, zFormat);
fossil_vprint(zFormat, ap);
va_end(ap);
}
/*
** Convert characters of z[] that are not allowed to be in branch or
** tag names into "_".
*/
static void gitmirror_sanitize_name(char *z){
static unsigned char aSafe[] = {
/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x */
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1x */
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 2x */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, /* 3x */
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, /* 5x */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, /* 7x */
};
unsigned char *zu = (unsigned char*)z;
int i;
for(i=0; zu[i]; i++){
if( zu[i]>0x7f || !aSafe[zu[i]] ){
zu[i] = '_';
}else if( zu[i]=='/' && (i==0 || zu[i+1]==0 || zu[i+1]=='/') ){
zu[i] = '_';
}else if( zu[i]=='.' && (zu[i+1]==0 || zu[i+1]=='.'
|| (i>0 && zu[i-1]=='.')) ){
zu[i] = '_';
}
}
}
/*
** Quote a filename as a C-style string using \\ and \" if necessary.
** If quoting is not necessary, just return a copy of the input string.
**
** The return value is a held in memory obtained from fossil_malloc()
** and must be freed by the caller.
*/
static char *gitmirror_quote_filename_if_needed(const char *zIn){
int i, j;
char c;
int nSpecial = 0;
char *zOut;
for(i=0; (c = zIn[i])!=0; i++){
if( c=='\\' || c=='"' || c=='\n' ){
nSpecial++;
}
}
if( nSpecial==0 ){
return fossil_strdup(zIn);
}
zOut = fossil_malloc( i+nSpecial+3 );
zOut[0] = '"';
for(i=0, j=1; (c = zIn[i])!=0; i++){
if( c=='\\' || c=='"' || c=='\n' ){
zOut[j++] = '\\';
if( c=='\n' ){
zOut[j++] = 'n';
}else{
zOut[j++] = c;
}
}else{
zOut[j++] = c;
}
}
zOut[j++] = '"';
zOut[j] = 0;
return zOut;
}
/*
** Find the Git-name corresponding to the Fossil-name zUuid.
**
** If the mark does not exist and if the bCreate flag is false, then
** return NULL. If the mark does not exist and the bCreate flag is true,
** then create the mark.
**
** The string returned is obtained from fossil_malloc() and should
** be freed by the caller.
*/
static char *gitmirror_find_mark(const char *zUuid, int isFile, int bCreate){
static Stmt sFind, sIns;
db_static_prepare(&sFind,
"SELECT coalesce(githash,printf(':%%d',id))"
" FROM mirror.mmark WHERE uuid=:uuid AND isfile=:isfile"
);
db_bind_text(&sFind, ":uuid", zUuid);
db_bind_int(&sFind, ":isfile", isFile!=0);
if( db_step(&sFind)==SQLITE_ROW ){
char *zMark = fossil_strdup(db_column_text(&sFind, 0));
db_reset(&sFind);
return zMark;
}
db_reset(&sFind);
if( !bCreate ){
return 0;
}
db_static_prepare(&sIns,
"INSERT INTO mirror.mmark(uuid,isfile) VALUES(:uuid,:isfile)"
);
db_bind_text(&sIns, ":uuid", zUuid);
db_bind_int(&sIns, ":isfile", isFile!=0);
db_step(&sIns);
db_reset(&sIns);
return mprintf(":%d", db_last_insert_rowid());
}
/* This is the SHA3-256 hash of an empty file */
static const char zEmptySha3[] =
"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a";
/*
** Export a single file named by zUuid.
**
** Return 0 on success and non-zero on any failure.
**
** If zUuid is a shunned file, then treat it as if it were any empty file.
** But files that are missing from the repository but have not been officially
** shunned cause an error return. Except, if bPhantomOk is true, then missing
** files are replaced by an empty file.
*/
static int gitmirror_send_file(FILE *xCmd, const char *zUuid, int bPhantomOk){
char *zMark;
int rid;
int rc;
Blob data;
rid = fast_uuid_to_rid(zUuid);
if( rid<0 ){
if( bPhantomOk || uuid_is_shunned(zUuid) ){
gitmirror_message(VERB_EXTRA, "missing file: %s\n", zUuid);
zUuid = zEmptySha3;
}else{
return 1;
}
}else{
rc = content_get(rid, &data);
if( rc==0 ){
if( bPhantomOk ){
blob_init(&data, 0, 0);
gitmirror_message(VERB_EXTRA, "missing file: %s\n", zUuid);
zUuid = zEmptySha3;
}else{
return 1;
}
}
}
zMark = gitmirror_find_mark(zUuid, 1, 1);
if( zMark[0]==':' ){
fprintf(xCmd, "blob\nmark %s\ndata %d\n", zMark, blob_size(&data));
fwrite(blob_buffer(&data), 1, blob_size(&data), xCmd);
fprintf(xCmd, "\n");
}
fossil_free(zMark);
blob_reset(&data);
return 0;
}
/*
** Transfer a check-in over to the mirror. "rid" is the BLOB.RID for
** the check-in to export.
**
** If any ancestor of the check-in has not yet been exported, then
** invoke this routine recursively to export the ancestor first.
** This can only happen on a timewarp, so deep nesting is unlikely.
**
** Before sending the check-in, first make sure all associated files
** have already been exported, and send "blob" records for any that
** have not been. Update the MIRROR.MMARK table so that it holds the
** marks for the exported files.
**
** Return zero on success and non-zero if the export should be stopped.
*/
static int gitmirror_send_checkin(
FILE *xCmd, /* Write fast-import text on this pipe */
int rid, /* BLOB.RID for the check-in to export */
const char *zUuid, /* BLOB.UUID for the check-in to export */
int *pnLimit, /* Stop when the counter reaches zero */
int fManifest /* MFESTFLG_* values */
){
Manifest *pMan; /* The check-in to be output */
int i; /* Loop counter */
int iParent; /* Which immediate ancestor is primary. -1 for none */
Stmt q; /* An SQL query */
char *zBranch; /* The branch of the check-in */
char *zMark; /* The Git-name of the check-in */
Blob sql; /* String of SQL for part of the query */
Blob comment; /* The comment text for the check-in */
int nErr = 0; /* Number of errors */
int bPhantomOk; /* True if phantom files should be ignored */
pMan = manifest_get(rid, CFTYPE_MANIFEST, 0);
if( pMan==0 ){
/* Must be a phantom. Return without doing anything, and in particular
** without creating a mark for this check-in. */
gitmirror_message(VERB_NORMAL, "missing check-in: %s\n", zUuid);
return 0;
}
/* Check to see if any parent logins have not yet been processed, and
** if so, create them */
for(i=0; i<pMan->nParent; i++){
char *zPMark = gitmirror_find_mark(pMan->azParent[i], 0, 0);
if( zPMark==0 ){
int prid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q",
pMan->azParent[i]);
int rc = gitmirror_send_checkin(xCmd, prid, pMan->azParent[i],
pnLimit, fManifest);
if( rc || *pnLimit<=0 ){
manifest_destroy(pMan);
return 1;
}
}
fossil_free(zPMark);
}
/* Ignore phantom files on check-ins that are over one year old */
bPhantomOk = db_int(0, "SELECT %.6f<julianday('now','-1 year')",
pMan->rDate);
/* Make sure all necessary files have been exported */
db_prepare(&q,
"SELECT uuid FROM files_of_checkin(%Q)"
" WHERE uuid NOT IN (SELECT uuid FROM mirror.mmark)",
zUuid
);
while( db_step(&q)==SQLITE_ROW ){
const char *zFUuid = db_column_text(&q, 0);
int n = gitmirror_send_file(xCmd, zFUuid, bPhantomOk);
nErr += n;
if( n ) gitmirror_message(VERB_ERROR, "missing file: %s\n", zFUuid);
}
db_finalize(&q);
/* If some required files could not be exported, abandon the check-in
** export */
if( nErr ){
gitmirror_message(VERB_ERROR,
"export of %s abandoned due to missing files\n", zUuid);
*pnLimit = 0;
return 1;
}
/* Figure out which branch this check-in is a member of */
zBranch = db_text(0,
"SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=%d",
TAG_BRANCH, rid
);
if( fossil_strcmp(zBranch,"trunk")==0 ){
fossil_free(zBranch);
zBranch = mprintf("master");
}else if( zBranch==0 ){
zBranch = mprintf("unknown");
}else{
gitmirror_sanitize_name(zBranch);
}
/* Export the check-in */
fprintf(xCmd, "commit refs/heads/%s\n", zBranch);
fossil_free(zBranch);
zMark = gitmirror_find_mark(zUuid,0,1);
fprintf(xCmd, "mark %s\n", zMark);
fossil_free(zMark);
fprintf(xCmd, "committer %s <%s@noemail.net> %lld +0000\n",
pMan->zUser, pMan->zUser,
(sqlite3_int64)((pMan->rDate-2440587.5)*86400.0)
);
blob_init(&comment, pMan->zComment, -1);
if( blob_size(&comment)==0 ){
blob_append(&comment, "(no comment)", -1);
}
blob_appendf(&comment, "\n\nFossilOrigin-Name: %s", zUuid);
fprintf(xCmd, "data %d\n%s\n", blob_size(&comment), blob_str(&comment));
blob_reset(&comment);
iParent = -1; /* Which ancestor is the primary parent */
for(i=0; i<pMan->nParent; i++){
char *zOther = gitmirror_find_mark(pMan->azParent[i],0,0);
if( zOther==0 ) continue;
if( iParent<0 ){
iParent = i;
fprintf(xCmd, "from %s\n", zOther);
}else{
fprintf(xCmd, "merge %s\n", zOther);
}
fossil_free(zOther);
}
if( iParent>=0 ){
db_prepare(&q,
"SELECT filename FROM files_of_checkin(%Q)"
" EXCEPT SELECT filename FROM files_of_checkin(%Q)",
pMan->azParent[iParent], zUuid
);
while( db_step(&q)==SQLITE_ROW ){
fprintf(xCmd, "D %s\n", db_column_text(&q,0));
}
db_finalize(&q);
}
blob_init(&sql, 0, 0);
blob_append_sql(&sql,
"SELECT filename, uuid, perm FROM files_of_checkin(%Q)",
zUuid
);
if( pMan->nParent ){
blob_append_sql(&sql,
" EXCEPT SELECT filename, uuid, perm FROM files_of_checkin(%Q)",
pMan->azParent[0]);
}
db_prepare(&q,
"SELECT x.filename, x.perm,"
" coalesce(mmark.githash,printf(':%%d',mmark.id))"
" FROM (%s) AS x, mirror.mmark"
" WHERE mmark.uuid=x.uuid AND isfile",
blob_sql_text(&sql)
);
blob_reset(&sql);
while( db_step(&q)==SQLITE_ROW ){
const char *zFilename = db_column_text(&q,0);
const char *zMode = db_column_text(&q,1);
const char *zMark = db_column_text(&q,2);
const char *zGitMode = "100644";
char *zFNQuoted = 0;
if( zMode ){
if( strchr(zMode,'x') ) zGitMode = "100755";
if( strchr(zMode,'l') ) zGitMode = "120000";
}
zFNQuoted = gitmirror_quote_filename_if_needed(zFilename);
fprintf(xCmd,"M %s %s %s\n", zGitMode, zMark, zFNQuoted);
fossil_free(zFNQuoted);
}
db_finalize(&q);
/* Include Fossil-generated auxiliary files in the check-in */
if( fManifest & MFESTFLG_RAW ){
Blob manifest;
content_get(rid, &manifest);
fprintf(xCmd,"M 100644 inline manifest\ndata %d\n%s\n",
blob_size(&manifest), blob_str(&manifest));
blob_reset(&manifest);
}
if( fManifest & MFESTFLG_UUID ){
int n = (int)strlen(zUuid);
fprintf(xCmd,"M 100644 inline manifest.uuid\ndata %d\n%s\n", n, zUuid);
}
if( fManifest & MFESTFLG_TAGS ){
Blob tagslist;
blob_init(&tagslist, 0, 0);
get_checkin_taglist(rid, &tagslist);
fprintf(xCmd,"M 100644 inline manifest.tags\ndata %d\n%s\n",
blob_size(&tagslist), blob_str(&tagslist));
blob_reset(&tagslist);
}
/* The check-in is finished, so decrement the counter */
(*pnLimit)--;
return 0;
}
/*
** Implementation of the "fossil git export" command.
*/
void gitmirror_export_command(void){
const char *zLimit; /* Text of the --limit flag */
int nLimit = 0x7fffffff; /* Numeric value of the --limit flag */
int nTotal = 0; /* Total number of check-ins to export */
char *zMirror; /* Name of the mirror */
char *z; /* Generic string */
char *zCmd; /* git command to run as a subprocess */
const char *zDebug = 0; /* Value of the --debug flag */
const char *zAutoPush = 0; /* Value of the --autopush flag */
char *zPushUrl; /* URL to sync the mirror to */
double rEnd; /* time of most recent export */
int rc; /* Result code */
int bForce; /* Do the export and sync even if no changes*/
int bNeedRepack = 0; /* True if we should run repack at the end */
int fManifest; /* Current "manifest" setting */
FILE *xCmd; /* Pipe to the "git fast-import" command */
FILE *pMarks; /* Git mark files */
Stmt q; /* Queries */
char zLine[200]; /* One line of a mark file */
zDebug = find_option("debug",0,1);
db_find_and_open_repository(0, 0);
zLimit = find_option("limit", 0, 1);
if( zLimit ){
nLimit = (unsigned int)atoi(zLimit);
if( nLimit<=0 ) fossil_fatal("--limit must be positive");
}
zAutoPush = find_option("autopush",0,1);
bForce = find_option("force","f",0)!=0;
gitmirror_verbosity = VERB_NORMAL;
while( find_option("quiet","q",0)!=0 ){ gitmirror_verbosity--; }
while( find_option("verbose","v",0)!=0 ){ gitmirror_verbosity++; }
verify_all_options();
if( g.argc!=4 && g.argc!=3 ){ usage("export ?MIRROR?"); }
if( g.argc==4 ){
Blob mirror;
file_canonical_name(g.argv[3], &mirror, 0);
db_set("last-git-export-repo", blob_str(&mirror), 0);
blob_reset(&mirror);
}
zMirror = db_get("last-git-export-repo", 0);
if( zMirror==0 ){
fossil_fatal("no Git repository specified");
}
/* Make sure the GIT repository directory exists */
rc = file_mkdir(zMirror, ExtFILE, 0);
if( rc ) fossil_fatal("cannot create directory \"%s\"", zMirror);
/* Make sure GIT has been initialized */
z = mprintf("%s/.git", zMirror);
if( !file_isdir(z, ExtFILE) ){
zCmd = mprintf("git init '%s'",zMirror);
gitmirror_message(VERB_NORMAL, "%s\n", zCmd);
rc = fossil_system(zCmd);
if( rc ){
fossil_fatal("cannot initialize the git repository using: \"%s\"", zCmd);
}
fossil_free(zCmd);
bNeedRepack = 1;
}
fossil_free(z);
/* Make sure the .mirror_state subdirectory exists */
z = mprintf("%s/.mirror_state", zMirror);
rc = file_mkdir(z, ExtFILE, 0);
if( rc ) fossil_fatal("cannot create directory \"%s\"", z);
fossil_free(z);
/* Attach the .mirror_state/db database */
db_multi_exec("ATTACH '%q/.mirror_state/db' AS mirror;", zMirror);
db_begin_write();
db_multi_exec(
"CREATE TABLE IF NOT EXISTS mirror.mconfig(\n"
" key TEXT PRIMARY KEY,\n"
" Value ANY\n"
") WITHOUT ROWID;\n"
"CREATE TABLE IF NOT EXISTS mirror.mmark(\n"
" id INTEGER PRIMARY KEY,\n"
" uuid TEXT,\n"
" isfile BOOLEAN,\n"
" githash TEXT,\n"
" UNIQUE(uuid,isfile)\n"
");"
);
if( !db_table_has_column("mirror","mmark","isfile") ){
db_multi_exec(
"ALTER TABLE mirror.mmark RENAME TO mmark_old;"
"CREATE TABLE IF NOT EXISTS mirror.mmark(\n"
" id INTEGER PRIMARY KEY,\n"
" uuid TEXT,\n"
" isfile BOOLEAN,\n"
" githash TEXT,\n"
" UNIQUE(uuid,isfile)\n"
");"
"INSERT OR IGNORE INTO mirror.mmark(id,uuid,githash,isfile)"
" SELECT id,uuid,githash,"
" NOT EXISTS(SELECT 1 FROM repository.event, repository.blob"
" WHERE event.objid=blob.rid"
" AND blob.uuid=mmark_old.uuid)"
" FROM mirror.mmark_old;\n"
"DROP TABLE mirror.mmark_old;\n"
);
}
/* Change the autopush setting if the --autopush flag is present */
if( zAutoPush ){
if( is_false(zAutoPush) ){
db_multi_exec("DELETE FROM mirror.mconfig WHERE key='autopush'");
}else{
db_multi_exec(
"REPLACE INTO mirror.mconfig(key,value)"
"VALUES('autopush',%Q)",
zAutoPush
);
}
}
/* See if there is any work to be done. Exit early if not, before starting
** the "git fast-import" command. */
if( !bForce
&& !db_exists("SELECT 1 FROM event WHERE type IN ('ci','t')"
" AND mtime>coalesce((SELECT value FROM mconfig"
" WHERE key='start'),0.0)")
){
gitmirror_message(VERB_NORMAL, "no changes\n");
db_commit_transaction();
return;
}
/* Do we need to include manifest files in the clone? */
fManifest = db_get_manifest_setting();
/* Change to the MIRROR directory so that the Git commands will work */
rc = file_chdir(zMirror, 0);
if( rc ) fossil_fatal("cannot change the working directory to \"%s\"",
zMirror);
/* Start up the git fast-import command */
if( zDebug ){
if( fossil_strcmp(zDebug,"stdout")==0 ){
xCmd = stdout;
}else{
xCmd = fopen(zDebug, "wb");
if( xCmd==0 ) fossil_fatal("cannot open file \"%s\" for writing", zDebug);
}
}else{
zCmd = mprintf("git fast-import"
" --export-marks=.mirror_state/marks.txt"
" --quiet --done");
gitmirror_message(VERB_NORMAL, "%s\n", zCmd);
xCmd = popen(zCmd, "w");
if( zCmd==0 ){
fossil_fatal("cannot start the \"git fast-import\" command");
}
fossil_free(zCmd);
}
/* Run the export */
rEnd = 0.0;
db_multi_exec(
"CREATE TEMP TABLE tomirror(objid,mtime,uuid);\n"
"INSERT INTO tomirror "
"SELECT objid, mtime, blob.uuid FROM event, blob\n"
" WHERE type='ci'"
" AND mtime>coalesce((SELECT value FROM mconfig WHERE key='start'),0.0)"
" AND blob.rid=event.objid"
" AND blob.uuid NOT IN (SELECT uuid FROM mirror.mmark WHERE NOT isfile);"
);
nTotal = db_int(0, "SELECT count(*) FROM tomirror");
if( nLimit<nTotal ){
nTotal = nLimit;
}else if( nLimit>nTotal ){
nLimit = nTotal;
}
db_prepare(&q,
"SELECT objid, mtime, uuid FROM tomirror ORDER BY mtime"
);
while( nLimit && db_step(&q)==SQLITE_ROW ){
int rid = db_column_int(&q, 0);
double rMTime = db_column_double(&q, 1);
const char *zUuid = db_column_text(&q, 2);
if( rMTime>rEnd ) rEnd = rMTime;
rc = gitmirror_send_checkin(xCmd, rid, zUuid, &nLimit, fManifest);
if( rc ) break;
gitmirror_message(VERB_NORMAL,"%d/%d \r", nTotal-nLimit, nTotal);
fflush(stdout);
}
db_finalize(&q);
fprintf(xCmd, "done\n");
if( zDebug ){
if( xCmd!=stdout ) fclose(xCmd);
}else{
pclose(xCmd);
}
gitmirror_message(VERB_NORMAL, "%d check-ins added to the %s\n",
nTotal-nLimit, zMirror);
/* Read the export-marks file. Transfer the new marks over into
** the import-marks file.
*/
pMarks = fopen(".mirror_state/marks.txt", "rb");
if( pMarks ){
db_prepare(&q, "UPDATE mirror.mmark SET githash=:githash WHERE id=:id");
while( fgets(zLine, sizeof(zLine), pMarks) ){
int j, k;
if( zLine[0]!=':' ) continue;
db_bind_int(&q, ":id", atoi(zLine+1));
for(j=1; zLine[j] && zLine[j]!=' '; j++){}
if( zLine[j]!=' ' ) continue;
j++;
if( zLine[j]==0 ) continue;
for(k=j; fossil_isalnum(zLine[k]); k++){}
zLine[k] = 0;
db_bind_text(&q, ":githash", &zLine[j]);
db_step(&q);
db_reset(&q);
}
db_finalize(&q);
fclose(pMarks);
file_delete(".mirror_state/marks.txt");
}else{
fossil_fatal("git fast-import didn't generate a marks file!");
}
db_multi_exec(
"CREATE INDEX IF NOT EXISTS mirror.mmarkx1 ON mmark(githash);"
);
/* Do any tags that have been created since the start time */
db_prepare(&q,
"SELECT substr(tagname,5), githash"
" FROM (SELECT tagxref.tagid AS xtagid, tagname, rid, max(mtime) AS mtime"
" FROM tagxref JOIN tag ON tag.tagid=tagxref.tagid"
" WHERE tag.tagname GLOB 'sym-*'"
" AND tagxref.tagtype=1"
" AND tagxref.mtime > coalesce((SELECT value FROM mconfig"
" WHERE key='start'),0.0)"
" GROUP BY tagxref.tagid) AS tx"
" JOIN blob ON tx.rid=blob.rid"
" JOIN mmark ON mmark.uuid=blob.uuid;"
);
while( db_step(&q)==SQLITE_ROW ){
char *zTagname = fossil_strdup(db_column_text(&q,0));
const char *zObj = db_column_text(&q,1);
char *zTagCmd;
gitmirror_sanitize_name(zTagname);
zTagCmd = mprintf("git tag -f \"%s\" %s", zTagname, zObj);
fossil_free(zTagname);
gitmirror_message(VERB_NORMAL, "%s\n", zTagCmd);
fossil_system(zTagCmd);
fossil_free(zTagCmd);
}
db_finalize(&q);
/* Update all references that might have changed since the start time */
db_prepare(&q,
"SELECT"
" tagxref.value AS name,"
" max(event.mtime) AS mtime,"
" mmark.githash AS gitckin"
" FROM tagxref, tag, event, blob, mmark"
" WHERE tagxref.tagid=tag.tagid"
" AND tagxref.tagtype>0"
" AND tag.tagname='branch'"
" AND event.objid=tagxref.rid"
" AND event.mtime > coalesce((SELECT value FROM mconfig"
" WHERE key='start'),0.0)"
" AND blob.rid=tagxref.rid"
" AND mmark.uuid=blob.uuid"
" GROUP BY 1"
);
while( db_step(&q)==SQLITE_ROW ){
char *zBrname = fossil_strdup(db_column_text(&q,0));
const char *zObj = db_column_text(&q,2);
char *zRefCmd;
if( fossil_strcmp(zBrname,"trunk")==0 ){
fossil_free(zBrname);
zBrname = fossil_strdup("master");
}else{
gitmirror_sanitize_name(zBrname);
}
zRefCmd = mprintf("git update-ref \"refs/heads/%s\" %s", zBrname, zObj);
fossil_free(zBrname);
gitmirror_message(VERB_NORMAL, "%s\n", zRefCmd);
fossil_system(zRefCmd);
fossil_free(zRefCmd);
}
db_finalize(&q);
/* Update the start time */
if( rEnd>0.0 ){
db_prepare(&q, "REPLACE INTO mirror.mconfig(key,value) VALUES('start',:x)");
db_bind_double(&q, ":x", rEnd);
db_step(&q);
db_finalize(&q);
}
db_commit_transaction();
/* Maybe run a git repack */
if( bNeedRepack ){
const char *zRepack = "git repack -adf";
gitmirror_message(VERB_NORMAL, "%s\n", zRepack);
fossil_system(zRepack);
}
/* Optionally do a "git push" */
zPushUrl = db_text(0, "SELECT value FROM mconfig WHERE key='autopush'");
if( zPushUrl ){
char *zPushCmd;
UrlData url;
if( sqlite3_strglob("http*", zPushUrl)==0 ){
url_parse_local(zPushUrl, 0, &url);
zPushCmd = mprintf("git push --mirror %s", url.canonical);
}else{
zPushCmd = mprintf("git push --mirror %s", zPushUrl);
}
gitmirror_message(VERB_NORMAL, "%s\n", zPushCmd);
fossil_free(zPushCmd);
zPushCmd = mprintf("git push --mirror %s", zPushUrl);
fossil_system(zPushCmd);
fossil_free(zPushCmd);
}
}
/*
** Implementation of the "fossil git status" command.
**
** Show the status of a "git export".
*/
void gitmirror_status_command(void){
char *zMirror;
char *z;
int n, k;
db_find_and_open_repository(0, 0);
verify_all_options();
zMirror = db_get("last-git-export-repo", 0);
if( zMirror==0 ){
fossil_print("Git mirror: none\n");
return;
}
fossil_print("Git mirror: %s\n", zMirror);
db_multi_exec("ATTACH '%q/.mirror_state/db' AS mirror;", zMirror);
z = db_text(0, "SELECT datetime(value) FROM mconfig WHERE key='start'");
if( z ){
double rAge = db_double(0.0, "SELECT julianday('now') - value"
" FROM mconfig WHERE key='start'");
if( rAge>1.0/86400.0 ){
fossil_print("Last export: %s (%z ago)\n", z, human_readable_age(rAge));
}else{
fossil_print("Last export: %s (moments ago)\n", z);
}
}
z = db_text(0, "SELECT value FROM mconfig WHERE key='autopush'");
if( z==0 ){
fossil_print("Autopush: off\n");
}else{
UrlData url;
url_parse_local(z, 0, &url);
fossil_print("Autopush: %s\n", url.canonical);
}
n = db_int(0,
"SELECT count(*) FROM event"
" WHERE type='ci'"
" AND mtime>coalesce((SELECT value FROM mconfig"
" WHERE key='start'),0.0)"
);
if( n==0 ){
fossil_print("Status: up-to-date\n");
}else{
fossil_print("Status: %d check-in%s awaiting export\n",
n, n==1 ? "" : "s");
}
n = db_int(0, "SELECT count(*) FROM mmark WHERE isfile");
k = db_int(0, "SELECT count(*) FROm mmark WHERE NOT isfile");
fossil_print("Exported: %d check-ins and %d file blobs\n", k, n);
}
/*
** COMMAND: git
**
** Usage: %fossil git SUBCOMMAND
**
** Do incremental import or export operations between Fossil and Git.
** Subcommands:
**
** fossil git export [MIRROR] [OPTIONS]
**
** Write content from the Fossil repository into the Git repository
** in directory MIRROR. The Git repository is created if it does not
** already exist. If the Git repository does already exist, then
** new content added to fossil since the previous export is appended.
**
** Repeat this command whenever new checkins are added to the Fossil
** repository in order to reflect those changes into the mirror. If
** the MIRROR option is omitted, the repository from the previous
** invocation is used.
**
** The MIRROR directory will contain a subdirectory named
** ".mirror_state" that contains information that Fossil needs to
** do incremental exports. Do not attempt to manage or edit the files
** in that directory since doing so can disrupt future incremental
** exports.
**
** Options:
** --autopush URL Automatically do a 'git push' to URL. The
** URL is remembered and used on subsequent exports
** to the same repository. Or if URL is "off" the
** auto-push mechanism is disabled
** --debug FILE Write fast-export text to FILE rather than
** piping it into "git fast-import".
** --force|-f Do the export even if nothing has changed
** --limit N Add no more than N new check-ins to MIRROR.
** Useful for debugging
** --quiet|-q Reduce output. Repeat for even less output.
** --verbose|-v More output.
**
** fossil git import MIRROR
**
** TBD...
**
** fossil git status
**
** Show the status of the current Git mirror, if there is one.
*/
void gitmirror_command(void){
char *zCmd;
int nCmd;
if( g.argc<3 ){
usage("export ARGS...");
}
zCmd = g.argv[2];
nCmd = (int)strlen(zCmd);
if( nCmd>2 && strncmp(zCmd,"export",nCmd)==0 ){
gitmirror_export_command();
}else
if( nCmd>2 && strncmp(zCmd,"import",nCmd)==0 ){
fossil_fatal("not yet implemented - check back later");
}else
if( nCmd>2 && strncmp(zCmd,"status",nCmd)==0 ){
gitmirror_status_command();
}else
{
fossil_fatal("unknown subcommand \"%s\": should be one of "
"\"export\", \"import\", \"status\"",
zCmd);
}
}
|
Changes to src/file.c.
| ︙ | ︙ | |||
435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
const char *zTail = file_tail(z);
if( zTail && zTail!=z ){
return mprintf("%.*s", (int)(zTail-z-1), z);
}else{
return 0;
}
}
/*
** Rename a file or directory.
** Returns zero upon success.
*/
int file_rename(
const char *zFrom,
| > > > > > > > > > > > > > > > > > > > | 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 |
const char *zTail = file_tail(z);
if( zTail && zTail!=z ){
return mprintf("%.*s", (int)(zTail-z-1), z);
}else{
return 0;
}
}
/* SQL Function: file_dirname(NAME)
**
** Return the directory for NAME
*/
void file_dirname_sql_function(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *zName = (const char*)sqlite3_value_text(argv[0]);
char *zDir;
if( zName==0 ) return;
zDir = file_dirname(zName);
if( zDir ){
sqlite3_result_text(context,zDir,-1,fossil_free);
}
}
/*
** Rename a file or directory.
** Returns zero upon success.
*/
int file_rename(
const char *zFrom,
|
| ︙ | ︙ | |||
592 593 594 595 596 597 598 599 600 601 602 603 604 605 | #else char *z = fossil_utf8_to_path(zFilename, 0); rc = unlink(zFilename); #endif fossil_path_free(z); return rc; } /* ** Create a directory called zName, if it does not already exist. ** If forceFlag is 1, delete any prior non-directory object ** with the same name. ** ** Return the number of errors. | > > > > > > > > > > > > > > > > > > > > | 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 |
#else
char *z = fossil_utf8_to_path(zFilename, 0);
rc = unlink(zFilename);
#endif
fossil_path_free(z);
return rc;
}
/* SQL Function: file_delete(NAME)
**
** Remove file NAME. Return zero on success and non-zero if anything goes
** wrong.
*/
void file_delete_sql_function(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *zName = (const char*)sqlite3_value_text(argv[0]);
int rc;
if( zName==0 ){
rc = 1;
}else{
rc = file_delete(zName);
}
sqlite3_result_int(context, rc);
}
/*
** Create a directory called zName, if it does not already exist.
** If forceFlag is 1, delete any prior non-directory object
** with the same name.
**
** Return the number of errors.
|
| ︙ | ︙ | |||
682 683 684 685 686 687 688 689 690 691 692 693 694 695 |
rc = rmdir(zName);
#endif
fossil_path_free(zMbcs);
return rc;
}
return 0;
}
/*
** Return true if the filename given is a valid filename for
** a file in a repository. Valid filenames follow all of the
** following rules:
**
** * Does not begin with "/"
| > > > > > > > > > > > > > > > > > > > > | 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 |
rc = rmdir(zName);
#endif
fossil_path_free(zMbcs);
return rc;
}
return 0;
}
/* SQL Function: rmdir(NAME)
**
** Try to remove the directory NAME. Return zero on success and non-zero
** for failure.
*/
void file_rmdir_sql_function(
sqlite3_context *context,
int argc,
sqlite3_value **argv
){
const char *zName = (const char*)sqlite3_value_text(argv[0]);
int rc;
if( zName==0 ){
rc = 1;
}else{
rc = file_rmdir(zName);
}
sqlite3_result_int(context, rc);
}
/*
** Return true if the filename given is a valid filename for
** a file in a repository. Valid filenames follow all of the
** following rules:
**
** * Does not begin with "/"
|
| ︙ | ︙ | |||
888 889 890 891 892 893 894 895 | /* ** Get the current working directory. ** ** On windows, the name is converted from unicode to UTF8 and all '\\' ** characters are converted to '/'. No conversions are needed on ** unix. */ | > > > | > > > > > > | 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 |
/*
** Get the current working directory.
**
** On windows, the name is converted from unicode to UTF8 and all '\\'
** characters are converted to '/'. No conversions are needed on
** unix.
**
** Store the value of the CWD in zBuf which is nBuf bytes in size.
** or if zBuf==0, allocate space to hold the result using fossil_malloc().
*/
char *file_getcwd(char *zBuf, int nBuf){
char zTemp[2000];
if( zBuf==0 ){
zBuf = zTemp;
nBuf = sizeof(zTemp);
}
#ifdef _WIN32
win32_getcwd(zBuf, nBuf);
#else
if( getcwd(zBuf, nBuf-1)==0 ){
if( errno==ERANGE ){
fossil_panic("pwd too big: max %d", nBuf-1);
}else{
fossil_panic("cannot find current working directory; %s",
strerror(errno));
}
}
#endif
return zBuf==zTemp ? fossil_strdup(zBuf) : zBuf;
}
/*
** Return true if zPath is an absolute pathname. Return false
** if it is relative.
*/
int file_is_absolute_path(const char *zPath){
|
| ︙ | ︙ | |||
1389 1390 1391 1392 1393 1394 1395 |
blob_set(pPath, &zUri[i]);
}else{
blob_set(pPath, "/");
}
}
/*
| | > > > > > > > | | 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 |
blob_set(pPath, &zUri[i]);
}else{
blob_set(pPath, "/");
}
}
/*
** Construct a random temporary filename into pBuf where the name of
** the temporary file is derived from zBasis. The suffix on the temp
** file is the same as the suffix on zBasis, and the temp file has
** the root of zBasis in its name.
**
** If zTag is not NULL, then try to create the temp-file using zTag
** as a differentiator. If that fails, or if zTag is NULL, then use
** a bunch of random characters as the tag.
*/
void file_tempname(Blob *pBuf, const char *zBasis, const char *zTag){
#if defined(_WIN32)
const char *azDirs[] = {
0, /* GetTempPath */
0, /* TEMP */
0, /* TMP */
".",
};
|
| ︙ | ︙ | |||
1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 |
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789";
unsigned int i;
const char *zDir = ".";
int cnt = 0;
char zRand[16];
#if defined(_WIN32)
wchar_t zTmpPath[MAX_PATH];
if( GetTempPathW(MAX_PATH, zTmpPath) ){
azDirs[0] = fossil_path_to_utf8(zTmpPath);
/* Removing trailing \ from the temp path */
| > > | 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 |
"abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"0123456789";
unsigned int i;
const char *zDir = ".";
int cnt = 0;
char zRand[16];
int nBasis;
const char *zSuffix;
#if defined(_WIN32)
wchar_t zTmpPath[MAX_PATH];
if( GetTempPathW(MAX_PATH, zTmpPath) ){
azDirs[0] = fossil_path_to_utf8(zTmpPath);
/* Removing trailing \ from the temp path */
|
| ︙ | ︙ | |||
1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 |
for(i=0; i<count(azDirs); i++){
if( azDirs[i]==0 ) continue;
if( !file_isdir(azDirs[i], ExtFILE) ) continue;
zDir = azDirs[i];
break;
}
do{
blob_zero(pBuf);
if( cnt++>20 ) fossil_panic("cannot generate a temporary filename");
| > > > > > > > > > > > > > > > > > > > > > | | | | | > > | > | 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 |
for(i=0; i<count(azDirs); i++){
if( azDirs[i]==0 ) continue;
if( !file_isdir(azDirs[i], ExtFILE) ) continue;
zDir = azDirs[i];
break;
}
assert( zBasis!=0 );
zSuffix = 0;
for(i=0; zBasis[i]; i++){
if( zBasis[i]=='/' || zBasis[i]=='\\' ){
zBasis += i+1;
i = -1;
}else if( zBasis[i]=='.' ){
zSuffix = zBasis + i;
}
}
if( zSuffix==0 || zSuffix<=zBasis ){
zSuffix = "";
nBasis = i;
}else{
nBasis = (int)(zSuffix - zBasis);
}
if( nBasis==0 ){
nBasis = 6;
zBasis = "fossil";
}
do{
blob_zero(pBuf);
if( cnt++>20 ) fossil_panic("cannot generate a temporary filename");
if( zTag==0 ){
sqlite3_randomness(15, zRand);
for(i=0; i<15; i++){
zRand[i] = (char)zChars[ ((unsigned char)zRand[i])%(sizeof(zChars)-1) ];
}
zRand[15] = 0;
zTag = zRand;
}
blob_appendf(pBuf, "%s/%.*s~%s%s", zDir, nBasis, zBasis, zTag, zSuffix);
zTag = 0;
}while( file_size(blob_str(pBuf), ExtFILE)>=0 );
#if defined(_WIN32)
fossil_path_free((char *)azDirs[0]);
fossil_path_free((char *)azDirs[1]);
fossil_path_free((char *)azDirs[2]);
/* Change all \ characters in the windows path into / so that they can
|
| ︙ | ︙ | |||
1487 1488 1489 1490 1491 1492 1493 |
zDir, tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec, cnt++, r%1000000, zSuffix);
}
/*
** COMMAND: test-tempname
| | | > > | | 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 |
zDir, tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec, cnt++, r%1000000, zSuffix);
}
/*
** COMMAND: test-tempname
** Usage: fossil test-name [--time SUFFIX] [--tag NAME] BASENAME ...
**
** Generate temporary filenames derived from BASENAME. Use the --time
** option to generate temp names based on the time of day. If --tag NAME
** is specified, try to use NAME as the differentiator in the temp file.
*/
void file_test_tempname(void){
int i;
const char *zSuffix = find_option("time",0,1);
Blob x = BLOB_INITIALIZER;
char *z;
const char *zTag = find_option("tag",0,1);
verify_all_options();
for(i=2; i<g.argc; i++){
if( zSuffix ){
z = file_time_tempname(g.argv[i], zSuffix);
fossil_print("%s\n", z);
fossil_free(z);
}else{
file_tempname(&x, g.argv[i], zTag);
fossil_print("%s\n", blob_str(&x));
blob_reset(&x);
}
}
}
|
| ︙ | ︙ |
Changes to src/finfo.c.
| ︙ | ︙ | |||
213 214 215 216 217 218 219 |
char *zOut;
if( zBr==0 ) zBr = "trunk";
if( iBrief ){
fossil_print("%s ", zDate);
zOut = mprintf(
"[%S] %s (user: %s, artifact: [%S], branch: %s)",
zCiUuid, zCom, zUser, zFileUuid, zBr);
| | | | 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
char *zOut;
if( zBr==0 ) zBr = "trunk";
if( iBrief ){
fossil_print("%s ", zDate);
zOut = mprintf(
"[%S] %s (user: %s, artifact: [%S], branch: %s)",
zCiUuid, zCom, zUser, zFileUuid, zBr);
comment_print(zOut, zCom, 11, iWidth, get_comment_format());
fossil_free(zOut);
}else{
blob_reset(&line);
blob_appendf(&line, "%S ", zCiUuid);
blob_appendf(&line, "%.10s ", zDate);
blob_appendf(&line, "%8.8s ", zUser);
blob_appendf(&line, "%8.8s ", zBr);
blob_appendf(&line,"%-39.39s", zCom );
comment_print(blob_str(&line), zCom, 0, iWidth, get_comment_format());
}
}
db_finalize(&q);
blob_reset(&fname);
}
}
|
| ︙ | ︙ | |||
328 329 330 331 332 333 334 335 336 337 338 339 340 341 |
tmFlags = timeline_ss_submenu();
if( tmFlags & TIMELINE_COLUMNAR ){
zStyle = "Columnar";
}else if( tmFlags & TIMELINE_COMPACT ){
zStyle = "Compact";
}else if( tmFlags & TIMELINE_VERBOSE ){
zStyle = "Verbose";
}else{
zStyle = "Modern";
}
url_initialize(&url, "finfo");
if( brBg ) url_add_parameter(&url, "brbg", 0);
if( uBg ) url_add_parameter(&url, "ubg", 0);
baseCheckin = name_to_rid_www("ci");
| > > | 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 |
tmFlags = timeline_ss_submenu();
if( tmFlags & TIMELINE_COLUMNAR ){
zStyle = "Columnar";
}else if( tmFlags & TIMELINE_COMPACT ){
zStyle = "Compact";
}else if( tmFlags & TIMELINE_VERBOSE ){
zStyle = "Verbose";
}else if( tmFlags & TIMELINE_CLASSIC ){
zStyle = "Classic";
}else{
zStyle = "Modern";
}
url_initialize(&url, "finfo");
if( brBg ) url_add_parameter(&url, "brbg", 0);
if( uBg ) url_add_parameter(&url, "ubg", 0);
baseCheckin = name_to_rid_www("ci");
|
| ︙ | ︙ | |||
501 502 503 504 505 506 507 |
if( zBr==0 ) zBr = "trunk";
if( uBg ){
zBgClr = hash_color(zUser);
}else if( brBg || zBgClr==0 || zBgClr[0]==0 ){
zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr);
}
gidx = graph_add_row(pGraph, frid>0 ? frid : fpid+1000000000,
| | | 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 |
if( zBr==0 ) zBr = "trunk";
if( uBg ){
zBgClr = hash_color(zUser);
}else if( brBg || zBgClr==0 || zBgClr[0]==0 ){
zBgClr = strcmp(zBr,"trunk")==0 ? "" : hash_color(zBr);
}
gidx = graph_add_row(pGraph, frid>0 ? frid : fpid+1000000000,
nParent, 0, aParent, zBr, zBgClr,
zUuid, 0);
if( strncmp(zDate, zPrevDate, 10) ){
sqlite3_snprintf(sizeof(zPrevDate), zPrevDate, "%.10s", zDate);
@ <tr><td>
@ <div class="divider timelineDate">%s(zPrevDate)</div>
@ </td><td></td><td></td></tr>
}
|
| ︙ | ︙ | |||
571 572 573 574 575 576 577 |
@ check-in: \
hyperlink_to_uuid(zCkin);
if( fShowId ){
@ (%d(fmid))
}
@ user: \
hyperlink_to_user(zUser, zDate, ",");
| | | 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 |
@ check-in: \
hyperlink_to_uuid(zCkin);
if( fShowId ){
@ (%d(fmid))
}
@ user: \
hyperlink_to_user(zUser, zDate, ",");
@ branch: %z(href("%R/timeline?t=%T",zBr))%h(zBr)</a>,
if( tmFlags & (TIMELINE_COMPACT|TIMELINE_VERBOSE) ){
@ size: %d(szFile))
}else{
@ size: %d(szFile)
}
if( zUuid && origCheckin==0 ){
if( nParent==0 ){
|
| ︙ | ︙ |
Changes to src/foci.c.
| ︙ | ︙ | |||
119 120 121 122 123 124 125 |
**
** (0) A full scan. Visit every manifest in the repo. (Slow)
** (1) checkinID=?. visit only the single manifest specified.
** (2) symName=? visit only the single manifest specified.
*/
static int fociBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
int i;
| | > | 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
**
** (0) A full scan. Visit every manifest in the repo. (Slow)
** (1) checkinID=?. visit only the single manifest specified.
** (2) symName=? visit only the single manifest specified.
*/
static int fociBestIndex(sqlite3_vtab *tab, sqlite3_index_info *pIdxInfo){
int i;
pIdxInfo->estimatedCost = 1000000000.0;
for(i=0; i<pIdxInfo->nConstraint; i++){
if( !pIdxInfo->aConstraint[i].usable ) continue;
if( pIdxInfo->aConstraint[i].op==SQLITE_INDEX_CONSTRAINT_EQ
&& (pIdxInfo->aConstraint[i].iColumn==FOCI_CHECKINID
|| pIdxInfo->aConstraint[i].iColumn==FOCI_SYMNAME)
){
if( pIdxInfo->aConstraint[i].iColumn==FOCI_CHECKINID ){
pIdxInfo->idxNum = 1;
}else{
|
| ︙ | ︙ |
Changes to src/graph.c.
| ︙ | ︙ | |||
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
**
** The nParent field is -1 for entires that do not participate in the graph
** but which are included just so that we can capture their background color.
*/
struct GraphRow {
int rid; /* The rid for the check-in */
i8 nParent; /* Number of parents. -1 for technote lines */
int *aParent; /* Array of parents. 0 element is primary .*/
char *zBranch; /* Branch name */
char *zBgClr; /* Background Color */
char zUuid[HNAME_MAX+1]; /* Check-in for file ID */
GraphRow *pNext; /* Next row down in the list of all rows */
GraphRow *pPrev; /* Previous row */
int idx; /* Row index. First is 1. 0 used for "none" */
int idxTop; /* Direct descendent highest up on the graph */
GraphRow *pChild; /* Child immediately above this node */
u8 isDup; /* True if this is duplicate of a prior entry */
u8 isLeaf; /* True if this is a leaf node */
u8 timeWarp; /* Child is earlier in time */
u8 bDescender; /* True if riser from bottom of graph to here. */
i8 iRail; /* Which rail this check-in appears on. 0-based.*/
i8 mergeOut; /* Merge out to this rail. -1 if no merge-out */
u8 mergeIn[GR_MAX_RAIL]; /* Merge in from non-zero rails */
int aiRiser[GR_MAX_RAIL]; /* Risers from this node to a higher row. */
int mergeUpto; /* Draw the mergeOut rail up to this level */
u64 mergeDown; /* Draw merge lines up from bottom of graph */
u64 railInUse; /* Mask of occupied rails at this row */
};
/* Context while building a graph
*/
struct GraphContext {
| > > > > > | 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
**
** The nParent field is -1 for entires that do not participate in the graph
** but which are included just so that we can capture their background color.
*/
struct GraphRow {
int rid; /* The rid for the check-in */
i8 nParent; /* Number of parents. -1 for technote lines */
i8 nCherrypick; /* Subset of aParent that are cherrypicks */
i8 nNonCherrypick; /* Number of non-cherrypick parents */
int *aParent; /* Array of parents. 0 element is primary .*/
char *zBranch; /* Branch name */
char *zBgClr; /* Background Color */
char zUuid[HNAME_MAX+1]; /* Check-in for file ID */
GraphRow *pNext; /* Next row down in the list of all rows */
GraphRow *pPrev; /* Previous row */
int idx; /* Row index. First is 1. 0 used for "none" */
int idxTop; /* Direct descendent highest up on the graph */
GraphRow *pChild; /* Child immediately above this node */
u8 isDup; /* True if this is duplicate of a prior entry */
u8 isLeaf; /* True if this is a leaf node */
u8 hasNormalOutMerge; /* Is parent of at laest 1 non-cherrypick merge */
u8 timeWarp; /* Child is earlier in time */
u8 bDescender; /* True if riser from bottom of graph to here. */
i8 iRail; /* Which rail this check-in appears on. 0-based.*/
i8 mergeOut; /* Merge out to this rail. -1 if no merge-out */
u8 mergeIn[GR_MAX_RAIL]; /* Merge in from non-zero rails */
int aiRiser[GR_MAX_RAIL]; /* Risers from this node to a higher row. */
int mergeUpto; /* Draw the mergeOut rail up to this level */
int cherrypickUpto; /* Continue the mergeOut rail up to here */
u64 mergeDown; /* Draw merge lines up from bottom of graph */
u64 cherrypickDown; /* Draw cherrypick lines up from bottom */
u64 railInUse; /* Mask of occupied rails at this row */
};
/* Context while building a graph
*/
struct GraphContext {
|
| ︙ | ︙ | |||
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
/*
** Add a new row to the graph context. Rows are added from top to bottom.
*/
int graph_add_row(
GraphContext *p, /* The context to which the row is added */
int rid, /* RID for the check-in */
int nParent, /* Number of parents */
int *aParent, /* Array of parents */
const char *zBranch, /* Branch for this check-in */
const char *zBgClr, /* Background color. NULL or "" for white. */
const char *zUuid, /* hash name of the object being graphed */
int isLeaf /* True if this row is a leaf */
){
GraphRow *pRow;
int nByte;
static int nRow = 0;
if( p->nErr ) return 0;
nByte = sizeof(GraphRow);
if( nParent>0 ) nByte += sizeof(pRow->aParent[0])*nParent;
pRow = (GraphRow*)safeMalloc( nByte );
pRow->aParent = nParent>0 ? (int*)&pRow[1] : 0;
pRow->rid = rid;
pRow->nParent = nParent;
pRow->zBranch = persistBranchName(p, zBranch);
if( zUuid==0 ) zUuid = "";
sqlite3_snprintf(sizeof(pRow->zUuid), pRow->zUuid, "%s", zUuid);
pRow->isLeaf = isLeaf;
memset(pRow->aiRiser, -1, sizeof(pRow->aiRiser));
if( zBgClr==0 ) zBgClr = "";
pRow->zBgClr = persistBranchName(p, zBgClr);
| > > > > > > | 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
/*
** Add a new row to the graph context. Rows are added from top to bottom.
*/
int graph_add_row(
GraphContext *p, /* The context to which the row is added */
int rid, /* RID for the check-in */
int nParent, /* Number of parents */
int nCherrypick, /* How many of aParent[] are actually cherrypicks */
int *aParent, /* Array of parents */
const char *zBranch, /* Branch for this check-in */
const char *zBgClr, /* Background color. NULL or "" for white. */
const char *zUuid, /* hash name of the object being graphed */
int isLeaf /* True if this row is a leaf */
){
GraphRow *pRow;
int nByte;
static int nRow = 0;
if( p->nErr ) return 0;
nByte = sizeof(GraphRow);
if( nParent>0 ) nByte += sizeof(pRow->aParent[0])*nParent;
pRow = (GraphRow*)safeMalloc( nByte );
pRow->aParent = nParent>0 ? (int*)&pRow[1] : 0;
pRow->rid = rid;
if( nCherrypick>=nParent ){
nCherrypick = nParent-1; /* Safety. Should never happen. */
}
pRow->nParent = nParent;
pRow->nCherrypick = nCherrypick;
pRow->nNonCherrypick = nParent - nCherrypick;
pRow->zBranch = persistBranchName(p, zBranch);
if( zUuid==0 ) zUuid = "";
sqlite3_snprintf(sizeof(pRow->zUuid), pRow->zUuid, "%s", zUuid);
pRow->isLeaf = isLeaf;
memset(pRow->aiRiser, -1, sizeof(pRow->aiRiser));
if( zBgClr==0 ) zBgClr = "";
pRow->zBgClr = persistBranchName(p, zBgClr);
|
| ︙ | ︙ | |||
282 283 284 285 286 287 288 | /* ** Create a merge-arrow riser going from pParent up to pChild. */ static void createMergeRiser( GraphContext *p, GraphRow *pParent, | | > < | < > > > > > > > > > > | | > > | 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 |
/*
** Create a merge-arrow riser going from pParent up to pChild.
*/
static void createMergeRiser(
GraphContext *p,
GraphRow *pParent,
GraphRow *pChild,
int isCherrypick
){
int u;
u64 mask;
GraphRow *pLoop;
if( pParent->mergeOut<0 ){
u = pParent->aiRiser[pParent->iRail];
if( u>=0 && u<pChild->idx ){
/* The thick arrow up to the next primary child of pDesc goes
** further up than the thin merge arrow riser, so draw them both
** on the same rail. */
pParent->mergeOut = pParent->iRail;
}else{
/* The thin merge arrow riser is taller than the thick primary
** child riser, so use separate rails. */
int iTarget = pParent->iRail;
pParent->mergeOut = findFreeRail(p, pChild->idx, pParent->idx-1, iTarget);
mask = BIT(pParent->mergeOut);
for(pLoop=pChild->pNext; pLoop && pLoop->rid!=pParent->rid;
pLoop=pLoop->pNext){
pLoop->railInUse |= mask;
}
}
}
if( isCherrypick ){
if( pParent->cherrypickUpto==0 || pParent->cherrypickUpto > pChild->idx ){
pParent->cherrypickUpto = pChild->idx;
}
}else{
pParent->hasNormalOutMerge = 1;
if( pParent->mergeUpto==0 || pParent->mergeUpto > pChild->idx ){
pParent->mergeUpto = pChild->idx;
}
}
pChild->mergeIn[pParent->mergeOut] = isCherrypick ? 2 : 1;
}
/*
** Compute the maximum rail number.
*/
static void find_max_rail(GraphContext *p){
GraphRow *pRow;
p->mxRail = 0;
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
if( pRow->iRail>p->mxRail ) p->mxRail = pRow->iRail;
if( pRow->mergeOut>p->mxRail ) p->mxRail = pRow->mergeOut;
while( p->mxRail<GR_MAX_RAIL
&& (pRow->mergeDown|pRow->cherrypickDown)>(BIT(p->mxRail+1)-1)
){
p->mxRail++;
}
}
}
/*
** Draw a riser from pRow to the top of the graph
|
| ︙ | ︙ | |||
393 394 395 396 397 398 399 |
** of this graph, then no arrows will be drawn for it, so remove it from
** the aParent[] array.
*/
if( omitDescenders ){
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
for(i=1; i<pRow->nParent; i++){
if( hashFind(p, pRow->aParent[i])==0 ){
| > > | > > > > > | | | 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 |
** of this graph, then no arrows will be drawn for it, so remove it from
** the aParent[] array.
*/
if( omitDescenders ){
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
for(i=1; i<pRow->nParent; i++){
if( hashFind(p, pRow->aParent[i])==0 ){
memmove(pRow->aParent+i, pRow->aParent+i+1,
sizeof(pRow->aParent[0])*(pRow->nParent-i-1));
pRow->nParent--;
if( i<pRow->nNonCherrypick ){
pRow->nNonCherrypick--;
}else{
pRow->nCherrypick--;
}
i--;
}
}
}
}
/* If the primary parent is in a different branch, but there are
** other parents in the same branch, reorder the parents to make
** the parent from the same branch the primary parent.
*/
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
if( pRow->isDup ) continue;
if( pRow->nNonCherrypick<2 ) continue; /* Not a fork */
pParent = hashFind(p, pRow->aParent[0]);
if( pParent==0 ) continue; /* Parent off-screen */
if( pParent->zBranch==pRow->zBranch ) continue; /* Same branch */
for(i=1; i<pRow->nNonCherrypick; i++){
pParent = hashFind(p, pRow->aParent[i]);
if( pParent && pParent->zBranch==pRow->zBranch ){
int t = pRow->aParent[0];
pRow->aParent[0] = pRow->aParent[i];
pRow->aParent[i] = t;
break;
}
|
| ︙ | ︙ | |||
563 564 565 566 567 568 569 |
}
if( iMrail==-1 ){
iMrail = findFreeRail(p, pRow->idx, p->nRow, 0);
if( p->mxRail>=GR_MAX_RAIL ) return;
mergeRiserFrom[iMrail] = parentRid;
}
mask = BIT(iMrail);
| > > > > | | > | | | 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 |
}
if( iMrail==-1 ){
iMrail = findFreeRail(p, pRow->idx, p->nRow, 0);
if( p->mxRail>=GR_MAX_RAIL ) return;
mergeRiserFrom[iMrail] = parentRid;
}
mask = BIT(iMrail);
if( i>=pRow->nNonCherrypick ){
pRow->mergeIn[iMrail] = 2;
pRow->cherrypickDown |= mask;
}else{
pRow->mergeIn[iMrail] = 1;
pRow->mergeDown |= mask;
}
for(pLoop=pRow->pNext; pLoop; pLoop=pLoop->pNext){
pLoop->railInUse |= mask;
}
}else{
/* Merge from an on-screen node */
createMergeRiser(p, pDesc, pRow, i>=pRow->nNonCherrypick);
if( p->mxRail>=GR_MAX_RAIL ) return;
}
}
}
/*
** Insert merge rails from primaries to duplicates.
*/
if( hasDup ){
int dupRail;
int mxRail;
find_max_rail(p);
mxRail = p->mxRail;
dupRail = mxRail+1;
if( p->mxRail>=GR_MAX_RAIL ) return;
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
if( !pRow->isDup ) continue;
pRow->iRail = dupRail;
pDesc = hashFind(p, pRow->rid);
assert( pDesc!=0 && pDesc!=pRow );
createMergeRiser(p, pDesc, pRow, 0);
if( pDesc->mergeOut>mxRail ) mxRail = pDesc->mergeOut;
}
if( dupRail<=mxRail ){
dupRail = mxRail+1;
for(pRow=p->pFirst; pRow; pRow=pRow->pNext){
if( pRow->isDup ) pRow->iRail = dupRail;
}
|
| ︙ | ︙ |
Changes to src/graph.js.
| ︙ | ︙ | |||
19 20 21 22 23 24 25 | ** "rowinfo": ROWINFO-ARRAY } ** ** The rowinfo field is an array of structures, one per entry in the timeline, ** where each structure has the following fields: ** ** id: The id of the <div> element for the row. This is an integer. ** to get an actual id, prepend "m" to the integer. The top node | | | | | | | > | > > > | > > | > > | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
** "rowinfo": ROWINFO-ARRAY }
**
** The rowinfo field is an array of structures, one per entry in the timeline,
** where each structure has the following fields:
**
** id: The id of the <div> element for the row. This is an integer.
** to get an actual id, prepend "m" to the integer. The top node
** is iTopRow and numbers increase moving down the timeline.
** bg: The background color for this row
** r: The "rail" that the node for this row sits on. The left-most
** rail is 0 and the number increases to the right.
** d: If exists and true then there is a "descender" - an arrow
** coming from the bottom of the page straight up to this node.
** mo: "merge-out". If it exists, this is the rail position
** for the upward portion of a merge arrow. The merge arrow goes as
** a solid normal merge line up to the row identified by "mu" and
** then as a dashed cherrypick merge line up further to "cu".
** If this value is omitted if there are no merge children.
** mu: The id of the row which is the top of the merge-out arrow.
** Only exists if "mo" exists.
** cu: Extend the mu merge arrow up to this row as a cherrypick
** merge line, if this value exists.
** u: Draw a thick child-line out of the top of this node and up to
** the node with an id equal to this value. 0 if it is straight to
** the top of the page, -1 if there is no thick-line riser.
** f: 0x01: a leaf node.
** au: An array of integers that define thick-line risers for branches.
** The integers are in pairs. For each pair, the first integer is
** is the rail on which the riser should run and the second integer
** is the id of the node upto which the riser should run. If there
** are no risers, this array does not exist.
** mi: "merge-in". An array of integer rail positions from which
** merge arrows should be drawn into this node. If the value is
** negative, then the rail position is the absolute value of mi[]
** and a thin merge-arrow descender is drawn to the bottom of
** the screen. This array is omitted if there are no inbound
** merges.
** ci: "cherrypick-in". Like "mi" except for cherrypick merges.
** omitted if there are no cherrypick merges.
** h: The artifact hash of the object being graphed
*/
var amendCssOnce = 1; // Only change the CSS one time
function amendCss(circleNodes,showArrowheads){
if( !amendCssOnce ) return;
var css = "";
if( circleNodes ){
|
| ︙ | ︙ | |||
81 82 83 84 85 86 87 |
canvasDiv.className = "tl-canvas";
canvasDiv.style.position = "absolute";
parent.appendChild(canvasDiv);
var elems = {};
var elemClasses = [
"rail", "mergeoffset", "node", "arrow u", "arrow u sm", "line",
| | > > | 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 |
canvasDiv.className = "tl-canvas";
canvasDiv.style.position = "absolute";
parent.appendChild(canvasDiv);
var elems = {};
var elemClasses = [
"rail", "mergeoffset", "node", "arrow u", "arrow u sm", "line",
"arrow merge r", "line merge", "arrow warp", "line warp",
"line cherrypick"
];
for( var i=0; i<elemClasses.length; i++ ){
var cls = elemClasses[i];
var elem = document.createElement("div");
elem.className = "tl-" + cls;
if( cls.indexOf("line")==0 ) elem.className += " v";
canvasDiv.appendChild(elem);
var k = cls.replace(/\s/g, "_");
var r = elem.getBoundingClientRect();
var w = Math.round(r.right - r.left);
var h = Math.round(r.bottom - r.top);
elems[k] = {w: w, h: h, cls: cls};
}
node = elems.node;
arrow = elems.arrow_u;
arrowSmall = elems.arrow_u_sm;
line = elems.line;
mArrow = elems.arrow_merge_r;
mLine = elems.line_merge;
cpLine = elems.line_cherrypick;
wArrow = elems.arrow_warp;
wLine = elems.line_warp;
var minRailPitch = Math.ceil((node.w+line.w)/2 + mArrow.w + 1);
if( window.innerWidth<400 ){
railPitch = minRailPitch;
}else{
|
| ︙ | ︙ | |||
184 185 186 187 188 189 190 191 192 193 |
var y0 = from.y + node.h/2;
var y1 = Math.ceil(to.y + node.h + arw.h/2);
drawLine(line,color,x,y0,null,y1);
x = to.x + (node.w-arw.w)/2;
var n = drawBox(arw.cls,null,x,y);
if(color) n.style.borderBottomColor = color;
}
function drawMergeLine(x0,y0,x1,y1){
drawLine(mLine,null,x0,y0,x1,y1);
}
| > > > > > > | < > > > > > > | > | | | > > > > | | > > > > > > > | > > > > > > > | | | | | | | | | | | | | | | | | | | | | | | | > > | | | | | | > | > | | > > > > > > > > > > > > > > > > > | 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 |
var y0 = from.y + node.h/2;
var y1 = Math.ceil(to.y + node.h + arw.h/2);
drawLine(line,color,x,y0,null,y1);
x = to.x + (node.w-arw.w)/2;
var n = drawBox(arw.cls,null,x,y);
if(color) n.style.borderBottomColor = color;
}
/* Draw thin horizontal or vertical lines representing merges */
function drawMergeLine(x0,y0,x1,y1){
drawLine(mLine,null,x0,y0,x1,y1);
}
function drawCherrypickLine(x0,y0,x1,y1){
drawLine(cpLine,null,x0,y0,x1,y1);
}
/* Draw an arrow representing an in-bound merge from the "rail"-th rail
** over to the node of "p". Make is a checkpoint merge is "isCP" is true */
function drawMergeArrow(p,rail,isCP){
var x0 = rail*railPitch + node.w/2;
if( rail in mergeLines ){
x0 += mergeLines[rail];
if( p.r<rail ) x0 += mLine.w;
}else{
x0 += (p.r<rail ? -1 : 1)*line.w/2;
}
var x1 = mArrow.w ? mArrow.w/2 : -node.w/2;
x1 = p.x + (p.r<rail ? node.w + Math.ceil(x1) : -x1);
var y = miLineY(p);
var x = p.x + (p.r<rail ? node.w : -mArrow.w);
var cls;
if( isCP ){
drawCherrypickLine(x0,y,x1,null);
cls = "arrow cherrypick " + (p.r<rail ? "l" : "r");
}else{
drawMergeLine(x0,y,x1,null);
cls = "arrow merge " + (p.r<rail ? "l" : "r");
}
drawBox(cls,null,x,y+(mLine.w-mArrow.h)/2);
}
function drawNode(p, btm){
if( p.bg ){
var e = document.getElementById("mc"+p.id);
if(e) e.style.backgroundColor = p.bg;
e = document.getElementById("md"+p.id);
if(e) e.style.backgroundColor = p.bg;
}
if( p.r<0 ) return;
if( p.u>0 ) drawUpArrow(p,tx.rowinfo[p.u-tx.iTopRow],p.fg);
var cls = node.cls;
if( p.hasOwnProperty('mi') && p.mi.length ) cls += " merge";
if( p.f&1 ) cls += " leaf";
var n = drawBox(cls,p.bg,p.x,p.y);
n.id = "tln"+p.id;
n.onclick = clickOnNode;
n.style.zIndex = 10;
if( !tx.omitDescenders ){
if( p.u==0 ) drawUpArrow(p,{x: p.x, y: -node.h},p.fg);
if( p.hasOwnProperty('d') ) drawUpArrow({x: p.x, y: btm-node.h/2},p,p.fg);
}
if( p.hasOwnProperty('mo') ){
var x0 = p.x + node.w/2;
var x1 = p.mo*railPitch + node.w/2;
var u = tx.rowinfo[p.mu-tx.iTopRow];
var y1 = miLineY(u);
if( p.u<0 || p.mo!=p.r ){
x1 += mergeLines[p.mo] = -mLine.w/2;
var y0 = p.y+2;
if( p.mu==p.id ){
drawCherrypickLine(x0,y0,x1+(x0<x1 ? mLine.w : 0),null);
y1 = y0;
}else{
drawMergeLine(x0,y0,x1+(x0<x1 ? mLine.w : 0),null);
drawMergeLine(x1,y0+mLine.w,null,y1);
}
if( p.hasOwnProperty('cu') ){
var u2 = tx.rowinfo[p.cu-tx.iTopRow];
var y2 = miLineY(u2);
drawCherrypickLine(x1,y1,null,y2);
}
}else if( mergeOffset ){
mergeLines[p.mo] = u.r<p.r ? -mergeOffset-mLine.w : mergeOffset;
x1 += mergeLines[p.mo];
if( p.mo<p.id ){
drawMergeLine(x1,p.y+node.h/2,null,y1);
}
if( p.hasOwnProperty('cu') ){
var u2 = tx.rowinfo[p.cu-tx.iTopRow];
var y2 = miLineY(u2);
drawCherrypickLine(x1,y1,null,y2);
}
}else{
delete mergeLines[p.mo];
}
}
if( p.hasOwnProperty('au') ){
for( var i=0; i<p.au.length; i+=2 ){
var rail = p.au[i];
var x0 = p.x + node.w/2;
var x1 = rail*railPitch + (node.w-line.w)/2;
if( x0<x1 ){
x0 = Math.ceil(x0);
x1 += line.w;
}
var y0 = p.y + (node.h-line.w)/2;
var u = tx.rowinfo[p.au[i+1]-tx.iTopRow];
if( u.id<p.id ){
drawLine(line,u.fg,x0,y0,x1,null);
drawUpArrow(p,u,u.fg);
}else{
var y1 = u.y + (node.h-line.w)/2;
drawLine(wLine,u.fg,x0,y0,x1,null);
drawLine(wLine,u.fg,x1-line.w,y0,null,y1+line.w);
drawLine(wLine,u.fg,x1,y1,u.x-wArrow.w/2,null);
var x = u.x-wArrow.w;
var y = u.y+(node.h-wArrow.h)/2;
var n = drawBox(wArrow.cls,null,x,y);
if( u.fg ) n.style.borderLeftColor = u.fg;
}
}
}
if( p.hasOwnProperty('mi') ){
for( var i=0; i<p.mi.length; i++ ){
var rail = p.mi[i];
if( rail<0 ){
rail = -rail;
mergeLines[rail] = -mLine.w/2;
var x = rail*railPitch + (node.w-mLine.w)/2;
var y = miLineY(p);
drawMergeLine(x,y,null,mergeBtm[rail]);
mergeBtm[rail] = y;
}
drawMergeArrow(p,rail,0);
}
}
if( p.hasOwnProperty('ci') ){
for( var i=0; i<p.ci.length; i++ ){
var rail = p.ci[i];
if( rail<0 ){
rail = -rail;
mergeLines[rail] = -mLine.w/2;
var x = rail*railPitch + (node.w-mLine.w)/2;
var y = miLineY(p);
drawCherrypickLine(x,y,null,mergeBtm[rail]);
mergeBtm[rail] = y;
}
drawMergeArrow(p,rail,1);
}
}
}
var mergeLines;
var mergeBtm = new Array;
function renderGraph(){
mergeLines = {};
canvasDiv.innerHTML = "";
var canvasY = absoluteY(canvasDiv);
for(var i=0; i<tx.rowinfo.length; i++ ){
var e = document.getElementById("m"+tx.rowinfo[i].id);
tx.rowinfo[i].y = absoluteY(e) - canvasY;
tx.rowinfo[i].x = tx.rowinfo[i].r*railPitch;
}
var tlBtm = document.querySelector(".timelineBottom");
if( tlBtm.offsetHeight<node.h ){
tlBtm.style.height = node.h + "px";
}
var btm = absoluteY(tlBtm) - canvasY + tlBtm.offsetHeight;
for( var i=0; i<tx.nrail; i++) mergeBtm[i] = btm;
for( var i=tx.rowinfo.length-1; i>=0; i-- ){
drawNode(tx.rowinfo[i], btm);
}
}
var selRow;
function clickOnNode(){
var p = tx.rowinfo[parseInt(this.id.match(/\d+$/)[0], 10)-tx.iTopRow];
|
| ︙ | ︙ | |||
388 389 390 391 392 393 394 395 396 397 398 399 400 401 |
lx = topObj.getElementsByClassName('timelineDateRow');
for(i=0; i<lx.length; i++){
var rx = lx[i];
if( rx.getAttribute('data-reordered') ) break;
rx.setAttribute('data-reordered',1);
rx.appendChild(rx.firstChild);
rx.insertBefore(rx.childNodes[1],rx.firstChild);
}
}
}
/* Look for all timeline-data-NN objects. Load each one and draw
** a graph for each one.
*/
| > > > > > > > | 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 |
lx = topObj.getElementsByClassName('timelineDateRow');
for(i=0; i<lx.length; i++){
var rx = lx[i];
if( rx.getAttribute('data-reordered') ) break;
rx.setAttribute('data-reordered',1);
rx.appendChild(rx.firstChild);
rx.insertBefore(rx.childNodes[1],rx.firstChild);
}
/* Do not show the HH:MM timestamps on very narrow displays
** as they take up too much horizontal space. */
lx = topObj.getElementsByClassName('timelineHistLink');
for(i=0; i<lx.length; i++){
var rx = lx[i];
rx.style.display="none";
}
}
}
/* Look for all timeline-data-NN objects. Load each one and draw
** a graph for each one.
*/
|
| ︙ | ︙ |
Changes to src/http.c.
| ︙ | ︙ | |||
106 107 108 109 110 111 112 |
blob_zero(pHdr);
i = strlen(g.url.path);
if( i>0 && g.url.path[i-1]=='/' ){
zSep = "";
}else{
zSep = "/";
}
| | | 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
blob_zero(pHdr);
i = strlen(g.url.path);
if( i>0 && g.url.path[i-1]=='/' ){
zSep = "";
}else{
zSep = "/";
}
blob_appendf(pHdr, "POST %s%s HTTP/1.0\r\n", g.url.path, zSep);
if( g.url.proxyAuth ){
blob_appendf(pHdr, "Proxy-Authorization: %s\r\n", g.url.proxyAuth);
}
if( g.zHttpAuth && g.zHttpAuth[0] ){
const char *zCredentials = g.zHttpAuth;
char *zEncoded = encode64(zCredentials, -1);
blob_appendf(pHdr, "Authorization: Basic %s\r\n", zEncoded);
|
| ︙ | ︙ | |||
343 344 345 346 347 348 349 350 351 352 353 354 355 356 |
fossil_print("redirect with status %d to %s\n", rc, &zLine[i]);
url_parse(&zLine[i], 0);
transport_close(&g.url);
transport_global_shutdown(&g.url);
fSeenHttpAuth = 0;
if( g.zHttpAuth ) free(g.zHttpAuth);
g.zHttpAuth = get_httpauth();
return http_exchange(pSend, pReply, useLogin, maxRedirect);
}else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){
if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){
isCompressed = 0;
}else if( fossil_strnicmp(&zLine[14],
"application/x-fossil-uncompressed", -1)==0 ){
isCompressed = 0;
| > | 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 |
fossil_print("redirect with status %d to %s\n", rc, &zLine[i]);
url_parse(&zLine[i], 0);
transport_close(&g.url);
transport_global_shutdown(&g.url);
fSeenHttpAuth = 0;
if( g.zHttpAuth ) free(g.zHttpAuth);
g.zHttpAuth = get_httpauth();
url_remember();
return http_exchange(pSend, pReply, useLogin, maxRedirect);
}else if( fossil_strnicmp(zLine, "content-type: ", 14)==0 ){
if( fossil_strnicmp(&zLine[14], "application/x-fossil-debug", -1)==0 ){
isCompressed = 0;
}else if( fossil_strnicmp(&zLine[14],
"application/x-fossil-uncompressed", -1)==0 ){
isCompressed = 0;
|
| ︙ | ︙ |
Changes to src/http_ssl.c.
| ︙ | ︙ | |||
385 386 387 388 389 390 391 392 |
}
/* Set the Global.zIpAddr variable to the server we are talking to.
** This is used to populate the ipaddr column of the rcvfrom table,
** if any files are received from the server.
*/
{
/* IPv4 only code */
| > > > > > | > > | 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 |
}
/* Set the Global.zIpAddr variable to the server we are talking to.
** This is used to populate the ipaddr column of the rcvfrom table,
** if any files are received from the server.
*/
{
#ifdef HAVE_BIO_ADDR_HOSTNAME_STRING
char *ip = BIO_ADDR_hostname_string(BIO_get_conn_address(iBio),1);
g.zIpAddr = mprintf("%s", ip);
OPENSSL_free(ip);
#else
/* IPv4 only code */
const unsigned char *ip;
ip = (const unsigned char*)BIO_ptr_ctrl(iBio,BIO_C_GET_CONNECT,2);
g.zIpAddr = mprintf("%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
#endif
}
X509_free(cert);
return 0;
}
/*
|
| ︙ | ︙ |
Changes to src/info.c.
| ︙ | ︙ | |||
115 116 117 118 119 120 121 |
zTags = info_tags_of_checkin(rid, 0);
if( zTags && zTags[0] ){
fossil_print("tags: %s\n", zTags);
}
free(zTags);
if( zComment ){
fossil_print("comment: ");
| | | 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
zTags = info_tags_of_checkin(rid, 0);
if( zTags && zTags[0] ){
fossil_print("tags: %s\n", zTags);
}
free(zTags);
if( zComment ){
fossil_print("comment: ");
comment_print(zComment, 0, 14, -1, get_comment_format());
free(zComment);
}
}
/*
** Print information about the URLs used to access a repository and
** checkouts in a repository.
|
| ︙ | ︙ | |||
200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
i64 fsize;
int verboseFlag = find_option("verbose","v",0)!=0;
if( !verboseFlag ){
verboseFlag = find_option("detail","l",0)!=0; /* deprecated */
}
if( g.argc==3
&& (fsize = file_size(g.argv[2], ExtFILE))>0
&& (fsize&0x1ff)==0
){
db_open_config(0, 0);
db_open_repository(g.argv[2]);
db_record_repository_filename(g.argv[2]);
fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>"));
| > | 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
i64 fsize;
int verboseFlag = find_option("verbose","v",0)!=0;
if( !verboseFlag ){
verboseFlag = find_option("detail","l",0)!=0; /* deprecated */
}
if( g.argc==3
&& file_isfile(g.argv[2], ExtFILE)
&& (fsize = file_size(g.argv[2], ExtFILE))>0
&& (fsize&0x1ff)==0
){
db_open_config(0, 0);
db_open_repository(g.argv[2]);
db_record_repository_filename(g.argv[2]);
fossil_print("project-name: %s\n", db_get("project-name", "<unnamed>"));
|
| ︙ | ︙ | |||
265 266 267 268 269 270 271 272 273 274 |
"INSERT OR IGNORE INTO ok SELECT pid FROM plink WHERE cid=%d;",
rid, rid
);
if( !parentsOnly ){
db_multi_exec(
"INSERT OR IGNORE INTO ok SELECT cid FROM plink WHERE pid=%d;", rid
);
}
blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
db_prepare(&q, "%s", blob_sql_text(&sql));
| > > > > > > > > > | > > > > | | 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 |
"INSERT OR IGNORE INTO ok SELECT pid FROM plink WHERE cid=%d;",
rid, rid
);
if( !parentsOnly ){
db_multi_exec(
"INSERT OR IGNORE INTO ok SELECT cid FROM plink WHERE pid=%d;", rid
);
if( db_table_exists("repository","cherrypick") ){
db_multi_exec(
"INSERT OR IGNORE INTO ok "
" SELECT parentid FROM cherrypick WHERE childid=%d;"
"INSERT OR IGNORE INTO ok "
" SELECT childid FROM cherrypick WHERE parentid=%d;",
rid, rid
);
}
}
blob_append_sql(&sql, " AND event.objid IN ok ORDER BY mtime DESC");
db_prepare(&q, "%s", blob_sql_text(&sql));
www_print_timeline(&q,
TIMELINE_DISJOINT
|TIMELINE_GRAPH
|TIMELINE_NOSCROLL
|TIMELINE_CHPICK,
0, 0, rid, 0);
db_finalize(&q);
}
/*
** Show a graph all wiki, tickets, and check-ins that refer to object zUuid.
**
** If zLabel is not NULL and the graph is not empty, then output zLabel as
|
| ︙ | ︙ | |||
610 611 612 613 614 615 616 | db_finalize(&q); style_footer(); } /* ** WEBPAGE: vinfo ** WEBPAGE: ci | | | | | | | | < | 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 |
db_finalize(&q);
style_footer();
}
/*
** WEBPAGE: vinfo
** WEBPAGE: ci
** URL: /ci/ARTIFACTID
** OR: /ci?name=ARTIFACTID
**
** Display information about a particular check-in. The exact
** same information is shown on the /info page if the name query
** parameter to /info describes a check-in.
**
** The ARTIFACTID can be a unique prefix for the HASH of the check-in,
** or a tag or branch name that identifies the check-in.
*/
void ci_page(void){
Stmt q1, q2, q3;
int rid;
int isLeaf;
int diffType; /* 0: no diff, 1: unified, 2: side-by-side */
u64 diffFlags; /* Flag parameter for text_diff() */
|
| ︙ | ︙ | |||
675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 |
int nUuid = db_column_bytes(&q1, 0);
char *zEUser, *zEComment;
const char *zUser;
const char *zOrigUser;
const char *zComment;
const char *zDate;
const char *zOrigDate;
style_header("Check-in [%S]", zUuid);
login_anonymous_available();
zEUser = db_text(0,
"SELECT value FROM tagxref"
" WHERE tagid=%d AND rid=%d AND tagtype>0",
TAG_USER, rid);
zEComment = db_text(0,
"SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
TAG_COMMENT, rid);
zOrigUser = db_column_text(&q1, 2);
zUser = zEUser ? zEUser : zOrigUser;
zComment = db_column_text(&q1, 3);
zDate = db_column_text(&q1,1);
zOrigDate = db_column_text(&q1, 4);
if( zOrigDate==0 ) zOrigDate = zDate;
@ <div class="section">Overview</div>
| > > > > > > > | 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 |
int nUuid = db_column_bytes(&q1, 0);
char *zEUser, *zEComment;
const char *zUser;
const char *zOrigUser;
const char *zComment;
const char *zDate;
const char *zOrigDate;
const char *zBrName;
int okWiki = 0;
Blob wiki_read_links = BLOB_INITIALIZER;
Blob wiki_add_links = BLOB_INITIALIZER;
style_header("Check-in [%S]", zUuid);
login_anonymous_available();
zEUser = db_text(0,
"SELECT value FROM tagxref"
" WHERE tagid=%d AND rid=%d AND tagtype>0",
TAG_USER, rid);
zEComment = db_text(0,
"SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
TAG_COMMENT, rid);
zBrName = db_text(0,
"SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
TAG_BRANCH, rid);
zOrigUser = db_column_text(&q1, 2);
zUser = zEUser ? zEUser : zOrigUser;
zComment = db_column_text(&q1, 3);
zDate = db_column_text(&q1,1);
zOrigDate = db_column_text(&q1, 4);
if( zOrigDate==0 ) zOrigDate = zDate;
@ <div class="section">Overview</div>
|
| ︙ | ︙ | |||
739 740 741 742 743 744 745 |
}
db_prepare(&q2,"SELECT substr(tag.tagname,5) FROM tagxref, tag "
" WHERE rid=%d AND tagtype>0 "
" AND tag.tagid=tagxref.tagid "
" AND +tag.tagname GLOB 'sym-*'", rid);
while( db_step(&q2)==SQLITE_ROW ){
const char *zTagName = db_column_text(&q2, 0);
| > | > > > > > > > > > > > > > > > > > | | 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 |
}
db_prepare(&q2,"SELECT substr(tag.tagname,5) FROM tagxref, tag "
" WHERE rid=%d AND tagtype>0 "
" AND tag.tagid=tagxref.tagid "
" AND +tag.tagname GLOB 'sym-*'", rid);
while( db_step(&q2)==SQLITE_ROW ){
const char *zTagName = db_column_text(&q2, 0);
if( fossil_strcmp(zTagName,zBrName)==0 ){
@ | %z(href("%R/timeline?r=%T&unhide",zTagName))%h(zTagName)</a>
if( wiki_tagid2("branch",zTagName)!=0 ){
blob_appendf(&wiki_read_links, " | %z%h</a>",
href("%R/wiki?name=branch/%h",zTagName), zTagName);
}else if( g.perm.Write && g.perm.WrWiki ){
blob_appendf(&wiki_add_links, " | %z%h</a>",
href("%R/wikiedit?name=branch/%h",zTagName), zTagName);
}
}else{
@ | %z(href("%R/timeline?t=%T&unhide",zTagName))%h(zTagName)</a>
if( wiki_tagid2("tag",zTagName)!=0 ){
blob_appendf(&wiki_read_links, " | %z%h</a>",
href("%R/wiki?name=tag/%h",zTagName), zTagName);
}else if( g.perm.Write && g.perm.WrWiki ){
blob_appendf(&wiki_add_links, " | %z%h</a>",
href("%R/wikiedit?name=tag/%h",zTagName), zTagName);
}
}
}
db_finalize(&q2);
@ </td></tr>
@ <tr><th>Files:</th>
@ <td>
@ %z(href("%R/tree?ci=%!S",zUuid))files</a>
@ | %z(href("%R/fileage?name=%!S",zUuid))file ages</a>
@ | %z(href("%R/tree?nofiles&type=tree&ci=%!S",zUuid))folders</a>
@ </td>
@ </tr>
@ <tr><th>%s(hname_alg(nUuid)):</th><td>%.32s(zUuid)<wbr>%s(zUuid+32)
if( g.perm.Setup ){
@ (Record ID: %d(rid))
}
@ </td></tr>
@ <tr><th>User & Date:</th><td>
hyperlink_to_user(zUser,zDate," on ");
hyperlink_to_date(zDate, "</td></tr>");
|
| ︙ | ︙ | |||
788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 |
const char *zDate = db_column_text(&q2, 2);
if( zUser==0 || zUser[0]==0 ) zUser = "unknown";
@ <tr><th>Received From:</th>
@ <td>%h(zUser) @ %h(zIpAddr) on %s(zDate)</td></tr>
}
db_finalize(&q2);
}
if( g.perm.Hyperlink ){
@ <tr><th>Other Links:</th>
@ <td>
@ %z(href("%R/artifact/%!S",zUuid))manifest</a>
@ | %z(href("%R/ci_tags/%!S",zUuid))tags</a>
if( g.perm.Admin ){
@ | %z(href("%R/mlink?ci=%!S",zUuid))mlink table</a>
}
if( g.anon.Write ){
@ | %z(href("%R/ci_edit?r=%!S",zUuid))edit</a>
}
@ </td>
@ </tr>
}
@ </table>
}else{
style_header("Check-in Information");
login_anonymous_available();
}
db_finalize(&q1);
render_backlink_graph(zUuid, "<div class=\"section\">References</div>\n");
@ <div class="section">Context</div>
render_checkin_context(rid, 0);
@ <div class="section">Changes</div>
@ <div class="sectionmenu">
diffFlags = construct_diff_flags(diffType);
zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 |
const char *zDate = db_column_text(&q2, 2);
if( zUser==0 || zUser[0]==0 ) zUser = "unknown";
@ <tr><th>Received From:</th>
@ <td>%h(zUser) @ %h(zIpAddr) on %s(zDate)</td></tr>
}
db_finalize(&q2);
}
/* Only show links to read wiki pages if the users can read wiki
** and if the wiki pages already exist */
if( g.perm.RdWiki
&& ((okWiki = wiki_tagid2("checkin",zUuid))!=0 ||
blob_size(&wiki_read_links)>0)
&& db_get_boolean("wiki-about",1)
){
const char *zLinks = blob_str(&wiki_read_links);
@ <tr><th>Wiki:</th><td>\
if( okWiki ){
@ %z(href("%R/wiki?name=checkin/%s",zUuid))this checkin</a>\
}else if( zLinks[0] ){
zLinks += 3;
}
@ %s(zLinks)</td></tr>
}
/* Only show links to create new wiki pages if the users can write wiki
** and if the wiki pages do not already exist */
if( g.perm.WrWiki
&& g.perm.RdWiki
&& g.perm.Write
&& (blob_size(&wiki_add_links)>0 || !okWiki)
&& db_get_boolean("wiki-about",1)
){
const char *zLinks = blob_str(&wiki_add_links);
@ <tr><th>Add Wiki:</th><td>\
if( !okWiki ){
@ %z(href("%R/wikiedit?name=checkin/%s",zUuid))this checkin</a>\
}else if( zLinks[0] ){
zLinks += 3;
}
@ %s(zLinks)</td></tr>
}
if( g.perm.Hyperlink ){
@ <tr><th>Other Links:</th>
@ <td>
@ %z(href("%R/artifact/%!S",zUuid))manifest</a>
@ | %z(href("%R/ci_tags/%!S",zUuid))tags</a>
if( g.perm.Admin ){
@ | %z(href("%R/mlink?ci=%!S",zUuid))mlink table</a>
}
if( g.anon.Write ){
@ | %z(href("%R/ci_edit?r=%!S",zUuid))edit</a>
}
@ </td>
@ </tr>
}
@ </table>
blob_reset(&wiki_read_links);
blob_reset(&wiki_add_links);
}else{
style_header("Check-in Information");
login_anonymous_available();
}
db_finalize(&q1);
if( !PB("nowiki") ){
wiki_render_associated("checkin", zUuid, 0);
}
render_backlink_graph(zUuid, "<div class=\"section\">References</div>\n");
@ <div class="section">Context</div>
render_checkin_context(rid, 0);
@ <div class="section">Changes</div>
@ <div class="sectionmenu">
diffFlags = construct_diff_flags(diffType);
zW = (diffFlags&DIFF_IGNORE_ALLWS)?"&w":"";
|
| ︙ | ︙ | |||
889 890 891 892 893 894 895 896 897 898 899 900 901 902 |
int rid;
Manifest *pWiki;
char *zUuid;
char *zDate;
Blob wiki;
int modPending;
const char *zModAction;
login_check_credentials();
if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
rid = name_to_rid_www("name");
if( rid==0 || (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))==0 ){
style_header("Wiki Page Information Error");
@ No such object: %h(P("name"))
| > > | 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 |
int rid;
Manifest *pWiki;
char *zUuid;
char *zDate;
Blob wiki;
int modPending;
const char *zModAction;
int tagid;
int ridNext;
login_check_credentials();
if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
rid = name_to_rid_www("name");
if( rid==0 || (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))==0 ){
style_header("Wiki Page Information Error");
@ No such object: %h(P("name"))
|
| ︙ | ︙ | |||
935 936 937 938 939 940 941 |
@ <tr><th>Artifact ID:</th>
@ <td>%z(href("%R/artifact/%!S",zUuid))%s(zUuid)</a>
if( g.perm.Setup ){
@ (%d(rid))
}
modPending = moderation_pending_www(rid);
@ </td></tr>
| | > > > > > > > > > | 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 |
@ <tr><th>Artifact ID:</th>
@ <td>%z(href("%R/artifact/%!S",zUuid))%s(zUuid)</a>
if( g.perm.Setup ){
@ (%d(rid))
}
modPending = moderation_pending_www(rid);
@ </td></tr>
@ <tr><th>Page Name:</th>\
@ <td>%z(href("%R/whistory?name=%h",pWiki->zWikiTitle))\
@ %h(pWiki->zWikiTitle)</a></td></tr>
@ <tr><th>Date:</th><td>
hyperlink_to_date(zDate, "</td></tr>");
@ <tr><th>Original User:</th><td>
hyperlink_to_user(pWiki->zUser, zDate, "</td></tr>");
if( pWiki->zMimetype ){
@ <tr><th>Mimetype:</th><td>%h(pWiki->zMimetype)</td></tr>
}
if( pWiki->nParent>0 ){
int i;
@ <tr><th>Parent%s(pWiki->nParent==1?"":"s"):</th><td>
for(i=0; i<pWiki->nParent; i++){
char *zParent = pWiki->azParent[i];
@ %z(href("info/%!S",zParent))%s(zParent)</a>
@ %z(href("%R/wdiff?id=%!S&pid=%!S",zUuid,zParent))(diff)</a>
}
@ </td></tr>
}
tagid = wiki_tagid(pWiki->zWikiTitle);
if( tagid>0 && (ridNext = wiki_next(tagid, pWiki->rDate))>0 ){
char *zId = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", ridNext);
@ <tr><th>Next</th>
@ <td>%z(href("%R/info/%!S",zId))%s(zId)</a></td>
}
@ </table>
if( g.perm.ModWiki && modPending ){
@ <div class="section">Moderation</div>
@ <blockquote>
@ <form method="POST" action="%R/winfo/%s(zUuid)">
@ <label><input type="radio" name="modaction" value="delete">
|
| ︙ | ︙ | |||
1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 | #define OBJTYPE_FORUM 0x0200 /* ** Possible flags for the second parameter to ** object_description() */ #define OBJDESC_DETAIL 0x0001 /* more detail */ #endif /* ** Write a description of an object to the www reply. ** ** If the object is a file then mention: ** | > | 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 | #define OBJTYPE_FORUM 0x0200 /* ** Possible flags for the second parameter to ** object_description() */ #define OBJDESC_DETAIL 0x0001 /* more detail */ #define OBJDESC_BASE 0x0002 /* Set <base> using this object */ #endif /* ** Write a description of an object to the www reply. ** ** If the object is a file then mention: ** |
| ︙ | ︙ | |||
1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 |
Stmt q;
int cnt = 0;
int nWiki = 0;
int objType = 0;
char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
int showDetail = (objdescFlags & OBJDESC_DETAIL)!=0;
char *prevName = 0;
db_prepare(&q,
"SELECT filename.name, datetime(event.mtime,toLocal()),"
" coalesce(event.ecomment,event.comment),"
" coalesce(event.euser,event.user),"
" b.uuid, mlink.mperm,"
" coalesce((SELECT value FROM tagxref"
| > | 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 |
Stmt q;
int cnt = 0;
int nWiki = 0;
int objType = 0;
char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
int showDetail = (objdescFlags & OBJDESC_DETAIL)!=0;
char *prevName = 0;
int bNeedBase = (objdescFlags & OBJDESC_BASE)!=0;
db_prepare(&q,
"SELECT filename.name, datetime(event.mtime,toLocal()),"
" coalesce(event.ecomment,event.comment),"
" coalesce(event.euser,event.user),"
" b.uuid, mlink.mperm,"
" coalesce((SELECT value FROM tagxref"
|
| ︙ | ︙ | |||
1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 |
@ <li>Symbolic link
objType |= OBJTYPE_SYMLINK;
}else if( mPerm==PERM_EXE ){
@ <li>Executable file
objType |= OBJTYPE_EXE;
}else{
@ <li>File
}
objType |= OBJTYPE_CONTENT;
@ %z(href("%R/finfo?name=%T&m=%!S",zName,zUuid))%h(zName)</a>
tag_private_status(rid);
if( showDetail ){
@ <ul>
}
| > > > > | 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 |
@ <li>Symbolic link
objType |= OBJTYPE_SYMLINK;
}else if( mPerm==PERM_EXE ){
@ <li>Executable file
objType |= OBJTYPE_EXE;
}else{
@ <li>File
if( bNeedBase ){
bNeedBase = 0;
style_set_current_page("doc/%S/%s",zVers,zName);
}
}
objType |= OBJTYPE_CONTENT;
@ %z(href("%R/finfo?name=%T&m=%!S",zName,zUuid))%h(zName)</a>
tag_private_status(rid);
if( showDetail ){
@ <ul>
}
|
| ︙ | ︙ | |||
1658 1659 1660 1661 1662 1663 1664 |
**
** Return the uninterpreted content of an artifact. Used primarily
** to view artifacts that are images.
*/
void rawartifact_page(void){
int rid = 0;
char *zUuid;
| < < > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 |
**
** Return the uninterpreted content of an artifact. Used primarily
** to view artifacts that are images.
*/
void rawartifact_page(void){
int rid = 0;
char *zUuid;
if( P("ci") && P("filename") ){
rid = artifact_from_ci_and_filename(0, 0);
}
if( rid==0 ){
rid = name_to_rid_www("name");
}
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
if( rid==0 ) fossil_redirect_home();
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
if( fossil_strcmp(P("name"), zUuid)==0 && login_is_nobody() ){
g.isConst = 1;
}
free(zUuid);
deliver_artifact(rid, P("m"));
}
/*
** WEBPAGE: secureraw
** URL: /secureraw/HASH?m=TYPE
**
** Return the uninterpreted content of an artifact. This is similar
** to /raw except in this case the only way to specify the artifact
** is by the full-length SHA1 or SHA3 hash. Abbreviations are not
** accepted.
*/
void secure_rawartifact_page(void){
int rid = 0;
const char *zUuid = PD("name", "");
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zUuid);
if( rid==0 ){
cgi_set_status(404, "Not Found");
@ Unknown artifact: "%h(zUuid)"
return;
}
g.isConst = 1;
deliver_artifact(rid, P("m"));
}
/*
** Generate a verbatim artifact as the result of an HTTP request.
** If zMime is not NULL, use it as the MIME-type. If zMime is
** NULL, guess at the MIME-type based on the filename
** associated with the artifact.
*/
void deliver_artifact(int rid, const char *zMime){
Blob content;
if( zMime==0 ){
char *zFName = db_text(0, "SELECT filename.name FROM mlink, filename"
" WHERE mlink.fid=%d"
" AND filename.fnid=mlink.fnid", rid);
if( !zFName ){
/* Look also at the attachment table */
zFName = db_text(0, "SELECT attachment.filename FROM attachment, blob"
|
| ︙ | ︙ | |||
1976 1977 1978 1979 1980 1981 1982 | const char *zMime; Blob downloadName; int renderAsWiki = 0; int renderAsHtml = 0; int objType; int asText; const char *zUuid; | | | 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 |
const char *zMime;
Blob downloadName;
int renderAsWiki = 0;
int renderAsHtml = 0;
int objType;
int asText;
const char *zUuid;
u32 objdescFlags = OBJDESC_BASE;
int descOnly = fossil_strcmp(g.zPath,"whatis")==0;
int isFile = fossil_strcmp(g.zPath,"file")==0;
const char *zLn = P("ln");
const char *zName = P("name");
HQuery url;
url_initialize(&url, g.zPath);
|
| ︙ | ︙ | |||
2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 |
style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
}else if( g.perm.Setup ){
@ <h2>Artifact %s(zUuid) (%d(rid)):</h2>
}else{
@ <h2>Artifact %s(zUuid):</h2>
}
blob_zero(&downloadName);
objType = object_description(rid, objdescFlags, &downloadName);
if( !descOnly && P("download")!=0 ){
cgi_redirectf("%R/raw/%T?name=%s", blob_str(&downloadName),
db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid));
/*NOTREACHED*/
}
if( g.perm.Admin ){
| > > | 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 |
style_submenu_element("Artifact", "%R/artifact/%S", zUuid);
}else if( g.perm.Setup ){
@ <h2>Artifact %s(zUuid) (%d(rid)):</h2>
}else{
@ <h2>Artifact %s(zUuid):</h2>
}
blob_zero(&downloadName);
asText = P("txt")!=0;
if( asText ) objdescFlags &= ~OBJDESC_BASE;
objType = object_description(rid, objdescFlags, &downloadName);
if( !descOnly && P("download")!=0 ){
cgi_redirectf("%R/raw/%T?name=%s", blob_str(&downloadName),
db_text("?", "SELECT uuid FROM blob WHERE rid=%d", rid));
/*NOTREACHED*/
}
if( g.perm.Admin ){
|
| ︙ | ︙ | |||
2091 2092 2093 2094 2095 2096 2097 |
db_finalize(&q);
}
style_submenu_element("Download", "%R/raw/%T?name=%s",
blob_str(&downloadName), zUuid);
if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){
style_submenu_element("Check-ins Using", "%R/timeline?n=200&uf=%s", zUuid);
}
| < | 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 |
db_finalize(&q);
}
style_submenu_element("Download", "%R/raw/%T?name=%s",
blob_str(&downloadName), zUuid);
if( db_exists("SELECT 1 FROM mlink WHERE fid=%d", rid) ){
style_submenu_element("Check-ins Using", "%R/timeline?n=200&uf=%s", zUuid);
}
zMime = mimetype_from_name(blob_str(&downloadName));
if( zMime ){
if( fossil_strcmp(zMime, "text/html")==0 ){
if( asText ){
style_submenu_element("Html", "%s", url_render(&url, "txt", 0, 0, 0));
}else{
renderAsHtml = 1;
|
| ︙ | ︙ | |||
2117 2118 2119 2120 2121 2122 2123 |
}
if( (objType & (OBJTYPE_WIKI|OBJTYPE_TICKET))!=0 ){
style_submenu_element("Parsed", "%R/info/%s", zUuid);
}
if( descOnly ){
style_submenu_element("Content", "%R/artifact/%s", zUuid);
}else{
| < < < > > > | | | 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 |
}
if( (objType & (OBJTYPE_WIKI|OBJTYPE_TICKET))!=0 ){
style_submenu_element("Parsed", "%R/info/%s", zUuid);
}
if( descOnly ){
style_submenu_element("Content", "%R/artifact/%s", zUuid);
}else{
@ <hr />
content_get(rid, &content);
if( renderAsWiki ){
wiki_render_by_mimetype(&content, zMime);
}else if( renderAsHtml ){
@ <iframe src="%R/raw/%T(blob_str(&downloadName))?name=%s(zUuid)"
@ width="100%%" frameborder="0" marginwidth="0" marginheight="0"
@ sandbox="allow-same-origin" id="ifm1">
@ </iframe>
@ <script nonce="%h(style_nonce())">
@ document.getElementById("ifm1").addEventListener("load",
@ function(){
@ this.height=this.contentDocument.documentElement.scrollHeight + 75;
@ }
@ );
@ </script>
}else{
style_submenu_element("Hex", "%s/hexdump?name=%s", g.zTop, zUuid);
if( zLn==0 || atoi(zLn)==0 ){
style_submenu_checkbox("ln", "Line Numbers", 0, 0);
}
blob_to_utf8_no_bom(&content, 0);
zMime = mimetype_from_content(&content);
@ <blockquote>
if( zMime==0 ){
const char *z;
z = blob_str(&content);
if( zLn ){
output_text_with_line_numbers(z, zLn);
}else{
@ <pre>
@ %h(z)
@ </pre>
}
}else if( strncmp(zMime, "image/", 6)==0 ){
@ <p>(file is %d(blob_size(&content)) bytes of image data)</i></p>
@ <p><img src="%R/raw/%s(zUuid)?m=%s(zMime)"></p>
style_submenu_element("Image", "%R/raw/%s?m=%s", zUuid, zMime);
}else{
@ <i>(file is %d(blob_size(&content)) bytes of binary data)</i>
}
@ </blockquote>
}
}
|
| ︙ | ︙ | |||
2476 2477 2478 2479 2480 2481 2482 | change_sym_tag(zNewBranch,"*"); } /* ** The apply_newtags method is called after all newtags have been added ** and the control artifact is completed and then written to the DB. */ | | > > > > > > > > > | > > > > > > | | | | < | > > | 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 |
change_sym_tag(zNewBranch,"*");
}
/*
** The apply_newtags method is called after all newtags have been added
** and the control artifact is completed and then written to the DB.
*/
static void apply_newtags(
Blob *ctrl,
int rid,
const char *zUuid,
const char *zUserOvrd, /* The user name on the control artifact */
int fDryRun /* Print control artifact, but make no changes */
){
Stmt q;
int nChng = 0;
db_prepare(&q, "SELECT tag, prefix, value FROM newtags"
" ORDER BY prefix || tag");
while( db_step(&q)==SQLITE_ROW ){
const char *zTag = db_column_text(&q, 0);
const char *zPrefix = db_column_text(&q, 1);
const char *zValue = db_column_text(&q, 2);
nChng++;
if( zValue ){
blob_appendf(ctrl, "T %s%F %s %F\n", zPrefix, zTag, zUuid, zValue);
}else{
blob_appendf(ctrl, "T %s%F %s\n", zPrefix, zTag, zUuid);
}
}
db_finalize(&q);
if( nChng>0 ){
int nrid;
Blob cksum;
if( zUserOvrd && zUserOvrd[0] ){
blob_appendf(ctrl, "U %F\n", zUserOvrd);
}else{
blob_appendf(ctrl, "U %F\n", login_name());
}
md5sum_blob(ctrl, &cksum);
blob_appendf(ctrl, "Z %b\n", &cksum);
if( fDryRun ){
assert( g.isHTTP==0 ); /* Only print control artifact in console mode. */
fossil_print("%s", blob_str(ctrl));
blob_reset(ctrl);
}else{
db_begin_transaction();
g.markPrivate = content_is_private(rid);
nrid = content_put(ctrl);
manifest_crosslink(nrid, ctrl, MC_PERMIT_HOOKS);
db_end_transaction(0);
}
assert( blob_is_reset(ctrl) );
}
}
/*
** This method checks that the date can be parsed.
** Returns 1 if datetime() can validate, 0 otherwise.
*/
|
| ︙ | ︙ | |||
2645 2646 2647 2648 2649 2650 2651 |
if( P(zLabel) ) cancel_special(zTag);
}
db_finalize(&q);
if( zHideFlag[0] ) hide_branch();
if( zCloseFlag[0] ) close_leaf(rid);
if( zNewTagFlag[0] && zNewTag[0] ) add_tag(zNewTag);
if( zNewBrFlag[0] && zNewBranch[0] ) change_branch(rid,zNewBranch);
| | | 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 |
if( P(zLabel) ) cancel_special(zTag);
}
db_finalize(&q);
if( zHideFlag[0] ) hide_branch();
if( zCloseFlag[0] ) close_leaf(rid);
if( zNewTagFlag[0] && zNewTag[0] ) add_tag(zNewTag);
if( zNewBrFlag[0] && zNewBranch[0] ) change_branch(rid,zNewBranch);
apply_newtags(&ctrl, rid, zUuid, 0, 0);
cgi_redirectf("%R/ci/%S", zUuid);
}
blob_zero(&comment);
blob_append(&comment, zNewComment, -1);
zUuid[10] = 0;
style_header("Edit Check-in [%s]", zUuid);
if( P("preview") ){
|
| ︙ | ︙ | |||
2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 | ** --bgcolor COLOR Apply COLOR to this check-in ** --branchcolor COLOR Apply and propagate COLOR to the branch ** --tag TAG Add new TAG to this check-in ** --cancel TAG Cancel TAG from this check-in ** --branch NAME Make this check-in the start of branch NAME ** --hide Hide branch starting from this check-in ** --close Mark this "leaf" as closed ** ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in ** year-month-day form, it may be truncated, the "T" may be replaced by ** a space, and it may also name a timezone offset from UTC as "-HH:MM" ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" ** means UTC. */ | > > > | 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 | ** --bgcolor COLOR Apply COLOR to this check-in ** --branchcolor COLOR Apply and propagate COLOR to the branch ** --tag TAG Add new TAG to this check-in ** --cancel TAG Cancel TAG from this check-in ** --branch NAME Make this check-in the start of branch NAME ** --hide Hide branch starting from this check-in ** --close Mark this "leaf" as closed ** -n|--dry-run Print control artifact, but make no changes ** --date-override DATETIME Set the change time on the control artifact ** --user-override USER Set the user name on the control artifact ** ** DATETIME may be "now" or "YYYY-MM-DDTHH:MM:SS.SSS". If in ** year-month-day form, it may be truncated, the "T" may be replaced by ** a space, and it may also name a timezone offset from UTC as "-HH:MM" ** (westward) or "+HH:MM" (eastward). Either no timezone suffix or "Z" ** means UTC. */ |
| ︙ | ︙ | |||
2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 | int fClose; /* True if leaf should be closed */ int fHide; /* True if branch should be hidden */ int fPropagateColor; /* True if color propagates before amend */ int fNewPropagateColor = 0; /* True if color propagates after amend */ int fHasHidden = 0; /* True if hidden tag already set */ int fHasClosed = 0; /* True if closed tag already set */ int fEditComment; /* True if editor to be used for comment */ const char *zChngTime; /* The change time on the control artifact */ const char *zUuid; Blob ctrl; Blob comment; char *zNow; int nTags, nCancels; int i; Stmt q; | > > | 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 | int fClose; /* True if leaf should be closed */ int fHide; /* True if branch should be hidden */ int fPropagateColor; /* True if color propagates before amend */ int fNewPropagateColor = 0; /* True if color propagates after amend */ int fHasHidden = 0; /* True if hidden tag already set */ int fHasClosed = 0; /* True if closed tag already set */ int fEditComment; /* True if editor to be used for comment */ int fDryRun; /* Print control artifact, make no changes */ const char *zChngTime; /* The change time on the control artifact */ const char *zUserOvrd; /* The user name on the control artifact */ const char *zUuid; Blob ctrl; Blob comment; char *zNow; int nTags, nCancels; int i; Stmt q; |
| ︙ | ︙ | |||
2954 2955 2956 2957 2958 2959 2960 |
}
zNewDate = find_option("date",0,1);
zNewUser = find_option("author",0,1);
pzNewTags = find_repeatable_option("tag",0,&nTags);
pzCancelTags = find_repeatable_option("cancel",0,&nCancels);
fClose = find_option("close",0,0)!=0;
fHide = find_option("hide",0,0)!=0;
| > > | > > | 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 |
}
zNewDate = find_option("date",0,1);
zNewUser = find_option("author",0,1);
pzNewTags = find_repeatable_option("tag",0,&nTags);
pzCancelTags = find_repeatable_option("cancel",0,&nCancels);
fClose = find_option("close",0,0)!=0;
fHide = find_option("hide",0,0)!=0;
fDryRun = find_option("dry-run","n",0)!=0;
if( fDryRun==0 ) fDryRun = find_option("dryrun","n",0)!=0;
zChngTime = find_option("date-override",0,1);
if( zChngTime==0 ) zChngTime = find_option("chngtime",0,1);
zUserOvrd = find_option("user-override",0,1);
db_find_and_open_repository(0,0);
user_select();
verify_all_options();
if( g.argc<3 || g.argc>=4 ) usage(AMEND_USAGE_STMT);
rid = name_to_typed_rid(g.argv[2], "ci");
if( rid==0 && !is_a_version(rid) ) fossil_fatal("no such check-in");
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
|
| ︙ | ︙ | |||
3050 3051 3052 3053 3054 3055 3056 |
cancel_tag(rid,pzCancelTags[i]);
}
fossil_free((void *)pzCancelTags);
}
if( fHide && !fHasHidden ) hide_branch();
if( fClose && !fHasClosed ) close_leaf(rid);
if( zNewBranch && zNewBranch[0] ) change_branch(rid,zNewBranch);
| | > | | > > > > | 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 |
cancel_tag(rid,pzCancelTags[i]);
}
fossil_free((void *)pzCancelTags);
}
if( fHide && !fHasHidden ) hide_branch();
if( fClose && !fHasClosed ) close_leaf(rid);
if( zNewBranch && zNewBranch[0] ) change_branch(rid,zNewBranch);
apply_newtags(&ctrl, rid, zUuid, zUserOvrd, fDryRun);
if( fDryRun==0 ){
show_common_info(rid, "uuid:", 1, 0);
}
if( g.localOpen ){
manifest_to_disk(rid);
}
}
|
Changes to src/json_status.c.
| ︙ | ︙ | |||
152 153 154 155 156 157 158 |
cson_object_set( oPay, "errorCount", json_new_int( nErr ) );
db_finalize(&q);
#if 0
/* TODO: add "merged with" status. First need (A) to decide on a
structure and (B) to set up some tests for the multi-merge
case.*/
| < | | 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
cson_object_set( oPay, "errorCount", json_new_int( nErr ) );
db_finalize(&q);
#if 0
/* TODO: add "merged with" status. First need (A) to decide on a
structure and (B) to set up some tests for the multi-merge
case.*/
db_prepare(&q, "SELECT mhash, id FROM vmerge WHERE id<=0");
while( db_step(&q)==SQLITE_ROW ){
const char *zLabel = "MERGED_WITH";
switch( db_column_int(&q, 1) ){
case -1: zLabel = "CHERRYPICK "; break;
case -2: zLabel = "BACKOUT "; break;
case -4: zLabel = "INTEGRATE "; break;
}
|
| ︙ | ︙ |
Changes to src/login.c.
| ︙ | ︙ | |||
547 548 549 550 551 552 553 |
int uid; /* User id logged in user */
char *zSha1Pw;
const char *zIpAddr; /* IP address of requestor */
const char *zReferer;
int noAnon = P("noanon")!=0;
login_check_credentials();
| | < < < < < < < < < < < < < < < < < < < < < < < | 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 |
int uid; /* User id logged in user */
char *zSha1Pw;
const char *zIpAddr; /* IP address of requestor */
const char *zReferer;
int noAnon = P("noanon")!=0;
login_check_credentials();
fossil_redirect_to_https_if_needed(1);
sqlite3_create_function(g.db, "constant_time_cmp", 2, SQLITE_UTF8, 0,
constant_time_cmp_function, 0, 0);
zUsername = P("u");
zPasswd = P("p");
anonFlag = g.zLogin==0 && PB("anon");
/* Handle log-out requests */
|
| ︙ | ︙ | |||
718 719 720 721 722 723 724 725 726 727 728 729 730 731 |
}
if( anonFlag ){
@ <input type="hidden" name="anon" value="1" />
}
if( g.zLogin ){
@ <p>Currently logged in as <b>%h(g.zLogin)</b>.
@ <input type="submit" name="out" value="Logout"></p>
}else{
@ <table class="login_out">
@ <tr>
@ <td class="form_label">User ID:</td>
if( anonFlag ){
@ <td><input type="text" id="u" name="u" value="anonymous" size="30"></td>
}else{
| > | 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 |
}
if( anonFlag ){
@ <input type="hidden" name="anon" value="1" />
}
if( g.zLogin ){
@ <p>Currently logged in as <b>%h(g.zLogin)</b>.
@ <input type="submit" name="out" value="Logout"></p>
@ </form>
}else{
@ <table class="login_out">
@ <tr>
@ <td class="form_label">User ID:</td>
if( anonFlag ){
@ <td><input type="text" id="u" name="u" value="anonymous" size="30"></td>
}else{
|
| ︙ | ︙ | |||
912 913 914 915 916 917 918 |
" AND length(cap)>0"
" AND length(pw)>0"
" AND constant_time_cmp(cookie,%Q)=0",
zLogin, zRemoteAddr, zCookie
);
return uid;
}
| < < < < < < < < < < < < < < < < | 890 891 892 893 894 895 896 897 898 899 900 901 902 903 |
" AND length(cap)>0"
" AND length(pw)>0"
" AND constant_time_cmp(cookie,%Q)=0",
zLogin, zRemoteAddr, zCookie
);
return uid;
}
/*
** Attempt to use Basic Authentication to establish the user. Return the
** (non-zero) uid if successful. Return 0 if it does not work.
*/
static int logic_basic_authentication(const char *zIpAddr){
const char *zAuth = PD("HTTP_AUTHORIZATION", 0);
|
| ︙ | ︙ | |||
1459 1460 1461 1462 1463 1464 1465 |
}else
#endif /* FOSSIL_ENABLE_JSON */
{
const char *zUrl = PD("REQUEST_URI", "index");
const char *zQS = P("QUERY_STRING");
Blob redir;
blob_init(&redir, 0, 0);
| | | 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 |
}else
#endif /* FOSSIL_ENABLE_JSON */
{
const char *zUrl = PD("REQUEST_URI", "index");
const char *zQS = P("QUERY_STRING");
Blob redir;
blob_init(&redir, 0, 0);
if( fossil_wants_https(1) ){
blob_appendf(&redir, "%s/login?g=%T", g.zHttpsURL, zUrl);
}else{
blob_appendf(&redir, "%R/login?g=%T", zUrl);
}
if( anonOk ) blob_append(&redir, "&anon", 5);
if( zQS && zQS[0] ){
blob_appendf(&redir, "&%s", zQS);
|
| ︙ | ︙ |
Changes to src/main.c.
| ︙ | ︙ | |||
201 202 203 204 205 206 207 |
const char *zLogin; /* Login name. NULL or "" if not logged in. */
const char *zSSLIdentity; /* Value of --ssl-identity option, filename of
** SSL client identity */
int useLocalauth; /* No login required if from 127.0.0.1 */
int noPswd; /* Logged in without password (on 127.0.0.1) */
int userUid; /* Integer user id */
int isHuman; /* True if access by a human, not a spider or bot */
| | > | 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
const char *zLogin; /* Login name. NULL or "" if not logged in. */
const char *zSSLIdentity; /* Value of --ssl-identity option, filename of
** SSL client identity */
int useLocalauth; /* No login required if from 127.0.0.1 */
int noPswd; /* Logged in without password (on 127.0.0.1) */
int userUid; /* Integer user id */
int isHuman; /* True if access by a human, not a spider or bot */
int comFmtFlags; /* Zero or more "COMMENT_PRINT_*" bit flags, should be
** accessed through get_comment_format(). */
/* Information used to populate the RCVFROM table */
int rcvid; /* The rcvid. 0 if not yet defined. */
char *zIpAddr; /* The remote IP address */
char *zNonce; /* The nonce used for login */
/* permissions available to current user */
|
| ︙ | ︙ | |||
376 377 378 379 380 381 382 383 384 385 386 387 388 389 |
*/
static void expand_args_option(int argc, void *argv){
Blob file = empty_blob; /* Content of the file */
Blob line = empty_blob; /* One line of the file */
unsigned int nLine; /* Number of lines in the file*/
unsigned int i, j, k; /* Loop counters */
int n; /* Number of bytes in one line */
char *z; /* General use string pointer */
char **newArgv; /* New expanded g.argv under construction */
const char *zFileName; /* input file name */
FILE *inFile; /* input FILE */
#if defined(_WIN32)
wchar_t buf[MAX_PATH];
#endif
| > | 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 |
*/
static void expand_args_option(int argc, void *argv){
Blob file = empty_blob; /* Content of the file */
Blob line = empty_blob; /* One line of the file */
unsigned int nLine; /* Number of lines in the file*/
unsigned int i, j, k; /* Loop counters */
int n; /* Number of bytes in one line */
unsigned int nArg; /* Number of new arguments */
char *z; /* General use string pointer */
char **newArgv; /* New expanded g.argv under construction */
const char *zFileName; /* input file name */
FILE *inFile; /* input FILE */
#if defined(_WIN32)
wchar_t buf[MAX_PATH];
#endif
|
| ︙ | ︙ | |||
409 410 411 412 413 414 415 |
if( z[0]=='-' ) z++;
if( z[0]==0 ) return; /* Stop searching at "--" */
if( fossil_strcmp(z, "args")==0 ) break;
}
if( i>=g.argc-1 ) return;
zFileName = g.argv[i+1];
| | | > > > | | | < > > | | | | | < > > | | < | < > > > > > | 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 |
if( z[0]=='-' ) z++;
if( z[0]==0 ) return; /* Stop searching at "--" */
if( fossil_strcmp(z, "args")==0 ) break;
}
if( i>=g.argc-1 ) return;
zFileName = g.argv[i+1];
if( strcmp(zFileName,"-")==0 ){
inFile = stdin;
}else if( !file_isfile(zFileName, ExtFILE) ){
fossil_fatal("Not an ordinary file: \"%s\"", zFileName);
}else{
inFile = fossil_fopen(zFileName,"rb");
if( inFile==0 ){
fossil_fatal("Cannot open -args file [%s]", zFileName);
}
}
blob_read_from_channel(&file, inFile, -1);
if(stdin != inFile){
fclose(inFile);
}
inFile = NULL;
blob_to_utf8_no_bom(&file, 1);
z = blob_str(&file);
for(k=0, nLine=1; z[k]; k++) if( z[k]=='\n' ) nLine++;
if( nLine>100000000 ) fossil_fatal("too many command-line arguments");
nArg = g.argc + nLine*2;
newArgv = fossil_malloc( sizeof(char*)*nArg );
for(j=0; j<i; j++) newArgv[j] = g.argv[j];
blob_rewind(&file);
while( (n = blob_line(&file, &line))>0 ){
if( n<1 ){
/* Reminder: corner-case: a line with 1 byte and no newline. */
continue;
}
z = blob_buffer(&line);
if('\n'==z[n-1]){
z[n-1] = 0;
}
if((n>1) && ('\r'==z[n-2])){
if(n==2) continue /*empty line*/;
z[n-2] = 0;
}
if(!z[0]) continue;
if( j>=nArg ){
fossil_fatal("malformed command-line arguments");
}
newArgv[j++] = z;
if( z[0]=='-' ){
for(k=1; z[k] && !fossil_isspace(z[k]); k++){}
if( z[k] ){
z[k] = 0;
k++;
if( z[k] ) newArgv[j++] = &z[k];
|
| ︙ | ︙ | |||
564 565 566 567 568 569 570 571 572 573 |
** This function attempts to find command line options known to contain
** bitwise flags and initializes the associated global variables. After
** this function executes, all global variables (i.e. in the "g" struct)
** containing option-settable bitwise flag fields must be initialized.
*/
static void fossil_init_flags_from_options(void){
const char *zValue = find_option("comfmtflags", 0, 1);
if( zValue ){
g.comFmtFlags = atoi(zValue);
}else{
| > > > | | 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 |
** This function attempts to find command line options known to contain
** bitwise flags and initializes the associated global variables. After
** this function executes, all global variables (i.e. in the "g" struct)
** containing option-settable bitwise flag fields must be initialized.
*/
static void fossil_init_flags_from_options(void){
const char *zValue = find_option("comfmtflags", 0, 1);
if( zValue==0 ){
zValue = find_option("comment-format", 0, 1);
}
if( zValue ){
g.comFmtFlags = atoi(zValue);
}else{
g.comFmtFlags = COMMENT_PRINT_UNSET; /* Command-line option not found. */
}
}
/*
** Check to see if the Fossil binary contains an appended repository
** file using the appendvfs extension. If so, change command-line arguments
** to cause Fossil to launch with "fossil ui" on that repo.
|
| ︙ | ︙ | |||
1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 |
if( g.fAnyTrace ){
fprintf(stderr,"/***** sigpipe received by subprocess %d ****\n", getpid());
}
#endif
db_panic_close();
exit(1);
}
/*
** Preconditions:
**
** * Environment variables are set up according to the CGI standard.
**
** If the repository is known, it has already been opened. If unknown,
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 |
if( g.fAnyTrace ){
fprintf(stderr,"/***** sigpipe received by subprocess %d ****\n", getpid());
}
#endif
db_panic_close();
exit(1);
}
/*
** Return true if it is appropriate to redirect requests to HTTPS.
**
** Redirect to https is appropriate if all of the above are true:
** (1) The redirect-to-https flag has a valud of iLevel or greater.
** (2) The current connection is http, not https or ssh
** (3) The sslNotAvailable flag is clear
*/
int fossil_wants_https(int iLevel){
if( g.sslNotAvailable ) return 0;
if( db_get_int("redirect-to-https",0)<iLevel ) return 0;
if( P("HTTPS")!=0 ) return 0;
return 1;
}
/*
** Redirect to the equivalent HTTPS request if the current connection is
** insecure and if the redirect-to-https flag greater than or equal to
** iLevel. iLevel is 1 for /login pages and 2 for every other page.
*/
int fossil_redirect_to_https_if_needed(int iLevel){
if( fossil_wants_https(iLevel) ){
const char *zQS = P("QUERY_STRING");
char *zURL;
if( zQS==0 || zQS[0]==0 ){
zURL = mprintf("%s%T", g.zHttpsURL, P("PATH_INFO"));
}else if( zQS[0]!=0 ){
zURL = mprintf("%s%T?%s", g.zHttpsURL, P("PATH_INFO"), zQS);
}
cgi_redirect_with_status(zURL, 301, "Moved Permanently");
return 1;
}
return 0;
}
/*
** Preconditions:
**
** * Environment variables are set up according to the CGI standard.
**
** If the repository is known, it has already been opened. If unknown,
|
| ︙ | ︙ | |||
1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 |
zPathInfo = "/xfer";
}
/* Use the first element of PATH_INFO as the page name
** and deliver the appropriate page back to the user.
*/
set_base_url(0);
if( zPathInfo==0 || zPathInfo[0]==0
|| (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
/* Second special case: If the PATH_INFO is blank, issue a redirect to
** the home page identified by the "index-page" setting in the repository
** CONFIG table, to "/index" if there no "index-page" setting. */
#ifdef FOSSIL_ENABLE_JSON
if(g.json.isJsonMode){
| > | 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 |
zPathInfo = "/xfer";
}
/* Use the first element of PATH_INFO as the page name
** and deliver the appropriate page back to the user.
*/
set_base_url(0);
if( fossil_redirect_to_https_if_needed(2) ) return;
if( zPathInfo==0 || zPathInfo[0]==0
|| (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
/* Second special case: If the PATH_INFO is blank, issue a redirect to
** the home page identified by the "index-page" setting in the repository
** CONFIG table, to "/index" if there no "index-page" setting. */
#ifdef FOSSIL_ENABLE_JSON
if(g.json.isJsonMode){
|
| ︙ | ︙ | |||
2424 2425 2426 2427 2428 2429 2430 | ** --localauth enable automatic login for requests from localhost ** --localhost listen on 127.0.0.1 only (always true for "ui") ** --https signal a request coming in via https ** --max-latency N Do not let any single HTTP request run for more than N ** seconds (only works on unix) ** --nocompress Do not compress HTTP replies ** --nojail Drop root privileges but do not enter the chroot jail | | > | 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 | ** --localauth enable automatic login for requests from localhost ** --localhost listen on 127.0.0.1 only (always true for "ui") ** --https signal a request coming in via https ** --max-latency N Do not let any single HTTP request run for more than N ** seconds (only works on unix) ** --nocompress Do not compress HTTP replies ** --nojail Drop root privileges but do not enter the chroot jail ** --nossl signal that no SSL connections are available (Always ** set by default for the "ui" command) ** --notfound URL Redirect ** -P|--port TCPPORT listen to request on port TCPPORT ** --th-trace trace TH1 execution (for debugging purposes) ** --repolist If REPOSITORY is dir, URL "/" lists repos. ** --scgi Accept SCGI rather than HTTP ** --skin LABEL Use override skin LABEL ** --usepidkey Use saved encryption key from parent process. This is |
| ︙ | ︙ | |||
2495 2496 2497 2498 2499 2500 2501 |
if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1;
zAltBase = find_option("baseurl", 0, 1);
fCreate = find_option("create",0,0)!=0;
if( find_option("scgi", 0, 0)!=0 ) flags |= HTTP_SERVER_SCGI;
if( zAltBase ){
set_base_url(zAltBase);
}
| | | 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 |
if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1;
zAltBase = find_option("baseurl", 0, 1);
fCreate = find_option("create",0,0)!=0;
if( find_option("scgi", 0, 0)!=0 ) flags |= HTTP_SERVER_SCGI;
if( zAltBase ){
set_base_url(zAltBase);
}
g.sslNotAvailable = find_option("nossl", 0, 0)!=0 || isUiCmd;
if( find_option("https",0,0)!=0 ){
cgi_replace_parameter("HTTPS","on");
}
if( find_option("localhost", 0, 0)!=0 ){
flags |= HTTP_SERVER_LOCALHOST;
}
|
| ︙ | ︙ | |||
2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 |
}
if( g.repositoryOpen ) flags |= HTTP_SERVER_HAD_REPOSITORY;
if( g.localOpen ) flags |= HTTP_SERVER_HAD_CHECKOUT;
db_close(1);
if( cgi_http_server(iPort, mxPort, zBrowserCmd, zIpAddr, flags) ){
fossil_fatal("unable to listen on TCP socket %d", iPort);
}
if( zMaxLatency ){
signal(SIGALRM, sigalrm_handler);
alarm(atoi(zMaxLatency));
}
g.httpIn = stdin;
g.httpOut = stdout;
| > > > > > > > > > | 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 |
}
if( g.repositoryOpen ) flags |= HTTP_SERVER_HAD_REPOSITORY;
if( g.localOpen ) flags |= HTTP_SERVER_HAD_CHECKOUT;
db_close(1);
if( cgi_http_server(iPort, mxPort, zBrowserCmd, zIpAddr, flags) ){
fossil_fatal("unable to listen on TCP socket %d", iPort);
}
/* For the parent process, the cgi_http_server() command above never
** returns (except in the case of an error). Instead, for each incoming
** client connection, a child process is created, file descriptors 0
** and 1 are bound to that connection, and the child returns.
**
** So, when control reaches this point, we are running as a
** child process, the HTTP or SCGI request is pending on file
** descriptor 0 and the reply should be written to file descriptor 1.
*/
if( zMaxLatency ){
signal(SIGALRM, sigalrm_handler);
alarm(atoi(zMaxLatency));
}
g.httpIn = stdin;
g.httpOut = stdout;
|
| ︙ | ︙ |
Changes to src/main.mk.
| ︙ | ︙ | |||
39 40 41 42 43 44 45 46 47 48 49 50 51 52 | $(SRCDIR)/comformat.c \ $(SRCDIR)/configure.c \ $(SRCDIR)/content.c \ $(SRCDIR)/cookies.c \ $(SRCDIR)/db.c \ $(SRCDIR)/delta.c \ $(SRCDIR)/deltacmd.c \ $(SRCDIR)/descendants.c \ $(SRCDIR)/diff.c \ $(SRCDIR)/diffcmd.c \ $(SRCDIR)/dispatch.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/etag.c \ | > | 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | $(SRCDIR)/comformat.c \ $(SRCDIR)/configure.c \ $(SRCDIR)/content.c \ $(SRCDIR)/cookies.c \ $(SRCDIR)/db.c \ $(SRCDIR)/delta.c \ $(SRCDIR)/deltacmd.c \ $(SRCDIR)/deltafunc.c \ $(SRCDIR)/descendants.c \ $(SRCDIR)/diff.c \ $(SRCDIR)/diffcmd.c \ $(SRCDIR)/dispatch.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/etag.c \ |
| ︙ | ︙ | |||
250 251 252 253 254 255 256 257 258 259 260 261 262 263 | $(OBJDIR)/comformat_.c \ $(OBJDIR)/configure_.c \ $(OBJDIR)/content_.c \ $(OBJDIR)/cookies_.c \ $(OBJDIR)/db_.c \ $(OBJDIR)/delta_.c \ $(OBJDIR)/deltacmd_.c \ $(OBJDIR)/descendants_.c \ $(OBJDIR)/diff_.c \ $(OBJDIR)/diffcmd_.c \ $(OBJDIR)/dispatch_.c \ $(OBJDIR)/doc_.c \ $(OBJDIR)/encode_.c \ $(OBJDIR)/etag_.c \ | > | 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | $(OBJDIR)/comformat_.c \ $(OBJDIR)/configure_.c \ $(OBJDIR)/content_.c \ $(OBJDIR)/cookies_.c \ $(OBJDIR)/db_.c \ $(OBJDIR)/delta_.c \ $(OBJDIR)/deltacmd_.c \ $(OBJDIR)/deltafunc_.c \ $(OBJDIR)/descendants_.c \ $(OBJDIR)/diff_.c \ $(OBJDIR)/diffcmd_.c \ $(OBJDIR)/dispatch_.c \ $(OBJDIR)/doc_.c \ $(OBJDIR)/encode_.c \ $(OBJDIR)/etag_.c \ |
| ︙ | ︙ | |||
388 389 390 391 392 393 394 395 396 397 398 399 400 401 | $(OBJDIR)/comformat.o \ $(OBJDIR)/configure.o \ $(OBJDIR)/content.o \ $(OBJDIR)/cookies.o \ $(OBJDIR)/db.o \ $(OBJDIR)/delta.o \ $(OBJDIR)/deltacmd.o \ $(OBJDIR)/descendants.o \ $(OBJDIR)/diff.o \ $(OBJDIR)/diffcmd.o \ $(OBJDIR)/dispatch.o \ $(OBJDIR)/doc.o \ $(OBJDIR)/encode.o \ $(OBJDIR)/etag.o \ | > | 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 | $(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 \ |
| ︙ | ︙ | |||
504 505 506 507 508 509 510 | APPNAME = fossil$(E) all: $(OBJDIR) $(APPNAME) | | | 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 | APPNAME = fossil$(E) all: $(OBJDIR) $(APPNAME) install: all mkdir -p $(INSTALLDIR) cp $(APPNAME) $(INSTALLDIR) codecheck: $(TRANS_SRC) $(OBJDIR)/codecheck1 $(OBJDIR)/codecheck1 $(TRANS_SRC) $(OBJDIR): |
| ︙ | ︙ | |||
722 723 724 725 726 727 728 729 730 731 732 733 734 735 | $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \ $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \ $(OBJDIR)/content_.c:$(OBJDIR)/content.h \ $(OBJDIR)/cookies_.c:$(OBJDIR)/cookies.h \ $(OBJDIR)/db_.c:$(OBJDIR)/db.h \ $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h \ $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h \ $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h \ $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h \ $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \ $(OBJDIR)/dispatch_.c:$(OBJDIR)/dispatch.h \ $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \ $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \ $(OBJDIR)/etag_.c:$(OBJDIR)/etag.h \ | > | 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 | $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \ $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \ $(OBJDIR)/content_.c:$(OBJDIR)/content.h \ $(OBJDIR)/cookies_.c:$(OBJDIR)/cookies.h \ $(OBJDIR)/db_.c:$(OBJDIR)/db.h \ $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h \ $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h \ $(OBJDIR)/deltafunc_.c:$(OBJDIR)/deltafunc.h \ $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h \ $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h \ $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \ $(OBJDIR)/dispatch_.c:$(OBJDIR)/dispatch.h \ $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \ $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \ $(OBJDIR)/etag_.c:$(OBJDIR)/etag.h \ |
| ︙ | ︙ | |||
1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 | $(OBJDIR)/deltacmd_.c: $(SRCDIR)/deltacmd.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/deltacmd.c >$@ $(OBJDIR)/deltacmd.o: $(OBJDIR)/deltacmd_.c $(OBJDIR)/deltacmd.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/deltacmd.o -c $(OBJDIR)/deltacmd_.c $(OBJDIR)/deltacmd.h: $(OBJDIR)/headers $(OBJDIR)/descendants_.c: $(SRCDIR)/descendants.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/descendants.c >$@ $(OBJDIR)/descendants.o: $(OBJDIR)/descendants_.c $(OBJDIR)/descendants.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/descendants.o -c $(OBJDIR)/descendants_.c | > > > > > > > > | 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 | $(OBJDIR)/deltacmd_.c: $(SRCDIR)/deltacmd.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/deltacmd.c >$@ $(OBJDIR)/deltacmd.o: $(OBJDIR)/deltacmd_.c $(OBJDIR)/deltacmd.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/deltacmd.o -c $(OBJDIR)/deltacmd_.c $(OBJDIR)/deltacmd.h: $(OBJDIR)/headers $(OBJDIR)/deltafunc_.c: $(SRCDIR)/deltafunc.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/deltafunc.c >$@ $(OBJDIR)/deltafunc.o: $(OBJDIR)/deltafunc_.c $(OBJDIR)/deltafunc.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/deltafunc.o -c $(OBJDIR)/deltafunc_.c $(OBJDIR)/deltafunc.h: $(OBJDIR)/headers $(OBJDIR)/descendants_.c: $(SRCDIR)/descendants.c $(OBJDIR)/translate $(OBJDIR)/translate $(SRCDIR)/descendants.c >$@ $(OBJDIR)/descendants.o: $(OBJDIR)/descendants_.c $(OBJDIR)/descendants.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/descendants.o -c $(OBJDIR)/descendants_.c |
| ︙ | ︙ |
Changes to src/makemake.tcl.
| ︙ | ︙ | |||
50 51 52 53 54 55 56 57 58 59 60 61 62 63 | comformat configure content cookies db delta deltacmd descendants diff diffcmd dispatch doc encode etag | > | 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | comformat configure content cookies db delta deltacmd deltafunc descendants diff diffcmd dispatch doc encode etag |
| ︙ | ︙ | |||
315 316 317 318 319 320 321 |
writeln [string map [list \
<<<SQLITE_OPTIONS>>> [join $SQLITE_OPTIONS " \\\n "] \
<<<SHELL_OPTIONS>>> [join $SHELL_OPTIONS " \\\n "] \
<<<MINIZ_OPTIONS>>> [join $MINIZ_OPTIONS " \\\n "]] {
all: $(OBJDIR) $(APPNAME)
| | | 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 |
writeln [string map [list \
<<<SQLITE_OPTIONS>>> [join $SQLITE_OPTIONS " \\\n "] \
<<<SHELL_OPTIONS>>> [join $SHELL_OPTIONS " \\\n "] \
<<<MINIZ_OPTIONS>>> [join $MINIZ_OPTIONS " \\\n "]] {
all: $(OBJDIR) $(APPNAME)
install: all
mkdir -p $(INSTALLDIR)
cp $(APPNAME) $(INSTALLDIR)
codecheck: $(TRANS_SRC) $(OBJDIR)/codecheck1
$(OBJDIR)/codecheck1 $(TRANS_SRC)
$(OBJDIR):
|
| ︙ | ︙ | |||
708 709 710 711 712 713 714 | endif #### The directories where the OpenSSL include and library files are located. # The recommended usage here is to use the Sysinternals junction tool # to create a hard link between an "openssl-1.x" sub-directory of the # Fossil source code directory and the target OpenSSL source directory. # | | | 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 | endif #### The directories where the OpenSSL include and library files are located. # The recommended usage here is to use the Sysinternals junction tool # to create a hard link between an "openssl-1.x" sub-directory of the # Fossil source code directory and the target OpenSSL source directory. # OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2r OPENSSLINCDIR = $(OPENSSLDIR)/include OPENSSLLIBDIR = $(OPENSSLDIR) #### Either the directory where the Tcl library is installed or the Tcl # source code directory resides (depending on the value of the macro # FOSSIL_TCL_SOURCE). If this points to the Tcl install directory, # this directory must have "include" and "lib" sub-directories. If |
| ︙ | ︙ | |||
1564 1565 1566 1567 1568 1569 1570 | # Enable support for the SQLite Encryption Extension? !ifndef USE_SEE USE_SEE = 0 !endif !if $(FOSSIL_ENABLE_SSL)!=0 | | | 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 | # Enable support for the SQLite Encryption Extension? !ifndef USE_SEE USE_SEE = 0 !endif !if $(FOSSIL_ENABLE_SSL)!=0 SSLDIR = $(B)\compat\openssl-1.0.2r SSLINCDIR = $(SSLDIR)\inc32 !if $(FOSSIL_DYNAMIC_BUILD)!=0 SSLLIBDIR = $(SSLDIR)\out32dll !else SSLLIBDIR = $(SSLDIR)\out32 !endif SSLLFLAGS = /nologo /opt:ref /debug |
| ︙ | ︙ | |||
1829 1830 1831 1832 1833 1834 1835 |
}
writeln "!if \$(FOSSIL_ENABLE_MINIZ)!=0"
writeln -nonewline " "
writeln "\$(OX)\\miniz\$O \\"; incr i
writeln "!endif"
writeln -nonewline " \$(OX)\\fossil.res\n\n"
writeln [string map [list <<<NEXT_LINE>>> \\] {
| > | > | > > | | 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 |
}
writeln "!if \$(FOSSIL_ENABLE_MINIZ)!=0"
writeln -nonewline " "
writeln "\$(OX)\\miniz\$O \\"; incr i
writeln "!endif"
writeln -nonewline " \$(OX)\\fossil.res\n\n"
writeln [string map [list <<<NEXT_LINE>>> \\] {
!ifndef BASEAPPNAME
BASEAPPNAME = fossil
!endif
APPNAME = $(OX)\$(BASEAPPNAME)$(E)
PDBNAME = $(OX)\$(BASEAPPNAME)$(P)
APPTARGETS =
all: $(OX) $(APPNAME)
zlib:
@echo Building zlib from "$(ZLIBDIR)"...
!if $(FOSSIL_ENABLE_WINXP)!=0
@pushd "$(ZLIBDIR)" && $(MAKE) /f win32\Makefile.msc $(ZLIB) "CC=cl $(XPCFLAGS)" "LD=link $(XPLDFLAGS)" && popd
|
| ︙ | ︙ |
Changes to src/manifest.c.
| ︙ | ︙ | |||
2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 |
return 0;
}
db_begin_transaction();
if( p->type==CFTYPE_MANIFEST ){
if( permitHooks ){
zScript = xfer_commit_code();
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
}
if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
char *zCom;
parentid = manifest_add_checkin_linkages(rid,p,p->nParent,p->azParent);
search_doc_touch('c', rid, 0);
db_multi_exec(
"REPLACE INTO event(type,mtime,objid,user,comment,"
| > > > > > > > > > > > | 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 |
return 0;
}
db_begin_transaction();
if( p->type==CFTYPE_MANIFEST ){
if( permitHooks ){
zScript = xfer_commit_code();
zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
}
if( p->nCherrypick && db_table_exists("repository","cherrypick") ){
int i;
for(i=0; i<p->nCherrypick; i++){
db_multi_exec(
"REPLACE INTO cherrypick(parentid,childid,isExclude)"
" SELECT rid, %d, %d FROM blob WHERE uuid=%Q",
rid, p->aCherrypick[i].zCPTarget[0]=='-',
p->aCherrypick[i].zCPTarget+1
);
}
}
if( !db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) ){
char *zCom;
parentid = manifest_add_checkin_linkages(rid,p,p->nParent,p->azParent);
search_doc_touch('c', rid, 0);
db_multi_exec(
"REPLACE INTO event(type,mtime,objid,user,comment,"
|
| ︙ | ︙ |
Changes to src/markdown.c.
| ︙ | ︙ | |||
1206 1207 1208 1209 1210 1211 1212 |
if( i<size && data[i]==' ') i++;
if( i<size && data[i]==' ') i++;
if( i>=size || data[i]<'0' || data[i]>'9' ) return 0;
while( i<size && data[i]>='0' && data[i]<='9' ){ i++; }
if( i+1>=size
| | | 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 |
if( i<size && data[i]==' ') i++;
if( i<size && data[i]==' ') i++;
if( i>=size || data[i]<'0' || data[i]>'9' ) return 0;
while( i<size && data[i]>='0' && data[i]<='9' ){ i++; }
if( i+1>=size
|| (data[i]!='.' && data[i]!=')')
|| (data[i+1]!=' ' && data[i+1]!='\t')
){
return 0;
}
i = i+2;
while( i<size && (data[i]==' ' || data[i]=='\t') ){ i++; }
return i;
|
| ︙ | ︙ |
Changes to src/merge.c.
| ︙ | ︙ | |||
45 46 47 48 49 50 51 |
}
fossil_print("%-*s [%S] by %s on %s\n%*s",
indent-1, zLabel,
db_column_text(&q, 3),
db_column_text(&q, 1),
db_column_text(&q, 0),
indent, "");
| | | 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
}
fossil_print("%-*s [%S] by %s on %s\n%*s",
indent-1, zLabel,
db_column_text(&q, 3),
db_column_text(&q, 1),
db_column_text(&q, 0),
indent, "");
comment_print(zCom, db_column_text(&q,2), indent, -1, get_comment_format());
fossil_free(zCom);
}
db_finalize(&q);
}
/* Pick the most recent leaf that is (1) not equal to vid and (2)
|
| ︙ | ︙ | |||
162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
);
}
free(zN);
free(zV);
}
free(aChng);
}
/*
** COMMAND: merge
**
** Usage: %fossil merge ?OPTIONS? ?VERSION?
**
** The argument VERSION is a version that should be merged into the
| > > > > > > > > > > | 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
);
}
free(zN);
free(zV);
}
free(aChng);
}
/* Make an entry in the vmerge table for the given id, and rid.
*/
static void vmerge_insert(int id, int rid){
db_multi_exec(
"INSERT OR IGNORE INTO vmerge(id,merge,mhash)"
"VALUES(%d,%d,(SELECT uuid FROM blob WHERE rid=%d))",
id, rid, rid
);
}
/*
** COMMAND: merge
**
** Usage: %fossil merge ?OPTIONS? ?VERSION?
**
** The argument VERSION is a version that should be merged into the
|
| ︙ | ︙ | |||
309 310 311 312 313 314 315 |
" WHERE event.objid=%d AND blob.rid=%d",
mid, mid
);
if( db_step(&q)==SQLITE_ROW ){
char *zCom = mprintf("Merging fork [%S] at %s by %s: \"%s\"",
db_column_text(&q, 0), db_column_text(&q, 1),
db_column_text(&q, 3), db_column_text(&q, 2));
| | | 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 |
" WHERE event.objid=%d AND blob.rid=%d",
mid, mid
);
if( db_step(&q)==SQLITE_ROW ){
char *zCom = mprintf("Merging fork [%S] at %s by %s: \"%s\"",
db_column_text(&q, 0), db_column_text(&q, 1),
db_column_text(&q, 3), db_column_text(&q, 2));
comment_print(zCom, db_column_text(&q,2), 0, -1, get_comment_format());
fossil_free(zCom);
}
db_finalize(&q);
}else{
usage("?OPTIONS? ?VERSION?");
return;
}
|
| ︙ | ︙ | |||
592 593 594 595 596 597 598 |
const char *zName = db_column_text(&q, 2);
int islinkm = db_column_int(&q, 3);
/* Copy content from idm over into idv. Overwrite idv. */
fossil_print("UPDATE %s\n", zName);
if( !dryRunFlag ){
undo_save(zName);
db_multi_exec(
| | > > | | 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 |
const char *zName = db_column_text(&q, 2);
int islinkm = db_column_int(&q, 3);
/* Copy content from idm over into idv. Overwrite idv. */
fossil_print("UPDATE %s\n", zName);
if( !dryRunFlag ){
undo_save(zName);
db_multi_exec(
"UPDATE vfile SET mtime=0, mrid=%d, chnged=%d, islink=%d,"
" mhash=CASE WHEN rid<>%d"
" THEN (SELECT uuid FROM blob WHERE blob.rid=%d) END"
" WHERE id=%d", ridm, integrateFlag?4:2, islinkm, ridm, ridm, idv
);
vfile_to_disk(0, idv, 0, 0);
}
}
db_finalize(&q);
/*
|
| ︙ | ︙ | |||
662 663 664 665 666 667 668 |
fossil_print("***** Cannot merge binary file %s\n", zName);
nConflict++;
}
blob_reset(&p);
blob_reset(&m);
blob_reset(&r);
}
| < | | 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 |
fossil_print("***** Cannot merge binary file %s\n", zName);
nConflict++;
}
blob_reset(&p);
blob_reset(&m);
blob_reset(&r);
}
vmerge_insert(idv, ridm);
}
db_finalize(&q);
/*
** Drop files that are in P and V but not in M
*/
db_prepare(&q,
|
| ︙ | ︙ | |||
739 740 741 742 743 744 745 |
zFullOldPath = db_text(0,"SELECT tmpfn FROM tmprn WHERE fn=%Q", zOldName);
if( !zFullOldPath ){
zFullOldPath = mprintf("%s%s", g.zLocalRoot, zOldName);
}
zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
if( file_size(zFullNewPath, RepoFILE)>=0 ){
Blob tmpPath;
| | | 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 |
zFullOldPath = db_text(0,"SELECT tmpfn FROM tmprn WHERE fn=%Q", zOldName);
if( !zFullOldPath ){
zFullOldPath = mprintf("%s%s", g.zLocalRoot, zOldName);
}
zFullNewPath = mprintf("%s%s", g.zLocalRoot, zNewName);
if( file_size(zFullNewPath, RepoFILE)>=0 ){
Blob tmpPath;
file_tempname(&tmpPath, "", 0);
db_multi_exec("INSERT INTO tmprn(fn,tmpfn) VALUES(%Q,%Q)",
zNewName, blob_str(&tmpPath));
if( file_islink(zFullNewPath) ){
symlink_copy(zFullNewPath, blob_str(&tmpPath));
}else{
file_copy(zFullNewPath, blob_str(&tmpPath));
}
|
| ︙ | ︙ | |||
784 785 786 787 788 789 790 |
" WHERE idp=0 AND idv=0 AND idm>0"
);
while( db_step(&q)==SQLITE_ROW ){
int idm = db_column_int(&q, 0);
const char *zName;
char *zFullName;
db_multi_exec(
| | > | > > > | 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 |
" WHERE idp=0 AND idv=0 AND idm>0"
);
while( db_step(&q)==SQLITE_ROW ){
int idm = db_column_int(&q, 0);
const char *zName;
char *zFullName;
db_multi_exec(
"REPLACE INTO vfile(vid,chnged,deleted,rid,mrid,"
"isexe,islink,pathname,mhash)"
" SELECT %d,%d,0,rid,mrid,isexe,islink,pathname,"
"CASE WHEN rid<>mrid"
" THEN (SELECT uuid FROM blob WHERE blob.rid=vfile.mrid) END "
"FROM vfile WHERE id=%d",
vid, integrateFlag?5:3, idm
);
zName = db_column_text(&q, 1);
zFullName = mprintf("%s%s", g.zLocalRoot, zName);
if( file_isfile_or_link(zFullName)
&& !db_exists("SELECT 1 FROM fv WHERE fn=%Q", zName) ){
fossil_print("ADDED %s (overwrites an unmanaged file)\n", zName);
|
| ︙ | ︙ | |||
824 825 826 827 828 829 830 |
}
/*
** Clean up the mid and pid VFILE entries. Then commit the changes.
*/
db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
if( pickFlag ){
| | | | | | 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 |
}
/*
** Clean up the mid and pid VFILE entries. Then commit the changes.
*/
db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
if( pickFlag ){
vmerge_insert(-1, mid);
/* For a cherry-pick merge, make the default check-in comment the same
** as the check-in comment on the check-in that is being merged in. */
db_multi_exec(
"REPLACE INTO vvar(name,value)"
" SELECT 'ci-comment', coalesce(ecomment,comment) FROM event"
" WHERE type='ci' AND objid=%d",
mid
);
}else if( backoutFlag ){
vmerge_insert(-2, pid);
}else if( integrateFlag ){
vmerge_insert(-4, mid);
}else{
vmerge_insert(0, mid);
}
if( !dryRunFlag ) undo_finish();
db_end_transaction(dryRunFlag);
}
|
Changes to src/mkbuiltin.c.
| ︙ | ︙ | |||
23 24 25 26 27 28 29 | ** The makefiles use this utility to package various resources (large scripts, ** GIF images, etc) that are separate files in the source code as byte ** arrays in the resulting executable. */ #include <stdio.h> #include <stdlib.h> #include <string.h> | | | 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
** The makefiles use this utility to package various resources (large scripts,
** GIF images, etc) that are separate files in the source code as byte
** arrays in the resulting executable.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
/*
** Read the entire content of the file named zFilename into memory obtained
** from malloc() and return a pointer to that memory. Write the size of the
** file into *pnByte.
*/
static unsigned char *read_file(const char *zFilename, int *pnByte){
|
| ︙ | ︙ | |||
52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
exit(1);
}
got = fread(z, 1, nByte, in);
fclose(in);
z[got] = 0;
return z;
}
/*
** There is an instance of the following for each file translated.
*/
typedef struct Resource Resource;
struct Resource {
char *zName;
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
exit(1);
}
got = fread(z, 1, nByte, in);
fclose(in);
z[got] = 0;
return z;
}
/*
** Try to compress a javascript file by removing unnecessary whitespace.
**
** Warning: This compression routine does not necessarily work for any
** arbitrary Javascript source file. But it should work ok for the
** well-behaved source files in this project.
*/
static void compressJavascript(unsigned char *z, int *pn){
int n = *pn;
int i, j, k;
for(i=j=0; i<n; i++){
unsigned char c = z[i];
if( c=='/' ){
if( z[i+1]=='*' ){
for(k=i+3; k<n && (z[k]!='/' || z[k-1]!='*'); k++){}
if( k<n ){
i = k;
while( i+1<n && isspace(z[i+1]) ) i++;
continue;
}
}else if( z[i+1]=='/' ){
for(k=i+2; k<n && z[k]!='\n'; k++){}
i = k;
while( i+1<n && isspace(z[i+1]) ) i++;
continue;
}
}
if( c=='\n' ){
while( j>0 && isspace(z[j-1]) ) j--;
z[j++] = '\n';
while( i+1<n && isspace(z[i+1]) ) i++;
continue;
}
z[j++] = c;
}
z[j] = 0;
*pn = j;
}
/*
** There is an instance of the following for each file translated.
*/
typedef struct Resource Resource;
struct Resource {
char *zName;
|
| ︙ | ︙ | |||
83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
int j, n;
Resource *aRes;
int nRes;
unsigned char *pData;
int nErr = 0;
int nSkip;
int nPrefix = 0;
if( argc>3 && strcmp(argv[1],"--prefix")==0 ){
nPrefix = (int)strlen(argv[2]);
argc -= 2;
argv += 2;
}
nRes = argc - 1;
| > | 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
int j, n;
Resource *aRes;
int nRes;
unsigned char *pData;
int nErr = 0;
int nSkip;
int nPrefix = 0;
int nName;
if( argc>3 && strcmp(argv[1],"--prefix")==0 ){
nPrefix = (int)strlen(argv[2]);
argc -= 2;
argv += 2;
}
nRes = argc - 1;
|
| ︙ | ︙ | |||
117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
/* Skip initial lines beginning with # */
nSkip = 0;
while( pData[nSkip]=='#' ){
while( pData[nSkip]!=0 && pData[nSkip]!='\n' ){ nSkip++; }
if( pData[nSkip]=='\n' ) nSkip++;
}
aRes[i].nByte = sz - nSkip;
aRes[i].idx = i;
printf("/* Content of file %s */\n", aRes[i].zName);
printf("static const unsigned char bidata%d[%d] = {\n ",
i, sz+1-nSkip);
for(j=nSkip, n=0; j<=sz; j++){
| > > > > > > > > | 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
/* Skip initial lines beginning with # */
nSkip = 0;
while( pData[nSkip]=='#' ){
while( pData[nSkip]!=0 && pData[nSkip]!='\n' ){ nSkip++; }
if( pData[nSkip]=='\n' ) nSkip++;
}
/* Compress javascript source files */
nName = (int)strlen(aRes[i].zName);
if( nName>3 && strcmp(&aRes[i].zName[nName-3],".js")==0 ){
int x = sz-nSkip;
compressJavascript(pData+nSkip, &x);
sz = x + nSkip;
}
aRes[i].nByte = sz - nSkip;
aRes[i].idx = i;
printf("/* Content of file %s */\n", aRes[i].zName);
printf("static const unsigned char bidata%d[%d] = {\n ",
i, sz+1-nSkip);
for(j=nSkip, n=0; j<=sz; j++){
|
| ︙ | ︙ |
Changes to src/name.c.
| ︙ | ︙ | |||
36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
if( !fossil_isdigit(z[5]) ) return 0;
if( !fossil_isdigit(z[6]) ) return 0;
if( z[7]!='-') return 0;
if( !fossil_isdigit(z[8]) ) return 0;
if( !fossil_isdigit(z[9]) ) return 0;
return 1;
}
/*
** Return the RID that is the "root" of the branch that contains
** check-in "rid" if inBranch==0 or the first check-in in the branch
** if inBranch==1.
*/
int start_of_branch(int rid, int inBranch){
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 |
if( !fossil_isdigit(z[5]) ) return 0;
if( !fossil_isdigit(z[6]) ) return 0;
if( z[7]!='-') return 0;
if( !fossil_isdigit(z[8]) ) return 0;
if( !fossil_isdigit(z[9]) ) return 0;
return 1;
}
/*
** Check to see if the string might be a compact date/time that omits
** the punctuation. Example: "20190327084549" instead of
** "2019-03-27 08:45:49". If the string is of the appropriate form,
** then return an alternative string (in static space) that is the same
** string with punctuation inserted.
**
** If the bVerifyNotAHash flag is true, then a check is made to see if
** the string is a hash prefix and NULL is returned if it is. If the
** bVerifyNotAHash flag is false, then the result is determined by syntax
** of the input string only, without reference to the artifact table.
*/
char *fossil_expand_datetime(const char *zIn, int bVerifyNotAHash){
static char zEDate[20];
static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' };
int n = (int)strlen(zIn);
int i, j;
/* Only three forms allowed:
** (1) YYYYMMDD
** (2) YYYYMMDDHHMM
** (3) YYYYMMDDHHMMSS
*/
if( n!=8 && n!=12 && n!=14 ) return 0;
/* Every character must be a digit */
for(i=0; fossil_isdigit(zIn[i]); i++){}
if( i!=n ) return 0;
/* Expand the date */
for(i=j=0; zIn[i]; i++){
if( i>=4 && (i%2)==0 ){
zEDate[j++] = aPunct[i/2];
}
zEDate[j++] = zIn[i];
}
zEDate[j] = 0;
/* Check for reasonable date values.
** Offset references:
** YYYY-MM-DD HH:MM:SS
** 0123456789 12345678
*/
i = atoi(zEDate);
if( i<1970 || i>2100 ) return 0;
i = atoi(zEDate+5);
if( i<1 || i>12 ) return 0;
i = atoi(zEDate+8);
if( i<1 || i>31 ) return 0;
if( n>8 ){
i = atoi(zEDate+11);
if( i>24 ) return 0;
i = atoi(zEDate+14);
if( i>60 ) return 0;
if( n==14 && atoi(zEDate+17)>60 ) return 0;
}
/* The string is not also a hash prefix */
if( bVerifyNotAHash ){
if( db_exists("SELECT 1 FROM blob WHERE uuid GLOB '%q*'",zIn) ) return 0;
}
/* It looks like this may be a date. Return it with punctuation added. */
return zEDate;
}
/*
** Return the RID that is the "root" of the branch that contains
** check-in "rid" if inBranch==0 or the first check-in in the branch
** if inBranch==1.
*/
int start_of_branch(int rid, int inBranch){
|
| ︙ | ︙ | |||
112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
int vid;
int rid = 0;
int nTag;
int i;
int startOfBranch = 0;
const char *zXTag; /* zTag with optional [...] removed */
int nXTag; /* Size of zXTag */
if( zType==0 || zType[0]==0 ){
zType = "*";
}else if( zType[0]=='b' ){
zType = "ci";
startOfBranch = 1;
}
| > | 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
int vid;
int rid = 0;
int nTag;
int i;
int startOfBranch = 0;
const char *zXTag; /* zTag with optional [...] removed */
int nXTag; /* Size of zXTag */
const char *zDate; /* Expanded date-time string */
if( zType==0 || zType[0]==0 ){
zType = "*";
}else if( zType[0]=='b' ){
zType = "ci";
startOfBranch = 1;
}
|
| ︙ | ︙ | |||
148 149 150 151 152 153 154 155 156 157 158 |
" ORDER BY isprim DESC, mtime DESC", vid);
}
if( rid ) return rid;
}
/* Date and times */
if( memcmp(zTag, "date:", 5)==0 ){
rid = db_int(0,
"SELECT objid FROM event"
" WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'"
" ORDER BY mtime DESC LIMIT 1",
| > > | | 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 |
" ORDER BY isprim DESC, mtime DESC", vid);
}
if( rid ) return rid;
}
/* Date and times */
if( memcmp(zTag, "date:", 5)==0 ){
zDate = fossil_expand_datetime(&zTag[5],0);
if( zDate==0 ) zDate = &zTag[5];
rid = db_int(0,
"SELECT objid FROM event"
" WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'"
" ORDER BY mtime DESC LIMIT 1",
zDate, zType);
return rid;
}
if( fossil_isdate(zTag) ){
rid = db_int(0,
"SELECT objid FROM event"
" WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'"
" ORDER BY mtime DESC LIMIT 1",
|
| ︙ | ︙ | |||
281 282 283 284 285 286 287 288 289 290 291 292 293 294 |
" AND event.type GLOB '%q'",
zTag, zType
);
if( rid>0 ){
if( startOfBranch ) rid = start_of_branch(rid,1);
return rid;
}
/* Undocumented: numeric tags get translated directly into the RID */
if( memcmp(zTag, "rid:", 4)==0 ){
zTag += 4;
for(i=0; fossil_isdigit(zTag[i]); i++){}
if( zTag[i]==0 ){
if( strcmp(zType,"*")==0 ){
| > > > > > > > > > > > > | 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 |
" AND event.type GLOB '%q'",
zTag, zType
);
if( rid>0 ){
if( startOfBranch ) rid = start_of_branch(rid,1);
return rid;
}
/* Pure numeric date/time */
zDate = fossil_expand_datetime(zTag, 0);
if( zDate ){
rid = db_int(0,
"SELECT objid FROM event"
" WHERE mtime<=julianday(%Q,fromLocal()) AND type GLOB '%q'"
" ORDER BY mtime DESC LIMIT 1",
zDate, zType);
if( rid) return rid;
}
/* Undocumented: numeric tags get translated directly into the RID */
if( memcmp(zTag, "rid:", 4)==0 ){
zTag += 4;
for(i=0; fossil_isdigit(zTag[i]); i++){}
if( zTag[i]==0 ){
if( strcmp(zType,"*")==0 ){
|
| ︙ | ︙ | |||
610 611 612 613 614 615 616 |
case 't': zType = "Ticket-change"; break;
case 'g': zType = "Tag-change"; break;
default: zType = "Unknown"; break;
}
fossil_print("type: %s by %s on %s\n", zType, db_column_text(&q,2),
db_column_text(&q, 1));
fossil_print("comment: ");
| | | 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 |
case 't': zType = "Ticket-change"; break;
case 'g': zType = "Tag-change"; break;
default: zType = "Unknown"; break;
}
fossil_print("type: %s by %s on %s\n", zType, db_column_text(&q,2),
db_column_text(&q, 1));
fossil_print("comment: ");
comment_print(db_column_text(&q,3), 0, 12, -1, get_comment_format());
}
db_finalize(&q);
/* Check to see if this object is used as a file in a check-in */
db_prepare(&q,
"SELECT filename.name, blob.uuid, datetime(event.mtime,toLocal()),"
" coalesce(euser,user), coalesce(ecomment,comment)"
|
| ︙ | ︙ | |||
632 633 634 635 636 637 638 |
while( db_step(&q)==SQLITE_ROW ){
fossil_print("file: %s\n", db_column_text(&q,0));
fossil_print(" part of [%S] by %s on %s\n",
db_column_text(&q, 1),
db_column_text(&q, 3),
db_column_text(&q, 2));
fossil_print(" ");
| | | 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 |
while( db_step(&q)==SQLITE_ROW ){
fossil_print("file: %s\n", db_column_text(&q,0));
fossil_print(" part of [%S] by %s on %s\n",
db_column_text(&q, 1),
db_column_text(&q, 3),
db_column_text(&q, 2));
fossil_print(" ");
comment_print(db_column_text(&q,4), 0, 12, -1, get_comment_format());
}
db_finalize(&q);
/* Check to see if this object is used as an attachment */
db_prepare(&q,
"SELECT attachment.filename,"
" attachment.comment,"
|
| ︙ | ︙ | |||
667 668 669 670 671 672 673 |
}else{
fossil_print(" via %s\n",
db_column_text(&q,7));
}
fossil_print(" by user %s on %s\n",
db_column_text(&q,2), db_column_text(&q,3));
fossil_print(" ");
| | | 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 |
}else{
fossil_print(" via %s\n",
db_column_text(&q,7));
}
fossil_print(" by user %s on %s\n",
db_column_text(&q,2), db_column_text(&q,3));
fossil_print(" ");
comment_print(db_column_text(&q,1), 0, 12, -1, get_comment_format());
}
db_finalize(&q);
}
/*
** COMMAND: whatis*
**
|
| ︙ | ︙ |
Changes to src/printf.c.
| ︙ | ︙ | |||
227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
wikiFlags = WIKI_INLINE | WIKI_NOBADLINKS;
}else{
wikiFlags = WIKI_INLINE | WIKI_NOBLOCK | WIKI_NOBADLINKS;
}
if( db_get_boolean("timeline-plaintext", 0) ){
wikiFlags |= WIKI_LINKSONLY;
}
}
return wikiFlags;
}
/*
| > > > | 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
wikiFlags = WIKI_INLINE | WIKI_NOBADLINKS;
}else{
wikiFlags = WIKI_INLINE | WIKI_NOBLOCK | WIKI_NOBADLINKS;
}
if( db_get_boolean("timeline-plaintext", 0) ){
wikiFlags |= WIKI_LINKSONLY;
}
if( db_get_boolean("timeline-hard-newlines", 0) ){
wikiFlags |= WIKI_NEWLINE;
}
}
return wikiFlags;
}
/*
|
| ︙ | ︙ | |||
959 960 961 962 963 964 965 966 967 968 969 970 971 972 |
}else{
Blob b = empty_blob;
vxprintf(&b, zFormat, ap);
fossil_puts(blob_str(&b), 0);
blob_reset(&b);
}
va_end(ap);
}
/*
** Print a trace message on standard error.
*/
void fossil_trace(const char *zFormat, ...){
va_list ap;
| > > > > > > > > > > | 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 |
}else{
Blob b = empty_blob;
vxprintf(&b, zFormat, ap);
fossil_puts(blob_str(&b), 0);
blob_reset(&b);
}
va_end(ap);
}
void fossil_vprint(const char *zFormat, va_list ap){
if( g.cgiOutput ){
cgi_vprintf(zFormat, ap);
}else{
Blob b = empty_blob;
vxprintf(&b, zFormat, ap);
fossil_puts(blob_str(&b), 0);
blob_reset(&b);
}
}
/*
** Print a trace message on standard error.
*/
void fossil_trace(const char *zFormat, ...){
va_list ap;
|
| ︙ | ︙ |
Changes to src/rebuild.c.
| ︙ | ︙ | |||
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
*/
static int totalSize; /* Total number of artifacts to process */
static int processCnt; /* Number processed so far */
static int ttyOutput; /* Do progress output */
static Bag bagDone; /* Bag of records rebuilt */
static char *zFNameFormat; /* Format string for filenames on deconstruct */
static int prefixLength; /* Length of directory prefix for deconstruct */
/*
** Draw the percent-complete message.
** The input is actually the permill complete.
*/
static void percent_complete(int permill){
| > > > | 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
*/
static int totalSize; /* Total number of artifacts to process */
static int processCnt; /* Number processed so far */
static int ttyOutput; /* Do progress output */
static Bag bagDone; /* Bag of records rebuilt */
static char *zFNameFormat; /* Format string for filenames on deconstruct */
static int cchFNamePrefix; /* Length of directory prefix in zFNameFormat */
static char *zDestDir; /* Destination directory on deconstruct */
static int prefixLength; /* Length of directory prefix for deconstruct */
static int fKeepRid1; /* Flag to preserve RID=1 on de- and reconstruct */
/*
** Draw the percent-complete message.
** The input is actually the permill complete.
*/
static void percent_complete(int permill){
|
| ︙ | ︙ | |||
272 273 274 275 276 277 278 279 280 281 282 283 284 285 |
manifest_crosslink(rid, pUse, MC_NONE);
}else{
/* We are doing "fossil deconstruct" */
char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
char *zFile = mprintf(zFNameFormat /*works-like:"%s:%s"*/,
zUuid, zUuid+prefixLength);
blob_write_to_file(pUse,zFile);
free(zFile);
free(zUuid);
blob_reset(pUse);
}
assert( blob_is_reset(pUse) );
rebuild_step_done(rid);
| > > > > > > > > > > > | 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
manifest_crosslink(rid, pUse, MC_NONE);
}else{
/* We are doing "fossil deconstruct" */
char *zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
char *zFile = mprintf(zFNameFormat /*works-like:"%s:%s"*/,
zUuid, zUuid+prefixLength);
blob_write_to_file(pUse,zFile);
if( rid==1 && fKeepRid1!=0 ){
char *zFnDotRid1 = mprintf("%s/.rid1", zDestDir);
char *zFnRid1 = zFile + cchFNamePrefix + 1; /* Skip directory slash */
Blob bFileContents = empty_blob;
blob_appendf(&bFileContents,
"# The file holding the artifact with RID=1\n"
"%s\n", zFnRid1);
blob_write_to_file(&bFileContents, zFnDotRid1);
blob_reset(&bFileContents);
free(zFnDotRid1);
}
free(zFile);
free(zUuid);
blob_reset(pUse);
}
assert( blob_is_reset(pUse) );
rebuild_step_done(rid);
|
| ︙ | ︙ | |||
929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 |
void recon_read_dir(char *zPath){
DIR *d;
struct dirent *pEntry;
Blob aContent; /* content of the just read artifact */
static int nFileRead = 0;
void *zUnicodePath;
char *zUtf8Name;
zUnicodePath = fossil_utf8_to_path(zPath, 1);
d = opendir(zUnicodePath);
if( d ){
while( (pEntry=readdir(d))!=0 ){
Blob path;
char *zSubpath;
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 |
void recon_read_dir(char *zPath){
DIR *d;
struct dirent *pEntry;
Blob aContent; /* content of the just read artifact */
static int nFileRead = 0;
void *zUnicodePath;
char *zUtf8Name;
static int recursionLevel = 0; /* Bookkeeping about the recursion level */
static char *zFnRid1 = 0; /* The file holding the artifact with RID=1 */
static int cchPathInitial = 0; /* The length of zPath on first recursion */
recursionLevel++;
if( recursionLevel==1 ){
cchPathInitial = strlen(zPath);
if( fKeepRid1!=0 ){
char *zFnDotRid1 = mprintf("%s/.rid1", zPath);
Blob bFileContents;
if( blob_read_from_file(&bFileContents, zFnDotRid1, ExtFILE)!=-1 ){
Blob line, value;
while( blob_line(&bFileContents, &line)>0 ){
if( blob_token(&line, &value)==0 ) continue; /* Empty line */
if( blob_buffer(&value)[0]=='#' ) continue; /* Comment */
blob_trim(&value);
zFnRid1 = mprintf("%s/%s", zPath, blob_str(&value));
break;
}
blob_reset(&bFileContents);
if( zFnRid1 ){
if( blob_read_from_file(&aContent, zFnRid1, ExtFILE)==-1 ){
fossil_fatal("some unknown error occurred while reading \"%s\"",
zFnRid1);
}else{
recon_set_hash_policy(0, zFnRid1);
content_put(&aContent);
recon_restore_hash_policy();
blob_reset(&aContent);
fossil_print("\r%d", ++nFileRead);
fflush(stdout);
}
}else{
fossil_fatal("an error occurred while reading or parsing \"%s\"",
zFnDotRid1);
}
}
free(zFnDotRid1);
}
}
zUnicodePath = fossil_utf8_to_path(zPath, 1);
d = opendir(zUnicodePath);
if( d ){
while( (pEntry=readdir(d))!=0 ){
Blob path;
char *zSubpath;
|
| ︙ | ︙ | |||
951 952 953 954 955 956 957 |
if( (pEntry->d_type==DT_UNKNOWN || pEntry->d_type==DT_LNK)
? (file_isdir(zSubpath, ExtFILE)==1) : (pEntry->d_type==DT_DIR) )
#else
if( file_isdir(zSubpath, ExtFILE)==1 )
#endif
{
recon_read_dir(zSubpath);
| | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > | 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 |
if( (pEntry->d_type==DT_UNKNOWN || pEntry->d_type==DT_LNK)
? (file_isdir(zSubpath, ExtFILE)==1) : (pEntry->d_type==DT_DIR) )
#else
if( file_isdir(zSubpath, ExtFILE)==1 )
#endif
{
recon_read_dir(zSubpath);
}else if( fossil_strcmp(zSubpath, zFnRid1)!=0 ){
blob_init(&path, 0, 0);
blob_appendf(&path, "%s", zSubpath);
if( blob_read_from_file(&aContent, blob_str(&path), ExtFILE)==-1 ){
fossil_fatal("some unknown error occurred while reading \"%s\"",
blob_str(&path));
}
recon_set_hash_policy(cchPathInitial, blob_str(&path));
content_put(&aContent);
recon_restore_hash_policy();
blob_reset(&path);
blob_reset(&aContent);
fossil_print("\r%d", ++nFileRead);
fflush(stdout);
}
free(zSubpath);
}
closedir(d);
}else {
fossil_fatal("encountered error %d while trying to open \"%s\".",
errno, g.argv[3]);
}
fossil_path_free(zUnicodePath);
if( recursionLevel==1 && zFnRid1!=0 ) free(zFnRid1);
recursionLevel--;
}
/*
** Helper functions called from recon_read_dir() to set and restore the correct
** hash policy for an artifact read from disk, inferred from the length of the
** path name.
*/
static int saved_eHashPolicy = -1;
void recon_set_hash_policy(
const int cchPathPrefix, /* Directory prefix length for zUuidAsFilePath */
const char *zUuidAsFilePath /* Relative, well-formed, from recon_read_dir() */
){
int cchUuidAsFilePath;
const char *zHashPart;
int cchHashPart = 0;
int new_eHashPolicy = -1;
assert( HNAME_COUNT==2 ); /* Review function if new hashes are implemented. */
if( zUuidAsFilePath==0 ) return;
cchUuidAsFilePath = strlen(zUuidAsFilePath);
if( cchUuidAsFilePath==0 ) return;
if( cchPathPrefix>=cchUuidAsFilePath ) return;
for( zHashPart = zUuidAsFilePath + cchPathPrefix; *zHashPart; zHashPart++ ){
if( *zHashPart!='/' ) cchHashPart++;
}
if( cchHashPart>=HNAME_LEN_K256 ){
new_eHashPolicy = HPOLICY_SHA3;
}else if( cchHashPart>=HNAME_LEN_SHA1 ){
new_eHashPolicy = HPOLICY_SHA1;
}
if( new_eHashPolicy!=-1 ){
saved_eHashPolicy = g.eHashPolicy;
g.eHashPolicy = new_eHashPolicy;
}
}
void recon_restore_hash_policy(){
if( saved_eHashPolicy!=-1 ){
g.eHashPolicy = saved_eHashPolicy;
saved_eHashPolicy = -1;
}
}
#if 0
/*
** COMMAND: test-hash-from-path*
**
** Usage: %fossil test-hash-from-path ?OPTIONS? DESTINATION UUID
**
** Generate a sample path name from DESTINATION and UUID, as the `deconstruct'
** command would do. Then try to guess the hash policy from the path name, as
** the `reconstruct' command would do.
**
** No files or directories will be created.
**
** Options:
** -L|--prefixlength N Set the length of the names of the DESTINATION
** subdirectories to N.
*/
void test_hash_from_path_cmd(void) {
char *zDest;
char *zUuid;
char *zFile;
const char *zHashPolicy = "unknown";
const char *zPrefixOpt = find_option("prefixlength","L",1);
int iPrefixLength;
if( !zPrefixOpt ){
iPrefixLength = 2;
}else{
iPrefixLength = atoi(zPrefixOpt);
if( iPrefixLength<0 || iPrefixLength>9 ){
fossil_fatal("N(%s) is not a valid prefix length!",zPrefixOpt);
}
}
if( g.argc!=4 ){
usage ("?OPTIONS? DESTINATION UUID");
}
zDest = g.argv[2];
zUuid = g.argv[3];
if( iPrefixLength ){
zFNameFormat = mprintf("%s/%%.%ds/%%s",zDest,iPrefixLength);
}else{
zFNameFormat = mprintf("%s/%%s",zDest);
}
cchFNamePrefix = strlen(zDest);
zFile = mprintf(zFNameFormat /*works-like:"%s:%s"*/,
zUuid, zUuid+iPrefixLength);
recon_set_hash_policy(cchFNamePrefix,zFile);
if( saved_eHashPolicy!=-1 ){
zHashPolicy = hpolicy_name();
}
recon_restore_hash_policy();
fossil_print(
"\nPath Name: %s"
"\nHash Policy: %s\n",
zFile,zHashPolicy);
free(zFile);
free(zFNameFormat);
zFNameFormat = 0;
cchFNamePrefix = 0;
}
#endif
/*
** COMMAND: reconstruct*
**
** Usage: %fossil reconstruct ?OPTIONS? FILENAME DIRECTORY
**
** This command studies the artifacts (files) in DIRECTORY and
** reconstructs the fossil record from them. It places the new
** fossil repository in FILENAME. Subdirectories are read, files
** with leading '.' in the filename are ignored.
**
** Options:
** -K|--keep-rid1 Read the filename of the artifact with
** RID=1 from the file .rid in DIRECTORY.
**
** See also: deconstruct, rebuild
*/
void reconstruct_cmd(void) {
char *zPassword;
fKeepRid1 = find_option("keep-rid1","K",0)!=0;
if( g.argc!=4 ){
usage("FILENAME DIRECTORY");
}
if( file_isdir(g.argv[3], ExtFILE)!=1 ){
fossil_print("\"%s\" is not a directory\n\n", g.argv[3]);
usage("FILENAME DIRECTORY");
}
|
| ︙ | ︙ | |||
1039 1040 1041 1042 1043 1044 1045 | ** writes all artifacts to the file system. The DESTINATION directory ** will be populated with subdirectories AA and files AA/BBBBBBBBB.., where ** AABBBBBBBBB.. is the 40+ character artifact ID, AA the first 2 characters. ** If -L|--prefixlength is given, the length (default 2) of the directory ** prefix can be set to 0,1,..,9 characters. ** ** Options: | | > > | | < > | 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 |
** writes all artifacts to the file system. The DESTINATION directory
** will be populated with subdirectories AA and files AA/BBBBBBBBB.., where
** AABBBBBBBBB.. is the 40+ character artifact ID, AA the first 2 characters.
** If -L|--prefixlength is given, the length (default 2) of the directory
** prefix can be set to 0,1,..,9 characters.
**
** Options:
** -R|--repository REPOSITORY Deconstruct given REPOSITORY.
** -K|--keep-rid1 Save the filename of the artifact with RID=1 to
** the file .rid1 in the DESTINATION directory.
** -L|--prefixlength N Set the length of the names of the DESTINATION
** subdirectories to N.
** --private Include private artifacts.
**
** See also: rebuild, reconstruct
*/
void deconstruct_cmd(void){
const char *zPrefixOpt;
Stmt s;
int privateFlag;
fKeepRid1 = find_option("keep-rid1","K",0)!=0;
/* get and check prefix length argument and build format string */
zPrefixOpt=find_option("prefixlength","L",1);
if( !zPrefixOpt ){
prefixLength = 2;
}else{
if( zPrefixOpt[0]>='0' && zPrefixOpt[0]<='9' && !zPrefixOpt[1] ){
prefixLength = (int)(*zPrefixOpt-'0');
|
| ︙ | ︙ | |||
1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 |
*/
#endif
if( prefixLength ){
zFNameFormat = mprintf("%s/%%.%ds/%%s",zDestDir,prefixLength);
}else{
zFNameFormat = mprintf("%s/%%s",zDestDir);
}
bag_init(&bagDone);
ttyOutput = 1;
processCnt = 0;
if (!g.fQuiet) {
fossil_print("0 (0%%)...\r");
fflush(stdout);
| > | 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 |
*/
#endif
if( prefixLength ){
zFNameFormat = mprintf("%s/%%.%ds/%%s",zDestDir,prefixLength);
}else{
zFNameFormat = mprintf("%s/%%s",zDestDir);
}
cchFNamePrefix = strlen(zDestDir);
bag_init(&bagDone);
ttyOutput = 1;
processCnt = 0;
if (!g.fQuiet) {
fossil_print("0 (0%%)...\r");
fflush(stdout);
|
| ︙ | ︙ |
Changes to src/regexp.c.
| ︙ | ︙ | |||
11 12 13 14 15 16 17 | ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** | | | 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ ** ******************************************************************************* ** ** This file was adapted from the ext/misc/regexp.c file in SQLite3. That ** file is in the public domain. ** ** See ../www/grep.md for details of the algorithm and RE dialect. */ #include "config.h" #include "regexp.h" |
| ︙ | ︙ | |||
85 86 87 88 89 90 91 |
};
#endif
/* Add a state to the given state set if it is not already there */
static void re_add_state(ReStateSet *pSet, int newState){
unsigned i;
for(i=0; i<pSet->nState; i++) if( pSet->aState[i]==newState ) return;
| | | 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
};
#endif
/* Add a state to the given state set if it is not already there */
static void re_add_state(ReStateSet *pSet, int newState){
unsigned i;
for(i=0; i<pSet->nState; i++) if( pSet->aState[i]==newState ) return;
pSet->aState[pSet->nState++] = (ReStateNumber)newState;
}
/* Extract the next unicode character from *pzIn and return it. Advance
** *pzIn to the first byte past the end of the character returned. To
** be clear: this routine converts utf8 to unicode. This routine is
** optimized for the common case where the next character is a single byte.
*/
|
| ︙ | ︙ | |||
120 121 122 123 124 125 126 |
c = 0xfffd;
}
}
return c;
}
static unsigned re_next_char_nocase(ReInput *p){
unsigned c = re_next_char(p);
| | | 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
c = 0xfffd;
}
}
return c;
}
static unsigned re_next_char_nocase(ReInput *p){
unsigned c = re_next_char(p);
return unicode_fold(c,2);
}
/* Return true if c is a perl "word" character: [A-Za-z0-9_] */
static int re_word_char(int c){
return unicode_isalnum(c) || c=='_';
}
|
| ︙ | ︙ | |||
154 155 156 157 158 159 160 | int c = RE_EOF+1; int cPrev = 0; int rc = 0; ReInput in; in.z = zIn; in.i = 0; | | | | 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 |
int c = RE_EOF+1;
int cPrev = 0;
int rc = 0;
ReInput in;
in.z = zIn;
in.i = 0;
in.mx = nIn>=0 ? nIn : (int)strlen((char const*)zIn);
/* Look for the initial prefix match, if there is one. */
if( pRe->nInit ){
unsigned char x = pRe->zInit[0];
while( in.i+pRe->nInit<=in.mx
&& (zIn[in.i]!=x ||
strncmp((const char*)zIn+in.i, (const char*)pRe->zInit, pRe->nInit)!=0)
){
in.i++;
}
if( in.i+pRe->nInit>in.mx ) return 0;
}
if( pRe->nState<=(sizeof(aSpace)/(sizeof(aSpace[0])*2)) ){
pToFree = 0;
aStateSet[0].aState = aSpace;
}else{
pToFree = fossil_malloc( sizeof(ReStateNumber)*2*pRe->nState );
if( pToFree==0 ) return -1;
aStateSet[0].aState = pToFree;
}
|
| ︙ | ︙ | |||
305 306 307 308 309 310 311 |
int i;
if( p->nAlloc<=p->nState && re_resize(p, p->nAlloc*2) ) return 0;
for(i=p->nState; i>iBefore; i--){
p->aOp[i] = p->aOp[i-1];
p->aArg[i] = p->aArg[i-1];
}
p->nState++;
| | | 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 |
int i;
if( p->nAlloc<=p->nState && re_resize(p, p->nAlloc*2) ) return 0;
for(i=p->nState; i>iBefore; i--){
p->aOp[i] = p->aOp[i-1];
p->aArg[i] = p->aArg[i-1];
}
p->nState++;
p->aOp[iBefore] = (char)op;
p->aArg[iBefore] = arg;
return iBefore;
}
/* Append a new opcode and argument to the end of the RE under construction.
*/
static int re_append(ReCompiled *p, int op, int arg){
|
| ︙ | ︙ | |||
594 595 596 597 598 599 600 |
if( zIn[0]=='^' ){
zIn++;
}else{
re_append(pRe, RE_OP_ANYSTAR, 0);
}
pRe->sIn.z = (unsigned char*)zIn;
pRe->sIn.i = 0;
| | | 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 |
if( zIn[0]=='^' ){
zIn++;
}else{
re_append(pRe, RE_OP_ANYSTAR, 0);
}
pRe->sIn.z = (unsigned char*)zIn;
pRe->sIn.i = 0;
pRe->sIn.mx = (int)strlen(zIn);
zErr = re_subcompile_re(pRe);
if( zErr ){
re_free(pRe);
return zErr;
}
if( rePeek(pRe)=='$' && pRe->sIn.i+1>=pRe->sIn.mx ){
re_append(pRe, RE_OP_MATCH, RE_EOF);
|
| ︙ | ︙ | |||
624 625 626 627 628 629 630 |
** regex engine over the string. Do not worry able trying to match
** unicode characters beyond plane 0 - those are very rare and this is
** just an optimization. */
if( pRe->aOp[0]==RE_OP_ANYSTAR ){
for(j=0, i=1; j<sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){
unsigned x = pRe->aArg[i];
if( x<=127 ){
| | | | | 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 |
** regex engine over the string. Do not worry able trying to match
** unicode characters beyond plane 0 - those are very rare and this is
** just an optimization. */
if( pRe->aOp[0]==RE_OP_ANYSTAR ){
for(j=0, i=1; j<sizeof(pRe->zInit)-2 && pRe->aOp[i]==RE_OP_MATCH; i++){
unsigned x = pRe->aArg[i];
if( x<=127 ){
pRe->zInit[j++] = (unsigned char)x;
}else if( x<=0xfff ){
pRe->zInit[j++] = (unsigned char)(0xc0 | (x>>6));
pRe->zInit[j++] = 0x80 | (x&0x3f);
}else if( x<=0xffff ){
pRe->zInit[j++] = (unsigned char)(0xd0 | (x>>12));
pRe->zInit[j++] = 0x80 | ((x>>6)&0x3f);
pRe->zInit[j++] = 0x80 | (x&0x3f);
}else{
break;
}
}
if( j>0 && pRe->zInit[j-1]==0 ) j--;
|
| ︙ | ︙ | |||
660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 |
int argc,
sqlite3_value **argv
){
ReCompiled *pRe; /* Compiled regular expression */
const char *zPattern; /* The regular expression */
const unsigned char *zStr;/* String being searched */
const char *zErr; /* Compile error message */
pRe = sqlite3_get_auxdata(context, 0);
if( pRe==0 ){
zPattern = (const char*)sqlite3_value_text(argv[0]);
if( zPattern==0 ) return;
zErr = re_compile(&pRe, zPattern, 0);
if( zErr ){
sqlite3_result_error(context, zErr, -1);
return;
}
if( pRe==0 ){
sqlite3_result_error_nomem(context);
return;
}
| > > | > > > | < < < < < < < | 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 |
int argc,
sqlite3_value **argv
){
ReCompiled *pRe; /* Compiled regular expression */
const char *zPattern; /* The regular expression */
const unsigned char *zStr;/* String being searched */
const char *zErr; /* Compile error message */
int setAux = 0; /* True to invoke sqlite3_set_auxdata() */
pRe = sqlite3_get_auxdata(context, 0);
if( pRe==0 ){
zPattern = (const char*)sqlite3_value_text(argv[0]);
if( zPattern==0 ) return;
zErr = re_compile(&pRe, zPattern, 0);
if( zErr ){
re_free(pRe);
sqlite3_result_error(context, zErr, -1);
return;
}
if( pRe==0 ){
sqlite3_result_error_nomem(context);
return;
}
setAux = 1;
}
zStr = (const unsigned char*)sqlite3_value_text(argv[1]);
if( zStr!=0 ){
sqlite3_result_int(context, re_match(pRe, zStr, -1));
}
if( setAux ){
sqlite3_set_auxdata(context, 0, pRe, (void(*)(void*))re_free);
}
}
/*
** Invoke this routine to register the regexp() function with the
** SQLite database connection.
*/
int re_add_sql_func(sqlite3 *db){
return sqlite3_create_function(db, "regexp", 2, SQLITE_UTF8, 0,
re_sql_func, 0, 0);
}
/*
|
| ︙ | ︙ |
Changes to src/repolist.c.
| ︙ | ︙ | |||
43 44 45 46 47 48 49 50 | sqlite3_stmt *pStmt; int rc; pRepo->isValid = 0; pRepo->zProjName = 0; pRepo->rMTime = 0.0; rc = sqlite3_open(pRepo->zRepoName, &db); | > | | > > > < | 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
sqlite3_stmt *pStmt;
int rc;
pRepo->isValid = 0;
pRepo->zProjName = 0;
pRepo->rMTime = 0.0;
g.dbIgnoreErrors++;
rc = sqlite3_open(pRepo->zRepoName, &db);
if( rc ) goto finish_repo_list;
rc = sqlite3_prepare_v2(db, "SELECT value FROM config"
" WHERE name='project-name'",
-1, &pStmt, 0);
if( rc ) goto finish_repo_list;
if( sqlite3_step(pStmt)==SQLITE_ROW ){
pRepo->zProjName = fossil_strdup((char*)sqlite3_column_text(pStmt,0));
}
sqlite3_finalize(pStmt);
rc = sqlite3_prepare_v2(db, "SELECT max(mtime) FROM event", -1, &pStmt, 0);
if( rc==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
pRepo->rMTime = sqlite3_column_double(pStmt,0);
}
pRepo->isValid = 1;
sqlite3_finalize(pStmt);
finish_repo_list:
g.dbIgnoreErrors--;
sqlite3_close(db);
}
/*
** Generate a web-page that lists all repositories located under the
** g.zRepositoryName directory and return non-zero.
**
** For the special case when g.zRepositoryName is a non-chroot-jail "/",
|
| ︙ | ︙ |
Changes to src/schema.c.
1 2 3 4 5 6 | /* ** Copyright (c) 2007 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /* ** Copyright (c) 2007 D. Richard Hipp ** ** This program is free software; you can redistribute it and/or ** modify it under the terms of the Simplified BSD License (also ** known as the "2-Clause License" or "FreeBSD License".) ** ** This program is distributed in the hope that it will be useful, ** but without any warranty; without even the implied warranty of ** merchantability or fitness for a particular purpose. ** ** Author contact information: ** drh@hwaci.com ** http://www.hwaci.com/drh/ |
| ︙ | ︙ | |||
350 351 352 353 354 355 356 | @ -- used to reduce push operations to a single HTTP request in the @ -- common case when one repository only talks to a single server. @ -- @ CREATE TABLE unsent( @ rid INTEGER PRIMARY KEY -- Record ID of the phantom @ ); @ | | | 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 | @ -- used to reduce push operations to a single HTTP request in the @ -- common case when one repository only talks to a single server. @ -- @ CREATE TABLE unsent( @ rid INTEGER PRIMARY KEY -- Record ID of the phantom @ ); @ @ -- Each artifact can have one or more tags. A tag @ -- is defined by a row in the next table. @ -- @ -- Wiki pages are tagged with "wiki-NAME" where NAME is the name of @ -- the wiki page. Tickets changes are tagged with "ticket-UUID" where @ -- UUID is the indentifier of the ticket. Tags used to assign symbolic @ -- names to baselines are branches are of the form "sym-NAME" where @ -- NAME is the symbolic name. |
| ︙ | ︙ | |||
375 376 377 378 379 380 381 | @ INSERT INTO tag VALUES(6, 'private'); -- TAG_PRIVATE @ INSERT INTO tag VALUES(7, 'cluster'); -- TAG_CLUSTER @ INSERT INTO tag VALUES(8, 'branch'); -- TAG_BRANCH @ INSERT INTO tag VALUES(9, 'closed'); -- TAG_CLOSED @ INSERT INTO tag VALUES(10,'parent'); -- TAG_PARENT @ INSERT INTO tag VALUES(11,'note'); -- TAG_NOTE @ | | | 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 | @ INSERT INTO tag VALUES(6, 'private'); -- TAG_PRIVATE @ INSERT INTO tag VALUES(7, 'cluster'); -- TAG_CLUSTER @ INSERT INTO tag VALUES(8, 'branch'); -- TAG_BRANCH @ INSERT INTO tag VALUES(9, 'closed'); -- TAG_CLOSED @ INSERT INTO tag VALUES(10,'parent'); -- TAG_PARENT @ INSERT INTO tag VALUES(11,'note'); -- TAG_NOTE @ @ -- Assignments of tags to artifacts. Note that we allow tags to @ -- have values assigned to them. So we are not really dealing with @ -- tags here. These are really properties. But we are going to @ -- keep calling them tags because in many cases the value is ignored. @ -- @ CREATE TABLE tagxref( @ tagid INTEGER REFERENCES tag, -- The tag that added or removed @ tagtype INTEGER, -- 0:-,cancel 1:+,single 2:*,propagate |
| ︙ | ︙ | |||
457 458 459 460 461 462 463 464 465 466 467 468 469 470 | @ -- Add as many fields as required below this line @ login TEXT, @ username TEXT, @ mimetype TEXT, @ icomment TEXT @ ); @ CREATE INDEX ticketchng_idx1 ON ticketchng(tkt_id, tkt_mtime); ; /* ** Predefined tagid values */ #if INTERFACE # define TAG_BGCOLOR 1 /* Set the background color for display */ | > > > > > > > > > | 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 | @ -- Add as many fields as required below this line @ login TEXT, @ username TEXT, @ mimetype TEXT, @ icomment TEXT @ ); @ CREATE INDEX ticketchng_idx1 ON ticketchng(tkt_id, tkt_mtime); @ @ -- For tracking cherrypick merges @ CREATE TABLE cherrypick( @ parentid INT, @ childid INT, @ isExclude BOOLEAN DEFAULT false, @ PRIMARY KEY(parentid, childid) @ ) WITHOUT ROWID; @ CREATE INDEX cherrypick_cid ON cherrypick(childid); ; /* ** Predefined tagid values */ #if INTERFACE # define TAG_BGCOLOR 1 /* Set the background color for display */ |
| ︙ | ︙ | |||
479 480 481 482 483 484 485 | # define TAG_PARENT 10 /* Change to parentage on a check-in */ # define TAG_NOTE 11 /* Extra text appended to a check-in comment */ #endif /* ** The schema for the local FOSSIL database file found at the root ** of every check-out. This database contains the complete state of | | | 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 | # define TAG_PARENT 10 /* Change to parentage on a check-in */ # define TAG_NOTE 11 /* Extra text appended to a check-in comment */ #endif /* ** The schema for the local FOSSIL database file found at the root ** of every check-out. This database contains the complete state of ** the checkout. See also the addendum in zLocalSchemaVmerge[]. */ const char zLocalSchema[] = @ -- The VVAR table holds miscellanous information about the local database @ -- in the form of name-value pairs. This is similar to the VAR table @ -- table in the repository except that this table holds information that @ -- is specific to the local checkout. @ -- |
| ︙ | ︙ | |||
504 505 506 507 508 509 510 | @ @ -- Each entry in the vfile table represents a single file in the @ -- current checkout. @ -- @ -- The file.rid field is 0 for files or folders that have been @ -- added but not yet committed. @ -- | | | > | | | < < < | > > > > > > > > > > > > | > | > > > | > > > > > | > | 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 | @ @ -- Each entry in the vfile table represents a single file in the @ -- current checkout. @ -- @ -- The file.rid field is 0 for files or folders that have been @ -- added but not yet committed. @ -- @ -- Vfile.chnged meaning: @ -- 0 File is unmodified @ -- 1 Manually edited and/or modified as part of a merge command @ -- 2 Replaced by a merge command @ -- 3 Added by a merge command @ -- 4,5 Same as 2,3 except merge using --integrate @ -- @ CREATE TABLE vfile( @ id INTEGER PRIMARY KEY, -- ID of the checked out file @ vid INTEGER REFERENCES blob, -- The checkin this file is part of. @ chnged INT DEFAULT 0, -- 0:unchng 1:edit 2:m-chng 3:m-add 4:i-chng 5:i-add @ deleted BOOLEAN DEFAULT 0, -- True if deleted @ isexe BOOLEAN, -- True if file should be executable @ islink BOOLEAN, -- True if file should be symlink @ rid INTEGER, -- Originally from this repository record @ mrid INTEGER, -- Based on this record due to a merge @ mtime INTEGER, -- Mtime of file on disk. sec since 1970 @ pathname TEXT, -- Full pathname relative to root @ origname TEXT, -- Original pathname. NULL if unchanged @ mhash TEXT, -- Hash of mrid iff mrid!=rid @ UNIQUE(pathname,vid) @ ); @ @ -- Identifier for this file type. @ -- The integer is the same as 'FSLC'. @ PRAGMA application_id=252006674; ; /* Additional local database initialization following the schema ** enhancement of 2019-01-19, in which the mhash column was added ** to vmerge and vfile. */ const char zLocalSchemaVmerge[] = @ -- This table holds a record of uncommitted merges in the local @ -- file tree. If a VFILE entry with id has merged with another @ -- record, there is an entry in this table with (id,merge) where @ -- merge is the RECORD table entry that the file merged against. @ -- An id of 0 or <-3 here means the version record itself. When @ -- id==(-1) that is a cherrypick merge, id==(-2) that is a @ -- backout merge and id==(-4) is a integrate merge. @ -- @ @ CREATE TABLE vmerge( @ id INTEGER REFERENCES vfile, -- VFILE entry that has been merged @ merge INTEGER, -- Merged with this record @ mhash TEXT -- SHA1/SHA3 hash for merge object @ ); @ CREATE UNIQUE INDEX vmergex1 ON vmerge(id,mhash); @ @ -- The following trigger will prevent older versions of Fossil that @ -- do not know about the new vmerge.mhash column from updating the @ -- vmerge table. This must be done with a trigger, since legacy Fossil @ -- uses INSERT OR IGNORE to update vmerge, and the OR IGNORE will cause @ -- a NOT NULL constraint to be silently ignored. @ @ CREATE TRIGGER vmerge_ck1 AFTER INSERT ON vmerge @ WHEN new.mhash IS NULL BEGIN @ SELECT raise(FAIL, @ 'trying to update a newer checkout with an older version of Fossil'); @ END; @ ; /* ** The following table holds information about forum posts. It ** is created on-demand whenever the manifest parser encounters ** a forum-post artifact. */ |
| ︙ | ︙ |
Changes to src/security_audit.c.
| ︙ | ︙ | |||
139 140 141 142 143 144 145 |
@ </ul>
if( zPubPages && zPubPages[0] ){
@ <p>Change GLOB patterns exceptions using the "Public pages" setting
@ on the <a href="setup_access">Access Settings</a> page.</p>
}
}
| | | | > | | | > > | 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 |
@ </ul>
if( zPubPages && zPubPages[0] ){
@ <p>Change GLOB patterns exceptions using the "Public pages" setting
@ on the <a href="setup_access">Access Settings</a> page.</p>
}
}
/* Make sure the HTTPS is required for login, at least, so that the
** password does not go across the Internet in the clear.
*/
if( db_get_int("redirect-to-https",0)==0 ){
@ <li><p><b>WARNING:</b>
@ Sensitive material such as login passwords can be sent over an
@ unencrypted connection.
@ <p>Fix this by changing the "Redirect to HTTPS" setting on the
@ <a href="setup_access">Access Control</a> page. If you were using
@ the old "Redirect to HTTPS on Login Page" setting, switch to the
@ new setting: it has a more secure implementation.
}
/* Anonymous users should not be able to harvest email addresses
** from tickets.
*/
if( hasAnyCap(zAnonCap, "e") ){
@ <li><p><b>WARNING:</b>
|
| ︙ | ︙ | |||
283 284 285 286 287 288 289 |
@ <li><p>
@ Users with administrator privilege are: %s(z)
fossil_free(z);
if( n>3 ){
@ <li><p><b>WARNING:</b>
@ Administrator privilege is granted to
@ <a href='setup_ulist?with=as'>%d(n) users</a>.
| | | 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 |
@ <li><p>
@ Users with administrator privilege are: %s(z)
fossil_free(z);
if( n>3 ){
@ <li><p><b>WARNING:</b>
@ Administrator privilege is granted to
@ <a href='setup_ulist?with=as'>%d(n) users</a>.
@ Ideally, administrator privilege ('s' or 'a') should only
@ be granted to one or two users.
}
}
/* The push-unversioned privilege should only be provided to
** specific individuals, not to entire classes of people.
** And no too many people should have this privilege.
|
| ︙ | ︙ |
Changes to src/setup.c.
| ︙ | ︙ | |||
117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
"Timeline display preferences");
if( setup_user ){
setup_menu_entry("Login-Group", "setup_login_group",
"Manage single sign-on between this repository and others"
" on the same server");
setup_menu_entry("Tickets", "tktsetup",
"Configure the trouble-ticketing system for this repository");
}
setup_menu_entry("Search","srchsetup",
"Configure the built-in search engine");
setup_menu_entry("URL Aliases", "waliassetup",
"Configure URL aliases");
if( setup_user ){
setup_menu_entry("Notification", "setup_notification",
| > > | 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
"Timeline display preferences");
if( setup_user ){
setup_menu_entry("Login-Group", "setup_login_group",
"Manage single sign-on between this repository and others"
" on the same server");
setup_menu_entry("Tickets", "tktsetup",
"Configure the trouble-ticketing system for this repository");
setup_menu_entry("Wiki", "setup_wiki",
"Configure the wiki for this repository");
}
setup_menu_entry("Search","srchsetup",
"Configure the built-in search engine");
setup_menu_entry("URL Aliases", "waliassetup",
"Configure URL aliases");
if( setup_user ){
setup_menu_entry("Notification", "setup_notification",
|
| ︙ | ︙ | |||
280 281 282 283 284 285 286 | */ void multiple_choice_attribute( const char *zLabel, /* The text label on the menu */ const char *zVar, /* The corresponding row in the VAR table */ const char *zQP, /* The query parameter */ const char *zDflt, /* Default value if VAR table entry does not exist */ int nChoice, /* Number of choices */ | | | 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 |
*/
void multiple_choice_attribute(
const char *zLabel, /* The text label on the menu */
const char *zVar, /* The corresponding row in the VAR table */
const char *zQP, /* The query parameter */
const char *zDflt, /* Default value if VAR table entry does not exist */
int nChoice, /* Number of choices */
const char *const *azChoice /* Choices in pairs (VAR value, Display) */
){
const char *z = db_get(zVar, zDflt);
const char *zQ = P(zQP);
int i;
if( zQ && fossil_strcmp(zQ,z)!=0){
const int nZQ = (int)strlen(zQ);
login_verify_csrf_secret();
|
| ︙ | ︙ | |||
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 |
/*
** WEBPAGE: setup_access
**
** The access-control settings page. Requires Setup privileges.
*/
void setup_access(void){
login_check_credentials();
if( !g.perm.Setup ){
login_needed(0);
return;
}
style_header("Access Control Settings");
db_begin_transaction();
@ <form action="%s(g.zTop)/setup_access" method="post"><div>
login_insert_csrf_secret();
@ <input type="submit" name="submit" value="Apply Changes" /></p>
@ <hr />
| > > > > > | | > | > > | | < | > | 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 |
/*
** WEBPAGE: setup_access
**
** The access-control settings page. Requires Setup privileges.
*/
void setup_access(void){
static const char * const azRedirectOpts[] = {
"0", "Off",
"1", "Login Page Only",
"2", "All Pages"
};
login_check_credentials();
if( !g.perm.Setup ){
login_needed(0);
return;
}
style_header("Access Control Settings");
db_begin_transaction();
@ <form action="%s(g.zTop)/setup_access" method="post"><div>
login_insert_csrf_secret();
@ <input type="submit" name="submit" value="Apply Changes" /></p>
@ <hr />
multiple_choice_attribute("Redirect to HTTPS",
"redirect-to-https", "redirhttps", "0",
count(azRedirectOpts)/2, azRedirectOpts);
@ <p>Force the use of HTTPS by redirecting to HTTPS when an
@ unencrypted request is received. This feature can be enabled
@ for the Login page only, or for all pages.
@ <p>Further details: When enabled, this option causes the $secureurl TH1
@ variable is set to an "https:" variant of $baseurl. Otherwise,
@ $secureurl is just an alias for $baseurl.
@ (Property: "redirect-to-https". "0" for off, "1" for Login page only,
@ "2" otherwise.)
@ <hr />
onoff_attribute("Require password for local access",
"localauth", "localauth", 0, 0);
@ <p>When enabled, the password sign-in is always required for
@ web access. When disabled, unrestricted web access from 127.0.0.1
@ is allowed for the <a href="%R/help/ui">fossil ui</a> command or
@ from the <a href="%R/help/server">fossil server</a>,
|
| ︙ | ︙ | |||
688 689 690 691 692 693 694 |
"timeline-plaintext", "tpt", 0, 0);
@ <p>In timeline displays, check-in comments are displayed literally,
@ without any wiki or HTML interpretation. Use CSS to change
@ display formatting features such as fonts and line-wrapping behavior.
@ (Property: "timeline-plaintext")</p>
@ <hr />
| | > | > > > > > > > | 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 |
"timeline-plaintext", "tpt", 0, 0);
@ <p>In timeline displays, check-in comments are displayed literally,
@ without any wiki or HTML interpretation. Use CSS to change
@ display formatting features such as fonts and line-wrapping behavior.
@ (Property: "timeline-plaintext")</p>
@ <hr />
onoff_attribute("Truncate comment at first blank line (Git-style)",
"timeline-truncate-at-blank", "ttb", 0, 0);
@ <p>In timeline displays, check-in comments are displayed only through
@ the first blank line. This is the traditional way to display comments
@ in Git repositories (Property: "timeline-truncate-at-blank")</p>
@ <hr />
onoff_attribute("Break comments at newline characters",
"timeline-hard-newlines", "thnl", 0, 0);
@ <p>In timeline displays, newline characters in check-in comments force
@ a line break on the display.
@ (Property: "timeline-hard-newlines")</p>
@ <hr />
onoff_attribute("Use Universal Coordinated Time (UTC)",
"timeline-utc", "utc", 1, 0);
@ <p>Show times as UTC (also sometimes called Greenwich Mean Time (GMT) or
@ Zulu) instead of in local time. On this server, local time is currently
tmDiff = db_double(0.0, "SELECT julianday('now')");
|
| ︙ | ︙ | |||
872 873 874 875 876 877 878 | @ a convenient place for newbies @ to download a ZIP archive or a tarball of the project. By default, @ the latest trunk check-in is downloaded. Change this tag to something @ else (ex: release) to alter the behavior of the /download page. @ (Property: "download-tag") @ </p> @ <hr /> | < < < < < < < | 890 891 892 893 894 895 896 897 898 899 900 901 902 903 |
@ a convenient place for newbies
@ to download a ZIP archive or a tarball of the project. By default,
@ the latest trunk check-in is downloaded. Change this tag to something
@ else (ex: release) to alter the behavior of the /download page.
@ (Property: "download-tag")
@ </p>
@ <hr />
entry_attribute("Index Page", 60, "index-page", "idxpg", "/home", 0);
@ <p>Enter the pathname of the page to display when the "Home" menu
@ option is selected and when no pathname is
@ specified in the URL. For example, if you visit the url:</p>
@
@ <blockquote><p>%h(g.zBaseURL)</p></blockquote>
@
|
| ︙ | ︙ | |||
920 921 922 923 924 925 926 927 928 929 930 931 932 933 |
@ (Property: sitemap-download)<br>
entry_attribute("License", 40, "sitemap-license", "smlicense",
"", 0);
@ (Property: sitemap-license)<br>
entry_attribute("Contact", 40, "sitemap-contact", "smcontact",
"", 0);
@ (Property: sitemap-contact)
@ <hr />
onoff_attribute("Use HTML as wiki markup language",
"wiki-use-html", "wiki-use-html", 0, 0);
@ <p>Use HTML as the wiki markup language. Wiki links will still be parsed
@ but all other wiki formatting will be ignored. This option is helpful
@ if you have chosen to use a rich HTML editor for wiki markup such as
@ TinyMCE.</p>
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 |
@ (Property: sitemap-download)<br>
entry_attribute("License", 40, "sitemap-license", "smlicense",
"", 0);
@ (Property: sitemap-license)<br>
entry_attribute("Contact", 40, "sitemap-contact", "smcontact",
"", 0);
@ (Property: sitemap-contact)
@ <hr />
@ <p><input type="submit" name="submit" value="Apply Changes" /></p>
@ </div></form>
db_end_transaction(0);
style_footer();
}
/*
** WEBPAGE: setup_wiki
**
** The "Admin/Wiki" page. Requires Setup privilege.
*/
void setup_wiki(void){
login_check_credentials();
if( !g.perm.Setup ){
login_needed(0);
return;
}
style_header("Wiki Configuration");
db_begin_transaction();
@ <form action="%s(g.zTop)/setup_wiki" method="post"><div>
login_insert_csrf_secret();
@ <input type="submit" name="submit" value="Apply Changes" /></p>
@ <hr />
onoff_attribute("Associate Wiki Pages With Branches, Tags, or Checkins",
"wiki-about", "wiki-about", 1, 0);
@ <p>
@ Associate wiki pages with branches, tags, or checkins, based on
@ the wiki page name. Wiki pages that begin with "branch/", "checkin/"
@ or "tag/" and which continue with the name of an existing branch, checkin
@ or tag are treated specially when this feature is enabled.
@ <ul>
@ <li> <b>branch/</b><i>branch-name</i>
@ <li> <b>checkin/</b><i>full-checkin-hash</i>
@ <li> <b>tag/</b><i>tag-name</i>
@ </ul>
@ (Property: "wiki-about")</p>
@ <hr />
onoff_attribute("Enable WYSIWYG Wiki Editing",
"wysiwyg-wiki", "wysiwyg-wiki", 0, 0);
@ <p>Enable what-you-see-is-what-you-get (WYSIWYG) editing of wiki pages.
@ The WYSIWYG editor generates HTML instead of markup, which makes
@ subsequent manual editing more difficult.
@ (Property: "wysiwyg-wiki")</p>
@ <hr />
onoff_attribute("Use HTML as wiki markup language",
"wiki-use-html", "wiki-use-html", 0, 0);
@ <p>Use HTML as the wiki markup language. Wiki links will still be parsed
@ but all other wiki formatting will be ignored. This option is helpful
@ if you have chosen to use a rich HTML editor for wiki markup such as
@ TinyMCE.</p>
|
| ︙ | ︙ |
Changes to src/shell.c.
| ︙ | ︙ | |||
152 153 154 155 156 157 158 159 160 161 162 163 164 165 | # define isatty(h) _isatty(h) # ifndef access # define access(f,m) _access((f),(m)) # endif # ifndef unlink # define unlink _unlink # endif # undef popen # define popen _popen # undef pclose # define pclose _pclose #else /* Make sure isatty() has a prototype. */ extern int isatty(int); | > > > | 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 | # define isatty(h) _isatty(h) # ifndef access # define access(f,m) _access((f),(m)) # endif # ifndef unlink # define unlink _unlink # endif # ifndef strdup # define strdup _strdup # endif # undef popen # define popen _popen # undef pclose # define pclose _pclose #else /* Make sure isatty() has a prototype. */ extern int isatty(int); |
| ︙ | ︙ | |||
2139 2140 2141 2142 2143 2144 2145 | #define FSDIR_COLUMN_DATA 3 /* File content */ #define FSDIR_COLUMN_PATH 4 /* Path to top of search */ #define FSDIR_COLUMN_DIR 5 /* Path is relative to this directory */ /* ** Set the result stored by context ctx to a blob containing the | | > > > > > > > | > > | > > > > > > > > > > | | > > > > > | > | 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 |
#define FSDIR_COLUMN_DATA 3 /* File content */
#define FSDIR_COLUMN_PATH 4 /* Path to top of search */
#define FSDIR_COLUMN_DIR 5 /* Path is relative to this directory */
/*
** Set the result stored by context ctx to a blob containing the
** contents of file zName. Or, leave the result unchanged (NULL)
** if the file does not exist or is unreadable.
**
** If the file exceeds the SQLite blob size limit, through an
** SQLITE_TOOBIG error.
**
** Throw an SQLITE_IOERR if there are difficulties pulling the file
** off of disk.
*/
static void readFileContents(sqlite3_context *ctx, const char *zName){
FILE *in;
sqlite3_int64 nIn;
void *pBuf;
sqlite3 *db;
int mxBlob;
in = fopen(zName, "rb");
if( in==0 ){
/* File does not exist or is unreadable. Leave the result set to NULL. */
return;
}
fseek(in, 0, SEEK_END);
nIn = ftell(in);
rewind(in);
db = sqlite3_context_db_handle(ctx);
mxBlob = sqlite3_limit(db, SQLITE_LIMIT_LENGTH, -1);
if( nIn>mxBlob ){
sqlite3_result_error_code(ctx, SQLITE_TOOBIG);
fclose(in);
return;
}
pBuf = sqlite3_malloc64( nIn ? nIn : 1 );
if( pBuf==0 ){
sqlite3_result_error_nomem(ctx);
fclose(in);
return;
}
if( nIn==(sqlite3_int64)fread(pBuf, 1, (size_t)nIn, in) ){
sqlite3_result_blob64(ctx, pBuf, nIn, sqlite3_free);
}else{
sqlite3_result_error_code(ctx, SQLITE_IOERR);
sqlite3_free(pBuf);
}
fclose(in);
}
/*
** Implementation of the "readfile(X)" SQL function. The entire content
|
| ︙ | ︙ | |||
2286 2287 2288 2289 2290 2291 2292 | #endif } /* ** Argument zFile is the name of a file that will be created and/or written ** by SQL function writefile(). This function ensures that the directory ** zFile will be written to exists, creating it if required. The permissions | | > | < | | 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 |
#endif
}
/*
** Argument zFile is the name of a file that will be created and/or written
** by SQL function writefile(). This function ensures that the directory
** zFile will be written to exists, creating it if required. The permissions
** for any path components created by this function are set in accordance
** with the current umask.
**
** If an OOM condition is encountered, SQLITE_NOMEM is returned. Otherwise,
** SQLITE_OK is returned if the directory is successfully created, or
** SQLITE_ERROR otherwise.
*/
static int makeDirectory(
const char *zFile
){
char *zCopy = sqlite3_mprintf("%s", zFile);
int rc = SQLITE_OK;
if( zCopy==0 ){
rc = SQLITE_NOMEM;
}else{
int nCopy = (int)strlen(zCopy);
int i = 1;
while( rc==SQLITE_OK ){
struct stat sStat;
int rc2;
for(; zCopy[i]!='/' && i<nCopy; i++);
if( i==nCopy ) break;
zCopy[i] = '\0';
rc2 = fileStat(zCopy, &sStat);
if( rc2!=0 ){
if( mkdir(zCopy, 0777) ) rc = SQLITE_ERROR;
}else{
if( !S_ISDIR(sStat.st_mode) ) rc = SQLITE_ERROR;
}
zCopy[i] = '/';
i++;
}
|
| ︙ | ︙ | |||
2473 2474 2475 2476 2477 2478 2479 |
}
if( argc==4 ){
mtime = sqlite3_value_int64(argv[3]);
}
res = writeFile(context, zFile, argv[1], mode, mtime);
if( res==1 && errno==ENOENT ){
| | | 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 |
}
if( argc==4 ){
mtime = sqlite3_value_int64(argv[3]);
}
res = writeFile(context, zFile, argv[1], mode, mtime);
if( res==1 && errno==ENOENT ){
if( makeDirectory(zFile)==SQLITE_OK ){
res = writeFile(context, zFile, argv[1], mode, mtime);
}
}
if( argc>2 && res!=0 ){
if( S_ISLNK(mode) ){
ctxErrorMsg(context, "failed to create symlink: %s", zFile);
|
| ︙ | ︙ | |||
2664 2665 2666 2667 2668 2669 2670 |
pCur->iRowid++;
if( S_ISDIR(m) ){
/* Descend into this directory */
int iNew = pCur->iLvl + 1;
FsdirLevel *pLvl;
if( iNew>=pCur->nLvl ){
int nNew = iNew+1;
| | | | 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 |
pCur->iRowid++;
if( S_ISDIR(m) ){
/* Descend into this directory */
int iNew = pCur->iLvl + 1;
FsdirLevel *pLvl;
if( iNew>=pCur->nLvl ){
int nNew = iNew+1;
sqlite3_int64 nByte = nNew*sizeof(FsdirLevel);
FsdirLevel *aNew = (FsdirLevel*)sqlite3_realloc64(pCur->aLvl, nByte);
if( aNew==0 ) return SQLITE_NOMEM;
memset(&aNew[pCur->nLvl], 0, sizeof(FsdirLevel)*(nNew-pCur->nLvl));
pCur->aLvl = aNew;
pCur->nLvl = nNew;
}
pCur->iLvl = iNew;
pLvl = &pCur->aLvl[iNew];
|
| ︙ | ︙ | |||
2745 2746 2747 2748 2749 2750 2751 |
mode_t m = pCur->sStat.st_mode;
if( S_ISDIR(m) ){
sqlite3_result_null(ctx);
#if !defined(_WIN32) && !defined(WIN32)
}else if( S_ISLNK(m) ){
char aStatic[64];
char *aBuf = aStatic;
| | | | 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 |
mode_t m = pCur->sStat.st_mode;
if( S_ISDIR(m) ){
sqlite3_result_null(ctx);
#if !defined(_WIN32) && !defined(WIN32)
}else if( S_ISLNK(m) ){
char aStatic[64];
char *aBuf = aStatic;
sqlite3_int64 nBuf = 64;
int n;
while( 1 ){
n = readlink(pCur->zPath, aBuf, nBuf);
if( n<nBuf ) break;
if( aBuf!=aStatic ) sqlite3_free(aBuf);
nBuf = nBuf*2;
aBuf = sqlite3_malloc64(nBuf);
if( aBuf==0 ){
sqlite3_result_error_nomem(ctx);
return SQLITE_NOMEM;
}
}
sqlite3_result_text(ctx, aBuf, n, SQLITE_TRANSIENT);
|
| ︙ | ︙ | |||
4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 | } #endif if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY; return rc; } /************************* End ../ext/misc/appendvfs.c ********************/ #ifdef SQLITE_HAVE_ZLIB /************************* Begin ../ext/misc/zipfile.c ******************/ /* ** 2017-12-26 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 4085 4086 4087 4088 4089 4090 4091 4092 4093 4094 4095 4096 4097 4098 4099 4100 4101 4102 4103 4104 4105 4106 4107 4108 4109 4110 4111 4112 4113 4114 4115 4116 4117 4118 4119 4120 4121 4122 4123 4124 4125 4126 4127 4128 4129 4130 4131 4132 4133 4134 4135 4136 4137 4138 4139 4140 4141 4142 4143 4144 4145 4146 4147 4148 4149 4150 4151 4152 4153 4154 4155 4156 4157 4158 4159 4160 4161 4162 4163 4164 4165 4166 4167 4168 4169 4170 4171 4172 4173 4174 4175 4176 4177 4178 4179 4180 4181 4182 4183 4184 4185 4186 4187 4188 4189 4190 4191 4192 4193 4194 4195 4196 4197 4198 4199 4200 4201 4202 4203 4204 4205 4206 4207 4208 4209 |
}
#endif
if( rc==SQLITE_OK ) rc = SQLITE_OK_LOAD_PERMANENTLY;
return rc;
}
/************************* End ../ext/misc/appendvfs.c ********************/
/************************* Begin ../ext/misc/memtrace.c ******************/
/*
** 2019-01-21
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
**
** May you do good and not evil.
** May you find forgiveness for yourself and forgive others.
** May you share freely, never taking more than you give.
**
*************************************************************************
**
** This file implements an extension that uses the SQLITE_CONFIG_MALLOC
** mechanism to add a tracing layer on top of SQLite. If this extension
** is registered prior to sqlite3_initialize(), it will cause all memory
** allocation activities to be logged on standard output, or to some other
** FILE specified by the initializer.
**
** This file needs to be compiled into the application that uses it.
**
** This extension is used to implement the --memtrace option of the
** command-line shell.
*/
#include <assert.h>
#include <string.h>
#include <stdio.h>
/* The original memory allocation routines */
static sqlite3_mem_methods memtraceBase;
static FILE *memtraceOut;
/* Methods that trace memory allocations */
static void *memtraceMalloc(int n){
if( memtraceOut ){
fprintf(memtraceOut, "MEMTRACE: allocate %d bytes\n",
memtraceBase.xRoundup(n));
}
return memtraceBase.xMalloc(n);
}
static void memtraceFree(void *p){
if( p==0 ) return;
if( memtraceOut ){
fprintf(memtraceOut, "MEMTRACE: free %d bytes\n", memtraceBase.xSize(p));
}
memtraceBase.xFree(p);
}
static void *memtraceRealloc(void *p, int n){
if( p==0 ) return memtraceMalloc(n);
if( n==0 ){
memtraceFree(p);
return 0;
}
if( memtraceOut ){
fprintf(memtraceOut, "MEMTRACE: resize %d -> %d bytes\n",
memtraceBase.xSize(p), memtraceBase.xRoundup(n));
}
return memtraceBase.xRealloc(p, n);
}
static int memtraceSize(void *p){
return memtraceBase.xSize(p);
}
static int memtraceRoundup(int n){
return memtraceBase.xRoundup(n);
}
static int memtraceInit(void *p){
return memtraceBase.xInit(p);
}
static void memtraceShutdown(void *p){
memtraceBase.xShutdown(p);
}
/* The substitute memory allocator */
static sqlite3_mem_methods ersaztMethods = {
memtraceMalloc,
memtraceFree,
memtraceRealloc,
memtraceSize,
memtraceRoundup,
memtraceInit,
memtraceShutdown,
0
};
/* Begin tracing memory allocations to out. */
int sqlite3MemTraceActivate(FILE *out){
int rc = SQLITE_OK;
if( memtraceBase.xMalloc==0 ){
rc = sqlite3_config(SQLITE_CONFIG_GETMALLOC, &memtraceBase);
if( rc==SQLITE_OK ){
rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &ersaztMethods);
}
}
memtraceOut = out;
return rc;
}
/* Deactivate memory tracing */
int sqlite3MemTraceDeactivate(void){
int rc = SQLITE_OK;
if( memtraceBase.xMalloc!=0 ){
rc = sqlite3_config(SQLITE_CONFIG_MALLOC, &memtraceBase);
if( rc==SQLITE_OK ){
memset(&memtraceBase, 0, sizeof(memtraceBase));
}
}
memtraceOut = 0;
return rc;
}
/************************* End ../ext/misc/memtrace.c ********************/
#ifdef SQLITE_HAVE_ZLIB
/************************* Begin ../ext/misc/zipfile.c ******************/
/*
** 2017-12-26
**
** The author disclaims copyright to this source code. In place of
** a legal notice, here is a blessing:
|
| ︙ | ︙ | |||
4418 4419 4420 4421 4422 4423 4424 |
if( argc>3 ){
zFile = argv[3];
nFile = (int)strlen(zFile)+1;
}
rc = sqlite3_declare_vtab(db, ZIPFILE_SCHEMA);
if( rc==SQLITE_OK ){
| | | 4557 4558 4559 4560 4561 4562 4563 4564 4565 4566 4567 4568 4569 4570 4571 |
if( argc>3 ){
zFile = argv[3];
nFile = (int)strlen(zFile)+1;
}
rc = sqlite3_declare_vtab(db, ZIPFILE_SCHEMA);
if( rc==SQLITE_OK ){
pNew = (ZipfileTab*)sqlite3_malloc64((sqlite3_int64)nByte+nFile);
if( pNew==0 ) return SQLITE_NOMEM;
memset(pNew, 0, nByte+nFile);
pNew->db = db;
pNew->aBuffer = (u8*)&pNew[1];
if( zFile ){
pNew->zFile = (char*)&pNew->aBuffer[ZIPFILE_BUFFER_SIZE];
memcpy(pNew->zFile, zFile, nFile);
|
| ︙ | ︙ | |||
4866 4867 4868 4869 4870 4871 4872 |
aRead = pTab->aBuffer;
rc = zipfileReadData(pFile, aRead, ZIPFILE_CDS_FIXED_SZ, iOff, pzErr);
}else{
aRead = (u8*)&aBlob[iOff];
}
if( rc==SQLITE_OK ){
| | | | 5005 5006 5007 5008 5009 5010 5011 5012 5013 5014 5015 5016 5017 5018 5019 5020 5021 5022 5023 5024 5025 5026 5027 5028 5029 5030 5031 |
aRead = pTab->aBuffer;
rc = zipfileReadData(pFile, aRead, ZIPFILE_CDS_FIXED_SZ, iOff, pzErr);
}else{
aRead = (u8*)&aBlob[iOff];
}
if( rc==SQLITE_OK ){
sqlite3_int64 nAlloc;
ZipfileEntry *pNew;
int nFile = zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF]);
int nExtra = zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF+2]);
nExtra += zipfileGetU16(&aRead[ZIPFILE_CDS_NFILE_OFF+4]);
nAlloc = sizeof(ZipfileEntry) + nExtra;
if( aBlob ){
nAlloc += zipfileGetU32(&aRead[ZIPFILE_CDS_SZCOMPRESSED_OFF]);
}
pNew = (ZipfileEntry*)sqlite3_malloc64(nAlloc);
if( pNew==0 ){
rc = SQLITE_NOMEM;
}else{
memset(pNew, 0, sizeof(ZipfileEntry));
rc = zipfileReadCDS(aRead, &pNew->cds);
if( rc!=SQLITE_OK ){
*pzErr = sqlite3_mprintf("failed to read CDS at offset %lld", iOff);
|
| ︙ | ︙ | |||
5041 5042 5043 5044 5045 5046 5047 |
** case.
*/
static int zipfileDeflate(
const u8 *aIn, int nIn, /* Input */
u8 **ppOut, int *pnOut, /* Output */
char **pzErr /* OUT: Error message */
){
| | | | 5180 5181 5182 5183 5184 5185 5186 5187 5188 5189 5190 5191 5192 5193 5194 5195 5196 5197 5198 |
** case.
*/
static int zipfileDeflate(
const u8 *aIn, int nIn, /* Input */
u8 **ppOut, int *pnOut, /* Output */
char **pzErr /* OUT: Error message */
){
sqlite3_int64 nAlloc = compressBound(nIn);
u8 *aOut;
int rc = SQLITE_OK;
aOut = (u8*)sqlite3_malloc64(nAlloc);
if( aOut==0 ){
rc = SQLITE_NOMEM;
}else{
int res;
z_stream str;
memset(&str, 0, sizeof(str));
str.next_in = (Bytef*)aIn;
|
| ︙ | ︙ | |||
5118 5119 5120 5121 5122 5123 5124 |
int szFinal = pCDS->szUncompressed;
if( szFinal>0 ){
u8 *aBuf;
u8 *aFree = 0;
if( pCsr->pCurrent->aData ){
aBuf = pCsr->pCurrent->aData;
}else{
| | | 5257 5258 5259 5260 5261 5262 5263 5264 5265 5266 5267 5268 5269 5270 5271 |
int szFinal = pCDS->szUncompressed;
if( szFinal>0 ){
u8 *aBuf;
u8 *aFree = 0;
if( pCsr->pCurrent->aData ){
aBuf = pCsr->pCurrent->aData;
}else{
aBuf = aFree = sqlite3_malloc64(sz);
if( aBuf==0 ){
rc = SQLITE_NOMEM;
}else{
FILE *pFile = pCsr->pFile;
if( pFile==0 ){
pFile = ((ZipfileTab*)(pCsr->base.pVtab))->pWriteFd;
}
|
| ︙ | ︙ | |||
5957 5958 5959 5960 5961 5962 5963 |
ZipfileBuffer body;
ZipfileBuffer cds;
};
static int zipfileBufferGrow(ZipfileBuffer *pBuf, int nByte){
if( pBuf->n+nByte>pBuf->nAlloc ){
u8 *aNew;
| | | | | 6096 6097 6098 6099 6100 6101 6102 6103 6104 6105 6106 6107 6108 6109 6110 6111 6112 6113 6114 6115 6116 6117 |
ZipfileBuffer body;
ZipfileBuffer cds;
};
static int zipfileBufferGrow(ZipfileBuffer *pBuf, int nByte){
if( pBuf->n+nByte>pBuf->nAlloc ){
u8 *aNew;
sqlite3_int64 nNew = pBuf->n ? pBuf->n*2 : 512;
int nReq = pBuf->n + nByte;
while( nNew<nReq ) nNew = nNew*2;
aNew = sqlite3_realloc64(pBuf->a, nNew);
if( aNew==0 ) return SQLITE_NOMEM;
pBuf->a = aNew;
pBuf->nAlloc = (int)nNew;
}
return SQLITE_OK;
}
/*
** xStep() callback for the zipfile() aggregate. This can be called in
** any of the following ways:
|
| ︙ | ︙ | |||
6155 6156 6157 6158 6159 6160 6161 |
/*
** xFinalize() callback for zipfile aggregate function.
*/
void zipfileFinal(sqlite3_context *pCtx){
ZipfileCtx *p;
ZipfileEOCD eocd;
| | | | | 6294 6295 6296 6297 6298 6299 6300 6301 6302 6303 6304 6305 6306 6307 6308 6309 6310 6311 6312 6313 6314 6315 6316 6317 6318 6319 6320 6321 6322 6323 6324 6325 6326 6327 6328 |
/*
** xFinalize() callback for zipfile aggregate function.
*/
void zipfileFinal(sqlite3_context *pCtx){
ZipfileCtx *p;
ZipfileEOCD eocd;
sqlite3_int64 nZip;
u8 *aZip;
p = (ZipfileCtx*)sqlite3_aggregate_context(pCtx, sizeof(ZipfileCtx));
if( p==0 ) return;
if( p->nEntry>0 ){
memset(&eocd, 0, sizeof(eocd));
eocd.nEntry = (u16)p->nEntry;
eocd.nEntryTotal = (u16)p->nEntry;
eocd.nSize = p->cds.n;
eocd.iOffset = p->body.n;
nZip = p->body.n + p->cds.n + ZIPFILE_EOCD_FIXED_SZ;
aZip = (u8*)sqlite3_malloc64(nZip);
if( aZip==0 ){
sqlite3_result_error_nomem(pCtx);
}else{
memcpy(aZip, p->body.a, p->body.n);
memcpy(&aZip[p->body.n], p->cds.a, p->cds.n);
zipfileSerializeEOCD(&eocd, &aZip[p->body.n + p->cds.n]);
sqlite3_result_blob(pCtx, aZip, (int)nZip, zipfileFree);
}
}
sqlite3_free(p->body.a);
sqlite3_free(p->cds.a);
}
|
| ︙ | ︙ | |||
8546 8547 8548 8549 8550 8551 8552 8553 8554 8555 8556 8557 8558 8559 8560 8561 8562 8563 8564 8565 8566 8567 8568 8569 8570 8571 8572 8573 8574 8575 8576 8577 8578 |
*/
typedef struct ShellState ShellState;
struct ShellState {
sqlite3 *db; /* The database */
u8 autoExplain; /* Automatically turn on .explain mode */
u8 autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */
u8 autoEQPtest; /* autoEQP is in test mode */
u8 statsOn; /* True to display memory stats before each finalize */
u8 scanstatsOn; /* True to display scan stats before each finalize */
u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */
u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */
u8 nEqpLevel; /* Depth of the EQP output graph */
unsigned mEqpLines; /* Mask of veritical lines in the EQP output graph */
int outCount; /* Revert to stdout when reaching zero */
int cnt; /* Number of records displayed so far */
FILE *out; /* Write results here */
FILE *traceOut; /* Output for sqlite3_trace() */
int nErr; /* Number of errors seen */
int mode; /* An output mode setting */
int modePrior; /* Saved mode */
int cMode; /* temporary output mode for the current query */
int normalMode; /* Output mode before ".explain on" */
int writableSchema; /* True if PRAGMA writable_schema=ON */
int showHeader; /* True to show column names in List or Column mode */
int nCheck; /* Number of ".check" commands run */
unsigned shellFlgs; /* Various flags */
char *zDestTable; /* Name of destination table when MODE_Insert */
char *zTempFile; /* Temporary file that might need deleting */
char zTestcase[30]; /* Name of current test case */
char colSeparator[20]; /* Column separator character for several modes */
char rowSeparator[20]; /* Row separator character for MODE_Ascii */
char colSepPrior[20]; /* Saved column separator */
char rowSepPrior[20]; /* Saved row separator */
| > > > > > > > > | 8685 8686 8687 8688 8689 8690 8691 8692 8693 8694 8695 8696 8697 8698 8699 8700 8701 8702 8703 8704 8705 8706 8707 8708 8709 8710 8711 8712 8713 8714 8715 8716 8717 8718 8719 8720 8721 8722 8723 8724 8725 |
*/
typedef struct ShellState ShellState;
struct ShellState {
sqlite3 *db; /* The database */
u8 autoExplain; /* Automatically turn on .explain mode */
u8 autoEQP; /* Run EXPLAIN QUERY PLAN prior to seach SQL stmt */
u8 autoEQPtest; /* autoEQP is in test mode */
u8 autoEQPtrace; /* autoEQP is in trace mode */
u8 statsOn; /* True to display memory stats before each finalize */
u8 scanstatsOn; /* True to display scan stats before each finalize */
u8 openMode; /* SHELL_OPEN_NORMAL, _APPENDVFS, or _ZIPFILE */
u8 doXdgOpen; /* Invoke start/open/xdg-open in output_reset() */
u8 nEqpLevel; /* Depth of the EQP output graph */
u8 eTraceType; /* SHELL_TRACE_* value for type of trace */
unsigned mEqpLines; /* Mask of veritical lines in the EQP output graph */
int outCount; /* Revert to stdout when reaching zero */
int cnt; /* Number of records displayed so far */
int lineno; /* Line number of last line read from in */
FILE *in; /* Read commands from this stream */
FILE *out; /* Write results here */
FILE *traceOut; /* Output for sqlite3_trace() */
int nErr; /* Number of errors seen */
int mode; /* An output mode setting */
int modePrior; /* Saved mode */
int cMode; /* temporary output mode for the current query */
int normalMode; /* Output mode before ".explain on" */
int writableSchema; /* True if PRAGMA writable_schema=ON */
int showHeader; /* True to show column names in List or Column mode */
int nCheck; /* Number of ".check" commands run */
unsigned nProgress; /* Number of progress callbacks encountered */
unsigned mxProgress; /* Maximum progress callbacks before failing */
unsigned flgProgress; /* Flags for the progress callback */
unsigned shellFlgs; /* Various flags */
sqlite3_int64 szMax; /* --maxsize argument to .open */
char *zDestTable; /* Name of destination table when MODE_Insert */
char *zTempFile; /* Temporary file that might need deleting */
char zTestcase[30]; /* Name of current test case */
char colSeparator[20]; /* Column separator character for several modes */
char rowSeparator[20]; /* Row separator character for MODE_Ascii */
char colSepPrior[20]; /* Saved column separator */
char rowSepPrior[20]; /* Saved row separator */
|
| ︙ | ︙ | |||
8609 8610 8611 8612 8613 8614 8615 8616 8617 8618 8619 8620 8621 8622 | */ #define SHELL_OPEN_UNSPEC 0 /* No open-mode specified */ #define SHELL_OPEN_NORMAL 1 /* Normal database file */ #define SHELL_OPEN_APPENDVFS 2 /* Use appendvfs */ #define SHELL_OPEN_ZIPFILE 3 /* Use the zipfile virtual table */ #define SHELL_OPEN_READONLY 4 /* Open a normal database read-only */ #define SHELL_OPEN_DESERIALIZE 5 /* Open using sqlite3_deserialize() */ /* ** These are the allowed shellFlgs values */ #define SHFLG_Pagecache 0x00000001 /* The --pagecache option is used */ #define SHFLG_Lookaside 0x00000002 /* Lookaside memory is used */ #define SHFLG_Backslash 0x00000004 /* The --backslash option is used */ | > > > > > > > > > > > > > > | 8756 8757 8758 8759 8760 8761 8762 8763 8764 8765 8766 8767 8768 8769 8770 8771 8772 8773 8774 8775 8776 8777 8778 8779 8780 8781 8782 8783 |
*/
#define SHELL_OPEN_UNSPEC 0 /* No open-mode specified */
#define SHELL_OPEN_NORMAL 1 /* Normal database file */
#define SHELL_OPEN_APPENDVFS 2 /* Use appendvfs */
#define SHELL_OPEN_ZIPFILE 3 /* Use the zipfile virtual table */
#define SHELL_OPEN_READONLY 4 /* Open a normal database read-only */
#define SHELL_OPEN_DESERIALIZE 5 /* Open using sqlite3_deserialize() */
#define SHELL_OPEN_HEXDB 6 /* Use "dbtotxt" output as data source */
/* Allowed values for ShellState.eTraceType
*/
#define SHELL_TRACE_PLAIN 0 /* Show input SQL text */
#define SHELL_TRACE_EXPANDED 1 /* Show expanded SQL text */
#define SHELL_TRACE_NORMALIZED 2 /* Show normalized SQL text */
/* Bits in the ShellState.flgProgress variable */
#define SHELL_PROGRESS_QUIET 0x01 /* Omit announcing every progress callback */
#define SHELL_PROGRESS_RESET 0x02 /* Reset the count when the progres
** callback limit is reached, and for each
** top-level SQL statement */
#define SHELL_PROGRESS_ONCE 0x04 /* Cancel the --limit after firing once */
/*
** These are the allowed shellFlgs values
*/
#define SHFLG_Pagecache 0x00000001 /* The --pagecache option is used */
#define SHFLG_Lookaside 0x00000002 /* Lookaside memory is used */
#define SHFLG_Backslash 0x00000004 /* The --backslash option is used */
|
| ︙ | ︙ | |||
9310 9311 9312 9313 9314 9315 9316 9317 9318 9319 9320 9321 9322 9323 |
}
p->sGraph.zPrefix[0] = 0;
eqp_render_level(p, 0);
eqp_reset(p);
}
}
/*
** This is the callback routine that the shell
** invokes for each row of a query result.
*/
static int shell_callback(
void *pArg,
int nArg, /* Number of result columns */
| > > > > > > > > > > > > > > > > > > > > | 9471 9472 9473 9474 9475 9476 9477 9478 9479 9480 9481 9482 9483 9484 9485 9486 9487 9488 9489 9490 9491 9492 9493 9494 9495 9496 9497 9498 9499 9500 9501 9502 9503 9504 |
}
p->sGraph.zPrefix[0] = 0;
eqp_render_level(p, 0);
eqp_reset(p);
}
}
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
/*
** Progress handler callback.
*/
static int progress_handler(void *pClientData) {
ShellState *p = (ShellState*)pClientData;
p->nProgress++;
if( p->nProgress>=p->mxProgress && p->mxProgress>0 ){
raw_printf(p->out, "Progress limit reached (%u)\n", p->nProgress);
if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0;
if( p->flgProgress & SHELL_PROGRESS_ONCE ) p->mxProgress = 0;
return 1;
}
if( (p->flgProgress & SHELL_PROGRESS_QUIET)==0 ){
raw_printf(p->out, "Progress %u\n", p->nProgress);
}
return 0;
}
#endif /* SQLITE_OMIT_PROGRESS_CALLBACK */
/*
** This is the callback routine that the shell
** invokes for each row of a query result.
*/
static int shell_callback(
void *pArg,
int nArg, /* Number of result columns */
|
| ︙ | ︙ | |||
10244 10245 10246 10247 10248 10249 10250 10251 10252 10253 10254 10255 10256 10257 | #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE) sqlite3SelectTrace = savedSelectTrace; #endif #if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE) sqlite3WhereTrace = savedWhereTrace; #endif } /* ** Run a prepared statement */ static void exec_prepared_stmt( ShellState *pArg, /* Pointer to ShellState */ sqlite3_stmt *pStmt /* Statment to run */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 10425 10426 10427 10428 10429 10430 10431 10432 10433 10434 10435 10436 10437 10438 10439 10440 10441 10442 10443 10444 10445 10446 10447 10448 10449 10450 10451 10452 10453 10454 10455 10456 10457 10458 10459 10460 10461 10462 10463 10464 10465 10466 10467 10468 10469 10470 10471 10472 10473 10474 10475 10476 10477 10478 10479 10480 10481 10482 10483 10484 10485 10486 10487 10488 10489 10490 10491 10492 10493 10494 10495 10496 10497 10498 |
#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_SELECTTRACE)
sqlite3SelectTrace = savedSelectTrace;
#endif
#if defined(SQLITE_DEBUG) && defined(SQLITE_ENABLE_WHERETRACE)
sqlite3WhereTrace = savedWhereTrace;
#endif
}
/* Create the TEMP table used to store parameter bindings */
static void bind_table_init(ShellState *p){
int wrSchema = 0;
sqlite3_db_config(p->db, SQLITE_DBCONFIG_WRITABLE_SCHEMA, -1, &wrSchema);
sqlite3_db_config(p->db, SQLITE_DBCONFIG_WRITABLE_SCHEMA, 1, 0);
sqlite3_exec(p->db,
"CREATE TABLE IF NOT EXISTS temp.sqlite_parameters(\n"
" key TEXT PRIMARY KEY,\n"
" value ANY\n"
") WITHOUT ROWID;",
0, 0, 0);
sqlite3_db_config(p->db, SQLITE_DBCONFIG_WRITABLE_SCHEMA, wrSchema, 0);
}
/*
** Bind parameters on a prepared statement.
**
** Parameter bindings are taken from a TEMP table of the form:
**
** CREATE TEMP TABLE sqlite_parameters(key TEXT PRIMARY KEY, value)
** WITHOUT ROWID;
**
** No bindings occur if this table does not exist. The special character '$'
** is included in the table name to help prevent collisions with actual tables.
** The table must be in the TEMP schema.
*/
static void bind_prepared_stmt(ShellState *pArg, sqlite3_stmt *pStmt){
int nVar;
int i;
int rc;
sqlite3_stmt *pQ = 0;
nVar = sqlite3_bind_parameter_count(pStmt);
if( nVar==0 ) return; /* Nothing to do */
if( sqlite3_table_column_metadata(pArg->db, "TEMP", "sqlite_parameters",
"key", 0, 0, 0, 0, 0)!=SQLITE_OK ){
return; /* Parameter table does not exist */
}
rc = sqlite3_prepare_v2(pArg->db,
"SELECT value FROM temp.sqlite_parameters"
" WHERE key=?1", -1, &pQ, 0);
if( rc || pQ==0 ) return;
for(i=1; i<=nVar; i++){
char zNum[30];
const char *zVar = sqlite3_bind_parameter_name(pStmt, i);
if( zVar==0 ){
sqlite3_snprintf(sizeof(zNum),zNum,"?%d",i);
zVar = zNum;
}
sqlite3_bind_text(pQ, 1, zVar, -1, SQLITE_STATIC);
if( sqlite3_step(pQ)==SQLITE_ROW ){
sqlite3_bind_value(pStmt, i, sqlite3_column_value(pQ, 0));
}else{
sqlite3_bind_null(pStmt, i);
}
sqlite3_reset(pQ);
}
sqlite3_finalize(pQ);
}
/*
** Run a prepared statement
*/
static void exec_prepared_stmt(
ShellState *pArg, /* Pointer to ShellState */
sqlite3_stmt *pStmt /* Statment to run */
|
| ︙ | ︙ | |||
10497 10498 10499 10500 10501 10502 10503 |
/* echo the sql statement if echo on */
if( pArg && ShellHasFlag(pArg, SHFLG_Echo) ){
utf8_printf(pArg->out, "%s\n", zStmtSql ? zStmtSql : zSql);
}
/* Show the EXPLAIN QUERY PLAN if .eqp is on */
| | | 10738 10739 10740 10741 10742 10743 10744 10745 10746 10747 10748 10749 10750 10751 10752 |
/* echo the sql statement if echo on */
if( pArg && ShellHasFlag(pArg, SHFLG_Echo) ){
utf8_printf(pArg->out, "%s\n", zStmtSql ? zStmtSql : zSql);
}
/* Show the EXPLAIN QUERY PLAN if .eqp is on */
if( pArg && pArg->autoEQP && sqlite3_stmt_isexplain(pStmt)==0 ){
sqlite3_stmt *pExplain;
char *zEQP;
int triggerEQP = 0;
disable_debug_trace_modes();
sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, -1, &triggerEQP);
if( pArg->autoEQP>=AUTOEQP_trigger ){
sqlite3_db_config(db, SQLITE_DBCONFIG_TRIGGER_EQP, 1, 0);
|
| ︙ | ︙ | |||
10546 10547 10548 10549 10550 10551 10552 |
}
restore_debug_trace_modes();
}
if( pArg ){
pArg->cMode = pArg->mode;
if( pArg->autoExplain ){
| | < < | < > | 10787 10788 10789 10790 10791 10792 10793 10794 10795 10796 10797 10798 10799 10800 10801 10802 10803 10804 10805 10806 10807 10808 10809 10810 10811 10812 10813 10814 10815 10816 |
}
restore_debug_trace_modes();
}
if( pArg ){
pArg->cMode = pArg->mode;
if( pArg->autoExplain ){
if( sqlite3_stmt_isexplain(pStmt)==1 ){
pArg->cMode = MODE_Explain;
}
if( sqlite3_stmt_isexplain(pStmt)==2 ){
pArg->cMode = MODE_EQP;
}
}
/* If the shell is currently in ".explain" mode, gather the extra
** data required to add indents to the output.*/
if( pArg->cMode==MODE_Explain ){
explain_data_prepare(pArg, pStmt);
}
}
bind_prepared_stmt(pArg, pStmt);
exec_prepared_stmt(pArg, pStmt);
explain_data_delete(pArg);
eqp_render(pArg);
/* print usage stats if stats on */
if( pArg && pArg->statsOn ){
display_stats(db, pArg, 0);
|
| ︙ | ︙ | |||
10893 10894 10895 10896 10897 10898 10899 |
** start of the description of what that command does.
*/
static const char *(azHelp[]) = {
#if defined(SQLITE_HAVE_ZLIB) && !defined(SQLITE_OMIT_VIRTUALTABLE)
".archive ... Manage SQL archives",
" Each command must have exactly one of the following options:",
" -c, --create Create a new archive",
| | > > | > > > > > > | 11132 11133 11134 11135 11136 11137 11138 11139 11140 11141 11142 11143 11144 11145 11146 11147 11148 11149 11150 11151 11152 11153 11154 11155 11156 11157 11158 11159 11160 11161 11162 11163 11164 11165 11166 11167 11168 11169 11170 11171 11172 11173 11174 11175 11176 11177 11178 11179 11180 11181 11182 11183 11184 11185 11186 11187 11188 11189 11190 |
** start of the description of what that command does.
*/
static const char *(azHelp[]) = {
#if defined(SQLITE_HAVE_ZLIB) && !defined(SQLITE_OMIT_VIRTUALTABLE)
".archive ... Manage SQL archives",
" Each command must have exactly one of the following options:",
" -c, --create Create a new archive",
" -u, --update Add files or update files with changed mtime",
" -i, --insert Like -u but always add even if mtime unchanged",
" -t, --list List contents of archive",
" -x, --extract Extract files from archive",
" Optional arguments:",
" -v, --verbose Print each filename as it is processed",
" -f FILE, --file FILE Operate on archive FILE (default is current db)",
" -a FILE, --append FILE Operate on FILE opened using the apndvfs VFS",
" -C DIR, --directory DIR Change to directory DIR to read/extract files",
" -n, --dryrun Show the SQL that would have occurred",
" Examples:",
" .ar -cf archive.sar foo bar # Create archive.sar from files foo and bar",
" .ar -tf archive.sar # List members of archive.sar",
" .ar -xvf archive.sar # Verbosely extract files from archive.sar",
" See also:",
" http://sqlite.org/cli.html#sqlar_archive_support",
#endif
#ifndef SQLITE_OMIT_AUTHORIZATION
".auth ON|OFF Show authorizer callbacks",
#endif
".backup ?DB? FILE Backup DB (default \"main\") to FILE",
" --append Use the appendvfs",
" --async Write to FILE without a journal and without fsync()",
".bail on|off Stop after hitting an error. Default OFF",
".binary on|off Turn binary output on or off. Default OFF",
".cd DIRECTORY Change the working directory to DIRECTORY",
".changes on|off Show number of rows changed by SQL",
".check GLOB Fail if output since .testcase does not match",
".clone NEWDB Clone data into NEWDB from the existing database",
".databases List names and files of attached databases",
".dbconfig ?op? ?val? List or change sqlite3_db_config() options",
".dbinfo ?DB? Show status information about the database",
".dump ?TABLE? ... Render all database content as SQL",
" Options:",
" --preserve-rowids Include ROWID values in the output",
" --newlines Allow unescaped newline characters in output",
" TABLE is LIKE pattern for the tables to dump",
".echo on|off Turn command echo on or off",
".eqp on|off|full|... Enable or disable automatic EXPLAIN QUERY PLAN",
" Other Modes:",
#ifdef SQLITE_DEBUG
" test Show raw EXPLAIN QUERY PLAN output",
" trace Like \"full\" but also enable \"PRAGMA vdbe_trace\"",
#endif
" trigger Like \"full\" but also show trigger bytecode",
".excel Display the output of next command in a spreadsheet",
".exit ?CODE? Exit this program with return-code CODE",
".expert EXPERIMENTAL. Suggest indexes for specified queries",
/* Because explain mode comes on automatically now, the ".explain" mode
** is removed from the help screen. It is still supported for legacy, however */
/*".explain ?on|off|auto? Turn EXPLAIN output mode on or off or to automatic",*/
".fullschema ?--indent? Show schema and the content of sqlite_stat tables",
|
| ︙ | ︙ | |||
10980 10981 10982 10983 10984 10985 10986 10987 10988 10989 10990 10991 10992 10993 10994 10995 10996 10997 10998 10999 11000 | " -e Invoke system text editor", " -x Open in a spreadsheet", ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE", " Options:", " --append Use appendvfs to append database to the end of FILE", #ifdef SQLITE_ENABLE_DESERIALIZE " --deserialize Load into memory useing sqlite3_deserialize()", #endif " --new Initialize FILE to an empty database", " --readonly Open FILE readonly", " --zip FILE is a ZIP archive", ".output ?FILE? Send output to FILE or stdout if FILE is omitted", " If FILE begins with '|' then open it as a pipe.", ".print STRING... Print literal STRING", ".prompt MAIN CONTINUE Replace the standard prompts", ".quit Exit this program", ".read FILE Read input from FILE", ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE", ".save FILE Write in-memory database into FILE", ".scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off", ".schema ?PATTERN? Show the CREATE statements matching PATTERN", | > > > > > > > > > > > > > > > > | 11227 11228 11229 11230 11231 11232 11233 11234 11235 11236 11237 11238 11239 11240 11241 11242 11243 11244 11245 11246 11247 11248 11249 11250 11251 11252 11253 11254 11255 11256 11257 11258 11259 11260 11261 11262 11263 | " -e Invoke system text editor", " -x Open in a spreadsheet", ".open ?OPTIONS? ?FILE? Close existing database and reopen FILE", " Options:", " --append Use appendvfs to append database to the end of FILE", #ifdef SQLITE_ENABLE_DESERIALIZE " --deserialize Load into memory useing sqlite3_deserialize()", " --hexdb Load the output of \"dbtotxt\" as an in-memory database", " --maxsize N Maximum size for --hexdb or --deserialized database", #endif " --new Initialize FILE to an empty database", " --readonly Open FILE readonly", " --zip FILE is a ZIP archive", ".output ?FILE? Send output to FILE or stdout if FILE is omitted", " If FILE begins with '|' then open it as a pipe.", ".parameter CMD ... Manage SQL parameter bindings", " clear Erase all bindings", " init Initialize the TEMP table that holds bindings", " list List the current parameter bindings", " set PARAMETER VALUE Given SQL parameter PARAMETER a value of VALUE", " PARAMETER should start with '$', ':', '@', or '?'", " unset PARAMETER Remove PARAMETER from the binding table", ".print STRING... Print literal STRING", #ifndef SQLITE_OMIT_PROGRESS_CALLBACK ".progress N Invoke progress handler after every N opcodes", " --limit N Interrupt after N progress callbacks", " --once Do no more than one progress interrupt", " --quiet|-q No output except at interrupts", " --reset Reset the count for each input and interrupt", #endif ".prompt MAIN CONTINUE Replace the standard prompts", ".quit Exit this program", ".read FILE Read input from FILE", ".restore ?DB? FILE Restore content of DB (default \"main\") from FILE", ".save FILE Write in-memory database into FILE", ".scanstats on|off Turn sqlite3_stmt_scanstatus() metrics on or off", ".schema ?PATTERN? Show the CREATE statements matching PATTERN", |
| ︙ | ︙ | |||
11036 11037 11038 11039 11040 11041 11042 | #ifndef SQLITE_NOHAVE_SYSTEM ".system CMD ARGS... Run CMD ARGS... in a system shell", #endif ".tables ?TABLE? List names of tables matching LIKE pattern TABLE", ".testcase NAME Begin redirecting output to 'testcase-out.txt'", ".timeout MS Try opening locked tables for MS milliseconds", ".timer on|off Turn SQL timer on or off", | > | > > > > > > > > > > > > > > | 11299 11300 11301 11302 11303 11304 11305 11306 11307 11308 11309 11310 11311 11312 11313 11314 11315 11316 11317 11318 11319 11320 11321 11322 11323 11324 11325 11326 11327 11328 | #ifndef SQLITE_NOHAVE_SYSTEM ".system CMD ARGS... Run CMD ARGS... in a system shell", #endif ".tables ?TABLE? List names of tables matching LIKE pattern TABLE", ".testcase NAME Begin redirecting output to 'testcase-out.txt'", ".timeout MS Try opening locked tables for MS milliseconds", ".timer on|off Turn SQL timer on or off", #ifndef SQLITE_OMIT_TRACE ".trace ?OPTIONS? Output each SQL statement as it is run", " FILE Send output to FILE", " stdout Send output to stdout", " stderr Send output to stderr", " off Disable tracing", " --expanded Expand query parameters", #ifdef SQLITE_ENABLE_NORMALIZE " --normalized Normal the SQL statements", #endif " --plain Show SQL as it is input", " --stmt Trace statement execution (SQLITE_TRACE_STMT)", " --profile Profile statements (SQLITE_TRACE_PROFILE)", " --row Trace each row (SQLITE_TRACE_ROW)", " --close Trace connection close (SQLITE_TRACE_CLOSE)", #endif /* SQLITE_OMIT_TRACE */ ".vfsinfo ?AUX? Information about the top-level VFS", ".vfslist List all available VFSes", ".vfsname ?AUX? Print the name of the VFS stack", ".width NUM1 NUM2 ... Set column widths for \"column\" mode", " Negative values right-justify", }; |
| ︙ | ︙ | |||
11114 11115 11116 11117 11118 11119 11120 |
}
sqlite3_free(zPat);
}
return n;
}
/* Forward reference */
| | | 11392 11393 11394 11395 11396 11397 11398 11399 11400 11401 11402 11403 11404 11405 11406 |
}
sqlite3_free(zPat);
}
return n;
}
/* Forward reference */
static int process_input(ShellState *p);
/*
** Read the content of file zName into memory obtained from sqlite3_malloc64()
** and return a pointer to the buffer. The caller is responsible for freeing
** the memory.
**
** If parameter pnByte is not NULL, (*pnByte) is set to the number of bytes
|
| ︙ | ︙ | |||
11244 11245 11246 11247 11248 11249 11250 11251 11252 11253 11254 11255 11256 11257 |
rc = SHELL_OPEN_ZIPFILE;
}
}
fclose(f);
return rc;
}
/* Flags for open_db().
**
** The default behavior of open_db() is to exit(1) if the database fails to
** open. The OPEN_DB_KEEPALIVE flag changes that so that it prints an error
** but still returns without calling exit.
**
** The OPEN_DB_ZIPFILE flag causes open_db() to prefer to open files as a
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 11522 11523 11524 11525 11526 11527 11528 11529 11530 11531 11532 11533 11534 11535 11536 11537 11538 11539 11540 11541 11542 11543 11544 11545 11546 11547 11548 11549 11550 11551 11552 11553 11554 11555 11556 11557 11558 11559 11560 11561 11562 11563 11564 11565 11566 11567 11568 11569 11570 11571 11572 11573 11574 11575 11576 11577 11578 11579 11580 11581 11582 11583 11584 11585 11586 11587 11588 11589 11590 11591 11592 11593 11594 11595 11596 11597 11598 11599 11600 11601 11602 11603 11604 11605 11606 11607 11608 11609 11610 11611 11612 11613 11614 11615 11616 11617 11618 11619 11620 11621 11622 11623 |
rc = SHELL_OPEN_ZIPFILE;
}
}
fclose(f);
return rc;
}
#ifdef SQLITE_ENABLE_DESERIALIZE
/*
** Reconstruct an in-memory database using the output from the "dbtotxt"
** program. Read content from the file in p->zDbFilename. If p->zDbFilename
** is 0, then read from standard input.
*/
static unsigned char *readHexDb(ShellState *p, int *pnData){
unsigned char *a = 0;
int nLine;
int n = 0;
int pgsz = 0;
int iOffset = 0;
int j, k;
int rc;
FILE *in;
unsigned char x[16];
char zLine[1000];
if( p->zDbFilename ){
in = fopen(p->zDbFilename, "r");
if( in==0 ){
utf8_printf(stderr, "cannot open \"%s\" for reading\n", p->zDbFilename);
return 0;
}
nLine = 0;
}else{
in = p->in;
nLine = p->lineno;
}
*pnData = 0;
nLine++;
if( fgets(zLine, sizeof(zLine), in)==0 ) goto readHexDb_error;
rc = sscanf(zLine, "| size %d pagesize %d", &n, &pgsz);
if( rc!=2 ) goto readHexDb_error;
if( n<=0 ) goto readHexDb_error;
a = sqlite3_malloc( n );
if( a==0 ){
utf8_printf(stderr, "Out of memory!\n");
goto readHexDb_error;
}
memset(a, 0, n);
if( pgsz<512 || pgsz>65536 || (pgsz & (pgsz-1))!=0 ){
utf8_printf(stderr, "invalid pagesize\n");
goto readHexDb_error;
}
for(nLine++; fgets(zLine, sizeof(zLine), in)!=0; nLine++){
rc = sscanf(zLine, "| page %d offset %d", &j, &k);
if( rc==2 ){
iOffset = k;
continue;
}
if( strncmp(zLine, "| end ", 6)==0 ){
break;
}
rc = sscanf(zLine,"| %d: %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx"
" %hhx %hhx %hhx %hhx %hhx %hhx %hhx %hhx",
&j, &x[0], &x[1], &x[2], &x[3], &x[4], &x[5], &x[6], &x[7],
&x[8], &x[9], &x[10], &x[11], &x[12], &x[13], &x[14], &x[15]);
if( rc==17 ){
k = iOffset+j;
if( k+16<=n ){
memcpy(a+k, x, 16);
}
}
}
*pnData = n;
if( in!=p->in ){
fclose(in);
}else{
p->lineno = nLine;
}
return a;
readHexDb_error:
if( in!=stdin ){
fclose(in);
}else{
while( fgets(zLine, sizeof(zLine), p->in)!=0 ){
nLine++;
if(strncmp(zLine, "| end ", 6)==0 ) break;
}
p->lineno = nLine;
}
sqlite3_free(a);
utf8_printf(stderr,"Error on line %d of --hexdb input\n", nLine);
return 0;
}
#endif /* SQLITE_ENABLE_DESERIALIZE */
/* Flags for open_db().
**
** The default behavior of open_db() is to exit(1) if the database fails to
** open. The OPEN_DB_KEEPALIVE flag changes that so that it prints an error
** but still returns without calling exit.
**
** The OPEN_DB_ZIPFILE flag causes open_db() to prefer to open files as a
|
| ︙ | ︙ | |||
11277 11278 11279 11280 11281 11282 11283 11284 11285 11286 11287 11288 11289 11290 |
}
switch( p->openMode ){
case SHELL_OPEN_APPENDVFS: {
sqlite3_open_v2(p->zDbFilename, &p->db,
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, "apndvfs");
break;
}
case SHELL_OPEN_DESERIALIZE: {
sqlite3_open(0, &p->db);
break;
}
case SHELL_OPEN_ZIPFILE: {
sqlite3_open(":memory:", &p->db);
break;
| > | 11643 11644 11645 11646 11647 11648 11649 11650 11651 11652 11653 11654 11655 11656 11657 |
}
switch( p->openMode ){
case SHELL_OPEN_APPENDVFS: {
sqlite3_open_v2(p->zDbFilename, &p->db,
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, "apndvfs");
break;
}
case SHELL_OPEN_HEXDB:
case SHELL_OPEN_DESERIALIZE: {
sqlite3_open(0, &p->db);
break;
}
case SHELL_OPEN_ZIPFILE: {
sqlite3_open(":memory:", &p->db);
break;
|
| ︙ | ︙ | |||
11299 11300 11301 11302 11303 11304 11305 |
break;
}
}
globalDb = p->db;
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n",
p->zDbFilename, sqlite3_errmsg(p->db));
| | > > > | 11666 11667 11668 11669 11670 11671 11672 11673 11674 11675 11676 11677 11678 11679 11680 11681 11682 11683 |
break;
}
}
globalDb = p->db;
if( p->db==0 || SQLITE_OK!=sqlite3_errcode(p->db) ){
utf8_printf(stderr,"Error: unable to open database \"%s\": %s\n",
p->zDbFilename, sqlite3_errmsg(p->db));
if( openFlags & OPEN_DB_KEEPALIVE ){
sqlite3_open(":memory:", &p->db);
return;
}
exit(1);
}
#ifndef SQLITE_OMIT_LOAD_EXTENSION
sqlite3_enable_load_extension(p->db, 1);
#endif
sqlite3_fileio_init(p->db, 0, 0);
sqlite3_shathree_init(p->db, 0, 0);
|
| ︙ | ︙ | |||
11331 11332 11333 11334 11335 11336 11337 |
if( p->openMode==SHELL_OPEN_ZIPFILE ){
char *zSql = sqlite3_mprintf(
"CREATE VIRTUAL TABLE zip USING zipfile(%Q);", p->zDbFilename);
sqlite3_exec(p->db, zSql, 0, 0, 0);
sqlite3_free(zSql);
}
#ifdef SQLITE_ENABLE_DESERIALIZE
| > | > > > | > > > > > > > | > > > | 11701 11702 11703 11704 11705 11706 11707 11708 11709 11710 11711 11712 11713 11714 11715 11716 11717 11718 11719 11720 11721 11722 11723 11724 11725 11726 11727 11728 11729 11730 11731 11732 11733 11734 11735 11736 11737 |
if( p->openMode==SHELL_OPEN_ZIPFILE ){
char *zSql = sqlite3_mprintf(
"CREATE VIRTUAL TABLE zip USING zipfile(%Q);", p->zDbFilename);
sqlite3_exec(p->db, zSql, 0, 0, 0);
sqlite3_free(zSql);
}
#ifdef SQLITE_ENABLE_DESERIALIZE
else
if( p->openMode==SHELL_OPEN_DESERIALIZE || p->openMode==SHELL_OPEN_HEXDB ){
int rc;
int nData = 0;
unsigned char *aData;
if( p->openMode==SHELL_OPEN_DESERIALIZE ){
aData = (unsigned char*)readFile(p->zDbFilename, &nData);
}else{
aData = readHexDb(p, &nData);
if( aData==0 ){
utf8_printf(stderr, "Error in hexdb input\n");
return;
}
}
rc = sqlite3_deserialize(p->db, "main", aData, nData, nData,
SQLITE_DESERIALIZE_RESIZEABLE |
SQLITE_DESERIALIZE_FREEONCLOSE);
if( rc ){
utf8_printf(stderr, "Error: sqlite3_deserialize() returns %d\n", rc);
}
if( p->szMax>0 ){
sqlite3_file_control(p->db, "main", SQLITE_FCNTL_SIZE_LIMIT, &p->szMax);
}
}
#endif
}
}
/*
** Attempt to close the databaes connection. Report errors.
|
| ︙ | ︙ | |||
11543 11544 11545 11546 11547 11548 11549 |
if( f==0 ){
utf8_printf(stderr, "Error: cannot open \"%s\"\n", zFile);
}
}
return f;
}
| | | | | | | | | > | > > > > > | > > > > > > > > > > > > > > > > > > > > | | > > > | > > > > > > > | 11927 11928 11929 11930 11931 11932 11933 11934 11935 11936 11937 11938 11939 11940 11941 11942 11943 11944 11945 11946 11947 11948 11949 11950 11951 11952 11953 11954 11955 11956 11957 11958 11959 11960 11961 11962 11963 11964 11965 11966 11967 11968 11969 11970 11971 11972 11973 11974 11975 11976 11977 11978 11979 11980 11981 11982 11983 11984 11985 11986 11987 11988 11989 11990 11991 11992 11993 11994 |
if( f==0 ){
utf8_printf(stderr, "Error: cannot open \"%s\"\n", zFile);
}
}
return f;
}
#ifndef SQLITE_OMIT_TRACE
/*
** A routine for handling output from sqlite3_trace().
*/
static int sql_trace_callback(
unsigned mType, /* The trace type */
void *pArg, /* The ShellState pointer */
void *pP, /* Usually a pointer to sqlite_stmt */
void *pX /* Auxiliary output */
){
ShellState *p = (ShellState*)pArg;
sqlite3_stmt *pStmt;
const char *zSql;
int nSql;
if( p->traceOut==0 ) return 0;
if( mType==SQLITE_TRACE_CLOSE ){
utf8_printf(p->traceOut, "-- closing database connection\n");
return 0;
}
if( mType!=SQLITE_TRACE_ROW && ((const char*)pX)[0]=='-' ){
zSql = (const char*)pX;
}else{
pStmt = (sqlite3_stmt*)pP;
switch( p->eTraceType ){
case SHELL_TRACE_EXPANDED: {
zSql = sqlite3_expanded_sql(pStmt);
break;
}
#ifdef SQLITE_ENABLE_NORMALIZE
case SHELL_TRACE_NORMALIZED: {
zSql = sqlite3_normalized_sql(pStmt);
break;
}
#endif
default: {
zSql = sqlite3_sql(pStmt);
break;
}
}
}
if( zSql==0 ) return 0;
nSql = strlen30(zSql);
while( nSql>0 && zSql[nSql-1]==';' ){ nSql--; }
switch( mType ){
case SQLITE_TRACE_ROW:
case SQLITE_TRACE_STMT: {
utf8_printf(p->traceOut, "%.*s;\n", nSql, zSql);
break;
}
case SQLITE_TRACE_PROFILE: {
sqlite3_int64 nNanosec = *(sqlite3_int64*)pX;
utf8_printf(p->traceOut, "%.*s; -- %lld ns\n", nSql, zSql, nNanosec);
break;
}
}
return 0;
}
#endif
/*
** A no-op routine that runs with the ".breakpoint" doc-command. This is
|
| ︙ | ︙ | |||
12034 12035 12036 12037 12038 12039 12040 |
{ "number of triggers:",
"SELECT count(*) FROM %s WHERE type='trigger'" },
{ "number of views:",
"SELECT count(*) FROM %s WHERE type='view'" },
{ "schema size:",
"SELECT total(length(sql)) FROM %s" },
};
| | > | | > > > > > > > > > > | 12454 12455 12456 12457 12458 12459 12460 12461 12462 12463 12464 12465 12466 12467 12468 12469 12470 12471 12472 12473 12474 12475 12476 12477 12478 12479 12480 12481 12482 12483 12484 12485 12486 12487 12488 |
{ "number of triggers:",
"SELECT count(*) FROM %s WHERE type='trigger'" },
{ "number of views:",
"SELECT count(*) FROM %s WHERE type='view'" },
{ "schema size:",
"SELECT total(length(sql)) FROM %s" },
};
int i, rc;
unsigned iDataVersion;
char *zSchemaTab;
char *zDb = nArg>=2 ? azArg[1] : "main";
sqlite3_stmt *pStmt = 0;
unsigned char aHdr[100];
open_db(p, 0);
if( p->db==0 ) return 1;
rc = sqlite3_prepare_v2(p->db,
"SELECT data FROM sqlite_dbpage(?1) WHERE pgno=1",
-1, &pStmt, 0);
if( rc ){
if( !sqlite3_compileoption_used("ENABLE_DBPAGE_VTAB") ){
utf8_printf(stderr, "the \".dbinfo\" command requires the "
"-DSQLITE_ENABLE_DBPAGE_VTAB compile-time options\n");
}else{
utf8_printf(stderr, "error: %s\n", sqlite3_errmsg(p->db));
}
sqlite3_finalize(pStmt);
return 1;
}
sqlite3_bind_text(pStmt, 1, zDb, -1, SQLITE_STATIC);
if( sqlite3_step(pStmt)==SQLITE_ROW
&& sqlite3_column_bytes(pStmt,0)>100
){
memcpy(aHdr, sqlite3_column_blob(pStmt,0), 100);
sqlite3_finalize(pStmt);
}else{
|
| ︙ | ︙ | |||
12637 12638 12639 12640 12641 12642 12643 | return SQLITE_ERROR; } /* ** Values for ArCommand.eCmd. */ #define AR_CMD_CREATE 1 | | | | > | | | | | | > | 13068 13069 13070 13071 13072 13073 13074 13075 13076 13077 13078 13079 13080 13081 13082 13083 13084 13085 13086 13087 13088 13089 13090 13091 13092 13093 13094 13095 13096 13097 13098 13099 13100 13101 13102 13103 |
return SQLITE_ERROR;
}
/*
** Values for ArCommand.eCmd.
*/
#define AR_CMD_CREATE 1
#define AR_CMD_UPDATE 2
#define AR_CMD_INSERT 3
#define AR_CMD_EXTRACT 4
#define AR_CMD_LIST 5
#define AR_CMD_HELP 6
/*
** Other (non-command) switches.
*/
#define AR_SWITCH_VERBOSE 7
#define AR_SWITCH_FILE 8
#define AR_SWITCH_DIRECTORY 9
#define AR_SWITCH_APPEND 10
#define AR_SWITCH_DRYRUN 11
static int arProcessSwitch(ArCommand *pAr, int eSwitch, const char *zArg){
switch( eSwitch ){
case AR_CMD_CREATE:
case AR_CMD_EXTRACT:
case AR_CMD_LIST:
case AR_CMD_UPDATE:
case AR_CMD_INSERT:
case AR_CMD_HELP:
if( pAr->eCmd ){
return arErrorMsg(pAr, "multiple command options");
}
pAr->eCmd = eSwitch;
break;
|
| ︙ | ︙ | |||
12703 12704 12705 12706 12707 12708 12709 12710 12711 12712 12713 12714 12715 12716 |
const char *zLong;
char cShort;
u8 eSwitch;
u8 bArg;
} aSwitch[] = {
{ "create", 'c', AR_CMD_CREATE, 0 },
{ "extract", 'x', AR_CMD_EXTRACT, 0 },
{ "list", 't', AR_CMD_LIST, 0 },
{ "update", 'u', AR_CMD_UPDATE, 0 },
{ "help", 'h', AR_CMD_HELP, 0 },
{ "verbose", 'v', AR_SWITCH_VERBOSE, 0 },
{ "file", 'f', AR_SWITCH_FILE, 1 },
{ "append", 'a', AR_SWITCH_APPEND, 1 },
{ "directory", 'C', AR_SWITCH_DIRECTORY, 1 },
| > | 13136 13137 13138 13139 13140 13141 13142 13143 13144 13145 13146 13147 13148 13149 13150 |
const char *zLong;
char cShort;
u8 eSwitch;
u8 bArg;
} aSwitch[] = {
{ "create", 'c', AR_CMD_CREATE, 0 },
{ "extract", 'x', AR_CMD_EXTRACT, 0 },
{ "insert", 'i', AR_CMD_INSERT, 0 },
{ "list", 't', AR_CMD_LIST, 0 },
{ "update", 'u', AR_CMD_UPDATE, 0 },
{ "help", 'h', AR_CMD_HELP, 0 },
{ "verbose", 'v', AR_SWITCH_VERBOSE, 0 },
{ "file", 'f', AR_SWITCH_FILE, 1 },
{ "append", 'a', AR_SWITCH_APPEND, 1 },
{ "directory", 'C', AR_SWITCH_DIRECTORY, 1 },
|
| ︙ | ︙ | |||
13038 13039 13040 13041 13042 13043 13044 |
}
}
return rc;
}
/*
| | > > > > > | > > | > | 13472 13473 13474 13475 13476 13477 13478 13479 13480 13481 13482 13483 13484 13485 13486 13487 13488 13489 13490 13491 13492 13493 13494 13495 13496 13497 13498 13499 13500 13501 13502 13503 13504 13505 13506 |
}
}
return rc;
}
/*
** Implementation of .ar "create", "insert", and "update" commands.
**
** create -> Create a new SQL archive
** insert -> Insert or reinsert all files listed
** update -> Insert files that have changed or that were not
** previously in the archive
**
** Create the "sqlar" table in the database if it does not already exist.
** Then add each file in the azFile[] array to the archive. Directories
** are added recursively. If argument bVerbose is non-zero, a message is
** printed on stdout for each file archived.
**
** The create command is the same as update, except that it drops
** any existing "sqlar" table before beginning. The "insert" command
** always overwrites every file named on the command-line, where as
** "update" only overwrites if the size or mtime or mode has changed.
*/
static int arCreateOrUpdateCommand(
ArCommand *pAr, /* Command arguments and options */
int bUpdate, /* true for a --create. */
int bOnlyIfChanged /* Only update if file has changed */
){
const char *zCreate =
"CREATE TABLE IF NOT EXISTS sqlar(\n"
" name TEXT PRIMARY KEY, -- name of the file\n"
" mode INT, -- access permissions\n"
" mtime INT, -- last modification time\n"
" sz INT, -- original file size\n"
|
| ︙ | ︙ | |||
13072 13073 13074 13075 13076 13077 13078 |
" mode,\n"
" mtime,\n"
" CASE substr(lsmode(mode),1,1)\n"
" WHEN '-' THEN length(data)\n"
" WHEN 'd' THEN 0\n"
" ELSE -1 END,\n"
" sqlar_compress(data)\n"
| | | > | | > | 13514 13515 13516 13517 13518 13519 13520 13521 13522 13523 13524 13525 13526 13527 13528 13529 13530 13531 13532 13533 13534 13535 13536 13537 13538 13539 13540 13541 13542 13543 13544 13545 |
" mode,\n"
" mtime,\n"
" CASE substr(lsmode(mode),1,1)\n"
" WHEN '-' THEN length(data)\n"
" WHEN 'd' THEN 0\n"
" ELSE -1 END,\n"
" sqlar_compress(data)\n"
" FROM fsdir(%Q,%Q) AS disk\n"
" WHERE lsmode(mode) NOT LIKE '?%%'%s;"
,
"REPLACE INTO %s(name,mode,mtime,data)\n"
" SELECT\n"
" %s,\n"
" mode,\n"
" mtime,\n"
" data\n"
" FROM fsdir(%Q,%Q) AS disk\n"
" WHERE lsmode(mode) NOT LIKE '?%%'%s;"
};
int i; /* For iterating through azFile[] */
int rc; /* Return code */
const char *zTab = 0; /* SQL table into which to insert */
char *zSql;
char zTemp[50];
char *zExists = 0;
arExecSql(pAr, "PRAGMA page_size=512");
rc = arExecSql(pAr, "SAVEPOINT ar;");
if( rc!=SQLITE_OK ) return rc;
zTemp[0] = 0;
if( pAr->bZip ){
/* Initialize the zipfile virtual table, if necessary */
|
| ︙ | ︙ | |||
13118 13119 13120 13121 13122 13123 13124 13125 13126 13127 |
zTab = "sqlar";
if( bUpdate==0 ){
rc = arExecSql(pAr, zDrop);
if( rc!=SQLITE_OK ) goto end_ar_transaction;
}
rc = arExecSql(pAr, zCreate);
}
for(i=0; i<pAr->nArg && rc==SQLITE_OK; i++){
char *zSql2 = sqlite3_mprintf(zInsertFmt[pAr->bZip], zTab,
pAr->bVerbose ? "shell_putsnl(name)" : "name",
| > > > > > > > > > > > | | > | 13562 13563 13564 13565 13566 13567 13568 13569 13570 13571 13572 13573 13574 13575 13576 13577 13578 13579 13580 13581 13582 13583 13584 13585 13586 13587 13588 13589 13590 13591 13592 13593 13594 13595 13596 13597 13598 13599 13600 13601 13602 13603 13604 13605 |
zTab = "sqlar";
if( bUpdate==0 ){
rc = arExecSql(pAr, zDrop);
if( rc!=SQLITE_OK ) goto end_ar_transaction;
}
rc = arExecSql(pAr, zCreate);
}
if( bOnlyIfChanged ){
zExists = sqlite3_mprintf(
" AND NOT EXISTS("
"SELECT 1 FROM %s AS mem"
" WHERE mem.name=disk.name"
" AND mem.mtime=disk.mtime"
" AND mem.mode=disk.mode)", zTab);
}else{
zExists = sqlite3_mprintf("");
}
if( zExists==0 ) rc = SQLITE_NOMEM;
for(i=0; i<pAr->nArg && rc==SQLITE_OK; i++){
char *zSql2 = sqlite3_mprintf(zInsertFmt[pAr->bZip], zTab,
pAr->bVerbose ? "shell_putsnl(name)" : "name",
pAr->azArg[i], pAr->zDir, zExists);
rc = arExecSql(pAr, zSql2);
sqlite3_free(zSql2);
}
end_ar_transaction:
if( rc!=SQLITE_OK ){
sqlite3_exec(pAr->db, "ROLLBACK TO ar; RELEASE ar;", 0, 0, 0);
}else{
rc = arExecSql(pAr, "RELEASE ar;");
if( pAr->bZip && pAr->zFile ){
zSql = sqlite3_mprintf("DROP TABLE %s", zTemp);
arExecSql(pAr, zSql);
sqlite3_free(zSql);
}
}
sqlite3_free(zExists);
return rc;
}
/*
** Implementation of ".ar" dot command.
*/
static int arDotCommand(
|
| ︙ | ︙ | |||
13174 13175 13176 13177 13178 13179 13180 |
cmd.zSrcTable = sqlite3_mprintf("zipfile(%Q)", cmd.zFile);
}
}
cmd.bZip = 1;
}else if( cmd.zFile ){
int flags;
if( cmd.bAppend ) eDbType = SHELL_OPEN_APPENDVFS;
| | > | 13630 13631 13632 13633 13634 13635 13636 13637 13638 13639 13640 13641 13642 13643 13644 13645 |
cmd.zSrcTable = sqlite3_mprintf("zipfile(%Q)", cmd.zFile);
}
}
cmd.bZip = 1;
}else if( cmd.zFile ){
int flags;
if( cmd.bAppend ) eDbType = SHELL_OPEN_APPENDVFS;
if( cmd.eCmd==AR_CMD_CREATE || cmd.eCmd==AR_CMD_INSERT
|| cmd.eCmd==AR_CMD_UPDATE ){
flags = SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE;
}else{
flags = SQLITE_OPEN_READONLY;
}
cmd.db = 0;
if( cmd.bDryRun ){
utf8_printf(pState->out, "-- open database '%s'%s\n", cmd.zFile,
|
| ︙ | ︙ | |||
13211 13212 13213 13214 13215 13216 13217 |
goto end_ar_command;
}
cmd.zSrcTable = sqlite3_mprintf("sqlar");
}
switch( cmd.eCmd ){
case AR_CMD_CREATE:
| | > > > > | | 13668 13669 13670 13671 13672 13673 13674 13675 13676 13677 13678 13679 13680 13681 13682 13683 13684 13685 13686 13687 13688 13689 13690 13691 13692 13693 13694 13695 13696 13697 13698 13699 13700 13701 13702 13703 |
goto end_ar_command;
}
cmd.zSrcTable = sqlite3_mprintf("sqlar");
}
switch( cmd.eCmd ){
case AR_CMD_CREATE:
rc = arCreateOrUpdateCommand(&cmd, 0, 0);
break;
case AR_CMD_EXTRACT:
rc = arExtractCommand(&cmd);
break;
case AR_CMD_LIST:
rc = arListCommand(&cmd);
break;
case AR_CMD_HELP:
arUsage(pState->out);
break;
case AR_CMD_INSERT:
rc = arCreateOrUpdateCommand(&cmd, 1, 0);
break;
default:
assert( cmd.eCmd==AR_CMD_UPDATE );
rc = arCreateOrUpdateCommand(&cmd, 1, 1);
break;
}
}
end_ar_command:
if( cmd.db!=pState->db ){
close_db(cmd.db);
}
|
| ︙ | ︙ | |||
13326 13327 13328 13329 13330 13331 13332 13333 13334 13335 13336 13337 13338 13339 13340 13341 13342 13343 13344 13345 13346 13347 13348 13349 13350 |
|| (c=='s' && n>=3 && strncmp(azArg[0], "save", n)==0)
){
const char *zDestFile = 0;
const char *zDb = 0;
sqlite3 *pDest;
sqlite3_backup *pBackup;
int j;
const char *zVfs = 0;
for(j=1; j<nArg; j++){
const char *z = azArg[j];
if( z[0]=='-' ){
if( z[1]=='-' ) z++;
if( strcmp(z, "-append")==0 ){
zVfs = "apndvfs";
}else
{
utf8_printf(stderr, "unknown option: %s\n", azArg[j]);
return 1;
}
}else if( zDestFile==0 ){
zDestFile = azArg[j];
}else if( zDb==0 ){
zDb = zDestFile;
zDestFile = azArg[j];
}else{
| > > > > | > > > > | 13787 13788 13789 13790 13791 13792 13793 13794 13795 13796 13797 13798 13799 13800 13801 13802 13803 13804 13805 13806 13807 13808 13809 13810 13811 13812 13813 13814 13815 13816 13817 13818 13819 13820 13821 13822 13823 13824 13825 13826 13827 13828 13829 13830 13831 13832 13833 13834 13835 13836 13837 13838 13839 13840 13841 13842 |
|| (c=='s' && n>=3 && strncmp(azArg[0], "save", n)==0)
){
const char *zDestFile = 0;
const char *zDb = 0;
sqlite3 *pDest;
sqlite3_backup *pBackup;
int j;
int bAsync = 0;
const char *zVfs = 0;
for(j=1; j<nArg; j++){
const char *z = azArg[j];
if( z[0]=='-' ){
if( z[1]=='-' ) z++;
if( strcmp(z, "-append")==0 ){
zVfs = "apndvfs";
}else
if( strcmp(z, "-async")==0 ){
bAsync = 1;
}else
{
utf8_printf(stderr, "unknown option: %s\n", azArg[j]);
return 1;
}
}else if( zDestFile==0 ){
zDestFile = azArg[j];
}else if( zDb==0 ){
zDb = zDestFile;
zDestFile = azArg[j];
}else{
raw_printf(stderr, "Usage: .backup ?DB? ?OPTIONS? FILENAME\n");
return 1;
}
}
if( zDestFile==0 ){
raw_printf(stderr, "missing FILENAME argument on .backup\n");
return 1;
}
if( zDb==0 ) zDb = "main";
rc = sqlite3_open_v2(zDestFile, &pDest,
SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE, zVfs);
if( rc!=SQLITE_OK ){
utf8_printf(stderr, "Error: cannot open \"%s\"\n", zDestFile);
close_db(pDest);
return 1;
}
if( bAsync ){
sqlite3_exec(pDest, "PRAGMA synchronous=OFF; PRAGMA journal_mode=OFF;",
0, 0, 0);
}
open_db(p, 0);
pBackup = sqlite3_backup_init(pDest, "main", p->db, zDb);
if( pBackup==0 ){
utf8_printf(stderr, "Error: %s\n", sqlite3_errmsg(pDest));
close_db(pDest);
return 1;
}
|
| ︙ | ︙ | |||
13625 13626 13627 13628 13629 13630 13631 13632 13633 13634 13635 13636 13637 13638 13639 13640 13641 13642 |
rc = 1;
}
}else
if( c=='e' && strncmp(azArg[0], "eqp", n)==0 ){
if( nArg==2 ){
p->autoEQPtest = 0;
if( strcmp(azArg[1],"full")==0 ){
p->autoEQP = AUTOEQP_full;
}else if( strcmp(azArg[1],"trigger")==0 ){
p->autoEQP = AUTOEQP_trigger;
}else if( strcmp(azArg[1],"test")==0 ){
p->autoEQP = AUTOEQP_on;
p->autoEQPtest = 1;
}else{
p->autoEQP = (u8)booleanValue(azArg[1]);
}
}else{
| > > > > > > > > > > > > | | 14094 14095 14096 14097 14098 14099 14100 14101 14102 14103 14104 14105 14106 14107 14108 14109 14110 14111 14112 14113 14114 14115 14116 14117 14118 14119 14120 14121 14122 14123 14124 14125 14126 14127 14128 14129 14130 14131 |
rc = 1;
}
}else
if( c=='e' && strncmp(azArg[0], "eqp", n)==0 ){
if( nArg==2 ){
p->autoEQPtest = 0;
if( p->autoEQPtrace ){
if( p->db ) sqlite3_exec(p->db, "PRAGMA vdbe_trace=OFF;", 0, 0, 0);
p->autoEQPtrace = 0;
}
if( strcmp(azArg[1],"full")==0 ){
p->autoEQP = AUTOEQP_full;
}else if( strcmp(azArg[1],"trigger")==0 ){
p->autoEQP = AUTOEQP_trigger;
#ifdef SQLITE_DEBUG
}else if( strcmp(azArg[1],"test")==0 ){
p->autoEQP = AUTOEQP_on;
p->autoEQPtest = 1;
}else if( strcmp(azArg[1],"trace")==0 ){
p->autoEQP = AUTOEQP_full;
p->autoEQPtrace = 1;
open_db(p, 0);
sqlite3_exec(p->db, "SELECT name FROM sqlite_master LIMIT 1", 0, 0, 0);
sqlite3_exec(p->db, "PRAGMA vdbe_trace=ON;", 0, 0, 0);
#endif
}else{
p->autoEQP = (u8)booleanValue(azArg[1]);
}
}else{
raw_printf(stderr, "Usage: .eqp off|on|trace|trigger|full\n");
rc = 1;
}
}else
if( c=='e' && strncmp(azArg[0], "exit", n)==0 ){
if( nArg>1 && (rc = (int)integerValue(azArg[1]))!=0 ) exit(rc);
rc = 2;
|
| ︙ | ︙ | |||
14210 14211 14212 14213 14214 14215 14216 14217 14218 14219 14220 14221 14222 14223 14224 14225 14226 14227 14228 14229 14230 14231 14232 |
session_close_all(p);
close_db(p->db);
p->db = 0;
p->zDbFilename = 0;
sqlite3_free(p->zFreeOnClose);
p->zFreeOnClose = 0;
p->openMode = SHELL_OPEN_UNSPEC;
/* Check for command-line arguments */
for(iName=1; iName<nArg && azArg[iName][0]=='-'; iName++){
const char *z = azArg[iName];
if( optionMatch(z,"new") ){
newFlag = 1;
#ifdef SQLITE_HAVE_ZLIB
}else if( optionMatch(z, "zip") ){
p->openMode = SHELL_OPEN_ZIPFILE;
#endif
}else if( optionMatch(z, "append") ){
p->openMode = SHELL_OPEN_APPENDVFS;
}else if( optionMatch(z, "readonly") ){
p->openMode = SHELL_OPEN_READONLY;
#ifdef SQLITE_ENABLE_DESERIALIZE
}else if( optionMatch(z, "deserialize") ){
p->openMode = SHELL_OPEN_DESERIALIZE;
| > > > > > | | | 14691 14692 14693 14694 14695 14696 14697 14698 14699 14700 14701 14702 14703 14704 14705 14706 14707 14708 14709 14710 14711 14712 14713 14714 14715 14716 14717 14718 14719 14720 14721 14722 14723 14724 14725 14726 14727 14728 14729 14730 14731 14732 14733 14734 14735 |
session_close_all(p);
close_db(p->db);
p->db = 0;
p->zDbFilename = 0;
sqlite3_free(p->zFreeOnClose);
p->zFreeOnClose = 0;
p->openMode = SHELL_OPEN_UNSPEC;
p->szMax = 0;
/* Check for command-line arguments */
for(iName=1; iName<nArg && azArg[iName][0]=='-'; iName++){
const char *z = azArg[iName];
if( optionMatch(z,"new") ){
newFlag = 1;
#ifdef SQLITE_HAVE_ZLIB
}else if( optionMatch(z, "zip") ){
p->openMode = SHELL_OPEN_ZIPFILE;
#endif
}else if( optionMatch(z, "append") ){
p->openMode = SHELL_OPEN_APPENDVFS;
}else if( optionMatch(z, "readonly") ){
p->openMode = SHELL_OPEN_READONLY;
#ifdef SQLITE_ENABLE_DESERIALIZE
}else if( optionMatch(z, "deserialize") ){
p->openMode = SHELL_OPEN_DESERIALIZE;
}else if( optionMatch(z, "hexdb") ){
p->openMode = SHELL_OPEN_HEXDB;
}else if( optionMatch(z, "maxsize") && iName+1<nArg ){
p->szMax = integerValue(azArg[++iName]);
#endif /* SQLITE_ENABLE_DESERIALIZE */
}else if( z[0]=='-' ){
utf8_printf(stderr, "unknown option: %s\n", z);
rc = 1;
goto meta_command_exit;
}
}
/* If a filename is specified, try to open it first */
zNewFilename = nArg>iName ? sqlite3_mprintf("%s", azArg[iName]) : 0;
if( zNewFilename || p->openMode==SHELL_OPEN_HEXDB ){
if( newFlag ) shellDeleteFile(zNewFilename);
p->zDbFilename = zNewFilename;
open_db(p, OPEN_DB_KEEPALIVE);
if( p->db==0 ){
utf8_printf(stderr, "Error: cannot open '%s'\n", zNewFilename);
sqlite3_free(zNewFilename);
}else{
|
| ︙ | ︙ | |||
14327 14328 14329 14330 14331 14332 14333 14334 14335 14336 14337 14338 14339 14340 14341 14342 14343 14344 14345 14346 14347 14348 14349 14350 14351 14352 14353 14354 14355 14356 14357 |
p->out = stdout;
rc = 1;
} else {
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
}
}
}else
if( c=='p' && n>=3 && strncmp(azArg[0], "print", n)==0 ){
int i;
for(i=1; i<nArg; i++){
if( i>1 ) raw_printf(p->out, " ");
utf8_printf(p->out, "%s", azArg[i]);
}
raw_printf(p->out, "\n");
}else
if( c=='p' && strncmp(azArg[0], "prompt", n)==0 ){
if( nArg >= 2) {
strncpy(mainPrompt,azArg[1],(int)ArraySize(mainPrompt)-1);
}
if( nArg >= 3) {
strncpy(continuePrompt,azArg[2],(int)ArraySize(continuePrompt)-1);
}
}else
if( c=='q' && strncmp(azArg[0], "quit", n)==0 ){
rc = 2;
}else
if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 ){
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | | | | > > | 14813 14814 14815 14816 14817 14818 14819 14820 14821 14822 14823 14824 14825 14826 14827 14828 14829 14830 14831 14832 14833 14834 14835 14836 14837 14838 14839 14840 14841 14842 14843 14844 14845 14846 14847 14848 14849 14850 14851 14852 14853 14854 14855 14856 14857 14858 14859 14860 14861 14862 14863 14864 14865 14866 14867 14868 14869 14870 14871 14872 14873 14874 14875 14876 14877 14878 14879 14880 14881 14882 14883 14884 14885 14886 14887 14888 14889 14890 14891 14892 14893 14894 14895 14896 14897 14898 14899 14900 14901 14902 14903 14904 14905 14906 14907 14908 14909 14910 14911 14912 14913 14914 14915 14916 14917 14918 14919 14920 14921 14922 14923 14924 14925 14926 14927 14928 14929 14930 14931 14932 14933 14934 14935 14936 14937 14938 14939 14940 14941 14942 14943 14944 14945 14946 14947 14948 14949 14950 14951 14952 14953 14954 14955 14956 14957 14958 14959 14960 14961 14962 14963 14964 14965 14966 14967 14968 14969 14970 14971 14972 14973 14974 14975 14976 14977 14978 14979 14980 14981 14982 14983 14984 14985 14986 14987 14988 14989 14990 14991 14992 14993 14994 14995 14996 14997 14998 14999 15000 15001 15002 15003 15004 15005 15006 15007 15008 15009 15010 15011 15012 15013 15014 15015 15016 15017 15018 15019 15020 15021 |
p->out = stdout;
rc = 1;
} else {
sqlite3_snprintf(sizeof(p->outfile), p->outfile, "%s", zFile);
}
}
}else
if( c=='p' && n>=3 && strncmp(azArg[0], "parameter", n)==0 ){
open_db(p,0);
if( nArg<=1 ) goto parameter_syntax_error;
/* .parameter clear
** Clear all bind parameters by dropping the TEMP table that holds them.
*/
if( nArg==2 && strcmp(azArg[1],"clear")==0 ){
int wrSchema = 0;
sqlite3_db_config(p->db, SQLITE_DBCONFIG_WRITABLE_SCHEMA, -1, &wrSchema);
sqlite3_db_config(p->db, SQLITE_DBCONFIG_WRITABLE_SCHEMA, 1, 0);
sqlite3_exec(p->db, "DROP TABLE IF EXISTS temp.sqlite_parameters;",
0, 0, 0);
sqlite3_db_config(p->db, SQLITE_DBCONFIG_WRITABLE_SCHEMA, wrSchema, 0);
}else
/* .parameter list
** List all bind parameters.
*/
if( nArg==2 && strcmp(azArg[1],"list")==0 ){
sqlite3_stmt *pStmt = 0;
int rx;
int len = 0;
rx = sqlite3_prepare_v2(p->db,
"SELECT max(length(key)) "
"FROM temp.sqlite_parameters;", -1, &pStmt, 0);
if( rx==SQLITE_OK && sqlite3_step(pStmt)==SQLITE_ROW ){
len = sqlite3_column_int(pStmt, 0);
if( len>40 ) len = 40;
}
sqlite3_finalize(pStmt);
pStmt = 0;
if( len ){
rx = sqlite3_prepare_v2(p->db,
"SELECT key, quote(value) "
"FROM temp.sqlite_parameters;", -1, &pStmt, 0);
while( sqlite3_step(pStmt)==SQLITE_ROW ){
utf8_printf(p->out, "%-*s %s\n", len, sqlite3_column_text(pStmt,0),
sqlite3_column_text(pStmt,1));
}
sqlite3_finalize(pStmt);
}
}else
/* .parameter init
** Make sure the TEMP table used to hold bind parameters exists.
** Create it if necessary.
*/
if( nArg==2 && strcmp(azArg[1],"init")==0 ){
bind_table_init(p);
}else
/* .parameter set NAME VALUE
** Set or reset a bind parameter. NAME should be the full parameter
** name exactly as it appears in the query. (ex: $abc, @def). The
** VALUE can be in either SQL literal notation, or if not it will be
** understood to be a text string.
*/
if( nArg==4 && strcmp(azArg[1],"set")==0 ){
int rx;
char *zSql;
sqlite3_stmt *pStmt;
const char *zKey = azArg[2];
const char *zValue = azArg[3];
bind_table_init(p);
zSql = sqlite3_mprintf(
"REPLACE INTO temp.sqlite_parameters(key,value)"
"VALUES(%Q,%s);", zKey, zValue);
if( zSql==0 ) shell_out_of_memory();
pStmt = 0;
rx = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
if( rx!=SQLITE_OK ){
sqlite3_finalize(pStmt);
pStmt = 0;
zSql = sqlite3_mprintf(
"REPLACE INTO temp.sqlite_parameters(key,value)"
"VALUES(%Q,%Q);", zKey, zValue);
if( zSql==0 ) shell_out_of_memory();
rx = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
sqlite3_free(zSql);
if( rx!=SQLITE_OK ){
utf8_printf(p->out, "Error: %s\n", sqlite3_errmsg(p->db));
sqlite3_finalize(pStmt);
pStmt = 0;
rc = 1;
}
}
sqlite3_step(pStmt);
sqlite3_finalize(pStmt);
}else
/* .parameter unset NAME
** Remove the NAME binding from the parameter binding table, if it
** exists.
*/
if( nArg==3 && strcmp(azArg[1],"unset")==0 ){
char *zSql = sqlite3_mprintf(
"DELETE FROM temp.sqlite_parameters WHERE key=%Q", azArg[2]);
if( zSql==0 ) shell_out_of_memory();
sqlite3_exec(p->db, zSql, 0, 0, 0);
sqlite3_free(zSql);
}else
/* If no command name matches, show a syntax error */
parameter_syntax_error:
showHelp(p->out, "parameter");
}else
if( c=='p' && n>=3 && strncmp(azArg[0], "print", n)==0 ){
int i;
for(i=1; i<nArg; i++){
if( i>1 ) raw_printf(p->out, " ");
utf8_printf(p->out, "%s", azArg[i]);
}
raw_printf(p->out, "\n");
}else
#ifndef SQLITE_OMIT_PROGRESS_CALLBACK
if( c=='p' && n>=3 && strncmp(azArg[0], "progress", n)==0 ){
int i;
int nn = 0;
p->flgProgress = 0;
p->mxProgress = 0;
p->nProgress = 0;
for(i=1; i<nArg; i++){
const char *z = azArg[i];
if( z[0]=='-' ){
z++;
if( z[0]=='-' ) z++;
if( strcmp(z,"quiet")==0 || strcmp(z,"q")==0 ){
p->flgProgress |= SHELL_PROGRESS_QUIET;
continue;
}
if( strcmp(z,"reset")==0 ){
p->flgProgress |= SHELL_PROGRESS_RESET;
continue;
}
if( strcmp(z,"once")==0 ){
p->flgProgress |= SHELL_PROGRESS_ONCE;
continue;
}
if( strcmp(z,"limit")==0 ){
if( i+1>=nArg ){
utf8_printf(stderr, "Error: missing argument on --limit\n");
rc = 1;
goto meta_command_exit;
}else{
p->mxProgress = (int)integerValue(azArg[++i]);
}
continue;
}
utf8_printf(stderr, "Error: unknown option: \"%s\"\n", azArg[i]);
rc = 1;
goto meta_command_exit;
}else{
nn = (int)integerValue(z);
}
}
open_db(p, 0);
sqlite3_progress_handler(p->db, nn, progress_handler, p);
}else
#endif /* SQLITE_OMIT_PROGRESS_CALLBACK */
if( c=='p' && strncmp(azArg[0], "prompt", n)==0 ){
if( nArg >= 2) {
strncpy(mainPrompt,azArg[1],(int)ArraySize(mainPrompt)-1);
}
if( nArg >= 3) {
strncpy(continuePrompt,azArg[2],(int)ArraySize(continuePrompt)-1);
}
}else
if( c=='q' && strncmp(azArg[0], "quit", n)==0 ){
rc = 2;
}else
if( c=='r' && n>=3 && strncmp(azArg[0], "read", n)==0 ){
FILE *inSaved = p->in;
int savedLineno = p->lineno;
if( nArg!=2 ){
raw_printf(stderr, "Usage: .read FILE\n");
rc = 1;
goto meta_command_exit;
}
p->in = fopen(azArg[1], "rb");
if( p->in==0 ){
utf8_printf(stderr,"Error: cannot open \"%s\"\n", azArg[1]);
rc = 1;
}else{
rc = process_input(p);
fclose(p->in);
}
p->in = inSaved;
p->lineno = savedLineno;
}else
if( c=='r' && n>=3 && strncmp(azArg[0], "restore", n)==0 ){
const char *zSrcFile;
const char *zDb;
sqlite3 *pSrc;
sqlite3_backup *pBackup;
|
| ︙ | ︙ | |||
15382 15383 15384 15385 15386 15387 15388 15389 15390 |
}
}else{
raw_printf(stderr, "Usage: .timer on|off\n");
rc = 1;
}
}else
if( c=='t' && strncmp(azArg[0], "trace", n)==0 ){
open_db(p, 0);
| > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > | | | | > | | < > > > | < > | 16025 16026 16027 16028 16029 16030 16031 16032 16033 16034 16035 16036 16037 16038 16039 16040 16041 16042 16043 16044 16045 16046 16047 16048 16049 16050 16051 16052 16053 16054 16055 16056 16057 16058 16059 16060 16061 16062 16063 16064 16065 16066 16067 16068 16069 16070 16071 16072 16073 16074 16075 16076 16077 16078 16079 16080 16081 16082 16083 16084 16085 16086 16087 |
}
}else{
raw_printf(stderr, "Usage: .timer on|off\n");
rc = 1;
}
}else
#ifndef SQLITE_OMIT_TRACE
if( c=='t' && strncmp(azArg[0], "trace", n)==0 ){
int mType = 0;
int jj;
open_db(p, 0);
for(jj=1; jj<nArg; jj++){
const char *z = azArg[jj];
if( z[0]=='-' ){
if( optionMatch(z, "expanded") ){
p->eTraceType = SHELL_TRACE_EXPANDED;
}
#ifdef SQLITE_ENABLE_NORMALIZE
else if( optionMatch(z, "normalized") ){
p->eTraceType = SHELL_TRACE_NORMALIZED;
}
#endif
else if( optionMatch(z, "plain") ){
p->eTraceType = SHELL_TRACE_PLAIN;
}
else if( optionMatch(z, "profile") ){
mType |= SQLITE_TRACE_PROFILE;
}
else if( optionMatch(z, "row") ){
mType |= SQLITE_TRACE_ROW;
}
else if( optionMatch(z, "stmt") ){
mType |= SQLITE_TRACE_STMT;
}
else if( optionMatch(z, "close") ){
mType |= SQLITE_TRACE_CLOSE;
}
else {
raw_printf(stderr, "Unknown option \"%s\" on \".trace\"\n", z);
rc = 1;
goto meta_command_exit;
}
}else{
output_file_close(p->traceOut);
p->traceOut = output_file_open(azArg[1], 0);
}
}
if( p->traceOut==0 ){
sqlite3_trace_v2(p->db, 0, 0, 0);
}else{
if( mType==0 ) mType = SQLITE_TRACE_STMT;
sqlite3_trace_v2(p->db, mType, sql_trace_callback, p);
}
}else
#endif /* !defined(SQLITE_OMIT_TRACE) */
#if SQLITE_USER_AUTHENTICATION
if( c=='u' && strncmp(azArg[0], "user", n)==0 ){
if( nArg<2 ){
raw_printf(stderr, "Usage: .user SUBCOMMAND ...\n");
rc = 1;
goto meta_command_exit;
|
| ︙ | ︙ | |||
15637 15638 15639 15640 15641 15642 15643 15644 15645 15646 15647 15648 15649 15650 |
*/
static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){
int rc;
char *zErrMsg = 0;
open_db(p, 0);
if( ShellHasFlag(p,SHFLG_Backslash) ) resolve_backslashes(zSql);
BEGIN_TIMER;
rc = shell_exec(p, zSql, &zErrMsg);
END_TIMER;
if( rc || zErrMsg ){
char zPrefix[100];
if( in!=0 || !stdin_is_interactive ){
sqlite3_snprintf(sizeof(zPrefix), zPrefix,
| > | 16312 16313 16314 16315 16316 16317 16318 16319 16320 16321 16322 16323 16324 16325 16326 |
*/
static int runOneSqlLine(ShellState *p, char *zSql, FILE *in, int startline){
int rc;
char *zErrMsg = 0;
open_db(p, 0);
if( ShellHasFlag(p,SHFLG_Backslash) ) resolve_backslashes(zSql);
if( p->flgProgress & SHELL_PROGRESS_RESET ) p->nProgress = 0;
BEGIN_TIMER;
rc = shell_exec(p, zSql, &zErrMsg);
END_TIMER;
if( rc || zErrMsg ){
char zPrefix[100];
if( in!=0 || !stdin_is_interactive ){
sqlite3_snprintf(sizeof(zPrefix), zPrefix,
|
| ︙ | ︙ | |||
15673 15674 15675 15676 15677 15678 15679 | ** is interactive - the user is typing it it. Otherwise, input ** is coming from a file or device. A prompt is issued and history ** is saved only if input is interactive. An interrupt signal will ** cause this routine to exit immediately, unless input is interactive. ** ** Return the number of errors. */ | | < > | | | | | | 16349 16350 16351 16352 16353 16354 16355 16356 16357 16358 16359 16360 16361 16362 16363 16364 16365 16366 16367 16368 16369 16370 16371 16372 16373 16374 16375 16376 16377 16378 16379 16380 16381 16382 16383 16384 16385 16386 16387 |
** is interactive - the user is typing it it. Otherwise, input
** is coming from a file or device. A prompt is issued and history
** is saved only if input is interactive. An interrupt signal will
** cause this routine to exit immediately, unless input is interactive.
**
** Return the number of errors.
*/
static int process_input(ShellState *p){
char *zLine = 0; /* A single input line */
char *zSql = 0; /* Accumulated SQL text */
int nLine; /* Length of current line */
int nSql = 0; /* Bytes of zSql[] used */
int nAlloc = 0; /* Allocated zSql[] space */
int nSqlPrior = 0; /* Bytes of zSql[] used by prior line */
int rc; /* Error code */
int errCnt = 0; /* Number of errors seen */
int startline = 0; /* Line number for start of current input */
p->lineno = 0;
while( errCnt==0 || !bail_on_error || (p->in==0 && stdin_is_interactive) ){
fflush(p->out);
zLine = one_input_line(p->in, zLine, nSql>0);
if( zLine==0 ){
/* End of input */
if( p->in==0 && stdin_is_interactive ) printf("\n");
break;
}
if( seenInterrupt ){
if( p->in!=0 ) break;
seenInterrupt = 0;
}
p->lineno++;
if( nSql==0 && _all_whitespace(zLine) ){
if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zLine);
continue;
}
if( zLine && (zLine[0]=='.' || zLine[0]=='#') && nSql==0 ){
if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zLine);
if( zLine[0]=='.' ){
|
| ︙ | ︙ | |||
15729 15730 15731 15732 15733 15734 15735 |
}
nSqlPrior = nSql;
if( nSql==0 ){
int i;
for(i=0; zLine[i] && IsSpace(zLine[i]); i++){}
assert( nAlloc>0 && zSql!=0 );
memcpy(zSql, zLine+i, nLine+1-i);
| | | | | 16405 16406 16407 16408 16409 16410 16411 16412 16413 16414 16415 16416 16417 16418 16419 16420 16421 16422 16423 16424 16425 16426 16427 16428 16429 16430 16431 16432 16433 16434 16435 16436 16437 16438 16439 16440 16441 16442 |
}
nSqlPrior = nSql;
if( nSql==0 ){
int i;
for(i=0; zLine[i] && IsSpace(zLine[i]); i++){}
assert( nAlloc>0 && zSql!=0 );
memcpy(zSql, zLine+i, nLine+1-i);
startline = p->lineno;
nSql = nLine-i;
}else{
zSql[nSql++] = '\n';
memcpy(zSql+nSql, zLine, nLine+1);
nSql += nLine;
}
if( nSql && line_contains_semicolon(&zSql[nSqlPrior], nSql-nSqlPrior)
&& sqlite3_complete(zSql) ){
errCnt += runOneSqlLine(p, zSql, p->in, startline);
nSql = 0;
if( p->outCount ){
output_reset(p);
p->outCount = 0;
}else{
clearTempFile(p);
}
}else if( nSql && _all_whitespace(zSql) ){
if( ShellHasFlag(p, SHFLG_Echo) ) printf("%s\n", zSql);
nSql = 0;
}
}
if( nSql && !_all_whitespace(zSql) ){
errCnt += runOneSqlLine(p, zSql, p->in, startline);
}
free(zSql);
free(zLine);
return errCnt>0;
}
/*
|
| ︙ | ︙ | |||
15841 15842 15843 15844 15845 15846 15847 |
static void process_sqliterc(
ShellState *p, /* Configuration data */
const char *sqliterc_override /* Name of config file. NULL to use default */
){
char *home_dir = NULL;
const char *sqliterc = sqliterc_override;
char *zBuf = 0;
| | > | | | | > > > > > > > > > | 16517 16518 16519 16520 16521 16522 16523 16524 16525 16526 16527 16528 16529 16530 16531 16532 16533 16534 16535 16536 16537 16538 16539 16540 16541 16542 16543 16544 16545 16546 16547 16548 16549 16550 16551 16552 16553 16554 16555 16556 16557 16558 16559 16560 16561 16562 16563 16564 16565 16566 16567 16568 16569 16570 16571 16572 16573 16574 16575 16576 16577 16578 16579 16580 16581 16582 16583 16584 16585 16586 16587 16588 16589 |
static void process_sqliterc(
ShellState *p, /* Configuration data */
const char *sqliterc_override /* Name of config file. NULL to use default */
){
char *home_dir = NULL;
const char *sqliterc = sqliterc_override;
char *zBuf = 0;
FILE *inSaved = p->in;
int savedLineno = p->lineno;
if (sqliterc == NULL) {
home_dir = find_home_dir(0);
if( home_dir==0 ){
raw_printf(stderr, "-- warning: cannot find home directory;"
" cannot read ~/.sqliterc\n");
return;
}
zBuf = sqlite3_mprintf("%s/.sqliterc",home_dir);
sqliterc = zBuf;
}
p->in = fopen(sqliterc,"rb");
if( p->in ){
if( stdin_is_interactive ){
utf8_printf(stderr,"-- Loading resources from %s\n",sqliterc);
}
process_input(p);
fclose(p->in);
}
p->in = inSaved;
p->lineno = savedLineno;
sqlite3_free(zBuf);
}
/*
** Show available command line options
*/
static const char zOptions[] =
#if defined(SQLITE_HAVE_ZLIB) && !defined(SQLITE_OMIT_VIRTUALTABLE)
" -A ARGS... run \".archive ARGS\" and exit\n"
#endif
" -append append the database to the end of the file\n"
" -ascii set output mode to 'ascii'\n"
" -bail stop after hitting an error\n"
" -batch force batch I/O\n"
" -column set output mode to 'column'\n"
" -cmd COMMAND run \"COMMAND\" before reading stdin\n"
" -csv set output mode to 'csv'\n"
#if defined(SQLITE_ENABLE_DESERIALIZE)
" -deserialize open the database using sqlite3_deserialize()\n"
#endif
" -echo print commands before execution\n"
" -init FILENAME read/process named file\n"
" -[no]header turn headers on or off\n"
#if defined(SQLITE_ENABLE_MEMSYS3) || defined(SQLITE_ENABLE_MEMSYS5)
" -heap SIZE Size of heap for memsys3 or memsys5\n"
#endif
" -help show this message\n"
" -html set output mode to HTML\n"
" -interactive force interactive I/O\n"
" -line set output mode to 'line'\n"
" -list set output mode to 'list'\n"
" -lookaside SIZE N use N entries of SZ bytes for lookaside memory\n"
#if defined(SQLITE_ENABLE_DESERIALIZE)
" -maxsize N maximum size for a --deserialize database\n"
#endif
" -memtrace trace all memory allocations and deallocations\n"
" -mmap N default mmap size set to N\n"
#ifdef SQLITE_ENABLE_MULTIPLEX
" -multiplex enable the multiplexor VFS\n"
#endif
" -newline SEP set output row separator. Default: '\\n'\n"
" -nullvalue TEXT set text string for NULL values. Default ''\n"
" -pagecache SIZE N use N slots of SZ bytes each for page cache memory\n"
|
| ︙ | ︙ | |||
16200 16201 16202 16203 16204 16205 16206 16207 16208 16209 16210 16211 16212 16213 16214 16215 16216 16217 16218 16219 16220 16221 16222 |
data.openMode = SHELL_OPEN_ZIPFILE;
#endif
}else if( strcmp(z,"-append")==0 ){
data.openMode = SHELL_OPEN_APPENDVFS;
#ifdef SQLITE_ENABLE_DESERIALIZE
}else if( strcmp(z,"-deserialize")==0 ){
data.openMode = SHELL_OPEN_DESERIALIZE;
#endif
}else if( strcmp(z,"-readonly")==0 ){
data.openMode = SHELL_OPEN_READONLY;
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
}else if( strncmp(z, "-A",2)==0 ){
/* All remaining command-line arguments are passed to the ".archive"
** command, so ignore them */
break;
#endif
}
}
verify_uninitialized();
#ifdef SQLITE_SHELL_INIT_PROC
{
| > > > > | 16886 16887 16888 16889 16890 16891 16892 16893 16894 16895 16896 16897 16898 16899 16900 16901 16902 16903 16904 16905 16906 16907 16908 16909 16910 16911 16912 |
data.openMode = SHELL_OPEN_ZIPFILE;
#endif
}else if( strcmp(z,"-append")==0 ){
data.openMode = SHELL_OPEN_APPENDVFS;
#ifdef SQLITE_ENABLE_DESERIALIZE
}else if( strcmp(z,"-deserialize")==0 ){
data.openMode = SHELL_OPEN_DESERIALIZE;
}else if( strcmp(z,"-maxsize")==0 && i+1<argc ){
data.szMax = integerValue(argv[++i]);
#endif
}else if( strcmp(z,"-readonly")==0 ){
data.openMode = SHELL_OPEN_READONLY;
#if !defined(SQLITE_OMIT_VIRTUALTABLE) && defined(SQLITE_HAVE_ZLIB)
}else if( strncmp(z, "-A",2)==0 ){
/* All remaining command-line arguments are passed to the ".archive"
** command, so ignore them */
break;
#endif
}else if( strcmp(z, "-memtrace")==0 ){
sqlite3MemTraceActivate(stderr);
}
}
verify_uninitialized();
#ifdef SQLITE_SHELL_INIT_PROC
{
|
| ︙ | ︙ | |||
16299 16300 16301 16302 16303 16304 16305 16306 16307 16308 16309 16310 16311 16312 |
data.openMode = SHELL_OPEN_ZIPFILE;
#endif
}else if( strcmp(z,"-append")==0 ){
data.openMode = SHELL_OPEN_APPENDVFS;
#ifdef SQLITE_ENABLE_DESERIALIZE
}else if( strcmp(z,"-deserialize")==0 ){
data.openMode = SHELL_OPEN_DESERIALIZE;
#endif
}else if( strcmp(z,"-readonly")==0 ){
data.openMode = SHELL_OPEN_READONLY;
}else if( strcmp(z,"-ascii")==0 ){
data.mode = MODE_Ascii;
sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,
SEP_Unit);
| > > | 16989 16990 16991 16992 16993 16994 16995 16996 16997 16998 16999 17000 17001 17002 17003 17004 |
data.openMode = SHELL_OPEN_ZIPFILE;
#endif
}else if( strcmp(z,"-append")==0 ){
data.openMode = SHELL_OPEN_APPENDVFS;
#ifdef SQLITE_ENABLE_DESERIALIZE
}else if( strcmp(z,"-deserialize")==0 ){
data.openMode = SHELL_OPEN_DESERIALIZE;
}else if( strcmp(z,"-maxsize")==0 && i+1<argc ){
data.szMax = integerValue(argv[++i]);
#endif
}else if( strcmp(z,"-readonly")==0 ){
data.openMode = SHELL_OPEN_READONLY;
}else if( strcmp(z,"-ascii")==0 ){
data.mode = MODE_Ascii;
sqlite3_snprintf(sizeof(data.colSeparator), data.colSeparator,
SEP_Unit);
|
| ︙ | ︙ | |||
16354 16355 16356 16357 16358 16359 16360 16361 16362 16363 16364 16365 16366 16367 |
}else if( strcmp(z,"-heap")==0 ){
i++;
}else if( strcmp(z,"-pagecache")==0 ){
i+=2;
}else if( strcmp(z,"-lookaside")==0 ){
i+=2;
}else if( strcmp(z,"-mmap")==0 ){
i++;
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
}else if( strcmp(z,"-sorterref")==0 ){
i++;
#endif
}else if( strcmp(z,"-vfs")==0 ){
i++;
| > > | 17046 17047 17048 17049 17050 17051 17052 17053 17054 17055 17056 17057 17058 17059 17060 17061 |
}else if( strcmp(z,"-heap")==0 ){
i++;
}else if( strcmp(z,"-pagecache")==0 ){
i+=2;
}else if( strcmp(z,"-lookaside")==0 ){
i+=2;
}else if( strcmp(z,"-mmap")==0 ){
i++;
}else if( strcmp(z,"-memtrace")==0 ){
i++;
#ifdef SQLITE_ENABLE_SORTER_REFERENCES
}else if( strcmp(z,"-sorterref")==0 ){
i++;
#endif
}else if( strcmp(z,"-vfs")==0 ){
i++;
|
| ︙ | ︙ | |||
16472 16473 16474 16475 16476 16477 16478 |
}
if( zHistory ){ shell_read_history(zHistory); }
#if HAVE_READLINE || HAVE_EDITLINE
rl_attempted_completion_function = readline_completion;
#elif HAVE_LINENOISE
linenoiseSetCompletionCallback(linenoise_completion);
#endif
| > | > | | 17166 17167 17168 17169 17170 17171 17172 17173 17174 17175 17176 17177 17178 17179 17180 17181 17182 17183 17184 17185 17186 17187 17188 17189 |
}
if( zHistory ){ shell_read_history(zHistory); }
#if HAVE_READLINE || HAVE_EDITLINE
rl_attempted_completion_function = readline_completion;
#elif HAVE_LINENOISE
linenoiseSetCompletionCallback(linenoise_completion);
#endif
data.in = 0;
rc = process_input(&data);
if( zHistory ){
shell_stifle_history(2000);
shell_write_history(zHistory);
free(zHistory);
}
}else{
data.in = stdin;
rc = process_input(&data);
}
}
set_table_name(&data, 0);
if( data.db ){
session_close_all(&data);
close_db(data.db);
}
|
| ︙ | ︙ |
Changes to src/sitemap.c.
| ︙ | ︙ | |||
191 192 193 194 195 196 197 |
@ <li>%z(href("%R/bloblist"))List of Artifacts</a></li>
@ <li>%z(href("%R/timewarps"))List of "Timewarp" Check-ins</a></li>
@ </ul>
@ </li>
}
@ <li>Help
@ <ul>
| > > > > | | > > | > | 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
@ <li>%z(href("%R/bloblist"))List of Artifacts</a></li>
@ <li>%z(href("%R/timewarps"))List of "Timewarp" Check-ins</a></li>
@ </ul>
@ </li>
}
@ <li>Help
@ <ul>
if( g.perm.Admin || g.perm.Write ||
g.perm.WrForum || g.perm.WrTForum ||
g.perm.NewWiki || g.perm.ApndWiki || g.perm.WrWiki || g.perm.ModWiki ||
g.perm.NewTkt || g.perm.ApndTkt || g.perm.WrTkt || g.perm.ModTkt ){
@ <li>%z(href("%R/wiki_rules"))Wiki Formatting Rules</a></li>
@ <li>%z(href("%R/md_rules"))Markdown Formatting Rules</a></li>
}
@ <li>%z(href("%R/help"))List of All Commands and Web Pages</a></li>
@ <li>%z(href("%R/test-all-help"))All "help" text on a single page</a></li>
if( g.perm.Admin || g.perm.Write || g.perm.WrUnver ){
@ <li>%z(href("%R/mimetype_list"))Filename suffix to MIME type map</a></li>
}
@ </ul></li>
if( g.perm.Admin ){
@ <li>%z(href("%R/setup"))Administration Pages</a>
@ <ul>
@ <li>%z(href("%R/modreq"))Pending Moderation Requests</a></li>
@ <li>%z(href("%R/admin_log"))Admin log</a></li>
@ <li>%z(href("%R/cachestat"))Status of the web-page cache</a></li>
|
| ︙ | ︙ |
Changes to src/sqlcmd.c.
| ︙ | ︙ | |||
146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
const void *notUsed
){
add_content_sql_commands(db);
db_add_aux_functions(db);
re_add_sql_func(db);
search_sql_setup(db);
foci_register(db);
g.repositoryOpen = 1;
g.db = db;
sqlite3_db_config(db, SQLITE_DBCONFIG_MAINDBNAME, "repository");
db_maybe_set_encryption_key(db, g.zRepositoryName);
if( g.zLocalDbName ){
char *zSql = sqlite3_mprintf("ATTACH %Q AS 'localdb' KEY ''",
g.zLocalDbName);
| > | 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
const void *notUsed
){
add_content_sql_commands(db);
db_add_aux_functions(db);
re_add_sql_func(db);
search_sql_setup(db);
foci_register(db);
deltafunc_init(db);
g.repositoryOpen = 1;
g.db = db;
sqlite3_db_config(db, SQLITE_DBCONFIG_MAINDBNAME, "repository");
db_maybe_set_encryption_key(db, g.zRepositoryName);
if( g.zLocalDbName ){
char *zSql = sqlite3_mprintf("ATTACH %Q AS 'localdb' KEY ''",
g.zLocalDbName);
|
| ︙ | ︙ | |||
253 254 255 256 257 258 259 | ** ** WARNING: Careless use of this command can corrupt a Fossil repository ** in ways that are unrecoverable. Be sure you know what you are doing before ** running any SQL commands that modify the repository database. ** ** The following extensions to the usual SQLite commands are provided: ** | | | > > > > > > > > | | | | > > > > > > > > | < | | 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 |
**
** WARNING: Careless use of this command can corrupt a Fossil repository
** in ways that are unrecoverable. Be sure you know what you are doing before
** running any SQL commands that modify the repository database.
**
** The following extensions to the usual SQLite commands are provided:
**
** checkin_mtime(X,Y) Return the mtime for the file Y (a BLOB.RID)
** found in check-in X (another BLOB.RID value).
**
** compress(X) Compress text X.
**
** content(X) Return the content of artifact X. X can be an
** artifact hash or hash prefix or a tag. Artifacts
** are stored compressed and deltaed. This function
** does all necessary decompression and undeltaing.
**
** decompress(X) Decompress text X. Undoes the work of
** compress(X).
**
** delta_apply(X,D) Apply delta D to source blob X and return
** the result.
**
** delta_create(X,Y) Create and return a delta that will convert
** X into Y.
**
** delta_output_size(D) Return the number of bytes of output to expect
** when applying delta D
**
** delta_parse(D) A table-valued function that deconstructs
** delta D and returns rows for each element of
** that delta.
**
** files_of_checkin(X) A table-valued function that returns info on
** all files contained in check-in X. Example:
** SELECT * FROM files_of_checkin('trunk');
**
** now() Return the number of seconds since 1970.
**
** REGEXP The REGEXP operator works, unlike in
** standard SQLite.
**
** symbolic_name_to_rid(X) Return the BLOB.RID corresponding to symbolic
** name X.
*/
void cmd_sqlite3(void){
int noRepository;
const char *zConfigDb;
extern int sqlite3_shell(int, char**);
#ifdef FOSSIL_ENABLE_TH1_HOOKS
g.fNoThHook = 1;
|
| ︙ | ︙ |
Changes to src/sqlite3.c.
more than 10,000 changes
Changes to src/sqlite3.h.
| ︙ | ︙ | |||
119 120 121 122 123 124 125 | ** been edited in any way since it was last checked in, then the last ** four hexadecimal digits of the hash may be modified. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ | | | | | 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 | ** been edited in any way since it was last checked in, then the last ** four hexadecimal digits of the hash may be modified. ** ** See also: [sqlite3_libversion()], ** [sqlite3_libversion_number()], [sqlite3_sourceid()], ** [sqlite_version()] and [sqlite_source_id()]. */ #define SQLITE_VERSION "3.28.0" #define SQLITE_VERSION_NUMBER 3028000 #define SQLITE_SOURCE_ID "2019-04-16 19:49:53 884b4b7e502b4e991677b53971277adfaf0a04a284f8e483e2553d0f83156b50" /* ** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version sqlite3_sourceid ** ** These interfaces provide the same information as the [SQLITE_VERSION], ** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros |
| ︙ | ︙ | |||
185 186 187 188 189 190 191 192 193 194 195 196 197 198 | ** ** See also: SQL functions [sqlite_compileoption_used()] and ** [sqlite_compileoption_get()] and the [compile_options pragma]. */ #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS SQLITE_API int sqlite3_compileoption_used(const char *zOptName); SQLITE_API const char *sqlite3_compileoption_get(int N); #endif /* ** CAPI3REF: Test To See If The Library Is Threadsafe ** ** ^The sqlite3_threadsafe() function returns zero if and only if ** SQLite was compiled with mutexing code omitted due to the | > > > | 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | ** ** See also: SQL functions [sqlite_compileoption_used()] and ** [sqlite_compileoption_get()] and the [compile_options pragma]. */ #ifndef SQLITE_OMIT_COMPILEOPTION_DIAGS SQLITE_API int sqlite3_compileoption_used(const char *zOptName); SQLITE_API const char *sqlite3_compileoption_get(int N); #else # define sqlite3_compileoption_used(X) 0 # define sqlite3_compileoption_get(X) ((void*)0) #endif /* ** CAPI3REF: Test To See If The Library Is Threadsafe ** ** ^The sqlite3_threadsafe() function returns zero if and only if ** SQLite was compiled with mutexing code omitted due to the |
| ︙ | ︙ | |||
819 820 821 822 823 824 825 826 827 828 829 830 831 832 | ** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS ** layer a hint of how large the database file will grow to be during the ** current transaction. This hint is not guaranteed to be accurate but it ** is often close. The underlying VFS might choose to preallocate database ** file space based on this hint in order to help writes to the database ** file run faster. ** ** <li>[[SQLITE_FCNTL_CHUNK_SIZE]] ** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS ** extends and truncates the database file in chunks of a size specified ** by the user. The fourth argument to [sqlite3_file_control()] should ** point to an integer (type int) containing the new chunk-size to use ** for the nominated database. Allocating database file space in large ** chunks (say 1MB at a time), may reduce file-system fragmentation and | > > > > > > > > > | 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 | ** The [SQLITE_FCNTL_SIZE_HINT] opcode is used by SQLite to give the VFS ** layer a hint of how large the database file will grow to be during the ** current transaction. This hint is not guaranteed to be accurate but it ** is often close. The underlying VFS might choose to preallocate database ** file space based on this hint in order to help writes to the database ** file run faster. ** ** <li>[[SQLITE_FCNTL_SIZE_LIMIT]] ** The [SQLITE_FCNTL_SIZE_LIMIT] opcode is used by in-memory VFS that ** implements [sqlite3_deserialize()] to set an upper bound on the size ** of the in-memory database. The argument is a pointer to a [sqlite3_int64]. ** If the integer pointed to is negative, then it is filled in with the ** current limit. Otherwise the limit is set to the larger of the value ** of the integer pointed to and the current database size. The integer ** pointed to is set to the new limit. ** ** <li>[[SQLITE_FCNTL_CHUNK_SIZE]] ** The [SQLITE_FCNTL_CHUNK_SIZE] opcode is used to request that the VFS ** extends and truncates the database file in chunks of a size specified ** by the user. The fourth argument to [sqlite3_file_control()] should ** point to an integer (type int) containing the new chunk-size to use ** for the nominated database. Allocating database file space in large ** chunks (say 1MB at a time), may reduce file-system fragmentation and |
| ︙ | ︙ | |||
1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 | #define SQLITE_FCNTL_WIN32_GET_HANDLE 29 #define SQLITE_FCNTL_PDB 30 #define SQLITE_FCNTL_BEGIN_ATOMIC_WRITE 31 #define SQLITE_FCNTL_COMMIT_ATOMIC_WRITE 32 #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33 #define SQLITE_FCNTL_LOCK_TIMEOUT 34 #define SQLITE_FCNTL_DATA_VERSION 35 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO | > | 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 | #define SQLITE_FCNTL_WIN32_GET_HANDLE 29 #define SQLITE_FCNTL_PDB 30 #define SQLITE_FCNTL_BEGIN_ATOMIC_WRITE 31 #define SQLITE_FCNTL_COMMIT_ATOMIC_WRITE 32 #define SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE 33 #define SQLITE_FCNTL_LOCK_TIMEOUT 34 #define SQLITE_FCNTL_DATA_VERSION 35 #define SQLITE_FCNTL_SIZE_LIMIT 36 /* deprecated names */ #define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE #define SQLITE_SET_LOCKPROXYFILE SQLITE_FCNTL_SET_LOCKPROXYFILE #define SQLITE_LAST_ERRNO SQLITE_FCNTL_LAST_ERRNO |
| ︙ | ︙ | |||
1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 | ** than the configured sorter-reference size threshold - then a reference ** is stored in each sorted record and the required column values loaded ** from the database as records are returned in sorted order. The default ** value for this option is to never use this optimization. Specifying a ** negative value for this option restores the default behaviour. ** This option is only available if SQLite is compiled with the ** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option. ** </dl> */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ #define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ #define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ | > > > > > > > > > > > | 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 | ** than the configured sorter-reference size threshold - then a reference ** is stored in each sorted record and the required column values loaded ** from the database as records are returned in sorted order. The default ** value for this option is to never use this optimization. Specifying a ** negative value for this option restores the default behaviour. ** This option is only available if SQLite is compiled with the ** [SQLITE_ENABLE_SORTER_REFERENCES] compile-time option. ** ** [[SQLITE_CONFIG_MEMDB_MAXSIZE]] ** <dt>SQLITE_CONFIG_MEMDB_MAXSIZE ** <dd>The SQLITE_CONFIG_MEMDB_MAXSIZE option accepts a single parameter ** [sqlite3_int64] parameter which is the default maximum size for an in-memory ** database created using [sqlite3_deserialize()]. This default maximum ** size can be adjusted up or down for individual databases using the ** [SQLITE_FCNTL_SIZE_LIMIT] [sqlite3_file_control|file-control]. If this ** configuration setting is never used, then the default maximum is determined ** by the [SQLITE_MEMDB_DEFAULT_MAXSIZE] compile-time option. If that ** compile-time option is not set, then the default maximum is 1073741824. ** </dl> */ #define SQLITE_CONFIG_SINGLETHREAD 1 /* nil */ #define SQLITE_CONFIG_MULTITHREAD 2 /* nil */ #define SQLITE_CONFIG_SERIALIZED 3 /* nil */ #define SQLITE_CONFIG_MALLOC 4 /* sqlite3_mem_methods* */ #define SQLITE_CONFIG_GETMALLOC 5 /* sqlite3_mem_methods* */ |
| ︙ | ︙ | |||
1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 | #define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ #define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ /* ** CAPI3REF: Database Connection Configuration Options ** ** These constants are the available integer configuration options that ** can be passed as the second argument to the [sqlite3_db_config()] interface. ** | > | 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 | #define SQLITE_CONFIG_MMAP_SIZE 22 /* sqlite3_int64, sqlite3_int64 */ #define SQLITE_CONFIG_WIN32_HEAPSIZE 23 /* int nByte */ #define SQLITE_CONFIG_PCACHE_HDRSZ 24 /* int *psz */ #define SQLITE_CONFIG_PMASZ 25 /* unsigned int szPma */ #define SQLITE_CONFIG_STMTJRNL_SPILL 26 /* int nByte */ #define SQLITE_CONFIG_SMALL_MALLOC 27 /* boolean */ #define SQLITE_CONFIG_SORTERREF_SIZE 28 /* int nByte */ #define SQLITE_CONFIG_MEMDB_MAXSIZE 29 /* sqlite3_int64 */ /* ** CAPI3REF: Database Connection Configuration Options ** ** These constants are the available integer configuration options that ** can be passed as the second argument to the [sqlite3_db_config()] interface. ** |
| ︙ | ︙ | |||
2060 2061 2062 2063 2064 2065 2066 | ** The second parameter is a pointer to an integer into which ** is written 0 or 1 to indicate whether triggers are disabled or enabled ** following this call. The second parameter may be a NULL pointer, in ** which case the trigger setting is not reported back. </dd> ** ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]] ** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt> | | | | 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 | ** The second parameter is a pointer to an integer into which ** is written 0 or 1 to indicate whether triggers are disabled or enabled ** following this call. The second parameter may be a NULL pointer, in ** which case the trigger setting is not reported back. </dd> ** ** [[SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER]] ** <dt>SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER</dt> ** <dd> ^This option is used to enable or disable the ** [fts3_tokenizer()] function which is part of the ** [FTS3] full-text search engine extension. ** There should be two additional arguments. ** The first argument is an integer which is 0 to disable fts3_tokenizer() or ** positive to enable fts3_tokenizer() or negative to leave the setting ** unchanged. ** The second parameter is a pointer to an integer into which ** is written 0 or 1 to indicate whether fts3_tokenizer is disabled or enabled |
| ︙ | ︙ | |||
2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 | ** features include but are not limited to the following: ** <ul> ** <li> The [PRAGMA writable_schema=ON] statement. ** <li> Writes to the [sqlite_dbpage] virtual table. ** <li> Direct writes to [shadow tables]. ** </ul> ** </dd> ** </dl> */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ #define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1004 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION 1005 /* int int* */ #define SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE 1006 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_QPSG 1007 /* int int* */ #define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */ #define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */ #define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */ | > > > > > > > > > > > > | | 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 | ** features include but are not limited to the following: ** <ul> ** <li> The [PRAGMA writable_schema=ON] statement. ** <li> Writes to the [sqlite_dbpage] virtual table. ** <li> Direct writes to [shadow tables]. ** </ul> ** </dd> ** ** [[SQLITE_DBCONFIG_WRITABLE_SCHEMA]] <dt>SQLITE_DBCONFIG_WRITABLE_SCHEMA</dt> ** <dd>The SQLITE_DBCONFIG_WRITABLE_SCHEMA option activates or deactivates the ** "writable_schema" flag. This has the same effect and is logically equivalent ** to setting [PRAGMA writable_schema=ON] or [PRAGMA writable_schema=OFF]. ** The first argument to this setting is an integer which is 0 to disable ** the writable_schema, positive to enable writable_schema, or negative to ** leave the setting unchanged. The second parameter is a pointer to an ** integer into which is written 0 or 1 to indicate whether the writable_schema ** is enabled or disabled following this call. ** </dd> ** </dl> */ #define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */ #define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */ #define SQLITE_DBCONFIG_ENABLE_FKEY 1002 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_TRIGGER 1003 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER 1004 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION 1005 /* int int* */ #define SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE 1006 /* int int* */ #define SQLITE_DBCONFIG_ENABLE_QPSG 1007 /* int int* */ #define SQLITE_DBCONFIG_TRIGGER_EQP 1008 /* int int* */ #define SQLITE_DBCONFIG_RESET_DATABASE 1009 /* int int* */ #define SQLITE_DBCONFIG_DEFENSIVE 1010 /* int int* */ #define SQLITE_DBCONFIG_WRITABLE_SCHEMA 1011 /* int int* */ #define SQLITE_DBCONFIG_MAX 1011 /* Largest DBCONFIG */ /* ** CAPI3REF: Enable Or Disable Extended Result Codes ** METHOD: sqlite3 ** ** ^The sqlite3_extended_result_codes() routine enables or disables the ** [extended result codes] feature of SQLite. ^The extended result |
| ︙ | ︙ | |||
2343 2344 2345 2346 2347 2348 2349 | ** does not affect the value returned by sqlite3_total_changes(). ** ** ^Changes made as part of [foreign key actions] are included in the ** count, but those made as part of REPLACE constraint resolution are ** not. ^Changes to a view that are intercepted by INSTEAD OF triggers ** are not counted. ** | | | 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 | ** does not affect the value returned by sqlite3_total_changes(). ** ** ^Changes made as part of [foreign key actions] are included in the ** count, but those made as part of REPLACE constraint resolution are ** not. ^Changes to a view that are intercepted by INSTEAD OF triggers ** are not counted. ** ** The [sqlite3_total_changes(D)] interface only reports the number ** of rows that changed due to SQL statement run against database ** connection D. Any changes by other database connections are ignored. ** To detect changes against a database file from other database ** connections use the [PRAGMA data_version] command or the ** [SQLITE_FCNTL_DATA_VERSION] [file control]. ** ** If a separate thread makes changes on the same database connection |
| ︙ | ︙ | |||
2987 2988 2989 2990 2991 2992 2993 | ** ^The callback function registered by sqlite3_profile() is invoked ** as each SQL statement finishes. ^The profile callback contains ** the original statement text and an estimate of wall-clock time ** of how long that statement took to run. ^The profile callback ** time is in units of nanoseconds, however the current implementation ** is only capable of millisecond resolution so the six least significant ** digits in the time are meaningless. Future versions of SQLite | | | | | 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 | ** ^The callback function registered by sqlite3_profile() is invoked ** as each SQL statement finishes. ^The profile callback contains ** the original statement text and an estimate of wall-clock time ** of how long that statement took to run. ^The profile callback ** time is in units of nanoseconds, however the current implementation ** is only capable of millisecond resolution so the six least significant ** digits in the time are meaningless. Future versions of SQLite ** might provide greater resolution on the profiler callback. Invoking ** either [sqlite3_trace()] or [sqlite3_trace_v2()] will cancel the ** profile callback. */ SQLITE_API SQLITE_DEPRECATED void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*); SQLITE_API SQLITE_DEPRECATED void *sqlite3_profile(sqlite3*, void(*xProfile)(void*,const char*,sqlite3_uint64), void*); /* |
| ︙ | ︙ | |||
3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 | ** zero is returned. ** ** If F is a NULL pointer, then sqlite3_uri_parameter(F,P) returns NULL and ** sqlite3_uri_boolean(F,P,B) returns B. If F is not a NULL pointer and ** is not a database file pathname pointer that SQLite passed into the xOpen ** VFS method, then the behavior of this routine is undefined and probably ** undesirable. */ SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64); /* | > > | 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 | ** zero is returned. ** ** If F is a NULL pointer, then sqlite3_uri_parameter(F,P) returns NULL and ** sqlite3_uri_boolean(F,P,B) returns B. If F is not a NULL pointer and ** is not a database file pathname pointer that SQLite passed into the xOpen ** VFS method, then the behavior of this routine is undefined and probably ** undesirable. ** ** See the [URI filename] documentation for additional information. */ SQLITE_API const char *sqlite3_uri_parameter(const char *zFilename, const char *zParam); SQLITE_API int sqlite3_uri_boolean(const char *zFile, const char *zParam, int bDefault); SQLITE_API sqlite3_int64 sqlite3_uri_int64(const char*, const char*, sqlite3_int64); /* |
| ︙ | ︙ | |||
3625 3626 3627 3628 3629 3630 3631 | ** and [sqlite3_prepare16_v3()] assume that the prepared statement will ** be used just once or at most a few times and then destroyed using ** [sqlite3_finalize()] relatively soon. The current implementation acts ** on this hint by avoiding the use of [lookaside memory] so as not to ** deplete the limited store of lookaside memory. Future versions of ** SQLite may act on this hint differently. ** | | | < | | | > | | > > > > > | 3664 3665 3666 3667 3668 3669 3670 3671 3672 3673 3674 3675 3676 3677 3678 3679 3680 3681 3682 3683 3684 3685 3686 3687 3688 3689 3690 3691 3692 3693 3694 |
** and [sqlite3_prepare16_v3()] assume that the prepared statement will
** be used just once or at most a few times and then destroyed using
** [sqlite3_finalize()] relatively soon. The current implementation acts
** on this hint by avoiding the use of [lookaside memory] so as not to
** deplete the limited store of lookaside memory. Future versions of
** SQLite may act on this hint differently.
**
** [[SQLITE_PREPARE_NORMALIZE]] <dt>SQLITE_PREPARE_NORMALIZE</dt>
** <dd>The SQLITE_PREPARE_NORMALIZE flag is a no-op. This flag used
** to be required for any prepared statement that wanted to use the
** [sqlite3_normalized_sql()] interface. However, the
** [sqlite3_normalized_sql()] interface is now available to all
** prepared statements, regardless of whether or not they use this
** flag.
**
** [[SQLITE_PREPARE_NO_VTAB]] <dt>SQLITE_PREPARE_NO_VTAB</dt>
** <dd>The SQLITE_PREPARE_NO_VTAB flag causes the SQL compiler
** to return an error (error code SQLITE_ERROR) if the statement uses
** any virtual tables.
** </dl>
*/
#define SQLITE_PREPARE_PERSISTENT 0x01
#define SQLITE_PREPARE_NORMALIZE 0x02
#define SQLITE_PREPARE_NO_VTAB 0x04
/*
** CAPI3REF: Compiling An SQL Statement
** KEYWORDS: {SQL statement compiler}
** METHOD: sqlite3
** CONSTRUCTOR: sqlite3_stmt
**
|
| ︙ | ︙ | |||
3861 3862 3863 3864 3865 3866 3867 3868 3869 3870 3871 3872 3873 3874 | ** ^The sqlite3_stmt_readonly() interface returns true for [BEGIN] since ** [BEGIN] merely sets internal flags, but the [BEGIN|BEGIN IMMEDIATE] and ** [BEGIN|BEGIN EXCLUSIVE] commands do touch the database and so ** sqlite3_stmt_readonly() returns false for those commands. */ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); /* ** CAPI3REF: Determine If A Prepared Statement Has Been Reset ** METHOD: sqlite3_stmt ** ** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the ** [prepared statement] S has been stepped at least once using ** [sqlite3_step(S)] but has neither run to completion (returned | > > > > > > > > > > > > | 3905 3906 3907 3908 3909 3910 3911 3912 3913 3914 3915 3916 3917 3918 3919 3920 3921 3922 3923 3924 3925 3926 3927 3928 3929 3930 | ** ^The sqlite3_stmt_readonly() interface returns true for [BEGIN] since ** [BEGIN] merely sets internal flags, but the [BEGIN|BEGIN IMMEDIATE] and ** [BEGIN|BEGIN EXCLUSIVE] commands do touch the database and so ** sqlite3_stmt_readonly() returns false for those commands. */ SQLITE_API int sqlite3_stmt_readonly(sqlite3_stmt *pStmt); /* ** CAPI3REF: Query The EXPLAIN Setting For A Prepared Statement ** METHOD: sqlite3_stmt ** ** ^The sqlite3_stmt_isexplain(S) interface returns 1 if the ** prepared statement S is an EXPLAIN statement, or 2 if the ** statement S is an EXPLAIN QUERY PLAN. ** ^The sqlite3_stmt_isexplain(S) interface returns 0 if S is ** an ordinary statement or a NULL pointer. */ SQLITE_API int sqlite3_stmt_isexplain(sqlite3_stmt *pStmt); /* ** CAPI3REF: Determine If A Prepared Statement Has Been Reset ** METHOD: sqlite3_stmt ** ** ^The sqlite3_stmt_busy(S) interface returns true (non-zero) if the ** [prepared statement] S has been stepped at least once using ** [sqlite3_step(S)] but has neither run to completion (returned |
| ︙ | ︙ | |||
4000 4001 4002 4003 4004 4005 4006 | ** the value of the fourth parameter then the resulting string value will ** contain embedded NULs. The result of expressions involving strings ** with embedded NULs is undefined. ** ** ^The fifth argument to the BLOB and string binding interfaces ** is a destructor used to dispose of the BLOB or ** string after SQLite has finished with it. ^The destructor is called | | > > | 4056 4057 4058 4059 4060 4061 4062 4063 4064 4065 4066 4067 4068 4069 4070 4071 4072 | ** the value of the fourth parameter then the resulting string value will ** contain embedded NULs. The result of expressions involving strings ** with embedded NULs is undefined. ** ** ^The fifth argument to the BLOB and string binding interfaces ** is a destructor used to dispose of the BLOB or ** string after SQLite has finished with it. ^The destructor is called ** to dispose of the BLOB or string even if the call to the bind API fails, ** except the destructor is not called if the third parameter is a NULL ** pointer or the fourth parameter is negative. ** ^If the fifth argument is ** the special value [SQLITE_STATIC], then SQLite assumes that the ** information is in static, unmanaged space and does not need to be freed. ** ^If the fifth argument has the value [SQLITE_TRANSIENT], then ** SQLite makes its own private copy of the data immediately, before ** the sqlite3_bind_*() routine returns. ** |
| ︙ | ︙ | |||
4917 4918 4919 4920 4921 4922 4923 4924 4925 4926 4927 4928 4929 4930 | ** <tr><td><b>sqlite3_value_type</b><td>→<td>Default ** datatype of the value ** <tr><td><b>sqlite3_value_numeric_type </b> ** <td>→ <td>Best numeric datatype of the value ** <tr><td><b>sqlite3_value_nochange </b> ** <td>→ <td>True if the column is unchanged in an UPDATE ** against a virtual table. ** </table></blockquote> ** ** <b>Details:</b> ** ** These routines extract type, size, and content information from ** [protected sqlite3_value] objects. Protected sqlite3_value objects ** are used to pass parameter information into implementation of | > > | 4975 4976 4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 | ** <tr><td><b>sqlite3_value_type</b><td>→<td>Default ** datatype of the value ** <tr><td><b>sqlite3_value_numeric_type </b> ** <td>→ <td>Best numeric datatype of the value ** <tr><td><b>sqlite3_value_nochange </b> ** <td>→ <td>True if the column is unchanged in an UPDATE ** against a virtual table. ** <tr><td><b>sqlite3_value_frombind </b> ** <td>→ <td>True if value originated from a [bound parameter] ** </table></blockquote> ** ** <b>Details:</b> ** ** These routines extract type, size, and content information from ** [protected sqlite3_value] objects. Protected sqlite3_value objects ** are used to pass parameter information into implementation of |
| ︙ | ︙ | |||
4977 4978 4979 4980 4981 4982 4983 4984 4985 4986 4987 4988 4989 4990 | ** the value for that column returned without setting a result (probably ** because it queried [sqlite3_vtab_nochange()] and found that the column ** was unchanging). ^Within an [xUpdate] method, any value for which ** sqlite3_value_nochange(X) is true will in all other respects appear ** to be a NULL value. If sqlite3_value_nochange(X) is invoked anywhere other ** than within an [xUpdate] method call for an UPDATE statement, then ** the return value is arbitrary and meaningless. ** ** Please pay particular attention to the fact that the pointer returned ** from [sqlite3_value_blob()], [sqlite3_value_text()], or ** [sqlite3_value_text16()] can be invalidated by a subsequent call to ** [sqlite3_value_bytes()], [sqlite3_value_bytes16()], [sqlite3_value_text()], ** or [sqlite3_value_text16()]. ** | > > > > > | 5037 5038 5039 5040 5041 5042 5043 5044 5045 5046 5047 5048 5049 5050 5051 5052 5053 5054 5055 | ** the value for that column returned without setting a result (probably ** because it queried [sqlite3_vtab_nochange()] and found that the column ** was unchanging). ^Within an [xUpdate] method, any value for which ** sqlite3_value_nochange(X) is true will in all other respects appear ** to be a NULL value. If sqlite3_value_nochange(X) is invoked anywhere other ** than within an [xUpdate] method call for an UPDATE statement, then ** the return value is arbitrary and meaningless. ** ** ^The sqlite3_value_frombind(X) interface returns non-zero if the ** value X originated from one of the [sqlite3_bind_int|sqlite3_bind()] ** interfaces. ^If X comes from an SQL literal value, or a table column, ** and expression, then sqlite3_value_frombind(X) returns zero. ** ** Please pay particular attention to the fact that the pointer returned ** from [sqlite3_value_blob()], [sqlite3_value_text()], or ** [sqlite3_value_text16()] can be invalidated by a subsequent call to ** [sqlite3_value_bytes()], [sqlite3_value_bytes16()], [sqlite3_value_text()], ** or [sqlite3_value_text16()]. ** |
| ︙ | ︙ | |||
5023 5024 5025 5026 5027 5028 5029 5030 5031 5032 5033 5034 5035 5036 | SQLITE_API const void *sqlite3_value_text16le(sqlite3_value*); SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); SQLITE_API int sqlite3_value_bytes(sqlite3_value*); SQLITE_API int sqlite3_value_bytes16(sqlite3_value*); SQLITE_API int sqlite3_value_type(sqlite3_value*); SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); SQLITE_API int sqlite3_value_nochange(sqlite3_value*); /* ** CAPI3REF: Finding The Subtype Of SQL Values ** METHOD: sqlite3_value ** ** The sqlite3_value_subtype(V) function returns the subtype for ** an [application-defined SQL function] argument V. The subtype | > | 5088 5089 5090 5091 5092 5093 5094 5095 5096 5097 5098 5099 5100 5101 5102 | SQLITE_API const void *sqlite3_value_text16le(sqlite3_value*); SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); SQLITE_API int sqlite3_value_bytes(sqlite3_value*); SQLITE_API int sqlite3_value_bytes16(sqlite3_value*); SQLITE_API int sqlite3_value_type(sqlite3_value*); SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); SQLITE_API int sqlite3_value_nochange(sqlite3_value*); SQLITE_API int sqlite3_value_frombind(sqlite3_value*); /* ** CAPI3REF: Finding The Subtype Of SQL Values ** METHOD: sqlite3_value ** ** The sqlite3_value_subtype(V) function returns the subtype for ** an [application-defined SQL function] argument V. The subtype |
| ︙ | ︙ | |||
5758 5759 5760 5761 5762 5763 5764 | ** CAPI3REF: Return The Filename For A Database Connection ** METHOD: sqlite3 ** ** ^The sqlite3_db_filename(D,N) interface returns a pointer to a filename ** associated with database N of connection D. ^The main database file ** has the name "main". If there is no attached database N on the database ** connection D, or if database N is a temporary or in-memory database, then | | | 5824 5825 5826 5827 5828 5829 5830 5831 5832 5833 5834 5835 5836 5837 5838 | ** CAPI3REF: Return The Filename For A Database Connection ** METHOD: sqlite3 ** ** ^The sqlite3_db_filename(D,N) interface returns a pointer to a filename ** associated with database N of connection D. ^The main database file ** has the name "main". If there is no attached database N on the database ** connection D, or if database N is a temporary or in-memory database, then ** this function will return either a NULL pointer or an empty string. ** ** ^The filename returned by this function is the output of the ** xFullPathname method of the [VFS]. ^In other words, the filename ** will be an absolute pathname, even if the filename used ** to open the database originally was a URI or relative pathname. */ SQLITE_API const char *sqlite3_db_filename(sqlite3 *db, const char *zDbName); |
| ︙ | ︙ | |||
9992 9993 9994 9995 9996 9997 9998 | ** ** If argument pzTab is not NULL, then *pzTab is set to point to a ** nul-terminated utf-8 encoded string containing the name of the table ** affected by the current change. The buffer remains valid until either ** sqlite3changeset_next() is called on the iterator or until the ** conflict-handler function returns. If pnCol is not NULL, then *pnCol is ** set to the number of columns in the table affected by the change. If | | | 10058 10059 10060 10061 10062 10063 10064 10065 10066 10067 10068 10069 10070 10071 10072 | ** ** If argument pzTab is not NULL, then *pzTab is set to point to a ** nul-terminated utf-8 encoded string containing the name of the table ** affected by the current change. The buffer remains valid until either ** sqlite3changeset_next() is called on the iterator or until the ** conflict-handler function returns. If pnCol is not NULL, then *pnCol is ** set to the number of columns in the table affected by the change. If ** pbIndirect is not NULL, then *pbIndirect is set to true (1) if the change ** is an indirect change, or false (0) otherwise. See the documentation for ** [sqlite3session_indirect()] for a description of direct and indirect ** changes. Finally, if pOp is not NULL, then *pOp is set to one of ** [SQLITE_INSERT], [SQLITE_DELETE] or [SQLITE_UPDATE], depending on the ** type of change that the iterator currently points to. ** ** If no error occurs, SQLITE_OK is returned. If an error does occur, an |
| ︙ | ︙ | |||
10859 10860 10861 10862 10863 10864 10865 | ** CAPI3REF: Rebase a changeset ** EXPERIMENTAL ** ** Argument pIn must point to a buffer containing a changeset nIn bytes ** in size. This function allocates and populates a buffer with a copy ** of the changeset rebased rebased according to the configuration of the ** rebaser object passed as the first argument. If successful, (*ppOut) | | | 10925 10926 10927 10928 10929 10930 10931 10932 10933 10934 10935 10936 10937 10938 10939 | ** CAPI3REF: Rebase a changeset ** EXPERIMENTAL ** ** Argument pIn must point to a buffer containing a changeset nIn bytes ** in size. This function allocates and populates a buffer with a copy ** of the changeset rebased rebased according to the configuration of the ** rebaser object passed as the first argument. If successful, (*ppOut) ** is set to point to the new buffer containing the rebased changeset and ** (*pnOut) to its size in bytes and SQLITE_OK returned. It is the ** responsibility of the caller to eventually free the new buffer using ** sqlite3_free(). Otherwise, if an error occurs, (*ppOut) and (*pnOut) ** are set to zero and an SQLite error code returned. */ SQLITE_API int sqlite3rebaser_rebase( sqlite3_rebaser*, |
| ︙ | ︙ | |||
11226 11227 11228 11229 11230 11231 11232 | ** Query for the details of phrase match iIdx within the current row. ** Phrase matches are numbered starting from zero, so the iIdx argument ** should be greater than or equal to zero and smaller than the value ** output by xInstCount(). ** ** Usually, output parameter *piPhrase is set to the phrase number, *piCol ** to the column in which it occurs and *piOff the token offset of the | < < < < | | | 11292 11293 11294 11295 11296 11297 11298 11299 11300 11301 11302 11303 11304 11305 11306 11307 | ** Query for the details of phrase match iIdx within the current row. ** Phrase matches are numbered starting from zero, so the iIdx argument ** should be greater than or equal to zero and smaller than the value ** output by xInstCount(). ** ** Usually, output parameter *piPhrase is set to the phrase number, *piCol ** to the column in which it occurs and *piOff the token offset of the ** first token of the phrase. Returns SQLITE_OK if successful, or an error ** code (i.e. SQLITE_NOMEM) if an error occurs. ** ** This API can be quite slow if used with an FTS5 table created with the ** "detail=none" or "detail=column" option. ** ** xRowid: ** Returns the rowid of the current row. ** |
| ︙ | ︙ | |||
11272 11273 11274 11275 11276 11277 11278 | ** ** ** xSetAuxdata(pFts5, pAux, xDelete) ** ** Save the pointer passed as the second argument as the extension functions ** "auxiliary data". The pointer may then be retrieved by the current or any ** future invocation of the same fts5 extension function made as part of | | | | 11334 11335 11336 11337 11338 11339 11340 11341 11342 11343 11344 11345 11346 11347 11348 11349 11350 11351 11352 11353 11354 11355 11356 11357 11358 11359 11360 11361 11362 11363 | ** ** ** xSetAuxdata(pFts5, pAux, xDelete) ** ** Save the pointer passed as the second argument as the extension functions ** "auxiliary data". The pointer may then be retrieved by the current or any ** future invocation of the same fts5 extension function made as part of ** the same MATCH query using the xGetAuxdata() API. ** ** Each extension function is allocated a single auxiliary data slot for ** each FTS query (MATCH expression). If the extension function is invoked ** more than once for a single FTS query, then all invocations share a ** single auxiliary data context. ** ** If there is already an auxiliary data pointer when this function is ** invoked, then it is replaced by the new pointer. If an xDelete callback ** was specified along with the original pointer, it is invoked at this ** point. ** ** The xDelete callback, if one is specified, is also invoked on the ** auxiliary data pointer after the FTS5 query has finished. ** ** If an error (e.g. an OOM condition) occurs within this function, ** the auxiliary data is set to NULL and an error code returned. If the ** xDelete parameter was not NULL, it is invoked on the auxiliary data ** pointer before returning. ** ** ** xGetAuxdata(pFts5, bClear) ** |
| ︙ | ︙ | |||
11520 11521 11522 11523 11524 11525 11526 | ** same token for inputs "first" and "1st". Say that token is in ** fact "first", so that when the user inserts the document "I won ** 1st place" entries are added to the index for tokens "i", "won", ** "first" and "place". If the user then queries for '1st + place', ** the tokenizer substitutes "first" for "1st" and the query works ** as expected. ** | | | | | | | 11582 11583 11584 11585 11586 11587 11588 11589 11590 11591 11592 11593 11594 11595 11596 11597 11598 11599 11600 | ** same token for inputs "first" and "1st". Say that token is in ** fact "first", so that when the user inserts the document "I won ** 1st place" entries are added to the index for tokens "i", "won", ** "first" and "place". If the user then queries for '1st + place', ** the tokenizer substitutes "first" for "1st" and the query works ** as expected. ** ** <li> By querying the index for all synonyms of each query term ** separately. In this case, when tokenizing query text, the ** tokenizer may provide multiple synonyms for a single term ** within the document. FTS5 then queries the index for each ** synonym individually. For example, faced with the query: ** ** <codeblock> ** ... MATCH 'first place'</codeblock> ** ** the tokenizer offers both "1st" and "first" as synonyms for the ** first token in the MATCH query and FTS5 effectively runs a query ** similar to: |
| ︙ | ︙ | |||
11548 11549 11550 11551 11552 11553 11554 | ** Using this method, when tokenizing document text, the tokenizer ** provides multiple synonyms for each token. So that when a ** document such as "I won first place" is tokenized, entries are ** added to the FTS index for "i", "won", "first", "1st" and ** "place". ** ** This way, even if the tokenizer does not provide synonyms | | | 11610 11611 11612 11613 11614 11615 11616 11617 11618 11619 11620 11621 11622 11623 11624 | ** Using this method, when tokenizing document text, the tokenizer ** provides multiple synonyms for each token. So that when a ** document such as "I won first place" is tokenized, entries are ** added to the FTS index for "i", "won", "first", "1st" and ** "place". ** ** This way, even if the tokenizer does not provide synonyms ** when tokenizing query text (it should not - to do so would be ** inefficient), it doesn't matter if the user queries for ** 'first + place' or '1st + place', as there are entries in the ** FTS index corresponding to both forms of the first token. ** </ol> ** ** Whether it is parsing document or query text, any call to xToken that ** specifies a <i>tflags</i> argument with the FTS5_TOKEN_COLOCATED bit |
| ︙ | ︙ |
Changes to src/stash.c.
| ︙ | ︙ | |||
19 20 21 22 23 24 25 26 27 28 29 | #include "config.h" #include "stash.h" #include <assert.h> /* ** SQL code to implement the tables needed by the stash. */ static const char zStashInit[] = @ CREATE TABLE IF NOT EXISTS localdb.stash( @ stashid INTEGER PRIMARY KEY, -- Unique stash identifier | > > > > > > > > > > > > > | > < > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
#include "config.h"
#include "stash.h"
#include <assert.h>
/*
** SQL code to implement the tables needed by the stash.
**
** Historical schema changes:
**
** 2019-01-19: stash.hash and stashfile.hash columns added. The
** corresponding stash.vid and stashfile.rid columns are
** retained for compatibility with older versions of
** fossil but are no longer used.
**
** 2016-10-16: Change the PRIMARY KEY on stashfile from (origname,stashid)
** to (newname,stashid).
**
** 2011-09-01: stashfile.isLink column added
**
*/
static const char zStashInit[] =
@ CREATE TABLE IF NOT EXISTS localdb.stash(
@ stashid INTEGER PRIMARY KEY, -- Unique stash identifier
@ vid INTEGER, -- Legacy baseline RID value. Do not use.
@ hash TEXT, -- The SHA hash for the baseline
@ comment TEXT, -- Comment for this stash. Or NULL
@ ctime TIMESTAMP -- When the stash was created
@ );
@ CREATE TABLE IF NOT EXISTS localdb.stashfile(
@ stashid INTEGER REFERENCES stash, -- Stash that contains this file
@ isAdded BOOLEAN, -- True if this is an added file
@ isRemoved BOOLEAN, -- True if this file is deleted
@ isExec BOOLEAN, -- True if file is executable
@ isLink BOOLEAN, -- True if file is a symlink
@ rid INTEGER, -- Legacy baseline RID value. Do not use
@ hash TEXT, -- Hash for baseline or NULL
@ origname TEXT, -- Original filename
@ newname TEXT, -- New name for file at next check-in
@ delta BLOB, -- Delta from baseline or raw content
@ PRIMARY KEY(newname, stashid)
@ );
@ INSERT OR IGNORE INTO vvar(name, value) VALUES('stash-next', 1);
;
/*
** Make sure the stash and stashfile tables exist and have been
** upgraded to their latest format. Create and upgrade the tables
** as necessary.
*/
static void stash_tables_exist_and_current(void){
if( db_table_has_column("localdb","stashfile","hash") ){
/* The schema is up-to-date. But it could be that an older version
** of Fossil that does no know about the stash.hash and stashfile.hash
** columns has run since the schema was updated, and added entries that
** have NULL hash columns. Check for this case, and fill in any missing
** hash values.
*/
if( db_int(0, "SELECT hash IS NULL FROM stash"
" ORDER BY stashid DESC LIMIT 1")
){
db_multi_exec(
"UPDATE stash"
" SET hash=(SELECT uuid FROM blob WHERE blob.rid=stash.vid)"
" WHERE hash IS NULL;"
"UPDATE stashfile"
" SET hash=(SELECT uuid FROM blob WHERE blob.rid=stashfile.rid)"
" WHERE hash IS NULL AND rid>0;"
);
}
return;
}
if( !db_table_exists("localdb","stashfile")
|| !db_table_exists("localdb","stash")
){
/* Tables do not exist. Create them from scratch. */
db_multi_exec("DROP TABLE IF EXISTS localdb.stash;");
db_multi_exec("DROP TABLE IF EXISTS localdb.stashfile;");
db_multi_exec(zStashInit /*works-like:""*/);
return;
}
/* The tables exists but are not necessarily current. Upgrade them
** to the latest format.
**
** We can assume the 2011-09-01 format that includes the stashfile.isLink
** column. The only upgrades we need to worry about the PRIMARY KEY
** change on 2016-10-16 and the addition of the "hash" columns on
** 2019-01-19.
*/
db_multi_exec(
"ALTER TABLE localdb.stash RENAME TO old_stash;"
"ALTER TABLE localdb.stashfile RENAME TO old_stashfile;"
);
db_multi_exec(zStashInit /*works-like:""*/);
db_multi_exec(
"INSERT INTO localdb.stash(stashid,vid,hash,comment,ctime)"
" SELECT stashid, vid,"
" (SELECT uuid FROM blob WHERE blob.rid=old_stash.vid),"
" comment, ctime FROM old_stash;"
"DROP TABLE old_stash;"
);
db_multi_exec(
"INSERT INTO localdb.stashfile(stashid,isAdded,isRemoved,isExec,"
"isLink,rid,hash,origname,newname,delta)"
" SELECT stashid, isAdded, isRemoved, isExec, isLink, rid,"
" (SELECT uuid FROM blob WHERE blob.rid=old_stashfile.rid),"
" origname, newname, delta FROM old_stashfile;"
"DROP TABLE old_stashfile;"
);
}
/*
** Update the stash.vid and stashfile.rid values after a RID renumbering
** event.
*/
void stash_rid_renumbering_event(void){
if( !db_table_has_column("localdb","stash","hash") ){
/* If the stash schema was the older style that lacked hash value, then
** recovery is not possible. Save off the old data, then reset the stash
** to empty. */
if( db_table_exists("localdb","stash") ){
db_multi_exec("ALTER TABLE stash RENAME TO broken_stash;");
fossil_print("Unrecoverable stash content stored in \"broken_stash\"\n");
}
if( db_table_exists("localdb","stashfile") ){
db_multi_exec("ALTER TABLE stashfile RENAME TO broken_stashfile;");
fossil_print("Unrecoverable stashfile content stored"
" in \"broken_stashfile\"\n");
}
}else{
/* Reset stash.vid and stash.rid values based on hashes */
db_multi_exec(
"UPDATE stash"
" SET vid=(SELECT rid FROM blob WHERE blob.uuid=stash.hash);"
"UPDATE stashfile"
" SET rid=(SELECT rid FROM blob WHERE blob.uuid=stashfile.hash)"
" WHERE hash IS NOT NULL;"
);
}
}
/*
** Add zFName to the stash given by stashid. zFName might be the name of a
** file or a directory. If a directory, add all changed files contained
** within that directory.
*/
static void stash_add_file_or_dir(int stashid, int vid, const char *zFName){
|
| ︙ | ︙ | |||
75 76 77 78 79 80 81 |
" OR pathname=%Q OR origname=%Q)",
zTreename, zTreename, zTreename, zTreename
);
}
db_prepare(&q, "%s", blob_sql_text(&sql));
blob_reset(&sql);
db_prepare(&ins,
| | | | > | 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 |
" OR pathname=%Q OR origname=%Q)",
zTreename, zTreename, zTreename, zTreename
);
}
db_prepare(&q, "%s", blob_sql_text(&sql));
blob_reset(&sql);
db_prepare(&ins,
"INSERT INTO stashfile(stashid, isAdded, isRemoved, isExec, isLink, rid, "
"hash, origname, newname, delta)"
"VALUES(%d,:isadd,:isrm,:isexe,:islink,:rid,"
"(SELECT uuid FROM blob WHERE rid=:rid),:orig,:new,:content)",
stashid
);
while( db_step(&q)==SQLITE_ROW ){
int deleted = db_column_int(&q, 0);
int rid = db_column_int(&q, 3);
const char *zName = db_column_text(&q, 4);
const char *zOrig = db_column_text(&q, 5);
|
| ︙ | ︙ | |||
169 170 171 172 173 174 175 |
zComment = blob_str(&comment);
}
stashid = db_lget_int("stash-next", 1);
db_lset_int("stash-next", stashid+1);
vid = db_lget_int("checkout", 0);
vfile_check_signature(vid, 0);
db_multi_exec(
| | | | | | | 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 |
zComment = blob_str(&comment);
}
stashid = db_lget_int("stash-next", 1);
db_lset_int("stash-next", stashid+1);
vid = db_lget_int("checkout", 0);
vfile_check_signature(vid, 0);
db_multi_exec(
"INSERT INTO stash(stashid,vid,hash,comment,ctime)"
"VALUES(%d,%d,(SELECT uuid FROM blob WHERE rid=%d),%Q,julianday('now'))",
stashid, vid, vid, zComment
);
if( g.argc>3 ){
int i;
for(i=3; i<g.argc; i++){
stash_add_file_or_dir(stashid, vid, g.argv[i]);
}
}else{
stash_add_file_or_dir(stashid, vid, g.zLocalRoot);
}
return stashid;
}
/*
** Apply a stash to the current checkout.
*/
static void stash_apply(int stashid, int nConflict){
int vid;
Stmt q;
db_prepare(&q,
"SELECT blob.rid, isRemoved, isExec, isLink, origname, newname, delta"
" FROM stashfile, blob WHERE stashid=%d AND blob.uuid=stashfile.hash",
stashid
);
vid = db_lget_int("checkout",0);
db_multi_exec("CREATE TEMP TABLE sfile(pathname TEXT PRIMARY KEY %s)",
filename_collation());
while( db_step(&q)==SQLITE_ROW ){
int rid = db_column_int(&q, 0);
|
| ︙ | ︙ | |||
294 295 296 297 298 299 300 |
int fIncludeBinary, /* Do diffs against binary files */
u64 diffFlags /* Other diff flags */
){
Stmt q;
Blob empty;
blob_zero(&empty);
db_prepare(&q,
| | | | 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 |
int fIncludeBinary, /* Do diffs against binary files */
u64 diffFlags /* Other diff flags */
){
Stmt q;
Blob empty;
blob_zero(&empty);
db_prepare(&q,
"SELECT blob.rid, isRemoved, isExec, isLink, origname, newname, delta"
" FROM stashfile, blob WHERE stashid=%d AND blob.uuid=stashfile.hash",
stashid
);
while( db_step(&q)==SQLITE_ROW ){
int rid = db_column_int(&q, 0);
int isRemoved = db_column_int(&q, 1);
int isLink = db_column_int(&q, 3);
int isBin1, isBin2;
|
| ︙ | ︙ | |||
420 421 422 423 424 425 426 | ** Show the contents of a stash as a diff against its baseline. ** With gshow and gcat, gdiff-command is used instead of internal ** diff logic. ** ** fossil stash pop ** fossil stash apply ?STASHID? ** | | | 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 | ** Show the contents of a stash as a diff against its baseline. ** With gshow and gcat, gdiff-command is used instead of internal ** diff logic. ** ** fossil stash pop ** fossil stash apply ?STASHID? ** ** Apply STASHID or the most recently created stash to the current ** working checkout. The "pop" command deletes that changeset from ** the stash after applying it but the "apply" command retains the ** changeset. ** ** fossil stash goto ?STASHID? ** ** Update to the baseline checkout for STASHID then apply the |
| ︙ | ︙ | |||
461 462 463 464 465 466 467 |
** fossil stash diff ?STASHID? ?DIFF-OPTIONS?
** fossil stash gdiff ?STASHID? ?DIFF-OPTIONS?
*/
void stash_cmd(void){
const char *zCmd;
int nCmd;
int stashid = 0;
| < < < | < < < < < < < < < < < < | 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 |
** fossil stash diff ?STASHID? ?DIFF-OPTIONS?
** fossil stash gdiff ?STASHID? ?DIFF-OPTIONS?
*/
void stash_cmd(void){
const char *zCmd;
int nCmd;
int stashid = 0;
undo_capture_command_line();
db_must_be_within_tree();
db_open_config(0, 0);
db_begin_transaction();
stash_tables_exist_and_current();
if( g.argc<=2 ){
zCmd = "save";
}else{
zCmd = g.argv[2];
}
nCmd = strlen(zCmd);
if( memcmp(zCmd, "save", nCmd)==0 ){
|
| ︙ | ︙ | |||
507 508 509 510 511 512 513 514 515 516 517 518 519 520 |
db_finalize(&q);
newArgv[0] = g.argv[0];
newArgv[1] = 0;
g.argv = newArgv;
g.argc = nFile+2;
if( nFile==0 ) return;
}
g.argv[1] = "revert";
revert_cmd();
}else
if( memcmp(zCmd, "snapshot", nCmd)==0 ){
stash_create();
}else
if( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){
| > > > | 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 |
db_finalize(&q);
newArgv[0] = g.argv[0];
newArgv[1] = 0;
g.argv = newArgv;
g.argc = nFile+2;
if( nFile==0 ) return;
}
/* Make sure the stash has committed before running the revert, so that
** we have a copy of the changes before deleting them. */
db_commit_transaction();
g.argv[1] = "revert";
revert_cmd();
}else
if( memcmp(zCmd, "snapshot", nCmd)==0 ){
stash_create();
}else
if( memcmp(zCmd, "list", nCmd)==0 || memcmp(zCmd, "ls", nCmd)==0 ){
|
| ︙ | ︙ | |||
532 533 534 535 536 537 538 |
width = -1;
}
if( !verboseFlag ){
verboseFlag = find_option("detail","l",0)!=0; /* deprecated */
}
verify_all_options();
db_prepare(&q,
| < | | | 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 |
width = -1;
}
if( !verboseFlag ){
verboseFlag = find_option("detail","l",0)!=0; /* deprecated */
}
verify_all_options();
db_prepare(&q,
"SELECT stashid, hash, comment, datetime(ctime) FROM stash"
" ORDER BY ctime"
);
if( verboseFlag ){
db_prepare(&q2, "SELECT isAdded, isRemoved, origname, newname"
" FROM stashfile WHERE stashid=$id");
}
while( db_step(&q)==SQLITE_ROW ){
int stashid = db_column_int(&q, 0);
const char *zCom;
n++;
fossil_print("%5d: [%.14s] on %s\n",
stashid,
db_column_text(&q, 1),
db_column_text(&q, 3)
);
zCom = db_column_text(&q, 2);
if( zCom && zCom[0] ){
fossil_print(" ");
comment_print(zCom, 0, 7, width, get_comment_format());
}
if( verboseFlag ){
db_bind_int(&q2, "$id", stashid);
while( db_step(&q2)==SQLITE_ROW ){
int isAdded = db_column_int(&q2, 0);
int isRemoved = db_column_int(&q2, 1);
const char *zOrig = db_column_text(&q2, 2);
|
| ︙ | ︙ | |||
626 627 628 629 630 631 632 |
}else
if( memcmp(zCmd, "goto", nCmd)==0 ){
int nConflict;
int vid;
if( g.argc>4 ) usage("apply STASHID");
stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
undo_begin();
| | > | 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 |
}else
if( memcmp(zCmd, "goto", nCmd)==0 ){
int nConflict;
int vid;
if( g.argc>4 ) usage("apply STASHID");
stashid = stash_get_id(g.argc==4 ? g.argv[3] : 0);
undo_begin();
vid = db_int(0, "SELECT blob.rid FROM stash,blob"
" WHERE stashid=%d AND blob.uuid=stash.hash", stashid);
nConflict = update_to(vid);
stash_apply(stashid, nConflict);
db_multi_exec("UPDATE vfile SET mtime=0 WHERE pathname IN "
"(SELECT origname FROM stashfile WHERE stashid=%d)",
stashid);
undo_finish();
}else
|
| ︙ | ︙ |
Changes to src/style.c.
| ︙ | ︙ | |||
154 155 156 157 158 159 160 |
char *chref(const char *zExtra, const char *zFormat, ...){
char *zUrl;
va_list ap;
va_start(ap, zFormat);
zUrl = vmprintf(zFormat, ap);
va_end(ap);
if( g.perm.Hyperlink && !g.javascriptHyperlink ){
| | | 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 |
char *chref(const char *zExtra, const char *zFormat, ...){
char *zUrl;
va_list ap;
va_start(ap, zFormat);
zUrl = vmprintf(zFormat, ap);
va_end(ap);
if( g.perm.Hyperlink && !g.javascriptHyperlink ){
char *zHUrl = mprintf("<a class=\"%s\" href=\"%h\">", zExtra, zUrl);
fossil_free(zUrl);
return zHUrl;
}
needHrefJs = 1;
return mprintf("<a class='%s' data-href='%z' href='%R/honeypot'>",
zExtra, zUrl);
}
|
| ︙ | ︙ | |||
387 388 389 390 391 392 393 | ** header template lacks a <body> tag, then all of the following is ** prepended. */ static char zDfltHeader[] = @ <html> @ <head> @ <base href="$baseurl/$current_page" /> | | < < < > > > > > > > > > > > > | | | 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 |
** header template lacks a <body> tag, then all of the following is
** prepended.
*/
static char zDfltHeader[] =
@ <html>
@ <head>
@ <base href="$baseurl/$current_page" />
@ <meta http-equiv="Content-Security-Policy" content="$default_csp" />
@ <meta name="viewport" content="width=device-width, initial-scale=1.0">
@ <title>$<project_name>: $<title></title>
@ <link rel="alternate" type="application/rss+xml" title="RSS Feed" \
@ href="$home/timeline.rss" />
@ <link rel="stylesheet" href="$stylesheet_url" type="text/css" \
@ media="screen" />
@ </head>
@ <body>
;
/*
** Initialize all the default TH1 variables
*/
static void style_init_th1_vars(const char *zTitle){
const char *zNonce = style_nonce();
/*
** Do not overwrite the TH1 variable "default_csp" if it exists, as this
** allows it to be properly overridden via the TH1 setup script (i.e. it
** is evaluated before the header is rendered).
*/
char *zDfltCsp = sqlite3_mprintf("default-src 'self' data: ; "
"script-src 'self' 'nonce-%s' ; "
"style-src 'self' 'unsafe-inline'",
zNonce);
Th_MaybeStore("default_csp", zDfltCsp);
sqlite3_free(zDfltCsp);
Th_Store("nonce", zNonce);
Th_Store("project_name", db_get("project-name","Unnamed Fossil Project"));
Th_Store("project_description", db_get("project-description",""));
if( zTitle ) Th_Store("title", zTitle);
Th_Store("baseurl", g.zBaseURL);
Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL);
Th_Store("home", g.zTop);
Th_Store("index_page", db_get("index-page","/home"));
if( local_zCurrentPage==0 ) style_set_current_page("%T", g.zPath);
Th_Store("current_page", local_zCurrentPage);
Th_Store("csrf_token", g.zCsrfToken);
Th_Store("release_version", RELEASE_VERSION);
Th_Store("manifest_version", MANIFEST_VERSION);
|
| ︙ | ︙ | |||
912 913 914 915 916 917 918 |
}
}
/* Process through TH1 in order to give an opportunity to substitute
** variables such as $baseurl.
*/
Th_Store("baseurl", g.zBaseURL);
| | | 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 |
}
}
/* Process through TH1 in order to give an opportunity to substitute
** variables such as $baseurl.
*/
Th_Store("baseurl", g.zBaseURL);
Th_Store("secureurl", fossil_wants_https(1)? g.zHttpsURL: g.zBaseURL);
Th_Store("home", g.zTop);
image_url_var("logo");
image_url_var("background");
Th_Render(blob_str(&css));
/* Tell CGI that the content returned by this page is considered cacheable */
g.isConst = 1;
|
| ︙ | ︙ | |||
1029 1030 1031 1032 1033 1034 1035 |
void webpage_error(const char *zFormat, ...){
int i;
int showAll;
char *zErr = 0;
int isAuth = 0;
char zCap[100];
static const char *const azCgiVars[] = {
| | | | 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 |
void webpage_error(const char *zFormat, ...){
int i;
int showAll;
char *zErr = 0;
int isAuth = 0;
char zCap[100];
static const char *const azCgiVars[] = {
"COMSPEC", "DOCUMENT_ROOT", "GATEWAY_INTERFACE", "SCGI",
"HTTP_ACCEPT", "HTTP_ACCEPT_CHARSET", "HTTP_ACCEPT_ENCODING",
"HTTP_ACCEPT_LANGUAGE", "HTTP_AUTHENICATION",
"HTTP_CONNECTION", "HTTP_HOST",
"HTTP_IF_NONE_MATCH", "HTTP_IF_MODIFIED_SINCE",
"HTTP_USER_AGENT", "HTTP_REFERER", "PATH_INFO", "PATH_TRANSLATED",
"QUERY_STRING", "REMOTE_ADDR", "REMOTE_PORT",
"REMOTE_USER", "REQUEST_METHOD",
"REQUEST_URI", "SCRIPT_FILENAME", "SCRIPT_NAME", "SERVER_PROTOCOL",
"HOME", "FOSSIL_HOME", "USERNAME", "USER", "FOSSIL_USER",
"SQLITE_TMPDIR", "TMPDIR",
"TEMP", "TMP", "FOSSIL_VFS",
"FOSSIL_FORCE_TICKET_MODERATION", "FOSSIL_FORCE_WIKI_MODERATION",
"FOSSIL_TCL_PATH", "TH1_DELETE_INTERP", "TH1_ENABLE_DOCS",
"TH1_ENABLE_HOOKS", "TH1_ENABLE_TCL", "REMOTE_HOST",
};
login_check_credentials();
if( g.perm.Admin || g.perm.Setup || db_get_boolean("test_env_enable",0) ){
isAuth = 1;
}
for(i=0; i<count(azCgiVars); i++) (void)P(azCgiVars[i]);
|
| ︙ | ︙ |
Changes to src/sync.c.
| ︙ | ︙ | |||
153 154 155 156 157 158 159 |
}
if( find_option("verbose","v",0)!=0 ){
*pSyncFlags |= SYNC_VERBOSE;
}
url_proxy_options();
clone_ssh_find_options();
if( !uvOnly ) db_find_and_open_repository(0, 0);
| | | 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
}
if( find_option("verbose","v",0)!=0 ){
*pSyncFlags |= SYNC_VERBOSE;
}
url_proxy_options();
clone_ssh_find_options();
if( !uvOnly ) db_find_and_open_repository(0, 0);
db_open_config(0, 1);
if( g.argc==2 ){
if( db_get_boolean("auto-shun",1) ) configSync = CONFIGSET_SHUN;
}else if( g.argc==3 ){
zUrl = g.argv[2];
}
if( ((*pSyncFlags) & (SYNC_PUSH|SYNC_PULL))==(SYNC_PUSH|SYNC_PULL)
&& db_get_boolean("uv-sync",0)
|
| ︙ | ︙ |
Changes to src/tag.c.
| ︙ | ︙ | |||
357 358 359 360 361 362 363 |
fossil_print("%s", blob_str(&ctrl));
blob_reset(&ctrl);
}else{
nrid = content_put(&ctrl);
manifest_crosslink(nrid, &ctrl, MC_PERMIT_HOOKS);
}
assert( blob_is_reset(&ctrl) );
| > | > | 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 |
fossil_print("%s", blob_str(&ctrl));
blob_reset(&ctrl);
}else{
nrid = content_put(&ctrl);
manifest_crosslink(nrid, &ctrl, MC_PERMIT_HOOKS);
}
assert( blob_is_reset(&ctrl) );
if( g.localOpen ){
manifest_to_disk(rid);
}
}
/*
** COMMAND: tag
**
** Usage: %fossil tag SUBCOMMAND ...
**
|
| ︙ | ︙ | |||
392 393 394 395 396 397 398 399 400 401 402 403 404 405 | ** ** %fossil tag cancel ?--raw? TAGNAME CHECK-IN ** ** Remove the tag TAGNAME from CHECK-IN, and also remove ** the propagation of the tag to any descendants. Use the ** the --dryrun or -n options to see what would have happened. ** ** %fossil tag find ?OPTIONS? TAGNAME ** ** List all objects that use TAGNAME. TYPE can be "ci" for ** check-ins or "e" for events. The limit option limits the number ** of results to the given value. ** ** Options: | > > > > > > > | 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 | ** ** %fossil tag cancel ?--raw? TAGNAME CHECK-IN ** ** Remove the tag TAGNAME from CHECK-IN, and also remove ** the propagation of the tag to any descendants. Use the ** the --dryrun or -n options to see what would have happened. ** ** Options: ** --raw Raw tag name. ** --date-override DATETIME Set date and time deleted. ** --user-override USER Name USER when deleting the tag. ** --dryrun|-n Display the control artifact, but do ** not insert it into the database. ** ** %fossil tag find ?OPTIONS? TAGNAME ** ** List all objects that use TAGNAME. TYPE can be "ci" for ** check-ins or "e" for events. The limit option limits the number ** of results to the given value. ** ** Options: |
| ︙ | ︙ | |||
430 431 432 433 434 435 436 |
** fossil update tag:decaf
**
** will assume that "decaf" is a tag/branch name.
**
*/
void tag_cmd(void){
int n;
| < < < < < > > > > > > > | > > > > | 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 |
** fossil update tag:decaf
**
** will assume that "decaf" is a tag/branch name.
**
*/
void tag_cmd(void){
int n;
db_find_and_open_repository(0, 0);
if( g.argc<3 ){
goto tag_cmd_usage;
}
n = strlen(g.argv[2]);
if( n==0 ){
goto tag_cmd_usage;
}
if( strncmp(g.argv[2],"add",n)==0 ){
char *zValue;
int dryRun = 0;
int fRaw = find_option("raw","",0)!=0;
const char *zPrefix = fRaw ? "" : "sym-";
int fPropagate = find_option("propagate","",0)!=0;
const char *zDateOvrd = find_option("date-override",0,1);
const char *zUserOvrd = find_option("user-override",0,1);
if( find_option("dryrun","n",0)!=0 ) dryRun = TAG_ADD_DRYRUN;
if( g.argc!=5 && g.argc!=6 ){
usage("add ?options? TAGNAME CHECK-IN ?VALUE?");
}
zValue = g.argc==6 ? g.argv[5] : 0;
db_begin_transaction();
tag_add_artifact(zPrefix, g.argv[3], g.argv[4], zValue,
1+fPropagate+dryRun,zDateOvrd,zUserOvrd);
db_end_transaction(0);
}else
if( strncmp(g.argv[2],"branch",n)==0 ){
fossil_fatal("the \"fossil tag branch\" command is discontinued\n"
"Use the \"fossil branch new\" command instead.");
}else
if( strncmp(g.argv[2],"cancel",n)==0 ){
int dryRun = 0;
int fRaw = find_option("raw","",0)!=0;
const char *zPrefix = fRaw ? "" : "sym-";
const char *zDateOvrd = find_option("date-override",0,1);
const char *zUserOvrd = find_option("user-override",0,1);
if( find_option("dryrun","n",0)!=0 ) dryRun = TAG_ADD_DRYRUN;
if( g.argc!=5 ){
usage("cancel ?options? TAGNAME CHECK-IN");
}
db_begin_transaction();
tag_add_artifact(zPrefix, g.argv[3], g.argv[4], 0, dryRun,
zDateOvrd, zUserOvrd);
db_end_transaction(0);
}else
if( strncmp(g.argv[2],"find",n)==0 ){
Stmt q;
int fRaw = find_option("raw","",0)!=0;
const char *zFindLimit = find_option("limit","n",1);
const int nFindLimit = zFindLimit ? atoi(zFindLimit) : -2000;
const char *zType = find_option("type","t",1);
Blob sql = empty_blob;
if( zType==0 || zType[0]==0 ) zType = "*";
if( g.argc!=4 ){
usage("find ?--raw? ?-t|--type TYPE? ?-n|--limit #? TAGNAME");
}
if( fRaw ){
|
| ︙ | ︙ | |||
526 527 528 529 530 531 532 533 534 535 536 537 538 539 |
db_finalize(&q);
}
}
}else
if(( strncmp(g.argv[2],"list",n)==0 )||( strncmp(g.argv[2],"ls",n)==0 )){
Stmt q;
if( g.argc==3 ){
db_prepare(&q,
"SELECT tagname FROM tag"
" WHERE EXISTS(SELECT 1 FROM tagxref"
" WHERE tagid=tag.tagid"
" AND tagtype>0)"
" ORDER BY tagname"
| > | 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 |
db_finalize(&q);
}
}
}else
if(( strncmp(g.argv[2],"list",n)==0 )||( strncmp(g.argv[2],"ls",n)==0 )){
Stmt q;
int fRaw = find_option("raw","",0)!=0;
if( g.argc==3 ){
db_prepare(&q,
"SELECT tagname FROM tag"
" WHERE EXISTS(SELECT 1 FROM tagxref"
" WHERE tagid=tag.tagid"
" AND tagtype>0)"
" ORDER BY tagname"
|
| ︙ | ︙ | |||
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 |
** reparenting operation, simply delete the tag.
**
** --test Make database entries but do not add the tag artifact.
** So the reparent operation will be undone by the next
** "fossil rebuild" command.
** --dryrun | -n Print the tag that would have been created but do not
** actually change the database in any way.
*/
void reparent_cmd(void){
int bTest = find_option("test","",0)!=0;
int rid;
int i;
Blob value;
char *zUuid;
int dryRun = 0;
if( find_option("dryrun","n",0)!=0 ) dryRun = TAG_ADD_DRYRUN;
db_find_and_open_repository(0, 0);
verify_all_options();
if( g.argc<4 ){
usage("[OPTIONS] CHECK-IN PARENT ...");
}
rid = name_to_typed_rid(g.argv[2], "ci");
blob_init(&value, 0, 0);
for(i=3; i<g.argc; i++){
int pid = name_to_typed_rid(g.argv[i], "ci");
if( i>3 ) blob_append(&value, " ", 1);
zUuid = rid_to_uuid(pid);
blob_append(&value, zUuid, strlen(zUuid));
fossil_free(zUuid);
}
if( bTest && !dryRun ){
tag_insert("parent", 1, blob_str(&value), -1, 0.0, rid);
}else{
zUuid = rid_to_uuid(rid);
| > > > > > > | > | 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 |
** reparenting operation, simply delete the tag.
**
** --test Make database entries but do not add the tag artifact.
** So the reparent operation will be undone by the next
** "fossil rebuild" command.
** --dryrun | -n Print the tag that would have been created but do not
** actually change the database in any way.
** --date-override DATETIME Set the change time on the control artifact
** --user-override USER Set the user name on the control artifact
*/
void reparent_cmd(void){
int bTest = find_option("test","",0)!=0;
int rid;
int i;
Blob value;
char *zUuid;
int dryRun = 0;
const char *zDateOvrd; /* The change time on the control artifact */
const char *zUserOvrd; /* The user name on the control artifact */
if( find_option("dryrun","n",0)!=0 ) dryRun = TAG_ADD_DRYRUN;
zDateOvrd = find_option("date-override",0,1);
zUserOvrd = find_option("user-override",0,1);
db_find_and_open_repository(0, 0);
verify_all_options();
if( g.argc<4 ){
usage("[OPTIONS] CHECK-IN PARENT ...");
}
rid = name_to_typed_rid(g.argv[2], "ci");
blob_init(&value, 0, 0);
for(i=3; i<g.argc; i++){
int pid = name_to_typed_rid(g.argv[i], "ci");
if( i>3 ) blob_append(&value, " ", 1);
zUuid = rid_to_uuid(pid);
blob_append(&value, zUuid, strlen(zUuid));
fossil_free(zUuid);
}
if( bTest && !dryRun ){
tag_insert("parent", 1, blob_str(&value), -1, 0.0, rid);
}else{
zUuid = rid_to_uuid(rid);
tag_add_artifact("","parent",zUuid,blob_str(&value),1|dryRun,
zDateOvrd,zUserOvrd);
}
}
/*
** WEBPAGE: taglist
**
|
| ︙ | ︙ | |||
667 668 669 670 671 672 673 |
" AND tagname GLOB 'sym-*'"
" ORDER BY tagname"
);
@ <ul>
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0);
if( g.perm.Hyperlink ){
| | > > > > > > > > > > > > > > > | | | | | > > > > > > > | < | > > > > > > | | 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 |
" AND tagname GLOB 'sym-*'"
" ORDER BY tagname"
);
@ <ul>
while( db_step(&q)==SQLITE_ROW ){
const char *zName = db_column_text(&q, 0);
if( g.perm.Hyperlink ){
@ <li>%z(chref("taglink","%R/timeline?t=%T",zName))
@ %h(zName)</a></li>
}else{
@ <li><span class="tagDsp">%h(zName)</span></li>
}
}
@ </ul>
db_finalize(&q);
style_footer();
}
/*
** WEBPAGE: /tagtimeline
**
** Render a timeline with all check-ins that contain non-propagating
** symbolic tags.
**
** Query parameters:
**
** ng No graph
** nohidden Hide check-ins with "hidden" tag
** onlyhidden Show only check-ins with "hidden" tag
** brbg Background color by branch name
** ubg Background color by user name
*/
void tagtimeline_page(void){
Blob sql = empty_blob;
Stmt q;
int tmFlags; /* Timeline display flags */
int fNoHidden = PB("nohidden")!=0; /* The "nohidden" query parameter */
int fOnlyHidden = PB("onlyhidden")!=0; /* The "onlyhidden" query parameter */
login_check_credentials();
if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
style_header("Tagged Check-ins");
style_submenu_element("List", "taglist");
login_anonymous_available();
timeline_ss_submenu();
cookie_render();
@ <h2>Check-ins with non-propagating tags:</h2>
blob_append(&sql, timeline_query_for_www(), -1);
blob_append_sql(&sql,
"AND blob.rid IN (SELECT rid FROM tagxref"
" WHERE tagtype=1 AND srcid>0"
" AND tagid IN (SELECT tagid FROM tag "
" WHERE tagname GLOB 'sym-*'))");
if( fNoHidden || fOnlyHidden ){
const char* zUnaryOp = fNoHidden ? "NOT" : "";
blob_append_sql(&sql,
" AND %s EXISTS(SELECT 1 FROM tagxref"
" WHERE tagid=%d AND tagtype>0 AND rid=blob.rid)\n",
zUnaryOp/*safe-for-%s*/, TAG_HIDDEN);
}
db_prepare(&q, "%s ORDER BY event.mtime DESC /*sort*/", blob_sql_text(&sql));
blob_reset(&sql);
/* Always specify TIMELINE_DISJOINT, or graph_finish() may fail because of too
** many descenders to (off-screen) parents. */
tmFlags = TIMELINE_DISJOINT | TIMELINE_NOSCROLL;
if( PB("ng")==0 ) tmFlags |= TIMELINE_GRAPH;
if( PB("brbg")!=0 ) tmFlags |= TIMELINE_BRCOLOR;
if( PB("ubg")!=0 ) tmFlags |= TIMELINE_UCOLOR;
www_print_timeline(&q, tmFlags, 0, 0, 0, 0);
db_finalize(&q);
@ <br />
style_footer();
}
|
Changes to src/th_main.c.
| ︙ | ︙ | |||
409 410 411 412 413 414 415 416 417 418 419 420 421 422 |
manifest_destroy(pManifest);
return rid;
}
}
Th_SetResult(interp, "file name not found in manifest", -1);
return 0;
}
/*
** TH1 command: puts STRING
** TH1 command: html STRING
**
** Output STRING escaped for HTML (puts) or unchanged (html).
*/
| > > > > > > > > > > > > > > > > > > > > | 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 |
manifest_destroy(pManifest);
return rid;
}
}
Th_SetResult(interp, "file name not found in manifest", -1);
return 0;
}
/*
** TH1 command: nonce
**
** Returns the value of the cryptographic nonce for the request being
** processed.
*/
static int nonceCmd(
Th_Interp *interp,
void *pConvert,
int argc,
const char **argv,
int *argl
){
if( argc!=1 ){
return Th_WrongNumArgs(interp, "nonce");
}
Th_SetResult(interp, style_nonce(), -1);
return TH_OK;
}
/*
** TH1 command: puts STRING
** TH1 command: html STRING
**
** Output STRING escaped for HTML (puts) or unchanged (html).
*/
|
| ︙ | ︙ | |||
1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 |
return TH_ERROR;
}
}else{
Th_SetResult(interp, "repository unavailable", -1);
return TH_ERROR;
}
}
/*
** TH1 command: unversioned content FILENAME
**
** Attempts to locate the specified unversioned file and return its contents.
** An error is generated if the repository is not open or the unversioned file
** cannot be found.
| > > > > > > > > > > > > > > > > > > > | 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 |
return TH_ERROR;
}
}else{
Th_SetResult(interp, "repository unavailable", -1);
return TH_ERROR;
}
}
/*
** TH1 command: cgiHeaderLine line
**
** Adds the specified line to the CGI header.
*/
static int cgiHeaderLineCmd(
Th_Interp *interp,
void *p,
int argc,
const char **argv,
int *argl
){
if( argc!=2 ){
return Th_WrongNumArgs(interp, "cgiHeaderLine line");
}
cgi_append_header(argv[1]);
return TH_OK;
}
/*
** TH1 command: unversioned content FILENAME
**
** Attempts to locate the specified unversioned file and return its contents.
** An error is generated if the repository is not open or the unversioned file
** cannot be found.
|
| ︙ | ︙ | |||
1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 |
const char *zName;
Th_CommandProc xProc;
void *pContext;
} aCommand[] = {
{"anoncap", hascapCmd, (void*)&anonFlag},
{"anycap", anycapCmd, 0},
{"artifact", artifactCmd, 0},
{"checkout", checkoutCmd, 0},
{"combobox", comboboxCmd, 0},
{"date", dateCmd, 0},
{"decorate", wikiCmd, (void*)&aFlags[2]},
{"dir", dirCmd, 0},
{"enable_output", enableOutputCmd, 0},
{"encode64", encode64Cmd, 0},
{"getParameter", getParameterCmd, 0},
{"glob_match", globMatchCmd, 0},
{"globalState", globalStateCmd, 0},
{"httpize", httpizeCmd, 0},
{"hascap", hascapCmd, (void*)&zeroInt},
{"hasfeature", hasfeatureCmd, 0},
{"html", putsCmd, (void*)&aFlags[0]},
{"htmlize", htmlizeCmd, 0},
{"http", httpCmd, 0},
{"insertCsrf", insertCsrfCmd, 0},
{"linecount", linecntCmd, 0},
{"markdown", markdownCmd, 0},
{"puts", putsCmd, (void*)&aFlags[1]},
{"query", queryCmd, 0},
{"randhex", randhexCmd, 0},
{"redirect", redirectCmd, 0},
{"regexp", regexpCmd, 0},
{"reinitialize", reinitializeCmd, 0},
{"render", renderCmd, 0},
| > > | 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 |
const char *zName;
Th_CommandProc xProc;
void *pContext;
} aCommand[] = {
{"anoncap", hascapCmd, (void*)&anonFlag},
{"anycap", anycapCmd, 0},
{"artifact", artifactCmd, 0},
{"cgiHeaderLine", cgiHeaderLineCmd, 0},
{"checkout", checkoutCmd, 0},
{"combobox", comboboxCmd, 0},
{"date", dateCmd, 0},
{"decorate", wikiCmd, (void*)&aFlags[2]},
{"dir", dirCmd, 0},
{"enable_output", enableOutputCmd, 0},
{"encode64", encode64Cmd, 0},
{"getParameter", getParameterCmd, 0},
{"glob_match", globMatchCmd, 0},
{"globalState", globalStateCmd, 0},
{"httpize", httpizeCmd, 0},
{"hascap", hascapCmd, (void*)&zeroInt},
{"hasfeature", hasfeatureCmd, 0},
{"html", putsCmd, (void*)&aFlags[0]},
{"htmlize", htmlizeCmd, 0},
{"http", httpCmd, 0},
{"insertCsrf", insertCsrfCmd, 0},
{"linecount", linecntCmd, 0},
{"markdown", markdownCmd, 0},
{"nonce", nonceCmd, 0},
{"puts", putsCmd, (void*)&aFlags[1]},
{"query", queryCmd, 0},
{"randhex", randhexCmd, 0},
{"redirect", redirectCmd, 0},
{"regexp", regexpCmd, 0},
{"reinitialize", reinitializeCmd, 0},
{"render", renderCmd, 0},
|
| ︙ | ︙ | |||
2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 |
Th_Trace("th1-setup {%h} => %h<br />\n", g.th1Setup,
Th_ReturnCodeName(rc, 0));
}
}
g.th1Flags &= ~TH_INIT_MASK;
g.th1Flags |= (flags & TH_INIT_MASK);
}
/*
** Store a string value in a variable in the interpreter.
*/
void Th_Store(const char *zName, const char *zValue){
Th_FossilInit(TH_INIT_DEFAULT);
if( zValue ){
| > > > > > > > > > > > > > > | 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 |
Th_Trace("th1-setup {%h} => %h<br />\n", g.th1Setup,
Th_ReturnCodeName(rc, 0));
}
}
g.th1Flags &= ~TH_INIT_MASK;
g.th1Flags |= (flags & TH_INIT_MASK);
}
/*
** Store a string value in a variable in the interpreter if the variable
** does not already exist.
*/
void Th_MaybeStore(const char *zName, const char *zValue){
Th_FossilInit(TH_INIT_DEFAULT);
if( zValue && !Th_ExistsVar(g.interp, zName, -1) ){
if( g.thTrace ){
Th_Trace("maybe_set %h {%h}<br />\n", zName, zValue);
}
Th_SetVar(g.interp, zName, -1, zValue, strlen(zValue));
}
}
/*
** Store a string value in a variable in the interpreter.
*/
void Th_Store(const char *zName, const char *zValue){
Th_FossilInit(TH_INIT_DEFAULT);
if( zValue ){
|
| ︙ | ︙ |
Changes to src/th_tcl.c.
| ︙ | ︙ | |||
96 97 98 99 100 101 102 | # define _WIN32_WINNT 0x0502 /* SetDllDirectory, Windows XP SP2 */ # endif # include <windows.h> # ifndef TCL_DIRECTORY_SEP # define TCL_DIRECTORY_SEP '\\' # endif # ifndef TCL_LIBRARY_NAME | | | | | | | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | # define _WIN32_WINNT 0x0502 /* SetDllDirectory, Windows XP SP2 */ # endif # include <windows.h> # ifndef TCL_DIRECTORY_SEP # define TCL_DIRECTORY_SEP '\\' # endif # ifndef TCL_LIBRARY_NAME # define TCL_LIBRARY_NAME "tcl87.dll\0" # endif # ifndef TCL_MINOR_OFFSET # define TCL_MINOR_OFFSET (4) # endif # ifndef dlopen # define dlopen(a,b) (void *)LoadLibrary((a)) # endif # ifndef dlsym # define dlsym(a,b) GetProcAddress((HANDLE)(a),(b)) # endif # ifndef dlclose # define dlclose(a) FreeLibrary((HANDLE)(a)) # endif # else # include <dlfcn.h> # ifndef TCL_DIRECTORY_SEP # define TCL_DIRECTORY_SEP '/' # endif # if defined(__CYGWIN__) # ifndef TCL_LIBRARY_NAME # define TCL_LIBRARY_NAME "libtcl8.7.dll\0" # endif # ifndef TCL_MINOR_OFFSET # define TCL_MINOR_OFFSET (8) # endif # elif defined(__APPLE__) # ifndef TCL_LIBRARY_NAME # define TCL_LIBRARY_NAME "libtcl8.7.dylib\0" # endif # ifndef TCL_MINOR_OFFSET # define TCL_MINOR_OFFSET (8) # endif # elif defined(__FreeBSD__) # ifndef TCL_LIBRARY_NAME # define TCL_LIBRARY_NAME "libtcl87.so\0" # endif # ifndef TCL_MINOR_OFFSET # define TCL_MINOR_OFFSET (7) # endif # else # ifndef TCL_LIBRARY_NAME # define TCL_LIBRARY_NAME "libtcl8.7.so\0" # endif # ifndef TCL_MINOR_OFFSET # define TCL_MINOR_OFFSET (8) # endif # endif /* defined(__CYGWIN__) */ # endif /* defined(_WIN32) */ # ifndef TCL_FINDEXECUTABLE_NAME |
| ︙ | ︙ | |||
970 971 972 973 974 975 976 |
*pxDeleteInterp = xDeleteInterp;
*pxFinalize = xFinalize;
return TH_OK;
}
} while( --aFileName[TCL_MINOR_OFFSET]>'3' ); /* Tcl 8.4+ */
aFileName[TCL_MINOR_OFFSET] = 'x';
Th_ErrorMessage(interp,
| | | 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 |
*pxDeleteInterp = xDeleteInterp;
*pxFinalize = xFinalize;
return TH_OK;
}
} while( --aFileName[TCL_MINOR_OFFSET]>'3' ); /* Tcl 8.4+ */
aFileName[TCL_MINOR_OFFSET] = 'x';
Th_ErrorMessage(interp,
"could not load any supported Tcl 8.x shared library \"",
aFileName, -1);
return TH_ERROR;
#else
*phLibrary = 0;
*pxFindExecutable = Tcl_FindExecutable;
*pxCreateInterp = Tcl_CreateInterp;
*pxDeleteInterp = Tcl_DeleteInterp;
|
| ︙ | ︙ | |||
1008 1009 1010 1011 1012 1013 1014 |
Tcl_IncrRefCount(objPtr);
resultObjPtr = Tcl_SetVar2Ex(pInterp, "argv0", NULL, objPtr,
TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG);
Tcl_DecrRefCount(objPtr); objPtr = 0;
if( !resultObjPtr ){
return TCL_ERROR;
}
| | | 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 |
Tcl_IncrRefCount(objPtr);
resultObjPtr = Tcl_SetVar2Ex(pInterp, "argv0", NULL, objPtr,
TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG);
Tcl_DecrRefCount(objPtr); objPtr = 0;
if( !resultObjPtr ){
return TCL_ERROR;
}
objPtr = Tcl_NewWideIntObj(argc - 1);
Tcl_IncrRefCount(objPtr);
resultObjPtr = Tcl_SetVar2Ex(pInterp, "argc", NULL, objPtr,
TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG);
Tcl_DecrRefCount(objPtr); objPtr = 0;
if( !resultObjPtr ){
return TCL_ERROR;
}
|
| ︙ | ︙ | |||
1190 1191 1192 1193 1194 1195 1196 | tclContext->useTip285 = canUseTip285(); /* Add the TH1 integration commands to Tcl. */ Tcl_CallWhenDeleted(tclInterp, Th1DeleteProc, interp); Tcl_CreateObjCommand(tclInterp, "th1Eval", Th1EvalObjCmd, interp, NULL); Tcl_CreateObjCommand(tclInterp, "th1Expr", Th1ExprObjCmd, interp, NULL); /* If necessary, evaluate the custom Tcl setup script. */ setup = tclContext->setup; | | | 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 |
tclContext->useTip285 = canUseTip285();
/* Add the TH1 integration commands to Tcl. */
Tcl_CallWhenDeleted(tclInterp, Th1DeleteProc, interp);
Tcl_CreateObjCommand(tclInterp, "th1Eval", Th1EvalObjCmd, interp, NULL);
Tcl_CreateObjCommand(tclInterp, "th1Expr", Th1ExprObjCmd, interp, NULL);
/* If necessary, evaluate the custom Tcl setup script. */
setup = tclContext->setup;
if( setup && Tcl_EvalEx(tclInterp, setup, -1, 0)!=TCL_OK ){
Th_ErrorMessage(interp,
"Tcl setup script error:", Tcl_GetStringResult(tclInterp), -1);
Tcl_DeleteInterp(tclInterp);
tclContext->interp = tclInterp = 0;
return TH_ERROR;
}
return TH_OK;
|
| ︙ | ︙ |
Changes to src/timeline.c.
| ︙ | ︙ | |||
89 90 91 92 93 94 95 | } } /* ** Allowed flags for the tmFlags argument to www_print_timeline */ #if INTERFACE | | | > | 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
}
}
/*
** Allowed flags for the tmFlags argument to www_print_timeline
*/
#if INTERFACE
#define TIMELINE_ARTID 0x000001 /* Show artifact IDs on non-check-in lines*/
#define TIMELINE_LEAFONLY 0x000002 /* Show "Leaf" but not "Merge", "Fork" etc*/
#define TIMELINE_BRIEF 0x000004 /* Combine adjacent elements of same obj */
#define TIMELINE_GRAPH 0x000008 /* Compute a graph */
#define TIMELINE_DISJOINT 0x000010 /* Elements are not contiguous */
#define TIMELINE_FCHANGES 0x000020 /* Detail file changes */
#define TIMELINE_BRCOLOR 0x000040 /* Background color by branch name */
#define TIMELINE_UCOLOR 0x000080 /* Background color by user */
#define TIMELINE_FRENAMES 0x000100 /* Detail only file name changes */
#define TIMELINE_UNHIDE 0x000200 /* Unhide check-ins with "hidden" tag */
#define TIMELINE_SHOWRID 0x000400 /* Show RID values in addition to UUIDs */
#define TIMELINE_BISECT 0x000800 /* Show supplimental bisect information */
#define TIMELINE_COMPACT 0x001000 /* Use the "compact" view style */
#define TIMELINE_VERBOSE 0x002000 /* Use the "detailed" view style */
#define TIMELINE_MODERN 0x004000 /* Use the "modern" view style */
#define TIMELINE_COLUMNAR 0x008000 /* Use the "columns" view style */
#define TIMELINE_CLASSIC 0x010000 /* Use the "classic" view style */
#define TIMELINE_VIEWS 0x01f000 /* Mask for all of the view styles */
#define TIMELINE_NOSCROLL 0x100000 /* Don't scroll to the selection */
#define TIMELINE_FILEDIFF 0x200000 /* Show File differences, not ckin diffs */
#define TIMELINE_CHPICK 0x400000 /* Show cherrypick merges */
#endif
/*
** Hash a string and use the hash to determine a background color.
*/
char *hash_color(const char *z){
int i; /* Loop counter */
|
| ︙ | ︙ | |||
288 289 290 291 292 293 294 295 296 297 298 299 300 301 |
if( tmFlags & TIMELINE_GRAPH ){
pGraph = graph_init();
}
db_static_prepare(&qbranch,
"SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid",
TAG_BRANCH
);
@ <table id="timelineTable%d(iTableId)" class="timelineTable">
blob_zero(&comment);
while( db_step(pQuery)==SQLITE_ROW ){
int rid = db_column_int(pQuery, 0);
const char *zUuid = db_column_text(pQuery, 1);
int isLeaf = db_column_int(pQuery, 5);
| > > > > > | 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 |
if( tmFlags & TIMELINE_GRAPH ){
pGraph = graph_init();
}
db_static_prepare(&qbranch,
"SELECT value FROM tagxref WHERE tagid=%d AND tagtype>0 AND rid=:rid",
TAG_BRANCH
);
if( (tmFlags & TIMELINE_CHPICK)!=0
&& !db_table_exists("repository","cherrypick")
){
tmFlags &= ~TIMELINE_CHPICK;
}
@ <table id="timelineTable%d(iTableId)" class="timelineTable">
blob_zero(&comment);
while( db_step(pQuery)==SQLITE_ROW ){
int rid = db_column_int(pQuery, 0);
const char *zUuid = db_column_text(pQuery, 1);
int isLeaf = db_column_int(pQuery, 5);
|
| ︙ | ︙ | |||
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 |
}else{
zBgClr = hash_color(zBr);
}
}
}
if( zType[0]=='c' && pGraph ){
int nParent = 0;
int aParent[GR_MAX_RAIL];
static Stmt qparent;
db_static_prepare(&qparent,
"SELECT pid FROM plink"
" WHERE cid=:rid AND pid NOT IN phantom"
" ORDER BY isprim DESC /*sort*/"
);
db_bind_int(&qparent, ":rid", rid);
while( db_step(&qparent)==SQLITE_ROW && nParent<count(aParent) ){
aParent[nParent++] = db_column_int(&qparent, 0);
}
db_reset(&qparent);
| > > > > > > > > > > > > > > | | | | 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 |
}else{
zBgClr = hash_color(zBr);
}
}
}
if( zType[0]=='c' && pGraph ){
int nParent = 0;
int nCherrypick = 0;
int aParent[GR_MAX_RAIL];
static Stmt qparent;
db_static_prepare(&qparent,
"SELECT pid FROM plink"
" WHERE cid=:rid AND pid NOT IN phantom"
" ORDER BY isprim DESC /*sort*/"
);
db_bind_int(&qparent, ":rid", rid);
while( db_step(&qparent)==SQLITE_ROW && nParent<count(aParent) ){
aParent[nParent++] = db_column_int(&qparent, 0);
}
db_reset(&qparent);
if( (tmFlags & TIMELINE_CHPICK)!=0 && nParent>0 ){
static Stmt qcherrypick;
db_static_prepare(&qcherrypick,
"SELECT parentid FROM cherrypick"
" WHERE childid=:rid AND parentid NOT IN phantom"
);
db_bind_int(&qcherrypick, ":rid", rid);
while( db_step(&qcherrypick)==SQLITE_ROW && nParent<count(aParent) ){
aParent[nParent++] = db_column_int(&qcherrypick, 0);
nCherrypick++;
}
db_reset(&qcherrypick);
}
gidx = graph_add_row(pGraph, rid, nParent, nCherrypick, aParent,
zBr, zBgClr, zUuid, isLeaf);
db_reset(&qbranch);
@ <div id="m%d(gidx)" class="tl-nodemark"></div>
}else if( zType[0]=='e' && pGraph && zBgClr && zBgClr[0] ){
/* For technotes, make a graph node with nParent==(-1). This will
** not actually draw anything on the graph, but it will set the
** background color of the timeline entry */
gidx = graph_add_row(pGraph, rid, -1, 0, 0, zBr, zBgClr, zUuid, 0);
@ <div id="m%d(gidx)" class="tl-nodemark"></div>
}
@</td>
if( !isSelectedOrCurrent ){
@ <td class="timeline%s(zStyle)Cell" id='mc%d(gidx)'>
}else{
@ <td class="timeline%s(zStyle)Cell">
|
| ︙ | ︙ | |||
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 |
@ (%d(rid))
}
}
}
if( zType[0]!='c' ){
/* Comments for anything other than a check-in are generated by
** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */
wiki_convert(&comment, 0, WIKI_INLINE);
}else{
if( bCommentGitStyle ){
/* Truncate comment at first blank line */
int ii, jj;
int n = blob_size(&comment);
char *z = blob_str(&comment);
for(ii=0; ii<n; ii++){
| > > | 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 |
@ (%d(rid))
}
}
}
if( zType[0]!='c' ){
/* Comments for anything other than a check-in are generated by
** "fossil rebuild" and expect to be rendered as text/x-fossil-wiki */
if( zType[0]=='w' ) wiki_hyperlink_override(zUuid);
wiki_convert(&comment, 0, WIKI_INLINE);
wiki_hyperlink_override(0);
}else{
if( bCommentGitStyle ){
/* Truncate comment at first blank line */
int ii, jj;
int n = blob_size(&comment);
char *z = blob_str(&comment);
for(ii=0; ii<n; ii++){
|
| ︙ | ︙ | |||
839 840 841 842 843 844 845 |
**
** id: The id of the <div> element for the row. This is an integer.
** to get an actual id, prepend "m" to the integer. The top node
** is iTopRow and numbers increase moving down the timeline.
** bg: The background color for this row
** r: The "rail" that the node for this row sits on. The left-most
** rail is 0 and the number increases to the right.
| | | | | | > | > > > | > > | > > > > | > > | > | > > > > > | | < | > > > > > | | > > > > > > | | > > > > > > > > | | | > > > > > | | | 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 |
**
** id: The id of the <div> element for the row. This is an integer.
** to get an actual id, prepend "m" to the integer. The top node
** is iTopRow and numbers increase moving down the timeline.
** bg: The background color for this row
** r: The "rail" that the node for this row sits on. The left-most
** rail is 0 and the number increases to the right.
** d: If exists and true then there is a "descender" - an arrow
** coming from the bottom of the page straight up to this node.
** mo: "merge-out". If it exists, this is the rail position
** for the upward portion of a merge arrow. The merge arrow goes as
** a solid normal merge line up to the row identified by "mu" and
** then as a dashed cherrypick merge line up further to "cu".
** If this value is omitted if there are no merge children.
** mu: The id of the row which is the top of the merge-out arrow.
** Only exists if "mo" exists.
** cu: Extend the mu merge arrow up to this row as a cherrypick
** merge line, if this value exists.
** u: Draw a thick child-line out of the top of this node and up to
** the node with an id equal to this value. 0 if it is straight to
** the top of the page, -1 if there is no thick-line riser.
** f: 0x01: a leaf node.
** au: An array of integers that define thick-line risers for branches.
** The integers are in pairs. For each pair, the first integer is
** is the rail on which the riser should run and the second integer
** is the id of the node upto which the riser should run. If there
** are no risers, this array does not exist.
** mi: "merge-in". An array of integer rail positions from which
** merge arrows should be drawn into this node. If the value is
** negative, then the rail position is the absolute value of mi[]
** and a thin merge-arrow descender is drawn to the bottom of
** the screen. This array is omitted if there are no inbound
** merges.
** ci: "cherrypick-in". Like "mi" except for cherrypick merges.
** omitted if there are no cherrypick merges.
** h: The artifact hash of the object being graphed
*/
for(pRow=pGraph->pFirst; pRow; pRow=pRow->pNext){
int k = 0;
cgi_printf("{\"id\":%d,", pRow->idx);
cgi_printf("\"bg\":\"%s\",", pRow->zBgClr);
cgi_printf("\"r\":%d,", pRow->iRail);
if( pRow->bDescender ){
cgi_printf("\"d\":%d,", pRow->bDescender);
}
if( pRow->mergeOut>=0 ){
cgi_printf("\"mo\":%d,", pRow->mergeOut);
if( pRow->mergeUpto==0 ) pRow->mergeUpto = pRow->idx;
cgi_printf("\"mu\":%d,", pRow->mergeUpto);
if( pRow->cherrypickUpto>0 && pRow->cherrypickUpto<pRow->mergeUpto ){
cgi_printf("\"cu\":%d,", pRow->cherrypickUpto);
}
}
cgi_printf("\"u\":%d,", pRow->aiRiser[pRow->iRail]);
k = 0;
if( pRow->isLeaf ) k |= 1;
cgi_printf("\"f\":%d,",k);
for(i=k=0; i<GR_MAX_RAIL; i++){
if( i==pRow->iRail ) continue;
if( pRow->aiRiser[i]>0 ){
if( k==0 ){
cgi_printf("\"au\":");
cSep = '[';
}
k++;
cgi_printf("%c%d,%d", cSep, i, pRow->aiRiser[i]);
cSep = ',';
}
}
if( k ){
cgi_printf("],");
}
if( colorGraph && pRow->zBgClr[0]=='#' ){
cgi_printf("\"fg\":\"%s\",", bg_to_fg(pRow->zBgClr));
}
/* mi */
for(i=k=0; i<GR_MAX_RAIL; i++){
if( pRow->mergeIn[i]==1 ){
int mi = i;
if( (pRow->mergeDown >> i) & 1 ) mi = -mi;
if( k==0 ){
cgi_printf("\"mi\":");
cSep = '[';
}
k++;
cgi_printf("%c%d", cSep, mi);
cSep = ',';
}
}
if( k ) cgi_printf("],");
/* ci */
for(i=k=0; i<GR_MAX_RAIL; i++){
if( pRow->mergeIn[i]==2 ){
int mi = i;
if( (pRow->cherrypickDown >> i) & 1 ) mi = -mi;
if( k==0 ){
cgi_printf("\"ci\":");
cSep = '[';
}
k++;
cgi_printf("%c%d", cSep, mi);
cSep = ',';
}
}
if( k ) cgi_printf("],");
cgi_printf("\"h\":\"%!S\"}%s",
pRow->zUuid, pRow->pNext ? ",\n" : "]\n");
}
@ }</script>
style_graph_generator();
graph_free(pGraph);
}
}
|
| ︙ | ︙ | |||
959 960 961 962 963 964 965 | return zBase; } /* ** Convert a symbolic name used as an argument to the a=, b=, or c= ** query parameters of timeline into a julianday mtime value. */ | | > > > > > > > > | 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 |
return zBase;
}
/*
** Convert a symbolic name used as an argument to the a=, b=, or c=
** query parameters of timeline into a julianday mtime value.
*/
double symbolic_name_to_mtime(const char *z, const char **pzDisplay){
double mtime;
int rid;
const char *zDate;
if( z==0 ) return -1.0;
if( fossil_isdate(z) ){
mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", z);
if( mtime>0.0 ) return mtime;
}
zDate = fossil_expand_datetime(z, 1);
if( zDate!=0
&& (mtime = db_double(0.0, "SELECT julianday(%Q,fromLocal())", zDate))>0.0
){
if( pzDisplay ) *pzDisplay = fossil_strdup(zDate);
return mtime;
}
rid = symbolic_name_to_rid(z, "*");
if( rid ){
mtime = db_double(0.0, "SELECT mtime FROM event WHERE objid=%d", rid);
}else{
mtime = db_double(-1.0,
"SELECT max(event.mtime) FROM event, tag, tagxref"
|
| ︙ | ︙ | |||
1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 |
blob_append(&expr, zEnd, -1);
return blob_str(&expr);
}
/* If execution reaches this point, the pattern was empty. Return NULL. */
return 0;
}
/*
** WEBPAGE: timeline
**
** Query parameters:
**
** a=TIMEORTAG After this event
** b=TIMEORTAG Before this event
** c=TIMEORTAG "Circa" this event
** m=TIMEORTAG Mark this event
** n=COUNT Maximum number of events. "all" for no limit
** p=CHECKIN Parents and ancestors of CHECKIN
** d=CHECKIN Children and descendants of CHECKIN
** dp=CHECKIN The same as d=CHECKIN&p=CHECKIN
** t=TAG Show only check-ins with the given TAG
** r=TAG Show check-ins related to TAG, equivalent to t=TAG&rel
** rel Show related check-ins as well as those matching t=TAG
** mionly Limit rel to show ancestors but not descendants
** ms=MATCHSTYLE Set tag match style to EXACT, GLOB, LIKE, REGEXP
** u=USER Only show items associated with USER
** y=TYPE 'ci', 'w', 't', 'e', 'f', or 'all'.
** ss=VIEWSTYLE c: "Compact" v: "Verbose" m: "Modern" j: "Columnar"
** advm Use the "Advanced" or "Busy" menu design.
** ng No Graph.
** nd Do not highlight the focus check-in
** v Show details of files changed
** f=CHECKIN Show family (immediate parents and children) of CHECKIN
** from=CHECKIN Path from...
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > | 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 |
blob_append(&expr, zEnd, -1);
return blob_str(&expr);
}
/* If execution reaches this point, the pattern was empty. Return NULL. */
return 0;
}
/*
** Similar to fossil_expand_datetime()
**
** Add missing "-" characters into a date/time. Examples:
**
** 20190419 => 2019-04-19
** 201904 => 2019-04
*/
const char *timeline_expand_datetime(const char *zIn){
static char zEDate[20];
static const char aPunct[] = { 0, 0, '-', '-', ' ', ':', ':' };
int n = (int)strlen(zIn);
int i, j;
/* Only three forms allowed:
** (1) YYYYMMDD
** (2) YYYYMM
** (3) YYYYWW
*/
if( n!=8 && n!=6 ) return zIn;
/* Every character must be a digit */
for(i=0; fossil_isdigit(zIn[i]); i++){}
if( i!=n ) return zIn;
/* Expand the date */
for(i=j=0; zIn[i]; i++){
if( i>=4 && (i%2)==0 ){
zEDate[j++] = aPunct[i/2];
}
zEDate[j++] = zIn[i];
}
zEDate[j] = 0;
/* It looks like this may be a date. Return it with punctuation added. */
return zEDate;
}
/*
** WEBPAGE: timeline
**
** Query parameters:
**
** a=TIMEORTAG After this event
** b=TIMEORTAG Before this event
** c=TIMEORTAG "Circa" this event
** m=TIMEORTAG Mark this event
** n=COUNT Maximum number of events. "all" for no limit
** p=CHECKIN Parents and ancestors of CHECKIN
** d=CHECKIN Children and descendants of CHECKIN
** dp=CHECKIN The same as d=CHECKIN&p=CHECKIN
** t=TAG Show only check-ins with the given TAG
** r=TAG Show check-ins related to TAG, equivalent to t=TAG&rel
** rel Show related check-ins as well as those matching t=TAG
** mionly Limit rel to show ancestors but not descendants
** nowiki Do not show wiki associated with branch or tag
** ms=MATCHSTYLE Set tag match style to EXACT, GLOB, LIKE, REGEXP
** u=USER Only show items associated with USER
** y=TYPE 'ci', 'w', 't', 'e', 'f', or 'all'.
** ss=VIEWSTYLE c: "Compact" v: "Verbose" m: "Modern" j: "Columnar"
** advm Use the "Advanced" or "Busy" menu design.
** ng No Graph.
** ncp Omit cherrypick merges
** nd Do not highlight the focus check-in
** v Show details of files changed
** f=CHECKIN Show family (immediate parents and children) of CHECKIN
** from=CHECKIN Path from...
** to=CHECKIN ... to this
** shorest ... show only the shortest path
** rel ... also show related checkins
** uf=FILE_HASH Show only check-ins that contain the given file version
** chng=GLOBLIST Show only check-ins that involve changes to a file whose
** name matches one of the comma-separate GLOBLIST
** brbg Background color from branch name
** ubg Background color from user
** namechng Show only check-ins that have filename changes
** forks Show only forks and their children
** cherrypicks Show all cherrypicks
** ym=YYYY-MM Show only events for the given year/month
** yw=YYYY-WW Show only events for the given week of the given year
** yw=YYYY-MM-DD Show events for the week that includes the given day
** ymd=YYYY-MM-DD Show only events on the given day
** days=N Show events over the previous N days
** datefmt=N Override the date format
** bisect Show the check-ins that are in the current bisect
|
| ︙ | ︙ | |||
1429 1430 1431 1432 1433 1434 1435 |
const char *zDay = P("ymd"); /* Check-ins for the day YYYY-MM-DD */
const char *zNDays = P("days"); /* Show events over the previous N days */
int nDays = 0; /* Numeric value for zNDays */
const char *zChng = P("chng"); /* List of GLOBs for files that changed */
int useDividers = P("nd")==0; /* Show dividers if "nd" is missing */
int renameOnly = P("namechng")!=0; /* Show only check-ins that rename files */
int forkOnly = PB("forks"); /* Show only forks and their children */
| | > > > | 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 |
const char *zDay = P("ymd"); /* Check-ins for the day YYYY-MM-DD */
const char *zNDays = P("days"); /* Show events over the previous N days */
int nDays = 0; /* Numeric value for zNDays */
const char *zChng = P("chng"); /* List of GLOBs for files that changed */
int useDividers = P("nd")==0; /* Show dividers if "nd" is missing */
int renameOnly = P("namechng")!=0; /* Show only check-ins that rename files */
int forkOnly = PB("forks"); /* Show only forks and their children */
int bisectLocal = PB("bisect"); /* Show the check-ins of the bisect */
const char *zBisect = P("bid"); /* Bisect description */
int cpOnly = PB("cherrypicks"); /* Show all cherrypick checkins */
int tmFlags = 0; /* Timeline flags */
const char *zThisTag = 0; /* Suppress links to this tag */
const char *zThisUser = 0; /* Suppress links to this user */
HQuery url; /* URL for various branch links */
int from_rid = name_to_typed_rid(P("from"),"ci"); /* from= for paths */
int to_rid = name_to_typed_rid(P("to"),"ci"); /* to= for path timelines */
int noMerge = P("shortest")==0; /* Follow merge links if shorter */
int me_rid = name_to_typed_rid(P("me"),"ci"); /* me= for common ancestory */
int you_rid = name_to_typed_rid(P("you"),"ci");/* you= for common ancst */
int pd_rid;
double rBefore, rAfter, rCirca; /* Boundary times */
const char *z;
char *zOlderButton = 0; /* URL for Older button at the bottom */
char *zNewerButton = 0; /* URL for Newer button at the top */
int selectedRid = -9999999; /* Show a highlight on this RID */
int disableY = 0; /* Disable type selector on submenu */
int advancedMenu = 0; /* Use the advanced menu design */
char *zPlural; /* Ending for plural forms */
int showCherrypicks = 1; /* True to show cherrypick merges */
/* Set number of rows to display */
cookie_read_parameter("n","n");
z = P("n");
if( z==0 ) z = db_get("timeline-default-length",0);
if( z ){
if( fossil_strcmp(z,"all")==0 ){
|
| ︙ | ︙ | |||
1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 |
nEntry = 50;
}
cgi_replace_query_parameter("n",z);
cookie_write_parameter("n","n",0);
tmFlags |= timeline_ss_submenu();
cookie_link_parameter("advm","advm","0");
advancedMenu = atoi(PD("advm","0"));
/* To view the timeline, must have permission to read project data.
*/
pd_rid = name_to_typed_rid(P("dp"),"ci");
if( pd_rid ){
p_rid = d_rid = pd_rid;
}
login_check_credentials();
if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum)
| > > > > > > | | 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 |
nEntry = 50;
}
cgi_replace_query_parameter("n",z);
cookie_write_parameter("n","n",0);
tmFlags |= timeline_ss_submenu();
cookie_link_parameter("advm","advm","0");
advancedMenu = atoi(PD("advm","0"));
/* Omit all cherry-pick merge lines if the "ncp" query parameter is
** present or if this repository lacks a "cherrypick" table. */
if( PB("ncp") || !db_table_exists("repository","cherrypick") ){
showCherrypicks = 0;
}
/* To view the timeline, must have permission to read project data.
*/
pd_rid = name_to_typed_rid(P("dp"),"ci");
if( pd_rid ){
p_rid = d_rid = pd_rid;
}
login_check_credentials();
if( (!g.perm.Read && !g.perm.RdTkt && !g.perm.RdWiki && !g.perm.RdForum)
|| (bisectLocal && !g.perm.Setup)
){
login_needed(g.anon.Read && g.anon.RdTkt && g.anon.RdWiki);
return;
}
cookie_read_parameter("y","y");
zType = P("y");
if( zType==0 ){
|
| ︙ | ︙ | |||
1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 |
/* Convert r=TAG to t=TAG&rel. */
if( zBrName && !related ){
cgi_delete_query_parameter("r");
cgi_set_query_parameter("t", zBrName);
cgi_set_query_parameter("rel", "1");
zTagName = zBrName;
related = 1;
}
/* Ignore empty tag query strings. */
if( zTagName && !*zTagName ){
zTagName = 0;
}
/* Finish preliminary processing of tag match queries. */
if( zTagName ){
/* Interpet the tag style string. */
if( fossil_stricmp(zMatchStyle, "glob")==0 ){
matchStyle = MS_GLOB;
}else if( fossil_stricmp(zMatchStyle, "like")==0 ){
matchStyle = MS_LIKE;
}else if( fossil_stricmp(zMatchStyle, "regexp")==0 ){
matchStyle = MS_REGEXP;
| > > | 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 |
/* Convert r=TAG to t=TAG&rel. */
if( zBrName && !related ){
cgi_delete_query_parameter("r");
cgi_set_query_parameter("t", zBrName);
cgi_set_query_parameter("rel", "1");
zTagName = zBrName;
related = 1;
zType = "ci";
}
/* Ignore empty tag query strings. */
if( zTagName && !*zTagName ){
zTagName = 0;
}
/* Finish preliminary processing of tag match queries. */
if( zTagName ){
zType = "ci";
/* Interpet the tag style string. */
if( fossil_stricmp(zMatchStyle, "glob")==0 ){
matchStyle = MS_GLOB;
}else if( fossil_stricmp(zMatchStyle, "like")==0 ){
matchStyle = MS_LIKE;
}else if( fossil_stricmp(zMatchStyle, "regexp")==0 ){
matchStyle = MS_REGEXP;
|
| ︙ | ︙ | |||
1548 1549 1550 1551 1552 1553 1554 |
if( (zTagSql && db_int(0,"SELECT count(*) "
"FROM tagxref NATURAL JOIN tag WHERE %s",zTagSql/*safe-for-%s*/)<=nEntry)
){
nEntry = -1;
zCirca = 0;
}
if( zType[0]=='a' ){
| | | | | > > > | 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 |
if( (zTagSql && db_int(0,"SELECT count(*) "
"FROM tagxref NATURAL JOIN tag WHERE %s",zTagSql/*safe-for-%s*/)<=nEntry)
){
nEntry = -1;
zCirca = 0;
}
if( zType[0]=='a' ){
tmFlags |= TIMELINE_BRIEF | TIMELINE_GRAPH | TIMELINE_CHPICK;
}else{
tmFlags |= TIMELINE_GRAPH | TIMELINE_CHPICK;
}
if( PB("ncp") ){
tmFlags &= ~TIMELINE_CHPICK;
}
if( PB("ng") || zSearch!=0 ){
tmFlags &= ~(TIMELINE_GRAPH|TIMELINE_CHPICK);
}
if( PB("brbg") ){
tmFlags |= TIMELINE_BRCOLOR;
}
if( PB("unhide") ){
tmFlags |= TIMELINE_UNHIDE;
}
|
| ︙ | ︙ | |||
1604 1605 1606 1607 1608 1609 1610 |
" AND pid IN rnfork;",
TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH
);
tmFlags |= TIMELINE_UNHIDE;
zType = "ci";
disableY = 1;
}
| | > | > > > > > > > > | | 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 |
" AND pid IN rnfork;",
TAG_BRANCH, TAG_BRANCH, TAG_BRANCH, TAG_BRANCH
);
tmFlags |= TIMELINE_UNHIDE;
zType = "ci";
disableY = 1;
}
if( bisectLocal
&& fossil_strcmp(g.zIpAddr,"127.0.0.1")==0
&& db_open_local(0)
){
int iCurrent = db_lget_int("checkout",0);
char *zPerm = bisect_permalink();
bisect_create_bilog_table(iCurrent, 0);
tmFlags |= TIMELINE_UNHIDE | TIMELINE_BISECT;
zType = "ci";
disableY = 1;
style_submenu_element("Permalink", "%R/timeline?bid=%z", zPerm);
}else{
bisectLocal = 0;
}
if( zBisect!=0 && bisect_create_bilog_table(0, zBisect) ){
tmFlags |= TIMELINE_UNHIDE | TIMELINE_BISECT;
zType = "ci";
disableY = 1;
}else{
zBisect = 0;
}
style_header("Timeline");
if( advancedMenu ){
style_submenu_element("Help", "%R/help?cmd=/timeline");
}
login_anonymous_available();
|
| ︙ | ︙ | |||
1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 |
}
if( ((from_rid && to_rid) || (me_rid && you_rid)) && g.perm.Read ){
/* If from= and to= are present, display all nodes on a path connecting
** the two */
PathNode *p = 0;
const char *zFrom = 0;
const char *zTo = 0;
if( from_rid && to_rid ){
p = path_shortest(from_rid, to_rid, noMerge, 0);
zFrom = P("from");
zTo = P("to");
}else{
if( path_common_ancestor(me_rid, you_rid) ){
p = path_first();
}
zFrom = P("me");
zTo = P("you");
}
| > > > > > > > > | > > | | | > | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | < > > > > > > > | | | 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 |
}
if( ((from_rid && to_rid) || (me_rid && you_rid)) && g.perm.Read ){
/* If from= and to= are present, display all nodes on a path connecting
** the two */
PathNode *p = 0;
const char *zFrom = 0;
const char *zTo = 0;
Blob ins;
int nNodeOnPath = 0;
if( from_rid && to_rid ){
p = path_shortest(from_rid, to_rid, noMerge, 0);
zFrom = P("from");
zTo = P("to");
}else{
if( path_common_ancestor(me_rid, you_rid) ){
p = path_first();
}
zFrom = P("me");
zTo = P("you");
}
blob_init(&ins, 0, 0);
db_multi_exec(
"CREATE TABLE IF NOT EXISTS temp.pathnode(x INTEGER PRIMARY KEY);"
);
if( p ){
blob_init(&ins, 0, 0);
blob_append_sql(&ins, "INSERT INTO pathnode(x) VALUES(%d)", p->rid);
p = p->u.pTo;
nNodeOnPath = 1;
while( p ){
blob_append_sql(&ins, ",(%d)", p->rid);
p = p->u.pTo;
nNodeOnPath++;
}
}
path_reset();
db_multi_exec("%s", blob_str(&ins)/*safe-for-%s*/);
blob_reset(&ins);
if( related ){
db_multi_exec(
"CREATE TABLE IF NOT EXISTS temp.related(x INTEGER PRIMARY KEY);"
"INSERT OR IGNORE INTO related(x)"
" SELECT pid FROM plink WHERE cid IN pathnode AND NOT isprim;"
);
if( P("mionly")==0 ){
db_multi_exec(
"INSERT OR IGNORE INTO related(x)"
" SELECT cid FROM plink WHERE pid IN pathnode;"
);
}
if( showCherrypicks ){
db_multi_exec(
"INSERT OR IGNORE INTO related(x)"
" SELECT parentid FROM cherrypick WHERE childid IN pathnode;"
);
if( P("mionly")==0 ){
db_multi_exec(
"INSERT OR IGNORE INTO related(x)"
" SELECT childid FROM cherrypick WHERE parentid IN pathnode;"
);
}
}
db_multi_exec("INSERT OR IGNORE INTO pathnode SELECT x FROM related");
}
blob_append_sql(&sql, " AND event.objid IN pathnode");
addFileGlobExclusion(zChng, &sql);
tmFlags |= TIMELINE_DISJOINT;
db_multi_exec("%s", blob_sql_text(&sql));
if( advancedMenu ){
style_submenu_checkbox("v", "Files", (zType[0]!='a' && zType[0]!='c'),0);
}
blob_appendf(&desc, "%d check-ins going from ", nNodeOnPath);
blob_appendf(&desc, "%z[%h]</a>", href("%R/info/%h", zFrom), zFrom);
blob_append(&desc, " to ", -1);
blob_appendf(&desc, "%z[%h]</a>", href("%R/info/%h",zTo), zTo);
if( related ){
int nRelated = db_int(0, "SELECT count(*) FROM timeline") - nNodeOnPath;
if( nRelated>0 ){
blob_appendf(&desc, " and %d related check-in%s", nRelated,
nRelated>1 ? "s" : "");
}
}
addFileGlobDescription(zChng, &desc);
}else if( (p_rid || d_rid) && g.perm.Read && zTagSql==0 ){
/* If p= or d= is present, ignore all other parameters other than n= */
char *zUuid;
int np, nd;
tmFlags |= TIMELINE_DISJOINT;
if( p_rid && d_rid ){
if( p_rid!=d_rid ) p_rid = d_rid;
if( P("n")==0 ) nEntry = 10;
}
db_multi_exec(
"CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY)"
);
zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d",
p_rid ? p_rid : d_rid);
blob_append_sql(&sql, " AND event.objid IN ok");
nd = 0;
if( d_rid ){
compute_descendants(d_rid, nEntry==0 ? 0 : nEntry+1);
nd = db_int(0, "SELECT count(*)-1 FROM ok");
if( nd>=0 ) db_multi_exec("%s", blob_sql_text(&sql));
if( nd>0 ) blob_appendf(&desc, "%d descendant%s", nd,(1==nd)?"":"s");
if( useDividers ) selectedRid = d_rid;
db_multi_exec("DELETE FROM ok");
}
if( p_rid ){
compute_ancestors(p_rid, nEntry==0 ? 0 : nEntry+1, 0);
np = db_int(0, "SELECT count(*)-1 FROM ok");
if( np>0 ){
if( nd>0 ) blob_appendf(&desc, " and ");
blob_appendf(&desc, "%d ancestors", np);
db_multi_exec("%s", blob_sql_text(&sql));
}
if( useDividers ) selectedRid = p_rid;
|
| ︙ | ︙ | |||
1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 |
db_multi_exec(
"CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);"
"INSERT INTO ok VALUES(%d);"
"INSERT OR IGNORE INTO ok SELECT pid FROM plink WHERE cid=%d;"
"INSERT OR IGNORE INTO ok SELECT cid FROM plink WHERE pid=%d;",
f_rid, f_rid, f_rid
);
blob_append_sql(&sql, " AND event.objid IN ok");
db_multi_exec("%s", blob_sql_text(&sql));
if( useDividers ) selectedRid = f_rid;
blob_appendf(&desc, "Parents and children of check-in ");
zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", f_rid);
blob_appendf(&desc, "%z[%S]</a>", href("%R/info/%!S", zUuid), zUuid);
tmFlags |= TIMELINE_DISJOINT;
| > > > > > > > > > | 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 |
db_multi_exec(
"CREATE TEMP TABLE IF NOT EXISTS ok(rid INTEGER PRIMARY KEY);"
"INSERT INTO ok VALUES(%d);"
"INSERT OR IGNORE INTO ok SELECT pid FROM plink WHERE cid=%d;"
"INSERT OR IGNORE INTO ok SELECT cid FROM plink WHERE pid=%d;",
f_rid, f_rid, f_rid
);
if( showCherrypicks ){
db_multi_exec(
"INSERT OR IGNORE INTO ok SELECT parentid FROM cherrypick"
" WHERE childid=%d;"
"INSERT OR IGNORE INTO ok SELECT childid FROM cherrypick"
" WHERE parentid=%d;",
f_rid, f_rid
);
}
blob_append_sql(&sql, " AND event.objid IN ok");
db_multi_exec("%s", blob_sql_text(&sql));
if( useDividers ) selectedRid = f_rid;
blob_appendf(&desc, "Parents and children of check-in ");
zUuid = db_text("", "SELECT uuid FROM blob WHERE rid=%d", f_rid);
blob_appendf(&desc, "%z[%S]</a>", href("%R/info/%!S", zUuid), zUuid);
tmFlags |= TIMELINE_DISJOINT;
|
| ︙ | ︙ | |||
1763 1764 1765 1766 1767 1768 1769 |
}
if( renameOnly ){
blob_append_sql(&cond, " AND event.objid IN rnfile ");
}
if( forkOnly ){
blob_append_sql(&cond, " AND event.objid IN rnfork ");
}
| > > > > > > > > | > | > | 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 |
}
if( renameOnly ){
blob_append_sql(&cond, " AND event.objid IN rnfile ");
}
if( forkOnly ){
blob_append_sql(&cond, " AND event.objid IN rnfork ");
}
if( cpOnly && showCherrypicks ){
db_multi_exec(
"CREATE TABLE IF NOT EXISTS cpnodes(rid INTEGER PRIMARY KEY);"
"INSERT OR IGNORE INTO cpnodes SELECT childid FROM cherrypick;"
"INSERT OR IGNORE INTO cpnodes SELECT parentid FROM cherrypick;"
);
blob_append_sql(&cond, " AND event.objid IN cpnodes ");
}
if( bisectLocal || zBisect!=0 ){
blob_append_sql(&cond, " AND event.objid IN (SELECT rid FROM bilog) ");
}
if( zYearMonth ){
zYearMonth = timeline_expand_datetime(zYearMonth);
blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%m',event.mtime) ",
zYearMonth);
}
else if( zYearWeek ){
zYearWeek = timeline_expand_datetime(zYearWeek);
char *z = db_text(0, "SELECT strftime('%%Y-%%W',%Q)", zYearWeek);
if( z && z[0] ){
zYearWeekStart = db_text(0, "SELECT date(%Q,'-6 days','weekday 1')",
zYearWeek);
zYearWeek = z;
}else{
if( strlen(zYearWeek)==7 ){
|
| ︙ | ︙ | |||
1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 |
}
}
blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%W',event.mtime) ",
zYearWeek);
nEntry = -1;
}
else if( zDay ){
zDay = db_text(0, "SELECT date(%Q)", zDay);
if( zDay==0 || zDay[0]==0 ){
zDay = db_text(0, "SELECT date('now')");
}
blob_append_sql(&cond, " AND %Q=date(event.mtime) ",
zDay);
nEntry = -1;
}
else if( zNDays ){
nDays = atoi(zNDays);
if( nDays<1 ) nDays = 1;
blob_append_sql(&cond, " AND event.mtime>=julianday('now','-%d days') ",
nDays);
nEntry = -1;
}
if( zTagSql ){
| > | > > | | | | > > > > > > > | | > | | < | | < > > | < > > > > > > | > | | > | | < > | | > | | | | | | < < | 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 |
}
}
blob_append_sql(&cond, " AND %Q=strftime('%%Y-%%W',event.mtime) ",
zYearWeek);
nEntry = -1;
}
else if( zDay ){
zDay = timeline_expand_datetime(zDay);
zDay = db_text(0, "SELECT date(%Q)", zDay);
if( zDay==0 || zDay[0]==0 ){
zDay = db_text(0, "SELECT date('now')");
}
blob_append_sql(&cond, " AND %Q=date(event.mtime) ",
zDay);
nEntry = -1;
}
else if( zNDays ){
nDays = atoi(zNDays);
if( nDays<1 ) nDays = 1;
blob_append_sql(&cond, " AND event.mtime>=julianday('now','-%d days') ",
nDays);
nEntry = -1;
}
if( zTagSql ){
db_multi_exec(
"CREATE TEMP TABLE selected_nodes(rid INTEGER PRIMARY KEY);"
"INSERT OR IGNORE INTO selected_nodes"
" SELECT tagxref.rid FROM tagxref NATURAL JOIN tag"
" WHERE %s AND tagtype>0", zTagSql/*safe-for-%s*/
);
if( !related ){
blob_append_sql(&cond, " AND blob.rid IN selected_nodes");
}else{
db_multi_exec(
"CREATE TEMP TABLE related_nodes(rid INTEGER PRIMARY KEY);"
"INSERT INTO related_nodes SELECT rid FROM selected_nodes;"
);
blob_append_sql(&cond, " AND blob.rid IN related_nodes");
/* The next two blob_appendf() calls add SQL that causes check-ins that
** are not part of the branch which are parents or children of the
** branch to be included in the report. These related check-ins are
** useful in helping to visualize what has happened on a quiescent
** branch that is infrequently merged with a much more activate branch.
*/
db_multi_exec(
"INSERT OR IGNORE INTO related_nodes"
" SELECT pid FROM selected_nodes CROSS JOIN plink"
" WHERE selected_nodes.rid=plink.cid;"
);
if( P("mionly")==0 ){
db_multi_exec(
"INSERT OR IGNORE INTO related_nodes"
" SELECT cid FROM selected_nodes CROSS JOIN plink"
" WHERE selected_nodes.rid=plink.pid;"
);
if( showCherrypicks ){
db_multi_exec(
"INSERT OR IGNORE INTO related_nodes"
" SELECT childid FROM selected_nodes CROSS JOIN cherrypick"
" WHERE selected_nodes.rid=cherrypick.parentid;"
);
}
}
if( showCherrypicks ){
db_multi_exec(
"INSERT OR IGNORE INTO related_nodes"
" SELECT parentid FROM selected_nodes CROSS JOIN cherrypick"
" WHERE selected_nodes.rid=cherrypick.childid;"
);
}
if( (tmFlags & TIMELINE_UNHIDE)==0 ){
db_multi_exec(
"DELETE FROM related_nodes WHERE rid IN "
" (SELECT related_nodes.rid FROM related_nodes, tagxref"
" WHERE tagid=%d AND tagtype>0 AND tagxref.rid=related_nodes.rid)",
TAG_HIDDEN
);
}
}
}
if( (zType[0]=='w' && !g.perm.RdWiki)
|| (zType[0]=='t' && !g.perm.RdTkt)
|| (zType[0]=='e' && !g.perm.RdWiki)
|| (zType[0]=='c' && !g.perm.Read)
|| (zType[0]=='g' && !g.perm.Read)
|| (zType[0]=='f' && !g.perm.RdForum)
|
| ︙ | ︙ | |||
1915 1916 1917 1918 1919 1920 1921 |
zThisUser = zUser;
}
if( zSearch ){
blob_append_sql(&cond,
" AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')",
zSearch, zSearch);
}
| | | | | 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 |
zThisUser = zUser;
}
if( zSearch ){
blob_append_sql(&cond,
" AND (event.comment LIKE '%%%q%%' OR event.brief LIKE '%%%q%%')",
zSearch, zSearch);
}
rBefore = symbolic_name_to_mtime(zBefore, &zBefore);
rAfter = symbolic_name_to_mtime(zAfter, &zAfter);
rCirca = symbolic_name_to_mtime(zCirca, &zCirca);
blob_append_sql(&sql, "%s", blob_sql_text(&cond));
if( rAfter>0.0 ){
if( rBefore>0.0 ){
blob_append_sql(&sql,
" AND event.mtime>=%.17g AND event.mtime<=%.17g"
" ORDER BY event.mtime ASC", rAfter-ONE_SECOND, rBefore+ONE_SECOND);
nEntry = -1;
|
| ︙ | ︙ | |||
1994 1995 1996 1997 1998 1999 2000 |
blob_appendf(&desc, " that contain filename changes");
tmFlags |= TIMELINE_DISJOINT|TIMELINE_FRENAMES;
}
if( forkOnly ){
blob_appendf(&desc, " associated with forks");
tmFlags |= TIMELINE_DISJOINT;
}
| | | > > > > | 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 |
blob_appendf(&desc, " that contain filename changes");
tmFlags |= TIMELINE_DISJOINT|TIMELINE_FRENAMES;
}
if( forkOnly ){
blob_appendf(&desc, " associated with forks");
tmFlags |= TIMELINE_DISJOINT;
}
if( bisectLocal || zBisect!=0 ){
blob_appendf(&desc, " in a bisect");
tmFlags |= TIMELINE_DISJOINT;
}
if( cpOnly && showCherrypicks ){
blob_appendf(&desc, " that participate in a cherrypick merge");
tmFlags |= TIMELINE_CHPICK|TIMELINE_DISJOINT;
}
if( zUser ){
blob_appendf(&desc, " by user %h", zUser);
tmFlags |= TIMELINE_DISJOINT;
}
if( zTagSql ){
if( matchStyle==MS_EXACT ){
if( related ){
|
| ︙ | ︙ | |||
2044 2045 2046 2047 2048 2049 2050 |
};
double rDate;
zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/");
if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
zDate = mprintf("%s", (zAfter ? zAfter : zBefore));
}
if( zDate ){
| | | | 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 |
};
double rDate;
zDate = db_text(0, "SELECT min(timestamp) FROM timeline /*scan*/");
if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
zDate = mprintf("%s", (zAfter ? zAfter : zBefore));
}
if( zDate ){
rDate = symbolic_name_to_mtime(zDate, 0);
if( db_int(0,
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
" WHERE blob.rid=event.objid AND mtime<=%.17g%s)",
rDate-ONE_SECOND, blob_sql_text(&cond))
){
zOlderButton = fossil_strdup(url_render(&url, "b", zDate, "a", 0));
}
free(zDate);
}
zDate = db_text(0, "SELECT max(timestamp) FROM timeline /*scan*/");
if( (!zDate || !zDate[0]) && ( zAfter || zBefore ) ){
zDate = mprintf("%s", (zBefore ? zBefore : zAfter));
}
if( zDate ){
rDate = symbolic_name_to_mtime(zDate, 0);
if( db_int(0,
"SELECT EXISTS (SELECT 1 FROM event CROSS JOIN blob"
" WHERE blob.rid=event.objid AND mtime>=%.17g%s)",
rDate+ONE_SECOND, blob_sql_text(&cond))
){
zNewerButton = fossil_strdup(url_render(&url, "a", zDate, "b", 0));
}
|
| ︙ | ︙ | |||
2091 2092 2093 2094 2095 2096 2097 |
if( PB("showsql") ){
@ <pre>%h(blob_sql_text(&sql))</pre>
}
if( search_restrict(SRCH_CKIN)!=0 ){
style_submenu_element("Search", "%R/search?y=c");
}
if( advancedMenu ){
| | > | > | > > > > > > > > > > > > > > | > | 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 |
if( PB("showsql") ){
@ <pre>%h(blob_sql_text(&sql))</pre>
}
if( search_restrict(SRCH_CKIN)!=0 ){
style_submenu_element("Search", "%R/search?y=c");
}
if( advancedMenu ){
style_submenu_element("Basic", "%s",
url_render(&url, "advm", "0", "udc", "1"));
}else{
style_submenu_element("Advanced", "%s",
url_render(&url, "advm", "1", "udc", "1"));
}
if( PB("showid") ) tmFlags |= TIMELINE_SHOWRID;
if( useDividers && zMark && zMark[0] ){
double r = symbolic_name_to_mtime(zMark, 0);
if( r>0.0 ) selectedRid = timeline_add_divider(r);
}
blob_zero(&sql);
db_prepare(&q, "SELECT * FROM timeline ORDER BY sortby DESC /*scan*/");
if( fossil_islower(desc.aData[0]) ){
desc.aData[0] = fossil_toupper(desc.aData[0]);
}
if( zBrName
&& !PB("nowiki")
&& wiki_render_associated("branch", zBrName, WIKIASSOC_ALL)
){
@ <div class="section">%b(&desc)</div>
}else
if( zTagName
&& matchStyle==MS_EXACT
&& zBrName==0
&& !PB("nowiki")
&& wiki_render_associated("tag", zTagName, WIKIASSOC_ALL)
){
@ <div class="section">%b(&desc)</div>
} else{
@ <h2>%b(&desc)</h2>
}
blob_reset(&desc);
/* Report any errors. */
if( zError ){
@ <p class="generalError">%h(zError)</p>
}
|
| ︙ | ︙ | |||
2215 2216 2217 2218 2219 2220 2221 |
}
if( content_is_private(rid) ){
sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*UNPUBLISHED* ");
n += strlen(zPrefix+n);
}
zFree = mprintf("[%S] %s%s", zId, zPrefix, zCom);
/* record another X lines */
| | | 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 |
}
if( content_is_private(rid) ){
sqlite3_snprintf(sizeof(zPrefix)-n, &zPrefix[n], "*UNPUBLISHED* ");
n += strlen(zPrefix+n);
}
zFree = mprintf("[%S] %s%s", zId, zPrefix, zCom);
/* record another X lines */
nLine += comment_print(zFree, zCom, 9, width, get_comment_format());
fossil_free(zFree);
if(verboseFlag){
if( !fchngQueryInit ){
db_prepare(&fchngQuery,
"SELECT (pid<=0) AS isnew,"
" (fid==0) AS isdel,"
|
| ︙ | ︙ |
Changes to src/tkt.c.
| ︙ | ︙ | |||
1295 1296 1297 1298 1299 1300 1301 |
z++;
}else{
fossil_print(" Change ");
}
fossil_print("%h: ",z);
if( blob_size(&val)>50 || contains_newline(&val)) {
fossil_print("\n ");
| | | 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 |
z++;
}else{
fossil_print(" Change ");
}
fossil_print("%h: ",z);
if( blob_size(&val)>50 || contains_newline(&val)) {
fossil_print("\n ");
comment_print(blob_str(&val),0,4,-1,get_comment_format());
}else{
fossil_print("%s\n",blob_str(&val));
}
blob_reset(&val);
}
}
manifest_destroy(pTicket);
|
| ︙ | ︙ |
Changes to src/undo.c.
| ︙ | ︙ | |||
162 163 164 165 166 167 168 |
"INSERT OR IGNORE INTO stashfile SELECT * FROM undo_stashfile;"
);
}
}
ncid = db_lget_int("undo_checkout", 0);
ucid = db_lget_int("checkout", 0);
db_lset_int("undo_checkout", ucid);
| | | 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
"INSERT OR IGNORE INTO stashfile SELECT * FROM undo_stashfile;"
);
}
}
ncid = db_lget_int("undo_checkout", 0);
ucid = db_lget_int("checkout", 0);
db_lset_int("undo_checkout", ucid);
db_set_checkout(ncid);
}
/*
** Reset the undo memory.
*/
void undo_reset(void){
static const char zSql[] =
|
| ︙ | ︙ |
Changes to src/unicode.c.
| ︙ | ︙ | |||
57 58 59 60 61 62 63 |
0x0027E802, 0x0027F402, 0x00280403, 0x0028F001, 0x0028F805,
0x00291C02, 0x00292C03, 0x00294401, 0x0029C002, 0x0029D402,
0x002A0403, 0x002AF001, 0x002AF808, 0x002B1C03, 0x002B2C03,
0x002B8802, 0x002BC002, 0x002BE806, 0x002C0403, 0x002CF001,
0x002CF807, 0x002D1C02, 0x002D2C03, 0x002D5802, 0x002D8802,
0x002DC001, 0x002E0801, 0x002EF805, 0x002F1803, 0x002F2804,
0x002F5C01, 0x002FCC08, 0x00300005, 0x0030F807, 0x00311803,
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | > | | | | > | | | | | 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 |
0x0027E802, 0x0027F402, 0x00280403, 0x0028F001, 0x0028F805,
0x00291C02, 0x00292C03, 0x00294401, 0x0029C002, 0x0029D402,
0x002A0403, 0x002AF001, 0x002AF808, 0x002B1C03, 0x002B2C03,
0x002B8802, 0x002BC002, 0x002BE806, 0x002C0403, 0x002CF001,
0x002CF807, 0x002D1C02, 0x002D2C03, 0x002D5802, 0x002D8802,
0x002DC001, 0x002E0801, 0x002EF805, 0x002F1803, 0x002F2804,
0x002F5C01, 0x002FCC08, 0x00300005, 0x0030F807, 0x00311803,
0x00312804, 0x00315402, 0x00318802, 0x0031DC01, 0x0031FC01,
0x00320404, 0x0032F001, 0x0032F807, 0x00331803, 0x00332804,
0x00335402, 0x00338802, 0x00340004, 0x0034EC02, 0x0034F807,
0x00351803, 0x00352804, 0x00353C01, 0x00355C01, 0x00358802,
0x0035E401, 0x00360802, 0x00372801, 0x00373C06, 0x00375801,
0x00376008, 0x0037C803, 0x0038C401, 0x0038D007, 0x0038FC01,
0x00391C09, 0x00396802, 0x003AC401, 0x003AD009, 0x003B2006,
0x003C041F, 0x003CD00C, 0x003DC417, 0x003E340B, 0x003E6424,
0x003EF80F, 0x003F380D, 0x0040AC14, 0x00412806, 0x00415804,
0x00417803, 0x00418803, 0x00419C07, 0x0041C404, 0x0042080C,
0x00423C01, 0x00426806, 0x0043EC01, 0x004D740C, 0x004E400A,
0x00500001, 0x0059B402, 0x005A0001, 0x005A6C02, 0x005BAC03,
0x005C4803, 0x005CC805, 0x005D4802, 0x005DC802, 0x005ED023,
0x005F6004, 0x005F7401, 0x0060000F, 0x00621402, 0x0062A401,
0x0064800C, 0x0064C00C, 0x00650001, 0x00651002, 0x00677822,
0x00685C05, 0x00687802, 0x0069540A, 0x0069801D, 0x0069FC01,
0x006A8007, 0x006AA006, 0x006AC00F, 0x006C0005, 0x006CD011,
0x006D6823, 0x006E0003, 0x006E840D, 0x006F980E, 0x006FF004,
0x00709014, 0x0070EC05, 0x0071F802, 0x00730008, 0x00734019,
0x0073B401, 0x0073D001, 0x0073DC03, 0x0077003A, 0x0077EC05,
0x007EF401, 0x007EFC03, 0x007F3403, 0x007F7403, 0x007FB403,
0x007FF402, 0x00800065, 0x0081980A, 0x0081E805, 0x00822805,
0x00828020, 0x00834021, 0x00840002, 0x00840C04, 0x00842002,
0x00845001, 0x00845803, 0x00847806, 0x00849401, 0x00849C01,
0x0084A401, 0x0084B801, 0x0084E802, 0x00850005, 0x00852804,
0x00853C01, 0x00862802, 0x00864297, 0x0091000B, 0x0092704E,
0x00940276, 0x009E53E0, 0x00ADD820, 0x00AE6068, 0x00B39406,
0x00B3BC03, 0x00B3E404, 0x00B3F802, 0x00B5C001, 0x00B5FC01,
0x00B7804F, 0x00B8C020, 0x00BA001A, 0x00BA6C59, 0x00BC00D6,
0x00BFC00C, 0x00C00005, 0x00C02019, 0x00C0A807, 0x00C0D802,
0x00C0F403, 0x00C26404, 0x00C28001, 0x00C3EC01, 0x00C64002,
0x00C6580A, 0x00C70024, 0x00C8001F, 0x00C8A81E, 0x00C94001,
0x00C98020, 0x00CA2827, 0x00CB0140, 0x01370040, 0x02924037,
0x0293F802, 0x02983403, 0x0299BC10, 0x029A7802, 0x029BC008,
0x029C0017, 0x029C8002, 0x029E2402, 0x02A00801, 0x02A01801,
0x02A02C01, 0x02A08C09, 0x02A0D804, 0x02A1D004, 0x02A20002,
0x02A2D012, 0x02A33802, 0x02A38012, 0x02A3E003, 0x02A3F001,
0x02A3FC01, 0x02A4980A, 0x02A51C0D, 0x02A57C01, 0x02A60004,
0x02A6CC1B, 0x02A77802, 0x02A79401, 0x02A8A40E, 0x02A90C01,
0x02A93002, 0x02A97004, 0x02A9DC03, 0x02A9EC03, 0x02AAC001,
0x02AAC803, 0x02AADC02, 0x02AAF802, 0x02AB0401, 0x02AB7802,
0x02ABAC07, 0x02ABD402, 0x02AD6C01, 0x02AF8C0B, 0x03600001,
0x036DFC02, 0x036FFC02, 0x037FFC01, 0x03EC7801, 0x03ECA401,
0x03EEC810, 0x03F4F802, 0x03F7F002, 0x03F8001A, 0x03F88033,
0x03F95013, 0x03F9A004, 0x03FBFC01, 0x03FC040F, 0x03FC6807,
0x03FCEC06, 0x03FD6C0B, 0x03FF8007, 0x03FFA007, 0x03FFE405,
0x04040003, 0x0404DC09, 0x0405E411, 0x04063003, 0x0406400C,
0x04068001, 0x0407402E, 0x040B8001, 0x040DD805, 0x040E7C01,
0x040F4001, 0x0415BC01, 0x04215C01, 0x0421DC02, 0x04247C01,
0x0424FC01, 0x04280403, 0x04281402, 0x04283004, 0x0428E003,
0x0428FC01, 0x04294009, 0x0429FC01, 0x042B2001, 0x042B9402,
0x042BC007, 0x042CE407, 0x042E6404, 0x04349004, 0x043D180B,
0x043D5405, 0x04400003, 0x0440E016, 0x0441FC04, 0x0442C012,
0x04433401, 0x04440003, 0x04449C0E, 0x04450004, 0x04451402,
0x0445CC03, 0x04460003, 0x0446CC0E, 0x04471409, 0x04476C01,
0x04477403, 0x0448B013, 0x044AA401, 0x044B7C0C, 0x044C0004,
0x044CEC02, 0x044CF807, 0x044D1C02, 0x044D2C03, 0x044D5C01,
0x044D8802, 0x044D9807, 0x044DC005, 0x0450D412, 0x04512C05,
0x04516C01, 0x04517402, 0x0452C014, 0x04531801, 0x0456BC07,
0x0456E020, 0x04577002, 0x0458C014, 0x0459800D, 0x045AAC0D,
0x045C740F, 0x045CF004, 0x0460B010, 0x04674407, 0x04676807,
0x04678801, 0x04679001, 0x0468040A, 0x0468CC07, 0x0468EC0D,
0x0469440B, 0x046A2813, 0x046A7805, 0x0470BC08, 0x0470E008,
0x04710405, 0x0471C002, 0x04724816, 0x0472A40E, 0x0474C406,
0x0474E801, 0x0474F002, 0x0474FC07, 0x04751C01, 0x04762805,
0x04764002, 0x04764C05, 0x047BCC06, 0x047F541D, 0x047FFC01,
0x0491C005, 0x04D0C009, 0x05A9B802, 0x05ABC006, 0x05ACC010,
0x05AD1002, 0x05BA5C04, 0x05BD3C01, 0x05BD4437, 0x05BE3C04,
0x05BF8801, 0x06F27008, 0x074000F6, 0x07440027, 0x0744A4C0,
0x07480046, 0x074C0057, 0x075B0401, 0x075B6C01, 0x075BEC01,
0x075C5401, 0x075CD401, 0x075D3C01, 0x075DBC01, 0x075E2401,
0x075EA401, 0x075F0C01, 0x0760028C, 0x076A6C05, 0x076A840F,
0x07800007, 0x07802011, 0x07806C07, 0x07808C02, 0x07809805,
0x0784C007, 0x07853C01, 0x078BB004, 0x078BFC01, 0x07A34007,
0x07A51007, 0x07A57802, 0x07B2B001, 0x07B2C001, 0x07B4B801,
0x07BBC002, 0x07C0002C, 0x07C0C064, 0x07C2800F, 0x07C2C40F,
0x07C3040F, 0x07C34425, 0x07C4405D, 0x07C5C03D, 0x07C7981D,
0x07C8402C, 0x07C90009, 0x07C94002, 0x07C98006, 0x07CC03D6,
0x07DB800D, 0x07DBC00B, 0x07DC0074, 0x07DE0059, 0x07DF800C,
0x07E0000C, 0x07E04038, 0x07E1400A, 0x07E18028, 0x07E2401E,
0x07E4000C, 0x07E43465, 0x07E5CC04, 0x07E5E829, 0x07E69406,
0x07E6B81D, 0x07E73487, 0x07E9800E, 0x07E9C004, 0x07E9E003,
0x07EA0003, 0x07EA4006, 0x38000401, 0x38008060, 0x380400F0,
};
static const unsigned int aAscii[4] = {
0xFFFFFFFF, 0xFC00FFFF, 0xF8000001, 0xF8000001,
};
if( (unsigned int)c<128 ){
return ( (aAscii[c >> 5] & ((unsigned int)1 << (c & 0x001F)))==0 );
|
| ︙ | ︙ | |||
174 175 176 177 178 179 180 |
** If the argument is a codepoint corresponding to a lowercase letter
** in the ASCII range with a diacritic added, return the codepoint
** of the ASCII letter only. For example, if passed 235 - "LATIN
** SMALL LETTER E WITH DIAERESIS" - return 65 ("LATIN SMALL LETTER
** E"). The resuls of passing a codepoint that corresponds to an
** uppercase letter are undefined.
*/
| | | | | > | | > | | | | > > | < > > > > > > > | > > | < < > | > > | | > > | > > | | | | | 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 |
** If the argument is a codepoint corresponding to a lowercase letter
** in the ASCII range with a diacritic added, return the codepoint
** of the ASCII letter only. For example, if passed 235 - "LATIN
** SMALL LETTER E WITH DIAERESIS" - return 65 ("LATIN SMALL LETTER
** E"). The resuls of passing a codepoint that corresponds to an
** uppercase letter are undefined.
*/
static int unicode_remove_diacritic(int c, int bComplex){
static const unsigned short aDia[] = {
0, 1797, 1848, 1859, 1891, 1928, 1940, 1995,
2024, 2040, 2060, 2110, 2168, 2206, 2264, 2286,
2344, 2383, 2472, 2488, 2516, 2596, 2668, 2732,
2782, 2842, 2894, 2954, 2984, 3000, 3028, 3336,
3456, 3696, 3712, 3728, 3744, 3766, 3832, 3896,
3912, 3928, 3944, 3968, 4008, 4040, 4056, 4106,
4138, 4170, 4202, 4234, 4266, 4296, 4312, 4344,
4408, 4424, 4442, 4472, 4488, 4504, 6148, 6198,
6264, 6280, 6360, 6429, 6505, 6529, 61448, 61468,
61512, 61534, 61592, 61610, 61642, 61672, 61688, 61704,
61726, 61784, 61800, 61816, 61836, 61880, 61896, 61914,
61948, 61998, 62062, 62122, 62154, 62184, 62200, 62218,
62252, 62302, 62364, 62410, 62442, 62478, 62536, 62554,
62584, 62604, 62640, 62648, 62656, 62664, 62730, 62766,
62830, 62890, 62924, 62974, 63032, 63050, 63082, 63118,
63182, 63242, 63274, 63310, 63368, 63390,
};
#define HIBIT ((unsigned char)0x80)
static const unsigned char aChar[] = {
'\0', 'a', 'c', 'e', 'i', 'n',
'o', 'u', 'y', 'y', 'a', 'c',
'd', 'e', 'e', 'g', 'h', 'i',
'j', 'k', 'l', 'n', 'o', 'r',
's', 't', 'u', 'u', 'w', 'y',
'z', 'o', 'u', 'a', 'i', 'o',
'u', 'u'|HIBIT, 'a'|HIBIT, 'g', 'k', 'o',
'o'|HIBIT, 'j', 'g', 'n', 'a'|HIBIT, 'a',
'e', 'i', 'o', 'r', 'u', 's',
't', 'h', 'a', 'e', 'o'|HIBIT, 'o',
'o'|HIBIT, 'y', '\0', '\0', '\0', '\0',
'\0', '\0', '\0', '\0', 'a', 'b',
'c'|HIBIT, 'd', 'd', 'e'|HIBIT, 'e', 'e'|HIBIT,
'f', 'g', 'h', 'h', 'i', 'i'|HIBIT,
'k', 'l', 'l'|HIBIT, 'l', 'm', 'n',
'o'|HIBIT, 'p', 'r', 'r'|HIBIT, 'r', 's',
's'|HIBIT, 't', 'u', 'u'|HIBIT, 'v', 'w',
'w', 'x', 'y', 'z', 'h', 't',
'w', 'y', 'a', 'a'|HIBIT, 'a'|HIBIT, 'a'|HIBIT,
'e', 'e'|HIBIT, 'e'|HIBIT, 'i', 'o', 'o'|HIBIT,
'o'|HIBIT, 'o'|HIBIT, 'u', 'u'|HIBIT, 'u'|HIBIT, 'y',
};
unsigned int key = (((unsigned int)c)<<3) | 0x00000007;
int iRes = 0;
int iHi = sizeof(aDia)/sizeof(aDia[0]) - 1;
int iLo = 0;
while( iHi>=iLo ){
int iTest = (iHi + iLo) / 2;
if( key >= aDia[iTest] ){
iRes = iTest;
iLo = iTest+1;
}else{
iHi = iTest-1;
}
}
assert( key>=aDia[iRes] );
if( bComplex==0 && (aChar[iRes] & 0x80) ) return c;
return (c > (aDia[iRes]>>3) + (aDia[iRes]&0x07)) ? c : ((int)aChar[iRes] & 0x7F);
}
/*
** Return true if the argument interpreted as a unicode codepoint
** is a diacritical modifier character.
*/
int unicode_is_diacritic(int c){
unsigned int mask0 = 0x08029FDF;
unsigned int mask1 = 0x000361F8;
if( c<768 || c>817 ) return 0;
return (c < 768+32) ?
(mask0 & ((unsigned int)1 << (c-768))) :
(mask1 & ((unsigned int)1 << (c-768-32)));
}
/*
** Interpret the argument as a unicode codepoint. If the codepoint
** is an upper case character that has a lower case equivalent,
** return the codepoint corresponding to the lower case version.
** Otherwise, return a copy of the argument.
**
** The results are undefined if the value passed to this function
** is less than zero.
*/
int unicode_fold(int c, int eRemoveDiacritic){
/* Each entry in the following array defines a rule for folding a range
** of codepoints to lower case. The rule applies to a range of nRange
** codepoints starting at codepoint iCode.
**
** If the least significant bit in flags is clear, then the rule applies
** to all nRange codepoints (i.e. all nRange codepoints are upper case and
** need to be folded). Or, if it is set, then the rule only applies to
|
| ︙ | ︙ | |||
268 269 270 271 272 273 274 |
static const struct TableEntry {
unsigned short iCode;
unsigned char flags;
unsigned char nRange;
} aEntry[] = {
{65, 14, 26}, {181, 66, 1}, {192, 14, 23},
{216, 14, 7}, {256, 1, 48}, {306, 1, 6},
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | > | | | | | | | | > | 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 |
static const struct TableEntry {
unsigned short iCode;
unsigned char flags;
unsigned char nRange;
} aEntry[] = {
{65, 14, 26}, {181, 66, 1}, {192, 14, 23},
{216, 14, 7}, {256, 1, 48}, {306, 1, 6},
{313, 1, 16}, {330, 1, 46}, {376, 156, 1},
{377, 1, 6}, {383, 144, 1}, {385, 52, 1},
{386, 1, 4}, {390, 46, 1}, {391, 0, 1},
{393, 44, 2}, {395, 0, 1}, {398, 34, 1},
{399, 40, 1}, {400, 42, 1}, {401, 0, 1},
{403, 44, 1}, {404, 48, 1}, {406, 54, 1},
{407, 50, 1}, {408, 0, 1}, {412, 54, 1},
{413, 56, 1}, {415, 58, 1}, {416, 1, 6},
{422, 62, 1}, {423, 0, 1}, {425, 62, 1},
{428, 0, 1}, {430, 62, 1}, {431, 0, 1},
{433, 60, 2}, {435, 1, 4}, {439, 64, 1},
{440, 0, 1}, {444, 0, 1}, {452, 2, 1},
{453, 0, 1}, {455, 2, 1}, {456, 0, 1},
{458, 2, 1}, {459, 1, 18}, {478, 1, 18},
{497, 2, 1}, {498, 1, 4}, {502, 162, 1},
{503, 174, 1}, {504, 1, 40}, {544, 150, 1},
{546, 1, 18}, {570, 74, 1}, {571, 0, 1},
{573, 148, 1}, {574, 72, 1}, {577, 0, 1},
{579, 146, 1}, {580, 30, 1}, {581, 32, 1},
{582, 1, 10}, {837, 38, 1}, {880, 1, 4},
{886, 0, 1}, {895, 38, 1}, {902, 20, 1},
{904, 18, 3}, {908, 28, 1}, {910, 26, 2},
{913, 14, 17}, {931, 14, 9}, {962, 0, 1},
{975, 4, 1}, {976, 180, 1}, {977, 182, 1},
{981, 186, 1}, {982, 184, 1}, {984, 1, 24},
{1008, 176, 1}, {1009, 178, 1}, {1012, 170, 1},
{1013, 168, 1}, {1015, 0, 1}, {1017, 192, 1},
{1018, 0, 1}, {1021, 150, 3}, {1024, 36, 16},
{1040, 14, 32}, {1120, 1, 34}, {1162, 1, 54},
{1216, 6, 1}, {1217, 1, 14}, {1232, 1, 96},
{1329, 24, 38}, {4256, 70, 38}, {4295, 70, 1},
{4301, 70, 1}, {5112, 190, 6}, {7296, 126, 1},
{7297, 128, 1}, {7298, 130, 1}, {7299, 134, 2},
{7301, 132, 1}, {7302, 136, 1}, {7303, 138, 1},
{7304, 100, 1}, {7312, 142, 43}, {7357, 142, 3},
{7680, 1, 150}, {7835, 172, 1}, {7838, 120, 1},
{7840, 1, 96}, {7944, 190, 8}, {7960, 190, 6},
{7976, 190, 8}, {7992, 190, 8}, {8008, 190, 6},
{8025, 191, 8}, {8040, 190, 8}, {8072, 190, 8},
{8088, 190, 8}, {8104, 190, 8}, {8120, 190, 2},
{8122, 166, 2}, {8124, 188, 1}, {8126, 124, 1},
{8136, 164, 4}, {8140, 188, 1}, {8152, 190, 2},
{8154, 160, 2}, {8168, 190, 2}, {8170, 158, 2},
{8172, 192, 1}, {8184, 152, 2}, {8186, 154, 2},
{8188, 188, 1}, {8486, 122, 1}, {8490, 116, 1},
{8491, 118, 1}, {8498, 12, 1}, {8544, 8, 16},
{8579, 0, 1}, {9398, 10, 26}, {11264, 24, 47},
{11360, 0, 1}, {11362, 112, 1}, {11363, 140, 1},
{11364, 114, 1}, {11367, 1, 6}, {11373, 108, 1},
{11374, 110, 1}, {11375, 104, 1}, {11376, 106, 1},
{11378, 0, 1}, {11381, 0, 1}, {11390, 102, 2},
{11392, 1, 100}, {11499, 1, 4}, {11506, 0, 1},
{42560, 1, 46}, {42624, 1, 28}, {42786, 1, 14},
{42802, 1, 62}, {42873, 1, 4}, {42877, 98, 1},
{42878, 1, 10}, {42891, 0, 1}, {42893, 88, 1},
{42896, 1, 4}, {42902, 1, 20}, {42922, 80, 1},
{42923, 76, 1}, {42924, 78, 1}, {42925, 84, 1},
{42926, 80, 1}, {42928, 92, 1}, {42929, 86, 1},
{42930, 90, 1}, {42931, 68, 1}, {42932, 1, 12},
{42946, 0, 1}, {42948, 178, 1}, {42949, 82, 1},
{42950, 96, 1}, {43888, 94, 80}, {65313, 14, 26},
};
static const unsigned short aiOff[] = {
1, 2, 8, 15, 16, 26, 28, 32,
34, 37, 38, 40, 48, 63, 64, 69,
71, 79, 80, 116, 202, 203, 205, 206,
207, 209, 210, 211, 213, 214, 217, 218,
219, 775, 928, 7264, 10792, 10795, 23217, 23221,
23228, 23229, 23231, 23254, 23256, 23275, 23278, 26672,
30152, 30204, 35267, 54721, 54753, 54754, 54756, 54787,
54793, 54809, 57153, 57274, 57921, 58019, 58363, 59314,
59315, 59324, 59325, 59326, 59332, 59356, 61722, 62528,
65268, 65341, 65373, 65406, 65408, 65410, 65415, 65424,
65436, 65439, 65450, 65462, 65472, 65476, 65478, 65480,
65482, 65488, 65506, 65511, 65514, 65521, 65527, 65528,
65529,
};
int ret = c;
assert( sizeof(unsigned short)==2 && sizeof(unsigned char)==1 );
if( c<128 ){
|
| ︙ | ︙ | |||
375 376 377 378 379 380 381 |
assert( iRes>=0 && c>=aEntry[iRes].iCode );
p = &aEntry[iRes];
if( c<(p->iCode + p->nRange) && 0==(0x01 & p->flags & (p->iCode ^ c)) ){
ret = (c + (aiOff[p->flags>>1])) & 0x0000FFFF;
assert( ret>0 );
}
| > | > | 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 |
assert( iRes>=0 && c>=aEntry[iRes].iCode );
p = &aEntry[iRes];
if( c<(p->iCode + p->nRange) && 0==(0x01 & p->flags & (p->iCode ^ c)) ){
ret = (c + (aiOff[p->flags>>1])) & 0x0000FFFF;
assert( ret>0 );
}
if( eRemoveDiacritic ){
ret = unicode_remove_diacritic(ret, eRemoveDiacritic==2);
}
}
else if( c>=66560 && c<66600 ){
ret = c + 40;
}
else if( c>=66736 && c<66772 ){
ret = c + 40;
|
| ︙ | ︙ |
Changes to src/update.c.
| ︙ | ︙ | |||
158 159 160 161 162 163 164 |
db_get_int("autosync-tries", 1), 1) ){
fossil_fatal("update abandoned due to sync failure");
}
}
/* Create any empty directories now, as well as after the update,
** so changes in settings are reflected now */
| | | 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
db_get_int("autosync-tries", 1), 1) ){
fossil_fatal("update abandoned due to sync failure");
}
}
/* Create any empty directories now, as well as after the update,
** so changes in settings are reflected now */
if( !dryRunFlag ) ensure_empty_dirs_created(0);
if( internalUpdate ){
tid = internalUpdate;
}else if( g.argc>=3 ){
if( fossil_strcmp(g.argv[2], "current")==0 ){
/* If VERSION is "current", then use the same algorithm to find the
** target as if VERSION were omitted. */
|
| ︙ | ︙ | |||
225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
}
if( tid==0 ){
return;
}
db_begin_transaction();
vfile_check_signature(vid, CKSIG_ENOTFILE);
if( !dryRunFlag && !internalUpdate ) undo_begin();
if( load_vfile_from_rid(tid) && !forceMissingFlag ){
fossil_fatal("missing content, unable to update");
};
/*
| > > > > | 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
}
if( tid==0 ){
return;
}
db_begin_transaction();
db_multi_exec(
"CREATE TEMP TABLE dir_to_delete(name TEXT %s PRIMARY KEY)WITHOUT ROWID",
filename_collation()
);
vfile_check_signature(vid, CKSIG_ENOTFILE);
if( !dryRunFlag && !internalUpdate ) undo_begin();
if( load_vfile_from_rid(tid) && !forceMissingFlag ){
fossil_fatal("missing content, unable to update");
};
/*
|
| ︙ | ︙ | |||
453 454 455 456 457 458 459 |
** file but keep the edited version around. */
fossil_print("CONFLICT %s - edited locally but deleted by update\n",
zName);
nConflict++;
}else{
fossil_print("REMOVE %s\n", zName);
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
| | > > > > > > > > > > > > | 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 |
** file but keep the edited version around. */
fossil_print("CONFLICT %s - edited locally but deleted by update\n",
zName);
nConflict++;
}else{
fossil_print("REMOVE %s\n", zName);
if( !dryRunFlag && !internalUpdate ) undo_save(zName);
if( !dryRunFlag ){
char *zDir;
file_delete(zFullPath);
zDir = file_dirname(zName);
while( zDir!=0 ){
char *zNext;
db_multi_exec("INSERT OR IGNORE INTO dir_to_delete(name)"
"VALUES(%Q)", zDir);
zNext = db_changes() ? file_dirname(zDir) : 0;
fossil_free(zDir);
zDir = zNext;
}
}
}
}else if( idt>0 && idv>0 && ridt!=ridv && chnged ){
/* Merge the changes in the current tree into the target version */
Blob r, t, v;
int rc;
if( nameChng ){
fossil_print("MERGE %s -> %s\n", zName, zNewName);
|
| ︙ | ︙ | |||
527 528 529 530 531 532 533 |
}
/* Report on conflicts
*/
if( !dryRunFlag ){
Stmt q;
int nMerge = 0;
| < | | 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 |
}
/* Report on conflicts
*/
if( !dryRunFlag ){
Stmt q;
int nMerge = 0;
db_prepare(&q, "SELECT mhash, id FROM vmerge WHERE id<=0");
while( db_step(&q)==SQLITE_ROW ){
const char *zLabel = "merge";
switch( db_column_int(&q, 1) ){
case -1: zLabel = "cherrypick merge"; break;
case -2: zLabel = "backout merge"; break;
}
fossil_warning("uncommitted %s against %S.",
|
| ︙ | ︙ | |||
565 566 567 568 569 570 571 |
/*
** Clean up the mid and pid VFILE entries. Then commit the changes.
*/
if( dryRunFlag ){
db_end_transaction(1); /* With --dry-run, rollback changes */
}else{
| > | > > > > > > > > > | | | 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 |
/*
** Clean up the mid and pid VFILE entries. Then commit the changes.
*/
if( dryRunFlag ){
db_end_transaction(1); /* With --dry-run, rollback changes */
}else{
char *zPwd;
ensure_empty_dirs_created(1);
sqlite3_create_function(g.db, "rmdir", 1, SQLITE_UTF8, 0,
file_rmdir_sql_function, 0, 0);
zPwd = file_getcwd(0,0);
db_multi_exec(
"SELECT rmdir(%Q||name) FROM dir_to_delete"
" WHERE (%Q||name)<>%Q ORDER BY name DESC",
g.zLocalRoot, g.zLocalRoot, zPwd
);
fossil_free(zPwd);
if( g.argc<=3 ){
/* All files updated. Shift the current checkout to the target. */
db_multi_exec("DELETE FROM vfile WHERE vid!=%d", tid);
checkout_set_all_exe(tid);
manifest_to_disk(tid);
db_set_checkout(tid);
}else{
/* A subset of files have been checked out. Keep the current
** checkout unchanged. */
db_multi_exec("DELETE FROM vfile WHERE vid!=%d", vid);
}
if( !internalUpdate ) undo_finish();
if( setmtimeFlag ) vfile_check_signature(tid, CKSIG_SETMTIME);
db_end_transaction(0);
}
}
/*
** Create empty directories specified by the empty-dirs setting.
*/
void ensure_empty_dirs_created(int clearDirTable){
char *zEmptyDirs = db_get("empty-dirs", 0);
if( zEmptyDirs!=0 ){
int i;
Blob dirName;
Blob dirsList;
zEmptyDirs = fossil_strdup(zEmptyDirs);
|
| ︙ | ︙ | |||
612 613 614 615 616 617 618 |
if( file_mkfolder(zPath, RepoFILE, 0, 1)!=0 ) {
fossil_warning("couldn't create directory %s as "
"required by empty-dirs setting", zDir);
}
break;
}
case 1: { /* exists, and is a directory */
| | > > > > > | 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 |
if( file_mkfolder(zPath, RepoFILE, 0, 1)!=0 ) {
fossil_warning("couldn't create directory %s as "
"required by empty-dirs setting", zDir);
}
break;
}
case 1: { /* exists, and is a directory */
/* make sure this directory is not on the delete list */
if( clearDirTable ){
db_multi_exec(
"DELETE FROM dir_to_delete WHERE name=%Q", zDir
);
}
break;
}
case 2: { /* exists, but isn't a directory */
fossil_warning("file %s found, but a directory is required "
"by empty-dirs setting", zDir);
}
}
|
| ︙ | ︙ | |||
863 864 865 866 867 868 869 |
blob_write_to_file(&record, zFull);
}
file_setexe(zFull, rvPerm==PERM_EXE);
fossil_print("REVERT %s\n", zFile);
mtime = file_mtime(zFull, RepoFILE);
db_multi_exec(
"UPDATE vfile"
| | > | 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 |
blob_write_to_file(&record, zFull);
}
file_setexe(zFull, rvPerm==PERM_EXE);
fossil_print("REVERT %s\n", zFile);
mtime = file_mtime(zFull, RepoFILE);
db_multi_exec(
"UPDATE vfile"
" SET mtime=%lld, chnged=%d, deleted=0, isexe=%d, islink=%d,"
" mrid=rid, mhash=NULL"
" WHERE pathname=%Q OR origname=%Q",
mtime, rvChnged, rvPerm==PERM_EXE, rvPerm==PERM_LNK, zFile, zFile
);
}
blob_reset(&record);
free(zFull);
}
|
| ︙ | ︙ |
Changes to src/vfile.c.
| ︙ | ︙ | |||
86 87 88 89 90 91 92 |
db_begin_transaction();
p = manifest_get(vid, CFTYPE_MANIFEST, 0);
if( p==0 ) {
db_end_transaction(1);
return 0;
}
db_prepare(&ins,
| | | | 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
db_begin_transaction();
p = manifest_get(vid, CFTYPE_MANIFEST, 0);
if( p==0 ) {
db_end_transaction(1);
return 0;
}
db_prepare(&ins,
"INSERT INTO vfile(vid,isexe,islink,rid,mrid,pathname,mhash) "
" VALUES(:vid,:isexe,:islink,:id,:id,:name,NULL)");
db_prepare(&ridq, "SELECT rid,size FROM blob WHERE uuid=:uuid");
db_bind_int(&ins, ":vid", vid);
manifest_file_rewind(p);
nMissing = 0;
while( (pFile = manifest_file_next(p,0))!=0 ){
if( pFile->zUuid==0 || uuid_is_shunned(pFile->zUuid) ) continue;
db_bind_text(&ridq, ":uuid", pFile->zUuid);
|
| ︙ | ︙ | |||
240 241 242 243 244 245 246 |
** if --hash is used, check to see if they have been edited by
** looking at their artifact hashes */
const char *zUuid = db_column_text(&q, 5);
int nUuid = db_column_bytes(&q, 5);
assert( origSize==currentSize );
if( !hname_verify_file_hash(zName, zUuid, nUuid) ) chnged = 1;
}
| | | 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
** if --hash is used, check to see if they have been edited by
** looking at their artifact hashes */
const char *zUuid = db_column_text(&q, 5);
int nUuid = db_column_bytes(&q, 5);
assert( origSize==currentSize );
if( !hname_verify_file_hash(zName, zUuid, nUuid) ) chnged = 1;
}
if( (cksigFlags & CKSIG_SETMTIME) && (chnged==0 || chnged==2 || chnged==4)){
i64 desiredMtime;
if( mtime_of_manifest_file(vid,rid,&desiredMtime)==0 ){
if( currentMtime!=desiredMtime ){
file_set_mtime(zName, desiredMtime);
currentMtime = file_mtime(zName, RepoFILE);
}
}
|
| ︙ | ︙ | |||
357 358 359 360 361 362 363 |
blob_reset(&content);
db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
file_mtime(zName, RepoFILE), id);
}
db_finalize(&q);
}
| < < < < < < < < < < < < < < < < < < | 357 358 359 360 361 362 363 364 365 366 367 368 369 370 |
blob_reset(&content);
db_multi_exec("UPDATE vfile SET mtime=%lld WHERE id=%d",
file_mtime(zName, RepoFILE), id);
}
db_finalize(&q);
}
/*
** Check to see if the directory named in zPath is the top of a checkout.
** In other words, check to see if directory pPath contains a file named
** "_FOSSIL_" or ".fslckout". Return true or false.
*/
int vfile_top_of_checkout(const char *zPath){
char *zFile;
|
| ︙ | ︙ | |||
964 965 966 967 968 969 970 |
vfile_aggregate_checksum_repository(vid, &hash);
printf("archive: %s\n", blob_str(&hash));
blob_reset(&hash);
vfile_aggregate_checksum_manifest(vid, &hash, &hash2);
printf("manifest: %s\n", blob_str(&hash));
printf("recorded: %s\n", blob_str(&hash2));
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 |
vfile_aggregate_checksum_repository(vid, &hash);
printf("archive: %s\n", blob_str(&hash));
blob_reset(&hash);
vfile_aggregate_checksum_manifest(vid, &hash, &hash2);
printf("manifest: %s\n", blob_str(&hash));
printf("recorded: %s\n", blob_str(&hash2));
}
/*
** This routine recomputes certain columns of the vfile and vmerge tables
** when the associated repository is swapped out for a clone of the same
** project, and the blob.rid value change. The following columns are
** updated:
**
** vmerge.merge
** vfile.vid
** vfile.rid
** vfile.mrid
**
** Also:
**
** vvar.value WHERE name='checkout'
*/
void vfile_rid_renumbering_event(int dryRun){
int oldVid;
int newVid;
char *zUnresolved;
oldVid = db_lget_int("checkout", 0);
newVid = db_int(0, "SELECT blob.rid FROM blob, vvar"
" WHERE blob.uuid=vvar.value"
" AND vvar.name='checkout-hash'");
/* The idMap table will make old RID values into new ones */
db_multi_exec(
"CREATE TEMP TABLE idMap(oldrid INTEGER PRIMARY KEY, newrid INT);\n"
);
/* Add the RID value for the current check-out */
db_multi_exec(
"INSERT INTO idMap(oldrid, newrid) VALUES(%d,%d)",
oldVid, newVid
);
/* Add the RID values for any other check-ins that have been merged into
** the current check-out. */
db_multi_exec(
"INSERT OR IGNORE INTO idMap(oldrid, newrid)"
" SELECT vmerge.merge, blob.rid FROM vmerge, blob"
" WHERE blob.uuid=vmerge.mhash;"
);
/* Add RID values for files in the current check-out */
db_multi_exec(
"CREATE TEMP TABLE hashoffile(name TEXT PRIMARY KEY, hash TEXT)"
"WITHOUT ROWID;"
"INSERT INTO hashoffile(name,hash)"
" SELECT filename, uuid FROM vvar, files_of_checkin(vvar.value)"
" WHERE vvar.name='checkout-hash';"
"INSERT OR IGNORE INTO idMap(oldrid, newrid)"
" SELECT vfile.rid, blob.rid FROM vfile, hashoffile, blob"
" WHERE hashoffile.name=coalesce(vfile.origname,vfile.pathname)"
" AND blob.uuid=hashoffile.hash;"
);
/* Add RID values for merged-in files */
db_multi_exec(
"INSERT OR IGNORE INTO idMap(oldrid, newrid)"
" SELECT vfile.mrid, blob.rid FROM vfile, blob"
" WHERE blob.uuid=vfile.mhash;"
);
if( dryRun ){
Stmt q;
db_prepare(&q, "SELECT oldrid, newrid, blob.uuid"
" FROM idMap, blob WHERE blob.rid=idMap.newrid");
while( db_step(&q)==SQLITE_ROW ){
fossil_print("%8d -> %8d %.25s\n",
db_column_int(&q,0),
db_column_int(&q,1),
db_column_text(&q,2));
}
db_finalize(&q);
}
/* Verify that all RID values in the VFILE table and VMERGE table have
** been resolved. */
zUnresolved = db_text("",
"WITH allrid(x) AS ("
" SELECT rid FROM vfile"
" UNION SELECT mrid FROM vfile"
" UNION SELECT merge FROM vmerge"
" UNION SELECT %d"
")"
"SELECT group_concat(x,' ') FROM allrid"
" WHERE x NOT IN (SELECT oldrid FROM idMap);",
oldVid
);
if( zUnresolved[0] ){
fossil_fatal("Unresolved RID values: %s\n", zUnresolved);
}
/* Make the changes to the VFILE and VMERGE tables */
if( !dryRun ){
db_multi_exec(
"UPDATE vfile"
" SET rid=(SELECT newrid FROM idMap WHERE oldrid=vfile.rid)"
" WHERE vid=%d AND rid>0;", oldVid);
db_multi_exec(
"UPDATE vfile"
" SET mrid=(SELECT newrid FROM idMap WHERE oldrid=vfile.mrid)"
" WHERE vid=%d AND mrid>0;", oldVid);
db_multi_exec(
"UPDATE vfile"
" SET vid=%d"
" WHERE vid=%d", newVid, oldVid);
db_multi_exec(
"UPDATE vmerge"
" SET merge=(SELECT newrid FROM idMap WHERE oldrid=vmerge.merge);");
db_lset_int("checkout",newVid);
}
/* Clear out the TEMP tables we constructed */
db_multi_exec(
"DROP TABLE idMap;"
"DROP TABLE hashoffile;"
);
}
|
Changes to src/wiki.c.
| ︙ | ︙ | |||
69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
@ Rules for wiki page names:
well_formed_wiki_name_rules();
style_footer();
return 1;
}
return 0;
}
/*
** WEBPAGE: home
** WEBPAGE: index
** WEBPAGE: not_found
**
** The /home, /index, and /not_found pages all redirect to the homepage
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
@ Rules for wiki page names:
well_formed_wiki_name_rules();
style_footer();
return 1;
}
return 0;
}
/*
** Return the tagid associated with a particular wiki page.
*/
int wiki_tagid(const char *zPageName){
return db_int(0, "SELECT tagid FROM tag WHERE tagname='wiki-%q'",zPageName);
}
int wiki_tagid2(const char *zPrefix, const char *zPageName){
return db_int(0, "SELECT tagid FROM tag WHERE tagname='wiki-%q/%q'",
zPrefix, zPageName);
}
/*
** Return the RID of the next or previous version of a wiki page.
** Return 0 if rid is the last/first version.
*/
int wiki_next(int tagid, double mtime){
return db_int(0,
"SELECT srcid FROM tagxref"
" WHERE tagid=%d AND mtime>%.16g"
" ORDER BY mtime ASC LIMIT 1",
tagid, mtime);
}
int wiki_prev(int tagid, double mtime){
return db_int(0,
"SELECT srcid FROM tagxref"
" WHERE tagid=%d AND mtime<%.16g"
" ORDER BY mtime DESC LIMIT 1",
tagid, mtime);
}
/*
** WEBPAGE: home
** WEBPAGE: index
** WEBPAGE: not_found
**
** The /home, /index, and /not_found pages all redirect to the homepage
|
| ︙ | ︙ | |||
260 261 262 263 264 265 266 |
}
if( (ok & W_HELP)!=0 ){
style_submenu_element("Help", "%R/wikihelp");
}
if( (ok & W_NEW)!=0 && g.anon.NewWiki ){
style_submenu_element("New", "%R/wikinew");
}
| < < < < < < < < < < < < < < < | | | | 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 |
}
if( (ok & W_HELP)!=0 ){
style_submenu_element("Help", "%R/wikihelp");
}
if( (ok & W_NEW)!=0 && g.anon.NewWiki ){
style_submenu_element("New", "%R/wikinew");
}
if( (ok & W_SANDBOX)!=0 ){
style_submenu_element("Sandbox", "%R/wiki?name=Sandbox");
}
}
/*
** WEBPAGE: wikihelp
** A generic landing page for wiki.
*/
void wiki_helppage(void){
login_check_credentials();
if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
style_header("Wiki Help");
wiki_standard_submenu(W_ALL_BUT(W_HELP));
@ <h2>Wiki Links</h2>
@ <ul>
@ <li> %z(href("%R/timeline?y=w"))Recent changes</a> to wiki pages.</li>
@ <li> Formatting rules for %z(href("%R/wiki_rules"))Fossil Wiki</a> and for
@ %z(href("%R/md_rules"))Markdown Wiki</a>.</li>
@ <li> Use the %z(href("%R/wiki?name=Sandbox"))Sandbox</a>
@ to experiment.</li>
if( g.perm.NewWiki ){
@ <li> Create a %z(href("%R/wikinew"))new wiki page</a>.</li>
if( g.perm.Write ){
@ <li> Create a %z(href("%R/technoteedit"))new tech-note</a>.</li>
}
}
@ <li> %z(href("%R/wcontent"))List of All Wiki Pages</a>
@ available on this server.</li>
if( g.perm.ModWiki ){
@ <li> %z(href("%R/modreq"))Tend to pending moderation requests</a></li>
}
if( search_restrict(SRCH_WIKI)!=0 ){
@ <li> %z(href("%R/wikisrch"))Search</a> for wiki pages containing key
@ words</li>
}
@ </ul>
|
| ︙ | ︙ | |||
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 |
void wiki_srchpage(void){
login_check_credentials();
style_header("Wiki Search");
wiki_standard_submenu(W_HELP|W_LIST|W_SANDBOX);
search_screen(SRCH_WIKI, 0);
style_footer();
}
/*
** WEBPAGE: wiki
** URL: /wiki?name=PAGENAME
*/
void wiki_page(void){
char *zTag;
int rid = 0;
int isSandbox;
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < | | 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 |
void wiki_srchpage(void){
login_check_credentials();
style_header("Wiki Search");
wiki_standard_submenu(W_HELP|W_LIST|W_SANDBOX);
search_screen(SRCH_WIKI, 0);
style_footer();
}
/* Return values from wiki_page_type() */
#define WIKITYPE_UNKNOWN (-1)
#define WIKITYPE_NORMAL 0
#define WIKITYPE_BRANCH 1
#define WIKITYPE_CHECKIN 2
#define WIKITYPE_TAG 3
/*
** Figure out what type of wiki page we are dealing with.
*/
static int wiki_page_type(const char *zPageName){
if( db_get_boolean("wiki-about",1)==0 ){
return WIKITYPE_NORMAL;
}else
if( sqlite3_strglob("checkin/*", zPageName)==0
&& db_exists("SELECT 1 FROM blob WHERE uuid=%Q",zPageName+8)
){
return WIKITYPE_CHECKIN;
}else
if( sqlite3_strglob("branch/*", zPageName)==0 ){
return WIKITYPE_BRANCH;
}else
if( sqlite3_strglob("tag/*", zPageName)==0 ){
return WIKITYPE_TAG;
}
return WIKITYPE_NORMAL;
}
/*
** Add an appropriate style_header() for either the /wiki or /wikiedit page
** for zPageName.
*/
static int wiki_page_header(
int eType, /* Page type. -1 for unknown */
const char *zPageName, /* Name of the page */
const char *zExtra /* Extra prefix text on the page header */
){
if( eType<0 ) eType = wiki_page_type(zPageName);
switch( eType ){
case WIKITYPE_NORMAL: {
style_header("%s%s", zExtra, zPageName);
break;
}
case WIKITYPE_CHECKIN: {
zPageName += 8;
style_header("Notes About Checkin %S", zPageName);
style_submenu_element("Checkin Timeline","%R/timeline?f=%s", zPageName);
style_submenu_element("Checkin Info","%R/info/%s", zPageName);
break;
}
case WIKITYPE_BRANCH: {
zPageName += 7;
style_header("Notes About Branch %h", zPageName);
style_submenu_element("Branch Timeline","%R/timeline?r=%t", zPageName);
break;
}
case WIKITYPE_TAG: {
zPageName += 4;
style_header("Notes About Tag %h", zPageName);
style_submenu_element("Tag Timeline","%R/timeline?t=%t",zPageName);
break;
}
}
return eType;
}
/*
** Wiki pages with special names "branch/...", "checkin/...", and "tag/..."
** requires perm.Write privilege in addition to perm.WrWiki in order
** to write. This function determines whether the extra perm.Write
** is required and available. Return true if writing to the wiki page
** may proceed, and return false if permission is lacking.
*/
static int wiki_special_permission(const char *zPageName){
if( strncmp(zPageName,"branch/",7)!=0
&& strncmp(zPageName,"checkin/",8)!=0
&& strncmp(zPageName,"tag/",4)!=0
){
return 1;
}
if( db_get_boolean("wiki-about",1)==0 ){
return 1;
}
return g.perm.Write;
}
/*
** WEBPAGE: wiki
** URL: /wiki?name=PAGENAME
*/
void wiki_page(void){
char *zTag;
int rid = 0;
int isSandbox;
unsigned submenuFlags = W_HELP;
Blob wiki;
Manifest *pWiki = 0;
const char *zPageName;
const char *zMimetype = 0;
char *zBody = mprintf("%s","<i>Empty Page</i>");
login_check_credentials();
|
| ︙ | ︙ | |||
383 384 385 386 387 388 389 |
if( pWiki ){
zBody = pWiki->zWiki;
zMimetype = pWiki->zMimetype;
}
}
zMimetype = wiki_filter_mimetypes(zMimetype);
if( !g.isHome ){
| < < < < < | > > | | | < | | < < < < < < | < | > > > | | | > | 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 |
if( pWiki ){
zBody = pWiki->zWiki;
zMimetype = pWiki->zMimetype;
}
}
zMimetype = wiki_filter_mimetypes(zMimetype);
if( !g.isHome ){
if( ((rid && g.perm.WrWiki) || (!rid && g.perm.NewWiki))
&& wiki_special_permission(zPageName)
){
if( db_get_boolean("wysiwyg-wiki", 0) ){
style_submenu_element("Edit", "%R/wikiedit?name=%T&wysiwyg=1",
zPageName);
}else{
style_submenu_element("Edit", "%R/wikiedit?name=%T", zPageName);
}
}else if( rid && g.perm.ApndWiki ){
style_submenu_element("Edit", "%R/wikiappend?name=%T", zPageName);
}
if( g.perm.Hyperlink ){
style_submenu_element("History", "%R/whistory?name=%T", zPageName);
}
}
style_set_current_page("%T?name=%T", g.zPath, zPageName);
wiki_page_header(WIKITYPE_UNKNOWN, zPageName, "");
wiki_standard_submenu(submenuFlags);
if( zBody[0]==0 ){
@ <i>This page has been deleted</i>
}else{
blob_init(&wiki, zBody, -1);
wiki_render_by_mimetype(&wiki, zMimetype);
blob_reset(&wiki);
}
attachment_list(zPageName, "<hr /><h2>Attachments:</h2><ul>");
manifest_destroy(pWiki);
style_footer();
}
/*
** Write a wiki artifact into the repository
|
| ︙ | ︙ | |||
489 490 491 492 493 494 495 496 497 498 499 500 501 502 |
const char *zPageName;
int n;
const char *z;
char *zBody = (char*)P("w");
const char *zMimetype = wiki_filter_mimetypes(P("mimetype"));
int isWysiwyg = P("wysiwyg")!=0;
int goodCaptcha = 1;
if( P("edit-wysiwyg")!=0 ){ isWysiwyg = 1; zBody = 0; }
if( P("edit-markup")!=0 ){ isWysiwyg = 0; zBody = 0; }
if( zBody ){
if( isWysiwyg ){
Blob body;
blob_zero(&body);
| > > | 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 |
const char *zPageName;
int n;
const char *z;
char *zBody = (char*)P("w");
const char *zMimetype = wiki_filter_mimetypes(P("mimetype"));
int isWysiwyg = P("wysiwyg")!=0;
int goodCaptcha = 1;
int eType = WIKITYPE_UNKNOWN;
int havePreview = 0;
if( P("edit-wysiwyg")!=0 ){ isWysiwyg = 1; zBody = 0; }
if( P("edit-markup")!=0 ){ isWysiwyg = 0; zBody = 0; }
if( zBody ){
if( isWysiwyg ){
Blob body;
blob_zero(&body);
|
| ︙ | ︙ | |||
523 524 525 526 527 528 529 530 531 532 533 534 535 536 |
zTag = mprintf("wiki-%s", zPageName);
rid = db_int(0,
"SELECT rid FROM tagxref"
" WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
" ORDER BY mtime DESC", zTag
);
free(zTag);
if( (rid && !g.perm.WrWiki) || (!rid && !g.perm.NewWiki) ){
login_needed(rid ? g.anon.WrWiki : g.anon.NewWiki);
return;
}
if( zBody==0 && (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))!=0 ){
zBody = pWiki->zWiki;
zMimetype = pWiki->zMimetype;
| > > > > | 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 |
zTag = mprintf("wiki-%s", zPageName);
rid = db_int(0,
"SELECT rid FROM tagxref"
" WHERE tagid=(SELECT tagid FROM tag WHERE tagname=%Q)"
" ORDER BY mtime DESC", zTag
);
free(zTag);
if( !wiki_special_permission(zPageName) ){
login_needed(0);
return;
}
if( (rid && !g.perm.WrWiki) || (!rid && !g.perm.NewWiki) ){
login_needed(rid ? g.anon.WrWiki : g.anon.NewWiki);
return;
}
if( zBody==0 && (pWiki = manifest_get(rid, CFTYPE_WIKI, 0))!=0 ){
zBody = pWiki->zWiki;
zMimetype = pWiki->zMimetype;
|
| ︙ | ︙ | |||
573 574 575 576 577 578 579 |
cgi_redirectf("wiki?name=%T", zPageName);
}
if( P("cancel")!=0 ){
cgi_redirectf("wiki?name=%T", zPageName);
return;
}
if( zBody==0 ){
| | > > > > > | > > > > > | | | | > > > > > > > > > > > > > > > > > > > > | | > > > > > | > > > > | 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 |
cgi_redirectf("wiki?name=%T", zPageName);
}
if( P("cancel")!=0 ){
cgi_redirectf("wiki?name=%T", zPageName);
return;
}
if( zBody==0 ){
zBody = mprintf("");
}
style_set_current_page("%T?name=%T", g.zPath, zPageName);
eType = wiki_page_header(WIKITYPE_UNKNOWN, zPageName, "Edit: ");
if( rid && !isSandbox && g.perm.ApndWiki ){
if( g.perm.Attach ){
style_submenu_element("Attach",
"%s/attachadd?page=%T&from=%s/wiki%%3fname=%T",
g.zTop, zPageName, g.zTop, zPageName);
}
}
if( !goodCaptcha ){
@ <p class="generalError">Error: Incorrect security code.</p>
}
blob_zero(&wiki);
while( fossil_isspace(zBody[0]) ) zBody++;
blob_append(&wiki, zBody, -1);
if( P("preview")!=0 ){
havePreview = 1;
if( zBody[0] ){
@ Preview:<hr />
wiki_render_by_mimetype(&wiki, zMimetype);
@ <hr />
blob_reset(&wiki);
}
}
for(n=2, z=zBody; z[0]; z++){
if( z[0]=='\n' ) n++;
}
if( n<20 ) n = 20;
if( n>30 ) n = 30;
if( !isWysiwyg ){
/* Traditional markup-only editing */
char *zPlaceholder = 0;
switch( eType ){
case WIKITYPE_NORMAL: {
zPlaceholder = mprintf("Enter text for wiki page %s", zPageName);
break;
}
case WIKITYPE_BRANCH: {
zPlaceholder = mprintf("Enter notes about branch %s", zPageName+7);
break;
}
case WIKITYPE_CHECKIN: {
zPlaceholder = mprintf("Enter notes about check-in %.20s", zPageName+8);
break;
}
case WIKITYPE_TAG: {
zPlaceholder = mprintf("Enter notes about tag %s", zPageName+4);
break;
}
}
form_begin(0, "%R/wikiedit");
@ <div>Markup style:
mimetype_option_menu(zMimetype);
@ <br /><textarea name="w" class="wikiedit" cols="80" \
@ rows="%d(n)" wrap="virtual" placeholder="%h(zPlaceholder)">\
@ %h(zBody)</textarea>
@ <br />
fossil_free(zPlaceholder);
if( db_get_boolean("wysiwyg-wiki", 0) ){
@ <input type="submit" name="edit-wysiwyg" value="Wysiwyg Editor"
@ onclick='return confirm("Switching to WYSIWYG-mode\nwill erase your markup\nedits. Continue?")' />
}
@ <input type="submit" name="preview" value="Preview Your Changes" />
}else{
/* Wysiwyg editing */
Blob html, temp;
havePreview = 1;
form_begin("", "%R/wikiedit");
@ <div>
@ <input type="hidden" name="wysiwyg" value="1" />
blob_zero(&temp);
wiki_convert(&wiki, &temp, 0);
blob_zero(&html);
htmlTidy(blob_str(&temp), &html);
blob_reset(&temp);
wysiwygEditor("w", blob_str(&html), 60, n);
blob_reset(&html);
@ <br />
@ <input type="submit" name="edit-markup" value="Markup Editor"
@ onclick='return confirm("Switching to markup-mode\nwill erase your WYSIWYG\nedits. Continue?")' />
}
login_insert_csrf_secret();
if( havePreview ){
if( isWysiwyg || zBody[0] ){
@ <input type="submit" name="submit" value="Apply These Changes" />
}else{
@ <input type="submit" name="submit" value="Delete This Wiki Page" />
}
}
@ <input type="hidden" name="name" value="%h(zPageName)" />
@ <input type="submit" name="cancel" value="Cancel"
@ onclick='confirm("Abandon your changes?")' />
@ </div>
captcha_generate(0);
@ </form>
manifest_destroy(pWiki);
|
| ︙ | ︙ | |||
841 842 843 844 845 846 847 | @ <input type="submit" name="submit" value="Append Your Changes" /> @ <input type="submit" name="cancel" value="Cancel" /> captcha_generate(0); @ </form> style_footer(); } | < < < < < < < < < < < < < < < > > > > > > | | | < > > > > | | > > | | > | > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > < | > > > > > > > > > | | > < | > | | < < | < < < < < < | < > < > > > > > > > | > > > > > > > > > > > > > > > > > | | | > > > < > | < | | > | > > > | > > | > > | < > | > > > > > > | < > > > > > > > > | > | > > > > > | > > > > > > > > | | > | > > > > > > | > > > > < > | 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 |
@ <input type="submit" name="submit" value="Append Your Changes" />
@ <input type="submit" name="cancel" value="Cancel" />
captcha_generate(0);
@ </form>
style_footer();
}
/*
** WEBPAGE: whistory
** URL: /whistory?name=PAGENAME
**
** Additional parameters:
**
** showid Show RID values
**
** Show the complete change history for a single wiki page.
*/
void whistory_page(void){
Stmt q;
const char *zPageName;
double rNow;
int showRid;
login_check_credentials();
if( !g.perm.Hyperlink ){ login_needed(g.anon.Hyperlink); return; }
zPageName = PD("name","");
style_header("History Of %s", zPageName);
showRid = P("showid")!=0;
db_prepare(&q,
"SELECT"
" event.mtime,"
" blob.uuid,"
" coalesce(event.euser,event.user),"
" event.objid"
" FROM event, blob, tag, tagxref"
" WHERE event.type='w' AND blob.rid=event.objid"
" AND tag.tagname='wiki-%q'"
" AND tagxref.tagid=tag.tagid AND tagxref.srcid=event.objid"
" ORDER BY event.mtime DESC",
zPageName
);
@ <h2>History of <a href="%R/wiki?name=%T(zPageName)">%h(zPageName)</a></h2>
@ <div class="brlist">
@ <table>
@ <thead><tr>
@ <th>Age</th>
@ <th>Hash</th>
@ <th>User</th>
if( showRid ){
@ <th>RID</th>
}
@ <th> </th>
@ </tr></thead><tbody>
rNow = db_double(0.0, "SELECT julianday('now')");
while( db_step(&q)==SQLITE_ROW ){
double rMtime = db_column_double(&q, 0);
const char *zUuid = db_column_text(&q, 1);
const char *zUser = db_column_text(&q, 2);
int wrid = db_column_int(&q, 3);
/* sqlite3_int64 iMtime = (sqlite3_int64)(rMtime*86400.0); */
char *zAge = human_readable_age(rNow - rMtime);
@ <tr>
/* @ <td data-sortkey="%016llx(iMtime)">%s(zAge)</td> */
@ <td>%s(zAge)</td>
fossil_free(zAge);
@ <td>%z(href("%R/info/%s",zUuid))%S(zUuid)</a></td>
@ <td>%h(zUser)</td>
if( showRid ){
@ <td>%z(href("%R/artifact/%S",zUuid))%d(wrid)</a></td>
}
@ <td>%z(href("%R/wdiff?id=%S",zUuid))diff</a></td>
@ </tr>
}
@ </tbody></table></div>
db_finalize(&q);
/* style_table_sorter(); */
style_footer();
}
/*
** WEBPAGE: wdiff
**
** Show the changes to a wiki page.
**
** Query parameters:
**
** id=HASH Hash prefix for the child version to be diffed.
** rid=INTEGER RecordID for the child version
** pid=HASH Hash prefix for the parent.
**
** The "id" query parameter is required. "pid" is optional. If "pid"
** is omitted, then the diff is against the first parent of the child.
*/
void wdiff_page(void){
const char *zId;
const char *zPid;
Manifest *pW1, *pW2 = 0;
int rid1, rid2, nextRid;
Blob w1, w2, d;
u64 diffFlags;
login_check_credentials();
if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
zId = P("id");
if( zId==0 ){
rid1 = atoi(PD("rid","0"));
}else{
rid1 = name_to_typed_rid(zId, "w");
}
zId = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid1);
pW1 = manifest_get(rid1, CFTYPE_WIKI, 0);
if( pW1==0 ) fossil_redirect_home();
blob_init(&w1, pW1->zWiki, -1);
zPid = P("pid");
if( zPid==0 && pW1->nParent ){
zPid = pW1->azParent[0];
}
if( zPid ){
char *zDate;
rid2 = name_to_typed_rid(zPid, "w");
pW2 = manifest_get(rid2, CFTYPE_WIKI, 0);
blob_init(&w2, pW2->zWiki, -1);
@ <h2>Changes to \
@ "%z(href("%R/whistory?name=%s",pW1->zWikiTitle))%h(pW1->zWikiTitle)</a>" \
zDate = db_text(0, "SELECT datetime(%.16g)",pW2->rDate);
@ between %z(href("%R/info/%s",zPid))%z(zDate)</a> \
zDate = db_text(0, "SELECT datetime(%.16g)",pW1->rDate);
@ and %z(href("%R/info/%s",zId))%z(zDate)</a></h2>
style_submenu_element("Previous", "%R/wdiff?id=%S", zPid);
}else{
blob_zero(&w2);
@ <h2>Initial version of \
@ "%z(href("%R/whistory?name=%s",pW1->zWikiTitle))%h(pW1->zWikiTitle)</a>"\
@ </h2>
}
nextRid = wiki_next(wiki_tagid(pW1->zWikiTitle),pW1->rDate);
if( nextRid ){
style_submenu_element("Next", "%R/wdiff?rid=%d", nextRid);
}
style_header("Changes To %s", pW1->zWikiTitle);
blob_zero(&d);
diffFlags = construct_diff_flags(1);
text_diff(&w2, &w1, &d, 0, diffFlags | DIFF_HTML | DIFF_LINENO);
@ <pre class="udiff">
@ %s(blob_str(&d))
@ <pre>
manifest_destroy(pW1);
manifest_destroy(pW2);
style_footer();
}
/*
** A query that returns information about all wiki pages.
**
** wname Name of the wiki page
** wsort Sort names by this label
** wrid rid of the most recent version of the page
** wmtime time most recent version was created
** wcnt Number of versions of this wiki page
**
** The wrid value is zero for deleted wiki pages.
*/
static const char listAllWikiPages[] =
@ SELECT
@ substr(tag.tagname, 6) AS wname,
@ lower(substr(tag.tagname, 6)) AS sortname,
@ tagxref.value+0 AS wrid,
@ max(tagxref.mtime) AS wmtime,
@ count(*) AS wcnt
@ FROM
@ tag,
@ tagxref
@ WHERE
@ tag.tagname GLOB 'wiki-*'
@ AND tagxref.tagid=tag.tagid
@ GROUP BY 1
@ ORDER BY 2;
;
/*
** WEBPAGE: wcontent
**
** all=1 Show deleted pages
** showid Show rid values for each page.
**
** List all available wiki pages with date created and last modified.
*/
void wcontent_page(void){
Stmt q;
double rNow;
int showAll = P("all")!=0;
int showRid = P("showid")!=0;
login_check_credentials();
if( !g.perm.RdWiki ){ login_needed(g.anon.RdWiki); return; }
style_header("Available Wiki Pages");
if( showAll ){
style_submenu_element("Active", "%s/wcontent", g.zTop);
}else{
style_submenu_element("All", "%s/wcontent?all=1", g.zTop);
}
wiki_standard_submenu(W_ALL_BUT(W_LIST));
db_prepare(&q, listAllWikiPages/*works-like:""*/);
@ <div class="brlist">
@ <table class='sortable' data-column-types='tKN' data-init-sort='1'>
@ <thead><tr>
@ <th>Name</th>
@ <th>Last Change</th>
@ <th>Versions</th>
if( showRid ){
@ <th>RID</th>
}
@ </tr></thead><tbody>
rNow = db_double(0.0, "SELECT julianday('now')");
while( db_step(&q)==SQLITE_ROW ){
const char *zWName = db_column_text(&q, 0);
const char *zSort = db_column_text(&q, 1);
int wrid = db_column_int(&q, 2);
double rWmtime = db_column_double(&q, 3);
sqlite3_int64 iMtime = (sqlite3_int64)(rWmtime*86400.0);
char *zAge;
int wcnt = db_column_int(&q, 4);
char *zWDisplayName;
if( sqlite3_strglob("checkin/*", zWName)==0 ){
zWDisplayName = mprintf("%.25s...", zWName);
}else{
zWDisplayName = mprintf("%s", zWName);
}
if( wrid==0 ){
if( !showAll ) continue;
@ <tr><td data-sortkey="%h(zSort)">\
@ %z(href("%R/whistory?name=%T",zWName))<s>%h(zWDisplayName)</s></a></td>
}else{
@ <tr><td data=sortkey='%h(zSort)">\
@ %z(href("%R/wiki?name=%T",zWName))%h(zWDisplayName)</a></td>
}
zAge = human_readable_age(rNow - rWmtime);
@ <td data-sortkey="%016llx(iMtime)">%s(zAge)</td>
fossil_free(zAge);
@ <td>%z(href("%R/whistory?name=%T",zWName))%d(wcnt)</a></td>
if( showRid ){
@ <td>%d(wrid)</td>
}
@ </tr>
fossil_free(zWDisplayName);
}
@ </tbody></table></div>
db_finalize(&q);
style_table_sorter();
style_footer();
}
/*
** WEBPAGE: wfind
**
** URL: /wfind?title=TITLE
|
| ︙ | ︙ | |||
1378 1379 1380 1381 1382 1383 1384 |
verify_all_options();
if( g.argc!=3 ) usage("FILE");
blob_zero(&out);
blob_read_from_file(&in, g.argv[2], ExtFILE);
markdown_to_html(&in, 0, &out);
blob_write_to_file(&out, "-");
}
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 |
verify_all_options();
if( g.argc!=3 ) usage("FILE");
blob_zero(&out);
blob_read_from_file(&in, g.argv[2], ExtFILE);
markdown_to_html(&in, 0, &out);
blob_write_to_file(&out, "-");
}
/*
** Allowed flags for wiki_render_associated
*/
#if INTERFACE
#define WIKIASSOC_FULL_TITLE 0x00001 /* Full title */
#define WIKIASSOC_MENU_READ 0x00002 /* Add submenu link to read wiki */
#define WIKIASSOC_MENU_WRITE 0x00004 /* Add submenu link to add wiki */
#define WIKIASSOC_ALL 0x00007 /* All of the above */
#endif
/*
** Show the default Section label for an associated wiki page.
*/
static void wiki_section_label(
const char *zPrefix, /* "branch", "tag", or "checkin" */
const char *zName, /* Name of the object */
unsigned int mFlags /* Zero or more WIKIASSOC_* flags */
){
if( (mFlags & WIKIASSOC_FULL_TITLE)==0 ){
@ <div class="section">About</div>
}else if( zPrefix[0]=='c' ){ /* checkin/... */
@ <div class="section">About checkin %.20h(zName)</div>
}else{
@ <div class="section">About %s(zPrefix) %h(zName)</div>
}
}
/*
** Add an "Wiki" button in a submenu that links to the read-wiki page.
*/
static void wiki_submenu_to_read_wiki(
const char *zPrefix, /* "branch", "tag", or "checkin" */
const char *zName, /* Name of the object */
unsigned int mFlags /* Zero or more WIKIASSOC_* flags */
){
if( g.perm.RdWiki && (mFlags & WIKIASSOC_MENU_READ)!=0 ){
style_submenu_element("Wiki", "%R/wiki?name=%s/%t", zPrefix, zName);
}
}
/*
** Check to see if there exists a wiki page with a name zPrefix/zName.
** If there is, then render a <div class='section'>..</div> and
** return true.
**
** If there is no such wiki page, return false.
*/
int wiki_render_associated(
const char *zPrefix, /* "branch", "tag", or "checkin" */
const char *zName, /* Name of the object */
unsigned int mFlags /* Zero or more WIKIASSOC_* flags */
){
int rid;
Manifest *pWiki;
if( !db_get_boolean("wiki-about",1) ) return 0;
rid = db_int(0,
"SELECT rid FROM tagxref"
" WHERE tagid=(SELECT tagid FROM tag WHERE tagname='wiki-%q/%q')"
" ORDER BY mtime DESC LIMIT 1",
zPrefix, zName
);
if( rid==0 ){
if( g.perm.WrWiki && g.perm.Write && (mFlags & WIKIASSOC_MENU_WRITE)!=0 ){
style_submenu_element("Add Wiki", "%R/wikiedit?name=%s/%t",
zPrefix, zName);
}
}
pWiki = manifest_get(rid, CFTYPE_WIKI, 0);
if( pWiki==0 ) return 0;
if( fossil_strcmp(pWiki->zMimetype, "text/x-markdown")==0 ){
Blob tail = BLOB_INITIALIZER;
Blob title = BLOB_INITIALIZER;
Blob markdown;
blob_init(&markdown, pWiki->zWiki, -1);
markdown_to_html(&markdown, &title, &tail);
if( blob_size(&title) ){
@ <div class="section">%h(blob_str(&title))</div>
}else{
wiki_section_label(zPrefix, zName, mFlags);
}
wiki_submenu_to_read_wiki(zPrefix, zName, mFlags);
convert_href_and_output(&tail);
blob_reset(&tail);
blob_reset(&title);
blob_reset(&markdown);
}else if( fossil_strcmp(pWiki->zMimetype, "text/plain")==0 ){
wiki_section_label(zPrefix, zName, mFlags);
wiki_submenu_to_read_wiki(zPrefix, zName, mFlags);
@ <pre>
@ %h(pWiki->zWiki)
@ </pre>
}else{
Blob tail = BLOB_INITIALIZER;
Blob title = BLOB_INITIALIZER;
Blob wiki;
Blob *pBody;
blob_init(&wiki, pWiki->zWiki, -1);
if( wiki_find_title(&wiki, &title, &tail) ){
@ <div class="section">%h(blob_str(&title))</div>
pBody = &tail;
}else{
wiki_section_label(zPrefix, zName, mFlags);
pBody = &wiki;
}
wiki_submenu_to_read_wiki(zPrefix, zName, mFlags);
@ <div class="wiki">
wiki_convert(pBody, 0, WIKI_BUTTONS);
@ </div>
blob_reset(&tail);
blob_reset(&title);
blob_reset(&wiki);
}
manifest_destroy(pWiki);
return 1;
}
|
Changes to src/wikiformat.c.
| ︙ | ︙ | |||
27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
*/
#define WIKI_HTMLONLY 0x001 /* HTML markup only. No wiki */
#define WIKI_INLINE 0x002 /* Do not surround with <p>..</p> */
#define WIKI_NOBLOCK 0x004 /* No block markup of any kind */
#define WIKI_BUTTONS 0x008 /* Allow sub-menu buttons */
#define WIKI_NOBADLINKS 0x010 /* Ignore broken hyperlinks */
#define WIKI_LINKSONLY 0x020 /* No markup. Only decorate links */
#endif
/*
** These are the only markup attributes allowed.
*/
enum allowed_attr_t {
| > | 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
*/
#define WIKI_HTMLONLY 0x001 /* HTML markup only. No wiki */
#define WIKI_INLINE 0x002 /* Do not surround with <p>..</p> */
#define WIKI_NOBLOCK 0x004 /* No block markup of any kind */
#define WIKI_BUTTONS 0x008 /* Allow sub-menu buttons */
#define WIKI_NOBADLINKS 0x010 /* Ignore broken hyperlinks */
#define WIKI_LINKSONLY 0x020 /* No markup. Only decorate links */
#define WIKI_NEWLINE 0x040 /* Honor \n - break lines at each \n */
#endif
/*
** These are the only markup attributes allowed.
*/
enum allowed_attr_t {
|
| ︙ | ︙ | |||
679 680 681 682 683 684 685 |
}
if( (p->state & ALLOW_WIKI)!=0 ){
if( z[0]=='\n' ){
n = paragraphBreakLength(z);
if( n>0 ){
*pTokenType = TOKEN_PARAGRAPH;
return n;
| | | 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 |
}
if( (p->state & ALLOW_WIKI)!=0 ){
if( z[0]=='\n' ){
n = paragraphBreakLength(z);
if( n>0 ){
*pTokenType = TOKEN_PARAGRAPH;
return n;
}else{
*pTokenType = TOKEN_NEWLINE;
return 1;
}
}
if( (p->state & AT_NEWLINE)!=0 && fossil_isspace(z[0]) ){
n = listItemLength(z, '*');
if( n>0 ){
|
| ︙ | ︙ | |||
1105 1106 1107 1108 1109 1110 1111 |
** is not the UUID of a ticket, return false.
*/
static int is_ticket(
const char *zTarget, /* Ticket UUID */
int *pClosed /* True if the ticket is closed */
){
static Stmt q;
| < | < | 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 |
** is not the UUID of a ticket, return false.
*/
static int is_ticket(
const char *zTarget, /* Ticket UUID */
int *pClosed /* True if the ticket is closed */
){
static Stmt q;
int n;
int rc;
char zLower[HNAME_MAX+1];
char zUpper[HNAME_MAX+1];
n = strlen(zTarget);
memcpy(zLower, zTarget, n+1);
canonical16(zLower, n+1);
memcpy(zUpper, zLower, n+1);
zUpper[n-1]++;
if( !db_static_stmt_is_init(&q) ){
const char *zClosedExpr = db_get("ticket-closed-expr", "status='Closed'");
db_static_prepare(&q,
"SELECT %s FROM ticket "
" WHERE tkt_uuid>=:lwr AND tkt_uuid<:upr",
zClosedExpr /*safe-for-%s*/
);
}
db_bind_text(&q, ":lwr", zLower);
db_bind_text(&q, ":upr", zUpper);
if( db_step(&q)==SQLITE_ROW ){
rc = 1;
*pClosed = db_column_int(&q, 0);
}else{
|
| ︙ | ︙ | |||
1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 |
" AND (SELECT value FROM tagxref WHERE tagid=tag.tagid"
" ORDER BY mtime DESC LIMIT 1) > 0", zTarget))
){
return zTarget;
}
return 0;
}
/*
** Resolve a hyperlink. The zTarget argument is the content of the [...]
** in the wiki. Append to the output string whatever text is appropriate
** for opening the hyperlink. Write into zClose[0...nClose-1] text that will
** close the markup.
**
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 |
" AND (SELECT value FROM tagxref WHERE tagid=tag.tagid"
" ORDER BY mtime DESC LIMIT 1) > 0", zTarget))
){
return zTarget;
}
return 0;
}
static const char *wikiOverrideHash = 0;
/*
** Fossil-wiki hyperlinks to wiki pages should be overridden to the
** hash value supplied. If the value is NULL, then override is cancelled
** and all overwrites operate normally.
*/
void wiki_hyperlink_override(const char *zUuid){
wikiOverrideHash = zUuid;
}
/*
** If links to wiki page zTarget should be redirected to some historical
** version of that page, then return the hash of the historical version.
** If no override is required, return NULL.
*/
static const char *wiki_is_overridden(const char *zTarget){
if( wikiOverrideHash==0 ) return 0;
/* The override should only happen if the override version is not the
** latest version of the wiki page. */
if( !db_exists(
"SELECT 1 FROM tag, blob, tagxref AS xA, tagxref AS xB "
" WHERE tag.tagname GLOB 'wiki-%q*'"
" AND blob.uuid GLOB '%q'"
" AND xA.tagid=tag.tagid AND xA.rid=blob.rid"
" AND xB.tagid=tag.tagid AND xB.mtime>xA.mtime",
zTarget, wikiOverrideHash
) ){
return 0;
}
return wikiOverrideHash;
}
/*
** Resolve a hyperlink. The zTarget argument is the content of the [...]
** in the wiki. Append to the output string whatever text is appropriate
** for opening the hyperlink. Write into zClose[0...nClose-1] text that will
** close the markup.
**
|
| ︙ | ︙ | |||
1257 1258 1259 1260 1261 1262 1263 |
}else{
zTerm = "";
}
}else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
&& db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){
blob_appendf(p->pOut, "<a href=\"%R/timeline?c=%T\">", zTarget);
}else if( (z = validWikiPageName(p, zTarget))!=0 ){
| > > > > | > | 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 |
}else{
zTerm = "";
}
}else if( strlen(zTarget)>=10 && fossil_isdigit(zTarget[0]) && zTarget[4]=='-'
&& db_int(0, "SELECT datetime(%Q) NOT NULL", zTarget) ){
blob_appendf(p->pOut, "<a href=\"%R/timeline?c=%T\">", zTarget);
}else if( (z = validWikiPageName(p, zTarget))!=0 ){
const char *zOverride = wiki_is_overridden(zTarget);
if( zOverride ){
blob_appendf(p->pOut, "<a href=\"%R/info/%S\">", zOverride);
}else{
blob_appendf(p->pOut, "<a href=\"%R/wiki?name=%T\">", z);
}
}else if( zTarget>=&zOrig[2] && !fossil_isspace(zTarget[-2]) ){
/* Probably an array subscript in code */
zTerm = "";
}else if( (p->state & (WIKI_NOBADLINKS|WIKI_LINKSONLY))!=0 ){
zTerm = "";
}else{
blob_appendf(p->pOut, "<span class=\"brokenlink\">[%h]", zTarget);
|
| ︙ | ︙ | |||
1338 1339 1340 1341 1342 1343 1344 |
blob_append(p->pOut, "\n\n", 1);
p->wantAutoParagraph = 1;
}
p->state |= AT_PARAGRAPH|AT_NEWLINE;
break;
}
case TOKEN_NEWLINE: {
| > > > | > | 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 |
blob_append(p->pOut, "\n\n", 1);
p->wantAutoParagraph = 1;
}
p->state |= AT_PARAGRAPH|AT_NEWLINE;
break;
}
case TOKEN_NEWLINE: {
if( p->renderFlags & WIKI_NEWLINE ){
blob_append(p->pOut, "<br>\n", 5);
}else{
blob_append(p->pOut, "\n", 1);
}
p->state |= AT_NEWLINE;
break;
}
case TOKEN_BUL_LI: {
if( inlineOnly ){
blob_append(p->pOut, " • ", -1);
}else{
|
| ︙ | ︙ |
Changes to src/xfer.c.
| ︙ | ︙ | |||
970 971 972 973 974 975 976 |
" AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
" AND blob.rid<=%d"
" ORDER BY blob.rid DESC",
pXfer->resync
);
}else{
db_prepare(&q,
| | | 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 |
" AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
" AND blob.rid<=%d"
" ORDER BY blob.rid DESC",
pXfer->resync
);
}else{
db_prepare(&q,
"SELECT uuid FROM unclustered JOIN blob USING(rid) /*scan*/"
" WHERE NOT EXISTS(SELECT 1 FROM shun WHERE uuid=blob.uuid)"
" AND NOT EXISTS(SELECT 1 FROM phantom WHERE rid=blob.rid)"
" AND NOT EXISTS(SELECT 1 FROM private WHERE rid=blob.rid)"
);
}
while( db_step(&q)==SQLITE_ROW ){
blob_appendf(pXfer->pOut, "igot %s\n", db_column_text(&q, 0));
|
| ︙ | ︙ | |||
1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 |
#define SYNC_VERBOSE 0x0010 /* Extra diagnostics */
#define SYNC_RESYNC 0x0020 /* --verily */
#define SYNC_UNVERSIONED 0x0040 /* Sync unversioned content */
#define SYNC_UV_REVERT 0x0080 /* Copy server unversioned to client */
#define SYNC_FROMPARENT 0x0100 /* Pull from the parent project */
#define SYNC_UV_TRACE 0x0200 /* Describe UV activities */
#define SYNC_UV_DRYRUN 0x0400 /* Do not actually exchange files */
#endif
/*
** Floating-point absolute value
*/
static double fossil_fabs(double x){
return x>0.0 ? x : -x;
| > | 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 |
#define SYNC_VERBOSE 0x0010 /* Extra diagnostics */
#define SYNC_RESYNC 0x0020 /* --verily */
#define SYNC_UNVERSIONED 0x0040 /* Sync unversioned content */
#define SYNC_UV_REVERT 0x0080 /* Copy server unversioned to client */
#define SYNC_FROMPARENT 0x0100 /* Pull from the parent project */
#define SYNC_UV_TRACE 0x0200 /* Describe UV activities */
#define SYNC_UV_DRYRUN 0x0400 /* Do not actually exchange files */
#define SYNC_IFABLE 0x0800 /* Inability to sync is not fatal */
#endif
/*
** Floating-point absolute value
*/
static double fossil_fabs(double x){
return x>0.0 ? x : -x;
|
| ︙ | ︙ | |||
1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 |
const char *zOpType = 0;/* Push, Pull, Sync, Clone */
double rSkew = 0.0; /* Maximum time skew */
int uvHashSent = 0; /* The "pragma uv-hash" message has been sent */
int uvDoPush = 0; /* Generate uvfile messages to send to server */
int nUvGimmeSent = 0; /* Number of uvgimme cards sent on this cycle */
int nUvFileRcvd = 0; /* Number of uvfile cards received on this cycle */
sqlite3_int64 mtime; /* Modification time on a UV file */
if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH;
if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE|SYNC_UNVERSIONED))==0
&& configRcvMask==0 && configSendMask==0 ) return 0;
if( syncFlags & SYNC_FROMPARENT ){
configRcvMask = 0;
configSendMask = 0;
| > | 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 |
const char *zOpType = 0;/* Push, Pull, Sync, Clone */
double rSkew = 0.0; /* Maximum time skew */
int uvHashSent = 0; /* The "pragma uv-hash" message has been sent */
int uvDoPush = 0; /* Generate uvfile messages to send to server */
int nUvGimmeSent = 0; /* Number of uvgimme cards sent on this cycle */
int nUvFileRcvd = 0; /* Number of uvfile cards received on this cycle */
sqlite3_int64 mtime; /* Modification time on a UV file */
int autopushFailed = 0; /* Autopush following commit failed if true */
if( db_get_boolean("dont-push", 0) ) syncFlags &= ~SYNC_PUSH;
if( (syncFlags & (SYNC_PUSH|SYNC_PULL|SYNC_CLONE|SYNC_UNVERSIONED))==0
&& configRcvMask==0 && configSendMask==0 ) return 0;
if( syncFlags & SYNC_FROMPARENT ){
configRcvMask = 0;
configSendMask = 0;
|
| ︙ | ︙ | |||
2279 2280 2281 2282 2283 2284 2285 2286 2287 |
**
** Except, when cloning we will sometimes get an error on the
** first message exchange because the project-code is unknown
** and so the login card on the request was invalid. The project-code
** is returned in the reply before the error card, so second and
** subsequent messages should be OK. Nevertheless, we need to ignore
** the error card on the first message of a clone.
*/
if( blob_eq(&xfer.aToken[0],"error") && xfer.nToken==2 ){
| > > > < | | > > > > > < < < < < < < < < < < < < < | | < | 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 |
**
** Except, when cloning we will sometimes get an error on the
** first message exchange because the project-code is unknown
** and so the login card on the request was invalid. The project-code
** is returned in the reply before the error card, so second and
** subsequent messages should be OK. Nevertheless, we need to ignore
** the error card on the first message of a clone.
**
** Also ignore "not authorized to write" errors if this is an
** autopush following a commit.
*/
if( blob_eq(&xfer.aToken[0],"error") && xfer.nToken==2 ){
char *zMsg = blob_terminate(&xfer.aToken[1]);
defossilize(zMsg);
if( (syncFlags & SYNC_IFABLE)!=0
&& sqlite3_strlike("%not authorized to write%",zMsg,0)==0 ){
autopushFailed = 1;
nErr++;
}else if( (syncFlags & SYNC_CLONE)==0 || nCycle>0 ){
fossil_force_newline();
fossil_print("Error: %s\n", zMsg);
blob_appendf(&xfer.err, "server says: %s\n", zMsg);
nErr++;
break;
}
}else
/* Unknown message */
if( xfer.nToken>0 ){
if( blob_str(&xfer.aToken[0])[0]=='<' ){
|
| ︙ | ︙ | |||
2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 |
transport_close(&g.url);
transport_global_shutdown(&g.url);
if( nErr && go==2 ){
db_multi_exec("DROP TABLE onremote");
manifest_crosslink_end(MC_PERMIT_HOOKS);
content_enable_dephantomize(1);
db_end_transaction(0);
}
if( (syncFlags & SYNC_CLONE)==0 && g.rcvid && fossil_any_has_fork(g.rcvid) ){
fossil_warning("***** WARNING: a fork has occurred *****\n"
"use \"fossil leaves -multiple\" for more details.");
}
return nErr;
}
| > > > > > > > > > | 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 |
transport_close(&g.url);
transport_global_shutdown(&g.url);
if( nErr && go==2 ){
db_multi_exec("DROP TABLE onremote");
manifest_crosslink_end(MC_PERMIT_HOOKS);
content_enable_dephantomize(1);
db_end_transaction(0);
}
if( nErr && autopushFailed ){
fossil_warning(
"Warning: The check-in was successful and is saved locally but you\n"
" are not authorized to push the changes back to the server\n"
" at %s",
g.url.canonical
);
nErr--;
}
if( (syncFlags & SYNC_CLONE)==0 && g.rcvid && fossil_any_has_fork(g.rcvid) ){
fossil_warning("***** WARNING: a fork has occurred *****\n"
"use \"fossil leaves -multiple\" for more details.");
}
return nErr;
}
|
Changes to test/graph-test-1.wiki.
| ︙ | ︙ | |||
76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
* <a href="../../../timeline?a=2bc3cfeb&n=5"
target="testwindow">Branch risers comes from the bottom of the
screen, not from the andygoth-crlf branch.</a>
* <a href="../../../timeline?a=b8c7af5b&n=12"
target="testwindow">Check-in 2de15c8e has merge arrows from two
different trunk check-ins. One of the merge risers also branches
to check-in ea7f3297</a>
External:
* <a href="http://www.sqlite.org/src/timeline?c=2010-09-29&nd"
target="testwindow">Timewarp due to a mis-configured system clock.</a>
* <a href="http://core.tcl.tk/tk/finfo?name=tests/id.test"
target="testwindow">Show all three separate deletions of "id.test".
| > > > > > > > > > > | 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
* <a href="../../../timeline?a=2bc3cfeb&n=5"
target="testwindow">Branch risers comes from the bottom of the
screen, not from the andygoth-crlf branch.</a>
* <a href="../../../timeline?a=b8c7af5b&n=12"
target="testwindow">Check-in 2de15c8e has merge arrows from two
different trunk check-ins. One of the merge risers also branches
to check-in ea7f3297</a>
* <a href="../../../timeline?b=ae8709e2&n=25" target="testwindow">
Cherrypick merge arrows</a>
* <a href="../../../timeline?r=branch-1.37" target="testwindow">Branch
1.37 with cherry-pick merges from trunk.</a>
* <a href="../../../timeline?f=68bd2e7bedb8d05a" target="testwindow">
Single check-in takes both a full merge and a cherrypick merge</a>
* <a href="../../../timeline?b=dc81ac70&n=14" target="testwindow">
Mixed merge arrow, partly fully and partly cherrypick</a>
* <a href="../../../timeline?b=dc81ac70&n=13" target="testwindow">
Mixed merge arrow to bottom of screen.</a>
External:
* <a href="http://www.sqlite.org/src/timeline?c=2010-09-29&nd"
target="testwindow">Timewarp due to a mis-configured system clock.</a>
* <a href="http://core.tcl.tk/tk/finfo?name=tests/id.test"
target="testwindow">Show all three separate deletions of "id.test".
|
| ︙ | ︙ |
Changes to test/th1.test.
| ︙ | ︙ | |||
1027 1028 1029 1030 1031 1032 1033 |
# moved from Tcl builds to plain or the reverse. Sorting the
# command lists eliminates a dependence on order.
#
fossil test-th-eval "info commands"
set sorted_result [lsort $RESULT]
protOut "Sorted: $sorted_result"
set base_commands {anoncap anycap array artifact break breakpoint catch\
| | | | | | | | < | 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 |
# moved from Tcl builds to plain or the reverse. Sorting the
# command lists eliminates a dependence on order.
#
fossil test-th-eval "info commands"
set sorted_result [lsort $RESULT]
protOut "Sorted: $sorted_result"
set base_commands {anoncap anycap array artifact break breakpoint catch\
cgiHeaderLine checkout combobox continue date decorate dir enable_output \
encode64 error expr for getParameter glob_match globalState hascap \
hasfeature html htmlize http httpize if info insertCsrf lindex linecount \
list llength lsearch markdown nonce proc puts query randhex redirect\
regexp reinitialize rename render repository return searchable set\
setParameter setting stime string styleFooter styleHeader styleScript\
tclReady trace unset unversioned uplevel upvar utime verifyCsrf wiki}
set tcl_commands {tclEval tclExpr tclInvoke tclIsSafe tclMakeSafe}
if {$th1Tcl} {
test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands $tcl_commands"]}
} else {
test th1-info-commands-1 {$sorted_result eq [lsort "$base_commands"]}
}
###############################################################################
fossil test-th-eval "info vars"
if {$th1Hooks} {
test th1-info-vars-1 {[lsort $RESULT] eq \
|
| ︙ | ︙ |
Changes to tools/fossil-autocomplete.bash.
1 2 3 4 5 6 7 |
# Command name completion for Fossil.
# Mailing-list contribution by Stuart Rackham.
function _fossil() {
local cur commands
cur=${COMP_WORDS[COMP_CWORD]}
commands=$(fossil help --all)
if [ $COMP_CWORD -eq 1 ] || [ ${COMP_WORDS[1]} = help ]; then
| | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# Command name completion for Fossil.
# Mailing-list contribution by Stuart Rackham.
function _fossil() {
local cur commands
cur=${COMP_WORDS[COMP_CWORD]}
commands=$(fossil help --all)
if [ $COMP_CWORD -eq 1 ] || [ ${COMP_WORDS[1]} = help ]; then
# Command name completion for 1st argument or 2nd if help command.
COMPREPLY=( $(compgen -W "$commands" $cur) )
else
# File name completion for other arguments.
COMPREPLY=( $(compgen -f $cur{}) )
fi
}
complete -o default -F _fossil fossil f
|
Added tools/fslsrv.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
#!/bin/bash
BASEPORT=12345
FOSSIL=fossil
OLDPID=`pgrep -P 1 fossil`
PGARGS="-P 1"
if [ "$1" = "-f" ] ; then PGARGS= ; shift ; fi
if [ -n "$OLDPID" ]
then
echo "Killing running Fossil server first..."
pkill $PGARGS fossil
for i in $(seq 30)
do
if [ -n "$(pgrep $PGARGS fossil)" ]
then
if [ $i -eq 1 ]
then
echo -n "Waiting for it to die..."
else
echo -n .
fi
sleep '0.1'
else
break
fi
echo
done
killall -9 fossil 2> /dev/null
fi
if [ -x ./fossil ]
then
# We're running from a build tree, so use that version instead
FOSSIL=./fossil
fi
function start_one() {
bn=$1
port=$(($BASEPORT + $2))
url="$3"
$FOSSIL server --localhost --port $port --scgi \
--baseurl $url --errorlog ~/log/fossil/$bn-errors.log \
~/museum/$bn.fossil > ~/log/fossil/$bn-stdout.log &
echo Fossil server running for $bn, PID $!, port $port.
}
start_one example 0 https://example.com/code
start_one foo 1 https://foo.net
|
Changes to win/Makefile.dmc.
| ︙ | ︙ | |||
26 27 28 29 30 31 32 | TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL) LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32 dnsapi SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_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 SHELL_OPTIONS = -DNDEBUG=1 -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 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen | | | | | 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | TCC = $(DMDIR)\bin\dmc $(CFLAGS) $(DMCDEF) $(SSL) $(INCL) LIBS = $(DMDIR)\extra\lib\ zlib wsock32 advapi32 dnsapi SQLITE_OPTIONS = -DNDEBUG=1 -DSQLITE_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 SHELL_OPTIONS = -DNDEBUG=1 -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 -Dmain=sqlite3_shell -DSQLITE_SHELL_IS_UTF8=1 -DSQLITE_OMIT_LOAD_EXTENSION=1 -DUSE_SYSTEM_SQLITE=$(USE_SYSTEM_SQLITE) -DSQLITE_SHELL_DBNAME_PROC=sqlcmd_get_dbname -DSQLITE_SHELL_INIT_PROC=sqlcmd_init_proc -Daccess=file_access -Dsystem=fossil_system -Dgetenv=fossil_getenv -Dfopen=fossil_fopen SRC = add_.c alerts_.c allrepo_.c attach_.c 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 file_.c finfo_.c foci_.c forum_.c fshell_.c fusefs_.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 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)\file$O $(OBJDIR)\finfo$O $(OBJDIR)\foci$O $(OBJDIR)\forum$O $(OBJDIR)\fshell$O $(OBJDIR)\fusefs$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 RC=$(DMDIR)\bin\rcc RCFLAGS=-32 -w1 -I$(SRCDIR) /D__DMC__ APPNAME = $(OBJDIR)\fossil$(E) all: $(APPNAME) $(APPNAME) : translate$E mkindex$E codecheck1$E headers $(OBJ) $(OBJDIR)\link cd $(OBJDIR) codecheck1$E $(SRC) $(DMDIR)\bin\link @link $(OBJDIR)\fossil.res: $B\win\fossil.rc $(RC) $(RCFLAGS) -o$@ $** $(OBJDIR)\link: $B\win\Makefile.dmc $(OBJDIR)\fossil.res +echo add alerts allrepo attach 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 file finfo foci forum fshell fusefs glob graph gzip hname http http_socket http_ssl http_transport import info json json_artifact json_branch json_config json_diff json_dir json_finfo json_login json_query json_report json_status json_tag json_timeline json_user json_wiki leaf loadctrl login lookslike main manifest markdown markdown_html md5 merge merge3 moderate name path piechart pivot popen pqueue printf publish purge rebuild regexp repolist report rss schema search security_audit setup setupuser sha1 sha1hard sha3 shun sitemap skins smtp sqlcmd stash stat statrep style sync tag tar th_main timeline tkt tktsetup undo unicode unversioned update url user utf8 util verify vfile webmail wiki wikiformat winfile winhttp wysiwyg xfer xfersetup zip shell sqlite3 th th_lang > $@ +echo fossil >> $@ +echo fossil >> $@ +echo $(LIBS) >> $@ +echo. >> $@ +echo fossil >> $@ translate$E: $(SRCDIR)\translate.c |
| ︙ | ︙ | |||
288 289 290 291 292 293 294 295 296 297 298 299 300 301 | +translate$E $** > $@ $(OBJDIR)\deltacmd$O : deltacmd_.c deltacmd.h $(TCC) -o$@ -c deltacmd_.c deltacmd_.c : $(SRCDIR)\deltacmd.c +translate$E $** > $@ $(OBJDIR)\descendants$O : descendants_.c descendants.h $(TCC) -o$@ -c descendants_.c descendants_.c : $(SRCDIR)\descendants.c +translate$E $** > $@ | > > > > > > | 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 | +translate$E $** > $@ $(OBJDIR)\deltacmd$O : deltacmd_.c deltacmd.h $(TCC) -o$@ -c deltacmd_.c deltacmd_.c : $(SRCDIR)\deltacmd.c +translate$E $** > $@ $(OBJDIR)\deltafunc$O : deltafunc_.c deltafunc.h $(TCC) -o$@ -c deltafunc_.c deltafunc_.c : $(SRCDIR)\deltafunc.c +translate$E $** > $@ $(OBJDIR)\descendants$O : descendants_.c descendants.h $(TCC) -o$@ -c descendants_.c descendants_.c : $(SRCDIR)\descendants.c +translate$E $** > $@ |
| ︙ | ︙ | |||
944 945 946 947 948 949 950 | $(OBJDIR)\zip$O : zip_.c zip.h $(TCC) -o$@ -c zip_.c zip_.c : $(SRCDIR)\zip.c +translate$E $** > $@ headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h | | | 950 951 952 953 954 955 956 957 958 | $(OBJDIR)\zip$O : zip_.c zip.h $(TCC) -o$@ -c zip_.c zip_.c : $(SRCDIR)\zip.c +translate$E $** > $@ headers: makeheaders$E page_index.h builtin_data.h default_css.h VERSION.h +makeheaders$E add_.c:add.h alerts_.c:alerts.h allrepo_.c:allrepo.h attach_.c:attach.h 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 file_.c:file.h finfo_.c:finfo.h foci_.c:foci.h forum_.c:forum.h fshell_.c:fshell.h fusefs_.c:fusefs.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 @copy /Y nul: headers |
Changes to win/Makefile.mingw.
| ︙ | ︙ | |||
172 173 174 175 176 177 178 | endif #### The directories where the OpenSSL include and library files are located. # The recommended usage here is to use the Sysinternals junction tool # to create a hard link between an "openssl-1.x" sub-directory of the # Fossil source code directory and the target OpenSSL source directory. # | | | 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | endif #### The directories where the OpenSSL include and library files are located. # The recommended usage here is to use the Sysinternals junction tool # to create a hard link between an "openssl-1.x" sub-directory of the # Fossil source code directory and the target OpenSSL source directory. # OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2r OPENSSLINCDIR = $(OPENSSLDIR)/include OPENSSLLIBDIR = $(OPENSSLDIR) #### Either the directory where the Tcl library is installed or the Tcl # source code directory resides (depending on the value of the macro # FOSSIL_TCL_SOURCE). If this points to the Tcl install directory, # this directory must have "include" and "lib" sub-directories. If |
| ︙ | ︙ | |||
461 462 463 464 465 466 467 468 469 470 471 472 473 474 | $(SRCDIR)/comformat.c \ $(SRCDIR)/configure.c \ $(SRCDIR)/content.c \ $(SRCDIR)/cookies.c \ $(SRCDIR)/db.c \ $(SRCDIR)/delta.c \ $(SRCDIR)/deltacmd.c \ $(SRCDIR)/descendants.c \ $(SRCDIR)/diff.c \ $(SRCDIR)/diffcmd.c \ $(SRCDIR)/dispatch.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/etag.c \ | > | 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 | $(SRCDIR)/comformat.c \ $(SRCDIR)/configure.c \ $(SRCDIR)/content.c \ $(SRCDIR)/cookies.c \ $(SRCDIR)/db.c \ $(SRCDIR)/delta.c \ $(SRCDIR)/deltacmd.c \ $(SRCDIR)/deltafunc.c \ $(SRCDIR)/descendants.c \ $(SRCDIR)/diff.c \ $(SRCDIR)/diffcmd.c \ $(SRCDIR)/dispatch.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/etag.c \ |
| ︙ | ︙ | |||
672 673 674 675 676 677 678 679 680 681 682 683 684 685 | $(OBJDIR)/comformat_.c \ $(OBJDIR)/configure_.c \ $(OBJDIR)/content_.c \ $(OBJDIR)/cookies_.c \ $(OBJDIR)/db_.c \ $(OBJDIR)/delta_.c \ $(OBJDIR)/deltacmd_.c \ $(OBJDIR)/descendants_.c \ $(OBJDIR)/diff_.c \ $(OBJDIR)/diffcmd_.c \ $(OBJDIR)/dispatch_.c \ $(OBJDIR)/doc_.c \ $(OBJDIR)/encode_.c \ $(OBJDIR)/etag_.c \ | > | 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 | $(OBJDIR)/comformat_.c \ $(OBJDIR)/configure_.c \ $(OBJDIR)/content_.c \ $(OBJDIR)/cookies_.c \ $(OBJDIR)/db_.c \ $(OBJDIR)/delta_.c \ $(OBJDIR)/deltacmd_.c \ $(OBJDIR)/deltafunc_.c \ $(OBJDIR)/descendants_.c \ $(OBJDIR)/diff_.c \ $(OBJDIR)/diffcmd_.c \ $(OBJDIR)/dispatch_.c \ $(OBJDIR)/doc_.c \ $(OBJDIR)/encode_.c \ $(OBJDIR)/etag_.c \ |
| ︙ | ︙ | |||
810 811 812 813 814 815 816 817 818 819 820 821 822 823 | $(OBJDIR)/comformat.o \ $(OBJDIR)/configure.o \ $(OBJDIR)/content.o \ $(OBJDIR)/cookies.o \ $(OBJDIR)/db.o \ $(OBJDIR)/delta.o \ $(OBJDIR)/deltacmd.o \ $(OBJDIR)/descendants.o \ $(OBJDIR)/diff.o \ $(OBJDIR)/diffcmd.o \ $(OBJDIR)/dispatch.o \ $(OBJDIR)/doc.o \ $(OBJDIR)/encode.o \ $(OBJDIR)/etag.o \ | > | 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 | $(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 \ |
| ︙ | ︙ | |||
1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 | $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \ $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \ $(OBJDIR)/content_.c:$(OBJDIR)/content.h \ $(OBJDIR)/cookies_.c:$(OBJDIR)/cookies.h \ $(OBJDIR)/db_.c:$(OBJDIR)/db.h \ $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h \ $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h \ $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h \ $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h \ $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \ $(OBJDIR)/dispatch_.c:$(OBJDIR)/dispatch.h \ $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \ $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \ $(OBJDIR)/etag_.c:$(OBJDIR)/etag.h \ | > | 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 | $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \ $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \ $(OBJDIR)/content_.c:$(OBJDIR)/content.h \ $(OBJDIR)/cookies_.c:$(OBJDIR)/cookies.h \ $(OBJDIR)/db_.c:$(OBJDIR)/db.h \ $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h \ $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h \ $(OBJDIR)/deltafunc_.c:$(OBJDIR)/deltafunc.h \ $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h \ $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h \ $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \ $(OBJDIR)/dispatch_.c:$(OBJDIR)/dispatch.h \ $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \ $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \ $(OBJDIR)/etag_.c:$(OBJDIR)/etag.h \ |
| ︙ | ︙ | |||
1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 | $(OBJDIR)/deltacmd_.c: $(SRCDIR)/deltacmd.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/deltacmd.c >$@ $(OBJDIR)/deltacmd.o: $(OBJDIR)/deltacmd_.c $(OBJDIR)/deltacmd.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/deltacmd.o -c $(OBJDIR)/deltacmd_.c $(OBJDIR)/deltacmd.h: $(OBJDIR)/headers $(OBJDIR)/descendants_.c: $(SRCDIR)/descendants.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/descendants.c >$@ $(OBJDIR)/descendants.o: $(OBJDIR)/descendants_.c $(OBJDIR)/descendants.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/descendants.o -c $(OBJDIR)/descendants_.c | > > > > > > > > | 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 | $(OBJDIR)/deltacmd_.c: $(SRCDIR)/deltacmd.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/deltacmd.c >$@ $(OBJDIR)/deltacmd.o: $(OBJDIR)/deltacmd_.c $(OBJDIR)/deltacmd.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/deltacmd.o -c $(OBJDIR)/deltacmd_.c $(OBJDIR)/deltacmd.h: $(OBJDIR)/headers $(OBJDIR)/deltafunc_.c: $(SRCDIR)/deltafunc.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/deltafunc.c >$@ $(OBJDIR)/deltafunc.o: $(OBJDIR)/deltafunc_.c $(OBJDIR)/deltafunc.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/deltafunc.o -c $(OBJDIR)/deltafunc_.c $(OBJDIR)/deltafunc.h: $(OBJDIR)/headers $(OBJDIR)/descendants_.c: $(SRCDIR)/descendants.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/descendants.c >$@ $(OBJDIR)/descendants.o: $(OBJDIR)/descendants_.c $(OBJDIR)/descendants.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/descendants.o -c $(OBJDIR)/descendants_.c |
| ︙ | ︙ |
Changes to win/Makefile.mingw.mistachkin.
| ︙ | ︙ | |||
172 173 174 175 176 177 178 | endif #### The directories where the OpenSSL include and library files are located. # The recommended usage here is to use the Sysinternals junction tool # to create a hard link between an "openssl-1.x" sub-directory of the # Fossil source code directory and the target OpenSSL source directory. # | | | 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 | endif #### The directories where the OpenSSL include and library files are located. # The recommended usage here is to use the Sysinternals junction tool # to create a hard link between an "openssl-1.x" sub-directory of the # Fossil source code directory and the target OpenSSL source directory. # OPENSSLDIR = $(SRCDIR)/../compat/openssl-1.0.2r OPENSSLINCDIR = $(OPENSSLDIR)/include OPENSSLLIBDIR = $(OPENSSLDIR) #### Either the directory where the Tcl library is installed or the Tcl # source code directory resides (depending on the value of the macro # FOSSIL_TCL_SOURCE). If this points to the Tcl install directory, # this directory must have "include" and "lib" sub-directories. If |
| ︙ | ︙ | |||
461 462 463 464 465 466 467 468 469 470 471 472 473 474 | $(SRCDIR)/comformat.c \ $(SRCDIR)/configure.c \ $(SRCDIR)/content.c \ $(SRCDIR)/cookies.c \ $(SRCDIR)/db.c \ $(SRCDIR)/delta.c \ $(SRCDIR)/deltacmd.c \ $(SRCDIR)/descendants.c \ $(SRCDIR)/diff.c \ $(SRCDIR)/diffcmd.c \ $(SRCDIR)/dispatch.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/etag.c \ | > | 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 | $(SRCDIR)/comformat.c \ $(SRCDIR)/configure.c \ $(SRCDIR)/content.c \ $(SRCDIR)/cookies.c \ $(SRCDIR)/db.c \ $(SRCDIR)/delta.c \ $(SRCDIR)/deltacmd.c \ $(SRCDIR)/deltafunc.c \ $(SRCDIR)/descendants.c \ $(SRCDIR)/diff.c \ $(SRCDIR)/diffcmd.c \ $(SRCDIR)/dispatch.c \ $(SRCDIR)/doc.c \ $(SRCDIR)/encode.c \ $(SRCDIR)/etag.c \ |
| ︙ | ︙ | |||
672 673 674 675 676 677 678 679 680 681 682 683 684 685 | $(OBJDIR)/comformat_.c \ $(OBJDIR)/configure_.c \ $(OBJDIR)/content_.c \ $(OBJDIR)/cookies_.c \ $(OBJDIR)/db_.c \ $(OBJDIR)/delta_.c \ $(OBJDIR)/deltacmd_.c \ $(OBJDIR)/descendants_.c \ $(OBJDIR)/diff_.c \ $(OBJDIR)/diffcmd_.c \ $(OBJDIR)/dispatch_.c \ $(OBJDIR)/doc_.c \ $(OBJDIR)/encode_.c \ $(OBJDIR)/etag_.c \ | > | 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 | $(OBJDIR)/comformat_.c \ $(OBJDIR)/configure_.c \ $(OBJDIR)/content_.c \ $(OBJDIR)/cookies_.c \ $(OBJDIR)/db_.c \ $(OBJDIR)/delta_.c \ $(OBJDIR)/deltacmd_.c \ $(OBJDIR)/deltafunc_.c \ $(OBJDIR)/descendants_.c \ $(OBJDIR)/diff_.c \ $(OBJDIR)/diffcmd_.c \ $(OBJDIR)/dispatch_.c \ $(OBJDIR)/doc_.c \ $(OBJDIR)/encode_.c \ $(OBJDIR)/etag_.c \ |
| ︙ | ︙ | |||
810 811 812 813 814 815 816 817 818 819 820 821 822 823 | $(OBJDIR)/comformat.o \ $(OBJDIR)/configure.o \ $(OBJDIR)/content.o \ $(OBJDIR)/cookies.o \ $(OBJDIR)/db.o \ $(OBJDIR)/delta.o \ $(OBJDIR)/deltacmd.o \ $(OBJDIR)/descendants.o \ $(OBJDIR)/diff.o \ $(OBJDIR)/diffcmd.o \ $(OBJDIR)/dispatch.o \ $(OBJDIR)/doc.o \ $(OBJDIR)/encode.o \ $(OBJDIR)/etag.o \ | > | 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 | $(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 \ |
| ︙ | ︙ | |||
1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 | $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \ $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \ $(OBJDIR)/content_.c:$(OBJDIR)/content.h \ $(OBJDIR)/cookies_.c:$(OBJDIR)/cookies.h \ $(OBJDIR)/db_.c:$(OBJDIR)/db.h \ $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h \ $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h \ $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h \ $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h \ $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \ $(OBJDIR)/dispatch_.c:$(OBJDIR)/dispatch.h \ $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \ $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \ $(OBJDIR)/etag_.c:$(OBJDIR)/etag.h \ | > | 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 | $(OBJDIR)/comformat_.c:$(OBJDIR)/comformat.h \ $(OBJDIR)/configure_.c:$(OBJDIR)/configure.h \ $(OBJDIR)/content_.c:$(OBJDIR)/content.h \ $(OBJDIR)/cookies_.c:$(OBJDIR)/cookies.h \ $(OBJDIR)/db_.c:$(OBJDIR)/db.h \ $(OBJDIR)/delta_.c:$(OBJDIR)/delta.h \ $(OBJDIR)/deltacmd_.c:$(OBJDIR)/deltacmd.h \ $(OBJDIR)/deltafunc_.c:$(OBJDIR)/deltafunc.h \ $(OBJDIR)/descendants_.c:$(OBJDIR)/descendants.h \ $(OBJDIR)/diff_.c:$(OBJDIR)/diff.h \ $(OBJDIR)/diffcmd_.c:$(OBJDIR)/diffcmd.h \ $(OBJDIR)/dispatch_.c:$(OBJDIR)/dispatch.h \ $(OBJDIR)/doc_.c:$(OBJDIR)/doc.h \ $(OBJDIR)/encode_.c:$(OBJDIR)/encode.h \ $(OBJDIR)/etag_.c:$(OBJDIR)/etag.h \ |
| ︙ | ︙ | |||
1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 | $(OBJDIR)/deltacmd_.c: $(SRCDIR)/deltacmd.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/deltacmd.c >$@ $(OBJDIR)/deltacmd.o: $(OBJDIR)/deltacmd_.c $(OBJDIR)/deltacmd.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/deltacmd.o -c $(OBJDIR)/deltacmd_.c $(OBJDIR)/deltacmd.h: $(OBJDIR)/headers $(OBJDIR)/descendants_.c: $(SRCDIR)/descendants.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/descendants.c >$@ $(OBJDIR)/descendants.o: $(OBJDIR)/descendants_.c $(OBJDIR)/descendants.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/descendants.o -c $(OBJDIR)/descendants_.c | > > > > > > > > | 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 | $(OBJDIR)/deltacmd_.c: $(SRCDIR)/deltacmd.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/deltacmd.c >$@ $(OBJDIR)/deltacmd.o: $(OBJDIR)/deltacmd_.c $(OBJDIR)/deltacmd.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/deltacmd.o -c $(OBJDIR)/deltacmd_.c $(OBJDIR)/deltacmd.h: $(OBJDIR)/headers $(OBJDIR)/deltafunc_.c: $(SRCDIR)/deltafunc.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/deltafunc.c >$@ $(OBJDIR)/deltafunc.o: $(OBJDIR)/deltafunc_.c $(OBJDIR)/deltafunc.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/deltafunc.o -c $(OBJDIR)/deltafunc_.c $(OBJDIR)/deltafunc.h: $(OBJDIR)/headers $(OBJDIR)/descendants_.c: $(SRCDIR)/descendants.c $(TRANSLATE) $(TRANSLATE) $(SRCDIR)/descendants.c >$@ $(OBJDIR)/descendants.o: $(OBJDIR)/descendants_.c $(OBJDIR)/descendants.h $(SRCDIR)/config.h $(XTCC) -o $(OBJDIR)/descendants.o -c $(OBJDIR)/descendants_.c |
| ︙ | ︙ |
Changes to win/Makefile.msc.
| ︙ | ︙ | |||
96 97 98 99 100 101 102 | # Enable support for the SQLite Encryption Extension? !ifndef USE_SEE USE_SEE = 0 !endif !if $(FOSSIL_ENABLE_SSL)!=0 | | | 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | # Enable support for the SQLite Encryption Extension? !ifndef USE_SEE USE_SEE = 0 !endif !if $(FOSSIL_ENABLE_SSL)!=0 SSLDIR = $(B)\compat\openssl-1.0.2r SSLINCDIR = $(SSLDIR)\inc32 !if $(FOSSIL_DYNAMIC_BUILD)!=0 SSLLIBDIR = $(SSLDIR)\out32dll !else SSLLIBDIR = $(SSLDIR)\out32 !endif SSLLFLAGS = /nologo /opt:ref /debug |
| ︙ | ︙ | |||
401 402 403 404 405 406 407 408 409 410 411 412 413 414 |
comformat_.c \
configure_.c \
content_.c \
cookies_.c \
db_.c \
delta_.c \
deltacmd_.c \
descendants_.c \
diff_.c \
diffcmd_.c \
dispatch_.c \
doc_.c \
encode_.c \
etag_.c \
| > | 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 |
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 \
|
| ︙ | ︙ | |||
611 612 613 614 615 616 617 618 619 620 621 622 623 624 |
$(OX)\configure$O \
$(OX)\content$O \
$(OX)\cookies$O \
$(OX)\cson_amalgamation$O \
$(OX)\db$O \
$(OX)\delta$O \
$(OX)\deltacmd$O \
$(OX)\descendants$O \
$(OX)\diff$O \
$(OX)\diffcmd$O \
$(OX)\dispatch$O \
$(OX)\doc$O \
$(OX)\encode$O \
$(OX)\etag$O \
| > | 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 |
$(OX)\configure$O \
$(OX)\content$O \
$(OX)\cookies$O \
$(OX)\cson_amalgamation$O \
$(OX)\db$O \
$(OX)\delta$O \
$(OX)\deltacmd$O \
$(OX)\deltafunc$O \
$(OX)\descendants$O \
$(OX)\diff$O \
$(OX)\diffcmd$O \
$(OX)\dispatch$O \
$(OX)\doc$O \
$(OX)\encode$O \
$(OX)\etag$O \
|
| ︙ | ︙ | |||
731 732 733 734 735 736 737 |
$(OX)\zip$O \
!if $(FOSSIL_ENABLE_MINIZ)!=0
$(OX)\miniz$O \
!endif
$(OX)\fossil.res
| > | > | > > | | 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 |
$(OX)\zip$O \
!if $(FOSSIL_ENABLE_MINIZ)!=0
$(OX)\miniz$O \
!endif
$(OX)\fossil.res
!ifndef BASEAPPNAME
BASEAPPNAME = fossil
!endif
APPNAME = $(OX)\$(BASEAPPNAME)$(E)
PDBNAME = $(OX)\$(BASEAPPNAME)$(P)
APPTARGETS =
all: $(OX) $(APPNAME)
zlib:
@echo Building zlib from "$(ZLIBDIR)"...
!if $(FOSSIL_ENABLE_WINXP)!=0
@pushd "$(ZLIBDIR)" && $(MAKE) /f win32\Makefile.msc $(ZLIB) "CC=cl $(XPCFLAGS)" "LD=link $(XPLDFLAGS)" && popd
|
| ︙ | ︙ | |||
808 809 810 811 812 813 814 815 816 817 818 819 820 821 | echo $(OX)\configure.obj >> $@ echo $(OX)\content.obj >> $@ echo $(OX)\cookies.obj >> $@ echo $(OX)\cson_amalgamation.obj >> $@ echo $(OX)\db.obj >> $@ echo $(OX)\delta.obj >> $@ echo $(OX)\deltacmd.obj >> $@ echo $(OX)\descendants.obj >> $@ echo $(OX)\diff.obj >> $@ echo $(OX)\diffcmd.obj >> $@ echo $(OX)\dispatch.obj >> $@ echo $(OX)\doc.obj >> $@ echo $(OX)\encode.obj >> $@ echo $(OX)\etag.obj >> $@ | > | 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 | echo $(OX)\configure.obj >> $@ echo $(OX)\content.obj >> $@ echo $(OX)\cookies.obj >> $@ echo $(OX)\cson_amalgamation.obj >> $@ echo $(OX)\db.obj >> $@ echo $(OX)\delta.obj >> $@ echo $(OX)\deltacmd.obj >> $@ echo $(OX)\deltafunc.obj >> $@ echo $(OX)\descendants.obj >> $@ echo $(OX)\diff.obj >> $@ echo $(OX)\diffcmd.obj >> $@ echo $(OX)\dispatch.obj >> $@ echo $(OX)\doc.obj >> $@ echo $(OX)\encode.obj >> $@ echo $(OX)\etag.obj >> $@ |
| ︙ | ︙ | |||
1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 | translate$E $** > $@ $(OX)\deltacmd$O : deltacmd_.c deltacmd.h $(TCC) /Fo$@ -c deltacmd_.c deltacmd_.c : $(SRCDIR)\deltacmd.c translate$E $** > $@ $(OX)\descendants$O : descendants_.c descendants.h $(TCC) /Fo$@ -c descendants_.c descendants_.c : $(SRCDIR)\descendants.c translate$E $** > $@ | > > > > > > | 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 | translate$E $** > $@ $(OX)\deltacmd$O : deltacmd_.c deltacmd.h $(TCC) /Fo$@ -c deltacmd_.c deltacmd_.c : $(SRCDIR)\deltacmd.c translate$E $** > $@ $(OX)\deltafunc$O : deltafunc_.c deltafunc.h $(TCC) /Fo$@ -c deltafunc_.c deltafunc_.c : $(SRCDIR)\deltafunc.c translate$E $** > $@ $(OX)\descendants$O : descendants_.c descendants.h $(TCC) /Fo$@ -c descendants_.c descendants_.c : $(SRCDIR)\descendants.c translate$E $** > $@ |
| ︙ | ︙ | |||
1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 | 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 \ 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 \ | > | 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 | 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 \ |
| ︙ | ︙ |
Changes to win/buildmsvc.bat.
| ︙ | ︙ | |||
181 182 183 184 185 186 187 | :skip_setupVisualStudio %_VECHO% VcInstallDir = '%VCINSTALLDIR%' REM REM NOTE: Attempt to create the build output directory, if necessary. REM | > > > > > > > | | | | | | 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
:skip_setupVisualStudio
%_VECHO% VcInstallDir = '%VCINSTALLDIR%'
REM
REM NOTE: Attempt to create the build output directory, if necessary.
REM
IF NOT DEFINED BUILDDIR (
SET BUILDDIR=%ROOT%\msvcbld%BUILDSUFFIX%
)
%_VECHO% BuildSuffix = '%BUILDSUFFIX%'
%_VECHO% BuildDir = '%BUILDDIR%'
IF NOT EXIST "%BUILDDIR%" (
%__ECHO% MKDIR "%BUILDDIR%"
IF ERRORLEVEL 1 (
ECHO Could not make directory "%BUILDDIR%".
GOTO errors
)
)
REM
REM NOTE: Attempt to change to the created build output directory so that
REM the generated files will be placed there.
REM
%__ECHO2% PUSHD "%BUILDDIR%"
IF ERRORLEVEL 1 (
ECHO Could not change to directory "%BUILDDIR%".
GOTO errors
)
REM
REM NOTE: If requested, setup the build environment to refer to the Windows
REM SDK v7.1A, which is required if the binaries are being built with
REM Visual Studio 201x and need to work on Windows XP.
|
| ︙ | ︙ |
Changes to www/admin-v-setup.md.
1 2 3 4 5 6 | # The Differences Between the Setup and Admin User Capabilities Several of the Fossil user capabilities form a clear power hierarchy. Mathematically speaking: > *Setup > Admin > Moderator > User > Subscriber > Anonymous > Nobody* | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # The Differences Between the Setup and Admin User Capabilities Several of the Fossil user capabilities form a clear power hierarchy. Mathematically speaking: > *Setup > Admin > Moderator > User > Subscriber > Anonymous > Nobody* This document explains the distinction between the first two. For the others, see: * [How Moderation Works](./forum.wiki#moderation) * [Users vs Subscribers](./alerts.md#uvs) |
| ︙ | ︙ | |||
283 284 285 286 287 288 289 |
to 0 to Setup only while allowing Admin-only users to change it
from 0 to 1. Fossil doesn't currently allow that.</p>
* <p><b>Risky</b>: The <tt>https-login</tt> setting falls under
the "Security" section above, but it should probably never be
adjusted by Admin-only users. Sites that want it on will never
want it to be disabled without a very good reason.</p>
| | | 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 |
to 0 to Setup only while allowing Admin-only users to change it
from 0 to 1. Fossil doesn't currently allow that.</p>
* <p><b>Risky</b>: The <tt>https-login</tt> setting falls under
the "Security" section above, but it should probably never be
adjusted by Admin-only users. Sites that want it on will never
want it to be disabled without a very good reason.</p>
<p>There is also an inverse risk: if the site has a front-end
HTTPS proxy that uses HTTP to communicate over localhost to
Fossil, enabling this setting will create an infinite redirect
loop! (Ask me how I know.)</p>
* <p><b>Dangerous</b>: The <tt>email-send-command</tt> setting
could allow a rogue Admin to run arbitrary commands on the host
|
| ︙ | ︙ |
Changes to www/backoffice.md.
1 2 3 4 5 6 | Backoffice ========== This is technical documentation about the internal workings of Fossil. Ordinary Fossil users do not need to know about anything covered by this document. The information here is intended for people who want to enhance | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | Backoffice ========== This is technical documentation about the internal workings of Fossil. Ordinary Fossil users do not need to know about anything covered by this document. The information here is intended for people who want to enhance or extend the Fossil code, or who just want a deeper understanding of the internal workings of Fossil. What Is The Backoffice ---------------------- The backoffice is a mechanism used by a [Fossil server](/doc/trunk/www/server.wiki) to do low-priority background work that is not directly related to the user interface. Here are some examples of the kinds of work that backoffice performs: 1. Sending email alerts and notifications 2. Sending out daily digests of email notifications 3. Other background email handling chores |
| ︙ | ︙ | |||
47 48 49 50 51 52 53 | request. The backoffice is not a daemon. Each backoffice process runs for a short while and then exits. This helps keep Fossil easy to manage, since there are no daemons to start and stop. To upgrade Fossil to a new version, you simply replace the older "fossil" executable with the newer one, and the backoffice processes will (within a minute or so) start using the new | | | | | | 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | request. The backoffice is not a daemon. Each backoffice process runs for a short while and then exits. This helps keep Fossil easy to manage, since there are no daemons to start and stop. To upgrade Fossil to a new version, you simply replace the older "fossil" executable with the newer one, and the backoffice processes will (within a minute or so) start using the new one. (Upgrading the executable on Windows is more complicated, since on Windows it is not possible to replace an executable file that is in active use. But Windows users probably already know this.) The backoffice is serialized and rate limited. No more than a single backoffice process will be running at once, and backoffice runs will not occur more frequently than once every 60 seconds. If a Fossil server is idle, then no backoffice processes will be running. That means there are no extra processes sitting around taking up memory and process table slots for seldom accessed repositories. The backoffice is an on-demand system. A busy repository will usually have a backoffice running at all times. But an infrequently accessed repository will only have backoffice processes running for a minute or two following the most recent access. Manually Running The Backoffice ------------------------------- The automatic backoffice runs are sufficient for most installations. However, the daily digest of email notifications is handled by the backoffice. If a Fossil server can sometimes go more than a day without being accessed, then the automatic backoffice will never run, and the daily digest might not go out until somebody does visit a webpage. If this is a problem, an adminstrator can set up a cron job to periodically run: > fossil backoffice _REPOSITORY_ That command will cause backoffice processing to occur immediately. Note that this is almost never necessary for an internet-facing Fossil repository, since most repositories will get multiple accesses per day from random robots, which will be sufficient to kick off the daily digest emails. And even for a private server, if there is very little traffic, then the daily digests are probably a no-op anyhow |
| ︙ | ︙ | |||
116 117 118 119 120 121 122 | > fossil test-backoffice-lease -R _REPOSITORY_ If a system has been idle for a long time, then there will be no backoffice processes. (Either the process id entries in the lease will be zero, or there will exist no process associated with the process id.) When a new web request comes in, the system sees that no backoffice process is active and so it kicks off a separate | | | | 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | > fossil test-backoffice-lease -R _REPOSITORY_ If a system has been idle for a long time, then there will be no backoffice processes. (Either the process id entries in the lease will be zero, or there will exist no process associated with the process id.) When a new web request comes in, the system sees that no backoffice process is active and so it kicks off a separate process to run backoffice. The new backoffice process becomes the "current" process. It sets a lease expiration time for itself to be 60 seconds in the future. Then it does the backoffice processing and exits. Note that usually the backoffice process will exit long before its lease expires. That is ok. The lease is there to limit the rate at which backoffice processes run. If a new backoffice process starts up and sees that the "current" lease has |
| ︙ | ︙ | |||
178 179 180 181 182 183 184 | can be fixed. The "backoffice-logfile" setting is the name of a log file onto which is appended a short message everything a backoffice process actually starts to do the backoffice work. This log file can be used to verify that backoffice really is running, if there is any doubt. The "backoffice-disable" setting prevents automatic backoffice processing, if true. Use this to completely disable backoffice processing that occurs automatically after each HTTP request. The "backoffice-disable" | | | 178 179 180 181 182 183 184 185 186 187 188 189 | can be fixed. The "backoffice-logfile" setting is the name of a log file onto which is appended a short message everything a backoffice process actually starts to do the backoffice work. This log file can be used to verify that backoffice really is running, if there is any doubt. The "backoffice-disable" setting prevents automatic backoffice processing, if true. Use this to completely disable backoffice processing that occurs automatically after each HTTP request. The "backoffice-disable" setting does not affect the operation of the manual "fossil backoffice" command. Most installations should leave "backoffice-nodelay" and "backoffice-disable" set to their default values of off and leave "backoffice-logfile" unset or set to an empty string. |
Changes to www/blockchain.md.
| ︙ | ︙ | |||
18 19 20 21 22 23 24 | Some people have come to associate blockchain with cryptocurrency, however, and since Fossil has nothing to do with cryptocurrency, the claim that Fossil is build around blockchain is met with skepticism. The key thing to note here is that cryptocurrency implementations like BitCoin are built around blockchain, but they are not synonymous with blockchain. Blockchain is a much broader concept. Blockchain is a mechanism for | | | 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | Some people have come to associate blockchain with cryptocurrency, however, and since Fossil has nothing to do with cryptocurrency, the claim that Fossil is build around blockchain is met with skepticism. The key thing to note here is that cryptocurrency implementations like BitCoin are built around blockchain, but they are not synonymous with blockchain. Blockchain is a much broader concept. Blockchain is a mechanism for constructed a distributed ledger of transactions. Yes, you can use a distributed ledger to implement a cryptocurrency, but you can also use a distributed ledger to implement a version control system, and probably many other kinds of applications as well. Blockchain is a much broader idea than cryptocurrency. [(1)]: https://en.wikipedia.org/wiki/Blockchain |
Changes to www/build.wiki.
| ︙ | ︙ | |||
71 72 73 74 75 76 77 78 79 80 81 82 | <h2>2.0 Compiling</h2> <ol> <li value="5"> <p>Unpack the ZIP or tarball you downloaded then <b>cd</b> into the directory created.</p></li> <li><i>(Optional, Unix only)</i> Run <b>./configure</b> to construct a makefile. <ol type="a"> <li><p> | > > > > > > > > > > > > > > | > > | > > > > > > > > > | 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | <h2>2.0 Compiling</h2> <ol> <li value="5"> <p>Unpack the ZIP or tarball you downloaded then <b>cd</b> into the directory created.</p></li> <li><i>(Optional, Debian-compatible Linux only)</i> Make sure you have all the necessary tools and libraries at hand by running: <b>sudo apt install tcl-dev tk libssl-dev</b>. <li><i>(Optional, Unix only)</i> Run <b>./configure</b> to construct a makefile. <ol type="a"> <li><p> The build system for Fossil on Unix-like systems assumes that the OpenSSL development and runtime files are available on your system, because unprotected repositories are trivial to attack otherwise. Indeed, some public Fossil repositories — including Fossil's own — today run in an HTTPS-only mode, so that you can't even do an anonymous clone from them without using the TLS features added to Fossil by OpenSSL. To weaken that stance could allow a [https://en.wikipedia.org/wiki/Man-in-the-middle_attack|man in the middle attack], such as one that substitutes malicious code into your Fossil repository clone.</p> <p>You can force the Fossil build system to avoid searching for, building against, and linking to the OpenSSL library by passing <b>--with-openssl=none</b> to the <tt>configure</tt> script.</p> <p>If you do not have the OpenSSL development libraries on your system, we recommend that you install them, typically via your OS's package manager. The Fossil build system goes to a lot of effort to seek these out wherever they may be found, so that is typically all you need to do.</p> <p>For more advanced use cases, see the [./ssl.wiki#openssl-bin|OpenSSL discussion in the "TLS and Fossil" document].</p> <li><p> To build a statically linked binary (suitable for use inside a chroot jail) add the <b>--static</b> option. <li><p> To enable the native [./th1.md#tclEval | Tcl integration feature] feature, |
| ︙ | ︙ | |||
134 135 136 137 138 139 140 | file "<b>win\buildmsvc.bat</b>" may be used and it will attempt to detect and use the latest installed version of MSVC.<br><br>To enable the optional <a href="https://www.openssl.org/">OpenSSL</a> support, first <a href="https://www.openssl.org/source/">download the official source code for OpenSSL</a> and extract it to an appropriately named "<b>openssl-X.Y.ZA</b>" subdirectory within the local [/tree?ci=trunk&name=compat | compat] directory (e.g. | | | 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 | file "<b>win\buildmsvc.bat</b>" may be used and it will attempt to detect and use the latest installed version of MSVC.<br><br>To enable the optional <a href="https://www.openssl.org/">OpenSSL</a> support, first <a href="https://www.openssl.org/source/">download the official source code for OpenSSL</a> and extract it to an appropriately named "<b>openssl-X.Y.ZA</b>" subdirectory within the local [/tree?ci=trunk&name=compat | compat] directory (e.g. "<b>compat/openssl-1.0.2r</b>"), then make sure that some recent <a href="http://www.perl.org/">Perl</a> binaries are installed locally, and finally run one of the following commands: <blockquote><pre> nmake /f Makefile.msc FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin </pre></blockquote> <blockquote><pre> buildmsvc.bat FOSSIL_ENABLE_SSL=1 FOSSIL_BUILD_SSL=1 PERLDIR=C:\full\path\to\Perl\bin |
| ︙ | ︙ | |||
163 164 165 166 167 168 169 | "openssl-devel" packages installed first. </ol> </ol> <h2>3.0 Installing</h2> <ol> | | | 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 | "openssl-devel" packages installed first. </ol> </ol> <h2>3.0 Installing</h2> <ol> <li value="9"> <p>The finished binary is named "fossil" (or "fossil.exe" on Windows). Put this binary in a directory that is somewhere on your PATH environment variable. It does not matter where.</p> <li> <p><b>(Optional:)</b> |
| ︙ | ︙ |
Changes to www/changes.wiki.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
<title>Change Log</title>
<a name='v2_7'></a>
<h2>Changes for Version 2.7 (2018-09-22)</h2>
* Add the [./alerts.md|email alerts] feature for commits, ticket
changes, wiki changes, forum posts, and announcements. This is
still a work in progress. It is functional, but it is not as easy to
setup and use as it ought to be.
* Add the [./forum.wiki|discussion forum] feature.
* Add new user capabilities letters needed to support alerts and forum.
Formerly, user capabilities were letters from [a-z], but with the
enhancements, the supply of lower case letters was exhausted.
User capabilities are now letters in [a-zA-Z0-9].
* The built-in skins are now responsive, providing better layout on
small screens, including mobile devices.
* The default skin now includes a hamburger menu that is generated
by the [/sitemap] page.
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
<title>Change Log</title>
<a name='v2_9'></a>
<h2>Changes for Version 2.9 (pending)</h2>
* Added the [/help?cmd=git|fossil git export] command and instructions
for [./mirrortogithub.md|creating a GitHub mirror of a Fossil project].
* Improved handling of relative hyperlinks on the
[/help?cmd=/artifact|/artifact] pages for wiki. For example,
hyperlinks and the lizard <img> now work correctly
for both [/artifact/2ff24ab0887cf522] and
[/doc/0d7ac90d575004c2415/www/index.wiki].
* For the "[/help?cmd=update|fossil update]" and
"[/help?cmd=checkout|fossil checkout]" commands, if a
managed file is removed because it is no longer part of the target
check-in and the directory containing the file is empty after the
file is removed and the directory is not the current working
directory and is not on the [/help?cmd=empty-dirs|empty-dirs]
list, then also remove the directory.
* Update internal Unicode character tables, used in regular expression
handling, from version 11.0 to 12.0.
* In "[/help?cmd=regexp|fossil regexp]", "[/help?cmd=grep|fossil grep]"
and the TH1 "regexp" command, the -nocase option now removes multiple
diacritics from the same character (derived from SQLite's
remove_diacritics=2)
<a name='v2_8'></a>
<h2>Changes for Version 2.8 (2019-02-20)</h2>
* Show cherry-pick merges as dotted lines on the timeline graph.
→ The "fossil rebuild" command must be run to create and
populate the new "cherrypick" table in the repository in order
for this feature to operate.
* Add the ability to associate branches, check-ins, and tags with
specially-named Wiki pages. This gives the ability to better
document branches and tags, and provide more documentation on
check-ins beyond the check-in comment. The associated Wiki is
automatically displayed on /info pages for check-ins, and on
/timeline?r=BRANCH and /timeline?t=TAG pages for branches and
tags. This feature is on by default, but can be disabled in on
the Admin/Wiki page.
* Enhance the repository list page (shown for example by
"fossil all ui") so that it shows the name and last check-in
time for each project. The implementation of the repository
list page is now broken out into a separate source file (repolist.c).
* Allow users with Forum Supervisor permission ('6') to add Forum
Write Trusted permission ('4') to users as they are approving a
forum post by that user.
* When running a bisect, report the number of check-ins still in
the search range and the estimated number of bisect steps remaining.
Do this at each step of the bisect.
* Provide a permanent link to a bisect timeline using the bid= query
parameter.
* Make the chronological forum display feature available to all users,
and make it the default format on mobile devices.
* Break out Wiki setup into a separate /setup_wiki page, accessible
on the standard menus through Admin/Wiki.
* Add "Next" and "Previous" buttons on the /wdiff page, allowing
the user to step through the versions of a wiki page.
* Improve the display of the /whistory page.
* Omit the "HH:MM" timestamps on timeline graphs on narrow-screen
devices, to improve horizontal space uses. This helps make Fossil
more mobile-friendly.
* Enhance /wcontent to show a sortable list of Wiki pages together
with the number of revisions and the most recent change time for
each page.
* Hyperlinks to Wiki pages on the /timeline go to the specific
version of the Wiki page named in the timeline, not to the latest
version.
* Enhancements to the "amend", "tag", and "reparent" commands, including
adding options --override-date, --override-user, and --dry-run.
* Add the global --comment-format command-line option and the
comment-format setting to control the display of the command-line
timeline.
* Change the "fossil reparent" command so that it only works from
within an active checkout.
* On the /setup_ucap_list, show administrators how many users have
each capability. The counts are a hyperlink to the /setup_ulist
page showing the subset of users that have that capability.
* Provide the ability to redirect all HTTP pages to HTTPS. Formerly
one could cause this to occur for the /login page only. That option
still exists, but the redirect can now also be done for all pages.
* "Compress" the built-in javascript by omitting comments and
leading and trailing whitespace.
* Detect when the repository used by a checkout is swapped out for
a clone that uses different RID values, and make appropriate adjustments
to the checkout database to avoid any problems.
* Add the backoffice-disable setting to completely disable the
backoffice feature.
* Update the built-in SQLite to version 3.27.1.
* Various other small enhancements to webpages and documentation.
<a name='v2_7'></a>
<h2>Changes for Version 2.7 (2018-09-22)</h2>
* Add the [./alerts.md|email alerts] feature for commits, ticket
changes, wiki changes, forum posts, and announcements. This is
still a work in progress. It is functional, but it is not as easy to
setup and use as it ought to be.
* Add the [./forum.wiki|discussion forum] feature.
* Add new user capabilities letters needed to support alerts and forum.
Formerly, user capabilities were letters from [a-z], but with the
enhancements, the supply of lower case letters was exhausted.
User capabilities are now letters in [a-zA-Z0-9].
* The built-in skins are now responsive, providing better layout on
small screens, including mobile devices.
* The default skin now includes a hamburger menu that is generated
by the [/sitemap] page.
* All of the built-in skins now use a
[https://en.wikipedia.org/wiki/Content_Security_Policy|Content Security Policy (CSP)]
to help prevent cross-site injection and forgery attacks. There are no known
vulnerabilities in Fossil. The added CSP does not fix anything; it merely adds
another layer of defense.
* The [/sitemap] and other list pages show as multiple columns if
the viewing window is wide enough.
* There is an optional "js" file for each skin that can be used to
|
| ︙ | ︙ |
Changes to www/concepts.wiki.
| ︙ | ︙ | |||
80 81 82 83 84 85 86 | Communication between repositories is via HTTP. Remote repositories are identified by URL. You can also point a web browser at a repository and get human-readable status, history, and tracking information about the project. <h3>2.1 Identification Of Artifacts</h3> | | | | > | > > | < | > | | | | 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 | Communication between repositories is via HTTP. Remote repositories are identified by URL. You can also point a web browser at a repository and get human-readable status, history, and tracking information about the project. <h3>2.1 Identification Of Artifacts</h3> A particular version of a particular file is called an "artifact". Each artifact has a universally unique name which is the <a href="http://en.wikipedia.org/wiki/SHA1">SHA1</a> or <a href="http://en.wikipedia.org/wiki/SHA3">SHA3-256</a> hash of the content of that file expressed as either 40 or 64 characters of lower-case hexadecimal. (See the [./hashpolicy.wiki|hash policy document] for information on which algorithm is used, when.) Such a hash is referred to as the Artifact ID. These hash algorithms were created with Fossil's purpose in mind: to provide a highly forgery-resistant identifier for a blob of data, such as a file. Given any file, it is simple to find the artifact ID for that file. But given an artifact ID, it is computationally intractable to generate a file that will have that same artifact ID. Artifact IDs look something like this: <blockquote><b> 6089f0b563a9db0a6d90682fe47fd7161ff867c8<br> 59712614a1b3ccfd84078a37fa5b606e28434326<br> 19dbf73078be9779edd6a0156195e610f81c94f9<br> |
| ︙ | ︙ |
Changes to www/customskin.md.
1 2 3 4 5 | Theming ======= Every HTML page generated by Fossil has the following basic structure: | < > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | Theming ======= Every HTML page generated by Fossil has the following basic structure: <blockquote><table border=1 cellpadding=10><tbody> <tr><td style='background-color:lightblue;text-align:center;'>Header</td></tr> <tr><td style='background-color:lightgreen;text-align:center;'> Fossil-Generated Content</td></tr> <tr><td style='background-color:lightblue;text-align:center;'>Footer</td></tr> <tr><td style='background-color:lightyellow;text-align:center;'>Javascript (optional)</td></tr> </tbody></table></blockquote> The header and footer control the "look" of Fossil pages. Those two sections can be customized separately for each repository to develop a new theme. The header will normally look something like this: |
| ︙ | ︙ | |||
132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
media="screen" />
</head>
The same TH1 interpreter is used for both the header and the footer
and for all scripts contained within them both. Hence, any global
TH1 variables that are set by the header are available to the footer.
TH1 Variables
-------------
Before expanding the TH1 within the header and footer, Fossil first
initializes a number of TH1 variables to values that depend on
respository settings and the specific page being generated.
| > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
media="screen" />
</head>
The same TH1 interpreter is used for both the header and the footer
and for all scripts contained within them both. Hence, any global
TH1 variables that are set by the header are available to the footer.
Customizing the ≡ Hamburger Menu
--------------------------------
The menu bar of the default skin has an entry to open a drop-down menu with
additional navigation links, represented by the ≡ button (hence the name
"hamburger menu"). The Javascript logic to open and close the hamburger menu
when the button is clicked is contained in the optional Javascript part (js.txt)
of the default skin. Out of the box, the drop-down menu shows the [Site
Map](../../../sitemap), loaded by an AJAX request prior to the first display.
The ≡ button for the hamburger menu is added to the menu bar by the following
TH1 command in the default skin header.txt, right before the menu bar links:
html "<a id='hbbtn' href='#'>☰</a>"
The hamburger button can be repositioned between the other menu links (but the
drop-down menu is always left-aligned with the menu bar), or it can be removed
by deleting the above statement (the Javascript logic detects this case and
remains idle, so it's not necessary to modify the default skin js.txt).
The following empty element at the bottom of the default skin header.txt serves
as the panel to hold the drop-down menu elements:
<div id='hbdrop'></div>
Out of the box, the contents of the panel is populated with the [Site
Map](../../../sitemap), but only if the panel does not already contain any HTML
elements (that is, not just comments, plain text or non-presentational white
space). So the hamburger menu can be customized by replacing the empty `<div
id='hbdrop'></div>` element with a menu structure knitted according to the
following template:
<div id="hbdrop" data-anim-ms="400">
<ul class="columns" style="column-width: 20em; column-count: auto">
<!-- NEW GROUP WITH HEADING LINK -->
<li>
<a href="$home$index_page">Link: Home</a>
<ul>
<li><a href="$home/timeline">Link: Timeline</a></li>
<li><a href="$home/dir?ci=tip">Link: File List</a></li>
</ul>
</li>
<!-- NEW GROUP WITH HEADING TEXT -->
<li>
Heading Text
<ul>
<li><a href="$home/doc/trunk/www/customskin.md">Link: Theming</a></li>
<li><a href="$home/doc/trunk/www/th1.md">Link: TH1 Scripts</a></li>
</ul>
</li>
<!-- NEXT GROUP GOES HERE -->
</ul>
</div>
The custom `data-anim-ms` attribute can be added to the panel element to direct
the Javascript logic to override the default menu animation duration of 400 ms.
A faster animation duration of 80-200 ms may be preferred for smaller menus. The
animation is disabled by setting the attribute to `"0"`.
TH1 Variables
-------------
Before expanding the TH1 within the header and footer, Fossil first
initializes a number of TH1 variables to values that depend on
respository settings and the specific page being generated.
|
| ︙ | ︙ | |||
172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
specified by the Admin/Configuration setup page.
* **current_page** - The name of the page currently being processed,
without the leading "/" and without query parameters.
Examples: "timeline", "doc/trunk/README.txt", "wiki".
* **csrf_token** - A token used to prevent cross-site request forgery.
* **release_version** - The release version of Fossil. Ex: "1.31"
* **manifest_version** - A prefix on the check-in hash of the
specific version of fossil that is running. Ex: "\[47bb6432a1\]"
* **manifest_date** - The date of the source-code check-in for the
| > > > > > > | 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
specified by the Admin/Configuration setup page.
* **current_page** - The name of the page currently being processed,
without the leading "/" and without query parameters.
Examples: "timeline", "doc/trunk/README.txt", "wiki".
* **csrf_token** - A token used to prevent cross-site request forgery.
* **default_csp** - The content to be used within the default header
for the "Content-Security-Policy" meta tag.
* **nonce** - The value of the cryptographic nonce for the request
being processed.
* **release_version** - The release version of Fossil. Ex: "1.31"
* **manifest_version** - A prefix on the check-in hash of the
specific version of fossil that is running. Ex: "\[47bb6432a1\]"
* **manifest_date** - The date of the source-code check-in for the
|
| ︙ | ︙ |
Changes to www/delta_format.wiki.
1 | <title>Fossil Delta Format</title> | < | | | | > > | > > > > | > > > > > > | > > | > > | > > | > > | > > > | > > > | > > > | > > | > > > > > > > > | | | | | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 |
<title>Fossil Delta Format</title>
<h1>1.0 Overview</h1>
<p>Fossil achieves efficient storage and low-bandwidth synchronization
through the use of delta-compression. Instead of storing
or transmitting the complete content of an artifact, Fossil stores or
transmits only the changes relative to a related artifact.
</p>
<p>This document describes the delta-encoding format used by Fossil.
The intended audience is developers working on either
<a href="index.wiki">Fossil</a> itself, or on tools compatible with
Fossil. Understanding of this document is <em>not</em> required for
ordinary users of Fossil. This document is an implementation detail.</p>
<p>This document only describes the delta file format. A
[./delta_encoder_algorithm.wiki|separate document] describes one possible
algorithm for generating deltas in this format.</p>
<h2>1.1 Sample Software And Analysis Tools</h2>
<p>The core routines used to generate and apply deltas in this format
are contained in the [../src/delta.c|delta.c] source file. Interface
logic, including "test-*" commands to directly manipulate deltas are
contained in the [../src/deltacmd.c|deltacmd.c] source file. SQL functions
to create, apply, and analyze deltas are implemented by code in the
[../src/deltafunc.c|deltafunc.c] source file.
<p>The following command-line tools are available to create and apply
deltas and to test the delta logic:
* [/help?cmd=test-delta|fossil test-delta] → Run self-tests of
the delta logic
* [/help?cmd=test-delta-create|fossil test-delta-create X Y] → compute
a delta that converts file X into file Y. Output that delta.
* [/help?cmd=test-delta-apply|fossil test-delta-apply X D] → apply
delta D to input file X and output the result.
* [/help?cmd=test-delta-analyze|fossil test-delta-analyze X Y] → compute
and delta that converts file X into file Y but instead of writing the
delta to output, write performance information about the delta.
<p>When running the [/help?cmd=sqlite3|fossil sql] command to get an
interactive SQL session connected to the repository, the following
additional SQL functions are provided:
* <b>delta_create(</b><i>X</i><b>,</b><i>Y</i><b>)</b> →
Compute a data that carries blob X into blob Y and return that delta
as a blob.
* <b>delta_apply(</b><i>X</i><b>,</b><i>D</i><b>)</b> →
Apply delta D to input blob X return a new blob which is the result.
* <b>delta_output_size(</b><i>D</i>)</b> →
Return the size of the output that would result from applying delta D.
* <b>delta_parse(</b><i>D</i>)</b> → This is a table-valued function
that returns one row for the header, for the trailer, and for each segment
in delta D.
<a name="structure"></a><h1>2.0 Structure</h1>
<img src="delta1.gif" align="left" hspace="10">
<p>A delta consists of three parts, a "header", a "trailer", and a
"segment-list" between them.</p>
<p>Both header and trailer provide information about the target
helping the decoder, and the segment-list describes how the target can
be constructed from the original.</p>
<a name="header"></a><h2>2.1 Header</h2>
<img src="delta6.gif" align="left" hspace="10">
<p>The header consists of a single number followed by a newline
character (ASCII 0x0a). The number is the length of the target in
bytes.</p>
<p>This means that, given a delta, the decoder can compute the size of
the target (and allocate any necessary memory based on that) by simply
reading the first line of the delta and decoding the number found
there. In other words, before it has to decode everything else.</p>
<a name="trailer"></a><h2>2.2 Trailer</h2>
<img src="delta5.gif" align="left" hspace="10">
<p>The trailer consists of a single number followed by a semicolon (ASCII
0x3b). This number is a checksum of the target and can be used by a
decoder to verify that the delta applied correctly, reconstructing the
target from the original.</p>
<p>The checksum is computed by treating the target as a series of
32-bit integer numbers (MSB first), and summing these up, modulo
2^32-1. A target whose length is not a multiple of 4 is padded with
0-bytes (ASCII 0x00) at the end.</p>
<p>By putting this information at the end of the delta a decoder has
it available immediately after the target has been reconstructed
fully.</p>
<a name="slist"></a><h2>2.3 Segment-List</h2>
<img src="delta2.gif" align="left" hspace="10">
<p>The segment-list of a delta describes how to create the target from
the original by a combination of inserting literal byte-sequences and
copying ranges of bytes from the original. This is where the
compression takes place, by encoding the large common parts of
original and target in small copy instructions.</p>
<p>The target is constructed from beginning to end, with the data
generated by each instruction appended after the data of all previous
instructions, with no gaps.</p>
<a name="insertlit"></a><h3>2.3.1 Insert Literal</h3>
<p>A literal is specified by two elements, the size of the literal in
bytes, and the bytes of the literal itself.</p>
<img src="delta4.gif" align="left" hspace="10">
<p>The length is written first, followed by a colon character (ASCII
0x3a), followed by the bytes of the literal.</p>
<a name="copyrange"></a><h3>2.3.2 Copy Range</h3>
<p>A range to copy is specified by two numbers, the offset of the
first byte in the original to copy, and the size of the range, in
bytes. The size zero is special, its usage indicates that the range
extends to the end of the original.</p>
<img src="delta3.gif" align="left" hspace="10">
<p>The length is written first, followed by an "at" character (ASCII
0x40), then the offset, followed by a comma (ASCII 0x2c).</p>
<a name="intcoding"></a><h1>3.0 Encoding of integers</h1>
<p>
The format currently handles only 32 bit integer numbers. They are
written base-64 encoded, MSB first, and without leading
"0"-characters, except if they are significant (i.e. 0 => "0").
</p>
<p>
The base-64 coding is described in
<a href="http://www.ietf.org/rfc/rfc3548.txt">RFC 3548</a>.
</p>
<a name="examples"></a><h1>4.0 Examples</h1>
<a name="examplesint"></a><h2>4.1 Integer encoding</h2>
<table border=1>
<tr>
<th>Value</th>
<th>Encoding</th>
</tr>
<tr>
<td>0</td>
<td>0</td>
</tr>
<tr>
<td>6246</td>
<td>1Xb</td>
</tr>
<tr>
<td>-1101438770</td>
<td>2zMM3E</td>
</tr>
</table>
<a name="examplesdelta"></a><h2>4.2 Delta encoding</h2>
<p>An example of a delta using the specified encoding is:</p>
<table border=1><tr><td><pre>
1Xb
4E@0,2:thFN@4C,6:scenda1B@Jd,6:scenda5x@Kt,6:pieces79@Qt,F: Example: eskil~E@Y0,2zMM3E;</pre>
</td></tr></table>
|
| ︙ | ︙ | |||
204 205 206 207 208 209 210 | * Ticketing interface (expand this bullet) </pre></td></tr></table> | | | 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 | * Ticketing interface (expand this bullet) </pre></td></tr></table> <a name="notes"></a><h1>Notes</h1> <ul> <li>Pure text files generate a pure text delta. </li> <li>Binary files generate a delta that may contain some binary data. </li> <li>The delta encoding does not attempt to compress the content. It was considered to be much more sensible to do compression using a separate general-purpose compression library, like <a href="http://www.zlib.net">zlib</a>. </li> </ul> |
Changes to www/env-opts.md.
| ︙ | ︙ | |||
29 30 31 32 33 34 35 | global setting should be used to force the case sensitivity to the most sensible condition. `--chdir DIRECTORY`: Change to the named directory before processing any commands. | > | | 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 | global setting should be used to force the case sensitivity to the most sensible condition. `--chdir DIRECTORY`: Change to the named directory before processing any commands. `--comfmtflags NUMBER` `--comment-format NUMBER`: Specify flags that control how check-in comments and certain other text outputs are formatted for display. The flags are individual bits in `NUMBER`, which must be specified in base 10: * _0_ — Uses the revised algorithm with no special handling. * _1_ — Uses the legacy algorithm, other flags are ignored. |
| ︙ | ︙ | |||
153 154 155 156 157 158 159 160 161 162 163 164 165 166 | `FOSSIL_USER`: Name of the default user account if the checkout, local or global `default-user` setting is not present. The first environment variable found in the environment from the list `FOSSIL_USER`, `USER`, `LOGNAME`, and `USERNAME` is the user name. If none of those are set, then the default user name is "root". See the discussion of Fossil Username below for a lot more detail. `FOSSIL_TCL_PATH`: When Tcl stubs support is configured, point to a specific file or folder containing the version of Tcl to load at run time. `FOSSIL_TEMP`: Fallback location of the temporary directories and files created and deleted when running the test suite. The first environment | > > > > > > > > > > > > | 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 | `FOSSIL_USER`: Name of the default user account if the checkout, local or global `default-user` setting is not present. The first environment variable found in the environment from the list `FOSSIL_USER`, `USER`, `LOGNAME`, and `USERNAME` is the user name. If none of those are set, then the default user name is "root". See the discussion of Fossil Username below for a lot more detail. `FOSSIL_SECURITY_LEVEL`: If set to any of the values listed below, additional measures for password security will be enabled (also see [How To Use Encrypted Repositories][encryptedrepos.wiki]): [encryptedrepos.wiki]: /doc/trunk/www/encryptedrepos.wiki * _≥1_ — Do not remember passwords. * _≥2_ — Use a scrambled matrix for password input. `FOSSIL_TCL_PATH`: When Tcl stubs support is configured, point to a specific file or folder containing the version of Tcl to load at run time. `FOSSIL_TEMP`: Fallback location of the temporary directories and files created and deleted when running the test suite. The first environment |
| ︙ | ︙ |
Changes to www/fileformat.wiki.
| ︙ | ︙ | |||
527 528 529 530 531 532 533 | [#wikichng | wiki artifact]. The <b>Z</b> card is the required checksum over the rest of the artifact. <a name="forum"></a> <h3>2.8 Forum Posts</h3> | | | | 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 | [#wikichng | wiki artifact]. The <b>Z</b> card is the required checksum over the rest of the artifact. <a name="forum"></a> <h3>2.8 Forum Posts</h3> Forum posts are intended as a mechanism for users and developers to discuss a project. Forum posts are like messages on a mailing list. The following cards are allowed on an forum post artifact: <blockquote> <b>D</b> <i>time-and-date-stamp</i><br /> <b>G</b> <i>thread-root</i><br /> <b>H</b> <i>thread-title</i><br /> <b>I</b> <i>in-reply-to</i><br /> <b>N</b> <i>mimetype</i><br /> <b>P</b> <i>parent-artifact-id</i><br /> <b>U</b> <i>user-name</i><br /> <b>W</b> <i>size</i> <b>\n</b> <i>text</i> <b>\n</b><br /> <b>Z</b> <i>checksum</i> </blockquote> Every forum post must have either one <b>I</b> card and one <b>G</b> card or one <b>H</b> card. Forum posts are organized into topic threads. The initial post for a thread (the root post) has an <b>H</b> card giving the title or subject for that thread. The argument to the <b>H</b> card is a string in the same format as a comment string in a <b>C</b> card. All follow-up posts have an <b>I</b> card that indicates which prior post in the same thread the current forum post is replying to, and a <b>G</b> card specifying the root post for |
| ︙ | ︙ |
Changes to www/forum.wiki.
| ︙ | ︙ | |||
64 65 66 67 68 69 70 |
nearest wifi router or cellular data tower.
* <b>Interlink with Other Fossil-Managed Artifacts:</b> Because forum
posts are normal Fossil artifacts, you can interlink them with
other Fossil artifacts using short internal links: link to forum
threads from a [./tickets.wiki | ticket], link to a wiki document
from a forum post, etc.
| | | 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
nearest wifi router or cellular data tower.
* <b>Interlink with Other Fossil-Managed Artifacts:</b> Because forum
posts are normal Fossil artifacts, you can interlink them with
other Fossil artifacts using short internal links: link to forum
threads from a [./tickets.wiki | ticket], link to a wiki document
from a forum post, etc.
* <b>Durable Links:</b> Once you create a valid internal artifact
link in Fossil, it <em>remains</em> valid, durably. With
third-party forum software and mailing list search engines, your
links are only valid until the third-party component changes its
URL scheme or disappears from the web.
* <b>Role-Based Access Control:</b> The forum uses the same
|
| ︙ | ︙ |
Changes to www/fossil-v-git.wiki.
| ︙ | ︙ | |||
178 179 180 181 182 183 184 | Fossil says that the unix philosophy is "it just works". Both individuals have written their DVCSes to reflect their own view of the "unix philosophy". <h3>2.6 One vs. Many Check-outs per Repository</h3> A "repository" in Git is a pile-of-files in the ".git" subdirectory | | > > > > > > > > > | 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | Fossil says that the unix philosophy is "it just works". Both individuals have written their DVCSes to reflect their own view of the "unix philosophy". <h3>2.6 One vs. Many Check-outs per Repository</h3> A "repository" in Git is a pile-of-files in the ".git" subdirectory of a single check-out. The check-out and the repository are located together in the filesystem. With Fossil, a "repository" is a single SQLite database file that can be stored anywhere. There can be multiple active check-outs from the same repository, perhaps open on different branches or on different snapshots of the same branch. Long-running tests or builds can be running in one check-out while changes are being committed in another. (Update 2019-01-29:) The check-out and the repository in Git used to be inseparable. More recently, Git has been enhanced with the "[https://git-scm.com/docs/git-worktree|worktree]" command that allows a single repository to host multiple check-outs. However, the interface is sufficiently difficult to use that most people find it easier to create a separate clone for each check-out. <h3>2.7 What you should have done vs. What you actually did</h3> Git puts a lot of emphasis on maintaining a "clean" check-in history. Extraneous and experimental branches by individual developers often never make it into the main repository. And branches are often rebased before being pushed, to make it appear as if development had been linear. Git strives to record what |
| ︙ | ︙ |
Changes to www/hashpolicy.wiki.
1 2 | <title>Hash Policy</title> | > > | | > | | | | | > | | | | | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 | <title>Hash Policy</title> <h2>Executive Summary</h2> <b>Or: How To Avoid Reading This Article</b> There was much angst over the [http://www.shattered.io|SHAttered attack] against SHA1 when it was announced in early 2017. If you are concerned about this and its implications for Fossil, simply [./quickstart.wiki#install|upgrade to Fossil 2.1 or later], and the problem will go away. Everything will continue to work as before. All of your legacy repositories will continue to work, and all of your old check-ins will still have the same name. Your workflow will be unchanged. But if you are curious and want a deeper understanding of what is going on, read on... <h2>Introduction</h2> The first snapshot-based distributed version control system was [http://www.monotone.ca|Monotone]. Many of the ideas behind the design of Fossil were copied from Monotone, including the use of a SHA1 hash to assign names to artifacts. Git and Mercurial did the same thing. The SHA1 hash algorithm is used only to create names for artifacts in Fossil (and in Git, Mercurial, and Monotone). It is not used for security. Nevertheless, when the [http://www.shattered.io|SHAttered attack] found two different PDF files with the same SHA1 hash, many users learned that "SHA1 is broken". They see that Fossil (and Git, Mercurial, and Monotone) use SHA1 and they therefore conclude that "Fossil is broken". This is not true, but it is a public relations problem. So the decision was made to migrate Fossil away from SHA1. This article describes how that migration is occurring. <h2>Use Of Hardened SHA1</h2> In Fossil version 2.0 ([/timeline?c=version-2.0|2017-03-03]), the internal SHA1 implementation was changed from a generic FIPS PUB 180-4 SHA1 implementation to a "Hardened SHA1" [[https://github.com/cr-marcstevens/sha1collisiondetection|1]] [[https://marc-stevens.nl/research/papers/C13-S.pdf|2]]. The Hardened SHA1 algorithm automatically detects when the artifact being hashed is specifically designed to exploit the known weaknesses in the SHA1 algorithm, and when it detects such an attack it changes the hash algorithm (by increasing the number of rounds in the compression function) to make the algorithm secure again. If the attack detection gets a false possible, that means that Hardened SHA1 will get a different answer than the standard FIPS PUB 180-4 SHA1, but the creators of Hardened SHA1 (see the second paper [[https://marc-stevens.nl/research/papers/C13-S.pdf|2]]) report that the probability of a false positive is vanishingly small - less than 1 false positive out of 10<sup><font size=1>27</font></sup> hashes. Hardened SHA1 is slower (and a lot bigger) but Fossil does not do that much hashing, so performance is not really an issue. All versions of Fossil moving forward will use Hardened SHA1. So if someone says "SHA1 is broken, and Fossil uses SHA1, therefore Fossil is broken," you can rebut the argument by pointing out that Fossil uses <em>Hardened SHA1</em>, not generic SHA1, and Hardened SHA1 is <em>not</em> broken. <h2>Support For SHA3-256</h2> Prior to Fossil version 2.0 ([/timeline?c=version-2.0|2017-03-03]), all artifacts in all Fossil repositories were named by only a SHA1 hash. Version 2.0 extended the [./fileformat.wiki|Fossil file format] to allow artifacts to be named by either SHA1 or SHA3-256 hashes. (SHA3-256 is the only variant of SHA3 that Fossil uses for artifact naming, so for the remainder of this article it will be called simply "SHA3". Similarly, "Hardened SHA1" will shortened to "SHA1" in the remaining text.) Other than permitting the use of SHA3 in addition to SHA1, there were no file format changes in Fossil version 2.0 relative to the previous version 1.37. Both Fossil 2.0 and Fossil 1.37 read and write all the same repositories and sync with one another, as long as none of the repositories contain artifacts named using SHA3. If a repository does contain artifacts named using SHA3, Fossil 1.37 will |
| ︙ | ︙ | |||
162 163 164 165 166 167 168 | Of course, if some members of your team stubbornly refuse to upgrade past Fossil 1.37, you should avoid changing the hash policy and creating artifacts with SHA3 names, because once you do that your recalcitrant coworkers will no longer be able to collaborate. <h2>A Pure SHA3 Future</h2> | | | | | | > > > > > > > > > > > | 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 | Of course, if some members of your team stubbornly refuse to upgrade past Fossil 1.37, you should avoid changing the hash policy and creating artifacts with SHA3 names, because once you do that your recalcitrant coworkers will no longer be able to collaborate. <h2>A Pure SHA3 Future</h2> At some point in the future, after everybody has finally upgraded to Fossil 2.0 or later, the default hash policy will probably change to "sha3", or maybe even "shun-sha1". By the time that happens, you will probably already be using SHA3 on all your projects and so you are unlikely to notice. This probably won't happen until after all of the operating systems shipping binary versions of Fossil switch to Fossil 2.1 or later. The big standout is Debian Stretch (v9), since it still ships Fossil 1.37 due to its stable packages policy. Stretch will be the current release version of Debian until "some time mid 2019" according to [https://lists.debian.org/debian-devel-announce/2018/04/msg00006.html|the latest published release plan]. Debian Stretch will then remain in support until June 2022. We do not yet know if we will switch the default hash policy during that window or not until after Stretch is fully out of support. |
Added www/image-format-vs-repo-size.ipynb.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Image Format vs Fossil Repository Size\n",
"\n",
"## Prerequisites\n",
"\n",
"This notebook was developed with [JupyterLab][jl]. To follow in my footsteps, install that and the needed Python packages:\n",
"\n",
" $ pip install jupyterlab matplotlib pandas wand\n",
"\n",
"In principle, it should also work with [Anaconda Navigator][an], but because [Wand][wp] is not currently in the Anaconda base package set, you may run into difficulties making it work, as we did on macOS. These problems might not occur on Windows or Linux.\n",
"\n",
"This notebook uses the Python 2 kernel because macOS does not include Python 3, and we don't want to make adding that a prerequisite for those re-running this experiement on their own macOS systems. The code was written with Python 3 syntax changes in mind, but we haven't yet successfully tried it in a Python 3 Jupyter kernel.\n",
"\n",
"[an]: https://www.anaconda.com/distribution/\n",
"[jl]: https://github.com/jupyterlab/\n",
"[wp]: http://wand-py.org/\n",
"\n",
"\n",
"## Running\n",
"\n",
"The next cell generates the test repositories. This takes about 45 seconds to run, primarily due to the `sleep 1` synchronization call, made 40 times in the main test loop.\n",
"\n",
"The one after that produces the bar chart from the collected data, all but instantaneously.\n",
"\n",
"This split allows you to generate the expensive experimental data in a single pass, then play as many games as you like with the generated data.\n",
"\n",
"\n",
"## Discussion\n",
"\n",
"That is kept in [a separate document](image-format-vs-repo-size.md) so we can share that document with Fossil's Markdown renderer."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import random\n",
"import time\n",
"\n",
"from wand.color import Color\n",
"from wand.drawing import Drawing\n",
"from wand.image import Image\n",
"\n",
"import pandas as pd\n",
"\n",
"size = 256\n",
"iterations = 10\n",
"start = time.time()\n",
"repo_sizes = []\n",
"\n",
"formats = ['JPEG', 'BMP', 'TIFF', 'PNG']\n",
"for f in formats:\n",
" ext = f.lower()\n",
" tdir = 'test' + '-' + ext\n",
" repo = tdir + '.fossil'\n",
" ifn = 'test.' + ext\n",
" ipath = os.path.join(tdir, ifn)\n",
" rs = []\n",
" \n",
" def add_repo_size():\n",
" rs.append(os.path.getsize(repo) / 1024.0 / 1024.0)\n",
"\n",
" try:\n",
" # Create test repo\n",
" if not os.path.exists(tdir): os.mkdir(tdir, 0o700)\n",
" cmd = 'cd {0} ; fossil init ../{1} && fossil open ../{1} && fossil set binary-glob \"*.{2}\"'.format(\n",
" tdir, repo, ext\n",
" )\n",
" if os.system(cmd) != 0:\n",
" raise RuntimeError('Failed to create test repo ' + repo)\n",
" add_repo_size()\n",
"\n",
" # Create test image and add it to the repo\n",
" img = Image(width = size, height = size, depth = 8,\n",
" background = 'white')\n",
" img.alpha_channel = 'remove'\n",
" img.evaluate('gaussiannoise', 1.0)\n",
" img.save(filename = ipath)\n",
" cmd = 'cd {0} ; fossil add {1} && fossil ci -m \"initial\"'.format(\n",
" tdir, ifn\n",
" )\n",
" if os.system(cmd) != 0:\n",
" raise RuntimeError('Failed to add ' + ifn + ' to test repo')\n",
" #print \"Created test repo \" + repo + \" for format \" + f + \".\"\n",
" add_repo_size()\n",
"\n",
" # Change a random pixel to a random RGB value and check it in\n",
" # $iterations times.\n",
" for i in range(iterations):\n",
" with Drawing() as draw:\n",
" x = random.randint(0, size - 1)\n",
" y = random.randint(0, size - 1)\n",
"\n",
" r = random.randint(0, 255)\n",
" g = random.randint(0, 255)\n",
" b = random.randint(0, 255)\n",
" \n",
" draw.fill_color = Color('rgb({0},{1},{2})'.format(\n",
" r, g, b\n",
" ))\n",
" draw.color(x, y, 'point')\n",
" draw(img)\n",
" img.save(filename = ipath)\n",
" \n",
" # ImageMagick appears to use some kind of asynchronous\n",
" # file saving mechanism, so we have to give it time to\n",
" # complete.\n",
" time.sleep(1.0)\n",
" \n",
" cmd = 'cd {0} ; fossil ci -m \"change {1} step {2}\"'.format(\n",
" tdir, f, i\n",
" )\n",
" if os.system(cmd) != 0:\n",
" raise RuntimeError('Failed to change ' + f + ' image, step ' + str(i))\n",
" add_repo_size()\n",
" \n",
" # Repo complete for this format\n",
" repo_sizes.append(pd.Series(rs, name=f))\n",
"\n",
" finally:\n",
" if os.path.exists(ipath): os.remove(ipath)\n",
" if os.path.exists(tdir):\n",
" os.system('cd ' + tdir + ' ; fossil close -f')\n",
" os.rmdir(tdir)\n",
" if os.path.exists(repo): os.remove(repo)\n",
" \n",
"print(\"Experiment completed in \" + str(time.time() - start) + \" seconds.\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"%config InlineBackend.figure_formats = ['svg']\n",
"\n",
"import matplotlib as mpl\n",
"import matplotlib.pyplot as plt\n",
"\n",
"# Merge per-format test data into a single DataFrame without the first\n",
"# first 3 rows: the initial empty repo state (boring) and the repo DB\n",
"# size as it \"settles\" in its first few checkins.\n",
"data = pd.concat(repo_sizes, axis=1).drop(range(3))\n",
"\n",
"mpl.rcParams['figure.figsize'] = (6, 4)\n",
"ax = data.plot(kind = 'bar', colormap = 'coolwarm',\n",
" grid = False, width = 0.8,\n",
" edgecolor = 'white', linewidth = 2)\n",
"ax.axes.set_xlabel('Checkin index')\n",
"ax.axes.set_ylabel('Repo size (MiB)')\n",
"plt.savefig('image-format-vs-repo-size.svg', transparent=True)\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 2",
"language": "python",
"name": "python2"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.15"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
|
Added www/image-format-vs-repo-size.md.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
# Image Format vs Fossil Repo Size
## The Problem
Fossil has a [delta compression][dc] feature which removes redundant
information from a file — with respect to the version checked in at the
tip of the current working branch — when checking in a subsequent
version.¹ That delta is then [zlib][zl]-compressed before being stored
in the Fossil repository database file.
Storing pre-compressed data files in a Fossil repository defeats both of
these space-saving measures:
1. Binary data compression algorithms — whether lossless as with zlib
or lossy as with JPEG — turn the file data into [pseudorandom
noise][prn].²
Typical data compression algorithms are not [hash functions][hf],
where the goal is that a change to each bit in the input has a
statistically even chance of changing every bit in the output, but
because they do approach that pathological condition, pre-compressed
data tends to defeat Fossil’s delta compression algorithm, there
being so little correlation between two different outputs from the
binary data compression algorithm.
2. An ideal lossless binary data compression algorithm cannot be
applied more than once to make the data even smaller, since random
noise is incompressible. The consequence for our purposes here is
that pre-compressed data doesn’t benefit from Fossil’s zlib
compression.
You might then ask, what does it matter if the space savings comes from
the application file format (e.g. JPEG, Zip, etc.) or from Fossil
itself? It really doesn’t, as far as point 2 above goes, but point 1
causes the Fossil repository to balloon out of proportion to the size of
the input data change on each checkin. This article will illustrate that
problem, quantify it, and give a solution to it.
[dc]: ./delta_format.wiki
[hf]: https://en.wikipedia.org/wiki/Hash_function
[prn]: https://en.wikipedia.org/wiki/Pseudorandomness
[zl]: http://www.zlib.net/
## Affected File Formats
In this article’s core experiment, we use 2D image file formats, but
this article’s advice also applies to many other file types. For just a
few examples out of what must be thousands:
* **Microsoft Office**: The [OOXML document format][oox] used from
Office 2003 onward (`.docx`, `.xlsx`, `.pptx`, etc.) are Zip files
containing an XML document file and several collateral files.
* **Libre Office**: Its [ODF][odf] format is designed in more or less
the same way as OOXML.
* **Java**: A Java [`.jar` file][jcl] is a Zip file containing JVM
`.class` files, manifest files, and more.
* **Windows Installer:** An [`*.msi` file][wi] is a proprietary
database format that contains, among other things, [Microsoft
Cabinet][cab]-compressed files, which in turn may hold Windows
executables, which [may themselves be compressed][exc].
* **SVG, PDF, TIFF, etc.**: Many file formats are available in both
compressed and uncompressed forms. You should use the uncompressed
form with Fossil wherever practical, as we will show below.
[cab]: https://en.wikipedia.org/wiki/Cabinet_(file_format)
[exc]: https://en.wikipedia.org/wiki/Executable_compression
[jcl]: https://en.wikipedia.org/wiki/Java_(programming_language)
[odf]: https://en.wikipedia.org/wiki/OpenDocument
[oox]: https://en.wikipedia.org/wiki/Office_Open_XML
[wi]: https://en.wikipedia.org/wiki/Windows_Installer
## Demonstration
The companion `image-format-vs-repo-size.ipynb` file ([download][nbd],
[preview][nbp]) is a [Jupyter][jp] notebook implementing the following
experiment:
1. Create an empty Fossil repository; save its initial size.
2. Use [ImageMagick][im] via [Wand][wp] to generate a JPEG file of a
particular size — currently 256 px² — filled with Gaussian noise to
make data compression more difficult than with a solid-color image.
3. Check that image into the new Fossil repo, and remember that size.
4. Change a random pixel in the image to a random RGB value, save that
image, check it in, and remember the new Fossil repo size.
5. Iterate on step 4 some number of times — currently 10 — and remember
the Fossil repo size at each step.
6. Repeat the above steps for BMP, TIFF,³ and PNG.
7. Create a bar chart showing how the Fossil repository size changes
with each checkin.
We chose to use Jupyter for this because it makes it easy for you to
modify the notebook to try different things. Want to see how the
results change with a different image size? Easy, change the `size`
value in the second cell of the notebook. Want to try more image
formats? You can put anything ImageMagick can recognize into the
`formats` list. Want to find the break-even point for images like those
in your own respository? Easily done with a small amount of code.
[im]: https://www.imagemagick.org/
[jp]: https://jupyter.org/
[nbd]: ./image-format-vs-repo-size.ipynb
[nbp]: https://nbviewer.jupyter.org/urls/fossil-scm.org/fossil/doc/trunk/www/image-format-vs-repo-size.ipynb
[wp]: http://wand-py.org/
## Results
Running the notebook gives a bar chart something like⁴ this:

There are a few key things we want to draw your attention to in that
chart:
* BMP and uncompressed TIFF are nearly identical in size for all
checkins, and the repository growth rate is negligible.⁵ We owe this
economy to Fossil’s delta compression feature.
* The JPEG and TIFF bars increase by large amounts on most checkins
even though each checkin encodes only a *single-pixel change*!
* Because JPEG’s lossy nature allows it to start smaller and have
smaller size increases than than PNG, the crossover point with
BMP/TIFF isn’t until 7-9 checkins in typical runs of this [Monte
Carlo experiment][mce]. Given a choice among these four file
formats and a willingness to use lossy image compression, a rational
tradeoff is to choose JPEG for repositories where each image will
change fewer than that number of times.
[mce]: https://en.wikipedia.org/wiki/Monte_Carlo_method
## Automated Recompression
Since programs that produce and consume binary-compressed data files
often make it either difficult or impossible to work with the
uncompressed form, we want an automated method for producing the
uncompressed form to make Fossil happy while still having the compressed
form to keep our content creation applications happy. This `Makefile`
should⁶ do that for BMP, PNG, SVG, and XLSX files:
.SUFFIXES: .bmp .png .svg .svgz
.svgz.svg:
gzip -dc < $< > $@
.svg.svgz:
gzip -9c < $< > $@
.bmp.png:
convert -quality 95 $< $@
.png.bmp:
convert $< $@
SS_FILES := $(wildcard spreadsheet/*)
all: $(SS_FILES) illus.svg image.bmp doc-big.pdf
reconstitute: illus.svgz image.png
( cd spreadsheet ; zip -9 ../spreadsheet.xlsx) * )
qpdf doc-big.pdf doc-small.pdf
$(SS_FILES): spreadsheet.xlsx
unzip $@ -d $<
doc-big.pdf: doc-small.pdf
qpdf --stream-data=uncompress $@ $<
This `Makefile` allows you to treat the compressed version as the
process input, but to actually check in only the changes against the
uncompressed version by typing “`make`” before “`fossil ci`”. This is
not actually an extra step in practice, since if you’ve got a
`Makefile`-based project, you should be building (and testing!) it
before checking each change in anyway!
Because this technique is based on dependency rules, only the necessary
files are generated on each `make` command.
You only have to run “`make reconstitute`” *once* after opening a fresh
Fossil checkout to produce those compressed sources. After that, you
work with the compressed files in your content creation programs. Your
build system might include some kind of bootstrapping or
auto-configuration step that you could attach this to, so that it
doesn’t need to be run by hand.
This `Makefile` illustrates two primary strategies:
### Input and Ouput File Formats Differ by Extension
In the case of SVG and the bitmap image formats, the file name extension
differs between the cases, so we can use `make` suffix rules to get the
behavior we want. The top half of the `Makefile` just tells `make` how
to map from `*.svg` to `*.svgz` and vice versa, and the same for `*.bmp`
to/from `*.png`.
### Input and Output Use the Same Extension
We don’t have that luxury for Excel and PDF files, each for a different
reason:
* **Excel:** Excel has no way to work with the unpacked Zip file
contents at all, so we have to unpack it into a subdirectory, which
is what we check into Fossil. On making a fresh Fossil checkout, we
have to pack that subdirectory’s contents back up into an `*.xlsx`
file with “`make reconstitute`” so we can edit it with Excel again.
* **PDF:** All PDF readers can display an uncompressed PDF file, but
many PDF-*producing* programs have no option for uncompressed
output. Since the file name extension is the same either way, we
treat the compressed PDF as the source to the process, yielding an
automatically-uncompressed PDF for the benefit of Fossil. Unlike
with the Excel case, there is no simple “file base name to directory
name” mapping, so we just created the `-big` to `-small` name scheme
here.
----
## Footnotes and Digressions
1. Several other programs also do delta compression, so they’ll also be
affected by this problem: [rsync][rs], [Unison][us], [Git][git],
etc. When using file copying and synchronization programs *without*
delta compression, it’s best to use the most highly-compressed file
format you can tolerate, since they copy the whole file any time any
bit of it changes.
2. In fact, a good way to gauge the effectiveness of a given
compression scheme is to run its output through the same sort of
tests we use to gauge how “random” a given [PRNG][prng] is. Another
way to look at it is that if there is a discernible pattern in the
output of a compression scheme, it’s information that could be
further compressed.
3. We're using *uncompressed* TIFF here, not [LZW][lzw]- or
Zip-compressed TIFF, either of which would give similar results to
PNG, which is always zlib-compressed.
4. The raw data changes somewhat from one run to the next due to the
use of random noise in the image to make the zlib/PNG compression
more difficult, and the random pixel changes. Those test design
choices make this a [Monte Carlo experient][mce]. We’ve found that
the overall character of the results doesn’t change from one run to
the next.
The code in the notebook’s third cell drops the first three columns
of data because the first column (the empty repository size) is
boring, and the subsequent two checkins show the SQLite DB file
format settling in with its first few checkins. There’s a single
line in the notebook you can comment out to get a bar chart with
these data included.
If you do this, you’ll see a mildly interesting result: the size of
the first checkin in the BMP and TIFF cases is roughly the same as
that for the PNG case, because both PNG and Fossil use the zlib
binary data compression algorithm.
5. A low-tech format like BMP will have a small edge in practice
because TIFF metadata includes the option for multiple timestamps,
UUIDs, etc., which bloat the checkin size by creating many small
deltas. If you don't need the advantages of TIFF, a less capable
image file format will give smaller checkin sizes for a given amount
of change.
6. The `Makefile` above is not battle-tested. Please report bugs and
needed extensions [on the forum][for].
[for]: https://fossil-scm.org/forum/forumpost/15e677f2c8
[git]: https://git-scm.com/
[lzw]: https://en.wikipedia.org/wiki/Lempel%E2%80%93Ziv%E2%80%93Welch
[prng]: https://en.wikipedia.org/wiki/Pseudorandom_number_generator
[rs]: https://rsync.samba.org/
[us]: http://www.cis.upenn.edu/~bcpierce/unison/
|
Added www/image-format-vs-repo-size.svg.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 |
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Created with matplotlib (http://matplotlib.org/) -->
<svg height="288pt" version="1.1" viewBox="0 0 432 288" width="432pt" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<style type="text/css">
*{stroke-linecap:butt;stroke-linejoin:round;}
</style>
</defs>
<g id="figure_1">
<g id="patch_1">
<path d="M 0 288
L 432 288
L 432 0
L 0 0
z
" style="fill:none;"/>
</g>
<g id="axes_1">
<g id="patch_2">
<path d="M 54 256.32
L 388.8 256.32
L 388.8 34.56
L 54 34.56
z
" style="fill:none;"/>
</g>
<g id="patch_3">
<path clip-path="url(#pbb31c7aa29)" d="M 63 256.32
L 70.2 256.32
L 70.2 175.617561
L 63 175.617561
z
" style="fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_4">
<path clip-path="url(#pbb31c7aa29)" d="M 99 256.32
L 106.2 256.32
L 106.2 165.315122
L 99 165.315122
z
" style="fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_5">
<path clip-path="url(#pbb31c7aa29)" d="M 135 256.32
L 142.2 256.32
L 142.2 148.14439
L 135 148.14439
z
" style="fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_6">
<path clip-path="url(#pbb31c7aa29)" d="M 171 256.32
L 178.2 256.32
L 178.2 141.276098
L 171 141.276098
z
" style="fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_7">
<path clip-path="url(#pbb31c7aa29)" d="M 207 256.32
L 214.2 256.32
L 214.2 139.559024
L 207 139.559024
z
" style="fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_8">
<path clip-path="url(#pbb31c7aa29)" d="M 243 256.32
L 250.2 256.32
L 250.2 137.841951
L 243 137.841951
z
" style="fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_9">
<path clip-path="url(#pbb31c7aa29)" d="M 279 256.32
L 286.2 256.32
L 286.2 136.983415
L 279 136.983415
z
" style="fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_10">
<path clip-path="url(#pbb31c7aa29)" d="M 315 256.32
L 322.2 256.32
L 322.2 125.822439
L 315 125.822439
z
" style="fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_11">
<path clip-path="url(#pbb31c7aa29)" d="M 351 256.32
L 358.2 256.32
L 358.2 119.812683
L 351 119.812683
z
" style="fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_12">
<path clip-path="url(#pbb31c7aa29)" d="M 70.2 256.32
L 77.4 256.32
L 77.4 135.266341
L 70.2 135.266341
z
" style="fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_13">
<path clip-path="url(#pbb31c7aa29)" d="M 106.2 256.32
L 113.4 256.32
L 113.4 135.266341
L 106.2 135.266341
z
" style="fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_14">
<path clip-path="url(#pbb31c7aa29)" d="M 142.2 256.32
L 149.4 256.32
L 149.4 135.266341
L 142.2 135.266341
z
" style="fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_15">
<path clip-path="url(#pbb31c7aa29)" d="M 178.2 256.32
L 185.4 256.32
L 185.4 135.266341
L 178.2 135.266341
z
" style="fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_16">
<path clip-path="url(#pbb31c7aa29)" d="M 214.2 256.32
L 221.4 256.32
L 221.4 135.266341
L 214.2 135.266341
z
" style="fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_17">
<path clip-path="url(#pbb31c7aa29)" d="M 250.2 256.32
L 257.4 256.32
L 257.4 135.266341
L 250.2 135.266341
z
" style="fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_18">
<path clip-path="url(#pbb31c7aa29)" d="M 286.2 256.32
L 293.4 256.32
L 293.4 135.266341
L 286.2 135.266341
z
" style="fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_19">
<path clip-path="url(#pbb31c7aa29)" d="M 322.2 256.32
L 329.4 256.32
L 329.4 134.407805
L 322.2 134.407805
z
" style="fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_20">
<path clip-path="url(#pbb31c7aa29)" d="M 358.2 256.32
L 365.4 256.32
L 365.4 134.407805
L 358.2 134.407805
z
" style="fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_21">
<path clip-path="url(#pbb31c7aa29)" d="M 77.4 256.32
L 84.6 256.32
L 84.6 136.124878
L 77.4 136.124878
z
" style="fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_22">
<path clip-path="url(#pbb31c7aa29)" d="M 113.4 256.32
L 120.6 256.32
L 120.6 136.124878
L 113.4 136.124878
z
" style="fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_23">
<path clip-path="url(#pbb31c7aa29)" d="M 149.4 256.32
L 156.6 256.32
L 156.6 136.124878
L 149.4 136.124878
z
" style="fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_24">
<path clip-path="url(#pbb31c7aa29)" d="M 185.4 256.32
L 192.6 256.32
L 192.6 136.124878
L 185.4 136.124878
z
" style="fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_25">
<path clip-path="url(#pbb31c7aa29)" d="M 221.4 256.32
L 228.6 256.32
L 228.6 136.124878
L 221.4 136.124878
z
" style="fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_26">
<path clip-path="url(#pbb31c7aa29)" d="M 257.4 256.32
L 264.6 256.32
L 264.6 134.407805
L 257.4 134.407805
z
" style="fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_27">
<path clip-path="url(#pbb31c7aa29)" d="M 293.4 256.32
L 300.6 256.32
L 300.6 134.407805
L 293.4 134.407805
z
" style="fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_28">
<path clip-path="url(#pbb31c7aa29)" d="M 329.4 256.32
L 336.6 256.32
L 336.6 134.407805
L 329.4 134.407805
z
" style="fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_29">
<path clip-path="url(#pbb31c7aa29)" d="M 365.4 256.32
L 372.6 256.32
L 372.6 134.407805
L 365.4 134.407805
z
" style="fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_30">
<path clip-path="url(#pbb31c7aa29)" d="M 84.6 256.32
L 91.8 256.32
L 91.8 133.549268
L 84.6 133.549268
z
" style="fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_31">
<path clip-path="url(#pbb31c7aa29)" d="M 120.6 256.32
L 127.8 256.32
L 127.8 116.378537
L 120.6 116.378537
z
" style="fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_32">
<path clip-path="url(#pbb31c7aa29)" d="M 156.6 256.32
L 163.8 256.32
L 163.8 104.359024
L 156.6 104.359024
z
" style="fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_33">
<path clip-path="url(#pbb31c7aa29)" d="M 192.6 256.32
L 199.8 256.32
L 199.8 99.207805
L 192.6 99.207805
z
" style="fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_34">
<path clip-path="url(#pbb31c7aa29)" d="M 228.6 256.32
L 235.8 256.32
L 235.8 82.89561
L 228.6 82.89561
z
" style="fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_35">
<path clip-path="url(#pbb31c7aa29)" d="M 264.6 256.32
L 271.8 256.32
L 271.8 71.734634
L 264.6 71.734634
z
" style="fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_36">
<path clip-path="url(#pbb31c7aa29)" d="M 300.6 256.32
L 307.8 256.32
L 307.8 60.573659
L 300.6 60.573659
z
" style="fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_37">
<path clip-path="url(#pbb31c7aa29)" d="M 336.6 256.32
L 343.8 256.32
L 343.8 50.27122
L 336.6 50.27122
z
" style="fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="patch_38">
<path clip-path="url(#pbb31c7aa29)" d="M 372.6 256.32
L 379.8 256.32
L 379.8 45.12
L 372.6 45.12
z
" style="fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="matplotlib.axis_1">
<g id="xtick_1">
<g id="line2d_1">
<defs>
<path d="M 0 0
L 0 3.5
" id="m445964aa5c" style="stroke:#000000;stroke-width:0.8;"/>
</defs>
<g>
<use style="stroke:#000000;stroke-width:0.8;" x="77.4" xlink:href="#m445964aa5c" y="256.32"/>
</g>
</g>
<g id="text_1">
<!-- 3 -->
<defs>
<path d="M 40.578125 39.3125
Q 47.65625 37.796875 51.625 33
Q 55.609375 28.21875 55.609375 21.1875
Q 55.609375 10.40625 48.1875 4.484375
Q 40.765625 -1.421875 27.09375 -1.421875
Q 22.515625 -1.421875 17.65625 -0.515625
Q 12.796875 0.390625 7.625 2.203125
L 7.625 11.71875
Q 11.71875 9.328125 16.59375 8.109375
Q 21.484375 6.890625 26.8125 6.890625
Q 36.078125 6.890625 40.9375 10.546875
Q 45.796875 14.203125 45.796875 21.1875
Q 45.796875 27.640625 41.28125 31.265625
Q 36.765625 34.90625 28.71875 34.90625
L 20.21875 34.90625
L 20.21875 43.015625
L 29.109375 43.015625
Q 36.375 43.015625 40.234375 45.921875
Q 44.09375 48.828125 44.09375 54.296875
Q 44.09375 59.90625 40.109375 62.90625
Q 36.140625 65.921875 28.71875 65.921875
Q 24.65625 65.921875 20.015625 65.03125
Q 15.375 64.15625 9.8125 62.3125
L 9.8125 71.09375
Q 15.4375 72.65625 20.34375 73.4375
Q 25.25 74.21875 29.59375 74.21875
Q 40.828125 74.21875 47.359375 69.109375
Q 53.90625 64.015625 53.90625 55.328125
Q 53.90625 49.265625 50.4375 45.09375
Q 46.96875 40.921875 40.578125 39.3125
z
" id="DejaVuSans-33"/>
</defs>
<g transform="translate(80.159375 269.6825)rotate(-90)scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-33"/>
</g>
</g>
</g>
<g id="xtick_2">
<g id="line2d_2">
<g>
<use style="stroke:#000000;stroke-width:0.8;" x="113.4" xlink:href="#m445964aa5c" y="256.32"/>
</g>
</g>
<g id="text_2">
<!-- 4 -->
<defs>
<path d="M 37.796875 64.3125
L 12.890625 25.390625
L 37.796875 25.390625
z
M 35.203125 72.90625
L 47.609375 72.90625
L 47.609375 25.390625
L 58.015625 25.390625
L 58.015625 17.1875
L 47.609375 17.1875
L 47.609375 0
L 37.796875 0
L 37.796875 17.1875
L 4.890625 17.1875
L 4.890625 26.703125
z
" id="DejaVuSans-34"/>
</defs>
<g transform="translate(116.159375 269.6825)rotate(-90)scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-34"/>
</g>
</g>
</g>
<g id="xtick_3">
<g id="line2d_3">
<g>
<use style="stroke:#000000;stroke-width:0.8;" x="149.4" xlink:href="#m445964aa5c" y="256.32"/>
</g>
</g>
<g id="text_3">
<!-- 5 -->
<defs>
<path d="M 10.796875 72.90625
L 49.515625 72.90625
L 49.515625 64.59375
L 19.828125 64.59375
L 19.828125 46.734375
Q 21.96875 47.46875 24.109375 47.828125
Q 26.265625 48.1875 28.421875 48.1875
Q 40.625 48.1875 47.75 41.5
Q 54.890625 34.8125 54.890625 23.390625
Q 54.890625 11.625 47.5625 5.09375
Q 40.234375 -1.421875 26.90625 -1.421875
Q 22.3125 -1.421875 17.546875 -0.640625
Q 12.796875 0.140625 7.71875 1.703125
L 7.71875 11.625
Q 12.109375 9.234375 16.796875 8.0625
Q 21.484375 6.890625 26.703125 6.890625
Q 35.15625 6.890625 40.078125 11.328125
Q 45.015625 15.765625 45.015625 23.390625
Q 45.015625 31 40.078125 35.4375
Q 35.15625 39.890625 26.703125 39.890625
Q 22.75 39.890625 18.8125 39.015625
Q 14.890625 38.140625 10.796875 36.28125
z
" id="DejaVuSans-35"/>
</defs>
<g transform="translate(152.159375 269.6825)rotate(-90)scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-35"/>
</g>
</g>
</g>
<g id="xtick_4">
<g id="line2d_4">
<g>
<use style="stroke:#000000;stroke-width:0.8;" x="185.4" xlink:href="#m445964aa5c" y="256.32"/>
</g>
</g>
<g id="text_4">
<!-- 6 -->
<defs>
<path d="M 33.015625 40.375
Q 26.375 40.375 22.484375 35.828125
Q 18.609375 31.296875 18.609375 23.390625
Q 18.609375 15.53125 22.484375 10.953125
Q 26.375 6.390625 33.015625 6.390625
Q 39.65625 6.390625 43.53125 10.953125
Q 47.40625 15.53125 47.40625 23.390625
Q 47.40625 31.296875 43.53125 35.828125
Q 39.65625 40.375 33.015625 40.375
z
M 52.59375 71.296875
L 52.59375 62.3125
Q 48.875 64.0625 45.09375 64.984375
Q 41.3125 65.921875 37.59375 65.921875
Q 27.828125 65.921875 22.671875 59.328125
Q 17.53125 52.734375 16.796875 39.40625
Q 19.671875 43.65625 24.015625 45.921875
Q 28.375 48.1875 33.59375 48.1875
Q 44.578125 48.1875 50.953125 41.515625
Q 57.328125 34.859375 57.328125 23.390625
Q 57.328125 12.15625 50.6875 5.359375
Q 44.046875 -1.421875 33.015625 -1.421875
Q 20.359375 -1.421875 13.671875 8.265625
Q 6.984375 17.96875 6.984375 36.375
Q 6.984375 53.65625 15.1875 63.9375
Q 23.390625 74.21875 37.203125 74.21875
Q 40.921875 74.21875 44.703125 73.484375
Q 48.484375 72.75 52.59375 71.296875
z
" id="DejaVuSans-36"/>
</defs>
<g transform="translate(188.159375 269.6825)rotate(-90)scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-36"/>
</g>
</g>
</g>
<g id="xtick_5">
<g id="line2d_5">
<g>
<use style="stroke:#000000;stroke-width:0.8;" x="221.4" xlink:href="#m445964aa5c" y="256.32"/>
</g>
</g>
<g id="text_5">
<!-- 7 -->
<defs>
<path d="M 8.203125 72.90625
L 55.078125 72.90625
L 55.078125 68.703125
L 28.609375 0
L 18.3125 0
L 43.21875 64.59375
L 8.203125 64.59375
z
" id="DejaVuSans-37"/>
</defs>
<g transform="translate(224.159375 269.6825)rotate(-90)scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-37"/>
</g>
</g>
</g>
<g id="xtick_6">
<g id="line2d_6">
<g>
<use style="stroke:#000000;stroke-width:0.8;" x="257.4" xlink:href="#m445964aa5c" y="256.32"/>
</g>
</g>
<g id="text_6">
<!-- 8 -->
<defs>
<path d="M 31.78125 34.625
Q 24.75 34.625 20.71875 30.859375
Q 16.703125 27.09375 16.703125 20.515625
Q 16.703125 13.921875 20.71875 10.15625
Q 24.75 6.390625 31.78125 6.390625
Q 38.8125 6.390625 42.859375 10.171875
Q 46.921875 13.96875 46.921875 20.515625
Q 46.921875 27.09375 42.890625 30.859375
Q 38.875 34.625 31.78125 34.625
z
M 21.921875 38.8125
Q 15.578125 40.375 12.03125 44.71875
Q 8.5 49.078125 8.5 55.328125
Q 8.5 64.0625 14.71875 69.140625
Q 20.953125 74.21875 31.78125 74.21875
Q 42.671875 74.21875 48.875 69.140625
Q 55.078125 64.0625 55.078125 55.328125
Q 55.078125 49.078125 51.53125 44.71875
Q 48 40.375 41.703125 38.8125
Q 48.828125 37.15625 52.796875 32.3125
Q 56.78125 27.484375 56.78125 20.515625
Q 56.78125 9.90625 50.3125 4.234375
Q 43.84375 -1.421875 31.78125 -1.421875
Q 19.734375 -1.421875 13.25 4.234375
Q 6.78125 9.90625 6.78125 20.515625
Q 6.78125 27.484375 10.78125 32.3125
Q 14.796875 37.15625 21.921875 38.8125
z
M 18.3125 54.390625
Q 18.3125 48.734375 21.84375 45.5625
Q 25.390625 42.390625 31.78125 42.390625
Q 38.140625 42.390625 41.71875 45.5625
Q 45.3125 48.734375 45.3125 54.390625
Q 45.3125 60.0625 41.71875 63.234375
Q 38.140625 66.40625 31.78125 66.40625
Q 25.390625 66.40625 21.84375 63.234375
Q 18.3125 60.0625 18.3125 54.390625
z
" id="DejaVuSans-38"/>
</defs>
<g transform="translate(260.159375 269.6825)rotate(-90)scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-38"/>
</g>
</g>
</g>
<g id="xtick_7">
<g id="line2d_7">
<g>
<use style="stroke:#000000;stroke-width:0.8;" x="293.4" xlink:href="#m445964aa5c" y="256.32"/>
</g>
</g>
<g id="text_7">
<!-- 9 -->
<defs>
<path d="M 10.984375 1.515625
L 10.984375 10.5
Q 14.703125 8.734375 18.5 7.8125
Q 22.3125 6.890625 25.984375 6.890625
Q 35.75 6.890625 40.890625 13.453125
Q 46.046875 20.015625 46.78125 33.40625
Q 43.953125 29.203125 39.59375 26.953125
Q 35.25 24.703125 29.984375 24.703125
Q 19.046875 24.703125 12.671875 31.3125
Q 6.296875 37.9375 6.296875 49.421875
Q 6.296875 60.640625 12.9375 67.421875
Q 19.578125 74.21875 30.609375 74.21875
Q 43.265625 74.21875 49.921875 64.515625
Q 56.59375 54.828125 56.59375 36.375
Q 56.59375 19.140625 48.40625 8.859375
Q 40.234375 -1.421875 26.421875 -1.421875
Q 22.703125 -1.421875 18.890625 -0.6875
Q 15.09375 0.046875 10.984375 1.515625
z
M 30.609375 32.421875
Q 37.25 32.421875 41.125 36.953125
Q 45.015625 41.5 45.015625 49.421875
Q 45.015625 57.28125 41.125 61.84375
Q 37.25 66.40625 30.609375 66.40625
Q 23.96875 66.40625 20.09375 61.84375
Q 16.21875 57.28125 16.21875 49.421875
Q 16.21875 41.5 20.09375 36.953125
Q 23.96875 32.421875 30.609375 32.421875
z
" id="DejaVuSans-39"/>
</defs>
<g transform="translate(296.159375 269.6825)rotate(-90)scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-39"/>
</g>
</g>
</g>
<g id="xtick_8">
<g id="line2d_8">
<g>
<use style="stroke:#000000;stroke-width:0.8;" x="329.4" xlink:href="#m445964aa5c" y="256.32"/>
</g>
</g>
<g id="text_8">
<!-- 10 -->
<defs>
<path d="M 12.40625 8.296875
L 28.515625 8.296875
L 28.515625 63.921875
L 10.984375 60.40625
L 10.984375 69.390625
L 28.421875 72.90625
L 38.28125 72.90625
L 38.28125 8.296875
L 54.390625 8.296875
L 54.390625 0
L 12.40625 0
z
" id="DejaVuSans-31"/>
<path d="M 31.78125 66.40625
Q 24.171875 66.40625 20.328125 58.90625
Q 16.5 51.421875 16.5 36.375
Q 16.5 21.390625 20.328125 13.890625
Q 24.171875 6.390625 31.78125 6.390625
Q 39.453125 6.390625 43.28125 13.890625
Q 47.125 21.390625 47.125 36.375
Q 47.125 51.421875 43.28125 58.90625
Q 39.453125 66.40625 31.78125 66.40625
z
M 31.78125 74.21875
Q 44.046875 74.21875 50.515625 64.515625
Q 56.984375 54.828125 56.984375 36.375
Q 56.984375 17.96875 50.515625 8.265625
Q 44.046875 -1.421875 31.78125 -1.421875
Q 19.53125 -1.421875 13.0625 8.265625
Q 6.59375 17.96875 6.59375 36.375
Q 6.59375 54.828125 13.0625 64.515625
Q 19.53125 74.21875 31.78125 74.21875
z
" id="DejaVuSans-30"/>
</defs>
<g transform="translate(332.159375 276.045)rotate(-90)scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-31"/>
<use x="63.623047" xlink:href="#DejaVuSans-30"/>
</g>
</g>
</g>
<g id="xtick_9">
<g id="line2d_9">
<g>
<use style="stroke:#000000;stroke-width:0.8;" x="365.4" xlink:href="#m445964aa5c" y="256.32"/>
</g>
</g>
<g id="text_9">
<!-- 11 -->
<g transform="translate(368.159375 276.045)rotate(-90)scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-31"/>
<use x="63.623047" xlink:href="#DejaVuSans-31"/>
</g>
</g>
</g>
<g id="text_10">
<!-- Checkin index -->
<defs>
<path d="M 64.40625 67.28125
L 64.40625 56.890625
Q 59.421875 61.53125 53.78125 63.8125
Q 48.140625 66.109375 41.796875 66.109375
Q 29.296875 66.109375 22.65625 58.46875
Q 16.015625 50.828125 16.015625 36.375
Q 16.015625 21.96875 22.65625 14.328125
Q 29.296875 6.6875 41.796875 6.6875
Q 48.140625 6.6875 53.78125 8.984375
Q 59.421875 11.28125 64.40625 15.921875
L 64.40625 5.609375
Q 59.234375 2.09375 53.4375 0.328125
Q 47.65625 -1.421875 41.21875 -1.421875
Q 24.65625 -1.421875 15.125 8.703125
Q 5.609375 18.84375 5.609375 36.375
Q 5.609375 53.953125 15.125 64.078125
Q 24.65625 74.21875 41.21875 74.21875
Q 47.75 74.21875 53.53125 72.484375
Q 59.328125 70.75 64.40625 67.28125
z
" id="DejaVuSans-43"/>
<path d="M 54.890625 33.015625
L 54.890625 0
L 45.90625 0
L 45.90625 32.71875
Q 45.90625 40.484375 42.875 44.328125
Q 39.84375 48.1875 33.796875 48.1875
Q 26.515625 48.1875 22.3125 43.546875
Q 18.109375 38.921875 18.109375 30.90625
L 18.109375 0
L 9.078125 0
L 9.078125 75.984375
L 18.109375 75.984375
L 18.109375 46.1875
Q 21.34375 51.125 25.703125 53.5625
Q 30.078125 56 35.796875 56
Q 45.21875 56 50.046875 50.171875
Q 54.890625 44.34375 54.890625 33.015625
z
" id="DejaVuSans-68"/>
<path d="M 56.203125 29.59375
L 56.203125 25.203125
L 14.890625 25.203125
Q 15.484375 15.921875 20.484375 11.0625
Q 25.484375 6.203125 34.421875 6.203125
Q 39.59375 6.203125 44.453125 7.46875
Q 49.3125 8.734375 54.109375 11.28125
L 54.109375 2.78125
Q 49.265625 0.734375 44.1875 -0.34375
Q 39.109375 -1.421875 33.890625 -1.421875
Q 20.796875 -1.421875 13.15625 6.1875
Q 5.515625 13.8125 5.515625 26.8125
Q 5.515625 40.234375 12.765625 48.109375
Q 20.015625 56 32.328125 56
Q 43.359375 56 49.78125 48.890625
Q 56.203125 41.796875 56.203125 29.59375
z
M 47.21875 32.234375
Q 47.125 39.59375 43.09375 43.984375
Q 39.0625 48.390625 32.421875 48.390625
Q 24.90625 48.390625 20.390625 44.140625
Q 15.875 39.890625 15.1875 32.171875
z
" id="DejaVuSans-65"/>
<path d="M 48.78125 52.59375
L 48.78125 44.1875
Q 44.96875 46.296875 41.140625 47.34375
Q 37.3125 48.390625 33.40625 48.390625
Q 24.65625 48.390625 19.8125 42.84375
Q 14.984375 37.3125 14.984375 27.296875
Q 14.984375 17.28125 19.8125 11.734375
Q 24.65625 6.203125 33.40625 6.203125
Q 37.3125 6.203125 41.140625 7.25
Q 44.96875 8.296875 48.78125 10.40625
L 48.78125 2.09375
Q 45.015625 0.34375 40.984375 -0.53125
Q 36.96875 -1.421875 32.421875 -1.421875
Q 20.0625 -1.421875 12.78125 6.34375
Q 5.515625 14.109375 5.515625 27.296875
Q 5.515625 40.671875 12.859375 48.328125
Q 20.21875 56 33.015625 56
Q 37.15625 56 41.109375 55.140625
Q 45.0625 54.296875 48.78125 52.59375
z
" id="DejaVuSans-63"/>
<path d="M 9.078125 75.984375
L 18.109375 75.984375
L 18.109375 31.109375
L 44.921875 54.6875
L 56.390625 54.6875
L 27.390625 29.109375
L 57.625 0
L 45.90625 0
L 18.109375 26.703125
L 18.109375 0
L 9.078125 0
z
" id="DejaVuSans-6b"/>
<path d="M 9.421875 54.6875
L 18.40625 54.6875
L 18.40625 0
L 9.421875 0
z
M 9.421875 75.984375
L 18.40625 75.984375
L 18.40625 64.59375
L 9.421875 64.59375
z
" id="DejaVuSans-69"/>
<path d="M 54.890625 33.015625
L 54.890625 0
L 45.90625 0
L 45.90625 32.71875
Q 45.90625 40.484375 42.875 44.328125
Q 39.84375 48.1875 33.796875 48.1875
Q 26.515625 48.1875 22.3125 43.546875
Q 18.109375 38.921875 18.109375 30.90625
L 18.109375 0
L 9.078125 0
L 9.078125 54.6875
L 18.109375 54.6875
L 18.109375 46.1875
Q 21.34375 51.125 25.703125 53.5625
Q 30.078125 56 35.796875 56
Q 45.21875 56 50.046875 50.171875
Q 54.890625 44.34375 54.890625 33.015625
z
" id="DejaVuSans-6e"/>
<path id="DejaVuSans-20"/>
<path d="M 45.40625 46.390625
L 45.40625 75.984375
L 54.390625 75.984375
L 54.390625 0
L 45.40625 0
L 45.40625 8.203125
Q 42.578125 3.328125 38.25 0.953125
Q 33.9375 -1.421875 27.875 -1.421875
Q 17.96875 -1.421875 11.734375 6.484375
Q 5.515625 14.40625 5.515625 27.296875
Q 5.515625 40.1875 11.734375 48.09375
Q 17.96875 56 27.875 56
Q 33.9375 56 38.25 53.625
Q 42.578125 51.265625 45.40625 46.390625
z
M 14.796875 27.296875
Q 14.796875 17.390625 18.875 11.75
Q 22.953125 6.109375 30.078125 6.109375
Q 37.203125 6.109375 41.296875 11.75
Q 45.40625 17.390625 45.40625 27.296875
Q 45.40625 37.203125 41.296875 42.84375
Q 37.203125 48.484375 30.078125 48.484375
Q 22.953125 48.484375 18.875 42.84375
Q 14.796875 37.203125 14.796875 27.296875
z
" id="DejaVuSans-64"/>
<path d="M 54.890625 54.6875
L 35.109375 28.078125
L 55.90625 0
L 45.3125 0
L 29.390625 21.484375
L 13.484375 0
L 2.875 0
L 24.125 28.609375
L 4.6875 54.6875
L 15.28125 54.6875
L 29.78125 35.203125
L 44.28125 54.6875
z
" id="DejaVuSans-78"/>
</defs>
<g transform="translate(186.104688 287.643438)scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-43"/>
<use x="69.824219" xlink:href="#DejaVuSans-68"/>
<use x="133.203125" xlink:href="#DejaVuSans-65"/>
<use x="194.726562" xlink:href="#DejaVuSans-63"/>
<use x="249.707031" xlink:href="#DejaVuSans-6b"/>
<use x="307.617188" xlink:href="#DejaVuSans-69"/>
<use x="335.400391" xlink:href="#DejaVuSans-6e"/>
<use x="398.779297" xlink:href="#DejaVuSans-20"/>
<use x="430.566406" xlink:href="#DejaVuSans-69"/>
<use x="458.349609" xlink:href="#DejaVuSans-6e"/>
<use x="521.728516" xlink:href="#DejaVuSans-64"/>
<use x="585.205078" xlink:href="#DejaVuSans-65"/>
<use x="646.712891" xlink:href="#DejaVuSans-78"/>
</g>
</g>
</g>
<g id="matplotlib.axis_2">
<g id="ytick_1">
<g id="line2d_10">
<defs>
<path d="M 0 0
L -3.5 0
" id="mdf25573cb4" style="stroke:#000000;stroke-width:0.8;"/>
</defs>
<g>
<use style="stroke:#000000;stroke-width:0.8;" x="54" xlink:href="#mdf25573cb4" y="256.32"/>
</g>
</g>
<g id="text_11">
<!-- 0.0 -->
<defs>
<path d="M 10.6875 12.40625
L 21 12.40625
L 21 0
L 10.6875 0
z
" id="DejaVuSans-2e"/>
</defs>
<g transform="translate(31.096875 260.119219)scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-30"/>
<use x="63.623047" xlink:href="#DejaVuSans-2e"/>
<use x="95.410156" xlink:href="#DejaVuSans-30"/>
</g>
</g>
</g>
<g id="ytick_2">
<g id="line2d_11">
<g>
<use style="stroke:#000000;stroke-width:0.8;" x="54" xlink:href="#mdf25573cb4" y="212.362927"/>
</g>
</g>
<g id="text_12">
<!-- 0.2 -->
<defs>
<path d="M 19.1875 8.296875
L 53.609375 8.296875
L 53.609375 0
L 7.328125 0
L 7.328125 8.296875
Q 12.9375 14.109375 22.625 23.890625
Q 32.328125 33.6875 34.8125 36.53125
Q 39.546875 41.84375 41.421875 45.53125
Q 43.3125 49.21875 43.3125 52.78125
Q 43.3125 58.59375 39.234375 62.25
Q 35.15625 65.921875 28.609375 65.921875
Q 23.96875 65.921875 18.8125 64.3125
Q 13.671875 62.703125 7.8125 59.421875
L 7.8125 69.390625
Q 13.765625 71.78125 18.9375 73
Q 24.125 74.21875 28.421875 74.21875
Q 39.75 74.21875 46.484375 68.546875
Q 53.21875 62.890625 53.21875 53.421875
Q 53.21875 48.921875 51.53125 44.890625
Q 49.859375 40.875 45.40625 35.40625
Q 44.1875 33.984375 37.640625 27.21875
Q 31.109375 20.453125 19.1875 8.296875
z
" id="DejaVuSans-32"/>
</defs>
<g transform="translate(31.096875 216.162146)scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-30"/>
<use x="63.623047" xlink:href="#DejaVuSans-2e"/>
<use x="95.410156" xlink:href="#DejaVuSans-32"/>
</g>
</g>
</g>
<g id="ytick_3">
<g id="line2d_12">
<g>
<use style="stroke:#000000;stroke-width:0.8;" x="54" xlink:href="#mdf25573cb4" y="168.405854"/>
</g>
</g>
<g id="text_13">
<!-- 0.4 -->
<g transform="translate(31.096875 172.205072)scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-30"/>
<use x="63.623047" xlink:href="#DejaVuSans-2e"/>
<use x="95.410156" xlink:href="#DejaVuSans-34"/>
</g>
</g>
</g>
<g id="ytick_4">
<g id="line2d_13">
<g>
<use style="stroke:#000000;stroke-width:0.8;" x="54" xlink:href="#mdf25573cb4" y="124.44878"/>
</g>
</g>
<g id="text_14">
<!-- 0.6 -->
<g transform="translate(31.096875 128.247999)scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-30"/>
<use x="63.623047" xlink:href="#DejaVuSans-2e"/>
<use x="95.410156" xlink:href="#DejaVuSans-36"/>
</g>
</g>
</g>
<g id="ytick_5">
<g id="line2d_14">
<g>
<use style="stroke:#000000;stroke-width:0.8;" x="54" xlink:href="#mdf25573cb4" y="80.491707"/>
</g>
</g>
<g id="text_15">
<!-- 0.8 -->
<g transform="translate(31.096875 84.290926)scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-30"/>
<use x="63.623047" xlink:href="#DejaVuSans-2e"/>
<use x="95.410156" xlink:href="#DejaVuSans-38"/>
</g>
</g>
</g>
<g id="ytick_6">
<g id="line2d_15">
<g>
<use style="stroke:#000000;stroke-width:0.8;" x="54" xlink:href="#mdf25573cb4" y="36.534634"/>
</g>
</g>
<g id="text_16">
<!-- 1.0 -->
<g transform="translate(31.096875 40.333853)scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-31"/>
<use x="63.623047" xlink:href="#DejaVuSans-2e"/>
<use x="95.410156" xlink:href="#DejaVuSans-30"/>
</g>
</g>
</g>
<g id="text_17">
<!-- Repo size (MiB) -->
<defs>
<path d="M 44.390625 34.1875
Q 47.5625 33.109375 50.5625 29.59375
Q 53.5625 26.078125 56.59375 19.921875
L 66.609375 0
L 56 0
L 46.6875 18.703125
Q 43.0625 26.03125 39.671875 28.421875
Q 36.28125 30.8125 30.421875 30.8125
L 19.671875 30.8125
L 19.671875 0
L 9.8125 0
L 9.8125 72.90625
L 32.078125 72.90625
Q 44.578125 72.90625 50.734375 67.671875
Q 56.890625 62.453125 56.890625 51.90625
Q 56.890625 45.015625 53.6875 40.46875
Q 50.484375 35.9375 44.390625 34.1875
z
M 19.671875 64.796875
L 19.671875 38.921875
L 32.078125 38.921875
Q 39.203125 38.921875 42.84375 42.21875
Q 46.484375 45.515625 46.484375 51.90625
Q 46.484375 58.296875 42.84375 61.546875
Q 39.203125 64.796875 32.078125 64.796875
z
" id="DejaVuSans-52"/>
<path d="M 18.109375 8.203125
L 18.109375 -20.796875
L 9.078125 -20.796875
L 9.078125 54.6875
L 18.109375 54.6875
L 18.109375 46.390625
Q 20.953125 51.265625 25.265625 53.625
Q 29.59375 56 35.59375 56
Q 45.5625 56 51.78125 48.09375
Q 58.015625 40.1875 58.015625 27.296875
Q 58.015625 14.40625 51.78125 6.484375
Q 45.5625 -1.421875 35.59375 -1.421875
Q 29.59375 -1.421875 25.265625 0.953125
Q 20.953125 3.328125 18.109375 8.203125
z
M 48.6875 27.296875
Q 48.6875 37.203125 44.609375 42.84375
Q 40.53125 48.484375 33.40625 48.484375
Q 26.265625 48.484375 22.1875 42.84375
Q 18.109375 37.203125 18.109375 27.296875
Q 18.109375 17.390625 22.1875 11.75
Q 26.265625 6.109375 33.40625 6.109375
Q 40.53125 6.109375 44.609375 11.75
Q 48.6875 17.390625 48.6875 27.296875
z
" id="DejaVuSans-70"/>
<path d="M 30.609375 48.390625
Q 23.390625 48.390625 19.1875 42.75
Q 14.984375 37.109375 14.984375 27.296875
Q 14.984375 17.484375 19.15625 11.84375
Q 23.34375 6.203125 30.609375 6.203125
Q 37.796875 6.203125 41.984375 11.859375
Q 46.1875 17.53125 46.1875 27.296875
Q 46.1875 37.015625 41.984375 42.703125
Q 37.796875 48.390625 30.609375 48.390625
z
M 30.609375 56
Q 42.328125 56 49.015625 48.375
Q 55.71875 40.765625 55.71875 27.296875
Q 55.71875 13.875 49.015625 6.21875
Q 42.328125 -1.421875 30.609375 -1.421875
Q 18.84375 -1.421875 12.171875 6.21875
Q 5.515625 13.875 5.515625 27.296875
Q 5.515625 40.765625 12.171875 48.375
Q 18.84375 56 30.609375 56
z
" id="DejaVuSans-6f"/>
<path d="M 44.28125 53.078125
L 44.28125 44.578125
Q 40.484375 46.53125 36.375 47.5
Q 32.28125 48.484375 27.875 48.484375
Q 21.1875 48.484375 17.84375 46.4375
Q 14.5 44.390625 14.5 40.28125
Q 14.5 37.15625 16.890625 35.375
Q 19.28125 33.59375 26.515625 31.984375
L 29.59375 31.296875
Q 39.15625 29.25 43.1875 25.515625
Q 47.21875 21.78125 47.21875 15.09375
Q 47.21875 7.46875 41.1875 3.015625
Q 35.15625 -1.421875 24.609375 -1.421875
Q 20.21875 -1.421875 15.453125 -0.5625
Q 10.6875 0.296875 5.421875 2
L 5.421875 11.28125
Q 10.40625 8.6875 15.234375 7.390625
Q 20.0625 6.109375 24.8125 6.109375
Q 31.15625 6.109375 34.5625 8.28125
Q 37.984375 10.453125 37.984375 14.40625
Q 37.984375 18.0625 35.515625 20.015625
Q 33.0625 21.96875 24.703125 23.78125
L 21.578125 24.515625
Q 13.234375 26.265625 9.515625 29.90625
Q 5.8125 33.546875 5.8125 39.890625
Q 5.8125 47.609375 11.28125 51.796875
Q 16.75 56 26.8125 56
Q 31.78125 56 36.171875 55.265625
Q 40.578125 54.546875 44.28125 53.078125
z
" id="DejaVuSans-73"/>
<path d="M 5.515625 54.6875
L 48.1875 54.6875
L 48.1875 46.484375
L 14.40625 7.171875
L 48.1875 7.171875
L 48.1875 0
L 4.296875 0
L 4.296875 8.203125
L 38.09375 47.515625
L 5.515625 47.515625
z
" id="DejaVuSans-7a"/>
<path d="M 31 75.875
Q 24.46875 64.65625 21.28125 53.65625
Q 18.109375 42.671875 18.109375 31.390625
Q 18.109375 20.125 21.3125 9.0625
Q 24.515625 -2 31 -13.1875
L 23.1875 -13.1875
Q 15.875 -1.703125 12.234375 9.375
Q 8.59375 20.453125 8.59375 31.390625
Q 8.59375 42.28125 12.203125 53.3125
Q 15.828125 64.359375 23.1875 75.875
z
" id="DejaVuSans-28"/>
<path d="M 9.8125 72.90625
L 24.515625 72.90625
L 43.109375 23.296875
L 61.8125 72.90625
L 76.515625 72.90625
L 76.515625 0
L 66.890625 0
L 66.890625 64.015625
L 48.09375 14.015625
L 38.1875 14.015625
L 19.390625 64.015625
L 19.390625 0
L 9.8125 0
z
" id="DejaVuSans-4d"/>
<path d="M 19.671875 34.8125
L 19.671875 8.109375
L 35.5 8.109375
Q 43.453125 8.109375 47.28125 11.40625
Q 51.125 14.703125 51.125 21.484375
Q 51.125 28.328125 47.28125 31.5625
Q 43.453125 34.8125 35.5 34.8125
z
M 19.671875 64.796875
L 19.671875 42.828125
L 34.28125 42.828125
Q 41.5 42.828125 45.03125 45.53125
Q 48.578125 48.25 48.578125 53.8125
Q 48.578125 59.328125 45.03125 62.0625
Q 41.5 64.796875 34.28125 64.796875
z
M 9.8125 72.90625
L 35.015625 72.90625
Q 46.296875 72.90625 52.390625 68.21875
Q 58.5 63.53125 58.5 54.890625
Q 58.5 48.1875 55.375 44.234375
Q 52.25 40.28125 46.1875 39.3125
Q 53.46875 37.75 57.5 32.78125
Q 61.53125 27.828125 61.53125 20.40625
Q 61.53125 10.640625 54.890625 5.3125
Q 48.25 0 35.984375 0
L 9.8125 0
z
" id="DejaVuSans-42"/>
<path d="M 8.015625 75.875
L 15.828125 75.875
Q 23.140625 64.359375 26.78125 53.3125
Q 30.421875 42.28125 30.421875 31.390625
Q 30.421875 20.453125 26.78125 9.375
Q 23.140625 -1.703125 15.828125 -13.1875
L 8.015625 -13.1875
Q 14.5 -2 17.703125 9.0625
Q 20.90625 20.125 20.90625 31.390625
Q 20.90625 42.671875 17.703125 53.65625
Q 14.5 64.65625 8.015625 75.875
z
" id="DejaVuSans-29"/>
</defs>
<g transform="translate(25.017187 184.129063)rotate(-90)scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-52"/>
<use x="69.419922" xlink:href="#DejaVuSans-65"/>
<use x="130.943359" xlink:href="#DejaVuSans-70"/>
<use x="194.419922" xlink:href="#DejaVuSans-6f"/>
<use x="255.601562" xlink:href="#DejaVuSans-20"/>
<use x="287.388672" xlink:href="#DejaVuSans-73"/>
<use x="339.488281" xlink:href="#DejaVuSans-69"/>
<use x="367.271484" xlink:href="#DejaVuSans-7a"/>
<use x="419.761719" xlink:href="#DejaVuSans-65"/>
<use x="481.285156" xlink:href="#DejaVuSans-20"/>
<use x="513.072266" xlink:href="#DejaVuSans-28"/>
<use x="552.085938" xlink:href="#DejaVuSans-4d"/>
<use x="638.365234" xlink:href="#DejaVuSans-69"/>
<use x="666.148438" xlink:href="#DejaVuSans-42"/>
<use x="734.751953" xlink:href="#DejaVuSans-29"/>
</g>
</g>
</g>
<g id="patch_39">
<path d="M 54 256.32
L 54 34.56
" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/>
</g>
<g id="patch_40">
<path d="M 388.8 256.32
L 388.8 34.56
" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/>
</g>
<g id="patch_41">
<path d="M 54 256.32
L 388.8 256.32
" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/>
</g>
<g id="patch_42">
<path d="M 54 34.56
L 388.8 34.56
" style="fill:none;stroke:#000000;stroke-linecap:square;stroke-linejoin:miter;stroke-width:0.8;"/>
</g>
<g id="legend_1">
<g id="patch_43">
<path d="M 61 101.2725
L 116.046875 101.2725
Q 118.046875 101.2725 118.046875 99.2725
L 118.046875 41.56
Q 118.046875 39.56 116.046875 39.56
L 61 39.56
Q 59 39.56 59 41.56
L 59 99.2725
Q 59 101.2725 61 101.2725
z
" style="fill:#ffffff;opacity:0.8;stroke:#cccccc;stroke-linejoin:miter;"/>
</g>
<g id="patch_44">
<path d="M 63 51.158438
L 83 51.158438
L 83 44.158438
L 63 44.158438
z
" style="fill:#3b4cc0;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="text_18">
<!-- JPEG -->
<defs>
<path d="M 9.8125 72.90625
L 19.671875 72.90625
L 19.671875 5.078125
Q 19.671875 -8.109375 14.671875 -14.0625
Q 9.671875 -20.015625 -1.421875 -20.015625
L -5.171875 -20.015625
L -5.171875 -11.71875
L -2.09375 -11.71875
Q 4.4375 -11.71875 7.125 -8.046875
Q 9.8125 -4.390625 9.8125 5.078125
z
" id="DejaVuSans-4a"/>
<path d="M 19.671875 64.796875
L 19.671875 37.40625
L 32.078125 37.40625
Q 38.96875 37.40625 42.71875 40.96875
Q 46.484375 44.53125 46.484375 51.125
Q 46.484375 57.671875 42.71875 61.234375
Q 38.96875 64.796875 32.078125 64.796875
z
M 9.8125 72.90625
L 32.078125 72.90625
Q 44.34375 72.90625 50.609375 67.359375
Q 56.890625 61.8125 56.890625 51.125
Q 56.890625 40.328125 50.609375 34.8125
Q 44.34375 29.296875 32.078125 29.296875
L 19.671875 29.296875
L 19.671875 0
L 9.8125 0
z
" id="DejaVuSans-50"/>
<path d="M 9.8125 72.90625
L 55.90625 72.90625
L 55.90625 64.59375
L 19.671875 64.59375
L 19.671875 43.015625
L 54.390625 43.015625
L 54.390625 34.71875
L 19.671875 34.71875
L 19.671875 8.296875
L 56.78125 8.296875
L 56.78125 0
L 9.8125 0
z
" id="DejaVuSans-45"/>
<path d="M 59.515625 10.40625
L 59.515625 29.984375
L 43.40625 29.984375
L 43.40625 38.09375
L 69.28125 38.09375
L 69.28125 6.78125
Q 63.578125 2.734375 56.6875 0.65625
Q 49.8125 -1.421875 42 -1.421875
Q 24.90625 -1.421875 15.25 8.5625
Q 5.609375 18.5625 5.609375 36.375
Q 5.609375 54.25 15.25 64.234375
Q 24.90625 74.21875 42 74.21875
Q 49.125 74.21875 55.546875 72.453125
Q 61.96875 70.703125 67.390625 67.28125
L 67.390625 56.78125
Q 61.921875 61.421875 55.765625 63.765625
Q 49.609375 66.109375 42.828125 66.109375
Q 29.4375 66.109375 22.71875 58.640625
Q 16.015625 51.171875 16.015625 36.375
Q 16.015625 21.625 22.71875 14.15625
Q 29.4375 6.6875 42.828125 6.6875
Q 48.046875 6.6875 52.140625 7.59375
Q 56.25 8.5 59.515625 10.40625
z
" id="DejaVuSans-47"/>
</defs>
<g transform="translate(91 51.158438)scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-4a"/>
<use x="29.492188" xlink:href="#DejaVuSans-50"/>
<use x="89.794922" xlink:href="#DejaVuSans-45"/>
<use x="152.978516" xlink:href="#DejaVuSans-47"/>
</g>
</g>
<g id="patch_45">
<path d="M 63 65.836563
L 83 65.836563
L 83 58.836563
L 63 58.836563
z
" style="fill:#aac7fd;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="text_19">
<!-- BMP -->
<g transform="translate(91 65.836563)scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-42"/>
<use x="68.603516" xlink:href="#DejaVuSans-4d"/>
<use x="154.882812" xlink:href="#DejaVuSans-50"/>
</g>
</g>
<g id="patch_46">
<path d="M 63 80.514688
L 83 80.514688
L 83 73.514688
L 63 73.514688
z
" style="fill:#f7b89c;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="text_20">
<!-- TIFF -->
<defs>
<path d="M -0.296875 72.90625
L 61.375 72.90625
L 61.375 64.59375
L 35.5 64.59375
L 35.5 0
L 25.59375 0
L 25.59375 64.59375
L -0.296875 64.59375
z
" id="DejaVuSans-54"/>
<path d="M 9.8125 72.90625
L 19.671875 72.90625
L 19.671875 0
L 9.8125 0
z
" id="DejaVuSans-49"/>
<path d="M 9.8125 72.90625
L 51.703125 72.90625
L 51.703125 64.59375
L 19.671875 64.59375
L 19.671875 43.109375
L 48.578125 43.109375
L 48.578125 34.8125
L 19.671875 34.8125
L 19.671875 0
L 9.8125 0
z
" id="DejaVuSans-46"/>
</defs>
<g transform="translate(91 80.514688)scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-54"/>
<use x="61.083984" xlink:href="#DejaVuSans-49"/>
<use x="90.576172" xlink:href="#DejaVuSans-46"/>
<use x="148.095703" xlink:href="#DejaVuSans-46"/>
</g>
</g>
<g id="patch_47">
<path d="M 63 95.192813
L 83 95.192813
L 83 88.192813
L 63 88.192813
z
" style="fill:#b40426;stroke:#ffffff;stroke-linejoin:miter;stroke-width:2;"/>
</g>
<g id="text_21">
<!-- PNG -->
<defs>
<path d="M 9.8125 72.90625
L 23.09375 72.90625
L 55.421875 11.921875
L 55.421875 72.90625
L 64.984375 72.90625
L 64.984375 0
L 51.703125 0
L 19.390625 60.984375
L 19.390625 0
L 9.8125 0
z
" id="DejaVuSans-4e"/>
</defs>
<g transform="translate(91 95.192813)scale(0.1 -0.1)">
<use xlink:href="#DejaVuSans-50"/>
<use x="60.302734" xlink:href="#DejaVuSans-4e"/>
<use x="135.107422" xlink:href="#DejaVuSans-47"/>
</g>
</g>
</g>
</g>
</g>
<defs>
<clipPath id="pbb31c7aa29">
<rect height="221.76" width="334.8" x="54" y="34.56"/>
</clipPath>
</defs>
</svg>
|
Changes to www/makefile.wiki.
| ︙ | ︙ | |||
97 98 99 100 101 102 103 |
all of the makefiles for all targets will be rebuild.
There is an option code verification step implemented using
15. [/file/src/codecheck1.c | codecheck1.c]
This file implements a small utility program ("codecheck1")
| | | | 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
all of the makefiles for all targets will be rebuild.
There is an option code verification step implemented using
15. [/file/src/codecheck1.c | codecheck1.c]
This file implements a small utility program ("codecheck1")
that scans other Fossil source files looking for errors in printf-style
format strings.
The codecheck1 utility detects missing or surplus arguments on
printf-like functions and dangerous uses of "%s" that might
permit SQL injection or cross-site scripting attacks. This code
check step is run automatically on each build of Fossil, and can
also be run separately by typing "make codecheck". Note that the
built-in printf format checking of GCC does not function for Fossil
since Fossil implements its own printf (in the
[/file/src/printf.c | printf.c] source file) that includes special
|
| ︙ | ︙ |
Added www/mirrortogithub.md.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# How To Mirror A Fossil Repository On GitHub
Beginning with Fossil version 2.9, you can mirror a Fossil-based
project on GitHub by following these steps:
<ol>
<li><p>Create an account on GitHub if you do not have one already. Log
into that account.
<li><p>Create a new project. GitHub will ask you if you want to prepopulate
your project with various things like a README file. Answer "no" to
everything. You want a completely blank project. GitHub will then
supply you with a URL for your project that will look something
like this:
<blockquote>
https://github.com/username/project.git
</blockquote>
<li><p>Back on your workstation, move to a checkout for your Fossil
project and type:
<blockquote>
<pre>$ fossil git export /path/to/git/repo --autopush \
https://<font color="orange">username</font>:<font color="red">password</font>@github.com/username/project.git</pre>
</blockquote>
<p> In place of the <code>/path/to...</code> argument above, put in
some directory name that is <i>outside</i> of your Fossil checkout. If
you keep multiple Fossil checkouts in a directory of their own,
consider using <code>../git-mirror</code> to place the Git export
mirror alongside them, for example. Fossil will create this
directory if necessary. This directory will become a Git
repository that holds a translation of your Fossil repository.
<p> The <code>--autopush</code> option tells Fossil that you want to
push the Git translation up to GitHub every time it is updated.
<p> The URL parameter is the same as the one GitHub gave you, but with
your GitHub <font color="orange">username</font> and <font
color="red">password</font> added.
<p> If your GitHub account uses two-factor authentication (2FA), you
will have to <a href="https://github.com/settings/tokens">generate
a personal access token</a> and use that in place of your actual
password in the URL. This token should have “repo” scope enabled,
only.
<p> You can also run the command above outside of any open checkout of
your project by supplying the “<code>-R repository</code>”
option.
<li><p>Get some coffee. Depending on the size of your project, the
initial "<code>fossil git export</code>" command in the previous
step might run for several minutes.
<li><p>And you are done! Assuming everything worked, your project is now
mirrored on GitHub.
<li><p>Whenever you update your project, simply run this command to update
the mirror:
<blockquote>
<pre>$ fossil git export</pre>
</blockquote>
<p> Unlike with the first time you ran that command, you don’t need
the remaining arguments, because Fossil remembers those things.
Subsequent mirror updates should usually happen in a fraction of
a second.
<li><p>To see the status of your mirror, run:
<blockquote>
<pre>$ fossil git status</pre>
</blockquote>
</ol>
## Notes:
* The mirroring is one-way. If you check in changes on GitHub, those
changes will not be reabsorbed by Fossil. There are technical problems
that make a two-way mirror all but impossible.
This also means that you cannot accept pull requests on GitHub.
* The "`fossil git export`" command creates subprocesses that run "`git`"
commands, so you must have Git installed on your machine for any
of this to work.
* The Git repository will have an extra unmanaged top-level directory named
"`.mirror_state`" that contains one or more files. Those files are
used to store the intermediate state of the translation so that
subsequent invocations of "`fossil git export`" will know where you
left off the last time and what new content needs to be moved over into
Git. Be careful not to mess with the `.mirror_state` directory or
any of its contents. Do not put those files under Git management. Do
not edit or delete them.
* The name of the "trunk" branch is automatically translated into "master"
in the Git mirror.
* Only check-ins and simple tags are translated to Git. Git does not
support wiki or tickets or unversioned content or any of the other
features of Fossil that make it so convenient to use, so those other
elements cannot be mirrored in Git.
* In Git, all tags must be unique. If your Fossil repository has the
same tag on two or more check-ins, the tag will only be preserved on
the chronologically newest check-in.
* There is a
[long list of restrictions](https://git-scm.com/docs/git-check-ref-format)
on tag and branch names in Git. If any of your Fossil tag or branch names
violate these rules, then the names are translated prior to being exported
to Git. The translation usually involves converting the offending characters
into underscores.
## Example GitHub Mirrors
As of this writing (2019-03-16) Fossil’s own repository is mirrored
on GitHub at:
>
<https://github.com/drhsqlite/fossil-mirror>
In addition, an official Git mirror of SQLite is available:
>
<https://github.com/sqlite/sqlite>
The Fossil source repositories for these mirrors are at
<https://www2.fossil-scm.org/fossil> and <https://www2.sqlite.org/src>,
respectively. Both repositories are hosted on the same VM at
[Linode](https://www.linode.com). On that machine, there is a
[cron job](https://linux.die.net/man/8/cron)
that runs at 17 minutes after the hour, every hour that does:
>
/usr/bin/fossil sync -u -R /home/www/fossil/fossil.fossil
/usr/bin/fossil sync -R /home/www/fossil/sqlite.fossil
/usr/bin/fossil git export -R /home/www/fossil/fossil.fossil
/usr/bin/fossil git export -R /home/www/fossil/sqlite.fossil
The initial two "sync" commands pull in changes from the primary
Fossil repositores for Fossil and SQLite. The last two lines
export the changes to Git and push the results up to GitHub.
|
Changes to www/mkindex.tcl.
| ︙ | ︙ | |||
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
grep.md {Fossil grep vs POSIX grep}
hacker-howto.wiki {Hacker How-To}
hashpolicy.wiki {Hash Policy: Choosing Between SHA1 and SHA3-256}
/help {Lists of Commands and Webpages}
hints.wiki {Fossil Tips And Usage Hints}
index.wiki {Home Page}
inout.wiki {Import And Export To And From Git}
makefile.wiki {The Fossil Build Process}
/md_rules {Markdown Formatting Rules}
newrepo.wiki {How To Create A New Fossil Repository}
password.wiki {Password Management And Authentication}
pop.wiki {Principles Of Operation}
private.wiki {Creating, Syncing, and Deleting Private Branches}
qandc.wiki {Questions And Criticisms}
quickstart.wiki {Fossil Quick Start Guide}
| > > | 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
grep.md {Fossil grep vs POSIX grep}
hacker-howto.wiki {Hacker How-To}
hashpolicy.wiki {Hash Policy: Choosing Between SHA1 and SHA3-256}
/help {Lists of Commands and Webpages}
hints.wiki {Fossil Tips And Usage Hints}
index.wiki {Home Page}
inout.wiki {Import And Export To And From Git}
image-format-vs-repo-size.md {Image Format vs Fossil Repo Size}
makefile.wiki {The Fossil Build Process}
mirrortogithub.md {How To Mirror A Fossil Repository On GitHub}
/md_rules {Markdown Formatting Rules}
newrepo.wiki {How To Create A New Fossil Repository}
password.wiki {Password Management And Authentication}
pop.wiki {Principles Of Operation}
private.wiki {Creating, Syncing, and Deleting Private Branches}
qandc.wiki {Questions And Criticisms}
quickstart.wiki {Fossil Quick Start Guide}
|
| ︙ | ︙ | |||
76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
sync.wiki {The Fossil Sync Protocol}
tech_overview.wiki {A Technical Overview Of The Design And Implementation
Of Fossil}
tech_overview.wiki {SQLite Databases Used By Fossil}
th1.md {The TH1 Scripting Language}
tickets.wiki {The Fossil Ticket System}
theory1.wiki {Thoughts On The Design Of The Fossil DVCS}
unvers.wiki {Unversioned Files}
webpage-ex.md {Webpage Examples}
webui.wiki {The Fossil Web Interface}
whyusefossil.wiki {Why You Should Use Fossil}
whyusefossil.wiki {Benefits Of Version Control}
wikitheory.wiki {Wiki In Fossil}
/wiki_rules {Wiki Formatting Rules}
| > | 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
sync.wiki {The Fossil Sync Protocol}
tech_overview.wiki {A Technical Overview Of The Design And Implementation
Of Fossil}
tech_overview.wiki {SQLite Databases Used By Fossil}
th1.md {The TH1 Scripting Language}
tickets.wiki {The Fossil Ticket System}
theory1.wiki {Thoughts On The Design Of The Fossil DVCS}
tls-nginx.md {Proxying Fossil via HTTPS with nginx}
unvers.wiki {Unversioned Files}
webpage-ex.md {Webpage Examples}
webui.wiki {The Fossil Web Interface}
whyusefossil.wiki {Why You Should Use Fossil}
whyusefossil.wiki {Benefits Of Version Control}
wikitheory.wiki {Wiki In Fossil}
/wiki_rules {Wiki Formatting Rules}
|
| ︙ | ︙ |
Changes to www/permutedindex.html.
| ︙ | ︙ | |||
98 99 100 101 102 103 104 105 106 107 108 109 110 111 | <li><a href="adding_code.wiki">Features To Fossil — Adding New</a></li> <li><a href="fileformat.wiki">File Format — Fossil</a></li> <li><a href="globs.md"><b>File Name Glob Patterns</b></a></li> <li><a href="unvers.wiki">Files — Unversioned</a></li> <li><a href="branching.wiki">Forking, Merging, and Tagging — Branching,</a></li> <li><a href="delta_format.wiki">Format — Fossil Delta</a></li> <li><a href="fileformat.wiki">Format — Fossil File</a></li> <li><a href="../../../md_rules">Formatting Rules — Markdown</a></li> <li><a href="../../../wiki_rules">Formatting Rules — Wiki</a></li> <li><a href="forum.wiki">Forums — Fossil</a></li> <li><a href="blockchain.md"><b>Fossil As Blockchain</b></a></li> <li><a href="changes.wiki"><b>Fossil Changelog</b></a></li> <li><a href="concepts.wiki"><b>Fossil Core Concepts</b></a></li> <li><a href="delta_encoder_algorithm.wiki"><b>Fossil Delta Encoding Algorithm</b></a></li> | > | 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 | <li><a href="adding_code.wiki">Features To Fossil — Adding New</a></li> <li><a href="fileformat.wiki">File Format — Fossil</a></li> <li><a href="globs.md"><b>File Name Glob Patterns</b></a></li> <li><a href="unvers.wiki">Files — Unversioned</a></li> <li><a href="branching.wiki">Forking, Merging, and Tagging — Branching,</a></li> <li><a href="delta_format.wiki">Format — Fossil Delta</a></li> <li><a href="fileformat.wiki">Format — Fossil File</a></li> <li><a href="image-format-vs-repo-size.md">Format vs Fossil Repo Size — Image</a></li> <li><a href="../../../md_rules">Formatting Rules — Markdown</a></li> <li><a href="../../../wiki_rules">Formatting Rules — Wiki</a></li> <li><a href="forum.wiki">Forums — Fossil</a></li> <li><a href="blockchain.md"><b>Fossil As Blockchain</b></a></li> <li><a href="changes.wiki"><b>Fossil Changelog</b></a></li> <li><a href="concepts.wiki"><b>Fossil Core Concepts</b></a></li> <li><a href="delta_encoder_algorithm.wiki"><b>Fossil Delta Encoding Algorithm</b></a></li> |
| ︙ | ︙ | |||
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | <li><a href="fossil-v-git.wiki"><b>Fossil Versus Git</b></a></li> <li><a href="quotes.wiki">Fossil, Git, and DVCSes in General — Quotes: What People Are Saying About</a></li> <li><a href="faq.wiki"><b>Frequently Asked Questions</b></a></li> <li><a href="quotes.wiki">General — Quotes: What People Are Saying About Fossil, Git, and DVCSes in</a></li> <li><a href="fossil-v-git.wiki">Git — Fossil Versus</a></li> <li><a href="inout.wiki">Git — Import And Export To And From</a></li> <li><a href="quotes.wiki">Git, and DVCSes in General — Quotes: What People Are Saying About Fossil,</a></li> <li><a href="globs.md">Glob Patterns — File Name</a></li> <li><a href="env-opts.md">Global Options — Environment Variables and</a></li> <li><a href="customgraph.md">Graph — Theming: Customizing the Timeline</a></li> <li><a href="grep.md">grep — Fossil grep vs POSIX</a></li> <li><a href="grep.md">grep vs POSIX grep — Fossil</a></li> <li><a href="quickstart.wiki">Guide — Fossil Quick Start</a></li> <li><a href="style.wiki">Guidelines — Source Code Style</a></li> <li><a href="hacker-howto.wiki"><b>Hacker How-To</b></a></li> <li><a href="adding_code.wiki"><b>Hacking Fossil</b></a></li> <li><a href="hashpolicy.wiki"><b>Hash Policy: Choosing Between SHA1 and SHA3-256</b></a></li> <li><a href="hints.wiki">Hints — Fossil Tips And Usage</a></li> <li><a href="index.wiki"><b>Home Page</b></a></li> <li><a href="selfhost.wiki">Hosting Repositories — Fossil Self</a></li> <li><a href="aboutcgi.wiki"><b>How CGI Works In Fossil</b></a></li> <li><a href="aboutdownload.wiki"><b>How The Download Page Works</b></a></li> <li><a href="server.wiki"><b>How To Configure A Fossil Server</b></a></li> <li><a href="newrepo.wiki"><b>How To Create A New Fossil Repository</b></a></li> <li><a href="encryptedrepos.wiki"><b>How To Use Encrypted Repositories</b></a></li> <li><a href="hacker-howto.wiki">How-To — Hacker</a></li> <li><a href="fossil-from-msvc.wiki">IDE — Integrating Fossil in the Microsoft Express 2010</a></li> <li><a href="tech_overview.wiki">Implementation Of Fossil — A Technical Overview Of The Design And</a></li> <li><a href="inout.wiki"><b>Import And Export To And From Git</b></a></li> <li><a href="build.wiki">Installing Fossil — Compiling and</a></li> <li><a href="fossil-from-msvc.wiki"><b>Integrating Fossil in the Microsoft Express 2010 IDE</b></a></li> <li><a href="selfcheck.wiki">Integrity Self Checks — Fossil Repository</a></li> <li><a href="webui.wiki">Interface — The Fossil Web</a></li> <li><a href="th1.md">Language — The TH1 Scripting</a></li> <li><a href="copyright-release.html">License Agreement — Contributor</a></li> <li><a href="../../../help"><b>Lists of Commands and Webpages</b></a></li> <li><a href="password.wiki">Management And Authentication — Password</a></li> <li><a href="../../../sitemap">Map — Site</a></li> <li><a href="../../../md_rules"><b>Markdown Formatting Rules</b></a></li> <li><a href="backoffice.md">mechanism of Fossil — The Backoffice</a></li> <li><a href="branching.wiki">Merging, and Tagging — Branching, Forking,</a></li> <li><a href="fossil-from-msvc.wiki">Microsoft Express 2010 IDE — Integrating Fossil in the</a></li> <li><a href="fiveminutes.wiki">Minutes as a Single User — Up and Running in 5</a></li> <li><a href="globs.md">Name Glob Patterns — File</a></li> <li><a href="checkin_names.wiki">Names — Check-in And Version</a></li> <li><a href="adding_code.wiki">New Features To Fossil — Adding</a></li> <li><a href="newrepo.wiki">New Fossil Repository — How To Create A</a></li> <li><a href="alerts.md">Notifications — Email Alerts And</a></li> <li><a href="foss-cklist.wiki">Open-Source Projects — Checklist For Successful</a></li> <li><a href="pop.wiki">Operation — Principles Of</a></li> <li><a href="env-opts.md">Options — Environment Variables and Global</a></li> <li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil — A Technical</a></li> <li><a href="index.wiki">Page — Home</a></li> <li><a href="aboutdownload.wiki">Page Works — How The Download</a></li> | > > > > > > | 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | <li><a href="fossil-v-git.wiki"><b>Fossil Versus Git</b></a></li> <li><a href="quotes.wiki">Fossil, Git, and DVCSes in General — Quotes: What People Are Saying About</a></li> <li><a href="faq.wiki"><b>Frequently Asked Questions</b></a></li> <li><a href="quotes.wiki">General — Quotes: What People Are Saying About Fossil, Git, and DVCSes in</a></li> <li><a href="fossil-v-git.wiki">Git — Fossil Versus</a></li> <li><a href="inout.wiki">Git — Import And Export To And From</a></li> <li><a href="quotes.wiki">Git, and DVCSes in General — Quotes: What People Are Saying About Fossil,</a></li> <li><a href="mirrortogithub.md">GitHub — How To Mirror A Fossil Repository On</a></li> <li><a href="globs.md">Glob Patterns — File Name</a></li> <li><a href="env-opts.md">Global Options — Environment Variables and</a></li> <li><a href="customgraph.md">Graph — Theming: Customizing the Timeline</a></li> <li><a href="grep.md">grep — Fossil grep vs POSIX</a></li> <li><a href="grep.md">grep vs POSIX grep — Fossil</a></li> <li><a href="quickstart.wiki">Guide — Fossil Quick Start</a></li> <li><a href="style.wiki">Guidelines — Source Code Style</a></li> <li><a href="hacker-howto.wiki"><b>Hacker How-To</b></a></li> <li><a href="adding_code.wiki"><b>Hacking Fossil</b></a></li> <li><a href="hashpolicy.wiki"><b>Hash Policy: Choosing Between SHA1 and SHA3-256</b></a></li> <li><a href="hints.wiki">Hints — Fossil Tips And Usage</a></li> <li><a href="index.wiki"><b>Home Page</b></a></li> <li><a href="selfhost.wiki">Hosting Repositories — Fossil Self</a></li> <li><a href="aboutcgi.wiki"><b>How CGI Works In Fossil</b></a></li> <li><a href="aboutdownload.wiki"><b>How The Download Page Works</b></a></li> <li><a href="server.wiki"><b>How To Configure A Fossil Server</b></a></li> <li><a href="newrepo.wiki"><b>How To Create A New Fossil Repository</b></a></li> <li><a href="mirrortogithub.md"><b>How To Mirror A Fossil Repository On GitHub</b></a></li> <li><a href="encryptedrepos.wiki"><b>How To Use Encrypted Repositories</b></a></li> <li><a href="hacker-howto.wiki">How-To — Hacker</a></li> <li><a href="tls-nginx.md">HTTPS with nginx — Proxying Fossil via</a></li> <li><a href="fossil-from-msvc.wiki">IDE — Integrating Fossil in the Microsoft Express 2010</a></li> <li><a href="image-format-vs-repo-size.md"><b>Image Format vs Fossil Repo Size</b></a></li> <li><a href="tech_overview.wiki">Implementation Of Fossil — A Technical Overview Of The Design And</a></li> <li><a href="inout.wiki"><b>Import And Export To And From Git</b></a></li> <li><a href="build.wiki">Installing Fossil — Compiling and</a></li> <li><a href="fossil-from-msvc.wiki"><b>Integrating Fossil in the Microsoft Express 2010 IDE</b></a></li> <li><a href="selfcheck.wiki">Integrity Self Checks — Fossil Repository</a></li> <li><a href="webui.wiki">Interface — The Fossil Web</a></li> <li><a href="th1.md">Language — The TH1 Scripting</a></li> <li><a href="copyright-release.html">License Agreement — Contributor</a></li> <li><a href="../../../help"><b>Lists of Commands and Webpages</b></a></li> <li><a href="password.wiki">Management And Authentication — Password</a></li> <li><a href="../../../sitemap">Map — Site</a></li> <li><a href="../../../md_rules"><b>Markdown Formatting Rules</b></a></li> <li><a href="backoffice.md">mechanism of Fossil — The Backoffice</a></li> <li><a href="branching.wiki">Merging, and Tagging — Branching, Forking,</a></li> <li><a href="fossil-from-msvc.wiki">Microsoft Express 2010 IDE — Integrating Fossil in the</a></li> <li><a href="fiveminutes.wiki">Minutes as a Single User — Up and Running in 5</a></li> <li><a href="mirrortogithub.md">Mirror A Fossil Repository On GitHub — How To</a></li> <li><a href="globs.md">Name Glob Patterns — File</a></li> <li><a href="checkin_names.wiki">Names — Check-in And Version</a></li> <li><a href="adding_code.wiki">New Features To Fossil — Adding</a></li> <li><a href="newrepo.wiki">New Fossil Repository — How To Create A</a></li> <li><a href="tls-nginx.md">nginx — Proxying Fossil via HTTPS with</a></li> <li><a href="alerts.md">Notifications — Email Alerts And</a></li> <li><a href="foss-cklist.wiki">Open-Source Projects — Checklist For Successful</a></li> <li><a href="pop.wiki">Operation — Principles Of</a></li> <li><a href="env-opts.md">Options — Environment Variables and Global</a></li> <li><a href="tech_overview.wiki">Overview Of The Design And Implementation Of Fossil — A Technical</a></li> <li><a href="index.wiki">Page — Home</a></li> <li><a href="aboutdownload.wiki">Page Works — How The Download</a></li> |
| ︙ | ︙ | |||
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 | <li><a href="private.wiki">Private Branches — Creating, Syncing, and Deleting</a></li> <li><a href="makefile.wiki">Process — The Fossil Build</a></li> <li><a href="contribute.wiki">Project — Contributing Code or Documentation To The Fossil</a></li> <li><a href="embeddeddoc.wiki">Project Documentation — Embedded</a></li> <li><a href="foss-cklist.wiki">Projects — Checklist For Successful Open-Source</a></li> <li><a href="childprojects.wiki">Projects — Child</a></li> <li><a href="sync.wiki">Protocol — The Fossil Sync</a></li> <li><a href="faq.wiki">Questions — Frequently Asked</a></li> <li><a href="qandc.wiki"><b>Questions And Criticisms</b></a></li> <li><a href="quickstart.wiki">Quick Start Guide — Fossil</a></li> <li><a href="quotes.wiki"><b>Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</b></a></li> <li><a href="selfhost.wiki">Repositories — Fossil Self Hosting</a></li> <li><a href="encryptedrepos.wiki">Repositories — How To Use Encrypted</a></li> <li><a href="newrepo.wiki">Repository — How To Create A New Fossil</a></li> <li><a href="selfcheck.wiki">Repository Integrity Self Checks — Fossil</a></li> <li><a href="reviews.wiki"><b>Reviews</b></a></li> <li><a href="../../../md_rules">Rules — Markdown Formatting</a></li> <li><a href="../../../wiki_rules">Rules — Wiki Formatting</a></li> <li><a href="fiveminutes.wiki">Running in 5 Minutes as a Single User — Up and</a></li> <li><a href="quotes.wiki">Saying About Fossil, Git, and DVCSes in General — Quotes: What People Are</a></li> <li><a href="th1.md">Scripting Language — The TH1</a></li> <li><a href="selfcheck.wiki">Self Checks — Fossil Repository Integrity</a></li> <li><a href="selfhost.wiki">Self Hosting Repositories — Fossil</a></li> <li><a href="server.wiki">Server — How To Configure A Fossil</a></li> <li><a href="settings.wiki">Settings — Fossil</a></li> <li><a href="admin-v-setup.md">Setup and Admin User Capabilities — The Differences Between the</a></li> <li><a href="hashpolicy.wiki">SHA1 and SHA3-256 — Hash Policy: Choosing Between</a></li> <li><a href="hashpolicy.wiki">SHA3-256 — Hash Policy: Choosing Between SHA1 and</a></li> <li><a href="shunning.wiki"><b>Shunning: Deleting Content From Fossil</b></a></li> <li><a href="fiveminutes.wiki">Single User — Up and Running in 5 Minutes as a</a></li> <li><a href="../../../sitemap"><b>Site Map</b></a></li> <li><a href="customskin.md">Skins — Custom</a></li> <li><a href="style.wiki"><b>Source Code Style Guidelines</b></a></li> <li><a href="antibot.wiki">Spiders and Bots — Defense against</a></li> <li><a href="tech_overview.wiki"><b>SQLite Databases Used By Fossil</b></a></li> <li><a href="ssl.wiki">SSL with Fossil — Using</a></li> <li><a href="quickstart.wiki">Start Guide — Fossil Quick</a></li> <li><a href="stats.wiki">Statistics — Performance</a></li> | > > > > | 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 | <li><a href="private.wiki">Private Branches — Creating, Syncing, and Deleting</a></li> <li><a href="makefile.wiki">Process — The Fossil Build</a></li> <li><a href="contribute.wiki">Project — Contributing Code or Documentation To The Fossil</a></li> <li><a href="embeddeddoc.wiki">Project Documentation — Embedded</a></li> <li><a href="foss-cklist.wiki">Projects — Checklist For Successful Open-Source</a></li> <li><a href="childprojects.wiki">Projects — Child</a></li> <li><a href="sync.wiki">Protocol — The Fossil Sync</a></li> <li><a href="tls-nginx.md"><b>Proxying Fossil via HTTPS with nginx</b></a></li> <li><a href="faq.wiki">Questions — Frequently Asked</a></li> <li><a href="qandc.wiki"><b>Questions And Criticisms</b></a></li> <li><a href="quickstart.wiki">Quick Start Guide — Fossil</a></li> <li><a href="quotes.wiki"><b>Quotes: What People Are Saying About Fossil, Git, and DVCSes in General</b></a></li> <li><a href="image-format-vs-repo-size.md">Repo Size — Image Format vs Fossil</a></li> <li><a href="selfhost.wiki">Repositories — Fossil Self Hosting</a></li> <li><a href="encryptedrepos.wiki">Repositories — How To Use Encrypted</a></li> <li><a href="newrepo.wiki">Repository — How To Create A New Fossil</a></li> <li><a href="selfcheck.wiki">Repository Integrity Self Checks — Fossil</a></li> <li><a href="mirrortogithub.md">Repository On GitHub — How To Mirror A Fossil</a></li> <li><a href="reviews.wiki"><b>Reviews</b></a></li> <li><a href="../../../md_rules">Rules — Markdown Formatting</a></li> <li><a href="../../../wiki_rules">Rules — Wiki Formatting</a></li> <li><a href="fiveminutes.wiki">Running in 5 Minutes as a Single User — Up and</a></li> <li><a href="quotes.wiki">Saying About Fossil, Git, and DVCSes in General — Quotes: What People Are</a></li> <li><a href="th1.md">Scripting Language — The TH1</a></li> <li><a href="selfcheck.wiki">Self Checks — Fossil Repository Integrity</a></li> <li><a href="selfhost.wiki">Self Hosting Repositories — Fossil</a></li> <li><a href="server.wiki">Server — How To Configure A Fossil</a></li> <li><a href="settings.wiki">Settings — Fossil</a></li> <li><a href="admin-v-setup.md">Setup and Admin User Capabilities — The Differences Between the</a></li> <li><a href="hashpolicy.wiki">SHA1 and SHA3-256 — Hash Policy: Choosing Between</a></li> <li><a href="hashpolicy.wiki">SHA3-256 — Hash Policy: Choosing Between SHA1 and</a></li> <li><a href="shunning.wiki"><b>Shunning: Deleting Content From Fossil</b></a></li> <li><a href="fiveminutes.wiki">Single User — Up and Running in 5 Minutes as a</a></li> <li><a href="../../../sitemap"><b>Site Map</b></a></li> <li><a href="image-format-vs-repo-size.md">Size — Image Format vs Fossil Repo</a></li> <li><a href="customskin.md">Skins — Custom</a></li> <li><a href="style.wiki"><b>Source Code Style Guidelines</b></a></li> <li><a href="antibot.wiki">Spiders and Bots — Defense against</a></li> <li><a href="tech_overview.wiki"><b>SQLite Databases Used By Fossil</b></a></li> <li><a href="ssl.wiki">SSL with Fossil — Using</a></li> <li><a href="quickstart.wiki">Start Guide — Fossil Quick</a></li> <li><a href="stats.wiki">Statistics — Performance</a></li> |
| ︙ | ︙ | |||
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 | <li><a href="fiveminutes.wiki">User — Up and Running in 5 Minutes as a Single</a></li> <li><a href="admin-v-setup.md">User Capabilities — The Differences Between the Setup and Admin</a></li> <li><a href="ssl.wiki"><b>Using SSL with Fossil</b></a></li> <li><a href="env-opts.md">Variables and Global Options — Environment</a></li> <li><a href="whyusefossil.wiki">Version Control — Benefits Of</a></li> <li><a href="checkin_names.wiki">Version Names — Check-in And</a></li> <li><a href="fossil-v-git.wiki">Versus Git — Fossil</a></li> <li><a href="grep.md">vs POSIX grep — Fossil grep</a></li> <li><a href="webui.wiki">Web Interface — The Fossil</a></li> <li><a href="customskin.md">Web Pages — Theming: Customizing The Appearance of</a></li> <li><a href="webpage-ex.md"><b>Webpage Examples</b></a></li> <li><a href="../../../help">Webpages — Lists of Commands and</a></li> <li><a href="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General — Quotes:</a></li> <li><a href="whyusefossil.wiki"><b>Why You Should Use Fossil</b></a></li> <li><a href="../../../wiki_rules"><b>Wiki Formatting Rules</b></a></li> <li><a href="wikitheory.wiki"><b>Wiki In Fossil</b></a></li> <li><a href="aboutdownload.wiki">Works — How The Download Page</a></li> <li><a href="aboutcgi.wiki">Works In Fossil — How CGI</a></li> <li><a href="whyusefossil.wiki">You Should Use Fossil — Why</a></li> </ul></div> | > > | 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 | <li><a href="fiveminutes.wiki">User — Up and Running in 5 Minutes as a Single</a></li> <li><a href="admin-v-setup.md">User Capabilities — The Differences Between the Setup and Admin</a></li> <li><a href="ssl.wiki"><b>Using SSL with Fossil</b></a></li> <li><a href="env-opts.md">Variables and Global Options — Environment</a></li> <li><a href="whyusefossil.wiki">Version Control — Benefits Of</a></li> <li><a href="checkin_names.wiki">Version Names — Check-in And</a></li> <li><a href="fossil-v-git.wiki">Versus Git — Fossil</a></li> <li><a href="tls-nginx.md">via HTTPS with nginx — Proxying Fossil</a></li> <li><a href="image-format-vs-repo-size.md">vs Fossil Repo Size — Image Format</a></li> <li><a href="grep.md">vs POSIX grep — Fossil grep</a></li> <li><a href="webui.wiki">Web Interface — The Fossil</a></li> <li><a href="customskin.md">Web Pages — Theming: Customizing The Appearance of</a></li> <li><a href="webpage-ex.md"><b>Webpage Examples</b></a></li> <li><a href="../../../help">Webpages — Lists of Commands and</a></li> <li><a href="quotes.wiki">What People Are Saying About Fossil, Git, and DVCSes in General — Quotes:</a></li> <li><a href="whyusefossil.wiki"><b>Why You Should Use Fossil</b></a></li> <li><a href="../../../wiki_rules"><b>Wiki Formatting Rules</b></a></li> <li><a href="wikitheory.wiki"><b>Wiki In Fossil</b></a></li> <li><a href="aboutdownload.wiki">Works — How The Download Page</a></li> <li><a href="aboutcgi.wiki">Works In Fossil — How CGI</a></li> <li><a href="whyusefossil.wiki">You Should Use Fossil — Why</a></li> </ul></div> |
Changes to www/quickstart.wiki.
1 2 3 4 5 6 | <title>Fossil Quick Start Guide</title> <h1 align="center">Fossil Quick Start</h1> <p>This is a guide to get you started using fossil quickly and painlessly.</p> | | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<title>Fossil Quick Start Guide</title>
<h1 align="center">Fossil Quick Start</h1>
<p>This is a guide to get you started using fossil quickly
and painlessly.</p>
<h2 id="install">Installing</h2>
<p>Fossil is a single self-contained C program. You need to
either download a
<a href="https://www.fossil-scm.org/fossil/uv/download.html">precompiled
binary</a>
or <a href="build.wiki">compile it yourself</a> from sources.
Install fossil by putting the fossil binary
|
| ︙ | ︙ |
Changes to www/server.wiki.
1 | <title>How To Configure A Fossil Server</title> | > | > > | | > | > | | > | > > > | | | | > | > | > > > | | | | > | | > | | > | > | | < | > | | > > > > | > > > | > > | > > > | > > > > > > > | > | | > > > > | | > > | < | > | | < | < | < < < < < < < < < < < < < < < < < < < < | | | > > | | | > > | < > < < | | | | | | | | | | | | | < < | < > | < | < < | | < > | | | | < | < | > | | < < < < < < < | < < < < < | < < < > > | < < | | > | | | | > | | | | | | | > | | | | | | > | > > | | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 |
<title>How To Configure A Fossil Server</title>
<h2>Introduction</h2>
<blockquote>
A server is not necessary to use Fossil, but a server does help in collaborating with
peers. A Fossil server also works well as a complete website for a project.
For example, the complete [https://www.fossil-scm.org/] website, including the
page you are now reading,
is just a Fossil server displaying the content of the
self-hosting repository for Fossil.
This article is a guide for setting up your own Fossil server.
See "[./aboutcgi.wiki|How CGI Works In Fossil]" for background
information on the underlying CGI technology.
See "[./sync.wiki|The Fossil Sync Protocol]" for information on the
wire protocol used for client/server communication.
</blockquote>
<h2>Overview</h2>
<blockquote>
There are basically four ways to set up a Fossil server:
<ol>
<li>A stand-alone server
<li>Using inetd, xinetd, or stunnel
<li>CGI
<li>SCGI (a.k.a. SimpleCGI)
</ol>
Each of these can serve either a single repository, or a directory hierarchy
containing many repositories with names ending in ".fossil".
</blockquote>
<h2 id="standalone">Standalone server</h2>
<blockquote>
The easiest way to set up a Fossil server is to use either the
[/help/server|server] or the [/help/ui|ui] commands:
<ul>
<li><b>fossil server</b> <i>REPOSITORY</i>
<li><b>fossil ui</b> <i>REPOSITORY</i>
</ul>
The <i>REPOSITORY</i> argument is either the name of the repository file, or
a directory containing many repositories named <tt>*.fossil</tt>.
Both of these commands start a Fossil server, usually on TCP port 8080, though
a higher numbered port might also be used if 8080 is already occupied. You can
access these using URLs of the form <b>http://localhost:8080/</b>, or if
<i>REPOSITORY</i> is a directory, URLs of the form
<b>http://localhost:8080/</b><i>repo</i><b>/</b> where <i>repo</i> is the base
name of the repository file without the ".fossil" suffix.
There are several key differences between "ui" and "server":
<ul>
<li>"ui" always binds the server to the loopback IP address
(127.0.0.1) so that it cannot serve to other machines.
<li>anyone who visits this URL is treated as the all-powerful Setup
user, which is why the first difference exists.
<li>"ui" launches a local web browser pointed at this URL.
</ul>
You can omit the <i>REPOSITORY</i> argument if you run one of the above
commands from within a Fossil checkout directory to serve that
repository:
<blockquote><pre>
$ fossil ui # or...
$ fossil serve
</pre></blockquote>
Note that you can abbreviate Fossil sub-commands, as long as they are
unambiguous. "<tt>server</tt>" can currently be as short as
"<tt>ser</tt>".
As a more complicated example, you can serve a directory containing
multiple <tt>*.fossil</tt> files like so:
<blockquote><pre>
$ fossil server --port 9000 --repolist /path/to/repo/dir
</pre></blockquote>
There is an [/file/tools/fslsrv | example script] in the Fossil
distribution that wraps <tt>fossil server</tt> to produce more
complicated effects. Feel free to take it, study it, and modify it to
suit your local needs.
See the [/help/server|online documentation] for more information on the
options and arguments you can give to these commands.
</blockquote>
<h2 id="inetd">Fossil as an inetd/xinetd service</h2>
<blockquote>
A Fossil server can be launched on-demand by inetd or xinetd using
the [/help/http|fossil http] command. To launch Fossil from inetd, modify
your inetd configuration file (typically "/etc/inetd.conf") to contain a
line something like this:
<blockquote>
<pre>
80 stream tcp nowait.1000 root /usr/bin/fossil /usr/bin/fossil http /home/fossil/repo.fossil
</pre>
</blockquote>
In this example, you are telling "inetd" that when an incoming connection
appears on TCP port "80", that it should launch the binary "/usr/bin/fossil"
program with the arguments shown.
Obviously you will
need to modify the pathnames for your particular setup.
The final argument is either the name of the fossil repository to be served,
or a directory containing multiple repositories.
If you use a non-standard TCP port on
systems where the port-specification must be a symbolic name and cannot be
numeric, add the desired name and port to /etc/services. For example, if
you want your Fossil server running on TCP port 12345 instead of 80, you
will need to add:
<blockquote>
<pre>
fossil 12345/tcp #fossil server
</pre>
</blockquote>
and use the symbolic name ('fossil' in this example) instead of the numeral ('12345')
in inetd.conf. For details, see the relevant section in your system's documentation, e.g.
the [https://www.freebsd.org/doc/en/books/handbook/network-inetd.html|FreeBSD Handbook] in
case you use FreeBSD.
If your system is running xinetd, then the configuration is likely to be
in the file "/etc/xinetd.conf" or in a subfile of "/etc/xinetd.d".
An xinetd configuration file will appear like this:
<blockquote>
<pre>
service http
{
port = 80
socket_type = stream
wait = no
user = root
server = /usr/bin/fossil
server_args = http /home/fossil/repos/
}
</pre>
</blockquote>
The xinetd example above has Fossil configured to serve multiple
repositories, contained under the "/home/fossil/repos/" directory.
In both cases notice that Fossil was launched as root. This is not required,
but if it is done, then Fossil will automatically put itself into a chroot
jail for the user who owns the fossil repository before reading any information
off of the wire.
Inetd or xinetd must be enabled, and must be (re)started whenever their configuration
changes - consult your system's documentation for details.
Using inetd or xinetd is a more complex setup
than the "standalone" server, but it has the
advantage of only using system resources when an actual connection is
attempted. If no-one ever connects to that port, a Fossil server will
not (automatically) run. It has the disadvantage of requiring "root" access
and therefore may not normally be available to lower-priced "shared" servers
on the Internet.
The configuration for <tt>stunnel</tt> is similar, but it is covered in
[./ssl.wiki#stunnel|a separate document].
</blockquote>
<h2 id="cgi">Fossil as CGI</h2>
<blockquote>
A Fossil server can also be run from an ordinary web server as a CGI program.
This feature allows Fossil to be seamlessly integrated into a larger website.
CGI is how the [./selfhost.wiki | self-hosting fossil repositories] are
implemented.
To run Fossil as CGI, create a CGI script (here called "repo") in the CGI directory
of your web server and having content like this:
<blockquote><pre>
#!/usr/bin/fossil
repository: /home/fossil/repo.fossil
</pre></blockquote>
As always, adjust your paths appropriately.
It may be necessary to set permissions properly, or to modify an ".htaccess"
file or make other server-specific changes. Consult the documentation
for your particular web server. In particular, the following permissions are
<em>normally</em> required (but, again, may be different for a particular
configuration):
<ul>
<li>The Fossil binary (/usr/bin/fossil in the example above)
must be readable/executable, and ALL directories leading up to it
must be readable by the process which executes the CGI.</li>
<li>ALL directories leading to the CGI script must also be readable and the CGI
script itself must be executable for the user under which it will run (which often differs
from the one running the web server - consult your site's documentation or administrator).</li>
<li>The repository file AND the directory containing it must be writable by the same account
which executes the Fossil binary (again, this might differ from the WWW user). The directory
needs to be writable so that sqlite can write its journal files.</li>
<li>Fossil must be able to create temporary files, the default directory
for which depends on the OS. When the CGI process is operating within
a chroot, ensure that this directory exists and is readable/writeable
by the user who executes the Fossil binary.</li>
</ul>
Once the script is set up correctly, and assuming your server is also set
correctly, you should be able to access your repository with a URL like:
<b>http://mydomain.org/cgi-bin/repo</b> (assuming the "repo" script is
accessible under "cgi-bin", which would be a typical deployment on Apache
for instance).
To serve multiple repositories from a directory using CGI, use the "directory:"
tag in the CGI script rather than "repository:". You might also want to add
a "notfound:" tag to tell where to redirect if the particular repository requested
by the URL is not found:
<blockquote><pre>
#!/usr/bin/fossil
directory: /home/fossil/repos
notfound: http://url-to-go-to-if-repo-not-found/
</pre></blockquote>
Once deployed, a URL like: <b>http://mydomain.org/cgi-bin/repo/XYZ</b>
will serve up the repository "/home/fossil/repos/XYZ.fossil" (if it exists).
Additional options available to the CGI script are documented in the
source code. As of 2017-07-02, the available options are described at
[/artifact/9a52a07b?ln=1777-1824|main.c lines 1777 through 1824].
</blockquote>
<h2 id="scgi">Fossil as SCGI</h2>
<blockquote>
The [/help/server|fossil server] command, described above as a way of
starting a stand-alone web server, can also be used for SCGI. Simply add
the --scgi command-line option and the stand-alone server will interpret
and respond to the SimpleCGI or SCGI protocol rather than raw HTTP. This can
be used in combination with a webserver (such as [http://nginx.org|Nginx])
that does not support CGI. A typical Nginx configuration to support SCGI
with Fossil would look something like this:
<blockquote><pre>
location /demo_project/ {
include scgi_params;
scgi_pass localhost:9000;
scgi_param SCRIPT_NAME "/demo_project";
scgi_param HTTPS "on";
}
</pre></blockquote>
Note that Fossil requires the SCRIPT_NAME variable
in order to function properly, but Nginx does not provide this
variable by default,
so it is necessary to provide the SCRIPT_NAME parameter in the configuration.
Failure to do this will cause Fossil to return an error.
All of the features of the stand-alone server mode described above,
such as the ability to serve a directory full of Fossil repositories
rather than just a single repository, work the same way in SCGI mode.
For security, it is probably a good idea to add the --localhost option
to the [/help/server|fossil server] command to prevent Fossil from accepting
off-site connections. One might also want to specify the listening TCP port
number, rather than letting Fossil choose one for itself, just to avoid
ambiguity. A typical command to start a Fossil SCGI server
would be something like this:
<blockquote><pre>
fossil server $REPOSITORY --scgi --localhost --port 9000
</pre></blockquote>
</blockquote>
<h2 id="tls">Securing a repository with TLS</h2>
<blockquote>
Fossil's built-in HTTP server (e.g. "fossil server") does not support
TLS, but there are multiple ways to protect your Fossil server with
TLS. All of this is covered in a separate document, <a
href="./ssl.wiki">Using TLS-Encrypted Communications with Fossil</a>.
</blockquote>
<h2 id="loadmgmt">Managing Server Load</h2>
<blockquote>
A Fossil server is very efficient and normally presents a very light
load on the server.
The Fossil [./selfhost.wiki | self-hosting server] is a 1/24th slice VM at
[http://www.linode.com | Linode.com] hosting 65 other repositories in
addition to Fossil (and including some very high-traffic sites such
as [http://www.sqlite.org] and [http://system.data.sqlite.org]) and
it has a typical load of 0.05 to 0.1. A single HTTP request to Fossil
normally takes less than 10 milliseconds of CPU time to complete, so
requests can be arriving at a continuous rate of 20 or more per second,
and the CPU can still be mostly idle.
However, there are some Fossil web pages that can consume large
amounts of CPU time, especially on repositories with a large number
of files or with long revision histories. High CPU usage pages include
[/help?cmd=/zip | /zip], [/help?cmd=/tarball | /tarball],
[/help?cmd=/annotate | /annotate] and others. On very large repositories,
these commands can take 15 seconds or more of CPU time.
If these kinds of requests arrive too quickly, the load average on the
server can grow dramatically, making the server unresponsive.
Fossil provides two capabilities to help avoid server overload problems
due to excessive requests to expensive pages:
<ol>
<li><p>An optional cache is available that remembers the 10 most recently
requested /zip or /tarball pages and returns the precomputed answer
if the same page is requested again.</p>
<li><p>Page requests can be configured to fail with a
[http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.5.4 | "503 Server Overload"]
HTTP error if an expensive request is received while the host load
average is too high.</p>
</ol>
Both of these load-control mechanisms are turned off by default, but they
are recommended for high-traffic sites.
The webpage cache is activated using the [/help?cmd=cache|fossil cache init]
command-line on the server. Add a -R option to specify the specific repository
for which to enable caching. If running this command as root, be sure to
"chown" the cache database (which is a separate file in the same directory
and with the same name as the repository but with the suffix changed to ".cache")
to give it write permission for the userid of the webserver.
To activate the server load control feature
visit the Admin → Access setup page in the administrative web
interface; in the "<b>Server Load Average Limit</b>" box
enter the load average threshold above which "503 Server
Overload" replies will be issued for expensive requests. On the
self-hosting Fossil server, that value is set to 1.5, but you could easily
set it higher on a multi-core server.
The maximum load average can also be set on the command line using
commands like this:
<blockquote><pre>
fossil set max-loadavg 1.5
fossil all set max-loadavg 1.5
</pre></blockquote>
The second form is especially useful for changing the maximum load average
simultaneously on a large number of repositories.
Note that this load-average limiting feature is only available on operating
systems that support the "getloadavg()" API. Most modern Unix systems have
this interface, but Windows does not, so the feature will not work on Windows.
Note also that Linux implements "getloadavg()" by accessing the "/proc/loadavg"
file in the "proc" virtual filesystem. If you are running a Fossil instance
inside a chroot() jail on Linux, you will need to make the "/proc" file
system available inside that jail in order for this feature to work. On
the [./selfhost.wiki|self-hosting Fossil repositories], this was accomplished
by adding a line to the "/etc/fstab" file that looks like:
<blockquote><pre>
chroot_jail_proc /home/www/proc proc ro 0 0
</pre></blockquote>
The /home/www/proc pathname should be adjusted so that the "/proc" component is
in the root of the chroot jail, of course.
To see if the load-average limiter is functional, visit the [/test_env] page
of the server to view the current load average. If the value for the load
average is greater than zero, that means that it is possible to activate
the load-average limiter on that repository. If the load average shows
exactly "0.0", then that means that Fossil is unable to find the load average
(either because it is in a chroot() jail without /proc access, or because
it is running on a system that does not support "getloadavg()") and so the
|
| ︙ | ︙ |
Changes to www/ssl.wiki.
|
| | | | > > > > > > > > > | > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > > > > > > > > > > > > > > | | > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > > > > | > > > > > > > | > > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | > | > > > > > | > > > > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 |
<title>TLS and Fossil</title>
<h2>Using TLS-Encrypted Communications with Fossil</h2>
If you are storing sensitive information in a repository accessible over
a network whose integrity you do not fully trust, you should use TLS to
encrypt all communications with it. This is most true for repositories
accessed over the Internet, especially if they will be accessed from
edge networks you do not control, since that admits of various forms of
[https://en.wikipedia.org/wiki/Man-in-the-middle_attack|man in the
middle attack].
TLS protects the credentials used to access the server, prevents
eavesdropping, prevents in-flight data modification, prevents server
identify spoofing, and more.
There are two major aspects to this, both of which have to be addressed
in different ways. Those are the subjects of the next two major
sections.
<h2 id="client">Fossil TLS Configuration: Client Side</h2>
Fossil itself has built-in support for TLS on the client side only. That
is to say, you can build it against [https://www.openssl.org/|the
OpenSSL library], which will allow it to clone and sync with a remote
Fossil repository via <tt>https</tt> URIs.
<h3 id="openssl-bin">Building Against OpenSSL Automatically</h3>
The <tt>configure</tt> script will attempt to find OpenSSL on your
system automatically. It first tries asking the <tt>pkg-config</tt>
system where the OpenSSL development files are, and if that fails, it
falls back to looking through a list of likely directories.
If it can't find the files it needs, the most common solution is to
install the OpenSSL development package on your system via your OS's
package manager. Examples:
* <b>RHEL & Fedora</b>: <tt>sudo yum install openssl-devel</tt>
* <b>Debian & Ubuntu</b>: <tt>sudo apt install libssl-dev</tt>
* <b>FreeBSD</b>: <tt>su -c 'pkg install openssl'</tt>
* <b>macOS</b>: <tt>sudo brew install openssl</tt>
* <b>Cygwin</b>: Install <tt>openssl-devel</tt> via Cygwin's
<tt>setup-*.exe</tt> program
The macOS case requires explanation. Apple last shipped OpenSSL
develpoment files in OS X 10.6 (Snow Leopard), choosing to deprecate it
from that point forward. (Apple wants you to use their proprietary
platform-specific encryption methods instead.) Since macOS has no
built-in package manager, a number have sprung up out of the FOSS world.
It is not known to this author whether Fossil's current build system can
find OpenSSL as installed with any of these other package managers, so
unless you have a particular reason to avoid it, we recomend that you
use [https://brew.sh|Homebrew] on macOS to install OpenSSL as above.
Fossil's build system will seek it out and use it automatically.
<h3 id="openssl-src">Building Against a Non-Platform Version of
OpenSSL</h3>
The Fossil build system has a few other methods for finding OpenSSL when
the automatic methods fail or when you'd prefer that Fossil use a
different version of OpenSSL than the one Fossil's build system picks on
its own.
A good reason to do this is when the Fossil build system finds a
functioning version of OpenSSL which is nevertheless unsuitable. One
common case is that your OS is sufficiently outdated that the platform
version of OpenSSL can no longer communicate with remote systems
adhering to the latest advice on secure communications. An old OpenSSL
might not support any of the
[https://en.wikipedia.org/wiki/Cipher_suite|cipher suites] the remote
Fossil repository's HTTPS proxy is willing to offer, for example, so
that even though both sides are speaking a variant of TLS/SSL, the peers
cannot come to an agreement on the cryptography.
If you've installed the OpenSSL development files somewhere that
Fossil's build system cannot find on its own, you can clue it in by
passing the <tt>--with-openssl</tt> option to the <tt>configure</tt>
script. Type <tt>./configure --help</tt> for details.
Another option is to download the source code to OpenSSL and build
Fossil against that private version of OpenSSL:
<pre>
cd compat # relative to the Fossil source tree root
tar xf /path/to/openssl-*.tar.gz
ln -fs openssl-x.y.z openssl
cd openssl
./config # or, e.g. ./Configure darwin64-x86_64-cc
make -j11
cd ../..
./configure --with-openssl=tree
make -j11
</pre>
That will get you a Fossil binary statically linked to this in-tree
version of OpenSSL.
Beware, taking this path typically opens you up to new problems, which
are conveniently covered in the next section!
<h3 id="certs">Certificates</h3>
To verify the identify of a server, TLS uses
[https://en.wikipedia.org/wiki/X.509#Certificates|X.509 certificates].
If you are using a self-signed certificate, you'll be asked if you want
to accept the certificate the first time you communicate with the
server. Verify the certificate fingerprint is correct, then answer
"always" to remember your decision.
If you are cloning from or syncing to Fossil servers that use a
certificate signed by a
[https://en.wikipedia.org/wiki/Certificate_authority|certificate
authority] (CA), Fossil needs to know which CAs you trust to sign those
certificates. Fossil relies on the OpenSSL library to have some way to
check a trusted list of CA signing keys.
There are two common ways this fails:
# <p>The OpenSSL library Fossil is linked to doesn't have a CA
signing key set at all, so that it initially trusts no certificates
at all.</p>
# <p>The OpenSSL library does have a CA cert set, but your Fossil server's
TLS certificate was signed by a CA that isn't in that set.</p>
A common reason to fall into the second trap is that you're using
certificates signed by a local private CA, as often happens in large
enterprises. You can solve this sort of problem by getting your local
CA's signing certificate in PEM format and pointing OpenSSL at it:
<pre>
fossil set --global ssl-ca-location /path/to/local-ca.pem
</pre>
The use of <tt>--global</tt> with this option is common, since you may
have multiple reposotories served under certificates signed by that same
CA.
A common way to run into the broader first problem is that you're on
FreeBSD, which does not install a CA certificate set by default, even as
a dependency of the OpenSSL library. If you're using a certificate
signed by one of the major public CAs, you can solve this by installing
the <tt>ca_root_nss</tt> package. That package contains the Mozilla NSS
certificate bundle, which gets installed in a location that OpenSSL
checks by default, so you don't need to change any Fossil settings.
(This is the same certificate set that ships with Firefox, by the way.)
The same sort of thing happens with the Windows build of OpenSSL, but
for a different core reason: Windows does ship with a stock CA
certificate set, but it's not in a format that OpenSSL understands how
to use. Rather than try to find a way to convert the data format, you
may find it acceptable to use the same Mozilla NSS cert set. I do not
know of a way to easily get this from Mozilla themselves, but I did find
a [https://curl.haxx.se/docs/caextract.html|third party source] for the
<tt>cacert.pem</tt> file. Install it somewhere on your system, then
point Fossil at it like so:
<pre>
fossil set --global ssl-ca-location /path/to/cacert.pem
</pre>
This can also happen if you've linked Fossil to a version of OpenSSL
[#openssl-src|built from source]. That same <tt>cacert.pem</tt> fix can
work in that case, too.
When you build Fossil on Linux platforms against the binary OpenSSL
package provided with the OS, you typically get a root cert store along
with the platform OpenSSL package, either built-in or as a hard
dependency.
<h4>Client-Side Certificates</h4>
You can also use client side certificates to add an extra layer of
authentication, over and above Fossil's built in user management. If you
are particularly paranoid, you'll want to use this to remove the ability
of anyone on the internet from making any request to Fossil. Without
presenting a valid client side certificate, the web server won't invoke
the Fossil CGI handler.
Configure your server to request a client side certificate, and set up a
certificate authority to sign your client certificates. For each person
who needs to access the repository, create a private key and certificate
signed with that CA.
The PEM encoded private key and certificate should be stored in a single
file, simply by concatenating the key and certificate files. Specify the
location of this file with the <tt>ssl-identity</tt> setting, or the
<tt>--ssl-identity</tt> option to the <tt>clone</tt> command.
If you've password protected the private key, the password will be
requested every time you connect to the server. This password is not
stored by fossil, as doing so would defeat the purpose of having a
password.
If you attempt to connect to a server which requests a client
certificate, but don't provide one, fossil will show an error message
which explains what to do to authenticate with the server.
<h2 id="server">Fossil TLS Configuration: Server Side</h2>
Fossil's built-in HTTP server feature does not currently have a built-in
way to serve via HTTP over TLS, a.k.a. HTTPS, even when you've linked
Fossil to OpenSSL. To serve a Fossil repository via HTTPS, you must put
it behind some kind of HTTPS proxy.
<h3 id="stunnel">stunnel Alone</h3>
[https://www.stunnel.org/ | <tt>stunnel</tt>] is an
[https://en.wikipedia.org/wiki/Inetd | <tt>inetd</tt>]-like process that
accepts and decodes TLS-encrypted connections. It can directly proxy
Fossil communications, allowing secure TLS-encrypted communications to a
Fossil repository server. You simply need to install <tt>stunnel</tt>
and then place something like this in its main configuration file,
<tt>stunnel.conf</tt>:
<nowiki><pre>
[https]
accept = www.ubercool-project.org:443
TIMEOUTclose = 0
exec = /usr/bin/fossil
execargs = /usr/bin/fossil http /home/fossil/ubercool.fossil --https
</pre></nowiki>
The directory where that file goes varies between OSes, so check the man
pages on your system to find out where it should be locally.
See the <tt>stunnel</tt> documentation for further details about this
configuration file.
It is important that the [/help/http | <tt>fossil http</tt>] command in
that configuration include the <tt>--https</tt> option to let Fossil
know to use "<tt>https</tt>" instead of "<tt>http</tt>" as the URL
scheme on generated hyperlinks.
<h3 id="althttpd">stunnel + althttpd</h3>
The public SQLite and Fossil web sites can't just use stunnel + Fossil
because parts of the web site are static, served by
[https://www.sqlite.org/docsrc/file/misc/althttpd.c|a separate web
server called <tt>althttpd</tt>], written by the primary author of both
SQLite and Fossil. It is a lightweight HTTP-only web server. It handles
the static HTTP hits on <tt>sqlite.org</tt> and <tt>fossil-scm.org</tt>,
delegating HTTPS and dynamic Fossil content hits to stunnel and Fossil.
The only documentation for althttpd currently is in its header comment.
As is typical for drh software, althttpd is a single-file C program, so
that at worst, you just have to read its code to understand it.
<h3 id="nginx">nginx</h3>
If your needs are more complex than althttpd can handle or you'd prefer
to use only software available in your server operating system's package
repository, we recommend that you step up to [http://nginx.org/|nginx].
Setting this up is complex enough that we cover it [./tls-nginx.md|in a
separate document].
<h2 id="enforcing">Enforcing TLS Access</h2>
To use TLS encryption in cloning and syncing to a remote Fossil
repository, be sure to use the <tt>https:</tt> URI scheme in
<tt>clone</tt> and <tt>sync</tt> commands. If your server is configured
to serve the repository via both HTTP and HTTPS, it's easy to
accidentally use unencrypted HTTP if you forget the all-important 's'.
As of Fossil 2.8, there is a setting in the Fossil UI under Admin →
Access called "Redirect to HTTPS," which is set to "Off" by default.
Changing this only affects web UI access to the Fossil repository. It
doesn't affect clones and syncs done via the <tt>http</tt> URI scheme.
In Fossil 2.7 and earlier, there was a much weaker form of this setting
affecting the <tt>/login</tt> page only. If you're using this setting,
you should migrate to the new setting as soon as possible, because the
old setting allows multiple ways of defeating it.
<b id="rloop">WARNING:</b> Enabling HTTPS redirects at the Fossil repo
level while running Fossil behind an HTTPS proxy can result in an
infinite redirect loop. It happens when the proxy mechanism presents
"<tt>http</tt>" URIs to Fossil, so Fossil issues a redirect, so the browser
fetches the page again, causing Fossil to see an "<tt>http</tt>" URI again, so
it issues a redirect...'round and 'round it goes until the web browser
detects it's in a redirect loop and gives up. This problem prevents you
from getting back into the Admin UI to fix it, but there are several
ways to fix it:
# <p><b>Reset via CLI.</b> You can turn the setting back off from the
CLI with the command "<tt>fossil -R /path/to/repo.fossil set
redirect-to-https 0</tt>". (Currently doesn't work.)</p>
# <p><b>Backup first.</b> This setting is stored in the Fossil
repository, so if you make a backup first <i>on the server</i>, you
can restore the repo file if enabling this feature creates a
redirect loop.</p>
# <p><b>Download, fix, and restore.</b> You can copy the remote
repository file down to a local machine, use <tt>fossil ui</tt> to
fix the setting, and then upload it to the repository server
again.</p>
It's best to enforce TLS-only access at the front-end proxy level
anyway. It not only avoids the problem entirely, it can be significantly
more secure. The [./tls-nginx.md|nginx TLS proxy guide] shows one way
to achieve this.</p>
<h2>Terminology Note</h2>
This document is called <tt>ssl.wiki</tt> for historical reasons. The
TLS protocol was originally called SSL, and it went through several
revisions before being replaced by TLS. Years before this writing, SSL
finally became entirely obsolete due to weaknesses in the protocol fixed
in the later TLS series of protocols.
Some people still use the term "SSL" when they actually mean "TLS," but
in the Fossil project, we always use "TLS" except when we must preserve
some sort of historical compatibility, as with this document's name in
order to avoid broken external URLs. The Fossil TLS-related settings
also often use "<tt>ssl</tt>" in their names, for the same reason.
This series of protocols is also called "HTTPS" after the URI scheme
used to specify "HTTP over TLS."
|
Changes to www/th1.md.
| ︙ | ︙ | |||
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | There are many new commands added to TH1 and used to access the special features of Fossil. The following is a summary of the extended commands: * anoncap * anycap * artifact * checkout * combobox * date * decorate * dir * enable\_output * encode64 * getParameter * glob\_match * globalState * hascap * hasfeature * html * htmlize * http * httpize * insertCsrf * linecount * markdown * puts * query * randhex * redirect * regexp * reinitialize * render | > > | 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | There are many new commands added to TH1 and used to access the special features of Fossil. The following is a summary of the extended commands: * anoncap * anycap * artifact * cgiHeaderLine * checkout * combobox * date * decorate * dir * enable\_output * encode64 * getParameter * glob\_match * globalState * hascap * hasfeature * html * htmlize * http * httpize * insertCsrf * linecount * markdown * nonce * puts * query * randhex * redirect * regexp * reinitialize * render |
| ︙ | ︙ | |||
244 245 246 247 248 249 250 251 252 253 254 255 256 257 | ------------------------------------------- * artifact ID ?FILENAME? Attempts to locate the specified artifact and return its contents. An error is generated if the repository is not open or the artifact cannot be found. <a name="checkout"></a>TH1 checkout Command ------------------------------------------- * checkout ?BOOLEAN? Return the fully qualified directory name of the current checkout or an | > > > > > > > | 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 | ------------------------------------------- * artifact ID ?FILENAME? Attempts to locate the specified artifact and return its contents. An error is generated if the repository is not open or the artifact cannot be found. <a name="cgiHeaderLine"></a>TH1 cgiHeaderLine Command ----------------------------------------------------- * cgiHeaderLine line Adds the specified line to the CGI header. <a name="checkout"></a>TH1 checkout Command ------------------------------------------- * checkout ?BOOLEAN? Return the fully qualified directory name of the current checkout or an |
| ︙ | ︙ | |||
443 444 445 446 447 448 449 450 451 452 453 454 455 456 | * markdown STRING Renders the input string as markdown. The result is a two-element list. The first element contains the body, rendered as HTML. The second element is the text-only title string. <a name="puts"></a>TH1 puts Command ----------------------------------- * puts STRING Outputs the STRING unchanged. | > > > > > > > | 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 | * markdown STRING Renders the input string as markdown. The result is a two-element list. The first element contains the body, rendered as HTML. The second element is the text-only title string. <a name="nonce"></a>TH1 nonce Command ------------------------------------- * nonce Returns the value of the cryptographic nonce for the request being processed. <a name="puts"></a>TH1 puts Command ----------------------------------- * puts STRING Outputs the STRING unchanged. |
| ︙ | ︙ |
Added www/tls-nginx.md.
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 |
# Proxying Fossil via HTTPS with nginx
One of the [many ways](./ssl.wiki) to provide TLS-encrypted HTTP access
(a.k.a. HTTPS) to Fossil is to run it behind a web proxy that supports
TLS. This document explains how to use the powerful [nginx web
server](http://nginx.org/) to do that.
## Benefits
This scheme is complicated, even with the benefit of this guide and
pre-built binary packages. Why should you put up with this complexity?
Because it gives many benefits that are difficult or impossible to get
with the less complicated options:
* **Power** — nginx is one of the most powerful web servers in the
world. The chance that you will run into a web serving wall that you
can’t scale with nginx is very low.
To give you some idea of the sort of thing you can readily
accomplish with nginx, your author runs a single public web server
that provides transparent name-based virtual hosting for four
separate domains:
* One is entirely static, not involving any dynamic content or
Fossil integration at all.
* Another is served almost entirely by Fossil, with a few select
static content exceptions punched past Fossil, which are handled
entirely via nginx.
* The other two domains are aliases for one another — e.g.
`example.com` and `example.net` — with most of the content being
static. This pair of domains has three different Fossil repo
proxies attached to various sections of the URI hierarchy.
All of this is done with minimal configuration repetition between
the site configurations.
* **Integration** — Because nginx is so popular, it integrates with
many different technologies, and many other systems integrate with it in
turn. This makes it great middleware, sitting between the outer web
world and interior site services like Fossil. It allows Fossil to
participate seamlessly as part of a larger web stack.
* **Availability** — nginx is already in most operating system binary
package repositories, so you don’t need to go out of your way to get it.
## Fossil Remote Access Methods
Fossil provides four major ways to access a repository it’s serving
remotely, three of which are straightforward to use with nginx:
* **HTTP** — Fossil has a built-in HTTP server: `fossil server`.
While this method is efficient and it’s possible to use nginx to
proxy access to another HTTP server, this option is overkill for our
purposes. nginx is itself a fully featured HTTP server, so we will
choose in this guide not to make nginx reinterpret Fossil’s
implementation of HTTP.
* **CGI** — This method is simple but inefficient, because it launches
a separate Fossil instance on every HTTP hit.
Since Fossil is a relatively small self-contained program, and it’s
designed to start up quickly, this method can work well in a
surprisingly large number of cases.
Nevertheless, we will avoid this option in this document because
we’re already buying into a certain amount of complexity here in
order to gain power. There’s no sense in throwing away any of that
hard-won performance on CGI overhead.
* **SCGI** — The [SCGI protocol][scgi] provides the simplicity of CGI
without its performance problems.
* **SSH** — This method exists primarily to avoid the need for HTTPS
in the first place. There is probably a way to get nginx to proxy
Fossil to HTTPS via SSH, but it would be pointlessly complicated.
SCGI it is, then.
# Installing
The first step is to install the pieces we’ll be working with. This
varies on different operating systems, so to avoid overcomplicating this
guide, we’re going to assume you’re using Ubuntu Server 18.04 LTS, a
common Tier 1 offering for [virtual private servers][vps].
SSH into your server, then say:
$ sudo apt install certbot fossil nginx
For other operating systems, simply visit [the front Certbot web
page][cb] and tell it what OS and web stack you’re using. Chances are
good that they’ve got a good guide for you already.
# Running Fossil in SCGI Mode
You presumably already have a working Fossil configuration on the public
server you’re trying to set up and are just following this guide to
replace HTTP service with HTTPS.
(You can adjust the advice in this guide to get both HTTP *and* HTTPS
service on the same site, but I strongly recommend that you do not do
that: the good excuses remaining for continuing to allow HTTP on public
web servers are running thin these days.)
I run my Fossil SCGI server instances with a variant of [the `fslsrv`
shell script](/file/tools/fslsrv) currently hosted in the Fossil source
code repository. You’ll want to download that and make a copy of it, so
you can customize it to your particular needs.
This script allows running multiple Fossil SCGI servers, one per
repository, each bound to a different high-numbered `localhost` port, so
that only nginx can see and proxy them out to the public. The
“`example`” repo is on TCP port localhost:12345, and the “`foo`” repo is
on localhost:12346.
As written, the `fslsrv` script expects repositories to be stored in the
calling user’s home directory under `~/museum`, because where else do
you keep Fossils?
That home directory also needs to have a directory to hold log files,
`~/log/fossil/*.log`. Fossil doesn’t put out much logging, but when it
does, it’s better to have it captured than to need to re-create the
problem after the fact.
The use of `--baseurl` in this script lets us have each Fossil
repository mounted in a different location in the URL scheme. Here, for
example, we’re saying that the “`example`” repository is hosted under
the `/code` URI on its domains, but that the “`foo`” repo is hosted at
the top level of its domain. You’ll want to do something like the
former for a Fossil repo that’s just one piece of a larger site, but the
latter for a repo that is basically the whole point of the site.
You might also want another script to automate the update, build, and
deployment steps for new Fossil versions:
#!/bin/sh
cd $HOME/src/fossil/trunk
fossil up
make -j11
killall fossil
sudo make install
fslsrv
The `killall fossil` step is needed only on OSes that refuse to let you
replace a running binary on disk.
As written, the `fslsrv` script assumes a Linux environment. It expects
`/bin/bash` to exist, and it depends on non-POSIX tools like `pgrep`.
It should not be difficult to port to systems like macOS or the BSDs.
# Configuring Let’s Encrypt, the Easy Way
If your web serving needs are simple, [Certbot][cb] can configure nginx
for you and keep its certificates up to date. You can follow the Certbot
documentation for [nginx on Ubuntu 18.04 LTS guide][cbnu] as-is, though
we’d recommend one small change: to use the version of Certbot in the
Ubuntu package repository rather than the first-party Certbot package
that the guide recommends.
The primary local configuration you need is to tell nginx how to proxy
certain URLs down to the Fossil instance you started above with the
`fslsrv` script:
location / {
include scgi_params;
scgi_pass 127.0.0.1:12345;
scgi_param HTTPS "on";
scgi_param SCRIPT_NAME "";
}
The TCP port number in that snippet is the key: it has to match the port
number generated by `fslsrv` from the base port number passed to the
`start_one` function.
# Configuring Let’s Encrypt, the Hard Way
If you’re finding that you can’t get certificates to be issued or
renewed using the Easy Way instructions, the problem is usually that
your nginx configuration is too complicated for Certbot’s `--nginx`
plugin to understand. It attempts to rewrite your nginx configuration
files on the fly to achieve the renewal, and if it doesn’t put its
directives in the right locations, the domain verification can fail.
Let’s Encrypt uses the [Automated Certificate Management
Environment][acme] protocol (ACME) to determine whether a given client
actually has control over the domain(s) for which it wants a certificate
minted. Let’s Encrypt will not blithely let you mint certificates for
`google.com` and `paypal.com` just because you ask for it!
Your author’s configuration, glossed above, is complicated enough that
the current version of Certbot (0.28 at the time of this writing) can’t
cope with it. That’s the primary motivation for me to write this guide:
I’m addressing the “me” years hence who needs to upgrade to Ubuntu 20.04
or 22.04 LTS and has forgotten all of this stuff. 😉
## Step 1: Shifting into Manual
The first thing to do is to turn off all of the Certbot automation,
because it’ll only get in our way. First, disable the Certbot package’s
automatic background updater:
$ sudo systemctl disable certbot.timer
Next, edit `/etc/letsencrypt/renewal/example.com.conf` to disable the
nginx plugins. You’re looking for two lines setting the “install” and
“auth” plugins to “nginx”. You can comment them out or remove them
entirely.
## Step 2: Configuring nginx
On Ubuntu systems, at least, the primary user-level configuration file
is `/etc/nginx/sites-enabled/default`. For a configuration like I
described at the top of this article, I recommend that this file contain
only a list of include statements, one for each site that server hosts:
include local/example
include local/foo
Those files then each define one domain’s configuration. Here,
`/etc/nginx/local/example` contains the configuration for
`*.example.com` and `*.example.net`; and `local/foo` contains the
configuration for `*.foo.net`.
Here’s an example configuration:
server {
server_name .foo.net;
include local/tls-common;
charset utf-8;
access_log /var/log/nginx/foo.net-https-access.log;
error_log /var/log/nginx/foo.net-https-error.log;
# Bypass Fossil for the static Doxygen docs
location /doc/html {
root /var/www/foo.net;
location ~* \.(html|ico|css|js|gif|jpg|png)$ {
expires 7d;
add_header Vary Accept-Encoding;
access_log off;
}
}
# Redirect everything else to the Fossil instance
location / {
include scgi_params;
scgi_pass 127.0.0.1:12345;
scgi_param HTTPS "on";
scgi_param SCRIPT_NAME "";
}
}
server {
server_name .foo.net;
root /var/www/foo.net;
include local/http-certbot-only;
access_log /var/log/nginx/foo.net-http-access.log;
error_log /var/log/nginx/foo.net-http-error.log;
}
Notice that we need two `server { }` blocks: one for HTTPS service, and
one for HTTP-only service:
### HTTP over TLS (HTTPS) Service
The first `server { }` block includes this file, `local/tls-common`:
listen 443 ssl;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
ssl_stapling on;
ssl_stapling_verify on;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA:ECDHE-ECDSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256”;
ssl_session_cache shared:le_nginx_SSL:1m;
ssl_prefer_server_ciphers on;
ssl_session_timeout 1440m;
These are the common TLS configuration parameters used by all domains
hosted by this server.
The first line tells nginx to accept TLS-encrypted HTTP connections on
the standard HTTPS port. It is the same as `listen 443; ssl on;` in
older versions of nginx.
Since all of those domains share a single TLS certificate, we reference
the same `example.com/*.pem` files written out by Certbot with the
`ssl_certificate*` lines.
The `ssl_dhparam` directive isn’t strictly required, but without it, the
server becomes vulnerable to the [Logjam attack][lja] because some of
the cryptography steps are precomputed, making the attacker’s job much
easier. The parameter file this directive references should be
generated automatically by the Let’s Encrypt package upon installation,
making those parameters unique to your server and thus unguessable. If
the file doesn’t exist on your system, you can create it manually, so:
$ sudo openssl dhparam -out /etc/letsencrypt/dhparams.pem 2048
Beware, this can take a long time. On a shared Linux host I tried it on
running OpenSSL 1.1.0g, it took about 21 seconds, but on a fast, idle
iMac running LibreSSL 2.6.5, it took 8 minutes and 4 seconds!
The next section is also optional. It enables [OCSP stapling][ocsp], a
protocol that improves the speed and security of the TLS connection
negotiation.
The next section containing the `ssl_protocols` and `ssl_ciphers` lines
restricts the TLS implementation to only those protocols and ciphers
that are currently believed to be safe and secure. This section is the
one most prone to bit-rot: as new attacks on TLS and its associated
technologies are discovered, this configuration is likely to need to
change. Even if we fully succeed in [keeping this document
up-to-date](#evolution), the nature of this guide is to recommend static
configurations for your server. You will have to keep an eye on this
sort of thing and evolve your local configuration as the world changes
around it.
Running a TLS certificate checker against your site occasionally is a
good idea. The most thorough service I’m aware of is the [Qualys SSL
Labs Test][qslt], which gives the site I’m basing this guide on an “A”
rating at the time of this writing. The long `ssl_ciphers` line above is
based on [their advice][qslc]: the default nginx configuration tells
OpenSSL to use whatever ciphersuites it considers “high security,” but
some of those have come to be considered “weak” in the time between that
judgement and the time of this writing. By explicitly giving the list of
ciphersuites we want OpenSSL to use within nginx, we can remove those
that become considered weak in the future.
<a id=”hsts”></a>There are a few things you can do to get an even better
grade, such as to enable [HSTS][hsts], which prevents a particular
variety of [man in the middle attack][mitm] where our HTTP-to-HTTPS
permanent redirect is intercepted, allowing the attacker to prevent the
automatic upgrade of the connection to a secure TLS-encrypted one. I
didn’t enable that in the configuration above, because it is something a
site administrator should enable only after the configuration is tested
and stable, and then only after due consideration. There are ways to
lock your users out of your site by jumping to HSTS hastily. When you’re
ready, there are [guides you can follow][nest] elsewhere online.
### HTTP-Only Service
While we’d prefer not to offer HTTP service at all, we need to do so for
two reasons:
* The temporary reason is that until we get Let’s Encrypt certificates
minted and configured properly, we can’t use HTTPS yet at all.
* The ongoing reason is that the Certbot [ACME][acme] HTTP-01
challenge used by the Let’s Encrypt service only runs over HTTP. This is
not only because it has to work before HTTPS is first configured,
but also because it might need to work after a certificate is
accidentally allowed to lapse, to get that server back into a state
where it can speak HTTPS safely again.
So, from the second `service { }` block, we include this file to set up
the minimal HTTP service we reqiure, `local/http-certbot-only`:
listen 80;
listen [::]:80;
# This is expressed as a rewrite rule instead of an "if" because
# http://wiki.nginx.org/IfIsEvil
#rewrite ^(/.well-known/acme-challenge/.*) $1 break;
# Force everything else to HTTPS with a permanent redirect.
#return 301 https://$host$request_uri;
As written above, this configuration does nothing other than to tell
nginx that it’s allowed to serve content via HTTP on port 80 as well.
We’ll uncomment the `rewrite` and `return` directives below, when we’re
ready to begin testing.
#### Why the Repetition?
These `server { }` blocks contain several directives that have to be
either completely repeated or copied with only trivial changes when
you’re hosting multiple domains from a single server.
You might then wonder, why haven’t I factored some of those directives
into the included files `local/tls-common` and
`local/http-certbot-only`? Why can’t the HTTP-only `server { }` block
above be just two lines? That is, why can I not say:
server_name .foo.net;
include local/http-certbot-only;
Then in `local/http-certbot-only` say:
root /var/www/$host;
access_log /var/log/nginx/$host-http-access.log;
error_log /var/log/nginx/$host-http-error.log;
Sadly, nginx doesn’t allow variable subtitution into these particular
directives. As I understand it, allowing that would make nginx slower,
so we must largely repeat these directives in each HTTP `server { }`
block.
These configurations are, as shown, as small as I know how to get them.
If you know of a way to reduce some of this repitition, [I solicit your
advice][fd].
## Step 3: Dry Run
We want to first request a dry run, because Let’s Encrypt puts some
rather low limits on how often you’re allowed to request an actual
certificate. You want to be sure everything’s working before you do
that. You’ll run a command something like this:
$ sudo certbot certonly --webroot --dry-run \
--webroot-path /var/www/example.com \
-d example.com -d www.example.com \
-d example.net -d www.example.net \
--webroot-path /var/www/foo.net \
-d foo.net -d www.foo.net
There are two key options here.
First, we’re telling Certbot to use its `--webroot` plugin instead of
the automated `--nginx` plugin. With this plugin, Certbot writes the
[ACME][acme] HTTP-01 challenge files to the static web document root
directory behind each domain. For this example, we’ve got two web
roots, one of which holds documents for two different second-level
domains (`example.com` and `example.net`) with `www` at the third level
being optional. This is a common sort of configuration these days, but
you needn’t feel that you must slavishly imitate it; the other web root
is for an entirely different domain, also with `www` being optional.
Since all of these domains are served by a single nginx instance, we
need to give all of this in a single command, because we want to mint a
single certificate that authenticates all of these domains.
The second key option is `--dry-run`, which tells Certbot not to do
anything permanent. We’re just seeing if everything works as expected,
at this point.
### Troubleshooting the Dry Run
If that didn’t work, try creating a manual test:
$ mkdir -p /var/www/example.com/.well-known/acme-challenge
$ echo hi > /var/www/example.com/.well-known/acme-challenge/test
Then try to pull that file over HTTP — not HTTPS! — as
`http://example.com/.well-known/acme-challenge/test`. I’ve found that
using Firefox or Safari is better for this sort of thing than Chrome,
because Chrome is more aggressive about automatically forwarding URLs to
HTTPS even if you requested “`http`”.
In extremis, you can do the test manually:
$ telnet foo.net 80
GET /.well-known/acme-challenge/test HTTP/1.1
Host: example.com
HTTP/1.1 200 OK
Server: nginx/1.14.0 (Ubuntu)
Date: Sat, 19 Jan 2019 19:43:58 GMT
Content-Type: application/octet-stream
Content-Length: 3
Last-Modified: Sat, 19 Jan 2019 18:21:54 GMT
Connection: keep-alive
ETag: "5c436ac2-4"
Accept-Ranges: bytes
hi
You type the first two lines at the remote system, plus the doubled
“Enter” to create the blank line, and you get something back that
hopefully looks like the rest of the text above.
The key bits you’re looking for here are the “hi” line at the end — the
document content you created above — and the “200 OK” response code. If
you get a 404 or other error response, you need to look into your web
server logs to find out what’s going wrong.
Note that it’s important to do this test with HTTP/1.1 when debugging a
name-based virtual hosting configuration like this. Unless you test only
with the primary domain name alias for the server, this test will fail.
Using the example configuration above, you can only use the
easier-to-type HTTP/1.0 protocol to test the `foo.net` alias.
If you’re still running into trouble, the log file written by Certbot
can be helpful. It tells you where it’s writing it early in each run.
## Step 4: Getting Your First Certificate
Once the dry run is working, you can drop the `--dry-run` option and
re-run the long command above. (The one with all the `--webroot*`
flags.) This should now succeed, and it will save all of those flag
values to your Let’s Encrypt configuration file, so you don’t need to
keep giving them.
## Step 5: Test It
Edit the `local/http-certbot-only` file and uncomment the `redirect` and
`return` directives, then restart your nginx server and make sure it now
forces everything to HTTPS like it should:
$ sudo systemctl restart nginx
Test ideas:
* Visit both Fossil and non-Fossil URLs
* Log into the repo, log out, and log back in
* Clone via `http`: ensure that it redirects to `https`, and that
subsequent `fossil sync` commands go directly to `https` due to the
301 permanent redirect.
This forced redirect is why we don’t need the Fossil Admin → Access
"Redirect to HTTPS on the Login page" setting to be enabled. Not only
is it unnecessary with this HTTPS redirect at the front-end proxy level,
it would actually [cause an infinite redirect loop if
enabled](./ssl.wiki#rloop).
## Step 6: Re-Sync Your Repositories
Now that the repositories hosted by this server are available via HTTPS,
you need to tell Fossil about it:
$ cd ~/path/to/checkout
$ fossil sync https://example.com/code
Once that’s done per repository file, all checkouts of that repo will
from that point on use the HTTPS URI to sync.
You might wonder if that’s necessary, since we have the automatic
HTTP-to-HTTPS redirect on this site now. If you clone or sync one of
these nginx-hosted Fossil repositories over an untrustworthy network
that allows [MITM attacks][mitm], that redirect won’t protect you from a
sufficiently capable and motivated attacker unless you’ve also gone
ahead and [enabled HSTS](#hsts). You can put off the need to enable
HSTS by explicitly using HTTPS URIs.
## Step 7: Renewing Automatically
Now that the configuration is solid, you can renew the LE cert with the
`certbot` command from above without the `--dry-run` flag plus a restart
of nginx:
sudo certbot certonly --webroot \
--webroot-path /var/www/example.com \
-d example.com -d www.example.com \
-d example.net -d www.example.net \
--webroot-path /var/www/foo.net \
-d foo.net -d www.foo.net
sudo systemctl restart nginx
I put those commands in a script in the `PATH`, then arrange to call that
periodically. Let’s Encrypt doesn’t let you renew the certificate very
often unless forced, and when forced there’s a maximum renewal counter.
Nevertheless, some people recommend running this daily and just letting
it fail until the server lets you renew. Others arrange to run it no
more often than it’s known to work without complaint. Suit yourself.
-----------
<a id=”evolution”></a>
**Document Evolution**
TLS and web proxying are a constantly evolving technology. This article
replaces my [earlier effort][2016], which had whole sections that were
basically obsolete within about a year of posting it. Two years on, and
I was encouraging readers to ignore about half of that HOWTO. I am now
writing this document about 3 years later because Let’s Encrypt
deprecated key technology that HOWTO depended on, to the point that
following that old HOWTO is more likely to confuse than enlighten.
There is no particularly good reason to expect that this sort of thing
will not continue to happen, so this effort is expected to be a living
document. If you do not have commit access on the `fossil-scm.org`
repository to update this document as the world changes around it, you
can discuss this document [on the forum][fd]. This document’s author
keeps an eye on the forum and expects to keep this document updated with
ideas that appear in that thread.
[2016]: https://www.mail-archive.com/fossil-users@lists.fossil-scm.org/msg22907.html
[acme]: https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment
[cb]: https://certbot.eff.org/
[cbnu]: https://certbot.eff.org/lets-encrypt/ubuntubionic-nginx
[fd]: https://fossil-scm.org/forum/forumpost/ae6a4ee157
[hsts]: https://en.wikipedia.org/wiki/HTTP_Strict_Transport_Security
[lja]: https://en.wikipedia.org/wiki/Logjam_(computer_security)
[mitm]: https://en.wikipedia.org/wiki/Man-in-the-middle_attack
[nest]: https://www.nginx.com/blog/http-strict-transport-security-hsts-and-nginx/
[ocsp]: https://en.wikipedia.org/wiki/OCSP_stapling
[qslc]: https://github.com/ssllabs/research/wiki/SSL-and-TLS-Deployment-Best-Practices
[qslt]: https://www.ssllabs.com/ssltest/
[scgi]: https://en.wikipedia.org/wiki/Simple_Common_Gateway_Interface
[vps]: https://en.wikipedia.org/wiki/Virtual_private_server
|